diff --git a/CHANGELOG.md b/CHANGELOG.md index 8db3aac..9198477 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +## v1.05 release 2017-02-04 + +### Added + +- Support for chemical element numbers 113-118 (see release notes for detailed information) +- Experimental support of InChI/InChIKey for regular single-strand polymers (see release notes for detailed information)Several minor bugfixes is necessary for treating large molecules (previous versions supported only V2000 format limited to not more than 1000 atoms). +- Provisional support for extended features of Molfile V3000 +- API procedure for direct conversion of Molfile input to InChI, as well as demo program illustrating its use +- New set of API procedures for both low and high-level operations (InChI extensible interface, IXA), as well as a demo program illustrating its use +- Convience options added to inchi-1 executable +- New optin `Large Molecules` instructs inchi-1 executable to accept molecules containing more than 1024 (but less than 32767) atoms +- A new option `Polymers` instructs inchi-1 executable to accept polymer data in input V2000 Molfiles +- The new option "Output at Error an empty InChI", `OutErrInChI` (`/OutErrInChI` under Windows, `-OutErrInChI` under Linux) instructs inchi-1 executable to output empty InChI and corresponding InChIKey if error occurs (default behaviour is output nothing) +- Option `Record:N` (`/Record:N` under Windows, `-Record:N` under Linux) instructs inchi-1 executable to process only the N-th record of the input file in SDF format + +### Changed + +- Implementation of wildcard expansion in "Allow Multiple Input (AMI) mode" (like in `inchi-1 /AMI *.mol`) under Windows is improved, in order to tolerate possible large expansion volumes +- Source code was significantly changed in order to ensure multithread execution safety of the InChI library +- Several minor changes (including refactoring) +- Updated documentation + ## v1.04 release 2011-10-12 ### Added diff --git a/INCHI-1-BIN/linux/32bit/inchi-1-32.gz b/INCHI-1-BIN/linux/32bit/inchi-1-32.gz new file mode 100644 index 0000000..3680031 Binary files /dev/null and b/INCHI-1-BIN/linux/32bit/inchi-1-32.gz differ diff --git a/INCHI-1-BIN/linux/32bit/inchi-1.gz b/INCHI-1-BIN/linux/32bit/inchi-1.gz deleted file mode 100644 index 3035777..0000000 Binary files a/INCHI-1-BIN/linux/32bit/inchi-1.gz and /dev/null differ diff --git a/INCHI-1-BIN/linux/64bit/inchi-1.gz b/INCHI-1-BIN/linux/64bit/inchi-1.gz index 20205ac..406458d 100644 Binary files a/INCHI-1-BIN/linux/64bit/inchi-1.gz and b/INCHI-1-BIN/linux/64bit/inchi-1.gz differ diff --git a/INCHI-1-BIN/readme.txt b/INCHI-1-BIN/readme.txt index 646fd03..4daf770 100644 --- a/INCHI-1-BIN/readme.txt +++ b/INCHI-1-BIN/readme.txt @@ -1,16 +1,19 @@ /* * International Chemical Identifier (InChI) * Version 1 - * Software version 1.04 - * September 9, 2011 + * Software version 1.05 + * January 27, 2017 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. * * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 + * International Chemical Identifier (InChI) * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it @@ -18,88 +21,32 @@ * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust - * c/o FIZ CHEMIE Berlin + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. + * or e-mail to alan@inchi-trust.org * */ - -This directory includes 'command line' InChI executable and InChI API library binaries. -The 32 and 64 bit versions are supplied for both Windows and Linux. +This directory contains binaries of command line InChI executable (inchi-1) +and InChI API library (libinchi). The 32 and 64 bit versions are supplied +for both Windows and Linux. Also included is winchi-1.exe, a graphical Windows application (directory 'windows'; a 32 bit version which will also run under 64 bit Windows). +Note that: -========= - FILES -========= - -readme.txt This file - -WINDOWS SUB-DIRECTORY - winchi-1.exe InChI graphical Windows application - - 32BIT SUB-DIRECTORY - inchi-1.exe InChI stand-alone command line executable, 32 bit - - DLL SUB-DIRECTORY - libinchi.dll InChI dynamic-link library, 32 bit - - 64BIT SUB-DIRECTORY - inchi-1.exe InChI stand-alone command line executable, 64 bit - - DLL SUB-DIRECTORY - libinchi.dll InChI dynamic-link library, 64 bit - -LINUX SUB-DIRECTORY - - 32BIT SUB-DIRECTORY - inchi-1.gz InChI stand-alone command line executable, 32 bit; gzipped - - SO SUB-DIRECTORY - libinchi.so.1.04.00.gz shared library for InChI API, 32 bit; gzipped - - 64BIT SUB-DIRECTORY - inchi-1.gz InChI stand-alone command line executable, 64 bit; gzipped - - SO SUB-DIRECTORY - libinchi.so.1.04.00.gz shared library for InChI API, 64 bit; gzipped - - - -Note-1. InChI stand-alone executable inchi-1[.exe] does not require dll/so libraries. - -Note-2. Example programs which use InChI API library for both Windows (dll) and Linux (so) -are supplied in INCHI-1-API/InChI_API section of this distribution package. -There are examples for C ('inchi_main' application, see projects for MS Visual Studio 2008 'vc9' -and gcc 'gcc_so_makefile' ) and Python ('python_sample'). Also supplied ther -are InChI API library sorce codes and related MS Visual Studio 2008/gcc projects. - -Note-3. To use the shared library, you may wish to create 'libinchi.so.1' -as a symbolic link to 'libinchi.so.1.04.00' - - - -========= - LINKS -========= +* InChI stand-alone executable inchi-1[.exe] does not require InChI dll/so libraries. -IUPAC http://www.iupac.org/inchi -InChI Trust http://www.inchi-trust.org -InChI discussion group https://lists.sourceforge.net/lists/listinfo/inchi-discuss +* To use the shared library, you may wish to create 'libinchi.so.1' as a symbolic link + to 'libinchi.so.1.05.00' diff --git a/INCHI-1-BIN/windows/32bit/dll/libinchi.dll b/INCHI-1-BIN/windows/32bit/dll/libinchi.dll index 655b08b..245fc84 100644 Binary files a/INCHI-1-BIN/windows/32bit/dll/libinchi.dll and b/INCHI-1-BIN/windows/32bit/dll/libinchi.dll differ diff --git a/INCHI-1-BIN/windows/32bit/inchi-1.exe b/INCHI-1-BIN/windows/32bit/inchi-1.exe index 784c3cf..82e4800 100644 Binary files a/INCHI-1-BIN/windows/32bit/inchi-1.exe and b/INCHI-1-BIN/windows/32bit/inchi-1.exe differ diff --git a/INCHI-1-BIN/windows/64bit/dll/libinchi.dll b/INCHI-1-BIN/windows/64bit/dll/libinchi.dll index 35acf74..8b0b8b6 100644 Binary files a/INCHI-1-BIN/windows/64bit/dll/libinchi.dll and b/INCHI-1-BIN/windows/64bit/dll/libinchi.dll differ diff --git a/INCHI-1-BIN/windows/64bit/inchi-1.exe b/INCHI-1-BIN/windows/64bit/inchi-1.exe index 20a0930..fa45628 100644 Binary files a/INCHI-1-BIN/windows/64bit/inchi-1.exe and b/INCHI-1-BIN/windows/64bit/inchi-1.exe differ diff --git a/INCHI-1-BIN/windows/winchi-1.exe b/INCHI-1-BIN/windows/winchi-1.exe index 0420565..7c88b1f 100644 Binary files a/INCHI-1-BIN/windows/winchi-1.exe and b/INCHI-1-BIN/windows/winchi-1.exe differ diff --git a/INCHI-1-DOC/External-contributors.pdf b/INCHI-1-DOC/External-contributors.pdf new file mode 100644 index 0000000..c199c8d Binary files /dev/null and b/INCHI-1-DOC/External-contributors.pdf differ diff --git a/INCHI-1-DOC/InChI_API_Reference.pdf b/INCHI-1-DOC/InChI_API_Reference.pdf index 8eff439..f74e27a 100644 Binary files a/INCHI-1-DOC/InChI_API_Reference.pdf and b/INCHI-1-DOC/InChI_API_Reference.pdf differ diff --git a/INCHI-1-DOC/InChI_TechMan.pdf b/INCHI-1-DOC/InChI_TechMan.pdf index 3ac2384..f714c06 100644 Binary files a/INCHI-1-DOC/InChI_TechMan.pdf and b/INCHI-1-DOC/InChI_TechMan.pdf differ diff --git a/INCHI-1-DOC/InChI_UserGuide.pdf b/INCHI-1-DOC/InChI_UserGuide.pdf index 9365d67..cedd8ba 100644 Binary files a/INCHI-1-DOC/InChI_UserGuide.pdf and b/INCHI-1-DOC/InChI_UserGuide.pdf differ diff --git a/INCHI-1-DOC/RelNotes.pdf b/INCHI-1-DOC/RelNotes.pdf index e3aa0df..faaaf53 100644 Binary files a/INCHI-1-DOC/RelNotes.pdf and b/INCHI-1-DOC/RelNotes.pdf differ diff --git a/INCHI-1-DOC/readme.txt b/INCHI-1-DOC/readme.txt index ce88f86..2c1df9f 100644 --- a/INCHI-1-DOC/readme.txt +++ b/INCHI-1-DOC/readme.txt @@ -1,16 +1,19 @@ /* * International Chemical Identifier (InChI) * Version 1 - * Software version 1.04 - * September 9, 2011 + * Software version 1.05 + * January 27, 2017 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. * * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 + * International Chemical Identifier (InChI) * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it @@ -18,53 +21,29 @@ * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust - * c/o FIZ CHEMIE Berlin + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. + * or e-mail to alan@inchi-trust.org * */ - This directory contains documentation related to InChI/InChIKey -identifiers, v. 1, and InChI/InChIKey software v. 1.04 (2011). - - -========= - FILES -========= +identifiers, v. 1, and InChI/InChIKey software v. 1.05 (Winter 2017). -readme.txt This file - -RelNotes.pdf Release Notes for InChI software v. 1.04 +RelNotes.pdf Release Notes InChI_UserGuide.pdf InChI User's Guide - InChI_API_Reference.pdf InChI API Reference - InChI_TechMan.pdf InChI Technical Manual - - - - - -========= - LINKS -========= - -IUPAC http://www.iupac.org/inchi -InChI Trust http://www.inchi-trust.org -InChI discussion group https://lists.sourceforge.net/lists/listinfo/inchi-discuss +External-contributors.pdf List of external contributors (other than InChITrust) + who specifically contributed to this release diff --git a/INCHI-1-SRC/INCHI/common/ichi.h b/INCHI-1-SRC/INCHI/common/ichi.h deleted file mode 100644 index 2191aac..0000000 --- a/INCHI-1-SRC/INCHI/common/ichi.h +++ /dev/null @@ -1,272 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHI_H__ -#define __INCHI_H__ - -#include "incomdef.h" - -#define REQ_MODE_BASIC 0x000001 /* B include Fixed-H layer */ -#define REQ_MODE_TAUT 0x000002 /* T include Mobile-H layer */ -#define REQ_MODE_ISO 0x000004 /* I */ -#define REQ_MODE_NON_ISO 0x000008 /* NI */ -#define REQ_MODE_STEREO 0x000010 /* S */ -#define REQ_MODE_ISO_STEREO 0x000020 /* IS */ -#define REQ_MODE_NOEQ_STEREO 0x000040 /* SS */ -#define REQ_MODE_REDNDNT_STEREO 0x000080 /* RS */ -#define REQ_MODE_NO_ALT_SBONDS 0x000100 /* NASB */ -/* new 10-10-2003 */ -#define REQ_MODE_RELATIVE_STEREO 0x000200 /* REL All Relative Stereo */ -#define REQ_MODE_RACEMIC_STEREO 0x000400 /* RAC All Racemic Stereo */ -#define REQ_MODE_SC_IGN_ALL_UU 0x000800 /* IAUSC Ignore stereocenters if All Undef/Unknown */ -#define REQ_MODE_SB_IGN_ALL_UU 0x001000 /* IAUSC Ignore stereobonds if All Undef/Unknown */ -#define REQ_MODE_CHIR_FLG_STEREO 0x002000 /* SUCF If Chiral flag then Abs otherwise Rel stereo */ -/* end of 10-10-2003 */ - -/*^^^ 2009-12-05 */ -#define REQ_MODE_DIFF_UU_STEREO 0x004000 /* SLUUD Make labels for unknown and undefined stereo different */ -/*^^^ 2009-12-05 */ -#define REQ_MODE_MIN_SB_RING_MASK 0x0F0000 /* RSB */ -#define REQ_MODE_MIN_SB_RING_SHFT 16 - -#define REQ_MODE_DEFAULT (REQ_MODE_BASIC | REQ_MODE_TAUT | REQ_MODE_ISO | REQ_MODE_NON_ISO | REQ_MODE_STEREO) - -#define WARN_FAILED_STEREO 0x0001 -#define WARN_FAILED_ISOTOPIC 0x0002 -#define WARN_FAILED_ISOTOPIC_STEREO 0x0004 -#define ERR_NO_CANON_RESULTS 0x0008 - -/*********** compare components flags **********************************/ -#define CMP_COMPONENTS 0x0001 /* perform compare components */ -#define CMP_COMPONENTS_NONISO 0x0002 /* ignore isotopic */ -#define CMP_COMPONENTS_NONTAUT 0x0004 /* compare non-tautomeric */ -/****************** chemical identifier member definitions *************/ -typedef struct tagINChI_IsotopicAtom { - AT_NUMB nAtomNumber; /* Canonical atom number */ - NUM_H nIsoDifference; /* 0=non-isotopic; 1=rounded avg. atomic mass */ - NUM_H nNum_H; /* number of 1H isotopic atoms attached */ - NUM_H nNum_D; /* number of 2H isotopic atoms attached */ - NUM_H nNum_T; /* number of 3H isotopic atoms attached */ -} INChI_IsotopicAtom; -typedef struct tagINChI_IsotopicTGroup { - AT_NUMB nTGroupNumber; /* Tautomeric group number */ - AT_NUMB nNum_H; /* number of 1H isotopic atoms */ - AT_NUMB nNum_D; /* number of 2H isotopic atoms */ - AT_NUMB nNum_T; /* number of 3H isotopic atoms */ -} INChI_IsotopicTGroup; -typedef struct tagINChI_Stereo { /* [N] = allocated length */ - /* ---- possibly tetrahedral stereogenic atoms */ - int nNumberOfStereoCenters; - AT_NUMB *nNumber; /* Canonical number of a possibly tetrahedral - * stereogenic atom or allenes [nNumberOfAtoms] */ - S_CHAR *t_parity; /* tetrahedral (relative, see nCompInv2Abs) atom parities [nNumberOfAtoms] */ - /* ---- possibly tetrahedral stereogenic atoms of the iverted structure */ - AT_NUMB *nNumberInv; /* Canonical number of a possibly tetrahedral - * stereogenic atom or allene [nNumberOfAtoms] */ - S_CHAR *t_parityInv; /* tetrahedral inverted atom parities [nNumberOfAtoms] */ - /* bFlagAbsStereoIsInverted = nCompInv2Abs==-1: Abs stereo = Inverted */ - int nCompInv2Abs; /* 0=>Inv = Abs stereo; -1=> Inv < Abs stereo, +1=> Inv > Abs stereo; +2=> in reading InChI: no /m was found and in /sN N>0 */ - int bTrivialInv; /* 1=> nCompInv2Abs!= 0 && Inverted = Abs stereo with inverted parities 1<-->2 */ - /* ---- possibly stereogenic bonds and tetrahedral cumuleles */ - int nNumberOfStereoBonds; - AT_NUMB *nBondAtom1; /* Canonical number of a first atom - * [number of bonds] */ - AT_NUMB *nBondAtom2; /* Canonical number of a second atom - * [number of bonds] */ - S_CHAR *b_parity; /* possibly stereogenic bond parities - * [number of bonds] */ -} INChI_Stereo; -#define INCHI_FLAG_ACID_TAUT 0x0001 /* tautomerism of dissociated acid invoked */ -#define INCHI_FLAG_REL_STEREO 0x0002 /* requested relative stereo */ -#define INCHI_FLAG_RAC_STEREO 0x0004 /* requested racemic stereo */ -#define INCHI_FLAG_SC_IGN_ALL_UU 0x0008 /* ignored all undefined/unknown stereocenters, non-isotopic */ -#define INCHI_FLAG_SB_IGN_ALL_UU 0x0010 /* ignored all undefined/unknown stereocenters, non-isotopic */ -#define INCHI_FLAG_SC_IGN_ALL_ISO_UU 0x0020 /* ignored all undefined/unknown stereocenters, isotopic */ -#define INCHI_FLAG_SB_IGN_ALL_ISO_UU 0x0040 /* ignored all undefined/unknown stereocenters, isotopic */ -#define INCHI_FLAG_HARD_ADD_REM_PROTON 0x0080 /* in normalization a proton has been added or removed along alt path */ - -#define INCHI_OUT_NO_AUX_INFO 0x0001 /* do not output Aux Info */ -#define INCHI_OUT_SHORT_AUX_INFO 0x0002 /* output short version of Aux Info */ -#define INCHI_OUT_ONLY_AUX_INFO 0x0004 /* output only Aux Info */ -#define INCHI_OUT_EMBED_REC 0x0008 /* embed reconnected INChI into disconnected INChI */ -#define INCHI_OUT_SDFILE_ONLY 0x0010 /* save input data in a Molfile instead of creating INChI */ -#define INCHI_OUT_XML 0x0020 /* output xml INChI */ -#define INCHI_OUT_PLAIN_TEXT 0x0040 /* output plain text INChI */ -#define INCHI_OUT_PLAIN_TEXT_COMMENTS 0x0080 /* output plain text annotation */ -#define INCHI_OUT_XML_TEXT_COMMENTS 0x0100 /* output xml text annotation */ -#define INCHI_OUT_WINCHI_WINDOW 0x0200 /* output into wINChI text window */ -#define INCHI_OUT_TABBED_OUTPUT 0x0400 /* tab-delimited (only for plain text) */ -#define INCHI_OUT_SDFILE_ATOMS_DT 0x0800 /* SDfile output H isotopes as D and T */ -#define INCHI_OUT_SDFILE_SPLIT 0x1000 /* Split SDfile into components */ - -#define INCHI_OUT_FIX_TRANSPOSITION_CHARGE_BUG 0x2000 - /* used to accomodate FIX_TRANSPOSITION_CHARGE_BUG */ - -#define INCHI_OUT_STDINCHI 0x4000 -#define INCHI_OUT_SAVEOPT 0x8000 - - -/* Bits encoding InChI creation options to be saved */ -#define SAVE_OPT_SLUUD 0x0001 -#define SAVE_OPT_SUU 0x0002 -#define SAVE_OPT_FIXEDH 0x0004 -#define SAVE_OPT_RECMET 0x0008 -#define SAVE_OPT_KET 0x0010 -#define SAVE_OPT_15T 0x0020 - - -#define INCHI_OUT_PRINT_OPTIONS (INCHI_OUT_EMBED_REC | \ - INCHI_OUT_XML | \ - INCHI_OUT_PLAIN_TEXT | \ - INCHI_OUT_PLAIN_TEXT_COMMENTS | \ - INCHI_OUT_XML_TEXT_COMMENTS) - - -/*******REQ_MODE_SB_IGN_ALL_UU*************** chemical identifier definition *****************/ -typedef struct tagINChI { /* [N] = allocated length */ - - int nErrorCode; /* 0 = success */ - INCHI_MODE nFlags; /* INCHI_FLAG_ACID_TAUT tautomerism of dissociated acid invoked - INCHI_FLAG_REL_STEREO requested relative stereo - INCHI_FLAG_RAC_STEREO requested racemic stereo - INCHI_FLAG_SC_IGN_ALL_UU ignored all undefined/unknown stereocenters, non-isotopic - INCHI_FLAG_SB_IGN_ALL_UU ignored all undefined/unknown stereocenters, non-isotopic - INCHI_FLAG_SC_IGN_ALL_ISO_UU ignored all undefined/unknown stereocenters, isotopic - INCHI_FLAG_SB_IGN_ALL_ISO_UU ignored all undefined/unknown stereocenters, isotopic - INCHI_FLAG_HARD_ADD_REM_PROTON in normalization a proton has been added or removed along alt path - */ - /* ---- basic & tautomer layer */ - int nTotalCharge; - int nNumberOfAtoms; - char *szHillFormula; - U_CHAR *nAtom; /* atomic numbers [nNumberOfAtoms] from the Periodic Table */ - int lenConnTable; - AT_NUMB *nConnTable; /* Connection table [nNumberOfAtoms+NumberOfBonds] */ - int lenTautomer; - AT_NUMB *nTautomer; /* NumGroups; ((NumAt+1, NumH, At1..AtNumAt),...); {INCHI_T_NUM_MOVABLE = 1} - old - * NumGroups; ((NumAt+2, NumH, Num(-), At1..AtNumAt),...); {INCHI_T_NUM_MOVABLE = 2} - new - * Allocated length: [5*nNumberOfAtoms/2+1], see Alloc_INChI(...) */ - S_CHAR *nNum_H; /* number of terminal hydrogen atoms on each atom; in tautomeric - * representation these H on tautomeric atoms are not included [nNumberOfAtoms] */ - S_CHAR *nNum_H_fixed;/* number of terminal hydrogen atoms on tautomeric atoms, - * in non-atautomeric representation only [nNumberOfAtoms] */ - /* ---- isotopic & isotopic tautomeric layer */ - int nNumberOfIsotopicAtoms; - INChI_IsotopicAtom *IsotopicAtom; /* [nNumberOfIsotopicAtoms] */ - int nNumberOfIsotopicTGroups; - /* in reversing InChI keeps a pointer to stolen from AuxInfo coordinates */ - INChI_IsotopicTGroup *IsotopicTGroup; /* [nNumberOfIsotopicAtoms] */ - /* ---- stereo layer */ - INChI_Stereo *Stereo; - INChI_Stereo *StereoIsotopic; - /* not including mobile H groups */ - AT_NUMB *nPossibleLocationsOfIsotopicH; /* [0]=> length including 0th element, location1,...*/ - int bDeleted; -#if ( bREUSE_INCHI == 1 ) - int nRefCount; -#endif -#if ( bRELEASE_VERSION == 0 ) - int bExtract; -#endif -#if ( READ_INCHI_STRING == 1 ) - int nLink; /* negative: ignore InChI; positive: index of (Reconnected component) + 1 linked to it */ -#endif -} INChI; - -typedef INChI *PINChI2[TAUT_NUM]; - -typedef struct tagOrigInfo { - S_CHAR cCharge; - S_CHAR cRadical; /* 0=none, 1=doublet, 2=triplet, 3=unknown */ - S_CHAR cUnusualValence; /* see get_unusual_el_valence() */ -} ORIG_INFO; -/******************** auxiliary chemical identifier info **************/ -typedef struct tagINChI_Aux { /* [N] = allocated length */ - - int nErrorCode; /* 0 = success */ - int nNumberOfAtoms; - int nNumberOfTGroups; /* non-zero only in tautomeric representation */ - int bIsIsotopic; /* filled out even though isotopic has not been requested */ - int bIsTautomeric; /* filled out even though tautomeric has not been requested; non-zero if taut exists */ - /* canonical numbers of the atoms: nOrigAtNosInCanonOrd[i-1]+1 = */ - /* input atom number for the canonical number i */ - AT_NUMB *nOrigAtNosInCanonOrd; /* [nNumberOfInputAtoms*1.5]; */ - AT_NUMB *nIsotopicOrigAtNosInCanonOrd; /* [nNumberOfInputAtoms*1.5]; */ - /* same for the inverted structure */ - AT_NUMB *nOrigAtNosInCanonOrdInv; /* inveterted stereo [nNumberOfInputAtoms*1.5]; */ - AT_NUMB *nIsotopicOrigAtNosInCanonOrdInv; /* [nNumberOfInputAtoms*1.5]; */ - AT_NUMB *nConstitEquNumbers; /* [nNumberOfAtoms*1.5] */ - AT_NUMB *nConstitEquTGroupNumbers; /* [nNumberOfAtoms/2] */ - AT_NUMB *nConstitEquIsotopicNumbers; /* [nNumberOfAtoms*1.5] */ - AT_NUMB *nConstitEquIsotopicTGroupNumbers; /* [nNumberOfAtoms/2] */ -#if ( bREUSE_INCHI == 1 ) - int nRefCount; -#endif -#if ( TEST_RENUMB_ATOMS == 1 ) - unsigned long ulNormTime; - unsigned long ulCanonTime; -#endif - - ORIG_INFO *OrigInfo; - MOL_COORD *szOrigCoord; - NUM_H nNumRemovedProtons; - NUM_H nNumRemovedIsotopicH[NUM_H_ISOTOPES]; /* isotopic H that may be exchanged and considered - randomly distributed, including removed protons; - order: 0=>1H, 1=>D, 2=>T */ - int bDeleted; - INCHI_MODE bTautFlags; /* t_group_info->bTautFlags */ - INCHI_MODE bTautFlagsDone; /* t_group_info->bTautFlagsDone */ - INCHI_MODE bNormalizationFlags; /* t_group_info->tni.bNormalizationFlags */ - int nCanonFlags; -} INChI_Aux; - -typedef INChI_Aux *PINChI_Aux2[TAUT_NUM]; - -/********************* array of pointers for sorting components and INChI output *********/ -typedef struct tagINChIforSort { - INChI *pINChI[TAUT_NUM]; - INChI_Aux *pINChI_Aux[TAUT_NUM]; - short ord_number; /* for stable sort */ - short n1; /* points to the original; used in structure reconstruction only */ - short n2; /* points to the original; used in structure reconstruction only */ - short n3; /* points to the original; used in structure reconstruction only */ -}INCHI_SORT; - -#endif /* __INCHI_H__ */ diff --git a/INCHI-1-SRC/INCHI/common/ichi_bns.c b/INCHI-1-SRC/INCHI/common/ichi_bns.c deleted file mode 100644 index 98bb2f9..0000000 --- a/INCHI-1-SRC/INCHI/common/ichi_bns.c +++ /dev/null @@ -1,10095 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - - -#include "mode.h" - -#include "ichierr.h" -#include "incomdef.h" -#include "inpdef.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "util.h" -#include "ichicomp.h" -#include "ichister.h" -#include "ichi_bns.h" - - -#define BNS_MARK_ONLY_BLOCKS 1 /* 1 => find only blocks, do not search for ring systems */ -#define ALLOW_ONLY_SIMPLE_ALT_PATH 0 /* 0 => allow alt. path to contain same bond 2 times (in opposite directions) */ -#define CHECK_TG_ALT_PATH 0 /* 1=> when chacking alt path of a tautomeric atom modify - t-group, not the atom */ - /* 0=> old mode */ - -#define FIX_CPOINT_BOND_CAP 1 /* 1=> fix bug in case of double bond from neutral cpoint */ -#define RESET_EDGE_FORBIDDEN_MASK 1 /* 1: previous; 0: do not apply "edge->forbidden &= pBNS->edge_forbidden_mask" */ -#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) -#define IS_FORBIDDEN(EDGE_FORBIDDEN, PBNS) (EDGE_FORBIDDEN) -#else -#define IS_FORBIDDEN(EDGE_FORBIDDEN, PBNS) (EDGE_FORBIDDEN & PBNS->edge_forbidden_mask) -#endif - - -typedef enum tagAtTypeTotals -{ - /* counts do not include: - charged atom adjacent to another charged atom - atom in an unusual valence state or adjacent to an atom in an unusual valence state - radicals different from singlet - */ - /*ATTOT_NUM_Plus. */ /* number of positive charges, +1, is (ATTOT_NUM_CHARGES + ATTOT_TOT_CHARGE)/2 */ - /*ATTOT_NUM_Minus.*/ /* number of negative charges, -1, is (ATTOT_NUM_CHARGES - ATTOT_TOT_CHARGE)/2 */ - ATTOT_NUM_NP_Plus, /* 0 no H: =N(+)=, #N(+)-, =N(+)<, does not include onium cations >P(+)<, >N(+)< */ - ATTOT_NUM_NP_Proton, /* 1 H(+): -NH3(+), =NH2(+), >NH2(+), =NH(+)-, >NH(+)-, #NH(+), N=N,P */ - ATTOT_NUM_NP_H, /* 2 H: -NH2, =NH, >NH -NH(-) */ - ATTOT_NUM_N_Minus, /* 3 (-): -NH(-), >N(-), =N(-) */ - ATTOT_NUM_NP, /* 4 no H: >N- =N-, #N */ - ATTOT_NUM_ON, /* 5 -N=O: do not allow -N=O => -NH-OH during H(+) add/removal */ - ATTOT_NUM_COH, /* 6 =C-OH, #C-OH; O=O,S,Se,Te */ - ATTOT_NUM_CSH, /* 7 -C-SH, -C-SeH -C-TeH */ - ATTOT_NUM_ZOH, /* 8 =Z-OH, #Z-OH; O=O,S,Se,Te; Z may have charge, Z != C */ - ATTOT_NUM_OOH, /* 9 -O-OH, O=O,S,Se,Te */ - ATTOT_NUM_ZOOH, /* 10 O=Z-OH, O=O,S,Se,Te */ - ATTOT_NUM_NOH, /* 11 =N-OH, -N(-)-OH */ - ATTOT_NUM_N_OH, /* 12 >N-OH, -NH-OH, >NH(+)-OH, -N(-)-OH */ - ATTOT_NUM_CO, /* 13 -C=O, =C=O; O=O,S,Se,Te */ - ATTOT_NUM_ZO, /* 14 -Z=O, =Z=O; O=O,S,Se,Te; Z may have charge */ - ATTOT_NUM_NO, /* 15 -N=O, =N(+)=O */ - ATTOT_NUM_N_O, /* 16 >N(+)=O, =N(+)=O */ - ATTOT_NUM_CO_Minus, /* 17 =C-O(-), #C-O(-); O=O,S,Se,Te */ - ATTOT_NUM_CS_Minus, /* 18 -C-S(-); S = S, Se, Te */ - ATTOT_NUM_ZO_Minus, /* 19 =Z-O(-), #Z-O(-); O = O, S, Se, Te */ - ATTOT_NUM_OO_Minus, /* 20 -O-O(-), O=O,S,Se,Te */ - ATTOT_NUM_ZOO_Minus, /* 21 O=Z-O(-), O=O,S,Se,Te */ - ATTOT_NUM_NO_Minus, /* 22 >N-O(-), -NH-O(-) */ - ATTOT_NUM_N_O_Minus, /* 23 -NH-O(-), >N-O(-); O = O, S, Se, Te */ - ATTOT_NUM_O_Minus, /* 24 -Z-O(-); O=O,S,Se,Te */ - ATTOT_NUM_OH_Plus, /* 25 any OH(+) */ - ATTOT_NUM_O_Plus, /* 26 any O(+) without H */ - ATTOT_NUM_Proton, /* 27 proton */ - ATTOT_NUM_HalAnion, /* 28 Halogen anion */ - ATTOT_NUM_HalAcid, /* 29 Halogen acid */ - ATTOT_NUM_Errors, /* 30 for debugging */ - ATTOT_TOT_CHARGE, /* 31 total of positive and negative single charges, +1 and -1 */ - ATTOT_NUM_CHARGES, /* 32 number of positive and negative single charges, +1 and -1 */ - ATTOT_ARRAY_LEN /* 33 array length */ -} AT_TYPE_TOTALS; - -#define ATBIT_NP_Plus (1 << ATTOT_NUM_NP_Plus) -#define ATBIT_NP_Proton (1 << ATTOT_NUM_NP_Proton) -#define ATBIT_NP_H (1 << ATTOT_NUM_NP_H) -#define ATBIT_N_Minus (1 << ATTOT_NUM_N_Minus) -#define ATBIT_NP (1 << ATTOT_NUM_NP) -#define ATBIT_ON (1 << ATTOT_NUM_ON) -#define ATBIT_COH (1 << ATTOT_NUM_COH) -#define ATBIT_CSH (1 << ATTOT_NUM_CSH) -#define ATBIT_ZOH (1 << ATTOT_NUM_ZOH) -#define ATBIT_OOH (1 << ATTOT_NUM_OOH) -#define ATBIT_ZOOH (1 << ATTOT_NUM_ZOOH) -#define ATBIT_NOH (1 << ATTOT_NUM_NOH) -#define ATBIT_N_OH (1 << ATTOT_NUM_N_OH) -#define ATBIT_CO (1 << ATTOT_NUM_CO) -#define ATBIT_ZO (1 << ATTOT_NUM_ZO) -#define ATBIT_NO (1 << ATTOT_NUM_NO) -#define ATBIT_N_O (1 << ATTOT_NUM_N_O) -#define ATBIT_CO_Minus (1 << ATTOT_NUM_CO_Minus) -#define ATBIT_CS_Minus (1 << ATTOT_NUM_CS_Minus) -#define ATBIT_ZO_Minus (1 << ATTOT_NUM_ZO_Minus) -#define ATBIT_OO_Minus (1 << ATTOT_NUM_OO_Minus) -#define ATBIT_ZOO_Minus (1 << ATTOT_NUM_ZOO_Minus) -#define ATBIT_NO_Minus (1 << ATTOT_NUM_NO_Minus) -#define ATBIT_N_O_Minus (1 << ATTOT_NUM_N_O_Minus) -#define ATBIT_O_Minus (1 << ATTOT_NUM_O_Minus) -#define ATBIT_OH_Plus (1 << ATTOT_NUM_OH_Plus) -#define ATBIT_O_Plus (1 << ATTOT_NUM_O_Plus) -#define ATBIT_Proton (1 << ATTOT_NUM_Proton) -#define ATBIT_HalAnion (1 << ATTOT_NUM_HalAnion) -#define ATBIT_HalAcid (1 << ATTOT_NUM_HalAcid) - - -#define ATBIT_Errors (1 << ATTOT_NUM_Errors) - -typedef struct tagProtonRemovalMaskAndType -{ - int typePos; /* atoms accessible to positive charges */ - int maskPos; - int typeNeg; /* atoms accessible to negative charges */ - int maskNeg; - int typeH; /* atoms accessible to hydrogen atoms */ - int maskH; -} PRMAT; - -#define PR_SIMPLE_MSK (ATBIT_NP_Proton | ATBIT_OH_Plus) -#define PR_SIMPLE_TYP (ATT_ATOM_N | ATT_ATOM_P | ATT_O_PLUS) - -#define ATBIT_MSK_NP (ATBIT_NP_Plus | ATBIT_NP_Proton | ATBIT_NP_H | ATBIT_N_Minus | ATBIT_NP) -#define KNOWN_ACIDIC_TYPE (ATT_ACIDIC_CO | ATT_ACIDIC_S | ATT_OO | ATT_ZOO | ATT_NO) -#define ATBIT_MSK_OS (ATBIT_COH | ATBIT_CSH | ATBIT_ZOH | ATBIT_OOH | ATBIT_ZOOH | ATBIT_NOH | ATBIT_N_OH |\ - ATBIT_CO | ATBIT_ZO | ATBIT_NO | ATBIT_N_O |\ - ATBIT_CO_Minus | ATBIT_CS_Minus | ATBIT_ZO_Minus | ATBIT_OO_Minus |\ - ATBIT_ZOO_Minus | ATBIT_NO_Minus | ATBIT_N_O_Minus /*| ATBIT_O_Minus*/ ) -#define ATBIT_MSK_H (ATBIT_NP_Proton | ATBIT_NP_H | ATBIT_COH | ATBIT_CSH | ATBIT_ZOH | ATBIT_OOH |\ - ATBIT_ZOOH | ATBIT_NOH | ATBIT_N_OH) - -#define ATTYP_OS (ATT_ACIDIC_CO | ATT_ACIDIC_S | ATT_OO | ATT_ZOO | ATT_NO /*| ATT_OTHER_NEG_O*/ | ATT_OTHER_ZO) -#define ATTYP_NP (ATT_ATOM_N | ATT_ATOM_P) -#define ATTYP_N (ATT_ATOM_N) -#define ATTYP_P (ATT_ATOM_P) - -/************* simple proton removal from acids **************************/ -#define AR_ANY_OH 0 /* 1 => create unknown to be acidic anions */ -#define AR_SIMPLE_STEPS 3 -/* acidic groups for proton removal, step 1 */ -#define AR_SIMPLE_MSK1 (ATBIT_COH | ATBIT_CSH | ATBIT_OOH | ATBIT_ZOOH | ATBIT_NOH | ATBIT_HalAcid) -#define AR_SIMPLE_TYP1 (ATT_ACIDIC_CO | ATT_ACIDIC_S | ATT_OO | ATT_ZOO | ATT_NO | ATT_HalAcid) -/* acidic groups for proton removal, step 2 */ -#define AR_SIMPLE_MSK2 (AR_ANY_OH? (ATBIT_N_OH) :0) -#define AR_SIMPLE_TYP2 (AR_ANY_OH? (ATT_N_O) :0) -/* acidic groups for proton removal, step 3 */ -#define AR_SIMPLE_MSK3 (AR_ANY_OH? (ATBIT_ZOH) :0) -#define AR_SIMPLE_TYP3 (AR_ANY_OH? (ATT_OTHER_ZO):0) - -/************* simple proton addition to acids **************************/ -#define AA_ANY_O_Minus 0 /* 1 => neutralize unknown to be acidic anions */ -#define AA_SIMPLE_STEPS 3 -/* acidic groups for proton addition, step 1 */ -#define AA_SIMPLE_MSK1 (ATBIT_CO_Minus | ATBIT_CS_Minus | ATBIT_OO_Minus | ATBIT_ZOO_Minus | ATBIT_NO_Minus | ATBIT_O_Minus | ATBIT_HalAnion) -#define AA_SIMPLE_TYP1 (ATT_ACIDIC_CO | ATT_ACIDIC_S | ATT_OO | ATT_ZOO | ATT_NO | ATT_OH_MINUS | ATT_HalAnion ) -/* acidic groups for proton addition, step 2 */ -#define AA_SIMPLE_MSK2 (AA_ANY_O_Minus? (ATBIT_N_O_Minus) :0) -#define AA_SIMPLE_TYP2 (AA_ANY_O_Minus? (ATT_N_O) :0) -/* acidic groups for proton addition, step 3 */ -#define AA_SIMPLE_MSK3 (AA_ANY_O_Minus? (ATBIT_ZO_Minus | ATBIT_O_Minus):0) -#define AA_SIMPLE_TYP3 (AA_ANY_O_Minus? (ATT_OTHER_ZO) :0) - -#if ( FIX_NP_MINUS_BUG == 1 ) -/* allow to add H(+) to =N(-) which previously was #N */ -#undef AA_SIMPLE_STEPS -#define AA_SIMPLE_STEPS 4 -#define AA_SIMPLE_MSK4 ATBIT_N_Minus -#define AA_SIMPLE_TYP4 ATT_NP_MINUS_V23 -#endif - -/************* hard proton removal from NP **************************/ -/* (+) charge group for proton removal: mask & type */ -#define PR_HARD_MSK_POS ATBIT_MSK_NP -#define PR_HARD_TYP_POS ATTYP_N -#define PR_HARD_TYP_POSP ATTYP_P -/* (-) charge group for proton removal */ -#define PR_HARD_MSK_NEG (ATBIT_MSK_NP | ATBIT_MSK_OS) -#define PR_HARD_TYP_NEG (ATTYP_N | ATTYP_OS) -/* H-group for proton removal */ -#define PR_HARD_MSK_H (ATBIT_MSK_NP | ATBIT_MSK_OS) -#define PR_HARD_TYP_H (ATTYP_N | ATTYP_OS) - -/************* hard proton removal from acids **************************/ -/* (+) charge group for proton removal: mask & type */ -#define AR_HARD_MSK_POS ATBIT_MSK_NP -#define AR_HARD_TYP_POS ATTYP_N -/* (-) charge group for proton removal */ -#define AR_HARD_MSK_NEG (ATBIT_MSK_NP | ATBIT_MSK_OS) -#define AR_HARD_TYP_NEG (ATTYP_N | ATTYP_OS) -/* H-group acid for proton removal */ -#define AR_HARD_MSK_HA (ATBIT_CO | ATBIT_NO ) -#define AR_HARD_TYP_HA (ATT_ACIDIC_CO | ATT_NO) -/* H-group non-acid for proton removal */ -#define AR_HARD_MSK_HN ((ATBIT_MSK_NP | ATBIT_MSK_OS) & ~AR_HARD_MSK_HA) -#define AR_HARD_TYP_HN ((ATTYP_N | ATTYP_OS) /*& ~AR_HARD_TYP_HA*/) - -/************* hard proton addition to acids **************************/ -/* (+) charge group for proton removal: mask & type */ -#define AA_HARD_MSK_POS ATBIT_MSK_NP -#define AA_HARD_TYP_POS ATTYP_N -/* (-) charge group for negative charge removal */ -#define AA_HARD_MSK_NEG ((ATBIT_MSK_NP | ATBIT_MSK_OS) & ~(ATBIT_CO | ATBIT_NO )) -#define AA_HARD_TYP_NEG (ATTYP_N | ATTYP_OS) -/* (-) charge group to accept negative charges */ -#define AA_HARD_MSK_CO (ATBIT_CO | ATBIT_NO ) -#define AA_HARD_TYP_CO (ATT_ACIDIC_CO | ATT_NO) -/* H-group non-acid for proton removal */ -#define AA_HARD_MSK_H (ATBIT_MSK_NP | ATBIT_MSK_OS) -#define AA_HARD_TYP_H (ATTYP_N | ATTYP_OS) - - -/*****************************************************************************/ -#define BNS_MAX_NUM_FLOW_CHANGES (1+2*MAX_BOND_EDGE_CAP) - -/* -- opiginal Pascal values -- -#define NO_VERTEX 0 -#define BLOSSOM_BASE -1 -#define FIRST_INDX 1 -*/ - -#define TREE_NOT_IN_M 0 /* not in T or T' */ -#define TREE_IN_2 1 /* in T' and not s-reachable */ -#define TREE_IN_2BLOSS 2 /* in T' and in a blossom, is s-reachable */ -#define TREE_IN_1 3 /* in T and is s-reachable */ - -#define TREE_IS_S_REACHABLE(X) (Tree[X] >= TREE_IN_2BLOSS) -#define TREE_IS_ON_SCANQ TREE_IS_S_REACHABLE -/* #define TREE_IS_ON_SCANQ(X) (Tree[X] != TREE_NOT_IN_M) */ -#define TREE_MARK(X, MARK) do{ if( Tree[X] < MARK ) Tree[X]=MARK; }while(0) - - -/***************************************************************************** - * store changes done to check whether an alternating path exists - * (see bSetBnsToCheckAltPath, bRestoreBnsAfterCheckAltPath) -******************************************************************************/ -typedef struct tagAltPathChanges -{ - /* caps changed in up to 2 vertices */ - VertexFlow nOldCapsVert[2][MAXVAL+1]; - Vertex vOldVert[2]; - S_CHAR bSetOldCapsVert[2]; /* number of caps to restore, including st-cap */ - /* save ids of the newly created temporary vertices */ - Vertex vNewVertex[2]; - S_CHAR bSetNew[2]; /* indicators whether to remove vertices */ - -} ALT_PATH_CHANGES; - -/*****************************************************************************/ - -/* Local functions */ - -int RestoreRadicalsOnly( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at ); -int bRadChangesAtomType( BN_STRUCT *pBNS, BN_DATA *pBD, Vertex v, Vertex v_1, Vertex v_2 ); -int BnsAdjustFlowBondsRad( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at, int num_atoms ); -int SetAtomRadAndChemValFromVertexCapFlow( BN_STRUCT *pBNS, inp_ATOM *atom, int v1 ); -int bNeedToTestTheFlow( int bond_type, int nTestFlow, int bTestForNonStereoBond ); -int RestoreEdgeFlow( BNS_EDGE *edge, int delta, int bChangeFlow ); -int SetAtomBondType( BNS_EDGE *edge, U_CHAR *bond_type12, U_CHAR *bond_type21, int delta, int bChangeFlow ); -int RestoreBnStructFlow( BN_STRUCT *pBNS, int bChangeFlow ); -int CompTGroupNumber( const void *tg1, const void *tg2 ); -int CompCGroupNumber( const void *cg1, const void *cg2 ); - -/* Rings, Blocks, Non-stereo bonds */ -int ReInitBnStructForAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bUnknAltAsNoStereo ); -int MarkRingSystemsAltBns( BN_STRUCT* pBNS, int bUnknAltAsNoStereo ); -int MarkNonStereoAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bUnknAltAsNoStereo ); - -/* Called from BalancedNetworkSearch */ -int GetVertexDegree( BN_STRUCT* pBNS, Vertex v ); -/* Vertex Get2ndNeighbor1( BN_STRUCT* pBNS, Vertex u, EdgeIndex iedge ); not used */ -Vertex Get2ndEdgeVertex( BN_STRUCT* pBNS, Edge uv ); -Vertex GetVertexNeighbor( BN_STRUCT* pBNS, Vertex v, int neigh, EdgeIndex *iedge ); -int GetEdgePointer( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv, BNS_EDGE **uv, S_CHAR *s_or_t ); -int AugmentEdge( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv, int delta, S_CHAR bReverse, int bChangeFlow ); -int rescap_mark( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv ); -int rescap( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv ); -Vertex FindBase( Vertex u, Vertex *BasePtr ); -int FindPathToVertex_s( Vertex x, Edge *SwitchEdge, Vertex *BasePtr, Vertex *Path, int MaxPathLen ); -Vertex MakeBlossom( BN_STRUCT* pBNS, Vertex *ScanQ, int *pQSize, - Vertex *Pu, Vertex *Pv, int max_len_Pu_Pv, - Edge *SwitchEdge, Vertex *BasePtr, - Vertex u, Vertex v, EdgeIndex iuv, Vertex b_u, Vertex b_v, S_CHAR *Tree ); -int PullFlow( BN_STRUCT *pBNS, Edge *SwitchEdge, Vertex x, Vertex y, int delta, S_CHAR bReverse, int bChangeFlow ); -int FindPathCap( BN_STRUCT* pBNS, Edge *SwitchEdge, Vertex x, Vertex y, int delta ); - -/* -int SetBondType( BNS_EDGE *edge, U_CHAR *bond_type12, U_CHAR *bond_type21, int delta, int bChangeFlow ); -int SetBondsRestoreBnStructFlow( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bChangeFlow ); -*/ -int SetBondsFromBnStructFlow( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bChangeFlow0 ); -int MarkAtomsAtTautGroups( BN_STRUCT *pBNS, int num_atoms, BN_AATG *pAATG, int nEnd1, int nEnd2 ); - -int nMinFlow2Check( BN_STRUCT *pBNS, int iedge ); -int nMaxFlow2Check( BN_STRUCT *pBNS, int iedge ); -int nCurFlow2Check( BN_STRUCT *pBNS, int iedge ); - -/* Bonds testing */ -/* -int bRestoreFlowToCheckOneBond( BN_STRUCT *pBNS, BNS_FLOW_CHANGES *fcd, int nTestFlow, inp_ATOM *at, int num_atoms, int bChangeFlow ); -*/ -int bSetFlowToCheckOneBond( BN_STRUCT *pBNS, int iedge, int flow, BNS_FLOW_CHANGES *fcd ); -int bRestoreFlowAfterCheckOneBond( BN_STRUCT *pBNS, BNS_FLOW_CHANGES *fcd ); -int bSetBondsAfterCheckOneBond( BN_STRUCT *pBNS, BNS_FLOW_CHANGES *fcd, int nTestFlow, inp_ATOM *at, int num_atoms, int bChangeFlow ); -int BnsTestAndMarkAltBonds( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at, int num_atoms, BNS_FLOW_CHANGES *fcd, int bChangeFlow, int nBondTypeToTest ); -int bIsAltBond( int bond_type ); - -/* Fix bonds */ -int fix_special_bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int edge_forbidden_mask ); -int TempFix_NH_NH_Bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms ); -int CorrectFixing_NH_NH_Bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms ); - -/* Alt path testing */ -int bSetBnsToCheckAltPath( BN_STRUCT *pBNS, int nVertDoubleBond, int nVertSingleBond, AT_NUMB type, - int path_type, ALT_PATH_CHANGES *apc, BNS_FLOW_CHANGES *fcd, int *nDots ); -int bRestoreBnsAfterCheckAltPath( BN_STRUCT *pBNS, ALT_PATH_CHANGES *apc, int bChangeFlow ); -Vertex GetGroupVertex(BN_STRUCT *pBNS, Vertex v1, AT_NUMB type); -BNS_IEDGE GetEdgeToGroupVertex( BN_STRUCT *pBNS, Vertex v1, AT_NUMB type); -int bAddNewVertex( BN_STRUCT *pBNS, int nVertDoubleBond, int nCap, int nFlow, int nMaxAdjEdges, int *nDots ); -int AddNewEdge( BNS_VERTEX *p1, BNS_VERTEX *p2, BN_STRUCT *pBNS, int nEdgeCap, int nEdgeFlow ); -int bAddStCapToAVertex( BN_STRUCT *pBNS, Vertex v1, Vertex v2, VertexFlow *nOldCapVertSingleBond, int *nDots, int bAdjacentDonors ); - -static void remove_alt_bond_marks(inp_ATOM *at, int num_atoms); -int bIsBnsEndpoint( BN_STRUCT *pBNS, int v ); - -/* Protons removal, charge neutralization */ -int is_acidic_CO( inp_ATOM *atom, int at_no ); -int mark_at_type( inp_ATOM *atom, int num_atoms, int nAtTypeTotals[] ); -int GetAtomChargeType( inp_ATOM *atom, int at_no, int nAtTypeTotals[], int *pMask, int bSubtract ); -int AddChangedAtHChargeBNS( inp_ATOM *at, int num_atoms, int nAtTypeTotals[], S_CHAR *mark ); -int EliminatePlusMinusChargeAmbiguity( BN_STRUCT *pBNS, int num_atoms ); -int AddOrRemoveExplOrImplH( int nDelta, inp_ATOM *at, int num_atoms, AT_NUMB at_no, T_GROUP_INFO *t_group_info ); -int SubtractOrChangeAtHChargeBNS( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, - int nAtTypeTotals[], S_CHAR *mark, T_GROUP_INFO *t_group_info, int bSubtract ); -int is_Z_atom( U_CHAR el_number ); -int IsZOX( inp_ATOM *atom, int at_x, int ord ); -int SimpleRemoveHplusNPO( inp_ATOM *at, int num_atoms, int nAtTypeTotals[], T_GROUP_INFO *t_group_info ); -int CreateCGroupInBnStruct( inp_ATOM *at, int num_atoms, - BN_STRUCT *pBNS, int nType, int nMask, int nCharge ); -int CreateTGroupInBnStruct( inp_ATOM *at, int num_atoms, - BN_STRUCT *pBNS, int nType, int nMask ); -int RemoveLastGroupFromBnStruct( inp_ATOM *at, int num_atoms, int tg, BN_STRUCT *pBNS ); -int SetInitCapFlowToCurrent( BN_STRUCT *pBNS ); -int SimpleRemoveAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2remove ); -int SimpleAddAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2add ); -int HardRemoveAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2remove, - int *nNumCanceledCharges, BN_STRUCT *pBNS, BN_DATA *pBD ); -int HardAddAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2add, - int *nNumCanceledCharges, BN_STRUCT *pBNS, BN_DATA *pBD ); -int HardRemoveHplusNP( inp_ATOM *at, int num_atoms, int bCancelChargesAlways, int *nNumCanceledCharges, - BN_AATG *pAATG, BN_STRUCT *pBNS, BN_DATA *pBD ); -int RemoveNPProtonsAndAcidCharges( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, BN_STRUCT *pBNS, BN_DATA *pBD ); -Vertex GetPrevVertex( BN_STRUCT* pBNS, Vertex y, Edge *SwitchEdge, EdgeIndex *iuv ); -int bIgnoreVertexNonTACN_atom( BN_STRUCT* pBNS, Vertex u, Vertex v ); -int bIgnoreVertexNonTACN_group( BN_STRUCT* pBNS, Vertex v, Vertex w, Edge *SwitchEdge ); -int bIsRemovedHfromNHaion( BN_STRUCT* pBNS, Vertex u, Vertex v ); -int bIsAggressiveDeprotonation( BN_STRUCT* pBNS, Vertex v, Vertex w, Edge *SwitchEdge ); - -int bIsAtomTypeHard( inp_ATOM *at, int endpoint, int nType, int nMask, int nCharge ); -int bIsHDonorAccAtomType( inp_ATOM *at, int endpoint, int *cSubType ); -int bIsNegAtomType( inp_ATOM *at, int i, int *cSubType ); - -#if ( BNS_RAD_SEARCH == 1 ) -int RegisterRadEndpoint( BN_STRUCT *pBNS, BN_DATA *pBD, Vertex u); -int cmp_rad_endpoints( const void *a1, const void *a2 ); -int cmp_endpoints_rad( const void *a1, const void *a2 ); -#endif - -int bHasChargedNeighbor( inp_ATOM *at, int iat ); -/*****************************************************************************/ -/**** prim(v) is v' *****/ -#define prim(v) (Vertex)((v)^1) - -/*****************************************************************************/ -#define SwitchEdge_Vert1(u) SwitchEdge[u][0] -#define SwitchEdge_Vert2(u) Get2ndEdgeVertex( pBNS, SwitchEdge[u] ) -#define SwitchEdge_IEdge(u) SwitchEdge[u][1] -/*****************************************************************************/ - - - - - -/*****************************************************************************/ -/* Returns value > 0 if a bond has been changed */ -/*****************************************************************************/ -int RestoreEdgeFlow( BNS_EDGE *edge, int delta, int bChangeFlow ) -{ - /*flow1 = edge->flow;*/ /* output from BNS */ - switch ( bChangeFlow & BNS_EF_CHNG_RSTR ) { - case 0: /* the flow has not been permitted to change inside the BNS */ - /* nothing to do */ - /*flow1 = edge->flow;*/ /* output from BNS, the original flow value */ - /*flow2 = flow1 + delta;*/ /* the flow would be changed to this value by the BNS if permitted */ - break; - case BNS_EF_CHNG_FLOW: /* the flow has been changed by the BNS; update flow0 */ - /*flow2 = edge->flow;*/ /* output from BNS, the changed value */ - /*flow1 = flow2 - delta;*/ /* the original flow value before the BNS */ - edge->flow0 = edge->flow; /* SAVE NEW EDGE FLOW AS THE INITIAL FLOW FROM CHEM. BONDS */ - break; - case BNS_EF_CHNG_RSTR: /* the flow has been changed by the BNS; requested to change it back */ - /*flow2 = edge->flow;*/ /* output from BNS, the changed value */ - /*flow1 = flow2 - delta;*/ /* the original flow value before the BNS */ - edge->flow = edge->flow-delta; /* CHANGE EDGE FLOW BACK (RESTORE) */ - break; - case BNS_EF_RSTR_FLOW: /* the flow has not been permitted to change inside the BNS */ - /* nothing to do */ - /*flow1 = edge->flow;*/ /* output from BNS, the original flow value */ - /*flow2 = flow1 + delta;*/ /* the flow would be changed to this value by the BNS if permitted */ - break; - } - return 0; -} - - - -/*****************************************************************************/ -/* Returns value > 0 if a bond has been changed; do not change flow */ -/*****************************************************************************/ -int SetAtomBondType( BNS_EDGE *edge, U_CHAR *bond_type12, U_CHAR *bond_type21, int delta, int bChangeFlow ) -{ - int flow1, flow2, tmp, ret = 0; - int bond_mark, bond_type, new_bond_type; - - if ( !edge->pass || !bond_type21 ) - return 0; - - switch ( bChangeFlow & BNS_EF_CHNG_RSTR ) { - case 0: /* the flow has not been permitted to change inside the BNS: simulated in case of check one bond */ - case BNS_EF_RSTR_FLOW: /* the flow has not been permitted to change inside the BNS: obsolete mode, unexpected bChangeFlow */ - flow1 = edge->flow0; /* output from BNS, the original (old) flow value */ - flow2 = flow1 + delta; /* the flow would be changed to this value by the BNS if permitted */ - break; - case BNS_EF_CHNG_FLOW: /* the flow has been changed by the BNS */ - case BNS_EF_CHNG_RSTR: /* the flow has been changed by the BNS; requested to change it back */ - flow2 = edge->flow; /* output from BNS, the changed (new) value */ - flow1 = edge->flow0; /* the original flow (old) value before the BNS */ - break; - default: - return 0; /* added 2006-03-21 */ - } - - if ( (bChangeFlow & BNS_EF_CHNG_BONDS) && (bChangeFlow & BNS_EF_ALTR_NS) !=BNS_EF_ALTR_NS ) { - /* set new bond types according to the new flow values */ - new_bond_type = flow2+BOND_SINGLE; - if ( *bond_type12 != new_bond_type ) { - *bond_type12 = *bond_type21 = new_bond_type; - ret ++; - } - } else - if ( bChangeFlow & BNS_EF_ALTR_BONDS ) { - if ( flow1 == flow2 ) { - goto exit_function; - } - /* update alternating bond information */ - if ( flow1 > flow2 ) { - /* make sure flow2 > flow1 */ - tmp = flow1; - flow1 = flow2; - flow2 = tmp; - } - bond_mark = 0; - switch( bond_type = (*bond_type12 & BOND_TYPE_MASK) ) { - - case BOND_SINGLE: - case BOND_DOUBLE: - case BOND_TRIPLE: - /* assume that the input bond type fits either flow1 or flow2 */ - if ( flow1 == 0 && flow2 == 1 ) { - if ( bChangeFlow & BNS_EF_SET_NOSTEREO ) { - bond_mark = BOND_MARK_ALT12NS; - bond_type = BOND_ALT12NS; - } else { - bond_mark = BOND_MARK_ALT12; - bond_type = BOND_ALTERN; - } - } else - if ( flow1 == 0 && flow2 == 2 ) { - bond_mark = BOND_MARK_ALT13; - bond_type = BOND_ALT_13; - } else - if ( flow1 == 1 && flow2 == 2 ) { - bond_mark = BOND_MARK_ALT23; - bond_type = BOND_ALT_23; - } else { - return BNS_BOND_ERR; /* error */ - } - break; - case BOND_TAUTOM: - if ( flow1 == 0 && flow2 == 1 ) { - bond_mark = BOND_MARK_ALT12NS; - } else { - return BNS_BOND_ERR; /* error */ - } - break; - - default: - new_bond_type = bond_type; - bond_mark = (*bond_type12 & BOND_MARK_MASK); - switch( bond_mark ) { - case BOND_MARK_ALT12: - if ( (bChangeFlow & BNS_EF_SET_NOSTEREO) && flow1 == 0 && flow2 == 1 ) { - bond_mark = BOND_MARK_ALT12NS; - new_bond_type = BOND_ALT12NS; - break; - } - case BOND_MARK_ALT12NS: - if ( flow1 == 2 || flow2 == 2 ) { - bond_mark = BOND_MARK_ALT123; - new_bond_type = BOND_ALT_123; - } - break; - case BOND_MARK_ALT13: - if ( flow1 == 1 || flow2 == 1 ) { - bond_mark = BOND_MARK_ALT123; - new_bond_type = BOND_ALT_123; - } - break; - case BOND_MARK_ALT23: - if ( flow1 == 0 || flow2 == 0 ) { - bond_mark = BOND_MARK_ALT123; - new_bond_type = BOND_ALT_123; - } - break; - case BOND_MARK_ALT123: - break; - - case 0: /* special case: second alt bond testing */ - if ( flow1 == 0 && flow2 == 1 ) { - bond_mark = BOND_MARK_ALT12; - } else - if ( flow1 == 0 && flow2 == 2 ) { - bond_mark = BOND_MARK_ALT13; - } else - if ( flow1 == 1 && flow2 == 2 ) { - bond_mark = BOND_MARK_ALT23; - } else { - return BNS_BOND_ERR; /* error */ - } - break; - - - default: - return BNS_BOND_ERR; /* error */ - } - switch( bond_type ) { - case BOND_TAUTOM: - break; - case BOND_ALTERN: - case BOND_ALT12NS: - case BOND_ALT_123: - case BOND_ALT_13: - case BOND_ALT_23: - bond_type = new_bond_type; - break; - default: - return BNS_BOND_ERR; /* error */ - } - } - new_bond_type = bond_type | bond_mark; - if ( new_bond_type != *bond_type12 ) { - *bond_type12 = *bond_type21 = new_bond_type; - ret ++; - } - } -exit_function: - return ret; -} - - - -/*****************************************************************************/ -int RunBalancedNetworkSearch( BN_STRUCT *pBNS, BN_DATA *pBD, int bChangeFlow ) -{ - /* Run BNS until no aug pass is found */ - int pass, delta=0, nSumDelta; - nSumDelta = 0; - for ( pass = 0; pass < pBNS->max_altp; pass ++ ) { - pBNS->alt_path = pBNS->altp[pass]; - pBNS->bChangeFlow = 0; - delta=BalancedNetworkSearch ( pBNS, pBD, bChangeFlow ); - ReInitBnData( pBD ); - if ( 0 < delta ) { - pBNS->num_altp ++; - nSumDelta += abs( delta ); - } else { - break; - } - } - if ( IS_BNS_ERROR(delta) ) - return delta; - return nSumDelta; /* number of eliminated pairs of "dots" */ -} - - - -/*****************************************************************************/ -int SetAtomRadAndChemValFromVertexCapFlow( BN_STRUCT *pBNS, inp_ATOM *atom, int v1 ) -{ - BNS_VERTEX *vert = pBNS->vert + v1; - inp_ATOM *at = atom + v1; - S_CHAR cValue; - int nChanges = 0; - /* set only on the 1st pass */ - if ( !vert->st_edge.pass ) { - return 0; - } - /* adjust chem_bonds_valence */ - cValue = at->chem_bonds_valence - at->valence; - if ( cValue >= 0 && cValue != vert->st_edge.flow ) { - at->chem_bonds_valence = at->valence + vert->st_edge.flow; - nChanges ++; - } - /* adjast radical */ - switch ( vert->st_edge.cap - vert->st_edge.flow ) { - case 0: - cValue = 0; - break; - case 1: - cValue = RADICAL_DOUBLET; - break; - case 2: - cValue = RADICAL_TRIPLET; - break; - default: - return BNS_BOND_ERR; - } - if ( cValue != at->radical ) { - at->radical = cValue; - nChanges ++; - } - - return nChanges; -} - - - -/*****************************************************************************/ -int AddChangedAtHChargeBNS( inp_ATOM *at, int num_atoms, int nAtTypeTotals[], S_CHAR *mark ) -{ - int i, mask, num; - for ( i = 0, num = 0; i < num_atoms; i ++ ) { - if ( mark[i] ) { - mark[i] = 0; -#if ( FIX_NORM_BUG_ADD_ION_PAIR == 1 ) - /* add ignoring adjacent charges */ - at[i].at_type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, -2 ); -#else - at[i].at_type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); -#endif - num ++; - } - } - return num; -} - - - -/************************************************************************************/ -/* Eliminate neutral representation ambiguity: replace (+)--N==(-) with (+)==N--(-) */ -/* here (+) is positive charge group, (-) is negative charge group, N is N or P */ -/* this reduces possibility of ion pair -OH => -O(+) + H(+) creation instead of */ -/* removing H(+) from N or P */ -/* */ -/*==== Call this function after alt path was found and new flows have been set. ====*/ -/* */ -/************************************************************************************/ -int EliminatePlusMinusChargeAmbiguity( BN_STRUCT *pBNS, int num_atoms ) -{ - int pass, i, v0, v1, v2, ineigh1, /*ineigh0,*/ /*ineigh2,*/ vLast, n, delta, ret, err = 0; - BNS_EDGE *edge; - int nFound, k; - - for ( pass = pBNS->num_altp-1, ret = 0; 0 <= pass; pass -- ) { - - pBNS->alt_path = pBNS->altp[pass]; - v1 = ALTP_START_ATOM(pBNS->alt_path); - n = ALTP_PATH_LEN(pBNS->alt_path); - delta = ALTP_DELTA(pBNS->alt_path); - vLast = ALTP_END_ATOM(pBNS->alt_path); - v0 = v2 = NO_VERTEX; /* negative number */ - - for ( i = 0; i < n; i ++, delta = -delta, v0 = v1, v1 = v2 /*, ineigh0 = ineigh1*/ ) { - ineigh1 = ALTP_THIS_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v1->v2 neighbor */ - /*ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i);*/ /* v2->v1 neighbor */ - edge = pBNS->edge + pBNS->vert[v1].iedge[ineigh1]; - /* follow the BN Structure, not the inp_ATOM, to take care of swithching to - t-groups, c-groups or other fictitious edges/vertices - */ - v2 = edge->neighbor12 ^ v1; - if ( v1 < num_atoms && - ( v0 >= num_atoms && ( pBNS->vert[v0].type & BNS_VERT_TYPE_C_GROUP ) || - v2 >= num_atoms && ( pBNS->vert[v2].type & BNS_VERT_TYPE_C_GROUP ) ) ) { - int cgPos, cgNeg; - int neighPos = -1, neighNeg = -1; - BNS_EDGE *edgePos, *edgeNeg; - nFound = 0; - for ( k = pBNS->vert[v1].num_adj_edges-1; k >= 0 && (neighPos < 0 || neighNeg < 0); k -- ) { - BNS_EDGE *next_edge = pBNS->edge + pBNS->vert[v1].iedge[k]; - int v = next_edge->neighbor12 ^ v1; - if ( pBNS->vert[v].type & BNS_VERT_TYPE_C_GROUP ) { - if ( pBNS->vert[v].type & BNS_VERT_TYPE_C_NEGATIVE ) { - cgNeg = v; - neighNeg = k; - nFound ++; - } else { - cgPos = v; - neighPos = k; - nFound ++; - } - } - } - if ( 2 == nFound && neighPos >= 0 && neighNeg >= 0 ) { - /* both c-groups have been found */ - edgePos = pBNS->edge + pBNS->vert[v1].iedge[neighPos]; - edgeNeg = pBNS->edge + pBNS->vert[v1].iedge[neighNeg]; - if ( edgePos->flow < edgeNeg->flow ) { - /* ambiguity found; replace (+cg)--N==(-cg) with (+cg)==N--(-cg) */ - int dflow = edgeNeg->flow - edgePos->flow; - - edgePos->flow += dflow; - pBNS->vert[cgPos].st_edge.cap += dflow; - pBNS->vert[cgPos].st_edge.flow += dflow; - - edgeNeg->flow -= dflow; - pBNS->vert[cgNeg].st_edge.cap -= dflow; - pBNS->vert[cgNeg].st_edge.flow -= dflow; - ret ++; - } - } - } - } - - if ( v2 != vLast ) { - err = BNS_PROGRAM_ERR; - } - } - return err? err : ret; -} - - - -/*********************************************************************************/ -int AddOrRemoveExplOrImplH( int nDelta, inp_ATOM *at, int num_atoms, AT_NUMB at_no, T_GROUP_INFO *t_group_info ) -{ - int i, iso, tot_num_iso_H, - num_H, /* number of H before the removal, including explicit H */ - nNum2Remove, /*number of H to remove */ - nNumRemovedExplicitH, - nNumExplicit2Implicit; - S_CHAR num_iso_H[NUM_H_ISOTOPES]; - inp_ATOM *at_H; - - if ( !nDelta ) { - return 0; - } - /* add */ - if ( nDelta > 0 ) { - at[at_no].num_H += nDelta; - t_group_info->tni.nNumRemovedProtons --; - return nDelta; - } - /* remove */ - nNum2Remove = -nDelta; - nNumRemovedExplicitH = t_group_info->tni.nNumRemovedExplicitH; /* number of explicit H saved separately in - at[num_atoms+i], i=0..nNumRemovedExplicitH-1 */ - tot_num_iso_H = NUM_ISO_H(at,at_no); - num_H = at[at_no].num_H; - /* - tot_num_iso_H = NUM_ISO_H(at,at_no); - num_H = at[at_no].num_H; - nNumAtomExplicitH = 0; - nNumRemovedExplicitH = t_group_info->tni.nNumRemovedExplicitH; - tot_num_explicit_iso_H = 0; - */ - at_H = at + num_atoms; - memcpy( num_iso_H, at[at_no].num_iso_H, sizeof(num_iso_H)); - /* Remove all explicit H, otherwise a false stereo can occur. - Example: remove H(+) from the following substructure: - - H H - A / A / - >X==N(+) produces false stereogenic bond: >X==N - B \ B - H - - To avoid this effect all explicit H atoms must be removed - */ - nNumExplicit2Implicit = 0; - for ( i = 0; i < nNumRemovedExplicitH; ) { - if ( at_H[i].neighbor[0] == at_no ) { - int m, k, orig_no = at_H[i].orig_at_number; - nNumRemovedExplicitH --; - nNumExplicit2Implicit ++; - if ( nNumRemovedExplicitH > i ) { - inp_ATOM at_i = at_H[i]; - memmove( at_H+i, at_H+i+1, sizeof(at_H[0])*(nNumRemovedExplicitH-i) ); - at_H[nNumRemovedExplicitH] = at_i; /* save removed H (for debugging purposes?) */ - } - /* adjust 0D parities */ - if ( at[at_no].sb_parity[0] ) { - for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[at_no].sb_parity[m]; m ++ ) { - if ( at[at_no].sn_orig_at_num[m] == orig_no ) { -#ifdef _DEBUG - if ( at[at_no].sn_ord[m] >= 0 ) { - int stop = 1; /* sb maintenance error */ - } -#endif - if ( at[at_no].valence >= MIN_NUM_STEREO_BOND_NEIGH ) { - at[at_no].sn_ord[m] = k = (at[at_no].sb_ord[m]==0); - at[at_no].sn_orig_at_num[m] = at[(int)at[at_no].neighbor[k]].orig_at_number; - if ( ATOM_PARITY_WELL_DEF( at[at_no].sb_parity[m] ) ) { - at[at_no].sb_parity[m] = 3 - at[at_no].sb_parity[m]; - } - } else { - at[at_no].sn_ord[m] = -99; /* no sb neighbor exists anymore */ - at[at_no].sn_orig_at_num[m] = 0; - if ( ATOM_PARITY_WELL_DEF( at[at_no].sb_parity[m] ) ) { - int pnxt_atom, pinxt2cur, pinxt_sb_parity_ord; - if ( 0 < get_opposite_sb_atom( at, at_no, at[at_no].sb_ord[m], - &pnxt_atom, &pinxt2cur, &pinxt_sb_parity_ord ) ) { - at[at_no].sb_parity[m] = - at[pnxt_atom].sb_parity[pinxt_sb_parity_ord] = AB_PARITY_UNDF; - } -#ifdef _DEBUG - else { - int stop = 1; /* sb maintenance error */ - } -#endif - } - } - } - } - } - /* do not increment i here: we have shifted next at_H[] element - to the ith position and decremented nNumRemovedExplicitH */ - } else { - i ++; - } - } - - for ( iso = -1; iso < NUM_H_ISOTOPES && 0 < nNum2Remove; iso ++ ) { - /* each pass removes up to one H */ - if ( iso < 0 ) { - /* try to remove non-isotopic */ - while ( tot_num_iso_H < num_H && 0 < nNum2Remove ) { - /* non-isotopic H exists */ - num_H --; - t_group_info->tni.nNumRemovedProtons ++; - nNum2Remove --; - } - } else { - /* remove isotopic */ - while ( num_iso_H[iso] && num_H && 0 < nNum2Remove ) { - /* isotopic H exists */ - num_H --; - num_iso_H[iso] --; - t_group_info->tni.nNumRemovedProtonsIsotopic[iso] ++; - t_group_info->tni.nNumRemovedProtons ++; - nNum2Remove --; - } - } - } -#if ( bRELEASE_VERSION != 1 ) - if ( nNum2Remove ) { - int stop = 1; /* Program error */ - } -#endif - if ( nDelta + nNum2Remove < 0 ) { - at[at_no].num_H = num_H; - memcpy( at[at_no].num_iso_H, num_iso_H, sizeof(at[0].num_iso_H)); - t_group_info->tni.nNumRemovedExplicitH = nNumRemovedExplicitH; - } - return nDelta + nNum2Remove; -} - - - -/*********************************************************************************/ -int SubtractOrChangeAtHChargeBNS( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, - int nAtTypeTotals[], S_CHAR *mark, T_GROUP_INFO *t_group_info, int bSubtract ) -{ - int pass, i, v0, v1, v2, ineigh1, /*ineigh2,*/ vLast, n, delta, ret, err = 0; - BNS_EDGE *edge; - int nDeltaH, nDeltaCharge; - int mask, type; - - for ( pass = pBNS->num_altp-1, ret = 0; 0 <= pass; pass -- ) { - - pBNS->alt_path = pBNS->altp[pass]; - v1 = ALTP_START_ATOM(pBNS->alt_path); - n = ALTP_PATH_LEN(pBNS->alt_path); - delta = ALTP_DELTA(pBNS->alt_path); - vLast = ALTP_END_ATOM(pBNS->alt_path); - v0 = v2 = NO_VERTEX; - - for ( i = 0; i < n; i ++, delta = -delta, v0 = v1, v1 = v2 ) { - ineigh1 = ALTP_THIS_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v1->v2 neighbor */ - /*ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i);*/ /* v2->v1 neighbor */ - edge = pBNS->edge + pBNS->vert[v1].iedge[ineigh1]; - /* follow the BN Structure, not the inp_ATOM, to take care of swithching to - t-groups, c-groups or other fictitious edges/vertices - */ - v2 = edge->neighbor12 ^ v1; - if ( v1 < num_atoms && (v0 >= num_atoms || v2 >= num_atoms) ) { - nDeltaH = nDeltaCharge = 0; - if ( v0 >= num_atoms ) { - /* delta(v0-v1) = -delta(v1-v2) along the alternating path */ - if ( pBNS->vert[v0].type & BNS_VERT_TYPE_TGROUP ) { - nDeltaH -= delta; - } else - if ( pBNS->vert[v0].type & BNS_VERT_TYPE_C_GROUP ) { - nDeltaCharge += delta; - } - } - if ( v2 >= num_atoms ) { - if ( pBNS->vert[v2].type & BNS_VERT_TYPE_TGROUP ) { - nDeltaH += delta; - } else - if ( pBNS->vert[v2].type & BNS_VERT_TYPE_C_GROUP ) { - nDeltaCharge -= delta; - } - } - if ( nDeltaH || nDeltaCharge ) { - if ( bSubtract ) { - if ( !mark[v1] ) { - /* first time the atom has been encountered: subtract */ -#if ( FIX_NORM_BUG_ADD_ION_PAIR == 1 ) - type = GetAtomChargeType( at, v1, nAtTypeTotals, &mask, 2 ); -#else - type = GetAtomChargeType( at, v1, nAtTypeTotals, &mask, 1 ); -#endif - ret ++; /* number of changed atoms */ - mark[v1] ++; - } - } else { /* Change */ - at[v1].charge += nDeltaCharge; - if ( nDeltaH ) { - AddOrRemoveExplOrImplH( nDeltaH, at, num_atoms, (AT_NUMB)v1, t_group_info ); - } - ret ++; /* number of changed atoms */ - } - } - } - } - - if ( v2 != vLast ) { - err = BNS_PROGRAM_ERR; - } - } - return err? err : ret; -} - - - -/*********************************************************************************/ -int SetBondsFromBnStructFlow( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bChangeFlow0 ) -{ - int pass, i, v0, v1, v2, ineigh1, ineigh2, vLast, n, delta, ret, ret_val, err = 0; - BNS_EDGE *edge; - int bMovingRad = 0, bChangeFlowAdd; - int bChangeFlow = (bChangeFlow0 & ~BNS_EF_SET_NOSTEREO); - /* - bCheckMovingRad = (bChangeFlow & BNS_EF_ALTR_NS) == BNS_EF_ALTR_NS && - pBNS->tot_st_cap > pBNS->tot_st_flow; - */ - for ( pass = pBNS->num_altp-1, ret = 0; 0 <= pass; pass -- ) { - pBNS->alt_path = pBNS->altp[pass]; - v1 = ALTP_START_ATOM(pBNS->alt_path); - n = ALTP_PATH_LEN(pBNS->alt_path); - delta = ALTP_DELTA(pBNS->alt_path); - vLast = ALTP_END_ATOM(pBNS->alt_path); - if ( (bChangeFlow0 & BNS_EF_SET_NOSTEREO) && - (pBNS->vert[v1].st_edge.cap0 > pBNS->vert[v1].st_edge.flow0 || - pBNS->vert[vLast].st_edge.cap0 > pBNS->vert[vLast].st_edge.flow0 ) ) { - bMovingRad ++; - bChangeFlowAdd = BNS_EF_SET_NOSTEREO; - ret |= 2; - } else { - bChangeFlowAdd = 0; - } - /* start vertex */ - if ( (bChangeFlow & BNS_EF_CHNG_RSTR) == BNS_EF_CHNG_RSTR) { - /* restore s-v1 edge flow to the BNS this pass input value */ - ; /*pBNS->vert[v1].st_edge.flow -= delta;*/ - } else - if ( (bChangeFlow & BNS_EF_SAVE_ALL) == BNS_EF_SAVE_ALL ) { - if ( v1 < num_atoms ) { - /* will produce wrong result if called for v1 next time? */ - ret_val = SetAtomRadAndChemValFromVertexCapFlow( pBNS, at, v1 ); - if ( ret_val < 0 ) { - err = BNS_PROGRAM_ERR; - } else { - ret |= (ret_val > 0); - } - } - /*pBNS->vert[v1].st_edge.flow0 = pBNS->vert[v1].st_edge.flow;*/ - } - pBNS->vert[v1].st_edge.pass = 0; - - v0 = v2 = NO_VERTEX; - for ( i = 0; i < n; i ++, delta = -delta, v0 = v1, v1 = v2 ) { - ineigh1 = ALTP_THIS_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v1->v2 neighbor */ - ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v2->v1 neighbor */ - edge = pBNS->edge + pBNS->vert[v1].iedge[ineigh1]; - /* follow the BN Structure, not the inp_ATOM, to take care of swithching to - t-groups, c-groups or other fictitious edges/vertices - */ - - v2 = edge->neighbor12 ^ v1; - - /* change at->chem_bonds_valence 2004-03-08 */ - if ( (bChangeFlow & BNS_EF_CHNG_BONDS) && v1 < num_atoms ) { - if ( v0 >= num_atoms && v2 < num_atoms ) { - at[v1].chem_bonds_valence += delta; /* change in v1-v2 bond order */ - } else - if ( v0 < num_atoms && v2 >= num_atoms && v0 != NO_VERTEX ) { - at[v1].chem_bonds_valence -= delta; /* change in v0-v1 bond order */ - } - } - - if ( !edge->pass ) - continue; - - if ( v1 < num_atoms && ineigh1 < at[v1].valence && - v2 < num_atoms && ineigh2 < at[v2].valence ) { - if ( (bChangeFlow0 & BNS_EF_ALTR_NS )==BNS_EF_ALTR_NS && - (bChangeFlow0 & BNS_EF_SAVE_ALL)==BNS_EF_SAVE_ALL ) { - /* 2004-07-02 special mode: save new ring bonds and mark as non-stereo non-ring bonds */ - if ( at[v1].nRingSystem != at[v2].nRingSystem ) { - /* non-ring bond (bridge) */ - bChangeFlowAdd = BNS_EF_ALTR_NS; - } else { - /* ring bond */ - bChangeFlowAdd = 0; - } - } - /* change bonds on the first pass only: in this case all flow correspond to the BNS output */ - ret_val = SetAtomBondType( edge, &at[v1].bond_type[ineigh1], &at[v2].bond_type[ineigh2], delta, bChangeFlow | bChangeFlowAdd ); - if ( ret_val < 0 ) { - err = BNS_PROGRAM_ERR; - } else { - ret |= (ret_val > 0); - } - } - edge->pass = 0; - } - - if ( v2 != vLast ) { - err = BNS_PROGRAM_ERR; - } else - if ( (bChangeFlow & BNS_EF_CHNG_RSTR) == BNS_EF_CHNG_RSTR) { - /* restore v2-t edge flow to the BNS this pass input value */ - /* "+=" instead of "-=" explanation: delta must have same sign as at the last edge */ - ; /*pBNS->vert[v2].st_edge.flow += delta; */ - } else - if ( (bChangeFlow & BNS_EF_SAVE_ALL) == BNS_EF_SAVE_ALL ) { - if ( v2 < num_atoms ) { - ret_val = SetAtomRadAndChemValFromVertexCapFlow( pBNS, at, v2 ); - if ( ret_val < 0 ) { - err = BNS_PROGRAM_ERR; - } else { - ret |= (ret_val > 0); - } - } - /*pBNS->vert[v2].st_edge.flow0 = pBNS->vert[v2].st_edge.flow;*/ - } - pBNS->vert[v2].st_edge.pass = 0; - - } - return err? err : ret; -} - - - -/*********************************************************************************/ -int MarkAtomsAtTautGroups( BN_STRUCT *pBNS, int num_atoms, BN_AATG *pAATG, int nEnd1, int nEnd2 ) -{ - int pass, i, j, v1, v2, ineigh1, ineigh2, vLast, vFirst, n, delta, err = 0; - BNS_EDGE *edge; - S_CHAR cDelta[MAX_ALT_AATG_ARRAY_LEN]; - AT_NUMB nVertex[MAX_ALT_AATG_ARRAY_LEN]; - int nLenDelta = 0, last_i, nNumFound; - - for ( pass = pBNS->num_altp-1; 0 <= pass; pass -- ) { - pBNS->alt_path = pBNS->altp[pass]; - vFirst = - v1 = ALTP_START_ATOM(pBNS->alt_path); - n = ALTP_PATH_LEN(pBNS->alt_path); - delta = ALTP_DELTA(pBNS->alt_path); - vLast = ALTP_END_ATOM(pBNS->alt_path); - v2 = NO_VERTEX; - pAATG->nNumFound = 0; /* initialize */ - - if ( nEnd1 != vFirst && nEnd1 != vLast ) { - nEnd1 = -1; /* really not the end */ - } - if ( nEnd2 != vFirst && nEnd2 != vLast ) { - nEnd2 = -1; /* really not the end */ - } - - for ( i = 0; i < n; i ++, delta = -delta, v1 = v2 ) { - ineigh1 = ALTP_THIS_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v1->v2 neighbor */ - ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v2->v1 neighbor */ - edge = pBNS->edge + pBNS->vert[v1].iedge[ineigh1]; - /* follow the BN Structure, not the inp_ATOM, to take care of swithching to - t-groups, c-groups or other fictitious edges/vertices - */ - v2 = edge->neighbor12 ^ v1; - /* - if ( v1 < num_atoms && v2 < num_atoms ) { - continue; - } - */ - /* BNS increased edge flow by delta */ - if ( v1 >= num_atoms && - ((pBNS->vert[v1].type & BNS_VERT_TYPE_TGROUP)||(pBNS->vert[v1].type & BNS_VERT_TYPE_TEMP)) && - 0 <= v2 && v2 < num_atoms && (pBNS->vert[v2].type & BNS_VERT_TYPE_ATOM ) ) { - /* - if ( !(pAATG->nMarkedAtom[v2] & AATG_MARK_IN_PATH) ) { - pAATG->nMarkedAtom[v2] |= AATG_MARK_IN_PATH; - pAATG->nNumFound ++; - } - */ - /* BNS increased bond order in v1(t-group)-v2(atom) by delta: added delta attachments */ - if ( nLenDelta < MAX_ALT_AATG_ARRAY_LEN ) { - cDelta[nLenDelta] = delta; - nVertex[nLenDelta] = v2; - nLenDelta ++; - } - } else - if ( v2 >= num_atoms && - ((pBNS->vert[v2].type & BNS_VERT_TYPE_TGROUP)||(pBNS->vert[v2].type & BNS_VERT_TYPE_TEMP)) && - 0 <= v1 && v1 < num_atoms && (pBNS->vert[v1].type & BNS_VERT_TYPE_ATOM ) ) { - /* - if ( !(pAATG->nMarkedAtom[v1] & AATG_MARK_IN_PATH) ) { - pAATG->nMarkedAtom[v1] |= AATG_MARK_IN_PATH; - pAATG->nNumFound ++; - } - */ - /* BNS increased bond order in v1(atom)-v2(t-group) by delta: added delta attachments */ - if ( nLenDelta < MAX_ALT_AATG_ARRAY_LEN ) { - cDelta[nLenDelta] = delta; - nVertex[nLenDelta] = v1; - nLenDelta ++; - } - } else - /* special case when the testing 'dot' was placed on an atom (should be nEnd1 only) */ - if ( 0 <= v1 && v1 == nEnd1 || v1 == nEnd2 && 0 <= v2 && v2 < num_atoms ) { - if ( nLenDelta < MAX_ALT_AATG_ARRAY_LEN ) { - cDelta[nLenDelta] = -delta; - nVertex[nLenDelta] = v1; - nLenDelta ++; - } - } else - if ( 0 <= v2 && v2 == nEnd1 || v2 == nEnd2 && 0 <= v1 && v1 < num_atoms ) { - if ( nLenDelta < MAX_ALT_AATG_ARRAY_LEN ) { - cDelta[nLenDelta] = -delta; - nVertex[nLenDelta] = v2; - nLenDelta ++; - } - } - } - - if ( v2 != vLast ) { - err = BNS_PROGRAM_ERR; - } else { - last_i = -1; - nNumFound = 0; - /* first run */ - for ( i = 1, j = 0; i < nLenDelta; j = i ++ ) { - /* ignore sequences (-1,+1) and (+1,-1) in cDelta[] because they */ - /* describe ordinary aug. paths of moving a single attachment */ - /* we are looking for aug. paths describing movement of 2 or more */ - if ( cDelta[j] > 0 && cDelta[i] > 0 || - cDelta[j] < 0 && cDelta[i] < 0 ) { - if ( j == last_i ) { - /* three attachments moved */ - return 0; - } - v1 = nVertex[j]; - if ( !(pAATG->nMarkedAtom[v1] & AATG_MARK_IN_PATH) ) { - nNumFound ++; - } - v2 = nVertex[i]; - if ( !(pAATG->nMarkedAtom[v2] & AATG_MARK_IN_PATH) ) { - nNumFound ++; - } - last_i = i; - } - } - if ( !nNumFound ) { - return 0; - } - if ( nNumFound > 4 ) { - return 0; - } - if ( nNumFound < 4 ) { - return 0; - } - /* second run */ - for ( i = 1, j = 0; i < nLenDelta; j = i ++ ) { - /* ignore sequences (-1,+1) and (+1,-1) in cDelta[] because they */ - /* describe ordinary aug. paths of moving a single attachment */ - /* we are looking for aug. paths describing movement of 2 or more */ - if ( cDelta[j] > 0 && cDelta[i] > 0 || - cDelta[j] < 0 && cDelta[i] < 0 ) { - v1 = nVertex[i-1]; - if ( !(pAATG->nMarkedAtom[v1] & AATG_MARK_IN_PATH) ) { - pAATG->nMarkedAtom[v1] |= AATG_MARK_IN_PATH; - pAATG->nNumFound ++; - } - v2 = nVertex[i]; - if ( !(pAATG->nMarkedAtom[v2] & AATG_MARK_IN_PATH) ) { - pAATG->nMarkedAtom[v2] |= AATG_MARK_IN_PATH; - pAATG->nNumFound ++; - } - } - } - } - } - return err? err : pAATG->nNumFound; -} - - - -/*********************************************************************************/ -int RestoreBnStructFlow( BN_STRUCT *pBNS, int bChangeFlow ) -{ - int pass, i, v1, v2, ineigh1, ineigh2, vLast, n, delta, ret, err = 0; - BNS_EDGE *edge; - - for ( pass = pBNS->num_altp - 1, ret = 0; 0 <= pass; pass -- ) { - pBNS->alt_path = pBNS->altp[pass]; - v1 = ALTP_START_ATOM(pBNS->alt_path); - n = ALTP_PATH_LEN(pBNS->alt_path); - delta = ALTP_DELTA(pBNS->alt_path); - vLast = ALTP_END_ATOM(pBNS->alt_path); - v2 = NO_VERTEX; - /* starting vertex */ - if ( (bChangeFlow & BNS_EF_CHNG_RSTR) == BNS_EF_CHNG_RSTR) { - pBNS->vert[v1].st_edge.flow -= delta; /* restore s-v1 edge flow to the BNS input value */ - } else - if ( (bChangeFlow & BNS_EF_SAVE_ALL) == BNS_EF_SAVE_ALL ) { - pBNS->vert[v1].st_edge.flow0 = pBNS->vert[v1].st_edge.flow; - } - /* augmenting path edges */ - for ( i = 0; i < n; i ++, delta = -delta, v1 = v2 ) { - ineigh1 = ALTP_THIS_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v1->v2 neighbor */ - ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v2->v1 neighbor */ - edge = pBNS->edge + pBNS->vert[v1].iedge[ineigh1]; - v2 = edge->neighbor12 ^ v1; - RestoreEdgeFlow( edge, delta, bChangeFlow ); - edge->pass = 0; - } - /* ending vertex */ - if ( v2 != vLast ) { - err = BNS_PROGRAM_ERR; - } else - if ( (bChangeFlow & BNS_EF_CHNG_RSTR) == BNS_EF_CHNG_RSTR) { - /* restore v2-t edge flow to the original value */ - /* "+=" instead of "-=" explanation: delta must have same sign as at the last edge */ - pBNS->vert[v2].st_edge.flow += delta; - } else - if ( (bChangeFlow & BNS_EF_SAVE_ALL) == BNS_EF_SAVE_ALL ) { - pBNS->vert[v2].st_edge.flow0 = pBNS->vert[v2].st_edge.flow; - } - - } - return err? err : ret; -} - - - -/***************************************************************************************/ -int bNeedToTestTheFlow( int bond_type, int nTestFlow, int bTestForNonStereoBond ) -{ - int nBondType = ( BOND_TYPE_MASK & bond_type ); - int nBondAttrib = ( BOND_MARK_MASK & bond_type ); - - if ( bTestForNonStereoBond ) { - if ( nBondAttrib || nBondType == BOND_ALTERN || nBondType == BOND_ALT12NS ) { - switch( nTestFlow ) { - case 0: /* single: can be 1 (single)? */ - if ( nBondAttrib == BOND_MARK_ALT12NS|| - nBondAttrib == BOND_MARK_ALT123 || - nBondAttrib == BOND_MARK_ALT13 ) { - return 0; /* yes, already checked */ - } - break; - - case 1: /* double: can be 2 (double)? */ - if ( nBondAttrib == BOND_MARK_ALT12NS|| - nBondAttrib == BOND_MARK_ALT123 || - nBondAttrib == BOND_MARK_ALT23 ) { - return 0; /* yes, already checked */ - } - break; - case 2: /* triple: can be 3 (triple)? */ - if ( nBondAttrib == BOND_MARK_ALT13 || - nBondAttrib == BOND_MARK_ALT123 || - nBondAttrib == BOND_MARK_ALT23 ) { - return 0; /* yes, already checked */ - } - break; - } - } - } else { - if ( nBondAttrib || nBondType == BOND_ALTERN || nBondType == BOND_ALT12NS ) { - switch( nTestFlow ) { - case 0: /* single: can be 1 (single)? */ - if ( nBondAttrib == BOND_MARK_ALT12 || - nBondAttrib == BOND_MARK_ALT12NS|| - nBondAttrib == BOND_MARK_ALT123 || - nBondAttrib == BOND_MARK_ALT13 ) { - return 0; - } - break; - - case 1: /* double: can be 2 (double)? */ - if ( nBondAttrib == BOND_MARK_ALT12 || - nBondAttrib == BOND_MARK_ALT12NS|| - nBondAttrib == BOND_MARK_ALT123 || - nBondAttrib == BOND_MARK_ALT23 ) { - return 0; /* yes */ - } - break; - case 2: /* triple: can be 3 (triple)? */ - if ( nBondAttrib == BOND_MARK_ALT13 || - nBondAttrib == BOND_MARK_ALT123 || - nBondAttrib == BOND_MARK_ALT23 ) { - return 0; - } - break; - } - } - } - return 1; -} - - - -/***********************************************************************************/ -int nBondsValenceInpAt( const inp_ATOM *at, int *nNumAltBonds, int *nNumWrongBonds ) -{ - int j, bond_type, nBondsValence = 0, nAltBonds = 0, nNumWrong = 0; - for ( j = 0; j < at->valence; j ++ ) { - bond_type = at->bond_type[j] & BOND_TYPE_MASK; - switch( bond_type ) { - case 0: /* for structure from InChI reconstruction */ - case BOND_SINGLE: - case BOND_DOUBLE: - case BOND_TRIPLE: - nBondsValence += bond_type; - break; - case BOND_ALTERN: - nAltBonds ++; - break; - default: - nNumWrong ++; - } - } - switch ( nAltBonds ) { - case 0: - break; - case 1: - nBondsValence += 1; /* 1 or greater than 3 is wrong */ - nNumWrong ++; - break; - default: - nBondsValence += nAltBonds+1; - break; - } - if ( nNumAltBonds ) *nNumAltBonds = nAltBonds; - if ( nNumWrongBonds ) *nNumWrongBonds = nNumWrong; - return nBondsValence; -} - - - -/******************************************************************************/ -/* if radical or has aromatic bonds then augment to the lowest "multiplicity" */ -/******************************************************************************/ -int BnsAdjustFlowBondsRad( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at, int num_atoms ) -{ - int bError=0, nOrigDelta=0, ret, num_removed; - -#if( CHECK_AROMBOND2ALT == 1 ) - int *pcValMinusBondsVal = NULL; - int i, nValMinusBondsVal, nAltBonds, bIgnore, valen, is_rad, excess; - - /* find valence excess (it may only be due to aromatic bonds) */ - for ( i = 0; i < num_atoms; i ++ ) - { - valen = nBondsValenceInpAt( at+i, &nAltBonds, &bIgnore ); - nValMinusBondsVal = (int)at[i].chem_bonds_valence - valen; - bIgnore += (nAltBonds > 3); - if ( !bIgnore && nValMinusBondsVal > 0 ) - { - if ( !pcValMinusBondsVal && - !(pcValMinusBondsVal = (int *)inchi_calloc(num_atoms, sizeof(pcValMinusBondsVal[0])))) - { - bError = BNS_OUT_OF_RAM; - goto exit_function; - } - /* mark atoms that have extra unsatisfied valence due to aromatic bonds */ - is_rad = (at[i].radical == RADICAL_DOUBLET); - excess = nValMinusBondsVal+ is_rad; - pcValMinusBondsVal[i] = excess; - } - } -#endif /* CHECK_AROMBOND2ALT */ - - /* match bonds to valences */ - do - { - num_removed = 0; - ret = RunBalancedNetworkSearch( pBNS, pBD, BNS_EF_CHNG_FLOW ); - if ( IS_BNS_ERROR( ret ) ) - { - bError = ret; - } - else - { - nOrigDelta += ret; - num_removed = pBNS->num_altp; /* number of augmenting paths */ - if ( ret > 0 ) - { - /* save new bonds in at[] and flows in pBNS and at[] */ - ret = SetBondsFromBnStructFlow( pBNS, at, num_atoms, BNS_EF_SAVE_ALL ); /* must include 1: 5=(4|1) */ - if ( IS_BNS_ERROR( ret ) ) - { - bError = ret; - } - ret = RestoreBnStructFlow( pBNS, BNS_EF_SAVE_ALL ); /* must include 1: 5=(4|1) */ - if ( IS_BNS_ERROR( ret ) ) - { - bError = ret; - } - } - ReInitBnStructAltPaths( pBNS ); - } - } while ( num_removed && num_removed == pBNS->max_altp && !bError ); - -#if( CHECK_AROMBOND2ALT == 1 ) - /* check whether aromatic bonds have been replaces with alternating bonds */ - if ( !bError && pcValMinusBondsVal ) - { - for ( i = 0; i < num_atoms; i ++ ) - { - if ( !pcValMinusBondsVal[i] ) - continue; - valen = nBondsValenceInpAt( at+i, &nAltBonds, &bIgnore ); - nValMinusBondsVal = (int)at[i].chem_bonds_valence - valen; - is_rad = (at[i].radical == RADICAL_DOUBLET); - excess = nValMinusBondsVal + is_rad; - if ( bIgnore || - ( pcValMinusBondsVal[i] - excess ) != 1 ) - { - /* radical excess has not been reduced */ - bError = BNS_ALTBOND_ERR; - break; - } - } - } - -exit_function: - if ( pcValMinusBondsVal ) - inchi_free( pcValMinusBondsVal ); -#endif /* CHECK_AROMBOND2ALT */ - - return bError? bError : nOrigDelta; -} - - - -/***************************************************************************************/ -int BnsTestAndMarkAltBonds( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at, int num_atoms, BNS_FLOW_CHANGES *fcd, int bChangeFlow, int nBondTypeToTest ) -{ - int ret, iat, ineigh, neigh; - int nMinFlow, nMaxFlow, nTestFlow, nCurFlow; - int iedge, bSuccess, bError, nDots, nChanges, bTestForNonStereoBond; - /* Normalize bonds and find tautomeric groups */ - bError = 0; - nChanges = 0; - bTestForNonStereoBond = pBNS->tot_st_cap > pBNS->tot_st_flow; - for ( iat = 0; iat < num_atoms && !bError; iat ++ ) { - for ( ineigh = 0; ineigh < at[iat].valence && !bError; ineigh ++ ) { - neigh = at[iat].neighbor[ineigh]; - if ( neigh < iat ) - continue; /* we have already tested the bond */ - iedge = pBNS->vert[iat].iedge[ineigh]; - if ( IS_FORBIDDEN(pBNS->edge[iedge].forbidden, pBNS) ) - continue; - if ( nBondTypeToTest && (at[iat].bond_type[ineigh] & BOND_TYPE_MASK) != nBondTypeToTest ) - continue; - nMinFlow = nMinFlow2Check( pBNS, iedge ); - nMaxFlow = nMaxFlow2Check( pBNS, iedge ); - nCurFlow = nCurFlow2Check( pBNS, iedge ); - if ( nMinFlow == nMaxFlow ) { - if ( nMaxFlow && bTestForNonStereoBond ) { - nTestFlow = nMaxFlow - (int)(pBNS->tot_st_cap - pBNS->tot_st_flow); /* temporary use of nTestFlow */ - nMinFlow = inchi_max( 0, nTestFlow ); - } else { - continue; - } - } - for ( nTestFlow = nMinFlow; nTestFlow <= nMaxFlow && !bError; nTestFlow ++ ) { - if ( nTestFlow == nCurFlow ) - continue; - if ( !bNeedToTestTheFlow( at[iat].bond_type[ineigh], nTestFlow, bTestForNonStereoBond ) ) - continue; - bSuccess = 0; - nDots = bSetFlowToCheckOneBond( pBNS, iedge, nTestFlow, fcd ); - if ( IS_BNS_ERROR(nDots) ) { - if ( nDots == BNS_CANT_SET_BOND ) { - ret = bRestoreFlowAfterCheckOneBond( pBNS, fcd ); - if ( !IS_BNS_ERROR( ret ) ) { - continue; - } - } - bError = nDots; - } else - if ( nDots > 0 ) { - ret = RunBalancedNetworkSearch( pBNS, pBD, bChangeFlow ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - } else - if ( ret > 0 ) { - if ( 2*ret == nDots ) { - ret = bSetBondsAfterCheckOneBond( pBNS, fcd, nTestFlow, at, num_atoms, bChangeFlow ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - } else { - nChanges += (ret & 1); - ret = SetBondsFromBnStructFlow( pBNS, at, num_atoms, bChangeFlow ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - } else - if ( ret >= 0 ) { - nChanges += (ret & 1); - bSuccess = 1; - } else { - bError = ret; - } - } - } - /* typically 2*ret < nDots; 2*ret > nDots should not happen. Check later */ - ret = RestoreBnStructFlow( pBNS, bChangeFlow & BNS_EF_CHNG_RSTR); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - } - } - /* --- reinitialize to repeat the calculations --- */ - ReInitBnStructAltPaths( pBNS ); - } else - if ( nDots == 0 ) { - ret = bSetBondsAfterCheckOneBond( pBNS, fcd, nTestFlow, at, num_atoms, bChangeFlow ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - } else { - nChanges += (ret & 1); - } - } - ret = bRestoreFlowAfterCheckOneBond( pBNS, fcd ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - } - } - } - } - return bError? bError : nChanges; -} - - - -/************************************************************************/ -static void remove_alt_bond_marks(inp_ATOM *at, int num_atoms) -{ - int i, j, val; - for ( i = 0; i < num_atoms; i++ ) { - for ( val = at[i].valence, j = 0; j < val; j ++ ) { - at[i].bond_type[j] &= BOND_TYPE_MASK; - } - } -} - - - -/***************************************************************************************/ -int SetForbiddenEdges( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int forbidden_mask ) -{ - static U_CHAR el_number_O; - static U_CHAR el_number_C; - static U_CHAR el_number_N; - - int i, j, neigh, num_found; - BNS_IEDGE iedge; - /*S_CHAR edge_forbidden_mask = BNS_EDGE_FORBIDDEN_MASK;*/ - S_CHAR edge_forbidden_mask = forbidden_mask; - - pBNS->edge_forbidden_mask |= forbidden_mask; - - if ( !el_number_C ) { - el_number_O = (U_CHAR)get_periodic_table_number( "O" ); - el_number_C = (U_CHAR)get_periodic_table_number( "C" ); - el_number_N = (U_CHAR)get_periodic_table_number( "N" ); - } - - num_found = 0; - - for ( i = 0; i < num_atoms; i ++ ) { - /* acetyl */ - if ( at[i].el_number == el_number_C && 3 == at[i].valence && - 4 == at[i].chem_bonds_valence ) { - int num_O = 0; - int bond_to_O_val = 0; - int forbidden_bond_pos = -1; - int forbidden_bond_val = -1; - for ( j = 0; j < at[i].valence; j ++ ) { - neigh = at[i].neighbor[j]; - if ( at[neigh].el_number == el_number_O && - at[neigh].valence == 1 ) { - num_O ++; - bond_to_O_val += (at[i].bond_type[j] & BOND_TYPE_MASK); - } else { - forbidden_bond_pos = j; - forbidden_bond_val = (at[i].bond_type[j] & BOND_TYPE_MASK); - } - } - if ( 2 == num_O && 3 == bond_to_O_val && 1 == forbidden_bond_val ) { - iedge = pBNS->vert[i].iedge[forbidden_bond_pos]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_found ++; - } - } else - /* nitro */ - if ( at[i].el_number == el_number_N && 3 == at[i].valence && - (4 == at[i].chem_bonds_valence || 5 == at[i].chem_bonds_valence) ) { - int num_O = 0; - int bond_to_O_val = 0; - int forbidden_bond_pos = -1; - int forbidden_bond_val = -1; - for ( j = 0; j < at[i].valence; j ++ ) { - neigh = at[i].neighbor[j]; - if ( at[neigh].el_number == el_number_O && - at[neigh].valence == 1 ) { - num_O ++; - bond_to_O_val += (at[i].bond_type[j] & BOND_TYPE_MASK); - } else { - forbidden_bond_pos = j; - forbidden_bond_val = (at[i].bond_type[j] & BOND_TYPE_MASK); - - } - } - if ( 2 == num_O && (3 == bond_to_O_val || 4 == bond_to_O_val) && 1 == forbidden_bond_val ) { - iedge = pBNS->vert[i].iedge[forbidden_bond_pos]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_found ++; - } - } - } -#if ( REMOVE_ION_PAIRS_FIX_BONDS == 1 ) - num_found += fix_special_bonds( pBNS, at, num_atoms, edge_forbidden_mask ); -#endif -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) - num_found += TempFix_NH_NH_Bonds( pBNS, at, num_atoms ); -#endif - return num_found; -} - - - -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) -/************************************************************************/ -int TempFix_NH_NH_Bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms ) -{ - static U_CHAR el_number_N; - int i, j, neigh, num_found; - BNS_IEDGE iedge; - S_CHAR edge_forbidden_mask = BNS_EDGE_FORBIDDEN_TEMP; - if ( !el_number_N ) { - el_number_N = (U_CHAR)get_periodic_table_number( "N" ); - } - for ( i = 0, num_found = 0; i < num_atoms; i ++ ) { - /* -NH-NH- or -NH-NH3 */ - if ( at[i].el_number == el_number_N && at[i].valence < 3 && at[i].num_H && - 3 == at[i].chem_bonds_valence + at[i].num_H && - at[i].chem_bonds_valence == at[i].valence && - !at[i].charge && !at[i].radical ) { - for ( j = 0; j < at[i].valence; j ++ ) { - neigh = at[i].neighbor[j]; - if ( neigh < i && - at[neigh].el_number == el_number_N && at[neigh].valence < 3 && at[neigh].num_H && - 3 == at[neigh].chem_bonds_valence + at[neigh].num_H && - at[neigh].chem_bonds_valence == at[neigh].valence && - !at[neigh].charge && !at[neigh].radical) { - iedge = pBNS->vert[i].iedge[j]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_found ++; - } - } - } - } - return num_found; -} - - - -/************************************************************************/ -int CorrectFixing_NH_NH_Bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms ) -{ - static U_CHAR el_number_N; - int i, j, neigh, num_found; - BNS_IEDGE iedge; - S_CHAR edge_forbidden_mask = BNS_EDGE_FORBIDDEN_TEMP; - if ( !el_number_N ) { - el_number_N = (U_CHAR)get_periodic_table_number( "N" ); - } - for ( i = 0, num_found = 0; i < num_atoms; i ++ ) { - /* -NH-NH- or -NH-NH3 */ - if ( at[i].el_number == el_number_N && at[i].valence < 3 ) { - for ( j = 0; j < at[i].valence; j ++ ) { - neigh = at[i].neighbor[j]; - if ( neigh < i && - at[neigh].el_number == el_number_N && at[neigh].valence < 3 ) { - if ( BOND_TYPE_SINGLE != (at[i].bond_type[j] & BOND_TYPE_MASK) ) { - iedge = pBNS->vert[i].iedge[j]; - if ( pBNS->edge[iedge].forbidden & edge_forbidden_mask ) { - pBNS->edge[iedge].forbidden &= ~edge_forbidden_mask; - num_found ++; - } - } - } - } - } - } - return num_found; -} -#endif - - - - -/******************************************************/ -/* fixes bonds set by remove_ion_pairs() in strutil.c */ -/******************************************************/ -int fix_special_bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int forbidden_mask ) -{ - int num_changes = 0; - - /* 0 1 2 3 4 5 6 7 8 9 8 9 */ -#if ( FIX_REM_ION_PAIRS_Si_BUG == 1 ) - static const char el[] = "N;P;As;Sb;O;S;Se;Te;C;Si;"; /* 8 elements + C, Si */ -#else - static const char el[] = "N;P;As;Sb;O;S;Se;Te;C;Si"; /* 8 elements + C, Si */ -#endif - static char en[12]; /* same number: 8 elements */ - static int ne=0; /* will be 8 and 10 */ - -#define ELEM_N_FST 0 -#define ELEM_N_LEN 4 -#define ELEM_O_FST 4 -#define ELEM_O_LEN 4 -#define ELEM_S_FST (ELEM_O_FST+1) -#define ELEM_S_LEN (ELEM_O_LEN-1) -#define ELEM_C_FST 8 -#define ELEM_C_LEN 2 - -#define MAX_NEIGH 6 - - int i, k, n1, n2, n3, n4, i1, i2, i3, i4, bond_type; - inp_ATOM *a; - char elname[ATOM_EL_LEN]; - int j[3], m[3], num_O, k_O, num_N, num_OH, num_OM, num_X, num_other, k_N; - - BNS_IEDGE iedge; - /*S_CHAR edge_forbidden_mask = BNS_EDGE_FORBIDDEN_MASK;*/ - S_CHAR edge_forbidden_mask = forbidden_mask; - - pBNS->edge_forbidden_mask |= edge_forbidden_mask; - - if ( !ne ) { /* one time initialization */ - const char *b, *e; - int len; - for ( b = el; e = strchr( b, ';'); b = e+1 ) { - len = e-b; - memcpy( elname, b, len ); - elname[len] = '\0'; - en[ne++] = get_periodic_table_number( elname ); - } - en[ne] = '\0'; - en[ne+1] = '\0'; - } - for ( i = 0, a = at; i < num_atoms; i ++, a++ ) { - if ( !a->charge && !a->radical && - 2 <= a->chem_bonds_valence + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && - 0 == num_of_H( at, i ) && - 2 == nNoMetalBondsValence(at, i) + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && - NULL != memchr( en+ELEM_N_FST, at[i].el_number, ELEM_N_LEN) ) { - /* found N(V), no H */ - if ( 2 == nNoMetalNumBonds(at, i) ) { - /* #N= */ - /* fix bonds: double and triple: =N# so that bonds cannot be changed by the normalization */ -#if ( FIX_N_V_METAL_BONDS_GPF == 1 ) - if ( 0 > (i1 = nNoMetalNeighIndex( at, i )) || - 0 > (i2 = nNoMetalOtherNeighIndex( at, i, - n1 = a->neighbor[i1]/* non-metal neighbor #1 */ ) ) ) { - /*num_err ++; */ /* do not count would-be original InChI v.1 buffer overflow GPF */ - continue; /* v1 bug: 2 bonds to metal yield i1 < 0 and/or i2 < 0 => bounds violation */ - } -#else - i1 = nNoMetalNeighIndex( at, i ); - n1 = a->neighbor[i1]; /* non-metal neighbor #1 */ - i2 = nNoMetalOtherNeighIndex( at, i, n1 ); -#endif - n2 = a->neighbor[i2]; /* non-metal neighbor #2 */ - /* forbid all edges to non-metals */ - iedge = pBNS->vert[i].iedge[i1]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; /* fix bond to neighbor #1 */ - iedge = pBNS->vert[i].iedge[i2]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; /* fix bond to neighbor #1 */ - num_changes ++; /* added 11-15-2005 */ - /* i n3 */ - /* forbid single bond edge beyond the neighboring =N- as in #N=N- */ - if ( (at[i].bond_type[i1] & BOND_TYPE_MASK) == BOND_TYPE_DOUBLE ) { - i3 = i1; - n3 = n1; - } else { - i3 = i2; - n3 = n2; - } - if ( 0 == NUMH(at, n3) && 2 == nNoMetalNumBonds( at, n3 ) && - 3 == nNoMetalBondsValence( at, n3 ) && - NULL != memchr( en+ELEM_N_FST, at[n3].el_number, ELEM_N_LEN) && - 0 <= (k = nNoMetalOtherNeighIndex( at, n3, i )) ) { - /* found =N- ; forbid the edge*/ - iedge = pBNS->vert[n3].iedge[k]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_changes ++; - } - - } else - if ( 3 == nNoMetalNumBonds(at, i) && - /* | */ - /* found =N= */ - /* locate all non-metal neighbors */ - 0 <= (j[0] = nNoMetalNeighIndex( at, i )) && - 0 <= (j[1] = nNoMetalOtherNeighIndex( at, i, m[0] = a->neighbor[j[0]] )) && - 0 <= (j[2] = nNoMetalOtherNeighIndex2( at, i, m[0], m[1] = a->neighbor[j[1]] )) ) { - /* count specific neighbors: N(V)=N, N(V)-N N(V)=O, and N(V)-N */ - /* if there is a single neighbor connected by a double bond, namely - N(V)=N and/or N(V)=O, then fix the bond(s). - If N(V)=O was fixed then do not fix another bond */ - m[2] = a->neighbor[j[2]]; - num_O = num_N = 0; - for ( k= 0; k < 3; k ++ ) { - n1 = m[k]; - i1 = j[k]; - if ( NULL != memchr( en+ELEM_N_FST, at[n1].el_number, ELEM_N_LEN) ) { - k_N = k; - num_N ++; - } else - if ( NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) && - 1 == nNoMetalNumBonds( at, n1 ) ) { - k_O = k; - num_O ++; - } - } - num_other = 0; - if ( 1 == num_O && 0 == at[n1=m[k_O]].charge && 0 == at[n1].radical && - BOND_TYPE_DOUBLE == (at[i].bond_type[i1=j[k_O]] & BOND_TYPE_MASK) ) { - /* fix bond to neighbor =O */ - iedge = pBNS->vert[i].iedge[i1]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_changes ++; - num_other ++; /* indicator: double to a terminal O has been fixed */ - } - if ( !num_other && num_O <= 1 && - 1 == num_N && 0 == at[n1=m[k_N]].charge && 0 == at[n1].radical && - BOND_TYPE_DOUBLE == (at[i].bond_type[i1=j[k_N]] & BOND_TYPE_MASK ) ) { - /* fix bond to neighbor =N */ - iedge = pBNS->vert[i].iedge[i1]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_changes ++; - } - - } else - if ( 4 == nNoMetalNumBonds(at, i) ) { - /* | */ - /* found -N=N- */ - /* | */ - /* locate non-metal neighbor connected by a double bond; - * if it is =N- then fix the double bond and the single bond beyond the neighbor - */ - num_N = 0; - num_other = 0; - for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { - if ( BOND_TYPE_DOUBLE == (at[i].bond_type[i1] & BOND_TYPE_MASK) && - !is_el_a_metal(at[n1=(int)at[i].neighbor[i1]].el_number) && - NULL != memchr( en+ELEM_N_FST, at[n1].el_number, ELEM_N_LEN) ) { - num_N ++; - n2 = n1; - i2 = i1; - } - } - if ( 1 == num_N && 0 == NUMH(at, n2) && - 2 == nNoMetalNumBonds( at, n2 ) && - 3 == nNoMetalBondsValence(at, n2) && - 0 <= ( i3 = nNoMetalOtherNeighIndex( at, n2, i ) ) && - BOND_TYPE_SINGLE == (at[n2].bond_type[i3] & BOND_TYPE_MASK) ) { - /* fix the single bond beyond the N(V) neighbor N(V)=N- */ - iedge = pBNS->vert[n2].iedge[i3]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_changes ++; - /* fix the double bond */ - iedge = pBNS->vert[i].iedge[i2]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_changes ++; - } - } - } else - if ( !a->charge && !a->radical && - 2 <= a->chem_bonds_valence + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && - 0 == num_of_H( at, i ) && - 2 == nNoMetalBondsValence(at, i) + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && - NULL != memchr( en+ELEM_S_FST, a->el_number, ELEM_S_LEN) && - 3 == nNoMetalNumBonds( at, i ) ) { - /* found S(IV), no H, one double bond, total 3 bonds */ - /* OH - / - in O=S (X != O) fix single bond O-X (type 1) - \ - X - - X - / - in Z=S (X, Y != OH) fix double bond Z=S (type 2) - \ - Y - */ - num_N = 0; /* number of non-metal neighbors connected by a double bond */ - num_OH = 0; /* number of neighbors OH connected by a single bond */ - num_OM = 0; /* number of charged neighbors O connected by a single bond */ - num_O = 0; /* number of neighbors =O connected by a double bond */ - num_other = 0; - for ( i1 = 0; i1 < a->valence; i1 ++ ) { - n1=(int)a->neighbor[i1]; - if ( is_el_a_metal(at[n1].el_number) ) { - continue; - } - bond_type = (a->bond_type[i1] & BOND_TYPE_MASK); - if ( BOND_TYPE_DOUBLE == bond_type ) { - num_N ++; - n2 = n1; - i2 = i1; - if ( NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) ) { - num_O ++; - } - } else - if ( BOND_TYPE_SINGLE == bond_type && - 1 == nNoMetalNumBonds( at, n1 ) && - 1 == nNoMetalBondsValence(at, n1 ) && - NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) ) { - if ( 0 == at[n1].charge ) { - num_OH ++; - n3 = n1; - i3 = i1; - } else { - num_OM ++; - } - } else { - num_other ++; - n4 = n1; - i4 = i1; - } - } - if ( 1 == num_N && 1 == num_O && 1 == num_OH + num_OM ) { - if ( 1 == num_other ) { - /* type 1: fix the single bond S-X */ - iedge = pBNS->vert[i].iedge[i4]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_changes ++; - } - } else - if ( 1 == num_N && !num_OH && !num_OM ) { - int bFound = 0; /* flag */ - int bDoNotFixAnyBond = 0; /* flag */ - /* avoid case N=S-NH or N=S-N(-); N = N, P, As, Sb */ - if ( NULL != memchr( en+ELEM_N_FST, at[n2].el_number, ELEM_N_LEN) ) { - U_CHAR el_number = at[n2].el_number; - for ( i1 = 0; i1 < a->valence; i1 ++ ) { - n1=(int)a->neighbor[i1]; - bond_type = (a->bond_type[i1] & BOND_TYPE_MASK); - if ( BOND_TYPE_SINGLE == bond_type && - (NUMH(at, n1) || -1 == at[n1].charge) && - el_number == at[n1].el_number ) { - i3 = i1; - n3 = n1; - bFound ++; - } - } - } - /* exception: check if Z==X and they belong to the same ring system */ - for ( i1 = 0; i1 < a->valence; i1 ++ ) { - if ( i1 != i2 ) { - n1=(int)a->neighbor[i1]; - if ( at[n2].el_number == at[n1].el_number && - at[n2].nRingSystem == at[n1].nRingSystem ) { - bDoNotFixAnyBond ++; - } - } - } - - if ( bDoNotFixAnyBond ) { - ; /* do nothing */ - } else - if ( bFound ) { - if ( 1 == bFound && - 0 <= ( i4 = nNoMetalOtherNeighIndex2( at, i, n2, n3) ) ) { - /* fix bond i4 */ - iedge = pBNS->vert[i].iedge[i4]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_changes ++; - } - } else { - /* fix the double bond >S=X */ - iedge = pBNS->vert[i].iedge[i2]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_changes ++; - /* -- test later -- - if ( 2 == nNoMetalNumBonds( at, n2 ) && - 0 <= ( i3 = nNoMetalOtherNeighIndex( at, n2, i ) ) ) { - iedge = pBNS->vert[n2].iedge[i3]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_changes ++; - } - -------------------*/ - } - } - } else - if ( !a->charge && !a->radical && - 4 <= a->chem_bonds_valence + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && - 0 == num_of_H( at, i ) && - 4 == nNoMetalBondsValence(at, i) + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && - NULL != memchr( en+ELEM_S_FST, a->el_number, ELEM_S_LEN) && - 4 == nNoMetalNumBonds( at, i ) ) { - /* found S(VI), no H, two double bonds or one triple bond */ - /* O - || - in O=S--Y- (X, Y -- non-terminal) fix single bonds S-X, S-Y (type 1) - \ - X-- - - O - || - in O=S--O(-) (X -- non-terminal) fix single bond S-X (type 2) - \ - X-- - - O - || - in O=S--OH (X -- non-terminal) fix single bond S-X (type 3) - \ - X-- - - */ - int iN[4]; /* indexes of non-terminal neighbors connected by a single bond */ - num_N = 0; /* number of non-metal neighbors connected by a double bond */ - num_OH = 0; /* number of neighbors OH connected by a single bond */ - num_OM = 0; /* number of non-terminal neighbors connected by a single bond */ - num_O = 0; /* number of neighbors =O connected by a double bond */ - num_X = 0; /* number of terminal atom X != O connected by a single bond */ - num_other = 0; - for ( i1 = 0; i1 < a->valence; i1 ++ ) { - n1=(int)a->neighbor[i1]; - if ( is_el_a_metal(at[n1].el_number) ) { - continue; - } - bond_type = (a->bond_type[i1] & BOND_TYPE_MASK); - if ( BOND_TYPE_DOUBLE == bond_type ) { - num_N ++; - if ( (0 == at[n1].charge -#if ( S_VI_O_PLUS_METAL_FIX_BOND == 1 ) - || 1 == at[n1].charge && 2 == at[n1].valence -#endif - ) && 0 == at[n1].radical && - 0 == num_of_H( at, n1 ) && - NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) && - 1 == nNoMetalNumBonds( at, n1 ) ) { - - num_O ++; - } - } else - if ( BOND_TYPE_SINGLE == bond_type && - 1 == nNoMetalNumBonds( at, n1 ) && - NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) && - 1 >= num_of_H( at, n1 ) && - 1 == (( 0 == at[n1].charge) && 1==num_of_H( at, n1 )) - +((-1 == at[n1].charge) && 0==num_of_H( at, n1 )) ) { - - num_OH ++; /* -OH or -O(-) */ - - } else - if ( BOND_TYPE_SINGLE == bond_type && - 1 < nNoMetalNumBonds( at, n1 ) ) { - - iN[num_OM ++] = i1; /* non-terminal neighbor connected by a single bond */ - - } else - if ( BOND_TYPE_SINGLE == bond_type && - 1 == nNoMetalNumBonds( at, n1 ) ) { - - num_X ++; /* other terminal neighbor connected by a single bond */ - - } else { - num_other ++; - } - } - if ( num_N == num_O && 2 == num_O && 2 == num_OH + num_OM + num_X && 0 == num_other ) { - for ( i2 = 0; i2 < num_OM; i2 ++ ) { - i1 = iN[i2]; - /* fix bond i1 */ - iedge = pBNS->vert[i].iedge[i1]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_changes ++; - } - } - } else - if ( !a->charge && !a->radical && - 6 <= a->chem_bonds_valence + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && - 0 == num_of_H( at, i ) && - 6 == nNoMetalBondsValence(at, i) + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && - NULL != memchr( en+ELEM_S_FST, a->el_number, ELEM_S_LEN) && - 5 == nNoMetalNumBonds( at, i ) ) { - /* found S(VIII), no H, three double bonds or two triple bond */ - /* - - O - || - in O=S--Y-- (X, Y -- non-terminal) fix single bond S-X, S-Y (type 4) - //\ - O X-- - - note: this structure is a mistakenly drawn structure - - O O - || || - O=S--O--Y-- or O=S--Y-- - \ \ - X-- O--X-- - - - */ - int iN[5]; /* indexes of non-terminal neighbors connected by a single bond */ - num_N = 0; /* number of non-metal neighbors connected by a double bond */ - num_OH = 0; /* number of neighbors OH connected by a single bond */ - num_OM = 0; /* number of non-terminal neighbors connected by a single bond */ - num_O = 0; /* number of neighbors =O connected by a double bond */ - num_X = 0; /* number of terminal atom X != O connected by a single bond */ - num_other = 0; - for ( i1 = 0; i1 < a->valence; i1 ++ ) { - n1=(int)a->neighbor[i1]; - if ( is_el_a_metal(at[n1].el_number) ) { - continue; - } - bond_type = (a->bond_type[i1] & BOND_TYPE_MASK); - if ( BOND_TYPE_DOUBLE == bond_type ) { - num_N ++; - if ( (0 == at[n1].charge -#if ( S_VI_O_PLUS_METAL_FIX_BOND == 1 ) - || 1 == at[n1].charge && 2 == at[n1].valence -#endif - ) - && 0 == at[n1].radical && - 0 == num_of_H( at, n1 ) && - NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) && - 1 == nNoMetalNumBonds( at, n1 ) ) { - - num_O ++; - } - } else - if ( BOND_TYPE_SINGLE == bond_type && - 1 == nNoMetalNumBonds( at, n1 ) && - NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) && - 1 >= num_of_H( at, n1 ) && - 1 == (( 0 == at[n1].charge) && 1==num_of_H( at, n1 )) - +((-1 == at[n1].charge) && 0==num_of_H( at, n1 )) ) { - - num_OH ++; /* -OH or -O(-) */ - - } else - if ( BOND_TYPE_SINGLE == bond_type && - 1 < nNoMetalNumBonds( at, n1 ) ) { - - iN[num_OM ++] = i1; /* non-terminal neighbor connected by a single bond */ - - } else - if ( BOND_TYPE_SINGLE == bond_type && - 1 == nNoMetalNumBonds( at, n1 ) ) { - - num_X ++; /* other terminal neighbor connected by a single bond */ - - } else { - num_other ++; - } - } - if ( num_N == num_O && 3 == num_O && 2 == num_OH + num_OM + num_X && 0 == num_other ) { - for ( i2 = 0; i2 < num_OM; i2 ++ ) { - i1 = iN[i2]; - /* fix bond i1 */ - iedge = pBNS->vert[i].iedge[i1]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_changes ++; - } - } - } - } - return num_changes; -} - - - -#define ALL_NONMETAL_Z 0 - - - - -/***************************************************************************************/ -int is_Z_atom( U_CHAR el_number ) -{ - typedef enum tag_Z_elnumber { - el_C , - el_N , - el_P , - el_As, - el_Sb, - el_S , - el_Se, - el_Te, - el_Cl, - el_Br, - el_I , -#if ( ALL_NONMETAL_Z == 1 ) - el_B , - el_O , - el_Si, - el_Ge, - el_F , - el_At, -#endif - el_len - } Z_ELNUMBER; - static U_CHAR el_numb[el_len]; -/* - return is_el_a_metal( (int)el_number ); -*/ - if ( !el_numb[el_C] ) { - el_numb[el_C ] = (U_CHAR)get_periodic_table_number( "C" ); - el_numb[el_N ] = (U_CHAR)get_periodic_table_number( "N" ); - el_numb[el_P ] = (U_CHAR)get_periodic_table_number( "P" ); - el_numb[el_As] = (U_CHAR)get_periodic_table_number( "As" ); - el_numb[el_Sb] = (U_CHAR)get_periodic_table_number( "Sb" ); - el_numb[el_S ] = (U_CHAR)get_periodic_table_number( "S" ); - el_numb[el_Se] = (U_CHAR)get_periodic_table_number( "Se" ); - el_numb[el_Te] = (U_CHAR)get_periodic_table_number( "Te" ); - el_numb[el_Cl] = (U_CHAR)get_periodic_table_number( "Cl" ); - el_numb[el_Br] = (U_CHAR)get_periodic_table_number( "Br" ); - el_numb[el_I ] = (U_CHAR)get_periodic_table_number( "I" ); -#if ( ALL_NONMETAL_Z == 1 ) - el_numb[el_B ] = (U_CHAR)get_periodic_table_number( "B" ); - el_numb[el_O ] = (U_CHAR)get_periodic_table_number( "O" ); - el_numb[el_Si] = (U_CHAR)get_periodic_table_number( "Si" ); - el_numb[el_Ge] = (U_CHAR)get_periodic_table_number( "Ge" ); - el_numb[el_F ] = (U_CHAR)get_periodic_table_number( "F" ); - el_numb[el_At] = (U_CHAR)get_periodic_table_number( "At" ); -#endif - } - if ( memchr( el_numb, el_number, el_len ) ) { - return 1; - } - return 0; - -} - - - -/***************************************************************************************/ -int IsZOX( inp_ATOM *atom, int at_x, int ord ) -{ /* detect O==Z--X, O=O,S,Se,Te */ - static U_CHAR el_number_O = 0; - static U_CHAR el_number_S = 0; - static U_CHAR el_number_Se = 0; - static U_CHAR el_number_Te = 0; - inp_ATOM *at_Z = atom + atom[at_x].neighbor[ord]; - - int i, neigh, num_O; - - if ( !el_number_O ) { - el_number_O = (U_CHAR)get_periodic_table_number( "O" ); - el_number_S = (U_CHAR)get_periodic_table_number( "S" ); - el_number_Se = (U_CHAR)get_periodic_table_number( "Se" ); - el_number_Te = (U_CHAR)get_periodic_table_number( "Te" ); - } - for ( i = 0, num_O = 0; i < at_Z->valence; i ++ ) { - neigh = at_Z->neighbor[i]; - if ( neigh == at_x ) { - continue; - } - if ( atom[neigh].valence == 1 && - atom[neigh].chem_bonds_valence == 2 && - atom[neigh].charge == 0 && - atom[neigh].radical == 0 && - (atom[neigh].el_number == el_number_O || - atom[neigh].el_number == el_number_S || - atom[neigh].el_number == el_number_Se || - atom[neigh].el_number == el_number_Te ) ) { - num_O ++; - } - } - return num_O; -} - - - -/***************************************************************************************/ -int GetAtomChargeType( inp_ATOM *atom, int at_no, int nAtTypeTotals[], int *pMask, int bSubtract ) -{ - static U_CHAR el_number_C = 0; - static U_CHAR el_number_O = 0; - static U_CHAR el_number_S = 0; - static U_CHAR el_number_Se = 0; - static U_CHAR el_number_Te = 0; - static U_CHAR el_number_P = 0; - static U_CHAR el_number_N = 0; - static U_CHAR el_number_H = 0; - - static U_CHAR el_number_F = 0; - static U_CHAR el_number_Cl = 0; - static U_CHAR el_number_Br = 0; - static U_CHAR el_number_I = 0; - - inp_ATOM *at = atom + at_no; -#if ( FIX_NORM_BUG_ADD_ION_PAIR == 1 ) - int i, neigh, mask, bit, type, num_z, num_m, num_o, delta = bSubtract > 0 ? -1 : 1; /* 0 or -2 => add, 1 or 2 => subtract */ - int bNoAdjIon = (bSubtract==0 || bSubtract==1); -#else - int i, neigh, mask, bit, type, num_z, num_m, num_o, delta = bSubtract? -1 : 1; -#endif - int bUnsatNHasTerminalO = 0; - if ( !el_number_C ) { - el_number_C = (U_CHAR)get_periodic_table_number( "C" ); - el_number_O = (U_CHAR)get_periodic_table_number( "O" ); - el_number_S = (U_CHAR)get_periodic_table_number( "S" ); - el_number_Se = (U_CHAR)get_periodic_table_number( "Se" ); - el_number_Te = (U_CHAR)get_periodic_table_number( "Te" ); - el_number_P = (U_CHAR)get_periodic_table_number( "P" ); - el_number_N = (U_CHAR)get_periodic_table_number( "N" ); - el_number_H = (U_CHAR)get_periodic_table_number( "H" ); - el_number_F = (U_CHAR)get_periodic_table_number( "F" ); - el_number_Cl = (U_CHAR)get_periodic_table_number( "Cl" ); - el_number_Br = (U_CHAR)get_periodic_table_number( "Br" ); - el_number_I = (U_CHAR)get_periodic_table_number( "I" ); - } - - type = ATT_NONE; - mask = 0; - if ( at->radical && at->radical != RADICAL_SINGLET ) { - goto exit_function; - } - if ( is_el_a_metal( at->el_number ) ) { - goto exit_function; /* metal */ - } - if ( at->charge < -1 || at->charge > 1 ) { - goto exit_function; - } - if ( !at->valence && at->charge == 1 && !at->num_H && !at->radical && at->el_number == el_number_H ) { - /* a proton (#1) */ - type = ATT_PROTON; - mask = ATBIT_Proton; - goto count_mask_bits; - } - if ( !at->valence && at->charge == -1 && !at->num_H && !at->radical && - ( at->el_number == el_number_F || - at->el_number == el_number_Cl || - at->el_number == el_number_Br || - at->el_number == el_number_I ) - ) { - /* a halogen anion (#2) */ - type = ATT_HalAnion; - mask = ATBIT_HalAnion; - goto count_mask_bits; - } -#if ( HAL_ACID_H_XCHG == 1 ) - /* halogen/chalcogen acid */ - if ( !at->valence && at->charge == 0 && 1 == at->num_H && !at->radical && - ( at->el_number == el_number_F || - at->el_number == el_number_Cl || - at->el_number == el_number_Br || - at->el_number == el_number_I ) || - !at->valence && at->charge == 0 && 2 == at->num_H && !at->radical && - ( at->el_number == el_number_O || - at->el_number == el_number_S || - at->el_number == el_number_Se || - at->el_number == el_number_Te ) - ) { - /* a halogen/chalcogen acid (#3) */ - type = ATT_HalAcid; - mask = ATBIT_HalAcid; - goto count_mask_bits; - } -#endif - if ( detect_unusual_el_valence( at->el_number, at->charge, at->radical, - at->chem_bonds_valence, at->num_H, - at->valence ) ) { - goto exit_function; /* unusual valence state */ - } - /* check neighbors */ - for ( i = 0, num_z = 0, num_m = 0, num_o = 0; i < at->valence; i ++ ) { - neigh = at->neighbor[i]; -#if ( FIX_NORM_BUG_ADD_ION_PAIR == 1 ) - if ( atom[neigh].charge < -1 || atom[neigh].charge > 1 ) { - goto exit_function; /* neighboring charge */ - } - if ( atom[neigh].charge && at->charge ) { - if ( bNoAdjIon ) { - goto exit_function; /* neighboring charge */ - } - type = ATT_NONE; - mask = 0; - goto count_mask_bits; - } -#else - if ( atom[neigh].charge < -1 || atom[neigh].charge > 1 || atom[neigh].charge && at->charge ) { - goto exit_function; /* neighboring charge */ - } -#endif - if ( detect_unusual_el_valence( atom[neigh].el_number, atom[neigh].charge, atom[neigh].radical, - atom[neigh].chem_bonds_valence, atom[neigh].num_H, - atom[neigh].valence ) ) { - goto exit_function; /* neighbor in unusual valence state */ - } - if ( is_Z_atom( atom[neigh].el_number ) ) { - num_z ++; - } - if ( is_el_a_metal( atom[neigh].el_number ) ) { - num_m ++; - } - num_o += (atom[neigh].el_number == el_number_O); - if ( at->el_number == el_number_N && at->valence == 2 && !at->charge && - /*at->valence < at->chem_bonds_valence &&*/ - atom[neigh].valence == 1 && atom[neigh].chem_bonds_valence == 2 && - (atom[neigh].el_number == el_number_O || - atom[neigh].el_number == el_number_S || - atom[neigh].el_number == el_number_Se || - atom[neigh].el_number == el_number_Te )) { - bUnsatNHasTerminalO ++; - } - } - /* O, S, Se, Te */ - if ( at->el_number == el_number_O || - at->el_number == el_number_S || - at->el_number == el_number_Se || - at->el_number == el_number_Te ) { - if ( at->charge == 1 ) { - if ( at->num_H ) { /* #4 */ - type = ATT_O_PLUS; - mask |= ATBIT_OH_Plus; - } else { /* #5 */ - type = ATT_O_PLUS; - mask |= ATBIT_O_Plus; - } - } else - if ( at->valence > 1 ) { - goto exit_function; /* not a terminal atom #C1 */ - } else - if ( at->valence && !(num_z || num_o) ) { - if ( num_m == at->valence ) { - goto exit_function; /* #C2 */ - } - goto count_mask_bits; /* #C3 count charges, no donor or acceptor found */ - } else - /* here at->neigh[0] is one of: O, or Z={C, N, P, As, Sb, S, Se, Te, Cl, Br, I} */ - if ( at->valence ) { - neigh = at->neighbor[0]; /* Z or O only */ - if ( !atom[neigh].charge && atom[neigh].el_number == el_number_C && - atom[neigh].chem_bonds_valence > atom[neigh].valence ) { - /* =C-OH, #C-OH, =C-O(-), #C-O(-), -C=O, =C=O; O = O, S, Se, Te */ - type = ATT_ACIDIC_CO; - if ( at->num_H == 1 ) { - mask |= (ATBIT_COH); /* #6: =C-OH, #C-OH; O=O,S,Se,Te */ - /*nAtTypeTotals[ATTOT_NUM_COH] ++;*/ - } else - if ( at->charge == -1 ) { - mask |= (ATBIT_CO_Minus); /* #7: =C-O(-), #C-O(-); O=O,S,Se,Te */ - /*nAtTypeTotals[ATTOT_NUM_CO_Minus] ++;*/ - } else - if ( !at->num_H && !at->charge ) { - mask |= (ATBIT_CO); /* #8 -C=O, =C=O; O=O,S,Se,Te */ - /*nAtTypeTotals[ATTOT_NUM_CO] ++;*/ - } else { - mask |= (ATBIT_Errors); - /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ - } - } else - if ( !atom[neigh].charge && - (atom[neigh].el_number == el_number_O || - atom[neigh].el_number == el_number_S || - atom[neigh].el_number == el_number_Se || - atom[neigh].el_number == el_number_Te ) && - atom[neigh].chem_bonds_valence == atom[neigh].valence ) { - /* -O-OH, -O-O(-); O = O, S, Se, Te */ - type = ATT_OO; - if ( at->num_H == 1 ) { - mask |= (ATBIT_OOH); /* #9 -O-OH */ - /*nAtTypeTotals[ATTOT_NUM_OOH] ++;*/ - } else - if ( at->charge == -1 ) { - mask |= (ATBIT_OO_Minus); /* #10 -O-O(-) */ - /*nAtTypeTotals[ATTOT_NUM_OO_Minus] ++;*/ - } else { - mask |= (ATBIT_Errors); - /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ - } - } else - if ( !atom[neigh].charge && - atom[neigh].chem_bonds_valence == atom[neigh].valence && - atom[neigh].el_number == el_number_C && - at->el_number != el_number_O ) { - /* >C-S(-), >C-SH; S = S, Se, Te */ - type = ATT_ACIDIC_S; - if ( at->num_H == 1 ) { - mask |= (ATBIT_CSH); /* #11: >C-SH, >CH-SH, -CH2-SH; S = S, Se, Te */ - /*nAtTypeTotals[ATTOT_NUM_CSH] ++;*/ - } else - if ( at->charge == -1 ) { - mask |= (ATBIT_CS_Minus); /* #12: >C-S(-), >CH-S(-), -CH2-S(-); S = S, Se, Te */ - /*nAtTypeTotals[ATTOT_NUM_CS_Minus] ++;*/ - } else { - mask |= (ATBIT_Errors); - /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ - } - } else - if ( atom[neigh].el_number == el_number_N && - atom[neigh].valence == 2 && (!atom[neigh].num_H || atom[neigh].num_H == 1 && atom[neigh].charge == 1) ) { - /* N or N(-) or NH(+) neighbor */ - type = ATT_NO; /* single bond only */ - if ( at->num_H == 1 ) { - mask |= (ATBIT_NOH); /* #13: =N-OH, =NH(+)-OH, #N(+)-OH, -N(-)-OH; O = O, S, Se, Te */ - /*nAtTypeTotals[ATTOT_NUM_NOH] ++;*/ - } else - if ( at->charge == -1 ) { - mask |= (ATBIT_NO_Minus); /* #14: =N-O(-); O = O, S, Se, Te */ - /*nAtTypeTotals[ATTOT_NUM_NO_Minus] ++;*/ - } else - if ( atom[neigh].charge == 1 || atom[neigh].charge == 0 ) { - mask |= (ATBIT_NO); /* #15: =N(+)=O, -NH(+)=O -N=O */ - /*nAtTypeTotals[ATTOT_NUM_NO] ++;*/ - } else { - mask |= (ATBIT_Errors); - /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ - } - } else - if ( atom[neigh].el_number == el_number_N ) { - type = ATT_N_O; /* #16: single bond only */ - if ( at->num_H == 1 ) { - mask |= (ATBIT_N_OH); /* #16: -NH-OH, >N-OH or >N(+)charge == -1 ) { - mask |= (ATBIT_N_O_Minus); /* #17: -NH-O(-), >N-O(-); O = O, S, Se, Te */ - /*nAtTypeTotals[ATTOT_NUM_NO_Minus] ++;*/ - } else - if ( atom[neigh].charge == 1 ) { - mask |= (ATBIT_N_O); /* #18: >N(+)=O */ - /*nAtTypeTotals[ATTOT_NUM_NO] ++;*/ - } else { - mask |= (ATBIT_Errors); - /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ - } - } else - if ( atom[neigh].el_number != el_number_C && atom[neigh].el_number != el_number_O && - !is_el_a_metal( atom[neigh].el_number ) && - atom[neigh].chem_bonds_valence > atom[neigh].valence ) { - /* =Z-OH, #Z-OH, =Z-O(-), #Z-O(-), -Z=O, =Z=O; - =Z(+)-OH, #Z(+)-OH, =Z-O(-), #Z-O(-), -Z(+)=O, =Z(+)=O; O = O, S, Se, Te */ - /* neigh = Z\{N,C} = P, As, Sb, S, Se, Te, Cl, Br, I */ - if ( at->chem_bonds_valence == 1 && IsZOX( atom, at_no, 0 ) ) { - type = ATT_ZOO; - if ( at->num_H == 1 ) { - mask |= (ATBIT_ZOOH); /* 18: O=Z-OH; O=O,S,Se,Te; Z may have charge */ - /*nAtTypeTotals[ATTOT_NUM_ZOOH] ++;*/ - } else - if ( at->charge == -1 ) { - mask |= (ATBIT_ZOO_Minus); /* 19: O=Z-O(-); O = O, S, Se, Te */ - /*nAtTypeTotals[ATTOT_NUM_ZOO_Minus] ++;*/ - } else { - mask |= (ATBIT_Errors); - /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ - } - } else { - type = ATT_OTHER_ZO; - if ( at->num_H == 1 ) { - mask |= (ATBIT_ZOH); /* 20: =Z-OH, #Z-OH; O=O,S,Se,Te; Z may have charge */ - /*nAtTypeTotals[ATTOT_NUM_ZOH] ++;*/ - } else - if ( at->charge == -1 ) { - mask |= (ATBIT_ZO_Minus); /* 21: =Z-O(-), #Z-O(-); O = O, S, Se, Te */ - /*nAtTypeTotals[ATTOT_NUM_ZO_Minus] ++;*/ - } else - if ( at->num_H == 0 ) { - mask |= (ATBIT_ZO); /* 22: -Z=O, =Z=O; O=O,S,Se,Te; Z may have charge */ - /*nAtTypeTotals[ATTOT_NUM_ZO] ++;*/ - } else { - mask |= (ATBIT_Errors); - /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ - } - } - } else - if ( at->charge == -1 && !is_el_a_metal( atom[neigh].el_number ) ) { - /* >Z-O(-); O=O,S,Se,Te */ - type = ATT_OTHER_NEG_O; - mask |= (ATBIT_O_Minus); /* 23: -Z-O(-); O=O,S,Se,Te */ - /*nAtTypeTotals[ATTOT_NUM_ZO_Minus] ++;*/ - } - } else - if ( at->charge == -1 && at->num_H == 1 ) { - type = ATT_OH_MINUS; - mask |= (ATBIT_O_Minus); /* 25: HO(-); O=O,S,Se,Te */ - } - } else - /* P, N, neutral valence = 3 (not 5) */ - if ( (at->el_number == el_number_N || - at->el_number == el_number_P) && - 0 <= at->valence && at->valence <= 3 && - at->chem_bonds_valence + at->num_H == 3 + at->charge ) { - if ( at->valence && !(num_z /*|| num_o == at->valence*/) ) { - if ( num_m == at->valence ) { - goto exit_function; - } - goto count_mask_bits; /* N(III), N(-)(II), N(+)(IV) and same P that have only oxygen neighbors are ignored here */ - } - type = (at->el_number == el_number_N)? ATT_ATOM_N : ATT_ATOM_P; - switch ( at->charge ) { - case -1: - if (at->el_number == el_number_N) { - mask |= (ATBIT_N_Minus); /* 26: -NH(-), =N(-), >N(-) */ - - if ( at->num_H ) - mask |= (ATBIT_NP_H); /* 27: -NH(-) */ -#if ( FIX_NP_MINUS_BUG == 1 ) - else - if ( at->valence == 1 && at->chem_bonds_valence >= 2 && (at->bond_type[0] & BOND_MARK_ALL) ) - type |= ATT_NP_MINUS_V23; /* =N(-) created by normalization 2010-03-11 DT */ -#endif - - } - /*nAtTypeTotals[ATTOT_NUM_N_Minus] += (at->el_number == el_number_N);*/ - break; - case 0: - if ( at->num_H ) { - mask |= (ATBIT_NP_H); /* 28: -NH2, =NH, >NH */ - /*nAtTypeTotals[ATTOT_NUM_NP_H] ++;*/ - } else { - if ( bUnsatNHasTerminalO == 1 ) { - mask |= (ATBIT_ON); /* 29: -N=O,-N=OH(+) only, not =N-OH */ - } else { - mask |= (ATBIT_NP); /* 30: -P=O,-P=OH(+), >N- =N- (incl. =N-OH) , #N */ - /*nAtTypeTotals[ATTOT_NUM_NP] ++;*/ - } - } - break; /* ignore neutral N or P */ - case 1: - if ( at->num_H ) { - mask |= (ATBIT_NP_Proton); /* 31: NH4(+), -NH3(+), =NH2(+), >NH2(+), =NH(+)-, >NH(+)-, #NH(+) */ - /*nAtTypeTotals[ATTOT_NUM_NP_Proton] ++;*/ - } else - if ( at->chem_bonds_valence > at->valence ) { - mask |= (ATBIT_NP_Plus); /* =N(+)=, #N(+)-, =N(+)< */ - /*nAtTypeTotals[ATTOT_NUM_NP_Plus] ++;*/ - } else { - type = 0; /* 32: ignore onium cations >N(+)< */ - } - break; - default: - mask |= (1 << ATTOT_NUM_Errors); - /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ - break; - } - } -count_mask_bits: - if ( nAtTypeTotals ) { - if ( mask && !(mask & (ATBIT_Errors)) ) { - for ( i = 0, bit = 1; i < ATTOT_ARRAY_LEN; i ++, bit <<= 1 ) { - if ( bit & mask ) { - nAtTypeTotals[i] += delta; - } - } - } - /* count charges */ - if ( at->charge ) { - nAtTypeTotals[ATTOT_TOT_CHARGE] += delta * at->charge; - nAtTypeTotals[ATTOT_NUM_CHARGES] += delta; - } - } - if ( pMask ) { - *pMask = mask; - } -exit_function: - if ( mask & (ATBIT_Errors) ) { - type = 0; - if ( nAtTypeTotals ) { - nAtTypeTotals[ATTOT_NUM_Errors] += 1; - } - } - return type; -} - - - -/***************************************************************************************/ -int SimpleRemoveHplusNPO( inp_ATOM *at, int num_atoms, int nAtTypeTotals[], T_GROUP_INFO *t_group_info ) -{ - int i, mask, type, num_removed; - for ( i = 0, num_removed = 0; i < num_atoms; i ++ ) { - if ( (PR_SIMPLE_TYP & (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) && - (PR_SIMPLE_MSK & mask ) ) { -#if ( bRELEASE_VERSION == 0 ) - if ( at[i].charge != 1 || at[i].num_H == 0 ) { - return -1; /* program error */ - } -#endif - type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ - at[i].charge = 0; - AddOrRemoveExplOrImplH( -1, at, num_atoms, (AT_NUMB)i, t_group_info ); - /*at[i].num_H --;*/ - num_removed ++; -#if ( FIX_NORM_BUG_ADD_ION_PAIR == 1 ) - type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ -#else - type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* bug: subtract instead of add */ -#endif - /* - if ( nAtTypeTotals ) { - nAtTypeTotals[ATTOT_NUM_NP_Proton] --; - if ( at[i].num_H ) { - nAtTypeTotals[ATTOT_NUM_NP_H] ++; - } else { - nAtTypeTotals[ATTOT_NUM_NP] ++; - } - nAtTypeTotals[ATTOT_TOT_CHARGE] --; - nAtTypeTotals[ATTOT_NUM_CHARGES] --; - } - */ - } - } - return num_removed; -} - - - -/***************************************************************************************/ -int bIsAtomTypeHard( inp_ATOM *at, int endpoint, int nType, int nMask, int nCharge ) -{ - int mask; - if ( (nType & GetAtomChargeType( at, endpoint, NULL, &mask, 0 )) && (mask & nMask) -#if ( OPPOSITE_CHARGE_IN_CGROUP == 0 ) - && ( at[endpoint].charge == nCharge || !at[endpoint].charge ) -#endif - ) { - return 1; - } - return 0; - -} - - - -/***************************************************************************************/ -int bIsHDonorAccAtomType( inp_ATOM *at, int endpoint, int *cSubType ) -{ - if ( bIsAtomTypeHard( at, endpoint, PR_HARD_TYP_H, PR_HARD_MSK_H, 0 ) ) { - /* obtain donor/acceptor info */ - int neutral_valence = at[endpoint].chem_bonds_valence + at[endpoint].num_H - at[endpoint].charge; - if ( neutral_valence != 2 /* O, S, Se, Te */ && - neutral_valence != 3 /* N, P */ ) { - return -1; /* wrong endpoint neutral valence */ - } else { - int edge_flow = at[endpoint].num_H; - int num_bonds = at[endpoint].valence; - int edge_cap = neutral_valence - num_bonds; /* does not allow to reduce -NH3(+) to #N or -OH(+)- to -O- */ - edge_flow = inchi_min( edge_flow, edge_cap); - /* what this means: */ - if ( edge_cap ) { - if ( edge_cap > edge_flow ) - *cSubType |= SALT_ACCEPTOR; - if ( edge_flow ) - *cSubType |= SALT_DONOR_H; - return 4; - } - } - } - return -1; -} - - - -/***************************************************************************************/ -int bIsNegAtomType( inp_ATOM *at, int endpoint, int *cSubType ) -{ - int sub_type = 0; - if ( bIsAtomTypeHard( at, endpoint, PR_HARD_TYP_NEG, PR_HARD_MSK_NEG, -1 ) ) { - /* obtain donor/acceptor info */ - int neutral_valence = at[endpoint].chem_bonds_valence + at[endpoint].num_H - at[endpoint].charge; - if ( neutral_valence != 2 /* O, S, Se, Te */ && - neutral_valence != 3 /* N, P */ ) { - return -1; /* wrong endpoint neutral valence */ - } else { - int edge_flow = (at[endpoint].charge == -1); - int num_bonds = at[endpoint].valence; - int edge_cap = neutral_valence - num_bonds - at[endpoint].num_H; /* does not allow to reduce -NH3(+) to #N or -OH(+)- to -O- */ - edge_flow = inchi_min( edge_flow, edge_cap); - /* what this means: */ - if ( edge_cap ) { - if ( edge_cap > edge_flow ) - sub_type |= SALT_ACCEPTOR; - if ( edge_flow ) { - sub_type |= SALT_DONOR_Neg; - } - if ( sub_type ) { - *cSubType |= sub_type; - return 4; - } - } - } - } - return -1; -} - - - -/*****************************************************************************/ -int bIsHardRemHCandidate( inp_ATOM *at, int i, int *cSubType ) -{ - int ret1, ret2, ret; - int sub_type = 0; - ret1 = bIsHDonorAccAtomType( at, i, &sub_type ); - ret2 = bIsNegAtomType( at, i, &sub_type ); - ret = inchi_max(ret1, ret2); - if ( ret > 0 && sub_type ) { - *cSubType |= sub_type; - return ret; - } - return -1; -} - - - -/***************************************************************************************/ -int CreateCGroupInBnStruct( inp_ATOM *at, int num_atoms, - BN_STRUCT *pBNS, int nType, int nMask, int nCharge ) -{ - int k, c_point, cg, centerpoint, fictpoint, type, ret = 0; - int num_cg = 1; - int num_edges = pBNS->num_edges; - int num_vertices = pBNS->num_vertices; /* new c-group bns-ID */ - BNS_VERTEX *vert_ficpoint, *ver_ficpont_prev; /* fictitious vertex describing charge c-group */ - BNS_VERTEX *vertex_cpoint; - BNS_EDGE *edge; /* edge between that vertex and the tautomeric c_point */ - int mask, num_CPoints; - - /* Debug: check overflow */ - if ( num_vertices + num_cg >= pBNS->max_vertices ) { - return BNS_VERT_EDGE_OVFL; - } - /* count new c-group edges */ - for ( c_point = 0, num_CPoints = 0; c_point < num_atoms; c_point ++ ) { - if ( (nType & GetAtomChargeType( at, c_point, NULL, &mask, 0 )) && (mask & nMask) -#if ( OPPOSITE_CHARGE_IN_CGROUP == 0 ) - && ( at[c_point].charge == nCharge || !at[c_point].charge ) -#endif - ) { - num_CPoints ++; - } - } - if ( !num_CPoints ) { - return 0; - } - - - - /* clear the new vertex */ - memset( pBNS->vert+num_vertices, 0, 1*sizeof(pBNS->vert[0]) ); - /* *old* Make sure the last t-group has the largest t-group ID: - this is necessary to correctly add new edges and vertices for testing augmenting paths - */ - /**************************************/ - /* initialize new fictitious vertex */ - /* representing c-point group */ - /**************************************/ - ver_ficpont_prev = pBNS->vert+num_vertices - 1; - - for ( cg = 0; cg < num_cg; cg ++, ver_ficpont_prev = vert_ficpoint ) { - /* - vert_ficpoint-1 is the last vertex; - vert_ficpoint is the being added vertex - Note: nGroupNumber are not contiguous - */ - vert_ficpoint = pBNS->vert+num_vertices + cg; - vert_ficpoint->iedge = ver_ficpont_prev->iedge + ver_ficpont_prev->max_adj_edges; - vert_ficpoint->max_adj_edges = num_CPoints+BNS_ADD_EDGES; - vert_ficpoint->num_adj_edges = 0; - vert_ficpoint->st_edge.flow = vert_ficpoint->st_edge.flow0 = 0; - vert_ficpoint->st_edge.cap = vert_ficpoint->st_edge.cap0 = 0; - vert_ficpoint->type = BNS_VERT_TYPE_C_GROUP | ((nCharge<0)?BNS_VERT_TYPE_C_NEGATIVE:0); - } - - /************************************************/ - /* connect c-points to the fictitious vertices */ - /* representing c-point groups; set caps, flows */ - /************************************************/ - cg = 1; - for ( c_point = 0; c_point < num_atoms; c_point ++ ) { - if ( (nType & (type=GetAtomChargeType( at, c_point, NULL, &mask, 0 ))) && (mask & nMask) -#if ( OPPOSITE_CHARGE_IN_CGROUP == 0 ) - && ( at[c_point].charge == nCharge || !at[c_point].charge) -#endif - ); - else - continue; - fictpoint = cg + num_vertices - 1; /* c-group vertex index */ - vert_ficpoint = pBNS->vert + fictpoint; /* c-group vertex */ - vertex_cpoint = pBNS->vert + c_point; /* c_point vertex */ - /* Debug: check overflow */ - if ( fictpoint >= pBNS->max_vertices || - num_edges >= pBNS->max_edges || - vert_ficpoint->num_adj_edges >= vert_ficpoint->max_adj_edges || - vertex_cpoint->num_adj_edges >= vertex_cpoint->max_adj_edges ) { - ret = BNS_VERT_EDGE_OVFL; - break; - } - vertex_cpoint->type |= BNS_VERT_TYPE_C_POINT; - if ( (KNOWN_ACIDIC_TYPE & type) && nCharge < 0 ) { - vertex_cpoint->type |= pBNS->type_TACN; - } -#if ( FIX_CPOINT_BOND_CAP != 1 ) /* { */ - /* set capacity = 1 to the edges from the c_point to the centerpoint(s) */ - /* if their current capacity is zero */ - /* the centerpoint is any adjacent atom that is adjacent to a multiple bond */ - for ( k = 0; k < vertex_cpoint->num_adj_edges; k ++ ) { - int iedge = vertex_cpoint->iedge[k]; - if ( !pBNS->edge[iedge].cap ) { - /* single bond, possibly between c_point and centerpoint */ - centerpoint = (pBNS->edge[iedge].neighbor12 ^ c_point); - if ( centerpoint < pBNS->num_atoms && - pBNS->vert[centerpoint].st_edge.cap >= 1 ) { - int bond_type = (at[c_point].bond_type[k] & BOND_TYPE_MASK); - if ( bond_type == BOND_TAUTOM || - bond_type == BOND_ALTERN || - bond_type == BOND_SINGLE ) { - pBNS->edge[iedge].cap = 1; - } - } - } - } -#endif /* } FIX_CPOINT_BOND_CAP */ - /* create a new edge connecting c_point to the new fictitious c-group vertex vert_ficpoint */ - edge = pBNS->edge + num_edges; - edge->cap = 1; - edge->flow = 0; - edge->pass = 0; -#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) - edge->forbidden &= pBNS->edge_forbidden_mask; /* remove previous temporary ban */ -#endif - /* nCharge = +1: mark edge to c-point having no (+)-moveable charge with flow=1 */ - /* nCharge = -1: mark edge to c-point having -1 moveable charge with flow=1 */ - if ( nCharge==1 && at[c_point].charge != 1 || nCharge==-1 && at[c_point].charge == -1 ) - /*if ( !CHARGED_CPOINT(at,c_point) )*/ - { - /* increment new edge flow, update st_edges of the adjacent vertices */ - edge->flow ++; - /* increment c-group vertex st-flow & cap */ - vert_ficpoint->st_edge.flow ++; - vert_ficpoint->st_edge.cap ++; - /* increment c-point vertex st-flow & cap */ - vertex_cpoint->st_edge.flow ++; - vertex_cpoint->st_edge.cap ++; - } -#if ( FIX_CPOINT_BOND_CAP == 1 ) /* { */ - /* set capacity = 1 to the edges from the c_point to the centerpoint(s) */ - /* if their current capacity is zero */ - /* the centerpoint is any adjacent atom that is adjacent to a multiple bond */ - for ( k = 0; k < vertex_cpoint->num_adj_edges; k ++ ) { - int iedge = vertex_cpoint->iedge[k]; - VertexFlow nNewCap = vertex_cpoint->st_edge.cap; - centerpoint = (pBNS->edge[iedge].neighbor12 ^ c_point); - if ( !pBNS->edge[iedge].cap ) { - /* single bond, possibly between c_point and centerpoint */ - if ( centerpoint < pBNS->num_atoms && - pBNS->vert[centerpoint].st_edge.cap >= 1 ) { - nNewCap = inchi_min( pBNS->vert[centerpoint].st_edge.cap, nNewCap ); - nNewCap = inchi_min( nNewCap, MAX_BOND_EDGE_CAP ); - pBNS->edge[iedge].cap = nNewCap; - } - } -#if ( FIX_CPOINT_BOND_CAP2 == 1 ) /* multiple bond */ - else - if ( centerpoint < pBNS->num_atoms && - edge->flow && pBNS->edge[iedge].cap < MAX_BOND_EDGE_CAP ) { - pBNS->edge[iedge].cap ++; - } -#endif - } -#endif /* } FIX_CPOINT_BOND_CAP */ - /* connect edge to c_point and fictpoint and increment the counters of neighbors and edges */ - edge->neighbor1 = c_point; /* the smallest out of v1=endopoint and v2=num_vertices */ - edge->neighbor12 = c_point ^ fictpoint; /* v1 ^ v2 */ - vertex_cpoint->iedge[vertex_cpoint->num_adj_edges] = num_edges; - vert_ficpoint->iedge[vert_ficpoint->num_adj_edges] = num_edges ++; - edge->neigh_ord[0] = vertex_cpoint->num_adj_edges ++; - edge->neigh_ord[1] = vert_ficpoint->num_adj_edges ++; - edge->cap0 = edge->cap; - edge->flow0 = edge->flow; - - } - ret = pBNS->num_vertices; /* new c-group atom number */ - pBNS->num_edges = num_edges; - pBNS->num_vertices += num_cg; - pBNS->num_c_groups += num_cg; - - return ret; -} - -/*********************************************************************************/ -int CreateTGroupInBnStruct( inp_ATOM *at, int num_atoms, - BN_STRUCT *pBNS, int nType, int nMask ) -{ - int ret = 0; - /* ret = ReInitBnStruct( pBNS ); */ - int k, endpoint, tg, centerpoint, fictpoint; - int num_tg = 1; - int num_edges = pBNS->num_edges; - int num_vertices = pBNS->num_vertices; - BNS_VERTEX *vert_ficpoint, *ver_ficpont_prev; /* fictitious vertex describing t-group */ - BNS_VERTEX *vert_endpoint; - BNS_EDGE *edge; /* edge between that vertex and the tautomeric endpoint */ - int mask, num_endpoints, neutral_valence, edge_flow, edge_cap, num_bonds; - - /* Debug: check overflow */ - if ( num_vertices + num_tg >= pBNS->max_vertices ) { - return BNS_VERT_EDGE_OVFL; - } - /* count new t-group edges */ - for ( endpoint = 0, num_endpoints = 0; endpoint < num_atoms; endpoint ++ ) { - if ( (nType & GetAtomChargeType( at, endpoint, NULL, &mask, 0 )) && (mask & nMask) - ) { - num_endpoints ++; - } - } - if ( !num_endpoints ) { - return 0; - } - - - /* since t-group IDs may be not contiguous, clear all vertices that will be added. - all-zeroes-vertex will be ignored by the BNS - */ - memset( pBNS->vert+num_vertices, 0, num_tg*sizeof(pBNS->vert[0]) ); - /* *old* Make sure the last t-group has the largest t-group ID: - this is necessary to correctly add new edges and vertices for testing augmenting paths - */ - /**************************************/ - /* initialize new fictitious vertex */ - /* representing t-point group */ - /**************************************/ - ver_ficpont_prev = pBNS->vert+num_vertices - 1; - - for ( tg = 0; tg < num_tg; tg ++, ver_ficpont_prev = vert_ficpoint ) { - /* - vert_ficpoint-1 is the last vertex; - vert_ficpoint is the vertex that is being added - Note: nGroupNumber are not contiguous - */ - vert_ficpoint = pBNS->vert+num_vertices + tg; - vert_ficpoint->iedge = ver_ficpont_prev->iedge + ver_ficpont_prev->max_adj_edges; - vert_ficpoint->max_adj_edges = num_endpoints+BNS_ADD_EDGES+BNS_ADD_SUPER_TGROUP; - vert_ficpoint->num_adj_edges = 0; - vert_ficpoint->st_edge.flow = vert_ficpoint->st_edge.flow0 = 0; - vert_ficpoint->st_edge.cap = vert_ficpoint->st_edge.cap0 = 0; - vert_ficpoint->type |= BNS_VERT_TYPE_TGROUP; - } - tg = 1; - for ( endpoint = 0; endpoint < num_atoms; endpoint ++ ) { - if ( (nType & GetAtomChargeType( at, endpoint, NULL, &mask, 0 )) && (mask & nMask)); - else - continue; - fictpoint = tg + num_vertices - 1; - vert_ficpoint = pBNS->vert + fictpoint; - vert_endpoint = pBNS->vert + endpoint; - /* Debug: check overflow */ - if ( fictpoint >= pBNS->max_vertices || - num_edges >= pBNS->max_edges || - vert_ficpoint->num_adj_edges >= vert_ficpoint->max_adj_edges || - vert_endpoint->num_adj_edges >= vert_endpoint->max_adj_edges ) { - ret = BNS_VERT_EDGE_OVFL; - break; - } - /* obtain donor/acceptor info */ - neutral_valence = at[endpoint].chem_bonds_valence + at[endpoint].num_H - at[endpoint].charge; - if ( neutral_valence != 2 /* O, S, Se, Te */ && - neutral_valence != 3 /* N, P */ ) { - ret = BNS_PROGRAM_ERR; /* wrong endpoint neutral valence */ - break; - } - edge_flow = at[endpoint].num_H; - num_bonds = at[endpoint].valence; - edge_cap = neutral_valence - num_bonds; /* does not allow to reduce -NH3(+) to #N or -OH(+)- to -O- */ - if ( 3 == neutral_valence /* N or P */ && 1 < num_bonds ) { - edge_cap ++; /* allow -NH2(+)- => -N=, >NH(+)- => >N- */ - } - edge_flow = inchi_min( edge_flow, edge_cap); - /* - if ( !nGetEndpointInfo( at, endpoint, &eif ) ) { - ret = BNS_BOND_ERR; - break; - } - */ - vert_endpoint->type |= BNS_VERT_TYPE_ENDPOINT; - /* create a new edge connecting endpoint to the new fictitious t-group vertex vert_ficpoint */ - edge = pBNS->edge + num_edges; - edge->cap = edge_cap; - edge->flow = edge_flow; - edge->pass = 0; -#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) - edge->forbidden &= pBNS->edge_forbidden_mask; -#endif - /* adjust st_flow and st_cap of the adjacent vertices */ - /* adjust t-group vertex st-flow & cap */ - vert_ficpoint->st_edge.flow += edge->flow; - vert_ficpoint->st_edge.cap += edge->flow; - /* adjust endpoint vertex st-flow & cap */ - vert_endpoint->st_edge.flow += edge->flow; - vert_endpoint->st_edge.cap += edge->flow; - - /* adjust edge cap & flow according to the number of H and number of bonds */ - for ( k = 0; k < vert_endpoint->num_adj_edges; k ++ ) { - int iedge = vert_endpoint->iedge[k]; - VertexFlow nNewCap = vert_endpoint->st_edge.cap; - if ( !pBNS->edge[iedge].cap ) { - /* single bond, possibly between endpoint and centerpoint */ - centerpoint = (pBNS->edge[iedge].neighbor12 ^ endpoint); - if ( centerpoint < pBNS->num_atoms && - pBNS->vert[centerpoint].st_edge.cap >= 1 ) { - nNewCap = inchi_min( pBNS->vert[centerpoint].st_edge.cap, nNewCap ); - nNewCap = inchi_min( nNewCap, MAX_BOND_EDGE_CAP ); - pBNS->edge[iedge].cap = nNewCap; - } - } - } - - /* connect edge to endpoint and fictpoint and increment the counters of neighbors and edges */ - edge->neighbor1 = endpoint; /* the smallest out of v1=endopoint and v2=num_vertices */ - edge->neighbor12 = endpoint ^ fictpoint; /* v1 ^ v2 */ - vert_endpoint->iedge[vert_endpoint->num_adj_edges] = num_edges; - vert_ficpoint->iedge[vert_ficpoint->num_adj_edges] = num_edges ++; - edge->neigh_ord[0] = vert_endpoint->num_adj_edges ++; - edge->neigh_ord[1] = vert_ficpoint->num_adj_edges ++; - edge->cap0 = edge->cap; - edge->flow0 = edge->flow; - } - - ret = pBNS->num_vertices; /* new t-group atom number */ - pBNS->num_edges = num_edges; - pBNS->num_vertices += num_tg; - pBNS->num_t_groups += num_tg; - - return ret; -} - - - -/*********************************************************************************/ -int RemoveLastGroupFromBnStruct( inp_ATOM *at, int num_atoms, int tg, BN_STRUCT *pBNS ) -{ - int ret = 0; - /* ret = ReInitBnStruct( pBNS ); */ - int k, endpoint, /*centerpoint, fictpoint,*/ iedge; - int num_edges = pBNS->num_edges; - int num_vertices = pBNS->num_vertices; - BNS_VERTEX *vert_ficpoint /*, *ver_ficpont_prev*/; /* fictitious vertex describing t-group */ - BNS_VERTEX *vert_endpoint; - BNS_EDGE *edge; /* edge between that vertex and the tautomeric endpoint */ - /*int mask, num_endpoints, neutral_valence, edge_flow, edge_cap, num_bonds;*/ - int is_t_group = 0, is_c_group = 0; - - /* Debug: check overflow */ - if ( pBNS->num_added_atoms + pBNS->num_c_groups + pBNS->num_t_groups + num_atoms >= pBNS->max_vertices ) { - return BNS_VERT_EDGE_OVFL; - } - if ( tg + 1 != num_vertices ) { - return BNS_VERT_EDGE_OVFL; - } - vert_ficpoint = pBNS->vert + tg; - if ( vert_ficpoint->type & BNS_VERT_TYPE_TGROUP ) { - is_t_group = 1; - } - if ( vert_ficpoint->type & BNS_VERT_TYPE_C_GROUP ) { - is_c_group = 1; - if ( vert_ficpoint->type & BNS_VERT_TYPE_C_NEGATIVE ) - is_c_group = 2; - } - for ( k = vert_ficpoint->num_adj_edges-1; 0 <= k; k -- ) { - iedge = vert_ficpoint->iedge[k]; - if ( iedge + 1 != num_edges ) { - return BNS_VERT_EDGE_OVFL; - } - edge = pBNS->edge + iedge; - endpoint = edge->neighbor12 ^ tg; - vert_endpoint = pBNS->vert + endpoint; - /* adjust st_flow, st_cap */ - vert_endpoint->st_edge.cap0 = - vert_endpoint->st_edge.cap -= edge->flow; - vert_endpoint->st_edge.flow0 = - vert_endpoint->st_edge.flow -= edge->flow; - if ( pBNS->type_TACN && (vert_endpoint->type & pBNS->type_TACN) == pBNS->type_TACN ) { - vert_endpoint->type ^= pBNS->type_TACN; - } - if ( is_t_group ) { - vert_endpoint->type ^= (vert_ficpoint->type & BNS_VERT_TYPE_ENDPOINT); - } - if ( is_c_group ) { - vert_endpoint->type ^= (vert_ficpoint->type & BNS_VERT_TYPE_C_POINT); - } - /* remove edge */ - if ( edge->neigh_ord[0]+1 != vert_endpoint->num_adj_edges ) { - return BNS_VERT_EDGE_OVFL; - } - vert_endpoint->num_adj_edges --; - memset( edge, 0, sizeof(*edge) ); - num_edges --; - if ( 1 == is_t_group && endpoint < num_atoms ) { - at->endpoint = 0; - } - if ( 1 == is_c_group && endpoint < num_atoms ) { - at->c_point = 0; - } - } - memset( vert_ficpoint, 0, sizeof(*vert_ficpoint) ); - num_vertices --; - - pBNS->num_edges = num_edges; - pBNS->num_vertices = num_vertices; - if ( is_t_group ) - pBNS->num_t_groups --; - if ( is_c_group ) - pBNS->num_c_groups --; - - return ret; -} - - - -/******************************************************************************************/ -int SetInitCapFlowToCurrent( BN_STRUCT *pBNS ) -{ - int i, j; - BNS_EDGE *pEdge=NULL; - for ( i = 0; i < pBNS->num_vertices; i ++ ) { - pBNS->vert[i].st_edge.flow0 = pBNS->vert[i].st_edge.flow; - pBNS->vert[i].st_edge.cap0 = pBNS->vert[i].st_edge.cap; - for ( j = 0; j < pBNS->vert[i].num_adj_edges; j ++ ) { - pEdge = pBNS->edge + pBNS->vert[i].iedge[j]; - pEdge->cap0 = pEdge->cap; - pEdge->flow0 = pEdge->flow; - } - } - return 0; -} - - - -/******************************************************************************************/ -int ArTypMask[] = { - AR_SIMPLE_TYP1, AR_SIMPLE_MSK1, - AR_SIMPLE_TYP2, AR_SIMPLE_MSK2, - AR_SIMPLE_TYP3, AR_SIMPLE_MSK3, - 0, 0 }; - - - -/******************************************************************************************/ -int SimpleRemoveAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2remove ) -{ - int i, j, max_j=-1, mask, type, num_removed; - int num[AR_SIMPLE_STEPS+1], num_tot; - - for ( j = 0; ArTypMask[2*j]; j ++ ) { - num[max_j = j] = 0; - } - - for ( i = 0; i < num_atoms; i ++ ) { - if ( !at[i].charge && at[i].num_H && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { - for ( j = 0; j <= max_j; j ++ ) { - if ( (type & ArTypMask[2*j]) && (mask && ArTypMask[2*j+1]) ) { - num[j] ++; - break; - } - } - } - } - for ( j = 0, num_tot = 0; j <= max_j; j ++ ) { - if ( (num_tot += num[j]) >= num2remove ) { - max_j = j; - break; - } - } - if ( !num_tot ) { - return 0; - } - for ( i = 0, num_removed = 0; i < num_atoms && num_removed < num2remove; i ++ ) { - if ( !at[i].charge && at[i].num_H && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { - for ( j = 0; j <= max_j; j ++ ) { - if ( num[j] && (type & ArTypMask[2*j]) && (mask && ArTypMask[2*j+1]) ) { - type = GetAtomChargeType( at, i, pAATG->nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ - num[j] --; - at[i].charge --; - AddOrRemoveExplOrImplH( -1, at, num_atoms, (AT_NUMB)i, pAATG->t_group_info ); - /*at[i].num_H --;*/ - num_removed ++; - type = GetAtomChargeType( at, i, pAATG->nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ - break; - } - } - } - } - /* - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] -= num_removed; - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] += num_removed; - */ - return num_removed; -} - - - -/******************************************************************************************/ -int bHasAcidicHydrogen( inp_ATOM *at, int i ) -{ - int bFound = 0, j, type, mask; - if ( !at[i].charge && at[i].num_H && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { - for ( j = 0; ArTypMask[2*j]; j ++ ) { - if ( (type & ArTypMask[2*j]) && (mask & ArTypMask[2*j+1]) ) { - bFound ++; - break; - } - } - } - return bFound; -} - - - -/******************************************************************************************/ -int bHasOtherExchangableH ( inp_ATOM *at, int i ) -{ - int bFound = 0, type, mask; - if ( at[i].num_H && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { - if ( (type & ATT_ATOM_N) && (mask & ATBIT_NP_H) ) { - bFound ++; - } - } - return bFound; -} - - - -/******************************************************************************************/ -int AaTypMask[] = { - AA_SIMPLE_TYP1, AA_SIMPLE_MSK1, -#if ( FIX_NP_MINUS_BUG == 1 ) - AA_SIMPLE_TYP4, AA_SIMPLE_MSK4, /* should not follow 0,0 pair */ -#endif - AA_SIMPLE_TYP2, AA_SIMPLE_MSK2, - AA_SIMPLE_TYP3, AA_SIMPLE_MSK3, - 0, 0 }; - - - -/******************************************************************************************/ -int SimpleAddAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2add ) -{ - int i, j, max_j=-1, mask, type, num_added; - int num[AR_SIMPLE_STEPS+1], num_tot; - - for ( j = 0; AaTypMask[2*j]; j ++ ) { - num[max_j = j] = 0; - } - - for ( i = 0; i < num_atoms; i ++ ) { - if ( at[i].charge==-1 && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { - for ( j = 0; j <= max_j; j ++ ) { - if ( (type & AaTypMask[2*j]) && (mask && AaTypMask[2*j+1]) ) { - num[j] ++; - break; - } - } - } - } - for ( j = 0, num_tot = 0; j <= max_j; j ++ ) { - if ( (num_tot += num[j]) >= num2add ) { - max_j = j; - break; - } - } - if ( !num_tot ) { - return 0; - } - for ( i = 0, num_added = 0; i < num_atoms && num_added < num2add; i ++ ) { - if ( at[i].charge==-1 && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { - for ( j = 0; j <= max_j; j ++ ) { - if ( num[j] && (type & AaTypMask[2*j]) && (mask && AaTypMask[2*j+1]) ) { - type = GetAtomChargeType( at, i, pAATG->nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ - num[j] --; - at[i].charge ++; - AddOrRemoveExplOrImplH( 1, at, num_atoms, (AT_NUMB)i, pAATG->t_group_info ); - /*at[i].num_H ++;*/ - num_added ++; - type = GetAtomChargeType( at, i, pAATG->nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ - break; - } - } - } - } - /* - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] += num_added; - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] -= num_added; - */ - return num_added; -} - - - -/******************************************************************************************/ -int bHasAcidicMinus( inp_ATOM *at, int i ) -{ - int bFound = 0, j, type, mask; - if ( at[i].charge==-1 && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { - for ( j = 0; AaTypMask[2*j]; j ++ ) { - if ( (type & AaTypMask[2*j]) && (mask & AaTypMask[2*j+1]) ) { - bFound ++; - break; - } - } - } - return bFound; -} - - - -/****************************************************************************************** -Create 2 tautomeric groups: (1) for O on -C=O, (2) for the rest of the atoms. -Pull H from (2) to (1); remove later -*******************************************************************************************/ -int HardRemoveAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2remove, int *nNumCanceledCharges, BN_STRUCT *pBNS, BN_DATA *pBD ) -{ - int cg_Plus = 0; - int cg_Minus = 0; - int tg_H_Other = 0; - int tg_H_Acid = 0; - - int ret = 0, ret2; - int nDelta, nNumChanges = 0, nNumMoved2AcidH = 0, nNumNeutralized = 0, nPrevNumCharges; - - int nPosCharges, nPosCharges2; - int nNegCharges, nNegCharges2; - /* - int nNumNP_H, nNumNP_H2; - int nNumOS_H, nNumOS_H2; - */ - - nPosCharges = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; - nNegCharges = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; - /* - nNumNP_H = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] + - pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton]; - nNumOS_H = pAATG->nAtTypeTotals[ATTOT_NUM_COH] + - pAATG->nAtTypeTotals[ATTOT_NUM_CSH] + - pAATG->nAtTypeTotals[ATTOT_NUM_ZOH]; - */ - - /* prevent free exchange H <-> (-) */ - pBNS->type_CN = (BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE); - pBNS->type_T = BNS_VERT_TYPE_TGROUP; - pBNS->type_TACN = BNS_VERT_TYPE_ACID; - /* create (+) charge group */ - cg_Plus = CreateCGroupInBnStruct( at, num_atoms, pBNS, AR_HARD_TYP_POS, AR_HARD_MSK_POS, 1 ); - /* create (-) charge group */ - /* - if ( nAtTypeTotals[ATTOT_NUM_CO_Minus] + - nAtTypeTotals[ATTOT_NUM_CS_Minus] + - nAtTypeTotals[ATTOT_NUM_ZO_Minus] + - nAtTypeTotals[ATTOT_NUM_N_Minus] ) - */ - cg_Minus = CreateCGroupInBnStruct( at, num_atoms, pBNS, AR_HARD_TYP_NEG, AR_HARD_MSK_NEG, -1 ); - - pBNS->type_CN = (BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE); - pBNS->type_T = BNS_VERT_TYPE_TGROUP; - pBNS->type_TACN = BNS_VERT_TYPE_ACID; - - /* create tautomeric group for non-acidic or negatively charged acidic O */ - tg_H_Other = CreateTGroupInBnStruct( at, num_atoms, pBNS, AR_HARD_TYP_HN, AR_HARD_MSK_HN ); - - /* create tautomeric group for possibly acidic O */ - tg_H_Acid = CreateTGroupInBnStruct( at, num_atoms, pBNS, AR_HARD_TYP_HA, AR_HARD_MSK_HA ); - if ( tg_H_Other >= num_atoms && tg_H_Acid >= num_atoms ) { - /* find alt path to remove one proton */ - do { - /* remove a proton */ - nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; - ret = bExistsAltPath( pBNS, pBD, pAATG, at, num_atoms, - tg_H_Other /*nVertDoubleBond*/, tg_H_Acid /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON ); - if ( IS_BNS_ERROR( ret ) ) { - return ret; - } - if ( ret & 1 ) { - nDelta = (ret & ~3) >> 2; - nNumChanges += (0 != (ret & 2)); - if ( nDelta ) { - /* radical pair has disappeared */ - ; /* goto quick_exit;*/ - } - nNumMoved2AcidH ++; - if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + 1 ) { - nNumNeutralized += (nPrevNumCharges - (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - 1))/2; - } - } - - } while ( (ret & 1) && nNumMoved2AcidH < num2remove ); - - /* neutralize: remove ion pairs like >N(+)=-O(-) => >N-=O */ - if ( (nNumMoved2AcidH /*|| bCancelChargesAlways*/) && cg_Minus >= num_atoms && cg_Plus >= num_atoms && - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] > abs(pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE]) ) { - do { - nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; - ret = bExistsAltPath( pBNS, pBD, pAATG, at, num_atoms, - cg_Minus /*nVertDoubleBond*/, cg_Plus /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON ); - if ( IS_BNS_ERROR( ret ) ) { - return ret; - } - if ( ret & 1 ) { - nDelta = (ret & ~3) >> 2; - nNumChanges += (0 != (ret & 2)); - if ( nDelta ) { - /* radical pair has disappeared */ - ; /* goto quick_exit;*/ - } - if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] ) { - nNumNeutralized += (nPrevNumCharges - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES])/2; - } - } - } while ( ret & 1 ); - } - } - - ret = 0; - if ( tg_H_Acid >= num_atoms ) { - ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, tg_H_Acid, pBNS ); - if ( !ret && ret2 ) - ret = ret2; - } - if ( tg_H_Other >= num_atoms ) { - ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, tg_H_Other, pBNS ); - if ( !ret && ret2 ) - ret = ret2; - } - if ( cg_Minus >= num_atoms ) { - ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Minus, pBNS ); - if ( !ret && ret2 ) - ret = ret2; - } - if ( cg_Plus >= num_atoms ) { - ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Plus, pBNS ); - if ( !ret && ret2 ) - ret = ret2; - } - - pBNS->type_CN = 0; - pBNS->type_T = 0; - pBNS->type_TACN = 0; - - if ( ret ) { - return ret; - } - if ( pAATG->nAtTypeTotals[ATTOT_NUM_CO_Minus] + pAATG->nAtTypeTotals[ATTOT_NUM_ZO_Minus] && - pAATG->nAtTypeTotals[ATTOT_NUM_N_Minus] ) { - } - - nPosCharges2 = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; - nNegCharges2 = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; - /* - nNumNP_H2 = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] + - pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton]; - nNumOS_H2 = pAATG->nAtTypeTotals[ATTOT_NUM_COH] + - pAATG->nAtTypeTotals[ATTOT_NUM_CSH] + - pAATG->nAtTypeTotals[ATTOT_NUM_ZOH]; - */ - if ( (nPosCharges - nNegCharges) - (nPosCharges2 - nNegCharges2) != 0 ) { - return BNS_PROGRAM_ERR; - } - - if ( nNumCanceledCharges ) { -#if ( FIX_CANCEL_CHARGE_COUNT_BUG == 1 ) - *nNumCanceledCharges += 2*nNumNeutralized; -#else - *nNumCanceledCharges = 2*nNumNeutralized; -#endif - } - - return nNumMoved2AcidH; -} - - - -/******************************************************************************************/ -int HardAddAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2add, int *nNumCanceledCharges, BN_STRUCT *pBNS, BN_DATA *pBD ) -{ - int cg_Plus = 0; - int cg_Minus_CO = 0; - int cg_Minus_Other = 0; - int tg_H = 0; - - int ret = 0, ret2; - int nDelta, nNumChanges = 0, nNumMoved2AcidMinus = 0, nNumNeutralized = 0, nPrevNumCharges; - - int nPosCharges, nPosCharges2; - int nNegCharges, nNegCharges2; - /* - int nNumNP_H, nNumNP_H2; - int nNumOS_H, nNumOS_H2; - */ - nPosCharges = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; - nNegCharges = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; - /* - nNumNP_H = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] + - pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton]; - nNumOS_H = pAATG->nAtTypeTotals[ATTOT_NUM_COH] + - pAATG->nAtTypeTotals[ATTOT_NUM_CSH] + - pAATG->nAtTypeTotals[ATTOT_NUM_ZOH]; - */ - /* prevent free exchange H <-> (-) */ - pBNS->type_CN = (BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE); - pBNS->type_T = BNS_VERT_TYPE_TGROUP; - pBNS->type_TACN = BNS_VERT_TYPE_ACID; - /* create (+) charge group */ - cg_Plus = CreateCGroupInBnStruct( at, num_atoms, pBNS, AA_HARD_TYP_POS, AA_HARD_MSK_POS, 1 ); - /* create (-) charge group */ - /* - if ( nAtTypeTotals[ATTOT_NUM_CO_Minus] + - nAtTypeTotals[ATTOT_NUM_CS_Minus] + - nAtTypeTotals[ATTOT_NUM_ZO_Minus] + - nAtTypeTotals[ATTOT_NUM_N_Minus] ) - */ - cg_Minus_CO = CreateCGroupInBnStruct( at, num_atoms, pBNS, AA_HARD_TYP_CO, AA_HARD_MSK_CO, -1 ); - - cg_Minus_Other = CreateCGroupInBnStruct( at, num_atoms, pBNS, AA_HARD_TYP_NEG, AA_HARD_MSK_NEG, -1 ); - - pBNS->type_CN = (BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE); - pBNS->type_T = BNS_VERT_TYPE_TGROUP; - pBNS->type_TACN = BNS_VERT_TYPE_ACID; - - /* create tautomeric group for all H */ - tg_H = CreateTGroupInBnStruct( at, num_atoms, pBNS, AA_HARD_TYP_H, AA_HARD_MSK_H ); - - - if ( cg_Minus_Other >= num_atoms && cg_Minus_CO >= num_atoms ) { - /* find alt path to remove one proton */ - do { - /* add a proton */ - nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; - ret = bExistsAltPath( pBNS, pBD, pAATG, at, num_atoms, - cg_Minus_Other /*nVertDoubleBond*/, cg_Minus_CO /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON ); - if ( IS_BNS_ERROR( ret ) ) { - return ret; - } - if ( ret & 1 ) { - nDelta = (ret & ~3) >> 2; - nNumChanges += (0 != (ret & 2)); - if ( nDelta ) { - /* radical pair has disappeared */ - ; /* goto quick_exit;*/ - } - nNumMoved2AcidMinus ++; - if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + 1 ) { - nNumNeutralized += (nPrevNumCharges - (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - 1))/2; - } - } - - } while ( (ret & 1) && nNumMoved2AcidMinus < num2add ); - - /* neutralize: remove ion pairs like >N(+)=-O(-) => >N-=O */ - if ( (nNumMoved2AcidMinus /*|| bCancelChargesAlways*/) && cg_Minus_Other >= num_atoms && cg_Plus >= num_atoms && - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] > abs(pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE]) ) { - do { - nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; - ret = bExistsAltPath( pBNS, pBD, pAATG, at, num_atoms, - cg_Minus_Other /*nVertDoubleBond*/, cg_Plus /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON ); - if ( IS_BNS_ERROR( ret ) ) { - return ret; - } - if ( ret & 1 ) { - nDelta = (ret & ~3) >> 2; - nNumChanges += (0 != (ret & 2)); - if ( nDelta ) { - /* radical pair has disappeared */ - ; /* goto quick_exit;*/ - } - if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] ) { - nNumNeutralized += (nPrevNumCharges - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES])/2; - } - } - } while ( ret & 1 ); - } - } - - ret = 0; - if ( tg_H >= num_atoms ) { - ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, tg_H, pBNS ); - if ( !ret && ret2 ) - ret = ret2; - } - if ( cg_Minus_Other >= num_atoms ) { - ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Minus_Other, pBNS ); - if ( !ret && ret2 ) - ret = ret2; - } - if ( cg_Minus_CO >= num_atoms ) { - ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Minus_CO, pBNS ); - if ( !ret && ret2 ) - ret = ret2; - } - if ( cg_Plus >= num_atoms ) { - ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Plus, pBNS ); - if ( !ret && ret2 ) - ret = ret2; - } - - pBNS->type_CN = 0; - pBNS->type_T = 0; - pBNS->type_TACN = 0; - - if ( ret ) { - return ret; - } - if ( pAATG->nAtTypeTotals[ATTOT_NUM_CO_Minus] + pAATG->nAtTypeTotals[ATTOT_NUM_ZO_Minus] && - pAATG->nAtTypeTotals[ATTOT_NUM_N_Minus] ) { - } - - nPosCharges2 = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; - nNegCharges2 = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; - /* - nNumNP_H2 = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] + - pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton]; - nNumOS_H2 = pAATG->nAtTypeTotals[ATTOT_NUM_COH] + - pAATG->nAtTypeTotals[ATTOT_NUM_CSH] + - pAATG->nAtTypeTotals[ATTOT_NUM_ZOH]; - */ - if ( (nPosCharges - nNegCharges) - (nPosCharges2 - nNegCharges2) != 0 ) { - return BNS_PROGRAM_ERR; - } - - if ( nNumCanceledCharges ) { -#if ( FIX_CANCEL_CHARGE_COUNT_BUG == 1 ) - *nNumCanceledCharges += 2*nNumNeutralized; -#else - *nNumCanceledCharges = 2*nNumNeutralized; -#endif - } - - return nNumMoved2AcidMinus; -} - - - -/******************************************************************************************/ -/* examples include removal of H from tautomeric O that belongs to the same t-group as N: */ -/* >N(+)=-N=-OH =(taut.)=> >N(+)=-NH-=O =(+charge move)=> >N-=NH(+)-=O => >N-=N-=O + H(+) */ -/******************************************************************************************/ -int HardRemoveHplusNP( inp_ATOM *at, int num_atoms, int bCancelChargesAlways, int *nNumCanceledCharges, - BN_AATG *pAATG, BN_STRUCT *pBNS, BN_DATA *pBD ) -{ - - int cg_Plus = 0; - int cg_Minus = 0; - int tg_H = 0; -#if ( MOVE_PPLUS_TO_REMOVE_PROTONS == 1 ) - int cg_PlusP = 0; -#endif -#if ( FIX_REM_PROTON_COUNT_BUG == 1 ) - int nPrevRemovedProtons, nCurrRemovedProtons; -#endif - int ret = 0, ret2; - int nDelta, nNumChanges = 0, nNumRemovedProtons = 0, nNumNeutralized = 0, nPrevNumCharges; - - int nPosCharges, nPosCharges2; - int nNegCharges, nNegCharges2; - /* - int nNumNP_H, nNumNP_H2; - int nNumOS_H, nNumOS_H2; - */ - - nPosCharges = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; - nNegCharges = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; - /* - nNumNP_H = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] + - pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton]; - nNumOS_H = pAATG->nAtTypeTotals[ATTOT_NUM_COH] + - pAATG->nAtTypeTotals[ATTOT_NUM_CSH] + - pAATG->nAtTypeTotals[ATTOT_NUM_ZOH]; - */ - /* prevent free exchange H <-> (-) */ - pBNS->type_CN = (BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE); - pBNS->type_T = BNS_VERT_TYPE_TGROUP; - pBNS->type_TACN = BNS_VERT_TYPE_ACID; - /* create (+) charge group */ - cg_Plus = CreateCGroupInBnStruct( at, num_atoms, pBNS, PR_HARD_TYP_POS, PR_HARD_MSK_POS, 1 ); - /* create (-) charge group */ - /* - if ( nAtTypeTotals[ATTOT_NUM_CO_Minus] + - nAtTypeTotals[ATTOT_NUM_CS_Minus] + - nAtTypeTotals[ATTOT_NUM_ZO_Minus] + - nAtTypeTotals[ATTOT_NUM_N_Minus] ) - */ -#if ( MOVE_PPLUS_TO_REMOVE_PROTONS == 1 ) - cg_PlusP = CreateCGroupInBnStruct( at, num_atoms, pBNS, PR_HARD_TYP_POSP, PR_HARD_MSK_POS, 1 ); -#endif - cg_Minus = CreateCGroupInBnStruct( at, num_atoms, pBNS, PR_HARD_TYP_NEG, PR_HARD_MSK_NEG, -1 ); - - /* create single tautomeric group */ - tg_H = CreateTGroupInBnStruct( at, num_atoms, pBNS, PR_HARD_TYP_H, PR_HARD_MSK_H ); - - if ( tg_H >= num_atoms && cg_Plus >= num_atoms ) { - -#if ( FIX_N_MINUS_NORN_BUG == 1 ) - /* neutralize: remove ion pairs like >N(+)=-O(-) => >N-=O; >N(+)=-NH(-) => >N-=NH */ - if ( (nNumRemovedProtons || bCancelChargesAlways) && cg_Minus >= num_atoms && cg_Plus >= num_atoms && - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] > abs(pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE]) ) { - do { - nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; -#if ( FIX_REM_PROTON_COUNT_BUG == 1 ) - nPrevRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons; -#endif - ret = bExistsAltPath( pBNS, pBD, pAATG, at, num_atoms, - cg_Minus /*nVertDoubleBond*/, cg_Plus /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON ); - if ( IS_BNS_ERROR( ret ) ) { - return ret; - } -#if ( FIX_REM_PROTON_COUNT_BUG == 1 ) - nCurrRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons; - if ( nCurrRemovedProtons != nPrevRemovedProtons ) { - return BNS_RADICAL_ERR; - } -#endif - if ( ret & 1 ) { - nDelta = (ret & ~3) >> 2; - nNumChanges += (0 != (ret & 2)); - if ( nDelta ) { - /* radical pair has disappeared */ - ; /* goto quick_exit;*/ - } - if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] ) { - nNumNeutralized += (nPrevNumCharges - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES])/2; - } - } - } while ( ret & 1 ); - } -#endif - /* find alt path to remove one proton */ - do { - /* remove a proton */ - nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; -#if ( FIX_REM_PROTON_COUNT_BUG == 1 ) - nPrevRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons; -#endif - ret = bExistsAltPath( pBNS, pBD, pAATG, at, num_atoms, - tg_H /*nVertDoubleBond*/, cg_Plus /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON ); - if ( IS_BNS_ERROR( ret ) ) { - return ret; - } -#if ( FIX_REM_PROTON_COUNT_BUG == 1 ) - nCurrRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons; - if ( nCurrRemovedProtons != nPrevRemovedProtons + (ret & 1) ) { - return BNS_RADICAL_ERR; - } -#endif - if ( ret & 1 ) { - nDelta = (ret & ~3) >> 2; - nNumChanges += (0 != (ret & 2)); - if ( nDelta ) { - /* radical pair has disappeared */ - ; /* goto quick_exit;*/ - } - nNumRemovedProtons ++; - if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + 1 ) { - nNumNeutralized += (nPrevNumCharges - (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - 1))/2; - } - } - - } while ( ret & 1 ); - - /* neutralize: remove ion pairs like >N(+)=-O(-) => >N-=O */ - if ( (nNumRemovedProtons || bCancelChargesAlways) && cg_Minus >= num_atoms && cg_Plus >= num_atoms && - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] > abs(pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE]) ) { - do { - nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; -#if ( FIX_REM_PROTON_COUNT_BUG == 1 ) - nPrevRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons; -#endif - ret = bExistsAltPath( pBNS, pBD, pAATG, at, num_atoms, - cg_Minus /*nVertDoubleBond*/, cg_Plus /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON ); - if ( IS_BNS_ERROR( ret ) ) { - return ret; - } -#if ( FIX_REM_PROTON_COUNT_BUG == 1 ) - nCurrRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons; - if ( nCurrRemovedProtons != nPrevRemovedProtons ) { - return BNS_RADICAL_ERR; - } -#endif - if ( ret & 1 ) { - nDelta = (ret & ~3) >> 2; - nNumChanges += (0 != (ret & 2)); - if ( nDelta ) { - /* radical pair has disappeared */ - ; /* goto quick_exit;*/ - } - if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] ) { - nNumNeutralized += (nPrevNumCharges - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES])/2; - } - } - } while ( ret & 1 ); - } - } - ret = 0; - if ( tg_H >= num_atoms ) { - ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, tg_H, pBNS ); - if ( !ret && ret2 ) - ret = ret2; - } - if ( cg_Minus >= num_atoms ) { - ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Minus, pBNS ); - if ( !ret && ret2 ) - ret = ret2; - } -#if ( MOVE_PPLUS_TO_REMOVE_PROTONS == 1 ) - if ( cg_PlusP >= num_atoms ) { - ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_PlusP, pBNS ); - if ( !ret && ret2 ) - ret = ret2; - } -#endif - if ( cg_Plus >= num_atoms ) { - ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Plus, pBNS ); - if ( !ret && ret2 ) - ret = ret2; - } - - pBNS->type_CN = 0; - pBNS->type_T = 0; - pBNS->type_TACN = 0; - - if ( ret ) { - return ret; - } - if ( pAATG->nAtTypeTotals[ATTOT_NUM_CO_Minus] + pAATG->nAtTypeTotals[ATTOT_NUM_ZO_Minus] && - pAATG->nAtTypeTotals[ATTOT_NUM_N_Minus] ) { - } - - nPosCharges2 = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; - nNegCharges2 = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; - /* - nNumNP_H2 = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] + - pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton]; - nNumOS_H2 = pAATG->nAtTypeTotals[ATTOT_NUM_COH] + - pAATG->nAtTypeTotals[ATTOT_NUM_CSH] + - pAATG->nAtTypeTotals[ATTOT_NUM_ZOH]; - */ - if ( (nPosCharges - nNegCharges) - (nPosCharges2 - nNegCharges2) != nNumRemovedProtons ) { - return BNS_PROGRAM_ERR; - } - - if ( nNumCanceledCharges ) { -#if ( FIX_CANCEL_CHARGE_COUNT_BUG == 1 ) - *nNumCanceledCharges += 2*nNumNeutralized; -#else - *nNumCanceledCharges = 2*nNumNeutralized; -#endif - } - - return nNumRemovedProtons; -} - - - -/***************************************************************************************/ -int mark_at_type( inp_ATOM *atom, int num_atoms, int nAtTypeTotals[] ) -{ - int i, max_num_ions, mask, type; - /*int max_protons, max_O_Minus, num_H = 0, num_CO=0;*/ - if ( nAtTypeTotals ) { - memset( nAtTypeTotals, 0, ATTOT_ARRAY_LEN * sizeof(nAtTypeTotals[0]) ); - } - for ( i = 0; i < num_atoms; i++ ) { - type = GetAtomChargeType( atom, i, nAtTypeTotals, &mask, 0 ); - atom[i].at_type = type; - /* - num_H += ((type & PR_HARD_TYP_H) && (mask & ATBIT_MSK_H)); - num_CO += ((type & AR_HARD_TYP_HA) && (mask & AR_HARD_MSK_HA)); - */ - } - if ( nAtTypeTotals ) { - /* - max_protons = nAtTypeTotals[ATTOT_NUM_NP_Proton] + - inchi_min(num_H, nAtTypeTotals[ATTOT_NUM_NP_Plus]); - max_O_Minus = nAtTypeTotals[ATTOT_NUM_CO_Minus] + nAtTypeTotals[ATTOT_NUM_CS_Minus] + - nAtTypeTotals[ATTOT_NUM_ZO_Minus] + nAtTypeTotals[ATTOT_NUM_OO_Minus] + - nAtTypeTotals[ATTOT_NUM_ZOO_Minus] + nAtTypeTotals[ATTOT_NUM_NO_Minus] + - nAtTypeTotals[ATTOT_NUM_O_Minus] +nAtTypeTotals[ATTOT_NUM_N_Minus]; - ; - max_num_ions = max_protons + max_O_Minus + nAtTypeTotals[ATTOT_NUM_CHARGES]; - */ - max_num_ions = nAtTypeTotals[ATTOT_NUM_CHARGES]; - } else { - max_num_ions = 0; - } - return max_num_ions; -} - - - -/***************************************************************************************/ -int RemoveNPProtonsAndAcidCharges( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, BN_STRUCT *pBNS, BN_DATA *pBD ) -{ - - /* prepare data structure */ - int num; - int nNumCanceledCharges = 0; - int nNumHardRemovedProtons = 0; - int nNumHardRemovedAcidicProtons = 0; - T_GROUP_INFO *t_group_info = pAATG->t_group_info; - int ret=0, bError = 0; - int bAllowHardRemove = (t_group_info->bTautFlags & TG_FLAG_TEST_TAUT__SALTS) && - (t_group_info->bTautFlags & TG_FLAG_TEST_TAUT2_SALTS) && - (t_group_info->bTautFlags & TG_FLAG_MOVE_POS_CHARGES ) && - (t_group_info->bTautFlags & TG_FLAG_HARD_ADD_REM_PROTONS); - if ( pAATG->nMarkedAtom && num_atoms < pAATG->nAllocLen ) - { - inchi_free( pAATG->nMarkedAtom ); - qzfree( pAATG->nEndPoint ); - memset( pAATG, 0, sizeof(*pAATG) ); - } - if ( !pAATG->nMarkedAtom && (pAATG->nMarkedAtom = (S_CHAR *) inchi_malloc( num_atoms * sizeof(pAATG->nMarkedAtom[0]))) ) - { - pAATG->nAllocLen = num_atoms; - pAATG->nNumFound = 0; - } - - /* o TECHMAN-5.1. Remove protons from charged heteroatoms */ - - /* (TECHMAN-5.1a) Simple remove of protons from N, P, and O,S,Se,Te */ - if ( num = pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton] + pAATG->nAtTypeTotals[ATTOT_NUM_OH_Plus] ) - { - ret = SimpleRemoveHplusNPO(at, num_atoms, pAATG->nAtTypeTotals, t_group_info); - if ( ret != num ) - { - bError = BNS_PROGRAM_ERR; - goto exit_function; - } - /*t_group_info->nNumRemovedProtons += ret;*/ - t_group_info->tni.bNormalizationFlags |= (ret > 0)? FLAG_PROTON_NPO_SIMPLE_REMOVED : 0; - } - - if ( (num = pAATG->nAtTypeTotals[ATTOT_NUM_NP_Plus]) && bAllowHardRemove ) - { - /* [TECHMAN-5.1b] Hard removing more protons from cationic N; charges may be canceled */ - ret = HardRemoveHplusNP(at, num_atoms, 1, &nNumCanceledCharges, pAATG, pBNS, pBD); - if ( IS_BNS_ERROR( ret ) ) - { - bError = ret; - goto exit_function; - } - nNumHardRemovedProtons += ret; - /*t_group_info->nNumRemovedProtons += ret;*/ - t_group_info->tni.bNormalizationFlags |= (ret > 0)? FLAG_PROTON_NP_HARD_REMOVED : 0; - } - - - if ( pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] > 0 ) - { - /* o TECHMAN-5.2. Remove protons from neutral heteroatoms */ - - /* (TECHMAN-5.2a) Simple removal */ - ret = SimpleRemoveAcidicProtons( at, num_atoms, pAATG, pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] ); - if ( IS_BNS_ERROR( ret ) ) - { - bError = ret; - goto exit_function; - } - /*t_group_info->nNumRemovedProtons += ret;*/ - t_group_info->tni.bNormalizationFlags |= (ret > 0)? FLAG_PROTON_AC_SIMPLE_REMOVED : 0; - if ( pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] > 0 && bAllowHardRemove ) - { - /* (TECHMAN-5.2b) Hard removal */ - ret = HardRemoveAcidicProtons( at, num_atoms, pAATG, pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE], &nNumCanceledCharges, pBNS, pBD ); - if ( IS_BNS_ERROR( ret ) ) - { - bError = ret; - goto exit_function; - } - if ( ret > 0 ) - { - int ret2 = SimpleRemoveAcidicProtons( at, num_atoms, pAATG, ret ); - if ( ret2 != ret ) - { - bError = BNS_PROGRAM_ERR; - goto exit_function; - } - /*t_group_info->nNumRemovedProtons += ret;*/ - t_group_info->tni.bNormalizationFlags |= (ret > 0)? FLAG_PROTON_AC_HARD_REMOVED : 0; - nNumHardRemovedAcidicProtons += ret; - } - } - } - else - if ( pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] < 0 ) - { - ret = SimpleAddAcidicProtons( at, num_atoms, pAATG, -pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] ); - if ( IS_BNS_ERROR( ret ) ) - { - bError = ret; - goto exit_function; - } - /*t_group_info->nNumRemovedProtons -= ret;*/ - /* - CHECK_TACN == 1 prohibits replacing (-) on N with H unless H can be moved to N - along an alternating path from another heteroatom (t-group will be detected). - */ - t_group_info->tni.bNormalizationFlags |= (ret > 0)? FLAG_PROTON_AC_SIMPLE_ADDED : 0; - if ( pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] < 0 && bAllowHardRemove ) { - ret = HardAddAcidicProtons( at, num_atoms, pAATG, -pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE], &nNumCanceledCharges, pBNS, pBD ); - if ( IS_BNS_ERROR( ret ) ) - { - bError = ret; - goto exit_function; - } - if ( ret > 0 ) - { - int ret2 = SimpleAddAcidicProtons( at, num_atoms, pAATG, ret ); - if ( ret2 != ret ) - { - bError = BNS_PROGRAM_ERR; - goto exit_function; - } - /*t_group_info->nNumRemovedProtons -= ret;*/ - t_group_info->tni.bNormalizationFlags |= (ret > 0)? FLAG_PROTON_AC_HARD_ADDED : 0; - nNumHardRemovedAcidicProtons -= ret; - } - } - } - t_group_info->tni.bNormalizationFlags |= nNumCanceledCharges? FLAG_PROTON_CHARGE_CANCEL : 0; - -exit_function: - if ( bError ) - { - ret = IS_BNS_ERROR(bError)? bError : BNS_PROGRAM_ERR; - } - return ret; -} - - - -/********************************/ -/* */ -/* Main normalization procedure */ -/* */ -/********************************/ -int mark_alt_bonds_and_taut_groups ( inp_ATOM *at, inp_ATOM *at_fixed_bonds_out, int num_atoms, - T_GROUP_INFO *t_group_info, INCHI_MODE *inpbTautFlags, INCHI_MODE *inpbTautFlagsDone ) - - -{ - BN_STRUCT *pBNS = NULL; - BN_DATA *pBD = NULL; - int bError, nChanges, nTotChanges, taut_found, salt_found, taut_pass, salt_pass, salt_step, ret, ret2, num; - int nOrigDelta, num_changed_bonds; - int max_altp = BN_MAX_ALTP; - int bChangeFlow = (BNS_EF_CHNG_RSTR | BNS_EF_ALTR_BONDS); - BNS_FLOW_CHANGES fcd[BNS_MAX_NUM_FLOW_CHANGES+1]; - C_GROUP_INFO CGroupInfo; - C_GROUP_INFO *c_group_info = &CGroupInfo; - S_GROUP_INFO SGroupInfo; - S_GROUP_INFO *s_group_info = &SGroupInfo; - INCHI_MODE *pbTautFlags = t_group_info? &t_group_info->bTautFlags : inpbTautFlags; - INCHI_MODE *pbTautFlagsDone = t_group_info? &t_group_info->bTautFlagsDone : inpbTautFlagsDone; - - int nAtTypeTotals[ATTOT_ARRAY_LEN]; - int nNumOrigTotAtoms; - - BN_AATG aatg; - BN_AATG *pAATG = &aatg; - -#ifdef FIX_AROM_RADICAL /* Added 2011-05-09 IPl */ - int i, n_arom_radicals=0, *stored_radicals=NULL; -#endif - - nChanges = 0; - bError = 0; - - memset( c_group_info, 0, sizeof(*c_group_info) ); - memset( s_group_info, 0, sizeof(*s_group_info) ); - memset( pAATG, 0, sizeof(*pAATG) ); - - -#ifdef FIX_AROM_RADICAL /* Added 2011-05-09 IPl */ - for ( i = 0; i < num_atoms; i ++ ) - { - if ( (at[i].radical==RADICAL_DOUBLET) && (at[i].valence==2) && - (at[i].bond_type[0]==BOND_ALTERN) && (at[i].bond_type[1]==BOND_ALTERN) ) - { - n_arom_radicals++; - if ( !stored_radicals ) - { - stored_radicals = (int *) inchi_calloc(num_atoms, sizeof(int)); - /* 2011-08-05 explicit cast added due to Evan Bolton */ - if ( !stored_radicals ) - { - bError = BNS_OUT_OF_RAM; - goto exit_function; - } - stored_radicals[i] = RADICAL_DOUBLET; - at[i].radical = 0; - at[i].num_H++; - } - } - } - -#endif - - - if ( (*pbTautFlags & TG_FLAG_MOVE_POS_CHARGES) && num_atoms > 1 ) { - /* charge groups memory allocation */ - c_group_info->c_group = (C_GROUP *)inchi_calloc(num_atoms/2, sizeof(c_group_info->c_group[0])); - c_group_info->c_candidate = (C_CANDIDATE*)inchi_calloc(num_atoms, sizeof(c_group_info->c_candidate[0])); - if (c_group_info->c_group && c_group_info->c_candidate) { - c_group_info->max_num_c_groups = num_atoms/2; - c_group_info->max_num_candidates = num_atoms; - } else { - bError = BNS_OUT_OF_RAM; /* error: out of RAM */ - /*printf("BNS_OUT_OF_RAM-1: num_at=%d, c_gr=%lx c_can=%lx\n", num_atoms, c_group_info->c_group, c_group_info->c_candidate);*/ - goto exit_function; - } - } - - if ( *pbTautFlags & TG_FLAG_TEST_TAUT__SALTS ) { - if ( t_group_info ) { - /* salt groups memory allocation */ - s_group_info->s_candidate = (S_CANDIDATE*)inchi_calloc(num_atoms, sizeof(s_group_info->s_candidate[0])); - if (s_group_info->s_candidate) { - s_group_info->max_num_candidates = num_atoms; - } else { - bError = BNS_OUT_OF_RAM; /* error: out of RAM */ - /*printf("BNS_OUT_OF_RAM-2\n");*/ - goto exit_function; - } - } - } - if ( t_group_info ) { - if ( t_group_info->tGroupNumber ) - inchi_free( t_group_info->tGroupNumber ); - t_group_info->tGroupNumber = (AT_NUMB *)inchi_calloc( 2*num_atoms+1, sizeof(t_group_info->tGroupNumber[0]) ); - if ( !t_group_info->tGroupNumber ) { - /*printf("BNS_OUT_OF_RAM-9\n");*/ - bError = BNS_OUT_OF_RAM; /* error: out of RAM */ - goto exit_function; - } - num = t_group_info->tni.nNumRemovedExplicitH; - memset ( &t_group_info->tni, 0, sizeof(t_group_info->tni) ); - t_group_info->tni.nNumRemovedExplicitH = num; - } - - - -/* -again: -*/ - /* allocate Balanced Network Data Strucures; replace Alternating bonds with Single */ - if ( (pBNS = AllocateAndInitBnStruct( at, num_atoms, BNS_ADD_ATOMS, BNS_ADD_EDGES, max_altp, &num_changed_bonds )) && - (pBD = AllocateAndInitBnData( pBNS->max_vertices )) ) - { - - - pBNS->pbTautFlags = pbTautFlags; /* carry through all functions */ - pBNS->pbTautFlagsDone = pbTautFlagsDone; /* carry through all functions */ - -#if ( BNS_PROTECT_FROM_TAUT == 1 ) - /* protect bonds to acetyl and nitro */ - SetForbiddenEdges( pBNS, at, num_atoms, BNS_EDGE_FORBIDDEN_MASK ); -#endif - - - /* set bonds in case of input "aromatic" bonds or multiple radicals */ - -#ifdef FIX_AROM_RADICAL /* Added 2011-05-09 IPl */ - if ( n_arom_radicals ) - { - ret = BnsAdjustFlowBondsRad( pBNS, pBD, at, num_atoms ); - if ( stored_radicals ) - { - for ( i = 0; i < num_atoms; i ++ ) - { - if ( stored_radicals[i] ) - { - at[i].radical = stored_radicals[i]; - at[i].num_H--; - } - } - } - if ( IS_BNS_ERROR( ret ) ) - { - bError = ret; - goto exit_function; - } - } -#endif - ret = BnsAdjustFlowBondsRad( pBNS, pBD, at, num_atoms ); - - - /* (here pair(s) of radicals could have disappeared from the atoms) */ - if ( IS_BNS_ERROR( ret ) ) - { - bError = ret; - goto exit_function; - } - pBNS->tot_st_flow += 2*ret; - - - - /*return 0;*/ /* debug */ - nOrigDelta = ret; - if ( pBNS->tot_st_cap > pBNS->tot_st_flow ) { - /* has radical */ - bChangeFlow |= BNS_EF_SET_NOSTEREO; - } - /******************************************************************** - * Remove protons from NH(+), but not PH(+) - * Add protons to COO(-) etc. - * or remove protons from COOH etc to make the organic part neutral - * Note: for now (-) from N(-) can be only canceled or moved to -C=O - ********************************************************************/ - if ( ( *pbTautFlags & TG_FLAG_VARIABLE_PROTONS ) && t_group_info && - mark_at_type( at, num_atoms, nAtTypeTotals ) && - nAtTypeTotals[ATTOT_NUM_CHARGES] ) { - /* - the structure is simple to neutralize if it yields exactly - num[H(+)] = num[N,P H(+)] - num[N,S,O(-)] = num[=C-O(-)] + num[C-S(-)] + num[N(-)] + num[other O(-), S(-)] - - and n(p) = num[H(+)] - num[N,S,O(-)] (no protons, no negative N,O,S condition) - - Additional check is needed if: - min{num[N,PH], num[N,P(+), not onium]} > 0 - => possibility to yield more H(+) - - min_charge = orig_charge(P,N,O,S) - n(p) - n(OH,SH) - max_charge = orig_charge(P,N,O,S) - n(p) + n(O,S,N(-)) - */ - - - nNumOrigTotAtoms = t_group_info->tni.nNumRemovedExplicitH + num_atoms; - pAATG->nAtTypeTotals = nAtTypeTotals; - pAATG->t_group_info = t_group_info; -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) - pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP; -#endif - /***********************************************************/ - /* */ - /* ( D E ) P R O T O N A T I O N */ - /* */ - /***********************************************************/ - ret = RemoveNPProtonsAndAcidCharges( at, num_atoms, pAATG, pBNS, pBD ); -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) - pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP; -#endif - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - goto exit_function; - } - if ( t_group_info->tni.bNormalizationFlags ) { - SetInitCapFlowToCurrent( pBNS ); - if ( at_fixed_bonds_out ) { - /* copy modified initial tautomeric structure for displaying - Warning: implicit H counts in at_fixed_bonds_out include explicit Hs */ - memcpy( at_fixed_bonds_out, at, nNumOrigTotAtoms*sizeof(at_fixed_bonds_out[0]) ); - /* -- will be done in FillOutInputInfAtom() -- - RemoveExcessiveImplicitH( num_atoms, t_group_info->tni.nNumRemovedExplicitH, at_fixed_bonds_out ); - */ - } - } - } - /****************** initial bonds normalization ***************/ - if ( *pbTautFlags & TG_FLAG_MOVE_POS_CHARGES ) { - /******************* find moveable positive charges **********************/ - do { /* cycling while ret>0 added 2004-06-04 */ -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) - pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP; - CorrectFixing_NH_NH_Bonds( pBNS, at, num_atoms ); -#endif - ret = MarkChargeGroups ( at, num_atoms, c_group_info, t_group_info, pBNS, pBD ); -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) - pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP; -#endif - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - goto exit_function; - } - if ( ret ) { - nChanges += ret; - ret2 = AddCGroups2BnStruct( pBNS, at, num_atoms, c_group_info ); - if ( IS_BNS_ERROR( ret2 ) ) { - bError = ret2; - goto exit_function; - } - *pbTautFlagsDone |= TG_FLAG_MOVE_POS_CHARGES_DONE; - } - } while ( ret > 0 ); -#if ( BNS_RAD_SEARCH == 1 ) -#else - /* moveable charges may allow to cancel radicals -- check it */ - if ( pBNS->tot_st_cap > pBNS->tot_st_flow ) { - ret = BnsAdjustFlowBondsRad( pBNS, pBD, at, num_atoms ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - goto exit_function; - } - if ( ret > 0 ) { - /* - pBNS->tot_st_flow += 2*ret; - ret = ReInitBnStruct( pBNS, at, num_atoms, 1 ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - goto exit_function; - } - */ - bError = BNS_RADICAL_ERR; - goto exit_function; - } - } -#endif - } - /************************************************************************/ - /******** test bonds for bond tautomerism **************/ - /******** replace moveable bonds with "alternating" bonds **************/ - /************************************************************************/ - ret = BnsTestAndMarkAltBonds( pBNS, pBD, at, num_atoms, fcd, bChangeFlow, 0 ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - goto exit_function; - } - nChanges += ret; - /*********************** end of initial bonds normalization *************/ - nTotChanges = 0; - /* check for tautomerism */ - /* find new tautomer groups */ - salt_pass = 0; - salt_step = 0; - salt_found = 0; - - /*************************************************************/ - /* */ - /* M A I N C Y C L E B E G I N */ - /* */ - /*************************************************************/ - - do { - nTotChanges += nChanges; - nChanges = 0; - taut_pass = 0; - - /**************** regular bond/H/(-)/positive charges tautomerism cycle begin **************/ - do { - taut_pass ++; - for ( taut_found = 0; 0 < (ret=MarkTautomerGroups( at, num_atoms, t_group_info, c_group_info, pBNS, pBD )); taut_found ++ ) - ; - if ( ret < 0 ) { - bError = ret; - } - if ( taut_found && !salt_pass ) { - *pbTautFlagsDone |= TG_FLAG_TEST_TAUT__ATOMS_DONE; - } - if ( taut_found || salt_found ) { - /****************** repeat bonds normalization ***************/ - ret = ReInitBnStructAddGroups( pBNS, at, num_atoms, t_group_info, c_group_info ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - goto exit_function; - } -#if ( BNS_RAD_SEARCH == 1 ) -#else - /* discovered moveable charges and H-atoms may allow to cancel radicals */ - if ( pBNS->tot_st_cap > pBNS->tot_st_flow ) { - ret = BnsAdjustFlowBondsRad( pBNS, pBD, at, num_atoms ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - goto exit_function; - } - if ( ret > 0 ) { - /* - pBNS->tot_st_flow += 2*ret; - ret = ReInitBnStruct( pBNS, at, num_atoms, 1 ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - goto exit_function; - } - */ - bError = BNS_RADICAL_ERR; - goto exit_function; - } - } -#endif - /****************** update bonds normalization ***************/ - if ( *pbTautFlags & TG_FLAG_MOVE_POS_CHARGES ) { - /******************* find moveable charges ***************/ - do { /* cycling while ret>0 added 2004-06-04 */ -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) - pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP; - CorrectFixing_NH_NH_Bonds( pBNS, at, num_atoms ); -#endif - ret = MarkChargeGroups ( at, num_atoms, c_group_info, t_group_info, pBNS, pBD ); -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) - pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP; -#endif - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - goto exit_function; - } - nChanges+= ret; - if ( ret > 0 ) { - ret2 = ReInitBnStructAddGroups( pBNS, at, num_atoms, t_group_info, c_group_info ); - if ( IS_BNS_ERROR( ret2 ) ) { - bError = ret2; - goto exit_function; - } - *pbTautFlagsDone |= TG_FLAG_MOVE_POS_CHARGES_DONE; - } - } while ( ret > 0 ); - } - - /************************************************************************/ - /******** find moveable bonds: **************/ - /******** test bonds for bond tautomerism **************/ - /******** replace moveable bonds with "alternating" bonds **************/ - /************************************************************************/ - ret = BnsTestAndMarkAltBonds( pBNS, pBD, at, num_atoms, fcd, bChangeFlow, 0 ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - goto exit_function; - } - nChanges+= ret; - /****************** end of update bonds normalization ***************/ - } - salt_found = 0; - - } while( taut_found && !bError ); - /**************** regular bond/H/(-)/positive charges tautomerism cycle end **************/ - - if ( bError ) { - break; - } - - /******************* 'salt' tautomerism permitted *************************/ - if ( *pbTautFlags & TG_FLAG_TEST_TAUT__SALTS ) { - - if ( *pbTautFlags & TG_FLAG_TEST_TAUT2_SALTS ) { - /*********** requested one or more "salt" attachement migrartion test ********/ - if ( !nChanges && salt_pass && salt_step ) { - break; /* done */ - } - if ( !salt_step ) { /* salt step 0: process one attachment migrartion */ -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) - pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP; - CorrectFixing_NH_NH_Bonds( pBNS, at, num_atoms ); -#endif - salt_found = MarkSaltChargeGroups ( at, num_atoms, s_group_info, - t_group_info, c_group_info, pBNS, pBD ); -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) - pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP; -#endif - if ( salt_found < 0 ) { - bError = salt_found; - break; - } else - if ( salt_found > 0 ) { - *pbTautFlagsDone |= TG_FLAG_TEST_TAUT__SALTS_DONE; - } - salt_step = !salt_found; - /* if new 'salt' atoms have been found then repeat regular taut. search - * MarkTautomerGroups() and do not perform salt step 1 - * if new 'salt' atoms have NOT been found then switch to salt step 1 - * and never repeat salt step 0 for the current structure - */ - } - if ( salt_step /*|| - (t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT)*/ ) { - /* salt step 1: process more than one attachment migration */ -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) - pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP; - CorrectFixing_NH_NH_Bonds( pBNS, at, num_atoms ); -#endif - salt_found = MarkSaltChargeGroups2 ( at, num_atoms, s_group_info, - t_group_info, c_group_info, pBNS, pBD ); -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) - pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP; -#endif - if ( salt_found < 0 ) { - bError = salt_found; - break; - } else - if ( salt_found == 1 || salt_found == 5 ) { - *pbTautFlagsDone |= TG_FLAG_TEST_TAUT2_SALTS_DONE; - if ( salt_found == 5 ) { - *pbTautFlagsDone |= TG_FLAG_TEST_TAUT3_SALTS_DONE; - } - /* salt_found == 2 => only negative charges involved */ - } - } - - salt_pass ++; - - } else { /* !( *pbTautFlags & TG_FLAG_TEST_TAUT2_SALTS ) */ - /*************** requested only one attachement migration test **********/ - if ( !nChanges && salt_pass ) { /* one attachment migration */ - break; - } /* salt step 0: process one attachment migration */ -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) - pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP; - CorrectFixing_NH_NH_Bonds( pBNS, at, num_atoms ); -#endif - salt_found = MarkSaltChargeGroups ( at, num_atoms, s_group_info, - t_group_info, c_group_info, pBNS, pBD ); -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) - pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP; -#endif - if ( salt_found < 0 ) { - bError = salt_found; - break; - } else - if ( salt_found > 0 ) { - *pbTautFlagsDone |= TG_FLAG_TEST_TAUT__SALTS_DONE; - } - salt_pass ++; - } /* ( *pbTautFlags & TG_FLAG_TEST_TAUT2_SALTS ) */ - } /* ( *pbTautFlags & TG_FLAG_TEST_TAUT__SALTS ) */ - - } while ( salt_found && !bError ); - /*************************************************************/ - /* */ - /* M A I N C Y C L E E N D */ - /* */ - /*************************************************************/ - - if ( *pbTautFlags & TG_FLAG_MERGE_TAUT_SALTS ) { - if ( !bError && s_group_info /*&& s_group_info->num_candidates > 0*/ ) { - ret = MergeSaltTautGroups( at, num_atoms, s_group_info, - t_group_info, c_group_info, pBNS ); - if ( ret < 0 ) { - bError = ret; - } else - if ( ret > 0 ) { - *pbTautFlagsDone |= TG_FLAG_MERGE_TAUT_SALTS_DONE; - } - } - } - if ( !bError && t_group_info && - (t_group_info->bTautFlags & TG_FLAG_VARIABLE_PROTONS) && - (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE|TG_FLAG_FOUND_ISOTOPIC_H_DONE)) ) { - ret = MakeIsotopicHGroup( at, num_atoms, s_group_info, t_group_info ); - if ( ret < 0 ) { - bError = ret; - } - } - /* success */ - remove_alt_bond_marks( at, num_atoms); - - /************************************************ - * Temporarily ignore all non-alternating bonds - * and mark non-ring alt bonds non-stereogenic - ************************************************/ - - ReInitBnStructForAltBns( pBNS, at, num_atoms, 0 ); - MarkRingSystemsAltBns( pBNS, 0 ); - MarkNonStereoAltBns( pBNS, at, num_atoms, 0 ); -#if ( FIX_EITHER_DB_AS_NONSTEREO == 1 ) - /* second time unknown ("Either") alternating bonds are treated as non-stereogenic */ - /* stereobonds bonds that lost stereo get "Either" stereo_type */ - ReInitBnStructForAltBns( pBNS, at, num_atoms, 1 ); - MarkRingSystemsAltBns( pBNS, 1 ); - MarkNonStereoAltBns( pBNS, at, num_atoms, 1 ); -#endif - } else { - bError = BNS_OUT_OF_RAM; - /*printf("BNS_OUT_OF_RAM-3\n");*/ - } - -exit_function: - - pBNS = DeAllocateBnStruct( pBNS ); - pBD = DeAllocateBnData( pBD ); -/*#if ( MOVE_CHARGES == 1 )*/ - if ( c_group_info ) { - if ( c_group_info->c_group ) { - inchi_free( c_group_info->c_group ); - } - if ( c_group_info->c_candidate ) { - inchi_free( c_group_info->c_candidate ); - } - } -/*#endif*/ - if ( s_group_info && s_group_info->s_candidate ) { - inchi_free( s_group_info->s_candidate ); - } - if ( pAATG && pAATG->nMarkedAtom ) { - inchi_free( pAATG->nMarkedAtom ); - qzfree( pAATG->nEndPoint ); - /*qzfree( pAATG->nAtTypeTotals );*/ /* nAtTypeTotals is a stack array */ - } - if ( t_group_info && t_group_info->tGroupNumber ) { - inchi_free( t_group_info->tGroupNumber ); - t_group_info->tGroupNumber = NULL; - } - - if ( !bError && num_atoms == 1 && at[0].at_type == ATT_PROTON && t_group_info && !t_group_info->tni.nNumRemovedExplicitH ) { - /* remove single isolated proton */ - t_group_info->tni.nNumRemovedProtons = 1; - t_group_info->tni.bNormalizationFlags |= FLAG_PROTON_SINGLE_REMOVED; - if ( at[0].iso_atw_diff ) { - t_group_info->tni.nNumRemovedProtonsIsotopic[at[0].iso_atw_diff-1] ++; - } - if ( at_fixed_bonds_out ) { - memcpy( at_fixed_bonds_out, at, num_atoms*sizeof(at_fixed_bonds_out[0]) ); - } - /*num_atoms --;*/ - } - - - /* - Additional currently unused info: - - nOrigDelta > 0: original structure has been changed - due to fiund augmenting path(s) - nChanges > 0: either alt. bonds or taut. groups have been found - */ - -#ifdef FIX_AROM_RADICAL /* Added 2011-05-09 IPl */ - if ( stored_radicals ) - inchi_free( stored_radicals ); -#endif - - - return bError? bError : num_atoms; /* ret = 0 => success, any other => error */ - -} - - - -/*********************************************************************************/ -int nMaxFlow2Check( BN_STRUCT *pBNS, int iedge ) -{ - BNS_EDGE *pEdge = pBNS->edge + iedge; - int nMaxFlow = (pEdge->cap & EDGE_FLOW_MASK); /* edge cap */ - - if ( nMaxFlow > MAX_BOND_EDGE_CAP ) { - nMaxFlow = MAX_BOND_EDGE_CAP; - } - return nMaxFlow; -} - - - -/*********************************************************************************/ -int nCurFlow2Check( BN_STRUCT *pBNS, int iedge ) -{ - BNS_EDGE *pEdge = pBNS->edge + iedge; - int nCurFlow = (pEdge->flow & EDGE_FLOW_MASK); /* edge flow */ - return nCurFlow; -} - - - -/*********************************************************************************/ -int nMinFlow2Check( BN_STRUCT *pBNS, int iedge ) -{ - BNS_EDGE *pEdge = pBNS->edge + iedge; - Vertex v1 = pEdge->neighbor1; - Vertex v2 = v1 ^ pEdge->neighbor12; - int f12 = (pEdge->flow & EDGE_FLOW_MASK); - int rescap1, rescap2, rescap12, i, iedge_i; - - if ( f12 > 0 ) { - for ( i = 0, rescap1 = 0; i < pBNS->vert[v1].num_adj_edges; i ++ ) { - iedge_i = pBNS->vert[v1].iedge[i]; - if ( iedge_i == iedge ) - continue; - rescap1 += (pBNS->edge[iedge_i].cap & EDGE_FLOW_MASK) - (pBNS->edge[iedge_i].flow & EDGE_FLOW_MASK); - } - for ( i = 0, rescap2 = 0; i < pBNS->vert[v2].num_adj_edges; i ++ ) { - iedge_i = pBNS->vert[v2].iedge[i]; - if ( iedge_i == iedge ) - continue; - rescap2 += (pBNS->edge[iedge_i].cap & EDGE_FLOW_MASK) - (pBNS->edge[iedge_i].flow & EDGE_FLOW_MASK); - } - rescap12 = inchi_min( rescap1, rescap2 ); - rescap12 = inchi_min( rescap12, f12 ); - return f12-rescap12; - } - return 0; -} - - - -/**********************************************************************************/ -int bSetBondsAfterCheckOneBond( BN_STRUCT *pBNS, BNS_FLOW_CHANGES *fcd, int nTestFlow, inp_ATOM *at, int num_atoms, int bChangeFlow0 ) -{ - int ifcd, iedge, new_flow, ret_val, nChanges = 0, bError=0; - int bChangeFlow; - Vertex v1, v2; - int ineigh1, ineigh2; - BNS_EDGE *pEdge; - - bChangeFlow0 &= ~BNS_EF_CHNG_RSTR; /* do not change pEdge flow in SetBondType */ - if ( !bChangeFlow0 ) - return 0; - - bChangeFlow = (bChangeFlow0 & ~BNS_EF_SET_NOSTEREO); - /* find the next to the last changed */ - if ( bChangeFlow0 & BNS_EF_SET_NOSTEREO ) { - - for ( ifcd = 0; NO_VERTEX != (iedge = fcd[ifcd].iedge); ifcd ++ ) { - iedge = fcd[ifcd].iedge; - pEdge = pBNS->edge + iedge; - if ( !pEdge->pass ) { - continue; - } - - if ( !ifcd && nTestFlow>=0 ) { - new_flow = nTestFlow; - } else { - new_flow = (int)pEdge->flow; - } - - v1 = pEdge->neighbor1; - v2 = pEdge->neighbor12 ^ v1; - if ( v1 < num_atoms && v2 < num_atoms && new_flow != pEdge->flow0 ) { - if ( (pBNS->vert[v1].st_edge.cap0 == pBNS->vert[v1].st_edge.flow0) != - (pBNS->vert[v1].st_edge.cap == pBNS->vert[v1].st_edge.flow ) || - (pBNS->vert[v2].st_edge.cap0 == pBNS->vert[v2].st_edge.flow0) != - (pBNS->vert[v2].st_edge.cap == pBNS->vert[v2].st_edge.flow )) { - bChangeFlow |= BNS_EF_SET_NOSTEREO; - nChanges |= BNS_EF_SET_NOSTEREO; - } - } - } - - } else { - for ( ifcd = 0; NO_VERTEX != (iedge = fcd[ifcd].iedge); ifcd ++ ) - ; - } - - /* restore in reversed order to correctly handle vertex changed more than once */ - for ( ifcd -= 1; 0 <= ifcd; ifcd -- ) { - - iedge = fcd[ifcd].iedge; - pEdge = pBNS->edge + iedge; - if ( !pEdge->pass ) { - continue; - } - - if ( !ifcd && nTestFlow>=0 ) { - new_flow = nTestFlow; - } else { - new_flow = (int)pEdge->flow; - } - - v1 = pEdge->neighbor1; - v2 = pEdge->neighbor12 ^ v1; - if ( v1 < num_atoms && v2 < num_atoms && bChangeFlow && new_flow != pEdge->flow0 ) { - ineigh1 = pEdge->neigh_ord[0]; - ineigh2 = pEdge->neigh_ord[1]; - ret_val = SetAtomBondType( pEdge, &at[v1].bond_type[ineigh1], &at[v2].bond_type[ineigh2], new_flow-pEdge->flow0, bChangeFlow ); - if ( !IS_BNS_ERROR( ret_val ) ) { - nChanges |= (ret_val > 0); - } else { - bError = ret_val; - } - } - pEdge->pass = 0; - } - return bError? bError : nChanges; -} - - - -/**********************************************************************************/ -int bRestoreFlowAfterCheckOneBond( BN_STRUCT *pBNS, BNS_FLOW_CHANGES *fcd ) -{ - int ifcd, iedge; - Vertex v1, v2; - BNS_EDGE *pEdge; - - /* find the next to the last changed */ - for ( ifcd = 0; NO_VERTEX != (iedge = fcd[ifcd].iedge); ifcd ++ ) - ; - - /* restore in reversed order to correctly handle vertex changed more than once */ - for ( ifcd -= 1; 0 <= ifcd; ifcd -- ) { - - /* restore edge flow & cap */ - iedge = fcd[ifcd].iedge; - pEdge = pBNS->edge + iedge; - pEdge->flow = fcd[ifcd].flow; - pEdge->cap = fcd[ifcd].cap; - pEdge->pass = 0; - - /* restore st-flow, cap */ - if ( NO_VERTEX != (v1 = fcd[ifcd].v1) ) { - pBNS->vert[v1].st_edge.flow = fcd[ifcd].flow_st1; - pBNS->vert[v1].st_edge.cap = fcd[ifcd].cap_st1; - pBNS->vert[v1].st_edge.pass = 0; - } - if ( NO_VERTEX != (v2 = fcd[ifcd].v2) ) { - pBNS->vert[v2].st_edge.flow = fcd[ifcd].flow_st2; - pBNS->vert[v2].st_edge.cap = fcd[ifcd].cap_st2; - pBNS->vert[v2].st_edge.pass = 0; - } - } - return 0; -} - - - -/**********************************************************************************/ -int bSetFlowToCheckOneBond( BN_STRUCT *pBNS, int iedge, int flow, BNS_FLOW_CHANGES *fcd ) -{ - BNS_EDGE *pEdge = pBNS->edge + iedge; - int f12 = (pEdge->flow & EDGE_FLOW_MASK); /* the original flow */ - int ifcd = 0; - int nDots = 0; - int i, iedge_i; - - fcd[ifcd].iedge = NO_VERTEX; - - if ( f12 < flow ) { - /* Increase edge flow: Grab flow from the neighbors and delete it: set flow12=cap12 = 0 */ - /************************************************************************************/ - /* For example, simulate a new fixed double bond in place of a single bond and */ - /* creates ONE or NONE (in case of a radical on adjacent atom) augmenting paths and */ - /* makes it impossible for the BNS to set same flow as it originally was */ - /************************************************************************************/ - Vertex v1 = pEdge->neighbor1; - Vertex v2 = v1 ^ pEdge->neighbor12; - Vertex v_i; /* neighbor of v1 or v2 */ - BNS_EDGE *pEdge_i; - int delta1, delta2, f, st_edge_rescap; - - if ( (pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK) < flow || - (pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK) < flow ) { - return BNS_CANT_SET_BOND; - } - if ( (pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK) < f12 || - (pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK) < f12 ) { - return BNS_CAP_FLOW_ERR; - } - - - fcd[ifcd].iedge = iedge; - fcd[ifcd].flow = pEdge->flow; - fcd[ifcd].cap = pEdge->cap; - - fcd[ifcd].v1 = v1; - fcd[ifcd].flow_st1 = pBNS->vert[v1].st_edge.flow; - fcd[ifcd].cap_st1 = pBNS->vert[v1].st_edge.cap; - - fcd[ifcd].v2 = v2; - fcd[ifcd].flow_st2 = pBNS->vert[v2].st_edge.flow; - fcd[ifcd].cap_st2 = pBNS->vert[v2].st_edge.cap; - - fcd[++ifcd].iedge = NO_VERTEX; /* mark the end of the fcd[] data */ - pEdge->pass |= 64; - - delta1 = delta2 = flow - f12; - - if ( f12 > 0 ) { - /* remove old edge flow from the flow and cap of the adjacent vertices' st-edges */ - pBNS->vert[v1].st_edge.cap = ((pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK)-f12) | (pBNS->vert[v1].st_edge.cap & ~EDGE_FLOW_ST_MASK); - pBNS->vert[v2].st_edge.cap = ((pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK)-f12) | (pBNS->vert[v2].st_edge.cap & ~EDGE_FLOW_ST_MASK); - pBNS->vert[v1].st_edge.flow = ((pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK)-f12) | (pBNS->vert[v1].st_edge.flow & ~EDGE_FLOW_ST_MASK); - pBNS->vert[v2].st_edge.flow = ((pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK)-f12) | (pBNS->vert[v2].st_edge.flow & ~EDGE_FLOW_ST_MASK); - /* delete current edge flow and capacity */ - pEdge->flow = (pEdge->flow & ~EDGE_FLOW_MASK); - } - pEdge->cap = (pEdge->cap & ~EDGE_FLOW_MASK); - - /* grab the adjacent vertex1 radical (st_edge_rescap) if it exists */ - st_edge_rescap = (pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK) - (pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK); - while ( st_edge_rescap && delta1 ) { - st_edge_rescap --; /* grab the radical */ - delta1 --; - pBNS->vert[v1].st_edge.cap = ((pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v1].st_edge.cap & ~EDGE_FLOW_ST_MASK); - nDots --; - } - /* grab the adjacent vertex2 radical (st_edge_rescap) if it exists */ - st_edge_rescap = (pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK) - (pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK); - while ( st_edge_rescap && delta2 ) { - st_edge_rescap --; /* grab the radical */ - delta2 --; - pBNS->vert[v2].st_edge.cap = ((pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v2].st_edge.cap & ~EDGE_FLOW_ST_MASK); - nDots --; - } - /* grab flows from v1 neighbors */ - for ( i = 0; delta1 && i < pBNS->vert[v1].num_adj_edges; i ++ ) { - iedge_i = pBNS->vert[v1].iedge[i]; - if ( iedge_i == iedge ) - continue; - pEdge_i = pBNS->edge + iedge_i; - if ( IS_FORBIDDEN(pEdge_i->forbidden, pBNS) ) - continue; - f = (pEdge_i->flow & EDGE_FLOW_MASK); - if ( f ) { - v_i = pEdge_i->neighbor12 ^ v1; - - fcd[ifcd].iedge = iedge_i; - fcd[ifcd].flow = pEdge_i->flow; - fcd[ifcd].cap = pEdge_i->cap; - - fcd[ifcd].v1 = v_i; - fcd[ifcd].flow_st1 = pBNS->vert[v_i].st_edge.flow; - fcd[ifcd].cap_st1 = pBNS->vert[v_i].st_edge.cap; - - fcd[ifcd].v2 = NO_VERTEX; - fcd[ifcd].flow_st2 = 0; - fcd[ifcd].cap_st2 = 0; - - fcd[++ifcd].iedge = NO_VERTEX; /* mark the end of the fcd[] data */ - pEdge_i->pass |= 64; - - while ( f && delta1 ) { - f --; - delta1 --; - pEdge_i->flow = ((pEdge_i->flow & EDGE_FLOW_MASK) - 1) | (pEdge_i->flow & ~EDGE_FLOW_MASK); - pBNS->vert[v_i].st_edge.flow = ((pBNS->vert[v_i].st_edge.flow & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v_i].st_edge.flow & ~EDGE_FLOW_ST_MASK); - /* next 2 lines added 01-22-2002 */ - pBNS->vert[v1].st_edge.cap = ((pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v1].st_edge.cap & ~EDGE_FLOW_ST_MASK); - pBNS->vert[v1].st_edge.flow = ((pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v1].st_edge.flow & ~EDGE_FLOW_ST_MASK); - - nDots ++; - } - - } - } - /* grab flows from v2 neighbors */ - for ( i = 0; delta2 && i < pBNS->vert[v2].num_adj_edges; i ++ ) { - iedge_i = pBNS->vert[v2].iedge[i]; - if ( iedge_i == iedge ) - continue; - pEdge_i = pBNS->edge + iedge_i; - if ( IS_FORBIDDEN(pEdge_i->forbidden, pBNS) ) - continue; - f = (pEdge_i->flow & EDGE_FLOW_MASK); - if ( f ) { - v_i = pEdge_i->neighbor12 ^ v2; - - fcd[ifcd].iedge = iedge_i; - fcd[ifcd].flow = pEdge_i->flow; - fcd[ifcd].cap = pEdge_i->cap; - - fcd[ifcd].v1 = v_i; - fcd[ifcd].flow_st1 = pBNS->vert[v_i].st_edge.flow; - fcd[ifcd].cap_st1 = pBNS->vert[v_i].st_edge.cap; - - fcd[ifcd].v2 = NO_VERTEX; - fcd[ifcd].flow_st2 = 0; - fcd[ifcd].cap_st2 = 0; - - fcd[++ifcd].iedge = NO_VERTEX; /* mark the end of the fcd[] data */ - pEdge_i->pass |= 64; - - while ( f && delta2 ) { - f --; - delta2 --; - pEdge_i->flow = ((pEdge_i->flow & EDGE_FLOW_MASK) - 1) | (pEdge_i->flow & ~EDGE_FLOW_MASK); - pBNS->vert[v_i].st_edge.flow = ((pBNS->vert[v_i].st_edge.flow & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v_i].st_edge.flow & ~EDGE_FLOW_ST_MASK); - /* next 2 lines added 01-22-2002 */ - pBNS->vert[v2].st_edge.cap = ((pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v2].st_edge.cap & ~EDGE_FLOW_ST_MASK); - pBNS->vert[v2].st_edge.flow = ((pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v2].st_edge.flow & ~EDGE_FLOW_ST_MASK); - - nDots ++; - } - - } - } - if ( delta1 || delta2 ) { - return BNS_CANT_SET_BOND; - } - } - - if ( f12 >= flow ) { - /* Decrease edge flow: Redirect flow to the neighbors and delete it on the edge: set flow12=cap12 = 0 */ - /* f12==flow fixes flow through the edge so that BNS cannot change it */ - /**********************************************************************************************/ - /* For example, simulate a removal of a double bond and create ONE or NONE augmenting path */ - /* Make it impossible for BNS to set same flow as it originally was */ - /**********************************************************************************************/ - Vertex v1 = pEdge->neighbor1; - Vertex v2 = (v1 ^ pEdge->neighbor12); - int delta; - /* if NOT (st-cap >= st-flow >= f12 >= flow) then error in the BN structure */ - if ( (pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK) < f12 || - (pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK) < f12 || - (pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK) < flow || - (pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK) < flow ) { - return BNS_CAP_FLOW_ERR; - } - fcd[ifcd].iedge = iedge; - fcd[ifcd].flow = pEdge->flow; - fcd[ifcd].cap = pEdge->cap; - - fcd[ifcd].v1 = v1; - fcd[ifcd].flow_st1 = pBNS->vert[v1].st_edge.flow; - fcd[ifcd].cap_st1 = pBNS->vert[v1].st_edge.cap; - - fcd[ifcd].v2 = v2; - fcd[ifcd].flow_st2 = pBNS->vert[v2].st_edge.flow; - fcd[ifcd].cap_st2 = pBNS->vert[v2].st_edge.cap; - - fcd[++ifcd].iedge = NO_VERTEX; /* mark the end of the fcd[] data */ - pEdge->pass |= 64; - - delta = f12 - flow; - /* remove current edge flow from st-edges */ - /* -- seem to be a bug -- - pBNS->vert[v1].st_edge.flow = ((pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK)-delta) | (pBNS->vert[v1].st_edge.flow & ~EDGE_FLOW_ST_MASK); - pBNS->vert[v2].st_edge.flow = ((pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK)-delta) | (pBNS->vert[v2].st_edge.flow & ~EDGE_FLOW_ST_MASK); - */ - - /* replacement to the above 2 lines 01-16-2002 */ - /* remove old edge flow from the flow of the adjacent vertices' st-edges */ - - pBNS->vert[v1].st_edge.flow = ((pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK)-f12) | (pBNS->vert[v1].st_edge.flow & ~EDGE_FLOW_ST_MASK); - pBNS->vert[v2].st_edge.flow = ((pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK)-f12) | (pBNS->vert[v2].st_edge.flow & ~EDGE_FLOW_ST_MASK); - - /* added 01-16-2002: reduce st-cap if new flow > 0 */ - /* remove new edge flow from the cap of the adjacent vertices' st-edges */ - pBNS->vert[v1].st_edge.cap = ((pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK)-flow) | (pBNS->vert[v1].st_edge.cap & ~EDGE_FLOW_ST_MASK); - pBNS->vert[v2].st_edge.cap = ((pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK)-flow) | (pBNS->vert[v2].st_edge.cap & ~EDGE_FLOW_ST_MASK); - - /* delete current edge flow and capacity */ - pEdge->flow = (pEdge->flow & ~EDGE_FLOW_MASK); - pEdge->cap = (pEdge->cap & ~EDGE_FLOW_MASK); - nDots = 2*delta; - } - return nDots; -} - - - -/**********************************************************************************/ -/* Connect new (fictitious, temporary) vertex to to nVertDoubleBond by a new edge */ -/* Add radical (set st-cap=1) to the new vertex, set cap=1 to the new edge */ -/* Add radical (set st-cap=1) to nVertSingleBond */ -/* Find augmenting path connecting new vertex to nVertSingleBond */ -/* This corresponds to moving H-atom from nVertSingleBond to nVertDoubleBond */ -/**********************************************************************************/ -int bAddNewVertex( BN_STRUCT *pBNS, int nVertDoubleBond, int nCap, int nFlow, int nMaxAdjEdges, int *nDots ) -{ - Vertex vlast = pBNS->num_vertices - 1; - Vertex vnew = pBNS->num_vertices; - Vertex v2 = nVertDoubleBond; - BNS_VERTEX *pVert2 = pBNS->vert + v2; /* pointer to an old vertex */ - BNS_VERTEX *pNewVert = pBNS->vert + vnew; /* pointer to a new vertex */ - - EdgeIndex iedge = pBNS->num_edges; - BNS_EDGE *pEdge = pBNS->edge + iedge; /* pointer to a new edge */ - - if ( iedge >= pBNS->max_edges || vnew >= pBNS->max_vertices ) { - return BNS_VERT_EDGE_OVFL; /* edges or vertices overflow */ - } - if ( (pBNS->vert[vlast].iedge - pBNS->iedge) + pBNS->vert[vlast].max_adj_edges + nMaxAdjEdges >= pBNS->max_iedges ) { - return BNS_VERT_EDGE_OVFL; /* iedges overflow */ - } - if ( pVert2->num_adj_edges >= pVert2->max_adj_edges || nMaxAdjEdges <= 0 ) { - return BNS_VERT_EDGE_OVFL; /* neighbors overflow */ - } - /* fill out the new edge, set its cap and flow, connect */ - /* memset( pEdge, 0, sizeof(*pEdge) ); */ - pEdge->cap = pEdge->cap0 = nCap; - pEdge->flow = pEdge->flow0 = nFlow; - pEdge->pass = 0; - pEdge->neighbor1 = v2; - pEdge->neighbor12 = v2 ^ vnew; - pEdge->forbidden = 0; - /* fill out the new vertex */ - /* memset( pNewVert, 0, sizeof(*pNewVert) ); */ - pNewVert->max_adj_edges = nMaxAdjEdges; - pNewVert->num_adj_edges = 0; - pNewVert->st_edge.cap0 = pNewVert->st_edge.cap = nCap; - pNewVert->st_edge.flow0 = pNewVert->st_edge.flow = nFlow; - pNewVert->st_edge.pass = 0; /* add initialization; added 2006-03-25 */ - pNewVert->iedge = pBNS->vert[vlast].iedge + pBNS->vert[vlast].max_adj_edges; - pNewVert->type = BNS_VERT_TYPE_TEMP; - *nDots += nCap - nFlow; - - pEdge->neigh_ord[v2>vnew] = pVert2->num_adj_edges; - pEdge->neigh_ord[v2num_adj_edges; - - /* connect new edge to v2 */ - pVert2->iedge[pVert2->num_adj_edges ++] = iedge; - /* connect new edge to vnew */ - pNewVert->iedge[pNewVert->num_adj_edges ++] = iedge; - - /* fix v2 flow and cap */ - *nDots -= (int)pVert2->st_edge.cap - (int)pVert2->st_edge.flow; - pVert2->st_edge.flow += nFlow; - if ( pVert2->st_edge.cap < pVert2->st_edge.flow ) { - pVert2->st_edge.cap = pVert2->st_edge.flow; - } - *nDots += (int)pVert2->st_edge.cap - (int)pVert2->st_edge.flow; - - pBNS->num_edges ++; - pBNS->num_vertices ++; - - return vnew; -} - - - -/*****************************************************************************************************/ -int AddNewEdge( BNS_VERTEX *p1, BNS_VERTEX *p2, BN_STRUCT *pBNS, int nEdgeCap, int nEdgeFlow ) -{ - int ip1 = p1 - pBNS->vert; - int ip2 = p2 - pBNS->vert; - int ie = pBNS->num_edges; - BNS_EDGE *e = pBNS->edge + ie; - /* debug: check bounds */ - if ( ip1 >= pBNS->max_vertices || ip1 < 0 || - ip2 >= pBNS->max_vertices || ip2 < 0 || - ie >= pBNS->max_edges || ie < 0 || - (p1->iedge - pBNS->iedge) < 0 || - (p1->iedge - pBNS->iedge) + p1->max_adj_edges > pBNS->max_iedges || - (p2->iedge - pBNS->iedge) < 0 || - (p2->iedge - pBNS->iedge) + p2->max_adj_edges > pBNS->max_iedges || - p1->num_adj_edges >= p1->max_adj_edges || - p2->num_adj_edges >= p2->max_adj_edges ) { - return BNS_VERT_EDGE_OVFL; - } - /* clear the edge */ - memset( e, 0, sizeof(*e) ); - /* connect */ - e->neighbor1 = inchi_min( ip1, ip2 ); - e->neighbor12 = ip1 ^ ip2; - p1->iedge[p1->num_adj_edges] = ie; - p2->iedge[p2->num_adj_edges] = ie; - e->neigh_ord[ip1 > ip2] = p1->num_adj_edges ++; - e->neigh_ord[ip1 < ip2] = p2->num_adj_edges ++; - e->cap = e->cap0 = nEdgeCap; - e->flow = e->flow0 = nEdgeFlow; - p1->st_edge.flow += nEdgeFlow; - p2->st_edge.flow += nEdgeFlow; - if ( p1->st_edge.cap < p1->st_edge.flow ) { - p1->st_edge.cap = p1->st_edge.flow; - } - if ( p2->st_edge.cap < p2->st_edge.flow ) { - p2->st_edge.cap = p2->st_edge.flow; - } - pBNS->num_edges ++; - return ie; -} - - - -/**********************************************************************************/ -BNS_IEDGE GetEdgeToGroupVertex( BN_STRUCT *pBNS, Vertex v1, AT_NUMB type) -{ - if ( v1 < pBNS->num_atoms ) { - Vertex v2; - BNS_EDGE *pEdge1; - BNS_VERTEX *pVert1 = pBNS->vert+v1; - int i = pVert1->num_adj_edges-1; - - while( 0 <= i ) { - pEdge1 = pBNS->edge + pVert1->iedge[i]; - v2 = pEdge1->neighbor12 ^ v1; - if ( pBNS->vert[v2].type == type ) { - return IS_FORBIDDEN(pEdge1->forbidden, pBNS)? NO_VERTEX : pVert1->iedge[i]; - } - i --; - } - return NO_VERTEX; /* not found t-group */ - } else - if ( v1 < pBNS->num_vertices ) { - return NO_VERTEX; - } - return BNS_VERT_EDGE_OVFL; -} - - - -/**********************************************************************************/ -Vertex GetGroupVertex(BN_STRUCT *pBNS, Vertex v1, AT_NUMB type) -{ - if ( v1 < pBNS->num_atoms ) { - Vertex v2; - BNS_EDGE *pEdge1; - BNS_VERTEX *pVert1 = pBNS->vert+v1; - int i = pVert1->num_adj_edges-1; - - AT_NUMB type2; - - if ( type == BNS_VERT_TYPE_ENDPOINT ) - type2 = BNS_VERT_TYPE_TGROUP; - else - if ( type == BNS_VERT_TYPE_C_POINT ) - type2 = BNS_VERT_TYPE_C_GROUP; - else - type2 = 0; - - if ( (pVert1->type & type) == type ) { - while( 0 <= i ) { - pEdge1 = pBNS->edge + pVert1->iedge[i]; - v2 = pEdge1->neighbor12 ^ v1; - if ( pBNS->vert[v2].type == type2 ) { - if ( IS_FORBIDDEN(pEdge1->forbidden, pBNS) ) { - return NO_VERTEX; - } - return v2; - } - i --; - } - } - return BNS_BOND_ERR; /* not found t-group */ - - } else - if ( v1 < pBNS->num_vertices ) { - return NO_VERTEX; - } - return BNS_VERT_EDGE_OVFL; -} - - - -/**********************************************************************************/ -int bAddStCapToAVertex( BN_STRUCT *pBNS, Vertex v1, Vertex v2, VertexFlow *nOldCapVertSingleBond, int *nDots, int bAdjacentDonors ) -{ - BNS_VERTEX *pVert1 = pBNS->vert + v1; - BNS_VERTEX *pVert; - BNS_EDGE *pEdge; - Vertex v; - int i, n; - VertexFlow nNewCap; - /* Change v1: increment its st-cap */ - n = 0; - nOldCapVertSingleBond[n++] = pVert1->st_edge.cap; - /*if ( pVert1->st_edge.cap == pVert1->st_edge.flow ) {*/ - pVert1->st_edge.cap ++; - *nDots += 1; - /*}*/ - /* increment caps of adjacent edges if - (1) the neighbor has st-cap != 0 and - (2) (edge cap==0) OR (nSumEdgeCap < pVert1->st_edge.cap && pVert->st_edge.flow > pVert1->st_edge.cap) - */ - if ( !(pVert1->type & BNS_VERT_TYPE_ANY_GROUP) ) { - /* - AT_NUMB nSumEdgeCap = 0; - for ( i = 0; i < pVert1->num_adj_edges; i ++ ) { - pEdge = pBNS->edge + pVert1->iedge[i]; - nSumEdgeCap += pEdge->cap; - } - */ - /* do not increment caps of t-group or c-group edges */ - for ( i = 0; i < pVert1->num_adj_edges; i ++ ) { - pEdge = pBNS->edge + pVert1->iedge[i]; - nOldCapVertSingleBond[n++] = pEdge->cap; /* save edge cap */ - v = pEdge->neighbor12 ^ v1; - if ( v == v2 && !bAdjacentDonors ) { - continue; - } - pVert = pBNS->vert + v; - if ( pVert->type & BNS_VERT_TYPE_ANY_GROUP ) - continue; - nNewCap = inchi_min(pVert->st_edge.cap, pVert1->st_edge.cap); - nNewCap = inchi_min(nNewCap, MAX_BOND_EDGE_CAP); - pEdge->cap = nNewCap; /* change edge cap */ - /* - if ( pVert->st_edge.cap > 0 && !pEdge->cap ) { - pEdge->cap ++; - } else - if ( pVert->st_edge.flow > pVert1->st_edge.cap && - pEdge->cap < MAX_BOND_EDGE_CAP && - nSumEdgeCap < pVert1->st_edge.cap ) { - pEdge->cap ++; - } - */ - } - } - - return n; /* number of elements in nOldCapVertSingleBond[*] */ -} - - - -/**********************************************************************************/ - -#define BNS_CHK_ALTP_NO_ALTPATH 0 -#define BNS_CHK_ALTP_SAME_TGROUP 1 -#define BNS_CHK_ALTP_SAME_VERTEX 2 -#define BNS_CHK_ALTP_SET_SUCCESS 4 - - - -/**********************************************************************************/ -int bSetBnsToCheckAltPath( BN_STRUCT *pBNS, int nVertDoubleBond, int nVertSingleBond, AT_NUMB type, - int path_type, ALT_PATH_CHANGES *apc, BNS_FLOW_CHANGES *fcd, int *nDots ) -{ - - if ( !pBNS->vert[nVertDoubleBond].st_edge.flow && - - !( path_type == ALT_PATH_MODE_REM2H_CHG || - path_type == ALT_PATH_MODE_ADD2H_CHG || - path_type == ALT_PATH_MODE_REM2H_TST || - path_type == ALT_PATH_MODE_ADD2H_TST ) - ) { - return BNS_CHK_ALTP_NO_ALTPATH; - } else { - - - Vertex vNew; - Vertex v1 = nVertSingleBond; - Vertex v2 = nVertDoubleBond; - - BNS_VERTEX *pVert1 = pBNS->vert + v1; - BNS_VERTEX *pVert2 = pBNS->vert + v2; - int n, bAdjacentDonors = 0; - int ifcd = 0; - - Vertex t1=NO_VERTEX; - Vertex t2=NO_VERTEX; - int iapc; - -/*#if ( TEST_REMOVE_S_ATOMS == 1 )*/ /* && ALT_PATH_MODE_4_SALT == path_type */ - if ( ( *pBNS->pbTautFlags & TG_FLAG_TEST_TAUT2_SALTS ) && - - ALT_PATH_MODE_4_SALT2 == path_type && - (BNS_VERT_TYPE_ENDPOINT & type) ) { - -/* ---------------------------------------------------------- - \ action | DB action (v2) | SB action (v1) | -vertex \ | accept H @ vertex | donate H @ vertex | -type \ | nVertDoubleBond | nVertSingleBond | -----------------+-------------------+-------------------+ - -ZH (v1) | error | -ZH(.) | -(cap>0 on edge | | increment | - except v1-v2) | | st-cap on Z | -----------------+-------------------+-------------------+ - =Z (v2) | =Z-(.) | error | - (st-flow>0) | add fict vertex | | - | with st-cap=1 | | -----------------+-------------------+-------------------+ - endpoint | T(.) | T-(.) | - of t-group | increment | add fict vertex | - represented | st-cap on T | with st-cap=1 | - by fictitious | | | - vertex T | | | ---------------------------------------------------------- -*/ - - int bSet_v1; /* indicator: v1 has been set */ - int bSet_v2; /* indicator: v2 has been set */ - int i; - - Vertex v1t = NO_VERTEX; - Vertex v2t = NO_VERTEX; - Vertex v1Act, v2Act; - Vertex v; - - memset( apc, 0, sizeof(*apc) ); - fcd[ifcd].iedge = NO_VERTEX; - *nDots = 0; - - if ( v1 == v2 ) { - return BNS_CHK_ALTP_SAME_VERTEX; - } - - /* check whether v1 has neighbors adjacent to - multiple bonds - */ - for ( i = 0, n = 0; i < pVert1->num_adj_edges; i ++ ) { - v = (pBNS->edge + pVert1->iedge[i])->neighbor12 ^ v1; /* v is adjacent to v1 */ - if ( v == v2 ) - continue; /* ignore connection to v2 */ - n += (pBNS->vert[v].st_edge.cap > 0); - } - if ( !n ) { - return BNS_CHK_ALTP_NO_ALTPATH; /* the vertex cannot have flow */ - } - - v1Act = v1; - v2Act = v2; - - /* find t-group that contains v1 */ - if ( (pVert1->type & type) == type ) { - v1t = GetGroupVertex(pBNS, v1, type); - if ( IS_BNS_ERROR( v1t ) ) { - return v1t; - } - if ( v1t != NO_VERTEX ) { - v1Act = v1t; - } - } - /* find t-group that contains v2 */ - if ( (pVert2->type & type) == type ) { - v2t = GetGroupVertex(pBNS, v2, type); - if ( IS_BNS_ERROR( v2t ) ) { - return v2t; - } - if ( v2t != NO_VERTEX ) { - v2Act = v2t; - } - } - if ( v1t != NO_VERTEX && v1t == v2t ) { - return BNS_CHK_ALTP_SAME_TGROUP; - } - - bSet_v1 = bSet_v2 = 0; - /* create new edges adjacent to v1t or v2 */ - iapc = 0; - if ( v1t != NO_VERTEX ) { - /* create new edge and vertex, connect to v1t */ - vNew = bAddNewVertex( pBNS, v1t, 1, 0, 1, nDots ); - if ( IS_BNS_ERROR(vNew) ) { - return vNew; - } - apc->vNewVertex[iapc] = vNew; - apc->bSetNew[iapc] = 1; - bSet_v1 = 1; - iapc ++; - } - if ( v2t == NO_VERTEX ) { - /* create new edge and vertex, connect to v2 */ - vNew = bAddNewVertex( pBNS, v2, 1, 0, 1, nDots ); - if ( IS_BNS_ERROR(vNew) ) { - return vNew; - } - apc->vNewVertex[iapc] = vNew; - apc->bSetNew[iapc] = 1; - bSet_v2 = 1; - iapc ++; - } - - /* add st-cap to v1 and/or v2t */ - iapc = 0; - if ( !bSet_v1 ) { - /* add st-cap to v1 */ - if ( v1t != NO_VERTEX ) { - return BNS_BOND_ERR; - } - n = bAddStCapToAVertex( pBNS, v1, v2Act, apc->nOldCapsVert[iapc], nDots, 0 ); - apc->bSetOldCapsVert[iapc] = n; - apc->vOldVert[iapc] = v1; - iapc ++; - } - if ( !bSet_v2 ) { - /* add st-cap to v2t */ - if ( v2t == NO_VERTEX ) { - return BNS_BOND_ERR; - } - n = bAddStCapToAVertex( pBNS, v2t, v1Act, apc->nOldCapsVert[iapc], nDots, 0 ); - apc->bSetOldCapsVert[iapc] = n; - apc->vOldVert[iapc] = v2t; - iapc ++; - } - if ( *nDots < 0 || *nDots %2 ) { - return BNS_SET_ALTP_ERR; - } - return BNS_CHK_ALTP_SET_SUCCESS; - - } - /* ( *pBNS->pbTautFlags & TG_FLAG_TEST_TAUT2_SALTS ) */ -/*#endif*/ /* ( TEST_REMOVE_S_ATOMS == 1 && ALT_PATH_MODE_4_SALT == path_type ) */ - /*****************************************************************/ - if ( path_type == ALT_PATH_MODE_REM2H_CHG || - path_type == ALT_PATH_MODE_ADD2H_CHG || - path_type == ALT_PATH_MODE_REM2H_TST || - path_type == ALT_PATH_MODE_ADD2H_TST ) { /* added 2004-03-18 */ - - int bDonors = (path_type == ALT_PATH_MODE_REM2H_CHG) || (path_type == ALT_PATH_MODE_REM2H_TST); - - int bSet_v1; /* indicator: v1 has been set */ - int bSet_v2; /* indicator: v2 has been set */ - int i, cap = 1; - - Vertex v1t = NO_VERTEX; - Vertex v2t = NO_VERTEX; - Vertex v1Act, v2Act; - Vertex v; - - memset( apc, 0, sizeof(*apc) ); - fcd[ifcd].iedge = NO_VERTEX; - *nDots = 0; - /* - if ( v1 == v2 ) { - return BNS_CHK_ALTP_SAME_VERTEX; - } - */ - /* check whether v1 and v2 have proper neighbors */ - for ( i = 0, n = bAdjacentDonors = 0; i < pVert1->num_adj_edges; i ++ ) { - v = (pBNS->edge + pVert1->iedge[i])->neighbor12 ^ v1; /* v is adjacent to v1 */ - /* do not ignore connection to v2 - if ( v == v2 ) - continue; - */ - n += bDonors ? (pBNS->vert[v].st_edge.cap > 0) : ((pBNS->edge + pVert1->iedge[i])->flow > 0); - bAdjacentDonors += bDonors ? (v == v2) && ((pBNS->edge + pVert1->iedge[i])->flow < MAX_BOND_EDGE_CAP) : 0; - /* two donors connected by a single or double bond */ - } - if ( !n && !bAdjacentDonors ) { - return BNS_CHK_ALTP_NO_ALTPATH; /* the vertex cannot have flow */ - } - for ( i = 0, n = bAdjacentDonors = 0; i < pVert2->num_adj_edges; i ++ ) { - v = (pBNS->edge + pVert2->iedge[i])->neighbor12 ^ v2; /* v is adjacent to v2 */ - /* do not ignore connection to v1 - if ( v == v1 ) - continue; - */ - n += bDonors ? (pBNS->vert[v].st_edge.cap > 0) : ((pBNS->edge + pVert2->iedge[i])->flow > 0); - bAdjacentDonors += bDonors ? (v == v1) && ((pBNS->edge + pVert2->iedge[i])->flow < MAX_BOND_EDGE_CAP ) : 0; - /* two donors connected by a single or double bond */ - } - if ( !n && !bAdjacentDonors ) { - return BNS_CHK_ALTP_NO_ALTPATH; /* the vertex cannot have flow */ - } - - v1Act = v1; - v2Act = v2; - - /* find t-group that contains v1 */ - if ( (pVert1->type & type) == type ) { - v1t = GetGroupVertex(pBNS, v1, type); - if ( BNS_BOND_ERR == v1t ) { - v1t = NO_VERTEX; - } else - if ( IS_BNS_ERROR( v1t ) ) { - return v1t; - } else - if ( v1t != NO_VERTEX ) { - v1Act = v1t; - } - } - /* find t-group that contains v2 */ - if ( (pVert2->type & type) == type ) { - v2t = GetGroupVertex(pBNS, v2, type); - if ( BNS_BOND_ERR == v2t ) { - v2t = NO_VERTEX; - } else - if ( IS_BNS_ERROR( v2t ) ) { - return v2t; - } else - if ( v2t != NO_VERTEX ) { - v2Act = v2t; - } - } - - if ( v1t != NO_VERTEX && v1t == v2t ) { - cap = 2; /* same t-group */ - } - - /* bAddNewVertex: (bDonors != 0) == (vit != NO_VERTEX), i=1,2 */ - bSet_v1 = bSet_v2 = 0; - /* create new edges adjacent to v1t or v2 */ - iapc = 0; - if ( (bDonors != 0) == (v1t != NO_VERTEX) ) { - /* create new edge and vertex, connect to v1Act */ - vNew = bAddNewVertex( pBNS, v1Act, cap, 0, 1, nDots ); - if ( IS_BNS_ERROR(vNew) ) { - return vNew; - } - apc->vNewVertex[iapc] = vNew; - apc->bSetNew[iapc] = 1; - bSet_v1 = 1; - iapc ++; - } - if ( (bDonors != 0) == (v2t != NO_VERTEX) && cap == 1 ) { - /* create new edge and vertex, connect to v2Act; do not do it if cap==2 */ - vNew = bAddNewVertex( pBNS, v2Act, cap, 0, 1, nDots ); - if ( IS_BNS_ERROR(vNew) ) { - return vNew; - } - apc->vNewVertex[iapc] = vNew; - apc->bSetNew[iapc] = 1; - bSet_v2 = 1; - iapc ++; - } else - if ( (bDonors != 0) == (v2t != NO_VERTEX) ) { - bSet_v2 = 1; - } - - /* add st-cap to v1 and/or v2t */ - iapc = 0; - /* if cap=2 then just increment st_cap 2 times */ - if ( !bSet_v1 ) { - /* add st-cap to v1 */ - if ( (bDonors != 0) == (v1t != NO_VERTEX) ) { - return BNS_BOND_ERR; - } - n = bAddStCapToAVertex( pBNS, v1Act, v2Act, apc->nOldCapsVert[iapc], nDots, bAdjacentDonors ); - apc->bSetOldCapsVert[iapc] = n; - apc->vOldVert[iapc] = v1Act; - iapc ++; - } - if ( !bSet_v2 ) { - /* add st-cap to v2t */ - if ( (bDonors != 0) == (v2t != NO_VERTEX) ) { - return BNS_BOND_ERR; - } - n = bAddStCapToAVertex( pBNS, v2Act, v1Act, apc->nOldCapsVert[iapc], nDots, bAdjacentDonors ); - apc->bSetOldCapsVert[iapc] = n; - apc->vOldVert[iapc] = v2Act; - iapc ++; - } - if ( *nDots < 0 || *nDots %2 ) { - return BNS_SET_ALTP_ERR; - } - return BNS_CHK_ALTP_SET_SUCCESS; - - } - /**************************************************************************/ - if ( path_type == ALT_PATH_MODE_REM_PROTON ) { /* added 2004-03-05 */ - if ( v1 >= 0 && v2 >= 0 && - (pVert1->type & BNS_VERT_TYPE_ANY_GROUP) && - (pVert2->type & BNS_VERT_TYPE_ANY_GROUP) ) { - /* create new edge and vertex, connect to v2 */ - if ( (pBNS->vert[v1].type & BNS_VERT_TYPE_C_GROUP) && - (pBNS->vert[v1].st_edge.flow == 2*pBNS->vert[v1].num_adj_edges ) ) { - /* so far in a charge group max edge flow = 1 2004-03-08 */ - return BNS_CHK_ALTP_NO_ALTPATH; - } - memset( apc, 0, sizeof(*apc) ); - fcd[ifcd].iedge = NO_VERTEX; - *nDots = 0; - iapc = 0; - - vNew = bAddNewVertex( pBNS, v2, 1, 0, 1, nDots ); - if ( IS_BNS_ERROR(vNew) ) { - return vNew; - } - apc->vNewVertex[iapc] = vNew; - apc->bSetNew[iapc] = 1; - /*iapc ++;*/ - /* add st-cap (dot) to v1 */ - n = bAddStCapToAVertex( pBNS, v1, v2, apc->nOldCapsVert[iapc], nDots, 0 ); - apc->bSetOldCapsVert[iapc] = n; - apc->vOldVert[iapc] = v1; - iapc ++; - return BNS_CHK_ALTP_SET_SUCCESS; - } - } - -#if ( NEUTRALIZE_ENDPOINTS == 1 ) /* { */ - - - *nDots = 0; - memset( apc, 0, sizeof(*apc) ); - fcd[ifcd].iedge = NO_VERTEX; - - if ( type & BNS_VERT_TYPE_ENDPOINT ) { - BNS_IEDGE iedge; - AT_NUMB type2; - int ret2; - /* prohibit charge movement */ - type2 = BNS_VERT_TYPE_C_GROUP; - iedge = GetEdgeToGroupVertex( pBNS, v1, type2 ); - if (iedge != NO_VERTEX ) { - /* set flow=1 on an edge to a c-group vertex to make sure there is no positive charge - * when moving tautomeric H-atoms - */ - ret2 = bSetFlowToCheckOneBond( pBNS, iedge, 1, fcd+ifcd ); - if ( IS_BNS_ERROR(ret2) ) { - return ret2; - } - *nDots += ret2; - while ( fcd[ifcd].iedge != NO_VERTEX ) { - ifcd ++; - } - } - iedge = GetEdgeToGroupVertex( pBNS, v2, type2 ); - if (iedge != NO_VERTEX ) { - /* set flow=1 on an edge to a c-group vertex to make sure there is no positive charge - * when moving tautomeric H-atoms - */ - ret2 = bSetFlowToCheckOneBond( pBNS, iedge, 1, fcd+ifcd ); - if ( IS_BNS_ERROR(ret2) ) { - return ret2; - } - *nDots += ret2; - while ( fcd[ifcd].iedge != NO_VERTEX ) { - ifcd ++; - } - } - /* set hydrogen counts */ - type2 = BNS_VERT_TYPE_TGROUP; - iedge = GetEdgeToGroupVertex( pBNS, v1, type2 ); - if (iedge != NO_VERTEX ) { - /* set flow=1 on an edge to a t-group vertex to make sure there is - * a moveable hydrogen atom or (-) on v1 when moving tautomeric H-atoms - */ -#if ( FIX_H_CHECKING_TAUT == 1 ) - ret2 = bSetFlowToCheckOneBond( pBNS, iedge, 1, fcd+ifcd ); - if ( IS_BNS_ERROR(ret2) ) { - return ret2; - } - *nDots += ret2; - while ( fcd[ifcd].iedge != NO_VERTEX ) { - ifcd ++; - } -#else - t1 = pBNS->edge[iedge].neighbor12 ^ v1; -#endif - } - iedge = GetEdgeToGroupVertex( pBNS, v2, type2 ); - if (iedge != NO_VERTEX ) { - /* set flow=0 on an edge to a t-group vertex to make sure there is - * no moveable hydrogen atom or (-) on v2 when moving tautomeric H-atoms - */ -#if ( FIX_H_CHECKING_TAUT == 1 ) - ret2 = bSetFlowToCheckOneBond( pBNS, iedge, 0, fcd+ifcd ); - if ( IS_BNS_ERROR(ret2) ) { - return ret2; - } - *nDots += ret2; - while ( fcd[ifcd].iedge != NO_VERTEX ) { - ifcd ++; - } -#else - t2 = pBNS->edge[iedge].neighbor12 ^ v2; -#endif - } - -#if ( FIX_H_CHECKING_TAUT == 1 ) -#else - if ( t1 == t2 && t1 != NO_VERTEX ) { - return BNS_CHK_ALTP_SAME_TGROUP; - } -#endif - iapc = 0; - /* create new edge and vertex with cap=1 at v2 and/or t1 */ - if ( t1 != NO_VERTEX ) { - /* create new edge and vertex, connect to t1 */ - vNew = bAddNewVertex( pBNS, t1, 1/*cap*/, 0/*flow*/, 1/*max_adj_edges*/, nDots ); - if ( IS_BNS_ERROR(vNew) ) { - return vNew; - } - apc->vNewVertex[iapc] = vNew; - apc->bSetNew[iapc] = 1; - iapc ++; - } - if ( t2 == NO_VERTEX ) { - /* create new edge and vertex, connect to v2 */ - vNew = bAddNewVertex( pBNS, v2, 1/*cap*/, 0/*flow*/, 1/*max_adj_edges*/, nDots ); - if ( IS_BNS_ERROR(vNew) ) { - return vNew; - } - apc->vNewVertex[iapc] = vNew; - apc->bSetNew[iapc] = 1; - iapc ++; - } - - /* add st-cap to v1 and/or v2t */ - iapc = 0; - if ( t1 == NO_VERTEX ) { - /* add st-cap to v1 */ - n = bAddStCapToAVertex( pBNS, v1, (Vertex)(t2 == NO_VERTEX? v2:t2), apc->nOldCapsVert[iapc], nDots, 0 ); - apc->bSetOldCapsVert[iapc] = n; - apc->vOldVert[iapc] = v1; - iapc ++; - } - if ( t2 != NO_VERTEX ) { - /* add st-cap to t2 */ - n = bAddStCapToAVertex( pBNS, t2, (Vertex)(t1 == NO_VERTEX? v1:t1), apc->nOldCapsVert[iapc], nDots, 0 ); - apc->bSetOldCapsVert[iapc] = n; - apc->vOldVert[iapc] = t2; - iapc ++; - } - } else { - /* create new edge and vertex, connect to v2 */ - vNew = bAddNewVertex( pBNS, v2, 1 /* cap*/, 0 /* flow */, 1 /* max_adj_edges */, nDots ); - if ( IS_BNS_ERROR(vNew) ) { - return vNew; - } - apc->vNewVertex[0] = vNew; - apc->bSetNew[0] = 1; - - /* add st-cap to v1 */ - n = bAddStCapToAVertex( pBNS, v1, v2, apc->nOldCapsVert[0], nDots, 0 ); - apc->bSetOldCapsVert[0] = n; - apc->vOldVert[0] = v1; - } -#else /* } NEUTRALIZE_ENDPOINTS == 0 {*/ - - *nDots = 0; - memset( apc, 0, sizeof(*apc) ); - fcd[ifcd].iedge = NO_VERTEX; - - /* create new edge and vertex, connect to v2 */ - vNew = bAddNewVertex( pBNS, v2, 1 /* cap*/, 0 /* flow */, 1 /* max_adj_edges */, nDots, 0 ); - if ( IS_BNS_ERROR(vNew) ) { - return vNew; - } - apc->vNewVertex[0] = vNew; - apc->bSetNew[0] = 1; - - /* add st-cap to v1 */ - n = bAddStCapToAVertex( pBNS, v1, v2, apc->nOldCapsVert[0], nDots ); - apc->bSetOldCapsVert[0] = n; - apc->vOldVert[0] = v1; -#endif /* } NEUTRALIZE_ENDPOINTS */ - - if ( *nDots < 0 || *nDots %2 ) { - return BNS_SET_ALTP_ERR; - } - return BNS_CHK_ALTP_SET_SUCCESS; - } - /*return BNS_CHK_ALTP_NO_ALTPATH;*/ -} - - - -/**********************************************************************************/ -int bRestoreBnsAfterCheckAltPath( BN_STRUCT *pBNS, ALT_PATH_CHANGES *apc, int bChangeFlow ) -/* int nVertDoubleBond, int nVertSingleBond, int nNewVertex, AT_NUMB *nOldCapVertSingleBond */ -{ - BNS_EDGE *pEdge; - Vertex vNew; - Vertex vOld; - BNS_VERTEX *pOldVert; - BNS_VERTEX *pNewVert; - int i, j, n, ret; - - ret = 0; - if ( bChangeFlow & BNS_EF_UPD_H_CHARGE ) { - /* remove new temp. vertices and edges connectong them to the structure */ - for ( i = sizeof(apc->bSetNew)/sizeof(apc->bSetNew[0])-1; 0 <= i; i -- ) { - if ( apc->bSetNew[i] ) { - vNew = apc->vNewVertex[i]; - pNewVert = pBNS->vert + vNew; - for ( j = 0; j < pNewVert->num_adj_edges; j ++ ) { - pEdge = pBNS->edge+pNewVert->iedge[j]; - vOld = pEdge->neighbor12 ^ vNew; - pOldVert = pBNS->vert + vOld; - pOldVert->st_edge.flow -= pEdge->flow; - pOldVert->st_edge.cap -= pEdge->flow; - /* disconnect new edge from pOldVert */ - pOldVert->iedge[--pOldVert->num_adj_edges] = 0; - /* clear the new edge */ - memset( pEdge, 0, sizeof(*pEdge) ); - /* and decrement the total number of edges */ - pBNS->num_edges --; - } - /* clear the new vertex */ - memset( pNewVert, 0, sizeof( pNewVert ) ); - /* and decrement the total number of vertices (new vertice ids are contiguous */ - pBNS->num_vertices --; - ret ++; - } - } - /* Restore changed caps of old vertices */ - for ( i = sizeof(apc->bSetOldCapsVert)/sizeof(apc->bSetOldCapsVert[0])-1; 0 <= i; i -- ) { - if ( n = apc->bSetOldCapsVert[i] ) { - pOldVert = pBNS->vert + apc->vOldVert[i]; - if ( pOldVert->st_edge.flow <= apc->nOldCapsVert[i][0] ) { - pOldVert->st_edge.cap = apc->nOldCapsVert[i][0]; - n --; - ret ++; - for ( j = 0; j < n && j < pOldVert->num_adj_edges; j ++ ) { - pEdge = pBNS->edge + pOldVert->iedge[j]; - pEdge->cap = apc->nOldCapsVert[i][j+1]; - } - } - } - } - } else { - /* Restore changed caps of old vertices */ - for ( i = sizeof(apc->bSetOldCapsVert)/sizeof(apc->bSetOldCapsVert[0])-1; 0 <= i; i -- ) { - if ( n = apc->bSetOldCapsVert[i] ) { - pOldVert = pBNS->vert + apc->vOldVert[i]; - pOldVert->st_edge.cap = apc->nOldCapsVert[i][0]; - n --; - ret ++; - for ( j = 0; j < n && j < pOldVert->num_adj_edges; j ++ ) { - pEdge = pBNS->edge + pOldVert->iedge[j]; - pEdge->cap = apc->nOldCapsVert[i][j+1]; - } - } - } - - /* remove new temp. vertices and edges connectong them to the structure */ - for ( i = sizeof(apc->bSetNew)/sizeof(apc->bSetNew[0])-1; 0 <= i; i -- ) { - if ( apc->bSetNew[i] ) { - vNew = apc->vNewVertex[i]; - pNewVert = pBNS->vert + vNew; - for ( j = 0; j < pNewVert->num_adj_edges; j ++ ) { - pEdge = pBNS->edge+pNewVert->iedge[j]; - vOld = pEdge->neighbor12 ^ vNew; - pOldVert = pBNS->vert + vOld; - /* disconnect new edge from pOldVert */ - pOldVert->iedge[--pOldVert->num_adj_edges] = 0; - /* clear the new edge */ - memset( pEdge, 0, sizeof(*pEdge) ); - /* and decrement the total number of edges */ - pBNS->num_edges --; - } - /* clear the new vertex */ - memset( pNewVert, 0, sizeof( pNewVert ) ); - /* and decrement the total number of vertices (new vertice ids are contiguous */ - pBNS->num_vertices --; - ret ++; - } - } - } - return 0; -} - - - -/**********************************************************************************/ -int bExistsAnyAltPath( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at, int num_atoms, - int nVert2, int nVert1, int path_type ) -{ - int nRet1, nRet2; - nRet1 = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, nVert2, nVert1, path_type ); - if ( nRet1 > 0 ) - return nRet1; - nRet2 = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, nVert1, nVert2, path_type ); - if ( nRet2 > 0 ) - return nRet2; - if ( IS_BNS_ERROR( nRet1 ) ) - return nRet1; - if ( IS_BNS_ERROR( nRet2 ) ) - return nRet2; - return 0; -} - -#define ALT_PATH_TAUTOM 1 -#define ALT_PATH_CHARGE 2 -#define ALT_PATH_4_SALT 3 - - - -/**********************************************************************************/ -int bIsBnsEndpoint( BN_STRUCT *pBNS, int v ) -{ - int i, vt; - BNS_VERTEX *pVert; /* vertices */ - BNS_EDGE *pEdge; /* edges */ - - if ( 0 <= v && v < pBNS->num_atoms && (pVert = pBNS->vert+v) && (pVert->type & BNS_VERT_TYPE_ENDPOINT) ) { - for ( i = pVert->num_adj_edges - 1; 0 <= i; i -- ) { - pEdge = pBNS->edge + pVert->iedge[i]; - vt = pEdge->neighbor12 ^ v; - if ( pBNS->vert[vt].type & BNS_VERT_TYPE_TGROUP ) { - return !IS_FORBIDDEN(pEdge->forbidden, pBNS); - } - } - } - return 0; -} - - - -/**********************************************************************************/ -#if ( BNS_RAD_SEARCH == 1 ) -/**********************************************************************************/ -int bRadChangesAtomType( BN_STRUCT *pBNS, BN_DATA *pBD, Vertex v, Vertex v_1, Vertex v_2 ) -{ - - EdgeIndex iuv; - Vertex v_O, v_ChgOrH; - /* the previous atom along the path: should be a terminal atom */ - if ( v_1 == NO_VERTEX ) { - v_1 = GetPrevVertex( pBNS, v, pBD->SwitchEdge, &iuv ); - } - v_O = v_1 / 2 - 1; - if ( v_O < 0 || v_O >= pBNS->num_atoms ) { - return 0; - } - /* make sure v_O is a terminal atom: its second neighbor is not an atom */ - if ( pBNS->vert[pBNS->edge[pBNS->vert[v_O].iedge[1]].neighbor12 ^ v_O].type & BNS_VERT_TYPE_ATOM ) { - return 0; - } - /* the next to previous vertex vertex along the path: should be a Charge or Taut group vertex */ - if ( v_2 == NO_VERTEX ) { - v_2 = GetPrevVertex( pBNS, v_1, pBD->SwitchEdge, &iuv ); - } - v_ChgOrH = v_2 / 2 - 1; - if ( v_ChgOrH < pBNS->num_atoms ) { - return 0; - } - /* make sure v_ChgOrH is a charge or taut_group */ - if ( pBNS->vert[v_ChgOrH].type & (BNS_VERT_TYPE_TGROUP | BNS_VERT_TYPE_C_GROUP) ) - return 1; - return 0; -} - - - -/**********************************************************************************/ -int RegisterRadEndpoint( BN_STRUCT *pBNS, BN_DATA *pBD, Vertex u) -{ - EdgeIndex iuv; - int i, num_found; - Vertex v, w; - Vertex u_last, v2; - switch( pBD->bRadSrchMode ) { - case RAD_SRCH_NORM: - /* go backwards along alt path and stop at the 1st found atom (not a fictitious vertex) */ - /* we need only vertices where a radical may be moved, therefore exclude u%2=1 (odd) vertices */ - /* atom number = u/2-1; u = 0 or 1 is 's' or 't' vertices, respectively, they are not atoms */ - num_found = 0; - while ( u > Vertex_t && (u % 2 || u/2 > pBNS->num_atoms ) ) { - u = GetPrevVertex( pBNS, u, pBD->SwitchEdge, &iuv ); - } - w = u/2 - 1; /* Check whether u is a radical endpoint */ - if ( Vertex_t < u && w < pBNS->num_atoms && - pBNS->vert[w].st_edge.cap == (pBNS->vert[w].st_edge.flow & EDGE_FLOW_ST_MASK) ) { - /* u is an atom; it is not a radical atom */ - /* now search for the starting radical atom by following the path back from u */ - v = u_last = u; - while( v > Vertex_t ) { - u = v; - v = GetPrevVertex( pBNS, u, pBD->SwitchEdge, &iuv ); /* Radical endpoint */ - } - /* check whether u is a radical atom */ - if ( !(u%2) && Vertex_t < u && - (u = u/2 - 1) < pBNS->num_atoms && - pBNS->vert[u].st_edge.cap > (pBNS->vert[u].st_edge.flow & EDGE_FLOW_ST_MASK) ) { - /* at pBNS->vert[u] we have found the radical that originated the path */ - /* pBD->RadEndpoints[2k] is the radical, pBD->RadEndpoints[2k+1] is the farthest atom */ - /* to which the radical may be moved (farthest reachable atom) */ - - /* add *all* atoms that may receive radical from u_rad */ - /* exception: at2 in: ==(+/-/H)---at1==at2(possible rad endpoint) if pBNS->type_TACN */ - - for ( v = u_last; v > Vertex_t; v = GetPrevVertex( pBNS, v, pBD->SwitchEdge, &iuv ) ) { - if ( !(v%2) && (v2 = v/2 - 1) < pBNS->num_atoms && - pBNS->vert[v2].st_edge.cap == (pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK) ) { - /* check exception */ - if ( pBNS->type_TACN && - bRadChangesAtomType( pBNS, pBD, v, NO_VERTEX, NO_VERTEX ) ) { - continue; - } - /* add */ - for ( i = 0; i < pBD->nNumRadEndpoints; i += 2 ) { - /* check whether this pair, (u,w), has already been saved */ - if ( u == pBD->RadEndpoints[i] && - v2 == pBD->RadEndpoints[i+1] ) { - break; - } - } - if ( i >= pBD->nNumRadEndpoints ) { - /* add new (u,w) pair */ - if ( pBD->nNumRadEndpoints+2 <= pBD->max_num_vertices ) { - /* add */ - pBD->RadEndpoints[pBD->nNumRadEndpoints ++] = u; /* radical */ - pBD->RadEndpoints[pBD->nNumRadEndpoints ++] = v2; /* endpoint */ - num_found ++; - /*return 1;*/ /* registered */ - } else { - return BNS_VERT_EDGE_OVFL; - } - } - } - } - if ( num_found ) { - return 1; - } - } - } - break; - - case RAD_SRCH_FROM_FICT: - /* find nearest atom accessible from a fictitious vertex */ - /* go backwards along alt path and stop at the 1st found atom (not a fictitious vertex) */ - v = u; - w = NO_VERTEX; /* the nearest atom -- radical-endpoint */ - u = NO_VERTEX; /* fictitious vertex carrying a radical */ - while ( v > Vertex_t ) { - u = v; - if ( !(v % 2) && v/2 <= pBNS->num_atoms && - pBNS->vert[v/2-1].st_edge.cap - pBNS->vert[v/2-1].st_edge.flow < 2 ) { - w = v; /* vertex w is atom that may be singlet or doublet but not triplet */ - } - v = GetPrevVertex( pBNS, u, pBD->SwitchEdge, &iuv ); - } - v = u/2 - 1; /* vertex u may be the radical from which the path originated; w is the nearest atom */ - if ( w == NO_VERTEX || u == NO_VERTEX || w % 2 || u == w || v < pBNS->num_atoms || - pBNS->vert[v].st_edge.cap == pBNS->vert[v].st_edge.flow || - (w = w/2 - 1) >= pBNS->num_atoms ) { - break; /* reject */ - } - u = v; - /* at pBNS->vert[u] we have found the radical that originated the path, w is the nearest atom */ - for ( i = 0; i < pBD->nNumRadEndpoints; i += 2 ) { - if ( u == pBD->RadEndpoints[i] && - w == pBD->RadEndpoints[i+1] ) { - break; /* this pair has already been stored */ - } - } - if ( i >= pBD->nNumRadEndpoints ) { - /* a new pair has been found */ - if ( pBD->nNumRadEndpoints+2 <= pBD->max_num_vertices ) { - /* add */ - pBD->RadEndpoints[pBD->nNumRadEndpoints ++] = u; /* radical */ - pBD->RadEndpoints[pBD->nNumRadEndpoints ++] = w; /* endpoint */ - return 1; /* registered */ - } else { - return BNS_VERT_EDGE_OVFL; - } - } - break; - } - - return 0; /* rejected */ -} - - - -/**********************************************************************************/ -int cmp_rad_endpoints( const void *a1, const void *a2 ) -{ - /* Vertex radical_vertex, radical_endpoint */ - const Vertex *p1 = (const Vertex *)a1; - const Vertex *p2 = (const Vertex *)a2; - if ( p1[0] < p2[0] ) - return -1; - if ( p1[0] > p2[0] ) - return 1; - if ( p1[1] < p2[1] ) - return -1; - if ( p1[1] > p2[1] ) - return 1; - return 0; -} - - - -/**********************************************************************************/ -int RemoveRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at ) -{ - BNS_EDGE *e; - EdgeIndex ie; - BNS_VERTEX *p1, *p2; - Vertex v1, v2; - int i, delta, rad; - for ( i = pBD->nNumRadEdges-1; 0 <= i; i -- ) { - ie = pBD->RadEdges[i]; - if ( ie < 0 || ie >= pBNS->num_edges ) { - goto error_exit; - } - e = pBNS->edge + ie; - v1 = e->neighbor1; - v2 = e->neighbor12 ^ v1; /* v2 > v1 <=> v2 was added later */ - if ( ie + 1 != pBNS->num_edges || - v1 < 0 || v1 >= pBNS->num_vertices || - v2 < 0 || v2 >= pBNS->num_vertices ) { - goto error_exit; - } - p1 = pBNS->vert + v1; - p2 = pBNS->vert + v2; - - if ( p2->iedge[p2->num_adj_edges-1] != ie || - p1->iedge[p1->num_adj_edges-1] != ie ) { - goto error_exit; - } - p2->num_adj_edges --; - p1->num_adj_edges --; - p2->iedge[p2->num_adj_edges] = 0; - p1->iedge[p1->num_adj_edges] = 0; - p2->st_edge.flow -= e->flow; - p1->st_edge.flow -= e->flow; - - if ( !p2->num_adj_edges && v2 >= pBNS->num_atoms ) { - if ( v2+1 != pBNS->num_vertices ) { - goto error_exit; - } - memset( p2, 0, sizeof(*p2) ); - pBNS->num_vertices --; - } - if ( !p1->num_adj_edges && v1 >= pBNS->num_atoms ) { - if ( v1+1 != pBNS->num_vertices ) { - goto error_exit; - } - memset( p1, 0, sizeof(*p1) ); - pBNS->num_vertices --; - } - if ( at && v1 < pBNS->num_atoms ) { - delta = p1->st_edge.cap - p1->st_edge.flow; - rad = at[v1].radical; - switch( delta ) { - case 0: - if ( rad == RADICAL_DOUBLET ) - rad = 0; - break; - case 1: - if ( rad != RADICAL_DOUBLET ) - rad = RADICAL_DOUBLET; - } - at[v1].radical = rad; - } - memset( e, 0, sizeof(*e) ); - pBNS->num_edges --; - } - pBD->nNumRadEdges = 0; - pBD->nNumRadicals = 0; - pBD->bRadSrchMode = RAD_SRCH_NORM; - return 0; -error_exit: - return BNS_PROGRAM_ERR; -} - - - -/**********************************************************************************/ -int RestoreRadicalsOnly( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at ) -{ - BNS_EDGE *e; - EdgeIndex ie; - BNS_VERTEX *p1, *p2; - Vertex v1, v2; - int i, delta, rad; - int p1_num_adj_edges, p2_num_adj_edges; - - for ( i = pBD->nNumRadEdges-1; 0 <= i; i -- ) { - ie = pBD->RadEdges[i]; - if ( ie < 0 || ie >= pBNS->num_edges ) { - goto error_exit; - } - e = pBNS->edge + ie; - v1 = e->neighbor1; /* atom */ - v2 = e->neighbor12 ^ v1; /* v2 > v1 <=> v2 was added later */ - if ( v1 < 0 || v1 >= pBNS->num_atoms || - v2 < pBNS->num_atoms || v2 >= pBNS->num_vertices ) { - goto error_exit; - } - p1 = pBNS->vert + v1; - p2 = pBNS->vert + v2; - - p1_num_adj_edges = e->neigh_ord[0]; - p2_num_adj_edges = e->neigh_ord[1]; - - if ( p2->iedge[p2_num_adj_edges] != ie || - p1->iedge[p1_num_adj_edges] != ie ) { - goto error_exit; - } - - if ( at && v1 < pBNS->num_atoms ) { - delta = p1->st_edge.cap - p1->st_edge.flow + e->flow; - rad = at[v1].radical; - switch( delta ) { - case 0: - if ( rad == RADICAL_DOUBLET ) - rad = 0; - break; - case 1: - if ( rad != RADICAL_DOUBLET ) - rad = RADICAL_DOUBLET; - } - at[v1].radical = rad; - } - } - return 0; -error_exit: - return BNS_PROGRAM_ERR; -} - - - -/**********************************************************************************/ -int SetRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode ) -{ - int ret, i, j, k, num_new_edges, delta; - BNS_VERTEX *pRad, *pEndp; - Vertex wRad, vRad, vEndp, nNumRadicals; - int nDots=0 /* added initialization, 2006-03 */, nNumEdges; - if ( pBNS->tot_st_cap <= pBNS->tot_st_flow ) { - return 0; - } - pBD->nNumRadEndpoints = 0; - pBD->nNumRadEdges = 0; - pBD->bRadSrchMode = bRadSrchMode; - pBNS->alt_path = pBNS->altp[0]; - pBNS->bChangeFlow = 0; - ret = BalancedNetworkSearch( pBNS, pBD, BNS_EF_RAD_SRCH ); - ReInitBnData( pBD ); - ReInitBnStructAltPaths( pBNS ); - if ( !ret && pBD->nNumRadEndpoints >= 2 ) { - /* sort by radical locations */ - qsort( pBD->RadEndpoints, pBD->nNumRadEndpoints/2, 2*sizeof(pBD->RadEndpoints[0]), cmp_rad_endpoints ); - num_new_edges = 0; - nNumRadicals = 0; - /* create new vertices (type=BNS_VERT_TYPE_TEMP) and edges with flow=cap=1 */ - /* connecting the new vertices radical vertices */ - for ( i = 0; i < pBD->nNumRadEndpoints; i = j ) { - wRad = pBD->RadEndpoints[i]; - pRad = pBNS->vert + wRad; - delta = pRad->st_edge.cap - (pRad->st_edge.flow & EDGE_FLOW_ST_MASK); - if ( delta <= 0 ) { - delta = 1; - } - nNumEdges = 0; - for ( j = i; j < pBD->nNumRadEndpoints && wRad == pBD->RadEndpoints[j] ; j += 2 ) { - nNumEdges ++; - } - /* add new aux vertex to the radical atom/vertex */ - vRad = bAddNewVertex( pBNS, wRad, delta, delta, nNumEdges+1, &nDots ); - if ( IS_BNS_ERROR( vRad ) ) { - ret = vRad; - goto error_exit; - } - pRad = pBNS->vert + vRad; - pBD->RadEdges[pBD->nNumRadEdges ++] = pRad->iedge[pRad->num_adj_edges-1]; - /* replace references to vertex wRad with vRad */ - for ( k = i, nNumEdges = 0; k < j; k += 2 ) { - pBD->RadEndpoints[k] = vRad; - } - nNumRadicals ++; - } - /* all vRad vertex indices should be in the range vFirstNewVertex...vFirstNewVertex+nNumRadicals-1 */ - /* connect new vertices to the radical endpoints thus replacing radicals with even-length alternating cycles */ - for ( i = 0; i < pBD->nNumRadEndpoints; i = j ) { - vRad = pBD->RadEndpoints[i]; - pRad = pBNS->vert + vRad; - for ( j = i; j < pBD->nNumRadEndpoints && vRad == pBD->RadEndpoints[j] ; j += 2 ) { - /* connect vew vertex pRad to radical endpoints */ - vEndp = pBD->RadEndpoints[j+1]; - pEndp = pBNS->vert + vEndp; - ret = AddNewEdge( pRad, pEndp, pBNS, 1, 0 ); - if ( IS_BNS_ERROR( ret ) ) { - goto error_exit; - } - pBD->RadEdges[pBD->nNumRadEdges ++] = ret; - } - } - pBD->nNumRadicals = nNumRadicals; - return nNumRadicals; /* done */ - } - return 0; /* nothing to do */ - -error_exit: - RemoveRadEndpoints( pBNS, pBD, NULL ); - return ret; - -} - - - -/**********************************************************************************/ -#define MAX_NUM_RAD 256 - - - -/***************************************************************************/ -int SetRadEndpoints2( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode ) -{ - int ret = 0, i, j, k, n, num_new_edges, delta = 1; - BNS_VERTEX *pRad, *pEndp; - Vertex wRad, vRad, vEndp, nNumRadicals; - Vertex vRadList[MAX_NUM_RAD], vRadEqul[MAX_NUM_RAD]; - int nNumRad = 0; - int edge_flow; - int nDots=0 /* added initialization, 2006-03 */, nNumEdges; - NodeSet VertSet; - if ( pBNS->tot_st_cap <= pBNS->tot_st_flow ) { - return 0; - } - /* find all radicals: their vertices have st_cap-st_flow=delta */ - /* save radical atom numbers in vRadList[] and remove radical by making st_cap=st_flow */ - for ( i = 0; i < pBNS->num_atoms; i ++ ) { - if ( pBNS->vert[i].st_edge.cap - delta == (pBNS->vert[i].st_edge.flow & EDGE_FLOW_ST_MASK) ) { - if ( nNumRad < MAX_NUM_RAD ) { - pBNS->vert[i].st_edge.cap -= delta; - pBNS->tot_st_cap -= delta; - vRadList[nNumRad] = i; /* radical position; i > j <=> vRadList[i] > vRadList[j] */ - vRadEqul[nNumRad] = nNumRad; /* the smallest radical atom that has reachable - * atoms in common with this radical atom - * always keep vRadEqul[nNumRad] <= nNumRad */ - nNumRad ++; - } - } - } - if ( pBNS->tot_st_cap - pBNS->tot_st_flow > nNumRad ) { - return BNS_CAP_FLOW_ERR; /* extra st_cap on non-atoms or program error */ - } - memset( &VertSet, 0, sizeof(VertSet) ); - /* find reachable atoms by enabling each radical separately */ - for ( j = 0; j < nNumRad; j ++ ) { - i = vRadList[j]; - pBD->nNumRadEndpoints = 0; - pBD->nNumRadEdges = 0; - pBD->bRadSrchMode = bRadSrchMode; - pBNS->alt_path = pBNS->altp[0]; - pBNS->bChangeFlow = 0; - pBNS->vert[i].st_edge.cap += delta; /* enable single radical */ - pBNS->tot_st_cap += delta; - ret = BalancedNetworkSearch( pBNS, pBD, BNS_EF_RAD_SRCH ); /* find reachable atoms */ - ReInitBnData( pBD ); - ReInitBnStructAltPaths( pBNS ); - pBD->bRadSrchMode = RAD_SRCH_NORM; - pBNS->vert[i].st_edge.cap -= delta; /* disable single radical */ - pBNS->tot_st_cap -= delta; - if ( IS_BNS_ERROR( ret ) ) { - goto error_exit; - } else - if ( ret ) { - ret = BNS_RADICAL_ERR; /* found augmenting path: should not happen since only one radical was enabled */ - goto error_exit; - } - if ( !ret && pBD->nNumRadEndpoints >= 2 ) { - /* sort by: primary_key=radical locations, secondary_key=radical endoint */ - qsort( pBD->RadEndpoints, pBD->nNumRadEndpoints/2, 2*sizeof(pBD->RadEndpoints[0]), cmp_rad_endpoints ); - if ( pBD->RadEndpoints[0] != i || pBD->RadEndpoints[pBD->nNumRadEndpoints-2] != i ) { - ret = BNS_RADICAL_ERR; /* more than one radical vertex */ - goto error_exit; - } - if ( nNumRad > 1 ) { - /* if more than one radical then save reachable atoms in bitmaps to allow */ - /* faster finding whether same atoms are reachable by two or more radicals */ - /* Later merge such sets */ - if ( NULL == VertSet.bitword ) { - SetBitCreate( ); - if ( !NodeSetCreate( &VertSet, pBNS->num_atoms, nNumRad ) ) { - ret = BNS_OUT_OF_RAM; /* out of RAM */ - goto error_exit; - } - } - NodeSetFromRadEndpoints( &VertSet, j, pBD->RadEndpoints, pBD->nNumRadEndpoints); - /* do not allow any radical center be treated as a reachable atom: */ - RemoveFromNodeSet( &VertSet, j, vRadList, nNumRad ); - } - } - } - /* restore radical st_cap so that st_cap-st_flow=delta */ - for ( j = 0; j < nNumRad; j ++ ) { - i = vRadList[j]; - pBNS->vert[i].st_edge.cap += delta; - pBNS->tot_st_cap += delta; - } - /* merge lists that have common radical endpoints */ - /* defect: if vertex sets i and j do not intersect they will be compared 2 times */ - /* total up to nNumRad*(nNumRad-1)/2 calls to DoNodeSetsIntersect() */ - if ( nNumRad > 1 ) { - for ( i = 0; i < nNumRad; i ++ ) { - if ( vRadEqul[i] != i ) - continue; - do { - n = 0; - for ( j = i+1; j < nNumRad; j ++ ) { - if ( vRadEqul[j] != j ) - continue; - if ( DoNodeSetsIntersect( &VertSet, i, j) ) { - AddNodeSet2ToNodeSet1( &VertSet, i, j); - vRadEqul[j] = i; /* Set j was copied to set i; i < j */ - n ++; - } - } - } while( n ); - } - /* fill out pBD->RadEndpoints[] */ - for ( i = 0, n = 0; i < nNumRad; i ++ ) { - if ( i == vRadEqul[i] ) { - if ( !IsNodeSetEmpty( &VertSet, i) ) { - /* store equivalent radicals */ - for ( j = i+1; j < nNumRad; j ++ ) { - if (i == vRadEqul[j] ) { - pBD->RadEndpoints[n++] = vRadList[i]; - pBD->RadEndpoints[n++] = -vRadList[j]-2; /* equivalent radical, alvays not zero */ - } - } - /* store endpoints */ - n = AddNodesToRadEndpoints( &VertSet, i, pBD->RadEndpoints, vRadList[i], n, pBD->max_len_Pu_Pv ); - if ( n < 0 ) { - ret = BNS_RADICAL_ERR; /* pBD->RadEndpoints overflow */ - goto error_exit; - } - } else { - pBD->RadEndpoints[n++] = vRadList[i]; - pBD->RadEndpoints[n++] = -1; /* immobile radical, only one edge to add */ - } - } - } - pBD->nNumRadEndpoints = n; - NodeSetFree( &VertSet ); - } else - if ( nNumRad == 1 && !pBD->nNumRadEndpoints ) { - /* 2006-07-30: a single radical; no possible endpoint found */ - for ( i = 0, n = 0; i < nNumRad; i ++ ) { - pBD->RadEndpoints[n++] = vRadList[i]; - pBD->RadEndpoints[n++] = -1; /* immobile radical, only one edge to add */ - } - pBD->nNumRadEndpoints = n; - } - - if ( !ret && pBD->nNumRadEndpoints >= 2 ) { - /* already sorted by radical locations */ - num_new_edges = 0; - nNumRadicals = 0; - /************************************************************************** - * create new vertices (type=BNS_VERT_TYPE_TEMP) and edges with flow=cap=1 - * connecting the new vertices radical vertices - * - * - * Original structure: atom A is a radical center A==B--C*--D==E - * A*--B==C--D==E atoms C and E are reachable: A==B--C===D--E* - * - * Resultant temporary structure: - * A---B==C--D==E - * || / / - * || / / The additional new vertex (*) and its - * || / / 3 edges replace the radical with alternating - * || / / circuits that allow same bond changes - * || / / as moving the radical to atoms C or E. - * ||// "Double bonds" here have edge cap=1, flow=1 - * (*) "Single bonds" have edge cap=1, flow=0 - * - * The "equivalent radical centers" (which have at least one reachable atom - * in common) are connected to (*) with "double bonds" (edge cap=1, flow=1). - * Reachable non-radical atoms are connected by edges with cap=1, flow=0 - * After running BNS to find alt.path a "double bond" from (*) may move - * to another atom thus muving the radical. - * - * Number of additional (*) vertices = number of sets of - * "equivalent radical centers". - * Each such a set may include one or more radical centers. - * - * The radicals will be re-created in RemoveRadEndpoints() - ***************************************************************************/ - for ( i = 0; i < pBD->nNumRadEndpoints; i = j ) { - wRad = pBD->RadEndpoints[i]; - pRad = pBNS->vert + wRad; - delta = pRad->st_edge.cap - (pRad->st_edge.flow & EDGE_FLOW_ST_MASK); - if ( delta <= 0 ) { - delta = 1; - } - nNumEdges = 0; - for ( j = i; j < pBD->nNumRadEndpoints && wRad == pBD->RadEndpoints[j] ; j += 2 ) { - nNumEdges += (pBD->RadEndpoints[j+1] != -1); /* immobile radicals have one edge only */ - } - /* add new aux vertex to the radical atom/vertex making st_cap-st_flow=0 */ - /* in case of immobile radical there will be no additional eddges since nNumEdges=0 */ - vRad = bAddNewVertex( pBNS, wRad, delta, delta, nNumEdges+1, &nDots ); - if ( IS_BNS_ERROR( vRad ) ) { - ret = vRad; - goto error_exit; - } - pRad = pBNS->vert + vRad; - pBD->RadEdges[pBD->nNumRadEdges ++] = pRad->iedge[pRad->num_adj_edges-1]; - /* replace references to vertex wRad with vRad */ - for ( k = i, nNumEdges = 0; k < j; k += 2 ) { - pBD->RadEndpoints[k] = vRad; - } - nNumRadicals ++; - } - /* all vRad vertex indices should be in the range vFirstNewVertex...vFirstNewVertex+nNumRadicals-1 */ - /* connect new vertices to the radical endpoints thus replacing radicals with even-length alternating cycles */ - for ( i = 0; i < pBD->nNumRadEndpoints; i = j ) { - vRad = pBD->RadEndpoints[i]; - pRad = pBNS->vert + vRad; - for ( j = i; j < pBD->nNumRadEndpoints && vRad == pBD->RadEndpoints[j] ; j += 2 ) { - /* connect vew vertex pRad to radical endpoints */ - vEndp = pBD->RadEndpoints[j+1]; - if ( vEndp == -1 ) - continue; - if ( vEndp < 0 ) { - edge_flow = 1; - vEndp = -vEndp - 2; /* equivalent radical centers */ - } else { - edge_flow = 0; - } - pEndp = pBNS->vert + vEndp; - ret = AddNewEdge( pRad, pEndp, pBNS, 1, edge_flow ); - if ( IS_BNS_ERROR( ret ) ) { - goto error_exit; - } - pBD->RadEdges[pBD->nNumRadEdges ++] = ret; - } - } - pBD->nNumRadicals = nNumRadicals; - return nNumRadicals; /* done */ - } - return 0; /* nothing to do */ - -error_exit: - RemoveRadEndpoints( pBNS, pBD, NULL ); - NodeSetFree( &VertSet ); - return ret; - -} - - -#else - - - -/**********************************************************************************/ -int SetRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode ) -{ - return 0; -} -int RemoveRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at ) -{ - return 0; -} -int SetRadEndpoints2( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode ) -{ - return 0; -} -#endif - - - -/**********************************************************************************/ -/* Return value ret bits if not IS_BNS_ERROR(ret): - - ret & 1 => Success - ret & 2 => Bonds changed to Alt - (ret & ~3) >> 2 => nDelta: number of removed dots -*/ -int bExistsAltPath( BN_STRUCT *pBNS, BN_DATA *pBD, BN_AATG *pAATG, inp_ATOM *at, int num_atoms, - int nVertDoubleBond, int nVertSingleBond, int path_type ) -{ - ALT_PATH_CHANGES apc; - int ret, ret_val, bError, bSuccess, bChangeFlow=0, nDots, nDelta, bDoMarkChangedBonds = 1; - int bAdjustRadicals = 0; - AT_NUMB type; - BNS_FLOW_CHANGES fcd[4*BNS_MAX_NUM_FLOW_CHANGES+1]; - ENDPOINT_INFO eif; -#if ( KETO_ENOL_TAUT == 1 ) - ENDPOINT_INFO eif2; -#endif - - /* initialize */ - switch( path_type ) { - case ALT_PATH_MODE_TAUTOM: - /* Check for alt path allowing to move H and (-). Purpose: confirm possible tautomerism */ - type = BNS_VERT_TYPE_ENDPOINT; - bChangeFlow = BNS_EF_CHNG_RSTR; - if ( !at[nVertSingleBond].endpoint && - (!nGetEndpointInfo( at, nVertSingleBond, &eif ) || !eif.cDonor ) ) - return 0; - if ( !at[nVertDoubleBond].endpoint && - (!nGetEndpointInfo( at, nVertDoubleBond, &eif ) || !eif.cAcceptor ) ) - return 0; - break; - -#if ( KETO_ENOL_TAUT == 1 ) - case ALT_PATH_MODE_TAUTOM_KET: - /* Check for alt path allowing to move H and (-). Purpose: confirm possible tautomerism */ - type = BNS_VERT_TYPE_ENDPOINT; - bChangeFlow = BNS_EF_CHNG_RSTR; - - if ( !at[nVertSingleBond].endpoint && - (!nGetEndpointInfo_KET( at, nVertSingleBond, &eif ) || !eif.cDonor ) ) - return 0; - if ( !at[nVertDoubleBond].endpoint && - (!nGetEndpointInfo_KET( at, nVertDoubleBond, &eif2 ) || !eif2.cAcceptor ) ) - return 0; - /* - if ( eif.cKetoEnolCode + eif2.cKetoEnolCode != 3 ) - return 0; - */ - break; - -#endif - case ALT_PATH_MODE_CHARGE: - /* Find alt path allowing to move (+). Purpose: establish "charge groups", - mark alt. bonds due to (+) charge movement */ - type = BNS_VERT_TYPE_C_POINT; - bChangeFlow = (BNS_EF_CHNG_RSTR | BNS_EF_ALTR_BONDS); - break; - - case ALT_PATH_MODE_4_SALT: - case ALT_PATH_MODE_4_SALT2: - /* Find alt paths allowing to move (-) and H between "acidic oxygen atoms". - Purpose: mark alt bonds due to this "long range" tautomerism. */ - type = BNS_VERT_TYPE_ENDPOINT; - bChangeFlow = (BNS_EF_CHNG_RSTR | BNS_EF_ALTR_BONDS); - if ( !bIsBnsEndpoint( pBNS, nVertSingleBond ) /* !at[nVertSingleBond].endpoint*/ && - (!nGetEndpointInfo( at, nVertSingleBond, &eif ) || !eif.cDonor ) ) - return 0; - if ( !bIsBnsEndpoint( pBNS, nVertDoubleBond ) /* !at[nVertDoubleBond].endpoint*/ && - (!nGetEndpointInfo( at, nVertDoubleBond, &eif ) || !eif.cAcceptor ) ) - return 0; - memset( &apc, 0, sizeof(apc) ); - break; - - case ALT_PATH_MODE_REM2H_CHG: - bChangeFlow |= BNS_EF_ALTR_BONDS; /* fall through */ - case ALT_PATH_MODE_REM2H_TST: - bChangeFlow |= BNS_EF_CHNG_RSTR; - type = BNS_VERT_TYPE_ENDPOINT; - /* allow non-tautomeric donors or any tautomeric atom */ - if ( !bIsBnsEndpoint( pBNS, nVertSingleBond ) /* not linked to a t-group or the edge forbidden */&& - (!nGetEndpointInfo( at, nVertSingleBond, &eif ) || !eif.cDonor ) ) /* not a donor */ - return 0; - if ( !bIsBnsEndpoint( pBNS, nVertDoubleBond ) /* not connected to a t-group */ && - (!nGetEndpointInfo( at, nVertDoubleBond, &eif ) || !eif.cDonor ) ) - return 0; - memset( &apc, 0, sizeof(apc) ); - break; - - case ALT_PATH_MODE_ADD2H_CHG: - bChangeFlow |= BNS_EF_ALTR_BONDS; /* fall through */ - case ALT_PATH_MODE_ADD2H_TST: - bChangeFlow |= BNS_EF_CHNG_RSTR; - type = BNS_VERT_TYPE_ENDPOINT; - /* allow non-tautomeric acceptors or any tautomeric atom */ - if ( !bIsBnsEndpoint( pBNS, nVertSingleBond ) /* !at[nVertSingleBond].endpoint*/ && - (!nGetEndpointInfo( at, nVertSingleBond, &eif ) || !eif.cAcceptor ) ) - return 0; - if ( !bIsBnsEndpoint( pBNS, nVertDoubleBond ) /* !at[nVertSingleBond].endpoint*/ && - (!nGetEndpointInfo( at, nVertDoubleBond, &eif ) || !eif.cAcceptor ) ) - return 0; - break; - - case ALT_PATH_MODE_REM_PROTON: - /* alt path is between the t-group (nVertDoubleBond) and - the (+)-charge group (nVertSingleBond) */ - type = 0; - /*bDoMarkChangedBonds = 0;*/ - bChangeFlow = (BNS_EF_SAVE_ALL | BNS_EF_UPD_H_CHARGE) | BNS_EF_ALTR_NS; /* added BNS_EF_ALTR_NS: set non-stereo altern non-ring bonds 2004-07-02*/ - break; - default: - type = 0; - bChangeFlow = BNS_EF_CHNG_RSTR; - break; - } - - bError = 0; - bSuccess = 0; - nDelta = 0; - - ret = SetRadEndpoints2( pBNS, pBD, RAD_SRCH_NORM ); - if ( IS_BNS_ERROR( ret ) ) { - return ret; - } - - /* set BNS to check alt path */ - ret = bSetBnsToCheckAltPath( pBNS, nVertDoubleBond, nVertSingleBond, type, path_type, &apc, fcd, &nDots ); - switch( ret ) { - case BNS_CHK_ALTP_NO_ALTPATH: - ret = RemoveRadEndpoints( pBNS, pBD, NULL ); - return ret; - case BNS_CHK_ALTP_SAME_TGROUP: - bSuccess = 1; - goto reinit_BNS; - case BNS_CHK_ALTP_SAME_VERTEX: - ret = RemoveRadEndpoints( pBNS, pBD, NULL ); - return ret? ret : 1; /* very strange ... set a breakpoint here */ - case BNS_CHK_ALTP_SET_SUCCESS: - break; /* actually check the existence of the altpath */ - case BNS_CANT_SET_BOND: - goto reinit_BNS; - default: - ret_val = RemoveRadEndpoints( pBNS, pBD, NULL ); - if ( IS_BNS_ERROR( ret ) ) { - return ret; - } - return BNS_PROGRAM_ERR; - } - - bAdjustRadicals = ( (bChangeFlow & BNS_EF_UPD_RAD_ORI) && !(bChangeFlow & BNS_EF_RSTR_FLOW) ); - - /***************************************************************** - * nDots = 2 for ALT_PATH_CHARGE (checking moveable positive charges) - * Now nDots for ALT_PATH_TAUTOM or ALT_PATH_4_SALT can be greater - * because some of the bonds are effectively removed and dots - * (vertex st-caps) may be added - * -- to make sure there is no (+) charge on a tautomeric endpoint - * -- to fix positions of moveable tautomeric attachements - * (H and (-)-charges) at the ends of an alt path - */ - - /* run BNS */ - - ret = RunBalancedNetworkSearch( pBNS, pBD, bChangeFlow ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - } else - if ( ret > 0 ) { - if ( 2*ret >= nDots ) { - nDelta = 2*ret - nDots; /* non-zero means augmentation created another alt. path -- between radicals */ - if ( pAATG && pAATG->nMarkedAtom ) { - if ( pAATG->nAtTypeTotals && (bChangeFlow & BNS_EF_UPD_H_CHARGE) ) { - memset( pAATG->nMarkedAtom, 0, num_atoms*sizeof(pAATG->nMarkedAtom[0]) ); - /* mark atoms that have charge or H changed, check their input types (that is, before changes), - and subtract their input charge/H from nAtTypeTotals */ - SubtractOrChangeAtHChargeBNS( pBNS, at, num_atoms, pAATG->nAtTypeTotals, pAATG->nMarkedAtom, NULL, 1 ); - /* ZChange charges and/or H, update t_group_info, do not check types or change nAtTypeTotals */ - /* Atom types will be checked and nAtTypeTotals will be changed in - AddChangedAtHChargeBNS() later */ - SubtractOrChangeAtHChargeBNS( pBNS, at, num_atoms, NULL, NULL, pAATG->t_group_info, 0 ); - } else - if ( !pAATG->nAtTypeTotals ){ - bDoMarkChangedBonds = MarkAtomsAtTautGroups( pBNS, num_atoms, pAATG, nVertSingleBond, nVertDoubleBond ); - if ( bDoMarkChangedBonds < 0 ) { - bError = bDoMarkChangedBonds; - bDoMarkChangedBonds = 0; - } - } - } - if ( bDoMarkChangedBonds ) { - /* mark bonds that were changed to configure bond testing */ - ret_val = bSetBondsAfterCheckOneBond( pBNS, fcd, -1, at, num_atoms, bChangeFlow ); - if ( IS_BNS_ERROR( ret_val ) ) { - bError = ret_val; - } - /*ret = SetBondsRestoreBnStructFlow( pBNS, at, num_atoms, bChangeFlow );*/ - /* mark all other changed bonds */ - ret = SetBondsFromBnStructFlow( pBNS, at, num_atoms, bChangeFlow ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - } else - if ( !(ret & 1) && !(ret_val & 1) ) { - bSuccess = 1; - } else - if ( ((ret & 1) || (ret_val & 1)) && - (bChangeFlow & BNS_EF_ALTR_BONDS) || (bChangeFlow & BNS_EF_UPD_H_CHARGE) ) { - /* some bonds have been changed to alternating */ - bSuccess = 3; - } else { - bError = BNS_BOND_ERR; - } - if ( !bError && pAATG && pAATG->nMarkedAtom && (bChangeFlow & BNS_EF_UPD_H_CHARGE) ) { - /* Update radicals to avoid errors in atom type check in AddChangedAtHChargeBNS() */ - if ( bAdjustRadicals ) { - ret_val = RestoreRadicalsOnly( pBNS, pBD, at ); - if ( IS_BNS_ERROR( ret_val ) ) { - bError = ret_val; - } - } - /* Check atom types of marked atoms and add charge/H changes to nAtTypeTotals */ - /* Changing atoms were marked in the 1st call to SubtractOrChangeAtHChargeBNS(..., 1) above */ - AddChangedAtHChargeBNS( at, num_atoms, pAATG->nAtTypeTotals, pAATG->nMarkedAtom ); - if ( bChangeFlow & BNS_EF_CHNG_FLOW ) { - /* eliminate ambiguities in already changed flow: - replace (+)--N==(-) with (+)==N--(-) (both represent neutral N) */ - EliminatePlusMinusChargeAmbiguity( pBNS, num_atoms ); - } - } - } - } - ret = RestoreBnStructFlow( pBNS, bChangeFlow & BNS_EF_CHNG_RSTR); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - } - } -reinit_BNS: - /* --- reinitialize to repeat the calculations --- */ - bRestoreBnsAfterCheckAltPath( pBNS, &apc, bChangeFlow & BNS_EF_UPD_H_CHARGE ); - bRestoreFlowAfterCheckOneBond( pBNS, fcd ); - ret_val = RemoveRadEndpoints( pBNS, pBD, bAdjustRadicals? at : NULL ); - ReInitBnStructAltPaths( pBNS ); - return bError? bError : ret_val? ret_val : (bSuccess + 4*nDelta); -} - - - -/*****************************************************************************/ -BN_STRUCT* AllocateAndInitBnStruct( inp_ATOM *at, int num_atoms, int nMaxAddAtoms, int nMaxAddEdges, int max_altp, int *pNum_changed_bonds ) -{ - BN_STRUCT *pBNS = NULL; - BNS_VERTEX *vert; - - int neigh, num_changed_bonds=0; - U_CHAR bond_type, bond_mark; - - int i, j, k, n_edges, num_bonds, num_edges, f1, f2, edge_cap, edge_flow, st_cap, st_flow, flag_alt_bond; - int tot_st_cap, tot_st_flow; - int max_tg, max_edges, max_vertices, len_alt_path, max_iedges, num_altp; -#if ( BNS_RAD_SEARCH == 1 ) - int num_rad = 0; - - nMaxAddEdges += 1; -#endif -#if ( FIX_NUM_TG == 1 ) - max_tg = inchi_max( num_atoms / 2, 5); -#else - max_tg = num_atoms; -#endif - num_changed_bonds = 0; - - for ( i = 0, num_bonds = 0; i < num_atoms; i ++ ) { - num_bonds += at[i].valence; -#if ( BNS_RAD_SEARCH == 1 ) - num_rad += (at[i].radical == RADICAL_DOUBLET); -#endif - } - /* each atom has enough edges to belong to a tautomeric group + nMaxAddEdges */ - /* number of atoms is large enough to accommodate max. possible number of t-groups + nMaxAddAtoms */ - /* max_altp cannot be larger than BN_MAX_ALTP = 16 */ - num_edges = (num_bonds /= 2); - /* +1 for a super-tautomeric group */ - max_vertices = num_atoms + nMaxAddAtoms + max_tg + 1; - /* +max_tg for edges between t-groups and super-tautomeric group */ - max_edges = num_edges + (nMaxAddEdges + NUM_KINDS_OF_GROUPS)*max_vertices + max_tg; -#if ( BNS_RAD_SEARCH == 1 ) - if ( num_rad ) { - max_vertices *= 2; - max_edges *= 2; - } -#endif - max_iedges = 2*max_edges; - len_alt_path = max_vertices+iALTP_HDR_LEN+1; /* may overflow if an edge is traversed in 2 directions */ - - if ( !( pBNS = (BN_STRUCT *)inchi_calloc( 1, sizeof(BN_STRUCT)) ) || - !( pBNS->edge = (BNS_EDGE *)inchi_calloc( max_edges, sizeof(BNS_EDGE)) ) || - !( pBNS->vert = (BNS_VERTEX *)inchi_calloc( max_vertices,sizeof(BNS_VERTEX)) ) || - !( pBNS->iedge = (BNS_IEDGE *)inchi_calloc( max_iedges, sizeof(BNS_IEDGE)) ) ) { - return DeAllocateBnStruct( pBNS ); - } - /* alt path init */ - for ( num_altp = 0; num_altp < max_altp && num_altp < BN_MAX_ALTP; num_altp ++ ) { - if ( !( pBNS->altp[num_altp] = (BNS_ALT_PATH*)inchi_calloc( len_alt_path,sizeof(BNS_ALT_PATH))) ) { - return DeAllocateBnStruct( pBNS ); - } - ALTP_ALLOCATED_LEN(pBNS->altp[num_altp]) = len_alt_path; - pBNS->len_alt_path = len_alt_path; /* ??? duplication ??? */ - /* re-init */ - ALTP_DELTA(pBNS->altp[num_altp]) = 0; - ALTP_START_ATOM(pBNS->altp[num_altp]) = NO_VERTEX; - ALTP_END_ATOM(pBNS->altp[num_altp]) = NO_VERTEX; - ALTP_PATH_LEN(pBNS->altp[num_altp]) = 0; - } - pBNS->alt_path = NULL; - pBNS->num_altp = 0; - pBNS->max_altp = num_altp; - - - /* fill vertices (no connectivity) */ - pBNS->vert[0].iedge = pBNS->iedge; - for ( i = 0; i < num_atoms; i ++ ) { - k = pBNS->vert[i].max_adj_edges = at[i].valence + (nMaxAddEdges + NUM_KINDS_OF_GROUPS); - pBNS->vert[i+1].iedge = pBNS->vert[i].iedge + k; - } - pBNS->num_atoms = num_atoms; /* number of real atoms */ - pBNS->num_added_atoms = 0; - pBNS->num_t_groups = 0; /* number of added t-groups */ - pBNS->num_c_groups = 0; - pBNS->nMaxAddAtoms = nMaxAddAtoms; - pBNS->nMaxAddEdges = nMaxAddEdges; - - pBNS->num_vertices = num_atoms; /* current number of vertices, a sum of - pBNS->num_atoms - pBNS->num_t_groups - pBNS->num_added_atoms - */ - pBNS->max_vertices = max_vertices; - - - pBNS->num_bonds = num_bonds; /* number of real edges (bonds) */ - pBNS->max_edges = max_edges; - pBNS->max_iedges = max_iedges; - - /* - To remove t-groups and added atoms: - In atoms i = 0..pBNS->num_atoms-1 - pBNS->vert[i].num_adj_edges = pBNS->vert[i].max_adj_edges - pBNS->nMaxAddEdges - NUM_KINDS_OF_GROUPS; - pBNS->num_vertices = pBNS->num_atoms; - pBNS->num_edges = pBNS->num_bonds; - pBNS->num_added_atoms = 0; - pBNS->num_t_groups = 0; - pBNS->num_added_edges = 0; - - ALTP_DELTA(pBNS->alt_path) = 0; - ALTP_START_ATOM(pBNS->alt_path) = NO_VERTEX; - ALTP_END_ATOM(pBNS->alt_path) = NO_VERTEX; - ALTP_PATH_LEN(pBNS->alt_path) = 0; - - */ - - - /* fill edges and connectivity */ - tot_st_cap = tot_st_flow = 0; - for ( i = 0, n_edges = 0; i < num_atoms; i ++ ) { - vert = &pBNS->vert[i]; - st_cap = 0; - st_flow = 0; - flag_alt_bond = 0; - for ( j = 0; j < at[i].valence; j ++ ) { - neigh = at[i].neighbor[j]; - /* find this bond at the neighbor */ - for ( k = 0; k < at[neigh].valence; k ++ ) { - if ( at[neigh].neighbor[k] == i ) { - break; - } - } - bond_type = (at[i].bond_type[j] & BOND_TYPE_MASK); - bond_mark = (at[i].bond_type[j] & ~BOND_TYPE_MASK); - if ( bond_type != BOND_SINGLE && bond_type != BOND_DOUBLE && - bond_type != BOND_TRIPLE /*&& bond_type != BOND_ALTERN*/ ) { - /* make Unknown or Alternating bonds single */ - bond_type = 1; - at[i].bond_type[j] = bond_mark | bond_type; - num_changed_bonds ++; - } - if ( neigh > i ) { - /* this is the first time we encounter this bond */ - f1 = MAX_AT_FLOW(at[i]); - f2 = MAX_AT_FLOW(at[neigh]); - edge_flow = bond_type-1; - if ( edge_flow > MAX_BOND_EDGE_CAP ) { - flag_alt_bond ++; - edge_flow = 0; /* BNS will determine flows (that is, bonds) */ - edge_cap = AROM_BOND_EDGE_CAP; - } else { -#if ( 0 && KETO_ENOL_TAUT == 1 ) /* ????? */ - edge_cap = inchi_max(f1, f2); -#else - edge_cap = inchi_min(f1, f2); -#endif - edge_cap = inchi_min(edge_cap, MAX_BOND_EDGE_CAP); /* max capacity = 2 means up to triple bond */ - } - pBNS->edge[n_edges].neighbor1 = (AT_NUMB)i; - pBNS->edge[n_edges].neighbor12 = (AT_NUMB)(i ^ neigh); - pBNS->edge[n_edges].flow = - pBNS->edge[n_edges].flow0 = edge_flow; - pBNS->edge[n_edges].cap = - pBNS->edge[n_edges].cap0 = edge_cap; - pBNS->edge[n_edges].neigh_ord[0] = j; - pBNS->edge[n_edges].neigh_ord[1] = k; - pBNS->edge[n_edges].pass = 0; - pBNS->edge[n_edges].forbidden = 0; - - vert->iedge[j] = pBNS->vert[neigh].iedge[k] = n_edges ++; - } else { - /* this is the second time we encounter this bond. It was stored at */ - int iedge = pBNS->vert[neigh].iedge[k]; - edge_cap = pBNS->edge[iedge].cap; - edge_flow = pBNS->edge[iedge].flow; - } - st_flow += edge_flow; - st_cap += edge_cap; - } - vert->num_adj_edges = j; - vert->st_edge.cap = - vert->st_edge.cap0 = MAX_AT_FLOW(at[i]); - vert->st_edge.flow = - vert->st_edge.flow0 = st_flow; - vert->type = BNS_VERT_TYPE_ATOM; - tot_st_cap += vert->st_edge.cap; - tot_st_flow += vert->st_edge.flow; - } - *pNum_changed_bonds = num_changed_bonds/2; - - pBNS->num_edges = n_edges; /* number of edges */ - pBNS->num_added_edges = 0; - - pBNS->tot_st_cap = tot_st_cap; - pBNS->tot_st_flow = tot_st_flow; - - return pBNS; -} - - - -/*****************************************************************************/ -BN_STRUCT* DeAllocateBnStruct( BN_STRUCT *pBNS ) -{ - int i; - if ( pBNS ) { - if ( pBNS->edge ) { - inchi_free( pBNS->edge ); - } - for ( i = 0; i < pBNS->max_altp && i < BN_MAX_ALTP ; i ++ ) { - if ( pBNS->altp[i] ) { - inchi_free( pBNS->altp[i] ); - } - } - if ( pBNS->vert ) { - if ( pBNS->vert[0].iedge ) { - inchi_free( pBNS->vert[0].iedge ); - } - inchi_free( pBNS->vert ); - } - inchi_free( pBNS ); - } - return NULL; -} - - - -/*****************************************************************************/ -int ReInitBnStructAltPaths( BN_STRUCT *pBNS ) -{ - int i; - for ( i = 0; i < pBNS->max_altp && i < BN_MAX_ALTP; i ++ ) { - if ( pBNS->altp[i] ) { - ALTP_DELTA(pBNS->altp[i]) = 0; - ALTP_PATH_LEN(pBNS->altp[i]) = 0; - ALTP_START_ATOM(pBNS->altp[i]) = NO_VERTEX; - ALTP_END_ATOM(pBNS->altp[i]) = NO_VERTEX; - } - } - pBNS->alt_path = NULL; - pBNS->num_altp = 0; - return i; -} - - - -/*****************************************************************************/ -int ReInitBnStructAddGroups( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, T_GROUP_INFO *tgi, C_GROUP_INFO *cgi ) -{ - int ret; - /* strip all t-groups and c-groups */ - ret = ReInitBnStruct( pBNS, at, num_atoms, 0 ); - if ( ret ) { - ret = BNS_REINIT_ERR; - goto exit_function; - } -/*#if ( MOVE_CHARGES == 1 )*/ - if ( *pBNS->pbTautFlags & TG_FLAG_MOVE_POS_CHARGES ) { - /* add c-groups */ - ret = AddCGroups2BnStruct( pBNS, at, num_atoms, cgi ); - if ( IS_BNS_ERROR( ret ) ) { - goto exit_function; - } - } -/*#endif*/ - /* add t-groups */ - ret = AddTGroups2BnStruct( pBNS, at, num_atoms, tgi ); - if ( IS_BNS_ERROR( ret ) ) { - goto exit_function; - } -exit_function: - return ret; -} - - - -/*****************************************************************************/ -int ReInitBnStruct( BN_STRUCT *pBNS, inp_ATOM *at, int num_at, int bRemoveGroupsFromAtoms ) -{ - int i, vfict, kfict, iedgefict, endpoint, centerpoint, iedge, k; - int ret = 0; - if ( pBNS ) { - if ( pBNS->vert && pBNS->edge ) { - /* debug */ - for ( k = 0, i = 0; k < pBNS->num_edges; k ++ ) { - if ( pBNS->edge[k].pass ) { - i ++; - } - } - ret += i * 100; - /* restore flow and cap on edges to vertices connected to fictitious atoms */ - for ( vfict = pBNS->num_atoms; vfict < pBNS->num_vertices; vfict ++ ) { - for ( kfict = 0; kfict < pBNS->vert[vfict].num_adj_edges; kfict ++ ) { - iedgefict = pBNS->vert[vfict].iedge[kfict]; /* fictitious edge to the endpoint */ - endpoint = pBNS->edge[iedgefict].neighbor12 ^ vfict; /* the endpoint */ - /* to simlify restore cap and flow in ALL edges to the endpoint */ - if ( bRemoveGroupsFromAtoms && endpoint < num_at ) { - at[endpoint].c_point = 0; - at[endpoint].endpoint = 0; - } - for ( k = 0; k < pBNS->vert[endpoint].num_adj_edges; k ++ ) { - iedge = pBNS->vert[endpoint].iedge[k]; /* edge to endpoint */ - centerpoint = pBNS->edge[iedge].neighbor12 ^ endpoint; - pBNS->edge[iedge].cap = pBNS->edge[iedge].cap0; - pBNS->edge[iedge].flow = pBNS->edge[iedge].flow0; - pBNS->edge[iedge].pass = 0; -#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) - pBNS->edge[iedge].forbidden &= pBNS->edge_forbidden_mask; -#endif - pBNS->vert[centerpoint].st_edge.cap = pBNS->vert[centerpoint].st_edge.cap0; - pBNS->vert[centerpoint].st_edge.flow = pBNS->vert[centerpoint].st_edge.flow0; - } - pBNS->vert[endpoint].st_edge.cap = pBNS->vert[endpoint].st_edge.cap0; - pBNS->vert[endpoint].st_edge.flow = pBNS->vert[endpoint].st_edge.flow0; - pBNS->vert[endpoint].type &= BNS_VERT_TYPE_ATOM; - } - } - /* reset number of neighbors */ - if ( pBNS->num_edges > pBNS->num_bonds ) { - for ( i = 0; i < pBNS->num_atoms; i ++ ) { - pBNS->vert[i].num_adj_edges = - pBNS->vert[i].max_adj_edges - pBNS->nMaxAddEdges - NUM_KINDS_OF_GROUPS; - } - } - } else { - ret += 2; - } - if ( !pBNS->edge ) { - ret += 4; - } - if ( !pBNS->iedge ) { - ret += 8; - } - - ReInitBnStructAltPaths( pBNS ); - - pBNS->num_vertices = pBNS->num_atoms; - pBNS->num_edges = pBNS->num_bonds; - pBNS->num_added_atoms = 0; - pBNS->num_t_groups = 0; - pBNS->num_c_groups = 0; - pBNS->num_added_edges = 0; - - } else { - ret += 1; - } - - return ret; -} - - - -/*****************************************************************************/ -int CompTGroupNumber( const void *tg1, const void *tg2 ) -{ - return (int)((const T_GROUP *)tg1)->nGroupNumber - (int)((const T_GROUP *)tg2)->nGroupNumber; -} - - - -/*****************************************************************************/ -int CompCGroupNumber( const void *cg1, const void *cg2 ) -{ - return (int)((const C_GROUP *)cg1)->nGroupNumber - (int)((const C_GROUP *)cg2)->nGroupNumber; -} - - - -/*****************************************************************************/ -int AddTGroups2BnStruct( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, T_GROUP_INFO *tgi ) -{ - int ret = 0; - /* ret = ReInitBnStruct( pBNS ); */ - if ( tgi && tgi->num_t_groups && tgi->t_group ) { - int i, k, endpoint, centerpoint, fictpoint; - int num_tg = tgi->num_t_groups; - int num_edges = pBNS->num_edges; - int num_vertices = pBNS->num_vertices; - BNS_VERTEX *vert_ficpoint, *ver_ficpont_prev; /* fictitious vertex describing t-group */ - BNS_VERTEX *vert_endpoint; - BNS_EDGE *edge; /* edge between that vertex and the tautomeric endpoint */ - int nMaxTGroupNumber = 0; - ENDPOINT_INFO eif; - - /* Debug: check overflow */ - if ( num_vertices + num_tg >= pBNS->max_vertices ) { - return BNS_VERT_EDGE_OVFL; - } - /* find the largest t-group ID */ - for ( i = 0; i < num_tg; i ++ ) { - if ( tgi->t_group[i].nGroupNumber > nMaxTGroupNumber ) { - nMaxTGroupNumber = tgi->t_group[i].nGroupNumber; - } - } - /* since t-group IDs may be not contiguous, clear all vertices that will be added. - all-zeroes-vertex will be ignored by the BNS - */ - memset( pBNS->vert+num_vertices, 0, nMaxTGroupNumber*sizeof(pBNS->vert[0]) ); - /* Make sure the last t-group has the largest t-group ID: - this is necessary to correctly add new edges and vertices for testing augmenting paths - */ -#if ( bRELEASE_VERSION != 1 ) - insertions_sort( tgi->t_group, num_tg, sizeof(tgi->t_group[0]), CompTGroupNumber ); - for ( i = 1; i < num_tg; i ++ ) { - if ( 1 != tgi->t_group[i].nGroupNumber - tgi->t_group[i-1].nGroupNumber ) { - return BNS_BOND_ERR; - } - } -#else - if ( nMaxTGroupNumber != tgi->t_group[num_tg-1].nGroupNumber ) { - insertions_sort( tgi->t_group, num_tg, sizeof(tgi->t_group[0]), CompTGroupNumber ); - } -#endif - /* initialize new fictitious vertices */ - ver_ficpont_prev = pBNS->vert+num_vertices - 1; - - for ( i = 0; i < num_tg; i ++, ver_ficpont_prev = vert_ficpoint ) { - /* - vert_ficpoint-1 is the last vertex; - vert_ficpoint is the vertex that is being added - Note: nGroupNumber are not contiguous - */ - vert_ficpoint = pBNS->vert+num_vertices + tgi->t_group[i].nGroupNumber - 1; - vert_ficpoint->iedge = ver_ficpont_prev->iedge + ver_ficpont_prev->max_adj_edges; - vert_ficpoint->max_adj_edges = tgi->t_group[i].nNumEndpoints+BNS_ADD_EDGES+BNS_ADD_SUPER_TGROUP; - vert_ficpoint->num_adj_edges = 0; - vert_ficpoint->st_edge.flow = vert_ficpoint->st_edge.flow0 = 0; - vert_ficpoint->st_edge.cap = vert_ficpoint->st_edge.cap0 = 0; - vert_ficpoint->type = BNS_VERT_TYPE_TGROUP; - } - - for ( endpoint = 0; endpoint < num_atoms; endpoint ++ ) { - if ( !at[endpoint].endpoint ) - continue; - fictpoint = at[endpoint].endpoint + num_vertices - 1; - vert_ficpoint = pBNS->vert + fictpoint; - vert_endpoint = pBNS->vert + endpoint; - /* Debug: check overflow */ - if ( fictpoint >= pBNS->max_vertices || - num_edges >= pBNS->max_edges || - vert_ficpoint->num_adj_edges >= vert_ficpoint->max_adj_edges || - vert_endpoint->num_adj_edges >= vert_endpoint->max_adj_edges ) { - ret = BNS_VERT_EDGE_OVFL; - break; - } - /* obtain donor/acceptor info */ - if ( !nGetEndpointInfo( at, endpoint, &eif ) ) { -#if ( KETO_ENOL_TAUT == 1 ) - if ( !((tgi->bTautFlags & TG_FLAG_KETO_ENOL_TAUT ) && - nGetEndpointInfo_KET( at, endpoint, &eif )) ) -#endif - { - ret = BNS_BOND_ERR; - break; - } - } - - vert_endpoint->type |= BNS_VERT_TYPE_ENDPOINT; - - /* set capacity = 1 to the edges from the endpoint to the centerpoint(s) */ - for ( k = 0; k < vert_endpoint->num_adj_edges; k ++ ) { - int iedge = vert_endpoint->iedge[k]; - if ( !pBNS->edge[iedge].cap ) { - /* single bond, possibly between endpoint and centerpoint */ - centerpoint = (pBNS->edge[iedge].neighbor12 ^ endpoint); - if ( centerpoint < pBNS->num_atoms && - pBNS->vert[centerpoint].st_edge.cap >= 1 ) { - int bond_type = (at[endpoint].bond_type[k] & BOND_TYPE_MASK); - if (bond_type == BOND_TAUTOM || - bond_type == BOND_ALTERN || - bond_type == BOND_ALT12NS || - bond_type == BOND_SINGLE ) { - pBNS->edge[iedge].cap = 1; - } - } - } - } - /* create a new edge connecting endpoint to the new fictitious t-group vertex vert_ficpoint */ - edge = pBNS->edge + num_edges; - edge->cap = 1; - edge->flow = 0; - edge->pass = 0; -#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) - edge->forbidden &= pBNS->edge_forbidden_mask; -#endif - /* later include case when the charge change allows the endpoint to become tautomeric */ - /* mark endoint having moveable H atom with flow=1 */ - - /* -- old "no charges" version -- */ - /* if (at[endpoint].chem_bonds_valence == at[endpoint].valence) */ - /* -- the following line takes charges into account -- */ - if ( eif.cDonor ) /* means the endpoint has an H-atom to donate */ - { - /* increment edge flow */ - edge->flow ++; - /* increment one vertex st-flow & cap */ - vert_ficpoint->st_edge.flow ++; - vert_ficpoint->st_edge.cap ++; - /* increment another vertex st-flow & cap */ - vert_endpoint->st_edge.flow ++; - vert_endpoint->st_edge.cap ++; - } - /* connect edge to endpoint and fictpoint and increment the counters of neighbors and edges */ - edge->neighbor1 = endpoint; /* the smallest out of v1=endopoint and v2=num_vertices */ - edge->neighbor12 = endpoint ^ fictpoint; /* v1 ^ v2 */ - vert_endpoint->iedge[vert_endpoint->num_adj_edges] = num_edges; - vert_ficpoint->iedge[vert_ficpoint->num_adj_edges] = num_edges ++; - edge->neigh_ord[0] = vert_endpoint->num_adj_edges ++; - edge->neigh_ord[1] = vert_ficpoint->num_adj_edges ++; - edge->cap0 = edge->cap; - edge->flow0 = edge->flow; - } - - pBNS->num_edges = num_edges; - pBNS->num_vertices += nMaxTGroupNumber; - pBNS->num_t_groups = num_tg; - - } - return ret; -} - - - -/*****************************************************************************/ -/*#if ( MOVE_CHARGES == 1 )*/ /* { */ - - - -/*****************************************************************************/ -int AddCGroups2BnStruct( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, C_GROUP_INFO *cgi ) -{ - int ret = 0; - /* ret = ReInitBnStruct( pBNS ); */ - if ( cgi && cgi->num_c_groups && cgi->c_group ) { - int i, k, c_point, centerpoint, fictpoint; - int num_cg = cgi->num_c_groups; - int num_edges = pBNS->num_edges; - int num_vertices = pBNS->num_vertices; - BNS_VERTEX *vert_ficpoint, *ver_ficpont_prev; /* fictitious vertex describing charge c-group */ - BNS_VERTEX *vertex_cpoint; - BNS_EDGE *edge; /* edge between that vertex and the tautomeric c_point */ - int nMaxCGroupNumber = 0; - - /* Debug: check overflow */ - if ( num_vertices + num_cg >= pBNS->max_vertices ) { - return BNS_VERT_EDGE_OVFL; - } - /* find the largest t-group ID */ - for ( i = 0; i < num_cg; i ++ ) { - if ( cgi->c_group[i].nGroupNumber > nMaxCGroupNumber ) { - nMaxCGroupNumber = cgi->c_group[i].nGroupNumber; - } - } - /* since t-group IDs may be not contiguous, clear all vertices that will be added. - all-zeroes-vertex will be ignored by the BNS - */ - memset( pBNS->vert+num_vertices, 0, nMaxCGroupNumber*sizeof(pBNS->vert[0]) ); - /* Make sure the last t-group has the largest t-group ID: - this is necessary to correctly add new edges and vertices for testing augmenting paths - */ -#if ( bRELEASE_VERSION != 1 ) - insertions_sort( cgi->c_group, num_cg, sizeof(cgi->c_group[0]), CompCGroupNumber ); - for ( i = 1; i < num_cg; i ++ ) { - if ( 1 != cgi->c_group[i].nGroupNumber - cgi->c_group[i-1].nGroupNumber ) { - return BNS_BOND_ERR; - } - } -#else - if ( nMaxCGroupNumber != cgi->c_group[num_cg-1].nGroupNumber ) { - insertions_sort( cgi->c_group, num_cg, sizeof(cgi->c_group[0]), CompCGroupNumber ); - } -#endif - /**************************************/ - /* initialize new fictitious vertices */ - /* representing c-point groups */ - /**************************************/ - ver_ficpont_prev = pBNS->vert+num_vertices - 1; - - for ( i = 0; i < num_cg; i ++, ver_ficpont_prev = vert_ficpoint ) { - /* - vert_ficpoint-1 is the last vertex; - vert_ficpoint is the being added vertex - Note: nGroupNumber are not contiguous - */ - vert_ficpoint = pBNS->vert+num_vertices + cgi->c_group[i].nGroupNumber - 1; - vert_ficpoint->iedge = ver_ficpont_prev->iedge + ver_ficpont_prev->max_adj_edges; - vert_ficpoint->max_adj_edges = cgi->c_group[i].num_CPoints+BNS_ADD_EDGES; - vert_ficpoint->num_adj_edges = 0; - vert_ficpoint->st_edge.flow = vert_ficpoint->st_edge.flow0 = 0; - vert_ficpoint->st_edge.cap = vert_ficpoint->st_edge.cap0 = 0; - vert_ficpoint->type = BNS_VERT_TYPE_C_GROUP; - } - - /************************************************/ - /* connect c-points to the fictitious vertices */ - /* representing c-point groups; set caps, flows */ - /************************************************/ - for ( c_point = 0; c_point < num_atoms; c_point ++ ) { - if ( !at[c_point].c_point ) - continue; - fictpoint = at[c_point].c_point + num_vertices - 1; /* c-group vertex index */ - vert_ficpoint = pBNS->vert + fictpoint; /* c-group vertex */ - vertex_cpoint = pBNS->vert + c_point; /* c_point vertex */ - /* Debug: check overflow */ - if ( fictpoint >= pBNS->max_vertices || - num_edges >= pBNS->max_edges || - vert_ficpoint->num_adj_edges >= vert_ficpoint->max_adj_edges || - vertex_cpoint->num_adj_edges >= vertex_cpoint->max_adj_edges ) { - ret = BNS_VERT_EDGE_OVFL; - break; - } - vertex_cpoint->type |= BNS_VERT_TYPE_C_POINT; -#if ( FIX_CPOINT_BOND_CAP != 1 ) /* { */ - /* set capacity = 1 to the edges from the c_point to the centerpoint(s) */ - /* if their current capacity is zero */ - /* the centerpoint is any adjacent atom that is adjacent to a multiple bond */ - for ( k = 0; k < vertex_cpoint->num_adj_edges; k ++ ) { - int iedge = vertex_cpoint->iedge[k]; - if ( !pBNS->edge[iedge].cap ) { - /* single bond, possibly between c_point and centerpoint */ - centerpoint = (pBNS->edge[iedge].neighbor12 ^ c_point); - if ( centerpoint < pBNS->num_atoms && - pBNS->vert[centerpoint].st_edge.cap >= 1 ) { - int bond_type = (at[c_point].bond_type[k] & BOND_TYPE_MASK); - if ( bond_type == BOND_TAUTOM || - bond_type == BOND_ALTERN || - bond_type == BOND_SINGLE ) { - pBNS->edge[iedge].cap = 1; - } - } - } - } -#endif /* } FIX_CPOINT_BOND_CAP */ - /* create a new edge connecting c_point to the new fictitious c-group vertex vert_ficpoint */ - edge = pBNS->edge + num_edges; - edge->cap = 1; - edge->flow = 0; - edge->pass = 0; -#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) - edge->forbidden &= pBNS->edge_forbidden_mask; -#endif - /* mark edge to c-point having NO moveable charge with flow=1 */ - if ( !CHARGED_CPOINT(at,c_point) ) { - /* increment edge flow */ - edge->flow ++; - /* increment c-group vertex st-flow & cap */ - vert_ficpoint->st_edge.flow ++; - vert_ficpoint->st_edge.cap ++; - /* increment c-point vertex st-flow & cap */ - vertex_cpoint->st_edge.flow ++; - vertex_cpoint->st_edge.cap ++; - } -#if ( FIX_CPOINT_BOND_CAP == 1 ) /* { */ - /* set capacity = 1 to the edges from the c_point to the centerpoint(s) */ - /* if their current capacity is zero */ - /* the centerpoint is any adjacent atom that is adjacent to a multiple bond */ - for ( k = 0; k < vertex_cpoint->num_adj_edges; k ++ ) { - int iedge = vertex_cpoint->iedge[k]; - VertexFlow nNewCap = vertex_cpoint->st_edge.cap; - centerpoint = (pBNS->edge[iedge].neighbor12 ^ c_point); - if ( !pBNS->edge[iedge].cap ) { - /* single bond, possibly between c_point and centerpoint */ - if ( centerpoint < pBNS->num_atoms && - pBNS->vert[centerpoint].st_edge.cap >= 1 ) { - nNewCap = inchi_min( pBNS->vert[centerpoint].st_edge.cap, nNewCap ); - nNewCap = inchi_min( nNewCap, MAX_BOND_EDGE_CAP ); - pBNS->edge[iedge].cap = nNewCap; - } -#if ( FIX_CPOINT_BOND_CAP2 == 1 ) /* multiple bond */ - else - if ( centerpoint < pBNS->num_atoms && - edge->flow && pBNS->edge[iedge].cap < MAX_BOND_EDGE_CAP ) { - pBNS->edge[iedge].cap ++; - } -#endif - } - } -#endif /* } FIX_CPOINT_BOND_CAP */ - /* connect edge to c_point and fictpoint and increment the counters of neighbors and edges */ - edge->neighbor1 = c_point; /* the smallest out of v1=endopoint and v2=num_vertices */ - edge->neighbor12 = c_point ^ fictpoint; /* v1 ^ v2 */ - vertex_cpoint->iedge[vertex_cpoint->num_adj_edges] = num_edges; - vert_ficpoint->iedge[vert_ficpoint->num_adj_edges] = num_edges ++; - edge->neigh_ord[0] = vertex_cpoint->num_adj_edges ++; - edge->neigh_ord[1] = vert_ficpoint->num_adj_edges ++; - edge->cap0 = edge->cap; - edge->flow0 = edge->flow; - - } - - pBNS->num_edges = num_edges; - pBNS->num_vertices += nMaxCGroupNumber; - pBNS->num_c_groups = num_cg; - - } - return ret; -} -/*#endif*/ /* } MOVE_CHARGES == 1 */ - - - - -/*****************************************************************************/ -void ClearAllBnDataVertices( Vertex *v, Vertex value, int size ) -{ - int i; - for ( i = 0; i < size; i ++ ) { - v[i] = value; - } -} - - - -/*****************************************************************************/ -void ClearAllBnDataEdges( Edge *e, Vertex value, int size ) -{ - int i; - for ( i = 0; i < size; i ++ ) { - e[i][0] = value; - } -} - - -/*****************************************************************************/ -BN_DATA *DeAllocateBnData( BN_DATA *pBD ) -{ - if ( pBD ) { - if ( pBD->BasePtr ) - inchi_free( pBD->BasePtr ); - if ( pBD->SwitchEdge ) - inchi_free( pBD->SwitchEdge ); - if ( pBD->Tree ) - inchi_free( pBD->Tree ); - if ( pBD->ScanQ ) - inchi_free( pBD->ScanQ ); - if ( pBD->Pu ) - inchi_free( pBD->Pu ); - if ( pBD->Pv ) - inchi_free( pBD->Pv ); -#if ( BNS_RAD_SEARCH == 1 ) - if ( pBD->RadEndpoints ) { - inchi_free( pBD->RadEndpoints ); - } - if ( pBD->RadEdges ) { - inchi_free( pBD->RadEdges ); - } -#endif - inchi_free( pBD ); - } - return NULL; -} - - -/*****************************************************************************/ -BN_DATA *AllocateAndInitBnData( int max_num_vertices ) -{ - BN_DATA *pBD = NULL; - int max_len_Pu_Pv; - max_num_vertices = 2*max_num_vertices+2; - max_len_Pu_Pv = max_num_vertices/2+1; - max_len_Pu_Pv += max_len_Pu_Pv % 2; /* even length */ - if ( !(pBD = (BN_DATA *) inchi_calloc( 1, sizeof(BN_DATA) ) ) || - !(pBD->BasePtr = (Vertex *) inchi_calloc( max_num_vertices, sizeof(Vertex) ) ) || - !(pBD->SwitchEdge = (Edge *) inchi_calloc( max_num_vertices, sizeof(Edge ) ) ) || - !(pBD->Tree = (S_CHAR *) inchi_calloc( max_num_vertices, sizeof(S_CHAR) ) ) || - !(pBD->ScanQ = (Vertex *) inchi_calloc( max_num_vertices, sizeof(Vertex) ) ) || - !(pBD->Pu = (Vertex *) inchi_calloc( max_len_Pu_Pv, sizeof(Vertex) ) ) || -#if ( BNS_RAD_SEARCH == 1 ) - !(pBD->RadEndpoints = (Vertex *) inchi_calloc( max_len_Pu_Pv, sizeof(Vertex) ) ) || - !(pBD->RadEdges = (EdgeIndex*) inchi_calloc( max_len_Pu_Pv, sizeof(EdgeIndex) ) ) || -#endif - !(pBD->Pv = (Vertex *) inchi_calloc( max_len_Pu_Pv, sizeof(Vertex) ) ) - ) { - pBD = DeAllocateBnData( pBD ); - } else { - /* Initialize data */ - ClearAllBnDataEdges(pBD->SwitchEdge, NO_VERTEX, max_num_vertices); - ClearAllBnDataVertices(pBD->BasePtr, NO_VERTEX, max_num_vertices); - memset(pBD->Tree, TREE_NOT_IN_M, max_num_vertices); - pBD->QSize = -1; - pBD->max_len_Pu_Pv = max_len_Pu_Pv; - pBD->max_num_vertices = max_num_vertices; -#if ( BNS_RAD_SEARCH == 1 ) - pBD->nNumRadEndpoints = 0; -#endif - } - return pBD; -} - - - -/*****************************************************************************/ -int ReInitBnData( BN_DATA *pBD ) -{ - int i, ret = 0; - Vertex u, v; - if ( pBD ) { - if ( !pBD->ScanQ ) { - ret += 2; - } - if ( !pBD->BasePtr ) { - ret += 4; - } - if ( !pBD->SwitchEdge ) { - ret += 8; - } - if ( !pBD->Tree ) { - ret += 16; - } - if ( !ret ) { - for ( i = 0; i <= pBD->QSize; i ++ ) { - u = pBD->ScanQ[i]; - v = prim(u); - pBD->BasePtr[u] = - pBD->BasePtr[v] = - pBD->SwitchEdge_Vert1(u) = - pBD->SwitchEdge_Vert1(v) = NO_VERTEX; - pBD->Tree[u] = - pBD->Tree[v] = TREE_NOT_IN_M; - } - } - pBD->QSize = -1; - if ( !pBD->Pu ) { - ret += 32; - } - if ( !pBD->Pv ) { - ret += 64; - } - } else { - ret += 1; - } - - return ret; -} - - - -/*****************************************************************************/ -int GetVertexDegree( BN_STRUCT* pBNS, Vertex v ) -{ - int i = v / 2 - 1; - if ( i >= 0 ) { - if ( pBNS->vert[i].st_edge.cap > 0 ) { - return pBNS->vert[i].num_adj_edges+1; /* add 1 neighbor for s or t */ - } else { - return 0; /* since the edge s-v has zero capacity, we ignore vertex v */ - } - } else { - return pBNS->num_vertices; - } -} - - - -/*****************************************************************************/ -Vertex Get2ndEdgeVertex( BN_STRUCT* pBNS, Edge uv ) -{ - /* - Vertex ret; - */ - if ( uv[1] >= 0 ) { - /* -- debug -- - if ( uv[1] > pBNS->num_edges || uv[0] > 2*pBNS->num_vertices+3 ) { - int stop = 1; - } - ret = ((uv[0]-2) ^ (2*pBNS->edge[uv[1]].neighbor12+1)) + 2; - if ( ret > 2*pBNS->num_vertices+3 ) { - int stop = 1; - } - return ret; - -- end debug -- */ - return ((uv[0]-2) ^ (2*pBNS->edge[uv[1]].neighbor12+1)) + 2; - /*short u = uv[0]-FIRST_INDX; */ - /*short t = 2*(((u / 2 - 1) ^ pBNS->edge[uv[1]].neighbor12) + 1) + ((u+1) & 1) + FIRST_INDX; */ - /*return t; */ - } - if ( uv[0] <= 1 ) - return -(1 + uv[1]); /* vertex1 is s or t, return x or y */ - else - return uv[0] % 2; /* vertex1 is x or y, return s or t; never happens? -- NSC 3737, 7634,... */ -} - - - -/*****************************************************************************/ -Vertex GetVertexNeighbor( BN_STRUCT* pBNS, Vertex v, int neigh, EdgeIndex *iedge ) -{ - /* neigh = 0 => the neighbor is s or t except case when v is s or t. */ - /* v= FIRST_INDX or FIRST_INDX+1: v is s or t respectively */ - int i, neighbor; - if ( (i = v - 2) >= 0 ) { - /* neighbor of x or y */ - if ( neigh ) { - neigh --; - /* x or y */ - *iedge = pBNS->vert[i/2].iedge[neigh]; - if ( !(pBNS->edge[*iedge].cap & EDGE_FLOW_MASK) || IS_FORBIDDEN(pBNS->edge[*iedge].forbidden, pBNS) ) { - return NO_VERTEX; - } - neighbor = (i ^ (2 * pBNS->edge[*iedge].neighbor12 + 1)) + 2; /* parity opposite to v parity */ - } else { - /* neighbor of x or y is s or t */ - neighbor = (v & 1); /* s or t, same parity as v */ - *iedge = -( neighbor + 1 ); - } - } else { - /* neighbor of s or t: x or y, same parity as v */ - if ( !(pBNS->vert[neigh].st_edge.cap & EDGE_FLOW_ST_MASK) ) { - return NO_VERTEX; - } - neighbor = 2*neigh + 2 + (v & 1); /* parity same as the parity of v */ - *iedge = -( neighbor + 1 ); - } - return neighbor; -} - - - -/*****************************************************************************/ -int GetEdgePointer( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv, BNS_EDGE **uv, S_CHAR *s_or_t ) -{ - int i = u / 2 - 1; - int j = v / 2 - 1; - int bBackward = BNS_WRONG_PARMS; - *uv = NULL; - if ( i >= 0 ) { - /* u is an atom */ - if ( j >= 0 ) { - /* v is an atom */ - if ( (u+v)%2 ) { - *uv = pBNS->edge+iuv; - bBackward = ( u & 1 ); - *s_or_t = 0; - } - } else - /* v is s or t */ - if ( v >= 0 && !((u+v)%2) ) { - *uv = (BNS_EDGE*)&pBNS->vert[i].st_edge; - bBackward = !(v & 1); - *s_or_t = v+3; /* 3=> v=s, 4=> v=t */ - } - } else - if ( j >= 0 ) { - /* u is s or t */ - if ( u >= 0 && !((u+v)%2) ) { - /* v is an atom */ - *uv = (BNS_EDGE*)&pBNS->vert[j].st_edge; - bBackward = (u & 1 ); - *s_or_t = u+1; /* 1=> u=s, 2=> u=t */ - } - } - return bBackward; -} - - - -/*****************************************************************************/ -int AugmentEdge( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv, int delta, S_CHAR bReverse, int bChangeFlow ) -{ - int f, flow, ret=0; - - BNS_ST_EDGE *pst_edge; - BNS_EDGE *pedge; - S_CHAR s_or_t; - int bBackward = GetEdgePointer( pBNS, u, v, iuv, &pedge, &s_or_t ); - if ( !IS_BNS_ERROR(bBackward) ) { - if ( bBackward ) { - delta = -delta; - } - - if ( s_or_t ) { - pst_edge = ( BNS_ST_EDGE *) pedge; - flow = pst_edge->flow; - f = (flow & EDGE_FLOW_ST_MASK) + delta; /* new flow */ - if ( !delta ) { - /*((BNS_ST_EDGE *)pedge)->flow = pst_edge->flow & ~EDGE_FLOW_ST_PATH;*/ - pst_edge->flow = pst_edge->flow & ~EDGE_FLOW_ST_PATH; - } else { - int cap = pst_edge->cap; - if ( f < 0 || f > cap ) { - ret = BNS_WRONG_PARMS; - } else { - if ( !(bChangeFlow & BNS_EF_CHNG_FLOW) ) { - f -= delta; /* do not actually change the flow, only find the augmenting path */ - } else - if ( delta ) { - /*((BNS_ST_EDGE *)pedge)->pass ++;*/ - pst_edge->pass ++; - } - flow = (flow & ~(EDGE_FLOW_ST_PATH | EDGE_FLOW_ST_MASK)) + f; - /*((BNS_ST_EDGE *)pedge)->flow = flow;*/ - pst_edge->flow = flow; - /*((BNS_ST_EDGE *)pedge)->delta += delta; */ - if ( bReverse ) { - /* u <- v; Note: in case of bReverse s_or_t has actually been determined - for the u' <- v' pair; therefore s and t should be switched - in order to correctly determine the 1st or the last atom - on the augmenting path. - */ - switch( s_or_t ) { - case 1: /* u = t: t<-v, v is the last vertex */ - ALTP_END_ATOM(pBNS->alt_path) = v / 2 - 1; - break; - case 2: /* u = s: s<-v, error */ - ret = BNS_WRONG_PARMS; - break; - case 3: /* v = t: u<-t, error */ - ret = BNS_WRONG_PARMS; - break; - case 4: /* v = s: u<-s, u is the first vertex */ - ALTP_START_ATOM(pBNS->alt_path) = u / 2 - 1; - ALTP_DELTA(pBNS->alt_path) = delta; - break; - default: - ret = BNS_WRONG_PARMS; - break; - } - } else { - /* u -> v */ - switch( s_or_t ) { - case 1: /* u = s: s->v, v is the first vertex */ - ALTP_START_ATOM(pBNS->alt_path) = v / 2 - 1; - ALTP_DELTA(pBNS->alt_path) = delta; - break; - case 2: /* u = t: t->v, error */ - ret = BNS_WRONG_PARMS; - break; - case 3: /* v = s: u->s, error */ - ret = BNS_WRONG_PARMS; - break; - case 4: /* v = t: u->t, u is the last vertex */ - ALTP_END_ATOM(pBNS->alt_path) = u / 2 - 1; - break; - default: - ret = BNS_WRONG_PARMS; - break; - } - } - } - } - } else { - f = (pedge->flow & EDGE_FLOW_MASK) + delta; - if ( !delta ) { - pedge->flow &= ~EDGE_FLOW_PATH; - } else { - if ( f < 0 || f > pedge->cap ) { - ret = BNS_WRONG_PARMS; - } else { - AT_NUMB iu = u / 2 - 1; - AT_NUMB iv = v / 2 - 1; - int indx; - if ( !(bChangeFlow & BNS_EF_CHNG_FLOW) ) { - f -= delta; /* do not actually change the flow, only find the augmenting path */ - } else - if ( delta ) { - pedge->pass ++; - } - pedge->flow = (pedge->flow & ~(EDGE_FLOW_PATH | EDGE_FLOW_MASK)) | f; - if ( ALTP_MAY_ADD(pBNS->alt_path) ) { - /* bReverse? u <- v : u -> v */ - indx = bReverse? (pedge->neighbor1 == iv) : (pedge->neighbor1 == iu); - ALTP_CUR_THIS_ATOM_NEIGHBOR(pBNS->alt_path) = pedge->neigh_ord[1-indx]; - ALTP_CUR_NEXT_ATOM_NEIGHBOR(pBNS->alt_path) = pedge->neigh_ord[indx]; - ALTP_NEXT(pBNS->alt_path); - } else { - ALTP_OVERFLOW(pBNS->alt_path) = 1; - ret = BNS_ALTPATH_OVFL; - } - } - } - } - return ret? ret : f; - - } - return bBackward; -} - - - -/*********************************************************************************/ -/* find residual capacity and mark the edge as belonging to the augmenting path */ -/*********************************************************************************/ -int rescap_mark( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv ) -{ - BNS_ST_EDGE *pst_edge; - BNS_EDGE *pedge; - - int f, flow; - S_CHAR s_or_t; - int bBackward = GetEdgePointer( pBNS, u, v, iuv, &pedge, &s_or_t ); - - if ( !IS_BNS_ERROR( bBackward ) ) { - - if ( s_or_t ) { - pst_edge = (BNS_ST_EDGE *)pedge; - flow = pst_edge->flow; - f = (flow & EDGE_FLOW_ST_MASK); - if ( !bBackward ) { - f = (int)pst_edge->cap - f; - } - if ( flow & EDGE_FLOW_ST_PATH ) { - pBNS->bNotASimplePath ++; - f /= 2; /* this is the second time we pass the same edge: reduce flow by a factor of 2 */ - } else { - pst_edge->flow |= EDGE_FLOW_ST_PATH; /* mark the edge */ - } - } else { - flow = pedge->flow; - f = flow & EDGE_FLOW_MASK; - if ( !bBackward ) { - f = (int)pedge->cap - f; - } - if ( flow & EDGE_FLOW_PATH ) { - f /= 2; /* this is the second time we pass the same edge: reduce flow by a factor of 2 */ - pBNS->bNotASimplePath ++; - } else { - pedge->flow |= EDGE_FLOW_PATH; /* mark the edge */ - } - } - return f; - - } - return bBackward; -} - - - -/***************************************************************************** -* Get previous vertex in the searched path * -* z is SwitchEdge_Vert2(y) != y. Go backward from z to y * -******************************************************************************/ -Vertex GetPrevVertex( BN_STRUCT* pBNS, Vertex y, Edge *SwitchEdge, EdgeIndex *iuv ) -{ - Vertex w, z, x2, y2, n; - EdgeIndex iwy; - - w = SwitchEdge_Vert1(y); - z = SwitchEdge_Vert2(y); - iwy = SwitchEdge_IEdge(y); - if ( z == y ) { - *iuv = iwy; - return w; - } - x2 = prim(y); - y2 = prim(z); - n = 0; - while ( y2 != NO_VERTEX ) { - w = SwitchEdge_Vert1(y2); - z = SwitchEdge_Vert2(y2); - iwy = SwitchEdge_IEdge(y2); - if ( w == x2 ) { - *iuv = iwy; - /*return z; */ - return (y + z)%2? z : prim(z); - } - n ++; -#ifdef _DEBUG - if ( n ) { - int stop = 1; - } -#endif - if ( w == y2 ) - return NO_VERTEX; - y2 = w; - } - return y2; -} - - - -#define CHECK_TACN 1 - - - - -/***************************************************************************** - The purpose is to avoid paths - - (H-group)[u]---atom[v]---((-)-cgroup)[w], - - where atom[v] is not acidic and (-) and H are not interchangeable without - explicit bond tautomerism. - - It is important that acidic atoms are only O,S,Se,Te and should have - only one chemical bond. Only because of this an early rejection of - the vertex v (before it gets on SCANQ) is possible. - - CHECK_TACN == 1 prohibits replacing (-) on N with H unless H can be moved to N - along an alternating path from another heteroatom (t-group will be detected). -*****************************************************************************/ - - - -#if ( FIX_TACN_POSSIBLE_BUG == 1 ) /* { */ - - - -/*****************************************************************************/ -int bIgnoreVertexNonTACN_atom( BN_STRUCT* pBNS, Vertex u, Vertex v ) -{ -#define TYPE_T 1 /* t-group [also called H-group] */ -#define TYPE_CN 2 /* (-)c-group */ - int i, degree, ret, u_is_taut=0, w_is_taut, num_allowed=0, num_found_groups=0; - Vertex w; - EdgeIndex ivw; - if ( !pBNS->type_TACN || u <= 1 || v <= 1 || - (pBNS->vert[v/2-1].type & pBNS->type_TACN) ) { - return 0; /* add/remove H(+) is allowed for acidic atoms */ - } - if ( !pBNS->type_T || !pBNS->type_CN ) - return 0; /* should not happen */ - u_is_taut = ((pBNS->vert[u/2-1].type & pBNS->type_T) == pBNS->type_T )? TYPE_T : - ((pBNS->vert[u/2-1].type & pBNS->type_CN) == pBNS->type_CN)? TYPE_CN : 0; - if ( u_is_taut ) { - /* u is either t-group vertex or (-) c-group */ - degree = GetVertexDegree( pBNS, v ); - for ( i = 0; i < degree; i ++ ) { - /* v = vert[u].neighbor[i]; */ - w = GetVertexNeighbor( pBNS, v, i, &ivw ); - if ( w == NO_VERTEX || w <= 1 ) { - continue; /* the atom has only single bonds or it is s or t, ignore it */ - } - if ( w != u && (ret = rescap(pBNS, v, w, ivw)) > 0 ) { - num_allowed ++; - w_is_taut = ((pBNS->vert[w/2-1].type & pBNS->type_CN) == pBNS->type_CN)? TYPE_CN : - ((pBNS->vert[w/2-1].type & pBNS->type_T) == pBNS->type_T )? TYPE_T : 0; - if ( (u_is_taut | w_is_taut) == (TYPE_T | TYPE_CN) ) { - num_found_groups ++; - } - } - } - if ( num_found_groups && num_allowed == 1 ) { - return 1; /* reject */ - } - } - return 0; -#undef TYPE_T -#undef TYPE_CN -} - - - -#else /* } FIX_TACN_POSSIBLE_BUG { */ - - - -/*****************************************************************************/ -int bIgnoreVertexNonTACN_atom( BN_STRUCT* pBNS, Vertex u, Vertex v ) -{ - int i, degree, ret, u_is_taut=0, num_allowed=0, num_found_groups=0; - Vertex w; - EdgeIndex ivw; - if ( !pBNS->type_TACN || u <= 1 || v <= 1 || - (pBNS->vert[v/2-1].type & pBNS->type_TACN) ) { - return 0; /* add/remove H(+) is allowed for acidic atoms */ - } - if ( !pBNS->type_T || !pBNS->type_CN ) - return 0; /* should not happen */ - if ( (u_is_taut = (pBNS->vert[u/2-1].type & pBNS->type_T) == pBNS->type_T) || - ( (pBNS->vert[u/2-1].type & pBNS->type_CN) == pBNS->type_CN) ) { - /* u is either t-group vertex or (-) c-group */ - degree = GetVertexDegree( pBNS, v ); - for ( i = 0; i < degree; i ++ ) { - /* v = vert[u].neighbor[i]; */ - w = GetVertexNeighbor( pBNS, v, i, &ivw ); - if ( w == NO_VERTEX || w <= 1 ) { - continue; /* the atom has only single bonds or it is s or t, ignore it */ - } - if ( w != u && (ret = rescap(pBNS, v, w, ivw)) > 0 ) { - num_allowed ++; - if ( (u_is_taut? ((pBNS->vert[w/2-1].type & pBNS->type_CN) == pBNS->type_CN) : - ((pBNS->vert[w/2-1].type & pBNS->type_T) == pBNS->type_T ) ) ) { - num_found_groups ++; - } - } - } - if ( num_found_groups && num_allowed == 1 ) { - return 1; /* reject */ - } - - } - return 0; -} - - - -#endif /* } FIX_TACN_POSSIBLE_BUG */ - - - -/***************************************************************************** - The purpose is to avoid paths - (H-group)[u]---atom[v]---((-)-cgroup)[w], - - where atom[v] is not acidic and (-) and H are not interchangeable without - explicit bond tautomerism. - - It is important that acidic atoms are only O,S,Se,Te and should have - only one chemical bond. Only because of this an early rejection of - the vertex v (before it gets on SCANQ) is possible. -*****************************************************************************/ - - - -#if ( FIX_TACN_POSSIBLE_BUG == 1 ) /* { */ - - - -/*****************************************************************************/ -int bIgnoreVertexNonTACN_group( BN_STRUCT* pBNS, Vertex v, Vertex w, Edge *SwitchEdge ) -{ -#define TYPE_T 1 /* t-group [also called H-group] */ -#define TYPE_CN 2 /* (-)c-group */ - int u_is_taut=0, w_is_taut=0; - Vertex u; - EdgeIndex iuv; - if ( v <= 1 || w <= 1 ) - return 0; -#if ( CHECK_TACN == 1 ) - if ( !pBNS->type_TACN || - (pBNS->vert[v/2-1].type & pBNS->type_TACN) ) { - return 0; - } - if ( !pBNS->type_T || !pBNS->type_CN ) - return 0; /* should not happen */ -#endif - u = GetPrevVertex( pBNS, v, SwitchEdge, &iuv ); - /* - u = SwitchEdge_Vert1(v); - iuv = SwitchEdge_IEdge(v); - */ - if ( u == NO_VERTEX || iuv < 0 ) - return 0; /* should not happen */ - /* check edge adjacency */ - if ( pBNS->edge[iuv].neighbor1 != (u/2-1) && pBNS->edge[iuv].neighbor1 != v/2-1 || - (pBNS->edge[iuv].neighbor12 ^ (u/2-1)) != (v/2-1) ) { - return 0; /* !!! should not happen !!! */ - } - -#if ( CHECK_TACN == 1 ) - u_is_taut = ((pBNS->vert[u/2-1].type & pBNS->type_T) == pBNS->type_T )? TYPE_T : - ((pBNS->vert[u/2-1].type & pBNS->type_CN) == pBNS->type_CN)? TYPE_CN : 0; - w_is_taut = ((pBNS->vert[w/2-1].type & pBNS->type_T) == pBNS->type_T )? TYPE_T : - ((pBNS->vert[w/2-1].type & pBNS->type_CN) == pBNS->type_CN)? TYPE_CN : 0; - if ( (u_is_taut | w_is_taut) == (TYPE_T | TYPE_CN ) ) { - /* rescap must have already been checked */ - return 1; - } -#endif - - return 0; -#undef TYPE_T -#undef TYPE_CN -} - - - -#else /* } FIX_TACN_POSSIBLE_BUG { */ - - - -/*****************************************************************************/ -int bIgnoreVertexNonTACN_group( BN_STRUCT* pBNS, Vertex v, Vertex w, Edge *SwitchEdge ) -{ - int u_is_taut=0, w_is_taut=0; - Vertex u; - EdgeIndex iuv; - if ( v <= 1 || w <= 1 ) - return 0; -#if ( CHECK_TACN == 1 ) - if ( !pBNS->type_TACN || - (pBNS->vert[v/2-1].type & pBNS->type_TACN) ) { - return 0; - } - if ( !pBNS->type_T || !pBNS->type_CN ) - return 0; /* should not happen */ -#endif - u = GetPrevVertex( pBNS, v, SwitchEdge, &iuv ); - /* - u = SwitchEdge_Vert1(v); - iuv = SwitchEdge_IEdge(v); - */ - if ( u == NO_VERTEX || iuv < 0 ) - return 0; /* should not happen */ - /* check edge adjacency */ - if ( pBNS->edge[iuv].neighbor1 != (u/2-1) && pBNS->edge[iuv].neighbor1 != v/2-1 || - (pBNS->edge[iuv].neighbor12 ^ (u/2-1)) != (v/2-1) ) { - return 0; /* !!! should not happen !!! */ - } - -#if ( CHECK_TACN == 1 ) - if ( ((u_is_taut = (pBNS->vert[u/2-1].type & pBNS->type_T) == pBNS->type_T) || - ( (pBNS->vert[u/2-1].type & pBNS->type_CN) == pBNS->type_CN)) && - ((w_is_taut = (pBNS->vert[w/2-1].type & pBNS->type_T) == pBNS->type_T) || - ( (pBNS->vert[w/2-1].type & pBNS->type_CN) == pBNS->type_CN)) && - u_is_taut + w_is_taut == 1 ) { - /* rescap must have already been checked */ - return 1; - } -#endif - - return 0; -} -#endif /* } FIX_TACN_POSSIBLE_BUG { */ - - - -#if ( FIX_KEEP_H_ON_NH_ANION == 1 ) - - - -/*********************************************************************************/ -/* Detect an attempt to remove H from -NH(-) to make =N(-); */ -/* all taut atoma except N are 'acidic' */ -/*********************************************************************************/ -int bIsRemovedHfromNHaion( BN_STRUCT* pBNS, Vertex u, Vertex v ) -{ - int i, u2, v2, vat2; - Vertex vtg, vat; - BNS_VERTEX *pvAT, *pvCN; - BNS_EDGE *pEdge; - if ( !pBNS->type_TACN || u <= 1 || v <= 1 || - u%2 || !(v%2) /* the edge flow may only increase */ ) { - return 0; - } - if ((pBNS->vert[u2 = u/2-1].type & pBNS->type_TACN) || - (pBNS->vert[v2 = v/2-1].type & pBNS->type_TACN) ) { - return 0; /* add/remove H is allowed for acidic atoms */ - } - if ( !pBNS->type_T || !pBNS->type_CN ) - return 0; /* should not happen */ - /* find which of u, v vertices is N and which is t-group */ - if ( ((pBNS->vert[u2].type & pBNS->type_T) == pBNS->type_T ) && v2 < pBNS->num_atoms ) { - vtg = u; - vat = v; - } else - if ( ((pBNS->vert[v2].type & pBNS->type_T) == pBNS->type_T ) && u2 < pBNS->num_atoms ) { - vtg = v; - vat = u; - } else { - return 0; - } - vat2 = vat/2-1; - pvAT = pBNS->vert + vat2; /* atom */ - for ( i = pvAT->num_adj_edges-1; 0 <= i; i -- ) { - pEdge = pBNS->edge + pvAT->iedge[i]; - pvCN = pBNS->vert + (pEdge->neighbor12 ^ vat2); - if ( ((pvCN->type & pBNS->type_CN) == pBNS->type_CN) && pEdge->flow > 0 ) { - return 1; /* detected */ - } - } - return 0; -} -#endif - - - -#if ( FIX_AVOID_ADP == 1 ) -/************************************************************************ - Detect (tg)-N=A-A=A-A=N-(tg) - u v w - k = 5 4 3 2 1 0 1 2 - ^ - odd number means ADP -*************************************************************************/ -int bIsAggressiveDeprotonation( BN_STRUCT* pBNS, Vertex v, Vertex w, Edge *SwitchEdge ) -{ -#define TYPE_T 1 /* t-group [also called H-group] */ -#define TYPE_CN 2 /* (-)c-group */ -#define TYPE_AT 4 - int k, v2, u2, w2, u2_next, type0, type1, type2, type; - Vertex u, u_next; - EdgeIndex iuv; - if ( v <= 1 || w <= 1 ) - return 0; - - if ( !pBNS->type_TACN || !pBNS->type_T || !pBNS->type_CN ) - return 0; /* should not happen */ - v2 = v/2 - 1; - w2 = w/2 - 1; - if ( v2 >= pBNS->num_atoms || w2 < pBNS->num_atoms ) - goto cross_edge; - - if ( !((pBNS->vert[w2].type & pBNS->type_T) == pBNS->type_T ) && - !((pBNS->vert[w2].type & pBNS->type_CN) == pBNS->type_CN) ) - goto cross_edge; - /* v ia an atom, w is a t-group, v != w' */ - for ( k = 0, u = v; 1 < (u_next = u, u = GetPrevVertex( pBNS, u, SwitchEdge, &iuv )); k ++ ) { - u2 = u/2 - 1; - if ( u2 >= pBNS->num_atoms ) { - /* moving backward along the alt path we have found a vertex - that is not an atom. Possibly it is a t- or (-)c-group */ - if ( !( k % 2 ) ) { - return 0; /* even vertex -- always okay */ - } - if ( !((pBNS->vert[u2].type & pBNS->type_T) == pBNS->type_T ) && - !((pBNS->vert[u2].type & pBNS->type_CN) == pBNS->type_CN) ) { - /* not a t- or (-)c-group */ - return 0; - } - u2_next = u_next/2 - 1; - if ( !(pBNS->vert[v2 ].type & pBNS->type_TACN) && - !(pBNS->vert[u2_next].type & pBNS->type_TACN) ) { - /* none of the atoms at the ends are N */ - return 0; - } - return 1; - } - } - return 0; -cross_edge: - /***************************************************************************** - * v and w (v=w') are same vertex reached with opposite "phases". - * w cannot be (t) because this would have been detected earlier -- ??? - * (t)-A=A-A=A-A=A-(t) - * v - * 3 2 1 0 1 2 3 4 - * kv kw - * (kv + kw)%2 == 1 <==> aggressive deprotonation - *****************************************************************************/ - if ( v == prim(w) ) { - type0 = 0; - if ( v2 >= pBNS->num_atoms ) { - type0 = ((pBNS->vert[v2].type & pBNS->type_T) == pBNS->type_T )? TYPE_T : - ((pBNS->vert[v2].type & pBNS->type_CN) == pBNS->type_CN)? TYPE_CN : 0; - } - - - - } - - - return 0; -} -#endif - - - -/*****************************************************************************/ -int rescap( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv ) -{ - BNS_ST_EDGE *pst_edge; - BNS_EDGE *pedge; - - int f; - S_CHAR s_or_t; - int bBackward = GetEdgePointer( pBNS, u, v, iuv, &pedge, &s_or_t ); - if ( !IS_BNS_ERROR(bBackward) ) { - - if ( s_or_t ) { - pst_edge = (BNS_ST_EDGE *)pedge; - f = (pst_edge->flow & EDGE_FLOW_ST_MASK); - if ( !bBackward ) { - f = (int)pst_edge->cap-f; - } - } else { - f = (pedge->flow & EDGE_FLOW_MASK); - if ( !bBackward ) { - f = (int)pedge->cap-f; - } - } - return f; - - } - return bBackward; /* error */ -} - - - -/********************************************************************************* - W.Kocay, D.Stone, - "An Algorithm for Balanced Flows", - The Journal of Combinatorial Mathematics and Combinatorial Computing, - vol. 19 (1995) pp. 3-31 - - W.Kocay, D.Stone, - "Balanced network flows", - Bulletin of the Institute of Combinatorics and its Applications, - vol. 7 (1993), pp. 17--32 - - N = a balanced network (bipartite directed graph) of: - n=2*V+2 vertices (incl. s (source) and t (target); - each other vertex i is included 2 times: - set X (x[i]) of V vertices (v) and a set Y (y[j]) of - V complementary vertices (v') so that x[i]' = y[i], x[i]''=x[i], and - m=2*E+2*V edges (each original undirected edge i-j is represented as - 2 directed edges: x[i]->y[j] and x[j]->y[i]; plus - V edges s->x[i] and V edges y[j]->t) - v' = a complement vertex to v - v'u' = (uv)' = a complement edge to uv - rescap(uv) = cap(uv)-f(uv) if uv is a forward edge - = f(uv) if uv is a backward edge - (i) 0 <= f(uv) <= cap(uv) - (ii) f+(u) = f-(u) for all u in X, Y where f+(u) is a total flow out of u, - and f-(u) is a total flow into u - (iii) f(uv) = f((uv)') (balanced flow condition) - - S = a set of all s-searchable vertices - S- = all other vertices - if t in S, then N contains a valid augmenting path P, so that flow can be - augmented on both P and P' - if t not in S, then let K=[S,S-], the set of all edges directed from S to S-. - K is an edge-cut that has a special structure. - Let - A = {x[i], y[i] |x[i] not in S, y[i] in S} - B = {x[i], y[i] |x[i] in S, y[i] not in S} - C = {x[i], y[i] |x[i] in S, y[i] in S} - D = {x[i], y[i] |x[i] not in S, y[i] not in S} - N[C] = subgraph of N induced by C; - it consists of of a number of connected components C[i] - K[i] = those edges of K with one endpoint in C[i]. - - If t is in S- then - - i) Each C[i] = C[i]' - ii) There are no edges between C and D - iii) Each K[i] has odd capacity, it is called a balanced edge-cut. - - balcap(K) = cap(K) - odd(K), where odd(K) is the number of connected components in N[C]. - Name "odd(K)" is because each cap(K[i]) is odd. - - Max-Balanced-Flow-Min-Balanced-Cut Theorem: - - Let f be a balanced flow in N, and let K be any balanced edge-cut. - The value of a maximum balanced flow equals the capacity of a minimum - edge-cut, that is, val(f) = balcap(K) when f is maximum and K is minimum. - -*********************************************************************************/ - -/*******************************************************/ -/* */ -/* VERTEX NUMBERING */ -/* */ -/* Total number of atoms = n */ -/* Total number of vertices = 2*n+2 */ -/*******************************************************/ -/* */ -/* atom numbering starts from 0: */ -/* */ -/* atoms s t x0 y0 x1 y1 ... xi yi ...xn yn */ -/* vertices 0 1 2 3 4 5 ... 2i-2 2i-1 ...2n-2 2n-1 */ -/* */ -/* atom = vertex/2-1; if negative then s or t */ -/* */ -/* vertex = (atom + 1) * 2 + i; i=0 for x, i=1 for y */ -/* */ -/*******************************************************/ - - - -/*********************************************************************************/ -/* v' variable is called prim(v) for now */ -/*********************************************************************************/ -int BalancedNetworkSearch ( BN_STRUCT* pBNS, BN_DATA *pBD, int bChangeFlow ) -{ /* N has source s, target t, the mirror network M is constructed */ - /* the tree T contains a valid sv-path for each v in T. Simultaneously the complementary - tree T' is built as indicated in comments. The trees T and T' must have no edges or - vertices in common. Initially T will be built as in breadth-first-search, and T' will - be the complementary tree, it will contain the complementary valid v't-path. - */ - - Vertex *BasePtr = pBD->BasePtr; - Edge *SwitchEdge = pBD->SwitchEdge; - S_CHAR *Tree = pBD->Tree; - Vertex *ScanQ = pBD->ScanQ; - int QSize = pBD->QSize; - Vertex *Pu = pBD->Pu; - Vertex *Pv = pBD->Pv; - int max_len_Pu_Pv= pBD->max_len_Pu_Pv; - - /* added to translate into C */ - int i, k, degree, delta, ret = 0; - Vertex u, b_u, v, b_v, w; - EdgeIndex iuv; -#if ( BNS_RAD_SEARCH == 1 ) - int n, bRadSearch = (BNS_EF_RAD_SRCH & bChangeFlow) && pBD->RadEndpoints; - BRS_MODE bRadSrchMode = RAD_SRCH_NORM; - int bRadSearchPrelim = 0; - if ( bRadSearch ) { - pBD->nNumRadEndpoints = 0; - bRadSrchMode = pBD->bRadSrchMode; - bRadSearchPrelim = pBNS->type_TACN && bRadSrchMode == RAD_SRCH_NORM; - } -#endif - -/* -- Always -- - Vertex_s = FIRST_INDX; - Vertex_t = Vertex_s+1; -*/ - QSize = k = 0; /* put s on ScanQ = set S */ - ScanQ[QSize] = Vertex_s; - BasePtr[Vertex_t] = Vertex_s; - BasePtr[Vertex_s] = BLOSSOM_BASE; /* create initial blossom C(Vertex_s) with base s */ - Tree[Vertex_s] = TREE_IN_1; - - do { - u = ScanQ[k]; /* select u from the head of ScanQ */ - /* since u is on the queue, it has a blossom C(U) with base b_u */ - b_u = FindBase( u, BasePtr ); - degree = GetVertexDegree( pBNS, u ); -#if ( BNS_RAD_SEARCH == 1 ) - n = 0; -#endif - for ( i = 0; i < degree; i ++ ) { - /* v = vert[u].neighbor[i]; */ - v = GetVertexNeighbor( pBNS, u, i, &iuv ); - if ( v == NO_VERTEX ) { - continue; /* the atom has only single bonds, ignore it */ - } -#if ( BNS_RAD_SEARCH == 1 ) - if ( !k && bRadSrchMode == RAD_SRCH_FROM_FICT && v/2 <= pBNS->num_atoms ) { - continue; /* start from fict. vertices only */ - } - if ( bRadSearchPrelim && v/2 > pBNS->num_atoms ) { - continue; /* during initial add/remove H allow radical movement only through real atoms */ - } -#endif - if ( /* PrevPt[u] != v ** avoid edges of T */ - ( SwitchEdge_Vert1(u) != v || SwitchEdge_Vert2(u) != u ) /* avoid edges of T */ - && (ret = rescap(pBNS, u, v, iuv)) > 0 ) { - /* special treatment to prevent H<->(-) replacement on non-acidic atoms */ - /*----------------------------------------------------------------------*/ - if ( pBNS->type_TACN ) { - if ( bIgnoreVertexNonTACN_atom( pBNS, u, v ) ) { - continue; - } - if ( bIgnoreVertexNonTACN_group( pBNS, u, v, SwitchEdge ) ) { - continue; - } -#if ( FIX_KEEP_H_ON_NH_ANION == 1 ) - if ( bIsRemovedHfromNHaion( pBNS, u, v ) ) { - continue; - } -#endif -#if ( FIX_AVOID_ADP == 1 ) - if ( bIsAggressiveDeprotonation( pBNS, u, v, SwitchEdge ) ) { - continue; - } -#endif - - } - /*----------------------------------------------------------------------*/ - b_v = FindBase(v, BasePtr); /* Notation: b_x is a base of x */ - - if ( b_v == NO_VERTEX ) { /* originally 0 instead of NO_VERTEX */ - /* Important note: following "A. Note of Implementing the Algorithm" from the - article by Kocay and Stone, all references to PrevPt[a] have been - replaced with SwitchEdge[a][0]=SwitchEdge_Vert1(a); to be on a safe side - the check whether (SwitchEdge_Vert2(a)==a) has been added. - */ - - /* v is not yet in T or T' -- add it to T and M */ - /*PrevPt[v] = u; ** this effectively adds v to T and v' to T' */ - QSize ++; - ScanQ[QSize] = v; - TREE_MARK(v, TREE_IN_1); /* mark v s-reachable (in T) */ - TREE_MARK(prim(v), TREE_IN_2); /* mark v' in T' */ - - /* Switch Edges: If u in T then Pu (a valid su-path) can be constructed - by successfully executing u=PrevPt[u] until u=s. - For vertices in T' the situation is different. - Suppose uv and v'u' are added to a mirror network M creating a blossom: - - s T (Note: in the code v' is prim(v), - T / \ u' is prim(u), etc.) - / ... - w=x1 - / \ u in T, v in T' - u=y2 v'=y3 - \ / <--- two added edges uv and (uv)'=v'u' - -------- X ------------intersection of the edges ------------------------ - / \ <--- (the edges intersection in the picture is shown as X) - u'=x2 v=x3 u' it T', v' in T - \ / - T' w'=y1 - \ ... - \ / - t T' - - Vertices v and u' now become s-reachable; - The valid paths to v and u' must use the edges uv and v'u' respectively. - - For each vertex z in S we define a switch-edge that allows a valid sz-path - to be constructed, SwitchEdge[v]=uv and SwitchEdge[u']=v'u'. (We don't - know the direction of the edge uv, it may be (u,v) or (v,u). In either case, - the complementary edge is indicated by v'u'). - - Vertex w' also becomes s-reachable when uv is added to M, and a valid sw'-path - must use one of uv and v'u'. Therefore we choose one of them, say uv (see below - the rule of choosing the switch-edge), and define SwitchEdge[w'] = uv. - - When the addition of an edge uv to M causes a vertex z to become s-reachable - (where z was previously non-reachable), z is placed on the ScanQ, that is, into S. - The edge uv is said to be a switch-edge for z. - - Rule: We choose the the order of the vertices uv to be such that the valid sz-path - consists of a valid su-path, followed by edge uv, followed by a valid vz-path. - - For vertices z in T we can take SwitchEdge[z]=yz where y=PrevPt[z] since - it is the edge yz that allows z to be s-reachable. - For vertices z not in S we take SwitchEdge[z]=NONE. - - */ - - /* v is not yet in T or T' -- add it to T and M */ - SwitchEdge_Vert1(v) = u; /* this effectively adds uv and v'u' to M */ - SwitchEdge_IEdge(v) = iuv; - - BasePtr[prim(v)] = v; - BasePtr[v] = BLOSSOM_BASE; /* create a trivial blossom C(v) with base v */ -#if ( BNS_RAD_SEARCH == 1 ) - n ++; -#endif - } else - if ( TREE_IS_S_REACHABLE(prim(v)) /*Is_s_Reachable(prim(v)*/ - /* if v' is reachable, an st-path is given by P(u)-uv-P'(v') */ - /*&& PrevPt[prim(u)] != prim(v) ** avoid edges of T' */ - && (SwitchEdge_Vert1(prim(u)) != prim(v) || SwitchEdge_Vert2(prim(u)) != prim(u)) /* avoid edges of T' */ - && b_u != b_v - && !(pBNS->type_TACN && bIgnoreVertexNonTACN_group( pBNS, prim(v), u, SwitchEdge )) -#if ( FIX_KEEP_H_ON_NH_ANION == 1 ) - && !(pBNS->type_TACN && bIsRemovedHfromNHaion( pBNS, prim(v), u )) -#endif - ) { -#if ( BNS_RAD_SEARCH == 1 ) - n ++; -#endif - /* there is now a valid sv-path via u avoiding b_v (unless v==b_v) - => u, v, u', and v' now all become part of the same connected component of M[C] */ - w = MakeBlossom( pBNS, ScanQ, &QSize, Pu, Pv, max_len_Pu_Pv, SwitchEdge, BasePtr, u, v, iuv, b_u, b_v, Tree ); - /* this constructed the new blossom and returned its base */ - if ( IS_BNS_ERROR( w ) ) { - pBD->QSize = QSize; - return w; /* error */ - } - b_u = w; /* the new base of C(u) */ - if ( prim(w) == Vertex_t ) { - /* t is now s-reachable, a valid augmenting path P exists in M */ - delta = FindPathCap( pBNS, SwitchEdge, Vertex_s, Vertex_t, 10000 ); /* compute the residual capacity of P + P' */ - if ( IS_BNS_ERROR( delta ) ) { - pBD->QSize = QSize; - return delta; /* error */ - } -#if ( ALLOW_ONLY_SIMPLE_ALT_PATH == 1 ) - if ( pBNS->bNotASimplePath || abs(delta) > 1 ) { - delta = 0; - } -#endif - if ( delta ) { - pBNS->bChangeFlow |= (bChangeFlow & BNS_EF_CHNG_FLOW); - } - ret = PullFlow( pBNS, SwitchEdge, Vertex_s, Vertex_t, delta, 0, bChangeFlow ); /* augment on a pair of valid st-paths */ - pBD->QSize = QSize; - return ( IS_BNS_ERROR(ret)? ret : delta ); - } - } - } else - if ( IS_BNS_ERROR( ret ) ) { - pBD->QSize = QSize; - return ret; /* error */ - } - } -#if ( BNS_RAD_SEARCH == 1 ) - if ( bRadSearch && !n ) { - /* the BNS stopped at u */ - n = RegisterRadEndpoint( pBNS, pBD, u); - if ( IS_BNS_ERROR( n ) ) { - pBD->QSize = QSize; - return n; - } - } -#endif - k ++; /* advance ScanQ */ - } while( k <= QSize ); - /* if this point is reached, no valid augmenting path exists, ScanQ contains - the set S of all s-reachable vertices and K=[S,S-] is a minimum balanced edge-cut */ - /* ClearFlowMarks( vert, num_vert); */ - pBD->QSize = QSize; - return 0; -} -/******************************************************************** -Blossoms. - - The vertices of a mirror network M consist T U T'. Intersection T ^ T' is empty. - - The edges of M consist of switch-edges and their complements because edges - are added in complementary pairs, one of which is always a switch-edge. - - The base of every blossom is in T. - Let C(i) be a blossom with base b_i. Since C(i)=C(i)', C(i) contains vertices of T and T'. - Since every valid sv-path to v in C(i) contains b_i, b_i is the first s-reachable vertex of C(i). - - Suppose the mirror network M contains a valid sz-path P(z) to all vertices z in ScanQ. - Every vertex of P(z) is s-reachable therefore its vertices are all in blossoms or - trivial blossoms. - - Let z be an s-reachable vertex and P(z) be a valid path in M. - Then every valid sz-path in M contains exactly the same sequence of blossom bases as P(z). - -*********************************************************************/ - - - -/*********************************************************************** - BasePtr[u] = -2 NO_VERTEX u is not a blossom - -1 BLOSSOM_BASE u is the base of its blossom - v a vertex closer to the base -************************************************************************/ -Vertex FindBase( Vertex u, Vertex *BasePtr ) -{ - if ( BasePtr[u] == NO_VERTEX ) { - return NO_VERTEX; - } else - if ( BasePtr[u] == BLOSSOM_BASE ) { - return u; - } else { - Vertex b; - b = FindBase(BasePtr[u], BasePtr ); - BasePtr[u] = b; /* path compression */ - return b; - } -} - - - -/*********************************************************************************/ -/* Returns index of the last path element and the path */ -/*********************************************************************************/ -int FindPathToVertex_s( Vertex x, Edge *SwitchEdge, Vertex *BasePtr, Vertex *Path, int MaxPathLen ) -{ - /* x is the base of a blossom, construct a valid Path of blossom bases to s */ - int i = 0; - Path[i] = x; - while ( x != Vertex_s ) { - x = FindBase(SwitchEdge_Vert1(x), BasePtr); - if ( ++i < MaxPathLen ) { - Path[i] = x; - } else { - return BNS_WRONG_PARMS; - } - } - return i; -} - - - -/*********************************************************************************/ -/* Make a blossom */ -/*********************************************************************************/ -Vertex MakeBlossom( BN_STRUCT* pBNS, Vertex *ScanQ, int *pQSize, - Vertex *Pu, Vertex *Pv, int max_len_Pu_Pv, - Edge *SwitchEdge, Vertex *BasePtr, - Vertex u, Vertex v, EdgeIndex iuv, Vertex b_u, Vertex b_v, S_CHAR *Tree ) -{ - /* In order to find the base of the new blossom, the paths - P(u) and P(v') are constructed and compared in order to - find the last blossom base they have in common which - is reachable on a valid path. - - Edge uv connects two blossoms, their bases are b_u and b_v. - */ - Vertex w, z; - int len_Pu, len_Pv; - int i, j; - EdgeIndex izw; - - len_Pu = FindPathToVertex_s( b_u, SwitchEdge, BasePtr, Pu, max_len_Pu_Pv ); - if ( IS_BNS_ERROR( len_Pu ) ) { - return len_Pu; - } - len_Pv = FindPathToVertex_s( b_v, SwitchEdge, BasePtr, Pv, max_len_Pu_Pv ); - if ( IS_BNS_ERROR( len_Pv ) ) { - return len_Pv; - } - i = len_Pu; - j = len_Pv; - /* initially Pu[i] and Pv[j] both equal to s, but their first elements are different */ - /* find the last blossom base common to Pu and Pv */ - while ( i >= 0 && j >= 0 && Pu[i] == Pv[j] ) { - /* was (Pu[i]==Pv[j] && i>=0 && j>=0) => tested Pu[-1], Pv[-1] <- pointed by W.Ihlenfeldt 08-26-2004*/ - i --; - j --; - } - i ++; - w = Pu[i]; /* w is the last common vertex */ - z = SwitchEdge_Vert1(w); - izw = SwitchEdge_IEdge(w); - /* now extend the blossom if rescap(zw) >= 2 */ - while ( w != Vertex_s && rescap(pBNS, z, w, izw) >= 2 ) - { - i++; - w = Pu[i]; - z = SwitchEdge_Vert1(w); - izw = SwitchEdge_IEdge(w); - } - /* w is the base of the new blossom */ - /* first follow the path Pu from w to b_u */ - for ( i = i-1; i >= 0; i -- ) { - z = Pu[i]; /* z is the base of the blossom */ - BasePtr[z] = w; - BasePtr[prim(z)] = w; /* w is the new base of the blossom */ - /* z and z' may already be part of a blossom that is being - swallowed into a larger blossom. - We don't want to change the switch edge in that case. - */ - - if ( !TREE_IS_ON_SCANQ(prim(z)) /*!IsInScanQ(prim(z)) */) - { - SwitchEdge_Vert1(prim(z)) = prim(v); /* set the switch edge of z' */ - /* SwitchEdge[prim(z)][1] = prim(u); */ - SwitchEdge_IEdge(prim(z)) = iuv; - (*pQSize) ++; - ScanQ[*pQSize] = prim(z); /* add z' to ScanQ */ - TREE_MARK(prim(z), TREE_IN_2BLOSS); /* mark z' s-reachable */ - } - } - /* now follow the path Pv */ - for ( j = j; j >= 0; j -- ) { - z = Pv[j]; /* z is the base of the blossom */ - BasePtr[z] = w; - BasePtr[prim(z)] = w; /* w is the new base of the blossom */ - /* z and z' may already be part of a blossom that is being - swallowed into a larger blossom. - We don't want to change the switch edge in that case. - */ - - if ( !TREE_IS_ON_SCANQ(prim(z)) /*!IsInScanQ(prim(z)) */ ) - { - SwitchEdge_Vert1(prim(z)) = u; /* set the switch edge of z' */ - /* SwitchEdge[prim(z)][1] = v; */ - SwitchEdge_IEdge(prim(z)) = iuv; - (*pQSize) ++; - ScanQ[*pQSize] = prim(z); /* add z' to ScanQ */ - TREE_MARK(prim(z), TREE_IN_2BLOSS); /* mark z' s-reachable */ - } - } - - if ( !TREE_IS_ON_SCANQ(prim(w)) /* !IsInScanQ(prim(w))*/ ) - { /* add w' to the blossom */ - SwitchEdge_Vert1(prim(w)) = u; /* set the switch edge of w' */ - /* SwitchEdge[prim(w)][1] = v; */ - SwitchEdge_IEdge(prim(w)) = iuv; - (*pQSize) ++; - ScanQ[*pQSize] = prim(w); /* add w' to ScanQ */ - TREE_MARK(prim(w), TREE_IN_2BLOSS); /* mark w' s-reachable */ - } - return w; -} - - - -/***************************************************************************** - When t is found to be s-reachable, a valid st-path P is known to exist. - Its complementary path P' is also valid. Once the residual capacity - delta(P) is known, the flow is augmented by calling PullFlow(s,t,delta). - It constructs the path P by using the switch-edges. - Let uv=SwitchEdge[t]. - Then P is given by a valid su-path, followed by the edge uv, followed by - a valid vt-path. - PullFlow is a recursive procedure that constructs the path and its complement. - - Let wz=SwitchEdge[y]. PullFlow(x, y, delta) uses the xw- and zy-portions of P - (see below). Since it must also augment on P' simultaneously, the zy-portion - is replaced by the y'z'-portion. - - x y' - | | P: x--w--z--y - P | | P' P': y'-z'-w'-x' - | o - o \ - / w' z \ - / o----\ /----o / - \ / \ / \ / - \/ X \/ - /\ / \ /\ - / \ w / \ z' / \ - \ o---- ----o / Using a switch-edge wz and w'z' - \ / to construct P and P' - o o - | | - | | - x' y - -**********************************************************************************/ - - - - -/*********************************************************************************/ -int PullFlow( BN_STRUCT *pBNS, Edge *SwitchEdge, Vertex x, Vertex y, int delta, S_CHAR bReverse, int bChangeFlow ) -{ /* - Augment the flow by delta on all edges on a path P - between x and y in the order of the path; - AugmentEdge( pBNS, w, z, iwz, delta, 0 ) means the path is in w->z direction - AugmentEdge( pBNS, w, z, iwz, delta, 1 ) means the path is in w<-z direction - - Unlike PullFlow in the paper by Kocay & Stone, here the augmentation - always starts at "s", proceeds sequentially through the path end terminates at "t". - Since we do not really need the complement path, PullFlow ignores it. - - */ - - Vertex w, z; - EdgeIndex iwz; - int ret = 0; - - w = SwitchEdge_Vert1(y); - z = SwitchEdge_Vert2(y); - iwz = SwitchEdge_IEdge(y); - if ( bReverse ) { - /* P consists of a path from x to w, then wz, then a path from z to y. */ - /* z may equal y, in which case z is just PrevPt[y] */ - if ( z != y ) { - ret = PullFlow( pBNS, SwitchEdge, prim(y), prim(z), delta, (S_CHAR)(1-bReverse), bChangeFlow ); /* augment between z and y */ - } - if ( !IS_BNS_ERROR(ret) ) { - ret = AugmentEdge( pBNS, w, z, iwz, delta, bReverse, bChangeFlow); - } - /* Do not augment the complementary path: AugmentEdge( prim(z), prim(w), vert, delta); */ - /* w may equal x, in which case there is no need to call PullFlow(x, w) */ - if ( w != x && !IS_BNS_ERROR(ret) ) { - ret = PullFlow( pBNS, SwitchEdge, x, w, delta, bReverse, bChangeFlow ); /* augment between x and w */ - } - } else { - /* P consists of a path from x to w, then wz, then a path from z to y. */ - /* w may equal x, in which case there is no need to call PullFlow(x, w) */ - if ( w != x && !IS_BNS_ERROR(ret) ) { - ret = PullFlow( pBNS, SwitchEdge, x, w, delta, bReverse, bChangeFlow ); /* augment between x and w */ - } - if ( !IS_BNS_ERROR(ret) ) { - ret = AugmentEdge( pBNS, w, z, iwz, delta, bReverse, bChangeFlow); - } - /* z may equal y, in which case z is just PrevPt[y] */ - if ( z != y && !IS_BNS_ERROR(ret) ) { - ret = PullFlow( pBNS, SwitchEdge, prim(y), prim(z), delta, (S_CHAR)(1-bReverse), bChangeFlow ); /* augment between z and y */ - } - } - return ret; -} - - - -/******************************************************************************** -Before augmenting on the two paths, it is necessary to find delta(P). -This can be done by following the paths and computing the minimum -residual capacity of all edges on P. An edge on both P and P' counts -for only half of its actual residual capacity, since augmentng on P by -delta will simutaneously reduce its capacity on P' by delta. -The path P can only be followed by using the switch-edges, as in PullFlow(...). -FindPathCap( x, y, delta ) is a recursive procedure that finds the residual -capacity on the portion of P between x and y. delta is the minimum capacity -found so far along the path. -********************************************************************************/ -int FindPathCap( BN_STRUCT* pBNS, Edge *SwitchEdge, Vertex x, Vertex y, int delta ) -{ /* find the minimum residual capacity of all edges - between x and y in a valid st-path P. - delta is the minimum found so far - the vertices occur in order s,...,x,...,y,...,t along P - the vertices occur in order s,...,y',...,x',...,t along P' - */ - Vertex w, z, iwz; - int cap, delta2; - static int level; - - if ( level ++ > 50 ) { -#ifdef _DEBUG - int stop = 1; -#else - ; -#endif - } - - - w = SwitchEdge_Vert1(y); - z = SwitchEdge_Vert2(y); /* wz is on the path P */ - iwz = SwitchEdge_IEdge(y); /* edge index */ - - /* rescap_mark() detects edges passed 2 times and reduces rescap */ - cap = rescap_mark( pBNS, w, z, iwz ); - - if ( IS_BNS_ERROR( cap ) ) { - level --; - return cap; - } - if ( cap < delta ) { - delta = cap; - } - /* P consists of a path from x to w, then wz, then a path from z to y */ - if ( w != x ) { - delta2 = FindPathCap( pBNS, SwitchEdge, x, w, delta ); - delta = inchi_min( delta2, delta ); - } - if ( z != y ) { - delta2 = FindPathCap( pBNS, SwitchEdge, prim(y), prim(z), delta ); - delta = inchi_min( delta2, delta ); - } - level --; - return delta; -} - - - -/*********************************************************************************/ -/* BT = bond types */ -#define BT_ALTERN_BOND 1 /* 1-2, possibly stereo */ -#define BT_OTHER_ALTERN_BOND 2 /* 1-3, 2-3, 1-2-3 alternating non-stereo non-taut bonds */ - -#define BT_ALTERN_NS_BOND 4 - -#define BT_TAUTOM_BOND 8 - -#define BT_ALTERN_UNKN_BOND 16 - -#define BT_IGNORE_BOND 0 - -#define BT_NONSTEREO_MASK (BT_TAUTOM_BOND|BT_ALTERN_NS_BOND) - -#define BT_ALT_BOND_MASK (BT_ALTERN_BOND|BT_OTHER_ALTERN_BOND) - -#define BT_NONTAUT_BOND_MASK (BT_ALTERN_BOND|BT_OTHER_ALTERN_BOND|BT_ALTERN_NS_BOND) - -/* BNS members redefinitions for finding non-stereo bonds */ -/* BNS_EDGE */ -#define nBlockNumberAltBns flow /* internal variable of the DFS traversal: mark traversed bonds */ -#define nNumAtInBlockAltBns cap -#define nBondTypeInpAltBns pass /* 0=>cannot be stereo at all, 1=>alt or taut non-stereo, 2=>can be stereo */ -#define nBondNonStereoAltBns cap /* 1=>found to be non-stereogenic although BondTypeInp=2; 0=>as in BondTypeInp */ - -#if ( BNS_MARK_ONLY_BLOCKS == 1 ) /* { */ -/* BNS_VERTEX */ -#define bCutVertexAltBns st_edge.cap0 /* cut-vertex flag */ -#define nRingSystemAltBns st_edge.cap /* ordering number of a ring system */ -#define nNumAtInRingSystemAltBns st_edge.flow0 /* number of vertices in a ring system */ -#define nBlockSystemAltBns st_edge.flow /* result of the DFS traversal: even cirquit must be within one block */ - -#endif /* } */ - -#define valenceAltBns num_adj_edges -/*********************************************************************************/ - - -/********************************************************************************/ -int MarkRingSystemsAltBns( BN_STRUCT* pBNS, int bUnknAltAsNoStereo ) -{ - AT_NUMB *nStackAtom = NULL; - int nTopStackAtom; - AT_NUMB *nRingStack = NULL; - int nTopRingStack; /* was AT_NUMB */ - AT_NUMB *nBondStack = NULL; - int nTopBondStack; - AT_NUMB *nDfsNumber = NULL; - AT_NUMB *nLowNumber = NULL; - S_CHAR *cNeighNumb = NULL; - AT_NUMB nDfs; - AT_NUMB nNumAtInRingSystem; - int i, j, u, w, start, nNumRingSystems, nNumStartChildren; - BNS_VERTEX *at = pBNS->vert; - BNS_EDGE *bond = pBNS->edge; - int num_atoms = pBNS->num_atoms; - int num_edges = pBNS->num_bonds; - - /* allocate arrays */ - nStackAtom = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nStackAtom[0])); - nRingStack = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nRingStack[0])); - nDfsNumber = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nDfsNumber[0])); - nLowNumber = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nLowNumber[0])); - nBondStack = (AT_NUMB *) (num_edges? inchi_malloc(num_edges*sizeof(nBondStack[0])):NULL); /* special case: no bonds 2006-03 */ - cNeighNumb = (S_CHAR *) inchi_malloc(num_atoms*sizeof(cNeighNumb[0])); - /* check allocation */ - if ( !nStackAtom || !nRingStack || !nDfsNumber || !nLowNumber || !nBondStack && num_edges || !cNeighNumb - ) { - nNumRingSystems = CT_OUT_OF_RAM; /* program error */ /* */ - goto exit_function; - } - - /******************************************** - * - * Find Cut-vertices & Blocks - * - * 1\ /5 has 3 blocks (maximal subgraphs that - * Example: | >3--4< | are nonseparable by deleting a single vertex): - * 2/ \6 (1,2,3, has 3 bonds), (3,4, has 1 bond), and (4,5,6, has 3 bonds) - * - * Cut-vertices or articulation points are - * intersections of the blocks: points 3 and 4. - ********************************************/ - - /******************************************************** - - RingSystemAlt are atoms connected by alternating bonds - (as must be indicated in bIsAltBond()): - - BOND_ALTERN - BOND_ALT_123 - BOND_ALT_13 - BOND_ALT_23 - - Since other bonds may be present, we possibly need - to restart to move to another component - *********************************************************/ - - nNumRingSystems = 0; - memset( nDfsNumber, 0, num_atoms*sizeof(nDfsNumber[0])); - - for ( start = 0; start < num_atoms; start ++ ) { - if ( nDfsNumber[start] ) - continue; - for ( i = 0; i < at[start].valenceAltBns; i ++ ) { - if ( bond[at[start].iedge[i]].nBondTypeInpAltBns & BT_ALTERN_BOND ) - goto found_alt; - } - continue; - -found_alt: - - - /* initiation */ - u = start; /* start atom */ - nDfs = 0; - nTopStackAtom =-1; - nTopRingStack =-1; - nTopBondStack =-1; - memset( cNeighNumb, 0, num_atoms*sizeof(cNeighNumb[0])); - /* push the start atom on the stack */ - nLowNumber[u] = nDfsNumber[u] = ++nDfs; - nStackAtom[++nTopStackAtom] = (AT_NUMB)u; - nRingStack[++nTopRingStack] = (AT_NUMB)u; - - nNumStartChildren = 0; - - do { - /* advance */ - /*while ( (int)at[i=nStackAtom[nTopStackAtom]].valenceAltBns > (j = (int)cNeighNumb[i]) )*/ - /* replaced due to missing sequence point */ - while ( i=(int)nStackAtom[nTopStackAtom], j = (int)cNeighNumb[i], (int)at[i].valenceAltBns > j ) - { - cNeighNumb[i] ++; - if ( !(bond[w=at[i].iedge[j]].nBondTypeInpAltBns & BT_ALT_BOND_MASK) ) { - continue; - } - u = (int)(bond[at[i].iedge[j]].neighbor12 ^ i); - if ( !nDfsNumber[u] ) { - /* tree edge, 1st visit -- advance */ - nStackAtom[++nTopStackAtom] = (AT_NUMB)u; - nRingStack[++nTopRingStack] = (AT_NUMB)u; - nBondStack[++nTopBondStack] = (AT_NUMB)w; - nLowNumber[u] = nDfsNumber[u] = ++nDfs; - nNumStartChildren += (i == start); - } else - if ( !nTopStackAtom || u != (int)nStackAtom[nTopStackAtom-1] ) { /* may comment out ? */ - /* back edge: u is not a predecessor of i */ - if ( nDfsNumber[u] < nDfsNumber[i] ) { - /* Back edge, 1st visit: u is ancestor of i. Save and compare */ - nBondStack[++nTopBondStack] = (AT_NUMB)w; - if ( nLowNumber[i] > nDfsNumber[u] ) { - nLowNumber[i] = nDfsNumber[u]; - } - } - } - } - cNeighNumb[i] = 0; - - /* back up */ - if ( i != start ) { - u = (int)nStackAtom[nTopStackAtom-1]; /* predecessor of i */ - if ( nLowNumber[i] >= nDfsNumber[u] ) { - /* output the block; the block was entered through its first bond u->i */ - nNumRingSystems ++; - /*at[u].nBlockSystemAltBns = nNumRingSystems;*/ /* mark the atom */ - nNumAtInRingSystem = 1; - /* - if ( u != start || nNumStartChildren > 1 ) { - at[u].bCutVertexAltBns += 1; // mark cut-vertex (articulation point) - } - */ - while ( nTopRingStack >= 0 ) { - j = nRingStack[nTopRingStack--]; - /*at[j].nBlockSystemAltBns = nNumRingSystems;*/ /* mark the atom */ - nNumAtInRingSystem ++; - if ( i == j ) { - break; - } - } - while ( nTopBondStack >= 0 ) { - w = nBondStack[nTopBondStack--]; - bond[w].nBlockNumberAltBns = nNumRingSystems; /* mark the bond */ - bond[w].nNumAtInBlockAltBns = nNumAtInRingSystem; - if ( i == bond[w].neighbor1 && u == (i ^ bond[w].neighbor12) || - u == bond[w].neighbor1 && i == (u ^ bond[w].neighbor12)) { - break; - } - } - } else - if ( nLowNumber[u] > nLowNumber[i] ) { - /* inherit */ - nLowNumber[u] = nLowNumber[i]; - } - } - } while ( --nTopStackAtom >= 0 ); - } - -#if ( BNS_MARK_ONLY_BLOCKS != 1 ) /* { */ - - /******************************************** - * - * Find Ring Systems - * Including chain atoms X: A-X-B, where the bonds (of any kind) are bridges. - * - ********************************************/ - - /* initiation */ - - nNumRingSystems = 0; - - for ( start = 0; start < num_atoms; start ++ ) { - if ( at[start].nRingSystemAltBns ) - continue; - for ( i = 0; i < at[start].valenceAltBns; i ++ ) { - if ( bond[at[start].iedge[i]].nBondTypeInpAltBns & BT_ALT_BOND_MASK ) - goto found_alt2; - } - continue; - -found_alt2: - - u = start; /* start atom */ - nDfs = 0; - nTopStackAtom =-1; - nTopRingStack =-1; - memset( nDfsNumber, 0, num_atoms*sizeof(nDfsNumber[0])); - memset( cNeighNumb, 0, num_atoms*sizeof(cNeighNumb[0])); - /* push the start atom on the stack */ - nLowNumber[u] = nDfsNumber[u] = ++nDfs; - nStackAtom[++nTopStackAtom] = (AT_NUMB)u; - nRingStack[++nTopRingStack] = (AT_NUMB)u; - - do { - /* advance */ -advance_ring: - /*if ( (int)at[i=nStackAtom[nTopStackAtom]].valenceAltBns > (j = (int)cNeighNumb[i]) )*/ - /* replaced due to missing sequence point */ - if ( i=(int)nStackAtom[nTopStackAtom], j = (int)cNeighNumb[i], (int)at[i].valenceAltBns > j ) - { - cNeighNumb[i] ++; - if ( !(bond[at[i].iedge[j]].nBondTypeInpAltBns & BT_ALTERN_BOND) ) { - goto advance_ring; - } - u = (int)(bond[at[i].iedge[j]].neighbor12 ^ i); - if ( !nDfsNumber[u] ) { - /* tree edge, 1st visit -- advance */ - nStackAtom[++nTopStackAtom] = (AT_NUMB)u; - nRingStack[++nTopRingStack] = (AT_NUMB)u; - nLowNumber[u] = nDfsNumber[u] = ++nDfs; - } else - if ( !nTopStackAtom || u != (int)nStackAtom[nTopStackAtom-1] ) { - /* back edge: u is not a predecessor of i */ - if ( nDfsNumber[u] < nDfsNumber[i] ) { - /* Back edge, 1st visit: u is ancestor of i. Compare */ - if ( nLowNumber[i] > nDfsNumber[u] ) { - nLowNumber[i] = nDfsNumber[u]; - } - } - } - goto advance_ring; - } else { - cNeighNumb[i] = 0; - } - - /* back up */ - if ( nDfsNumber[i] == nLowNumber[i] ) { - /* found a ring system */ - nNumRingSystems ++; - - /* unwind nRingStack[] down to i */ - - /* count atoms in a ring system */ - for ( nNumAtInRingSystem = 0, j = nTopRingStack; 0 <= j; j -- ) { - nNumAtInRingSystem ++; - if ( i == (int)nRingStack[j] ) { - break; - } - } - while ( nTopRingStack >= 0 ) { - j = (int)nRingStack[nTopRingStack--]; - at[j].nRingSystemAltBns = (AT_NUMB)nNumRingSystems; /* ring system id */ - at[j].nNumAtInRingSystemAltBns = nNumAtInRingSystem; - if ( i == j ) { - /* reached atom on the top of nStackAtom[] stack */ - break; - } - } - } else - if ( nTopStackAtom > 0 ) { - j = (int)nStackAtom[nTopStackAtom-1]; - /* inherit nLowNumber */ - if ( nLowNumber[j] > nLowNumber[i] ) { - nLowNumber[j] = nLowNumber[i]; - } - } - } while ( --nTopStackAtom >= 0 ); - } - -#endif /* } BNS_MARK_ONLY_BLOCKS != 1 */ - -exit_function: - if ( nStackAtom ) - inchi_free( nStackAtom ); - if ( nRingStack ) - inchi_free( nRingStack ); - if ( nDfsNumber ) - inchi_free( nDfsNumber ); - if ( nLowNumber ) - inchi_free( nLowNumber ); - if ( nBondStack ) - inchi_free( nBondStack ); - if ( cNeighNumb ) - inchi_free( cNeighNumb ); - return nNumRingSystems; -} - - - -/*****************************************************************************/ -int ReInitBnStructForAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bUnknAltAsNoStereo ) -{ - Vertex v, v2; - int ret, bond_type, num_to_test, j; - BNS_EDGE *pBond; - BNS_VERTEX *pAtom; - /* strip all t-groups and c-groups */ - num_to_test = 0; - if ( bUnknAltAsNoStereo ) { - for ( j = 0; j < pBNS->num_edges; j ++ ) { - pBNS->edge[j].pass = 0; - } - } - ret = ReInitBnStruct( pBNS, at, num_atoms, 0 ); - if ( ret || pBNS->num_atoms != num_atoms || pBNS->num_vertices != num_atoms || pBNS->num_bonds != pBNS->num_edges ) { - ret = BNS_REINIT_ERR; - goto exit_function; - } - /* eliminate bonds and fix st-caps */ - for ( v = 0; v < num_atoms; v ++ ) { - pAtom = pBNS->vert + v; - for ( j = 0; j < pAtom->valenceAltBns; j ++ ) { - pBond = pBNS->edge + pAtom->iedge[j]; - if ( pBond->neighbor1 == v ) { - bond_type = (at[v].bond_type[j] & BOND_TYPE_MASK); - v2 = pBond->neighbor12 ^ v; - if ( at[v].endpoint || at[v2].endpoint ) { - bond_type = 0; /* any bond to an endpoint considered non-stereogenic */ - } -#if ( FIX_EITHER_DB_AS_NONSTEREO == 1 ) - if ( bUnknAltAsNoStereo ) { - if ( bond_type == BOND_ALTERN && at[v].bond_stereo[j] == STEREO_DBLE_EITHER ) { - bond_type = 0; /* treat unknown (Either) ALT bond as non-stereo */ - } - } -#endif - switch ( bond_type ) { - - case BOND_ALTERN : - pBond->nBondTypeInpAltBns = BT_ALTERN_BOND; - num_to_test ++; - break; - - case BOND_ALT_123: - case BOND_ALT_13 : - case BOND_ALT_23 : - pBond->nBondTypeInpAltBns = BT_OTHER_ALTERN_BOND; - break; - - case BOND_TAUTOM : - pBond->nBondTypeInpAltBns = BT_TAUTOM_BOND; - break; - - case BOND_ALT12NS: - pBond->nBondTypeInpAltBns = BT_ALTERN_NS_BOND; - break; - - case 0: - case BOND_SINGLE : - case BOND_DOUBLE : - case BOND_TRIPLE : - pBond->nBondTypeInpAltBns = BT_IGNORE_BOND; - break; - - default: - pBond->nBondTypeInpAltBns = BT_IGNORE_BOND; - break; - - } - pBond->nBondNonStereoAltBns = - pBond->nBlockNumberAltBns = - pBond->nNumAtInBlockAltBns = 0; - -#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) - pBond->forbidden &= pBNS->edge_forbidden_mask; -#endif - } - } - pAtom->bCutVertexAltBns = - pAtom->nRingSystemAltBns = - pAtom->nNumAtInRingSystemAltBns = - pAtom->nBlockSystemAltBns = 0; - } - - return num_to_test; -exit_function: - return ret; -} - - - -/*****************************************************************************/ -int MarkNonStereoAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bUnknAltAsNoStereo ) -{ - int num_bonds = pBNS->num_bonds; - int ret; - int ibond, ib1, ib2; - BNS_EDGE *pBond; - Vertex iat1, iat2; - - ret = 0; - - if ( pBNS->num_atoms != num_atoms || pBNS->num_vertices != num_atoms || pBNS->num_bonds != pBNS->num_edges ) { - ret = BNS_REINIT_ERR; - goto exit_function; - } - if ( bUnknAltAsNoStereo ) { - for ( ibond=0; ibond < num_bonds; ibond ++ ) { - pBond = pBNS->edge + ibond; - if ( pBond->nBondTypeInpAltBns != BT_ALTERN_BOND && pBond->nBondTypeInpAltBns != BT_IGNORE_BOND ) { - continue; - } - iat1 = pBond->neighbor1; - iat2 = pBond->neighbor12 ^ iat1; - ib1 = pBond->neigh_ord[0]; - ib2 = pBond->neigh_ord[1]; - if ( /* alt bond non-adjacent to a taut. endpoint: */ - (pBond->nBondTypeInpAltBns == BT_ALTERN_BOND && - pBond->nNumAtInBlockAltBns <= 3 ) /* non-ring bond */ || - /* alt bond adjacent to a taut. endpoint: */ - (pBond->nBondTypeInpAltBns == BT_IGNORE_BOND && - (at[iat1].bond_type[ib1] & BOND_TYPE_MASK) == BOND_ALTERN ) - ) { - if ( (at[iat1].bond_type[ib1] & BOND_TYPE_MASK) == BOND_ALTERN ) { - /* bond_type = BOND_ALT12NS; */ - at[iat1].bond_stereo[ib1] = - at[iat2].bond_stereo[ib2] =STEREO_DBLE_EITHER; - ret ++; - } - } - } - } else { - for ( ibond=0; ibond < num_bonds; ibond ++ ) { - pBond = pBNS->edge + ibond; - if ( pBond->nBondTypeInpAltBns != BT_ALTERN_BOND && pBond->nBondTypeInpAltBns != BT_IGNORE_BOND ) { - continue; - } - iat1 = pBond->neighbor1; - iat2 = pBond->neighbor12 ^ iat1; - ib1 = pBond->neigh_ord[0]; - ib2 = pBond->neigh_ord[1]; - if ( /* alt bond non-adjacent to a taut. endpoint: */ - (pBond->nBondTypeInpAltBns == BT_ALTERN_BOND && - pBond->nNumAtInBlockAltBns <= 3 ) /* non-ring bond */ || - /* alt bond adjacent to a taut. endpoint: */ - (pBond->nBondTypeInpAltBns == BT_IGNORE_BOND && - (at[iat1].bond_type[ib1] & BOND_TYPE_MASK) == BOND_ALTERN ) - ) - { - at[iat1].bond_type[ib1] = - at[iat2].bond_type[ib2] =BOND_ALT12NS; - ret ++; - } - } - } - -exit_function: - - return ret; -} - -#if ( READ_INCHI_STRING == 1 ) -/*****************************************************************************/ -#ifndef RI_ERR_ALLOC -/* from ichirvrs.h */ -#define RI_ERR_ALLOC (-1) -#define RI_ERR_SYNTAX (-2) -#define RI_ERR_PROGR (-3) -#endif - - - -/*****************************************************************************/ -int bHasChargedNeighbor( inp_ATOM *at, int iat ) -{ - int i; - for( i = 0; i < at[iat].valence; i ++ ) { - if ( at[(int)at[iat].neighbor[i]].charge ) - return 1; - } - return 0; -} - - - -/********************************************************************************* - *num_protons_to_add = nToBeRemovedByNormFromRevrs - - nToBeRemovedByNormFromRevrs > 0: less protons should be allowed to be - added by the Normalization of the Reconstructed Structure - nToBeRemovedByNormFromRevrs < 0: prepare more H(+) to be removed by - the InChI Normalization of the Reconstructed Structure - - OrigStruct -> NormOrig + n(orig)*H(+) - RevrStruct -> NormRevr + n(revr)*H(+) - nToBeRemovedByNormFromRevrs = n(orig) - n(revr) [each may be negative] - - n(orig) > n(revr) or nToBeRemovedByNormFromRevrs > 0 means: - ----------------------------------------------------------- - - Too many protons were added by the Normalization to the Reconstructed Structure - (a) n(revr) < 0 => protons were added while they should not have been added; - Solution: "neutralize" (-) charged proton acceptors by moving charges to other atoms - on the condition ADP cannot add in another way; - (b) n(orig) > n(revr) => 0 => too few protons were removed - Solution: (the easiest) attach H(+) to =O or -N< or -N= - Solution: move (+) from N or OH to an atom adjacent to (-) charge or to - an atom that is not N. - - n(orig) < n(revr) or nToBeRemovedByNormFromRevrs < 0 means: - ----------------------------------------------------------- - - Too few protons were added by the Normalization to the Reconstructed Stucture - (a) n(orig) < 0 => protons were not added while they should have been added; - Solution: move (-) to O by replacing =O with -O(-) - (b) 0 <= n(orig) < n(revr) => too many protons were removed - - Note: it is critically important to takr into account cumbersome Normalization - Total Charge: if it is >= 0 then no H(+) may be removed from -OH or by ADP - However, if N(+) is present then ADP will always try to remove a proton -*********************************************************************************/ -int AddRemoveProtonsRestr( inp_ATOM *at, int num_atoms, int *num_protons_to_add, - int nNumProtAddedByRestr, INCHI_MODE bNormalizationFlags, - int num_tg, int nChargeRevrs, int nChargeInChI ) -{ - int i, j, ret = 0; - int nAtTypeTotals[ATTOT_ARRAY_LEN]; - int num_prot = *num_protons_to_add; - int type, mask, bSuccess, nTotCharge, nNumSuccess = 0; - int max_j_Aa=-1, max_j_Ar=-1; - -/* for the reference: - -#define FLAG_NORM_CONSIDER_TAUT ( FLAG_PROTON_NPO_SIMPLE_REMOVED | \ - FLAG_PROTON_NP_HARD_REMOVED | \ - FLAG_PROTON_AC_SIMPLE_ADDED | \ - FLAG_PROTON_AC_SIMPLE_REMOVED | \ - FLAG_PROTON_AC_HARD_REMOVED | \ - FLAG_PROTON_AC_HARD_ADDED | \ - FLAG_PROTON_SINGLE_REMOVED | \ - FLAG_PROTON_CHARGE_CANCEL ) - -#define FLAG_FORCE_SALT_TAUT ( FLAG_PROTON_NP_HARD_REMOVED | \ - FLAG_PROTON_AC_HARD_REMOVED | \ - FLAG_PROTON_AC_HARD_ADDED ) - -*/ - /* if ChargeRevrs > nChargeInChI then we should prevent proton addition or facilitate proton removal - a typical case is (=) on N or O instead of C(-) - - if ChargeRevrs < nChargeInChI then we should prevent proton removal or facilitate proton addition - */ - - mark_at_type( at, num_atoms, nAtTypeTotals ); - for ( i = nTotCharge = 0; i < num_atoms; i ++ ) { - nTotCharge += at[i].charge; - } - /* size for SimpleAddAcidicProtons() */ - for ( max_j_Aa = 0; AaTypMask[2*max_j_Aa]; max_j_Aa ++ ) - ; - /* size for SimpleRemoveAcidicProtons */ - for ( max_j_Ar = 0; ArTypMask[2*max_j_Ar]; max_j_Ar ++ ) - ; - if ( num_prot < 0 && nAtTypeTotals[ATTOT_TOT_CHARGE]-nNumProtAddedByRestr <= 0 ) { - /* remove proton(s) */ - /* use test from SimpleAddAcidicProtons() to test whether removal of H(+) from =C-OH, etc. is correct */ - for ( i = 0; i < num_atoms && num_prot; i ++ ) { - /* choose an atom */ - if ( at[i].sb_parity[0] || at[i].p_parity || at[i].charge || - !at[i].num_H || at[i].radical || bHasChargedNeighbor( at, i ) ) { - continue; - } - /* try to remove a proton and check whether InChI would add it back */ - at[i].charge --; - at[i].num_H --; - type = GetAtomChargeType( at, i, NULL, &mask, 0 ); - at[i].charge ++; - at[i].num_H ++; - - if ( type ) { - for ( bSuccess = 0, j = 0; j < max_j_Aa; j ++ ) { - if ( bSuccess = (type & AaTypMask[2*j]) && (mask && AaTypMask[2*j+1]) ) { - break; /* the proton may be added to this atom */ - } - } - if ( bSuccess ) { - type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ - at[i].charge --; - at[i].num_H --; - type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ - num_prot ++; /* success */ - nNumSuccess ++; - } - } - } - } - if ( num_prot < 0 && num_tg && nAtTypeTotals[ATTOT_TOT_CHARGE]-nNumProtAddedByRestr <= 0 ) { - /* alternative proton removal: O=C-NH => (-)O-C=N, O and N are taut. endpoints */ - int endp2, centp, k, i0, k0; - for ( i = 0; i < num_atoms; i ++ ) { - /* choose an atom */ - if ( !at[i].endpoint || at[i].sb_parity[0] || at[i].p_parity || - at[i].radical || at[i].charge || bHasChargedNeighbor( at, i ) ) { - continue; - } - /* looking for tautomeric =O */ - if ( 1 != at[i].valence || BOND_TYPE_DOUBLE != at[i].bond_type[0] || at[i].num_H || - 2 != get_endpoint_valence( at[i].el_number ) ) { - continue; - } - centp = at[i].neighbor[0]; - if ( at[centp].sb_parity[0] || at[centp].p_parity || !is_centerpoint_elem( at[centp].el_number ) ) { - continue; - } - /* found a possible centerpoint, looking for -NH endpoint */ - for ( k = 0; k < at[centp].valence; k ++ ) { - if ( at[centp].bond_type[k] != BOND_TYPE_SINGLE ) { - continue; - } - endp2 = at[centp].neighbor[k]; - if ( at[endp2].endpoint != at[i].endpoint || - !at[endp2].num_H || at[endp2].charge || - at[endp2].sb_parity[0] || at[endp2].p_parity || - at[endp2].valence != at[endp2].chem_bonds_valence || - 3 != at[endp2].chem_bonds_valence + at[endp2].num_H || - 3 != get_endpoint_valence( at[endp2].el_number ) ) { - continue; - } - /* find bonds in reciprocal ajacency lists */ - for ( i0 = 0; i0 < at[centp].valence && i != at[centp].neighbor[i0]; i0 ++ ) - ; - for ( k0 = 0; k0 < at[endp2].valence && centp != at[endp2].neighbor[k0]; k0 ++ ) - ; - if ( i0 == at[centp].valence || k0 == at[endp2].valence ) { - return RI_ERR_PROGR; - } - /* -NH has been found */ - type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ - type = GetAtomChargeType( at, endp2, nAtTypeTotals, &mask, 1 ); /* subtract at[endp2] */ - - at[i].bond_type[0] --; - at[centp].bond_type[i0] --; - at[i].chem_bonds_valence --; - at[i].charge --; - - at[endp2].bond_type[k0] ++; - at[centp].bond_type[k] ++; - at[endp2].chem_bonds_valence ++; - at[endp2].num_H --; - - num_prot ++; - nNumSuccess ++; - - type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add at[i] */ - type = GetAtomChargeType( at, endp2, nAtTypeTotals, &mask, 0 ); /* add at[endp2] */ - } - } - } - if ( num_prot > 0 ) { - /* add protons */ - /* 1. Use test from SimpleRemoveAcidicProtons() to test whether addition of H(+) to =C-O(-), etc. is correct */ - for ( i = 0; i < num_atoms && num_prot && nAtTypeTotals[ATTOT_TOT_CHARGE]-nNumProtAddedByRestr >= 0; i ++ ) { - /* choose an atom */ - if ( at[i].sb_parity[0] || at[i].p_parity || at[i].num_H || - at[i].charge != -1 || at[i].radical || bHasChargedNeighbor( at, i ) ) { - continue; - } - /* try to add a proton and check whether InChI would remove it back */ - at[i].charge ++; - at[i].num_H ++; - type = GetAtomChargeType( at, i, NULL, &mask, 0 ); - at[i].charge --; - at[i].num_H --; - - if ( type ) { - for ( bSuccess = 0, j = 0; j < max_j_Ar; j ++ ) { - if ( bSuccess = (type & ArTypMask[2*j]) && (mask && ArTypMask[2*j+1]) ) { - break; - } - } - if ( bSuccess ) { - type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ - at[i].charge ++; - at[i].num_H ++; - type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ - num_prot --; /* success */ - nNumSuccess ++; - } - } - } - /* 2. Use test from SimpleRemoveHplusNPO() */ - for ( i = 0; i < num_atoms && num_prot; i ++ ) { - /* choose an atom */ - if ( at[i].sb_parity[0] || at[i].p_parity || - at[i].charge || at[i].radical || bHasChargedNeighbor( at, i ) ) { - continue; - } - /* try to add a proton and check whether InChI would remove it back */ - at[i].num_H ++; - at[i].charge ++; - bSuccess = (PR_SIMPLE_TYP & (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) && - (PR_SIMPLE_MSK & mask ); - at[i].num_H --; /* failed */ - at[i].charge --; - if ( bSuccess ) { - type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ - at[i].num_H ++; - at[i].charge ++; - type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ - num_prot --; /* succeeded */ - nNumSuccess ++; - } - } - } - - if ( num_prot < 0 && (bNormalizationFlags & FLAG_PROTON_AC_HARD_ADDED) && 1 == num_tg && - nAtTypeTotals[ATTOT_TOT_CHARGE]-nNumProtAddedByRestr <= 0 ) { - /* try to remove protons from tautomeric N (specific ADP must be present) */ - int nNumAcceptors_DB_O=0, nNumDonors_SB_NH=0, num_max, num_success; - for ( i = 0; i < num_atoms; i ++ ) { - /* choose an atom */ - if ( !at[i].endpoint || at[i].radical || - at[i].sb_parity[0] || at[i].p_parity || bHasChargedNeighbor( at, i ) ) { - continue; - } - type = GetAtomChargeType( at, i, NULL, &mask, 0 ); - if ( (type & AA_HARD_TYP_CO) && (mask & AA_HARD_MSK_CO) ) { - nNumAcceptors_DB_O ++; - } else - if ( (type == ATT_ATOM_N ) && (mask == ATBIT_NP_H) && !at[i].charge && - at[i].valence == at[i].chem_bonds_valence ) { - nNumDonors_SB_NH ++; - } - } - num_max = inchi_min( nNumAcceptors_DB_O, nNumDonors_SB_NH ); - for ( i = 0, num_success = 0; i < num_atoms && num_success < num_max && num_prot < 0; i ++ ) { - /* choose an atom */ - if ( !at[i].endpoint|| at[i].radical || at[i].sb_parity[0] || - at[i].p_parity || bHasChargedNeighbor( at, i ) ) { - continue; - } - type = GetAtomChargeType( at, i, NULL, &mask, 0 ); - if ( (type == ATT_ATOM_N ) && (mask == ATBIT_NP_H) && !at[i].charge && - at[i].valence == at[i].chem_bonds_valence ) { - type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ - at[i].num_H --; - at[i].charge --; - type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ - num_prot ++; - num_success ++; - nNumSuccess ++; - } - } - } -/*exit_function:*/ - *num_protons_to_add = num_prot; - return ret<0? ret : nNumSuccess; -} - - - -/*****************************************************************************/ -int AddRemoveIsoProtonsRestr( inp_ATOM *at, int num_atoms, NUM_H num_protons_to_add[], int num_tg ) -{ - int i, j, k, n, ret = 0; - int nNumSuccess = 0, min_at, max_at, num_H, num_iso_H, num_expl_H, num_expl_iso_H; - int iCurIso; /* 0=> 1H, 1=> D, 2=> T */ - int iCurMode, iCurMode1, iCurMode2; /* 0=> Not Endpoints, 1=> Endpoints */ - static U_CHAR el_number_H = 0; - - /* distribute isotopes from heaviest to lightest; pick up atoms in order 1. Not endpoints; 2. Endpoints */ - iCurMode1 = 0; - iCurMode2 = num_tg ? 1 : 0; - if ( !el_number_H ) { - el_number_H = (U_CHAR) get_periodic_table_number( "H" ); - } - for ( iCurMode = iCurMode1; iCurMode <= iCurMode2; iCurMode ++ ) { - for ( iCurIso = 2; 0 <= iCurIso; iCurIso -- ) { - /* check for isotopic H to add */ - if ( !num_protons_to_add[iCurIso] ) { - continue; - } - if ( 0 > num_protons_to_add[iCurIso] ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - /* limits for atom scanning */ - min_at = 0; - max_at = num_atoms; - /* cycle withio the limits */ - for ( i = min_at; i < max_at && 0 < num_protons_to_add[iCurIso]; i ++ ) { - /* pick an atom */ - if ( iCurMode ) { - if ( at[i].endpoint ) - j = i; /* atom number */ - else - continue; - } else - if ( !at[i].endpoint && - 1 == bHeteroAtomMayHaveXchgIsoH( at, i ) ) { /* atom number */ - j = i; - } else - if ( at[i].el_number == el_number_H && at[i].charge == 1 && - !at[i].valence && !at[i].radical && !at[i].iso_atw_diff ) { - /* proton, not isotopic; make it isotopic */ - at[i].iso_atw_diff = 1 + iCurIso; - num_protons_to_add[iCurIso] --; - nNumSuccess ++; - continue; - } else { - continue; - } - /* j is the atom number */ - /* count implicit H */ - num_H = at[j].num_H; - num_iso_H = NUM_ISO_H(at,j); - while ( num_H > 0 && num_protons_to_add[iCurIso] > 0 ) { - /* substitute one implicit H with an isotopic atom H */ - at[j].num_iso_H[iCurIso] ++; - at[j].num_H --; - num_protons_to_add[iCurIso] --; - num_H --; - num_iso_H ++; - nNumSuccess ++; - } - /* count explicit H */ - num_expl_H = num_expl_iso_H = 0; - for ( k = 0; k < at[j].valence && num_atoms <= (n=at[j].neighbor[k]); k ++ ) { - num_expl_H += (0 == at[n].iso_atw_diff); - num_expl_iso_H += (0 != at[n].iso_atw_diff); - } - while ( num_expl_H > 0 && num_protons_to_add[iCurIso] > 0 ) { - /* substitute one explicit H with an isotopic atom H */ - n = at[j].neighbor[num_expl_H]; - if ( at[n].iso_atw_diff ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - at[n].iso_atw_diff = 1 + iCurIso; - num_expl_H --; - num_expl_iso_H ++; - num_protons_to_add[iCurIso] --; - nNumSuccess ++; - } - } - } - } -exit_function: - return ret<0? ret : nNumSuccess; -} - -#endif - diff --git a/INCHI-1-SRC/INCHI/common/ichi_io.h b/INCHI-1-SRC/INCHI/common/ichi_io.h deleted file mode 100644 index 40e3771..0000000 --- a/INCHI-1-SRC/INCHI/common/ichi_io.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - - INCHI_IOSTREAM OPERATIONS - - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - -void inchi_ios_init(INCHI_IOSTREAM *ios, int io_type, FILE *f); -void inchi_ios_flush(INCHI_IOSTREAM *ios); -void inchi_ios_flush2(INCHI_IOSTREAM *ios, FILE *f2); -void inchi_ios_close(INCHI_IOSTREAM *ios); -void inchi_ios_reset(INCHI_IOSTREAM *ios); - -int inchi_ios_gets( char *szLine, int len, INCHI_IOSTREAM *ios, int *bTooLongLine ); -int inchi_ios_getsTab( char *szLine, int len, INCHI_IOSTREAM *ios, int *bTooLongLine ); -int inchi_ios_getsTab1( char *szLine, int len, INCHI_IOSTREAM *ios, int *bTooLongLine ); - -int inchi_ios_print( INCHI_IOSTREAM *ios, const char* lpszFormat, ... ); -int inchi_ios_print_nodisplay( INCHI_IOSTREAM *ios, const char* lpszFormat, ... ); - -/* Print to string buffer or to file+stderr */ -int inchi_ios_eprint( INCHI_IOSTREAM *ios, const char* lpszFormat, ... ); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - - PLAIN FILE OPERATIONS - - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -/* Print to file, echoing to stderr */ -int inchi_fprintf( FILE* f, const char* lpszFormat, ... ); -int inchi_print_nodisplay( FILE* f, const char* lpszFormat, ... ); - -char* inchi_fgetsLf( char* line, int line_len, FILE* inp ); -int inchi_fgetsLfTab( char *szLine, int len, FILE *f ); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - - - diff --git a/INCHI-1-SRC/INCHI/common/ichicano.h b/INCHI-1-SRC/INCHI/common/ichicano.h deleted file mode 100644 index 3c498ee..0000000 --- a/INCHI-1-SRC/INCHI/common/ichicano.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHICANO_H__ -#define __INCHICANO_H__ - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - -int GetCanonLengths(int num_at, sp_ATOM* at, ATOM_SIZES *s, T_GROUP_INFO *t_group_info ); - -int AllocateCS(CANON_STAT *pCS, int num_at, int num_at_tg, int nLenCT, int nLenCTAtOnly, - int nLenLinearCTStereoDble, int nLenLinearCTIsotopicStereoDble, - int nLenLinearCTStereoCarb, int nLenLinearCTIsotopicStereoCarb, - int nLenLinearCTTautomer, int nLenLinearCTIsotopicTautomer, - int nLenIsotopic, INCHI_MODE nMode, BCN *pBCN ); - -int DeAllocateCS(CANON_STAT *pCS ); - -void DeAllocBCN(BCN *pBCN ); - -int Canon_INChI(int num_atoms, int num_at_tg, sp_ATOM* at, - CANON_STAT* pCS, INCHI_MODE nMode, int bTautFtcn); -int GetBaseCanonRanking(int num_atoms, int num_at_tg, sp_ATOM* at[], - T_GROUP_INFO *t_group_info, ATOM_SIZES s[], BCN *pBCN, - struct tagInchiTime *ulTimeOutTime, int bFixIsoFixedH ); -int bCanonIsFinerThanEquitablePartition( int num_atoms, sp_ATOM* at, AT_RANK *nSymmRank ); -int UpdateFullLinearCT(int num_atoms, int num_at_tg, sp_ATOM* at, AT_RANK *nRank, AT_RANK *nAtomNumber, - CANON_STAT* pCS, int bFirstTime ); - -int FixCanonEquivalenceInfo(int num_at_tg, AT_RANK *nSymmRank, AT_RANK *nCurrRank, - AT_RANK *nTempRank, AT_NUMB *nAtomNumber, int *bChanged); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /* __INCHICANO_H__ */ diff --git a/INCHI-1-SRC/INCHI/common/ichicant.h b/INCHI-1-SRC/INCHI/common/ichicant.h deleted file mode 100644 index 107b9ec..0000000 --- a/INCHI-1-SRC/INCHI/common/ichicant.h +++ /dev/null @@ -1,410 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHICANT_H__ -#define __INCHICANT_H__ - -/******************************************************/ -/* */ -/* Canonicalization definitions */ -/* */ -/******************************************************/ -#include "ichisize.h" - - -#ifndef INCHI_US_SHORT_DEF -typedef signed short S_SHORT; -typedef unsigned short U_SHORT; -#define INCHI_US_SHORT_DEF -#endif - -/*typedef unsigned long INCHI_MODE;*/ - -typedef union tagSplitLong { - unsigned long ul; - U_SHORT us[2]; -}SU_LONG; - -#define _HI 1 /* Intel platform */ -#define _LO 0 - -#define NEIGH_LIST_LEN 4 -#define U_LONG_LEN 2 - -#ifndef defined_NEIGH_LIST -typedef AT_RANK *NEIGH_LIST; -#define defined_NEIGH_LIST -#endif - -typedef struct tagEQUIV_INFO { - int nNumSets; - int *nCutVertexAtom; /* cut-vertex atom for the set of equivalent atoms */ - int *nFirstInSet; /* first of equivalent atoms in the connected to the cut-vertex atom parts of the structure */ - int *nNumInSet; /* number of the equivalent atoms connected to the cut-vertex atom */ - int *nAtomNo; /* eqivalent atom number */ - int *nAddToRank; /* number to add to the rank to normalize */ -} EQUIV_INFO; - -#define MOL_PART_MASK (~0x0U ^ 0x07U) - - -typedef struct tagAtData_dch { - char element[3]; - int valence; - }AT_DATA; - - -#define MAXVAL 20 /* maximum valence */ - -#define ATOM_EL_LEN 6 - -typedef struct tagAtomInvariantBytes { - S_CHAR cNotExactlyHillOrderNumber; - S_CHAR cNumberOfConnections; - /* S_CHAR cNumberOfNonHydrogenBonds; */ - S_CHAR cAtomicNumber; -#if ( HYDROGENS_IN_INIT_RANKS == 1 ) - S_CHAR cNumberOfAttachedHydrogens; -#endif -} ATOM_INVARIANT_BYTES; - -typedef struct tagAtomInvariant { - /* non-isotopic part */ -#if ( USE_DISTANCES_FOR_RANKING == 1 ) - AT_RANK nDistanceFromTerminal; -#endif - ATOM_INVARIANT_BYTES b; - AT_RANK cNum_tautomer; /* 0 or for tautomer endpoint: number of endpoints in the group */ - AT_RANK cNum_tautomer_num[T_NUM_NO_ISOTOPIC]; /* 0 or numbers from t_gtroup */ - /* isotopic part */ - AT_ISO_SORT_KEY iso_sort_key; - AT_RANK cNum_tautomer_iso[T_NUM_ISOTOPIC]; /* 0 or numbers from t_group */ -} ATOM_INVARIANT; -/**********************************/ -typedef enum tagAtInvariantIndexes { - AT_INV_HILL_ORDER, - AT_INV_NUM_CONNECTIONS, - AT_INV_NUM_H, - /* for endpoint + undirected graph, otherwise 0 */ - AT_INV_NUM_TG_ENDPOINTS, - AT_INV_TG_NUMBERS, /* num H, num (-) */ - AT_INV_NUM_H_FIX = AT_INV_TG_NUMBERS+T_NUM_NO_ISOTOPIC, - AT_INV_BREAK1, - /* here compare iso sort key */ - AT_INV_TAUT_ISO = AT_INV_BREAK1, - AT_INV_LENGTH = AT_INV_TAUT_ISO + T_NUM_ISOTOPIC -} AT_INV_INDEXES; - -typedef struct tagAtomInvariant2 { - AT_NUMB val[AT_INV_LENGTH]; - AT_ISO_SORT_KEY iso_sort_key; - S_CHAR iso_aux_key; -} ATOM_INVARIANT2; - -/******************* Partition **********************************/ -typedef struct tagPartition { - AT_RANK *Rank; - AT_NUMB *AtNumber; -} Partition; - -/********************* BFCN *************************************/ -typedef struct tagFixHOrTautCanonNumbering { - - int num_at_tg; /* = num_atoms for non-taut */ - int num_atoms; - int nCanonFlags; - NEIGH_LIST *NeighList; /* length = num_at_tg */ - /****************************/ - /* base structure */ - /****************************/ - AT_RANK *LinearCt; /* connection table atoms (+taut. groups, directed graph)*/ - int nLenLinearCtAtOnly; - int nLenLinearCt; - int nMaxLenLinearCt; - - Partition PartitionCt; /* canonical numbering */ - AT_RANK *nSymmRankCt; /* orbits */ - - /* orig. fixed by tautomerism H positions */ - NUM_H *nNumHOrig; /* original H atoms positions + taut. info, excluding tautomeric H */ - NUM_H *nNumH; /* canonical H atoms positions + taut. info, excluding tautomeric H */ - int nLenNumH; /* length = num_atoms + 2*num_taut_groups */ - - /* fixed H: original positions of tautomeric H; exists obly for tautomeric structures */ - NUM_H *nNumHOrigFixH; /* original fixed positions of tautomeric H */ - NUM_H *nNumHFixH; /* canonical fixed positions of tautomeric H */ - int nLenNumHFixH; /* length = num_atoms */ - - /*******************************************************************************/ - /* the following exists only if isotopic and isotopic results requested */ - /*******************************************************************************/ - Partition PartitionCtIso; /* canonical numbering of isotopic base structure, defined later */ - AT_RANK *nSymmRankCtIso; /* orbits of isotopic structure */ - AT_ISO_SORT_KEY *iso_sort_keys; /* original isotopic sort keys for atoms and taut groups */ - AT_ISO_SORT_KEY *iso_sort_keysOrig; /* canonical isotopic sort keys for atoms and taut groups */ - int len_iso_sort_keys; - S_CHAR *iso_exchg_atnos; /* canonical: 0=> tautomeric or may have isotopic H exchanged */ - S_CHAR *iso_exchg_atnosOrig; /* original: 0=> tautomeric or may have isotopic H exchanged */ - -} FTCN; - -/******************** BCN *************************************/ -typedef struct tagBaseCanonNumbering { - - AT_RANK **pRankStack; - int nMaxLenRankStack; - int num_max; /* allocated nRank[] arrays lengths in pRankStack */ - int num_at_tg; /* all of the following arrays have this length */ - int num_atoms; - struct tagInchiTime *ulTimeOutTime; - FTCN ftcn[TAUT_NUM]; - -} BCN; - -/*********************************** - * - * CANON_STAT - */ -typedef struct tagCanonStat { - /* statistics */ - long lNumBreakTies; - long lNumNeighListIter; - long lNumTotCT; - long lNumDecreasedCT; - long lNumRejectedCT; - long lNumEqualCT; - struct tagInchiTime *ulTimeOutTime; - long lTotalTime; - - /* control */ - int bFirstCT; - int bKeepSymmRank; - int bStereoIsBetter; - - int nCanonFlags; - - /* data : */ - - AT_NUMB *LinearCT; /* connection table only */ - AT_ISOTOPIC *LinearCTIsotopic; - AT_ISO_TGROUP *LinearCTIsotopicTautomer; - AT_STEREO_DBLE *LinearCTStereoDble; - AT_STEREO_CARB *LinearCTStereoCarb; - AT_STEREO_DBLE *LinearCTStereoDbleInv; - AT_STEREO_CARB *LinearCTStereoCarbInv; - AT_STEREO_DBLE *LinearCTIsotopicStereoDble; - AT_STEREO_CARB *LinearCTIsotopicStereoCarb; - AT_STEREO_DBLE *LinearCTIsotopicStereoDbleInv; - AT_STEREO_CARB *LinearCTIsotopicStereoCarbInv; - AT_TAUTOMER *LinearCTTautomer; /* minimal */ - -/* second copies of line notation arrays */ - - AT_NUMB *LinearCT2; /* to save non-isotopic CT */ - - int nLenLinearCTStereoDble; - int nLenLinearCTStereoDbleInv; - int nMaxLenLinearCTStereoDble; /* new */ - - int bCmpStereo; /* 0 => no stereo to invert; - 1 => StereoCtInv < StereoCt; - 2 => StereoCtInv = StereoCt; - 3 => StereoCtInv > StereoCt; - */ - int nLenLinearCTStereoCarb; - int nLenLinearCTStereoCarbInv; - int nMaxLenLinearCTStereoCarb; /* new */ - - int nLenLinearCTIsotopic; - int nMaxLenLinearCTIsotopic; - - int nLenLinearCTIsotopicTautomer; - int nMaxLenLinearCTIsotopicTautomer; - - int nLenLinearCT; /* connection table only */ - int nLenLinearCT2; /* connection table only, non-isotopic result */ - int nLenLinearCTAtOnly; /* connection table only without tautomeric pseudoatoms */ - int nLenLinearCTAtOnly2; /* connection table only, non-isotopic result without tautomeric pseudoatoms */ - int nMaxLenLinearCT; /* connection table only */ - - int nLenLinearCTTautomer; - int nMaxLenLinearCTTautomer; - - int bCmpIsotopicStereo; /* 0 => no stereo to invert; - 1 => StereoCtInv < StereoCt; - 2 => StereoCtInv = StereoCt; - 3 => StereoCtInv > StereoCt; - */ - int nLenLinearCTIsotopicStereoDble; - int nLenLinearCTIsotopicStereoDbleInv; - int nMaxLenLinearCTIsotopicStereoDble; - - int nLenLinearCTIsotopicStereoCarb; /* new */ - int nLenLinearCTIsotopicStereoCarbInv; /* new */ - int nMaxLenLinearCTIsotopicStereoCarb; - S_CHAR *bRankUsedForStereo; /* canon. rank used for stereo mapping */ - S_CHAR *bAtomUsedForStereo; /* 0 if not a stereo atom or during a canon. rank being mapped on this atom; */ - /* STEREO_AT_MARK if an unpapped stereogenic atom */ - /* or a number of stereogenic bonds adjacent to an atom */ - - AT_RANK *nPrevAtomNumber; - - AT_RANK *nCanonOrd; /* atom numbers in order of increasing canon. ranks */ - AT_RANK *nSymmRank; /* symmetry numbers in order of atoms */ - AT_RANK *nCanonOrdTaut; /* t-group numbers numbers in order of increasing canon. ranks */ - AT_RANK *nSymmRankTaut; /* t-group symmetry numbers in order of t-groups */ - - AT_RANK *nCanonOrdStereo; /* atom numbers in order of increasing canon. ranks */ - AT_RANK *nCanonOrdStereoInv; /* atom numbers in order of increasing canon. ranks */ - AT_RANK *nCanonOrdStereoTaut; /* t-group numbers in order of increasing canon. ranks */ - - AT_RANK *nSymmRankIsotopic; - AT_RANK *nCanonOrdIsotopic; /* atom numbers in order of increasing canon. ranks */ - AT_RANK *nSymmRankIsotopicTaut; /* !!! */ - AT_RANK *nCanonOrdIsotopicTaut; /*/ t-group numbers in order of increasing canon. ranks */ - - AT_RANK *nCanonOrdIsotopicStereo; - AT_RANK *nCanonOrdIsotopicStereoInv; - AT_RANK *nCanonOrdIsotopicStereoTaut; /* !!! */ - - /* actual lengths if successfully calculated */ - - int nLenCanonOrd; /* Superceded by any of the following > 0 */ - int nLenCanonOrdTaut; /* !!! Superceded by any of the following > 0 */ - int nLenCanonOrdIsotopic; - int nLenCanonOrdIsotopicTaut; /* !!! */ - int nLenCanonOrdStereo; - int nLenCanonOrdStereoTaut; /* !!! */ - int nLenCanonOrdIsotopicStereo; - int nLenCanonOrdIsotopicStereoTaut; /* !!! */ - - /* other */ - - int bHasIsotopicInTautomerGroups; - T_GROUP_INFO *t_group_info; - int bIgnoreIsotopic; - int bDoubleBondSquare; /* 0 or 2 */ - INCHI_MODE nMode; -#if ( bRELEASE_VERSION == 0 ) - int bExtract; /* for debug only */ -#endif - NEIGH_LIST *NeighList; - BCN *pBCN; - S_CHAR *nNum_H; /* number of terminal hydrogen atoms on each atom except tautomeric [num_atoms], in order of canonical numbers */ - S_CHAR *nNum_H_fixed;/* number of terminal hydrogen atoms on tautomeric atoms (for non-atautomeric representation) [num_atoms] */ - S_CHAR *nExchgIsoH; -} CANON_STAT; - -/**************************************************/ -typedef struct tagCanonData { - - /* same names/types as in ConTable; here the order is from original numbering */ - - AT_NUMB *LinearCT; /* output ?? */ - - int nMaxLenLinearCT; - int nLenLinearCT; - int nLenCTAtOnly; - int nCanonFlags; - /* hydrogen atoms fixed in tautomeric representation: - compare before diff sign inversion: (+) <=> Ct1->() > Ct2->() */ - NUM_H *NumH; - int lenNumH; /* used length */ - int maxlenNumH; /* n + T_NUM_NO_ISOTOPIC*(n_tg-n) + 1 */ - - /* hydrogen atoms fixed in non-tautomeric representation only: - compare before diff sign inversion: (+) <=> Ct1->() > Ct2->() */ - NUM_H *NumHfixed; - int lenNumHfixed; /* used length */ - int maxlenNumHfixed; /* max length = n+1 */ - - /* isotopic atoms (without tautomeric H) and isotopic tautomeric groups */ - /* note: AT_ISO_SORT_KEY and T_GROUP_ISOWT are identical types: long */ - AT_ISO_SORT_KEY *iso_sort_key; - int len_iso_sort_key; /* used length */ - int maxlen_iso_sort_key; /* max length = n_tg+1 */ - S_CHAR *iso_exchg_atnos; - int len_iso_exchg_atnos; /* used length */ - int maxlen_iso_exchg_atnos; - - /* isotopic hydrogen atoms fixed in non-tautomeric representation only */ -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - AT_ISO_SORT_KEY *iso_sort_key_Hfixed; - int len_iso_sort_key_Hfixed; /* used length */ - int maxlen_iso_sort_key_Hfixed; /* max length = n+1 */ -#endif - /* auxiliary ranking */ - - AT_RANK *nAuxRank; - - struct tagInchiTime *ulTimeOutTime; /* timeout */ - -} CANON_DATA; -/**************************************************/ - -typedef struct tagCanonCounts { - long lNumBreakTies; - long lNumDecreasedCT; - long lNumRejectedCT; - long lNumEqualCT; - long lNumTotCT; - double dGroupSize; - long lNumGenerators; - long lNumStoredIsomorphisms; - -} CANON_COUNTS; -/*********************************************** - tree structure: one segment - - canon. rank - at.no orig. atom numbers on which the canon. rank has been successfully mapped - ... - at.no except the last at.no: it is not known if it has been mapped until all atoms are mapped - num.at+1 number of atoms in this segment -*/ - -typedef struct tagCurTree { - AT_NUMB *tree; - int max_len; /* allocated length of tree in sizeof(tree[0]) units */ - int cur_len; /* currently used length */ - int incr_len; /* reallocation increment */ -} CUR_TREE; - -#endif /* __INCHICANT_H__ */ diff --git a/INCHI-1-SRC/INCHI/common/ichicomn.h b/INCHI-1-SRC/INCHI/common/ichicomn.h deleted file mode 100644 index d448183..0000000 --- a/INCHI-1-SRC/INCHI/common/ichicomn.h +++ /dev/null @@ -1,296 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHICOMN_H__ -#define __INCHICOMN_H__ - -#include "ichierr.h" - - -/******************************** - * - * Globals for sorting - */ - -extern const NEIGH_LIST *pNeighList_RankForSort; -extern const ATOM_INVARIANT2 *pAtomInvariant2ForSort; -extern const AT_NUMB *pNeighborsForSort; -extern const AT_RANK *pn_RankForSort; - -extern AT_RANK nMaxAtNeighRankForSort; - -extern int nNumCompNeighborsRanksCountEql; - - -#define tsort insertions_sort - -typedef struct tagEquNeigh { - int num_to; /* number of neighbors with equal mapping ranks; one of them has min. canon. number */ - AT_RANK to_at[4]; /* to_atom neighbors #s with equal mapping ranks */ - AT_RANK from_at; /* from_at neighbor # which has min. canon. number and can be mapped on any of the above to_at[] */ - AT_RANK rank; /* equal mapping rank value */ - AT_RANK canon_rank; /* min. canon. number */ -} EQ_NEIGH; - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -/*******************************************************************/ -/* ichiisot.c */ -int unpack_iso_sort_key( AT_ISO_SORT_KEY iso_sort_key, S_CHAR *num_1H, S_CHAR *num_2H, S_CHAR *num_3H, S_CHAR *iso_atw_diff ); -AT_ISO_SORT_KEY make_iso_sort_key( int iso_atw_diff, int num_1H, int num_2H, int num_3H); -int set_atom_iso_sort_keys( int num_at, sp_ATOM *at, T_GROUP_INFO* t_group_info, int *bHasIsotopicInTautomerGroups ); -/***********************************************************************/ -/* ichisort.c */ - -void insertions_sort_NeighList_AT_NUMBERS( NEIGH_LIST base, AT_RANK *nRank ); -int insertions_sort_NeighList_AT_NUMBERS3( NEIGH_LIST base, AT_RANK *nRank ); -int insertions_sort_AT_RANK( AT_RANK *base, int num ); -void insertions_sort_NeighListBySymmAndCanonRank( NEIGH_LIST base, const AT_RANK *nSymmRank, const AT_RANK *nCanonRank ); -int CompareNeighListLex( NEIGH_LIST pp1, NEIGH_LIST pp2, const AT_RANK *nRank); -int CompareNeighListLexUpToMaxRank( NEIGH_LIST pp1, NEIGH_LIST pp2, const AT_RANK *nRank, AT_RANK nMaxAtNeighRank ); -int compare_NeighLists( const NEIGH_LIST *op1, const NEIGH_LIST *op2 ); -int CompNeighborsAT_NUMBER( const void* a1, const void* a2); -int comp_AT_RANK( const void* a1, const void* a2); -int CompRank(const void* a1, const void* a2 ); -int CompRanksOrd( const void* a1, const void* a2 ); - -int CompAtomInvariants2Only( const void* a1, const void* a2 ); -int CompAtomInvariants2( const void* a1, const void* a2 ); - -int CompNeighListRanks( const void* a1, const void* a2 ); -int CompNeighListRanksOrd( const void* a1, const void* a2 ); -int CompNeighLists( const void* a1, const void* a2 ); -int CompNeighListsUpToMaxRank( const void* a1, const void* a2 ); -int CompNeighborsRanksCountEql( const void* a1, const void* a2 ); -int CompRanksInvOrd( const void* a1, const void* a2 ); -int CompChemElemLex( const void *a1, const void *a2 ); - - - -NEIGH_LIST *CreateNeighList( int num_atoms, int num_at_tg, sp_ATOM* at, int bDoubleBondSquare, T_GROUP_INFO *t_group_info ); -NEIGH_LIST *CreateNeighListFromLinearCT( AT_NUMB *LinearCT, int nLenCT, int num_atoms ); - -void FreeNeighList( NEIGH_LIST *pp ); -int BreakAllTies( int num_atoms, int num_max, AT_RANK **pRankStack, - NEIGH_LIST *NeighList, AT_RANK *nTempRank, CANON_STAT *pCS); - -/******************************************************************************/ -/* ichimap.c */ -void switch_ptrs( AT_RANK **p1, AT_RANK **p2 ); - -int SortedEquInfoToRanks( const AT_RANK* nSymmRank, AT_RANK* nRank, const AT_RANK* nAtomNumber, int num_atoms, int *bChanged ); -int SortedRanksToEquInfo( AT_RANK* nSymmRank, const AT_RANK* nRank, const AT_RANK* nAtomNumber, int num_atoms ); - -int SetNewRanksFromNeighLists( int num_atoms, NEIGH_LIST *NeighList, AT_RANK *nRank, AT_RANK *nNewRank, - AT_RANK *nAtomNumber, int bUseAltSort, int ( *comp )(const void *, const void *) ); -int SetNewRanksFromNeighLists3( int num_atoms, NEIGH_LIST *NeighList, AT_RANK *nRank, AT_RANK *nNewRank, - AT_RANK *nAtomNumber ); -int SetNewRanksFromNeighLists4( int num_atoms, NEIGH_LIST *NeighList, AT_RANK *nRank, AT_RANK *nNewRank, - AT_RANK *nAtomNumber, AT_RANK nMaxAtRank ); -void SortNeighListsBySymmAndCanonRank( int num_atoms, NEIGH_LIST *NeighList, const AT_RANK *nSymmRank, const AT_RANK *nCanonRank ); -int SortNeighLists2( int num_atoms, AT_RANK *nRank, NEIGH_LIST *NeighList, AT_RANK *nAtomNumber ); -int DifferentiateRanks2( int num_atoms, NEIGH_LIST *NeighList, - int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, - AT_RANK *nAtomNumber, long *lNumIter, int bUseAltSort ); - -int DifferentiateRanks3( int num_atoms, NEIGH_LIST *NeighList, - int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, - AT_RANK *nAtomNumber, long *lNumIter ); -int DifferentiateRanks4( int num_atoms, NEIGH_LIST *NeighList, - int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, - AT_RANK *nAtomNumber, AT_RANK nMaxAtRank, long *lNumIter ); -int DifferentiateRanksBasic( int num_atoms, NEIGH_LIST *NeighList, - int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, - AT_RANK *nAtomNumber, long *lNumIter, int bUseAltSort ); - -int parity_of_mapped_atom2( int from_at, int to_at, const sp_ATOM *at, EQ_NEIGH *pEN, - const AT_RANK *nCanonRankFrom, const AT_RANK *nRankFrom, const AT_RANK *nRankTo ); - -int parity_of_mapped_half_bond( int from_at, int to_at, int from_neigh, int to_neigh, - sp_ATOM *at, EQ_NEIGH *pEN, - const AT_RANK *nCanonRankFrom, const AT_RANK *nRankFrom, const AT_RANK *nRankTo ); - -int HalfStereoBondParity( sp_ATOM *at, int at_no1, int i_sb_neigh, const AT_RANK *nRank ); - -int NumberOfTies( AT_RANK **pRankStack1, AT_RANK **pRankStack2, int length, int at_no1, int at_no2, AT_RANK *nNewRank, int *bAddStack, int *bMapped1 ); - -int map_an_atom2( int num_atoms, int num_max, int at_no1/*from*/, int at_no2/*to*/, - AT_RANK *nTempRank, - int nNumMappedRanks, int *pnNewNumMappedRanks, - CANON_STAT *pCS, - NEIGH_LIST *NeighList, - AT_RANK **pRankStack1, AT_RANK **pRankStack2, int *bAddStack ); -int ClearPreviousMappings( AT_RANK **pRankStack1 ); - -int SetOneStereoBondIllDefParity( sp_ATOM *at, int jc, /* atom number*/ int k /* stereo bond ord. number*/, int new_parity ); -int RemoveOneStereoBond( sp_ATOM *at, int jc, /* atom number*/ int k /* stereo bond number*/ ); -int RemoveOneStereoCenter( sp_ATOM *at, int jc /* atom number*/ ); -int RemoveCalculatedNonStereo( sp_ATOM *at, int num_atoms, int num_at_tg, - AT_RANK **pRankStack1, AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, - const AT_RANK *nSymmRank, AT_RANK *nCanonRank, - AT_RANK *nAtomNumberCanon, CANON_STAT *pCS, - int vABParityUnknown); - - -int might_change_other_atom_parity( sp_ATOM *at, int num_atoms, int at_no, AT_RANK *nRank2, AT_RANK *nRank1 ); - -int map_stereo_bonds4 ( - sp_ATOM *at, int num_atoms, int num_at_tg, int num_max, int bAllene, - const AT_RANK *nCanonRankFrom, const AT_RANK *nAtomNumberCanonFrom, /* non-stereo canon ranking */ - AT_RANK *nCanonRankTo, /* output canonical numbering*/ - const AT_RANK *nSymmRank, AT_RANK **pRankStack1/*from*/, AT_RANK **pRankStack2/*to*/, - AT_RANK *nTempRank, int nNumMappedRanksInput, - AT_RANK *nSymmStereo, NEIGH_LIST *NeighList, - CANON_STAT *pCS, CUR_TREE *cur_tree, int nNumMappedBonds, - int vABParityUnknown); - -int map_stereo_atoms4 ( - sp_ATOM *at, int num_atoms, int num_at_tg, int num_max, - const AT_RANK *nCanonRankFrom, const AT_RANK *nAtomNumberCanonFrom, AT_RANK *nCanonRankTo, /* canonical numbering to be mapped */ - const AT_RANK *nSymmRank, AT_RANK **pRankStack1/*from*/, AT_RANK **pRankStack2/*to*/, - AT_RANK *nTempRank, int nNumMappedRanksInput, - AT_RANK *nSymmStereo, NEIGH_LIST *NeighList, - CANON_STAT *pCS, CUR_TREE *cur_tree, int nNumMappedAtoms, - int vABParityUnknown); - - -int CurTreeAlloc( CUR_TREE *cur_tree, int num_atoms ); -int CurTreeReAlloc( CUR_TREE *cur_tree ); -void CurTreeFree( CUR_TREE *cur_tree ); -int CurTreeAddRank( CUR_TREE *cur_tree, AT_NUMB rank ); -int CurTreeRemoveLastRank( CUR_TREE *cur_tree ); -int CurTreeReplaceLastRank( CUR_TREE *cur_tree, AT_NUMB rank ); -int CurTreeFindTheRankPos( CUR_TREE *cur_tree, AT_NUMB rank ); -int CurTreeGetPos( CUR_TREE *cur_tree ); -int CurTreeSetPos( CUR_TREE *cur_tree, int len ); -int CurTreeAddAtom( CUR_TREE *cur_tree, int at_no ); -int CurTreeRemoveLastAtom( CUR_TREE *cur_tree ); -int CurTreeIsLastRank( CUR_TREE *cur_tree, AT_NUMB rank ); -int CurTreeIsLastAtomEqu( CUR_TREE *cur_tree, int at_no, AT_NUMB *nSymmStereo ); -int CurTreeRemoveIfLastAtom( CUR_TREE *cur_tree, int at_no ); -int CurTreeRemoveLastRankIfNoAtoms( CUR_TREE *cur_tree ); -void CurTreeKeepLastAtomsOnly( CUR_TREE *cur_tree, int tpos, int shift ); - - -void SetUseAtomForStereo( S_CHAR *bAtomUsedForStereo, sp_ATOM *at, int num_atoms ); - -int nJoin2Mcrs( AT_RANK *nEqArray, AT_RANK n1, AT_RANK n2 ); -AT_RANK nGetMcr( AT_RANK *nEqArray, AT_RANK n ); -int bUniqueAtNbrFromMappingRank( AT_RANK **pRankStack, AT_RANK nAtRank, AT_NUMB *nAtNumber ); - -int Next_SB_At_CanonRanks2( AT_RANK *canon_rank1, AT_RANK *canon_rank2, /* canonical numbers */ - AT_RANK *canon_rank1_min, AT_RANK *canon_rank2_min, - int *bFirstTime, S_CHAR *bAtomUsedForStereo, - const ppAT_RANK pRankStack1, const ppAT_RANK pRankStack2, - const AT_RANK *nCanonRankFrom, const AT_RANK *nAtomNumberCanonFrom, - const sp_ATOM *at, int num_atoms, int bAllene ); - -int Next_SC_At_CanonRank2( AT_RANK *canon_rank1, /* 1st call input: largest canon number mapped so far or 0 */ - /* output: suggested canon. rank > than input if success */ - AT_RANK *canon_rank1_min, /* 1st call:0 next calls: first tried canon. number */ - int *bFirstTime, /* 1 at the time of the 1st call */ - S_CHAR *bAtomUsedForStereo, /* STEREO_AT_MARK if the atom has not been mapped yet */ - const ppAT_RANK pRankStack1, /* mapping ranks/sort order of atoms with canon. numbers (from) */ - const ppAT_RANK pRankStack2, /* mapping ranks/sort order of atoms with stereo (to) */ - const AT_RANK *nAtomNumberCanonFrom, /* sorted order of the canon. numbers */ - int num_atoms ); - -int NextStereoParity2Test( int *stereo_bond_parity, int *sb_parity_calc, - int nNumBest, int nNumWorse, int nNumUnkn, int nNumUndf, int nNumCalc, - int vABParityUnknown); - -int All_SB_Same( AT_RANK canon_rank1, AT_RANK canon_rank2, /* canonical numbers */ - const ppAT_RANK pRankStack1, const ppAT_RANK pRankStack2, - const AT_RANK *nAtomNumberCanonFrom, - sp_ATOM *at ); - -int All_SC_Same( AT_RANK canon_rank1, /* canonical number */ - const ppAT_RANK pRankStack1, const ppAT_RANK pRankStack2, - const AT_RANK *nAtomNumberCanonFrom, - const sp_ATOM *at ); - - -int CompareLinCtStereoDoubleToValues( AT_STEREO_DBLE *LinearCTStereoDble, - AT_RANK at_rank_canon1, AT_RANK at_rank_canon2, U_CHAR bond_parity ); - -int CompareLinCtStereoAtomToValues( AT_STEREO_CARB *LinearCTStereoCarb, - AT_RANK at_rank_canon1, U_CHAR parity ); -int CompareLinCtStereoDble ( AT_STEREO_DBLE *LinearCTStereoDble1, int nLenLinearCTStereoDble1, - AT_STEREO_DBLE *LinearCTStereoDble2, int nLenLinearCTStereoDble2 ); -int CompareLinCtStereoCarb ( AT_STEREO_CARB *LinearCTStereoCarb1, int nLenLinearCTStereoCarb1, - AT_STEREO_CARB *LinearCTStereoCarb2, int nLenLinearCTStereoCarb2 ); -int CompareLinCtStereo ( AT_STEREO_DBLE *LinearCTStereoDble1, int nLenLinearCTStereoDble1, - AT_STEREO_CARB *LinearCTStereoCarb1, int nLenLinearCTStereoCarb1, - AT_STEREO_DBLE *LinearCTStereoDble2, int nLenLinearCTStereoDble2, - AT_STEREO_CARB *LinearCTStereoCarb2, int nLenLinearCTStereoCarb2 ); -/***************************************************************************/ -/* ichicans.c */ -int UnmarkNonStereo( sp_ATOM *at, int num_atoms, const AT_RANK *nRank, const AT_RANK *nAtomNumber, int bIsotopic ); -int FillSingleStereoDescriptors(sp_ATOM *at, int i, int num_trans, const AT_RANK *nRank - , AT_STEREO_CARB *LinearCTStereoCarb, int *nStereoCarbLen, int nMaxStereoCarbLen - , AT_STEREO_DBLE *LinearCTStereoDble, int *nStereoDbleLen, int nMaxStereoDbleLen - , int bAllene ); -void SwitchAtomStereoAndIsotopicStereo( sp_ATOM *at, int num_atoms, int *bSwitched ); -void SetCtToIsotopicStereo( CANON_STAT *pCS, CANON_STAT *pCS2 ); -void SetCtToNonIsotopicStereo( CANON_STAT *pCS, CANON_STAT *pCS2 ); -int FillAllStereoDescriptors( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, const AT_RANK *nAtomNumberCanon, CANON_STAT *pCS ); -int FillOutStereoParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, const AT_RANK *nAtomNumberCanon, - const AT_RANK *nRank, const AT_RANK *nAtomNumber, CANON_STAT *pCS, int bIsotopic ); -int InvertStereo( sp_ATOM *at, int num_at_tg, - AT_RANK *nCanonRank, AT_RANK *nAtomNumberCanon, - CANON_STAT *pCS, int bInvertLinearCTStereo ); -int find_atoms_with_parity( sp_ATOM *at, S_CHAR *visited, int from_atom, int cur_atom ); -int GetStereoNeighborPos( sp_ATOM *at, int iAt1, int iAt2 ); -int GetStereoBondParity(sp_ATOM *at, int i, int n, AT_RANK *nRank ); -int GetStereoCenterParity(sp_ATOM *at, int i, AT_RANK *nRank ); -int GetPermutationParity( sp_ATOM *at, AT_RANK nAvoidNeighbor, AT_RANK *nCanonRank ); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /* __INCHICOMN_H__ */ diff --git a/INCHI-1-SRC/INCHI/common/ichierr.h b/INCHI-1-SRC/INCHI/common/ichierr.h deleted file mode 100644 index a9a1e7a..0000000 --- a/INCHI-1-SRC/INCHI/common/ichierr.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHIERR_H__ -#define __INCHIERR_H__ - -#define _IS_OKAY 0 -#define _IS_WARNING 1 -#define _IS_ERROR 2 /* Microsoft defined its own IS_ERROR() macro */ -#define _IS_FATAL 3 -#define _IS_UNKNOWN 4 /* unknown error: used in INChI DLL only */ -#define _IS_EOF -1 /* end of file */ -#define _IS_SKIP -2 - -#define CT_ERR_FIRST (-30000) -#define CT_OVERFLOW (CT_ERR_FIRST- 0) /*(-30000) */ -#define CT_LEN_MISMATCH (CT_ERR_FIRST- 1) /*(-30001) */ -#define CT_OUT_OF_RAM (CT_ERR_FIRST- 2) /*(-30002) */ -#define CT_RANKING_ERR (CT_ERR_FIRST- 3) /*(-30003) */ -#define CT_ISOCOUNT_ERR (CT_ERR_FIRST- 4) /*(-30004) */ -#define CT_TAUCOUNT_ERR (CT_ERR_FIRST- 5) /*(-30005) */ -#define CT_ISOTAUCOUNT_ERR (CT_ERR_FIRST- 6) /*(-30006) */ -#define CT_MAPCOUNT_ERR (CT_ERR_FIRST- 7) /*(-30007) */ -#define CT_TIMEOUT_ERR (CT_ERR_FIRST- 8) /*(-30008) */ -#define CT_ISO_H_ERR (CT_ERR_FIRST- 9) /*(-30009) */ -#define CT_STEREOCOUNT_ERR (CT_ERR_FIRST-10) /*(-30010) */ -#define CT_ATOMCOUNT_ERR (CT_ERR_FIRST-11) /*(-30011) */ -#define CT_STEREOBOND_ERROR (CT_ERR_FIRST-12) /*(-30012) */ -#define CT_USER_QUIT_ERR (CT_ERR_FIRST-13) /*(-30013) */ -#define CT_REMOVE_STEREO_ERR (CT_ERR_FIRST-14) /*(-30014) */ -#define CT_CALC_STEREO_ERR (CT_ERR_FIRST-15) /*(-30015) */ -#define CT_CANON_ERR (CT_ERR_FIRST-16) /*(-30016) */ -#define CT_STEREO_CANON_ERR (CT_ERR_FIRST-17) /*(-30017) */ -#define CT_WRONG_FORMULA (CT_ERR_FIRST-18) /*(-30017) */ -#define CT_UNKNOWN_ERR (CT_ERR_FIRST-19) /*(-30019) */ - -#define CT_ERR_MIN CT_UNKNOWN_ERR -#define CT_ERR_MAX CT_ERR_FIRST - -#define CHECK_OVERFLOW(Len, Maxlen) ( (Len) >= (Maxlen) ) -#define RETURNED_ERROR(nVal) (CT_ERR_MIN<=(nVal) && (nVal)<=CT_ERR_MAX) - - -#define BNS_ERR -9999 -#define BNS_WRONG_PARMS (BNS_ERR + 0) /*(-9999)*/ -#define BNS_OUT_OF_RAM (BNS_ERR + 1) /*(-9998)*/ -#define BNS_PROGRAM_ERR (BNS_ERR + 2) /*(-9997)*/ -#define BNS_ALTPATH_OVFL (BNS_ERR + 3) /*(-9996)*/ -#define BNS_BOND_ERR (BNS_ERR + 4) /*(-9995)*/ -#define BNS_VERT_NUM_ERR (BNS_ERR + 5) /*(-9994)*/ -#define BNS_VERT_EDGE_OVFL (BNS_ERR + 6) /*(-9993)*/ -#define BNS_SET_ALTP_ERR (BNS_ERR + 7) /*(-9992)*/ -#define BNS_CPOINT_ERR (BNS_ERR + 8) /*(-9991)*/ -#define BNS_CANT_SET_BOND (BNS_ERR + 9) /*(-9990)*/ -#define BNS_CAP_FLOW_ERR (BNS_ERR + 10) /*(-9989)*/ -#define BNS_RADICAL_ERR (BNS_ERR + 11) /*(-9988)*/ -#define BNS_REINIT_ERR (BNS_ERR + 12) /*(-9987)*/ -#define BNS_ALTBOND_ERR (BNS_ERR + 13) /*(-9986)*/ - -#define BNS_MAX_ERR_VALUE (BNS_ERR + 19) /*(-9980)*/ - -#define IS_BNS_ERROR(X) (BNS_ERR <= (X) && (X) <= BNS_MAX_ERR_VALUE) - - -#define INCHI_INP_ERROR_ERR 40 -#define INCHI_INP_ERROR_RET (-1) - -#define INCHI_INP_FATAL_ERR 1 -#define INCHI_INP_FATAL_RET 0 - -#define INCHI_INP_EOF_ERR 11 -#define INCHI_INP_EOF_RET 0 - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -extern int (*UserAction)(void); /* callback */ -extern int (*ConsoleQuit)(void); /* Console user issued CTRL+C etc. */ - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -#define LOG_MASK_WARN 1 -#define LOG_MASK_ERR 2 -#define LOG_MASK_FATAL 4 - -#define LOG_MASK_ALL (LOG_MASK_WARN | LOG_MASK_ERR | LOG_MASK_FATAL) -#define LOG_MASK_NO_WARN (LOG_MASK_ERR | LOG_MASK_FATAL) - -#ifdef TARGET_LIB_FOR_WINCHI -#include - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -extern void (*FWPRINT) (const char * format, va_list argptr ); -extern void (*DRAWDATA) ( struct DrawData * pDrawData); -extern int (*DRAWDATA_EXISTS) ( int nComponent, int nType, int bReconnected ); -extern struct DrawData * (*GET_DRAWDATA) ( int nComponent, int nType, int bReconnected ); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -#endif - -#define USER_ACTION_QUIT 1 - -#endif /* __INCHIERR_H__ */ diff --git a/INCHI-1-SRC/INCHI/common/ichiisot.c b/INCHI-1-SRC/INCHI/common/ichiisot.c deleted file mode 100644 index c761ea8..0000000 --- a/INCHI-1-SRC/INCHI/common/ichiisot.c +++ /dev/null @@ -1,132 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - -#include "mode.h" - -#include "incomdef.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichicant.h" -#include "ichicomn.h" - -#ifdef NEVER -/**********************************************************************************/ -int unpack_iso_sort_key( AT_ISO_SORT_KEY iso_sort_key, S_CHAR *num_1H, S_CHAR *num_2H, S_CHAR *num_3H, S_CHAR *iso_atw_diff ) -{ - int is_negative; - AT_ISO_SORT_KEY HOnlyAtwPart; - static const AT_ISO_SORT_KEY MultAtwDiff = AT_ISO_SORT_KEY_MULT*AT_ISO_SORT_KEY_MULT*AT_ISO_SORT_KEY_MULT; - if ( !iso_sort_key ) { - *num_1H = *num_2H = *num_3H = *iso_atw_diff = 0; - return 0; - } else - if ( iso_sort_key < 0 ) { - is_negative = 1; - iso_sort_key = -iso_sort_key; - HOnlyAtwPart = MultAtwDiff - iso_sort_key % MultAtwDiff; - iso_sort_key += HOnlyAtwPart; - } else { - is_negative = 0; - HOnlyAtwPart = iso_sort_key % MultAtwDiff; - iso_sort_key -= HOnlyAtwPart; - } - - iso_sort_key /= MultAtwDiff; - - *num_1H = (S_CHAR)(HOnlyAtwPart % AT_ISO_SORT_KEY_MULT); - HOnlyAtwPart /= AT_ISO_SORT_KEY_MULT; - *num_2H = (S_CHAR)(HOnlyAtwPart % AT_ISO_SORT_KEY_MULT); - HOnlyAtwPart /= AT_ISO_SORT_KEY_MULT; - *num_3H = (S_CHAR)(HOnlyAtwPart % AT_ISO_SORT_KEY_MULT); - - *iso_atw_diff = (S_CHAR)(is_negative? -iso_sort_key : iso_sort_key); - - return 1; -} -#endif - -/**********************************************************************************/ -AT_ISO_SORT_KEY make_iso_sort_key( int iso_atw_diff, int num_1H, int num_2H, int num_3H) -{ - AT_ISO_SORT_KEY iso_sort_key = 0, mult=1; - - iso_sort_key += mult * num_1H; - mult *= AT_ISO_SORT_KEY_MULT; - iso_sort_key += mult * num_2H; - mult *= AT_ISO_SORT_KEY_MULT; - iso_sort_key += mult * num_3H; - mult *= AT_ISO_SORT_KEY_MULT; - iso_sort_key += mult * iso_atw_diff; - return iso_sort_key; -} -/**********************************************************************************/ -/* set sp_ATOM isotopic sort keys */ -int set_atom_iso_sort_keys( int num_at, sp_ATOM *at, T_GROUP_INFO* t_group_info, int *bHasIsotopicInTautomerGroups ) -{ - int i, num_isotopic = 0, bMergedTgroup; - AT_ISO_SORT_KEY iso_sort_key; - T_GROUP *t_group = - (t_group_info && - t_group_info->t_group && - t_group_info->num_t_groups > 0)? t_group_info->t_group : NULL; - - if ( bHasIsotopicInTautomerGroups ) - *bHasIsotopicInTautomerGroups = 0; - for ( i = 0; i < num_at; i ++ ) { - bMergedTgroup = (t_group_info && t_group_info->nIsotopicEndpointAtomNumber && (at[i].cFlags & AT_FLAG_ISO_H_POINT)); - if ( (!at[i].endpoint || !t_group) && !bMergedTgroup ) { - iso_sort_key = make_iso_sort_key(at[i].iso_atw_diff, at[i].num_iso_H[0], at[i].num_iso_H[1], at[i].num_iso_H[2]); - } else { - /* H isotopes go to the tautomer part of the CT (name) */ - /* if (at[i].endpoint && t_group) ... */ - iso_sort_key = make_iso_sort_key(at[i].iso_atw_diff, 0, 0, 0); - if ( bHasIsotopicInTautomerGroups ) - *bHasIsotopicInTautomerGroups += (at[i].num_iso_H[0] || at[i].num_iso_H[1] || at[i].num_iso_H[2] || bMergedTgroup); - } - at[i].iso_sort_key = iso_sort_key; - num_isotopic += (iso_sort_key != 0); - } - return num_isotopic; -} - diff --git a/INCHI-1-SRC/INCHI/common/ichimain.h b/INCHI-1-SRC/INCHI/common/ichimain.h deleted file mode 100644 index 5e31c23..0000000 --- a/INCHI-1-SRC/INCHI/common/ichimain.h +++ /dev/null @@ -1,224 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHIMAIN_H__ -#define __INCHIMAIN_H__ - -#define ESC_KEY 27 -#define INCHI_SEGM_BUFLEN 64000 - -/********************************************************************/ -typedef struct tagStructData { - unsigned long ulStructTime; - int nErrorCode; - int nErrorType; - int nStructReadError; - char pStrErrStruct[STR_ERR_LEN]; - long fPtrStart; /* or number of processed structures */ - long fPtrEnd; /* or number of errors */ - int bXmlStructStarted; - int bUserQuit; - int bUserQuitComponent; - int bUserQuitComponentDisplay; - int bChiralFlag; - /* information related to normal or disconnected layers */ - int num_taut[INCHI_NUM]; - int num_non_taut[INCHI_NUM]; - INCHI_MODE bTautFlags[INCHI_NUM]; /* reconnected does not have TG_FLAG_DISCONNECT_COORD_DONE flag */ - INCHI_MODE bTautFlagsDone[INCHI_NUM]; /* reconnected does not have TG_FLAG_DISCONNECT_COORD_DONE flag */ - int num_components[INCHI_NUM]; /* number of allocated INChI, INChI_Aux data structures */ - /* debugging info */ -#if ( bRELEASE_VERSION == 0 ) - int bExtract; -#endif - -} STRUCT_DATA; - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -int process_single_input( int argc, char *argv[ ] ); - -int ReadCommandLineParms(int argc, const char *argv[], INPUT_PARMS *ip, - char *szSdfDataValue, unsigned long *ulDisplTime, - int bReleaseVersion, INCHI_IOSTREAM *log_file); -void HelpCommandLineParms(INCHI_IOSTREAM *f); -int OpenFiles( FILE **inp_file, FILE **output_file, FILE **log_file, FILE **prb_file, INPUT_PARMS *ip ); -#ifndef COMPILE_ANSI_ONLY -int DisplayStructure( inp_ATOM *at, int num_at, int num_removed_H, int bAdd_DT_to_num_H, int nNumRemovedProtons, NUM_H nNumRemovedProtonsIsotopic[], - int bIsotopic, int j /*bTautomeric*/, INChI **cur_INChI, INChI_Aux **cur_INChI_Aux, - int bAbcNumbers, DRAW_PARMS *dp, INCHI_MODE nMode, char *szTitle ); -void FillTableParms( SET_DRAW_PARMS *sdp, INChI **cur_INChI, INChI_Aux **cur_INChI_Aux, INCHI_MODE nMode, int bShowIsotopic, int bShowTaut ); -void FillCompositeTableParms( SET_DRAW_PARMS *sdp, AT_NUMB StereoFlags, - INCHI_MODE nMode, int bShowIsotopic, int bShowTaut ); -#endif -int PrintInputParms(INCHI_IOSTREAM *log_file, INPUT_PARMS *ip); -const char *ErrMsg( int nErrorCode ); -int SortAndPrintINChI(INCHI_IOSTREAM *output_file, - char *pStr, int nStrLen, - INCHI_IOSTREAM *log_file, - INPUT_PARMS *ip, - ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data, - COMP_ATOM_DATA composite_norm_data[INCHI_NUM][TAUT_NUM+1], - ORIG_STRUCT *pOrigStruct, int num_components[INCHI_NUM], - int num_non_taut[INCHI_NUM], int num_taut[INCHI_NUM], - INCHI_MODE bTautFlags[INCHI_NUM], INCHI_MODE bTautFlagsDone[INCHI_NUM], - NORM_CANON_FLAGS *pncFlags, long num_inp, - PINChI2 *pINChI[INCHI_NUM], - PINChI_Aux2 *pINChI_Aux[INCHI_NUM], - int *pSortPrintINChIFlags, unsigned char save_opt_bits); -void FreeAllINChIArrays(PINChI2 *pINChI[INCHI_NUM], - PINChI_Aux2 *pINChI_Aux[INCHI_NUM], - int num_components[2]); -void FreeINChIArrays(PINChI2 *pINChI, - PINChI_Aux2 *pINChI_Aux, - int num_components ); -void SplitTime(unsigned long ulTotalTime, - int *hours, int *minutes, int *seconds, int *mseconds ); - -int ReadTheStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, INCHI_IOSTREAM *inp_file, ORIG_ATOM_DATA *orig_inp_data, - int inp_index, int *out_index ); -int TreatReadTheStructureErrors( STRUCT_DATA *sd, INPUT_PARMS *ip, int nLogMask, - INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, - INCHI_IOSTREAM *output_file, - INCHI_IOSTREAM *prb_file, - ORIG_ATOM_DATA *orig_inp_data, - long *num_inp, - char *pStr, int nStrLen ); -int GetOneComponent(STRUCT_DATA *sd, INPUT_PARMS *ip, - INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, - INP_ATOM_DATA *inp_cur_data, - ORIG_ATOM_DATA *orig_inp_data, - int i, long num_inp, char *pStr, int nStrLen ); -int CreateOneComponentINChI(STRUCT_DATA *sd, INPUT_PARMS *ip, - INP_ATOM_DATA *inp_cur_data, ORIG_ATOM_DATA *orig_inp_data, - PINChI2 *pINChI, PINChI_Aux2 *pINChI_Aux, int iINChI, - int i, long num_inp, INP_ATOM_DATA **inp_norm_data, - NORM_CANON_FLAGS *pncFlags, INCHI_IOSTREAM *log_file); -int TreatCreateOneComponentINChIError(STRUCT_DATA *sd, INPUT_PARMS *ip, - ORIG_ATOM_DATA *orig_inp_data, - int i, long num_inp, - INCHI_IOSTREAM *inp_file, - INCHI_IOSTREAM *log_file, - INCHI_IOSTREAM *output_file, - INCHI_IOSTREAM *prb_file, - char *pStr, int nStrLen ); -int TreatCreateINChIWarning(STRUCT_DATA *sd, INPUT_PARMS *ip, - ORIG_ATOM_DATA *orig_inp_data, - long num_inp, - INCHI_IOSTREAM *inp_file, - INCHI_IOSTREAM *log_file, - INCHI_IOSTREAM *output_file, - INCHI_IOSTREAM *prb_file, - char *pStr, int nStrLen ); - -#if ( TEST_RENUMB_ATOMS == 1 || READ_INCHI_STRING == 1 ) /* { */ -int CompareINChI( INChI *i1, INChI *i2, INChI_Aux *a1, INChI_Aux *a2 ); -#endif - -void eat_keyboard_input( void ); -int user_quit( const char *msg, unsigned long ulMaxTime ); - -int GetOneStructure(STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, - INCHI_IOSTREAM *inp_file, - INCHI_IOSTREAM *log_file, - INCHI_IOSTREAM *output_file, - INCHI_IOSTREAM *prb_file, - ORIG_ATOM_DATA *orig_inp_data, - long *num_inp, char *pStr, int nStrLen, - STRUCT_FPTRS *struct_fptrs ); -int ProcessOneStructure(STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, - PINChI2 *pINChI2[INCHI_NUM], - PINChI_Aux2 *pINChI_Aux2[INCHI_NUM], - INCHI_IOSTREAM *inp_file, - INCHI_IOSTREAM *log_file, - INCHI_IOSTREAM *output_file, - INCHI_IOSTREAM *prb_file, - ORIG_ATOM_DATA *orig_inp_data, - ORIG_ATOM_DATA *prep_inp_data, - long num_inp, char *pStr, int nStrLen, - unsigned char save_opt_bits); - -int CreateOneStructureINChI(STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, - PINChI2 *pINChI2[INCHI_NUM], - PINChI_Aux2 *pINChI_Aux2[INCHI_NUM], - int iINChI, - INCHI_IOSTREAM *inp_file, - INCHI_IOSTREAM *log_file, - INCHI_IOSTREAM *output_file, - INCHI_IOSTREAM *prb_file, - ORIG_ATOM_DATA *orig_inp_data, - ORIG_ATOM_DATA *prep_inp_data, - COMP_ATOM_DATA composite_norm_data2[][TAUT_NUM+1], - long num_inp, char *pStr, int nStrLen, - NORM_CANON_FLAGS *pncFlags ); - -int bIsStructChiral(PINChI2 *pINChI2[INCHI_NUM], int num_components[]); -int PreprocessOneStructure(STRUCT_DATA *sd, INPUT_PARMS *ip, - ORIG_ATOM_DATA *orig_inp_data, - ORIG_ATOM_DATA *prep_inp_data ); -int FillOutOrigStruct(ORIG_ATOM_DATA *orig_inp_data, - ORIG_STRUCT *pOrigStruct, - STRUCT_DATA *sd); -void FreeOrigStruct( ORIG_STRUCT *pOrigStruct); - - -int ReadWriteInChI(INCHI_IOSTREAM *pInp, INCHI_IOSTREAM *pOut, INCHI_IOSTREAM *pLog, - INPUT_PARMS *ip_inp, - STRUCT_DATA *sd_inp, - /* the following are InChI library-specific parameters */ - inp_ATOM **at, int *num_at, - char *szMsg, int nMsgLen, - unsigned long WarningFlags[2][2]); - -int CompareHillFormulasNoH(const char *f1, const char *f2, int *num_H1, int *num_H2); - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /* __INCHIMAIN_H__ */ diff --git a/INCHI-1-SRC/INCHI/common/ichimak2.c b/INCHI-1-SRC/INCHI/common/ichimak2.c deleted file mode 100644 index 05dc618..0000000 --- a/INCHI-1-SRC/INCHI/common/ichimak2.c +++ /dev/null @@ -1,1225 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include - - -#include "mode.h" - -#include "inpdef.h" -#include "ichi.h" -#include "strutil.h" -#include "util.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "ichicant.h" -#include "ichicano.h" -#include "ichicomn.h" - -#include "ichicomp.h" -#include "ichimain.h" -#include "ichimake.h" - - -int GetHillFormulaCounts( U_CHAR *nAtom, S_CHAR *nNum_H, int num_atoms, - AT_NUMB *nTautomer, int lenTautomer, - int *pnum_C, int *pnum_H, int *pnLen, int *pnNumNonHAtoms ); -int MakeHillFormula( U_CHAR *nAtom, int num_atoms, - char *szLinearCT, int nLen_szLinearCT, int num_C, int num_H, int *bOverflow ); - -#if ( FIX_DALKE_BUGS == 1 ) -#else -char *AllocateAndFillHillFormula( INChI *pINChI ); -#endif - -int AddElementAndCount( const char *szElement, int mult, char *szLinearCT, int nLenLinearCT, int *bOverflow ); - -int Copy2StereoBondOrAllene( INChI_Stereo *Stereo, int *nNumberOfStereoCenters, int *nNumberOfStereoBonds, - AT_STEREO_DBLE *LinearCTStereoDble, - AT_NUMB *pCanonOrd, AT_RANK *pCanonRank, sp_ATOM *at, int bIsotopic ); - -int CopyLinearCTStereoToINChIStereo( INChI_Stereo *Stereo, - AT_STEREO_CARB *LinearCTStereoCarb, int nLenLinearCTStereoCarb, - AT_STEREO_DBLE *LinearCTStereoDble, int nLenLinearCTStereoDble - , AT_NUMB *pCanonOrd, AT_RANK *pCanonRank, sp_ATOM *at, int bIsotopic - , AT_STEREO_CARB *LinearCTStereoCarbInv - , AT_STEREO_DBLE *LinearCTStereoDbleInv - , AT_NUMB *pCanonOrdInv, AT_RANK *pCanonRankInv ); -int GetHillFormulaIndexLength( int count ); - -int MarkAmbiguousStereo( sp_ATOM *at, inp_ATOM *norm_at, int bIsotopic, AT_NUMB *pCanonOrd, - AT_STEREO_CARB *LinearCTStereoCarb, int nLenLinearCTStereoCarb, - AT_STEREO_DBLE *LinearCTStereoDble, int nLenLinearCTStereoDble ); - -INCHI_MODE UnmarkAllUndefinedUnknownStereo( INChI_Stereo *Stereo, INCHI_MODE nUserMode ); - -int CleanCoord( MOL_COORD szCoord, int delim ); - -/**********************************************************************************************/ -int MakeHillFormulaString( char *szHillFormula, char *szLinearCT, int nLen_szLinearCT, int *bOverflow) -{ - int nLen; - if ( szHillFormula && !*bOverflow ) { - if ( nLen_szLinearCT > ( nLen = strlen(szHillFormula) ) ) { - memcpy( szLinearCT, szHillFormula, nLen+1 ); - return nLen; - } - *bOverflow |= 1; - return nLen_szLinearCT+1; - } - return 0; -} -/********************************************************************************************** - * MS Windows dependent: sprintf() is supposed to return the length of the output string - * Carbon atoms are always first - * Bridging hydrogen atoms are always last - **********************************************************************************************/ -int GetHillFormulaIndexLength( int count ) -{ - char szCount[16]; - if ( count > 1 ) { - return sprintf( szCount, "%d", count ); - } - return 0; -} -/**********************************************************************************************/ -int GetHillFormulaCounts( U_CHAR *nAtom, S_CHAR *nNum_H, int num_atoms, - AT_NUMB *nTautomer, int lenTautomer, - int *pnum_C, int *pnum_H, int *pnLen, int *pnNumNonHAtoms ) -{ - char szElement[4]; - U_CHAR nPrevAtom = (U_CHAR)-2; - int bCarbon, bHydrogen, nElemLen, nFormLen, nNumNonHAtoms; - int mult, i, num_H, num_C; - - num_H = 0; - num_C = 0; - bCarbon = 0; - bHydrogen = 0; - nElemLen = 0; - nFormLen = 0; - mult = 0; - nNumNonHAtoms = num_atoms; - for ( i = 0; i < num_atoms; i ++ ) { - if ( nPrevAtom != nAtom[i] ) { - if ( mult ) { - if ( bHydrogen ) { - num_H += mult; - }else - if ( bCarbon ) { - num_C += mult; - } else { - nFormLen += nElemLen; - nFormLen += GetHillFormulaIndexLength( mult ); - } - } - - if ( GetElementFormulaFromAtNum((int)nAtom[i], szElement ) ) { - return -1; /* wrong element */ - } - mult = 1; - - nElemLen = strlen(szElement); - nPrevAtom = nAtom[i]; - bCarbon = !strcmp( szElement, "C" ); - bHydrogen = !strcmp( szElement, "H" ); - if ( bHydrogen ) { - nNumNonHAtoms = i; - } - } else { - mult ++; - } - - num_H += nNum_H[i]; - } - /* NumGroups; ((NumAt+1, NumH, At1..AtNumAt),...) */ - if ( nTautomer && lenTautomer > 0 ) { - int num_groups = nTautomer[0]; - for ( i = 1; i < lenTautomer && num_groups > 0; i += nTautomer[i]+1, num_groups -- ) { - num_H += nTautomer[i+1]; - } - } - - if ( mult ) { - if ( bHydrogen ) { - num_H += mult; - } else - if ( bCarbon ) { - num_C += mult; - } else { - nFormLen += nElemLen; - nFormLen += GetHillFormulaIndexLength( mult ); - } - } - - if ( num_C ) { - nFormLen += strlen( "C" ); - nFormLen += GetHillFormulaIndexLength( num_C ); - } - - if ( num_H ) { - nFormLen += strlen( "H" ); - nFormLen += GetHillFormulaIndexLength( num_H ); - } - *pnum_C = num_C; - *pnum_H = num_H; - *pnLen = nFormLen; - *pnNumNonHAtoms = nNumNonHAtoms; - - return 0; -} -/**********************************************************************************************/ -int AddElementAndCount( const char *szElement, int mult, char *szLinearCT, int nLenLinearCT, int *bOverflow ) -{ - char szMult[16]; - int len1, len2; - if ( mult > 0 && !*bOverflow && 0 < (len1 = strlen( szElement )) ) { - if ( mult > 1 ) { - len2 = sprintf( szMult, "%d", mult ); - } else { - len2 = 0; - szMult[0] = '\0'; - } - if ( len1 + len2 < nLenLinearCT ) { - memcpy( szLinearCT, szElement, len1 ); - memcpy( szLinearCT+len1, szMult, len2+1 ); /* adding zero termination */ - return len1+len2; - } else { - (*bOverflow) ++; - } - } - return 0; -} -/**********************************************************************************************/ -/* if num_C > 0 then nAtom does not contain C or H */ -/* otherwise all elements are in alphabetic order */ -int MakeHillFormula( U_CHAR *nAtom, int num_atoms, - char *szLinearCT, int nLen_szLinearCT, int num_C, int num_H, int *bOverflow ) -{ - char szElement[4]; - int mult, compare2H; - int i, nLen, bOvfl; - U_CHAR nPrevAtom; - - nLen = 0; - mult = 0; - bOvfl = 0; - nPrevAtom = (U_CHAR)-2; /* non-existent number */ - - - if ( num_C ) { - nLen += AddElementAndCount( "C", num_C, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl ); - if ( num_H ) { - nLen += AddElementAndCount( "H", num_H, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl ); - num_H = 0; - } - } - - for ( i = 0; i < num_atoms; i ++ ) { - - if ( nPrevAtom != nAtom[i] ) { - if ( mult ) { - nLen += AddElementAndCount( szElement, mult, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl ); - } - mult = 1; - if ( GetElementFormulaFromAtNum((int)nAtom[i], szElement ) ) { - return -1; /* wrong element */ - } - nPrevAtom = nAtom[i]; - if ( !strcmp( "C", szElement ) ) { - return -1; - } - compare2H = strcmp( "H", szElement ); - if ( !compare2H ) { - return -1; - } - if ( compare2H < 0 && num_H ) { - /* H-atom should be located in front of szElement */ - nLen += AddElementAndCount( "H", num_H, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl ); - num_H = 0; - } - } else { - mult ++; - } - } - if ( mult ) { - /* the last element if any */ - nLen += AddElementAndCount( szElement, mult, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl ); - } - if ( num_H ) { - /* if H has not been output... */ - nLen += AddElementAndCount( "H", num_H, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl ); - } - *bOverflow |= (0 != bOvfl); - return bOvfl? nLen_szLinearCT+1: nLen; -} -/**********************************************************************************************/ -char *AllocateAndFillHillFormula( INChI *pINChI ) -{ - int num_C, num_H, nLen, nNumNonHAtoms, ret, bOverflow; - char *pHillFormula = NULL; - bOverflow = 0; - if ( !GetHillFormulaCounts( pINChI->nAtom, pINChI->nNum_H, pINChI->nNumberOfAtoms, - pINChI->nTautomer, pINChI->lenTautomer, - &num_C, &num_H, &nLen, &nNumNonHAtoms ) ) { - if ( pHillFormula = (char*) inchi_malloc( nLen+1 ) ) { - ret = MakeHillFormula( pINChI->nAtom+num_C, nNumNonHAtoms-num_C, - pHillFormula, nLen+1, num_C, num_H, &bOverflow ); - if ( ret != nLen || bOverflow ) { - inchi_free( pHillFormula ); - pHillFormula = NULL; - } - } - } - return pHillFormula; -} - -/************************************************************************************/ -/* return value: 0 => copied to stereo bonds; 1=> Allene copied to stereocenters */ -/* on input nNumberOfStereoBonds==NULL means second call, use Stereo->...Inv */ -/************************************************************************************/ -int Copy2StereoBondOrAllene( INChI_Stereo *Stereo, int *nNumberOfStereoCenters, int *nNumberOfStereoBonds, - AT_STEREO_DBLE *LinearCTStereoDble, - AT_NUMB *pCanonOrd, AT_RANK *pCanonRank, sp_ATOM *at, int bIsotopic ) -{ - int cumulene_len, j, next_j /* ordering number of the central allene atom */, next_neigh; - AT_RANK at_num; - int parity; - if ( pCanonOrd && pCanonRank ) { - j = pCanonOrd[(int)LinearCTStereoDble->at_num1-1]; - /* if allene then find the central atom, at[next_j] */ - if ( bIsotopic ) { - cumulene_len = BOND_CHAIN_LEN(at[j].stereo_bond_parity2[0]); - if ( cumulene_len % 2 && (1 >= MAX_NUM_STEREO_BONDS || !at[j].stereo_bond_neighbor2[1]) ) { - next_j = at[j].neighbor[(int)at[j].stereo_bond_ord2[0]]; - for ( cumulene_len = (cumulene_len-1)/2; cumulene_len && 2==at[next_j].valence; cumulene_len -- ) { - next_neigh = (j == at[next_j].neighbor[0]); - j = next_j; - next_j = at[next_j].neighbor[next_neigh]; - } - /* next_j is the central atom */ - } else { - cumulene_len = -1; /* not an allene */ - } - - } else { - cumulene_len = BOND_CHAIN_LEN(at[j].stereo_bond_parity[0]); - if ( cumulene_len % 2 && (1 >= MAX_NUM_STEREO_BONDS || !at[j].stereo_bond_neighbor[1]) ) { - next_j = at[j].neighbor[(int)at[j].stereo_bond_ord[0]]; - for ( cumulene_len = (cumulene_len-1)/2; cumulene_len && 2==at[next_j].valence; cumulene_len -- ) { - next_neigh = (j == at[next_j].neighbor[0]); - j = next_j; - next_j = at[next_j].neighbor[next_neigh]; - } - } else { - cumulene_len = -1; /* not an allene */ - } - } - if ( !cumulene_len ) { - /* allene has been found; insert new stereocenter and parity */ - AT_NUMB *nNumber; - S_CHAR *t_parity; - nNumber = nNumberOfStereoBonds? Stereo->nNumber : Stereo->nNumberInv; - t_parity = nNumberOfStereoBonds? Stereo->t_parity : Stereo->t_parityInv; - at_num = pCanonRank[next_j]; - parity = LinearCTStereoDble->parity; - /* free room for the new stereocenter */ - for ( j = 0; j < *nNumberOfStereoCenters && Stereo->nNumber[j] < at_num; j ++ ) - ; - if ( j < *nNumberOfStereoCenters ) { - memmove( nNumber + j + 1, nNumber + j, (*nNumberOfStereoCenters-j)*sizeof(nNumber[0]) ); - memmove( t_parity + j + 1, t_parity + j, (*nNumberOfStereoCenters-j)*sizeof(t_parity[0]) ); - } - /* fill the new stereo center info */ - - nNumber[j] = at_num; - t_parity[j] = parity; - (*nNumberOfStereoCenters) ++; - return 1; - } - } - /* save the stereo bond info */ - if ( nNumberOfStereoBonds ) { - j = *nNumberOfStereoBonds; - Stereo->b_parity[j] = LinearCTStereoDble->parity; - Stereo->nBondAtom1[j] = LinearCTStereoDble->at_num1; - Stereo->nBondAtom2[j] = LinearCTStereoDble->at_num2; - (*nNumberOfStereoBonds) ++; - } - return 0; -} -/***************************************************************************/ -int CopyLinearCTStereoToINChIStereo( INChI_Stereo *Stereo, - AT_STEREO_CARB *LinearCTStereoCarb, int nLenLinearCTStereoCarb, - AT_STEREO_DBLE *LinearCTStereoDble, int nLenLinearCTStereoDble - , AT_NUMB *pCanonOrd, AT_RANK *pCanonRank, sp_ATOM *at, int bIsotopic - , AT_STEREO_CARB *LinearCTStereoCarbInv - , AT_STEREO_DBLE *LinearCTStereoDbleInv - , AT_NUMB *pCanonOrdInv, AT_RANK *pCanonRankInv ) -{ - int n, i, nErrorCode = 0, len; - int bAllene; - int diff; - int lenInv, bAlleneInv; - /* stereo centers */ - n = Stereo->nNumberOfStereoCenters = nLenLinearCTStereoCarb; - for ( i = 0; i < n; i ++ ) { - Stereo->nNumber[i] = LinearCTStereoCarb[i].at_num; - Stereo->t_parity[i] = LinearCTStereoCarb[i].parity; - Stereo->nNumberInv[i] = LinearCTStereoCarbInv[i].at_num; - Stereo->t_parityInv[i] = LinearCTStereoCarbInv[i].parity; - } - /* stereo bonds */ - n = nLenLinearCTStereoDble; - lenInv = Stereo->nNumberOfStereoCenters; - for ( i = len = 0; i < n; i ++ ) { - bAllene = - Copy2StereoBondOrAllene( Stereo, &Stereo->nNumberOfStereoCenters, - &len, LinearCTStereoDble+i, pCanonOrd, pCanonRank, at, bIsotopic ); - bAlleneInv = - Copy2StereoBondOrAllene( Stereo, &lenInv, - NULL, LinearCTStereoDbleInv+i, pCanonOrdInv, pCanonRankInv, at, bIsotopic ); - /* make sure double bond stereo is identical in original and inverted geometry */ - /* Note: all allenes are AFTER double bonds in LinearCTStereoDble... */ - if ( bAllene != bAlleneInv || !bAllene && - CompareLinCtStereoDble ( LinearCTStereoDble+i, 1, - LinearCTStereoDbleInv+i, 1 ) ) { - nErrorCode = -4; /* double bond stereo Inv is NOT identical to Abs */ - goto exit_function; - } - } - Stereo->nNumberOfStereoBonds = len; - - if ( lenInv != Stereo->nNumberOfStereoCenters ) { - nErrorCode = -5; /* different number of stereo centers in Abs and Inv */ - goto exit_function; - } - /* compare inverted stereocenters to absolute */ - n = Stereo->nNumberOfStereoCenters; - diff = 0; - for ( i = 0, diff = 0; i < n; i ++ ) { - if ( Stereo->nNumberInv[i] != Stereo->nNumber[i] ) { - diff = (Stereo->nNumberInv[i] > Stereo->nNumber[i])? 2 : -2; - break; /* Abs != Inv */ - } - if ( Stereo->t_parityInv[i] != Stereo->t_parity[i] ) { - diff = (Stereo->t_parityInv[i] > Stereo->t_parity[i])? 1 : -1; - break; /* Abs != Inv */ - } - } - Stereo->nCompInv2Abs = (diff > 0)? 1 : (diff < 0)? -1 : 0; - if ( diff == -1 || diff == 1 ) { - /* the first found difference was in parities */ - for ( i = 0, diff = 0; i < n; i ++ ) { - if ( Stereo->nNumberInv[i] != Stereo->nNumber[i] ) { - diff = 2; /* difference in stereo center numbering */ - break; - } - /* parities can be only 1, 2, 3, 4. Therefore only mutually inverted pairs - * (t_parityInv, t_parity) = (1,2) or (2,1) statisfy conditions - * (t_parityInv != t_parity) && (t_parityInv + t_parity == 3) - */ - if ( Stereo->t_parityInv[i] == Stereo->t_parity[i] || - Stereo->t_parityInv[i] + Stereo->t_parity[i] != 3 ) { - diff = 1; /* parities are same or different and cannot be obtained by simple inversion */ - break; - } - } - Stereo->bTrivialInv = !diff; - } else { - Stereo->bTrivialInv = 0; - } -exit_function: - - return nErrorCode; -} -/***************************************************************************/ -int MarkAmbiguousStereo( sp_ATOM *at, inp_ATOM *norm_at, int bIsotopic, AT_NUMB *pCanonOrd, - AT_STEREO_CARB *LinearCTStereoCarb, int nLenLinearCTStereoCarb, - AT_STEREO_DBLE *LinearCTStereoDble, int nLenLinearCTStereoDble ) -{ - int n, i, j1, j2, num, mark_atom, mark_bond; - - if ( !pCanonOrd ) - return -1; - num = 0; - n = nLenLinearCTStereoCarb; - mark_atom = bIsotopic? AMBIGUOUS_STEREO_ATOM_ISO : AMBIGUOUS_STEREO_ATOM; - for ( i = 0; i < n; i ++ ) { - /* mark ambiguous stereo centers (for displaying and "Ambiguous stereo" message) */ - if ( ATOM_PARITY_NOT_UNKN(LinearCTStereoCarb[i].parity) && - at[j1=pCanonOrd[(int)LinearCTStereoCarb[i].at_num-1]].bAmbiguousStereo ) { - at[j1].bAmbiguousStereo |= mark_atom; - norm_at[j1].bAmbiguousStereo |= mark_atom; - num ++; - } - } - - n = nLenLinearCTStereoDble; - mark_bond = bIsotopic? AMBIGUOUS_STEREO_BOND_ISO : AMBIGUOUS_STEREO_BOND; - for ( i = 0; i < n; i ++ ) { - /* mark ambiguous stereo bonds or allenes (for displaying and "Ambiguous stereo" message) */ - if ( ATOM_PARITY_WELL_DEF(LinearCTStereoDble[i].parity) ) { - j1=pCanonOrd[(int)LinearCTStereoDble[i].at_num1-1]; - j2=pCanonOrd[(int)LinearCTStereoDble[i].at_num2-1]; - if ( at[j1].bAmbiguousStereo || at[j2].bAmbiguousStereo ) { - /* if it is an allene then mark the central atom only - because the bonds should not be marked to avoid misleading - message "Ambiguous stereo: bond(s)": Allene makes a stereocenter - */ - int j1_parity = bIsotopic? at[j1].stereo_bond_parity2[0] : - at[j1].stereo_bond_parity[0]; - int cumulene_len = BOND_CHAIN_LEN(j1_parity); /* 0 => double bond, 1 => allene, 2 => cumulene,..*/ - if ( cumulene_len % 2 && (1 >= MAX_NUM_STEREO_BONDS || - !(bIsotopic? at[j1].stereo_bond_neighbor2[1] : - at[j1].stereo_bond_neighbor[1] )) ) { - /* found an allene; locate its central atom */ - int next_j, next_neigh; - int j = j1; - next_j = at[j].neighbor[bIsotopic? at[j].stereo_bond_ord2[0] : - at[j].stereo_bond_ord[0] ]; - for ( cumulene_len = (cumulene_len-1)/2; - cumulene_len && 2==at[next_j].valence; - cumulene_len -- ) { - next_neigh = (j == at[next_j].neighbor[0]); - j = next_j; - next_j = at[next_j].neighbor[next_neigh]; - } - /* next_j is the central atom */ - if ( 2==at[next_j].valence ) { - at[next_j].bAmbiguousStereo |= mark_atom; - norm_at[next_j].bAmbiguousStereo |= mark_atom; - num ++; - continue; /* do not mark the cumulene "bond" endpoints */ - } - } - /* not an allene, mark double bond or cumulene end atoms */ - if ( at[j1].bAmbiguousStereo ) { - at[j1].bAmbiguousStereo |= mark_bond; /* ??? */ - norm_at[j1].bAmbiguousStereo |= mark_bond; - num ++; - } - if ( at[j2].bAmbiguousStereo ) { - at[j2].bAmbiguousStereo |= mark_bond; /* ??? */ - norm_at[j2].bAmbiguousStereo |= mark_bond; - num ++; - } - } - } - } - return num; - -} -/**********************************************************************************************/ -INCHI_MODE UnmarkAllUndefinedUnknownStereo( INChI_Stereo *Stereo, INCHI_MODE nUserMode ) -{ - INCHI_MODE nRet = 0; - int i, n; - if ( !Stereo || Stereo && !Stereo->nNumberOfStereoCenters && !Stereo->nNumberOfStereoBonds) { - return nRet; - } - - /* stereocenters */ - if ( !Stereo->nCompInv2Abs && - (n=Stereo->nNumberOfStereoCenters) > 0 && (nUserMode & REQ_MODE_SC_IGN_ALL_UU) ) { - - for ( i = 0; i < n && !ATOM_PARITY_WELL_DEF(Stereo->t_parity[i]); i ++ ) - ; - if ( i == n ) { - Stereo->nNumberOfStereoCenters = 0; - for ( i = 0; i < n; i ++ ) { - Stereo->t_parity[i] = 0; - Stereo->nNumber[i] = 0; - Stereo->t_parityInv[i] = 0; - Stereo->nNumberInv[i] = 0; - } - nRet |= REQ_MODE_SC_IGN_ALL_UU; - } - } - /* stereobonds */ - if ( (n=Stereo->nNumberOfStereoBonds) > 0 && (nUserMode & REQ_MODE_SB_IGN_ALL_UU) ) { - for ( i = 0; i < n && !ATOM_PARITY_WELL_DEF(Stereo->b_parity[i]); i ++ ) - ; - if ( i == n ) { - Stereo->nNumberOfStereoBonds = 0; - for ( i = 0; i < n; i ++ ) { - Stereo->b_parity[i] = 0; - Stereo->nBondAtom1[i] = 0; - Stereo->nBondAtom2[i] = 0; - } - nRet |= REQ_MODE_SB_IGN_ALL_UU; - } - } - - return nRet; -} -#if ( defined(TARGET_API_LIB) || ADD_CMLPP==1 ) -/**********************************************************************************************/ -void WriteCoord( char *str, double x ) -{ - if ( x < -9999999.9 ) { - sprintf( str, "%10.2e", x ); - } else - if ( x < -999999.99 ) { - sprintf( str, "%10.2f", x ); - } else - if ( x < -99999.999 ) { - sprintf( str, "%10.3f", x ); - } else - if ( x < 99999.9999 ) { - sprintf( str, "%10.4f", x ); - } else - if ( x < 999999.999 ) { - sprintf( str, "%10.3f", x ); - } else - if ( x < 9999999.99 ) { - sprintf( str, "%10.2f", x ); - } else - if ( x < 99999999.9 ) { - sprintf( str, "%10.1f", x ); - } else { - sprintf( str, "%10.3e", x ); - } -} -#endif -/* used CANON_STAT members - - pCS->LinearCT - pCS->LinearCTIsotopic - pCS->LinearCTIsotopicStereoCarb - pCS->LinearCTIsotopicStereoCarbInv - pCS->LinearCTIsotopicStereoDble - pCS->LinearCTIsotopicStereoDbleInv - pCS->LinearCTIsotopicTautomer - pCS->LinearCTStereoCarb - pCS->LinearCTStereoCarbInv - pCS->LinearCTStereoDble - pCS->LinearCTStereoDbleInv - pCS->nCanonOrd - pCS->nCanonOrdIsotopic - pCS->nCanonOrdIsotopicStereo - pCS->nCanonOrdIsotopicStereoInv - pCS->nCanonOrdIsotopicStereoTaut - pCS->nCanonOrdIsotopicTaut - pCS->nCanonOrdStereo - pCS->nCanonOrdStereoInv - pCS->nCanonOrdStereoTaut - pCS->nCanonOrdTaut - pCS->nLenCanonOrd - pCS->nLenCanonOrdIsotopic - pCS->nLenCanonOrdIsotopicStereo - pCS->nLenCanonOrdIsotopicStereoTaut - pCS->nLenCanonOrdIsotopicTaut - pCS->nLenCanonOrdStereo - pCS->nLenCanonOrdStereoTaut - pCS->nLenCanonOrdTaut - pCS->nLenLinearCTAtOnly - pCS->nLenLinearCTIsotopic - pCS->nLenLinearCTIsotopicStereoCarb - pCS->nLenLinearCTIsotopicStereoDble - pCS->nLenLinearCTIsotopicTautomer - pCS->nLenLinearCTStereoCarb - pCS->nLenLinearCTStereoDble - pCS->nNum_H - pCS->nNum_H_fixed - pCS->nSymmRank - pCS->nSymmRankIsotopic - pCS->nSymmRankIsotopicTaut - pCS->nSymmRankTaut - pCS->t_group_info - pCS->t_group_info->num_t_groups - -*/ -/**********************************************************************************************/ -int FillOutINChI( INChI *pINChI, INChI_Aux *pINChI_Aux, - int num_atoms, int num_at_tg, int num_removed_H, - sp_ATOM *at, inp_ATOM *norm_at, CANON_STAT *pCS, int bTautomeric, - INCHI_MODE nUserMode, char *pStrErrStruct ) -{ - int i, j, m, n, g, len, ii, ret=0; - - AT_NUMB *pSymmRank, *pOrigNosInCanonOrd, *pConstitEquNumb, *pCanonOrd=NULL, *pCanonOrdInv=NULL, *pCanonOrdTaut; - T_GROUP_INFO *t_group_info = pCS->t_group_info; - T_GROUP *t_group; - int nErrorCode = 0; - AT_NUMB *pCanonRank, *pCanonRankInv; /* canonical ranks of the atoms or tautomeric groups */ - AT_NUMB *pCanonRankAtoms=NULL, *pSortOrd = NULL; - AT_RANK nMinOrd; - INChI_Stereo *Stereo; - int bUseNumberingInv = 0, bUseIsotopicNumberingInv = 0; - INCHI_MODE nStereoUnmarkMode; - - /*AT_NUMB *pCanonOrdNonIso = NULL, *pCanonOrdIso = NULL;*/ - /*AT_NUMB *nOrigAtNosInCanonOrdNonIso = NULL, *nOrigAtNosInCanonOrdIso = NULL;*/ - - /* Check for warnings */ - if ( pCS->nLenLinearCTStereoCarb < 0 || pCS->nLenLinearCTStereoDble < 0 || - pCS->nLenCanonOrdStereo < 0 || pCS->nLenCanonOrdStereoTaut < 0) { - nErrorCode |= WARN_FAILED_STEREO; - } - if ( pCS->nLenLinearCTIsotopic < 0 || pCS->nLenLinearCTIsotopicTautomer < 0 || - pCS->nLenCanonOrdIsotopic < 0 || pCS->nLenCanonOrdIsotopicTaut < 0 ) { - nErrorCode |= WARN_FAILED_ISOTOPIC; - } - if ( pCS->nLenLinearCTIsotopicStereoCarb < 0 || pCS->nLenLinearCTIsotopicStereoDble < 0 || - pCS->nLenCanonOrdIsotopicStereo < 0 || pCS->nLenCanonOrdIsotopicStereoTaut < 0) { - nErrorCode |= WARN_FAILED_ISOTOPIC_STEREO; - } - pCanonRankAtoms = (AT_NUMB *)inchi_calloc( num_at_tg+1, sizeof(pCanonRankAtoms[0]) ); - pSortOrd = (AT_NUMB *)inchi_calloc( num_at_tg+1, sizeof(pSortOrd[0]) ); /* must have more than num_atoms */ - - if ( !pCanonRankAtoms || !pSortOrd ) { - nErrorCode = 0; - ret = CT_OUT_OF_RAM; /* */ - pINChI->nErrorCode = pINChI_Aux->nErrorCode = CT_OUT_OF_RAM; - goto exit_function; - } - - /* total charge */ - for ( i = 0, n = 0; i < num_atoms+num_removed_H; i ++ ) { - n += at[i].charge; - } - pINChI->nTotalCharge = n; - - /* number of atoms */ - pINChI->nNumberOfAtoms = num_atoms; - pINChI_Aux->nNumberOfAtoms = num_atoms; - - /* removed protons and detachable isotopic H */ - if ( bTautomeric && t_group_info ) { - pINChI_Aux->nNumRemovedProtons = t_group_info->tni.nNumRemovedProtons; - for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { - pINChI_Aux->nNumRemovedIsotopicH[i] = t_group_info->num_iso_H[i] - + t_group_info->tni.nNumRemovedProtonsIsotopic[i]; - } - if ( pINChI_Aux->bNormalizationFlags & FLAG_FORCE_SALT_TAUT ) { - pINChI->nFlags |= INCHI_FLAG_HARD_ADD_REM_PROTON; - } - if ( pINChI_Aux->bNormalizationFlags & (FLAG_NORM_CONSIDER_TAUT &~FLAG_PROTON_CHARGE_CANCEL) ) { - AddMOLfileError(pStrErrStruct, "Proton(s) added/removed"); - } - if ( pINChI_Aux->bNormalizationFlags & FLAG_PROTON_CHARGE_CANCEL ) { - AddMOLfileError(pStrErrStruct, "Charges neutralized"); - } - } - - /* abs or rel stereo may establish one of two canonical numberings */ - if ( (pCS->nLenLinearCTStereoCarb > 0 || pCS->nLenLinearCTStereoDble > 0) && - pCS->nLenCanonOrdStereo > 0 && - (pCS->LinearCTStereoCarb && pCS->LinearCTStereoCarbInv || - pCS->LinearCTStereoDble && pCS->LinearCTStereoDbleInv) && - pCS->nCanonOrdStereo && pCS->nCanonOrdStereoInv - ) { - - pCanonRank = pCanonRankAtoms; - pCanonOrd = pCS->nCanonOrdStereo; - pCanonRankInv = pSortOrd; - pCanonOrdInv = pCS->nCanonOrdStereoInv; - Stereo = pINChI->Stereo; - for ( i = 0; i < num_at_tg; i ++ ) { - pCanonRankInv[pCanonOrdInv[i]] = - pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); - } - /********************************************************************/ - /* copy stereo bonds and stereo centers; compare Inv and Abs stereo */ - /********************************************************************/ - nErrorCode = CopyLinearCTStereoToINChIStereo( Stereo, - pCS->LinearCTStereoCarb, pCS->nLenLinearCTStereoCarb, - pCS->LinearCTStereoDble, pCS->nLenLinearCTStereoDble - , pCanonOrd, pCanonRank, at, 0 /* non-isotopic */ - , pCS->LinearCTStereoCarbInv - , pCS->LinearCTStereoDbleInv - , pCanonOrdInv, pCanonRankInv ); - - if ( Stereo->t_parityInv && Stereo->nNumberInv ) { - if ( nUserMode & REQ_MODE_RELATIVE_STEREO ) { - pINChI->nFlags |= INCHI_FLAG_REL_STEREO; - } - if ( nUserMode & REQ_MODE_RACEMIC_STEREO ) { - pINChI->nFlags |= INCHI_FLAG_RAC_STEREO; - } - if ( Stereo->nCompInv2Abs ) { - if ( Stereo->nCompInv2Abs == -1 ) { - /* switch pointers in Stereo so that the stereo becomes the smallest (relative) */ - /* flag Stereo->nCompInv2Abs == -1 will keep track of this exchange */ - AT_NUMB *nNumberInv = Stereo->nNumberInv; - S_CHAR *t_parityInv = Stereo->t_parityInv; - Stereo->nNumberInv = Stereo->nNumber; - Stereo->t_parityInv = Stereo->t_parity; - Stereo->nNumber = nNumberInv; - Stereo->t_parity = t_parityInv; - /* switch pointers to set rel. stereo to pINChI_Aux->nOrigAtNosInCanonOrd - and inv. stereo to pINChI_Aux->nOrigAtNosInCanonOrdInv */ - switch_ptrs( &pCanonRank, &pCanonRankInv ); - switch_ptrs( &pCanonOrd, &pCanonOrdInv ); - bUseNumberingInv = 1; /* use inverted stereo numbering instead of normal */ - } - } - } - - for ( i = 0; i < num_atoms; i ++ ) { - pINChI_Aux->nOrigAtNosInCanonOrdInv[i] = at[pCanonOrdInv[i]].orig_at_number; - pINChI_Aux->nOrigAtNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; - } - if ( bUseNumberingInv ) { - /* switch ptrs back to avoid confusion */ - switch_ptrs( &pCanonRank, &pCanonRankInv ); - switch_ptrs( &pCanonOrd, &pCanonOrdInv ); - /* save inverted stereo ranks & order because it represents the smallest (relative) */ - memcpy( pCanonRank, pCanonRankInv, num_at_tg * sizeof(pCanonRank[0]) ); - /* change pCS->nCanonOrdStereo[] to inverted: */ - memcpy( pCanonOrd, pCanonOrdInv, num_at_tg * sizeof(pCanonOrd[0]) ); - } - pCanonRankInv = NULL; - pCanonOrdInv = NULL; - pOrigNosInCanonOrd = NULL; - - } else { /*------------------------------ no stereo */ - - pCanonOrd = pCS->nLenCanonOrdStereo > 0? pCS->nCanonOrdStereo : - pCS->nLenCanonOrd > 0? pCS->nCanonOrd : NULL; - pCanonRank = pCanonRankAtoms; - pOrigNosInCanonOrd = pINChI_Aux->nOrigAtNosInCanonOrd; - if ( pCanonOrd && pCanonRank ) { - for ( i = 0; i < num_atoms; i ++ ) { - pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); - pOrigNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; - } - for ( ; i < num_at_tg; i ++ ) { - pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); - } - } - } - /*pCanonOrdNonIso = pCanonOrd;*/ /* save for aux info */ - - - if ( pINChI_Aux->OrigInfo ) { - /* charges, radicals, valences */ - for ( i = 0; i < num_atoms; i ++ ) { - ii = pCanonOrd[i]; - if ( norm_at[ii].valence || norm_at[ii].num_H ) { - pINChI_Aux->OrigInfo[i].cCharge = norm_at[ii].charge; - pINChI_Aux->OrigInfo[i].cRadical = (norm_at[ii].radical==RADICAL_SINGLET)? 0 : - (norm_at[ii].radical==RADICAL_DOUBLET)? 1 : - (norm_at[ii].radical==RADICAL_TRIPLET)? 2 : - norm_at[ii].radical? 3 : 0 ; - pINChI_Aux->OrigInfo[i].cUnusualValence = - get_unusual_el_valence( norm_at[ii].el_number, norm_at[ii].charge, norm_at[ii].radical, - norm_at[ii].chem_bonds_valence, norm_at[ii].num_H, norm_at[ii].valence ); - } else { - /* charge of a single atom component is in the INChI; valence = 0 is standard */ - pINChI_Aux->OrigInfo[i].cRadical = (norm_at[ii].radical==RADICAL_SINGLET)? 0 : - (norm_at[ii].radical==RADICAL_DOUBLET)? 1 : - (norm_at[ii].radical==RADICAL_TRIPLET)? 2 : - norm_at[ii].radical? 3 : 0 ; - } - - } - } - - /* non-isotopic canonical numbers and equivalence of atoms (Aux) */ - pConstitEquNumb = pINChI_Aux->nConstitEquNumbers; /* contitutional equivalence */ - pSymmRank = pCS->nSymmRank; - if ( pCanonOrd && pCanonRank && pSymmRank && pConstitEquNumb ) { - for ( i = 0; i < num_atoms; i ++ ) { - pConstitEquNumb[i] = pSymmRank[pCanonOrd[i]]; /* constit. equ. ranks in order of canonical numbers */ - pSortOrd[i] = i; - } - for ( ; i < num_at_tg; i ++ ) { - pSortOrd[i] = MAX_ATOMS; /* for debugging only */ - } - pn_RankForSort = pConstitEquNumb; - qsort( pSortOrd, num_atoms, sizeof(pSortOrd[0]), CompRanksOrd ); - for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= num_atoms; j ++ ) { - if ( j == num_atoms || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) { - nMinOrd ++; - if ( j - i > 1 ) { - /* found a sequence of equivalent atoms: i..j-1 */ - while ( i < j ) { - pConstitEquNumb[pSortOrd[i++]] = nMinOrd; /* = min. canon. rank in the group of equ. atoms */ - } - /* at this point j == i */ - } else { - pConstitEquNumb[pSortOrd[i++]] = 0; /* means the atom is not equivalent to any other */ - } - nMinOrd = pSortOrd[j]; /* at the end j = num_atoms */ - } - } - } else { - nErrorCode |= ERR_NO_CANON_RESULTS; - ret = -1; /* program error; no breakpoint here */ - goto exit_function; - } - /* atomic numbers from the Periodic Table */ - for ( i = 0; i < num_atoms; i ++ ) { - pINChI->nAtom[i] = (int)at[pCanonOrd[i]].el_number; - } - /* connection table: atoms only (before 7-29-2003 pCS->LinearCT2 contained non-isotopic CT) */ - if ( pCS->nLenLinearCTAtOnly <= 0 || !pCS->LinearCT || !pINChI->nConnTable ) { - nErrorCode |= ERR_NO_CANON_RESULTS; - ret = -2; - goto exit_function; - } - memcpy( pINChI->nConnTable, pCS->LinearCT, sizeof(pINChI->nConnTable[0])*pCS->nLenLinearCTAtOnly); - pINChI->lenConnTable = pCS->nLenLinearCTAtOnly; - - /* tautomeric group(s) canonical representation */ - len = 0; - if ( bTautomeric && 0 < (n = SortTautomerGroupsAndEndpoints( t_group_info, num_atoms, num_at_tg, pCanonRank )) ) { - /* SortTautomerGroupsAndEndpoints() produces canonically ordered t-groups */ - pINChI->nFlags |= (t_group_info->bTautFlagsDone & TG_FLAG_ALL_SALT_DONE)? INCHI_FLAG_ACID_TAUT : 0; - /* number of tautomeric groups */ - pINChI->nTautomer[len ++] = (AT_NUMB)n; - /* store each tautomeric group, one by one */ - for ( i = 0; i < n; i ++ ) { - g = (int)t_group_info->tGroupNumber[i]; /* original group numbers in sorted order */ - t_group = t_group_info->t_group + g; /* pointer to the tautomeric group */ - /* NumAt+INCHI_T_NUM_MOVABLE (group length excluding this number) */ - pINChI->nTautomer[len ++] = t_group->nNumEndpoints+INCHI_T_NUM_MOVABLE; - /* Num(H), Num(-) */ - for ( j = 0; j < INCHI_T_NUM_MOVABLE && j < T_NUM_NO_ISOTOPIC; j ++ ) - pINChI->nTautomer[len ++] = t_group->num[j]; - for ( j = T_NUM_NO_ISOTOPIC; j < INCHI_T_NUM_MOVABLE; j ++ ) - pINChI->nTautomer[len ++] = 0; /* should not happen */ - /* tautomeric group endpoint canonical numbers, pre-sorted in ascending order */ - for ( j = (int)t_group->nFirstEndpointAtNoPos, - m = j + (int)t_group->nNumEndpoints; j < m; j ++ ) { - pINChI->nTautomer[len ++] = pCanonRank[(int)t_group_info->nEndpointAtomNumber[j]]; /* At[j] */ - } - } - pINChI->lenTautomer = len; - pINChI_Aux->nNumberOfTGroups = n; - } else { - pINChI->lenTautomer = 0; - pINChI_Aux->nNumberOfTGroups = 0; - if ( t_group_info && ((t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) || - t_group_info->nNumIsotopicEndpoints>1 && - (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE))) - ) { - /* only protons (re)moved or added */ - pINChI->lenTautomer = 1; - pINChI->nTautomer[0] = 0; - } - } - - /* number of H (excluding tautomeric) */ - if ( pCS->nNum_H ) { - for ( i = 0; i < num_atoms; i ++ ) { - pINChI->nNum_H[i] = pCS->nNum_H[i]; - } - } - /* number of fixed H (tautomeric H in non-tautomeric representation) */ - if ( pCS->nNum_H_fixed && !pINChI->lenTautomer ) { - for ( i = 0; i < num_atoms; i ++ ) { - pINChI->nNum_H_fixed[i] = pCS->nNum_H_fixed[i]; - pINChI->nNum_H[i] += pCS->nNum_H_fixed[i]; - } - } - - /*********************************************************** - * tautomeric group(s) numbering and symmetry; - * should not depend on switching to rel. stereo numbering - */ - if ( pINChI->lenTautomer && (n=pINChI_Aux->nNumberOfTGroups) ) { - pCanonOrdTaut = pCS->nLenCanonOrdStereoTaut > 0? pCS->nCanonOrdStereoTaut : - pCS->nLenCanonOrdTaut > 0? pCS->nCanonOrdTaut : NULL; - pConstitEquNumb = pINChI_Aux->nConstitEquTGroupNumbers; - pSymmRank = pCS->nSymmRankTaut; - if ( pCanonOrdTaut && pSymmRank && pConstitEquNumb ) { - for ( i = 0; i < n; i ++ ) { - pConstitEquNumb[i] = pSymmRank[pCanonOrdTaut[i]]; - pSortOrd[i] = i; - } - pn_RankForSort = pConstitEquNumb; - qsort( pSortOrd, n, sizeof(pSortOrd[0]), CompRanksOrd ); - for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= n; j ++ ) { - if ( j == n || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) { - nMinOrd ++; /* make is start from 1, not from zero */ - if ( j - i > 1 ) { - /* found a sequence of more than one equivalent t-groups: i..j-1 */ - while ( i < j ) { - pConstitEquNumb[pSortOrd[i++]] = nMinOrd; - } - } else { - pConstitEquNumb[pSortOrd[i++]] = 0; - } - nMinOrd = pSortOrd[j]; /* at the end j == n */ - } - } - } - } - - /* Allocate and fill Hill formula */ - if ( !(pINChI->szHillFormula = AllocateAndFillHillFormula( pINChI ) ) ) { - nErrorCode = 0; - ret = CT_WRONG_FORMULA; /* CT_OUT_OF_RAM;*/ /* */ - pINChI->nErrorCode = pINChI_Aux->nErrorCode = ret; - goto exit_function; - } - - if ( nStereoUnmarkMode = UnmarkAllUndefinedUnknownStereo( pINChI->Stereo, nUserMode ) ) { - pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU)? INCHI_FLAG_SC_IGN_ALL_UU : 0; - pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU)? INCHI_FLAG_SB_IGN_ALL_UU : 0; - if ( (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU) || - (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU) ) { - AddMOLfileError(pStrErrStruct, "Omitted undefined stereo"); - } - } - - /*************************/ - /* mark ambiguous stereo */ - /*************************/ - MarkAmbiguousStereo( at, norm_at, 0 /* non-isotopic */, pCanonOrd, - pCS->LinearCTStereoCarb, pCS->nLenLinearCTStereoCarb, - pCS->LinearCTStereoDble, pCS->nLenLinearCTStereoDble ); - - - /************************************************************************ - * - * isotopic part - */ - /* abs or rel stereo may establish one of two canonical numberings */ - if ( (pCS->nLenLinearCTIsotopicStereoCarb > 0 || pCS->nLenLinearCTIsotopicStereoDble > 0) && - pCS->nLenCanonOrdIsotopicStereo > 0 && - (pCS->LinearCTIsotopicStereoCarb && pCS->LinearCTIsotopicStereoCarbInv || - pCS->LinearCTIsotopicStereoDble && pCS->LinearCTIsotopicStereoDbleInv) && - pCS->nCanonOrdIsotopicStereo && pCS->nCanonOrdIsotopicStereoInv - ) { - /* found isotopic stereo */ - pCanonRank = pCanonRankAtoms; - pCanonOrd = pCS->nCanonOrdIsotopicStereo; - pCanonRankInv = pSortOrd; - pCanonOrdInv = pCS->nCanonOrdIsotopicStereoInv; - Stereo = pINChI->StereoIsotopic; - for ( i = 0; i < num_at_tg; i ++ ) { - pCanonRankInv[pCanonOrdInv[i]] = - pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); - } - /********************************************************************/ - /* copy stereo bonds and stereo centers; compare Inv and Abs stereo */ - /********************************************************************/ - nErrorCode = CopyLinearCTStereoToINChIStereo( Stereo, - pCS->LinearCTIsotopicStereoCarb, pCS->nLenLinearCTIsotopicStereoCarb, - pCS->LinearCTIsotopicStereoDble, pCS->nLenLinearCTIsotopicStereoDble - , pCanonOrd, pCanonRank, at, 1 /* isotopic */ - , pCS->LinearCTIsotopicStereoCarbInv - , pCS->LinearCTIsotopicStereoDbleInv - , pCanonOrdInv, pCanonRankInv ); - - if ( Stereo->t_parityInv && Stereo->nNumberInv ) { - if ( nUserMode & REQ_MODE_RELATIVE_STEREO ) { - pINChI->nFlags |= INCHI_FLAG_REL_STEREO; - } - if ( nUserMode & REQ_MODE_RACEMIC_STEREO ) { - pINChI->nFlags |= INCHI_FLAG_RAC_STEREO; - } - if ( Stereo->nCompInv2Abs ) { - if ( Stereo->nCompInv2Abs == -1 ) { - /* switch pointers so that the stereo becomes the smallest (relative) */ - /* flag Stereo->nCompInv2Abs == -1 will keep track of this exchange */ - AT_NUMB *nNumberInv = Stereo->nNumberInv; - S_CHAR *t_parityInv = Stereo->t_parityInv; - Stereo->nNumberInv = Stereo->nNumber; - Stereo->t_parityInv = Stereo->t_parity; - Stereo->nNumber = nNumberInv; - Stereo->t_parity = t_parityInv; - switch_ptrs( &pCanonRank, &pCanonRankInv ); - switch_ptrs( &pCanonOrd, &pCanonOrdInv ); - bUseIsotopicNumberingInv = 1; - } - } - } - - for ( i = 0; i < num_atoms; i ++ ) { - pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv[i] = at[pCanonOrdInv[i]].orig_at_number; - pINChI_Aux->nIsotopicOrigAtNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; - } - if ( bUseIsotopicNumberingInv ) { - switch_ptrs( &pCanonRank, &pCanonRankInv ); - switch_ptrs( &pCanonOrd, &pCanonOrdInv ); - memcpy( pCanonRank, pCanonRankInv, num_at_tg * sizeof(pCanonRank[0]) ); - memcpy( pCanonOrd, pCanonOrdInv, num_at_tg * sizeof(pCanonOrd[0]) ); - } - pCanonRankInv = NULL; - pCanonOrdInv = NULL; - pOrigNosInCanonOrd = NULL; - - } else { - /* no isotopic stereo */ - pCanonOrd = pCS->nLenCanonOrdIsotopicStereo > 0? pCS->nCanonOrdIsotopicStereo : - pCS->nLenCanonOrdIsotopic > 0? pCS->nCanonOrdIsotopic : NULL; - pCanonRank = pCanonRankAtoms; - pOrigNosInCanonOrd = pINChI_Aux->nIsotopicOrigAtNosInCanonOrd; - if ( pCanonOrd && pCanonRank ) { - for ( i = 0; i < num_atoms; i ++ ) { /* Fix13 -- out of bounds */ - pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); - pOrigNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; - } - for ( ; i < num_at_tg; i ++ ) { /* Fix13 -- out of bounds */ - pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); - } - } - } - /*pCanonOrdIso = pCanonOrd;*/ - - pConstitEquNumb = pINChI_Aux->nConstitEquIsotopicNumbers; - pSymmRank = pCS->nSymmRankIsotopic; - if ( pCanonOrd && pCanonRank && pConstitEquNumb && pSymmRank ) { - for ( i = 0; i < num_atoms; i ++ ) { - pConstitEquNumb[i] = pSymmRank[pCanonOrd[i]]; - pSortOrd[i] = i; - } - for ( ; i < num_at_tg; i ++ ) { - pSortOrd[i] = i; - } - pn_RankForSort = pConstitEquNumb; - qsort( pSortOrd, num_atoms, sizeof(pSortOrd[0]), CompRanksOrd ); - for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= num_atoms; j ++ ) { - if ( j == num_atoms || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) { - nMinOrd ++; - if ( j - i > 1 ) { - /* found a sequence of equivalent atoms: i..j-1 */ - while ( i < j ) { - pConstitEquNumb[pSortOrd[i++]] = nMinOrd; - } - } else { - pConstitEquNumb[pSortOrd[i++]] = 0; /* nMinOrd; */ - } - nMinOrd = pSortOrd[j]; - } - } - } else { - goto exit_function; /* no isotopic info available */ - } - /* isotopic atoms */ - n = pINChI->nNumberOfIsotopicAtoms = pCS->nLenLinearCTIsotopic; - for ( i = 0; i < n; i ++ ) { - pINChI->IsotopicAtom[i].nAtomNumber = pCS->LinearCTIsotopic[i].at_num; - pINChI->IsotopicAtom[i].nIsoDifference = pCS->LinearCTIsotopic[i].iso_atw_diff; - pINChI->IsotopicAtom[i].nNum_H = pCS->LinearCTIsotopic[i].num_1H; - pINChI->IsotopicAtom[i].nNum_D = pCS->LinearCTIsotopic[i].num_D; - pINChI->IsotopicAtom[i].nNum_T = pCS->LinearCTIsotopic[i].num_T; - } - /* isotopic tautomeric groups */ - n = pINChI->nNumberOfIsotopicTGroups = pCS->nLenLinearCTIsotopicTautomer; - for ( i = 0; i < n; i ++ ) { - pINChI->IsotopicTGroup[i].nTGroupNumber = pCS->LinearCTIsotopicTautomer[i].tgroup_num; - pINChI->IsotopicTGroup[i].nNum_H = pCS->LinearCTIsotopicTautomer[i].num[2]; - pINChI->IsotopicTGroup[i].nNum_D = pCS->LinearCTIsotopicTautomer[i].num[1]; - pINChI->IsotopicTGroup[i].nNum_T = pCS->LinearCTIsotopicTautomer[i].num[0]; - } - /* atoms that may exchange isotopic H-atoms */ - if ( pCS->nExchgIsoH && pINChI->nPossibleLocationsOfIsotopicH ) { - for ( i = 0, j = 1; i < num_atoms; i ++ ) { - if ( pCS->nExchgIsoH[i] ) { - pINChI->nPossibleLocationsOfIsotopicH[j++] = (AT_NUMB)(i+1); /* canonical number */ - } - } - pINChI->nPossibleLocationsOfIsotopicH[0] = (AT_NUMB)j; /* length including the 0th element */ - } - - if ( nStereoUnmarkMode = UnmarkAllUndefinedUnknownStereo( pINChI->StereoIsotopic, nUserMode ) ) { - pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU)? INCHI_FLAG_SC_IGN_ALL_ISO_UU : 0; - pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU)? INCHI_FLAG_SC_IGN_ALL_ISO_UU : 0; - if ( (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU) || - (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU) ) { - AddMOLfileError(pStrErrStruct, "Omitted undefined stereo"); - } - } - /* mark ambiguous stereo */ - MarkAmbiguousStereo( at, norm_at, 1 /* isotopic */, pCanonOrd, - pCS->LinearCTIsotopicStereoCarb, pCS->nLenLinearCTIsotopicStereoCarb, - pCS->LinearCTIsotopicStereoDble, pCS->nLenLinearCTIsotopicStereoDble ); - - /*********************************************************** - * isotopic tautomeric group(s) numbering and symmetry; - * should not depend on switching to rel. stereo numbering - */ - if ( pINChI->lenTautomer && pINChI_Aux->nConstitEquIsotopicTGroupNumbers && pCS->nSymmRankIsotopicTaut && - (pCS->nLenLinearCTIsotopic || pCS->nLenLinearCTIsotopicTautomer) && - t_group_info && t_group_info->num_t_groups > 0 ) { - n = t_group_info->num_t_groups; - pCanonOrdTaut = pCS->nLenCanonOrdIsotopicStereoTaut > 0? - (n=pCS->nLenCanonOrdIsotopicStereoTaut, pCS->nCanonOrdIsotopicStereoTaut) : - pCS->nLenCanonOrdIsotopicTaut > 0? - (n=pCS->nLenCanonOrdIsotopicTaut,pCS->nCanonOrdIsotopicTaut) : (n=0,(AT_RANK*)NULL); - pConstitEquNumb = pINChI_Aux->nConstitEquIsotopicTGroupNumbers; - pSymmRank = pCS->nSymmRankIsotopicTaut; - if ( pCanonOrdTaut && pSymmRank && pConstitEquNumb && n > 0 ) { - for ( i = 0; i < n; i ++ ) { - pConstitEquNumb[i] = pSymmRank[pCanonOrdTaut[i]]; - pSortOrd[i] = i; - } - pn_RankForSort = pConstitEquNumb; - qsort( pSortOrd, n, sizeof(pSortOrd[0]), CompRanksOrd ); - for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= n; j ++ ) { - if ( j == n || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) { - nMinOrd ++; - if ( j - i > 1 ) { - /* found a sequence of equivalent t-groups: i..j-1 */ - while ( i < j ) { - pConstitEquNumb[pSortOrd[i++]] = nMinOrd; - } - } else { - pConstitEquNumb[pSortOrd[i++]] = 0; /* nMinOrd; */ - } - nMinOrd = pSortOrd[j]; /* at the end j = n */ - } - } - } - } - - -exit_function: - if ( pCanonRankAtoms ) - inchi_free( pCanonRankAtoms ); - if ( pSortOrd ) - inchi_free( pSortOrd ); - - pINChI->nErrorCode |= nErrorCode; - pINChI_Aux->nErrorCode |= nErrorCode; - - return ret; -} diff --git a/INCHI-1-SRC/INCHI/common/ichimake.c b/INCHI-1-SRC/INCHI/common/ichimake.c deleted file mode 100644 index 5135d2c..0000000 --- a/INCHI-1-SRC/INCHI/common/ichimake.c +++ /dev/null @@ -1,4747 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include - - -#include "mode.h" - -#if ( TEST_RENUMB_ATOMS == 1 ) -#include "ichitime.h" -#endif -#include "inpdef.h" -#include "ichi.h" -#include "strutil.h" -#include "util.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "ichicant.h" -#include "ichicano.h" -#include "ichicomn.h" - -#include "ichicomp.h" -#include "ichimain.h" -#include "ichimake.h" -#include "ichister.h" -#include "ichi_io.h" - -int inp2spATOM( inp_ATOM *inp_at, int num_inp_at, sp_ATOM *at ); -int GetElementAndCount( const char **f, char *szEl, int *count ); -int CompareHillFormulas( const char *f1, const char *f2 ); -int CompareInchiStereo( INChI_Stereo *Stereo1, INCHI_MODE nFlags1, INChI_Stereo *Stereo2, INCHI_MODE nFlags2 ); -int CompareReversedStereoINChI( INChI_Stereo *s1/* InChI from reversed struct */, INChI_Stereo *s2 /* input InChI */); -int GetAtomOrdNbrInCanonOrd( inp_ATOM *norm_at, AT_NUMB *nAtomOrdNbr, - AT_NUMB *nOrigAtNosInCanonOrd, int num_at ); -int FillOutCanonInfAtom(inp_ATOM *norm_at, INF_ATOM_DATA *inf_norm_at_data, int init_num_at, int bIsotopic, - INChI *pINChI, INChI_Aux *pINChI_Aux, int bAbcNumbers, INCHI_MODE nMode); -int FillOutOneCanonInfAtom(inp_ATOM *inp_norm_at, INF_ATOM_DATA *inf_norm_at_data, - AT_NUMB *pStereoFlags, int init_num_at, int offset, int offset_H, int bIsotopic, - INChI *pINChI, INChI_Aux *pINChI_Aux, int bAbcNumbers, INCHI_MODE nMode); -int FillOutInputInfAtom(inp_ATOM *inp_at, INF_ATOM_DATA *inf_at_data, int init_num_at, int num_removed_H, - int bAdd_DT_to_num_H, int nNumRemovedProtons, NUM_H *nNumRemovedProtonsIsotopic, int bIsotopic, int bAbcNumbers); - -int CheckCanonNumberingCorrectness( - int num_atoms, int num_at_tg, - sp_ATOM *at, CANON_STAT *pCS, int bTautomeric, - char *pStrErrStruct ); - -static int CompareDfsDescendants4CT( const void *a1, const void *a2 ); -int GetSp3RelRacAbs( const INChI *pINChI, INChI_Stereo *Stereo ); - - -#if ( TEST_RENUMB_ATOMS == 1 || READ_INCHI_STRING == 1 ) /* { */ -int CompareStereoINChI( INChI_Stereo *s1, INChI_Stereo *s2 ); -#endif - -#if ( READ_INCHI_STRING == 1 ) /* { */ -/*************************************************************************************/ - -int CompareReversedStereoINChI2( INChI_Stereo *s1, INChI_Stereo *s2, ICR *picr); - -#endif -/**********************************************************************************************/ -int inp2spATOM( inp_ATOM *inp_at, int num_inp_at, sp_ATOM *at ) -{ - int i, j, val; - memset( at, 0, sizeof(at[0])*num_inp_at ); - for ( i = 0; i < num_inp_at; i ++ ) { - strncpy( at[i].elname, inp_at[i].elname, sizeof(at[0].elname) ); - at[i].el_number = (U_CHAR)get_periodic_table_number( at[i].elname ); - val = at[i].valence = inp_at[i].valence; - for ( j = 0; j < val; j ++ ) { - at[i].neighbor[j] = inp_at[i].neighbor[j]; - at[i].bond_type[j] = inp_at[i].bond_type[j]; - } - at[i].chem_bonds_valence = inp_at[i].chem_bonds_valence; - at[i].orig_at_number = inp_at[i].orig_at_number; - at[i].orig_compt_at_numb= inp_at[i].orig_compt_at_numb; - at[i].endpoint = inp_at[i].endpoint; - at[i].iso_atw_diff = inp_at[i].iso_atw_diff; - at[i].num_H = inp_at[i].num_H; - at[i].cFlags = inp_at[i].cFlags; - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { - at[i].num_iso_H[j] = inp_at[i].num_iso_H[j]; - } - at[i].charge = inp_at[i].charge; - at[i].radical = inp_at[i].radical; - -#if ( FIND_RING_SYSTEMS == 1 ) - at[i].nBlockSystem = inp_at[i].nBlockSystem; - at[i].bCutVertex = inp_at[i].bCutVertex; - at[i].nRingSystem = inp_at[i].nRingSystem; - at[i].nNumAtInRingSystem = inp_at[i].nNumAtInRingSystem; -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) - at[i].nDistanceFromTerminal = inp_at[i].nDistanceFromTerminal; -#endif -#endif - -/* - at[i].x = inp_at[i].x; - at[i].y = inp_at[i].y; - at[i].z = inp_at[i].z; -*/ - } - return 0; -} -/**********************************************************************************************/ -int GetElementAndCount( const char **f, char *szEl, int *count ) -{ - const char *p = *f; - char *q; - int i = 0; - if ( *p ) { - if ( isupper( UCINT *p ) ) { - szEl[i++] = *p++; - if ( *p && islower( UCINT *p ) ) { - szEl[i++] = *p++; - } - szEl[i] = '\0'; - if ( 1 == i && szEl[0] == 'C' ) { - szEl[0] = 'A'; /* less than any element: */ - /* carbon-containing compounds should be first */ - } - if ( *p && isdigit( UCINT *p ) ) { - *count = strtol( p, &q, 10 ); - p = q; - } else { - *count = 1; - } - *f = p; /* next element; */ - return 1; - } - return -1; /* not a chemical formula */ - } - strcpy( szEl, "Zz" ); /* zero termination 'element' is larger than any other element */ - *count = 9999; /* zero termination 'element count' is larger than any other count */ - return 0; -} -/********************************************************************************************** - - E1 < E2 if strcmp( E1, E2) < 0 OR E2 is empty and E1 is not - n1 < n2 if value n1 > n2 - - Sorting order: - - C10H22N - C10H22 - C2 - Ag2Cl2 - Ag2Cl - Ag2F2 - Ag2 - AgCl - AgF - F6S - F2S - -**********************************************************************************************/ -int CompareHillFormulas( const char *f1, const char *f2 ) -{ - char szEl1[4], szEl2[4]; - int count1, count2, ret1, ret2, ret; - - do { - ret1 = GetElementAndCount( &f1, szEl1, &count1 ); - ret2 = GetElementAndCount( &f2, szEl2, &count2 ); - if ( 0 <= ret1 && 0 <= ret2 ) { - if ( ret = strcmp( szEl1, szEl2 ) ) { - return ret; /* lexicographic order, string termination > any character */ - } - if ( ret = count2 - count1 ) { - return ret; /* inverse atom count order */ - } - } else { - return 0; /* program error */ - } - - } while ( 0 < ret1 && 0 < ret2 ); - - return 0; -} -/**********************************************************************************************/ -int CompareHillFormulasNoH( const char *f1, const char *f2, int *num_H1, int *num_H2 ) -{ - char szEl1[4], szEl2[4]; - int count1, count2, ret1, ret2, ret; - - do { - ret1 = GetElementAndCount( &f1, szEl1, &count1 ); - if ( 0 < ret1 && szEl1[0] == 'H' && !szEl1[1] ) { - *num_H1 += count1; - ret1 = GetElementAndCount( &f1, szEl1, &count1 ); - } - ret2 = GetElementAndCount( &f2, szEl2, &count2 ); - if ( 0 < ret2 && szEl2[0] == 'H' && !szEl2[1] ) { - *num_H2 += count2; - ret2 = GetElementAndCount( &f2, szEl2, &count2 ); - } - if ( 0 <= ret1 && 0 <= ret2 ) { - if ( ret = strcmp( szEl1, szEl2 ) ) { - return ret; /* lexicographic order, string termination > any character */ - } - if ( ret = count2 - count1 ) { - return ret; /* inverse atom count order */ - } - } else { - return 0; /* program error */ - } - - } while ( 0 < ret1 && 0 < ret2 ); - - return 0; -} - -/**************************************************************/ -int CompareTautNonIsoPartOfINChI( const INChI *i1, const INChI *i2 ) -{ - int len1, len2, ret, i; - - len1 = i1->lenTautomer > 0 && i1->nTautomer[0]? i1->lenTautomer:0; - len2 = i2->lenTautomer > 0 && i2->nTautomer[0]? i2->lenTautomer:0; - if ( ret = len2 - len1 ) { - return ret; - } - for ( i = 0; i < len1; i ++ ) { - if ( ret = (int)i2->nTautomer[i] - (int)i1->nTautomer[i] ) - return ret; - } - return 0; -} - -/**********************************************************************************************/ -/* sorting in descending order: return -1 if *p1 > *p2, return +1 if *p1 < *p2 */ -/**********************************************************************************************/ -int CompINChITautVsNonTaut(const INCHI_SORT *p1, const INCHI_SORT *p2, int bCompareIsotopic) -{ - int ret, num, i, num_H1, num_H2; - - const INChI *i1 = NULL; /* Mobile-H layers in Mobile-H sorting order */ - const INChI *i2 = NULL; /* Fixed-H layers in Fixed-H sorting order */ - - int n1; /* TAUT_YES if tautomeric i1 exists, otherwise TAUT_NON */ - - /* INChI_Stereo *Stereo1, *Stereo2; */ - - n1 = ( p1->pINChI[TAUT_YES] && p1->pINChI[TAUT_YES]->nNumberOfAtoms )? TAUT_YES : TAUT_NON; - - i1 = p1->pINChI[n1]; - i2 = (n1 == TAUT_YES && p2->pINChI[TAUT_NON] && - p2->pINChI[TAUT_NON]->nNumberOfAtoms)? p2->pINChI[TAUT_NON] : (const INChI *)NULL; - - - /* non-deleted-non-empty < deleted < empty */ - if ( i1 && !i2 ) - return 0; /* non-empty is the smallest (first) */ - if ( !i1 && i2 ) - return 0; - if ( !i1 && !i2 ) - return 0; - if ( i1->bDeleted ) - return 1; /* deleted is the largest (last) among non-empty */ - if ( i2->bDeleted ) - return -1; - - if ( i1->nNumberOfAtoms > 0 && !i2->nNumberOfAtoms ) - return 0; - - i2 = i2; - - num_H1 = num_H2 = 0; - - /* do not compare terminal H */ - if ( ret = CompareHillFormulasNoH( i1->szHillFormula, i2->szHillFormula, &num_H1, &num_H2 ) ) { - return ret; /* lexicographic order except the shorter one is greater (last): CH2O < CH2; C3XX < C2XX */ - } - - /********************************************************* - compare non-isotopic non-tautomeric part - *********************************************************/ - - /* compare number of atoms (excluding terminal H) */ - if ( ret = i2->nNumberOfAtoms - i1->nNumberOfAtoms ) - return ret; /* more atoms first */ - - /* compare elements (excluding terminal H) */ - num = i1->nNumberOfAtoms; - for ( i = 0; i < num; i ++ ) { /* should always be equal if Hill formulas are same */ - if ( ret = (int)i2->nAtom[i] - (int)i1->nAtom[i] ) - return ret; /* greater periodic number first */ - } - /********************************************************** - compare connection tables - ***********************************************************/ - if ( ret = i2->lenConnTable - i1->lenConnTable ) - return ret; /* longer connection table first */ - num = i2->lenConnTable; - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)i2->nConnTable[i] - (int)i1->nConnTable[i] ) - return ret; /* greater connection table first */ - } - /********************************************************* - compare compare total number of H (inverse: H3 < H2 ) - **********************************************************/ - if ( ret = num_H2 - num_H1 ) - return ret; - /********************************************************* - compare non-tautomeric num_H: N < NH3 < NH2 < NH - **********************************************************/ - num = i1->nNumberOfAtoms; - for ( i = 0; i < num; i ++ ) { - if ( i2->nNum_H[i] != i1->nNum_H[i] ) { - return !i2->nNum_H[i]? 1 : /* no H first */ - !i1->nNum_H[i]? -1 : - (int)i2->nNum_H[i] - (int)i1->nNum_H[i]; - } - } - /********************************************************* - compare non-isotopic tautomeric part - *********************************************************/ - if ( ret = CompareTautNonIsoPartOfINChI( i1, i2) ) { - return ret; - } - /* - if ( ret = i2->lenTautomer - i1->lenTautomer ) - return ret; - num = inchi_min( i2->lenTautomer, i1->lenTautomer ); - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)i2->nTautomer[i] - (int)i1->nTautomer[i] ) - return ret; - } - */ - /********************************************************* - * * - * at this point both components are either tautomeric * - * or non-tautomeric * - * * - *********************************************************/ - - /********************************************************* - non-tautomeric "fixed H" specific - *********************************************************/ - if ( /*TAUT_NON == bTaut &&*/ (i2 && i2->nNum_H_fixed ) ) { - /* first, compare non-tautomeric chem. formulas -- they may be different */ - /* secondly, compare fixed-H distribution */ - if ( i2->nNum_H_fixed ) { - num = i2->nNumberOfAtoms; - for ( i = 0; i < num; i ++ ) { - if ( i2->nNum_H_fixed[i] != 0 ) { - return 1; - } - } - } - } - /********************************************************* - compare non-isotopic stereo - *********************************************************/ - ret = CompareInchiStereo( i1->Stereo, i1->nFlags, i2->Stereo, i2->nFlags ); - if ( ret ) { - return ret; - } - /******************************************************* - do not switch back to tautomeric i1, i2 - *******************************************************/ - /* -- how to switch back -- - if ( i1t ) { - i1 = i1t; - i1t = NULL; - } - if ( i2t ) { - i2 = i2t; - i2t = NULL; - } - */ - /****************************************************** - compare isotopic non-tautomeric part - ******************************************************/ - if ( bCompareIsotopic ) { - if ( ret = i2->nNumberOfIsotopicAtoms - i1->nNumberOfIsotopicAtoms ) - return ret; - num = i1->nNumberOfIsotopicAtoms; - /* compare isotopic atoms */ - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)i2->IsotopicAtom[i].nAtomNumber - (int)i1->IsotopicAtom[i].nAtomNumber ) - return ret; - if ( ret = (int)i2->IsotopicAtom[i].nIsoDifference - (int)i1->IsotopicAtom[i].nIsoDifference ) - return ret; - } - /* compare isotopic H */ - /* if tautomeric comparison mode then here are compared only non-tautomeric H */ - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)i2->IsotopicAtom[i].nNum_T - (int)i1->IsotopicAtom[i].nNum_T ) - return ret; - if ( ret = (int)i2->IsotopicAtom[i].nNum_D - (int)i1->IsotopicAtom[i].nNum_D ) - return ret; - if ( ret = (int)i2->IsotopicAtom[i].nNum_H - (int)i1->IsotopicAtom[i].nNum_H ) - return ret; - } - /***************************************************** - compare isotopic tautomeric part - *****************************************************/ - if ( ret = i2->nNumberOfIsotopicTGroups || i1->nNumberOfIsotopicTGroups ) - return ret; - /* - num = i1->nNumberOfIsotopicTGroups; - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)i2->IsotopicTGroup[i].nTGroupNumber - (int)i1->IsotopicTGroup[i].nTGroupNumber ) - return ret; - if ( ret = (int)i2->IsotopicTGroup[i].nNum_T - (int)i1->IsotopicTGroup[i].nNum_T ) - return ret; - if ( ret = (int)i2->IsotopicTGroup[i].nNum_D - (int)i1->IsotopicTGroup[i].nNum_D ) - return ret; - if ( ret = (int)i2->IsotopicTGroup[i].nNum_H - (int)i1->IsotopicTGroup[i].nNum_H ) - return ret; - } - */ - /**************************************************** - compare isotopic stereo - ****************************************************/ - ret = CompareInchiStereo( i1->StereoIsotopic, i1->nFlags, i2->StereoIsotopic, i2->nFlags ); - if ( ret ) { - return ret; - } - - } - - - /********************************************************** - compare charges: non-charged first, then in order of - ascending charges (negative first) - ***********************************************************/ - if ( i2->nTotalCharge && i1->nTotalCharge ) { - /* both are charged; smaller charges first */ - ret = (int)i1->nTotalCharge - (int)i2->nTotalCharge; - return ret; - } - if ( ret = (i1->nTotalCharge? 1:0) - (i2->nTotalCharge? 1:0) ) { - /* only one is charged; uncharged first */ - return ret; - } - /* stable sort */ - /*ret = p1->ord_number - p2->ord_number;*/ - - return ret; -} - -/*************************** stereo ***********************************************************/ -typedef enum tagSp3StereoTypeTmp { - SP3_NONE = 0, /* no sp3 stereo: no /t, /m, /s segments */ - /* /t is present: */ - SP3_ONLY = 1, /* no /s or /m segment: inversion leaves the structure unchanged */ - SP3_ABS = 2, /* abs stereo: both /m and /s are present */ - SP3_REL = 4, /* rel stereo: /s is present, /m is not */ - SP3_RAC = 8, /* racemic stereo: /s is presen, /m is nott */ - SP3_TYPE = (SP3_ABS|SP3_REL|SP3_RAC), /* bitmap for checking the presence of /m */ - SP3_ANY = (SP3_ABS|SP3_REL|SP3_RAC|SP3_ONLY) /* bitmap for checking the presence of /t */ -} SP3_TYPE_TMP; - -/**********************************************************************************************/ -int GetSp3RelRacAbs( const INChI *pINChI, INChI_Stereo *Stereo ) -{ - int nRet = SP3_NONE; - if ( pINChI && !pINChI->bDeleted && Stereo && 0 < Stereo->nNumberOfStereoCenters ) { - if ( 0 != Stereo->nCompInv2Abs ) { - if ( pINChI->nFlags & INCHI_FLAG_REL_STEREO ) { -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - if ( 1 < Stereo->nNumberOfStereoCenters ) { - nRet = SP3_REL; - } -#else - nRet = SP3_REL; -#endif - } else - if ( pINChI->nFlags & INCHI_FLAG_RAC_STEREO ) { -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - if ( 1 < Stereo->nNumberOfStereoCenters ) { - nRet = SP3_REL; - } -#else - nRet = SP3_RAC; -#endif - } else { - nRet = SP3_ABS; - } - } else -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - if ( !(( pINChI->nFlags & (INCHI_FLAG_REL_STEREO|INCHI_FLAG_RAC_STEREO) ) && 1 == Stereo->nNumberOfStereoCenters) ) -#endif - { - nRet = SP3_ONLY; /* SP3_NONE if relative stereo and 1 stereocenter */ - } - } - return nRet; -} - -/* char sDifSegs[DIFL_LENGTH][DIFS_LENGTH]; */ - -/**********************************************************************************************/ -/* sorting in descending order: return -1 if *p1 > *p2, return +1 if *p1 < *p2 */ -/**********************************************************************************************/ -int CompINChILayers(const INCHI_SORT *p1, const INCHI_SORT *p2, - char sDifSegs[][DIFS_LENGTH], int bFixTranspChargeBug ) -{ - int ret = 0, num, i, num_H1, num_H2; - - const INChI *i1 = NULL; /* Mobile-H layers in Mobile-H sorting order */ - const INChI *i2 = NULL; /* Fixed-H layers in Fixed-H sorting order */ - - int n1; /* TAUT_YES if tautomeric i1 exists, otherwise TAUT_NON */ - - INChI_Stereo *Stereo1, *Stereo2; - INChI_Stereo *IsoStereo1, *IsoStereo2; - int bRelRac[DIFL_LENGTH]; - char *psDifSegs; - - n1 = ( p1->pINChI[TAUT_YES] && p1->pINChI[TAUT_YES]->nNumberOfAtoms )? TAUT_YES : TAUT_NON; - - i1 = p1->pINChI[n1]; - i2 = (n1 == TAUT_YES && p2->pINChI[TAUT_NON] && - p2->pINChI[TAUT_NON]->nNumberOfAtoms)? p2->pINChI[TAUT_NON] : (const INChI *)NULL; - - num_H1 = num_H2 = 0; - memset( bRelRac, DIFV_BOTH_EMPTY, sizeof(bRelRac) ); - /*=====================*/ - /*==== /f ======*/ - /*=====================*/ - if ( i1 && !i1->bDeleted && i1->szHillFormula && i1->szHillFormula[0] ) { - sDifSegs[DIFL_M][DIFS_f_FORMULA] |= DIFV_NEQ2PRECED; - if ( i2 && !i2->bDeleted && i2->szHillFormula && i2->szHillFormula[0] ) { - if ( !CompareHillFormulasNoH( i1->szHillFormula, i2->szHillFormula, &num_H1, &num_H2 ) && - num_H1 == num_H2 ) { - sDifSegs[DIFL_F][DIFS_f_FORMULA] |= DIFV_EQL2PRECED; - } else { - sDifSegs[DIFL_F][DIFS_f_FORMULA] |= DIFV_NEQ2PRECED; - } - } else { - sDifSegs[DIFL_F][DIFS_f_FORMULA] |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; - } - } else { - sDifSegs[DIFL_M][DIFS_f_FORMULA] |= DIFV_BOTH_EMPTY; - if ( i2 && !i2->bDeleted && i2->szHillFormula && i2->szHillFormula[0] ) { - sDifSegs[DIFL_F][DIFS_f_FORMULA] |= DIFV_NEQ2PRECED; - } else { - sDifSegs[DIFL_F][DIFS_f_FORMULA] |= DIFV_BOTH_EMPTY; - } - } - /*=====================*/ - /*==== /c ======*/ - /*=====================*/ - if ( i1 && !i1->bDeleted && i1->lenConnTable > 1 ) { - sDifSegs[DIFL_M][DIFS_f_FORMULA] |= DIFV_NEQ2PRECED; - } else { - sDifSegs[DIFL_M][DIFS_f_FORMULA] |= DIFV_BOTH_EMPTY; - } - /*=====================*/ - /*==== /h ======*/ - /*=====================*/ - /* M: H atoms */ - if ( i1 && !i1->bDeleted ) { - num_H1 = (i1->lenTautomer > 0 && i1->nTautomer && i1->nTautomer[0])? 1 : 0; /* number of t-groups */ - if ( !num_H1 && i1->nNum_H ) { - for ( i = 0; i < i1->nNumberOfAtoms; i ++ ) { /* immobile H */ - if ( i1->nNum_H[i] ) { - num_H1 = 1; - break; - } - } - } - sDifSegs[DIFL_M][DIFS_h_H_ATOMS] |= num_H1? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; - } else { - sDifSegs[DIFL_M][DIFS_h_H_ATOMS] |= DIFV_BOTH_EMPTY; - } - /* F: fixed mobile H */ - if ( i2 && !i2->bDeleted && i2->nNum_H_fixed ) { - num_H2 = 0; - if ( i1 && !i1->bDeleted ) { - for ( i = 0; i < i1->nNumberOfAtoms; i ++ ) { - if ( i2->nNum_H_fixed[i] ) { - num_H2 = 1; - break; - } - } - } - sDifSegs[DIFL_F][DIFS_h_H_ATOMS] |= num_H2? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; - } else { - sDifSegs[DIFL_F][DIFS_h_H_ATOMS] |= DIFV_BOTH_EMPTY; - } - /* MI: exchangable isotopic H: see OutputINChI1(), num_iso_H[] */ - - /*=====================*/ - /*==== /q ======*/ - /*=====================*/ - psDifSegs = &sDifSegs[DIFL_F][DIFS_q_CHARGE]; - if ( i1 && !i1->bDeleted ) { - if ( i1->nTotalCharge ) { - sDifSegs[DIFL_M][DIFS_q_CHARGE] |= DIFV_NEQ2PRECED; - } else { - sDifSegs[DIFL_M][DIFS_q_CHARGE] |= DIFV_BOTH_EMPTY; - } - if ( i2 && !i2->bDeleted ) { - if ( i1->nTotalCharge ) { - if ( i1->nTotalCharge == i2->nTotalCharge ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else - if ( i2->nTotalCharge ) { - *psDifSegs |= DIFV_NEQ2PRECED; - } else { - *psDifSegs |= DIFV_IS_EMPTY; - } - } else { - if ( i2->nTotalCharge ) { - *psDifSegs |= DIFV_NEQ2PRECED; - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - } - } else - if ( !i2 ) { - if (bFixTranspChargeBug==1) - { - /* bug explanation: - - component #1 is tautomeric, component #2 is not - Mobile-H(#2) > Mobile-H(#1) - Fixed-H(#2) = Mobile-H(#2) < Fixed-H(#1) - - Layer first_charge second_charge - - Mobile-H 0 (comp#1) -1 (comp#2) - Fixed-H none (comp#2) -1 (comp#1) - - v1.01 charge compared decided that charge layers are same and omitted Fixed-H /q layer - - Solution: when component permutation is detected AND fixed-H component does not exist, - compare Mobile-H charge [0 (comp#1) in the example] to the charge of Mobile-H [-1 (comp#2)] - of the component that has none Fixed-H charge - */ - - /* Fixed-H i2 is empty because Fixed-H struct is same as Mobile-H */ - if ( p1->ord_number != p2->ord_number && /* component order in Fixed-H is different from Mobile-H */ - n1 == TAUT_YES && p2->pINChI[TAUT_YES] && !p2->pINChI[TAUT_YES]->bDeleted && - p2->pINChI[TAUT_YES]->nNumberOfAtoms ) { - int i2_nTotalCharge = p2->pINChI[TAUT_YES]->nTotalCharge; - - if ( i1->nTotalCharge ) { - if ( i1->nTotalCharge == i2_nTotalCharge ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else - if ( i2_nTotalCharge ) { - *psDifSegs |= DIFV_NEQ2PRECED; - } else { - *psDifSegs |= DIFV_IS_EMPTY; - } - } else { - if ( i2_nTotalCharge ) { - *psDifSegs |= DIFV_NEQ2PRECED; - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - } - } else { - *psDifSegs |= i1->nTotalCharge? DIFV_EQL2PRECED : DIFV_BOTH_EMPTY; - } - } - else /* if (bFixTranspChargeBug==1) */ - { - *psDifSegs |= i1->nTotalCharge? DIFV_EQL2PRECED : DIFV_BOTH_EMPTY; - } - } - - else /* if ( !i2 ) { */ - { - /* i2 && i2->bDeleted */ - *psDifSegs |= i1->nTotalCharge? DIFV_IS_EMPTY : DIFV_BOTH_EMPTY; - } - - } else { - sDifSegs[DIFL_M][DIFS_q_CHARGE] |= DIFV_BOTH_EMPTY; - if ( i2 && !i2->bDeleted ) { - if ( i2->nTotalCharge ) { - sDifSegs[DIFL_F][DIFS_q_CHARGE] |= DIFV_NEQ2PRECED; - } else { - sDifSegs[DIFL_F][DIFS_q_CHARGE] |= DIFV_BOTH_EMPTY; - } - } - } - /*************** stereo *****************/ - if ( i1 && !i1->bDeleted ) { - Stereo1 = i1->Stereo; - IsoStereo1 = i1->StereoIsotopic; - } else { - Stereo1 = NULL; - IsoStereo1 = NULL; - } - if ( i2 && !i2->bDeleted ) { - Stereo2 = i2->Stereo; - IsoStereo2 = i2->StereoIsotopic; - } else { - Stereo2 = NULL; - IsoStereo2 = NULL; - } - /*=====================*/ - /*==== /b ======*/ - /*=====================*/ - /* M double bond stereo */ - psDifSegs = &sDifSegs[DIFL_M][DIFS_b_SBONDS]; - if ( Stereo1 && Stereo1->nNumberOfStereoBonds ) { - *psDifSegs |= DIFV_NEQ2PRECED; - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - /* F double bond stereo */ - psDifSegs = &sDifSegs[DIFL_F][DIFS_b_SBONDS]; - if ( Stereo2 && Stereo2->nNumberOfStereoBonds ) { - if ( Stereo1 && Stereo1->nNumberOfStereoBonds ) { - if ( Eql_INChI_Stereo( Stereo1, EQL_SP2, Stereo2, EQL_SP2, 0 ) ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_NEQ2PRECED; - } - } else { - *psDifSegs |= DIFV_NEQ2PRECED; - } - } else { - if ( Stereo1 && Stereo1->nNumberOfStereoBonds ) { - *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - } - /* MI double bond stereo */ - psDifSegs = &sDifSegs[DIFL_MI][DIFS_b_SBONDS]; - if ( IsoStereo1 && IsoStereo1->nNumberOfStereoBonds ) { - if ( Eql_INChI_Stereo( IsoStereo1, EQL_SP2, Stereo1, EQL_SP2, 0 ) ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_NEQ2PRECED; - } - } else { - if ( Stereo1 && Stereo1->nNumberOfStereoBonds ) { - *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - } - /* FI double bond stereo */ - psDifSegs = &sDifSegs[DIFL_FI][DIFS_b_SBONDS]; - if ( IsoStereo2 && IsoStereo2->nNumberOfStereoBonds ) { - if ( Eql_INChI_Stereo( IsoStereo2, EQL_SP2, Stereo2, EQL_SP2, 0 ) ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else { - if ( !(Stereo1 && Stereo1->nNumberOfStereoBonds) && - !(Stereo2 && Stereo2->nNumberOfStereoBonds) && - Eql_INChI_Stereo( IsoStereo2, EQL_SP2, IsoStereo1, EQL_SP2, 0 ) ) { - *psDifSegs |= DIFV_FI_EQ_MI; - } else { - *psDifSegs |= DIFV_NEQ2PRECED; - } - } - } else { - /* the solution table for FI stereo, - in case of FI stereo is empty - E = segment is empty, NE = not empty - +==============================+ - | M | MI | F | result | - +=====+=====+=====+============+ - | E | E | E | both empty | - +-----+-----+-----+------------+ - | NE | E | E | both empty | - +-----+-----+-----+------------+ - | E | NE | E | is empty | - +-----+-----+-----+------------+ - | NE | NE | E | both empty | - +-----+-----+-----+------------+ - | E | E | NE | is empty | - +-----+-----+-----+------------+ - | NE | E | NE | is empty | - +-----+-----+-----+------------+ - | E | NE | NE | is empty | - +-----+-----+-----+------------+ - | NE | NE | ME | is empty | - +==============================+ - */ - if ( Stereo2 && Stereo2->nNumberOfStereoBonds ) { - *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ - } else - if ( IsoStereo1 && IsoStereo1->nNumberOfStereoBonds && - !(Stereo1 && Stereo1->nNumberOfStereoBonds) - ) { - *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - } - /*==================================*/ - /*==== /t, /m, /s for M ======*/ - /*==================================*/ - /* M sp3 stereo */ - - - bRelRac[DIFL_M ] = GetSp3RelRacAbs( i1, Stereo1 ); /* Mobile-H */ - bRelRac[DIFL_MI] = GetSp3RelRacAbs( i1, IsoStereo1 ); - bRelRac[DIFL_F ] = GetSp3RelRacAbs( i2, Stereo2 ); /* Fixed-H */ - bRelRac[DIFL_FI] = GetSp3RelRacAbs( i2, IsoStereo2 ); - if ( SP3_NONE != bRelRac[DIFL_M] ) { - sDifSegs[DIFL_M][DIFS_t_SATOMS] |= (bRelRac[DIFL_M] & SP3_ANY)? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; - sDifSegs[DIFL_M][DIFS_m_SP3INV] |= (bRelRac[DIFL_M] & SP3_ABS)? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; - sDifSegs[DIFL_M][DIFS_s_STYPE] |= (bRelRac[DIFL_M] & SP3_TYPE)? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; - } else { - sDifSegs[DIFL_M][DIFS_t_SATOMS] |= DIFV_BOTH_EMPTY; - sDifSegs[DIFL_M][DIFS_m_SP3INV] |= DIFV_BOTH_EMPTY; - sDifSegs[DIFL_M][DIFS_s_STYPE] |= DIFV_BOTH_EMPTY; - } - /*=====================*/ - /*==== /t ======*/ - /*=====================*/ - /* F sp3 stereo */ - psDifSegs = &sDifSegs[DIFL_F][DIFS_t_SATOMS]; - if ( SP3_ANY & bRelRac[DIFL_F] ) { - if ( Eql_INChI_Stereo( Stereo2, EQL_SP3, Stereo1, EQL_SP3, 0 ) ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_NEQ2PRECED; - } - } else - if ( SP3_ANY & bRelRac[DIFL_M] ) { - *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - /* MI sp3 stereo */ - psDifSegs = &sDifSegs[DIFL_MI][DIFS_t_SATOMS]; - if ( SP3_ANY & bRelRac[DIFL_MI] ) { - if ( Eql_INChI_Stereo( IsoStereo1, EQL_SP3, Stereo1, EQL_SP3, 0 ) ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_NEQ2PRECED; - } - } else - if ( SP3_ANY & bRelRac[DIFL_M] ) { - *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - /* FI sp3 stereo */ - psDifSegs = &sDifSegs[DIFL_FI][DIFS_t_SATOMS]; - if ( SP3_ANY & bRelRac[DIFL_FI] ) { - if ( Eql_INChI_Stereo( IsoStereo2, EQL_SP3, Stereo2, EQL_SP3, 0 ) ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else - if ( !(SP3_ANY & bRelRac[DIFL_M]) && - !(SP3_ANY & bRelRac[DIFL_F]) && - Eql_INChI_Stereo( IsoStereo2, EQL_SP3, IsoStereo1, EQL_SP3, 0 ) ) { - *psDifSegs |= DIFV_FI_EQ_MI; - } else { - *psDifSegs |= DIFV_NEQ2PRECED; - } - } else /* similar to /b */ - if ( (SP3_ANY & bRelRac[DIFL_F]) ) { - *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ - } else - if ( (SP3_ANY & bRelRac[DIFL_MI]) && !(SP3_ANY & bRelRac[DIFL_M]) ) { - *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - /*=====================*/ - /*==== /m ======*/ - /*=====================*/ - /* F sp3 abs stereo inversion */ - psDifSegs = &sDifSegs[DIFL_F][DIFS_m_SP3INV]; - if ( bRelRac[DIFL_F] & SP3_ABS ) { - /* the order of || operands below is critically important: || is not a commutative operation */ - if ( !(bRelRac[DIFL_M] & SP3_ABS) || Stereo2->nCompInv2Abs != Stereo1->nCompInv2Abs ) { - *psDifSegs |= DIFV_NEQ2PRECED; - } else { - *psDifSegs |= DIFV_EQL2PRECED; - } - } else - if ( bRelRac[DIFL_M] & SP3_ABS ) { - *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - /* MI sp3 abs stereo inversion */ - psDifSegs = &sDifSegs[DIFL_MI][DIFS_m_SP3INV]; - if ( SP3_ABS & bRelRac[DIFL_MI] ) { - if ( (SP3_ABS & bRelRac[DIFL_M]) && IsoStereo1->nCompInv2Abs == Stereo1->nCompInv2Abs ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_NEQ2PRECED; - } - } else - if ( SP3_ABS & bRelRac[DIFL_M] ) { - *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - /* FI sp3 abs stereo inversion */ - psDifSegs = &sDifSegs[DIFL_FI][DIFS_m_SP3INV]; - if ( SP3_ABS & bRelRac[DIFL_FI] ) { - if ( (SP3_ABS & bRelRac[DIFL_F]) && IsoStereo2->nCompInv2Abs == Stereo2->nCompInv2Abs ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else - if ( !(SP3_ABS & bRelRac[DIFL_M]) && - !(SP3_ABS & bRelRac[DIFL_F]) && - (SP3_ABS & bRelRac[DIFL_MI]) && /* make sure IsoStereo1 != NULL */ - IsoStereo2->nCompInv2Abs == IsoStereo1->nCompInv2Abs ) { - *psDifSegs |= DIFV_FI_EQ_MI; - } else { - *psDifSegs |= DIFV_NEQ2PRECED; - } - } else /* similar to /b */ - /* the order of || operands below is critically important: || is no a commutative operation */ - if ( (SP3_ABS & bRelRac[DIFL_F]) ) { - *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ - } else - if ( (SP3_ABS & bRelRac[DIFL_MI]) && !(SP3_ABS & bRelRac[DIFL_M]) ) { - *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - /*=====================*/ - /*==== /s ======*/ - /*=====================*/ - /* F sp3 stereo type */ - psDifSegs = &sDifSegs[DIFL_F][DIFS_s_STYPE]; - if ( bRelRac[DIFL_F] & SP3_TYPE ) { - if ( (bRelRac[DIFL_F] & SP3_TYPE) == (bRelRac[DIFL_M] & SP3_TYPE) ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_NEQ2PRECED; - } - } else - if ( bRelRac[DIFL_M] & SP3_TYPE ) { - *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - /* MI sp3 stereo type */ - psDifSegs = &sDifSegs[DIFL_MI][DIFS_s_STYPE]; - if ( SP3_TYPE & bRelRac[DIFL_MI] ) { - if ( (SP3_TYPE & bRelRac[DIFL_MI]) == (SP3_TYPE & bRelRac[DIFL_M]) ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_NEQ2PRECED; - } - } else - if ( SP3_TYPE & bRelRac[DIFL_M] ) { - *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - /* FI sp3 stereo type */ - psDifSegs = &sDifSegs[DIFL_FI][DIFS_s_STYPE]; - if ( SP3_TYPE & bRelRac[DIFL_FI] ) { - if ( (SP3_TYPE & bRelRac[DIFL_FI]) == (SP3_TYPE & bRelRac[DIFL_F]) ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else - if ( !(SP3_TYPE & bRelRac[DIFL_M]) && - !(SP3_TYPE & bRelRac[DIFL_F]) && - (SP3_TYPE & bRelRac[DIFL_MI]) ) { - *psDifSegs |= DIFV_FI_EQ_MI; - } else { - *psDifSegs |= DIFV_NEQ2PRECED; - } - } else /* similar to /b */ - /* the order of || operands below is critically important: || is not a commutative operation */ - if ( (SP3_TYPE & bRelRac[DIFL_F]) ) { - *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ - } else - if ( (SP3_TYPE & bRelRac[DIFL_MI]) && !(SP3_TYPE & bRelRac[DIFL_M]) ) { - *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - /*=====================*/ - /*==== /o ======*/ - /*=====================*/ - if ( p1 && p2 && p1->ord_number != p2->ord_number ) { - sDifSegs[DIFL_F][DIFS_o_TRANSP] |= DIFV_NEQ2PRECED; - } - /*=====================*/ - /*==== /i ======*/ - /*=====================*/ - /* M isotopic atoms */ - psDifSegs = &sDifSegs[DIFL_MI][DIFS_i_IATOMS]; - if ( i1 && !i1->bDeleted && (i1->nNumberOfIsotopicAtoms || i1->nNumberOfIsotopicTGroups) ) { - *psDifSegs |= DIFV_NEQ2PRECED; - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - /* F isotopic atoms */ - psDifSegs = &sDifSegs[DIFL_FI][DIFS_i_IATOMS]; - if ( i2 && !i2->bDeleted ) { - if ( i2->nNumberOfIsotopicAtoms || i2->nNumberOfIsotopicTGroups ) { - if ( !i1 || i1->bDeleted || - i2->nNumberOfIsotopicAtoms != i1->nNumberOfIsotopicAtoms || - i2->nNumberOfIsotopicTGroups != i1->nNumberOfIsotopicTGroups ) { - *psDifSegs |= DIFV_NEQ2PRECED; - } else { - int diff; - num = i1->nNumberOfIsotopicAtoms; - diff = 0; - for ( i = 0; i < num; i ++ ) { - /* compare isotopic atoms */ - if ( diff = (int)i2->IsotopicAtom[i].nAtomNumber - (int)i1->IsotopicAtom[i].nAtomNumber ) - break; - if ( diff = (int)i2->IsotopicAtom[i].nIsoDifference - (int)i1->IsotopicAtom[i].nIsoDifference ) - break; - /* compare isotopic H */ - if ( diff = (int)i2->IsotopicAtom[i].nNum_T - (int)i1->IsotopicAtom[i].nNum_T ) - break; - if ( diff = (int)i2->IsotopicAtom[i].nNum_D - (int)i1->IsotopicAtom[i].nNum_D ) - break; - if ( diff = (int)i2->IsotopicAtom[i].nNum_H - (int)i1->IsotopicAtom[i].nNum_H ) - break; - } - if ( !diff ) { - num = i1->nNumberOfIsotopicTGroups; - for ( i = 0; i < num; i ++ ) { - if ( diff = (int)i2->IsotopicTGroup[i].nTGroupNumber - (int)i1->IsotopicTGroup[i].nTGroupNumber ) - break; - if ( diff = (int)i2->IsotopicTGroup[i].nNum_T - (int)i1->IsotopicTGroup[i].nNum_T ) - break; - if ( diff = (int)i2->IsotopicTGroup[i].nNum_D - (int)i1->IsotopicTGroup[i].nNum_D ) - return diff; - if ( diff = (int)i2->IsotopicTGroup[i].nNum_H - (int)i1->IsotopicTGroup[i].nNum_H ) - break; - } - } - *psDifSegs |= diff? DIFV_NEQ2PRECED : DIFV_FI_EQ_MI; - - } - } else - if ( i1 && !i1->bDeleted && (i1->nNumberOfIsotopicAtoms || i1->nNumberOfIsotopicTGroups) ) { - *psDifSegs |= DIFV_IS_EMPTY; - } - } else - if ( !i2 ) { - if ( i1 && !i1->bDeleted && (i1->nNumberOfIsotopicAtoms || i1->nNumberOfIsotopicTGroups) ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - } - - return ret; -} -/**********************************************************************************************/ -int INChI_SegmentAction( char cDifSegs ) -{ - if ( !(cDifSegs & DIFV_OUTPUT_OMIT_F) ) { - return INCHI_SEGM_OMIT; - } - if ( (cDifSegs & DIFV_OUTPUT_EMPTY_T) && !(cDifSegs & DIFV_OUTPUT_EMPTY_F) ) { - return INCHI_SEGM_EMPTY; - } - if ( (cDifSegs & DIFV_OUTPUT_FILL_T) ) { - return INCHI_SEGM_FILL; - } - return INCHI_SEGM_OMIT; /* the control flow shoul never reach this point */ -} -/**********************************************************************************************/ -int MarkUnusedAndEmptyLayers( char sDifSegs[][DIFS_LENGTH] ) -{ - /**************************************************** - 1. If all elements of a layer are DIFV_IS_EMPTY and/or DIFV_BOTH_EMPTY - and/or DIFV_EQL2PRECED and/or DIFV_FI_EQ_MI - and there is NO succeeding non-empty layer then mark the 1st element - of the layer DIFV_BOTH_EMPTY; this layerr will be omitted. - - 2. If all elements of a layer are DIFV_IS_EMPTY and/or DIFV_BOTH_EMPTY - and/or DIFV_EQL2PRECED and/or DIFV_FI_EQ_MI - and there IS a succeeding non-empty layer then mark the 1st element - of the layer DIFV_IS_EMPTY and all other elements DIFV_BOTH_EMPTY; - only the first empty segment of this layerr will be output. - - 3. If NOT all elements of a layer are DIFV_IS_EMPTY and/or DIFV_BOTH_EMPTY - and/or DIFV_EQL2PRECED and/or DIFV_FI_EQ_MI - and the 1st element of the layer is DIFV_BOTH_EMPTY then mark it - DIFV_IS_EMPTY; it will be output as empty (except M layer). - */ - - int i, nLayer, sBits, nFirstSegm; -#define nFirstFmlSegm DIFS_f_FORMULA -#define nFirstIsoSegm DIFS_i_IATOMS - /* FI */ - nLayer = DIFL_FI; - nFirstSegm = nFirstIsoSegm; - sBits = 0; - for ( i = 0; i < DIFS_idf_LENGTH; i ++ ) { - sBits |= sDifSegs[nLayer][i]; - } - if ( !(sBits & DIFV_OUTPUT_OMIT_F) ) { - /* Omit the FI layer */ - memset( sDifSegs[nLayer], DIFV_BOTH_EMPTY, DIFS_idf_LENGTH); - } else - if ( sDifSegs[nLayer][nFirstSegm] == DIFV_BOTH_EMPTY || - !(sDifSegs[nLayer][nFirstSegm] & DIFV_OUTPUT_OMIT_F) ) { - sDifSegs[nLayer][nFirstSegm] = DIFV_IS_EMPTY; - } - - /* MI */ - nLayer = DIFL_MI; - nFirstSegm = nFirstIsoSegm; - sBits = 0; - for ( i = 0; i < DIFS_idf_LENGTH; i ++ ) { - sBits |= sDifSegs[nLayer][i]; - } - if ( !(sBits & DIFV_OUTPUT_OMIT_F) ) { - /* Omit the MI layer */ - memset( sDifSegs[nLayer], DIFV_BOTH_EMPTY, DIFS_idf_LENGTH); - } else - if ( sDifSegs[nLayer][nFirstSegm] == DIFV_BOTH_EMPTY || - !(sDifSegs[nLayer][nFirstSegm] & DIFV_OUTPUT_OMIT_F) ) { - sDifSegs[nLayer][nFirstSegm] = DIFV_IS_EMPTY; - } - - /* F */ - nLayer = DIFL_F; - nFirstSegm = nFirstFmlSegm; - sBits = 0; - for ( i = 0; i < DIFS_idf_LENGTH; i ++ ) { - sBits |= sDifSegs[nLayer][i]; - } - if ( !(sBits & DIFV_OUTPUT_OMIT_F) && - sDifSegs[DIFL_FI][nFirstIsoSegm] == DIFV_BOTH_EMPTY ) { - /* Omit the F layer: no non-iotopic and no isotopic segments */ - memset( sDifSegs[nLayer], DIFV_BOTH_EMPTY, DIFS_idf_LENGTH); - } else - /* do not omit fixed-H layer */ - if ( sDifSegs[nLayer][nFirstSegm] == DIFV_BOTH_EMPTY || - !(sDifSegs[nLayer][nFirstSegm] & DIFV_OUTPUT_OMIT_F) ) { - sDifSegs[nLayer][nFirstSegm] = DIFV_IS_EMPTY; - } - - /* M -- leave as it is */ - return 0; -#undef nFirstFmlSegm -#undef nFirstIsoSegm -} -/*********************************************************************************************/ -int CompareInchiStereo( INChI_Stereo *Stereo1, INCHI_MODE nFlags1, INChI_Stereo *Stereo2, INCHI_MODE nFlags2 ) -{ - int i, num, ret; - if ( Stereo2 && Stereo1 ) { - /* compare stereogenic bonds */ - num = inchi_min( Stereo2->nNumberOfStereoBonds, Stereo1->nNumberOfStereoBonds ); - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)Stereo2->nBondAtom1[i] - (int)Stereo1->nBondAtom1[i] ) - return ret; - if ( ret = (int)Stereo2->nBondAtom2[i] - (int)Stereo1->nBondAtom2[i] ) - return ret; - if ( ret = (int)Stereo2->b_parity[i] - (int)Stereo1->b_parity[i] ) - return ret; - } - if ( ret = (int)Stereo2->nNumberOfStereoBonds - (int)Stereo1->nNumberOfStereoBonds ) - return ret; - /* compare stereogenic atoms */ -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - if ( ((nFlags1 | nFlags2) & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO) ) && - 1 == Stereo2->nNumberOfStereoCenters && - 1 == Stereo1->nNumberOfStereoCenters ) { - ; /* do not compare single stereocenters in case of relative stereo */ - } else -#endif - { - num = inchi_min( Stereo2->nNumberOfStereoCenters, Stereo1->nNumberOfStereoCenters ); - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)Stereo2->nNumber[i] - (int)Stereo1->nNumber[i] ) - return ret; - if ( ret = (int)Stereo2->t_parity[i] - (int)Stereo1->t_parity[i] ) - return ret; - } - if ( ret = (int)Stereo2->nNumberOfStereoCenters - (int)Stereo1->nNumberOfStereoCenters ) - return ret; - /* compare stereo-abs-is-inverted flags for non-relative, non-racemic */ - if ( !((nFlags1 | nFlags2) & (INCHI_FLAG_RAC_STEREO | INCHI_FLAG_REL_STEREO)) ) { - if ( ret = (Stereo2->nCompInv2Abs < 0) - (Stereo1->nCompInv2Abs < 0) ) { - return ret; - } - } - } - } else - if ( Stereo2 && ( Stereo2->nNumberOfStereoBonds > 0 || - Stereo2->nNumberOfStereoCenters > 0 -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - && /* do not compare single stereocenters in case of relative stereo */ - !((nFlags2 & (INCHI_FLAG_REL_STEREO|INCHI_FLAG_RAC_STEREO)) && - 1 == Stereo2->nNumberOfStereoCenters - ) -#endif - ) ) { - return 1; - }else - if ( Stereo1 && ( Stereo1->nNumberOfStereoBonds > 0 || - Stereo1->nNumberOfStereoCenters > 0 -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - && /* do not compare single stereocenters in case of relative stereo */ - !((nFlags1 & (INCHI_FLAG_REL_STEREO|INCHI_FLAG_RAC_STEREO)) && - 1 == Stereo1->nNumberOfStereoCenters - ) -#endif - ) ) { - return -1; - } - return 0; -} -/**********************************************************************************************/ -/* sorting in descending order: return -1 if *p1 > *p2, return +1 if *p1 < *p2 */ -/**********************************************************************************************/ -int CompINChI2(const INCHI_SORT *p1, const INCHI_SORT *p2, int bTaut, int bCompareIsotopic) -{ - int ret, num, i, num_H1, num_H2; - - const INChI *i1 = NULL; /* tautomeric if exists, otherwise non-tautomeric */ - const INChI *i2 = NULL; /* tautomeric if exists, otherwise non-tautomeric */ - - int n1; /* TAUT_YES if tautomeric i1 exists, otherwise TAUT_NON */ - int n2; /* TAUT_YES if tautomeric i2 exists, otherwise TAUT_NON */ - - const INChI *i1n = NULL; /* non-tautomeric if both tautomeric AND non-tautomeric exist */ - const INChI *i2n = NULL; /* non-tautomeric if both tautomeric AND non-tautomeric exist */ - - /*const INChI *i1t = NULL;*/ /* temp for i1 if both tautomeric AND non-tautomeric exist */ - /*const INChI *i2t = NULL;*/ /* temp for i2 if both tautomeric AND non-tautomeric exist */ - - - /* INChI_Stereo *Stereo1, *Stereo2; */ - - n1 = ( p1->pINChI[TAUT_YES] && p1->pINChI[TAUT_YES]->nNumberOfAtoms )? TAUT_YES : TAUT_NON; - n2 = ( p2->pINChI[TAUT_YES] && p2->pINChI[TAUT_YES]->nNumberOfAtoms )? TAUT_YES : TAUT_NON; - - i1 = p1->pINChI[n1]; - i1n = (n1 == TAUT_YES && p1->pINChI[TAUT_NON] && - p1->pINChI[TAUT_NON]->nNumberOfAtoms)? p1->pINChI[TAUT_NON] : (const INChI *)NULL; - - i2 = p2->pINChI[n2]; - i2n = (n2 == TAUT_YES && p2->pINChI[TAUT_NON] && - p2->pINChI[TAUT_NON]->nNumberOfAtoms)? p2->pINChI[TAUT_NON] : (const INChI *)NULL; - - /* non-deleted-non-empty < deleted < empty */ - if ( i1 && !i2 ) - return -1; /* non-empty is the smallest (first) */ - if ( !i1 && i2 ) - return 1; - if ( !i1 && !i2 ) - return 0; - if ( i1->bDeleted && !i2->bDeleted ) - return 1; /* deleted is the largest (last) among non-empty */ - if ( !i1->bDeleted && i2->bDeleted ) - return -1; - - num_H1 = num_H2 = 0; - - /* do not compare terminal H */ - if ( ret = CompareHillFormulasNoH( i1->szHillFormula, i2->szHillFormula, &num_H1, &num_H2 ) ) { - return ret; /* lexicographic order except the shorter one is greater (last): CH2O < CH2; C3XX < C2XX */ - } - - /********************************************************* - compare non-isotopic non-tautomeric part - *********************************************************/ - - /* compare number of atoms (excluding terminal H) */ - if ( ret = i2->nNumberOfAtoms - i1->nNumberOfAtoms ) - return ret; /* more atoms first */ - - /* compare elements (excluding terminal H) */ - num = i1->nNumberOfAtoms; - for ( i = 0; i < num; i ++ ) { /* should always be equal if Hill formulas are same */ - if ( ret = (int)i2->nAtom[i] - (int)i1->nAtom[i] ) - return ret; /* greater periodic number first */ - } - /********************************************************** - compare connection tables - ***********************************************************/ - if ( ret = i2->lenConnTable - i1->lenConnTable ) - return ret; /* longer connection table first */ - num = i2->lenConnTable; - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)i2->nConnTable[i] - (int)i1->nConnTable[i] ) - return ret; /* greater connection table first */ - } - /********************************************************* - compare compare total number of H (inverse: H3 < H2 ) - **********************************************************/ - if ( ret = num_H2 - num_H1 ) - return ret; - /********************************************************* - compare non-tautomeric num_H: N < NH3 < NH2 < NH - **********************************************************/ - num = i1->nNumberOfAtoms; - for ( i = 0; i < num; i ++ ) { - if ( i2->nNum_H[i] != i1->nNum_H[i] ) { - return !i2->nNum_H[i]? 1 : /* no H first */ - !i1->nNum_H[i]? -1 : - (int)i2->nNum_H[i] - (int)i1->nNum_H[i]; - } - } - /********************************************************* - compare non-isotopic tautomeric part - *********************************************************/ - if ( ret = CompareTautNonIsoPartOfINChI( i1, i2) ) { - return ret; - } - /* - if ( ret = i2->lenTautomer - i1->lenTautomer ) - return ret; - num = inchi_min( i2->lenTautomer, i1->lenTautomer ); - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)i2->nTautomer[i] - (int)i1->nTautomer[i] ) - return ret; - } - */ - /********************************************************* - * * - * at this point both components are either tautomeric * - * or non-tautomeric * - * * - *********************************************************/ - - /********************************************************* - non-tautomeric "fixed H" specific - *********************************************************/ - if ( TAUT_NON == bTaut && (i1n && i1n->nNum_H_fixed || i2n && i2n->nNum_H_fixed) ) { - /* first, compare non-tautomeric chem. formulas -- they may be different */ - const char *f1 = (i1n /*&& i1n->nNum_H_fixed*/)? i1n->szHillFormula : i1->szHillFormula; - const char *f2 = (i2n /*&& i2n->nNum_H_fixed*/)? i2n->szHillFormula : i2->szHillFormula; - if ( f1 && f2 &&(ret = CompareHillFormulas( f1, f2 ))) { - return ret; - } - /* secondly, compare fixed-H distribution */ - if ( i1n && i1n->nNum_H_fixed && i2n && i2n->nNum_H_fixed ) { - num = inchi_min( i1n->nNumberOfAtoms, i2n->nNumberOfAtoms); - for ( i = 0; i < num; i ++ ) { - if ( i2n->nNum_H_fixed[i] != i1n->nNum_H_fixed[i] ) { - return !i2n->nNum_H_fixed[i]? 1 : /* no fixed H first */ - !i1n->nNum_H_fixed[i]? -1 : - (int)i2n->nNum_H_fixed[i] - (int)i1n->nNum_H_fixed[i]; - } - } - if ( ret = (int)i2n->nNumberOfAtoms - (int)i1n->nNumberOfAtoms ) { - return ret; /* should not happen */ - } - } else - if ( i1n && i1n->nNum_H_fixed ) { - num = i1n->nNumberOfAtoms; - for ( i = 0; i < num; i ++ ) { /* added 2004-05-04 */ - if ( i1n->nNum_H_fixed[i] ) { - return -1; /* i1n->nNum_H_fixed[i] > 0? -1:1;*/ - } - } - /* p1 is tautomeric, p2 is not tautomeric; this must have been detected earlier */ - /*return -1;*/ /* has fixed H first *//* */ /* removed 2004-05-04 */ - } else { - num = i2n->nNumberOfAtoms; - for ( i = 0; i < num; i ++ ) { /* added 2004-05-04 */ - if ( i2n->nNum_H_fixed[i] ) { - return 1; /* i2n->nNum_H_fixed[i] > 0? 1:-1;*/ - } - } - /* p2 is tautomeric, p1 is not tautomeric; this must have been detected earlier */ - /*return 1; */ /* has fixed H first *//* */ /* removed 2004-05-04 */ - } - } - - /************************************************************************* - if requested non-tautomeric comparison then - prepare to compare non-taut non-isotopic stereo, etc. - *************************************************************************/ - if ( TAUT_NON == bTaut ) { - if ( i1n ) { - /*i1t = i1;*/ - i1 = i1n; - } - if ( i2n ) { - /*i2t = i2;*/ - i2 = i2n; - } - } - - /********************************************************* - compare non-isotopic stereo - *********************************************************/ - ret = CompareInchiStereo( i1->Stereo, i1->nFlags, i2->Stereo, i2->nFlags ); - if ( ret ) { - return ret; - } - /******************************************************* - do not switch back to tautomeric i1, i2 - *******************************************************/ - /* -- how to switch back -- - if ( i1t ) { - i1 = i1t; - i1t = NULL; - } - if ( i2t ) { - i2 = i2t; - i2t = NULL; - } - */ - /****************************************************** - compare isotopic non-tautomeric part - ******************************************************/ - if ( bCompareIsotopic ) { - if ( ret = i2->nNumberOfIsotopicAtoms - i1->nNumberOfIsotopicAtoms ) - return ret; - num = i1->nNumberOfIsotopicAtoms; - /* compare isotopic atoms */ - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)i2->IsotopicAtom[i].nAtomNumber - (int)i1->IsotopicAtom[i].nAtomNumber ) - return ret; - if ( ret = (int)i2->IsotopicAtom[i].nIsoDifference - (int)i1->IsotopicAtom[i].nIsoDifference ) - return ret; - } - /* compare isotopic H */ - /* if tautomeric comparison mode then here are compared only non-tautomeric H */ - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)i2->IsotopicAtom[i].nNum_T - (int)i1->IsotopicAtom[i].nNum_T ) - return ret; - if ( ret = (int)i2->IsotopicAtom[i].nNum_D - (int)i1->IsotopicAtom[i].nNum_D ) - return ret; - if ( ret = (int)i2->IsotopicAtom[i].nNum_H - (int)i1->IsotopicAtom[i].nNum_H ) - return ret; - } - /***************************************************** - compare isotopic tautomeric part - *****************************************************/ - if ( ret = i2->nNumberOfIsotopicTGroups - i1->nNumberOfIsotopicTGroups ) - return ret; - num = i1->nNumberOfIsotopicTGroups; - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)i2->IsotopicTGroup[i].nTGroupNumber - (int)i1->IsotopicTGroup[i].nTGroupNumber ) - return ret; - if ( ret = (int)i2->IsotopicTGroup[i].nNum_T - (int)i1->IsotopicTGroup[i].nNum_T ) - return ret; - if ( ret = (int)i2->IsotopicTGroup[i].nNum_D - (int)i1->IsotopicTGroup[i].nNum_D ) - return ret; - if ( ret = (int)i2->IsotopicTGroup[i].nNum_H - (int)i1->IsotopicTGroup[i].nNum_H ) - return ret; - } - - /**************************************************** - compare isotopic stereo - ****************************************************/ - ret = CompareInchiStereo( i1->StereoIsotopic, i1->nFlags, i2->StereoIsotopic, i2->nFlags ); - if ( ret ) { - return ret; - } - } - - /********************************************************** - compare charges: non-charged first, then in order of - ascending charges (negative first) - ***********************************************************/ - if ( i2->nTotalCharge && i1->nTotalCharge ) { - /* both are charged; smaller charges first */ - ret = (int)i1->nTotalCharge - (int)i2->nTotalCharge; - return ret; - } - if ( ret = (i1->nTotalCharge? 1:0) - (i2->nTotalCharge? 1:0) ) { - /* only one is charged; uncharged first */ - return ret; - } - /* stable sort */ - /*ret = p1->ord_number - p2->ord_number;*/ - - return ret; -} -/***********************************************************************/ -int CompINChINonTaut2(const void *p1, const void *p2) -{ - int ret; - ret = CompINChI2( (const INCHI_SORT *)p1, (const INCHI_SORT *)p2, TAUT_NON, 1 ); -#if ( CANON_FIXH_TRANS == 1 ) - if ( !ret ) { - /* to obtain canonical transposition 2004-05-10 */ - ret = CompINChI2( (const INCHI_SORT *)p1, (const INCHI_SORT *)p2, TAUT_YES, 1 ); - } -#endif - if ( !ret ) { - /* stable sort */ - ret = ((const INCHI_SORT *)p1)->ord_number - ((const INCHI_SORT *)p2)->ord_number; - } - return ret; -} -/***********************************************************************/ -int CompINChITaut2(const void *p1, const void *p2) -{ - int ret; - ret = CompINChI2( (const INCHI_SORT *)p1, (const INCHI_SORT *)p2, TAUT_YES, 1 ); -#if ( CANON_FIXH_TRANS == 1 ) - if ( !ret ) { - /* to obtain canonical transposition 2004-05-10 */ - ret = CompINChI2( (const INCHI_SORT *)p1, (const INCHI_SORT *)p2, TAUT_NON, 1 ); - } -#endif - if ( !ret ) { - /* stable sort */ - ret = ((const INCHI_SORT *)p1)->ord_number - ((const INCHI_SORT *)p2)->ord_number; - } - return ret; -} -/**********************************************************************************************/ -/* strrev from K&R is not in ANSI-compatible C library */ -void mystrrev( char *p ) -{ - char c, *q = p; - while( *q++ ) - ; - q -= 2; /* pointer to the last character */ - while ( p < q ) { - c = *q; /* swap */ - *q-- = *p; - *p++ = c; - } -} -/*****************************************************************************************/ -/* Find DFS order for CT(canon. numbers and Hs) output */ -/*****************************************************************************************/ - -static AT_NUMB *gDfs4CT_nDfsNumber; -static AT_NUMB *gDfs4CT_nNumDescendants; -static int gDfs4CT_nCurrentAtom; - -/**********************************************************************************************/ -static int CompareDfsDescendants4CT( const void *a1, const void *a2 ) -{ - int neigh1 = (int)*(const AT_RANK*)a1; - int neigh2 = (int)*(const AT_RANK*)a2; - if ( neigh1 > MAX_ATOMS ) { - if ( neigh2 > MAX_ATOMS ) { - return 0; - } - return 1; - } else - if ( neigh2 > MAX_ATOMS ) { - return -1; - } else { - AT_RANK nCurDfsNumber = gDfs4CT_nDfsNumber[gDfs4CT_nCurrentAtom]; - int nDesc1 = nCurDfsNumber > gDfs4CT_nDfsNumber[neigh1]? - 0 : (int)gDfs4CT_nNumDescendants[neigh1]; - int nDesc2 = nCurDfsNumber > gDfs4CT_nDfsNumber[neigh2]? - 0 : (int)gDfs4CT_nNumDescendants[neigh2]; - int ret; - if ( ret = nDesc1 - nDesc2 ) { - return ret; - } - return (int)neigh1 - (int)neigh2; /* canon. numbers difference */ - } -} -/**********************************************************************************************/ -/* sp_ATOM *at, AT_RANK *nRank, int num_atoms */ -AT_NUMB *GetDfsOrder4CT( AT_NUMB *LinearCT, int nLenCT, S_CHAR *nNum_H, int num_atoms, int nCtMode ) -{ - AT_NUMB *nStackAtom = NULL; - int nTopStackAtom=-1; - AT_NUMB *nNumDescendants = NULL; /* number of descendants incl. closures and the atom itself */ - AT_NUMB *nDfsNumber = NULL; - S_CHAR *cNeighNumb = NULL; - NEIGH_LIST *nl = NULL; - AT_NUMB nDfs; - int i, j, u, k, start, num_rings, nTotOutputStringLen; - AT_NUMB *nOutputString = NULL, cDelim; - int bCtPredecessors = (nCtMode & CT_MODE_PREDECESSORS); - - /* int nNumStartChildren; */ - - - /* allocate arrays */ - nStackAtom = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nStackAtom[0])); - nNumDescendants = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nNumDescendants[0])); - nDfsNumber = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nDfsNumber[0])); - cNeighNumb = (S_CHAR *) inchi_malloc(num_atoms*sizeof(cNeighNumb[0])); - nl = CreateNeighListFromLinearCT( LinearCT, nLenCT, num_atoms ); - /* check allocation */ - if ( !nStackAtom || !nNumDescendants || !nDfsNumber || !cNeighNumb || !nl ) { - /* ret = CT_OUT_OF_RAM; */ /* program error */ /* */ - goto exit_function; - } - if ( bCtPredecessors ) { - start = 0; - } else { - /* find DFS start vertex (atom) */ - for ( i = 1, start = 0; i < num_atoms; i ++ ) { - if ( nl[i][0] < nl[start][0] ) { /* index = nRank-1 */ - start = i; - } - } - } - /* - vertex information: - 1. Number of (forward edges) + (back edges, first visit -- ring closures): nl[i][0] - 2. Number of vertices traversed from this vertex, including the vertex: nNumDescendants[i] - 3. Each edge information: - a. forward edge (0) or back edge (1) indicator: nDfsNumber[i] > nDfsNumber[neigh] - b. neighbor at another end of the edge neigh = nl[i][k+1], k < i - - Total per edge: 2 + 2*(number of edges) - */ - - /* DFS initiation */ - u = start; /* start atom */ - nDfs = 0; - nTopStackAtom =-1; - memset( nDfsNumber, 0, num_atoms*sizeof(nDfsNumber[0])); - memset( nNumDescendants, 0, num_atoms*sizeof(nNumDescendants[0])); - memset( cNeighNumb, 0, num_atoms*sizeof(cNeighNumb[0])); - /* push the start atom on the stack */ - nDfsNumber[u] = ++nDfs; - if ( bCtPredecessors ) { - nNumDescendants[u] = 0; /* atom #1 has no predecessor */ - } else { - nNumDescendants[u] = 1; /* count itself as a descendant */ - } - nStackAtom[++nTopStackAtom] = (AT_NUMB)u; - /* nNumStartChildren = 0; */ - num_rings = 0; - - /* DFS */ - - do { - /* advance */ - while ( i=(int)nStackAtom[nTopStackAtom], j = (int)cNeighNumb[i]+1, (int)nl[i][0] >= j ) - /*while ( (int)nl[i=nStackAtom[nTopStackAtom]][0] >= (j = (int)cNeighNumb[i]+1) )*/ - /* replaced due to missing sequence point; undefined behavior, pointed by Geoffrey Hutchison */ - { - cNeighNumb[i] ++; - u = (int)nl[i][j]; /* jth neighbor of the vertex i */ - if ( !nDfsNumber[u] ) { - /* tree edge, 1st visit -- advance */ - /* put unexplored vertex u on the stack for further examination */ - nStackAtom[++nTopStackAtom] = (AT_NUMB)u; - nDfsNumber[u] = ++nDfs; - if ( bCtPredecessors ) { - nNumDescendants[u] = i+1; /* predecessor's rank */ - } else { - nNumDescendants[u] ++; /* count atom u as its descendant */ - } - } else - if ( nTopStackAtom && u != (int)nStackAtom[nTopStackAtom-1] && - /* back edge: u is not a predecessor of i */ - nDfsNumber[u] < nDfsNumber[i] ) { - /* Back edge, 1st visit: u is an ancestor of i (ring closure) */ - if ( !bCtPredecessors ) { - nNumDescendants[i] ++; /* count closures as descendants */ - } - num_rings ++; /* count ring closures */ - } else { - nl[i][j] = MAX_ATOMS+1; /* back edge, 2nd visit: mark as deleted */ - } - } - cNeighNumb[i] = 0; /* all neighbors of the ith atom have been - traversed; resore the neighbor counter */ - /* back up */ - if ( !bCtPredecessors && nTopStackAtom /* that is, i != start */) { - u = (int)nStackAtom[nTopStackAtom-1]; /* predecessor of i */ - nNumDescendants[u] += nNumDescendants[i]; /* add descendants */ - } - } while ( --nTopStackAtom >= 0 ); - - /* Sort the neighbors in ascending order so that: - primary key = number of descendants in the DFS tree; closure neighbor is 0 - secondary key = canonical number (here vertex number = canonical number - 1) - */ - - /* set static globals for the sorting: */ - gDfs4CT_nDfsNumber = nDfsNumber; - gDfs4CT_nNumDescendants = nNumDescendants; - gDfs4CT_nCurrentAtom = -1; - - /* sorting; deleted will be the last neighbors */ - for ( i = 0; i < num_atoms; i ++ ) { - if ( nl[i][0] > 1 ) { - gDfs4CT_nCurrentAtom = i; - insertions_sort( &nl[i][1], nl[i][0], sizeof(nl[i][1]), CompareDfsDescendants4CT ); - } - /* reduce number of neighbors to exclude deleted */ - for ( k = 0; k < nl[i][0] && nl[i][k+1] <= MAX_ATOMS; k ++ ) - ; - nl[i][0] = k; - } - - nTotOutputStringLen = 3*(num_atoms+num_rings+1); /* last 3 elements are a 'zero termination' */ - - if ( bCtPredecessors ) { - if ( nOutputString = (AT_RANK *)inchi_calloc( nTotOutputStringLen, sizeof(nOutputString[0]) ) ) { - cDelim = '-'; - for ( u = 0, k = -3 ; u < num_atoms; u ++ ) { - k += 3; - if ( k+6 > nTotOutputStringLen ) { - goto exit_error; /* program error */ - } - nOutputString[k] = nNumDescendants[u]? nNumDescendants[u] : MAX_ATOMS+1; - nOutputString[k+1] = nNum_H? 16+nNum_H[u]:0; - nOutputString[k+2] = k? ',' : '\0'; - for ( j = 1; j <= nl[u][0] && nDfsNumber[u] > nDfsNumber[i=nl[u][j]]; j ++ ) { - /* closures */ - k += 3; - if ( k+6 > nTotOutputStringLen ) { - goto exit_error; /* program error */ - } - nOutputString[k] = i+1; /* closure */ - nOutputString[k+1] = 0; - nOutputString[k+2] = cDelim; - } - } - } - } else { - if ( nNumDescendants ) { /* do not need anymore */ - inchi_free( nNumDescendants ); - nNumDescendants = NULL; - } - /* - the output string contains: - (num_atoms) atoms for the DFS (spanning) tree - (num_atoms-1) delimiters for the DFS (spanning) tree - 1 character for each atom that has 1 terminal hydrogen atoms - 2 characters for each atom that has 2-9 terminal hydrogen atoms - 3 characters for each atom that has 10-99 terminal hydrogen atoms, etc. - (num_rings) atoms for the ring closures - (num_rings) delimiters for the ring closures - */ - - if ( nOutputString = (AT_RANK *)inchi_calloc( nTotOutputStringLen, sizeof(nOutputString[0]) ) ) { - u = start; /* start atom */ - nTopStackAtom =-1; - memset( cNeighNumb, 0, num_atoms*sizeof(cNeighNumb[0])); - /* push the start atom on the stack */ - nStackAtom[++nTopStackAtom] = (AT_NUMB)u; - /* output the starting atom */ - k = 0; - nOutputString[k] = u+1; - nOutputString[k+1] = nNum_H? 16+nNum_H[u]:0; - nOutputString[k+2] = '\0'; - - do { - /* advance */ - while ( i=(int)nStackAtom[nTopStackAtom], j = (int)cNeighNumb[i]+1, (int)nl[i][0] >= j ) - /*while ( (int)nl[i=nStackAtom[nTopStackAtom]][0] >= (j = (int)cNeighNumb[i]+1) )*/ - /* replaced due to missing sequence point; undefined behavior, reported by Geoffrey Hutchison */ - { - k += 3; - if ( k+6 > nTotOutputStringLen ) { - goto exit_error; /* program error */ - } - cNeighNumb[i] ++; - u = (int)nl[i][j]; /* neighbor */ - - /* output neighbor's canonical number */ - nOutputString[k] = u+1; - - if ( nDfsNumber[u] > nDfsNumber[i] ) { - /* tree edge, 1st visit -- advance */ - /* put 'unexplored' vertex u on the stack */ - nStackAtom[++nTopStackAtom] = (AT_NUMB)u; - - /* output neighbor's number of H */ - nOutputString[k+1] = nNum_H? 16+nNum_H[u]:0; - } else { - nOutputString[k+1] = 0; - } - /* output a delimiter preceding the neighbor */ - if ( 1 < nl[i][0] ) { - if ( j == 1 ) { - cDelim = '('; - } else - if ( j == nl[i][0] ) { - cDelim = ')'; - } else { - cDelim = ','; - } - } else { - cDelim = '-'; - } - nOutputString[k+2] = cDelim; - } - cNeighNumb[i] = 0; - - /* back up: nothing else to do */ - } while ( --nTopStackAtom >= 0 ); - } - } - goto exit_function; - -exit_error: - if ( nOutputString ) { - inchi_free( nOutputString ); - nOutputString = NULL; - } - -exit_function: - if ( nStackAtom ) - inchi_free( nStackAtom ); - if ( nNumDescendants ) - inchi_free( nNumDescendants ); - if ( nDfsNumber ) - inchi_free( nDfsNumber ); - if ( cNeighNumb ) - inchi_free( cNeighNumb ); - if ( nl ) - FreeNeighList( nl ); - return nOutputString; -} -/**********************************************************************************************/ -int GetInpStructErrorType( INPUT_PARMS *ip, int err, char *pStrErrStruct, int num_inp_atoms ) -{ - if ( err && err == 9 ) - return _IS_ERROR; /* sdfile bypassed to $$$$ */ - if ( err && err < 30 ) - return _IS_FATAL; - if ( num_inp_atoms <= 0 || err ) { - if ( 98 == err && 0 == num_inp_atoms && ip->bAllowEmptyStructure ) - return _IS_WARNING; - return _IS_ERROR; - } - if ( pStrErrStruct[0] ) - return _IS_WARNING; - return _IS_OKAY; -} -/**********************************************************************************************/ -int ProcessStructError( INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *log_file, /*int err,*/ - char *pStrErrStruct, int nErrorType, - int *bXmlStructStarted, long num_inp, INPUT_PARMS *ip, char *pStr, int nStrLen ) -{ - int b_ok; -#ifdef TARGET_LIB_FOR_WINCHI - int bPlainText = (ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) && - (ip->bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW ) && - !(ip->bINChIOutputOptions & INCHI_OUT_XML); -#else - int bPlainText = 0; -#endif - if ( !bPlainText && *bXmlStructStarted <= 0 ) { - return nErrorType; - } - /* Fatal error, Error, Warning */ - if ( nErrorType ) { - if ( bPlainText ) { - if ( !(b_ok=OutputINChIPlainError( output_file, pStr, nStrLen, pStrErrStruct, nErrorType ) ) ) { - inchi_ios_eprint( log_file, "Cannot create message for error (structure #%ld.%s%s%s%s) Terminating.\n", - num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - } else { - inchi_ios_print( output_file, "\n" ); /* add a blank line after the WINCHI Window message */ - } - } else { - if ( !(b_ok=OutputINChIXmlError( output_file, pStr, nStrLen, 2, /*err,*/ pStrErrStruct, nErrorType ) ) ) { - inchi_ios_eprint( log_file, "Cannot create xml tag for error (structure #%ld.%s%s%s%s) Terminating.\n", - num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - } - if ( !b_ok || nErrorType == _IS_FATAL || nErrorType == _IS_ERROR ) { - /* close current structure output */ - if ( !OutputINChIXmlStructEndTag( output_file, pStr, nStrLen, 1 ) ) { - inchi_ios_eprint( log_file, "Cannot create end xml tag for structure #%ld.%s%s%s%s Terminating.\n", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - *bXmlStructStarted = -1; - b_ok = 0; - } else { - *bXmlStructStarted = 0; - } - } - } - return b_ok? nErrorType : _IS_FATAL; - } - return nErrorType; - -} - -#if ( TEST_RENUMB_ATOMS == 1 ) /* { */ -/***************************************************************************************/ -int CompareStereoINChI( INChI_Stereo *s1, INChI_Stereo *s2 ) -{ - if ( s1 == NULL && s2 == NULL ) - return 0; - if ( (s1 == NULL) ^ (s2 == NULL) ) - return 20; - - if ( s1->nNumberOfStereoCenters != s2->nNumberOfStereoCenters ) - return 21; - if ( s1->nNumberOfStereoCenters > 0 ) { - if ( memcmp( s1->nNumber, s2->nNumber, s1->nNumberOfStereoCenters*sizeof(s1->nNumber[0]) ) ) - return 22; - if ( memcmp( s1->t_parity, s2->t_parity, s1->nNumberOfStereoCenters*sizeof(s1->t_parity[0]) ) ) - return 23; - if ( s1->nNumberInv && s2->nNumberInv ) { - if ( memcmp( s1->nNumberInv, s2->nNumberInv, s1->nNumberOfStereoCenters*sizeof(s1->nNumber[0]) ) ) - return 28; - if ( memcmp( s1->t_parityInv, s2->t_parityInv, s1->nNumberOfStereoCenters*sizeof(s1->t_parity[0]) ) ) - return 29; - if ( s1->nCompInv2Abs != s2->nCompInv2Abs || - s1->bTrivialInv != s2->bTrivialInv ) { - return 30; - } - } else - if ( s1->nNumberInv || s2->nNumberInv ) { - return 31; - } - } - if ( s1->nNumberOfStereoBonds != s2->nNumberOfStereoBonds ) - return 24; - if ( s1->nNumberOfStereoBonds > 0 ) { - if ( memcmp( s1->nBondAtom1, s2->nBondAtom1, s1->nNumberOfStereoBonds*sizeof(s1->nBondAtom1[0]) ) ) - return 25; - if ( memcmp( s1->nBondAtom2, s2->nBondAtom2, s1->nNumberOfStereoBonds*sizeof(s1->nBondAtom2[0]) ) ) - return 26; - if ( memcmp( s1->b_parity, s2->b_parity, s1->nNumberOfStereoBonds*sizeof(s1->b_parity[0]) ) ) - return 27; - } - return 0; -} -/***************************************************************************************/ -int CompareINChI( INChI *i1, INChI *i2, INChI_Aux *a1, INChI_Aux *a2 ) -{ - int ret; - if ( i1 == NULL && i2 == NULL ) - return 0; - if ( (i1 == NULL) ^ (i2 == NULL) ) - return 1; - - if ( i1->nErrorCode == i2->nErrorCode ) { - if ( i1->nErrorCode ) - return 0; - } else { - return 2; - } - - if ( i1->nNumberOfAtoms != i2->nNumberOfAtoms ) - return 3; - if ( i1->nNumberOfAtoms > 0 ) { - if ( memcmp( i1->nAtom, i2->nAtom, i1->nNumberOfAtoms*sizeof(i1->nAtom[0]) ) ) - return 4; - if ( memcmp( i1->nNum_H, i2->nNum_H, i1->nNumberOfAtoms*sizeof(i1->nNum_H[0]) ) ) - return 5; - if ( i1->nNum_H_fixed && i2->nNum_H_fixed && - memcmp( i1->nNum_H_fixed, i2->nNum_H_fixed, i1->nNumberOfAtoms*sizeof(i1->nNum_H_fixed[0]) ) ) { - return 6; - } - if ( strcmp( i1->szHillFormula, i2->szHillFormula ) ) - return 7; - } - - if ( i1->lenConnTable != i2->lenConnTable ) - return 8; - if ( i1->lenConnTable > 0 && memcmp( i1->nConnTable, i2->nConnTable, i1->lenConnTable*sizeof(i1->nConnTable[0]) ) ) - return 9; - - if ( i1->lenTautomer != i2->lenTautomer ) - return 10; - if ( i1->lenTautomer > 0 && memcmp( i1->nTautomer, i2->nTautomer, i1->lenTautomer*sizeof(i1->nTautomer[0]) ) ) - return 11; - - if ( i1->nNumberOfIsotopicAtoms != i2->nNumberOfIsotopicAtoms ) - return 12; - if ( i1->nNumberOfIsotopicAtoms > 0 && memcmp( i1->IsotopicAtom, i2->IsotopicAtom, i1->nNumberOfIsotopicAtoms*sizeof(i1->IsotopicAtom[0]) ) ) - return 13; - - if ( i1->nNumberOfIsotopicTGroups != i2->nNumberOfIsotopicTGroups ) - return 14; - if ( i1->nNumberOfIsotopicTGroups > 0 && memcmp( i1->IsotopicTGroup, i2->IsotopicTGroup, i1->nNumberOfIsotopicTGroups*sizeof(i1->IsotopicTGroup[0]) ) ) - return 15; - if ( a1->nNumRemovedProtons != a2->nNumRemovedProtons ) - return 16; - if ( memcmp( a1->nNumRemovedIsotopicH, a2->nNumRemovedIsotopicH, sizeof(a1->nNumRemovedIsotopicH) ) ) - return 17; - if ( i1->nPossibleLocationsOfIsotopicH && i2->nPossibleLocationsOfIsotopicH ) { - if ( i1->nPossibleLocationsOfIsotopicH[0] != i2->nPossibleLocationsOfIsotopicH[0] || - memcmp(i1->nPossibleLocationsOfIsotopicH, i2->nPossibleLocationsOfIsotopicH, - sizeof(i1->nPossibleLocationsOfIsotopicH[0])*i1->nPossibleLocationsOfIsotopicH[0]) ) - return 18; - } else - if ( !i1->nPossibleLocationsOfIsotopicH != !i2->nPossibleLocationsOfIsotopicH ) { - return 19; - } - /* ret = 20..31 */ - if ( ret = CompareStereoINChI( i1->Stereo, i2->Stereo ) ) - return ret; - /* ret = 40..51 */ - if ( ret = CompareStereoINChI( i1->StereoIsotopic, i2->StereoIsotopic ) ) - return ret+20; - - return 0; -} -#endif /* } TEST_RENUMB_ATOMS == 1 */ -#if ( READ_INCHI_STRING == 1 ) /* { */ -/*************************************************************************************/ -int CompareReversedStereoINChI( INChI_Stereo *s1/* InChI from reversed struct */, INChI_Stereo *s2 /* input InChI */) -{ - if ( s1 == NULL && s2 == NULL ) - return 0; - if ( (s1 == NULL) ^ (s2 == NULL) ) { - INChI_Stereo *s = s1? s1 : s2; - if ( s->nNumberOfStereoCenters || s->nNumberOfStereoBonds ) { - return 20; /* Diff: Missing Stereo */ - } else { - return 0; - } - } - - if ( s1->nNumberOfStereoCenters != s2->nNumberOfStereoCenters ) - return 21; /* Diff: Number of sp3 stereocenters */ - if ( s1->nNumberOfStereoCenters > 0 ) { - if ( memcmp( s1->nNumber, s2->nNumber, s1->nNumberOfStereoCenters*sizeof(s1->nNumber[0]) ) ) - return 22; /* Diff: sp3 stereocenter locations */ - if ( memcmp( s1->t_parity, s2->t_parity, s1->nNumberOfStereoCenters*sizeof(s1->t_parity[0]) ) ) - return 23; /* Diff: sp3 stereocenter parities */ - if ( s1->nCompInv2Abs != s2->nCompInv2Abs && s1->nCompInv2Abs && s2->nCompInv2Abs ) - return 24; /* Diff: sp3 inversion */ - /* - if ( s1->nNumberInv && s2->nNumberInv ) { - if ( memcmp( s1->nNumberInv, s2->nNumberInv, s1->nNumberOfStereoCenters*sizeof(s1->nNumber[0]) ) ) - return 25; - if ( memcmp( s1->t_parityInv, s2->t_parityInv, s1->nNumberOfStereoCenters*sizeof(s1->t_parity[0]) ) ) - return 26; - if ( s1->nCompInv2Abs != s2->nCompInv2Abs || - s1->bTrivialInv != s2->bTrivialInv ) { - return 27; - } - } else - if ( s1->nNumberInv || s2->nNumberInv ) { - return 28; - } - */ - } - if ( s1->nNumberOfStereoBonds != s2->nNumberOfStereoBonds ) - return 25; /* Diff: Number of stereobonds */ - if ( s1->nNumberOfStereoBonds > 0 ) { - if ( memcmp( s1->nBondAtom1, s2->nBondAtom1, s1->nNumberOfStereoBonds*sizeof(s1->nBondAtom1[0]) ) ) - return 26; /* Diff: Stereobond 1st atom locations */ - if ( memcmp( s1->nBondAtom2, s2->nBondAtom2, s1->nNumberOfStereoBonds*sizeof(s1->nBondAtom2[0]) ) ) - return 27; /* Diff: Stereobond 2nd atom locations */ - if ( memcmp( s1->b_parity, s2->b_parity, s1->nNumberOfStereoBonds*sizeof(s1->b_parity[0]) ) ) - return 28; /* Diff: Stereobond parities */ - } - return 0; -} -/*************************************************************************************/ -int CompareReversedStereoINChI2( INChI_Stereo *s1/* InChI from reversed struct */, INChI_Stereo *s2 /* input InChI */, ICR *picr) -{ - int ret = 0; - int j1, j2, num_eq, num_dif, num_extra_undf, num_miss_undf, num_in1_only, num_in2_only; - int bAddSb = !(picr->num_sb_undef_in1_only + picr->num_sb_in1_only + picr->num_sb_in2_only); - int bAddSc = !(picr->num_sc_undef_in1_only + picr->num_sc_in1_only + picr->num_sc_in2_only); - - int nNumSc1 = s1? s1->nNumberOfStereoCenters : 0; - int nNumSc2 = s2? s2->nNumberOfStereoCenters : 0; - int nNumSb1 = s1? s1->nNumberOfStereoBonds : 0; - int nNumSb2 = s2? s2->nNumberOfStereoBonds : 0; - - if ( (nNumSc1 || nNumSc1) && - ( nNumSc1 != nNumSc2 || - memcmp( s1->nNumber, s2->nNumber, nNumSc1*sizeof(s1->nNumber[0] ) ) || - memcmp( s1->t_parity, s2->t_parity, nNumSc1*sizeof(s1->t_parity[0]) ) ) ) { - - num_eq = num_dif = num_extra_undf = num_miss_undf = num_in1_only = num_in2_only = 0; - for ( j1 = j2 = 0; j1 < nNumSc1 && j2 < nNumSc2; ) { - if ( s1->nNumber[j1] == s2->nNumber[j2] ) { - if ( s1->t_parity[j1] == s2->t_parity[j2] ) { - num_eq ++; - } else { - num_dif ++; - } - j1 ++; - j2 ++; - } else - if ( s1->nNumber[j1] < s2->nNumber[j2] ) { - num_in1_only ++; - if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { - num_extra_undf ++; - } - if ( bAddSc ) { - if ( picr->num_sc_in1_only < ICR_MAX_SC_IN1_ONLY ) - picr->sc_in1_only[picr->num_sc_in1_only ++] = j1; - if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { - if ( picr->num_sc_undef_in1_only < ICR_MAX_SC_UNDF ) - picr->sc_undef_in1_only[picr->num_sc_undef_in1_only ++] = j1; - } - } - j1 ++; - } else { - num_in2_only ++; - if ( s2->t_parity[j2] == AB_PARITY_UNDF ) { - num_miss_undf ++; - } - if ( bAddSc ) { - if ( picr->num_sc_in2_only < ICR_MAX_SC_IN2_ONLY ) - picr->sc_in2_only[picr->num_sc_in2_only ++] = j2; - if ( s2->t_parity[j2] == AB_PARITY_UNDF ) { - if ( picr->num_sc_undef_in2_only < ICR_MAX_SC_UNDF ) - picr->sc_undef_in2_only[picr->num_sc_undef_in2_only ++] = j1; - } - } - j2 ++; - } - } - while ( j1 < nNumSc1 ) { - if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { - num_extra_undf ++; - } - num_in1_only ++; - if ( bAddSc ) { - if ( picr->num_sc_in1_only < ICR_MAX_SC_IN1_ONLY ) - picr->sc_in1_only[picr->num_sc_in1_only ++] = j1; - if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { - if ( picr->num_sc_undef_in1_only < ICR_MAX_SC_UNDF ) - picr->sc_undef_in1_only[picr->num_sc_undef_in1_only ++] = j1; - } - } - j1 ++; - } - while ( j2 < nNumSc2 ) { - if ( s2->t_parity[j2] == AB_PARITY_UNDF ) { - num_miss_undf ++; - } - num_in2_only ++; - if ( bAddSc ) { - if ( picr->num_sc_in2_only < ICR_MAX_SC_IN2_ONLY ) - picr->sc_in2_only[picr->num_sc_in2_only ++] = j2; - } - j2 ++; - } - if ( num_dif ) { - ret |= IDIF_SC_PARITY; - } - if ( num_in1_only ) { - if ( num_extra_undf ) { - ret |= IDIF_SC_EXTRA_UNDF; - } - if ( num_in1_only != num_extra_undf ) { - ret |= IDIF_SC_EXTRA; - } - } - if ( num_in2_only ) { - if ( num_miss_undf ) { - ret |= IDIF_SC_MISS_UNDF; - } - if ( num_in2_only != num_miss_undf ) { - ret |= IDIF_SC_MISS; - } - } - } - if ( s1 && s2 && s1->nCompInv2Abs != s2->nCompInv2Abs && s1->nCompInv2Abs && s2->nCompInv2Abs ) { - ret |= IDIF_SC_INV; - } - - if ( (nNumSb1 || nNumSb2 ) && - (nNumSb1 != nNumSb2 || - memcmp( s1->nBondAtom1, s2->nBondAtom1, nNumSb1*sizeof(s1->nBondAtom1[0]) ) || - memcmp( s1->nBondAtom2, s2->nBondAtom2, nNumSb1*sizeof(s1->nBondAtom2[0]) ) || - memcmp( s1->b_parity, s2->b_parity, nNumSb1*sizeof(s1->b_parity[0]) ) ) ) { - - num_eq = num_dif = num_extra_undf = num_miss_undf = num_in1_only = num_in2_only = 0; - for ( j1 = j2 = 0; j1 < nNumSb1 && j2 < nNumSb2; ) { - if ( s1->nBondAtom1[j1] == s2->nBondAtom1[j2] && - s1->nBondAtom2[j1] == s2->nBondAtom2[j2] ) { - if ( s1->b_parity[j1] == s2->b_parity[j2] ) { - num_eq ++; - } else { - num_dif ++; - } - j1 ++; - j2 ++; - } else - if ( s1->nBondAtom1[j1] < s2->nBondAtom1[j2] || - s1->nBondAtom1[j1] == s2->nBondAtom1[j2] && s1->nBondAtom2[j1] < s2->nBondAtom2[j2]) { - num_in1_only ++; - if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { - num_extra_undf ++; - } - if ( bAddSb ) { - if ( picr->num_sb_in1_only < ICR_MAX_SB_IN1_ONLY ) - picr->sb_in1_only[picr->num_sb_in1_only ++] = j1; - if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { - if ( picr->num_sb_undef_in1_only < ICR_MAX_SB_UNDF ) - picr->sb_undef_in1_only[picr->num_sb_undef_in1_only ++] = j1; - } - } - j1 ++; - } else { - num_in2_only ++; - if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { - num_miss_undf ++; - } - if ( bAddSb ) { - if ( picr->num_sb_in2_only < ICR_MAX_SB_IN2_ONLY ) - picr->sb_in2_only[picr->num_sb_in2_only ++] = j2; - if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { - if ( picr->num_sb_undef_in2_only < ICR_MAX_SB_UNDF ) - picr->sb_undef_in2_only[picr->num_sb_undef_in2_only ++] = j1; - } - } - j2 ++; - } - } - while ( j1 < nNumSb1 ) { - num_in1_only ++; - if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { - num_extra_undf ++; - } - if ( bAddSb ) { - if ( picr->num_sb_in1_only < ICR_MAX_SB_IN1_ONLY ) - picr->sb_in1_only[picr->num_sb_in1_only ++] = j1; - if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { - if ( picr->num_sb_undef_in1_only < ICR_MAX_SB_UNDF ) - picr->sb_undef_in1_only[picr->num_sb_undef_in1_only ++] = j1; - } - } - j1 ++; - } - while ( j2 < nNumSb2 ) { - num_in2_only ++; - if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { - num_miss_undf ++; - } - if ( bAddSb ) { - if ( picr->num_sb_in2_only < ICR_MAX_SB_IN2_ONLY ) - picr->sb_in2_only[picr->num_sb_in2_only ++] = j2; - if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { - if ( picr->num_sb_undef_in2_only < ICR_MAX_SB_UNDF ) - picr->sb_undef_in2_only[picr->num_sb_undef_in2_only ++] = j1; - } - } - j2 ++; - } - if ( num_dif ) { - ret |= IDIF_SB_PARITY; - } - if ( num_in1_only ) { - if ( num_extra_undf ) { - ret |= IDIF_SB_EXTRA_UNDF; - } - if ( num_in1_only != num_extra_undf ) { - ret |= IDIF_SB_EXTRA; - } - } - if ( num_in2_only ) { - if ( num_miss_undf ) { - ret |= IDIF_SB_MISS_UNDF; - } - if ( num_in2_only != num_miss_undf ) { - ret |= IDIF_SB_MISS; - } - } - } - - return ret; -} -/*************************************************************************************/ -int CompareReversedINChI( INChI *i1 /* InChI from reversed struct */, INChI *i2 /* input InChI */, INChI_Aux *a1, INChI_Aux *a2 ) -{ - int ret; - if ( i1 == NULL && i2 == NULL ) - return 0; - if ( (i1 == NULL) ^ (i2 == NULL) ) - return 1; /* Diff: Missing InChI */ - - if ( i1->nErrorCode == i2->nErrorCode ) { - if ( i1->nErrorCode ) - return 0; - } else { - return 2; /* Diff: Error codes */ - } - if ( i1->bDeleted != i2->bDeleted ) { - return 1; /* Diff: Missing InChI */ - } - if ( i1->nNumberOfAtoms != i2->nNumberOfAtoms ) - return 3; /* Diff: Num. atoms */ - if ( i1->nNumberOfAtoms > 0 ) { - if ( memcmp( i1->nAtom, i2->nAtom, i1->nNumberOfAtoms*sizeof(i1->nAtom[0]) ) ) - return 4; /* Diff: Elements */ - if ( strcmp( i1->szHillFormula, i2->szHillFormula ) ) - return 7; /* Diff: Hill Formulas */ - if ( memcmp( i1->nNum_H, i2->nNum_H, i1->nNumberOfAtoms*sizeof(i1->nNum_H[0]) ) ) { - if ( i1->lenConnTable > 1 || i2->lenConnTable > 1 ) { - return 5; /* Diff: H Locations (mobile H present) */ - } else { - return 6; /* Diff: H Locations (no mobile H) */ - } - } - /* fixed H */ - if ( i1->nNum_H_fixed || i2->nNum_H_fixed ) { - int bHasFixedH1 = 0, bHasFixedH2 = 0, i, j1, j2; - if ( i1->nNum_H_fixed ) { - for ( i = 0; i < i1->nNumberOfAtoms; i ++ ) { - if ( i1->nNum_H_fixed[i] ) { - bHasFixedH1 ++; - } - } - } - if ( i2->nNum_H_fixed ) { - for ( i = 0; i < i2->nNumberOfAtoms; i ++ ) { - if ( i2->nNum_H_fixed[i] ) { - bHasFixedH2 ++; - } - } - } - /* count the differences */ - j1 = j2 = 0; - if ( bHasFixedH1 && !bHasFixedH2 ) { - for ( i = 0; i < i1->nNumberOfAtoms; i ++ ) { - if ( i1->nNum_H_fixed[i] > 0 ) { - j1 ++; - } else - if ( i1->nNum_H_fixed[i] < 0 ) { - j2 ++; - } - } - - return 18; /* Diff: Extra Fixed-H */ - } else - if ( !bHasFixedH1 && bHasFixedH2 ) { - for ( i = j1 = j2 = 0; i < i1->nNumberOfAtoms; i ++ ) { - if ( 0 > i2->nNum_H_fixed[i] ) { - j1 ++; - } else - if ( 0 < i2->nNum_H_fixed[i] ) { - j2 ++; - } - } - return 19; /* Diff: Missed Fixed-H */ - } else - if ( bHasFixedH1 && bHasFixedH2 && - memcmp( i1->nNum_H_fixed, i2->nNum_H_fixed, i1->nNumberOfAtoms*sizeof(i1->nNum_H_fixed[0]) ) ) { - for ( i = j1 = j2 = 0; i < i1->nNumberOfAtoms; i ++ ) { - if ( i1->nNum_H_fixed[i] > i2->nNum_H_fixed[i] ) { - j1 ++; - } else - if ( i1->nNum_H_fixed[i] < i2->nNum_H_fixed[i] ) { - j2 ++; - } - } - } - ret = (j1 && j2)? 20 : j1? 18 : j2? 19 : 0; - if ( ret ) { - return ret; /* 20 => Diff: NotEql Fixed-H */ - /* 19 => Diff: Missed Fixed-H (i1 has less) */ - /* 18 => Diff: Extra Fixed-H (i1 has more) */ - } - } - } - - if ( i1->lenConnTable != i2->lenConnTable ) - return 8; /* Diff: Connections length */ - if ( i1->lenConnTable > 0 && memcmp( i1->nConnTable, i2->nConnTable, i1->lenConnTable*sizeof(i1->nConnTable[0]) ) ) - return 9; /* Diff: Connections */ - /* output special cases: different number of t-groups, different sizes of t-groups, different endpoints */ - if ( i1->lenTautomer != i2->lenTautomer && (i1->lenTautomer > 1 || i2->lenTautomer > 1) ) - return 10; /* Diff: Mobile groups length */ /* in isotopic or deprotonated cases i1->lenTautomer == 1 && i1->nTautomer[0] = 0 */ - if ( (i1->lenTautomer > 1 && i2->lenTautomer > 1) && - memcmp( i1->nTautomer, i2->nTautomer, i1->lenTautomer*sizeof(i1->nTautomer[0]) ) ) - return 11; /* Diff: Mobile groups */ - - if ( i1->nNumberOfIsotopicAtoms != i2->nNumberOfIsotopicAtoms ) - return 12; /* Diff: Isotopic atoms number */ - if ( i1->nNumberOfIsotopicAtoms > 0 && memcmp( i1->IsotopicAtom, i2->IsotopicAtom, i1->nNumberOfIsotopicAtoms*sizeof(i1->IsotopicAtom[0]) ) ) - return 13; /* Diff: Isotopic atoms */ - if ( i1->nTotalCharge != i2->nTotalCharge ) - return 14; /* Diff: Charge */ -/* - if ( i1->nNumberOfIsotopicTGroups != i2->nNumberOfIsotopicTGroups ) - return 14; - if ( i1->nNumberOfIsotopicTGroups > 0 && memcmp( i1->IsotopicTGroup, i2->IsotopicTGroup, i1->nNumberOfIsotopicTGroups*sizeof(i1->IsotopicTGroup[0]) ) ) - return 15; -*/ - if ( a1 && a2 ) { - if ( a1->nNumRemovedProtons != a2->nNumRemovedProtons ) - return 16; /* Diff: Number of removed protons */ - if ( memcmp( a1->nNumRemovedIsotopicH, a2->nNumRemovedIsotopicH, sizeof(a1->nNumRemovedIsotopicH) ) ) - return 17; /* Diff: Removed isotopic H */ - } -/* - if ( i1->nPossibleLocationsOfIsotopicH && i2->nPossibleLocationsOfIsotopicH ) { - if ( i1->nPossibleLocationsOfIsotopicH[0] != i2->nPossibleLocationsOfIsotopicH[0] || - memcmp(i1->nPossibleLocationsOfIsotopicH, i2->nPossibleLocationsOfIsotopicH, - sizeof(i1->nPossibleLocationsOfIsotopicH[0])*i1->nPossibleLocationsOfIsotopicH[0]) ) - return 18; - } else - if ( !i1->nPossibleLocationsOfIsotopicH != !i2->nPossibleLocationsOfIsotopicH ) { - return 19; - } -*/ - /* ret = 20..31 => 40..51 */ - if ( ret = CompareReversedStereoINChI( i1->Stereo, i2->Stereo ) ) - return ret+20; - /* ret = 40..51 => 60..71 */ - - if ( !i2->StereoIsotopic && i2->Stereo && i1->StereoIsotopic && - 0 < (i1->StereoIsotopic->nNumberOfStereoBonds + i1->StereoIsotopic->nNumberOfStereoCenters) && - 0 == CompareReversedStereoINChI( i1->StereoIsotopic, i2->Stereo ) ) { - /* InChI from reversed structure does not contain fully duplicated isotopic stereo */ - ; - } else - - if ( ret = CompareReversedStereoINChI( i1->StereoIsotopic, i2->StereoIsotopic ) ) { - return ret+40; - } - - return 0; -} - -/*******************************************************************************/ -int CompareIcr( ICR *picr1, ICR *picr2, INCHI_MODE *pin1, INCHI_MODE *pin2, INCHI_MODE mask ) -{ - int nNumExtraBits1 = 0, nNumExtraBits2 = 0, bit1, bit2; - INCHI_MODE Flg1=picr1->flags, Flg2 = picr2->flags, cur_bit = 1, in1, in2; - int i, ret; - - /* compare flags */ - in1 = in2 = 0; - for ( i = 0; Flg1 || Flg2; i ++, Flg1 >>= 1, Flg2 >>= 1, cur_bit <<= 1 ) { - if ( !(mask & cur_bit) ) { - continue; - } - bit1 = Flg1 & 1; - bit2 = Flg2 & 1; - if ( bit1 && !bit2 ) { - in1 |= 1 << i; - nNumExtraBits1 ++; - } else - if ( !bit1 && bit2 ) { - in2 |= 1 << i; - nNumExtraBits2 ++; - } - } - if ( nNumExtraBits1 && !nNumExtraBits2 ) { - ret = 1; - } else - if ( !nNumExtraBits1 && nNumExtraBits2 ) { - ret = -1; - } else - if ( !in1 && !in2 ) { - ret = 0; - } else { - ret = 2; /* compare produced undefined results */ - } - if ( pin1 ) *pin1 = in1; - if ( pin2 ) *pin2 = in2; - /* more detailed compare not implemented */ - return ret; -} - -/*********************************************************************************************************/ -INCHI_MODE CompareReversedINChI2( INChI *i1 /* InChI from reversed struct */, INChI *i2 /* input InChI */, - INChI_Aux *a1, INChI_Aux *a2, ICR *picr, int *err ) -{ - INCHI_MODE ret = 0; - INChI_Stereo *Stereo1=NULL, *Stereo2=NULL; - int n1, n2, m, j, j1, j2, ret2, num_H1, num_H2; - - *err = 0; - - memset( picr, 0, sizeof(*picr) ); - - if ( i1 == NULL && i2 == NULL ) - return 0; - if ( (i1 == NULL) ^ (i2 == NULL) ) { - ret |= IDIF_PROBLEM; /* one InChI exists while another doesn't */ - goto exit_function; - } - - if ( i1->nErrorCode == i2->nErrorCode ) { - if ( i1->nErrorCode ) { - ret |= IDIF_PROBLEM; /* both InChI have same error codes */ - goto exit_function; - } - } else { - ret |= IDIF_PROBLEM; /* at least one InChI has an error code */ - goto exit_function; - } - - if ( i1->nNumberOfAtoms != i2->nNumberOfAtoms ) { - ret |= IDIF_NUM_AT; - goto exit_function; - } - if ( i1->nNumberOfAtoms > 0 ) { - if ( memcmp( i1->nAtom, i2->nAtom, i1->nNumberOfAtoms*sizeof(i1->nAtom[0]) ) ) { - ret |= IDIF_ATOMS; - goto exit_function; - } - /* IDIF_NON_TAUT_H, IDIF_MORE_FH, IDIF_LESS_FH */ - if ( memcmp( i1->nNum_H, i2->nNum_H, i1->nNumberOfAtoms*sizeof(i1->nNum_H[0]) ) ) { - ret |= IDIF_POSITION_H; - for ( j1 = 0; j1 < i1->nNumberOfAtoms; j1 ++ ) { - if ( i1->nNum_H[j1] != i2->nNum_H[j1] && picr->num_diff_pos_H < ICR_MAX_DIFF_FIXED_H ) { - picr->diff_pos_H_at[picr->num_diff_pos_H] = j1; - picr->diff_pos_H_nH[picr->num_diff_pos_H] = i1->nNum_H[j1] - i2->nNum_H[j1]; - picr->num_diff_pos_H ++; - } - } - } - /* fixed H */ - if ( i1->nNum_H_fixed || i2->nNum_H_fixed ) { - int bHasFixedH1 = 0, bHasFixedH2 = 0, i; - if ( i1->nNum_H_fixed ) { - for ( i = 0; i < i1->nNumberOfAtoms; i ++ ) { - if ( i1->nNum_H_fixed[i] ) { - bHasFixedH1 ++; - } - } - } - if ( i2->nNum_H_fixed ) { - for ( i = 0; i < i2->nNumberOfAtoms; i ++ ) { - if ( i2->nNum_H_fixed[i] ) { - bHasFixedH2 ++; - } - } - } - if ( bHasFixedH1 && !bHasFixedH2 ) { - for ( i = j = 0; i < i1->nNumberOfAtoms; i ++ ) { - if ( i1->nNum_H_fixed[i] ) { - if ( j < ICR_MAX_DIFF_FIXED_H ) { - picr->fixed_H_at1_more[j] = i; - picr->fixed_H_nH1_more[j] = i1->nNum_H_fixed[i]; - j ++; - } - } - } - picr->num_fixed_H1_more = j; - ret |= IDIF_MORE_FH; /* Extra Fixed-H */ - } else - if ( !bHasFixedH1 && bHasFixedH2 ) { - for ( i = j = 0; i < i2->nNumberOfAtoms; i ++ ) { - if ( i2->nNum_H_fixed[i] ) { - if ( j < ICR_MAX_DIFF_FIXED_H ) { - picr->fixed_H_at2_more[j] = i; - picr->fixed_H_nH2_more[j] = i2->nNum_H_fixed[i]; - j ++; - } - } - } - picr->num_fixed_H2_more = j; - ret |= IDIF_LESS_FH; /* Missed Fixed-H */ - } else - if ( bHasFixedH1 && bHasFixedH2 && - memcmp( i1->nNum_H_fixed, i2->nNum_H_fixed, i1->nNumberOfAtoms*sizeof(i1->nNum_H_fixed[0]) ) ) { - for ( i = j1 = j2 = 0; i < i1->nNumberOfAtoms; i ++ ) { - if ( i1->nNum_H_fixed[i] > i2->nNum_H_fixed[i] ) { - if ( j1 < ICR_MAX_DIFF_FIXED_H ) { - picr->fixed_H_at1_more[j1] = i; - picr->fixed_H_nH1_more[j1] = i1->nNum_H_fixed[i] - i2->nNum_H_fixed[i]; - j1 ++; - } - } else - if ( i1->nNum_H_fixed[i] < i2->nNum_H_fixed[i] ) { - if ( j2 < ICR_MAX_DIFF_FIXED_H ) { - picr->fixed_H_at2_more[j2] = i; - picr->fixed_H_nH2_more[j2] = i2->nNum_H_fixed[i] - i1->nNum_H_fixed[i]; - j2 ++; - } - } - } - ret |= (j1? IDIF_MORE_FH:0) | (j2? IDIF_LESS_FH:0); - picr->num_fixed_H1_more = j1; - picr->num_fixed_H2_more = j2; - } - } - } - /* compare formulas and H */ - num_H1 = 0; - num_H2 = 0; - ret2 = CompareHillFormulasNoH( i1->szHillFormula, i2->szHillFormula, &num_H1, &num_H2 ); - picr->tot_num_H1 = num_H1; - picr->tot_num_H2 = num_H2; - if ( ret2 ) { - ret |= IDIF_NUM_EL; - goto exit_function; - } - if ( num_H1 > num_H2 ) { - ret |= IDIF_MORE_H; - } - if ( num_H1 < num_H2 ) { - ret |= IDIF_LESS_H; - } - - if ( i1->lenConnTable != i2->lenConnTable ) { - ret |= IDIF_CON_LEN; - goto exit_function; - } else - if ( i1->lenConnTable > 0 && memcmp( i1->nConnTable, i2->nConnTable, i1->lenConnTable*sizeof(i1->nConnTable[0]) ) ) { - ret |= IDIF_CON_TBL; - goto exit_function; - } - /* output special cases: different number of t-groups, different sizes of t-groups, different endpoints */ - /* in isotopic or deprotonated cases i1->lenTautomer == 1 && i1->nTautomer[0] = 0 */ -/* - if ( i1->lenTautomer != i2->lenTautomer && (i1->lenTautomer > 1 || i2->lenTautomer > 1) ) { - ret |= IDIF_TAUT_LEN; - } -*/ - /* compare number of t-groups */ - n1 = i1->lenTautomer? i1->nTautomer[0] : 0; - n2 = i2->lenTautomer? i2->nTautomer[0] : 0; - if ( !n1 && n2 ) { - ret |= IDIF_NO_TAUT; - } else - if ( n1 && !n2 ) { - ret |= IDIF_WRONG_TAUT; - } else - if ( n1 == 1 && n2 > 1 ) { - ret |= IDIF_SINGLE_TG; - } else - if ( n1 > 1 && n2 == 1 ) { - ret |= IDIF_MULTIPLE_TG; - } else - if ( n1 != n2 ) { - ret |= IDIF_NUM_TG; - } - if ( n1 || n2 ) { - /* number of endpoints */ - int num1 = 0, num2 = 0, num_M1=0, num_M2=0; - int len, num_eq, num_in1_only, num_in2_only; - AT_NUMB *pe1 = (AT_NUMB *) inchi_malloc( (i1->lenTautomer+1) * sizeof(pe1[0]) ); - AT_NUMB *pe2 = (AT_NUMB *) inchi_malloc( (i2->lenTautomer+1) * sizeof(pe2[0]) ); - num_H1 = num_H2=0; - /* collect endpoints, H, (-) */ - if ( !pe1 || !pe2 ) { - if ( pe1 ) inchi_free( pe1 ); - if ( pe2 ) inchi_free( pe2 ); - *err = -1; /* allocation error */ - goto exit_function; - } - for ( m = 1; m < i1->lenTautomer; m += len ) { - len = i1->nTautomer[m ++]; - num_H1 += i1->nTautomer[m]; - num_M1 += i1->nTautomer[m+1]; - for ( j = 2; j < len; j ++ ) { - pe1[num1 ++] = i1->nTautomer[m + j]; - } - } - for ( m = 1; m < i2->lenTautomer; m += len ) { - len = i2->nTautomer[m ++]; - num_H2 += i2->nTautomer[m]; - num_M2 += i2->nTautomer[m+1]; - for ( j = 2; j < len; j ++ ) { - pe2[num2 ++] = i2->nTautomer[m + j]; - } - } - picr->num_taut_H1 = num_H1; - picr->num_taut_H2 = num_H2; - picr->num_taut_M1 = num_M1; - picr->num_taut_M2 = num_M2; - /* sort endpoints */ - insertions_sort_AT_RANK( pe1, num1 ); - insertions_sort_AT_RANK( pe2, num2 ); - /* compare */ - /* - if ( num1 < num2 ) { - ret |= IDIF_LESS_TG_ENDP; - } else - if ( num1 > num2 ) { - ret |= IDIF_MORE_TG_ENDP; - } - */ - /* compare all */ - num_eq = num_in1_only = num_in2_only = 0; - for ( j1 = j2 = 0; j1 < num1 && j2 < num2; ) { - if( pe1[j1] == pe2[j2] ) { - j1 ++; - j2 ++; - num_eq ++; - } else - if ( pe1[j1] < pe2[j2] ) { /* BC: fixed, was pe2[j1] 2006-03-27 */ - if ( picr->num_endp_in1_only < ICR_MAX_ENDP_IN1_ONLY ) { - picr->endp_in1_only[picr->num_endp_in1_only ++] = pe1[j1]; - } - j1 ++; - num_in1_only ++; - } else { - if ( picr->num_endp_in2_only < ICR_MAX_ENDP_IN2_ONLY ) { - picr->endp_in2_only[picr->num_endp_in2_only ++] = pe2[j2]; - } - j2 ++; - num_in2_only ++; - } - } - while ( j1 < num1 ) { - if ( picr->num_endp_in1_only < ICR_MAX_ENDP_IN1_ONLY ) { - picr->endp_in1_only[picr->num_endp_in1_only ++] = pe1[j1]; - } - j1 ++; - num_in1_only ++; - } - while ( j2 < num2 ) { - if ( picr->num_endp_in2_only < ICR_MAX_ENDP_IN2_ONLY ) { - picr->endp_in2_only[picr->num_endp_in2_only ++] = pe2[j2]; - } - j2 ++; - num_in2_only ++; - } - if ( num_in1_only ) { - ret |= IDIF_EXTRA_TG_ENDP; - } - if ( num_in2_only ) { - ret |= IDIF_MISS_TG_ENDP; - } - if ( !num_in1_only && !num_in2_only && num_eq ) { - ; /* same t-groups endpoints */ - } else { - ret |= IDIF_DIFF_TG_ENDP; - } - inchi_free( pe1 ); - inchi_free( pe2 ); - - } - - if ( (i1->lenTautomer > 1 && i2->lenTautomer > 1) && - ( i1->lenTautomer != i2->lenTautomer || - memcmp( i1->nTautomer, i2->nTautomer, i1->lenTautomer*sizeof(i1->nTautomer[0]) ) ) ) - ret |= IDIF_TG; - - if ( i1->nNumberOfIsotopicAtoms != i2->nNumberOfIsotopicAtoms ) { - ret |= IDIF_NUM_ISO_AT; - } else - if ( i1->nNumberOfIsotopicAtoms > 0 && memcmp( i1->IsotopicAtom, i2->IsotopicAtom, i1->nNumberOfIsotopicAtoms*sizeof(i1->IsotopicAtom[0]) ) ) - ret |= IDIF_ISO_AT; - if ( i1->nTotalCharge != i2->nTotalCharge ) - ret |= IDIF_CHARGE; - if ( a1 && a1->nNumRemovedProtons && (!a2 || a2->nNumRemovedProtons != a1->nNumRemovedProtons) ) { - ret |= IDIF_REM_PROT; - } - if ( a1 && (!a2 || - a2->nNumRemovedIsotopicH[0] != a1->nNumRemovedIsotopicH[0] || - a2->nNumRemovedIsotopicH[1] != a1->nNumRemovedIsotopicH[1] || - a2->nNumRemovedIsotopicH[2] != a1->nNumRemovedIsotopicH[2]) ) { - ret |= IDIF_REM_ISO_H; - } - -/* - if ( i1->nPossibleLocationsOfIsotopicH && i2->nPossibleLocationsOfIsotopicH ) { - if ( i1->nPossibleLocationsOfIsotopicH[0] != i2->nPossibleLocationsOfIsotopicH[0] || - memcmp(i1->nPossibleLocationsOfIsotopicH, i2->nPossibleLocationsOfIsotopicH, - sizeof(i1->nPossibleLocationsOfIsotopicH[0])*i1->nPossibleLocationsOfIsotopicH[0]) ) - return 18; - } else - if ( !i1->nPossibleLocationsOfIsotopicH != !i2->nPossibleLocationsOfIsotopicH ) { - return 19; - } -*/ - if ( i1->StereoIsotopic && - i1->StereoIsotopic->nNumberOfStereoBonds + i1->StereoIsotopic->nNumberOfStereoCenters ) { - Stereo1 = i1->StereoIsotopic; - } else { - Stereo1 = i1->Stereo; - } - if ( i2->StereoIsotopic && - i2->StereoIsotopic->nNumberOfStereoBonds + i2->StereoIsotopic->nNumberOfStereoCenters ) { - Stereo2 = i2->StereoIsotopic; - } else { - Stereo2 = i2->Stereo; - } - ret |= CompareReversedStereoINChI2( Stereo1, Stereo2, picr ); - -exit_function: - - picr->flags = ret; - - return ret; -} -#endif /* } READ_INCHI_STRING */ -/***************************************************************************************/ -int Create_INChI( INChI **ppINChI, INChI_Aux **ppINChI_Aux, ORIG_ATOM_DATA *orig_inp_data, /* not used */ - inp_ATOM *inp_at, INP_ATOM_DATA *out_norm_data[2], - int num_inp_at, INCHI_MODE nUserMode, - INCHI_MODE *pbTautFlags, INCHI_MODE *pbTautFlagsDone, - struct tagInchiTime *ulMaxTime, T_GROUP_INFO *ti_out, char *pStrErrStruct) -{ -/* -#define NON_TAUT 0 -#define TAUT 1 -*/ - sp_ATOM *at[TAUT_NUM]; /* at[0]=>non-tautomeric, at[1]=>tautomeric */ - /* inp_ATOM *out_norm_taut_at, *out_norm_nontaut_at; */ - int i, n1, n2, num_atoms, num_at_tg, num_removed_H, num_removed_H_taut=0, ret=0, ret2=0; - INCHI_MODE nMode=0; - T_GROUP_INFO vt_group_info; - T_GROUP_INFO vt_group_info_orig; - T_GROUP_INFO * /*const*/ t_group_info = &vt_group_info; - T_GROUP_INFO * /*const*/ t_group_info_orig = &vt_group_info_orig; - - CANON_STAT CS, CS2; - CANON_STAT *pCS = &CS; - CANON_STAT *pCS2 = &CS2; /* save all allocations to avoid memory leaks in case Canon_INChI() removes the pointer */ - - ATOM_SIZES s[TAUT_NUM]; - - BCN Bcn; - BCN *pBCN = &Bcn; - - int bHasIsotopicAtoms = 0; - int bMayHaveStereo = 0; - int num_taut_at = 0; - - inp_ATOM *out_at = NULL; /*, *norm_at_fixed_bonds[TAUT_NUM]; */ /* = {out_norm_nontaut_at, out_norm_taut_at} ; */ - INChI *pINChI=NULL; /* added initialization 2006-03 */ - INChI_Aux *pINChI_Aux=NULL; /* added initialization 2006-03 */ - int bPointedEdgeStereo = ((TG_FLAG_POINTED_EDGE_STEREO & *pbTautFlags)? PES_BIT_POINT_EDGE_STEREO:0) - | ((TG_FLAG_PHOSPHINE_STEREO & *pbTautFlags)? PES_BIT_PHOSPHINE_STEREO :0) - | ((TG_FLAG_ARSINE_STEREO & *pbTautFlags)? PES_BIT_ARSINE_STEREO :0) - | ((TG_FLAG_FIX_SP3_BUG & *pbTautFlags)? PES_BIT_FIX_SP3_BUG :0); - INCHI_MODE bTautFlags = (*pbTautFlags & (~(INCHI_MODE)TG_FLAG_ALL_TAUTOMERIC) ); - INCHI_MODE bTautFlagsDone = (*pbTautFlagsDone /*& (~(INCHI_MODE)TG_FLAG_ALL_TAUTOMERIC) */); -#if ( bRELEASE_VERSION == 0 ) - int bExtract = 0; /* EXTR_HAS_ATOM_WITH_DEFINED_PARITY; */ -#endif - -/*^^^ */ - int bFixIsoFixedH = 0; - int bFixTermHChrg = 0; - -#if ( TEST_RENUMB_ATOMS == 1 ) - long ulNormTime=0; - long ulCanonTime=0, ulCanonTime2=0; - - inchiTime ulNormTimeStart; - inchiTime ulCanonTimeStart; - - InchiTimeGet( &ulNormTimeStart ); -#endif - - /* vABParityUnknown holds actual value of an internal constant signifying */ - /* unknown parity: either the same as for undefined parity (default==standard) */ - /* or a specific one (non-std; requested by SLUUD switch). */ - int vABParityUnknown = AB_PARITY_UNDF; - if ( 0 != ( nUserMode & REQ_MODE_DIFF_UU_STEREO) ) - { - /* Make labels for unknown and undefined stereo different */ - vABParityUnknown = AB_PARITY_UNKN; - } - - - -/*^^^ */ -#if ( FIX_ISO_FIXEDH_BUG == 1 ) - if (TG_FLAG_FIX_ISO_FIXEDH_BUG & *pbTautFlags) - bFixIsoFixedH = 1; -#endif -#if ( FIX_TERM_H_CHRG_BUG == 1 ) - if (TG_FLAG_FIX_TERM_H_CHRG_BUG & *pbTautFlags) - bFixTermHChrg = 1; -#endif -/*^^^ */ - - memset( s, 0, sizeof(s) ); - if ( pBCN ) { - memset( pBCN, 0, sizeof( pBCN[0] ) ); - } - memset( t_group_info, 0, sizeof(*t_group_info) ); - memset( t_group_info_orig, 0, sizeof(*t_group_info_orig) ); - /*norm_at[TAUT_NON] = out_norm_data[TAUT_NON]->at; *//* output normalized non-tautomeric component */ - /*norm_at[TAUT_YES] = out_norm_data[TAUT_YES]->at; *//* output normalized tautomeric component */ - /*norm_at_fixed_bonds[TAUT_NON] = NULL;*/ - /*norm_at_fixed_bonds[TAUT_YES] = out_norm_data[TAUT_YES]->at_fixed_bonds;*/ - for ( i = 0; i < TAUT_NUM; i ++ ) { - if ( out_norm_data[i]->at ) { - if ( !(at[i] = (sp_ATOM *) inchi_malloc( num_inp_at * sizeof(*at[0]) ) ) ) { - ret = -1; - } - } else { - at[i] = NULL; - } - } - if ( !out_norm_data[TAUT_NON]->at && !out_norm_data[TAUT_YES]->at || !inp_at || ret ) { - ret = -1; - goto exit_function; - } - /* the first struct to process: tautomeric if exists else non-tautomeric */ - out_at = out_norm_data[TAUT_YES]->at? out_norm_data[TAUT_YES]->at : out_norm_data[TAUT_NON]->at; - /* copy the input structure to be normalized to the buffer for the normalization data */ - memcpy( out_at, inp_at, num_inp_at*sizeof(out_at[0]) ); - - /* tautomeric groups setting */ - t_group_info->bIgnoreIsotopic = 0; /* include tautomeric group isotopic info in MarkTautomerGroups() */ - t_group_info->bTautFlags = *pbTautFlags; - t_group_info->bTautFlagsDone = *pbTautFlagsDone; - - /* Preprocess the structure; here THE NUMBER OF ATOMS MAY BE REDUCED */ - /* ??? Ambiguity: H-D may become HD or DH (that is, H+implicit D or D+implicit H) */ - if ( TG_FLAG_H_ALREADY_REMOVED & bTautFlags ) { - INP_ATOM_DATA *out_norm_data1 = out_norm_data[TAUT_YES]->at? out_norm_data[TAUT_YES] : - out_norm_data[TAUT_NON]->at? out_norm_data[TAUT_NON] : NULL; - if ( out_norm_data1 ) { - num_at_tg = - num_atoms = out_norm_data1->num_at - out_norm_data1->num_removed_H; - num_removed_H = out_norm_data1->num_removed_H; - t_group_info->tni.nNumRemovedExplicitH = num_removed_H; - } else { - ret = -1; - goto exit_function; - } - } else { - num_at_tg = - num_atoms = remove_terminal_HDT( num_inp_at, out_at, bFixTermHChrg ); - num_removed_H = num_inp_at - num_atoms; - t_group_info->tni.nNumRemovedExplicitH = num_removed_H; - add_DT_to_num_H( num_atoms, out_at ); - } - /*fix_odd_things( num_atoms, out_at );*/ -#if ( FIND_RING_SYSTEMS == 1 ) - MarkRingSystemsInp( out_at, num_atoms, 0 ); -#endif - /* duplicate the preprocessed structure so that all supplied out_norm_data[]->at buffers are filled */ - if ( out_at != out_norm_data[TAUT_YES]->at && out_norm_data[TAUT_YES]->at ) { - memcpy( out_norm_data[TAUT_YES]->at, out_at, num_inp_at*sizeof(out_at[0]) ); - } - if ( out_norm_data[TAUT_YES]->at_fixed_bonds && out_norm_data[TAUT_YES]->at ) { - memcpy( out_norm_data[TAUT_YES]->at_fixed_bonds, out_at, num_inp_at*sizeof(out_at[0]) ); - } - if ( out_at != out_norm_data[TAUT_NON]->at && out_norm_data[TAUT_NON]->at ) { - memcpy( out_norm_data[TAUT_NON]->at, out_at, num_inp_at*sizeof(out_at[0]) ); - } - - /******************************************************************************* - * ??? not true ??? duplicate inp_at and keep inp_at[] unchanged after terminal hydrogens removal - * set stereo parities in taut_at[], non_taut_at[] - * obtain max. lenghts of the name stereo parts - * Ignore absence/presence of isotopic stereo for now - * mark isotopic atoms - *******************************************************************************/ - if ( out_norm_data[TAUT_YES]->at && at[TAUT_YES] ) { - /* final normalization of possibly tautomeric structure */ - ret = mark_alt_bonds_and_taut_groups ( out_norm_data[TAUT_YES]->at, out_norm_data[TAUT_YES]->at_fixed_bonds, num_atoms, - t_group_info, NULL, NULL ); - if ( ret < 0 ) { - goto exit_function;/* out of RAM or other normalization problem */ - } - num_taut_at = ret; /* number of atoms without removed H? */ - num_removed_H_taut = t_group_info->tni.nNumRemovedExplicitH; - out_norm_data[TAUT_YES]->num_at = num_atoms + num_removed_H_taut; /* protons might have been removed */ - out_norm_data[TAUT_YES]->num_removed_H = num_removed_H_taut; - out_norm_data[TAUT_YES]->nNumRemovedProtons += t_group_info->tni.nNumRemovedProtons; - for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { - out_norm_data[TAUT_YES]->nNumRemovedProtonsIsotopic[i] += t_group_info->tni.nNumRemovedProtonsIsotopic[i] /*+ t_group_info->num_iso_H[i]*/; - out_norm_data[TAUT_YES]->num_iso_H[i] += t_group_info->num_iso_H[i]; - } - /* mark deleted isolated tautomeric H(+) */ - if ( num_taut_at == 1 && out_norm_data[TAUT_YES]->at[0].at_type == ATT_PROTON && - t_group_info && t_group_info->tni.nNumRemovedProtons == 1 ) { - out_norm_data[TAUT_YES]->bDeleted = 1; - FreeInpAtom( &out_norm_data[TAUT_YES]->at_fixed_bonds ); - } else - if ( (t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) && - out_norm_data[TAUT_YES]->at_fixed_bonds) { - out_norm_data[TAUT_YES]->bTautPreprocessed = 1; - } - /* - if ( !(t_group_info->tni.bNormalizationFlags & (FLAG_NORM_CONSIDER_TAUT & ~FLAG_PROTON_SINGLE_REMOVED)) && - out_norm_data[TAUT_YES]->at_fixed_bonds) { - FreeInpAtom( &out_norm_data[TAUT_YES]->at_fixed_bonds ); - } - */ - /*out_norm_data[TAUT_YES]->num_removed_H = num_removed_H_taut;*/ - out_norm_data[TAUT_YES]->bTautFlags = *pbTautFlags = t_group_info->bTautFlags; - out_norm_data[TAUT_YES]->bTautFlagsDone = *pbTautFlagsDone = t_group_info->bTautFlagsDone; - out_norm_data[TAUT_YES]->bNormalizationFlags = t_group_info->tni.bNormalizationFlags; - /* create internal sp_ATOM at[] out of out_norm_data[]->at */ - inp2spATOM( out_norm_data[TAUT_YES]->at, num_inp_at, at[TAUT_YES] ); - /* set stereo parities to at[]; nUserMode: accept alt. stereo bonds, min ring size */ - ret = set_stereo_parity( out_norm_data[TAUT_YES]->at, at[TAUT_YES], num_taut_at, num_removed_H_taut, - &s[TAUT_YES].nMaxNumStereoAtoms, &s[TAUT_YES].nMaxNumStereoBonds, nUserMode, - bPointedEdgeStereo, vABParityUnknown ); -#if ( bRELEASE_VERSION == 0 ) - if ( 0 < ret ) { - bExtract |= EXTR_HAS_ATOM_WITH_DEFINED_PARITY; - } - if ( t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT ) { - bExtract |= EXTR_TAUT_TREATMENT_CHARGES; - } -#endif - if ( RETURNED_ERROR( ret ) ) { - goto exit_function; /* stereo bond error */ - } - s[TAUT_YES].bMayHaveStereo = (s[TAUT_YES].nMaxNumStereoAtoms || s[TAUT_YES].nMaxNumStereoBonds); - /* - * mark isotopic atoms and atoms that have non-tautomeric - * isotopic terminal hydrogen atoms 1H, 2H(D), 3H(T) - */ - s[TAUT_YES].num_isotopic_atoms = set_atom_iso_sort_keys( num_taut_at, at[TAUT_YES], t_group_info, - &s[TAUT_YES].bHasIsotopicTautGroups ); - /************************************************************************** - * prepare tautomeric (if no tautomerism found then prepare non-tautomeric) - * structure for canonicalizaton: - ************************************************************************** - * remove t-groups that have no H, - * remove charges from t-groups if requested - * renumber t-groups and find final t_group_info->num_t_groups - * add to t-groups lists of endpoints tgroup->nEndpointAtomNumber[] - * calculate length of the t-group part of the connection table - **************************************************************************/ - s[TAUT_YES].nLenLinearCTTautomer = CountTautomerGroups( at[TAUT_YES], num_taut_at, t_group_info ); - if ( RETURNED_ERROR(s[TAUT_YES].nLenLinearCTTautomer) ) { /* added error treatment 9-11-2003 */ - ret = s[TAUT_YES].nLenLinearCTTautomer; - goto exit_function; - /* error has happened; no breakpoint here - s[TAUT_YES].nLenLinearCTTautomer = 0; - */ - } else - if ( s[TAUT_YES].nLenLinearCTTautomer > 0 ) { - num_at_tg = num_taut_at+t_group_info->num_t_groups; - /* ??? -not true- create t_group_info_orig for multiple calls with atom renumbering */ - make_a_copy_of_t_group_info( t_group_info_orig /* dest*/, t_group_info /* source*/ ); - /* mark isotopic tautomer groups: calculate t_group->iWeight */ - s[TAUT_YES].nLenLinearCTIsotopicTautomer=set_tautomer_iso_sort_keys( t_group_info ); - if ( s[TAUT_YES].nLenLinearCTIsotopicTautomer < 0 ) { - /* ??? -error cannot happen- error has happened; no breakpoint here */ - s[TAUT_YES].nLenLinearCTIsotopicTautomer = 0; - } - out_norm_data[TAUT_YES]->bTautomeric = s[TAUT_YES].nLenLinearCTTautomer; - } - /* new variable: s[TAUT_YES].nLenCT introduced 7-22-2002 */ - GetCanonLengths( num_taut_at, at[TAUT_YES], &s[TAUT_YES], t_group_info ); - } - if ( out_norm_data[TAUT_NON]->at && out_norm_data[TAUT_YES]->at && at[TAUT_NON] && !s[TAUT_YES].nLenLinearCTTautomer ) { - /* the structure is non-tautomeric: use tautomeric treatment results only for it */ - inchi_free( at[TAUT_NON] ); - at[TAUT_NON] = NULL; - } else - if ( !out_norm_data[TAUT_NON]->at && out_norm_data[TAUT_YES]->at && - !at[TAUT_NON] && at[TAUT_YES] && !s[TAUT_YES].nLenLinearCTTautomer ) { - /* requested tautomeric; found non-tautomeric; it is located in out_norm_data[TAUT_YES]->at */ - out_norm_data[TAUT_YES]->bTautomeric = 0; - } else - if ( out_norm_data[TAUT_NON]->at && at[TAUT_NON] ) { - /* the structure needs non-tautomeric treatment: final normalization of non-tautomeric structure */ - ret = mark_alt_bonds_and_taut_groups ( out_norm_data[TAUT_NON]->at, NULL, num_atoms, NULL, &bTautFlags, &bTautFlagsDone ); - if ( ret < 0 ) { - goto exit_function; /* out of RAM or other normalization problem */ - } - out_norm_data[TAUT_NON]->num_at = num_atoms + num_removed_H; - out_norm_data[TAUT_NON]->num_removed_H = num_removed_H; - out_norm_data[TAUT_NON]->bTautFlags = *pbTautFlags; - out_norm_data[TAUT_NON]->bTautFlagsDone = *pbTautFlagsDone; - out_norm_data[TAUT_NON]->bNormalizationFlags = 0; - /* create internal sp_ATOM at[] out of out_norm_data[]->at */ - inp2spATOM( out_norm_data[TAUT_NON]->at, num_inp_at, at[TAUT_NON] ); - /* set stereo parities to at[]; nUserMode: accept alt. stereo bonds, min ring size */ - ret = set_stereo_parity( out_norm_data[TAUT_NON]->at, at[TAUT_NON], num_atoms, num_removed_H, - &s[TAUT_NON].nMaxNumStereoAtoms, &s[TAUT_NON].nMaxNumStereoBonds, nUserMode, - bPointedEdgeStereo, vABParityUnknown ); -#if ( bRELEASE_VERSION == 0 ) - if ( 0 < ret ) { - bExtract |= EXTR_HAS_ATOM_WITH_DEFINED_PARITY; - } -#endif - if ( RETURNED_ERROR( ret ) ) { - goto exit_function; /* stereo bond error */ - } - s[TAUT_NON].bMayHaveStereo = (s[TAUT_NON].nMaxNumStereoAtoms || s[TAUT_NON].nMaxNumStereoBonds); - /* - * mark isotopic atoms and atoms that have non-tautomeric - * isotopic terminal hydrogen atoms 1H, 2H(D), 3H(T) - */ - s[TAUT_NON].num_isotopic_atoms = set_atom_iso_sort_keys( num_atoms, at[TAUT_NON], NULL, NULL ); - GetCanonLengths( num_atoms, at[TAUT_NON], &s[TAUT_NON], NULL); - out_norm_data[TAUT_NON]->bTautomeric = 0; - } - - /**********************************************************/ - /* common */ - bMayHaveStereo = s[TAUT_YES].bMayHaveStereo || s[TAUT_NON].bMayHaveStereo; - bHasIsotopicAtoms = s[TAUT_NON].num_isotopic_atoms > 0 || s[TAUT_NON].bHasIsotopicTautGroups > 0 || - s[TAUT_YES].num_isotopic_atoms > 0 || s[TAUT_YES].bHasIsotopicTautGroups > 0 ; -/*^^^ */ - if (bFixIsoFixedH) /* 2008-03-21 DT */ - bHasIsotopicAtoms = bHasIsotopicAtoms || - s[TAUT_YES].nLenLinearCTTautomer > 0 && t_group_info && - (0 < NUM_H_ISOTOPES && t_group_info->tni.nNumRemovedProtonsIsotopic[0] || - 1 < NUM_H_ISOTOPES && t_group_info->tni.nNumRemovedProtonsIsotopic[1] || - 2 < NUM_H_ISOTOPES && t_group_info->tni.nNumRemovedProtonsIsotopic[2]) ; -/*^^^ */ - bHasIsotopicAtoms = bHasIsotopicAtoms || - s[TAUT_YES].nLenIsotopicEndpoints > 1 && t_group_info && - (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE|TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE)); - - /* default mode */ - if ( !(nUserMode & REQ_MODE_DEFAULT) ) { - /* default */ - nUserMode |= REQ_MODE_DEFAULT; - } - - /* adjust the mode to the reality */ - if ( ( nUserMode & REQ_MODE_ISO ) && !bHasIsotopicAtoms ) { - nUserMode ^= REQ_MODE_ISO; - nUserMode |= REQ_MODE_NON_ISO; /* at least one is needed */ - } - if ( (nUserMode & REQ_MODE_STEREO) && ( nUserMode & REQ_MODE_ISO ) ) { - nUserMode |= REQ_MODE_ISO_STEREO; - } - if ( (nUserMode & REQ_MODE_STEREO) && !( nUserMode & REQ_MODE_NON_ISO ) ) { - nUserMode ^= REQ_MODE_STEREO; - } - if ( !bMayHaveStereo ) { - if ( nUserMode & REQ_MODE_STEREO ) - nUserMode ^= REQ_MODE_STEREO; - if ( nUserMode & REQ_MODE_ISO_STEREO ) - nUserMode ^= REQ_MODE_ISO_STEREO; - } - - if ( (nUserMode & REQ_MODE_BASIC) && (!out_norm_data[TAUT_NON]->at || !ppINChI[TAUT_NON] || !ppINChI_Aux[TAUT_NON] || !at[TAUT_NON]) ) { - nUserMode ^= REQ_MODE_BASIC; - } - if ( (nUserMode & REQ_MODE_TAUT) && (!out_norm_data[TAUT_YES]->at || !ppINChI[TAUT_YES] || !ppINChI_Aux[TAUT_YES] || !at[TAUT_YES]) ) { - nUserMode ^= REQ_MODE_TAUT; - } - - - switch ((int)nUserMode & (REQ_MODE_BASIC | REQ_MODE_TAUT)) { - case REQ_MODE_BASIC: - n1 = TAUT_NON; - n2 = TAUT_NON; - break; - case REQ_MODE_TAUT: - n1 = TAUT_YES; - n2 = TAUT_YES; - break; - case (REQ_MODE_BASIC | REQ_MODE_TAUT): - n1 = TAUT_NON; - n2 = TAUT_YES; - break; - default: - ret = -3; - goto exit_function; /* program error: inconsistent nUserMode or missing taut/non-taut allocation */ /* */ - } -#if ( TEST_RENUMB_ATOMS == 1 ) - ulNormTime = InchiTimeElapsed( &ulNormTimeStart); -#endif - /************************************************************ - * * - * Obtain all non-stereo canonical numberings * - * * - ************************************************************/ -#if ( TEST_RENUMB_ATOMS == 1 ) - InchiTimeGet( &ulCanonTimeStart ); -#endif - if ( (nUserMode & REQ_MODE_NON_ISO) && !(nUserMode & REQ_MODE_ISO) ) { - /* added for special non-isotopic test mode 2004-10-04 */ - if ( t_group_info ) { - t_group_info->bIgnoreIsotopic = 1; - if ( t_group_info->nIsotopicEndpointAtomNumber ) { - t_group_info->nIsotopicEndpointAtomNumber[0] = inchi_min(1, t_group_info->nIsotopicEndpointAtomNumber[0]); - } - memset( t_group_info->num_iso_H, 0, sizeof(t_group_info->num_iso_H) ); - memset ( t_group_info->tni.nNumRemovedProtonsIsotopic, 0, sizeof(t_group_info->tni.nNumRemovedProtonsIsotopic)); - t_group_info->bTautFlagsDone &= ~(TG_FLAG_FOUND_ISOTOPIC_H_DONE|TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE); - } - for ( i = 0; i < TAUT_NUM; i ++ ) { - s[i].bHasIsotopicTautGroups = 0; - s[i].bIgnoreIsotopic = 1; - s[i].nLenIsotopic = 0; - s[i].nLenIsotopicEndpoints = 0; - s[i].nLenLinearCTIsotopicTautomer = 0; - s[i].num_isotopic_atoms = 0; - } - bHasIsotopicAtoms = 0; - } - ret = GetBaseCanonRanking( num_atoms, num_at_tg, at, t_group_info, s, pBCN, ulMaxTime, bFixIsoFixedH ); -#if ( TEST_RENUMB_ATOMS == 1 ) - ulCanonTime = InchiTimeElapsed( &ulCanonTimeStart ); -#endif - if ( ret < 0 ) { - goto exit_function; /* program error */ - } -#if ( bRELEASE_VERSION == 0 && FIND_CANON_NE_EQUITABLE == 1 ) - /* Debug only: find whether canonical equivalence is different from equitable partition */ - if ( bCanonIsFinerThanEquitablePartition( num_atoms, at[n1], pBCN->ftcn[TAUT_NON].nSymmRankCt ) ) { - bExtract |= EXTR_CANON_NE_EQUITABLE; - } -#endif - /* added for special non-isotopic test mode 2004-10-04 */ - if ( !pBCN->ftcn[n1].PartitionCt.Rank ) { - n1 = ALT_TAUT(n1); - } - if ( !pBCN->ftcn[n2].PartitionCt.Rank ) { - n2 = ALT_TAUT(n2); - } - if ( n1 > n2 ) { - ret = CT_TAUCOUNT_ERR; - goto exit_function; /* program error */ - } - - /************************************************************ - * * - * Obtain stereo canonical numberings * - * * - ************************************************************/ - - for ( i = n2; i >= n1 && !RETURNED_ERROR( ret ); i -- ) { - - memset( pCS, 0, sizeof(*pCS) ); - - switch( i ) { - case TAUT_NON: /* non-tautomeric */ - nMode = 0; - nMode = (s[i].nLenLinearCTTautomer == 0)? CANON_MODE_CT:CANON_MODE_TAUT; - nMode |= (bHasIsotopicAtoms && (nUserMode & REQ_MODE_ISO))? CANON_MODE_ISO:0; - nMode |= (s[TAUT_NON].bMayHaveStereo && (nUserMode & REQ_MODE_STEREO) )? CANON_MODE_STEREO:0; - nMode |= (bHasIsotopicAtoms && s[TAUT_NON].bMayHaveStereo && (nUserMode & REQ_MODE_ISO_STEREO))? CANON_MODE_ISO_STEREO:0; - nMode |= (nUserMode & REQ_MODE_NOEQ_STEREO )? CMODE_NOEQ_STEREO : 0; - nMode |= (nUserMode & REQ_MODE_REDNDNT_STEREO)? CMODE_REDNDNT_STEREO : 0; - nMode |= (nUserMode & REQ_MODE_NO_ALT_SBONDS )? CMODE_NO_ALT_SBONDS : 0; - if ( (nMode & CANON_MODE_STEREO) == CANON_MODE_STEREO || - (nMode & CANON_MODE_ISO_STEREO) == CANON_MODE_ISO_STEREO ) { - nMode |= (nUserMode & REQ_MODE_RELATIVE_STEREO)? CMODE_RELATIVE_STEREO: 0; - nMode |= (nUserMode & REQ_MODE_RACEMIC_STEREO )? CMODE_RACEMIC_STEREO : 0; - nMode |= (nUserMode & REQ_MODE_SC_IGN_ALL_UU )? CMODE_SC_IGN_ALL_UU : 0; - nMode |= (nUserMode & REQ_MODE_SB_IGN_ALL_UU )? CMODE_SB_IGN_ALL_UU : 0; - } - if ( ret= AllocateCS( pCS, num_atoms, num_atoms, s[TAUT_NON].nLenCT, s[TAUT_NON].nLenCTAtOnly, - s[TAUT_NON].nLenLinearCTStereoDble, s[TAUT_NON].nMaxNumStereoBonds, - s[TAUT_NON].nLenLinearCTStereoCarb, s[TAUT_NON].nMaxNumStereoAtoms, - 0, 0, s[TAUT_NON].nLenIsotopic, nMode, pBCN ) ) { - goto exit_function; - } - *pCS2 = *pCS; - break; - case TAUT_YES: /* tautomeric */ - nMode = 0; - nMode = (s[i].nLenLinearCTTautomer == 0)? CANON_MODE_CT:CANON_MODE_TAUT; - nMode |= (bHasIsotopicAtoms && (nUserMode & REQ_MODE_ISO) )? CANON_MODE_ISO:0; - nMode |= (s[TAUT_YES].bMayHaveStereo && (nUserMode & REQ_MODE_STEREO) )? CANON_MODE_STEREO:0; - nMode |= (bHasIsotopicAtoms && s[TAUT_YES].bMayHaveStereo && (nUserMode & REQ_MODE_ISO_STEREO))? CANON_MODE_ISO_STEREO:0; - nMode |= (nUserMode & REQ_MODE_NOEQ_STEREO )? CMODE_NOEQ_STEREO : 0; - nMode |= (nUserMode & REQ_MODE_REDNDNT_STEREO)? CMODE_REDNDNT_STEREO : 0; - nMode |= (nUserMode & REQ_MODE_NO_ALT_SBONDS )? CMODE_NO_ALT_SBONDS : 0; - if ( (nMode & CANON_MODE_STEREO) == CANON_MODE_STEREO || - (nMode & CANON_MODE_ISO_STEREO) == CANON_MODE_ISO_STEREO ) { - nMode |= (nUserMode & REQ_MODE_RELATIVE_STEREO)? CMODE_RELATIVE_STEREO: 0; - nMode |= (nUserMode & REQ_MODE_RACEMIC_STEREO )? CMODE_RACEMIC_STEREO : 0; - nMode |= (nUserMode & REQ_MODE_SC_IGN_ALL_UU )? CMODE_SC_IGN_ALL_UU : 0; - nMode |= (nUserMode & REQ_MODE_SB_IGN_ALL_UU )? CMODE_SB_IGN_ALL_UU : 0; - } - if ( ret= AllocateCS( pCS, num_atoms, num_at_tg, s[TAUT_YES].nLenCT, s[TAUT_YES].nLenCTAtOnly, - s[TAUT_YES].nLenLinearCTStereoDble, s[TAUT_YES].nMaxNumStereoBonds, - s[TAUT_YES].nLenLinearCTStereoCarb, s[TAUT_YES].nMaxNumStereoAtoms, - s[TAUT_YES].nLenLinearCTTautomer, s[TAUT_YES].nLenLinearCTIsotopicTautomer, - s[TAUT_YES].nLenIsotopic, nMode, pBCN ) ) { - goto exit_function; - } - *pCS2 = *pCS; - break; - } - - - /*^^^ 2009-12-05 */ - nMode |= (nUserMode & REQ_MODE_DIFF_UU_STEREO)? REQ_MODE_DIFF_UU_STEREO : 0; - /*^^^ 2009-12-05 */ - - - /* settings */ - pCS->lNumDecreasedCT = -1; - pCS->bDoubleBondSquare = DOUBLE_BOND_NEIGH_LIST? 2:0; /* 2 => special mode */ - pCS->bIgnoreIsotopic = !((s[TAUT_NON].num_isotopic_atoms || - s[TAUT_YES].num_isotopic_atoms || - s[TAUT_YES].bHasIsotopicTautGroups) || - (nUserMode & REQ_MODE_NON_ISO) || - !(nUserMode & REQ_MODE_ISO)); - - if ( (nUserMode & REQ_MODE_NON_ISO) && !(nUserMode & REQ_MODE_ISO) ) { - pCS->bIgnoreIsotopic = 1; /* 10-04-2004 */ - } - - if ( i == TAUT_YES ) { /* tautomeric */ - pCS->t_group_info = t_group_info; /* ??? make a copy or reuse ??? */ - pCS->t_group_info->bIgnoreIsotopic = !(s[TAUT_YES].bHasIsotopicTautGroups || - (nUserMode & REQ_MODE_NON_ISO) || - !(nUserMode & REQ_MODE_ISO)); - if ( (nUserMode & REQ_MODE_NON_ISO) && !(nUserMode & REQ_MODE_ISO) ) { - pCS->t_group_info->bIgnoreIsotopic = 1; /* 10-04-2004 */ - } - } - pCS->ulTimeOutTime = pBCN->ulTimeOutTime; - /*=========== Obsolete Mode Bits (bit 0 is Least Significant Bit) =========== - * - * Mode Bits Description - * '0' c 0 Only one connection table canonicalization - * '1' C 1 Recalculate CT using fixed nSymmRank - * '2' i 1|2 Isotopic canonicalization (internal) - * '3' I 1|2|4 Isotopic canonicalization (output) - * '4' s 1|8 Stereo canonicalization - * '5' S 1|2|4|16 Stereo isotopic canonicalization - * '6' A 1|2|4|8|16 Output All - */ -#if ( TEST_RENUMB_ATOMS == 1 ) - InchiTimeGet( &ulCanonTimeStart ); -#endif - /*************************************** - The last canonicalization step - ***************************************/ - if ( pBCN ) { - /* USE_CANON2 == 1 */ - pCS->NeighList = NULL; - pCS->pBCN = pBCN; - ret = Canon_INChI( num_atoms, i?num_at_tg:num_atoms, at[i], pCS, nMode, i); - } else { - /* old way */ - pCS->NeighList = CreateNeighList( num_atoms, i?num_at_tg:num_atoms, at[i], pCS->bDoubleBondSquare, pCS->t_group_info ); - pCS->pBCN = NULL; - ret = Canon_INChI( num_atoms, i?num_at_tg:num_atoms, at[i], pCS, nMode, i); - } - - pINChI = ppINChI[i]; /* pointers to already allocated still empty InChI */ - pINChI_Aux = ppINChI_Aux[i]; - if ( ret <= 0 ) { - /***************************************/ - /* failure in Canon_INChI() */ - /***************************************/ - pINChI->nErrorCode = ret; - pINChI_Aux->nErrorCode = ret; - } else { - /***************************************/ - /* success Canon_INChI() */ - /* save canonicalization results in */ - /* pINChI and pINChI_Aux */ - /***************************************/ - pINChI->nErrorCode = 0; - pINChI_Aux->nErrorCode = 0; - pINChI->bDeleted = pINChI_Aux->bDeleted = out_norm_data[i]->bDeleted; - pINChI_Aux->nCanonFlags = pCS->nCanonFlags; - pINChI_Aux->bTautFlags = out_norm_data[i]->bTautFlags; - pINChI_Aux->bTautFlagsDone = out_norm_data[i]->bTautFlagsDone; - pINChI_Aux->bNormalizationFlags = out_norm_data[i]->bNormalizationFlags; - /* may return an error or a warning */ - ret = FillOutINChI( pINChI, pINChI_Aux, - num_atoms, i?num_at_tg:num_atoms, - i?num_removed_H_taut:num_removed_H, at[i], - out_norm_data[i]->at, pCS, i, nUserMode, - pStrErrStruct ); - if ( RETURNED_ERROR( ret ) ) { - /* failure in FillOutINChI() */ - pINChI->nErrorCode = ret; - pINChI_Aux->nErrorCode = ret; - } else { - /****************************/ - /* success in FillOutINChI() */ - /****************************/ -#if ( bRELEASE_VERSION == 0 ) - if ( pINChI->Stereo && - (pINChI->Stereo->nCompInv2Abs && !pINChI->Stereo->bTrivialInv) || - pINChI->StereoIsotopic && - (pINChI->StereoIsotopic->nCompInv2Abs && !pINChI->StereoIsotopic->bTrivialInv) ) { - bExtract |= EXTR_NON_TRIVIAL_STEREO; - } -#endif - /* mark non-tautomeric representation as having another, tautomeric representation */ - if ( pINChI_Aux && s[TAUT_YES].nLenLinearCTTautomer ) { - pINChI_Aux->bIsTautomeric = s[TAUT_YES].nLenLinearCTTautomer; - } -#if ( bRELEASE_VERSION == 0 ) - pCS->bExtract |= bExtract; - pINChI->bExtract |= pCS->bExtract; -#endif - ret2 = CheckCanonNumberingCorrectness( - num_atoms, i?num_at_tg:num_atoms, - at[i], pCS, i, pStrErrStruct ); - if ( ret2 ) { - pINChI->nErrorCode = ret2; - pINChI_Aux->nErrorCode = ret2; - ret = ret2; - } - } - } -#if ( TEST_RENUMB_ATOMS == 1 ) - ulCanonTime2 = InchiTimeElapsed( &ulCanonTimeStart ); - pINChI_Aux->ulCanonTime = ulCanonTime+ulCanonTime2; - pINChI_Aux->ulNormTime = ulNormTime; -#endif - FreeNeighList( pCS->NeighList ); - DeAllocateCS( pCS2 ); - - pINChI = NULL; /* avoid dangling pointers */ - pINChI_Aux = NULL; /* avoid dangling pointers */ - } - if ( ret == 0 ) { - ret = num_atoms; - } - /* treat the results later */ - -exit_function: - DeAllocBCN( pBCN ); - if ( at[TAUT_YES] ) - inchi_free( at[TAUT_YES] ); - if ( at[TAUT_NON] ) - inchi_free( at[TAUT_NON] ); - if ( ti_out ) { - *ti_out = *t_group_info; - } else { - free_t_group_info( t_group_info ); - } - free_t_group_info( t_group_info_orig ); - return ret; -} -#ifndef COMPILE_ANSI_ONLY /* { */ -/***************************************************************************************/ -int GetAtomOrdNbrInCanonOrd( inp_ATOM *norm_at, AT_NUMB *nAtomOrdNbr, - AT_NUMB *nOrigAtNosInCanonOrd, int num_at ) -{ - AT_NUMB *nCanonNbr, *nOrigAtNos, *nOrigAtNosOrd; - int i, ret; - - ret = 0; - - nCanonNbr = (AT_NUMB *)inchi_calloc( num_at, sizeof(nCanonNbr[0]) ); - nOrigAtNos = (AT_NUMB *)inchi_calloc( num_at, sizeof(nOrigAtNos[0]) ); - nOrigAtNosOrd = (AT_NUMB *)inchi_calloc( num_at, sizeof(nOrigAtNosOrd[0]) ); - - if ( !nCanonNbr || !nOrigAtNos || !nAtomOrdNbr || !nOrigAtNosOrd ) { - ret = CT_OUT_OF_RAM; /* */ - goto exit_function; - } - for ( i = 0; i < num_at; i ++ ) { - nCanonNbr[i] = nAtomOrdNbr[i] = nOrigAtNosOrd[i] = (AT_NUMB)i; - nOrigAtNos[i] = norm_at[i].orig_at_number; - } - /* get nCanonNbr[]: canon. numbers-1 in order of increasing original atom numbers */ - pn_RankForSort = nOrigAtNosInCanonOrd; - qsort( nCanonNbr, num_at, sizeof(nCanonNbr[0]), CompRank ); - /* get nOrigAtNosOrd[]: norm_atom ord. numbers the same order of increasing original atom numbers */ - pn_RankForSort = nOrigAtNos; - qsort( nOrigAtNosOrd, num_at, sizeof(nOrigAtNosOrd[0]), CompRank ); - /* check whether the 2 sets of origiginal atom numbers have identical elements */ - for ( i = 0; i < num_at; i ++ ) { - if ( nOrigAtNosInCanonOrd[nCanonNbr[i]] != nOrigAtNos[nOrigAtNosOrd[i]] ) { - ret = CT_RANKING_ERR; /* */ - goto exit_function; - } - } - for ( i = 0; i < num_at; i ++ ) { - nAtomOrdNbr[(int)nCanonNbr[i]] = nOrigAtNosOrd[i]; - } - -/* - pn_RankForSort = nCanonNbr; - qsort( nAtomOrdNbr, num_at, sizeof(nCanonNbr[0]), CompRank ); -*/ - -exit_function: - if ( nCanonNbr ) - inchi_free( nCanonNbr ); - if ( nOrigAtNos ) - inchi_free( nOrigAtNos ); - if ( nOrigAtNosOrd ) - inchi_free( nOrigAtNosOrd ); - return ret; -} -/***************************************************************************************/ -int FillOutCanonInfAtom(inp_ATOM *norm_at, INF_ATOM_DATA *inf_norm_at_data, int init_num_at, int bIsotopic, - INChI *pINChI, INChI_Aux *pINChI_Aux, int bAbcNumbers, INCHI_MODE nMode) -{ - int i, j, m, n, num_stereo, k, c, ret, len_str, len, atw, num_err; - int next_atom[MAX_CUMULENE_LEN+1], best_next_atom[MAX_CUMULENE_LEN+1], cur_atom; - int next_neigh[MAX_CUMULENE_LEN+1], best_next_neigh[MAX_CUMULENE_LEN+1], best_len; - int num_iso_H[NUM_H_ISOTOPES]; - char *str; - AT_NUMB g, e; - int num_at = pINChI->nNumberOfAtoms; - int nNumberOfTGroups = (pINChI->lenTautomer && pINChI->nTautomer && pINChI->nTautomer[0])? (int)pINChI->nTautomer[0] : 0; - AT_NUMB *nOrigAtNosInCanonOrd; - INChI_Stereo *Stereo; - AT_NUMB *nConstitEquNumbers; - AT_NUMB *nConstitEquTGroupNumbers; - S_CHAR *t_parity = NULL; - AT_NUMB *nNumber = NULL; - int bIncludeIsotopicH; - - AT_NUMB *nNormAtNosInCanonOrd; - int (*MakeNumber)(char*, int, const char*, int) = bAbcNumbers? MakeAbcNumber:MakeDecNumber; - int bRel= (0 != (nMode & ( REQ_MODE_RELATIVE_STEREO))); - int bRac= (0 != (nMode & ( REQ_MODE_RACEMIC_STEREO))); - int bRelRac= bRel || bRac; - int bDoDisplaySp3 = 1; - - inf_ATOM *inf_norm_at = inf_norm_at_data? inf_norm_at_data->at : NULL; - - ret = 0; - num_err = 0; - - if ( !inf_norm_at ) - return ret; - /* prepare removeable protons and H info */ - inf_norm_at_data->nNumRemovedProtons = pINChI_Aux->nNumRemovedProtons; - MakeRemovedProtonsString( pINChI_Aux->nNumRemovedProtons, pINChI_Aux->nNumRemovedIsotopicH, NULL, bIsotopic, - inf_norm_at_data->szRemovedProtons, &inf_norm_at_data->num_removed_iso_H ); - /* fill out info atom */ - if ( bIsotopic && !(pINChI->nNumberOfIsotopicAtoms || pINChI->nNumberOfIsotopicTGroups || - pINChI->nPossibleLocationsOfIsotopicH && pINChI->nPossibleLocationsOfIsotopicH[0]>1) ) - bIsotopic = 0; - - Stereo = bIsotopic? pINChI->StereoIsotopic : - pINChI->Stereo; - bDoDisplaySp3 = (NULL != Stereo) && (Stereo->nNumberOfStereoCenters > 0); - -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - if ( bDoDisplaySp3 && bRelRac && Stereo->nNumberOfStereoCenters < 2 && - (Stereo->nCompInv2Abs || ATOM_PARITY_ILL_DEF(Stereo->t_parity[0]) ) ) { - bDoDisplaySp3 = 0; - if ( Stereo->nCompInv2Abs ) { - inf_norm_at_data->StereoFlags |= bRel? INF_STEREO_REL : bRac? INF_STEREO_RAC : 0; - } - } -#endif - /* flag has stereo */ - if ( (NULL != Stereo) && (bDoDisplaySp3 || Stereo->nNumberOfStereoBonds > 0) ) { - inf_norm_at_data->StereoFlags |= INF_STEREO; - } - - /* - if ( bDoDisplaySp3 && bRelRac && Stereo->nNumberOfStereoCenters < 2 && - (Stereo->nCompInv2Abs || ATOM_PARITY_ILL_DEF(Stereo->t_parity[0]) ) ) { - bDoDisplaySp3 = 0; - } - */ - if ( bDoDisplaySp3 && Stereo->nCompInv2Abs ) { - /* inversion changes stereo */ - if ( bRel ) { - inf_norm_at_data->StereoFlags |= INF_STEREO_REL; - } else - if ( bRac ) { - inf_norm_at_data->StereoFlags |= INF_STEREO_RAC; - } else { - inf_norm_at_data->StereoFlags |= INF_STEREO_ABS; - } - if ( bRelRac ) { - inf_norm_at_data->StereoFlags |= (Stereo->nCompInv2Abs > 0)? INF_STEREO_NORM : INF_STEREO_INV; - } - } - if ( bDoDisplaySp3 && Stereo->nCompInv2Abs < 0 && !bRelRac ) { - /* display Inv stereo which is Absolute Stereo */ - nNumber = Stereo->nNumberInv; - t_parity = Stereo->t_parityInv; - nOrigAtNosInCanonOrd = bIsotopic? pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv : - pINChI_Aux->nOrigAtNosInCanonOrdInv; - } else { - /* display Inv stereo which is Absolute Stereo */ - if ( bDoDisplaySp3 ) { - nNumber = Stereo->nNumber; - t_parity = Stereo->t_parity; - } - nOrigAtNosInCanonOrd = bIsotopic? pINChI_Aux->nIsotopicOrigAtNosInCanonOrd : - pINChI_Aux->nOrigAtNosInCanonOrd; - } - - nConstitEquNumbers = bIsotopic? pINChI_Aux->nConstitEquIsotopicNumbers : - pINChI_Aux->nConstitEquNumbers; - nConstitEquTGroupNumbers = bIsotopic? pINChI_Aux->nConstitEquIsotopicTGroupNumbers : - pINChI_Aux->nConstitEquTGroupNumbers; - memset( inf_norm_at, 0, init_num_at*sizeof(inf_norm_at[0]) ); - - /* obtain norm_at[] atom numbers (from zero) in order of canonical numbers */ - nNormAtNosInCanonOrd = (AT_NUMB *)inchi_calloc( num_at, sizeof(nNormAtNosInCanonOrd[0]) ); - if ( ret = GetAtomOrdNbrInCanonOrd( norm_at, nNormAtNosInCanonOrd, nOrigAtNosInCanonOrd, num_at ) ) { - goto exit_function; - } - - /* atom canonical and equivalence numbers > 0 */ - for ( i = 0; i < num_at; i ++ ) { - j = (int)nNormAtNosInCanonOrd[i]; - if ( j < 0 || j >= num_at ) - continue; - inf_norm_at[j].nCanonNbr = (AT_NUMB)(i+1); - inf_norm_at[j].nCanonEquNbr = nConstitEquNumbers[i]; -#ifdef DISPLAY_DEBUG_DATA - inf_norm_at[j].nDebugData = 0; -#if ( DISPLAY_DEBUG_DATA == DISPLAY_DEBUG_DATA_C_POINT ) - inf_norm_at[j].nDebugData = norm_at[j].c_point; -#endif -#endif - } - /* tautomeric groups */ - if ( nNumberOfTGroups ) { - /* - : start from 1: bypass number of t-groups - : j is a counter within the current t-group - : g is a tautomeric group canonical number - : e is a tautomeric group equivalence - */ - for ( g = 1, i = 1; g <= nNumberOfTGroups; g ++ ) { - n = (int)pINChI->nTautomer[i] - INCHI_T_NUM_MOVABLE; /* number of atoms in t-group */ - e = nConstitEquTGroupNumbers[(int)g - 1]; - /* bypass number of hydrogen atoms, negative charges, ... */ - for ( i += INCHI_T_NUM_MOVABLE+1, j = 0; j < n && i < pINChI->lenTautomer; j ++, i ++ ) { - /* scan canonical numbers of atoms within the atom t-group */ - k = (int)nNormAtNosInCanonOrd[(int)pINChI->nTautomer[i]-1]; - inf_norm_at[k].nTautGroupCanonNbr = g; - inf_norm_at[k].nTautGroupEquNbr = e; - } - } - if ( i != pINChI->lenTautomer || g != nNumberOfTGroups+1 ) { - ret = CT_TAUCOUNT_ERR; /* */ - goto exit_function; - } - } - /* atoms that may exchange isotopic H */ - if ( bIsotopic && pINChI->nPossibleLocationsOfIsotopicH && (n = (int)pINChI->nPossibleLocationsOfIsotopicH[0]) ) { - for ( i = 1; i < n; i ++ ) { - j = (int)pINChI->nPossibleLocationsOfIsotopicH[i]; - k = (int)nNormAtNosInCanonOrd[j - 1]; - if ( !inf_norm_at[k].nTautGroupCanonNbr ) { - inf_norm_at[k].cFlags |= AT_FLAG_ISO_H_POINT; - } - } - } - -#if ( DISPLAY_RING_SYSTEMS == 1 ) - /* debug only */ - for ( j = 0; j < num_at; j ++ ) { - inf_norm_at[j].nCanonNbr = norm_at[j].nBlockSystem; - inf_norm_at[j].nCanonEquNbr = norm_at[j].nRingSystem; -#if ( USE_DISTANCES_FOR_RANKING == 1 ) - inf_norm_at[j].nTautGroupCanonNbr = norm_at[j].nDistanceFromTerminal; - inf_norm_at[j].nTautGroupEquNbr = norm_at[j].bCutVertex; -#else - inf_norm_at[j].nTautGroupCanonNbr = norm_at[j].bCutVertex; - inf_norm_at[j].nTautGroupEquNbr = 0; -#endif - } -#endif - - - - /* Write isotopic mass, chemical element symbols and hydrogens, charge, radical, canon. numbers */ - len_str = sizeof(inf_norm_at[0].at_string); - for ( i = 0; i < init_num_at; i ++ ) { - str = inf_norm_at[i].at_string; - len = 0; - bIncludeIsotopicH = bIsotopic && !inf_norm_at[i].nTautGroupCanonNbr && !(inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT); - /* isotopic mass */ - atw = 0; - if ( norm_at[i].iso_atw_diff && bIsotopic ) { - if ( norm_at[i].at_type == ATT_PROTON ) { - ; /* do not set isotopic mass of a tautomeric proton */ - } else - if ( norm_at[i].el_number == PERIODIC_NUMBER_H && norm_at[i].chem_bonds_valence == 1 && - !norm_at[i].charge && !norm_at[i].radical && !norm_at[i].num_H && - (inf_norm_at[j=(int)norm_at[i].neighbor[0]].nTautGroupCanonNbr || (inf_norm_at[j].cFlags & AT_FLAG_ISO_H_POINT) ) ) { - ; /* do not set isotopic mass of an exchangeable proton */ - } else { - atw = get_atw(norm_at[i].elname); - atw += (norm_at[i].iso_atw_diff>0)? norm_at[i].iso_atw_diff-1:norm_at[i].iso_atw_diff; - /*len += sprintf( str+len, "^%d", atw );*/ - } - } - /* element name */ - if ( norm_at[i].el_number == PERIODIC_NUMBER_H && 2 <= atw && atw <= 3 ) { - len += sprintf( str+len, "%s", atw==2? "D" : "T" ); - } else { - if ( atw ) { - len += sprintf( str+len, "^%d", atw ); - } - len += sprintf( str+len, "%s", norm_at[i].elname ); - } - /* hydrogens */ - /* find number of previuosly removed terminal hydrogen atoms because these terminal H will be displayed */ - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { - num_iso_H[j] = norm_at[i].num_iso_H[j]; - } - /* n = number of implicit H to display */ - for ( j = num_at, n = (int)norm_at[i].num_H; j < init_num_at; j ++ ) { - /* subtract number of removed terminal */ - /* H atoms from the total number of H atoms */ - if ( i == (int)norm_at[j].neighbor[0] ) { - n -= 1; /* found explicit H => decrement number of implicit H */ - m = (int)norm_at[j].iso_atw_diff-1; - if ( 0 <= m && m < NUM_H_ISOTOPES ) { - /* subtract number of removed terminal isotopic H */ - /* atoms from the total number of isotopic H atoms */ - num_iso_H[m] -= 1; - } - } - } - /* at this point n = number of implicit H to display, - num_iso_H[] contains number of implicit isotopic H among n */ - if ( bIncludeIsotopicH ) { - /* subtract number of isotopic H atoms from the total number of H atoms */ - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { - n -= num_iso_H[j]; - } - } - /* non-isotopic hydrogen atoms */ - if ( n > 1 ) { - len += sprintf( str+len, "H%d", n ); - } else - if ( n == 1 ) { - len += sprintf( str+len, "H" ); - } - /* isotopic hydrogen atoms */ - if ( bIncludeIsotopicH ) { - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { - if ( num_iso_H[j] ) { - if ( j == 0 || j != 1 && j != 2 ) { - len += sprintf( str+len, "^%dH", j+1 ); - } else { - len += sprintf( str+len, j == 1? "D" : "T" ); - } - if ( num_iso_H[j] != 1 ) { - len += sprintf( str+len, "%d", (int)num_iso_H[j] ); - } - } - } - } - if ( norm_at[i].el_number == PERIODIC_NUMBER_H && str[0] == str[1] ) { - char *q; - if ( !str[2] ) { - str[1] = '2'; /* quick fix: replace HH with H2 */ - } else - if ( isdigit( UCINT str[2] ) && (n = strtol( str+2, &q, 10 )) && !q[0] ) { - len = 1 + sprintf( str+1, "%d", n+1 ); - } - } - /* - if ( str[0] == 'H' && str[1] == 'H' && !str[2] ) { - str[1] = '2'; - } - */ - /* charge */ - if ( abs(norm_at[i].charge) > 1 ) - len += sprintf( str+len, "%+d", norm_at[i].charge ); - else - if ( abs(norm_at[i].charge) == 1 ) - len += sprintf( str+len, "%s", norm_at[i].charge>0? "+" : "-" ); - /* radical */ - if ( norm_at[i].radical ) - len += sprintf( str+len, "%s", norm_at[i].radical==RADICAL_SINGLET? ":" : - norm_at[i].radical==RADICAL_DOUBLET? "." : - norm_at[i].radical==RADICAL_TRIPLET? ".." : "?"); - } - - /* stereogenic centers */ - if ( bDoDisplaySp3 && Stereo && 0 < (num_stereo = Stereo->nNumberOfStereoCenters) ) { - for ( i = 0; i < num_stereo; i ++ ) { - j = (int)nNormAtNosInCanonOrd[(int)nNumber[i]-1]; - c = t_parity[i]; - c = c==1? '-' : c==2? '+' : c==3? 'u' : c== 4? '?' : '*'; - inf_norm_at[j].cStereoCenterParity = c; - str=inf_norm_at[j].at_string; - len = strlen(str); - if ( len + 3 < (int)sizeof(inf_norm_at[0].at_string) ) { - str[len++] = '('; - str[len++] = inf_norm_at[j].cStereoCenterParity; - str[len++] = ')'; - str[len] = '\0'; - /* mark ambuguous stereo center */ - if ( norm_at[j].bAmbiguousStereo && (c=='+' || c=='-' || c=='?') && str[0] != '!' && - len+1 < (int)sizeof(inf_norm_at[0].at_string) ) { - memmove( str+1, str, len+1 ); - str[0] = '!'; /* output the atom in red color */ - } - } - } - } - - /* stereogenic bonds */ - /* (cumulenes with odd number of double bonds are stereocenters, */ - /* and atom parity should be set) */ - if ( Stereo && 0 < (num_stereo = Stereo->nNumberOfStereoBonds) ) { - for ( i = 0; i < num_stereo; i ++ ) { - int start_at, num_eql=0, bAmbiguousStereoBond = 0; - j = (int)nNormAtNosInCanonOrd[(int)Stereo->nBondAtom1[i]-1]; - k = (int)nNormAtNosInCanonOrd[(int)Stereo->nBondAtom2[i]-1]; - start_at = j; - c = Stereo->b_parity[i]; - - c = c==1? '-' : c==2? '+' : c==3? 'u' : c== 4? '?' : '*'; - - /* mark ambuguous stereo bond atom(s) */ - if ( norm_at[j].bAmbiguousStereo && (c=='+' || c=='-' ) && - (len=strlen(str=inf_norm_at[j].at_string)+1) < (int)sizeof(inf_norm_at[0].at_string) && - str[0] != '!' ) { - memmove( str+1, str, len ); - str[0] = '!'; /* output the atom in red color */ - bAmbiguousStereoBond ++; - } - if ( norm_at[k].bAmbiguousStereo && (c=='+' || c=='-') && - (len=strlen(str=inf_norm_at[k].at_string)+1) < (int)sizeof(inf_norm_at[0].at_string) && - str[0] != '!' ) { - memmove( str+1, str, len ); - str[0] = '!'; /* output the atom in red color */ - bAmbiguousStereoBond ++; - } - - /* find the opposite atom k. */ - /* Note: since it may be a cumulene, find the shortest(best) path */ - /* to atom number k to avoid confusion in case of, for example, */ - /* 4-member aromatic rings. */ - best_len = MAX_CUMULENE_LEN+1; /* moved here from inside the cycle 1-8-2003 */ - for ( n = 0; n < norm_at[j].valence; n ++ ) { - if ( norm_at[j].bond_type[n] == BOND_SINGLE ) { - /* single bond cannot be stereogenic. */ - continue; - } - /* best_len = MAX_CUMULENE_LEN+1; */ - len = 0; /* number of bonds in cumulene - 1 */ - next_atom[len] = (int)norm_at[j].neighbor[n]; - next_neigh[len] = n; - cur_atom = j; - while ( next_atom[len] != k && len < MAX_CUMULENE_LEN && 2 == norm_at[next_atom[len]].valence ) { - next_neigh[len+1] = ((int)norm_at[next_atom[len]].neighbor[0] == cur_atom); - next_atom[len+1] = (int)norm_at[next_atom[len]].neighbor[next_neigh[len+1]]; - cur_atom = next_atom[len]; - len ++; - } - if ( next_atom[len] == k ) { - if ( len < best_len ) { - memcpy( best_next_neigh, next_neigh, sizeof(best_next_neigh) ); - memcpy( best_next_atom, next_atom, sizeof(best_next_atom) ); - best_len = len; - num_eql = 0; - if ( len == 0 ) { - break; /* path length cannot be smaller than 1 */ - } - } else - if ( len == best_len ) { - num_eql ++; - } - } - } - if ( best_len <= MAX_CUMULENE_LEN && best_next_atom[best_len] == k ) { - if ( num_eql ) { - num_err ++; /* program error; no breakpoint here */ - } - if ( best_len % 2 ) { - /* even number of bonds: chiral atom, draw parity on the cenrtal atom */ - j = best_next_atom[best_len/2]; - inf_norm_at[j].cStereoCenterParity = c; - str=inf_norm_at[j].at_string; - len = strlen(str); - if ( len + 3 < (int)sizeof(inf_norm_at[0].at_string) ) { - str[len++] = '('; - str[len++] = inf_norm_at[j].cStereoCenterParity; - str[len++] = ')'; - str[len] = '\0'; - } - } else { - /* odd number of bonds: draw parity on the central bond */ - if ( best_len == 0 ) { - /* double bond */ - j = start_at; - k = best_next_neigh[0]; - } else { - /* cumulene */ - best_len = best_len/2-1; - j = best_next_atom[best_len]; - k = best_next_neigh[best_len+1]; /* added +1 to display cumulene parity on the middle bond (6-24-2002) */ - } - /* mark "forward" bond */ - for ( m = 0; m < MAX_STEREO_BONDS && inf_norm_at[j].cStereoBondParity[m]; m ++ ) - ; - if ( m < MAX_STEREO_BONDS ) { - inf_norm_at[j].cStereoBondParity[m] = c; - inf_norm_at[j].cStereoBondNumber[m] = k; - inf_norm_at[j].cStereoBondWarning[m] = bAmbiguousStereoBond; - } else { - num_err ++; /* program error; no breakpoint here */ - } - /* mark "backward" bond */ - n = norm_at[j].neighbor[k]; - for ( k = 0; k < norm_at[n].valence && j != (int)norm_at[n].neighbor[k]; k ++ ) - ; - if ( k < norm_at[n].valence ) { - j = n; - for ( m = 0; m < MAX_STEREO_BONDS && inf_norm_at[j].cStereoBondParity[m]; m ++ ) - ; - if ( m < MAX_STEREO_BONDS ) { - inf_norm_at[j].cStereoBondParity[m] = c; - inf_norm_at[j].cStereoBondNumber[m] = k; - inf_norm_at[j].cStereoBondWarning[m] = bAmbiguousStereoBond; - } else { - num_err ++; /* program error; no breakpoint here */ - } - } else { - num_err ++; /* program error; no breakpoint here */ - } - } - } else { - num_err ++; /* program error; no breakpoint here */ - } - } - } - - for ( i = 0; i < init_num_at; i ++ ) { - /* canonical numbers */ - if ( inf_norm_at[i].nCanonNbr ) { - str = inf_norm_at[i].at_string; - len = strlen(str); - len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nCanonNbr ); - if ( inf_norm_at[i].nCanonEquNbr || inf_norm_at[i].nTautGroupCanonNbr || (inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT) ) { - if ( inf_norm_at[i].nCanonEquNbr ) { - len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nCanonEquNbr ); - } else - if ( len + 1 < len_str ) { - len += 1; - strcat( str, "/" ); - } - } - /* tautomeric groups */ - if ( inf_norm_at[i].nTautGroupCanonNbr ) { - len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nTautGroupCanonNbr ); - if ( inf_norm_at[i].nTautGroupEquNbr ) { - len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nTautGroupEquNbr ); - } - } - if ( (inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT) && len+2 <= len_str ) { - str[len++] = '/'; - str[len++] = '*'; - str[len] = '\0'; - } -#ifdef DISPLAY_DEBUG_DATA - if ( inf_norm_at[i].nDebugData ) { - len += (*MakeNumber)( str+len, len_str-len, "`", (int)inf_norm_at[i].nDebugData ); - } -#endif - } - } - - -exit_function: - - if ( nNormAtNosInCanonOrd ) - inchi_free( nNormAtNosInCanonOrd ); - - - return ret; -} -/***************************************************************************************/ -int FillOutOneCanonInfAtom(inp_ATOM *inp_norm_at, INF_ATOM_DATA *inf_norm_at_data, - AT_NUMB *pStereoFlags, int init_num_at, int offset, int offset_H, int bIsotopic, - INChI *pINChI, INChI_Aux *pINChI_Aux, int bAbcNumbers, INCHI_MODE nMode) -{ - int i, j, m, n, num_stereo, k, c, ret, len_str, len, atw, num_err; - int next_atom[MAX_CUMULENE_LEN+1], best_next_atom[MAX_CUMULENE_LEN+1], cur_atom; - int next_neigh[MAX_CUMULENE_LEN+1], best_next_neigh[MAX_CUMULENE_LEN+1], best_len, bIncludeIsotopicH; - int num_iso_H[NUM_H_ISOTOPES]; - char *str; - AT_NUMB g, e; - int num_at = pINChI->nNumberOfAtoms; - int num_H = init_num_at - num_at; /* number of removed H */ - int nNumberOfTGroups = (pINChI->lenTautomer && pINChI->nTautomer && pINChI->nTautomer[0])? (int)pINChI->nTautomer[0] : 0; - AT_NUMB *nOrigAtNosInCanonOrd; - INChI_Stereo *Stereo; - AT_NUMB *nConstitEquNumbers; - AT_NUMB *nConstitEquTGroupNumbers; - S_CHAR *t_parity = NULL; - AT_NUMB *nNumber = NULL; - - AT_NUMB *nNormAtNosInCanonOrd; - int (*MakeNumber)(char*, int, const char*, int) = bAbcNumbers? MakeAbcNumber:MakeDecNumber; - int bRel= (0 != (nMode & ( REQ_MODE_RELATIVE_STEREO))); - int bRac= (0 != (nMode & ( REQ_MODE_RACEMIC_STEREO))); - int bRelRac= bRel || bRac; - int bDoDisplaySp3 = 1; - - inf_ATOM *inf_norm_at = (inf_norm_at_data && inf_norm_at_data->at)? inf_norm_at_data->at+offset : NULL; - inp_ATOM *norm_at = inp_norm_at? inp_norm_at + offset : NULL; - inf_ATOM *inf_norm_at_H = (inf_norm_at_data && inf_norm_at_data->at)? inf_norm_at_data->at+offset_H : NULL; - inp_ATOM *norm_at_H = inp_norm_at? inp_norm_at + offset_H : NULL; - - ret = 0; - num_err = 0; - - if ( !inf_norm_at ) - return ret; - /* -- already added in FillOutCompositeCanonInfAtom() -- - if ( bIsotopic ) { - for ( i = 0, j = 0; i < NUM_H_ISOTOPES; i ++ ) { - if ( pINChI_Aux->nNumRemovedIsotopicH[i] ) { - inf_norm_at_data->num_iso_H[i] += pINChI_Aux->nNumRemovedIsotopicH[i]; - inf_norm_at_data->num_removed_iso_H ++; - } - } - } - */ - - if ( bIsotopic && !(pINChI->nNumberOfIsotopicAtoms || pINChI->nNumberOfIsotopicTGroups || - pINChI->nPossibleLocationsOfIsotopicH && pINChI->nPossibleLocationsOfIsotopicH[0]>1) ) - bIsotopic = 0; - - Stereo = bIsotopic? pINChI->StereoIsotopic : - pINChI->Stereo; - bDoDisplaySp3 = (NULL != Stereo) && (Stereo->nNumberOfStereoCenters > 0); - -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - if ( bDoDisplaySp3 && bRelRac && Stereo->nNumberOfStereoCenters < 2 && - (Stereo->nCompInv2Abs || ATOM_PARITY_ILL_DEF(Stereo->t_parity[0]) ) ) { - bDoDisplaySp3 = 0; - if ( Stereo->nCompInv2Abs ) { - inf_norm_at_data->StereoFlags |= bRel? INF_STEREO_REL : bRac? INF_STEREO_RAC : 0; - } - } -#endif - /* flag has stereo */ - if ( (NULL != Stereo) && (bDoDisplaySp3 || Stereo->nNumberOfStereoBonds > 0) ) { - (*pStereoFlags) |= INF_STEREO; - } - - /* - if ( bDoDisplaySp3 && bRelRac && Stereo->nCompInv2Abs && Stereo->nNumberOfStereoCenters < 2 ) { - bDoDisplaySp3 = 0; - } - */ - if ( bDoDisplaySp3 && Stereo->nCompInv2Abs ) { - /* inversion changes stereo */ - if ( bRel ) { - (*pStereoFlags) |= INF_STEREO_REL; - } else - if ( bRac ) { - (*pStereoFlags) |= INF_STEREO_RAC; - } else { - (*pStereoFlags) |= INF_STEREO_ABS; - } - if ( bRelRac ) { - (*pStereoFlags) |= (Stereo->nCompInv2Abs > 0)? INF_STEREO_NORM : INF_STEREO_INV; - } - } - if ( bDoDisplaySp3 && Stereo->nCompInv2Abs < 0 && !bRelRac ) { - /* display Inv stereo which is Absolute Stereo */ - nNumber = Stereo->nNumberInv; - t_parity = Stereo->t_parityInv; - nOrigAtNosInCanonOrd = bIsotopic? pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv : - pINChI_Aux->nOrigAtNosInCanonOrdInv; - } else { - /* display Output stereo which is Absolute Stereo */ - if ( bDoDisplaySp3 ) { - nNumber = Stereo->nNumber; - t_parity = Stereo->t_parity; - } - nOrigAtNosInCanonOrd = bIsotopic? pINChI_Aux->nIsotopicOrigAtNosInCanonOrd : - pINChI_Aux->nOrigAtNosInCanonOrd; - } - - nConstitEquNumbers = bIsotopic? pINChI_Aux->nConstitEquIsotopicNumbers : - pINChI_Aux->nConstitEquNumbers; - nConstitEquTGroupNumbers = bIsotopic? pINChI_Aux->nConstitEquIsotopicTGroupNumbers : - pINChI_Aux->nConstitEquTGroupNumbers; - memset( inf_norm_at, 0, num_at*sizeof(inf_norm_at[0]) ); - if ( num_H > 0 ) { - memset( inf_norm_at_H, 0, num_H*sizeof(inf_norm_at[0]) ); - } - - /* obtain norm_at[] atom numbers (from zero) in order of canonical numbers */ - nNormAtNosInCanonOrd = (AT_NUMB *)inchi_calloc( num_at, sizeof(nNormAtNosInCanonOrd[0]) ); - if ( ret = GetAtomOrdNbrInCanonOrd( norm_at, nNormAtNosInCanonOrd, nOrigAtNosInCanonOrd, num_at ) ) { - goto exit_function; - } - - /* atom canonical and equivalence numbers > 0 */ - for ( i = 0; i < num_at; i ++ ) { - j = (int)nNormAtNosInCanonOrd[i]; - if ( j < 0 || j >= num_at ) - continue; - inf_norm_at[j].nCanonNbr = (AT_NUMB)(i+1); - inf_norm_at[j].nCanonEquNbr = nConstitEquNumbers[i]; -#ifdef DISPLAY_DEBUG_DATA - inf_norm_at[j].nDebugData = 0; -#if ( DISPLAY_DEBUG_DATA == DISPLAY_DEBUG_DATA_C_POINT ) - inf_norm_at[j].nDebugData = norm_at[j].c_point; -#endif -#endif - } - /* tautomeric groups */ - if ( nNumberOfTGroups ) { - /* - : start from 1: bypass number of t-groups - : j is a counter within the current t-group - : g is a tautomeric group canonical number - : e is a tautomeric group equivalence - */ - for ( g = 1, i = 1; g <= nNumberOfTGroups; g ++ ) { - n = (int)pINChI->nTautomer[i] - INCHI_T_NUM_MOVABLE; /* number of atoms in t-group */ - e = nConstitEquTGroupNumbers[(int)g - 1]; - /* bypass number of hydrogen atoms, negative charges, ... */ - for ( i += INCHI_T_NUM_MOVABLE+1, j = 0; j < n && i < pINChI->lenTautomer; j ++, i ++ ) { - /* scan canonical numbers of atoms within the atom t-group */ - k = (int)nNormAtNosInCanonOrd[(int)pINChI->nTautomer[i]-1]; - inf_norm_at[k].nTautGroupCanonNbr = g; - inf_norm_at[k].nTautGroupEquNbr = e; - } - } - if ( i != pINChI->lenTautomer || g != nNumberOfTGroups+1 ) { - ret = CT_TAUCOUNT_ERR; /* */ - goto exit_function; - } - } - /* atoms that may exchange isotopic H */ - if ( bIsotopic && pINChI->nPossibleLocationsOfIsotopicH && (n = (int)pINChI->nPossibleLocationsOfIsotopicH[0]) ) { - for ( i = 1; i < n; i ++ ) { - j = (int)pINChI->nPossibleLocationsOfIsotopicH[i]; - k = (int)nNormAtNosInCanonOrd[j - 1]; - if ( !inf_norm_at[k].nTautGroupCanonNbr ) { - inf_norm_at[k].cFlags |= AT_FLAG_ISO_H_POINT; - } - } - } -#if ( DISPLAY_RING_SYSTEMS == 1 ) - /* debug only */ - for ( j = 0; j < num_at; j ++ ) { - inf_norm_at[j].nCanonNbr = norm_at[j].nBlockSystem; - inf_norm_at[j].nCanonEquNbr = norm_at[j].nRingSystem; -#if ( USE_DISTANCES_FOR_RANKING == 1 ) - inf_norm_at[j].nTautGroupCanonNbr = norm_at[j].nDistanceFromTerminal; - inf_norm_at[j].nTautGroupEquNbr = norm_at[j].bCutVertex; -#else - inf_norm_at[j].nTautGroupCanonNbr = norm_at[j].bCutVertex; - inf_norm_at[j].nTautGroupEquNbr = 0; -#endif - } -#endif - - - - /* Write isotopic mass, chemical element symbols and hydrogens, charge, radical, canon. numbers */ - len_str = sizeof(inf_norm_at[0].at_string); - for ( i = 0; i < init_num_at; i ++ ) { - inf_ATOM *cur_inf_norm_at = (i < num_at)? inf_norm_at+i : inf_norm_at_H+i-num_at; - inp_ATOM *cur_norm_at = (i < num_at)? norm_at +i : norm_at_H +i-num_at; - str = cur_inf_norm_at->at_string; - len = 0; - bIncludeIsotopicH = bIsotopic && (i >= num_at || !inf_norm_at[i].nTautGroupCanonNbr && !(inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT)); - /* isotopic mass */ - atw = 0; - if ( cur_norm_at->iso_atw_diff && bIsotopic ) { - if ( cur_norm_at->at_type == ATT_PROTON ) { - ; /* do not set isotopic mass of a tautomeric proton */ - } else - if ( num_at <= i && cur_norm_at->el_number == PERIODIC_NUMBER_H && cur_norm_at->chem_bonds_valence == 1 && - !cur_norm_at->charge && !cur_norm_at->radical && !cur_norm_at->num_H && - 0 <= (j=(int)cur_norm_at->neighbor[0]-offset) && j < num_at && - (inf_norm_at[j].nTautGroupCanonNbr || (inf_norm_at[j].cFlags & AT_FLAG_ISO_H_POINT) ) ) { - ; /* do not set isotopic mass of an exchangeable proton */ - } else { - atw = get_atw(cur_norm_at->elname); - atw += (cur_norm_at->iso_atw_diff>0)? cur_norm_at->iso_atw_diff-1:cur_norm_at->iso_atw_diff; - /*len += sprintf( str+len, "^%d", atw );*/ - } - } - /* element name */ - if ( cur_norm_at->el_number == PERIODIC_NUMBER_H && 2 <= atw && atw <= 3 ) { - len += sprintf( str+len, "%s", atw==2? "D" : "T" ); - } else { - if ( atw ) { - len += sprintf( str+len, "^%d", atw ); - } - len += sprintf( str+len, "%s", cur_norm_at->elname ); - } - /* hydrogens */ - /* find number of previuosly removed terminal hydrogen atoms */ - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { - num_iso_H[j] = cur_norm_at->num_iso_H[j]; - } - for ( j = 0, n = (int)cur_norm_at->num_H; j < num_H; j ++ ) { - /* subtract number of removed terminal */ - /* H atoms from the total number of H atoms */ - if ( i == (int)norm_at_H[j].neighbor[0]-offset ) { - n -= 1; - m = (int)norm_at_H[j].iso_atw_diff-1; - if ( 0 <= m && m < NUM_H_ISOTOPES ) { - /* subtract number of removed terminal isotopic */ - /* H atoms from the total number of isotopic H atoms */ - num_iso_H[m] -= 1; - } - } - } - if ( bIncludeIsotopicH ) { - /* subtract number of isotopic H atoms from the total number of H atoms */ - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { - n -= num_iso_H[j]; - } - } - /* non-isotopic hydrogen atoms */ - if ( n > 1 ) { - len += sprintf( str+len, "H%d", n ); - } else - if ( n == 1 ) { - len += sprintf( str+len, "H" ); - } - /* isotopic hydrogen atoms */ - if ( bIncludeIsotopicH ) { - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { - if ( num_iso_H[j] ) { - if ( j == 0 || j != 1 && j != 2 ) { - len += sprintf( str+len, "^%dH", j+1 ); - } else { - len += sprintf( str+len, j == 1? "D" : "T" ); - } - if ( num_iso_H[j] != 1 ) { - len += sprintf( str+len, "%d", (int)num_iso_H[j] ); - } - } - } - } - if ( cur_norm_at->el_number == PERIODIC_NUMBER_H && str[0] == str[1] ) { - char *q; - if ( !str[2] ) { - str[1] = '2'; /* quick fix: replace HH with H2 */ - } else - if ( isdigit( UCINT str[2] ) && (n = strtol( str+2, &q, 10 )) && !q[0] ) { - len = 1 + sprintf( str+1, "%d", n+1 ); - } - } - /* charge */ - if ( abs(cur_norm_at->charge) > 1 ) - len += sprintf( str+len, "%+d", cur_norm_at->charge ); - else - if ( abs(cur_norm_at->charge) == 1 ) - len += sprintf( str+len, "%s", cur_norm_at->charge>0? "+" : "-" ); - /* radical */ - if ( cur_norm_at->radical ) - len += sprintf( str+len, "%s", cur_norm_at->radical==RADICAL_SINGLET? ":" : - cur_norm_at->radical==RADICAL_DOUBLET? "." : - cur_norm_at->radical==RADICAL_TRIPLET? ".." : "?"); - } - - /* stereogenic centers */ - if ( bDoDisplaySp3 && Stereo && 0 < (num_stereo = Stereo->nNumberOfStereoCenters) ) { - for ( i = 0; i < num_stereo; i ++ ) { - j = (int)nNormAtNosInCanonOrd[(int)nNumber[i]-1]; - c = t_parity[i]; - c = c==1? '-' : c==2? '+' : c==3? 'u' : c== 4? '?' : '*'; - inf_norm_at[j].cStereoCenterParity = c; - str=inf_norm_at[j].at_string; - len = strlen(str); - if ( len + 3 < (int)sizeof(inf_norm_at[0].at_string) ) { - str[len++] = '('; - str[len++] = inf_norm_at[j].cStereoCenterParity; - str[len++] = ')'; - str[len] = '\0'; - /* mark ambuguous stereo center */ - if ( norm_at[j].bAmbiguousStereo && (c=='+' || c=='-' || c=='?') && str[0] != '!' && - len+1 < (int)sizeof(inf_norm_at[0].at_string) ) { - memmove( str+1, str, len+1 ); - str[0] = '!'; /* output the atom in red color */ - } - } - } - } - - /* stereogenic bonds */ - /* (cumulenes with odd number of double bonds are stereocenters, */ - /* and atom parity should be set) */ - if ( Stereo && 0 < (num_stereo = Stereo->nNumberOfStereoBonds) ) { - for ( i = 0; i < num_stereo; i ++ ) { - int start_at, num_eql=0, bAmbiguousStereoBond = 0; - j = (int)nNormAtNosInCanonOrd[(int)Stereo->nBondAtom1[i]-1]; - k = (int)nNormAtNosInCanonOrd[(int)Stereo->nBondAtom2[i]-1]; - start_at = j; - c = Stereo->b_parity[i]; - - c = c==1? '-' : c==2? '+' : c==3? 'u' : c== 4? '?' : '*'; - - /* mark ambuguous stereo bond atom(s) */ - if ( norm_at[j].bAmbiguousStereo && (c=='+' || c=='-' ) && - (len=strlen(str=inf_norm_at[j].at_string)+1) < (int)sizeof(inf_norm_at[0].at_string) && - str[0] != '!' ) { - memmove( str+1, str, len ); - str[0] = '!'; /* output the atom in red color */ - bAmbiguousStereoBond ++; - } - if ( norm_at[k].bAmbiguousStereo && (c=='+' || c=='-') && - (len=strlen(str=inf_norm_at[k].at_string)+1) < (int)sizeof(inf_norm_at[0].at_string) && - str[0] != '!' ) { - memmove( str+1, str, len ); - str[0] = '!'; /* output the atom in red color */ - bAmbiguousStereoBond ++; - } - - /* find the opposite atom k. */ - /* Note: since it may be a cumulene, find the shortest(best) path */ - /* to atom number k to avoid confusion in case of, for example, */ - /* 4-member aromatic rings. */ - best_len = MAX_CUMULENE_LEN+1; /* moved here from inside the cycle 1-8-2003 */ - for ( n = 0; n < norm_at[j].valence; n ++ ) { - if ( norm_at[j].bond_type[n] == BOND_SINGLE ) { - /* single bond cannot be stereogenic. */ - continue; - } - /* best_len = MAX_CUMULENE_LEN+1; */ - len = 0; /* number of bonds in cumulene - 1 */ - next_atom[len] = (int)norm_at[j].neighbor[n]-offset; - next_neigh[len] = n; - cur_atom = j; - while ( next_atom[len] != k && len < MAX_CUMULENE_LEN && 2 == norm_at[next_atom[len]].valence ) { - next_neigh[len+1] = ((int)norm_at[next_atom[len]].neighbor[0]-offset == cur_atom); - next_atom[len+1] = (int)norm_at[next_atom[len]].neighbor[next_neigh[len+1]]-offset; - cur_atom = next_atom[len]; - len ++; - } - if ( next_atom[len] == k ) { - if ( len < best_len ) { - memcpy( best_next_neigh, next_neigh, sizeof(best_next_neigh) ); - memcpy( best_next_atom, next_atom, sizeof(best_next_atom) ); - best_len = len; - num_eql = 0; - if ( len == 0 ) { - break; /* path length cannot be smaller than 1 */ - } - } else - if ( len == best_len ) { - num_eql ++; - } - } - } - if ( best_len <= MAX_CUMULENE_LEN && best_next_atom[best_len] == k ) { - if ( num_eql ) { - num_err ++; /* program error; no breakpoint here */ - } - if ( best_len % 2 ) { - /* even number of bonds: chiral atom, draw parity on the cenrtal atom */ - j = best_next_atom[best_len/2]; - inf_norm_at[j].cStereoCenterParity = c; - str=inf_norm_at[j].at_string; - len = strlen(str); - if ( len + 3 < (int)sizeof(inf_norm_at[0].at_string) ) { - str[len++] = '('; - str[len++] = inf_norm_at[j].cStereoCenterParity; - str[len++] = ')'; - str[len] = '\0'; - } - } else { - /* odd number of bonds: draw parity on the central bond */ - if ( best_len == 0 ) { - /* double bond */ - j = start_at; - k = best_next_neigh[0]; - } else { - /* cumulene */ - best_len = best_len/2-1; - j = best_next_atom[best_len]; - k = best_next_neigh[best_len+1]; /* added +1 to display cumulene parity on the middle bond (6-24-2002) */ - } - /* mark "forward" bond */ - for ( m = 0; m < MAX_STEREO_BONDS && inf_norm_at[j].cStereoBondParity[m]; m ++ ) - ; - if ( m < MAX_STEREO_BONDS ) { - inf_norm_at[j].cStereoBondParity[m] = c; - inf_norm_at[j].cStereoBondNumber[m] = k; - inf_norm_at[j].cStereoBondWarning[m] = bAmbiguousStereoBond; - } else { - num_err ++; /* program error; no breakpoint here */ - } - /* mark "backward" bond */ - n = norm_at[j].neighbor[k]-offset; - for ( k = 0; k < norm_at[n].valence && j != (int)norm_at[n].neighbor[k]-offset; k ++ ) - ; - if ( k < norm_at[n].valence ) { - j = n; - for ( m = 0; m < MAX_STEREO_BONDS && inf_norm_at[j].cStereoBondParity[m]; m ++ ) - ; - if ( m < MAX_STEREO_BONDS ) { - inf_norm_at[j].cStereoBondParity[m] = c; - inf_norm_at[j].cStereoBondNumber[m] = k; - inf_norm_at[j].cStereoBondWarning[m] = bAmbiguousStereoBond; - } else { - num_err ++; /* program error; no breakpoint here */ - } - } else { - num_err ++; /* program error; no breakpoint here */ - } - } - } else { - num_err ++; /* program error; no breakpoint here */ - } - } - } - - for ( i = 0; i < num_at; i ++ ) { - /* canonical numbers */ - if ( inf_norm_at[i].nCanonNbr ) { - str = inf_norm_at[i].at_string; - len = strlen(str); - len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nCanonNbr ); - if ( inf_norm_at[i].nCanonEquNbr || inf_norm_at[i].nTautGroupCanonNbr || (inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT) ) { - if ( inf_norm_at[i].nCanonEquNbr ) { - len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nCanonEquNbr ); - } else - if ( len + 1 < len_str ) { - len += 1; - strcat( str, "/" ); - } - } - /* tautomeric groups */ - if ( inf_norm_at[i].nTautGroupCanonNbr ) { - len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nTautGroupCanonNbr ); - if ( inf_norm_at[i].nTautGroupEquNbr ) { - len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nTautGroupEquNbr ); - } - } - if ( (inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT) && len+2 <= len_str ) { - str[len++] = '/'; - str[len++] = '*'; - str[len] = '\0'; - } -#ifdef DISPLAY_DEBUG_DATA - if ( inf_norm_at[i].nDebugData ) { - len += (*MakeNumber)( str+len, len_str-len, "`", (int)inf_norm_at[i].nDebugData ); - } -#endif - } - } - - -exit_function: - - if ( nNormAtNosInCanonOrd ) - inchi_free( nNormAtNosInCanonOrd ); - - return ret; -} - -/***************************************************************************************/ -int FillOutInputInfAtom(inp_ATOM *inp_at, INF_ATOM_DATA *inf_at_data, int init_num_at, int num_removed_H, - int bAdd_DT_to_num_H, int nNumRemovedProtons, NUM_H *nNumRemovedProtonsIsotopic, int bIsotopic, int bAbcNumbers) -{ - int i, j, m, n, ret, len_str, len, atw; - int num_iso_H[NUM_H_ISOTOPES]; - char *str; - int num_at = init_num_at - num_removed_H; - int (*MakeNumber)(char*, int, const char*, int) = MakeDecNumber; - - inf_ATOM *inf_at = inf_at_data? inf_at_data->at : NULL; - - - ret = 0; - - - if ( !inf_at ) - return ret; - - memset( inf_at, 0, init_num_at*sizeof(inf_at[0]) ); - - inf_at_data->nNumRemovedProtons = nNumRemovedProtons; - MakeRemovedProtonsString( nNumRemovedProtons, nNumRemovedProtonsIsotopic, NULL, bIsotopic, inf_at_data->szRemovedProtons, NULL ); - /* atom canonical and equivalence numbers > 0 */ - for ( i = 0; i < num_at; i ++ ) { -#if ( DISPLAY_ORIG_AT_NUMBERS == 1 ) - inf_at[i].nCanonNbr = inp_at[i].orig_at_number; -#else - inf_at[i].nCanonNbr = (AT_NUMB)(i+1); -#endif - } - /* Write isotopic mass, chemical element symbols and hydrogens, charge, radical, canon. numbers */ - len_str = sizeof(inf_at[0].at_string); - for ( i = 0; i < init_num_at; i ++ ) { - str = inf_at[i].at_string; - len = 0; - /* isotopic mass */ - atw = 0; - if ( inp_at[i].iso_atw_diff && bIsotopic ) { - atw = get_atw(inp_at[i].elname); - atw += (inp_at[i].iso_atw_diff>0)? inp_at[i].iso_atw_diff-1:inp_at[i].iso_atw_diff; - /*len += sprintf( str+len, "^%d", atw );*/ - } - /* element name */ - if ( inp_at[i].el_number == PERIODIC_NUMBER_H && 2 <= atw && atw <= 3 ) { - len += sprintf( str+len, "%s", atw==2? "D" : "T" ); - } else { - if ( atw ) { - len += sprintf( str+len, "^%d", atw ); - } - len += sprintf( str+len, "%s", inp_at[i].elname ); - } - /* hydrogens */ - /* find number of previuosly removed terminal hydrogen atoms */ - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { - num_iso_H[j] = inp_at[i].num_iso_H[j]; - } - for ( j = num_at, n = (int)inp_at[i].num_H; j < init_num_at; j ++ ) { - /* subtract number of removed terminal */ - /* H atoms from the total number of H atoms */ - if ( i == (int)inp_at[j].neighbor[0] ) { - n -= 1; - m = (int)inp_at[j].iso_atw_diff-1; - if ( 0 <= m && m < NUM_H_ISOTOPES ) { - /* subtract number of removed terminal isotopic */ - /* H atoms from the total number of isotopic H atoms */ - num_iso_H[m] -= 1; - } - } - } - if ( bIsotopic && !bAdd_DT_to_num_H ) { - /* subtract number of isotopic H atoms from the total number of H atoms */ - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { - n -= num_iso_H[j]; - } - } - /* non-isotopic hydrogen atoms */ - if ( n > 1 ) { - len += sprintf( str+len, "H%d", n ); - } else - if ( n == 1 ) { - len += sprintf( str+len, "H" ); /* fixed 12-21-2002: removed 3rd argument */ - } - if ( bIsotopic ) { - /* isotopic hydrogen atoms */ - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { - if ( num_iso_H[j] ) { - if ( j == 0 || j != 1 && j != 2 ) { - len += sprintf( str+len, "^%dH", j+1 ); - } else { - len += sprintf( str+len, j == 1? "D" : "T" ); - } - if ( num_iso_H[j] != 1 ) { - len += sprintf( str+len, "%d", (int)num_iso_H[j] ); - } - } - } - } - if ( inp_at[i].el_number == PERIODIC_NUMBER_H && str[0] == str[1] ) { - char *q; - if ( !str[2] ) { - str[1] = '2'; /* quick fix: replace HH with H2 */ - } else - if ( isdigit( UCINT str[2] ) && (n = strtol( str+2, &q, 10 )) && !q[0] ) { - len = 1 + sprintf( str+1, "%d", n+1 ); - } - } - /* - if ( str[0] == 'H' && str[1] == 'H' && !str[2] ) { - str[1] = '2'; - } - */ - /* charge */ - if ( abs(inp_at[i].charge) > 1 ) - len += sprintf( str+len, "%+d", inp_at[i].charge ); - else - if ( abs(inp_at[i].charge) == 1 ) - len += sprintf( str+len, "%s", inp_at[i].charge>0? "+" : "-" ); - /* radical */ - if ( inp_at[i].radical ) - len += sprintf( str+len, "%s", inp_at[i].radical==RADICAL_SINGLET? ":" : - inp_at[i].radical==RADICAL_DOUBLET? "." : - inp_at[i].radical==RADICAL_TRIPLET? ".." : "?"); - } - - for ( i = 0; i < init_num_at; i ++ ) { - /* canonical numbers */ - if ( inf_at[i].nCanonNbr ) { - str = inf_at[i].at_string; - len = strlen(str); - len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_at[i].nCanonNbr ); - if ( inf_at[i].nCanonEquNbr || inf_at[i].nTautGroupCanonNbr ) { - len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_at[i].nCanonEquNbr ); - } - /* tautomeric groups */ - if ( inf_at[i].nTautGroupCanonNbr ) { - len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_at[i].nTautGroupCanonNbr ); - if ( inf_at[i].nTautGroupEquNbr ) { - len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_at[i].nTautGroupEquNbr ); - } - } - } - } - ret = init_num_at; - - return ret; -} -/**********************************************************************************************/ -int FillOutInfAtom(inp_ATOM *norm_at, INF_ATOM_DATA *inf_norm_at_data, int init_num_at, int num_removed_H, - int bAdd_DT_to_num_H, int nNumRemovedProtons, NUM_H *nNumRemovedProtonsIsotopic, int bIsotopic, - INChI *pINChI, INChI_Aux *pINChI_Aux, int bAbcNumbers, INCHI_MODE nMode ) -{ - if ( norm_at && inf_norm_at_data && inf_norm_at_data->at ) { - if ( pINChI && pINChI_Aux ) { - return FillOutCanonInfAtom( norm_at, inf_norm_at_data, init_num_at, bIsotopic, pINChI, - pINChI_Aux, bAbcNumbers, nMode); - } else { - return FillOutInputInfAtom( norm_at, inf_norm_at_data, init_num_at, num_removed_H, bAdd_DT_to_num_H, - nNumRemovedProtons, nNumRemovedProtonsIsotopic, bIsotopic, bAbcNumbers); - } - } - return 0; -} -/***************************************************************************************/ -int FillOutCompositeCanonInfAtom(COMP_ATOM_DATA *composite_norm_data, INF_ATOM_DATA *inf_norm_at_data, - int bIsotopic, int bTautomeric, - PINChI2 *pINChI2, PINChI_Aux2 *pINChI_Aux2, int bAbcNumbers, INCHI_MODE nMode) -{ - int i, num_components, j, k, ret; - inp_ATOM *inp_norm_at; - INChI *pINChI; - INChI_Aux *pINChI_Aux; - int num_inp_at, num_at, num_H, offset, offset_H, next_offset, next_offset_H; - - if ( composite_norm_data && inf_norm_at_data && (bTautomeric == TAUT_INI || pINChI2 && pINChI_Aux2) ) { - composite_norm_data += bTautomeric; - inp_norm_at = composite_norm_data->at; - num_components = composite_norm_data->num_components; - offset = 0; - offset_H = composite_norm_data->num_at - composite_norm_data->num_removed_H; - if ( bTautomeric == TAUT_INI ) { - ret = FillOutInputInfAtom( composite_norm_data->at, inf_norm_at_data, composite_norm_data->num_at, - composite_norm_data->num_removed_H, 0 /*bAdd_DT_to_num_H*/, - composite_norm_data->nNumRemovedProtons, - composite_norm_data->nNumRemovedProtonsIsotopic, - bIsotopic, bAbcNumbers); - return ret; - } else { - for ( i = 0; i < num_components; i ++ ) { - j = inchi_min(bTautomeric, TAUT_YES); - /* count isotopic H on removed atoms -- isolated H(+) cations */ - inf_norm_at_data->nNumRemovedProtons += pINChI_Aux2[i][j]->nNumRemovedProtons; - if ( bIsotopic && bTautomeric == TAUT_YES ) { - for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) { - if ( pINChI_Aux2[i][j]->nNumRemovedIsotopicH[k] ) { - inf_norm_at_data->num_iso_H[k] += pINChI_Aux2[i][j]->nNumRemovedIsotopicH[k]; - inf_norm_at_data->num_removed_iso_H += pINChI_Aux2[i][j]->nNumRemovedIsotopicH[k]; - } - } - } - /* ignore deleted components */ - if ( pINChI2[i][j] && pINChI2[i][j]->bDeleted ) { - continue; - } - if ( !pINChI2[i][j] || !pINChI2[i][j]->nNumberOfAtoms ) { - j = ALT_TAUT(j); - if ( !pINChI2[i][j] || !pINChI2[i][j]->nNumberOfAtoms ) { - continue; /* error ??? */ - } - } - pINChI = pINChI2[i][j]; - pINChI_Aux = pINChI_Aux2[i][j]; - next_offset = composite_norm_data->nOffsetAtAndH[2*i]; - next_offset_H = composite_norm_data->nOffsetAtAndH[2*i+1]; - num_at = next_offset - offset; - if ( num_at <= 0 ) - continue; - num_H = next_offset_H - offset_H; - num_inp_at = num_at + num_H; - if ( num_at != pINChI->nNumberOfAtoms || num_at != pINChI_Aux->nNumberOfAtoms ) { - return 0; /* error */ - } - ret = FillOutOneCanonInfAtom(inp_norm_at, inf_norm_at_data, - inf_norm_at_data->pStereoFlags+i+1, num_inp_at, - offset, offset_H, bIsotopic, pINChI, pINChI_Aux, bAbcNumbers, nMode); - if ( ret ) - return 0; /* error */ - - inf_norm_at_data->StereoFlags |= inf_norm_at_data->pStereoFlags[i+1]; - offset = next_offset; - offset_H = next_offset_H; - } - } - MakeRemovedProtonsString( inf_norm_at_data->nNumRemovedProtons, inf_norm_at_data->num_iso_H, NULL, bIsotopic, - inf_norm_at_data->szRemovedProtons, &inf_norm_at_data->num_removed_iso_H ); - } - return 1; -} -#endif /* } ifndef COMPILE_ANSI_ONLY */ -/**********************************************************************************************/ -int CheckCanonNumberingCorrectness(int num_atoms, int num_at_tg, - sp_ATOM *at, CANON_STAT *pCS, int bTautomeric, - char *pStrErrStruct ) -{ - int i, ret=0; - AT_NUMB *pCanonOrd=NULL; - int nErrorCode = 0; - AT_NUMB *pCanonRank; /* canonical ranks of the atoms or tautomeric groups */ - AT_NUMB *pCanonRankAtoms=NULL; - - static int count=0; /* for debug only */ - count ++; - - pCanonRankAtoms = (AT_NUMB *)inchi_calloc( num_at_tg+1, sizeof(pCanonRankAtoms[0]) ); - - /********************************************************************************************** - * - * non-isotopic part - */ - pCanonOrd = pCS->nLenCanonOrdStereo > 0? pCS->nCanonOrdStereo : - pCS->nLenCanonOrd > 0? pCS->nCanonOrd : NULL; - pCanonRank = pCanonRankAtoms; - if ( pCanonOrd && pCanonRank ) { - for ( i = 0; i < num_at_tg; i ++ ) { - pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); - } - ret = UpdateFullLinearCT( num_atoms, num_at_tg, at, pCanonRank, pCanonOrd, pCS, 0 ); - if ( ret /*|| memcmp(pCS->LinearCT, pCS->LinearCT2, sizeof(AT_RANK) * pCS->nLenLinearCT )*/ ) { - nErrorCode |= WARN_FAILED_STEREO; - } - - } else { - nErrorCode |= ERR_NO_CANON_RESULTS; - goto exit_function; - } - /********************************************************************************************** - * - * isotopic part - */ - pCanonOrd = pCS->nLenCanonOrdIsotopicStereo > 0? pCS->nCanonOrdIsotopicStereo : - pCS->nLenCanonOrdIsotopic > 0? pCS->nCanonOrdIsotopic : NULL; - pCanonRank = pCanonRankAtoms; - - if ( pCanonOrd && pCanonRank ) { - for ( i = 0; i < num_at_tg; i ++ ) { - pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); - } - ret = UpdateFullLinearCT( num_atoms, num_at_tg, at, pCanonRank, pCanonOrd, pCS, 0 ); - if ( ret /*|| memcmp(pCS->LinearCT, pCS->LinearCT2, sizeof(AT_RANK) * pCS->nLenLinearCT )*/ ) { - nErrorCode |= (pCS->nLenCanonOrdIsotopicStereo? WARN_FAILED_ISOTOPIC_STEREO : WARN_FAILED_ISOTOPIC); - } - - } - -exit_function: - if ( pCanonRankAtoms ) - inchi_free( pCanonRankAtoms ); - - if ( nErrorCode ) { - return CT_CANON_ERR; - } - return 0; -} diff --git a/INCHI-1-SRC/INCHI/common/ichimake.h b/INCHI-1-SRC/INCHI/common/ichimake.h deleted file mode 100644 index c5b4d90..0000000 --- a/INCHI-1-SRC/INCHI/common/ichimake.h +++ /dev/null @@ -1,240 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHIMAKE_H__ -#define __INCHIMAKE_H__ - -/***********************************************************************/ -/* replace all ' ' delimiters with ',' */ -#define ITEM_DELIMETER "," -#define EXTRA_SPACE "" -#define COMMA_EXTRA_SPACE "," -#define LEN_EXTRA_SPACE 0 - -/**********************************************************************************************/ -/* nCtMode for output INChI */ - -#define CT_MODE_NO_ORPHANS 1 /* no orphans, that CT should have only atoms with neighbors */ -#define CT_MODE_ABC_NUMBERS 2 -#define CT_MODE_ATOM_COUNTS 4 -#define CT_MODE_PREDECESSORS 8 -#define CT_MODE_EQL_H_TOGETHER 16 -#define CT_MODE_ABC_NUM_CLOSURES 32 /* in CT_MODE_ABC_NUMBERS output AB1AC2AB instead of AB-AC-A-B */ - - -/*************** Macros for retrieving requested INChI and INChI_Aux *****************************/ -/* S->pINChI[TAUT_YES] has info: */ -#define HAS_T(S) (S->pINChI[TAUT_YES] && S->pINChI[TAUT_YES]->nNumberOfAtoms) -/* S->pINChI[TAUT_NON] has info: */ -#define HAS_N(S) (S->pINChI[TAUT_NON] && S->pINChI[TAUT_NON]->nNumberOfAtoms) - -/* S->pINChI[TAUT_YES] has tautomeric info: */ -#define HAS_TT(S) (S->pINChI[TAUT_YES] && S->pINChI[TAUT_YES]->nNumberOfAtoms && S->pINChI[TAUT_YES]->lenTautomer>0) -/* S->pINChI[TAUT_YES] has non-taitomeric info: */ -#define HAS_TN(S) (S->pINChI[TAUT_YES] && S->pINChI[TAUT_YES]->nNumberOfAtoms && !S->pINChI[TAUT_YES]->lenTautomer) -/* S->pINChI[TAUT_NON] has non-tautomeric info: */ -#define HAS_NN(S) (S->pINChI[TAUT_NON] && S->pINChI[TAUT_NON]->nNumberOfAtoms && !S->pINChI[TAUT_NON]->lenTautomer) -#define GET_II(M,S) ((M==OUT_N1)? (HAS_TN(S)? TAUT_YES : HAS_NN(S)? TAUT_NON : -1): \ - (M==OUT_T1 || M==OUT_TN)? (HAS_T(S) ? TAUT_YES : HAS_N(S) ? TAUT_NON : -1): \ - (M==OUT_NN)? (HAS_NN(S)? TAUT_NON : HAS_TN(S)? TAUT_YES : -1): \ - (M==OUT_NT)? ((HAS_TT(S) && HAS_NN(S)) ? TAUT_NON : -1) : -1) - -/*********************************/ -/* Equivalence flags definitions */ -/*********************************/ - -/* What is equal (ii = INChI_ITEM) */ -#define iiSTEREO 0x0001 /* stereo (sp2 or sp3) */ -#define iiSTEREO_INV 0x0002 /* inverted stereo (sp3) */ -#define iiNUMB 0x0004 /* numbering or inverted stereo numbering */ -#define iiEQU 0x0008 /* equivalence info */ -/* derived: - (iiSTEREO_INV | iiNUMB) = numbering of inverted stereo -*/ - -/* Additional info to what is equal (INCHI_ITEM_TYPE = iit) */ -#define iitISO 0x0010 /* Isotopic */ -#define iitNONTAUT 0x0020 /* Non-tautomeric */ -/* derived: - (iitISO | iitNONTAUT) = isotopic non-tautomeric -*/ - -/* Where is the equivalent item located (INChI_ITEM_EQUAL_TO = iiEq2) */ -#define iiEq2NONTAUT 0x0040 /* non-tautomeric */ -#define iiEq2ISO 0x0080 /* isotopic */ -#define iiEq2INV 0x0100 /* equal to inverted (stereo sp3) or to numbering of inverted stereo */ - -#define iiEmpty 0x0200 /* item is empty while in the preceding layer the item is not empty */ - -/*********************** Printing strings external declarations *******************************/ - -extern const char sCompDelim[]; - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -/**********************************************************************************************/ -int FillOutINChI( INChI *pINChI, INChI_Aux *pINChI_Aux, - int num_atoms, int num_at_tg, int num_removed_H, - sp_ATOM *at, inp_ATOM *norm_at, CANON_STAT *pCS, int bTautomeric, - INCHI_MODE nUserMode, char *pStrErrStruct ); - - - -int MakeHillFormulaString( char *szHillFormula, char *szLinearCT, int nLen_szLinearCT, int *bOverflow); - -int bHasOrigInfo( ORIG_INFO *OrigInfo, int num_atoms ); -int EqlOrigInfo( INChI_Aux *a1, INChI_Aux *a2 ); - -int MakeAbcNumber( char *szString, int nStringLen, const char *szLeadingDelim, int nValue ); -int MakeDecNumber( char *szString, int nStringLen, const char *szLeadingDelim, int nValue ); -int MakeCtStringNew( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, - S_CHAR *nNum_H, int num_atoms, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); -int MakeCtStringOld( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); -int MakeCtString( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, - S_CHAR *nNum_H, int num_atoms, /* both parameters are not used here */ - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); -int MakeTautString( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); -int MakeEquString( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); -int MakeIsoAtomString( INChI_IsotopicAtom *IsotopicAtom, int nNumberOfIsotopicAtoms, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); -int MakeIsoTautString( INChI_IsotopicTGroup *IsotopicTGroup, int nNumberOfIsotopicTGroups, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); -int MakeIsoHString( int num_iso_H[], char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); -int MakeStereoString( AT_NUMB *at1, AT_NUMB *at2, S_CHAR *parity, int bAddDelim, int nLenCT, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); -int MakeCRVString( ORIG_INFO *OrigInfo, int nLenCT, int bAddDelim, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); -int MakeMult( int mult, const char *szTailingDelim, char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); -int MakeDelim( const char *szTailingDelim, char *szLinearCT, int nLen_szLinearCT, int *bOverflow); -int MakeEqStr( const char *szTailingDelim, int mult, char *szLinearCT, int nLen_szLinearCT, int *bOverflow); -int MakeHString( int bAddDelim, S_CHAR *LinearCT, int nLenCT, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow ); -AT_NUMB *GetDfsOrder4CT( AT_NUMB *LinearCT, int nLenCT, S_CHAR *nNum_H, int num_atoms, int nCtMode ); - -const char *EquString( int EquVal ); - - -int str_HillFormula(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int num_components, int bUseMulipliers); -int str_HillFormula2(INCHI_SORT *pINChISort /* non-taut */, INCHI_SORT *pINChISort2 /* taut */, - char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int num_components, int bUseMulipliers); -int str_Connections(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int ATOM_MODE, int num_components, int bUseMulipliers); -int str_H_atoms(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int ATOM_MODE, int TAUT_MODE, - int num_components, int bUseMulipliers); -int str_Charge2(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); -int str_Sp2(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); -int str_IsoSp2(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); -int str_Sp3(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bRelRac, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); -int str_IsoSp3(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bRelRac, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); -int str_StereoAbsInv(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int num_components); -int str_IsoStereoAbsInv(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int num_components); -int str_IsoAtoms(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bAbcNumbers, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); -int str_FixedH_atoms(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int ATOM_MODE, int num_components, int bUseMulipliers); - -int str_AuxNumb(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions); -int str_AuxEqu(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); -int str_AuxTgroupEqu(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bUseMulipliers); -int str_AuxIsoTgroupEqu(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bOmitRepetitions, int bUseMulipliers); -int str_AuxInvSp3(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); -int str_AuxInvSp3Numb(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions); -int str_AuxIsoNumb(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions); -int str_AuxIsoEqu(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); -int str_AuxInvIsoSp3(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); -int str_AuxInvIsoSp3Numb(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions); -int str_AuxChargeRadVal(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bUseMulipliers); -int bin_AuxTautTrans(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, - AT_NUMB **pTrans_n, AT_NUMB **pTrans_s, int bOutType, int num_components); -int str_AuxTautTrans(AT_NUMB *nTrans_n, AT_NUMB *nTrans_s, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int TAUT_MODE, int num_components); -int CompareTautNonIsoPartOfINChI( const INChI *i1, const INChI *i2 ); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /*__INCHIMAKE_H__*/ diff --git a/INCHI-1-SRC/INCHI/common/ichimap1.c b/INCHI-1-SRC/INCHI/common/ichimap1.c deleted file mode 100644 index 62d62d7..0000000 --- a/INCHI-1-SRC/INCHI/common/ichimap1.c +++ /dev/null @@ -1,887 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - -#include "mode.h" - -#include "incomdef.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichicant.h" -#include "ichicomn.h" - -#include "ichicomp.h" - - - - -/**************************************************************************************/ -/* Check if all equivalent to cr1 possibly stereogenic atoms: */ -/* 1) have KNOWN parity, and */ -/* 2) their parities are same */ -/**************************************************************************************/ -int All_SC_Same( AT_RANK canon_rank1, /* canonical number */ - const ppAT_RANK pRankStack1, const ppAT_RANK pRankStack2, - const AT_RANK *nAtomNumberCanonFrom, - const sp_ATOM *at ) -{ - int n1 = (int)nAtomNumberCanonFrom[(int)canon_rank1-1]; - AT_RANK r1 = pRankStack1[0][n1]; - int iMax1 = (int)r1; - int i1, s1; - int bFound=0, stereo_atom_parity=-1; - - /* find one stereo atom such that canon_rank1 can be mapped on it */ - for ( i1 = 1; i1 <= iMax1 && r1 == pRankStack2[0][s1=(int)pRankStack2[1][iMax1-i1]]; i1++ ) { - if ( at[s1].stereo_bond_neighbor[0] ) { - bFound=0; /* at[s1] is not sp3-stereogenic: it belongs to a stereobond */ - break; - } else - if ( i1 == 1 ) { - stereo_atom_parity = PARITY_VAL(at[s1].stereo_atom_parity); - if ( !ATOM_PARITY_KNOWN(stereo_atom_parity) ) { - bFound=0; /* at[s1] does not have a KNOWN parity */ - break; - } - } else - if ( stereo_atom_parity != PARITY_VAL(at[s1].stereo_atom_parity) ) { - bFound=0; /* two equivalent atoms have different parities */ - break; - } - bFound ++; - } - return bFound; -} -/**************************************************************************************/ -/* get next available (not mapped yet) rank for a stereo center atom */ -int Next_SC_At_CanonRank2( AT_RANK *canon_rank1, /* 1st call input: largest canon number mapped so far or 0 */ - /* output: suggested canon. rank > than input if success */ - AT_RANK *canon_rank1_min, /* 1st call:0 next calls: first tried canon. number */ - int *bFirstTime, /* 1 at the time of the 1st call */ - S_CHAR *bAtomUsedForStereo, /* STEREO_AT_MARK if the atom has not been mapped yet */ - const ppAT_RANK pRankStack1, /* mapping ranks/sort order of atoms with canon. numbers (from) */ - const ppAT_RANK pRankStack2, /* mapping ranks/sort order of atoms with stereo (to) */ - const AT_RANK *nAtomNumberCanonFrom, /* sorted order of the canon. numbers */ - int num_atoms ) -{ - AT_RANK canon_rank1_inp = *canon_rank1; - AT_RANK cr1; /* canonical rank (canonical number) */ - AT_RANK r1; /* mapping rank */ - int n1; /* ord. number of an atom with the canon. number */ - int s1; /* ord. number of an atom with stereo */ - int i1, bFound=0; - int iMax1; - - if ( canon_rank1_inp < *canon_rank1_min ) { - canon_rank1_inp = *canon_rank1_min; - } else - if ( canon_rank1_inp < 1 ) { - canon_rank1_inp = 1; - } else { - canon_rank1_inp ++; /* next canonical rank */ - } - cr1 = canon_rank1_inp; - - while ( (int) cr1 <= num_atoms ) { - n1 = (int)nAtomNumberCanonFrom[(int)cr1-1]; /* atom1 (which has canon. rank cr1) ord. number */ - iMax1 = (int)(r1 = pRankStack1[0][n1]); /* mapping rank of atom1 */ - /* find atoms "to" to which the canon. number can be mapped; they have mapping rank r1, number s1 */ - for ( i1 = 1; i1 <= iMax1 && r1 == pRankStack2[0][s1=(int)pRankStack2[1][iMax1-i1]]; i1++ ) { - /* looking for a stereo center atom that has mapping rank r1 */ - if ( bAtomUsedForStereo[s1] == STEREO_AT_MARK ) { - /* found a sterogenic atom that has not been mapped yet */ - bFound = 1; - break; - } - } - if ( bFound ) { - /* one stereogenic not mapped yet atom "to" has been found */ - if ( *bFirstTime ) { - *canon_rank1_min = cr1; - *bFirstTime = 0; - } - break; - } else { - /* a not mapped yet stereogenic atom has not found */ - /* for the mapping rank r1 defined by the canonical rank cr1; try next cr1 */ - cr1 ++; - } - } - if ( bFound ) { - /* success */ - *canon_rank1 = cr1; - return 1; - } - return 0; -} -/**********************************************************************/ -int CompareLinCtStereoDble ( AT_STEREO_DBLE *LinearCTStereoDble1, int nLenLinearCTStereoDble1, - AT_STEREO_DBLE *LinearCTStereoDble2, int nLenLinearCTStereoDble2 ) -{ - int i, num, ret = 0; - - /* compare double bonds */ - if ( LinearCTStereoDble1 && LinearCTStereoDble2 ) { - num = inchi_min(nLenLinearCTStereoDble1, nLenLinearCTStereoDble2); - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)LinearCTStereoDble1[i].at_num1 - (int)LinearCTStereoDble2[i].at_num1 ) - break; - if ( ret = (int)LinearCTStereoDble1[i].at_num2 - (int)LinearCTStereoDble2[i].at_num2 ) - break; - if ( ret = (int)LinearCTStereoDble1[i].parity - (int)LinearCTStereoDble2[i].parity ) - break; - } - if ( !ret ) { - ret = nLenLinearCTStereoDble1 - nLenLinearCTStereoDble2; - } - } else - if ( LinearCTStereoDble1 && nLenLinearCTStereoDble1 > 0 ) { - ret = 1; - } else - if ( LinearCTStereoDble2 && nLenLinearCTStereoDble2 > 0 ) { - ret = -1; - } - return ret; -} -/**********************************************************************/ -int CompareLinCtStereoCarb ( AT_STEREO_CARB *LinearCTStereoCarb1, int nLenLinearCTStereoCarb1, - AT_STEREO_CARB *LinearCTStereoCarb2, int nLenLinearCTStereoCarb2 ) -{ - int i, num, ret = 0; - - /* compare stereocenters */ - if ( LinearCTStereoCarb1 && LinearCTStereoCarb2 ) { - num = inchi_min(nLenLinearCTStereoCarb1, nLenLinearCTStereoCarb2); - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)LinearCTStereoCarb1[i].at_num - (int)LinearCTStereoCarb2[i].at_num ) - break; - if ( ret = (int)LinearCTStereoCarb1[i].parity - (int)LinearCTStereoCarb2[i].parity ) - break; - } - if ( !ret ) { - ret = nLenLinearCTStereoCarb1 - nLenLinearCTStereoCarb2; - } - } else - if ( LinearCTStereoCarb1 && nLenLinearCTStereoCarb1 > 0 ) { - ret = 1; - } else - if ( LinearCTStereoCarb2 && nLenLinearCTStereoCarb2 > 0 ) { - ret = -1; - } - - return ret; -} -/**********************************************************************/ -int CompareLinCtStereo ( AT_STEREO_DBLE *LinearCTStereoDble1, int nLenLinearCTStereoDble1, - AT_STEREO_CARB *LinearCTStereoCarb1, int nLenLinearCTStereoCarb1, - AT_STEREO_DBLE *LinearCTStereoDble2, int nLenLinearCTStereoDble2, - AT_STEREO_CARB *LinearCTStereoCarb2, int nLenLinearCTStereoCarb2 ) -{ - int ret; - - /* compare double bonds */ - ret = CompareLinCtStereoDble ( LinearCTStereoDble1, nLenLinearCTStereoDble1, - LinearCTStereoDble2, nLenLinearCTStereoDble2 ); - if ( !ret ) { - ret = CompareLinCtStereoCarb ( LinearCTStereoCarb1, nLenLinearCTStereoCarb1, - LinearCTStereoCarb2, nLenLinearCTStereoCarb2 ); - } - return ret; -} -/**************************************************************************************/ -int CompareLinCtStereoAtomToValues( AT_STEREO_CARB *LinearCTStereoCarb, - AT_RANK at_rank_canon1, U_CHAR parity ) -{ - if ( LinearCTStereoCarb->at_num CT_GREATER_THAN at_rank_canon1 ) - return 1; - if ( LinearCTStereoCarb->at_num != at_rank_canon1 ) - return -1; - if ( LinearCTStereoCarb->parity CT_GREATER_THAN parity ) - return 1; - if ( LinearCTStereoCarb->parity != parity ) - return -1; - return 0; -} -/**************************************************************************************/ -/* Find atom number from the mapping rank and return 1, or */ -/* if the mapping rank is tied and the atom number is not unique then return 0 */ -int bUniqueAtNbrFromMappingRank( AT_RANK **pRankStack, AT_RANK nAtRank, AT_NUMB *nAtNumber ) -{ - int r = (int)nAtRank-1; - AT_NUMB i = pRankStack[1][r]; - if ( nAtRank == pRankStack[0][(int)i] && - (!r || nAtRank != pRankStack[0][pRankStack[1][r-1]]) - ) { - *nAtNumber = i; - return 1; - } - return 0; -} -/**************************************************************************************/ -/* Get minimal set (class) representative and partially compress the partitioning */ -/* mcr = minimal class representative. */ -AT_RANK nGetMcr( AT_RANK *nEqArray, AT_RANK n ) -{ - AT_RANK n1, n2, mcr; /* recursive version is much shorter. */ - - n1=nEqArray[(int)n]; - if ( n == n1 ) { - return n; - } - /* 1st pass: find mcr */ - while ( n1 != (n2=nEqArray[(int)n1])) { - n1 = n2; - } - /* 2nd pass: copy mcr to each element of the set starting from nEqArray[n] */ - mcr = n1; - n1 = n; - while ( /*n1*/ mcr != (n2=nEqArray[(int)n1]) ) { - nEqArray[(int)n1]=mcr; - n1 = n2; - } - return ( mcr ); -} -/**************************************************************************************/ -/* Join 2 sets (classes) that have members n1 and n2 */ -int nJoin2Mcrs( AT_RANK *nEqArray, AT_RANK n1, AT_RANK n2 ) -{ - n1 = nGetMcr( nEqArray, n1 ); - n2 = nGetMcr( nEqArray, n2 ); - if ( n1 < n2 ) { - nEqArray[n2] = n1; - return 1; /* a change has been made */ - } - if ( n2 < n1 ) { - nEqArray[n1] = n2; - return 1; /* a change has been made */ - } - return 0; /* no changes */ -} - -/********************************************************************************* - * For all pairs of atoms that are: * - * (a) connected by a possibly stereogenic bond * - * (b) "equivalent" at this point to canon_rank1-canon_rank2 : * - * Check if they: * - * 1) are connected by a stereo bond or cumulene bonds of the same length * - * 2) have KNOWN parity, and * - * 3) their parities are same * - *********************************************************************************/ -int All_SB_Same( AT_RANK canon_rank1, AT_RANK canon_rank2, /* canonical numbers */ - const ppAT_RANK pRankStack1, const ppAT_RANK pRankStack2, - const AT_RANK *nAtomNumberCanonFrom, sp_ATOM *at ) -{ - int n1 = (int)nAtomNumberCanonFrom[(int)canon_rank1-1]; /* at1 has canon_rank1 */ - int n2 = (int)nAtomNumberCanonFrom[(int)canon_rank2-1]; /* at2 has canon_rank2 */ - AT_RANK r1 = pRankStack1[0][n1]; /* at1 mapping rank */ - AT_RANK r2 = pRankStack1[0][n2]; /* at2 mapping rank */ - AT_RANK rNeigh1, rNeigh2; - int iMax1 = (int)r1; - /* int iMax2 = (int)r2; */ - int i1, i2, s1, s2, k1, k2, m, k, num_equal; - int bNotFound=1, cumulene_len, stereo_bond_parity; - - /* at the first atom that possibly may have canon_rank1 find one stereo bond such that */ - /* canon_rank1-canon_rank2 possibly may be mapped on it */ - for ( i1 = 1; i1 <= iMax1 && r1 == pRankStack2[0][s1=(int)pRankStack2[1][iMax1-i1]]; i1++ ) { - /* at[n1] may be possible to map on at[s1] */ - for ( k1 = 0, s2= 0, bNotFound=1; - k1 < MAX_NUM_STEREO_BONDS && (s2=(int)at[s1].stereo_bond_neighbor[k1]) && - (bNotFound = (r2 != pRankStack2[0][--s2])); k1 ++ ) - ; /* continue until the 1st at[s2] (to which at[n2] may be mapped) have been found */ - if ( !bNotFound ) { - break; /* stop at 1st found */ - } - } - if ( bNotFound ) { - return -1; /* error: no mapping exists */ - } - for ( k2 = 0, m = 0; k2 < MAX_NUM_STEREO_BONDS && (m=(int)at[s2].stereo_bond_neighbor[k2]) && m-1 != s1; k2 ++ ) - ; - if ( m-1 != s1 ) { - return -1; /* program error: stereo bond in opposite direction not found */ - } - stereo_bond_parity = at[s1].stereo_bond_parity[k1]; - if ( !PARITY_KNOWN(stereo_bond_parity) ) { - return 0; - } - cumulene_len = BOND_CHAIN_LEN(stereo_bond_parity); - rNeigh1 = pRankStack2[0][(int)at[s1].neighbor[(int)at[s1].stereo_bond_ord[k1]]]; - rNeigh2 = pRankStack2[0][(int)at[s2].neighbor[(int)at[s2].stereo_bond_ord[k2]]]; - - num_equal = 0; - /* Search among ALL neighbors because sometimes a stereo bond may be mapped on a non-stereo bond. */ - /* If is so then return 0: not all mappings are stereo-equivalent */ - for ( s1 = 1; s1 <= iMax1 && r1 == pRankStack2[0][i1=(int)pRankStack2[1][iMax1-s1]]; s1++ ) { - for ( k = 0; k < at[i1].valence; k ++ ) { - n1 = at[i1].neighbor[k]; - if ( rNeigh1 != pRankStack2[0][n1] ) { - continue; /* wrong neighbor */ - } - if ( cumulene_len ) { - int prev, next, len, j; - for ( prev=i1, len=0, next = n1; len < cumulene_len; len ++ ) { - if ( at[next].valence == 2 && !at[next].num_H ) { - j = ((int)at[next].neighbor[0] == prev); - prev = next; - next = at[next].neighbor[j]; - } else { - break; /* cannot continue */ - } - } - if ( len != cumulene_len || - r2 != pRankStack2[0][next] || - rNeigh2 != pRankStack2[0][prev] ) { - /* cumulene chain not found */ - continue; - } - i2 = next; - } else { - i2 = n1; - } - /* find if a stereogenic bond between at[i1]-at[i2] exists */ - for ( k1 = 0; k1 < MAX_NUM_STEREO_BONDS && - (m=(int)at[i1].stereo_bond_neighbor[k1]) && m-1 != i2; k1 ++ ) - ; - if ( m-1 != i2 ) { - return 0; - } - for ( k2 = 0; k2 < MAX_NUM_STEREO_BONDS && - (m=(int)at[i2].stereo_bond_neighbor[k2]) && m-1 != i1; k2 ++ ) - ; - if ( m-1 != i1 ) { - return 0; - } - if ( at[i1].stereo_bond_parity[k1] != at[i2].stereo_bond_parity[k2] ) { - return -1; /* program error */ - } - if ( stereo_bond_parity != at[i1].stereo_bond_parity[k1] ) { - return 0; - } - num_equal ++; - } - } - return num_equal; -} - -/**************************************************************************************/ -/* get min. ranks for the stereo bond atoms */ -int Next_SB_At_CanonRanks2( AT_RANK *canon_rank1, AT_RANK *canon_rank2, /* canonical numbers */ - AT_RANK *canon_rank1_min, AT_RANK *canon_rank2_min, - int *bFirstTime, S_CHAR *bAtomUsedForStereo, - const ppAT_RANK pRankStack1, const ppAT_RANK pRankStack2, - const AT_RANK *nCanonRankFrom, const AT_RANK *nAtomNumberCanonFrom, - const sp_ATOM *at, int num_atoms, int bAllene ) -{ - AT_RANK canon_rank1_inp = *canon_rank1; - AT_RANK canon_rank2_inp = *canon_rank2; - AT_RANK cr1, cr2; /* canonical ranks (canonical numbers) */ - AT_RANK r1, r2; /* mapping ranks */ - int n1, n2; /* ord. numbers of atoms with stereo */ - int s1, s2; /* ord. numbers of atoms with canon. numbers */ - int i1, i2, k, m; - int iMax1, iMax2; - - if ( canon_rank1_inp < *canon_rank1_min || - canon_rank1_inp == *canon_rank1_min && - canon_rank2_inp < *canon_rank2_min ) { - - canon_rank1_inp = *canon_rank1_min; - canon_rank2_inp = *canon_rank2_min; - } else - if ( canon_rank1_inp < 2 ) { - canon_rank1_inp = 2; - canon_rank2_inp = 0; - } - cr1 = canon_rank1_inp; - cr2 = num_atoms; /* initialize. 1/8/2002 */ - while ( (int) cr1 <= num_atoms ) { - cr2 = cr1; - n1 = (int)nAtomNumberCanonFrom[(int)cr1-1]; /* atom1=at[n1] (which has canon. rank) ord. number */ - iMax1 = (int)(r1 = pRankStack1[0][n1]); /* mapping rank of atom1 */ - for ( i1 = 1; i1 <= iMax1 && r1 == pRankStack2[0][s1=(int)pRankStack2[1][iMax1-i1]]; i1++ ) { - /* looking for a stereo bond atom that has mapping rank r1 */ - /* found at[s1] such that rank cr1 can be mapped on at[s1] because cr1 and s1 have equal */ - /* mapping rank = r1. Check at[s1] stereo bonds */ - if ( bAtomUsedForStereo[s1] && bAtomUsedForStereo[s1] < STEREO_AT_MARK ) { - for ( k = 0, s2 = 0; k < MAX_NUM_STEREO_BONDS && (s2=(int)at[s1].stereo_bond_neighbor[k]); k ++ ) { - /* stereo bond at[s1]-at[s2] has been found */ - if ( bAtomUsedForStereo[--s2] ) { - /* stereo bonds have not been mapped. however, this check is not needed */ - int cumulene_len = BOND_CHAIN_LEN(at[s1].stereo_bond_parity[k]); - if ( cumulene_len%2 && !bAllene || /* 09-26-2003 */ - !(cumulene_len%2) && bAllene ) { /* 08-17-2003 Fix05 */ - continue; - } - iMax2 = (int)(r2 = pRankStack2[0][s2]); /* mapping rank of atom2 */ - /* Go back to canonical ranks and find an atom that has mapping rank r2 */ - /* and is connected to the atom with canonical rank cr1 (possibly by cumulene chain) */ - /* These cr1-cr2 canon. ranks possibly can be mapped on at[s1]-at[s2] stereo bond */ - for ( i2 = 1; i2 <= iMax2 && r2 == pRankStack1[0][n2=(int)pRankStack1[1][iMax2-i2]]; i2++ ) { - if ( cumulene_len ) { - int prev, next, len, j; - for ( m = 0; m < at[n1].valence; m ++ ) { - for ( prev=n1, len=0, next = (int)at[n1].neighbor[m]; len < cumulene_len; len ++ ) { - if ( at[next].valence == 2 && !at[next].num_H ) { - j = ((int)at[next].neighbor[0] == prev); - prev = next; - next = at[next].neighbor[j]; - } else { - break; /* cannot continue */ - } - } - if ( len == cumulene_len && n2 == next ) { - break; - } - } - } else { - for ( m = 0; m < at[n1].valence && n2 != (int)at[n1].neighbor[m]; m ++ ) - ; - } - if ( m < at[n1].valence && - nCanonRankFrom[n2] < cr2 && - nCanonRankFrom[n2] > canon_rank2_inp ) { - - cr2 = nCanonRankFrom[n2]; /* found a candidate for cr2 */ - } - } - } - } - } - } - if ( cr2 >= cr1 ) { - /* not found for this r1 */ - cr1 ++; - canon_rank2_inp = 0; - } else { - /* found cr2 < cr1 */ - if ( *bFirstTime ) { - *canon_rank1_min = cr1; - *canon_rank2_min = cr2; - *bFirstTime = 0; - } - break; - } - } - if ( cr1 > cr2 && cr1 <= num_atoms ) { - /* success */ - *canon_rank1 = cr1; - *canon_rank2 = cr2; - return 1; - } - return 0; -} -/******************************************************************************************/ -int NextStereoParity2Test( int *stereo_bond_parity, int *sb_parity_calc, - int nNumBest, int nNumWorse, int nNumUnkn, int nNumUndf, int nNumCalc, - int vABParityUnknown) -{ - /* sequence of (stereo_bond_parity, sb_parity_calc) pairs: - - (BEST_PARITY, BEST_PARITY) - | - (BEST_PARITY, WORSE_PARITY) - | - (WORSE_PARITY, WORSE_PARITY) (BEST_PARITY, 0) - \___________________________________________/ - | - (WORSE_PARITY, 0) - | - (AB_PARITY_UNKN, 0) - | - (AB_PARITY_UNDF, 0) - | - - Meaning: - stereo_bond_parity is the parity we are looking for - stereo_bond_parity==sb_parity_calc => parity to be calculated from canonical numbers - stereo_bond_parity!=sb_parity_calc => parity is already known - */ -get_next_parity: - switch ( *stereo_bond_parity ) { - case BEST_PARITY: - switch ( *sb_parity_calc ) { - case 0: /* BEST_PARITY(known) : (BEST_PARITY, 0) -> */ - *stereo_bond_parity = WORSE_PARITY; /* WORSE_PARITY(known): (WORSE_PARITY, 0) */ - if ( !nNumWorse ) { - goto get_next_parity; - } - break; - case BEST_PARITY: /* BEST_PARITY(calc) : (BEST_PARITY, BEST_PARITY) -> */ - *sb_parity_calc = WORSE_PARITY; /* BEST_PARITY(known): (BEST_PARITY, WORSE_PARITY) */ - if ( !nNumBest ) { - goto get_next_parity; - } - break; - case WORSE_PARITY: /* BEST_PARITY(known): (BEST_PARITY, WORSE_PARITY)-> */ - *stereo_bond_parity = WORSE_PARITY; /* WORSE_PARITY(calc): (WORSE_PARITY,WORSE_PARITY) */ - if ( !nNumCalc ) { /* added 12-17-2003 */ - goto get_next_parity; - } - break; - } - break; - case WORSE_PARITY: - switch ( *sb_parity_calc ) { - case 0: /* WORSE_PARITY(known) : (WORSE_PARITY, 0) -> */ - *stereo_bond_parity = vABParityUnknown /* AB_PARITY_UNKN */;/* AB_PARITY_UNKN(known): (AB_PARITY_UNKN, 0) */ - if ( !nNumUnkn ) { - goto get_next_parity; - } - break; - case BEST_PARITY: /* error */ - return CT_STEREOCOUNT_ERR; /* */ - case WORSE_PARITY: /* WORSE_PARITY(calc) : (WORSE_PARITY,WORSE_PARITY)-> */ - *sb_parity_calc = 0; /* WORSE_PARITY(known): (WORSE_PARITY, 0) */ - if ( !nNumWorse ) { - goto get_next_parity; - } - break; - } - break; - - case AB_PARITY_UNKN: /* AB_PARITY_UNKN(known): (AB_PARITY_UNKN, 0) -> */ - if ( *sb_parity_calc ) /* error */ - { - return CT_STEREOCOUNT_ERR; /* */ - } - *stereo_bond_parity = AB_PARITY_UNDF; /* AB_PARITY_UNDF(known): (AB_PARITY_UNDF, 0) */ - if ( !nNumUndf ) - { - return 1; /*goto next_canon_ranks;*/ - } - break; - - case AB_PARITY_UNDF: /* AB_PARITY_UNDF(known): (AB_PARITY_UNDF, 0) -> */ - if ( *sb_parity_calc ) { /* error */ - return CT_STEREOCOUNT_ERR; /* */ - } - return 1; /*goto next_canon_ranks;*/ /* next canon ranks */ - } - return 0; -} -/**************************************************************************************/ -int CompareLinCtStereoDoubleToValues( AT_STEREO_DBLE *LinearCTStereoDble, - AT_RANK at_rank_canon1, AT_RANK at_rank_canon2, U_CHAR bond_parity ) -{ - if ( LinearCTStereoDble->at_num1 CT_GREATER_THAN at_rank_canon1 ) - return 1; - if ( LinearCTStereoDble->at_num1 != at_rank_canon1 ) - return -1; - if ( LinearCTStereoDble->at_num2 CT_GREATER_THAN at_rank_canon2 ) - return 1; - if ( LinearCTStereoDble->at_num2 != at_rank_canon2 ) - return -1; - if ( LinearCTStereoDble->parity CT_GREATER_THAN bond_parity ) - return 1; - if ( LinearCTStereoDble->parity != bond_parity ) - return -1; - return 0; -} -/**************************************************************************************/ -/* Set for at[i]: */ -/* 0 if atom has no parity */ -/* STEREO_AT_MARK=8 if atom has stereo parity and has no stereo bonds */ -/* num_stereo_bonds number of stereogenic bonds adjacent to the atom <= 3 */ -void SetUseAtomForStereo( S_CHAR *bAtomUsedForStereo, sp_ATOM *at, int num_atoms ) -{ - int i, k; - memset( bAtomUsedForStereo, 0, sizeof( bAtomUsedForStereo[0] )*num_atoms ); - for ( i = 0; i < num_atoms; i ++ ) { - if ( at[i].parity ) { - for ( k = 0; k < MAX_NUM_STEREO_BONDS && at[i].stereo_bond_neighbor[k]; k ++ ) - ; - bAtomUsedForStereo[i] = k? k : STEREO_AT_MARK; - } - } -} - -/********************************************************************************* - * tree structure: one segment - * - * canon. rank - * at.no // orig. atom numbers on which the canon. - * // rank has been successfully mapped - * ... - * at.no // except the last at.no: it is not known if - * // it has been mapped until all atoms are mapped - * num.at+1 // number of atoms in this segment plus one canon. rank - * - *********************************************************************************/ - - - /******************************************************************************** -typedef struct tagCurTree { - AT_NUMB *tree; - int max_len; // allocated length of tree in sizeof(tree[0]) units - int cur_len; // currently used length - int incr_len; // reallocation increment -} CUR_TREE; - *********************************************************************************/ - - -/**************************************************************************************/ -int CurTreeAlloc( CUR_TREE *cur_tree, int num_atoms ) -{ - if ( cur_tree ) { - if ( cur_tree->tree && cur_tree->max_len > 0 && !(cur_tree->max_len % num_atoms) ) { - /* do not reallocate */ - cur_tree->cur_len = 0; - cur_tree->incr_len = num_atoms; - memset( cur_tree->tree, 0, cur_tree->max_len * sizeof(cur_tree->tree[0]) ); - return 0; /* ok */ - } - inchi_free( cur_tree->tree ); - memset( cur_tree, 0, sizeof(*cur_tree) ); - if ( cur_tree->tree = (AT_NUMB *)inchi_calloc( num_atoms, sizeof(cur_tree->tree[0]) ) ) { - cur_tree->incr_len = - cur_tree->max_len = num_atoms; - return 0; /* ok */ - } - } - return -1; /* error */ /* */ -} -/**************************************************************************************/ -int CurTreeReAlloc( CUR_TREE *cur_tree ) -{ - if ( cur_tree ) { - if ( cur_tree->tree && cur_tree->max_len > 0 && cur_tree->incr_len > 0 ) { - void *p = cur_tree->tree; - if ( cur_tree->tree = (AT_NUMB *)inchi_calloc( cur_tree->max_len + cur_tree->incr_len, sizeof(cur_tree->tree[0]) ) ) { - memcpy( cur_tree->tree, p, cur_tree->cur_len * sizeof(cur_tree->tree[0]) ); - inchi_free( p ); - cur_tree->max_len += cur_tree->incr_len; - return 0; /* ok */ - } - } - } - return -1; /* error */ /* */ -} -/**************************************************************************************/ -void CurTreeFree( CUR_TREE *cur_tree ) -{ - if ( cur_tree ) { - inchi_free( cur_tree->tree ); - memset( cur_tree, 0, sizeof(*cur_tree) ); - } -} -/**************************************************************************************/ -int CurTreeAddRank( CUR_TREE *cur_tree, AT_NUMB rank ) -{ - if ( cur_tree ) { - if ( cur_tree->cur_len + 2 > cur_tree->max_len ) { - if ( CurTreeReAlloc( cur_tree ) ) { - return -1; /* error */ /* */ - } - } - cur_tree->tree[cur_tree->cur_len++] = rank; - cur_tree->tree[cur_tree->cur_len++] = 1; - return 0; - } - return -1; /* error */ /* */ -} -/**************************************************************************************/ -int CurTreeIsLastRank( CUR_TREE *cur_tree, AT_NUMB rank ) -{ - if ( cur_tree && cur_tree->cur_len > 0 ) { - int rank_pos; - rank_pos = cur_tree->cur_len-1; - rank_pos -= cur_tree->tree[rank_pos]; - if ( rank_pos >= 0 ) { - return (rank == cur_tree->tree[rank_pos]); - } - } - return 0; /* not found */ -} -/**************************************************************************************/ -int CurTreeRemoveLastRankIfNoAtoms( CUR_TREE *cur_tree ) -{ - if ( cur_tree && cur_tree->tree && cur_tree->cur_len >= 2 ) { - if ( 1 == cur_tree->tree[ cur_tree->cur_len - 1 ] ) { - return CurTreeRemoveLastRank( cur_tree ); /* 0=> success, -1=>failed */ - } - return 1; /* cannot remove */ - } - return -1; /* error */ /* */ -} -/**************************************************************************************/ -int CurTreeAddAtom( CUR_TREE *cur_tree, int at_no ) -{ - if ( cur_tree ) { - if ( cur_tree->cur_len + 1 > cur_tree->max_len ) { - if ( CurTreeReAlloc( cur_tree ) ) { - return -1; /* error */ /* */ - } - } - if ( cur_tree->cur_len > 0 ) { - AT_NUMB new_len = cur_tree->tree[ --cur_tree->cur_len ] + 1; - cur_tree->tree[cur_tree->cur_len++] = (AT_NUMB)at_no; - cur_tree->tree[cur_tree->cur_len++] = new_len; - return 0; - } - } - return -1; -} -/**************************************************************************************/ -void CurTreeKeepLastAtomsOnly( CUR_TREE *cur_tree, int tpos, int shift ) -{ /* on first entry: shift = 1; other values may occur in subsequent recursion */ - /* cur_tree[cur_tree->cur_len - shift] is the length of a segment */ - /* action: remove all atoms except the last from all segments - that have length value positon to the right from tpos */ - int cur_length_pos; - if ( cur_tree && cur_tree->tree && (cur_length_pos = cur_tree->cur_len - shift) > tpos ) { - if ( cur_tree->tree[ cur_length_pos ] > 2 ) { - /* current segment contains more than 1 atom. Leave in the segment: rank, the last atom, length value */ - /* subtract (old segment length)-(new segment length) from the tree length */ - /* actual segment length including segment length value = (cur_tree->tree[cur_length_pos]+1) */ - cur_tree->cur_len -= (int)cur_tree->tree[ cur_length_pos ] - 2; - memmove( cur_tree->tree + cur_length_pos - cur_tree->tree[ cur_length_pos ] + 1, /* 1st atom pos */ - cur_tree->tree + cur_length_pos - 1, /* last atom in the current segment position */ - (shift+1)*sizeof(cur_tree->tree[0]) ); - /* (current segment length) distance from the last tree element has not changed */ - cur_tree->tree[ cur_tree->cur_len - shift] = 2; - /* add 3 to move to the previous segment length position */ - shift += 3; /* lenghth = 3 accounts for 3 currently present. segment items: - (1) the last atom, (2) rank, (3) length value */ - } else { - shift += (int)cur_tree->tree[ cur_length_pos ] + 1; /* cur_tree->cur_len - (previous segment length position) */ - } - CurTreeKeepLastAtomsOnly( cur_tree, tpos, shift ); - } -} -/**************************************************************************************/ -int CurTreeRemoveIfLastAtom( CUR_TREE *cur_tree, int at_no ) -{ - if ( cur_tree && cur_tree->tree && cur_tree->cur_len > 2 ) { - AT_NUMB len = cur_tree->tree[ cur_tree->cur_len - 1 ]; - if ( len >= 2 && (int)cur_tree->tree[ cur_tree->cur_len - 2 ] == at_no ) { - cur_tree->tree[--cur_tree->cur_len-1] = len-1; - return 0; - } - return 1; /* not found */ - } - return -1; /* error */ /* */ -} -/**************************************************************************************/ -int CurTreeGetPos( CUR_TREE *cur_tree ) -{ - if ( cur_tree ) { - return cur_tree->cur_len; - } - return -1; -} -/**************************************************************************************/ -int CurTreeSetPos( CUR_TREE *cur_tree, int len ) -{ - if ( cur_tree ) { - cur_tree->cur_len = len; - return 0; - } - return -1; -} -/**************************************************************************************/ -int CurTreeRemoveLastRank( CUR_TREE *cur_tree ) -{ - if ( cur_tree && cur_tree->cur_len > 0 ) { - cur_tree->cur_len -= cur_tree->tree[cur_tree->cur_len-1]+1; - if ( cur_tree->cur_len >= 0 ) { - return 0; - } - } - return -1; -} -/**************************************************************************************/ -/* Find if the atom is equivalent to already successfully tried current atoms */ -int CurTreeIsLastAtomEqu( CUR_TREE *cur_tree, int at_no, AT_NUMB *nSymmStereo ) -{ - if ( cur_tree && cur_tree->tree && nSymmStereo && cur_tree->cur_len > 1 ) { - AT_NUMB nEq = nSymmStereo[at_no]; - int end = cur_tree->cur_len-1; - int len = cur_tree->tree[end]-1; - for ( ; len > 0; len -- ) { - if ( nSymmStereo[(int)cur_tree->tree[end-len]] == nEq ) - return 1; - } - return 0; - } - return -1; /* error */ /* */ -} -#ifdef NEVER /* not used */ -/**************************************************************************************/ -int CurTreeRemoveLastAtom( CUR_TREE *cur_tree ) -{ - if ( cur_tree && cur_tree->tree && cur_tree->cur_len > 2 ) { - AT_NUMB len = cur_tree->tree[ --cur_tree->cur_len ]; - if ( len >= 2 ) { - cur_tree->tree[cur_tree->cur_len-1] = len-1; - return 0; - } - } - return -1; -} -/**************************************************************************************/ -int CurTreeReplaceLastRank( CUR_TREE *cur_tree, AT_NUMB rank ) -{ - if ( !CurTreeRemoveLastRank( cur_tree ) ) { - return CurTreeAddRank( cur_tree, rank ); - } - return -1; -} -/**************************************************************************************/ -/* returns cur_tree->cur_len for the block containing the rank */ -int CurTreeFindTheRankPos( CUR_TREE *cur_tree, AT_NUMB rank ) -{ - int i, k; - if ( cur_tree && cur_tree->tree && (i=cur_tree->cur_len) > 0 ) { - while( 0 <= (k = i-(int)cur_tree->tree[i-1]-1) ) { - if ( cur_tree->tree[k] == rank ) { - return i; - } - i = k; - } - } - return -1; /* error */ /* */ -} -#endif - - diff --git a/INCHI-1-SRC/INCHI/common/ichinorm.h b/INCHI-1-SRC/INCHI/common/ichinorm.h deleted file mode 100644 index ca19873..0000000 --- a/INCHI-1-SRC/INCHI/common/ichinorm.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHINORM_H__ -#define __INCHINORM_H__ - - -#include "mode.h" -#include "ichi_bns.h" - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -/* main normalization procedure */ -int mark_alt_bonds_and_taut_groups (inp_ATOM *at, inp_ATOM *at_fixed_bonds_out, int num_atoms, - T_GROUP_INFO *t_group_info, INCHI_MODE *inpbTautFlags, INCHI_MODE *inpbTautFlagsDone); - -int MarkTautomerGroups(inp_ATOM *at, int num_atoms, - T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD); -int MarkChargeGroups(inp_ATOM *at, int num_atoms, - C_GROUP_INFO *c_group_info, T_GROUP_INFO *t_group_info, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD); -int MarkSaltChargeGroups(inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, - T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD); -int MarkSaltChargeGroups2(inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, - T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD); -int MergeSaltTautGroups(inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, - T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, - struct BalancedNetworkStructure *pBNS); -int MakeIsotopicHGroup(inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, - T_GROUP_INFO *t_group_info); - -int remove_terminal_HDT(int num_atoms, inp_ATOM *at, int bFixTermHChrg); -int RemoveExcessiveImplicitH(int num_atoms, int num_removed_H, inp_ATOM *at); -int add_DT_to_num_H(int num_atoms, inp_ATOM *at); -int MarkRingSystemsInp(inp_ATOM *at, int num_atoms, int start); -int free_t_group_info(T_GROUP_INFO *t_group_info); -int make_a_copy_of_t_group_info(T_GROUP_INFO *t_group_info, - T_GROUP_INFO *t_group_info_orig); -int set_tautomer_iso_sort_keys(T_GROUP_INFO *t_group_info); -int CountTautomerGroups(sp_ATOM *at, int num_atoms, T_GROUP_INFO *t_group_info); -int CountTautomerGroupsInpAt(inp_ATOM *at, int num_atoms, T_GROUP_INFO *t_group_info); -int SortTautomerGroupsAndEndpoints(T_GROUP_INFO *t_group_info, - int num_atoms, int num_at_tg, AT_RANK *nRank); -int FillIsotopicAtLinearCT(int num_atoms, sp_ATOM* at, - const AT_RANK *nAtomNumber, - AT_ISOTOPIC *LinearCTIsotopic, - int nMaxLenLinearCTIsotopic, int *pnLenLinearCTIsotopic); -int FillTautLinearCT2(int num_atoms, int num_at_tg, int bIsoTaut, - const AT_RANK *nRank, const AT_RANK *nAtomNumber, - const AT_RANK *nSymmRank, const AT_RANK *nRankIso, - const AT_RANK *nAtomNumberIso, const AT_RANK *nSymmRankIso, - AT_TAUTOMER *LinearCTTautomer, - int nMaxLenLinearCTTautomer, int *pnLenLinearCTTautomer, - AT_ISO_TGROUP *LinearCTIsotopicTautomer, - int nMaxLenLinearCTIsotopicTautomer, - int *pnLenLinearCTIsotopicTautomer, - T_GROUP_INFO *t_group_info); - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -#endif /* __INCHINORM_H__ */ diff --git a/INCHI-1-SRC/INCHI/common/ichiparm.c b/INCHI-1-SRC/INCHI/common/ichiparm.c deleted file mode 100644 index 04218c1..0000000 --- a/INCHI-1-SRC/INCHI/common/ichiparm.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include -/* #include */ -#include -#include - -#include "mode.h" /* moved from below, suggestion by David Mosenkis */ - -#ifndef COMPILE_ANSI_ONLY -#include -#endif - -#include "inpdef.h" -#include "ichi.h" -#include "strutil.h" -#include "util.h" -#include "ichidrp.h" -#include "ichierr.h" -#include "ichimain.h" -#include "extr_ct.h" - -#ifdef TARGET_LIB_FOR_WINCHI -#include "ichi_lib.h" -#endif - -#include "ichicomp.h" - -#if ( ADD_CMLPP == 1 ) -#include "readcml.hpp" -#include "debug.h" -#endif - -#include "ichi_io.h" -#include "ichiparm.h" diff --git a/INCHI-1-SRC/INCHI/common/ichiparm.h b/INCHI-1-SRC/INCHI/common/ichiparm.h deleted file mode 100644 index 8772ed0..0000000 --- a/INCHI-1-SRC/INCHI/common/ichiparm.h +++ /dev/null @@ -1,2658 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -int DetectInputINChIFileType(FILE **inp_file, INPUT_PARMS *ip, const char *fmode); -void HelpCommandLineParmsReduced(INCHI_IOSTREAM *f); - - - - -/*****************************************************************************************/ -int ReadCommandLineParms(int argc, const char *argv[], INPUT_PARMS *ip, - char *szSdfDataValue, unsigned long *ulDisplTime, - int bReleaseVersion, INCHI_IOSTREAM *log_file) -{ - int i, k, c; - const char *q; - unsigned long ul; - int nFontSize = -9; - int nMode = 0; - int nReleaseMode = nMode | (REQ_MODE_BASIC | REQ_MODE_TAUT | REQ_MODE_ISO | REQ_MODE_STEREO); - - -#if ( MIN_SB_RING_SIZE > 0 ) - int nMinDbRinSize = MIN_SB_RING_SIZE, mdbr=0; -#endif - - /*int bNotRecognized=0;*/ - char szNameSuffix[32], szOutputPath[512]; - int bNameSuffix, bOutputPath; - int bMergeAllInputStructures; - int bDisconnectSalts = (DISCONNECT_SALTS==1); - int bDoNotAddH = 0; - - - - int bVer1Options = 1; - int bReconnectCoord = (RECONNECT_METALS==1); - int bDisconnectCoord = (DISCONNECT_METALS==1); - - - -#ifdef TARGET_LIB_FOR_WINCHI -/* int bVer1Options = 0; - int bReconnectCoord = 1; - int bDisconnectCoord = 1; */ - int is_gui = 1; - int bINChIOutputOptions = INCHI_OUT_EMBED_REC; /* embed reconnected & output full aux info */ - int bCompareComponents = CMP_COMPONENTS; -#else -/* int bVer1Options = 1; - int bReconnectCoord = (RECONNECT_METALS==1); - int bDisconnectCoord = (DISCONNECT_METALS==1); */ - int is_gui = 0; - int bINChIOutputOptions = ((EMBED_REC_METALS_INCHI==1)? INCHI_OUT_EMBED_REC : 0); - /*| INCHI_OUT_NO_AUX_INFO INCHI_OUT_SHORT_AUX_INFO*/ - int bCompareComponents = 0; -#endif - - int bDisconnectCoordChkVal = (CHECK_METAL_VALENCE == 1); - int bMovePositiveCharges = (MOVE_CHARGES==1); - int bAcidTautomerism = (DISCONNECT_SALTS==1)? (TEST_REMOVE_S_ATOMS==1? 2:1):0; - int bUnchargedAcidTaut = (CHARGED_SALTS_ONLY==0); - int bMergeSaltTGroups = (DISCONNECT_SALTS==1); - int bDisplayCompositeResults = 0; - -#define VER103_DEFAULT_MODE (REQ_MODE_TAUT | REQ_MODE_ISO | REQ_MODE_STEREO |\ - REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU) - - INCHI_MODE bVer1DefaultMode = VER103_DEFAULT_MODE; - - const char *ext[MAX_NUM_PATHS+1]; - const char *pArg; - double t; - int bRecognizedOption; - int bDisplay = 0; - int bNoStructLabels = 0; - int bOutputMolfileOnly = 0; - int bOutputMolfileDT = 0; - int bOutputMolfileSplit = 0; - int bForcedChiralFlag = 0; - -#if ( READ_INCHI_STRING == 1 ) - int bDisplayIfRestoreWarnings = 0; -#endif - -#ifdef TARGET_LIB_FOR_WINCHI - int bXml = INCHI_OUT_XML; -#else - int bXml = INCHI_OUT_PLAIN_TEXT; -#endif - int bTgFlagVariableProtons = 1; - int bTgFlagHardAddRenProtons = 1; -#ifdef STEREO_WEDGE_ONLY - int bPointedEdgeStereo = STEREO_WEDGE_ONLY; /* NEWPS TG_FLAG_POINTED_EDGE_STEREO*/ -#endif -#if ( FIX_ADJ_RAD == 1 ) - int bFixAdjacentRad = 0; -#endif - int bAddPhosphineStereo = 1; - int bAddArsineStereo = 1; - int bFixSp3bug = 1; - int bFixFB2 = 1; - int bKetoEnolTaut = 0; - int b15TautNonRing = 0; - int bStdFormat = 1; - int bHashKey = 0; - int bHashXtra1 = 0; - int bHashXtra2 = 0; - - ext[0] = ".mol"; - ext[1] = bVer1Options? ".txt" : ".ich"; - ext[2] = ".log"; - ext[3] = ".prb"; - ext[4] = ""; - -#if ( MAX_NUM_PATHS < 4 ) - #error Wrong initialization -#endif - - - - /* init table parms */ - memset(ip, 0, sizeof(*ip)); - - /* default are standard InChI generation options */ - bVer1DefaultMode &= ~REQ_MODE_BASIC; /* "FIXEDH - OFF" */ - bReconnectCoord = 0; /* "RECMET - OFF" */ - bPointedEdgeStereo = 1; /* "NEWPS" */ - ip->bFixNonUniformDraw = 1; /* "FNUD" */ - - - -#ifndef COMPILE_ANSI_ONLY - strcpy( ip->tdp.ReqShownFoundTxt[ilSHOWN], "Shown" ); - ip->dp.sdp.tdp = &ip->tdp; - ip->dp.pdp = &ip->pdp; -#endif - - memset( szNameSuffix, 0, sizeof(szNameSuffix) ); - bNameSuffix = 0; - memset(szOutputPath, 0, sizeof(szOutputPath) ); - bOutputPath = 0; - bMergeAllInputStructures = 0; - - *ulDisplTime = 0; -#ifdef TARGET_API_LIB - ip->msec_MaxTime = 0; /* milliseconds, default = unlimited in libinchi */ -#else - ip->msec_MaxTime = 60000; /* milliseconds, default = 60 sec */ -#endif - - - if ( bReleaseVersion ) - { - /* normal */ - /*ip->bINChIOutputOptions |= INCHI_OUT_PLAIN_TEXT;*/ - /*ip->bXml = 1;*/ - ip->bAbcNumbers = 0; - ip->bCtPredecessors = 0; - /* - -- testing -- - ip->bXml = 0; - ip->bAbcNumbers = 1; - */ - } - else - { - /*bXml = INCHI_OUT_PLAIN_TEXT;*/ - nReleaseMode = 0; - } - - if ( bVer1Options ) - { - bNameSuffix = 1; - szNameSuffix[0] = '\0'; - } - - - /* Parse */ - - for ( i = 1; i < argc; i ++ ) - { - - /* if ( !(bVer1Options & 1) && INCHI_OPTION_PREFX == argv[i][0] && INCHI_OPTION_PREFX != argv[i][1] ) */ - if ( is_gui && INCHI_OPTION_PREFX == argv[i][0] && INCHI_OPTION_PREFX != argv[i][1] ) - { - /*=== parsing TARGET_LIB_FOR_WINCHI GUI (and v. 0.9xx Beta)options ===*/ - - pArg = argv[i]+1; - - - /*--- Input options ---*/ - if ( !stricmp( pArg, "INPAUX" )) - { - if (INPUT_NONE == ip->nInputType) - ip->nInputType = INPUT_INCHI_PLAIN; - } - - - - else if ( INPUT_NONE == ip->nInputType && - (!memicmp( pArg, "SDF", 3 )) && - ( pArg[3] == ':' ) ) - { - k = 0; - mystrncpy( ip->szSdfDataHeader, pArg+4, MAX_SDF_HEADER+1 ); - LtrimRtrim( ip->szSdfDataHeader, &k ); - if ( k ) { - ip->pSdfLabel = ip->szSdfDataHeader; - ip->pSdfValue = szSdfDataValue; - ip->nInputType = INPUT_SDFILE; - } else { - ip->pSdfLabel = NULL; - ip->pSdfValue = NULL; - ip->nInputType = INPUT_MOLFILE; - } - } - - else if ( INPUT_NONE == ip->nInputType && !stricmp( pArg, "MOL" ) ) - { - ip->nInputType = INPUT_MOLFILE; - } - else if ( INPUT_NONE == ip->nInputType && !stricmp( pArg, "SDF" ) ) - { - ip->nInputType = INPUT_MOLFILE; - } -#if ( ADD_CMLPP == 1 ) - else if ( INPUT_NONE == ip->nInputType && !stricmp( pArg, "CML" ) ) { - /* CMLfile label */ - ip->nInputType = INPUT_CMLFILE; - } -#endif - - else if ( !memicmp( pArg, "START:", 6 ) ) - { - ip->first_struct_number = strtol(pArg+6, NULL, 10); - } - else if ( !memicmp( pArg, "END:", 4 ) ) - { - ip->last_struct_number = strtol(pArg+4, NULL, 10); - } -#ifdef BUILD_WITH_ENG_OPTIONS - else if ( !memicmp( pArg, "RSB:", 4 ) ) - { - mdbr = (int)strtol(pArg+4, NULL, 10); - } else - if ( !memicmp( pArg, "DISCONSALT:", 11 ) ) { - bDisconnectSalts = (0 != strtol(pArg+11, NULL, 10)); - } else - if ( !memicmp( pArg, "DISCONMETAL:", 12 ) ) { - bDisconnectCoord = (0 != strtol(pArg+12, NULL, 10)); - } else - if ( !memicmp( pArg, "RECONMETAL:", 11 ) ) { - bReconnectCoord = (0 != strtol(pArg+11, NULL, 10)); - } else - if ( !memicmp( pArg, "DISCONMETALCHKVAL:", 18 ) ) { - bDisconnectCoordChkVal = (0 != strtol(pArg+18, NULL, 10)); - } else - if ( !memicmp( pArg, "MOVEPOS:", 8 ) ) { - bMovePositiveCharges = (0 != strtol(pArg+8, NULL, 10)); - } else - if ( !memicmp( pArg, "MERGESALTTG:", 12 ) ) { - bMergeSaltTGroups = (0 != strtol(pArg+12, NULL, 10)); - } else - if ( !memicmp( pArg, "UNCHARGEDACIDS:", 15) ) { - bUnchargedAcidTaut = (0 != strtol(pArg+15, NULL, 16));; - } else - if ( !memicmp( pArg, "ACIDTAUT:", 9 ) ) { - bAcidTautomerism = c = (int)strtol(pArg+9, NULL, 10); - if ( 0 <= c && c <= 2 ) bAcidTautomerism = c; - /*else bNotRecognized = 2*bReleaseVersion;*/ - } -#endif - - /*--- Output options ---*/ -#if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) ) - else if ( !stricmp( pArg, "Tabbed" ) ) - { - bXml |= INCHI_OUT_TABBED_OUTPUT; - } -#endif - else if ( !stricmp( pArg, "NOLABELS" ) ) - { - bNoStructLabels = 1; - } - else if ( !stricmp( pArg, "SAVEOPT" ) ) - { - bINChIOutputOptions |= INCHI_OUT_SAVEOPT; - } - else if ( !stricmp( pArg, "AUXNONE" ) ) - { - /* no aux. info */ - bINChIOutputOptions |= INCHI_OUT_NO_AUX_INFO; /* no aux info */ - bINChIOutputOptions &= ~INCHI_OUT_SHORT_AUX_INFO; - } - - -#if ( defined(BUILD_WITH_ENG_OPTIONS) || defined(TARGET_LIB_FOR_WINCHI) ) - else if ( !stricmp( pArg, "SDFID" ) ) - { - ip->bGetSdfileId = 1; - } - else if ( !stricmp( pArg, "XML" ) ) - { - bXml &= ~INCHI_OUT_PLAIN_TEXT; - bXml |= INCHI_OUT_XML; - /*bNotRecognized = 2*bReleaseVersion;*/ - } - else if ( !stricmp( pArg, "PLAIN" ) ) - { - bXml |= INCHI_OUT_PLAIN_TEXT; - bXml &= ~INCHI_OUT_XML; - } - else if ( !stricmp( pArg, "ANNPLAIN" ) ) - { - bXml |= INCHI_OUT_PLAIN_TEXT_COMMENTS; - bXml &= ~INCHI_OUT_XML_TEXT_COMMENTS; - } - else if ( !stricmp( pArg, "ANNXML" ) ) - { - bXml |= INCHI_OUT_XML_TEXT_COMMENTS; - bXml &= ~INCHI_OUT_PLAIN_TEXT_COMMENTS; - } - else if ( !memicmp( pArg, "AUXINFO:", 8 ) && isdigit(UCINT pArg[8]) ) - { - k = strtol(pArg+8, NULL, 10); - if ( k == 0 ) - { - bINChIOutputOptions |= INCHI_OUT_NO_AUX_INFO; /* no aux info */ - bINChIOutputOptions &= ~INCHI_OUT_SHORT_AUX_INFO; - } - else if ( k == 1 ) - { - bINChIOutputOptions &= ~(INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO); /* include full aux info */ - } - else if ( k == 2 ) - { - bINChIOutputOptions &= ~INCHI_OUT_NO_AUX_INFO; /* include short aux info */ - bINChIOutputOptions |= INCHI_OUT_SHORT_AUX_INFO; - } - else - { - bINChIOutputOptions = k; /* override everything */ - } - } - else if ( !stricmp( pArg, "MERGE" ) ) - { - bMergeAllInputStructures = 1; - } - - else if ( !stricmp( pArg, "PGO" ) ) - { - ip->bSaveAllGoodStructsAsProblem = 1; - } - else if ( !stricmp( pArg, "DCR" ) ) - { - bDisplayCompositeResults = 1; - } - - else if ( !stricmp( pArg, "DSB" ) ) - { - nMode |= REQ_MODE_NO_ALT_SBONDS; - } - else if ( !stricmp( pArg, "NOHDR" ) ) - { - bNoStructLabels = 1; - } - - else if ( !stricmp( pArg, "NoVarH" ) ) - { - bTgFlagVariableProtons = 0; - } -#endif /* BUILD_WITH_ENG_OPTIONS */ - - - /*--- All modes (std and non-std InChI) structure perception options ---*/ - else if ( !stricmp( pArg, "SNON" ) ) - { - bVer1DefaultMode &= ~REQ_MODE_STEREO; - nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); - } - else if ( !stricmp( pArg, "NEWPSOFF" ) ) - { - bPointedEdgeStereo = 0; - } - else if ( !stricmp( pArg, "DONOTADDH" ) ) - { - bDoNotAddH = 1; - } - - /* Non-standard */ -#ifndef USE_STDINCHI_API - - /* Non-std InChI structure perception options */ - else if ( !stricmp( pArg, "SREL" ) ) - { - if ( nMode & REQ_MODE_RACEMIC_STEREO ) - { - nMode ^= REQ_MODE_RACEMIC_STEREO; - } - if ( nMode & REQ_MODE_CHIR_FLG_STEREO ) - { - nMode ^= REQ_MODE_CHIR_FLG_STEREO; - } - nMode |= REQ_MODE_RELATIVE_STEREO; - nMode |= REQ_MODE_STEREO; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "SRAC" ) ) - { - if ( nMode & REQ_MODE_RELATIVE_STEREO ) - { - nMode ^= REQ_MODE_RELATIVE_STEREO; - } - if ( nMode & REQ_MODE_CHIR_FLG_STEREO ) - { - nMode ^= REQ_MODE_CHIR_FLG_STEREO; - } - nMode |= REQ_MODE_RACEMIC_STEREO; - nMode |= REQ_MODE_STEREO; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "SUCF" ) ) - { - if ( nMode & REQ_MODE_RELATIVE_STEREO ) - { - nMode ^= REQ_MODE_RELATIVE_STEREO; - } - if ( nMode & REQ_MODE_RACEMIC_STEREO ) - { - nMode ^= REQ_MODE_RACEMIC_STEREO; - } - nMode |= REQ_MODE_CHIR_FLG_STEREO; /* stereo defined by the Chiral flag */ - nMode |= REQ_MODE_STEREO; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "ChiralFlagON" ) ) - { - /* used only with /SUCF */ - /* NB: do not toggle off bStdFormat! (if necessary SUCF will do) */ - bForcedChiralFlag &= ~FLAG_SET_INP_AT_NONCHIRAL; - bForcedChiralFlag |= FLAG_SET_INP_AT_CHIRAL; - } - else if ( !stricmp( pArg, "ChiralFlagOFF" ) ) - { - /* used only with /SUCF */ - /* NB: do not toggle off bStdFormat! (if necessary SUCF will do) */ - bForcedChiralFlag &= ~FLAG_SET_INP_AT_CHIRAL; - bForcedChiralFlag |= FLAG_SET_INP_AT_NONCHIRAL; - } - - /*--- Non-std InChI creation options ---*/ - - else if ( !stricmp( pArg, "SUU" ) ) - { - /* include omitted undef/unknown stereo */ - bVer1DefaultMode &= ~(REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU); - bStdFormat = 0; - } - else if ( !stricmp( pArg, "SLUUD" ) ) - { - /* make labels for unknown and undefined stereo different */ - bVer1DefaultMode |= REQ_MODE_DIFF_UU_STEREO; - bStdFormat = 0; - } - /* FixedH */ - else if ( !stricmp( pArg, "FIXEDH" ) ) - { - bVer1DefaultMode |= REQ_MODE_BASIC; /* tautomeric */ - bStdFormat = 0; - } - /* RecMet */ - else if ( !stricmp( pArg, "RECMET" ) ) - { - /* reconnect metals */ - bReconnectCoord = 1; - bStdFormat = 0; - } -#if ( KETO_ENOL_TAUT == 1 ) - else if ( !stricmp( pArg, "KET" ) ) - { - bKetoEnolTaut = 1; - bStdFormat = 0; - } -#endif -#if ( TAUT_15_NON_RING == 1 ) - else if ( !stricmp( pArg, "15T" ) ) - { - b15TautNonRing = 1; - bStdFormat = 0; - } -#endif - -#endif - /*--- Generation options ---*/ - - /* InChIKey/InChI hash */ - else if ( !stricmp( pArg, "Key" ) ) - { - bHashKey = 1; - } - else if ( !stricmp( pArg, "XHash1" ) ) - { - bHashXtra1 = 1; - } - else if ( !stricmp( pArg, "XHash2" ) ) - { - bHashXtra2 = 1; - } - - - /*--- (engineering) Undo bug/draw fixes options ---*/ -#ifdef BUILD_WITH_ENG_OPTIONS - else if ( !stricmp( pArg, "FixSp3bugOFF" ) ) - { - bFixSp3bug = 0; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "FBOFF" ) ) - { - bFixSp3bug = 0; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "FB2OFF" ) ) - { - bFixFB2 = 0; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "SPXYZOFF" ) ) - { - bAddPhosphineStereo = 0; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "SASXYZOFF" ) ) - { - bAddArsineStereo = 0; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "FNUDOFF" ) ) - { - ip->bFixNonUniformDraw = 0; - bStdFormat = 0; - } - - /*--- (hidden) Old structure-perception and InChI creation options ---*/ - /*--- (engineering) Old structure-perception and InChI creation options ---*/ - - else if ( !stricmp( pArg, "NOUUSB" ) ) - { - nMode |= REQ_MODE_SB_IGN_ALL_UU; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "NOUUSC" ) ) - { - nMode |= REQ_MODE_SC_IGN_ALL_UU; - bStdFormat = 0; - } - -#if ( FIX_ADJ_RAD == 1 ) - else if ( !stricmp( pArg, "FixRad" ) ) - { - bFixAdjacentRad = 1; - bStdFormat = 0; - } -#endif -#if ( UNDERIVATIZE == 1 ) - else if ( !stricmp( pArg, "DoDRV" ) ) - { - ip->bUnderivatize = 1; - bStdFormat = 0; - } -#endif -#if ( RING2CHAIN == 1 ) - else if ( !stricmp( pArg, "DoR2C" ) ) - { - ip->bRing2Chain = 1; - bStdFormat = 0; - } -#endif -#if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) - else if ( !stricmp( pArg, "DoneOnly" ) ) - { - ip->bIngnoreUnchanged = 1; - bStdFormat = 0; - } -#endif - else if ( !stricmp( pArg, "NoADP" ) ) - { - bTgFlagHardAddRenProtons = 0; - bStdFormat = 0; - } - else if ( !memicmp( pArg, "DISCONSALT:", 11 ) ) - { - bDisconnectSalts = (0 != strtol(pArg+11, NULL, 10)); - bStdFormat = 0; - } - else if ( !memicmp( pArg, "DISCONMETAL:", 12 ) ) - { - bDisconnectCoord = (0 != strtol(pArg+12, NULL, 10)); - bStdFormat = 0; - } - else if ( !memicmp( pArg, "RECONMETAL:", 11 ) ) - { - bReconnectCoord = (0 != strtol(pArg+11, NULL, 10)); - bStdFormat = 0; - } - else if ( !memicmp( pArg, "DISCONMETALCHKVAL:", 18 ) ) - { - bDisconnectCoordChkVal = (0 != strtol(pArg+18, NULL, 10)); - bStdFormat = 0; - } - else if ( !memicmp( pArg, "MOVEPOS:", 8 ) ) - { - bMovePositiveCharges = (0 != strtol(pArg+8, NULL, 10)); - bStdFormat = 0; - } - else if ( !memicmp( pArg, "MERGESALTTG:", 12 ) ) - { - bMergeSaltTGroups = (0 != strtol(pArg+12, NULL, 10)); - bStdFormat = 0; - } - else if ( !memicmp( pArg, "UNCHARGEDACIDS:", 15) ) - { - bUnchargedAcidTaut = (0 != strtol(pArg+15, NULL, 16)); - bStdFormat = 0; - } - else if ( !memicmp( pArg, "ACIDTAUT:", 9 ) ) - { - bAcidTautomerism = c = (int)strtol(pArg+9, NULL, 10); - if ( 0 <= c && c <= 2 ) bAcidTautomerism = c; - /*else bNotRecognized = 2*bReleaseVersion;*/ - bStdFormat = 0; - } - - /*--- (hidden) Old output and other options ---*/ - - else if ( !memicmp( pArg, "O:", 2 ) ) - { - bNameSuffix = 1; - strncpy(szNameSuffix, pArg+2, sizeof(szNameSuffix)-1); - } - else if ( !memicmp( pArg, "OP:", 3 ) ) - { - bOutputPath = 1; - strncpy(szOutputPath, pArg+3, sizeof(szOutputPath)-1); - } - else if ( !stricmp( pArg, "ALT" ) ) - { - ip->bAbcNumbers = 1; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "SCT" ) ) - { - ip->bCtPredecessors = 1; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "CMP" ) ) - { - bCompareComponents = CMP_COMPONENTS; - } - else if ( !stricmp( pArg, "CMPNONISO" ) ) - { - bCompareComponents = CMP_COMPONENTS | CMP_COMPONENTS_NONISO; - } - else if ( !stricmp( pArg, "PW" ) ) - { - ip->bSaveWarningStructsAsProblem = 1; - } - -#endif /* BUILD_WITH_ENG_OPTIONS */ - - - else - { - /*for ( k = 0; c=pArg[k]; k ++ )*/ - k = 0; - c=pArg[k]; /* prohibit multiple option concatenations, strict syntax check 2008-11-05 DT */ - { - c = toupper( c ); - switch ( c ) - { - case 'D': - bDisplay |= 1; - if ( (pArg[k+1] == 'C' || pArg[k+1] == 'c') && !pArg[k+2] ) - { - bDisplay |= 1; - k++; - ip->bDisplayEachComponentINChI = 1; - } - else if ( !pArg[k+1] ) - { - bDisplay |= 1; - } - break; - case 'W': - if ( pArg[k+1] == 'D' ) - { - /* restore Display Time functionality */ - c = 'D'; - k ++; - } - t = strtod( pArg+k+1, (char**)&q ); /* cast deliberately discards 'const' qualifier */ - if ( q > pArg+k+1 && errno == ERANGE || t < 0.0 || t*1000.0 > (double)ULONG_MAX) - { - ul = 0; - } - else - { - ul = (unsigned long)(t*1000.0); - } - if ( /*q > pArg+k &&*/ !*q ) - { - k = q - pArg - 1; /* k will be incremented by the for() cycle */ - switch( c ) - { - case 'D': - *ulDisplTime = ul; - break; - case 'W': - ip->msec_MaxTime = ul; - break; - } - } - break; - case 'F': - c = (int)strtol( pArg+k+1, (char**)&q, 10 ); /* cast deliberately discards 'const' qualifier */ - if ( q > pArg+k && !*q ) - { - k = q - pArg - 1; - if ( abs(c) > 5 ) - { - nFontSize = -c; /* font size 5 or less is too small */ - } - } - break; - default: - if ( !pArg[k+1] ) - { - switch ( c ) - { - case 'B': - nMode |= REQ_MODE_BASIC; - nReleaseMode = 0; - /*bNotRecognized = bReleaseVersion;*/ - bStdFormat = 0; - break; - case 'T': - nMode |= REQ_MODE_TAUT; - nReleaseMode = 0; - /*bNotRecognized = bReleaseVersion;*/ - break; - case 'I': - nMode |= REQ_MODE_ISO; - nReleaseMode = 0; - /*bNotRecognized = bReleaseVersion;*/ - break; - case 'N': - nMode |= REQ_MODE_NON_ISO; - nReleaseMode = 0; - bStdFormat = 0; - /*bNotRecognized = bReleaseVersion;*/ - break; - case 'S': - nMode |= REQ_MODE_STEREO; - nReleaseMode = 0; - /*bNotRecognized = bReleaseVersion;*/ - break; - case 'E': - if ( nReleaseMode & REQ_MODE_STEREO ) - { - nReleaseMode ^= REQ_MODE_STEREO; - bStdFormat = 0; - } - break; -#ifndef TARGET_LIB_FOR_WINCHI - default: - inchi_ios_eprint(log_file, "Unrecognized option: \"%c\".\n", c); - -#endif - } - } - - -#ifndef TARGET_LIB_FOR_WINCHI - else - { - inchi_ios_eprint(log_file, "Unrecognized option: \"%c\".\n", c); - } -#endif - } - /* - if ( bNotRecognized && bNotRecognized == bReleaseVersion ) { - inchi_ios_eprint(stderr, "Unrecognized option: \"%c\".\n", c); - bNotRecognized = 0; - } - */ - } - } - - - /* - if ( bNotRecognized && bNotRecognized == 2*bReleaseVersion ) - { - inchi_ios_eprint(stderr, "Unrecognized option: \"%s\".\n", argv[i]); - bNotRecognized = 0; - } - */ - - } - /*=== end of parsing TARGET_LIB_FOR_WINCHI GUI (and v. 0.9xx Beta)options ===*/ - - - - - else if ( (bVer1Options & 1) && INCHI_OPTION_PREFX == argv[i][0] && argv[i][1] ) - { - /*=== parsing stand-alone/library InChI options ===*/ - - - pArg = argv[i] + 1; - - bRecognizedOption = 2; - bVer1Options += 2; - /* always on: REQ_MODE_TAUT | REQ_MODE_ISO | REQ_MODE_STEREO */ -#ifdef CML_DEBUG - printf ("1 argv %d %s\n", i, argv[i]); -#endif - - - /*--- Input options ---*/ - if ( !stricmp( pArg, "STDIO" ) ) - { - bNameSuffix = 0; - } - else if ( !stricmp( pArg, "INPAUX" )) - { - if (INPUT_NONE == ip->nInputType) - ip->nInputType = INPUT_INCHI_PLAIN; - } - else if ( /* INPUT_NONE == ip->nInputType &&*/ - !memicmp( pArg, "SDF:", 4 ) ) - { - /* SDfile label */ - k = 0; - mystrncpy( ip->szSdfDataHeader, pArg+4, MAX_SDF_HEADER+1 ); - LtrimRtrim( ip->szSdfDataHeader, &k ); - if ( k ) - { - ip->pSdfLabel = ip->szSdfDataHeader; - ip->pSdfValue = szSdfDataValue; - if ( INPUT_NONE == ip->nInputType ) - { - ip->nInputType = INPUT_SDFILE; - } - } - else - { - ip->pSdfLabel = NULL; - ip->pSdfValue = NULL; - if ( INPUT_NONE == ip->nInputType ) - { - ip->nInputType = INPUT_MOLFILE; - } - } - } - -#if ( ADD_CMLPP == 1 ) - else if ( INPUT_NONE == ip->nInputType && !stricmp( pArg, "CML" ) ) - { - /* CMLfile label */ - ip->nInputType = INPUT_CMLFILE; - } -#endif - else if ( !memicmp( pArg, "START:", 6 ) ) - { - ip->first_struct_number = strtol(pArg+6, NULL, 10); - } - else if ( !memicmp( pArg, "END:", 4 ) ) - { - ip->last_struct_number = strtol(pArg+4, NULL, 10); - } -#ifdef BUILD_WITH_ENG_OPTIONS - else if ( !memicmp( pArg, "RSB:", 4 )) - { - mdbr = (int)strtol(pArg+4, NULL, 10); - } -#endif - - - /*--- Output options ---*/ -#if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) ) - else if ( !stricmp( pArg, "Tabbed" ) ) - { - bXml |= INCHI_OUT_TABBED_OUTPUT; - } -#endif - else if ( !stricmp( pArg, "NOLABELS" ) ) - { - bNoStructLabels = 1; - } - else if ( !stricmp( pArg, "SAVEOPT" ) ) - { - bINChIOutputOptions |= INCHI_OUT_SAVEOPT; - } - else if ( !stricmp( pArg, "AUXNONE" ) ) - { - /* no aux. info */ - bINChIOutputOptions |= INCHI_OUT_NO_AUX_INFO; /* no aux info */ - bINChIOutputOptions &= ~INCHI_OUT_SHORT_AUX_INFO; - } - else if ( !stricmp( pArg, "OUTPUTSDF" ) ) - { - /* output SDfile */ - bOutputMolfileOnly = 1; - } - else if ( !stricmp( pArg, "SdfAtomsDT" ) ) - { - /* output isotopes H as D and T in SDfile */ - bOutputMolfileDT = 1; - } - else if ( !stricmp( pArg, "D" ) ) - { - /* display the structures */ - bDisplay |= 1; - } - else if ( !memicmp( pArg, "F", 1 ) && (c = (int)strtol( pArg+1, (char**)&q, 10 ), q > pArg+1) ) - { - nFontSize = -c; /* struct. display font size */ - } - else if ( !stricmp( pArg, "EQU" ) ) - { - bCompareComponents = CMP_COMPONENTS; - } - - - /*--- All modes (std and non-std InChI) structure perception options ---*/ - else if ( !stricmp( pArg, "SNON" ) ) - { - bVer1DefaultMode &= ~REQ_MODE_STEREO; /* no stereo */ - nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); - } - else if ( !stricmp( pArg, "NEWPSOFF" ) ) - { - bPointedEdgeStereo = 0; - } - else if ( !stricmp( pArg, "DONOTADDH" ) ) - { - bDoNotAddH = 1; - } - - - /* Non-standard */ -#ifndef USE_STDINCHI_API - - /* Non-std InChI structure perception options */ - - else if ( !stricmp( pArg, "SREL" ) ) - { - bVer1DefaultMode |= REQ_MODE_STEREO; /* relative stereo */ - nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_CHIR_FLG_STEREO); - nMode |= REQ_MODE_RELATIVE_STEREO; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "SRAC" ) ) - { - /* REQ_MODE_CHIR_FLG_STEREO */ - bVer1DefaultMode |= REQ_MODE_STEREO; /* racemic stereo */ - nMode &= ~(REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); - nMode |= REQ_MODE_RACEMIC_STEREO; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "SUCF" ) ) - { - bVer1DefaultMode |= REQ_MODE_STEREO; /* stereo defined by the Chiral flag */ - nMode &= ~(REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO); - nMode |= REQ_MODE_CHIR_FLG_STEREO; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "ChiralFlagON" ) ) - { - /* used only with /SUCF */ - /* NB: do not toggle off bStdFormat! (if necessary SUCF will do) */ - bForcedChiralFlag &= ~FLAG_SET_INP_AT_NONCHIRAL; - bForcedChiralFlag |= FLAG_SET_INP_AT_CHIRAL; - } - else if ( !stricmp( pArg, "ChiralFlagOFF" ) ) - { - /* used only with /SUCF */ - /* NB: do not toggle off bStdFormat! (if necessary SUCF will do) */ - bForcedChiralFlag &= ~FLAG_SET_INP_AT_CHIRAL; - bForcedChiralFlag |= FLAG_SET_INP_AT_NONCHIRAL; - } - - - /*--- Non-std InChI creation options ---*/ - - /* Stereo */ - else if ( !stricmp( pArg, "SUU" ) ) - { - /* include omitted undef/unknown stereo */ - bVer1DefaultMode &= ~(REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU); - bStdFormat = 0; - } - else if ( !stricmp( pArg, "SLUUD" ) ) - { - /* Make labels for unknown and undefined stereo different */ - bVer1DefaultMode |= REQ_MODE_DIFF_UU_STEREO; - bStdFormat = 0; - } - /* FixedH */ - else if ( !stricmp( pArg, "FIXEDH" ) ) - { - bVer1DefaultMode |= REQ_MODE_BASIC; /* tautomeric */ - bStdFormat = 0; - } - /* RecMet */ - else if ( !stricmp( pArg, "RECMET" ) ) - { - /* reconnect metals */ - bReconnectCoord = 1; - bStdFormat = 0; - } -#if ( KETO_ENOL_TAUT == 1 ) - else if ( !stricmp( pArg, "KET" ) ) - { - bKetoEnolTaut = 1; - bStdFormat = 0; - } -#endif -#if ( TAUT_15_NON_RING == 1 ) - else if ( !stricmp( pArg, "15T" ) ) - { - b15TautNonRing = 1; - bStdFormat = 0; - } -#endif - -#endif - - /*--- Generation options ---*/ - else if ( !memicmp( pArg, "W", 1 ) && (t = strtod( pArg+1, (char**)&q ), q > pArg+1) ) - { - if ( errno == ERANGE || t < 0.0 || t*1000.0 > (double)ULONG_MAX) - { - ul = 0; - } - else - { - ul = (unsigned long)(t*1000.0); /* max. time per structure */ - } - ip->msec_MaxTime = ul; - } - else if ( !stricmp( pArg, "WarnOnEmptyStructure" ) ) - { - ip->bAllowEmptyStructure = 1; - } - - /* InChIKey/InChI hash */ - else if ( !stricmp( pArg, "Key" ) ) - { - bHashKey = 1; - } - else if ( !stricmp( pArg, "XHash1" ) ) - { - bHashXtra1 = 1; - } - else if ( !stricmp( pArg, "XHash2" ) ) - { - bHashXtra2 = 1; - } - - - /*--- Conversion modes ---*/ - - -#if ( READ_INCHI_STRING == 1 ) - else if ( !stricmp( pArg, "InChI2InChI" ) ) - { - /* Read InChI Identifiers and output InChI Identifiers */ - ip->nInputType = INPUT_INCHI; - ip->bReadInChIOptions |= READ_INCHI_OUTPUT_INCHI; - ip->bReadInChIOptions &= ~READ_INCHI_TO_STRUCTURE; - } - else if ( !stricmp( pArg, "InChI2Struct" ) ) - { - /* Split InChI Identifiers into components */ - ip->bReadInChIOptions |= READ_INCHI_TO_STRUCTURE; - ip->bReadInChIOptions &= ~READ_INCHI_OUTPUT_INCHI; - ip->nInputType = INPUT_INCHI; - } -#ifdef BUILD_WITH_ENG_OPTIONS - else if ( !stricmp( pArg, "KeepBalanceP" ) ) - { - /* When spliting InChI Identifiers into components: */ - /* If MobileH output then add p to each component; */ - /* Otherwise add one more component containing balance */ - /* of protons and exchangeable isotopic H */ - ip->bReadInChIOptions |= READ_INCHI_KEEP_BALANCE_P; - bStdFormat = 0; - } -#endif -#endif - - - - /*--- (engineering) Undo bug/draw fixes options ---*/ -#ifdef BUILD_WITH_ENG_OPTIONS - else if ( !stricmp( pArg, "FixSp3bugOFF" ) ) - { - bFixSp3bug = 0; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "FBOFF" ) ) - { - bFixSp3bug = 0; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "FB2OFF" ) ) - { - bFixFB2 = 0; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "SPXYZOFF" ) ) - { - bAddPhosphineStereo = 0; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "SASXYZOFF" ) ) - { - bAddArsineStereo = 0; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "FNUDOFF" ) ) - { - ip->bFixNonUniformDraw = 0; - bStdFormat = 0; - } - - /*--- (engineering) Old structure-perception and InChI creation options ---*/ -#if ( FIX_ADJ_RAD == 1 ) - else if ( !stricmp( pArg, "FixRad" ) ) - { - bFixAdjacentRad = 1; - bStdFormat = 0; - } -#endif -#if ( UNDERIVATIZE == 1 ) - else if ( !stricmp( pArg, "DoDRV" ) ) - { - ip->bUnderivatize = 1; - bStdFormat = 0; - } -#endif -#if ( RING2CHAIN == 1 ) - else if ( !stricmp( pArg, "DoR2C" ) ) - { - ip->bRing2Chain = 1; - bStdFormat = 0; - } -#endif -#if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) - else if ( !stricmp( pArg, "DoneOnly" ) ) - { - ip->bIngnoreUnchanged = 1; - bStdFormat = 0; - } -#endif - - else if ( !memicmp( pArg, "MOVEPOS:", 8 ) ) - { - bMovePositiveCharges = (0 != strtol(pArg+8, NULL, 10)); - bStdFormat = 0; - } - - else if ( !stricmp( pArg, "NoADP" ) ) - { - bTgFlagHardAddRenProtons = 0; - bStdFormat = 0; - } - /* Tautomer perception off */ - else if ( !stricmp( pArg, "EXACT" ) ) - { - bVer1DefaultMode |= REQ_MODE_BASIC; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "ONLYRECSALT" ) ) - { - /* do not disconnect salts */ - bDisconnectSalts = 0; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "ONLYEXACT" ) || !stricmp( pArg, "ONLYFIXEDH" ) ) - { - bVer1DefaultMode |= REQ_MODE_BASIC; - bVer1DefaultMode &= ~REQ_MODE_TAUT; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "ONLYNONISO" ) ) - { - bVer1DefaultMode |= REQ_MODE_NON_ISO; - bVer1DefaultMode &= ~REQ_MODE_ISO; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "TAUT" ) ) - { - bVer1DefaultMode &= ~REQ_MODE_BASIC; - bVer1DefaultMode |= REQ_MODE_TAUT; - } - else if ( !stricmp( pArg, "ONLYRECMET" ) ) - { - /* do not disconnect metals */ - bDisconnectCoord = 0; - bStdFormat = 0; - } - - /*--- (hidden) Old output and other options ---*/ - - else if ( !stricmp( pArg, "SdfSplit" ) ) - { - /* Split single Molfiles into disconnected components */ - bOutputMolfileSplit = 1; - } - else if ( !stricmp( pArg, "DCR" ) ) - { - bDisplayCompositeResults = 1; - } - else if ( !stricmp( pArg, "AUXFULL" ) || !stricmp( pArg, "AUXMAX" ) ) - { - /* full aux info */ - bINChIOutputOptions &= ~(INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO); /* include short aux info */ - } - else if ( !stricmp( pArg, "AUXMIN" ) ) - { - /* minimal aux info */ - bINChIOutputOptions &= ~INCHI_OUT_NO_AUX_INFO; /* include short aux info */ - bINChIOutputOptions |= INCHI_OUT_SHORT_AUX_INFO; - } -#if ( READ_INCHI_STRING == 1 ) - else if ( !stricmp( pArg, "DDSRC" ) ) - { - bDisplayIfRestoreWarnings = 1; /* InChI->Structure debugging: Display Debug Structure Restore Components */ - } -#endif - else if ( !stricmp( pArg, "NoVarH" ) ) - { - bTgFlagVariableProtons = 0; - } - else if ( !stricmp( pArg, "FULL" ) ) - { - bVer1DefaultMode = VER103_DEFAULT_MODE; - nMode = 0; - bReconnectCoord = 1; /* full output */ - bINChIOutputOptions = ((EMBED_REC_METALS_INCHI==1)? INCHI_OUT_EMBED_REC : 0) | INCHI_OUT_SHORT_AUX_INFO; - ip->bCtPredecessors = 0; - ip->bAbcNumbers = 0; - bXml |= INCHI_OUT_PLAIN_TEXT | INCHI_OUT_PLAIN_TEXT_COMMENTS; - bXml &= ~(INCHI_OUT_XML | INCHI_OUT_XML_TEXT_COMMENTS); - } - else if ( !stricmp( pArg, "MIN" ) ) - { - bVer1DefaultMode = VER103_DEFAULT_MODE; - nMode = 0; - bReconnectCoord = 1; /* minimal output */ - bINChIOutputOptions = ((EMBED_REC_METALS_INCHI==1)? INCHI_OUT_EMBED_REC : 0) | INCHI_OUT_NO_AUX_INFO; /* minimal compressed output */ - ip->bCtPredecessors = 1; - ip->bAbcNumbers = 1; - bXml |= INCHI_OUT_PLAIN_TEXT | INCHI_OUT_PLAIN_TEXT_COMMENTS; - bXml &= ~(INCHI_OUT_XML | INCHI_OUT_XML_TEXT_COMMENTS); - } - else if ( !stricmp( pArg, "COMPRESS" ) ) - { - ip->bAbcNumbers = 1; - ip->bCtPredecessors = 1; /* compressed output */ - } - -#if ( READ_INCHI_STRING == 1 ) - else if ( !stricmp( pArg, "InChI2InChI" ) ) - { - /* Read InChI Identifiers and output InChI Identifiers */ - ip->nInputType = INPUT_INCHI; - ip->bReadInChIOptions |= READ_INCHI_OUTPUT_INCHI; - ip->bReadInChIOptions &= ~READ_INCHI_TO_STRUCTURE; - } - - else if ( !stricmp( pArg, "SplitInChI" ) ) - { - /* Split InChI Identifiers into components */ - ip->bReadInChIOptions |= READ_INCHI_SPLIT_OUTPUT; - } -#endif - else if ( !stricmp( pArg, "MOLFILENUMBER" ) ) - { - ip->bGetMolfileNumber |= 1; - } - else if ( !stricmp( pArg, "OutputPLAIN" ) ) - { - bXml |= INCHI_OUT_PLAIN_TEXT; - bXml &= ~INCHI_OUT_XML; - } - else if ( !stricmp( pArg, "OutputXML" ) ) - { - bXml |= INCHI_OUT_XML; - bXml &= ~INCHI_OUT_PLAIN_TEXT; - } - else if ( !stricmp( pArg, "OutputANNPLAIN" ) ) - { - bXml |= INCHI_OUT_PLAIN_TEXT_COMMENTS; - bXml &= ~INCHI_OUT_XML_TEXT_COMMENTS; - bXml |= INCHI_OUT_WINCHI_WINDOW; /* debug */ - } - else if ( !stricmp( pArg, "OutputANNXML" ) ) - { - bXml |= INCHI_OUT_XML_TEXT_COMMENTS; - bXml &= ~INCHI_OUT_PLAIN_TEXT_COMMENTS; - } else - if ( !stricmp( pArg, "ONLYEXACT" ) || !stricmp( pArg, "ONLYFIXEDH" ) ) { - bVer1DefaultMode |= REQ_MODE_BASIC; - bVer1DefaultMode &= ~REQ_MODE_TAUT; - } else - if ( !stricmp( pArg, "ONLYNONISO" ) ) { - bVer1DefaultMode |= REQ_MODE_NON_ISO; - bVer1DefaultMode &= ~REQ_MODE_ISO; - } else - if ( !stricmp( pArg, "TAUT" ) ) { - bVer1DefaultMode &= ~REQ_MODE_BASIC; - bVer1DefaultMode |= REQ_MODE_TAUT; - } else - if ( !stricmp( pArg, "ONLYRECMET" ) ) { /* do not disconnect metals */ - bDisconnectCoord = 0; - } else - if ( !stricmp( pArg, "ONLYRECSALT" ) ) { /* do not disconnect salts */ - bDisconnectSalts = 0; - } else - if ( !memicmp( pArg, "MOVEPOS:", 8 ) ) { /* added -- 2010-03-01 DT */ - bMovePositiveCharges = (0 != strtol(pArg+8, NULL, 10)); - } else - if ( !memicmp( pArg, "RSB:", 4 )) { - mdbr = (int)strtol(pArg+4, NULL, 10); - } else - if ( !stricmp( pArg, "EQU" ) ) { - bCompareComponents = CMP_COMPONENTS; - } else - if ( !stricmp( pArg, "EQUNONISO" ) ) - { - bCompareComponents = CMP_COMPONENTS | CMP_COMPONENTS_NONISO; - } - else if ( !memicmp( pArg, "OP:", 3 ) ) - { - bOutputPath = 1; - strncpy(szOutputPath, pArg+3, sizeof(szOutputPath)-1); - } -#endif /* BUILD_WITH_ENG_OPTIONS */ - - - - /* Display unrecognized option */ - else - { - bRecognizedOption = 0; -#ifndef TARGET_LIB_FOR_WINCHI - inchi_ios_eprint(log_file, "Unrecognized option: \"%s\".\n", pArg); -#endif - } - bVer1Options |= bRecognizedOption; - - - } - /*=== end of parsing stand-alone/library InChI options ===*/ - - - - else if ( ip->num_paths < MAX_NUM_PATHS ) - { - char *sz; - if ( argv[i] && argv[i][0] ) - { - if ( sz = (char*) inchi_malloc( (strlen(argv[i]) + 1)*sizeof(sz[0])) ) - { - strcpy( sz, argv[i] ); - } -#ifdef CML_DEBUG - printf ("1 path %d argv %d %s\n", ip -> num_paths, i, argv [i]); -#endif - ip->path[ip->num_paths++] = sz; - } - } - - - - } /* for ( i = 1; i < argc; i ++ ) */ - - - - if ( bHashKey != 0 ) - /* Suppress InChIKey calculation if: - compressed output OR Inchi2Struct OR Inchi2Inchi */ - { - - if ( (ip->bAbcNumbers ==1) && (ip->bCtPredecessors == 1) ) - { -#ifndef TARGET_LIB_FOR_WINCHI - inchi_ios_eprint(log_file, "Terminating: generation of InChIKey is not available with 'Compress' option\n"); - return -1; -#endif - bHashKey = 0; - } - if ( ip->nInputType == INPUT_INCHI ) - { -#ifndef TARGET_LIB_FOR_WINCHI - inchi_ios_eprint(log_file, "Terminating: generation of InChIKey is not available in InChI conversion mode\n"); - return -1; -#endif - bHashKey = 0; - } - else - if ( bOutputMolfileOnly == 1 ) - { -#ifndef TARGET_LIB_FOR_WINCHI - inchi_ios_eprint(log_file, "Terminating: generation of InChIKey is not available with 'OutputSDF' option\n"); - return -1; -#endif - bHashKey = 0; - } - - } - - - if ( bNameSuffix || bOutputPath ) - { - const char *p = NULL; - char *r = NULL; - char *sz; - int len; - const char szNUL[] = "NUL"; /* fix for AMD processor: use const char[] instead of just "NUL" constant 2008-11-5 DT */ - - /* find the 1st path */ - for ( i = 0; i < MAX_NUM_PATHS; i ++ ) - { - if ( !p && ip->path[i] && ip->path[i][0] ) - { - p = ip->path[i]; - break; - } - } - /* fix output path */ - if ( bOutputPath && szOutputPath[0] && p ) - { - /* remove last slash */ - len = strlen(szOutputPath); - if ( len > 0 && szOutputPath[len-1] != INCHI_PATH_DELIM ) - { - szOutputPath[len++] = INCHI_PATH_DELIM; - szOutputPath[len] = '\0'; - } - if ( len > 0 && (r = (char *)strrchr( p, INCHI_PATH_DELIM ) ) && r[1] ) - { - strcat( szOutputPath, r+1 ); - p = szOutputPath; - } - } /* add missing paths */ - for ( i = 0; p && i < MAX_NUM_PATHS; i ++ ) - { - /* fix for AMD processor: changed order 2008-11-5 DT */ - if ( !ip->path[i] || !ip->path[i][0] ) - { - len = strlen( p ) + strlen(szNameSuffix) + strlen( ext[i] ); - if ( sz = (char*) inchi_malloc( (len+1)*sizeof(sz[0]) ) ) - { - strcpy( sz, p ); - strcat( sz, szNameSuffix ); - strcat( sz, ext[i] ); - ip->num_paths++; - ip->path[i] =sz; - } - } else - if ( !stricmp( ip->path[i], szNUL ) ) - { - inchi_free( (char *)ip->path[i] ); /* cast deliberately const qualifier */ - ip->path[i] = NULL; - } - } - } - - - - -#if ( READ_INCHI_STRING == 1 ) - if ( INPUT_INCHI == ip->nInputType ) - { - bCompareComponents = 0; - /*bDisplayCompositeResults = 0;*/ -#if ( I2S_MODIFY_OUTPUT == 1 ) - if ( !(ip->bReadInChIOptions & READ_INCHI_TO_STRUCTURE ) ) -#endif - { - bOutputMolfileOnly = 0; - /*bNoStructLabels = 1;*/ - bINChIOutputOptions |= INCHI_OUT_NO_AUX_INFO; - bINChIOutputOptions &= ~INCHI_OUT_SHORT_AUX_INFO; - bINChIOutputOptions &= ~INCHI_OUT_ONLY_AUX_INFO; - } - ip->bDisplayIfRestoreWarnings = bDisplayIfRestoreWarnings; - if ( !(bINChIOutputOptions & - - (INCHI_OUT_SDFILE_ONLY | /* not in bINChIOutputOptions yet */ - INCHI_OUT_XML | /* not in bINChIOutputOptions yet */ - INCHI_OUT_PLAIN_TEXT | /* not in bINChIOutputOptions yet */ - INCHI_OUT_PLAIN_TEXT_COMMENTS | /* not in bINChIOutputOptions yet */ - INCHI_OUT_XML_TEXT_COMMENTS /* not in bINChIOutputOptions yet */ - ) ) -#if ( I2S_MODIFY_OUTPUT == 1 ) - && !bOutputMolfileOnly - && !(bXml & (INCHI_OUT_XML | INCHI_OUT_XML_TEXT_COMMENTS | INCHI_OUT_PLAIN_TEXT | INCHI_OUT_XML_TEXT_COMMENTS)) -#endif - ) { - bINChIOutputOptions |= INCHI_OUT_PLAIN_TEXT; - } - } -#endif - - - if ( bVer1Options ) - { - nMode |= bVer1DefaultMode; - } - else if ( bReleaseVersion ) - { - nMode |= nReleaseMode; - } - -#if ( defined(COMPILE_ANSI_ONLY) || defined(TARGET_LIB_FOR_WINCHI) ) - if ( bCompareComponents && !(bDisplay & 1) ) { - bCompareComponents = 0; - } -#endif - /* Save original options */ - /* nOrigMode = nMode; */ -#ifndef COMPILE_ANSI_ONLY - ip->dp.sdp.nFontSize = nFontSize; - ip->dp.sdp.ulDisplTime = *ulDisplTime; - ip->bDisplay = bDisplay; -#ifdef TARGET_LIB_FOR_WINCHI - ip->bDisplayCompositeResults = bDisplay; -#else - ip->bDisplayCompositeResults = bDisplayCompositeResults; -#endif -#else - ip->bDisplayEachComponentINChI = 0; - bCompareComponents = 0; -#endif - ip->bMergeAllInputStructures = bMergeAllInputStructures; - ip->bDoNotAddH = bDoNotAddH; - /* set default options */ - if ( !nMode || nMode == REQ_MODE_STEREO ) { - /* requested all output */ - nMode |= (REQ_MODE_BASIC | REQ_MODE_TAUT | REQ_MODE_ISO | REQ_MODE_NON_ISO | REQ_MODE_STEREO); - } else { - if ( !(nMode & (REQ_MODE_BASIC | REQ_MODE_TAUT)) ) { - nMode |= (REQ_MODE_BASIC | REQ_MODE_TAUT); - } - if ( (nMode & REQ_MODE_STEREO) && !(nMode & (REQ_MODE_ISO | REQ_MODE_NON_ISO)) ) { - nMode |= (REQ_MODE_ISO | REQ_MODE_NON_ISO); - } - } - /* if the user requested isotopic then unconditionally add non-isotopic output. */ - if ( nMode & REQ_MODE_ISO ) { - nMode |= REQ_MODE_NON_ISO; - } -#if ( MIN_SB_RING_SIZE > 0 ) - if ( mdbr ) { - nMinDbRinSize = mdbr; - } - nMode |= (nMinDbRinSize << REQ_MODE_MIN_SB_RING_SHFT) & REQ_MODE_MIN_SB_RING_MASK; -#endif - /* input file */ - if ( ip->nInputType == INPUT_NONE && ip->num_paths > 0 ) { - ip->nInputType = INPUT_MOLFILE; /* default */ -#if ( ADD_CMLPP == 1 ) - { - const char *p; - if ( ip->path[0] && ( p = strrchr(ip->path[0], '.' ) ) && - !stricmp( p, ".cml") ) { - ip->nInputType = INPUT_CMLFILE; - } - } -#endif - } - ip->nMode = nMode; - if ( (bCompareComponents & CMP_COMPONENTS) && (nMode & REQ_MODE_BASIC) ) { - bCompareComponents |= CMP_COMPONENTS_NONTAUT; /* compare non-tautomeric */ - } - ip->bCompareComponents = bCompareComponents; - - ip->bINChIOutputOptions = bINChIOutputOptions | (bOutputMolfileOnly? INCHI_OUT_SDFILE_ONLY : 0); - if ( bOutputMolfileOnly ) { - bXml &= ~(INCHI_OUT_XML | INCHI_OUT_PLAIN_TEXT | - INCHI_OUT_PLAIN_TEXT_COMMENTS | INCHI_OUT_XML_TEXT_COMMENTS | INCHI_OUT_TABBED_OUTPUT); -#if ( SDF_OUTPUT_DT == 1 ) - ip->bINChIOutputOptions |= bOutputMolfileDT? INCHI_OUT_SDFILE_ATOMS_DT : 0; - ip->bINChIOutputOptions |= bOutputMolfileSplit? INCHI_OUT_SDFILE_SPLIT : 0; -#endif - } - if ( bXml & INCHI_OUT_XML ) { - bXml &= ~(INCHI_OUT_PLAIN_TEXT | INCHI_OUT_XML_TEXT_COMMENTS | INCHI_OUT_TABBED_OUTPUT); - } -#ifdef TARGET_LIB_FOR_WINCHI - if ( !(bDisplay & 1) ) { - bXml &= ~(INCHI_OUT_PLAIN_TEXT_COMMENTS | INCHI_OUT_XML_TEXT_COMMENTS); /* do not ouput comments in wINChI text file results */ - } else { - bXml |= INCHI_OUT_WINCHI_WINDOW; - } -#endif - ip->bINChIOutputOptions |= bXml; - ip->bNoStructLabels = bNoStructLabels; - - if ( bForcedChiralFlag ) { - ip->bChiralFlag = bForcedChiralFlag; - } - - /*******************************************/ - /* tautomeric/salts settings */ - /*******************************************/ - - ip->bTautFlags = 0; /* initialize */ - ip->bTautFlagsDone = 0; /* initialize */ - - /* find regular tautomerism */ - ip->bTautFlags |= TG_FLAG_TEST_TAUT__ATOMS; - /* disconnect salts */ - ip->bTautFlags |= bDisconnectSalts? TG_FLAG_DISCONNECT_SALTS : 0; - /* if possible find long-range H/(-) taut. on =C-OH, >C=O */ - ip->bTautFlags |= bAcidTautomerism? TG_FLAG_TEST_TAUT__SALTS : 0; - /* allow long-range movement of N(+), P(+) charges */ - ip->bTautFlags |= bMovePositiveCharges? TG_FLAG_MOVE_POS_CHARGES : 0; - /* multi-attachement long-range H/(-) taut. on =C-OH, >C=O */ - ip->bTautFlags |= (bAcidTautomerism > 1)? TG_FLAG_TEST_TAUT2_SALTS : 0; - /* (debug) allow to find long-range H-only tautomerism on =C-OH, >C=O */ - ip->bTautFlags |= (bUnchargedAcidTaut==1)? TG_FLAG_ALLOW_NO_NEGTV_O : 0; - /* merge =C-OH and >C=O containing t-groups and other =C-OH groups */ - ip->bTautFlags |= bMergeSaltTGroups? TG_FLAG_MERGE_TAUT_SALTS : 0; - ip->bTautFlags |= bDisconnectCoord? TG_FLAG_DISCONNECT_COORD : 0; - ip->bTautFlags |=(bDisconnectCoord && - bReconnectCoord)? TG_FLAG_RECONNECT_COORD : 0; - ip->bTautFlags |= bDisconnectCoordChkVal? TG_FLAG_CHECK_VALENCE_COORD : 0; - ip->bTautFlags |= bTgFlagVariableProtons? TG_FLAG_VARIABLE_PROTONS : 0; - ip->bTautFlags |= bTgFlagHardAddRenProtons? TG_FLAG_HARD_ADD_REM_PROTONS : 0; - ip->bTautFlags |= bKetoEnolTaut? TG_FLAG_KETO_ENOL_TAUT : 0; - ip->bTautFlags |= b15TautNonRing? TG_FLAG_1_5_TAUT : 0; - -#ifdef STEREO_WEDGE_ONLY - ip->bTautFlags |= bPointedEdgeStereo? TG_FLAG_POINTED_EDGE_STEREO : 0; -#endif -#if ( FIX_ADJ_RAD == 1 ) - ip->bTautFlags |= bFixAdjacentRad? TG_FLAG_FIX_ADJ_RADICALS : 0; -#endif - ip->bTautFlags |= bAddPhosphineStereo? TG_FLAG_PHOSPHINE_STEREO : 0; - ip->bTautFlags |= bAddArsineStereo? TG_FLAG_ARSINE_STEREO : 0; - ip->bTautFlags |= bFixSp3bug? TG_FLAG_FIX_SP3_BUG : 0; - - - if (bFixFB2) - { -#if ( FIX_ISO_FIXEDH_BUG == 1 ) - ip->bTautFlags |= TG_FLAG_FIX_ISO_FIXEDH_BUG; /* accomodate FIX_ISO_FIXEDH_BUG */ -#endif -#if ( FIX_TERM_H_CHRG_BUG == 1 ) - ip->bTautFlags |= TG_FLAG_FIX_TERM_H_CHRG_BUG; /* accomodate FIX_TERM_H_CHRG_BUG */ -#endif -#if ( FIX_TRANSPOSITION_CHARGE_BUG == 1 ) - ip->bINChIOutputOptions |= INCHI_OUT_FIX_TRANSPOSITION_CHARGE_BUG; -#endif - } - - - - - if ( !ip->nInputType ) - ip->nInputType = INPUT_MOLFILE; - - /* Check if /SNon requested turn OFF SUU/SLUUD */ - if ( ! (ip->nMode & REQ_MODE_STEREO) ) - { - ip->nMode &= ~REQ_MODE_DIFF_UU_STEREO; - ip->nMode &= ~(REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU); - } - - - /* Standard InChI ? */ - if (bStdFormat) - { - ip->bINChIOutputOptions |= INCHI_OUT_STDINCHI; - } - - /* InChIKey ? */ - if ( !bHashKey ) - ip->bCalcInChIHash = INCHIHASH_NONE; - else - ip->bCalcInChIHash = INCHIHASH_KEY; - - /* Extension(s) to hash (in non-std mode only) ? */ - if ( !bHashKey ) - { - if ( (bHashXtra1!=0) || (bHashXtra2!=0) ) - inchi_ios_eprint(log_file, - "Hash extension(s) not generated: InChIKey not requested"); - } - else - { - if ( bHashXtra1 ) - { - if ( bHashXtra2 ) ip->bCalcInChIHash = INCHIHASH_KEY_XTRA1_XTRA2; - else ip->bCalcInChIHash = INCHIHASH_KEY_XTRA1; - } - else if ( bHashXtra2 ) - { - ip->bCalcInChIHash = INCHIHASH_KEY_XTRA2; - } - } - - - return 0; -} - - - -/*******************************************************************/ -int PrintInputParms( INCHI_IOSTREAM *log_file, INPUT_PARMS *ip ) -{ -INCHI_MODE nMode = ip->nMode; -int k; -int bStdFormat = 1; -int first=1; - - -#ifdef TARGET_LIB_FOR_WINCHI - int bInChI2Struct = 0; /* as of December 2008, winchi-1 does not convert InChI to structure */ -#else - int bInChI2Struct = (ip->bReadInChIOptions & READ_INCHI_TO_STRUCTURE) && ip->nInputType == INPUT_INCHI; -#endif - - if ( !(ip->bINChIOutputOptions & INCHI_OUT_STDINCHI) ) - bStdFormat = 0; - - - /* some stereo */ - if ( ! (nMode & REQ_MODE_STEREO) ) - { - inchi_ios_eprint( log_file, "Using specific structure perception features:\n"); - first = 0; - inchi_ios_eprint( log_file, " Stereo OFF\n"); - } - else - { - if ( ! (TG_FLAG_POINTED_EDGE_STEREO & ip->bTautFlags) ) - { - if (first) - { - inchi_ios_eprint( log_file, "Using specific structure perception features:\n"); - first = 0; - } - inchi_ios_eprint( log_file, " Both ends of wedge point to stereocenters\n"); - } - } - if ( ip->bDoNotAddH ) - { - if (first) - inchi_ios_eprint( log_file, "Using specific structure perception features:\n"); - inchi_ios_eprint( log_file, " Do not add H\n"); - } - - - /* Generation/conversion indicator */ - if (bStdFormat) - { - if ( !(ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY) && !bInChI2Struct ) - inchi_ios_eprint( log_file, "Generating standard InChI\n" ); - -#if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) && !defined(TARGET_EXE_USING_API) ) - /* effective only in command line program InChI or stdInChI */ - else if ( bInChI2Struct ) - inchi_ios_eprint( log_file, "Converting InChI(s) to structure(s) in %s\n", - (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY)? - "MOL format" : "aux. info format" ); -#endif - } - else - inchi_ios_eprint( log_file, "Generating non-standard InChI with the options: \n" ); - - - /* SDfile output */ - - if ( ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY ) - { - inchi_ios_eprint( log_file, - "Output SDfile only without stereochemical information and atom coordinates%s\n", - (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ATOMS_DT)? - "\n(write H isotopes as D, T)":"" ); - } - - /* Fixed/Mobile H */ - if (!bStdFormat) - { - if( (nMode & (REQ_MODE_BASIC | REQ_MODE_TAUT )) == (REQ_MODE_BASIC | REQ_MODE_TAUT) ) - inchi_ios_eprint( log_file, " Mobile H Perception OFF (include FixedH layer)\n" ); - else if( (nMode & (REQ_MODE_BASIC | REQ_MODE_TAUT )) == (REQ_MODE_TAUT) ) - inchi_ios_eprint( log_file, " Mobile H Perception ON (omit FixedH layer)\n" ); - else if( (nMode & (REQ_MODE_BASIC | REQ_MODE_TAUT )) == (REQ_MODE_BASIC) ) - inchi_ios_eprint( log_file, " Mobile H ignored\n" ); - else - inchi_ios_eprint( log_file, " Undefined Mobile H mode\n" ); - - if ( (ip->bTautFlags & TG_FLAG_VARIABLE_PROTONS) ) - if ( !(ip->bTautFlags & TG_FLAG_HARD_ADD_REM_PROTONS) ) - inchi_ios_eprint( log_file, " Disabled Aggressive (De)protonation\n" ); - -#if ( FIND_RING_SYSTEMS != 1 ) - inchi_ios_eprint( log_file, " %s5-, 6-, 7-memb. ring taut. ignored\n", i?"; ":""); -#endif - - /* RecMet */ - if ( ip->bTautFlags & TG_FLAG_DISCONNECT_COORD ) - { - if ( ip->bTautFlags & TG_FLAG_RECONNECT_COORD ) - inchi_ios_eprint( log_file, " Include bonds to metals\n"); - else - inchi_ios_eprint( log_file, " Do not reconnect metals (omit RecMet layer)\n"); - } - else - inchi_ios_eprint( log_file, " Do not disconnect metals\n"); - - /* isotopic - always ON, output disabled. 09-17-2009*/ - /* - if ( nMode & REQ_MODE_ISO ) - inchi_ios_eprint( log_file, " Isotopic ON\n"); - else if ( nMode & REQ_MODE_NON_ISO ) - inchi_ios_eprint( log_file, " Isotopic OFF\n"); - */ - -#if ( FIX_ADJ_RAD == 1 ) - if ( ip->bTautFlags & TG_FLAG_FIX_ADJ_RADICALS ) - inchi_ios_eprint( log_file, "Fix Adjacent Radicals\n" ); -#endif - - /* stereo */ - if ( nMode & REQ_MODE_STEREO ) - { - inchi_ios_eprint( log_file, " %s%s%s%sStereo ON\n", - ( nMode & REQ_MODE_NOEQ_STEREO )? "Slow ":"", - ( nMode & REQ_MODE_REDNDNT_STEREO )? "Redund. ":"", - ( nMode & REQ_MODE_NO_ALT_SBONDS)? "No AltBond ":"", - - ( nMode & REQ_MODE_RACEMIC_STEREO)? "Racemic " : - ( nMode & REQ_MODE_RELATIVE_STEREO)? "Relative " : - ( nMode & REQ_MODE_CHIR_FLG_STEREO)? "Chiral Flag " : "Absolute " ); - if ( 0 == (nMode & (REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU)) ) - inchi_ios_eprint( log_file, " Include undefined/unknown stereogenic centers and bonds\n"); - else if ( REQ_MODE_SC_IGN_ALL_UU == (nMode & (REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU)) ) - inchi_ios_eprint( log_file, " Omit undefined/unknown stereogenic centers\n"); - else if ( REQ_MODE_SB_IGN_ALL_UU == (nMode & (REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU)) ) - inchi_ios_eprint( log_file, " Omit undefined/unknown stereogenic bonds\n"); - else - /*case REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU*/ - inchi_ios_eprint( log_file, " Omit undefined/unknown stereogenic centers and bonds\n"); - - if ( 0 != (nMode & REQ_MODE_DIFF_UU_STEREO) ) - { - inchi_ios_eprint( log_file, " Make labels for unknown and undefined stereo different\n"); - } - - -#if ( defined(MIN_SB_RING_SIZE) && MIN_SB_RING_SIZE > 0 ) - k = (ip->nMode & REQ_MODE_MIN_SB_RING_MASK) >> REQ_MODE_MIN_SB_RING_SHFT; - if ( bRELEASE_VERSION != 1 || k != MIN_SB_RING_SIZE ) - { - if ( k >= 3 ) - inchi_ios_eprint( log_file, " Min. stereobond ring size: %d\n", k ); - else - inchi_ios_eprint( log_file, " Min. stereobond ring size: NONE\n" ); - } -#endif - } /* stereo */ - - } /* !bStdFormat */ - - - - if ( !bStdFormat ) - { - if ( TG_FLAG_KETO_ENOL_TAUT & ip->bTautFlags) - inchi_ios_eprint( log_file, " Account for keto-enol tautomerism\n"); - else - inchi_ios_eprint( log_file, " Do not account for keto-enol tautomerism\n"); - if ( TG_FLAG_1_5_TAUT & ip->bTautFlags) - inchi_ios_eprint( log_file, " Account for 1,5-tautomerism\n"); - else - inchi_ios_eprint( log_file, " Do not account for 1,5-tautomerism\n"); -#ifdef BUILD_WITH_ENG_OPTIONS - if ( TG_FLAG_PHOSPHINE_STEREO & ip->bTautFlags ) - inchi_ios_eprint( log_file, " Include phosphine stereochemistry\n"); - else - inchi_ios_eprint( log_file, " Do not include phosphine stereochemistry\n"); - if ( TG_FLAG_ARSINE_STEREO & ip->bTautFlags ) - inchi_ios_eprint( log_file, " Include arsine stereochemistry\n"); - else - inchi_ios_eprint( log_file, " Do not include arsine stereochemistry\n"); - if ( ! (TG_FLAG_FIX_SP3_BUG & ip->bTautFlags) ) - inchi_ios_eprint( log_file, " Turned OFF fix of bug leading to missing or undefined sp3 parity\n"); - if ( !(TG_FLAG_FIX_ISO_FIXEDH_BUG & ip->bTautFlags) ) - inchi_ios_eprint( log_file, " Turned OFF bug-fixes found after v.1.02b release\n"); - if ( !(ip->bFixNonUniformDraw) ) - inchi_ios_eprint( log_file, " Turned OFF fixes of non-uniform drawing issues\n"); - if ( ! (TG_FLAG_MOVE_POS_CHARGES & ip->bTautFlags) ) - inchi_ios_eprint( log_file, " MovePos turned OFF\n"); -#endif - } /* !bStdFormat */ - - if ( ip->bCalcInChIHash != INCHIHASH_NONE ) - { - if (bStdFormat) - inchi_ios_eprint( log_file, "Generating standard InChIKey\n"); - else - inchi_ios_eprint( log_file, "Generating InChIKey\n"); - if ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1 ) - inchi_ios_eprint( log_file, "Generating hash extension (1st block)\n"); - else if ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA2 ) - inchi_ios_eprint( log_file, "Generating hash extension (2nd block)\n"); - else if ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) - inchi_ios_eprint( log_file, "Generating hash extension (two blocks)\n"); - } - - if ( ip->bINChIOutputOptions & INCHI_OUT_SAVEOPT ) - { - inchi_ios_eprint( log_file, "Saving InChI creation options" ); - if ( bStdFormat ) - { - inchi_ios_eprint( log_file, " suppressed for standard InChI" ); - /* NB: actual suppression takes place on InChI serialization */ - /* (as on e.g. Inchi2Inchi conversion it may appear that we create non-std */ - /* InChI instead of standard one) */ - } - inchi_ios_eprint( log_file, "\n" ); - - } - - - if ( ip->bAllowEmptyStructure ) - inchi_ios_eprint( log_file, "Issue warning on empty structure\n" ); - - - - - /* Input */ - if ( ip->nInputType ) - { - inchi_ios_eprint( log_file, "Input format: %s", - ip->nInputType == INPUT_MOLFILE? "MOLfile" : - ip->nInputType == INPUT_SDFILE? "SDfile" : - ip->nInputType == INPUT_CMLFILE? "CMLfile" : -#if ( READ_INCHI_STRING == 1 ) - ip->nInputType == INPUT_INCHI? "InChI (plain identifier)" : -#endif - ip->nInputType == INPUT_INCHI_XML? "InChI AuxInfo (xml)" : - ip->nInputType == INPUT_INCHI_PLAIN? "InChI AuxInfo (plain)" : "Unknown" ); - if ( (ip->nInputType == INPUT_MOLFILE || ip->nInputType == INPUT_SDFILE) && - ip->bGetMolfileNumber ) - inchi_ios_eprint( log_file, " (attempting to read Molfile number)" ); - inchi_ios_eprint( log_file, "\n"); - } - - if ( ip->szSdfDataHeader[0] && ip->nInputType != INPUT_SDFILE ) - inchi_ios_eprint( log_file, " SDfile data header: \"%s\"\n", ip->szSdfDataHeader); - - /* Output */ - - inchi_ios_eprint( log_file, "Output format: %s%s\n", - (ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT)? "Plain text" : - (ip->bINChIOutputOptions & INCHI_OUT_XML)? "XML": - ((ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY) && bInChI2Struct )? "SDfile only (without stereochemical info and atom coordinates)" : - ((ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY) && !bInChI2Struct)? "SDfile only" : "Unknown", - - ((ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) && - (ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT))? ", tabbed":""); - -#if ( bRELEASE_VERSION == 1 ) - if ( ip->bCtPredecessors || ip->bAbcNumbers ) - { - if ( ip->bCtPredecessors && ip->bAbcNumbers ) - inchi_ios_eprint( log_file, "Representation: Compressed\n"); - else - inchi_ios_eprint( log_file, "Connection table: %s, %s\n", - ip->bCtPredecessors? "Predecessor_numbers(closures)":"Canon_numbers(branching, ring closures)", - ip->bAbcNumbers? "Shorter alternative":"Numerical"); - } -#else - if ( (bRELEASE_VERSION != 1) || ip->bCtPredecessors || ip->bAbcNumbers ) - inchi_ios_eprint( log_file, "Connection table: %s, %s\n", - ip->bCtPredecessors? "Predecessor_numbers(closures)":"Canon_numbers(branching, ring closures)", - ip->bAbcNumbers? "Shorter alternative":"Numerical"); - else - inchi_ios_eprint( log_file, "Representation: Numerical"); -#endif - - if ( !(ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY) ) - { - if( ip->bINChIOutputOptions & INCHI_OUT_NO_AUX_INFO ) - inchi_ios_eprint( log_file, "Aux. info suppressed\n"); - else if ( ip->bINChIOutputOptions & INCHI_OUT_SHORT_AUX_INFO ) - inchi_ios_eprint( log_file, "Minimal Aux. info\n"); - else - inchi_ios_eprint( log_file, "Full Aux. info\n"); - } - - - - if ( ip->msec_MaxTime ) - { - unsigned long seconds = ip->msec_MaxTime/1000; - unsigned long milliseconds = (ip->msec_MaxTime%1000); - inchi_ios_eprint( log_file, "Timeout per structure: %lu.%03lu sec\n", seconds, milliseconds); - } - else - inchi_ios_eprint( log_file, "No timeout"); - inchi_ios_eprint( log_file, "Up to %d atoms per structure\n", MAX_ATOMS); - if ( ip->first_struct_number > 1 ) - inchi_ios_eprint( log_file, "Skipping %ld structure%s\n", ip->first_struct_number-1, ip->first_struct_number==2? "":"s" ); - if ( ip->last_struct_number > 0 ) - inchi_ios_eprint( log_file, "Terminate after structure #%ld\n", ip->last_struct_number ); - if ( ip->bSaveWarningStructsAsProblem && ip->path[3] && ip->path[3][0] ) - inchi_ios_eprint( log_file, "Saving warning structures into the problem file\n"); - if ( ip->bSaveAllGoodStructsAsProblem && ip->path[3] && ip->path[3][0] ) - inchi_ios_eprint( log_file, "Saving only all good structures into the problem file\n"); - /* Report debug modes */ -#if ( bRELEASE_VERSION != 1 ) - inchi_ios_eprint( log_file, "Release version = NO\n"); -#endif - - -#if ( TRACE_MEMORY_LEAKS == 1 && defined(_DEBUG) ) - inchi_ios_eprint( log_file, "Tracing memory leaks (SLOW)\n"); -#endif - - inchi_ios_eprint( log_file, "\n" ); - - - -#if ( bRELEASE_VERSION != 1 ) -#if ( FIND_RING_SYSTEMS == 1 ) - inchi_ios_eprint( log_file, "Find ring systems=Y\nTautomers:\n" ); - inchi_ios_eprint( log_file, " 4-pyridinol=%s\n", TAUT_4PYRIDINOL_RINGS==1? "Y":"N"); - inchi_ios_eprint( log_file, " pyrazole=%s\n", TAUT_PYRAZOLE_RINGS==1? "Y":"N"); - inchi_ios_eprint( log_file, " tropolone=%s\n", TAUT_TROPOLONE_7==1? "Y":"N"); - inchi_ios_eprint( log_file, " tropolone-5=%s\n", TAUT_TROPOLONE_5==1? "Y":"N"); - inchi_ios_eprint( log_file, "Only chain attachments to tautomeric rings=%s\n", TAUT_RINGS_ATTACH_CHAIN==1? "Y":"N"); -#endif - if ( ip->bGetSdfileId ) - inchi_ios_eprint( log_file, "Extracting SDfile IDs\n"); - - inchi_ios_eprint( log_file, "\nDbg: MOVE_CHARGES=%d\n", - 0!=(ip->bTautFlags&TG_FLAG_MOVE_POS_CHARGES)); - inchi_ios_eprint( log_file, " REPLACE_ALT_WITH_TAUT=%d; NEUTRALIZE_ENDPOINTS=%d; BNS_PROTECT_FROM_TAUT=%d\n", - REPLACE_ALT_WITH_TAUT, NEUTRALIZE_ENDPOINTS, BNS_PROTECT_FROM_TAUT); - inchi_ios_eprint( log_file, " DISCONNECT_SALTS=%d; TEST_TAUT_SALTS=%d; TEST_TAUT2_SALTS=%d\n", - 0!=(ip->bTautFlags&TG_FLAG_DISCONNECT_SALTS), - 0!=(ip->bTautFlags&TG_FLAG_TEST_TAUT__SALTS), - 0!=(ip->bTautFlags&TG_FLAG_TEST_TAUT2_SALTS)); - - inchi_ios_eprint( log_file, " CHARGED_ACID_TAUT_ONLY=%d MERGE_TAUT_SALTS=%d\n", - 0==(ip->bTautFlags&TG_FLAG_ALLOW_NO_NEGTV_O), - 0!=(ip->bTautFlags&TG_FLAG_MERGE_TAUT_SALTS)); - inchi_ios_eprint( log_file, " DISCONNECT_COORD=%d\n", 0!=(ip->bTautFlags&TG_FLAG_DISCONNECT_COORD) ); -#if ( TEST_RENUMB_ATOMS == 1 ) - inchi_ios_eprint( log_file, "\nDbg: TEST_RENUMB_ATOMS=%d; TEST_RENUMB_NEIGH=%d; TEST_RENUMB_SWITCH=%d\n", - TEST_RENUMB_ATOMS, TEST_RENUMB_NEIGH, TEST_RENUMB_SWITCH ); - inchi_ios_eprint( log_file, " TEST_RENUMB_ATOMS_SAVE_LONGEST=%d\n", - TEST_RENUMB_ATOMS_SAVE_LONGEST); -#endif - -#endif /* ( bRELEASE_VERSION != 1 ) */ - - - return 0; -} - - - -/************************************************************************************/ -void HelpCommandLineParms( INCHI_IOSTREAM *f ) -{ - if ( !f ) - return; - -#if ( bRELEASE_VERSION == 1 ) - - inchi_ios_print_nodisplay( f, -#ifdef TARGET_EXE_USING_API - "%s ver %s%s.\n\nUsage:\ninchi_main inputFile [outputFile [logFile [problemFile]]] [%coption[ %coption...]]\n", - INCHI_NAME, INCHI_VERSION, TARGET_ID_STRING, - INCHI_OPTION_PREFX, INCHI_OPTION_PREFX); -#else - "%s ver %s%s.\n\nUsage:\ninchi-%s inputFile [outputFile [logFile [problemFile]]] [%coption [%coption...]]\n", - INCHI_NAME, INCHI_VERSION, TARGET_ID_STRING, - INCHI_VERSION, INCHI_OPTION_PREFX, INCHI_OPTION_PREFX); -#if ( BUILD_WITH_AMI == 1 ) - inchi_ios_print_nodisplay( f, - "inchi-%s inputFiles... %cAMI [%coption[ %coption...]]\n", - INCHI_VERSION, INCHI_OPTION_PREFX, INCHI_OPTION_PREFX, INCHI_OPTION_PREFX); -#endif -#endif - - inchi_ios_print_nodisplay( f, "\nOptions:\n"); - - inchi_ios_print_nodisplay( f, "\nInput\n"); - inchi_ios_print_nodisplay( f, " STDIO Use standard input/output streams\n"); - inchi_ios_print_nodisplay( f, " InpAux Input structures in %s default aux. info format\n (for use with STDIO)\n", INCHI_NAME); - inchi_ios_print_nodisplay( f, " SDF:DataHeader Read from the input SDfile the ID under this DataHeader\n"); -#if ( ADD_CMLPP == 1 ) - inchi_ios_print_nodisplay( f, " CML Input in CML format (default for input file .CML extension)\n"); -#endif -/* - inchi_ios_print_nodisplay( f, " START:n Skip structures up to n-th one\n"); - inchi_ios_print_nodisplay( f, " END:m Skip structures after m-th one\n"); -*/ -#if ( BUILD_WITH_AMI == 1 ) - inchi_ios_print_nodisplay( f, " AMI Allow multiple input files (wildcards supported)\n"); -#endif - - inchi_ios_print_nodisplay( f, "Output\n"); - inchi_ios_print_nodisplay( f, " AuxNone Omit auxiliary information (default: Include)\n"); - inchi_ios_print_nodisplay( f, " SaveOpt Save custom InChI creation options (non-standard InChI)\n"); - inchi_ios_print_nodisplay( f, " NoLabels Omit structure number, DataHeader and ID from %s output\n", INCHI_NAME); - inchi_ios_print_nodisplay( f, " Tabbed Separate structure number, %s, and AuxInfo with tabs\n", INCHI_NAME); - - /*inchi_ios_print_nodisplay( f, " Compress Compressed output\n"); */ - /*inchi_ios_print_nodisplay( f, " FULL Standard set of options for Full Verbose Output\n");*/ - /*inchi_ios_print_nodisplay( f, " MIN Standard set of options for Minimal Concise Output\n");*/ -#if ( defined(_WIN32) && defined(_MSC_VER) && !defined(COMPILE_ANSI_ONLY) && !defined(TARGET_API_LIB) ) - inchi_ios_print_nodisplay( f, " D Display the structures\n"); - inchi_ios_print_nodisplay( f, " EQU Display sets of identical components\n"); - inchi_ios_print_nodisplay( f, " Fnumber Set display Font size in number of points\n"); -#endif - /*inchi_ios_print_nodisplay( f, " PLAIN Plain text output (Default: XML format)\n");*/ - inchi_ios_print_nodisplay( f, " OutputSDF Convert %s created with default aux. info to SDfile\n", INCHI_NAME); -#if ( SDF_OUTPUT_DT == 1 ) - inchi_ios_print_nodisplay( f, " SdfAtomsDT Output Hydrogen Isotopes to SDfile as Atoms D and T\n"); -#endif -#if ( BUILD_WITH_AMI == 1 ) - inchi_ios_print_nodisplay( f, " AMIOutStd Write output to stdout (in AMI mode)\n"); - inchi_ios_print_nodisplay( f, " AMILogStd Write log to stderr (in AMI mode)\n"); - inchi_ios_print_nodisplay( f, " AMIPrbNone Suppress creation of problem files (in AMI mode)\n"); -#endif - inchi_ios_print_nodisplay( f, "Structure perception\n"); - inchi_ios_print_nodisplay( f, " SNon Exclude stereo (default: include absolute stereo)\n"); - inchi_ios_print_nodisplay( f, " NEWPSOFF Both ends of wedge point to stereocenters (default: a narrow end)\n"); - inchi_ios_print_nodisplay( f, " DoNotAddH All H are explicit (default: add H according to usual valences)\n"); - -#ifndef USE_STDINCHI_API - inchi_ios_print_nodisplay( f, "Stereo perception modifiers (non-standard InChI)\n"); - inchi_ios_print_nodisplay( f, " SRel Relative stereo\n"); - inchi_ios_print_nodisplay( f, " SRac Racemic stereo\n"); - inchi_ios_print_nodisplay( f, " SUCF Use Chiral Flag: On means Absolute stereo, Off - Relative\n"); - - inchi_ios_print_nodisplay( f, "Customizing InChI creation (non-standard InChI)\n"); - inchi_ios_print_nodisplay( f, " SUU Always include omitted unknown/undefined stereo\n"); - inchi_ios_print_nodisplay( f, " SLUUD Make labels for unknown and undefined stereo different\n"); - inchi_ios_print_nodisplay( f, " RecMet Include reconnected metals results\n"); - inchi_ios_print_nodisplay( f, " FixedH Include Fixed H layer\n"); - inchi_ios_print_nodisplay( f, " KET Account for keto-enol tautomerism (experimental)\n"); - inchi_ios_print_nodisplay( f, " 15T Account for 1,5-tautomerism (experimental)\n"); -#endif - - inchi_ios_print_nodisplay( f, "Generation\n"); - inchi_ios_print_nodisplay( f, " Wnumber Set time-out per structure in seconds; W0 means unlimited\n"); - inchi_ios_print_nodisplay( f, " WarnOnEmptyStructure Warn and produce empty %s for empty structure\n", INCHI_NAME); - inchi_ios_print_nodisplay( f, " Key Generate InChIKey\n"); - inchi_ios_print_nodisplay( f, " XHash1 Generate hash extension (to 256 bits) for 1st block of InChIKey\n"); - inchi_ios_print_nodisplay( f, " XHash2 Generate hash extension (to 256 bits) for 2nd block of InChIKey\n"); - - - inchi_ios_print_nodisplay( f, "Conversion\n"); -#ifdef TARGET_EXE_USING_API - inchi_ios_print_nodisplay( f, " InChI2InChI Test mode: Mol/SDfile->%s->%s\n", INCHI_NAME, INCHI_NAME); - inchi_ios_print_nodisplay( f, " InChI2Struct Test mode: Mol/SDfile->%s->Structure->(%s+AuxInfo)\n", INCHI_NAME, INCHI_NAME); -#else - inchi_ios_print_nodisplay( f, " InChI2InChI Convert %s string(s) into %s string(s)\n", INCHI_NAME, INCHI_NAME); - inchi_ios_print_nodisplay( f, " InChI2Struct Convert InChI string(s) to structure(s) in InChI aux.info format\n"); -#endif - -#ifdef BUILD_WITH_ENG_OPTIONS -#if 0 - inchi_ios_print_nodisplay( f, "Engineering options (for testing only)\n"); - inchi_ios_print_nodisplay( f, " NoADP Disable Aggressive Deprotonation\n"); -#if ( FIX_ADJ_RAD == 1 ) - inchi_ios_print_nodisplay( f, " FixRad Fix Adjacent Radicals\n"); -#endif - inchi_ios_print_nodisplay( f, " SPXYZOFF Do not include Phosphines Stereochemistry\n"); - inchi_ios_print_nodisplay( f, " SAsXYZOFF Do not include Arsines Stereochemistry\n"); - inchi_ios_print_nodisplay( f, " FBOFF Do not fix bug leading to missing or undefined sp3 parity\n" ); - inchi_ios_print_nodisplay( f, " FB2OFF Do not fix bugs found after v.1.02b release\n" ); - inchi_ios_print_nodisplay( f, " FNUDOFF Do not fix non-uniform drawing issues\n" ); -#endif -#endif - - -/* -breleaseversion<1 -#else - inchi_ios_print_nodisplay( f, "%s ver %s. Special testing version 12-12-2002.\n", INCHI_NAME, INCHI_VERSION); - inchi_ios_print_nodisplay( f, "\nUsage:\ncINChI09b inputFile [outputFile [logFile [problemFile]]] [%coption[ %coption...]]\n", INCHI_OPTION_PREFX, INCHI_OPTION_PREFX); - inchi_ios_print_nodisplay( f, "\nOptions:\n"); - inchi_ios_print_nodisplay( f, "\tB Basic\n"); - inchi_ios_print_nodisplay( f, "\tT basic Tautomeric\n"); - inchi_ios_print_nodisplay( f, "\tI Isotopic\n"); - inchi_ios_print_nodisplay( f, "\tN Non-isotopic\n"); - inchi_ios_print_nodisplay( f, "\tS Stereo\n"); - inchi_ios_print_nodisplay( f, "\tE Exclude Stereo\n"); - inchi_ios_print_nodisplay( f, "\tD Display the structures\n"); - inchi_ios_print_nodisplay( f, "\tALT produce shorter ALTernative representation (Abc)\n"); - inchi_ios_print_nodisplay( f, "\tSCT produce shorter connection table representation\n"); - inchi_ios_print_nodisplay( f, "\tXML output in xml format\n"); - inchi_ios_print_nodisplay( f, "\tPLAIN output in plain format\n"); - inchi_ios_print_nodisplay( f, "\tMERGE Merge all MOLfiles from the input file into one compound\n"); - inchi_ios_print_nodisplay( f, "\tWnumber time-out per structure in seconds, W0 means unlimited\n"); - inchi_ios_print_nodisplay( f, "\tFnumber set display Font size, points\n"); - inchi_ios_print_nodisplay( f, "\tSREL Relative Stereo\n"); - inchi_ios_print_nodisplay( f, "\tSRAC Racemic Stereo\n"); - inchi_ios_print_nodisplay( f, "\tNOUUSB Omit stereobonds if all are unknown/undefined\n"); - inchi_ios_print_nodisplay( f, "\tNOUUSC Omit stereocenters if all are unknown/undefined\n"); - inchi_ios_print_nodisplay( f, "\tSS Slow Stereo: do not use stereo equivalence\n"); - inchi_ios_print_nodisplay( f, "\tRS Do not test for Redundant Stereo elements\n"); - inchi_ios_print_nodisplay( f, "\tPW Save warning structures in the problems file\n"); - inchi_ios_print_nodisplay( f, "\tPGO Save only all good structures in the problems file\n"); - inchi_ios_print_nodisplay( f, "\tDSB Double Stereo Bonds only (ignore alternating bonds stereo)\n"); - inchi_ios_print_nodisplay( f, "\tRSB:n Min Ring Size for detecting for Stereo Bonds (n=1 => all)\n"); - inchi_ios_print_nodisplay( f, "\tAUXINFO:0 do not output auxiliary information (default:1)\n"); - inchi_ios_print_nodisplay( f, "\tDISCONSALT:0 do not disconnect salts (default:1)\n"); - inchi_ios_print_nodisplay( f, "\tDISCONMETAL:0 do not disconnect metals (default:1)\n"); - inchi_ios_print_nodisplay( f, "\tDISCONMETALCHKVAL:1 do not disconnect if typical valence (default:0)\n"); - inchi_ios_print_nodisplay( f, "\tRECONMETAL:0 do not reconnect metals (default:1)\n"); - inchi_ios_print_nodisplay( f, "\tMOVEPOS:0 do not check moveable positive charges (default:1)\n"); - inchi_ios_print_nodisplay( f, "\tACIDTAUT:n n=1: one H/(-) tautomerism, 2: more (deflt), 0:none\n"); - inchi_ios_print_nodisplay( f, "\tMERGESALTTG:1 merge salt t-groups (default), 0: do not merge\n"); - inchi_ios_print_nodisplay( f, "\tUNCHARGEDACIDS:1 Apply salt (acid) tautomerism in neutral species\n"); - inchi_ios_print_nodisplay( f, "\tO:[suffix] Open all 4 files adding suffix to the inputFile name\n"); - inchi_ios_print_nodisplay( f, "\tOP:outputpath Set output path\n"); - inchi_ios_print_nodisplay( f, "\tMOL input file is a MOLfile (default)\n"); - inchi_ios_print_nodisplay( f, "\tSDF[:DataHeader] Include SDfile data for the header into the results\n"); - inchi_ios_print_nodisplay( f, "\tSDFID extract CAS r.n. in addition to requested SDfile data\n"); - inchi_ios_print_nodisplay( f, "\tSTART:number Start at the given structure ordering number\n"); - inchi_ios_print_nodisplay( f, "\tEND:number Terminate after the given structure ordering number\n"); -*/ -#endif - -} - - - - -/*^^^ */ -/************************************************************************************/ -void HelpCommandLineParmsReduced( INCHI_IOSTREAM *f ) -{ - if ( !f ) - return; - -#if ( bRELEASE_VERSION == 1 ) - - - /*^^^ */ - inchi_ios_print_nodisplay( f, -#ifdef TARGET_EXE_USING_API - "%s ver %s%s.\n\nUsage:\ninchi_main inputFile [outputFile [logFile [problemFile]]] [%coption[ %coption...]]\n", - INCHI_NAME, INCHI_VERSION, TARGET_ID_STRING, - INCHI_OPTION_PREFX, INCHI_OPTION_PREFX); -#else - "%s ver %s%s.\n\nUsage:\nc%s-%s inputFile [outputFile [logFile [problemFile]]] [%coption[ %coption...]]\n", - INCHI_NAME, INCHI_VERSION, TARGET_ID_STRING, - INCHI_NAME, INCHI_VERSION, INCHI_OPTION_PREFX, INCHI_OPTION_PREFX); -#endif - /*^^^ */ - - inchi_ios_print_nodisplay( f, "\nOptions:\n"); - - - inchi_ios_print_nodisplay( f, "\nInput\n"); - inchi_ios_print_nodisplay( f, " STDIO Use standard input/output streams\n"); - inchi_ios_print_nodisplay( f, " InpAux Input structures in %s default aux. info format\n (for use with STDIO)\n", INCHI_NAME); - inchi_ios_print_nodisplay( f, " SDF:DataHeader Read from the input SDfile the ID under this DataHeader\n"); -#if ( ADD_CMLPP == 1 ) - inchi_ios_print_nodisplay( f, " CML Input in CML format (default for input file .CML extension)\n"); -#endif -/* - inchi_ios_print_nodisplay( f, " START:n Skip structures up to n-th one\n"); - inchi_ios_print_nodisplay( f, " END:m Skip structures after m-th one\n"); -*/ -#if ( BUILD_WITH_AMI == 1 ) - inchi_ios_print_nodisplay( f, " AMI Allow multiple input files (wildcards supported)\n"); -#endif - - inchi_ios_print_nodisplay( f, "Output\n"); - inchi_ios_print_nodisplay( f, " AuxNone Omit auxiliary information (default: Include)\n"); - inchi_ios_print_nodisplay( f, " SaveOpt Save custom InChI creation options (non-standard InChI)\n"); - inchi_ios_print_nodisplay( f, " NoLabels Omit structure number, DataHeader and ID from %s output\n", INCHI_NAME); - inchi_ios_print_nodisplay( f, " Tabbed Separate structure number, %s, and AuxInfo with tabs\n", INCHI_NAME); - /* inchi_ios_print_nodisplay( f, " Compress Compressed output\n"); */ -#if ( defined(_WIN32) && defined(_MSC_VER) && !defined(COMPILE_ANSI_ONLY) && !defined(TARGET_API_LIB) ) - inchi_ios_print_nodisplay( f, " D Display the structures\n"); - inchi_ios_print_nodisplay( f, " EQU Display sets of identical components\n"); - inchi_ios_print_nodisplay( f, " Fnumber Set display Font size in number of points\n"); -#endif - inchi_ios_print_nodisplay( f, " OutputSDF Convert %s created with default aux. info to SDfile\n", INCHI_NAME); -#if ( SDF_OUTPUT_DT == 1 ) - inchi_ios_print_nodisplay( f, " SdfAtomsDT Output Hydrogen Isotopes to SDfile as Atoms D and T\n"); -#endif -#if ( BUILD_WITH_AMI == 1 ) - inchi_ios_print_nodisplay( f, " AMIOutStd Write output to stdout (in AMI mode)\n"); - inchi_ios_print_nodisplay( f, " AMILogStd Write log to stderr (in AMI mode)\n"); - inchi_ios_print_nodisplay( f, " AMIPrbNone Suppress creation of problem files (in AMI mode)\n"); -#endif - inchi_ios_print_nodisplay( f, "Structure perception\n"); - inchi_ios_print_nodisplay( f, " SNon Exclude stereo (default: include absolute stereo)\n"); - inchi_ios_print_nodisplay( f, " NEWPSOFF Both ends of wedge point to stereocenters (default: a narrow end)\n"); - inchi_ios_print_nodisplay( f, " DoNotAddH All H are explicit (default: add H according to usual valences)\n"); - -#ifndef USE_STDINCHI_API - inchi_ios_print_nodisplay( f, "Stereo perception modifiers (non-standard InChI)\n"); - inchi_ios_print_nodisplay( f, " SRel Relative stereo\n"); - inchi_ios_print_nodisplay( f, " SRac Racemic stereo\n"); - inchi_ios_print_nodisplay( f, " SUCF Use Chiral Flag: On means Absolute stereo, Off - Relative\n"); - - inchi_ios_print_nodisplay( f, "Customizing InChI creation (non-standard InChI)\n"); - inchi_ios_print_nodisplay( f, " SUU Always include omitted unknown/undefined stereo\n"); - inchi_ios_print_nodisplay( f, " SLUUD Make labels for unknown and undefined stereo different\n"); - inchi_ios_print_nodisplay( f, " RecMet Include reconnected metals results\n"); - inchi_ios_print_nodisplay( f, " FixedH Include Fixed H layer\n"); - inchi_ios_print_nodisplay( f, " KET Account for keto-enol tautomerism (experimental)\n"); - inchi_ios_print_nodisplay( f, " 15T Account for 1,5-tautomerism (experimental)\n"); -#endif - - inchi_ios_print_nodisplay( f, "Generation\n"); - inchi_ios_print_nodisplay( f, " Wnumber Set time-out per structure in seconds; W0 means unlimited\n"); - inchi_ios_print_nodisplay( f, " WarnOnEmptyStructure Warn and produce empty %s for empty structure\n", INCHI_NAME); - inchi_ios_print_nodisplay( f, " Key Generate InChIKey\n"); - inchi_ios_print_nodisplay( f, " XHash1 Generate hash extension (to 256 bits) for 1st block of InChIKey\n"); - inchi_ios_print_nodisplay( f, " XHash2 Generate hash extension (to 256 bits) for 2nd block of InChIKey\n"); - -#ifdef BUILD_WITH_ENG_OPTIONS -#if 0 - inchi_ios_print_nodisplay( f, "Engineering options (for testing only)\n"); - inchi_ios_print_nodisplay( f, " NoADP Disable Aggressive Deprotonation\n"); -#if ( FIX_ADJ_RAD == 1 ) - inchi_ios_print_nodisplay( f, " FixRad Fix Adjacent Radicals\n"); -#endif - inchi_ios_print_nodisplay( f, " SPXYZOFF Do not include Phosphines Stereochemistry\n"); - inchi_ios_print_nodisplay( f, " SAsXYZOFF Do not include Arsines Stereochemistry\n"); - inchi_ios_print_nodisplay( f, " FBOFF Do not fix bug leading to missing or undefined sp3 parity\n" ); - inchi_ios_print_nodisplay( f, " FB2OFF Do not fix bugs found after v.1.02b release\n" ); - inchi_ios_print_nodisplay( f, " FNUDOFF Do not fix non-uniform drawing issues\n" ); -#endif -#endif - - - -#endif /* #if ( bRELEASE_VERSION == 1 ) */ -} - - - - -#define fprintf2 inchi_fprintf - -#ifndef TARGET_API_LIB -/************************************************************************************/ -int OpenFiles( FILE **inp_file, FILE **output_file, FILE **log_file, FILE **prb_file, INPUT_PARMS *ip ) -{ -/* - -- Files -- - ip->path[0] => Input - ip->path[1] => Output (INChI) - ip->path[2] => Log - ip->path[3] => Problem structures - ip->path[4] => Errors file (ACD Labs) - -*/ - /* logfile -- open as early as possible */ - if ( !ip->path[2] || !ip->path[2][0] ) { - fprintf2( stderr, "%s version %s%s%s\n", INCHI_NAME, INCHI_VERSION, TARGET_ID_STRING, bRELEASE_VERSION? "":""); /* (Pre-release, for evaluation purposes only)" ); */ - fprintf2( stderr, "Log file not specified. Using standard error output.\n"); - *log_file = stderr; - } else - if ( !(*log_file = fopen( ip->path[2], "w" ) ) ) { - fprintf2( stderr, "%s version %s%s%s\n", INCHI_NAME, INCHI_VERSION, TARGET_ID_STRING, bRELEASE_VERSION? "":""); /* (Pre-release, for evaluation purposes only)" );*/ - fprintf2( stderr, "Cannot open log file '%s'. Using standard error output.\n", ip->path[2] ); - *log_file = stderr; - } else { - fprintf2( *log_file, "%s version %s%s%s\n", INCHI_NAME, INCHI_VERSION, TARGET_ID_STRING, bRELEASE_VERSION? "":""); /* (Pre-release, for evaluation purposes only)" );*/ - fprintf2( *log_file, "Opened log file '%s'\n", ip->path[2] ); - } - /* input file */ - if ( (ip->nInputType == INPUT_MOLFILE || ip->nInputType == INPUT_SDFILE || - ip->nInputType == INPUT_CMLFILE || ip->nInputType == INPUT_INCHI || - ip->nInputType == INPUT_INCHI_PLAIN ) && ip->num_paths > 0 ) - { - const char *fmode = NULL; -#if ( defined(_MSC_VER)&&defined(_WIN32) || defined(__BORLANDC__)&&defined(__WIN32__) || defined(__GNUC__)&&defined(__MINGW32__)&&defined(_WIN32) ) - /* compilers that definitely allow fopen "rb" (binary read) mode */ - fmode = "rb"; - if ( !ip->path[0] || !ip->path[0][0] || !(*inp_file = fopen( ip->path[0], "rb" ) ) ) - { - fprintf2( *log_file, "Cannot open input file '%s'. Terminating.\n", ip->path[0]? ip->path[0] : "" ); - goto exit_function; - } - else - { - if ( ip->nInputType == INPUT_CMLFILE ) - { - int c; -#ifdef CML_DEBUG - printf ("cr %d lf %d ret %d\n", (int) '\r', (int) '\f', (int) '\n'); -#endif - /* read up to the end of the first line */ - while( (c = fgetc( *inp_file )) && c != EOF && c != '\n' && c != '\r' ) - ; - if ( c == '\r' || c == EOF ) - { - /* text file contains CR; close and reopen as "text" */ - fclose( *inp_file ); - if ( !(*inp_file = fopen( ip->path[0], "r" ) ) ) - { - fprintf2( *log_file, "Cannot open input file '%s' (2nd attempt). Terminating.\n", ip->path[0] ); - goto exit_function; - } - fprintf2( *log_file, "Opened input file '%s'\n", ip->path[0] ); - fmode = "r"; - } - else - { - fclose( *inp_file ); - if ( !(*inp_file = fopen( ip->path[0], "rb" ) ) ) - { - fprintf2( *log_file, "Cannot open input file '%s' (2nd attempt). Terminating.\n", ip->path[0] ); - goto exit_function; - } - fprintf2( *log_file, "Opened input file '%s': no CR.\n", ip->path[0] ); - fmode = "rb"; - } - } /* CML */ - else - fprintf2( *log_file, "Opened input file '%s'\n", ip->path[0] ); - - } -#else - if ( !ip->path[0] || !ip->path[0][0] || !(*inp_file = fopen( ip->path[0], "r" ) ) ) { - fprintf2( *log_file, "Cannot open input file '%s'. Terminating.\n", ip->path[0]? ip->path[0] : "" ); - goto exit_function; - } else { - fprintf2( *log_file, "Opened input file '%s'\n", ip->path[0] ); - } - fmode = "r"; -#endif - DetectInputINChIFileType( inp_file, ip, fmode ); - } else - if ( (ip->nInputType != INPUT_MOLFILE && ip->nInputType != INPUT_SDFILE && ip->nInputType != INPUT_CMLFILE && ip->nInputType != INPUT_INCHI - /*^^^ post-1.02b */ - && ip->nInputType != INPUT_INCHI_PLAIN - )) { - fprintf2( *log_file, "Input file type not specified. Terminating.\n"); - goto exit_function; - } else { - fprintf2( *log_file, "Input file not specified. Using standard input.\n"); - *inp_file = stdin; - } - /* output file */ - if ( !ip->path[1] || !ip->path[1][0] ) { - fprintf2( *log_file, "Output file not specified. Using standard output.\n"); - *output_file = stdout; - } else { - if ( !(*output_file = fopen( ip->path[1], "w" ) ) ) { - fprintf2( *log_file, "Cannot open output file '%s'. Terminating.\n", ip->path[1] ); - goto exit_function; - } else { - fprintf2( *log_file, "Opened output file '%s'\n", ip->path[1] ); - if ( (ip->bINChIOutputOptions & (INCHI_OUT_PLAIN_TEXT)) && - *inp_file != stdin && - !(ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY) && - !ip->bNoStructLabels && - !(ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT)) { - PrintFileName( "* Input_File: \"%s\"\n", *output_file, ip->path[0] ); - } - } - } - /* problem file */ - if ( ip->path[3] && ip->path[3][0] ) { - const char *fmode = "w"; -#if ( defined(_MSC_VER)&&defined(_WIN32) || defined(__BORLANDC__)&&defined(__WIN32__) || defined(__GNUC__)&&defined(__MINGW32__)&&defined(_WIN32) ) - if ( ip->nInputType != INPUT_CMLFILE ) { - fmode = "wb"; - } -#endif - if ( !(*prb_file = fopen( ip->path[3], fmode ) ) ) { - fprintf2( *log_file, "Cannot open problem file '%s'. Terminating.\n", ip->path[3] ); - goto exit_function; - } else { - fprintf2( *log_file, "Opened problem file '%s'\n", ip->path[3] ); - } - } - return 1; /* success */ - -exit_function: - return 0; /* failed */ - -} -#define NUM_VERSIONS 7 -#define LEN_VERSIONS 64 -/*******************************************************************/ -static int bMatchOnePrefix( int len, char *str, int lenPrefix[], - char strPrefix[][LEN_VERSIONS], int numPrefix); -/*******************************************************************/ -static int bMatchOnePrefix( int len, char *str, int lenPrefix[], - char strPrefix[][LEN_VERSIONS], int numPrefix) -{ - int i; - for ( i = 0; i < numPrefix; i ++ ) { - if ( len >= lenPrefix[i] && - !memcmp( str, strPrefix[i], lenPrefix[i] ) ) { - return 1; - } - } - return 0; -} -/*******************************************************************/ -int DetectInputINChIFileType( FILE **inp_file, INPUT_PARMS *ip, const char *fmode ) -{ - char szLine[256], ret = 0; - static char szPlnVersion[NUM_VERSIONS][LEN_VERSIONS]; /* = "INChI:1.1Beta/";*/ - static int lenPlnVersion[NUM_VERSIONS]; - static char szPlnAuxVer[NUM_VERSIONS][LEN_VERSIONS]; /* = "AuxInfo:1.1Beta/";*/ - static int lenPlnAuxVer[NUM_VERSIONS]; - static char szXmlVersion[NUM_VERSIONS][LEN_VERSIONS]; /* = "";*/ - static int lenXmlVersion[NUM_VERSIONS]; - static char szXmlStruct[LEN_VERSIONS] = "nInputType == INPUT_INCHI_XML || ip->nInputType == INPUT_INCHI_PLAIN || ip->nInputType == INPUT_INCHI ) { - return 1; - } - if ( !bInitilized ) { - lenPlnVersion[0] = sprintf( szPlnVersion[0], "%s=%s/", INCHI_NAME, INCHI_VERSION ); - lenPlnVersion[1] = sprintf( szPlnVersion[1], "INChI=1.12Beta/" ); - lenPlnVersion[2] = sprintf( szPlnVersion[2], "INChI=1.0RC/" ); - lenPlnVersion[3] = sprintf( szPlnVersion[3], "InChI=1.0RC/" ); - lenPlnVersion[4] = sprintf( szPlnVersion[4], "InChI=1/" ); - lenPlnVersion[5] = sprintf( szPlnVersion[5], "MoChI=1a/" ); - lenPlnVersion[6] = sprintf( szPlnVersion[6], "InChI=1S/" ); - lenPlnAuxVer[0] = sprintf( szPlnAuxVer[0], "AuxInfo=%s/", INCHI_VERSION ); - lenPlnAuxVer[1] = sprintf( szPlnAuxVer[1], "AuxInfo=1.12Beta/" ); - lenPlnAuxVer[2] = sprintf( szPlnAuxVer[2], "AuxInfo=1.0RC/" ); - lenPlnAuxVer[3] = sprintf( szPlnAuxVer[3], "AuxInfo=1.0RC/" ); - lenPlnAuxVer[4] = sprintf( szPlnAuxVer[4], "AuxInfo=1/" ); - lenPlnAuxVer[5] = sprintf( szPlnAuxVer[5], "AuxInfo=1a/" ); - lenPlnAuxVer[6] = sprintf( szPlnAuxVer[6], "AuxInfo=1/" ); - lenXmlVersion[0] = sprintf( szXmlVersion[0], "<%s version=\"%s\">", INCHI_NAME, INCHI_VERSION ); - lenXmlVersion[1] = sprintf( szXmlVersion[1], "" ); - lenXmlVersion[2] = sprintf( szXmlVersion[2], "" ); - lenXmlVersion[3] = sprintf( szXmlVersion[3], "" ); - lenXmlVersion[4] = sprintf( szXmlVersion[4], "" ); - lenXmlVersion[5] = sprintf( szXmlVersion[5], "" ); - lenXmlVersion[6] = sprintf( szXmlVersion[6], "" ); - lenXmlIdentVer[0] = sprintf( szXmlIdentVer[0], "= 2 && !bINChI_xml ) { - ip->nInputType = INPUT_INCHI_PLAIN; - ret = 1; - } else - if ( !bINChI_plain && bINChI_xml >= 3 ) { - ip->nInputType = INPUT_INCHI_XML; - ret = 1; - } -/*exit_function:*/ - fclose ( *inp_file ); - *inp_file = fopen( ip->path[0], fmode ); - return ret; -} -#undef NUM_VERSIONS -#undef LEN_VERSIONS - -#endif /* TARGET_API_LIB */ diff --git a/INCHI-1-SRC/INCHI/common/ichiprt1.c b/INCHI-1-SRC/INCHI/common/ichiprt1.c deleted file mode 100644 index 3691e56..0000000 --- a/INCHI-1-SRC/INCHI/common/ichiprt1.c +++ /dev/null @@ -1,4171 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include - -#include "mode.h" - -#include "inpdef.h" -#include "ichi.h" -#include "strutil.h" -#include "util.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "ichicant.h" -#include "ichicano.h" -#include "ichicomn.h" -#include "ichister.h" - -#include "ichicomp.h" -#include "ichimain.h" -#include "ichimake.h" - -#include "ichi_io.h" - -int PrintXmlStartTag(char *pStr, - int indent, int bEnd, const char *tag, - const char *l1, int v1, const char *l2, int v2, - const char *l3, int v3, const char *l4, int v4, - const char *l5, int v5, const char *l6, int v6); -int Needs2addXmlEntityRefs(const char *s ); -int AddXmlEntityRefs(const char *p, char *d ); -#if ( TEST_RENUMB_ATOMS == 1 ) /* { */ -int CompareStereoINChI( INChI_Stereo *s1, INChI_Stereo *s2 ); -#endif - -int str_LineStart(const char *tag, char *tag2, int val2, char *pStr, int ind ); -int str_LineEnd(const char *tag, int tot_len, int nStrLen, - int *bOverflow, char *pStr, int ind, int bPlainTextTags ); -int CleanOrigCoord(MOL_COORD szCoord, int delim ); -int WriteOrigCoord(int num_inp_atoms, MOL_COORD *szMolCoord, int *i, - char *szBuf, int buf_len); -int WriteOrigAtoms(int num_inp_atoms, inp_ATOM *at, int *i, - char *szBuf, int buf_len, - STRUCT_DATA *sd); -int WriteOrigBonds(int num_inp_atoms, inp_ATOM *at, int *i, - char *szBuf, int buf_len, - STRUCT_DATA *sd); - -void GetSaveOptLetters(unsigned char save_opt_bits, char* let1, char* let2); - - -char VER_STRING[64]; - -const char sCompDelim[] = ";"; /* component delimiter */ -const char sIdenticalValues[] = "*"; /* identical component */ -const char x_space[] = " "; - -/* xml output: words & additional tags */ -const char x_inchi[] = INCHI_NAME; -const char x_inchi_ver[] = "version"; /* "InChI.version"; */ -const char x_curr_ver[] = INCHI_VERSION; - -const char x_structure[] = "structure"; -const char x_number[] = "number"; -const char x_header[] = "id.name"; -const char x_value[] = "id.value"; - -const char x_empty[] = ""; - -const char x_type[] = "type"; - -const char x_message[] = "message"; -const char x_text[] = "value"; - -const char x_ferr[] = "fatal (aborted)"; -const char x_err[] = "error (no InChI)"; -const char x_warn[] = "warning"; - -const char x_basic[] = "identifier"; -const char x_tautomeric[] = "mobile-H"; -const char x_reconnected[] = "reconnected"; - -const char x_ver[] = "version"; - -const char x_type_alpha[] = "alpha"; -const char x_type_numer[] = "numeric"; -const char x_type_predec[] = "sct"; -const char x_type_normal[] = "normal"; -const char x_type_short[] = "compressed"; -const char x_basic_layer[] = "basic"; - -const char x_aux_basic[] = "identifier.auxiliary-info"; -const char x_aux_comm[] = "!-- This section is NOT a part of the identifier, it is not unique --"; - -const char x_ign_uu_sp2[] = "omit_undef_dbond"; -const char x_ign_uu_sp3[] = "omit_undef_sp3"; - -const char x_line_opening[] = "<"; -const char x_line_closing[] = ""; - -const char x_abs[] = "1"; -const char x_rel[] = "2"; -const char x_rac[] = "3"; - -#define MAX_TAG_LEN 64 - -typedef struct tagInchiTag -{ - const char *szPlainLabel; - const char *szPlainComment; - const char *szXmlLabel; - int bAlwaysOutput; -} INCHI_TAG; - -/* identifier */ -const INCHI_TAG IdentLbl[] = -{ - /* prefixes: may be combined in this order */ -/* IL_FIXH_ORD, */ { "/", "fixed_H", "fixed-H", 0 }, /* fixed H */ -/* IL_ISOT_ORD, */ { "/", "isotopic", "isotopic", 0 }, /* isotopic */ -/* IL_STER_ORD, */ { "/", "stereo", "stereo", 0 }, /* stereo */ - /* items */ -/* IL_VERS_ORD, */ { "" , "version", "version", 1 }, -/* IL_FML__ORD, */ { "/", "formula", "formula", 1 }, /* basic part formula */ -/* IL_CONN_ORD, */ { "/c", "connections", "connections", 1 }, -/* IL_ALLH_ORD, */ { "/h", "H_atoms", "H", 1 }, -/* IL_CHRG_ORD, */ { "/q", "charge", "charge", 1 }, -/* IL_PROT_ORD, */ { "/p", "protons", "protons", 0 }, - /* stereo */ -/* IL_DBND_ORD, */ { "/b", "dbond", "dbond", 0 }, -/* IL_SP3S_ORD, */ { "/t", "sp3", "sp3", 0 }, -/* IL_INVS_ORD, */ { "/m", "sp3:inverted", "abs.inverted", 0 }, /* mirrored */ -/* IL_TYPS_ORD, */ { "/s", "type (1=abs, 2=rel, 3=rac)", "type", 0 }, /* stereo type */ - /* isotopic */ -/* IL_ATMS_ORD, */ { "/i", "atoms", "atoms", 1 }, - /* isotopic mobile H only */ -/* IL_XCGA_ORD, */ { "/h", "exchangeable_H", "H-isotopic", 1 }, - /* fixed H only */ -/* IL_FMLF_ORD, */ { "/f", "formula", "formula", 1 }, /* fixed H formula */ -/* IL_HFIX_ORD, */ { "/h", "H_fixed" , "H-fixed" , 1 }, /* fixed-H */ -/* IL_TRNS_ORD, */ { "/o", "transposition", "transposition", 0 }, /* order */ -/* IL_REC__ORD, */ { "/r", "reconnected bond(s) to metal(s) formula", "formula", 0 } -}; - -/* - - Parsing plain text InChI (FML is a chemical formula) - ======================== - - 1.12Beta/FML /i /f[FML] /i [/o] /rFML /i /f[FML] /i [/o] end - | | | | | | | | | -Labels | chqpbtms | hbtms | hqbtms | btms | chqpbtms | hbtms | hqbtms | btms | -inside: | | | | | | | | | - | non-iso- | iso- | fix- | iso- | non-iso- | iso- | fix- | iso- | -meaning: | topic | topic | ed H | topic | topic | topic | ed H | topic | - |----------+-------+--------+---------|----------+-------+--------+---------| - | mobile-H | fixed-H | mobile-H | fixed-H | - |----------+-------+--------+---------|----------+-------+--------+---------| - | | | - | normal or disconected metal | reconnected bonds to metal | - |_____________________________________|_____________________________________| - - meanings of h: - - /h - immobile H & mobile H group(s) - /i/h - exchangeable isotopic H (common) - /f/h - fixed-H - /f/i/h - never happens - -*/ - -typedef enum tagIdentLblOrd -{ - IL_FIXH_ORD, - IL_ISOT_ORD, - IL_STER_ORD, - - IL_VERS_ORD, - IL_FML__ORD, - IL_CONN_ORD, - IL_ALLH_ORD, - IL_CHRG_ORD, - IL_PROT_ORD, - - IL_DBND_ORD, - IL_SP3S_ORD, - IL_INVS_ORD, - IL_TYPS_ORD, - - IL_ATMS_ORD, - - IL_XCGA_ORD, - - IL_FMLF_ORD, - IL_HFIX_ORD, - IL_TRNS_ORD, - IL_REC__ORD, - - IL_MAX_ORD /* max number of tags */ -} IDENT_LBL_ORD; - -typedef enum tagIdentLblBit -{ - IL_FIXH = 1 << IL_FIXH_ORD, - IL_ISOT = 1 << IL_ISOT_ORD, - IL_STER = 1 << IL_STER_ORD, - - IL_VERS = 1 << IL_VERS_ORD, - IL_FML_ = 1 << IL_FML__ORD, - IL_CONN = 1 << IL_CONN_ORD, - IL_ALLH = 1 << IL_ALLH_ORD, - IL_CHRG = 1 << IL_CHRG_ORD, - IL_PROT = 1 << IL_PROT_ORD, - - IL_DBND = 1 << IL_DBND_ORD, - IL_SP3S = 1 << IL_SP3S_ORD, - IL_INVS = 1 << IL_INVS_ORD, - IL_TYPS = 1 << IL_TYPS_ORD, - - IL_ATMS = 1 << IL_ATMS_ORD, - - IL_XCGA = 1 << IL_XCGA_ORD, - - IL_FMLF = 1 << IL_FMLF_ORD, - IL_HFIX = 1 << IL_HFIX_ORD, - IL_TRNS = 1 << IL_TRNS_ORD, - IL_REC_ = 1 << IL_REC__ORD - -} IDENT_LBL_BIT; - -/* aux info */ -const INCHI_TAG AuxLbl[] = -{ - /* prefixes may be combined in this order */ -/* AL_FIXH_ORD, */ { "/", "fixed_H", "fixed-H", 0 }, /* fixed-H */ -/* AL_ISOT_ORD, */ { "/", "isotopic", "isotopic", 0 }, /* isotopic */ -/* AL_STER_ORD, */ { "/", "abs_stereo_inverted", "stereo.abs.inverted", 0 }, /* inv abs sp3 stereo */ -/* AL_REVR_ORD, */ { "/", "reversibility", "reversibility", 0 }, /* reversibility */ - /* items */ -/* AL_VERS_ORD, */ { "", "version", "version", 1 }, -/* AL_NORM_ORD, */ { "/", "normalization_type", "norm-type", 1 }, -/* AL_ANBR_ORD, */ { "/N:", "original_atom_numbers", "atom.orig-nbr", 1 }, -/* AL_AEQU_ORD, */ { "/E:", "atom_equivalence", "atom.equivalence", 0 }, -/* AL_GEQU_ORD, */ { "/gE:", "group_equivalence", "group.equivalence", 0 }, - /* inv abs sp3 stereo */ -/* AL_SP3I_ORD, */ { "/it:", "sp3", "sp3", 0 }, -/* AL_SP3N_ORD, */ { "/iN:", "original_atom_numbers", "atom.orig-nbr", 0 }, - -/* AL_CRV__ORD, */ { "/CRV:", "charge_radical_valence", "charges-rad-val", 0 }, - /* reversibility */ -/* AL_ATMR_ORD, */ { "/rA:", "atoms", "atoms", 0 }, -/* AL_BNDR_ORD, */ { "/rB:", "bonds", "bonds", 0 }, -/* AL_XYZR_ORD, */ { "/rC:", "xyz", "xyz", 0 }, - /* fixed-H only */ -/* AL_FIXN_ORD, */ { "/F:", "original_atom_numbers", "atom.orig-nbr", 1 }, - /* isotopic only */ -/* AL_ISON_ORD, */ { "/I:", "original_atom_numbers", "atom.orig-nbr", 1 }, - -/* AL_REC__ORD, */ { "/R:", "reconnected bond(s) to metal(s) part", "", 1 } - -}; - -typedef enum tagAuxLblOrd -{ - AL_FIXH_ORD, - AL_ISOT_ORD, - AL_STER_ORD, - AL_REVR_ORD, - - AL_VERS_ORD, - AL_NORM_ORD, - AL_ANBR_ORD, - AL_AEQU_ORD, - AL_GEQU_ORD, - - AL_SP3I_ORD, - AL_SP3N_ORD, - - AL_CRV__ORD, - - AL_ATMR_ORD, - AL_BNDR_ORD, - AL_XYZR_ORD, - - AL_FIXN_ORD, - - AL_ISON_ORD, - - AL_REC__ORD, - - AL_MAX_ORD /* max number of tags */ - -} AUX_LBL_ORD; - - -typedef enum tagAuxLblBit -{ - AL_FIXH = 1 << AL_FIXH_ORD, - AL_ISOT = 1 << AL_ISOT_ORD, - AL_STER = 1 << AL_STER_ORD, - AL_REVR = 1 << AL_REVR_ORD, - - AL_VERS = 1 << AL_VERS_ORD, - AL_NORM = 1 << AL_NORM_ORD, - AL_ANBR = 1 << AL_ANBR_ORD, - AL_AEQU = 1 << AL_AEQU_ORD, - AL_GEQU = 1 << AL_GEQU_ORD, - - AL_SP3I = 1 << AL_SP3I_ORD, - AL_SP3N = 1 << AL_SP3N_ORD, - - AL_CRV_ = 1 << AL_CRV__ORD, - - AL_ATMR = 1 << AL_ATMR_ORD, - AL_BNDR = 1 << AL_BNDR_ORD, - AL_XYZR = 1 << AL_XYZR_ORD, - - AL_FIXN = 1 << AL_FIXN_ORD, - - AL_ISON = 1 << AL_ISON_ORD, - - AL_REC_ = 1 << AL_REC__ORD - -} AUX_LBL_BIT; - -const int MAX_TAG_NUM = inchi_max((int)IL_MAX_ORD, (int)AL_MAX_ORD); - -char *szGetTag(const INCHI_TAG *Tag, int nTag, int bTag, char *szTag, int *bAlways); - -#define SP(N) (x_space+sizeof(x_space)-1-(N)) -/**********************************************************************************************/ -typedef struct tagXmlEntityRef -{ - char nChar; - const char *pRef; -} X_REF; -const X_REF xmlRef[] = { {'<', "<"}, {'&', "&"}, {'>', ">"}, {'"', """}, {'\'', "'"}, {0, NULL}, }; -const char szRefChars[sizeof(xmlRef)/sizeof(xmlRef[0])] = {'<', '&', '>', '"', '\'', '\0' }; -/**********************************************************************************************/ -int PrintXmlStartTag(char *pStr, int indent, int bEnd, const char *tag, - const char *l1, int v1, const char *l2, int v2, - const char *l3, int v3, const char *l4, int v4, - const char *l5, int v5, const char *l6, int v6) -{ - int len=0; - if ( tag ) { - len += sprintf( pStr+len, "%s<%s", SP(indent), tag); - } - if ( l1 ) { - len += sprintf( pStr+len, " %s=\"%d\"", l1, v1); - } - if ( l2 ) { - len += sprintf( pStr+len, " %s=\"%d\"", l2, v2); - } - if ( l3 ) { - len += sprintf( pStr+len, " %s=\"%d\"", l3, v3); - } - if ( l4 ) { - len += sprintf( pStr+len, " %s=\"%d\"", l4, v4); - } - if ( l5 ) { - len += sprintf( pStr+len, " %s=\"%d\"", l5, v5); - } - if ( l6 ) { - len += sprintf( pStr+len, " %s=\"%d\"", l6, v6); - } - if ( (bEnd & 3) ) { - len += sprintf( pStr+len, "%s%s", (bEnd & 1)?"/":"", (bEnd & 2)?">":""); - } - return len; -} - -/**********************************************************************************************/ -int Needs2addXmlEntityRefs( const char *s ) -{ - int len = 0; - const X_REF *q = xmlRef, *r; - const char *p; - if ( s && *s ) { - for ( q = xmlRef, len = 0; q->nChar; q ++ ) { - for ( p = s; p = strchr( p, q->nChar ); p ++ ) { - if ( q->nChar == '&' ) { - for ( r = xmlRef; r->nChar; r ++ ) { - if ( !memcmp( p, r->pRef, strlen(r->pRef) ) ) - goto DoNotSubstitute; - } - } - len += strlen(q->pRef)-1; -DoNotSubstitute:; - } - } - if ( len ) { - len += strlen( s ); - } - } - return len; -} - -/**********************************************************************************************/ -int AddXmlEntityRefs( const char *p, char *d ) -{ - int len_d, n; - const X_REF *q = xmlRef, *r; - - len_d = 0; - while ( *p ) { - n = strcspn( p, szRefChars ); - if ( n > 0 ) { - /* first n characters of p do not contain referenced chars; copy them */ - strncpy( d+len_d, p, n ); /* does not have zero termination */ - len_d += n; /* new destination length */ - p += n; /* position of the referenced char in the source */ - } - if ( *p ) { - if ( *p == '&' ) { - for ( r = xmlRef; r->nChar; r ++ ) { - if ( !memcmp( p, r->pRef, strlen(r->pRef) ) ) { - d[len_d++] = *p; - goto DoNotSubstitute; - } - } - } - q = xmlRef + (strchr( szRefChars, UCINT *p) - szRefChars); - strcpy( d+len_d, q->pRef ); /* add entity reference and zero termination */ - len_d += strlen( d + len_d ); /* new destination length */ -DoNotSubstitute: - p ++; - } else { - d[len_d] = '\0'; /* add zero termination */ - } - - } - return len_d; -} - -/**********************************************************************************************/ -int OutputINChIXmlRootStartTag( INCHI_IOSTREAM *output_file ) -{ - char pStr[128]; - sprintf( pStr, "<%s %s=\"%s\">", x_inchi, x_inchi_ver, x_curr_ver ); - inchi_ios_print_nodisplay( output_file, "%s\n", pStr ); - return 0; -} - -/**********************************************************************************************/ -int OutputINChIXmlRootEndTag( INCHI_IOSTREAM *output_file ) -{ - char pStr[128]; - sprintf( pStr, "", x_inchi ); - inchi_ios_print_nodisplay( output_file, "%s\n", pStr ); - return 0; -} - -/**********************************************************************************************/ -int OutputINChIXmlStructStartTag( INCHI_IOSTREAM *output_file, char *pStr, int ind /* indent*/, - int nStrLen, int bNoStructLabels, - int num_input_struct, const char *szSdfLabel, const char *szSdfValue ) -{ - char szBuf[64]; - int nEstLen1; - int nEstLen2; - int ret = 0; - int tot_len; - char *pSdfLabel = NULL, *pSdfValue = NULL, *p; - /* substitute special characters (see szRefChars[]) with xml Entity References */ - int len; - if ( bNoStructLabels ) { - /* no labela at all */ - inchi_ios_print( output_file, "%s\n", "" ); /* empty line */ - tot_len = 0; - tot_len += sprintf(pStr+tot_len, "%s<%s", SP(ind), x_structure); - tot_len += sprintf(pStr+tot_len, ">" ); - inchi_ios_print( output_file, "%s\n", pStr ); - ret = 1; /* success */ - } else - if ( !(szSdfLabel && szSdfLabel[0]) && !(szSdfValue && szSdfValue[0]) ) { - /* only structure number if present */ - inchi_ios_print( output_file, "%s\n", "" ); /* empty line */ - tot_len = 0; - tot_len += sprintf(pStr+tot_len, "%s<%s", SP(ind), x_structure); - if ( num_input_struct > 0 ) { - tot_len += sprintf(pStr+tot_len, " %s=\"%d\"", x_number, num_input_struct); - } - tot_len += sprintf(pStr+tot_len, ">" ); - inchi_ios_print( output_file, "%s\n", pStr ); - ret = 1; /* success */ - } else { - if ( len = Needs2addXmlEntityRefs( szSdfLabel ) ) { - if ( p = (char*) inchi_malloc( len+1 ) ) { - AddXmlEntityRefs( szSdfLabel, p ); - szSdfLabel = pSdfLabel = p; - } - } - if ( len = Needs2addXmlEntityRefs( szSdfValue ) ) { - if ( p = (char*) inchi_malloc( len+1 ) ) { - AddXmlEntityRefs( szSdfValue, p ); - szSdfValue = pSdfValue = p; - } - } - nEstLen1 = ind + 1 + sizeof(x_structure)-1 - + 1 + sizeof(x_number)-1 + 1 + sprintf(szBuf,"\"%d\"", num_input_struct) + 2; - nEstLen2 = 1 + sizeof(x_header)-1 + 1 + 2 + (szSdfLabel? strlen(szSdfLabel):0) - + 1 + sizeof(x_value) -1 + 1 + 2 + (szSdfValue? strlen(szSdfValue):0) + 2; - if ( nEstLen1 <= nStrLen ) { - inchi_ios_print( output_file, "%s\n", "" ); /* empty line */ - tot_len = 0; - tot_len += sprintf(pStr+tot_len, "%s<%s", SP(ind), x_structure); - tot_len += sprintf(pStr+tot_len, " %s=\"%d\"", x_number, num_input_struct); - if ( nEstLen1 + nEstLen2 <= nStrLen ) { - tot_len += sprintf(pStr+tot_len, " %s=\"%s\"", x_header, szSdfLabel? szSdfLabel:x_empty); - tot_len += sprintf(pStr+tot_len, " %s=\"%s\"", x_value, szSdfValue? szSdfValue:x_empty); - } - tot_len += sprintf(pStr+tot_len, ">" ); - inchi_ios_print( output_file, "%s\n", pStr ); - ret = 1; /* success */ - } - if ( pSdfValue ) { - inchi_free ( pSdfValue ); - } - if ( pSdfLabel ) { - inchi_free( pSdfLabel ); - } - } - return ret; /* 0 => Buffer overflow */ -} - -/**********************************************************************************************/ -int OutputINChIXmlStructEndTag( INCHI_IOSTREAM *output_file, char *pStr, int nStrLen, int ind ) -{ - if ( output_file && pStr ) - { - int nEstLen1 = ind + 1 + 1 + sizeof(x_structure)-1 + 2; - if ( nEstLen1 <= nStrLen ) - { - sprintf(pStr, "%s", SP(ind), x_structure); - inchi_ios_print( output_file, "%s\n", pStr ); - return 1; - } - } - return 0; -} - -/**********************************************************************************************/ -int OutputINChIXmlError( INCHI_IOSTREAM *output_file, char *pStr, int nStrLen, int ind, - /*int nErrorNumber,*/ char *pErrorText, int bError ) -{ - /* char szBuf[64]; */ - const char *pErr; - char *pNewErrorText=NULL, *szErrorText = pErrorText; - int nEstLen, len=0, ret = 0; - - switch( bError ) { - case _IS_WARNING: - pErr = x_warn; - break; - case _IS_ERROR: - pErr = x_err; - break; - default: /* _IS_FATAL */ - pErr = x_ferr; - break; - } - -#if ( ENTITY_REFS_IN_XML_MESSAGES == 1 ) - /* insert xml entity references if necessary */ - if ( len = Needs2addXmlEntityRefs( szErrorText ) ) { - if ( pNewErrorText = (char*) inchi_malloc( len+1 ) ) { - AddXmlEntityRefs( szErrorText, pNewErrorText ); - szErrorText = pNewErrorText; - } - } -#else - szErrorText = pErrorText; -#endif - - - nEstLen = ind + 1 + sizeof(x_message)-1 - + 1 + sizeof(x_type)-1 + 1 + 1 + strlen(pErr)-1 - /* + 1 + sizeof(x_code)-1 + 1 + sprintf(szBuf, "%d", nErrorNumber) */ - + 1 + sizeof(x_text)-1 + 1 + 1 + strlen(szErrorText) + 2; - if ( nEstLen <= nStrLen ) { - /* - sprintf( pStr, "%s<%s %s=\"%s\" %s=\"%d\" %s=\"%s\"/>", - SP(ind), x_message, x_type, pErr, x_code, nErrorNumber, x_text, szErrorText ); - */ - sprintf( pStr, "%s<%s %s=\"%s\" %s=\"%s\"/>", - SP(ind), x_message, x_type, pErr, x_text, szErrorText ); - inchi_ios_print( output_file, "%s\n", pStr ); - /* - pErrorText[0] = '\0'; // do not repeat same output - */ - ret = 1; - } - if ( pNewErrorText ) - inchi_free( pNewErrorText ); - return ret; - -} - -/**********************************************************************************************/ -int OutputINChIPlainError( INCHI_IOSTREAM *output_file, char *pStr, int nStrLen, - char *pErrorText, int bError ) -{ - /* char szBuf[64]; */ - const char *pErr; - char *pNewErrorText=NULL, *szErrorText = pErrorText; - int nEstLen, ret = 0; - - switch( bError ) { - case _IS_WARNING: - pErr = x_warn; - break; - case _IS_ERROR: - pErr = x_err; - break; - default: /* _IS_FATAL */ - pErr = x_ferr; - break; - } - /* <%s: >, x_message */ - nEstLen = sizeof(x_message)-1 + 1 + 1 - /* <%s=\"%s\">, x_type, pErr */ - + sizeof(x_type)-1 + 1 + 1 + strlen(pErr) + 1 - /* < %s=\"%s\"\n>, x_text, szErrorText */ - + 1 + sizeof(x_text)-1 + 1 + 1 + strlen(szErrorText) + 1 + 1; - if ( nEstLen < nStrLen ) { - sprintf( pStr, "%s: %s=\"%s\" %s=\"%s\"", - x_message, x_type, pErr, x_text, szErrorText ); - inchi_ios_print( output_file, "%s\n", pStr ); - ret = 1; - } - if ( pNewErrorText ) - inchi_free( pNewErrorText ); - return ret; - -} - -/**************************************************************************/ - -#ifndef OUT_TN /* defined in mode.h; quoted here for reference purposes only */ - -#define OUT_N1 0 /* non-tautomeric only */ -#define OUT_T1 1 /* tautomeric if present otherwise non-tautomeric */ -#define OUT_NT 2 /* only non-taut representations of tautomeric */ -#define OUT_TN 3 /* tautomeric if present otherwise non-tautomeric; - sepatately output non-taut representations of tautomeric if present */ -/* OUT_TN = OUT_T1 + OUT_NT */ -#endif - - -/******************************************************************/ -const char *EquString( int EquVal ) -{ - int bFrom = EquVal & (iiSTEREO | iiSTEREO_INV | iiNUMB | iiEQU ); - int bType = EquVal & (iitISO | iitNONTAUT ); - int bEq2 = EquVal & (iiEq2NONTAUT | iiEq2ISO | iiEq2INV ); - const char *r = ""; - -#if ( FIX_EMPTY_LAYER_BUG == 1 ) - int bEmpty= EquVal & iiEmpty; - if ( bEmpty ) { - r = "e"; - return r; - } -#endif - - switch ( bFrom ) { - - case iiSTEREO: /* ------------ Stereo --------------------*/ - switch ( bType ) { - case iitISO: /* iso main stereo =... */ - switch( bEq2 ) { - case 0: - r = "m"; /* iso main stereo = main stereo */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - case iitNONTAUT: /* non-taut stereo =... */ - switch( bEq2 ) { - case 0: - r = "m"; /* non-taut stereo = main stereo */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - case (iitNONTAUT | iitISO): /* iso non-taut stereo = ... */ - switch( bEq2 ) { - case 0: - r = "m"; /* iso non-taut stereo = main stereo */ - break; - case iiEq2ISO: - r = "M"; /* iso non-taut stereo = main iso stereo */ - break; - case iiEq2NONTAUT: - r = "n"; /* iso non-taut stereo = non-taut stereo */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - - case iiSTEREO_INV: /*---------- Inverted Aux Stereo ------*/ - if ( bEq2 & iiEq2INV ) { /* stereo = Inverted(another stereo) */ - bEq2 &= ~iiEq2INV; - switch( bType ) { - case 0: /* main = ...*/ - switch( bEq2 ) { - case 0: - r = "im"; /* main = Inv(main) */ - break; - case iiEq2ISO: - r = "iM"; /* main = Inv(main iso) */ - break; - case iiEq2NONTAUT: - r = "in"; /* maim = Inv(non-taut) */ - break; - case (iiEq2NONTAUT | iiEq2ISO): - r = "iN"; /* maim = Inv(non-taut iso ) */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - case iitISO: /* main iso = ...*/ - switch( bEq2 ) { - case 0: - r = "im"; /* main iso = Inv(main) */ - break; - case iiEq2ISO: - r = "iM"; /* main iso = Inv(main iso) */ - break; - case iiEq2NONTAUT: - r = "in"; /* maim iso = Inv(non-taut) */ - break; - case (iiEq2NONTAUT | iiEq2ISO): - r = "iN"; /* maim = Inv(non-taut iso ) */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - case iitNONTAUT: /* non-taut = ... */ - switch( bEq2 ) { - case 0: - r = "im"; /* non-taut = Inv(main) */ - break; - case iiEq2ISO: - r = "iM"; /* non-taut = Inv(main iso) */ - break; - case iiEq2NONTAUT: - r = "in"; /* non-taut = Inv(non-taut) */ - break; - case (iiEq2NONTAUT | iiEq2ISO): - r = "iN"; /* non-taut = Inv(non-taut iso ) */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - case (iitNONTAUT | iitISO): - switch( bEq2 ) { - case 0: - r = "im"; /* non-taut iso = Inv(main) */ - break; - case iiEq2ISO: - r = "iM"; /* non-taut iso = Inv(main iso) */ - break; - case iiEq2NONTAUT: - r = "in"; /* non-taut iso = Inv(non-taut) */ - break; - case (iiEq2NONTAUT | iiEq2ISO): - r = "iN"; /* non-taut iso = Inv(non-taut iso ) */ - break; - default: - r = "??"; /* should not happen */ - } - break; - default: - r = "??"; /* should not happen */ - break; - } - - } else { /* Inv stereo = another (non-inverted) stereo */ - - switch( bType ) { - case iitISO: /* main iso = ...*/ - switch( bEq2 ) { - case 0: - r = "m"; /* main = (inverted aux) main */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - case iitNONTAUT: /* non-taut = ... */ - switch( bEq2 ) { - case 0: - r = "m"; /* non-taut = (inverted aux) main */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - case (iitNONTAUT | iitISO): /* non-taut iso = ...*/ - switch( bEq2 ) { - case 0: - r = "m"; /* non-taut iso = (inverted aux) main */ - break; - case iiEq2ISO: - r = "M"; /* non-taut iso = (inverted aux) main iso */ - break; - case iiEq2NONTAUT: - r = "n"; /* non-taut iso = (inverted aux) non-taut */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - default: - r = "??"; /* should not happen */ - break; - } - } - break; - - case ( iiNUMB | iiSTEREO_INV): /*------------- Inv Stereo Numbering ------------*/ - switch( bType ) { - case 0: /* inv stereo numb main = ...*/ - switch( bEq2 ) { - case 0: - r = "m"; /* inv stereo numb main = main numb */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - case iitISO: /* inv stereo iso numb main = ...*/ - switch( bEq2 ) { - case 0: - r = "m"; /* inv stereo iso numb main = main numb */ - break; - case iiEq2INV: - r = "im"; /* inv stereo iso numb main = InvStereo(main) numb */ - break; - case iiEq2ISO: - r = "M"; /* inv stereo iso numb main = isotopic main numb */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - case iitNONTAUT: /* inv stereo numb non-taut = ... */ - switch( bEq2 ) { - case 0: - r = "m"; /* inv stereo numb non-taut = main numb */ - break; - case iiEq2NONTAUT: - r = "n"; /* inv stereo numb non-taut = non-taut numb */ - break; - case iiEq2INV: - r = "im"; /* inv stereo numb non-taut = InvStereo(main) numb */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - case (iitNONTAUT | iitISO): /* inv stereo numb non-taut iso = ... */ - switch( bEq2 ) { - case 0: - r = "m"; /* inv stereo numb non-taut iso = main numb */ - break; - case iiEq2ISO: - r = "M"; /* inv stereo numb non-taut iso = main numb iso */ - break; - case (iiEq2ISO | iiEq2INV): - r = "iM"; /* inv stereo numb non-taut iso = InvStereo(main iso) numb */ - break; - case iiEq2NONTAUT: - r = "n"; /* inv stereo numb non-taut iso = non-taut numb */ - break; - case (iiEq2NONTAUT | iiEq2ISO): - r = "N"; /* inv stereo numb non-taut iso = non-taut iso numb */ - break; - case iiEq2INV: - r = "im"; /* inv stereo numb non-taut iso = InvStereo(main) numb */ - break; - case (iiEq2NONTAUT | iiEq2INV): - r = "in"; /* inv stereo numb non-taut iso = InvStereo(non-taut) numb ) */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - - case iiNUMB: /*------------- Canonical Numbering ------------*/ - switch( bType ) { - case 0: /* numb main = ...*/ - r = "??"; /* should not happen */ - break; - case iitISO: /* iso numb main = ...*/ - switch( bEq2 ) { - case 0: - r = "m"; /* iso numb main = main numb */ - break; - default: - r = "??"; /* should not happen */ - } - break; - case iitNONTAUT: /* numb non-taut = ... */ - switch( bEq2 ) { - case 0: - r = "m"; /* numb non-taut = main numb */ - break; - default: - r = "??"; /* should not happen */ - } - break; - case (iitNONTAUT | iitISO): /* numb non-taut iso = ... */ - switch( bEq2 ) { - case 0: - r = "m"; /* numb non-taut iso = main numb */ - break; - case iiEq2ISO: - r = "M"; /* numb non-taut iso = main numb iso */ - break; - case iiEq2NONTAUT: - r = "n"; /* numb non-taut iso = non-taut numb */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - - case iiEQU: /*------------- Atom Equivalence ------------*/ - switch( bType ) { - case 0: /* equivalence main = ...*/ - r = "??"; /* should not happen */ - break; - case iitISO: /* equivalence main iso = ...*/ - switch( bEq2 ) { - case 0: - r = "m"; /* equivalence main = main equ */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - case iitNONTAUT: /* equivalence non-taut = ... */ - switch( bEq2 ) { - case 0: - r = "m"; /* equivalence non-taut = main equ */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - case (iitNONTAUT | iitISO): /* equivalence non-taut iso = ... */ - switch( bEq2 ) { - case 0: - r = "m"; /* equivalence non-taut iso = main equ */ - break; - case iiEq2ISO: - r = "M"; /* equivalence non-taut iso = main iso equ */ - break; - case iiEq2NONTAUT: - r = "n"; /* equivalence non-taut iso = non-taut equ */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - default: - r = "??"; /* should not happen */ - break; - } - return r; -} - -/**********************************************************************************************/ - -#define OUT_NONTAUT OUT_NN /* was OUT_NT until 2004-04-07 */ - - -/**********************************************************************************************/ -int OutputINChI2(char *pStr, int nStrLen, - INCHI_SORT *pINChISortTautAndNonTaut2[][TAUT_NUM], - int iINChI, - ORIG_STRUCT *pOrigStruct, - int bDisconnectedCoord, int bOutputType, int bINChIOutputOptions, - int bXml, int bAbcNumbers,int bCtPredecessors, int bNoStructLabels, - int num_components2[], - int num_non_taut2[], int num_taut2[], - INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *log_file, - int num_input_struct, - const char *szSdfLabel, const char *szSdfValue, long lSdfId, - int *pSortPrintINChIFlags, - unsigned char save_opt_bits) -{ - int bINChIOutputOptions0 = bINChIOutputOptions & ~(INCHI_OUT_XML | INCHI_OUT_PLAIN_TEXT | INCHI_OUT_PLAIN_TEXT_COMMENTS); - int bINChIOutputOptionsCur; - int bCurOption, ret, i; - - ret = 0; - - for ( i = 0; i < 3; i ++ ) - { - switch( i ) - { - case 0: - bCurOption = INCHI_OUT_XML; - break; - case 1: - bCurOption = INCHI_OUT_PLAIN_TEXT; - break; - case 2: - bCurOption = INCHI_OUT_PLAIN_TEXT_COMMENTS; - break; - default: - continue; - } - if ( bINChIOutputOptions & bCurOption ) - { - bINChIOutputOptionsCur = bINChIOutputOptions0 | bCurOption; - if ( i != 1 ) - { - bINChIOutputOptionsCur &= ~INCHI_OUT_TABBED_OUTPUT; - } - ret |= OutputINChI1( pStr, nStrLen, - pINChISortTautAndNonTaut2, - iINChI, - pOrigStruct, - bDisconnectedCoord, bOutputType, bINChIOutputOptionsCur, - bXml, bAbcNumbers,bCtPredecessors, bNoStructLabels, - num_components2, - num_non_taut2, num_taut2, - output_file, log_file, - num_input_struct, - szSdfLabel, szSdfValue, lSdfId, - pSortPrintINChIFlags, - save_opt_bits); - } - } - - return ret; -} - -/**********************************************************************************/ -char *szGetTag( const INCHI_TAG *Tag, int nTag, int bTag, char *szTag, int *bAlways ) -{ - int i, j, bit, num, len; - if ( 0 < nTag && nTag < 3 ) { - /* no plain text comments: pick up the last tag */ - for ( i = 0, j = -1, bit = 1; i < MAX_TAG_NUM; i ++, bit <<= 1 ) { - if ( bTag & bit ) { - j = i; - } - } - if ( j >= 0 ) { - strcpy( szTag, nTag == 1? Tag[j].szXmlLabel : nTag == 2? Tag[j].szPlainLabel : "???" ); - if ( nTag != 2 ) { - *bAlways = Tag[j].bAlwaysOutput; - } - return szTag; - } - } else - if ( nTag == 3 ) { - /* plain text with comments */ - szTag[0] = '{'; - szTag[1] = '\0'; - for ( i = 0, j = -1, bit = 1, num=0; i < MAX_TAG_NUM; i ++, bit <<= 1 ) { - if ( bTag & bit ) { - j = i; - if ( num ++ ) { - strcat( szTag, ":" ); - } - strcat( szTag, Tag[i].szPlainComment ); - } - } - if ( num ) { - strcat( szTag, "}" ); - num = strlen( Tag[j].szPlainLabel ); - len = strlen( szTag ); - if ( len ) { - memmove( szTag + num, szTag, len+1 ); - memcpy( szTag, Tag[j].szPlainLabel, num ); - } else { - strcpy ( szTag, Tag[j].szPlainLabel ); - } - *bAlways = Tag[j].bAlwaysOutput; - } else { - strcpy( szTag, "???" ); - } - return szTag; - } - strcpy( szTag, "???" ); - return szTag; -} - - -/***************************************************************************************/ -/* sorting in descending order: return -1 if *p1 > *p2, return +1 if *p1 < *p2 */ -/***************************************************************************************/ -int OutputINChI1(char *pStr, int nStrLen, - INCHI_SORT *pINChISortTautAndNonTaut2[][TAUT_NUM], - int iINChI, - ORIG_STRUCT *pOrigStruct, - int bDisconnectedCoord, int bOutputType, int bINChIOutputOptions, - int bXml, int bAbcNumbers,int bCtPredecessors, int bNoStructLabels, - int num_components2[], int num_non_taut2[], int num_taut2[], - INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *log_file, - int num_input_struct, - const char *szSdfLabel, const char *szSdfValue, long lSdfId, - int *pSortPrintINChIFlags, - unsigned char save_opt_bits) -{ -/* - bINChIOutputOptions bits: - - INCHI_OUT_NO_AUX_INFO 0x0001 do not output Aux Info - INCHI_OUT_SHORT_AUX_INFO 0x0002 output short version of Aux Info - INCHI_OUT_ONLY_AUX_INFO 0x0004 output only Aux Info - INCHI_OUT_EMBED_REC 0x0008 embed reconnected INChI into disconnected INChI - -*/ - - /*int ATOM_MODE = ((bAbcNumbers?2:0)|5|(bCtPredecessors?8:0));*/ - int ATOM_MODE = ((bAbcNumbers?CT_MODE_ABC_NUMBERS:0) - | CT_MODE_ATOM_COUNTS - | CT_MODE_NO_ORPHANS -#if ( EQL_H_NUM_TOGETHER == 1 ) - | CT_MODE_EQL_H_TOGETHER -#endif -#if ( ABC_CT_NUM_CLOSURES == 1 ) - | (bAbcNumbers && bCtPredecessors? CT_MODE_ABC_NUM_CLOSURES:0) -#endif - | (bCtPredecessors?CT_MODE_PREDECESSORS:0)); - - int TAUT_MODE = (bAbcNumbers?CT_MODE_ABC_NUMBERS:0); - char sDifSegs[DIFL_LENGTH][DIFS_LENGTH]; - /* bOutputType = - TAUT_YES => tautomeric only (if no tautomeric components then no output; - TAUT_NON => only non-tautomeric output (if no non-taut present then no output; - TAUT_BOTH => tautomeric and non-tautomeric */ - - int i, j, ii, jj, /*ii2, jj2,*/ tot_len, tot_len2, bOverflow, bEmbeddedOutputCalled=0; - int bIsotopic, bTautIsoHNum, bTautIsoAt, bHasIsotopicAtoms[TAUT_NUM]; - int bStereoSp2[TAUT_NUM], bStereoSp3[TAUT_NUM]; - int bIsotopicStereoSp2[TAUT_NUM], bIsotopicStereoSp3[TAUT_NUM]; - int bStereoAbsInverted[TAUT_NUM], bIsotopicStereoAbsInverted[TAUT_NUM]; - int bStereoAbs[TAUT_NUM], bIsotopicStereoAbs[TAUT_NUM]; - int bAtomEqu[TAUT_NUM], bTautEqu[TAUT_NUM], bIsotopicAtomEqu[TAUT_NUM], bIsotopicTautEqu[TAUT_NUM]; - int bInvStereo[TAUT_NUM], bInvIsotopicStereo[TAUT_NUM]; - int bInvStereoOrigNumb[TAUT_NUM], bInvIsotopicStereoOrigNumb[TAUT_NUM], bIsotopicOrigNumb[TAUT_NUM]; - int bTautomeric, bNonTautomeric, bTautomericAcid, bHardAddRemProton, iCurTautMode; - int bRequestedRacemicStereo=0, bRequestedRelativeStereo = 0, bRelRac; - int bRacemicStereo[TAUT_NUM], bRelativeStereo[TAUT_NUM]; - int bIsotopicRacemicStereo[TAUT_NUM], bIsotopicRelativeStereo[TAUT_NUM]; - int bChargesRadVal[TAUT_NUM], bOrigCoord[TAUT_NUM]; - int bIgn_UU_Sp3[TAUT_NUM], bIgn_UU_Sp2[TAUT_NUM]; - int bIgn_UU_Sp3_Iso[TAUT_NUM], bIgn_UU_Sp2_Iso[TAUT_NUM]; - int ind, inc, bNonTautIsIdenticalToTaut = 1; - int bNonTautNonIsoIdentifierNotEmpty = 0, bNonTautIsoIdentifierNotEmpty = 0; - INCHI_SORT **pINChISortTautAndNonTaut = pINChISortTautAndNonTaut2[iINChI]; - INCHI_SORT *pINChISort =pINChISortTautAndNonTaut[TAUT_YES]; - INCHI_SORT *pINChISort2=pINChISortTautAndNonTaut[TAUT_YES]; - INCHI_SORT *is, *is2; - INChI *pINChI /*, *pINChI2*/; - INChI_Aux *pINChI_Aux = NULL; - - - int ret = 0; /* 0=>failed, 1=>success */ - int bOutType = bOutputType; /* ??? */ - int nTag; - int bTautomericOutputAllowed, bSecondNonTautPass; - int num_components = num_components2[iINChI]; - int num_comp[TAUT_NUM], max_num_comp; - int num_iso_H[NUM_H_ISOTOPES], bHasIsoH; - int nNumRemovedProtons, nNumMovedProtons; - int bTautAndNonTaut, bTautIsNonTaut; - - int bAlways = 0; - int bUseMulipliers = 1; - int bOmitRepetitions = 1; - int bPlainTextTags = 2; /* 0 => no plain tags, 1=> plain text tags, 2=>plaintext tags without consecutive // */ - int bPlainText = 0 != (bINChIOutputOptions & (INCHI_OUT_PLAIN_TEXT | INCHI_OUT_PLAIN_TEXT_COMMENTS)); - int bPlainTextCommnts = 0 != (bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT_COMMENTS); - int bPlainTabbedOutput; - int bTag1, bTag2, bTag3, bFhTag; /* tag bits */ - int nCurINChISegment, nSegmAction; - char szTag1[MAX_TAG_LEN], szTag2[MAX_TAG_LEN], szTag3[MAX_TAG_LEN]; - const char *pLF, *pTAB; - - /*^^^ 15 April, 2008 */ - int bFixTranspChargeBug = 0; -#if ( FIX_TRANSPOSITION_CHARGE_BUG == 1 ) /* 2008-01-02 */ - if ( INCHI_OUT_FIX_TRANSPOSITION_CHARGE_BUG & bINChIOutputOptions ) - bFixTranspChargeBug = 1; -#endif - /*^^^ 15 April, 2008 */ - - bXml = 0 != (bINChIOutputOptions & INCHI_OUT_XML); - nTag = bPlainTextCommnts? 3 : bPlainText? 2 : bXml? 1 : 0; /* tag type */ - ind = bXml? 1 : -1; - inc = bXml? 1 : -1; - pLF = bPlainTextCommnts? "\n" : "\0"; - bFhTag = 0; - bPlainTabbedOutput = 0 != (bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT) && - bPlainText && !bXml && !bPlainTextCommnts; -#if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) ) - pTAB = bPlainTabbedOutput? "\t" : "\n"; -#else - pTAB = "\n"; -#endif - - - memset( sDifSegs, DIFV_BOTH_EMPTY, sizeof(sDifSegs) ); - - if ( !pStr ) { - inchi_ios_eprint(log_file, - "Cannot allocate output buffer. No output for structure #%d.%s%s%s%s\n", - num_input_struct, SDF_LBL_VAL(szSdfLabel, szSdfValue)); - return ret; - } - - bSecondNonTautPass = 0; -/* -- commented out to allow empty InChI -- - if (!num_components ) - { - return 0; - } -*/ - - /* init version string */ - if ( !VER_STRING[0] ) - { - strcpy(VER_STRING, "(V"); - strcat(VER_STRING, INCHI_VERSION); - strcat(VER_STRING, ")"); - } - for ( i = 0; i < TAUT_NUM; i ++ ) - { - bHasIsotopicAtoms[i] = num_comp[i] = - bStereoSp2[i] = bStereoSp3[i] = - bIsotopicStereoSp2[i] = bIsotopicStereoSp3[i] = - bIsotopicOrigNumb[i] = - bStereoAbs[i] = bIsotopicStereoAbs[i] = - bStereoAbsInverted[i] = bIsotopicStereoAbsInverted[i] = - bRacemicStereo[i] = bRelativeStereo[i] = - bIsotopicRacemicStereo[i] = bIsotopicRelativeStereo[i] = - bAtomEqu[i] = bTautEqu[i] = - bIsotopicAtomEqu[i] = bIsotopicTautEqu[i] = - bInvStereo[i] = bInvIsotopicStereo[i] = - bInvStereoOrigNumb[i] = bInvIsotopicStereoOrigNumb[i] = - bIgn_UU_Sp3[i] = bIgn_UU_Sp2[i] = - bIgn_UU_Sp3_Iso[i] = bIgn_UU_Sp2_Iso[i] = - bChargesRadVal[i] = bOrigCoord[i] = 0; - } - - /* find if it is isotopic */ - bIsotopic = bTautomeric = bNonTautomeric = bTautomericAcid = - bHardAddRemProton = bTautIsoHNum = bTautIsoAt = 0; - bTautAndNonTaut = bTautIsNonTaut = 0; - /* - x = bStereo, bStereoSp2, bStereoSp3, bStereoAbsInverted, - bIsotopicStereo, bIsotopicStereoSp2, bIsotopicStereoSp3, bIsotopicStereoAbsInverted - - OUT_N1: x[TAUT_NON] refers to non-tautomeric only - OUT_T1: x[TAUT_YES] refers to tautomeric if exists otherwise non-tautomeric - OUT_NT: x[TAUT_NON] refers to non-taut representations of tautomeric - OUT_TN: x[TAUT_YES] refers to tautomeric if exists otherwise non-tautomeric - x[TAUT_NON] refers to non-taut representations of tautomeric - */ - - memset( num_iso_H, 0, sizeof(num_iso_H) ); - nNumRemovedProtons = 0; - nNumMovedProtons = 0; - bHasIsoH = 0; - bTautomericOutputAllowed = (bOutType==OUT_T1 || bOutType== OUT_TN); - pINChISort=pINChISortTautAndNonTaut[bTautomericOutputAllowed? TAUT_YES : TAUT_NON]; - is = pINChISort; - is2 = (bOutType== OUT_TN)? pINChISortTautAndNonTaut[TAUT_NON] : NULL; - - for ( i = 0, is2 = pINChISortTautAndNonTaut[TAUT_NON]; i < num_components; i ++, is ++, is2? is2++:NULL ) - { - CompINChILayers( is, is2, sDifSegs, bFixTranspChargeBug ); - bNonTautIsIdenticalToTaut = bNonTautIsIdenticalToTaut && !CompINChITautVsNonTaut(is, is2, 1); - if ( is && (pINChI_Aux = is->pINChI_Aux[TAUT_YES]) ) - { - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) - { - bHasIsoH += abs(pINChI_Aux->nNumRemovedIsotopicH[j]); - num_iso_H[j] += pINChI_Aux->nNumRemovedIsotopicH[j]; - } - nNumRemovedProtons += pINChI_Aux->nNumRemovedProtons; - nNumMovedProtons += abs(pINChI_Aux->nNumRemovedProtons); - } - if ( bTautomericOutputAllowed ) - { - /* check for removed isotopic H */ - for ( j = TAUT_YES; j < TAUT_NUM; j ++ ) - { - switch ( bOutType ) { - case OUT_N1: /* x[TAUT_NON]: non-tautomeric only -- never happens */ - jj = GET_II(bOutType,is); - if ( jj != j ) - continue; - ii = TAUT_NON; - break; - case OUT_T1: /* x[TAUT_YES]: tautomeric if present otherwise non-tautomeric */ - jj = GET_II(bOutType,is); - if ( jj != j ) - continue; - ii = TAUT_YES; - break; - case OUT_NT: /* x[TAUT_NON]: only non-taut representations of tautomeric -- never happens */ - jj = GET_II(bOutType,is); - if ( jj != j ) - continue; - ii = TAUT_NON; - break; - /* main path of control flow */ - case OUT_TN: /* x[TAUT_YES]: tautomeric if present otherwise non-tautomeric; - * x[TAUT_NON]: non-taut only if tautomeric is present */ - jj = ( j == TAUT_YES )? GET_II(OUT_T1,is) : ( j == TAUT_NON )? GET_II(OUT_NT,is) : -1; - if ( jj == TAUT_YES ) - { - /* Fix12 */ - if ( is->pINChI[jj]->lenTautomer > 0 ) - { - bTautAndNonTaut += (!is->pINChI[jj]->bDeleted && HAS_N(is)); - } else - { - bTautIsNonTaut ++; - } - } - if ( jj < 0 ) - continue; - ii = j; - break; - default: - continue; - } - if ( jj != j ) - continue; - if ( (pINChI = is->pINChI[jj]) && pINChI->nNumberOfAtoms > 0 && (pINChI_Aux = is->pINChI_Aux[jj]) ) - { - bTautIsoHNum += (pINChI_Aux->nNumRemovedIsotopicH[0] + - pINChI_Aux->nNumRemovedIsotopicH[1] + - pINChI_Aux->nNumRemovedIsotopicH[2]); - bTautIsoAt += (pINChI->nNumberOfIsotopicAtoms>0 || pINChI->nNumberOfIsotopicTGroups > 0 ); - } - } - } - } - sDifSegs[DIFL_M ][DIFS_p_PROTONS] = nNumRemovedProtons? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; - sDifSegs[DIFL_MI][DIFS_h_H_ATOMS] = bHasIsoH? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; - - MarkUnusedAndEmptyLayers( sDifSegs ); - - - - bNonTautIsIdenticalToTaut = bNonTautIsIdenticalToTaut && !bTautIsoHNum; - /*********************************************************************************************/ - for ( i = 0, is = pINChISort; i < num_components; i ++, is ++ ) - { - int bCurIso, bCurStereo, bCurIsoStereo, bCurHasIsoStereo /* Fix14 */, bCurTaut /*, bCurTaut2*/; - int bCompExists, bCurIsoHPos, bCurIsoHStereo; - int bCurStereoSp2, bCurIsoStereoSp2, bCurStereoSp3, bCurIsoStereoSp3, bCurIsoStereoSp3Inv; - int bCurRacemic, bCurRelative, bCurIsoRacemic, bCurIsoRelative; - bCompExists = 0; - for ( j = TAUT_NON; j < TAUT_NUM; j ++ ) - { - switch ( bOutType ) { - case OUT_N1: /* x[TAUT_NON]: non-tautomeric only */ - jj = GET_II(bOutType,is); - if ( jj != j ) - continue; - ii = TAUT_NON; - break; - case OUT_T1: /* x[TAUT_YES]: tautomeric if present otherwise non-tautomeric */ - jj = GET_II(bOutType,is); - if ( jj != j ) - continue; - ii = TAUT_YES; - break; - case OUT_NT: /* x[TAUT_NON]: only non-taut representations of tautomeric */ - jj = GET_II(bOutType,is); - if ( jj != j ) - continue; - ii = TAUT_NON; - break; - /* main control flow comes here: requested both mobile and fixed H results */ - case OUT_TN: /* x[TAUT_YES]: tautomeric if present otherwise non-tautomeric; - * x[TAUT_NON]: non-taut only if tautomeric is present */ - jj = ( j == TAUT_YES )? GET_II(OUT_T1,is) : ( j == TAUT_NON )? GET_II(OUT_NT,is) : -1; - if ( jj < 0 ) - { - /* Fix12 */ - if ( bTautAndNonTaut && bTautIsNonTaut && - j == TAUT_NON && 0 <= (jj = GET_II(OUT_T1,is)) && - !is->pINChI[jj]->bDeleted && !is->pINChI[jj]->lenTautomer ) - { - ; /* the requested non-tautomeric component is in tautomeric position - (is->pINChI[TAUT_YES]); - process it also as non-tautomeric if Fixed-H layer was requested */ - } - else - { - continue; - } - } - ii = j; /* ii is what we wanted; jj is what we found (0 = TAUT_NON: fixed_H, 1 = TAUT_YES: mobile_H) */ - /* -- not used 2004-09-16 --- - if ( is2 ) { - jj2 = ( j == TAUT_YES )? GET_II(OUT_T1,is2) : ( j == TAUT_NON )? GET_II(OUT_NT,is2) : -1; - if ( jj2 >= 0 ) { - ii2 = j; - } else { - ii2 = -1; - } - } else { - jj2 = ii2 = -1; - } - -----------------------------*/ - break; - default: - continue; - } - if ( (pINChI = is->pINChI[jj]) && pINChI->nNumberOfAtoms > 0 ) - { - /*pINChI_Aux = is->pINChI_Aux[jj];*/ - bCompExists ++; - bCurTaut = (pINChI->lenTautomer > 0); - bCurIso = (pINChI->nNumberOfIsotopicAtoms>0 || pINChI->nNumberOfIsotopicTGroups > 0 ); - bCurIsoHPos = (pINChI->nPossibleLocationsOfIsotopicH && pINChI->nPossibleLocationsOfIsotopicH[0] > 1 || pINChI->lenTautomer > 1); - /* present isotopic H + their possible positions AND/OR isotopic atoms */ - bCurIsoHStereo = bCurIsoHPos && (bTautIsoHNum || bTautIsoAt) || bCurIso; - if ( jj == j && pINChI->bDeleted ) - { - num_comp[j] --; - if ( bCurTaut ) - { - bTautomeric |= 1; /* tautomeric representation is present */ - bNonTautomeric |= HAS_N(is); - } - bIsotopic |= bCurIso; - continue; /* deleted H(+) in tautomeric representation */ - } - bCurStereoSp2 = pINChI->Stereo && (pINChI->Stereo->nNumberOfStereoBonds > 0); - bCurHasIsoStereo = - bCurStereoSp3 = pINChI->Stereo && (pINChI->Stereo->nNumberOfStereoCenters > 0 ); - bCurIsoStereoSp2 = bCurIsoHStereo && pINChI->StereoIsotopic && (pINChI->StereoIsotopic->nNumberOfStereoBonds > 0); - bCurIsoStereoSp3 = bCurIsoHStereo && pINChI->StereoIsotopic && (pINChI->StereoIsotopic->nNumberOfStereoCenters > 0); - bCurIsoStereoSp3Inv = bCurIsoStereoSp3 && pINChI->StereoIsotopic->nCompInv2Abs; /* inversion changes sp3 stereo */ - bRequestedRacemicStereo |= (0!=(pINChI->nFlags & INCHI_FLAG_RAC_STEREO)); - - bRequestedRelativeStereo |= (0!=(pINChI->nFlags & INCHI_FLAG_REL_STEREO)); - /* check whether isotopic stereo is same as non-isotopic; if same than do not output isotopic stereo */ - if ( bCurStereoSp2 && bCurIsoStereoSp2 ) - { - bCurIsoStereoSp2 = !Eql_INChI_Stereo( pINChI->Stereo, EQL_SP2, pINChI->StereoIsotopic, EQL_SP2, 0 ); - } - if ( bCurStereoSp3 && bCurIsoStereoSp3 ) - { - /* bCurIsoStereoSp3=0 means (iso stereo sp3) = (non-iso stereo sp3) or (iso stereo sp3) = Inv(non-iso stereo sp3) */ - bCurIsoStereoSp3 = !Eql_INChI_Stereo( pINChI->Stereo, EQL_SP3, pINChI->StereoIsotopic, EQL_SP3, - (pINChI->nFlags & INCHI_FLAG_RAC_STEREO) || (pINChI->nFlags & INCHI_FLAG_REL_STEREO) ); - if ( !bCurIsoStereoSp3 ) { - /* inversion changes iso sp3 differently from non-iso sp3 Fix11 */ - bCurIsoStereoSp3Inv &= (pINChI->StereoIsotopic->nCompInv2Abs != pINChI->Stereo->nCompInv2Abs); - } - } - - bCurRelative = bRequestedRelativeStereo && bCurStereoSp3; -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - bCurRelative = bCurRelative && - (pINChI->Stereo->nNumberOfStereoCenters > 1 ) && - (pINChI->Stereo->nCompInv2Abs != 0) && -#endif - - - - bCurIsoRelative = bRequestedRelativeStereo && (bCurIsoStereoSp3 || bCurIsoStereoSp3Inv); -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - bCurIsoRelative = bCurIsoRelative && - (pINChI->StereoIsotopic->nNumberOfStereoCenters > 1 ) && - (pINChI->StereoIsotopic->nCompInv2Abs != 0) && -#endif - - - bCurRacemic = bRequestedRacemicStereo && bCurStereoSp3; -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - bCurRacemic = bCurRacemic && - (pINChI->Stereo->nCompInv2Abs != 0) && - (pINChI->Stereo->nNumberOfStereoCenters > 0 ) ? - pINChI->Stereo->nNumberOfStereoCenters : 0; -#endif - - bCurIsoRacemic = bRequestedRacemicStereo && (bCurIsoStereoSp3 || bCurIsoStereoSp3Inv); -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - bCurIsoRacemic = bCurIsoRacemic & - (pINChI->StereoIsotopic->nCompInv2Abs != 0) && - (pINChI->StereoIsotopic->nNumberOfStereoCenters > 0 ) ? - pINChI->StereoIsotopic->nNumberOfStereoCenters : 0; -#endif - if ( bRequestedRelativeStereo ) - { - bCurStereoSp3 = bCurRelative || bCurStereoSp3 && (pINChI->Stereo->nNumberOfStereoCenters > 1 ); /* Fix11 */ - bCurIsoStereoSp3 = bCurIsoRelative ? bCurIsoStereoSp3 : 0; - } - else - { - if ( bRequestedRacemicStereo ) - { - bCurStereoSp3 = bCurRacemic > 1 || bCurStereoSp3 && (pINChI->Stereo->nNumberOfStereoCenters > 1 ); /* Fix11 */ - bCurIsoStereoSp3 = bCurIsoRacemic > 1? bCurIsoStereoSp3 : 0; - } - } - bCurStereo = bCurStereoSp2 || bCurStereoSp3; - bCurIsoStereo = bCurIsoStereoSp2 || bCurIsoStereoSp3; - - bIsotopic |= bCurIso; - bHasIsotopicAtoms[ii] |= bCurIso; - bStereoSp2[ii] |= bCurStereoSp2; - bStereoSp3[ii] |= bCurStereoSp3; - bIgn_UU_Sp3[ii] |= !bCurStereoSp3 && (pINChI->nFlags & INCHI_FLAG_SC_IGN_ALL_UU); - bIgn_UU_Sp2[ii] |= !bCurStereoSp2 && (pINChI->nFlags & INCHI_FLAG_SB_IGN_ALL_UU); - bIsotopicStereoSp2[ii] |= bCurIsoStereoSp2; - bIsotopicStereoSp3[ii] |= bCurIsoStereoSp3; - bIgn_UU_Sp3_Iso[ii] |= !bCurIsoStereoSp3 && (pINChI->nFlags & INCHI_FLAG_SC_IGN_ALL_ISO_UU); - bIgn_UU_Sp2_Iso[ii] |= !bCurIsoStereoSp2 && (pINChI->nFlags & INCHI_FLAG_SB_IGN_ALL_ISO_UU); - bStereoAbs[ii] |= bCurStereoSp3 && (pINChI->Stereo->nCompInv2Abs != 0); - bStereoAbsInverted[ii] |= bCurStereoSp3 && (pINChI->Stereo->nCompInv2Abs < 0); - /* Fix08: missing isotopic inverted flag if isotopic = inverted non-isotopic */ - bIsotopicStereoAbsInverted[ii] |= bCurIsoStereoSp3 && (pINChI->StereoIsotopic->nCompInv2Abs < 0) || - !bCurIsoStereoSp3 && pINChI->StereoIsotopic && pINChI->Stereo && - pINChI->StereoIsotopic->nCompInv2Abs && - pINChI->StereoIsotopic->nCompInv2Abs != pINChI->Stereo->nCompInv2Abs; - /* Fix 11: missing /s1 if only isotopic stereo is inverted */ - bIsotopicStereoAbs[ii] |= bCurIsoStereoSp3 && (pINChI->StereoIsotopic->nCompInv2Abs != 0) || - !bCurIsoStereoSp3 && pINChI->StereoIsotopic && pINChI->Stereo && - pINChI->StereoIsotopic->nCompInv2Abs && - pINChI->StereoIsotopic->nCompInv2Abs != pINChI->Stereo->nCompInv2Abs; - - bRelativeStereo[ii] |= bCurRelative; - bIsotopicRelativeStereo[ii] |= bCurIsoRelative; - bRacemicStereo[ii] |= bCurRacemic; - bIsotopicRacemicStereo[ii] |= bCurIsoRacemic; - - bTautomericAcid |= (0!=(pINChI->nFlags & INCHI_FLAG_ACID_TAUT)); - bHardAddRemProton |= (0!=(pINChI->nFlags & INCHI_FLAG_HARD_ADD_REM_PROTON)); - if ( bCurTaut ) - { - bTautomeric |= 1; /* tautomeric representation is present */ - /* does tautomeric structure have also a non-tautomeric repesentation? */ - bNonTautomeric |= HAS_N(is); - } - - /* auxiliary info */ - if ( !(bINChIOutputOptions & INCHI_OUT_NO_AUX_INFO) && (pINChI_Aux = is->pINChI_Aux[jj]) ) - { - /* detect presence of constitutional equivalence onfo */ - int bCurEqu, bCurTautEqu=0, bCurIsoEqu=0, bCurIsoTautEqu=0; /* Fix15-disabled */ - bAtomEqu[ii] |= (bCurEqu = bHasEquString( pINChI_Aux->nConstitEquNumbers, - pINChI_Aux->nNumberOfAtoms)); - if ( bCurTaut ) - { - bTautEqu[ii] |= (bCurTautEqu = bHasEquString( pINChI_Aux->nConstitEquTGroupNumbers, - pINChI_Aux->nNumberOfTGroups)); - } - if ( bCurIso ) - { - bIsotopicAtomEqu[ii] |= (bCurIsoEqu = bHasEquString( pINChI_Aux->nConstitEquIsotopicNumbers, - pINChI_Aux->nNumberOfAtoms)) /*|| bCurEqu*/; - if ( bCurTaut ) - { - bIsotopicTautEqu[ii] |= (bCurIsoTautEqu = bHasEquString( pINChI_Aux->nConstitEquIsotopicTGroupNumbers, - pINChI_Aux->nNumberOfTGroups)) /*|| bCurTautEqu*/; - } - /* non-zero if isotopic numbering for inverted isotopic stereo is different */ - bIsotopicOrigNumb[ii] |= bCurHasIsoStereo && /* Fix14 */ - pINChI_Aux->nOrigAtNosInCanonOrdInv && - pINChI_Aux->nIsotopicOrigAtNosInCanonOrd && - (0 != memcmp( pINChI_Aux->nOrigAtNosInCanonOrdInv, - pINChI_Aux->nIsotopicOrigAtNosInCanonOrd, - sizeof(pINChI_Aux->nOrigAtNosInCanonOrdInv[0]) - * pINChI_Aux->nNumberOfAtoms)); - - } - /* inverted stereo */ - if ( bCurStereoSp3 && pINChI->Stereo->nCompInv2Abs ) - { - bInvStereo[ii] |= 1; - bInvStereoOrigNumb[ii] |= pINChI_Aux->nOrigAtNosInCanonOrd && - pINChI_Aux->nOrigAtNosInCanonOrdInv && - (0 != memcmp( pINChI_Aux->nOrigAtNosInCanonOrd, - pINChI_Aux->nOrigAtNosInCanonOrdInv, - sizeof(pINChI_Aux->nOrigAtNosInCanonOrd[0]) - * pINChI_Aux->nNumberOfAtoms)); - } - /* inverted isotopic stereo */ - if ( bCurIsoStereoSp3 && pINChI->StereoIsotopic->nCompInv2Abs ) - { - bInvIsotopicStereo[ii] |= 1; - bInvIsotopicStereoOrigNumb[ii] |= pINChI_Aux->nIsotopicOrigAtNosInCanonOrd && - pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv && - (0 != memcmp( pINChI_Aux->nIsotopicOrigAtNosInCanonOrd, - pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv, - sizeof(pINChI_Aux->nIsotopicOrigAtNosInCanonOrd[0]) - * pINChI_Aux->nNumberOfAtoms)); - } - if ( pINChI_Aux->OrigInfo && bHasOrigInfo(pINChI_Aux->OrigInfo, pINChI_Aux->nNumberOfAtoms) ) - { - bChargesRadVal[ii] |= 1; - } - } - } - } - if ( bCompExists ) - { - for ( j = TAUT_NON; j < TAUT_NUM; j ++ ) - { - num_comp[j] ++; - } - } - } - if ( bTautomeric /*&& bTautomericAcid*/ ) /* "&& bTautomericAcid" commented out 2004-06-02 */ - { - bTautomeric += bTautomericAcid; /* long-range tautomerism */ - bTautomeric += (bHardAddRemProton? 4 : 0); - } - if ( bRequestedRacemicStereo || bRequestedRelativeStereo ) - { - /* do not output inverted stereo info */ - for ( i = 0; i < TAUT_NUM; i ++ ) - { - /* Fix11 */ - bStereoAbsInverted[i] = - bStereoAbs[i] = - bInvStereo[i] = - bInvStereoOrigNumb[i] = 0; - /* bIsotopicRelativeStereo[i]=0 may happen because iso stereo is same or inverted non-iso stereo */ - bIsotopicStereoAbsInverted[i] = - bIsotopicStereoAbs[i] = - bInvIsotopicStereo[i] = - bInvIsotopicStereoOrigNumb[i] = 0; - /* -- commented out: Fix11-- - if ( bRacemicStereo[i] || bRelativeStereo[i] ) - { - bStereoAbsInverted[i] = - bStereoAbs[i] = - bInvStereo[i] = - bInvStereoOrigNumb[i] = 0; - } - if ( bIsotopicRacemicStereo[i] || bIsotopicRelativeStereo[i] ) - { - bIsotopicStereoAbsInverted[i] = - bIsotopicStereoAbs[i] = - bInvIsotopicStereo[i] = - bInvIsotopicStereoOrigNumb[i] = 0; - } - */ - } - } - - - iCurTautMode = bOutType == OUT_N1? TAUT_NON: /* only non-taut */ - bOutType == OUT_T1? TAUT_YES: /* tautomeric if present, otherwise non-tautomeric */ - bOutType == OUT_NT? TAUT_NON: /* only non-taut representations of tautomeric */ - bOutType == OUT_TN? TAUT_YES: /* tautomeric if present otherwise non-tautomeric; */ - -1; /* separately output non-taut representations of tautomeric if present */ - - if ( iCurTautMode < 0 ) - { - return 0; /* error */ - } - - if ( bXml ) - { - ind += inc* (1+iINChI); - } - - bOverflow = 0; - - num_components = num_comp[iCurTautMode]; - - max_num_comp = inchi_max(num_comp[TAUT_NON], num_comp[TAUT_YES]); - - if ( bINChIOutputOptions & INCHI_OUT_ONLY_AUX_INFO ) - { - goto output_aux_info; - } - - nCurINChISegment = DIFL_M; - - /****************************************** - * - * Structure (Compound) Header - * - ******************************************/ - if ( bXml ) - { - /* -- moved to the line above goto output_aux_info; - ind += inc* (1+iINChI); - */ - /* basic title, version */ - if ( INCHI_BAS == iINChI ) - { - inchi_ios_print( output_file, "\n" ); /* empty line */ - } - tot_len = sprintf(pStr, "%s<%s %s=\"%s\"", - SP(ind), x_basic, x_ver, x_curr_ver); - if ( INCHI_REC == iINChI || INCHI_BAS == iINChI && bDisconnectedCoord ) - { - tot_len += sprintf(pStr+tot_len, " %s=\"%d\"", x_reconnected, iINChI ); - } - if ( bAbcNumbers || bCtPredecessors ) - { - const char *pNumber = ""; - const char *pDelim = ""; - const char *pCtType = ""; - if ( bAbcNumbers && bCtPredecessors ) - { - pNumber = x_type_short; - } - else - { - pNumber = bAbcNumbers? x_type_alpha : x_type_numer; - pDelim = (bAbcNumbers && bCtPredecessors)? "-":""; - pCtType = bCtPredecessors? x_type_predec:""; - } - /* type */ - tot_len += sprintf(pStr+tot_len, " %s=\"%s%s%s\"", x_type, pNumber, pDelim, pCtType); - } - sprintf(pStr+tot_len,">"); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - } - else - if ( INCHI_BAS == iINChI ) - { - /* eliminate empty line in plain text output */ - if ( bNoStructLabels ) - { - ; -/* -- removed empty line before InChI --- -#ifndef TARGET_API_LIB - inchi_ios_print( output_file, "\n" ); -#else - ; -#endif -*/ - } - else - { - if ( !(szSdfLabel && szSdfLabel[0]) && !(szSdfValue && szSdfValue[0]) ) - { - tot_len = sprintf( pStr, "%sStructure: %d", pLF, num_input_struct ); - inchi_ios_print( output_file, "%s%s", pStr, pTAB ); - } - else - { - tot_len = sprintf( pStr, "%sStructure: %d.%s%s%s%s", - pLF, - num_input_struct, - SDF_LBL_VAL(szSdfLabel, szSdfValue) ); - if ( lSdfId ) - { - tot_len --; - tot_len += sprintf( pStr + tot_len, ":%ld", lSdfId ); - } - inchi_ios_print( output_file, "%s%s", pStr, pTAB ); - } - } - /* inchi_ios_print( output_file, "%s%s=%s", pLF, (FLAG_SORT_PRINT_ReChI_PREFIX & *pSortPrintINChIFlags)? INCHI_REC_NAME : INCHI_NAME, pLF ); */ - inchi_ios_print( output_file, "%s%s=%s", pLF, INCHI_NAME, pLF ); - } - - - /***************************************************** - * - * version (10-29-2003) - * - ****************************************************/ - if ( INCHI_BAS == iINChI || !(bINChIOutputOptions & INCHI_OUT_EMBED_REC) /* || !bXml */) - { - /* xml: only if the first or not embedded; plain: always */ - szGetTag( IdentLbl, nTag, bTag1 = IL_VERS, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - tot_len += sprintf(pStr + tot_len, "%s", x_curr_ver); - - /* 10-17-2008 Add 'standard' flag if necessary */ - if ( bINChIOutputOptions & INCHI_OUT_STDINCHI ) - tot_len += sprintf(pStr + tot_len, "S"); - - /*if ( bXml ) {*/ /* avoid leading slash in plain output */ - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - /*}*/ - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - - - /***************************************************** - * - * atoms, connection tables and tautomeric info - * - ****************************************************/ - /******************* constitution: dot-disconnected Hill formulas: */ - if ( num_components2[0] || num_components2[1] ) - { - szGetTag( IdentLbl, nTag, bTag1 = INCHI_REC == iINChI? IL_REC_ : IL_FML_, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - tot_len = str_HillFormula(pINChISort, pStr, nStrLen, tot_len, - &bOverflow, bOutType, num_components, bUseMulipliers); - - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, 1 ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - /**************** semicolon/dot-disconnected connection tables */ - szGetTag( IdentLbl, nTag, bTag1 = IL_CONN, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - tot_len2 = str_Connections(pINChISort, pStr, nStrLen, tot_len, - &bOverflow, bOutType, ATOM_MODE, num_components, bUseMulipliers); - /* current version does not output empty (";;;;") connectivity */ - if ( tot_len != tot_len2 /*|| !bXml*/ ) { /* 2004-06-30: never output empty connection table */ - tot_len = tot_len2; - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -2, bPlainTextTags ) ) - goto exit_function; /* pStr overfow */ - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - /************** hydrogen atoms; do not output empty */ - if ( INCHI_SEGM_FILL == INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_h_H_ATOMS] ) ) - { - szGetTag( IdentLbl, nTag, bTag1 = IL_ALLH, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - tot_len2 = str_H_atoms(pINChISort, pStr, nStrLen, tot_len, - &bOverflow, bOutType, ATOM_MODE, TAUT_MODE, - num_components, bUseMulipliers); - if ( tot_len != tot_len2 /*|| !bXml*/ ) { /* 2004-06-21: never output empty */ - tot_len = tot_len2; - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -2, 1 ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - } - - - bFhTag = 0; - - -repeat_INChI_output: - - /***************************************************** - * charge - */ - - nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_q_CHARGE] ); - if ( nSegmAction ) - { - szGetTag( IdentLbl, nTag, bTag1 = IL_CHRG | bFhTag, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - if ( INCHI_SEGM_FILL == nSegmAction ) - { - tot_len = str_Charge2(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, num_components, - bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); - bNonTautNonIsoIdentifierNotEmpty += bSecondNonTautPass; - } - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); - } - } - - /***************************************************** - * removed protons - */ - if ( iCurTautMode == TAUT_YES && !bSecondNonTautPass ) - { - - nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_p_PROTONS] ); - if ( nSegmAction ) - { - szGetTag( IdentLbl, nTag, bTag1 = IL_PROT | bFhTag, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - tot_len += sprintf( pStr + tot_len, "%+d", nNumRemovedProtons ); - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); - } - } - - } - - - /************************************************** - * - * non-isotopic stereo - */ - - { - int i; - i = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_t_SATOMS] ); - i = i; - } - - if ( INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_b_SBONDS] ) || - INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_t_SATOMS] ) || - INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_m_SP3INV] ) || - INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_s_STYPE] ) ) - { - /* stereo */ - szGetTag( IdentLbl, nTag, bTag1 = IL_STER | bFhTag, szTag1, &bAlways ); - if ( bXml ) - { - str_LineStart( szTag1, NULL, 0, pStr, ind ); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - } - - /* sp2 */ - /*if ( bStereoSp2[iCurTautMode] )*/ - if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_b_SBONDS] ) ) - { - szGetTag( IdentLbl, nTag, bTag2 = bTag1 | IL_DBND, szTag2, &bAlways ); - tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); - if ( INCHI_SEGM_FILL == nSegmAction ) - { - tot_len = str_Sp2(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, - bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); - bNonTautNonIsoIdentifierNotEmpty += bSecondNonTautPass; - } - if ( str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); /* sp2 */ - } - } - - /* sp3 */ - /*if ( bStereoSp3[iCurTautMode] )*/ - if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_t_SATOMS] ) ) - { - bRelRac = bRelativeStereo[iCurTautMode] || bRacemicStereo[iCurTautMode]; - szGetTag( IdentLbl, nTag, bTag2 = bTag1 | IL_SP3S, szTag2, &bAlways ); - tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); - if ( INCHI_SEGM_FILL == nSegmAction ) { - tot_len = str_Sp3(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, bRelRac, - bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); - bNonTautNonIsoIdentifierNotEmpty += bSecondNonTautPass; - } - - if (str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags )) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); /* sp3 */ - } - } - - /* bStereoAbsInverted[iCurTautMode] */ - - /* if ( bStereoAbs[iCurTautMode] ) */ - if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_m_SP3INV] ) ) - { - szGetTag( IdentLbl, nTag, bTag2 = bTag1 | IL_INVS, szTag2, &bAlways ); - tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); - if ( INCHI_SEGM_FILL == nSegmAction ) { - tot_len = str_StereoAbsInv(pINChISort, pStr, nStrLen, tot_len, - &bOverflow, bOutType, num_components); - bNonTautNonIsoIdentifierNotEmpty += bSecondNonTautPass; - } - - if (str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags )) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); /* stereo-abs-inv */ - } - } - - /* stereo type */ - /*if ( bRacemicStereo[iCurTautMode] || bRelativeStereo[iCurTautMode] || bStereoAbs[iCurTautMode] )*/ - if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_s_STYPE] ) ) - { - const char *p_stereo = bRelativeStereo[iCurTautMode]? x_rel : - bRacemicStereo[iCurTautMode] ? x_rac : x_abs; - szGetTag( IdentLbl, nTag, bTag2 = bTag1 | IL_TYPS, szTag2, &bAlways ); - tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); - if ( INCHI_SEGM_FILL == nSegmAction ) { - tot_len += MakeDelim( p_stereo, pStr + tot_len, nStrLen-tot_len, &bOverflow); - bNonTautNonIsoIdentifierNotEmpty += bSecondNonTautPass; - } - if (str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags )) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); /* no abs, inv or racemic stereo */ - } - - if ( bXml ) - { - /* close stereo */ - ind -= inc; - if ( str_LineEnd( szTag1, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "////" ); /* sp3, sp2, abs-inv, stereo.type */ - } - } - - - /**************************************************** - * - * Isotopic canonical results - * - ****************************************************/ - nCurINChISegment ++; /* switch from M to MI or from F to FI */ - - /*if ( bIsotopic || !bSecondNonTautPass && bHasIsoH )*/ - if ( INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_i_IATOMS] ) ) - { - /* isotopic #1: composition -- atoms -- do not output in xml if empty */ - szGetTag( IdentLbl, nTag, bTag1 = IL_ISOT | bFhTag, szTag1, &bAlways ); - if ( bXml ) - { - str_LineStart( szTag1, NULL, 0, pStr, ind ); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - } - /* isotopic atoms without mobile H. - * Fixed 2004-06-15: always output if not bXml. Note: - * Previous condition if( bHasIsotopicAtoms[iCurTautMode] || bIsotopic && !bXml) - * did not optput /i in case of only mobile isotopic H - */ - if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_i_IATOMS] ) ) - { - szGetTag( IdentLbl, nTag, bTag2 = bTag1 | IL_ATMS, szTag2, &bAlways ); - tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); - /*if ( bHasIsotopicAtoms[iCurTautMode] )*/ - if ( INCHI_SEGM_FILL == nSegmAction ) - { - tot_len2 = str_IsoAtoms(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, bAbcNumbers, - bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); - bNonTautIsoIdentifierNotEmpty += bSecondNonTautPass; - } - else - { - tot_len2 = tot_len; - } - - tot_len = tot_len2; - if ( str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - - } - - /* isotopic #1a: composition -- exchangeable isotopic H (mobile H only) */ - /*if ( !bSecondNonTautPass && bHasIsoH )*/ - if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_h_H_ATOMS] ) ) - { - szGetTag( IdentLbl, nTag, bTag2 = bTag1 | IL_XCGA, szTag2, &bAlways ); - tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); - tot_len += MakeIsoHString( num_iso_H, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, &bOverflow); - bNonTautIsoIdentifierNotEmpty += bSecondNonTautPass; - if ( str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - - /*************************************************** - * - * Isotopic stereo - * - ***************************************************/ - - /*if ( bIsotopicStereo[iCurTautMode] )*/ - if ( INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_b_SBONDS] ) || - INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_t_SATOMS] ) || - INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_m_SP3INV] ) || - INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_s_STYPE] ) ) - { - /* stereo */ - szGetTag( IdentLbl, nTag, bTag2 = bTag1 | IL_STER, szTag2, &bAlways ); - if ( bXml ) - { - str_LineStart( szTag2, NULL, 0, pStr, ind ); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - } - - /************************ - isotopic #2: sp2 - ************************/ - /*if ( bIsotopicStereoSp2[iCurTautMode] )*/ - if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_b_SBONDS] ) ) - { - szGetTag( IdentLbl, nTag, bTag3 = bTag2 | IL_DBND, szTag3, &bAlways ); - tot_len = str_LineStart( szTag3, NULL, 0, pStr, ind ); - if ( INCHI_SEGM_FILL == nSegmAction ) { - tot_len = str_IsoSp2(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, - bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); - bNonTautIsoIdentifierNotEmpty += bSecondNonTautPass; - } - if ( str_LineEnd( szTag3, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); /* iso sp2 */ - } - } - - /************************ - isotopic #3: sp3 - ************************/ - /*if ( bIsotopicStereoSp3[iCurTautMode] )*/ - if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_t_SATOMS] ) ) - { - bRelRac = bIsotopicRelativeStereo[iCurTautMode] || bIsotopicRacemicStereo[iCurTautMode]; - szGetTag( IdentLbl, nTag, bTag3 = bTag2 | IL_SP3S, szTag3, &bAlways ); - tot_len = str_LineStart( szTag3, NULL, 0, pStr, ind ); - if ( INCHI_SEGM_FILL == nSegmAction ) { - tot_len = str_IsoSp3(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, bRelRac, - bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); - bNonTautIsoIdentifierNotEmpty += bSecondNonTautPass; - } - if ( str_LineEnd( szTag3, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "/" ); /* iso-sp3 */ - } - } - - /* isotopic #4: abs inverted */ - if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_m_SP3INV] ) ) - { - szGetTag( IdentLbl, nTag, bTag3 = bTag2 | IL_INVS, szTag3, &bAlways ); - tot_len = str_LineStart( szTag3, NULL, 0, pStr, ind ); - if ( INCHI_SEGM_FILL == nSegmAction ) { - tot_len = str_IsoStereoAbsInv(pINChISort, pStr, nStrLen, tot_len, - &bOverflow, bOutType, num_components); - bNonTautIsoIdentifierNotEmpty += bSecondNonTautPass; - } - if ( str_LineEnd( szTag3, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "/" ); - } - } - - /* isotopic #5: stereo type. Do not output if it has already been output in non-iso */ - if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_s_STYPE] ) ) - { - const char *p_stereo = bIsotopicRelativeStereo[iCurTautMode]? x_rel : - bIsotopicRacemicStereo[iCurTautMode] ? x_rac : x_abs; - szGetTag( IdentLbl, nTag, bTag3 = bTag2 | IL_TYPS, szTag3, &bAlways ); - tot_len = str_LineStart( szTag3, NULL, 0, pStr, ind ); - if ( INCHI_SEGM_FILL == nSegmAction ) { - tot_len += MakeDelim( p_stereo, pStr + tot_len, nStrLen-tot_len, &bOverflow); - bNonTautIsoIdentifierNotEmpty += bSecondNonTautPass; - } - if ( str_LineEnd( szTag3, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "/" ); /* no abs, inv or racemic stereo */ - } - if ( bXml ) - { - /************************ - close isotopic stereo - ************************/ - ind -= inc; - if ( str_LineEnd( szTag2, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - } - else - { - if ( !bXml ) - { - /* no isotopic stereo */ - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "////" ); /* sp3, sp2, abs-inv, stereo.type */ - } - } - - /* close isotopic */ - if ( bXml ) - { - ind -= inc; - if ( str_LineEnd( szTag1, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "///" ); /* isotopic composition, sp2, sp3 */ - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "//" ); /* inv or racemic stereo */ - } - } - -#if ( CANON_FIXH_TRANS == 1 ) - if ( bOutType == OUT_NONTAUT && bOutputType == OUT_TN && bSecondNonTautPass && - INCHI_SEGM_FILL == INChI_SegmentAction( sDifSegs[DIFL_F][DIFS_o_TRANSP] )) - { - /* find and print non-tautomeric components transposition, if non-trivial */ - AT_NUMB *nTrans_n, *nTrans_s; - - if ( 0 < bin_AuxTautTrans(pINChISort, pINChISort2, &nTrans_n, &nTrans_s, bOutType, num_components) ) - { - /* a non-trivial transposition does exist; output start tag */ - szGetTag( IdentLbl, nTag, bTag1 = IL_TRNS | bFhTag, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - /* print the transposition, cycle after cycle */ - tot_len = str_AuxTautTrans(nTrans_n, nTrans_s, pStr, nStrLen, tot_len, - &bOverflow, TAUT_MODE, num_components); - bNonTautIsoIdentifierNotEmpty += bSecondNonTautPass; - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - /* detected transposition */ - *pSortPrintINChIFlags |= (INCHI_BAS == iINChI)? FLAG_SORT_PRINT_TRANSPOS_BAS : - FLAG_SORT_PRINT_TRANSPOS_REC; - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "/" ); - } - } - } -#endif - - - /************************************************************** - At this point the INChI part of the output has been done. - If this INChI is tautomeric and non-tautomeric results exist - then we need to output non-tautomeric data: - fixed H, - stereo, - isotopic - isotopic stereo - ***************************************************************/ - if ( bOutType == OUT_TN && !bSecondNonTautPass && - bNonTautIsIdenticalToTaut && bTautomeric && bNonTautomeric ) - { - /* Fixed-H layer is empty in the Identifier */ - *pSortPrintINChIFlags |= (INCHI_BAS == iINChI)? FLAG_SORT_PRINT_NO_NFIX_H_BAS : - FLAG_SORT_PRINT_NO_NFIX_H_REC; - *pSortPrintINChIFlags |= (INCHI_BAS == iINChI)? FLAG_SORT_PRINT_NO_IFIX_H_BAS : - FLAG_SORT_PRINT_NO_IFIX_H_REC; - } - - if ( bOutType == OUT_TN && !bNonTautIsIdenticalToTaut /* added 2004-10-04 Fix16 */ -#ifdef OLD_ITEM_DISCOVERY - && bTautomeric && bNonTautomeric -#endif - && INChI_SegmentAction( sDifSegs[DIFL_F][DIFS_f_FORMULA] ) - /* special case: removed isolated H(+): */ - /* || iCurTautMode == TAUT_YES && num_comp[TAUT_YES] < num_comp[TAUT_NON] && - 0 < num_comp[TAUT_NON]*/ - ) - { - /* add the second (non-tautomeric) output */ - bOutType = OUT_NONTAUT; /* pick up only non-tautomeric representation of tautomeric */ - iCurTautMode = TAUT_NON; - pINChISort = pINChISortTautAndNonTaut[TAUT_NON]; - bSecondNonTautPass = 1; - nCurINChISegment = DIFL_F; - num_components = num_comp[iCurTautMode]; /* number of components could change due to removal of isolated H(+) from tautomeric */ - bFhTag = IL_FIXH; - szGetTag( IdentLbl, nTag, bTag1 = bFhTag, szTag1, &bAlways ); - if ( bXml ) - { - /* open non-tautomeric */ - str_LineStart( szTag1, NULL, 0, pStr, ind ); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - } - /***** constitution non-taut: dot-disconnected Hill formulas: -- only if different */ - szGetTag( IdentLbl, nTag, bTag1 = IL_FMLF | bFhTag, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_f_FORMULA] ); - if ( INCHI_SEGM_FILL == nSegmAction ) - { - tot_len2 = str_HillFormula2(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, num_components, bUseMulipliers); - bNonTautNonIsoIdentifierNotEmpty += bSecondNonTautPass; - } - else - { - tot_len2 = tot_len; - } - tot_len = tot_len2; - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - - nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_h_H_ATOMS] ); - if ( INCHI_SEGM_FILL == nSegmAction ) - { - szGetTag( IdentLbl, nTag, bTag1 = IL_HFIX | bFhTag, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); /* open H-fixed */ - /* output the second non-tautomeric item: fixed H -- do not output in xml if empty */ - tot_len2 = str_FixedH_atoms(pINChISort, pStr, nStrLen, tot_len, - &bOverflow, bOutType, ATOM_MODE, num_components, bUseMulipliers); - tot_len = tot_len2; - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - bNonTautNonIsoIdentifierNotEmpty += bSecondNonTautPass; - } - goto repeat_INChI_output; - } - else - { - if ( bOutType == OUT_NONTAUT && bOutputType == OUT_TN && bSecondNonTautPass /* && bTautomeric && bNonTautomeric*/ ) - { - /* the second (non-taut) output has been done; restore variables */ - bOutType = OUT_TN; - iCurTautMode = TAUT_YES; - pINChISort = pINChISortTautAndNonTaut[TAUT_YES]; - bSecondNonTautPass = 0; - num_components = num_comp[iCurTautMode]; - if ( !bNonTautNonIsoIdentifierNotEmpty ) - { - /* Fixed-H layer is empty in the Identifier */ - *pSortPrintINChIFlags |= (INCHI_BAS == iINChI)? FLAG_SORT_PRINT_NO_NFIX_H_BAS : - FLAG_SORT_PRINT_NO_NFIX_H_REC; - } - if ( !bNonTautIsoIdentifierNotEmpty ) - { - /* Fixed-H layer is empty in the Identifier */ - *pSortPrintINChIFlags |= (INCHI_BAS == iINChI)? FLAG_SORT_PRINT_NO_IFIX_H_BAS : - FLAG_SORT_PRINT_NO_IFIX_H_REC; - } - if ( bXml ) - { - /* close non-tautomeric */ - ind -= inc; - szGetTag( IdentLbl, nTag, bTag1 = bFhTag, szTag1, &bAlways ); - if ( str_LineEnd( szTag1, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - bFhTag = 0; - } - } - - - /************************************************ - * output INChI of the reconnected structure * - ************************************************/ - bEmbeddedOutputCalled = 0; - if ( bDisconnectedCoord && INCHI_BAS == iINChI && - (bINChIOutputOptions & INCHI_OUT_EMBED_REC) && num_components2[INCHI_REC] ) - { - int nRet; - bEmbeddedOutputCalled = 1; - - if ( !bXml ) - { - /* output blank line before /R: in case of bPlainTextCommnts=1 */ - inchi_ios_print( output_file, "%s", pLF ); - } - /* end of disconnected INChI output */ - - nRet = OutputINChI1( pStr, nStrLen, - pINChISortTautAndNonTaut2, - INCHI_REC, NULL, - 0 /*bDisconnectedCoord*/, bOutputType, - bINChIOutputOptions | INCHI_OUT_NO_AUX_INFO, - bXml, bAbcNumbers, bCtPredecessors, bNoStructLabels, - num_components2, num_non_taut2, num_taut2, - output_file, log_file, - num_input_struct, - szSdfLabel, szSdfValue, lSdfId, - pSortPrintINChIFlags, - save_opt_bits); - if ( !nRet ) - goto exit_function; /* error */ - } - - if ( bXml ) - { - /* close INChI identifier (basic) */ - ind -= inc; - if ( str_LineEnd( x_basic, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - else - { - /* save InChI creation options if requested ...*/ - if ( !bEmbeddedOutputCalled) - { - if ( bINChIOutputOptions & INCHI_OUT_SAVEOPT ) - { - /* ... and not std-InChI output */ - if ( 0 == (bINChIOutputOptions & INCHI_OUT_STDINCHI) ) - { - char let1, let2; - GetSaveOptLetters(save_opt_bits, &let1, &let2); - inchi_ios_print( output_file, "\\%c%c", let1, let2 ); - } - } - } - - if ( !bEmbeddedOutputCalled && !bPlainTextCommnts ) - { /* plain text comment earlier ended with LF */ - inchi_ios_print( output_file, "%s%s", - (!num_components2[0] && !num_components2[1])? "//":"", /* empty InChI=// */ - (bINChIOutputOptions & INCHI_OUT_NO_AUX_INFO)? "\n" : pTAB ); - /* end of INChI= output */ - } - - - } - -output_aux_info: - - bFhTag = 0; - - if( !(bINChIOutputOptions & INCHI_OUT_NO_AUX_INFO) ) - { - /* output aux info */ - - /************************************************************* - * - * Aux info non-isotopic - * - *************************************************************/ - - num_components = num_comp[iCurTautMode]; - if ( bXml ) - { - /* aux. info header */ - /* empty line if INChI output has been printed */ - if ( !(bINChIOutputOptions & INCHI_OUT_ONLY_AUX_INFO) ) - { - inchi_ios_print( output_file, "\n" ); - } - /* basic.aux-info title, version */ - tot_len = sprintf(pStr, "%s<%s %s=\"%s\"", - SP(ind), x_aux_basic, x_ver, x_curr_ver ); - if ( INCHI_REC == iINChI || INCHI_BAS == iINChI && bDisconnectedCoord ) - { - tot_len += sprintf(pStr+tot_len, " %s=\"%d\"", x_reconnected, iINChI ); - } - if ( bAbcNumbers ) - { - /* type */ - const char *pNumber = x_type_short; - tot_len += sprintf(pStr+tot_len, " %s=\"%s\"", x_type, pNumber); - } - - sprintf(pStr+tot_len,">"); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - if ( !(bINChIOutputOptions & INCHI_OUT_ONLY_AUX_INFO) ) - { - /* comment */ - tot_len = sprintf( pStr, "%s<%s>", SP(ind), x_aux_comm ); - inchi_ios_print( output_file, "%s\n", pStr ); - } - } - else - { - if ( INCHI_BAS == iINChI ) - { - tot_len = sprintf( pStr, "AuxInfo=" ); /* in wINChI window, separate INChI: from AuxInfo: with blank line */ - inchi_ios_print( output_file, "%s%s%s", - /* blank line before AuxInfo in winchi window unless it is an annotation */ - (bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW) ? "\n":"", - pStr, - pLF); - szGetTag( AuxLbl, nTag, bTag1 = AL_VERS, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - tot_len += sprintf(pStr + tot_len, "%s", x_curr_ver); - /* avoid leading slash in plain output */ - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( INCHI_REC == iINChI ) - { - szGetTag( AuxLbl, nTag, bTag1 = AL_REC_, szTag1, &bAlways ); - inchi_ios_print( output_file, "%s%s", szTag1, pLF ); - } - } - - } - /* normalization type */ - if ( num_components2[0] || num_components2[1] ) - { - szGetTag( AuxLbl, nTag, bTag1 = AL_NORM, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - tot_len += sprintf( pStr + tot_len, "%d", (bTautomeric && bTautomericOutputAllowed)? bTautomeric : 0); - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - - - -repeat_INChI_Aux_output: - - /************************************************************** - * Original atom numbers in order of canonical numbers - **************************************************************/ - if ( num_components2[0] || num_components2[1] ) - { - szGetTag( AuxLbl, nTag, bTag1 = (bSecondNonTautPass? AL_FIXN : AL_ANBR) | bFhTag, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - /* original numbering output */ - tot_len = str_AuxNumb(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, - bSecondNonTautPass, bOmitRepetitions); - - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - /********************************************** - * Symmetry numbers (constit. equivalence) - **********************************************/ - if ( bAtomEqu[iCurTautMode] ) - { - /* aux equ atoms */ - /* 1. Compare to tautomeric equivalence (in case of second, non-taut, pass only) */ - /* 2. Compare to the previous component if (1) failed to find equivalence */ - szGetTag( AuxLbl, nTag, bTag1 = AL_AEQU | bFhTag, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - tot_len = str_AuxEqu(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, - bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); - - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "/" ); - } - } - /***************************************************** - * Tautomeric groups equivalence - *****************************************************/ - if ( bTautomericOutputAllowed && bTautomeric && bTautEqu[iCurTautMode] && !bSecondNonTautPass ) - { - /***************************************************** - * Tautomeric groups constitutional equivalence - */ - /* aux tgroup equ */ - szGetTag( AuxLbl, nTag, bTag1 = AL_GEQU | bFhTag, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - tot_len = str_AuxTgroupEqu(pINChISort, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, - num_components, bUseMulipliers); - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - else - { - if ( !bXml && bTautomericOutputAllowed && bTautomeric ) - { - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "/" ); - } - } - - /**************************************************** - * Inverted stereo -- sp3 only + canonical numbering - ****************************************************/ - if ( bInvStereo[iCurTautMode] ) - { - szGetTag( AuxLbl, nTag, bTag1 = AL_STER | bFhTag, szTag1, &bAlways ); - if ( bXml ) - { - /*************************** - inv stereo start tag - ****************************/ - str_LineStart( szTag1, NULL, 0, pStr, ind ); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - } - /**************************** - inverted sp3 start tag - *****************************/ - szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_SP3I, szTag2, &bAlways ); - tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); - tot_len = str_AuxInvSp3(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, - bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); - if ( str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - - /************************************* - inverted sp3 canonical numbering - **************************************/ - if ( bInvStereoOrigNumb[iCurTautMode] ) - { - szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_SP3N, szTag2, &bAlways ); - tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); - tot_len = str_AuxInvSp3Numb(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, - bSecondNonTautPass, bOmitRepetitions); - if ( str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); - } - } - - if ( bXml ) - { - /* close sp3 inv */ - ind -= inc; - if ( str_LineEnd( szTag1, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "//" ); - } /* Inverted stereo -- sp3 only + canonical numbering */ - } - - - /* omitted undefined/unknown non-isotopic stereo */ - if ( bXml ) - { - if ( bIgn_UU_Sp2[iCurTautMode] || bIgn_UU_Sp3[iCurTautMode] ) - { - /* */ - szGetTag( IdentLbl, nTag, bTag1 = IL_STER, szTag1, &bAlways ); - tot_len = PrintXmlStartTag( pStr, ind, 3, szTag1, - (bIgn_UU_Sp2[iCurTautMode])? x_ign_uu_sp2 : NULL, 1, - (bIgn_UU_Sp3[iCurTautMode])? x_ign_uu_sp3 : NULL, 1, - NULL, 0, NULL, 0, NULL, 0, NULL, 0 ); - inchi_ios_print( output_file, "%s\n", pStr ); - } - } - - /*************************************************************** - * - * Additional information: charges, radicals, - * special valences, coordinates - * - ***************************************************************/ - /************************************************************** - * - * Aux info isotopic - * - **************************************************************/ -repeat_INChI_Aux_Iso_output: - /* if InChI Fixed-H isotopic is empty then do not output corresponding AuxInfo */ - i = bSecondNonTautPass && - (*pSortPrintINChIFlags & ((INCHI_BAS == iINChI)? FLAG_SORT_PRINT_NO_IFIX_H_BAS : - FLAG_SORT_PRINT_NO_IFIX_H_REC )); - - if ( bIsotopic && !i && - (bIsotopicOrigNumb[iCurTautMode] || - bIsotopicAtomEqu[iCurTautMode] || - bTautomericOutputAllowed && bTautomeric && bIsotopicTautEqu[iCurTautMode] || - bInvIsotopicStereo[iCurTautMode] || - bXml && ( bIgn_UU_Sp3_Iso[iCurTautMode] || bIgn_UU_Sp2_Iso[iCurTautMode] ) ) ) - { - /*************************************/ - /* isotopic aux info header */ - /*************************************/ - szGetTag( AuxLbl, nTag, bTag1 = AL_ISOT | bFhTag, szTag1, &bAlways ); - if ( bXml ) - { - str_LineStart( szTag1, NULL, 0, pStr, ind ); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - } - else - { - pStr[tot_len = 0] = '\0'; - } - /***************************************************************** - * Original atom numbers in order of isotopic canonical numbers - *****************************************************************/ - szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_ISON, szTag2, &bAlways ); - if ( bIsotopicOrigNumb[iCurTautMode] ) - { - tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); - tot_len = str_AuxIsoNumb(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, - bSecondNonTautPass, bOmitRepetitions); - if ( str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( !bXml ) - { - /*if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" );*/ - inchi_ios_print( output_file, "%s%s", szTag2, pLF ); /* mark isotopic output */ - } - } - - /*************************/ - /* Isotopic symmetry */ - /*************************/ - if ( bIsotopicAtomEqu[iCurTautMode] ) - { - /* atoms */ - szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_AEQU, szTag2, &bAlways ); - tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); - tot_len = str_AuxIsoEqu(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, - bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); - if ( str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -2/*was -1: Fix15*/, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "/" ); - } - } - - /********************************/ - /* Tautomeric groups, isotopic */ - /********************************/ - if ( bTautomericOutputAllowed && bTautomeric && bIsotopicTautEqu[iCurTautMode] ) - { - /********************************************/ - /* Isotopic tautomeric groups equivalence */ - /********************************************/ - szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_GEQU, szTag2, &bAlways ); - tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); - tot_len = str_AuxIsoTgroupEqu(pINChISort, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, - bOmitRepetitions, bUseMulipliers); - if ( str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -2/*was -1: Fix15*/, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( !bXml && bTautomericOutputAllowed && bTautomeric ) - { - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "/" ); - } - } - - /************************************* - * Isotopic inverted stereo - *************************************/ - if ( bInvIsotopicStereo[iCurTautMode] ) - { - szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_STER, szTag2, &bAlways ); - if ( bXml ) - { - /************************************ - inv isotopic stereo start tag - *************************************/ - str_LineStart( szTag2, NULL, 0, pStr, ind ); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - } - /************************************* - inverted isotopic sp3 start tag - **************************************/ - szGetTag( AuxLbl, nTag, bTag3 = bTag2 | AL_SP3I, szTag3, &bAlways ); - tot_len = str_LineStart( szTag3, NULL, 0, pStr, ind ); - tot_len = str_AuxInvIsoSp3(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, - bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); - if ( str_LineEnd( szTag3, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - /********************************************* - inverted isotopic sp3 canonical numbering - **********************************************/ - if ( bInvIsotopicStereoOrigNumb[iCurTautMode] ) - { - szGetTag( AuxLbl, nTag, bTag3 = bTag2 | AL_SP3N, szTag3, &bAlways ); - tot_len = str_LineStart( szTag3, NULL, 0, pStr, ind ); - tot_len = str_AuxInvIsoSp3Numb(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, - bSecondNonTautPass, bOmitRepetitions); - if ( str_LineEnd( szTag3, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "/" ); - } - } - if ( bXml ) - { - /* close sp3 inv */ - ind -= inc; - if ( str_LineEnd( szTag2, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "//" ); - } - } - - /* totally omitted undefined/unknown isotopic stereo */ - if ( bXml ) - { - if ( bIgn_UU_Sp3_Iso[iCurTautMode] || bIgn_UU_Sp2_Iso[iCurTautMode] ) - { - /* */ - szGetTag( IdentLbl, nTag, bTag1 = IL_STER, szTag1, &bAlways ); - tot_len = PrintXmlStartTag( pStr, ind, 3, szTag1, - (bIgn_UU_Sp2_Iso[iCurTautMode])? x_ign_uu_sp2 : NULL, 1, - (bIgn_UU_Sp3_Iso[iCurTautMode])? x_ign_uu_sp3 : NULL, 1, - NULL, 0, NULL, 0, NULL, 0, NULL, 0 ); - inchi_ios_print( output_file, "%s\n", pStr ); - } - } - - - if ( bXml ) - { - /***************** close isotopic ***********************/ - ind -= inc; - if ( str_LineEnd( szTag1, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - } /* Aux info isotopic */ - - -#if ( CANON_FIXH_TRANS != 1 ) - if ( bSecondNonTautPass ) - { - /* find and print non-tautomeric components transposition, if non-trivial */ - AT_NUMB *nTrans_n, *nTrans_s; - if ( 0 < bin_AuxTautTrans(pINChISort, pINChISort2, &nTrans_n, &nTrans_s, bOutType, num_components) ) { - /* a non-trivial transposition does exist; output start tag */ - tot_len = str_LineStart( tag=x_aux_trans, NULL, 0, pStr, ind ); - /* print the transposition, cycle after cycle */ - tot_len = str_AuxTautTrans(nTrans_n, nTrans_s, pStr, nStrLen, tot_len, - &bOverflow, TAUT_MODE, num_components); - if ( str_LineEnd( bXml? tag:p_aux_at_inv_nbr, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - /* detected transposition */ - *pSortPrintINChIFlags |= (INCHI_BAS == iINChI)? FLAG_SORT_PRINT_TRANSPOS_BAS : - FLAG_SORT_PRINT_TRANSPOS_REC; - } else - if ( !bXml ) { - if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); - } - } -#endif - - /************************************************************** - At this point the INChI_Aux part of the output has been completed. - If this INChI is tautomeric and non-tautomeric results exist - then we need to output non-tautomeric auxilialy data - (same as above excluding tautomeric information) - Currently this is enabled for xml output only - ***************************************************************/ - - if ( bOutType == OUT_TN && bTautomeric && bNonTautomeric && - /* Check whether the Fixed-H layer is empty */ - (*pSortPrintINChIFlags & ((INCHI_BAS == iINChI)? FLAG_SORT_PRINT_NO_NFIX_H_BAS : - FLAG_SORT_PRINT_NO_NFIX_H_REC )) && - (*pSortPrintINChIFlags & ((INCHI_BAS == iINChI)? FLAG_SORT_PRINT_NO_IFIX_H_BAS : - FLAG_SORT_PRINT_NO_IFIX_H_REC )) - ) - { - bNonTautomeric = 0; /* bNonTautIdentifierNotEmpty == 0 => no fixed H info 02-10-2995 */ - } - - if ( bOutType == OUT_TN && bTautomeric && bNonTautomeric ) - { - /* add the second (non-tautomeric) output */ - bOutType = OUT_NONTAUT; - iCurTautMode = TAUT_NON; - pINChISort = pINChISortTautAndNonTaut[TAUT_NON]; - bSecondNonTautPass = 1; - num_components = num_comp[iCurTautMode]; - bFhTag = AL_FIXH; - if ( bXml ) - { - szGetTag( AuxLbl, nTag, bTag1 = bFhTag, szTag1, &bAlways ); - str_LineStart( szTag1, NULL, 0, pStr, ind ); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - } - else - { - pStr[tot_len=0] = '\0'; - } - - /* if InChI Fixed-H isotopic is empty then do not output corresponding AuxInfo */ - if ( !(*pSortPrintINChIFlags & - ((INCHI_BAS == iINChI)? FLAG_SORT_PRINT_NO_NFIX_H_BAS : - FLAG_SORT_PRINT_NO_NFIX_H_REC )) - ) - { - goto repeat_INChI_Aux_output; - } - else - { - goto repeat_INChI_Aux_Iso_output; - } - } - else - { - if ( bOutType == OUT_NONTAUT && bOutputType == OUT_TN && bTautomeric && bNonTautomeric ) - { - /* the second (non-taut) output has been done; restore variables */ - bOutType = OUT_TN; - iCurTautMode = TAUT_YES; - pINChISort = pINChISortTautAndNonTaut[TAUT_YES]; - bSecondNonTautPass = 0; - /* set correct num components for the reversibility info 02-10-2005 */ - num_components = num_comp[iCurTautMode]; - if ( bXml ) - { - /* close non-tautomeric */ - szGetTag( AuxLbl, nTag, bTag1 = bFhTag, szTag1, &bAlways ); - ind -= inc; - if ( str_LineEnd( szTag1, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - bFhTag = 0; - } - } - - - /***************************************/ - /* charges, radicals, unusual valences */ - /***************************************/ - if ( !bSecondNonTautPass && bChargesRadVal[iCurTautMode] ) - { - /* aux equ atoms */ - /* 1. Compare to tautomeric equivalence (in case of second, non-taut, pass only) */ - /* 2. Compare to the previous component if (1) failed to find equivalence */ - szGetTag( AuxLbl, nTag, bTag1 = AL_CRV_ | bFhTag, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - tot_len = str_AuxChargeRadVal(pINChISort, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, - num_components, bUseMulipliers); - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - - /* output the original input structure -- quick fix */ - if ( !bSecondNonTautPass && pOrigStruct && pOrigStruct->num_atoms && - pOrigStruct->szAtoms && pOrigStruct->szBonds && pOrigStruct->szCoord ) - { - int length, cur_pos, line_len, last_pos, nMaxLineLen; - char *p; - nMaxLineLen = inchi_min( 80, nStrLen ); /* restrict line length to 80 characters */ - /********************** - reversibility info - **********************/ - szGetTag( AuxLbl, nTag, bTag1 = AL_REVR | bFhTag, szTag1, &bAlways ); - if ( bXml ) - { - str_LineStart( szTag1, NULL, 0, pStr, ind ); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - } - /* === atoms === */ - szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_ATMR, szTag2, &bAlways ); - if ( bXml ) - { - str_LineStart( szTag2, NULL, 0, pStr, ind ); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - /* first line indent */ - strcpy( pStr, SP(ind)); - tot_len = ind; - } - else - { - pStr[tot_len = 0] = '\0'; - inchi_ios_print( output_file, "%s%s", szTag2, pStr ); - } - p = pOrigStruct->szAtoms; - length = strlen( p ); - line_len = nMaxLineLen - tot_len; - for ( cur_pos = 0; cur_pos < length; cur_pos = last_pos ) - { - if ( length - cur_pos >= line_len ) - { - last_pos = cur_pos + line_len; - /* search backward for the nearest first atom letter (always uppercase) */ - while ( cur_pos < last_pos && !isupper( UCINT p[last_pos] ) ) { - last_pos --; - } - } - else - { - last_pos = length; - } - if ( last_pos > cur_pos ) - { - memcpy( pStr + tot_len, p+cur_pos, last_pos - cur_pos ); - pStr[tot_len + last_pos - cur_pos] = '\0'; - inchi_ios_print( output_file, "%s%s", pStr, !bXml && bPlainTextTags? "" : "\n" ); - } - else - { - break; - } - } - if ( bXml ) - { - ind -= inc; - pStr[0] = '\0'; - if ( str_LineEnd( szTag2, 0, nMaxLineLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - else - { - if ( pLF[0] ) - { - inchi_ios_print( output_file, "%s", pLF ); - } - } - - - /* === bonds === */ - szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_BNDR, szTag2, &bAlways ); - if ( bXml ) - { - str_LineStart( szTag2, NULL, 0, pStr, ind ); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - /* first line indent */ - strcpy( pStr, SP(ind)); - tot_len = ind; - } - else - { - pStr[tot_len = 0] = '\0'; - inchi_ios_print( output_file, "%s%s", szTag2, pStr ); - } - - p = pOrigStruct->szBonds; - length = strlen( p ); - line_len = nMaxLineLen - tot_len; - for ( cur_pos = 0; cur_pos < length; cur_pos = last_pos ) - { - if ( length - cur_pos >= line_len ) - { - last_pos = cur_pos + line_len - 1; - /* search backward for the nearest first bond delimiter ";" */ - while ( cur_pos < last_pos && p[last_pos] != ';' ) - { - last_pos --; - } - if ( cur_pos < last_pos ) - { - last_pos ++; /* include ';' at the end of the line */ - } - } - else - { - last_pos = length; - } - if ( last_pos > cur_pos ) - { - memcpy( pStr + tot_len, p+cur_pos, last_pos - cur_pos ); - pStr[tot_len + last_pos - cur_pos] = '\0'; - inchi_ios_print( output_file, "%s%s", pStr, !bXml && bPlainTextTags? "" : "\n" ); - } - else - { - break; - } - } - - if ( bXml ) - { - ind -= inc; - pStr[0] = '\0'; - if ( str_LineEnd( szTag2, 0, nMaxLineLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - else - { - if ( pLF[0] ) - { - inchi_ios_print( output_file, "%s", pLF ); - } - } - - - /* === coordinates === */ - szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_XYZR, szTag2, &bAlways ); - if ( bXml ) - { - str_LineStart( szTag2, NULL, 0, pStr, ind ); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - /* first line indent */ - strcpy( pStr, SP(ind)); - tot_len = ind; - } - else - { - pStr[tot_len = 0] = '\0'; - inchi_ios_print( output_file, "%s%s", szTag2, pStr ); - } - - p = pOrigStruct->szCoord; - length = strlen( p ); - line_len = nMaxLineLen - tot_len; - for ( cur_pos = 0; cur_pos < length; cur_pos = last_pos ) - { - if ( length - cur_pos >= line_len ) - { - last_pos = cur_pos + line_len - 1; - /* search backward for the nearest first coord. delimiter ";" */ - while ( cur_pos < last_pos && p[last_pos] != ';' ) - { - last_pos --; - } - if ( cur_pos < last_pos ) - { - last_pos ++; /* include ';' at the end of the line */ - } - } - else - { - last_pos = length; - } - if ( last_pos > cur_pos ) - { - memcpy( pStr + tot_len, p+cur_pos, last_pos - cur_pos ); - pStr[tot_len + last_pos - cur_pos] = '\0'; - inchi_ios_print( output_file, "%s%s", pStr, !bXml && bPlainTextTags? "" : "\n" ); - } - else - { - break; - } - } - - if ( bXml ) - { - ind -= inc; - pStr[0] = '\0'; - if ( str_LineEnd( szTag2, 0, nMaxLineLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - else - { - if ( pLF[0] ) - { - inchi_ios_print( output_file, "%s", pLF ); - } - } - - if ( bXml ) - { - /*************************** - close reversibility info - ***************************/ - ind -= inc; - if ( str_LineEnd( szTag1, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - } - - - - /************************************************ - * output INChI_Aux of the reconnected structure * - ************************************************/ - bEmbeddedOutputCalled = 0; - if ( bDisconnectedCoord && INCHI_BAS == iINChI && (bINChIOutputOptions & INCHI_OUT_EMBED_REC) && - num_components2[INCHI_REC] && !(bINChIOutputOptions & INCHI_OUT_NO_AUX_INFO) ) - { - int nRet; - bEmbeddedOutputCalled = 1; - if ( !bXml ) - { - inchi_ios_print( output_file, "%s", pLF ); - } - - nRet = OutputINChI1(pStr, nStrLen, - pINChISortTautAndNonTaut2, - INCHI_REC, - NULL, - 0 /*bDisconnectedCoord*/, bOutputType, - INCHI_OUT_ONLY_AUX_INFO | bINChIOutputOptions, - bXml, bAbcNumbers, bCtPredecessors, bNoStructLabels, - num_components2, - num_non_taut2, num_taut2, - output_file, log_file, - num_input_struct, - szSdfLabel, szSdfValue, lSdfId, - pSortPrintINChIFlags, - save_opt_bits); - if ( !nRet ) - goto exit_function; /* error */ - } - - /* close INChI_Aux */ - if ( bXml ) - { - ind -= inc; - if ( str_LineEnd( x_aux_basic, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - else - { - if ( !bEmbeddedOutputCalled && !bPlainTextCommnts ) - { - inchi_ios_print( output_file, "%s\n", (!num_components2[0] && !num_components2[1])? "//":"" ); - /* plain text comment earlier ended with LF */ - } - } - - - /* in wINChI window, separate AuxInfo: from InChIKey: with blank line */ - inchi_ios_print( output_file, "%s", - (bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW) ? "\n":""); - - - - - } /* end of output aux info */ - - - ret = 1; -exit_function: - - if ( bOverflow ) - { - strcpy( pStr, "Output buffer overflow"); - if ( bXml ) - { - OutputINChIXmlError( output_file, pStr, nStrLen, ind /*, 0*/ /* err number */, pStr, _IS_FATAL ); - } - else - { - inchi_ios_print( output_file, "\nFATAL ERROR: %s\n", pStr ); - } - } - - /* inchi_free( pStr ); */ - return ret; - - -} /* OutputINChI1 */ - - - -/***************************************************************/ -int str_LineStart( const char *tag, char *tag2, int val2, char *pStr, int ind ) -{ - int tot_len = 0; - if ( ind >= 0 ) { - if ( ind > 0 ) { - /* xml: indent */ - memset( pStr + tot_len, ' ', ind ); - tot_len += ind; - } - /* xml: tag */ - strcpy( pStr + tot_len, x_line_opening ); - strcat( pStr + tot_len, tag ); - if ( tag2 ) { - tot_len += strlen(pStr + tot_len); - tot_len += sprintf( pStr + tot_len, " %s=\"%d\"%s", tag2, val2, x_close_line ); - - } else { - strcat( pStr + tot_len, x_close_line ); - tot_len += strlen(pStr + tot_len); - } - } else { - pStr[tot_len] = '\0'; - } - return tot_len; -} -/***************************************************************/ -int str_LineEnd( const char *tag, int tot_len, int nStrLen, int *bOverflow, char *pStr, int ind, int bPlainTextTags ) -{ - static int add_tag_len = sizeof(x_line_closing)-1 + sizeof(x_close_line)-1; - int tag_len; - /* check buffer overflow */ - if ( *bOverflow ) - return 1; - if ( ind >= 0 ) { /* xml */ - tag_len = ind + add_tag_len + strlen(tag); - if ( tot_len + tag_len < nStrLen - 2 ) { - /* output " \n" */ - tot_len += sprintf( pStr + tot_len, "%s%s%s%s\n", SP(ind), x_line_closing, tag, x_close_line ); - } else { - *bOverflow += 1; - return 1; - } - } else { /* plain */ - pStr[tot_len] = '\0'; /* add zero termination 2004-04-26 */ - /* insert plain text tag if: - (a) pStr has non-zero length, or - (b) ind < -1 - */ - - if ( pStr[0] || ind < -1 ) { - tag_len = bPlainTextTags? strlen( tag ):0; - if ( tot_len + tag_len < nStrLen - 2 ) { - if ( tag_len > 0 ) { - /* insert plain text tag */ - memmove( pStr+tag_len, pStr, tot_len + 1 ); - memcpy( pStr, tag, tag_len ); - } - } else { - *bOverflow += 1; - return 1; - } - }/* else - if ( bPlainTextTags == 1 ) { - strcpy( pStr, "/" ); - }*/ - } - return 0; -} - - - -/**********************************************************************************************/ -int CleanOrigCoord( MOL_COORD szCoord, int delim ) -{ -#define MIN_BOND_LENGTH (1.0e-6) - char szVal[LEN_COORD+1]; - MOL_COORD szBuf; - char *q; - int len, last, fst, dec_pnt, num_zer=0, len_buf = 0, e; - int k, i; - double coord; - - for ( k = 0; k < NUM_COORD*LEN_COORD; k += LEN_COORD ) { - memcpy( szVal, szCoord+k, LEN_COORD ); - szVal[LEN_COORD] = '\0'; - LtrimRtrim(szVal, &len); - coord = strtod(szVal, &q); - if ( MIN_BOND_LENGTH > fabs(coord) ) { - strcpy( szVal, "0" ); - len = 1; - num_zer ++; - } else { - len = q - szVal; - /* last = (last mantissa digit position + 1) */ - if ( (q = strchr(szVal, 'e')) || (q = strchr(szVal, 'E')) || - (q = strchr(szVal, 'd')) || (q = strchr(szVal, 'D')) ) { - /* floating point */ - last = q - szVal; - /* remove (+) and leading zeroes from the exponent */ - e = (int)strtol( szVal+last+1, &q, 10 ); /* exponent */ - if ( e ) { - /* new exp; update the length */ - len = last+1+sprintf( szVal+last+1, "%d", e ); /* print exp without leading zeroes and '+' */ - } else { - /* exponent is zero */ - len = last; - } - } else { - last = len; - } - /* fst = (first mantissa digit); fst=1 if the sign is present, otherwise 0 */ - fst = (szVal[0]!='.' && !isdigit( UCINT szVal[0] )); - /* dec_pnt = (decimal point position) or last */ - if ( q = strchr(szVal, '.') ) { - dec_pnt = q - szVal; - } else { - dec_pnt = last; - } - last -= 1; /* last mantissa digit position */ - /* remove trailing zeroes in the range dec_pnt+1..last-1 */ - for ( i = last; dec_pnt < i && '0' == szVal[i]; i -- ) - ; - if ( i == dec_pnt ) { - i --; /* remove decimal point, too */ - } - if ( i < last ) { - memmove( szVal+i+1, szVal+last+1, len-last ); - len -= last-i; - } - /* remove leading zeroes */ - for ( i = fst; i < len && '0' == szVal[i]; i ++ ) - ; - if ( i > fst ) { - memmove( szVal + fst, szVal+i, len-fst ); - len -= i-fst; - } - } - if ( len_buf ) - szBuf[len_buf++] = delim; - memcpy( szBuf + len_buf, szVal, len ); /* does not copy zero termination*/ - len_buf += len; - } - /* zero termination */ - if ( len_buf < (int)sizeof(MOL_COORD) ) { - memset( szBuf+len_buf, 0, sizeof(MOL_COORD) - len_buf); - } - memcpy( szCoord, szBuf, sizeof(MOL_COORD) ); - return num_zer; -#undef MIN_BOND_LENGTH -} - -/******************************************************************************************/ -int WriteOrigCoord( int num_inp_atoms, MOL_COORD *szMolCoord, int *i, char *szBuf, int buf_len ) -{ - - int j, num_zer, len, cur_len; - char *p; - MOL_COORD szCurCoord; - cur_len = 0; - for ( j = *i; j < num_inp_atoms; ) { - memcpy( szCurCoord, szMolCoord[j], sizeof(szCurCoord)); - num_zer = CleanOrigCoord( szCurCoord, ',' ); - if ( NUM_COORD == num_zer ) { - len = 0; - } else { - if ( p = (char *)memchr( szCurCoord, '\0', sizeof(szCurCoord)) ) { - len = p - szCurCoord; - } else { - len = sizeof(szCurCoord); - } - } - if ( len + cur_len + 1 < buf_len ) { - if ( len ) { - memcpy( szBuf + cur_len, szCurCoord, len * sizeof(szBuf[0]) ); - } - szBuf[cur_len += len] = ';'; - cur_len ++; - j ++; - } else { - break; - } - } - szBuf[cur_len] = '\0'; - *i = j; /* next item */ - return cur_len; -} -/******************************************************************************************/ -/* - number of atoms - [c|n] chiral/nonchiral - - Element - #valence - +/-[charge>1] - .#rad (#rad=1, 2, 3: singlet, doulet, triplet) - [.]i#iso_mass - [.]{o|e|u|?} atom parity = {1:2:3:4} - [.]h[#of 1H>1] - [.]d[#of 2H>1] - [.]t[#of 3H>1] - - Note: . occurs only once and only if radical or 1-character element -*/ -int WriteOrigAtoms( int num_inp_atoms, inp_ATOM *at, int *i, char *szBuf, int buf_len, STRUCT_DATA *sd) -{ - int j, k, n, len, len0, cur_len, val, bonds_val, mw, parity, num_trans, is_ok, b_self; - static char szIsoH[] = "hdt"; - char szCurAtom[32]; - AT_NUMB nNeighOrder[MAXVAL], neigh; - - cur_len = 0; - if ( 0 == *i ) { - cur_len = sprintf( szBuf, "%d%s", num_inp_atoms, - (sd->bChiralFlag & FLAG_INP_AT_CHIRAL)? "c" : - (sd->bChiralFlag & FLAG_INP_AT_NONCHIRAL)? "n" : "" ); - } - for ( j = *i; j < num_inp_atoms; ) { - /* tetrahedral parity treatment */ - parity = 0; - num_trans = 0; - if ( at[j].p_parity ) { - /* verify neighbors */ - is_ok = 1; - b_self = 0; - for ( n = 0, k = 0; n < MAX_NUM_STEREO_ATOM_NEIGH; n ++ ) { - neigh = at[j].p_orig_at_num[n]-1; - if ( is_in_the_list( at[j].neighbor, neigh, at[j].valence ) && - at[neigh].orig_at_number == at[j].p_orig_at_num[n] ) { - /* real neighbor */ - nNeighOrder[k ++] = at[j].p_orig_at_num[n]; - } else - if ( (int)neigh == j && at[neigh].orig_at_number == at[j].p_orig_at_num[n] ) { - /* central atom is a neighbor */ - num_trans = n; /* move this neighbor to 0 position permutation parity */ - b_self ++; - } else { - is_ok = 0; - break; - } - } - if ( is_ok && b_self <= 1 && b_self + k == MAX_NUM_STEREO_ATOM_NEIGH ) { - num_trans += insertions_sort( nNeighOrder, k, sizeof(nNeighOrder[0]), comp_AT_RANK ); - if ( ATOM_PARITY_WELL_DEF( at[j].p_parity ) ) { - parity = 2 - (num_trans + at[j].p_parity) % 2; - } else - if ( ATOM_PARITY_ILL_DEF( at[j].p_parity ) ) { - parity = at[j].p_parity; - } else { - ; /* invalid atom parity */ - } - } else { - ;/* add error message here */ - } - } - - len = len0 = strlen( at[j].elname ); - memcpy( szCurAtom, at[j].elname, len ); - bonds_val = nBondsValenceInpAt( at+j, NULL, NULL ); - - if ( (val=needed_unusual_el_valence( at[j].el_number, at[j].charge, at[j].radical, - at[j].chem_bonds_valence, bonds_val, at[j].num_H, at[j].valence )) || - at[j].charge || at[j].radical || at[j].iso_atw_diff || NUM_ISO_H(at,j) || parity ) { - /* valence */ - if ( val ) { - len += sprintf( szCurAtom + len, "%d", val > 0? val : 0 ); - } - /* charge */ - if ( val = at[j].charge ) { - szCurAtom[len++] = val>0? '+' : '-'; - if ( (val = abs(val)) > 1 ) { - len += sprintf( szCurAtom + len, "%d", val ); - } - } - /* radical */ - if ( val = at[j].radical ) { - len += sprintf(szCurAtom + len, ".%d", val); - } - /* isotopic shift */ - if ( val = at[j].iso_atw_diff ) { - mw = get_atw_from_elnum( at[j].el_number ); - if ( val == 1 ) - val = mw; - else - if ( val > 0 ) - val = mw + val -1; - else - val = mw + val; - len += sprintf( szCurAtom + len, "%si%d", len == len0? ".":"", val ); - } - /* parity */ - if ( parity ) { - len += sprintf( szCurAtom + len, "%s%s", len == len0? ".":"", - parity == AB_PARITY_ODD? "o" : - parity == AB_PARITY_EVEN? "e" : - parity == AB_PARITY_UNKN? "u" : - parity == AB_PARITY_UNDF? "?" : "" ); - } - /* implicit isotopic H */ - if ( NUM_ISO_H(at,j) ) { - for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) { - if ( val = at[j].num_iso_H[k] ) { - len += sprintf( szCurAtom + len, "%s%c", len == len0? ".":"", szIsoH[k] ); - if ( val > 1 ) { - len += sprintf(szCurAtom + len, "%d", val); - } - } - } - } - } - if ( len + cur_len < buf_len ) { - memcpy( szBuf + cur_len, szCurAtom, len ); - cur_len += len; - j ++; - } else { - break; - } - szBuf[cur_len] = '\0'; - *i = j; - - } - return cur_len; -} -/******************************************************************************************/ -/* - bpA;bpAbpA... - -b = bond type: -============= -w = undefined stereo, double -s = single -d = double -t = triple -a = aromatic -p = up from the current atom to the neighbor -P = uP from the neighbor to the current atom -v = undefined stereo Either, single from the current atom to the neighbor -V = undefined stereo Either, single from the neighbor to the current atom -n = down from the current atom to the neighbor -N = dowN from the neighbor to the current atom - -p = bond parity: -================ -- = odd -+ = even -u = unknown -? = undefined - = no parity (empty) - - -A = neighbor orig. atom number -=============== -neighbor orig. atom number < number of the current atom -Number of the current atom: 2 until first ";", 3 until 2nd ";", etc. - -*/ - -/************************************************************************************/ -/* output bonds in ascending order of the neighboring atom original numbers */ -int WriteOrigBonds( int num_inp_atoms, inp_ATOM *at, int *i, char *szBuf, int buf_len, STRUCT_DATA *sd) -{ - int j, k, k2, kk, len, cur_len, j2=0, bond_stereo, bond_char, bond_parity, bond_parityNM, num_trans; - char szCurBonds[7*MAXVAL+2]; /* num_neigh*(1 byte bond type + 2 bytes for bond parity up to 4 digits per neighbor number) + at the end one ';' */ - AT_RANK nNeighOrder[MAXVAL]; - int chain_len, pnxt_atom, pinxt2cur, pinxt_sb_parity_ord; - int chain_len2, pnxt_atom2, pinxt2cur2, pinxt_sb_parity_ord2, m1, m2; - int pcur_atom, picur2nxt, picur_sb_parity_ord; - - cur_len = 0; - for ( j = *i; j < num_inp_atoms; ) { - len = 0; - if ( at[j].valence > 1 ) { - for ( k = 0; k < at[j].valence; k ++ ) { - nNeighOrder[k] = k; - } - pn_RankForSort = at[j].neighbor; - num_trans = insertions_sort( nNeighOrder, at[j].valence, sizeof(nNeighOrder[0]), CompRank ); - } else { - num_trans = 0; - nNeighOrder[0] = 0; - } - for ( kk = 0; kk < at[j].valence; kk ++ ) { - k = nNeighOrder[kk]; - j2 = at[j].neighbor[k]; - bond_parity = 0; - bond_parityNM = 0; - if ( j2 < j ) { - bond_stereo = at[j].bond_stereo[k]; - switch( at[j].bond_type[k] ) { - case BOND_TYPE_SINGLE: - switch( bond_stereo ) { - case STEREO_SNGL_UP: - bond_char = 'p'; - break; - case -STEREO_SNGL_UP: - bond_char = 'P'; - break; - case STEREO_SNGL_DOWN: - bond_char = 'n'; - break; - case -STEREO_SNGL_DOWN: - bond_char = 'N'; - break; -#if ( FIX_EITHER_STEREO_IN_AUX_INFO == 1 ) - case STEREO_SNGL_EITHER: - bond_char = 'v'; - break; - case -STEREO_SNGL_EITHER: - bond_char = 'V'; - break; -#else - case STEREO_SNGL_EITHER: - case -STEREO_SNGL_EITHER: - bond_char = 'v'; - break; -#endif - default: - bond_char = 's'; - break; - } - break; - case BOND_TYPE_DOUBLE: - switch( bond_stereo ) { - case STEREO_DBLE_EITHER: - case -STEREO_DBLE_EITHER: - bond_char = 'w'; - break; - default: - bond_char = 'd'; - break; - } - break; - case BOND_TYPE_TRIPLE: - bond_char = 't'; - break; - case BOND_TYPE_ALTERN: - bond_char = 'a'; - break; - default: - bond_char = 's'; - break; - } - /* check for allene/cumulene */ - k2 = is_in_the_list( at[j2].neighbor, (AT_NUMB)j, at[j2].valence ) - at[j2].neighbor; - chain_len = chain_len2 = 0; - if ( at[j].sb_parity[0] ) { - for ( m1 = 0; m1 < MAX_NUM_STEREO_BONDS && at[j].sb_parity[m1]; m1 ++ ) { - if ( k == at[j].sb_ord[m1] ) { - chain_len = get_opposite_sb_atom( at, j, k, - &pnxt_atom, &pinxt2cur, &pinxt_sb_parity_ord ); - break; - } - } - } - if ( at[j2].sb_parity[0] ) { - for ( m2 = 0; m2 < MAX_NUM_STEREO_BONDS && at[j2].sb_parity[m2]; m2 ++ ) { - if ( k2 == at[j2].sb_ord[m2] ) { - chain_len2 = get_opposite_sb_atom( at, j2, k2, - &pnxt_atom2, &pinxt2cur2, &pinxt_sb_parity_ord2 ); - break; - } - } - } - if ( chain_len == 1 && chain_len2 == 1 || /* regular stereobond */ - chain_len > 1 && j > pnxt_atom ) { /* j is a cumulene endpoint */ - int m; - pcur_atom = j; /* pcur_atom > pnxt_atom */ - picur2nxt = k; - picur_sb_parity_ord = -1; - for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[pcur_atom].sb_parity[m]; m ++ ) { - if ( at[pcur_atom].sb_ord[m] == k ) { - picur_sb_parity_ord = m; - break; - } - } - chain_len2 = 0; - } else - if ( chain_len2 > 1 && j2 > pnxt_atom2 ) { /* j2 is a cumulene endpoint */ - int m; - pcur_atom = j2; - picur2nxt = k2; - pnxt_atom = pnxt_atom2; - pinxt2cur = pinxt2cur2; - pinxt_sb_parity_ord = pinxt_sb_parity_ord2; - picur_sb_parity_ord = -1; - for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[pcur_atom].sb_parity[m]; m ++ ) { - if ( at[pcur_atom].sb_ord[m] == k2 ) - picur_sb_parity_ord = m; - } - chain_len = chain_len2; - chain_len2 = 0; - } else { - chain_len = chain_len2 = 0; - } - - /*len += sprintf( szCurBonds + len, "%c%d", bond_char, val+1);*/ - if ( chain_len ) { - /* both atoms belong to a stereo bond */ - int kc; - int p1, p2, p1NM, p2NM, neigh, neigh1, neigh2, bHasMetal, bWellDef; - int bNeighSwitched1, bNeighSwitched2; - - p1 = SB_PARITY_1(at[pcur_atom].sb_parity[picur_sb_parity_ord]); - p1NM = SB_PARITY_2(at[pcur_atom].sb_parity[picur_sb_parity_ord]); - p2 = SB_PARITY_1(at[pnxt_atom].sb_parity[pinxt_sb_parity_ord]); - p2NM = SB_PARITY_2(at[pnxt_atom].sb_parity[pinxt_sb_parity_ord]); - - bWellDef = ATOM_PARITY_WELL_DEF(p1) && ATOM_PARITY_WELL_DEF(p2); - bHasMetal = ATOM_PARITY_WELL_DEF(p1NM) && ATOM_PARITY_WELL_DEF(p2NM); - - bNeighSwitched1 = bNeighSwitched2 = 0; - - if ( bWellDef || bHasMetal ) { - - neigh1 = num_inp_atoms; - for ( kc = 0; kc < at[pcur_atom].valence; kc ++ ) { - if ( kc == picur2nxt ) - continue; - neigh = at[pcur_atom].neighbor[kc]; - if ( bHasMetal && is_el_a_metal( at[neigh].el_number ) ) - continue; - if ( neigh < neigh1 ) - neigh1 = neigh; - } - if ( neigh1 < num_inp_atoms ) { - bNeighSwitched1 = (neigh1 != at[pcur_atom].neighbor[(int)at[pcur_atom].sn_ord[picur_sb_parity_ord]]); - } else { - AddMOLfileError(sd->pStrErrStruct, "Cannot find 0D stereobond neighbor"); - /* - sd->nStructReadError = 99; - sd->nErrorType = _IS_ERROR; - */ - - } - - neigh2 = num_inp_atoms; - for ( kc = 0; kc < at[pnxt_atom].valence; kc ++ ) { - if ( kc == pinxt2cur ) - continue; - neigh = at[pnxt_atom].neighbor[kc]; - if ( bHasMetal && is_el_a_metal( at[neigh].el_number ) ) - continue; - if ( neigh < neigh2 ) - neigh2 = neigh; - } - if ( neigh2 < num_inp_atoms ) { - bNeighSwitched2 = (neigh2 != at[pnxt_atom].neighbor[(int)at[pnxt_atom].sn_ord[pinxt_sb_parity_ord]]); - } else { - AddMOLfileError(sd->pStrErrStruct, "Cannot find 0D stereobond neighbor"); - /* - sd->nStructReadError = 99; - sd->nErrorType = _IS_ERROR; - */ - - } - - if ( neigh1 < num_inp_atoms && neigh2 < num_inp_atoms ) { - if ( ATOM_PARITY_WELL_DEF(p1) && ATOM_PARITY_WELL_DEF(p2) ) { - bond_parity = 2 - (p1 + p2 + bNeighSwitched1 + bNeighSwitched2) % 2; - } else { - bond_parity = inchi_min( p1, p2 ); - } - - if ( bHasMetal ) { - bond_parityNM = 2 - (p1NM + p2NM + bNeighSwitched1 + bNeighSwitched2) % 2; - } else - if ( p1NM && p2NM ) { - bond_parityNM = inchi_min( p1NM, p2NM ); - } - } - } else { - if ( p1 && p2 ) { - bond_parity = inchi_min( p1, p2 ); - } - if ( p1NM && p2NM ) { - bond_parityNM = inchi_min( p1NM, p2NM ); - } - if ( bond_parityNM && !bond_parity ) { - bond_parity = AB_PARITY_UNDF; - } - } - } - len += sprintf( szCurBonds + len, "%c%s%s%d", - - bond_char, - - (bond_parity == AB_PARITY_ODD)? "-" : - (bond_parity == AB_PARITY_EVEN)? "+" : - (bond_parity == AB_PARITY_UNKN)? "u" : - (bond_parity == AB_PARITY_UNDF)? "?" : "", - - (bond_parityNM == AB_PARITY_ODD)? "-" : - (bond_parityNM == AB_PARITY_EVEN)? "+" : - (bond_parityNM == AB_PARITY_UNKN)? "u" : - (bond_parityNM == AB_PARITY_UNDF)? "?" : "", - - j2+1); - } - } - if ( len + cur_len + 2 < buf_len ) { - memcpy( szBuf + cur_len, szCurBonds, len ); - cur_len += len; - szBuf[ cur_len ++ ] = ';'; - j ++; - } else { - break; - } - } - szBuf[cur_len] = '\0'; - *i = num_inp_atoms>0? j : 0; - return cur_len; -} - - -#define ORIG_STR_BUFLEN (7*MAXVAL+2) /* > 7*MAXVAL+2 = 142 */ -/******************************************************************************************/ -int FillOutOrigStruct( ORIG_ATOM_DATA *orig_inp_data, ORIG_STRUCT *pOrigStruct, STRUCT_DATA *sd ) -{ - char szBuf[ORIG_STR_BUFLEN]; - int i, len, len_coord, len_atoms, len_bonds; - /* coordinates */ - len_coord = i = 0; - - if (orig_inp_data->szCoord) { - - while ( len = WriteOrigCoord( orig_inp_data->num_inp_atoms, - orig_inp_data->szCoord, &i, szBuf, sizeof(szBuf) )) { - len_coord += len; - } - pOrigStruct->szCoord = (char*) inchi_malloc( (len_coord + 1)*sizeof(pOrigStruct->szCoord[0]) ); - i = 0; - if ( pOrigStruct->szCoord && - len_coord == WriteOrigCoord( orig_inp_data->num_inp_atoms, - orig_inp_data->szCoord, &i, pOrigStruct->szCoord, len_coord+1 ) && - i == orig_inp_data->num_inp_atoms ) { - /* success */ - if ( orig_inp_data->szCoord ) { - inchi_free( orig_inp_data->szCoord ); - orig_inp_data->szCoord = NULL; - } - } else { - return -1; - } - - } - - /* atoms */ - len_atoms = i = 0; - while ( len = WriteOrigAtoms( orig_inp_data->num_inp_atoms, - orig_inp_data->at, &i, szBuf, sizeof(szBuf), sd)) { - len_atoms += len; - if ( !orig_inp_data->num_inp_atoms ) - break; - } - pOrigStruct->szAtoms = (char*) inchi_malloc( (len_atoms + 1)*sizeof(pOrigStruct->szAtoms[0]) ); - i = 0; - if ( pOrigStruct->szAtoms && - len_atoms == WriteOrigAtoms( orig_inp_data->num_inp_atoms, - orig_inp_data->at, &i, pOrigStruct->szAtoms, len_atoms+1, sd ) && - i == orig_inp_data->num_inp_atoms ) { - ; /* success */ - } else { - return -1; - } - /* bonds */ - len_bonds = 0; - i = 1; - while ( len = WriteOrigBonds( orig_inp_data->num_inp_atoms, - orig_inp_data->at, &i, szBuf, sizeof(szBuf), NULL)) { - len_bonds += len; - if ( !orig_inp_data->num_inp_atoms ) - break; - } - pOrigStruct->szBonds = (char*) inchi_malloc( (len_bonds + 2)*sizeof(pOrigStruct->szBonds[0]) ); - i = 1; - if ( pOrigStruct->szBonds && - len_bonds == WriteOrigBonds( orig_inp_data->num_inp_atoms, - orig_inp_data->at, &i, pOrigStruct->szBonds, len_bonds+2, sd ) && - i == orig_inp_data->num_inp_atoms ) { - ; /* success */ - } else { - return -1; - } - pOrigStruct->num_atoms = orig_inp_data->num_inp_atoms; - return 0; -} -/*****************************************************************/ -void FreeOrigStruct( ORIG_STRUCT *pOrigStruct) -{ - if ( pOrigStruct ) { - if ( pOrigStruct->szAtoms ) - inchi_free( pOrigStruct->szAtoms ); - if ( pOrigStruct->szBonds ) - inchi_free( pOrigStruct->szBonds ); - if ( pOrigStruct->szCoord ) - inchi_free( pOrigStruct->szCoord ); - memset( pOrigStruct, 0, sizeof(*pOrigStruct) ); - - } -} - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get the two letters encoding the saved InChI creation options. - -The first one encodes RecMet/FixedH/SUU/SLUUD options. -Each of options is a binary switch {ON,OFF}, so it totals to 2*2*2*2=16 values -which are encoded by capital letters ‘A’ through ‘P’. - -The second character encodes experimental (InChI 1 extension) options KET and 15T. -Each of these options is a binary switch ON/OFF, so there are 2*2=4 combinations, -currently encoded by ‘A’ through ‘D’. -Note that anything but 'A' here would indicate "extended" InChI 1 Also, there is a -reservation for future needs: the 2nd memo char may accommodate two more ON/OFF -binary options (at 26-base encoding). -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void GetSaveOptLetters(unsigned char save_opt_bits, char* let1, char* let2) -{ -const char a2p[]="ABCDEFGHIJKLMNOP"; - /* SaveOptBits layout: {unused|unused|Ket|15T|RecMet|FixedH|SUU|SLUUD} */ - *let1 = a2p [ (size_t) ( save_opt_bits & 0x0f ) ]; - *let2 = a2p [ (size_t) ( (save_opt_bits & 0x30) >> 4 ) ]; -} diff --git a/INCHI-1-SRC/INCHI/common/ichiprt2.c b/INCHI-1-SRC/INCHI/common/ichiprt2.c deleted file mode 100644 index f880fb4..0000000 --- a/INCHI-1-SRC/INCHI/common/ichiprt2.c +++ /dev/null @@ -1,1613 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include - -#include "mode.h" - -#include "inpdef.h" -#include "ichi.h" -#include "strutil.h" -#include "util.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "ichicant.h" -#include "ichicano.h" -#include "ichicomn.h" - -#include "ichicomp.h" -#include "ichimain.h" -#include "ichimake.h" - -/*****************************************************************************************/ -int Eql_INChI_Stereo( INChI_Stereo *s1, int eql1, INChI_Stereo *s2, int eql2, int bRelRac ) -{ - int inv1=0, inv2=0, len; - - if ( !s1 ) { - return 0; - } -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) -#else - bRelRac = 0; -#endif - - if( EQL_SP2 == eql1 ) { - if ( (len=s1->nNumberOfStereoBonds) > 0 && s1->b_parity && s1->nBondAtom1 && s1->nBondAtom2 ) { - if ( !s2 ) { - if ( EQL_EXISTS == eql2 ) { - /* find whether double bond stereo exists*/ - return 1; - } - return 0; - } - if ( EQL_SP2 == eql2 && - len == s2->nNumberOfStereoBonds && s2->b_parity && s2->nBondAtom1 && s2->nBondAtom2 && - !memcmp( s1->nBondAtom1, s2->nBondAtom1, len * sizeof(s1->nBondAtom1[0])) && - !memcmp( s1->nBondAtom2, s2->nBondAtom2, len * sizeof(s1->nBondAtom2[0])) && - !memcmp( s1->b_parity, s2->b_parity, len * sizeof(s1->b_parity[0])) ) { - return 1; - } - } - return 0; - } else - if ( (eql1 == EQL_SP3 || (inv1 = (eql1 == EQL_SP3_INV))) && - (len=s1->nNumberOfStereoCenters) > (bRelRac? 1 : 0) ) { - - S_CHAR *t_parity1, *t_parity2; - AT_NUMB *nNumber1, *nNumber2; - if ( inv1 ) { - if ( s1->nCompInv2Abs ) { - t_parity1 = s1->t_parityInv; - nNumber1 = s1->nNumberInv; - } else { - return 0; - } - } else { - t_parity1 = s1->t_parity; - nNumber1 = s1->nNumber; - } - - if ( t_parity1 && nNumber1 ) { - if ( !s2 ) { - if ( EQL_EXISTS == eql2 && (!inv1 || s1->nCompInv2Abs) ) { - /* the 1st sp3 (inverted if requested) stereo exists*/ - return 1; - } - return 0; /* both sp3 do not exist */ - } - if( (eql2 == EQL_SP3 || (inv2 = (eql2 == EQL_SP3_INV))) && - len == s2->nNumberOfStereoCenters ) { - if ( inv2 ) { - if ( s2->nCompInv2Abs && s1->nCompInv2Abs ) { - t_parity2 = s2->t_parityInv; - nNumber2 = s2->nNumberInv; - } else { - /* if one sp3 is inverted then another should have non-trivial inverted stereo */ - return 0; - } - } else { - if ( inv1 && !s2->nCompInv2Abs ) { - /* if one sp3 is inverted then another should have non-trivial inverted stereo */ - return 0; - } - t_parity2 = s2->t_parity; - nNumber2 = s2->nNumber; - } - if ( t_parity2 && nNumber2 ) { - if ( inv1 ^ inv2 ) { - int i, num_inv; - for ( i = 0, num_inv = 0; i < len; i ++ ) { - if ( nNumber1[i] != nNumber2[i] ) - break; - if ( ATOM_PARITY_WELL_DEF(t_parity1[i]) && - ATOM_PARITY_WELL_DEF(t_parity2[i]) ) { - if ( 3 == t_parity1[i] + t_parity2[i] ) { - num_inv ++; - } else { - break; - } - } else - if ( t_parity1[i] != t_parity2[i] ) { - break; - } - } - return (len == i && num_inv > 0); - } else { - return !memcmp( t_parity1, t_parity2, len*sizeof(t_parity1[0])) && - !memcmp( nNumber1, nNumber2, len*sizeof(nNumber1[0])); - } - } - } - } - } - return 0; -} -/**********************************************************************************************/ -int Eql_INChI_Isotopic( INChI *i1, INChI *i2 ) -{ - int eq = i1 && i2 && !i1->bDeleted && !i2->bDeleted && - ( i1->nNumberOfIsotopicAtoms > 0 || i1->nNumberOfIsotopicTGroups > 0 ) && - i1->nNumberOfIsotopicAtoms == i2->nNumberOfIsotopicAtoms && - i1->nNumberOfIsotopicTGroups == i2->nNumberOfIsotopicTGroups && - ( !i1->nNumberOfIsotopicAtoms || - i1->IsotopicAtom && i2->IsotopicAtom && - !memcmp( i1->IsotopicAtom, i2->IsotopicAtom, - i1->nNumberOfIsotopicAtoms * sizeof(i1->IsotopicAtom[0]) ) ) && - ( !i1->nNumberOfIsotopicTGroups || - i1->IsotopicTGroup && i2->IsotopicTGroup && - !memcmp( i1->IsotopicTGroup, i2->IsotopicTGroup, - i1->nNumberOfIsotopicTGroups * sizeof(i1->IsotopicAtom[0]) ) ); - return eq; - -} -/**********************************************************************************************/ -int Eql_INChI_Aux_Equ( INChI_Aux *a1, int eql1, INChI_Aux *a2, int eql2 ) -{ - int t1=0, t2=0, len; - AT_NUMB *n1=NULL, *n2=NULL; - if ( !a1 || !a2 ) { - return 0; - } - t1 = (eql1 & EQL_EQU_TG); - t2 = (eql2 & EQL_EQU_TG); - if ( t1 && t2 ) { - if ( (len = a1->nNumberOfTGroups) > 0 && len == a2->nNumberOfTGroups && !a1->bDeleted && !a2->bDeleted ) { - if (eql1 & EQL_EQU_ISO) { - if ( a1->bIsIsotopic ) { - n1 = a1->nConstitEquIsotopicTGroupNumbers; - } - } else { - n1 = a1->nConstitEquTGroupNumbers; - } - if (eql2 & EQL_EQU_ISO) { - if ( a2->bIsIsotopic ) { - n2 = a2->nConstitEquIsotopicTGroupNumbers; - } - } else { - n2 = a2->nConstitEquTGroupNumbers; - } - } - } else - if ( !t1 && !t2 ) { - if ( (len = a1->nNumberOfAtoms) > 0 && len == a2->nNumberOfAtoms && !a1->bDeleted && !a2->bDeleted ) { - if (eql1 & EQL_EQU_ISO) { - if ( a1->bIsIsotopic ) { - n1 = a1->nConstitEquIsotopicNumbers; - } - } else { - n1 = a1->nConstitEquNumbers; - } - if (eql2 & EQL_EQU_ISO) { - if ( a2->bIsIsotopic ) { - n2 = a2->nConstitEquIsotopicNumbers; - } - } else { - n2 = a2->nConstitEquNumbers; - } - } - } - if ( n1 && n2 && !memcmp(n1, n2, len*sizeof(n1[0])) && bHasEquString( n1, len) ) { - return 1; - } - return 0; -} -/**********************************************************************************************/ -int Eql_INChI_Aux_Num( INChI_Aux *a1, int eql1, INChI_Aux *a2, int eql2 ) -{ - int len; - AT_NUMB *n1=NULL, *n2=NULL; - if ( !a1 || !a2 ) { - return 0; - } - if ( (len = a1->nNumberOfAtoms) <= 0 || len != a2->nNumberOfAtoms || a1->bDeleted || a2->bDeleted ) { - return 0; - } - if ( (eql1 & EQL_NUM_ISO) && !a1->bIsIsotopic || - (eql2 & EQL_NUM_ISO) && !a2->bIsIsotopic ) { - return 0; - } - - switch ( eql1 ) { - case EQL_NUM: - n1 = a1->nOrigAtNosInCanonOrd; - break; - case EQL_NUM_ISO: - n1 = a1->nIsotopicOrigAtNosInCanonOrd; - break; - case EQL_NUM_INV: - n1 = a1->nOrigAtNosInCanonOrdInv; - break; - case ( EQL_NUM_INV | EQL_NUM_ISO ): - n1 = a1->nIsotopicOrigAtNosInCanonOrdInv; - break; - default: - return 0; - } - - switch ( eql2 ) { - case EQL_NUM: - n2 = a2->nOrigAtNosInCanonOrd; - break; - case EQL_NUM_ISO: - n2 = a2->nIsotopicOrigAtNosInCanonOrd; - break; - case EQL_NUM_INV: - n2 = a2->nOrigAtNosInCanonOrdInv; - break; - case ( EQL_NUM_INV | EQL_NUM_ISO ): - n2 = a2->nIsotopicOrigAtNosInCanonOrdInv; - break; - default: - return 0; - } - - if ( n1 && n2 && !memcmp( n1, n2, len*sizeof(n1[0])) ) { - return 1; - } - return 0; -} -/**********************************************************************************************/ -int bHasOrigInfo( ORIG_INFO *OrigInfo, int num_atoms ) -{ - int i, bFound = 0; - if ( OrigInfo && num_atoms > 0 ) { - for ( i = 0; !bFound && i < num_atoms; i ++ ) { - bFound |= (0 != OrigInfo[i].cCharge) || - (0 != OrigInfo[i].cRadical) || - (0 != OrigInfo[i].cUnusualValence); - - } - } - return bFound; -} -/**********************************************************************************************/ -int EqlOrigInfo( INChI_Aux *a1, INChI_Aux *a2 ) -{ - int ret = a1 && a2 && a1->nNumberOfAtoms == a2->nNumberOfAtoms && - bHasOrigInfo( a1->OrigInfo, a1->nNumberOfAtoms ) && a2->OrigInfo && - !memcmp( a1->OrigInfo, a2->OrigInfo, a1->nNumberOfAtoms * sizeof(a1->OrigInfo[0]) ); - return ret; - -} - -/**********************************************************************************************/ -int bHasEquString( AT_NUMB *LinearCT, int nLenCT ) -{ - /* produce output string; */ - int i, k; - if ( !LinearCT ) - return 0; - for ( k = 0; k < nLenCT; k ++ ) { - /* find the first equivalence number */ - if ( k != (int)LinearCT[k] - 1 ) - continue; - for ( i = k; i < nLenCT; i ++ ) { - if ( k != (int)LinearCT[i]-1 ) - continue; - if ( k < i ) { - return 1; - } - } - } - return 0; -} -/********************************************************************************************/ -int MakeMult( int mult, const char *szTailingDelim, char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) -{ - char szValue[16]; - int len = 0, len_delim; - if ( mult == 1 || *bOverflow ) - return 0; - if ( nCtMode & CT_MODE_ABC_NUMBERS ) { - len += MakeAbcNumber( szValue, (int)sizeof(szValue), NULL, mult ); - } else { - len += MakeDecNumber( szValue, (int)sizeof(szValue), NULL, mult ); - } - len_delim = strlen(szTailingDelim); - if ( len + len_delim < (int)sizeof(szValue) ) { - strcpy( szValue+len, szTailingDelim ); - len += len_delim; - if ( len < nLen_szLinearCT ) { - strcpy( szLinearCT, szValue ); - return len; - } - } - *bOverflow |= 1; - return 0; -} -/********************************************************************************************/ -int MakeDelim( const char *szTailingDelim, char *szLinearCT, int nLen_szLinearCT, int *bOverflow) -{ - int len_delim; - if ( !szTailingDelim || !*szTailingDelim || *bOverflow ) - return 0; - len_delim = strlen(szTailingDelim); - if ( len_delim < nLen_szLinearCT ) { - strcpy( szLinearCT, szTailingDelim ); - return len_delim; - } - *bOverflow |= 1; - return 0; -} -/********************************************************************************************/ -int MakeEqStr( const char *szTailingDelim, int mult, char *szLinearCT, int nLen_szLinearCT, int *bOverflow) -{ - int len = 0, len_delim; - char szValue[16]; - if ( !szTailingDelim || !*szTailingDelim || *bOverflow ) - return 0; - if ( mult != 1 ) { - len = MakeDecNumber( szValue, (int)sizeof(szValue), NULL, mult ); - } - len_delim = strlen(szTailingDelim); - if ( len_delim + len < nLen_szLinearCT ) { - if ( len > 0 ) { - memcpy( szLinearCT, szValue, len ); - } - strcpy( szLinearCT+len, szTailingDelim ); - return len + len_delim; - } - *bOverflow |= 1; - return 0; -} -/********************************************************************************************** - * nCtMode = 0: full - * 1: censored CT (no orphans) - * 2: compressed CT (Abs numbers) - **********************************************************************************************/ -int MakeCtStringNew( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, - S_CHAR *nNum_H, int num_atoms, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) -{ - /* produce output string; */ - int nLen = 0, len, i, bOvfl = *bOverflow; - char szValue[16]; - int nValue, nDelim, num_H; - AT_NUMB *nDfsOrderCT = NULL; - int bNoNum_H = (NULL == nNum_H); - int nNumRingClosures; - int bAbcNumbers = (0 != ( nCtMode & CT_MODE_ABC_NUMBERS )); - int bPredecessors = (0 != ( nCtMode & CT_MODE_PREDECESSORS )); - int bCountRingClosures = bAbcNumbers && bPredecessors && (nCtMode & CT_MODE_ABC_NUM_CLOSURES); - if ( nLenCT <= 1 ) { - return 0; /* no atoms or a single atom: no connection table */ - } - /* make array containing connection string data */ - if ( !(nDfsOrderCT = GetDfsOrder4CT( LinearCT, nLenCT, nNum_H, num_atoms, nCtMode ) ) ) { - (*bOverflow) ++; - return 0; - } - - /* add connection table string */ - if ( !bOvfl && bAddDelim ) { - if ( nLen_szLinearCT > 1 ) { - strcpy( szLinearCT, "," ); - nLen ++; - } else { - bOvfl = 1; - } - } - - if ( !bOvfl ) { - nNumRingClosures = 0; - for ( i = 0; nDfsOrderCT[i] && nLen < nLen_szLinearCT; i += 3 ) { - nValue = (nDfsOrderCT[i] > MAX_ATOMS)? 0 : nDfsOrderCT[i]; - num_H = nDfsOrderCT[i+1]? nDfsOrderCT[i+1]-16:0; - nDelim = nDfsOrderCT[i+2]; - len = 0; - /* delimiter */ - if ( bPredecessors ) { - if ( bCountRingClosures ) { - if ( nDelim == '-' && i > 3 && bNoNum_H ) { - if ( !nNumRingClosures ) { - int j; - for ( j = i; nDfsOrderCT[j] && '-' == nDfsOrderCT[j+2]; j += 3 ) { - nNumRingClosures ++; - } - if ( nNumRingClosures ) { - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, nNumRingClosures ); - } - nNumRingClosures --; - } else { - nNumRingClosures --; - } - } else { - nNumRingClosures = 0; - } - } else - if ( nDelim && !( bAbcNumbers && nDelim == ',' ) ) { - if ( nNum_H || i > 3 ) { - szValue[len ++] = nDelim; - } - } - } else { - if ( nDelim && !( bAbcNumbers && nDelim == '-' ) ) { - szValue[len ++] = nDelim; - } - } - if ( bAbcNumbers ) { - if ( nValue || i ) { /* the 1st value may be zero in case of presdecessor list */ - len += MakeAbcNumber( szValue+len, (int)sizeof(szValue)-len, NULL, nValue ); - } - if ( num_H ) { - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, num_H ); - } - } else { - if ( nValue || i ) { /* the 1st value may be zero in case of presdecessor list */ - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, nValue ); - } - if ( num_H ) { - szValue[len] = 'H'; - len ++; - if ( num_H > 1 ) { - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, num_H ); - } - } - } - if ( 0 <= len && nLen+len < nLen_szLinearCT ) { - if ( len ) { - strcpy( szLinearCT+nLen, szValue ); - nLen += len; - } - } else { - bOvfl = 1; - break; - } - } - } - *bOverflow |= bOvfl; - if ( nDfsOrderCT ) - inchi_free( nDfsOrderCT ); - return nLen; -} -/********************************************************************************************** - * nCtMode = 0: full - * 1: censored CT (no orphans) - * 2: compressed CT (Abs numbers) - **********************************************************************************************/ -int MakeCtStringOld( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) -{ - /* produce output string; */ - int nLen = 0, len, i, bLessThanPrev, bOvfl = *bOverflow; - AT_NUMB nMax = 0; - char szValue[16]; - int nValue, bNext = 0; - /* add connection table string */ - if ( !( nCtMode & CT_MODE_ABC_NUMBERS ) && !bOvfl && bAddDelim ) { - if ( nLen_szLinearCT > 1 ) { - strcpy( szLinearCT, "," ); - nLen ++; - } else { - bOvfl = 1; - } - } - if ( !bOvfl ) { - for ( i = 0; i < nLenCT && nLen < nLen_szLinearCT; i ++ ) { - bLessThanPrev = 0; - if ( !(nCtMode & CT_MODE_NO_ORPHANS) || ((bLessThanPrev=LinearCT[i] < nMax) || - i+1 < nLenCT && LinearCT[i+1] < (nMax=LinearCT[i])) ) { - nValue = LinearCT[i]; - if ( nCtMode & CT_MODE_ABC_NUMBERS ) { - len = MakeAbcNumber( szValue, (int)sizeof(szValue), (!bNext && bAddDelim)? ITEM_DELIMETER : NULL, nValue ); - } else - if ( nCtMode & CT_MODE_NO_ORPHANS ) { /* censored CT */ - /* output '-' as a delimiter to show a bonding for decimal output of the connection table */ - len = MakeDecNumber( szValue, (int)sizeof(szValue), bLessThanPrev? "-":ITEM_DELIMETER, nValue ); - } else { - len = MakeDecNumber( szValue, (int)sizeof(szValue), i? ITEM_DELIMETER:NULL, nValue ); - } - if ( 0 <= len && nLen+len < nLen_szLinearCT ) { - if ( len ) { - strcpy( szLinearCT+nLen, szValue ); - nLen += len; - bNext ++; - } - } else { - bOvfl = 1; - break; - } - } - } - } - *bOverflow |= bOvfl; - return nLen; -} -/********************************************************************************************** - * nCtMode = 0: decimal - * 2: compressed CT (Abs numbers) - **********************************************************************************************/ -int MakeHString( int bAddDelim, S_CHAR *LinearCT, int nLenCT, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow ) -{ -#define INIT_MIN_NUM_H (-4) -#define INIT_MAX_NUM_H 16 -#define INIT_LEN_NUM_H (INIT_MAX_NUM_H - INIT_MIN_NUM_H + 1) - - /* produce output string; */ - int nLen = 0, len, i, iFirst, nVal, bOvfl = *bOverflow; - char szValue[32]; - const char *pH; - int bNext = 0; - /* add connection table string */ - if ( !( nCtMode & CT_MODE_ABC_NUMBERS ) && !bOvfl && bAddDelim ) { - if ( nLen_szLinearCT > 1 ) { - strcpy( szLinearCT, "," ); - nLen ++; - } else { - bOvfl = 1; - } - } - if ( !bOvfl && 0 < nLenCT && LinearCT ) { - if ( nCtMode & CT_MODE_EQL_H_TOGETHER ) { - int curMinH = INIT_MIN_NUM_H; - int curMaxH = INIT_MAX_NUM_H; - int curLenH = INIT_LEN_NUM_H; - int nInitNumH[INIT_LEN_NUM_H]; - int *nNumH = nInitNumH; - int numAt, curNumH; - int j, bOutOfRange, tot_num_no_H; - /* count atoms H */ - do { - bOutOfRange = 0; - tot_num_no_H = 0; /* number of atoms that have no H */ - memset( nNumH, 0, curLenH*sizeof(nNumH[0]) ); - for ( i = 0; i < nLenCT; i ++ ) { - curNumH = LinearCT[i]; - if ( curNumH < curMinH ) { - curMinH = curNumH; - bOutOfRange ++; - } else - if ( curNumH > curMaxH ) { - curMaxH = curNumH; - bOutOfRange ++; - } else - if ( !bOutOfRange ) { - nNumH[curNumH-curMinH] ++; - } - tot_num_no_H += !curNumH; - } - if ( tot_num_no_H == nLenCT ) { - return nLen; /* empty string */ - } - if ( bOutOfRange ) { - /* for debug only */ - if ( nNumH != nInitNumH ) { - *bOverflow |= 1; - inchi_free( nNumH ); - return nLen; - } - /* end debug */ - curLenH = curMaxH - curMinH + 1; - nNumH = (int*) inchi_malloc( curLenH * sizeof(nNumH[0]) ); - if ( !nNumH ) { - *bOverflow |= 1; - return nLen; - } - } - } while ( bOutOfRange ); /* the loop may be executed 1 or 2 times only */ - - for ( curNumH = curMinH; curNumH <= curMaxH; curNumH ++ ) { - numAt = nNumH[curNumH-curMinH]; /* number of atoms that have curNumH atoms H */ - if ( !numAt || !curNumH ) { - continue; /* no atom has this number of H or number of H = 0 */ - } - j = 0; - while ( j < nLenCT && numAt ) { - if ( curNumH == LinearCT[j] ) { - iFirst = ++j; - numAt --; - for ( ; j < nLenCT && curNumH == LinearCT[j] && numAt; j ++ ) { - numAt --; - } - if ( nCtMode & CT_MODE_ABC_NUMBERS ) { - len = MakeAbcNumber( szValue, (int)sizeof(szValue), NULL, iFirst ); - } else { - len = MakeDecNumber( szValue, (int)sizeof(szValue), bNext?ITEM_DELIMETER:NULL, iFirst ); - bNext ++; /* add a delimiter (comma) before all except the first */ - } - if ( iFirst < j ) { - /* output last canonical number */ - if ( nCtMode & CT_MODE_ABC_NUMBERS ) { - len += MakeAbcNumber( szValue+len, (int)sizeof(szValue), NULL, j ); - } else { - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, "-", j ); - } - } - if ( !numAt || ( nCtMode & CT_MODE_ABC_NUMBERS ) ) { - /* add number of H */ - /* output number of H */ - nVal = curNumH; - if ( nCtMode & CT_MODE_ABC_NUMBERS ) { - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, nVal ); - } else { - pH = nVal > 0? "H":"h"; - nVal = abs(nVal); - if ( nVal > 1 ) { - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, pH, nVal ); - } else { - strcpy( szValue+len, pH ); - len ++; - } - } - } - /* add to the output */ - if ( 0 <= len && nLen+len < nLen_szLinearCT ) { - if ( len ) { - strcpy( szLinearCT+nLen, szValue ); - nLen += len; - bNext ++; - } - } else { - bOvfl = 1; - break; - } - } else { - j ++; - } - } - } - if ( nNumH != nInitNumH ) { - inchi_free( nNumH ); - } - } else { - iFirst = 0; - for ( i = iFirst+1; i <= nLenCT && nLen < nLen_szLinearCT; i ++ ) { - if ( i < nLenCT && LinearCT[i] == LinearCT[iFirst] ) { - continue; - } - /* output identical values located at i = iFirst..i-1 */ - if ( LinearCT[iFirst] ) { /* output only non-zero values */ - /* first canonical number */ - nVal = LinearCT[iFirst]; - iFirst ++; - if ( nCtMode & CT_MODE_ABC_NUMBERS ) { - len = MakeAbcNumber( szValue, (int)sizeof(szValue), NULL, iFirst ); - } else { - len = MakeDecNumber( szValue, (int)sizeof(szValue), bNext?ITEM_DELIMETER:NULL, iFirst ); - } - if ( iFirst < i ) { - /* output last canonical number */ - if ( nCtMode & CT_MODE_ABC_NUMBERS ) { - len += MakeAbcNumber( szValue+len, (int)sizeof(szValue), NULL, i ); - } else { - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, "-", i ); - } - } - /* output number of H */ - if ( nCtMode & CT_MODE_ABC_NUMBERS ) { - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, nVal ); - } else { - pH = nVal > 0? "H":"h"; - nVal = abs(nVal); - if ( nVal > 1 ) { - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, pH, nVal ); - } else { - strcpy( szValue+len, pH ); - len ++; - } - } - if ( 0 <= len && nLen+len < nLen_szLinearCT ) { - if ( len ) { - strcpy( szLinearCT+nLen, szValue ); - nLen += len; - bNext ++; - } - } else { - bOvfl = 1; - break; - } - } - iFirst = i; - } - } - } - - *bOverflow |= bOvfl; - return nLen; - -#undef INIT_MIN_NUM_H -#undef INIT_MAX_NUM_H -#undef INIT_LEN_NUM_H -} -/********************************************************************************************** - * nCtMode = 0: full - * 1: censored CT (no orphans, that CT should have only atoms with neighbors) - * 2: compressed CT (Abc numbers) - **********************************************************************************************/ -int MakeCtString( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, - S_CHAR *nNum_H, int num_atoms, /* both parameters are not used here */ - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) -{ - - if ( !nNum_H || !(nCtMode & CT_MODE_NO_ORPHANS) ) { - return MakeCtStringOld( LinearCT, nLenCT, bAddDelim, - szLinearCT, nLen_szLinearCT, nCtMode, bOverflow); - } else { - return MakeCtStringNew( LinearCT, nLenCT, bAddDelim, - nNum_H, num_atoms, - szLinearCT, nLen_szLinearCT, nCtMode, bOverflow); - } -} - - - -/********************************************************************************************** - * nCtMode = 0: full: decimal-only, with parentheses around t-groups - * 2: compressed CT: do not add comma before the output string if bAddDelim != 0 - * do not add parentheses around t-groups - * atom canon numbers an Abc - * LinearCT format: - * N = number of tautomeric groups - * n = number of endpoints + 1 in a tautomeric group #1 - * next INCHI_T_NUM_MOVABLE lines (any after the first non-zero): - * h = number of hydrogen atoms in the tautomeric group - * m = number of negative charges - * ... (the rest of the INCHI_T_NUM_MOVABLE has not been established, ignore them) - * c(1) = canonical number of the first atom in the t-group - * ... - * c(n-1) = canonical number of the last atom in the t-group - * - **********************************************************************************************/ - -int MakeTautString( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) -{ - /* produce output string; */ - int nLen = 0, len, i, bOvfl = *bOverflow; - char szValue[16]; - const char *p; - int nValue, nGroupLen, iGroupOutputCount, bCompressed; - /* make tautomer string */ - if ( !nLenCT || !LinearCT || !*LinearCT ) { - return nLen; - } - bCompressed = ( nCtMode & CT_MODE_ABC_NUMBERS ); - if ( !bCompressed && !bOvfl && bAddDelim ) { - if ( nLen_szLinearCT > 1+LEN_EXTRA_SPACE ) { - strcpy( szLinearCT, COMMA_EXTRA_SPACE); - nLen += 1+LEN_EXTRA_SPACE; - } else { - bOvfl = 1; - } - } - LinearCT ++; /* bypass number of tautomeric groups */ - nLenCT --; - - if ( !bOvfl ) { - for ( i = nGroupLen = iGroupOutputCount = 0; i < nLenCT && nLen < nLen_szLinearCT; i ++ ) { - nValue = (int)LinearCT[i]; - if ( nGroupLen == iGroupOutputCount ) { - nGroupLen = nValue; - iGroupOutputCount = 0; - /* group delimiter (uncompressed) */ - if ( !bCompressed ) { - if ( !i ) { - strcpy( szValue, "(" ); - len = 1; - } else { - strcpy( szValue, ")(" ); - len = 2; - } - } else { - len = 0; - } - } else - if ( bCompressed && iGroupOutputCount >= INCHI_T_NUM_MOVABLE ) { - /* compressed canon number in Abc */ - len = MakeAbcNumber( szValue, (int)sizeof(szValue), NULL, nValue ); - iGroupOutputCount ++; - } else { - /* always output number of hydrogen atoms as a decimal */ - /* output leading space if: */ - /* (a) this is the first output value in compressed mode (i==1 && bCompressed) */ - /* (b) this is not the first output value in non-compressed mode ( iGroupOutputCount && !bCompressed) */ - if ( bCompressed ) { - p = NULL; - len = 0; - switch( iGroupOutputCount ) { - case 0: - len = MakeDecNumber( szValue, (int)sizeof(szValue), (i == 1)? ITEM_DELIMETER:NULL, nValue ); - break; - case 1: - p = "-"; - break; - case 2: - p = "+"; - break; - } - if ( p ) { - switch( nValue ) { - case 0: - len = 0; - break; - case 1: - strcpy(szValue, p); - len = strlen(szValue); - break; - default: - len = MakeDecNumber( szValue, (int)sizeof(szValue), p, nValue ); - break; - } - } - } else { - if ( iGroupOutputCount >= INCHI_T_NUM_MOVABLE ) { - /* canonical number of the atom in the tautomeric group */ - len = MakeDecNumber( szValue, (int)sizeof(szValue), ITEM_DELIMETER, nValue ); - } else { - p = NULL; - len = 0; - if ( nValue ) { - switch( iGroupOutputCount ) { - case 0: - p = "H"; - break; - case 1: - p = "-"; - break; - case 2: - p = "+"; - break; - } - if ( p ) { - /* number of hydrogens */ - if ( nValue == 1 ) { - strcpy(szValue, p); - len = strlen(szValue); - } else { - len = MakeDecNumber( szValue, (int)sizeof(szValue), p, nValue ); - } - } - } - } - } - iGroupOutputCount ++; - } - if ( 0 <= len && nLen+len < nLen_szLinearCT ) { - if ( len ) { - strcpy( szLinearCT+nLen, szValue ); - nLen += len; - } - } else { - bOvfl = 1; - break; - } - } - if ( !bOvfl && !bCompressed && i ) { - if ( nLen + 1 < nLen_szLinearCT ) { - strcpy( szLinearCT+nLen, ")" ); - nLen ++; - } else { - bOvfl = 1; - } - } - } - *bOverflow |= bOvfl; - return nLen; -} -/********************************************************************************************** - * nCtMode = 0: full - * 2: compressed CT - * 22+3s3: 22=canon. number; +3=charge; s=singlet (d=doublet, t=triplet, s is omitted if valence=0), 3 = valence - * 22+3.3, (charge, valence) 22.3 (valence) 22t3 (triplet, valence) - * Ab+3t4: Ab=canon. number; +3=charge or "." t=triplet (or s, d), 4=valence - **********************************************************************************************/ -int MakeCRVString( ORIG_INFO *OrigInfo, int nLenCT, int bAddDelim, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) -{ - /* produce output string; */ - int nLen = 0, len, k, bAbcNumbers; - int bOvfl = *bOverflow; - char szValue[32]; - int bNext=0; - bAbcNumbers = ( nCtMode & CT_MODE_ABC_NUMBERS ); - /* add connection table string */ - if ( !bOvfl && bAddDelim ) { - if ( nLen_szLinearCT > 2 ) { - strcpy( szLinearCT, ", " ); - nLen += 2; - } else { - bOvfl = 1; - } - } - for ( k = 0; !bOvfl && k < nLenCT && nLen < nLen_szLinearCT; k ++ ) { - /* find the next non-empty entry */ - if ( OrigInfo[k].cCharge || OrigInfo[k].cRadical || OrigInfo[k].cUnusualValence ) { - if ( bAbcNumbers ) { - /* - 3 items: Ad+3d4 (canon. numb=Ad, charge=+3, doublet, valence = 4 - 2 items: Ad.d4 Ad+3.4 Ad+3d - 1 item: Ad+3 Ad.d Ad4 - - dot output before radical: no charge, radical is present - dot before valence: charge is present, no radical, valence is present - */ - len = MakeAbcNumber( szValue, (int)sizeof(szValue), NULL, k+1 ); - - /* charge */ - if ( OrigInfo[k].cCharge ) { - if ( OrigInfo[k].cCharge > 0 ) { - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, "+", OrigInfo[k].cCharge ); - } else { - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, OrigInfo[k].cCharge ); - } - } - /* radical */ - if ( OrigInfo[k].cRadical ) { - if ( !OrigInfo[k].cCharge ) { - szValue[len ++] = '.'; - } - switch( OrigInfo[k].cRadical ) { - case 1: - szValue[len ++] = 'd'; - break; - case 2: - szValue[len ++] = 't'; - break; - default: - szValue[len ++] = 'u'; - break; - } - } - /* valence */ - if ( OrigInfo[k].cUnusualValence ) { - if ( OrigInfo[k].cCharge && !OrigInfo[k].cRadical ) { - szValue[len ++] = '.'; - } - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, OrigInfo[k].cUnusualValence ); - } - } else { - /* - 3 items: 22+3d4 (canon. numb=22, charge=+3, doublet, valence = 4 - 2 items: 22d4 22+3.4 22+3d - 1 item: 22+3 22d 22.4 - - dot output before valence: - (a) charge, no radical, valence - (b) no charge, no radical, valence - that is, whenever valence is present and no radical - */ - len = MakeDecNumber( szValue, (int)sizeof(szValue), bNext? ITEM_DELIMETER:NULL, k+1 ); - /* charge */ - if ( OrigInfo[k].cCharge ) { - if ( OrigInfo[k].cCharge > 0 ) { - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, "+", OrigInfo[k].cCharge ); - } else { - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, OrigInfo[k].cCharge ); - } - } - /* radical */ - if ( OrigInfo[k].cRadical ) { - switch( OrigInfo[k].cRadical ) { - case 1: - szValue[len ++] = 'd'; - break; - case 2: - szValue[len ++] = 't'; - break; - default: - szValue[len ++] = 'u'; - break; - } - } - /* valence */ - if ( OrigInfo[k].cUnusualValence ) { - if ( !OrigInfo[k].cRadical ) { - szValue[len ++] = '.'; - } - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, OrigInfo[k].cUnusualValence ); - } - } - } else { - len = 0; - } - if ( len && nLen+len < nLen_szLinearCT ) { - strcpy( szLinearCT+nLen, szValue ); - nLen += len; - bNext ++; - } else - if ( len ) { - bOvfl = 1; - break; - } - } - *bOverflow |= bOvfl; - return nLen; -} - -/********************************************************************************************** - * nCtMode = 0: full - * 2: compressed CT - **********************************************************************************************/ -int MakeEquString( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) -{ - /* produce output string; */ - int nLen = 0, len, i, k, bAbcNumbers; - int bOvfl = *bOverflow; - char szValue[16]; - int bNext=0; - bAbcNumbers = ( nCtMode & CT_MODE_ABC_NUMBERS ); - /* add connection table string */ - if ( !bOvfl && bAddDelim ) { - if ( nLen_szLinearCT > 2 ) { - strcpy( szLinearCT, ", " ); - nLen += 2; - } else { - bOvfl = 1; - } - } - for ( k = 0; !bOvfl && k < nLenCT && nLen < nLen_szLinearCT; k ++ ) { - /* find the first equivalence number */ - if ( k != (int)LinearCT[k] - 1 ) - continue; - for ( i = k; i < nLenCT && nLen < nLen_szLinearCT; i ++ ) { - if ( k != (int)LinearCT[i]-1 ) - continue; - /* equivalence number: a minimal canon_number out of a group of equivalent atoms */ - /* is at canon_number-1 position of each equivalent atom. */ - if ( bAbcNumbers ) { - len = MakeAbcNumber( szValue, (int)sizeof(szValue), (i==k && bNext)? ITEM_DELIMETER : NULL, i+1 ); - } else { - len = MakeDecNumber( szValue, (int)sizeof(szValue), (i==k)? "(":ITEM_DELIMETER, i+1 ); - } - if ( 0 <= len && nLen+len < nLen_szLinearCT ) { - strcpy( szLinearCT+nLen, szValue ); - nLen += len; - bNext ++; - } else - if ( 0 > len ) { - bOvfl = 1; - break; - } - } - if ( !bOvfl && !bAbcNumbers ) { - if ( nLen + 2 < nLen_szLinearCT ) { - strcpy( szLinearCT+nLen, ")" ); - nLen ++; - } else { - bOvfl = 1; - } - } - } - *bOverflow |= bOvfl; - return nLen; -} -/********************************************************************************************** - * nCtMode = 0: full - * 2: compressed CT - **********************************************************************************************/ -int MakeIsoAtomString( INChI_IsotopicAtom *IsotopicAtom, int nNumberOfIsotopicAtoms, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) -{ - /* produce output string; */ - int nLen = 0, len, tot_len, ret, i, j, bOvfl = *bOverflow; - char szValue[64]; - char *p; - int nValue; - int bAbcNumbers = (nCtMode & CT_MODE_ABC_NUMBERS ); - static const char letter[] = "itdh"; - static const char *h[] = {"T", "D", "H"}; - static const char *sign[] = {"-", "+"}; - - if ( !bOvfl ) { - for ( i = 0; i < nNumberOfIsotopicAtoms && nLen < nLen_szLinearCT; i ++ ) { - p = szValue; - tot_len = 0; - for ( j = 0; j < 5; j ++ ) { - len = 0; - switch( j ) { - case 0: - nValue = (int)IsotopicAtom[i].nAtomNumber; - break; - case 1: - nValue = (int)IsotopicAtom[i].nIsoDifference; - break; - case 2: - nValue = (int)IsotopicAtom[i].nNum_T; - break; - case 3: - nValue = (int)IsotopicAtom[i].nNum_D; - break; - case 4: - nValue = (int)IsotopicAtom[i].nNum_H; - break; - } - if ( !j ) { - /* atom canonical number */ - len = (bAbcNumbers? MakeAbcNumber:MakeDecNumber) - ( p, (int)sizeof(szValue)-tot_len, - bAbcNumbers?NULL:(i?ITEM_DELIMETER:EXTRA_SPACE), nValue - ); - } else - if ( bAbcNumbers ) { /* Abc output */ - switch ( j ) { - case 1: /* nIsoDifference */ - len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, NULL, nValue ); - break; - case 2: /* nNum_T */ - case 3: /* nNum_D */ - case 4: /* nNum_H */ - if ( nValue ) { - if ( (int)sizeof(szValue) - tot_len > 1 ) { - p[len++]=letter[j-1]; - if ( 1 == nValue ) { - p[len] = '\0'; - } else { - ret = MakeDecNumber( p+len, (int)sizeof(szValue)-tot_len-len, NULL, nValue ); - len = (ret >= 0)? len+ret : ret; - } - } else { - len = -1; /* overflow */ - } - } - } - } else - if ( nValue ) { - if ( j == 1 ) { /* Decimal output */ - /* signed isotopic mass difference */ - int subtract = (nValue > 0); - /* (n = mass difference) > 0 corresponds to nValue = n+1 */ - /* subtract 1 from it so that mass difference for 35Cl or 12C is zero */ - len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, sign[nValue>=0], abs(nValue-subtract) ); - } else { - /* hydrogen isotope */ - if ( nValue != 1 ) { - len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, h[j-2], nValue ); - } else - if ( (int)sizeof(szValue)-tot_len > 1 ) { - strcpy( p, h[j-2] ); - len = 1; - } else { - len = -1; /* overflow */ - } - } - } else { - continue; /* do not write zeroes */ - } - if ( len < 0 ) { - bOvfl = 1; - break; - } - tot_len += len; - p += len; - } - if ( nLen+tot_len < nLen_szLinearCT ) { - memcpy( szLinearCT+nLen, szValue, tot_len+1 ); - nLen += tot_len; - } else { - bOvfl = 1; - break; - } - } - } - *bOverflow |= bOvfl; - return nLen; -} -/**********************************************************************************************/ -int MakeIsoTautString( INChI_IsotopicTGroup *IsotopicTGroup, int nNumberOfIsotopicTGroups, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) -{ - /* produce output string; */ - int nLen = 0, len, tot_len, i, j, bOvfl = *bOverflow; - AT_NUMB nMax; - char szValue[32]; - char *p; - int nValue; - int bAbcNumbers = ( nCtMode & CT_MODE_ABC_NUMBERS ); - static const char letter[] = "tdh"; - static const char *h[] = {"T", "D", "H"}; - /* add connection table string */ - nMax = 0; - if ( !bOvfl ) { - for ( i = 0; i < nNumberOfIsotopicTGroups && nLen < nLen_szLinearCT; i ++ ) { - p = szValue; - tot_len = 0; - for ( j = 0; j < 4; j ++ ) { - switch( j ) { - case 0: - nValue = (int)IsotopicTGroup[i].nTGroupNumber; - break; - case 1: - nValue = (int)IsotopicTGroup[i].nNum_T; - break; - case 2: - nValue = (int)IsotopicTGroup[i].nNum_D; - break; - case 3: - nValue = (int)IsotopicTGroup[i].nNum_H; - break; - } - if ( !j ) { - /* atom canonical number */ - len = (bAbcNumbers?MakeAbcNumber:MakeDecNumber) - ( p, (int)sizeof(szValue)-tot_len, - bAbcNumbers?NULL:(i?ITEM_DELIMETER:EXTRA_SPACE), - nValue - ); - } else - if ( nValue ) { - if ( bAbcNumbers ) { - len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, NULL, nValue ); - if ( len > 0 ) { /* make sure overflow has not happened */ - if ( (int)sizeof(szValue)-tot_len-len > 1 ) { - p[len++]=letter[j-1]; - p[len] = '\0'; - } else { - len = -1; /* overflow */ - } - } - } else { - /* hydrogen isotope */ - if ( nValue != 1 ) { - len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, h[j-1], nValue ); - } else - if ( (int)sizeof(szValue)-tot_len > 1 ) { - strcpy( p, h[j-1] ); - len = 1; - } else { - len = -1; /* overflow */ - } - } - } else { - continue; /* do not write zeroes */ - } - if ( len < 0 ) { - bOvfl = 1; - break; - } - p += len; - tot_len += len; - } - if ( nLen+tot_len < nLen_szLinearCT ) { - memcpy( szLinearCT+nLen, szValue, tot_len+1 ); - nLen += tot_len; - } else { - bOvfl = 1; - break; - } - } - } - *bOverflow |= bOvfl; - return nLen; -} -/**********************************************************************************************/ -int MakeIsoHString( int num_iso_H[], char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) -{ - /* produce output string; */ - int nLen = 0, len, tot_len, j, bOvfl = *bOverflow; - AT_NUMB nMax; - char szValue[32]; - char *p; - int nValue; - int bAbcNumbers = ( nCtMode & CT_MODE_ABC_NUMBERS ); - static const char letter[] = "tdh"; - static const char *h[] = {"T", "D", "H"}; - /* add connection table string */ - nMax = 0; - if ( !bOvfl ) { - p = szValue; - tot_len = 0; - for ( j = 1; j < 4; j ++ ) { - nValue = num_iso_H[NUM_H_ISOTOPES-j];/* j: 1=>T, 2=>D, 3=>1H */ - if ( nValue ) { - if ( bAbcNumbers ) { - len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, NULL, nValue ); - if ( len > 0 ) { /* make sure overflow has not happened */ - if ( (int)sizeof(szValue)-tot_len-len > 1 ) { - p[len++]=letter[j-1]; - p[len] = '\0'; - } else { - len = -1; /* overflow */ - } - } - } else { - /* hydrogen isotope */ - if ( nValue != 1 ) { - len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, h[j-1], nValue ); - } else - if ( (int)sizeof(szValue)-tot_len > 1 ) { - strcpy( p, h[j-1] ); - len = 1; - } else { - len = -1; /* overflow */ - } - } - } else { - continue; /* do not write zeroes */ - } - if ( len < 0 ) { - bOvfl = 1; - break; - } - p += len; - tot_len += len; - } - if ( nLen+tot_len < nLen_szLinearCT ) { - memcpy( szLinearCT+nLen, szValue, tot_len+1 ); - nLen += tot_len; - } else { - bOvfl = 1; - } - } - *bOverflow |= bOvfl; - return nLen; -} -/**********************************************************************************************/ -int MakeStereoString( AT_NUMB *at1, AT_NUMB *at2, S_CHAR *parity, int bAddDelim, int nLenCT, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) -{ - /* produce output string; */ - int nLen = 0, len, tot_len, i, j, bOvfl = *bOverflow; - char szValue[32]; - char *p; - int nValue; - static const char parity_char[] = "!-+u?"; - bAddDelim = 0; - if ( !bOvfl ) { - for ( i = 0; i < nLenCT && nLen < nLen_szLinearCT; i ++ ) { - p = szValue; - tot_len = 0; - for ( j = 0; j < 3; j ++ ) { - if ( j == 0 && at1 ) - nValue = (int)at1[i]; - else - if ( j == 1 && at2 ) - nValue = (int)at2[i]; - else - if ( j == 2 && parity ) - nValue = (int)parity[i]; - else - continue; - if ( nCtMode & CT_MODE_ABC_NUMBERS ) { - len = (j==2? MakeDecNumber : MakeAbcNumber)( p, (int)sizeof(szValue)-tot_len, NULL, nValue ); - } else { - if ( j < 2 ) { - len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, tot_len?"-":(i||bAddDelim)?ITEM_DELIMETER:NULL, nValue ); - } else - if ( tot_len + 1 < (int)sizeof(szValue) ) { - *p ++ = (0<=nValue && nValue<=4)? parity_char[nValue]:parity_char[0]; - *p = '\0'; - len = 1; - } else { - len = -1; /* Overflow */ - } - } - if ( len < 0 ) { - bOvfl = 1; - break; - } - p += len; - tot_len += len; - } - if ( nLen+tot_len < nLen_szLinearCT ) { - memcpy( szLinearCT+nLen, szValue, tot_len+1 ); - nLen += tot_len; - } else { - bOvfl = 1; - break; - } - } - } - *bOverflow |= bOvfl; - return nLen; -} -#ifdef ALPHA_BASE - -#if ( ALPHA_BASE != 27 ) -#error ALPHA_BASE definitions mismatch -#endif - -#else - -#define ALPHA_BASE 27 - -#endif - -#define ALPHA_MINUS '-' -#define ALPHA_ZERO_VAL '.' -#define ALPHA_ONE 'a' -#define ALPHA_ZERO '@' -/**********************************************************************************************/ -/* Produce an "Alphabetic" number, base 27 (27 digits: 0, a, b, ..., z) */ -/* The leading "digit" uppercase, the rest -- lowercase */ -/* szString length nStringLen includes 1 byte for zero termination */ -/* Return Value: length without zero termination; -1 means not enough room */ -/* Note: ASCII-encoding specific implementation */ -int MakeAbcNumber( char *szString, int nStringLen, const char *szLeadingDelim, int nValue ) -{ - char *p = szString; - char *q; - int nChar; - - if ( nStringLen < 2 ) - return -1; - while ( szLeadingDelim && *szLeadingDelim && --nStringLen ) { - *p ++ = *szLeadingDelim ++; - } - if ( nStringLen < 2 ) - return -1; - if ( !nValue ) { - *p++ = ALPHA_ZERO_VAL; /* zero value (cannot use 0) */ - *p = '\0'; - return 1; - } - if ( nValue < 0 ) { - *p++ = ALPHA_MINUS; - nStringLen --; - nValue = -nValue; - } - for ( q = p; nValue && --nStringLen; nValue /= ALPHA_BASE ) { - if ( nChar = nValue % ALPHA_BASE ) { - nChar = ALPHA_ONE + nChar - 1; - } else { - nChar = ALPHA_ZERO; - } - *q++ = nChar; - } - if ( nStringLen <= 0 ) - return -1; - *q = '\0'; - mystrrev( p ); - p[0] = toupper(p[0]); - return (q - szString); -} -#if ( READ_INCHI_STRING == 1 ) -/*****************************************************/ -static long abctol( const char *szString, char **q ); /* keep compiler happy */ - -long abctol( const char *szString, char **q ) -{ -#define __MYTOLOWER(c) ( ((c) >= 'A') && ((c) <= 'Z') ? ((c) - 'A' + 'a') : (c) ) - - long val = 0; - long sign = 1; - const char *p = szString; - if ( *p == ALPHA_MINUS ) { - p ++; - sign = -1; - } - if ( *p == ALPHA_ZERO ) { - p ++; - goto exit_function; - } - if ( !isupper(UCINT *p) ) { - p = szString; - goto exit_function; /* not an abc-number */ - } - val = __MYTOLOWER(*p) - ALPHA_ONE + 1; - p ++; - while ( *p ) { - if ( islower( UCINT *p ) ) { - val *= ALPHA_BASE; - val += *p - ALPHA_ONE + 1; - } else - if ( *p == ALPHA_ZERO ) { - val *= ALPHA_BASE; - } else { - break; - } - p ++; - } -exit_function: - if ( q ) { - *q = (char *)p; /* cast deliberately discards const qualifier */ - } - return val; -#undef __MYTOLOWER -} -/********************************************************/ -long inchi_strtol( const char *str, const char **p, int base) -{ - if ( base == ALPHA_BASE ) { - return abctol( str, (char **)p ); /* cast deliberately discards const qualifier */ - } else { - return strtol( str, (char **)p, base ); /* cast deliberately discards const qualifier */ - } -} -#endif -#undef ALPHA_BASE -#undef ALPHA_MINUS -#undef ALPHA_ZERO_VAL -#undef ALPHA_ONE -#undef ALPHA_ZERO - -/********************************************************/ -double inchi_strtod( const char *str, const char **p ) -{ - return strtod( str, (char **)p ); -} - -/**********************************************************************************************/ -/* Produce a decimal number */ -/* szString length nStringLen includes 1 byte for zero termination */ -/* Return Value: length without zero termination; -1 means not enough room */ -int MakeDecNumber( char *szString, int nStringLen, const char *szLeadingDelim, int nValue ) -{ -#define DECIMAL_BASE 10 -#define DECIMAL_MINUS '-' -#define DECIMAL_ZERO_VAL '0' -#define DECIMAL_ONE '1' -#define DECIMAL_ZERO '0' - char *p = szString; - char *q; - int nChar; - - if ( nStringLen < 2 ) - return -1; - while ( szLeadingDelim && *szLeadingDelim && --nStringLen ) { - *p ++ = *szLeadingDelim ++; - } - if ( nStringLen < 2 ) - return -1; - if ( !nValue ) { - *p++ = DECIMAL_ZERO_VAL; /* zero value (cannot use 0) */ - *p = '\0'; - return p-szString; - } - if ( nValue < 0 ) { - *p++ = DECIMAL_MINUS; - nStringLen --; - nValue = -nValue; - } - for ( q = p; nValue && --nStringLen; nValue /= DECIMAL_BASE ) { - if ( nChar = nValue % DECIMAL_BASE ) { - nChar = DECIMAL_ONE + nChar - 1; - } else { - nChar = DECIMAL_ZERO; - } - *q++ = nChar; - } - if ( nStringLen <= 0 ) - return -1; - *q = '\0'; - mystrrev( p ); - return (q - szString); -#undef DECIMAL_BASE -#undef DECIMAL_MINUS -#undef DECIMAL_ZERO_VAL -#undef DECIMAL_ONE -#undef DECIMAL_ZERO -} diff --git a/INCHI-1-SRC/INCHI/common/ichiqueu.c b/INCHI-1-SRC/INCHI/common/ichiqueu.c deleted file mode 100644 index 362bd84..0000000 --- a/INCHI-1-SRC/INCHI/common/ichiqueu.c +++ /dev/null @@ -1,1436 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include - -/*^^^ */ -#include "mode.h" - -#include "inpdef.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichi_bns.h" -/*^^^ */ - -/*******************************************************************/ - -#if ( FIND_RING_SYSTEMS == 1 ) /* { */ - -/* local prototypes */ -int are_alt_bonds( U_CHAR *bonds, int len ); -int AddBondsPos( inp_ATOM *atom, T_BONDPOS *BondPosTmp, int nNumBondPosTmp, T_BONDPOS *BondPos, int nMaxNumBondPos, int nNumBondPos ); -int AddEndPoints( T_ENDPOINT *EndPointTmp, int nNumNewEndPoint, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, int nNumEndPoint); - - - - - -/****************************************** - * - * Tautomerism in 5- and 6-member rings - * - ******************************************/ - -const int NONE = (AT_RANK)~0; - - -/* - 1,5 Tautomerism in 6-member alt ring: - - /=\ /==\ - HN C=O <-> N C-OH - \=/ \\-// - - - 1,2 Tautomerism in 5-member ring: - - - HN--X N==X - | \\ | \ - | Z <-> | Z - | / | // - N==Y HN--Y - - - 1,4 tautomerism in 7-member ring - - /C==D //C-D - O=B \ HO-B \\ - | E <-> | E - HO-A // O=A / - \\G-F \\G-F - - - 1,4 tautomerism in 5-member ring - - - O=B--C O-B==C - | \\ | \ - | D <-> | D - | / | // - HO-A==E HO=A--E - -*/ -typedef int CHECK_DFS_RING( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int nStartAtomNeighbor, - int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ); - -typedef int CHECK_CENTERPOINT ( inp_ATOM *atom, int iat ); - -CHECK_DFS_RING Check7MembTautRing; -CHECK_DFS_RING Check6MembTautRing; -CHECK_DFS_RING Check5MembTautRing; - -#if ( TAUT_15_NON_RING == 1 ) /* post v.1 feature */ -/* DFS simple alt path for 1,5 tautomerism, post v.1 feature */ -typedef int CHECK_DFS_PATH( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int jNxtNeigh, int nStartAtomNeighbor, - int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ); - -typedef int CHECK_DFS_CENTERPOINT( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int jNxtNeigh, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ); - - -CHECK_DFS_PATH Check15TautPath; -CHECK_DFS_CENTERPOINT Check15TautPathCenterpoint; - -int DFS_FindTautAltPath( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, - int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, - int nCycleLen, - AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, - CHECK_DFS_PATH *CheckDfsPath, CHECK_DFS_CENTERPOINT *CheckCenterPoint, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ); - -#define BOND_WRONG 64 -#define IS_ALT_OR_DBLBOND(X) (((X) == BOND_SINGLE || (X) == BOND_DOUBLE)? (X) : \ - ((X) == BOND_ALTERN || (X) == BOND_TAUTOM || (X) == BOND_ALT12NS)? BOND_ALTERN : \ - BOND_WRONG); - -#endif /* TAUT_15_NON_RING */ - -int DFS_FindTautInARing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, - int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, - int nCycleLen, - AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, - CHECK_DFS_RING *CheckDfsRing, CHECK_CENTERPOINT *CheckCenterPoint, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ); - -#if ( REPLACE_ALT_WITH_TAUT == 1 ) -#define REPLACE_THE_BOND(X) ( (X) == BOND_SINGLE || (X) == BOND_DOUBLE || (X) == BOND_ALTERN || (X) == BOND_ALT12NS ) -#else -#define REPLACE_THE_BOND(X) ( (X) == BOND_SINGLE || (X) == BOND_DOUBLE ) -#endif - - -int bIsCenterPointStrict( inp_ATOM *atom, int iat ) -{ - if ( atom[iat].valence == atom[iat].chem_bonds_valence ) { - int endpoint_valence = get_endpoint_valence(atom[iat].el_number); - if ( endpoint_valence && (endpoint_valence > atom[iat].valence && /* added a check for negative charge or H 3-31-03 */ - (atom[iat].num_H || atom[iat].charge == -1) || - !atom[iat].charge && atom[iat].c_point) ) { - return 1; /* may appear to be tautomeric or chargable - (this increases chem_bonds_valence), should be explored */ - } - return 0; - } - if (atom[iat].valence+1 == atom[iat].chem_bonds_valence && - is_centerpoint_elem_strict( atom[iat].el_number ) ) { - return 1; - } - return 0; -} -/********************************************************************************/ -int nGet14TautIn7MembAltRing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, - int nStartAtomNeighborEndpoint, int nStartAtomNeighborNeighborEndpoint, - AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, int nMaxLenDfsPath, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ) -{ - int nRet; - - *pnNumEndPoint = 0; - *pnNumBondPos = 0; - - if ( nMaxLenDfsPath <= 7 ) { - return -1; /* path is too short */ - } - - nRet = DFS_FindTautInARing( atom, nStartAtom, nStartAtomNeighbor, - nStartAtomNeighborEndpoint, nStartAtomNeighborNeighborEndpoint, 7, - nDfsPathPos, DfsPath, - Check7MembTautRing, bIsCenterPointStrict, - EndPoint, nMaxNumEndPoint, - BondPos, nMaxNumBondPos, - pnNumEndPoint, pnNumBondPos, - pBNS, pBD, num_atoms - ); - - - return nRet; -} -/********************************************************************************/ -int nGet14TautIn5MembAltRing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, - int nStartAtomNeighborEndpoint, int nStartAtomNeighborNeighborEndpoint, - AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, int nMaxLenDfsPath, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ) -{ - int nRet; - - *pnNumEndPoint = 0; - *pnNumBondPos = 0; - - if ( nMaxLenDfsPath <= 5 ) { - return -1; /* path is too short */ - } - - nRet = DFS_FindTautInARing( atom, nStartAtom, nStartAtomNeighbor, - nStartAtomNeighborEndpoint, nStartAtomNeighborNeighborEndpoint, 5, - nDfsPathPos, DfsPath, - Check7MembTautRing, bIsCenterPointStrict, - EndPoint, nMaxNumEndPoint, - BondPos, nMaxNumBondPos, - pnNumEndPoint, pnNumBondPos, - pBNS, pBD, num_atoms - ); - - - return nRet; -} - -/********************************************************************************/ -int nGet12TautIn5MembAltRing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, - AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, int nMaxLenDfsPath, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ) -{ - int nRet; - - *pnNumEndPoint = 0; - *pnNumBondPos = 0; - - if ( nMaxLenDfsPath <= 5 ) { - return -1; /* path is too short */ - } - - nRet = DFS_FindTautInARing( atom, nStartAtom, nStartAtomNeighbor, -1, -1, 5, - nDfsPathPos, DfsPath, - Check5MembTautRing, bIsCenterPointStrict, - EndPoint, nMaxNumEndPoint, - BondPos, nMaxNumBondPos, - pnNumEndPoint, pnNumBondPos, - pBNS, pBD, num_atoms - ); - return nRet; -} - -/********************************************************************************/ -int nGet15TautIn6MembAltRing( inp_ATOM *atom, int nStartAtom, AT_RANK *nDfsPathPos, - DFS_PATH *DfsPath, int nMaxLenDfsPath, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ) -{ - int nRet; - - *pnNumEndPoint = 0; - *pnNumBondPos = 0; - - if ( nMaxLenDfsPath <= 7 ) { - return -1; /* path is too short */ - } - - nRet = DFS_FindTautInARing( atom, nStartAtom, -1/*nStartAtomNeighbor*/, -1/*nStartAtomNeighbor2*/, - -1/*nStartAtomNeighborNeighbor*/, 6 /* nCycleLen*/, - nDfsPathPos, DfsPath, - Check6MembTautRing, bIsCenterPointStrict, - EndPoint, nMaxNumEndPoint, - BondPos, nMaxNumBondPos, - pnNumEndPoint, pnNumBondPos, - pBNS, pBD, num_atoms - ); - return nRet; -} -#if ( TAUT_15_NON_RING == 1 ) /***** post v.1 feature *****/ -/********************************************************************************/ -int nGet15TautInAltPath( inp_ATOM *atom, int nStartAtom, AT_RANK *nDfsPathPos, - DFS_PATH *DfsPath, int nMaxLenDfsPath, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ) -{ - int nRet; - - *pnNumEndPoint = 0; - *pnNumBondPos = 0; - - if ( nMaxLenDfsPath <= 7 ) { - return -1; /* path is too short */ - } - - nRet = DFS_FindTautAltPath( atom, nStartAtom, -1/*nStartAtomNeighbor*/, -1/*nStartAtomNeighbor2*/, - -1/*nStartAtomNeighborNeighbor*/, 4 /* nCycleLen*/, - nDfsPathPos, DfsPath, - Check15TautPath, Check15TautPathCenterpoint, - EndPoint, nMaxNumEndPoint, - BondPos, nMaxNumBondPos, - pnNumEndPoint, pnNumBondPos, - pBNS, pBD, num_atoms - ); - return nRet; -} -#endif -/********************************************************************************/ -/* DFS version */ -#define MAX_DFS_DEPTH 16 - -/********************************************************************************/ -int DFS_FindTautInARing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, - int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, - int nCycleLen, - AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, - CHECK_DFS_RING *CheckDfsRing, CHECK_CENTERPOINT *CheckCenterPoint, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ) -{ - /* Depth First Search */ - /* Ignore all atoms not belonging to the current ring system (=biconnected component) */ - AT_RANK nMinLenDfsPath; - int j, cur_at, nxt_at, prv_at; - int nLenDfsPath, nNumFound, ret; - AT_RANK nRingSystem; - int nDoNotTouchAtom1 = -1, nDoNotTouchAtom2 = -1; - - nLenDfsPath=0; - nNumFound=0; - - nCycleLen --; - - DfsPath[nLenDfsPath].at_no = cur_at = nStartAtom; - DfsPath[nLenDfsPath].bond_type = 0; - DfsPath[nLenDfsPath].bond_pos = -1; - nDfsPathPos[cur_at] = nLenDfsPath+1; /* mark */ - nRingSystem = atom[nStartAtom].nRingSystem; - nMinLenDfsPath = 0; - if ( nStartAtomNeighbor2 >= 0 ) { - nDoNotTouchAtom1 = (int)atom[cur_at].neighbor[nStartAtomNeighbor2]; - } - - - /* add the first neighbor to the 2nd tree position if required */ - if ( nStartAtomNeighbor >= 0 ) { - j = nStartAtomNeighbor; - prv_at = cur_at; - cur_at = atom[prv_at].neighbor[j]; - DfsPath[nLenDfsPath].bond_type = (atom[prv_at].bond_type[j] & ~BOND_MARK_ALL); -#if ( FIX_BOND23_IN_TAUT == 1 ) - DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER(pBNS,prv_at,j,DfsPath[nLenDfsPath].bond_type); -#endif - DfsPath[nLenDfsPath].bond_pos = j; - - nLenDfsPath ++; - - DfsPath[nLenDfsPath].at_no = cur_at; - DfsPath[nLenDfsPath].bond_type = 0; - DfsPath[nLenDfsPath].bond_pos = -1; - nDfsPathPos[cur_at] = nLenDfsPath+1; - nMinLenDfsPath ++; - if ( nStartAtomNeighborNeighbor >= 0 ) { - nDoNotTouchAtom2 = (int)atom[cur_at].neighbor[nStartAtomNeighborNeighbor]; - } - } - - /* MAIN DFS CYCLE: may find one and the same t-group 2 times; saves only one instance */ - /* traverse *all* paths starting at atom[nStartAtom]; max. path length = (nCycleLen+1) */ - while ( nLenDfsPath >= nMinLenDfsPath ) { - j = ++DfsPath[nLenDfsPath].bond_pos; - if ( j < atom[cur_at=(int)DfsPath[nLenDfsPath].at_no].valence ) { - DfsPath[nLenDfsPath].bond_type = (atom[cur_at].bond_type[j] & ~BOND_MARK_ALL); -#if ( FIX_BOND23_IN_TAUT == 1 ) - DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER(pBNS,cur_at,j,DfsPath[nLenDfsPath].bond_type); -#endif - nxt_at = (int)atom[cur_at].neighbor[j]; - if ( nxt_at == nDoNotTouchAtom1 || - nxt_at == nDoNotTouchAtom2 ) { - ; /* ignore */ - } else - if ( nDfsPathPos[nxt_at] ) { - /* found a ring closure or a step backwards */ - if ( 1 == nDfsPathPos[nxt_at] && nLenDfsPath == nCycleLen ) { - /* we have found the cycle; check it */ - ret = (*CheckDfsRing)( atom, DfsPath, nLenDfsPath, nStartAtomNeighbor, - nStartAtomNeighbor2, nStartAtomNeighborNeighbor, - EndPoint, nMaxNumEndPoint, BondPos, nMaxNumBondPos, - pnNumEndPoint, pnNumBondPos, - pBNS, pBD, num_atoms - ); - if ( ret < 0 ) { - nNumFound = ret; - goto clear_path; - } - nNumFound += ret; - - } - } else - if ( !(*CheckCenterPoint)( atom, nxt_at ) ) { - ; /* cannot advance to a non-centerpoint; ignore */ - } else - if ( nLenDfsPath < nCycleLen ) { - /* advance */ - nLenDfsPath ++; - cur_at = nxt_at; - DfsPath[nLenDfsPath].at_no = cur_at; - DfsPath[nLenDfsPath].bond_type = 0; - DfsPath[nLenDfsPath].bond_pos = -1; - nDfsPathPos[cur_at] = nLenDfsPath+1; /* mark */ - } - } else { - /* retract */ - nDfsPathPos[(int)DfsPath[nLenDfsPath].at_no] = 0; - nLenDfsPath --; - } - } -clear_path: - while ( 0 <= nLenDfsPath ) { - nDfsPathPos[(int)DfsPath[nLenDfsPath].at_no] = 0; - nLenDfsPath --; - } - return nNumFound; -} -#if ( TAUT_15_NON_RING == 1 ) /***** post v.1 feature *****/ -/********************************************************************************/ -int DFS_FindTautAltPath( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, - int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, - int nCycleLen, - AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, - CHECK_DFS_PATH *CheckDfsPath, CHECK_DFS_CENTERPOINT *CheckCenterPointPath, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ) -{ - /* Naive Depth First Search: same atom may be approached along different alt paths */ - /* Ignore all atoms not belonging to the current ring system (=biconnected component) */ - AT_RANK nMinLenDfsPath; - int j, cur_at, nxt_at, prv_at; - int nLenDfsPath, nNumFound, ret; - AT_RANK nRingSystem; - int nDoNotTouchAtom1 = -1, nDoNotTouchAtom2 = -1; - - nLenDfsPath=0; - nNumFound=0; - - nCycleLen --; /* indef of the last atom in the alt path, statring from 0 */ - - DfsPath[nLenDfsPath].at_no = cur_at = nStartAtom; - DfsPath[nLenDfsPath].bond_type = 0; - DfsPath[nLenDfsPath].bond_pos = -1; /* initialize index of the bond to the next atom */ - nDfsPathPos[cur_at] = nLenDfsPath+1; /* mark with distance + 1 */ - nRingSystem = atom[nStartAtom].nRingSystem; - nMinLenDfsPath = 0; /* allow to restart from nStartAtom */ - if ( nStartAtomNeighbor2 >= 0 ) { - nDoNotTouchAtom1 = (int)atom[cur_at].neighbor[nStartAtomNeighbor2]; - } - - - /* add the first neighbor to the 2nd tree position if required */ - if ( nStartAtomNeighbor >= 0 ) { - j = nStartAtomNeighbor; - prv_at = cur_at; - cur_at = atom[prv_at].neighbor[j]; - DfsPath[nLenDfsPath].bond_type = (atom[prv_at].bond_type[j] & ~BOND_MARK_ALL); -#if ( FIX_BOND23_IN_TAUT == 1 ) - DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER(pBNS,prv_at,j,DfsPath[nLenDfsPath].bond_type); -#endif - DfsPath[nLenDfsPath].bond_pos = j; /* fix index of the bond to the next atom */ - - nLenDfsPath ++; - - DfsPath[nLenDfsPath].at_no = cur_at; - DfsPath[nLenDfsPath].bond_type = 0; - DfsPath[nLenDfsPath].bond_pos = -1; - nDfsPathPos[cur_at] = nLenDfsPath+1; /* mark with distance + 1 */ - nMinLenDfsPath ++; /* allow to restart from nStartAtom's neighbor */ - if ( nStartAtomNeighborNeighbor >= 0 ) { - nDoNotTouchAtom2 = (int)atom[cur_at].neighbor[nStartAtomNeighborNeighbor]; - } - } - - /* MAIN DFS CYCLE: may find one and the same t-group 2 times; saves only one instance */ - /* traverse *all* paths starting at atom[nStartAtom]; max. path length = (nCycleLen+1) */ - while ( nLenDfsPath >= nMinLenDfsPath ) { - j = ++DfsPath[nLenDfsPath].bond_pos; - if ( j < atom[cur_at=(int)DfsPath[nLenDfsPath].at_no].valence ) { - DfsPath[nLenDfsPath].bond_type = (atom[cur_at].bond_type[j] & ~BOND_MARK_ALL); -#if ( FIX_BOND23_IN_TAUT == 1 ) - DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER(pBNS,cur_at,j,DfsPath[nLenDfsPath].bond_type); -#endif - nxt_at = (int)atom[cur_at].neighbor[j]; - if ( nxt_at == nDoNotTouchAtom1 || /* forbidden */ - nxt_at == nDoNotTouchAtom2 || /* forbidden */ - nDfsPathPos[nxt_at] || /* ring closure */ - nLenDfsPath && nxt_at == (int)DfsPath[nLenDfsPath-1].at_no /* step backwards */ - ) { - ; /* ignore nxt_at */ - } else - if ( nLenDfsPath == nCycleLen && - /* 1,5 and at least one of the endpoints is not in a ring */ - (atom[nxt_at].nNumAtInRingSystem == 1 || atom[nStartAtom].nNumAtInRingSystem == 1) && - /* we have found the alt path of the requested length; check it */ - /* calling Check15TautPath() */ - (ret = (*CheckDfsPath)( atom, DfsPath, nLenDfsPath, j, nStartAtomNeighbor, - nStartAtomNeighbor2, nStartAtomNeighborNeighbor, - EndPoint, nMaxNumEndPoint, BondPos, nMaxNumBondPos, - pnNumEndPoint, pnNumBondPos, - pBNS, pBD, num_atoms ) ) ) { - if ( ret < 0 ) { - nNumFound = ret; - goto clear_path; /* program error */ - } - nNumFound += ret; /* success */ - } else /* calling Check15TautPathCenterpoint() */ - if ( !(*CheckCenterPointPath)( atom, DfsPath, nLenDfsPath, j, - pBNS, pBD, num_atoms ) ) { - ; /* cannot advance to a non-centerpoint; ignore */ - } else - if ( nLenDfsPath < nCycleLen ) { - /* advance */ - nLenDfsPath ++; - cur_at = nxt_at; - DfsPath[nLenDfsPath].at_no = cur_at; - DfsPath[nLenDfsPath].bond_type = 0; - DfsPath[nLenDfsPath].bond_pos = -1; - nDfsPathPos[cur_at] = nLenDfsPath+1; /* mark */ - } - } else { - /* retract */ - nDfsPathPos[(int)DfsPath[nLenDfsPath].at_no] = 0; - nLenDfsPath --; - } - } -clear_path: - while ( 0 <= nLenDfsPath ) { - nDfsPathPos[(int)DfsPath[nLenDfsPath].at_no] = 0; - nLenDfsPath --; - } - return nNumFound; -} -#endif /* TAUT_15_NON_RING */ -/******************************************* - * check if bonds are alternating */ -int are_alt_bonds( U_CHAR *bonds, int len ) -{ - U_CHAR next_bond; - int i, bAnyBond, bTautBondPresent=BOND_ALTERN; - if ( len < 2 || bonds[0] == BOND_TRIPLE || bonds[0] == BOND_ALT_13 ) { - return 0; - } - next_bond = bonds[0]==BOND_SINGLE? BOND_DOUBLE : bonds[0]==BOND_DOUBLE? BOND_SINGLE : 0; - if ( bonds[0] == BOND_TAUTOM ) { - bTautBondPresent= BOND_TAUTOM; - next_bond = 0; - } else { - next_bond = bonds[0]==BOND_SINGLE? BOND_DOUBLE : bonds[0]==BOND_DOUBLE? BOND_SINGLE : 0; - } - - for ( i = 1; i < len; i ++ ) { - if ( bonds[i] == BOND_TAUTOM ) { - bTautBondPresent = BOND_TAUTOM; - bAnyBond = 1; - } else { - bAnyBond = (bonds[i] == BOND_ALTERN || bonds[i] == BOND_ALT12NS); - } - if ( next_bond ) { - if ( bonds[i] == next_bond || bAnyBond ) { - next_bond = (next_bond == BOND_SINGLE)? BOND_DOUBLE : BOND_SINGLE; - continue; - } - return 0; - } else - if ( bonds[i] == BOND_SINGLE ) { - next_bond = BOND_DOUBLE; - continue; - } else - if ( bonds[i] == BOND_DOUBLE ) { - next_bond = BOND_SINGLE; - continue; - } else - if ( !bAnyBond ) { - return 0; - } - } - return !next_bond? bTautBondPresent : - (next_bond == BOND_SINGLE)? BOND_DOUBLE : BOND_SINGLE; /* bond to the end atom */ -} - -/********************************************************************************/ -int AddBondsPos( inp_ATOM *atom, T_BONDPOS *BondPosTmp, int nNumBondPosTmp, T_BONDPOS *BondPos, - int nMaxNumBondPos, int nNumBondPos ) -{ - int i, j, k, cur_at, nxt_at; - /* add opposite direction bonds to BondPosTmp */ - for ( j = 0; j < nNumBondPosTmp; j += 2 ) { - cur_at = BondPosTmp[j].nAtomNumber; - nxt_at = atom[cur_at].neighbor[(int)BondPosTmp[j].neighbor_index]; - for ( k = 0; k < atom[nxt_at].valence; k ++ ) { - if ( cur_at == atom[nxt_at].neighbor[k] ) { - BondPosTmp[j+1].nAtomNumber = nxt_at; - BondPosTmp[j+1].neighbor_index = k; - break; - } - } - } - /* add new tautomeric bonds */ - for ( j = 0; j < nNumBondPosTmp; j += 2 ) { - for ( i = 0; i < nNumBondPos; i ++ ) { - if ( BondPos[i].nAtomNumber == BondPosTmp[j].nAtomNumber && - BondPos[i].neighbor_index == BondPosTmp[j].neighbor_index || - BondPos[i].nAtomNumber == BondPosTmp[j+1].nAtomNumber && - BondPos[i].neighbor_index == BondPosTmp[j+1].neighbor_index ) { - break; /* bond has already been added */ - } - } - if ( i == nNumBondPos ) { - if ( i > nMaxNumBondPos ) { - return -1; /* overflow */ - } - BondPos[nNumBondPos ++] = BondPosTmp[j]; - } - } - return nNumBondPos; -} -/********************************************************************************/ -int AddEndPoints( T_ENDPOINT *EndPointTmp, int nNumNewEndPoint, T_ENDPOINT *EndPoint, - int nMaxNumEndPoint, int nNumEndPoint) -{ - int i, j; - /* add new endpoints */ - for ( j = 0; j < nNumNewEndPoint; j ++ ) { - for ( i = 0; i < nNumEndPoint; i ++ ) { - if ( EndPoint[i].nAtomNumber == EndPointTmp[j].nAtomNumber ) { - break; - } - } - if ( i == nNumEndPoint ) { - if ( i > nMaxNumEndPoint ) { - return -1; /* overflow */ - } - EndPoint[nNumEndPoint ++] = EndPointTmp[j]; - } - } - return nNumEndPoint; -} -/********************************************************************************/ -/* - - 1,4 tautomerism in 7-member ring - - /C==D //C-D A=DfsPath[0].at_no - O=B \ HO-B \\ B=DfsPath[1].at_no - | E <-> | E nStartAtomNeighbor2: from A to HO - HO-A // O=A / nStartAtomNeighborNeighbor: from B to O - \\G-F \\G-F - - - 1,4 tautomerism in 5-member ring - - - O=B--C O-B==C - | \\ | \ - | D <-> | D - | / | // - HO-A==E HO=A--E - -*/ -/********************************************************************************/ -int Check7MembTautRing( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int nStartAtomNeighbor, - int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ) -{ -#define PATH_LEN 8 - - int i, j, k, /*m,*/ nNumEndPoint, nNumEndPointTmp, nNumBondPos, nNumBondPosTmp; - int endpoint, /*nMobile, nMobile1, nMobile2,*/ o1_at, o2_at; - int ret; - U_CHAR path_bonds[PATH_LEN+1], bond_type; - T_ENDPOINT EndPointTmp[2]; - T_BONDPOS BondPosTmp[2*PATH_LEN]; - ENDPOINT_INFO eif1, eif2; - int nErr=0; - - - if ( nLenDfsPath + 2 > PATH_LEN ) { - return -1; /* too long path */ - } - if ( nLenDfsPath != 6 && nLenDfsPath != 4 ) { - return -1; /* wrong call */ - } - - - nNumBondPos = *pnNumBondPos; - nNumEndPoint = *pnNumEndPoint; - nNumBondPosTmp = 0; - nNumEndPointTmp = 0; - ret = 0; - - o1_at = atom[(int)DfsPath[1].at_no].neighbor[nStartAtomNeighborNeighbor]; - o2_at = atom[(int)DfsPath[0].at_no].neighbor[nStartAtomNeighbor2]; - /* - nMobile1 = (atom[o1_at].charge == -1) + atom[o1_at].num_H; - nMobile2 = (atom[o2_at].charge == -1) + atom[o2_at].num_H; - */ - if ( !nGetEndpointInfo( atom, o1_at, &eif1 ) || - !nGetEndpointInfo( atom, o2_at, &eif2 ) ) { - return 0; - } - - /* save endpoints */ - for ( j = 0; j < 2; j ++ ) { - endpoint = j? o2_at : o1_at; - if ( !atom[endpoint].endpoint ) { - AddAtom2num( EndPointTmp[nNumEndPointTmp].num, atom, endpoint, 2 ); /* fill out */ - AddAtom2DA( EndPointTmp[nNumEndPointTmp].num_DA, atom, endpoint, 2 ); - /* - nMobile = j? nMobile2 : nMobile1; - } else { - nMobile = 0; - } - if ( nMobile ) { - EndPointTmp[nNumEndPointTmp].num[1] = (atom[endpoint].charge == -1); - EndPointTmp[nNumEndPointTmp].num[0] = nMobile; - for ( m = 0; m < T_NUM_ISOTOPIC; m ++ ) { - EndPointTmp[nNumEndPointTmp].num[T_NUM_NO_ISOTOPIC+m] = atom[endpoint].num_iso_H[NUM_H_ISOTOPES-m-1]; - } - */ - } else { - memset( EndPointTmp + nNumEndPointTmp, 0, sizeof(EndPointTmp[0]) ); - } - EndPointTmp[nNumEndPointTmp].nAtomNumber = endpoint; - EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint; - EndPointTmp[nNumEndPointTmp].nEquNumber = 0; - nNumEndPointTmp ++; - } - - - /* extract bonds */ - k = (int)DfsPath[1].at_no; - bond_type = (atom[k].bond_type[nStartAtomNeighborNeighbor] & ~BOND_MARK_ALL); -#if ( FIX_BOND23_IN_TAUT == 1 ) - bond_type = ACTUAL_ORDER(pBNS,k,nStartAtomNeighborNeighbor,bond_type); -#endif - path_bonds[0] = bond_type; - if ( REPLACE_THE_BOND( bond_type ) ) { - BondPosTmp[nNumBondPosTmp].nAtomNumber = k; - BondPosTmp[nNumBondPosTmp].neighbor_index = nStartAtomNeighborNeighbor; - nNumBondPosTmp += 2; - } - for ( i = 1; i <= nLenDfsPath; i ++ ) { - bond_type = DfsPath[i].bond_type; - path_bonds[i] = bond_type; - if ( REPLACE_THE_BOND( bond_type ) ) { - BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[i].at_no; - BondPosTmp[nNumBondPosTmp].neighbor_index = DfsPath[i].bond_pos; - nNumBondPosTmp += 2; - } - } - bond_type = (atom[(int)DfsPath[0].at_no].bond_type[nStartAtomNeighbor2] & ~BOND_MARK_ALL); -#if ( FIX_BOND23_IN_TAUT == 1 ) - bond_type = ACTUAL_ORDER(pBNS,(int)DfsPath[0].at_no,nStartAtomNeighbor2,bond_type); -#endif - path_bonds[i++] = bond_type; - if ( REPLACE_THE_BOND( bond_type ) ) { - BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[0].at_no; - BondPosTmp[nNumBondPosTmp].neighbor_index = nStartAtomNeighbor2; - nNumBondPosTmp += 2; - } - - if ( !are_alt_bonds( path_bonds, i ) ) { - return 0; - } - - /* path_bonds is from at_n1 to at_n2 */ - if ( !(j=are_alt_bonds( path_bonds, i )) ) { - return 0; - } - /* j is a bond type of the last bond to o2_at, the first bond from o1_at is 2-j if j=1 or 2 */ - - /* single bond at o2_at: it should have a mobile atom, o1_at should not */ - if ( j == BOND_SINGLE && (!atom[o2_at].endpoint && !eif2.cDonor || !atom[o1_at].endpoint && !eif1.cAcceptor) || - /* double bond at o2_at: it should not have a mobile atom, o1_at should */ - j == BOND_DOUBLE && (!atom[o2_at].endpoint && !eif2.cAcceptor || !atom[o1_at].endpoint && !eif1.cDonor) ) { - return 0; /* bond pattern does not fit */ - } - - - nNumBondPos = AddBondsPos( atom, BondPosTmp, nNumBondPosTmp, BondPos, nMaxNumBondPos, nNumBondPos ); - nNumEndPoint = AddEndPoints( EndPointTmp, nNumEndPointTmp, EndPoint, nMaxNumEndPoint, nNumEndPoint); - - if ( nNumBondPos >= 0 && nNumEndPoint >= 0 ) { - if (ret = (nNumBondPos > *pnNumBondPos) || (nNumEndPoint > *pnNumEndPoint)) { - *pnNumBondPos = nNumBondPos ; - *pnNumEndPoint = nNumEndPoint ; - } - } - - if ( ret ) { - /* finally check whether the bonds allow moving the hydrogens */ - if ( (atom[o1_at].endpoint != atom[o2_at].endpoint || !atom[o1_at].endpoint) ) { - nErr = bExistsAnyAltPath( pBNS, pBD, atom, num_atoms, o1_at, o2_at, ALT_PATH_MODE_TAUTOM ); - if ( nErr <= 0 ) - return nErr; - } - } - - return ret; - - -#undef PATH_LEN -} - -/********************************************************************************/ -/* - 1,5 Tautomerism in 6-member alt ring: - - /=\ /==\ N = DfsPath[0].at_no - HN C=O <-> N C-OH C = DfsPath[3].at_no - \=/ \\-// - -*/ -/********************************************************************************/ -/* check if a tautomeric 6-member ring has been found */ -int Check6MembTautRing( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int nStartAtomNeighbor, - int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ) -{ -#define PATH_LEN 4 - int i, j, k, /*m,*/ nNumBondPos, nNumEndPoint; - int nNumEndPointTmp, nNumBondPosTmp, o_at, ret; - /* int num_taut_endpoints, num_H; */ - int middle_pos; - int nMobile, endpoint, endpoint_valence, chem_bonds_valence; - int nMobile1, endpoint_valence1; /* o_at */ - int nMobile2, endpoint_valence2; /* n_at */ - int nxt_at; - int n_at; - U_CHAR path_bonds[2][PATH_LEN+1], bond_type; - T_ENDPOINT EndPointTmp[2]; - T_BONDPOS BondPosTmp[4*PATH_LEN]; - ENDPOINT_INFO eif1, eif2; - - if ( nStartAtomNeighbor >= 0 || nStartAtomNeighbor2 >= 0 || nStartAtomNeighborNeighbor >= 0 ) - return -1; /* wrong call */ - - if ( nLenDfsPath != 5 ) - return -1; /* wrong call */ - - nNumBondPos = *pnNumBondPos; - nNumEndPoint = *pnNumEndPoint; - nNumBondPosTmp = 0; - nNumEndPointTmp = 0; - ret = 0; - - n_at = (int)DfsPath[0].at_no; /* -N= or -NH- atom */ - nxt_at = DfsPath[middle_pos = (nLenDfsPath+1)/2].at_no; /* must have tautomeric neighbor -OH or =O or -NH2 or =NH */ - - if ( atom[nxt_at].valence != 3 -#if ( TAUT_RINGS_ATTACH_CHAIN == 1 ) - || !atom[nxt_at].bCutVertex -#endif - ) { - return 0; - } - - for ( i = 0; i < atom[nxt_at].valence; i ++ ) { - o_at = atom[nxt_at].neighbor[i]; - if ( o_at != DfsPath[middle_pos-1].at_no && o_at != DfsPath[middle_pos+1].at_no ) { - break; /* >=O or />-OH has been found */ - } - } - if ( i == atom[nxt_at].valence ) { - return 0; /* no neighboring atom >=O or />-OH */ - } - bond_type = (atom[nxt_at].bond_type[i] & ~BOND_MARK_ALL); -#if ( FIX_BOND23_IN_TAUT == 1 ) - bond_type = ACTUAL_ORDER(pBNS,nxt_at,i,bond_type); -#endif - if ( bond_type != BOND_SINGLE && - bond_type != BOND_DOUBLE && - bond_type != BOND_TAUTOM && - bond_type != BOND_ALT12NS && - bond_type != BOND_ALTERN ) { - return 0; - } - - /* check whether the two atoms already belong to one tautomeric group */ -#if ( TAUT_IGNORE_EQL_ENDPOINTS == 1 ) - if ( atom[n_at].endpoint && atom[n_at].endpoint == atom[o_at].endpoint ) { - return 0; - } -#endif - /* check =O valence; must be 2 for O, S, Se or 3 for N */ - if ( !(endpoint_valence1=nGetEndpointInfo( atom, o_at, &eif1 )) ) - { - return 0; /* n_at has been checked in MarkTautomerGroups(...) */ - } -/* - if ( 2 != endpoint_valence1 ) - return 0; // accept only O, S, Se -*/ - /* check hydrogens/endpoints */ - nMobile1 = atom[o_at].num_H + (atom[o_at].charge==-1); - if ( bond_type == BOND_SINGLE && !eif1.cDonor && !atom[o_at].endpoint ) - return 0; - /* not needed since nGetEndpointInfo returned non-zero - if ( nMobile1 + atom[o_at].chem_bonds_valence != endpoint_valence1 ) - return 0; - */ - - if ( !(endpoint_valence2=nGetEndpointInfo( atom, n_at, &eif2 ) ) ) { - return 0; /* should not happen here */ - } - nMobile2 = atom[n_at].num_H + (atom[n_at].charge==-1); - - nMobile = 0; - - /* can mobile group move from o_at to n_at? */ - nMobile += (atom[o_at].endpoint || eif1.cDonor) && /* from o_at */ - bond_type != BOND_DOUBLE && - ( atom[n_at].endpoint || /* to n_at */ - eif2.cNeutralBondsValence > atom[n_at].valence ); - /* can mobile group move from n_at to o_at? */ - nMobile += (atom[n_at].endpoint || eif2.cDonor) && /* from n_at */ - (atom[o_at].endpoint || /* to o_at */ - eif1.cNeutralBondsValence > atom[o_at].valence ) && - bond_type != BOND_SINGLE; - - - if ( !nMobile ) - return 0; - /* - num_H = atom[n_at].num_H + atom[o_at].num_H; - num_taut_endpoints = (0!=atom[n_at].endpoint) + (0!=atom[o_at].endpoint); // if O, N already are endpoints - if ( num_H != 1 && num_taut_endpoints != 2 && !(num_H==2 && num_taut_endpoints >= 1) ) { - return 0; - } - */ - /* extract -OH bond */ - nNumBondPosTmp = 0; - - path_bonds[0][0] = path_bonds[1][0] = bond_type; - if ( REPLACE_THE_BOND( bond_type ) ) { - BondPosTmp[nNumBondPosTmp].nAtomNumber = nxt_at; /* accumulate bonds to be */ - BondPosTmp[nNumBondPosTmp].neighbor_index = i; /* marked as tautomeric */ - nNumBondPosTmp += 2; /* leave room for the same bond in the opposite direction */ - } - - /* extract other bonds */ - /* path_bonds[] contents: - - - O OH OH - || | | - / \ // \ / \\ - || || <--> | || <--> || | - \ / \\ / \ // - NH N N - - path[0]: O=NH-=- OH-N... OH.N... - path[1] O=NH-=- OH-N... OH.N... - bonds are all bonds all bonds - single and are either are either - double alt or taut alt or taut - */ - for ( j = 0; j < middle_pos; j ++ ) { - for ( i = 0; i < 2; i ++ ) { - /* k = i? j : middle_pos-1-j; */ - k = i? middle_pos+j : middle_pos-1-j; - /* i=0: from O neighbor i=0: down to N, i=1: up to N */ - bond_type = DfsPath[k].bond_type; - - path_bonds[i][j+1] = bond_type; - if ( REPLACE_THE_BOND( bond_type ) ) { - BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[k].at_no; /* accumulate bonds to be */ - BondPosTmp[nNumBondPosTmp].neighbor_index = DfsPath[k].bond_pos; /* marked as tautomeric */ - nNumBondPosTmp += 2; /* leave room for the same bond in the opposite direction */ - } - } - } - if ( !are_alt_bonds( path_bonds[0], middle_pos+1 ) || !are_alt_bonds( path_bonds[1], middle_pos+1 ) ) { - return 0; - } - - /* finally check whether the bonds allow moving the hydrogens */ - if ( (atom[o_at].endpoint != atom[n_at].endpoint || !atom[o_at].endpoint) ) { - int nErr; - nErr = bExistsAnyAltPath( pBNS, pBD, atom, num_atoms, n_at, o_at, ALT_PATH_MODE_TAUTOM ); - if ( nErr <= 0 ) - return nErr; - } - /* save endpoints */ - for ( j = 0; j < 2; j ++ ) { - endpoint = j? n_at : /* =N- 2 */ - o_at; /* -OH 1 */ - if ( !atom[endpoint].endpoint ) { /* not a known endpoint */ - endpoint_valence = j? endpoint_valence2 : endpoint_valence1; - chem_bonds_valence = j? eif2.cNeutralBondsValence : eif1.cNeutralBondsValence; - /* endpoint_valence = get_endpoint_valence( atom[endpoint].el_number ); */ - nMobile = j? nMobile2 : nMobile1; - /* nMobile = (atom[endpoint].charge == -1) + atom[endpoint].num_H; */ - /* if ( nMobile + atom[endpoint].chem_bonds_valence != endpoint_valence ) -- fixed 02-06-2003*/ - if ( nMobile + chem_bonds_valence != endpoint_valence ) - return 0; /* abnormal endpoint valence; ignore. */ - AddAtom2num( EndPointTmp[nNumEndPointTmp].num, atom, endpoint, 2 ); /* fill out */ - AddAtom2DA( EndPointTmp[nNumEndPointTmp].num_DA, atom, endpoint, 2 ); -/* - EndPointTmp[nNumEndPointTmp].num[1] = (atom[endpoint].charge == -1); - EndPointTmp[nNumEndPointTmp].num[0] = nMobile; - for ( m = 0; m < T_NUM_ISOTOPIC; m ++ ) { - EndPointTmp[nNumEndPointTmp].num[T_NUM_NO_ISOTOPIC+m] = atom[endpoint].num_iso_H[NUM_H_ISOTOPES-m-1]; - } -*/ - } else { /* already an endpoint */ /* **now it is wrong:** no mobile atom/charge at this endpoint */ - memset( EndPointTmp + nNumEndPointTmp, 0, sizeof(EndPointTmp[0]) ); - } - EndPointTmp[nNumEndPointTmp].nAtomNumber = endpoint; - EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint; - EndPointTmp[nNumEndPointTmp].nEquNumber = 0; - - nNumEndPointTmp ++; - } - /* add collected tautomeric bonds and endpoints to the input/output data */ - nNumBondPos = AddBondsPos( atom, BondPosTmp, nNumBondPosTmp, BondPos, nMaxNumBondPos, nNumBondPos ); - nNumEndPoint = AddEndPoints( EndPointTmp, nNumEndPointTmp, EndPoint, nMaxNumEndPoint, nNumEndPoint); - - if ( nNumBondPos >= 0 && nNumEndPoint >= 0 ) { - if (ret = (nNumBondPos > *pnNumBondPos) || (nNumEndPoint > *pnNumEndPoint)) { - *pnNumBondPos = nNumBondPos ; - *pnNumEndPoint = nNumEndPoint ; - } - } - return ret; - -#undef PATH_LEN -} -#if ( TAUT_15_NON_RING == 1 ) /* post v.1 feature */ -/******************************************************************************** -Check (1,5) taut alt path centerpoint (unfinished) [add path checking] -*********************************************************************************/ -int Check15TautPathCenterpoint( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int jNxtNeigh, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ) -{ - int nxt_at = atom[DfsPath[nLenDfsPath].at_no].neighbor[jNxtNeigh]; - /* atom[nxt_at].endpoint below allows for keto-enol -CH< or -CH2- endpoints */ - return atom[nxt_at].endpoint || bIsCenterPointStrict( atom, nxt_at ); -} - -/********************************************************************************/ -/* - 1,5 Tautomerism in general (unfinished) [just a copy from 6-memb case] - - AH--B==C--D==E C may be carbon exhibiting keto-enol tautomerism - 0 1 2 3 4 as well as A or E may be previously detected such a carbon - ^ nxt_at - | - +-- = nLenDfsPath - -*/ -/********************************************************************************/ -/* check if 1,5 tautomeric path has been found */ -int Check15TautPath( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int jNxtNeigh, int nStartAtomNeighbor, - int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ) -{ -#define PATH_LEN 4 - int i, j, k, /*m,*/ nNumBondPos, nNumEndPoint, cur_at, prv_at, at1, at2 /*, at3, step_at*/; - int nNumEndPointTmp, nNumBondPosTmp, ret; - /* int num_taut_endpoints, num_H; */ - int nMobile, endpoint, endpoint_valence, chem_bonds_valence; - int nMobile1, endpoint_valence1; /* start atom, at1 */ - int nMobile2, endpoint_valence2; /* end atom, at2 */ - /*int nMobile3, endpoint_valence3=-1;*/ /* middle atom, at3 */ - /*int nxt_at;*/ - int alt_bonds[2]; - U_CHAR /*path_bonds[2][PATH_LEN+1],*/ bond_type; - T_ENDPOINT EndPointTmp[2]; - T_BONDPOS BondPosTmp[4*PATH_LEN]; - ENDPOINT_INFO eif1, eif2/*, eif3*/; - - if ( nStartAtomNeighbor >= 0 || nStartAtomNeighbor2 >= 0 || nStartAtomNeighborNeighbor >= 0 ) - return -1; /* wrong call */ - - if ( nLenDfsPath != 3 ) - return -1; /* wrong call */ - - nNumBondPos = *pnNumBondPos; - nNumEndPoint = *pnNumEndPoint; - nNumBondPosTmp = 0; - nNumEndPointTmp = 0; - ret = 0; - -/*-------add the last atom, nLenDfsPath=4 --*/ - j = jNxtNeigh; - prv_at = DfsPath[nLenDfsPath].at_no; - cur_at = atom[prv_at].neighbor[j]; - DfsPath[nLenDfsPath].bond_type = (atom[prv_at].bond_type[j] & ~BOND_MARK_ALL); -#if ( FIX_BOND23_IN_TAUT == 1 ) - DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER(pBNS,prv_at,j,DfsPath[nLenDfsPath].bond_type); -#endif - DfsPath[nLenDfsPath].bond_pos = j; /* fix index of the bond to the next atom */ - - nLenDfsPath ++; - - DfsPath[nLenDfsPath].at_no = cur_at; - DfsPath[nLenDfsPath].bond_type = 0; - DfsPath[nLenDfsPath].bond_pos = -1; - /*nDfsPathPos[cur_at] = nLenDfsPath+1;*/ /* mark with distance + 1 */ -/*------------------------------------------*/ - at1 = (int)DfsPath[0].at_no; - at2 = (int)DfsPath[nLenDfsPath].at_no; - /*at3 = (int)DfsPath[2].at_no;*/ - if ( atom[at1].endpoint && atom[at1].endpoint == atom[at2].endpoint ) { - /* start & end already belong to the same taut group */ - goto exit_function; /* nothing to do */ - } - - /* check bond types along alt path */ - alt_bonds[0] = alt_bonds[1] = 0; - for( i = 0; i < nLenDfsPath; i ++ ) { - alt_bonds[i%2] |= IS_ALT_OR_DBLBOND(DfsPath[i].bond_type); - } - if ( (alt_bonds[0] & alt_bonds[1] & (BOND_SINGLE | BOND_DOUBLE)) || - (alt_bonds[0] & BOND_WRONG) || (alt_bonds[1] & BOND_WRONG ) ) { - goto exit_function; /* incompatible with alt path or wrong bonds */\ - } - /* check possibly tautomeric endpoints at the ends */ - endpoint_valence1 = nGetEndpointInfo( atom, at1, &eif1 ); - endpoint_valence2 = nGetEndpointInfo( atom, at2, &eif2 ); -#ifdef NEVER /* do not use C-endpoint of keto-enol tautomer to find 1,5 the taut path */ - if ( !endpoint_valence1 && !atom[at1].endpoint || - !endpoint_valence2 && !atom[at2].endpoint ) - goto exit_function; /* at least one of the end atoms cannot be an endpoint */ -#endif - if ( !endpoint_valence1 || !endpoint_valence2 ) - goto exit_function; /* require both endpoints be heteroatoms */ - /* check hydrogens/endpoints */ - nMobile1 = atom[at1].num_H + (atom[at1].charge==-1); - if ( !atom[at1].endpoint ) { - if ( (alt_bonds[0] & BOND_SINGLE) && !eif1.cDonor ) - goto exit_function; - if ( (alt_bonds[0] & BOND_DOUBLE) && !eif1.cAcceptor ) - goto exit_function; - } - nMobile2 = atom[at2].num_H + (atom[at2].charge==-1); - if ( !atom[at2].endpoint ) { - if ( (alt_bonds[1] & BOND_SINGLE) && !eif2.cDonor ) - goto exit_function; - if ( (alt_bonds[1] & BOND_DOUBLE) && !eif2.cAcceptor ) - goto exit_function; - } - - nMobile = 0; - - /* can mobile group move from at1=o_at to at2=n_at? */ - nMobile += (atom[at1].endpoint || eif1.cDonor) && /* from o_at */ - !(alt_bonds[0] & BOND_DOUBLE) && - ( atom[at2].endpoint || /* to n_at */ - eif2.cNeutralBondsValence > atom[at2].valence ); - /* can mobile group move from at2=n_at to at1=o_at? */ - nMobile += (atom[at2].endpoint || eif2.cDonor) && /* from n_at */ - !(alt_bonds[1] & BOND_DOUBLE) && - ( atom[at1].endpoint || /* to o_at */ - eif1.cNeutralBondsValence > atom[at1].valence ); - - - if ( !nMobile ) - goto exit_function; - - /* check whether the bonds allow moving the hydrogens between at1 and at2 */ - if ( (atom[at1].endpoint != atom[at2].endpoint || !atom[at1].endpoint) ) { - int nErr; - nErr = bExistsAnyAltPath( pBNS, pBD, atom, num_atoms, at1, at2, ALT_PATH_MODE_TAUTOM ); - if ( nErr <= 0 ) { - ret = nErr; - goto exit_function; - } - } - - /* save tautomeric bonds */ - nNumBondPosTmp = 0; - for ( k = 0; k < nLenDfsPath; k ++ ) { - bond_type = DfsPath[k].bond_type; - if ( REPLACE_THE_BOND( bond_type ) ) { - BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[k].at_no; /* accumulate bonds to be */ - BondPosTmp[nNumBondPosTmp].neighbor_index = DfsPath[k].bond_pos; /* marked as tautomeric */ - nNumBondPosTmp += 2; /* leave room for the same bond in opposite direction */ - } - } - /* save endpoints */ - for ( j = 0; j < 2; j ++ ) { - endpoint = j? at2 : at1; - if ( !atom[endpoint].endpoint ) { /* not a known endpoint */ - endpoint_valence = j? endpoint_valence2 : endpoint_valence1; - chem_bonds_valence = j? eif2.cNeutralBondsValence : eif1.cNeutralBondsValence; - /* endpoint_valence = get_endpoint_valence( atom[endpoint].el_number ); */ - nMobile = j? nMobile2 : nMobile1; - /* nMobile = (atom[endpoint].charge == -1) + atom[endpoint].num_H; */ - /* if ( nMobile + atom[endpoint].chem_bonds_valence != endpoint_valence ) -- fixed 02-06-2003*/ - if ( nMobile + chem_bonds_valence != endpoint_valence ) - goto exit_function; /* abnormal endpoint valence; ignore. */ - AddAtom2num( EndPointTmp[nNumEndPointTmp].num, atom, endpoint, 2 ); /* fill out */ - AddAtom2DA( EndPointTmp[nNumEndPointTmp].num_DA, atom, endpoint, 2 ); - } else { /* already an endpoint */ /* **now it is wrong:** no mobile atom/charge at this endpoint */ - memset( EndPointTmp + nNumEndPointTmp, 0, sizeof(EndPointTmp[0]) ); - } - EndPointTmp[nNumEndPointTmp].nAtomNumber = endpoint; - EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint; - EndPointTmp[nNumEndPointTmp].nEquNumber = 0; - - nNumEndPointTmp ++; - } - /* add collected tautomeric bonds and endpoints to the input/output data */ - nNumBondPos = AddBondsPos( atom, BondPosTmp, nNumBondPosTmp, BondPos, nMaxNumBondPos, nNumBondPos ); - nNumEndPoint = AddEndPoints( EndPointTmp, nNumEndPointTmp, EndPoint, nMaxNumEndPoint, nNumEndPoint); - - if ( nNumBondPos >= 0 && nNumEndPoint >= 0 ) { - if (ret = (nNumBondPos > *pnNumBondPos) || (nNumEndPoint > *pnNumEndPoint)) { - *pnNumBondPos = nNumBondPos ; - *pnNumEndPoint = nNumEndPoint ; - } - } - -exit_function: - /*nDfsPathPos[DfsPath[nLenDfsPath].at_no] = 0;*/ - - return ret; - -#undef PATH_LEN -} -#endif /* TAUT_15_NON_RING */ - -/********************************************************************************/ -/* - - 1,4 tautomerism in 5-member ring - - - O=N2-C O-N2=C N1 = DfsPath[0].at_no - | \\ | \ N2 = DfsPath[1].at_no - | D <-> | D - | / | // - HO-N1=E HO=N1-E - -*/ -/********************************************************************************/ -/* check if a tautomeric 5-member ring (pyrazole derivatives) has been found */ -int Check5MembTautRing( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int nStartAtomNeighbor, - int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ) -{ -#define PATH_LEN 4 - int i, j, /*m,*/ nMobile, nMobile1, nMobile2; - int num_taut_endpoints, nNumBondPos, nNumBondPosTmp, nNumEndPoint, nNumEndPointTmp, ret; - int endpoint; - int n1_at = (int)DfsPath[0].at_no; - int n2_at = (int)DfsPath[1].at_no; - U_CHAR path_bonds[PATH_LEN+1], bond_type; - T_ENDPOINT EndPointTmp[2]; - T_BONDPOS BondPosTmp[2*PATH_LEN]; - ENDPOINT_INFO eif1, eif2; - - /* the two root atoms (atom[n1_at] and atom[n2_at]) cannot belong */ - /* to one and the same tautomeric group: it has been verified in MarkTautomerGroups() */ - - /* check hydrogens/endpoints */ - if ( nLenDfsPath != 4 ) { - return 0; /* program error */ - } - if ( nStartAtomNeighbor2 >= 0 || nStartAtomNeighborNeighbor >= 0 ) - return 0; /* program error: wrong call */ - - nNumBondPos = *pnNumBondPos; - nNumEndPoint = *pnNumEndPoint; - nNumEndPointTmp = 0; - nNumBondPosTmp = 0; - ret = 0; - - if ( !nGetEndpointInfo( atom, n1_at, &eif1 ) || - !nGetEndpointInfo( atom, n2_at, &eif2 ) ) { - return 0; - } - - nMobile1 = atom[n1_at].num_H + (atom[n1_at].charge==-1); - nMobile2 = atom[n2_at].num_H + (atom[n2_at].charge==-1); - nMobile = nMobile1 + nMobile2; - num_taut_endpoints = (0!=atom[n1_at].endpoint) + (0!=atom[n2_at].endpoint); /* if both N atoms already are endpoints */ - /* - if ( !(nMobile == 1 || num_taut_endpoints == 2) && !(nMobile>1 && num_taut_endpoints >= 1) ) { - return 0; - } - */ - if ( num_taut_endpoints == 0 && nMobile != 1 ) { - return 0; - } - - /* finally check whether the bonds allow moving the hydrogens */ - if ( (atom[n1_at].endpoint != atom[n2_at].endpoint || !atom[n1_at].endpoint) ) { - int nErr; - nErr = bExistsAnyAltPath( pBNS, pBD, atom, num_atoms, n1_at, n2_at, ALT_PATH_MODE_TAUTOM ); - if ( nErr <= 0 ) - return nErr; - } - - /* save endpoints */ - for ( j = 0; j < 2; j ++ ) { - endpoint = j? n1_at : n2_at; - if ( !atom[endpoint].endpoint ) { /* not a known endpoint */ -/* - nMobile = (atom[endpoint].charge == -1) + atom[endpoint].num_H; - } else { - nMobile = 0; - } - if ( nMobile ) { -*/ - AddAtom2num( EndPointTmp[nNumEndPointTmp].num, atom, endpoint, 2 ); /* fill out */ - AddAtom2DA( EndPointTmp[nNumEndPointTmp].num_DA, atom, endpoint, 2 ); - /* - EndPointTmp[nNumEndPointTmp].num[1] = (atom[endpoint].charge == -1); - EndPointTmp[nNumEndPointTmp].num[0] = nMobile; - for ( m = 0; m < T_NUM_ISOTOPIC; m ++ ) { - EndPointTmp[nNumEndPointTmp].num[T_NUM_NO_ISOTOPIC+m] = atom[endpoint].num_iso_H[NUM_H_ISOTOPES-m-1]; - } - */ - } else { - memset( EndPointTmp + nNumEndPointTmp, 0, sizeof(EndPointTmp[0]) ); - } - EndPointTmp[nNumEndPointTmp].nAtomNumber = endpoint; - EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint; - EndPointTmp[nNumEndPointTmp].nEquNumber = 0; - - nNumEndPointTmp ++; - } - - /* extract bonds */ - nNumBondPosTmp = 0; - for ( i = 1; i <= nLenDfsPath; i ++ ) { - bond_type = DfsPath[i].bond_type; - path_bonds[i-1] = bond_type; - if ( REPLACE_THE_BOND( bond_type ) ) { - BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[i].at_no; - BondPosTmp[nNumBondPosTmp].neighbor_index = DfsPath[i].bond_pos; - nNumBondPosTmp += 2; - } - } - /* path_bonds is from at_n2 to at_n1 */ - if ( !(i=are_alt_bonds( path_bonds, nLenDfsPath )) ) { - return 0; - } - /* i is a bond type of the last bond to at_n1, the first bond from at_n2 is 2-i if i=1 or 2 */ - - /* single bond at n1_at: it should have a mobile atom, n2_at should not */ - if ( i == BOND_SINGLE && (!atom[n1_at].endpoint && !eif1.cDonor || !atom[n2_at].endpoint && !eif2.cAcceptor ) || - /* double bond at n1_at: it should not have a mobile atom, n2_at should */ - i == BOND_DOUBLE && (!atom[n1_at].endpoint && !eif1.cAcceptor || !atom[n2_at].endpoint && !eif2.cDonor) ) { - return 0; /* bond pattern does not fit */ - } - - nNumBondPos = AddBondsPos( atom, BondPosTmp, nNumBondPosTmp, BondPos, nMaxNumBondPos, nNumBondPos ); - nNumEndPoint = AddEndPoints( EndPointTmp, nNumEndPointTmp, EndPoint, nMaxNumEndPoint, nNumEndPoint); - - if ( nNumBondPos >= 0 && nNumEndPoint >= 0 ) { - if (ret = (nNumBondPos > *pnNumBondPos) || (nNumEndPoint > *pnNumEndPoint)) { - *pnNumBondPos = nNumBondPos ; - *pnNumEndPoint = nNumEndPoint ; - } - } - return ret; - -#undef PATH_LEN -} -#endif /* } */ diff --git a/INCHI-1-SRC/INCHI/common/ichirvr4.c b/INCHI-1-SRC/INCHI/common/ichirvr4.c deleted file mode 100644 index bdb7256..0000000 --- a/INCHI-1-SRC/INCHI/common/ichirvr4.c +++ /dev/null @@ -1,3218 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - -/*^^^ */ -/*#define CHECK_WIN32_VC_HEAP*/ -#include "mode.h" - -#if ( READ_INCHI_STRING == 1 ) - -#include "ichi.h" -#include "ichitime.h" - -#include "inpdef.h" -#include "ichimain.h" -#include "ichierr.h" -#include "incomdef.h" -#include "ichiring.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "util.h" - -#include "ichicomp.h" -#include "ichister.h" - -#include "ichi_bns.h" - -#include "strutil.h" - -#include "ichirvrs.h" -/*^^^ */ - -/********************** Forbid carbon charge edges ***********************************/ -int ForbidCarbonChargeEdges( BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups, EDGE_LIST *pCarbonChargeEdges, int forbidden_edge_mask ) -{ -#define MAX_NUM_CARBON_CHARGE_EDGES 2 - int nType, i, k, ret; - BNS_EDGE *pEdge; - if ( ret = AllocEdgeList( pCarbonChargeEdges, MAX_NUM_CARBON_CHARGE_EDGES ) ) { - goto exit_function; - } - pCarbonChargeEdges->num_edges = 0; - for ( i = 0; i < MAX_NUM_CARBON_CHARGE_EDGES; i ++ ) { - switch( i ) { - case 0: - nType = TCG_Plus_C0; - break; - case 1: - nType = TCG_Minus_C0; - break; - default: - ret = RI_ERR_PROGR; - goto exit_function; - } - if ( (k = pTCGroups->nGroup[nType]) >= 0 ) { - k = pTCGroups->pTCG[k].nForwardEdge; - if ( k > 0 ) { - pEdge = pBNS->edge + k; - if ( !(pEdge->forbidden & forbidden_edge_mask) ) { - pEdge->forbidden |= forbidden_edge_mask; - if ( ret = AddToEdgeList( pCarbonChargeEdges, k, 0 ) ) { - goto exit_function; - } - } - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - } - } - ret = pCarbonChargeEdges->num_edges; -exit_function: - return ret; -#undef MAX_NUM_CARBON_CHARGE_EDGES -} -/******************************************************************************************************/ -int ForbidNintrogenPlus2BondsInSmallRings( BN_STRUCT *pBNS, inp_ATOM *at, int num_at, - VAL_AT *pVA, int min_ring_size, ALL_TC_GROUPS *pTCGroups, - EDGE_LIST *pNplus2BondsEdges, int forbidden_edge_mask ) -{ - int i, j, ret; - BNS_EDGE *e; - - ret = 0; - /* --- forbid edges that allow to make =N(+)= or #N(+)- in small ring */ - for ( i = 0; i < num_at; i ++ ) { - if ( at[i].valence == 2 && - !at[i].num_H && !at[i].endpoint && - pVA[i].cNumValenceElectrons == 5 && - pVA[i].cPeriodicRowNumber == 1 && - !pVA[i].cMaxFlowToMetal && pVA[i].nCPlusGroupEdge > 0 && - pVA[i].cnListIndex > 0 && cnList[pVA[i].cnListIndex-1].bits == cn_bits_MNP && - pVA[i].cMinRingSize && pVA[i].cMinRingSize <= min_ring_size ) { - - e = pBNS->edge + (j = pVA[i].nCPlusGroupEdge - 1); - if ( !(e->forbidden & forbidden_edge_mask) ) { - e->forbidden |= forbidden_edge_mask; - if ( ret = AddToEdgeList( pNplus2BondsEdges, j, 128 ) ) { - goto exit_function; - } - } - } - } - ret = 0; -exit_function: - return ret; -} - -/************************************************************************************************* -Problem: Formula in InChI from the reversed structure has less H than in the input InChI -Solutions: - -(a) | | - -B(-)-NH-=..-=N(+)< => -B(-)-NH(+)=-..=-N< (H is not removed from the ion pair) - | | - - | | -(b) >N(+)=-=...-=N-NH => >N-=-...=-N(+)-NH (charge from onium cannot be moved to remove H+) - | | -*************************************************************************************************/ -int FixLessHydrogenInFormula( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, - inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ) -{ - int iBPlus=NO_VERTEX, iNV=NO_VERTEX, iNH = NO_VERTEX, neigh; - EDGE_LIST NewlyFixedEdges; - int ret, i, j; - int num_at = pStruct->num_atoms; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - /* for RunBnsTestOnce */ - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - - AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR ); - if ( ret = AllocEdgeList( &NewlyFixedEdges, 2*num_at ) ) { - goto exit_function; - } - for ( i = 0; i < num_at; i ++ ) { - if ( (j = pVA[i].nCMinusGroupEdge-1) >= 0 ) { - if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) { - goto exit_function; - } - pBNS->edge[j].forbidden |= forbidden_edge_mask; - } - if ( (j = pVA[i].nCPlusGroupEdge-1) >= 0 ) { - if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) { - goto exit_function; - } - pBNS->edge[j].forbidden |= forbidden_edge_mask; - } - } - /* extra H has been removed; check non-tautomeric atoms */ - for ( i = 0; i < num_at; i ++ ) { - if ( !at2[i].endpoint && !pVA[i].cMetal && - pVA[i].cNumValenceElectrons == 5 && pVA[i].cPeriodicRowNumber == 1 && - at2[i].num_H == atf[i].num_H + 1) { - /* H was removed from N */ - iNH = i; - break; - } - } - if ( 0 <= iNH && iNH < num_at ) { - /* check neighbors for | | - (a) -B(+)- or (b) =N- - | | - */ - for ( j = 0; j < at2[i].valence; j ++ ) { - neigh = at2[iNH].neighbor[j]; - if ( at2[neigh].valence == 4 ) { - if ( at2[neigh].charge == -1 && at2[neigh].chem_bonds_valence == 4 && - !at2[neigh].radical && !at[neigh].num_H ) { - iBPlus = neigh; - } - } - } - } - if ( 0 <= iNH && iNH < num_at ) { - int bond_type_at2; - int bond_type_atf; - int num_bonds_in_path = 0; - int delta = -1, nxt = iNH, prv = NO_VERTEX, nxt_is_NPlus; - /* the changed bond to the dehydrogenated atom H should have greater order */ - /* delta = (new bond order in atf[]) - (restored bond order in at2[]) */ - nxt_is_NPlus = 0; - do { - i = nxt; - nxt = NO_VERTEX; - delta = -delta; - for ( j = 0; j < at2[i].valence; j ++ ) { - bond_type_at2 = at2[i].bond_type[j] & BOND_TYPE_MASK; /* restored bond */ - bond_type_atf = atf[i].bond_type[j] & BOND_TYPE_MASK; /* normalized bond */ - nxt_is_NPlus = 0; - if ( (bond_type_atf - bond_type_at2 == delta || bond_type_atf == BOND_ALT12NS) && - BOND_TYPE_SINGLE <= bond_type_at2 + delta && bond_type_at2 + delta <= BOND_TYPE_TRIPLE && - !at2[(int)at2[i].neighbor[j]].cFlags ) { - prv = i; - nxt = at2[i].neighbor[j]; - nxt_is_NPlus = at2[nxt].charge == 1 && atf[nxt].charge == 0 && - pVA[nxt].cNumValenceElectrons == 5 && pVA[nxt].cPeriodicRowNumber == 1; - at2[i].cFlags |= 1; /* avoid cycling */ - num_bonds_in_path ++; - if ( delta == -1 && at2[prv].valence == 4 && at2[prv].chem_bonds_valence == 5 && - !at2[prv].charge && !at2[prv].radical && pVA[prv].cNumValenceElectrons == 5 && - pVA[prv].nCPlusGroupEdge > 0 ) { - iNV = prv; - } - if ( at2[nxt].charge != atf[nxt].charge ) { - if ( (at2[nxt].charge == 1 || atf[nxt].charge == 1) && - pVA[nxt].nCPlusGroupEdge > 0 ) { - pBNS->edge[pVA[nxt].nCPlusGroupEdge-1].forbidden &= inv_forbidden_edge_mask; - } - if ( (at2[nxt].charge == -1 || atf[nxt].charge == -1) && - pVA[nxt].nCMinusGroupEdge > 0 ) { - pBNS->edge[pVA[nxt].nCMinusGroupEdge-1].forbidden &= inv_forbidden_edge_mask; - } - } - break; /* found */ - } - } - } while ( nxt >= 0 && !( nxt_is_NPlus && delta == -1 ) ); - for ( i = 0; i < num_at; i ++ ) { - at2[i].cFlags = 0; - } - if ( nxt >= 0 && nxt_is_NPlus && delta == -1 ) { - /* a simple alt path from NH-= to =N(+) has been found */ - if ( iBPlus || iNV ) { - /* move (+) charge from N(+) to iNV or, if iBPlus, then to iNH */ - if ( iNV >= 0 && (j = pVA[iNV].nCPlusGroupEdge-1) > 0 && pBNS->edge[j].flow > 0 || - iNH >= 0 && (j = pVA[iNH].nCPlusGroupEdge-1) > 0 && pBNS->edge[j].flow > 0 ) { - int ieFlower; - BNS_EDGE *pe = pBNS->edge + j, *peFlower = NULL; - Vertex v1 = pe->neighbor1; - Vertex v2 = v1 ^ pe->neighbor12; - BNS_VERTEX *pv1 = pBNS->vert + v1; - BNS_VERTEX *pv2 = pBNS->vert + v2; - - delta = 1; - /* prevent conversion of >N(+)= into N(V) neutral */ - ieFlower = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[nxt].nCPlusGroupEdge-1 ); - if ( ieFlower >= 0 ) { - peFlower = pBNS->edge + ieFlower; - if ( peFlower->flow == delta ) { - peFlower->forbidden |= forbidden_edge_mask; - if ( ret = AddToEdgeList( &NewlyFixedEdges, ieFlower, 0 )) { - goto exit_function; - } - } - } - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && - nDeltaCharge <= 0 /* charge moving to this atom disappers*/ ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else - if ( ret == 1 ) { - *pnTotalDelta += ret; - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - } else { - ret = 0; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - - } - - } - } - } -exit_function: - /* remove bond fixation */ - RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask ); - AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE ); - return ret; -} -/*********************************************************************************************** - - - X=Y-O(-) => X(-)-Y=O - - -************************************************************************************************/ -int FixMoreHydrogenInFormula( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, - inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ) -{ - int iNH = NO_VERTEX, neigh, neigh2; - EDGE_LIST NewlyFixedEdges; - int ret, i, j, k, k2, delta; - int num_at = pStruct->num_atoms; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - Vertex v1, v2; - /* for RunBnsTestOnce */ - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - BNS_EDGE *pe, *pe2; - - AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR ); - if ( ret = AllocEdgeList( &NewlyFixedEdges, 2*num_at ) ) { - goto exit_function; - } - /* fix all charges */ - for ( i = 0; i < num_at; i ++ ) { - if ( (j = pVA[i].nCMinusGroupEdge-1) >= 0 ) { - if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) { - goto exit_function; - } - pBNS->edge[j].forbidden |= forbidden_edge_mask; - } - if ( (j = pVA[i].nCPlusGroupEdge-1) >= 0 ) { - if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) { - goto exit_function; - } - pBNS->edge[j].forbidden |= forbidden_edge_mask; - } - } - - /* H(+) has been added to -O(-); check non-tautomeric atoms */ - for ( i = 0; i < num_at; i ++ ) { - if ( !(pStruct->bMobileH? at2[i].endpoint : pStruct->endpoint[i]) && !pVA[i].cMetal && - at2[i].num_H + 1 == atf[i].num_H && /* normalization added H ??? What would happen in Fixed-H case?*/ - (k = pVA[i].nCMinusGroupEdge-1) >= 0 && - pBNS->edge[k].flow == 1 && /* atom had (-) charge before preprocessing */ - at2[i].charge == -1 && atf[i].charge == 0 && /* and has no charge after preprocessing */ - at2[i].valence == 1 && at2[i].chem_bonds_valence == 1 && /* connected by a single bond */ - pVA[i].cNumValenceElectrons == 6 && /* atom is O, S, Se, Te */ - at2[neigh=at2[i].neighbor[0]].chem_bonds_valence > at2[neigh].valence - /* atom's single neighbor has multiple bond(s)*/ - ) { - /* H(+) was added to O in Y=X-O(-), where X is the only neighbor of O, X=neigh, Y=neigh2 */ - iNH = i; - for ( j = 0; j < at2[neigh].valence; j ++ ) { - neigh2 = at2[neigh].neighbor[j]; - if ( neigh2 != iNH && !at2[neigh2].endpoint && - !pBNS->edge[(int)pBNS->vert[neigh].iedge[j]].forbidden && - 4 <= pVA[neigh2].cNumValenceElectrons && - pVA[neigh2].cNumValenceElectrons <= 5 && /* neig2 is C or N */ - (k2 = pVA[neigh2].nCMinusGroupEdge-1) >= 0 && - !pBNS->edge[k2].flow /* negative charge may be moved to neigh2 */ ) { - break; - } - } - if ( j < at2[neigh].valence ) { - delta = 1; - pe = pBNS->edge + k; /* -O(-) negative charge edge; flow = 1 */ - pe2 = pBNS->edge + k2; /* X charge edge; flow = 0 */ - v1 = pe->neighbor1; - v2 = pe->neighbor12 ^ v1; - pe->flow -= delta; - pBNS->vert[v1].st_edge.flow -= delta; - pBNS->vert[v2].st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - pe2->forbidden &= inv_forbidden_edge_mask; /* allow the charge to move */ - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && - nDeltaCharge <= 1 ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else - if ( ret ) { - *pnTotalDelta += ret; - } else { - ret = RI_ERR_PROGR; - } - break; - } else { - /* the attempt has failed; restore the flow */ - ret = 0; - pe->flow += delta; - pBNS->vert[v1].st_edge.flow += delta; - pBNS->vert[v2].st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - } - } - } -exit_function: - /* remove bond fixation */ - RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask ); - AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE ); - return ret; -} -#if ( FIX_ADD_PROTON_FOR_ADP == 1 ) -/******************************************************************************************************/ -int FixAddProtonForADP( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, - inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, ICR *picr, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ) -{ - int iBPlus=NO_VERTEX, iNV=NO_VERTEX, iNH = NO_VERTEX, neigh, neigh2; - EDGE_LIST NewlyFixedEdges; - int ret, i, j, k, k2, delta; - int num_at = pStruct->num_atoms; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - Vertex v1, v2; - /* for RunBnsTestOnce */ - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - BNS_EDGE *pe, *pe2; - - ret = 0; - /* - AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR ); - - for ( i = 0; i < num_at; i ++ ) { - if ( at2[i].radical == RADICAL_DOUBLET && at2[i].endpoint ) { - pStruct->bExtract |= EXTRACT_STRUCT_NUMBER; - ret = 1; - break; - } - } - */ - return ret; -} -#endif -/****************************************************************************************************** - OH OH - / / - -NH => -NH(+) to eliminate false tautomerism. S(IV) or N(V) or P(V) may be a centerpoint - \\ \ - O O(-) -*******************************************************************************************************/ -int FixRemoveExtraTautEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, - inp_ATOM *at2, inp_ATOM *atf, inp_ATOM *atn, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, ICR *picr, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ) -{ - EDGE_LIST NewlyFixedEdges; - int ret, i, j, k, delta, centerpoint, endpoint1, endpoint2; - int num_at = pStruct->num_atoms; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - Vertex v1, v2; - /* for RunBnsTestOnce */ - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - BNS_EDGE *pe, *pe2; - - ret = 0; - - AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR ); - if ( ret = AllocEdgeList( &NewlyFixedEdges, 2*num_at ) ) { - goto exit_function; - } - /* fix all charges */ - for ( i = 0; i < num_at; i ++ ) { - if ( (j = pVA[i].nCMinusGroupEdge-1) >= 0 ) { - if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) { - goto exit_function; - } - pBNS->edge[j].forbidden |= forbidden_edge_mask; - } - if ( (j = pVA[i].nCPlusGroupEdge-1) >= 0 ) { - if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) { - goto exit_function; - } - pBNS->edge[j].forbidden |= forbidden_edge_mask; - } - } - - for ( i = 0; i < picr->num_endp_in1_only; i ++ ) { - endpoint1 = picr->endp_in1_only[i]-1; - if ( at2[endpoint1].valence == at2[endpoint1].chem_bonds_valence || - pVA[endpoint1].nCMinusGroupEdge <= 0 ) { - continue; - } - /* find centerpoint */ - for ( j = 0; j < at2[endpoint1].valence; j ++ ) { - if ( BOND_TYPE_DOUBLE == ( BOND_TYPE_MASK & at2[endpoint1].bond_type[j] ) ) { - centerpoint = at2[endpoint1].neighbor[j]; - if ( at2[centerpoint].charge || pVA[centerpoint].nCPlusGroupEdge <= 0 || - !is_centerpoint_elem( at2[centerpoint].el_number ) ) { - continue; - } - /* -- the centerpoint as depicted has no ChargeStruct flower --- - m = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[centerpoint].nCPlusGroupEdge-1 ); - if ( m < 0 || pBNS->edge[m].flow ) { - continue; - } - */ - /* find 2nd endpoint */ - for ( k = 0; k < at2[centerpoint].valence; k ++ ) { - if ( BOND_TYPE_SINGLE != ( BOND_TYPE_MASK & at2[centerpoint].bond_type[k] ) ) { - continue; - } - endpoint2 = at2[centerpoint].neighbor[k]; - if ( !at2[endpoint2].endpoint && atn[endpoint2].endpoint ) { - break; - } - } - if ( k == at2[centerpoint].valence ) { - continue; - } - /* the centerpoint and two extra endpoints have been found */ - pe = pBNS->edge + pVA[centerpoint].nCPlusGroupEdge - 1; - if ( !pe->flow ) { - continue; - } - pe2 = pBNS->edge + pVA[endpoint1].nCMinusGroupEdge - 1; - if ( pe2->flow ) { - continue; - } - delta = 1; - v1 = pe->neighbor1; - v2 = pe->neighbor12 ^ v1; - pe->flow -= delta; - pBNS->vert[v1].st_edge.flow -= delta; - pBNS->vert[v2].st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - pe2->forbidden &= inv_forbidden_edge_mask; /* allow the charge to move */ - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && - nDeltaCharge <= 1 ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else - if ( ret ) { - *pnTotalDelta += ret; - } else { - ret = RI_ERR_PROGR; - } - goto exit_function; - } else { - ret = 0; - pe->flow += delta; - pBNS->vert[v1].st_edge.flow += delta; - pBNS->vert[v2].st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - pe2->forbidden |= forbidden_edge_mask; - } - } - } - } - -exit_function: - /* remove bond fixation */ - RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask ); - AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE ); - return ret; -} -/*************************************************************************/ -int FillOutExtraFixedHDataRestr( StrFromINChI *pStruct ) -{ - int i, j, k, len, ret = 0; - AT_NUMB *pNum; - for ( i = 0; i < TAUT_NUM; i ++ ) { - if ( pStruct->pOneINChI_Aux[i] ) { - pNum = (pStruct->pOneINChI_Aux[i]->nIsotopicOrigAtNosInCanonOrd && - pStruct->pOneINChI_Aux[i]->nIsotopicOrigAtNosInCanonOrd[0])? - pStruct->pOneINChI_Aux[i]->nIsotopicOrigAtNosInCanonOrd: - (pStruct->pOneINChI_Aux[i]->nOrigAtNosInCanonOrd && - pStruct->pOneINChI_Aux[i]->nOrigAtNosInCanonOrd[0])? - pStruct->pOneINChI_Aux[i]->nOrigAtNosInCanonOrd : NULL; - } else { - pNum = NULL; - } - if ( pNum ) { - len = pStruct->num_atoms * sizeof(pStruct->nCanon2Atno[0][0]); - if ( !pStruct->nCanon2Atno[i] && - !(pStruct->nCanon2Atno[i] = (AT_NUMB *) inchi_malloc( len )) || - !pStruct->nAtno2Canon[i] && - !(pStruct->nAtno2Canon[i] = (AT_NUMB *) inchi_malloc( len ))) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - - INCHI_HEAPCHK - - memcpy( pStruct->nCanon2Atno[i], pNum, len ); /* ??? the next for(...) fills it out */ - - INCHI_HEAPCHK - - for ( j = 0; j < pStruct->num_atoms; j ++ ) { - k = pNum[j]-1; /* atom number */ - pStruct->nCanon2Atno[i][j] = (AT_NUMB)k; - pStruct->nAtno2Canon[i][k] = (AT_NUMB)j; - INCHI_HEAPCHK - } - } else - if ( !i ) { - ret = RI_ERR_PROGR; - goto exit_function; - } else { - if ( pStruct->nCanon2Atno[i] ) { - inchi_free( pStruct->nCanon2Atno[i] ); - pStruct->nCanon2Atno[i] = NULL; - } - INCHI_HEAPCHK - if ( pStruct->nAtno2Canon[i] ) { - inchi_free( pStruct->nAtno2Canon[i] ); - pStruct->nAtno2Canon[i] = NULL; - } - INCHI_HEAPCHK - } - } - -exit_function: - return ret; -} -/*************************************************************************/ -int FillOutExtraFixedHDataInChI( StrFromINChI *pStruct, INChI *pInChI[] ) -{ - int ret = 0; - /*--- allocate memory for Mobile/Fixed-H data from the input InChI ---*/ - if ( NULL == pStruct->endpoint ) { - pStruct->endpoint = (AT_NUMB *)inchi_calloc(pStruct->num_atoms, sizeof(pStruct->endpoint[0])); - } else { - memset( pStruct->endpoint, 0, pStruct->num_atoms * sizeof(pStruct->endpoint[0] ) ); - } - if ( NULL == pStruct->fixed_H ) { - pStruct->fixed_H = (S_CHAR *) inchi_malloc(pStruct->num_atoms * sizeof(pStruct->fixed_H[0])); - } - if ( !pStruct->endpoint || !pStruct->fixed_H ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - /*--- fill out Mobile/Fixed-H data from the input InChI ---*/ - GetTgroupInfoFromInChI( &pStruct->ti, NULL, pStruct->endpoint, pInChI[1] ); - if ( pInChI[0]->nNum_H_fixed ) { - memcpy( pStruct->fixed_H, pInChI[0]->nNum_H_fixed, pStruct->num_atoms * sizeof(pStruct->fixed_H[0]) ); - } else { - memset( pStruct->fixed_H, 0, pStruct->num_atoms * sizeof(pStruct->fixed_H[0]) ); - } - -exit_function: - return ret; -} -/***********************************************************************************************/ -int FillOutCMP2FHINCHI( StrFromINChI *pStruct, inp_ATOM *at2, VAL_AT *pVA, INChI *pInChI[], CMP2FHINCHI *pc2i ) -{ - int ret = 0, i, j; - int bFixHRevrsExists = pInChI[1] && pInChI[1]->nNumberOfAtoms > 0 && !pInChI[1]->bDeleted; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - S_CHAR *num_Fixed_H_Revrs = pStruct->pOneINChI[0]->nNum_H_fixed? pStruct->pOneINChI[0]->nNum_H_fixed : NULL; - /* atom number in structure that produced original InChI is atom number in all inp_ATOM *atoms */ - /* atom number in structure that produced restored InChI is in nAtomRevrs[]: */ - AT_NUMB *nAtno2CanonRevrs = pStruct->nAtno2Canon[0]; - S_CHAR *pnMobHInChI = (pInChI[1] && pInChI[1]->nNum_H)? pInChI[1]->nNum_H : - (pInChI[0] && pInChI[0]->nNum_H)? pInChI[0]->nNum_H : NULL; - S_CHAR *pnMobHRevrs = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNum_H)? - pStruct->pOneINChI[1]->nNum_H : - (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)? - pStruct->pOneINChI[0]->nNum_H : NULL; - int nNumTgHInChI, nNumTgMInChI, nNumTgHRevrs, nNumTgMRevrs; - memset( pc2i, 0, sizeof(*pc2i) ); - pc2i->nNumTgInChI = pStruct->ti.num_t_groups; - pc2i->nNumTgRevrs = pStruct->One_ti.num_t_groups; - pc2i->bHasDifference |= pc2i->nNumTgInChI != pc2i->nNumTgRevrs; - - pc2i->nNumRemHInChI = pStruct->nNumRemovedProtonsMobHInChI; - pc2i->nNumRemHRevrs = pStruct->One_ti.tni.nNumRemovedProtons; - pc2i->bHasDifference |= pc2i->nNumRemHInChI != pc2i->nNumRemHRevrs; - - pc2i->bFixedHLayerExistsRevrs = bFixHRevrsExists; - pc2i->bHasDifference |= !bFixHRevrsExists; - - for ( i = 0; i < pStruct->ti.num_t_groups && i < pStruct->One_ti.num_t_groups; i ++ ) { - nNumTgHInChI = pStruct->ti.t_group[i].num[0] - pStruct->ti.t_group[i].num[1]; - nNumTgMInChI = pStruct->ti.t_group[i].num[1]; - nNumTgHRevrs = pStruct->One_ti.t_group[i].num[0] - pStruct->One_ti.t_group[i].num[1]; - nNumTgMRevrs = pStruct->One_ti.t_group[i].num[1]; - - pc2i->bHasDifference |= nNumTgHInChI != nNumTgHRevrs; - pc2i->bHasDifference |= nNumTgMInChI != nNumTgMRevrs; - - if ( pStruct->ti.t_group[i].nNumEndpoints == - pStruct->One_ti.t_group[i].nNumEndpoints ) { - - if ( nNumTgHInChI != nNumTgHRevrs ) { - pc2i->nNumTgDiffH ++; - } - if ( nNumTgMInChI != nNumTgMRevrs ) { - pc2i->nNumTgDiffMinus ++; - } - } - pc2i->bHasDifference |= pStruct->ti.t_group[i].nNumEndpoints != - pStruct->One_ti.t_group[i].nNumEndpoints; - - pc2i->nNumTgHInChI += nNumTgHInChI; - pc2i->nNumTgMInChI += nNumTgMInChI; - pc2i->nNumTgHRevrs += nNumTgHRevrs; - pc2i->nNumTgMRevrs += nNumTgMRevrs; - - } - for ( ; i < pStruct->ti.num_t_groups; i ++ ) { - nNumTgHInChI = pStruct->ti.t_group[i].num[0] - pStruct->ti.t_group[i].num[1]; - nNumTgMInChI = pStruct->ti.t_group[i].num[1]; - pc2i->nNumTgHInChI += nNumTgHInChI; - pc2i->nNumTgMInChI += nNumTgMInChI; - pc2i->bHasDifference |= 1; - } - for ( ; i < pStruct->One_ti.num_t_groups; i ++ ) { - nNumTgHRevrs = pStruct->One_ti.t_group[i].num[0] - pStruct->One_ti.t_group[i].num[1]; - nNumTgMRevrs = pStruct->One_ti.t_group[i].num[1]; - pc2i->nNumTgHRevrs += nNumTgHRevrs; - pc2i->nNumTgMRevrs += nNumTgMRevrs; - pc2i->bHasDifference |= 1; - - } - for ( i = j = 0; i < pStruct->num_atoms; i ++ ) { - /* i = original InChI canonical number - 1 */ - /* k = atom number from InChI created out of restored Fixed-H structure */ - int iCanonRevrs = nAtno2CanonRevrs[i]; - int endptInChI = pStruct->endpoint[i]; /* endpoint in InChI */ - int endptRevrs = at_Mobile_H_Revrs? at_Mobile_H_Revrs[i].endpoint : 0; - int nFixHInChI = pStruct->fixed_H[i]; - int nFixHRevrs = num_Fixed_H_Revrs? num_Fixed_H_Revrs[iCanonRevrs]:0; - int nMobHInChI = pnMobHInChI? pnMobHInChI[i]:0; - int nMobHRevrs = pnMobHRevrs? pnMobHRevrs[iCanonRevrs]:0; - if ( /*(!endptInChI || !endptRevrs) &&*/ (nFixHInChI != nFixHRevrs ) || - (!endptInChI != !endptRevrs) || nMobHInChI != nMobHRevrs ) { - /* in InChI or reversed InChI atom[i] is not tautomeric */ - /* and number of fixed-H on the atom[i] differs */ - if ( j >= MAX_DIFF_FIXH ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - pc2i->c2at[j].endptInChI = endptInChI; - pc2i->c2at[j].endptRevrs = endptRevrs; - pc2i->bHasDifference |= !endptInChI != !endptRevrs; - pc2i->c2at[j].atomNumber = i; - pc2i->c2at[j].nValElectr = pVA[i].cNumValenceElectrons; - pc2i->c2at[j].nPeriodNum = pVA[i].cPeriodicRowNumber; - pc2i->c2at[j].nFixHInChI = nFixHInChI; - pc2i->c2at[j].nFixHRevrs = nFixHRevrs; - pc2i->bHasDifference |= nFixHInChI != nFixHRevrs; - pc2i->c2at[j].nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H[i] : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H[i] : 0; - pc2i->c2at[j].nMobHRevrs = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNum_H)? - pStruct->pOneINChI[1]->nNum_H[iCanonRevrs] : - (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)? - pStruct->pOneINChI[0]->nNum_H[iCanonRevrs] : 0; - pc2i->nNumDiffMobH += (nMobHInChI != nMobHRevrs && !endptRevrs && !endptInChI); - pc2i->bHasDifference |= nMobHInChI != nMobHRevrs; - pc2i->c2at[j].nNumHRevrs = at2[i].num_H; - pc2i->c2at[j].nAtChargeRevrs = at2[i].charge; - j ++; - } - pc2i->nNumEndpInChI += (endptInChI != 0); - pc2i->nNumEndpRevrs += (endptRevrs != 0); - - if ( !pVA[i].cMetal ) { - pc2i->nChargeFixHRevrsNonMetal += at2[i].charge; - pc2i->nChargeMobHRevrsNonMetal += at_Mobile_H_Revrs? at_Mobile_H_Revrs[i].charge : 0; - } - - /*pStruct->bExtract |= EXTRACT_STRUCT_NUMBER;*/ - } - pc2i->nChargeFixHInChI = pInChI[0]? pInChI[0]->nTotalCharge : 0; - pc2i->nChargeMobHInChI = pInChI[1]? pInChI[1]->nTotalCharge : 0; - - pc2i->nChargeMobHRevrs = pStruct->pOneINChI[1]? pStruct->pOneINChI[1]->nTotalCharge : - pStruct->pOneINChI[0]? pStruct->pOneINChI[0]->nTotalCharge : 0; - pc2i->nChargeFixHRevrs = pStruct->pOneINChI[0]? pStruct->pOneINChI[0]->nTotalCharge : 0; - - pc2i->bHasDifference |= pc2i->nChargeFixHInChI != pc2i->nChargeFixHRevrs; - pc2i->bHasDifference |= pc2i->nChargeMobHInChI != pc2i->nChargeMobHRevrs; - -exit_function: - pc2i->len_c2at = j; - - return ret; -} -/***********************************************************************************************/ -int FillOutCMP2MHINCHI( StrFromINChI *pStruct, ALL_TC_GROUPS *pTCGroups, inp_ATOM *at2, - VAL_AT *pVA, INChI *pInChI[], CMP2MHINCHI *pc2i ) -{ - int ret = 0, i, j, iat; - int bFixHRevrsExists = pInChI[1] && pInChI[1]->nNumberOfAtoms > 0 && !pInChI[1]->bDeleted; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[0] && - pStruct->pOne_norm_data[0]->at)? pStruct->pOne_norm_data[0]->at : NULL; - /* atom number in structure that produced original InChI is atom number in all inp_ATOM *atoms */ - /* atom number in structure that produced restored InChI is in nAtomRevrs[]: */ - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - AT_NUMB *nAtno2CanonRevrs = pStruct->nAtno2Canon[0]; - S_CHAR *pnMobHInChI = (pInChI[0] && pInChI[0]->nNum_H)? pInChI[0]->nNum_H : NULL; - S_CHAR *pnMobHRevrs = (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)? - pStruct->pOneINChI[0]->nNum_H : NULL; - int nNumTgHInChI, nNumTgMInChI, nNumTgHRevrs, nNumTgMRevrs; - memset( pc2i, 0, sizeof(*pc2i) ); - pc2i->nNumTgInChI = pStruct->ti.num_t_groups; - pc2i->nNumTgRevrs = pStruct->One_ti.num_t_groups; - pc2i->bHasDifference |= pc2i->nNumTgInChI != pc2i->nNumTgRevrs; - - pc2i->nNumRemHInChI = pStruct->nNumRemovedProtonsMobHInChI; - pc2i->nNumRemHRevrs = pStruct->One_ti.tni.nNumRemovedProtons; - /*pc2i->bHasDifference |= pc2i->nNumRemHInChI != pc2i->nNumRemHRevrs;*/ - - pc2i->bFixedHLayerExistsRevrs = bFixHRevrsExists; - /*pc2i->bHasDifference |= !bFixHRevrsExists;*/ - - for ( i = 0; i < pStruct->ti.num_t_groups; i ++ ) { - int jFst = pStruct->ti.t_group[i].nFirstEndpointAtNoPos; - int jNum = pStruct->ti.t_group[i].nNumEndpoints; - int is_N, is_O; - for ( j = 0; j < jNum; j ++ ) { - iat = pStruct->ti.nEndpointAtomNumber[jFst + j]; - is_N = pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1; - is_O = pVA[iat].cNumValenceElectrons == 6; - if ( is_N + is_O != 1 ) { - return RI_ERR_SYNTAX; - } - pc2i->nNumTgNInChI += is_N; - pc2i->nNumTgOInChI += is_O; - if ( at2[iat].chem_bonds_valence == at2[iat].valence ) { - /* donor */ - if ( is_N ) { - /* N */ - pc2i->nNumTgNHInChI += at2[iat].charge == 0 && at2[iat].num_H == 1; - pc2i->nNumTgNH2InChI += at2[iat].charge == 0 && at2[iat].num_H == 2; - pc2i->nNumTgNMinusInChI += at2[iat].charge == -1 && at2[iat].num_H == 0; - pc2i->nNumTgNHMinusInChI += at2[iat].charge == -1 && at2[iat].num_H == 1; - } else { - /* O, S, Se, Te */ - pc2i->nNumTgOHInChI += at2[iat].charge == 0 && at2[iat].num_H == 1; - pc2i->nNumTgOMinusInChI += at2[iat].charge == -1 && at2[iat].num_H == 0; - } - } else - if ( at2[iat].chem_bonds_valence == at2[iat].valence+1 ) { - /* donor */ - if ( is_N ) { - /* N */ - pc2i->nNumTgDBNHInChI += at2[iat].charge == 0 && at2[iat].num_H == 1; - pc2i->nNumTgDBNMinusInChI += at2[iat].charge == -1 && at2[iat].num_H == 0; - pc2i->nNumTgDBNInChI += at2[iat].charge == 0 && at2[iat].num_H == 0; - } else { - /* O, S, Se, Te */ - pc2i->nNumTgDBOInChI += at2[iat].charge == 0 && at2[iat].num_H == 0; - } - } - } - } - for ( i = 0; i < pStruct->One_ti.num_t_groups; i ++ ) { - int jFst = pStruct->One_ti.t_group[i].nFirstEndpointAtNoPos; - int jNum = pStruct->One_ti.t_group[i].nNumEndpoints; - int is_N, is_O; - for ( j = 0; j < jNum; j ++ ) { - iat = nCanon2AtnoRevrs[(int)pStruct->One_ti.nEndpointAtomNumber[jFst + j]]; - is_N = pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1; - is_O = pVA[iat].cNumValenceElectrons == 6; - if ( is_N + is_O != 1 ) { - return RI_ERR_PROGR; - } - pc2i->nNumTgNRevrs += is_N; - pc2i->nNumTgORevrs += is_O; - if ( at2[iat].chem_bonds_valence == at2[iat].valence ) { - /* donor */ - if ( is_N ) { - /* N */ - pc2i->nNumTgNHRevrs += at2[iat].charge == 0 && at2[iat].num_H == 1; - pc2i->nNumTgNH2Revrs += at2[iat].charge == 0 && at2[iat].num_H == 2; - pc2i->nNumTgNMinusRevrs += at2[iat].charge == -1 && at2[iat].num_H == 0; - pc2i->nNumTgNHMinusRevrs += at2[iat].charge == -1 && at2[iat].num_H == 1; - } else { - /* O, S, Se, Te */ - pc2i->nNumTgOHRevrs += at2[iat].charge == 0 && at2[iat].num_H == 1; - pc2i->nNumTgOMinusRevrs += at2[iat].charge == -1 && at2[iat].num_H == 0; - } - } else - if ( at2[iat].chem_bonds_valence == at2[iat].valence+1 ) { - /* donor */ - if ( is_N ) { - /* N */ - pc2i->nNumTgDBNHRevrs += at2[iat].charge == 0 && at2[iat].num_H == 1; - pc2i->nNumTgDBNMinusRevrs += at2[iat].charge == -1 && at2[iat].num_H == 0; - pc2i->nNumTgDBNRevrs += at2[iat].charge == 0 && at2[iat].num_H == 0; - } else { - /* O, S, Se, Te */ - pc2i->nNumTgDBORevrs += at2[iat].charge == 0 && at2[iat].num_H == 0; - } - } - } - } - - for ( i = 0; i < pStruct->ti.num_t_groups && i < pStruct->One_ti.num_t_groups; i ++ ) { - nNumTgHInChI = pStruct->ti.t_group[i].num[0] - pStruct->ti.t_group[i].num[1]; - nNumTgMInChI = pStruct->ti.t_group[i].num[1]; - nNumTgHRevrs = pStruct->One_ti.t_group[i].num[0] - pStruct->One_ti.t_group[i].num[1]; - nNumTgMRevrs = pStruct->One_ti.t_group[i].num[1]; - - pc2i->bHasDifference |= nNumTgHInChI != nNumTgHRevrs; - pc2i->bHasDifference |= nNumTgMInChI != nNumTgMRevrs; - - if ( pStruct->ti.t_group[i].nNumEndpoints == - pStruct->One_ti.t_group[i].nNumEndpoints ) { - - if ( nNumTgHInChI != nNumTgHRevrs ) { - pc2i->nNumTgDiffH ++; - } - if ( nNumTgMInChI != nNumTgMRevrs ) { - pc2i->nNumTgDiffMinus ++; - } - } - pc2i->bHasDifference |= pStruct->ti.t_group[i].nNumEndpoints != - pStruct->One_ti.t_group[i].nNumEndpoints; - - pc2i->nNumTgHInChI += nNumTgHInChI; - pc2i->nNumTgMInChI += nNumTgMInChI; - pc2i->nNumTgHRevrs += nNumTgHRevrs; - pc2i->nNumTgMRevrs += nNumTgMRevrs; - - } - for ( ; i < pStruct->ti.num_t_groups; i ++ ) { - nNumTgHInChI = pStruct->ti.t_group[i].num[0] - pStruct->ti.t_group[i].num[1]; - nNumTgMInChI = pStruct->ti.t_group[i].num[1]; - pc2i->nNumTgHInChI += nNumTgHInChI; - pc2i->nNumTgMInChI += nNumTgMInChI; - pc2i->bHasDifference |= 1; - } - for ( ; i < pStruct->One_ti.num_t_groups; i ++ ) { - nNumTgHRevrs = pStruct->One_ti.t_group[i].num[0] - pStruct->One_ti.t_group[i].num[1]; - nNumTgMRevrs = pStruct->One_ti.t_group[i].num[1]; - pc2i->nNumTgHRevrs += nNumTgHRevrs; - pc2i->nNumTgMRevrs += nNumTgMRevrs; - pc2i->bHasDifference |= 1; - - } - for ( i = j = 0; i < pStruct->num_atoms; i ++ ) { - /* i = original InChI canonical number - 1 */ - /* k = atom number from InChI created out of restored Fixed-H structure */ - int iCanonRevrs = nAtno2CanonRevrs[i]; - int endptInChI = at2[i].endpoint; /* endpoint in InChI */ - int endptRevrs = at_Mobile_H_Revrs? at_Mobile_H_Revrs[i].endpoint : 0; - int nMobHInChI = pnMobHInChI? pnMobHInChI[i]:0; - int nMobHRevrs = pnMobHRevrs? pnMobHRevrs[iCanonRevrs]:0; - if ( (!endptInChI != !endptRevrs) || nMobHInChI != nMobHRevrs ) { - /* in InChI or reversed InChI atom[i] is not tautomeric */ - /* and number of fixed-H on the atom[i] differs */ - if ( j >= MAX_DIFF_FIXH ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - pc2i->c2at[j].endptInChI = endptInChI; - pc2i->c2at[j].endptRevrs = endptRevrs; - pc2i->bHasDifference |= !endptInChI != !endptRevrs; - pc2i->c2at[j].atomNumber = i; - pc2i->c2at[j].nValElectr = pVA[i].cNumValenceElectrons; - pc2i->c2at[j].nPeriodNum = pVA[i].cPeriodicRowNumber; - pc2i->c2at[j].nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H[i] : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H[i] : 0; - pc2i->c2at[j].nMobHRevrs = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNum_H)? - pStruct->pOneINChI[1]->nNum_H[iCanonRevrs] : - (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)? - pStruct->pOneINChI[0]->nNum_H[iCanonRevrs] : 0; - - pc2i->nNumDiffMobH += (nMobHInChI != nMobHRevrs && !endptRevrs && !endptInChI); - pc2i->bHasDifference |= (nMobHInChI != nMobHRevrs); - pc2i->c2at[j].nNumHRevrs = at2[i].num_H; - pc2i->c2at[j].nAtChargeRevrs = at2[i].charge; - j ++; - } - pc2i->nNumEndpInChI += (endptInChI != 0); - pc2i->nNumEndpRevrs += (endptRevrs != 0); - - if ( !pVA[i].cMetal ) { - pc2i->nChargeMobHRevrsNonMetal += (at_Mobile_H_Revrs && !at_Mobile_H_Revrs[i].endpoint)? at_Mobile_H_Revrs[i].charge : 0; - } - - - /*pStruct->bExtract |= EXTRACT_STRUCT_NUMBER;*/ - } - pc2i->nChargeMobHRevrsNonMetal += pTCGroups->tgroup_charge; - - pc2i->nChargeMobHInChI = pInChI[0]? pInChI[0]->nTotalCharge : 0; - - pc2i->nChargeMobHRevrs = pStruct->pOneINChI[0]? pStruct->pOneINChI[0]->nTotalCharge : 0; - - pc2i->bHasDifference |= pc2i->nChargeMobHInChI != pc2i->nChargeMobHRevrs; - -exit_function: - pc2i->len_c2at = j; - - return ret; -} -/******************************************************************************************************/ -int NormalizeAndCompare(ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, - StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, INChI *pInChI[], long num_inp, int bHasSomeFixedH, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask) -{ - int i; - int err; - ICR icr, icr2; - int num_norm_endpoints, num_endpoints, num_norm_t_groups, num_mobile, num_norm_mobile, ret = 0; -#if ( bRELEASE_VERSION == 0 ) -#ifndef TARGET_API_LIB - const char *szCurHdr = (ip->pSdfValue && ip->pSdfValue[0])? ip->pSdfValue : "???"; - int iComponent = pTCGroups->iComponent; -#endif -#endif - T_GROUP_INFO *t_group_info = NULL; - inp_ATOM *at_norm = NULL; /* normalized */ - inp_ATOM *at_prep = NULL; /* preprocessed */ - INCHI_MODE cmpInChI, cmpInChI2; - int nDeltaPrev, nDeltaCur; - int iOrigInChI, iRevrInChI; - - - /***********************************************************/ - /* normalize and create one component InChI */ - /***********************************************************/ - ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - &t_group_info, &at_norm, &at_prep ); - if ( ret < 0 ) { -#if ( bRELEASE_VERSION == 0 ) -#ifndef TARGET_API_LIB - fprintf( stdout, "\nERROR in MakeOneInchi-1: %ld %s Comp:%d %c%c Err:%d\n", num_inp, - szCurHdr? szCurHdr: "???", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', ret); -#endif -#endif - goto exit_function; - } - if ( pStruct->bMobileH == TAUT_NON ) { - /* these indexes are used to compare Mobile-H InChI */ - iOrigInChI = (pInChI[1] && pInChI[1]->nNumberOfAtoms && !pInChI[1]->bDeleted)? 1 : 0; - iRevrInChI = (pStruct->pOneINChI[1] &&pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted)? 1 : 0; - } else { - iOrigInChI = 0; - iRevrInChI = 0; - } - - /************************************************************/ - /* compare */ - /************************************************************/ - if ( pStruct->iMobileH == TAUT_NON && (ret = FillOutExtraFixedHDataRestr( pStruct )) ) { - goto exit_function; - } - cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *a2*/, &icr, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - /********** InChI from restored structure has LESS hydrogen atoms ******************************/ - if ( (cmpInChI & IDIF_LESS_H) && at_prep && 0 < (nDeltaCur = icr.tot_num_H2 - icr.tot_num_H1) ) { - do { - ret = FixLessHydrogenInFormula( pBNS, pBD, pStruct, at, at2, at_prep, pVA, pTCGroups, - pnNumRunBNS, pnTotalDelta, forbidden_edge_mask ); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret ) { - /* Probably success. The changes are in pBNS. Create new InChI out of the new restored structure */ - ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - &t_group_info, &at_norm, &at_prep ); - if ( ret < 0 ) { -#if ( bRELEASE_VERSION == 0 ) -#ifndef TARGET_API_LIB - fprintf( stdout, "\nERROR in MakeOneInchi-2: %ld %s Comp:%d %c%c Err:%d\n", num_inp, - szCurHdr? szCurHdr: "???", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', ret); -#endif -#endif - goto exit_function; - } - /* compare new InChI to the original InChI */ - if ( pStruct->bMobileH == TAUT_NON ) { - iRevrInChI = (pStruct->pOneINChI[1] &&pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted)? 1 : 0; - } else { - iRevrInChI = 0; - } - if ( pStruct->iMobileH == TAUT_NON && (ret = FillOutExtraFixedHDataRestr( pStruct )) ) { - goto exit_function; - } - cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL, &icr, &err ); - nDeltaPrev = nDeltaCur; - nDeltaCur = icr.tot_num_H2 - icr.tot_num_H1; - } else { - break; - } - } while( (cmpInChI & IDIF_LESS_H) && at_prep && nDeltaCur && nDeltaCur < nDeltaPrev ); - } - /********** InChI from restored structure has MORE hydrogen atoms ******************************/ - if ( (cmpInChI & IDIF_MORE_H) && at_prep && 0 < (nDeltaCur = icr.tot_num_H1 - icr.tot_num_H2) ) { - do { - ret = FixMoreHydrogenInFormula( pBNS, pBD, pStruct, at, at2, at_prep, pVA, pTCGroups, - pnNumRunBNS, pnTotalDelta, forbidden_edge_mask ); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret ) { - /* Probably success. The changes are in pBNS. Create new InChI out of the new restored structure */ - ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - &t_group_info, &at_norm, &at_prep ); - if ( ret < 0 ) { -#if ( bRELEASE_VERSION == 0 ) -#ifndef TARGET_API_LIB - fprintf( stdout, "\nERROR in MakeOneInchi-3: %ld %s Comp:%d %c%c Err:%d\n", num_inp, - szCurHdr? szCurHdr: "???", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', ret); -#endif -#endif - goto exit_function; - } - /* compare new InChI to the original InChI */ - if ( pStruct->bMobileH == TAUT_NON ) { - iRevrInChI = (pStruct->pOneINChI[1] &&pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted)? 1 : 0; - } else { - iRevrInChI = 0; - } - if ( pStruct->iMobileH == TAUT_NON && (ret = FillOutExtraFixedHDataRestr( pStruct )) ) { - goto exit_function; - } - cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL, &icr, &err ); - nDeltaPrev = nDeltaCur; - nDeltaCur = icr.tot_num_H1 - icr.tot_num_H2; - } else { - break; - } - } while( (cmpInChI & IDIF_MORE_H) && at_prep && nDeltaCur && nDeltaCur < nDeltaPrev ); - } - /***************** Fix non-taut atoms normalized to tautomeric endpoints ***********************/ - if ( (cmpInChI & IDIF_EXTRA_TG_ENDP) && at_norm && 0 < (nDeltaCur = icr.num_endp_in1_only) ) { - do { - ret = FixRemoveExtraTautEndpoints( pBNS, pBD, pStruct, at, at2, at_prep, at_norm, pVA, pTCGroups, &icr, - pnNumRunBNS, pnTotalDelta, forbidden_edge_mask ); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret ) { - /* Probably success. The changes are in pBNS. Create new InChI out of the new restored structure */ - ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - &t_group_info, &at_norm, &at_prep ); - if ( ret < 0 ) { -#if ( bRELEASE_VERSION == 0 ) -#ifndef TARGET_API_LIB - fprintf( stdout, "\nERROR in MakeOneInchi-4: %ld %s Comp:%d %c%c Err:%d\n", num_inp, - szCurHdr? szCurHdr: "???", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', ret); -#endif -#endif - goto exit_function; - } - /* compare new InChI to the original InChI */ - if ( pStruct->bMobileH == TAUT_NON ) { - iRevrInChI = (pStruct->pOneINChI[1] &&pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted)? 1 : 0; - } else { - iRevrInChI = 0; - } - if ( pStruct->iMobileH == TAUT_NON && (ret = FillOutExtraFixedHDataRestr( pStruct )) ) { - goto exit_function; - } - cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL, &icr, &err ); - nDeltaPrev = nDeltaCur; - nDeltaCur = icr.num_endp_in1_only; - } else { - break; - } - } while( (cmpInChI & IDIF_EXTRA_TG_ENDP) && at_norm && nDeltaCur && nDeltaCur < nDeltaPrev ); - } - /************************ case of Fixed-H ******************************************************/ - - if ( pStruct->bMobileH == TAUT_NON ) { - int num_tries = 0; - do { - if ( 0 > (ret = FixFixedHRestoredStructure(ip, sd, pBNS, pBD, pStruct, at, at2, at3, pVA, pTCGroups, - &t_group_info, &at_norm, &at_prep, pInChI, - num_inp, bHasSomeFixedH, pnNumRunBNS, pnTotalDelta, forbidden_edge_mask, - forbidden_stereo_edge_mask) ) ) { - goto exit_function; - } - } while( num_tries ++ < 2 && ret > 0 ); - } - /************************ case of Fixed-H ******************************************************/ - if ( pStruct->bMobileH == TAUT_YES ) { - if ( 0 > (ret = FixMobileHRestoredStructure(ip, sd, pBNS, pBD, pStruct, at, at2, at3, pVA, pTCGroups, - &t_group_info, &at_norm, &at_prep, pInChI, - num_inp, bHasSomeFixedH, pnNumRunBNS, pnTotalDelta, forbidden_edge_mask, - forbidden_stereo_edge_mask) ) ) { - goto exit_function; - } - } - /**********************************************************************************************/ - /* stereo */ - cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *a2*/, &icr, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - cmpInChI2 = 0; - memset ( &icr2, 0, sizeof(icr2) ); - if ( iRevrInChI || iOrigInChI ) { - /* additional mobile-H compare in case of Fixed-H */ - cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *a2*/, &icr2, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - } - ret = FixRestoredStructureStereo( cmpInChI, &icr, cmpInChI2, &icr2, - ip, sd, pBNS, pBD, pStruct, at, at2, at3, pVA, pTCGroups, - &t_group_info, &at_norm, &at_prep, pInChI, - num_inp, pnNumRunBNS, pnTotalDelta, forbidden_edge_mask, - forbidden_stereo_edge_mask); - - if ( ret < 0 ) { - goto exit_function; - } -#if ( FIX_ADD_PROTON_FOR_ADP == 1 ) - /************************ check and fix ADP by adding a proton (dummy) *************************/ - if ( cmpInChI && pTCGroups->num_tgroups && pBNS->tot_st_cap > pBNS->tot_st_flow ) { - ret = FixAddProtonForADP( pBNS, pBD, pStruct, at, at2, at_prep, pVA, pTCGroups, &icr, - pnNumRunBNS, pnTotalDelta, forbidden_edge_mask ); - if ( ret < 0 ) { - goto exit_function; - } - } -#endif - /* moved to MakeOneInChIOutOfStrFromINChI(): - pStruct->nNumRemovedProtons = (pStruct->iMobileH == TAUT_YES)? pStruct->One_ti.tni.nNumRemovedProtons : 0; - */ - - /* count endpoints */ - num_endpoints = 0; - num_norm_endpoints = 0; - num_norm_t_groups = 0; - num_mobile = 0; - num_norm_mobile = 0; - at_norm = pStruct->pOne_norm_data[0]->at; - for ( i = 0; i < pTCGroups->num_tgroups; i ++ ) { - num_endpoints += pTCGroups->pTCG[i].num_edges; - num_mobile += pTCGroups->pTCG[i].tg_num_H + pTCGroups->pTCG[i].tg_num_Minus; - } - - if ( t_group_info ) { - /* after canonicalization, t_group_info->t_group[i].num[0] = number of H */ - /* t_group_info->t_group[i].num[1] = number of (-) */ - for ( i = 0; i < t_group_info->num_t_groups; i ++ ) { - if ( t_group_info->t_group[i].num[0] ) { - num_norm_t_groups ++; - num_norm_endpoints += t_group_info->t_group[i].nNumEndpoints; - num_norm_mobile += t_group_info->t_group[i].num[0]+t_group_info->t_group[i].num[1]; - } - } - } -#if ( bRELEASE_VERSION == 0 ) -#ifndef TARGET_API_LIB - if ( num_norm_t_groups != pTCGroups->num_tgroups || num_norm_endpoints != num_endpoints ) { - /* need aggressive (de)protonation */ - /* pStruct->bExtract |= EXTRACT_STRUCT_NUMBER; */ - fprintf( stdout, "NORMCOMP: %s comp=%d %c%c: InChI/NormRvrs NumTg=%d/%d NumEndp=%d/%d\n", - (*ip).pSdfValue, (*pTCGroups).iComponent, - pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', - pTCGroups->num_tgroups, num_norm_t_groups, - num_endpoints, num_norm_endpoints ); - - } -#endif -#endif - -exit_function: - - for( i = 0; i < TAUT_NUM; i ++ ) { - Free_INChI( &pStruct->pOneINChI[i] ); - Free_INChI_Aux( &pStruct->pOneINChI_Aux[i] ); - FreeInpAtomData( pStruct->pOne_norm_data[i] ); - if ( pStruct->pOne_norm_data[i] ) { - inchi_free( pStruct->pOne_norm_data[i] ); - pStruct->pOne_norm_data[i] = NULL; - } - } - free_t_group_info( &pStruct->One_ti ); - return ret; -} -/******************************************************************************************************/ -/* Find A=X< where all bonds to X except A=X are marked as stereogenic; temporary allow stereobonds */ -/* change and make A=X bonds single */ -int CheckAndRefixStereobonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ - int forbidden_edge_stereo = BNS_EDGE_FORBIDDEN_MASK; - int inv_forbidden_edge_stereo = ~forbidden_edge_stereo; - - int i, k, ne, j1, j2, num_wrong, num_fixed; - int ret2, retBNS, ret; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - EDGE_LIST FixedEdges, WrongEdges, CarbonChargeEdges; - - BNS_EDGE *pEdge; - Vertex v1, v2; - BNS_VERTEX *pv1, *pv2; - - ret = 0; - - /* to simplify, prepare new at[] from pBNS */ - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - return ret; - } - - num_wrong = 0; - /* find wrong double bonds */ - for ( i = 0; i < num_at; i ++ ) { - if ( at2[i].valence == 3 && - at2[i].chem_bonds_valence - at2[i].valence == 1 && - at2[i].sb_parity[0] && at2[i].sb_parity[1] && !at2[i].sb_parity[2] && - (at2[i].bond_type[j1=(int)at2[i].sb_ord[0]] & BOND_TYPE_MASK) == BOND_TYPE_SINGLE && - (at2[i].bond_type[j2=(int)at2[i].sb_ord[1]] & BOND_TYPE_MASK) == BOND_TYPE_SINGLE && - j1 != j2 ) { - - num_wrong ++; - } - } - if ( !num_wrong ) { - return 0; - } - num_fixed = 0; - for ( i = 0; i < pBNS->num_bonds; i ++ ) { - pEdge = pBNS->edge + i; - if ( pEdge->forbidden & forbidden_edge_stereo ) { - num_fixed ++; - } - } - - /* there may be no fixed stereo bonds at all, see #87607 */ - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &FixedEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &WrongEdges, EDGE_LIST_CLEAR ); - - /* do not goto exit_function before reaching this point: EdgeLists have not been initiated */ - - if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { - goto exit_function; - } - if ( (ret = AllocEdgeList( &FixedEdges, num_fixed )) || - (ret = AllocEdgeList( &WrongEdges, num_wrong )) ) { - goto exit_function; - } - /* collect wrong double bonds and set flow=0 */ - for ( i = 0; i < num_at && WrongEdges.num_edges < num_wrong; i ++ ) { - if ( at2[i].valence == 3 && - at2[i].chem_bonds_valence - at2[i].valence == 1 && - at2[i].sb_parity[0] && at2[i].sb_parity[1] && !at2[i].sb_parity[2] && - (at2[i].bond_type[j1=(int)at2[i].sb_ord[0]] & BOND_TYPE_MASK) == BOND_TYPE_SINGLE && - (at2[i].bond_type[j2=(int)at2[i].sb_ord[1]] & BOND_TYPE_MASK) == BOND_TYPE_SINGLE && - j1 != j2 ) { - switch ( j1 + j2 ) { - case 1: /* 0, 1 */ - k = 2; - break; - case 2: /* 0, 2 */ - k = 1; - break; - case 3: /* 1, 2 */ - k = 0; - break; - default: - ret = RI_ERR_PROGR; - goto exit_function; - } - ne = pBNS->vert[i].iedge[k]; - pEdge = pBNS->edge + ne; - v1 = pEdge->neighbor1; - v2 = pEdge->neighbor12 ^ v1; - pv1 = pBNS->vert + v1; - pv2 = pBNS->vert + v2; - - if ( !pEdge->flow ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - pEdge->flow --; - pEdge->forbidden |= forbidden_edge_mask; - pv1->st_edge.flow --; - pv2->st_edge.flow --; - pBNS->tot_st_flow -= 2; - if ( ret = AddToEdgeList( &WrongEdges, ne, 0 )) { - goto exit_function; - } - } - } - /* remove forbidden mark from stereo bonds (unfix stereo bonds) */ - for ( i = 0; i < pBNS->num_bonds && FixedEdges.num_edges < num_fixed; i ++ ) { - pEdge = pBNS->edge + i; - if ( pEdge->forbidden & forbidden_edge_stereo ) { - pEdge->forbidden &= inv_forbidden_edge_stereo; - FixedEdges.pnEdges[FixedEdges.num_edges ++] = i; - } - } - /* Run BNS to move charges and rearrange bond orders */ - retBNS = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( retBNS < 0 ) { - goto exit_function; - } else - if ( retBNS > 0 ) { - *pnTotalDelta += retBNS; - } - /* remove forbidden_edge_mask and set forbidden_edge_stereo */ - RemoveForbiddenEdgeMask( pBNS, &WrongEdges, forbidden_edge_mask ); - /* allow carbon charges to change */ - RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - /* fix previously unfixed stereo bonds */ - SetForbiddenEdgeMask( pBNS, &FixedEdges, forbidden_edge_stereo ); - /* Run BNS again in case not all edge flows are maximal */ - ret2 = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret2 < 0 ) { - goto exit_function; - } else - if ( ret2 > 0 ) { - *pnTotalDelta += retBNS; - } - ret = retBNS; - -exit_function: - - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &FixedEdges, EDGE_LIST_FREE ); - AllocEdgeList( &WrongEdges, EDGE_LIST_FREE ); - - return ret; -} -/******************************************************************************************************/ -/* Find and eliminate false Mobile-H groups: Cl(=O)3(-O(-)) => Cl(-)(=O)4 */ -int MoveChargeToRemoveCenerpoints(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ - int i, j, neigh, num_endpoints, num_success; - int num_donors, num_acceptors, bond_type, num_donors_O, num_acceptors_O, is_centerpoint_N, num_known_endpoints, num_wrong_neigh; - int ret2, ret_forbid_edges, ret, delta; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int forbidden_edge_test = BNS_EDGE_FORBIDDEN_TEST; - int bPossiblyIgnore = pStruct->charge >= 0 && (!pTCGroups->num_tgroups || pStruct->iMobileH == TAUT_NON && pStruct->ti.num_t_groups); - S_CHAR MobileChargeNeigh[MAXVAL], DoubleBondAcceptors[MAXVAL], DoubleBondNotONeigh[MAXVAL]; - int numMobileChargeNeigh, numDoubleBondAcceptors, numDoubleBondNotONeigh, numOtherDoubleBondOAcceptors=0; - EDGE_LIST ChargeListAllExcept_DB_O; - - - BNS_EDGE *pEdgeMinus, *pe; - Vertex v1m, v2m; - BNS_VERTEX *pv1m, *pv2m; - ret = 0; - num_success = 0; - - /* count O(+)H, N(+)H */ - - /* - if ( pStruct->charge >= 0 && (!pTCGroups->num_tgroups || pStruct->iMobileH == TAUT_NON && pStruct->ti.num_t_groups) ) { - goto exit_function; - } - */ - if ( ret = AllocEdgeList( &ChargeListAllExcept_DB_O, EDGE_LIST_CLEAR ) ) { - goto exit_function; - } - - - /* to simplify, prepare new at[] from pBNS */ - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } -#if ( FIND_RING_SYSTEMS == 1 ) - ret2 = MarkRingSystemsInp( at2, num_at, 0 ); - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } -#endif - /* mark bonds that cannot be tautomeric; do not forget to remove the marks later */ - ret_forbid_edges = SetForbiddenEdges( pBNS, at2, num_at, forbidden_edge_test ); - if ( ret_forbid_edges < 0 ) { - ret = ret_forbid_edges; - goto exit_function; - } - - for ( i = 0; i < num_at; i ++ ) { - if ( pVA[i].cNumValenceElectrons != 4 && /* not C, Si, Ge */ - !(pVA[i].nTautGroupEdge || pStruct->iMobileH == TAUT_NON && pStruct->endpoint && pStruct->endpoint[i] ) && - !at2[i].num_H && !at2[i].charge && at2[i].valence >= 2 && - at2[i].valence < at2[i].chem_bonds_valence && - is_centerpoint_elem( at2[i].el_number ) ) { - - is_centerpoint_N = (pVA[i].cNumValenceElectrons == 5 && (pVA[i].cPeriodicRowNumber == 1 || pVA[i].cMetal)); - /* look at the neighbors */ - numMobileChargeNeigh = numDoubleBondAcceptors = numDoubleBondNotONeigh = num_donors = num_acceptors = 0; - num_donors_O = num_acceptors_O = 0; - num_known_endpoints = num_wrong_neigh = 0; - for ( j = 0, num_endpoints = 0; j < at2[i].valence; j ++ ) { - neigh = at2[i].neighbor[j]; - if ( (at2[neigh].endpoint || pStruct->iMobileH == TAUT_NON && pStruct->endpoint && pStruct->endpoint[neigh]) || at2[neigh].charge > 0 ) { - num_known_endpoints ++; - continue; - } - if ( pBNS->edge[pBNS->vert[i].iedge[j]].forbidden & forbidden_edge_test ) { - continue; - } - bond_type = at2[i].bond_type[j] & BOND_TYPE_MASK; - if ( bond_type > BOND_TYPE_DOUBLE ) { - num_wrong_neigh ++; - continue; - } - if ( at2[neigh].num_H && bond_type == BOND_TYPE_SINGLE ) { - break; /* not this case */ - } - if ( at2[neigh].chem_bonds_valence - at2[neigh].charge - != get_endpoint_valence( at2[neigh].el_number ) ) { - if ( bond_type == BOND_TYPE_DOUBLE && pVA[neigh].cNumValenceElectrons != 6 ) { - DoubleBondNotONeigh[numDoubleBondNotONeigh ++] = j; - } - continue; - } - if ( at2[neigh].charge == -1 && bond_type == BOND_TYPE_SINGLE && - (pVA[neigh].nCMinusGroupEdge < 1 || pBNS->edge[pVA[neigh].nCMinusGroupEdge-1].flow != 1) ) { - break; - } - switch( bond_type ) { - case BOND_TYPE_SINGLE: - if ( at2[neigh].charge != -1 || pVA[neigh].nCMinusGroupEdge <= 0 ) { - num_wrong_neigh ++; - continue; - } - num_donors ++; - num_donors_O += (pVA[neigh].cNumValenceElectrons == 6 && pVA[neigh].cPeriodicRowNumber <= 4); - MobileChargeNeigh[numMobileChargeNeigh ++] = j; - break; - case BOND_TYPE_DOUBLE: - if ( at2[neigh].charge ) { - num_wrong_neigh ++; - continue; - } - DoubleBondAcceptors[numDoubleBondAcceptors ++] = j; - num_acceptors ++; - num_acceptors_O += (pVA[neigh].cNumValenceElectrons == 6 && pVA[neigh].cPeriodicRowNumber <= 4); - } - } - if ( j != at2[i].valence || !num_donors || !num_acceptors ) { - continue; - } - /* special case NOn(-) */ - if ( is_centerpoint_N && (num_donors == num_donors_O) && (num_acceptors == num_acceptors_O) ) { - continue; - } - if ( pStruct->iMobileH == TAUT_NON && num_donors == numDoubleBondNotONeigh ) { - /* fix all charges except on =O */ - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - int k, e, num_MovedCharges = 0; - - if ( !ChargeListAllExcept_DB_O.num_edges ) { - numOtherDoubleBondOAcceptors = 0; - for ( k = 0; k < num_at; k ++ ) { - if ( 1 == at2[k].valence && pBNS->edge[pBNS->vert[k].iedge[0]].flow && - !pBNS->edge[pBNS->vert[k].iedge[0]].forbidden && - !((e=pVA[k].nCMinusGroupEdge-1) >= 0 && pBNS->edge[e].flow) && - !((e=pVA[k].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].flow) && - /* 0 == at2[k].charge && */ - pVA[k].cNumValenceElectrons == 6 && !pVA[k].cMetal && - pStruct->endpoint && pStruct->endpoint[k] || - pStruct->fixed_H && pStruct->fixed_H[k] ) { - numOtherDoubleBondOAcceptors ++; /* do not fix this minus edge */ - } else - if ( (e=pVA[k].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].flow && - !pBNS->edge[e].forbidden && - ( ret = AddToEdgeList( &ChargeListAllExcept_DB_O, e, 64 )) ) { - goto exit_function; - } - if ( (e=pVA[k].nCPlusGroupEdge-1) >= 0 && - !pBNS->edge[e].forbidden && - ( ret = AddToEdgeList( &ChargeListAllExcept_DB_O, e, 64 )) ) { - goto exit_function; - } - } - } - /* fix double bonds to non-O neighbors connected by double bonds; - we will try to make these bons single */ - for ( k = 0; k < numDoubleBondNotONeigh; k ++ ) { - e = pBNS->vert[i].iedge[(int)DoubleBondNotONeigh[k]]; - if ( !pBNS->edge[e].forbidden && - (ret = AddToEdgeList( &ChargeListAllExcept_DB_O, e, 64 ))) { - goto exit_function; - } - } - /* attempt to make DoubleBondNotONeigh[] single */ - SetForbiddenEdgeMask( pBNS, &ChargeListAllExcept_DB_O, forbidden_edge_mask); - for ( k = 0; k < numDoubleBondNotONeigh && num_MovedCharges < numMobileChargeNeigh; k ++ ) { - pe = pBNS->edge + pBNS->vert[i].iedge[(int)DoubleBondNotONeigh[k]]; - delta = 1; - if ( pe->flow != delta ) - continue; - pv1m = pBNS->vert + (v1m = pe->neighbor1); - pv2m = pBNS->vert + (v2m = pe->neighbor12 ^ v1m); - pv1m->st_edge.flow -= delta; - pv2m->st_edge.flow -= delta; - pe->flow -= delta; - pBNS->tot_st_flow -= 2*delta; - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret == 1 && (vPathEnd == v1m && vPathStart == v2m || - vPathEnd == v2m && vPathStart == v1m) && - nDeltaCharge == 0 /* (-) moving from one to another atom*/ ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else - if ( ret == 1 ) { - *pnTotalDelta += ret; - num_MovedCharges ++; - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - } else { - ret = 0; - pv1m->st_edge.flow += delta; - pv2m->st_edge.flow += delta; - pe->flow += delta; - pBNS->tot_st_flow += 2*delta; - } - } - RemoveForbiddenEdgeMask( pBNS, &ChargeListAllExcept_DB_O, forbidden_edge_mask); - } else - if ( !bPossiblyIgnore || !num_known_endpoints && !num_wrong_neigh && (num_acceptors_O + num_donors_O) >=3 ) { - /* remove negative charges from the neighbors */ - pBNS->vert[i].st_edge.cap += num_donors; /* enough to make all bonds to donors double */ - pBNS->tot_st_cap += num_donors; - pVA[i].cInitCharge -= num_donors; /* work no matter what are known charge/valence */ - for ( j = 0; j < numMobileChargeNeigh; j ++ ) { - neigh = at2[i].neighbor[ (int)MobileChargeNeigh[j] ]; - pEdgeMinus = pBNS->edge + (pVA[neigh].nCMinusGroupEdge-1); - v1m = pEdgeMinus->neighbor1; - v2m = pEdgeMinus->neighbor12 ^ v1m; - pv1m = pBNS->vert + v1m; - pv2m = pBNS->vert + v2m; - delta = pEdgeMinus->flow; - pv1m->st_edge.flow -= delta; - pv2m->st_edge.flow -= delta; - if ( IS_BNS_VT_C_GR( pv1m->type ) ) { - /* irreversible change to ChargeStruct */ - pv1m->st_edge.cap -= delta; - } else - if ( IS_BNS_VT_C_GR( pv2m->type ) ) { - /* irreversible change to ChargeStruct */ - pv2m->st_edge.cap -= delta; - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - pBNS->tot_st_cap -= delta; - pBNS->tot_st_flow -= 2*delta; - pEdgeMinus->flow -= delta; - } - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else - if ( ret == num_donors ) { - *pnTotalDelta += ret; - num_success ++; - /*pStruct->bExtract |= EXTRACT_STRUCT_NUMBER;*/ - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - } - } - } - if ( ret_forbid_edges ) { - /* remove the marks */ - RemoveForbiddenBondFlowBits( pBNS, forbidden_edge_test ); - } - ret = num_success; -exit_function: - AllocEdgeList( &ChargeListAllExcept_DB_O, EDGE_LIST_FREE ); - return ret; -} -/******************************************************************************************************/ -/* Find and eliminate cases when Mobile H endpoint has radical on it (typical for wrong P(VI)(=O)3OH */ -int MakeSingleBondsMetal2ChargedHeteroat(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ - int i; - - int ret2, ret, pass; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - - int j, k; - int cur_num_edges; - BNS_EDGE *e; - Vertex v1, v2; - - EdgeIndex *pFixedEdges; - int nNumEdgesToFix; - - ret = 0; - - /* to simplify, prepare new at[] from pBNS */ - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - - pFixedEdges = NULL; - - nNumEdgesToFix = 0; /* cpunt nNumEdgesToFix only when pass==0 */ - cur_num_edges = 0; /* count cur_num_edges only when pass==1; at the end they must be equal */ - for ( pass = 0; pass < 2; pass ++ ) { - if ( pass ) { - /* 2nd pass: allocate edge storage */ - if ( !nNumEdgesToFix ) { - break; /* nothing to do */ - } - pFixedEdges = (EdgeIndex *) inchi_malloc(nNumEdgesToFix * sizeof( pFixedEdges[0] ) ); - if ( !pFixedEdges ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - } - for ( i = 0; i < num_at; i ++ ) { - int neigh; - if ( pVA[i].cMetal ) { - for ( j = 0; j < at2[i].valence; j ++ ) { - neigh = at2[i].neighbor[j]; - if ( pVA[neigh].cNumValenceElectrons == 4 && - pVA[neigh].cPeriodicRowNumber == 1 ) { - continue; /* ignore carbon */ - } - if ( at2[i].bond_type[j] > BOND_TYPE_SINGLE && at2[neigh].charge && - !pVA[neigh].cMetal && pVA[neigh].cnListIndex > 0 ) { - int cnBits = at2[neigh].charge > 0? MAKE_CN_BITS(cn_bits_N, cn_bits_P, 0, 0) : - MAKE_CN_BITS(cn_bits_N, cn_bits_M, 0, 0); - int atBits = cnList[pVA[neigh].cnListIndex-1].bits; - for ( k = 0; k < MAX_NUM_CN_BITS-1; k ++, atBits >>= cn_bits_shift ) { /* ??? */ - if ( (atBits & cnBits) == cnBits ) { - break; - } - } - if ( k == MAX_NUM_CN_BITS-1 ) { - continue; - } - if ( pass == 0 ) { - nNumEdgesToFix ++; - } else { - pFixedEdges[ cur_num_edges ++ ] = pBNS->vert[i].iedge[j]; - } - } - } - } - } - } - - /* restore the initial structures */ - memcpy( at2, at, (num_at + num_deleted_H)*sizeof(at2[0])); - - if ( nNumEdgesToFix && pFixedEdges ) { - if ( nNumEdgesToFix != cur_num_edges ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - /* change edge flow, fix the edges, and run BNS */ - for ( i = 0; i < nNumEdgesToFix; i ++ ) { - e = pBNS->edge + pFixedEdges[i]; - v1 = e->neighbor1; - v2 = e->neighbor12 ^ v1; - e->flow --; - e->forbidden |= forbidden_edge_mask; - pBNS->vert[v1].st_edge.flow --; - pBNS->vert[v2].st_edge.flow --; - pBNS->tot_st_flow -= 2; - (*pnTotalDelta) -= 2; - } - /* Run BNS allowing to change any charges */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else { - (*pnTotalDelta) += ret; - } - /* unfix the edges */ - for ( i = 0; i < nNumEdgesToFix; i ++ ) { - e = pBNS->edge + pFixedEdges[i]; - e->forbidden &= inv_forbidden_edge_mask; - } - if ( ret < 2 * nNumEdgesToFix ) { - /* not all fixes succeeded */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else { - (*pnTotalDelta) += ret; - } - } - } - if ( pFixedEdges ) { - inchi_free( pFixedEdges ); - pFixedEdges = NULL; - } - - -exit_function: - return ret; -} -/**************************************************************************/ -/* In Reconnected structure change 'salt bonds' to 'coordination bonds */ -/* for example, M-O-C= -> M(+)-O(-)-C= */ -/* Defect: instead of NH2-C=O(+)-M it will restore NH2(+)=C-O(-)-M(+) */ -/* However, in this release metal-organic compounds do not get much care */ -int SaltBondsToCoordBonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ - int i; - - int ret2, ret, cur_success; - int num_at = pStruct->num_atoms; - int num_edges = pBNS->num_bonds + 2 * pBNS->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - EDGE_LIST AllChargeEdges; - - int j, k, n; - BNS_EDGE *pe, *pePlusMetal, *peMinusO; - BNS_VERTEX *pv1, *pv2, *pvO, *pvM; - Vertex v1, v2, vPlusMinus; - - EdgeIndex ie, iePlusMetal, ieMinusO; - - Vertex vPathStart, vPathEnd; - int delta, nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - - ret = 0; - cur_success = 0; - AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); - - if ( pStruct->iInchiRec == INCHI_BAS || !pStruct->pSrm->bMetalAddFlower || pStruct->pSrm->nMetalMinBondOrder ) { - goto exit_function; - } - - /* to simplify, prepare new at[] from pBNS */ - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - for ( i = 0; i < num_at; i ++ ) { - if ( bIsMetalSalt( at2, i ) ) { - if ( !AllChargeEdges.num_edges ) { - /*--------- one-time action: fix all bonds, charges, taut. group edges ------------*/ - for ( j = 0; j < num_at; j ++ ) { - /* all bonds */ - for ( k = 0; k < at2[j].valence; k ++ ) { - n = at2[j].neighbor[k]; - if ( n < j && !pBNS->edge[ie = pBNS->vert[j].iedge[k]].forbidden && - ( ret = AddToEdgeList( &AllChargeEdges, ie, num_edges ) ) ) { - goto exit_function; - } - } - /* charge edges */ - if ( (ie=pVA[j].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[ie].forbidden && - (ret = AddToEdgeList( &AllChargeEdges, ie, num_edges ) ) ) { - goto exit_function; - } - if ( (ie=pVA[j].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[ie].forbidden && - ( ret = AddToEdgeList( &AllChargeEdges, ie, num_edges ) ) ) { - goto exit_function; - } - } - /* taut group edges */ - for ( j = 0; j < pTCGroups->num_tgroups; j ++ ) { - pv1 = pBNS->vert + (v1=pTCGroups->pTCG[j].nVertexNumber); /* t-group vertex */ - for ( k = 0; k < pv1->num_adj_edges; k ++ ) { - /* ie, pe - tautomeric atom edge; pv2 - endpoint vertex */ - /* Note: pe, pv2, v1 are not used here; they are to show how to traverse t-group */ - pv2 = pBNS->vert + (pe = pBNS->edge + (ie=pv1->iedge[k]))->neighbor1; - if ( ret = AddToEdgeList( &AllChargeEdges, ie, num_edges ) ) { - goto exit_function; - } - } - } - /*---------------------------------------------------------------*/ - } - /* replace all single bonds to neutral neighbors with zero-order bonds - allow neighbor charge change to (-1) and metal atom charge increment +1 */ - for ( k = 0; k < at2[i].valence; k ++ ) { - n = at2[i].neighbor[k]; - pe = pBNS->edge + pBNS->vert[i].iedge[k]; - if ( at2[n].charge || at2[i].bond_type[k] != BOND_TYPE_SINGLE ) { - continue; - } - iePlusMetal = pVA[i].nCPlusGroupEdge-1; - ieMinusO = pVA[n].nCMinusGroupEdge-1; - - if ( pe->flow != 1 || pe->forbidden || iePlusMetal < 0 ) { - continue; - } - pePlusMetal = pBNS->edge + iePlusMetal; - if ( pePlusMetal->flow <= 0 ) { - continue; /* to add (+) to metal this flow must be decremented */ - } - if ( ieMinusO >= 0 ) { - /* usually does not happen */ - peMinusO = pBNS->edge + ieMinusO; - - if ( peMinusO->flow || pePlusMetal->forbidden || peMinusO->forbidden ) { - continue; - } - - /* decrement bond order to 0 */ - delta = 1; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - pePlusMetal->forbidden &= inv_forbidden_edge_mask; - peMinusO->forbidden &= inv_forbidden_edge_mask; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) /*&& nDeltaCharge > 0*/ ) { - /* (+)charge was just moved, no change in number of charges */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - (*pnNumRunBNS) ++; - cur_success ++; /* 01 */ - } - } else { - pe->flow += delta; /* roll back */ - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } else - if ( NO_VERTEX != (vPlusMinus = GetPlusMinusVertex( pBNS, pTCGroups, 1, 1 ) ) ) { - /* manually add (-) charge to O and (+) charge to metal */ - /* decrement bond order to 0 */ - /*---------------------------------------------------------------------------*/ - /* */ - /* (+/-)* (+/-) Result: */ - /* | || */ - /* | || - Added (+) to M */ - /* (+)super (+)super - Incremented bond M-O */ - /* || | */ - /* || => | To make this attachment H, */ - /* (Y) (Y) increment */ - /* | || pTCGroups->pTCG[itg].tg_num_H */ - /* | || */ - /* (+)metal (+)hetero Technical details: */ - /* \\ \ increase capacities of */ - /* M M(+) edges to (+/-) otherwise */ - /* | || flow may not be able to */ - /* -O* -O-O increase */ - /* */ - /* After that change M=O bond order from 2 to 0 */ - /*---------------------------------------------------------------------------*/ - int i1, j1, k1; - delta = 1; - pvO = pBNS->vert + n; - pvM = pBNS->vert + i; - /* Increment st_edge.cap on (+/-) vertex */ - pBNS->vert[vPlusMinus].st_edge.cap += delta; - /* Increment st_edge.cap on O */ - pvO->st_edge.cap += delta; - /* increment cap on M-O edge */ - pe->cap += delta; - /* total cap count */ - pBNS->tot_st_cap += 2*delta; - - v1 = vPlusMinus; - v2 = n; /* atom O */ - - /* increase capacities of edges to Y */ - for ( i1 = 0; i1 < pBNS->vert[vPlusMinus].num_adj_edges; i1 ++ ) { - j1 = pBNS->edge[pBNS->vert[vPlusMinus].iedge[i1]].neighbor12 ^ vPlusMinus; - for ( k1 = 0; k1 < pBNS->vert[j1].num_adj_edges; k1 ++ ) { - pBNS->edge[pBNS->vert[j1].iedge[k1]].cap += delta; - } - } - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - pePlusMetal->forbidden &= inv_forbidden_edge_mask; - pe->forbidden &= inv_forbidden_edge_mask; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - cur_success = 0; - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) /*&& nDeltaCharge == 1*/ ) { - /* Added (+)charge to -N< => nDeltaCharge == 1 */ - /* Flow change on pe (-)charge edge (atom B-O(-)) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - (*pnNumRunBNS) ++; - cur_success ++; /* 01 */ - } - } - if ( cur_success ) { - /* set bond M=O order = 0 */ - if ( pe->flow != 2*delta ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - /* reduce pe bond order by 2*delta */ - pe->flow -= 2*delta; - pvO->st_edge.cap -= 2*delta; - pvO->st_edge.flow -= 2*delta; - pvM->st_edge.flow -= 2*delta; - pvM->st_edge.cap -= 2*delta; - pBNS->tot_st_cap -= 3*delta; - pBNS->tot_st_flow -= 4*delta; - /* fix M-O bond order to zero */ - pe->cap -= 2*delta; - /* add fixed (-) charge to O */ - pVA[n].cInitCharge -= delta; - } else { - /* failed */ - pBNS->vert[vPlusMinus].st_edge.cap -= delta; - pvO->st_edge.cap -= delta; - /*pTCGroups->pTCG[itg].edges_cap -= delta;*/ /* ???bug??? - commented out 2006-03-22 */ - pBNS->tot_st_cap -= 2*delta; - /* decrease capacities of edges to Y */ - for ( i1 = 0; i1 < pBNS->vert[vPlusMinus].num_adj_edges; i1 ++ ) { - j1 = pBNS->edge[pBNS->vert[vPlusMinus].iedge[i1]].neighbor12 ^ vPlusMinus; - for ( k1 = 0; k1 < pBNS->vert[j1].num_adj_edges; k1 ++ ) { - pBNS->edge[pBNS->vert[j1].iedge[k1]].cap -= delta; - } - } - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - } - } - } - -exit_function: - AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); - return ret; -} -#if ( KEEP_METAL_EDGE_FLOW == 1 ) -/******************************************************************************************************/ -int ForbidMetalCarbonEdges( BN_STRUCT *pBNS, inp_ATOM *at, int num_at, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, EDGE_LIST *pMetalCarbonEdges, int forbidden_edge_mask ) -{ - - int i, j, neigh, nNumEdgeMetalCarbon = 0, pass = 0, ret = 0; - BNS_VERTEX *pVert, *pNeigh; - BNS_EDGE *pEdge; - - /* count carbon-metal edges */ - - if ( pTCGroups->num_metal_atoms ) { -fill_ForbiddenEdgesMetalCarbon: - for ( i = 0; i < num_at; i ++ ) { - if ( pVA[i].cMetal && pVA[i].cNumBondsToMetal ) { - pVert = pBNS->vert + i; - for ( j = 0; j < pVert->num_adj_edges; j ++ ) { - pEdge = pBNS->edge + pVert->iedge[j]; - neigh = pEdge->neighbor12 ^ i; - pNeigh = pBNS->vert + neigh; - if ( !IS_BNS_VT_ATOM(pNeigh->type) ) - continue; - if ( at[neigh].endpoint ) - continue; - if ( pVA[neigh].cNumValenceElectrons == 4 && pVA[neigh].cPeriodicRowNumber == 1 && - pNeigh->st_edge.cap >= at[neigh].valence+1 ) { - if ( pass ) { - if ( ret = AddToEdgeList( pMetalCarbonEdges, pVert->iedge[j], 0 ) ) { - goto exit_function; - } - pEdge->forbidden |= forbidden_edge_mask; - } else { - nNumEdgeMetalCarbon ++; - } - } - - } - } - } - if ( !pass && nNumEdgeMetalCarbon ) { - if ( ret = AllocEdgeList( pMetalCarbonEdges, nNumEdgeMetalCarbon ) ) { - goto exit_function; - } - pass ++; - goto fill_ForbiddenEdgesMetalCarbon; - } - } -exit_function: - return ret; -} -#endif -/******************************************************************************************************/ -int RunBnsRestore1( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, - StrFromINChI *pStruct, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, INChI *pInChI[], - long num_inp, int bHasSomeFixedH ) -{ - int nNumRunBNS = 0; - - EDGE_LIST CarbonChargeEdges, MetalCarbonEdges, Nplus2BondsEdges; - - int nTotalDelta = 0, ret = 0, tot_num_fixes; - inp_ATOM *at = pStruct->at; - inp_ATOM *at2 = NULL; /* restored structure */ - inp_ATOM *at3 = NULL; /* structure for calculating one InChI */ - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; -#ifdef _DEBUG - int ret2; -#endif - -#if ( KEEP_METAL_EDGE_FLOW == 1 ) - BNS_VERTEX *pVert, *pNeigh; - int j, neigh; -#endif - - /* Edge lista initialization */ - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &MetalCarbonEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &Nplus2BondsEdges, EDGE_LIST_CLEAR ); - - if ( pStruct->iMobileH == TAUT_NON && - ( ret = FillOutExtraFixedHDataInChI( pStruct, pInChI ) ) ) { - goto exit_function; - } - - if ( !at2 && !(at2 = (inp_ATOM *) inchi_malloc((num_at + num_deleted_H)*sizeof(at2[0]))) || - !at3 && !(at3 = (inp_ATOM *) inchi_malloc((num_at + num_deleted_H)*sizeof(at3[0])))) { - return RI_ERR_ALLOC; - } - - if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, BNS_EDGE_FORBIDDEN_TEMP ))) { - goto exit_function; - } - -#if ( KEEP_METAL_EDGE_FLOW == 1 ) - /* count edges of -C(IV)< carbons connected to metals */ - if ( 0 > (ret = ForbidMetalCarbonEdges( pBNS, at, num_at, pVA, pTCGroups, &MetalCarbonEdges, BNS_EDGE_FORBIDDEN_TEMP ))) { - goto exit_function; - } -#endif - if ( 0 > (ret = ForbidNintrogenPlus2BondsInSmallRings( pBNS, at, num_at, pVA, 6, - pTCGroups, &Nplus2BondsEdges, BNS_EDGE_FORBIDDEN_TEMP ) ) ) { - goto exit_function; - } - - /*********** Run BNS #1: no charge on carbons and =N= ***************/ - if ( Nplus2BondsEdges.num_edges ) { - /* Run BNS leaving carbon charges unchanged */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - nNumRunBNS ++; - if ( ret < 0 ) { - goto exit_function; - } else { - nTotalDelta += ret; - } - RemoveForbiddenEdgeMask( pBNS, &Nplus2BondsEdges, BNS_EDGE_FORBIDDEN_TEMP ); - AllocEdgeList( &Nplus2BondsEdges, EDGE_LIST_FREE ); - } -#ifdef _DEBUG - /* debug only */ - memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; -#endif - /*************************** extend min ring size to 8 ****************************/ - if ( 0 > (ret = ForbidNintrogenPlus2BondsInSmallRings( pBNS, at, num_at, pVA, 8, - pTCGroups, &Nplus2BondsEdges, BNS_EDGE_FORBIDDEN_TEMP ) ) ) { - goto exit_function; - } - if ( Nplus2BondsEdges.num_edges ) { - /* Run BNS leaving carbon charges unchanged */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - nNumRunBNS ++; - if ( ret < 0 ) { - goto exit_function; - } else { - nTotalDelta += ret; - } - RemoveForbiddenEdgeMask( pBNS, &Nplus2BondsEdges, BNS_EDGE_FORBIDDEN_TEMP ); - AllocEdgeList( &Nplus2BondsEdges, EDGE_LIST_FREE ); - } -#ifdef _DEBUG - /* debug only */ - memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; -#endif - /*******************************************************************/ - if ( CarbonChargeEdges.num_edges > 0 ) { - /* Run BNS leaving carbon charges unchanged */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - nNumRunBNS ++; - if ( ret < 0 ) { - goto exit_function; - } else { - nTotalDelta += ret; - } - RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, BNS_EDGE_FORBIDDEN_TEMP ); - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); - } -#ifdef _DEBUG - /* debug only */ - memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; -#endif - /*******************************************************************/ - if ( MetalCarbonEdges.num_edges > 0 ) { - /* Run BNS leaving carbon charges unchanged */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - nNumRunBNS ++; - if ( ret < 0 ) { - goto exit_function; - } else { - nTotalDelta += ret; - } - RemoveForbiddenEdgeMask( pBNS, &MetalCarbonEdges, BNS_EDGE_FORBIDDEN_TEMP ); - AllocEdgeList( &MetalCarbonEdges, EDGE_LIST_FREE ); - } - /*******************************************************************/ - /* Run BNS allowing to change any charges */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - nNumRunBNS ++; - if ( ret < 0 ) { - goto exit_function; - } else { - nTotalDelta += ret; - } -#ifdef _DEBUG - /* debug only */ - memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; -#endif - -#if ( BNS_RAD_SEARCH == 1 ) - /******************************************************************/ - /* move unfulfilled 'radicals' from ChargeStruct to atoms */ - /* and set change charges of affected atoms to fit total charge */ - ret = MoveRadToAtomsAddCharges( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, BNS_EDGE_FORBIDDEN_TEMP ); - if ( ret < 0 ) { - goto exit_function; - } -#endif - /**************************************************************/ - /**************************************************************/ - /***** fix restore inconsistencies *****/ - /**************************************************************/ - /**************************************************************/ -#ifdef _DEBUG - /* debug only */ - memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; -#endif - - /* rearrange (+) and (-) edges flow so that there is no (+)flow=0 and (-)flow=1 */ - ret = RearrangePlusMinusEdgesFlow( pBNS, pBD, pVA, pTCGroups, BNS_EDGE_FORBIDDEN_TEMP ); - if ( ret < 0 ) { - goto exit_function; - } - - /*****************************************************************/ - /* Increment zero order metal bonds to heteroatoms */ - /*****************************************************************/ - ret = IncrementZeroOrderBondsToHeteroat( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - -#ifdef _DEBUG - /* debug only */ - memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; -#endif - -#if (MOVE_CHARGES_FROM_HETEREO_TO_METAL == 1 ) - /*****************************************************************/ - /* move charges from heteroatoms to metal atoms */ - /*****************************************************************/ - ret = MoveChargeFromHeteroatomsToMetals( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } -#endif - /*********************************************************************** - NH2 NH2 - \ \ - C==S(+)- => C(+)-S- where NH2 are not tautomeric - / / - NH2 NH2 - ************************************************************************/ - ret = MovePlusFromS2DiaminoCarbon( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - /*****************************************************************/ - /* Avoid charge separation on heteroatoms */ - /*****************************************************************/ - ret = EliminateChargeSeparationOnHeteroatoms( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP, 0); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret ) { - /*charge separation remains; allow changes of stereobonds in a ring and try again */ - ret = EliminateChargeSeparationOnHeteroatoms( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP, - BNS_EDGE_FORBIDDEN_MASK); - if ( ret < 0 ) { - goto exit_function; - } - } - /*****************************************************************/ - /* convert N#N(+)-N= into N(-)=N(+)=N- */ - /*****************************************************************/ - ret = RestoreNNNgroup( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - /*****************************************************************/ - /* convert Metal(q)-N(-)-O(-) Metal(q-2)-N=O (local change) */ - /*****************************************************************/ - ret = FixMetal_Nminus_Ominus( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - /*****************************************************************/ - /* convert N(-)=C= into N#C- - */ - /*****************************************************************/ - ret = RestoreCyanoGroup( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - /*****************************************************************/ - /* convert C(+)#N(+)- into C(-)#N(+)- */ - /*****************************************************************/ - ret = RestoreIsoCyanoGroup( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - /*****************************************************************/ - /* eliminate =N(V)= if possible */ - /* | */ - /*****************************************************************/ - ret = EliminateNitrogen5Val3Bonds(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - - /*****************************************************************/ - /* | | */ - /* convert -S- to =S= if possible */ - /* | | */ - /*****************************************************************/ - ret = Convert_SIV_to_SVI(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - - /*****************************************************************/ - /* =N(+)=O =N-O(-) */ - /* convert => if possible */ - /* Metal(q) Metal(q+2) */ - /*****************************************************************/ - ret = PlusFromDB_N_DB_O_to_Metal(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - - /*****************************************************************/ - /* forbidden edges prevents required in InChI tautomerism */ - /* incorrectly restored mobile H mix separate tautomeric groups */ - /* because an edge may not become forbidden */ - /* note: removes this 'forbidden_edge' bit from ALL edges */ - /*****************************************************************/ - ret = MoveMobileHToAvoidFixedBonds( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - - if ( ret < 0 ) { - goto exit_function; - } - /**************************************************************************/ - /* 2. Mobile H endpoint has radical on it (typical for wrong P(VI)(=O)3OH */ - tot_num_fixes = 0; - if ( pStruct->iMobileH==TAUT_NON ) { - ret = RemoveRadFromMobileHEndpointFixH( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - } else { - ret = RemoveRadFromMobileHEndpoint( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - } - if ( ret < 0 ) { - goto exit_function; - } - tot_num_fixes += ret; - /**************************************************************/ - /* make bonds between a charged heteroatom and a metal single */ - ret = MakeSingleBondsMetal2ChargedHeteroat(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - /**************************************************************/ - /* move (+) charges to >N- and other centerpoints */ - ret = MoveChargeToMakeCenerpoints(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - - /**************************************************************************/ - /* Find and eliminate false Mobile-H groups: Cl(=O)3(-O(-)) => Cl(-)(=O)4 */ - ret = MoveChargeToRemoveCenerpoints(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - /**************************************************************************/ - /* Find A=X< where all bonds to X except A=X are marked as stereogenic */ - /* make bonds A=X single */ - ret = CheckAndRefixStereobonds(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - /**************************************************************************/ - /* In Reconnected structure change 'salt bonds' to 'coordination bonds */ - /* for example, M-O-C= -> M(+)-O(-)-C= */ - /* Defect: instead of NH2-C=O(+)-M it will restore NH2(+)=C-O(-)-M(+) */ - /* However, in this release metal-organic compounds do not get much care */ - ret = SaltBondsToCoordBonds(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - /**************************************************************************/ - /* Normalize the structure and compare t-groups and stereobonds */ - ret = NormalizeAndCompare(ip, sd, pBNS, pBD, pStruct, at, at2, at3, pVA, pTCGroups, pInChI, num_inp, bHasSomeFixedH, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP, BNS_EDGE_FORBIDDEN_MASK); - if ( ret < 0 ) { - goto exit_function; - } - /**************************************************************************/ - /* Create InChI out of the restored structure */ - - - /*ret = nTotalDelta;*/ - -exit_function: - pStruct->at = at; - pStruct->at2 = at2; - at2 = NULL; - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &MetalCarbonEdges, EDGE_LIST_FREE ); - AllocEdgeList( &Nplus2BondsEdges, EDGE_LIST_FREE ); - if ( at2 ) { - inchi_free( at2 ); - } - if ( at3 ) { - inchi_free( at3 ); - } - - return ret; -} - -/******************************************************************************************************/ -int RestoreAtomMakeBNS( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, StrFromINChI *pStruct, int iComponent, - int iAtNoOffset, INChI *pInChI[], const char *szCurHdr, long num_inp, int bHasSomeFixedH ) -{ - int i, j, ret = 0, ret2; - /*int nDelta, nTotalDelta;*/ - VAL_AT *pVA = NULL; - VAL_AT va1; - int num_at = pStruct->num_atoms; - inp_ATOM *at = pStruct->at; - ALL_TC_GROUPS TCGroups; - ALL_TC_GROUPS *pTCGroups = &TCGroups; - int nAddEdges2eachAtom = 2, nAddVertices = 0; - - BFS_Q bfsq; - - /* BNS creation */ - BN_STRUCT *pBNS = NULL; - BN_DATA *pBD = NULL; - int nNum_changed_bonds = 0; - int bTreatMoreAtomsAsMetals = 0, bSecondPassNewMetals=0; - int nMaxAddAtoms = 2, nMaxAddEdges = 2, max_altp = BN_MAX_ALTP; - - memset( pTCGroups, 0, sizeof(pTCGroups[0]) ); - for ( i = 0; i < NUM_TCGROUP_TYPES; i ++ ) { - pTCGroups->nGroup[i] = TCG_None; /* unassigned */ - } - pTCGroups->iComponent = iComponent; - pTCGroups->iAtNoOffset = iAtNoOffset; - - if ( num_at == 1 ) { - /* single atom -- no bonds to restore */ - inp_ATOM *at2 = (inp_ATOM *) inchi_malloc(sizeof(at2[0])*(pStruct->num_atoms+pStruct->num_deleted_H)); - inp_ATOM *at3 = (inp_ATOM *) inchi_malloc(sizeof(at3[0])*(pStruct->num_atoms+pStruct->num_deleted_H)); - pStruct->at2 = at2; - at[0].charge = pInChI[0]->nTotalCharge; - if ( at2 ) { - memcpy( at2, at, sizeof(at2[0])*(pStruct->num_atoms+pStruct->num_deleted_H)); - } - if ( !at2 || !at3 ) { - if ( at3 ) inchi_free( at3 ); - return RI_ERR_ALLOC; - } - ret = MakeOneInChIOutOfStrFromINChI( ip, sd, pStruct, pStruct->at2, at3, pTCGroups ); - /* clean up */ - for( i = 0; i < TAUT_NUM; i ++ ) { - Free_INChI( &pStruct->pOneINChI[i] ); - Free_INChI_Aux( &pStruct->pOneINChI_Aux[i] ); - FreeInpAtomData( pStruct->pOne_norm_data[i] ); - if ( pStruct->pOne_norm_data[i] ) { - inchi_free( pStruct->pOne_norm_data[i] ); - pStruct->pOne_norm_data[i] = NULL; - } - } - free_t_group_info( &pStruct->One_ti ); - inchi_free( at3 ); - - return ret; - } - - AllocBfsQueue( &bfsq, BFS_Q_CLEAR, 0 ); - if ( !(pVA = (VAL_AT *) inchi_calloc( num_at, sizeof( pVA[0] ) ) ) ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - pStruct->pVA = pVA; - memset( &va1, 0, sizeof(va1) ); - pTCGroups->total_charge = pInChI[0]->nTotalCharge; - if ( 0 > ( ret = AllocBfsQueue( &bfsq, num_at, 0 /* min ring size undefined */ ) ) ) { - goto exit_function; - } - pStruct->pbfsq = &bfsq; - - if ( pStruct->iMobileH == TAUT_NON && pInChI[1] && pInChI[1]->nNumberOfAtoms > 1 && - ( ret = FillOutpStructEndpointFromInChI( pInChI[1], &pStruct->endpoint )) ) { - goto exit_function; - } - - /* mark metal atoms; find min ring sizes for atoms that have 2 bonds */ - for ( i = 0; i < num_at; i ++ ) { - pVA[i].cNumValenceElectrons = get_sp_element_type( at[i].el_number, &j ); - pVA[i].cPeriodicRowNumber = j; - pVA[i].cPeriodicNumber = at[i].el_number; - pVA[i].cNumValenceElectrons --; /* = -1 d- and f- metals, 0 for H, 1 for Na, 2 for Mg,.. = (ATYPE_Xx-1) */ - - if ( is_el_a_metal( at[i].el_number ) ) { - if ( pStruct->pSrm->bStereoRemovesMetalFlag ) { - /* treat metal as non-metal if it is stereogenic or has a stereobond */ - pVA[i].cMetal = !( at[i].p_parity || at[i].sb_parity[0] ); - } else { - pVA[i].cMetal = 1; - } - } - if ( at[i].valence == 2 && !at[i].num_H ) { - pVA[i].cMinRingSize = is_bond_in_Nmax_memb_ring( at, i, 0, bfsq.q, bfsq.nAtomLevel, - bfsq.cSource, 99 /* max ring size */ ); - } else { - pVA[i].cMinRingSize = 0; - } - } - /* AllocBfsQueue( &bfsq, BFS_Q_FREE, 0 ); */ - -repeat_for_new_metals: - /* set valences for the first time; find ChargeValence structures for each atom */ - for ( i = 0; i < num_at; i ++ ) { - /* get additional fictitious atoms information */ - pVA[i].cInitFreeValences = 0; - ret = GetAtomRestoreInfo( at, i, pVA, pStruct->pSrm, pStruct->bMobileH, pStruct->endpoint ); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret == TREAT_ATOM_AS_METAL && !bSecondPassNewMetals && !pVA[i].cMetal ) { - if ( pStruct->pSrm->bStereoRemovesMetalFlag ) { - /* treat metal as non-metal if it is stereogenic or has a stereobond */ - pVA[i].cMetal = !( at[i].p_parity || at[i].sb_parity[0] ); - } else { - pVA[i].cMetal = 1; - } - if ( pVA[i].cMetal ) { - bTreatMoreAtomsAsMetals ++; - } - } - pTCGroups->charge_on_atoms += pVA[i].cInitCharge; - } - if ( bTreatMoreAtomsAsMetals && !bSecondPassNewMetals ) { - for ( i = 0; i < num_at; i ++ ) { - /* clear all members of pVA[i] except two */ - pTCGroups->charge_on_atoms -= pVA[i].cInitCharge; - va1.cMetal = pVA[i].cMetal; - va1.cMinRingSize = pVA[i].cMinRingSize; - va1.cNumValenceElectrons = pVA[i].cNumValenceElectrons; - va1.cPeriodicRowNumber = pVA[i].cPeriodicRowNumber; - va1.cPeriodicNumber = pVA[i].cPeriodicNumber; - pVA[i] = va1; - } - bSecondPassNewMetals = 1; - goto repeat_for_new_metals; - } - - /* count atoms, bonds, additional edges and vertices in ChargeValence structures and t-groups */ - ret = nCountBnsSizes( at, num_at, nAddEdges2eachAtom, nAddVertices, &pStruct->ti, - pVA, pStruct->pSrm, pTCGroups ); - if ( ret < 0 ) { - goto exit_function; - } - - /* find and count groups; add counts of all other vertices to be created */ - ret = nAddSuperCGroups( pTCGroups ); - if ( ret < 0 ) { - goto exit_function; - } - - /* create the BNS and fill it with all real atoms */ - pBNS = AllocateAndInitTCGBnStruct( pStruct, pVA, pTCGroups, - nMaxAddAtoms, nMaxAddEdges, max_altp, &nNum_changed_bonds ); - if ( !pBNS ) { - ret = BNS_OUT_OF_RAM; - goto exit_function; - } - /* add t-groups to the BNS */ - ret = AddTGroups2TCGBnStruct( pBNS, pStruct, pVA, pTCGroups, nMaxAddEdges ); - if ( ret < 0 ) { - goto exit_function; - } - - /* add c-groups to the BNS; adjust charges */ - ret = AddCGroups2TCGBnStruct( pBNS, pStruct, pVA, pTCGroups, nMaxAddEdges ); - if ( ret < 0 ) { - goto exit_function; - } - - /* allocate BNData */ - pBD = AllocateAndInitBnData( pBNS->max_vertices + pBNS->max_vertices/2 ); - if ( !pBD ) { - ret = BNS_OUT_OF_RAM; - goto exit_function; - } - CheckBnsConsistency( pStruct, pBNS, pVA, pTCGroups, 0 ); - - /* restore bonds & charges */ - ret = RunBnsRestore1( ip, sd, pBNS, pBD, pStruct, pVA, pTCGroups, pInChI, num_inp, bHasSomeFixedH ); - if ( ret < 0 ) { - goto exit_function; - } - - ret = CheckBnsConsistency( pStruct, pBNS, pVA, pTCGroups, 1 ); -#if ( bRELEASE_VERSION == 0 ) -#ifndef TARGET_API_LIB - if ( ret ) { - fprintf( stdout, "Msg for: %ld %s comp=%d %c%c\n", num_inp, (szCurHdr && szCurHdr[0])? szCurHdr : "", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F' ); - } - if ( pStruct->iMobileH == TAUT_YES && pStruct->nNumRemovedProtons ) { - fprintf( stdout, "REMOVED_PROTONS%+d %ld %s\n", pStruct->nNumRemovedProtons, num_inp, (szCurHdr && szCurHdr[0])? szCurHdr : "" ); - /*pStruct->bExtract |= EXTRACT_STRUCT_NUMBER;*/ - } - if ( pStruct->bExtract & EXTRACT_STRUCT_NUMBER ) { - fprintf( stdout, "EXTRACT: %ld: %s\n", num_inp, (szCurHdr && szCurHdr[0])? szCurHdr : "" ); - } -#endif -#endif - { /* create the final structure in pStruct->at2 */ - inp_ATOM *at_tmp = pStruct->at; - pStruct->at = pStruct->at2; - memcpy( pStruct->at, at_tmp, sizeof(pStruct->at[0])*(pStruct->num_atoms + pStruct->num_deleted_H) ); - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at2 = pStruct->at; - pStruct->at = at_tmp; - if ( ret2 < 0 ) { - ret = ret2; - } - } - -exit_function: - - pStruct->pbfsq = NULL; - AllocBfsQueue( &bfsq, BFS_Q_FREE, 0 ); - - pBD = DeAllocateBnData( pBD ); - pBNS = DeAllocateBnStruct( pBNS ); - /* - if ( pVA ) inchi_free( pVA ); - */ - if ( pTCGroups->pTCG ) inchi_free( pTCGroups->pTCG ); - - return ret; -} -/******************************************************************************************************/ -int OneInChI2Atom( ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd, const char *szCurHdr, long num_inp, - StrFromINChI *pStruct, int iComponent, int iAtNoOffset, int bHasSomeFixedH, INChI *pInChI[]) -{ - int ret; - INPUT_PARMS *ip, ip_loc; - - ip_loc = *ip_inp; - ip = &ip_loc; - - sd->pStrErrStruct[0] = '\0'; - ret = RestoreAtomConnectionsSetStereo( pStruct, iComponent, iAtNoOffset, pInChI[0], pInChI[1]); - if ( ret < 0 ) { - goto exit_function; - } - ret = SetStereoBondTypesFrom0DStereo( pStruct, pInChI[0]); - if ( ret < 0 ) { - goto exit_function; - } - ret = ReconcileAllCmlBondParities( pStruct->at, pStruct->num_atoms, 0 ); - if ( ret < 0 ) { - goto exit_function; - } - /* main InChI restore function */ - ret = RestoreAtomMakeBNS( ip, sd, pStruct, iComponent, iAtNoOffset, pInChI, szCurHdr, num_inp, bHasSomeFixedH ); - -#ifndef COMPILE_ANSI_ONLY - if ( (pStruct->num_inp_actual>0? pStruct->num_inp_actual : num_inp) >= ip->first_struct_number && - ( (/*ret > 0 &&*/ ip->bDisplayIfRestoreWarnings ) && pStruct->pXYZ ) ) { - inchiTime ulTStart; - InchiTimeGet( &ulTStart ); - DisplayRestoredComponent( pStruct, iComponent, iAtNoOffset, pInChI[0], szCurHdr ); - sd->ulStructTime -= InchiTimeElapsed( &ulTStart ); /* subtract display time */ - } -#endif - if ( ret < 0 ) { - goto exit_function; - } - if ( (pStruct->num_inp_actual? pStruct->num_inp_actual: num_inp) >= ip->first_struct_number && ret >= 0 ) { - /* remove t-group markings and increment zero-order bonds, - otherwise MakeInChIOutOfStrFromINChI2() woild fail */ - /* --- moved to MakeInChIOutOfStrFromINChI2 --- - IncrZeroBondsAndClearEndpts(pStruct->at2, pStruct->num_atoms, iComponent+1); - CopySt2At( pStruct->at2, pStruct->st, pStruct->num_atoms ); - */ - /* include all restored structure features in pStruct->at2 */ - /* make full InChI out of pStruct->at2, pStruct->num_atoms */ - /***************************************************************************************/ - /* !!! pStruct->One_InChI etc. were removed at the exit from NormalizeAndCompare() !!! */ - /***************************************************************************************/ - if ( bHasSomeFixedH && pStruct->iInchiRec == INCHI_REC && pStruct->iMobileH == TAUT_YES && - !pStruct->bFixedHExists && !(ip->nMode & REQ_MODE_BASIC) ) { - /* reconnected components without Fixed-H layer may produce 'tautomeric' fragments like Cl(-) */ - ip->nMode |= REQ_MODE_BASIC; - } - ret = MakeInChIOutOfStrFromINChI2( ip, sd, pStruct, iComponent, iAtNoOffset, num_inp ); - if ( ret >= 0 ) { - ; - } -#if ( bRELEASE_VERSION == 0 ) -#ifndef TARGET_API_LIB - else { - fprintf( stdout, "\nERROR in MakeInChI-1: %ld %s Comp:%d %c%c Err:%d\n", num_inp, - szCurHdr? szCurHdr: "???", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', ret); - } -#endif -#endif - } - - -exit_function: - return ret; -} -/********************************************************************************************/ -int MakeProtonComponent( StrFromINChI *pStruct, int iComponent, int num_prot ) -{ - inp_ATOM *at = NULL; - int i; - - if ( num_prot <= 0 ) { - return 0; - } - /* allocate */ - pStruct->at = (inp_ATOM *) inchi_calloc( num_prot, sizeof(pStruct->at[0]) ); - pStruct->at2 = (inp_ATOM *) inchi_calloc( num_prot, sizeof(pStruct->at2[0]) ); - if ( !pStruct->at || !pStruct->at2 ) { - return 0; - } - /* create protons */ - at = pStruct->at; - /* fill out proton atom info */ - for ( i = 0; i < num_prot; i ++ ) { - strcpy( at[i].elname, "H" ); - at[i].el_number = EL_NUMBER_H; - at[i].orig_at_number = i+1; - /* - at[i].orig_compt_at_numb = i + 1; - at[i].component = i + 1; - */ - at[i].charge = 1; - } - memcpy( pStruct->at2, at, num_prot * sizeof(pStruct->at2[0]) ); - pStruct->bDeleted = 0; - pStruct->num_atoms = num_prot; - pStruct->bMobileH = TAUT_YES; - pStruct->iMobileH = TAUT_YES; - return num_prot; -} -/********************************************************************************************/ -int AddRemProtonsInRestrStruct( ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd, long num_inp, - int bHasSomeFixedH, - StrFromINChI *pStruct, int num_components, - StrFromINChI *pStructR, int num_componentsR, - NUM_H *nProtonsToBeRemovedByNormFromRevrs, int *recmet_change_balance ) -{ /* on entry and exit, all at[i].num_H do not include isotopic H and explicit terminal H are connected */ - int iComp, q, ret = 0; - int num_atoms, tot_num_at, num_deleted_H, num_tg, num_changed, num_deleted_components; - inp_ATOM *at; - INPUT_PARMS *ip, ip_loc; - int num_prot = *nProtonsToBeRemovedByNormFromRevrs; - int delta_recmet_prot, num_prot_prev, bAccumulateChanges=0, nNumProtAddedByRevrs; - INChI_Aux *pINChI_Aux; - INCHI_MODE bNormalizationFlags; - int nChargeRevrs, nChargeInChI; - - if ( !num_prot ) { - return 0; - } - delta_recmet_prot = 0; - num_changed = 0; - num_deleted_components = 0; - ip_loc = *ip_inp; - ip = &ip_loc; - /*---------------------------------------------------------------------------------- - nLink < 0 && num_componentsR > 0 => This is a Disconnected structure component; it is - same as already processed reconnected one - Do no preicess it - - nLink > 0 && num_componentsR > 0 => This is a Disconnected structure component; - (should not happen) It it is a result of (nLink-1)th Reconeected - component disconnection (NOT IMPLEMENTED YET) - - nLink = 0 => Process this component. It is either a reconnected - component, or a result of a disconnection (for now) - - nLink > 0 && num_componentsR = 0 => This is a Reconnected component that is same as - a disconnected one that will not be processed. - Process and save charge delta. - -----------------------------------------------------------------------------------*/ - - for ( iComp = 0; iComp < num_components && num_prot; iComp ++ ) { - bAccumulateChanges = 0; - if ( pStruct[iComp].nLink < 0 && num_componentsR > 0 ) { - /* check */ - q = -(pStruct[iComp].nLink+1); - if ( !pStructR || !num_componentsR || q >= num_componentsR || pStructR[q].nLink != (iComp+1) ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - continue; /* Disconnected structure component has already been processed as a Reconnected one */ - } - - at = pStruct[iComp].at2; - num_atoms = pStruct[iComp].num_atoms; - tot_num_at = pStruct[iComp].num_atoms+(num_deleted_H=pStruct[iComp].num_deleted_H); - bAccumulateChanges = ( pStruct[iComp].nLink > 0 && !num_componentsR ); - nChargeRevrs = pStruct[iComp].nChargeRevrs; - nChargeInChI = pStruct[iComp].nChargeInChI; - num_deleted_components += (0 != pStruct[iComp].bDeleted); - if ( !at || !num_atoms ) { - continue; - } - /* find whether it is a reconnected structure */ - q = bRevInchiComponentExists( pStruct+iComp, INCHI_REC, TAUT_YES, 0 )? INCHI_REC : INCHI_BAS; - /* - q = pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC] && - pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC][0][TAUT_YES] && - pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC][0][TAUT_YES]->nNumberOfAtoms? INCHI_REC : INCHI_BAS; - */ - pINChI_Aux = pStruct[iComp].RevInChI.pINChI_Aux[q][0][TAUT_YES]; /* 0 = 1st component in RevInChI */ - /*nNumProtAddedByRevrs = pINChI_Aux->nNumRemovedProtons;*/ - nNumProtAddedByRevrs = -pStruct[iComp].nNumRemovedProtonsByRevrs; - bNormalizationFlags = pINChI_Aux->bNormalizationFlags; - num_tg = pINChI_Aux->nNumberOfTGroups; - - - /* disconnect all explicit H and add the number of implicit iso H and all explicit terminal H to the number of implicit H */ - if ( 0 > ( ret = DisconnectedConnectedH( at, num_atoms, num_deleted_H ) ) ) { - goto exit_function; - } - num_prot_prev = num_prot; - ret = AddRemoveProtonsRestr( at, num_atoms, &num_prot, nNumProtAddedByRevrs, - bNormalizationFlags, num_tg, nChargeRevrs, nChargeInChI ); - - pStruct[iComp].bPostProcessed = ret; - num_changed += (ret > 0); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret > 0 ) { - /* recalculate InChI; it will reconnect at */ - StrFromINChI *pStruct1 = pStruct + iComp; - INCHI_MODE nMode = ip->nMode; - FreeAllINChIArrays( pStruct1->RevInChI.pINChI, - pStruct1->RevInChI.pINChI_Aux, - pStruct1->RevInChI.num_components ); - - if ( bHasSomeFixedH && pStruct1->iInchiRec == INCHI_REC && pStruct1->iMobileH == TAUT_YES && - !pStruct1->bFixedHExists && !(ip->nMode & REQ_MODE_BASIC) ) { - /* reconnected components without Fixed-H layer may produce 'tautomeric' fragments like Cl(-) */ - ip->nMode |= REQ_MODE_BASIC; - } - /* calls ConnectDisconnectedH(...): subtracts number of implicit iso H from implicit H */ - ret = MakeInChIOutOfStrFromINChI2( ip, sd, pStruct1, 0, 0, num_inp ); - ip->nMode = nMode; - if ( ret < 0 ) { - goto exit_function; - } - } else { - /* reconnect disconnected terminal H and subtracts number of implicit iso H from implicit H */ - if ( 0 > ( ret = ConnectDisconnectedH( at, num_atoms, num_deleted_H ) ) ) { - goto exit_function; - } - } - if ( bAccumulateChanges && recmet_change_balance ) { - /* processed Reconnected layer component that is also present in Disconnected layer */ - delta_recmet_prot += num_prot - num_prot_prev; - } - } - - iComp = num_components-1; - if ( !bHasSomeFixedH && num_prot > 0 && 1 == num_deleted_components && iComp >= 0 && pStruct[iComp].bDeleted ) { - /* add bare protons to the deleted Mobile-H component; undelete the component */ - num_prot_prev = num_prot; - if ( !MakeProtonComponent( pStruct+iComp, iComp, num_prot ) ) { - goto exit_function; - } else { - /* recalculate InChI; it will reconnect at */ - StrFromINChI *pStruct1 = pStruct + iComp; - INCHI_MODE nMode = ip->nMode; - num_changed ++; - num_prot = 0; - FreeAllINChIArrays( pStruct1->RevInChI.pINChI, - pStruct1->RevInChI.pINChI_Aux, - pStruct1->RevInChI.num_components ); - - if ( bHasSomeFixedH && pStruct1->iInchiRec == INCHI_REC && pStruct1->iMobileH == TAUT_YES && - !pStruct1->bFixedHExists && !(ip->nMode & REQ_MODE_BASIC) ) { - /* reconnected components without Fixed-H layer may produce 'tautomeric' fragments like Cl(-) */ - ip->nMode |= REQ_MODE_BASIC; - } - /* Although MakeInChIOutOfStrFromINChI2() calls ConnectDisconnectedH(...) */ - /* to subtracts number of implicit iso H from implicit H */ - /* this CANNOT have any effect on the deleted H component */ - ret = MakeInChIOutOfStrFromINChI2( ip, sd, pStruct1, 0, 0, num_inp ); - ip->nMode = nMode; - if ( ret < 0 ) { - goto exit_function; - } - if ( bAccumulateChanges && recmet_change_balance ) { - /* processed Reconnected layer component that is also present in Disconnected layer */ - delta_recmet_prot += num_prot - num_prot_prev; - } - } - } - *nProtonsToBeRemovedByNormFromRevrs = num_prot; - if ( recmet_change_balance ) { - *recmet_change_balance = delta_recmet_prot; - } - -exit_function: - return ret < 0? ret : num_changed; -} -/**********************************************************************************/ -int AddRemIsoProtonsInRestrStruct( ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd, long num_inp, int bHasSomeFixedH, - StrFromINChI *pStruct, int num_components, - StrFromINChI *pStructR, int num_componentsR, - NUM_H pProtonBalance[], NUM_H recmet_change_balance[] ) -{ /* on entry and exit, all at[i].num_H do not include isotopic H and explicit terminal H are connected */ - int iComp, q, k, ret = 0, bNotEmpty; - int num_atoms, tot_num_at, num_deleted_H, num_tg, num_changed; - inp_ATOM *at; - NUM_H num_prot[NUM_H_ISOTOPES], delta_recmet_prot[NUM_H_ISOTOPES], num_prot_prev[NUM_H_ISOTOPES]; - int bAccumulateChanges; - INChI_Aux *pINChI_Aux; - INChI *pINChI; - INCHI_MODE bNormalizationFlags; - INPUT_PARMS *ip, ip_loc; - - ip_loc = *ip_inp; - ip = &ip_loc; - - memcpy( num_prot, pProtonBalance, sizeof(num_prot) ); - for ( bNotEmpty=0, k = 0; k < NUM_H_ISOTOPES; k ++ ) { - bNotEmpty |= num_prot[k]; - } - if ( !bNotEmpty ) { - return 0; - } - memset ( delta_recmet_prot, 0, sizeof(delta_recmet_prot)); - num_changed = 0; - /*---------------------------------------------------------------------------------- - nLink < 0 && num_componentsR > 0 => This is a Disconnected structure component; it is - same as already processed reconnected one - Do no preicess it - - nLink > 0 && num_componentsR > 0 => This is a Disconnected structure component; - (should not happen) It it is a result of (nLink-1)th Reconeected - component disconnection (NOT IMPLEMENTED YET) - - nLink = 0 => Process this component. It is either a reconnected - component, or a result of a disconnection (for now) - - nLink > 0 && num_componentsR = 0 => This is a Reconnected component that is same as - a disconnected one that will not be processed. - Process and save charge delta. - -----------------------------------------------------------------------------------*/ - - for ( iComp = 0; iComp < num_components && num_prot; iComp ++ ) { - bAccumulateChanges = 0; - if ( pStruct[iComp].nLink < 0 && num_componentsR > 0 ) { - /* check */ - q = -(pStruct[iComp].nLink+1); - if ( !pStructR || !num_componentsR || q >= num_componentsR || pStructR[q].nLink != (iComp+1) ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - continue; /* Disconnected structure component has already been processed as a Reconnected one */ - } - - at = pStruct[iComp].at2; - num_atoms = pStruct[iComp].num_atoms; - tot_num_at = pStruct[iComp].num_atoms+(num_deleted_H=pStruct[iComp].num_deleted_H); - bAccumulateChanges = ( pStruct[iComp].nLink > 0 && !num_componentsR ); - - if ( !at || !num_atoms ) { - continue; - } - /* find whether it is a reconnected structure */ - q = pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC] && - pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC][0][TAUT_YES] && - pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC][0][TAUT_YES]->nNumberOfAtoms? INCHI_REC : INCHI_BAS; - - pINChI_Aux = pStruct[iComp].RevInChI.pINChI_Aux[q][0][TAUT_YES]; /* 0 = 1st component in RevInChI */ - pINChI = pStruct[iComp].RevInChI.pINChI[q][0][TAUT_YES]; /* 0 = 1st component in RevInChI */ - bNormalizationFlags = pINChI_Aux->bNormalizationFlags; - num_tg = pINChI_Aux->nNumberOfTGroups; - memcpy( num_prot_prev, num_prot, sizeof(num_prot_prev) ); - - /* pass CONNECTED explicit H to AddRemoveIsoProtonsRestr() for isotopic H addition */ - ret = AddRemoveIsoProtonsRestr( at, num_atoms, num_prot, num_tg ); - - pStruct[iComp].bPostProcessed |= ret; - num_changed += (ret > 0); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret > 0 ) { - StrFromINChI *pStruct1 = pStruct+iComp; - INCHI_MODE nMode = ip->nMode; - /* recalculate InChI; MakeInChIOutOfStrFromINChI2() will reconnect explicit H */ - /* disconnect all explicit H and add the number of implicit iso H and all explicit terminal H to the number of implicit H */ - if ( 0 > ( ret = DisconnectedConnectedH( at, num_atoms, num_deleted_H ) ) ) { - goto exit_function; - } - FreeAllINChIArrays( pStruct1->RevInChI.pINChI, - pStruct1->RevInChI.pINChI_Aux, - pStruct1->RevInChI.num_components ); - if ( bHasSomeFixedH && pStruct1->iInchiRec == INCHI_REC && pStruct1->iMobileH == TAUT_YES && - !pStruct1->bFixedHExists && !(ip->nMode & REQ_MODE_BASIC) ) { - /* reconnected components without Fixed-H layer may produce 'tautomeric' fragments like Cl(-) */ - ip->nMode |= REQ_MODE_BASIC; - } - /* input: disconnected explicit H, output: connected explicit H */ - ret = MakeInChIOutOfStrFromINChI2( ip, sd, pStruct1, 0, 0, num_inp ); - ip->nMode = nMode; - if ( ret < 0 ) { - goto exit_function; - } - } - /* the following was commented out 2007-08-28 by DT. Reason: it's a bug since H must be already connected */ - /* else { - if ( 0 > ( ret = ConnectDisconnectedH( at, num_atoms, num_deleted_H ) ) ) { - goto exit_function; - } - } */ - if ( bAccumulateChanges ) { - /* processed Reconnected layer component that is also present in Disconnected layer */ - for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) { - delta_recmet_prot[k] += num_prot[k] - num_prot_prev[k]; - } - } - } - - memcpy ( pProtonBalance, num_prot, sizeof(num_prot) ); - if ( recmet_change_balance ) { - memcpy ( recmet_change_balance, delta_recmet_prot, sizeof(delta_recmet_prot) ); - } -exit_function: - return ret < 0? ret : num_changed; -} - -#endif diff --git a/INCHI-1-SRC/INCHI/common/ichirvr5.c b/INCHI-1-SRC/INCHI/common/ichirvr5.c deleted file mode 100644 index 2c922ce..0000000 --- a/INCHI-1-SRC/INCHI/common/ichirvr5.c +++ /dev/null @@ -1,1197 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - -/*#define CHECK_WIN32_VC_HEAP*/ -#include "mode.h" - -#if ( READ_INCHI_STRING == 1 ) - -#include "ichi.h" -#include "ichitime.h" - -#include "inpdef.h" -#include "ichimain.h" -#include "ichierr.h" -#include "incomdef.h" -#include "ichiring.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "util.h" - -#include "ichicomp.h" -#include "ichister.h" - -#include "ichi_bns.h" - -#include "strutil.h" - -#include "ichirvrs.h" - - -#define INC_ADD_EDGE 64 -/***********************************************************************************************/ -int GetPlusMinusVertex( BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups, int bCheckForbiddenPlus, int bCheckForbiddenMinus ) -{ - int k, ePlusSuper, eMinusSuper, vPlusSuper, vMinusSuper, vPlusMinus1 = NO_VERTEX, vPlusMinus2 = NO_VERTEX; - BNS_EDGE *pEdge; - if ( (k = pTCGroups->nGroup[TCG_Plus]) >= 0 && - (ePlusSuper = pTCGroups->pTCG[k].nForwardEdge) > 0 && - (vPlusSuper = pTCGroups->pTCG[k].nVertexNumber) >= pBNS->num_atoms && - !((pEdge=pBNS->edge + ePlusSuper)->forbidden && bCheckForbiddenPlus) ) { - - vPlusMinus1 = pEdge->neighbor12 ^ vPlusSuper; - } - if ( (k = pTCGroups->nGroup[TCG_Minus]) >= 0 && - (eMinusSuper = pTCGroups->pTCG[k].nForwardEdge) > 0 && - (vMinusSuper = pTCGroups->pTCG[k].nVertexNumber) >= pBNS->num_atoms && - !((pEdge=pBNS->edge + eMinusSuper)->forbidden && bCheckForbiddenMinus) ) { - - vPlusMinus2 = pEdge->neighbor12 ^ eMinusSuper; - } - if ( bCheckForbiddenPlus && NO_VERTEX == vPlusMinus1 || - bCheckForbiddenMinus && NO_VERTEX == vPlusMinus2 ) { - return NO_VERTEX; - } - return (NO_VERTEX != vPlusMinus1)? vPlusMinus1 : vPlusMinus2; -} -/***********************************************************************************************/ -int bIsUnsatCarbonInASmallRing( inp_ATOM *at2, VAL_AT *pVA, int iat, BFS_Q *pbfsq, int min_ring_size ) -{ - int j, nCurRingSize, nMinRingSize; - if ( min_ring_size < 5 ) { - /* =C= in a small ring */ - if ( at2[iat].valence == 2 && - pVA[iat].cMinRingSize <= 5 && - at2[iat].chem_bonds_valence == 4 ) { - return 1; - } - } else { - if ( at2[iat].valence == 2 && - pVA[iat].cMinRingSize && - pVA[iat].cMinRingSize <= min_ring_size && - at2[iat].chem_bonds_valence == 3 ) { - return 1; - } - nCurRingSize = nMinRingSize = min_ring_size+1; - if ( (at2[iat].valence == 2 || at2[iat].valence == 3) && - at2[iat].chem_bonds_valence == at2[iat].valence+1 ) { - for ( j = 0; j < at2[iat].valence; j ++ ) { - nCurRingSize = is_bond_in_Nmax_memb_ring( at2, iat, j, pbfsq->q, - pbfsq->nAtomLevel, - pbfsq->cSource, (AT_RANK)nMinRingSize /* max ring size */ ); - if ( 0 < nCurRingSize && nCurRingSize < nMinRingSize ) { - nMinRingSize = nCurRingSize; - } - } - return (0 <= nCurRingSize)? (nMinRingSize <= min_ring_size) : nCurRingSize; - } - } - return 0; -} -/***********************************************************************************************/ -int FixMobileHRestoredStructure(ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, - StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, T_GROUP_INFO **ppt_group_info, inp_ATOM **ppat_norm, - inp_ATOM **ppat_prep, INChI *pInChI[], long num_inp, int bHasSomeFixedH, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask) -{ - /*--------- process extra or missing Fixed-H on non-tautomeric atoms ------*/ - /* at2 should be the most recently restored atom, Fixed-H */ - int i, j, k, iat, delta, tot_succes, cur_success, ret = 0; - CMP2MHINCHI c2i; - CMP2MHINCHI *pc2i = &c2i; - - EDGE_LIST AllChargeEdges, CurrEdges, CurrEdges2, CurrEdges3, TautEdges, NFlowerEdges, OtherNFlowerEdges, FixedLargeRingStereoEdges; - EDGE_LIST *pEdgeList = NULL; - - EdgeIndex e; - BNS_EDGE *pe; - Vertex v1, v2, vPlusMinus; - BNS_VERTEX *pv1, *pv2; - - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - - int nNumRunBNS = 0, forbidden_edge_mask_inv = ~forbidden_edge_mask; - - INCHI_HEAPCHK - - AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &CurrEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &NFlowerEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &CurrEdges2, EDGE_LIST_CLEAR ); - AllocEdgeList( &CurrEdges3, EDGE_LIST_CLEAR ); - AllocEdgeList( &OtherNFlowerEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &TautEdges, EDGE_LIST_CLEAR ); - - tot_succes = 0; - - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - /* taut group edges */ - for ( i = 0; i < pTCGroups->num_tgroups; i ++ ) { - pv1 = pBNS->vert + (v1=pTCGroups->pTCG[i].nVertexNumber); /* t-group vertex */ - for ( j = 0; j < pv1->num_adj_edges; j ++ ) { - /* e, pe - tautomeric atom edge; pv2 - endpoint vertex */ - /* Note: pe, pv2, v1 are not used here; they are to show how to traverse t-group */ - pv2 = pBNS->vert + (pe = pBNS->edge + (e=pv1->iedge[j]))->neighbor1; - if ( ret = AddToEdgeList( &TautEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - /* charge and flower edges */ - for ( i = 0; i < pStruct->num_atoms; i ++ ) { - if ( (e=pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - (ret = AddToEdgeList( &AllChargeEdges, e, INC_ADD_EDGE )) ) { - goto exit_function; - } - if ( (e=pVA[i].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - if ( ret = AddToEdgeList( &AllChargeEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - - /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ - if ( pVA[i].cNumValenceElectrons == 5 && !pVA[i].cMetal && /* N, P, As */ - NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e ))) { - - if ( !pBNS->edge[j].forbidden && pBNS->edge[j].flow ) { - if ( ret = AddToEdgeList( &AllChargeEdges, j, INC_ADD_EDGE ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NFlowerEdges, j, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else { - if ( ret = AddToEdgeList( &OtherNFlowerEdges, j, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - - } - } - if ( forbidden_stereo_edge_mask ) { - for ( i = 0; i < pStruct->num_atoms; i ++ ) { - for ( j = 0; j < at2[i].valence; j ++ ) { - if ( pBNS->edge[k = pBNS->vert[i].iedge[j]].forbidden == forbidden_stereo_edge_mask ) { - int nMinRingSize = is_bond_in_Nmax_memb_ring( at2, i, j, pStruct->pbfsq->q, - pStruct->pbfsq->nAtomLevel, - pStruct->pbfsq->cSource, 99 /* max ring size */ ); - if ( 0 < nMinRingSize && (ret = AddToEdgeList( &FixedLargeRingStereoEdges, k, INC_ADD_EDGE ))) { - goto exit_function; - } - } - } - } - } - - INCHI_HEAPCHK - - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - INCHI_HEAPCHK - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - - INCHI_HEAPCHK - - - - - if ( pc2i->nNumTgInChI == 1 && ( pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1 ) && - pc2i->nNumTgDBNMinusRevrs + pc2i->nNumTgNHMinusRevrs == 0 && pc2i->nNumTgOMinusInChI && - !(pTCGroups->pTCG[0].tg_RestoreFlags & TGRF_MINUS_FIRST) ) { - /*----------------------------------------------------*/ - /* case 01: restored has -O(-) and does not have N(-) */ - /* endpoints defined by the original InChI */ - /* restored has single taut. group or more */ - /* tautomeric endpoints. */ - /* Solution: move (-) from endp. -O(-) to endpoints N */ - /*----------------------------------------------------*/ - pTCGroups->pTCG[0].tg_RestoreFlags |= TGRF_MINUS_FIRST; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - if ( pc2i->nNumTgInChI == 1 && ( pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1 ) && - pc2i->nNumTgDBNMinusRevrs + pc2i->nNumTgNHMinusRevrs == 0 && pc2i->nNumTgOMinusInChI == 0 ) { - /*-------------------------------------------------------*/ - /* case 02: restored has no -O(-) and does not have N(-) */ - /* restored has single taut. group or more */ - /* tautomeric endpoints. */ - /* Solution: >N-AB=N- => >N(+)=AB-NH- (add H(+)) */ - /* Solution: >N-AB=NH => >N(+)=AB-NH2 (add H(+)) */ - /* SB_N_III DB_N_III */ - /*-------------------------------------------------------*/ - int iat_SB_N_III[MAX_DIFF_MOBH], iat_DB_N_III[MAX_DIFF_MOBH]; - int num_SB_N_III = 0, num_DB_N_III = 0, k1, k2; - CurrEdges.num_edges = 0; - cur_success = 0; - for ( i = 0; i < pStruct->num_atoms; i ++ ) { - iat = i; - if ( pVA[iat].cNumValenceElectrons == 5 && pVA[i].cPeriodicRowNumber == 1 && - !at2[iat].endpoint && !at2[iat].charge && !at2[iat].radical ) { - if ( num_DB_N_III < MAX_DIFF_MOBH && !at2[iat].num_H && - at2[iat].valence == 2 && - at2[iat].chem_bonds_valence == 3 && - !at2[iat].sb_parity[0] && /* do not eliminate stereobonds */ - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pBNS->edge[e].cap && !pBNS->edge[e].flow ) { - /* -N= */ - iat_DB_N_III[ num_DB_N_III ++ ] = iat; - } else - if ( num_DB_N_III < MAX_DIFF_MOBH && 1 == at2[iat].num_H && - at2[iat].valence == 1 && - at2[iat].chem_bonds_valence == 2 && - !at2[iat].sb_parity[0] && /* do not eliminate stereobonds */ - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pBNS->edge[e].cap && !pBNS->edge[e].flow ) { - /* -N= */ - iat_DB_N_III[ num_DB_N_III ++ ] = iat; - } else - if ( num_SB_N_III < MAX_DIFF_MOBH && !at2[iat].num_H && - at2[iat].valence == 3 && - at2[iat].chem_bonds_valence == 3 && - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pBNS->edge[e].cap && pBNS->edge[e].flow) { - /* -N< */ - iat_SB_N_III[ num_SB_N_III ++ ] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - } - if ( num_DB_N_III && num_SB_N_III ) { - EdgeIndex ieMinus; - BNS_EDGE *peMinus; - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - for ( i = 0; i < num_DB_N_III && !cur_success; i ++ ) { - iat = iat_DB_N_III[ i ]; - e = pBNS->edge[k1=pBNS->vert[iat].iedge[0]].flow? k1 : - pBNS->edge[k2=pBNS->vert[iat].iedge[1]].flow? k2 : NO_VERTEX; - if ( e == NO_VERTEX ) { - continue; /* should not happen */ - } - ieMinus = pVA[iat].nCMinusGroupEdge-1; - peMinus = pBNS->edge + ieMinus; - pe = pBNS->edge + e; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; /* fix double bond */ - peMinus->forbidden &= forbidden_edge_mask_inv; /* allow negative charge */ - delta = 1; - pe->flow -= delta; /* remove (-) from AB-O(-) */ - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 2 ) { - /* Added (-)charge -N= and (+) to -N< => nDeltaCharge == 2 */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 01 */ - - /* eliminate (-) charge and add H */ - pv1 = pBNS->vert + (v1 = peMinus->neighbor1); /* atom */ - pv2 = pBNS->vert + (v2 = peMinus->neighbor12 ^ v1);/* (=) vertex */ - /* effectively eliminate (-) edge by setting its cap=flow= 0 */ - peMinus->cap --; - peMinus->flow --; - pv1->st_edge.cap --; - pv1->st_edge.flow --; - pv2->st_edge.cap --; - pv2->st_edge.flow --; - pBNS->tot_st_flow -= 2; - pBNS->tot_st_cap -= 2; - /* add H */ - pStruct->at[iat].num_H ++; - /* register total charge increase */ - pTCGroups->total_charge ++; - pStruct->nNumRemovedProtonsByRevrs -= 1; - } - } else { - pe->forbidden &= forbidden_edge_mask_inv; - peMinus->forbidden |= forbidden_edge_mask; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - CurrEdges.num_edges = 0; /* clear current edge list */ - - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - } - if ( pc2i->nNumTgInChI == 1 && ( pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1 ) && /* ADP */ - pc2i->nNumTgMInChI == 0 && pc2i->nNumTgNInChI && pc2i->nNumTgOInChI ) { - /*-------------------------------------------------------*/ - /* case 03: restored has N and O endpoints, no (-) endp */ - /* case 04: original has single taut. group or more */ - /* tautomeric endpoints. */ - /* Solution: 1. Move taut attachment from O to N */ - /* Solution: 2. Replace the attachment with (-) */ - /* SB_N_III DB_N_III */ - /*-------------------------------------------------------*/ - /* - int iat_SB_N_III[MAX_DIFF_MOBH], iat_DB_N_III[MAX_DIFF_MOBH]; - int num_SB_N_III = 0, num_DB_N_III = 0, k1, k2, - */ - int itg, j1, j2, bAction = 0; - BNS_VERTEX *pTg, *pvEndp, *pvEndp2, *pvCent; - Vertex vEndp, vEndp2, vCent; - BNS_EDGE *peTg, *peTg2, *peCent1, *peCent2; - EdgeIndex eTg, eTg2; - - CurrEdges.num_edges = 0; - CurrEdges2.num_edges = 0; - cur_success = 0; - - /* 1st attempt: -NH-=O => -N(-)-=O or -N=-OH => -N(-)-=O */ - for ( itg = 0; itg < pTCGroups->num_tgroups && !cur_success; itg ++ ) { - pTg = pBNS->vert + pTCGroups->pTCG[itg].nVertexNumber; - for ( i = 0; i < pTg->num_adj_edges && !cur_success; i ++ ) { - pvEndp = pBNS->vert + (vEndp = (peTg = pBNS->edge + (eTg=pTg->iedge[i]))->neighbor1); - eTg2 = -1; - if ( pVA[vEndp].cNumValenceElectrons == 6 && peTg->cap ) { - /* endpoint -OH or =O found; search for a possible centerpoint */ - for ( j1 = 0; j1 < at2[vEndp].valence && eTg2 < 0; j1 ++ ) { - peCent1 = pBNS->edge + pvEndp->iedge[j1]; /* edge from O to a centerpoint */ - pvCent = pBNS->vert + (vCent = peCent1->neighbor12 ^ vEndp); /* centerpoint */ - if ( at2[vCent].endpoint || !peCent1->cap || - peCent1->flow + (peTg->cap == peTg->flow) != 1 ) { - continue; - } - /* search for another endpoint, N, around vCent */ - for ( j2 = 0; j2 < at2[vCent].valence; j2 ++ ) { - peCent2 = pBNS->edge + pvCent->iedge[j2]; - pvEndp2 = pBNS->vert + (vEndp2 = peCent2->neighbor12 ^ vCent); - if ( !peCent2->cap || peCent2->flow+peCent1->flow != 1 || - at2[vEndp2].endpoint != itg+1 || - pVA[vEndp2].cNumValenceElectrons != 5 || - 0 > (j=pVA[vEndp2].nTautGroupEdge-1) || - (peTg2 = pBNS->edge + j)->forbidden || - peCent2->flow + (peTg2->cap == peTg2->flow) != 1 ) { - continue; - } - eTg2 = j; - break; /* found OH-C=N- or O=C-NH- */ - } - } - } - if ( eTg2 >= 0 ) { - /*-------------------------------------------- - tg tg - eTg //\ eTg2 eTg / \\eTg2 - // \ / \\ - vEndp HO--C==N vEndp2 --> vEndp O==C--NH vEndp2 - ^ ^ ^ ^ ^ ^ - eCent1 | eCent2 eCent1 | eCent2 - vCent vCent - - additional action: -OH-C=N- => O=C-NH- - -------------------------------------------*/ - if ( 0 == peTg->cap - peTg->flow && 1 == peTg2->cap - peTg2->flow && - 0 == peCent1->flow && 1 == peCent2->flow ) { - peTg->flow --; /* 03 prepare */ - peTg2->flow ++; - peCent2->flow --; - peCent1->flow ++; - bAction |= 1; /* switched H position */ - } - if ( 1 == peTg->cap - peTg->flow && 0 == peTg2->cap - peTg2->flow && - 1 == peCent1->flow && 0 == peCent2->flow ) { - /* replace -NH- with -N(-)- */ - pTCGroups->pTCG[itg].tg_num_H --; - pTCGroups->pTCG[itg].tg_num_Minus ++; - pTCGroups->pTCG[itg].tg_RestoreFlags |= TGRF_MINUS_FIRST; - pTCGroups->pTCG[itg].tg_set_Minus = vEndp2+1; - pStruct->ti.t_group[itg].num[1] ++; /* increment number of (-), keep number of taut attachments */ - pTCGroups->total_charge --; - pTCGroups->tgroup_charge --; - pStruct->nNumRemovedProtonsByRevrs += 1; - bAction |= 2; /* single NH (at2[vEndp2]) replaced with N(-) */ - cur_success ++; /* 03/04 */ - } - } - } - } - - if ( 0 == pc2i->nNumTgNHInChI+ pc2i->nNumTgNH2InChI && pc2i->nNumTgOHInChI && !cur_success ) { - /* transfer an attachement to N */ - for ( itg = 0; itg < pTCGroups->num_tgroups; itg ++ ) { - pTg = pBNS->vert + pTCGroups->pTCG[itg].nVertexNumber; - for ( i = 0; i < pTg->num_adj_edges; i ++ ) { - pvEndp = pBNS->vert + (vEndp = (peTg = pBNS->edge + (eTg=pTg->iedge[i]))->neighbor1); - if ( pVA[vEndp].cNumValenceElectrons == 6 && - at2[vEndp].valence == at2[vEndp].chem_bonds_valence && - peTg->flow && peTg->flow == peTg->cap ) { - /* endpoint -OH found; save the tautomeric group edge */ - if ( ret = AddToEdgeList( &CurrEdges, eTg, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else - if ( pVA[vEndp].cNumValenceElectrons == 5 && - pVA[vEndp].cPeriodicRowNumber == 1 && - at2[vEndp].valence + 1 == at2[vEndp].chem_bonds_valence && - peTg->cap && peTg->flow + 1 == peTg->cap ) { - /* endpoint -N= or =NH found, check for -N=-OH */ - e = -1; - for ( j1 = 0; j1 < at2[vEndp].valence && e < 0; j1 ++ ) { - peCent1 = pBNS->edge + pvEndp->iedge[j1]; - if ( peCent1->flow == 1 ) { - /* double bond */ - pvCent = pBNS->vert + (vCent = peCent1->neighbor12 ^ vEndp); - if ( at2[vCent].endpoint ) - continue; - for ( j2 = 0; j2 < at2[vCent].valence; j2 ++ ) { - peCent2 = pBNS->edge + pvCent->iedge[j2]; - pvEndp2 = pBNS->vert + (vEndp2 = peCent2->neighbor12 ^ vCent); - if ( peCent2->flow || at2[vEndp2].endpoint != itg+1 || - pVA[vEndp2].cNumValenceElectrons != 6 || - 0 >= (e=pVA[vEndp2].nTautGroupEdge-1) || - pBNS->edge[e].forbidden || !pBNS->edge[e].flow ) { - e = -1; - continue; - } - /*********************/ - /* found -N=X-OH */ - /* vEndp ^ vEndp2 */ - /* vCent */ - /*********************/ - /* save this -OH taut edge */ - if ( ret = AddToEdgeList( &CurrEdges2, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - break; - } - } - } - if ( e < 0 && (ret = AddToEdgeList( &CurrEdges, eTg, INC_ADD_EDGE )) ) { - goto exit_function; - } - } - } - } - /* rearrange the flows */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - SetForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - SetForbiddenEdgeMask( pBNS, &CurrEdges2, forbidden_edge_mask ); - pEdgeList = CurrEdges2.num_edges? &CurrEdges2 : CurrEdges.num_edges? &CurrEdges : NULL; - - for ( i = 0; pEdgeList && i < pEdgeList->num_edges && !cur_success; i ++ ) { - pe = pBNS->edge + pEdgeList->pnEdges[i]; /* pe->flow = 1 <=> -OH */ - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); /* -OH atom */ - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); /* t-group vertex */ - /* locate the t-group */ - for ( itg = 0; itg < pTCGroups->num_tgroups; itg ++ ) { - if ( v2 == pTCGroups->pTCG[itg].nVertexNumber ) { - break; - } - } - if ( itg == pTCGroups->num_tgroups ) { - /* tgroup not found -- should not happen */ - continue; - } - - delta = 1; - pe->flow -= delta; /* add one attachment to */ - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 2 ) { - /* Added (-)charge -N= and (+) to -N< => nDeltaCharge == 2 */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 03 */ - /* replace -NH- with -N(-)- */ - pTCGroups->pTCG[itg].tg_num_H --; - pTCGroups->pTCG[itg].tg_num_Minus ++; - pTCGroups->pTCG[itg].tg_RestoreFlags |= TGRF_MINUS_FIRST; - pStruct->ti.t_group[itg].num[1] ++; - pTCGroups->total_charge --; - pTCGroups->tgroup_charge --; - pStruct->nNumRemovedProtonsByRevrs += 1; - bAction |= 4; /* H in the 1st available NH was replaced with (-) */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - } else - if ( pc2i->nNumTgNHInChI+ pc2i->nNumTgNH2InChI && pc2i->nNumTgOInChI && !cur_success ) { - /* change an attachement to N from H to (-) */ - for ( itg = 0; itg < pTCGroups->num_tgroups && !cur_success; itg ++ ) { - pTg = pBNS->vert + pTCGroups->pTCG[itg].nVertexNumber; - for ( i = 0; i < pTg->num_adj_edges && !cur_success; i ++ ) { - pvEndp2 = pBNS->vert + (vEndp2 = (peTg = pBNS->edge + pTg->iedge[i])->neighbor1); - if ( pVA[vEndp2].cNumValenceElectrons == 5 && pVA[vEndp2].cPeriodicRowNumber == 1 && - at2[vEndp2].valence == at2[vEndp2].chem_bonds_valence && - peTg->flow && peTg->flow == peTg->cap ) { - /* endpoint -NHn found; change its charge */ - cur_success ++; /* 04 */ - /* replace -NH- with -N(-)- */ - pTCGroups->pTCG[itg].tg_num_H --; - pTCGroups->pTCG[itg].tg_num_Minus ++; - pTCGroups->pTCG[itg].tg_RestoreFlags |= TGRF_MINUS_FIRST; - pTCGroups->pTCG[itg].tg_set_Minus = vEndp2 + 1; - pStruct->ti.t_group[itg].num[1] ++; - pTCGroups->total_charge --; - pTCGroups->tgroup_charge --; - pStruct->nNumRemovedProtonsByRevrs += 1; - bAction |= 8; /* manually set (-) charge to NH atom, vEndp2 */ - } - } - } - } - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( pStruct->One_ti.num_t_groups == 1 && pStruct->One_ti.t_group[0].num[1] ) { - /* this method did not work: no alt path from N(-) to =O */ - itg = 0; - if ( bAction & (8 | 2 ) ) { - /* roll back NH -> N(-) replacement; H move from OH to N is not undone */ - pTCGroups->pTCG[itg].tg_num_H ++; - pTCGroups->pTCG[itg].tg_num_Minus --; - pTCGroups->pTCG[itg].tg_RestoreFlags &= ~TGRF_MINUS_FIRST; - pTCGroups->pTCG[itg].tg_set_Minus = 0; - pStruct->ti.t_group[itg].num[1] --; - pTCGroups->total_charge ++; - pTCGroups->tgroup_charge ++; - pStruct->nNumRemovedProtonsByRevrs -= 1; - cur_success --; - } else - if ( bAction & 4 ) { - pTCGroups->pTCG[itg].tg_num_H ++; - pTCGroups->pTCG[itg].tg_num_Minus --; - pTCGroups->pTCG[itg].tg_RestoreFlags &= ~TGRF_MINUS_FIRST; - pStruct->ti.t_group[itg].num[1] --; - pTCGroups->total_charge ++; - pTCGroups->tgroup_charge ++; - pStruct->nNumRemovedProtonsByRevrs -= 1; - cur_success --; - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pc2i->nNumTgInChI == 1 && ( pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1 ) && /* ADP */ - pc2i->nNumTgMInChI == 0 && (pc2i->nNumTgNInChI || pc2i->nNumTgOInChI) && - NO_VERTEX != (vPlusMinus = GetPlusMinusVertex( pBNS, pTCGroups, 1, 1 )) ) { - /*---------------------------------------------------------------------------*/ - /* case 05: restored has N endpoints, no (-) endpoints */ - /* original has single taut. group or more */ - /* tautomeric endpoints. */ - /* Solution: Find -N< and allow (+) charge change */ - /* Fix all charges and taut attachments exept */ - /* =N- and =O (taut. endpoints) */ - /* Increment st_edge.cap on (+/-) vertex => add (+) charge to -N< */ - /* Increment tot. charge in other places */ - /* Increment t-group st_edge.cap */ - /* Run BNS */ - /* */ - /* (+/-)* (+/-) Result: */ - /* | || */ - /* | || - Added (+) to -N< */ - /* (+)super (+)super - Added attachment point to O */ - /* || | */ - /* || => | To make this attachment H, */ - /* (Y) (Y) increment */ - /* | || pTCGroups->pTCG[itg].tg_num_H */ - /* | || */ - /* (+)hetero (+)hetero Technical details: */ - /* \\ \ increase capacities of */ - /* N N(+) edges to (+/-) otherwise */ - /* | || flow may not be able to */ - /* *(t)--O=R. (t)==O-R. increase */ - /* */ - /* */ - /*---------------------------------------------------------------------------*/ - int itg; - BNS_VERTEX *pTg, *pvEndp; - Vertex vEndp, vTg; - BNS_EDGE *peTg; - EdgeIndex eTg; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - - CurrEdges.num_edges = 0; - CurrEdges2.num_edges = 0; - cur_success = 0; - /* find -N< and non-taut =N- or =O */ - for ( i = 0; i < pStruct->num_atoms; i ++ ) { - iat = nCanon2AtnoRevrs[i]; - /* -N< */ - if ( !at2[iat].endpoint && !at2[iat].charge && !at2[iat].radical && !at2[iat].num_H && - pVA[i].cNumValenceElectrons == 5 && pVA[i].cPeriodicRowNumber == 1 && - 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && pBNS->edge[e].flow && !pBNS->edge[e].forbidden) { - - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( !CurrEdges.num_edges ) { - goto exit_case_05; - } - /* find taut -N= and =O */ - for ( itg = 0; itg < pTCGroups->num_tgroups && !cur_success; itg ++ ) { - CurrEdges2.num_edges = 0; - pTg = pBNS->vert + (vTg = pTCGroups->pTCG[itg].nVertexNumber); - for ( i = 0; i < pTg->num_adj_edges; i ++ ) { - pvEndp = pBNS->vert + (vEndp = (peTg = pBNS->edge + (eTg=pTg->iedge[i]))->neighbor1); - if ( at2[vEndp].charge || at2[vEndp].radical || peTg->cap - peTg->flow != 1 ) { - continue; - } - /* t-group edges to -N= and =O */ - if ( ret = AddToEdgeList( &CurrEdges2, eTg, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - if ( !CurrEdges2.num_edges ) { - goto exit_case_05; - } - /* fix all charge edges except -N< and all taut. edges except =O and =N- */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - SetForbiddenEdgeMask( pBNS, &TautEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges2, forbidden_edge_mask ); - delta = 1; - /* Increment st_edge.cap on (+/-) vertex */ - pBNS->vert[vPlusMinus].st_edge.cap += delta; - /* Increment st_edge.cap on t-group */ - pTg->st_edge.cap += delta; - /* total cap count */ - pBNS->tot_st_cap += 2*delta; - - v1 = vPlusMinus; - v2 = vTg; - - /* increase capacities of edges to Y */ - for ( i = 0; i < pBNS->vert[vPlusMinus].num_adj_edges; i ++ ) { - j = pBNS->edge[pBNS->vert[vPlusMinus].iedge[i]].neighbor12 ^ vPlusMinus; - for ( k = 0; k < pBNS->vert[j].num_adj_edges; k ++ ) { - pBNS->edge[pBNS->vert[j].iedge[k]].cap += delta; - } - } - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Added (+)charge to -N< => nDeltaCharge == 1 */ - /* Flow change on pe (-)charge edge (atom B-O(-)) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 01 */ - /* update bookkeeping */ - pTCGroups->total_charge += delta; - pTCGroups->pTCG[itg].edges_cap += delta; - pTCGroups->pTCG[itg].tg_num_H += delta; - pStruct->nNumRemovedProtonsByRevrs -= delta; - } - } else { - pBNS->vert[vPlusMinus].st_edge.cap -= delta; - pTg->st_edge.cap -= delta; - /*pTCGroups->pTCG[itg].edges_cap -= delta;*/ /* ???bug??? - commented out 2006-03-22 */ - pBNS->tot_st_cap -= 2*delta; - /* decrease capacities of edges to Y */ - for ( i = 0; i < pBNS->vert[vPlusMinus].num_adj_edges; i ++ ) { - j = pBNS->edge[pBNS->vert[vPlusMinus].iedge[i]].neighbor12 ^ vPlusMinus; - for ( k = 0; k < pBNS->vert[j].num_adj_edges; k ++ ) { - pBNS->edge[pBNS->vert[j].iedge[k]].cap -= delta; - } - } - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &TautEdges, forbidden_edge_mask ); - } - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - -exit_case_05:; - } - - while ( pc2i->nNumDiffMobH && pc2i->nChargeMobHRevrs > pc2i->nChargeMobHInChI ) { - /*----------------------------------------------------*/ - /* case 06: restored has extra H attached to -O(-) */ - /* while the chrge should be on C, most pro- */ - /* bably in a small ring.ut. group or more */ - /* tautomeric endpoints. */ - /* Solution: move (-) from O to C */ - /*----------------------------------------------------*/ - int iO, mode; - EdgeIndex e2; - BNS_EDGE *pe2; - cur_success = 0; - for ( i = 0; !cur_success && i < pc2i->len_c2at; i ++ ) { - - if ( pc2i->c2at[i].nMobHRevrs == pc2i->c2at[i].nMobHInChI + 1 && - pc2i->c2at[i].nNumHRevrs == pc2i->c2at[i].nMobHInChI && - !pc2i->c2at[i].endptInChI && !pc2i->c2at[i].endptRevrs && - at2[iO = pc2i->c2at[i].atomNumber].charge == -1 && - 0 <= (e=pVA[iO].nCMinusGroupEdge-1) && (pe=pBNS->edge+e)->flow ) { - - /* try suitable atoms C */ - /* first look for =C= in a small ring */ - - for( mode = 4; !cur_success && mode <= 8; mode ++ ) { - - if ( mode == 8 ) - mode = 99; - - for ( iat = 0; !cur_success && iat < pStruct->num_atoms; iat ++ ) { - - if ( !at2[iat].charge && !at2[iat].radical && - pVA[iat].cNumValenceElectrons == 4 && - 0 <= (e2=pVA[iat].nCMinusGroupEdge-1) && !(pe2=pBNS->edge+e2)->flow && - 0 < bIsUnsatCarbonInASmallRing( at2, pVA, iat, pStruct->pbfsq, mode ) ) { - - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - /* allow negative charge on the chosen carbon */ - pe2->forbidden &= forbidden_edge_mask_inv; - - delta = 1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Added (-)charge to unsaturated C => nDeltaCharge == 2 */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 01 */ - tot_succes += cur_success; - } - } else { - pe->forbidden |= forbidden_edge_mask; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - } - } - } - } - if ( cur_success ) { - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } else { - break; - } - } - if ( pc2i->len_c2at && pc2i->nChargeMobHRevrs > pc2i->nChargeMobHInChI ) { - /*------------------------------------------------------------------*/ - /* case 07: -NO2 are to be tautomeric but they are not AND */ - /* InChI has a SINGLE tautomeric group */ - /* */ - /* (-)O (-)O */ - /* Solution: convert \ \ */ - /* N-X=...-Z(-) => N(+)=X- ...=Z */ - /* // / */ - /* O (-)O */ - /* */ - /* O O */ - /* or \\ \\ */ - /* N-X=...-Z(-) => N=X- ...=Z */ - /* // / */ - /* O (-)O */ - /* */ - /* */ - /* (a) move (-) from other tautomeric atom to O in O=N-X */ - /* or from other atom that has to be tautomeric */ - /* but is not */ - /* (b) create (+) [ion pair creation] on N as in */ - /* */ - /* OH OH */ - /* / / */ - /* -C=N => =C-N(+) */ - /* \\ \\ */ - /* O O */ - /* */ - /*------------------------------------------------------------------*/ - int num_DB_O = 0; - short iat_DB_O[MAX_DIFF_FIXH], iat_NO2[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - /* - AT_NUMB *nAtno2CanonRevrs = pStruct->nAtno2Canon[0]; - */ - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[0] && - pStruct->pOne_norm_data[0]->at)? pStruct->pOne_norm_data[0]->at : NULL; - - int iN, one_success; - BNS_EDGE *peDB_O_Minus; - int neigh, nNumO, nNumOthers; -#define CHG_SET_WRONG_TAUT_N 0 -#define CHG_SET_WRONG_TAUT_O 1 -#define CHG_SET_WRONG_TAUT_ALL 2 -#define CHG_LAST_SET 2 /* the last index in trying */ -#define CHG_SET_O_FIXED 3 -#define CHG_SET_NUM 4 - EDGE_LIST ChangeableEdges[CHG_SET_NUM]; - memset( ChangeableEdges, 0, sizeof(ChangeableEdges) ); - /* equivalent to AllocEdgeList( &EdgeList, EDGE_LIST_CLEAR ); */ - /* - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - */ - CurrEdges.num_edges = 0; /* clear current edge list */ - cur_success = 0; - for ( i = 0; i < pStruct->num_atoms; i ++ ) { - iat = nCanon2AtnoRevrs[i]; - if ( /* orig. InChI info: taut in orig. InChI =O located in -NO2 that is not taut in Reconstructed InChI */ - num_DB_O < MAX_DIFF_FIXH && - pVA[iat].cNumValenceElectrons == 6 /* O, S, Se, Te */ && - (!at2[iat].endpoint /*|| pc2i->c2at[i].nMobHInChI*/) && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - at2[iat].num_H == 0 && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ - /* reversed structure info: */ - !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint) /*|| pc2i->c2at[i].nMobHRevrs*/ && - !at2[iat].charge && - at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 && - /* find whether it belongs to NO2 */ - pVA[iN=at2[iat].neighbor[0]].cNumValenceElectrons == 5 && - at2[iN].valence == 3 && (at2[iN].charge == 0 || at2[iN].charge == 1) && - at2[iN].chem_bonds_valence == 5 - at2[iN].charge ) { - /* find the second O */ - nNumO = nNumOthers = 0; - for ( k = 0; k < at2[iN].valence; k ++ ) { - neigh = at2[iN].neighbor[k]; - if ( neigh == iat ) { - continue; - } - if ( pVA[neigh].cNumValenceElectrons == 6 && - !at2[neigh].endpoint && - !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[neigh].endpoint) && - at2[neigh].valence == 1 && at2[neigh].num_H == 0 && - at2[neigh].radical == 0 && (at2[neigh].charge == 0 || at2[neigh].charge == -1) && - at2[neigh].chem_bonds_valence - at2[neigh].charge == 2) { - nNumO ++; - } else - if ( at2[iN].bond_type[k] == BOND_TYPE_SINGLE && - at2[neigh].valence > 1 && - at2[neigh].valence < at2[neigh].chem_bonds_valence ) { - nNumOthers ++; - } - } - if ( nNumO != 1 || nNumOthers != 1 ) { - continue; - } - for ( k = 0; k < num_DB_O; k ++ ) { - if ( iat_NO2[k] == iN ) { - break; - } - } - if ( k == num_DB_O ) { - iat_NO2[num_DB_O] = iN; - iat_DB_O[num_DB_O ++] = iat; - } - /* save the =O (-)-edge to avoid interference */ - if ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e, INC_ADD_EDGE ) ) { - goto exit_case_07; - } - } - } - if ( num_DB_O ) { - /* search for falsely tautomeric negatively charged atoms N and O */ - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( pc2i->c2at[i].endptRevrs && !pc2i->c2at[i].endptInChI && - pc2i->c2at[i].nAtChargeRevrs == - 1 && - 0 <= (e=pVA[iat].nCMinusGroupEdge-1) && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && - 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e ) ) { - if ( pc2i->c2at[i].nValElectr == 6 ) { - if (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_WRONG_TAUT_O], e, INC_ADD_EDGE ) ) { - goto exit_case_07; - } - } else - if ( pc2i->c2at[i].nValElectr == 5 ) { - if (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_WRONG_TAUT_N], e, INC_ADD_EDGE ) ) { - goto exit_case_07; - } - } - if (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_WRONG_TAUT_ALL], e, INC_ADD_EDGE ) ) { - goto exit_case_07; - } - } - } - /* ------- finally, try to move charges from O=N --------------*/ - for ( i = 0; i < num_DB_O; i ++ ) { - int nDeltaChargeExpected; - one_success = 0; - delta = 1; - iat = iat_DB_O[i]; - peDB_O_Minus = pBNS->edge + (pVA[iat].nCMinusGroupEdge-1); - pe = pBNS->edge + pBNS->vert[iat].iedge[0]; - - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - for ( k = 0; !one_success && k <= CHG_LAST_SET; k ++ ) { - if ( !ChangeableEdges[k].num_edges ) { - continue; - } - nDeltaChargeExpected = 0; - - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &ChangeableEdges[k], forbidden_edge_mask ); - /* allow (-) charge to move to N=O */ - peDB_O_Minus->forbidden &= forbidden_edge_mask_inv; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && - nDeltaCharge == nDeltaChargeExpected ) { - /* Move (-) charge to =O and remove it an endpoint => nDeltaCharge == 0 */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - one_success ++; /* 07 */ - } - } - INCHI_HEAPCHK - } - cur_success += one_success; - - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - pe->forbidden &= forbidden_edge_mask_inv; - - if ( !one_success ) { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - } - } -exit_case_07: - for ( i = 0; i < CHG_SET_NUM; i ++ ) { - AllocEdgeList( &ChangeableEdges[i], EDGE_LIST_FREE ); - } - - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } -#undef CHG_SET_NOOH -#undef CHG_SET_WRONG_TAUT -#undef CHG_SET_TAUT -#undef CHG_LAST_SET -#undef CHG_SET_O_FIXED -#undef CHG_SET_NUM - } - - - -exit_function: - AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &CurrEdges, EDGE_LIST_FREE ); - AllocEdgeList( &CurrEdges2, EDGE_LIST_FREE ); - AllocEdgeList( &CurrEdges3, EDGE_LIST_FREE ); - AllocEdgeList( &NFlowerEdges, EDGE_LIST_FREE ); - AllocEdgeList( &OtherNFlowerEdges, EDGE_LIST_FREE ); - AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_FREE ); - AllocEdgeList( &TautEdges, EDGE_LIST_FREE ); - - - return ret; -} - -#endif diff --git a/INCHI-1-SRC/INCHI/common/ichirvr6.c b/INCHI-1-SRC/INCHI/common/ichirvr6.c deleted file mode 100644 index 35d1e54..0000000 --- a/INCHI-1-SRC/INCHI/common/ichirvr6.c +++ /dev/null @@ -1,1318 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - -/*#define CHECK_WIN32_VC_HEAP*/ -#include "mode.h" - -#if ( READ_INCHI_STRING == 1 ) - -#include "ichi.h" -#include "ichitime.h" - -#include "inpdef.h" -#include "ichimain.h" -#include "ichierr.h" -#include "incomdef.h" -#include "ichiring.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "util.h" - -#include "ichicomp.h" -#include "ichister.h" - -#include "ichi_bns.h" - -#include "strutil.h" - -#include "ichirvrs.h" - - -#define INC_ADD_EDGE 64 -/***********************************************************************************************/ -int FixRestoredStructureStereo( INCHI_MODE cmpInChI, ICR *icr, INCHI_MODE cmpInChI2, ICR *icr2, - ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, - StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, T_GROUP_INFO **ppt_group_info, inp_ATOM **ppat_norm, - inp_ATOM **ppat_prep, INChI *pInChI[], long num_inp, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask) -{ - /*--------- process extra or missing Fixed-H on non-tautomeric atoms ------*/ - /* at2 should be the most recently restored atom, Fixed-H */ - int i, j, k, delta, tot_succes, max_success, cur_success, ret = 0; - int err, iOrigInChI, iRevrInChI; - int j12, v1, v2, e, vRad; - BNS_VERTEX *pv1, *pv2, *pvRad; - BNS_EDGE *pe, *peRad; - EDGE_LIST AllChargeEdges, CurrEdges, NFlowerEdges, OtherNFlowerEdges, FixedStereoEdges, AllRadList; - EDGE_LIST TautMinusEdges[2]; /* 0 -> O & O(+), 1=> N & N(+) */ - - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - INChI_Stereo *pStereoInChI, *pStereo2InChI, *pStereoRevrs, *pStereo2Revrs; - - /* Stereo */ - - /* currently being processed layer */ - pStereoInChI = (pInChI[0]->StereoIsotopic && - pInChI[0]->StereoIsotopic->nNumberOfStereoBonds + - pInChI[0]->StereoIsotopic->nNumberOfStereoCenters)? - pInChI[0]->StereoIsotopic : pInChI[0]->Stereo; - - /* mobile-H layer in case of Fixed-H */ - pStereo2InChI = (pStruct->bMobileH == TAUT_YES || !pInChI[1] || - !pInChI[1]->nNumberOfAtoms || pInChI[1]->bDeleted)? - NULL: - (pInChI[1]->StereoIsotopic && - pInChI[1]->StereoIsotopic->nNumberOfStereoBonds + - pInChI[1]->StereoIsotopic->nNumberOfStereoCenters)? - pInChI[1]->StereoIsotopic : - pInChI[1]->Stereo; - - /* currently being processed layer */ - pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; - - /* mobile-H layer in case of Fixed-H */ - pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || - !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? - NULL: - (pStruct->pOneINChI[1]->StereoIsotopic && - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[1]->StereoIsotopic : - pStruct->pOneINChI[1]->Stereo; - - INCHI_HEAPCHK - - AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &CurrEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &NFlowerEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &OtherNFlowerEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &FixedStereoEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &AllRadList, EDGE_LIST_CLEAR ); - - AllocEdgeList( TautMinusEdges+0, EDGE_LIST_CLEAR ); - AllocEdgeList( TautMinusEdges+1, EDGE_LIST_CLEAR ); - - cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - - cmpInChI2 = 0; - - if ( pStruct->bMobileH == TAUT_NON ) { - /* these indexes are used to compare Mobile-H InChI */ - iOrigInChI = (pInChI[1] && pInChI[1]->nNumberOfAtoms && !pInChI[1]->bDeleted)? 1 : 0; - iRevrInChI = (pStruct->pOneINChI[1] &&pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted)? 1 : 0; - } else { - iOrigInChI = 0; - iRevrInChI = 0; - } - - memset ( icr2, 0, sizeof(*icr2) ); - if ( iRevrInChI || iOrigInChI ) { - /* additional mobile-H compare in case of Fixed-H */ - cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - } - - - if ( !(cmpInChI & IDIFF_SB) && !(cmpInChI2 & IDIFF_SB) ) { - goto exit_function; - } - /* need to temporarily remove fixing of stereogenic bonds */ - for ( i = 0; i < pStruct->num_atoms; i ++ ) { - pv1 = pBNS->vert + i; - for ( j = 0; j < at2[i].valence; j ++ ) { - pe = pBNS->edge + (e=pv1->iedge[j]); - if ( j == pe->neighbor1 ) { - /* do not store same bond 2 times */ - if ( (pe->forbidden & forbidden_stereo_edge_mask) && - (ret = AddToEdgeList( &FixedStereoEdges, e, INC_ADD_EDGE ) ) ) { - goto exit_function; - } - } - } - } - - - tot_succes = 0; - cur_success = 0; - if ( (cmpInChI & IDIF_SB_MISS) && (!cmpInChI2 || (cmpInChI2 & IDIF_SB_MISS)) && - 0 < (max_success = pBNS->tot_st_cap - pBNS->tot_st_flow) ) { - /*----------------------------------------------------*/ - /* case 01: extra stereogenic bond, radical present */ - /* X=N-O* => X=N=O and eliminate radical */ - /*----------------------------------------------------*/ - int aN; - BNS_VERTEX *pvO, *pvN; - BNS_EDGE *peNO; - - RemoveForbiddenEdgeMask( pBNS, &FixedStereoEdges, forbidden_stereo_edge_mask ); - - for ( i = 0; i < icr->num_sb_in2_only && cur_success < max_success; i ++ ) { - j12 = icr->sb_in2_only[i]; - pv1 = pBNS->vert + (v1 = pStereoInChI->nBondAtom1[j12]-1); - pv2 = pBNS->vert + (v2 = pStereoInChI->nBondAtom2[j12]-1); - for ( k = 0; k < at2[v1].valence; k ++ ) { - pe = pBNS->edge + (e = pv1->iedge[k]); - if ( v2 == (pe->neighbor12 ^ v1) ) - break; /* the edge has been found */ - } - if ( k == at2[v1].valence ) { - ret = RI_ERR_SYNTAX; - goto exit_function; - } - /* check v1 */ - pv1->st_edge.cap --; - pv1->st_edge.flow --; - pv2->st_edge.flow --; - pe->flow --; /* new radical on v2 */ - vRad = NO_VERTEX; - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - pv1->st_edge.cap ++; - pv1->st_edge.flow ++; - pv2->st_edge.flow ++; - pe->flow ++; /* remove new radical on v2 */ - - if ( ret == 1 /*&& !nDeltaH*/ && !nDeltaCharge && (v2 == vPathStart || v2 == vPathEnd) ) { - vRad = (v2 == vPathStart)? vPathEnd : vPathStart; - } else { - pv2->st_edge.cap --; - pv2->st_edge.flow --; - pv1->st_edge.flow --; - pe->flow --; /* new radical on v1 */ - vRad = NO_VERTEX; - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - pv2->st_edge.cap ++; - pv2->st_edge.flow ++; - pv1->st_edge.flow ++; - pe->flow ++; /* remove new radical on v1 */ - if ( ret == 1 /*&& !nDeltaH*/ && !nDeltaCharge && (v1 == vPathStart || v1 == vPathEnd) ) { - vRad = (v1 == vPathStart)? vPathEnd : vPathStart; - } - } - if ( vRad == NO_VERTEX ) { - continue; /* radical did not affect this bond */ - } - pvRad = pBNS->vert + vRad; - /* detect =N-O* */ - if ( pVA[vRad].cNumValenceElectrons == 6 && at2[vRad].valence == 1 && - (peRad = pBNS->edge + pvRad->iedge[0])->flow == 0 && - pVA[aN = peRad->neighbor12 ^ vRad].cNumValenceElectrons == 5 && - at2[aN].valence == 2 ) { - /*------------------------------------------------------------ - Fix Metal disconnection/normalization inconsistency : - disconnected restored - R=N(+)-M R=N--M R=N + M R=N + M - | -> || -> || -> | - O(-) O O O* <- radical - - The correct R=N + M(+) - disconnection | - would be this: O(-) - --------------------------------------------------------------*/ - pvN = pBNS->vert + aN; - pvO = pvRad; - peNO = peRad; - - /* N-O* => N=O */ - peNO->flow ++; - pvO->st_edge.flow ++; - pvN->st_edge.cap ++; - pvN->st_edge.flow ++; - pBNS->tot_st_cap += 1; - pBNS->tot_st_flow += 2; - cur_success ++; - } else { - /* all other radicals that affect stereo */ - delta = pvRad->st_edge.cap - pvRad->st_edge.flow; - pvRad->st_edge.cap -= delta; - pBNS->tot_st_cap -= delta; - } - } -/*exit_case_01:*/ - SetForbiddenEdgeMask( pBNS, &FixedStereoEdges, forbidden_stereo_edge_mask ); - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - /* - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - */ - cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - cmpInChI2 = 0; - memset ( icr2, 0, sizeof(*icr2) ); - if ( iRevrInChI || iOrigInChI ) { - /* additional mobile-H compare in case of Fixed-H */ - cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - } - - pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; - - - pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || - !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? - NULL: - (pStruct->pOneINChI[1]->StereoIsotopic && - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[1]->StereoIsotopic : - pStruct->pOneINChI[1]->Stereo; - - } - } - - cur_success = 0; - if ( !(cmpInChI & IDIF_SB_MISS) && (cmpInChI2 & IDIF_SB_MISS) && - icr2->num_sb_in2_only && - 0 < (max_success = pBNS->tot_st_cap - pBNS->tot_st_flow) ) { - /*----------------------------------------------------*/ - /* case 02: missing stereogenic bond in Mobile-H only */ - /* X=N-O* => X=N=O and eliminate radical */ - /*----------------------------------------------------*/ - int retC, ret2C, retS, ret2S; - INCHI_MODE cmpInChI_Prev, cmpInChI2_Prev; - ICR icr_Prev, icr2_Prev; - - /* blind attepmt */ - icr_Prev = *icr; - icr2_Prev = *icr2; - cmpInChI_Prev = cmpInChI; - cmpInChI2_Prev = cmpInChI2; - for ( i = AllRadList.num_edges = 0; i < pStruct->num_atoms; i ++ ) { - if ( pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow == 1 && - (ret = AddToEdgeList( &AllRadList, i, INC_ADD_EDGE ) ) ) { - goto exit_function; - } - } - for ( i = 0; i < AllRadList.num_edges; i ++ ) { - j = AllRadList.pnEdges[i]; - pBNS->vert[j].st_edge.cap -= 1; - pBNS->tot_st_cap -= 1; - } - /*-------------------------------------------------*/ - /* re-create InChI and see whether it looks better */ - /*-------------------------------------------------*/ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - /* - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - */ - cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - cmpInChI2 = 0; - memset ( icr2, 0, sizeof(*icr2) ); - if ( iRevrInChI || iOrigInChI ) { - /* additional mobile-H compare in case of Fixed-H */ - cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - } - retC = CompareIcr( icr, &icr_Prev, NULL, NULL, IDIFF_CONSTIT ); - retS = CompareIcr( icr, &icr_Prev, NULL, NULL, IDIFF_STEREO ); - ret2C = CompareIcr( icr2, &icr2_Prev, NULL, NULL, IDIFF_CONSTIT ); - ret2S = CompareIcr( icr2, &icr2_Prev, NULL, NULL, IDIFF_STEREO ); - - if ( 0 >= retC && - 0 >= retS && - 0 >= ret2C && - 0 > ret2S ) { - ; /* accept */ - } else { - /* reject */ - for ( i = 0; i < AllRadList.num_edges; i ++ ) { - j = AllRadList.pnEdges[i]; - pBNS->vert[j].st_edge.cap += 1; - pBNS->tot_st_cap += 1; - } - - /*-------------------------------------------------*/ - /* re-create InChI-- return to previous state */ - /*-------------------------------------------------*/ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - /* - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - */ - cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - cmpInChI2 = 0; - memset ( icr2, 0, sizeof(*icr2) ); - if ( iRevrInChI || iOrigInChI ) { - /* additional mobile-H compare in case of Fixed-H */ - cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - } - pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; - - - pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || - !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? - NULL: - (pStruct->pOneINChI[1]->StereoIsotopic && - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[1]->StereoIsotopic : - pStruct->pOneINChI[1]->Stereo; - } -/*exit_case_02:;*/ - } - - cur_success = 0; - if ( pStruct->bMobileH == TAUT_NON && (cmpInChI & IDIF_SB_EXTRA_UNDF) && - pStruct->endpoint ) { - /*------------------------------------------------------*/ - /* case 03: extra stereogenic bond in Fixed-H only */ - /* in Mobile-H this bond is not stereogenic. */ - /* Since this bond parity is not known, it is UNDEFINED */ - /*------------------------------------------------------*/ - int bDone, num_endpoints; - - TautMinusEdges[0].num_edges = 0; - TautMinusEdges[1].num_edges = 0; - AllChargeEdges.num_edges = 0; - /* in1 => in restored structure; in2 => in original InChI */ - for ( i = 0; i < icr->num_sb_undef_in1_only; i ++ ) { - j12 = icr->sb_undef_in1_only[i]; - pv1 = pBNS->vert + (v1 = pStereoRevrs->nBondAtom1[j12]-1); - pv2 = pBNS->vert + (v2 = pStereoRevrs->nBondAtom2[j12]-1); - - if ( pStereo2Revrs ) { - /* reject if it is extra in Mobile-H also */ - if ( icr2->num_sb_undef_in1_only ) { - for ( j = 0; j < icr2->num_sb_undef_in1_only; j ++ ) { - k = icr2->sb_undef_in1_only[j]; - if ( v1 == pStereo2Revrs->nBondAtom1[k] && - v2 == pStereo2Revrs->nBondAtom2[k] ) { - break; - } - } - if ( j < icr->num_sb_in1_only ) { - continue; /* extra stereobond in Mobile H also */ - } - } - } - /* reject if it is a stereobond in Mobile-H also */ - if ( pStereo2InChI && pStereo2InChI->nNumberOfStereoBonds ) { - for ( j = 0; j < pStereo2InChI->nNumberOfStereoBonds; j ++ ) { - if ( v1 == pStereo2InChI->nBondAtom1[j] && - v2 == pStereo2InChI->nBondAtom1[j] ) { - break; - } - } - if ( j < pStereo2InChI->nNumberOfStereoBonds ) { - continue; /* ignore this extra stereo bond: it is in Mobile-H */ - } - } - /* find the edge between v1 and v2 */ - for ( k = 0; k < at2[v1].valence; k ++ ) { - pe = pBNS->edge + (e = pv1->iedge[k]); - if ( v2 == (pe->neighbor12 ^ v1) ) - break; /* the edge has been found */ - } - if ( k == at2[v1].valence ) { - ret = RI_ERR_SYNTAX; - goto exit_function; - } - /* Fix all charges except negative charges on tautomeric endpoints */ - if ( !AllChargeEdges.num_edges && !TautMinusEdges[0].num_edges && !TautMinusEdges[1].num_edges ) { - for ( j = 0; j < pStruct->num_atoms; j ++ ) { - if ( (k=pVA[j].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { - if ( !pStruct->endpoint[j] ) { - if (ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else - if ( pVA[j].cNumValenceElectrons == 6 ) { - /* O */ - if (ret = AddToEdgeList( TautMinusEdges+0, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else { - /* N */ - if (ret = AddToEdgeList( TautMinusEdges+1, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( (k=pVA[j].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { - if ( ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ - if ( pVA[j].cNumValenceElectrons == 5 && !pVA[j].cMetal && /* N, P, As */ - NO_VERTEX != (k = GetChargeFlowerUpperEdge( pBNS, pVA, k ))) { - - if ( !pBNS->edge[j].forbidden && pBNS->edge[k].flow ) { - if ( ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - } - } - } - if ( !pe->flow ) - continue; - /* fix all charges except tautomeric; first allow only O, then only N, finally both N and O */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - for ( k = 1, bDone = 0; k < 4 && !bDone; k ++ ) { - /* fix tautomeric charges */ - num_endpoints = (TautMinusEdges+0)->num_edges + (TautMinusEdges+1)->num_edges; - if ( k == 2 ) { - /* fix charges on O */ - SetForbiddenEdgeMask( pBNS, TautMinusEdges+0, forbidden_edge_mask ); - num_endpoints -= (TautMinusEdges+0)->num_edges; - } - if ( k == 1 ) { - SetForbiddenEdgeMask( pBNS, TautMinusEdges+1, forbidden_edge_mask ); - num_endpoints -= (TautMinusEdges+1)->num_edges; - } - if ( num_endpoints >= 2 ) { - delta = 1; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; /* fix stereobond */ - pe->flow -= delta; /* decrement stereobond order */ - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { - /* Negative charge has been moved, no change in number of charges */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - (*pnNumRunBNS) ++; - cur_success ++; /* 01 */ - bDone = 1; - } - } else { - pe->forbidden &= ~forbidden_edge_mask; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - } - /* unfix tautomeric charges */ - if ( k == 2 ) - RemoveForbiddenEdgeMask( pBNS, TautMinusEdges+0, forbidden_edge_mask ); - if ( k == 1 ) - RemoveForbiddenEdgeMask( pBNS, TautMinusEdges+1, forbidden_edge_mask ); - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } -/*exit_case_03:*/ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - /* - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - */ - cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - cmpInChI2 = 0; - memset ( icr2, 0, sizeof(*icr2) ); - if ( iRevrInChI || iOrigInChI ) { - /* additional mobile-H compare in case of Fixed-H */ - cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - } - pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; - - - pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || - !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? - NULL: - (pStruct->pOneINChI[1]->StereoIsotopic && - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[1]->StereoIsotopic : - pStruct->pOneINChI[1]->Stereo; - - } - } - - cur_success = 0; - if ( (cmpInChI & IDIF_SB_EXTRA_UNDF) ) { - /*------------------------------------------------------*/ - /* case 04: extra stereogenic bond */ - /* Since this bond parity is not known, it is UNDEFINED */ - /*------------------------------------------------------*/ - int bDone, num_endpoints; - - TautMinusEdges[0].num_edges = 0; - TautMinusEdges[1].num_edges = 0; - AllChargeEdges.num_edges = 0; - /* in1 => in restored structure; in2 => in original InChI */ - for ( i = 0; i < icr->num_sb_undef_in1_only; i ++ ) { - j12 = icr->sb_undef_in1_only[i]; - pv1 = pBNS->vert + (v1 = pStereoRevrs->nBondAtom1[j12]-1); - pv2 = pBNS->vert + (v2 = pStereoRevrs->nBondAtom2[j12]-1); - - /* find the edge between v1 and v2 */ - for ( k = 0; k < at2[v1].valence; k ++ ) { - pe = pBNS->edge + (e = pv1->iedge[k]); - if ( v2 == (pe->neighbor12 ^ v1) ) - break; /* the edge has been found */ - } - if ( k == at2[v1].valence ) { - ret = RI_ERR_SYNTAX; - goto exit_function; - } - if ( pStereo2Revrs ) { - /* reject if it is not extra in Mobile-H also */ - if ( icr2->num_sb_undef_in1_only ) { - for ( j = 0; j < icr2->num_sb_undef_in1_only; j ++ ) { - k = icr2->sb_undef_in1_only[j]; - if ( v1 == pStereo2Revrs->nBondAtom1[k] && - v2 == pStereo2Revrs->nBondAtom2[k] ) { - break; - } - } - if ( j == icr->num_sb_in1_only ) { - continue; /* extra stereobond only in Fixed-H, not in Mobile H also */ - } - } - } - - /* Fix all charges except negative charges on tautomeric endpoints */ - if ( !AllChargeEdges.num_edges && !TautMinusEdges[0].num_edges && !TautMinusEdges[1].num_edges ) { - for ( j = 0; j < pStruct->num_atoms; j ++ ) { - if ( (k=pVA[j].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { - if (ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - if ( (k=pVA[j].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { - int bMayBeUnfixed = !at2[j].num_H && !(pStruct->endpoint && pStruct->endpoint[j]); - if ( bMayBeUnfixed && pVA[j].cNumValenceElectrons == 6 || - pVA[j].cNumValenceElectrons == 5 && pVA[j].cPeriodicRowNumber > 1 ) { - /* O & P */ - if (ret = AddToEdgeList( TautMinusEdges+0, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else - if ( bMayBeUnfixed && - pVA[j].cNumValenceElectrons == 5 && pVA[j].cPeriodicRowNumber == 1 ) { - /* N */ - if (ret = AddToEdgeList( TautMinusEdges+1, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else { - if ( ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ - if ( pVA[j].cNumValenceElectrons == 5 && !pVA[j].cMetal && /* N, P, As */ - NO_VERTEX != (k = GetChargeFlowerUpperEdge( pBNS, pVA, k ))) { - if ( !pBNS->edge[j].forbidden && pBNS->edge[k].flow ) { - if ( ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - } - } - } - if ( !pe->flow ) - continue; - /* fix all charges except tautomeric; first allow only O, then only N, finally both N and O */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - for ( k = 1, bDone = 0; k < 4 && !bDone; k ++ ) { - /* fix positive charges on heteroatoms */ - num_endpoints = (TautMinusEdges+0)->num_edges + (TautMinusEdges+1)->num_edges; - if ( k == 2 ) { - /* fix charges on O */ - SetForbiddenEdgeMask( pBNS, TautMinusEdges+0, forbidden_edge_mask ); - num_endpoints -= (TautMinusEdges+0)->num_edges; - } - if ( k == 1 ) { - /* fix charges on N */ - SetForbiddenEdgeMask( pBNS, TautMinusEdges+1, forbidden_edge_mask ); - num_endpoints -= (TautMinusEdges+1)->num_edges; - } - if ( num_endpoints >= 2 ) { - delta = 1; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; /* fix stereobond */ - pe->flow -= delta; /* decrement stereobond order */ - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { - /* Negative charge has been moved, no change in number of charges */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - (*pnNumRunBNS) ++; - cur_success ++; /* 01 */ - bDone = 1; - } - } else { - pe->forbidden &= ~forbidden_edge_mask; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - } - /* unfix tautomeric charges */ - if ( k == 2 ) - RemoveForbiddenEdgeMask( pBNS, TautMinusEdges+0, forbidden_edge_mask ); - if ( k == 1 ) - RemoveForbiddenEdgeMask( pBNS, TautMinusEdges+1, forbidden_edge_mask ); - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } -/*exit_case_04:*/ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - /* - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - */ - cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - cmpInChI2 = 0; - memset ( icr2, 0, sizeof(*icr2) ); - if ( iRevrInChI || iOrigInChI ) { - /* additional mobile-H compare in case of Fixed-H */ - cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - } - pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; - - - pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || - !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? - NULL: - (pStruct->pOneINChI[1]->StereoIsotopic && - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[1]->StereoIsotopic : - pStruct->pOneINChI[1]->Stereo; - - } - } - - cur_success = 0; - if ( pStruct->bMobileH == TAUT_YES && - (cmpInChI & IDIF_SB_EXTRA_UNDF && - !pStruct->ti.num_t_groups) - /*pStruct->bMobileH == TAUT_NON && (cmpInChI2 & IDIF_SB_EXTRA_UNDF)*/) { - /*----------------------------------------------------------*/ - /* case 05: extra stereogenic bond on =NH2(+), (B, Mobile-H)*/ - /* H H */ - /* original: N(+)=-N< -> N--==N/ */ - /* (A) H */ - /* double bond is marked as */ - /* not stereogenic due to */ - /* its change during proton */ - /* removal => No Stereo bond */ - /* (=NH may be tautomeric) */ - /* */ - /* H H */ - /* original: N=-N(+)< -> N--==N/ */ - /* (B) H */ - /* double bond was not */ - /* changed during proton */ - /* In Fixed-H this bond removal => Undef Stereo */ - /* may not be stereogenic (=NH is not tautomeric) */ - /* (a) due to (+) movement */ - /* (b) due to symmetry (2H), even if isotopic */ - /* */ - /* Fixed-H: move (+) to or from NH2 for Undef or No stereo */ - /* respectively */ - /* Mobile-H: Add H(+) to =NH and move the charge to =N- */ - /* to eliminate Undef stereo */ - /* Move charge from N to -NH2 to create */ - /* Undef Stereo */ - /* Since this bond parity is not known, it is UNDEFINED */ - /* */ - /* Solution: Add H(+) to =NH and move charge to -N= */ - /* */ - /*----------------------------------------------------------*/ - int aN, aC, i1, i2, vPlusMinus; - AllChargeEdges.num_edges = 0; - /* in1 => in restored structure; in2 => in original InChI */ - for ( i = 0; i < icr->num_sb_undef_in1_only; i ++ ) { - j12 = icr->sb_undef_in1_only[i]; - pv1 = pBNS->vert + (v1 = pStereoRevrs->nBondAtom1[j12]-1); - pv2 = pBNS->vert + (v2 = pStereoRevrs->nBondAtom2[j12]-1); - /* indicators of -NH: */ - i1 = at2[v1].valence == 1 && at2[v1].num_H == 1 && !at2[v1].endpoint && - pVA[v1].cNumValenceElectrons == 5 && pVA[v1].cPeriodicRowNumber == 1; - i2 = at2[v2].valence == 1 && at2[v2].num_H == 1 && !at2[v2].endpoint && - pVA[v2].cNumValenceElectrons == 5 && pVA[v2].cPeriodicRowNumber == 1; - if ( !i1 && !i2 || i1 && i2 ) { - continue; - } - /* find the edge between v1 and v2 */ - for ( k = 0; k < at2[v1].valence; k ++ ) { - pe = pBNS->edge + (e = pv1->iedge[k]); - if ( v2 == (pe->neighbor12 ^ v1) ) - break; /* the edge has been found */ - } - if ( k == at2[v1].valence ) { - ret = RI_ERR_SYNTAX; - goto exit_function; - } - if ( pe->flow != 1 ) { - continue; /* already charged */ - } - aN = i1? v1 : v2; /* -NH atom */ - aC = i1? v2 : v1; /* neighbor */ - /* Replace =NH with -NH2 - Create such a charge on some -N< that may be moved to NH2 to remove H(+): - transformation: - from: HN=C-=-N=(+vert)-Y=(+super)-(+/-) - to: 2HN-C*-=-N=(+vert)-Y=(+super)-(+/-)* - Run BNS to obtain: - 2HN-C=-=N(+)-(+vert)=Y-(+super)=(+/-) - */ - vPlusMinus = GetPlusMinusVertex( pBNS, pTCGroups, 1, 0 ); - if ( NO_VERTEX == vPlusMinus ) { - break; /* cannot do anything */ - } - /* increase edges to -Y-(+/-)-Y- capacities */ - delta = 1; - for ( i1 = 0; i1 < pBNS->vert[vPlusMinus].num_adj_edges; i1 ++ ) { - i2 = pBNS->edge[pBNS->vert[vPlusMinus].iedge[i1]].neighbor12 ^ vPlusMinus; - for ( k = 0; k < pBNS->vert[i2].num_adj_edges; k ++ ) { - pBNS->edge[pBNS->vert[i2].iedge[k]].cap += delta; - } - } - /* Fix all charges except (+) on -N< */ - if ( !AllChargeEdges.num_edges ) { - for ( j = 0; j < pStruct->num_atoms; j ++ ) { - if ( (k=pVA[j].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { - if (ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - if ( (k=pVA[j].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { - if ( pVA[j].cNumValenceElectrons == 5 && pVA[j].cPeriodicRowNumber == 1 && - !at2[j].num_H && at2[j].valence == 3 && - !(at2[j].endpoint || pStruct->endpoint && pStruct->endpoint[j]) ) { - ; /* do not fix -N< or =N(+)< */ - } else { - /* all others */ - if (ret = AddToEdgeList( TautMinusEdges+0, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ - if ( pVA[j].cNumValenceElectrons == 5 && !pVA[j].cMetal && /* N, P, As */ - NO_VERTEX != (k = GetChargeFlowerUpperEdge( pBNS, pVA, k ))) { - if ( !pBNS->edge[j].forbidden && pBNS->edge[k].flow ) { - if ( ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - } - } - } - /* Make bond to =NH single, add radical to aC */ - pe->flow -= delta; /* make single bond */ - pBNS->vert[aN].st_edge.flow -= delta; - pBNS->vert[aN].st_edge.cap -= delta; /* avoid radical on N */ - pBNS->vert[aC].st_edge.flow -= delta; /* create radical on C */ - pBNS->vert[vPlusMinus].st_edge.cap += delta; /* create radical on (+/-) */ - pBNS->tot_st_flow -= 2*delta; - /* fix C-NH bond */ - if ( ret = AddToEdgeList( &AllChargeEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - /* pBNS->tot_st_cap is unchanged */ - /* find all aC edges except pe to fix them */ - /* 2. Check whether it would work and do if it would */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );/* fix aC edges */ - pe->cap ++; - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == vPlusMinus && vPathStart == aC || - vPathEnd == aC && vPathStart == vPlusMinus) && nDeltaCharge == 1 ) { - /* Negative charge has been moved, no change in number of charges */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - (*pnNumRunBNS) ++; - /* 3. Add H to -NH and register increaded charge */ - pStruct->at[aN].num_H ++; - pTCGroups->total_charge ++; - cur_success ++; /* 01 */ - } - } else { - pe->flow += delta; /* make single bond */ - pBNS->vert[aN].st_edge.flow += delta; - pBNS->vert[aN].st_edge.cap += delta; /* avoid radical on N */ - pBNS->vert[aC].st_edge.flow += delta; /* create radical on C */ - pBNS->vert[vPlusMinus].st_edge.cap -= delta; /* create radical on (+/-) */ - pBNS->tot_st_flow += 2*delta; - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );/* fix aC edges */ - AllChargeEdges.num_edges --; /* remove pe from the list */ - CurrEdges.num_edges = 0; - continue; /* should not happen */ - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );/* fix aC edges */ - AllChargeEdges.num_edges --; /* remove pe from the list */ - CurrEdges.num_edges = 0; - } -/*exit_case_05:*/ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - /* - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - */ - cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - cmpInChI2 = 0; - memset ( icr2, 0, sizeof(*icr2) ); - if ( iRevrInChI || iOrigInChI ) { - /* additional mobile-H compare in case of Fixed-H */ - cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - } - pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; - - - pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || - !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? - NULL: - (pStruct->pOneINChI[1]->StereoIsotopic && - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[1]->StereoIsotopic : - pStruct->pOneINChI[1]->Stereo; - - } - } - - cur_success = 0; - if ( pStruct->bMobileH == TAUT_NON && pStereo2Revrs /* added check 2006-04-05 */ && - (cmpInChI2 & IDIF_SB_EXTRA_UNDF && - !pStruct->ti.num_t_groups) - /*pStruct->bMobileH == TAUT_NON && (cmpInChI2 & IDIF_SB_EXTRA_UNDF)*/) { - /*----------------------------------------------------------*/ - /* case 06: extra stereogenic bond on =NH2(+), (B, Fixed-H) */ - /* H H =========== */ - /* original: N(+)=-N< -> N--==N(+)< */ - /* (A) H H */ - /* double bond in Mobile-H */ - /* layer has Undef stereo */ - /* */ - /* */ - /* Fixed-H: move (+) to or from NH2 for Undef or No stereo */ - /* respectively */ - /* Mobile-H: Add H(+) to =NH and move the charge to =N- */ - /* to eliminate Undef stereo */ - /* Move charge from N to -NH2 to create */ - /* Undef Stereo */ - /* Since this bond parity is not known, it is UNDEFINED */ - /* */ - /* Solution: Move (+) from -NH2(+) to othe -N< */ - /* */ - /*----------------------------------------------------------*/ - int aN, aC, i1, i2, ePlus; - BNS_EDGE *pePlus; - AllChargeEdges.num_edges = 0; - /* in1 => in restored structure; in2 => in original InChI */ - for ( i = 0; i < icr2->num_sb_undef_in1_only; i ++ ) { - j12 = icr2->sb_undef_in1_only[i]; - pv1 = pBNS->vert + (v1 = pStereo2Revrs->nBondAtom1[j12]-1); - pv2 = pBNS->vert + (v2 = pStereo2Revrs->nBondAtom2[j12]-1); - /* indicators of -NH: */ - i1 = at2[v1].valence == 1 && at2[v1].num_H == 2 && !at2[v1].endpoint && - pVA[v1].cNumValenceElectrons == 5 && pVA[v1].cPeriodicRowNumber == 1; - i2 = at2[v2].valence == 1 && at2[v2].num_H == 2 && !at2[v2].endpoint && - pVA[v2].cNumValenceElectrons == 5 && pVA[v2].cPeriodicRowNumber == 1; - if ( !i1 && !i2 || i1 && i2 ) { - continue; - } - /* find the edge between v1 and v2 */ - for ( k = 0; k < at2[v1].valence; k ++ ) { - pe = pBNS->edge + (e = pv1->iedge[k]); - if ( v2 == (pe->neighbor12 ^ v1) ) - break; /* the edge has been found */ - } - if ( k == at2[v1].valence ) { - ret = RI_ERR_SYNTAX; - goto exit_function; - } - if ( pe->flow != 1 ) { - continue; /* already charged */ - } - aN = i1? v1 : v2; /* -NH atom */ - aC = i1? v2 : v1; /* neighbor */ - if ( 0 > (ePlus = pVA[aN].nCPlusGroupEdge-1) || - (pePlus = pBNS->edge + ePlus)->flow || /* must be (+) charged */ - pePlus->forbidden ) { - continue; - } - /* Move (+) from =NH2(+) to some other -N< - */ - /* Fix all charges except (+) on -N< */ - if ( !AllChargeEdges.num_edges ) { - for ( j = 0; j < pStruct->num_atoms; j ++ ) { - if ( (k=pVA[j].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { - if (ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - if ( (k=pVA[j].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { - if ( pVA[j].cNumValenceElectrons == 5 && pVA[j].cPeriodicRowNumber == 1 && - !at2[j].num_H && at2[j].valence == 3 && - !(at2[j].endpoint || pStruct->endpoint && pStruct->endpoint[j]) ) { - ; /* do not fix -N< or =N(+)< */ - } else { - /* all others */ - if (ret = AddToEdgeList( TautMinusEdges+0, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ - if ( pVA[j].cNumValenceElectrons == 5 && !pVA[j].cMetal && /* N, P, As */ - NO_VERTEX != (k = GetChargeFlowerUpperEdge( pBNS, pVA, k ))) { - if ( !pBNS->edge[j].forbidden && pBNS->edge[k].flow ) { - if ( ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - } - } - } - /* pePlus edge is already fixed; unfix it */ - /* To decrement (+) on =NH2(+) decrement its double bond order */ - delta = 1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - delta = 1; - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - pe->forbidden |= forbidden_edge_mask; - pePlus->forbidden &= ~forbidden_edge_mask; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { - /* (+)charge was just moved, no change in number of charges */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - (*pnNumRunBNS) ++; - cur_success ++; /* 01 */ - } - } else { - pe->flow += delta; /* roll back */ - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - pe->forbidden &= ~forbidden_edge_mask; - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );/* fix aC edges */ - } -/*exit_case_06:*/ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - /* - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - */ - cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr2, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - cmpInChI2 = 0; - memset ( icr2, 0, sizeof(*icr2) ); - if ( iRevrInChI || iOrigInChI ) { - /* additional mobile-H compare in case of Fixed-H */ - cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - } - pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; - - - pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || - !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? - NULL: - (pStruct->pOneINChI[1]->StereoIsotopic && - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[1]->StereoIsotopic : - pStruct->pOneINChI[1]->Stereo; - - } - } - - -exit_function: - SetForbiddenEdgeMask( pBNS, &FixedStereoEdges, forbidden_stereo_edge_mask ); - AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &CurrEdges, EDGE_LIST_FREE ); - AllocEdgeList( &NFlowerEdges, EDGE_LIST_FREE ); - AllocEdgeList( &OtherNFlowerEdges, EDGE_LIST_FREE ); - AllocEdgeList( &FixedStereoEdges, EDGE_LIST_FREE ); - AllocEdgeList( &AllRadList, EDGE_LIST_FREE ); /* eliminate memory leak */ - AllocEdgeList( TautMinusEdges+0, EDGE_LIST_FREE ); - AllocEdgeList( TautMinusEdges+1, EDGE_LIST_FREE ); - - return ret; -} -#endif diff --git a/INCHI-1-SRC/INCHI/common/ichirvrs.h b/INCHI-1-SRC/INCHI/common/ichirvrs.h deleted file mode 100644 index 1bee0c4..0000000 --- a/INCHI-1-SRC/INCHI/common/ichirvrs.h +++ /dev/null @@ -1,916 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __ICHIRVRS_H__ -#define __ICHIRVRS_H__ - -#define ICHICONST const - -#define RI_ERR_ALLOC (-1) -#define RI_ERR_SYNTAX (-2) -#define RI_ERR_PROGR (-3) -#define RI_ERR_EOL (-4) -#define RI_ERR_EOF (0) - - -#define NO_VALUE_INT 9999 -#define NOT_READ_INT 9998 /* has not been read yet */ - -#define VALUE_OCTET 8 /* number of electrons in a full shell */ - -#define INC_EDGE_LIST_DEFAULT 64 - -typedef struct tagXYZCoord { - double xyz[3]; -} XYZ_COORD; - -typedef struct tagStructRestoreMode { - int bMetalAddFlower; /* 1 => allow adjustable metal valence and charge; 0=> use std charge/valence */ - /* the following three apply only if bMetalAddFlower = 1 */ - int nMetalMinBondOrder; /* edge_flow=f means bond order=cMetalMinBondOrder+f */ - int nMetalInitEdgeFlow; /* one bond contribution to metal's (st-cap - st-flow) = */ - /* (nMetalInitBondOrder-nMetalMinBondOrder) - nMetalInitEdgeFlow */ - int nMetalInitBondOrder; /* >= nMetalMinBondOrder + nMetalInitEdgeFlow */ - /* same for metal-endpoint bonds */ - int nMetal2EndpointMinBondOrder; - int nMetal2EndpointInitBondOrder; - int nMetal2EndpointInitEdgeFlow; - int nMetalFlowerParam_D; /* additional edge capacity for canceling radicals */ - int nMetalMaxCharge_D; /* cap and/or flow for metal charge group */ - int bStereoRemovesMetalFlag; /* 1=> treat stereogenic atoms and atoms connected by a stereo bond as non-metals */ - - int bFixStereoBonds; /* 1=> forbid stereogenic double bonds from changing */ -} SRM; - -typedef struct tagReversedInChI { - PINChI2 *pINChI[INCHI_NUM]; - PINChI_Aux2 *pINChI_Aux[INCHI_NUM]; - int num_components[INCHI_NUM]; - int nRetVal; -} REV_INCHI; - -/**************************************/ -#define BFS_Q_CLEAR (-1) -#define BFS_Q_FREE (-2) -typedef struct tagBfsQueue { - QUEUE *q; - AT_RANK *nAtomLevel; - S_CHAR *cSource; - int num_at; - AT_RANK min_ring_size; /* 8 => detect 7-member and smaller rings */ -} BFS_Q; -/**************************************/ - -#define EXTRACT_STRUCT_NUMBER 1 - -/* additional Mobile-H parities to be added to Fixed-H parities */ -/* This allows to set parities that exist in Mobile-H layer only */ -typedef struct tagInpAtomAddParities { - /* cml 0D parities */ - S_CHAR bUsed0DParity; /* bit=1 => stereobond; bit=2 => stereocenter */ - /* cml tetrahedral parity */ - S_CHAR p_parity; - AT_NUMB p_orig_at_num[MAX_NUM_STEREO_ATOM_NEIGH]; - /* cml bond parities */ - S_CHAR sb_ord[MAX_NUM_STEREO_BONDS]; /* stereo bond/neighbor ordering number, starts from 0 */ - /* neighbors on both sides of stereobond have same sign=> trans/T/E, diff. signs => cis/C/Z */ - S_CHAR sn_ord[MAX_NUM_STEREO_BONDS]; /* ord. num. of the neighbor adjacent to the SB; starts from 0; - -1 means removed explicit H */ - /* neighbors on both sides of stereobond have same parity => trans/T/E/2, diff. parities => cis/C/Z/1 */ - S_CHAR sb_parity[MAX_NUM_STEREO_BONDS]; - AT_NUMB sn_orig_at_num[MAX_NUM_STEREO_BONDS]; /* orig. at number of sn_ord[] neighbors */ -} inp_ATOM_STEREO; - -#define FIX_STEREO_BOND_ORDER 0 /* 1=> fix stereobonds; treat metal as non-metal if it is stereogenic or has a stereobond */ - -#define METAL_FREE_CHARGE_VAL 1 /* 1=> allow free changing charges/valences of metals; initial bond order=0 or 1 */ -#define ALLOW_METAL_BOND_ZERO 1 /* 1=> allow zero flow (bobd order) to metals */ - -#if ( ALLOW_METAL_BOND_ZERO == 1 ) -/* INIT_METAL_BOND_ZERO=1 => INIT_METAL_BOND_FLOW=0 */ -#define INIT_METAL_BOND_ZERO 0 /* 1=> initialize zero order bond to metals */ -#define INIT_METAL_BOND_FLOW 1 /* 1=> init flow=1, 0 => init. flow = 0 */ -#else -#define INIT_METAL_BOND_ZERO 0 -#define INIT_METAL_BOND_FLOW 0 /* always 0 */ -#endif - -#define I2A_FLAG_FIXEDH 0x0001 -#define I2A_FLAG_RECMET 0x0002 - -#define EL_NUMBER_H 1 - - -#define ATYPE_H 1 -#define ATYPE_Na 2 -#define ATYPE_Mg 3 -#define ATYPE_B 4 -#define ATYPE_C 5 -#define ATYPE_N 6 -#define ATYPE_O 7 -#define ATYPE_Cl 8 - - -/* first bonds valence for charge = c is cValence[0][c+VAL_BASE]; VAL_MIN_CHARGE <= c <= VAL_MAX_CHARGE */ -/* second bonds valence for charge = c is cValence[1][c+VAL_BASE]; VAL_MIN_CHARGE <= c <= VAL_MAX_CHARGE */ -/* total number of valences is 2 = VAL_NUMBER */ -/* neutral bond valence orders are cValence[0][VAL_NEUTR_ORDER], cValence[1][VAL_NEUTR_ORDER] */ -#define VAL_BASE ( 1) -#define VAL_MIN_CHARGE (-1) -#define VAL_MAX_CHARGE ( 1) -#define VAL_NUMBER ( 2) -#define VAL_NEUTR_ORDER (VAL_MAX_CHARGE-VAL_MIN_CHARGE+1) -#define VAL_LENGTH (VAL_NEUTR_ORDER+1) -#define VAL_NEGAT_CHARGE 0 -#define VAL_NEUTR_CHARGE 1 -#define VAL_POSIT_CHARGE 2 - -typedef struct tagAtomIonPrperies { - /* char cValence[VAL_NUMBER][VAL_LENGTH]; */ /* ordering numbers of minimal valence, 0-based */ - char cDoNotAddH; /* InChI does not add H to this element */ - char cMetal; /* the element is a metal */ - char cNumBondsToMetal; /* number of bonds to metal */ - char cInitFlowToMetal; /* sum of init flow to metal atoms */ - char cInitValenceToMetal; /* sum of init adjusted bond orders to metal atoms */ - char cInitOrigValenceToMetal; /* sum of init bond orders to metal atoms */ - char cMaxFlowToMetal; /* max total edge flow to metal atoms */ - char cInitFreeValences; /* number of 'dots' to connect; charges are marked separately */ - S_CHAR cInitCharge; /* initial charge on the atom (not included in ChargeStruct */ - char cNumValenceElectrons; - char cPeriodicRowNumber; - char cMinRingSize; /* min ring size for atoms that have 2 bonds only */ - U_CHAR cPeriodicNumber; /* number in Periodic Table of elements */ - S_CHAR cnListIndex; /* (index in the cnList) + 1; 0 => none */ - int nCMinusGroupEdge; /* (index of the edge to the atom's (-) group) + 1 */ - int nCPlusGroupEdge; /* (index of the edge to the atom's (+) group) + 1 */ - int nMetalGroupEdge; /* index of the edge to the atom's M-group + 1 */ - int nTautGroupEdge; /* index of the edge from the atom to the t-group + 1 */ -} VAL_AT; - - -/******************************************************************************************************/ -#define INI_NUM_TCGROUPS 16 -#define INC_NUM_TCGROUPS 16 - -typedef enum tagTgRestoreFlags { - TGRF_MINUS_FIRST = 1 -} TGRF; -typedef struct tagTCGroup { - int type; /* group type */ - int ord_num; /* ordering number within the type, typically t-group number */ - int num_edges; - /* charge group specific */ - int st_cap; - int st_flow; - int edges_cap; - int edges_flow; - int nVertexNumber; /* group vertex number; 0 = unassigned */ - int nForwardEdge; /* edge index: from c-group to central Y-connecting vertex - or from supergroup to (+/-) vertex; 0 => unassigned */ - int nBackwardEdge; /* edge index: from central Y-connecting vertex - to supergroup; 0 => unassigned */ - /* tautomeric group specific */ - short tg_num_H; /* number of H in a tautomeric group */ - short tg_num_Minus; /* negative charge on t-group */ - Vertex tg_set_Minus; /* the vertex+1 that has to have (-) */ - short tg_RestoreFlags; /* Set (-) to first memberst of a t-group (usually, N) */ -} TC_GROUP; - -typedef enum tagTCGroupTypes { - TCG_None = -1, /* so far only ord=0 is used */ - /* group type ord */ - TCG_Plus0 = 0, /* BNS_VT_C_POS 0 */ - TCG_Plus1, /* BNS_VT_C_POS 1 */ - TCG_Minus0, /* BNS_VT_C_NEG 0 */ - TCG_Minus1, /* BNS_VT_C_NEG 1 */ - TCG_Plus_C0, /* BNS_VT_C_POS_C 0 */ - TCG_Plus_C1, /* BNS_VT_C_POS_C 1 */ - TCG_Minus_C0, /* BNS_VT_C_NEG_C 0 */ - TCG_Minus_C1, /* BNS_VT_C_NEG_C 1 */ - TCG_Plus_M0, /* BNS_VT_C_POS_M 0 */ - TCG_Plus_M1, /* BNS_VT_C_POS_M 1 */ - TCG_Minus_M0, /* BNS_VT_C_NEG_M 0 */ - TCG_Minus_M1, /* BNS_VT_C_NEG_M 1 */ - TCG_MeFlower0, /* BNS_VT_M_GROUP 0 */ /* base */ - TCG_MeFlower1, /* BNS_VT_M_GROUP 1 */ - TCG_MeFlower2, /* BNS_VT_M_GROUP 2 */ - TCG_MeFlower3, /* BNS_VT_M_GROUP 3 */ - - TCG_Plus, /* BNS_VT_C_POS_ALL 0 */ - TCG_Minus, /* BNS_VT_C_NEG_ALL 0 */ - - NUM_TCGROUP_TYPES /* number of group types */ -}TCGR_TYPE; - -typedef struct tagAllTCGroups { - TC_GROUP *pTCG; - int num_tc_groups; /* number of charge groups and metal-flower vertices */ - int max_tc_groups; /* number of allocated of pTCG[] elements */ - int nGroup[NUM_TCGROUP_TYPES]; /* tagTCGroupTypes */ - int nVertices; /* total number of vertices */ - int nEdges; /* total number of edges */ - int nAddIedges; /* additional increase of number of iedges for edge switching to another group */ - int num_atoms; /* number of atoms */ - int num_bonds; /* number of bonds */ - int num_tgroups; /* number t-groups */ - int num_tgroup_edges; /* number of edges to t-groups */ - /* charges */ - int tgroup_charge; /* total charge of all t-groups */ - int charge_on_atoms; /* charge permanently sitting on atoms */ - int added_charge; /* charge added to the c-groups */ - int total_charge; /* total charge of the component */ - int total_electrons; /* total number of electrons on all atoms */ - int total_electrons_metals; /* total number of electrons on unbonded metals */ - - int num_metal_atoms; /* number of metal atoms */ - int num_metal_bonds; /* number of atom-metal bonds */ - /* excess_charge = total_charge - added_charge - tgroup_charge: add to metals etc. */ - - int nEdge4charge; /* edge used to add charges; neighbor1=supercharge, another = (+/-) vertex */ - int nEdgePlus; /* edge to (+) supergroup; 0 means none */ - int nEdgeMinus; /* edge to (-) supergroup; 0 means none */ - int iComponent; /* component number */ - int iAtNoOffset; /* first atom number -- always 0 for now */ -} ALL_TC_GROUPS; - -/**************************************/ -#define EDGE_LIST_CLEAR (-1) -#define EDGE_LIST_FREE (-2) - -typedef struct tagEdgeList { - int num_alloc; - int num_edges; - EdgeIndex *pnEdges; -} EDGE_LIST; -/**************************************/ - -#define BOND_MARK_STEREO 0x10 -#define BOND_TYPE_STEREO (BOND_TYPE_SINGLE | BOND_MARK_STEREO) - -/* local */ -#define RESET_EDGE_FORBIDDEN_MASK 0 - -#define TREAT_ATOM_AS_METAL 99 - - -/************************************************************************************/ -typedef struct tagChargeValence { - int nValence; - int nCharge; - int nValenceOrderingNumber; -} CHARGE_VAL; - -#define MY_CONST const -/*************************************************************************************/ -typedef struct tagChargeChangeCandidate { - Vertex iat; - char num_bonds; - char chem_valence; - char cMetal; - char cNumBondsToMetal; - char cNumValenceElectrons; - char cPeriodicRowNumber; - char cNumChargeStates; - U_CHAR el_number; -} CC_CAND; - -typedef struct tagOneComponentRemovedAndExchangeableH { - NUM_H nNumRemovedProtons; - NUM_H nNumRemovedIsotopicH[NUM_H_ISOTOPES]; /* isotopic H that may be exchanged and considered - randomly distributed, including removed protons */ -} COMPONENT_REM_PROTONS; - -typedef struct tagRemovedAndExchangeableH { - /* totals for Mobile-H layer */ - NUM_H nNumRemovedProtons; - NUM_H nNumRemovedIsotopicH[NUM_H_ISOTOPES]; /* isotopic H that may be exchanged and considered - randomly distributed, including removed protons */ - /* for individual components from comparing Fixed-H vs Mobile-H formulas; NULL if not available */ - COMPONENT_REM_PROTONS *pNumProtons; -} REM_PROTONS; - -typedef struct tagInputInChI { - INChI *pInpInChI[INCHI_NUM][TAUT_NUM]; - int nNumComponents[INCHI_NUM][TAUT_NUM]; - REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM]; - int s[INCHI_NUM][TAUT_NUM][2]; /* s[0=non-iso, 1=iso] = 0,1,2,3 <= regular /s; -1=> "/s" (empty) */ - long num_inp; - inp_ATOM *atom; /* the whole restored structure made out of all components */ - int num_atoms; /* number of atoms including explicit H */ - int num_explicit_H; /* number of explicit H in the atom */ - INCHI_MODE CompareInchiFlags[INCHI_NUM][TAUT_NUM]; -} InpInChI; - -typedef struct tagStructFromInChI { - /* InChI component -> Structure result */ - inp_ATOM *at; /* length = num_atoms + num_deleted_H, zero pint struct for BNS->struct conversion */ - inp_ATOM_STEREO *st; /* additional stereo that exists only in Mobile-H layer */ - inp_ATOM *at2; /* length = num_atoms + num_deleted_H, the conversion result */ - - /* information from InChI only */ - T_GROUP_INFO ti; /* from original InChI[0] if Mobile-H from the beginning or later from InChI[1] if Fixed-H */ - AT_NUMB *endpoint; /* from original InChI[1] in case of Fixed-H only */ - S_CHAR *fixed_H; /* from original InChI[0] in case of Fixed-H only */ - XYZ_COORD *pXYZ; - int num_atoms; - int num_deleted_H; /* if requested and Fixed-H InChI is available */ - int nNumRemovedProtonsMobHInChI; /* number of protons removed from Mobile-H struct in original InChI */ - S_CHAR charge; - char bIsotopic; - - /* InChI -> Structure conversion parms and intermediate data */ - BN_STRUCT *pBNS; - BN_DATA *pBD; - ICHICONST SRM *pSrm; - - /* InChI layer to reverse */ - char bMobileH; - char iINCHI; - char bFixedHExists; /* fixed-H InChI exists or not */ - - /* InChI -> Struct component -> Full InChI result (both disconnected and connected if exist) */ - REV_INCHI RevInChI; - int nRemovedProtonsByNormFromRevrs; /* number of H(+) removed by normalization after - Struct Restore and before Add/Remove Protons */ - int nNumRemovedProtonsByRevrs; /* number of H(+) removed by the reconstruction, - before Add/Remove Protons, only from TAUT_YES */ - - int bExtract; /* for debugging */ - - /* single component InChI calculation */ - INChI *pOneINChI[TAUT_NUM]; /* InChI of restored structure */ - INChI_Aux *pOneINChI_Aux[TAUT_NUM]; - INP_ATOM_DATA *pOne_norm_data[TAUT_NUM]; /* normalized restored structure */ - S_CHAR *pOne_fixed_H; /* !!! from normalized restored structure in case of Fixed-H only */ - T_GROUP_INFO One_ti; /* t-groups of normalized canonicalized restored structure */ - int nOneINChI_bMobileH; /* type of restored structure InChI */ - int nNumRemovedProtons; /* =0 for Fixed-H, = num. removed protons in case of Mobile-H InChI */ - /* in case of Fixed-H processing see pStruct->One_ti.tni.nNumRemovedProtons */ - AT_NUMB *nAtno2Canon[TAUT_NUM]; /* nAtno2Canon[restored_at_no][*] = (atom canon number in restored struct)-1*/ - AT_NUMB *nCanon2Atno[TAUT_NUM]; /* nCanon2Atno[(atom canon number in restored struct)-1][*] = restored_at_no; */ - - int nError; - /* other parms */ - char iInchiRec; /* index in the original InChI array */ - char iMobileH; /* index in the original InChI array */ - char bDeleted; /* InChI component marked as Deleted, means a proton in Mobile-H layer */ - /* struct. ordering number "Structure: nnn" if present */ - long num_inp_actual; - - /* utility data */ - BFS_Q *pbfsq; - VAL_AT *pVA; - - int nLink; /* same as in INChI */ - int bPostProcessed; /* recalculate after add/remove protons */ - - /* TAUT_YES layer charges */ - int nChargeRevrs; /* component charge of the reconstructed structure, TAUT_YES layer */ - int nChargeInChI; /* component charge from the original InChI, TAUT_YES layer */ -} StrFromINChI; - - -#define EL_TYPE_O 0x0001 -#define EL_TYPE_S 0x0002 -#define EL_TYPE_N 0x0004 -#define EL_TYPE_P 0x0008 -#define EL_TYPE_C 0x0010 -#define EL_TYPE_X 0x0020 /* any not metal */ -#define EL_TYPE_MASK 0x003f -#define EL_TYPE_OSt 0x0100 /* terminal -OH, -O(-), -SH, -S(-), ... from fix_special_bonds(...) */ -#define EL_TYPE_PT 0x0200 /* may be a tautomeric endpoint */ - -/* the atom to which the node is attached has number 1; added atoms have numbers 2,3,... */ -#define MAX_CN_VAL 3 -typedef struct tagVertCapFlow { - S_SHORT type; - S_CHAR cap; - S_CHAR flow; - S_CHAR valence; -} VCF; -typedef struct tagEdgeCapFlow { - S_SHORT neigh; - S_CHAR cap; - S_CHAR bForbiddenEdge; - S_CHAR flow; -} ECF; -typedef struct tagChargeNodes { - VCF v; - ECF e[MAX_CN_VAL]; -} C_NODE; - - -#define cn_bits_N 1 /* Neutral: charge = 0 */ -#define cn_bits_P 2 /* Plus 1: charge = +1 */ -#define cn_bits_M 4 /* Minus 1: charge = -1 */ -#define cn_bits_shift 3 -#define MAX_NUM_CN_BITS 4 -#define MAKE_CN_BITS(A, B, C, D ) (( (( ((D) << cn_bits_shift | (C)) << cn_bits_shift ) | (B)) << cn_bits_shift ) | (A)) - -#define cn_bits_PNPN MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_P, cn_bits_N) -#define cn_bits_NPNP MAKE_CN_BITS(cn_bits_N, cn_bits_P, cn_bits_N, cn_bits_P) -#define cn_bits_NPN MAKE_CN_BITS(cn_bits_N, cn_bits_P, cn_bits_N, 0) -#define cn_bits_PNP MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_P, 0) -#define cn_bits_MNP MAKE_CN_BITS(cn_bits_M, cn_bits_N, cn_bits_P, 0) -#define cn_bits_PNM MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_M, 0) -#define cn_bits_EN MAKE_CN_BITS(cn_bits_P | cn_bits_M, cn_bits_N, 0, 0) -#define cn_bits_NMN MAKE_CN_BITS(cn_bits_N, cn_bits_M, cn_bits_N, 0) -#define cn_bits_NE MAKE_CN_BITS(cn_bits_N, cn_bits_P | cn_bits_M, 0, 0) -#define cn_bits_NEN MAKE_CN_BITS(cn_bits_N, cn_bits_M | cn_bits_N, cn_bits_N, 0) -#define cn_bits_NP MAKE_CN_BITS(cn_bits_N, cn_bits_P, 0, 0) -#define cn_bits_PN MAKE_CN_BITS(cn_bits_P, cn_bits_N, 0, 0) -#define cn_bits_NM MAKE_CN_BITS(cn_bits_N, cn_bits_M, 0, 0) -#define cn_bits_MN MAKE_CN_BITS(cn_bits_M, cn_bits_N, 0, 0) -#define cn_bits_P_ MAKE_CN_BITS(cn_bits_P, 0, 0, 0) -#define cn_bits_M_ MAKE_CN_BITS(cn_bits_M, 0, 0, 0) -#define cn_bits_N_ MAKE_CN_BITS(cn_bits_N, 0, 0, 0) -#define cn_bits_Me (-1) - -#define cnListIndexMe (17) /* index of {cnMe, cn_bits_Me,... } element of cnList[] */ - -extern int cnListNumEl; /* number of elements in cnList[] */ - -typedef struct tagChargeNodeList { - MY_CONST C_NODE *pCN; - int bits; - int nInitialCharge; - int len; -} CN_LIST; - -extern MY_CONST CN_LIST cnList[]; - -/************************ fixed H comparison ******************************************************/ -#define MAX_DIFF_FIXH 256 -#define MAX_DIFF_MOBH 256 -typedef struct tagAtomsCmpTwoFixedH { - AT_NUMB endptInChI; - AT_NUMB endptRevrs; - AT_NUMB atomNumber; - U_CHAR nValElectr; - U_CHAR nPeriodNum; - S_CHAR nFixHInChI; - S_CHAR nFixHRevrs; - S_CHAR nMobHInChI; - S_CHAR nMobHRevrs; - S_CHAR nNumHRevrs; - S_CHAR nAtChargeRevrs; - S_CHAR nValue; /* flag(s) */ -} CMP2FHATOMS; - -typedef struct tagStructCmpTwoFixedH { - CMP2FHATOMS c2at[MAX_DIFF_FIXH]; - short len_c2at; - short nNumRemHInChI; - short nNumRemHRevrs; - short nNumTgInChI; - short nNumTgRevrs; - short nNumEndpInChI; - short nNumEndpRevrs; - short nNumTgDiffMinus; /* number of would-be-identical t-groups that have different number of (-) */ - short nNumTgDiffH; /* number of would-be-identical t-groups that have different number of H */ - short nNumTgMInChI; /* number of (-) in orig. InChI t-groups */ - short nNumTgHInChI; /* number of H in orig. InChI t-groups */ - short nNumTgMRevrs; /* number of (-) in reversed structure t-groups */ - short nNumTgHRevrs; /* number of H in reversed structure t-groups */ - S_CHAR nChargeFixHInChI; - S_CHAR nChargeMobHInChI; - S_CHAR nChargeFixHRevrs; - S_CHAR nChargeMobHRevrs; - S_CHAR nChargeFixHRevrsNonMetal; /* charge does not include charges on metals */ - S_CHAR nChargeMobHRevrsNonMetal; /* charge does not include charges on metals */ - char bFixedHLayerExistsRevrs; - char bHasDifference; - U_CHAR nNumDiffMobH; - -} CMP2FHINCHI; - -/************************ Mobile H comparison *********************************************/ -typedef struct tagAtomsCmpTwoMobileH { - AT_NUMB endptInChI; - AT_NUMB endptRevrs; - AT_NUMB atomNumber; - U_CHAR nValElectr; - U_CHAR nPeriodNum; - S_CHAR nMobHInChI; /* number of H on the atom in the orig. InChI */ - S_CHAR nMobHRevrs; /* number of H on the atom in InChI from the reconstructed structure */ - S_CHAR nNumHRevrs; /* number of H on the atom in the being reconstructed structure */ - S_CHAR nAtChargeRevrs; - S_CHAR nValue; /* flag(s) */ -} CMP2MHATOMS; - -typedef struct tagStructCmpTwoMobileH { - CMP2MHATOMS c2at[MAX_DIFF_FIXH]; - short len_c2at; - short nNumRemHInChI; - short nNumRemHRevrs; - short nNumTgInChI; - short nNumTgRevrs; - short nNumEndpInChI; - short nNumEndpRevrs; - short nNumTgDiffMinus; /* number of would-be-identical t-groups that have different number of (-) */ - short nNumTgDiffH; /* number of would-be-identical t-groups that have different number of H */ - - short nNumTgMInChI; /* number of (-) in orig. InChI t-groups */ - short nNumTgHInChI; /* number of H in orig. InChI t-groups */ - short nNumTgOInChI; /* number of tautomeric O,S,Se in orig. InChI t-groups */ - short nNumTgNInChI; /* number of tautomeric N in orig. InChI t-groups */ - - short nNumTgMRevrs; /* number of (-) in reversed structure t-groups */ - short nNumTgHRevrs; /* number of H in reversed structure t-groups */ - short nNumTgORevrs; /* number of tautomeric O,S,Se in reversed structure t-groups */ - short nNumTgNRevrs; /* number of tautomeric N in reversed structure t-groups */ - - short nNumTgOMinusRevrs; /* number of -O(-) on endpoints found in restored structure */ - short nNumTgOHRevrs; /* number of -OH on endpoints found in restored structure */ - short nNumTgDBORevrs; /* number of =O on endpoints found in restored structure */ - short nNumTgNMinusRevrs; /* number of -N(-)- on endpoints found in restored structure */ - short nNumTgNHMinusRevrs; /* number of -NH(-) on endpoints found in restored structure */ - short nNumTgNHRevrs; /* number of -NH- on endpoints found in restored structure */ - short nNumTgNH2Revrs; /* number of -NH2 on endpoints found in restored structure */ - short nNumTgDBNHRevrs; /* number of =NH on endpoints found in restored structure */ - short nNumTgDBNMinusRevrs; /* number of =N(-) on endpoints found in restored structure */ - short nNumTgDBNRevrs; /* number of =N- on endpoints found in restored structure */ - - short nNumTgOMinusInChI; /* number of -O(-) on endpoints according to original InChI */ - short nNumTgOHInChI; /* number of -OH on endpoints according to original InChI */ - short nNumTgDBOInChI; /* number of =O on endpoints according to original InChI */ - short nNumTgNMinusInChI; /* number of -N(-)- on endpoints according to original InChI */ - short nNumTgNHMinusInChI; /* number of -NH(-) on endpoints according to original InChI */ - short nNumTgNHInChI; /* number of -NH- on endpoints according to original InChI */ - short nNumTgNH2InChI; /* number of -NH2 on endpoints according to original InChI */ - short nNumTgDBNHInChI; /* number of =NH on endpoints according to original InChI */ - short nNumTgDBNMinusInChI; /* number of =N(-) on endpoints according to original InChI */ - short nNumTgDBNInChI; /* number of =N- on endpoints according to original InChI */ - - S_CHAR nChargeMobHInChI; - S_CHAR nChargeMobHRevrs; - S_CHAR nChargeMobHRevrsNonMetal; /* charge does not include charges on metals; later add ion pairs rejection */ - char bFixedHLayerExistsRevrs; - char bHasDifference; - U_CHAR nNumDiffMobH; -} CMP2MHINCHI; - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -int OneInChI2Atom( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, const char *szCurHdr, long num_inp, - StrFromINChI *pStruct, int iComponent, int iAtNoOffset, int bHasSomeFixedH, INChI *pInChI[]); -int get_sp_element_type( int nPeriodicNumber, int *nRow ); -int get_bonds_valences( int nPeriodicNum, int bonds_valence, int num_H, VAL_AT *pVA ); - - -/* local prototypes */ -int AddExplicitDeletedH( inp_ATOM *at, int jv, int num_at, int *iDeletedH, int *iH, int nNumDeletedH, int bTwoStereo ); -int bFindCumuleneChain( inp_ATOM *at, AT_NUMB i1, AT_NUMB i2, AT_NUMB nCumulene[], int nMaxLen ); -int set_bond_type( inp_ATOM *at, AT_NUMB i1, AT_NUMB i2, int bType ); -int set_cumulene_0D_parity( inp_ATOM *at, inp_ATOM_STEREO *st, int num_at, int idelH1, int i1, int i2, int idelH2, int parity, int len ); -int set_atom_0D_parity( inp_ATOM *at, inp_ATOM_STEREO *st, int num_at, int num_deleted_H, int i1, int parity ); -int GetTgroupInfoFromInChI( T_GROUP_INFO *ti, inp_ATOM *at, AT_NUMB *endpoint, INChI *pInChI ); -int FillOutpStructEndpointFromInChI( INChI *pInChI, AT_NUMB **pEndpoint ); -int SetStereoBondTypeFor0DParity( inp_ATOM *at, int i1, int m1 ); -int SetStereoBondTypesFrom0DStereo( StrFromINChI *pStruct, INChI *pInChI); -void CopyAt2St( inp_ATOM *at, inp_ATOM_STEREO * st, int num_atoms ); -void CopySt2At( inp_ATOM *at, inp_ATOM_STEREO * st, int num_atoms ); -int RestoreAtomConnectionsSetStereo( StrFromINChI *pStruct, int iComponent, int iAtNoOffset, INChI *pInChI, INChI *pInChIMobH); -int RestoreAtomMakeBNS( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, StrFromINChI *pStruct, int iComponent, - int iAtNoOffset, INChI *pInChI[], const char *szCurHdr, long num_inp, int bHasSomeFixedH ); -int nAddSuperCGroups( ALL_TC_GROUPS *pTCGroups ); - -int AddCGroups2TCGBnStruct( BN_STRUCT *pBNS, StrFromINChI *pStruct, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, int nMaxAddEdges ); -int AddTGroups2TCGBnStruct( BN_STRUCT *pBNS, StrFromINChI *pStruct, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, int nMaxAddEdges ); -BN_STRUCT* AllocateAndInitTCGBnStruct( StrFromINChI *pStruct, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, - int nMaxAddAtoms, int nMaxAddEdges, - int max_altp, int *pNum_changed_bonds ); -int nCountBnsSizes( inp_ATOM *at, int num_at, int nAddEdges2eachAtom, int nAddVertices, - T_GROUP_INFO *ti, VAL_AT *pVA, ICHICONST SRM *pSrm, ALL_TC_GROUPS *pTCGroups ); -int GetAtomRestoreInfo( inp_ATOM *atom, int iat, VAL_AT *pVArray, ICHICONST SRM *pSrm, int bMobileH, AT_NUMB *endpoint ); -int AddEdgeFlow( int edge_cap, int edge_flow, BNS_EDGE *e01, BNS_VERTEX *pv0 /*src*/, - BNS_VERTEX *pv1/*dest*/, int *tot_st_cap, int *tot_st_flow ); -void SetEdgeCapFlow( BNS_EDGE *e, int edge_cap, int edge_flow ); - -void AddStCapFlow( BNS_VERTEX *vert_ficpoint, int *tot_st_flow, int *tot_st_cap, int cap, int flow ); -void SetStCapFlow( BNS_VERTEX *vert_ficpoint, int *tot_st_flow, int *tot_st_cap, int cap, int flow ); - -int ConnectSuperCGroup( int nTCG_Plus, int nAddGroups[], int num_add, - int *pcur_num_vertices, int *pcur_num_edges, - int *tot_st_cap, int *tot_st_flow, - BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups ); -int ConnectTwoVertices( BNS_VERTEX *p1, BNS_VERTEX *p2, BNS_EDGE *e, BN_STRUCT *pBNS, int bClearEdge ); - -int nTautEndpointEdgeCap( inp_ATOM *at, VAL_AT *pVA, int i ); - -/* -int GetAtomBondFlow( inp_ATOM *atom, VAL_AT *pVA, ICHICONST SRM *pSrm, int iat, int ineigh ); -void GetAtomStCapFlow( inp_ATOM *atom, VAL_AT *pVA, ICHICONST SRM *pSrm, int iat, int *pCap, int *pFlow ); -int GetAtomToMCGroupInitEdgeCapFlow( EdgeFlow *nEdgeCap, EdgeFlow *nEdgeFlow, ICHICONST SRM *pSrm, inp_ATOM *at, VAL_AT *pVA, int iat ); -void GetAtomToMetalInitEdgeCapFlow( EdgeFlow *nEdgeCap, EdgeFlow *nEdgeFlow ); -*/ -int AtomStcapStflow( inp_ATOM *atom, VAL_AT *pVA, ICHICONST SRM *pSrm, int iat, int *pnStcap, int *pnStflow, - EdgeFlow *pnMGroupEdgeCap, EdgeFlow *pnMGroupEdgeFlow ); -int BondFlowMaxcapMinorder( inp_ATOM *atom, VAL_AT *pVA, ICHICONST SRM *pSrm, int iat, int ineigh, - int *pnMaxcap, int *pnMinorder, int *pbNeedsFlower ); - - - -int clean_charge_val( CHARGE_VAL *pChargeVal, int len, inp_ATOM *atom, VAL_AT *pVA, int iat, int bIsMetal, int bMobileH, AT_NUMB *endpoint ); -int ReallocTCGroups( ALL_TC_GROUPS *pTCGroups, int nAdd ); -int RegisterTCGroup( ALL_TC_GROUPS *pTCGroups, int nGroupType, int nGroupOrdNum, - int nVertexCap, int nVertexFlow, int nEdgeCap, int nEdgeFlow, int nNumEdges); -int CopyBnsToAtom( StrFromINChI *pStruct, BN_STRUCT *pBNS, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int bAllowZeroBondOrder ); -int CheckBnsConsistency( StrFromINChI *pStruct, BN_STRUCT *pBNS, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int bNoRad ); - -int RunBnsRestore1( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, INChI *pInChI[], long num_inp, int bHasSomeFixedH); -int RunBnsRestoreOnce( BN_STRUCT *pBNS, BN_DATA *pBD, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups ); -int nNumEdgesToCnVertex( MY_CONST C_NODE *pCN, int len, int v ); -int ConnectMetalFlower( int *pcur_num_vertices, int *pcur_num_edges, - int *tot_st_cap, int *tot_st_flow, ICHICONST SRM *pSrm, - BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups ); -int AddRadicalToMetal( int *tot_st_cap, int *tot_st_flow, ICHICONST SRM *pSrm, BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups ); -int bMayBeACationInMobileHLayer( inp_ATOM *at, VAL_AT *pVA, int iat, int bMobileH ); - -int MakeOneInChIOutOfStrFromINChI( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, StrFromINChI *pStruct, inp_ATOM *at2, inp_ATOM *at3, ALL_TC_GROUPS *pTCGroups ); -void IncrZeroBondsAndClearEndpts(inp_ATOM *at, int num_at, int iComponent); -void IncrZeroBonds(inp_ATOM *at, int num_at, int iComponent ); -void ClearEndpts(inp_ATOM *at, int num_at ); -int DisplayRestoredComponent( StrFromINChI *pStruct, int iComponent, int iAtNoOffset, INChI *pInChI, const char *szCurHdr ); -int cmp_charge_val( const void *a1, const void *a2 ); - -int EvaluateChargeChanges( BN_STRUCT *pBNS, VAL_AT *pVA, int *pnDeltaH, int *pnDeltaCharge, int *pnNumVisitedAtoms ); -int RunBnsTestOnce( BN_STRUCT *pBNS, BN_DATA *pBD, VAL_AT *pVA, Vertex *pvFirst, Vertex *pvLast, - int *pPathLen, int *pnDeltaH, int *pnDeltaCharge, int *pnNumVisitedAtoms ); -int comp_cc_cand( const void *a1, const void *a2 ); -int MoveRadToAtomsAddCharges( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int forbidden_mask ); -void RemoveForbiddenBondFlowBits( BN_STRUCT *pBNS, int forbidden_edge_mask_int ); -int PlusFromDB_N_DB_O_to_Metal(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int AdjustTgroupsToForbiddenEdges2( BN_STRUCT *pBNS, inp_ATOM *at, VAL_AT *pVA, int num_atoms, int forbidden_mask ); -int AllocEdgeList( EDGE_LIST *pEdges, int nLen ); -int AddToEdgeList( EDGE_LIST *pEdges, int iedge, int nAddLen ); -int RemoveFromEdgeListByIndex( EDGE_LIST *pEdges, int index ); -int RemoveFromEdgeListByValue( EDGE_LIST *pEdges, int iedge ); -int FindInEdgeList( EDGE_LIST *pEdges, int iedge ); -int AllocBfsQueue( BFS_Q *pQ, int num_at, int min_ring_size ); -void RemoveForbiddenEdgeMask( BN_STRUCT *pBNS, EDGE_LIST *pEdges, int forbidden_edge_mask ); -void SetForbiddenEdgeMask( BN_STRUCT *pBNS, EDGE_LIST *pEdges, int forbidden_edge_mask ); -int ForbidCarbonChargeEdges( BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups, EDGE_LIST *pCarbonChargeEdges, int forbidden_edge_mask ); -int ForbidMetalCarbonEdges( BN_STRUCT *pBNS, inp_ATOM *at, int num_at, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, EDGE_LIST *pMetalCarbonEdges, int forbidden_edge_mask ); -int ForbidNintrogenPlus2BondsInSmallRings( BN_STRUCT *pBNS, inp_ATOM *at, int num_at, - VAL_AT *pVA, int min_ring_size, ALL_TC_GROUPS *pTCGroups, - EDGE_LIST *pNplus2BondsEdges, int forbidden_edge_mask ); -int RearrangePlusMinusEdgesFlow( BN_STRUCT *pBNS, BN_DATA *pBD, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, int forbidden_edge_mask ); -int IncrementZeroOrderBondsToHeteroat( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, - VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, - int forbidden_edge_mask); -int MoveChargeFromHeteroatomsToMetals( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, - VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, - int forbidden_edge_mask); -int EliminateChargeSeparationOnHeteroatoms( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, - VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, - int forbidden_edge_mask, int forbidden_stereo_edge_mask); -int MovePlusFromS2DiaminoCarbon( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, - VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int RestoreCyanoGroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int RestoreIsoCyanoGroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int RestoreNNNgroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int FixMetal_Nminus_Ominus( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int EliminateNitrogen5Val3Bonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int Convert_SIV_to_SVI(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int MoveMobileHToAvoidFixedBonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int RemoveRadFromMobileHEndpoint(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int RemoveRadFromMobileHEndpointFixH(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int MoveChargeToMakeCenerpoints(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int CheckAndRefixStereobonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int MoveChargeToRemoveCenerpoints(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int MakeSingleBondsMetal2ChargedHeteroat(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int SaltBondsToCoordBonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int FixLessHydrogenInFormula( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, - inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ); -int FixMoreHydrogenInFormula( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, - inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ); -int FixAddProtonForADP( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, - inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, ICR *picr, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ); -int ConnectDisconnectedH( inp_ATOM *at, int num_atoms, int num_deleted_H ); -int DisconnectedConnectedH( inp_ATOM *at, int num_atoms, int num_deleted_H ); -int MakeInChIOutOfStrFromINChI2( ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd_inp, StrFromINChI *pStruct, - int iComponent, int iAtNoOffset, long num_inp ); -int GetChargeFlowerUpperEdge( BN_STRUCT *pBNS, VAL_AT *pVA, int nChargeEdge ); -int get_pVA_atom_type( VAL_AT *pVA, inp_ATOM *at, int iat, int bond_type ); - -int NormalizeAndCompare(ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, - StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, INChI *pInChI[], long num_inp, int bHasSomeFixedH, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask); -/* call InChI normalization only */ -int NormalizeStructure( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, - StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, - VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - inp_ATOM *at_norm, inp_ATOM *at_fixed_bonds_out, T_GROUP_INFO *t_group_info ); -/* create one InChI */ -int MakeOneInChIOutOfStrFromINChI2( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - T_GROUP_INFO **t_group_info, inp_ATOM **at_norm, inp_ATOM **at_prep ); -/* fixed-H */ -int FillOutExtraFixedHDataRestr( StrFromINChI *pStruct ); -int FillOutExtraFixedHDataInChI( StrFromINChI *pStruct, INChI *pInChI[] ); -int FixFixedHRestoredStructure(ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, - StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, T_GROUP_INFO **ti, inp_ATOM **at_norm, inp_ATOM **at_prep, - INChI *pInChI[], long num_inp, int bHasSomeFixedH, int *pnNumRunBNS, int *pnTotalDelta, - int forbidden_edge_mask, int forbidden_stereo_edge_mask); -int FixRemoveExtraTautEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, - inp_ATOM *at2, inp_ATOM *atf, inp_ATOM *atn, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, ICR *picr, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ); -int FillOutCMP2FHINCHI( StrFromINChI *pStruct, inp_ATOM *at2, VAL_AT *pVA, INChI *pInChI[], CMP2FHINCHI *pc2i ); -int FillOutCMP2MHINCHI( StrFromINChI *pStruct, ALL_TC_GROUPS *pTCGroups, inp_ATOM *at2, - VAL_AT *pVA, INChI *pInChI[], CMP2MHINCHI *pc2i ); - -int bHas_N_V( inp_ATOM *at2, int num_atoms ); - -int GetPlusMinusVertex( BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups, int bCheckForbiddenPlus, int bCheckForbiddenMinus ); -int FixMobileHRestoredStructure(ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, - StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, T_GROUP_INFO **ppt_group_info, inp_ATOM **ppat_norm, - inp_ATOM **ppat_prep, INChI *pInChI[], long num_inp, int bHasSomeFixedH, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask); - -int FixRestoredStructureStereo( INCHI_MODE cmpInChI, ICR *icr, INCHI_MODE cmpInChI2, ICR *icr2, - ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, - StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, T_GROUP_INFO **ppt_group_info, inp_ATOM **ppat_norm, - inp_ATOM **ppat_prep, INChI *pInChI[], long num_inp, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask); - -int AddRemProtonsInRestrStruct( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, - int bHasSomeFixedH, - StrFromINChI *pStruct, int num_components, - StrFromINChI *pStructR, int num_componentsR, - NUM_H *pProtonBalance, int *recmet_change_balance ); -int AllInchiToStructure( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, char *szCurHdr, - ICHICONST SRM *pSrm, int bReqNonTaut, StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], - InpInChI *pOneInput ); -int AddProtonAndIsoHBalanceToMobHStruct( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, - long num_inp, int bHasSomeFixedH, char *szCurHdr, - StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], InpInChI *pOneInput); -int InChI2Atom( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, const char *szCurHdr, long num_inp, - StrFromINChI *pStruct, int iComponent, int iAtNoOffset, int bI2A_Flag, int bHasSomeFixedH, InpInChI *pOneInput); - -int MarkDisconectedIdenticalToReconnected ( InpInChI *pOneInput ); -void RemoveFixHInChIIdentical2MobH( InpInChI *pOneInput ); -void SetUpSrm( SRM *pSrm ); -void FreeInpInChI( InpInChI *pOneInput ); -void FreeStrFromINChI( StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], int nNumComponents[INCHI_NUM][TAUT_NUM] ); -int OldPrintCompareOneOrigInchiToRevInChI(StrFromINChI *pStruct, INChI *pInChI[TAUT_NUM], int bMobileH, - int iComponent, long num_inp, char *szCurHdr); -int CompareOneOrigInchiToRevInChI(StrFromINChI *pStruct, INChI *pInChI[TAUT_NUM], int bMobileH, int iComponent, - long num_inp, char *szCurHdr, - COMPONENT_REM_PROTONS *nCurRemovedProtons, INCHI_MODE CompareInchiFlags[]); -int CompareTwoPairsOfInChI( INChI *pInChI1[TAUT_NUM], INChI *pInChI2[TAUT_NUM], - int bMobileH, INCHI_MODE CompareInchiFlags[] ); -INCHI_MODE CompareReversedINChI3( INChI *i1 /* InChI from reversed struct */, INChI *i2 /* input InChI */, - INChI_Aux *a1, INChI_Aux *a2, int *err ); -INCHI_MODE CompareReversedStereoINChI3( INChI_Stereo *s1/* InChI from reversed struct */, INChI_Stereo *s2 /* input InChI */, ICR *picr); -int CompareAllOrigInchiToRevInChI(StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], InpInChI *pOneInput, int bReqNonTaut, - long num_inp, char *szCurHdr); -int CompareAllDisconnectedOrigInchiToRevInChI(StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], - InpInChI *pOneInput, int bHasSomeFixedH, - long num_inp, char *szCurHdr); -int insertions_sort_AT_NUMB( AT_NUMB *base, int num ); - -int AddRemIsoProtonsInRestrStruct( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, int bHasSomeFixedH, - StrFromINChI *pStruct, int num_components, - StrFromINChI *pStructR, int num_componentsR, - NUM_H pProtonBalance[], NUM_H recmet_change_balance[] ); -int OutputInChIOutOfStrFromINChI(ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd_inp, - long num_inp, int bINChIOutputOptions, - INCHI_IOSTREAM *pout, INCHI_IOSTREAM *plog, - InpInChI *pOneInput, int bHasSomeFixedH, - unsigned char save_opt_bits); - -int MergeStructureComponents( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, char *szCurHdr, - ICHICONST SRM *pSrm, int bReqNonTaut, StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], - InpInChI *pOneInput ); -int AddOneMsg( char *szMsg, int used_len, int tot_len, const char *szAddMsg, const char *szDelim ); -int FillOutCompareMessage( char *szMsg, int nLenMsg, INCHI_MODE bits[] ); -void clear_t_group_info( T_GROUP_INFO *ti ); -int bInpInchiComponentExists( InpInChI *pOneInput, int iINCHI, int bMobileH, int k ); -int bInpInchiComponentDeleted( InpInChI *pOneInput, int iInChI, int bMobileH, int k ); -int bRevInchiComponentExists( StrFromINChI *pStruct, int iInChI, int bMobileH, int k ); -int bRevInchiComponentDeleted( StrFromINChI *pStruct, int iInChI, int bMobileH, int k ); -int DetectInpInchiCreationOptions ( InpInChI *pOneInput, int *bHasReconnected, int *bHasMetal, - int *bHasFixedH, int *sFlag, int *bTautFlag ); -int DisplayStructureComponents( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, char *szCurHdr, - ICHICONST SRM *pSrm, int bReqNonTaut, StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], - InpInChI *pOneInput ); -int DisplayOneRestoredComponent( StrFromINChI *pStruct, inp_ATOM *at, - int iComponent, int nNumComponents, int bMobileH, - const char *szCurHdr ); -int DisplayAllRestoredComponents( inp_ATOM *at, int num_at, const char *szCurHdr ); -int CountStereoTypes( INChI *pInChI, int *num_known_SB, int *num_known_SC, - int *num_unk_und_SB, int *num_unk_und_SC, - int *num_SC_PIII, int *num_SC_AsIII); -int GetNumNeighborsFromInchi( INChI *pInChI, AT_NUMB nAtNumber ); -int bIsUnsatCarbonInASmallRing( inp_ATOM *at, VAL_AT *pVA, int iat, BFS_Q *pbfsq, int min_ring_size ); - -int MakeProtonComponent( StrFromINChI *pStruct, int iComponent, int num_prot ); - -/* extra configurarion */ -#define KEEP_METAL_EDGE_FLOW 0 /* counterexample: mdb0-1738.sdf.txt */ -#define MOVE_CHARGES_FROM_HETEREO_TO_METAL 0 /* disabled */ -#define FIX_ADD_PROTON_FOR_ADP 0 /* not used */ - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif diff --git a/INCHI-1-SRC/INCHI/common/ichisize.h b/INCHI-1-SRC/INCHI/common/ichisize.h deleted file mode 100644 index d6f3ab4..0000000 --- a/INCHI-1-SRC/INCHI/common/ichisize.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef ___INCHISIZE_H__ -#define ___INCHISIZE_H__ - -typedef unsigned short AT_NUMB; -typedef unsigned short AT_RANK; -#define AT_RANK_MASK ((AT_RANK)~0) - -typedef signed short NUM_H; -#define MAX_ATOMS 1024 - - -#define CHAR_MASK 0xFF - - -typedef AT_RANK *pAT_RANK; -typedef pAT_RANK *ppAT_RANK; - -typedef unsigned long INCHI_MODE; - -#define LEN_COORD 10 -#define NUM_COORD 3 -typedef char MOL_COORD[LEN_COORD*NUM_COORD + NUM_COORD-1]; /*copied 30 bytes from MOLfile */ - - -#endif /* ___INCHISIZE_H__ */ - diff --git a/INCHI-1-SRC/INCHI/common/ichisort.c b/INCHI-1-SRC/INCHI/common/ichisort.c deleted file mode 100644 index 0daa175..0000000 --- a/INCHI-1-SRC/INCHI/common/ichisort.c +++ /dev/null @@ -1,572 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - -#include "mode.h" - -#include "incomdef.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichicant.h" -#include "ichicomn.h" - -#include "ichicomp.h" - -#define RET_MAX 32767 - -/**********************************************************************************/ -void inchi_swap ( char *a, char *b, size_t width ) -{ - char tmp; - if ( a != b ) - while ( width-- ) { - tmp = *a; - *a++ = *b; - *b++ = tmp; - } -} -/**********************************************************************************/ -/* Sort by insertions */ -int insertions_sort( void *base, size_t num, size_t width, int ( *compare )(const void *e1, const void *e2 ) ) -{ - char *i, *j, *pk = (char*)base; - int num_trans = 0; - size_t k; - for( k=1; k < num; k++, pk += width ) { - /*for( i = pk, j = pk + width; j > (char*)base && (*compare)(i,j) > 0; j=i, i -= width )*/ - for( i = j = pk + width; j > (char*)base && (i -= width,(*compare)(i,j)) > 0; j=i ) /* changed to keep BoundsChecker happy 2007-09-24 DT */ - { - inchi_swap( i, j, width ); - num_trans ++; - } - } - return num_trans; -} -/**********************************************************************************/ -/* Sort by insertions */ -int insertions_sort_AT_NUMBERS( AT_NUMB *base, int num, int ( *compare )(const void *e1, const void *e2 ) ) -{ - AT_NUMB *i, *j, *pk, tmp; - int k, num_trans = 0; - for( k=1, pk = base; k < num; k++, pk ++ ) { - for( j = (i = pk) + 1, tmp = *j; j > base && (*compare)(i,&tmp) > 0; j=i, i -- ) { - *j = *i; - num_trans ++; - } - *j = tmp; - } - return num_trans; -} -/**********************************************************************************/ -/* Sort neighbors according to ranks in ascending order */ -void insertions_sort_NeighList_AT_NUMBERS( NEIGH_LIST base, AT_RANK *nRank ) -{ - AT_NUMB *i, *j, *pk, tmp; - AT_RANK rj; /* optimization */ - int k, num = (int)*base++; - for( k=1, pk = base; k < num; k++, pk ++ ) { - for( j = (i = pk) + 1, rj=nRank[(int)*j]; j > base && nRank[(int)*i] > rj; j=i, i -- ) { - tmp = *i; - *i = *j; - *j = tmp; - } - } -} -/**********************************************************************************/ -/* Sort neighbors according to ranks in ascending order */ -int insertions_sort_AT_RANK( AT_RANK *base, int num ) -{ - AT_RANK *i, *j, *pk, tmp; - int k, num_trans = 0; - for( k=1, pk = base; k < num; k++, pk ++ ) { - for( j = (i = pk) + 1, tmp = *j; j > base && *i > tmp; j=i, i -- ) { - *j = *i; - num_trans ++; - } - *j = tmp; - } - return num_trans; -} -/**********************************************************************************/ -/* Sort neighbors according to ranks in ascending order */ -int insertions_sort_NeighList_AT_NUMBERS3( NEIGH_LIST base, AT_RANK *nRank ) -{ - AT_NUMB *i, *j, *pk, tmp; - AT_RANK rj; - int k, n, num = (int)*base++; - for( k=1, pk = base, n=0; k < num; k++, pk ++ ) { - for( j = (i = pk) + 1, rj=nRank[(int)(tmp=*j)]; j > base && nRank[(int)*i] > rj; j=i, i -- ) { - *j = *i; - n ++; - } - *j = tmp; - } - return n; -} - -/**********************************************************************************/ -/* Sort neighbors according to symm. ranks (primary key) and canon. ranks (secondary key), in descending order */ -void insertions_sort_NeighListBySymmAndCanonRank( NEIGH_LIST base, const AT_RANK *nSymmRank, const AT_RANK *nCanonRank ) -{ - AT_NUMB *i, *j, *pk, tmp; - int diff; - int k, num = (int)*base++; - for( k=1, pk = base; k < num; k++, pk ++ ) { - for( j = (i = pk) + 1; j > base && /* always j > i */ - ( 0 > (diff = (int)nSymmRank[(int)*i] - (int)nSymmRank[(int)*j]) || - !diff && nCanonRank[(int)*i] < nCanonRank[(int)*j]); j=i, i -- ) { - tmp = *i; - *i = *j; - *j = tmp; - } - } -} - -/********************************************************************************************* - * - * Comparison functions - * - *********************************************************************************************/ -int CompNeighborsAT_NUMBER( const void* a1, const void* a2) -{ -#ifdef CT_NEIGH_INCREASE - return (int)pn_RankForSort[pNeighborsForSort[(int)*(const AT_NUMB*)a1]] - - (int)pn_RankForSort[pNeighborsForSort[(int)*(const AT_NUMB*)a2]]; -#else - return (int)pn_RankForSort[pNeighborsForSort[(int)*(const AT_NUMB*)a2]] - - (int)pn_RankForSort[pNeighborsForSort[(int)*(const AT_NUMB*)a1]]; -#endif -} - -/**********************************************************************************/ -int comp_AT_RANK( const void* a1, const void* a2) -{ - return (int)*(const AT_RANK*)a1 - (int)*(const AT_RANK*)a2; -} - -/**********************************************************************************/ -/* Compare for sorting Ranks only */ -int CompRank(const void* a1, const void* a2 ) -{ - int ret = (int)pn_RankForSort[(int)*(const AT_RANK*)a1] - - (int)pn_RankForSort[(int)*(const AT_RANK*)a2]; - return ret; -} -/**********************************************************************************/ -int CompRanksOrd( const void* a1, const void* a2 ) -{ - int ret; - ret = (int)pn_RankForSort[(int)*(const AT_RANK*)a1] - - (int)pn_RankForSort[(int)*(const AT_RANK*)a2]; - if ( !ret ) - ret = (int)*(const AT_RANK*)a1 - (int)*(const AT_RANK*)a2; - return ret; -} -/**********************************************************************************/ -int CompAtomInvariants2Only( const void* a1, const void* a2 ) -{ - const ATOM_INVARIANT2 *pAI1 = pAtomInvariant2ForSort + (int)*(const AT_RANK*)a1; - const ATOM_INVARIANT2 *pAI2 = pAtomInvariant2ForSort + (int)*(const AT_RANK*)a2; - int i; - for ( i = 0; i < AT_INV_BREAK1; i ++ ) { - if ( pAI1->val[i] == pAI2->val[i] ) - continue; - return (int)pAI1->val[i] - (int)pAI2->val[i]; - } - if ( pAI1->iso_sort_key != pAI2->iso_sort_key ) { - return ( pAI1->iso_sort_key > pAI2->iso_sort_key )? 1 : -1; - } - for ( ; i < AT_INV_LENGTH; i ++ ) { - if ( pAI1->val[i] != pAI2->val[i] ) - continue; - return (int)pAI1->val[i] - (int)pAI2->val[i]; - } - if ( pAI1->iso_aux_key != pAI2->iso_aux_key ) { - return ( pAI1->iso_aux_key > pAI2->iso_aux_key )? 1 : -1; - } - return 0; -} -/**********************************************************************************/ -int CompAtomInvariants2( const void* a1, const void* a2 ) -{ - /* Warning: the following line may be compiler implementation dependent */ - int ret = CompAtomInvariants2Only( a1, a2 ); - if ( !ret ) - ret = (int)*(const AT_RANK*)a1 - (int)*(const AT_RANK*)a2; - return ret; -} -/**********************************************************************************/ -/* Compare two elements lexicographically */ -int CompChemElemLex( const void *a1, const void *a2 ) -{ - return memcmp( a1, a2, 2); -} -/**********************************************************************************/ -/* lexicographic compare */ -int CompareNeighListLex( NEIGH_LIST pp1, NEIGH_LIST pp2, const AT_RANK *nRank) -{ - int len1 = (int)*pp1++; - int len2 = (int)*pp2++; - int len = inchi_min( len1, len2 ); - int diff = 0; - while ( len -- > 0 && !( diff = (int)nRank[*pp1++] - (int)nRank[*pp2++] ) ) - ; - return diff? diff : (len1 - len2); - -} -/**********************************************************************************/ -/* lexicographic compare */ -int CompareNeighListLexUpToMaxRank( NEIGH_LIST pp1, NEIGH_LIST pp2, const AT_RANK *nRank, AT_RANK nMaxAtNeighRank ) -{ - int len1 = (int)*pp1++; - int len2 = (int)*pp2++; - int diff = 0; - int len; - while( 0 < len1 && nRank[pp1[len1-1]] > nMaxAtNeighRank ) { - len1 --; - } - while( 0 < len2 && nRank[pp2[len2-1]] > nMaxAtNeighRank ) { - len2 --; - } - len = inchi_min( len1, len2 ); - while ( len -- > 0 && !( diff = (int)nRank[*pp1++] - (int)nRank[*pp2++] ) ) - ; - return diff? diff : (len1 - len2); - -} -/**********************************************************************************/ -int compare_NeighLists( const NEIGH_LIST *op1, const NEIGH_LIST *op2 ) -{ - return CompareNeighListLex( *op1, *op2, pn_RankForSort); -} -/**********************************************************************************/ -int CompNeighListRanks( const void* a1, const void* a2 ) -{ - int ret; - ret = (int)pn_RankForSort[*((const AT_RANK*)a1)] - - (int)pn_RankForSort[*((const AT_RANK*)a2)]; - if ( !ret ) - ret = compare_NeighLists( pNeighList_RankForSort + *((const AT_RANK*)a1), - pNeighList_RankForSort + *((const AT_RANK*)a2) ); - return ret; -} -/**********************************************************************************/ -int CompNeighLists( const void* a1, const void* a2 ) -{ - int ret; - ret = compare_NeighLists( pNeighList_RankForSort + *((const AT_RANK*)a1), - pNeighList_RankForSort + *((const AT_RANK*)a2) ); - return ret; -} -/**********************************************************************************/ -int CompNeighListsUpToMaxRank( const void* a1, const void* a2 ) -{ - int ret; - ret = CompareNeighListLexUpToMaxRank( pNeighList_RankForSort[*((const AT_RANK*)a1)], - pNeighList_RankForSort[*((const AT_RANK*)a2)], - pn_RankForSort, nMaxAtNeighRankForSort ); - return ret; -} -/**********************************************************************************/ -int CompNeighListRanksOrd( const void* a1, const void* a2 ) -{ - int ret = CompNeighListRanks( a1, a2 ); - if ( !ret ) - ret = (int)*((const AT_RANK*)a1) - (int)*((const AT_RANK*)a2); /* keep original order if identical */ - return ret; -} -/**********************************************************************************/ -int CompRanksInvOrd( const void* a1, const void* a2 ) -{ - return (int)*(const AT_RANK*)a2 - (int)*(const AT_RANK*)a1; -} -/**********************************************************************************/ -int CompNeighborsRanksCountEql( const void* a1, const void* a2 ) -{ -#ifdef CT_NEIGH_INCREASE - int ret = (int)pn_RankForSort[(int)*(const AT_RANK*)a1] - - (int)pn_RankForSort[(int)*(const AT_RANK*)a2]; -#else - int ret = (int)pn_RankForSort[(int)*(const AT_RANK*)a2] - - (int)pn_RankForSort[(int)*(const AT_RANK*)a1]; -#endif - nNumCompNeighborsRanksCountEql += !ret; - return ret; -} -/**************************************************************************************** - * - * In this neighbor list the (vertex number) = (canonical number) - 1 - * Since LinearCT is sorted so that parents are in ascending order - * and all neighbors of a parent are smaller than the parent and are - * in ascending order, the neighbors in the NEIGH_LIST are automatically - * sorted in ascending order - */ -NEIGH_LIST *CreateNeighListFromLinearCT( AT_NUMB *LinearCT, int nLenCT, int num_atoms ) -{ - /* atom numbers in LinearCT are canonical numbers - * order: parent[i] > neigh[i][0] < neigh[i][1]... neigh[i+1][0] < ... - * parent[i] < parent[i+1] - */ - int i, j; - S_CHAR *valence = NULL; - NEIGH_LIST *pp = NULL; - AT_NUMB *pAtList = NULL; - AT_RANK n_vertex, n_neigh; - int err = 1, num_bonds; - int length, start; - if ( (int)LinearCT[0] > num_atoms ) { - goto exit_function; - } - if ( !(valence = (S_CHAR*)inchi_calloc( num_atoms+1, sizeof(valence[0]) ) ) ) { - goto exit_function; - } - for ( i = 1, num_bonds = 0, n_vertex = LinearCT[0]; i < nLenCT; i ++ ) { - if ( (n_neigh = LinearCT[i]) < n_vertex ) { - valence[n_neigh] ++; - valence[n_vertex] ++; - num_bonds += 2; - } else - if ( (int)(n_vertex = n_neigh) > num_atoms ) { - goto exit_function; - } - } - if ( (int)n_vertex != num_atoms ) { - goto exit_function; - } - length = num_bonds + num_atoms + 1; - if ( pp = (NEIGH_LIST *) inchi_calloc((num_atoms+1), sizeof(NEIGH_LIST)) ) { - if ( pAtList = (AT_NUMB *) inchi_malloc( length*sizeof(*pAtList) ) ) { - /* create empty connection table */ - for ( i = 1, length = 0; i <= num_atoms; i ++ ) { - start = length; - length += (valence[i]+1); - pp[i-1] = pAtList + start; - pp[i-1][0] = 0; - } - /* fill out the CT */ - for ( i = 1, n_vertex = LinearCT[0]-1; i < nLenCT; i ++ ) { - if ( (n_neigh = LinearCT[i]-1) < n_vertex ) { - /* vertex - neighbor connection */ - j = (int)(++pp[(int)n_vertex][0]); - pp[(int)n_vertex][j] = n_neigh; - /* neighbor - vertex connection */ - j = (int)(++pp[(int)n_neigh][0]); - pp[(int)n_neigh][j] = n_vertex; - - } else - if ( (int)(n_vertex = n_neigh) >= num_atoms ) { - goto exit_function; - } - } - err = 0; - } - } -exit_function: - if ( valence ) { - inchi_free( valence ); - } - if ( err ) { - if ( pAtList ) - inchi_free( pAtList ); - if ( pp ) { - inchi_free( pp ); - pp = NULL; - } - } - return pp; -} - -/*********************************************************************************** - * NEIGH_LIST pp[] is an array of pointers to the lists of neighboring atoms numbers - * The first number in each list is a number of neighbors. - * In case of bDoubleBondSquare != 0 neighbors connected by the double bond appear 2 times - * The first element pp[0] is a pointer to be deallocated to free all the lists. - */ -NEIGH_LIST *CreateNeighList( int num_atoms, int num_at_tg, sp_ATOM* at, - int bDoubleBondSquare, T_GROUP_INFO *t_group_info ) -{ - /* +1 to add NULL termination */ - NEIGH_LIST *pp = (NEIGH_LIST *) inchi_calloc((num_at_tg+1), sizeof(NEIGH_LIST)); - T_GROUP *t_group = NULL; - AT_NUMB *nEndpointAtomNumber = NULL; - int num_t_groups = 0; - int nFirstEndpointAtNoPos; - - AT_NUMB *pAtList = NULL; - int length, start, val, i, j; - if ( pp ) { - if ( num_at_tg > num_atoms ) { - t_group = t_group_info->t_group; - num_t_groups = t_group_info->num_t_groups; - nEndpointAtomNumber = t_group_info->nEndpointAtomNumber; - } - - if ( !bDoubleBondSquare ) { - for ( i = 0, length = 0; i < num_atoms; i ++ ) { - length += (int)at[i].valence + (num_t_groups && at[i].endpoint); - } - length += num_atoms; - for ( i = 0; i < num_t_groups; i ++ ) { - length += (int)t_group[i].nNumEndpoints; - } - length += num_t_groups; - - } else { - for ( i = 0, length = 0; i < num_atoms; i ++ ) { - val = (int)at[i].valence; - for ( j = 0; j < val; j ++ ) { - length += 1 + (bDoubleBondSquare && BOND_DOUBLE == at[i].bond_type[j]); - } - length += (num_t_groups && at[i].endpoint); - } - length += num_atoms; - for ( i = 0; i < num_t_groups; i ++ ) { - length += (int)t_group[i].nNumEndpoints; - } - length += num_t_groups; - } - length ++; /* +1 to save number of neighbors */ - if ( pAtList = (AT_NUMB *) inchi_malloc( length*sizeof(*pAtList) ) ) { - if ( !bDoubleBondSquare ) { - for ( i = 0, length = 0; i < num_atoms; i ++ ) { - val = at[i].valence; - start = length ++; - for ( j = 0; j < val; j ++ ) { - pAtList[length ++] = at[i].neighbor[j]; - } - /* add endpoint */ - if (num_t_groups && at[i].endpoint) { - pAtList[length ++] = num_atoms + (int)at[i].endpoint - 1; - } - pAtList[start] = length - start - 1; /* number of neighbors before the list of neighbors */ - pp[i] = pAtList + start; /* pointer to the */ - } - - } else { - for ( i = 0, length = 0; i < num_atoms; i ++ ) { - val = at[i].valence; - start = length ++; - for ( j = 0; j < val; j ++ ) { - pAtList[length ++] = at[i].neighbor[j]; - if ( bDoubleBondSquare && BOND_DOUBLE == at[i].bond_type[j] ) { - pAtList[length ++] = at[i].neighbor[j]; /* a list of neighbor orig. numbers */ - } - } - /* add endpoint */ - if (num_t_groups && at[i].endpoint) { - pAtList[length ++] = num_atoms + (int)at[i].endpoint - 1; - } - pAtList[start] = length - start - 1; /* number of neighbors before the list of neighbors */ - pp[i] = pAtList + start; /* pointer to the */ - } - } - - /* add t-groups */ - for ( i = 0; i < num_t_groups; i ++ ) { - val = (int)t_group[i].nNumEndpoints; - start = length ++; - nFirstEndpointAtNoPos = (int)t_group[i].nFirstEndpointAtNoPos; - for ( j = 0; j < val; j ++ ) { - pAtList[length ++] = nEndpointAtomNumber[nFirstEndpointAtNoPos+j]; - } - pAtList[start] = length - start - 1; /* number of neighbors before the list of neighbors */ - pp[num_atoms+i] = pAtList + start; /* pointer to the */ - } - } else { - inchi_free ( pp ); - return NULL; - } - } - return pp; -} -/**********************************************************************************/ -void FreeNeighList( NEIGH_LIST *pp ) -{ - if ( pp ) { - if ( pp[0] ) { - inchi_free( pp[0] ); - } - inchi_free( pp ); - } -} - -/**********************************************************************************/ -int BreakAllTies( int num_atoms, int num_max, AT_RANK **pRankStack, - NEIGH_LIST *NeighList, AT_RANK *nTempRank, CANON_STAT *pCS) -{ - int i, nRet = -1, nNumRanks=1 /* value does not matter*/; - - AT_RANK *nPrevRank = *pRankStack ++; - AT_RANK *nPrevAtomNumber = *pRankStack ++; - - AT_RANK *nNewRank = NULL; - AT_RANK *nNewAtomNumber = NULL; - - if ( !pRankStack[0] ) { - pRankStack[0] = (AT_RANK *) inchi_malloc(num_max*sizeof(*nNewRank)); - } - if ( !pRankStack[1] ) { - pRankStack[1] = (AT_RANK *) inchi_malloc(num_max*sizeof(*nNewAtomNumber)); - } - if ( !pRankStack[0] || !pRankStack[1] ) - return CT_OUT_OF_RAM; /* */ - nNewRank = pRankStack[0]; - nNewAtomNumber = pRankStack[1]; - - if ( nNewRank && nNewAtomNumber ) { - memcpy( nNewAtomNumber, nPrevAtomNumber, num_atoms*sizeof(nNewAtomNumber[0])); - memcpy( nNewRank, nPrevRank, num_atoms*sizeof(nNewRank[0])); - - for ( i = 1, nRet=0; i < num_atoms; i ++ ) { /* 12-12-2001: replaced Prev... with New... */ - if ( nNewRank[(int)nNewAtomNumber[i-1]] == nNewRank[(int)nNewAtomNumber[i]] ) { - nNewRank[nNewAtomNumber[i-1]] = (AT_RANK)i; - nNumRanks = DifferentiateRanks2( num_atoms, NeighList, - nNumRanks, nNewRank, nTempRank, - nNewAtomNumber, &pCS->lNumNeighListIter, 1 ); - pCS->lNumBreakTies ++; - nRet ++; - } - } - } - return nRet; -} diff --git a/INCHI-1-SRC/INCHI/common/ichister.c b/INCHI-1-SRC/INCHI/common/ichister.c deleted file mode 100644 index 654963a..0000000 --- a/INCHI-1-SRC/INCHI/common/ichister.c +++ /dev/null @@ -1,3870 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include - -#include "mode.h" - -#include "ichierr.h" -#include "inpdef.h" -#include "extr_ct.h" -#include "ichister.h" -#include "ichiring.h" -#include "ichi.h" - -#include "ichicomp.h" -#include "util.h" - -#define ZTYPE_DOWN (-1) /* should be equal to -ZTYPE_UP */ -#define ZTYPE_NONE 0 -#define ZTYPE_UP 1 /* should be equal to -ZTYPE_DOWN */ -#define ZTYPE_3D 3 -#define ZTYPE_EITHER 9999 - -/* criteria for ill-defined */ -#define MIN_ANGLE 0.10 /* 5.73 degrees */ -#define MIN_SINE 0.03 /* min edge/plane angle in case the tetrahedra has significantly different edge length */ -#define MIN_ANGLE_DBOND 0.087156 /* 5 degrees = max angle considered as too small for unambiguous double bond stereo */ -#define MIN_SINE_OUTSIDE 0.06 /* min edge/plane angle to determine whether the central atom is outside of the tetrahedra */ -#define MIN_SINE_SQUARE 0.125 /* min edge/plane angle in case the tetrahedra is somewhat close to a parallelogram */ -#define MIN_SINE_EDGE 0.167 /* min sine/(min.edge) ratio to avoid undefined in case of long edges */ -#define MIN_LEN_STRAIGHT 1.900 /* min length of two normalized to 1 bonds in a straight line */ -#define MAX_SINE 0.70710678118654752440084436210485 /* 1/sqrt(2)=sin(pi/4) */ -#define MIN_BOND_LEN 0.000001 -#define ZERO_LENGTH MIN_BOND_LEN -#define ZERO_FLOAT 1.0e-12 -#define BOND_PARITY_UNDEFINED 64 -#if ( STEREO_CENTER_BONDS_NORM == 1 ) -#define MPY_SINE 1.00 /* was 3.0 */ -#define MAX_EDGE_RATIO 2.50 /* max max/min edge ratio for a tetrahedra close to a parallelogram */ -#else -#define MPY_SINE 3.00 -#define MAX_EDGE_RATIO 6.00 /* max max/min edge ratio for a tetrahedra close to a parallelogram */ -#endif -/* local prototypes */ -static int save_a_stereo_bond( int z_prod, int result_action, - int at1, int ord1, AT_NUMB *stereo_bond_neighbor1, S_CHAR *stereo_bond_ord1, S_CHAR *stereo_bond_z_prod1, S_CHAR *stereo_bond_parity1, - int at2, int ord2, AT_NUMB *stereo_bond_neighbor2, S_CHAR *stereo_bond_ord2, S_CHAR *stereo_bond_z_prod2, S_CHAR *stereo_bond_parity2 ); -static double get_z_coord( inp_ATOM* at, int cur_atom, int neigh_no, int *nType,int bPointedEdgeStereo ); -static double len3( const double c[] ); -static double len2( const double c[] ); -static double* diff3( const double a[], const double b[], double result[] ); -static double* add3( const double a[], const double b[], double result[] ); -static double* mult3( const double a[], double b, double result[] ); -static double* copy3( const double a[], double result[] ); -static double* change_sign3( const double a[], double result[] ); -static double dot_prod3( const double a[], const double b[] ); -static int dot_prodchar3( const S_CHAR a[], const S_CHAR b[] ); -static double* cross_prod3( const double a[], const double b[], double result[] ); -static double triple_prod( double a[], double b[], double c[], double *sine_value ); -static double triple_prod_and_min_abs_sine(double at_coord[][3], double *min_sine); -static int are_3_vect_in_one_plane( double at_coord[][3], double min_sine); -static int triple_prod_char( inp_ATOM *at, int at_1, int i_next_at_1, S_CHAR *z_dir1, - int at_2, int i_next_at_2, S_CHAR *z_dir2 ); - -static int CompDble( const void *a1, const void *a2 ); -static int Get2DTetrahedralAmbiguity( double at_coord[][3], int bAddExplicitNeighbor, int bFix2DstereoBorderCase ); -static double triple_prod_and_min_abs_sine2(double at_coord[][3], double central_at_coord[], int bAddedExplicitNeighbor, double *min_sine, int *bAmbiguous); -static int are_4at_in_one_plane( double at_coord[][3], double min_sine); -static int bInpAtomHasRequirdNeigh ( inp_ATOM *at, int cur_at, int RequirdNeighType, int NumDbleBonds ); -static int bIsSuitableHeteroInpAtom( inp_ATOM *at ); -static int bIsOxide( inp_ATOM *at, int cur_at ); -static int half_stereo_bond_parity( inp_ATOM *at, int cur_at, inp_ATOM *at_removed_H, int num_removed_H, S_CHAR *z_dir, - int bPointedEdgeStereo, int vABParityUnknown ); -static int get_allowed_stereo_bond_type( int bond_type ); -static int can_be_a_stereo_bond_with_isotopic_H( inp_ATOM *at, int cur_at, INCHI_MODE nMode ); -static int half_stereo_bond_action( int nParity, int bUnknown, int bIsotopic, int vABParityUnknown ); -static int set_stereo_bonds_parity( sp_ATOM *out_at, inp_ATOM *at, int at_1, inp_ATOM *at_removed_H, int num_removed_H, - INCHI_MODE nMode, QUEUE *q, AT_RANK *nAtomLevel, - S_CHAR *cSource, AT_RANK min_sb_ring_size, - int bPointedEdgeStereo, int vABParityUnknown ); -static int can_be_a_stereo_atom_with_isotopic_H( inp_ATOM *at, int cur_at, int bPointedEdgeStereo ); -static int set_stereo_atom_parity( sp_ATOM *out_at, inp_ATOM *at, int cur_at, inp_ATOM *at_removed_H, int num_removed_H, - int bPointedEdgeStereo, int vABParityUnknown ); -/* -int set_stereo_parity( inp_ATOM* at, sp_ATOM* at_output, int num_at, int num_removed_H, - int *nMaxNumStereoAtoms, int *nMaxNumStereoBonds, INCHI_MODE nMode, int bPointedEdgeStereo, vABParityUnknown ); -int get_opposite_sb_atom( inp_ATOM *at, int cur_atom, int icur2nxt, int *pnxt_atom, int *pinxt2cur, int *pinxt_sb_parity_ord ); -*/ -int ReconcileCmlIncidentBondParities( inp_ATOM *at, int cur_atom, int prev_atom, S_CHAR *visited, int bDisconnected ); -int comp_AT_NUMB( const void* a1, const void* a2); -int GetHalfStereobond0DParity( inp_ATOM *at, int cur_at, AT_NUMB nSbNeighOrigAtNumb[], int nNumExplictAttachments, int bond_parity, int nFlag ); -int GetStereocenter0DParity( inp_ATOM *at, int cur_at, int j1, AT_NUMB nSbNeighOrigAtNumb[], int nFlag ); -int GetSbNeighOrigAtNumb( inp_ATOM *at, int cur_at, inp_ATOM *at_removed_H, int num_removed_H, AT_NUMB nSbNeighOrigAtNumb[]); -int FixSb0DParities( inp_ATOM *at, /* inp_ATOM *at_removed_H, int num_removed_H,*/ int chain_length, - int at_1, int i_next_at_1, S_CHAR z_dir1[], - int at_2, int i_next_at_2, S_CHAR z_dir2[], - int *pparity1, int *pparity2 ); - -/******************************************************************/ - - -static double *pDoubleForSort; - -/**********************************************************************************/ -int comp_AT_NUMB( const void* a1, const void* a2) -{ - return (int)*(const AT_NUMB*)a1 - (int)*(const AT_NUMB*)a2; -} -/******************************************************************/ -double get_z_coord( inp_ATOM* at, int cur_atom, int neigh_no, int *nType, int bPointedEdgeStereo ) -{ - int stereo_value = at[cur_atom].bond_stereo[neigh_no]; - int stereo_type = abs( stereo_value ); - int neigh = (int)at[cur_atom].neighbor[neigh_no]; - double z = at[neigh].z - at[cur_atom].z; - int bFlat; - - if ( bFlat = (fabs(z) < ZERO_LENGTH) ) { - int i; - for ( i = 0; i < at[cur_atom].valence; i ++ ) { - if ( fabs(at[cur_atom].z - at[(int)at[cur_atom].neighbor[i]].z) > ZERO_LENGTH ) { - bFlat = 0; - break; - } - } - } - - if ( bFlat ) { - if ( !bPointedEdgeStereo || bPointedEdgeStereo * stereo_value >= 0 ) { - /* bPointedEdgeStereo > 0: define stereo from pointed end of the stereo bond only */ - /* bPointedEdgeStereo < 0: define stereo from wide end of the stereo bond only (case of removed H) */ - switch( stereo_type ) { - /* 1=Up (solid triangle), 6=Down (Dashed triangle), 4=Either (zigzag triangle) */ - case 0: /* No stereo */ - *nType = ZTYPE_NONE; - break; - case STEREO_SNGL_UP: /* 1= Up */ - *nType = ZTYPE_UP; - break; - case STEREO_SNGL_EITHER: /* 4 = Either */ - *nType = ZTYPE_EITHER; - break; - case STEREO_SNGL_DOWN: /* 6 = Down */ - *nType = ZTYPE_DOWN; - break; - default: - *nType = ZTYPE_NONE; /* ignore unexpected values */ - } - if ( stereo_value < 0 && (*nType == ZTYPE_DOWN || *nType == ZTYPE_UP) ) - *nType = -*nType; - } else { - *nType = ZTYPE_NONE; /* no stereo */ - } - } else - if ( stereo_type == STEREO_SNGL_EITHER && - ( !bPointedEdgeStereo || bPointedEdgeStereo * stereo_value >= 0 ) ) { - *nType = ZTYPE_EITHER; - } else { - *nType = ZTYPE_3D; - } - return z; -} -/******************************************************************/ -double len3( const double c[] ) -{ - return sqrt( c[0]*c[0] + c[1]*c[1] + c[2]*c[2] ); -} -/******************************************************************/ -double len2( const double c[] ) -{ - return sqrt( c[0]*c[0] + c[1]*c[1] ); -} -/******************************************************************/ -double* diff3( const double a[], const double b[], double result[] ) -{ - - result[0] = a[0] - b[0]; - result[1] = a[1] - b[1]; - result[2] = a[2] - b[2]; - - return result; -} -/******************************************************************/ -double* add3( const double a[], const double b[], double result[] ) -{ - result[0] = a[0] + b[0]; - result[1] = a[1] + b[1]; - result[2] = a[2] + b[2]; - - return result; -} -/******************************************************************/ -double* mult3( const double a[], double b, double result[] ) -{ - result[0] = a[0] * b; - result[1] = a[1] * b; - result[2] = a[2] * b; - - return result; -} -/*************************************************************/ -double* copy3( const double a[], double result[] ) -{ - result[0] = a[0]; - result[1] = a[1]; - result[2] = a[2]; - - return result; -} -/*************************************************************/ -double* change_sign3( const double a[], double result[] ) -{ - result[0] = -a[0]; - result[1] = -a[1]; - result[2] = -a[2]; - - return result; -} -/*************************************************************/ -double dot_prod3( const double a[], const double b[] ) -{ - return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; -} -/*************************************************************/ -int dot_prodchar3( const S_CHAR a[], const S_CHAR b[] ) -{ - int prod = ((int)a[0]*(int)b[0] + (int)a[1]*(int)b[1] + (int)a[2]*(int)b[2])/100; - if ( prod > 100 ) - prod = 100; - else - if ( prod < -100 ) - prod = -100; - return prod; -} -/*************************************************************/ -double* cross_prod3( const double a[], const double b[], double result[] ) -{ - double tmp[3]; - - tmp[0] = (a[1]*b[2]-a[2]*b[1]); - tmp[1] = -(a[0]*b[2]-a[2]*b[0]); - tmp[2] = (a[0]*b[1]-a[1]*b[0]); - - result[0] = tmp[0]; - result[1] = tmp[1]; - result[2] = tmp[2]; - - return result; -} -/*************************************************************/ -double triple_prod( double a[], double b[], double c[], double *sine_value ) -{ - double ab[3], dot_prod_ab_c, abs_c, abs_ab; - cross_prod3( a, b, ab ); - /* ab[0] = (a[1]*b[2]-a[2]*b[1]); */ - /* ab[1] = -(a[0]*b[2]-a[2]*b[0]); */ - /* ab[2] = (a[0]*b[1]-a[1]*b[0]); */ - dot_prod_ab_c = dot_prod3( ab, c ); - /* dot_prod_ab_c = ab[0]*c[0] + ab[1]*c[1] + ab[2]*c[2]; */ - if ( sine_value ) { - abs_c = len3( c ); - /* abs_c = sqrt( c[0]*c[0] + c[1]*c[1] + c[2]*c[2] ); */ - abs_ab = len3( ab ); - /* abs_ab = sqrt( ab[0]*ab[0] + ab[1]*ab[1] + ab[2]*ab[2] ); */ - - if ( abs_c > 1.e-7 /* otherwise c has zero length */ && abs_ab > 1.e-7 /* otherwise a is parallel to b*/ ) { - *sine_value = MPY_SINE * dot_prod_ab_c / ( abs_c * abs_ab); - /* *sine_value = dot_prod_ab_c / ( abs_c * abs_ab); */ - } else { - *sine_value = 0.0; - } - } - return dot_prod_ab_c; -} -/*************************************************************/ -int CompDble( const void *a1, const void *a2 ) -{ - double diff = pDoubleForSort[*(const int*)a1] - pDoubleForSort[*(const int*)a2]; - if ( diff > 0.0 ) - return 1; - if ( diff < 0.0 ) - return -1; - return 0; -} -/*************************************************************/ -#define T2D_OKAY 1 -#define T2D_WARN 2 -#define T2D_UNDF 4 -int Get2DTetrahedralAmbiguity( double at_coord[][3], int bAddExplicitNeighbor, int bFix2DstereoBorderCase ) -{ -/* const double one_pi = 2.0*atan2(1.0 , 0.0 ); */ -const double one_pi = 3.14159265358979323846; /* M_PI */ -const double two_pi = 2.0*one_pi; -const double dAngleAndPiMaxDiff = 2.0*atan2(1.0, sqrt(7.0)); /* min sine between 2 InPlane bonds */ -int nBondType[MAX_NUM_STEREO_ATOM_NEIGH], nBondOrder[MAX_NUM_STEREO_ATOM_NEIGH]; -double dBondDirection[MAX_NUM_STEREO_ATOM_NEIGH]; -volatile double dAngle, dAlpha, dLimit, dBisector; - /* 2010-02-10 added 'volatile': workaround ensuring proper behavior for gcc 32-bit */ - /* cml-enabled compiles at >=O1 for SID484922 and alike (both lin&win had problems) */ -int nNumNeigh = MAX_NUM_STEREO_ATOM_NEIGH - (bAddExplicitNeighbor != 0); -int i, num_Up, num_Dn, bPrev_Up, cur_len_Up, cur_first_Up, len_Up, first_Up; -int ret=0; - - for ( i = 0, num_Up = num_Dn = 0; i < nNumNeigh; i ++ ) - { - dAngle = atan2( at_coord[i][1], at_coord[i][0] ); /* range from -pi to +pi */ - if ( dAngle < 0.0 ) { - dAngle += two_pi; - } - dBondDirection[i] = dAngle; - nBondType[i] = (at_coord[i][2] > 0.0)? 1 : (at_coord[i][2] < 0.0)? -1 : 0; /* z-coord sign */ - if ( nBondType[i] > 0 ) { - num_Up ++; - } else - if ( nBondType[i] < 0 ) { - num_Dn ++; - } - nBondOrder[i] = i; - } - if ( num_Up < num_Dn ) { - for ( i = 0; i < nNumNeigh; i ++ ) { - nBondType[i] = -nBondType[i]; - } - inchi_swap( (char*)&num_Dn, (char*)&num_Up, sizeof(num_Dn) ); - } - if ( !num_Up ) { - return T2D_UNDF; - } - - /* sort according to the bond orientations */ - pDoubleForSort = dBondDirection; - insertions_sort( nBondOrder, (unsigned) nNumNeigh, sizeof(nBondOrder[0]), CompDble ); - - /* find the longest contiguous sequence of Up bonds */ - if ( num_Up == nNumNeigh ) { - /* all bonds are Up */ - len_Up = cur_len_Up = nNumNeigh; /* added cur_len_Up initialization 1/8/2002 */ - first_Up = 0; - } else { - /* at least one bond is not Up */ - cur_len_Up = len_Up = bPrev_Up = 0; - /* prev. cycle header version --- - for ( i = 0; 1; i ++ ) { - if ( i >= nNumNeigh && !bPrev_Up ) { - break; - } - ----------} */ - /* look at all bonds and continue (circle therough the beginning) as long as the current bond is Up */ - for ( i = 0; i < nNumNeigh || bPrev_Up; i ++ ) { - if ( nBondType[nBondOrder[i % nNumNeigh]] > 0 ) { - if ( bPrev_Up ) { - cur_len_Up ++; /* uncrement number of Up bonds in current contiguous sequence of them */ - } else { - bPrev_Up = 1; /* start new contiguous sequence of Up bonds */ - cur_len_Up = 1; - cur_first_Up = i % nNumNeigh; - } - } else - if ( bPrev_Up ) { /* end of contiguous sequence of Up bonds */ - if ( cur_len_Up > len_Up ) { - first_Up = cur_first_Up; /* store the sequence because it is longer than the ptrvious one */ - len_Up = cur_len_Up; - } - bPrev_Up = 0; - } - } - } -#if ( FIX_2D_STEREO_BORDER_CASE == 1 ) - /* check if the bonds with ordering numbers first_Up+len_Up and first_Up+len_Up+1 */ - /* have identical angles. In this case switch their order to enlarge the Up sequence */ -#define ZERO_ANGLE 0.000001 - if ( nNumNeigh - len_Up >= 2 ) { - int next1, next2; - for ( i = 1; i < nNumNeigh - len_Up; i ++ ) { - next2 = (first_Up+len_Up + i) % nNumNeigh; /* the 2nd after Up sequence */ - if ( nBondType[nBondOrder[next2]] > 0 ) { - next1 = (first_Up+len_Up) % nNumNeigh; /* the 1st after Up sequence */ - dAngle = dBondDirection[nBondOrder[next1]] - dBondDirection[nBondOrder[next2]]; - if ( fabs(dAngle) < ZERO_ANGLE ) { - inchi_swap( (char*)&nBondOrder[next1], (char*)&nBondOrder[next2], sizeof(nBondOrder[0]) ); - len_Up ++; - break; - } - } - } - } - /* check whether the not-Up bond (located before the found first-Up) has */ - /* same angle as the Up bond that precedes this not-Up bond */ - if ( nNumNeigh - len_Up >= 2 ) { - int next1, next2; - for ( i = 1; i < nNumNeigh - len_Up; i ++ ) { - next2 = (first_Up+nNumNeigh - i - 1 ) % nNumNeigh; /* the 2nd before Up sequence */ - if ( nBondType[nBondOrder[next2]] > 0 ) { - next1 = (first_Up+nNumNeigh-1) % nNumNeigh; /* the 1st before Up sequence */ - dAngle = dBondDirection[nBondOrder[next1]] - dBondDirection[nBondOrder[next2]]; - if ( fabs(dAngle) < ZERO_ANGLE ) { - inchi_swap( (char*)&nBondOrder[next1], (char*)&nBondOrder[next2], sizeof(nBondOrder[0]) ); - first_Up = next1; - len_Up ++; - break; - } - } - } - } -#else - if ( bFix2DstereoBorderCase ) { - /* check if the bonds with ordering numbers first_Up+len_Up and first_Up+len_Up+1 */ - /* have identical angles. In this case switch their order to enlarge the Up sequence */ -#define ZERO_ANGLE 0.000001 - if ( nNumNeigh - len_Up >= 2 ) { - int next1, next2; - for ( i = 1; i < nNumNeigh - len_Up; i ++ ) { - next2 = (first_Up+len_Up + i) % nNumNeigh; /* the 2nd after Up sequence */ - if ( nBondType[nBondOrder[next2]] > 0 ) { - next1 = (first_Up+len_Up) % nNumNeigh; /* the 1st after Up sequence */ - dAngle = dBondDirection[nBondOrder[next1]] - dBondDirection[nBondOrder[next2]]; - if ( fabs(dAngle) < ZERO_ANGLE ) { - inchi_swap( (char*)&nBondOrder[next1], (char*)&nBondOrder[next2], sizeof(nBondOrder[0]) ); - len_Up ++; - break; - } - } - } - } - /* check whether the not-Up bond (located before the found first-Up) has */ - /* same angle as the Up bond that precedes this not-Up bond */ - if ( nNumNeigh - len_Up >= 2 ) { - int next1, next2; - for ( i = 1; i < nNumNeigh - len_Up; i ++ ) { - next2 = (first_Up+nNumNeigh - i - 1 ) % nNumNeigh; /* the 2nd before Up sequence */ - if ( nBondType[nBondOrder[next2]] > 0 ) { - next1 = (first_Up+nNumNeigh-1) % nNumNeigh; /* the 1st before Up sequence */ - dAngle = dBondDirection[nBondOrder[next1]] - dBondDirection[nBondOrder[next2]]; - if ( fabs(dAngle) < ZERO_ANGLE ) { - inchi_swap( (char*)&nBondOrder[next1], (char*)&nBondOrder[next2], sizeof(nBondOrder[0]) ); - first_Up = next1; - len_Up ++; - break; - } - } - } - } - } -#endif - /* Turn all the bonds around the center so that */ - /* the 1st Up bond has zero radian direction */ - dAlpha = dBondDirection[nBondOrder[first_Up]]; - for ( i = 0; i < nNumNeigh; i ++ ) { - if ( i == nBondOrder[first_Up] ) { - dBondDirection[i] = 0.0; - } else { - dAngle = dBondDirection[i] - dAlpha; - if ( dAngle < 0.0 ) { - dAngle += two_pi; - } - dBondDirection[i] = dAngle; - } - } - - /******************************************************** - * Process particular cases - ********************************************************/ - - - if ( nNumNeigh == 3 ) /************************ 3 bonds ************************/ - { - - switch( num_Up ) - { - - - - case 0: /* 0 Up */ - return T2D_UNDF; - - - - - case 1: /* 1 Up */ - if ( num_Dn ) - { -#ifdef _DEBUG - if ( num_Dn != 1 ) /* debug only */ - return -1; -#endif - ret = (T2D_UNDF | T2D_WARN); - } - else - { - dAngle = dBondDirection[nBondOrder[(first_Up + 2) % nNumNeigh]] - - dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; - - if ( dAngle < 0.0 ) - dAngle += two_pi; - if ( dAngle - one_pi < -MIN_ANGLE || dAngle - one_pi > MIN_ANGLE ) - { - ret = T2D_OKAY; - } - else - { - ret = (T2D_UNDF | T2D_WARN); - } - } - break; - - - - - case 2: /* 2 Up */ - if ( num_Dn ) - { - dAlpha = dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]] - - dBondDirection[nBondOrder[(first_Up ) % nNumNeigh]]; - - if ( dAlpha < 0.0 ) - dAlpha += two_pi; - - if ( dAlpha > one_pi - MIN_ANGLE ) - { - ret = T2D_OKAY; - } - else if ( dAlpha < two_pi / 3.0 - MIN_ANGLE ) - { - ret = (T2D_UNDF | T2D_WARN); - } - else - { - /* angle between 2 Up bonds is between 120 and 180 degrees */ - /* direction of the (Alpha angle bisector) + 180 degrees */ - dBisector = dBondDirection[nBondOrder[(first_Up ) % nNumNeigh]]; - dBisector+= dBondDirection[nBondOrder[(first_Up + 1 ) % nNumNeigh]]; - dBisector/= 2.0; - dBisector-= one_pi; - if ( dBisector < 0.0 ) - { - dBisector += two_pi; - } - if ( dAlpha < two_pi / 3.0 + MIN_ANGLE ) - { - /* dAlpha is inside ( 2pi/3 - eps, 2pi/3 + eps ) interval */ - dLimit = MIN_ANGLE * 3.0 / 2.0; - } - else - { - dLimit = dAlpha * 3.0 / 2.0 - one_pi; - } - - dAngle = dBondDirection[nBondOrder[(first_Up + 2 ) % nNumNeigh]]; - - if ( dBisector - dAngle < -dLimit || - dBisector - dAngle > dLimit ) - { - ret = (T2D_UNDF | T2D_WARN); - } - else - { - ret = T2D_OKAY; - } - } - } /* if ( num_Dn ) */ - else - { - ret = T2D_OKAY; - } - break; - - - - case 3: /* 3 Up */ - ret = T2D_OKAY; - break; - - - default:/* other Up */ - return -1; - - } /* eof switch( num_Up ) at nNumNeigh == 3 */ - - } - - - else if ( nNumNeigh == 4) /******************************* 4 bonds ********************/ - { - switch( num_Up ) - { - - case 0: /* 0 Up */ - return T2D_UNDF; - - - case 1: /* 1 Up */ - if ( num_Dn ) - { - if ( nBondType[nBondOrder[(first_Up + 2) % nNumNeigh]] < 0 ) - { - /* - * Up, In Plane, Dn, In Plane. Undefined if angle between - * two In Plane bonds is wuthin pi +/- 2*arcsine(1/sqrt(8)) interval - * That is, 138.5 to 221.4 degrees; for certainty the interval is - * increased by 5.7 degrees at each end to - * 134.8 to 227.1 degrees - */ - dAngle = dBondDirection[nBondOrder[(first_Up + 3) % nNumNeigh]] - - dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; - if ( dAngle < 0.0 ) { - dAngle += two_pi; - } - if ( fabs( dAngle - one_pi ) < dAngleAndPiMaxDiff + MIN_ANGLE ) { - ret = (T2D_UNDF | T2D_WARN); - } - else - { - ret = T2D_OKAY; - } - } - else - { - ret = T2D_OKAY; - } -#ifdef _DEBUG - if ( num_Dn != 1 ) /* debug only */ - return -1; -#endif - } - else - { - ret = T2D_OKAY; - dAngle = dBondDirection[nBondOrder[(first_Up + 3) % nNumNeigh]] - - dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; - if ( dAngle < 0.0 ) - { - dAngle += two_pi; - } - if ( dAngle < one_pi - MIN_ANGLE ) - { - ret |= T2D_WARN; - } - } - break; - - - case 2: /* 2 Up */ -#if ( FIX_2D_STEREO_BORDER_CASE == 1 ) - if ( len_Up == 1 ) - { - ret = T2D_OKAY; - } - else - { - dAngle = dBondDirection[nBondOrder[(first_Up + 3) % nNumNeigh]] - - dBondDirection[nBondOrder[(first_Up + 0) % nNumNeigh]]; - dAngle = fabs(two_pi - dAngle); - dAlpha = dBondDirection[nBondOrder[(first_Up + 2) % nNumNeigh]] - - dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; - dAlpha = fabs(dAlpha); - if ( dAngle < 2.0 * ZERO_ANGLE && dAlpha > MIN_ANGLE || - dAlpha < 2.0 * ZERO_ANGLE && dAngle > MIN_ANGLE ) - { - ret = (T2D_OKAY | T2D_WARN); - } - else - { - ret = (T2D_UNDF | T2D_WARN); - } - } -#else - if ( bFix2DstereoBorderCase ) - { - /* bug fix */ - if ( len_Up == 1 ) - { - ret = T2D_OKAY; - } - else - { - dAngle = dBondDirection[nBondOrder[(first_Up + 3) % nNumNeigh]] - - dBondDirection[nBondOrder[(first_Up + 0) % nNumNeigh]]; - dAngle = fabs(two_pi - dAngle); - dAlpha = dBondDirection[nBondOrder[(first_Up + 2) % nNumNeigh]] - - dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; - dAlpha = fabs(dAlpha); - if ( dAngle < 2.0 * ZERO_ANGLE && dAlpha > MIN_ANGLE || - dAlpha < 2.0 * ZERO_ANGLE && dAngle > MIN_ANGLE ) - { - ret = (T2D_OKAY | T2D_WARN); - } - else - { - ret = (T2D_UNDF | T2D_WARN); - } - } - } - else - { - /* original InChI v. 1 bug */ - if ( cur_len_Up == 1 ) - { - ret = T2D_OKAY; - } - else - { - ret = (T2D_UNDF | T2D_WARN); - } - } -#endif - break; - - - case 3: /* 3 Up */ - ret = T2D_OKAY; - dAngle = dBondDirection[nBondOrder[(first_Up + 2) % nNumNeigh]] - - dBondDirection[nBondOrder[(first_Up + 0) % nNumNeigh]]; - if ( dAngle < 0.0 ) - { - dAngle += two_pi; - } - if ( dAngle < one_pi - MIN_ANGLE ) - { - ret |= T2D_WARN; - } - break; - - case 4: /* 4 Up */ - ret = (T2D_UNDF | T2D_WARN); - break; - - default:/* other Up */ - return -1; /* program error */ - - } /* eof switch( num_Up ) at nNumNeigh == 4 */ - - if ( ret == T2D_OKAY ) - { - /* check whether all bonds are inside a less than 180 degrees sector */ - for ( i = 0; i < nNumNeigh; i ++ ) - { - dAngle = dBondDirection[nBondOrder[(i + nNumNeigh - 1) % nNumNeigh]] - - dBondDirection[nBondOrder[ i % nNumNeigh]]; - if ( dAngle < 0.0 ) - { - dAngle += two_pi; - } - if ( dAngle < one_pi - MIN_ANGLE ) - { - ret |= T2D_WARN; - break; - } - } - } - - } /* eof nNumNeigh == 4 */ - - else /*************************** number of bonds != 3 or 4 ******************/ - { - - return -1; /* error */ - } - - - return ret; - -} - -/*************************************************************/ -double triple_prod_and_min_abs_sine2(double at_coord[][3], double central_at_coord[], int bAddedExplicitNeighbor, double *min_sine, int *bAmbiguous) -{ - double min_sine_value=9999.0, sine_value, min_edge_len, max_edge_len, min_edge_len_NoExplNeigh, max_edge_len_NoExplNeigh; - double s0, s1, s2, s3, e01, e02, e03, e12, e13, e23, tmp[3], e[3][3]; - double prod, ret, central_prod[4]; - int bLongEdges; - - if ( !min_sine ) { - return triple_prod( at_coord[0], at_coord[1], at_coord[2], NULL ); - } - - ret = triple_prod( at_coord[0], at_coord[1], at_coord[2], &sine_value ); - sine_value = MPY_SINE * fabs( sine_value ); - - diff3( at_coord[1], at_coord[0], e[2] ); - diff3( at_coord[0], at_coord[2], e[1] ); - diff3( at_coord[2], at_coord[1], e[0] ); - - /* lengths of the 6 edges of the tetrahedra */ - e03 = len3( at_coord[0] ); /* 1 */ - e13 = len3( at_coord[1] ); - e23 = len3( at_coord[2] ); /* includes added neighbor if bAddedExplicitNeighbor*/ - e02 = len3( e[1] ); /* includes added neighbor if bAddedExplicitNeighbor*/ - e12 = len3( e[0] ); /* includes added neighbor if bAddedExplicitNeighbor*/ - e01 = len3( e[2] ); - - /* min & max edge length */ - max_edge_len = - min_edge_len = e03; - - if ( min_edge_len > e13 ) - min_edge_len = e13; - if ( min_edge_len > e01 ) - min_edge_len = e01; - min_edge_len_NoExplNeigh = min_edge_len; - - if ( min_edge_len > e23 ) - min_edge_len = e23; - if ( min_edge_len > e02 ) - min_edge_len = e02; - if ( min_edge_len > e12 ) - min_edge_len = e12; - - if ( max_edge_len < e13 ) - max_edge_len = e13; - if ( max_edge_len < e01 ) - max_edge_len = e01; - max_edge_len_NoExplNeigh = max_edge_len; - - if ( max_edge_len < e23 ) - max_edge_len = e23; - if ( max_edge_len < e02 ) - max_edge_len = e02; - if ( max_edge_len < e12 ) - max_edge_len = e12; - - if ( !bAddedExplicitNeighbor ) { - min_edge_len_NoExplNeigh = min_edge_len; - max_edge_len_NoExplNeigh = max_edge_len; - } - - bLongEdges = bAddedExplicitNeighbor? - ( max_edge_len_NoExplNeigh < MAX_EDGE_RATIO * min_edge_len_NoExplNeigh ) : - ( max_edge_len < MAX_EDGE_RATIO * min_edge_len ); - - if ( sine_value > MIN_SINE && ( min_sine || bAmbiguous ) ) { - if ( min_sine ) { - prod = fabs( ret ); - /* tetrahedra height = volume(prod) / area of a plane(cross_prod) */ - /* (instead of a tetrahedra calculate parallelogram/parallelepiped area/volume) */ - - /* 4 heights from each of the 4 vertices to the opposite plane */ - s0 = prod / len3( cross_prod3( at_coord[1], at_coord[2], tmp ) ); - s1 = prod / len3( cross_prod3( at_coord[0], at_coord[2], tmp ) ); - s2 = prod / len3( cross_prod3( at_coord[0], at_coord[1], tmp ) ); - s3 = prod / len3( cross_prod3( e[0], e[1], tmp ) ); - /* abs. value of a sine of an angle between each tetrahedra edge and plane */ - /* sine = height / edge length */ - if ( (sine_value = s0/e01) < min_sine_value ) - min_sine_value = sine_value; - if ( (sine_value = s0/e02) < min_sine_value ) - min_sine_value = sine_value; - if ( (sine_value = s0/e03) < min_sine_value ) - min_sine_value = sine_value; - - if ( (sine_value = s1/e01) < min_sine_value ) - min_sine_value = sine_value; - if ( (sine_value = s1/e12) < min_sine_value ) - min_sine_value = sine_value; - if ( (sine_value = s1/e13) < min_sine_value ) - min_sine_value = sine_value; - - if ( (sine_value = s2/e02) < min_sine_value ) - min_sine_value = sine_value; - if ( (sine_value = s2/e12) < min_sine_value ) - min_sine_value = sine_value; - if ( (sine_value = s2/e23) < min_sine_value ) - min_sine_value = sine_value; - - if ( (sine_value = s3/e03) < min_sine_value ) - min_sine_value = sine_value; - if ( (sine_value = s3/e13) < min_sine_value ) - min_sine_value = sine_value; - if ( (sine_value = s3/e23) < min_sine_value ) - min_sine_value = sine_value; - /* actually use triple sine */ - *min_sine = sine_value = MPY_SINE * min_sine_value; - } - - if ( bAmbiguous && sine_value >= MIN_SINE ) { - /* check whether the central atom is outside the tetrahedra (0,0,0), at_coord[0,1,2] */ - /* compare the tetrahedra volume and the volume of a tetrahedra having central_at_coord[] vertex */ - int i; - diff3( central_at_coord, at_coord[0], tmp ); - central_prod[0] = triple_prod( at_coord[0], at_coord[1], central_at_coord, NULL ); - central_prod[1] = triple_prod( at_coord[1], at_coord[2], central_at_coord, NULL ); - central_prod[2] = triple_prod( at_coord[2], at_coord[0], central_at_coord, NULL ); - central_prod[3] = triple_prod( e[2], e[1], tmp, NULL ); - for ( i = 0; i <= 3; i ++ ) { - if ( central_prod[i] / ret < -MIN_SINE_OUTSIDE ) { - *bAmbiguous |= AMBIGUOUS_STEREO; - break; - } - } - } -#if ( STEREO_CENTER_BONDS_NORM == 1 ) - - if ( bLongEdges && !bAddedExplicitNeighbor && max_edge_len >= MIN_LEN_STRAIGHT ) { - /* possible planar tetragon */ - if ( sine_value < MIN_SINE_SQUARE ) { - *min_sine = MIN_SINE / 2.0; /* force parity to be undefined */ - if ( bAmbiguous && !*bAmbiguous ) { - *bAmbiguous |= AMBIGUOUS_STEREO; - } - } - } - - if ( bLongEdges && sine_value < MIN_SINE_SQUARE && sine_value < MIN_SINE_EDGE * min_edge_len_NoExplNeigh ) { - *min_sine = MIN_SINE / 2.0; /* force parity to be undefined */ - if ( bAmbiguous && !*bAmbiguous ) { - *bAmbiguous |= AMBIGUOUS_STEREO; - } - } -#endif - - } else - if ( min_sine ) { - *min_sine = sine_value; - } - - return ret; -} -/*************************************************************/ -double triple_prod_and_min_abs_sine(double at_coord[][3], double *min_sine) -{ - double min_sine_value=9999.0, sine_value; - double prod=0.0; - - if ( !min_sine ) { - return triple_prod( at_coord[0], at_coord[1], at_coord[2], NULL ); - } - - prod = triple_prod( at_coord[0], at_coord[1], at_coord[2], &sine_value ); - sine_value = fabs( sine_value ); - min_sine_value = inchi_min( min_sine_value, sine_value ); - - prod = triple_prod( at_coord[1], at_coord[2], at_coord[0], &sine_value ); - sine_value = fabs( sine_value ); - min_sine_value = inchi_min( min_sine_value, sine_value ); - - prod = triple_prod( at_coord[2], at_coord[0], at_coord[1], &sine_value ); - sine_value = fabs( sine_value ); - min_sine_value = inchi_min( min_sine_value, sine_value ); - - *min_sine = min_sine_value; - - return prod; -} -/*************************************************************/ -/* Find if point (0,0,0)a and 3 atoms are in one plane */ -int are_3_vect_in_one_plane( double at_coord[][3], double min_sine) -{ - double actual_min_sine; - double prod; - prod = triple_prod_and_min_abs_sine( at_coord, &actual_min_sine); - return actual_min_sine <= min_sine; -} -/*************************************************************/ -/* Find if 4 atoms are in one plane */ -int are_4at_in_one_plane( double at_coord[][3], double min_sine) -{ - double actual_min_sine, min_actual_min_sine; - double coord[3][3], prod; - int i, k, j; - for ( k = 0; k < 4; k ++ ) { /* cycle added 4004-08-15 */ - for ( i = j = 0; i < 4; i ++ ) { - if ( i != k ) { - diff3( at_coord[i], at_coord[k], coord[j] ); - j ++; - } - } - prod = triple_prod_and_min_abs_sine( coord, &actual_min_sine); - if ( !k || actual_min_sine < min_actual_min_sine ) { - min_actual_min_sine = actual_min_sine; - } - } - return min_actual_min_sine <= min_sine; -} -/*************************************************************/ -int triple_prod_char( inp_ATOM *at, int at_1, int i_next_at_1, S_CHAR *z_dir1, - int at_2, int i_next_at_2, S_CHAR *z_dir2 ) -{ - inp_ATOM *at1, *at2; - double pnt[3][3], len; - int i; - int ret = 0; - - at1 = at + at_1; - at2 = at + at[at_1].neighbor[i_next_at_1]; - - pnt[0][0] = at2->x - at1->x; - pnt[0][1] = at2->y - at1->y; - pnt[0][2] = at2->z - at1->z; - - at2 = at + at_2; - at1 = at + at[at_2].neighbor[i_next_at_2]; - - pnt[1][0] = at2->x - at1->x; - pnt[1][1] = at2->y - at1->y; - pnt[1][2] = at2->z - at1->z; -/* - * resultant pnt vector directions: - * - * pnt[0] pnt[1] - * - * [at_1]---->[...] [...]---->[at_2] - * - * - * add3 below: (pnt[0] + pnt[1]) -> pnt[1] - */ - add3( pnt[0], pnt[1], pnt[1] ); - - - - for ( i = 0; i < 3; i ++ ) { - pnt[0][i] = (double)z_dir1[i]; - pnt[2][i] = (double)z_dir2[i]; - } - for ( i = 0; i < 3; i ++ ) { - len = len3( pnt[i] ); - if ( len < MIN_BOND_LEN ) { - if ( i == 1 && (at[at_1].bUsed0DParity || at[at_2].bUsed0DParity) ) { - pnt[i][0] = 0.0; - pnt[i][1] = 1.0; - pnt[i][2] = 0.0; - len = 1.0; /* standard at_1-->at_2 vector coordinates in case of 0D allene */ - } else { - goto exit_function; /* too short bond */ - } - } - mult3( pnt[i], 1.0/len, pnt[i] ); - } - len = 100.0*triple_prod(pnt[0], pnt[1], pnt[2], NULL ); -/* - * ^ pnt[0] - * | The orientation on this diagram - * | produces len = -100 - * [at_1]------>[at_2] - * pnt[1] / - * / - * / pnt[2] (up from the plane) - * v - * - * Note: len is invariant upon at_1 <--> at_2 transposition because - * triple product changes sign upon pnt[0]<-->pnt[2] transposition and - * triple product changes sign upon pnt[1]--> -pnt[1] change of direction: - * - * triple_prod(pnt[0], pnt[1], pnt[2], NULL ) = - * triple_prod(pnt[2], -pnt[1], pnt[0], NULL ) - * - */ - - ret = len >= 0.0? (int)(floor(len+0.5)) : -(int)(floor(0.5-len)); - -exit_function: - - return ret; -} - - -/****************************************************************/ - -#if ( NEW_STEREOCENTER_CHECK == 1 ) /* { */ - -/********************************************************************************************/ -int bInpAtomHasRequirdNeigh ( inp_ATOM *at, int cur_at, int RequirdNeighType, int NumDbleBonds ) -{ - /* RequirdNeighType: - reqired neighbor types (bitmap): - 0 => any neighbors - 1 => no terminal hydrogen atom neighbors - 2 => no terminal -X and -XH together (don't care about -X, -XH bond type, charge, radical) - (X = tautomeric endpoint atom) - NumDbleBonds: - if non-zero then allow double, alternating and tautomeric bonds - */ - int i, j, ni, nj, bond_type, num_1s, num_mult, num_other; - - if ( at[cur_at].endpoint ) { /* tautomeric endpoint cannot be a stereo center */ - return 0; - } - - if ( (1 & RequirdNeighType) && at[cur_at].num_H ) { - return 0; - } - - if ( 2 & RequirdNeighType ) { - for ( i = 0; i < at[cur_at].valence; i ++ ) { - ni = (int)at[cur_at].neighbor[i]; - if ( at[ni].valence != 1 || - !get_endpoint_valence( at[ni].el_number ) ) { - continue; - } - for ( j = i+1; j < at[cur_at].valence; j ++ ) { - nj = (int)at[cur_at].neighbor[j]; - if ( at[nj].valence != 1 || - at[ni].el_number != at[nj].el_number || - !get_endpoint_valence( at[nj].el_number ) ) { - continue; - } - /* - * if (at[ni].num_H != at[nj].num_H) then the atoms (neighbors of at[cur_at] - * are tautomeric endpoints and are indistinguishable => cur_at is not stereogenic - * if (at[ni].num_H == at[nj].num_H) then the neighbors are indistinguishable - * and cur_at will be found non-sterogenic later - * get_endpoint_valence() check will not allow the neighbors to be carbons - * Therefore the following "if" is not needed; we may just return 0. - */ - if ( at[ni].num_H != at[nj].num_H && strcmp(at[ni].elname, "C" ) ) { - return 0; /* found -X and -XH neighbors */ - } - } - } - } - - num_1s = num_mult = num_other = 0; - - for ( i = 0; i < at[cur_at].valence; i ++ ) { - bond_type = (at[cur_at].bond_type[i] & ~BOND_MARK_ALL); - switch( bond_type ) { - case BOND_SINGLE: - num_1s ++; - break; - case BOND_DOUBLE: - case BOND_ALTERN: - case BOND_TAUTOM: - case BOND_ALT12NS: - num_mult ++; - break; - default: - num_other ++; - break; - } - } - - if ( num_other ) { - return 0; - } - - if ( NumDbleBonds && NumDbleBonds > num_mult || - !NumDbleBonds && at[cur_at].valence != num_1s ) { - return 0; - } - return 1; -} -/********************************************************************************************/ -int bCanInpAtomBeAStereoCenter( inp_ATOM *at, int cur_at, int bPointedEdgeStereo ) -{ - -/************************************************************************************* - * current version - ************************************************************************************* - * Use #define to split the stereocenter description table into parts - * to make it easier to read - * - * --------- 4 single bonds stereocenters ------- - * 0 1 2 3 4 5 - * - * | | | | | | - * -C- -Si- -Ge- -Sn- >As[+] >B[-] - * | | | | | | - */ -#define SZELEM1 "C\000","Si", "Ge", "Sn", "As", "B\000", -#define CCHARGE1 0, 0, 0, 0, 1, -1, -#define CNUMBONDSANDH1 4, 4, 4, 4, 4, 4, -#define CCHEMVALENCEH1 4, 4, 4, 4, 4, 4, -#define CHAS3MEMBRING1 0, 0, 0, 0, 0, 0, -#define CREQUIRDNEIGH1 0, 0, 0, 0, 3, 0, -/* - * --------------- S, Se stereocenters ---------- - * 6 7 8 9 10 11 12 13 - * - * | | || | | || - * -S= =S= -S[+] >S[+] -Se= =Se= -Se[+] >Se[+] - * | | | | | | | | - */ -#define SZELEM2 "S\000","S\000","S\000","S\000","Se", "Se", "Se", "Se", -#define CCHARGE2 0, 0, 1, 1, 0, 0, 1, 1, -#define CNUMBONDSANDH2 3, 4, 3, 4, 3, 4, 3, 4, -#define CCHEMVALENCEH2 4, 6, 3, 5, 4, 6, 3, 5, -#define CHAS3MEMBRING2 0, 0, 0, 0, 0, 0, 0, 0, -#define CREQUIRDNEIGH2 3, 3, 3, 3, 3, 3, 3, 3, -/* - * ------------------ N, P stereocenters ----------------- - * 14 15 16 17 18 19 20 - * - * Phosphine Arsine - * X---Y - * | | \ / | | \ / \ / - * =N- >N[+] N >P[+] =P- P As - * | | | | | | | - */ -#define SZELEM3 "N\000","N\000","N\000","P\000","P\000","P\000", "As", -#define CCHARGE3 0, 1, 0, 1, 0, 0, 0, -#define CNUMBONDSANDH3 4, 4, 3, 4, 4, 3, 3, -#define CCHEMVALENCEH3 5, 4, 3, 4, 5, 3, 3, -#define CHAS3MEMBRING3 0, 0, 1, 0, 0, 0, 0, -#define CREQUIRDNEIGH3 3, 3, 1, 3, 3, 2, 2, - -#define PHOSPHINE_STEREO 19 /* the number must match Phosphine number in the comments, see above */ -#define ARSINE_STEREO 20 /* the number must match Arsine number in the comments, see above */ - - static char szElem[][3]={ SZELEM1 SZELEM2 SZELEM3 }; - static S_CHAR cCharge[]={ CCHARGE1 CCHARGE2 CCHARGE3 }; - static S_CHAR cNumBondsAndH[]={ CNUMBONDSANDH1 CNUMBONDSANDH2 CNUMBONDSANDH3 }; - static S_CHAR cChemValenceH[]={ CCHEMVALENCEH1 CCHEMVALENCEH2 CCHEMVALENCEH3 }; - static S_CHAR cHas3MembRing[]={ CHAS3MEMBRING1 CHAS3MEMBRING2 CHAS3MEMBRING3 }; - static S_CHAR cRequirdNeigh[]={ CREQUIRDNEIGH1 CREQUIRDNEIGH2 CREQUIRDNEIGH3 }; - - static int n = sizeof(szElem)/sizeof(szElem[0]); - /* reqired neighbor types (bitmap): - 0 => check bonds only - 1 => no terminal hydrogen atom neighbors - 2 => no terminal -X and -XH together (don't care the bond type, charge, radical) - (X = tautomeric endpoint atom) - Note: whenever cChemValenceH[] > cNumBondsAndH[] - the tautomeric and/or alternating bonds - are permitted - - */ - int i, ret = 0; - for ( i = 0; i < n; i++ ) { - if ( !strcmp( at[cur_at].elname, szElem[i]) && - at[cur_at].charge == cCharge[i] && - (!at[cur_at].radical || at[cur_at].radical == 1) && - at[cur_at].valence +at[cur_at].num_H == cNumBondsAndH[i] && - at[cur_at].chem_bonds_valence+at[cur_at].num_H == cChemValenceH[i] && - (cHas3MembRing[i]? is_atom_in_3memb_ring( at, cur_at ) : 1) && - bInpAtomHasRequirdNeigh ( at, cur_at, cRequirdNeigh[i], cChemValenceH[i]-cNumBondsAndH[i]) ) { - ret = cNumBondsAndH[i]; - break; - } - } - - if ( i == PHOSPHINE_STEREO && !(bPointedEdgeStereo & PES_BIT_PHOSPHINE_STEREO) ) - ret = 0; - if ( i == ARSINE_STEREO && !(bPointedEdgeStereo & PES_BIT_ARSINE_STEREO) ) - ret = 0; - return ret; -} - -#else /* } NEW_STEREOCENTER_CHECK { */ - -/********************************************************************************************/ -int bCanAtomBeAStereoCenter( char *elname, S_CHAR charge, S_CHAR radical ) -{ - static const char szElem[][3] = { "C\000", "Si", "Ge", "N\000", "P\000", "As", "B\000" }; - static const S_CHAR cCharge[] = { 0, 0, 0, 1, 1, 1, -1 }; - int i, ret = 0; - for ( i = 0; i < sizeof(szElem)/sizeof(szElem[0]); i++ ) { - if ( !strcmp( elname, szElem[i] ) && (charge == cCharge[i]) ) { - ret = (!radical || radical == RADICAL_SINGLET); - break; - } - } - return ret; -} -#endif /* } NEW_STEREOCENTER_CHECK */ - -/****************************************************************/ -/* used for atoms adjacent to stereogenic bonds only */ -int bAtomHasValence3( char *elname, S_CHAR charge, S_CHAR radical ) -{ - static const char szElem[][3] = { "N\000" }; - static const S_CHAR cCharge[] = { 0, }; - int i, ret = 0; - for ( i = 0; i < (int)(sizeof(szElem)/sizeof(szElem[0])); i++ ) { - if ( !strcmp( elname, szElem[i] ) && (charge == cCharge[i]) ) { - ret = ( !radical || radical == RADICAL_SINGLET ); - break; - } - } - return ret; -} - -/****************************************************************/ -/* used for atoms adjacent to stereogenic bonds only */ -int bCanAtomHaveAStereoBond( char *elname, S_CHAR charge, S_CHAR radical ) -{ - static const char szElem[][3] = { "C\000", "Si", "Ge", "N\000", "N\000" }; - static const S_CHAR cCharge[] = { 0, 0, 0, 0, 1, }; - static const int n = sizeof(szElem)/sizeof(szElem[0]); - int i, ret = 0; - for ( i = 0; i < n; i++ ) { - if ( !strcmp( elname, szElem[i] ) && (charge == cCharge[i]) ) { - ret = (!radical || radical == RADICAL_SINGLET); - break; - } - } - return ret; -} -/****************************************************************/ -/* used for atoms adjacent to stereogenic bonds only */ -int bCanAtomBeMiddleAllene( char *elname, S_CHAR charge, S_CHAR radical ) -{ - static const char szElem[][3] = { "C\000", "Si", "Ge", }; - static const S_CHAR cCharge[] = { 0, 0, 0, }; - static const int n = sizeof(szElem)/sizeof(szElem[0]); - int i, ret = 0; - for ( i = 0; i < n; i++ ) { - if ( !strcmp( elname, szElem[i] ) && (charge == cCharge[i]) ) { - ret = (!radical || radical == RADICAL_SINGLET); - break; - } - } - return ret; -} -/*****************************************************************/ -int bIsSuitableHeteroInpAtom( inp_ATOM *at ) -{ - int val, num_H; - if ( 0 == at->charge && - (!at->radical || RADICAL_SINGLET == at->radical) && - 0 < (val=get_endpoint_valence( at->el_number ) )) { - num_H = at->num_H; - if ( val == at->chem_bonds_valence + num_H ) { - switch( val ) { - case 2: /* O */ - if ( !num_H && 1 == at->valence ) - return 0; /* =O */ - break; /* not found */ - case 3: /* N */ - if ( 1 == at->valence && 1 == num_H || - 2 == at->valence && 0 == num_H ) - return 1; /* =N- or =NH */ - break; /* not found */ - } - } - } - return -1; -} -/****************************************************************/ -int bIsOxide( inp_ATOM *at, int cur_at ) -{ - int i, bond_type; - inp_ATOM *a = at + cur_at, *an; - for ( i = 0; i < a->valence; i ++ ) { - bond_type = (a->bond_type[i] &= ~BOND_MARK_ALL); - if ( bond_type == BOND_DOUBLE ) { - an = at + (int)a->neighbor[i]; - if ( 1 == an->valence && - !an->charge && !an->num_H && !an->radical && - 2 == get_endpoint_valence( an->el_number ) ) { - return 1; - } - } else - if ( bond_type == BOND_TAUTOM || bond_type == BOND_ALT12NS ) { - an = at + (int)a->neighbor[i]; - if ( 1 == an->valence && - 2 == get_endpoint_valence( an->el_number ) ) { - return 1; - } - } - } - return 0; -} -/****************************************************************/ -/* used for atoms adjacent to stereogenic bonds only */ -int bCanAtomBeTerminalAllene( char *elname, S_CHAR charge, S_CHAR radical ) -{ - static const char szElem[][3] = { "C\000", "Si", "Ge", }; - static const S_CHAR cCharge[] = { 0, 0, 0, }; - static const int n = sizeof(szElem)/sizeof(szElem[0]); - int i, ret = 0; - for ( i = 0; i < n; i++ ) { - if ( !strcmp( elname, szElem[i] ) && (charge == cCharge[i]) ) { - ret = (!radical || radical == RADICAL_SINGLET); - break; - } - } - return ret; -} -/************************************************************************/ -int GetHalfStereobond0DParity( inp_ATOM *at, int cur_at, AT_NUMB nSbNeighOrigAtNumb[], - int nNumExplictAttachments, int bond_parity, int nFlag ) -{ - int m, last_parity, cur_parity; - int i, icur2nxt, icur2neigh, cur_order_parity, nxt_at; - AT_NUMB nNextSbAtOrigNumb; - /* find atom parities for all valid streobonds incident to at[cur_at] */ - for ( m = 0, last_parity = 0; m < MAX_NUM_STEREO_BONDS && at[cur_at].sb_parity[m]; m ++ ) { - icur2nxt = icur2neigh = -1; /* ordering number of neighbors in nSbNeighOrigAtNumb[] */ - cur_parity = 0; /* parity for mth stereobond incident to the cur_at */ - if ( 0 <= at[cur_at].sb_ord[m] && at[cur_at].sb_ord[m] < at[cur_at].valence && - 0 <= (nxt_at = at[cur_at].neighbor[(int)at[cur_at].sb_ord[m]]) && - at[nxt_at].valence <= MAX_NUM_STEREO_BONDS && /* make sure it is a valid stereobond */ - (nNextSbAtOrigNumb = at[nxt_at].orig_at_number) ) { - /* since at[cur_at].sn_ord[m] = -1 for explicit H use at[cur_at].sn_orig_at_num[m] */ - for ( i = 0; i < nNumExplictAttachments; i ++ ) { - if ( at[cur_at].sn_orig_at_num[m] == nSbNeighOrigAtNumb[i] ) { - icur2neigh = i; /* neighbor */ - } else - if ( nNextSbAtOrigNumb == nSbNeighOrigAtNumb[i] ) { - icur2nxt = i; /* atom connected by a stereobond */ - } - } - if ( icur2neigh >= 0 && icur2nxt >= 0 ) { - if ( ATOM_PARITY_WELL_DEF(at[cur_at].sb_parity[m]) ) { - /* parity of at[cur_atom] neighbor permutation to reach this order: { next_atom, neigh_atom, ...} */ - cur_order_parity = (icur2nxt + icur2neigh + (icur2nxt > icur2neigh) - 1) % 2; - cur_parity = 2 - (cur_order_parity + at[cur_at].sb_parity[m]) % 2; - } else { - /* unknowm/undef parities do not depend on the neighbor order */ - cur_parity = at[cur_at].sb_parity[m]; - } - } - } else { - continue; - } - /* use a well-known parity if available; if not then use preferably the unknown */ - if ( !last_parity ) { - last_parity = cur_parity; - } else - if ( last_parity != cur_parity && cur_parity ) { - if ( ATOM_PARITY_WELL_DEF(last_parity) ) { - if ( ATOM_PARITY_WELL_DEF(cur_parity) ) { - last_parity = 0; /* error: all well-defined parities should be same */ - break; - } - } else - if ( ATOM_PARITY_WELL_DEF(cur_parity) ) { - /* replace unknown/undefined parity with well-known */ - last_parity = cur_parity; - } else { - /* select min unknown/undefined parity (out of AB_PARITY_UNKN and AB_PARITY_UNDF) */ - last_parity = inchi_min(cur_parity, last_parity); - } - } - } - if ( last_parity ) { - bond_parity = last_parity; - at[cur_at].bUsed0DParity |= nFlag; /* set flag: used stereobond 0D parity */ - } - return bond_parity; -} -/*******************************************************************************************/ -int FixSb0DParities( inp_ATOM *at, /* inp_ATOM *at_removed_H, int num_removed_H,*/ int chain_length, - int at_1, int i_next_at_1, S_CHAR z_dir1[], - int at_2, int i_next_at_2, S_CHAR z_dir2[], - int *pparity1, int *pparity2 ) -{ - int k, parity1, parity2, abs_parity1, abs_parity2; - int j1, j2, parity_sign; - /* - AT_NUMB nSbNeighOrigAtNumb1[MAX_NUM_STEREO_BOND_NEIGH], nSbNeighOrigAtNumb2[MAX_NUM_STEREO_BOND_NEIGH]; - int nNumExplictAttachments1, nNumExplictAttachments2; - */ - parity1 = parity2 = AB_PARITY_NONE; - j1 = j2 = -1; - parity_sign = ( *pparity1 < 0 || *pparity2 < 0 )? -1 : 1; - - abs_parity1 = abs(*pparity1); - abs_parity2 = abs(*pparity2); - - for ( k = 0; k < MAX_NUM_STEREO_BONDS && at[at_1].sb_parity[k]; k ++ ) { - if ( at[at_1].sb_ord[k] == i_next_at_1 ) { - parity1 = at[at_1].sb_parity[k]; - j1 = k; - } - } - for ( k = 0; k < MAX_NUM_STEREO_BONDS && at[at_2].sb_parity[k]; k ++ ) { - if ( at[at_2].sb_ord[k] == i_next_at_2 ) { - parity2 = at[at_2].sb_parity[k]; - j2 = k; - } - } - switch( (j1 >= 0) + 2*(j2 >= 0) ) { - case 0: - /* the bond has no 0D parity */ - *pparity1 = *pparity2 = parity_sign * AB_PARITY_UNDF; - return 0; - case 1: - case 2: - /* 0D parity data error */ - *pparity1 = *pparity2 = AB_PARITY_NONE; - return -1; - case 3: - /* the bond has 0D parity */ - switch ( !(ATOM_PARITY_WELL_DEF( abs_parity1 ) && ATOM_PARITY_WELL_DEF( parity1 )) + - 2 * !(ATOM_PARITY_WELL_DEF( abs_parity2 ) && ATOM_PARITY_WELL_DEF( parity2 )) ) { - case 0: - /* both parities are well-defined; continue */ - break; - case 1: - /* 0D parity not well-defined for at_1 */ - *pparity1 = parity_sign * (ATOM_PARITY_WELL_DEF( parity1 )? abs_parity1 : - ATOM_PARITY_WELL_DEF( abs_parity1 )? parity1 : - inchi_min(abs_parity1, parity1)); - *pparity2 = parity_sign * abs_parity2; - return -1; - case 2: - /* 0D parity not well-defined for at_2 */ - *pparity1 = parity_sign * abs_parity1; - *pparity2 = parity_sign * (ATOM_PARITY_WELL_DEF( parity2 )? abs_parity2 : - ATOM_PARITY_WELL_DEF( abs_parity2 )? parity2 : - inchi_min(abs_parity2, parity2)); - return -1; - case 3: - abs_parity1 = (ATOM_PARITY_WELL_DEF( parity1 )? abs_parity1 : - ATOM_PARITY_WELL_DEF( abs_parity1 )? parity1 : - inchi_min(abs_parity1, parity1)); - abs_parity2 = (ATOM_PARITY_WELL_DEF( parity2 )? abs_parity2 : - ATOM_PARITY_WELL_DEF( abs_parity2 )? parity2 : - inchi_min(abs_parity2, parity2)); - *pparity1 = *pparity2 = parity_sign * inchi_min(abs_parity1, abs_parity2); - /*return (parity1 == parity2)? 0 : -1;*/ - return -1; - } - break; - } - /* we are here if both end-atoms of the bond have well-defined 0D parities */ - /* - nNumExplictAttachments1 = GetSbNeighOrigAtNumb( at, at_1, at_removed_H, num_removed_H, nSbNeighOrigAtNumb1 ); - nNumExplictAttachments2 = GetSbNeighOrigAtNumb( at, at_2, at_removed_H, num_removed_H, nSbNeighOrigAtNumb2 ); - parity1 = GetHalfStereobond0DParity( at, at_1, nSbNeighOrigAtNumb1, nNumExplictAttachments1, *pparity1, 0 ); - parity2 = GetHalfStereobond0DParity( at, at_2, nSbNeighOrigAtNumb2, nNumExplictAttachments2, *pparity2, 0 ); - */ - *pparity1 = parity_sign * abs_parity1; - *pparity2 = parity_sign * abs_parity2; - - if ( chain_length % 2 ) { - /* allene; chain_length = (number of double bonds) - 1 */ - /* - int zer1 = ( !z_dir1[0] && !z_dir1[1] && !z_dir1[2] ); - int zer2 = ( !z_dir2[0] && !z_dir2[1] && !z_dir2[2] ); - */ - int bWrong_z_dir1 = (0 != (at[at_1].bUsed0DParity & FlagSB_0D)); - int bWrong_z_dir2 = (0 != (at[at_2].bUsed0DParity & FlagSB_0D)); - - if ( bWrong_z_dir1 && bWrong_z_dir2 ) { - goto set_default; - } else - if ( bWrong_z_dir1 || bWrong_z_dir2 ) { - double r12[3], zi1[3], zi2[3], abs_r12, abs_zi2; - int at_i1, at_i2, j; - S_CHAR z_dir[3]; - r12[0] = at[at_2].x - at[at_1].x; - r12[1] = at[at_2].y - at[at_1].y; - r12[2] = at[at_2].z - at[at_1].z; - abs_r12 = len3( r12 ); - if ( abs_r12 < MIN_BOND_LEN ) { - goto set_default; - } - /* make r12[] point to the atom with 'good' z_dir[] */ - if ( bWrong_z_dir1 ) { - at_i1 = at_2; /* has good z_dir2[] */ - at_i2 = at_1; /* has bad z_dir1[] */ - zi1[0] = z_dir2[0]; - zi1[1] = z_dir2[1]; - zi1[2] = z_dir2[2]; - mult3( r12, 1.0/abs_r12, r12 ); /* make length = 1 */ - } else { - at_i1 = at_1; /* has good z_dir1[] */ - at_i2 = at_2; /* has bad z_dir2[] */ - zi1[0] = z_dir1[0]; - zi1[1] = z_dir1[1]; - zi1[2] = z_dir1[2]; - mult3( r12, -1.0/abs_r12, r12 ); /* make length = 1 */ - } - cross_prod3( r12, zi1, zi2 ); - abs_zi2 = len3( zi2 ); - mult3( zi2, 100.0/abs_zi2, zi2 ); /* make length = 100 */ - for ( j = 0; j < 3; j ++ ) { - z_dir[j] = (S_CHAR) (zi2[j]>= 0.0? floor(0.5 + zi2[j]) : - -floor(0.5 - zi2[j])); /* abs(z_dir) = 100 */ - } - if ( bWrong_z_dir1 ) { - memcpy( z_dir1, z_dir, sizeof(z_dir) ); - } else { - memcpy( z_dir2, z_dir, sizeof(z_dir) ); - } - } - return 0; - -set_default: - /* z_dir1[] = x-direction; z_dir2[] = z-direction; r12[] = y-direction */ - z_dir1[0] = 100; - z_dir1[1] = z_dir1[2] = 0; - z_dir2[0] = z_dir2[1] = 0; - z_dir2[2] = 100; - } - return 0; -} -/**********************************************************/ -/* without this InChI fails on reconstructed CID=450438 */ -/* (isotopic, Unknown SB adjacent to SB with known parity) */ -/**********************************************************/ -int FixUnkn0DStereoBonds(inp_ATOM *at, int num_at) -{ - int i, m, num=0; - - /* add usual Unknown stereobond descriptors to each Unknown bond */ - for( i = 0; i < num_at; i ++ ) { - for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[i].sb_parity[m]; m ++ ) { - if ( AB_PARITY_UNKN == at[i].sb_parity[m] ) { - at[i].bond_stereo[ (int)at[i].sb_ord[m] ] = STEREO_DBLE_EITHER; - num ++; - } - } - } -#ifdef NEVER - if ( num ) { - int j; - /* how to remove Unknown stereo bond parities */ - for( i = 0; i < num_at; i ++ ) { - for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[i].sb_parity[m]; m ++ ) { - if ( AB_PARITY_UNKN == at[i].sb_parity[m] ) { - for ( j = m+1; j < MAX_NUM_STEREO_BONDS; j ++ ) { - at[i].sb_parity[j-1] = at[i].sb_parity[j]; - at[i].sb_ord[j-1] = at[i].sb_ord[j]; - at[i].sn_ord[j-1] = at[i].sn_ord[j]; - at[i].sn_orig_at_num[j-1] = at[i].sn_orig_at_num[j]; - } - at[i].sb_parity[j-1] = 0; - at[i].sb_ord[j-1] = 0; - at[i].sn_ord[j-1] = 0; - at[i].sn_orig_at_num[j-1] = 0; - } - } - } - } -#endif - return num; -} -/*====================================================================================================== - -half_stereo_bond_parity() General Description: - - A) find projections of 3 bonds on a reasonable plane defined - by a vector z_dir perpendicular to the plane - B) calculate parity - -half_stereo_bond_parity() Detailed Description: - - 1) Find at_coord[] = vectors from the central atoms to its neighbors - 2) If only 2 neighbors are present, then create a reasonable 3rd neighbor - (an implicit H or a fictitious atom in case of =NX) coordinates - 3) Normalize at_coord[] to unit length - 4) Find unit vector pnt[2] perpendicular to the plane containing - at_coord[] arrow ends. - Even though it is not necessary, make z-coordinate of pnt[2] positive. - ** pnt[2] has the new z-axis direction ** - 5) Let pnt[0] = perpendicular to pnt[2] component of at_coord[0]; - Normalize pnt[0] to unit length. - ** pnt[0] has the new x-axis direction ** - 6) Let pnt[1] = pnt[2] x pnt[0] (cross-product); - ** pnt[1] has the new y-axis direction ** - 7) Find at_coord[] in the new xyz-basis and normalize their xy-projections - to a unit length - 8) In the new xy-plane find (counterclockwise) angles: - tmp1 = (from at_coord[0] to at_coord[1]) - tmp2 = (from at_coord[0] to at_coord[2]) - 9) Calculate the parity: if tmp1 < tmp2 then 1 (odd) else 2 (even) - (even: looking from the arrow end of the new z-axis, 0, 1, and 2 neighbors - are in clockwise order) - 10) Calculate z_dir = 100*pnt[2]. - - Note1. If z_dir vectors of atoms located at the opposite ends of a double bond have approximately - opposite directions (that is, their dot-product is negative) then the parity of the - stereogenic bond calculated from half-bond-parities should be inverted - - Note2. In case of a tetrahedral cumulene a triple product (z_dir1, (1->2), z_dir2) is used instead - of the dot-product. (1->2) is a vector from the atom#1 to the atom #2. This triple product - is invariant with respect to the atom numbering because it does not change upon (1,2) - permutation. - - Stereo ambiguity in case of 2 neighbors: - ---------------------------------------- - Undefined: single-double bond angle > pi - arcsin(0.03) = 178.28164199834454285275613218975 degrees - Ambiguous: single-double bond angle > 175 degrees = pi - 0.087156 Rad - - Return values - (cases: I=only in case of isotopic H atoms the neighbors are different, - N=in case of non-isotopic H atoms the neighbors are different) - - -4 = AB_PARITY_UNDF => atom is adjacent to a stereogenic bond, but the geometry is undefined, I - -3 = AB_PARITY_UNKN => atom is adjacent to a stereogenic bond, but the geometry is not known to the iuser, I - -2 =-AB_PARITY_EVEN => parity of an atom adjacent to a stereogenic bond, I - -1 =-AB_PARITY_ODD => parity of an atom adjacent to a stereogenic bond, I - 0 = AB_PARITY_NONE => the atom is not adjacent to a stereogenic bond - 1 = AB_PARITY_ODD => parity of an atom adjacent to a stereogenic bond, N&I - 2 = AB_PARITY_EVEN => parity of an atom adjacent to a stereogenic bond, N&I - 3 = AB_PARITY_UNKN => atom is adjacent to a stereogenic bond, but the geometry is not known to the iuser, N&I - 4 = AB_PARITY_UNDF => atom is adjacent to a stereogenic bond, but the geometry is undefined, N&I - 5 = AB_PARITY_IISO => atom constitutionally equivalent to this atom may be adjacent to a stereogenic bond, I - - -=====================================================================================================*/ - -int half_stereo_bond_parity( inp_ATOM *at, int cur_at, inp_ATOM *at_removed_H, - int num_removed_H, S_CHAR *z_dir, - int bPointedEdgeStereo, int vABParityUnknown ) -{ - double at_coord[MAX_NUM_STEREO_BOND_NEIGH][3], c, s, tmp[3], tmp1, tmp2, min_tmp, max_tmp, z; - double temp[3], pnt[3][3]; - int j, k, p0, p1, p2, next, bValence3=0, num_z, nType, num_either_single, num_either_double; - int nNumExplictAttachments; - int bond_parity = AB_PARITY_UNDF; - int num_H=0, num_iH, num_eH=0, num_nH=0 /* = num_iso_H[0] */; - int num_iso_H[NUM_H_ISOTOPES+1]; - int index_H[5]; /* cannot have more than 4 elements: 1 H, 1 1H, 1 D, 1 T atom(s) */ - /* const double one_pi = 2.0*atan2(1.0 , 0.0 ); */ - const double one_pi = 3.14159265358979323846; /* M_PI */ - const double two_pi = 2.0*one_pi; - int bIgnoreIsotopicH = (0 != (at[cur_at].cFlags & AT_FLAG_ISO_H_POINT)); - AT_NUMB nSbNeighOrigAtNumb[MAX_NUM_STEREO_BOND_NEIGH]; - - - if ( z_dir && !z_dir[0] && !z_dir[1] && !z_dir[2] ) { - z_dir[2]=100; - } - - num_H = at[cur_at].num_H; - if ( num_H > NUM_H_ISOTOPES ) - return 0; /* at least 2 H atoms are isotopically identical */ - - if ( MAX_NUM_STEREO_BOND_NEIGH < at[cur_at].valence + num_H || - MIN_NUM_STEREO_BOND_NEIGH > at[cur_at].valence + num_H ) - return 0; - - if ( !bCanAtomHaveAStereoBond( at[cur_at].elname, at[cur_at].charge, at[cur_at].radical ) ) - return 0; - if ( !bIgnoreIsotopicH ) { - for ( j = 0, num_nH = num_H; j < NUM_H_ISOTOPES; j ++ ) { - if ( (k = (int)at[cur_at].num_iso_H[j]) > 1 ) { - return AB_PARITY_IISO; /* two or more identical isotopic H atoms */ - } - num_nH -= k; - } - } - /* at this point num_nH = number of non-isotopic H atoms */ - if ( num_nH > 1 ) - return AB_PARITY_IISO; /* two or more identical non-isotopic H atoms */ - if ( num_nH < 0 ) - return CT_ISO_H_ERR; /* program error */ /* */ - - /******************************************************************** - * Note. At this point all (implicit and explicit) isotopic - * terminal H neighbors are either different or not present. - ********************************************************************/ - - /* locate explicit hydrogen atoms */ - /* (at_removed_H are sorted in ascending isotopic H mass order, non-isotopic first) */ - memset( num_iso_H, 0, sizeof(num_iso_H) ); - if ( at_removed_H && num_removed_H > 0 ) { - for ( j = 0; j < num_removed_H; j ++ ) { - if ( at_removed_H[j].neighbor[0] == cur_at ) { - k = bIgnoreIsotopicH? 0 : at_removed_H[j].iso_atw_diff; - if ( 0 <= k && k <= NUM_H_ISOTOPES ) { - if ( ++num_iso_H[k] > 1 ) /* num_iso_H[0] = number of non-isotopic H atoms */ - return CT_ISO_H_ERR; /* program error in counting hydrogens */ /* */ - index_H[num_eH++] = j; - } else { - return CT_ISO_H_ERR; /* program error */ /* */ - } - } - } - num_iH = num_H - num_eH; /* number of implicit non-isotopic and isotopic H atoms */ - if ( num_iH > 1 ) { - /* more than one implicit H: cannot reconstruct the geometry */ - bond_parity = -AB_PARITY_UNDF; - goto exit_function; - } - } else { - num_iH = num_H; - } - /* at this point num_iH = number of implicit non-isotopic and isotopic H atoms */ - if ( at[cur_at].valence + num_eH < MIN_NUM_STEREO_BOND_NEIGH ) { - /* =NH or =CHD when no explicit H is present */ - return num_H == 1? AB_PARITY_UNDF : -AB_PARITY_UNDF; - } - - bValence3 = bAtomHasValence3( at[cur_at].elname, at[cur_at].charge, at[cur_at].radical ); - /* - * Can one explicit hydrogen be added to make asymmetric configuration? - * For now we can add 1 H atom in case of an appropriate geometry if: - * (a) one non-isotopic H (even if explicit isotopic H atoms are present), or - * (b) one isotopic or non-isotopic H if NO explicit isotopic or non-isotopic H atom is present - * This makes sense only in case chem. valence = 4. In case of chem. valence = 3, do not check. - */ - if ( at[cur_at].valence + num_eH == MIN_NUM_STEREO_BOND_NEIGH && !bValence3 && - !(/*(a)*/ 1 == num_nH && !num_iso_H[0] || - /*(b)*/ 1 == num_H && !num_eH) - ) { - goto exit_function; - /* return num_H == 1? AB_PARITY_UNDF : -AB_PARITY_UNDF; */ - } - - /* store neighbors coordinates */ - num_z = num_either_single = num_either_double = 0; - for ( k = nNumExplictAttachments = 0; k < 2; k ++ ) { - switch( k ) { - case 0: - for ( j = 0; j < num_eH; j ++, nNumExplictAttachments ++ ) { - next = index_H[j]; - at_coord[nNumExplictAttachments][0] = at_removed_H[next].x - at[cur_at].x; - at_coord[nNumExplictAttachments][1] = at_removed_H[next].y - at[cur_at].y; - nSbNeighOrigAtNumb[nNumExplictAttachments] = at_removed_H[next].orig_at_number; - /* use the fact that (at_removed_H - at) = (number of atoms except removed explicit H) */ - z = -get_z_coord( at, (at_removed_H-at)+next, 0 /*neighbor #*/, &nType, -(bPointedEdgeStereo & PES_BIT_POINT_EDGE_STEREO) ); - switch ( nType ) { - case ZTYPE_EITHER: - num_either_single ++; /* bond in "Either" direction. */ - break; - case ZTYPE_UP: - case ZTYPE_DOWN: - nType = -nType; /* at_removed_H[] contains bonds TO the center, not from */ - z = len2( at_coord[nNumExplictAttachments] ); - /* - z = sqrt( at_coord[nNumExplictAttachments][0]*at_coord[nNumExplictAttachments][0] - + at_coord[nNumExplictAttachments][1]*at_coord[nNumExplictAttachments][1] ); - */ - if ( nType == ZTYPE_DOWN ) - z = -z; - /* no break; here */ - case ZTYPE_3D: - num_z ++; - } - at_coord[nNumExplictAttachments][2] = z; - } - break; - case 1: - for ( j = 0; j < at[cur_at].valence; j ++, nNumExplictAttachments ++ ) { - next = at[cur_at].neighbor[j]; - at_coord[nNumExplictAttachments][0] = at[next].x - at[cur_at].x; - at_coord[nNumExplictAttachments][1] = at[next].y - at[cur_at].y; - nSbNeighOrigAtNumb[nNumExplictAttachments] = at[next].orig_at_number; - - z = get_z_coord( at, cur_at, j /*neighbor #*/, &nType, (bPointedEdgeStereo & PES_BIT_POINT_EDGE_STEREO) ); - switch ( nType ) { - case ZTYPE_EITHER: - num_either_single ++; /* bond in "Either" direction. */ - break; - case ZTYPE_UP: - case ZTYPE_DOWN: - z = len2( at_coord[nNumExplictAttachments] ); - /* - z = sqrt( at_coord[nNumExplictAttachments][0]*at_coord[nNumExplictAttachments][0] - + at_coord[nNumExplictAttachments][1]*at_coord[nNumExplictAttachments][1] ); - */ - if ( nType == ZTYPE_DOWN ) - z = -z; - /* no break; here */ - case ZTYPE_3D: - num_z ++; - } - at_coord[nNumExplictAttachments][2] = z; - } - break; - } - } - - if ( num_either_single ) { - bond_parity = vABParityUnknown /*AB_PARITY_UNKN*/; /* single bond is 'unknown' */ - goto exit_function; - } - - /* nNumExplictAttachments is a total number of attachments, including removed explicit terminal hydrogens */ - if ( nNumExplictAttachments == 2 ) { - /* create coordinates of the implicit hydrogen (or a fictitious atom in case of ==N-X ), */ - /* coord[2][], attached to the cur_at. */ - for ( j = 0; j < 3; j ++ ) { - at_coord[2][j] = - ( at_coord[0][j] + at_coord[1][j] ); - } - nSbNeighOrigAtNumb[nNumExplictAttachments] = 0; /* implicit H or lone pair */ - } - for ( j = 0; j < 3; j ++ ) { - tmp[j] = len3( at_coord[j] ); - } - min_tmp = inchi_min( tmp[0], inchi_min(tmp[1], tmp[2]) ); - max_tmp = inchi_max( tmp[0], inchi_max(tmp[1], tmp[2]) ); - if ( min_tmp < MIN_BOND_LEN || min_tmp < MIN_SINE*max_tmp ) { - /* all bonds or some of bonds are too short */ - if ( at[cur_at].sb_parity[0] ) { - /* use bond psrity; the reconciliation in ReconcileAllCmlBondParities() - * has made all ways to calculate parity produce same result - */ - bond_parity = GetHalfStereobond0DParity( at, cur_at, nSbNeighOrigAtNumb, - nNumExplictAttachments, bond_parity, FlagSB_0D ); - } - - goto exit_function; - } - /* normalize lengths to 1 */ - for ( j = 0; j < 3; j ++ ) { - mult3( at_coord[j], 1.0/tmp[j], at_coord[j] ); - } - - /* find projections of at_coord vector differences on the plane containing their arrowhead ends */ - for ( j = 0; j < 3; j ++ ) { - /* pnt[0..2] = {0-1, 1-2, 2-0} */ - tmp[j] = len3(diff3( at_coord[j], at_coord[(j+1)%3], pnt[j] )); - if ( tmp[j] < MIN_SINE ) { - goto exit_function; /* angle #i-cur_at-#j is too small */ - } - mult3( pnt[j], 1.0/tmp[j], pnt[j] ); /* 2003-10-06 */ - } - /* find pnt[p2], a vector perpendicular to the plane, and its length tmp[p2] */ - /* replace previous pnt[p2], tmp[p2] with new values; the old values do not have any additional */ - /* information because pnt[p0]+pnt[p1]+pnt[p2]=0 */ - /* 10-6-2003: a cross-product of one pair pnt[j], pnt[(j+1)%3] can be very small. Find the larges one */ - tmp1 = len3( cross_prod3( pnt[0], pnt[1], temp ) ); - for (j = 1, k = 0; j < 3; j ++ ) { - tmp2 = len3( cross_prod3( pnt[j], pnt[(j+1)%3], temp ) ); - if ( tmp2 > tmp1 ) { - tmp1 = tmp2; - k = j; - } - } - /* previously p0=0, p1=1, p2=2 */ - p0 = k; - p1 = (k+1)%3; - p2 = (k+2)%3; - tmp[p2] = len3( cross_prod3( pnt[p0], pnt[p1], pnt[p2] ) ); - if ( tmp[p2] < MIN_SINE*tmp[p0]*tmp[p1] ) { - goto exit_function; /* pnt[p0] is almost colinear to pnt[p1] */ - } - /* new basis: pnt[p0], pnt[p1], pnt[p2]; set z-coord sign and make abs(pnt[p2]) = 1 */ - mult3( pnt[p2], (pnt[p2][2]>0.0? 1.0:-1.0)/tmp[p2], pnt[p2] ); /* unit vector in the new z-axis direction */ - - min_tmp = dot_prod3( at_coord[0], pnt[p2] ); /* non-planarity measure (sine): hight of at_coord[] pyramid */ - mult3( pnt[p2], min_tmp, pnt[p0] ); /* vector height of the pyramid, ideally 0 */ - /* find new pnt[p0] = projection of at_coord[p0] on plane orthogonal to pnt[p2] */ - tmp[p0] = len3(diff3( at_coord[0], pnt[p0], pnt[p0] )); - mult3( pnt[p0], 1.0/tmp[p0], pnt[p0] ); /* new x axis basis vector */ - cross_prod3( pnt[p2], pnt[p0], pnt[p1] ); /* new y axis basis vector */ - /* find at_coord in the new basis of {pnt[p0], pnt[p1], pnt[p2]} */ - for ( j = 0; j < 3; j ++ ) { - copy3( at_coord[j], temp ); - for ( k = 0; k < 3; k ++ ) { - at_coord[j][k] = dot_prod3( temp, pnt[(k+p0)%3] ); - } - /* new xy plane projection length */ - tmp[j] = sqrt(at_coord[j][0]*at_coord[j][0] + at_coord[j][1]*at_coord[j][1]); - /* make new xy plane projection length = 1 */ - mult3( at_coord[j], 1.0/tmp[j], at_coord[j] ); - } - - s = fabs( at_coord[1][0]*at_coord[2][1] - at_coord[1][1]*at_coord[2][0] ); /* 1-2 sine */ - c = at_coord[1][0]*at_coord[2][0] + at_coord[1][1]*at_coord[2][1]; /* 1-2 cosine */ - if ( s < MIN_SINE && c > 0.5 ) { - goto exit_function; /* bonds to neigh. 1 and 2 have almost same direction; relative angles are undefined */ - } - c = at_coord[0][0]; /* cosine of the angle between new Ox axis and a bond to the neighbor 0. Should be 1 */ - s = at_coord[0][1]; /* sine. Should be 0 */ - /* turn vectors so that vector #1 (at_coord[0]) becomes {1, 0} */ - for ( j = 0; j < MAX_NUM_STEREO_BOND_NEIGH; j ++ ) { - tmp1 = c*at_coord[j][0] + s*at_coord[j][1]; - tmp2 = -s*at_coord[j][0] + c*at_coord[j][1]; - at_coord[j][0] = tmp1; - at_coord[j][1] = tmp2; - } - /* counterclockwise angles from the direction to neigh 0 to to directions to neighbors 1 and 2: */ - tmp1 = atan2( at_coord[1][1], at_coord[1][0] ); /* range -pi and +pi */ - tmp2 = atan2( at_coord[2][1], at_coord[2][0] ); - if ( tmp1 < 0.0 ) - tmp1 += two_pi; /* range 0 to 2*pi */ - if ( tmp2 < 0.0 ) - tmp2 += two_pi; - /*----------------------------------- - Example - 1 \ case tmp1 < tmp2 - \ parity is odd - \ (counterclockwise) - A------- 0 - / - / - 2 / - - ------------------------------------*/ - bond_parity = 2 - ( tmp1 < tmp2 ); - for ( j = 0; j < 3; j ++ ) { - z_dir[j] = (S_CHAR) (pnt[p2][j]>= 0.0? floor(0.5 + 100.0 * pnt[p2][j]) : - -floor(0.5 - 100.0 * pnt[p2][j])); /* abs(z_dir) = 100 */ - } - /* check for ambiguity */ - if ( nNumExplictAttachments > 2 ) { - min_tmp = inchi_min( tmp1, tmp2 ); - max_tmp = inchi_max( tmp1, tmp2 ); - if ( min_tmp > one_pi-MIN_SINE || max_tmp < one_pi+MIN_SINE || max_tmp-min_tmp > one_pi - MIN_SINE ) { - at[cur_at].bAmbiguousStereo |= AMBIGUOUS_STEREO; - } else /* 3D ambiguity 8-28-2002 */ - if ( fabs(at_coord[0][2]) > MAX_SINE ) { /* all fabs(at_coord[j][2] (j=0..2) must be equal */ - at[cur_at].bAmbiguousStereo |= AMBIGUOUS_STEREO; - } - } else - if ( nNumExplictAttachments == 2 ) { /* 10-6-2003: added */ - min_tmp = fabs(tmp1 - one_pi); - if ( min_tmp < MIN_SINE ) { - bond_parity = AB_PARITY_UNDF; /* consider as undefined 10-6-2003 */ - } else - if ( min_tmp < MIN_ANGLE_DBOND ) { - at[cur_at].bAmbiguousStereo |= AMBIGUOUS_STEREO; - } - } - - - /* for 3 neighbors moving implicit H to the index=0 from index=2 position */ - /* can be done in 2 transpositions and does not change atom's parity */ -exit_function: - if ( num_H > 1 && bond_parity > 0 && !(bond_parity & AB_PARITY_0D) /*&& PARITY_WELL_DEF(bond_parity)*/ ) { - /* - * stereo only if isotopes are counted. Do not inverse - * Examples: sign for this: - * H D - * / / H - * ==C or ==CH / - * \ ==N (bValence3=1) - * D - * two explicit one explicit H isotope (D), - * isotopic H atoms one implicit H - */ - bond_parity = -bond_parity; /* refers to isotopically substituted structure only */ - } - return bond_parity; -} - -/*************************************************************/ -int save_a_stereo_bond( int z_prod, int result_action, - int at1, int ord1, AT_NUMB *stereo_bond_neighbor1, S_CHAR *stereo_bond_ord1, S_CHAR *stereo_bond_z_prod1, S_CHAR *stereo_bond_parity1, - int at2, int ord2, AT_NUMB *stereo_bond_neighbor2, S_CHAR *stereo_bond_ord2, S_CHAR *stereo_bond_z_prod2, S_CHAR *stereo_bond_parity2 ) -{ - int i1, i2; - for ( i1 = 0; i1 < MAX_NUM_STEREO_BONDS && stereo_bond_neighbor1[i1]; i1 ++ ) - ; - for ( i2 = 0; i2 < MAX_NUM_STEREO_BONDS && stereo_bond_neighbor2[i2]; i2 ++ ) - ; - if ( i1 == MAX_NUM_STEREO_BONDS || i2 == MAX_NUM_STEREO_BONDS ) - return 0; - - stereo_bond_parity1[i1] = - stereo_bond_parity2[i2] = result_action; - - stereo_bond_neighbor1[i1] = (AT_NUMB) (at2+1); - stereo_bond_ord1[i1] = (S_CHAR)ord1; - stereo_bond_neighbor2[i2] = (AT_NUMB) (at1+1); - stereo_bond_ord2[i2] = (S_CHAR)ord2; - stereo_bond_z_prod1[i1] = - stereo_bond_z_prod2[i2] = (S_CHAR)z_prod; - return 1; -} -/***************************************************************/ -int get_allowed_stereo_bond_type( int bond_type ) -{ -#if (ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS == 0 ) - if ( (bond_type & ~BOND_MARK_ALL) == BOND_TAUTOM ) - return 0; /* no tautomer bonds allowed */ - else -#endif -#if ( EXCL_ALL_AROM_BOND_PARITY == 1 ) /* { */ - /* a stereo bond cannot belong to an aromatic atom */ - if ( (bond_type &= ~BOND_MARK_ALL) == BOND_ALTERN ) - { - return 0; - } -#else /* } { */ -#if ( ADD_6MEMB_AROM_BOND_PARITY == 1 ) - /* accept any aromatic bond as a stereo bond */ - if ( (bond_type &= ~BOND_MARK_ALL) == BOND_ALTERN ) -#else - /* accept only aromatic bonds in non-6-member rings */ - if ( (bond_type &= ~BOND_MARK_ALL) == BOND_ALTERN ) ) -#endif - { - return BOND_ALTERN; - } -#endif /* } */ - else - /* at this point BOND_MARK_ALL bits have been removed from bond_type */ - if ( bond_type == BOND_DOUBLE || bond_type == BOND_SINGLE ) { - return bond_type; - } -#if (ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS == 1 ) - else - if ( bond_type == BOND_TAUTOM ) { - return BOND_TAUTOM; - } -#endif - - return 0; /* wrong bond type */ -} - -/*************************************************************/ -int can_be_a_stereo_bond_with_isotopic_H( inp_ATOM *at, int cur_at, INCHI_MODE nMode ) -{ - int i, j, next_at, num_stereo_bonds, bFound; - int bond_type, num_2s, num_alt; - int num_2s_next, num_alt_next, num_wrong_bonds_1, num_wrong_bonds_2; -#if ( N_V_STEREOBONDS == 1 ) - int n2sh, num_2s_hetero[2], num_2s_hetero_next[2], next_next_at, type_N, type_N_next; -#endif - if ( MAX_NUM_STEREO_BOND_NEIGH < at[cur_at].valence+at[cur_at].num_H || - MIN_NUM_STEREO_BOND_NEIGH > at[cur_at].valence+at[cur_at].num_H ) - return 0; - if ( !bCanAtomHaveAStereoBond( at[cur_at].elname, at[cur_at].charge, at[cur_at].radical ) ) - return 0; - /* count bonds and find the second atom on the stereo bond */ - num_2s = num_alt = num_wrong_bonds_1 = 0; -#if ( N_V_STEREOBONDS == 1 ) - num_2s_hetero[0] = num_2s_hetero[1] = type_N = 0; - if ( 0 == at[cur_at].num_H && 0 == at[cur_at].charge && 0 == at[cur_at].radical && - 3 == get_endpoint_valence( at[cur_at].el_number ) ) { - if ( 2 == at[cur_at].valence && 3 == at[cur_at].chem_bonds_valence ) { - type_N = 1; - } else - if ( 3 == at[cur_at].valence && 5 == at[cur_at].chem_bonds_valence ) { - type_N = 2; /* unfortunately includes >N# */ - } - } -#endif - for ( i = 0, num_stereo_bonds = 0; i < at[cur_at].valence; i ++ ) { - bFound = 0; - next_at = at[cur_at].neighbor[i]; - bond_type = get_allowed_stereo_bond_type( (int)at[cur_at].bond_type[i] ); - if ( bond_type == BOND_ALTERN ) { - num_alt ++; - if ( cur_at > next_at && !(nMode & CMODE_NO_ALT_SBONDS) ) - bFound = 1; - } else - if ( bond_type == BOND_DOUBLE ) { - num_2s ++; -#if ( N_V_STEREOBONDS == 1 ) - if ( 0 <= (n2sh = bIsSuitableHeteroInpAtom( at + next_at )) ) { - num_2s_hetero[n2sh] ++; /* n2sh=0 -> =N- or =NH; n2sh=1 -> =O */ - } -#endif - if ( cur_at > next_at ) - bFound = 1; - } else - if ( bond_type != BOND_SINGLE && bond_type != BOND_TAUTOM ) { - num_wrong_bonds_1 ++; -#if ( ONE_BAD_SB_NEIGHBOR == 1 ) - if ( num_wrong_bonds_1 > 1 || num_wrong_bonds_1 && 2 >= at[cur_at].valence ) { - return 0; /* wrong bond type */ - } else { - continue; - } -#else - return 0; /* wrong bond type */ -#endif - } - - if ( bFound ) { - /* check "next_at" atom on the opposite side of the bond */ - if ( MAX_NUM_STEREO_BOND_NEIGH < at[next_at].valence+at[next_at].num_H || - MIN_NUM_STEREO_BOND_NEIGH > at[next_at].valence+at[next_at].num_H ) - continue; - if ( !bCanAtomHaveAStereoBond( at[next_at].elname, at[next_at].charge, at[next_at].radical ) ) - continue; - /* next atom neighbors */ - num_2s_next = num_alt_next = num_wrong_bonds_2 = 0; -#if ( N_V_STEREOBONDS == 1 ) - num_2s_hetero_next[0] = num_2s_hetero_next[1] = type_N_next = 0; - if ( 0 == at[next_at].num_H && 0 == at[next_at].charge && 0 == at[next_at].radical && - 3 == get_endpoint_valence( at[next_at].el_number ) ) { - if ( 2 == at[next_at].valence && 3 == at[next_at].chem_bonds_valence ) { - type_N_next = 1; /* -N= */ - } else - if ( 3 == at[next_at].valence && 5 == at[next_at].chem_bonds_valence ) { - type_N_next = 2; /* unfortunately includes >N# */ - } - } -#endif - for ( j = 0; j < at[next_at].valence; j ++ ) { - bond_type = get_allowed_stereo_bond_type( (int)at[next_at].bond_type[j] ); - if ( bond_type == BOND_ALTERN ) - num_alt_next ++; - else - if ( bond_type == BOND_DOUBLE ) { - num_2s_next ++; -#if ( N_V_STEREOBONDS == 1 ) - next_next_at = at[next_at].neighbor[j]; - if ( 0 <= (n2sh = bIsSuitableHeteroInpAtom( at + next_next_at )) ) { - num_2s_hetero_next[n2sh] ++; /* n2sh=0 -> =N- or =NH; n2sh=1 -> =O */ - } -#endif - } else - if ( bond_type != BOND_SINGLE && bond_type != BOND_TAUTOM ) { - num_wrong_bonds_2 ++; -#if ( ONE_BAD_SB_NEIGHBOR == 1 ) - if ( num_wrong_bonds_1 > 1 || num_wrong_bonds_1 && 2 >= at[cur_at].valence ) { - break; /* wrong bond type */ - } else { - continue; - } -#else - break; /* wrong bond type */ -#endif - } - } - /* figure out whether the at[cur_at]--at[next_at] bond may not be stereogenic */ - -#if ( N_V_STEREOBONDS == 1 ) - if ( 3 == (type_N | type_N_next) && - ( 2 == type_N && !bIsOxide( at, cur_at ) || - 2 == type_N_next && !bIsOxide( at, next_at ) ) ) { - bFound = 0; - } else -#endif - if ( j < at[next_at].valence || /* at[next_at] has a wrong bond type*/ - (num_alt_next>0) + (num_2s_next>0) != 1 /* only one type of stereogenic bond permitted */ - ) { - bFound = 0; - } else - if ( 2 < num_2s_next ) { - bFound = 0; - } else - if ( 2 == num_2s_next ) { - if ( 2 == at[next_at].valence ) { - ; /* only one double bond permitted except cumulenes */ -#if ( N_V_STEREOBONDS == 1 ) - } else - if ( 1 == (num_2s_hetero_next[0] | num_2s_hetero_next[1]) && - 3 == at[next_at].valence + at[next_at].num_H && - 5 == at[next_at].chem_bonds_valence + at[next_at].num_H && - 3 == get_endpoint_valence( at[next_at].el_number ) && - (!type_N || bIsOxide( at, next_at )) ) { - ; /* - * found: - * - * \ / \ / \ / - * \ / \ / \ / - * N==C or N==C or N==N - * // \ // \ // \ - * O ^ \ N ^ \ O ^ \ - * | | | - * | | | - * at[next_at] at[next_at] at[next_at] - */ -#endif - } else { - bFound = 0; - } - } - - } - if ( bFound ) { - num_stereo_bonds++; - } - } - - if ( (num_alt>0) + (num_2s>0) != 1 || !num_stereo_bonds ) - return 0; - if ( num_2s > 1 ) { -#if ( N_V_STEREOBONDS == 1 ) - if ( 2 == num_2s && - 1 == (num_2s_hetero[0] | num_2s_hetero[1]) && - 3 == at[cur_at].valence + at[cur_at].num_H && - 5 == at[cur_at].chem_bonds_valence + at[cur_at].num_H && - 3 == get_endpoint_valence( at[cur_at].el_number ) ) { - ; - } else { - return 0; - } -#else - return 0; -#endif - } - - return num_stereo_bonds; -} -/*************************************************************/ -int half_stereo_bond_action( int nParity, int bUnknown, int bIsotopic, int vABParityUnknown ) -{ -#define AB_NEGATIVE 0x10 -#define AB_UNKNOWN 0x20 - int nAction; - - if ( nParity == AB_PARITY_NONE ) - return AB_PARITY_NONE; - - /* Unknown (type 1) in the parity value may come from the 'Either' single bond only */ - /* Treat it as a known single bond geometry and unknown (Either) double bond */ - if ( nParity == vABParityUnknown /*AB_PARITY_UNKN*/ ) - nParity = AB_PARITY_ODD | AB_UNKNOWN; - if ( nParity == -vABParityUnknown /*AB_PARITY_UNKN*/ ) - nParity = AB_PARITY_ODD | AB_UNKNOWN | AB_NEGATIVE; - - /* make positive, replace AB_PARITY_EVEN with AB_PARITY_ODD */ - if ( nParity < 0 ) - nParity = ((nParity == -AB_PARITY_EVEN)? AB_PARITY_ODD : (-nParity)) | AB_NEGATIVE; - else - if (nParity == AB_PARITY_EVEN) - nParity = AB_PARITY_ODD; - - /* Unknown (type 2): was detected in the double bond attribute */ - /* (this 'unknown' came from 'Either' double bond) */ - /* Treat both unknowns in the same way */ - if ( bUnknown ) - nParity |= AB_UNKNOWN; - - if ( bIsotopic ) { - switch ( nParity ) { - case AB_PARITY_ODD: - case AB_PARITY_ODD | AB_NEGATIVE: - nAction = AB_PARITY_CALC; - break; - case AB_PARITY_ODD | AB_UNKNOWN: - case AB_PARITY_UNDF | AB_UNKNOWN: - case AB_PARITY_ODD | AB_UNKNOWN | AB_NEGATIVE: - case AB_PARITY_UNDF | AB_UNKNOWN | AB_NEGATIVE: - nAction = vABParityUnknown /*AB_PARITY_UNKN*/; - break; - case AB_PARITY_IISO: - case AB_PARITY_IISO | AB_UNKNOWN: - nAction = AB_PARITY_NONE; - break; - case AB_PARITY_UNDF: - case AB_PARITY_UNDF | AB_NEGATIVE: - nAction = AB_PARITY_UNDF; - break; - default: - nAction = -1; /* program error */ - } - } else { - /* Non-isotopic */ - switch ( nParity ) { - case AB_PARITY_ODD: - nAction = AB_PARITY_CALC; - break; - case AB_PARITY_ODD | AB_UNKNOWN: - case AB_PARITY_UNDF | AB_UNKNOWN: - nAction = vABParityUnknown /*AB_PARITY_UNKN*/; - break; - /* case AB_PARITY_ODD | AB_UNKNOWN | AB_NEGATIVE: */ - case AB_PARITY_UNDF: - nAction = AB_PARITY_UNDF; - break; - case AB_PARITY_ODD | AB_UNKNOWN | AB_NEGATIVE: - case AB_PARITY_ODD | AB_NEGATIVE: - case AB_PARITY_IISO: - case AB_PARITY_IISO | AB_UNKNOWN: - case AB_PARITY_UNDF | AB_NEGATIVE: - case AB_PARITY_UNDF | AB_UNKNOWN | AB_NEGATIVE: - nAction = AB_PARITY_NONE; - break; - default: - nAction = -1; /* program error */ - } - } - return nAction; -#undef AB_NEGATIVE -#undef AB_UNKNOWN -} -/*************************************************************/ -int set_stereo_bonds_parity( sp_ATOM *out_at, inp_ATOM *at, int at_1, inp_ATOM *at_removed_H, int num_removed_H, - INCHI_MODE nMode, QUEUE *q, AT_RANK *nAtomLevel, S_CHAR *cSource, - AT_RANK min_sb_ring_size, int bPointedEdgeStereo, int vABParityUnknown ) -{ - int j, k, next_at_1, i_next_at_1, i_next_at_2, at_2, next_at_2, num_stereo_bonds, bFound, bAllene; - int bond_type, num_2s_1, num_alt_1; - int num_2s_2, num_alt_2; -#if ( ONE_BAD_SB_NEIGHBOR == 1 ) - int num_wrong_bonds_1, num_wrong_bonds_2; -#endif -#if ( N_V_STEREOBONDS == 1 ) - int n2sh, num_2s_hetero[2], num_2s_hetero_next[2], next_next_at, type_N, type_N_next; -#endif - int num_stored_stereo_bonds, num_stored_isotopic_stereo_bonds; - int chain_length, num_chains, cur_chain_length; - int all_at_2[MAX_NUM_STEREO_BONDS]; - int all_pos_1[MAX_NUM_STEREO_BONDS], all_pos_2[MAX_NUM_STEREO_BONDS]; - S_CHAR all_unkn[MAX_NUM_STEREO_BONDS]; - int /*at_1_parity, at_2_parity,*/ nUnknown, stop=0; - - /* at_1_parity = AB_PARITY_NONE; */ /* do not know */ - /* check valence */ - if ( MAX_NUM_STEREO_BOND_NEIGH < at[at_1].valence+at[at_1].num_H || - MIN_NUM_STEREO_BOND_NEIGH > at[at_1].valence+at[at_1].num_H ) - return 0; - if ( !bCanAtomHaveAStereoBond( at[at_1].elname, at[at_1].charge, at[at_1].radical ) ) - return 0; - if ( at[at_1].c_point ) - return 0; /* rejects atoms that can lose or gain a (positive) charge. 01-24-2003 */ - - /* middle cumulene atoms, for example, =C=, should be ignored here */ - /* only atoms at the ends of cumulene chains are considered. */ - if ( !at[at_1].num_H && 2 == at[at_1].valence && - BOND_DOUBLE == get_allowed_stereo_bond_type( (int)at[at_1].bond_type[0] ) && - BOND_DOUBLE == get_allowed_stereo_bond_type( (int)at[at_1].bond_type[1] ) ) { - return 0; - } - - /* count bonds and find the second atom on the stereo bond */ - num_2s_1 = num_alt_1 = 0; - chain_length = 0; - num_chains = 0; -#if ( ONE_BAD_SB_NEIGHBOR == 1 ) - num_wrong_bonds_1 = 0; -#endif -#if ( N_V_STEREOBONDS == 1 ) - num_2s_hetero[0] = num_2s_hetero[1] = type_N = 0; - if ( 0 == at[at_1].num_H && 0 == at[at_1].charge && 0 == at[at_1].radical && - 3 == get_endpoint_valence( at[at_1].el_number ) ) { - if ( 2 == at[at_1].valence && 3 == at[at_1].chem_bonds_valence ) { - type_N = 1; - } else - if ( 3 == at[at_1].valence && 5 == at[at_1].chem_bonds_valence ) { - type_N = 2; /* unfortunately includes >N# */ - } - } -#endif - for ( i_next_at_1 = 0, num_stereo_bonds = 0; i_next_at_1 < at[at_1].valence; i_next_at_1 ++ ) { - nUnknown = (at[at_1].bond_stereo[i_next_at_1] == STEREO_DBLE_EITHER); - bond_type = get_allowed_stereo_bond_type( (int)at[at_1].bond_type[i_next_at_1] ); - at_2 = -1; /* not found */ - if ( bond_type == BOND_ALTERN || - bond_type == BOND_DOUBLE ) { - next_at_1 = at_2 = at[at_1].neighbor[i_next_at_1]; - next_at_2 = at_1; - } - switch ( bond_type ) { - case BOND_ALTERN: - num_alt_1 ++; -#if ( FIND_RING_SYSTEMS == 1 ) - if ( at[at_1].nRingSystem != at[at_2].nRingSystem ) - continue; /* reject alt. bond connecting different ring systems */ -#endif - if ( (nMode & CMODE_NO_ALT_SBONDS) || - !bCanAtomHaveAStereoBond( at[at_2].elname, at[at_2].charge, at[at_2].radical ) ) { - continue; /* reject non-stereogenic bond to neighbor ord. #i_next_at_1 */ - } - break; - case BOND_DOUBLE: - /* check for cumulene/allene */ - num_2s_1++; - cur_chain_length = 0; - if ( bCanAtomBeTerminalAllene( at[at_1].elname, at[at_1].charge, at[at_1].radical ) ) { - /* - * Example of cumulene - * chain length = 2: >X=C=C=Y< - * | | | | - * 1st cumulene atom= at_1 | | at_2 =last cumlene chain atom - * next to at_1= next_at_1 next_at_2 =previous to at_2 - * - * chain length odd: stereocenter on the middle atom ( 1=> allene ) - * chain length even: "long stereogenic bond" - */ - while ((bAllene = - !at[at_2].num_H && at[at_2].valence == 2 && - BOND_DOUBLE == get_allowed_stereo_bond_type( (int)at[at_2].bond_type[0] ) && - BOND_DOUBLE == get_allowed_stereo_bond_type( (int)at[at_2].bond_type[1] )) && - bCanAtomBeMiddleAllene( at[at_2].elname, at[at_2].charge, at[at_2].radical ) ) { - k = ((int)at[at_2].neighbor[0]==next_at_2); /* opposite neighbor position */ - next_at_2 = at_2; - nUnknown += (at[at_2].bond_stereo[k] == STEREO_DBLE_EITHER); - at_2 = (int)at[at_2].neighbor[k]; - cur_chain_length ++; /* count =C= atoms */ - } - if ( cur_chain_length ) { - num_chains ++; - if ( bAllene /* at the end of the chain atom Y is =Y=, not =Y< or =Y- */ || - !bCanAtomBeTerminalAllene( at[at_2].elname, at[at_2].charge, at[at_2].radical ) ) { - cur_chain_length = 0; - continue; /* ignore: does not fit cumulene description; go to check next at_1 neighbor */ - } - chain_length = cur_chain_length; /* accept a stereogenic cumulele */ - } - } -#if ( N_V_STEREOBONDS == 1 ) - if ( !cur_chain_length && - 0 <= (n2sh = bIsSuitableHeteroInpAtom( at + at_2 )) ) { - num_2s_hetero[n2sh] ++; /* n2sh=0 -> =N- or =NH; n2sh=1 -> =O */ - } -#endif - if ( !cur_chain_length && - !bCanAtomHaveAStereoBond( at[at_2].elname, at[at_2].charge, at[at_2].radical ) ) { - continue; /* reject non-stereogenic bond to neighbor #i_next_at_1 */ - } - - break; - - case BOND_SINGLE: - case BOND_TAUTOM: - continue; /* reject non-stereogenic bond to neighbor #i_next_at_1 */ - default: -#if ( ONE_BAD_SB_NEIGHBOR == 1 ) - num_wrong_bonds_1 ++; - continue; -#else - return 0; /* wrong bond type; */ -#endif - } - - /* check atom at the opposite end of possibly stereogenic bond */ - - bFound = (at_2 >= 0 && at_1 > at_2 ); /* i_next_at_1 = at_1 stereogenic bond neighbor attachment number */ - - if ( bFound ) { - /* check "at_2" atom on the opposite side of the bond or cumulene chain */ - if ( MAX_NUM_STEREO_BOND_NEIGH < at[at_2].valence+at[at_2].num_H || - MIN_NUM_STEREO_BOND_NEIGH > at[at_2].valence+at[at_2].num_H ) - continue; - - /* check at_2 neighbors and bonds */ - num_2s_2 = num_alt_2 = 0; -#if ( N_V_STEREOBONDS == 1 ) - num_2s_hetero_next[0] = num_2s_hetero_next[1] = type_N_next = 0; - if ( 0 == at[at_2].num_H && 0 == at[at_2].charge && 0 == at[at_2].radical && - 3 == get_endpoint_valence( at[at_2].el_number ) ) { - if ( 2 == at[at_2].valence && 3 == at[at_2].chem_bonds_valence ) { - type_N_next = 1; /* -N= */ - } else - if ( 3 == at[at_2].valence && 5 == at[at_2].chem_bonds_valence ) { - type_N_next = 2; /* unfortunately includes >N# */ - } - } -#endif - i_next_at_2 = -1; /* unassigned mark */ -#if ( ONE_BAD_SB_NEIGHBOR == 1 ) - num_wrong_bonds_2 = 0; -#endif - for ( j = 0; j < at[at_2].valence; j ++ ) { - bond_type = get_allowed_stereo_bond_type( (int)at[at_2].bond_type[j] ); - if ( !bond_type ) { -#if ( ONE_BAD_SB_NEIGHBOR == 1 ) - num_wrong_bonds_2 ++; - continue; /* this bond type is not allowed to be adjacent to a stereo bond */ -#else - break; -#endif - } - if ( bond_type == BOND_DOUBLE ) { - num_2s_2 ++; -#if ( N_V_STEREOBONDS == 1 ) - next_next_at = at[at_2].neighbor[j]; - if ( 0 <= (n2sh = bIsSuitableHeteroInpAtom( at + next_next_at )) ) { - num_2s_hetero_next[n2sh] ++; /* n2sh=0 -> =N- or =NH; n2sh=1 -> =O */ - } -#endif - } else { - num_alt_2 += ( bond_type == BOND_ALTERN ); - } - if ( (int)at[at_2].neighbor[j] == next_at_2 ) - i_next_at_2 = j; /* assigned */ - } - if ( -#if ( ONE_BAD_SB_NEIGHBOR == 1 ) - num_wrong_bonds_2 > 1 || num_wrong_bonds_2 && 2 >= at[at_2].valence || -#else - j < at[at_2].valence /* "next" has a wrong bond type*/ || -#endif - (num_alt_2>0) + (num_2s_2>0) != 1 || /* all double XOR all alt bonds only */ - /* num_2s_2 > 1 ||*/ /* only one double bond permitted */ - i_next_at_2 < 0 /* atom next to the opposite atom not found */ ) { - bFound = 0; - } else - if ( at[at_2].c_point ) { - bFound = 0; /* rejects atoms that can lose or gain a (positive) charge. 01-24-2003 */ - } else - if ( num_2s_2 > 2 ) { - bFound = 0; - } else -#if ( N_V_STEREOBONDS == 1 ) - if ( 3 == (type_N | type_N_next) && - ( 2 == type_N && !bIsOxide( at, at_1 ) || - 2 == type_N_next && !bIsOxide( at, at_2 ) ) ) { - bFound = 0; - } else -#endif - if ( 2 == num_2s_2 ) { -#if ( N_V_STEREOBONDS == 1 ) - if ( !chain_length && - 1 == (num_2s_hetero_next[0] | num_2s_hetero_next[1]) && - 3 == at[at_2].valence + at[at_2].num_H && - 5 == at[at_2].chem_bonds_valence + at[at_2].num_H && - 3 == get_endpoint_valence( at[at_2].el_number ) && - (!type_N || bIsOxide( at, at_2 )) ) { - /* - * found: - * - * \ / \ / \ / - * \ / \ / \ / - * N==C or N==C or N==N - * // \ // \ // \ - * O ^ \ N ^ \ O ^ \ - * | | | - * | | | - * at[at_2] at[at_2] at[at_2] - */ - ; - } else { - bFound = 0; - } -#else - bFound = 0; -#endif - } - - - if ( chain_length && num_alt_2 ) - return 0; /* allow no alt bonds in cumulenes */ - } - if ( bFound ) { - all_pos_1[num_stereo_bonds] = i_next_at_1; /* neighbor to at_1 position */ - all_pos_2[num_stereo_bonds] = i_next_at_2; /* neighbor to at_2 position */ - all_at_2[num_stereo_bonds] = at_2; /* at_2 */ - all_unkn[num_stereo_bonds] = nUnknown; /* stereogenic bond has Unknown configuration */ - /* - if ( (at[at_1].bUsed0DParity & 2) || (at[at_2].bUsed0DParity & 2) ) { - for ( k = 0; k < MAX_NUM_STEREO_BONDS && at[at_1].sb_parity[k]; k ++ ) { - if ( at[at_1].sb_neigh[k] == i_next_at_1 ) { - if ( at[at_1].sb_parity[k] == AB_PARITY_UNKN && !nUnknown ) { - all_unkn[num_stereo_bonds] = 1; - } - break; - } - } - } - */ - num_stereo_bonds ++; - } - } - if ( num_chains > 1 ) { - return 0; /* cannot be more than 1 cumulene chain. */ - } -#if ( ONE_BAD_SB_NEIGHBOR == 1 ) - if ( num_wrong_bonds_1 > 1 || num_wrong_bonds_1 && 2 >= at[at_1].valence ) { - return 0; /* wrong bond type */ - } -#endif - /* accept only short chains for now */ - /* chain_length=1: >C=C=C< tetrahedral center, allene */ - /* chain_length=2: >C=C=C=C< stereogenic bond, cumulene */ - if ( chain_length && (num_stereo_bonds != 1 || num_alt_1 || chain_length > MAX_CUMULENE_LEN) ) { - return 0; - } - - /* we need 1 double bond/chain XOR up to 3 arom. bonds */ - /* to have a stereogenic bond */ - if ( (num_alt_1>0) + (num_2s_1>0) != 1 || !num_stereo_bonds /*|| num_2s_1 > 1*/ ) - return 0; - - if ( num_2s_1 > 1 ) { -#if ( N_V_STEREOBONDS == 1 ) - if ( 2 == num_2s_1 && - 2 == type_N && - 1 == (num_2s_hetero[0] | num_2s_hetero[1]) && - 3 == at[at_1].valence + at[at_1].num_H && - 5 == at[at_1].chem_bonds_valence + at[at_1].num_H && - 3 == get_endpoint_valence( at[at_1].el_number ) ) { - ; - } else { - return 0; - } -#else - return 0; -#endif - } - - /* ================== calculate parities ====================== */ - - - /* find possibly stereo bonds and save them */ - num_stored_isotopic_stereo_bonds = 0; - num_stored_stereo_bonds = 0; - for ( k = 0; k < num_stereo_bonds; k ++ ) { - - int cur_parity, next_parity, abs_cur_parity, abs_next_parity, dot_prod_z; - S_CHAR z_dir1[3], z_dir2[3]; /* 3D vectors for half stereo bond parity direction */ - int chain_len_bits = MAKE_BITS_CUMULENE_LEN(chain_length); - int cur_parity_defined, next_parity_defined; - int cur_action, next_action, result_action; - - at_2 = all_at_2[k]; - i_next_at_1 = all_pos_1[k]; - -#if ( MIN_SB_RING_SIZE > 0 ) - if( at[at_1].nRingSystem == at[at_2].nRingSystem ) { - /* check min. ring size only if both double bond/cumulene */ - /* ending atoms belong to the same ring system */ - j = is_bond_in_Nmax_memb_ring( at, at_1, i_next_at_1, q, nAtomLevel, cSource, min_sb_ring_size ); - if ( j > 0 ) { - continue; - } else - if ( j < 0 ) { - return CT_STEREOBOND_ERROR; - } - } -#endif - - i_next_at_2 = all_pos_2[k]; - nUnknown = all_unkn[k]; - memset(z_dir1, 0, sizeof(z_dir1)); - memset(z_dir2, 0, sizeof(z_dir2)); - - /******************************************************************************** - * find atom parities (negative means parity due to H-isotopes only) - * and half stereo bond parity directions z_dir1, z_dir2. - * - * Bond can have unknown or undefined parity or no parity because of: - * 1. Geometry (poorly defined, cannot calculate, for example linear =C-F - * or =CHD with no geometry) -- Undefined parity - * H - * 2. Identical H atoms (no parity in principle, for example =C< ) - * -- No parity H - * - * 3. The user said double bond stereo is unknown - * or at least one of single bonds is in unknown direction - * -- Unknown parity - * - * These 3 cases (see above) are referred below as 1, 2, 3. - * Each of the cases may be present or not (2 possibilities) - * Total number of combination is 2*2*2=8 - * - * Since a case when all 3 are not present is a well-defined parity, - * we do not consider this case here. Then 2*2*2-1=7 cases are left. - * - * If several cases are present, list them below separated by "+". - * For example, 1+2 means (1) undefined geometry and (2) no parity - * is possible because of identical H atoms. - * - * N) Decision table, Non-isotopic, 2*2*2-1=7 cases: - * ================================================= - * none : 2+any: 1+2(e.g.=CH2); 1+2+3; 2; 2+3 AB_PARITY_NONE=0 - * undefined: 1 AB_PARITY_UNDF - * unknown : 1+3; 3 AB_PARITY_UNKN - * - * I) Decision table, Isotopic, 2*2*2-1=7 cases: - * ============================================= - * none : none - * undefined: 1; 1+2; 1+2+3; 2; 2+3 - * unknown : 1+3; 3 - * - * Note: When defining identical atoms H atoms in case 2, - * Isotopic and Non-isotopic cases are different: - * N: do NOT take into account the isotopic composition of H atoms - * I: DO take into account the isotopic composition of H atoms - * (it is assumed that H isotopes are always different) - * - * half_stereo_bond_parity() returns: - * ================================== - * Note: half_stereo_bond_parity() is unaware of case 3. - * - * can't be a half of a stereo bond AB_PARITY_NONE - * 1, isotopic & non-isotopic: AB_PARITY_UNDF - * 1, isotopic only -AB_PARITY_UNDF - * 2, no parity: identical H isotopes AB_PARITY_IISO - * 3, 'Either' single bond(s) AB_PARITY_UNKN ??? - * 3, 'Either' single bond(s), iso H -AB_PARITY_UNKN ??? - * defined parity AB_PARITY_ODD, AB_PARITY_EVEN - * defined parity for isotopic only: -AB_PARITY_ODD, -AB_PARITY_EVEN - * - * Resultant value for the stereo bond parity - * ---+-------------------+-------+--------+----------------+ - * 3? | half_stereo_bond_ | N or I| case 1,| bond parity | - * | parity()= | | 2 or 3 | | - * ---+-------------------+-------+--------+----------------+ - * ( AB_PARITY_ODD/EVEN) => N&I: - => AB_PARITY_CALC (=6, calc.later) - * 3+( AB_PARITY_ODD/EVEN) => N&I: 3 => AB_PARITY_UNKN (=3) - * (-AB_PARITY_ODD/EVEN) => N: 2 => AB_PARITY_NONE (=0) - * (-AB_PARITY_ODD/EVEN) => I: - => AB_PARITY_CALC - * 3+(-AB_PARITY_ODD/EVEN) => N: 2+3 => AB_PARITY_UNDF (=4) - * 3+(-AB_PARITY_ODD/EVEN) => I: 3 => AB_PARITY_UNKN - * ( AB_PARITY_IISO ) => N: 1+2, 2 => AB_PARITY_NONE (=0) - * ( AB_PARITY_IISO ) => I: 1+2, 2 => AB_PARITY_UNDF - * 3+( AB_PARITY_IISO ) => N: 1+2+3,2+3=> AB_PARITY_NONE - * 3+( AB_PARITY_IISO ) => I: 1+2+3,2+3=> AB_PARITY_UNDF - * ( AB_PARITY_UNDF ) => N&I: 1 => AB_PARITY_UNDF - * 3+( AB_PARITY_UNDF ) => N&I: 1+3 => AB_PARITY_UNKN - * (-AB_PARITY_UNDF ) => N: 1+2 => AB_PARITY_NONE - * (-AB_PARITY_UNDF ) => I: 1 => AB_PARITY_UNDF - * 3+(-AB_PARITY_UNDF ) => N: 1+2+3 => AB_PARITY_NONE - * 3+(-AB_PARITY_UNDF ) => I: 1+3 => AB_PARITY_UNKN - * ---+-------------------+-------+--------+----------------+ - - * If bond parity is undefined because abs(dot_prod_z) < MIN_DOT_PROD - * then replace: AB_PARITY_CALC - * with: AB_PARITY_UNDF - * Joining two half_bond_parity() results: - * - * - * atom1 \ atom2 | AB_PARITY_NONE AB_PARITY_UNKN AB_PARITY_UNDF AB_PARITY_CALC - * ----------------+--------------------------------------------------------------- - *0=AB_PARITY_NONE | AB_PARITY_NONE AB_PARITY_NONE AB_PARITY_NONE AB_PARITY_NONE - *3=AB_PARITY_UNKN | AB_PARITY_UNKN AB_PARITY_UNKN AB_PARITY_UNKN - *4=AB_PARITY_UNDF | AB_PARITY_UNDF AB_PARITY_UNDF - *6=AB_PARITY_CALC | AB_PARITY_CALC - * - * that is, take min out of the two - *********************************************************************************/ - - cur_parity = half_stereo_bond_parity( at, at_1, at_removed_H, num_removed_H, - z_dir1, bPointedEdgeStereo, vABParityUnknown ); - next_parity = half_stereo_bond_parity( at, at_2, at_removed_H, num_removed_H, - z_dir2, bPointedEdgeStereo, vABParityUnknown ); - - if ( RETURNED_ERROR(cur_parity) || RETURNED_ERROR(next_parity) ) { - return CT_CALC_STEREO_ERR; - } - if ( (at[at_1].bUsed0DParity & FlagSB_0D) || (at[at_1].bUsed0DParity & FlagSB_0D) ) { - FixSb0DParities( at, /* at_removed_H, num_removed_H,*/ chain_length, - at_1, i_next_at_1, z_dir1, - at_2, i_next_at_2, z_dir2, &cur_parity, &next_parity ); - } - - if ( cur_parity == AB_PARITY_NONE || abs(cur_parity) == AB_PARITY_IISO ) { - continue; - } - if ( next_parity == AB_PARITY_NONE || abs(next_parity) == AB_PARITY_IISO ) { - continue; - } - - cur_action = half_stereo_bond_action( cur_parity, nUnknown, 0, vABParityUnknown ); /* -1 => program error */ - next_action = half_stereo_bond_action( next_parity, nUnknown, 0, vABParityUnknown ); - result_action = inchi_min(cur_action, next_action); - - if ( result_action == -1 ) { - stop = 1; /* program error */ - } - - abs_cur_parity = abs( cur_parity ); - abs_next_parity = abs( next_parity ); - cur_parity_defined = ATOM_PARITY_WELL_DEF(abs_cur_parity); - next_parity_defined = ATOM_PARITY_WELL_DEF(abs_next_parity); - - - if ( cur_parity_defined && next_parity_defined ) { - /* find how the whole bond parity depend on geometry */ - /* if dot_prod_z < 0 then bond_parity := 3-bond_parity */ - /* can be done only for a well-defined geometry */ - /* - dot_prod_z = (chain_len_bits & BIT_CUMULENE_CHI)? - triple_prod_char( at, at_1, i_next_at_1, z_dir1, at_2, i_next_at_2, z_dir2 ) : - dot_prodchar3(z_dir1, z_dir2); - */ - dot_prod_z = (chain_len_bits && BOND_CHAIN_LEN(chain_len_bits)%2)? - triple_prod_char( at, at_1, i_next_at_1, z_dir1, at_2, i_next_at_2, z_dir2 ) : - dot_prodchar3(z_dir1, z_dir2); - - if ( abs(dot_prod_z) < MIN_DOT_PROD ) { - /* The geometry is not well-defined. Eliminate AB_PARITY_CALC */ - result_action = inchi_min( result_action, AB_PARITY_UNDF ); - } - } else { - dot_prod_z = 0; - } - - if ( result_action != AB_PARITY_NONE && result_action != -1 ) { - /* stereo, no isotopes (only positive) */ - if ( cur_parity > 0 && next_parity > 0 ) { - if ( save_a_stereo_bond( dot_prod_z, result_action | chain_len_bits, - at_1, i_next_at_1, out_at[at_1].stereo_bond_neighbor, - out_at[at_1].stereo_bond_ord, out_at[at_1].stereo_bond_z_prod, - out_at[at_1].stereo_bond_parity, - at_2, i_next_at_2, out_at[at_2].stereo_bond_neighbor, - out_at[at_2].stereo_bond_ord, out_at[at_2].stereo_bond_z_prod, - out_at[at_2].stereo_bond_parity) ) { - if ( !out_at[at_1].parity || - cur_parity_defined && !ATOM_PARITY_WELL_DEF(abs(out_at[at_1].parity)) ) { - out_at[at_1].parity = cur_parity; - memcpy( out_at[at_1].z_dir, z_dir1, sizeof(out_at[0].z_dir) ); - } - if ( !out_at[at_2].parity || - next_parity_defined && !ATOM_PARITY_WELL_DEF(abs(out_at[at_2].parity)) ) { - out_at[at_2].parity = next_parity; - memcpy( out_at[at_2].z_dir, z_dir2, sizeof(out_at[0].z_dir) ); - } - out_at[at_1].bAmbiguousStereo |= at[at_1].bAmbiguousStereo; - out_at[at_2].bAmbiguousStereo |= at[at_2].bAmbiguousStereo; - num_stored_stereo_bonds ++; - } - } - } - - /* stereo + isotopic (all non-zero) */ - cur_action = half_stereo_bond_action( cur_parity, nUnknown, 1, vABParityUnknown ); /* -1 => program error */ - next_action = half_stereo_bond_action( next_parity, nUnknown, 1, vABParityUnknown ); - result_action = inchi_min(cur_action, next_action); - cur_parity = abs_cur_parity; - next_parity = abs_next_parity; - if ( result_action != AB_PARITY_NONE && result_action != -1 ) { - /* stero, isotopic */ - if ( cur_parity > 0 && next_parity > 0 ) { - if( save_a_stereo_bond( dot_prod_z, result_action | chain_len_bits, - at_1, i_next_at_1, out_at[at_1].stereo_bond_neighbor2, - out_at[at_1].stereo_bond_ord2, out_at[at_1].stereo_bond_z_prod2, - out_at[at_1].stereo_bond_parity2, - at_2, i_next_at_2, out_at[at_2].stereo_bond_neighbor2, - out_at[at_2].stereo_bond_ord2, out_at[at_2].stereo_bond_z_prod2, - out_at[at_2].stereo_bond_parity2) ) { - if ( !out_at[at_1].parity2 || - cur_parity_defined && !ATOM_PARITY_WELL_DEF(abs(out_at[at_1].parity2)) ) { - out_at[at_1].parity2 = cur_parity /*| chain_len_bits*/; - if ( !out_at[at_1].parity ) { - memcpy( out_at[at_1].z_dir, z_dir1, sizeof(out_at[0].z_dir) ); - } - } - if ( !out_at[at_2].parity2 || /* next line changed from abs(out_at[at_2].parity) 2006-03-05 */ - next_parity_defined && !ATOM_PARITY_WELL_DEF(abs(out_at[at_2].parity2)) ) { - out_at[at_2].parity2 = next_parity /*| chain_len_bits*/; - if ( !out_at[at_2].parity ) { - memcpy( out_at[at_2].z_dir, z_dir2, sizeof(out_at[0].z_dir) ); - } - } - out_at[at_1].bAmbiguousStereo |= at[at_1].bAmbiguousStereo; - out_at[at_2].bAmbiguousStereo |= at[at_2].bAmbiguousStereo; - num_stored_isotopic_stereo_bonds ++; - } - } - } else - if ( result_action == -1 ) { - stop = 1; /* program error? */ - } - - } - if ( stop ) { - return CT_CALC_STEREO_ERR; - } - return /*num_stored_stereo_bonds+*/ num_stored_isotopic_stereo_bonds; -} - - -/*********************************************************************/ -/* if isotopic H, D, T added, can the atom be a stereo center? */ -#if ( NEW_STEREOCENTER_CHECK == 1 ) -/* int bCanInpAtomBeAStereoCenter( inp_ATOM *at, int cur_at ) */ -int can_be_a_stereo_atom_with_isotopic_H( inp_ATOM *at, int cur_at, int bPointedEdgeStereo ) -{ - int nNumNeigh; - if ( (nNumNeigh = bCanInpAtomBeAStereoCenter( at, cur_at, bPointedEdgeStereo )) && - at[cur_at].valence + at[cur_at].num_H == nNumNeigh && - at[cur_at].num_H <= NUM_H_ISOTOPES - ) { - return 1; - } - return 0; -} -#else -int can_be_a_stereo_atom_with_isotopic_H( inp_ATOM *at, int cur_at ) -{ - int j, ret = 0; - if ( bCanAtomBeAStereoCenter( at[cur_at].elname, at[cur_at].charge, at[cur_at].radical ) && - at[cur_at].valence + at[cur_at].num_H == MAX_NUM_STEREO_ATOM_NEIGH && - at[cur_at].num_H < MAX_NUM_STEREO_ATOM_NEIGH - ) { - - for ( j = 0, ret=1; ret && j < at[cur_at].valence; j ++ ) { - if ( (at[cur_at].bond_type[j] & ~BOND_MARK_ALL) != BOND_SINGLE ) { - ret = 0; - } - } - } - return ret; -} -#endif -/***************************************************************/ -int GetStereocenter0DParity( inp_ATOM *at, int cur_at, int j1, AT_NUMB nSbNeighOrigAtNumb[], int nFlag ) -{ - int parity = AB_PARITY_NONE; - if ( at[cur_at].p_parity && (j1 == MAX_NUM_STEREO_ATOM_NEIGH-1 || j1 == MAX_NUM_STEREO_ATOM_NEIGH) ) { - int i, num_trans_inp, num_trans_neigh; - AT_NUMB nInpNeighOrigAtNumb[MAX_NUM_STEREO_ATOM_NEIGH]; - for ( i = 0; i < MAX_NUM_STEREO_ATOM_NEIGH; i ++ ) { - nInpNeighOrigAtNumb[i] = at[cur_at].p_orig_at_num[i]; - if ( nInpNeighOrigAtNumb[i] == at[cur_at].orig_at_number ) { - nInpNeighOrigAtNumb[i] = 0; /* lone pair or explicit H */ - } - } - num_trans_inp = insertions_sort( nInpNeighOrigAtNumb, MAX_NUM_STEREO_ATOM_NEIGH, sizeof(nInpNeighOrigAtNumb[0]), comp_AT_NUMB ); - num_trans_neigh = insertions_sort( nSbNeighOrigAtNumb, j1, sizeof(nSbNeighOrigAtNumb[0]), comp_AT_NUMB ); - if ( j1 == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { - ; /*num_trans_neigh += j1;*/ /* the lone pair or implicit H is implicitly at the top of the list */ - } - if ( !memcmp( nInpNeighOrigAtNumb + MAX_NUM_STEREO_ATOM_NEIGH-j1, nSbNeighOrigAtNumb, j1*sizeof(AT_NUMB) ) ) { - if ( ATOM_PARITY_WELL_DEF(at[cur_at].p_parity) ) { - parity = 2 - (num_trans_inp + num_trans_neigh + at[cur_at].p_parity) % 2; - } else { - parity = at[cur_at].p_parity; - } - at[cur_at].bUsed0DParity |= nFlag; /* 0D parity used for streocenter parity */ - } - } - return parity; -} - -/*************************************************************** - * Get stereo atom parity for the current order of attachments - * The result in at[cur_at].parity is valid for previously removed - * explicit hydrogen atoms, including isotopic ones, that are located in at_removed_H[] - * The return value is a calculated parity. - */ -#define ADD_EXPLICIT_HYDROGEN_NEIGH 1 -#define ADD_EXPLICIT_LONE_PAIR_NEIGH 2 -int set_stereo_atom_parity( sp_ATOM *out_at, inp_ATOM *at, int cur_at, inp_ATOM *at_removed_H, - int num_removed_H, int bPointedEdgeStereo, int vABParityUnknown) -{ - int j, k, next_at, num_z, j1, nType, num_explicit_H, tot_num_iso_H, nMustHaveNumNeigh; - int num_explicit_iso_H[NUM_H_ISOTOPES+1]; /* numbers of removed hydrogen atoms */ - int index_H[MAX_NUM_STEREO_ATOM_NEIGH]; /* cannot have more than 4 elements: 1 H, 1 D, 1 T atom(s) */ - double z, sum_xyz[3], min_sine, triple_product; - double at_coord[MAX_NUM_STEREO_ATOM_NEIGH][3]; - double bond_len_xy[4], rmax=0.0, rmin=0.0; - double at_coord_center[3]; - int parity, bAmbiguous = 0, bAddExplicitNeighbor = 0, b2D = 0, n2DTetrahedralAmbiguity = 0; - int bIgnoreIsotopicH = (0 != (at[cur_at].cFlags & AT_FLAG_ISO_H_POINT)); - AT_NUMB nSbNeighOrigAtNumb[MAX_NUM_STEREO_ATOM_NEIGH]; - - out_at[cur_at].parity = - out_at[cur_at].parity2 = - out_at[cur_at].stereo_atom_parity = - out_at[cur_at].stereo_atom_parity2 = AB_PARITY_NONE; - parity = AB_PARITY_NONE; - - memset(num_explicit_iso_H, 0, sizeof(num_explicit_iso_H)); - num_explicit_H = 0; - -#if ( NEW_STEREOCENTER_CHECK == 1 ) - if ( !(nMustHaveNumNeigh = bCanInpAtomBeAStereoCenter( at, cur_at, bPointedEdgeStereo ) ) || - at[cur_at].num_H > NUM_H_ISOTOPES - ) { - goto exit_function; - } -#else - nMustHaveNumNeigh = MAX_NUM_STEREO_ATOM_NEIGH; - if ( !bCanAtomBeAStereoCenter( at[cur_at].elname, at[cur_at].charge, at[cur_at].radical ) || - at[cur_at].valence + at[cur_at].num_H != nMustHaveNumNeigh || - at[cur_at].num_H > NUM_H_ISOTOPES - ) { - goto exit_function; - } - for ( j = 0; j < at[cur_at].valence; j ++ ) { - if ( (at[cur_at].bond_type[j] & ~BOND_MARK_ALL) != BOND_SINGLE ) { - goto exit_function; - } - } -#endif - - /* numbers of isotopic H atoms */ - for ( j = 0, tot_num_iso_H = 0; j < NUM_H_ISOTOPES; j ++ ) { - if ( at[cur_at].num_iso_H[j] > 1 ) { - goto exit_function; /* two or more identical hydrogen isotopic neighbors */ - } - tot_num_iso_H += at[cur_at].num_iso_H[j]; - } - if ( bIgnoreIsotopicH ) { - tot_num_iso_H = 0; /* isotopic H considered subject to exchange => ignore isotopic */ - } - /* number of non-isotopic H atoms */ - if ( at[cur_at].num_H - tot_num_iso_H > 1 ) { - goto exit_function; /* two or more identical hydrogen non-isotopic neighbors */ - } - - /* count removed explicit terminal hydrogens attached to at[cur_at]. */ - /* the result is num_explicit_H. */ - /* Removed hydrogens are sorted in increasing isotopic shift order */ - if ( at_removed_H && num_removed_H > 0 ) { - for ( j = 0; j < num_removed_H; j ++ ) { - if ( at_removed_H[j].neighbor[0] == cur_at ) { - k = at_removed_H[j].iso_atw_diff; - /* iso_atw_diff values: H=>0, 1H=>1, D=2H=>2, T=3H=>3 */ - if ( k < 0 || k > NUM_H_ISOTOPES || bIgnoreIsotopicH ) - k = 0; /* treat wrong H isotopes as non-isotopic H */ - num_explicit_iso_H[k] ++; - index_H[num_explicit_H++] = j; - } - } - } - - /* coordinates initialization */ - num_z = 0; - sum_xyz[0] = sum_xyz[1] = sum_xyz[2] = 0.0; - - at_coord_center[0] = - at_coord_center[1] = - at_coord_center[2] = 0.0; - - /* fill out stereo center neighbors coordinates */ - /* and obtain the parity from the geometry */ - - for ( k = 0, j1 = 0; k < 2; k ++ ) { - switch( k ) { - - case 0: - /* add coordinates of removed hydrogens */ - for ( j = 0; j < num_explicit_H; j ++, j1 ++ ) { - next_at = index_H[j]; - /* use bond description located at removed_H atom */ - /* minus sign at get_z_coord: at_removed_H[] contains bonds TO at[cur_at], not FROM it. */ - /* Note: &at[(at_removed_H-at)+ next_at] == &at_removed_H[next_at] */ - z = -get_z_coord( at, (at_removed_H-at)+ next_at, 0 /*neighbor #*/, &nType, -(bPointedEdgeStereo & PES_BIT_POINT_EDGE_STEREO) ); - switch ( nType ) { - case ZTYPE_EITHER: - parity = vABParityUnknown /*AB_PARITY_UNKN*/ ; /* no parity: bond in "Either" direction. */ - goto exit_function; - case ZTYPE_UP: - case ZTYPE_DOWN: - nType = -nType; /* at_removed_H[] contains bonds TO the center, not from */ - b2D ++; - /* no break; here */ - case ZTYPE_3D: - num_z ++; - } - - nSbNeighOrigAtNumb[j1] = at_removed_H[next_at].orig_at_number; - at_coord[j1][0] = at_removed_H[next_at].x-at[cur_at].x; - at_coord[j1][1] = at_removed_H[next_at].y-at[cur_at].y; - bond_len_xy[j1] = len2(at_coord[j1]); - /* bond_len_xy[j1] = sqrt(at_coord[j1][0]*at_coord[j1][0]+at_coord[j1][1]*at_coord[j1][1]); */ - at_coord[j1][2] = (nType==ZTYPE_3D? z : - nType==ZTYPE_UP? bond_len_xy[j1] : - nType==ZTYPE_DOWN? -bond_len_xy[j1] : 0.0 ); - } - break; - case 1: - /* add all coordinates of other neighboring atoms */ - for ( j = 0; j < at[cur_at].valence; j ++, j1 ++ ) { - next_at = at[cur_at].neighbor[j]; - z = get_z_coord( at, cur_at, j, &nType, (bPointedEdgeStereo & PES_BIT_POINT_EDGE_STEREO) ); - switch ( nType ) { - case ZTYPE_EITHER: - parity = vABParityUnknown /*AB_PARITY_UNKN*/; /* unknown parity: bond in "Either" direction. */ - goto exit_function; - case ZTYPE_UP: - case ZTYPE_DOWN: - b2D ++; - case ZTYPE_3D: - num_z ++; - } - - nSbNeighOrigAtNumb[j1] = at[next_at].orig_at_number; - at_coord[j1][0] = at[next_at].x-at[cur_at].x; - at_coord[j1][1] = at[next_at].y-at[cur_at].y; - bond_len_xy[j1] = len2(at_coord[j1]); - /* bond_len_xy[j1] = sqrt(at_coord[j1][0]*at_coord[j1][0]+at_coord[j1][1]*at_coord[j1][1]); */ - at_coord[j1][2] = (nType==ZTYPE_3D? z : - nType==ZTYPE_UP? bond_len_xy[j1] : - nType==ZTYPE_DOWN? -bond_len_xy[j1] : 0.0 ); - } - break; - } - } - /* j1 is the number of explicit neighbors (that is, all neighbors except implicit H) */ - - b2D = (b2D == num_z && num_z); /* 1 => two-dimensional */ - - if ( MAX_NUM_STEREO_ATOM_NEIGH != at[cur_at].valence+num_explicit_H && - MAX_NUM_STEREO_ATOM_NEIGH-1 != at[cur_at].valence+num_explicit_H ) { - /* not enough geometry data to find the central atom parity */ - if ( nMustHaveNumNeigh == at[cur_at].valence+at[cur_at].num_H && - at[cur_at].num_H > 1 ) { - /* only isotopic parity is possible; no non-isotopic parity */ - if ( parity == vABParityUnknown /*AB_PARITY_UNKN*/ ) { - parity = -vABParityUnknown /*AB_PARITY_UNKN*/; /* the user marked the center as "unknown" */ - } else { - parity = -AB_PARITY_UNDF; /* not enough geometry; only isotopic parity is possible */ - } - } else { - parity = AB_PARITY_NONE; /* not a stereocenter at all */ - } - goto exit_function; - } - /* make all vector lengths equal to 1; exit if too short. 9-10-2002 */ - for ( j = 0; j < j1; j ++ ) { - z = len3( at_coord[j] ); - if ( z < MIN_BOND_LEN ) { - /* bond length is too small: use 0D parities */ - if ( AB_PARITY_NONE == (parity = GetStereocenter0DParity( at, cur_at, j1, nSbNeighOrigAtNumb, FlagSC_0D )) ) { - parity = AB_PARITY_UNDF; - } - goto exit_function; - } -#if ( STEREO_CENTER_BONDS_NORM == 1 ) - else { - mult3( at_coord[j], 1.0/z, at_coord[j] ); - } -#endif - rmax = j? inchi_max( rmax, z) : z; - rmin = j? inchi_min( rmin, z) : z; - } - if ( rmin / rmax < MIN_SINE ) { - /* bond ratio is too small: use 0D parities */ - if ( AB_PARITY_NONE == (parity = GetStereocenter0DParity( at, cur_at, j1, nSbNeighOrigAtNumb, FlagSC_0D )) ) { - parity = AB_PARITY_UNDF; - } - goto exit_function; - } - for ( j = 0; j < j1; j ++ ) { - add3( sum_xyz, at_coord[j], sum_xyz ); - } - - - - /* here j1 is a number of neighbors including explicit terminal isotopic H */ - /* num_explicit_iso_H[0] = number of explicit non-isotopic hydrogen atom neighbors */ - j = j1; - /* Add Explicit Neighbor */ - if ( j1 == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { - /* add an explicit neighbor if possible */ - if ( nMustHaveNumNeigh == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { - bAddExplicitNeighbor = ADD_EXPLICIT_LONE_PAIR_NEIGH; - } else - if ( nMustHaveNumNeigh == MAX_NUM_STEREO_ATOM_NEIGH ) { - /* check whether an explicit non-isotopic hydrogen can be added */ - /* to an atom that is a stereogenic atom */ - if ( 1 == at[cur_at].num_H - num_explicit_H && /* the atom has only one one implicit hydrogen */ - 1 == at[cur_at].num_H - tot_num_iso_H ) { /* this hydrogen is non-isotopic */ - bAddExplicitNeighbor = ADD_EXPLICIT_HYDROGEN_NEIGH; - } - } - } - - if ( bAddExplicitNeighbor ) { - /*********************************************************** - * May happen only if (j1 == MAX_NUM_STEREO_ATOM_NEIGH-1) - * 3 neighbors only, no H-neighbors. Create and add coordinates of an implicit H - * or a fake 4th neighbor, that is, a lone pair - */ - if ( parity == vABParityUnknown /*AB_PARITY_UNKN*/ ) { - goto exit_function; /* the user insists the parity is unknown and the isotopic */ - /* composition of the neighbors does not contradict */ - } else - if ( num_z == 0 || are_3_vect_in_one_plane(at_coord, MIN_SINE) ) { - /* "hydrogen down" rule is needed to resolve an ambiguity */ - if ( num_z > 0 ) { - bAmbiguous |= AMBIGUOUS_STEREO; - } -#if ( APPLY_IMPLICIT_H_DOWN_RULE == 1 ) /* { */ - /* Although H should be at the top of the list, add it to the bottom. */ - /* This will be taken care of later by inverting parity 1<->2 */ - at_coord[j][0] = 0.0; - at_coord[j][1] = 0.0; -#if ( STEREO_CENTER_BONDS_NORM == 1 ) - at_coord[j][2] = -1.0; -#else - at_coord[j][2] = -(bond_len_xy[0]+bond_len_xy[1]+bond_len_xy[2])/3.0; -#endif -#else /* } APPLY_IMPLICIT_H_DOWN_RULE { */ -#if (ALWAYS_SET_STEREO_PARITY == 1) - parity = AB_PARITY_EVEN; /* suppose atoms are pre-sorted (testing) */ -#else - /* all 3 bonds are in one plain: try to get 0D parities */ - if ( AB_PARITY_NONE == (parity = GetStereocenter0DParity( at, cur_at, j1, nSbNeighOrigAtNumb, FlagSC_0D )) ) { - parity = AB_PARITY_UNDF; - } - /*parity = AB_PARITY_UNDF;*/ /* no parity can be calculated found */ -#endif - goto exit_function; -#endif /* } APPLY_IMPLICIT_H_DOWN_RULE */ - } else { - /* we have enough information to find implicit hydrogen coordinates */ - /* - at_coord[j][0] = -sum_x; - at_coord[j][1] = -sum_y; - at_coord[j][2] = -sum_z; - */ - copy3( sum_xyz, at_coord[j] ); - change_sign3( at_coord[j], at_coord[j] ); - z = len3( at_coord[j] ); -#if ( FIX_STEREO_SCALING_BUG == 1 ) - if ( z > 1.0 ) { - rmax *= z; - } else { - rmin *= z; - } -#else - /* Comparing the original bond lengths to lenghts derived from normalized to 1 */ - /* This bug leads to pronouncing legitimate stereogenic atoms */ - /* connected by 3 bonds "undefined" if in a nicely drawn 2D structure */ - /* bond lengths are about 20 or greater. Reported by Reinhard Dunkel 2005-08-05 */ - if ( bPointedEdgeStereo & PES_BIT_FIX_SP3_BUG ) { - /* coordinate scaling bug fixed here */ - if ( z > 1.0 ) { - rmax *= z; - } else { - rmin *= z; - } - } else { - /* original InChI v.1 bug */ - rmax = inchi_max( rmax, z ); - rmin = inchi_min( rmin, z ); - } -#endif - if ( z < MIN_BOND_LEN || rmin/rmax < MIN_SINE ) { - /* the new 4th bond is too short: try to get 0D parities */ - if ( AB_PARITY_NONE == (parity = GetStereocenter0DParity( at, cur_at, j1, nSbNeighOrigAtNumb, FlagSC_0D )) ) { - parity = AB_PARITY_UNDF; - } - goto exit_function; - } -#if ( STEREO_CENTER_BOND4_NORM == 1 ) - else { - mult3( at_coord[j], 1.0/z, at_coord[j] ); - } -#endif - } - } else - if ( j1 != MAX_NUM_STEREO_ATOM_NEIGH ) { - if ( parity == vABParityUnknown /*AB_PARITY_UNKN*/ ) { - parity = -AB_PARITY_UNDF; /* isotopic composition of H-neighbors contradicts 'unknown' */ - } - goto exit_function; - } else /* j1 == MAX_NUM_STEREO_ATOM_NEIGH */ - if ( num_z == 0 || are_4at_in_one_plane(at_coord, MIN_SINE) ) { - /* all four neighours in xy plane: undefined geometry. */ - if ( num_z > 0 ) { - bAmbiguous |= AMBIGUOUS_STEREO; - } - if ( parity != vABParityUnknown /*AB_PARITY_UNKN*/ ) { -#if (ALWAYS_SET_STEREO_PARITY == 1) - parity = AB_PARITY_EVEN; /* suppose atoms are pre-sorted (testing) */ -#else - /* all 4 bonds are in one plain: try to get 0D parities */ - if ( AB_PARITY_NONE == (parity = GetStereocenter0DParity( at, cur_at, j1, nSbNeighOrigAtNumb, FlagSC_0D )) ) { - parity = AB_PARITY_UNDF; - } else - if ( ATOM_PARITY_WELL_DEF( parity ) ) { - bAmbiguous &= ~AMBIGUOUS_STEREO; /* 0D parity has resolved the ambiguity */ - } -#endif - } - goto exit_function; - } - /*********************************************************** - * At this point we have 4 neighboring atoms. - * check for tetrahedral ambiguity in 2D case - */ - if ( b2D ) - { - - n2DTetrahedralAmbiguity = Get2DTetrahedralAmbiguity( at_coord, bAddExplicitNeighbor, (bPointedEdgeStereo & PES_BIT_FIX_SP3_BUG ) ); - - if ( 0 < n2DTetrahedralAmbiguity ) - { - if ( T2D_WARN & n2DTetrahedralAmbiguity ) { - bAmbiguous |= AMBIGUOUS_STEREO; - } - if ( T2D_UNDF & n2DTetrahedralAmbiguity ) { - if ( parity != vABParityUnknown /*AB_PARITY_UNKN*/ ) { -#if (ALWAYS_SET_STEREO_PARITY == 1) - parity = AB_PARITY_EVEN; /* suppose atoms are pre-sorted (testing) */ -#else - parity = AB_PARITY_UNDF; /* no parity */ -#endif - } - goto exit_function; - } - } else - if ( n2DTetrahedralAmbiguity < 0 ) { - bAmbiguous |= AMBIGUOUS_STEREO_ERROR; /* error */ - parity = AB_PARITY_UNDF; - goto exit_function; - } - } - - /************************************************************/ - /* Move coordinates origin to the neighbor #0 */ - for ( j = 1; j < MAX_NUM_STEREO_ATOM_NEIGH; j ++ ) { - diff3(at_coord[j], at_coord[0], at_coord[j]); - } - diff3(at_coord_center, at_coord[0], at_coord_center); - - /* - for ( k = 0; k < 3; k++ ) { - for ( j = 1; j < MAX_NUM_STEREO_ATOM_NEIGH; j ++ ) { - at_coord[j][k] -= at_coord[0][k]; - } - at_coord_center[k] -= at_coord[0][k]; - } - */ - /******************************************************** - * find the central (cur_at) atom's parity - * (orientation of atoms #1-3 when looking from #0) - ********************************************************/ - triple_product = triple_prod_and_min_abs_sine2(&at_coord[1], at_coord_center, bAddExplicitNeighbor, &min_sine, &bAmbiguous); - /* - * check for tetrahedral ambiguity -- leave it out for now - */ - if ( fabs(triple_product) > ZERO_FLOAT && (min_sine > MIN_SINE || fabs(min_sine) > ZERO_FLOAT && (n2DTetrahedralAmbiguity & T2D_OKAY ) ) ) { - /* Even => sorted in correct order, Odd=>transposed */ - parity = triple_product > 0.0? AB_PARITY_EVEN : AB_PARITY_ODD; - /* if ( num_explicit_H && at[cur_at].removed_H_parity % 2 ) */ - /* odd transposition of the removed implicit H */ - /* out_at[cur_at].parity = 3 - out_at[cur_at].parity; */ - - /* moved; see below */ - /* out_at[cur_at].bAmbiguousStereo |= bAmbiguous; */ - /* at[cur_at].bAmbiguousStereo |= bAmbiguous; */ - - /* for 4 attached atoms, moving the implicit H from index=3 to index=0 */ - /* can be done in odd number (3) transpositions: (23)(12)(01), which inverts the parity */ - if ( j1 == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { - parity = 3 - parity; - } - } else { -#if (ALWAYS_SET_STEREO_PARITY == 1) - parity = AT_PARITY_EVEN; /* suppose atoms are pre-sorted (testing) */ -#else - if ( num_z > 0 ) { - bAmbiguous |= AMBIGUOUS_STEREO; - } - parity = AB_PARITY_UNDF; /* no parity: 4 bonds are in one plane. */ -#endif - } -exit_function: - - if ( parity ) { - out_at[cur_at].bAmbiguousStereo |= bAmbiguous; - at[cur_at].bAmbiguousStereo |= bAmbiguous; - } - - - /* non-isotopic parity */ - if ( at[cur_at].num_H > 1 || parity <= 0 ) - ; /* no non-isotopic parity */ - else - out_at[cur_at].parity = parity; - - /* isotopic parity */ - if ( parity == -AB_PARITY_UNDF || parity == -vABParityUnknown /*AB_PARITY_UNKN*/ ) - parity = -parity; - if ( parity < 0 ) - parity = AB_PARITY_NONE; - out_at[cur_at].parity2 = parity; - - - parity = PARITY_VAL(out_at[cur_at].parity); - out_at[cur_at].stereo_atom_parity = ATOM_PARITY_WELL_DEF( parity )? AB_PARITY_CALC : parity; - parity = PARITY_VAL(out_at[cur_at].parity2); - out_at[cur_at].stereo_atom_parity2 = ATOM_PARITY_WELL_DEF( parity )? AB_PARITY_CALC : parity; - /* - out_at[cur_at].parity2 = out_at[cur_at].parity; // save for stereo + isotopic canon. - if ( out_at[cur_at].parity ) { - if ( num_explicit_H > 1 || j1 == MAX_NUM_STEREO_ATOM_NEIGH-1 && num_explicit_H ) { - // X H X - // for example, >C< or >C-D - // Y D Y - // parity exists for stereo + isotopic atoms canonicalization only - out_at[cur_at].parity = 0; - } - } - // returning 0 means this can be an adjacent to a stereogenic bond atom - */ - - return (int)out_at[cur_at].parity2; - -} -#undef ADD_EXPLICIT_HYDROGEN_NEIGH -#undef ADD_EXPLICIT_LONE_PAIR_NEIGH - -/*************************************************************/ -int set_stereo_parity( inp_ATOM* at, sp_ATOM* at_output, int num_at, int num_removed_H, - int *nMaxNumStereoAtoms, int *nMaxNumStereoBonds, INCHI_MODE nMode, - int bPointedEdgeStereo, int vABParityUnknown ) -{ - int num_3D_stereo_atoms=0; - int num_stereo_bonds=0; /* added to fix allene stereo bug reported for FClC=C=CFCl by Burt Leland - 2009-02-05 DT */ - - int i, is_stereo, num_stereo, max_stereo_atoms=0, max_stereo_bonds=0; - QUEUE *q = NULL; - AT_RANK *nAtomLevel = NULL; - S_CHAR *cSource = NULL; - AT_RANK min_sb_ring_size = 0; - - /********************************************************** - * - * Note: this parity reflects only relative positions of - * the atoms-neighbors and their ordering in the - * lists of neighbors. - * - * To obtain the actual parity, the parity of a number - * of neighbors transpositions (to obtain a sorted - * list of numbers assigned to the atoms) should be - * added. - * - **********************************************************/ - - /********************************************************************************* - - An example of parity=1 for stereogenic center, tetrahedral asymmetric atom - - - - (1) - | - | - [C] | - | - (2)------(0) - / - / - / - / - (3) - - - Notation: (n) is a tetrahedral atom neighbor; n is an index of a neighbor in - the central_at->neighbor[] array : neighbor atom number is central_at->neighbor[n]. - - (0)-(1), (0)-(2), (0)-(3) are lines connecting atom [C] neighbors to neighbor (0) - (0), (1) and (2) are in the plane - (0)-(3) is directed from the plain to the viewer - [C] is somewhere between (0), (1), (2), (3) - Since (1)-(2)-(3) are in a clockwise order when looking from (0), parity is 2, or even; - otherwise parity would be 1, or odd. - - ********************************************************************************** - - Examples of a stereogenic bond. - - Notation: [atom number], (index of a neighbor): - [1] and [2] are atoms connected by the stereogenic bond - numbers in () are indexes of neighbors of [1] or [2]. - (12 x 16)z = z-component of [1]-[2] and [1]-[6] cross-product - - atom [1] atom [2] - [8] [4] prod01 = (12 x 16)z < 0 prod01 = (21 x 24)z < 0 - \ / prod02 = (12 x 18)z > 0 prod02 = (21 x 25)z > 0 - (2) (1) 0 transpositions because 0 transpositions because - \ / double bond is in 0 posit. double bond is in 0 position - [1]==(0)(0)==[2] 0 = (prod01 > prod02) 0 = (prod01 > prod02) - / \ - (1) (2) result: parity = 2, even result: parity=2, even - / \ - [6] [5] - - - - atom [1] atom [2] - [8] [5] prod01 = (12 x 18)z > 0 prod01 = (21 x 24)z > 0 - \ / prod02 = (12 x 16)z < 0 prod02 = (21 x 25)z < 0 - (0) (2) 2 transpositions to move 1 transposition to move - \ / at [2] from 2 to 0 pos. at [1] from 1 to 0 position - [1]==(2)(1)==[2] 1 = (prod01 > prod02) 1 = (prod01 > prod02) - / \ - (1) (0) result: parity = (1+2) result: parity=(1+1) - / \ 2-(1+2)%2 = 1, odd 2-(1+1)%2 = 2, even - [6] [4] - - - *********************************************************************************** - Note: atoms' numbers [1], [2], [4],... are not used to calculate parity at this - point. They will be used for each numbering in the canonicalization. - Note: parity=3 for a stereo atom means entered undefined bond direction - parity=4 for an atom means parity cannot be determined from the given geometry - ***********************************************************************************/ - - if ( !at_output || !at ) { - return -1; - } - - /* clear stereo descriptors */ - - for( i = 0; i < num_at; i ++ ) { - at_output[i].parity = 0; - at_output[i].parity2 = 0; - memset(&at_output[i].stereo_bond_neighbor[0], 0, sizeof(at_output[0].stereo_bond_neighbor) ); - memset(&at_output[i].stereo_bond_neighbor2[0], 0, sizeof(at_output[0].stereo_bond_neighbor2) ); - memset(&at_output[i].stereo_bond_ord[0], 0, sizeof(at_output[0].stereo_bond_ord) ); - memset(&at_output[i].stereo_bond_ord2[0], 0, sizeof(at_output[0].stereo_bond_ord2) ); - memset(&at_output[i].stereo_bond_z_prod[0], 0, sizeof(at_output[0].stereo_bond_z_prod) ); - memset(&at_output[i].stereo_bond_z_prod2[0], 0, sizeof(at_output[0].stereo_bond_z_prod2) ); - memset(&at_output[i].stereo_bond_parity[0], 0, sizeof(at_output[0].stereo_bond_parity) ); - memset(&at_output[i].stereo_bond_parity2[0], 0, sizeof(at_output[0].stereo_bond_parity2) ); - } - /* estimate max numbers of stereo atoms and bonds if isotopic H are added */ - if ( nMaxNumStereoAtoms || nMaxNumStereoBonds ) { - for( i = 0, num_stereo = 0; i < num_at; i ++ ) { - int num; - num = can_be_a_stereo_atom_with_isotopic_H( at, i, bPointedEdgeStereo ); - if ( num ) { - max_stereo_atoms += num; - } else - if ( (num = can_be_a_stereo_bond_with_isotopic_H( at, i, nMode ) ) ) { /* accept cumulenes */ - max_stereo_bonds += num; - } - } - if ( nMaxNumStereoAtoms ) - *nMaxNumStereoAtoms = max_stereo_atoms; - if ( nMaxNumStereoBonds ) - *nMaxNumStereoBonds = max_stereo_bonds; - } - /* calculate stereo descriptors */ -#if ( MIN_SB_RING_SIZE > 0 ) - min_sb_ring_size = (AT_RANK)(((nMode & REQ_MODE_MIN_SB_RING_MASK) >> REQ_MODE_MIN_SB_RING_SHFT) & AT_RANK_MASK); - if ( min_sb_ring_size >= 3 ) { - /* create BFS data structure for finding for each stereo bond its min. ring sizes */ - q = QueueCreate( num_at+1, sizeof(qInt) ); - nAtomLevel = (AT_RANK*)inchi_calloc(sizeof(nAtomLevel[0]),num_at); - cSource = (S_CHAR *)inchi_calloc(sizeof(cSource[0]),num_at); - if ( !q || !cSource || !nAtomLevel ) { - num_3D_stereo_atoms = CT_OUT_OF_RAM; - goto exit_function; - } - } else { - min_sb_ring_size = 2; - } -#endif - /* main cycle: set stereo parities */ - for( i = 0, num_stereo = 0; i < num_at; i ++ ) - { - is_stereo = set_stereo_atom_parity( at_output, at, i, at+num_at, num_removed_H, - bPointedEdgeStereo, vABParityUnknown ) ; - if ( is_stereo ) - { - num_3D_stereo_atoms += ATOM_PARITY_WELL_DEF( is_stereo ); - } - else - { - is_stereo = set_stereo_bonds_parity( at_output, at, i, at+num_at, num_removed_H, nMode, - q, nAtomLevel, cSource, min_sb_ring_size, - bPointedEdgeStereo, vABParityUnknown ); - if ( RETURNED_ERROR( is_stereo ) ) { - num_3D_stereo_atoms = is_stereo; - break; - } - num_stereo_bonds += (is_stereo != 0); /* added to fix bug reported by Burt Leland - 2009-02-05 DT */ - } - num_stereo += (is_stereo != 0); - is_stereo = is_stereo; - } - - /* added to fix bug reported by Burt Leland - 2009-02-05 DT */ - if ( max_stereo_atoms < num_3D_stereo_atoms && nMaxNumStereoAtoms ) - *nMaxNumStereoAtoms = num_3D_stereo_atoms; - if ( max_stereo_bonds < num_stereo_bonds && nMaxNumStereoBonds ) - *nMaxNumStereoBonds = num_stereo_bonds; - - /* - if ( (nMode & REQ_MODE_SC_IGN_ALL_UU ) - REQ_MODE_SC_IGN_ALL_UU - REQ_MODE_SB_IGN_ALL_UU - */ - -#if ( MIN_SB_RING_SIZE > 0 ) - if ( q ) { - q = QueueDelete( q ); - } - if ( nAtomLevel ) - inchi_free( nAtomLevel ); - if ( cSource ) - inchi_free( cSource ); -exit_function: -#endif - - - return num_3D_stereo_atoms; -} -/***************************************************************** - * Functions that disconnect bonds - * - *=== During Preprocessing === - * - * RemoveInpAtBond - * DisconnectMetalSalt (is not aware of bond parities) - * DisconnectAmmoniumSalt - * - *=== Before Normalization === - * - * remove_terminal_HDT - * - *=== During the Normalization === - * - * AddOrRemoveExplOrImplH - * - *****************************************************************/ -int ReconcileAllCmlBondParities( inp_ATOM *at, int num_atoms, int bDisconnected ) -{ - int i, ret = 0; - S_CHAR *visited = (S_CHAR*) inchi_calloc( num_atoms, sizeof(*visited) ); - if ( !visited ) - return -1; /* out of RAM */ - for ( i = 0; i < num_atoms; i ++ ) { - if ( at[i].sb_parity[0] && !visited[i] && !(bDisconnected && is_el_a_metal(at[i].el_number)) ) { - if ( ret = ReconcileCmlIncidentBondParities( at, i, -1, visited, bDisconnected ) ) { - break; /* error */ - } - } - } - inchi_free ( visited ); - return ret; -} -/*****************************************************************/ -int ReconcileCmlIncidentBondParities( inp_ATOM *at, int cur_atom, int prev_atom, S_CHAR *visited, int bDisconnected ) -{ - /* visited = 0 or parity => atom has not been visited - 10 + parity => currently is on the stack + its final parity - 20 + parity => has been visited; is not on the stack anymore + its final parity */ - int i, j, nxt_atom, ret = 0, len; - int icur2nxt, icur2neigh; /* cur atom neighbors */ - int inxt2cur, inxt2neigh; /* next atom neighbors */ - int cur_parity, nxt_parity; - int cur_order_parity, nxt_order_parity, cur_sb_parity, nxt_sb_parity, bCurMask, bNxtMask; - /* !(bDisconnected && is_el_a_metal(at[i].el_number) */ - - if ( at[cur_atom].valence > MAX_NUM_STEREO_BONDS ) - return 0; /* ignore */ - - if ( !at[cur_atom].sb_parity[0] ) - return 1; /* wrong call */ - - if ( visited[cur_atom] >= 10 ) - return 2; /* program error */ - - cur_parity = visited[cur_atom] % 10; - - visited[cur_atom] += 10; - - for ( i = 0; i < MAX_NUM_STEREO_BONDS && at[cur_atom].sb_parity[i]; i ++ ) { - icur2nxt = (int)at[cur_atom].sb_ord[i]; - len = get_opposite_sb_atom( at, cur_atom, icur2nxt, &nxt_atom, &inxt2cur, &j ); - if ( !len ) { - return 4; /* could not find the opposite atom: bond parity data error */ - } - if ( nxt_atom == prev_atom ) - continue; - if ( visited[nxt_atom] >= 20 ) - continue; /* back edge, second visit: ignore */ - if ( at[nxt_atom].valence > MAX_NUM_STEREO_BONDS ) - continue; /* may be treated only after metal disconnection */ - - if ( bDisconnected && (at[cur_atom].sb_parity[i] & SB_PARITY_FLAG) ) { - cur_sb_parity = (at[cur_atom].sb_parity[i] >> SB_PARITY_SHFT); - bCurMask = 3 << SB_PARITY_SHFT; - } else { - cur_sb_parity = (at[cur_atom].sb_parity[i] & SB_PARITY_MASK); - bCurMask = 3; - } - if ( bDisconnected && (at[nxt_atom].sb_parity[j] & SB_PARITY_FLAG) ) { - nxt_sb_parity = (at[nxt_atom].sb_parity[j] >> SB_PARITY_SHFT); - bNxtMask = 3 << SB_PARITY_SHFT; - } else { - nxt_sb_parity = (at[nxt_atom].sb_parity[j] & SB_PARITY_MASK); - bNxtMask = 3; - } - - - - if ( !ATOM_PARITY_WELL_DEF(cur_sb_parity) || - !ATOM_PARITY_WELL_DEF(nxt_sb_parity) ) { - if ( cur_sb_parity == nxt_sb_parity ) { - continue; - /*goto move_forward;*/ /* bypass unknown/undefined */ - } - return 3; /* sb parities do not match: bond parity data error */ - } - - icur2neigh = (int)at[cur_atom].sn_ord[i]; - inxt2neigh = (int)at[nxt_atom].sn_ord[j]; - /* parity of at[cur_atom].neighbor[] premutation to reach this order: { next_atom, neigh_atom, ...} */ - - /* 1. move next_atom from position=icur2nxt to position=0 => - * icur2nxt permutations - * 2. move neigh_atom from position=inxt2neigh+(inxt2cur > inxt2neigh) to position=1 => - * inxt2neigh+(inxt2cur > inxt2neigh)-1 permutations. - * Note if (inxt2cur > inxt2neigh) then move #1 increments neigh_atom position - * Note add 4 because icur2neigh may be negative due to isotopic H removal - */ - cur_order_parity = (4+icur2nxt + icur2neigh + (icur2neigh > icur2nxt)) % 2; - /* same for next atom: */ - /* parity of at[nxt_atom].neighbor[] premutation to reach this order: { cur_atom, neigh_atom, ...} */ - nxt_order_parity = (4+inxt2cur + inxt2neigh + (inxt2neigh > inxt2cur)) % 2; - - nxt_parity = visited[nxt_atom] % 10; - - if ( !cur_parity ) { - cur_parity = 2 - (cur_order_parity + cur_sb_parity) % 2; - visited[cur_atom] += cur_parity; - } else - if ( cur_parity != 2 - (cur_order_parity + cur_sb_parity) % 2 ) { - - /***** reconcile bond parities *****/ - - /* Each bond parity is split into two values located at the end atoms. - For T (trans) the values are (1,1) or (2,2) - For C (cis) the values are (1,2) or (2,1) - The fact that one pair = another with inverted parities, namely - Inv(1,1) = (2,2) and Inv(1,2) = (2,1), allows to - simultaneouly invert parities of the current bond end atoms - (at[cur_atom].sb_parity[i], at[nxt_atom].sb_parity[j]) - so that the final current atom parity cur_parity - calculated later in stereochemical canonicalization for - each stereobond incident with the current atomis same. - Achieving this is called here RECONCILIATION. - If at the closure of an aromatic circuit the parities of - next atom cannot be reconciled with already calculated then - this function returns 5 (error). - */ - - at[cur_atom].sb_parity[i] ^= bCurMask; - at[nxt_atom].sb_parity[j] ^= bNxtMask; - cur_sb_parity ^= 3; - nxt_sb_parity ^= 3; - } - - if ( !nxt_parity ) { - nxt_parity = 2 - (nxt_order_parity + nxt_sb_parity) % 2; - visited[nxt_atom] += nxt_parity; - } else - if ( nxt_parity != 2 - (nxt_order_parity + nxt_sb_parity) % 2 ) { - return 5; /* algorithm does not work for Mebius-like structures */ - } - -/* move_forward: */ - if ( visited[nxt_atom] < 10 ) { - ret = ReconcileCmlIncidentBondParities( at, nxt_atom, cur_atom, visited, bDisconnected ); - if ( ret ) { - break; - } - } - } - visited[cur_atom] += 10; /* all bonds incident to the current atom have - been processed or an error occurred. */ - return ret; -} -/*****************************************************************/ -int get_opposite_sb_atom( inp_ATOM *at, int cur_atom, int icur2nxt, int *pnxt_atom, int *pinxt2cur, int *pinxt_sb_parity_ord ) -{ - AT_NUMB nxt_atom; - int j, len; - - len = 0; - while ( len ++ < 20 ) { /* arbitrarily set cumulene length limit to avoid infinite loop */ - nxt_atom = at[cur_atom].neighbor[icur2nxt]; - for ( j = 0; j < MAX_NUM_STEREO_BONDS && at[nxt_atom].sb_parity[j]; j ++ ) { - if ( cur_atom == at[nxt_atom].neighbor[(int)at[nxt_atom].sb_ord[j]] ) { - /* found the opposite atom */ - *pnxt_atom = nxt_atom; - *pinxt2cur = at[nxt_atom].sb_ord[j]; - *pinxt_sb_parity_ord = j; - return len; - } - } - if ( j ) { - return 0; /* reached atom(s) with stereobond (sb) parity, the opposite atom has not been found */ - } - if ( at[nxt_atom].valence == 2 && 2*BOND_TYPE_DOUBLE == at[nxt_atom].chem_bonds_valence ) { - /* follow cumulene =X= path */ - icur2nxt = (at[nxt_atom].neighbor[0] == cur_atom); - cur_atom = nxt_atom; - } else { - return 0; /* neither atom with a sb parity not middle cumulene could be reached */ - } - } - return 0; /* too long chain of cumulene was found */ -} diff --git a/INCHI-1-SRC/INCHI/common/ichitime.h b/INCHI-1-SRC/INCHI/common/ichitime.h deleted file mode 100644 index 92bad0f..0000000 --- a/INCHI-1-SRC/INCHI/common/ichitime.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __ICHITIME_H__ -#define __ICHITIME_H__ - - -#ifdef COMPILE_ANSI_ONLY - -#ifdef __FreeBSD__ -#include -#endif - -/* get times() */ -#ifdef INCHI_USETIMES -#include -#endif - -/*#include */ - -#include - -typedef struct tagInchiTime { - clock_t clockTime; -} inchiTime; - -#else - -/* Win32 _ftime(): */ -#include - -typedef struct tagInchiTime { - unsigned long clockTime; /* Time in seconds since midnight (00:00:00), January 1, 1970; - signed long overflow expected in 2038 */ - long millitime; /* milliseconds */ - -} inchiTime; - -#endif - - -#ifdef TARGET_EXE_USING_API - -#define InchiTimeGet e_InchiTimeGet -#define InchiTimeMsecDiff e_InchiTimeMsecDiff -#define InchiTimeAddMsec e_InchiTimeAddMsec -#define bInchiTimeIsOver e_bInchiTimeIsOver -#define InchiTimeElapsed e_InchiTimeElapsed - -#define FullMaxClock e_FullMaxClock -#define HalfMaxClock e_HalfMaxClock -#define MaxPositiveClock e_MaxPositiveClock -#define MinNegativeClock e_MinNegativeClock -#define HalfMaxPositiveClock e_HalfMaxPositiveClock -#define HalfMinNegativeClock e_HalfMinNegativeClock - - - -#endif - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - -void InchiTimeGet( inchiTime *TickEnd ); -long InchiTimeMsecDiff( inchiTime *TickEnd, inchiTime *TickStart ); -void InchiTimeAddMsec( inchiTime *TickEnd, unsigned long nNumMsec ); -int bInchiTimeIsOver( inchiTime *TickEnd ); -long InchiTimeElapsed( inchiTime *TickStart ); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /* __ICHITIME_H__ */ - diff --git a/INCHI-1-SRC/INCHI/common/ikey_base26.c b/INCHI-1-SRC/INCHI/common/ikey_base26.c deleted file mode 100644 index b30b3db..0000000 --- a/INCHI-1-SRC/INCHI/common/ikey_base26.c +++ /dev/null @@ -1,1366 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - InChIKey: procedures for base-26 encoding - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - -#ifdef _MSC_VER -#if _MSC_VER > 1000 -#pragma warning( disable : 4996 ) -#endif -#endif - -#include -#include -#include -#include "ikey_base26.h" - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - As the 2^14 (16384) is very close to 26^3 (17576), a triplet of uppercase - letters A..Z encodes 14 bits with good efficiency. - For speed, we just tabulate triplets below. - - We should throw away 17576-16384= 1192 triplets. - These are 676 triplets starting from 'E', the most frequent letter in English - texts (the other 516 are those started at 'T' , "TAA" to "TTV"). - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -static char t26[][4] = -{ -"AAA","AAB","AAC","AAD","AAE","AAF","AAG","AAH","AAI","AAJ","AAK","AAL","AAM","AAN","AAO","AAP", -"AAQ","AAR","AAS","AAT","AAU","AAV","AAW","AAX","AAY","AAZ","ABA","ABB","ABC","ABD","ABE","ABF", -"ABG","ABH","ABI","ABJ","ABK","ABL","ABM","ABN","ABO","ABP","ABQ","ABR","ABS","ABT","ABU","ABV", -"ABW","ABX","ABY","ABZ","ACA","ACB","ACC","ACD","ACE","ACF","ACG","ACH","ACI","ACJ","ACK","ACL", -"ACM","ACN","ACO","ACP","ACQ","ACR","ACS","ACT","ACU","ACV","ACW","ACX","ACY","ACZ","ADA","ADB", -"ADC","ADD","ADE","ADF","ADG","ADH","ADI","ADJ","ADK","ADL","ADM","ADN","ADO","ADP","ADQ","ADR", -"ADS","ADT","ADU","ADV","ADW","ADX","ADY","ADZ","AEA","AEB","AEC","AED","AEE","AEF","AEG","AEH", -"AEI","AEJ","AEK","AEL","AEM","AEN","AEO","AEP","AEQ","AER","AES","AET","AEU","AEV","AEW","AEX", -"AEY","AEZ","AFA","AFB","AFC","AFD","AFE","AFF","AFG","AFH","AFI","AFJ","AFK","AFL","AFM","AFN", -"AFO","AFP","AFQ","AFR","AFS","AFT","AFU","AFV","AFW","AFX","AFY","AFZ","AGA","AGB","AGC","AGD", -"AGE","AGF","AGG","AGH","AGI","AGJ","AGK","AGL","AGM","AGN","AGO","AGP","AGQ","AGR","AGS","AGT", -"AGU","AGV","AGW","AGX","AGY","AGZ","AHA","AHB","AHC","AHD","AHE","AHF","AHG","AHH","AHI","AHJ", -"AHK","AHL","AHM","AHN","AHO","AHP","AHQ","AHR","AHS","AHT","AHU","AHV","AHW","AHX","AHY","AHZ", -"AIA","AIB","AIC","AID","AIE","AIF","AIG","AIH","AII","AIJ","AIK","AIL","AIM","AIN","AIO","AIP", -"AIQ","AIR","AIS","AIT","AIU","AIV","AIW","AIX","AIY","AIZ","AJA","AJB","AJC","AJD","AJE","AJF", -"AJG","AJH","AJI","AJJ","AJK","AJL","AJM","AJN","AJO","AJP","AJQ","AJR","AJS","AJT","AJU","AJV", -"AJW","AJX","AJY","AJZ","AKA","AKB","AKC","AKD","AKE","AKF","AKG","AKH","AKI","AKJ","AKK","AKL", -"AKM","AKN","AKO","AKP","AKQ","AKR","AKS","AKT","AKU","AKV","AKW","AKX","AKY","AKZ","ALA","ALB", -"ALC","ALD","ALE","ALF","ALG","ALH","ALI","ALJ","ALK","ALL","ALM","ALN","ALO","ALP","ALQ","ALR", -"ALS","ALT","ALU","ALV","ALW","ALX","ALY","ALZ","AMA","AMB","AMC","AMD","AME","AMF","AMG","AMH", -"AMI","AMJ","AMK","AML","AMM","AMN","AMO","AMP","AMQ","AMR","AMS","AMT","AMU","AMV","AMW","AMX", -"AMY","AMZ","ANA","ANB","ANC","AND","ANE","ANF","ANG","ANH","ANI","ANJ","ANK","ANL","ANM","ANN", -"ANO","ANP","ANQ","ANR","ANS","ANT","ANU","ANV","ANW","ANX","ANY","ANZ","AOA","AOB","AOC","AOD", -"AOE","AOF","AOG","AOH","AOI","AOJ","AOK","AOL","AOM","AON","AOO","AOP","AOQ","AOR","AOS","AOT", -"AOU","AOV","AOW","AOX","AOY","AOZ","APA","APB","APC","APD","APE","APF","APG","APH","API","APJ", -"APK","APL","APM","APN","APO","APP","APQ","APR","APS","APT","APU","APV","APW","APX","APY","APZ", -"AQA","AQB","AQC","AQD","AQE","AQF","AQG","AQH","AQI","AQJ","AQK","AQL","AQM","AQN","AQO","AQP", -"AQQ","AQR","AQS","AQT","AQU","AQV","AQW","AQX","AQY","AQZ","ARA","ARB","ARC","ARD","ARE","ARF", -"ARG","ARH","ARI","ARJ","ARK","ARL","ARM","ARN","ARO","ARP","ARQ","ARR","ARS","ART","ARU","ARV", -"ARW","ARX","ARY","ARZ","ASA","ASB","ASC","ASD","ASE","ASF","ASG","ASH","ASI","ASJ","ASK","ASL", -"ASM","ASN","ASO","ASP","ASQ","ASR","ASS","AST","ASU","ASV","ASW","ASX","ASY","ASZ","ATA","ATB", -"ATC","ATD","ATE","ATF","ATG","ATH","ATI","ATJ","ATK","ATL","ATM","ATN","ATO","ATP","ATQ","ATR", -"ATS","ATT","ATU","ATV","ATW","ATX","ATY","ATZ","AUA","AUB","AUC","AUD","AUE","AUF","AUG","AUH", -"AUI","AUJ","AUK","AUL","AUM","AUN","AUO","AUP","AUQ","AUR","AUS","AUT","AUU","AUV","AUW","AUX", -"AUY","AUZ","AVA","AVB","AVC","AVD","AVE","AVF","AVG","AVH","AVI","AVJ","AVK","AVL","AVM","AVN", -"AVO","AVP","AVQ","AVR","AVS","AVT","AVU","AVV","AVW","AVX","AVY","AVZ","AWA","AWB","AWC","AWD", -"AWE","AWF","AWG","AWH","AWI","AWJ","AWK","AWL","AWM","AWN","AWO","AWP","AWQ","AWR","AWS","AWT", -"AWU","AWV","AWW","AWX","AWY","AWZ","AXA","AXB","AXC","AXD","AXE","AXF","AXG","AXH","AXI","AXJ", -"AXK","AXL","AXM","AXN","AXO","AXP","AXQ","AXR","AXS","AXT","AXU","AXV","AXW","AXX","AXY","AXZ", -"AYA","AYB","AYC","AYD","AYE","AYF","AYG","AYH","AYI","AYJ","AYK","AYL","AYM","AYN","AYO","AYP", -"AYQ","AYR","AYS","AYT","AYU","AYV","AYW","AYX","AYY","AYZ","AZA","AZB","AZC","AZD","AZE","AZF", -"AZG","AZH","AZI","AZJ","AZK","AZL","AZM","AZN","AZO","AZP","AZQ","AZR","AZS","AZT","AZU","AZV", -"AZW","AZX","AZY","AZZ","BAA","BAB","BAC","BAD","BAE","BAF","BAG","BAH","BAI","BAJ","BAK","BAL", -"BAM","BAN","BAO","BAP","BAQ","BAR","BAS","BAT","BAU","BAV","BAW","BAX","BAY","BAZ","BBA","BBB", -"BBC","BBD","BBE","BBF","BBG","BBH","BBI","BBJ","BBK","BBL","BBM","BBN","BBO","BBP","BBQ","BBR", -"BBS","BBT","BBU","BBV","BBW","BBX","BBY","BBZ","BCA","BCB","BCC","BCD","BCE","BCF","BCG","BCH", -"BCI","BCJ","BCK","BCL","BCM","BCN","BCO","BCP","BCQ","BCR","BCS","BCT","BCU","BCV","BCW","BCX", -"BCY","BCZ","BDA","BDB","BDC","BDD","BDE","BDF","BDG","BDH","BDI","BDJ","BDK","BDL","BDM","BDN", -"BDO","BDP","BDQ","BDR","BDS","BDT","BDU","BDV","BDW","BDX","BDY","BDZ","BEA","BEB","BEC","BED", -"BEE","BEF","BEG","BEH","BEI","BEJ","BEK","BEL","BEM","BEN","BEO","BEP","BEQ","BER","BES","BET", -"BEU","BEV","BEW","BEX","BEY","BEZ","BFA","BFB","BFC","BFD","BFE","BFF","BFG","BFH","BFI","BFJ", -"BFK","BFL","BFM","BFN","BFO","BFP","BFQ","BFR","BFS","BFT","BFU","BFV","BFW","BFX","BFY","BFZ", -"BGA","BGB","BGC","BGD","BGE","BGF","BGG","BGH","BGI","BGJ","BGK","BGL","BGM","BGN","BGO","BGP", -"BGQ","BGR","BGS","BGT","BGU","BGV","BGW","BGX","BGY","BGZ","BHA","BHB","BHC","BHD","BHE","BHF", -"BHG","BHH","BHI","BHJ","BHK","BHL","BHM","BHN","BHO","BHP","BHQ","BHR","BHS","BHT","BHU","BHV", -"BHW","BHX","BHY","BHZ","BIA","BIB","BIC","BID","BIE","BIF","BIG","BIH","BII","BIJ","BIK","BIL", -"BIM","BIN","BIO","BIP","BIQ","BIR","BIS","BIT","BIU","BIV","BIW","BIX","BIY","BIZ","BJA","BJB", -"BJC","BJD","BJE","BJF","BJG","BJH","BJI","BJJ","BJK","BJL","BJM","BJN","BJO","BJP","BJQ","BJR", -"BJS","BJT","BJU","BJV","BJW","BJX","BJY","BJZ","BKA","BKB","BKC","BKD","BKE","BKF","BKG","BKH", -"BKI","BKJ","BKK","BKL","BKM","BKN","BKO","BKP","BKQ","BKR","BKS","BKT","BKU","BKV","BKW","BKX", -"BKY","BKZ","BLA","BLB","BLC","BLD","BLE","BLF","BLG","BLH","BLI","BLJ","BLK","BLL","BLM","BLN", -"BLO","BLP","BLQ","BLR","BLS","BLT","BLU","BLV","BLW","BLX","BLY","BLZ","BMA","BMB","BMC","BMD", -"BME","BMF","BMG","BMH","BMI","BMJ","BMK","BML","BMM","BMN","BMO","BMP","BMQ","BMR","BMS","BMT", -"BMU","BMV","BMW","BMX","BMY","BMZ","BNA","BNB","BNC","BND","BNE","BNF","BNG","BNH","BNI","BNJ", -"BNK","BNL","BNM","BNN","BNO","BNP","BNQ","BNR","BNS","BNT","BNU","BNV","BNW","BNX","BNY","BNZ", -"BOA","BOB","BOC","BOD","BOE","BOF","BOG","BOH","BOI","BOJ","BOK","BOL","BOM","BON","BOO","BOP", -"BOQ","BOR","BOS","BOT","BOU","BOV","BOW","BOX","BOY","BOZ","BPA","BPB","BPC","BPD","BPE","BPF", -"BPG","BPH","BPI","BPJ","BPK","BPL","BPM","BPN","BPO","BPP","BPQ","BPR","BPS","BPT","BPU","BPV", -"BPW","BPX","BPY","BPZ","BQA","BQB","BQC","BQD","BQE","BQF","BQG","BQH","BQI","BQJ","BQK","BQL", -"BQM","BQN","BQO","BQP","BQQ","BQR","BQS","BQT","BQU","BQV","BQW","BQX","BQY","BQZ","BRA","BRB", -"BRC","BRD","BRE","BRF","BRG","BRH","BRI","BRJ","BRK","BRL","BRM","BRN","BRO","BRP","BRQ","BRR", -"BRS","BRT","BRU","BRV","BRW","BRX","BRY","BRZ","BSA","BSB","BSC","BSD","BSE","BSF","BSG","BSH", -"BSI","BSJ","BSK","BSL","BSM","BSN","BSO","BSP","BSQ","BSR","BSS","BST","BSU","BSV","BSW","BSX", -"BSY","BSZ","BTA","BTB","BTC","BTD","BTE","BTF","BTG","BTH","BTI","BTJ","BTK","BTL","BTM","BTN", -"BTO","BTP","BTQ","BTR","BTS","BTT","BTU","BTV","BTW","BTX","BTY","BTZ","BUA","BUB","BUC","BUD", -"BUE","BUF","BUG","BUH","BUI","BUJ","BUK","BUL","BUM","BUN","BUO","BUP","BUQ","BUR","BUS","BUT", -"BUU","BUV","BUW","BUX","BUY","BUZ","BVA","BVB","BVC","BVD","BVE","BVF","BVG","BVH","BVI","BVJ", -"BVK","BVL","BVM","BVN","BVO","BVP","BVQ","BVR","BVS","BVT","BVU","BVV","BVW","BVX","BVY","BVZ", -"BWA","BWB","BWC","BWD","BWE","BWF","BWG","BWH","BWI","BWJ","BWK","BWL","BWM","BWN","BWO","BWP", -"BWQ","BWR","BWS","BWT","BWU","BWV","BWW","BWX","BWY","BWZ","BXA","BXB","BXC","BXD","BXE","BXF", -"BXG","BXH","BXI","BXJ","BXK","BXL","BXM","BXN","BXO","BXP","BXQ","BXR","BXS","BXT","BXU","BXV", -"BXW","BXX","BXY","BXZ","BYA","BYB","BYC","BYD","BYE","BYF","BYG","BYH","BYI","BYJ","BYK","BYL", -"BYM","BYN","BYO","BYP","BYQ","BYR","BYS","BYT","BYU","BYV","BYW","BYX","BYY","BYZ","BZA","BZB", -"BZC","BZD","BZE","BZF","BZG","BZH","BZI","BZJ","BZK","BZL","BZM","BZN","BZO","BZP","BZQ","BZR", -"BZS","BZT","BZU","BZV","BZW","BZX","BZY","BZZ","CAA","CAB","CAC","CAD","CAE","CAF","CAG","CAH", -"CAI","CAJ","CAK","CAL","CAM","CAN","CAO","CAP","CAQ","CAR","CAS","CAT","CAU","CAV","CAW","CAX", -"CAY","CAZ","CBA","CBB","CBC","CBD","CBE","CBF","CBG","CBH","CBI","CBJ","CBK","CBL","CBM","CBN", -"CBO","CBP","CBQ","CBR","CBS","CBT","CBU","CBV","CBW","CBX","CBY","CBZ","CCA","CCB","CCC","CCD", -"CCE","CCF","CCG","CCH","CCI","CCJ","CCK","CCL","CCM","CCN","CCO","CCP","CCQ","CCR","CCS","CCT", -"CCU","CCV","CCW","CCX","CCY","CCZ","CDA","CDB","CDC","CDD","CDE","CDF","CDG","CDH","CDI","CDJ", -"CDK","CDL","CDM","CDN","CDO","CDP","CDQ","CDR","CDS","CDT","CDU","CDV","CDW","CDX","CDY","CDZ", -"CEA","CEB","CEC","CED","CEE","CEF","CEG","CEH","CEI","CEJ","CEK","CEL","CEM","CEN","CEO","CEP", -"CEQ","CER","CES","CET","CEU","CEV","CEW","CEX","CEY","CEZ","CFA","CFB","CFC","CFD","CFE","CFF", -"CFG","CFH","CFI","CFJ","CFK","CFL","CFM","CFN","CFO","CFP","CFQ","CFR","CFS","CFT","CFU","CFV", -"CFW","CFX","CFY","CFZ","CGA","CGB","CGC","CGD","CGE","CGF","CGG","CGH","CGI","CGJ","CGK","CGL", -"CGM","CGN","CGO","CGP","CGQ","CGR","CGS","CGT","CGU","CGV","CGW","CGX","CGY","CGZ","CHA","CHB", -"CHC","CHD","CHE","CHF","CHG","CHH","CHI","CHJ","CHK","CHL","CHM","CHN","CHO","CHP","CHQ","CHR", -"CHS","CHT","CHU","CHV","CHW","CHX","CHY","CHZ","CIA","CIB","CIC","CID","CIE","CIF","CIG","CIH", -"CII","CIJ","CIK","CIL","CIM","CIN","CIO","CIP","CIQ","CIR","CIS","CIT","CIU","CIV","CIW","CIX", -"CIY","CIZ","CJA","CJB","CJC","CJD","CJE","CJF","CJG","CJH","CJI","CJJ","CJK","CJL","CJM","CJN", -"CJO","CJP","CJQ","CJR","CJS","CJT","CJU","CJV","CJW","CJX","CJY","CJZ","CKA","CKB","CKC","CKD", -"CKE","CKF","CKG","CKH","CKI","CKJ","CKK","CKL","CKM","CKN","CKO","CKP","CKQ","CKR","CKS","CKT", -"CKU","CKV","CKW","CKX","CKY","CKZ","CLA","CLB","CLC","CLD","CLE","CLF","CLG","CLH","CLI","CLJ", -"CLK","CLL","CLM","CLN","CLO","CLP","CLQ","CLR","CLS","CLT","CLU","CLV","CLW","CLX","CLY","CLZ", -"CMA","CMB","CMC","CMD","CME","CMF","CMG","CMH","CMI","CMJ","CMK","CML","CMM","CMN","CMO","CMP", -"CMQ","CMR","CMS","CMT","CMU","CMV","CMW","CMX","CMY","CMZ","CNA","CNB","CNC","CND","CNE","CNF", -"CNG","CNH","CNI","CNJ","CNK","CNL","CNM","CNN","CNO","CNP","CNQ","CNR","CNS","CNT","CNU","CNV", -"CNW","CNX","CNY","CNZ","COA","COB","COC","COD","COE","COF","COG","COH","COI","COJ","COK","COL", -"COM","CON","COO","COP","COQ","COR","COS","COT","COU","COV","COW","COX","COY","COZ","CPA","CPB", -"CPC","CPD","CPE","CPF","CPG","CPH","CPI","CPJ","CPK","CPL","CPM","CPN","CPO","CPP","CPQ","CPR", -"CPS","CPT","CPU","CPV","CPW","CPX","CPY","CPZ","CQA","CQB","CQC","CQD","CQE","CQF","CQG","CQH", -"CQI","CQJ","CQK","CQL","CQM","CQN","CQO","CQP","CQQ","CQR","CQS","CQT","CQU","CQV","CQW","CQX", -"CQY","CQZ","CRA","CRB","CRC","CRD","CRE","CRF","CRG","CRH","CRI","CRJ","CRK","CRL","CRM","CRN", -"CRO","CRP","CRQ","CRR","CRS","CRT","CRU","CRV","CRW","CRX","CRY","CRZ","CSA","CSB","CSC","CSD", -"CSE","CSF","CSG","CSH","CSI","CSJ","CSK","CSL","CSM","CSN","CSO","CSP","CSQ","CSR","CSS","CST", -"CSU","CSV","CSW","CSX","CSY","CSZ","CTA","CTB","CTC","CTD","CTE","CTF","CTG","CTH","CTI","CTJ", -"CTK","CTL","CTM","CTN","CTO","CTP","CTQ","CTR","CTS","CTT","CTU","CTV","CTW","CTX","CTY","CTZ", -"CUA","CUB","CUC","CUD","CUE","CUF","CUG","CUH","CUI","CUJ","CUK","CUL","CUM","CUN","CUO","CUP", -"CUQ","CUR","CUS","CUT","CUU","CUV","CUW","CUX","CUY","CUZ","CVA","CVB","CVC","CVD","CVE","CVF", -"CVG","CVH","CVI","CVJ","CVK","CVL","CVM","CVN","CVO","CVP","CVQ","CVR","CVS","CVT","CVU","CVV", -"CVW","CVX","CVY","CVZ","CWA","CWB","CWC","CWD","CWE","CWF","CWG","CWH","CWI","CWJ","CWK","CWL", -"CWM","CWN","CWO","CWP","CWQ","CWR","CWS","CWT","CWU","CWV","CWW","CWX","CWY","CWZ","CXA","CXB", -"CXC","CXD","CXE","CXF","CXG","CXH","CXI","CXJ","CXK","CXL","CXM","CXN","CXO","CXP","CXQ","CXR", -"CXS","CXT","CXU","CXV","CXW","CXX","CXY","CXZ","CYA","CYB","CYC","CYD","CYE","CYF","CYG","CYH", -"CYI","CYJ","CYK","CYL","CYM","CYN","CYO","CYP","CYQ","CYR","CYS","CYT","CYU","CYV","CYW","CYX", -"CYY","CYZ","CZA","CZB","CZC","CZD","CZE","CZF","CZG","CZH","CZI","CZJ","CZK","CZL","CZM","CZN", -"CZO","CZP","CZQ","CZR","CZS","CZT","CZU","CZV","CZW","CZX","CZY","CZZ","DAA","DAB","DAC","DAD", -"DAE","DAF","DAG","DAH","DAI","DAJ","DAK","DAL","DAM","DAN","DAO","DAP","DAQ","DAR","DAS","DAT", -"DAU","DAV","DAW","DAX","DAY","DAZ","DBA","DBB","DBC","DBD","DBE","DBF","DBG","DBH","DBI","DBJ", -"DBK","DBL","DBM","DBN","DBO","DBP","DBQ","DBR","DBS","DBT","DBU","DBV","DBW","DBX","DBY","DBZ", -"DCA","DCB","DCC","DCD","DCE","DCF","DCG","DCH","DCI","DCJ","DCK","DCL","DCM","DCN","DCO","DCP", -"DCQ","DCR","DCS","DCT","DCU","DCV","DCW","DCX","DCY","DCZ","DDA","DDB","DDC","DDD","DDE","DDF", -"DDG","DDH","DDI","DDJ","DDK","DDL","DDM","DDN","DDO","DDP","DDQ","DDR","DDS","DDT","DDU","DDV", -"DDW","DDX","DDY","DDZ","DEA","DEB","DEC","DED","DEE","DEF","DEG","DEH","DEI","DEJ","DEK","DEL", -"DEM","DEN","DEO","DEP","DEQ","DER","DES","DET","DEU","DEV","DEW","DEX","DEY","DEZ","DFA","DFB", -"DFC","DFD","DFE","DFF","DFG","DFH","DFI","DFJ","DFK","DFL","DFM","DFN","DFO","DFP","DFQ","DFR", -"DFS","DFT","DFU","DFV","DFW","DFX","DFY","DFZ","DGA","DGB","DGC","DGD","DGE","DGF","DGG","DGH", -"DGI","DGJ","DGK","DGL","DGM","DGN","DGO","DGP","DGQ","DGR","DGS","DGT","DGU","DGV","DGW","DGX", -"DGY","DGZ","DHA","DHB","DHC","DHD","DHE","DHF","DHG","DHH","DHI","DHJ","DHK","DHL","DHM","DHN", -"DHO","DHP","DHQ","DHR","DHS","DHT","DHU","DHV","DHW","DHX","DHY","DHZ","DIA","DIB","DIC","DID", -"DIE","DIF","DIG","DIH","DII","DIJ","DIK","DIL","DIM","DIN","DIO","DIP","DIQ","DIR","DIS","DIT", -"DIU","DIV","DIW","DIX","DIY","DIZ","DJA","DJB","DJC","DJD","DJE","DJF","DJG","DJH","DJI","DJJ", -"DJK","DJL","DJM","DJN","DJO","DJP","DJQ","DJR","DJS","DJT","DJU","DJV","DJW","DJX","DJY","DJZ", -"DKA","DKB","DKC","DKD","DKE","DKF","DKG","DKH","DKI","DKJ","DKK","DKL","DKM","DKN","DKO","DKP", -"DKQ","DKR","DKS","DKT","DKU","DKV","DKW","DKX","DKY","DKZ","DLA","DLB","DLC","DLD","DLE","DLF", -"DLG","DLH","DLI","DLJ","DLK","DLL","DLM","DLN","DLO","DLP","DLQ","DLR","DLS","DLT","DLU","DLV", -"DLW","DLX","DLY","DLZ","DMA","DMB","DMC","DMD","DME","DMF","DMG","DMH","DMI","DMJ","DMK","DML", -"DMM","DMN","DMO","DMP","DMQ","DMR","DMS","DMT","DMU","DMV","DMW","DMX","DMY","DMZ","DNA","DNB", -"DNC","DND","DNE","DNF","DNG","DNH","DNI","DNJ","DNK","DNL","DNM","DNN","DNO","DNP","DNQ","DNR", -"DNS","DNT","DNU","DNV","DNW","DNX","DNY","DNZ","DOA","DOB","DOC","DOD","DOE","DOF","DOG","DOH", -"DOI","DOJ","DOK","DOL","DOM","DON","DOO","DOP","DOQ","DOR","DOS","DOT","DOU","DOV","DOW","DOX", -"DOY","DOZ","DPA","DPB","DPC","DPD","DPE","DPF","DPG","DPH","DPI","DPJ","DPK","DPL","DPM","DPN", -"DPO","DPP","DPQ","DPR","DPS","DPT","DPU","DPV","DPW","DPX","DPY","DPZ","DQA","DQB","DQC","DQD", -"DQE","DQF","DQG","DQH","DQI","DQJ","DQK","DQL","DQM","DQN","DQO","DQP","DQQ","DQR","DQS","DQT", -"DQU","DQV","DQW","DQX","DQY","DQZ","DRA","DRB","DRC","DRD","DRE","DRF","DRG","DRH","DRI","DRJ", -"DRK","DRL","DRM","DRN","DRO","DRP","DRQ","DRR","DRS","DRT","DRU","DRV","DRW","DRX","DRY","DRZ", -"DSA","DSB","DSC","DSD","DSE","DSF","DSG","DSH","DSI","DSJ","DSK","DSL","DSM","DSN","DSO","DSP", -"DSQ","DSR","DSS","DST","DSU","DSV","DSW","DSX","DSY","DSZ","DTA","DTB","DTC","DTD","DTE","DTF", -"DTG","DTH","DTI","DTJ","DTK","DTL","DTM","DTN","DTO","DTP","DTQ","DTR","DTS","DTT","DTU","DTV", -"DTW","DTX","DTY","DTZ","DUA","DUB","DUC","DUD","DUE","DUF","DUG","DUH","DUI","DUJ","DUK","DUL", -"DUM","DUN","DUO","DUP","DUQ","DUR","DUS","DUT","DUU","DUV","DUW","DUX","DUY","DUZ","DVA","DVB", -"DVC","DVD","DVE","DVF","DVG","DVH","DVI","DVJ","DVK","DVL","DVM","DVN","DVO","DVP","DVQ","DVR", -"DVS","DVT","DVU","DVV","DVW","DVX","DVY","DVZ","DWA","DWB","DWC","DWD","DWE","DWF","DWG","DWH", -"DWI","DWJ","DWK","DWL","DWM","DWN","DWO","DWP","DWQ","DWR","DWS","DWT","DWU","DWV","DWW","DWX", -"DWY","DWZ","DXA","DXB","DXC","DXD","DXE","DXF","DXG","DXH","DXI","DXJ","DXK","DXL","DXM","DXN", -"DXO","DXP","DXQ","DXR","DXS","DXT","DXU","DXV","DXW","DXX","DXY","DXZ","DYA","DYB","DYC","DYD", -"DYE","DYF","DYG","DYH","DYI","DYJ","DYK","DYL","DYM","DYN","DYO","DYP","DYQ","DYR","DYS","DYT", -"DYU","DYV","DYW","DYX","DYY","DYZ","DZA","DZB","DZC","DZD","DZE","DZF","DZG","DZH","DZI","DZJ", -"DZK","DZL","DZM","DZN","DZO","DZP","DZQ","DZR","DZS","DZT","DZU","DZV","DZW","DZX","DZY","DZZ", - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - E-starteds are intentionally omitted -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -"FAA","FAB","FAC","FAD","FAE","FAF","FAG","FAH","FAI","FAJ","FAK","FAL", -"FAM","FAN","FAO","FAP","FAQ","FAR","FAS","FAT","FAU","FAV","FAW","FAX","FAY","FAZ","FBA","FBB", -"FBC","FBD","FBE","FBF","FBG","FBH","FBI","FBJ","FBK","FBL","FBM","FBN","FBO","FBP","FBQ","FBR", -"FBS","FBT","FBU","FBV","FBW","FBX","FBY","FBZ","FCA","FCB","FCC","FCD","FCE","FCF","FCG","FCH", -"FCI","FCJ","FCK","FCL","FCM","FCN","FCO","FCP","FCQ","FCR","FCS","FCT","FCU","FCV","FCW","FCX", -"FCY","FCZ","FDA","FDB","FDC","FDD","FDE","FDF","FDG","FDH","FDI","FDJ","FDK","FDL","FDM","FDN", -"FDO","FDP","FDQ","FDR","FDS","FDT","FDU","FDV","FDW","FDX","FDY","FDZ","FEA","FEB","FEC","FED", -"FEE","FEF","FEG","FEH","FEI","FEJ","FEK","FEL","FEM","FEN","FEO","FEP","FEQ","FER","FES","FET", -"FEU","FEV","FEW","FEX","FEY","FEZ","FFA","FFB","FFC","FFD","FFE","FFF","FFG","FFH","FFI","FFJ", -"FFK","FFL","FFM","FFN","FFO","FFP","FFQ","FFR","FFS","FFT","FFU","FFV","FFW","FFX","FFY","FFZ", -"FGA","FGB","FGC","FGD","FGE","FGF","FGG","FGH","FGI","FGJ","FGK","FGL","FGM","FGN","FGO","FGP", -"FGQ","FGR","FGS","FGT","FGU","FGV","FGW","FGX","FGY","FGZ","FHA","FHB","FHC","FHD","FHE","FHF", -"FHG","FHH","FHI","FHJ","FHK","FHL","FHM","FHN","FHO","FHP","FHQ","FHR","FHS","FHT","FHU","FHV", -"FHW","FHX","FHY","FHZ","FIA","FIB","FIC","FID","FIE","FIF","FIG","FIH","FII","FIJ","FIK","FIL", -"FIM","FIN","FIO","FIP","FIQ","FIR","FIS","FIT","FIU","FIV","FIW","FIX","FIY","FIZ","FJA","FJB", -"FJC","FJD","FJE","FJF","FJG","FJH","FJI","FJJ","FJK","FJL","FJM","FJN","FJO","FJP","FJQ","FJR", -"FJS","FJT","FJU","FJV","FJW","FJX","FJY","FJZ","FKA","FKB","FKC","FKD","FKE","FKF","FKG","FKH", -"FKI","FKJ","FKK","FKL","FKM","FKN","FKO","FKP","FKQ","FKR","FKS","FKT","FKU","FKV","FKW","FKX", -"FKY","FKZ","FLA","FLB","FLC","FLD","FLE","FLF","FLG","FLH","FLI","FLJ","FLK","FLL","FLM","FLN", -"FLO","FLP","FLQ","FLR","FLS","FLT","FLU","FLV","FLW","FLX","FLY","FLZ","FMA","FMB","FMC","FMD", -"FME","FMF","FMG","FMH","FMI","FMJ","FMK","FML","FMM","FMN","FMO","FMP","FMQ","FMR","FMS","FMT", -"FMU","FMV","FMW","FMX","FMY","FMZ","FNA","FNB","FNC","FND","FNE","FNF","FNG","FNH","FNI","FNJ", -"FNK","FNL","FNM","FNN","FNO","FNP","FNQ","FNR","FNS","FNT","FNU","FNV","FNW","FNX","FNY","FNZ", -"FOA","FOB","FOC","FOD","FOE","FOF","FOG","FOH","FOI","FOJ","FOK","FOL","FOM","FON","FOO","FOP", -"FOQ","FOR","FOS","FOT","FOU","FOV","FOW","FOX","FOY","FOZ","FPA","FPB","FPC","FPD","FPE","FPF", -"FPG","FPH","FPI","FPJ","FPK","FPL","FPM","FPN","FPO","FPP","FPQ","FPR","FPS","FPT","FPU","FPV", -"FPW","FPX","FPY","FPZ","FQA","FQB","FQC","FQD","FQE","FQF","FQG","FQH","FQI","FQJ","FQK","FQL", -"FQM","FQN","FQO","FQP","FQQ","FQR","FQS","FQT","FQU","FQV","FQW","FQX","FQY","FQZ","FRA","FRB", -"FRC","FRD","FRE","FRF","FRG","FRH","FRI","FRJ","FRK","FRL","FRM","FRN","FRO","FRP","FRQ","FRR", -"FRS","FRT","FRU","FRV","FRW","FRX","FRY","FRZ","FSA","FSB","FSC","FSD","FSE","FSF","FSG","FSH", -"FSI","FSJ","FSK","FSL","FSM","FSN","FSO","FSP","FSQ","FSR","FSS","FST","FSU","FSV","FSW","FSX", -"FSY","FSZ","FTA","FTB","FTC","FTD","FTE","FTF","FTG","FTH","FTI","FTJ","FTK","FTL","FTM","FTN", -"FTO","FTP","FTQ","FTR","FTS","FTT","FTU","FTV","FTW","FTX","FTY","FTZ","FUA","FUB","FUC","FUD", -"FUE","FUF","FUG","FUH","FUI","FUJ","FUK","FUL","FUM","FUN","FUO","FUP","FUQ","FUR","FUS","FUT", -"FUU","FUV","FUW","FUX","FUY","FUZ","FVA","FVB","FVC","FVD","FVE","FVF","FVG","FVH","FVI","FVJ", -"FVK","FVL","FVM","FVN","FVO","FVP","FVQ","FVR","FVS","FVT","FVU","FVV","FVW","FVX","FVY","FVZ", -"FWA","FWB","FWC","FWD","FWE","FWF","FWG","FWH","FWI","FWJ","FWK","FWL","FWM","FWN","FWO","FWP", -"FWQ","FWR","FWS","FWT","FWU","FWV","FWW","FWX","FWY","FWZ","FXA","FXB","FXC","FXD","FXE","FXF", -"FXG","FXH","FXI","FXJ","FXK","FXL","FXM","FXN","FXO","FXP","FXQ","FXR","FXS","FXT","FXU","FXV", -"FXW","FXX","FXY","FXZ","FYA","FYB","FYC","FYD","FYE","FYF","FYG","FYH","FYI","FYJ","FYK","FYL", -"FYM","FYN","FYO","FYP","FYQ","FYR","FYS","FYT","FYU","FYV","FYW","FYX","FYY","FYZ","FZA","FZB", -"FZC","FZD","FZE","FZF","FZG","FZH","FZI","FZJ","FZK","FZL","FZM","FZN","FZO","FZP","FZQ","FZR", -"FZS","FZT","FZU","FZV","FZW","FZX","FZY","FZZ","GAA","GAB","GAC","GAD","GAE","GAF","GAG","GAH", -"GAI","GAJ","GAK","GAL","GAM","GAN","GAO","GAP","GAQ","GAR","GAS","GAT","GAU","GAV","GAW","GAX", -"GAY","GAZ","GBA","GBB","GBC","GBD","GBE","GBF","GBG","GBH","GBI","GBJ","GBK","GBL","GBM","GBN", -"GBO","GBP","GBQ","GBR","GBS","GBT","GBU","GBV","GBW","GBX","GBY","GBZ","GCA","GCB","GCC","GCD", -"GCE","GCF","GCG","GCH","GCI","GCJ","GCK","GCL","GCM","GCN","GCO","GCP","GCQ","GCR","GCS","GCT", -"GCU","GCV","GCW","GCX","GCY","GCZ","GDA","GDB","GDC","GDD","GDE","GDF","GDG","GDH","GDI","GDJ", -"GDK","GDL","GDM","GDN","GDO","GDP","GDQ","GDR","GDS","GDT","GDU","GDV","GDW","GDX","GDY","GDZ", -"GEA","GEB","GEC","GED","GEE","GEF","GEG","GEH","GEI","GEJ","GEK","GEL","GEM","GEN","GEO","GEP", -"GEQ","GER","GES","GET","GEU","GEV","GEW","GEX","GEY","GEZ","GFA","GFB","GFC","GFD","GFE","GFF", -"GFG","GFH","GFI","GFJ","GFK","GFL","GFM","GFN","GFO","GFP","GFQ","GFR","GFS","GFT","GFU","GFV", -"GFW","GFX","GFY","GFZ","GGA","GGB","GGC","GGD","GGE","GGF","GGG","GGH","GGI","GGJ","GGK","GGL", -"GGM","GGN","GGO","GGP","GGQ","GGR","GGS","GGT","GGU","GGV","GGW","GGX","GGY","GGZ","GHA","GHB", -"GHC","GHD","GHE","GHF","GHG","GHH","GHI","GHJ","GHK","GHL","GHM","GHN","GHO","GHP","GHQ","GHR", -"GHS","GHT","GHU","GHV","GHW","GHX","GHY","GHZ","GIA","GIB","GIC","GID","GIE","GIF","GIG","GIH", -"GII","GIJ","GIK","GIL","GIM","GIN","GIO","GIP","GIQ","GIR","GIS","GIT","GIU","GIV","GIW","GIX", -"GIY","GIZ","GJA","GJB","GJC","GJD","GJE","GJF","GJG","GJH","GJI","GJJ","GJK","GJL","GJM","GJN", -"GJO","GJP","GJQ","GJR","GJS","GJT","GJU","GJV","GJW","GJX","GJY","GJZ","GKA","GKB","GKC","GKD", -"GKE","GKF","GKG","GKH","GKI","GKJ","GKK","GKL","GKM","GKN","GKO","GKP","GKQ","GKR","GKS","GKT", -"GKU","GKV","GKW","GKX","GKY","GKZ","GLA","GLB","GLC","GLD","GLE","GLF","GLG","GLH","GLI","GLJ", -"GLK","GLL","GLM","GLN","GLO","GLP","GLQ","GLR","GLS","GLT","GLU","GLV","GLW","GLX","GLY","GLZ", -"GMA","GMB","GMC","GMD","GME","GMF","GMG","GMH","GMI","GMJ","GMK","GML","GMM","GMN","GMO","GMP", -"GMQ","GMR","GMS","GMT","GMU","GMV","GMW","GMX","GMY","GMZ","GNA","GNB","GNC","GND","GNE","GNF", -"GNG","GNH","GNI","GNJ","GNK","GNL","GNM","GNN","GNO","GNP","GNQ","GNR","GNS","GNT","GNU","GNV", -"GNW","GNX","GNY","GNZ","GOA","GOB","GOC","GOD","GOE","GOF","GOG","GOH","GOI","GOJ","GOK","GOL", -"GOM","GON","GOO","GOP","GOQ","GOR","GOS","GOT","GOU","GOV","GOW","GOX","GOY","GOZ","GPA","GPB", -"GPC","GPD","GPE","GPF","GPG","GPH","GPI","GPJ","GPK","GPL","GPM","GPN","GPO","GPP","GPQ","GPR", -"GPS","GPT","GPU","GPV","GPW","GPX","GPY","GPZ","GQA","GQB","GQC","GQD","GQE","GQF","GQG","GQH", -"GQI","GQJ","GQK","GQL","GQM","GQN","GQO","GQP","GQQ","GQR","GQS","GQT","GQU","GQV","GQW","GQX", -"GQY","GQZ","GRA","GRB","GRC","GRD","GRE","GRF","GRG","GRH","GRI","GRJ","GRK","GRL","GRM","GRN", -"GRO","GRP","GRQ","GRR","GRS","GRT","GRU","GRV","GRW","GRX","GRY","GRZ","GSA","GSB","GSC","GSD", -"GSE","GSF","GSG","GSH","GSI","GSJ","GSK","GSL","GSM","GSN","GSO","GSP","GSQ","GSR","GSS","GST", -"GSU","GSV","GSW","GSX","GSY","GSZ","GTA","GTB","GTC","GTD","GTE","GTF","GTG","GTH","GTI","GTJ", -"GTK","GTL","GTM","GTN","GTO","GTP","GTQ","GTR","GTS","GTT","GTU","GTV","GTW","GTX","GTY","GTZ", -"GUA","GUB","GUC","GUD","GUE","GUF","GUG","GUH","GUI","GUJ","GUK","GUL","GUM","GUN","GUO","GUP", -"GUQ","GUR","GUS","GUT","GUU","GUV","GUW","GUX","GUY","GUZ","GVA","GVB","GVC","GVD","GVE","GVF", -"GVG","GVH","GVI","GVJ","GVK","GVL","GVM","GVN","GVO","GVP","GVQ","GVR","GVS","GVT","GVU","GVV", -"GVW","GVX","GVY","GVZ","GWA","GWB","GWC","GWD","GWE","GWF","GWG","GWH","GWI","GWJ","GWK","GWL", -"GWM","GWN","GWO","GWP","GWQ","GWR","GWS","GWT","GWU","GWV","GWW","GWX","GWY","GWZ","GXA","GXB", -"GXC","GXD","GXE","GXF","GXG","GXH","GXI","GXJ","GXK","GXL","GXM","GXN","GXO","GXP","GXQ","GXR", -"GXS","GXT","GXU","GXV","GXW","GXX","GXY","GXZ","GYA","GYB","GYC","GYD","GYE","GYF","GYG","GYH", -"GYI","GYJ","GYK","GYL","GYM","GYN","GYO","GYP","GYQ","GYR","GYS","GYT","GYU","GYV","GYW","GYX", -"GYY","GYZ","GZA","GZB","GZC","GZD","GZE","GZF","GZG","GZH","GZI","GZJ","GZK","GZL","GZM","GZN", -"GZO","GZP","GZQ","GZR","GZS","GZT","GZU","GZV","GZW","GZX","GZY","GZZ","HAA","HAB","HAC","HAD", -"HAE","HAF","HAG","HAH","HAI","HAJ","HAK","HAL","HAM","HAN","HAO","HAP","HAQ","HAR","HAS","HAT", -"HAU","HAV","HAW","HAX","HAY","HAZ","HBA","HBB","HBC","HBD","HBE","HBF","HBG","HBH","HBI","HBJ", -"HBK","HBL","HBM","HBN","HBO","HBP","HBQ","HBR","HBS","HBT","HBU","HBV","HBW","HBX","HBY","HBZ", -"HCA","HCB","HCC","HCD","HCE","HCF","HCG","HCH","HCI","HCJ","HCK","HCL","HCM","HCN","HCO","HCP", -"HCQ","HCR","HCS","HCT","HCU","HCV","HCW","HCX","HCY","HCZ","HDA","HDB","HDC","HDD","HDE","HDF", -"HDG","HDH","HDI","HDJ","HDK","HDL","HDM","HDN","HDO","HDP","HDQ","HDR","HDS","HDT","HDU","HDV", -"HDW","HDX","HDY","HDZ","HEA","HEB","HEC","HED","HEE","HEF","HEG","HEH","HEI","HEJ","HEK","HEL", -"HEM","HEN","HEO","HEP","HEQ","HER","HES","HET","HEU","HEV","HEW","HEX","HEY","HEZ","HFA","HFB", -"HFC","HFD","HFE","HFF","HFG","HFH","HFI","HFJ","HFK","HFL","HFM","HFN","HFO","HFP","HFQ","HFR", -"HFS","HFT","HFU","HFV","HFW","HFX","HFY","HFZ","HGA","HGB","HGC","HGD","HGE","HGF","HGG","HGH", -"HGI","HGJ","HGK","HGL","HGM","HGN","HGO","HGP","HGQ","HGR","HGS","HGT","HGU","HGV","HGW","HGX", -"HGY","HGZ","HHA","HHB","HHC","HHD","HHE","HHF","HHG","HHH","HHI","HHJ","HHK","HHL","HHM","HHN", -"HHO","HHP","HHQ","HHR","HHS","HHT","HHU","HHV","HHW","HHX","HHY","HHZ","HIA","HIB","HIC","HID", -"HIE","HIF","HIG","HIH","HII","HIJ","HIK","HIL","HIM","HIN","HIO","HIP","HIQ","HIR","HIS","HIT", -"HIU","HIV","HIW","HIX","HIY","HIZ","HJA","HJB","HJC","HJD","HJE","HJF","HJG","HJH","HJI","HJJ", -"HJK","HJL","HJM","HJN","HJO","HJP","HJQ","HJR","HJS","HJT","HJU","HJV","HJW","HJX","HJY","HJZ", -"HKA","HKB","HKC","HKD","HKE","HKF","HKG","HKH","HKI","HKJ","HKK","HKL","HKM","HKN","HKO","HKP", -"HKQ","HKR","HKS","HKT","HKU","HKV","HKW","HKX","HKY","HKZ","HLA","HLB","HLC","HLD","HLE","HLF", -"HLG","HLH","HLI","HLJ","HLK","HLL","HLM","HLN","HLO","HLP","HLQ","HLR","HLS","HLT","HLU","HLV", -"HLW","HLX","HLY","HLZ","HMA","HMB","HMC","HMD","HME","HMF","HMG","HMH","HMI","HMJ","HMK","HML", -"HMM","HMN","HMO","HMP","HMQ","HMR","HMS","HMT","HMU","HMV","HMW","HMX","HMY","HMZ","HNA","HNB", -"HNC","HND","HNE","HNF","HNG","HNH","HNI","HNJ","HNK","HNL","HNM","HNN","HNO","HNP","HNQ","HNR", -"HNS","HNT","HNU","HNV","HNW","HNX","HNY","HNZ","HOA","HOB","HOC","HOD","HOE","HOF","HOG","HOH", -"HOI","HOJ","HOK","HOL","HOM","HON","HOO","HOP","HOQ","HOR","HOS","HOT","HOU","HOV","HOW","HOX", -"HOY","HOZ","HPA","HPB","HPC","HPD","HPE","HPF","HPG","HPH","HPI","HPJ","HPK","HPL","HPM","HPN", -"HPO","HPP","HPQ","HPR","HPS","HPT","HPU","HPV","HPW","HPX","HPY","HPZ","HQA","HQB","HQC","HQD", -"HQE","HQF","HQG","HQH","HQI","HQJ","HQK","HQL","HQM","HQN","HQO","HQP","HQQ","HQR","HQS","HQT", -"HQU","HQV","HQW","HQX","HQY","HQZ","HRA","HRB","HRC","HRD","HRE","HRF","HRG","HRH","HRI","HRJ", -"HRK","HRL","HRM","HRN","HRO","HRP","HRQ","HRR","HRS","HRT","HRU","HRV","HRW","HRX","HRY","HRZ", -"HSA","HSB","HSC","HSD","HSE","HSF","HSG","HSH","HSI","HSJ","HSK","HSL","HSM","HSN","HSO","HSP", -"HSQ","HSR","HSS","HST","HSU","HSV","HSW","HSX","HSY","HSZ","HTA","HTB","HTC","HTD","HTE","HTF", -"HTG","HTH","HTI","HTJ","HTK","HTL","HTM","HTN","HTO","HTP","HTQ","HTR","HTS","HTT","HTU","HTV", -"HTW","HTX","HTY","HTZ","HUA","HUB","HUC","HUD","HUE","HUF","HUG","HUH","HUI","HUJ","HUK","HUL", -"HUM","HUN","HUO","HUP","HUQ","HUR","HUS","HUT","HUU","HUV","HUW","HUX","HUY","HUZ","HVA","HVB", -"HVC","HVD","HVE","HVF","HVG","HVH","HVI","HVJ","HVK","HVL","HVM","HVN","HVO","HVP","HVQ","HVR", -"HVS","HVT","HVU","HVV","HVW","HVX","HVY","HVZ","HWA","HWB","HWC","HWD","HWE","HWF","HWG","HWH", -"HWI","HWJ","HWK","HWL","HWM","HWN","HWO","HWP","HWQ","HWR","HWS","HWT","HWU","HWV","HWW","HWX", -"HWY","HWZ","HXA","HXB","HXC","HXD","HXE","HXF","HXG","HXH","HXI","HXJ","HXK","HXL","HXM","HXN", -"HXO","HXP","HXQ","HXR","HXS","HXT","HXU","HXV","HXW","HXX","HXY","HXZ","HYA","HYB","HYC","HYD", -"HYE","HYF","HYG","HYH","HYI","HYJ","HYK","HYL","HYM","HYN","HYO","HYP","HYQ","HYR","HYS","HYT", -"HYU","HYV","HYW","HYX","HYY","HYZ","HZA","HZB","HZC","HZD","HZE","HZF","HZG","HZH","HZI","HZJ", -"HZK","HZL","HZM","HZN","HZO","HZP","HZQ","HZR","HZS","HZT","HZU","HZV","HZW","HZX","HZY","HZZ", -"IAA","IAB","IAC","IAD","IAE","IAF","IAG","IAH","IAI","IAJ","IAK","IAL","IAM","IAN","IAO","IAP", -"IAQ","IAR","IAS","IAT","IAU","IAV","IAW","IAX","IAY","IAZ","IBA","IBB","IBC","IBD","IBE","IBF", -"IBG","IBH","IBI","IBJ","IBK","IBL","IBM","IBN","IBO","IBP","IBQ","IBR","IBS","IBT","IBU","IBV", -"IBW","IBX","IBY","IBZ","ICA","ICB","ICC","ICD","ICE","ICF","ICG","ICH","ICI","ICJ","ICK","ICL", -"ICM","ICN","ICO","ICP","ICQ","ICR","ICS","ICT","ICU","ICV","ICW","ICX","ICY","ICZ","IDA","IDB", -"IDC","IDD","IDE","IDF","IDG","IDH","IDI","IDJ","IDK","IDL","IDM","IDN","IDO","IDP","IDQ","IDR", -"IDS","IDT","IDU","IDV","IDW","IDX","IDY","IDZ","IEA","IEB","IEC","IED","IEE","IEF","IEG","IEH", -"IEI","IEJ","IEK","IEL","IEM","IEN","IEO","IEP","IEQ","IER","IES","IET","IEU","IEV","IEW","IEX", -"IEY","IEZ","IFA","IFB","IFC","IFD","IFE","IFF","IFG","IFH","IFI","IFJ","IFK","IFL","IFM","IFN", -"IFO","IFP","IFQ","IFR","IFS","IFT","IFU","IFV","IFW","IFX","IFY","IFZ","IGA","IGB","IGC","IGD", -"IGE","IGF","IGG","IGH","IGI","IGJ","IGK","IGL","IGM","IGN","IGO","IGP","IGQ","IGR","IGS","IGT", -"IGU","IGV","IGW","IGX","IGY","IGZ","IHA","IHB","IHC","IHD","IHE","IHF","IHG","IHH","IHI","IHJ", -"IHK","IHL","IHM","IHN","IHO","IHP","IHQ","IHR","IHS","IHT","IHU","IHV","IHW","IHX","IHY","IHZ", -"IIA","IIB","IIC","IID","IIE","IIF","IIG","IIH","III","IIJ","IIK","IIL","IIM","IIN","IIO","IIP", -"IIQ","IIR","IIS","IIT","IIU","IIV","IIW","IIX","IIY","IIZ","IJA","IJB","IJC","IJD","IJE","IJF", -"IJG","IJH","IJI","IJJ","IJK","IJL","IJM","IJN","IJO","IJP","IJQ","IJR","IJS","IJT","IJU","IJV", -"IJW","IJX","IJY","IJZ","IKA","IKB","IKC","IKD","IKE","IKF","IKG","IKH","IKI","IKJ","IKK","IKL", -"IKM","IKN","IKO","IKP","IKQ","IKR","IKS","IKT","IKU","IKV","IKW","IKX","IKY","IKZ","ILA","ILB", -"ILC","ILD","ILE","ILF","ILG","ILH","ILI","ILJ","ILK","ILL","ILM","ILN","ILO","ILP","ILQ","ILR", -"ILS","ILT","ILU","ILV","ILW","ILX","ILY","ILZ","IMA","IMB","IMC","IMD","IME","IMF","IMG","IMH", -"IMI","IMJ","IMK","IML","IMM","IMN","IMO","IMP","IMQ","IMR","IMS","IMT","IMU","IMV","IMW","IMX", -"IMY","IMZ","INA","INB","INC","IND","INE","INF","ING","INH","INI","INJ","INK","INL","INM","INN", -"INO","INP","INQ","INR","INS","INT","INU","INV","INW","INX","INY","INZ","IOA","IOB","IOC","IOD", -"IOE","IOF","IOG","IOH","IOI","IOJ","IOK","IOL","IOM","ION","IOO","IOP","IOQ","IOR","IOS","IOT", -"IOU","IOV","IOW","IOX","IOY","IOZ","IPA","IPB","IPC","IPD","IPE","IPF","IPG","IPH","IPI","IPJ", -"IPK","IPL","IPM","IPN","IPO","IPP","IPQ","IPR","IPS","IPT","IPU","IPV","IPW","IPX","IPY","IPZ", -"IQA","IQB","IQC","IQD","IQE","IQF","IQG","IQH","IQI","IQJ","IQK","IQL","IQM","IQN","IQO","IQP", -"IQQ","IQR","IQS","IQT","IQU","IQV","IQW","IQX","IQY","IQZ","IRA","IRB","IRC","IRD","IRE","IRF", -"IRG","IRH","IRI","IRJ","IRK","IRL","IRM","IRN","IRO","IRP","IRQ","IRR","IRS","IRT","IRU","IRV", -"IRW","IRX","IRY","IRZ","ISA","ISB","ISC","ISD","ISE","ISF","ISG","ISH","ISI","ISJ","ISK","ISL", -"ISM","ISN","ISO","ISP","ISQ","ISR","ISS","IST","ISU","ISV","ISW","ISX","ISY","ISZ","ITA","ITB", -"ITC","ITD","ITE","ITF","ITG","ITH","ITI","ITJ","ITK","ITL","ITM","ITN","ITO","ITP","ITQ","ITR", -"ITS","ITT","ITU","ITV","ITW","ITX","ITY","ITZ","IUA","IUB","IUC","IUD","IUE","IUF","IUG","IUH", -"IUI","IUJ","IUK","IUL","IUM","IUN","IUO","IUP","IUQ","IUR","IUS","IUT","IUU","IUV","IUW","IUX", -"IUY","IUZ","IVA","IVB","IVC","IVD","IVE","IVF","IVG","IVH","IVI","IVJ","IVK","IVL","IVM","IVN", -"IVO","IVP","IVQ","IVR","IVS","IVT","IVU","IVV","IVW","IVX","IVY","IVZ","IWA","IWB","IWC","IWD", -"IWE","IWF","IWG","IWH","IWI","IWJ","IWK","IWL","IWM","IWN","IWO","IWP","IWQ","IWR","IWS","IWT", -"IWU","IWV","IWW","IWX","IWY","IWZ","IXA","IXB","IXC","IXD","IXE","IXF","IXG","IXH","IXI","IXJ", -"IXK","IXL","IXM","IXN","IXO","IXP","IXQ","IXR","IXS","IXT","IXU","IXV","IXW","IXX","IXY","IXZ", -"IYA","IYB","IYC","IYD","IYE","IYF","IYG","IYH","IYI","IYJ","IYK","IYL","IYM","IYN","IYO","IYP", -"IYQ","IYR","IYS","IYT","IYU","IYV","IYW","IYX","IYY","IYZ","IZA","IZB","IZC","IZD","IZE","IZF", -"IZG","IZH","IZI","IZJ","IZK","IZL","IZM","IZN","IZO","IZP","IZQ","IZR","IZS","IZT","IZU","IZV", -"IZW","IZX","IZY","IZZ","JAA","JAB","JAC","JAD","JAE","JAF","JAG","JAH","JAI","JAJ","JAK","JAL", -"JAM","JAN","JAO","JAP","JAQ","JAR","JAS","JAT","JAU","JAV","JAW","JAX","JAY","JAZ","JBA","JBB", -"JBC","JBD","JBE","JBF","JBG","JBH","JBI","JBJ","JBK","JBL","JBM","JBN","JBO","JBP","JBQ","JBR", -"JBS","JBT","JBU","JBV","JBW","JBX","JBY","JBZ","JCA","JCB","JCC","JCD","JCE","JCF","JCG","JCH", -"JCI","JCJ","JCK","JCL","JCM","JCN","JCO","JCP","JCQ","JCR","JCS","JCT","JCU","JCV","JCW","JCX", -"JCY","JCZ","JDA","JDB","JDC","JDD","JDE","JDF","JDG","JDH","JDI","JDJ","JDK","JDL","JDM","JDN", -"JDO","JDP","JDQ","JDR","JDS","JDT","JDU","JDV","JDW","JDX","JDY","JDZ","JEA","JEB","JEC","JED", -"JEE","JEF","JEG","JEH","JEI","JEJ","JEK","JEL","JEM","JEN","JEO","JEP","JEQ","JER","JES","JET", -"JEU","JEV","JEW","JEX","JEY","JEZ","JFA","JFB","JFC","JFD","JFE","JFF","JFG","JFH","JFI","JFJ", -"JFK","JFL","JFM","JFN","JFO","JFP","JFQ","JFR","JFS","JFT","JFU","JFV","JFW","JFX","JFY","JFZ", -"JGA","JGB","JGC","JGD","JGE","JGF","JGG","JGH","JGI","JGJ","JGK","JGL","JGM","JGN","JGO","JGP", -"JGQ","JGR","JGS","JGT","JGU","JGV","JGW","JGX","JGY","JGZ","JHA","JHB","JHC","JHD","JHE","JHF", -"JHG","JHH","JHI","JHJ","JHK","JHL","JHM","JHN","JHO","JHP","JHQ","JHR","JHS","JHT","JHU","JHV", -"JHW","JHX","JHY","JHZ","JIA","JIB","JIC","JID","JIE","JIF","JIG","JIH","JII","JIJ","JIK","JIL", -"JIM","JIN","JIO","JIP","JIQ","JIR","JIS","JIT","JIU","JIV","JIW","JIX","JIY","JIZ","JJA","JJB", -"JJC","JJD","JJE","JJF","JJG","JJH","JJI","JJJ","JJK","JJL","JJM","JJN","JJO","JJP","JJQ","JJR", -"JJS","JJT","JJU","JJV","JJW","JJX","JJY","JJZ","JKA","JKB","JKC","JKD","JKE","JKF","JKG","JKH", -"JKI","JKJ","JKK","JKL","JKM","JKN","JKO","JKP","JKQ","JKR","JKS","JKT","JKU","JKV","JKW","JKX", -"JKY","JKZ","JLA","JLB","JLC","JLD","JLE","JLF","JLG","JLH","JLI","JLJ","JLK","JLL","JLM","JLN", -"JLO","JLP","JLQ","JLR","JLS","JLT","JLU","JLV","JLW","JLX","JLY","JLZ","JMA","JMB","JMC","JMD", -"JME","JMF","JMG","JMH","JMI","JMJ","JMK","JML","JMM","JMN","JMO","JMP","JMQ","JMR","JMS","JMT", -"JMU","JMV","JMW","JMX","JMY","JMZ","JNA","JNB","JNC","JND","JNE","JNF","JNG","JNH","JNI","JNJ", -"JNK","JNL","JNM","JNN","JNO","JNP","JNQ","JNR","JNS","JNT","JNU","JNV","JNW","JNX","JNY","JNZ", -"JOA","JOB","JOC","JOD","JOE","JOF","JOG","JOH","JOI","JOJ","JOK","JOL","JOM","JON","JOO","JOP", -"JOQ","JOR","JOS","JOT","JOU","JOV","JOW","JOX","JOY","JOZ","JPA","JPB","JPC","JPD","JPE","JPF", -"JPG","JPH","JPI","JPJ","JPK","JPL","JPM","JPN","JPO","JPP","JPQ","JPR","JPS","JPT","JPU","JPV", -"JPW","JPX","JPY","JPZ","JQA","JQB","JQC","JQD","JQE","JQF","JQG","JQH","JQI","JQJ","JQK","JQL", -"JQM","JQN","JQO","JQP","JQQ","JQR","JQS","JQT","JQU","JQV","JQW","JQX","JQY","JQZ","JRA","JRB", -"JRC","JRD","JRE","JRF","JRG","JRH","JRI","JRJ","JRK","JRL","JRM","JRN","JRO","JRP","JRQ","JRR", -"JRS","JRT","JRU","JRV","JRW","JRX","JRY","JRZ","JSA","JSB","JSC","JSD","JSE","JSF","JSG","JSH", -"JSI","JSJ","JSK","JSL","JSM","JSN","JSO","JSP","JSQ","JSR","JSS","JST","JSU","JSV","JSW","JSX", -"JSY","JSZ","JTA","JTB","JTC","JTD","JTE","JTF","JTG","JTH","JTI","JTJ","JTK","JTL","JTM","JTN", -"JTO","JTP","JTQ","JTR","JTS","JTT","JTU","JTV","JTW","JTX","JTY","JTZ","JUA","JUB","JUC","JUD", -"JUE","JUF","JUG","JUH","JUI","JUJ","JUK","JUL","JUM","JUN","JUO","JUP","JUQ","JUR","JUS","JUT", -"JUU","JUV","JUW","JUX","JUY","JUZ","JVA","JVB","JVC","JVD","JVE","JVF","JVG","JVH","JVI","JVJ", -"JVK","JVL","JVM","JVN","JVO","JVP","JVQ","JVR","JVS","JVT","JVU","JVV","JVW","JVX","JVY","JVZ", -"JWA","JWB","JWC","JWD","JWE","JWF","JWG","JWH","JWI","JWJ","JWK","JWL","JWM","JWN","JWO","JWP", -"JWQ","JWR","JWS","JWT","JWU","JWV","JWW","JWX","JWY","JWZ","JXA","JXB","JXC","JXD","JXE","JXF", -"JXG","JXH","JXI","JXJ","JXK","JXL","JXM","JXN","JXO","JXP","JXQ","JXR","JXS","JXT","JXU","JXV", -"JXW","JXX","JXY","JXZ","JYA","JYB","JYC","JYD","JYE","JYF","JYG","JYH","JYI","JYJ","JYK","JYL", -"JYM","JYN","JYO","JYP","JYQ","JYR","JYS","JYT","JYU","JYV","JYW","JYX","JYY","JYZ","JZA","JZB", -"JZC","JZD","JZE","JZF","JZG","JZH","JZI","JZJ","JZK","JZL","JZM","JZN","JZO","JZP","JZQ","JZR", -"JZS","JZT","JZU","JZV","JZW","JZX","JZY","JZZ","KAA","KAB","KAC","KAD","KAE","KAF","KAG","KAH", -"KAI","KAJ","KAK","KAL","KAM","KAN","KAO","KAP","KAQ","KAR","KAS","KAT","KAU","KAV","KAW","KAX", -"KAY","KAZ","KBA","KBB","KBC","KBD","KBE","KBF","KBG","KBH","KBI","KBJ","KBK","KBL","KBM","KBN", -"KBO","KBP","KBQ","KBR","KBS","KBT","KBU","KBV","KBW","KBX","KBY","KBZ","KCA","KCB","KCC","KCD", -"KCE","KCF","KCG","KCH","KCI","KCJ","KCK","KCL","KCM","KCN","KCO","KCP","KCQ","KCR","KCS","KCT", -"KCU","KCV","KCW","KCX","KCY","KCZ","KDA","KDB","KDC","KDD","KDE","KDF","KDG","KDH","KDI","KDJ", -"KDK","KDL","KDM","KDN","KDO","KDP","KDQ","KDR","KDS","KDT","KDU","KDV","KDW","KDX","KDY","KDZ", -"KEA","KEB","KEC","KED","KEE","KEF","KEG","KEH","KEI","KEJ","KEK","KEL","KEM","KEN","KEO","KEP", -"KEQ","KER","KES","KET","KEU","KEV","KEW","KEX","KEY","KEZ","KFA","KFB","KFC","KFD","KFE","KFF", -"KFG","KFH","KFI","KFJ","KFK","KFL","KFM","KFN","KFO","KFP","KFQ","KFR","KFS","KFT","KFU","KFV", -"KFW","KFX","KFY","KFZ","KGA","KGB","KGC","KGD","KGE","KGF","KGG","KGH","KGI","KGJ","KGK","KGL", -"KGM","KGN","KGO","KGP","KGQ","KGR","KGS","KGT","KGU","KGV","KGW","KGX","KGY","KGZ","KHA","KHB", -"KHC","KHD","KHE","KHF","KHG","KHH","KHI","KHJ","KHK","KHL","KHM","KHN","KHO","KHP","KHQ","KHR", -"KHS","KHT","KHU","KHV","KHW","KHX","KHY","KHZ","KIA","KIB","KIC","KID","KIE","KIF","KIG","KIH", -"KII","KIJ","KIK","KIL","KIM","KIN","KIO","KIP","KIQ","KIR","KIS","KIT","KIU","KIV","KIW","KIX", -"KIY","KIZ","KJA","KJB","KJC","KJD","KJE","KJF","KJG","KJH","KJI","KJJ","KJK","KJL","KJM","KJN", -"KJO","KJP","KJQ","KJR","KJS","KJT","KJU","KJV","KJW","KJX","KJY","KJZ","KKA","KKB","KKC","KKD", -"KKE","KKF","KKG","KKH","KKI","KKJ","KKK","KKL","KKM","KKN","KKO","KKP","KKQ","KKR","KKS","KKT", -"KKU","KKV","KKW","KKX","KKY","KKZ","KLA","KLB","KLC","KLD","KLE","KLF","KLG","KLH","KLI","KLJ", -"KLK","KLL","KLM","KLN","KLO","KLP","KLQ","KLR","KLS","KLT","KLU","KLV","KLW","KLX","KLY","KLZ", -"KMA","KMB","KMC","KMD","KME","KMF","KMG","KMH","KMI","KMJ","KMK","KML","KMM","KMN","KMO","KMP", -"KMQ","KMR","KMS","KMT","KMU","KMV","KMW","KMX","KMY","KMZ","KNA","KNB","KNC","KND","KNE","KNF", -"KNG","KNH","KNI","KNJ","KNK","KNL","KNM","KNN","KNO","KNP","KNQ","KNR","KNS","KNT","KNU","KNV", -"KNW","KNX","KNY","KNZ","KOA","KOB","KOC","KOD","KOE","KOF","KOG","KOH","KOI","KOJ","KOK","KOL", -"KOM","KON","KOO","KOP","KOQ","KOR","KOS","KOT","KOU","KOV","KOW","KOX","KOY","KOZ","KPA","KPB", -"KPC","KPD","KPE","KPF","KPG","KPH","KPI","KPJ","KPK","KPL","KPM","KPN","KPO","KPP","KPQ","KPR", -"KPS","KPT","KPU","KPV","KPW","KPX","KPY","KPZ","KQA","KQB","KQC","KQD","KQE","KQF","KQG","KQH", -"KQI","KQJ","KQK","KQL","KQM","KQN","KQO","KQP","KQQ","KQR","KQS","KQT","KQU","KQV","KQW","KQX", -"KQY","KQZ","KRA","KRB","KRC","KRD","KRE","KRF","KRG","KRH","KRI","KRJ","KRK","KRL","KRM","KRN", -"KRO","KRP","KRQ","KRR","KRS","KRT","KRU","KRV","KRW","KRX","KRY","KRZ","KSA","KSB","KSC","KSD", -"KSE","KSF","KSG","KSH","KSI","KSJ","KSK","KSL","KSM","KSN","KSO","KSP","KSQ","KSR","KSS","KST", -"KSU","KSV","KSW","KSX","KSY","KSZ","KTA","KTB","KTC","KTD","KTE","KTF","KTG","KTH","KTI","KTJ", -"KTK","KTL","KTM","KTN","KTO","KTP","KTQ","KTR","KTS","KTT","KTU","KTV","KTW","KTX","KTY","KTZ", -"KUA","KUB","KUC","KUD","KUE","KUF","KUG","KUH","KUI","KUJ","KUK","KUL","KUM","KUN","KUO","KUP", -"KUQ","KUR","KUS","KUT","KUU","KUV","KUW","KUX","KUY","KUZ","KVA","KVB","KVC","KVD","KVE","KVF", -"KVG","KVH","KVI","KVJ","KVK","KVL","KVM","KVN","KVO","KVP","KVQ","KVR","KVS","KVT","KVU","KVV", -"KVW","KVX","KVY","KVZ","KWA","KWB","KWC","KWD","KWE","KWF","KWG","KWH","KWI","KWJ","KWK","KWL", -"KWM","KWN","KWO","KWP","KWQ","KWR","KWS","KWT","KWU","KWV","KWW","KWX","KWY","KWZ","KXA","KXB", -"KXC","KXD","KXE","KXF","KXG","KXH","KXI","KXJ","KXK","KXL","KXM","KXN","KXO","KXP","KXQ","KXR", -"KXS","KXT","KXU","KXV","KXW","KXX","KXY","KXZ","KYA","KYB","KYC","KYD","KYE","KYF","KYG","KYH", -"KYI","KYJ","KYK","KYL","KYM","KYN","KYO","KYP","KYQ","KYR","KYS","KYT","KYU","KYV","KYW","KYX", -"KYY","KYZ","KZA","KZB","KZC","KZD","KZE","KZF","KZG","KZH","KZI","KZJ","KZK","KZL","KZM","KZN", -"KZO","KZP","KZQ","KZR","KZS","KZT","KZU","KZV","KZW","KZX","KZY","KZZ","LAA","LAB","LAC","LAD", -"LAE","LAF","LAG","LAH","LAI","LAJ","LAK","LAL","LAM","LAN","LAO","LAP","LAQ","LAR","LAS","LAT", -"LAU","LAV","LAW","LAX","LAY","LAZ","LBA","LBB","LBC","LBD","LBE","LBF","LBG","LBH","LBI","LBJ", -"LBK","LBL","LBM","LBN","LBO","LBP","LBQ","LBR","LBS","LBT","LBU","LBV","LBW","LBX","LBY","LBZ", -"LCA","LCB","LCC","LCD","LCE","LCF","LCG","LCH","LCI","LCJ","LCK","LCL","LCM","LCN","LCO","LCP", -"LCQ","LCR","LCS","LCT","LCU","LCV","LCW","LCX","LCY","LCZ","LDA","LDB","LDC","LDD","LDE","LDF", -"LDG","LDH","LDI","LDJ","LDK","LDL","LDM","LDN","LDO","LDP","LDQ","LDR","LDS","LDT","LDU","LDV", -"LDW","LDX","LDY","LDZ","LEA","LEB","LEC","LED","LEE","LEF","LEG","LEH","LEI","LEJ","LEK","LEL", -"LEM","LEN","LEO","LEP","LEQ","LER","LES","LET","LEU","LEV","LEW","LEX","LEY","LEZ","LFA","LFB", -"LFC","LFD","LFE","LFF","LFG","LFH","LFI","LFJ","LFK","LFL","LFM","LFN","LFO","LFP","LFQ","LFR", -"LFS","LFT","LFU","LFV","LFW","LFX","LFY","LFZ","LGA","LGB","LGC","LGD","LGE","LGF","LGG","LGH", -"LGI","LGJ","LGK","LGL","LGM","LGN","LGO","LGP","LGQ","LGR","LGS","LGT","LGU","LGV","LGW","LGX", -"LGY","LGZ","LHA","LHB","LHC","LHD","LHE","LHF","LHG","LHH","LHI","LHJ","LHK","LHL","LHM","LHN", -"LHO","LHP","LHQ","LHR","LHS","LHT","LHU","LHV","LHW","LHX","LHY","LHZ","LIA","LIB","LIC","LID", -"LIE","LIF","LIG","LIH","LII","LIJ","LIK","LIL","LIM","LIN","LIO","LIP","LIQ","LIR","LIS","LIT", -"LIU","LIV","LIW","LIX","LIY","LIZ","LJA","LJB","LJC","LJD","LJE","LJF","LJG","LJH","LJI","LJJ", -"LJK","LJL","LJM","LJN","LJO","LJP","LJQ","LJR","LJS","LJT","LJU","LJV","LJW","LJX","LJY","LJZ", -"LKA","LKB","LKC","LKD","LKE","LKF","LKG","LKH","LKI","LKJ","LKK","LKL","LKM","LKN","LKO","LKP", -"LKQ","LKR","LKS","LKT","LKU","LKV","LKW","LKX","LKY","LKZ","LLA","LLB","LLC","LLD","LLE","LLF", -"LLG","LLH","LLI","LLJ","LLK","LLL","LLM","LLN","LLO","LLP","LLQ","LLR","LLS","LLT","LLU","LLV", -"LLW","LLX","LLY","LLZ","LMA","LMB","LMC","LMD","LME","LMF","LMG","LMH","LMI","LMJ","LMK","LML", -"LMM","LMN","LMO","LMP","LMQ","LMR","LMS","LMT","LMU","LMV","LMW","LMX","LMY","LMZ","LNA","LNB", -"LNC","LND","LNE","LNF","LNG","LNH","LNI","LNJ","LNK","LNL","LNM","LNN","LNO","LNP","LNQ","LNR", -"LNS","LNT","LNU","LNV","LNW","LNX","LNY","LNZ","LOA","LOB","LOC","LOD","LOE","LOF","LOG","LOH", -"LOI","LOJ","LOK","LOL","LOM","LON","LOO","LOP","LOQ","LOR","LOS","LOT","LOU","LOV","LOW","LOX", -"LOY","LOZ","LPA","LPB","LPC","LPD","LPE","LPF","LPG","LPH","LPI","LPJ","LPK","LPL","LPM","LPN", -"LPO","LPP","LPQ","LPR","LPS","LPT","LPU","LPV","LPW","LPX","LPY","LPZ","LQA","LQB","LQC","LQD", -"LQE","LQF","LQG","LQH","LQI","LQJ","LQK","LQL","LQM","LQN","LQO","LQP","LQQ","LQR","LQS","LQT", -"LQU","LQV","LQW","LQX","LQY","LQZ","LRA","LRB","LRC","LRD","LRE","LRF","LRG","LRH","LRI","LRJ", -"LRK","LRL","LRM","LRN","LRO","LRP","LRQ","LRR","LRS","LRT","LRU","LRV","LRW","LRX","LRY","LRZ", -"LSA","LSB","LSC","LSD","LSE","LSF","LSG","LSH","LSI","LSJ","LSK","LSL","LSM","LSN","LSO","LSP", -"LSQ","LSR","LSS","LST","LSU","LSV","LSW","LSX","LSY","LSZ","LTA","LTB","LTC","LTD","LTE","LTF", -"LTG","LTH","LTI","LTJ","LTK","LTL","LTM","LTN","LTO","LTP","LTQ","LTR","LTS","LTT","LTU","LTV", -"LTW","LTX","LTY","LTZ","LUA","LUB","LUC","LUD","LUE","LUF","LUG","LUH","LUI","LUJ","LUK","LUL", -"LUM","LUN","LUO","LUP","LUQ","LUR","LUS","LUT","LUU","LUV","LUW","LUX","LUY","LUZ","LVA","LVB", -"LVC","LVD","LVE","LVF","LVG","LVH","LVI","LVJ","LVK","LVL","LVM","LVN","LVO","LVP","LVQ","LVR", -"LVS","LVT","LVU","LVV","LVW","LVX","LVY","LVZ","LWA","LWB","LWC","LWD","LWE","LWF","LWG","LWH", -"LWI","LWJ","LWK","LWL","LWM","LWN","LWO","LWP","LWQ","LWR","LWS","LWT","LWU","LWV","LWW","LWX", -"LWY","LWZ","LXA","LXB","LXC","LXD","LXE","LXF","LXG","LXH","LXI","LXJ","LXK","LXL","LXM","LXN", -"LXO","LXP","LXQ","LXR","LXS","LXT","LXU","LXV","LXW","LXX","LXY","LXZ","LYA","LYB","LYC","LYD", -"LYE","LYF","LYG","LYH","LYI","LYJ","LYK","LYL","LYM","LYN","LYO","LYP","LYQ","LYR","LYS","LYT", -"LYU","LYV","LYW","LYX","LYY","LYZ","LZA","LZB","LZC","LZD","LZE","LZF","LZG","LZH","LZI","LZJ", -"LZK","LZL","LZM","LZN","LZO","LZP","LZQ","LZR","LZS","LZT","LZU","LZV","LZW","LZX","LZY","LZZ", -"MAA","MAB","MAC","MAD","MAE","MAF","MAG","MAH","MAI","MAJ","MAK","MAL","MAM","MAN","MAO","MAP", -"MAQ","MAR","MAS","MAT","MAU","MAV","MAW","MAX","MAY","MAZ","MBA","MBB","MBC","MBD","MBE","MBF", -"MBG","MBH","MBI","MBJ","MBK","MBL","MBM","MBN","MBO","MBP","MBQ","MBR","MBS","MBT","MBU","MBV", -"MBW","MBX","MBY","MBZ","MCA","MCB","MCC","MCD","MCE","MCF","MCG","MCH","MCI","MCJ","MCK","MCL", -"MCM","MCN","MCO","MCP","MCQ","MCR","MCS","MCT","MCU","MCV","MCW","MCX","MCY","MCZ","MDA","MDB", -"MDC","MDD","MDE","MDF","MDG","MDH","MDI","MDJ","MDK","MDL","MDM","MDN","MDO","MDP","MDQ","MDR", -"MDS","MDT","MDU","MDV","MDW","MDX","MDY","MDZ","MEA","MEB","MEC","MED","MEE","MEF","MEG","MEH", -"MEI","MEJ","MEK","MEL","MEM","MEN","MEO","MEP","MEQ","MER","MES","MET","MEU","MEV","MEW","MEX", -"MEY","MEZ","MFA","MFB","MFC","MFD","MFE","MFF","MFG","MFH","MFI","MFJ","MFK","MFL","MFM","MFN", -"MFO","MFP","MFQ","MFR","MFS","MFT","MFU","MFV","MFW","MFX","MFY","MFZ","MGA","MGB","MGC","MGD", -"MGE","MGF","MGG","MGH","MGI","MGJ","MGK","MGL","MGM","MGN","MGO","MGP","MGQ","MGR","MGS","MGT", -"MGU","MGV","MGW","MGX","MGY","MGZ","MHA","MHB","MHC","MHD","MHE","MHF","MHG","MHH","MHI","MHJ", -"MHK","MHL","MHM","MHN","MHO","MHP","MHQ","MHR","MHS","MHT","MHU","MHV","MHW","MHX","MHY","MHZ", -"MIA","MIB","MIC","MID","MIE","MIF","MIG","MIH","MII","MIJ","MIK","MIL","MIM","MIN","MIO","MIP", -"MIQ","MIR","MIS","MIT","MIU","MIV","MIW","MIX","MIY","MIZ","MJA","MJB","MJC","MJD","MJE","MJF", -"MJG","MJH","MJI","MJJ","MJK","MJL","MJM","MJN","MJO","MJP","MJQ","MJR","MJS","MJT","MJU","MJV", -"MJW","MJX","MJY","MJZ","MKA","MKB","MKC","MKD","MKE","MKF","MKG","MKH","MKI","MKJ","MKK","MKL", -"MKM","MKN","MKO","MKP","MKQ","MKR","MKS","MKT","MKU","MKV","MKW","MKX","MKY","MKZ","MLA","MLB", -"MLC","MLD","MLE","MLF","MLG","MLH","MLI","MLJ","MLK","MLL","MLM","MLN","MLO","MLP","MLQ","MLR", -"MLS","MLT","MLU","MLV","MLW","MLX","MLY","MLZ","MMA","MMB","MMC","MMD","MME","MMF","MMG","MMH", -"MMI","MMJ","MMK","MML","MMM","MMN","MMO","MMP","MMQ","MMR","MMS","MMT","MMU","MMV","MMW","MMX", -"MMY","MMZ","MNA","MNB","MNC","MND","MNE","MNF","MNG","MNH","MNI","MNJ","MNK","MNL","MNM","MNN", -"MNO","MNP","MNQ","MNR","MNS","MNT","MNU","MNV","MNW","MNX","MNY","MNZ","MOA","MOB","MOC","MOD", -"MOE","MOF","MOG","MOH","MOI","MOJ","MOK","MOL","MOM","MON","MOO","MOP","MOQ","MOR","MOS","MOT", -"MOU","MOV","MOW","MOX","MOY","MOZ","MPA","MPB","MPC","MPD","MPE","MPF","MPG","MPH","MPI","MPJ", -"MPK","MPL","MPM","MPN","MPO","MPP","MPQ","MPR","MPS","MPT","MPU","MPV","MPW","MPX","MPY","MPZ", -"MQA","MQB","MQC","MQD","MQE","MQF","MQG","MQH","MQI","MQJ","MQK","MQL","MQM","MQN","MQO","MQP", -"MQQ","MQR","MQS","MQT","MQU","MQV","MQW","MQX","MQY","MQZ","MRA","MRB","MRC","MRD","MRE","MRF", -"MRG","MRH","MRI","MRJ","MRK","MRL","MRM","MRN","MRO","MRP","MRQ","MRR","MRS","MRT","MRU","MRV", -"MRW","MRX","MRY","MRZ","MSA","MSB","MSC","MSD","MSE","MSF","MSG","MSH","MSI","MSJ","MSK","MSL", -"MSM","MSN","MSO","MSP","MSQ","MSR","MSS","MST","MSU","MSV","MSW","MSX","MSY","MSZ","MTA","MTB", -"MTC","MTD","MTE","MTF","MTG","MTH","MTI","MTJ","MTK","MTL","MTM","MTN","MTO","MTP","MTQ","MTR", -"MTS","MTT","MTU","MTV","MTW","MTX","MTY","MTZ","MUA","MUB","MUC","MUD","MUE","MUF","MUG","MUH", -"MUI","MUJ","MUK","MUL","MUM","MUN","MUO","MUP","MUQ","MUR","MUS","MUT","MUU","MUV","MUW","MUX", -"MUY","MUZ","MVA","MVB","MVC","MVD","MVE","MVF","MVG","MVH","MVI","MVJ","MVK","MVL","MVM","MVN", -"MVO","MVP","MVQ","MVR","MVS","MVT","MVU","MVV","MVW","MVX","MVY","MVZ","MWA","MWB","MWC","MWD", -"MWE","MWF","MWG","MWH","MWI","MWJ","MWK","MWL","MWM","MWN","MWO","MWP","MWQ","MWR","MWS","MWT", -"MWU","MWV","MWW","MWX","MWY","MWZ","MXA","MXB","MXC","MXD","MXE","MXF","MXG","MXH","MXI","MXJ", -"MXK","MXL","MXM","MXN","MXO","MXP","MXQ","MXR","MXS","MXT","MXU","MXV","MXW","MXX","MXY","MXZ", -"MYA","MYB","MYC","MYD","MYE","MYF","MYG","MYH","MYI","MYJ","MYK","MYL","MYM","MYN","MYO","MYP", -"MYQ","MYR","MYS","MYT","MYU","MYV","MYW","MYX","MYY","MYZ","MZA","MZB","MZC","MZD","MZE","MZF", -"MZG","MZH","MZI","MZJ","MZK","MZL","MZM","MZN","MZO","MZP","MZQ","MZR","MZS","MZT","MZU","MZV", -"MZW","MZX","MZY","MZZ","NAA","NAB","NAC","NAD","NAE","NAF","NAG","NAH","NAI","NAJ","NAK","NAL", -"NAM","NAN","NAO","NAP","NAQ","NAR","NAS","NAT","NAU","NAV","NAW","NAX","NAY","NAZ","NBA","NBB", -"NBC","NBD","NBE","NBF","NBG","NBH","NBI","NBJ","NBK","NBL","NBM","NBN","NBO","NBP","NBQ","NBR", -"NBS","NBT","NBU","NBV","NBW","NBX","NBY","NBZ","NCA","NCB","NCC","NCD","NCE","NCF","NCG","NCH", -"NCI","NCJ","NCK","NCL","NCM","NCN","NCO","NCP","NCQ","NCR","NCS","NCT","NCU","NCV","NCW","NCX", -"NCY","NCZ","NDA","NDB","NDC","NDD","NDE","NDF","NDG","NDH","NDI","NDJ","NDK","NDL","NDM","NDN", -"NDO","NDP","NDQ","NDR","NDS","NDT","NDU","NDV","NDW","NDX","NDY","NDZ","NEA","NEB","NEC","NED", -"NEE","NEF","NEG","NEH","NEI","NEJ","NEK","NEL","NEM","NEN","NEO","NEP","NEQ","NER","NES","NET", -"NEU","NEV","NEW","NEX","NEY","NEZ","NFA","NFB","NFC","NFD","NFE","NFF","NFG","NFH","NFI","NFJ", -"NFK","NFL","NFM","NFN","NFO","NFP","NFQ","NFR","NFS","NFT","NFU","NFV","NFW","NFX","NFY","NFZ", -"NGA","NGB","NGC","NGD","NGE","NGF","NGG","NGH","NGI","NGJ","NGK","NGL","NGM","NGN","NGO","NGP", -"NGQ","NGR","NGS","NGT","NGU","NGV","NGW","NGX","NGY","NGZ","NHA","NHB","NHC","NHD","NHE","NHF", -"NHG","NHH","NHI","NHJ","NHK","NHL","NHM","NHN","NHO","NHP","NHQ","NHR","NHS","NHT","NHU","NHV", -"NHW","NHX","NHY","NHZ","NIA","NIB","NIC","NID","NIE","NIF","NIG","NIH","NII","NIJ","NIK","NIL", -"NIM","NIN","NIO","NIP","NIQ","NIR","NIS","NIT","NIU","NIV","NIW","NIX","NIY","NIZ","NJA","NJB", -"NJC","NJD","NJE","NJF","NJG","NJH","NJI","NJJ","NJK","NJL","NJM","NJN","NJO","NJP","NJQ","NJR", -"NJS","NJT","NJU","NJV","NJW","NJX","NJY","NJZ","NKA","NKB","NKC","NKD","NKE","NKF","NKG","NKH", -"NKI","NKJ","NKK","NKL","NKM","NKN","NKO","NKP","NKQ","NKR","NKS","NKT","NKU","NKV","NKW","NKX", -"NKY","NKZ","NLA","NLB","NLC","NLD","NLE","NLF","NLG","NLH","NLI","NLJ","NLK","NLL","NLM","NLN", -"NLO","NLP","NLQ","NLR","NLS","NLT","NLU","NLV","NLW","NLX","NLY","NLZ","NMA","NMB","NMC","NMD", -"NME","NMF","NMG","NMH","NMI","NMJ","NMK","NML","NMM","NMN","NMO","NMP","NMQ","NMR","NMS","NMT", -"NMU","NMV","NMW","NMX","NMY","NMZ","NNA","NNB","NNC","NND","NNE","NNF","NNG","NNH","NNI","NNJ", -"NNK","NNL","NNM","NNN","NNO","NNP","NNQ","NNR","NNS","NNT","NNU","NNV","NNW","NNX","NNY","NNZ", -"NOA","NOB","NOC","NOD","NOE","NOF","NOG","NOH","NOI","NOJ","NOK","NOL","NOM","NON","NOO","NOP", -"NOQ","NOR","NOS","NOT","NOU","NOV","NOW","NOX","NOY","NOZ","NPA","NPB","NPC","NPD","NPE","NPF", -"NPG","NPH","NPI","NPJ","NPK","NPL","NPM","NPN","NPO","NPP","NPQ","NPR","NPS","NPT","NPU","NPV", -"NPW","NPX","NPY","NPZ","NQA","NQB","NQC","NQD","NQE","NQF","NQG","NQH","NQI","NQJ","NQK","NQL", -"NQM","NQN","NQO","NQP","NQQ","NQR","NQS","NQT","NQU","NQV","NQW","NQX","NQY","NQZ","NRA","NRB", -"NRC","NRD","NRE","NRF","NRG","NRH","NRI","NRJ","NRK","NRL","NRM","NRN","NRO","NRP","NRQ","NRR", -"NRS","NRT","NRU","NRV","NRW","NRX","NRY","NRZ","NSA","NSB","NSC","NSD","NSE","NSF","NSG","NSH", -"NSI","NSJ","NSK","NSL","NSM","NSN","NSO","NSP","NSQ","NSR","NSS","NST","NSU","NSV","NSW","NSX", -"NSY","NSZ","NTA","NTB","NTC","NTD","NTE","NTF","NTG","NTH","NTI","NTJ","NTK","NTL","NTM","NTN", -"NTO","NTP","NTQ","NTR","NTS","NTT","NTU","NTV","NTW","NTX","NTY","NTZ","NUA","NUB","NUC","NUD", -"NUE","NUF","NUG","NUH","NUI","NUJ","NUK","NUL","NUM","NUN","NUO","NUP","NUQ","NUR","NUS","NUT", -"NUU","NUV","NUW","NUX","NUY","NUZ","NVA","NVB","NVC","NVD","NVE","NVF","NVG","NVH","NVI","NVJ", -"NVK","NVL","NVM","NVN","NVO","NVP","NVQ","NVR","NVS","NVT","NVU","NVV","NVW","NVX","NVY","NVZ", -"NWA","NWB","NWC","NWD","NWE","NWF","NWG","NWH","NWI","NWJ","NWK","NWL","NWM","NWN","NWO","NWP", -"NWQ","NWR","NWS","NWT","NWU","NWV","NWW","NWX","NWY","NWZ","NXA","NXB","NXC","NXD","NXE","NXF", -"NXG","NXH","NXI","NXJ","NXK","NXL","NXM","NXN","NXO","NXP","NXQ","NXR","NXS","NXT","NXU","NXV", -"NXW","NXX","NXY","NXZ","NYA","NYB","NYC","NYD","NYE","NYF","NYG","NYH","NYI","NYJ","NYK","NYL", -"NYM","NYN","NYO","NYP","NYQ","NYR","NYS","NYT","NYU","NYV","NYW","NYX","NYY","NYZ","NZA","NZB", -"NZC","NZD","NZE","NZF","NZG","NZH","NZI","NZJ","NZK","NZL","NZM","NZN","NZO","NZP","NZQ","NZR", -"NZS","NZT","NZU","NZV","NZW","NZX","NZY","NZZ","OAA","OAB","OAC","OAD","OAE","OAF","OAG","OAH", -"OAI","OAJ","OAK","OAL","OAM","OAN","OAO","OAP","OAQ","OAR","OAS","OAT","OAU","OAV","OAW","OAX", -"OAY","OAZ","OBA","OBB","OBC","OBD","OBE","OBF","OBG","OBH","OBI","OBJ","OBK","OBL","OBM","OBN", -"OBO","OBP","OBQ","OBR","OBS","OBT","OBU","OBV","OBW","OBX","OBY","OBZ","OCA","OCB","OCC","OCD", -"OCE","OCF","OCG","OCH","OCI","OCJ","OCK","OCL","OCM","OCN","OCO","OCP","OCQ","OCR","OCS","OCT", -"OCU","OCV","OCW","OCX","OCY","OCZ","ODA","ODB","ODC","ODD","ODE","ODF","ODG","ODH","ODI","ODJ", -"ODK","ODL","ODM","ODN","ODO","ODP","ODQ","ODR","ODS","ODT","ODU","ODV","ODW","ODX","ODY","ODZ", -"OEA","OEB","OEC","OED","OEE","OEF","OEG","OEH","OEI","OEJ","OEK","OEL","OEM","OEN","OEO","OEP", -"OEQ","OER","OES","OET","OEU","OEV","OEW","OEX","OEY","OEZ","OFA","OFB","OFC","OFD","OFE","OFF", -"OFG","OFH","OFI","OFJ","OFK","OFL","OFM","OFN","OFO","OFP","OFQ","OFR","OFS","OFT","OFU","OFV", -"OFW","OFX","OFY","OFZ","OGA","OGB","OGC","OGD","OGE","OGF","OGG","OGH","OGI","OGJ","OGK","OGL", -"OGM","OGN","OGO","OGP","OGQ","OGR","OGS","OGT","OGU","OGV","OGW","OGX","OGY","OGZ","OHA","OHB", -"OHC","OHD","OHE","OHF","OHG","OHH","OHI","OHJ","OHK","OHL","OHM","OHN","OHO","OHP","OHQ","OHR", -"OHS","OHT","OHU","OHV","OHW","OHX","OHY","OHZ","OIA","OIB","OIC","OID","OIE","OIF","OIG","OIH", -"OII","OIJ","OIK","OIL","OIM","OIN","OIO","OIP","OIQ","OIR","OIS","OIT","OIU","OIV","OIW","OIX", -"OIY","OIZ","OJA","OJB","OJC","OJD","OJE","OJF","OJG","OJH","OJI","OJJ","OJK","OJL","OJM","OJN", -"OJO","OJP","OJQ","OJR","OJS","OJT","OJU","OJV","OJW","OJX","OJY","OJZ","OKA","OKB","OKC","OKD", -"OKE","OKF","OKG","OKH","OKI","OKJ","OKK","OKL","OKM","OKN","OKO","OKP","OKQ","OKR","OKS","OKT", -"OKU","OKV","OKW","OKX","OKY","OKZ","OLA","OLB","OLC","OLD","OLE","OLF","OLG","OLH","OLI","OLJ", -"OLK","OLL","OLM","OLN","OLO","OLP","OLQ","OLR","OLS","OLT","OLU","OLV","OLW","OLX","OLY","OLZ", -"OMA","OMB","OMC","OMD","OME","OMF","OMG","OMH","OMI","OMJ","OMK","OML","OMM","OMN","OMO","OMP", -"OMQ","OMR","OMS","OMT","OMU","OMV","OMW","OMX","OMY","OMZ","ONA","ONB","ONC","OND","ONE","ONF", -"ONG","ONH","ONI","ONJ","ONK","ONL","ONM","ONN","ONO","ONP","ONQ","ONR","ONS","ONT","ONU","ONV", -"ONW","ONX","ONY","ONZ","OOA","OOB","OOC","OOD","OOE","OOF","OOG","OOH","OOI","OOJ","OOK","OOL", -"OOM","OON","OOO","OOP","OOQ","OOR","OOS","OOT","OOU","OOV","OOW","OOX","OOY","OOZ","OPA","OPB", -"OPC","OPD","OPE","OPF","OPG","OPH","OPI","OPJ","OPK","OPL","OPM","OPN","OPO","OPP","OPQ","OPR", -"OPS","OPT","OPU","OPV","OPW","OPX","OPY","OPZ","OQA","OQB","OQC","OQD","OQE","OQF","OQG","OQH", -"OQI","OQJ","OQK","OQL","OQM","OQN","OQO","OQP","OQQ","OQR","OQS","OQT","OQU","OQV","OQW","OQX", -"OQY","OQZ","ORA","ORB","ORC","ORD","ORE","ORF","ORG","ORH","ORI","ORJ","ORK","ORL","ORM","ORN", -"ORO","ORP","ORQ","ORR","ORS","ORT","ORU","ORV","ORW","ORX","ORY","ORZ","OSA","OSB","OSC","OSD", -"OSE","OSF","OSG","OSH","OSI","OSJ","OSK","OSL","OSM","OSN","OSO","OSP","OSQ","OSR","OSS","OST", -"OSU","OSV","OSW","OSX","OSY","OSZ","OTA","OTB","OTC","OTD","OTE","OTF","OTG","OTH","OTI","OTJ", -"OTK","OTL","OTM","OTN","OTO","OTP","OTQ","OTR","OTS","OTT","OTU","OTV","OTW","OTX","OTY","OTZ", -"OUA","OUB","OUC","OUD","OUE","OUF","OUG","OUH","OUI","OUJ","OUK","OUL","OUM","OUN","OUO","OUP", -"OUQ","OUR","OUS","OUT","OUU","OUV","OUW","OUX","OUY","OUZ","OVA","OVB","OVC","OVD","OVE","OVF", -"OVG","OVH","OVI","OVJ","OVK","OVL","OVM","OVN","OVO","OVP","OVQ","OVR","OVS","OVT","OVU","OVV", -"OVW","OVX","OVY","OVZ","OWA","OWB","OWC","OWD","OWE","OWF","OWG","OWH","OWI","OWJ","OWK","OWL", -"OWM","OWN","OWO","OWP","OWQ","OWR","OWS","OWT","OWU","OWV","OWW","OWX","OWY","OWZ","OXA","OXB", -"OXC","OXD","OXE","OXF","OXG","OXH","OXI","OXJ","OXK","OXL","OXM","OXN","OXO","OXP","OXQ","OXR", -"OXS","OXT","OXU","OXV","OXW","OXX","OXY","OXZ","OYA","OYB","OYC","OYD","OYE","OYF","OYG","OYH", -"OYI","OYJ","OYK","OYL","OYM","OYN","OYO","OYP","OYQ","OYR","OYS","OYT","OYU","OYV","OYW","OYX", -"OYY","OYZ","OZA","OZB","OZC","OZD","OZE","OZF","OZG","OZH","OZI","OZJ","OZK","OZL","OZM","OZN", -"OZO","OZP","OZQ","OZR","OZS","OZT","OZU","OZV","OZW","OZX","OZY","OZZ","PAA","PAB","PAC","PAD", -"PAE","PAF","PAG","PAH","PAI","PAJ","PAK","PAL","PAM","PAN","PAO","PAP","PAQ","PAR","PAS","PAT", -"PAU","PAV","PAW","PAX","PAY","PAZ","PBA","PBB","PBC","PBD","PBE","PBF","PBG","PBH","PBI","PBJ", -"PBK","PBL","PBM","PBN","PBO","PBP","PBQ","PBR","PBS","PBT","PBU","PBV","PBW","PBX","PBY","PBZ", -"PCA","PCB","PCC","PCD","PCE","PCF","PCG","PCH","PCI","PCJ","PCK","PCL","PCM","PCN","PCO","PCP", -"PCQ","PCR","PCS","PCT","PCU","PCV","PCW","PCX","PCY","PCZ","PDA","PDB","PDC","PDD","PDE","PDF", -"PDG","PDH","PDI","PDJ","PDK","PDL","PDM","PDN","PDO","PDP","PDQ","PDR","PDS","PDT","PDU","PDV", -"PDW","PDX","PDY","PDZ","PEA","PEB","PEC","PED","PEE","PEF","PEG","PEH","PEI","PEJ","PEK","PEL", -"PEM","PEN","PEO","PEP","PEQ","PER","PES","PET","PEU","PEV","PEW","PEX","PEY","PEZ","PFA","PFB", -"PFC","PFD","PFE","PFF","PFG","PFH","PFI","PFJ","PFK","PFL","PFM","PFN","PFO","PFP","PFQ","PFR", -"PFS","PFT","PFU","PFV","PFW","PFX","PFY","PFZ","PGA","PGB","PGC","PGD","PGE","PGF","PGG","PGH", -"PGI","PGJ","PGK","PGL","PGM","PGN","PGO","PGP","PGQ","PGR","PGS","PGT","PGU","PGV","PGW","PGX", -"PGY","PGZ","PHA","PHB","PHC","PHD","PHE","PHF","PHG","PHH","PHI","PHJ","PHK","PHL","PHM","PHN", -"PHO","PHP","PHQ","PHR","PHS","PHT","PHU","PHV","PHW","PHX","PHY","PHZ","PIA","PIB","PIC","PID", -"PIE","PIF","PIG","PIH","PII","PIJ","PIK","PIL","PIM","PIN","PIO","PIP","PIQ","PIR","PIS","PIT", -"PIU","PIV","PIW","PIX","PIY","PIZ","PJA","PJB","PJC","PJD","PJE","PJF","PJG","PJH","PJI","PJJ", -"PJK","PJL","PJM","PJN","PJO","PJP","PJQ","PJR","PJS","PJT","PJU","PJV","PJW","PJX","PJY","PJZ", -"PKA","PKB","PKC","PKD","PKE","PKF","PKG","PKH","PKI","PKJ","PKK","PKL","PKM","PKN","PKO","PKP", -"PKQ","PKR","PKS","PKT","PKU","PKV","PKW","PKX","PKY","PKZ","PLA","PLB","PLC","PLD","PLE","PLF", -"PLG","PLH","PLI","PLJ","PLK","PLL","PLM","PLN","PLO","PLP","PLQ","PLR","PLS","PLT","PLU","PLV", -"PLW","PLX","PLY","PLZ","PMA","PMB","PMC","PMD","PME","PMF","PMG","PMH","PMI","PMJ","PMK","PML", -"PMM","PMN","PMO","PMP","PMQ","PMR","PMS","PMT","PMU","PMV","PMW","PMX","PMY","PMZ","PNA","PNB", -"PNC","PND","PNE","PNF","PNG","PNH","PNI","PNJ","PNK","PNL","PNM","PNN","PNO","PNP","PNQ","PNR", -"PNS","PNT","PNU","PNV","PNW","PNX","PNY","PNZ","POA","POB","POC","POD","POE","POF","POG","POH", -"POI","POJ","POK","POL","POM","PON","POO","POP","POQ","POR","POS","POT","POU","POV","POW","POX", -"POY","POZ","PPA","PPB","PPC","PPD","PPE","PPF","PPG","PPH","PPI","PPJ","PPK","PPL","PPM","PPN", -"PPO","PPP","PPQ","PPR","PPS","PPT","PPU","PPV","PPW","PPX","PPY","PPZ","PQA","PQB","PQC","PQD", -"PQE","PQF","PQG","PQH","PQI","PQJ","PQK","PQL","PQM","PQN","PQO","PQP","PQQ","PQR","PQS","PQT", -"PQU","PQV","PQW","PQX","PQY","PQZ","PRA","PRB","PRC","PRD","PRE","PRF","PRG","PRH","PRI","PRJ", -"PRK","PRL","PRM","PRN","PRO","PRP","PRQ","PRR","PRS","PRT","PRU","PRV","PRW","PRX","PRY","PRZ", -"PSA","PSB","PSC","PSD","PSE","PSF","PSG","PSH","PSI","PSJ","PSK","PSL","PSM","PSN","PSO","PSP", -"PSQ","PSR","PSS","PST","PSU","PSV","PSW","PSX","PSY","PSZ","PTA","PTB","PTC","PTD","PTE","PTF", -"PTG","PTH","PTI","PTJ","PTK","PTL","PTM","PTN","PTO","PTP","PTQ","PTR","PTS","PTT","PTU","PTV", -"PTW","PTX","PTY","PTZ","PUA","PUB","PUC","PUD","PUE","PUF","PUG","PUH","PUI","PUJ","PUK","PUL", -"PUM","PUN","PUO","PUP","PUQ","PUR","PUS","PUT","PUU","PUV","PUW","PUX","PUY","PUZ","PVA","PVB", -"PVC","PVD","PVE","PVF","PVG","PVH","PVI","PVJ","PVK","PVL","PVM","PVN","PVO","PVP","PVQ","PVR", -"PVS","PVT","PVU","PVV","PVW","PVX","PVY","PVZ","PWA","PWB","PWC","PWD","PWE","PWF","PWG","PWH", -"PWI","PWJ","PWK","PWL","PWM","PWN","PWO","PWP","PWQ","PWR","PWS","PWT","PWU","PWV","PWW","PWX", -"PWY","PWZ","PXA","PXB","PXC","PXD","PXE","PXF","PXG","PXH","PXI","PXJ","PXK","PXL","PXM","PXN", -"PXO","PXP","PXQ","PXR","PXS","PXT","PXU","PXV","PXW","PXX","PXY","PXZ","PYA","PYB","PYC","PYD", -"PYE","PYF","PYG","PYH","PYI","PYJ","PYK","PYL","PYM","PYN","PYO","PYP","PYQ","PYR","PYS","PYT", -"PYU","PYV","PYW","PYX","PYY","PYZ","PZA","PZB","PZC","PZD","PZE","PZF","PZG","PZH","PZI","PZJ", -"PZK","PZL","PZM","PZN","PZO","PZP","PZQ","PZR","PZS","PZT","PZU","PZV","PZW","PZX","PZY","PZZ", -"QAA","QAB","QAC","QAD","QAE","QAF","QAG","QAH","QAI","QAJ","QAK","QAL","QAM","QAN","QAO","QAP", -"QAQ","QAR","QAS","QAT","QAU","QAV","QAW","QAX","QAY","QAZ","QBA","QBB","QBC","QBD","QBE","QBF", -"QBG","QBH","QBI","QBJ","QBK","QBL","QBM","QBN","QBO","QBP","QBQ","QBR","QBS","QBT","QBU","QBV", -"QBW","QBX","QBY","QBZ","QCA","QCB","QCC","QCD","QCE","QCF","QCG","QCH","QCI","QCJ","QCK","QCL", -"QCM","QCN","QCO","QCP","QCQ","QCR","QCS","QCT","QCU","QCV","QCW","QCX","QCY","QCZ","QDA","QDB", -"QDC","QDD","QDE","QDF","QDG","QDH","QDI","QDJ","QDK","QDL","QDM","QDN","QDO","QDP","QDQ","QDR", -"QDS","QDT","QDU","QDV","QDW","QDX","QDY","QDZ","QEA","QEB","QEC","QED","QEE","QEF","QEG","QEH", -"QEI","QEJ","QEK","QEL","QEM","QEN","QEO","QEP","QEQ","QER","QES","QET","QEU","QEV","QEW","QEX", -"QEY","QEZ","QFA","QFB","QFC","QFD","QFE","QFF","QFG","QFH","QFI","QFJ","QFK","QFL","QFM","QFN", -"QFO","QFP","QFQ","QFR","QFS","QFT","QFU","QFV","QFW","QFX","QFY","QFZ","QGA","QGB","QGC","QGD", -"QGE","QGF","QGG","QGH","QGI","QGJ","QGK","QGL","QGM","QGN","QGO","QGP","QGQ","QGR","QGS","QGT", -"QGU","QGV","QGW","QGX","QGY","QGZ","QHA","QHB","QHC","QHD","QHE","QHF","QHG","QHH","QHI","QHJ", -"QHK","QHL","QHM","QHN","QHO","QHP","QHQ","QHR","QHS","QHT","QHU","QHV","QHW","QHX","QHY","QHZ", -"QIA","QIB","QIC","QID","QIE","QIF","QIG","QIH","QII","QIJ","QIK","QIL","QIM","QIN","QIO","QIP", -"QIQ","QIR","QIS","QIT","QIU","QIV","QIW","QIX","QIY","QIZ","QJA","QJB","QJC","QJD","QJE","QJF", -"QJG","QJH","QJI","QJJ","QJK","QJL","QJM","QJN","QJO","QJP","QJQ","QJR","QJS","QJT","QJU","QJV", -"QJW","QJX","QJY","QJZ","QKA","QKB","QKC","QKD","QKE","QKF","QKG","QKH","QKI","QKJ","QKK","QKL", -"QKM","QKN","QKO","QKP","QKQ","QKR","QKS","QKT","QKU","QKV","QKW","QKX","QKY","QKZ","QLA","QLB", -"QLC","QLD","QLE","QLF","QLG","QLH","QLI","QLJ","QLK","QLL","QLM","QLN","QLO","QLP","QLQ","QLR", -"QLS","QLT","QLU","QLV","QLW","QLX","QLY","QLZ","QMA","QMB","QMC","QMD","QME","QMF","QMG","QMH", -"QMI","QMJ","QMK","QML","QMM","QMN","QMO","QMP","QMQ","QMR","QMS","QMT","QMU","QMV","QMW","QMX", -"QMY","QMZ","QNA","QNB","QNC","QND","QNE","QNF","QNG","QNH","QNI","QNJ","QNK","QNL","QNM","QNN", -"QNO","QNP","QNQ","QNR","QNS","QNT","QNU","QNV","QNW","QNX","QNY","QNZ","QOA","QOB","QOC","QOD", -"QOE","QOF","QOG","QOH","QOI","QOJ","QOK","QOL","QOM","QON","QOO","QOP","QOQ","QOR","QOS","QOT", -"QOU","QOV","QOW","QOX","QOY","QOZ","QPA","QPB","QPC","QPD","QPE","QPF","QPG","QPH","QPI","QPJ", -"QPK","QPL","QPM","QPN","QPO","QPP","QPQ","QPR","QPS","QPT","QPU","QPV","QPW","QPX","QPY","QPZ", -"QQA","QQB","QQC","QQD","QQE","QQF","QQG","QQH","QQI","QQJ","QQK","QQL","QQM","QQN","QQO","QQP", -"QQQ","QQR","QQS","QQT","QQU","QQV","QQW","QQX","QQY","QQZ","QRA","QRB","QRC","QRD","QRE","QRF", -"QRG","QRH","QRI","QRJ","QRK","QRL","QRM","QRN","QRO","QRP","QRQ","QRR","QRS","QRT","QRU","QRV", -"QRW","QRX","QRY","QRZ","QSA","QSB","QSC","QSD","QSE","QSF","QSG","QSH","QSI","QSJ","QSK","QSL", -"QSM","QSN","QSO","QSP","QSQ","QSR","QSS","QST","QSU","QSV","QSW","QSX","QSY","QSZ","QTA","QTB", -"QTC","QTD","QTE","QTF","QTG","QTH","QTI","QTJ","QTK","QTL","QTM","QTN","QTO","QTP","QTQ","QTR", -"QTS","QTT","QTU","QTV","QTW","QTX","QTY","QTZ","QUA","QUB","QUC","QUD","QUE","QUF","QUG","QUH", -"QUI","QUJ","QUK","QUL","QUM","QUN","QUO","QUP","QUQ","QUR","QUS","QUT","QUU","QUV","QUW","QUX", -"QUY","QUZ","QVA","QVB","QVC","QVD","QVE","QVF","QVG","QVH","QVI","QVJ","QVK","QVL","QVM","QVN", -"QVO","QVP","QVQ","QVR","QVS","QVT","QVU","QVV","QVW","QVX","QVY","QVZ","QWA","QWB","QWC","QWD", -"QWE","QWF","QWG","QWH","QWI","QWJ","QWK","QWL","QWM","QWN","QWO","QWP","QWQ","QWR","QWS","QWT", -"QWU","QWV","QWW","QWX","QWY","QWZ","QXA","QXB","QXC","QXD","QXE","QXF","QXG","QXH","QXI","QXJ", -"QXK","QXL","QXM","QXN","QXO","QXP","QXQ","QXR","QXS","QXT","QXU","QXV","QXW","QXX","QXY","QXZ", -"QYA","QYB","QYC","QYD","QYE","QYF","QYG","QYH","QYI","QYJ","QYK","QYL","QYM","QYN","QYO","QYP", -"QYQ","QYR","QYS","QYT","QYU","QYV","QYW","QYX","QYY","QYZ","QZA","QZB","QZC","QZD","QZE","QZF", -"QZG","QZH","QZI","QZJ","QZK","QZL","QZM","QZN","QZO","QZP","QZQ","QZR","QZS","QZT","QZU","QZV", -"QZW","QZX","QZY","QZZ","RAA","RAB","RAC","RAD","RAE","RAF","RAG","RAH","RAI","RAJ","RAK","RAL", -"RAM","RAN","RAO","RAP","RAQ","RAR","RAS","RAT","RAU","RAV","RAW","RAX","RAY","RAZ","RBA","RBB", -"RBC","RBD","RBE","RBF","RBG","RBH","RBI","RBJ","RBK","RBL","RBM","RBN","RBO","RBP","RBQ","RBR", -"RBS","RBT","RBU","RBV","RBW","RBX","RBY","RBZ","RCA","RCB","RCC","RCD","RCE","RCF","RCG","RCH", -"RCI","RCJ","RCK","RCL","RCM","RCN","RCO","RCP","RCQ","RCR","RCS","RCT","RCU","RCV","RCW","RCX", -"RCY","RCZ","RDA","RDB","RDC","RDD","RDE","RDF","RDG","RDH","RDI","RDJ","RDK","RDL","RDM","RDN", -"RDO","RDP","RDQ","RDR","RDS","RDT","RDU","RDV","RDW","RDX","RDY","RDZ","REA","REB","REC","RED", -"REE","REF","REG","REH","REI","REJ","REK","REL","REM","REN","REO","REP","REQ","RER","RES","RET", -"REU","REV","REW","REX","REY","REZ","RFA","RFB","RFC","RFD","RFE","RFF","RFG","RFH","RFI","RFJ", -"RFK","RFL","RFM","RFN","RFO","RFP","RFQ","RFR","RFS","RFT","RFU","RFV","RFW","RFX","RFY","RFZ", -"RGA","RGB","RGC","RGD","RGE","RGF","RGG","RGH","RGI","RGJ","RGK","RGL","RGM","RGN","RGO","RGP", -"RGQ","RGR","RGS","RGT","RGU","RGV","RGW","RGX","RGY","RGZ","RHA","RHB","RHC","RHD","RHE","RHF", -"RHG","RHH","RHI","RHJ","RHK","RHL","RHM","RHN","RHO","RHP","RHQ","RHR","RHS","RHT","RHU","RHV", -"RHW","RHX","RHY","RHZ","RIA","RIB","RIC","RID","RIE","RIF","RIG","RIH","RII","RIJ","RIK","RIL", -"RIM","RIN","RIO","RIP","RIQ","RIR","RIS","RIT","RIU","RIV","RIW","RIX","RIY","RIZ","RJA","RJB", -"RJC","RJD","RJE","RJF","RJG","RJH","RJI","RJJ","RJK","RJL","RJM","RJN","RJO","RJP","RJQ","RJR", -"RJS","RJT","RJU","RJV","RJW","RJX","RJY","RJZ","RKA","RKB","RKC","RKD","RKE","RKF","RKG","RKH", -"RKI","RKJ","RKK","RKL","RKM","RKN","RKO","RKP","RKQ","RKR","RKS","RKT","RKU","RKV","RKW","RKX", -"RKY","RKZ","RLA","RLB","RLC","RLD","RLE","RLF","RLG","RLH","RLI","RLJ","RLK","RLL","RLM","RLN", -"RLO","RLP","RLQ","RLR","RLS","RLT","RLU","RLV","RLW","RLX","RLY","RLZ","RMA","RMB","RMC","RMD", -"RME","RMF","RMG","RMH","RMI","RMJ","RMK","RML","RMM","RMN","RMO","RMP","RMQ","RMR","RMS","RMT", -"RMU","RMV","RMW","RMX","RMY","RMZ","RNA","RNB","RNC","RND","RNE","RNF","RNG","RNH","RNI","RNJ", -"RNK","RNL","RNM","RNN","RNO","RNP","RNQ","RNR","RNS","RNT","RNU","RNV","RNW","RNX","RNY","RNZ", -"ROA","ROB","ROC","ROD","ROE","ROF","ROG","ROH","ROI","ROJ","ROK","ROL","ROM","RON","ROO","ROP", -"ROQ","ROR","ROS","ROT","ROU","ROV","ROW","ROX","ROY","ROZ","RPA","RPB","RPC","RPD","RPE","RPF", -"RPG","RPH","RPI","RPJ","RPK","RPL","RPM","RPN","RPO","RPP","RPQ","RPR","RPS","RPT","RPU","RPV", -"RPW","RPX","RPY","RPZ","RQA","RQB","RQC","RQD","RQE","RQF","RQG","RQH","RQI","RQJ","RQK","RQL", -"RQM","RQN","RQO","RQP","RQQ","RQR","RQS","RQT","RQU","RQV","RQW","RQX","RQY","RQZ","RRA","RRB", -"RRC","RRD","RRE","RRF","RRG","RRH","RRI","RRJ","RRK","RRL","RRM","RRN","RRO","RRP","RRQ","RRR", -"RRS","RRT","RRU","RRV","RRW","RRX","RRY","RRZ","RSA","RSB","RSC","RSD","RSE","RSF","RSG","RSH", -"RSI","RSJ","RSK","RSL","RSM","RSN","RSO","RSP","RSQ","RSR","RSS","RST","RSU","RSV","RSW","RSX", -"RSY","RSZ","RTA","RTB","RTC","RTD","RTE","RTF","RTG","RTH","RTI","RTJ","RTK","RTL","RTM","RTN", -"RTO","RTP","RTQ","RTR","RTS","RTT","RTU","RTV","RTW","RTX","RTY","RTZ","RUA","RUB","RUC","RUD", -"RUE","RUF","RUG","RUH","RUI","RUJ","RUK","RUL","RUM","RUN","RUO","RUP","RUQ","RUR","RUS","RUT", -"RUU","RUV","RUW","RUX","RUY","RUZ","RVA","RVB","RVC","RVD","RVE","RVF","RVG","RVH","RVI","RVJ", -"RVK","RVL","RVM","RVN","RVO","RVP","RVQ","RVR","RVS","RVT","RVU","RVV","RVW","RVX","RVY","RVZ", -"RWA","RWB","RWC","RWD","RWE","RWF","RWG","RWH","RWI","RWJ","RWK","RWL","RWM","RWN","RWO","RWP", -"RWQ","RWR","RWS","RWT","RWU","RWV","RWW","RWX","RWY","RWZ","RXA","RXB","RXC","RXD","RXE","RXF", -"RXG","RXH","RXI","RXJ","RXK","RXL","RXM","RXN","RXO","RXP","RXQ","RXR","RXS","RXT","RXU","RXV", -"RXW","RXX","RXY","RXZ","RYA","RYB","RYC","RYD","RYE","RYF","RYG","RYH","RYI","RYJ","RYK","RYL", -"RYM","RYN","RYO","RYP","RYQ","RYR","RYS","RYT","RYU","RYV","RYW","RYX","RYY","RYZ","RZA","RZB", -"RZC","RZD","RZE","RZF","RZG","RZH","RZI","RZJ","RZK","RZL","RZM","RZN","RZO","RZP","RZQ","RZR", -"RZS","RZT","RZU","RZV","RZW","RZX","RZY","RZZ","SAA","SAB","SAC","SAD","SAE","SAF","SAG","SAH", -"SAI","SAJ","SAK","SAL","SAM","SAN","SAO","SAP","SAQ","SAR","SAS","SAT","SAU","SAV","SAW","SAX", -"SAY","SAZ","SBA","SBB","SBC","SBD","SBE","SBF","SBG","SBH","SBI","SBJ","SBK","SBL","SBM","SBN", -"SBO","SBP","SBQ","SBR","SBS","SBT","SBU","SBV","SBW","SBX","SBY","SBZ","SCA","SCB","SCC","SCD", -"SCE","SCF","SCG","SCH","SCI","SCJ","SCK","SCL","SCM","SCN","SCO","SCP","SCQ","SCR","SCS","SCT", -"SCU","SCV","SCW","SCX","SCY","SCZ","SDA","SDB","SDC","SDD","SDE","SDF","SDG","SDH","SDI","SDJ", -"SDK","SDL","SDM","SDN","SDO","SDP","SDQ","SDR","SDS","SDT","SDU","SDV","SDW","SDX","SDY","SDZ", -"SEA","SEB","SEC","SED","SEE","SEF","SEG","SEH","SEI","SEJ","SEK","SEL","SEM","SEN","SEO","SEP", -"SEQ","SER","SES","SET","SEU","SEV","SEW","SEX","SEY","SEZ","SFA","SFB","SFC","SFD","SFE","SFF", -"SFG","SFH","SFI","SFJ","SFK","SFL","SFM","SFN","SFO","SFP","SFQ","SFR","SFS","SFT","SFU","SFV", -"SFW","SFX","SFY","SFZ","SGA","SGB","SGC","SGD","SGE","SGF","SGG","SGH","SGI","SGJ","SGK","SGL", -"SGM","SGN","SGO","SGP","SGQ","SGR","SGS","SGT","SGU","SGV","SGW","SGX","SGY","SGZ","SHA","SHB", -"SHC","SHD","SHE","SHF","SHG","SHH","SHI","SHJ","SHK","SHL","SHM","SHN","SHO","SHP","SHQ","SHR", -"SHS","SHT","SHU","SHV","SHW","SHX","SHY","SHZ","SIA","SIB","SIC","SID","SIE","SIF","SIG","SIH", -"SII","SIJ","SIK","SIL","SIM","SIN","SIO","SIP","SIQ","SIR","SIS","SIT","SIU","SIV","SIW","SIX", -"SIY","SIZ","SJA","SJB","SJC","SJD","SJE","SJF","SJG","SJH","SJI","SJJ","SJK","SJL","SJM","SJN", -"SJO","SJP","SJQ","SJR","SJS","SJT","SJU","SJV","SJW","SJX","SJY","SJZ","SKA","SKB","SKC","SKD", -"SKE","SKF","SKG","SKH","SKI","SKJ","SKK","SKL","SKM","SKN","SKO","SKP","SKQ","SKR","SKS","SKT", -"SKU","SKV","SKW","SKX","SKY","SKZ","SLA","SLB","SLC","SLD","SLE","SLF","SLG","SLH","SLI","SLJ", -"SLK","SLL","SLM","SLN","SLO","SLP","SLQ","SLR","SLS","SLT","SLU","SLV","SLW","SLX","SLY","SLZ", -"SMA","SMB","SMC","SMD","SME","SMF","SMG","SMH","SMI","SMJ","SMK","SML","SMM","SMN","SMO","SMP", -"SMQ","SMR","SMS","SMT","SMU","SMV","SMW","SMX","SMY","SMZ","SNA","SNB","SNC","SND","SNE","SNF", -"SNG","SNH","SNI","SNJ","SNK","SNL","SNM","SNN","SNO","SNP","SNQ","SNR","SNS","SNT","SNU","SNV", -"SNW","SNX","SNY","SNZ","SOA","SOB","SOC","SOD","SOE","SOF","SOG","SOH","SOI","SOJ","SOK","SOL", -"SOM","SON","SOO","SOP","SOQ","SOR","SOS","SOT","SOU","SOV","SOW","SOX","SOY","SOZ","SPA","SPB", -"SPC","SPD","SPE","SPF","SPG","SPH","SPI","SPJ","SPK","SPL","SPM","SPN","SPO","SPP","SPQ","SPR", -"SPS","SPT","SPU","SPV","SPW","SPX","SPY","SPZ","SQA","SQB","SQC","SQD","SQE","SQF","SQG","SQH", -"SQI","SQJ","SQK","SQL","SQM","SQN","SQO","SQP","SQQ","SQR","SQS","SQT","SQU","SQV","SQW","SQX", -"SQY","SQZ","SRA","SRB","SRC","SRD","SRE","SRF","SRG","SRH","SRI","SRJ","SRK","SRL","SRM","SRN", -"SRO","SRP","SRQ","SRR","SRS","SRT","SRU","SRV","SRW","SRX","SRY","SRZ","SSA","SSB","SSC","SSD", -"SSE","SSF","SSG","SSH","SSI","SSJ","SSK","SSL","SSM","SSN","SSO","SSP","SSQ","SSR","SSS","SST", -"SSU","SSV","SSW","SSX","SSY","SSZ","STA","STB","STC","STD","STE","STF","STG","STH","STI","STJ", -"STK","STL","STM","STN","STO","STP","STQ","STR","STS","STT","STU","STV","STW","STX","STY","STZ", -"SUA","SUB","SUC","SUD","SUE","SUF","SUG","SUH","SUI","SUJ","SUK","SUL","SUM","SUN","SUO","SUP", -"SUQ","SUR","SUS","SUT","SUU","SUV","SUW","SUX","SUY","SUZ","SVA","SVB","SVC","SVD","SVE","SVF", -"SVG","SVH","SVI","SVJ","SVK","SVL","SVM","SVN","SVO","SVP","SVQ","SVR","SVS","SVT","SVU","SVV", -"SVW","SVX","SVY","SVZ","SWA","SWB","SWC","SWD","SWE","SWF","SWG","SWH","SWI","SWJ","SWK","SWL", -"SWM","SWN","SWO","SWP","SWQ","SWR","SWS","SWT","SWU","SWV","SWW","SWX","SWY","SWZ","SXA","SXB", -"SXC","SXD","SXE","SXF","SXG","SXH","SXI","SXJ","SXK","SXL","SXM","SXN","SXO","SXP","SXQ","SXR", -"SXS","SXT","SXU","SXV","SXW","SXX","SXY","SXZ","SYA","SYB","SYC","SYD","SYE","SYF","SYG","SYH", -"SYI","SYJ","SYK","SYL","SYM","SYN","SYO","SYP","SYQ","SYR","SYS","SYT","SYU","SYV","SYW","SYX", -"SYY","SYZ","SZA","SZB","SZC","SZD","SZE","SZF","SZG","SZH","SZI","SZJ","SZK","SZL","SZM","SZN", -"SZO","SZP","SZQ","SZR","SZS","SZT","SZU","SZV","SZW","SZX","SZY","SZZ", -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - TAA to TTV - 516 triplets - intentionally omitted - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -"TTW","TTX","TTY","TTZ","TUA","TUB","TUC","TUD","TUE","TUF","TUG","TUH","TUI","TUJ","TUK","TUL", -"TUM","TUN","TUO","TUP","TUQ","TUR","TUS","TUT","TUU","TUV","TUW","TUX","TUY","TUZ","TVA","TVB", -"TVC","TVD","TVE","TVF","TVG","TVH","TVI","TVJ","TVK","TVL","TVM","TVN","TVO","TVP","TVQ","TVR", -"TVS","TVT","TVU","TVV","TVW","TVX","TVY","TVZ","TWA","TWB","TWC","TWD","TWE","TWF","TWG","TWH", -"TWI","TWJ","TWK","TWL","TWM","TWN","TWO","TWP","TWQ","TWR","TWS","TWT","TWU","TWV","TWW","TWX", -"TWY","TWZ","TXA","TXB","TXC","TXD","TXE","TXF","TXG","TXH","TXI","TXJ","TXK","TXL","TXM","TXN", -"TXO","TXP","TXQ","TXR","TXS","TXT","TXU","TXV","TXW","TXX","TXY","TXZ","TYA","TYB","TYC","TYD", -"TYE","TYF","TYG","TYH","TYI","TYJ","TYK","TYL","TYM","TYN","TYO","TYP","TYQ","TYR","TYS","TYT", -"TYU","TYV","TYW","TYX","TYY","TYZ","TZA","TZB","TZC","TZD","TZE","TZF","TZG","TZH","TZI","TZJ", -"TZK","TZL","TZM","TZN","TZO","TZP","TZQ","TZR","TZS","TZT","TZU","TZV","TZW","TZX","TZY","TZZ", -"UAA","UAB","UAC","UAD","UAE","UAF","UAG","UAH","UAI","UAJ","UAK","UAL","UAM","UAN","UAO","UAP", -"UAQ","UAR","UAS","UAT","UAU","UAV","UAW","UAX","UAY","UAZ","UBA","UBB","UBC","UBD","UBE","UBF", -"UBG","UBH","UBI","UBJ","UBK","UBL","UBM","UBN","UBO","UBP","UBQ","UBR","UBS","UBT","UBU","UBV", -"UBW","UBX","UBY","UBZ","UCA","UCB","UCC","UCD","UCE","UCF","UCG","UCH","UCI","UCJ","UCK","UCL", -"UCM","UCN","UCO","UCP","UCQ","UCR","UCS","UCT","UCU","UCV","UCW","UCX","UCY","UCZ","UDA","UDB", -"UDC","UDD","UDE","UDF","UDG","UDH","UDI","UDJ","UDK","UDL","UDM","UDN","UDO","UDP","UDQ","UDR", -"UDS","UDT","UDU","UDV","UDW","UDX","UDY","UDZ","UEA","UEB","UEC","UED","UEE","UEF","UEG","UEH", -"UEI","UEJ","UEK","UEL","UEM","UEN","UEO","UEP","UEQ","UER","UES","UET","UEU","UEV","UEW","UEX", -"UEY","UEZ","UFA","UFB","UFC","UFD","UFE","UFF","UFG","UFH","UFI","UFJ","UFK","UFL","UFM","UFN", -"UFO","UFP","UFQ","UFR","UFS","UFT","UFU","UFV","UFW","UFX","UFY","UFZ","UGA","UGB","UGC","UGD", -"UGE","UGF","UGG","UGH","UGI","UGJ","UGK","UGL","UGM","UGN","UGO","UGP","UGQ","UGR","UGS","UGT", -"UGU","UGV","UGW","UGX","UGY","UGZ","UHA","UHB","UHC","UHD","UHE","UHF","UHG","UHH","UHI","UHJ", -"UHK","UHL","UHM","UHN","UHO","UHP","UHQ","UHR","UHS","UHT","UHU","UHV","UHW","UHX","UHY","UHZ", -"UIA","UIB","UIC","UID","UIE","UIF","UIG","UIH","UII","UIJ","UIK","UIL","UIM","UIN","UIO","UIP", -"UIQ","UIR","UIS","UIT","UIU","UIV","UIW","UIX","UIY","UIZ","UJA","UJB","UJC","UJD","UJE","UJF", -"UJG","UJH","UJI","UJJ","UJK","UJL","UJM","UJN","UJO","UJP","UJQ","UJR","UJS","UJT","UJU","UJV", -"UJW","UJX","UJY","UJZ","UKA","UKB","UKC","UKD","UKE","UKF","UKG","UKH","UKI","UKJ","UKK","UKL", -"UKM","UKN","UKO","UKP","UKQ","UKR","UKS","UKT","UKU","UKV","UKW","UKX","UKY","UKZ","ULA","ULB", -"ULC","ULD","ULE","ULF","ULG","ULH","ULI","ULJ","ULK","ULL","ULM","ULN","ULO","ULP","ULQ","ULR", -"ULS","ULT","ULU","ULV","ULW","ULX","ULY","ULZ","UMA","UMB","UMC","UMD","UME","UMF","UMG","UMH", -"UMI","UMJ","UMK","UML","UMM","UMN","UMO","UMP","UMQ","UMR","UMS","UMT","UMU","UMV","UMW","UMX", -"UMY","UMZ","UNA","UNB","UNC","UND","UNE","UNF","UNG","UNH","UNI","UNJ","UNK","UNL","UNM","UNN", -"UNO","UNP","UNQ","UNR","UNS","UNT","UNU","UNV","UNW","UNX","UNY","UNZ","UOA","UOB","UOC","UOD", -"UOE","UOF","UOG","UOH","UOI","UOJ","UOK","UOL","UOM","UON","UOO","UOP","UOQ","UOR","UOS","UOT", -"UOU","UOV","UOW","UOX","UOY","UOZ","UPA","UPB","UPC","UPD","UPE","UPF","UPG","UPH","UPI","UPJ", -"UPK","UPL","UPM","UPN","UPO","UPP","UPQ","UPR","UPS","UPT","UPU","UPV","UPW","UPX","UPY","UPZ", -"UQA","UQB","UQC","UQD","UQE","UQF","UQG","UQH","UQI","UQJ","UQK","UQL","UQM","UQN","UQO","UQP", -"UQQ","UQR","UQS","UQT","UQU","UQV","UQW","UQX","UQY","UQZ","URA","URB","URC","URD","URE","URF", -"URG","URH","URI","URJ","URK","URL","URM","URN","URO","URP","URQ","URR","URS","URT","URU","URV", -"URW","URX","URY","URZ","USA","USB","USC","USD","USE","USF","USG","USH","USI","USJ","USK","USL", -"USM","USN","USO","USP","USQ","USR","USS","UST","USU","USV","USW","USX","USY","USZ","UTA","UTB", -"UTC","UTD","UTE","UTF","UTG","UTH","UTI","UTJ","UTK","UTL","UTM","UTN","UTO","UTP","UTQ","UTR", -"UTS","UTT","UTU","UTV","UTW","UTX","UTY","UTZ","UUA","UUB","UUC","UUD","UUE","UUF","UUG","UUH", -"UUI","UUJ","UUK","UUL","UUM","UUN","UUO","UUP","UUQ","UUR","UUS","UUT","UUU","UUV","UUW","UUX", -"UUY","UUZ","UVA","UVB","UVC","UVD","UVE","UVF","UVG","UVH","UVI","UVJ","UVK","UVL","UVM","UVN", -"UVO","UVP","UVQ","UVR","UVS","UVT","UVU","UVV","UVW","UVX","UVY","UVZ","UWA","UWB","UWC","UWD", -"UWE","UWF","UWG","UWH","UWI","UWJ","UWK","UWL","UWM","UWN","UWO","UWP","UWQ","UWR","UWS","UWT", -"UWU","UWV","UWW","UWX","UWY","UWZ","UXA","UXB","UXC","UXD","UXE","UXF","UXG","UXH","UXI","UXJ", -"UXK","UXL","UXM","UXN","UXO","UXP","UXQ","UXR","UXS","UXT","UXU","UXV","UXW","UXX","UXY","UXZ", -"UYA","UYB","UYC","UYD","UYE","UYF","UYG","UYH","UYI","UYJ","UYK","UYL","UYM","UYN","UYO","UYP", -"UYQ","UYR","UYS","UYT","UYU","UYV","UYW","UYX","UYY","UYZ","UZA","UZB","UZC","UZD","UZE","UZF", -"UZG","UZH","UZI","UZJ","UZK","UZL","UZM","UZN","UZO","UZP","UZQ","UZR","UZS","UZT","UZU","UZV", -"UZW","UZX","UZY","UZZ","VAA","VAB","VAC","VAD","VAE","VAF","VAG","VAH","VAI","VAJ","VAK","VAL", -"VAM","VAN","VAO","VAP","VAQ","VAR","VAS","VAT","VAU","VAV","VAW","VAX","VAY","VAZ","VBA","VBB", -"VBC","VBD","VBE","VBF","VBG","VBH","VBI","VBJ","VBK","VBL","VBM","VBN","VBO","VBP","VBQ","VBR", -"VBS","VBT","VBU","VBV","VBW","VBX","VBY","VBZ","VCA","VCB","VCC","VCD","VCE","VCF","VCG","VCH", -"VCI","VCJ","VCK","VCL","VCM","VCN","VCO","VCP","VCQ","VCR","VCS","VCT","VCU","VCV","VCW","VCX", -"VCY","VCZ","VDA","VDB","VDC","VDD","VDE","VDF","VDG","VDH","VDI","VDJ","VDK","VDL","VDM","VDN", -"VDO","VDP","VDQ","VDR","VDS","VDT","VDU","VDV","VDW","VDX","VDY","VDZ","VEA","VEB","VEC","VED", -"VEE","VEF","VEG","VEH","VEI","VEJ","VEK","VEL","VEM","VEN","VEO","VEP","VEQ","VER","VES","VET", -"VEU","VEV","VEW","VEX","VEY","VEZ","VFA","VFB","VFC","VFD","VFE","VFF","VFG","VFH","VFI","VFJ", -"VFK","VFL","VFM","VFN","VFO","VFP","VFQ","VFR","VFS","VFT","VFU","VFV","VFW","VFX","VFY","VFZ", -"VGA","VGB","VGC","VGD","VGE","VGF","VGG","VGH","VGI","VGJ","VGK","VGL","VGM","VGN","VGO","VGP", -"VGQ","VGR","VGS","VGT","VGU","VGV","VGW","VGX","VGY","VGZ","VHA","VHB","VHC","VHD","VHE","VHF", -"VHG","VHH","VHI","VHJ","VHK","VHL","VHM","VHN","VHO","VHP","VHQ","VHR","VHS","VHT","VHU","VHV", -"VHW","VHX","VHY","VHZ","VIA","VIB","VIC","VID","VIE","VIF","VIG","VIH","VII","VIJ","VIK","VIL", -"VIM","VIN","VIO","VIP","VIQ","VIR","VIS","VIT","VIU","VIV","VIW","VIX","VIY","VIZ","VJA","VJB", -"VJC","VJD","VJE","VJF","VJG","VJH","VJI","VJJ","VJK","VJL","VJM","VJN","VJO","VJP","VJQ","VJR", -"VJS","VJT","VJU","VJV","VJW","VJX","VJY","VJZ","VKA","VKB","VKC","VKD","VKE","VKF","VKG","VKH", -"VKI","VKJ","VKK","VKL","VKM","VKN","VKO","VKP","VKQ","VKR","VKS","VKT","VKU","VKV","VKW","VKX", -"VKY","VKZ","VLA","VLB","VLC","VLD","VLE","VLF","VLG","VLH","VLI","VLJ","VLK","VLL","VLM","VLN", -"VLO","VLP","VLQ","VLR","VLS","VLT","VLU","VLV","VLW","VLX","VLY","VLZ","VMA","VMB","VMC","VMD", -"VME","VMF","VMG","VMH","VMI","VMJ","VMK","VML","VMM","VMN","VMO","VMP","VMQ","VMR","VMS","VMT", -"VMU","VMV","VMW","VMX","VMY","VMZ","VNA","VNB","VNC","VND","VNE","VNF","VNG","VNH","VNI","VNJ", -"VNK","VNL","VNM","VNN","VNO","VNP","VNQ","VNR","VNS","VNT","VNU","VNV","VNW","VNX","VNY","VNZ", -"VOA","VOB","VOC","VOD","VOE","VOF","VOG","VOH","VOI","VOJ","VOK","VOL","VOM","VON","VOO","VOP", -"VOQ","VOR","VOS","VOT","VOU","VOV","VOW","VOX","VOY","VOZ","VPA","VPB","VPC","VPD","VPE","VPF", -"VPG","VPH","VPI","VPJ","VPK","VPL","VPM","VPN","VPO","VPP","VPQ","VPR","VPS","VPT","VPU","VPV", -"VPW","VPX","VPY","VPZ","VQA","VQB","VQC","VQD","VQE","VQF","VQG","VQH","VQI","VQJ","VQK","VQL", -"VQM","VQN","VQO","VQP","VQQ","VQR","VQS","VQT","VQU","VQV","VQW","VQX","VQY","VQZ","VRA","VRB", -"VRC","VRD","VRE","VRF","VRG","VRH","VRI","VRJ","VRK","VRL","VRM","VRN","VRO","VRP","VRQ","VRR", -"VRS","VRT","VRU","VRV","VRW","VRX","VRY","VRZ","VSA","VSB","VSC","VSD","VSE","VSF","VSG","VSH", -"VSI","VSJ","VSK","VSL","VSM","VSN","VSO","VSP","VSQ","VSR","VSS","VST","VSU","VSV","VSW","VSX", -"VSY","VSZ","VTA","VTB","VTC","VTD","VTE","VTF","VTG","VTH","VTI","VTJ","VTK","VTL","VTM","VTN", -"VTO","VTP","VTQ","VTR","VTS","VTT","VTU","VTV","VTW","VTX","VTY","VTZ","VUA","VUB","VUC","VUD", -"VUE","VUF","VUG","VUH","VUI","VUJ","VUK","VUL","VUM","VUN","VUO","VUP","VUQ","VUR","VUS","VUT", -"VUU","VUV","VUW","VUX","VUY","VUZ","VVA","VVB","VVC","VVD","VVE","VVF","VVG","VVH","VVI","VVJ", -"VVK","VVL","VVM","VVN","VVO","VVP","VVQ","VVR","VVS","VVT","VVU","VVV","VVW","VVX","VVY","VVZ", -"VWA","VWB","VWC","VWD","VWE","VWF","VWG","VWH","VWI","VWJ","VWK","VWL","VWM","VWN","VWO","VWP", -"VWQ","VWR","VWS","VWT","VWU","VWV","VWW","VWX","VWY","VWZ","VXA","VXB","VXC","VXD","VXE","VXF", -"VXG","VXH","VXI","VXJ","VXK","VXL","VXM","VXN","VXO","VXP","VXQ","VXR","VXS","VXT","VXU","VXV", -"VXW","VXX","VXY","VXZ","VYA","VYB","VYC","VYD","VYE","VYF","VYG","VYH","VYI","VYJ","VYK","VYL", -"VYM","VYN","VYO","VYP","VYQ","VYR","VYS","VYT","VYU","VYV","VYW","VYX","VYY","VYZ","VZA","VZB", -"VZC","VZD","VZE","VZF","VZG","VZH","VZI","VZJ","VZK","VZL","VZM","VZN","VZO","VZP","VZQ","VZR", -"VZS","VZT","VZU","VZV","VZW","VZX","VZY","VZZ","WAA","WAB","WAC","WAD","WAE","WAF","WAG","WAH", -"WAI","WAJ","WAK","WAL","WAM","WAN","WAO","WAP","WAQ","WAR","WAS","WAT","WAU","WAV","WAW","WAX", -"WAY","WAZ","WBA","WBB","WBC","WBD","WBE","WBF","WBG","WBH","WBI","WBJ","WBK","WBL","WBM","WBN", -"WBO","WBP","WBQ","WBR","WBS","WBT","WBU","WBV","WBW","WBX","WBY","WBZ","WCA","WCB","WCC","WCD", -"WCE","WCF","WCG","WCH","WCI","WCJ","WCK","WCL","WCM","WCN","WCO","WCP","WCQ","WCR","WCS","WCT", -"WCU","WCV","WCW","WCX","WCY","WCZ","WDA","WDB","WDC","WDD","WDE","WDF","WDG","WDH","WDI","WDJ", -"WDK","WDL","WDM","WDN","WDO","WDP","WDQ","WDR","WDS","WDT","WDU","WDV","WDW","WDX","WDY","WDZ", -"WEA","WEB","WEC","WED","WEE","WEF","WEG","WEH","WEI","WEJ","WEK","WEL","WEM","WEN","WEO","WEP", -"WEQ","WER","WES","WET","WEU","WEV","WEW","WEX","WEY","WEZ","WFA","WFB","WFC","WFD","WFE","WFF", -"WFG","WFH","WFI","WFJ","WFK","WFL","WFM","WFN","WFO","WFP","WFQ","WFR","WFS","WFT","WFU","WFV", -"WFW","WFX","WFY","WFZ","WGA","WGB","WGC","WGD","WGE","WGF","WGG","WGH","WGI","WGJ","WGK","WGL", -"WGM","WGN","WGO","WGP","WGQ","WGR","WGS","WGT","WGU","WGV","WGW","WGX","WGY","WGZ","WHA","WHB", -"WHC","WHD","WHE","WHF","WHG","WHH","WHI","WHJ","WHK","WHL","WHM","WHN","WHO","WHP","WHQ","WHR", -"WHS","WHT","WHU","WHV","WHW","WHX","WHY","WHZ","WIA","WIB","WIC","WID","WIE","WIF","WIG","WIH", -"WII","WIJ","WIK","WIL","WIM","WIN","WIO","WIP","WIQ","WIR","WIS","WIT","WIU","WIV","WIW","WIX", -"WIY","WIZ","WJA","WJB","WJC","WJD","WJE","WJF","WJG","WJH","WJI","WJJ","WJK","WJL","WJM","WJN", -"WJO","WJP","WJQ","WJR","WJS","WJT","WJU","WJV","WJW","WJX","WJY","WJZ","WKA","WKB","WKC","WKD", -"WKE","WKF","WKG","WKH","WKI","WKJ","WKK","WKL","WKM","WKN","WKO","WKP","WKQ","WKR","WKS","WKT", -"WKU","WKV","WKW","WKX","WKY","WKZ","WLA","WLB","WLC","WLD","WLE","WLF","WLG","WLH","WLI","WLJ", -"WLK","WLL","WLM","WLN","WLO","WLP","WLQ","WLR","WLS","WLT","WLU","WLV","WLW","WLX","WLY","WLZ", -"WMA","WMB","WMC","WMD","WME","WMF","WMG","WMH","WMI","WMJ","WMK","WML","WMM","WMN","WMO","WMP", -"WMQ","WMR","WMS","WMT","WMU","WMV","WMW","WMX","WMY","WMZ","WNA","WNB","WNC","WND","WNE","WNF", -"WNG","WNH","WNI","WNJ","WNK","WNL","WNM","WNN","WNO","WNP","WNQ","WNR","WNS","WNT","WNU","WNV", -"WNW","WNX","WNY","WNZ","WOA","WOB","WOC","WOD","WOE","WOF","WOG","WOH","WOI","WOJ","WOK","WOL", -"WOM","WON","WOO","WOP","WOQ","WOR","WOS","WOT","WOU","WOV","WOW","WOX","WOY","WOZ","WPA","WPB", -"WPC","WPD","WPE","WPF","WPG","WPH","WPI","WPJ","WPK","WPL","WPM","WPN","WPO","WPP","WPQ","WPR", -"WPS","WPT","WPU","WPV","WPW","WPX","WPY","WPZ","WQA","WQB","WQC","WQD","WQE","WQF","WQG","WQH", -"WQI","WQJ","WQK","WQL","WQM","WQN","WQO","WQP","WQQ","WQR","WQS","WQT","WQU","WQV","WQW","WQX", -"WQY","WQZ","WRA","WRB","WRC","WRD","WRE","WRF","WRG","WRH","WRI","WRJ","WRK","WRL","WRM","WRN", -"WRO","WRP","WRQ","WRR","WRS","WRT","WRU","WRV","WRW","WRX","WRY","WRZ","WSA","WSB","WSC","WSD", -"WSE","WSF","WSG","WSH","WSI","WSJ","WSK","WSL","WSM","WSN","WSO","WSP","WSQ","WSR","WSS","WST", -"WSU","WSV","WSW","WSX","WSY","WSZ","WTA","WTB","WTC","WTD","WTE","WTF","WTG","WTH","WTI","WTJ", -"WTK","WTL","WTM","WTN","WTO","WTP","WTQ","WTR","WTS","WTT","WTU","WTV","WTW","WTX","WTY","WTZ", -"WUA","WUB","WUC","WUD","WUE","WUF","WUG","WUH","WUI","WUJ","WUK","WUL","WUM","WUN","WUO","WUP", -"WUQ","WUR","WUS","WUT","WUU","WUV","WUW","WUX","WUY","WUZ","WVA","WVB","WVC","WVD","WVE","WVF", -"WVG","WVH","WVI","WVJ","WVK","WVL","WVM","WVN","WVO","WVP","WVQ","WVR","WVS","WVT","WVU","WVV", -"WVW","WVX","WVY","WVZ","WWA","WWB","WWC","WWD","WWE","WWF","WWG","WWH","WWI","WWJ","WWK","WWL", -"WWM","WWN","WWO","WWP","WWQ","WWR","WWS","WWT","WWU","WWV","WWW","WWX","WWY","WWZ","WXA","WXB", -"WXC","WXD","WXE","WXF","WXG","WXH","WXI","WXJ","WXK","WXL","WXM","WXN","WXO","WXP","WXQ","WXR", -"WXS","WXT","WXU","WXV","WXW","WXX","WXY","WXZ","WYA","WYB","WYC","WYD","WYE","WYF","WYG","WYH", -"WYI","WYJ","WYK","WYL","WYM","WYN","WYO","WYP","WYQ","WYR","WYS","WYT","WYU","WYV","WYW","WYX", -"WYY","WYZ","WZA","WZB","WZC","WZD","WZE","WZF","WZG","WZH","WZI","WZJ","WZK","WZL","WZM","WZN", -"WZO","WZP","WZQ","WZR","WZS","WZT","WZU","WZV","WZW","WZX","WZY","WZZ","XAA","XAB","XAC","XAD", -"XAE","XAF","XAG","XAH","XAI","XAJ","XAK","XAL","XAM","XAN","XAO","XAP","XAQ","XAR","XAS","XAT", -"XAU","XAV","XAW","XAX","XAY","XAZ","XBA","XBB","XBC","XBD","XBE","XBF","XBG","XBH","XBI","XBJ", -"XBK","XBL","XBM","XBN","XBO","XBP","XBQ","XBR","XBS","XBT","XBU","XBV","XBW","XBX","XBY","XBZ", -"XCA","XCB","XCC","XCD","XCE","XCF","XCG","XCH","XCI","XCJ","XCK","XCL","XCM","XCN","XCO","XCP", -"XCQ","XCR","XCS","XCT","XCU","XCV","XCW","XCX","XCY","XCZ","XDA","XDB","XDC","XDD","XDE","XDF", -"XDG","XDH","XDI","XDJ","XDK","XDL","XDM","XDN","XDO","XDP","XDQ","XDR","XDS","XDT","XDU","XDV", -"XDW","XDX","XDY","XDZ","XEA","XEB","XEC","XED","XEE","XEF","XEG","XEH","XEI","XEJ","XEK","XEL", -"XEM","XEN","XEO","XEP","XEQ","XER","XES","XET","XEU","XEV","XEW","XEX","XEY","XEZ","XFA","XFB", -"XFC","XFD","XFE","XFF","XFG","XFH","XFI","XFJ","XFK","XFL","XFM","XFN","XFO","XFP","XFQ","XFR", -"XFS","XFT","XFU","XFV","XFW","XFX","XFY","XFZ","XGA","XGB","XGC","XGD","XGE","XGF","XGG","XGH", -"XGI","XGJ","XGK","XGL","XGM","XGN","XGO","XGP","XGQ","XGR","XGS","XGT","XGU","XGV","XGW","XGX", -"XGY","XGZ","XHA","XHB","XHC","XHD","XHE","XHF","XHG","XHH","XHI","XHJ","XHK","XHL","XHM","XHN", -"XHO","XHP","XHQ","XHR","XHS","XHT","XHU","XHV","XHW","XHX","XHY","XHZ","XIA","XIB","XIC","XID", -"XIE","XIF","XIG","XIH","XII","XIJ","XIK","XIL","XIM","XIN","XIO","XIP","XIQ","XIR","XIS","XIT", -"XIU","XIV","XIW","XIX","XIY","XIZ","XJA","XJB","XJC","XJD","XJE","XJF","XJG","XJH","XJI","XJJ", -"XJK","XJL","XJM","XJN","XJO","XJP","XJQ","XJR","XJS","XJT","XJU","XJV","XJW","XJX","XJY","XJZ", -"XKA","XKB","XKC","XKD","XKE","XKF","XKG","XKH","XKI","XKJ","XKK","XKL","XKM","XKN","XKO","XKP", -"XKQ","XKR","XKS","XKT","XKU","XKV","XKW","XKX","XKY","XKZ","XLA","XLB","XLC","XLD","XLE","XLF", -"XLG","XLH","XLI","XLJ","XLK","XLL","XLM","XLN","XLO","XLP","XLQ","XLR","XLS","XLT","XLU","XLV", -"XLW","XLX","XLY","XLZ","XMA","XMB","XMC","XMD","XME","XMF","XMG","XMH","XMI","XMJ","XMK","XML", -"XMM","XMN","XMO","XMP","XMQ","XMR","XMS","XMT","XMU","XMV","XMW","XMX","XMY","XMZ","XNA","XNB", -"XNC","XND","XNE","XNF","XNG","XNH","XNI","XNJ","XNK","XNL","XNM","XNN","XNO","XNP","XNQ","XNR", -"XNS","XNT","XNU","XNV","XNW","XNX","XNY","XNZ","XOA","XOB","XOC","XOD","XOE","XOF","XOG","XOH", -"XOI","XOJ","XOK","XOL","XOM","XON","XOO","XOP","XOQ","XOR","XOS","XOT","XOU","XOV","XOW","XOX", -"XOY","XOZ","XPA","XPB","XPC","XPD","XPE","XPF","XPG","XPH","XPI","XPJ","XPK","XPL","XPM","XPN", -"XPO","XPP","XPQ","XPR","XPS","XPT","XPU","XPV","XPW","XPX","XPY","XPZ","XQA","XQB","XQC","XQD", -"XQE","XQF","XQG","XQH","XQI","XQJ","XQK","XQL","XQM","XQN","XQO","XQP","XQQ","XQR","XQS","XQT", -"XQU","XQV","XQW","XQX","XQY","XQZ","XRA","XRB","XRC","XRD","XRE","XRF","XRG","XRH","XRI","XRJ", -"XRK","XRL","XRM","XRN","XRO","XRP","XRQ","XRR","XRS","XRT","XRU","XRV","XRW","XRX","XRY","XRZ", -"XSA","XSB","XSC","XSD","XSE","XSF","XSG","XSH","XSI","XSJ","XSK","XSL","XSM","XSN","XSO","XSP", -"XSQ","XSR","XSS","XST","XSU","XSV","XSW","XSX","XSY","XSZ","XTA","XTB","XTC","XTD","XTE","XTF", -"XTG","XTH","XTI","XTJ","XTK","XTL","XTM","XTN","XTO","XTP","XTQ","XTR","XTS","XTT","XTU","XTV", -"XTW","XTX","XTY","XTZ","XUA","XUB","XUC","XUD","XUE","XUF","XUG","XUH","XUI","XUJ","XUK","XUL", -"XUM","XUN","XUO","XUP","XUQ","XUR","XUS","XUT","XUU","XUV","XUW","XUX","XUY","XUZ","XVA","XVB", -"XVC","XVD","XVE","XVF","XVG","XVH","XVI","XVJ","XVK","XVL","XVM","XVN","XVO","XVP","XVQ","XVR", -"XVS","XVT","XVU","XVV","XVW","XVX","XVY","XVZ","XWA","XWB","XWC","XWD","XWE","XWF","XWG","XWH", -"XWI","XWJ","XWK","XWL","XWM","XWN","XWO","XWP","XWQ","XWR","XWS","XWT","XWU","XWV","XWW","XWX", -"XWY","XWZ","XXA","XXB","XXC","XXD","XXE","XXF","XXG","XXH","XXI","XXJ","XXK","XXL","XXM","XXN", -"XXO","XXP","XXQ","XXR","XXS","XXT","XXU","XXV","XXW","XXX","XXY","XXZ","XYA","XYB","XYC","XYD", -"XYE","XYF","XYG","XYH","XYI","XYJ","XYK","XYL","XYM","XYN","XYO","XYP","XYQ","XYR","XYS","XYT", -"XYU","XYV","XYW","XYX","XYY","XYZ","XZA","XZB","XZC","XZD","XZE","XZF","XZG","XZH","XZI","XZJ", -"XZK","XZL","XZM","XZN","XZO","XZP","XZQ","XZR","XZS","XZT","XZU","XZV","XZW","XZX","XZY","XZZ", - -"YAA","YAB","YAC","YAD", -"YAE","YAF","YAG","YAH","YAI","YAJ","YAK","YAL","YAM","YAN","YAO","YAP","YAQ","YAR","YAS","YAT", -"YAU","YAV","YAW","YAX","YAY","YAZ","YBA","YBB","YBC","YBD","YBE","YBF","YBG","YBH","YBI","YBJ", -"YBK","YBL","YBM","YBN","YBO","YBP","YBQ","YBR","YBS","YBT","YBU","YBV","YBW","YBX","YBY","YBZ", -"YCA","YCB","YCC","YCD","YCE","YCF","YCG","YCH","YCI","YCJ","YCK","YCL","YCM","YCN","YCO","YCP", -"YCQ","YCR","YCS","YCT","YCU","YCV","YCW","YCX","YCY","YCZ","YDA","YDB","YDC","YDD","YDE","YDF", -"YDG","YDH","YDI","YDJ","YDK","YDL","YDM","YDN","YDO","YDP","YDQ","YDR","YDS","YDT","YDU","YDV", -"YDW","YDX","YDY","YDZ","YEA","YEB","YEC","YED","YEE","YEF","YEG","YEH","YEI","YEJ","YEK","YEL", -"YEM","YEN","YEO","YEP","YEQ","YER","YES","YET","YEU","YEV","YEW","YEX","YEY","YEZ","YFA","YFB", -"YFC","YFD","YFE","YFF","YFG","YFH","YFI","YFJ","YFK","YFL","YFM","YFN","YFO","YFP","YFQ","YFR", -"YFS","YFT","YFU","YFV","YFW","YFX","YFY","YFZ","YGA","YGB","YGC","YGD","YGE","YGF","YGG","YGH", -"YGI","YGJ","YGK","YGL","YGM","YGN","YGO","YGP","YGQ","YGR","YGS","YGT","YGU","YGV","YGW","YGX", -"YGY","YGZ","YHA","YHB","YHC","YHD","YHE","YHF","YHG","YHH","YHI","YHJ","YHK","YHL","YHM","YHN", -"YHO","YHP","YHQ","YHR","YHS","YHT","YHU","YHV","YHW","YHX","YHY","YHZ","YIA","YIB","YIC","YID", -"YIE","YIF","YIG","YIH","YII","YIJ","YIK","YIL","YIM","YIN","YIO","YIP","YIQ","YIR","YIS","YIT", -"YIU","YIV","YIW","YIX","YIY","YIZ","YJA","YJB","YJC","YJD","YJE","YJF","YJG","YJH","YJI","YJJ", -"YJK","YJL","YJM","YJN","YJO","YJP","YJQ","YJR","YJS","YJT","YJU","YJV","YJW","YJX","YJY","YJZ", -"YKA","YKB","YKC","YKD","YKE","YKF","YKG","YKH","YKI","YKJ","YKK","YKL","YKM","YKN","YKO","YKP", -"YKQ","YKR","YKS","YKT","YKU","YKV","YKW","YKX","YKY","YKZ","YLA","YLB","YLC","YLD","YLE","YLF", -"YLG","YLH","YLI","YLJ","YLK","YLL","YLM","YLN","YLO","YLP","YLQ","YLR","YLS","YLT","YLU","YLV", -"YLW","YLX","YLY","YLZ","YMA","YMB","YMC","YMD","YME","YMF","YMG","YMH","YMI","YMJ","YMK","YML", -"YMM","YMN","YMO","YMP","YMQ","YMR","YMS","YMT","YMU","YMV","YMW","YMX","YMY","YMZ","YNA","YNB", -"YNC","YND","YNE","YNF","YNG","YNH","YNI","YNJ","YNK","YNL","YNM","YNN","YNO","YNP","YNQ","YNR", -"YNS","YNT","YNU","YNV","YNW","YNX","YNY","YNZ","YOA","YOB","YOC","YOD","YOE","YOF","YOG","YOH", -"YOI","YOJ","YOK","YOL","YOM","YON","YOO","YOP","YOQ","YOR","YOS","YOT","YOU","YOV","YOW","YOX", -"YOY","YOZ","YPA","YPB","YPC","YPD","YPE","YPF","YPG","YPH","YPI","YPJ","YPK","YPL","YPM","YPN", -"YPO","YPP","YPQ","YPR","YPS","YPT","YPU","YPV","YPW","YPX","YPY","YPZ","YQA","YQB","YQC","YQD", -"YQE","YQF","YQG","YQH","YQI","YQJ","YQK","YQL","YQM","YQN","YQO","YQP","YQQ","YQR","YQS","YQT", -"YQU","YQV","YQW","YQX","YQY","YQZ","YRA","YRB","YRC","YRD","YRE","YRF","YRG","YRH","YRI","YRJ", -"YRK","YRL","YRM","YRN","YRO","YRP","YRQ","YRR","YRS","YRT","YRU","YRV","YRW","YRX","YRY","YRZ", -"YSA","YSB","YSC","YSD","YSE","YSF","YSG","YSH","YSI","YSJ","YSK","YSL","YSM","YSN","YSO","YSP", -"YSQ","YSR","YSS","YST","YSU","YSV","YSW","YSX","YSY","YSZ","YTA","YTB","YTC","YTD","YTE","YTF", -"YTG","YTH","YTI","YTJ","YTK","YTL","YTM","YTN","YTO","YTP","YTQ","YTR","YTS","YTT","YTU","YTV", -"YTW","YTX","YTY","YTZ","YUA","YUB","YUC","YUD","YUE","YUF","YUG","YUH","YUI","YUJ","YUK","YUL", -"YUM","YUN","YUO","YUP","YUQ","YUR","YUS","YUT","YUU","YUV","YUW","YUX","YUY","YUZ","YVA","YVB", -"YVC","YVD","YVE","YVF","YVG","YVH","YVI","YVJ","YVK","YVL","YVM","YVN","YVO","YVP","YVQ","YVR", -"YVS","YVT","YVU","YVV","YVW","YVX","YVY","YVZ","YWA","YWB","YWC","YWD","YWE","YWF","YWG","YWH", -"YWI","YWJ","YWK","YWL","YWM","YWN","YWO","YWP","YWQ","YWR","YWS","YWT","YWU","YWV","YWW","YWX", -"YWY","YWZ","YXA","YXB","YXC","YXD","YXE","YXF","YXG","YXH","YXI","YXJ","YXK","YXL","YXM","YXN", -"YXO","YXP","YXQ","YXR","YXS","YXT","YXU","YXV","YXW","YXX","YXY","YXZ","YYA","YYB","YYC","YYD", -"YYE","YYF","YYG","YYH","YYI","YYJ","YYK","YYL","YYM","YYN","YYO","YYP","YYQ","YYR","YYS","YYT", -"YYU","YYV","YYW","YYX","YYY","YYZ","YZA","YZB","YZC","YZD","YZE","YZF","YZG","YZH","YZI","YZJ", -"YZK","YZL","YZM","YZN","YZO","YZP","YZQ","YZR","YZS","YZT","YZU","YZV","YZW","YZX","YZY","YZZ", - -"ZAA","ZAB","ZAC","ZAD", -"ZAE","ZAF","ZAG","ZAH","ZAI","ZAJ","ZAK","ZAL","ZAM","ZAN","ZAO","ZAP","ZAQ","ZAR","ZAS","ZAT", -"ZAU","ZAV","ZAW","ZAX","ZAY","ZAZ","ZBA","ZBB","ZBC","ZBD","ZBE","ZBF","ZBG","ZBH","ZBI","ZBJ", -"ZBK","ZBL","ZBM","ZBN","ZBO","ZBP","ZBQ","ZBR","ZBS","ZBT","ZBU","ZBV","ZBW","ZBX","ZBY","ZBZ", -"ZCA","ZCB","ZCC","ZCD","ZCE","ZCF","ZCG","ZCH","ZCI","ZCJ","ZCK","ZCL","ZCM","ZCN","ZCO","ZCP", -"ZCQ","ZCR","ZCS","ZCT","ZCU","ZCV","ZCW","ZCX","ZCY","ZCZ","ZDA","ZDB","ZDC","ZDD","ZDE","ZDF", -"ZDG","ZDH","ZDI","ZDJ","ZDK","ZDL","ZDM","ZDN","ZDO","ZDP","ZDQ","ZDR","ZDS","ZDT","ZDU","ZDV", -"ZDW","ZDX","ZDY","ZDZ","ZEA","ZEB","ZEC","ZED","ZEE","ZEF","ZEG","ZEH","ZEI","ZEJ","ZEK","ZEL", -"ZEM","ZEN","ZEO","ZEP","ZEQ","ZER","ZES","ZET","ZEU","ZEV","ZEW","ZEX","ZEY","ZEZ","ZFA","ZFB", -"ZFC","ZFD","ZFE","ZFF","ZFG","ZFH","ZFI","ZFJ","ZFK","ZFL","ZFM","ZFN","ZFO","ZFP","ZFQ","ZFR", -"ZFS","ZFT","ZFU","ZFV","ZFW","ZFX","ZFY","ZFZ","ZGA","ZGB","ZGC","ZGD","ZGE","ZGF","ZGG","ZGH", -"ZGI","ZGJ","ZGK","ZGL","ZGM","ZGN","ZGO","ZGP","ZGQ","ZGR","ZGS","ZGT","ZGU","ZGV","ZGW","ZGX", -"ZGY","ZGZ","ZHA","ZHB","ZHC","ZHD","ZHE","ZHF","ZHG","ZHH","ZHI","ZHJ","ZHK","ZHL","ZHM","ZHN", -"ZHO","ZHP","ZHQ","ZHR","ZHS","ZHT","ZHU","ZHV","ZHW","ZHX","ZHY","ZHZ","ZIA","ZIB","ZIC","ZID", -"ZIE","ZIF","ZIG","ZIH","ZII","ZIJ","ZIK","ZIL","ZIM","ZIN","ZIO","ZIP","ZIQ","ZIR","ZIS","ZIT", -"ZIU","ZIV","ZIW","ZIX","ZIY","ZIZ","ZJA","ZJB","ZJC","ZJD","ZJE","ZJF","ZJG","ZJH","ZJI","ZJJ", -"ZJK","ZJL","ZJM","ZJN","ZJO","ZJP","ZJQ","ZJR","ZJS","ZJT","ZJU","ZJV","ZJW","ZJX","ZJY","ZJZ", -"ZKA","ZKB","ZKC","ZKD","ZKE","ZKF","ZKG","ZKH","ZKI","ZKJ","ZKK","ZKL","ZKM","ZKN","ZKO","ZKP", -"ZKQ","ZKR","ZKS","ZKT","ZKU","ZKV","ZKW","ZKX","ZKY","ZKZ","ZLA","ZLB","ZLC","ZLD","ZLE","ZLF", -"ZLG","ZLH","ZLI","ZLJ","ZLK","ZLL","ZLM","ZLN","ZLO","ZLP","ZLQ","ZLR","ZLS","ZLT","ZLU","ZLV", -"ZLW","ZLX","ZLY","ZLZ","ZMA","ZMB","ZMC","ZMD","ZME","ZMF","ZMG","ZMH","ZMI","ZMJ","ZMK","ZML", -"ZMM","ZMN","ZMO","ZMP","ZMQ","ZMR","ZMS","ZMT","ZMU","ZMV","ZMW","ZMX","ZMY","ZMZ","ZNA","ZNB", -"ZNC","ZND","ZNE","ZNF","ZNG","ZNH","ZNI","ZNJ","ZNK","ZNL","ZNM","ZNN","ZNO","ZNP","ZNQ","ZNR", -"ZNS","ZNT","ZNU","ZNV","ZNW","ZNX","ZNY","ZNZ","ZOA","ZOB","ZOC","ZOD","ZOE","ZOF","ZOG","ZOH", -"ZOI","ZOJ","ZOK","ZOL","ZOM","ZON","ZOO","ZOP","ZOQ","ZOR","ZOS","ZOT","ZOU","ZOV","ZOW","ZOX", -"ZOY","ZOZ","ZPA","ZPB","ZPC","ZPD","ZPE","ZPF","ZPG","ZPH","ZPI","ZPJ","ZPK","ZPL","ZPM","ZPN", -"ZPO","ZPP","ZPQ","ZPR","ZPS","ZPT","ZPU","ZPV","ZPW","ZPX","ZPY","ZPZ","ZQA","ZQB","ZQC","ZQD", -"ZQE","ZQF","ZQG","ZQH","ZQI","ZQJ","ZQK","ZQL","ZQM","ZQN","ZQO","ZQP","ZQQ","ZQR","ZQS","ZQT", -"ZQU","ZQV","ZQW","ZQX","ZQY","ZQZ","ZRA","ZRB","ZRC","ZRD","ZRE","ZRF","ZRG","ZRH","ZRI","ZRJ", -"ZRK","ZRL","ZRM","ZRN","ZRO","ZRP","ZRQ","ZRR","ZRS","ZRT","ZRU","ZRV","ZRW","ZRX","ZRY","ZRZ", -"ZSA","ZSB","ZSC","ZSD","ZSE","ZSF","ZSG","ZSH","ZSI","ZSJ","ZSK","ZSL","ZSM","ZSN","ZSO","ZSP", -"ZSQ","ZSR","ZSS","ZST","ZSU","ZSV","ZSW","ZSX","ZSY","ZSZ","ZTA","ZTB","ZTC","ZTD","ZTE","ZTF", -"ZTG","ZTH","ZTI","ZTJ","ZTK","ZTL","ZTM","ZTN","ZTO","ZTP","ZTQ","ZTR","ZTS","ZTT","ZTU","ZTV", -"ZTW","ZTX","ZTY","ZTZ","ZUA","ZUB","ZUC","ZUD","ZUE","ZUF","ZUG","ZUH","ZUI","ZUJ","ZUK","ZUL", -"ZUM","ZUN","ZUO","ZUP","ZUQ","ZUR","ZUS","ZUT","ZUU","ZUV","ZUW","ZUX","ZUY","ZUZ","ZVA","ZVB", -"ZVC","ZVD","ZVE","ZVF","ZVG","ZVH","ZVI","ZVJ","ZVK","ZVL","ZVM","ZVN","ZVO","ZVP","ZVQ","ZVR", -"ZVS","ZVT","ZVU","ZVV","ZVW","ZVX","ZVY","ZVZ","ZWA","ZWB","ZWC","ZWD","ZWE","ZWF","ZWG","ZWH", -"ZWI","ZWJ","ZWK","ZWL","ZWM","ZWN","ZWO","ZWP","ZWQ","ZWR","ZWS","ZWT","ZWU","ZWV","ZWW","ZWX", -"ZWY","ZWZ","ZXA","ZXB","ZXC","ZXD","ZXE","ZXF","ZXG","ZXH","ZXI","ZXJ","ZXK","ZXL","ZXM","ZXN", -"ZXO","ZXP","ZXQ","ZXR","ZXS","ZXT","ZXU","ZXV","ZXW","ZXX","ZXY","ZXZ","ZYA","ZYB","ZYC","ZYD", -"ZYE","ZYF","ZYG","ZYH","ZYI","ZYJ","ZYK","ZYL","ZYM","ZYN","ZYO","ZYP","ZYQ","ZYR","ZYS","ZYT", -"ZYU","ZYV","ZYW","ZYX","ZYY","ZYZ","ZZA","ZZB","ZZC","ZZD","ZZE","ZZF","ZZG","ZZH","ZZI","ZZJ", -"ZZK","ZZL","ZZM","ZZN","ZZO","ZZP","ZZQ","ZZR","ZZS","ZZT","ZZU","ZZV","ZZW","ZZX","ZZY","ZZZ" -}; - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -static char d26[][3] = -{ -"AA","AB","AC","AD","AE","AF","AG","AH","AI","AJ","AK","AL","AM","AN","AO","AP", -"AQ","AR","AS","AT","AU","AV","AW","AX","AY","AZ","BA","BB","BC","BD","BE","BF", -"BG","BH","BI","BJ","BK","BL","BM","BN","BO","BP","BQ","BR","BS","BT","BU","BV", -"BW","BX","BY","BZ","CA","CB","CC","CD","CE","CF","CG","CH","CI","CJ","CK","CL", -"CM","CN","CO","CP","CQ","CR","CS","CT","CU","CV","CW","CX","CY","CZ","DA","DB", -"DC","DD","DE","DF","DG","DH","DI","DJ","DK","DL","DM","DN","DO","DP","DQ","DR", -"DS","DT","DU","DV","DW","DX","DY","DZ","EA","EB","EC","ED","EE","EF","EG","EH", -"EI","EJ","EK","EL","EM","EN","EO","EP","EQ","ER","ES","ET","EU","EV","EW","EX", -"EY","EZ","FA","FB","FC","FD","FE","FF","FG","FH","FI","FJ","FK","FL","FM","FN", -"FO","FP","FQ","FR","FS","FT","FU","FV","FW","FX","FY","FZ","GA","GB","GC","GD", -"GE","GF","GG","GH","GI","GJ","GK","GL","GM","GN","GO","GP","GQ","GR","GS","GT", -"GU","GV","GW","GX","GY","GZ","HA","HB","HC","HD","HE","HF","HG","HH","HI","HJ", -"HK","HL","HM","HN","HO","HP","HQ","HR","HS","HT","HU","HV","HW","HX","HY","HZ", -"IA","IB","IC","ID","IE","IF","IG","IH","II","IJ","IK","IL","IM","IN","IO","IP", -"IQ","IR","IS","IT","IU","IV","IW","IX","IY","IZ","JA","JB","JC","JD","JE","JF", -"JG","JH","JI","JJ","JK","JL","JM","JN","JO","JP","JQ","JR","JS","JT","JU","JV", -"JW","JX","JY","JZ","KA","KB","KC","KD","KE","KF","KG","KH","KI","KJ","KK","KL", -"KM","KN","KO","KP","KQ","KR","KS","KT","KU","KV","KW","KX","KY","KZ","LA","LB", -"LC","LD","LE","LF","LG","LH","LI","LJ","LK","LL","LM","LN","LO","LP","LQ","LR", -"LS","LT","LU","LV","LW","LX","LY","LZ","MA","MB","MC","MD","ME","MF","MG","MH", -"MI","MJ","MK","ML","MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV","MW","MX", -"MY","MZ","NA","NB","NC","ND","NE","NF","NG","NH","NI","NJ","NK","NL","NM","NN", -"NO","NP","NQ","NR","NS","NT","NU","NV","NW","NX","NY","NZ","OA","OB","OC","OD", -"OE","OF","OG","OH","OI","OJ","OK","OL","OM","ON","OO","OP","OQ","OR","OS","OT", -"OU","OV","OW","OX","OY","OZ","PA","PB","PC","PD","PE","PF","PG","PH","PI","PJ", -"PK","PL","PM","PN","PO","PP","PQ","PR","PS","PT","PU","PV","PW","PX","PY","PZ", -"QA","QB","QC","QD","QE","QF","QG","QH","QI","QJ","QK","QL","QM","QN","QO","QP", -"QQ","QR","QS","QT","QU","QV","QW","QX","QY","QZ","RA","RB","RC","RD","RE","RF", -"RG","RH","RI","RJ","RK","RL","RM","RN","RO","RP","RQ","RR","RS","RT","RU","RV", -"RW","RX","RY","RZ","SA","SB","SC","SD","SE","SF","SG","SH","SI","SJ","SK","SL", -"SM","SN","SO","SP","SQ","SR","SS","ST","SU","SV","SW","SX","SY","SZ","TA","TB", -"TC","TD","TE","TF","TG","TH","TI","TJ","TK","TL","TM","TN","TO","TP","TQ","TR", -"TS","TT","TU","TV","TW","TX","TY","TZ","UA","UB","UC","UD","UE","UF","UG","UH", -"UI","UJ","UK","UL","UM","UN","UO","UP","UQ","UR","US","UT","UU","UV","UW","UX", -"UY","UZ","VA","VB","VC","VD","VE","VF","VG","VH","VI","VJ","VK","VL","VM","VN", -"VO","VP","VQ","VR","VS","VT","VU","VV","VW","VX","VY","VZ","WA","WB","WC","WD", -"WE","WF","WG","WH","WI","WJ","WK","WL","WM","WN","WO","WP","WQ","WR","WS","WT", -"WU","WV","WW","WX","WY","WZ","XA","XB","XC","XD","XE","XF","XG","XH","XI","XJ", -"XK","XL","XM","XN","XO","XP","XQ","XR","XS","XT","XU","XV","XW","XX","XY","XZ", -"YA","YB","YC","YD","YE","YF","YG","YH","YI","YJ","YK","YL","YM","YN","YO","YP", -"YQ","YR","YS","YT","YU","YV","YW","YX","YY","YZ","ZA","ZB","ZC","ZD","ZE","ZF", -"ZG","ZH","ZI","ZJ","ZK","ZL","ZM","ZN","ZO","ZP","ZQ","ZR","ZS","ZT","ZU","ZV", -"ZW","ZX","ZY","ZZ" -}; - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Also tabulate 26 base-26 chars. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -static const char *c26 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; /* added const 2007-09-26 DT */ - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Weight scheme for check character . - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -#define N_UNIQUE_WEIGHTS 12 -static int weights_for_checksum[N_UNIQUE_WEIGHTS] = { 1,3,5,7,9,11,15,17,19,21,23,25 }; - /*^^^ co-primes with 26 which are < 26 */ - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get a character representing 1st 14-bit triplet (bits 0..13 of contiguous array of octets) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -const char* base26_triplet_1(const unsigned char *a) -{ -UINT32 b0, b1,h; - b0 = (UINT32) a[0]; /* 1111 1111 */ -#ifndef FIX_BASE26_ENC_BUG - b1 = (UINT32) ( a[1] & 0x3f ); /* 0011 1111 */ - h = (UINT32) ( b0 | b1 << 8 ); -#else - b1 = (UINT32) ( a[1] & 0xfc ); /* 1111 1100 */ - h = (UINT32) ( b0 << 8 | b1 ) >> 2; -#endif - return t26[h]; -} - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get a character representing 2nd 14-bit triplet (bits 14..27) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -const char* base26_triplet_2(const unsigned char *a) -{ -UINT32 b0, b1, b2, h; -#ifndef FIX_BASE26_ENC_BUG - b0 = (UINT32) ( a[1] & 0xc0); /* 1100 0000 */ - b1 = (UINT32) ( a[2] ); /* 1111 1111 */ - b2 = (UINT32) ( a[3] & 0x0f ); /* 0000 1111 */ - h = (UINT32)( b0 | b1 << 8 | b2 << 16 ) >> 6 ; -#else - b0 = (UINT32) ( a[1] & 0x03); /* 0000 0011 */ - b1 = (UINT32) ( a[2] ); /* 1111 1111 */ - b2 = (UINT32) ( a[3] & 0xf0 ); /* 1111 0000 */ - h = (UINT32)( b0 << 16 | b1 << 8 | b2 ) >> 4 ; -#endif - return t26[h]; -} - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get a character representing 3rd 14-bit triplet (bits 28..41) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -const char* base26_triplet_3(const unsigned char *a) -{ -UINT32 b0, b1, b2, h; -#ifndef FIX_BASE26_ENC_BUG - b0 = (UINT32) ( a[3] & 0xf0); /* 1111 0000 */ - b1 = (UINT32) ( a[4] ); /* 1111 1111 */ - b2 = (UINT32) ( a[5] & 0x03 ); /* 0000 0011 */ - h = (UINT32) ( b0 | b1 << 8 | b2 << 16 ) >> 4 ; -#else - b0 = (UINT32) ( a[3] & 0x0f); /* 0000 1111 */ - b1 = (UINT32) ( a[4] ); /* 1111 1111 */ - b2 = (UINT32) ( a[5] & 0xc0 ); /* 1100 0000 */ - h = (UINT32) ( b0 << 16 | b1 << 8 | b2 ) >> 6 ; -#endif - return t26[h]; -} - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get a character representing 4th 14-bit triplet (bits 42..55) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -const char* base26_triplet_4(const unsigned char *a) -{ -UINT32 b0, b1, h; -#ifndef FIX_BASE26_ENC_BUG - b0 = (UINT32) ( a[5] & 0xfc); /* 1111 1100 */ - b1 = (UINT32) ( a[6] ); /* 1111 1111 */ - h = (UINT32) ( b0 | b1 << 8 ) >> 2 ; -#else - b0 = (UINT32) ( a[5] & 0x3f); /* 0011 1111 */ - b1 = (UINT32) ( a[6] ); /* 1111 1111 */ - h = (UINT32) ( b0 << 8 | b1 ) ; -#endif - return t26[h]; -} - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Tail dublets -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - a4 a3 a2 a1 a0 - 28-36: 0001 1111 1111 0000 0000 0000 0000 0000 0000 0000 - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get dublet (bits 28..36) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -const char* base26_dublet_for_bits_28_to_36(unsigned char *a) -{ -UINT32 b0, b1, h; -#ifndef FIX_BASE26_ENC_BUG - b0 = (UINT32) ( a[3] & 0xf0); /* 1111 0000 */ - b1 = (UINT32) ( a[4] & 0x1f ); /* 0001 1111 */ - h = (UINT32)( b0 | b1 << 8 ) >> 4 ; -#else - b0 = (UINT32) ( a[3] & 0x0f); /* 0000 1111 */ - b1 = (UINT32) ( a[4] & 0xf8 ); /* 1111 1000 */ - h = (UINT32)( b0 << 8 | b1 ) >> 3 ; -#endif - return d26[h]; -} - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - a9 a8 a7 - 56-64: 0000 0000 0000 0001 1111 1111 - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get dublet (bits 56..64) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -const char* base26_dublet_for_bits_56_to_64(unsigned char *a) -{ -UINT32 b0, b1, h; -#ifndef FIX_BASE26_ENC_BUG - b0 = (UINT32) ( a[7] ); /* 1111 1111 */ - b1 = (UINT32) ( a[8] & 0x01 ); /* 0000 0001 */ - h = (UINT32)( b0 | b1 << 8 ); -#else - b0 = (UINT32) ( a[7] ); /* 1111 1111 */ - b1 = (UINT32) ( a[8] & 0x80 ); /* 1000 0000 */ - h = (UINT32)( b0 << 8 | b1 ) >> 7; -#endif - return d26[h]; -} - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Calculate check character A..Z for the string. -NB: ignore delimiter dashes. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -char base26_checksum(const char *str) -{ -size_t slen, j, jj=0, checksum = 0; -char c; - - slen = strlen(str); - - for (j=0; j < slen; j++) - { - c = str[j]; - if (c=='-') - continue; - - checksum+= weights_for_checksum[jj]*c; - - jj++; - if (jj > N_UNIQUE_WEIGHTS - 1) - jj = 0; - - } - return c26[checksum%26]; -} - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get hash extension in hexadecimal representation for the major block. -Len(extension) = 256 - 65 = 191 bit. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void get_xtra_hash_major_hex(const unsigned char *a, char* szXtra) -{ -unsigned char c; -int i, j, start_byte=8; -#ifndef FIX_BASE26_ENC_BUG - c = a[start_byte] & 0xfe ; /* 1111 1110 */ -#else - c = a[start_byte] & 0x7f ; /* 0111 1111 */ -#endif - j = sprintf(szXtra,"%02x", c); - for( i = start_byte+1; i < 32; i++ ) - j+= sprintf(szXtra+j,"%02x", a[i]); -} - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get hash extension in hexadecimal representation for the minor block. -Len(extension) = 256 - 37 = 219 bit. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void get_xtra_hash_minor_hex(const unsigned char *a, char* szXtra) -{ -unsigned char c; -int i, j, start_byte=4; -#ifndef FIX_BASE26_ENC_BUG - c = a[start_byte] & 0xe0 ; /* 1110 0000 */ -#else - c = a[start_byte] & 0x07 ; /* 0000 0111 */ -#endif - j = sprintf(szXtra,"%02x", c); - for( i = start_byte+1; i < 32; i++ ) - j+= sprintf(szXtra+j,"%02x", a[i]); -} diff --git a/INCHI-1-SRC/INCHI/common/ikey_base26.h b/INCHI-1-SRC/INCHI/common/ikey_base26.h deleted file mode 100644 index 2b6b1eb..0000000 --- a/INCHI-1-SRC/INCHI/common/ikey_base26.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __IKEY_BASE26_H__ -#define __IKEY_BASE26_H__ - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Base-26 encoding procedures. - - 'Base26' characters here are considered to be uppercase English - letters 'A..Z' - - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - -/* Uncomment the next line to fix base-26 encoding bug */ -/*#define FIX_BASE26_ENC_BUG 1*/ - - -typedef unsigned int UINT32; -typedef unsigned short int UINT16; - - - -#ifdef __cplusplus -extern "C" { -#endif - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get a character representing 1st 14-bit triplet (bits 0..13 of contiguous array of octets) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -const char* base26_triplet_1(const unsigned char *a); - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get a character representing 2nd 14-bit triplet (bits 14..27) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -const char* base26_triplet_2(const unsigned char *a); - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get a character representing 3rd 14-bit triplet (bits 28..41) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -const char* base26_triplet_3(const unsigned char *a); - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get a character representing 4th 14-bit triplet (bits 42..55) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -const char* base26_triplet_4(const unsigned char *a); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Tail dublets -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get dublet (bits 28..36) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -const char* base26_dublet_for_bits_28_to_36(unsigned char *a); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get dublet (bits 56..64) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -const char* base26_dublet_for_bits_56_to_64(unsigned char *a); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Calculate check character for the string. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -char base26_checksum(const char *str); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get hash extension in hexadecimal representation for the major block. -Len(extension) = 256 - 65 = 191 bit. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void get_xtra_hash_major_hex(const unsigned char *a, char* szXtra); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get hash extension in hexadecimal representation for the minor block. -Len(extension) = 256 - 37 = 219 bit. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void get_xtra_hash_minor_hex(const unsigned char *a, char* szXtra); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Used instead of isupper() to avoid locale interference. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -#define isbase26(_c) ( ((unsigned)(_c) >= 'A') && ((unsigned)(_c) <= 'Z') ) - - -#ifdef __cplusplus -} -#endif - - -#endif /*^^^ __IKEY_BASE26_H__ */ diff --git a/INCHI-1-SRC/INCHI/common/ikey_dll.c b/INCHI-1-SRC/INCHI/common/ikey_dll.c deleted file mode 100644 index 5be961f..0000000 --- a/INCHI-1-SRC/INCHI/common/ikey_dll.c +++ /dev/null @@ -1,567 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - InChIKey: calculation of hash for InChI string - - Uses truncated SHA-256 function. - SHA-256 implementation: Copyright (C) Brainspark B.V., - see files sha2.c, sha2.h. - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -#ifdef _MSC_VER -#if _MSC_VER > 1000 -#pragma warning( disable : 4996 ) -#endif -#endif - -#include -#include -#include -#include -#include - -#include "sha2.h" -#include "ikey_base26.h" - -#include "ichisize.h" -#include "mode.h" -#include "inchi_api.h" - -#include "util.h" - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Local options -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -#define INCHIKEY_DEBUG 0 /* 2 */ - -#define INCHIKEY_FLAG_OK 0 -#define INCHIKEY_NOT_VALID_FLAG 1 - -enum { MINOUTLENGTH=256 }; - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Local functions -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -void fprint_digest(FILE* fw, const char *header, unsigned char *a); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - EXPORTED FUNCTIONS -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStdINCHIKeyFromStdINCHI(const char* szINCHISource, - char* szINCHIKey) -{ - if ( strlen(szINCHISource) < LEN_INCHI_STRING_PREFIX+3 ) - return INCHIKEY_INVALID_STD_INCHI; - if (szINCHISource[LEN_INCHI_STRING_PREFIX+1]!='S') - return INCHIKEY_INVALID_STD_INCHI; - return GetINCHIKeyFromINCHI(szINCHISource, 0, 0, szINCHIKey, (char *) NULL, (char *) NULL); -} - -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetINCHIKeyFromINCHI(const char* szINCHISource, - const int xtra1, - const int xtra2, - char* szINCHIKey, - char* szXtra1, - char* szXtra2) -{ -int ret = INCHIKEY_OK; -int ret1 = INCHIKEY_OK; -int cn; -size_t slen, i, j, jproto=0, ncp, pos_slash1=0; -char *str = NULL, *smajor = NULL, *sminor = NULL, - *sproto=NULL, - *stmp = NULL, tmp[MINOUTLENGTH]; -unsigned char - digest_major[32], digest_minor[32]; - -char flagstd = 'S', /* standard key */ - flagnonstd = 'N', /* non-standard key */ - flagver = 'A', /* InChI v. 1 */ - flagproto = 'N'; /* no [de]protonization , by default */ -int nprotons; -/* -Protonization encoding: -N 0 -O +1 P +2 Q +3 R +4 S +5 T +6 U +7 V +8 W +9 X +10 Y +11 Z +12 -M -1 L-2 K -3 J -4 I -5 H -6 G -7 F -8 E -9 D -10 C -11 B -12 -A < -12 or > +12 -*/ -static const char *pplus = "OPQRSTUVWXYZ"; -static const char *pminus = "MLKJIHGFEDCB"; - -int bStdFormat = 0; -size_t bytelen = 32; - - - /* Check if input is a valid InChI string */ - - /* .. non-empty */ - if (szINCHISource==NULL) - return INCHIKEY_EMPTY_INPUT; - - slen = strlen(szINCHISource); - - /* .. has valid prefix */ - if (slen standard InChIKey */ - bStdFormat = 1; - pos_slash1++; - } - - /* .. has trailing slash in the right place */ - if (szINCHISource[pos_slash1]!='/') - return INCHIKEY_INVALID_INCHI_PREFIX; - - /* .. the rest of source string contains at least one a..Z0.9 or slash */ - /* TODO: improve/add full string check */ - if (!isalnum(szINCHISource[pos_slash1+1] ) && - ( szINCHISource[pos_slash1+1]!='/' ) ) - return INCHIKEY_INVALID_INCHI; - - - /*^^^ Ok. Will use a local copy of the source. */ - - extract_inchi_substring(&str, szINCHISource, slen); - if (NULL==str) - { - ret = INCHIKEY_NOT_ENOUGH_MEMORY; - goto fin; - } - slen = strlen(str); - - - /*^^^ Make buffers. */ - smajor = (char*) inchi_calloc( slen+1, sizeof(char)); - if (NULL==smajor) - { ret = INCHIKEY_NOT_ENOUGH_MEMORY; goto fin; } - sminor = (char*) inchi_calloc( 2*slen + 2, sizeof(char)); /* we may double the length ... */ - if (NULL==sminor) - { ret = INCHIKEY_NOT_ENOUGH_MEMORY; goto fin; } - stmp = (char*) inchi_calloc( slen+1, sizeof(char)); - if (NULL==stmp) - { ret = INCHIKEY_NOT_ENOUGH_MEMORY; goto fin; } - sproto = (char*) inchi_calloc( slen+1, sizeof(char)); - if (NULL==sproto) - { ret = INCHIKEY_NOT_ENOUGH_MEMORY; goto fin; } - - - szINCHIKey[0] = '\0'; - - - - /*^^^ Extract the major block. */ - - smajor[0] = '\0'; - for (j = pos_slash1 + 1; j < slen-1; j++) - { - if (str[j]=='/') - { - cn = str[j+1]; - switch (cn) - { - /* anything allowed from a major part */ - case 'c': case 'h': case 'q': continue; - - /* "/p"; protons now go to to special string, not to minor hash */ - case 'p': jproto = j; - continue; - - /* "/f", "/r" : may not occur in stdInChI */ - case 'f': case 'r': if ( bStdFormat ) - { - ret = INCHIKEY_INVALID_STD_INCHI; - goto fin; - } - break; - - /* anything allowed from a minor part */ - default: break; - } - break; - } - } - j++; - if (j==slen) - j++; - else - j--; - - if (jproto) - ncp = jproto - pos_slash1 - 1; - else - ncp = j - pos_slash1 - 1; - - - /*^^^ Trim 'InChI=1[S]/' */ - memcpy(smajor,str+pos_slash1+1, ncp*sizeof(str[0])); - smajor[ncp]='\0'; - - - /* Treat protonization */ - if (jproto) - { - /* 2009-01-07 fix bug/typo: assigned incorrect length to the protonation segment of - /* source string ( was sproto[ncp]='\0'; should be sproto[lenproto]='\0'; ) */ - int lenproto = j - (int) jproto; - if (lenproto<3) - { - /* empty "/p", should not occur */ - ret = INCHIKEY_INVALID_INCHI; - goto fin; - } - - memcpy(sproto,str+pos_slash1+ncp+1, lenproto*sizeof(str[0])); - sproto[lenproto]='\0'; - - nprotons = strtol( sproto+2, NULL, 10 ); - - if (nprotons > 0) - { - if (nprotons > 12) flagproto = 'A'; - else flagproto = pplus[nprotons-1]; - } - else if (nprotons < 0) - { - if (nprotons < -12) flagproto = 'A'; - else flagproto = pminus[-nprotons-1]; - } - else - { - /* should never occur */ - ret = INCHIKEY_INVALID_STD_INCHI; - goto fin; - } - } - - - - /*^^^ Extract the minor block. */ - - if (j != slen+1) /*^^^ check that something exists at right.*/ - { - ncp = slen-j; - memcpy(sminor,str+j, (ncp)*sizeof(str[0])); - sminor[ncp]='\0'; - } - else - sminor[0]='\0'; - - -#if INCHIKEY_DEBUG - fprintf(stdout,"Source: {%-s}\n",str); - fprintf(stdout,"SMajor: {%-s}\n",smajor); - fprintf(stdout,"SMinor: {%-s}\n",sminor); - fprintf(stdout,"SProto: {%-s}\n",sproto); -#endif - - - - /*^^^ Compute and compose the InChIKey string. */ - - - /*^^^ Major hash sub-string. */ - for( i = 0; i < 32; i++ ) - digest_major[i] = 0; - sha2_csum( (unsigned char *) smajor, (int) strlen(smajor), digest_major ); - - sprintf(tmp,"%-.3s%-.3s%-.3s%-.3s%-.2s", - base26_triplet_1(digest_major), base26_triplet_2(digest_major), - base26_triplet_3(digest_major), base26_triplet_4(digest_major), - base26_dublet_for_bits_56_to_64(digest_major)); - strcat(szINCHIKey, tmp); -#if (INCHIKEY_DEBUG>1) - fprint_digest(stderr, "Major hash, full SHA-256",digest_major); -#endif - - - - /*^^^ Minor hash sub-string. */ - for( i = 0; i < 32; i++ ) - digest_minor[i] = 0; - slen = strlen(sminor); - if ((slen>0)&&(slen<255)) - { - strcpy(stmp, sminor); - strcpy(sminor+slen,stmp); - } - sha2_csum( (unsigned char *) sminor, (int) strlen(sminor), digest_minor ); -#if (INCHIKEY_DEBUG>1) - fprint_digest(stderr, "Minor hash, full SHA-256",digest_minor); -#endif - - strcat(szINCHIKey, "-"); - sprintf(tmp,"%-.3s%-.3s%-.2s", - base26_triplet_1(digest_minor), - base26_triplet_2(digest_minor), - base26_dublet_for_bits_28_to_36(digest_minor)); - strcat(szINCHIKey, tmp); - - - /* Append a standard/non-standard flag */ - slen = strlen(szINCHIKey); - if ( bStdFormat ) - szINCHIKey[slen] = flagstd; - else - szINCHIKey[slen] = flagnonstd; - - /*^^^ Append InChI v.1 flag */ - szINCHIKey[slen+1] = flagver; - - /*^^^ Append dash */ - szINCHIKey[slen+2] = '-'; - - /*^^^ Append protonization flag */ - szINCHIKey[slen+3] = flagproto; - szINCHIKey[slen+4] = '\0'; - - -#if INCHIKEY_DEBUG - fprintf(stdout,"szINCHIKey: {%-s}\n",szINCHIKey); -#endif - - /* Hash extensions */ - if ( xtra1 && szXtra1 ) - { - get_xtra_hash_major_hex(digest_major, szXtra1); -#if INCHIKEY_DEBUG - fprintf(stderr,"XHash1=%-s\n",szXtra1); - fprintf(stderr,"j=%-d\n",j); -#endif - } - if ( xtra2 && szXtra2 ) - { - get_xtra_hash_minor_hex(digest_minor, szXtra2); -#if INCHIKEY_DEBUG - fprintf(stderr,"XHash2=%-s\n",szXtra2); - fprintf(stderr,"j=%-d\n",j); -#endif - } - - -fin:if (NULL!=str) inchi_free(str); - if (NULL!=smajor) inchi_free(smajor); - if (NULL!=sminor) inchi_free(sminor); - if (NULL!=stmp) inchi_free(stmp); - if (NULL!=sproto) inchi_free(sproto); - - if ( (ret==INCHIKEY_OK) && (ret1!=INCHIKEY_OK) ) - ret = ret1; - return ret; - -} - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Check if the string represents valid InChIKey. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL CheckINCHIKey(const char *szINCHIKey) -{ -size_t slen, j; - - - slen = strlen(szINCHIKey); - - - /*^^^ Proper length is 27 */ - if (slen != 27) - return INCHIKEY_INVALID_LENGTH; - - /*^^^ Should have dash in 14-th position */ - if (szINCHIKey[14] !='-') - return INCHIKEY_INVALID_LAYOUT; - /*^^^ Should have dash in 25-th position */ - if (szINCHIKey[25] !='-') - return INCHIKEY_INVALID_LAYOUT; - - /*^^^ All other should be uppercase */ - for (j = 0; j < 14; j++) - if ( !isbase26(szINCHIKey[j]) ) - return INCHIKEY_INVALID_LAYOUT; /* first block */ - for (j = 15; j < 25; j++) - if ( !isbase26(szINCHIKey[j]) ) - return INCHIKEY_INVALID_LAYOUT; /* second block */ - if ( !isbase26(szINCHIKey[26]) ) - return INCHIKEY_INVALID_LAYOUT; /* (de)protonation flag */ - - - /*^^^ No 'E' may appear in 0,3,6,and 9 positions of the 1st block ... */ - for (j=0; j <10; j+=3) - if (szINCHIKey[j]=='E') - return INCHIKEY_INVALID_LAYOUT; - /*^^^ ... and 0 and 3 pos. of the second block. */ - for (j=15; j <19; j+=3) - if (szINCHIKey[j]=='E') - return INCHIKEY_INVALID_LAYOUT; - - /*^^^ Check for version (only 1 allowed) */ - if (szINCHIKey[24] !='A') - return INCHIKEY_INVALID_VERSION; - - /*^^^ Check for standard-ness */ - if (szINCHIKey[23] == 'S') - return INCHIKEY_VALID_STANDARD; - else if (szINCHIKey[23] == 'N') - return INCHIKEY_VALID_NON_STANDARD; - else - return INCHIKEY_INVALID_LAYOUT; -} - - - - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void fprint_digest(FILE* fw, const char *header, unsigned char *a) -{ -size_t i, bytelen = 32; - fprintf(fw,"%s\n", header); - for( i = 0; i < bytelen; i++ ) - fprintf(fw,"%02x ", a[i]); - fprintf(fw,"\n" ); -} - - - -/********************************************************************/ - -#if ( defined( _WIN32 ) && defined( _MSC_VER ) && _MSC_VER >= 800 && defined(_USRDLL) && defined(BUILD_LINK_AS_DLL) ) - /* Win32 & MS VC ++, compile and link as a DLL */ -/*********************************************************/ -/* C calling conventions export from Win32 dll */ -/*********************************************************/ -/* prototypes */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -int cdecl_CheckINCHIKey(const char *szINCHIKey,const int strict); -int cdecl_GetINCHIKeyFromINCHI(const char* szINCHISource, - const int xtra1,const int xtra2, - char* szINCHIKey, char* szXtra1, char* szXtra2); -int cdecl_GetStdINCHIKeyFromStdINCHI(const char* szINCHISource, char* szINCHIKey); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -/* implementation */ -/* libinchi.def provides export without cdecl_ prefixes */ - -/********************************************************/ -int cdecl_GetStdINCHIKeyFromStdINCHI(const char* szINCHISource, char* szINCHIKey) -{ - return GetStdINCHIKeyFromStdINCHI(szINCHISource, szINCHIKey); -} -/********************************************************/ -int cdecl_CheckINCHIKey(const char *szINCHIKey,const int strict) -{ - return CheckINCHIKey(szINCHIKey); -} -/********************************************************/ -int cdecl_GetINCHIKeyFromINCHI(const char* szINCHISource, - const int xtra1,const int xtra2, - char* szINCHIKey, char* szXtra1, char* szXtra2) -{ - return GetINCHIKeyFromINCHI(szINCHISource, xtra1, xtra2, szINCHIKey, szXtra1, szXtra2); -} -#endif - - - -#if ( defined(__GNUC__) && __GNUC__ >= 3 && defined(__MINGW32__) && defined(_WIN32) ) -#include -/*********************************************************/ -/* Pacal calling conventions export from Win32 dll */ -/*********************************************************/ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif -/* prototypes */ -int PASCAL pasc_CheckINCHIKey(const char *szINCHIKey); -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -/* implementation */ -/* libinchi.def provides export without PASCAL pasc_ prefixes */ -/********************************************************/ -int PASCAL pasc_CheckINCHIKey(const char *szINCHIKey) -{ - return CheckINCHIKey(szINCHIKey); -} -#endif - diff --git a/INCHI-1-SRC/INCHI/common/inchi_api.h b/INCHI-1-SRC/INCHI/common/inchi_api.h deleted file mode 100644 index e10a1e2..0000000 --- a/INCHI-1-SRC/INCHI/common/inchi_api.h +++ /dev/null @@ -1,1349 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INHCH_API_H__ -#define __INHCH_API_H__ - - - - -/*^^^ Post-1.02b fix - thanks to David Foss */ -#ifndef FIND_RING_SYSTEMS -#define FIND_RING_SYSTEMS 1 -#endif -#ifndef FIND_RINS_SYSTEMS_DISTANCES -#define FIND_RINS_SYSTEMS_DISTANCES 0 -#endif - - -/* radical definitions */ -typedef enum tagINCHIRadical { - INCHI_RADICAL_NONE = 0, - INCHI_RADICAL_SINGLET = 1, - INCHI_RADICAL_DOUBLET = 2, - INCHI_RADICAL_TRIPLET = 3 -} inchi_Radical; - -/* bond type definitions */ -typedef enum tagINCHIBondType { - INCHI_BOND_TYPE_NONE = 0, - INCHI_BOND_TYPE_SINGLE = 1, - INCHI_BOND_TYPE_DOUBLE = 2, - INCHI_BOND_TYPE_TRIPLE = 3, - INCHI_BOND_TYPE_ALTERN = 4 /* avoid by all means */ -} inchi_BondType; -/* 2D stereo definitions */ -typedef enum tagINCHIBondStereo2D { - /* stereocenter-related; positive: the sharp end points to this atom */ - INCHI_BOND_STEREO_NONE = 0, - INCHI_BOND_STEREO_SINGLE_1UP = 1, - INCHI_BOND_STEREO_SINGLE_1EITHER = 4, - INCHI_BOND_STEREO_SINGLE_1DOWN = 6, - /* stereocenter-related; negative: the sharp end points to the opposite atom */ - INCHI_BOND_STEREO_SINGLE_2UP = -1, - INCHI_BOND_STEREO_SINGLE_2EITHER = -4, - INCHI_BOND_STEREO_SINGLE_2DOWN = -6, - /* stereobond-related */ - INCHI_BOND_STEREO_DOUBLE_EITHER = 3 /* unknown stereobond geometry */ -} inchi_BondStereo2D; - -/************************************************************************* - * Notes on using INCHI_BOND_STEREO_SINGLE_* from inchi_BondStereo2D * - * * - * These stereo markings are used by InChI to characterize a stereogenic * - * atom if and only if all neighbors of this atom have same z-coordinate * - * as this atom (that is, in case of 2D fragment). * - * The only exception is INCHI_BOND_STEREO_SINGLE_?EITHER marking which * - * always assigns to the atom an "unknown" parity (u). * - * * - * Note the behavior which is default for InChI software v.1.04/03/02std * - * (at -NEWPSOFF option is not supplied) 2D stereo interpretation: * - * only bonds that have sharp end pointing to the stereogenic atom are * - * considered as being out of plane and only sharp ends of * - * INCHI_BOND_STEREO_SINGLE_?EITHER bonds are considered to determine * - * whether the stereochemistry is unknown. * - *************************************************************************/ - -/* sizes definitions */ -#define MAXVAL 20 /* max number of bonds per atom */ -#define ATOM_EL_LEN 6 /* length of ASCIIZ element symbol field */ -#define NUM_H_ISOTOPES 3 /* number of hydrogen isotopes: protium, D, T */ -#define ISOTOPIC_SHIFT_FLAG 10000 /* add to isotopic mass if isotopic_mass = */ - /* (isotopic mass - average atomic mass) */ -#define ISOTOPIC_SHIFT_MAX 100 /* max abs(isotopic mass - average atomic mass) */ - -#ifndef INCHI_US_CHAR_DEF -typedef signed char S_CHAR; -typedef unsigned char U_CHAR; -#define INCHI_US_CHAR_DEF -#endif - -#ifndef INCHI_US_SHORT_DEF -typedef signed short S_SHORT; -typedef unsigned short U_SHORT; -#define INCHI_US_SHORT_DEF -#endif - -typedef S_SHORT AT_NUM; /* atom number; starts from 0 */ - -/************************************************* - * - * - * A T O M S a n d C O N N E C T I V I T Y - * - * - *************************************************/ - -typedef struct tagInchiAtom { - /* atom coordinates */ - double x; - double y; - double z; - /* connectivity */ - AT_NUM neighbor[MAXVAL]; /* adjacency list: ordering numbers of */ - /* the adjacent atoms, >= 0 */ - S_CHAR bond_type[MAXVAL]; /* inchi_BondType */ - /* 2D stereo */ - S_CHAR bond_stereo[MAXVAL]; /* inchi_BondStereo2D; negative if the */ - /* sharp end points to opposite atom */ - /* other atom properties */ - char elname[ATOM_EL_LEN]; /* zero-terminated chemical element name:*/ - /* "H", "Si", etc. */ - AT_NUM num_bonds; /* number of neighbors, bond types and bond*/ - /* stereo in the adjacency list */ - S_CHAR num_iso_H[NUM_H_ISOTOPES+1]; /* implicit hydrogen atoms */ - /* [0]: number of implicit non-isotopic H - (exception: num_iso_H[0]=-1 means INCHI - adds implicit H automatically), - [1]: number of implicit isotopic 1H (protium), - [2]: number of implicit 2H (deuterium), - [3]: number of implicit 3H (tritium) */ - AT_NUM isotopic_mass; /* 0 => non-isotopic; isotopic mass or */ - /* ISOTOPIC_SHIFT_FLAG + mass - (average atomic mass) */ - S_CHAR radical; /* inchi_Radical */ - S_CHAR charge; /* positive or negative; 0 => no charge */ -}inchi_Atom; - -/******************************************************************* - * Notes: 1. Atom ordering numbers (i, k, and atom[i].neighbor[j] below) - * start from zero; max. ordering number is (num_atoms-1). - * 2. inchi_Atom atom[i] is connected to the atom[atom[i].neighbor[j]] - * by a bond that has type atom[i].bond_type[j] and 2D stereo type - * atom[i].bond_stereo[j] (in case of no stereo - * atom[i].bond_stereo[j] = INCHI_BOND_STEREO_NONE) - * Index j is in the range 0 <= j <= (atom[i].num_bonds-1) - * 3. Any connection (represented by atom[i].neighbor[j], - * atom[i].bond_type[j], and atom[i].bond_stereo[j]) - * should be present in one or both adjacency list: - * if k = atom[i].neighbor[j] then i may or may not be present in - * atom[k].neighbor[] list. For example, the adjacency lists may be - * populated with only such neighbors that atom[i].neighbor[j] < i - * All elements of an adjacency list must be different, that is, - * a bond must be specified in an adjacency list only once. - * 4. in Molfiles usually - * (number of implicit H) = Valence - SUM(bond_type[]) - * 5. Seemingly illogical order of the inchi_Atom members was - * chosen in an attempt to avoid alignment problems when - * accessing inchi_Atom from unrelated to C programming - * languages such as Visual Basic. - *******************************************************************/ - -/******************************************************************* - 0D Stereo Parity and Type definitions - ******************************************************************* - Note: - ===== - o Below #A is the ordering number of atom A, starting from 0 - o See parity values corresponding to 'o', 'e', and 'u' in - inchi_StereoParity0D definition below) - - ============================================= - stereogenic bond >A=B< or cumulene >A=C=C=B< - ============================================= - - neighbor[4] : {#X,#A,#B,#Y} in this order - X central_atom : NO_ATOM - \ X Y type : INCHI_StereoType_DoubleBond - A==B \ / - \ A==B - Y - - parity= 'e' parity= 'o' unknown parity = 'u' - - Limitations: - ============ - o Atoms A and B in cumulenes MUST be connected by a chain of double bonds; - atoms A and B in a stereogenic 'double bond' may be connected by a double, - single, or alternating bond. - o One atom may belong to up to 3 stereogenic bonds (i.g. in a fused - aromatic structure). - o Multiple stereogenic bonds incident to any given atom should - either all except possibly one have (possibly different) defined - parities ('o' or 'e') or should all have an unknown parity 'u'. - - Note on parities of alternating stereobonds - =========================================== - D--E - In large rings (see Fig. 1, all // \\ - atoms are C) all alternating bonds B--C F--G - are treated as stereogenic. // \\ - To avoid "undefined" bond parities A H - for bonds BC, DE, FG, HI, JK, LM, AN \ / - it is recommended to mark them with N==M J==I - parities. \ / - L==K Fig. 1 - Such a marking will make - the stereochemical layer unambiguous - and it will be different from the B--C F--G - stereochemical layer of the second // \\ // \\ - structure (Fig. 2). A D--E H - \ / - N==M J==I - By default, double and alternating \ / - bonds in 8-member and greater rings L==K Fig. 2 - are treated by InChI as stereogenic. - - - ============================================= - tetrahedral atom - ============================================= - - 4 neighbors - - X neighbor[4] : {#W, #X, #Y, #Z} - | central_atom: #A - W--A--Y type : INCHI_StereoType_Tetrahedral - | - Z - parity: if (X,Y,Z) are clockwize when seen from W then parity is 'e' otherwise 'o' - Example (see AXYZW above): if W is above the plane XYZ then parity = 'e' - - 3 neighbors - - Y Y neighbor[4] : {#A, #X, #Y, #Z} - / / central_atom: #A - X--A (e.g. O=S ) type : INCHI_StereoType_Tetrahedral - \ \ - Z Z - - parity: if (X,Y,Z) are clockwize when seen from A then parity is 'e', - otherwise 'o' - unknown parity = 'u' - Example (see AXYZ above): if A is above the plane XYZ then parity = 'e' - This approach may be used also in case of an implicit H attached to A. - - ============================================= - allene - ============================================= - - X Y neighbor[4] : {#X,#A,#B,#Y} - \ / central_atom : #C - A=C=B type : INCHI_StereoType_Allene - - Y X - | | - when seen from A along A=C=B: X-A Y-A - - parity: 'e' 'o' - - parity: if A, B, Y are clockwise when seen from X then parity is 'e', - otherwise 'o' - unknown parity = 'u' - Example (see XACBY above): if X on the diagram is above the plane ABY - then parity is 'o' - - Limitations - =========== - o Atoms A and B in allenes MUST be connected by a chain of double bonds; - - ============================================== - Note. Correspondence to CML 0D stereo parities - ============================================== - a list of 4 atoms corresponds to CML atomRefs4 - - tetrahedral atom - ================ - CML atomParity > 0 <=> INCHI_PARITY_EVEN - CML atomParity < 0 <=> INCHI_PARITY_ODD - - | 1 1 1 1 | where xW is x-coordinate of - | xW xX xY xZ | atom W, etc. (xyz is a - CML atomParity = determinant | yW yX yY yZ | 'right-handed' Cartesian - | zW zX xY zZ | coordinate system) - - allene (not yet defined in CML) - =============================== - the parity corresponds to the sign of the following determinant - in exactly same way as for tetrahedral atoms: - - | 1 1 1 1 | where bonds and neighbor[4] array are - | xX xA xB xY | same as defined above for allenes - | yX yA yB yY | Obviously, the parity is same for - | zX zA xB zY | {#X,#A,#B,#Y} and {#Y,#B,#A,#X} - because of the even number of column permutations. - - stereogenic double bond and (not yet defined in CML) cumulenes - ============================================================== - CML 'C' (cis) <=> INCHI_PARITY_ODD - CML 'T' (trans) <=> INCHI_PARITY_EVEN - - - How InChI uses 0D parities - ========================== - - 1. 0D parities are used if all atom coordinates are zeroes. - - In addition to that: - - 2. 0D parities are used for Stereobonds, Allenes, or Cumulenes if: - - 2a. A bond to the end-atom is shorter than MIN_BOND_LEN=0.000001 - 2b. A ratio of two bond lengths to the end-atom is smaller than MIN_SINE=0.03 - 2c. In case of a linear fragment X-A=B end-atom A is treated as satisfying 2a-b - - 0D parities are used if 2a or 2b or 2c applies to one or both end-atoms. - - 3. 0D parities are used for Tetrahedral Atoms if at least one of 3a-c is true: - - 3a. One of bonds to the central atom is shorter than MIN_BOND_LEN=0.000001 - 3b. A ratio of two bond lengths to the central atom is smaller than MIN_SINE=0.03 - 3c. The four neighbors are almost in one plane or the central atom and - its only 3 explicit neighbors are almost in one plane - - Notes on 0D parities and 'undefined' stereogenic elements - ========================================================= - - If 0D parity is to be used according to 1-3 but CH3 CH3 - has not been provided then the corresponding \ / - stereogenic element is considered 'undefined'. C=CH - / - For example, if in the structure (Fig. 3) H - the explicit H has been moved so that it Fig. 3 - has same coordinates as atom >C= (that is, - the length of the bond H-C became zero) - then the double bond is assigned 'undefined' CH3 CH3 - parity which by default is omitted from the \ / - Identifier. CH=CH - - However, the structure on Fig. 4 will have double Fig. 4 - bond parity 'o' and its parity in the Identifier is (-). - - Notes on 0D parities in structures containing metals - ==================================================== - Since InChI disconnects bonds to metals the 0D parities upon the - disconnection may change in several different ways: - - 1) previously non-stereogenic bond may become stereogenic: - - \ / \ / - CH==CH disconnection CH==CH - \ / ======> - M M - - before the disconnection: after the disconnection: - atoms C have valence=5 and the double bond may become - the double bond is not stereogenic - recognized as stereogenic - - 2) previously stereogenic bond may become non-stereogenic: - - M M(+) - \ / / - N==C disconnection (-)N==C - \ ======> \ - - 3) Oddball structures, usually resulting from projecting 3D - structures on the plane, may contain fragment like that - depicted on Fig. 5: - - M A M A - |\ / B / B - | X / disconnection / / - |/ \ / ======> / / - C===C C===C - Fig. 5 - (X stands for bond intersection) - - A-C=C-B parity is A-C=C-B parity is - trans (e) cis (o) or undefined - because the bond because C valence = 3, - orientation is same not 4. - as on Fig, 6 below: - - A M - \ / Removal of M from the structure - C===C on Fig. 5 changes the geometry from trans - / \ to cis. - M' B Removal of M and M' from the structure - Fig. 6 on Fig. 6 does not change the A-C=C-B - geometry: it is trans. - - To resolve the problem InChI API accepts the second parity - corresponding to the metal-disconnected structure. - To store both bond parities use left shift by 3 bits: - - inchi_Stereo0D::parity = ParityOfConnected | (ParityOfDisconnected<<3) - - In case when only disconnected structure parity exists set - ParityOfConnected = INCHI_PARITY_UNDEFINED. - This is the only case when INCHI_PARITY_UNDEFINED parity - may be fed to the InChI. - - In cases when the bond parity in a disconnected structure exists and - differs from the parity in the connected structure the atoms A and B - should be non-metals. - -****************************************************************************/ - -#define NO_ATOM (-1) /* non-existent (central) atom */ - -/* 0D parity types */ -typedef enum tagINCHIStereoType0D { - INCHI_StereoType_None = 0, - INCHI_StereoType_DoubleBond = 1, - INCHI_StereoType_Tetrahedral = 2, - INCHI_StereoType_Allene = 3 -} inchi_StereoType0D; - -/* 0D parities */ -typedef enum tagINCHIStereoParity0D { - INCHI_PARITY_NONE = 0, - INCHI_PARITY_ODD = 1, /* 'o' */ - INCHI_PARITY_EVEN = 2, /* 'e' */ - INCHI_PARITY_UNKNOWN = 3, /* 'u' */ /* (see also readinch.c) - used in: Extract0DParities, INChITo_Atom */ - INCHI_PARITY_UNDEFINED = 4 /* '?' -- should not be used; however, see Note above */ -} inchi_StereoParity0D; - - -/************************************************* - * - * - * 0D - S T E R E O (if no coordinates given) - * - * - *************************************************/ - - -typedef struct tagINCHIStereo0D { - AT_NUM neighbor[4]; /* 4 atoms always */ - AT_NUM central_atom; /* central tetrahedral atom or a central */ - /* atom of allene; otherwise NO_ATOM */ - S_CHAR type; /* inchi_StereoType0D */ - S_CHAR parity; /* inchi_StereoParity0D: may be a combination of two parities: */ - /* ParityOfConnected | (ParityOfDisconnected << 3), see Note above */ -}inchi_Stereo0D; - - - - -/************************************************* - * - * - * I N C h I D L L I n p u t - * - * - *************************************************/ - - -/* Structure -> InChI, GetINCHI() / GetStdINCHI() */ -typedef struct tagINCHI_Input { - /* the caller is responsible for the data allocation and deallocation */ - inchi_Atom *atom; /* array of num_atoms elements */ - inchi_Stereo0D *stereo0D; /* array of num_stereo0D 0D stereo elements or NULL */ - char *szOptions; /* InChI options: space-delimited; each is preceded by */ - /* '/' or '-' depending on OS and compiler */ - AT_NUM num_atoms; /* number of atoms in the structure < 1024 */ - AT_NUM num_stereo0D; /* number of 0D stereo elements */ -}inchi_Input; - -/* InChI -> Structure, GetStructFromINCHI()/GetStructFromStdINCHI() */ -typedef struct tagINCHI_InputINCHI { - /* the caller is responsible for the data allocation and deallocation */ - char *szInChI; /* InChI ASCIIZ string to be converted to a strucure */ - char *szOptions; /* InChI options: space-delimited; each is preceded by */ - /* '/' or '-' depending on OS and compiler */ -} inchi_InputINCHI; - - -/************************************************* - * - * - * I N C h I D L L O u t p u t - * - * - *************************************************/ - -/* Structure -> InChI */ -typedef struct tagINCHI_Output { - /* zero-terminated C-strings allocated by GetStdINCHI() */ - /* to deallocate all of them call FreeStdINCHI() (see below) */ - char *szInChI; /* InChI ASCIIZ string */ - char *szAuxInfo; /* Aux info ASCIIZ string */ - char *szMessage; /* Error/warning ASCIIZ message */ - char *szLog; /* log-file ASCIIZ string, contains a human-readable list */ - /* of recognized options and possibly an Error/warning message */ -} inchi_Output; - -/* InChI -> Structure */ -typedef struct tagINCHI_OutputStruct { - /* 4 pointers are allocated by GetStructFromINCHI()/GetStructFromStdINCHI() */ - /* to deallocate all of them call FreeStructFromStdINCHI()/FreeStructFromStdINCHI() */ - inchi_Atom *atom; /* array of num_atoms elements */ - inchi_Stereo0D *stereo0D; /* array of num_stereo0D 0D stereo elements or NULL */ - AT_NUM num_atoms; /* number of atoms in the structure < 1024 */ - AT_NUM num_stereo0D; /* number of 0D stereo elements */ - char *szMessage; /* Error/warning ASCIIZ message */ - char *szLog; /* log-file ASCIIZ string, contains a human-readable list */ - /* of recognized options and possibly an Error/warning message */ - unsigned long WarningFlags[2][2]; /* warnings, see INCHIDIFF in inchicmp.h */ - /* [x][y]: x=0 => Reconnected if present in InChI otherwise Disconnected/Normal - x=1 => Disconnected layer if Reconnected layer is present - y=1 => Main layer or Mobile-H - y=0 => Fixed-H layer - */ -}inchi_OutputStruct; - - - - -/************************************************* - * - * - * I N C h I D L L I n t e r f a c e - * - * - *************************************************/ - -#if (defined( _WIN32 ) && defined( _MSC_VER ) && defined(BUILD_LINK_AS_DLL) ) - /* Win32 & MS VC ++, compile and link as a DLL */ - #ifdef _USRDLL - /* InChI library dll */ - #define INCHI_API __declspec(dllexport) - #define EXPIMP_TEMPLATE - #define INCHI_DECL __stdcall - #else - /* calling the InChI dll program */ - #define INCHI_API __declspec(dllimport) - #define EXPIMP_TEMPLATE extern - #define INCHI_DECL __stdcall - #endif -#else - /* create a statically linked InChI library or link to an executable */ - #define INCHI_API - #define EXPIMP_TEMPLATE - #define INCHI_DECL -#endif - -/*^^^ Return codes for - GetINCHI - GetStdINCHI - Get_inchi_Input_FromAuxInfo - Get_std_inchi_Input_FromAuxInfo - GetStructFromINCHI - GetStructFromStdINCHI -*/ -typedef enum tagRetValGetINCHI { - inchi_Ret_SKIP = -2, /* not used in InChI library */ - inchi_Ret_EOF = -1, /* no structural data has been provided */ - inchi_Ret_OKAY = 0, /* Success; no errors or warnings */ - inchi_Ret_WARNING = 1, /* Success; warning(s) issued */ - inchi_Ret_ERROR = 2, /* Error: no InChI has been created */ - inchi_Ret_FATAL = 3, /* Severe error: no InChI has been created (typically, memory allocation failure) */ - inchi_Ret_UNKNOWN = 4, /* Unknown program error */ - inchi_Ret_BUSY = 5 /* Previuos call to InChI has not returned yet */ - -} RetValGetINCHI; - -/*^^^ Return codes for CheckINCHI */ -typedef enum tagRetValCheckINCHI -{ - INCHI_VALID_STANDARD = 0, - INCHI_VALID_NON_STANDARD = -1, - INCHI_INVALID_PREFIX = 1, - INCHI_INVALID_VERSION = 2, - INCHI_INVALID_LAYOUT = 3, - INCHI_FAIL_I2I = 4 - -} RetValCheckINCHI; - - - -/* to compile all InChI code as a C++ code #define COMPILE_ALL_CPP */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - -/*^^^ InChI PREFIX */ -#define INCHI_STRING_PREFIX "InChI=" -#define LEN_INCHI_STRING_PREFIX 6 - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Format: - - Standard InChI starts with: InChI=1S/ - Non-standard one with: InChI=1/ - Empty std InChI: InChI=1S// - Empty InChI: InChI=1// - AuxInfo=1// - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - - - -/* EXPORTED FUNCTIONS */ - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -GetINCHI / GetStdINCHI - - - inchi_Input is created by the user; strings in inchi_Output are allocated and deallocated by InChI - inchi_Output does not need to be initilized out to zeroes; see FreeNCHI()/FreeSTDINCHI() on how to deallocate it - - - Valid options for GetINCHI: - (use - instead of / for O.S. other than MS Windows) - - Structure perception (compatible with stdInChI) - /NEWPSOFF /DoNotAddH /SNon - Stereo interpretation (lead to generation of non-standard InChI) - /SRel /SRac /SUCF /ChiralFlagON /ChiralFlagOFF - InChI creation options (lead to generation of non-standard InChI) - /SUU /SLUUD /FixedH /RecMet /KET /15T - - - GetINCHI produces standard InChI if no InChI creation/stereo modification options - are specified. Inveresely, if any of SUU/SLUUD/RecMet/FixedH/Ket/15T/SRel/SRac/SUCF - options are specified, generated InChI will be non-standard one. - - - GetStdINCHI produces standard InChI only. - The valid structure perception options are: - /NEWPSOFF /DoNotAddH /SNon - - - Other options are: - /AuxNone Omit auxiliary information (default: Include) - /Wnumber Set time-out per structure in seconds; W0 means unlimited - In InChI library the default value is unlimited - /OutputSDF Output SDfile instead of InChI - /WarnOnEmptyStructure - Warn and produce empty InChI for empty structure - /SaveOpt Save custom InChI creation options (non-standard InChI) - - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetINCHI( inchi_Input *inp, inchi_Output *out ); -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStdINCHI( inchi_Input *inp, inchi_Output *out ); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -FreeINCHI / FreeStdINCHI - - should be called to deallocate char* pointers - obtained from each GetINCHI /GetStdINCHI call - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeINCHI ( inchi_Output *out ); -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeStdINCHI ( inchi_Output *out ); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -GetStringLength - - helper: get string length - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStringLength( char *p ); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -GetStructFromINCHI / GetStructFromStdINCHI - - inchi_Inputinchi_InputINCHI is created by the user; pointers in inchi_OutputStruct are allocated and deallocated by InChI - inchi_OutputStruct does not need to be initilized out to zeroes; see FreeStructFromStdINCHI() on how to deallocate it - Option /Inchi2Struct is not needed for GetStructFromINCHI()/GetStructFromStdINCHI() - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStructFromINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ); -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStructFromStdINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -FreeStructFromINCHI / FreeStructFromStdINCHI - - should be called to deallocate pointers obtained from each - GetStructFromStdINCHI / GetStructFromINCHI - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeStructFromINCHI( inchi_OutputStruct *out ); -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeStructFromStdINCHI( inchi_OutputStruct *out ); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -GetINCHIfromINCHI - - GetINCHIfromINCHI does same as -InChI2InChI option: converts InChI into InChI for validation purposes - It may also be used to filter out specific layers. For instance, /Snon would remove stereochemical layer - Omitting /FixedH and/or /RecMet would remove Fixed-H or Reconnected layers - To keep all InChI layers use options string "/FixedH /RecMet"; option /InChI2InChI is not needed - inchi_InputINCHI is created by the user; strings in inchi_Output are allocated and deallocated by InChI - inchi_Output does not need to be initilized out to zeroes; see FreeINCHI() on how to deallocate it - - Note: there is no explicit tool to conversion from/to standard InChI - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetINCHIfromINCHI( inchi_InputINCHI *inpInChI, inchi_Output *out ); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -/***************************************************************** - * - * - * C o n v e r s i o n: InChI AuxInfo string => inchi_Input - * - * - *****************************************************************/ - -#ifndef STR_ERR_LEN -#define STR_ERR_LEN 256 -#endif - -typedef struct tagInchiInpData { - inchi_Input *pInp; /* a pointer to pInp that has all items 0 or NULL */ - int bChiral; /* 1 => the structure was marked as chiral, 2=> not chiral, 0=> not marked */ - char szErrMsg[STR_ERR_LEN]; -} InchiInpData; - -/* to compile all InChI code as a C++ code #define COMPILE_ALL_CPP */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get_inchi_Input_FromAuxInfo / Get_std_inchi_Input_FromAuxInfo - -Input: - szInchiAuxInfo: contains ASCIIZ string of InChI output for a single - structure or only the AuxInfo line - bDoNotAddH: if 0 then InChI will be allowed to add implicit H - bDiffUnkUndfStereo - if not 0, use different labels for unknown and undefined stereo - pInchiInp: should have a valid pointer pInchiInp->pInp to an empty - (all members = 0) inchi_Input structure - -Output: - pInchiInp: The following members of pInp may be filled during the call: - atom, num_atoms, stereo0D, num_stereo0D - Return value: see RetValGetINCHI - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL Get_inchi_Input_FromAuxInfo( - char *szInchiAuxInfo, - int bDoNotAddH, - int bDiffUnkUndfStereo, - InchiInpData *pInchiInp ); -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL Get_std_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, - int bDoNotAddH, - InchiInpData *pInchiInp ); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Free_inchi_Input / Free_std_inchi_Input - - To deallocate and write zeroes into the changed members of pInchiInp->pInp call - Free_std_inchi_Input( inchi_Input *pInp ) - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL Free_inchi_Input( inchi_Input *pInp ); -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL Free_std_inchi_Input( inchi_Input *pInp ); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -CheckINCHI - -Check if the string represents valid InChI/standard InChI. -Input: - szINCHI source InChI - strict if 0, just briefly check for proper layout (prefix, version, etc.) - The result may not be strict. - If not 0, try to perform InChI2InChI conversion and - returns success if a resulting InChI string exactly match source. - The result may be 'false alarm' due to imperfectness of conversion. -Returns: - success/errors codes - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL CheckINCHI(const char *szINCHI, const int strict); - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - InChIKey API - - - InChIKey description - - - - The InChIKey is a character signature based on a hash code of the InChI string. - Standard InChIKey is produced out of standard InChI. - Non-standard InChIKey is produced out of non-standard InChI. - - AAAAAAAAAAAAAA-BBBBBBBBCD-P - - - InChIKey layout is as follows: - - AAAAAAAAAAAAAA - First block (14 letters) - Encodes molecular skeleton (connectivity) - - BBBBBBBB - Second block (8 letters) - Encodes tautomers, stereochemistry, isotopomers, reconnected layer - C - 'S' for standard - 'N' for non-standard - D - InChI version ('A' for 1) - P - (de)protonation flag - Protonization encoding: - N 0 - O +1 P +2 Q +3 R +4 S +5 T +6 U +7 V +8 W +9 X +10 Y +11 Z +12 - M -1 L-2 K -3 J -4 I -5 H -6 G -7 F -8 E -9 D -10 C -11 B -12 - A < -12 or > +12 - - - All symbols except delimiter (dash, that is, minus) are uppercase English - letters representing a "base 26" encoding. - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - -/*^^^ Return codes for key generation procedure */ -#define INCHIKEY_OK 0 -#define INCHIKEY_UNKNOWN_ERROR 1 -#define INCHIKEY_EMPTY_INPUT 2 -#define INCHIKEY_INVALID_INCHI_PREFIX 3 -#define INCHIKEY_NOT_ENOUGH_MEMORY 4 -#define INCHIKEY_INVALID_INCHI 20 -#define INCHIKEY_INVALID_STD_INCHI 21 - - -/*^^^ Return codes for CheckINCHIKey */ -typedef enum tagRetValGetINCHIKey -{ - INCHIKEY_VALID_STANDARD = 0, - INCHIKEY_VALID_NON_STANDARD = -1, - INCHIKEY_INVALID_LENGTH = 1, - INCHIKEY_INVALID_LAYOUT = 2, - INCHIKEY_INVALID_VERSION = 3 -} RetValCheckINCHIKeyv; - - - -/* EXPORTED FUNCTIONS */ - - - -/* To compile all InChI code as a C++ code #define COMPILE_ALL_CPP */ - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -GetINCHIKeyFromINCHI - -Calculate InChIKey by InChI string. - -Input: - szINCHISource - source InChI string - xtra1 - =1 calculate hash extension (up to 256 bits; 1st block) - xtra2 - =1 calculate hash extension (up to 256 bits; 2nd block) - -Output: - szINCHIKey - InChIKey string - The user-supplied buffer szINCHIKey should be at least 28 bytes long. - szXtra1 - hash extension (up to 256 bits; 1st block) string - Caller should allocate space for 64 characters + trailing NULL - szXtra2 - hash extension (up to 256 bits; 2nd block) string - Caller should allocate space for 64 characters + trailing NULL - -Returns: - success/errors codes - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetINCHIKeyFromINCHI(const char* szINCHISource, - const int xtra1, - const int xtra2, - char* szINCHIKey, - char* szXtra1, - char* szXtra2); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -GetStdINCHIKeyFromStdINCHI - - "Standard" counterpart - - For compatibility with v. 1.02std, no extra hash calculation is allowed. - To calculate extra hash(es), use GetINCHIKeyFromINCHI with stdInChI as input. - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStdINCHIKeyFromStdINCHI(const char* szINCHISource, - char* szINCHIKey); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -CheckINCHIKey - -Check if the string represents valid InChIKey. -Input: - szINCHIKey - source InChIKey string -Returns: - success/errors codes - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL CheckINCHIKey(const char *szINCHIKey); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Modularized InChI generation API - - - - Note. Functions with STDINCHIGEN prefix are - retained for compatibility with v. 1.02std - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - - - -/*^^^ Data structures holding intermediate (normalization) results */ - -#ifndef MAX_NUM_STEREO_ATOM_NEIGH -#define MAX_NUM_STEREO_ATOM_NEIGH 4 -#endif -#ifndef MAX_NUM_STEREO_BONDS -#define MAX_NUM_STEREO_BONDS 3 -#endif -#ifndef INCHI_NUM -#define INCHI_NUM 2 /* = array size; member indexes: */ -#endif - - -typedef unsigned short AT_NUMBR; -typedef signed short NUM_HS; -typedef unsigned long INCHI_MODES; - -typedef struct tagNormAtom -{ - char elname[ATOM_EL_LEN]; /* chem. element name */ - U_CHAR el_number; /* number of the element in the Periodic Table */ - AT_NUMBR neighbor[MAXVAL]; /* positions (from 0) of the neighbors in the NORM_ATOM array */ - AT_NUMBR orig_at_number; /* original atom number, starts from 1 */ - AT_NUMBR orig_compt_at_numb; /* atom number within a component before terminal H removal */ - S_CHAR bond_stereo[MAXVAL]; /* 1=Up,4=Either,6=Down (this atom is at the pointing wedge) - negative => on the opposite side of the wedge; 3=Either double bond */ - U_CHAR bond_type[MAXVAL]; /* 1=single, 2=double, 3=triple, 4=1/2 (bond order is 1 or 2) */ - /* 5=1/2/3, 6=1/3, 7=2/3, 8=tautomeric, 9=1/2 non-stereogenic */ - - S_CHAR valence; /* number of bonds = number of neighbors not greater than MAXVAL */ - S_CHAR chem_bonds_valence; /* sum of bond types (1,2,3); type 4 needs special treatment */ - S_CHAR num_H; /* number of adjacent implicit hydrogen atoms including D and T */ - S_CHAR num_iso_H[NUM_H_ISOTOPES];/* number of adjacent implicit 1H(protium), 2H(D), 3H(T) < 16 */ - S_CHAR iso_atw_diff; /* =0 => natural isotopic abundances */ - /* >0 => (isotopic mass) - (rounded average atomic mass) + 1 */ - /* <0 => (isotopic mass) - (rounded average atomic mass) */ - S_CHAR charge; /* charge */ - S_CHAR radical; /* RADICAL_SINGLET, RADICAL_DOUBLET, or RADICAL_TRIPLET */ - S_CHAR bAmbiguousStereo; /* flag of detected stereo ambiguity */ - S_CHAR cFlags; /* AT_FLAG_ISO_H_POINT: atom may have exchangeable isotopic H */ - AT_NUMBR at_type; /* ATT_NONE, ATT_ACIDIC, etc. See InChI normalization code */ - AT_NUMBR component; /* number of the structure component > 0 */ - AT_NUMBR endpoint; /* id of a tautomeric group */ - AT_NUMBR c_point; /* id of a positive charge group */ - double x; /* x coordinate */ - double y; /* y coordinate */ - double z; /* x coordinate */ - /*--------- 0D parities ----------*/ - S_CHAR bUsed0DParity; /* bit=1 => stereobond; bit=2 => stereocenter */ - /*----- tetrahedral stereo parity */ - S_CHAR p_parity; /* tetrahedral (sp3) cml parity */ - AT_NUMBR p_orig_at_num[MAX_NUM_STEREO_ATOM_NEIGH]; /* orig_at_number of each neighbor > 0; 0=> no neighbor */ - /*----- stereo bond (SB) parities */ - S_CHAR sb_ord[MAX_NUM_STEREO_BONDS]; /* neighbor[] index of another end of this SB, starts from 0 */ - S_CHAR sn_ord[MAX_NUM_STEREO_BONDS]; /* neighbor[] index of a bond that is not this SB; starts from 0; - -1 means the neighbor is a removed explicit H */ - /* atoms on both ends of a stereobond have same parity => trans/T/E/2, diff. parities => cis/C/Z/1 */ - S_CHAR sb_parity[MAX_NUM_STEREO_BONDS]; /* parities of stereobonds (sp2) incident to this atom */ - AT_NUMBR sn_orig_at_num[MAX_NUM_STEREO_BONDS]; /* orig_at_number of sn_ord[] neighbor > 0 */ - -#if ( FIND_RING_SYSTEMS == 1 ) - S_CHAR bCutVertex; /* is the atom a cut-vertex or not */ - AT_NUMBR nRingSystem; /* starts from 1; number of a ring system */ - AT_NUMBR nNumAtInRingSystem; /* number of atoms in a ring system to which this at belongs */ - AT_NUMBR nBlockSystem; /* ambiguous if the atom is a cut-vertex: better apply this to bonds */ - -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) - AT_NUMBR nDistanceFromTerminal; /* not used */ -#endif - -#endif -} NORM_ATOM; - - - -typedef struct tagNormAtomData -{ - NORM_ATOM *at; /* atom list */ - NORM_ATOM *at_fixed_bonds; /* atom list with added or removed protons only */ - int num_at; /* number of atoms except removed terminal H */ - int num_removed_H; /* number of removed H; at[] has (num_at+num_removed_H) elements */ - int num_bonds; - int num_isotopic; /* number of isotopic atoms */ - int bExists; /* for internal use */ - int bDeleted; /* for internal use */ - int bHasIsotopicLayer; - int bTautomeric; - int bTautPreprocessed; /* for internal use */ - int nNumRemovedProtons; - NUM_HS nNumRemovedProtonsIsotopic[NUM_H_ISOTOPES]; - /* isotopic composition of removed protons, not included in num_iso_H[] */ - NUM_HS num_iso_H[NUM_H_ISOTOPES]; - /* isotopic H on tautomeric atoms and those - in nIsotopicEndpointAtomNumber */ - INCHI_MODES bTautFlags; /* for internal use */ - INCHI_MODES bTautFlagsDone; /* for internal use */ - INCHI_MODES bNormalizationFlags;/* for internal use */ -} NORM_ATOMS; - - -typedef struct tagINCHIGEN_DATA -{ - - char pStrErrStruct[STR_ERR_LEN]; /* intermediate log (warning/error report) */ - int num_components[INCHI_NUM]; /* number of allocated INChI, INChI_Aux data structures */ - /* index=0 => disconnected, 1 => reconnected structure */ - - /*^^^ The results of normalization stage */ - /*^^^ for each member of pair disconnected/reconnected structures: */ - NORM_ATOMS *NormAtomsNontaut[INCHI_NUM]; - NORM_ATOMS *NormAtomsTaut[INCHI_NUM]; - -} INCHIGEN_DATA; - - -/*^^^ InChI Generator Handle */ - -typedef void* INCHIGEN_HANDLE; - - - - -/* EXPORTED FUNCTIONS */ - - - -/* to compile all InChI code as a C++ code #define COMPILE_ALL_CPP */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -INCHIGEN_Create / STDINCHIGEN_Create - - InChI Generator: create generator - Returns handle of generator object or NULL on failure - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API -INCHIGEN_HANDLE INCHI_DECL INCHIGEN_Create(void); -EXPIMP_TEMPLATE INCHI_API -INCHIGEN_HANDLE INCHI_DECL STDINCHIGEN_Create(void); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -INCHIGEN_Setup / STDINCHIGEN_Setup - - InChI Generator: setup - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL INCHIGEN_Setup(INCHIGEN_HANDLE HGen, - INCHIGEN_DATA * pGenData, - inchi_Input * pInp); -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL STDINCHIGEN_Setup(INCHIGEN_HANDLE HGen, - INCHIGEN_DATA * pGenData, - inchi_Input * pInp); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -INCHIGEN_DoNormalization / STDINCHIGEN_DoNormalization - - InChI Generator: structure normalization stage - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL INCHIGEN_DoNormalization(INCHIGEN_HANDLE HGen, - INCHIGEN_DATA * pGenData); -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL STDINCHIGEN_DoNormalization(INCHIGEN_HANDLE HGen, - INCHIGEN_DATA * pGenData); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -INCHIGEN_DoCanonicalization / STDINCHIGEN_DoCanonicalization - - InChI Generator: structure canonicalization stage - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL INCHIGEN_DoCanonicalization - (INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData); -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL STDINCHIGEN_DoCanonicalization - (INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -INCHIGEN_DoSerialization / STDINCHIGEN_DoSerialization - - InChI Generator: InChI serialization stage - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL INCHIGEN_DoSerialization(INCHIGEN_HANDLE HGen, - INCHIGEN_DATA * pGenData, - inchi_Output * pResults); -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL STDINCHIGEN_DoSerialization(INCHIGEN_HANDLE HGen, - INCHIGEN_DATA * pGenData, - inchi_Output * pResults); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -INCHIGEN_DoSerialization / STDINCHIGEN_DoSerialization - - InChI Generator: reset stage (use before get next structure) - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API -void INCHI_DECL INCHIGEN_Reset(INCHIGEN_HANDLE HGen, - INCHIGEN_DATA * pGenData, - inchi_Output * pResults); -EXPIMP_TEMPLATE INCHI_API -void INCHI_DECL STDINCHIGEN_Reset(INCHIGEN_HANDLE HGen, - INCHIGEN_DATA * pGenData, - inchi_Output * pResults); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -INCHIGEN_DoSerialization / STDINCHIGEN_DoSerialization - - InChI Generator: destroy generator - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL INCHIGEN_Destroy(INCHIGEN_HANDLE HGen); -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL STDINCHIGEN_Destroy(INCHIGEN_HANDLE HGen); - - - - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Prototypes for C calling conventions: - - int GetINCHI( inchi_Input *inp, inchi_Output *out ); - int GetStdINCHI( inchi_Input *inp, inchi_Output *out ); - void FreeINCHI( inchi_Output *out ); - void FreeStdINCHI( inchi_Output *out ); - int GetStringLength( char *p ); - int Get_inchi_Input_FromAuxInfo - ( char *szInchiAuxInfo, int bDoNotAddH, int bDiffUnkUndfStereo, InchiInpData *pInchiInp ); - int Get_std_inchi_Input_FromAuxInfo - ( char *szInchiAuxInfo, int bDoNotAddH, int bDiffUnkUndfStereo,InchiInpData *pInchiInp ); - void Free_inchi_Input( inchi_Input *pInp ); - void Free_std_inchi_Input( inchi_Input *pInp ); - int GetStructFromINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ); - int GetStructFromStdINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ); - void FreeStructFromStdINCHI( inchi_OutputStruct *out ); - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Win32 Dumpbin export information - - ordinal hint RVA name -cdecl - 1 0 000B7EAB CheckINCHI - 2 1 000B7221 CheckINCHIKey - 3 2 000B7C62 FreeINCHI - 4 3 000B7B04 FreeStdINCHI - 5 4 000B72B7 FreeStructFromINCHI - 6 5 000B7BC2 FreeStructFromStdINCHI - 7 6 000B7E33 Free_inchi_Input - 8 7 000B7C58 Free_std_inchi_Input - 9 8 000B727B GetINCHI - 10 9 000B75B4 GetINCHIKeyFromINCHI - 11 A 000B757D GetINCHIfromINCHI - 12 B 000B8211 GetStdINCHI - 13 C 000B7F0A GetStdINCHIKeyFromStdINCHI - 14 D 000B77CB GetStringLength - 15 E 000B7CA3 GetStructFromINCHI - 16 F 000B778A GetStructFromStdINCHI - 17 10 000B7DAC Get_inchi_Input_FromAuxInfo - 18 11 000B7D6B Get_std_inchi_Input_FromAuxInfo - 19 12 000B7D6B INCHIGEN_Create - 20 13 000B7F2D INCHIGEN_Destroy - 21 14 000B7F23 INCHIGEN_DoCanonicalization - 22 15 000B7F23 INCHIGEN_DoNormalization - 23 16 000B714A INCHIGEN_DoSerialization - 24 17 000B7FCD INCHIGEN_Reset - 25 18 000B7FCD INCHIGEN_Setup - 26 19 000B7EA6 STDINCHIGEN_Create - 27 1A 000B7EA6 STDINCHIGEN_Destroy - 28 1B 000B711D STDINCHIGEN_DoCanonicalization - 29 1C 000B7073 STDINCHIGEN_DoNormalization - 30 1D 000B7FC3 STDINCHIGEN_DoSerialization - 31 1E 000B7668 STDINCHIGEN_Reset - 32 1F 000B7438 STDINCHIGEN_Setup -__stdcall or PASCAL - 33 20 000B7DFC _CheckINCHI@8 - 34 21 000B7802 _CheckINCHIKey@4 - 35 22 000B7F73 _FreeINCHI@4 - 36 23 000B7F82 _FreeStdINCHI@4 - 37 24 000B75E1 _FreeStructFromINCHI@4 - 38 25 000B7B81 _FreeStructFromStdINCHI@4 - 39 26 000B7B86 _Free_inchi_Input@4 - 40 27 000B7A96 _Free_std_inchi_Input@4 - 41 28 000B7B5E _GetINCHI@8 - 42 29 000B7285 _GetINCHIKeyFromINCHI@24 - 43 2A 000B758C _GetINCHIfromINCHI@8 - 44 2B 000B7CDA _GetStdINCHI@8 - 45 2C 000B7979 _GetStdINCHIKeyFromStdINCHI@8 - 46 2D 000B7BA4 _GetStringLength@4 - 47 2E 000B70A5 _GetStructFromINCHI@8 - 48 2F 000B79B0 _GetStructFromStdINCHI@8 - 49 30 000B8022 _Get_inchi_Input_FromAuxInfo@16 - 50 31 000B76E0 _Get_std_inchi_Input_FromAuxInfo@12 - 51 32 000B7230 _INCHIGEN_Create@0 - 52 33 000B760E _INCHIGEN_Destroy@4 - 53 34 000B7087 _INCHIGEN_DoCanonicalization@8 - 54 35 000B70B4 _INCHIGEN_DoNormalization@8 - 55 36 000B72D5 _INCHIGEN_DoSerialization@12 - 56 37 000B7FE1 _INCHIGEN_Reset@12 - 57 38 000B7163 _INCHIGEN_Setup@12 - 58 39 000B7159 _STDINCHIGEN_Create@0 - 59 3A 000B78A7 _STDINCHIGEN_Destroy@4 - 60 3B 000B72F3 _STDINCHIGEN_DoCanonicalization@8 - 61 3C 000B737A _STDINCHIGEN_DoNormalization@8 - 62 3D 000B7B72 _STDINCHIGEN_DoSerialization@12 - 63 3E 000B7654 _STDINCHIGEN_Reset@12 - 64 3F 000B75FF _STDINCHIGEN_Setup@12 - - - Note. Currently there is no callback function for aborting, progress, etc. - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -#endif /* __INHCH_API_H__ */ diff --git a/INCHI-1-SRC/INCHI/common/inchicmp.h b/INCHI-1-SRC/INCHI/common/inchicmp.h deleted file mode 100644 index bfec9b8..0000000 --- a/INCHI-1-SRC/INCHI/common/inchicmp.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHICMP_H__ -#define __INCHICMP_H__ - -typedef enum tagInchiCompareDiffBits { - INCHIDIFF_ZERO = 0x00000000, - INCHIDIFF_PROBLEM = 0x00000001, /* severe: at least one InChI does not exist */ - INCHIDIFF_NUM_AT = 0x00000001, /* severe: different number of atoms in the skeleton */ - INCHIDIFF_ATOMS = 0x00000001, /* severe: diiferent types of skeleton atoms */ - INCHIDIFF_NUM_EL = 0x00000001, /* severe: formulas differ in another element */ - INCHIDIFF_CON_LEN = 0x00000001, /* severe: different connection table lengths */ - INCHIDIFF_CON_TBL = 0x00000001, /* severe: different connection tables */ - INCHIDIFF_POSITION_H = 0x00000002, /* difference in non-taut (Mobile-H) or all H (Fixed-H) location/number */ - INCHIDIFF_MORE_FH = 0x00000004, /* extra fixed H */ - INCHIDIFF_LESS_FH = 0x00000004, /* missing fixed H */ - INCHIDIFF_MORE_H = 0x00000008, /* formulas differ in number of H */ - INCHIDIFF_LESS_H = 0x00000008, /* formulas differ in number of H */ - INCHIDIFF_NO_TAUT = 0x00000010, /* restored structure has no taut groups while the original InChI has some */ - INCHIDIFF_WRONG_TAUT = 0x00000020, /* restored has tautomerism while the original does not have it */ - INCHIDIFF_SINGLE_TG = 0x00000040, /* restored has 1 taut. group while the original InChI has multiple tg */ - INCHIDIFF_MULTIPLE_TG = 0x00000080, /* restored has multiple tg while the original InChI has only one tg */ - INCHIDIFF_EXTRA_TG_ENDP = 0x00000100, /* extra tautomeric endpoint(s) in restored structure */ - INCHIDIFF_MISS_TG_ENDP = 0x00000100, /* one or more tg endpoint is not in the restored structure */ - INCHIDIFF_DIFF_TG_ENDP = 0x00000100, /* lists of tg endpoints are different */ - INCHIDIFF_NUM_TG = 0x00000200, /* different number of tautomeric groups */ - INCHIDIFF_TG = 0x00000200, /* different tautomeric groups */ - INCHIDIFF_NUM_ISO_AT = 0x00000400, /* ?severe: restored struct. has different number of isotopic atoms */ - INCHIDIFF_ISO_AT = 0x00000400, /* ?severe: restored struct. has different locations/isotopes of isotopic atoms */ - INCHIDIFF_REM_ISO_H = 0x00000800, /* isotopic H removed */ - INCHIDIFF_MOB_ISO_H = 0x00001000, /* different number of mobile exchangeable isotopic H */ - INCHIDIFF_CHARGE = 0x00002000, /* restored structure has different charge */ - INCHIDIFF_REM_PROT = 0x00004000, /* proton(s) removed/added from the restored structure */ - INCHIDIFF_MOBH_PROTONS = 0x00008000, /* different proton balance */ - INCHIDIFF_SC_INV = 0x00010000, /* restores structure has different inversion stereocenter mark */ - INCHIDIFF_SC_PARITY = 0x00020000, /* restored structure has stereoatoms or allenes with different parity */ - INCHIDIFF_SC_EXTRA_UNDF = 0x00040000, /* restored structure has extra undefined stereocenter(s) */ - INCHIDIFF_SC_EXTRA = 0x00080000, /* restored structure has extra stereocenter(s) */ - INCHIDIFF_SC_MISS_UNDF = 0x00100000, /* restored structure has not some undefined stereocenter(s) */ - INCHIDIFF_SC_MISS = 0x00200000, /* restored structure has not some stereocenters that are not undefined */ - INCHIDIFF_SB_PARITY = 0x00400000, /* restored structure has stereobonds or cumulenes with different parity */ - INCHIDIFF_SB_EXTRA_UNDF = 0x00800000, /* restored structure has extra undefined stereobond(s) */ - INCHIDIFF_SB_EXTRA = 0x01000000, /* restored structure has extra stereobond(s) */ - INCHIDIFF_SB_MISS_UNDF = 0x02000000, /* restored structure has not some undefined stereocenters */ - INCHIDIFF_SB_MISS = 0x04000000, /* restored structure has not some stereobonds that are not undefined */ - INCHIDIFF_COMP_HLAYER = 0x08000000, /* Restored component has Mobile-H layer instead of both Mobile-H & Fixed-H or both instead of one */ - INCHIDIFF_COMP_NUMBER = 0x10000000, /* wrong number of components */ - INCHIDIFF_STR2INCHI_ERR = 0x20000000 /* Restored structure to InChI conversion error */ - /* reserved - 0x40000000 - 0x80000000 - */ -} INCHIDIFF; - -typedef enum tagtagCompareInchiMsgGroupID { - IDGRP_ZERO = 0, - IDGRP_ERR = 1, - IDGRP_H = 2, - IDGRP_MOB_GRP = 3, - IDGRP_ISO_AT = 4, - IDGRP_CHARGE = 5, - IDGRP_PROTONS = 6, - IDGRP_ISO_H = 7, - IDGRP_SC = 8, - IDGRP_SB = 9, - IDGRP_HLAYER =10, - IDGRP_COMP =11, - IDGRP_CONV_ERR =12 -} CMP_INCHI_MSG_GROUP_ID; - - -typedef struct tagCompareInchiMsg { - INCHIDIFF nBit; - CMP_INCHI_MSG_GROUP_ID nGroupID; - const char *szMsg; -} CMP_INCHI_MSG; - -typedef struct tagCompareInchiMsgGroup { - CMP_INCHI_MSG_GROUP_ID nGroupID; - const char *szGroupName; -} CMP_INCHI_MSG_GROUP; - - - - - - -#define INCHIDIFF_SB (INCHIDIFF_SB_PARITY | INCHIDIFF_SB_EXTRA_UNDF | INCHIDIFF_SB_EXTRA | INCHIDIFF_SB_MISS_UNDF | INCHIDIFF_SB_MISS) -#define INCHIDIFF_SC (INCHIDIFF_SC_PARITY | INCHIDIFF_SC_EXTRA_UNDF | INCHIDIFF_SC_EXTRA | INCHIDIFF_SC_MISS_UNDF | INCHIDIFF_SC_MISS) - -#define INCHIDIFF_CONSTIT (INCHIDIFF_POSITION_H | INCHIDIFF_MORE_FH | INCHIDIFF_LESS_FH | INCHIDIFF_MORE_H | INCHIDIFF_LESS_H |\ - INCHIDIFF_NO_TAUT | INCHIDIFF_WRONG_TAUT | INCHIDIFF_SINGLE_TG | INCHIDIFF_MULTIPLE_TG | \ - INCHIDIFF_NUM_TG | INCHIDIFF_EXTRA_TG_ENDP | INCHIDIFF_MISS_TG_ENDP | INCHIDIFF_TG | \ - INCHIDIFF_NUM_ISO_AT | INCHIDIFF_ISO_AT | INCHIDIFF_CHARGE | INCHIDIFF_REM_PROT | INCHIDIFF_REM_ISO_H |\ - INCHIDIFF_DIFF_TG_ENDP) -#define INCHIDIFF_STEREO (INCHIDIFF_SC_INV | INCHIDIFF_SC_PARITY | INCHIDIFF_SC_EXTRA_UNDF | INCHIDIFF_SC_EXTRA | \ - INCHIDIFF_SC_MISS_UNDF | INCHIDIFF_SC_MISS | INCHIDIFF_SB_PARITY | INCHIDIFF_SB_EXTRA_UNDF |\ - INCHIDIFF_SB_EXTRA | INCHIDIFF_SB_MISS_UNDF | INCHIDIFF_SB_MISS) - -#endif /* __INCHICMP_H__ */ diff --git a/INCHI-1-SRC/INCHI/common/inpdef.h b/INCHI-1-SRC/INCHI/common/inpdef.h deleted file mode 100644 index a6b19ce..0000000 --- a/INCHI-1-SRC/INCHI/common/inpdef.h +++ /dev/null @@ -1,369 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -/* input/output format */ -#ifndef __INPDEF_H__ -#define __INPDEF_H__ - -/*^^^ */ -#include "mode.h" -#include "incomdef.h" -#include "ichidrp.h" -/*^^^ */ - -#define bDrawingLabelLeftShift endpoint /* for drawing only */ -typedef S_SHORT ST_CAP_FLOW; - -/* inp_ATOM::at_type */ -#define ATT_NONE 0x0000 -#define ATT_ACIDIC_CO 0x0001 -#define ATT_ACIDIC_S 0x0002 -#define ATT_OO 0x0004 -#define ATT_ZOO 0x0008 -#define ATT_NO 0x0010 -#define ATT_N_O 0x0020 -#define ATT_ATOM_N 0x0040 -#define ATT_ATOM_P 0x0080 -#define ATT_OTHER_NEG_O 0x0100 -#define ATT_OTHER_ZO 0x0200 /* -Z=O or =Z=O */ -#define ATT_OH_MINUS 0x0400 /* OH(-), O=O,S,Se,Te */ -#define ATT_O_PLUS 0x0800 /* -OH2(+), =OH(+), -OH(+)-, OH3(+), =O(+)-, etc; O=O,S,Se,Te */ -#define ATT_PROTON 0x1000 -#define ATT_HalAnion 0x2000 -#define ATT_HalAcid 0x4000 -#if ( FIX_NP_MINUS_BUG == 1 ) -#define ATT_NP_MINUS_V23 0x8000 /* =N(-) or =P(-) where = previously was triple */ -#endif - -#define AT_FLAG_ISO_H_POINT 0x01 /* may have isotopic H */ - -#define PERIODIC_NUMBER_H 1 - -#ifndef NUMH -#define NUM_ISO_H(AT,N) (AT[N].num_iso_H[0]+AT[N].num_iso_H[1]+AT[N].num_iso_H[2]) -#define NUMH(AT,N) (AT[N].num_H+NUM_ISO_H(AT,N)) -#endif - -#define FlagSC_0D 1 /* bUsed0DParity */ -#define FlagSB_0D 2 /* bUsed0DParity */ - -#define SB_PARITY_FLAG 0x38 /* mask for disconnected metal parity if it is different */ -#define SB_PARITY_SHFT 3 /* number of right shift bits to get disconnected metal parity */ -#define SB_PARITY_MASK 0x07 -#define SB_PARITY_1(X) (X & SB_PARITY_MASK) /* refers to connected structure */ -#define SB_PARITY_2(X) (((X) >> SB_PARITY_SHFT) & SB_PARITY_MASK) /* refers to connected structure */ - - - -typedef struct tagInputAtom { - char elname[ATOM_EL_LEN]; /* chem. element name */ - U_CHAR el_number; /* number of the element in the Periodic Table */ - AT_NUMB neighbor[MAXVAL]; /* positions (from 0) of the neighbors in the inp_ATOM array */ - AT_NUMB orig_at_number; /* original atom number */ - AT_NUMB orig_compt_at_numb; /* atom number within the component before terminal H removal */ - S_CHAR bond_stereo[MAXVAL]; /* 1=Up,4=Either,6=Down; this atom is at the pointing wedge, - negative => on the opposite side; 3=Either double bond */ - U_CHAR bond_type[MAXVAL]; /* 1..4; 4="aromatic", should be discouraged on input */ - - S_CHAR valence; /* number of bonds = number of neighbors */ - S_CHAR chem_bonds_valence; /* sum of bond types (type 4 needs special treatment) */ - S_CHAR num_H; /* number of implicit hydrogens including D and T */ - S_CHAR num_iso_H[NUM_H_ISOTOPES]; /* number of implicit 1H, 2H(D), 3H(T) < 16 */ - S_CHAR iso_atw_diff; /* =0 => natural isotopic abundances */ - /* >0 => (mass) - (mass of the most abundant isotope) + 1 */ - /* <0 => (mass) - (mass of the most abundant isotope) */ - S_CHAR charge; /* charge */ - S_CHAR radical; /* RADICAL_SINGLET, RADICAL_DOUBLET, or RADICAL_TRIPLET */ - S_CHAR bAmbiguousStereo; - S_CHAR cFlags; /* AT_FLAG_ISO_H_POINT */ - AT_NUMB at_type; /* ATT_NONE, ATT_ACIDIC */ - AT_NUMB component; /* number of the structure component > 0 */ - AT_NUMB endpoint; /* id of a tautomeric group */ - AT_NUMB c_point; /* id of a positive charge group */ - double x; - double y; - double z; - /* cml 0D parities */ - S_CHAR bUsed0DParity; /* bit=1 => stereobond; bit=2 => stereocenter */ - /* cml tetrahedral parity */ - S_CHAR p_parity; - AT_NUMB p_orig_at_num[MAX_NUM_STEREO_ATOM_NEIGH]; - /* cml bond parities */ - S_CHAR sb_ord[MAX_NUM_STEREO_BONDS]; /* stereo bond/neighbor ordering number, starts from 0 */ - /* neighbors on both sides of stereobond have same sign=> trans/T/E, diff. signs => cis/C/Z */ - S_CHAR sn_ord[MAX_NUM_STEREO_BONDS]; /* ord. num. of the neighbor adjacent to the SB; starts from 0; - -1 means removed explicit H */ - /* neighbors on both sides of stereobond have same parity => trans/T/E/2, diff. parities => cis/C/Z/1 */ - S_CHAR sb_parity[MAX_NUM_STEREO_BONDS]; - AT_NUMB sn_orig_at_num[MAX_NUM_STEREO_BONDS]; /* orig. at number of sn_ord[] neighbors */ - -#if ( FIND_RING_SYSTEMS == 1 ) - S_CHAR bCutVertex; - AT_NUMB nRingSystem; - AT_NUMB nNumAtInRingSystem; - AT_NUMB nBlockSystem; - -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) - AT_NUMB nDistanceFromTerminal; /* terminal atom or ring system has 1, next has 2, etc. */ -#endif - -#endif -} inp_ATOM; - -typedef struct tagOrigAtom { - /* initially filled out by MolfileToOrigAtom */ - /* may be changed by disconnecting salts and disconnecting metals */ - inp_ATOM *at; - int num_dimensions; - int num_inp_bonds; - int num_inp_atoms; - /* may be changed by disconnecting salts and disconnecting metals */ - int num_components; /* set by MarkDisconnectedComponents() and disconnecting metals */ - int bDisconnectSalts; /* whether salt disconnection is possible */ - int bDisconnectCoord; /* 0 if no disconnection needed else (Num Implicit H to disconnect)+1 */ -#if ( bRELEASE_VERSION == 0 ) - int bExtract; -#endif - AT_NUMB *nCurAtLen; /* has max_num_components elements */ - AT_NUMB *nOldCompNumber; /* 0 or component number in previous numbering */ - int nNumEquSets; /* number of found component equivalence sets */ - AT_NUMB *nEquLabels; /* num_inp_atoms elements, value>0 marks atoms in the set #value */ - AT_NUMB *nSortedOrder; /* num_components elements, values = 1..num_components; only if num_components > 1 */ - int bSavedInINCHI_LIB[INCHI_NUM]; - int bPreprocessed[INCHI_NUM]; - MOL_COORD *szCoord; -} ORIG_ATOM_DATA; - -typedef struct tagOriginalStruct { - int num_atoms; - char *szAtoms; - char *szBonds; - char *szCoord; -} ORIG_STRUCT; - -typedef struct tagAtomParmsForDrawing { - char at_string[ATOM_INFO_LEN]; - int DrawingLabelLeftShift; - int DrawingLabelLength; - AT_NUMB nCanonNbr; /* if zero then do not use all data for the atom */ - AT_NUMB nCanonEquNbr; - AT_NUMB nTautGroupCanonNbr; - AT_NUMB nTautGroupEquNbr; - S_CHAR cFlags; /* AT_FLAG_ISO_H_POINT */ -#ifdef DISPLAY_DEBUG_DATA - int nDebugData; -#endif - S_CHAR cHighlightTheAtom; - S_CHAR cStereoCenterParity; - S_CHAR cStereoBondParity[MAX_STEREO_BONDS]; - S_CHAR cStereoBondWarning[MAX_STEREO_BONDS]; - S_CHAR cStereoBondNumber[MAX_STEREO_BONDS]; -} inf_ATOM; - - -#define INF_STEREO_ABS 0x0001 -#define INF_STEREO_REL 0x0002 -#define INF_STEREO_RAC 0x0004 -#define INF_STEREO_NORM 0x0008 -#define INF_STEREO_INV 0x0010 -#define INF_STEREO 0x0020 -#define INF_STEREO_ABS_REL_RAC (INF_STEREO_ABS | INF_STEREO_REL | INF_STEREO_RAC) -#define INF_STEREO_NORM_INV (INF_STEREO_NORM | INF_STEREO_INV) - -#define MAX_LEN_REMOVED_PROTONS 128 - -typedef struct tagInfoAtomData { - inf_ATOM *at; - int num_at; - AT_NUMB StereoFlags; - AT_NUMB num_components; - AT_NUMB *pStereoFlags; - - int nNumRemovedProtons; - int num_removed_iso_H; /* number of exchangable isotopic H */ - NUM_H num_iso_H[NUM_H_ISOTOPES]; /* number of exchangable isotopic H */ - char szRemovedProtons[MAX_LEN_REMOVED_PROTONS]; -} INF_ATOM_DATA; - -typedef struct tagInputAtomData { - inp_ATOM *at; - inp_ATOM *at_fixed_bonds; /* tautomeric case, added or removed H */ - int num_at; - int num_removed_H; - int num_bonds; - int num_isotopic; - int bExists; - int bDeleted; - int bHasIsotopicLayer; - int bTautomeric; - int bTautPreprocessed; - int nNumRemovedProtons; - NUM_H nNumRemovedProtonsIsotopic[NUM_H_ISOTOPES]; /* isotopic composition of removed protons, not included in num_iso_H[] */ - NUM_H num_iso_H[NUM_H_ISOTOPES]; /* isotopic H on tautomeric atoms and those in nIsotopicEndpointAtomNumber */ - INCHI_MODE bTautFlags; - INCHI_MODE bTautFlagsDone; - INCHI_MODE bNormalizationFlags; -} INP_ATOM_DATA; -typedef INP_ATOM_DATA INP_ATOM_DATA2[TAUT_NUM]; - -typedef struct tagNormCanonFlags { - INCHI_MODE bTautFlags[INCHI_NUM][TAUT_NUM]; - INCHI_MODE bTautFlagsDone[INCHI_NUM][TAUT_NUM]; - INCHI_MODE bNormalizationFlags[INCHI_NUM][TAUT_NUM]; - int nCanonFlags[INCHI_NUM][TAUT_NUM]; -} NORM_CANON_FLAGS; - -typedef struct tagCompositeAtomData { - inp_ATOM *at; - int num_at; - int num_removed_H; - int num_bonds; - int num_isotopic; - int bExists; - int bDeleted; /* unused */ - int bHasIsotopicLayer; - int bTautomeric; - int nNumRemovedProtons; - NUM_H nNumRemovedProtonsIsotopic[NUM_H_ISOTOPES]; /* isotopic composition of removed protons, not included in num_iso_H[] */ - NUM_H num_iso_H[NUM_H_ISOTOPES]; /* isotopic H on tautomeric atoms and those in nIsotopicEndpointAtomNumber */ - - AT_NUMB *nOffsetAtAndH; - int num_components; -} COMP_ATOM_DATA; -/* -typedef COMP_ATOM_DATA COMP_ATOM_DATA3[TAUT_NUM+1]; -*/ -#define ADD_LEN_STRUCT_FPTRS 100 /* allocation increments */ -typedef long INCHI_FPTR; -typedef struct tagStructFptrs { - INCHI_FPTR *fptr; /* input: fptr[cur_fptr] = file pointer to the structure to read */ - /* output: fptr[cur_fptr+1] = file pointer to the next structure or EOF */ - int len_fptr; /* allocated length of fptr */ - int cur_fptr; /* input: k-1 to read the kth struct, k = 1, 2, 3,...; left unchanged; struct number := cur_fptr+1 */ - int max_fptr; /* length of the filled out portion of fptr */ -} STRUCT_FPTRS; - -#define FLAG_INP_AT_CHIRAL 1 -#define FLAG_INP_AT_NONCHIRAL 2 -#define FLAG_SET_INP_AT_CHIRAL 4 -#define FLAG_SET_INP_AT_NONCHIRAL 8 - -/* BILLY 8/6/04 */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -int MolfileToInpAtom( FILE *inp_molfile, int bDoNotAddH, inp_ATOM **at, MOL_COORD **szCoord, int max_num_at, - int *num_dimensions, int *num_bonds, const char *pSdfLabel, char *pSdfValue, - long *Id, long *lMolfileNumber, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ); -int MolfileToOrigAtom( FILE *inp_molfile, ORIG_ATOM_DATA *orig_at_data, int bMergeAllInputStructures, - int bGetOrigCoord, int bDoNotAddH, - const char *pSdfLabel, char *pSdfValue, long *lSdfId, long *lMolfileNumber, - INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ); -int INChIToOrigAtom( INCHI_IOSTREAM *inp_molfile, ORIG_ATOM_DATA *orig_at_data, int bMergeAllInputStructures, - int bGetOrigCoord, int bDoNotAddH, int vABParityUnknown, INPUT_TYPE nInputType, - char *pSdfLabel, char *pSdfValue, long *lSdfId, - INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ); - -int MarkDisconnectedComponents( ORIG_ATOM_DATA *orig_at_data, int bProcessOldCompNumbers ); -int DisconnectSalts( ORIG_ATOM_DATA *orig_inp_data, int bDisconnect ); -int DisconnectMetals( ORIG_ATOM_DATA *orig_inp_data, int bCheckMetalValence, INCHI_MODE *bTautFlagsDone ); -int bMayDisconnectMetals( ORIG_ATOM_DATA *orig_inp_data, int bCheckMetalValence, INCHI_MODE *bTautFlagsDone ); -int bHasMetalAtom( ORIG_ATOM_DATA *orig_inp_data ); -int FixAdjacentRadicals( int num_inp_atoms, inp_ATOM *at ); /* FIX_ADJ_RAD == 1 */ -int fix_odd_things( int num_atoms, inp_ATOM *at, int bFixBug, int bFixNonUniformDraw ); -int post_fix_odd_things( int num_atoms, inp_ATOM *at ); -int remove_ion_pairs( int num_atoms, inp_ATOM *at ); - -int bFoundFeature( inp_ATOM *at, int num_atoms ); -int CopyMOLfile(FILE *inp_file, long fPtrStart, long fPtrEnd, FILE *prb_file, long nNumb); - -void FreeInpAtom( inp_ATOM **at ); -void FreeInfAtom( inf_ATOM **at ); -void FreeOrigAtData( ORIG_ATOM_DATA *orig_at_data ); -void FreeInpAtomData( INP_ATOM_DATA *inp_at_data ); -void FreeCompAtomData( COMP_ATOM_DATA *inp_at_data ); -void FreeInfoAtomData( INF_ATOM_DATA *inf_at_data ); - -int FixUnkn0DStereoBonds(inp_ATOM *at, int num_at); - -inf_ATOM *CreateInfAtom( int num_atoms ); -inp_ATOM *CreateInpAtom( int num_atoms ); - -int CreateInfoAtomData( INF_ATOM_DATA *inf_at_data, int num_atoms, int num_components ); -int AllocateInfoAtomData( INF_ATOM_DATA *inf_at_data, int num_atoms, int num_components ); -int DuplicateInfoAtomData( INF_ATOM_DATA *inf_at_data_to, const INF_ATOM_DATA *inf_at_data_from); -int CreateInpAtomData( INP_ATOM_DATA *inp_at_data, int num_atoms, int create_at_fixed_bonds ); -int CreateCompAtomData( COMP_ATOM_DATA *inp_at_data, int num_atoms, int num_components, int bIntermediateTaut ); -#ifndef COMPILE_ANSI_ONLY -int DisplayInputStructure( char *szOutputString, inp_ATOM *at, INF_ATOM_DATA *inf_at_data, int num_at, DRAW_PARMS *dp ); -#endif -void PrintFileName( const char *fmt, FILE *output_file, const char *szFname ); -void MySleep( unsigned long ms ); - -#ifndef __ICHITIME_H__ -struct tagInchiTime; -int bInchiTimeIsOver( struct tagInchiTime *TickEnd ); -#endif - -int get_endpoint_valence( U_CHAR el_number ); -#if ( KETO_ENOL_TAUT == 1 ) -int get_endpoint_valence_KET( U_CHAR el_number ); -#endif - -#if ( TEST_RENUMB_ATOMS == 1 ) /* { */ -int CopyInpAtomData( INP_ATOM_DATA *dest_inp_at_data, INP_ATOM_DATA *src_inp_at_data ); -void RenumbInpAtomData( INP_ATOM_DATA *dest_inp_at_data, INP_ATOM_DATA *src_inp_at_data, AT_RANK *new_ord ); -void MakeNewOrd( int num_atoms, AT_RANK *new_ord ); -#endif - -int ReconcileAllCmlBondParities( inp_ATOM *at, int num_atoms, int bDisconnected ); - - -/* BILLY 8/6/04 */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -#endif /* __INPDEF_H__ */ diff --git a/INCHI-1-SRC/INCHI/common/lreadmol.h b/INCHI-1-SRC/INCHI/common/lreadmol.h deleted file mode 100644 index 667cf20..0000000 --- a/INCHI-1-SRC/INCHI/common/lreadmol.h +++ /dev/null @@ -1,1290 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -/* local prototypes */ -int bypass_sdf_data_items( FILE* inp, long *cas_reg_no, char* comment, int lcomment, char *name, int lname, int prev_err, - const char *pSdfLabel, char *pSdfValue, char *pStrErr ); -MOL_DATA* read_mol_file( FILE* inp, MOL_HEADER_BLOCK *OnlyHeaderBlock, MOL_CTAB *OnlyCtab, - int bGetOrigCoord, int *err, char *pStrErr ); - - -static int mol_read_hdr(MOL_HEADER_BLOCK *hdr, FILE* inp, char *pStrErr); -static int mol_read_counts_line( MOL_CTAB* ctab, FILE *inp, char *pStrErr ); -static int read_atom_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ); -static int read_bonds_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ); -static int read_stext_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ); -static int read_properties_block( MOL_CTAB* ctab, MOL_HEADER_BLOCK *pHdr, FILE *inp, int err, char *pStrErr ); - -static int identify_sdf_label( char* inp_line, const char *pSdfLabel ); -static long extract_cas_rn( char *line ); -static int mol_copy_check_empty( char* dest, char* source, int len, char **first_space ); -static int mol_read_datum(void* data, int field_len, int data_type, char** line_ptr); - -static int RemoveNonPrintable( char *line ); - - -/******/ -#ifndef MOLFILE_ERR_FIN -#define MOLFILE_ERR_FIN(err, new_err, err_fin, msg) \ - if ( !(err) && (new_err) ) { (err) = (new_err);} AddMOLfileError(pStrErr, (msg)); goto err_fin -#endif -#ifndef MOLFILE_ERR_SET -#define MOLFILE_ERR_SET(err, new_err, msg) \ - if ( !(err) && (new_err) ) { (err) = (new_err);} AddMOLfileError(pStrErr, (msg)) -#endif - -/*************************************************************************/ -int AddMOLfileError( char *pStrErr, const char *szMsg ) -{ - if ( pStrErr && szMsg && szMsg[0] ) { - int lenStrErr = strlen( pStrErr ); - int lenMsg = strlen( szMsg ); - char *p = strstr( pStrErr, szMsg ); - if ( p && (p==pStrErr || *(p-1) == ' ' && (*(p-2) == ';' || *(p-2) == ':' )) && - (p+lenMsg == pStrErr+lenStrErr || - p[lenMsg] == ';' && p[lenMsg+1] == ' ' || - p[lenMsg-1]==':' && p[lenMsg]==' ') ) { - return 1; /* reject duplicates */ - } - if ( lenStrErr + lenMsg + 2*(lenStrErr > 0) < STR_ERR_LEN ) { - /* enough room to add */ - if (lenStrErr > 0) { - if ( pStrErr[lenStrErr-1] != ':' ) { - strcat( pStrErr, ";" ); - } - strcat( pStrErr, " " ); - } - strcat( pStrErr, szMsg ); - return 1; - } - /* no room */ - if ( strstr( pStrErr, "..." ) ) { - return 0; /* no room mark has already been set */ - } - if ( lenStrErr + 3 < STR_ERR_LEN ) { - strcat( pStrErr, "..." ); - } - } - return 0; -} -/*************** static **********************************************************/ -int mol_copy_check_empty( char* dest, char* source, int len, char **first_space ) -{ - int i, c; /* required len >= 0; dest must have at least len+1 bytes */ - if ( len > 0 ) - strncpy( dest, source, len ); - dest[len]='\0'; - len = ( len > 0 )? (int)strlen( dest) : 0; - for ( i = (len-1); i >= 0 && 0 != (c = source[i]) && isspace(UCINT c); i-- ) - ; - *first_space = dest + (i+1); /* first blank or zero terminating byte in dest */ - return len; /* number of actually processed bytes; zero termination not included */ -} -/************* static ************************************************************/ -int mol_read_datum(void* data, int field_len, int data_type, char** line_ptr) -{ -/* 1. 'field_len' for MOL_STRING_DATA does not include trailing zero, - * that is actual length of the string pointed by 'data' - * should be at least field_len+1 bytes. - * For numerical data 'field_len' is length of input data field - * For numerical integral data field_len <= 0 means read up to first - * non-numeric character as strtod() does ("free format") - * 2. return value: for MOL_STRING_DATA: number of bytes excluding trailing zero - * for all others: 1=success; 0 = empty; -1= error - * 3. on exit *line_ptr points to the next byte after the last entered - */ - char *p = *line_ptr, *q, *p_end; - int i, ret=1, c, len; - long ldata; - double ddata; - - switch( data_type ) { - case MOL_STRING_DATA: - for ( i= 0; i < field_len && 0 != (c = p[i]) && isspace(UCINT c); i++ ) /* pass by all leading spaces */ - ; - len = mol_copy_check_empty( (char*)data, &p[i], field_len-i, &q ); - ret = ( q - (char*)data );/* actual data length */ - *q = '\0'; /* add zero termination to data if it is not there yet*/ - *line_ptr += (len+i); /* ptr to the 1st byte of the next input field or to zero termination */ - break; - - case MOL_CHAR_INT_DATA: - case MOL_SHORT_INT_DATA: - case MOL_LONG_INT_DATA: - { /* block start */ - char str[MOL_MAX_VALUE_LEN+1]; - ldata = 0L; - if ( field_len > MOL_MAX_VALUE_LEN ) { - ret = -1; - }else - if ( field_len > 0 ) { /* fixed length */ - *line_ptr += ( len = mol_copy_check_empty( str, p, field_len, &q ) ); - *q = '\0'; - if ( !len || !(q-str) ) { /* empty string */ - ret = 0; - }else - if ( (ldata=strtol(str,&p_end,10), p_end != q) ){ /* wrong data: incompletely interpreted */ - ret = -1; - } - }else{ /* free format: field_len <= 0 */ - ldata = strtol( p, &p_end, 10 ); - *line_ptr += ( len = p_end - p ); - if ( len == 0 ){ - ret = 0; - } - } - - switch( data_type ) { - case MOL_CHAR_INT_DATA: - if ( SCHAR_MIN <= ldata && ldata <= SCHAR_MAX ){ /* from || to &&: 11-19-96 */ - *(S_CHAR*)data = (S_CHAR)ldata; - }else{ - *(S_CHAR*)data = (S_CHAR)0; - ret = -1; - } - break; - case MOL_SHORT_INT_DATA: - if ( SHRT_MIN <= ldata && ldata <= SHRT_MAX ){ - *(S_SHORT*)data = (S_SHORT)ldata; - }else{ - *(S_SHORT*)data = (S_SHORT)0; - ret = -1; - } - break; - case MOL_LONG_INT_DATA: - if ( LONG_MIN < ldata && ldata < LONG_MAX ){ - *(long*)data = (long)ldata; - }else{ - *(long*)data = 0L; - ret = -1; - } - break; - default: - ret=-1; - } - - } /* block end */ - break; - case MOL_DOUBLE_DATA: - case MOL_FLOAT_DATA: - { /* block start */ - char str[MOL_MAX_VALUE_LEN+1]; - if ( field_len > MOL_MAX_VALUE_LEN ) { - ret = -1; - ddata = 0.0; - }else - if ( field_len > 0 ) { - *line_ptr += (len = mol_copy_check_empty( str, p, field_len, &q )); - *q = '\0'; - if ( !len || !(q-str) ) { /* empty string */ - ddata = 0.0; - ret = 0; - }else - if ( (ddata=strtod(str,&p_end), p_end != q) ){ /* wrong data */ - ret = -1; - } - }else{ /* free format */ - ddata = strtod( p, &p_end ); - *line_ptr += ( len = p_end - p ); - if ( len == 0 ){ - ret = 0; - } - } - switch(data_type){ - case MOL_DOUBLE_DATA: - if ( ddata != HUGE_VAL && /*ldata*/ ddata != -HUGE_VAL ){ /* replaced ldata with ddata 6-30-98 DCh */ - *(double*)data = ddata; - }else{ - *(double*)data = 0.0; - ret = -1; - } - break; - case MOL_FLOAT_DATA: - if ( fabs(ddata) <= (double)FLT_MIN ) { - *(float*)data = 0.0; - }else - if ( fabs(ddata) >= (double)FLT_MAX ) { - *(float*)data = 0.0; - ret = -1; - }else{ - *(float*)data = (float)ddata; - } - break; - } - } /* block end */ - break; - case MOL_JUMP_TO_RIGHT: - for ( i = 0; i < field_len && p[i]; i++ ) - ; - *line_ptr += i; - ret = i; - break; - default: - ret = -1; - } - return ret; -} -/************* static ************************************************************/ -int mol_read_hdr(MOL_HEADER_BLOCK *hdr, FILE* inp, char *pStrErr) -{ - /* All input lines can have are up 80 characters */ - /* Header Block */ - char line[MOLFILEINPLINELEN]; /* + cr +lf +zero termination + reserve */ - int err = 0, len; - const int line_len = sizeof(line); - char *p; - - /* memset( &hdr, 0, sizeof( MOL_HEADER_BLOCK ) ); */ - /*------------ header line #1: name ----------------*/ - if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ - err = 1; /* can't read the input file line */ - /* AddMOLfileError( pStrErr, "Can't read header block name line" ); */ - goto err_fin; - } - remove_one_lf( line ); - /* -- Disabled to relax strictness: allow > 80 chars names. - if ( line[MOLFILEMAXLINELEN] ){ - err = 2; // too long line - goto err_fin; - } - */ - len = mol_read_datum( hdr->szMoleculeName, sizeof(hdr->szMoleculeName)-1, MOL_STRING_DATA, &p ); - /*----------- header line #2 -----------------------*/ - if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ - err = 3; /* can't read the input file line */ - /* AddMOLfileError( pStrErr, "Can't read header block line 2" ); */ - goto err_fin; - } - remove_one_lf( line ); - /* -- Disabled to relax strictness: allow > 80 chars names. - if ( line[MOLFILEMAXLINELEN] ){ - err = 4; // too long input file line - goto err_fin; - } - */ - len = mol_read_datum( hdr->szUserInitials, sizeof(hdr->szUserInitials)-1, MOL_STRING_DATA, &p ); - len = mol_read_datum( hdr->szProgramName, sizeof(hdr->szProgramName)-1, MOL_STRING_DATA, &p ); - - /*------------ Relax strictness -----------------------*/ - len = mol_read_datum( &hdr->cMonth, 2, MOL_CHAR_INT_DATA, &p ); - len = mol_read_datum( &hdr->cDay, 2, MOL_CHAR_INT_DATA, &p ); - len = mol_read_datum( &hdr->cYear, 2, MOL_CHAR_INT_DATA, &p ); - len = mol_read_datum( &hdr->cHour, 2, MOL_CHAR_INT_DATA, &p ); - len = mol_read_datum( &hdr->cMinute, 2, MOL_CHAR_INT_DATA, &p ); - len = mol_read_datum( hdr->szDimCode, sizeof(hdr->szDimCode)-1, MOL_STRING_DATA, &p ); - len = mol_read_datum( &hdr->nScalingFactor1, 2, MOL_SHORT_INT_DATA, &p ); - len = mol_read_datum( &hdr->dScalingFactor2, 10, MOL_DOUBLE_DATA, &p ); - len = mol_read_datum( &hdr->dEnergy, 12, MOL_DOUBLE_DATA, &p ); - len = mol_read_datum( &hdr->lInternalRegistryNumber, 6, MOL_LONG_INT_DATA, &p ); - - /* save the whole line 2 */ - p = line; - len = mol_read_datum( hdr->szMoleculeLine2, sizeof(hdr->szMoleculeLine2)-1, MOL_STRING_DATA, &p ); - - - /*------------ header line #3: comment ----------------*/ - if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ - err = 7; /* can't read the line */ - /* AddMOLfileError( pStrErr, "Can't read header block comment line" ); */ - goto err_fin; - } - remove_one_lf( line ); - /* -- Disabled to relax strictness: allow > 80 chars comments. - if ( line[MOLFILEMAXLINELEN] ){ - err = 8; // too long line - goto err_fin; - } - */ - len = mol_read_datum( hdr->szComment, sizeof(hdr->szComment)-1, MOL_STRING_DATA, &p ); - -err_fin: - - return err; -} -/********** static *****************************************************/ -int RemoveNonPrintable( char *line ) -{ - int i, c, num = 0; - if ( line ) { - for ( i = 0; c = UCINT line[i]; i ++ ) { - /* assuming ASCII charset */ - if ( c < ' ' || c >= 0x7F ) { - line[i] = '.'; - num ++; - } - } - } - return num; -} -/************** static *************************************************/ -int mol_read_counts_line( MOL_CTAB* ctab, FILE *inp, char *pStrErr ) -{ - char *p; - char line[MOLFILEINPLINELEN]; - const int line_len = sizeof(line); - int err = 0, len; - - if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ - MOLFILE_ERR_FIN (err, 1, err_fin, "Cannot read counts line"); - /* can't read the input file line */ - } - remove_one_lf( line ); - if ( line[MOLFILEMAXLINELEN] ){ - MOLFILE_ERR_SET (err, 0, "Too long counts line"); /* too long input file line */ - } - if ( 0 > mol_read_datum( &ctab->nNumberOfAtoms, 3, MOL_SHORT_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->nNumberOfBonds, 3, MOL_SHORT_INT_DATA, &p ) -#if ( MOL_QUERY == MOL_PRESENT ) - || 0 > mol_read_datum( &ctab->nNumberOfAtomsLists, 3, MOL_SHORT_INT_DATA, &p ) -#else - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) -#endif - || 0 > mol_read_datum( NULL, /*obsolete*/ 3, MOL_JUMP_TO_RIGHT, &p ) - || 0 > mol_read_datum( &ctab->cChiralFlag, 3, MOL_CHAR_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->nNumberOfStextEntries, 3, MOL_SHORT_INT_DATA, &p ) -#if ( MOL_CPSS == MOL_PRESENT ) - || 0 > mol_read_datum( &ctab->nNumberOfReactionComponentsPlus1, 3, MOL_SHORT_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->nNumberOfReactants, 3, MOL_SHORT_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->nNumberOfProducts, 3, MOL_SHORT_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->nNumberOfIntermediates, 3, MOL_SHORT_INT_DATA, &p ) -#else - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) -#endif - || 0 > mol_read_datum( &ctab->nNumberOfPropertyLines, 3, MOL_SHORT_INT_DATA, &p ) ){ - err = 3; /* can't interpret counts line */ - MOLFILE_ERR_SET (err, 3, "Cannot interpret counts line:"); /* too long input file line */ - RemoveNonPrintable( line ); - AddMOLfileError(pStrErr, line); - goto err_fin; - } - len = mol_read_datum( ctab->csCurrentCtabVersion, sizeof(ctab->csCurrentCtabVersion)-1, MOL_STRING_DATA, &p ); -err_fin: - return err; -} - -/************ static *************************************************************/ -int read_atom_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ) -{ - char *p; - char line[MOLFILEINPLINELEN]; - const int line_len = sizeof(line); - S_SHORT i, chg; - static S_SHORT charge_val[] = {0, 3, 2, 1, 'R', -1, -2, -3}; - /* 0 1 2 3 4 5 6 7 */ - /* - if ( NULL == ctab->MolAtom ){ - err = 1; - goto err_fin; // internal error: MolAtom structure has not been allocated - } - */ - - for ( i = 0; i < ctab->nNumberOfAtoms; i++ ) { - - if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ - if ( !err ) { - MOLFILE_ERR_SET (err, 2, "Cannot read atom block line"); - } - break; - } - remove_one_lf( line ); - if ( line[MOLFILEMAXLINELEN] ){ - MOLFILE_ERR_SET (err, 0, "Too long atom block line"); - } - if ( err ) { - if ( !strcmp( line, SDF_END_OF_DATA ) ) { - err = -abs(err); - break; - } - continue; /* bypass the rest of the Atom block */ - } - if ( NULL != ctab->szCoord ) { - mystrncpy( ctab->szCoord[i], p, 31 ); /* original coordinates */ - } - - if ( NULL != ctab->MolAtom ) { - if ( 0 > mol_read_datum( &ctab->MolAtom[i].fX, 10, MOL_DOUBLE_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolAtom[i].fY, 10, MOL_DOUBLE_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolAtom[i].fZ, 10, MOL_DOUBLE_DATA, &p ) - || 0 > mol_read_datum( NULL, /* undescribed in article*/ 1, MOL_JUMP_TO_RIGHT, &p ) - || 0 == mol_read_datum( &ctab->MolAtom[i].szAtomSymbol, 3, MOL_STRING_DATA, &p ) /* was sizeof(ctab->MolAtom[0].szAtomSymbol)-1 */ -#ifdef TARGET_EXE_USING_API - || 0 > mol_read_datum( &ctab->MolAtom[i].cMassDifference, 2, MOL_SHORT_INT_DATA, &p ) -#else - || 0 > mol_read_datum( &ctab->MolAtom[i].cMassDifference, 2, MOL_CHAR_INT_DATA, &p ) -#endif - || 0 > mol_read_datum( &ctab->MolAtom[i].cCharge, 3, MOL_CHAR_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolAtom[i].cStereoParity, 3, MOL_CHAR_INT_DATA, &p ) -#if ( MOL_QUERY == MOL_PRESENT ) - || 0 > mol_read_datum( &ctab->MolAtom[i].cH_countPlus1, 3, MOL_CHAR_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolAtom[i].cStereoCare, 3, MOL_CHAR_INT_DATA, &p ) -#else - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) -#endif - || 0 > mol_read_datum( &ctab->MolAtom[i].cValence, 3, MOL_CHAR_INT_DATA, &p ) ) { - - err = 4; - MOLFILE_ERR_SET (err, 4, "Cannot interpret atom block line:"); - RemoveNonPrintable( line ); - AddMOLfileError(pStrErr, line); - if ( !strcmp( line, SDF_END_OF_DATA ) ) { - err = -abs(err); - break; - } - continue; /* can't interpret a first half of atom block line */ - } - if ( 2 == strlen(ctab->MolAtom[i].szAtomSymbol) && isupper(UCINT ctab->MolAtom[i].szAtomSymbol[1])) - ctab->MolAtom[i].szAtomSymbol[1] = (char)tolower(UCINT ctab->MolAtom[i].szAtomSymbol[1]); /* 5-4-99 DCh*/ - - if ( (chg = (S_SHORT) ctab->MolAtom[i].cCharge)< 0 || chg >= (int)(sizeof ( charge_val ) / sizeof( charge_val[0] )) ) { - /* ctab->MolAtom[i].cCharge = 0; */ /* error; ignore for now */ - ctab->MolAtom[i].cCharge = (S_CHAR)(4 - chg); /* allow greater charges to accommodate NCI structures. 8-20-2002 */ - ctab->MolAtom[i].cRadical = 0; - }else - if ( 'R' == (chg = charge_val[chg]) ){ - ctab->MolAtom[i].cCharge = 0; - ctab->MolAtom[i].cRadical = RADICAL_DOUBLET; - }else{ - ctab->MolAtom[i].cCharge = (S_CHAR)chg; /* actual charge value */ - ctab->MolAtom[i].cRadical = 0; - } -#ifdef TARGET_EXE_USING_API - if ( ctab->MolAtom[i].cMassDifference ) { /* e_ReadMOL.c specific */ - ctab->MolAtom[i].cMassDifference += ISOTOPIC_SHIFT_FLAG; - } -#endif - - if ( -#if ( MOL_CPSS == MOL_PRESENT ) - 0 > mol_read_datum( &ctab->MolAtom[i].cH0_designator, 3, MOL_CHAR_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolAtom[i].cReactionComponentType, 3, MOL_CHAR_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolAtom[i].cReactionComponentNumber, 3, MOL_CHAR_INT_DATA, &p ) -#else - 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) -#endif -#if ( MOL_REACT == MOL_PRESENT ) - || 0 > mol_read_datum( &ctab->MolAtom[i].nAtomAtomMappingNumber, 3, MOL_SHORT_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolAtom[i].cReactionComponentType, 3, MOL_CHAR_INT_DATA, &p ) -#else - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) -#endif -#if ( MOL_REACT == MOL_PRESENT || MOL_QUERY == MOL_PRESENT ) - || 0 > mol_read_datum( &ctab->MolAtom[i].cExactChargeFlag, 3, MOL_CHAR_INT_DATA, &p ) -#else - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) -#endif - ){ - err = 5; /* can't interpret a second half of atom block line */ - MOLFILE_ERR_SET (err, 5, "Cannot interpret atom block line:"); - RemoveNonPrintable( line ); - AddMOLfileError(pStrErr, line); - if ( !strcmp( line, SDF_END_OF_DATA ) ) { - err = -abs(err); - break; - } - continue; - } - } - } -/* err_fin: */ - return err; -} -/************ static *************************************************************/ -int read_bonds_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ) -{ - char *p; - char line[MOLFILEINPLINELEN]; - const int line_len = sizeof(line); - S_SHORT i; - /* - if ( NULL == ctab->MolBond ){ - err = 1; - goto err_fin; // internal error: memory has not been allocated for MolBond structure - } - */ - for ( i = 0; i < ctab->nNumberOfBonds; i++ ) { - - if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ - if ( !err ) { - MOLFILE_ERR_SET (err, 2, "Cannot read bond block line"); - } - break; - } - remove_one_lf( line ); - if ( line[MOLFILEMAXLINELEN] ){ - err = err? err : 3; /* too long input file line */ - } - if ( err ) { - if ( !strcmp( line, SDF_END_OF_DATA ) ) { - err = -abs(err); - break; - } - continue; - } - - if ( ctab->MolBond ) { - if ( 0 > mol_read_datum( &ctab->MolBond[i].nAtomNo1, 3, MOL_SHORT_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolBond[i].nAtomNo2, 3, MOL_SHORT_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolBond[i].cBondType, 3, MOL_CHAR_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolBond[i].cBondStereo, 3, MOL_CHAR_INT_DATA, &p ) -#if ( MOL_QUERY == MOL_PRESENT ) - || 0 > mol_read_datum( &ctab->MolBond[i].cBondTopology, 3, MOL_CHAR_INT_DATA, &p ) /* ring/chain */ -#else - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) -#endif -#if ( MOL_REACT == MOL_PRESENT ) - || 0 > mol_read_datum( &ctab->MolBond[i].cReactingCenterStatus, 3, MOL_CHAR_INT_DATA, &p ) -#else - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) -#endif - ){ - if ( !err ) { - /* can't interpret bonds block line */ - MOLFILE_ERR_SET (err, 4, "Cannot interpret bond block line:"); - RemoveNonPrintable( line ); - AddMOLfileError(pStrErr, line); - } - if ( !strcmp( line, SDF_END_OF_DATA ) ) { - err = -abs(err); - break; - } - } - } - } - /* err_fin: */ - return err; -} -/********** static ***************************************************************/ -int read_stext_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ) -{ - /* just pass by all stext enties without attemp to interpret */ - char *p; - char line[MOLFILEINPLINELEN]; - const int line_len = sizeof(line); - S_SHORT i; - - for ( i = 0; i < 2*ctab->nNumberOfStextEntries; i++ ) { - - if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ - if ( !err ) { - MOLFILE_ERR_FIN (err, 2, err_fin, "Cannot read STEXT block line"); - } - break; - /* can't read the input file line */ - } - /* - remove_one_lf( line ); - if ( line[MOLFILEMAXLINELEN] ){ - MOLFILE_ERR_SET (err, 2, "Warning: Too long STEXT block line"); - // too long input file line - } - */ - } -err_fin: - return err; -} -/************ static *************************************************************/ -int read_properties_block( MOL_CTAB* ctab, MOL_HEADER_BLOCK *pHdr, FILE *inp, int err, char *pStrErr ) -{ - enum { MULTI_LINE_MODE_NO_MODE, MULTI_LINE_MODE_ISIS_ALIAS }; - char *p; - char line[MOLFILEINPLINELEN]; - const int line_len = sizeof(line); - int nMultiLineMode = MULTI_LINE_MODE_NO_MODE, nAtomNumber=0; - S_SHORT i, j; - char charM[2]; - char szBlank[3]; - char szType[4]; - S_SHORT skip_lines=0; - S_SHORT num_entries; - S_SHORT num_atoms = ctab->nNumberOfAtoms; - - int charge_encountered = 0; - int radical_encountered = 0; - int isotope_encountered = 0; - /* - if ( NULL == ctab->MolAtom ){ - err = 1; - goto err_fin; internal error: memory has not been allocated for MolAtom structure - } - */ - for ( i = 0; ctab->csCurrentCtabVersion[0]? 1 : (i < ctab->nNumberOfPropertyLines); i++ ) { /* the last line should be M END */ - /* ctab->csCurrentCtabVersion[0] == 0: - exactly ctab->nNumberOfPropertyLines lines including M END */ - /* ctab->csCurrentCtabVersion[0] != 0: - read until M END line was encountered */ - if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ - if ( !err ) { - MOLFILE_ERR_SET (err, 2, "Cannot read properties block line"); - } - goto err_fin; - } - remove_one_lf( line ); - if ( line[MOLFILEMAXLINELEN] ){ - MOLFILE_ERR_SET (err, 3, "Too long properties block line"); - continue; - } - if ( skip_lines > 0 ) { - skip_lines --; - continue; - } - /* alias. */ - if ( nMultiLineMode == MULTI_LINE_MODE_ISIS_ALIAS && nAtomNumber ) { - int len; - nMultiLineMode = MULTI_LINE_MODE_NO_MODE; - if ( 0 >= (len=normalize_name( p )) ) { - nAtomNumber = 0; - continue; - } - if( 0 < len && len < (int)(sizeof(ctab->MolAtom->szAtomSymbol)) ) { - int nCharge, nRad; - MOL_ATOM* MolAtom = ctab->MolAtom + nAtomNumber-1; - /* ctab->MolAtom[nAtomNumber-1].cAtomAliasedFlag = 1; */ - /* extract radicals & charges */ - extract_ChargeRadical( p, &nRad, &nCharge ); - /* Aliased atom cannot have charge, radical & mass difference */ - /* in the atom table or "M CHG", "M RAD", "M ISO" */ - /* if ( nCharge ) */ - MolAtom->cCharge = (S_CHAR)nCharge; - /* if ( nRad ) */ - MolAtom->cRadical = (char)nRad; - - if ( 1 == len && 'D' == p[0] ) { - /* H isotope */ - p[0] = 'H'; -#ifdef TARGET_EXE_USING_API - MolAtom->cMassDifference=(1 + ISOTOPIC_SHIFT_FLAG); -#else - MolAtom->cMassDifference=1; -#endif - } else - if ( 1 == len && 'T' == p[0] ) { - /* H isotope */ - p[0] = 'H'; -#ifdef TARGET_EXE_USING_API - MolAtom->cMassDifference=(2 + ISOTOPIC_SHIFT_FLAG); -#else - MolAtom->cMassDifference=2; -#endif - } else - MolAtom->cMassDifference=0; - if ( strlen(p) < sizeof(ctab->MolAtom[0].szAtomSymbol) ) { - strcpy(MolAtom->szAtomSymbol, p); - } else { - strcpy(MolAtom->szAtomSymbol, "???"); - } - MolAtom->cAtomAliasedFlag ++; - } - skip_lines = 0; - nAtomNumber = 0; - continue; - } - - if ( 1 != mol_read_datum( charM, sizeof(charM) - 1, MOL_STRING_DATA, &p ) - || 0 != mol_read_datum( szBlank, sizeof(szBlank) - 1, MOL_STRING_DATA, &p ) /* must contain 0 bytes */ - || 0 >= mol_read_datum( szType, sizeof(szType) - 1, MOL_STRING_DATA, &p ) /* must contain 3 bytes */ - ) { - if ( !strcmp( line, SDF_END_OF_DATA ) ) { - err = err? -abs(err): -4; - break; - } - continue; /* ignore because cannot recognize */ - } - if ( charM[0] == 'V' ){ - skip_lines = 0; /* ISIS/Desktop Atom Value: one-line property */ - continue; - } - if ( charM[0] == 'G' ){ - skip_lines = 1; /* ISIS/Desktop Group abbreviation: two-line property */ - continue; - } - if ( charM[0] == 'A' ) { - if ( NULL != ctab->MolAtom && - 0 < ( nAtomNumber = (int)strtol(szType, NULL, 10) ) && - nAtomNumber <= ctab->nNumberOfAtoms ){ - /* Atom Alias [ISIS/Desktop] two-line property */ - nMultiLineMode = MULTI_LINE_MODE_ISIS_ALIAS; - continue; - } else { - nAtomNumber = 0; - skip_lines = 1; - continue; - } - } - if ( charM[0] == 'S' && !strcmp( szType, "SKP" ) ){ /* skip lines */ - if ( 0 >= mol_read_datum( &skip_lines, 3, MOL_SHORT_INT_DATA, &p ) ) { - skip_lines = 0; - } - continue; - } - if ( charM[0] != 'M' ) {/* cannot recognize a line */ - continue; - } - if ( !strcmp( szType, "REG" ) ) { - int len; - p = p + strspn( p, " " ); - len = strcspn( p, " " ); - len = inchi_min( len, MOL_MAX_VALUE_LEN ); - mol_read_datum( &pHdr->lInternalRegistryNumber, len, MOL_LONG_INT_DATA, &p ); - continue; - } - - if ( !strcmp( szType, "END" ) ){ - if ( ctab->csCurrentCtabVersion[0] ) - break; /* end of property lines */ - continue; - } - - if ( NULL == ctab->MolAtom ) - continue; /* ignore because the user requested to bypass all this stuff */ - - /*----------------------------------- charge: Generic */ - if ( !strcmp( szType, "CHG" ) && - 0 < mol_read_datum( &num_entries, 3, MOL_SHORT_INT_DATA, &p ) && - 1 <= num_entries && num_entries <= 8 ) { - S_SHORT atoms[8]; - S_SHORT charges[8]; - if ( !charge_encountered && !radical_encountered ) { - /* first charge or radical record clears all Atom Block */ - /* entered charge and radical data to zeroes */ - charge_encountered = -1; - } - for ( j = 0; j < num_entries; j++ ) { - if ( 0 > mol_read_datum( &atoms[j], 0, MOL_SHORT_INT_DATA, &p ) || - 0 > mol_read_datum( &charges[j], 0, MOL_SHORT_INT_DATA, &p ) || - atoms[j] <= 0 || atoms[j] > num_atoms || - charges[j] < -15 || charges[j] > 15 ) { - goto charge_error; - } - } - if ( charge_encountered == -1 ) { - for ( j = 0; j < num_atoms; j++ ) { - if ( !ctab->MolAtom[j].cAtomAliasedFlag ) /* do not clear aliased atoms.*/ - ctab->MolAtom[j].cCharge = ctab->MolAtom[j].cRadical = '\0'; - } - charge_encountered = 1; - } - for ( j = 0; j < num_entries; j++ ) { - if ( !ctab->MolAtom[atoms[j]-1].cAtomAliasedFlag ) /* do not change aliased atoms.*/ - ctab->MolAtom[atoms[j]-1].cCharge = (S_CHAR)charges[j]; - } - continue; - charge_error: - MOLFILE_ERR_SET (err, 0, "Charge not recognized:"); - RemoveNonPrintable( line ); - AddMOLfileError(pStrErr, line); - continue; /* ignore for now */ - } - /*-------------------------------------- radical: Generic */ - if ( !strcmp( szType, "RAD" ) && - 0 < mol_read_datum( &num_entries, 3, MOL_SHORT_INT_DATA, &p ) && - 1 <= num_entries && num_entries <= 8 ) { - S_SHORT atoms[8]; - S_SHORT radicals[8]; - if ( !charge_encountered && !radical_encountered ) { - /* first charge or radical record clears all Atom Block */ - /* entered charge and radical data to zeroes */ - radical_encountered = -1; - } - for ( j = 0; j < num_entries; j++ ) { - if ( 0 > mol_read_datum( &atoms[j], 0, MOL_SHORT_INT_DATA, &p ) || - 0 > mol_read_datum( &radicals[j], 0, MOL_SHORT_INT_DATA, &p ) || - atoms[j] <= 0 || atoms[j] > num_atoms || - radicals[j] < 0 || radicals[j] > 3 ) { - goto radical_error; - } - } - if ( radical_encountered == -1 ) { - for ( j = 0; j < num_atoms; j++ ) { - if ( !ctab->MolAtom[j].cAtomAliasedFlag ) /* do not clear aliased atoms. 5-3-99 DCh */ - ctab->MolAtom[j].cCharge = ctab->MolAtom[j].cRadical = '\0'; - } - radical_encountered = 1; - } - for ( j = 0; j < num_entries; j++ ) { - if ( !ctab->MolAtom[atoms[j]-1].cAtomAliasedFlag ) { /* do not change aliased atoms. 5-3-99 DCh */ - ctab->MolAtom[atoms[j]-1].cRadical = (S_CHAR)radicals[j]; - } - } - continue; - radical_error: - MOLFILE_ERR_SET (err, 0, "Radical not recognized:"); - RemoveNonPrintable( line ); - AddMOLfileError(pStrErr, line); - continue; /* ignore error for now */ - } - /*-------------------------------------- isotope: Generic */ - if ( !strcmp( szType, "ISO" ) && - 0 < mol_read_datum( &num_entries, 3, MOL_SHORT_INT_DATA, &p ) && - 1 <= num_entries && num_entries <= 8 ) { - S_SHORT atoms[8]; - S_SHORT iso_mass[8]; /* contains istotope mass number, not difference. 7-14-00 DCh. */ - if ( !isotope_encountered ) { - /* first charge or radical record clears all Atom Block */ - /* entered charge and radical data to zeroes */ - isotope_encountered = -1; - } - for ( j = 0; j < num_entries; j++ ) { - if ( 0 > mol_read_datum( &atoms[j], 0, MOL_SHORT_INT_DATA, &p ) || - 0 > mol_read_datum( &iso_mass[j], 0, MOL_SHORT_INT_DATA, &p ) || - atoms[j] <= 0 || atoms[j] > num_atoms - /*|| iso_mass[j] < -18 || iso_mass[j] > 12*/ ) { - /* goto isotope_error; */ - atoms[j] = -1; /* flag error */ - MOLFILE_ERR_SET (err, 0, "Isotopic data not recognized:"); - RemoveNonPrintable( line ); - AddMOLfileError(pStrErr, line); - continue; /* ignore isotopic error for now */ - } - } - if ( isotope_encountered == -1 ) { - for ( j = 0; j < num_atoms; j++ ) { - /*if ( !ctab->MolAtom[j].cAtomAliasedFlag )*/ /* clear even aliased atoms */ - ctab->MolAtom[j].cMassDifference = 0; - } - isotope_encountered = 1; - } - for ( j = 0; j < num_entries; j++ ) { - if ( atoms[j] <= 0 ) - continue; /* ignore isotopic error for now */ - if ( 1 /* !ctab->MolAtom[atoms[j]-1].cAtomAliasedFlag */) { - char *at = ctab->MolAtom[atoms[j]-1].szAtomSymbol; - if ( at[1] || at[0] != 'D' && at[0] != 'T' ) { /* D & T cannot have ISO */ - /* need atomic weight to calculate isotope difference. 7-14-00 DCh. */ -#ifdef TARGET_EXE_USING_API - /*^^^ Check added 5-10-2008 - IPl */ - if (iso_mass[j] > 0) - /* According to MDL specification, p.12, only a positive - integer is allowed. And yes, there appeared some MOL/SD - files contaning here a negative value. This manifested - in mismatch in InChI_MAIN vs. cInChI-1/stdinchi-1 results. - */ - ctab->MolAtom[atoms[j]-1].cMassDifference = iso_mass[j]; /* mass, not difference */ - -#else - int atw, atw_diff; - /*^^^ - NB: According to MDL specification, difference should be in - [-18; +12] range, not in [-19; +19] as is checked below. */ - if ( (atw = get_atw( at )) && abs( atw_diff = (int)iso_mass[j] - atw ) < 20 ) { - ctab->MolAtom[atoms[j]-1].cMassDifference = (char)(atw_diff? atw_diff : ZERO_ATW_DIFF); - } -#endif - } - } - } - continue; - } - } -err_fin: - return err; -} -/************ global *************************************************************/ -MOL_DATA* delete_mol_data( MOL_DATA* mol_data ) -{ - if ( mol_data ) { - if ( mol_data->ctab.MolAtom ) - inchi_free( mol_data->ctab.MolAtom ); - if ( mol_data->ctab.MolBond ) - inchi_free( mol_data->ctab.MolBond ); - if ( mol_data->ctab.szCoord ) - inchi_free( mol_data->ctab.szCoord ); - inchi_free( mol_data ); - mol_data = NULL; - } - return mol_data; -} -/************* global ************************************************************/ -/* Comletely ingnore STEXT block, queries, and 3D features - */ -MOL_DATA* read_mol_file( FILE* inp, MOL_HEADER_BLOCK *OnlyHeaderBlock, MOL_CTAB *OnlyCtab, - int bGetOrigCoord, int *err, char *pStrErr ) -{ - MOL_DATA* mol_data = NULL; - int ret = 0, prev_ret, bEndOfData = 0; - int bReadAll = ( OnlyHeaderBlock == NULL ); - MOL_CTAB ctab, *pCtab = NULL; - MOL_HEADER_BLOCK *pHdr = NULL; - - *err = 0; - if ( bReadAll ) { - if ( NULL == ( mol_data = ( MOL_DATA* )inchi_calloc( 1, sizeof(MOL_DATA) ) ) ){ - ret = 1; /* can't allocate mol_data structure */ - AddMOLfileError( pStrErr, "Out of RAM" ); - goto err_fin; - } - pHdr = &mol_data->hdr; - pCtab = &mol_data->ctab; - } else { - pHdr = OnlyHeaderBlock; - pCtab = OnlyCtab? OnlyCtab : &ctab; - memset( pHdr, 0, sizeof( MOL_HEADER_BLOCK ) ); - memset( pCtab, 0, sizeof( MOL_CTAB ) ); - } - pCtab->MolBond = NULL; - pCtab->MolAtom = NULL; - pCtab->szCoord = NULL; - - if ( 0 != ( ret = mol_read_hdr(pHdr, inp, pStrErr) ) ){ - ret += 10; - goto err_fin; /* most probably end of file */ - } - if ( 0 != ( ret = mol_read_counts_line( pCtab , inp, pStrErr) ) ){ - ret += 20; - goto err_fin; - } - - if ( bReadAll ) { - if ( NULL == ( mol_data->ctab.MolAtom = (MOL_ATOM*)inchi_calloc(inchi_max(mol_data->ctab.nNumberOfAtoms,1), sizeof(MOL_ATOM)) ) ){ - ret = 2; /* can't allocate MolAtom structure */ - MOLFILE_ERR_FIN (ret, 2, err_fin, "Out of RAM"); - } - if ( bGetOrigCoord && - NULL == ( mol_data->ctab.szCoord = (MOL_COORD*)inchi_calloc(inchi_max(mol_data->ctab.nNumberOfAtoms,1), sizeof(MOL_COORD)) ) ){ - ret = 2; /* can't allocate MolAtom structure */ - MOLFILE_ERR_FIN (ret, 2, err_fin, "Out of RAM"); - } - } - if ( 0 != ( ret = read_atom_block(pCtab, inp, ret, pStrErr) ) ){ - if ( ret < 0 ) { - ret = -ret; - bEndOfData = 1; - } - ret += 30; - /* goto err_fin; */ - } - - if ( bReadAll && ret < 30 ) { - if ( !bEndOfData && NULL == ( mol_data->ctab.MolBond = (MOL_BONDS*)inchi_calloc(inchi_max(mol_data->ctab.nNumberOfBonds,1), sizeof(MOL_BONDS)) ) ){ - ret = 3; /* can't allocate MolBond structure */ - MOLFILE_ERR_FIN (ret, 3, err_fin, "Out of RAM"); - } - } - prev_ret = ret; - if ( !bEndOfData && 0 != ( ret = read_bonds_block(pCtab, inp, ret, pStrErr) ) ){ - if ( ret < 0 ) { - ret = -ret; - bEndOfData = 1; - } - ret = prev_ret? prev_ret : ret + 40; - } - prev_ret = ret; - if ( !bEndOfData && 0 != ( ret = read_stext_block(pCtab, inp, ret, pStrErr) ) ){ - ret = prev_ret? prev_ret : ret + 50; - } - prev_ret = ret; - if ( !bEndOfData && 0 != ( ret = read_properties_block(pCtab, pHdr, inp, ret, pStrErr) ) ){ - if ( ret < 0 ) { - ret = -ret; - bEndOfData = 1; - } - ret = prev_ret? prev_ret : ret + 60; - } - -err_fin: - *err = bEndOfData? -ret : ret; - if ( bReadAll ) { - if ( ret ) - mol_data = delete_mol_data( mol_data ); /* delete all results */ - return mol_data; - } else { - if ( ret ) - return NULL; - else - return (MOL_DATA*)OnlyHeaderBlock; - } -} - -/******************************************************************/ -static const char sdf_data_hdr_name[] = "NAME"; -static const char sdf_data_hdr_comm[] = "COMMENT"; -enum { SDF_START, SDF_DATA_HEADER, SDF_DATA_HEADER_NAME - , SDF_DATA_HEADER_COMMENT, SDF_DATA_HEADER_CAS - , SDF_DATA_HEADER_USER, SDF_DATA_LINE - , SDF_END_OF_DATA_ITEM, SDF_EMPTY_LINE, SDF_END_OF_DATA_BLOCK }; -/********** static ********************************************************/ -long extract_cas_rn( char *line ) -{ - int i, j; - i = line[0] == '-'? 1 : 0; - for ( j = i; line[i]; i ++ ) { - if ( isdigit( UCINT line[i] ) ) { - line[j++] = line[i]; - } else - if ( line[i] != '-' ) { - break; - } - } - line[j] = '\0'; - return strtol( line, NULL, 10 ); -} -/********** static ********************************************************/ -int identify_sdf_label( char* inp_line, const char *pSdfLabel ) -{ - char line[MOLFILEMAXLINELEN]; - char *p, *q; - int i, j, len; - if ( (p = strchr( inp_line, '<' )) && - (q = strchr( p, '>' )) && - (len = q-p-1) > 0 && len < (int)sizeof(line) ) { - memcpy( line, p+1, len ); - line[len] = '\0'; - for ( i = 0; isspace( UCINT line[i] ); i ++ ) - ; - for ( j = len-1; j >= i && isspace( UCINT line[i] ); j -- ) - ; - len = j-i+1; - p = line+i; - if ( pSdfLabel && pSdfLabel[0] && len == (int)strlen(pSdfLabel) && !memicmp( p, pSdfLabel, len ) ) - return SDF_DATA_HEADER_USER; - if ( len == sizeof(sdf_data_hdr_name)-1 && !memicmp( p, sdf_data_hdr_name, len ) ) - return SDF_DATA_HEADER_NAME; - if ( len == sizeof(sdf_data_hdr_comm)-1 && !memicmp( p, sdf_data_hdr_comm, len ) ) - return SDF_DATA_HEADER_COMMENT; - if ( !memicmp( p, "CAS", 3 ) ) - return SDF_DATA_HEADER_CAS; - } - return SDF_DATA_HEADER; -} -/************* global *****************************************************/ -int bypass_sdf_data_items( FILE* inp, long *cas_reg_no, char* comment, - int lcomment, char *name, int lname, int prev_err, - const char *pSdfLabel, char *pSdfValue, char *pStrErr ) -{ - char line[MOLFILEINPLINELEN]; - const int line_len = sizeof(line); - int err = 0; - int current_state = SDF_START; - int n_blank_lines = 0; - int n_lines = 0; - char* p = NULL; - int bNeedsName = name && lname > 0 && !name[0]; - int bNeedsComm = comment && lcomment > 0 && !comment[0]; - int bNeedsUser = pSdfLabel && pSdfLabel[0] && pSdfValue; - int bNeedsCASrn = 0; - int bCASrnIsUser = 0; - - if ( cas_reg_no != NULL ) { - bNeedsCASrn = 1; - *cas_reg_no = 0; - bCASrnIsUser = (bNeedsUser && !memicmp(pSdfLabel,"CAS", 3)); - } - - while ( err == 0 && - current_state !=SDF_END_OF_DATA_BLOCK && - NULL != ( p = inchi_fgetsLf( line, line_len, inp ) ) ) { - - if ( !n_lines && !memcmp(line, "M END", 6) ) { - continue; /* allow subtle errors */ - } - n_lines++; - - remove_trailing_spaces( line ); - if ( line[MOLFILEMAXLINELEN] ){ - if ( current_state != SDF_DATA_HEADER && - current_state != SDF_DATA_LINE && - current_state != SDF_DATA_HEADER_NAME && - current_state != SDF_DATA_HEADER_USER && - current_state != SDF_DATA_HEADER_COMMENT ) { - line[MOLFILEMAXLINELEN] = '\0'; - if ( !prev_err ) { - MOLFILE_ERR_SET (err, 0, "Too long SData line truncated"); - } - } else { - /* allow long lines in SDF data. 9-29-00 DCh */ - line[MOLFILEMAXLINELEN] = '\0'; - } - } - - n_blank_lines += ( *line == '\0' ); - - switch( current_state ) { - - case SDF_START: - case SDF_END_OF_DATA_ITEM: - case SDF_EMPTY_LINE: /* Added 9-25-97 DCh */ - - if ( 0 == strcmp( line, SDF_END_OF_DATA ) ) { - current_state = SDF_END_OF_DATA_BLOCK; - } - else - if ( '>' == *line ) { - current_state = ( bNeedsName || bNeedsComm || bNeedsCASrn || bNeedsUser )? identify_sdf_label(line, pSdfLabel) : SDF_DATA_HEADER; - }else - if ( *line == '\0' ) { /* Added 9-25-97 DCh */ - /* Relax the strictness: Allow more than 1 empty line. */ - current_state=SDF_EMPTY_LINE; - } else - if ( !prev_err ) { - MOLFILE_ERR_SET (err, 3, "Unexpected SData header line:"); - RemoveNonPrintable( line ); - AddMOLfileError(pStrErr, line); - /* unexpected contents of data header line */ - } else { - err = 3; - } - break; - - case SDF_DATA_HEADER_NAME: - if ( bNeedsName && 0 < normalize_name( line ) ) { - bNeedsName = 0; - mystrncpy( name, line, lname ); - } - goto got_data_line; - - case SDF_DATA_HEADER_COMMENT: - if ( bNeedsComm && 0 < normalize_name( line ) ) { - bNeedsComm = 0; - mystrncpy( comment, line, lcomment ); - } - goto got_data_line; - - case SDF_DATA_HEADER_USER: - if ( bNeedsUser && 0 < normalize_name( line ) ) { - bNeedsUser = 0; - mystrncpy( pSdfValue, line, MAX_SDF_VALUE+1 ); - if ( bCASrnIsUser && bNeedsCASrn ) { - *cas_reg_no = extract_cas_rn( line ); - bNeedsCASrn = (0 == *cas_reg_no); - } - } - goto got_data_line; - - case SDF_DATA_HEADER_CAS: - if ( bNeedsCASrn && 0 < normalize_name( line ) ) { - *cas_reg_no = extract_cas_rn( line ); - bNeedsCASrn = (0 == *cas_reg_no); - } - goto got_data_line; - - case SDF_DATA_HEADER: - case SDF_DATA_LINE: -got_data_line: - current_state = *line? SDF_DATA_LINE : SDF_END_OF_DATA_ITEM; - break; - - } - } - if ( 0 == err && SDF_END_OF_DATA_BLOCK != current_state && NULL == p ) - ; /* err = 4; */ /* unexpected end of file: missing $$$$ */ - else - if (err && ( n_blank_lines == n_lines && *line == '\0' ) ) - err = 5; /* empty lines -- do not know when this can happen */ - - if ( err && err != 5 && current_state != SDF_END_OF_DATA_BLOCK && p ) { - /* bypass up to $$$$ */ - while ( ( p = inchi_fgetsLf( line, line_len, inp ) ) && memcmp( line, SDF_END_OF_DATA, 4 ) ) - ; - if ( p ) { - err = 9; /* bypassed to $$$$; non-fatal */ - AddMOLfileError(pStrErr, "Bypassing to next structure"); - } - - } - - return err; -} -/**************** global **************************************************/ -MOL_DATA* read_sdfile_segment(FILE* inp, MOL_HEADER_BLOCK *OnlyHeaderBlock, MOL_CTAB *OnlyCtab, - int bGetOrigCoord, - char *pname, int lname, - long *Id, const char *pSdfLabel, char *pSdfValue, - int *err, char *pStrErr ) -{ - MOL_DATA* mol_data = read_mol_file( inp, OnlyHeaderBlock, OnlyCtab, bGetOrigCoord, err, pStrErr ); - int err_bypass_sdf = 0; - - if ( pname && lname ) { - pname[0] = '\0'; - } - if ( Id ) { - *Id = 0L; /* ignore for now */ - } - /* if ( mol_data && !*err ) { */ - if ( *err < 0 ) { - *err = -*err; /* end of data encountered */ - } else { - err_bypass_sdf = bypass_sdf_data_items( inp, Id, NULL, 0, pname, lname, *err, pSdfLabel, pSdfValue, pStrErr ); - if ( err_bypass_sdf ) { - *err = err_bypass_sdf; /* important to continue to the next good structure */ - } - } - /* } */ - return mol_data; -} -/******************* global *********************************************************/ -int CopyMOLfile(FILE *inp_file, long fPtrStart, long fPtrEnd, FILE *prb_file, long lNumb) -{ - char line[MOLFILEINPLINELEN], *p; - long fPtr; - int ret = 1; - char szNumber[32]; - - if ( inp_file && prb_file && fPtrStart >= 0L && - fPtrEnd > fPtrStart && - 0 == fseek( inp_file, fPtrStart, SEEK_SET ) ) { - - while ( fPtrEnd > (fPtr = ftell(inp_file)) && fPtr >= 0L && - inchi_fgetsLf( line, sizeof(line)-1, inp_file ) ) { - line[sizeof(line)-1] = '\0'; /* unnecessary extra precaution */ - if ( fPtr == fPtrStart && lNumb ) { - int len; - LtrimRtrim( line, &len ); - len = sprintf( szNumber, "#%ld%s", lNumb, len?"/":"" ); - mystrncpy( line+len, line, sizeof(line)-len-1 ); - memcpy( line, szNumber, len ); - } - if ( !strchr(line, '\n') ) { - p = line+strlen(line); - p[0] = '\n'; - p[1] = '\0'; - } - fputs( line, prb_file ); - } - ret = fseek( inp_file, fPtrEnd, SEEK_SET ); - } - return ret; -} diff --git a/INCHI-1-SRC/INCHI/common/runichi.c b/INCHI-1-SRC/INCHI/common/runichi.c deleted file mode 100644 index 4c49caf..0000000 --- a/INCHI-1-SRC/INCHI/common/runichi.c +++ /dev/null @@ -1,4008 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include -/* #include */ -#include -#include - - - -#include "mode.h" /* moved from below, suggestion by David Mosenkis */ - -#include "ichitime.h" - -#ifndef COMPILE_ANSI_ONLY -#include -#endif - -#include "inpdef.h" -#include "ichi.h" -#include "strutil.h" -#include "util.h" -#include "ichidrp.h" -#include "ichierr.h" -#include "ichimain.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichi_io.h" - -#ifdef TARGET_LIB_FOR_WINCHI -#include "ichi_lib.h" -#endif -#include "inchi_api.h" - -#include "ichicomp.h" - -#if ( ADD_CMLPP == 1 ) -#include "readcml.hpp" -#include "debug.h" -#endif - - -/* for DisplayTheWholeStructure() */ - -#define COMP_ORIG_0_MAIN 0x0001 -#define COMP_ORIG_0_RECN 0x0002 -#define COMP_PREP_0_MAIN 0x0004 -#define COMP_PREP_0_RECN 0x0008 -#define COMP_ORIG_1_MAIN 0x0010 -#define COMP_ORIG_1_RECN 0x0020 - - -/* local prototypes */ -int GetProcessingWarningsOneINChI(INChI *pINChI, INP_ATOM_DATA *inp_norm_data, char *pStrErrStruct); -int GetProcessingWarnings(INChI *cur_INChI[], INP_ATOM_DATA **inp_norm_data, STRUCT_DATA *sd); -int DisplayTheWholeStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, - INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, - ORIG_ATOM_DATA *orig_inp_data, long num_inp, int iINChI, int bShowStruct, int bINCHI_LIB_Flag ); -int DuplicateOrigAtom( ORIG_ATOM_DATA *new_orig_atom, ORIG_ATOM_DATA *orig_atom ); -int bCheckUnusualValences( ORIG_ATOM_DATA *orig_at_data, int bAddIsoH, char *pStrErrStruct ); -int CreateCompositeNormAtom( COMP_ATOM_DATA *composite_norm_data, INP_ATOM_DATA2 *all_inp_norm_data, - PINChI2 *pINChI, PINChI_Aux2 *pINChI_Aux, int num_components, INCHI_MODE nMode ); -int DetectInputINChIFileType( FILE **inp_file, INPUT_PARMS *ip, const char *fmode ); - - -/* callback */ -int (*ConsoleQuit)(void) = NULL; /* Console user issued CTRL+C etc. */ -int (*UserAction)(void) = NULL; /* callback */ - -#ifdef TARGET_LIB_FOR_WINCHI -void (*FWPRINT) (const char * format, va_list argptr )=NULL; -void (*DRAWDATA) ( struct DrawData * pDrawData) = NULL; -int (*DRAWDATA_EXISTS) ( int nComponent, int nType, int bReconnected ) = NULL; -struct DrawData * (*GET_DRAWDATA) ( int nComponent, int nType, int bReconnected ) = NULL; -#endif - -#if ( TEST_RENUMB_ATOMS == 1 ) /* { */ -/************************************************/ -/* atoms renumbering -- for testing only */ -/************************************************/ -typedef struct tagRenumbData { - PINChI2 ren_INChI2[1]; - PINChI_Aux2 ren_INChI_Aux[1]; - INP_ATOM_DATA orig_inp_cur_data; - INP_ATOM_DATA saved_inp_cur_data; -#if ( TEST_RENUMB_ATOMS_SAVE_LONGEST == 1 || TEST_RENUMB_SWITCH == 1 ) - INP_ATOM_DATA longest_inp_cur_data; -#endif - INP_ATOM_DATA ren_inp_norm_data1, ren_inp_norm_data2; - INP_ATOM_DATA *ren_inp_norm_data[2]; - int ren_counter; - int num_taut, num_non_taut, num_taut0, num_non_taut0; - AT_RANK *new_ord; - int nRet2, c1, c2, nComp, bRenumbErr; - unsigned long ulCurTimeNorm0, ulCurTimeCanon0, ulCurTimeNorm1, ulCurTimeCanon1; - unsigned long ulCurTimeNorm, ulCurTimeCanon, ulMaxTimeNorm, ulMaxTimeCanon; - unsigned long ulMaxTime, ulCurTime, ulCurTime0, ulCurTime1; -#if ( bRELEASE_VERSION == 0 ) - int bExtract; -#endif -} RENUMB_DATA; - -int RenumberingTestInit( RENUMB_DATA *pRenumbData, INP_ATOM_DATA *inp_cur_data ); -int RenumberingTestUninit( RENUMB_DATA *pRenumbData ); -int RenumberingTest( PINChI2 *pICh, PINChI_Aux2 *pINChI_Aux, ORIG_ATOM_DATA *orig_inp_data, int iINChI, - RENUMB_DATA *pRenumbData, INP_ATOM_DATA *inp_cur_data, INP_ATOM_DATA **inp_norm_data, - STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *prb_file, - int i, long num_inp, NORM_CANON_FLAGS *pncFlags); -/* -int RenumberingTest( INChI *pINChI[][TAUT_NUM], INChI_Aux *pINChI_Aux[][TAUT_NUM], int iINChI, - RENUMB_DATA *pRenumbData, INP_ATOM_DATA *inp_cur_data, INP_ATOM_DATA **inp_norm_data, - STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, INCHI_IOSTREAM *log_file, int i, long num_inp); -*/ -#endif /* } TEST_RENUMB_ATOMS */ - - - -#ifndef COMPILE_ANSI_ONLY -/********************************************************************/ -void FillTableParms( SET_DRAW_PARMS *sdp, INChI **cur_INChI, INChI_Aux **cur_INChI_Aux, - INCHI_MODE nMode, int bShowIsotopic, int indx ) -{ - TBL_DRAW_PARMS *tdp = sdp->tdp; - char (*ReqShownFound)[TDP_NUM_PAR] = tdp->ReqShownFound; - int i, j; - INChI_Stereo *Stereo; - int bShowTaut = (cur_INChI && cur_INChI[indx]->lenTautomer > 0)? 1 : 0; -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - int bRelRac = 0 != (nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO )); -#endif - if ( !cur_INChI || !cur_INChI_Aux ) { - sdp->tdp->bDrawTbl = 0; - sdp->bOrigAtom = 1; - return; - } - - /* Displayed */ - ReqShownFound[ilSHOWN][itBASIC] = bShowTaut? 'T':'\0'; - ReqShownFound[ilSHOWN][itISOTOPIC] = bShowIsotopic? 'I':'\0'; -/* - ReqShownFound[ilSHOWN][itBASIC] = bShowTaut? 'T':'B'; - ReqShownFound[ilSHOWN][itISOTOPIC] = bShowIsotopic? 'I':'N'; - */ - i = indx; - if ( cur_INChI[i] ) { - Stereo = bShowIsotopic? cur_INChI[i]->StereoIsotopic : cur_INChI[i]->Stereo; - } else { - Stereo = NULL; - } -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - if ( Stereo && ( 0 < Stereo->nNumberOfStereoBonds || - 0 < Stereo->nNumberOfStereoCenters-bRelRac ) ) { - ReqShownFound[ilSHOWN][itSTEREO] = 'S'; - if ( Stereo->nNumberOfStereoCenters && Stereo->nCompInv2Abs == -1 && - ( nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO ) ) ) { - if ( Stereo->nNumberOfStereoCenters < 2 && !Stereo->nNumberOfStereoBonds ) { - ReqShownFound[ilSHOWN][itSTEREO] = '\0'; - } else - if ( Stereo->nNumberOfStereoCenters >= 2 ) { - ReqShownFound[ilSHOWN][itSTEREO] = 's'; /* shown Inverted stereo */ - } - } -#else /* REL_RAC_STEREO_IGN_1_SC == 0 */ - if ( Stereo && ( Stereo->nNumberOfStereoBonds || Stereo->nNumberOfStereoCenters ) ) { - ReqShownFound[ilSHOWN][itSTEREO] = 'S'; - if ( Stereo->nNumberOfStereoCenters && Stereo->nCompInv2Abs == -1 && - ( nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO ) ) ) { - /* - if ( Stereo->nNumberOfStereoCenters < 2 && !Stereo->nNumberOfStereoBonds ) { - ReqShownFound[ilSHOWN][itSTEREO] = '\0'; - } else - if ( Stereo->nNumberOfStereoCenters >= 2 ) { - */ - ReqShownFound[ilSHOWN][itSTEREO] = 's'; /* shown Inverted stereo */ - /* - } - */ - } -#endif /* REL_RAC_STEREO_IGN_1_SC */ - } else { - ReqShownFound[ilSHOWN][itSTEREO] = '\0'; - } - /* - ReqShownFound[ilSHOWN][itSTEREO] = - (bShowIsotopic? (cur_INChI[i] && cur_INChI[i]->StereoIsotopic && - (cur_INChI[i]->StereoIsotopic->nNumberOfStereoBonds || - cur_INChI[i]->StereoIsotopic->nNumberOfStereoCenters) ) - : - (cur_INChI[i] && cur_INChI[i]->Stereo && - (cur_INChI[i]->Stereo->nNumberOfStereoBonds || - cur_INChI[i]->Stereo->nNumberOfStereoCenters) ) - ) ? 'S':'\0'; - */ - - /* remove zeroes between chars */ - for ( i = j = 0; i < TDP_NUM_PAR; i ++ ) { - if ( ReqShownFound[ilSHOWN][i] >= ' ' ) { - ReqShownFound[ilSHOWN][j++] = ReqShownFound[ilSHOWN][i]; - } - } - i = j; - for ( ; i < TDP_NUM_PAR; i ++ ) { - ReqShownFound[ilSHOWN][i] = '\0'; - } - - sdp->tdp->bDrawTbl = j? 1 : 0; - sdp->bOrigAtom = 0; -} -/********************************************************************/ -void FillCompositeTableParms( SET_DRAW_PARMS *sdp, AT_NUMB StereoFlags, - INCHI_MODE nMode, int bShowIsotopic, int bShowTaut ) -{ - TBL_DRAW_PARMS *tdp = sdp->tdp; - char (*ReqShownFound)[TDP_NUM_PAR] = tdp->ReqShownFound; - int i, j; - - /* Displayed */ - ReqShownFound[ilSHOWN][itBASIC] = bShowTaut? 'T':'\0'; - ReqShownFound[ilSHOWN][itISOTOPIC] = bShowIsotopic? 'I':'\0'; -/* - ReqShownFound[ilSHOWN][itBASIC] = bShowTaut? 'T':'B'; - ReqShownFound[ilSHOWN][itISOTOPIC] = bShowIsotopic? 'I':'N'; - */ - if ( StereoFlags & INF_STEREO ) { - ReqShownFound[ilSHOWN][itSTEREO] = 'S'; - if ( (StereoFlags & INF_STEREO_INV) && - ( nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO ) ) ) { - if (StereoFlags & (INF_STEREO_REL | INF_STEREO_RAC) ) { - ReqShownFound[ilSHOWN][itSTEREO] = 's'; - } else { - ReqShownFound[ilSHOWN][itSTEREO] = '\0'; /* shown Inverted stereo */ - } - } - } else { - ReqShownFound[ilSHOWN][itSTEREO] = '\0'; - } - /* - ReqShownFound[ilSHOWN][itSTEREO] = - (bShowIsotopic? (cur_INChI[i] && cur_INChI[i]->StereoIsotopic && - (cur_INChI[i]->StereoIsotopic->nNumberOfStereoBonds || - cur_INChI[i]->StereoIsotopic->nNumberOfStereoCenters) ) - : - (cur_INChI[i] && cur_INChI[i]->Stereo && - (cur_INChI[i]->Stereo->nNumberOfStereoBonds || - cur_INChI[i]->Stereo->nNumberOfStereoCenters) ) - ) ? 'S':'\0'; - */ - - /* remove zeroes between chars */ - for ( i = j = 0; i < TDP_NUM_PAR; i ++ ) { - if ( ReqShownFound[ilSHOWN][i] >= ' ' ) { - ReqShownFound[ilSHOWN][j++] = ReqShownFound[ilSHOWN][i]; - } - } - i = j; - for ( ; i < TDP_NUM_PAR; i ++ ) { - ReqShownFound[ilSHOWN][i] = '\0'; - } - - sdp->tdp->bDrawTbl = j? 1 : 0; - sdp->bOrigAtom = 0; -} -#endif -/* IchiParm.c was here */ -/*******************************************************************/ -#ifndef COMPILE_ANSI_ONLY -#ifndef TARGET_LIB_FOR_WINCHI -/*******************************************************************/ -int DisplayStructure( inp_ATOM *at, int num_at, int num_removed_H, int bAdd_DT_to_num_H, - int nNumRemovedProtons, NUM_H *nNumRemovedProtonsIsotopic, - int bIsotopic, int j /*bTautomeric*/, - INChI **cur_INChI, INChI_Aux **cur_INChI_Aux, - int bAbcNumbers, DRAW_PARMS *dp, INCHI_MODE nMode, char *szTitle ) -{ - INF_ATOM_DATA inf_data = {NULL,}; - int err = -1; - if ( CreateInfoAtomData( &inf_data, num_at, 1 ) ) { - err = 0; - FillOutInfAtom( at, &inf_data, num_at, num_removed_H, bAdd_DT_to_num_H, - nNumRemovedProtons, nNumRemovedProtonsIsotopic, bIsotopic, - cur_INChI?cur_INChI[j]:NULL, - cur_INChI_Aux?cur_INChI_Aux[j]:NULL, bAbcNumbers, nMode); - FillTableParms( &dp->sdp, cur_INChI, cur_INChI_Aux, nMode, bIsotopic, j ); - err = DisplayInputStructure( szTitle, at, &inf_data, num_at, dp ); - FreeInfoAtomData( &inf_data ); - } - return err; -} - -/*******************************************************************/ -int DisplayCompositeStructure( COMP_ATOM_DATA *composite_norm_data, int bIsotopic, int bTautomeric, - PINChI2 *pINChI2, PINChI_Aux2 *pINChI_Aux2, - int bAbcNumbers, DRAW_PARMS *dp, INCHI_MODE nMode, char *szTitle ) -{ - INF_ATOM_DATA inf_data; - int err = -1, ret; - memset( &inf_data, 0, sizeof(inf_data) ); - if ( CreateInfoAtomData( &inf_data, (composite_norm_data+bTautomeric)->num_at, - (composite_norm_data+bTautomeric)->num_components ) ) { - ret = FillOutCompositeCanonInfAtom(composite_norm_data, &inf_data, - bIsotopic, bTautomeric, - pINChI2, pINChI_Aux2, bAbcNumbers, nMode); - if ( !ret ) { - goto exit_function; /* error */ - } - if ( bTautomeric == TAUT_INI ) { - /* - FillOutInfAtom( (composite_norm_data+bTautomeric)->at, &inf_data, (composite_norm_data+bTautomeric)->num_at, - (composite_norm_data+bTautomeric)->num_removed_H, bAdd_DT_to_num_H, - (composite_norm_data+bTautomeric)->nNumRemovedProtons, - (composite_norm_data+bTautomeric)->nNumRemovedProtonsIsotopic, bIsotopic, - NULL, NULL, bAbcNumbers, nMode); - */ - ; - } else { - /* real check for tautomeric components 02-04-2005 */ - int m, nNumTautComponents = 0; - if ( 1 == bTautomeric ) { - for ( m = 0; m < composite_norm_data[TAUT_YES].num_components; m ++ ) { - if ( !pINChI2[m][TAUT_YES] ) - continue; - if ( pINChI2[m][TAUT_YES]->bDeleted || pINChI2[m][TAUT_YES]->lenTautomer > 0 ) - nNumTautComponents ++; - } - } - FillCompositeTableParms( &dp->sdp, inf_data.StereoFlags, nMode, bIsotopic, nNumTautComponents ); - } - err = DisplayInputStructure( szTitle, (composite_norm_data+bTautomeric)->at, &inf_data, (composite_norm_data+bTautomeric)->num_at, dp ); - FreeInfoAtomData( &inf_data ); - } -exit_function: - return err; -} -#endif -#endif -/************************************************/ -const char *ErrMsg( int nErrorCode ) -{ - const char *p; - static char szErrMsg[64]; - switch( nErrorCode ) { - case 0: p = ""; break; - case CT_OVERFLOW: p = "ARRAY OVERFLOW"; break; - case CT_LEN_MISMATCH: p = "LENGTH_MISMATCH"; break; - case CT_OUT_OF_RAM: p = "Out of RAM"; break; - case CT_RANKING_ERR: p = "RANKING_ERR"; break; - case CT_ISOCOUNT_ERR: p = "ISOCOUNT_ERR"; break; - case CT_TAUCOUNT_ERR: p = "TAUCOUNT_ERR"; break; - case CT_ISOTAUCOUNT_ERR: p = "ISOTAUCOUNT_ERR"; break; - case CT_MAPCOUNT_ERR: p = "MAPCOUNT_ERR"; break; - case CT_TIMEOUT_ERR: p = "Time limit exceeded"; break; - case CT_ISO_H_ERR: p = "ISO_H_ERR"; break; - case CT_STEREOCOUNT_ERR: p = "STEREOCOUNT_ERR"; break; - case CT_ATOMCOUNT_ERR: p = "ATOMCOUNT_ERR"; break; - case CT_STEREOBOND_ERROR: p = "STEREOBOND_ERR"; break; - case CT_USER_QUIT_ERR: p = "User requested termination"; break; - case CT_REMOVE_STEREO_ERR: p = "REMOVE_STEREO_ERR"; break; - case CT_CALC_STEREO_ERR: p = "CALC_STEREO_ERR"; break; - case CT_STEREO_CANON_ERR: p = "STEREO_CANON_ERR"; break; - case CT_CANON_ERR: p = "CANON_ERR"; break; - case CT_WRONG_FORMULA: p = "Wrong or missing chemical formula"; break; - /*case CT_CANON_ERR2: p = "CT_CANON_ERR2"; break;*/ - case CT_UNKNOWN_ERR: p = "UNKNOWN_ERR"; break; - case BNS_RADICAL_ERR: p = "Cannot process free radical center"; break; - case BNS_ALTBOND_ERR: p = "Cannot process aromatic bonds"; break; - - default: - if ( nErrorCode > CT_UNKNOWN_ERR ) { - sprintf( szErrMsg, "No description(%d)", nErrorCode ); - p = szErrMsg; - } else { - sprintf( szErrMsg, "UNKNOWN_ERR(%d)", CT_UNKNOWN_ERR - nErrorCode ); - p = szErrMsg; - } - break; - } - return p; -} -/***********************************************************************************/ -#ifndef COMPILE_ANSI_ONLY /* { */ -/***********************************************************************************/ -int SaveEquComponentsInfoAndSortOrder ( int iINChI, INCHI_SORT *pINChISort[TAUT_NUM], int *num_components, - ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data, - COMP_ATOM_DATA composite_norm_data[TAUT_NUM+1], - int bCompareComponents ) -{ - int nRet = 0, i, k, nNumDeleted; - /* equivalent components and sorting order */ - /* bCompareComponents: bit = 1 => compare */ - /* bit = 2 => compare non-isotopic */ - /* bit = 4 => compare non-tautomeric */ - int bCompareIsotopic, bCompareTaut, bCompareAlt; - ORIG_ATOM_DATA *inp_data = NULL; - - if ( num_components[iINChI] <= 1 ) - return 0; -#ifdef TARGET_LIB_FOR_WINCHI - if ( !DRAWDATA ) - return 0; -#endif - if ( !(bCompareComponents & CMP_COMPONENTS) ) - return 0; - bCompareIsotopic = !(bCompareComponents & CMP_COMPONENTS_NONISO); - bCompareTaut = (bCompareComponents & CMP_COMPONENTS_NONTAUT) ? TAUT_NON : TAUT_YES; - bCompareAlt = ALT_TAUT(bCompareTaut); - if ( num_components[iINChI] > 1 ) { - if ( prep_inp_data[iINChI].bSavedInINCHI_LIB[iINChI] && prep_inp_data[iINChI].bPreprocessed[iINChI] ) { - inp_data = prep_inp_data+iINChI; - } else - if ( orig_inp_data->bSavedInINCHI_LIB[iINChI] && !orig_inp_data->bPreprocessed[iINChI] ) { - inp_data = orig_inp_data; - } else { - inp_data = NULL; - } - if ( inp_data && !inp_data->nEquLabels && !prep_inp_data[iINChI].nSortedOrder ) { - int i1, i2, nSet; - AT_NUMB nAtNo; - AT_NUMB nNumAtoms = (AT_NUMB)inp_data->num_inp_atoms; - if ( (prep_inp_data[iINChI].nSortedOrder = - (AT_NUMB *)inchi_calloc(num_components[iINChI]+1, - sizeof(prep_inp_data[0].nSortedOrder[0])))) { - inp_data->nNumEquSets = 0; - for ( i1 = 0, nSet = 0; i1 < num_components[iINChI]; i1 = i2 ) { - nNumDeleted = (pINChISort[bCompareTaut][i1].pINChI[bCompareTaut] && pINChISort[bCompareTaut][i1].pINChI[bCompareTaut]->bDeleted); - for ( i2 = i1+1; i2 < num_components[iINChI]; i2 ++ ) { - /* isotopic/non-isotopic comparison does not separate equivalent components */ - if ( CompINChI2( pINChISort[bCompareTaut]+i1, pINChISort[bCompareTaut]+i2, bCompareTaut, bCompareIsotopic ) ) { - break; - } else { - nNumDeleted += (pINChISort[bCompareTaut][i2].pINChI[bCompareTaut] && pINChISort[bCompareTaut][i2].pINChI[bCompareTaut]->bDeleted); - } - } - if ( i2 - i1 - nNumDeleted > 1 ) { - if ( inp_data->nEquLabels || - (inp_data->nEquLabels = (AT_NUMB *)inchi_calloc(inp_data->num_inp_atoms+1, - sizeof(inp_data->nEquLabels[0]))) ) { - nSet ++; /* found i2-i1 equivalent components && memory has been allocated */ - for ( i = i1; i < i2; i ++ ) { - INChI_Aux *pINChI_Aux; - if (pINChISort[bCompareTaut][i].pINChI[bCompareTaut] && pINChISort[bCompareTaut][i].pINChI[bCompareTaut]->bDeleted) - continue; - pINChI_Aux = (pINChISort[bCompareTaut][i].pINChI_Aux[bCompareTaut] && - pINChISort[bCompareTaut][i].pINChI_Aux[bCompareTaut]->nNumberOfAtoms)? - pINChISort[bCompareTaut][i].pINChI_Aux[bCompareTaut]: - (pINChISort[bCompareTaut][i].pINChI_Aux[bCompareAlt] && - pINChISort[bCompareTaut][i].pINChI_Aux[bCompareAlt]->nNumberOfAtoms)? - pINChISort[bCompareTaut][i].pINChI_Aux[bCompareAlt]: - (INChI_Aux *)NULL; - if ( pINChI_Aux && pINChI_Aux->nOrigAtNosInCanonOrd ) { - for ( k = 0; k < pINChI_Aux->nNumberOfAtoms; k ++ ) { - if ( (nAtNo = pINChI_Aux->nOrigAtNosInCanonOrd[k]) && - nAtNo <= nNumAtoms ) { - inp_data->nEquLabels[nAtNo-1] = nSet; - } - } - } - } - } else { - return CT_OUT_OF_RAM; - } - } - } - nRet |= nSet? 1:0; - } else { - return CT_OUT_OF_RAM; - } - inp_data->nNumEquSets = nSet; - /* output order */ - prep_inp_data[iINChI].nSortedOrder[0] = 0; - for ( i1 = 0; i1 < num_components[iINChI]; i1 ++ ) { - prep_inp_data[iINChI].nSortedOrder[i1+1] = pINChISort[TAUT_YES][i1].ord_number+1; - } -#ifdef TARGET_LIB_FOR_WINCHI /* { */ - if ( DRAWDATA && GET_DRAWDATA && inp_data->nNumEquSets > 0 && inp_data->nEquLabels ) { - int nType = inp_data->bPreprocessed[iINChI]? - COMPONENT_ORIGINAL_PREPROCESSED : - COMPONENT_ORIGINAL; - struct DrawData *pDrawData = GET_DRAWDATA( 0, nType, iINChI); - if ( pDrawData && pDrawData->pWindowData && !pDrawData->pWindowData->nEquLabels ) { - /* copy equivalence data from inp_data to pDrawData->pWindowData */ - if ( inp_data->nEquLabels && - (pDrawData->pWindowData->nEquLabels = (AT_NUMB *)inchi_calloc(inp_data->num_inp_atoms, - sizeof(inp_data->nEquLabels[0])))) { - memcpy( pDrawData->pWindowData->nEquLabels, inp_data->nEquLabels, - inp_data->num_inp_atoms * sizeof(inp_data->nEquLabels[0])); - pDrawData->pWindowData->nNumEquSets = inp_data->nNumEquSets; - pDrawData->pWindowData->nCurEquLabel = 0; - } - } - } -#endif /* } TARGET_LIB_FOR_WINCHI */ - } - } - return nRet; -} - -/************************************************************************************************/ -int DisplayTheWholeCompositeStructure( INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, int iINChI, - PINChI2 *pINChI2, PINChI_Aux2 *pINChI_Aux2, - ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data, - COMP_ATOM_DATA composite_norm_data[TAUT_NUM+1] ) -{ - ORIG_ATOM_DATA *inp_data = NULL; - int jj, j, k, err = 0, nNumIntermediateTaut = 0, bDisplayTaut; - char szTitle[256]; - int nNumTautComponents, m; - - int bCompareIsotopic = !(ip->bCompareComponents & CMP_COMPONENTS_NONISO); - int bCompareTaut = (ip->bCompareComponents & CMP_COMPONENTS_NONTAUT) ? TAUT_NON : TAUT_YES; - - if ( ip->bCompareComponents & CMP_COMPONENTS ) { - if ( prep_inp_data[iINChI].bSavedInINCHI_LIB[iINChI] && prep_inp_data[iINChI].bPreprocessed[iINChI] ) { - inp_data = prep_inp_data+iINChI; - } else - if ( orig_inp_data->bSavedInINCHI_LIB[iINChI] && !orig_inp_data->bPreprocessed[iINChI] ) { - inp_data = orig_inp_data; - } - } - /************************************************************************** - * display from one up to 4 structure pictures-results for all components * - * Enable buttons: * - * BN (non-tautomeric non-isotopic): inp_norm_data[0]->bExists * - * TN (tautomeric non-isotopic): inp_norm_data[1]->bExists * - * BI (non-tautomeric isotopic): inp_norm_data[0]->bExists && * - * inp_norm_data[0]->bHasIsotopicLayer * - * TI (tautomeric isotopic): inp_norm_data[1]->bExists && * - * inp_norm_data[1]->bHasIsotopicLayer * - **************************************************************************/ - for ( jj = 0; ip->bDisplayCompositeResults && !sd->bUserQuitComponentDisplay && jj <= TAUT_INI; jj ++ ) { - /*for ( j = 0; ip->bDisplayCompositeResults && !sd->bUserQuitComponentDisplay && j <= TAUT_INI; j ++ )*/ - j = (jj==0)? TAUT_NON : (jj==1)? TAUT_INI : (jj==2)? TAUT_YES : -1; - if ( j < 0 ) - continue; - if ( composite_norm_data[j].bExists && composite_norm_data[j].num_components > 1 ) { - bDisplayTaut = (!(ip->nMode & REQ_MODE_BASIC) && !j)? -1 : j; - nNumTautComponents = 0; - if ( bDisplayTaut ) { - /* find whether the structure is actually tautomeric */ - for ( m = 0; m < composite_norm_data[TAUT_YES].num_components; m ++ ) { - if ( !pINChI2[m][TAUT_YES] ) - continue; - if ( pINChI2[m][TAUT_YES]->bDeleted || pINChI2[m][TAUT_YES]->lenTautomer > 0 ) - nNumTautComponents ++; - } - } - for ( k = 0; k <= composite_norm_data[j].bHasIsotopicLayer && !sd->bUserQuitComponentDisplay; k ++ ) { - /* added number of components, added another format for a single component case - DCh */ - int bMobileH = (bDisplayTaut>0 && nNumTautComponents); - sprintf( szTitle, "%s Structure #%ld%s%s.%s%s%s%s%s", - j == TAUT_INI? "Preprocessed":"Result for", num_inp, - bMobileH? ", mobile H": - bDisplayTaut==0?", fixed H":"", - /*j? ", mobile H":", fixed H",*/ - k? ", isotopic":"", - SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), iINChI? " (Reconnected)":""); -#ifndef TARGET_LIB_FOR_WINCHI - /****** Display composite Result structure **************/ - nNumIntermediateTaut += (j == TAUT_INI ); /* display TAUT_INI (preprocessed) only once */ - if ( j != TAUT_INI || nNumIntermediateTaut == 1 ) { - err = DisplayCompositeStructure( composite_norm_data, j==TAUT_INI? 1:k /* bIsotopic*/, - j/*tautomeric*/, - j==TAUT_INI? NULL:pINChI2, j==TAUT_INI? NULL:pINChI_Aux2, - ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); - } - if ( sd->bUserQuitComponentDisplay = (err==ESC_KEY) ) { - break; - } - - if ( inp_data && inp_data->nEquLabels && inp_data->nNumEquSets && !sd->bUserQuitComponentDisplay && - ((j == bCompareTaut || bCompareTaut && j == TAUT_INI) || - bCompareTaut && !composite_norm_data[bCompareTaut].bExists) && - (k == bCompareIsotopic || - bCompareIsotopic && !composite_norm_data[j].bHasIsotopicLayer) ) { - AT_NUMB nEquSet; - int bDisplaySaved = ip->bDisplay; - /****** Display Equ Sets of composite Result structure **************/ - for ( nEquSet = 1; nEquSet <= inp_data->nNumEquSets; nEquSet ++ ) { - sprintf( szTitle, "Equ set %d of %d, %s Structure #%ld%s%s.%s%s%s%s%s", - nEquSet, inp_data->nNumEquSets, - j == TAUT_INI? "Preprocessed":"Result for", - num_inp, - (bDisplayTaut>0 && nNumTautComponents)? ", mobile H": bDisplayTaut==0?", fixed H":"", - /*j? ", mobile H":", fixed H",*/ - k? ", isotopic":"", - SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), iINChI? " (Reconnected)":""); - ip->dp.nEquLabels = inp_data->nEquLabels; - ip->dp.nCurEquLabel = nEquSet; - ip->dp.nNumEquSets = inp_data->nNumEquSets; - ip->bDisplay = 1; /* force display if it was not requested */ - err = DisplayCompositeStructure( composite_norm_data, k, j, - pINChI2, pINChI_Aux2, - ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); - ip->dp.nEquLabels = NULL; - ip->dp.nCurEquLabel = 0; - ip->dp.nNumEquSets = 0; - ip->bDisplay = bDisplaySaved; /* restore display option */ - - if ( sd->bUserQuitComponentDisplay = (err==ESC_KEY) ) { - break; - } - } - } -#else - if(DRAWDATA && j <= TAUT_YES) - { - struct DrawData vDrawData; - vDrawData.pWindowData = CreateWinDataComposite_( composite_norm_data, k, j, - pINChI2, pINChI_Aux2, - ip->bAbcNumbers, &ip->dp, ip->nMode); - /* vDrawData.pWindowData = CreateWinData_( composite_norm_data[j].at, composite_norm_data[j].num_at, - k, j, pINChI[i], pINChI_Aux[i],ip->bAbcNumbers, &ip->dp, ip->nMode ); */ - if( vDrawData.pWindowData != NULL ) - { - int nType; - vDrawData.nComponent = 0; - if( j == 0 ) - nType = (k == 0) ? COMPONENT_BN: COMPONENT_BI; - else - nType = (k == 0) ? COMPONENT_TN: COMPONENT_TI; - vDrawData.nType = nType; - vDrawData.bReconnected = iINChI; /* 0=>main; 1=>reconnected */ - vDrawData.szTitle = _strdup(szTitle); - vDrawData.pWindowData->szTitle = _strdup(szTitle); - if ( inp_data && inp_data->nEquLabels && inp_data->nNumEquSets && - (j == bCompareTaut || bCompareTaut && !composite_norm_data[bCompareTaut].bExists) && - (k == bCompareIsotopic || bCompareIsotopic && !composite_norm_data[j].bHasIsotopicLayer) && - (vDrawData.pWindowData->nEquLabels = (AT_NUMB *)inchi_calloc(inp_data->num_inp_atoms, - sizeof(inp_data->nEquLabels[0])))) { - memcpy( vDrawData.pWindowData->nEquLabels, inp_data->nEquLabels, - inp_data->num_inp_atoms * sizeof(inp_data->nEquLabels[0])); - vDrawData.pWindowData->nNumEquSets = inp_data->nNumEquSets; - vDrawData.pWindowData->nCurEquLabel = 0; - } - DRAWDATA(&vDrawData); - } - } else - if(DRAWDATA && GET_DRAWDATA && j == TAUT_INI) - { - struct DrawData vDrawData; - struct DrawData *pDrawData; - - if ( !(ip->bCompareComponents & CMP_COMPONENTS) || - (ip->bCompareComponents & CMP_COMPONENTS_NONTAUT) || - !k != !composite_norm_data[j].bHasIsotopicLayer ) { - - continue; - } - /* - vDrawData.pWindowData = CreateWinDataComposite_( composite_norm_data, k, j, - pINChI2, pINChI_Aux2, - ip->bAbcNumbers, &ip->dp, ip->nMode); - */ - vDrawData.pWindowData = CreateWinDataComposite_( composite_norm_data, 1 /*k*/, j, - NULL, NULL, - ip->bAbcNumbers, &ip->dp, ip->nMode); - if( vDrawData.pWindowData != NULL ) - { - int nType = COMPONENT_ORIGINAL_PREPROCESSED; - pDrawData = GET_DRAWDATA( 0, nType, iINChI); - if ( pDrawData ) { - FreeDrawData( pDrawData ); - pDrawData->pWindowData = vDrawData.pWindowData; - vDrawData.pWindowData = NULL; - } else { - pDrawData = &vDrawData; - } - - /* vDrawData.pWindowData = CreateWinData_( composite_norm_data[j].at, composite_norm_data[j].num_at, - k, j, pINChI[i], pINChI_Aux[i],ip->bAbcNumbers, &ip->dp, ip->nMode ); */ - pDrawData->nComponent = 0; - pDrawData->nType = nType; - pDrawData->bReconnected = iINChI; /* 0=>main; 1=>reconnected */ - pDrawData->szTitle = _strdup(szTitle); - pDrawData->pWindowData->szTitle = _strdup(szTitle); - if ( inp_data && inp_data->nEquLabels && inp_data->nNumEquSets && - /*(j == bCompareTaut || bCompareTaut && !composite_norm_data[bCompareTaut].bExists) &&*/ - /*(k == bCompareIsotopic || bCompareIsotopic && !composite_norm_data[j].bHasIsotopicLayer) &&*/ - (pDrawData->pWindowData->nEquLabels = (AT_NUMB *)inchi_calloc(inp_data->num_inp_atoms, - sizeof(inp_data->nEquLabels[0])))) { - memcpy( pDrawData->pWindowData->nEquLabels, inp_data->nEquLabels, - inp_data->num_inp_atoms * sizeof(inp_data->nEquLabels[0])); - pDrawData->pWindowData->nNumEquSets = inp_data->nNumEquSets; - pDrawData->pWindowData->nCurEquLabel = 0; - } - if ( pDrawData == &vDrawData ) { - DRAWDATA(pDrawData); /* there was no prepocessed structure */ - } - } - } -#endif - } - } - } - return err; -} - -#endif /* }COMPILE_ANSI_ONLY */ - - - -/***********************************************************************************/ -/* pINChI[INCHI_BAS] refers to either disconnected or original structure; */ -/* num_components[INCHI_BAS] > 0 if there was input structure */ -/***********************************************************************************/ -/* pINChI[INCHI_REC] refers to the reconnected structure, */ -/* and only if the input structure has been disconnected, that is,*/ -/* num_components[INCHI_REC] > 0 */ -/***********************************************************************************/ -int SortAndPrintINChI(INCHI_IOSTREAM *output_file, - char *pStr, int nStrLen, - INCHI_IOSTREAM *log_file, - INPUT_PARMS *ip, - ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data, - COMP_ATOM_DATA composite_norm_data[INCHI_NUM][TAUT_NUM+1], - ORIG_STRUCT *pOrigStruct, int num_components[INCHI_NUM], - int num_non_taut[INCHI_NUM], int num_taut[INCHI_NUM], - INCHI_MODE bTautFlags[INCHI_NUM], INCHI_MODE bTautFlagsDone[INCHI_NUM], - NORM_CANON_FLAGS *pncFlags, long num_inp, - PINChI2 *pINChI[INCHI_NUM], - PINChI_Aux2 *pINChI_Aux[INCHI_NUM], - int *pSortPrintINChIFlags, unsigned char save_opt_bits) -{ - INCHI_SORT *pINChISort[INCHI_NUM][TAUT_NUM]; - int j, i, k, k1, ret, iINChI, max_num_components; - INCHI_MODE nMode; - int bDisconnectedCoord = (0 != (bTautFlagsDone[0] & TG_FLAG_DISCONNECT_COORD_DONE)); - int bINChIOutputOptions0, bCurOption, bINChIOutputOptionsCur, bEmbedReconnected, bAnnInXmlBrackets; - static const char szAnnHdr[] = "InChI ANNOTATED CONTENTS"; - int ikflag = 0; - - ret = 1; - for ( i = 0; i < INCHI_NUM; i ++ ) { - for ( k = 0; k < TAUT_NUM; k ++ ) { - bTautFlags[i] |= pncFlags->bTautFlags[i][k]; - bTautFlagsDone[i] |= pncFlags->bTautFlagsDone[i][k]; - } - } - nMode = ip->nMode; - if ( !(nMode & (REQ_MODE_BASIC|REQ_MODE_TAUT)) ) { - nMode |= (REQ_MODE_BASIC|REQ_MODE_TAUT); - } - - max_num_components = 0; - for ( j = 0; j < INCHI_NUM; j ++ ) { - if ( max_num_components < num_components[j] ) - max_num_components = num_components[j]; - } - if ( max_num_components <= 0 ) - max_num_components = 1; - - for ( j = 0, i = 0; j < INCHI_NUM; j ++ ) { - if ( num_components[j] ) { - for ( k1 = 0; k1 < TAUT_NUM; k1 ++ ) { - pINChISort[j][k1] = (INCHI_SORT *)inchi_calloc(max_num_components, sizeof(pINChISort[0][0][0]) ); - i += !pINChISort[j][k1]; /* number of failed allocatons */ - } - } else { - for ( k1 = 0; k1 < TAUT_NUM; k1 ++ ) { - pINChISort[j][k1] = NULL; /* keep BC happy */ - } - } - } - if ( i ) { - ret = CT_OUT_OF_RAM; - goto exit_function; - } - - - for ( j = 0; j < INCHI_NUM; j ++ ) { - - if ( !num_components[j] ) { - continue; - } - - iINChI = j; - -#if ( OUTPUT_CONNECTED_METAL_ONLY == 1 ) /* test: output connected as the only one INChI */ - if ( INCHI_BAS == j && num_components[INCHI_REC] ) { - j = INCHI_REC; - } -#endif - - /*j = INCHI_BAS; <- for debug only */ - /* for only normal or disconnected coord compounds */ - /* (j=0=INCHI_BAS => normal or disconnected, j=1=INCHI_REC => reconnected */ - for ( k1 = 0; k1 < TAUT_NUM; k1 ++ ) { - for ( i = 0; i < num_components[j]; i ++ ) { - for ( k = 0; k < TAUT_NUM; k ++ ) { - pINChISort[j][k1][i].pINChI[k] = pINChI[j][i][k]; - pINChISort[j][k1][i].pINChI_Aux[k] = pINChI_Aux[j][i][k]; - } - pINChISort[j][k1][i].ord_number = i; - } - } - /* sort component INChIs */ - for ( k1 = 0; k1 < TAUT_NUM; k1 ++ ) { - switch ( k1 ) { - case TAUT_NON: - qsort( pINChISort[j][k1], num_components[j], sizeof(pINChISort[0][0][0]), CompINChINonTaut2 ); - break; - case TAUT_YES: - qsort( pINChISort[j][k1], num_components[j], sizeof(pINChISort[0][0][0]), CompINChITaut2 ); - break; - } - } -#ifndef COMPILE_ANSI_ONLY -/* find equivalent and wINChI display order; use requested in ip->bCompareComponents comparison */ - ret = SaveEquComponentsInfoAndSortOrder ( iINChI, pINChISort[j], num_components, orig_inp_data, prep_inp_data, -#if ( FIX_DALKE_BUGS == 1 ) - composite_norm_data? composite_norm_data[j]:NULL, -#else - composite_norm_data[j], -#endif - ip->bCompareComponents ); - if ( RETURNED_ERROR( ret ) ) { - ret = 0; - goto exit_function; - } else { - ret = 1; - } -#endif - } - - if ( !( ip->bINChIOutputOptions & INCHI_OUT_PRINT_OPTIONS ) ) { - /* prepare InChI from the structures obtained by reversing InChI for returning to the caller */ - for ( j = 0; j < INCHI_NUM; j ++ ) { - if ( !num_components[j] ) { - continue; - } - /* pINChI[iINCHI][iComponent][bTaut] */ - /* j = disconnected/connected */ - /* k1 = sort order for Mobile or Fixed H */ - k1 = TAUT_YES; /* in Mobile H order */ - /* store components in Mobile H order */ - - for ( i = 0; i < num_components[j]; i ++ ) { - - if ( pINChISort[j][k1][i].pINChI[TAUT_NON] && - !pINChISort[j][k1][i].pINChI[TAUT_YES] ) { - /* make sure Mobile-H is always present */ - for ( k = 0; k < TAUT_NUM; k ++ ) { - pINChI[j][i][k] = pINChISort[j][k1][i].pINChI[ALT_TAUT(k)]; - pINChI_Aux[j][i][k] = pINChISort[j][k1][i].pINChI_Aux[ALT_TAUT(k)]; - } - } else { - - for ( k = 0; k < TAUT_NUM; k ++ ) { - pINChI[j][i][k] = pINChISort[j][k1][i].pINChI[k]; - pINChI_Aux[j][i][k] = pINChISort[j][k1][i].pINChI_Aux[k]; - } - } - } - } - - } else { - - /* print inchi string(s) */ - - - bINChIOutputOptions0 = ip->bINChIOutputOptions & ~INCHI_OUT_PRINT_OPTIONS; - - bEmbedReconnected = ip->bINChIOutputOptions & INCHI_OUT_EMBED_REC; - - for ( i = 0; i < 4; i ++ ) { - switch( i ) { - case 0: - bCurOption = INCHI_OUT_XML; - break; - case 1: - bCurOption = INCHI_OUT_PLAIN_TEXT; - break; - case 2: - bCurOption = INCHI_OUT_PLAIN_TEXT_COMMENTS; - break; - case 3: - bCurOption = INCHI_OUT_XML_TEXT_COMMENTS; - break; - default: - continue; - } - if ( ip->bINChIOutputOptions & bCurOption ) { - bAnnInXmlBrackets = 0; - if ( i == 1 ) { - ;/*bEmbedReconnected = 0;*/ - } - if ( i == 3 ) { - bCurOption = INCHI_OUT_XML; /* xml output as annotation */ - } - bINChIOutputOptionsCur = bINChIOutputOptions0 | bCurOption; - switch ( i ) { - case 0: - case 1: - /* output INChI */ - bINChIOutputOptionsCur |= bEmbedReconnected; - break; - case 2: - case 3: - /* output annotation */ - bAnnInXmlBrackets = (i == 2 && (ip->bINChIOutputOptions & INCHI_OUT_XML )); - if ( bAnnInXmlBrackets ) { - inchi_ios_print( output_file, "\n<%s>\n", szAnnHdr ); - } else { - inchi_ios_print( output_file, "\n==== %s ====\n", szAnnHdr ); - } - bINChIOutputOptionsCur |= bEmbedReconnected; - bINChIOutputOptionsCur &= ~INCHI_OUT_TABBED_OUTPUT; - break; - default: - continue; - } - -#ifdef TARGET_LIB_FOR_WINCHI - if ( ikflag==0 ) - output_file->type = INCHI_IOSTREAM_STRING; -#endif - - - ret &= OutputINChI2(pStr, nStrLen, - pINChISort, INCHI_BAS /*iINChI*/, - pOrigStruct, - bDisconnectedCoord, OUT_TN, - bINChIOutputOptionsCur, - 0 != (bINChIOutputOptionsCur & INCHI_OUT_XML), - ip->bAbcNumbers, ip->bCtPredecessors, - ip->bNoStructLabels, - num_components, num_non_taut, num_taut, - output_file, log_file, num_inp, - ip->pSdfLabel,ip->pSdfValue, ip->lSdfId, - pSortPrintINChIFlags, - save_opt_bits); - - if ( ret && !(bINChIOutputOptionsCur & INCHI_OUT_EMBED_REC) ) - { - ret &= OutputINChI2(pStr, nStrLen, pINChISort, INCHI_REC /*iINChI*/, - pOrigStruct, - bDisconnectedCoord, OUT_TN, - bINChIOutputOptionsCur, - 0 != (bINChIOutputOptionsCur & INCHI_OUT_XML), - ip->bAbcNumbers, ip->bCtPredecessors, - ip->bNoStructLabels, - num_components, num_non_taut, num_taut, - output_file, log_file, num_inp, - ip->pSdfLabel,ip->pSdfValue, ip->lSdfId, - pSortPrintINChIFlags, - save_opt_bits); - } - -#ifdef TARGET_LIB_FOR_WINCHI - /* always calculate InChIKey */ - ikflag++; - if (ikflag==1) - { - if (ret) - { - char ik_string[256]; /*^^^ Resulting InChIKey string */ - int ik_ret=0; /*^^^ InChIKey-calc result code */ - int xhash1, xhash2; - char szXtra1[256], szXtra2[256]; - size_t slen = output_file->s.nUsedLength; - char *buf = NULL; - extract_inchi_substring(&buf, output_file->s.pStr, slen); - inchi_ios_flush(output_file); - output_file->type = INCHI_IOSTREAM_FILE; - /* calculate and print InChIKey */ - if (NULL!=buf) - { - xhash1 = xhash2 = 0; - if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1 ) || - ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) - xhash1 = 1; - if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA2 ) || - ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) - xhash2 = 1; - ik_ret = GetINCHIKeyFromINCHI(buf, xhash1, xhash2, ik_string, szXtra1, szXtra2); - inchi_free(buf); - } - else - ik_ret = 3; - - if (ik_ret==INCHIKEY_OK) - { - /* NB: correctly treat tabbed output with InChIKey & hash extensions */ - char csep = '\n'; - if ( ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT ) - csep = '\t'; - inchi_ios_print(output_file, "InChIKey=%-s",ik_string); - if ( xhash1 ) - inchi_ios_print(output_file, "%cXHash1=%-s",csep,szXtra1); - if ( xhash2 ) - inchi_ios_print(output_file, "%cXHash2=%-s",csep,szXtra2); - inchi_ios_print(output_file, "\n"); - } - else - { - inchi_ios_print(log_file, "Warning (Could not compute InChIKey: ", num_inp); - } - - /*inchi_ios_flush(output_file); - inchi_ios_flush2(log_file, stderr);*/ - } - else - { - inchi_ios_flush(output_file); - output_file->type = INCHI_IOSTREAM_FILE; - } - - } - -#endif - - if ( bAnnInXmlBrackets ) { - inchi_ios_print( output_file, "\n\n", szAnnHdr ); - } - if ( !ret ) { - break; - } - } - } - } - -exit_function: - for ( j = 0; j < INCHI_NUM; j ++ ) { - for ( k1 = 0, i = 0; k1 < TAUT_NUM; k1 ++ ) { - if ( pINChISort[j][k1] ) { - inchi_free( pINChISort[j][k1] ); - } - } - } - ret = ret? 0 : _IS_FATAL; - - - - return ret; -} -/**********************************************************************************/ -void FreeAllINChIArrays( PINChI2 *pINChI[INCHI_NUM], PINChI_Aux2 *pINChI_Aux[INCHI_NUM], int num_components[INCHI_NUM] ) -{ - int k; - for ( k = 0; k < INCHI_NUM; k ++ ) { - FreeINChIArrays( pINChI[k], pINChI_Aux[k], num_components[k] ); - num_components[k] = 0; - if ( pINChI[k] ) { - inchi_free( pINChI[k] ); - pINChI[k] = NULL; - } - if ( pINChI_Aux[k] ) { - inchi_free( pINChI_Aux[k] ); - pINChI_Aux[k] = NULL; - } - - } -} -/**********************************************************************************/ -void FreeINChIArrays( PINChI2 *pINChI, PINChI_Aux2 *pINChI_Aux, int num_components ) -{ - int i, k; - /* release allocated memory */ - if ( pINChI ) { - for ( i = 0; i < num_components; i ++ ) { - for ( k = 0; k < TAUT_NUM; k ++ ) { - Free_INChI( &pINChI[i][k] ); - /* - inchi_free( pINChI[i][k] ); - pINChI[i][k] = NULL; - */ - } - } - } - if ( pINChI_Aux ) { - for ( i = 0; i < num_components; i ++ ) { - for ( k = 0; k < TAUT_NUM; k ++ ) { - Free_INChI_Aux( &pINChI_Aux[i][k] ); - /* - inchi_free( pINChI_Aux[i][k] ); - pINChI_Aux[i][k] = NULL; - */ - } - } - } -} - - -/********************************************** - * output " L=V" or " L missing" or "" - * The fprintf format string must contain %s%s%s%s - */ - -const char gsMissing[] = "is missing"; -const char gsEmpty[] = ""; -const char gsSpace[] = " "; -const char gsEqual[] = "="; - -#ifndef TARGET_API_LIB -/*********************************************************************************************************/ -void SplitTime( unsigned long ulTotalTime, int *hours, int *minutes, int *seconds, int *mseconds ) -{ - *mseconds = (int)(ulTotalTime % 1000); - ulTotalTime /= 1000; - *seconds = (int)(ulTotalTime % 60); - ulTotalTime /= 60; - *minutes = (int)(ulTotalTime % 60); - ulTotalTime /= 60; - *hours = (int)(ulTotalTime); -} -/*********************************************************************************************************/ -int ReadTheStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, INCHI_IOSTREAM *inp_file, ORIG_ATOM_DATA *orig_inp_data, - /* for CML:*/ int inp_index, int *out_index ) -{ - inchiTime ulTStart; - int nRet = 0, nRet2 = 0; - int bGetOrigCoord = !(ip->bINChIOutputOptions & (INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO)); - INCHI_MODE InpAtomFlags = 0; /* reading Molfile may set FLAG_INP_AT_CHIRAL bit */ - - /* vABParityUnknown holds actual value of an internal constant signifying */ - /* unknown parity: either the same as for undefined parity (default==standard) */ - /* or a specific one (non-std; requested by SLUUD switch). */ - int vABParityUnknown = AB_PARITY_UNDF; - if ( 0 != ( ip->nMode & REQ_MODE_DIFF_UU_STEREO) ) - { - /* Make labels for unknown and undefined stereo different */ - vABParityUnknown = AB_PARITY_UNKN; - } - - memset( sd, 0, sizeof(*sd) ); - switch ( ip->nInputType ) { - case INPUT_MOLFILE: - case INPUT_SDFILE: - if ( orig_inp_data ) { - if ( ip->pSdfValue && ip->pSdfValue[0] ) { - /* Added 07-29-2003 to avoid inheriting exact value from prev. structure - and to make reference to a (bad) structure with unknown ID Value */ - char *p, *q; /* q shadows prev declaration of const char *q */ - int n; - if ( (p = strrchr( ip->pSdfValue, '+' )) && - '[' == *(p-1) && 0 < (n=strtol(p+1,&q,10)) && q[0] && ']'==q[0] && !q[1] ) { - sprintf( p+1, "%d]", n+1 ); - } else { - strcat( ip->pSdfValue, " [+1]" ); - } - } - InchiTimeGet( &ulTStart ); - sd->fPtrStart = (inp_file->f == stdin)? -1 : ftell( inp_file->f ); - /* read the original structure */ - nRet2 = MolfileToOrigAtom( inp_file->f, orig_inp_data, ip->bMergeAllInputStructures, bGetOrigCoord, ip->bDoNotAddH, - ip->pSdfLabel, ip->pSdfValue, &ip->lSdfId, &ip->lMolfileNumber, - &InpAtomFlags, &sd->nStructReadError, sd->pStrErrStruct ); - - - if ( !ip->bGetSdfileId || ip->lSdfId == 999999) ip->lSdfId = 0; - if ( !ip->bGetMolfileNumber || ip->lMolfileNumber < 0 ) ip->lMolfileNumber = 0; - sd->fPtrEnd = (inp_file->f == stdin)? -1 : ftell( inp_file->f ); - sd->ulStructTime += InchiTimeElapsed( &ulTStart ); -#if ( bRELEASE_VERSION == 0 ) - sd->bExtract |= orig_inp_data->bExtract; -#endif - /* 2004-11-16: added Molfile Chiral Flag Mode */ - /* ***************************************************************************** - * Chiral flags are set in: - * - RunICHI.c #1610 -- ReadTheStructure() -- cInChI, wInChI (here) - * - e_IchiMain.c #273 -- main() -- C example of calling InChI dll - * - inchi_dll.c #1662 -- ExtractOneStructure -- InChI dll code - *******************************************************************************/ - /* 1. Highest precedence: Chiral Flag set by the user */ - if ( ip->bChiralFlag & FLAG_SET_INP_AT_CHIRAL ) { - InpAtomFlags = FLAG_INP_AT_CHIRAL; /* forced by the user */ - } else - if ( ip->bChiralFlag & FLAG_SET_INP_AT_NONCHIRAL ) { - InpAtomFlags = FLAG_INP_AT_NONCHIRAL; /* forced by the user */ - } else - if ( (InpAtomFlags & FLAG_INP_AT_CHIRAL) && (InpAtomFlags && FLAG_INP_AT_NONCHIRAL) ) { - InpAtomFlags &= ~FLAG_INP_AT_NONCHIRAL; - } - /* save requested flags in the AuxInfo */ - sd->bChiralFlag &= ~( FLAG_INP_AT_CHIRAL | FLAG_INP_AT_NONCHIRAL ); - sd->bChiralFlag |= InpAtomFlags & ( FLAG_INP_AT_CHIRAL | FLAG_INP_AT_NONCHIRAL ); - /* quick fix: modify ip->nMode on the fly */ - /* 2. The user requested both Stereo AND Chiral flag */ - if ( (ip->nMode & REQ_MODE_CHIR_FLG_STEREO) && (ip->nMode & REQ_MODE_STEREO) ) { - if ( InpAtomFlags & FLAG_INP_AT_CHIRAL ) { - /* structure has chiral flag or the user said it is chiral */ - ip->nMode &= ~(REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO); - sd->bChiralFlag |= FLAG_INP_AT_CHIRAL; /* write AuxInfo as chiral */ - } else { - ip->nMode &= ~REQ_MODE_RACEMIC_STEREO; - ip->nMode |= REQ_MODE_RELATIVE_STEREO; - sd->bChiralFlag |= FLAG_INP_AT_NONCHIRAL; /* write AuxInfo as explicitly not chiral */ - } - } - } else { - /* read the next original structure */ - int nStructReadError=0; - if ( !ip->bMergeAllInputStructures ) { - nRet2 = MolfileToOrigAtom( inp_file->f, NULL, 0, 0, 0, - NULL, NULL, NULL, NULL, - NULL, &nStructReadError, NULL ); - if ( nRet2 <= 0 && 10 < nStructReadError && nStructReadError < 20 ) { - return _IS_EOF; - } - } else { - return _IS_EOF; - } - } - break; - case INPUT_INCHI_XML: - case INPUT_INCHI_PLAIN: - if ( orig_inp_data ) { - if ( ip->pSdfValue && ip->pSdfValue[0] ) { - /* Added 07-29-2003 to avoid inheriting exact value from prev. structure - and to make reference to a (bad) structure with unknown ID Value */ - char *p, *q; - int n; - if ( (p = strrchr( ip->pSdfValue, '+' )) && - '[' == *(p-1) && 0 < (n=strtol(p+1,&q,10)) && q[0] && ']'==q[0] && !q[1] ) { - sprintf( p+1, "%d]", n+1 ); - } else { - strcat( ip->pSdfValue, " [+1]" ); - } - } - InchiTimeGet( &ulTStart ); - sd->fPtrStart = (inp_file->f == stdin)? -1 : ftell( inp_file->f ); - /* read the original structure */ - nRet2 = INChIToOrigAtom( inp_file, orig_inp_data, ip->bMergeAllInputStructures, - bGetOrigCoord, ip->bDoNotAddH, vABParityUnknown, - ip->nInputType, ip->pSdfLabel, ip->pSdfValue, &ip->lMolfileNumber, - &InpAtomFlags, &sd->nStructReadError, sd->pStrErrStruct ); - /*if ( !ip->bGetSdfileId || ip->lSdfId == 999999) ip->lSdfId = 0;*/ - sd->fPtrEnd = (inp_file->f == stdin)? -1 : ftell( inp_file->f ); - - sd->ulStructTime += InchiTimeElapsed( &ulTStart ); -#if ( bRELEASE_VERSION == 0 ) - sd->bExtract |= orig_inp_data->bExtract; -#endif - /* 2004-11-16: added Molfile Chiral Flag Mode */ - if ( ip->bChiralFlag & FLAG_SET_INP_AT_CHIRAL ) { - InpAtomFlags = FLAG_INP_AT_CHIRAL; /* forced by the user */ - } else - if ( ip->bChiralFlag & FLAG_SET_INP_AT_NONCHIRAL ) { - InpAtomFlags = FLAG_INP_AT_NONCHIRAL; /* forced by the user */ - } else - if ( (InpAtomFlags & FLAG_INP_AT_CHIRAL) && (InpAtomFlags && FLAG_INP_AT_NONCHIRAL) ) { - InpAtomFlags &= ~FLAG_INP_AT_NONCHIRAL; - } - sd->bChiralFlag |= InpAtomFlags; /* copy chiral flag to AuxInfo */ - /* quick fix: modify ip->nMode on the fly */ - if ( (ip->nMode & REQ_MODE_CHIR_FLG_STEREO) && (ip->nMode & REQ_MODE_STEREO) ) { - if ( InpAtomFlags & FLAG_INP_AT_CHIRAL ) { - ip->nMode &= ~(REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO); - } else { - ip->nMode &= ~REQ_MODE_RACEMIC_STEREO; - ip->nMode |= REQ_MODE_RELATIVE_STEREO; - } - } - } else { - /* read the next original structure */ - int nStructReadError=0; - if ( !ip->bMergeAllInputStructures ) { - nRet2 = INChIToOrigAtom( inp_file, NULL, 0, 0, 0, 0, - ip->nInputType, NULL, NULL, NULL, NULL, &nStructReadError, NULL ); - if ( nRet2 <= 0 && 10 < nStructReadError && nStructReadError < 20 ) { - return _IS_EOF; - } - } else { - return _IS_EOF; - } - } - break; - -#if ( ADD_CMLPP == 1 ) - /* BILLY 8/6/04 */ - case INPUT_CMLFILE: - if ( orig_inp_data ) { - - InchiTimeGet( &ulTStart ); - /* - if ( inp_index >= 0 ) { - sd->fPtrStart = inp_index; - } else { - sd->fPtrStart = GetCmlStructIndex(); - } - */ - sd->fPtrStart = -1; /* disable "CopyMOLfile() for CML input files */ - sd->fPtrEnd = -1; - /* read the original structure */ - nRet = CmlfileToOrigAtom( inp_file->f, orig_inp_data, ip->bMergeAllInputStructures, - bGetOrigCoord, ip->bDoNotAddH, inp_index, out_index, - ip->pSdfLabel, ip->pSdfValue, &ip->lSdfId, - &sd->nStructReadError, sd->pStrErrStruct ); - - - sd->ulStructTime += InchiTimeElapsed( &ulTStart ); -#if ( bRELEASE_VERSION == 0 ) - sd->bExtract |= orig_inp_data->bExtract; -#endif - } else { - /* read the next original structure */ - int nStructReadError=0; - if ( !ip->bMergeAllInputStructures ) { - nRet2 = CmlfileToOrigAtom( inp_file->f, NULL, 0, 0, 0, inp_index, out_index, - NULL, NULL, NULL, &nStructReadError, NULL ); - - if ( nRet2 <= 0 && 10 < nStructReadError && nStructReadError < 20 ) { - return _IS_EOF; - } - } else { - return _IS_EOF; - } - } - break; -#endif - - default: - nRet = _IS_FATAL; /* wrong file type */ - } - return nRet; -} -#endif -/*****************************************************************************************************/ -int TreatReadTheStructureErrors( STRUCT_DATA *sd, INPUT_PARMS *ip, int nLogMask, - INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, - INCHI_IOSTREAM *prb_file, /*^^^ was: INCHI_IOSTREAM */ - ORIG_ATOM_DATA *orig_inp_data, long *num_inp, char *pStr, int nStrLen ) -{ - int nRet = _IS_OKAY; - /* End of file */ - if ( 10 < sd->nStructReadError && sd->nStructReadError < 20 ) { - if ( sd->pStrErrStruct[0] ) { - inchi_ios_eprint( log_file, "%s inp structure #%ld: End of file.%s%s%s%s \n", sd->pStrErrStruct, *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - } - inchi_ios_eprint( log_file, "End of file detected after structure #%ld. \n", *num_inp-1 ); - nRet = _IS_EOF; - goto exit_function; /* end of file */ - } - - /*(*num_inp) ++;*/ - - /* Skipping the structures */ - if ( *num_inp < ip->first_struct_number ) { - -#if ( !defined(TARGET_API_LIB) && !defined(TARGET_EXE_STANDALONE) ) -/*^^^ #ifndef TARGET_API_LIB */ - if ( log_file->f != stderr ) { - inchi_fprintf( stderr, "\rSkipping structure #%ld.%s%s%s%s...", *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue)); - } -#endif - nRet = sd->nErrorType = _IS_SKIP; - goto exit_function; - } - - sd->nErrorType = GetInpStructErrorType( ip, sd->nStructReadError, sd->pStrErrStruct, orig_inp_data->num_inp_atoms ); - - /* init xml output */ - if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) && !ip->bXmlStarted ) { - OutputINChIXmlRootStartTag( output_file ); - ip->bXmlStarted ++; - } - /* init xml structure block */ - if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) && !sd->bXmlStructStarted ) { - if ( !OutputINChIXmlStructStartTag( output_file, pStr, 1, nStrLen, ip->bNoStructLabels, - *num_inp, ip->pSdfLabel, ip->pSdfValue ) ) { - inchi_ios_eprint( log_file, "Cannot create start xml tag for structure #%ld.%s%s%s%s Terminating.\n", *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - sd->bXmlStructStarted = -1; - nRet = _IS_FATAL; - goto exit_function; - } - sd->bXmlStructStarted ++; - } - - /* Fatal error */ - if ( sd->nErrorType == _IS_FATAL ) { - if ( nLogMask & LOG_MASK_FATAL ) - inchi_ios_eprint( log_file, "Fatal Error %d (aborted; %s) inp structure #%ld.%s%s%s%s\n", - sd->nStructReadError, sd->pStrErrStruct, *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); -#if ( bRELEASE_VERSION == 1 || EXTR_FLAGS == 0 ) - if ( prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd && !ip->bSaveAllGoodStructsAsProblem ) { - CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, *num_inp); - } -#endif - /* goto exit_function; */ - } - /* Non-fatal errors: do not produce INChI */ - if ( sd->nErrorType == _IS_ERROR ) { /* 70 => too many atoms */ - if ( nLogMask & LOG_MASK_ERR ) - inchi_ios_eprint( log_file, "Error %d (no %s; %s) inp structure #%ld.%s%s%s%s\n", - sd->nStructReadError, (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY)?"Molfile":INCHI_NAME, - sd->pStrErrStruct, *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); -#if ( bRELEASE_VERSION == 1 || EXTR_FLAGS == 0 ) - if ( prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd && !ip->bSaveAllGoodStructsAsProblem) { - CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, *num_inp); - } -#endif - } - - /* Warnings: try to produce INChI */ - if ( sd->nErrorType == _IS_WARNING ) { - if ( nLogMask & LOG_MASK_WARN ) - inchi_ios_eprint( log_file, "Warning: (%s) inp structure #%ld.%s%s%s%s\n", - sd->pStrErrStruct, *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - } - - /* xml error/warning processing; close xml struct block if error */ - if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) -#ifdef TARGET_LIB_FOR_WINCHI - || (ip->bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW) && (ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) -#endif - ) { - if ( sd->nErrorType != _IS_OKAY && sd->nErrorType != _IS_WARNING ) { - sd->nErrorType = - ProcessStructError( output_file, log_file, /*sd->nStructReadError,*/ - sd->pStrErrStruct, sd->nErrorType, &sd->bXmlStructStarted, *num_inp, ip, pStr, nStrLen ); - } - } -exit_function: - if ( nRet <= _IS_OKAY && sd->nErrorType > 0 ) { - nRet = sd->nErrorType; - } - return nRet; -} -/******************************************************************************************************/ -int GetOneComponent( STRUCT_DATA *sd, INPUT_PARMS *ip, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, - INP_ATOM_DATA *inp_cur_data, - ORIG_ATOM_DATA *orig_inp_data, int i, long num_inp, char *pStr, int nStrLen ) -{ - inchiTime ulTStart; - InchiTimeGet( &ulTStart ); - CreateInpAtomData( inp_cur_data, orig_inp_data->nCurAtLen[i], 0 ); - inp_cur_data->num_at = ExtractConnectedComponent( orig_inp_data->at, orig_inp_data->num_inp_atoms, i+1, inp_cur_data->at ); - sd->ulStructTime += InchiTimeElapsed( &ulTStart ); - - /* error processing */ - if ( inp_cur_data->num_at <= 0 || orig_inp_data->nCurAtLen[i] != inp_cur_data->num_at ) { - /* log error message */ - AddMOLfileError(sd->pStrErrStruct, "Cannot extract Component"); - inchi_ios_eprint( log_file, "%s #%d structure #%ld.%s%s%s%s\n", sd->pStrErrStruct, i+1, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue)); - sd->nErrorCode = inp_cur_data->num_at < 0? inp_cur_data->num_at : (orig_inp_data->nCurAtLen[i] != inp_cur_data->num_at)? CT_ATOMCOUNT_ERR : CT_UNKNOWN_ERR; - /* num_err ++; */ - sd->nErrorType = _IS_ERROR; - if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) -#ifdef TARGET_LIB_FOR_WINCHI - || (ip->bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW) && (ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) -#endif - ) { - /* xml error message */ - sd->nErrorType = ProcessStructError( output_file, log_file, /*sd->nErrorCode,*/ sd->pStrErrStruct, - sd->nErrorType, &sd->bXmlStructStarted, num_inp, ip, pStr, nStrLen ); - } - } - return sd->nErrorType; -} -/*******************************************************************************************/ -int GetProcessingWarningsOneINChI(INChI *pINChI, INP_ATOM_DATA *inp_norm_data, char *pStrErrStruct) -{ - int j; - int nAmbiguousStereoAtoms, nAmbiguousStereoBonds; - nAmbiguousStereoAtoms = 0; - nAmbiguousStereoBonds = 0; - - if ( inp_norm_data->at ) { - for ( j = 0; j < pINChI->nNumberOfAtoms; j ++ ) { - if ( inp_norm_data->at[j].bAmbiguousStereo & (AMBIGUOUS_STEREO_ATOM | AMBIGUOUS_STEREO_ATOM_ISO) ) { - nAmbiguousStereoAtoms ++; - } - if ( inp_norm_data->at[j].bAmbiguousStereo & (AMBIGUOUS_STEREO_BOND | AMBIGUOUS_STEREO_BOND_ISO) ) { - nAmbiguousStereoBonds ++; - } - } - if ( nAmbiguousStereoAtoms ) { - AddMOLfileError(pStrErrStruct, "Ambiguous stereo:"); - AddMOLfileError(pStrErrStruct, "center(s)"); - } - if ( nAmbiguousStereoBonds ) { - AddMOLfileError(pStrErrStruct, "Ambiguous stereo:"); - AddMOLfileError(pStrErrStruct, "bond(s)"); - } - } - return (nAmbiguousStereoAtoms || nAmbiguousStereoBonds); -} -/*******************************************************************************************/ -int GetProcessingWarnings(INChI *cur_INChI[], INP_ATOM_DATA **inp_norm_data, STRUCT_DATA *sd) -{ - int i, ret = 0; - for (i = 0; i < TAUT_NUM; i ++ ) { - if ( cur_INChI[i] && cur_INChI[i]->nNumberOfAtoms>0 ) { - ret |= GetProcessingWarningsOneINChI(cur_INChI[i], inp_norm_data[i], sd->pStrErrStruct); - } - } - return ret; -} - -/*******************************************************************************************/ -int CreateOneComponentINChI( STRUCT_DATA *sd, INPUT_PARMS *ip, INP_ATOM_DATA *inp_cur_data, ORIG_ATOM_DATA *orig_inp_data, - PINChI2 *pINChI, PINChI_Aux2 *pINChI_Aux, int iINChI, - int i, long num_inp, INP_ATOM_DATA **inp_norm_data, NORM_CANON_FLAGS *pncFlags, - INCHI_IOSTREAM *log_file ) -{ - inchiTime ulTStart, ulTEnd, *pulTEnd = NULL; - int k, num_at, ret = 0; - int bOrigCoord; - INCHI_MODE bTautFlags = ip->bTautFlags; - INCHI_MODE bTautFlagsDone = (ip->bTautFlagsDone | sd->bTautFlagsDone[INCHI_BAS]); - INChI *cur_INChI[TAUT_NUM]; - INChI_Aux *cur_INChI_Aux[TAUT_NUM]; - long lElapsedTime; - /* - PINChI2 *pINChI = pINChI2[iINChI]; - PINChI_Aux2 *pINChI_Aux = pINChI_Aux2[iINChI]; - */ - InchiTimeGet( &ulTStart ); - bOrigCoord = !(ip->bINChIOutputOptions & (INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO)); - - for ( k = 0; k < TAUT_NUM; k ++ ) { - cur_INChI[k] = NULL; - cur_INChI_Aux[k] = NULL; - } - /* allocate memory for non-tautimeric (k=0) and tautomeric (k=1) results */ - for ( k = 0; k < TAUT_NUM; k ++ ) { - int nAllocMode = (k==TAUT_YES? REQ_MODE_TAUT:0) | - (bTautFlagsDone & ( TG_FLAG_FOUND_ISOTOPIC_H_DONE | - TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE ))? - (ip->nMode & REQ_MODE_ISO):0; - - if ( k==TAUT_NON && (ip->nMode & REQ_MODE_BASIC ) || - k==TAUT_YES && (ip->nMode & REQ_MODE_TAUT ) ) { - /* alloc INChI and INChI_Aux */ - cur_INChI[k] = Alloc_INChI( inp_cur_data->at, inp_cur_data->num_at, &inp_cur_data->num_bonds, - &inp_cur_data->num_isotopic, nAllocMode ); - cur_INChI_Aux[k] = Alloc_INChI_Aux( inp_cur_data->num_at, - inp_cur_data->num_isotopic, nAllocMode, bOrigCoord ); - if ( cur_INChI_Aux[k] ) { - cur_INChI_Aux[k]->bIsIsotopic = inp_cur_data->num_isotopic; - } - /* alloc memory for the output structure: non-tautomeric and tautomeric (for displaying) */ - CreateInpAtomData( inp_norm_data[k], inp_cur_data->num_at, k ); - } else { - FreeInpAtomData( inp_norm_data[k] ); - } - } - lElapsedTime = InchiTimeElapsed( &ulTStart ); - if ( ip->msec_MaxTime ) { - ip->msec_LeftTime -= lElapsedTime; - } - sd->ulStructTime += lElapsedTime; - - -/*^^^#if ( !defined( TARGET_LIB_FOR_WINCHI ) && !defined( TARGET_API_LIB ) ) */ -#if ( !defined( TARGET_LIB_FOR_WINCHI ) && !defined( TARGET_API_LIB ) && !defined(TARGET_EXE_STANDALONE) ) -#if ( TEST_RENUMB_ATOMS != 1 ) - /* log file / console output */ - if ( log_file->f && log_file->f != stderr ) { /* NULL log_file now ignored. 11-23-2005 */ - if ( ip->bDisplay ) - inchi_ios_eprint( log_file, "Component #%d structure #%ld.%s%s%s%s...\n", i+1, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - else - inchi_fprintf( stderr, "Component #%d structure #%ld.%s%s%s%s...\r", i+1, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - } -#endif -#endif - /****************************************************** - * - * Get one component canonical numberings, etc. - * - ******************************************************/ - - /* - * Create_INChI() return value: - * num_at <= 0: error code - * num_at > 0: number of atoms (excluding terminal hydrogen atoms) - * inp_norm_data[0] => non-tautomeric, inp_norm_data[1] => tautomeric - */ - InchiTimeGet( &ulTStart ); - if ( ip->msec_MaxTime ) { - ulTEnd = ulTStart; - pulTEnd = &ulTEnd; - if ( ip->msec_LeftTime > 0 ) { - InchiTimeAddMsec( pulTEnd, ip->msec_LeftTime ); - } - } - num_at = Create_INChI( cur_INChI, cur_INChI_Aux, orig_inp_data/* not used */, inp_cur_data->at, - inp_norm_data, - inp_cur_data->num_at, - ip->nMode, &bTautFlags, &bTautFlagsDone, pulTEnd, NULL, sd->pStrErrStruct); - SetConnectedComponentNumber( inp_cur_data->at, inp_cur_data->num_at, i+1 ); /* normalization alters structure component number */ - for ( k = 0; k < TAUT_NUM; k ++ ) { - if ( cur_INChI_Aux[k] && cur_INChI_Aux[k]->nNumberOfAtoms > 0 ) { - pncFlags->bNormalizationFlags[iINChI][k] |= cur_INChI_Aux[k]->bNormalizationFlags; - pncFlags->bTautFlags[iINChI][k] |= cur_INChI_Aux[k]->bTautFlags; - pncFlags->bTautFlagsDone[iINChI][k] |= cur_INChI_Aux[k]->bTautFlagsDone; - pncFlags->nCanonFlags[iINChI][k] |= cur_INChI_Aux[k]->nCanonFlags; - } - } - - /* Detect errors */ - if ( num_at < 0 ) { - sd->nErrorCode = num_at; - } else - if ( num_at == 0 ) { - sd->nErrorCode = -1; - } else - if ( cur_INChI[TAUT_NON] && cur_INChI[TAUT_NON]->nErrorCode ) { - /* non-tautomeric error */ - sd->nErrorCode = cur_INChI[TAUT_NON]->nErrorCode; - } else - if ( cur_INChI[TAUT_YES] && cur_INChI[TAUT_YES]->nErrorCode ) { - /* tautomeric error */ - sd->nErrorCode = cur_INChI[TAUT_YES]->nErrorCode; - } -#if ( bRELEASE_VERSION == 0 ) - if ( cur_INChI[TAUT_NON] ) sd->bExtract |= cur_INChI[TAUT_NON]->bExtract; - if ( cur_INChI[TAUT_YES] ) sd->bExtract |= cur_INChI[TAUT_YES]->bExtract; - if ( (TG_FLAG_TEST_TAUT3_SALTS_DONE & bTautFlagsDone) ) { - sd->bExtract |= EXTR_TEST_TAUT3_SALTS_DONE; - } -#endif - /* detect and store stereo warnings */ - if ( !sd->nErrorCode ) { - GetProcessingWarnings(cur_INChI, inp_norm_data, sd); - } - - lElapsedTime = InchiTimeElapsed( &ulTStart ); - if ( ip->msec_MaxTime ) { - ip->msec_LeftTime -= lElapsedTime; - } - sd->ulStructTime += lElapsedTime; -#ifndef TARGET_API_LIB - /* Display the results */ - if ( ip->bDisplay ) - eat_keyboard_input(); -#endif - /* a) No matter what happened save the allocated INChI pointers */ - /* save the INChI of the current component */ - - InchiTimeGet( &ulTStart ); - for ( k = 0; k < TAUT_NUM; k ++ ) { - pINChI[i][k] = cur_INChI[k]; - pINChI_Aux[i][k] = cur_INChI_Aux[k]; - - cur_INChI[k] = NULL; - cur_INChI_Aux[k] = NULL; - } - - /* b) Count one component structure and/or INChI results only if there was no error */ - /* Set inp_norm_data[j]->num_removed_H = number of removed explicit H */ - - if ( !sd->nErrorCode ) { - - /* find where the current processed structure is located */ - int cur_is_in_non_taut = (pINChI[i][TAUT_NON] && pINChI[i][TAUT_NON]->nNumberOfAtoms>0); - int cur_is_in_taut = (pINChI[i][TAUT_YES] && pINChI[i][TAUT_YES]->nNumberOfAtoms>0); - int cur_is_non_taut = cur_is_in_non_taut && 0 == pINChI[i][TAUT_NON]->lenTautomer || - cur_is_in_taut && 0 == pINChI[i][TAUT_YES]->lenTautomer; - int cur_is_taut = cur_is_in_taut && 0 < pINChI[i][TAUT_YES]->lenTautomer; - /* - sd->bTautFlags[iINChI] |= bTautFlags; - sd->bTautFlagsDone[iINChI] |= bTautFlagsDone; - */ - if ( cur_is_non_taut + cur_is_taut ) { - /* count tautomeric and non-tautomeric components of the structures */ - int j1 = cur_is_in_non_taut? TAUT_NON:TAUT_YES; - int j2 = cur_is_in_taut? TAUT_YES:TAUT_NON; - int j; - sd->num_non_taut[iINChI] += cur_is_non_taut; - sd->num_taut[iINChI] += cur_is_taut; - for ( j = j1; j <= j2; j ++ ) { - int bIsotopic = (pINChI[i][j]->nNumberOfIsotopicAtoms || - pINChI[i][j]->nNumberOfIsotopicTGroups || - pINChI[i][j]->nPossibleLocationsOfIsotopicH && pINChI[i][j]->nPossibleLocationsOfIsotopicH[0]>1); - if ( j == TAUT_YES ) { - bIsotopic |= (0 < pINChI_Aux[i][j]->nNumRemovedIsotopicH[0] + - pINChI_Aux[i][j]->nNumRemovedIsotopicH[1] + - pINChI_Aux[i][j]->nNumRemovedIsotopicH[2]); - } - inp_norm_data[j]->bExists = 1; /* j=0: non-taut exists, j=1: taut exists */ - inp_norm_data[j]->bHasIsotopicLayer = bIsotopic; - /*inp_norm_data[j]->num_removed_H = inp_norm_data[j]->num_at - num_at;*/ - } - } - } -/* - return (sd->nErrorCode==CT_OUT_OF_RAM || sd->nErrorCode==CT_USER_QUIT_ERR)? _IS_FATAL : - sd->nErrorCode? _IS_ERROR : 0; -*/ - if ( sd->nErrorCode==CT_OUT_OF_RAM || sd->nErrorCode==CT_USER_QUIT_ERR ) { - ret = _IS_FATAL; - } else - if ( sd->nErrorCode ) { - ret = _IS_ERROR; - } - lElapsedTime = InchiTimeElapsed( &ulTStart ); - if ( ip->msec_MaxTime ) { - ip->msec_LeftTime -= lElapsedTime; - } - sd->ulStructTime += lElapsedTime; - return ret; -} -/****************************************************************************************************/ -int TreatCreateOneComponentINChIError(STRUCT_DATA *sd, INPUT_PARMS *ip, ORIG_ATOM_DATA *orig_inp_data, - int i, long num_inp, - INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, - INCHI_IOSTREAM *prb_file, /*^^^ was: INCHI_IOSTREAM */ - char *pStr, int nStrLen ) -{ - if ( sd->nErrorCode ) { - AddMOLfileError(sd->pStrErrStruct, ErrMsg(sd->nErrorCode) ); - inchi_ios_eprint( log_file, "Error %d (%s) structure #%ld component %d.%s%s%s%s\n", - sd->nErrorCode, sd->pStrErrStruct, num_inp, i+1, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - sd->nErrorType = (sd->nErrorCode==CT_OUT_OF_RAM || sd->nErrorCode==CT_USER_QUIT_ERR)? _IS_FATAL : _IS_ERROR; - if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) -#ifdef TARGET_LIB_FOR_WINCHI - || (ip->bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW) && (ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) -#endif - ) { - sd->nErrorType = ProcessStructError( output_file, log_file, /*sd->nErrorCode,*/ sd->pStrErrStruct, - sd->nErrorType, &sd->bXmlStructStarted, num_inp, ip, pStr, nStrLen ); - /* save the problem structure */ - if ( prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd && !ip->bSaveAllGoodStructsAsProblem ) { - CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, num_inp); - } - } else { - /* save the problem structure */ - if ( sd->nErrorCode && prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd && !ip->bSaveAllGoodStructsAsProblem ) { - CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, num_inp); - } - } - } -/*^^^ #ifndef TARGET_API_LIB */ -#if ( !defined( TARGET_API_LIB ) && !defined(TARGET_EXE_STANDALONE) ) - /* print the logfile record */ - if ( log_file->f && log_file->f != stderr && (sd->ulStructTime >= 1000 || sd->nErrorCode) ) { - fprintf( log_file->f, "%10lu msec structure #%ld.%s%s%s%s (%d component%s, %d atom%s, error=%d).\n", - sd->ulStructTime, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), - orig_inp_data->num_components, orig_inp_data->num_components==1?"":"s", - orig_inp_data->num_inp_atoms, orig_inp_data->num_inp_atoms==1?"":"s", sd->nErrorCode ); - } -#endif - return sd->nErrorType; -} -/****************************************************************************************************/ -int TreatCreateINChIWarning(STRUCT_DATA *sd, INPUT_PARMS *ip, ORIG_ATOM_DATA *orig_inp_data, long num_inp, - INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, - INCHI_IOSTREAM *prb_file, /*^^^ was: INCHI_IOSTREAM */ - char *pStr, int nStrLen ) -{ -#if ( bRELEASE_VERSION == 0 && (EXTR_FLAGS || EXTR_MASK) ) - if ( EXTR_MASK? ((sd->bExtract & EXTR_MASK) == EXTR_FLAGS) : (sd->bExtract & EXTR_FLAGS) ) { - char szMsg[64]; - sprintf( szMsg, "ExtractStruct.code=0x%X", sd->bExtract); - AddMOLfileError(sd->pStrErrStruct, szMsg); - } -#endif - if ( !sd->nErrorCode && sd->pStrErrStruct[0] ) { - inchi_ios_eprint( log_file, "Warning (%s) structure #%ld.%s%s%s%s\n", - sd->pStrErrStruct, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - sd->nErrorType = _IS_WARNING; - if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) -#ifdef TARGET_LIB_FOR_WINCHI - || (ip->bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW) && (ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) -#endif - ) { - sd->nErrorType = ProcessStructError( output_file, log_file, /*sd->nErrorCode,*/ sd->pStrErrStruct, - sd->nErrorType, &sd->bXmlStructStarted, num_inp, ip, pStr, nStrLen ); - } - /* save the structure as a problem structure if requested */ - if ( ip->bSaveWarningStructsAsProblem && !ip->bSaveAllGoodStructsAsProblem && - prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd ) { - CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, num_inp); - } -#if ( bRELEASE_VERSION == 0 ) - /* otherwise extract the structure as a problem structure if requested */ - else - if ( (EXTR_MASK? ((sd->bExtract & EXTR_MASK) == EXTR_FLAGS) : (sd->bExtract & EXTR_FLAGS)) && !ip->bSaveAllGoodStructsAsProblem && - prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd ) { - CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, num_inp); - } -#endif - } -#if ( bRELEASE_VERSION != 1 && bOUTPUT_ONE_STRUCT_TIME == 1 ) -#ifndef TARGET_API_LIB - if ( log_file && log_file != stderr ) { - fprintf( log_file, "%10lu msec structure %1dD #%ld.%s%s%s%s (%d component%s, %d atom%s, error=%d).\n", - sd->ulStructTime, orig_inp_data->num_dimensions, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), - orig_inp_data->num_components, orig_inp_data->num_components==1?"":"s", - orig_inp_data->num_inp_atoms, orig_inp_data->num_inp_atoms==1?"":"s", sd->nErrorCode ); - } -#else - if ( log_file ) { - inchi_ios_eprint( log_file, "%10lu msec structure %1dD #%ld.%s%s%s%s (%d component%s, %d atom%s, error=%d).\n", - sd->ulStructTime, orig_inp_data->num_dimensions, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), - orig_inp_data->num_components, orig_inp_data->num_components==1?"":"s", - orig_inp_data->num_inp_atoms, orig_inp_data->num_inp_atoms==1?"":"s", sd->nErrorCode ); - } -#endif -#endif - return sd->nErrorType; -} -/*******************************************************************************************/ -int DuplicateOrigAtom( ORIG_ATOM_DATA *new_orig_atom, ORIG_ATOM_DATA *orig_atom ) -{ - inp_ATOM *at = NULL; - AT_NUMB *nCurAtLen = NULL; - AT_NUMB *nOldCompNumber = NULL; - - if ( new_orig_atom->at && new_orig_atom->num_inp_atoms >= orig_atom->num_inp_atoms ) { - at = new_orig_atom->at; - } else { - at = (inp_ATOM *)inchi_calloc(orig_atom->num_inp_atoms+1, sizeof(at[0])); - } - if ( new_orig_atom->nOldCompNumber && new_orig_atom->num_components >= orig_atom->num_components ) { - nCurAtLen = new_orig_atom->nCurAtLen; - } else { - nCurAtLen = (AT_NUMB *)inchi_calloc(orig_atom->num_components+1, sizeof(nCurAtLen[0])); - } - if ( new_orig_atom->nCurAtLen && new_orig_atom->num_components >= orig_atom->num_components ) { - nOldCompNumber = new_orig_atom->nOldCompNumber; - } else { - nOldCompNumber = (AT_NUMB *)inchi_calloc(orig_atom->num_components+1, sizeof(nOldCompNumber[0])); - } - - if ( at && nCurAtLen && nOldCompNumber ) { - /* copy */ - if ( orig_atom->at ) - memcpy( at, orig_atom->at, orig_atom->num_inp_atoms * sizeof(new_orig_atom->at[0]) ); - if ( orig_atom->nCurAtLen ) - memcpy( nCurAtLen, orig_atom->nCurAtLen, orig_atom->num_components*sizeof(nCurAtLen[0]) ); - if ( orig_atom->nOldCompNumber ) - memcpy( nOldCompNumber, orig_atom->nOldCompNumber, orig_atom->num_components*sizeof(nOldCompNumber[0]) ); - /* deallocate */ - if ( new_orig_atom->at && new_orig_atom->at != at ) - inchi_free( new_orig_atom->at ); - if ( new_orig_atom->nCurAtLen && new_orig_atom->nCurAtLen != nCurAtLen ) - inchi_free( new_orig_atom->nCurAtLen ); - if ( new_orig_atom->nOldCompNumber && new_orig_atom->nOldCompNumber != nOldCompNumber ) - inchi_free( new_orig_atom->nOldCompNumber ); - - *new_orig_atom = *orig_atom; - new_orig_atom->at = at; - new_orig_atom->nCurAtLen = nCurAtLen; - new_orig_atom->nOldCompNumber = nOldCompNumber; - /* data that are not to be copied */ - new_orig_atom->nNumEquSets = 0; - memset(new_orig_atom->bSavedInINCHI_LIB, 0, sizeof(new_orig_atom->bSavedInINCHI_LIB)); - memset(new_orig_atom->bPreprocessed, 0, sizeof(new_orig_atom->bPreprocessed)); - /* arrays that are not to be copied */ - new_orig_atom->szCoord = NULL; - new_orig_atom->nEquLabels = NULL; - new_orig_atom->nSortedOrder = NULL; - return 0; - } - - /* deallocate */ - if ( at && new_orig_atom->at != at ) - inchi_free( at ); - if ( nCurAtLen && new_orig_atom->nCurAtLen != nCurAtLen ) - inchi_free( nCurAtLen ); - if ( nOldCompNumber && new_orig_atom->nOldCompNumber != nOldCompNumber ) - inchi_free( nOldCompNumber ); - - return -1; /* failed */ -} -#ifndef TARGET_API_LIB -/*******************************************************************************************/ -int GetOneStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, - INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, - INCHI_IOSTREAM *prb_file, /*^^^ was: INCHI_IOSTREAM */ - ORIG_ATOM_DATA *orig_inp_data, long *num_inp, char *pStr, int nStrLen, STRUCT_FPTRS *struct_fptrs ) -{ - int nRet, inp_index, out_index, bUseFptr = (NULL != struct_fptrs); - - FreeOrigAtData( orig_inp_data ); - /* - FreeOrigAtData( orig_inp_data + 1 ); - FreeOrigAtData( orig_inp_data + 2 ); - */ - - /* added for TARGET_LIB_FOR_WINCHI early EOF detection */ - inp_index = -1; - out_index = -1; - if ( struct_fptrs ) { - if ( inp_file->f == stdin ) { - return _IS_FATAL; - } - if ( ip->nInputType == INPUT_CMLFILE ) { - bUseFptr = 0; - } - /* initially allocate or increase length of struct_fptrs->fptr array */ - if ( !struct_fptrs->fptr || struct_fptrs->len_fptr <= struct_fptrs->cur_fptr+1 ) { - INCHI_FPTR *new_fptr = (INCHI_FPTR *)inchi_calloc( struct_fptrs->len_fptr + ADD_LEN_STRUCT_FPTRS, sizeof(new_fptr[0]) ); - if ( new_fptr ) { - if ( struct_fptrs->fptr ) { - if ( struct_fptrs->len_fptr ) { - memcpy( new_fptr, struct_fptrs->fptr, struct_fptrs->len_fptr*sizeof(new_fptr[0])); - } - inchi_free( struct_fptrs->fptr ); - } else { - struct_fptrs->len_fptr = 0; - struct_fptrs->cur_fptr = 0; - struct_fptrs->max_fptr = 0; - } - struct_fptrs->len_fptr += ADD_LEN_STRUCT_FPTRS; - struct_fptrs->fptr = new_fptr; - } else { - return _IS_FATAL; /* new_fptr allocation error */ - } - } - if ( struct_fptrs->fptr[struct_fptrs->cur_fptr] == EOF ) { - return _IS_EOF; - } else { - if ( bUseFptr ) { - if( fseek( inp_file->f, struct_fptrs->fptr[struct_fptrs->cur_fptr], SEEK_SET) ) { - return _IS_FATAL; - } - if ( struct_fptrs->cur_fptr && struct_fptrs->max_fptr <= struct_fptrs->cur_fptr ) { - return _IS_FATAL; - } - } else { - inp_index = struct_fptrs->fptr[struct_fptrs->cur_fptr]; - out_index = EOF; - } - } - *num_inp = struct_fptrs->cur_fptr; /* set structure count */ - } - - nRet = ReadTheStructure( sd, ip, inp_file, orig_inp_data, inp_index, &out_index ); - - if ( !nRet ) { - /***************************************************** - * In case of no error output structure xml start tag - * output read the structure errors and warnings - *****************************************************/ - if ( ip->nInputType == INPUT_INCHI_PLAIN || ip->nInputType == INPUT_INCHI_XML || - ip->nInputType == INPUT_MOLFILE || ip->nInputType == INPUT_SDFILE) { - if ( ip->lMolfileNumber ) { - *num_inp = ip->lMolfileNumber; - } else { - *num_inp += 1; - } - } else { - *num_inp += 1; - } - nRet = TreatReadTheStructureErrors( sd, ip, LOG_MASK_ALL, inp_file, log_file, output_file, prb_file, - orig_inp_data, num_inp, pStr, nStrLen ); - } - - /************************************************************/ - /* added for TARGET_LIB_FOR_WINCHI: look ahead for end of file detection */ - /************************************************************/ - if ( struct_fptrs && struct_fptrs->fptr && struct_fptrs->fptr[struct_fptrs->cur_fptr+1] <= 0 ) { - int nRet2 = 0; - INCHI_FPTR next_fptr; - STRUCT_DATA sd2; - - if ( nRet != _IS_EOF && nRet != _IS_FATAL ) { - if ( inp_file->f == stdin || struct_fptrs->len_fptr <= struct_fptrs->cur_fptr+1 ) { - return _IS_FATAL; - } - /* get next structure fptr */ - if ( bUseFptr ) { - next_fptr = ftell( inp_file->f ); - } else { - inp_index = out_index; - out_index = EOF; - } - /* read the next structure */ - nRet2 = ReadTheStructure( &sd2, ip, inp_file, NULL, inp_index, &out_index ); - /* restore fptr to the next structure */ - if ( bUseFptr ) { - if ( next_fptr != -1L ) { - fseek( inp_file->f, next_fptr, SEEK_SET); - } - } -#if ( ADD_CMLPP == 1 ) - else { - if ( inp_index >= 0 ) { - SetCmlStructIndex( inp_index ); /* so far nothing to do */ - } - } -#endif - } else { - /* treat current fatal error as end of file */ - struct_fptrs->fptr[struct_fptrs->cur_fptr] = EOF; - } - /* next is end of file or fatal */ - if ( nRet == _IS_EOF || nRet == _IS_FATAL || - nRet2 == _IS_EOF || nRet2 == _IS_FATAL ) { - struct_fptrs->fptr[struct_fptrs->cur_fptr+1] = EOF; - } else { - struct_fptrs->fptr[struct_fptrs->cur_fptr+1] = bUseFptr? sd->fPtrEnd : inp_index; - } - - /* update struct_fptrs->max_fptr */ - if ( struct_fptrs->max_fptr <= struct_fptrs->cur_fptr+1 ) { - struct_fptrs->max_fptr = struct_fptrs->cur_fptr+2; - } - } - - switch ( nRet ) { - case _IS_EOF: - *num_inp -= 1; - case _IS_FATAL: - case _IS_ERROR: - case _IS_SKIP: - goto exit_function; - } - - /* - if ( !orig_inp_data->num_dimensions ) { - AddMOLfileError(sd->pStrErrStruct, "0D"); */ /* 0D-structure: no coordinates - } - */ - - -exit_function: - return nRet; -} -#endif -#if ( TEST_RENUMB_ATOMS == 1 ) /* { */ -/************************************************************************************************/ -int RenumberingTestInit( RENUMB_DATA *pRenumbData, INP_ATOM_DATA *inp_cur_data ) -{ - int j; - pRenumbData->ren_inp_norm_data[0] = &pRenumbData->ren_inp_norm_data1; - pRenumbData->ren_inp_norm_data[1] = &pRenumbData->ren_inp_norm_data2; - memset( pRenumbData->ren_INChI2, 0, sizeof( pRenumbData->ren_INChI2 )); - memset( pRenumbData->ren_INChI_Aux, 0, sizeof( pRenumbData->ren_INChI_Aux )); - memset( &pRenumbData->orig_inp_cur_data, 0, sizeof( pRenumbData->orig_inp_cur_data )); - memset( &pRenumbData->saved_inp_cur_data, 0, sizeof( pRenumbData->saved_inp_cur_data )); - memset( pRenumbData->ren_inp_norm_data[0], 0, sizeof( *pRenumbData->ren_inp_norm_data[0] )); - memset( pRenumbData->ren_inp_norm_data[1], 0, sizeof( *pRenumbData->ren_inp_norm_data[1] )); -#if ( TEST_RENUMB_ATOMS_SAVE_LONGEST == 1 ) - memset( &pRenumbData->longest_inp_cur_data, 0, sizeof(pRenumbData->longest_inp_cur_data)); -#endif - CopyInpAtomData( &pRenumbData->orig_inp_cur_data, inp_cur_data ); - pRenumbData->ren_counter = pRenumbData->orig_inp_cur_data.num_at * pRenumbData->orig_inp_cur_data.num_at; - srand(1); /* for reproducibility */ - rand(); /* shift to avoid prev. sequences */ - pRenumbData->nComp = 0; - /*ren_counter = 29;*/ - pRenumbData->new_ord = (AT_RANK *)inchi_calloc( pRenumbData->orig_inp_cur_data.num_at, sizeof(pRenumbData->new_ord[0]) ); - if ( pRenumbData->new_ord ) { - for ( j = 0; j < pRenumbData->orig_inp_cur_data.num_at; j ++ ) { - pRenumbData->new_ord[j] = (AT_RANK)j; - } - return 0; - } - return -1; /* out of RAM */ -} -/************************************************************************************************/ -int RenumberingTestUninit( RENUMB_DATA *pRenumbData ) -{ - FreeInpAtomData( &pRenumbData->orig_inp_cur_data ); -#if ( TEST_RENUMB_ATOMS_SAVE_LONGEST == 1 ) - FreeInpAtomData( &pRenumbData->longest_inp_cur_data ); -#endif - inchi_free( pRenumbData->new_ord ); - return 0; -} -/************************************************************************************************/ -int RenumberingTest( PINChI2 *pINChI, PINChI_Aux2 *pINChI_Aux, ORIG_ATOM_DATA *orig_inp_data, int iINChI, - RENUMB_DATA *pRenumbData, INP_ATOM_DATA *inp_cur_data, INP_ATOM_DATA **inp_norm_data, - STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *prb_file, - int i, long num_inp, NORM_CANON_FLAGS *pncFlags) -{ - int k, bLongerTime; - CopyInpAtomData( &pRenumbData->saved_inp_cur_data, inp_cur_data ); - pRenumbData->nRet2 = 0; - pRenumbData->num_taut0 = sd->num_taut[iINChI]; - pRenumbData->num_non_taut0 = sd->num_non_taut[iINChI]; - pRenumbData->ulMaxTime = 0; - while ( -- pRenumbData->ren_counter >= 0 && !pRenumbData->nRet2 ) { - pRenumbData->nComp ++; - MakeNewOrd( pRenumbData->orig_inp_cur_data.num_at, pRenumbData->new_ord ); - RenumbInpAtomData( inp_cur_data /* output*/, &pRenumbData->orig_inp_cur_data/* input*/, pRenumbData->new_ord/* input*/ ); -#if ( TEST_RENUMB_ATOMS_SAVE_LONGEST == 1 ) - CopyInpAtomData( &pRenumbData->longest_inp_cur_data, inp_cur_data ); -#endif - if ( 470 == pRenumbData->nComp ) { - int stop = 1; /* debug only */ - } - - pRenumbData->nRet2 = CreateOneComponentINChI( sd, ip, inp_cur_data, NULL /*orig_inp_data*/, - pRenumbData->ren_INChI2, pRenumbData->ren_INChI_Aux, iINChI, - 0, num_inp, pRenumbData->ren_inp_norm_data, pncFlags, log_file ); - /* - CreateOneComponentINChI( sd, ip, inp_cur_data, orig_inp_data, pINChI2[iINChI], pINChI_Aux2[iINChI], iINChI, - i, num_inp, inp_norm_data, log_file ); - */ - if ( !pRenumbData->nRet2 ) { - pRenumbData->c1 = CompareINChI( pINChI[i][TAUT_NON], pRenumbData->ren_INChI2[0][TAUT_NON], - pINChI_Aux[i][TAUT_NON], pRenumbData->ren_INChI_Aux[0][TAUT_NON]); - pRenumbData->c2 = CompareINChI( pINChI[i][TAUT_YES], pRenumbData->ren_INChI2[0][TAUT_YES], - pINChI_Aux[i][TAUT_YES], pRenumbData->ren_INChI_Aux[0][TAUT_YES]); - if ( pRenumbData->c1 || pRenumbData->c2 || pRenumbData->nRet2 ) { - - /****** the renumbering result is different ******/ - - inchi_ios_eprint( log_file, "Compare (%d,%d) %d (err=%d) %s structure #%d component %d.%s%s%s%s\n", - pRenumbData->c1, pRenumbData->c2, - pRenumbData->nComp, pRenumbData->nRet2, INCHI_NAME, - num_inp, i+1, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - for ( k = 0; k < pRenumbData->orig_inp_cur_data.num_at; k ++ ) { - inchi_ios_eprint( log_file, " %d", (int)pRenumbData->new_ord[k] ); - } - inchi_ios_eprint( log_file, "\n" ); - pRenumbData->ren_counter = 0; /* force exit */ - pRenumbData->bRenumbErr = 1000*pRenumbData->c2 + pRenumbData->c1; -#if ( TEST_RENUMB_SWITCH == 1 ) - CopyInpAtomData( &pRenumbData->longest_inp_cur_data, inp_cur_data ); - if ( pRenumbData->longest_inp_cur_data.at ) { - for ( k = 0; k < pRenumbData->longest_inp_cur_data.num_at; k ++ ) { - pRenumbData->longest_inp_cur_data.at[k].orig_at_number = k+1; /* display new atom numbers */ - } - } -#endif - } -#if ( TEST_RENUMB_ATOMS_SAVE_LONGEST == 1 ) - /* output time per this component */ - inchi_ios_eprint( stderr, "\rComp#%d str#%ld/%d%s%s%s%s Ren %d/%d n(%lu:%lu)c(%lu:%lu)...\r", - i+1, num_inp, iINChI, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), pRenumbData->nComp, pRenumbData->ren_counter+pRenumbData->nComp, - pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulNormTime, pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulCanonTime, - pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulNormTime, pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulCanonTime); -#endif - /* make sure the max. time is not overwritten */ - pRenumbData->ulCurTime0 = pRenumbData->ren_INChI_Aux[0][TAUT_NON]? - (pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulNormTime - + pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulCanonTime) : 0; - pRenumbData->ulCurTime1 = pRenumbData->ren_INChI_Aux[0][TAUT_YES]? - (pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulNormTime - + pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulCanonTime) : 0; - pRenumbData->ulCurTime = inchi_max( pRenumbData->ulCurTime0, pRenumbData->ulCurTime1 ); - - pRenumbData->ulCurTimeCanon0 = pRenumbData->ren_INChI_Aux[0][TAUT_NON]? pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulCanonTime : 0; - pRenumbData->ulCurTimeCanon1 = pRenumbData->ren_INChI_Aux[0][TAUT_YES]? pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulCanonTime : 0; - pRenumbData->ulCurTimeCanon = inchi_max( pRenumbData->ulCurTimeCanon0, pRenumbData->ulCurTimeCanon1); - - pRenumbData->ulCurTimeNorm0 = pRenumbData->ren_INChI_Aux[0][TAUT_NON]? pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulNormTime:0; - pRenumbData->ulCurTimeNorm1 = pRenumbData->ren_INChI_Aux[0][TAUT_YES]? pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulNormTime:0; - pRenumbData->ulCurTimeNorm = inchi_max( pRenumbData->ulCurTimeNorm0, pRenumbData->ulCurTimeNorm1); - - - bLongerTime = 0; - if ( pRenumbData->ulCurTime > pRenumbData->ulMaxTime ) { - pRenumbData->ulMaxTime = pRenumbData->ulCurTime; - bLongerTime = 1; - } - if ( pRenumbData->ulMaxTimeCanon > pRenumbData->ulCurTimeCanon ) { - pRenumbData->ulMaxTimeCanon = pRenumbData->ulCurTimeCanon; - bLongerTime = 1; - } - if ( pRenumbData->ulMaxTimeNorm > pRenumbData->ulCurTimeCanon ) { - pRenumbData->ulMaxTimeCanon = pRenumbData->ulCurTimeCanon; - bLongerTime = 1; - } -#if ( TEST_RENUMB_ATOMS_SAVE_LONGEST == 1 || TEST_RENUMB_SWITCH == 1 ) - if ( bLongerTime || TEST_RENUMB_SWITCH == 1 && (pRenumbData->c1 || pRenumbData->c2 || pRenumbData->nRet2) ) { - char szLine[512]; - char szValue[512]; - inchi_ios_eprint( stderr, "\n" ); - sprintf( szLine, "Comp#%d str#%ld/%d%s%s%s%s Ren %d/%d n=%lu:%lu c=%lu:%lu", - i+1, num_inp, iINChI, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), pRenumbData->nComp, pRenumbData->ren_counter+pRenumbData->nComp, - pRenumbData->ren_INChI_Aux[0][TAUT_NON]? pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulNormTime:0, - pRenumbData->ren_INChI_Aux[0][TAUT_NON]? pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulCanonTime:0, - pRenumbData->ren_INChI_Aux[0][TAUT_YES]? pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulNormTime:0, - pRenumbData->ren_INChI_Aux[0][TAUT_YES]? pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulCanonTime:0); - sprintf( szValue, "%s (c%d/s%ld/i%d, r%d/%d n=%lu:%lu c=%lu:%lu)", - (ip->pSdfValue && ip->pSdfValue[0])? ip->pSdfValue:"unk", - i+1, num_inp, iINChI, pRenumbData->nComp, pRenumbData->ren_counter+pRenumbData->nComp, - pRenumbData->ren_INChI_Aux[0][TAUT_NON]? pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulNormTime:0, - pRenumbData->ren_INChI_Aux[0][TAUT_NON]? pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulCanonTime:0, - pRenumbData->ren_INChI_Aux[0][TAUT_YES]? pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulNormTime:0, - pRenumbData->ren_INChI_Aux[0][TAUT_YES]? pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulCanonTime:0); - - WriteToSDfile( &pRenumbData->longest_inp_cur_data, prb_file, szLine, NULL, ip->pSdfLabel, szValue ); - } -#endif - -#if ( TEST_RENUMB_SWITCH == 1 ) - if ( pRenumbData->c1 || pRenumbData->c2 || !pRenumbData->ren_counter ) { - inchi_swap( (char*)&pINChI[i][TAUT_NON], (char*)&pRenumbData->ren_INChI2[0][TAUT_NON], sizeof(&pRenumbData->ren_INChI2[0][0]) ); - inchi_swap( (char*)&pINChI[i][TAUT_YES], (char*)&pRenumbData->ren_INChI2[0][TAUT_YES], sizeof(&pRenumbData->ren_INChI2[0][0]) ); - inchi_swap( (char*)&pINChI_Aux[i][TAUT_NON], (char*)&pRenumbData->ren_INChI_Aux[0][TAUT_NON], sizeof(&pRenumbData->ren_INChI_Aux[0][0]) ); - inchi_swap( (char*)&pINChI_Aux[i][TAUT_YES], (char*)&pRenumbData->ren_INChI_Aux[0][TAUT_YES], sizeof(&pRenumbData->ren_INChI_Aux[0][0]) ); - } -#endif - } - - for ( k = 0; k < TAUT_NUM; k ++ ) { - if ( pRenumbData->ren_INChI2[0][k] ) { - Free_INChI(&pRenumbData->ren_INChI2[0][k]); - /* - inchi_free(pRenumbData->ren_INChI2[0][k]); - pRenumbData->ren_INChI2[0][k] = NULL; - */ - } - if ( pRenumbData->ren_INChI_Aux[0][k] ) { - Free_INChI_Aux(&pRenumbData->ren_INChI_Aux[0][k]); - /* - inchi_free(pRenumbData->ren_INChI_Aux[0][k]); - pRenumbData->ren_INChI_Aux[0][k] = NULL; - */ - } - } - } - /* eliminate overcounting due to multiple renumberings/recalculations */ - pRenumbData->num_taut = sd->num_taut[iINChI] - pRenumbData->num_taut0; - pRenumbData->num_non_taut = sd->num_non_taut[iINChI] - pRenumbData->num_non_taut0; - sd->num_taut[iINChI] = pRenumbData->num_taut0; - sd->num_non_taut[iINChI] = pRenumbData->num_non_taut0; - if ( pRenumbData->num_taut % pRenumbData->nComp || pRenumbData->num_non_taut % pRenumbData->nComp ) { - inchi_ios_eprint( log_file, "Compare (%d,%d) %d (err=%d) %s structure #%ld component %d.%s%s%s%s\n", - pRenumbData->num_non_taut % pRenumbData->nComp, pRenumbData->num_taut % pRenumbData->nComp, - pRenumbData->nComp, 333, INCHI_NAME, num_inp, i+1, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - } -#if ( TEST_RENUMB_SWITCH == 1 ) /* { */ - CopyInpAtomData( inp_norm_data[TAUT_NON], pRenumbData->ren_inp_norm_data[TAUT_NON] ); - CopyInpAtomData( inp_norm_data[TAUT_YES], pRenumbData->ren_inp_norm_data[TAUT_YES] ); - /* renumbered input structure */ -#ifndef COMPILE_ANSI_ONLY /* { */ - if ( /*ip->bDisplayEachComponentINChI &&*/ !pRenumbData->nRet2 ) { - int err, len; - /* - err = DisplayStructure( inp_cur_data->at, inp_cur_data->num_at, 0, 1, 0, NULL. 1, 0, NULL, NULL, - ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); - */ - err = DisplayStructure( inp_cur_data->at, inp_cur_data->num_at, 0, 1, 0, NULL, 1/*isotopic*/, 0/*taut*/, NULL, NULL, - ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); - if ( pRenumbData->c1 || pRenumbData->c2 ) { - len = strlen(szTitle); - strcat( szTitle, " (Renumbered)" ); - err = DisplayStructure( pRenumbData->longest_inp_cur_data.at, pRenumbData->longest_inp_cur_data.num_at, - 0, 1, 0, NULL, 1, 0, NULL, NULL, ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); - szTitle[len] = '\0'; - } - sd->bUserQuitComponentDisplay = (err==ESC_KEY); - if ( !err ) { - inchi_ios_eprint( stderr, "Cannot display the structure\n"); - } - } -#endif /* } COMPILE_ANSI_ONLY */ -#else /* } TEST_RENUMB_SWITCH { */ - CopyInpAtomData( inp_cur_data, &pRenumbData->saved_inp_cur_data ); -#endif /* } TEST_RENUMB_SWITCH */ - FreeInpAtomData( &pRenumbData->saved_inp_cur_data ); - FreeInpAtomData( pRenumbData->ren_inp_norm_data[TAUT_NON] ); - FreeInpAtomData( pRenumbData->ren_inp_norm_data[TAUT_YES] ); -#if ( TEST_RENUMB_ATOMS_SAVE_LONGEST == 1 || TEST_RENUMB_SWITCH == 1 ) - FreeInpAtomData( &pRenumbData->longest_inp_cur_data ); -#endif - return pRenumbData->nRet2; -} -#endif /* } TEST_RENUMB_ATOMS */ - -/****************************************************************************/ -int bCheckUnusualValences( ORIG_ATOM_DATA *orig_at_data, int bAddIsoH, char *pStrErrStruct ) -{ - int i, val, num_found = 0; - char msg[32]; - int len, num_H; - inp_ATOM *at = ( orig_at_data && orig_at_data->num_inp_atoms > 0 )? orig_at_data->at : NULL; - - if ( at ) { - for ( i = 0, num_found = 0; i < orig_at_data->num_inp_atoms; i ++ ) { - num_H = bAddIsoH? NUMH(at,i) : at[i].num_H; - val = detect_unusual_el_valence( at[i].el_number, at[i].charge, at[i].radical, - at[i].chem_bonds_valence, num_H, at[i].valence ); - if ( val ) { - num_found ++; - /* produce message */ - AddMOLfileError(pStrErrStruct, "Accepted unusual valence(s):"); - len = sprintf( msg, "%s", at[i].elname ); - if ( at[i].charge ) { - len += sprintf( msg+len, "%+d", at[i].charge ); - } - if ( at[i].radical ) { - len += sprintf( msg + len, ",%s", at[i].radical == RADICAL_SINGLET? "s" : - at[i].radical == RADICAL_DOUBLET? "d" : - at[i].radical == RADICAL_TRIPLET? "t" : "?" ); - } - len += sprintf( msg + len, "(%d)", val ); - AddMOLfileError(pStrErrStruct, msg); - } - } - } - return num_found; -} -/***************************************************************************/ -int PreprocessOneStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data ) -{ - int i; - INCHI_MODE bTautFlags = 0; - INCHI_MODE bTautFlagsDone = 0; - /*************************************************/ - /* 1. copy orig_inp_data --> prep_inp_data */ - /*************************************************/ - if ( 0 > DuplicateOrigAtom( prep_inp_data, orig_inp_data ) ) { - AddMOLfileError(sd->pStrErrStruct, "Out of RAM"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_FATAL; - goto exit_function; - } -#if ( bRELEASE_VERSION == 0 && (EXTR_HAS_METAL_ATOM & (EXTR_MASK | EXTR_FLAG) ) ) - if ( bHasMetalAtom( orig_inp_data ) ) { - sd->bExtract |= EXTR_HAS_METAL_ATOM; - } -#endif - - /*************************************************/ - /* 2. fix odd things in prep_inp_data */ - /*************************************************/ - - if ( 0 < fix_odd_things( prep_inp_data->num_inp_atoms, prep_inp_data->at, /*0*/ip->bTautFlags & TG_FLAG_FIX_SP3_BUG, ip->bFixNonUniformDraw ) ) { /* changed 2010-03-17 DT */ - AddMOLfileError(sd->pStrErrStruct, "Charges were rearranged"); - if ( sd->nErrorType < _IS_WARNING ) { - sd->nErrorType = _IS_WARNING; - } - sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ODD_THINGS_DONE; - } -#if ( FIX_ADJ_RAD == 1 ) - if ( ip->bTautFlags & TG_FLAG_FIX_ADJ_RADICALS ) { - if ( 0 < FixAdjacentRadicals( prep_inp_data->num_inp_atoms, prep_inp_data->at ) ) { - sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ADJ_RADICALS_DONE; - } - } -#endif -#if ( bRELEASE_VERSION == 0 && (EXTR_FLAGS & EXTR_HAS_FEATURE) ) - if ( bFoundFeature( prep_inp_data->at, prep_inp_data->num_inp_atoms ) ) { - sd->bExtract |= EXTR_HAS_FEATURE; - } -#endif - - /******************************************************************* - * Find whether the structure can be disconnected or is a salt - *******************************************************************/ - - - /* needs salt disconnection? */ - if ( ip->bTautFlags & TG_FLAG_DISCONNECT_SALTS ) { - prep_inp_data->bDisconnectSalts = (0 < DisconnectSalts( prep_inp_data, 0 )); - } else { - prep_inp_data->bDisconnectSalts = 0; - } - /* needs metal disconnection? */ - if ( ip->bTautFlags & TG_FLAG_DISCONNECT_COORD ) { - i = (0 != (ip->bTautFlags & TG_FLAG_CHECK_VALENCE_COORD)); - bMayDisconnectMetals( prep_inp_data, i, &bTautFlagsDone ); /* changes prep_inp_data->bDisconnectCoord */ - sd->bTautFlagsDone[INCHI_BAS] |= bTautFlagsDone; /* whether any disconnection has been rejected because of the metal proper valence */ -#if ( bRELEASE_VERSION == 0 ) - if ( i && (bTautFlagsDone & TG_FLAG_CHECK_VALENCE_COORD_DONE) ) { - sd->bExtract |= EXTR_METAL_WAS_NOT_DISCONNECTED; - } -#endif - } else { - prep_inp_data->bDisconnectCoord = 0; - } - orig_inp_data->bDisconnectSalts = prep_inp_data->bDisconnectSalts; - orig_inp_data->bDisconnectCoord = prep_inp_data->bDisconnectCoord; - - /*************************************************/ - /* 3. if( orig_inp_data->bDisconnectSalts ) then */ - /* -- disconnect salts in prep_inp_data */ - /*************************************************/ - - if ( ( ip->bTautFlags & TG_FLAG_DISCONNECT_SALTS ) && prep_inp_data->bDisconnectSalts && - 0 < (i=DisconnectSalts( prep_inp_data, 1 )) ) { - AddMOLfileError(sd->pStrErrStruct, "Salt was disconnected"); - sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_DISCONNECT_SALTS_DONE; - if ( sd->nErrorType < _IS_WARNING ) { - sd->nErrorType = _IS_WARNING; - } - if ( i = ReconcileAllCmlBondParities( prep_inp_data->at, prep_inp_data->num_inp_atoms, 0 ) ) { - char szErrCode[16]; - sprintf( szErrCode, "%d", i); - AddMOLfileError( sd->pStrErrStruct, "0D Parities Reconciliation failed:" ); - AddMOLfileError( sd->pStrErrStruct, szErrCode ); - } -#if ( bRELEASE_VERSION == 0 ) - sd->bExtract |= EXTR_SALT_WAS_DISCONNECTED; -#endif - } else { - prep_inp_data->bDisconnectSalts = 0; - } - - /***********************************************************/ - /* mark the (disconnected) components in prep_inp_data */ - /***********************************************************/ - - prep_inp_data->num_components = MarkDisconnectedComponents( prep_inp_data, 0 ); - - if ( prep_inp_data->num_components < 0 ) { - AddMOLfileError(sd->pStrErrStruct, "Out of RAM"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_FATAL; - goto exit_function; - } - - /***********************************************************/ - /* Detect isotopic H on heteroatoms -- necessary condition */ - /* for global isotopic tautomerism */ - /***********************************************************/ - - if ( i = bNumHeterAtomHasIsotopicH( prep_inp_data->at, prep_inp_data->num_inp_atoms ) ) { - if ( i & 1 ) { - sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FOUND_ISOTOPIC_H_DONE; - } - if ( i & 2 ) { - sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE; - } - } - - - /****************************************************************************/ - /* 4a. Detect unusual valences */ - /* should be called before metal disconnection */ - /****************************************************************************/ - - - if ( bCheckUnusualValences( prep_inp_data, 1, sd->pStrErrStruct ) ) { -#if ( bRELEASE_VERSION == 0 ) - sd->bExtract |= EXTR_UNUSUAL_VALENCES; -#else - ; -#endif - } - /***********************************************************/ - /* 5. if( orig_inp_data->bDisconnectCoord ) then */ - /* -- copy prep_inp_data --> prep_inp_data+1 */ - /* -- disconnect metals in prep_inp_data */ - /***********************************************************/ - - if ( prep_inp_data->bDisconnectCoord ) { - - prep_inp_data->num_components = MarkDisconnectedComponents( prep_inp_data, 0 ); - if ( prep_inp_data->num_components < 0 ) { - AddMOLfileError(sd->pStrErrStruct, "Out of RAM"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_FATAL; - goto exit_function; - } - /* save Reconnected structure in prep_inp_data+1 if requested */ - if ( 0 != ( ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) { - if ( 0 > DuplicateOrigAtom( prep_inp_data+1, prep_inp_data ) ) { - AddMOLfileError(sd->pStrErrStruct, "Out of RAM"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_FATAL; - goto exit_function; - } - sd->bTautFlags[INCHI_REC] = sd->bTautFlags[INCHI_BAS]; - sd->bTautFlagsDone[INCHI_REC] = sd->bTautFlagsDone[INCHI_BAS]; - { /* remove "parity undefined in disconnected structure" flag from reconnected structure */ - int k, m, p; - inp_ATOM *at = (prep_inp_data+1)->at; - int num_at = (prep_inp_data+1)->num_inp_atoms; - for ( k = 0; k < num_at; k ++ ) { - for ( m = 0; m < MAX_NUM_STEREO_BONDS && (p=at[k].sb_parity[m]); m ++ ) { - at[k].sb_parity[m] &= SB_PARITY_MASK; - } - } - } - } - - /* make Disconnected structure in prep_inp_data */ - i = (0 != ( ip->bTautFlags & TG_FLAG_CHECK_VALENCE_COORD )); - /* prep_inp_data->bDisconnectCoord > 1 means add prep_inp_data->bDisconnectCoord-1 explicit H atoms */ - if ( 0 < (i = DisconnectMetals( prep_inp_data, i, &bTautFlagsDone ) ) ) { - AddMOLfileError(sd->pStrErrStruct, "Metal was disconnected"); - sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_DISCONNECT_COORD_DONE; - if ( sd->nErrorType < _IS_WARNING ) { - sd->nErrorType = _IS_WARNING; - } -#if ( bRELEASE_VERSION == 0 ) - sd->bExtract |= EXTR_METAL_WAS_DISCONNECTED; -#endif - /* last parm=1 means find link between unchanged by Metal Disconnection components */ - prep_inp_data->num_components = MarkDisconnectedComponents( prep_inp_data, 1 ); - if ( prep_inp_data->num_components < 0 ) { - AddMOLfileError(sd->pStrErrStruct, "Out of RAM"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_FATAL; - goto exit_function; - } - - { /* set parities for the disconnected structure */ - int k, m, p; - inp_ATOM *at = (prep_inp_data)->at; - int num_at = (prep_inp_data)->num_inp_atoms; - for ( k = 0; k < num_at; k ++ ) { - for ( m = 0; m < MAX_NUM_STEREO_BONDS && (p=at[k].sb_parity[m]); m ++ ) { - if ( p & SB_PARITY_FLAG ) { - at[k].sb_parity[m] = (p >> SB_PARITY_SHFT) & SB_PARITY_MASK; - } - } - } - } - - if ( i = ReconcileAllCmlBondParities( prep_inp_data->at, prep_inp_data->num_inp_atoms, 1 ) ) { - char szErrCode[16]; - sprintf( szErrCode, "%d", i); - AddMOLfileError( sd->pStrErrStruct, "0D Parities Reconciliation failed:" ); - AddMOLfileError( sd->pStrErrStruct, szErrCode ); - } - -#if ( REMOVE_ION_PAIRS_DISC_STRU == 1 ) - if ( 0 < remove_ion_pairs( prep_inp_data->num_inp_atoms, prep_inp_data->at ) ) { - AddMOLfileError(sd->pStrErrStruct, "Charges were rearranged"); - if ( sd->nErrorType < _IS_WARNING ) { - sd->nErrorType = _IS_WARNING; - } - sd->bTautFlagsDone[INCHI_REC] |= TG_FLAG_FIX_ODD_THINGS_DONE; - sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ODD_THINGS_DONE; - } -#endif - - /* - if prep_inp_data->nOldCompNumber[i] = iINChI+1 > 0 then - component #(i+1) in prep_inp_data is identical to component #(iINChI+1) in prep_inp_data+1 - */ - } else - if ( i < 0 ) { - AddMOLfileError(sd->pStrErrStruct, "Cannot disconnect metal error"); - sd->nStructReadError = i; - sd->nErrorType = _IS_ERROR; - goto exit_function; - } - } else - { /* remove "disconnected structure parities" from the structure */ - int k, m, p; - inp_ATOM *at = (prep_inp_data)->at; - int num_at = (prep_inp_data)->num_inp_atoms; - for ( k = 0; k < num_at; k ++ ) { - for ( m = 0; m < MAX_NUM_STEREO_BONDS && (p=at[k].sb_parity[m]); m ++ ) { - at[k].sb_parity[m] &= SB_PARITY_MASK; - } - } - } - - -exit_function: - - if ( sd->nErrorType < _IS_ERROR && prep_inp_data ) { - - if ( 0 < post_fix_odd_things( prep_inp_data->num_inp_atoms, prep_inp_data->at ) ) { - AddMOLfileError(sd->pStrErrStruct, "Charges were rearranged"); - if ( sd->nErrorType < _IS_WARNING ) { - sd->nErrorType = _IS_WARNING; - } - sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ODD_THINGS_DONE; - } - if ( (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) && - (prep_inp_data+1)->at && (prep_inp_data+1)->num_inp_atoms > 0 ) { - if ( 0 < post_fix_odd_things( (prep_inp_data+1)->num_inp_atoms, (prep_inp_data+1)->at ) ) { - AddMOLfileError(sd->pStrErrStruct, "Charges were rearranged"); - if ( sd->nErrorType < _IS_WARNING ) { - sd->nErrorType = _IS_WARNING; - } - sd->bTautFlagsDone[INCHI_REC] |= TG_FLAG_FIX_ODD_THINGS_DONE; - sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ODD_THINGS_DONE; - } - } - } - - sd->bTautFlags[INCHI_BAS] |= bTautFlags; /* TG_FLAG_CHECK_VALENCE_COORD_DONE, TG_FLAG_MOVE_CHARGE_COORD_DONE */ - sd->bTautFlagsDone[INCHI_BAS] |= bTautFlagsDone; /* TG_FLAG_CHECK_VALENCE_COORD_DONE, TG_FLAG_MOVE_CHARGE_COORD_DONE */ - return sd->nErrorType; -} - -#ifndef COMPILE_ANSI_ONLY /* { */ -/************************************************************************************************/ -int DisplayTheWholeStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, - INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, - ORIG_ATOM_DATA *orig_inp_data, long num_inp, int iINChI, int bShowStruct, int bINCHI_LIB_Flag ) -{ - - int bDisplayEqu = 0; -#ifndef TARGET_LIB_FOR_WINCHI - /* Displaying equivalent input structures when disconnection has been done: */ - /* in case of TARGET_LIB_FOR_WINCHI equivalence info is always unknown here and bOriginalReconnected=0 */ - int bOriginalReconnected = iINChI < 0 && orig_inp_data && orig_inp_data->nEquLabels && - (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) && - (ip->bTautFlags & TG_FLAG_RECONNECT_COORD); - const char *lpszType = bOriginalReconnected? " (Reconnected)" : - (iINChI < 0 )? "" : - (iINChI == INCHI_BAS )? " (Preprocessed)" : - (iINChI == INCHI_REC )? " (Reconnected)" : ""; - int err = 0; - /* Display the original structure */ - bDisplayEqu = bShowStruct && ip->bDisplay && - ip->dp.nEquLabels && 0 < ip->dp.nCurEquLabel && ip->dp.nCurEquLabel <= ip->dp.nNumEquSets; -#else - if(!DRAWDATA || !DRAWDATA_EXISTS) - return 0; -#endif -#ifndef TARGET_API_LIB - /******************************************************************** - * Ask the user whether to process the input structure or quit - */ - if ( ip->bDisplay && inp_file->f != stdin ) { - if ( user_quit(bDisplayEqu?"Enter=Display identical components, Esc=Stop ?" : "Enter=Display, Esc=Stop ?", ip->ulDisplTime) ) { - sd->bUserQuit = 1; - goto exit_function; - } - } -#endif - /****************************************************** - * Display the whole input structure in console app - */ -/*^^^ #ifndef TARGET_LIB_FOR_WINCHI */ -#if ( !defined( TARGET_LIB_FOR_WINCHI ) && !defined(TARGET_EXE_STANDALONE) ) - if ( bShowStruct && ip->bDisplay ) { - if ( bDisplayEqu ) { - sprintf( szTitle, " Equ Set %d of %d, Input Structure #%ld.%s%s%s%s%s", - ip->dp.nCurEquLabel, ip->dp.nNumEquSets, - num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), lpszType); - } else { - sprintf( szTitle, "Input Structure #%ld.%s%s%s%s%s", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), lpszType); - } - err = DisplayStructure( orig_inp_data->at, orig_inp_data->num_inp_atoms, 0, 1, 0, NULL, 1/*isotopic*/, 0/*taut*/, NULL, NULL, - ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); - sd->bUserQuitComponent = (err==ESC_KEY); - if ( !err ) { - inchi_fprintf( stderr, "Cannot display the structure\n"); - } - } - if( !bDisplayEqu ) { - /* console output progress report */ - if ( ip->bDisplay && !sd->bUserQuitComponent ) { - if ( iINChI == 1 ) { - if ( ip->bDisplay ) - inchi_ios_eprint( log_file, "Processing (rec) structure #%ld.%s%s%s%s...\n", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - else - inchi_fprintf( stderr, "Processing (rec) structure #%ld.%s%s%s%s...\r", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - } else { - if ( ip->bDisplay ) - inchi_ios_eprint( log_file, "Processing structure #%ld.%s%s%s%s...\n", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - else - inchi_fprintf( stderr, "Processing structure #%ld.%s%s%s%s...\r", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - } - } - } -#endif - - - /****************************************************** - * Store the whole input structure in GUI application - */ -#ifdef TARGET_LIB_FOR_WINCHI - if ( ip->bDisplay && bINCHI_LIB_Flag ) -#else - if ( (ip->bDisplay || (ip->bCompareComponents & CMP_COMPONENTS)) && bINCHI_LIB_Flag ) -#endif - { - int bBit, k, bReconnected, nComponent, bPreprocessed; - for ( bBit = 1, k = 0; k < 8; k ++, bBit <<= 1 ) { - /****************************************************************************** - * bReconnected = k%2 (0 or 1) - * nComponent = k/4 (0 or 1) - * bPreprocessed = (k/2)%2 (0 or 1) - ******************************************************************************/ - if ( !(bINCHI_LIB_Flag & bBit) ) { - continue; - } - bReconnected = k%2; - nComponent = k/4; - bPreprocessed = ((k/2)%2); - - sprintf( szTitle, "%s Structure #%ld.%s%s%s%s", - bPreprocessed? "Preprocessed" : bReconnected? "Reconnected" : "Input", - num_inp, - SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue)); - -#ifdef TARGET_LIB_FOR_WINCHI - if(DRAWDATA && DRAWDATA_EXISTS) - { - struct DrawData vDrawData; - int nType = bPreprocessed? COMPONENT_ORIGINAL_PREPROCESSED : COMPONENT_ORIGINAL; - if ( DRAWDATA_EXISTS( nComponent, bPreprocessed, bReconnected ) ) { - sd->nErrorType = _IS_FATAL; - sd->nErrorCode = CT_UNKNOWN_ERR; - return -1; - } - vDrawData.pWindowData = CreateWinData_( orig_inp_data->at, orig_inp_data->num_inp_atoms, - 0, 1 /* bAdd_DT_to_num_H */, 0, NULL, 1, 0, NULL, NULL, - ip->bAbcNumbers, &ip->dp, ip->nMode ); - if( vDrawData.pWindowData != NULL ) - { - vDrawData.nComponent = nComponent; - vDrawData.nType = nType; /* COMPONENT_ORIGINAL or COMPONENT_ORIGINAL_PREPROCESSED */ - vDrawData.bReconnected = bReconnected; /* 0=>main; 1=>reconnected */ - vDrawData.pWindowData->szTitle = _strdup(szTitle); - vDrawData.szTitle = _strdup(szTitle); - DRAWDATA(&vDrawData); - if ( !nComponent ) { - /* keep track of saved INCHI_LIB data */ - orig_inp_data->bSavedInINCHI_LIB[bReconnected] ++; - orig_inp_data->bPreprocessed[bReconnected] = bPreprocessed; - } - } - } -#else - if ( !nComponent ) { - /* keep track of saved INCHI_LIB data */ - orig_inp_data->bSavedInINCHI_LIB[bReconnected] ++; - orig_inp_data->bPreprocessed[bReconnected] = bPreprocessed; - } -#endif - - } - } - -exit_function: - return sd->bUserQuit; -} -#endif /* } COMPILE_ANSI_ONLY */ -/************************************************************************************************/ -int ProcessOneStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, - PINChI2 *pINChI[INCHI_NUM], PINChI_Aux2 *pINChI_Aux[INCHI_NUM], - INCHI_IOSTREAM *inp_file, - INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *prb_file, /*^^^ was: INCHI_IOSTREAM */ - ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data, - long num_inp, char *pStr, int nStrLen, - unsigned char save_opt_bits) -{ - int nRet = 0, nRet1, i, k, maxINChI=0; - COMP_ATOM_DATA composite_norm_data[INCHI_NUM][TAUT_NUM+1]; /* [0]:non-taut, [1]:taut, [2]:intermediate taut struct */ - NORM_CANON_FLAGS ncFlags; - NORM_CANON_FLAGS *pncFlags = &ncFlags; - ORIG_STRUCT OrigStruct; - ORIG_STRUCT *pOrigStruct = NULL; - int bSortPrintINChIFlags=0; - - -#if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) - int ret1=0, ret2=0; -#endif - sd->bUserQuitComponent = 0; - sd->bUserQuitComponentDisplay = 0; - memset( composite_norm_data, 0, sizeof(composite_norm_data) ); - memset( pncFlags, 0, sizeof(*pncFlags) ); - /* ip->msec_LeftTime = ip->msec_MaxTime; */ /* start timeout countdown */ - - /* for testing only */ -#if ( REMOVE_ION_PAIRS_ORIG_STRU == 1 ) - fix_odd_things( orig_inp_data->num_inp_atoms, orig_inp_data->at, 0, ip->bFixNonUniformDraw ); -#endif - -#if ( UNDERIVATIZE == 1 ) /***** post v.1 feature *****/ - if ( ip->bUnderivatize && 0 > (ret2=underivatize( orig_inp_data )) ) { - long num_inp2 = num_inp; - AddMOLfileError(sd->pStrErrStruct, "Underivatization error"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_ERROR; - nRet = _IS_ERROR; - TreatReadTheStructureErrors( sd, ip, LOG_MASK_ALL, inp_file, log_file, output_file, prb_file, - prep_inp_data, &num_inp2, pStr, nStrLen ); - goto exit_function; /* output only if derivatives found */ - } -#endif /* UNDERIVATIZE == 1 */ -#if ( RING2CHAIN == 1 ) /***** post v.1 feature *****/ - if ( ip->bRing2Chain && 0 > (ret1 = Ring2Chain( orig_inp_data )) ) { - long num_inp2 = num_inp; - AddMOLfileError(sd->pStrErrStruct, "Ring to chain error"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_ERROR; - nRet = _IS_ERROR; - TreatReadTheStructureErrors( sd, ip, LOG_MASK_ALL, inp_file, log_file, output_file, prb_file, - prep_inp_data, &num_inp2, pStr, nStrLen ); - goto exit_function; /* output only if derivatives found */ - } -#endif /* RING2CHAIN == 1 */ -#if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) /***** post v.1 feature *****/ - if ( ip->bIngnoreUnchanged && !ret1 && !ret2 ) { - goto exit_function; /* output only if derivatives or ring/chain found */ - } -#endif /* RING2CHAIN == 1 || UNDERIVATIZE == 1 */ - - - /***** output MOLfile ***************/ - if ( ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY ) { - char szNumber[32]; - int ret1a=0, ret2a=0; /* for derivatives and ring-chain */ -/*^^^ #if ( !defined( TARGET_LIB_FOR_WINCHI ) && !defined( TARGET_API_LIB ) ) */ -#if ( !defined( TARGET_LIB_FOR_WINCHI ) && !defined( TARGET_API_LIB ) && !defined(TARGET_EXE_STANDALONE) ) -#if ( TEST_RENUMB_ATOMS != 1 ) - /* log file / console output */ - if ( log_file->f != stderr ) { - if ( ip->bDisplay ) - inchi_ios_eprint( log_file, "Writing structure #%ld.%s%s%s%s...\n", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - else - inchi_fprintf( stderr, "Writing structure #%ld.%s%s%s%s...\r", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - } -#endif -#endif - ret1a = sprintf( szNumber, "Structure #%ld", num_inp ); - ret2a = WriteOrigAtomDataToSDfile( orig_inp_data, output_file, szNumber, NULL, - (sd->bChiralFlag & FLAG_INP_AT_CHIRAL)? 1:0, - (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ATOMS_DT)? 1:0, ip->pSdfLabel, ip->pSdfValue ); - goto exit_function; - } - - /******* create full reversibility information **************/ - if ( !(ip->bINChIOutputOptions & (INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO)) ) { - pOrigStruct = &OrigStruct; - memset( pOrigStruct, 0, sizeof(*pOrigStruct)); - if ( FillOutOrigStruct( orig_inp_data, pOrigStruct, sd ) ) { - AddMOLfileError(sd->pStrErrStruct, "Cannot interpret reversibility information"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_ERROR; - nRet = _IS_ERROR; - } - } - /* create INChI for each connected component of the structure and optionally display them */ - /* create INChI for the whole disconnected or original structure */ - if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) { - nRet1 = CreateOneStructureINChI(sd, ip, szTitle, pINChI, pINChI_Aux, INCHI_BAS, - inp_file, log_file, output_file, prb_file, - orig_inp_data, prep_inp_data, - composite_norm_data, num_inp, - pStr, nStrLen, pncFlags ); - nRet = inchi_max(nRet, nRet1); - } - if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) { - maxINChI = 1; - } - - - if ( nRet != _IS_FATAL && nRet != _IS_ERROR && - (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) && - (ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) { - - /* create INChI for the whole reconnected structure */ - nRet1 = CreateOneStructureINChI(sd, ip, szTitle, pINChI, pINChI_Aux, INCHI_REC, - inp_file, log_file, output_file, prb_file, - orig_inp_data, prep_inp_data, - composite_norm_data,num_inp, - pStr, nStrLen, pncFlags); - nRet = inchi_max(nRet, nRet1); - if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) { - maxINChI = 2; - } - } - - - if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) { - - if ( (sd->bChiralFlag & FLAG_INP_AT_CHIRAL) && - (ip->nMode & REQ_MODE_STEREO) && - !(ip->nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO)) && - !bIsStructChiral( pINChI, sd->num_components ) ) { - - AddMOLfileError(sd->pStrErrStruct, "Not chiral"); - } - /*************************************/ - /* Output err/warn messages */ - /*************************************/ - if ( /*!sd->nErrorCode &&*/ !sd->bUserQuitComponent && !sd->bUserQuit ) { - /* if successful then returns 0, otherwise returns _IS_FATAL */ - /* exctract the structure if requested */ - nRet1 = TreatCreateINChIWarning(sd, ip, prep_inp_data, num_inp, - inp_file, log_file, output_file, prb_file,pStr, nStrLen ); - nRet = inchi_max(nRet, nRet1); - } - } - - - /************************************************/ - /* sort and print INChI for the whole structure */ - /************************************************/ - - if ( ip->nInputType != INPUT_INCHI ) - { - /* Prepare SaveOpt bits */ - save_opt_bits = 0; - if ( ip->bINChIOutputOptions & INCHI_OUT_SAVEOPT ) - { - if ( 0 != ( ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) - save_opt_bits |= SAVE_OPT_RECMET; - if ( 0 != ( ip->nMode & REQ_MODE_BASIC) ) - save_opt_bits |= SAVE_OPT_FIXEDH; - if ( 0 != ( ip->nMode & REQ_MODE_DIFF_UU_STEREO) ) - save_opt_bits |= SAVE_OPT_SLUUD; - if ( 0 == (ip->nMode & (REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU)) ) - save_opt_bits |= SAVE_OPT_SUU; - if ( 0 != (ip->bTautFlags & TG_FLAG_KETO_ENOL_TAUT) ) - save_opt_bits |= SAVE_OPT_KET; - if ( 0 != (ip->bTautFlags & TG_FLAG_1_5_TAUT) ) - save_opt_bits |= SAVE_OPT_15T; - /* Check if /SNon requested and turn OFF stereo bits if so */ - if ( ! (ip->nMode & REQ_MODE_STEREO) ) - { - save_opt_bits &= ~SAVE_OPT_SUU; - save_opt_bits &= ~SAVE_OPT_SLUUD; - } - } - } - - if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) { - - nRet1 = SortAndPrintINChI(output_file, pStr, nStrLen, log_file, - ip, orig_inp_data, prep_inp_data, - composite_norm_data, pOrigStruct, - sd->num_components, sd->num_non_taut, sd->num_taut, - sd->bTautFlags, sd->bTautFlagsDone, pncFlags, num_inp, - pINChI, pINChI_Aux, - &bSortPrintINChIFlags, save_opt_bits); - nRet = inchi_max(nRet, nRet1); - } -#ifndef COMPILE_ANSI_ONLY /* { */ - - /* display equivalent components on original or preprocessed structure(s) */ - -#ifndef TARGET_LIB_FOR_WINCHI - - if ( nRet != _IS_FATAL && nRet != _IS_ERROR && /*ip->bDisplay &&*/ - (ip->bCompareComponents & CMP_COMPONENTS) && !sd->bUserQuit && !sd->bUserQuitComponent ) - { - int j, ret, ord; - int bDisplaySaved = ip->bDisplay; - ORIG_ATOM_DATA *inp_data; - AT_NUMB nEquSet; - for ( ord = -1; ord < INCHI_NUM; ord ++ ) { - switch( ord ) { - case -1: - j = INCHI_BAS; /* preprocessed non-tautomeric */ - break; - case 0: - j = INCHI_REC; /* preprocessed tautomeric */ - break; - case 1: - j = -1; /* original input */ - break; - default: - continue; - } - inp_data = j < 0? orig_inp_data : prep_inp_data+j; - if ( inp_data && inp_data->num_inp_atoms && inp_data->at && - inp_data->nEquLabels && - inp_data->nNumEquSets ) { - for ( nEquSet = 1; nEquSet <= inp_data->nNumEquSets; nEquSet ++ ) { - ip->dp.nEquLabels = inp_data->nEquLabels; - ip->dp.nCurEquLabel = nEquSet; - ip->dp.nNumEquSets = inp_data->nNumEquSets; - ip->bDisplay = 1; /* force display if it was not requested */ - ret = DisplayTheWholeStructure( sd, ip, szTitle, inp_file, log_file, inp_data, num_inp, - j, 1 /*bShowStructure*/, 0 ); - ip->dp.nEquLabels = NULL; - ip->dp.nCurEquLabel = 0; - ip->dp.nNumEquSets = 0; - ip->bDisplay = bDisplaySaved; /* restore display option */ - if ( ret ) { - /* user pressed Esc */ - goto exit_loop; - } - } - } - } -exit_loop:; - } - -#endif - - - - /* display composite results and equivalent components on composite results */ - if ( nRet != _IS_FATAL && nRet != _IS_ERROR && /*ip->bDisplay &&*/ - ip->bDisplayCompositeResults ) { - int iINChI; - for ( iINChI = 0; iINChI < maxINChI && !sd->bUserQuitComponentDisplay; iINChI ++ ) { - DisplayTheWholeCompositeStructure( ip, sd, num_inp, iINChI, - pINChI[iINChI], pINChI_Aux[iINChI], - orig_inp_data, prep_inp_data, - composite_norm_data[iINChI] ); - } -#ifndef TARGET_LIB_FOR_WINCHI - if( !ip->bDisplay && sd->bUserQuitComponentDisplay ) { - sd->bUserQuit = 1; - } -#endif - } - -#endif /* } COMPILE_ANSI_ONLY */ - - - /* XML struct end tag */ - if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) && sd->bXmlStructStarted > 0 ) { - if ( !OutputINChIXmlStructEndTag( output_file, pStr, nStrLen, 1 ) ) { - inchi_ios_eprint( log_file, "Cannot create end xml tag for structure #%ld.%s%s%s%s Terminating.\n", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - sd->bXmlStructStarted = -1; /* do not repeat same message */ - nRet = _IS_FATAL; - } else { - sd->bXmlStructStarted = 0; /* do not continue xml output for this structure */ - } - } - if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) { - /* Special mode: extract all good MOLfiles into the problem file - * Do not extract any MOLfile that could not be processed (option /PGO) - */ - if ( prb_file && prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd && ip->bSaveAllGoodStructsAsProblem ) { - CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, 0); - } -#if ( /*bRELEASE_VERSION != 1 &&*/ EXTR_FLAGS == EXTR_TRANSPOSITION_EXAMPLES && EXTR_MASK == EXTR_FLAGS ) - else - if ( prb_file->f && (bSortPrintINChIFlags & - ( FLAG_SORT_PRINT_TRANSPOS_BAS | FLAG_SORT_PRINT_TRANSPOS_REC ) ) - ) { - CopyMOLfile(inp_file, sd->fPtrStart, sd->fPtrEnd, prb_file->f, 0); - } -#endif - } - for ( i = 0; i < INCHI_NUM; i ++ ) { - for ( k = 0; k < TAUT_NUM+1; k ++ ) { - FreeCompAtomData( &composite_norm_data[i][k] ); - } - } - FreeOrigStruct( pOrigStruct); - - -/* - FreeInpAtomData( inp_cur_data ); - FreeInpAtomData( inp_norm_data[0] ); - FreeInpAtomData( inp_norm_data[1] ); -*/ -exit_function: - - - return nRet; -} -/************************************************************************************************/ -int bIsStructChiral( PINChI2 *pINChI2[INCHI_NUM], int num_components[] ) -{ - int i, j, k; - INChI *pINChI; - INChI_Stereo *Stereo; - for ( j = 0; j < INCHI_NUM; j ++ ) { /* disconnected / reconnected */ - if ( !num_components[j] ) { - continue; - } - for ( i = 0; i < num_components[j]; i ++ ) { /* component */ - for ( k = 0; k < TAUT_NUM; k ++ ) { /* mobile/immobile H */ - if ( (pINChI = pINChI2[j][i][k]) && - !pINChI->bDeleted && - pINChI->nNumberOfAtoms > 0 ) { - - if ( (Stereo = pINChI->Stereo) && - Stereo->t_parity && - Stereo->nNumberOfStereoCenters > 0 && - Stereo->nCompInv2Abs ) { - return 1; /* inversion changed stereo */ - } - if ( (Stereo = pINChI->StereoIsotopic) && - Stereo->t_parity && - Stereo->nNumberOfStereoCenters > 0 && - Stereo->nCompInv2Abs ) { - return 1; /* inversion changed stereo */ - } - } - } - } - } - return 0; -} -/************************************************************************************************/ -int CreateOneStructureINChI( STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, - PINChI2 *pINChI2[INCHI_NUM], PINChI_Aux2 *pINChI_Aux2[INCHI_NUM], int iINChI, - INCHI_IOSTREAM *inp_file, - INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *prb_file, /*^^^ was: INCHI_IOSTREAM */ - ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data, - COMP_ATOM_DATA composite_norm_data2[][TAUT_NUM+1], - long num_inp, char *pStr, int nStrLen, NORM_CANON_FLAGS *pncFlags ) -{ - int i, j, k, /*m,*/ nRet = 0; -#ifndef TARGET_LIB_FOR_WINCHI - int n; -#ifndef COMPILE_ANSI_ONLY - int err; -#endif -#endif - - PINChI2 *pINChI = NULL; - PINChI_Aux2 *pINChI_Aux = NULL; - - INP_ATOM_DATA InpCurAtData; - INP_ATOM_DATA *inp_cur_data; - - INP_ATOM_DATA InpNormAtData, InpNormTautData; - INP_ATOM_DATA *inp_norm_data[TAUT_NUM]; /* = { &InpNormAtData, &InpNormTautData }; */ - ORIG_ATOM_DATA *cur_prep_inp_data = prep_inp_data + iINChI; - inchiTime ulTStart; -#ifndef COMPILE_ANSI_ONLY - int bShowStructure = 0; - int bStructurePreprocessed = 0; /* All changes except disconnection */ - int bStructureDisconnected = 0; - int bAlsoOutputReconnected = 0, bINCHI_LIB_Flag = 0; - COMP_ATOM_DATA *composite_norm_data = composite_norm_data2[iINChI]; - INP_ATOM_DATA2 *all_inp_norm_data = NULL; -#endif - - /* - if ( orig_inp_data is NOT empty AND - prep_inp_data[0] IS empty ) then: - - 1. copy orig_inp_data --> prep_inp_data[0] - 2. fix odd things in prep_inp_data[0] - 3. if( orig_inp_data->bDisconnectSalts ) then - -- disconnect salts in prep_inp_data[0] - 4. move protons to neutralize charges on heteroatoms - 5. if( orig_inp_data->bDisconnectCoord ) then - -- copy prep_inp_data[0] --> prep_inp_data[1] - -- disconnect metals in prep_inp_data[0] - - [ This all is done in PreprocessOneStructure() ] - - iINChI = 0 - ========= - (normal/disconnected layer) - - 1. normalize prep_inp_data[0] in inp_norm_data[0,1] - 2. create INChI[ iINChI ] out of inp_norm_data[0,1] - - - iINChI = 1 AND orig_inp_data->bDisconnectCoord > 0 - ================================================= - (reconnected layer) - - 1. normalize prep_inp_data[1] in inp_norm_data[0,1] - 2. create INChI[ iINChI ] out of inp_norm_data[0,1] - - */ - -#if ( TEST_RENUMB_ATOMS == 1 ) - RENUMB_DATA RenumbData; - RENUMB_DATA *pRenumbData = &RenumbData; -#endif - - - ip->msec_LeftTime = ip->msec_MaxTime; /* start timeout countdown for each component */ - -#if ( TEST_RENUMB_ATOMS == 1 ) - memset( pRenumbData, 0, sizeof(*pRenumbData) ); -#endif - - inp_cur_data = &InpCurAtData; - inp_norm_data[TAUT_NON] = &InpNormAtData; - inp_norm_data[TAUT_YES] = &InpNormTautData; - - memset( inp_cur_data , 0, sizeof( *inp_cur_data ) ); - memset( inp_norm_data[TAUT_NON], 0, sizeof( *inp_norm_data[0] ) ); - memset( inp_norm_data[TAUT_YES], 0, sizeof( *inp_norm_data[0] ) ); - -#ifndef COMPILE_ANSI_ONLY - memset( composite_norm_data+TAUT_NON, 0, sizeof( composite_norm_data[0] ) ); - memset( composite_norm_data+TAUT_YES, 0, sizeof( composite_norm_data[0] ) ); - memset( composite_norm_data+TAUT_INI, 0, sizeof( composite_norm_data[0] ) ); -#endif - if ( ip->bAllowEmptyStructure && !orig_inp_data->at && !orig_inp_data->num_inp_atoms ) { - ; - } else - if ( !orig_inp_data->at || !orig_inp_data->num_inp_atoms ) { - return 0; /* nothing to do */ - } - if ( iINChI == 1 && orig_inp_data->bDisconnectCoord <= 0 ) { - return 0; - } - - /* m = iINChI; */ /* orig_inp_data index */ - - if ( iINChI != INCHI_BAS && iINChI != INCHI_REC ) { - AddMOLfileError(sd->pStrErrStruct, "Fatal undetermined program error"); - sd->nStructReadError = 97; - nRet = sd->nErrorType = _IS_FATAL; - goto exit_function; - } - - /******************************************************************* - * * - * * - * Whole structure preprocessing: 1st step of the normalization * - * * - * Happen only on the first call to CreateOneStructureINChI() * - * * - * * - *******************************************************************/ - - if ( (!prep_inp_data->at || !prep_inp_data->num_inp_atoms) && orig_inp_data->num_inp_atoms > 0 ) { - /* the structure has not been preprocessed */ - if ( ip->msec_MaxTime ) { - InchiTimeGet( &ulTStart ); - } - PreprocessOneStructure( sd, ip, orig_inp_data, prep_inp_data ); - pncFlags->bTautFlags[iINChI][TAUT_YES] = - pncFlags->bTautFlags[iINChI][TAUT_NON] = sd->bTautFlags[INCHI_BAS] | ip->bTautFlags; - pncFlags->bTautFlagsDone[iINChI][TAUT_YES] = - pncFlags->bTautFlagsDone[iINChI][TAUT_NON] = sd->bTautFlagsDone[INCHI_BAS] | ip->bTautFlagsDone; - -#ifndef COMPILE_ANSI_ONLY - /* in this location the call happens once for each input structure, before preprocessing */ - bStructurePreprocessed = (0 != (sd->bTautFlagsDone[INCHI_BAS] & ( - TG_FLAG_MOVE_HPLUS2NEUTR_DONE | - TG_FLAG_DISCONNECT_SALTS_DONE | - TG_FLAG_MOVE_POS_CHARGES_DONE | - TG_FLAG_FIX_ODD_THINGS_DONE ))); - bStructureDisconnected = (0 != (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE)); - - bShowStructure = ( bStructurePreprocessed || - bStructureDisconnected || - prep_inp_data[0].num_components > 1); - - /* sd->bTautFlags[] contains output flags - ip->bTautFlags contains input flags - */ - bAlsoOutputReconnected = (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) && - (ip->bTautFlags & TG_FLAG_RECONNECT_COORD); - bINCHI_LIB_Flag = 0; - - /*************** output structures to TARGET_LIB_FOR_WINCHI conditions ********************* - * - * Send to TARGET_LIB_FOR_WINCHI: - * - * type component conditions - * - * COMPONENT_ORIGINAL #0: (num_components > 1) - * COMPONENT_ORIGINAL_PREPROCESSED #0: (num_components > 1) && (preprocessed) - * COMPONENT_ORIGINAL #1: (num_components = 1) && (preprocessed) - * - * Flags explanation: - * MAIN => iINChI=0, RECN => iINChI=1 (Reconnected) - * ORIG => Original, PREP => Preprocessed - * - * Possible flags: k - * - * COMP_ORIG_0_MAIN 0x0001 0 COMPONENT_ORIGINAL, bMain, component #0 - * COMP_ORIG_0_RECN 0x0002 1 COMPONENT_ORIGINAL, bRecn, component #0 - * - * COMP_PREP_0_MAIN 0x0004 2 COMPONENT_ORIGINAL_PREPROCESSED, bMain, component #0 - * COMP_PREP_0_RECN 0x0008 3 COMPONENT_ORIGINAL_PREPROCESSED, bRecn, component #0 - * - * COMP_ORIG_1_MAIN 0x0010 4 COMPONENT_ORIGINAL, bMain, component #1 - * COMP_ORIG_1_RECN 0x0020 5 COMPONENT_ORIGINAL, bRecn, component #1 - * - * bReconnected = k%2 (0 or 1) - * nComponent = k/4 (0 or 1) - * bPreprocessed = (k/2)%2 (0 or 1) - * - ******************************************************************************/ - /* Original -> Main, component #0, Original */ - if ( prep_inp_data[INCHI_BAS].num_components > 1 ) { - bINCHI_LIB_Flag |= COMP_ORIG_0_MAIN; - } else - /* Original -> Main, component #1, Original */ - if ( prep_inp_data[INCHI_BAS].num_components == 1 && bStructurePreprocessed ) { - bINCHI_LIB_Flag |= COMP_ORIG_1_MAIN; - /* preprocessed will be added when output canonicalization results */ - } - if ( bAlsoOutputReconnected ) { - /* Original -> Reconnected, component #0, Original */ - if ( prep_inp_data[INCHI_REC].num_components > 1 ) { - bINCHI_LIB_Flag |= COMP_ORIG_0_RECN; - } else - /* Original -> Reconnected, component #1, Original */ - if ( prep_inp_data[INCHI_BAS].num_components == 1 && bStructurePreprocessed ) { - bINCHI_LIB_Flag |= COMP_ORIG_1_RECN; - /* preprocessed will be added when output canonicalization results */ - } - } - if ( ip->msec_MaxTime ) { - ip->msec_LeftTime -= InchiTimeElapsed( &ulTStart ); - } - - /* display the ORIGINAL, UN-PREPROCESSED structure */ - if ( DisplayTheWholeStructure( sd, ip, szTitle, inp_file, log_file, orig_inp_data, num_inp, - -1, bShowStructure, bINCHI_LIB_Flag ) ) { - goto exit_function; - } -#endif - switch (sd->nErrorType) { - case _IS_ERROR: - case _IS_FATAL: - /* error message */ - nRet = TreatReadTheStructureErrors( sd, ip, LOG_MASK_ALL, inp_file, log_file, output_file, prb_file, - prep_inp_data, &num_inp, pStr, nStrLen ); - goto exit_cycle; - } - } - /* tranfer flags from INChI_Aux to sd */ - - - - - -#ifndef COMPILE_ANSI_ONLY /* { */ - - /******************************************/ - /* Displaying the structures */ - /* Only under WIN32 */ - /******************************************/ - if ( ip->bDisplayCompositeResults && - !sd->bUserQuitComponentDisplay && prep_inp_data[iINChI].num_components > 1) { - all_inp_norm_data = (INP_ATOM_DATA2 *)inchi_calloc( prep_inp_data[iINChI].num_components, sizeof(all_inp_norm_data[0])); - } - - - /* Display the input structure AFTER PREPROCESSING */ - switch ( iINChI ) { - - case INCHI_BAS: - /*------------ Possibly disconnected structure -------------------*/ - bStructurePreprocessed = 0 != (sd->bTautFlagsDone[iINChI] & ( - TG_FLAG_MOVE_HPLUS2NEUTR_DONE | - TG_FLAG_DISCONNECT_SALTS_DONE | - TG_FLAG_MOVE_POS_CHARGES_DONE | - TG_FLAG_MOVE_CHARGE_COORD_DONE | - TG_FLAG_DISCONNECT_COORD_DONE | - TG_FLAG_FIX_ODD_THINGS_DONE )); - bINCHI_LIB_Flag = 0; - - /* Preprocessed/Main -> Main, component #0, Preprocessed */ - if ( prep_inp_data[iINChI].num_components > 1 && bStructurePreprocessed ) { - bINCHI_LIB_Flag |= COMP_PREP_0_MAIN; - } - - bShowStructure = ( bStructurePreprocessed && prep_inp_data[iINChI].num_components > 1); - break; - - case INCHI_REC: - /*------------ Reconnected structure ------------------------------*/ - bAlsoOutputReconnected = (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) && - (ip->bTautFlags & TG_FLAG_RECONNECT_COORD); - if ( !bAlsoOutputReconnected ) { - break; - } - bStructurePreprocessed = 0 != (sd->bTautFlagsDone[iINChI] & ( - TG_FLAG_MOVE_HPLUS2NEUTR_DONE | - TG_FLAG_DISCONNECT_SALTS_DONE | - TG_FLAG_MOVE_POS_CHARGES_DONE | - TG_FLAG_FIX_ODD_THINGS_DONE )); - bINCHI_LIB_Flag = 0; - - /* Preprocessed/Reconnected -> Reconnected, component #0, Preprocessed */ - if ( prep_inp_data[iINChI].num_components > 1 && bStructurePreprocessed ) { - bINCHI_LIB_Flag |= COMP_PREP_0_RECN; - } - - bShowStructure = ( bStructurePreprocessed && prep_inp_data[iINChI].num_components > 1 ); - break; - default: - bShowStructure = 0; - } - if ( prep_inp_data[iINChI].num_inp_atoms > 0 ) { - if ( DisplayTheWholeStructure( sd, ip, szTitle, inp_file, log_file, prep_inp_data+iINChI, num_inp, - iINChI, bShowStructure, bINCHI_LIB_Flag ) ) { - goto exit_function; - } - } -#endif /* } ifndef COMPILE_ANSI_ONLY */ - - - - /* allocate pINChI[iINChI] and pINChI_Aux2[iINChI] -- arrays of pointers to INChI and INChI_Aux */ - /* assign values to sd->num_components[] */ - MYREALLOC2(PINChI2, PINChI_Aux2, pINChI2[iINChI], pINChI_Aux2[iINChI], sd->num_components[iINChI], cur_prep_inp_data->num_components, k); - if ( k ) { - AddMOLfileError(sd->pStrErrStruct, "Cannot allocate output data. Terminating"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_FATAL; - goto exit_function; - } - pINChI = pINChI2[iINChI]; - pINChI_Aux = pINChI_Aux2[iINChI]; - - /**************************************************************************/ - /* */ - /* */ - /* M A I N C Y C L E: P R O C E S S C O M P O N E N T S */ - /* */ - /* */ - /* O N E B Y O N E */ - /* */ - /* */ - /**************************************************************************/ - - for ( i = 0, nRet = 0; !sd->bUserQuitComponent && i < cur_prep_inp_data->num_components; i ++ ) { - if ( ip->msec_MaxTime ) { - InchiTimeGet( &ulTStart ); - } -#ifndef TARGET_LIB_FOR_WINCHI /* { */ -#if ( bREUSE_INCHI == 1 ) - if ( iINChI == INCHI_REC && (!ip->bDisplay && !ip->bDisplayCompositeResults && !(ip->bCompareComponents & CMP_COMPONENTS) || - sd->bUserQuitComponentDisplay) ) { - /* reconnected structure (06-20-2005: added "&& !ip->bDisplayCompositeResults" to display composite structure) */ - int m = iINChI-1; - /* find whether we have already calculated this INChI in basic (disconnected) layer */ - for ( j = n = 0; j < prep_inp_data[m].num_components; j ++ ) { - if ( i+1 == prep_inp_data[m].nOldCompNumber[j] && - (pINChI2[m][j][TAUT_NON] || pINChI2[m][j][TAUT_YES]) ) { - /* yes, we have already done this */ - if ( !n++ ) { - memcpy( pINChI +i, pINChI2 [m]+j, sizeof(pINChI[0])); - memcpy( pINChI_Aux+i, pINChI_Aux2[m]+j, sizeof(pINChI_Aux[0])); - for ( k = 0; k < TAUT_NUM; k ++ ) { - if ( pINChI[i][k] ) { - pINChI[i][k]->nRefCount ++; - if ( pINChI[i][k]->nNumberOfAtoms > 0 ) { - switch( k ) { - case TAUT_NON: - sd->num_non_taut[iINChI] ++; - break; - case TAUT_YES: - if ( pINChI[i][k]->lenTautomer > 0 ) { - sd->num_taut[iINChI] ++; - } else - if ( !pINChI[i][TAUT_NON] || !pINChI[i][TAUT_NON]->nNumberOfAtoms ) { - sd->num_non_taut[iINChI] ++; - } - break; - } - } - } - if ( pINChI_Aux[i][k] ) { - pINChI_Aux[i][k]->nRefCount ++; - } - } - } - } - } - if ( n == 1 ) { - continue; - } - if ( n > 1 ) { /* ith component is equivalent to more than one another component */ - AddMOLfileError(sd->pStrErrStruct, "Cannot distinguish components"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_ERROR; - goto exit_function; - } - } -#endif -#endif /* } TARGET_LIB_FOR_WINCHI */ - - /*****************************************************/ - /* a) allocate memory and extract current component */ - /*****************************************************/ - nRet = GetOneComponent( sd, ip, log_file, output_file, inp_cur_data, cur_prep_inp_data, i, num_inp, pStr, nStrLen ); - if ( ip->msec_MaxTime ) { - ip->msec_LeftTime -= InchiTimeElapsed( &ulTStart ); - } - switch ( nRet ) { - case _IS_ERROR: - case _IS_FATAL: - goto exit_cycle; - } -#ifndef TARGET_API_LIB - /* console request: Display the component? */ - if ( ip->bDisplay && inp_file->f != stdin ) { - if ( user_quit("Enter=Display Component, Esc=Stop ?", ip->ulDisplTime) ) { - sd->bUserQuitComponent = 1; - break; - } - } -#endif -#ifndef COMPILE_ANSI_ONLY /* { */ - /* b) Display the extracted original component structure */ - if ( inp_cur_data->at && ip->bDisplay && !sd->bUserQuitComponentDisplay ) { - if ( cur_prep_inp_data->num_components == 1 ) { - sprintf( szTitle, "%sInput Structure #%ld.%s%s%s%s%s", - bStructurePreprocessed? "Preprocessed ":"", - num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), iINChI? " (Reconnected)":""); - } else { - sprintf( szTitle, "Component #%d of %d, Input Structure #%ld.%s%s%s%s%s", - i+1, cur_prep_inp_data->num_components, - num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), iINChI? " (Reconnected)":""); - } -#ifndef TARGET_LIB_FOR_WINCHI - err = DisplayStructure( inp_cur_data->at, inp_cur_data->num_at, - 0, 1, 0, NULL, 1/*isotopic*/, 0/*taut*/, NULL, NULL, - ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); - sd->bUserQuitComponentDisplay = (err==ESC_KEY); - if ( !err ) - { - inchi_fprintf( stderr, "Cannot display the structure\n"); - } -#else - if(DRAWDATA && DRAWDATA_EXISTS) - { - struct DrawData vDrawData; - int nType = COMPONENT_ORIGINAL; - vDrawData.pWindowData = CreateWinData_( inp_cur_data->at, inp_cur_data->num_at, - 0, 1 /* bAdd_DT_to_num_H */, 0, NULL, - 1 /* display isotopic if present */, 0, NULL, NULL, - ip->bAbcNumbers, &ip->dp, ip->nMode ); - if( vDrawData.pWindowData != NULL ) - { - if ( DRAWDATA_EXISTS ( i+1, nType, iINChI ) ) { /* i = component number */ - nType = COMPONENT_ORIGINAL_PREPROCESSED; - } - vDrawData.nComponent = i+1; - vDrawData.nType = nType; - vDrawData.bReconnected = iINChI; /* 0=>main; 1=>reconnected */ - vDrawData.szTitle = _strdup(szTitle); - vDrawData.pWindowData->szTitle = _strdup(szTitle); - DRAWDATA(&vDrawData); - } - } -#endif - } -#endif /* } COMPILE_ANSI_ONLY */ - -#if ( TEST_RENUMB_ATOMS == 1 ) /* { */ - /****************************************************************************/ - /* R E N U M B E R I N G (testing only) Part I STARTS here */ - /****************************************************************************/ - RenumberingTestInit( pRenumbData, inp_cur_data ); - if ( log_file != stderr ) { - if ( ip->bDisplay ) - inchi_ios_eprint( log_file, "Component #%d structure #%ld.%s%s%s%s...\n", i+1, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - else - inchi_ios_eprint( stderr, "Component #%d structure #%ld.%s%s%s%s...\r", i+1, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - } - /****************************************************************************/ - /* R E N U M B E R I N G (testing only) Part I ENDS here */ - /****************************************************************************/ -#endif /* } TEST_RENUMB_ATOMS */ - - - /*******************************************************************************/ - /* */ - /* N O R M A L I Z A T I O N a n d C A N O N I C A L I Z A T I O N */ - /* */ - /* (both tautomeric and non-tautomeric if requested) */ - /* */ - /*******************************************************************************/ - /* c) Create the component's INChI ( copies ip->bTautFlags into sd->bTautFlags)*/ - /*******************************************************************************/ - nRet = CreateOneComponentINChI( sd, ip, inp_cur_data, orig_inp_data, pINChI/*2[iINChI]*/, pINChI_Aux/*2[iINChI]*/, iINChI, - i, num_inp, inp_norm_data, pncFlags, log_file ); - - -#if ( TEST_RENUMB_ATOMS == 1 ) /* { */ - /****************************************************************************/ - /* R E N U M B E R I N G (testing only) Part II STARTS here */ - /****************************************************************************/ - if ( !nRet ) { - nRet = RenumberingTest( pINChI/*2[iINChI]*/, pINChI_Aux/*2[iINChI]*/, orig_inp_data, iINChI, pRenumbData, inp_cur_data, inp_norm_data, sd, ip, szTitle, log_file, prb_file, i, num_inp, pncFlags); - } - RenumberingTestUninit( pRenumbData ); - /****************************************************************************/ - /* R E N U M B E R I N G (testing only) Part II ENDS here */ - /****************************************************************************/ -#endif /* } TEST_RENUMB_ATOMS */ - - - /* d) Display one component structure and/or INChI results only if there was no error */ -#ifndef COMPILE_ANSI_ONLY /* { */ - if ( !nRet ) { - /* output one component INChI to the stdout if requested */ - /* - if ( ip->bDisplayEachComponentINChI ) { - int cur_num_non_taut = (pINChI[i][TAUT_NON] && pINChI[i][TAUT_NON]->nNumberOfAtoms>0); - int cur_num_taut = (pINChI[i][TAUT_YES] && pINChI[i][TAUT_YES]->nNumberOfAtoms>0); - if ( ip->bDisplayEachComponentINChI && cur_num_non_taut + cur_num_taut ) { - SortAndPrintINChI(stdout, pStr, nStrLen, NULL, - ip, 1, cur_num_non_taut, cur_num_taut, - num_inp, pINChI+i, pINChI_Aux+i, - save_opt_bits); - } - } - */ - /************************************************************************** - * display from one up to 4 structure pictures-results for each component * - * Enable buttons: * - * BN (non-tautomeric non-isotopic): inp_norm_data[0]->bExists * - * TN (tautomeric non-isotopic): inp_norm_data[1]->bExists * - * BI (non-tautomeric isotopic): inp_norm_data[0]->bExists && * - * inp_norm_data[0]->bHasIsotopicLayer * - * TI (tautomeric isotopic): inp_norm_data[1]->bExists && * - * inp_norm_data[1]->bHasIsotopicLayer * - **************************************************************************/ - int bIsotopic, bTautomeric, bDisplayTaut, bHasIsotopicLayer, bFixedBondsTaut, m_max, m, nNumDisplayedFixedBondTaut=0; - for ( j = 0; ip->bDisplay && !sd->bUserQuitComponentDisplay && j < TAUT_NUM; j ++ ) { - if ( inp_norm_data[j]->bExists && !inp_norm_data[j]->bDeleted ) { - bTautomeric = (pINChI[i][j]->lenTautomer > 0); /* same as (inp_norm_data[j]->bTautomeric > 0) */ - /* if requested tautomeric and no tautmerism found then do not say mobile or fixed H. 2004-10-27 */ - bDisplayTaut = (!(ip->nMode & REQ_MODE_BASIC) && !bTautomeric)? -1 : bTautomeric; - bHasIsotopicLayer = (inp_norm_data[j]->bHasIsotopicLayer > 0); - for ( k = 0; k <= bHasIsotopicLayer; k ++ ) { - bIsotopic = (k > 0); - m_max = inp_norm_data[j]->at_fixed_bonds && inp_norm_data[j]->bTautPreprocessed? 1 : 0; - for ( m = m_max; 0 <= m; m -- ) { - bFixedBondsTaut = (m>0); - nNumDisplayedFixedBondTaut += bFixedBondsTaut; /* display only one time */ - /* added number of components, added another format for a single component case - DCh */ - if ( cur_prep_inp_data->num_components > 1 ) { - sprintf( szTitle, "%s Component #%d of %d, Structure #%ld%s%s.%s%s%s%s%s", - bFixedBondsTaut? "Preprocessed":"Result for", - i+1, cur_prep_inp_data->num_components, num_inp, - bDisplayTaut==1? ", mobile H": bDisplayTaut==0?", fixed H":"", - bIsotopic? ", isotopic":"", - SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), iINChI? " (Reconnected)":""); - } else { - sprintf( szTitle, "%s Structure #%ld%s%s.%s%s%s%s%s", - bFixedBondsTaut? "Preprocessed":"Result for", - num_inp, - bDisplayTaut==1? ", mobile H": bDisplayTaut==0?", fixed H":"", - bIsotopic? ", isotopic":"", - SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), iINChI? " (Reconnected)":""); - } -#ifndef TARGET_LIB_FOR_WINCHI - if ( bFixedBondsTaut && nNumDisplayedFixedBondTaut != 1 ) - continue; - if ( bFixedBondsTaut ) { - err = DisplayStructure( inp_norm_data[j]->at_fixed_bonds, inp_norm_data[j]->num_at, - inp_norm_data[j]->num_removed_H, 0 /*bAdd_DT_to_num_H*/, - inp_norm_data[j]->nNumRemovedProtons, - inp_norm_data[j]->nNumRemovedProtonsIsotopic, - bHasIsotopicLayer, j, NULL, NULL, - ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); - } else { - err = DisplayStructure( inp_norm_data[j]->at, inp_norm_data[j]->num_at, - 0, 0 /*bAdd_DT_to_num_H*/, 0, NULL, - k, j, pINChI[i], pINChI_Aux[i], - ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); - } - if ( sd->bUserQuitComponentDisplay = (err==ESC_KEY) ) { - break; - } -#else - if(DRAWDATA && !bFixedBondsTaut) - { - struct DrawData vDrawData; - vDrawData.pWindowData = CreateWinData_( inp_norm_data[j]->at, inp_norm_data[j]->num_at, - 0, 0 /* bAdd_DT_to_num_H */, 0, NULL, - k, j, pINChI[i], pINChI_Aux[i], - ip->bAbcNumbers, &ip->dp, ip->nMode ); - if( vDrawData.pWindowData != NULL ) - { - int nType; - vDrawData.nComponent = i+1; - if( bTautomeric == 0 ) - nType = (bIsotopic == 0) ? COMPONENT_BN: COMPONENT_BI; - else - nType = (bIsotopic == 0) ? COMPONENT_TN: COMPONENT_TI; - vDrawData.nType = nType; - vDrawData.bReconnected = iINChI; /* 0=>main; 1=>reconnected */ - vDrawData.szTitle = _strdup(szTitle); - vDrawData.pWindowData->szTitle = _strdup(szTitle); - DRAWDATA(&vDrawData); - } - } else - if(DRAWDATA && bFixedBondsTaut) - { - struct DrawData vDrawData; - if ( (ip->bCompareComponents & CMP_COMPONENTS) && - !(ip->bCompareComponents & CMP_COMPONENTS_NONTAUT) && - !bIsotopic == !inp_norm_data[j]->bHasIsotopicLayer ) { - - vDrawData.pWindowData = - CreateWinData_( inp_norm_data[j]->at_fixed_bonds, inp_norm_data[j]->num_at, - inp_norm_data[j]->num_removed_H, - 0 /* bAdd_DT_to_num_H */, - inp_norm_data[j]->nNumRemovedProtons, - inp_norm_data[j]->nNumRemovedProtonsIsotopic, - k, j, NULL, NULL, - ip->bAbcNumbers, &ip->dp, ip->nMode ); - } else { - continue; - } - if( vDrawData.pWindowData != NULL ) - { - vDrawData.nComponent = i+1; - vDrawData.nType = COMPONENT_ORIGINAL_PREPROCESSED; - vDrawData.bReconnected = iINChI; /* 0=>main; 1=>reconnected */ - vDrawData.szTitle = _strdup(szTitle); - vDrawData.pWindowData->szTitle = _strdup(szTitle); - DRAWDATA(&vDrawData); - } - } -#endif - } - } - } - } - - /* save normalized components for composite display */ - if ( ip->bDisplayCompositeResults && all_inp_norm_data ) { - for ( j = 0; j < TAUT_NUM; j ++ ) { - if ( inp_norm_data[j]->bExists ) { - all_inp_norm_data[i][j] = *inp_norm_data[j]; - memset( inp_norm_data[j], 0, sizeof(*inp_norm_data[0]) ); - } - } - } - - } -#endif /* } COMPILE_ANSI_ONLY */ - if ( nRet ) { - nRet = TreatCreateOneComponentINChIError(sd, ip, cur_prep_inp_data, i, num_inp, - inp_file, log_file, output_file, prb_file,pStr, nStrLen ); - - break; - } - } - /**************************************************************************/ - /* */ - /* */ - /* E N D O F T H E M A I N C Y C L E P R O C E S S I N G */ - /* */ - /* C O M P O N E N T S O N E B Y O N E */ - /* */ - /* */ - /**************************************************************************/ - - -exit_cycle: - -#if ( TEST_RENUMB_ATOMS == 1 ) /* { */ - if ( pRenumbData->bRenumbErr && (!nRet || nRet==_IS_WARNING) ) { - sd->nErrorCode = pRenumbData->bRenumbErr; - nRet = TreatCreateOneComponentINChIError(sd, ip, cur_prep_inp_data, -1, num_inp, - inp_file, log_file, output_file, prb_file,pStr, nStrLen ); - /* nRet = _IS_ERROR; */ - sd->nErrorCode = 0; - nRet = 0; - } -#endif /* } TEST_RENUMB_ATOMS */ - switch ( nRet ) { - - case _IS_FATAL: - case _IS_ERROR: - break; - - default: - -#ifndef COMPILE_ANSI_ONLY /* { */ - /* composite results picture(s) */ - if ( all_inp_norm_data ) { - int res = CreateCompositeNormAtom( composite_norm_data, all_inp_norm_data, pINChI, pINChI_Aux, - prep_inp_data[iINChI].num_components, ip->nMode ); - /* - for ( i = 0; i < prep_inp_data[iINChI].num_components; i ++ ) { - for ( k = 0; k < TAUT_NUM; k ++ ) { - FreeInpAtomData( &all_inp_norm_data[i][k] ); - } - } - inchi_free( all_inp_norm_data ); - all_inp_norm_data = NULL; - */ - } -#endif /* } COMPILE_ANSI_ONLY */ - - break; - } - - -#ifndef COMPILE_ANSI_ONLY /* { */ - /* avoid memory leaks in case of error */ - if ( all_inp_norm_data ) { - for ( i = 0; i < prep_inp_data[iINChI].num_components; i ++ ) { - for ( k = 0; k < TAUT_NUM; k ++ ) { - FreeInpAtomData( &all_inp_norm_data[i][k] ); - } - } - inchi_free( all_inp_norm_data ); - all_inp_norm_data = NULL; - } -#endif /* } COMPILE_ANSI_ONLY */ - - - FreeInpAtomData( inp_cur_data ); - for ( i = 0; i < TAUT_NUM; i ++ ) { - FreeInpAtomData( inp_norm_data[i] ); - } - - -exit_function: - - return nRet; -} - - - -#ifndef COMPILE_ANSI_ONLY /* { */ -/****************************************************************************/ -int CreateCompositeNormAtom( COMP_ATOM_DATA *composite_norm_data, INP_ATOM_DATA2 *all_inp_norm_data, - PINChI2 *pINChI, PINChI_Aux2 *pINChI_Aux, int num_components, INCHI_MODE nMode ) -{ - int i, j, jj, k, n, m, tot_num_at, tot_num_H, cur_num_at, cur_num_H, nNumRemovedProtons; - int num_comp[TAUT_NUM+1], num_taut[TAUT_NUM+1], num_del[TAUT_NUM+1], num_at[TAUT_NUM+1], num_inp_at[TAUT_NUM+1]; - int ret = 0, indicator = 1; - inp_ATOM *at, *at_from; - memset( num_comp, 0, sizeof(num_comp) ); - memset( num_taut, 0, sizeof(num_taut) ); - memset( num_del, 0, sizeof(num_taut) ); - /* count taut and non-taut components */ - for ( j = 0; j < TAUT_NUM; j ++ ) { - num_comp[j] = num_taut[j] = 0; - for ( i = 0; i < num_components; i ++ ) { - if ( all_inp_norm_data[i][j].bExists ) { - num_del[j] += (0 != all_inp_norm_data[i][j].bDeleted ); - num_comp[j] ++; - num_taut[j] += (0 != all_inp_norm_data[i][j].bTautomeric); - } - } - } - /* count intermediate taut structure components */ - if ( num_comp[TAUT_YES] > num_del[TAUT_YES] && num_taut[TAUT_YES] ) { - /* - num_comp[TAUT_INI] = num_comp[TAUT_YES] - num_del[TAUT_YES]; - */ - - for ( i = 0, j=TAUT_YES; i < num_components; i ++ ) { - if ( all_inp_norm_data[i][j].bExists && - (all_inp_norm_data[i][j].bDeleted || - all_inp_norm_data[i][j].bTautomeric && - all_inp_norm_data[i][j].at_fixed_bonds && - all_inp_norm_data[i][j].bTautPreprocessed) ) { - num_comp[TAUT_INI] ++; - } - } - - } - /* count atoms and allocate composite atom data */ - for ( jj = 0; jj <= TAUT_INI; jj ++ ) { - num_at[jj] = num_inp_at[jj] = 0; - j = inchi_min (jj, TAUT_YES); - if ( num_comp[jj] ) { - for ( i = 0; i < num_components; i ++ ) { - if ( all_inp_norm_data[i][j].bDeleted ) - continue; - /* find k = the normaized structure index */ - if ( jj == TAUT_INI ) { - if ( all_inp_norm_data[i][j].bExists && - all_inp_norm_data[i][j].at_fixed_bonds ) { - k = j; - } else - if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted && - !all_inp_norm_data[i][j].bDeleted ) { - k = ALT_TAUT(j); - } else - if ( all_inp_norm_data[i][j].bExists ) { - k = j; - } else { - continue; - } - } else { - if ( all_inp_norm_data[i][j].bExists ) { - k = j; - } else - if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted) { - k = ALT_TAUT(j); - } else { - continue; - } - } - num_inp_at[jj] += all_inp_norm_data[i][k].num_at; /* all atoms including terminal H */ - num_at[jj] += all_inp_norm_data[i][k].num_at - all_inp_norm_data[i][k].num_removed_H; - } - if ( num_inp_at[jj] ) { - if ( !CreateCompAtomData( composite_norm_data+jj, num_inp_at[jj], num_components, jj == TAUT_INI ) ) - goto exit_error; - composite_norm_data[jj].num_removed_H = num_inp_at[jj] - num_at[jj]; - } - } - } - /* fill out composite atom */ - for ( jj = 0; jj <= TAUT_INI; jj ++, indicator <<= 1 ) { - j = inchi_min (jj, TAUT_YES); - if ( num_comp[jj] ) { - tot_num_at = 0; - tot_num_H = 0; - for ( i = 0; i < num_components; i ++ ) { - if ( all_inp_norm_data[i][j].bDeleted ) { - composite_norm_data[jj].nNumRemovedProtons += all_inp_norm_data[i][j].nNumRemovedProtons; - for ( n = 0; n < NUM_H_ISOTOPES; n ++ ) { - composite_norm_data[jj].nNumRemovedProtonsIsotopic[n] += all_inp_norm_data[i][j].nNumRemovedProtonsIsotopic[n]; - } - continue; - } - nNumRemovedProtons = 0; - k = TAUT_NUM; - /* find k = the normaized structure index */ - if ( jj == TAUT_INI ) { - if ( all_inp_norm_data[i][j].bExists && all_inp_norm_data[i][j].at_fixed_bonds ) { - k = j; - } else - if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists ) { - k = ALT_TAUT(j); - } else - if ( all_inp_norm_data[i][j].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted ) { - k = j; - } else { - continue; - } - } else { - if ( all_inp_norm_data[i][j].bExists ) { - k = j; - } else - if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted ) { - k = ALT_TAUT(j); - } else { - continue; - } - } - /* copy main atoms */ - cur_num_H = all_inp_norm_data[i][k].num_removed_H; /* number of terminal H atoms */ - cur_num_at = all_inp_norm_data[i][k].num_at - cur_num_H; /* number of all but explicit terminal H atoms */ - - if ( (tot_num_at + cur_num_at) > num_at[jj] || - (num_at[jj] + tot_num_H + cur_num_H) > num_inp_at[jj] ) { - goto exit_error; /* miscount */ - } - at = composite_norm_data[jj].at+tot_num_at; /* points to the 1st destination atom */ - at_from = (jj == TAUT_INI && k == TAUT_YES && all_inp_norm_data[i][k].at_fixed_bonds)? - all_inp_norm_data[i][k].at_fixed_bonds : all_inp_norm_data[i][k].at; - memcpy( at, at_from, sizeof(composite_norm_data[0].at[0]) * cur_num_at ); /* copy atoms except terminal H */ - /* shift neighbors of main atoms */ - for ( n = 0; n < cur_num_at; n ++, at ++ ) { - for ( m = 0; m < at->valence; m ++ ) { - at->neighbor[m] += tot_num_at; - } - } - /* copy explicit H */ - if ( cur_num_H ) { - at = composite_norm_data[jj].at+num_at[jj]+tot_num_H; /* points to the 1st destination atom */ - memcpy( at, at_from+cur_num_at, - sizeof(composite_norm_data[0].at[0]) * cur_num_H ); - /* shift neighbors of explicit H atoms */ - for ( n = 0; n < cur_num_H; n ++, at ++ ) { - for ( m = 0; m < at->valence; m ++ ) { - at->neighbor[m] += tot_num_at; - } - } - } - /* composite counts */ - composite_norm_data[jj].bHasIsotopicLayer |= all_inp_norm_data[i][k].bHasIsotopicLayer; - composite_norm_data[jj].num_isotopic += all_inp_norm_data[i][k].num_isotopic; - composite_norm_data[jj].num_bonds += all_inp_norm_data[i][k].num_bonds; - composite_norm_data[jj].bTautomeric += (j == jj) && all_inp_norm_data[i][k].bTautomeric; - composite_norm_data[jj].nNumRemovedProtons += all_inp_norm_data[i][k].nNumRemovedProtons; - for ( n = 0; n < NUM_H_ISOTOPES; n ++ ) { - composite_norm_data[jj].nNumRemovedProtonsIsotopic[n] += all_inp_norm_data[i][k].nNumRemovedProtonsIsotopic[n]; - composite_norm_data[jj].num_iso_H[n] += all_inp_norm_data[i][k].num_iso_H[n]; - } - /* - composite_norm_data[j].num_at += cur_num_at + cur_num_H; - composite_norm_data[j].num_removed_H += cur_num_H; - */ - /* total count */ - tot_num_at += cur_num_at; - tot_num_H += cur_num_H; - /* offset for the next component */ - if ( composite_norm_data[jj].nOffsetAtAndH ) { - composite_norm_data[jj].nOffsetAtAndH[2*i] = tot_num_at; - composite_norm_data[jj].nOffsetAtAndH[2*i+1] = num_at[jj]+tot_num_H; - } - } - if ( tot_num_at != num_at[jj] || - num_at[jj] + tot_num_H != num_inp_at[jj] ) { - goto exit_error; /* miscount */ - } - composite_norm_data[jj].bExists = (tot_num_at>0); - ret |= indicator; - } - } - return ret; - - - - - -exit_error: - return ret; -} -#endif /* } COMPILE_ANSI_ONLY */ - - diff --git a/INCHI-1-SRC/INCHI/common/sha2.c b/INCHI-1-SRC/INCHI/common/sha2.c deleted file mode 100644 index 0e78671..0000000 --- a/INCHI-1-SRC/INCHI/common/sha2.c +++ /dev/null @@ -1,456 +0,0 @@ -/* - * FIPS-180-2 compliant SHA-256 implementation - * - * Copyright (C) Brainspark B.V. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - - -/* - * The SHA-256 standard was published by NIST in 2002. - * - * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf - */ - -#ifndef _CRT_SECURE_NO_DEPRECATE -#define _CRT_SECURE_NO_DEPRECATE 1 -#endif - -#include -#include - -#include "sha2.h" - -/* - * 32-bit integer manipulation macros (big endian) - */ -#ifndef GET_UINT32_BE -#define GET_UINT32_BE(n,b,i) \ -{ \ - (n) = ( (unsigned long) (b)[(i) ] << 24 ) \ - | ( (unsigned long) (b)[(i) + 1] << 16 ) \ - | ( (unsigned long) (b)[(i) + 2] << 8 ) \ - | ( (unsigned long) (b)[(i) + 3] ); \ -} -#endif -#ifndef PUT_UINT32_BE -#define PUT_UINT32_BE(n,b,i) \ -{ \ - (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ - (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ - (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ - (b)[(i) + 3] = (unsigned char) ( (n) ); \ -} -#endif - -/* - * SHA-2 context setup - */ -void sha2_starts( sha2_context *ctx ) -{ - ctx->total[0] = 0; - ctx->total[1] = 0; - - ctx->state[0] = 0x6A09E667; - ctx->state[1] = 0xBB67AE85; - ctx->state[2] = 0x3C6EF372; - ctx->state[3] = 0xA54FF53A; - ctx->state[4] = 0x510E527F; - ctx->state[5] = 0x9B05688C; - ctx->state[6] = 0x1F83D9AB; - ctx->state[7] = 0x5BE0CD19; -} - -static void sha2_process( sha2_context *ctx, unsigned char data[64] ) -{ - unsigned long temp1, temp2, W[64]; - unsigned long A, B, C, D, E, F, G, H; - - GET_UINT32_BE( W[0], data, 0 ); - GET_UINT32_BE( W[1], data, 4 ); - GET_UINT32_BE( W[2], data, 8 ); - GET_UINT32_BE( W[3], data, 12 ); - GET_UINT32_BE( W[4], data, 16 ); - GET_UINT32_BE( W[5], data, 20 ); - GET_UINT32_BE( W[6], data, 24 ); - GET_UINT32_BE( W[7], data, 28 ); - GET_UINT32_BE( W[8], data, 32 ); - GET_UINT32_BE( W[9], data, 36 ); - GET_UINT32_BE( W[10], data, 40 ); - GET_UINT32_BE( W[11], data, 44 ); - GET_UINT32_BE( W[12], data, 48 ); - GET_UINT32_BE( W[13], data, 52 ); - GET_UINT32_BE( W[14], data, 56 ); - GET_UINT32_BE( W[15], data, 60 ); - -#define SHR(x,n) ((x & 0xFFFFFFFF) >> n) -#define ROTR(x,n) (SHR(x,n) | (x << (32 - n))) - -#define S0(x) (ROTR(x, 7) ^ ROTR(x,18) ^ SHR(x, 3)) -#define S1(x) (ROTR(x,17) ^ ROTR(x,19) ^ SHR(x,10)) - -#define S2(x) (ROTR(x, 2) ^ ROTR(x,13) ^ ROTR(x,22)) -#define S3(x) (ROTR(x, 6) ^ ROTR(x,11) ^ ROTR(x,25)) - -#define F0(x,y,z) ((x & y) | (z & (x | y))) -#define F1(x,y,z) (z ^ (x & (y ^ z))) - -#define R(t) \ -( \ - W[t] = S1(W[t - 2]) + W[t - 7] + \ - S0(W[t - 15]) + W[t - 16] \ -) - -#define P(a,b,c,d,e,f,g,h,x,K) \ -{ \ - temp1 = h + S3(e) + F1(e,f,g) + K + x; \ - temp2 = S2(a) + F0(a,b,c); \ - d += temp1; h = temp1 + temp2; \ -} - - A = ctx->state[0]; - B = ctx->state[1]; - C = ctx->state[2]; - D = ctx->state[3]; - E = ctx->state[4]; - F = ctx->state[5]; - G = ctx->state[6]; - H = ctx->state[7]; - - P( A, B, C, D, E, F, G, H, W[ 0], 0x428A2F98 ); - P( H, A, B, C, D, E, F, G, W[ 1], 0x71374491 ); - P( G, H, A, B, C, D, E, F, W[ 2], 0xB5C0FBCF ); - P( F, G, H, A, B, C, D, E, W[ 3], 0xE9B5DBA5 ); - P( E, F, G, H, A, B, C, D, W[ 4], 0x3956C25B ); - P( D, E, F, G, H, A, B, C, W[ 5], 0x59F111F1 ); - P( C, D, E, F, G, H, A, B, W[ 6], 0x923F82A4 ); - P( B, C, D, E, F, G, H, A, W[ 7], 0xAB1C5ED5 ); - P( A, B, C, D, E, F, G, H, W[ 8], 0xD807AA98 ); - P( H, A, B, C, D, E, F, G, W[ 9], 0x12835B01 ); - P( G, H, A, B, C, D, E, F, W[10], 0x243185BE ); - P( F, G, H, A, B, C, D, E, W[11], 0x550C7DC3 ); - P( E, F, G, H, A, B, C, D, W[12], 0x72BE5D74 ); - P( D, E, F, G, H, A, B, C, W[13], 0x80DEB1FE ); - P( C, D, E, F, G, H, A, B, W[14], 0x9BDC06A7 ); - P( B, C, D, E, F, G, H, A, W[15], 0xC19BF174 ); - P( A, B, C, D, E, F, G, H, R(16), 0xE49B69C1 ); - P( H, A, B, C, D, E, F, G, R(17), 0xEFBE4786 ); - P( G, H, A, B, C, D, E, F, R(18), 0x0FC19DC6 ); - P( F, G, H, A, B, C, D, E, R(19), 0x240CA1CC ); - P( E, F, G, H, A, B, C, D, R(20), 0x2DE92C6F ); - P( D, E, F, G, H, A, B, C, R(21), 0x4A7484AA ); - P( C, D, E, F, G, H, A, B, R(22), 0x5CB0A9DC ); - P( B, C, D, E, F, G, H, A, R(23), 0x76F988DA ); - P( A, B, C, D, E, F, G, H, R(24), 0x983E5152 ); - P( H, A, B, C, D, E, F, G, R(25), 0xA831C66D ); - P( G, H, A, B, C, D, E, F, R(26), 0xB00327C8 ); - P( F, G, H, A, B, C, D, E, R(27), 0xBF597FC7 ); - P( E, F, G, H, A, B, C, D, R(28), 0xC6E00BF3 ); - P( D, E, F, G, H, A, B, C, R(29), 0xD5A79147 ); - P( C, D, E, F, G, H, A, B, R(30), 0x06CA6351 ); - P( B, C, D, E, F, G, H, A, R(31), 0x14292967 ); - P( A, B, C, D, E, F, G, H, R(32), 0x27B70A85 ); - P( H, A, B, C, D, E, F, G, R(33), 0x2E1B2138 ); - P( G, H, A, B, C, D, E, F, R(34), 0x4D2C6DFC ); - P( F, G, H, A, B, C, D, E, R(35), 0x53380D13 ); - P( E, F, G, H, A, B, C, D, R(36), 0x650A7354 ); - P( D, E, F, G, H, A, B, C, R(37), 0x766A0ABB ); - P( C, D, E, F, G, H, A, B, R(38), 0x81C2C92E ); - P( B, C, D, E, F, G, H, A, R(39), 0x92722C85 ); - P( A, B, C, D, E, F, G, H, R(40), 0xA2BFE8A1 ); - P( H, A, B, C, D, E, F, G, R(41), 0xA81A664B ); - P( G, H, A, B, C, D, E, F, R(42), 0xC24B8B70 ); - P( F, G, H, A, B, C, D, E, R(43), 0xC76C51A3 ); - P( E, F, G, H, A, B, C, D, R(44), 0xD192E819 ); - P( D, E, F, G, H, A, B, C, R(45), 0xD6990624 ); - P( C, D, E, F, G, H, A, B, R(46), 0xF40E3585 ); - P( B, C, D, E, F, G, H, A, R(47), 0x106AA070 ); - P( A, B, C, D, E, F, G, H, R(48), 0x19A4C116 ); - P( H, A, B, C, D, E, F, G, R(49), 0x1E376C08 ); - P( G, H, A, B, C, D, E, F, R(50), 0x2748774C ); - P( F, G, H, A, B, C, D, E, R(51), 0x34B0BCB5 ); - P( E, F, G, H, A, B, C, D, R(52), 0x391C0CB3 ); - P( D, E, F, G, H, A, B, C, R(53), 0x4ED8AA4A ); - P( C, D, E, F, G, H, A, B, R(54), 0x5B9CCA4F ); - P( B, C, D, E, F, G, H, A, R(55), 0x682E6FF3 ); - P( A, B, C, D, E, F, G, H, R(56), 0x748F82EE ); - P( H, A, B, C, D, E, F, G, R(57), 0x78A5636F ); - P( G, H, A, B, C, D, E, F, R(58), 0x84C87814 ); - P( F, G, H, A, B, C, D, E, R(59), 0x8CC70208 ); - P( E, F, G, H, A, B, C, D, R(60), 0x90BEFFFA ); - P( D, E, F, G, H, A, B, C, R(61), 0xA4506CEB ); - P( C, D, E, F, G, H, A, B, R(62), 0xBEF9A3F7 ); - P( B, C, D, E, F, G, H, A, R(63), 0xC67178F2 ); - - ctx->state[0] += A; - ctx->state[1] += B; - ctx->state[2] += C; - ctx->state[3] += D; - ctx->state[4] += E; - ctx->state[5] += F; - ctx->state[6] += G; - ctx->state[7] += H; -} - -/* - * SHA-2 process buffer - */ -void sha2_update( sha2_context *ctx, unsigned char *input, int ilen ) -{ - int fill; - unsigned long left; - - if( ilen <= 0 ) - return; - - left = ctx->total[0] & 0x3F; - fill = 64 - left; - - ctx->total[0] += ilen; - ctx->total[0] &= 0xFFFFFFFF; - - if( ctx->total[0] < (unsigned long) ilen ) - ctx->total[1]++; - - if( left && ilen >= fill ) - { - memcpy( (void *) (ctx->buffer + left), - (void *) input, fill ); - sha2_process( ctx, ctx->buffer ); - input += fill; - ilen -= fill; - left = 0; - } - - while( ilen >= 64 ) - { - sha2_process( ctx, input ); - input += 64; - ilen -= 64; - } - - if( ilen > 0 ) - { - memcpy( (void *) (ctx->buffer + left), - (void *) input, ilen ); - } -} - -static const unsigned char sha2_padding[64] = -{ - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -/* - * SHA-2 final digest - */ -void sha2_finish( sha2_context *ctx, unsigned char output[32] ) -{ - unsigned long last, padn; - unsigned long high, low; - unsigned char msglen[8]; - - high = ( ctx->total[0] >> 29 ) - | ( ctx->total[1] << 3 ); - low = ( ctx->total[0] << 3 ); - - PUT_UINT32_BE( high, msglen, 0 ); - PUT_UINT32_BE( low, msglen, 4 ); - - last = ctx->total[0] & 0x3F; - padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); - - sha2_update( ctx, (unsigned char *) sha2_padding, padn ); - sha2_update( ctx, msglen, 8 ); - - PUT_UINT32_BE( ctx->state[0], output, 0 ); - PUT_UINT32_BE( ctx->state[1], output, 4 ); - PUT_UINT32_BE( ctx->state[2], output, 8 ); - PUT_UINT32_BE( ctx->state[3], output, 12 ); - PUT_UINT32_BE( ctx->state[4], output, 16 ); - PUT_UINT32_BE( ctx->state[5], output, 20 ); - PUT_UINT32_BE( ctx->state[6], output, 24 ); - PUT_UINT32_BE( ctx->state[7], output, 28 ); -} - -/* - * Output = SHA-2( file contents ) - */ -int sha2_file( char *path, unsigned char output[32] ) -{ - FILE *f; - size_t n; - sha2_context ctx; - unsigned char buf[1024]; - - if( ( f = fopen( path, "rb" ) ) == NULL ) - return( 1 ); - - sha2_starts( &ctx ); - - while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) - sha2_update( &ctx, buf, (int) n ); - - sha2_finish( &ctx, output ); - - fclose( f ); - return( 0 ); -} - -/* - * Output = SHA-2( input buffer ) - */ -void sha2_csum( unsigned char *input, int ilen, - unsigned char output[32] ) -{ - sha2_context ctx; - - sha2_starts( &ctx ); - sha2_update( &ctx, input, ilen ); - sha2_finish( &ctx, output ); -} - -/* - * Output = HMAC-SHA-2( input buffer, hmac key ) - */ -void sha2_hmac( unsigned char *key, int keylen, - unsigned char *input, int ilen, - unsigned char output[32] ) -{ - int i; - sha2_context ctx; - unsigned char k_ipad[64]; - unsigned char k_opad[64]; - unsigned char tmpbuf[32]; - - memset( k_ipad, 0x36, 64 ); - memset( k_opad, 0x5C, 64 ); - - for( i = 0; i < keylen; i++ ) - { - if( i >= 64 ) break; - - k_ipad[i] ^= key[i]; - k_opad[i] ^= key[i]; - } - - sha2_starts( &ctx ); - sha2_update( &ctx, k_ipad, 64 ); - sha2_update( &ctx, input, ilen ); - sha2_finish( &ctx, tmpbuf ); - - sha2_starts( &ctx ); - sha2_update( &ctx, k_opad, 64 ); - sha2_update( &ctx, tmpbuf, 32 ); - sha2_finish( &ctx, output ); - - memset( k_ipad, 0, 64 ); - memset( k_opad, 0, 64 ); - memset( tmpbuf, 0, 32 ); - memset( &ctx, 0, sizeof( sha2_context ) ); -} - -#ifdef SELF_TEST -/* - * FIPS-180-2 test vectors - */ -static const char sha2_test_str[3][57] = -{ - { "abc" }, - { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" }, - { "" } -}; - -static const unsigned char sha2_test_sum[3][32] = -{ - { 0xBA, 0x78, 0x16, 0xBF, 0x8F, 0x01, 0xCF, 0xEA, - 0x41, 0x41, 0x40, 0xDE, 0x5D, 0xAE, 0x22, 0x23, - 0xB0, 0x03, 0x61, 0xA3, 0x96, 0x17, 0x7A, 0x9C, - 0xB4, 0x10, 0xFF, 0x61, 0xF2, 0x00, 0x15, 0xAD }, - { 0x24, 0x8D, 0x6A, 0x61, 0xD2, 0x06, 0x38, 0xB8, - 0xE5, 0xC0, 0x26, 0x93, 0x0C, 0x3E, 0x60, 0x39, - 0xA3, 0x3C, 0xE4, 0x59, 0x64, 0xFF, 0x21, 0x67, - 0xF6, 0xEC, 0xED, 0xD4, 0x19, 0xDB, 0x06, 0xC1 }, - { 0xCD, 0xC7, 0x6E, 0x5C, 0x99, 0x14, 0xFB, 0x92, - 0x81, 0xA1, 0xC7, 0xE2, 0x84, 0xD7, 0x3E, 0x67, - 0xF1, 0x80, 0x9A, 0x48, 0xA4, 0x97, 0x20, 0x0E, - 0x04, 0x6D, 0x39, 0xCC, 0xC7, 0x11, 0x2C, 0xD0 } -}; - -/* - * Checkup routine - */ -int sha2_self_test( void ) -{ - int i, j; - unsigned char buf[1000]; - unsigned char sha2sum[32]; - sha2_context ctx; - - for( i = 0; i < 3; i++ ) - { - printf( " SHA-256 test #%d: ", i + 1 ); - - sha2_starts( &ctx ); - - if( i < 2 ) - sha2_update( &ctx, (unsigned char *) sha2_test_str[i], - strlen( sha2_test_str[i] ) ); - else - { - memset( buf, 'a', 1000 ); - for( j = 0; j < 1000; j++ ) - sha2_update( &ctx, buf, 1000 ); - } - - sha2_finish( &ctx, sha2sum ); - - if( memcmp( sha2sum, sha2_test_sum[i], 20 ) != 0 ) - { - printf( "failed\n" ); - return( 1 ); - } - - printf( "passed\n" ); - } - - printf( "\n" ); - return( 0 ); -} -#else -int sha2_self_test( void ) -{ - return( 0 ); -} -#endif - diff --git a/INCHI-1-SRC/INCHI/common/sha2.h b/INCHI-1-SRC/INCHI/common/sha2.h deleted file mode 100644 index 67a6f4a..0000000 --- a/INCHI-1-SRC/INCHI/common/sha2.h +++ /dev/null @@ -1,134 +0,0 @@ -/** - * \file sha2.h - */ - - - - -/* - * FIPS-180-2 compliant SHA-256 implementation - * - * Copyright (C) Brainspark B.V. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -/* - * The SHA-256 standard was published by NIST in 2002. - * - * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf - */ - -#ifndef _SHA2_H -#define _SHA2_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief SHA-256 context structure - */ -typedef struct -{ - unsigned long total[2]; /*!< number of bytes processed */ - unsigned long state[8]; /*!< intermediate digest state */ - unsigned char buffer[64]; /*!< data block being processed */ -} -sha2_context; - -/** - * \brief SHA-256 context setup - * - * \param ctx SHA-256 context to be initialized - */ -void sha2_starts( sha2_context *ctx ); - -/** - * \brief SHA-256 process buffer - * - * \param ctx SHA-256 context - * \param input buffer holding the data - * \param ilen length of the input data - */ -void sha2_update( sha2_context *ctx, unsigned char *input, int ilen ); - -/** - * \brief SHA-256 final digest - * - * \param ctx SHA-256 context - * \param output SHA-256 checksum result - */ -void sha2_finish( sha2_context *ctx, unsigned char output[32] ); - -/** - * \brief Output = SHA-256( input buffer ) - * - * \param input buffer holding the data - * \param ilen length of the input data - * \param output SHA-256 checksum result - */ -void sha2_csum( unsigned char *input, int ilen, - unsigned char output[32] ); - -/** - * \brief Output = SHA-256( file contents ) - * - * \param path input file name - * \param output SHA-256 checksum result - * \return 0 if successful, or 1 if fopen failed - */ -int sha2_file( char *path, unsigned char output[32] ); - -/** - * \brief Output = HMAC-SHA-256( input buffer, hmac key ) - * - * \param key HMAC secret key - * \param keylen length of the HMAC key - * \param input buffer holding the data - * \param ilen length of the input data - * \param output HMAC-SHA-256 result - */ -void sha2_hmac( unsigned char *key, int keylen, - unsigned char *input, int ilen, - unsigned char output[32] ); - -/** - * \brief Checkup routine - * - * \return 0 if successful, or 1 if the test failed - */ -int sha2_self_test( void ); - -#ifdef __cplusplus -} -#endif - -#endif /* sha2.h */ - diff --git a/INCHI-1-SRC/INCHI/common/strutil.h b/INCHI-1-SRC/INCHI/common/strutil.h deleted file mode 100644 index 9e17f99..0000000 --- a/INCHI-1-SRC/INCHI/common/strutil.h +++ /dev/null @@ -1,410 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __STRUTIL_H__ -#define __STRUTIL_H__ - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -/* forward declaration */ -struct tagTautomerGroupsInfo; - -int ExtractConnectedComponent(inp_ATOM *at, int num_at, - int component_number, - inp_ATOM *component_at ); -int SetConnectedComponentNumber(inp_ATOM *at, int num_at, int component_number ); - -INChI *Alloc_INChI(inp_ATOM *at, int num_at, int *found_num_bonds, - int *found_num_isotopic, int nAllocMode ); -int Free_INChI(INChI **ppINChI); -int Free_INChI_Members(INChI *pINChI); -int Free_INChI_Stereo( INChI_Stereo *pINChI_Stereo ); -INChI_Aux *Alloc_INChI_Aux(int num_at, int num_isotopic_atoms, - int nAllocMode, int bOrigData ); -int Free_INChI_Aux(INChI_Aux **ppINChI_Aux ); - -int Create_INChI(INChI **ppINChI, - INChI_Aux **ppINChI_Aux, - ORIG_ATOM_DATA *orig_inp_data, - inp_ATOM *inp_at, - INP_ATOM_DATA *inp_norm_data[2], - int num_inp_at, - INCHI_MODE nUserMode, - INCHI_MODE *pbTautFlags, INCHI_MODE *pbTautFlagsDone, - struct tagInchiTime *ulMaxTime, - struct tagTautomerGroupsInfo *ti_out, - char *pStrErrStruct); - -int FillOutInfAtom(inp_ATOM *norm_at, - INF_ATOM_DATA *inf_norm_at_data, - int init_num_at, - int num_removed_H, int bAdd_DT_to_num_H, - int nNumRemovedProtons, NUM_H *nNumRemovedProtonsIsotopic, - int bIsotopic, - INChI *pINChI, - INChI_Aux *pINChI_Aux, - int bAbcNumbers, - INCHI_MODE nMode ); - -int FillOutCompositeCanonInfAtom(COMP_ATOM_DATA *composite_norm_data, - INF_ATOM_DATA *inf_norm_at_data, - int bIsotopic, int bTautomeric, - PINChI2 *pINChI2, - PINChI_Aux2 *pINChI_Aux2, - int bAbcNumbers, - INCHI_MODE nMode); - -#if ( FIX_DALKE_BUGS == 1 ) -char *AllocateAndFillHillFormula(INChI *pINChI ); -#endif - -typedef enum tagInchiDiffBits -{ - IDIF_PROBLEM = 0x00000001, /* severe: at least one InChI does not exist */ - IDIF_NUM_AT = 0x00000001, /* severe: different number of atoms in the skeleton */ - IDIF_ATOMS = 0x00000001, /* severe: diiferent types of skeleton atoms */ - IDIF_NUM_EL = 0x00000001, /* severe: formulas differ in another element */ - IDIF_CON_LEN = 0x00000001, /* severe: different connection table lengths */ - IDIF_CON_TBL = 0x00000001, /* severe: different connection tables */ - IDIF_POSITION_H = 0x00000002, /* difference in non-taut (Mobile-H) or all H (Fixed-H) location/number */ - IDIF_MORE_FH = 0x00000004, /* extra fixed H */ - IDIF_LESS_FH = 0x00000008, /* missing fixed H */ - IDIF_MORE_H = 0x00000010, /* formulas differ in number of H */ - IDIF_LESS_H = 0x00000020, /* formulas differ in number of H */ - /*IDIF_TAUT_LEN = 0x00000008,*/ /* different lengths of tautomer lists */ - IDIF_NO_TAUT = 0x00000040, /* restored structure has no taut groups while the original InChI has some */ - IDIF_WRONG_TAUT = 0x00000080, /* restored has tautomerism while the original does not have it */ - IDIF_SINGLE_TG = 0x00000100, /* restored has 1 taut. group while the original InChI has multiple tg */ - IDIF_MULTIPLE_TG = 0x00000200, /* restored has multiple tg while the original InChI has only one tg */ - IDIF_NUM_TG = 0x00000400, /* different number of tautomeric groups */ - /*IDIF_LESS_TG_ENDP = 0x00000200,*/ /* restores structure has less taut. endpoints */ - /*IDIF_MORE_TG_ENDP = 0x00000400,*/ /* restores structure has more taut. endpoints */ - IDIF_EXTRA_TG_ENDP = 0x00000800, /* extra tautomeric endpoint(s) in restored structure */ - IDIF_MISS_TG_ENDP = 0x00001000, /* one or more tg endpoint is not in the restored structure */ - IDIF_DIFF_TG_ENDP = 0x00002000, /* lists of tg endpoints are different */ - IDIF_TG = 0x00004000, /* different tautomeric groups */ - IDIF_NUM_ISO_AT = 0x00008000, /* ?severe: restored struct. has different number of isotopic atoms */ - IDIF_ISO_AT = 0x00010000, /* ?severe: restored struct. has different locations/isotopes of isotopic atoms */ - IDIF_CHARGE = 0x00020000, /* restored structure has different charge */ - IDIF_REM_PROT = 0x00040000, /* proton(s) removed/added from the restored structure */ - IDIF_REM_ISO_H = 0x00080000, /* isotopic H removed */ - IDIF_SC_INV = 0x00100000, /* restores structure has different inversion stereocenter mark */ - IDIF_SC_PARITY = 0x00200000, /* restored structure has stereoatoms or allenes with different parity */ - IDIF_SC_EXTRA_UNDF = 0x00400000, /* restored structure has extra undefined stereocenter(s) */ - IDIF_SC_EXTRA = 0x00800000, /* restored structure has extra stereocenter(s) */ - IDIF_SC_MISS_UNDF = 0x01000000, /* restored structure has not some undefined stereocenter(s) */ - IDIF_SC_MISS = 0x02000000, /* restored structure has not some stereocenters that are not undefined */ - IDIF_SB_PARITY = 0x04000000, /* restored structure has stereobonds or cumulenes with different parity */ - IDIF_SB_EXTRA_UNDF = 0x08000000, /* restored structure has extra undefined stereobond(s) */ - IDIF_SB_EXTRA = 0x10000000, /* restored structure has extra stereobond(s) */ - IDIF_SB_MISS_UNDF = 0x20000000, /* restored structure has not some undefined stereocenters */ - IDIF_SB_MISS = 0x40000000 /* restored structure has not some stereobonds that are not undefined */ -} IDIF; - - -#define IDIFF_SB (IDIF_SB_PARITY | IDIF_SB_EXTRA_UNDF | IDIF_SB_EXTRA | IDIF_SB_MISS_UNDF | IDIF_SB_MISS) -#define IDIFF_SC (IDIF_SC_PARITY | IDIF_SC_EXTRA_UNDF | IDIF_SC_EXTRA | IDIF_SC_MISS_UNDF | IDIF_SC_MISS) - -#define IDIFF_CONSTIT (IDIF_POSITION_H | IDIF_MORE_FH | IDIF_LESS_FH | IDIF_MORE_H | IDIF_LESS_H |\ - IDIF_NO_TAUT | IDIF_WRONG_TAUT | IDIF_SINGLE_TG | IDIF_MULTIPLE_TG | \ - IDIF_NUM_TG | IDIF_EXTRA_TG_ENDP | IDIF_MISS_TG_ENDP | IDIF_TG | \ - IDIF_NUM_ISO_AT | IDIF_ISO_AT | IDIF_CHARGE | IDIF_REM_PROT | IDIF_REM_ISO_H |\ - IDIF_DIFF_TG_ENDP) -#define IDIFF_STEREO (IDIF_SC_INV | IDIF_SC_PARITY | IDIF_SC_EXTRA_UNDF | IDIF_SC_EXTRA | \ - IDIF_SC_MISS_UNDF | IDIF_SC_MISS | IDIF_SB_PARITY | IDIF_SB_EXTRA_UNDF |\ - IDIF_SB_EXTRA | IDIF_SB_MISS_UNDF | IDIF_SB_MISS) - - -/*************************************************************************************/ -#define ICR_MAX_ENDP_IN1_ONLY 32 -#define ICR_MAX_ENDP_IN2_ONLY 32 -#define ICR_MAX_DIFF_FIXED_H 32 -#define ICR_MAX_SB_IN1_ONLY 32 -#define ICR_MAX_SB_IN2_ONLY 32 -#define ICR_MAX_SC_IN1_ONLY 32 -#define ICR_MAX_SC_IN2_ONLY 32 -#define ICR_MAX_SB_UNDF 32 -#define ICR_MAX_SC_UNDF 32 - -typedef struct tagInChICompareResults -{ - INCHI_MODE flags; - - int tot_num_H1; - int tot_num_H2; - int num_taut_H1; - int num_taut_H2; - int num_taut_M1; - int num_taut_M2; - - /* 1 => InChI from reversed struct. 2 => input InChI */ - - AT_NUMB endp_in1_only[ICR_MAX_ENDP_IN1_ONLY]; /* endpoint canonical number = index+1 */ - int num_endp_in1_only; - - AT_NUMB endp_in2_only[ICR_MAX_ENDP_IN2_ONLY]; /* endpoint canonical number = index+1 */ - int num_endp_in2_only; - - AT_NUMB diff_pos_H_at[ICR_MAX_DIFF_FIXED_H]; /* non-tautomeric H */ - S_CHAR diff_pos_H_nH[ICR_MAX_DIFF_FIXED_H]; - int num_diff_pos_H; - - AT_NUMB fixed_H_at1_more[ICR_MAX_DIFF_FIXED_H]; /* extra fixed_H */ - S_CHAR fixed_H_nH1_more[ICR_MAX_DIFF_FIXED_H]; - int num_fixed_H1_more; - - AT_NUMB fixed_H_at2_more[ICR_MAX_DIFF_FIXED_H]; /* missed fixed_H */ - S_CHAR fixed_H_nH2_more[ICR_MAX_DIFF_FIXED_H]; - int num_fixed_H2_more; - - AT_NUMB sc_in1_only[ICR_MAX_SC_IN1_ONLY]; - int num_sc_in1_only; - AT_NUMB sc_in2_only[ICR_MAX_SC_IN2_ONLY]; - int num_sc_in2_only; - - AT_NUMB sb_in1_only[ICR_MAX_SB_IN1_ONLY]; - int num_sb_in1_only; - AT_NUMB sb_in2_only[ICR_MAX_SB_IN2_ONLY]; - int num_sb_in2_only; - - AT_NUMB sb_undef_in1_only[ICR_MAX_SC_UNDF]; - int num_sb_undef_in1_only; - AT_NUMB sb_undef_in2_only[ICR_MAX_SC_UNDF]; - int num_sb_undef_in2_only; - - AT_NUMB sc_undef_in1_only[ICR_MAX_SB_UNDF]; - int num_sc_undef_in1_only; - AT_NUMB sc_undef_in2_only[ICR_MAX_SB_UNDF]; - int num_sc_undef_in2_only; - -} ICR; /* tagInChICompareResults */ - - - -INCHI_MODE CompareReversedINChI2(INChI *i1 /* InChI from reversed struct */, - INChI *i2 /* input InChI */, - INChI_Aux *a1, INChI_Aux *a2, - ICR *picr, int *err ); -int CompareIcr(ICR *picr1, ICR *picr2, INCHI_MODE *pin1, INCHI_MODE *pin2, INCHI_MODE mask ); - -int CompareReversedINChI(INChI *i1, INChI *i2, INChI_Aux *a1, INChI_Aux *a2 ); -const char *CompareReversedInchiMsg(int code); - -#define EQL_EXISTS 1 -#define EQL_SP3 2 -#define EQL_SP3_INV 4 -#define EQL_SP2 8 - -int Eql_INChI_Stereo(INChI_Stereo *s1, int eql1, INChI_Stereo *s2, int eql2, int bRelRac ); -int Eql_INChI_Isotopic(INChI *i1, INChI *i2 ); - -#define EQL_EQU 0 -#define EQL_EQU_TG 1 -#define EQL_EQU_ISO 2 - -int Eql_INChI_Aux_Equ(INChI_Aux *a1, int eql1, INChI_Aux *a2, int eql2 ); - -#define EQL_NUM 0 -#define EQL_NUM_INV 1 -#define EQL_NUM_ISO 2 -int Eql_INChI_Aux_Num(INChI_Aux *a1, int eql1, INChI_Aux *a2, int eql2 ); - -int bHasEquString(AT_NUMB *LinearCT, int nLenCT ); - -int CompINChINonTaut2(const void *p1, const void *p2); -int CompINChITaut2(const void *p1, const void *p2); -int CompINChI2(const INCHI_SORT *p1, const INCHI_SORT *p2, int bTaut, int bCompareIsotopic); -int CompINChITautVsNonTaut(const INCHI_SORT *p1, const INCHI_SORT *p2, int bCompareIsotopic); - -typedef enum tagDiffINChISegments -{ /* r = repetitive, n = non-repetitive */ - DIFS_f_FORMULA, /* 0 r; fixed-H <-> mobile-H */ - DIFS_c_CONNECT, /* 1 n; connection table; mobile-H only */ - DIFS_h_H_ATOMS, /* 2 n; hydrogen atoms: mobile-H and Fixed-H; have different meanings */ - DIFS_q_CHARGE, /* 3 r; charge; fixed-H <-> mobile-H */ - DIFS_p_PROTONS, /* 4 n; protons; mobile-H only */ - DIFS_b_SBONDS, /* 5 r: stereobonds: fixed-H <-> mobile-H * isotopic <-> non-isotopic */ - DIFS_t_SATOMS, /* 6 r: stereoatoms: fixed-H <-> mobile-H * isotopic <-> non-isotopic */ - DIFS_m_SP3INV, /* 7 r: stereo-abs-inv: fixed-H <-> mobile-H * isotopic <-> non-isotopic */ - DIFS_s_STYPE, /* 8 r: stereo-type: fixed-H <-> mobile-H * isotopic <-> non-isotopic */ - DIFS_i_IATOMS, /* 9 r: isotopic atoms: fixed-H <-> mobile-H * isotopic <-> non-isotopic */ - DIFS_o_TRANSP, /* 10 n: Fixed-H transposition */ - DIFS_idf_LENGTH, /* 11 length of the array relevant to the INChI Identifier */ - /* later elements referring to AuxInfo may be added */ - DIFS_LENGTH = DIFS_idf_LENGTH /* length of the array */ -} DIF_SEGMENTS; - -typedef enum tagDiffINChILayers -{ - DIFL_M, /* 0 main layer */ - DIFL_MI, /* 1 main isotopic */ - DIFL_F, /* 2 fixed-H */ - DIFL_FI, /* 3 fixed-H isotopic */ - DIFL_LENGTH /* number of layers */ -} DIF_LAYERS; - -/* Value meaning */ -typedef enum tagMarkDiff -{ - DIFV_BOTH_EMPTY = 0, /* both this and the component in the preceding namesake segment are empty */ - DIFV_EQL2PRECED = 1, /* equal to the component in the preceding namesake segment */ - DIFV_NEQ2PRECED = 2, /* different from the component in the preceding namesake segment */ - DIFV_IS_EMPTY = 4, /* is empty while the preceding namesake segment is not empty */ - DIFV_FI_EQ_MI = 8, /* FI stereo component is equal to the component in the MI namesake segment */ - /* while M and F components are empty */ - /* decision_F = bitmask: bits that should not be present */ - /* decision_T = bitmask: at least one of the bits should be present */ - /* decision = true if( !( BITS & decision_F ) && ( BITS & decision_F ) ) */ - DIFV_OUTPUT_EMPTY_T = (DIFV_IS_EMPTY), /* bits present for empty segment output */ - DIFV_OUTPUT_EMPTY_F = (DIFV_EQL2PRECED | DIFV_NEQ2PRECED | DIFV_FI_EQ_MI), /* bits NOT present */ - - DIFV_OUTPUT_OMIT_F = (DIFV_NEQ2PRECED | DIFV_IS_EMPTY), /* bits NOT present for omitting */ - - DIFV_OUTPUT_FILL_T = (DIFV_EQL2PRECED | DIFV_NEQ2PRECED | DIFV_FI_EQ_MI) - -} DIF_VALUES; - -typedef enum tagINChISegmAction -{ - INCHI_SEGM_OMIT = 0, - INCHI_SEGM_FILL = 1, /* the value is used in str_LineEnd() */ - INCHI_SEGM_EMPTY = 2 /* the value is used in str_LineEnd() */ -} INCHI_SEGM_ACTION; - -int CompINChILayers(const INCHI_SORT *p1, const INCHI_SORT *p2, - char sDifSegs[][DIFS_LENGTH], int bFixTranspChargeBug ); -int MarkUnusedAndEmptyLayers( char sDifSegs[][DIFS_LENGTH] ); -int INChI_SegmentAction( char cDifSegs ); - -#define FLAG_SORT_PRINT_TRANSPOS_BAS 1 /* transposition in the main InChI layer */ -#define FLAG_SORT_PRINT_TRANSPOS_REC 2 /* transposition in the reconnected InChI layer */ -#define FLAG_SORT_PRINT_NO_NFIX_H_BAS 4 /* no fixed H non-isotopic in the main InChI layer */ -#define FLAG_SORT_PRINT_NO_NFIX_H_REC 8 /* no fixed H non-isotopic in the reconnected InChI layer */ -#define FLAG_SORT_PRINT_NO_IFIX_H_BAS 16 /* no fixed H isotopic in the main InChI layer */ -#define FLAG_SORT_PRINT_NO_IFIX_H_REC 32 /* no fixed H isotopic in the the reconnected InChI layer */ -#define FLAG_SORT_PRINT_ReChI_PREFIX 64 /* Output ReChI instead of InChI */ - -int OutputINChI1(char *pStr, int nStrLen, - INCHI_SORT *pINChISortTautAndNonTaut2[][TAUT_NUM], - int iINChI, - ORIG_STRUCT *pOrigStruct, - int bDisconnectedCoord, int bOutputType, int bINChIOutputOptions, - int bXml, int bAbcNumbers,int bCtPredecessors, int bNoStructLabels, - int num_components2[], - int num_non_taut2[], int num_taut2[], - INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *log_file, - int num_input_struct, - const char *szSdfLabel, const char *szSdfValue, long lSdfId, - int *pSortPrintINChIFlags, - unsigned char save_opt_bits); - -int OutputINChI2(char *pStr, int nStrLen, - INCHI_SORT *pINChISortTautAndNonTaut2[][TAUT_NUM], - int iINChI, - ORIG_STRUCT *pOrigStruct, - int bDisconnectedCoord, int bOutputType, int bINChIOutputOptions, - int bXml, int bAbcNumbers,int bCtPredecessors, int bNoStructLabels, - int num_components2[], - int num_non_taut2[], int num_taut2[], - INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *log_file, - int num_input_struct, - const char *szSdfLabel, const char *szSdfValue, long lSdfId, - int *pSortPrintINChIFlags, - unsigned char save_opt_bits); - -int SaveEquComponentsInfoAndSortOrder(int iINChI, - INCHI_SORT *pINChISort[TAUT_NUM], - int *num_components, - ORIG_ATOM_DATA *orig_inp_data, - ORIG_ATOM_DATA *prep_inp_data, - COMP_ATOM_DATA composite_norm_data[TAUT_NUM], - int bCompareComponents ); -int OutputINChIXmlRootStartTag( INCHI_IOSTREAM *output_file ); -int OutputINChIXmlRootEndTag( INCHI_IOSTREAM *output_file ); -int OutputINChIXmlError( INCHI_IOSTREAM *output_file, char *pStr, int nStrLen, int ind, - /*int nErrorNumber,*/ char *szErrorText, int bError ); -int OutputINChIPlainError( INCHI_IOSTREAM *output_file, char *pStr, int nStrLen, - char *pErrorText, int bError ); -int OutputINChIXmlStructStartTag( INCHI_IOSTREAM *output_file, char *pStr, int ind /* indent*/, - int nStrLen, int bNoStructLabels, - int num_input_struct, const char *szSdfLabel, const char *szSdfValue ); -int OutputINChIXmlStructEndTag( INCHI_IOSTREAM *output_file, char *pStr, int nStrLen, int ind ); - -int GetInpStructErrorType(INPUT_PARMS *ip, int err, char *pStrErrStruct, int num_inp_atoms ); -int ProcessStructError( INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *log_file, /*int err,*/ - char *pStrErrStruct, int nErrorType, - int *bXmlStructStarted, long num_inp, INPUT_PARMS *ip, char *pStr, int nStrLen ); - -int bNumHeterAtomHasIsotopicH( inp_ATOM *atom, int num_atoms ); - -int WriteToSDfile( const INP_ATOM_DATA *inp_at_data, INCHI_IOSTREAM* fcb, const char* name, const char* comment, - const char *szLabel, const char *szValue ); -int WriteOrigAtomDataToSDfile( const ORIG_ATOM_DATA *inp_at_data, INCHI_IOSTREAM * fcb, const char* name, - const char* comment, int bChiral, int bAtomsDT, const char *szLabel, const char *szValue); -int bIsMetalSalt( inp_ATOM *at, int i ); - - -extern const char gsMissing[]; -extern const char gsEmpty[]; -extern const char gsSpace[]; -extern const char gsEqual[]; -/* -#define gsMissing "is missing" -#define gsEmpty "" -#define gsSpace " " -#define gsEqual "=" -*/ -/* format string for SDF_LBL_VAL(L,V): %s%s%s%s (four strings) */ -/*#define SDF_LBL_VAL(L,V) ((L)&&(L)[0])?gsSpace:gsEmpty, ((L)&&(L)[0])?L:gsEmpty, ((L)&&(L)[0])? (((V)&&(V)[0])?gsEqual:gsSpace):gsEmpty, ((L)&&(L)[0])?((V)&&(V)[0]?V:gsMissing):gsEmpty*/ -#define SDF_LBL_VAL(L,V) ((L)&&(L)[0])?gsSpace:gsEmpty, ((L)&&(L)[0])?L:gsEmpty, ((L)&&(L)[0])? (((V)&&(V)[0])?gsEqual:gsSpace):gsEmpty, ((V)&&(V)[0])?V:((L)&&(L)[0])?gsMissing:gsEmpty - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /* __STRUTIL_H__ */ - diff --git a/INCHI-1-SRC/INCHI/common/util.c b/INCHI-1-SRC/INCHI/common/util.c deleted file mode 100644 index d5c8f2f..0000000 --- a/INCHI-1-SRC/INCHI/common/util.c +++ /dev/null @@ -1,1173 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include - -#include "mode.h" - -#include "incomdef.h" -#include "inpdef.h" -#include "util.h" -#include "extr_ct.h" - - -#include "ichicomp.h" - -#define MIN_ATOM_CHARGE (-2) -#define MAX_ATOM_CHARGE 2 -#define NEUTRAL_STATE (-MIN_ATOM_CHARGE) -#define NUM_ATOM_CHARGES (MAX_ATOM_CHARGE - MIN_ATOM_CHARGE + 1) -#define MAX_NUM_VALENCES 5 /* max. number + 1 to provide zero termination */ - -typedef struct tagElData { - const char *szElName; - int nAtMass; /* Avg. atomic mass from the Periodic Chart of the Elements (Fisher cat. no. 05-702-10) */ - int nNormAtMass; /* Atomic mass of the most abundant isotope */ - double dAtMass; /* exact mw of the most abundant isotope */ - int nType; /* METAL or METAL2 */ - int nElNegPauling10; /* Pauling electronegativity x 10; 0 => unknown */ - int bDoNotAddH; /* InChI does not add implicit H to atoms that have bDoNotAddH != 0 */ - S_CHAR cValence[NUM_ATOM_CHARGES][MAX_NUM_VALENCES]; -} ELDATA; - -/* 2004=05-10: Added valences {1,3,5,7,} for As(2-) */ - -const ELDATA ElData[] = { -/* avg norm El No -------- Valence(s) of an ion or neutral atom -------------*/ -/* mw mass exact mw type neg H -2 -1 0 +1 +2 */ -{ "H", 1, 1, 1.007825035, 0 , 21, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -{ "D", 2, 2, 2.014101778, 0 , 21, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -{ "T", 3, 3, 3.016049268, 0 , 21, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -{ "He", 4, 4, 4.002600000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, -{ "Li", 7, 7, 7.016000000, METAL , 10, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -{ "Be", 9, 9, 9.012180000, METAL , 15, 0, {{0,}, {0,}, {2,}, {1,}, {0,} }}, -{ "B", 11, 11, 11.009300000, 0 , 20, 0, {{3,}, {4,}, {3,}, {2,}, {1,} }}, -{ "C", 12, 12, 12.000000000, 0 , 25, 0, {{2,}, {3,}, {4,}, {3,}, {2,} }}, -{ "N", 14, 14, 14.003074000, 0 , 30, 0, {{1,}, {2,}, {3,5}, {4,}, {3,} }}, -{ "O", 16, 16, 15.994914630, 0 , 35, 0, {{0,}, {1,}, {2,}, {3,5,}, {4,} }}, -{ "F", 19, 19, 18.998403220, 0 , 40, 0, {{0,}, {0,}, {1,}, {2,}, {3,5} }}, -{ "Ne", 20, 20, 19.992440000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, -{ "Na", 23, 23, 22.989770000, METAL , 9, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -{ "Mg", 24, 24, 23.985000000, METAL , 12, 0, {{0,}, {0,}, {2,}, {1,}, {0,} }}, -{ "Al", 27, 27, 26.981540000, METAL , 15, 0, {{3,5,}, {4,}, {3,}, {2,}, {1,} }}, -{ "Si", 28, 28, 27.976927100, 0 , 18, 0, {{2,}, {3,5}, {4,}, {3,}, {2,} }}, -{ "P", 31, 31, 30.973762000, 0 , 21, 0, {{1,3,5,7,}, {2,4,6,}, {3,5,}, {4,}, {3,} }}, -{ "S", 32, 32, 31.972070700, 0 , 25, 0, {{0,}, {1,3,5,7,}, {2,4,6}, {3,5,}, {4,} }}, -{ "Cl", 35, 35, 34.968852730, 0 , 30, 0, {{0,}, {0,}, {1,3,5,7}, {2,4,6}, {3,5,} }}, -{ "Ar", 40, 40, 39.962400000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, -{ "K", 39, 39, 38.963700000, METAL , 8, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -{ "Ca", 40, 40, 39.962600000, METAL , 10, 0, {{0,}, {0,}, {2,}, {1,}, {0,} }}, -{ "Sc", 45, 45, 44.955910000, METAL , 13, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Ti", 48, 48, 47.947950000, METAL , 15, 1, {{0,}, {0,}, {3,4}, {0,}, {0,} }}, -{ "V", 51, 51, 50.943960000, METAL , 16, 1, {{0,}, {0,}, {2,3,4,5,}, {0,}, {0,} }}, -{ "Cr", 52, 52, 51.940500000, METAL , 16, 1, {{0,}, {0,}, {2,3,6,}, {0,}, {0,} }}, -{ "Mn", 55, 55, 54.938050000, METAL2, 15, 1, {{0,}, {0,}, {2,3,4,6,}, {0,}, {0,} }}, -{ "Fe", 56, 56, 55.934900000, METAL2, 18, 1, {{0,}, {0,}, {2,3,4,6,}, {0,}, {0,} }}, -{ "Co", 59, 59, 58.933200000, METAL2, 18, 1, {{0,}, {0,}, {2,3,}, {0,}, {0,} }}, -{ "Ni", 59, 58, 57.935300000, METAL2, 18, 1, {{0,}, {0,}, {2,3,}, {0,}, {0,} }}, -{ "Cu", 64, 63, 62.929600000, METAL , 19, 1, {{0,}, {0,}, {1,2,}, {0,}, {0,} }}, -{ "Zn", 65, 64, 63.929147000, METAL , 16, 1, {{0,}, {0,}, {2,}, {0,}, {0,} }}, -{ "Ga", 70, 69, 68.925600000, METAL , 18, 0, {{3,5,}, {4,}, {3,}, {0,}, {1,} }}, -{ "Ge", 73, 74, 73.921177400, 0 , 18, 0, {{2,4,6,}, {3,5,}, {4,}, {3,}, {0,} }}, -{ "As", 75, 75, 74.921594200, 0 , 20, 0, {{1,3,5,7,}, {2,4,6,}, {3,5,}, {4,}, {3,} }}, -{ "Se", 79, 80, 79.916519600, 0 , 24, 0, {{0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, {4,} }}, -{ "Br", 80, 79, 78.918336100, 0 , 28, 0, {{0,}, {0,}, {1,3,5,7,}, {2,4,6,}, {3,5,} }}, -{ "Kr", 84, 84, 83.911500000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, -{ "Rb", 85, 85, 84.911800000, METAL , 8, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -{ "Sr", 88, 88, 87.905600000, METAL , 10, 0, {{0,}, {0,}, {2,}, {1,}, {0,} }}, -{ "Y", 89, 89, 88.905860000, METAL , 12, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Zr", 91, 90, 89.904700000, METAL , 14, 1, {{0,}, {0,}, {4,}, {0,}, {0,} }}, -{ "Nb", 93, 93, 92.906400000, METAL , 16, 1, {{0,}, {0,}, {3,5,}, {0,}, {0,} }}, -{ "Mo", 96, 98, 97.905400000, METAL , 18, 1, {{0,}, {0,}, {3,4,5,6,}, {0,}, {0,} }}, -{ "Tc", 98, 98, 97.907200000, METAL , 19, 1, {{0,}, {0,}, {7,}, {0,}, {0,} }}, -{ "Ru", 101, 102, 101.904300000, METAL , 22, 1, {{0,}, {0,}, {2,3,4,6,}, {0,}, {0,} }}, -{ "Rh", 103, 103, 102.905500000, METAL , 22, 1, {{0,}, {0,}, {2,3,4,}, {0,}, {0,} }}, -{ "Pd", 106, 106, 105.903500000, METAL , 22, 1, {{0,}, {0,}, {2,4,}, {0,}, {0,} }}, -{ "Ag", 108, 107, 106.905100000, METAL , 19, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -{ "Cd", 112, 114, 113.903400000, METAL , 17, 1, {{0,}, {0,}, {2,}, {0,}, {0,} }}, -{ "In", 115, 115, 114.903900000, METAL , 17, 0, {{3,5,}, {2,4,}, {3,}, {0,}, {1,} }}, -{ "Sn", 119, 120, 119.902200000, METAL2, 18, 0, {{2,4,6,}, {3,5}, {2,4,}, {3,}, {0,} }}, -{ "Sb", 122, 121, 120.903800000, METAL, 19, 0, {{1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,}, {3,} }}, -{ "Te", 128, 130, 129.906200000, 0 , 21, 0, {{0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,} }}, -{ "I", 127, 127, 126.904500000, 0 , 25, 0, {{0,}, {0,}, {1,3,5,7,}, {2,4,6}, {3,5,} }}, -{ "Xe", 131, 132, 131.904100000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, -{ "Cs", 133, 133, 132.905430000, METAL , 7, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -{ "Ba", 137, 138, 137.905200000, METAL , 9, 0, {{0,}, {0,}, {2,}, {1,}, {0,} }}, -{ "La", 139, 139, 138.906360000, METAL , 11, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Ce", 140, 140, 139.905400000, METAL2, 0, 1, {{0,}, {0,}, {3,4,}, {0,}, {0,} }}, -{ "Pr", 141, 141, 140.907660000, METAL2, 0, 1, {{0,}, {0,}, {3,4,}, {0,}, {0,} }}, -{ "Nd", 144, 142, 141.907719000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Pm", 145, 145, 144.912800000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Sm", 150, 152, 151.919700000, METAL2, 0, 1, {{0,}, {0,}, {2,3,}, {0,}, {0,} }}, -{ "Eu", 152, 153, 152.921200000, METAL2, 0, 1, {{0,}, {0,}, {2,3,}, {0,}, {0,} }}, -{ "Gd", 157, 158, 157.924099000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Tb", 159, 159, 158.925350000, METAL2, 0, 1, {{0,}, {0,}, {3,4,}, {0,}, {0,} }}, -{ "Dy", 163, 164, 163.929200000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, /* mw rounding uncertain */ -{ "Ho", 165, 165, 164.930300000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Er", 167, 166, 165.930300000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Tm", 169, 169, 168.934230000, METAL2, 0, 1, {{0,}, {0,}, {2,3,}, {0,}, {0,} }}, -{ "Yb", 173, 174, 173.938900000, METAL2, 0, 1, {{0,}, {0,}, {2,3,}, {0,}, {0,} }}, -{ "Lu", 175, 175, 174.940800000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Hf", 178, 180, 179.946600000, METAL , 13, 1, {{0,}, {0,}, {4,}, {0,}, {0,} }}, -{ "Ta", 181, 181, 180.948010000, METAL , 15, 1, {{0,}, {0,}, {5,}, {0,}, {0,} }}, -{ "W", 184, 184, 183.951000000, METAL2, 17, 1, {{0,}, {0,}, {3,4,5,6,}, {0,}, {0,} }}, -{ "Re", 186, 187, 186.955800000, METAL2, 19, 1, {{0,}, {0,}, {2,4,6,7,}, {0,}, {0,} }}, -{ "Os", 190, 192, 191.961500000, METAL2, 22, 1, {{0,}, {0,}, {2,3,4,6,}, {0,}, {0,} }}, -{ "Ir", 192, 193, 192.962900000, METAL2, 22, 1, {{0,}, {0,}, {2,3,4,6,}, {0,}, {0,} }}, -{ "Pt", 195, 195, 194.964800000, METAL2, 22, 1, {{0,}, {0,}, {2,4,}, {0,}, {0,} }}, -{ "Au", 197, 197, 196.966560000, METAL , 24, 1, {{0,}, {0,}, {1,3,}, {0,}, {0,} }}, -{ "Hg", 201, 202, 201.970617000, METAL2, 19, 1, {{0,}, {0,}, {1,2,}, {0,}, {0,} }}, -{ "Tl", 204, 205, 204.974400000, METAL2, 18, 0, {{3,5,}, {2,4,}, {1,3,}, {0,}, {0,} }}, -{ "Pb", 207, 208, 207.976627000, METAL2, 18, 0, {{2,4,6,}, {3,5}, {2,4,}, {3,}, {0,} }}, -{ "Bi", 209, 209, 208.980390000, METAL , 19, 0, {{1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,}, {3,} }}, -{ "Po", 209, 209, 208.982400000, METAL2, 20, 0, {{0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,} }}, -{ "At", 210, 210, 209.987100000, 0 , 22, 0, {{0,}, {0,}, {1,3,5,7,}, {2,4,6}, {3,5,} }}, -{ "Rn", 222, 222, 222.017500000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, -{ "Fr", 223, 223, 223.019700000, METAL , 0, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -{ "Ra", 226, 226, 226.025410000, METAL , 0, 0, {{0,}, {0,}, {2,}, {1,}, {0,} }}, -{ "Ac", 227, 227, 227.027750000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Th", 232, 232, 232.038050000, METAL2, 0, 1, {{0,}, {0,}, {3,4,}, {0,}, {0,} }}, -{ "Pa", 231, 231, 231.035880000, METAL2, 0, 1, {{0,}, {0,}, {3,4,5,}, {0,}, {0,} }}, -{ "U", 238, 238, 238.050790000, METAL2, 0, 1, {{0,}, {0,}, {3,4,5,6,}, {0,}, {0,} }}, -{ "Np", 237, 237, 237.048170000, METAL2, 0, 1, {{0,}, {0,}, {3,4,5,6,}, {0,}, {0,} }}, -{ "Pu", 244, 244, 244.064200000, METAL2, 0, 1, {{0,}, {0,}, {3,4,5,6,}, {0,}, {0,} }}, -{ "Am", 243, 243, 243.061370000, METAL2, 0, 1, {{0,}, {0,}, {3,4,5,6,}, {0,}, {0,} }}, -{ "Cm", 247, 247, 247.070300000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Bk", 247, 247, 247.070300000, METAL , 0, 1, {{0,}, {0,}, {3,4,}, {0,}, {0,} }}, -{ "Cf", 251, 251, 251.079600000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Es", 252, 252, 252.082800000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Fm", 257, 257, 257.095100000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Md", 258, 258, 258.098600000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "No", 259, 259, 259.100900000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -{ "Lr", 260, 260, 260.105400000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -{ "Rf", 261, 261, 261.108700000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, - -/*^^^ Added in v. 1.04 */ - -/* - Reference: - M. E. WIESER AND T. B. COPLEN. - Atomic weights of the elements 2009 (IUPAC Technical Report). - Pure Appl. Chem., Vol. 83, No. 2, pp. 359–396, 2011. - When available, the mass is given for isotope with the longest half-life. -*/ -/* 105 dubnium Db */ /* ? Like: Ta */ -{ "Db", 268, 268, 268.125000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -/* 106 seaborgium Sg */ /* ? Like: W */ -{ "Sg", 271, 271, 271.133000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -/* 107 bohrium Bh */ /* ? Like: Re */ -{ "Bh", 267, 267, 267.127700000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -/* 108 hassium Hs */ /* ? Like: Os */ -{ "Hs", 277, 277, 277.150000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -/* 109 meitnerium Mt */ /* ? Like: Ir */ -{ "Mt", 276, 276, 276.151000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -/* 110 darmstadtium Ds */ /* ? Like: Pt */ -{ "Ds", 281, 281, 281.162000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -/* 111 roentgenium Rg */ /* ? Like: Au */ -{ "Rg", 280, 280, 280.164000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -/* 112 copernicium Cn */ /* ? Like: Hg */ -{ "Cn", 285, 285, 285.174000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -/*^^^ End of added in v. 1.04 */ - -#ifdef INCHI_ZFRAG -{ "Zu", 0, 0, 0.000000000, 0 , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, //single bond fragment -{ "Zv", 0, 0, 0.000000000, 0 , 0, 1, {{0,}, {0,}, {2,}, {0,}, {0,} }}, //double bond fragment -{ "Zw", 0, 0, 0.000000000, 0 , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, //triple bond fragment -{ "Zx", 0, 0, 0.000000000, 0 , 0, 1, {{0,}, {0,}, {1,2,}, {0,}, {0,} }}, //aromatic bond fragment -#endif -{ "", 0, 0, 0.000000000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, - -}; - /* -#ifdef __cplusplus -} -#endif -*/ - -int ERR_ELEM = 255; -int nElDataLen = sizeof(ElData)/sizeof(ElData[0])-1; - -/***********************************************************************************/ -int GetElementFormulaFromAtNum(int nAtNum, char *szElement ) -{ - nAtNum -= 1; - if ( 0 < nAtNum ) - nAtNum += 2; /* bypass D, T */ - if ( 0 <= nAtNum && nAtNum < nElDataLen ) { - strcpy( szElement, ElData[nAtNum].szElName ); - return 0; - } - strcpy( szElement, "??" ); - return -1; -} -/***********************************************************************************/ -int get_el_number( const char* elname ) -{ - int i; - const char *p; - for ( i = 0; (p=ElData[i].szElName)[0] && strcmp( p, elname ); i++ ) - ; - return p[0]? i : ERR_ELEM; -} -/***********************************************************************************/ -int get_periodic_table_number( const char* elname ) -{ - int num; - num = get_el_number( elname ); - if ( num < ERR_ELEM ) - num = inchi_max(1, num-1); - return num; -} -/***********************************************************************************/ -int do_not_add_H( int nPeriodicNum ) -{ - return ElData[nPeriodicNum>1? nPeriodicNum+1:0].bDoNotAddH; -} -/***********************************************************************************/ -int get_el_valence( int nPeriodicNum, int charge, int val_num ) -{ - if ( charge < MIN_ATOM_CHARGE || charge > MAX_ATOM_CHARGE || val_num >= MAX_NUM_VALENCES ) - return 0; - return ElData[nPeriodicNum>1? nPeriodicNum+1:0].cValence[NEUTRAL_STATE+charge][val_num]; -} -/*********************************************************************************** - * output valence needed to unumbiguosly reconstruct bonds - ***********************************************************************************/ -int get_unusual_el_valence( int nPeriodicNum, int charge, int radical, int bonds_valence, int num_H, int num_bonds ) -{ - int i, num_found, chem_valence, rad_adj, known_chem_valence, exact_found; - if ( !num_bonds && !num_H ) - return 0; - if ( charge < MIN_ATOM_CHARGE || charge > MAX_ATOM_CHARGE ) { - if ( bonds_valence == num_bonds ) - return 0; /* all single bonds */ - return bonds_valence; - } - if ( !get_el_valence( nPeriodicNum, charge, 0 ) && bonds_valence == num_bonds ) - return 0; - - chem_valence = bonds_valence + num_H; - rad_adj = 0; - num_found = 0; - exact_found = 0; - - /* take into account radical */ - if (radical==RADICAL_DOUBLET) - rad_adj = 1; - else - if (radical==RADICAL_TRIPLET ) - rad_adj = 2; - - for ( i = 0; i < MAX_NUM_VALENCES; i ++ ) { - if ( 0 < (known_chem_valence = get_el_valence( nPeriodicNum, charge, i )-rad_adj) && - num_bonds <= known_chem_valence && known_chem_valence <= chem_valence ) { - num_found ++; - if ( known_chem_valence == chem_valence ) { - exact_found = 1; - break; - } - } - } - return (exact_found && 1 == num_found)? 0 : chem_valence; -} -/*********************************************************************************** - * output valence needed to unumbiguosly reconstruct number of H - ***********************************************************************************/ -int needed_unusual_el_valence( int nPeriodicNum, int charge, int radical, int bonds_valence, - int actual_bonds_valence, int num_H, int num_bonds ) -{ - int i, num_found, num_found_known, chem_valence, rad_adj, known_chem_valence, exact_found; - int num_H_expected; - char szElement[4]; - /* - if ( !num_bonds && !num_H ) - return 0; - */ - if ( num_bonds && !GetElementFormulaFromAtNum(nPeriodicNum, szElement ) ) { - num_H_expected = get_num_H( szElement, 0, NULL, charge, radical, actual_bonds_valence, 0,0,0,0 ); - } else { - num_H_expected = num_H; - } - - chem_valence = bonds_valence + num_H; - if ( charge < MIN_ATOM_CHARGE || charge > MAX_ATOM_CHARGE || - !get_el_valence( nPeriodicNum, charge, 0 ) || - do_not_add_H( nPeriodicNum ) || bonds_valence != actual_bonds_valence || - num_H_expected != num_H ) { - if ( !num_H && !num_H_expected && bonds_valence == actual_bonds_valence ) - return 0; /* no H */ - return chem_valence; /* needs to add H-atoms */ - } - - /* take into account radical */ - if (radical==RADICAL_DOUBLET) - rad_adj = 1; - else - if (radical==RADICAL_TRIPLET ) - rad_adj = 2; - else - rad_adj = 0; - - num_found_known = 0; - num_found = 0; - exact_found = 0; - - for ( i = 0; i < MAX_NUM_VALENCES; i ++ ) { - if ( 0 < (known_chem_valence = get_el_valence( nPeriodicNum, charge, i )) && - bonds_valence <= (known_chem_valence -= rad_adj) ) { - /* found known valence that fits without H */ - num_found_known ++; - if ( known_chem_valence <= chem_valence ) { - /* known valence is large enough to accommodate (implicit) H */ - num_found ++; - } - if ( known_chem_valence == chem_valence ) { - exact_found = 1; - break; - } - } - } - return (exact_found && 1 == num_found && 1 == num_found_known)? 0 : chem_valence? chem_valence : -1 /* needs zero */; -} -/*********************************************************************************** - * output valence that does not fit any known valences - ***********************************************************************************/ -int detect_unusual_el_valence( int nPeriodicNum, int charge, int radical, int bonds_valence, int num_H, int num_bonds ) -{ - int i, chem_valence, rad_adj, known_chem_valence; - - if ( !num_bonds && !num_H ) - return 0; - - if ( charge < MIN_ATOM_CHARGE || charge > MAX_ATOM_CHARGE ) { - if ( bonds_valence == num_bonds ) - return 0; /* all single bonds */ - return bonds_valence; - } - if ( !get_el_valence( nPeriodicNum, charge, 0 ) && bonds_valence == num_bonds ) - return 0; - - chem_valence = bonds_valence + num_H; - rad_adj = 0; - - /* take into account radical */ - if (radical==RADICAL_DOUBLET) - rad_adj = 1; - else - if (radical==RADICAL_TRIPLET || radical==RADICAL_SINGLET ) - rad_adj = 2; - - for ( i = 0; i < MAX_NUM_VALENCES; i ++ ) { - if ( 0 < (known_chem_valence = get_el_valence( nPeriodicNum, charge, i )-rad_adj) ) { - if ( known_chem_valence == chem_valence ) { - return 0; - } - } - } - return chem_valence; -} -/***********************************************************************************/ -int get_el_type( int nPeriodicNum ) -{ - return ElData[nPeriodicNum+1].nType; -} -/***********************************************************************************/ -int is_el_a_metal( int nPeriodicNum ) -{ - return 0!=(ElData[nPeriodicNum+1].nType & IS_METAL); -} -/******************************************************************************************************/ -/*#ifndef TARGET_API_LIB*/ -int extract_ChargeRadical( char *elname, int *pnRadical, int *pnCharge ) -{ - char *q, *r, *p; - int nCharge=0, nRad = 0, charge_len = 0, k, nVal, nSign, nLastSign=1, len; - - p = elname; - - /* extract radicals & charges */ - while ( q = strpbrk( p, "+-^" ) ) { - switch ( *q ) { - case '+': - case '-': - for ( k = 0, nVal=0; (nSign = ('+' == q[k])) || (nSign = -('-' == q[k])); k++ ) { - nVal += (nLastSign = nSign); - charge_len ++; - } - if ( nSign = (int)strtol( q+k, &r, 10 ) ) { /* fixed 12-5-2001 */ - nVal += nLastSign * (nSign-1); - } - charge_len = r - q; - nCharge += nVal; - break; - /* case '.': */ /* singlet '.' may be confused with '.' in formulas like CaO.H2O */ - case '^': - nRad = 1; /* doublet here is 1. See below */ - charge_len = 1; - for ( k = 1; q[0] == q[k]; k++ ) { - nRad ++; - charge_len ++; - } - break; - } - memmove( q, q+charge_len, strlen(q+charge_len)+1 ); - } - len = (int) strlen(p); - /* radical */ - if ( (q = strrchr( p, ':' )) && !q[1]) { - nRad = RADICAL_SINGLET; - q[0] = '\0'; - len --; - } else { - while( (q = strrchr( p, '.' )) && !q[1] ) { - nRad ++; - q[0] = '\0'; - len --; - } - - nRad = nRad == 1? RADICAL_DOUBLET : - nRad == 2? RADICAL_TRIPLET : 0; - } - *pnRadical = nRad; - *pnCharge = nCharge; - return ( nRad || nCharge ); - -} -/*#endif*/ -/****************************************************************/ -int extract_H_atoms( char *elname, S_CHAR num_iso_H[] ) -{ - int i, len, c, k, num_H, val; - char *q; - i = 0; - num_H = 0; - len = (int)strlen(elname); - c = UCINT elname[0]; - while ( i < len ) { - switch ( c ) { - case 'H': - k = 0; - break; - case 'D': - k = 1; - break; - case 'T': - k = 2; - break; - default: - k = -1; - break; - } - q = elname+i+1; /* pointer to the next to elname[i] character */ - c = UCINT q[0]; - if ( k >= 0 && !islower( c ) ) { - /* found a hydrogen */ - if ( isdigit( c ) ) { - val = (int)strtol( q, &q, 10 ); - /* q = pointer to the next to number of hydrogen atom(s) character */ - } else { - val = 1; - } - if ( k ) { - num_iso_H[k] += val; - } else { - num_H += val; - } - /* remove the hydrogen atom from the string */ - len -= (q-elname)-i; - memmove( elname+i, q, len + 1 ); - /* c = UCINT elname[i]; */ - } else { - i ++; - } - c = UCINT elname[i]; /* moved here 11-04-2002 */ - } - return num_H; -} -/***********************************************************************************/ -int get_num_H (const char* elname, int inp_num_H, S_CHAR inp_num_iso_H[], - int charge, int radical, int chem_bonds_valence, int atom_input_valence, - int bAliased, int bDoNotAddH, int bHasMetalNeighbor ) -{ - int val, i, el_number, num_H = 0, num_iso_H; - static int el_number_N = 0, el_number_S, el_number_O, el_number_C; - if ( !el_number_N ) { - el_number_N = get_el_number( "N" ); - el_number_S = get_el_number( "S" ); - el_number_O = get_el_number( "O" ); - el_number_C = get_el_number( "C" ); - } - /* atom_input_valence (cValence) cannot be specified in case of */ - /* aliased MOLFile atom with known inp_num_H or inp_num_iso_H[] */ - if ( bAliased ) { - num_H = inp_num_H; - } else - if ( atom_input_valence && (atom_input_valence !=15 || chem_bonds_valence) ) { - num_H = inchi_max( 0, atom_input_valence - chem_bonds_valence ); - } else - if ( atom_input_valence == 15 && !chem_bonds_valence ) { - num_H = 0; - } else - if ( MIN_ATOM_CHARGE <= charge && - MAX_ATOM_CHARGE >= charge && - ERR_ELEM != (el_number = get_el_number( elname ) ) && - !ElData[el_number].bDoNotAddH && !bDoNotAddH ) { - /* add hydrogen atoms according to standard element valence */ - if ( radical && radical != RADICAL_SINGLET ) { - if ( val = ElData[el_number].cValence[NEUTRAL_STATE+charge][0] ) { - val -= (radical==RADICAL_DOUBLET)? 1 : - (radical==RADICAL_SINGLET || radical==RADICAL_TRIPLET )? 2 : val; - /* if unknown radical then do not add H */ - num_H = inchi_max( 0, val - chem_bonds_valence ); - } - } else { - /* find the smallest valence that is greater than the sum of the chemical bond valences */ - for ( i = 0; - (val=ElData[el_number].cValence[NEUTRAL_STATE+charge][i]) && - val < chem_bonds_valence; - i++ ) - ; - /* special case: do not add H to N(IV), S(III), S+(II), S-(II) */ /* S ions added 2004-05-10 */ - if ( el_number == el_number_N && !charge && !radical && val == 5 ) - val = 3; - else - /* - if ( el_number == el_number_N && !charge && !radical && val == 3 && - chem_bonds_valence == 2 && bHasMetalNeighbor ) - val = 2; - else - */ - if ( el_number == el_number_S && !charge && !radical && val == 4 && chem_bonds_valence == 3 ) - val = 3; - else - if ( bHasMetalNeighbor && el_number != el_number_C && val > 0 ) { - val --; - } - /* - if ( (el_number == el_number_S || el_number == el_number_O) && - abs(charge)==1 && !radical && val == 3 && chem_bonds_valence == 2 && bHasMetalNeighbor ) - val = 2; - else - */ - num_H = inchi_max( 0, val - chem_bonds_valence ); - } - num_iso_H = 0; - if ( inp_num_iso_H ) { - for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { - num_iso_H += inp_num_iso_H[i]; - } - } - /* should not happen because atom here is not aliased */ - if ( num_iso_H ) { - if ( num_H >= num_iso_H ) { - num_H -= num_iso_H; - } else { - num_H = inp_num_H; /* as requested in the alias */ - /* num_H = (num_iso_H - num_H) % 2; */ /* keep unchanged parity of the total number of H atoms */ - } - } - /* should not happen because atom here is not aliased */ - if ( inp_num_H > num_H ) { - num_H = inp_num_H; /* as requested in the alias */ - /* num_H = inp_num_H + (inp_num_H - num_H)%2; */ /* keep unchanged parity of the number of non-isotopic H atoms */ - } - } else { - num_H = inp_num_H; - } - return num_H; -} -/***********************************************************************************/ -int get_atw_from_elnum( int nAtNum ) -{ - nAtNum -= 1; - if ( 0 < nAtNum ) - nAtNum += 2; /* bypass D, T */ - if ( 0 <= nAtNum && nAtNum < nElDataLen ) { - return (int)ElData[nAtNum].nAtMass; - } - return 0; -} -/***********************************************************************************/ -/* -int get_mw(char elname[]) -{ - int i; - - for (i=0; i 0 ) { - memmove( (void*) &name[i-n], (void*) &name[i], len-i+1 ); - i -= n; - len -= n; - } - n = -1; - } - } - if ( n == len ) /* empty line */ - name[len=0] = '\0'; - else - if ( ++n && n <= len ) { - len -= n; - name[len] = '\0'; - } - return len; -} -#endif /* ifndef TARGET_API_LIB */ -/************************************************/ -#ifndef inchi_malloc -void *inchi_malloc(size_t c) -{ - return malloc(c); -} -#endif -#ifndef inchi_calloc -void *inchi_calloc(size_t c, size_t n) -{ - return calloc(c,n); -} -#endif -#ifndef inchi_free -void inchi_free(void *p) -{ - if(p) { - free(p); /*added check if zero*/ - } -} -#endif - - - - - -#ifndef TARGET_API_LIB -/*************************************************************************/ -void remove_trailing_spaces( char* p ) -{ - int len; - for( len = (int)strlen( p ) - 1; len >= 0 && isspace( UCINT p[len] ); len-- ) - ; - p[++len] = '\0'; -} -/*************************************************************************/ -void remove_one_lf( char* p) -{ - size_t len; - if ( p && 0 < (len = strlen(p)) && p[len-1] == '\n' ){ - p[len-1] = '\0'; - if ( len >= 2 && p[len-2] == '\r' ) - p[len-2] = '\0'; - } -} -#endif /* ifndef TARGET_API_LIB */ - - - -/***************************************************************************/ -/* Copies up to maxlen characters INCLUDING end null from source to target */ -/* Fills out the rest of the target with null bytes */ -int mystrncpy(char *target,const char *source,unsigned maxlen) -{ /* protected from non-zero-terminated source and overlapped target/source. 7-9-99 DCh. */ - const char *p; - unsigned len; - - if (target==NULL || maxlen == 0 || source == NULL) - return 0; - if ( p = (const char*)memchr(source, 0, maxlen) ) { - len = p-source; /* maxlen does not include the found zero termination */ - } else { - len = maxlen-1; /* reduced length does not include one more byte for zero termination */ - } - if ( len ) - memmove( target, source, len ); - /* target[len] = '\0'; */ - memset( target+len, 0, maxlen-len); /* zero termination */ - return 1; -} -/************************************************************************/ -/* Remove leading and trailing white spaces */ -char* LtrimRtrim( char *p, int* nLen ) -{ - int i, len=0; - if ( p && (len = (int) strlen( p )) ) { - for ( i = 0; i < len && __isascii( p[i] ) && isspace( p[i] ); i++ ) - ; - if ( i ) - (memmove)( p, p+i, (len -= i)+1 ); - for ( ; 0 < len && __isascii( p[len-1] ) && isspace( p[len-1] ); len-- ) - ; - p[len] = '\0'; - } - if ( nLen ) - *nLen = len; - return p; -} -/*************************************************************************/ -AT_NUMB *is_in_the_list( AT_NUMB *pathAtom, AT_NUMB nNextAtom, int nPathLen ) -{ - for ( ; nPathLen && *pathAtom != nNextAtom; nPathLen--, pathAtom++ ) - ; - return nPathLen? pathAtom : NULL; -} -/******************************************************************************************************/ -int nBondsValToMetal( inp_ATOM* at, int iat ) -{ - int i, neigh, bond_type, nVal2Metal = 0; - inp_ATOM* a = at + iat; - for ( i = 0; i < a->valence; i ++ ) { - neigh = a->neighbor[i]; - if ( is_el_a_metal( at[(int)a->neighbor[i]].el_number ) ) { - bond_type = a->bond_type[i]; - if ( bond_type <= BOND_TYPE_TRIPLE ) { - nVal2Metal += bond_type; - } else { - return -1; /* bond to metal order is not well defined */ - } - } - } - return nVal2Metal; -} -/************************************************************************/ -int num_of_H( inp_ATOM *at, int iat ) -{ - static int el_number_H; - int i, n, num_explicit_H = 0; - inp_ATOM *a = at + iat; - if ( !el_number_H ) - el_number_H = get_periodic_table_number( "H" ); - for ( i = 0; i < a->valence; i ++ ) { - n = a->neighbor[i]; - num_explicit_H += ( 1 == at[n].valence && el_number_H == at[n].el_number ); - } - return num_explicit_H+NUMH(at,iat); -} -/************************************************************************/ -int has_other_ion_neigh( inp_ATOM *at, int iat, int iat_ion_neigh, const char *el, int el_len ) -{ - int charge = at[iat_ion_neigh].charge; - int i, neigh; - for ( i = 0; i < at[iat].valence; i ++ ) { - neigh = at[iat].neighbor[i]; - if ( neigh != iat_ion_neigh && at[neigh].charge == charge && - NULL != memchr( el, at[neigh].el_number, el_len ) ) { - return 1; - } - } - return 0; -} -/************************************************************************/ -/* BFS r=2 */ -int has_other_ion_in_sphere_2(inp_ATOM *at, int iat, int iat_ion_neigh, const char *el, int el_len ) -{ -#define MAXQ 16 - AT_NUMB q[MAXQ]; - int lenq=0, lenq2, dist = 0, i = 0, iq, neigh, j, nRet=0; - q[lenq++] = iat; - at[iat].cFlags = 1; - - iq = 0; - dist = 1; - /* use at->cFlags as an indicator */ - while ( dist <= 2 ) { - for ( lenq2 = lenq; iq < lenq2; iq ++ ) { - i = q[iq]; - for ( j = 0; j < at[i].valence; j ++ ) { - neigh = at[i].neighbor[j]; - if ( !at[neigh].cFlags && - at[neigh].valence <= 3 && - NULL != memchr( el, at[neigh].el_number, el_len ) ) { - q[lenq ++] = neigh; - at[neigh].cFlags = 1; - if ( neigh != iat_ion_neigh && - at[iat_ion_neigh].charge == at[neigh].charge ) { - nRet ++; - } - } - } - } - dist ++; - } - for ( iq = 0; iq < lenq; iq ++ ) { - i = q[iq]; - at[i].cFlags = 0; - } - return nRet; -} -/************************************************************************/ -int nNoMetalNumBonds( inp_ATOM *at, int at_no ) -{ - inp_ATOM *a = at + at_no; - int num_H = NUMH(a, 0); - int std_chem_bonds_valence = get_el_valence( a->el_number, a->charge, 0 ); - int i; - if ( a->chem_bonds_valence + num_H > std_chem_bonds_valence ) { - int valence_to_metal = 0; - int num_bonds_to_metal = 0; - for ( i = 0; i < a->valence; i ++ ) { - if ( is_el_a_metal( at[(int)a->neighbor[i]].el_number ) ) { - if ( (a->bond_type[i] & BOND_TYPE_MASK) >= BOND_TYPE_ALTERN ) { - return a->valence; /* fall back */ - } - num_bonds_to_metal ++; - valence_to_metal += (a->bond_type[i] & BOND_TYPE_MASK); - } - } - if ( a->chem_bonds_valence + num_H - valence_to_metal == std_chem_bonds_valence ) { - /* removing bonds to metal produces standard valence */ - return a->valence - num_bonds_to_metal; - } - } -#if ( S_VI_O_PLUS_METAL_FIX_BOND == 1 ) - else - if ( 1 == a->charge && 2 == get_endpoint_valence(a->el_number) && - a->chem_bonds_valence + num_H == std_chem_bonds_valence ) { - int valence_to_metal = 0; - int num_bonds_to_metal = 0; - for ( i = 0; i < a->valence; i ++ ) { - if ( is_el_a_metal( at[(int)a->neighbor[i]].el_number ) ) { - if ( (a->bond_type[i] & BOND_TYPE_MASK) >= BOND_TYPE_ALTERN ) { - return a->valence; /* fall back */ - } - num_bonds_to_metal ++; - valence_to_metal += (a->bond_type[i] & BOND_TYPE_MASK); - } - } - if ( 1 == valence_to_metal ) { - /* removing bonds to metal produces standard valence */ - return a->valence - num_bonds_to_metal; - } - } -#endif - - return a->valence; -} -/************************************************************************/ -int nNoMetalBondsValence( inp_ATOM *at, int at_no ) -{ - inp_ATOM *a = at + at_no; - int num_H = NUMH(a, 0); - int std_chem_bonds_valence = get_el_valence( a->el_number, a->charge, 0 ); - int i; - if ( a->chem_bonds_valence + num_H > std_chem_bonds_valence ) { - int valence_to_metal = 0; - /*int num_bonds_to_metal = 0;*/ - for ( i = 0; i < a->valence; i ++ ) { - if ( is_el_a_metal( at[(int)a->neighbor[i]].el_number ) ) { - if ( (a->bond_type[i] & BOND_TYPE_MASK) >= BOND_TYPE_ALTERN ) { - return a->valence; /* fall back */ - } - /*num_bonds_to_metal ++;*/ - valence_to_metal += (a->bond_type[i] & BOND_TYPE_MASK); - } - } - if ( a->chem_bonds_valence + num_H - valence_to_metal == std_chem_bonds_valence ) { - /* removing bonds to metal produces standard valence */ - return a->chem_bonds_valence - valence_to_metal; - } - } -#if ( S_VI_O_PLUS_METAL_FIX_BOND == 1 ) - else - if ( 1 == a->charge && 2 == get_endpoint_valence(a->el_number) && - a->chem_bonds_valence + num_H == std_chem_bonds_valence ) { - int valence_to_metal = 0; - /*int num_bonds_to_metal = 0;*/ - for ( i = 0; i < a->valence; i ++ ) { - if ( is_el_a_metal( at[(int)a->neighbor[i]].el_number ) ) { - if ( (a->bond_type[i] & BOND_TYPE_MASK) >= BOND_TYPE_ALTERN ) { - return a->valence; /* fall back */ - } - /*num_bonds_to_metal ++;*/ - valence_to_metal += (a->bond_type[i] & BOND_TYPE_MASK); - } - } - if ( 1 == valence_to_metal ) { - /* removing bonds to metal produces standard valence */ - return a->chem_bonds_valence - valence_to_metal; - } - } -#endif - return a->chem_bonds_valence; -} -/************************************************************************/ -int nNoMetalNeighIndex( inp_ATOM *at, int at_no ) -{ - inp_ATOM *a = at + at_no; - int i; - for ( i = 0; i < a->valence; i ++ ) { - if ( !is_el_a_metal( at[(int)a->neighbor[i]].el_number ) ) { - return i; - } - } - return -1; -} -/************************************************************************/ -int nNoMetalOtherNeighIndex( inp_ATOM *at, int at_no, int cur_neigh ) -{ - inp_ATOM *a = at + at_no; - int i, neigh; - for ( i = 0; i < a->valence; i ++ ) { - neigh = (int)a->neighbor[i]; - if ( neigh != cur_neigh && !is_el_a_metal( at[neigh].el_number ) ) { - return i; - } - } - return -1; -} -/************************************************************************/ -int nNoMetalOtherNeighIndex2( inp_ATOM *at, int at_no, int cur_neigh, int cur_neigh2 ) -{ - inp_ATOM *a = at + at_no; - int i, neigh; - for ( i = 0; i < a->valence; i ++ ) { - neigh = (int)a->neighbor[i]; - if ( neigh != cur_neigh && neigh != cur_neigh2 && !is_el_a_metal( at[neigh].el_number ) ) { - return i; - } - } - return -1; -} - - -#ifndef COMPILE_ANSI_ONLY -/**************************************************************************/ -int MakeRemovedProtonsString( int nNumRemovedProtons, NUM_H *nNumExchgIsotopicH, NUM_H *nNumRemovedProtonsIsotopic, - int bIsotopic, char *szRemovedProtons, int *num_removed_iso_H ) -{ - int i, j, len, num; - len = 0; - if ( nNumRemovedProtons ) { - len = sprintf ( szRemovedProtons, "Proton balance: %c %d H+", - nNumRemovedProtons>=0? '+':'-', abs(nNumRemovedProtons) ); - } - if ( bIsotopic && (nNumRemovedProtonsIsotopic || nNumExchgIsotopicH) ) { - for ( i = 0, j = 0; i < NUM_H_ISOTOPES; i ++ ) { - num = (nNumExchgIsotopicH? nNumExchgIsotopicH[i]:0) + - (nNumRemovedProtonsIsotopic? nNumRemovedProtonsIsotopic[i]:0); - if ( num ) { - len += sprintf( szRemovedProtons+len, "%s %d^%dH", - j? ", ":" [ removed ", num, i+1); - j ++; - } - } - if ( j ) { - len += sprintf( szRemovedProtons+len, " ]" ); - if ( num_removed_iso_H ) - *num_removed_iso_H = j; - } - } - if ( !len ) { - szRemovedProtons[0] = '\0'; - } - return len; -} -#endif - -/* - According to - http://info-uri.info/registry/OAIHandler?verb=GetRecord&metadataPrefix=reg&identifier=info:inchi/ - - An InChI identifier may contain the following characters: - - A-Z - a-z - 0-9 - ()*+,-./;=?@ - - - Here we consider any character not conforming this specification as a whitespace - which marks the end of the InChI string. - For example: - "InChI=1/Ar%" - "InChI=1/Ar\n" - "InChI=1/Ar\r\t" - all will be trimmed to - "InChI=1/Ar" - -*/ - -/**************************************************************************/ -void extract_inchi_substring(char ** buf, const char *str, size_t slen) -{ -size_t i; -char *p, pp; - - *buf = NULL; - if (str==NULL) - return; - if (strlen(str)<1) - return; - - p = strstr(str, "InChI="); - if (NULL==p) - return; - - for (i=0; i= 'A' && pp <='Z') continue; - if (pp >= 'a' && pp <='z') continue; - if (pp >= '0' && pp <='9') continue; - switch ( pp ) - { - case '(': - case ')': - case '*': - case '+': - case ',': - case '-': - case '.': - case '/': - case ';': - case '=': - case '?': - case '@': continue; - - default: break; - } - break; - } - - *buf = (char*) inchi_calloc(i+1, sizeof(char)); - memcpy(*buf, p, i); - (*buf)[i] = '\0'; - - return; -} - - - - - -#ifdef COMPILE_ANSI_ONLY -/*************************************************************************/ -/************* non-ANSI functions ****************/ -/*************************************************************************/ -#define __MYTOLOWER(c) ( ((c) >= 'A') && ((c) <= 'Z') ? ((c) - 'A' + 'a') : (c) ) - -#if ( defined(COMPILE_ADD_NON_ANSI_FUNCTIONS) || defined(__STDC__) && __STDC__ == 1 ) -/* support (VC++ Language extensions) = OFF && defined(COMPILE_ANSI_ONLY) */ -int memicmp ( const void * p1, const void * p2, size_t length ) -{ - const U_CHAR *s1 = (const U_CHAR*)p1; - const U_CHAR *s2 = (const U_CHAR*)p2; - while ( length-- ) { - if ( *s1 == *s2 || - __MYTOLOWER( (int)*s1 ) == __MYTOLOWER( (int)*s2 )) { - s1 ++; - s2 ++; - } else { - return __MYTOLOWER( (int)*s1 ) - __MYTOLOWER( (int)*s2 ); - } - } - return 0; -} -/*************************************************************************/ -int stricmp( const char *s1, const char *s2 ) -{ - while ( *s1 ) { - if ( *s1 == *s2 || - __MYTOLOWER( (int)*s1 ) == __MYTOLOWER( (int)*s2 )) { - s1 ++; - s2 ++; - } else { - return __MYTOLOWER( (int)*s1 ) - __MYTOLOWER( (int)*s2 ); - } - } - if ( *s2 ) - return -1; - return 0; -} -/*************************************************************************/ -char *_strnset( char *s, int val, size_t length ) -{ - char *ps = s; - while (length-- && *ps) - *ps++ = (char)val; - return s; -} -/*************************************************************************/ -char *_strdup( const char *string ) -{ - char *p = NULL; - if ( string ) { - size_t length = strlen( string ); - p = (char *) inchi_malloc( length + 1 ); - if ( p ) { - strcpy( p, string ); - } - } - return p; -} -#endif -#endif - - diff --git a/INCHI-1-SRC/INCHI/common/util.h b/INCHI-1-SRC/INCHI/common/util.h deleted file mode 100644 index ddcc2ae..0000000 --- a/INCHI-1-SRC/INCHI/common/util.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __UTIL_H__ -#define __UTIL_H__ - -#include "inpdef.h" - -/* BILLY 8/6/04 */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -int get_atw(const char *elname); -int get_atw_from_elnum( int nAtNum ); -int get_num_H (const char* elname, int inp_num_H, S_CHAR num_iso_H[], int charge, int radical, - int chem_bonds_valence, int atom_input_valence, int bAliased, int bDoNotAddH, int bHasMetalNeighbor ); -int extract_ChargeRadical( char *elname, int *pnRadical, int *pnCharge ); -int extract_H_atoms( char *elname, S_CHAR num_iso_H[] ); -int normalize_name( char* name ); - -int mystrncpy(char *target,const char *source,unsigned maxlen); -char* LtrimRtrim( char *p, int* nLen ); - -void remove_trailing_spaces( char* p ); -void remove_one_lf( char* p); -void mystrrev( char *p ); - - - - - - -#define ALPHA_BASE 27 -long inchi_strtol( const char *str, const char **p, int base); -double inchi_strtod( const char *str, const char **p ); - -AT_NUMB *is_in_the_list( AT_NUMB *pathAtom, AT_NUMB nNextAtom, int nPathLen ); -int get_periodic_table_number( const char* elname ); -int is_el_a_metal( int nPeriodicNum ); -int get_el_valence( int nPeriodicNum, int charge, int val_num ); -int get_unusual_el_valence( int nPeriodicNum, int charge, int radical, int bonds_valence, int num_H, int num_bonds ); -int detect_unusual_el_valence( int nPeriodicNum, int charge, int radical, int bonds_valence, int num_H, int num_bonds ); -int needed_unusual_el_valence( int nPeriodicNum, int charge, int radical, int bonds_valence, - int actual_bonds_val, int num_H, int num_bonds ); -int get_el_type( int nPeriodicNum ); -int get_el_number( const char* elname ); -int do_not_add_H( int nPeriodicNum ); -int GetElementFormulaFromAtNum(int nAtNum, char *szElement ); -int MakeRemovedProtonsString( int nNumRemovedProtons, NUM_H *nNumExchgIsotopicH, NUM_H *nNumRemovedProtonsIsotopic, - int bIsotopic, char *szRemovedProtons, int *num_removed_iso_H ); - -/* ion pairs and fixing bonds */ -int num_of_H( inp_ATOM *at, int iat ); -int has_other_ion_neigh( inp_ATOM *at, int iat, int iat_ion_neigh, const char *el, int el_len ); -int has_other_ion_in_sphere_2(inp_ATOM *at, int iat, int iat_ion_neigh, const char *el, int el_len ); -int nNoMetalNumBonds( inp_ATOM *at, int at_no ); -int nNoMetalBondsValence( inp_ATOM *at, int at_no ); -int nNoMetalNeighIndex( inp_ATOM *at, int at_no ); -int nNoMetalOtherNeighIndex( inp_ATOM *at, int at_no, int cur_neigh ); -int nNoMetalOtherNeighIndex2( inp_ATOM *at, int at_no, int cur_neigh, int cur_neigh2 ); - -void extract_inchi_substring(char ** buf, const char *str, size_t slen); - -/* mol2atom.c */ -int nBondsValToMetal( inp_ATOM* at, int iat ); - -/* ichi_bns.c */ -int nBondsValenceInpAt( const inp_ATOM *at, int *nNumAltBonds, int *nNumWrongBonds ); - -int bHeteroAtomMayHaveXchgIsoH( inp_ATOM *atom, int iat ); - -/* IChICan2.c */ -int SetBitFree( void ); - -void WriteCoord( char *str, double x ); - - -extern int ERR_ELEM; -extern int nElDataLen; - -/* BILLY 8/6/04 */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -#endif /* __UTIL_H__*/ - diff --git a/INCHI-1-SRC/INCHI/gcc/inchi-1/makefile b/INCHI-1-SRC/INCHI/gcc/inchi-1/makefile deleted file mode 100644 index 4cd79a1..0000000 --- a/INCHI-1-SRC/INCHI/gcc/inchi-1/makefile +++ /dev/null @@ -1,118 +0,0 @@ -# This will create: -# 32-bit executable under 32-bit Windows and 32-bit Linux, -# or 64-bit executable under 64-bit Linux/AMD64 -# -# To make 32-bit executable under 64-bit Linux/AMD64, use makefile32 -# -# -# -ifndef C_COMPILER - C_COMPILER = gcc -endif - -ifndef CPP_COMPILER - CPP_COMPILER = g++ -endif - -ifndef LINKER - LINKER = g++ -s -endif - -ifndef INCHI_EXECUTABLE - ifdef windir - EXE = .exe - else - EXE = - endif - INCHI_EXECUTABLE = inchi-1$(EXE) -endif - -ifndef P_MAIN - P_MAIN = ../../main -endif -ifndef P_LIBR - P_LIBR = ../../common -endif - -#P_INCL = -I$(P_MAIN) -I$(P_LIBR) -P_INCL = -I$(P_MAIN) -I$(P_LIBR) - -C_COMPILER_OPTIONS = $(P_INCL) -ansi -DCOMPILE_ANSI_ONLY -O3 -c -#C_COMPILER_OPTIONS = -c $(P_INCL) -ansi -O3 -fsigned-char -ffunction-sections -fexpensive-optimizations -fstack-check -fexceptions -Wall -pedantic -Wbad-function-cast -Wreturn-type -Wformat -Wuninitialized -Wcast-align -Wshadow -Wunused -Wunused-value -Wunused-variable -Wunused-function -Wunused-parameter -Wunused-label -Wcomment -Wcast-qual -Wconversion -Wimplicit-int -Wmissing-braces -Wmissing-declarations -Wmissing-prototypes -Wredundant-decls -Wsign-compare -Wfloat-equal -Wstrict-prototypes -Wwrite-strings -Wundef -Waggregate-return -Wchar-subscripts -Wformat-nonliteral -Wnested-externs -Wsequence-point -Wpointer-arith -mfancy-math-387 -mieee-fp -mno-soft-float - -ifdef windir -# no -ansi option due to reported MinGw bug - CPP_COMPILER_OPTIONS = $(P_INCL) -D_LIB -O3 -frtti -c - #CPP_COMPILER_OPTIONS = -c $(P_INCL) -D_LIB -O3 -frtti -Wall -pedantic -Wreturn-type -Wformat -Wuninitialized -ffunction-sections -fexpensive-optimizations -fstack-check -fexceptions -Wcast-align -Wshadow -Wunused -Wunused-value -Wunused-variable -Wunused-function -fsigned-char -Wcast-qual -Wconversion -Wmissing-braces -Wredundant-decls -Wsign-compare -Wfloat-equal -Wwrite-strings -mfancy-math-387 -mieee-fp -mno-soft-float -else - CPP_COMPILER_OPTIONS = $(P_INCL) -D_LIB -ansi -O3 -frtti -c - #CPP_COMPILER_OPTIONS = -c $(P_INCL) -D_LIB -ansi -O3 -frtti -Wall -pedantic -Wreturn-type -Wformat -Wuninitialized -ffunction-sections -fexpensive-optimizations -fstack-check -fexceptions -Wcast-align -Wshadow -Wunused -Wunused-value -Wunused-variable -Wunused-function -fsigned-char -Wcast-qual -Wconversion -Wmissing-braces -Wredundant-decls -Wsign-compare -Wfloat-equal -Wwrite-strings -mfancy-math-387 -mieee-fp -mno-soft-float -endif - - -#LINKER_OPTIONS = -static-libgcc -#LINKER_OPTIONS = -Wall -Wunused -Wunused-function - - - -INCHI_SRCS = $(P_LIBR)/ichican2.c $(P_LIBR)/ichiprt1.c \ - $(P_LIBR)/ichicano.c $(P_LIBR)/ichiprt2.c \ - $(P_LIBR)/ichicans.c $(P_LIBR)/ichiprt3.c \ - $(P_LIBR)/ichiisot.c $(P_LIBR)/ichiqueu.c \ - $(P_LIBR)/ichiring.c \ - $(P_LIBR)/ichimak2.c $(P_LIBR)/ichisort.c \ - $(P_LIBR)/ichimake.c $(P_LIBR)/ichister.c \ - $(P_LIBR)/ichimap1.c $(P_LIBR)/ichitaut.c \ - $(P_LIBR)/ichimap2.c $(P_LIBR)/ichi_bns.c \ - $(P_LIBR)/runichi.c \ - $(P_LIBR)/ichi_io.c \ - $(P_LIBR)/ichimap4.c $(P_LIBR)/strutil.c \ - $(P_LIBR)/ichinorm.c $(P_LIBR)/util.c \ - $(P_LIBR)/ichiparm.c \ - $(P_LIBR)/ichiread.c $(P_LIBR)/ichirvr1.c \ - $(P_LIBR)/ichirvr2.c $(P_LIBR)/ichirvr3.c \ - $(P_LIBR)/ichirvr4.c $(P_LIBR)/ichirvr5.c \ - $(P_LIBR)/ichirvr6.c $(P_LIBR)/ichirvr7.c \ - $(P_LIBR)/ikey_base26.c $(P_LIBR)/ikey_dll.c \ - $(P_LIBR)/sha2.c \ - $(P_MAIN)/dispstru.c $(P_MAIN)/mol2atom.c \ - $(P_MAIN)/ichialt.c $(P_MAIN)/readinch.c \ - $(P_MAIN)/ichimain.c $(P_MAIN)/readmol.c - -INCHI_OBJS = ichican2.o ichiprt1.o \ - ichicano.o ichiprt2.o \ - ichicans.o ichiprt3.o \ - ichiisot.o ichiqueu.o \ - ichiring.o \ - ichimak2.o ichisort.o \ - ichimake.o ichister.o \ - ichimap1.o ichitaut.o \ - ichimap2.o ichi_bns.o \ - runichi.o \ - ichi_io.o \ - ichimap4.o strutil.o \ - ichinorm.o util.o \ - ichiparm.o \ - ichiread.o ichirvr1.o \ - ichirvr2.o ichirvr3.o \ - ichirvr4.o ichirvr5.o \ - ichirvr6.o ichirvr7.o \ - ikey_base26.o ikey_dll.o \ - sha2.o \ - dispstru.o mol2atom.o \ - readinch.o \ - ichimain.o readmol.o - - -$(INCHI_EXECUTABLE) : $(INCHI_OBJS) - $(LINKER) $(LINKER_OPTIONS) -o $(INCHI_EXECUTABLE) $(INCHI_OBJS) -lm - -%.o: $(P_LIBR)/%.c - $(C_COMPILER) $(C_COMPILER_OPTIONS) $< - -%.o: $(P_MAIN)/%.c - $(C_COMPILER) $(C_COMPILER_OPTIONS) $< - -%.o: $(P_MAIN)/%.cpp - $(CPP_COMPILER) $(CPP_COMPILER_OPTIONS) $< - diff --git a/INCHI-1-SRC/INCHI/gcc/inchi-1/makefile32 b/INCHI-1-SRC/INCHI/gcc/inchi-1/makefile32 deleted file mode 100644 index 9915eb8..0000000 --- a/INCHI-1-SRC/INCHI/gcc/inchi-1/makefile32 +++ /dev/null @@ -1,100 +0,0 @@ -# This will create 32-bit executable under 64-bit Linux/AMD64 -# -# -ifndef C_COMPILER - C_COMPILER = gcc -endif - -ifndef CPP_COMPILER - CPP_COMPILER = g++ -endif - -ifndef LINKER - LINKER = g++ -s -endif - -ifndef INCHI_EXECUTABLE - INCHI_EXECUTABLE = inchi-1-32 -endif - -ifndef P_MAIN - P_MAIN = ../../main -endif -ifndef P_LIBR - P_LIBR = ../../common -endif - -#P_INCL = -I$(P_MAIN) -I$(P_LIBR) -P_INCL = -I$(P_MAIN) -I$(P_LIBR) - -C_COMPILER_OPTIONS = $(P_INCL) -m32 -ansi -DCOMPILE_ANSI_ONLY -O3 -c - -CPP_COMPILER_OPTIONS = $(P_INCL) -D_LIB -m32 -ansi -O3 -frtti -c - - -LINKER_OPTIONS = -m32 - - - -INCHI_SRCS = $(P_LIBR)/ichican2.c $(P_LIBR)/ichiprt1.c \ - $(P_LIBR)/ichicano.c $(P_LIBR)/ichiprt2.c \ - $(P_LIBR)/ichicans.c $(P_LIBR)/ichiprt3.c \ - $(P_LIBR)/ichiisot.c $(P_LIBR)/ichiqueu.c \ - $(P_LIBR)/ichiring.c \ - $(P_LIBR)/ichimak2.c $(P_LIBR)/ichisort.c \ - $(P_LIBR)/ichimake.c $(P_LIBR)/ichister.c \ - $(P_LIBR)/ichimap1.c $(P_LIBR)/ichitaut.c \ - $(P_LIBR)/ichimap2.c $(P_LIBR)/ichi_bns.c \ - $(P_LIBR)/runichi.c \ - $(P_LIBR)/ichi_io.c \ - $(P_LIBR)/ichimap4.c $(P_LIBR)/strutil.c \ - $(P_LIBR)/ichinorm.c $(P_LIBR)/util.c \ - $(P_LIBR)/ichiparm.c \ - $(P_LIBR)/ichiread.c $(P_LIBR)/ichirvr1.c \ - $(P_LIBR)/ichirvr2.c $(P_LIBR)/ichirvr3.c \ - $(P_LIBR)/ichirvr4.c $(P_LIBR)/ichirvr5.c \ - $(P_LIBR)/ichirvr6.c $(P_LIBR)/ichirvr7.c \ - $(P_LIBR)/ikey_base26.c $(P_LIBR)/ikey_dll.c \ - $(P_LIBR)/sha2.c \ - $(P_MAIN)/dispstru.c $(P_MAIN)/mol2atom.c \ - $(P_MAIN)/ichialt.c $(P_MAIN)/readinch.c \ - $(P_MAIN)/ichimain.c $(P_MAIN)/readmol.c - - -INCHI_OBJS = ichican2.o ichiprt1.o \ - ichicano.o ichiprt2.o \ - ichicans.o ichiprt3.o \ - ichiisot.o ichiqueu.o \ - ichiring.o \ - ichimak2.o ichisort.o \ - ichimake.o ichister.o \ - ichimap1.o ichitaut.o \ - ichimap2.o ichi_bns.o \ - runichi.o \ - ichi_io.o \ - ichimap4.o strutil.o \ - ichinorm.o util.o \ - ichiparm.o \ - ichiread.o ichirvr1.o \ - ichirvr2.o ichirvr3.o \ - ichirvr4.o ichirvr5.o \ - ichirvr6.o ichirvr7.o \ - ikey_base26.o ikey_dll.o \ - sha2.o \ - dispstru.o mol2atom.o \ - readinch.o \ - ichimain.o readmol.o - - -$(INCHI_EXECUTABLE) : $(INCHI_OBJS) - $(LINKER) $(LINKER_OPTIONS) -o $(INCHI_EXECUTABLE) $(INCHI_OBJS) -lm - -%.o: $(P_LIBR)/%.c - $(C_COMPILER) $(C_COMPILER_OPTIONS) $< - -%.o: $(P_MAIN)/%.c - $(C_COMPILER) $(C_COMPILER_OPTIONS) $< - -%.o: $(P_MAIN)/%.cpp - $(CPP_COMPILER) $(CPP_COMPILER_OPTIONS) $< - diff --git a/INCHI-1-SRC/INCHI/gcc/inchi-1/readme.txt b/INCHI-1-SRC/INCHI/gcc/inchi-1/readme.txt deleted file mode 100644 index 45b638d..0000000 --- a/INCHI-1-SRC/INCHI/gcc/inchi-1/readme.txt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - - -This directory contains gcc makefiles to create inchi-1 executable. - -The makefiles were tested with -- gcc version 3.4.5 (mingw-special) under WinXP Pro SP2; -- gcc v. 4.1.2 20061115 (Debian 4.1.1-21) under Debian Etch; -- gcc v. 4.2.4 under Ubuntu 2.6.24-24-server. - -The makefile should be placed into a directory that has a common parent with -"main" and "common" directories as it is in the zip file containing the source -code and this makefile. - -To use function times() instead of clock() for detecting timeout remove -ansi -compiler option and add option -DINCHI_USETIMES - - -========= - FILES -========= - -readme.txt This file - -makefile Creates: - 32-bit executable under 32-bit Windows and 32-bit Linux, - or 64-bit executable under 64-bit Linux - -makefile32 Use to make 32-bit executable under 64-bit Linux - - - -========= - LINKS -========= - -IUPAC http://www.iupac.org/inchi -InChI Trust http://www.inchi-trust.org -InChI discussion group https://lists.sourceforge.net/lists/listinfo/inchi-discuss diff --git a/INCHI-1-SRC/INCHI/main/debug.c b/INCHI-1-SRC/INCHI/main/debug.c deleted file mode 100644 index 8cee53c..0000000 --- a/INCHI-1-SRC/INCHI/main/debug.c +++ /dev/null @@ -1,138 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include "mode.h" - -#ifdef CML_DEBUG - -#include "debug.h" - -void PrintInpAtom (FILE *file_p, const inp_ATOM * const at_p, const unsigned int num_atoms) -{ - unsigned int i, j; - const AT_NUMB *at_num_p; - const U_CHAR *u_char_p; - const S_CHAR *s_char_p; - const char *char_p; - - const inp_ATOM *atom_p = at_p; - - for (j = 0; j < num_atoms; ++ j, ++ atom_p) - { - - fprintf (file_p, "\n**********************************\n"); - - if (!atom_p) - { - fprintf (file_p, "inp_Atom is NULL."); - return; - } - - fprintf (file_p, "elname: %s\n", atom_p -> elname); - fprintf (file_p, "el_number: %d\n", (int) (atom_p -> el_number)); - - fprintf (file_p, "neighbor:\n"); - at_num_p = atom_p -> neighbor; - - for (i = 0; i < MAXVAL; ++ i, ++ at_num_p) - { - fprintf (file_p, "%d ", (int) (*at_num_p)); - } - - fprintf (file_p, "\norig_at_number: %d\n", (int) (atom_p -> orig_at_number)); - fprintf (file_p, "orig_compt_at_numb: %d\n", (int) (atom_p -> orig_compt_at_numb)); - - fprintf (file_p, "bond_stereo:\n"); - s_char_p = atom_p -> bond_stereo; - for (i = 0; i < MAXVAL; ++ i, ++ s_char_p) - { - fprintf (file_p, "%d ", (int) (*s_char_p)); - } - - fprintf (file_p, "\nbond_type:\n"); - u_char_p = atom_p -> bond_type; - for (i = 0; i < MAXVAL; ++ i, ++ u_char_p) - { - fprintf (file_p, "%d ", (int) (*u_char_p)); - } - - fprintf (file_p, "\nvalence: %d\n", (int) (atom_p -> valence)); - fprintf (file_p, "chem_bonds_valence: %d\n", (int) (atom_p -> chem_bonds_valence)); - fprintf (file_p, "num_H: %d\n", (int) (atom_p -> num_H)); - - fprintf (file_p, "num_iso_H:\n"); - char_p = atom_p -> num_iso_H; - for (i = 0; i < NUM_H_ISOTOPES; ++ i, ++ char_p) - { - fprintf (file_p, "%d ", (int) (*char_p)); - } - - fprintf (file_p, "\niso_atw_diff: %d\n", (int) (atom_p -> iso_atw_diff)); - fprintf (file_p, "charge: %d\n", (int) (atom_p -> charge)); - fprintf (file_p, "radical: %d\n", (int) (atom_p -> radical)); - fprintf (file_p, "bAmbiguousStereo: %d\n", (int) (atom_p -> bAmbiguousStereo)); - fprintf (file_p, "cFlags: %d\n", (int) (atom_p -> cFlags)); - fprintf (file_p, "cInputParity: %d\n", (int) (atom_p -> p_parity)); - fprintf (file_p, "at_type: %d\n", (int) (atom_p -> at_type)); - fprintf (file_p, "component: %d\n", (int) (atom_p -> component)); - fprintf (file_p, "endpoint: %d\n", (int) (atom_p -> endpoint)); - fprintf (file_p, "c_point: %d\n", (int) (atom_p -> c_point)); - - fprintf (file_p, "x: %lf\n", (atom_p -> x)); - fprintf (file_p, "y: %lf\n", (atom_p -> y)); - fprintf (file_p, "z: %lf\n", (atom_p -> z)); - - #if ( FIND_RING_SYSTEMS == 1 ) - fprintf (file_p, "bCutVertex: %d\n", (int) (atom_p -> bCutVertex)); - fprintf (file_p, "nRingSystem: %d\n", (int) (atom_p -> nRingSystem)); - fprintf (file_p, "nNumAtInRingSystem: %d\n", (int) (atom_p -> nNumAtInRingSystem)); - fprintf (file_p, "nBlockSystem: %d\n", (int) (atom_p -> nBlockSystem)); - #endif - - #if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) - fprintf (file_p, "nDistanceFromTerminal: %d\n", (int) (atom_p -> nDistanceFromTerminal)); - #endif - - } -} - -#else -int cml_debug; /* keep compiler happy */ -#endif /* CML_DEBUG */ diff --git a/INCHI-1-SRC/INCHI/main/debug.h b/INCHI-1-SRC/INCHI/main/debug.h deleted file mode 100644 index 4dc0cf9..0000000 --- a/INCHI-1-SRC/INCHI/main/debug.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __DEBUG_H__ -#define __DEBUG_H__ -#include - -#include "mode.h" - -#ifdef CML_DEBUG -#include "inpdef.h" - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" -{ -#endif -#endif - -void PrintInpAtom (FILE *f_p, const inp_ATOM * const atom_p, const unsigned int num_atoms); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -#endif /* CML_DEBUG */ - -#endif /* __DEBUG_H__ */ - diff --git a/INCHI-1-SRC/INCHI/main/mol2atom.c b/INCHI-1-SRC/INCHI/main/mol2atom.c deleted file mode 100644 index 307366a..0000000 --- a/INCHI-1-SRC/INCHI/main/mol2atom.c +++ /dev/null @@ -1,1096 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include - -#include "mode.h" - -#include "incomdef.h" /*^^^ comdef.h renamed to incomdef.h (avoid collision with MS comdef.h) */ -#include "readmol.h" -#include "inpdef.h" -#include "util.h" - -#include "ichicomp.h" - -#if ( ADD_CMLPP == 1 ) -#include "debug.h" -#endif -#include "mol2atom.h" - -#define MIN_STDATA_X_COORD 0.0 -#define MAX_STDATA_X_COORD 256.0 -#define MIN_STDATA_Y_COORD 0.0 -#define MAX_STDATA_Y_COORD 256.0 -#define MIN_STDATA_Z_COORD 0.0 -#define MAX_STDATA_Z_COORD 256.0 -#define MAX_STDATA_AVE_BOND_LENGTH 20.0 -#define MIN_STDATA_AVE_BOND_LENGTH 10.0 - - -/* local prototypes */ -inp_ATOM* mol_to_atom( MOL_DATA* mol_data, int *num_atoms, int *num_bonds, inp_ATOM* at_inp, int bDoNotAddH, int *err, char *pStrErr ); -int mol_to_atom_xyz( MOL_DATA* mol_data, int num_atoms, inp_ATOM* at, int *err, char *pStrErr ); -long GetMolfileNumber( MOL_HEADER_BLOCK *pHdr ); - -/******************************************************************************************************/ -void FreeInpAtom( inp_ATOM **at ) -{ - if ( at && *at ) { - inchi_free( *at ); - *at = NULL; - } -} -/******************************************************************************************************/ -inp_ATOM *CreateInpAtom( int num_atoms ) -{ - - void *p = inchi_calloc(num_atoms, sizeof(inp_ATOM) ); - /* -- for debug only -- - if ( p == (void*)0x00CAD558 ) { - int stop = 1; - } - */ - return (inp_ATOM* )p; - - /* return (inp_ATOM* ) inchi_calloc(num_atoms, sizeof(inp_ATOM) ); */ -} -/******************************************************************************************************/ -void FreeInpAtomData( INP_ATOM_DATA *inp_at_data ) -{ - if ( inp_at_data ) { - FreeInpAtom( &inp_at_data->at ); - FreeInpAtom( &inp_at_data->at_fixed_bonds ); - memset( inp_at_data, 0, sizeof(*inp_at_data) ); - } -} -/******************************************************************************************************/ -int CreateInpAtomData( INP_ATOM_DATA *inp_at_data, int num_atoms, int create_at_fixed_bonds ) -{ - FreeInpAtomData( inp_at_data ); - if ( (inp_at_data->at = CreateInpAtom( num_atoms )) && - (!create_at_fixed_bonds || (inp_at_data->at_fixed_bonds = CreateInpAtom( num_atoms) ) ) ) { - inp_at_data->num_at = num_atoms; - return 1; - } - FreeInpAtomData( inp_at_data ); - return 0; -} -/******************************************************************************************************/ -void FreeCompAtomData( COMP_ATOM_DATA *inp_at_data ) -{ - FreeInpAtom( &inp_at_data->at ); - if ( inp_at_data->nOffsetAtAndH ) - inchi_free( inp_at_data->nOffsetAtAndH ); - memset( inp_at_data, 0, sizeof(*inp_at_data) ); -} -#ifndef COMPILE_ANSI_ONLY -/******************************************************************************************************/ -int CreateCompAtomData( COMP_ATOM_DATA *inp_at_data, int num_atoms, int num_components, int bIntermediateTaut ) -{ - FreeCompAtomData( inp_at_data ); - if ( (inp_at_data->at = CreateInpAtom( num_atoms )) && - (num_components <= 1 || bIntermediateTaut || - (inp_at_data->nOffsetAtAndH = (AT_NUMB*)inchi_calloc(sizeof(inp_at_data->nOffsetAtAndH[0]), 2*(num_components+1))))) { - - inp_at_data->num_at = num_atoms; - inp_at_data->num_components = (num_components>1)? num_components : 0; - return 1; - } - FreeCompAtomData( inp_at_data ); - return 0; -} - -/******************************************************************************************************/ -void FreeInfAtom( inf_ATOM **at ) -{ - if ( at && *at ) { - inchi_free( *at ); - *at = NULL; - } -} -/******************************************************************************************************/ -inf_ATOM *CreateInfAtom( int num_atoms ) -{ - return (inf_ATOM* ) inchi_calloc(num_atoms, sizeof(inf_ATOM) ); -} -/******************************************************************************************************/ -void FreeInfoAtomData( INF_ATOM_DATA *inf_at_data ) -{ - FreeInfAtom( &inf_at_data->at ); - if ( inf_at_data->pStereoFlags ) - inchi_free( inf_at_data->pStereoFlags ); - memset(inf_at_data, 0, sizeof(*inf_at_data)); -} -/******************************************************************************************************/ -int CreateInfoAtomData( INF_ATOM_DATA *inf_at_data, int num_atoms, int num_components ) -{ - FreeInfoAtomData( inf_at_data ); - memset( inf_at_data, 0, sizeof(*inf_at_data) ); - if ( (inf_at_data->at = CreateInfAtom( num_atoms )) && - (num_components <= 1 || - (inf_at_data->pStereoFlags = (AT_NUMB *)inchi_calloc(num_components+1, sizeof(inf_at_data->pStereoFlags[0]))) - ) - ) { - inf_at_data->num_at = num_atoms; - inf_at_data->num_components = num_components; - return 1; - } - FreeInfoAtomData( inf_at_data ); - return 0; -} -/******************************************************************************************************/ -int AllocateInfoAtomData( INF_ATOM_DATA *inf_at_data, int num_atoms, int num_components ) -{ - if ( inf_at_data->at = CreateInfAtom( num_atoms ) ) { - if ( num_components > 1 && - !(inf_at_data->pStereoFlags = (AT_NUMB *)inchi_calloc(num_components+1, sizeof(inf_at_data->pStereoFlags[0]))) ) { - FreeInfAtom( &inf_at_data->at ); - return 0; - } - return 1; - } - return 0; -} -/******************************************************************************************************/ -int DuplicateInfoAtomData( INF_ATOM_DATA *inf_at_data_to, const INF_ATOM_DATA *inf_at_data_from) -{ - *inf_at_data_to = *inf_at_data_from; - if ( AllocateInfoAtomData( inf_at_data_to, inf_at_data_from->num_at, inf_at_data_from->num_components ) ) { - memcpy( inf_at_data_to->at, inf_at_data_from->at, - inf_at_data_from->num_at * sizeof(inf_at_data_to->at[0])); - if ( inf_at_data_to->pStereoFlags && inf_at_data_from->pStereoFlags ) { - memcpy( inf_at_data_to->pStereoFlags, inf_at_data_from->pStereoFlags, - (inf_at_data_from->num_components+1)*sizeof(inf_at_data_to->pStereoFlags[0])); - } - return 1; - } - return 0; -} -#endif /* ifndef COMPILE_ANSI_ONLY */ - - -#if ( TEST_RENUMB_ATOMS == 1 ) /* { */ - -/******************************************************************************************************/ -int CopyInpAtomData( INP_ATOM_DATA *dest_inp_at_data, INP_ATOM_DATA *src_inp_at_data ) -{ - int ret = 1; - if ( !dest_inp_at_data->at || dest_inp_at_data->num_at != src_inp_at_data->num_at ) { - ret = CreateInpAtomData( dest_inp_at_data, src_inp_at_data->num_at, (NULL != src_inp_at_data->at_fixed_bonds) ); - } else { - inp_ATOM *at = dest_inp_at_data->at; /* save ptr to already allocated memory */ - inp_ATOM *at2 = dest_inp_at_data->at_fixed_bonds; - *dest_inp_at_data = *src_inp_at_data; /* copy all other (scalar) data */ - dest_inp_at_data->at = at; /* restore ptr to already allocated memory */ - dest_inp_at_data->at_fixed_bonds = at2; - } - if ( ret ) { - memcpy( dest_inp_at_data->at, src_inp_at_data->at, - src_inp_at_data->num_at*sizeof(dest_inp_at_data->at[0]) ); - if ( dest_inp_at_data->at_fixed_bonds && src_inp_at_data->at_fixed_bonds ) { - memcpy( dest_inp_at_data->at_fixed_bonds, src_inp_at_data->at_fixed_bonds, - src_inp_at_data->num_at*sizeof(dest_inp_at_data->at_fixed_bonds[0]) ); - } - } - return ret; -} -/******************************************************************************************************/ -void RenumbInpAtomData( INP_ATOM_DATA *dest_inp_at_data, INP_ATOM_DATA *src_inp_at_data, AT_RANK *new_ord ) -{ - int j, n, m, val; -#if ( TEST_RENUMB_NEIGH == 1 ) - int i, k; -#endif - int num_atoms = src_inp_at_data->num_at; - inp_ATOM *dest_at = dest_inp_at_data->at; - for ( n = 0; n < num_atoms; n ++ ) { - m = new_ord[n]; - dest_at[m] = src_inp_at_data->at[n]; - dest_at[m].orig_compt_at_numb = (AT_NUMB)(m+1); /* new ordering number within the component */ - val = dest_at[m].valence; - for ( j = 0; j < val; j ++ ) { - dest_at[m].neighbor[j] = new_ord[dest_at[m].neighbor[j]]; - } -#if ( TEST_RENUMB_NEIGH == 1 ) - for ( i = 0; i < val; i ++ ) { - j = i; - k = j + (rand() * (val-j)) / (RAND_MAX+1); - if ( k >= val || j == k ) { - continue; - } - swap( (char*)&dest_at[m].neighbor[j], (char*)&dest_at[m].neighbor[k], sizeof(dest_at[0].neighbor[0]) ); - swap( (char*)&dest_at[m].bond_stereo[j], (char*)&dest_at[m].bond_stereo[k], sizeof(dest_at[0].bond_stereo[0]) ); - swap( (char*)&dest_at[m].bond_type[j], (char*)&dest_at[m].bond_type[k], sizeof(dest_at[0].bond_type[0]) ); - /* adjust stereo bond links */ - if ( dest_at[m].sb_parity[0] ) { - int a; - for ( a = 0; a < MAX_NUM_STEREO_BONDS && dest_at[m].sb_parity[a]; a ++ ) { - - if ( k == (int)dest_at[m].sb_ord[a] ) { - dest_at[m].sb_ord[a] = j; - } else - if ( j == (int)dest_at[m].sb_ord[a] ) { - dest_at[m].sb_ord[a] = k; - } - - if ( k == (int)dest_at[m].sn_ord[a] ) { - dest_at[m].sn_ord[a] = j; - } else - if ( j == (int)dest_at[m].sn_ord[a] ) { - dest_at[m].sn_ord[a] = k; - } - } - } - } -#endif - } -} -/******************************************************************************************************/ -void MakeNewOrd( int num_atoms, AT_RANK *new_ord ) -{ - int i, j, k; - for ( i = 0; i < num_atoms; i ++ ) { - j = i; - k = (rand() * (num_atoms-i)) / (RAND_MAX+1); - if ( k >= num_atoms || j == k ) { - continue; - } - swap( (char*)&new_ord[j], (char*)&new_ord[k], sizeof(new_ord[0]) ); - } -} -#endif /* } TEST_RENUMB_ATOMS == 1 */ - -/******************************************************************************************************/ -inp_ATOM* mol_to_atom( MOL_DATA* mol_data, int *num_atoms, int *num_bonds, inp_ATOM* at_inp, - int bDoNotAddH, int *err, char *pStrErr ) -{ - inp_ATOM *at = NULL; - /* char *bond_stereo = NULL; */ - AT_NUMB *p1, *p2; - int i, a1, a2, n1, n2, bonds, iso_atw_diff; - char cBondStereo, cBondType; - static int el_number_H = 0; - - - if ( !el_number_H ) { - el_number_H = get_periodic_table_number( "H" ); /* one-time initialization */ - } - - *err = 0; - *num_atoms = *num_bonds = 0; - /* check if MOLfile contains atoms */ - if ( !mol_data || !mol_data->ctab.MolAtom || - 0 < mol_data->ctab.nNumberOfBonds && !mol_data->ctab.MolBond || - 0 >= (*num_atoms = mol_data->ctab.nNumberOfAtoms) ) { - /* MOLFILE_ERR_SET (*err, 0, "Empty structure"); */ - goto exit_function; /* no structure */ - } - /* allocate memory if necessary */ - if ( at_inp ) { - at = at_inp; - } else - if ( !(at = CreateInpAtom( *num_atoms ) ) ) { - *err = -1; - MOLFILE_ERR_FIN (*err, -1, exit_function, "Out of RAM"); - } - - /* copy atom info */ - for ( i = 0; i < *num_atoms; i ++ ) { - mystrncpy( at[i].elname, mol_data->ctab.MolAtom[i].szAtomSymbol, sizeof(at->elname) ); - /* at[i].chem_bonds_valence = mol_data->ctab.MolAtom[i].cValence; */ /* MOLfile valence; will change */ - at[i].orig_at_number = (AT_NUMB)(i+1); - at[i].iso_atw_diff = mol_data->ctab.MolAtom[i].cMassDifference; - at[i].charge = mol_data->ctab.MolAtom[i].cCharge; - at[i].radical = mol_data->ctab.MolAtom[i].cRadical; - /* see mol_to_atom_xyz() - at[i].x = mol_data->ctab.MolAtom[i].fX; - at[i].y = mol_data->ctab.MolAtom[i].fY; - at[i].z = mol_data->ctab.MolAtom[i].fZ; - */ - iso_atw_diff = mol_data->ctab.MolAtom[i].cMassDifference; - at[i].iso_atw_diff = iso_atw_diff==ZERO_ATW_DIFF? 1: - iso_atw_diff> 0? iso_atw_diff+1: - iso_atw_diff; -#if ( SINGLET_IS_TRIPLET == 1 ) - if ( at[i].radical == RADICAL_SINGLET ) { - at[i].radical = RADICAL_TRIPLET; - } -#endif -#if ( bRELEASE_VERSION != 1 ) - if ( isdigit( at[i].elname[0] ) ) { /* for testing */ - mystrncpy( at[i].elname, "C", sizeof(at->elname) ); - } -#endif - if ( ERR_ELEM == (n1 = get_periodic_table_number( at[i].elname ) ) ) { - /* Case when elname contains more than 1 element: extract number of H if possible */ - at[i].num_H = extract_H_atoms( at[i].elname, at[i].num_iso_H ); - if ( !at[i].elname[0] && NUMH(at, i) ) { - /* alias contains only H. Added 2004-07-21, fixed 2004-07-22 - * move the heaviest isotope to the "central atom" - * Note: this must be consistent with H-H treatment in remove_terminal_HDT() - */ - strcpy( at[i].elname, "H" ); - if ( NUM_ISO_H(at,i) ) { - int j; - for ( j = NUM_H_ISOTOPES-1; 0 <= j; j -- ) { - if ( at[i].num_iso_H[j] ) { - at[i].num_iso_H[j] --; - at[i].iso_atw_diff = 1 + j; - break; - } - } - } else { - at[i].num_H --; - } - } - if ( ERR_ELEM == (n1 = get_periodic_table_number( at[i].elname ) ) ) { - n1 = 0; - } - } - - at[i].el_number = (U_CHAR) n1; - if ( !n1 ) { - *err |= 64; /* Unrecognized aromatic bond(s) replaced with single */ - MOLFILE_ERR_SET (*err, 0, "Unknown element(s):"); - MOLFILE_ERR_SET (*err, 0, at[i].elname); - } else - /* replace explicit D or T with isotopic H (added 2003-06-02) */ - if ( el_number_H == n1 && !at[i].iso_atw_diff ) { - switch( at[i].elname[0] ) { - case 'D': - at[i].iso_atw_diff = 2; - mystrncpy( at[i].elname, "H", sizeof(at->elname) ); - break; - case 'T': - at[i].iso_atw_diff = 3; - mystrncpy( at[i].elname, "H", sizeof(at->elname) ); - break; - } - } - } - - - /*---------------- stereo information notes. ------------------------ - - Currently: 1. stereo sign - ========= -------------- - MOLfile (atom number = MOLfile atom number - 1, no stdata as an intermediate) - | if mol_data->ctab.MolBond[i].nAtomNo1 < mol_data->ctab.MolBond[i].nAtomNo2 - v then - inp_ATOM stereo > 0 - else - stereo < 0 - - 2. neighbor z-coordinate - ------------------------ - neighbor z-coord > 0 for Up if sign(stdata_bond_no) = sign(at[i].neighbor[j]-i) - - --------------------------------------------------------------------*/ - - /* copy bond info */ - for ( i = 0, bonds = 0; i < mol_data->ctab.nNumberOfBonds; i ++ ) { - cBondStereo = mol_data->ctab.MolBond[i].cBondStereo; - cBondType = mol_data->ctab.MolBond[i].cBondType; - a1 = mol_data->ctab.MolBond[i].nAtomNo1-1; - a2 = mol_data->ctab.MolBond[i].nAtomNo2-1; - - if ( a1 < 0 || a1 >= *num_atoms || - a2 < 0 || a2 >= *num_atoms || - a1 == a2 ) { - *err |= 1; /* bond for impossible atom number(s); ignored */ - MOLFILE_ERR_SET (*err, 0, "Bond to nonexistent atom"); - continue; - } - /* check for multiple bonds between same atoms */ - p1 = is_in_the_list( at[a1].neighbor, (AT_NUMB)a2, at[a1].valence ); - p2 = is_in_the_list( at[a2].neighbor, (AT_NUMB)a1, at[a2].valence ); - if ( (p1 || p2) && (p1 || at[a1].valence < MAXVAL) && (p2 || at[a2].valence < MAXVAL) ) { - n1 = p1? (p1 - at[a1].neighbor) : at[a1].valence ++; - n2 = p2? (p2 - at[a2].neighbor) : at[a2].valence ++; - MOLFILE_ERR_SET (*err, 0, "Multiple bonds between two atoms"); - *err |= 2; /* multiple bonds between atoms */ - } else - if ( !p1 && !p2 && at[a1].valence < MAXVAL && at[a2].valence < MAXVAL ) { - n1 = at[a1].valence ++; - n2 = at[a2].valence ++; - bonds ++; - } else { - char szMsg[64]; - *err |= 4; /* too large number of bonds. Some bonds ignored. */ - sprintf( szMsg, "Atom '%s' has more than %d bonds", - at[a1].valence>= MAXVAL? at[a1].elname:at[a2].elname, MAXVAL ); - MOLFILE_ERR_SET (*err, 0, szMsg); - continue; - } - if ( cBondType < MIN_INPUT_BOND_TYPE || cBondType > MAX_INPUT_BOND_TYPE ) { - char szBondType[16]; - sprintf( szBondType, "%d", cBondType ); - cBondType = 1; - MOLFILE_ERR_SET (*err, 0, "Unrecognized bond type:"); - MOLFILE_ERR_SET (*err, 0, szBondType); - *err |= 8; /* Unrecognized Bond type replaced with single bond */ - } - /* bond type */ - at[a1].bond_type[n1] = - at[a2].bond_type[n2] = cBondType; - /* connection */ - at[a1].neighbor[n1] = (AT_NUMB)a2; - at[a2].neighbor[n2] = (AT_NUMB)a1; - /* stereo */ - if ( cBondStereo == INPUT_STEREO_DBLE_EITHER /* 3 */ ) { - at[a1].bond_stereo[n1] = - at[a2].bond_stereo[n2] = STEREO_DBLE_EITHER; - } else - if ( cBondStereo == INPUT_STEREO_SNGL_UP || /* 1 */ - cBondStereo == INPUT_STEREO_SNGL_EITHER || /* 4 */ - cBondStereo == INPUT_STEREO_SNGL_DOWN /* 6 */ ) { - char cStereo; - switch ( cBondStereo ) { - case INPUT_STEREO_SNGL_UP: - cStereo = STEREO_SNGL_UP; - break; - case INPUT_STEREO_SNGL_EITHER: - cStereo = STEREO_SNGL_EITHER; - break; - case INPUT_STEREO_SNGL_DOWN: - cStereo = STEREO_SNGL_DOWN; - break; - } - at[a1].bond_stereo[n1] = cStereo; /* >0: the wedge (pointed) end is at this atom, a1 */ - at[a2].bond_stereo[n2] = -cStereo; /* <0: the wedge (pointed) end is at the opposite atom, a1 */ - } else - if ( cBondStereo ) { - *err |= 16; /* Ignored unrecognized Bond stereo */ - MOLFILE_ERR_SET (*err, 0, "Unrecognized bond stereo"); - continue; - } - } - *num_bonds = bonds; - - - /* special valences */ - calculate_valences (mol_data, at, num_atoms, bDoNotAddH, err, pStrErr); - -exit_function:; - return at; -} -/******************************************************************************************************/ -void calculate_valences (MOL_DATA* mol_data, inp_ATOM* at, int *num_atoms, int bDoNotAddH, int *err, char *pStrErr) -{ - int bNonMetal; - int a1, a2, n1, n2, valence; - AT_NUMB *p1; - - /* special valences */ - for ( bNonMetal = 0; bNonMetal < 2; bNonMetal ++ ) { - for ( a1 = 0; a1 < *num_atoms; a1 ++ ) { - int num_bond_type[MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE + 1], bond_type, bHasMetalNeighbor; - /* should the "!=" be replaced with "==" ??? */ - if ( bNonMetal == is_el_a_metal( at[a1].el_number ) ) { - continue; /* first process all metals, after that all non-metals */ - } - memset( num_bond_type, 0, sizeof(num_bond_type) ); - - /* valence = at[a1].chem_bonds_valence; */ /* save atom valence if available */ - /* 2006-08-31: fix for uncharged >N(IV)- in an aromatic ring */ - valence = (mol_data && mol_data->ctab.MolAtom) ? mol_data->ctab.MolAtom[a1].cValence : at[a1].chem_bonds_valence; - - at[a1].chem_bonds_valence = 0; - bHasMetalNeighbor = 0; - for ( n1 = 0; n1 < at[a1].valence; n1 ++ ) { - bond_type = at[a1].bond_type[n1] - MIN_INPUT_BOND_TYPE; - if ( bond_type < 0 || bond_type > MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE ) { - bond_type = 0; - MOLFILE_ERR_SET (*err, 0, "Unknown bond type in MOLfile assigned as a single bond"); - } - num_bond_type[ bond_type ] ++; - /* -- too a radical solution -- removed from next to ver 1.12B --- */ - } - for ( n1 = 0; MIN_INPUT_BOND_TYPE + n1 <= 3 && MIN_INPUT_BOND_TYPE + n1 <= MAX_INPUT_BOND_TYPE; n1 ++ ) { - /* add all bond orders except for "aromatic" bonds */ - at[a1].chem_bonds_valence += (MIN_INPUT_BOND_TYPE + n1) * num_bond_type[n1]; - } - n2 = 0; - if ( MIN_INPUT_BOND_TYPE <= BOND_TYPE_ALTERN && BOND_TYPE_ALTERN <= MAX_INPUT_BOND_TYPE && - ( n2 = num_bond_type[BOND_TYPE_ALTERN-MIN_INPUT_BOND_TYPE] ) ) { - /* accept input aromatic bonds for now */ - switch ( n2 ) { - case 2: - at[a1].chem_bonds_valence += 3; /* =A- */ - break; - case 3: - at[a1].chem_bonds_valence += 4; /* =A< */ - break; - default: - /* if 1 or >= 4 aromatic bonds then replace such bonds with single bonds */ - /* and detect an error in the input structure */ - for ( n1 = 0; n1 < at[a1].valence; n1 ++ ) { - if ( at[a1].bond_type[n1] == BOND_TYPE_ALTERN ) { - a2 = at[a1].neighbor[n1]; - p1 = is_in_the_list( at[a2].neighbor, (AT_NUMB)a1, at[a2].valence ); - if ( p1 ) { - at[a1].bond_type[n1] = - at[a2].bond_type[p1-at[a2].neighbor] = BOND_TYPE_SINGLE; - } else { - *err = -2; /* Program error */ - MOLFILE_ERR_SET (*err, 0, "Program error interpreting MOLfile"); - return; /* no structure */ - } - } - } - at[a1].chem_bonds_valence += n2; - *err |= 32; - MOLFILE_ERR_SET (*err, 0, "Atom has 1 or more than 3 aromatic bonds"); - n2 = 0; - break; - } - } - if ( n2 && !valence ) { - /* atom has aromatic bonds AND the chemical valence is not known */ - int num_H = NUMH(at, a1); - /* bug fix 2006-08-25: aliased H result in num_H > 0 => wrong call to detect_unusual_el_valence() */ - int chem_valence = at[a1].chem_bonds_valence /*+ num_H*/; - int bUnusualValenceArom = - detect_unusual_el_valence( (int)at[a1].el_number, at[a1].charge, - at[a1].radical, chem_valence, - num_H, at[a1].valence ); - int bUnusualValenceNoArom = - detect_unusual_el_valence( (int)at[a1].el_number, at[a1].charge, - at[a1].radical, chem_valence-1, - num_H, at[a1].valence ); -#if ( CHECK_AROMBOND2ALT == 1 ) - if ( bUnusualValenceArom && !bUnusualValenceNoArom && 0 == nBondsValToMetal( at, a1) ) -#else - if ( bUnusualValenceArom && !bUnusualValenceNoArom ) -#endif - { - /* typically NH in 5-member aromatic ring */ - at[a1].chem_bonds_valence --; - } - } else - if ( n2 && valence ) { - /* atom has aromatic bonds AND the chemical valence is known */ - int num_H = NUMH(at, a1); - int chem_valence = at[a1].chem_bonds_valence + num_H; - if ( valence == chem_valence-1 ) { - /* typically NH in 5-member aromatic ring */ - at[a1].chem_bonds_valence --; - } - } - - /************************************************************************************* - * - * Set number of hydrogen atoms - */ - if (mol_data) { - at[a1].num_H = get_num_H( at[a1].elname, at[a1].num_H, at[a1].num_iso_H, - at[a1].charge, at[a1].radical, - at[a1].chem_bonds_valence, - mol_data->ctab.MolAtom[a1].cValence, /* instead of valence */ - mol_data->ctab.MolAtom[a1].cAtomAliasedFlag, - bDoNotAddH, bHasMetalNeighbor ); - } - } - } -} -/******************************************************************************************************/ -int mol_to_atom_xyz( MOL_DATA* mol_data, int num_atoms, inp_ATOM* at, int *err, char *pStrErr ) -{ - int i, num_dimensions=0; - int num_bonds; - double max_x=-1.0e32, max_y=-1.0e32, max_z=-1.0e32; - double min_x= 1.0e32, min_y= 1.0e32, min_z= 1.0e32; - double macheps = 1.0e-10, small_coeff = 0.00001; - double x_coeff, y_coeff, z_coeff, coeff, average_bond_length; - - /* *err = 0; */ - /* check if MOLfile contains atoms */ - if ( !mol_data || !mol_data->ctab.MolAtom || - 0 < mol_data->ctab.nNumberOfBonds && !mol_data->ctab.MolBond || - 0 >= (num_atoms = mol_data->ctab.nNumberOfAtoms) ) { - goto exit_function; /* no structure */ - } - /* copy atom info */ - for ( i = 0; i < num_atoms; i ++ ) { - max_x = inchi_max(mol_data->ctab.MolAtom[i].fX, max_x); - min_x = inchi_min(mol_data->ctab.MolAtom[i].fX, min_x); - max_y = inchi_max(mol_data->ctab.MolAtom[i].fY, max_y); - min_y = inchi_min(mol_data->ctab.MolAtom[i].fY, min_y); - max_z = inchi_max(mol_data->ctab.MolAtom[i].fZ, max_z); - min_z = inchi_min(mol_data->ctab.MolAtom[i].fZ, min_z); - } - - /* copy bond info */ - num_bonds = 0; - average_bond_length = 0.0; - for ( i = 0; i < mol_data->ctab.nNumberOfBonds; i ++ ) { - int a1 = mol_data->ctab.MolBond[i].nAtomNo1-1; - int a2 = mol_data->ctab.MolBond[i].nAtomNo2-1; - double dx = mol_data->ctab.MolAtom[a1].fX-mol_data->ctab.MolAtom[a2].fX; - double dy = mol_data->ctab.MolAtom[a1].fY-mol_data->ctab.MolAtom[a2].fY; - double dz = mol_data->ctab.MolAtom[a1].fZ-mol_data->ctab.MolAtom[a2].fZ; - - if ( a1 < 0 || a1 >= num_atoms || - a2 < 0 || a2 >= num_atoms || - a1 == a2 ) { - *err |= 1; /* bond for impossible atom number(s); ignored */ - MOLFILE_ERR_SET (*err, 0, "Bond to nonexistent atom"); - continue; - } - average_bond_length += sqrt( dx*dx + dy*dy + dz*dz ); - num_bonds ++; - } - - /* convert to integral coordinates */ - - if ( max_x - min_x <= small_coeff*(fabs(max_x) + fabs(min_x)) ) - x_coeff = 0.0; - else - x_coeff = (MAX_STDATA_X_COORD - MIN_STDATA_X_COORD)/(max_x - min_x); - - if ( max_y - min_y <= small_coeff*(fabs(max_y) + fabs(min_y)) ) - y_coeff = 0.0; - else - y_coeff = (MAX_STDATA_Y_COORD - MIN_STDATA_Y_COORD)/(max_y - min_y); - if ( max_z - min_z <= small_coeff*(fabs(max_z) + fabs(min_z)) ) - z_coeff = 0.0; - else - z_coeff = (MAX_STDATA_Z_COORD - MIN_STDATA_Z_COORD)/(max_z - min_z); - - num_dimensions = ((x_coeff > macheps || y_coeff >macheps ) && fabs(z_coeff) < macheps)? 2: - (fabs(z_coeff) > macheps)? 3: 0; - - switch ( num_dimensions ) { - case 0: - coeff = 0.0; - break; - case 2: - /* choose the smallest stretching coefficient */ - if ( x_coeff > macheps && y_coeff > macheps ) { - coeff = inchi_min( x_coeff, y_coeff ); - }else - if ( x_coeff > macheps ){ - coeff = x_coeff; - }else - if ( y_coeff > macheps ){ - coeff = y_coeff; - }else{ - coeff = 1.0; - } - break; - case 3: - /* choose the smallest stretching coefficient */ - if ( x_coeff > macheps && y_coeff > macheps ) { - coeff = inchi_min( x_coeff, y_coeff ); - coeff = inchi_min( coeff, z_coeff ); - }else - if ( x_coeff > macheps ){ - coeff = inchi_min( x_coeff, z_coeff ); - }else - if ( y_coeff > macheps ){ - coeff = inchi_min( y_coeff, z_coeff ); - }else{ - coeff = z_coeff; - } - break; - default: - coeff = 0.0; - } - - if ( num_bonds > 0 ) { - average_bond_length /= (double)num_bonds; - if ( average_bond_length * coeff > MAX_STDATA_AVE_BOND_LENGTH ) { - coeff = MAX_STDATA_AVE_BOND_LENGTH / average_bond_length; /* avoid too long bonds */ - } else - if ( average_bond_length * coeff < macheps ) { - coeff = 1.0; /* all lengths are of zero length */ - } else - if ( average_bond_length * coeff < MIN_STDATA_AVE_BOND_LENGTH ) { - coeff = MIN_STDATA_AVE_BOND_LENGTH / average_bond_length; /* avoid too short bonds */ - } - } -#if ( NORMALIZE_INP_COORD == 1 ) - /* set integral coordinates */ - for ( i = 0; i < num_atoms; i ++ ) { - double x = mol_data->ctab.MolAtom[i].fX; - double y = mol_data->ctab.MolAtom[i].fY; - double z = mol_data->ctab.MolAtom[i].fZ; - x = (x - min_x)*coeff + MIN_STDATA_X_COORD; - y = (y - min_y)*coeff + MIN_STDATA_Y_COORD; - z = (z - min_z)*coeff + MIN_STDATA_Z_COORD; - /* floor() behavior is not well defined for negative arguments. - * Use positive arguments only to get nearest integer. - */ - at[i].x = ( x >= 0.0 )? (int)floor( x + 0.5 ) : -(int)floor( -x + 0.5 ); - at[i].y = ( y >= 0.0 )? (int)floor( y + 0.5 ) : -(int)floor( -y + 0.5 ); - at[i].z = ( z >= 0.0 )? (int)floor( z + 0.5 ) : -(int)floor( -z + 0.5 ); - } -#else - /* set input coordinates */ - for ( i = 0; i < num_atoms; i ++ ) { - double x = mol_data->ctab.MolAtom[i].fX; - double y = mol_data->ctab.MolAtom[i].fY; - double z = mol_data->ctab.MolAtom[i].fZ; - at[i].x = x; - at[i].y = y; - at[i].z = z; - } -#endif - -exit_function:; - return num_dimensions; -} -/****************************************************************************/ -long GetMolfileNumber( MOL_HEADER_BLOCK *pHdr ) -{ - static char sStruct[] = "Structure #"; - static char sINCHI[] = INCHI_NAME; - long lMolfileNumber = 0; - char *p, *q = NULL; - if ( pHdr ) { - if ( !memicmp( pHdr->szMoleculeName, sStruct, sizeof(sStruct)-1 ) ) { - p = pHdr->szMoleculeName + sizeof(sStruct)-1; - lMolfileNumber = strtol( p, &q, 10 ); - p = pHdr->szMoleculeLine2; - if ( !q || *q || - memicmp( p, sINCHI, sizeof(sINCHI)-1) || - !strstr( p+sizeof(sINCHI)-1, "SDfile Output" ) ) { - lMolfileNumber = 0; - } - } - } - return lMolfileNumber; -} -/****************************************************************************/ -int MolfileToInpAtom( FILE *inp_molfile, int bDoNotAddH, inp_ATOM **at, MOL_COORD **szCoord, int max_num_at, - int *num_dimensions, int *num_bonds, const char *pSdfLabel, char *pSdfValue, - long *Id, long *lMolfileNumber, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ) -{ - int num_atoms = 0; - MOL_DATA *mol_data = NULL; - MOL_HEADER_BLOCK OnlyHeaderBlock, *pOnlyHeaderBlock = NULL, *pHdr; - MOL_CTAB OnlyCtab, *pOnlyCtab = NULL; - char cSdfValueFirstChar; -#ifdef CML_DEBUG - FILE *f_p; -#endif - if ( at ) { - pOnlyHeaderBlock = NULL; - if ( *at && max_num_at ) { - memset( *at, 0, max_num_at * sizeof(inp_ATOM) ); - } - if ( szCoord && *szCoord ) { - inchi_free( *szCoord ); - *szCoord = NULL; - } - } else { - pOnlyHeaderBlock = &OnlyHeaderBlock; - pOnlyCtab = &OnlyCtab; - } - if ( pSdfValue ) { - cSdfValueFirstChar = pSdfValue[0]; - pSdfValue[0] = '\0'; - } - - mol_data = read_sdfile_segment(inp_molfile, pOnlyHeaderBlock, pOnlyCtab, NULL != szCoord, - NULL, 0, Id, pSdfLabel, pSdfValue, err, pStrErr ); - - pHdr = ( mol_data && !pOnlyHeaderBlock )? &mol_data->hdr : - ( !mol_data && pOnlyHeaderBlock )? pOnlyHeaderBlock : NULL; - if ( lMolfileNumber && pHdr ) { - *lMolfileNumber = GetMolfileNumber( pHdr ); - } - if ( pSdfValue && !pSdfValue[0] && - pSdfLabel && pSdfLabel[0] && pHdr ) { - if ( !stricmp(pSdfLabel, "MolfileName") ) { - mystrncpy( pSdfValue, pHdr->szMoleculeName, MAX_SDF_VALUE+1 ); - LtrimRtrim( pSdfValue, NULL ); - } else - if ( !stricmp(pSdfLabel, "MolfileLine2") ) { - mystrncpy( pSdfValue, pHdr->szMoleculeLine2, MAX_SDF_VALUE+1 ); - LtrimRtrim( pSdfValue, NULL ); - } else - if ( !stricmp(pSdfLabel, "MolfileComment") ) { - mystrncpy( pSdfValue, pHdr->szComment, MAX_SDF_VALUE+1 ); - LtrimRtrim( pSdfValue, NULL ); - } else - if ( !stricmp(pSdfLabel, "MolfileIntRegNo") && pHdr->lInternalRegistryNumber ) { - sprintf( pSdfValue, "%ld", pHdr->lInternalRegistryNumber ); - } - if ( !pSdfValue[0] ) { - pSdfValue[0] = cSdfValueFirstChar; - } - } - - if ( mol_data && at && !*err ) { - /* *at points to an allocated memory */ - if ( *at && mol_data->ctab.nNumberOfAtoms <= max_num_at ) { - *at = mol_to_atom( mol_data, &num_atoms, num_bonds, *at, bDoNotAddH, err, pStrErr ); - if ( *err >= 0 ) { - *num_dimensions = mol_to_atom_xyz( mol_data, num_atoms, *at, err, pStrErr ); - - if ( szCoord ) { - *szCoord = mol_data->ctab.szCoord; - mol_data->ctab.szCoord = NULL; - } - - } - } else - /* *at points to NULL */ - if ( !*at && mol_data->ctab.nNumberOfAtoms <= max_num_at ) { - *at = mol_to_atom( mol_data, &num_atoms, num_bonds, *at, bDoNotAddH, err, pStrErr ); - if ( *err >= 0 ) { - *num_dimensions = mol_to_atom_xyz( mol_data, num_atoms, *at, err, pStrErr ); - - if ( szCoord ) { - *szCoord = mol_data->ctab.szCoord; - mol_data->ctab.szCoord = NULL; - } - - } - } else { - MOLFILE_ERR_SET (*err, 0, "Too many atoms"); - *err = 70; - num_atoms = -1; - } - if ( *err > 0 ) { - *err += 100; - } - /* 11-16-2004: use Chiral flag */ - if ( num_atoms > 0 && at && *at && mol_data && pInpAtomFlags ) { - if ( mol_data->ctab.cChiralFlag ) { - *pInpAtomFlags |= FLAG_INP_AT_CHIRAL; - } else { - *pInpAtomFlags |= FLAG_INP_AT_NONCHIRAL; - } - } - } else - if ( !at ) { - num_atoms = pOnlyCtab->nNumberOfAtoms; - } - - if ( !pOnlyHeaderBlock ) { - delete_mol_data( mol_data ); - } -#ifdef CML_DEBUG - puts ("MOL"); - f_p = fopen ("mol.dbg", "a"); - if (f_p) - { - PrintInpAtom (f_p, *at, num_atoms); - fclose (f_p); - } - else - { - puts ("Couldn't open file"); - } -#endif - - return num_atoms; -} -/**********************************************************************************/ -void FreeOrigAtData( ORIG_ATOM_DATA *orig_at_data ) -{ - if ( !orig_at_data ) - return; - FreeInpAtom( &orig_at_data->at ); - if ( orig_at_data->nCurAtLen ) { - inchi_free( orig_at_data->nCurAtLen ); - } - if ( orig_at_data->nOldCompNumber ) { - inchi_free( orig_at_data->nOldCompNumber ); - } - if ( orig_at_data->szCoord ) { - inchi_free( orig_at_data->szCoord ); - } - - if ( orig_at_data->nEquLabels ) { - inchi_free( orig_at_data->nEquLabels ); - } - if ( orig_at_data->nSortedOrder ) { - inchi_free( orig_at_data->nSortedOrder ); - } - - memset( orig_at_data, 0, sizeof(*orig_at_data) ); -} -/**********************************************************************************/ -int MolfileToOrigAtom( FILE *inp_molfile, ORIG_ATOM_DATA *orig_at_data, int bMergeAllInputStructures, - int bGetOrigCoord, int bDoNotAddH, - const char *pSdfLabel, char *pSdfValue, long *lSdfId, long *lMolfileNumber, - INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ) -{ - /* inp_ATOM *at = NULL; */ - int num_dimensions_new; - int num_inp_bonds_new; - int num_inp_atoms_new; - inp_ATOM *at_new = NULL; - inp_ATOM *at_old = NULL; - int nNumAtoms = 0; - MOL_COORD *szCoordNew = NULL; - MOL_COORD *szCoordOld = NULL; - int i, j; - - if ( pStrErr ) { - pStrErr[0] = '\0'; - } - - /*FreeOrigAtData( orig_at_data );*/ - - do { - - at_old = orig_at_data? orig_at_data->at : NULL; /* save pointer to the previous allocation */ - szCoordOld = orig_at_data? orig_at_data->szCoord : NULL; - num_inp_atoms_new = - MolfileToInpAtom( inp_molfile, bDoNotAddH, orig_at_data? &at_new:NULL, (bGetOrigCoord && orig_at_data)? &szCoordNew : NULL, MAX_ATOMS, - &num_dimensions_new, &num_inp_bonds_new, - pSdfLabel, pSdfValue, lSdfId, lMolfileNumber, pInpAtomFlags, err, pStrErr ); - - - if ( num_inp_atoms_new <= 0 && !*err ) { - MOLFILE_ERR_SET (*err, 0, "Empty structure"); - *err = 98; - } else - if ( orig_at_data && !num_inp_atoms_new && 10 < *err && *err < 20 && orig_at_data->num_inp_atoms > 0 && bMergeAllInputStructures ) { - *err = 0; /* end of file */ - break; - } else - if ( num_inp_atoms_new > 0 && orig_at_data ) { - /* merge pOrigDataTmp + orig_at_data => pOrigDataTmp; */ - nNumAtoms = num_inp_atoms_new + orig_at_data->num_inp_atoms; - if ( nNumAtoms >= MAX_ATOMS ) { - MOLFILE_ERR_SET (*err, 0, "Too many atoms"); - *err = 70; - orig_at_data->num_inp_atoms = -1; - } else - if ( !at_old ) { - /* the first structure */ - orig_at_data->at = at_new; - orig_at_data->szCoord = szCoordNew; - at_new = NULL; - szCoordNew = NULL; - orig_at_data->num_inp_atoms = num_inp_atoms_new; - orig_at_data->num_inp_bonds = num_inp_bonds_new; - orig_at_data->num_dimensions = num_dimensions_new; - } else - if ( (orig_at_data->at = ( inp_ATOM* ) inchi_calloc( nNumAtoms, sizeof(inp_ATOM) )) && - (!szCoordNew || (orig_at_data->szCoord = (MOL_COORD *) inchi_calloc( nNumAtoms, sizeof(MOL_COORD) ))) ) { - /* switch at_new <--> orig_at_data->at; */ - if ( orig_at_data->num_inp_atoms ) { - memcpy( orig_at_data->at, at_old, orig_at_data->num_inp_atoms * sizeof(orig_at_data->at[0]) ); - /* adjust numbering in the newly read structure */ - for ( i = 0; i < num_inp_atoms_new; i ++ ) { - for ( j = 0; j < at_new[i].valence; j ++ ) { - at_new[i].neighbor[j] += orig_at_data->num_inp_atoms; - } - at_new[i].orig_at_number += orig_at_data->num_inp_atoms; /* 12-19-2003 */ - } - if ( orig_at_data->szCoord && szCoordOld ) { - memcpy( orig_at_data->szCoord, szCoordOld, orig_at_data->num_inp_atoms * sizeof(MOL_COORD) ); - } - } - if ( at_old ) { - inchi_free( at_old ); - at_old = NULL; - } - if ( szCoordOld ) { - inchi_free( szCoordOld ); - szCoordOld = NULL; - } - /* copy newly read structure */ - memcpy( orig_at_data->at + orig_at_data->num_inp_atoms, - at_new, - num_inp_atoms_new * sizeof(orig_at_data->at[0]) ); - if ( orig_at_data->szCoord && szCoordNew ) { - memcpy( orig_at_data->szCoord + orig_at_data->num_inp_atoms, - szCoordNew, - num_inp_atoms_new * sizeof(MOL_COORD) ); - } - /* add other things */ - orig_at_data->num_inp_atoms += num_inp_atoms_new; - orig_at_data->num_inp_bonds += num_inp_bonds_new; - orig_at_data->num_dimensions = inchi_max(num_dimensions_new, orig_at_data->num_dimensions); - } else { - MOLFILE_ERR_SET (*err, 0, "Out of RAM"); - *err = -1; - } - } else - if ( num_inp_atoms_new > 0 ) { - nNumAtoms += num_inp_atoms_new; - } - if ( at_new ) { - inchi_free( at_new ); - at_new = NULL; - } - - } while ( !*err && bMergeAllInputStructures ); - /* - if ( !*err ) { - orig_at_data->num_components = - MarkDisconnectedComponents( orig_at_data ); - if ( orig_at_data->num_components == 0 ) { - MOLFILE_ERR_SET (*err, 0, "No components found"); - *err = 99; - } - if ( orig_at_data->num_components < 0 ) { - MOLFILE_ERR_SET (*err, 0, "Too many components"); - *err = 99; - } - } - */ - if ( szCoordNew ) { - inchi_free( szCoordNew ); - } - if ( at_new ) { - inchi_free( at_new ); - } - if ( *err ) { - FreeOrigAtData( orig_at_data ); - } - if ( *err && !(10 < *err && *err < 20) && pStrErr && !pStrErr[0] ) { - MOLFILE_ERR_SET (*err, 0, "Unknown error"); /* */ - } - return orig_at_data? orig_at_data->num_inp_atoms : nNumAtoms; -} - - diff --git a/INCHI-1-SRC/INCHI/main/mol2atom.h b/INCHI-1-SRC/INCHI/main/mol2atom.h deleted file mode 100644 index 2de7fcc..0000000 --- a/INCHI-1-SRC/INCHI/main/mol2atom.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __MOL2ATOM_H__ -#define __MOL2ATOM_H__ - -#include "readmol.h" - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -void calculate_valences (MOL_DATA* mol_data, inp_ATOM* at, int *num_atoms, int bDoNotAddH, int *err, char *pStrErr); -/* void WriteCoord( char *str, double x );*/ - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /* __MOL2ATOM_H__ */ diff --git a/INCHI-1-SRC/INCHI/main/readinch.c b/INCHI-1-SRC/INCHI/main/readinch.c deleted file mode 100644 index d1e9e1f..0000000 --- a/INCHI-1-SRC/INCHI/main/readinch.c +++ /dev/null @@ -1,272 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include -#include -#include - -#include "mode.h" - -#include "inpdef.h" -#include "util.h" -#include "readmol.h" - -#include "ichicomp.h" -#include "ichierr.h" -#include "extr_ct.h" - -#include "ichi_io.h" - -#define NO_ATOM (-1) /* non-existent (central) atom */ - -#define SB_PARITY_FLAG 0x38 /* disconnected structure has undef. parity */ -#define SB_PARITY_SHFT 3 -#define SB_PARITY_MASK 0x07 -#define SB_PARITY_1(X) (X & SB_PARITY_MASK) /* refers to connected structure */ -#define SB_PARITY_2(X) (((X) >> SB_PARITY_SHFT) & SB_PARITY_MASK) /* refers to connected structure */ - -/* 0D parity types */ -typedef enum tagINCHIStereoType0D { - INCHI_StereoType_None = 0, - INCHI_StereoType_DoubleBond = 1, - INCHI_StereoType_Tetrahedral = 2, - INCHI_StereoType_Allene = 3 -} inchi_StereoType0D; - -/* 0D parities */ -typedef enum tagINCHIStereoParity0D { - INCHI_PARITY_NONE = AB_PARITY_NONE, - INCHI_PARITY_ODD = AB_PARITY_ODD , /* 'o' */ - INCHI_PARITY_EVEN = AB_PARITY_EVEN, /* 'e' */ - INCHI_PARITY_UNKNOWN = AB_PARITY_UNKN, /* 'u' */ - INCHI_PARITY_UNDEFINED = AB_PARITY_UNDF /* '?' -- should not be used; however, see Note above */ -} inchi_StereoParity0D; - -/* bond type definitions */ -typedef enum tagINCHIBondType { - INCHI_BOND_TYPE_NONE = 0, - INCHI_BOND_TYPE_SINGLE = BOND_TYPE_SINGLE, - INCHI_BOND_TYPE_DOUBLE = BOND_TYPE_DOUBLE, - INCHI_BOND_TYPE_TRIPLE = BOND_TYPE_TRIPLE, - INCHI_BOND_TYPE_ALTERN = BOND_TYPE_ALTERN -} inchi_BondType; - -/* 2D stereo definitions */ -typedef enum tagINCHIBondStereo2D { - /* stereocenter-related; positive: the sharp end points to this atom */ - INCHI_BOND_STEREO_NONE = 0, - INCHI_BOND_STEREO_SINGLE_1UP = STEREO_SNGL_UP, - INCHI_BOND_STEREO_SINGLE_1EITHER = STEREO_SNGL_EITHER, - INCHI_BOND_STEREO_SINGLE_1DOWN = STEREO_SNGL_DOWN, - /* stereocenter-related; negative: the sharp end points to the opposite atom */ - INCHI_BOND_STEREO_SINGLE_2UP = -STEREO_SNGL_UP, - INCHI_BOND_STEREO_SINGLE_2EITHER = -STEREO_SNGL_EITHER, - INCHI_BOND_STEREO_SINGLE_2DOWN = -STEREO_SNGL_DOWN, - /* stereobond-related */ - INCHI_BOND_STEREO_DOUBLE_EITHER = STEREO_DBLE_EITHER -} inchi_BondStereo2D; - -/****************************************************************************/ -typedef struct tagINCHIStereo0D { - S_SHORT neighbor[4]; /* 4 atoms always */ - S_SHORT central_atom; /* central tetrahedral atom or a central */ - /* atom of allene; otherwise NO_ATOM */ - S_CHAR type; /* inchi_StereoType0D */ - S_CHAR parity; /* inchi_StereoParity0D: may be a combination of two parities: */ - /* ParityOfConnected | (ParityOfDisconnected << 3), see Note above */ -}inchi_Stereo0D; -/****************************************************************************/ - -/* This contains executable code. Included in lReadAux.c, e_ReadINCH.c, ReadINCH.c, */ -#include "aux2atom.h" - - -int INChIToOrigAtom( INCHI_IOSTREAM *inp_molfile, ORIG_ATOM_DATA *orig_at_data, int bMergeAllInputStructures, - int bGetOrigCoord, int bDoNotAddH, int vABParityUnknown, INPUT_TYPE nInputType, - char *pSdfLabel, char *pSdfValue, long *lSdfId, - INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ) -{ - /* inp_ATOM *at = NULL; */ - int num_dimensions_new; - int num_inp_bonds_new; - int num_inp_atoms_new; - inp_ATOM *at_new = NULL; - inp_ATOM *at_old = NULL; - int nNumAtoms = 0; - MOL_COORD *szCoordNew = NULL; - MOL_COORD *szCoordOld = NULL; - int i, j; - - if ( pStrErr ) { - pStrErr[0] = '\0'; - } - - /*FreeOrigAtData( orig_at_data );*/ - if ( lSdfId ) - *lSdfId = 0; - do { - - at_old = orig_at_data? orig_at_data->at : NULL; /* save pointer to the previous allocation */ - szCoordOld = orig_at_data? orig_at_data->szCoord : NULL; - num_inp_atoms_new = - INChIToInpAtom( inp_molfile, (bGetOrigCoord && orig_at_data)? &szCoordNew : NULL, - bDoNotAddH, vABParityUnknown, nInputType, orig_at_data? &at_new:NULL, - MAX_ATOMS, - &num_dimensions_new, &num_inp_bonds_new, - pSdfLabel, pSdfValue, lSdfId, pInpAtomFlags, err, pStrErr ); - if ( num_inp_atoms_new <= 0 && !*err ) { - MOLFILE_ERR_SET (*err, 0, "Empty structure"); - *err = 98; - } else - if ( orig_at_data && !num_inp_atoms_new && 10 < *err && *err < 20 && orig_at_data->num_inp_atoms > 0 && bMergeAllInputStructures ) { - *err = 0; /* end of file */ - break; - } else - if ( num_inp_atoms_new > 0 && orig_at_data ) { - /* merge pOrigDataTmp + orig_at_data => pOrigDataTmp; */ - nNumAtoms = num_inp_atoms_new + orig_at_data->num_inp_atoms; - if ( nNumAtoms >= MAX_ATOMS ) { - MOLFILE_ERR_SET (*err, 0, "Too many atoms"); - *err = 70; - orig_at_data->num_inp_atoms = -1; - } else - if ( !at_old ) { - /* the first structure */ - orig_at_data->at = at_new; - orig_at_data->szCoord = szCoordNew; - at_new = NULL; - szCoordNew = NULL; - orig_at_data->num_inp_atoms = num_inp_atoms_new; - orig_at_data->num_inp_bonds = num_inp_bonds_new; - orig_at_data->num_dimensions = num_dimensions_new; - } else - if ( (orig_at_data->at = ( inp_ATOM* ) inchi_calloc( nNumAtoms, sizeof(inp_ATOM) )) && - (!szCoordNew || (orig_at_data->szCoord = (MOL_COORD *) inchi_calloc( nNumAtoms, sizeof(MOL_COORD) ))) ) { - /* switch at_new <--> orig_at_data->at; */ - if ( orig_at_data->num_inp_atoms ) { - memcpy( orig_at_data->at, at_old, orig_at_data->num_inp_atoms * sizeof(orig_at_data->at[0]) ); - /* adjust numbering in the newly read structure */ - for ( i = 0; i < num_inp_atoms_new; i ++ ) { - for ( j = 0; j < at_new[i].valence; j ++ ) { - at_new[i].neighbor[j] += orig_at_data->num_inp_atoms; - } - at_new[i].orig_at_number += orig_at_data->num_inp_atoms; /* 12-19-2003 */ - } - if ( orig_at_data->szCoord && szCoordOld ) { - memcpy( orig_at_data->szCoord, szCoordOld, orig_at_data->num_inp_atoms * sizeof(MOL_COORD) ); - } - } - if ( at_old ) { - inchi_free( at_old ); - at_old = NULL; - } - if ( szCoordOld ) { - inchi_free( szCoordOld ); - szCoordOld = NULL; - } - /* copy newly read structure */ - memcpy( orig_at_data->at + orig_at_data->num_inp_atoms, - at_new, - num_inp_atoms_new * sizeof(orig_at_data->at[0]) ); - if ( orig_at_data->szCoord && szCoordNew ) { - memcpy( orig_at_data->szCoord + orig_at_data->num_inp_atoms, - szCoordNew, - num_inp_atoms_new * sizeof(MOL_COORD) ); - } - /* add other things */ - orig_at_data->num_inp_atoms += num_inp_atoms_new; - orig_at_data->num_inp_bonds += num_inp_bonds_new; - orig_at_data->num_dimensions = inchi_max(num_dimensions_new, orig_at_data->num_dimensions); - } else { - MOLFILE_ERR_SET (*err, 0, "Out of RAM"); - *err = -1; - } - } else - if ( num_inp_atoms_new > 0 ) { - nNumAtoms += num_inp_atoms_new; - } - if ( at_new ) { - inchi_free( at_new ); - at_new = NULL; - } - - } while ( !*err && bMergeAllInputStructures ); - /* - if ( !*err ) { - orig_at_data->num_components = - MarkDisconnectedComponents( orig_at_data ); - if ( orig_at_data->num_components == 0 ) { - MOLFILE_ERR_SET (*err, 0, "No components found"); - *err = 99; - } - if ( orig_at_data->num_components < 0 ) { - MOLFILE_ERR_SET (*err, 0, "Too many components"); - *err = 99; - } - } - */ - if ( szCoordNew ) { - inchi_free( szCoordNew ); - } - if ( at_new ) { - inchi_free( at_new ); - } - if ( !*err && orig_at_data ) { /* added testing (orig_at_data != NULL) */ - if ( ReconcileAllCmlBondParities( orig_at_data->at, orig_at_data->num_inp_atoms, 0 ) ) { - MOLFILE_ERR_SET (*err, 0, "Cannot reconcile stereobond parities"); /* */ - if (!orig_at_data->num_dimensions) { - *err = 1; - } - } - } - if ( *err ) { - FreeOrigAtData( orig_at_data ); - } - if ( *err && !(10 < *err && *err < 20) && pStrErr && !pStrErr[0] ) { - MOLFILE_ERR_SET (*err, 0, "Unknown error"); /* */ - } - return orig_at_data? orig_at_data->num_inp_atoms : nNumAtoms; -} - - diff --git a/INCHI-1-SRC/INCHI/main/readmol.c b/INCHI-1-SRC/INCHI/main/readmol.c deleted file mode 100644 index 40c24d6..0000000 --- a/INCHI-1-SRC/INCHI/main/readmol.c +++ /dev/null @@ -1,59 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include -#include -#include - -#include "mode.h" - -#include "incomdef.h" /*^^^ comdef.h renamed to incomdef.h (avoid collision with MS comdef.h) */ -#include "util.h" -#include "readmol.h" -#include "ichi_io.h" -#include "ichicomp.h" -#include "inpdef.h" - - -#include "lreadmol.h" diff --git a/INCHI-1-SRC/INCHI/main/readmol.h b/INCHI-1-SRC/INCHI/main/readmol.h deleted file mode 100644 index c6ca938..0000000 --- a/INCHI-1-SRC/INCHI/main/readmol.h +++ /dev/null @@ -1,209 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __READMOL_H__ -#define __READMOL_H__ - -/*************** read MOL file V2000.************************/ -/* ref: A.Dalby et al, "Description of Several Chemical Structure - * File Formats Used by Computer Programs Developed at Molecular - * Design Limited", J. Chem. Inf. Comput. Sci., 1992, 32, 244-255. - */ - -/* -#define MOLFILEINPLINELEN 84 // add cr, lf, double zero termination -#ifndef MOLFILEMAXLINELEN -#define MOLFILEMAXLINELEN 80 -#endif -*/ - -#define MOLFILEINPLINELEN 204 /* add cr, lf, double zero termination */ -#ifndef MOLFILEMAXLINELEN -#define MOLFILEMAXLINELEN 200 -#endif - - -#define MOL_PRESENT 1 -#define MOL_ABSENT 0 - -/* configuration */ -#define MOL_QUERY MOL_ABSENT -#define MOL_CPSS MOL_ABSENT -#define MOL_REACT MOL_ABSENT - -#define MOL_STRING_DATA 'S' -#define MOL_CHAR_INT_DATA 'C' -#define MOL_SHORT_INT_DATA 'N' -#define MOL_LONG_INT_DATA 'L' -#define MOL_DOUBLE_DATA 'D' -#define MOL_FLOAT_DATA 'F' -#define MOL_JUMP_TO_RIGHT 'J' -#define MOL_MAX_VALUE_LEN 32 /* max length of string containing a numerical value */ - - -#define SDF_END_OF_DATA "$$$$" - -/****************************************************************************/ -typedef struct tagMOL_HEADER_BLOCK { - /* Line #1 */ - char szMoleculeName[MOLFILEMAXLINELEN+1]; /* up to 80 characters */ - /* Line #2: optional */ - char szMoleculeLine2[MOLFILEMAXLINELEN+1]; /* the whole line2 -- up to 80 characters */ - char szUserInitials[3]; /* 2 bytes; char */ - char szProgramName[9]; /* 8 bytes; char */ - char cMonth; /* 2 bytes; integral */ - char cDay; /* 2 bytes; integral */ - char cYear; /* 2 bytes; integral */ - char cHour; /* 2 bytes; integral */ - char cMinute; /* 2 bytes; integral */ - char szDimCode[3]; /* 2 bytes: dimensional code; char */ - short nScalingFactor1; /* 2 bytes; I2 */ - double dScalingFactor2; /* 10 bytes, F10.5 */ - double dEnergy; /* 10 bytes, F10.5 */ - long lInternalRegistryNumber;/* 6 bytes, integral */ - /* Line #3: comment */ - char szComment[81]; -}MOL_HEADER_BLOCK; - -/****************************************************************************/ -typedef struct tagMOL_ATOM { - double fX; /* F10.5; Generic */ - double fY; /* F10.5; Generic */ - double fZ; /* F10.5; Generic */ - char szAtomSymbol[6]; /* aaa; Generic */ /* changed from 4 to 6 to match STDATA */ - S_CHAR cMassDifference; /* dd; (M_ISO) Generic: -3..+4 otherwise 0 or 127=most abund. isotope */ - S_CHAR cCharge; /* ccc; (M CHG), Generic: 1=+3,2=+2,3=+1,4=doublet,5=-1,6=-2,7=-3 */ - char cRadical; /* (M RAD) */ - char cStereoParity; /* sss; Generic */ -#if ( MOL_QUERY == MOL_PRESENT ) - char cH_countPlus1; /* hhh; Query; Hn means >= n H; H0 means no H */ - char cStereoCare; /* bbb; Query: 0=ignore; 1=must match */ -#endif - char cValence; /* vvv: 0=no marking; (1..14)=(1..14); 15=zero valence */ - /* number of bonds including bonds to implies H's */ -#if ( MOL_CPSS == MOL_PRESENT ) - char cH0_designator; /* HHH: CPSS */ - char cReactionComponentType; /* rrr: CPSS: 1=reactant, 2=product, 3=intermediate */ - char cReactionComponentNumber; /* iii: CPSS: 0 to (n-1) */ -#endif - -#if ( MOL_REACT == MOL_PRESENT ) - short nAtomAtomMappingNumber; /* mmm: Reaction: 1..255 */ - char cInversionRetentionFlag; /* nnn: 1=inverted,2=retained config.; 0=property not applied */ -#endif -#if ( MOL_REACT == MOL_PRESENT || MOL_QUERY == MOL_PRESENT ) - char cExactChargeFlag; /* eee: 1=charge on atom must match exactly, 0=property not applied */ -#endif - char cMyNumImpH; /* number of implicit H calculated for adding H to strings in STDATA */ - char cDisplayAtom; /* Do not hide element's name ( applies to C 7-25-98 DCh */ - char cAtomAliasedFlag; /* Do not remove charge/radical/isotope if it is in the alias. 9-3-99 DCh */ -} MOL_ATOM; - -/****************************************************************************/ -typedef struct tagMOL_BONDS { - short nAtomNo1; /* 111: First atom number: Generic */ - short nAtomNo2; /* 222: Second atom number: Generic */ - char cBondType; /* ttt: 1,2,3=single, double, triple; 4=aromatic; 5=single or double */ - /* 6=single or aromatic, 7=double or aromatic, 8=any. */ - /* values 4-8 are for SSS queries only */ - char cBondStereo; /* sss: Single bonds: 0=not stereo, 1=up, 4=either, 6=down */ - /* Double bonds: 0=use x,y,z to determine cis/trans */ - /* 3=cis or trans (either) */ - /* xxx: not used */ -#if ( MOL_QUERY == MOL_PRESENT ) - char cBondTopology; /* rrr: 0=either, 1=ring, 2=chain: SSS queries only */ -#endif -#if ( MOL_REACT == MOL_PRESENT ) - char cReactingCenterStatus; /* ccc: 0=unmarked, 1=a center, -1=not a center; Additional: */ - /* 2=no charge,4=bond made/broken,8=bond order changes */ - /* 12=4+8; 5=4+1, 9=8+1, 13=12+1 are also possible */ -#endif -} MOL_BONDS; -/****************************************************************************/ -typedef struct tagMOL_CTAB { - /* Line #1: Counts line */ - short nNumberOfAtoms; /* aaa; <= 255; Generic */ - short nNumberOfBonds; /* bbb; <= 255; Generic */ -#if ( MOL_QUERY == MOL_PRESENT ) - short nNumberOfAtomsLists; /* lll; <= 30; Query */ -#endif - /* fff; Obsolete */ - char cChiralFlag; /* ccc; 0 or 1; Generic */ - short nNumberOfStextEntries; /* sss; CPSS */ -#if ( MOL_CPSS == MOL_PRESENT ) - short nNumberOfReactionComponentsPlus1; /* xxx; CPSS */ - short nNumberOfReactants; /* rrr; CPSS */ - short nNumberOfProducts; /* ppp; CPSS */ - short nNumberOfIntermediates; /* iii; CPSS */ -#endif - short nNumberOfPropertyLines; /* mmm; Generic */ - char csCurrentCtabVersion[7]; /* vvvvvv; Generic; 'V2000' */ - /* The Atom Block */ - MOL_ATOM *MolAtom; - MOL_BONDS *MolBond; - MOL_COORD *szCoord; -} MOL_CTAB; - -typedef struct tagMOL_DATA { - MOL_HEADER_BLOCK hdr; - MOL_CTAB ctab; -} MOL_DATA; - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - -MOL_DATA* delete_mol_data( MOL_DATA* mol_data ); -MOL_DATA* read_sdfile_segment(FILE* inp, MOL_HEADER_BLOCK *OnlyHeaderBlock, MOL_CTAB *OnlyCtab, - int bGetOrigCoord, - char *pname, int lname, - long *Id, const char *pSdfLabel, char *pSdfValue, - int *err, char *pStrErr ); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -#endif /*__READMOL_H__*/ diff --git a/INCHI-1-SRC/INCHI/readme.txt b/INCHI-1-SRC/INCHI/readme.txt deleted file mode 100644 index 13bff48..0000000 --- a/INCHI-1-SRC/INCHI/readme.txt +++ /dev/null @@ -1,79 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - - -This directory contains inchi-1 source code and executables -(command-line executables produce standard and non-standard InChI/InChIKey). - - -========= - FILES -========= - -readme.txt This file - -common SUB-DIRECTORY - Part of the source code common with InChI library - -main SUB-DIRECTORY - The rest of the source code - -gcc SUB-DIRECTORY - inchi-1 SUB-DIRECTORY - Contains gcc makefile to create inchi-1 executable - -vc9 SUB-DIRECTORY - inchi-1 SUB-DIRECTORY - Contains Microsoft Visual Studio 2008 project - to create inchi-1.exe - - -The inchi-1.exe created with Microsoft Visual Studio 2008 is in INCHI-1-BIN -section of this distribution. - - - -========= - LINKS -========= - -IUPAC http://www.iupac.org/inchi -InChI Trust http://www.inchi-trust.org -InChI discussion group https://lists.sourceforge.net/lists/listinfo/inchi-discuss diff --git a/INCHI-1-SRC/INCHI/vc9/inchi-1/inchi-1.aps b/INCHI-1-SRC/INCHI/vc9/inchi-1/inchi-1.aps deleted file mode 100644 index 5b0681c..0000000 Binary files a/INCHI-1-SRC/INCHI/vc9/inchi-1/inchi-1.aps and /dev/null differ diff --git a/INCHI-1-SRC/INCHI_API/bin/readme.txt b/INCHI-1-SRC/INCHI_API/bin/readme.txt new file mode 100644 index 0000000..c2c9e1b --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/bin/readme.txt @@ -0,0 +1,41 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +This directory presents (initially empty) layout of +sub-directories where the created binaries are stored. diff --git a/INCHI-1-SRC/INCHI_API/demos/inchi_main/gcc/libinchi.map b/INCHI-1-SRC/INCHI_API/demos/inchi_main/gcc/libinchi.map new file mode 100644 index 0000000..323dc85 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/gcc/libinchi.map @@ -0,0 +1,4 @@ +{ +global: CheckINCHI; CheckINCHIKey; FreeINCHI; FreeStdINCHI; FreeStructFromINCHI; FreeStructFromStdINCHI; Free_inchi_Input; Free_std_inchi_Input; FreeStructFromINCHIEx; GetINCHI; GetINCHIKeyFromINCHI; GetINCHIfromINCHI; GetStdINCHI; GetStdINCHIKeyFromStdINCHI; GetStringLength; GetStructFromINCHI; GetStructFromStdINCHI; Get_inchi_Input_FromAuxInfo; Get_std_inchi_Input_FromAuxInfo; GetINCHIEx; GetStructFromINCHIEx; INCHIGEN_Create; INCHIGEN_Destroy; INCHIGEN_DoCanonicalization; INCHIGEN_DoNormalization; INCHIGEN_DoSerialization; INCHIGEN_Reset; INCHIGEN_Setup; STDINCHIGEN_Create; STDINCHIGEN_Destroy; STDINCHIGEN_DoCanonicalization; STDINCHIGEN_DoNormalization; STDINCHIGEN_DoSerialization; STDINCHIGEN_Reset; STDINCHIGEN_Setup; MakeINCHIFromMolfileText; IXA_STATUS_Create; IXA_STATUS_Clear; IXA_STATUS_Destroy; IXA_STATUS_HasError; IXA_STATUS_HasWarning; IXA_STATUS_GetCount; IXA_STATUS_GetSeverity; IXA_STATUS_GetMessage; IXA_MOL_Create; IXA_MOL_Clear; IXA_MOL_Destroy; IXA_MOL_ReadMolfile; IXA_MOL_ReadInChI; IXA_MOL_SetChiral; IXA_MOL_GetChiral; IXA_MOL_CreateAtom; IXA_MOL_SetAtomElement; IXA_MOL_SetAtomAtomicNumber; IXA_MOL_SetAtomMass; IXA_MOL_SetAtomCharge; IXA_MOL_SetAtomRadical; IXA_MOL_SetAtomHydrogens; IXA_MOL_SetAtomX; IXA_MOL_SetAtomY; IXA_MOL_SetAtomZ; IXA_MOL_CreateBond; IXA_MOL_SetBondType; IXA_MOL_SetBondWedge; IXA_MOL_SetDblBondConfig; IXA_MOL_CreateStereoTetrahedron; IXA_MOL_CreateStereoRectangle; IXA_MOL_CreateStereoAntiRectangle; IXA_MOL_SetStereoParity; IXA_MOL_GetNumAtoms; IXA_MOL_GetNumBonds; IXA_MOL_GetAtomId; IXA_MOL_GetBondId; IXA_MOL_GetAtomIndex; IXA_MOL_GetBondIndex; IXA_MOL_GetAtomNumBonds; IXA_MOL_GetAtomBond; IXA_MOL_GetCommonBond; IXA_MOL_GetBondAtom1; IXA_MOL_GetBondAtom2; IXA_MOL_GetAtomElement; IXA_MOL_GetAtomAtomicNumber; IXA_MOL_GetAtomMass; IXA_MOL_GetAtomCharge; IXA_MOL_GetAtomRadical; IXA_MOL_GetAtomHydrogens; IXA_MOL_GetAtomX; IXA_MOL_GetAtomY; IXA_MOL_GetAtomZ; IXA_MOL_GetBondType; IXA_MOL_GetBondWedge; IXA_MOL_GetDblBondConfig; IXA_MOL_GetNumStereos; IXA_MOL_GetStereoId; IXA_MOL_GetStereoIndex; IXA_MOL_GetStereoTopology; IXA_MOL_GetStereoCentralAtom; IXA_MOL_GetStereoCentralBond; IXA_MOL_GetStereoNumVertices; IXA_MOL_GetStereoVertex; IXA_MOL_GetStereoParity; IXA_INCHIBUILDER_Create; IXA_INCHIBUILDER_SetMolecule; IXA_INCHIBUILDER_GetInChI; IXA_INCHIBUILDER_GetInChIEx; IXA_INCHIBUILDER_GetAuxInfo; IXA_INCHIBUILDER_GetLog; IXA_INCHIBUILDER_Destroy; IXA_INCHIBUILDER_SetOption; IXA_INCHIBUILDER_SetOption_Stereo; IXA_INCHIBUILDER_SetOption_Timeout; IXA_INCHIKEYBUILDER_Create; IXA_INCHIKEYBUILDER_SetInChI; IXA_INCHIKEYBUILDER_GetInChIKey; IXA_INCHIKEYBUILDER_Destroy;local: *; +}; + diff --git a/INCHI-1-SRC/INCHI_API/demos/inchi_main/gcc/makefile b/INCHI-1-SRC/INCHI_API/demos/inchi_main/gcc/makefile new file mode 100644 index 0000000..8ea91ca --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/gcc/makefile @@ -0,0 +1,196 @@ +# Comment out the next line to create so/dll only +CREATE_MAIN = 1 +# Comment out the next line to create mol2inchi executable (otherwise, inchi_maiin is created) +#CALLER_IS_MOL2INCHI = 1 +# or define ISLINUX in command line: make ISLINUX=1 +ISLINUX = 1 +# Linux fpic option: replace -fPIC with -fpic if the latter works +# Comment out "LINUX_Z_RELRO =" if -z relro is not supported +# These options are needed to avoid the following SELinux message: +# "Error: cannot restore segment prot after reloc: Permission denied" +# In addition, inchi.map restricts set of expoorted from .so +# functions to those which belong to InChI API +LINUX_MAP = ,--version-script=libinchi.map +ifdef ISLINUX +LINUX_FPIC = -fPIC +LINUX_Z_RELRO = ,-z,relro +endif +# === version === +MAIN_VERSION = .1 +VERSION = $(MAIN_VERSION).05.00 +# === executable & library directory === +ifndef LIB_DIR + LIB_DIR = ../../../bin/Linux +endif +# === InChI Library name === +ifndef INCHI_LIB_NAME + INCHI_LIB_NAME = libinchi +endif +INCHI_LIB_PATHNAME = $(LIB_DIR)/$(INCHI_LIB_NAME) +# === Main program name ==== +ifndef API_CALLER_NAME + ifndef CALLER_IS_MOL2INCHI + API_CALLER_NAME = inchi_main$ + else + API_CALLER_NAME = mol2inchi$ + endif +endif +API_CALLER_PATHNAME = $(LIB_DIR)/$(API_CALLER_NAME) +# === Linker to create (Shared) InChI library ==== +ifndef SHARED_LINK + SHARED_LINK = gcc -shared +endif +# === Linker to create Main program ===== +ifndef LINKER + ifdef ISLINUX + LINKER_CWD_PATH = -Wl,-R,"" + endif + LINKER = gcc -s $(LINKER_CWD_PATH) +endif +ifndef P_LIBR + P_LIBR = ../../../libinchi/src/ +endif +ifndef P_LIBR_IXA + P_LIBR_IXA = ../../../libinchi/src/ixa/ +endif +ifndef P_BASE + P_BASE = ../../../../INCHI_BASE/src/ +endif +ifndef P_MAIN + ifndef CALLER_IS_MOL2INCHI + P_MAIN = ../src/ + else + P_MAIN = ../src/ + endif +endif +# === C Compiler =============== +ifndef C_COMPILER + C_COMPILER = gcc +endif +# === C Compiler Options ======= +ifndef C_OPTIONS + ifndef CALLER_IS_MOL2INCHI + C_OPTIONS = -ansi -O3 -c + else + C_OPTIONS = -O3 -c + endif + ifdef ISLINUX + ifndef C_SO_OPTIONS + C_SO_OPTIONS = $(LINUX_FPIC) -DTARGET_API_LIB -DCOMPILE_ANSI_ONLY + endif + endif + ifndef C_MAIN_OPTIONS + C_MAIN_OPTIONS = -DBUILD_LINK_AS_DLL -DTARGET_EXE_USING_API + endif +endif +ifdef CREATE_MAIN +ifndef CALLER_IS_MOL2INCHI +API_CALLER_SRCS = $(P_MAIN)e_0dstereo.c \ +$(P_MAIN)e_ichimain.c \ +$(P_MAIN)e_ichi_io.c \ +$(P_MAIN)e_ichi_parms.c \ +$(P_MAIN)e_inchi_atom.c \ +$(P_MAIN)e_mol2atom.c \ +$(P_MAIN)e_readinch.c \ +$(P_MAIN)e_readmol.c \ +$(P_MAIN)e_readstru.c \ +$(P_MAIN)e_util.c \ +$(P_MAIN)e_ichimain_a.c +API_CALLER_OBJS = e_0dstereo.o \ +e_ichimain.o \ +e_ichi_io.o \ +e_ichi_parms.o \ +e_inchi_atom.o \ +e_mol2atom.o \ +e_readinch.o \ +e_readmol.o \ +e_readstru.o \ +e_util.o \ +e_ichimain_a.o +else +API_CALLER_SRCS = $(P_MAIN)mol2inchi.c \ +$(P_MAIN)getcputime.c \ +$(P_MAIN)moreutil.c +API_CALLER_OBJS = mol2inchi.o \ +getcputime.o \ +moreutil.o +endif +# === InChI Main Link rule ================ +$(API_CALLER_PATHNAME) : $(API_CALLER_OBJS) $(INCHI_LIB_PATHNAME).so$(VERSION) + $(LINKER) -o $(API_CALLER_PATHNAME) $(API_CALLER_OBJS) \ + $(INCHI_LIB_PATHNAME).so$(VERSION) -lm +# === InChI Main compile rule ============ +%.o: $(P_MAIN)%.c + $(C_COMPILER) $(C_MAIN_OPTIONS) $(C_OPTIONS) $< +endif +# === InChI Library Object files ============ +INCHI_LIB_OBJS = ichican2.o \ +ichicano.o \ +ichi_io.o \ +ichierr.o \ +ichicans.o \ +ichiisot.o \ +ichilnct.o \ +ichimak2.o \ +ichimake.o \ +ichimap1.o \ +ichimap2.o \ +ichimap4.o \ +ichinorm.o \ +ichiparm.o \ +ichiprt1.o \ +ichiprt2.o \ +ichiprt3.o \ +ichiqueu.o \ +ichiring.o \ +ichisort.o \ +ichister.o \ +ichitaut.o \ +ichi_bns.o \ +inchi_dll.o \ +ichiread.o \ +ichirvr1.o \ +ichirvr2.o \ +ichirvr3.o \ +ichirvr4.o \ +ichirvr5.o \ +ichirvr6.o \ +ichirvr7.o \ +ikey_dll.o \ +ikey_base26.o \ +inchi_dll_main.o \ +inchi_dll_a.o \ +inchi_dll_a2.o \ +inchi_dll_b.o \ +ixa_inchikey_builder.o \ +ixa_read_mol.o \ +ixa_status.o \ +ixa_builder.o \ +ixa_mol.o \ +ixa_read_inchi.o \ +mol_fmt1.o \ +mol_fmt2.o \ +mol_fmt3.o \ +mol2atom.o \ +mol_fmt4.o \ +readinch.o \ +runichi.o \ +runichi2.o \ +runichi3.o \ +runichi4.o \ +sha2.o \ +strutil.o \ +util.o +# === InChI Library link rule ========= +$(INCHI_LIB_PATHNAME).so$(VERSION): $(INCHI_LIB_OBJS) + $(SHARED_LINK) $(SHARED_LINK_PARM) -o $(INCHI_LIB_PATHNAME).so$(VERSION) \ +$(INCHI_LIB_OBJS) -Wl$(LINUX_MAP)$(LINUX_Z_RELRO),-soname,$(INCHI_LIB_NAME).so$(MAIN_VERSION) -lm + ln -fs $(INCHI_LIB_NAME).so$(VERSION) \ +$(INCHI_LIB_PATHNAME).so$(MAIN_VERSION) +# === InChI Library compile rule ========= +%.o: $(P_LIBR)%.c + $(C_COMPILER) $(C_SO_OPTIONS) $(C_OPTIONS) $< +%.o: $(P_LIBR_IXA)%.c + $(C_COMPILER) $(C_SO_OPTIONS) $(C_OPTIONS) $< +%.o: $(P_BASE)%.c + $(C_COMPILER) $(C_SO_OPTIONS) $(C_OPTIONS) $< diff --git a/INCHI-1-SRC/INCHI_API/demos/inchi_main/gcc/makefile32 b/INCHI-1-SRC/INCHI_API/demos/inchi_main/gcc/makefile32 new file mode 100644 index 0000000..5bd5cd3 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/gcc/makefile32 @@ -0,0 +1,199 @@ +# This will create 32-bit executable under 64-bit Linux +# +# +# Comment out the next line to create so/dll only +CREATE_MAIN = 1 +# Comment out the next line to create mol2inchi executable (otherwise, inchi_maiin is created) +#CALLER_IS_MOL2INCHI = 1 +# or define ISLINUX in command line: make ISLINUX=1 +ISLINUX = 1 +# Linux fpic option: replace -fPIC with -fpic if the latter works +# Comment out "LINUX_Z_RELRO =" if -z relro is not supported +# These options are needed to avoid the following SELinux message: +# "Error: cannot restore segment prot after reloc: Permission denied" +# In addition, inchi.map restricts set of expoorted from .so +# functions to those which belong to InChI API +LINUX_MAP = ,--version-script=libinchi.map +ifdef ISLINUX +LINUX_FPIC = -fPIC +LINUX_Z_RELRO = ,-z,relro +endif +# === version === +MAIN_VERSION = .1 +VERSION = $(MAIN_VERSION).05.00 +# === executable & library directory === +ifndef LIB_DIR + LIB_DIR = ../../../bin/Linux/32bit +endif +# === InChI Library name === +ifndef INCHI_LIB_NAME + INCHI_LIB_NAME = libinchi +endif +INCHI_LIB_PATHNAME = $(LIB_DIR)/$(INCHI_LIB_NAME) +# === Main program name ==== +ifndef API_CALLER_NAME + ifndef CALLER_IS_MOL2INCHI + API_CALLER_NAME = inchi_main$ + else + API_CALLER_NAME = mol2inchi$ + endif +endif +API_CALLER_PATHNAME = $(LIB_DIR)/$(API_CALLER_NAME) +# === Linker to create (Shared) InChI library ==== +ifndef SHARED_LINK + SHARED_LINK = gcc -shared +endif +# === Linker to create Main program ===== +ifndef LINKER + ifdef ISLINUX + LINKER_CWD_PATH = -Wl,-R,"" + endif + LINKER = gcc -s $(LINKER_CWD_PATH) +endif +ifndef P_LIBR + P_LIBR = ../../../libinchi/src/ +endif +ifndef P_LIBR_IXA + P_LIBR_IXA = ../../../libinchi/src/ixa/ +endif +ifndef P_BASE + P_BASE = ../../../../INCHI_BASE/src/ +endif +ifndef P_MAIN + ifndef CALLER_IS_MOL2INCHI + P_MAIN = ../src/ + else + P_MAIN = ../src/ + endif +endif +# === C Compiler =============== +ifndef C_COMPILER + C_COMPILER = gcc +endif +# === C Compiler Options ======= +ifndef C_OPTIONS + ifndef CALLER_IS_MOL2INCHI + C_OPTIONS = -m32 -ansi -O3 -c + else + C_OPTIONS = -m32 -O3 -c + endif + ifdef ISLINUX + ifndef C_SO_OPTIONS + C_SO_OPTIONS = $(LINUX_FPIC) -DTARGET_API_LIB -DCOMPILE_ANSI_ONLY + endif + endif + ifndef C_MAIN_OPTIONS + C_MAIN_OPTIONS = -DBUILD_LINK_AS_DLL -DTARGET_EXE_USING_API + endif +endif +ifdef CREATE_MAIN +ifndef CALLER_IS_MOL2INCHI +API_CALLER_SRCS = $(P_MAIN)e_0dstereo.c \ +$(P_MAIN)e_ichimain.c \ +$(P_MAIN)e_ichi_io.c \ +$(P_MAIN)e_ichi_parms.c \ +$(P_MAIN)e_inchi_atom.c \ +$(P_MAIN)e_mol2atom.c \ +$(P_MAIN)e_readinch.c \ +$(P_MAIN)e_readmol.c \ +$(P_MAIN)e_readstru.c \ +$(P_MAIN)e_util.c \ +$(P_MAIN)e_ichimain_a.c +API_CALLER_OBJS = e_0dstereo.o \ +e_ichimain.o \ +e_ichi_io.o \ +e_ichi_parms.o \ +e_inchi_atom.o \ +e_mol2atom.o \ +e_readinch.o \ +e_readmol.o \ +e_readstru.o \ +e_util.o \ +e_ichimain_a.o +else +API_CALLER_SRCS = $(P_MAIN)mol2inchi.c \ +$(P_MAIN)getcputime.c \ +$(P_MAIN)moreutil.c +API_CALLER_OBJS = mol2inchi.o \ +getcputime.o \ +moreutil.o +endif +# === InChI Main Link rule ================ +$(API_CALLER_PATHNAME) : $(API_CALLER_OBJS) $(INCHI_LIB_PATHNAME).so$(VERSION) + $(LINKER) -m32 -o $(API_CALLER_PATHNAME) $(API_CALLER_OBJS) \ + $(INCHI_LIB_PATHNAME).so$(VERSION) -lm +# === InChI Main compile rule ============ +%.o: $(P_MAIN)%.c + $(C_COMPILER) $(C_MAIN_OPTIONS) $(C_OPTIONS) $< +endif +# === InChI Library Object files ============ +INCHI_LIB_OBJS = ichican2.o \ +ichicano.o \ +ichi_io.o \ +ichierr.o \ +ichicans.o \ +ichiisot.o \ +ichilnct.o \ +ichimak2.o \ +ichimake.o \ +ichimap1.o \ +ichimap2.o \ +ichimap4.o \ +ichinorm.o \ +ichiparm.o \ +ichiprt1.o \ +ichiprt2.o \ +ichiprt3.o \ +ichiqueu.o \ +ichiring.o \ +ichisort.o \ +ichister.o \ +ichitaut.o \ +ichi_bns.o \ +inchi_dll.o \ +ichiread.o \ +ichirvr1.o \ +ichirvr2.o \ +ichirvr3.o \ +ichirvr4.o \ +ichirvr5.o \ +ichirvr6.o \ +ichirvr7.o \ +ikey_dll.o \ +ikey_base26.o \ +inchi_dll_main.o \ +inchi_dll_a.o \ +inchi_dll_a2.o \ +inchi_dll_b.o \ +ixa_inchikey_builder.o \ +ixa_read_mol.o \ +ixa_status.o \ +ixa_builder.o \ +ixa_mol.o \ +ixa_read_inchi.o \ +mol_fmt1.o \ +mol_fmt2.o \ +mol_fmt3.o \ +mol2atom.o \ +mol_fmt4.o \ +readinch.o \ +runichi.o \ +runichi2.o \ +runichi3.o \ +runichi4.o \ +sha2.o \ +strutil.o \ +util.o +# === InChI Library link rule ========= +$(INCHI_LIB_PATHNAME).so$(VERSION): $(INCHI_LIB_OBJS) + $(SHARED_LINK) $(SHARED_LINK_PARM) -m32 -o $(INCHI_LIB_PATHNAME).so$(VERSION) \ +$(INCHI_LIB_OBJS) -Wl$(LINUX_MAP)$(LINUX_Z_RELRO),-soname,$(INCHI_LIB_NAME).so$(MAIN_VERSION) -lm + ln -fs $(INCHI_LIB_NAME).so$(VERSION) \ +$(INCHI_LIB_PATHNAME).so$(MAIN_VERSION) +# === InChI Library compile rule ========= +%.o: $(P_LIBR)%.c + $(C_COMPILER) $(C_SO_OPTIONS) $(C_OPTIONS) $< +%.o: $(P_LIBR_IXA)%.c + $(C_COMPILER) $(C_SO_OPTIONS) $(C_OPTIONS) $< +%.o: $(P_BASE)%.c + $(C_COMPILER) $(C_SO_OPTIONS) $(C_OPTIONS) $< diff --git a/INCHI-1-SRC/INCHI_API/gcc_so_makefile/run_make_on_linux.sh b/INCHI-1-SRC/INCHI_API/demos/inchi_main/gcc/run_make_on_linux.sh similarity index 92% rename from INCHI-1-SRC/INCHI_API/gcc_so_makefile/run_make_on_linux.sh rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/gcc/run_make_on_linux.sh index e8bde66..a6d09d8 100644 --- a/INCHI-1-SRC/INCHI_API/gcc_so_makefile/run_make_on_linux.sh +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/gcc/run_make_on_linux.sh @@ -1,2 +1,2 @@ -#!/bin/sh -make ISLINUX=1 +#!/bin/sh +make ISLINUX=1 diff --git a/INCHI-1-SRC/INCHI_API/demos/inchi_main/readme.txt b/INCHI-1-SRC/INCHI_API/demos/inchi_main/readme.txt new file mode 100644 index 0000000..baa800a --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/readme.txt @@ -0,0 +1,49 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +This directory contains example of using "classic" InChI API. + +The source codes are placed in sub-directory 'src'; +gcc/Linux makefiles in sub-directory 'gcc'; +MS VS 2008 project is placed in sub-directory 'vc9'. + +The created binaries are saved in upper-level directory 'bin'. + +The demo program inchi_main are practically the same as its +predecessor in previous InChI Software version, 1.04. diff --git a/INCHI-1-SRC/INCHI/common/aux2atom.h b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/aux2atom.h similarity index 100% rename from INCHI-1-SRC/INCHI/common/aux2atom.h rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/src/aux2atom.h diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/e_0dstereo.c b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_0dstereo.c similarity index 94% rename from INCHI-1-SRC/INCHI_API/inchi_main/e_0dstereo.c rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_0dstereo.c index d88b4f9..3e532a8 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_main/e_0dstereo.c +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_0dstereo.c @@ -1,3130 +1,3118 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -/* this file is used only in case of #define CREATE_0D_PARITIES */ -/* hardcoded bFixSp3Bug = 1 fixes sp3 bugs in original InChI v. 1.00 */ -/* Phosphine and Arsine sp3 stereo are not supported in this mode */ - -#include -#include -#include -#include -#include - - -#include "e_mode.h" -#include "e_ctl_data.h" -#include "inchi_api.h" - - -#include "e_inchi_atom.h" -#include "e_ichisize.h" -#include "e_comdef.h" -#include "e_ichicomp.h" -#include "e_util.h" -#include "e_0dstereo.h" - - - -#define ALWAYS_SET_STEREO_PARITY 0 - -#define SB_PARITY_FLAG 0x38 /* disconnected structure has undef. parity */ -#define SB_PARITY_SHFT 3 -#define SB_PARITY_MASK 0x07 -#define SB_PARITY_1(X) (X & SB_PARITY_MASK) /* refers to connected structure */ -#define SB_PARITY_2(X) (((X) >> SB_PARITY_SHFT) & SB_PARITY_MASK) /* refers to connected structure */ - -#define inchi_NUMH(AT,CUR_AT) (AT[CUR_AT].num_iso_H[0]+AT[CUR_AT].num_iso_H[1]+AT[CUR_AT].num_iso_H[2]+AT[CUR_AT].num_iso_H[3]) -#define inchi_NUMH2(AT,CUR_AT) ((AT[CUR_AT].num_iso_H[0]>0?AT[CUR_AT].num_iso_H[0]:0) +AT[CUR_AT].num_iso_H[1]+AT[CUR_AT].num_iso_H[2]+AT[CUR_AT].num_iso_H[3]) -#define IS_METAL(a) (a == AtType_Metal || a == AtType_Sn4 || a == AtType_Sn3 || a == AtType_Sn2) - -typedef struct tagStereo0D { - inchi_Stereo0D *stereo0D; /* array of num_stereo0D 0D stereo elements or NULL */ - int num_stereo0D; /* number of 0D stereo elements */ - int max_num_Stereo0D; /* allocated length of stereo0D */ - int delta_num_stereo0D;/* allocation increments */ - S_CHAR *cAtType; -} Stereo0D; - -typedef enum tagAtType { - /* possible stereocenter */ - AtType_C4 = 1, /* >C< */ - AtType_Si4 = 2, /* >Si< */ - AtType_Ge4 = 3, /* >Ge< */ - AtType_Sn4 = 4, /* >Sn< */ - AtType_B4m = 5, /* B(-) */ - AtType_S4 = 6, /* =S< */ - AtType_S6 = 7, /* >S<, 2 double bonds */ - AtType_S3p = 8, /* -S(+)< */ - AtType_S5p = 9, /* >S(+)<, 1 double bond */ - AtType_Se4 =10, /* =Se< */ - AtType_Se6 =11, /* >Se<, 2 double bonds */ - AtType_Se3p =12, /* -Se(+)< */ - AtType_Se5p =13, /* >Se(+)<, 1 double bond */ - AtType_N5 =14, /* >N<, 1 double bond */ - AtType_N4p =15, /* >N(+)< */ - AtType_N3r =16, /* -N<| (N in a 3-member ring) */ - AtType_P4p =17, /* >N(+)< */ - AtType_P5 =18, /* >P<, 1 double bond */ - AtType_As4p =19, /* >As(+)< */ - AtType_As5 =20, /* >As<, 1 double bond */ - /* possible stereobond */ - AtType_C3 =21, /* =C<, =C/ */ - AtType_Si3 =22, - AtType_Ge3 =23, - AtType_Sn3 =24, - AtType_N3p =25, /* =N(+)< */ - AtType_N3 =26, /* =N/ */ - /* middle allene/cumulene */ - AtType_C2 =27, /* =C= */ - AtType_Si2 =28, /* =Si= */ - AtType_Ge2 =29, /* =Ge= */ - AtType_Sn2 =30, /* =Ge= */ - /* may become stereobond after charge or radical shift */ - AtType_Nns =31, - /* metal */ - AtType_Metal =32, - /* terminal H */ - AtType_TermH =33, - AtType_TermD =34, - AtType_TermT =35 -} AT_TYPE; - -typedef enum tagElType { - ElType_C = 1, - ElType_Si = 2, - ElType_Ge = 3, - ElType_Sn = 4, - ElType_B = 5, - ElType_S = 6, - ElType_Se = 7, - ElType_N = 8, - ElType_P = 9, - ElType_As =10, - ElType_H =11, - ElType_D =12, - ElType_T =13 - -} EL_TYPE; - -inchi_Stereo0D *e_GetNewStereo( Stereo0D *pStereo ); - - -static int ee_extract_ChargeRadical( char *elname, int *pnRadical, int *pnCharge ); -static int ee_extract_H_atoms( char *elname, S_CHAR num_iso_H[] ); -static int e_GetElType( inchi_Atom *at, int cur_atom ); -static int e_bCanInpAtomBeAStereoCenter( int cur_at, S_CHAR *cAtType ); -static int e_nNumNonMetalNeigh( inchi_Atom *atom, int cur_at, Stereo0D *pStereo, int *i_ord_LastMetal ); - -void e_inchi_swap ( char *a, char *b, size_t width ); -int e_insertions_sort( void *base, size_t num, size_t width, int ( *compare )(const void *e1, const void *e2 ) ); -int e_bCanAtomHaveAStereoBond( inchi_Atom *at, int cur_at, S_CHAR *cAtType ); -int e_bCanAtomBeMiddleAllene( int cur_at, S_CHAR *cAtType ); -int e_bCanAtomBeTerminalAllene( int cur_at, S_CHAR *cAtType ); - -/**********************************************************************************/ -#define AMBIGUOUS_STEREO 1 -#define AMBIGUOUS_STEREO_ERROR 32 - - -#define AB_MAX_WELL_DEFINED_PARITY inchi_max(INCHI_PARITY_ODD, INCHI_PARITY_EVEN) /* 1, 2 => well defined parities, uncluding 'unknown' */ -#define AB_MIN_WELL_DEFINED_PARITY inchi_min(INCHI_PARITY_ODD, INCHI_PARITY_EVEN) /* min(INCHI_PARITY_ODD, INCHI_PARITY_EVEN) */ - -#define AMBIGUOUS_STEREO 1 - -#define MIN_DOT_PROD 50 /* min value of at->stereo_bond_z_prod[i] to define parity */ - -#define ATOM_PARITY_WELL_DEF(X) (AB_MIN_WELL_DEFINED_PARITY <= (X) && (X) <= AB_MAX_WELL_DEFINED_PARITY) -/**********************************************************************************/ - -#define CT_ERR_FIRST (-30000) -#define CT_OUT_OF_RAM (CT_ERR_FIRST- 2) /*(-30002) */ -#define CT_ISO_H_ERR (CT_ERR_FIRST- 9) /*(-30009) */ -#define CT_CALC_STEREO_ERR (CT_ERR_FIRST-15) /*(-30015) */ -#define CT_UNKNOWN_ERR (CT_ERR_FIRST-18) /*(-30018) */ - -#define CT_ERR_MIN CT_UNKNOWN_ERR -#define CT_ERR_MAX CT_ERR_FIRST - -#define RETURNED_ERROR(nVal) (CT_ERR_MIN<=(nVal) && (nVal)<=CT_ERR_MAX) -/**********************************************************************************/ -#define MAX_CUMULENE_LEN 2 /* max number of bonds in a cumulene chain - 1 */ -/**********************************************************************************/ -int ee_extract_ChargeRadical( char *elname, int *pnRadical, int *pnCharge ) -{ - char *q, *r, *p; - int nCharge=0, nRad = 0, charge_len = 0, k, nVal, nSign, nLastSign=1, len; - - p = elname; - - /* extract radicals & charges */ - while ( q = strpbrk( p, "+-^" ) ) { - switch ( *q ) { - case '+': - case '-': - for ( k = 0, nVal=0; (nSign = ('+' == q[k])) || (nSign = -('-' == q[k])); k++ ) { - nVal += (nLastSign = nSign); - charge_len ++; - } - if ( nSign = (int)strtol( q+k, &r, 10 ) ) { /* fixed 12-5-2001 */ - nVal += nLastSign * (nSign-1); - } - charge_len = r - q; - nCharge += nVal; - break; - /* case '.': */ /* singlet '.' may be confused with '.' in formulas like CaO.H2O */ - case '^': - nRad = 1; /* doublet here is 1. See below */ - charge_len = 1; - for ( k = 1; q[0] == q[k]; k++ ) { - nRad ++; - charge_len ++; - } - break; - } - memmove( q, q+charge_len, strlen(q+charge_len)+1 ); - } - len = strlen(p); - /* radical */ - if ( (q = strrchr( p, ':' )) && !q[1]) { - nRad = RADICAL_SINGLET; - q[0] = '\0'; - len --; - } else { - while( (q = strrchr( p, '.' )) && !q[1] ) { - nRad ++; - q[0] = '\0'; - len --; - } - - nRad = nRad == 1? RADICAL_DOUBLET : - nRad == 2? RADICAL_TRIPLET : 0; - } - *pnRadical = nRad; - *pnCharge = nCharge; - return ( nRad || nCharge ); - -} -/***********************************************************************/ -int ee_extract_H_atoms( char *elname, S_CHAR num_iso_H[] ) -{ - int i, len, c, k, val, bExtracted = 0; - char *q; - i = 0; - len = (int)strlen(elname); - c = UCINT elname[0]; - while ( i < len ) { - switch ( c ) { - case 'H': - k = 0; - break; - case 'D': - k = 2; - break; - case 'T': - k = 3; - break; - default: - k = -1; - break; - } - q = elname+i+1; /* pointer to the next to elname[i] character */ - c = UCINT q[0]; - if ( k >= 0 && !islower( c ) ) { - /* found a hydrogen */ - bExtracted ++; - if ( isdigit( c ) ) { - val = (int)strtol( q, &q, 10 ); - /* q = pointer to the next to number of hydrogen atom(s) character */ - } else { - val = 1; - } - num_iso_H[k] += val; - /* remove the hydrogen atom from the string */ - len -= (q-elname)-i; - memmove( elname+i, q, len + 1 ); - /* c = UCINT elname[i]; */ - } else { - i ++; - } - c = UCINT elname[i]; /* moved here 11-04-2002 */ - } - return bExtracted; -} -/************************************************/ -#define MAX_BOND_TYPE 4 -int e_GetElType( inchi_Atom *at, int cur_atom ) -{ - char szEl[ATOM_EL_LEN]; - int nRadical, nCharge, bChargeOrRad, bH, bAddH = 0, nElType=0, bond_valence, valence, num_H=0; - S_CHAR num_iso_H[NUM_H_ISOTOPES+1]; - S_CHAR num_bonds[MAX_BOND_TYPE]; - int i; - int nRadicalValence = 0; - - if ( sizeof(at->num_iso_H) != sizeof(num_iso_H) || - sizeof(at->num_iso_H[0]) != sizeof(num_iso_H[0]) ) { - /* program error */ - return -1; - } - strcpy( szEl, at[cur_atom].elname); - memset( num_iso_H, 0, sizeof(num_iso_H) ); - bChargeOrRad = ee_extract_ChargeRadical( szEl, &nRadical, &nCharge ); - bH = ee_extract_H_atoms( szEl, num_iso_H ); - if ( !bChargeOrRad ) { - nRadical = at[cur_atom].radical; - nCharge = at[cur_atom].charge; - } - if ( !bH ) { - memcpy( num_iso_H, at[cur_atom].num_iso_H, sizeof(num_iso_H) ); - if ( bAddH = (num_iso_H[0] < 0 ) ) { - num_iso_H[0] = 0; - } - } - num_H = num_iso_H[0]+num_iso_H[1]+num_iso_H[2]+num_iso_H[3]; - - if (nRadical==INCHI_RADICAL_DOUBLET) { - nRadicalValence = 1; - } else - if ( nRadical==INCHI_RADICAL_TRIPLET || nRadical==INCHI_RADICAL_SINGLET ){ - nRadicalValence = 2; - } - - /* element type */ - if ( !strcmp( szEl, "C" ) ) { - nElType = ElType_C; - } else - if ( !strcmp( szEl, "Si" ) ) { - nElType = ElType_Si; - } else - if ( !strcmp( szEl, "Ge" ) ) { - nElType = ElType_Ge; - } else - if ( !strcmp( szEl, "Sn" ) ) { - nElType = ElType_Sn; - } else - if ( !strcmp( szEl, "B" ) ) { - nElType = ElType_B; - } else - if ( !strcmp( szEl, "S" ) ) { - nElType = ElType_S; - } else - if ( !strcmp( szEl, "Se" ) ) { - nElType = ElType_Se; - } else - if ( !strcmp( szEl, "N" ) ) { - nElType = ElType_N; - } else - if ( !strcmp( szEl, "P" ) ) { - nElType = ElType_P; - } else - if ( !strcmp( szEl, "As" ) ) { - nElType = ElType_As; - } else - if ( !szEl[0] ) { - if ( 1 == num_H && (num_iso_H[0] == 1 || num_iso_H[1] == 1) ) { - nElType = ElType_H; - } else - if ( 1 == num_H && num_iso_H[2] == 1 ) { - nElType = ElType_D; - } else - if ( 1 == num_H && num_iso_H[3] == 1 ) { - nElType = ElType_T; - } else { - return -1; - } - } else { - if ( e_is_element_a_metal( szEl ) ) { - return AtType_Metal; - } - return -1; /* no stereo */ - } - - /* atom type */ - memset( num_bonds, 0, sizeof(num_bonds) ); - bond_valence = 0; - valence = at[cur_atom].num_bonds; - for ( i = 0; i < valence; i ++ ) { - if ( 0 < at[cur_atom].bond_type[i] && at[cur_atom].bond_type[i] <= MAX_BOND_TYPE ) { - num_bonds[at[cur_atom].bond_type[i]-1] ++; - } - } - bond_valence = num_bonds[0] + 2*num_bonds[1] + 3*num_bonds[2]; - if ( num_bonds[3] ) { - if ( num_bonds[3] == 2 ) { - bond_valence += 3; /* -C= */ - } else - if ( num_bonds[3] == 3 ) { - bond_valence += 4; /* >C= */ - } else { - return -1; - } - } - - switch( nElType ) { - - case ElType_C: - case ElType_Si: - case ElType_Ge: - case ElType_Sn: - - if ( bAddH && bond_valence + num_H + (abs(nCharge)==1) + nRadicalValence == 4 ) { - bAddH = 0; /* no H will be added */ - } - - if ( bond_valence == valence && (valence==4 || valence == 3 && (bAddH || num_H==1)) && !nRadical ) { - switch( nElType ) { - case ElType_C: - return AtType_C4; - case ElType_Si: - return AtType_Si4; - case ElType_Ge: - return AtType_Ge4; - case ElType_Sn: - return AtType_Sn4; - } - } else - if ( bond_valence == valence && (valence==3 || valence == 2 && (bAddH || num_H==1)) && nRadical == INCHI_RADICAL_DOUBLET ) { - switch( nElType ) { - case ElType_C: - return AtType_C3; - case ElType_Si: - return -1; - case ElType_Ge: - return -1; - case ElType_Sn: - return AtType_Metal; - } - } else - if ( bond_valence == 4 && valence == 2 && !num_H && !nRadical ) { - /* two double bonds or single & triple */ - switch( nElType ) { - case ElType_C: - return AtType_C2; - case ElType_Si: - return AtType_Si2; - case ElType_Ge: - return AtType_Ge2; - case ElType_Sn: - return AtType_Sn2; - } - } else - if ( bond_valence == 3 && valence == 2 && !num_H && nRadical == INCHI_RADICAL_DOUBLET ) { - /* two double bonds or single & triple */ - switch( nElType ) { - case ElType_C: - return AtType_C2; - case ElType_Si: - return -1; - case ElType_Ge: - return -1; - case ElType_Sn: - return AtType_Metal; - } - } else - if ( bond_valence > valence && (valence==3 || valence == 2 && (bAddH || num_H==1)) && !nRadical ) { - /* "bond_valence > valence" instead of "bond_valence == valence+1" to accommodate - * erroneouse acceptance by 1.12Beta of stereo bond case when C has valence > 5 */ - switch( nElType ) { - case ElType_C: - return AtType_C3; - case ElType_Si: - return AtType_Si3; - case ElType_Ge: - return AtType_Ge3; - case ElType_Sn: - return AtType_Sn3; - } - } else - if ( bond_valence == valence+1 && bond_valence > 3 ) { - /* trying to accommodate hypervalence in coord compounds before disconnection */ - switch( nElType ) { - case ElType_C: - return AtType_C3; - case ElType_Si: - return -1; - case ElType_Ge: - return -1; - case ElType_Sn: - return AtType_Metal; - } - } else - if ( bond_valence == valence && bond_valence >= 2 && abs(nCharge) == 1 && 3 == (valence + (bAddH || num_H==1)) ) { - /* trying to accommodate C(-)-N(+) bond that may become double after ion pair removal. added 2004-01-31 */ - switch( nElType ) { - case ElType_C: - return AtType_C3; - case ElType_Si: - return AtType_Si3; - case ElType_Ge: - return -1; - case ElType_Sn: - return AtType_Metal; - } - } - return (nElType == ElType_Sn)? AtType_Metal : -1; - - case ElType_B: - if ( bond_valence == valence && (valence==4 || valence==3 && (bAddH || num_H==1)) && nCharge == -1 && !nRadical ) { - return AtType_B4m; - } - return -1; - - case ElType_S: - case ElType_Se: - if ( (valence == 3 && bond_valence == 3) && (nCharge == 1 || nRadical == INCHI_RADICAL_DOUBLET) ) { - switch( nElType ) { - case ElType_S: - return AtType_S3p; - case ElType_Se: - return AtType_Se3p; - } - } else - if ( (valence == 3 && bond_valence == 4) && nCharge == 0 && !nRadical ) { - switch( nElType ) { - case ElType_S: - return AtType_S4; - case ElType_Se: - return AtType_Se4; - } - } else - if ( (valence == 4 && bond_valence == 5) && (nCharge == 1 || nRadical == INCHI_RADICAL_DOUBLET) ) { - switch( nElType ) { - case ElType_S: - return AtType_S5p; - case ElType_Se: - return AtType_Se5p; - } - } else - if ( (valence == 4 && bond_valence == 6) && nCharge == 0 && !nRadical ) { - switch( nElType ) { - case ElType_S: - return AtType_S6; - case ElType_Se: - return AtType_S6; - } - } else { - return -1; - } - case ElType_N: - case ElType_P: - case ElType_As: - if ( bAddH && bond_valence + num_H - (abs(nCharge)==1? nCharge:0) + nRadicalValence == 3 ) { - bAddH = 0; /* no H will be added */ - } else - if ( bAddH && bond_valence + num_H == 5 ) { - bAddH = 0; /* no H will be added */ - } - if ( bond_valence == valence && (valence==4 || valence==3 && (bAddH || num_H==1)) && nCharge == 1 && !nRadical ) { - switch( nElType ) { - case ElType_N: - return AtType_N4p; - case ElType_P: - return AtType_P4p; - case ElType_As: - return AtType_As4p; - } - } else - if ( bond_valence == valence+1 && (valence==4 /*|| valence == 3*/) && nCharge == 0 ) { - switch( nElType ) { - case ElType_N: - return AtType_N5; - case ElType_P: - return AtType_P5; - case ElType_As: - return AtType_As5; - } - } else - if ( bond_valence == valence && valence==3 && nCharge == 0 && !nRadical ) { - switch( nElType ) { - case ElType_N: - { - AT_NUM neigh1, neigh2; - int j1, j2, bIn3MembRing = 0; - for ( j1 = 0; j1 < valence && !bIn3MembRing; j1 ++ ) { - neigh1 = at[cur_atom].neighbor[j1]; - for ( j2 = j1+1; j2 < valence && !bIn3MembRing; j2 ++ ) { - neigh2 = at[cur_atom].neighbor[j2]; - if ( e_is_in_the_slist( at[neigh1].neighbor, neigh2, at[neigh1].num_bonds ) ) { - bIn3MembRing ++; - } - } - } - return bIn3MembRing? AtType_N3r : AtType_Nns /* -1*/; - } - case ElType_P: - case ElType_As: - return -1; - } - } else - if ( bond_valence == valence+1 && valence==2 && nCharge == 0 && !nRadical ) { - switch( nElType ) { - case ElType_N: - return AtType_N3; - case ElType_P: - case ElType_As: - return -1; - } - } else - if ( bond_valence == valence+1 && valence==3 && nCharge == 0 && !nRadical ) { - /* reproduce 1.12Beta bug: =N< is accepted in stereogenic bonds */ - switch( nElType ) { - case ElType_N: - return AtType_N3; - case ElType_P: - case ElType_As: - return -1; - } - } else - if ( bond_valence == valence && valence==2 && nCharge == 0 && nRadical == INCHI_RADICAL_DOUBLET ) { - switch( nElType ) { - case ElType_N: - return AtType_N3; - case ElType_P: - case ElType_As: - return -1; - } - } else - if ( bond_valence == valence+1 && (valence==3 || valence == 2 && (bAddH || num_H==1)) && nCharge == 1 ) { - switch( nElType ) { - case ElType_N: - return AtType_N3p; - case ElType_P: - case ElType_As: - return -1; - } - } else - if ( bond_valence == valence && (valence==3 || valence == 2 && (bAddH || num_H==1)) && nCharge == 1 && nRadical == INCHI_RADICAL_DOUBLET ) { - switch( nElType ) { - case ElType_N: - return AtType_N3p; - case ElType_P: - case ElType_As: - return -1; - } - } else - if ( bond_valence == valence && valence == 2 && (bAddH || num_H==1) && nCharge == 0 || - bond_valence == valence && valence == 2 && nCharge == -1 ) { - switch( nElType ) { - case ElType_N: - return AtType_Nns; - case ElType_P: - case ElType_As: - return -1; - } - } else - if ( (bond_valence == valence+2 || bond_valence + nRadicalValence == valence+2) && valence==3 ) { - switch( nElType ) { - case ElType_N: - return AtType_Nns; /* -N<< may be in a stereogenic bond in case of double bond metal disconnection */ - case ElType_P: - case ElType_As: - return -1; - } - } - return -1; - - case ElType_H: - if ( valence == 1 ) - return AtType_TermH; - break; - case ElType_D: - if ( valence == 1 ) - return AtType_TermD; - break; - case ElType_T: - if ( valence == 1 ) - return AtType_TermT; - break; - - } - - return -1; -} - - -/**********************************************************************************/ -void e_inchi_swap ( char *a, char *b, size_t width ) -{ - char tmp; - if ( a != b ) - while ( width-- ) { - tmp = *a; - *a++ = *b; - *b++ = tmp; - } -} -/**********************************************************************************/ -/* Sort by insertions */ -int e_insertions_sort( void *base, size_t num, size_t width, int ( *compare )(const void *e1, const void *e2 ) ) -{ - char *i, *j, *pk; - int num_trans = 0; - size_t k; - for( k=1, pk = (char*)base; k < num; k++, pk += width ) { - for( i = pk, j = pk + width; j > (char*)base && (*compare)(i,j) > 0; j=i, i -= width ) { - e_inchi_swap( i, j, width ); - num_trans ++; - } - } - return num_trans; -} - - -#define ZTYPE_DOWN (-1) /* should be equal to -ZTYPE_UP */ -#define ZTYPE_NONE 0 -#define ZTYPE_UP 1 /* should be equal to -ZTYPE_DOWN */ -#define ZTYPE_3D 3 -#define ZTYPE_EITHER 9999 - -/* criteria for ill-defined */ -#define MIN_ANGLE 0.10 /* 5.73 degrees */ -#define MIN_SINE 0.03 /* min edge/plane angle in case the tetrahedra has significantly different edge length */ -#define MIN_ANGLE_DBOND 0.087156 /* 5 degrees = max angle considered as too small for unambiguous double bond stereo */ -#define MIN_SINE_OUTSIDE 0.06 /* min edge/plane angle to determine whether the central atom is outside of the tetrahedra */ -#define MIN_SINE_SQUARE 0.125 /* min edge/plane angle in case the tetrahedra is somewhat close to a parallelogram */ -#define MIN_SINE_EDGE 0.167 /* min sine/(min.edge) ratio to avoid undefined in case of long edges */ -#define MIN_LEN_STRAIGHT 1.900 /* min length of two normalized to 1 bonds in a straight line */ -#define MAX_SINE 0.70710678118654752440084436210485 /* 1/sqrt(2)=sin(pi/4) */ -#define MIN_BOND_LEN 0.000001 -#define ZERO_LENGTH MIN_BOND_LEN -#define ZERO_FLOAT 1.0e-12 -#define BOND_PARITY_UNDEFINED 64 -#if( STEREO_CENTER_BONDS_NORM == 1 ) -#define MPY_SINE 1.00 /* was 3.0 */ -#define MAX_EDGE_RATIO 2.50 /* max max/min edge ratio for a tetrahedra close to a parallelogram */ -#else -#define MPY_SINE 3.00 -#define MAX_EDGE_RATIO 6.00 /* max max/min edge ratio for a tetrahedra close to a parallelogram */ -#endif -/* local prototypes */ -static double e_get_z_coord( inchi_Atom* at, int cur_atom, int neigh_no, int *nType,int bPointedEdgeStereo ); -static double e_len3( const double c[] ); -static double e_len2( const double c[] ); -static double* e_diff3( const double a[], const double b[], double result[] ); -static double* e_add3( const double a[], const double b[], double result[] ); -static double* e_mult3( const double a[], double b, double result[] ); -static double* e_copy3( const double a[], double result[] ); -static double* e_change_sign3( const double a[], double result[] ); -static double e_dot_prod3( const double a[], const double b[] ); -static int e_dot_prodchar3( const S_CHAR a[], const S_CHAR b[] ); -static double* e_cross_prod3( const double a[], const double b[], double result[] ); -static double e_triple_prod( double a[], double b[], double c[], double *sine_value ); -static double e_triple_prod_and_min_abs_sine(double at_coord[][3], double *min_sine); -static int are_3_vect_in_one_plane( double at_coord[][3], double min_sine); -static int e_triple_prod_char( inchi_Atom *at, int at_1, int i_next_at_1, S_CHAR *z_dir1, - int at_2, int i_next_at_2, S_CHAR *z_dir2 ); - -static int e_CompDble( const void *a1, const void *a2 ); -static int e_Get2DTetrahedralAmbiguity( double at_coord[][3], int bAddExplicitNeighbor ); -static double e_triple_prod_and_min_abs_sine2(double at_coord[][3], double central_at_coord[], int bAddedExplicitNeighbor, double *min_sine, int *bAmbiguous); -static int e_are_4at_in_one_plane( double at_coord[][3], double min_sine); -static int e_half_stereo_bond_parity( inchi_Atom *at, int cur_at, S_CHAR *z_dir, int *bOnlyNonMetal, int bPointedEdgeStereo, Stereo0D *pStereo ); -static int e_set_stereo_bonds_parity( Stereo0D *pStereo, inchi_Atom *at, int at_1, int bPointedEdgeStereo ); -static int e_set_stereo_atom_parity( Stereo0D *pStereo, inchi_Atom *at, int cur_at, int bPointedEdgeStereo ); -static int e_FixSb0DParities( inchi_Atom *at, Stereo0D *pStereo, int chain_length, AT_NUM at_middle, - int at_1, int i_next_at_1, S_CHAR z_dir1[], S_CHAR z_dir1NM[], int bOnlyNM1, int bAnomaly1NM, int parity1, int parity1NM, - int at_2, int i_next_at_2, S_CHAR z_dir2[], S_CHAR z_dir2NM[], int bOnlyNM2, int bAnomaly2NM, int parity2, int parity2NM ); - - -/******************************************************************/ - - -static double *pDoubleForSort; - -/**********************************************************************************/ -double e_get_z_coord( inchi_Atom* at, int cur_atom, int neigh_no, int *nType, int bPointedEdgeStereo ) -{ - int stereo_value = at[cur_atom].bond_stereo[neigh_no]; - int stereo_type = abs( stereo_value ); - int neigh = (int)at[cur_atom].neighbor[neigh_no]; - double z = at[neigh].z - at[cur_atom].z; - int bFlat; - - if ( bFlat = (fabs(z) < ZERO_LENGTH) ) { - int i; - for ( i = 0; i < at[cur_atom].num_bonds; i ++ ) { - if ( fabs(at[cur_atom].z - at[(int)at[cur_atom].neighbor[i]].z) > ZERO_LENGTH ) { - bFlat = 0; - break; - } - } - } - - if ( bFlat ) { - if ( !bPointedEdgeStereo || bPointedEdgeStereo * stereo_value >= 0 ) { - /* bPointedEdgeStereo > 0: define stereo from pointed end of the stereo bond only */ - /* bPointedEdgeStereo < 0: define stereo from wide end of the stereo bond only (case of removed H) */ - switch( stereo_type ) { - /* 1=Up (solid triangle), 6=Down (Dashed triangle), 4=Either (zigzag triangle) */ - case 0: /* No stereo */ - *nType = ZTYPE_NONE; - break; - case INCHI_BOND_STEREO_SINGLE_1UP: /* 1= Up */ - *nType = ZTYPE_UP; - break; - case INCHI_BOND_STEREO_SINGLE_1EITHER: /* 4 = Either */ - *nType = ZTYPE_EITHER; - break; - case INCHI_BOND_STEREO_SINGLE_1DOWN: /* 6 = Down */ - *nType = ZTYPE_DOWN; - break; - default: - *nType = ZTYPE_NONE; /* ignore unexpected values */ - } - if ( stereo_value < 0 && (*nType == ZTYPE_DOWN || *nType == ZTYPE_UP) ) - *nType = -*nType; - } else { - *nType = ZTYPE_NONE; /* no stereo */ - } - } else - if ( stereo_type == INCHI_BOND_STEREO_SINGLE_1EITHER && - ( !bPointedEdgeStereo || bPointedEdgeStereo * stereo_value >= 0 ) ) { - *nType = ZTYPE_EITHER; - } else { - *nType = ZTYPE_3D; - } - return z; -} -/******************************************************************/ -double e_len3( const double c[] ) -{ - return sqrt( c[0]*c[0] + c[1]*c[1] + c[2]*c[2] ); -} -/******************************************************************/ -double e_len2( const double c[] ) -{ - return sqrt( c[0]*c[0] + c[1]*c[1] ); -} -/******************************************************************/ -double* e_diff3( const double a[], const double b[], double result[] ) -{ - - result[0] = a[0] - b[0]; - result[1] = a[1] - b[1]; - result[2] = a[2] - b[2]; - - return result; -} -/******************************************************************/ -double* e_add3( const double a[], const double b[], double result[] ) -{ - result[0] = a[0] + b[0]; - result[1] = a[1] + b[1]; - result[2] = a[2] + b[2]; - - return result; -} -/******************************************************************/ -double* e_mult3( const double a[], double b, double result[] ) -{ - result[0] = a[0] * b; - result[1] = a[1] * b; - result[2] = a[2] * b; - - return result; -} -/*************************************************************/ -double* e_copy3( const double a[], double result[] ) -{ - result[0] = a[0]; - result[1] = a[1]; - result[2] = a[2]; - - return result; -} -/*************************************************************/ -double* e_change_sign3( const double a[], double result[] ) -{ - result[0] = -a[0]; - result[1] = -a[1]; - result[2] = -a[2]; - - return result; -} -/*************************************************************/ -double e_dot_prod3( const double a[], const double b[] ) -{ - return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; -} -/*************************************************************/ -int e_dot_prodchar3( const S_CHAR a[], const S_CHAR b[] ) -{ - int prod = ((int)a[0]*(int)b[0] + (int)a[1]*(int)b[1] + (int)a[2]*(int)b[2])/100; - if ( prod > 100 ) - prod = 100; - else - if ( prod < -100 ) - prod = -100; - return prod; -} -/*************************************************************/ -double* e_cross_prod3( const double a[], const double b[], double result[] ) -{ - double tmp[3]; - - tmp[0] = (a[1]*b[2]-a[2]*b[1]); - tmp[1] = -(a[0]*b[2]-a[2]*b[0]); - tmp[2] = (a[0]*b[1]-a[1]*b[0]); - - result[0] = tmp[0]; - result[1] = tmp[1]; - result[2] = tmp[2]; - - return result; -} -/*************************************************************/ -double e_triple_prod( double a[], double b[], double c[], double *sine_value ) -{ - double ab[3], dot_prod_ab_c, abs_c, abs_ab; - e_cross_prod3( a, b, ab ); - /* ab[0] = (a[1]*b[2]-a[2]*b[1]); */ - /* ab[1] = -(a[0]*b[2]-a[2]*b[0]); */ - /* ab[2] = (a[0]*b[1]-a[1]*b[0]); */ - dot_prod_ab_c = e_dot_prod3( ab, c ); - /* dot_prod_ab_c = ab[0]*c[0] + ab[1]*c[1] + ab[2]*c[2]; */ - if ( sine_value ) { - abs_c = e_len3( c ); - /* abs_c = sqrt( c[0]*c[0] + c[1]*c[1] + c[2]*c[2] ); */ - abs_ab = e_len3( ab ); - /* abs_ab = sqrt( ab[0]*ab[0] + ab[1]*ab[1] + ab[2]*ab[2] ); */ - - if ( abs_c > 1.e-7 /* otherwise c has zero length */ && abs_ab > 1.e-7 /* otherwise a is parallel to b*/ ) { - *sine_value = MPY_SINE * dot_prod_ab_c / ( abs_c * abs_ab); - /* *sine_value = dot_prod_ab_c / ( abs_c * abs_ab); */ - } else { - *sine_value = 0.0; - } - } - return dot_prod_ab_c; -} -/*************************************************************/ -int e_CompDble( const void *a1, const void *a2 ) -{ - double diff = pDoubleForSort[*(const int*)a1] - pDoubleForSort[*(const int*)a2]; - if ( diff > 0.0 ) - return 1; - if ( diff < 0.0 ) - return -1; - return 0; -} -/*************************************************************/ -#define T2D_OKAY 1 -#define T2D_WARN 2 -#define T2D_UNDF 4 -int e_Get2DTetrahedralAmbiguity( double at_coord[][3], int bAddExplicitNeighbor ) -{ -#define ZERO_ANGLE 0.000001 - const double one_pi = 2.0*atan2(1.0 /* y */, 0.0 /* x */); - const double two_pi = 2.0*one_pi; - const double dAngleAndPiMaxDiff = 2.0*atan2(1.0, sqrt(7.0)); /* min sine between 2 InPlane bonds */ - int nBondType[MAX_NUM_STEREO_ATOM_NEIGH], nBondOrder[MAX_NUM_STEREO_ATOM_NEIGH]; - double dBondDirection[MAX_NUM_STEREO_ATOM_NEIGH], dAngle, dAlpha, dLimit, dBisector; - int nNumNeigh = MAX_NUM_STEREO_ATOM_NEIGH - (bAddExplicitNeighbor != 0); - int i, num_Up, num_Dn, bPrev_Up, cur_len_Up, cur_first_Up, len_Up, first_Up; - int ret; - int bFixSp3Bug = 1; /* 1=> FixSp3Bug option for overlapping 2D stereo bonds */ - - ret = 0; - for ( i = 0, num_Up = num_Dn = 0; i < nNumNeigh; i ++ ) { - dAngle = atan2( at_coord[i][1], at_coord[i][0] ); /* range from -pi to +pi */ - if ( dAngle < 0.0 ) { - dAngle += two_pi; - } - dBondDirection[i] = dAngle; - nBondType[i] = (at_coord[i][2] > 0.0)? 1 : (at_coord[i][2] < 0.0)? -1 : 0; /* z-coord sign */ - if ( nBondType[i] > 0 ) { - num_Up ++; - } else - if ( nBondType[i] < 0 ) { - num_Dn ++; - } - nBondOrder[i] = i; - } - if ( num_Up < num_Dn ) { - for ( i = 0; i < nNumNeigh; i ++ ) { - nBondType[i] = -nBondType[i]; - } - e_inchi_swap( (char*)&num_Dn, (char*)&num_Up, sizeof(num_Dn) ); - } - if ( !num_Up ) { - return T2D_UNDF; - } - /* sort according to the bond orientations */ - pDoubleForSort = dBondDirection; - e_insertions_sort( nBondOrder, nNumNeigh, sizeof(nBondOrder[0]), e_CompDble ); - - /* find the longest contiguous sequence of Up bonds */ - if ( num_Up == nNumNeigh ) { - /* all bonds are Up */ - len_Up = cur_len_Up = nNumNeigh; /* added cur_len_Up initialization 1/8/2002 */ - first_Up = 0; - } else { - /* at least one bond is not Up */ - cur_len_Up = len_Up = bPrev_Up = 0; - /* prev. cycle header version --- - for ( i = 0; 1; i ++ ) { - if ( i >= nNumNeigh && !bPrev_Up ) { - break; - } - ----------} */ - /* look at all bonds and continue (circle therough the beginning) as long as the current bond is Up */ - for ( i = 0; i < nNumNeigh || bPrev_Up; i ++ ) { - if ( nBondType[nBondOrder[i % nNumNeigh]] > 0 ) { - if ( bPrev_Up ) { - cur_len_Up ++; /* uncrement number of Up bonds in current contiguous sequence of them */ - } else { - bPrev_Up = 1; /* start new contiguous sequence of Up bonds */ - cur_len_Up = 1; - cur_first_Up = i % nNumNeigh; - } - } else - if ( bPrev_Up ) { /* end of contiguous sequence of Up bonds */ - if ( cur_len_Up > len_Up ) { - first_Up = cur_first_Up; /* store the sequence because it is longer than the ptrvious one */ - len_Up = cur_len_Up; - } - bPrev_Up = 0; - } - } - } - if ( bFixSp3Bug ) { - /* check if the bonds with ordering numbers first_Up+len_Up and first_Up+len_Up+1 */ - /* have identical angles. In this case switch their order to enlarge the Up sequence */ - if ( nNumNeigh - len_Up >= 2 ) { - int next1, next2; - for ( i = 1; i < nNumNeigh - len_Up; i ++ ) { - next2 = (first_Up+len_Up + i) % nNumNeigh; /* the 2nd after Up sequence */ - if ( nBondType[nBondOrder[next2]] > 0 ) { - next1 = (first_Up+len_Up) % nNumNeigh; /* the 1st after Up sequence */ - dAngle = dBondDirection[nBondOrder[next1]] - dBondDirection[nBondOrder[next2]]; - if ( fabs(dAngle) < ZERO_ANGLE ) { - e_inchi_swap( (char*)&nBondOrder[next1], (char*)&nBondOrder[next2], sizeof(nBondOrder[0]) ); - len_Up ++; - break; - } - } - } - } - /* check whether the not-Up bond (located before the found first-Up) has */ - /* same angle as the Up bond that precedes this not-Up bond */ - if ( nNumNeigh - len_Up >= 2 ) { - int next1, next2; - for ( i = 1; i < nNumNeigh - len_Up; i ++ ) { - next2 = (first_Up+nNumNeigh - i - 1 ) % nNumNeigh; /* the 2nd before Up sequence */ - if ( nBondType[nBondOrder[next2]] > 0 ) { - next1 = (first_Up+nNumNeigh-1) % nNumNeigh; /* the 1st before Up sequence */ - dAngle = dBondDirection[nBondOrder[next1]] - dBondDirection[nBondOrder[next2]]; - if ( fabs(dAngle) < ZERO_ANGLE ) { - e_inchi_swap( (char*)&nBondOrder[next1], (char*)&nBondOrder[next2], sizeof(nBondOrder[0]) ); - first_Up = next1; - len_Up ++; - break; - } - } - } - } - } - /* Turn all the bonds around the center so that */ - /* the 1st Up bond has zero radian direction */ - dAlpha = dBondDirection[nBondOrder[first_Up]]; - for ( i = 0; i < nNumNeigh; i ++ ) { - if ( i == nBondOrder[first_Up] ) { - dBondDirection[i] = 0.0; - } else { - dAngle = dBondDirection[i] - dAlpha; - if ( dAngle < 0.0 ) { - dAngle += two_pi; - } - dBondDirection[i] = dAngle; - } - } - - /******************************************************** - * Process particular cases - ********************************************************/ - - switch( nNumNeigh ) { - - /************************ 3 bonds *********************** - */ - case 3: - switch( num_Up ) { - /* -------------------------- 0 Up ------------ */ - case 0: - return T2D_UNDF; - /* -------------------------- 1 Up ------------ */ - case 1: - if ( num_Dn ) { -#ifdef _DEBUG - if ( num_Dn != 1 ) /* debug only */ - return -1; -#endif - ret = (T2D_UNDF | T2D_WARN); - } else { - dAngle = dBondDirection[nBondOrder[(first_Up + 2) % nNumNeigh]] - - dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; - if ( dAngle < 0.0 ) { - dAngle += two_pi; - } - if ( dAngle - one_pi < -MIN_ANGLE || dAngle - one_pi > MIN_ANGLE ) { - ret = T2D_OKAY; - } else { - ret = (T2D_UNDF | T2D_WARN); - } - } - break; - /* -------------------------- 2 Up ------------ */ - case 2: - if ( num_Dn ) { - dAlpha = dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]] - - dBondDirection[nBondOrder[(first_Up ) % nNumNeigh]]; - if ( dAlpha < 0.0 ) { - dAlpha += two_pi; - } - if ( dAlpha > one_pi - MIN_ANGLE ) { - ret = T2D_OKAY; - } else - if ( dAlpha < two_pi / 3.0 - MIN_ANGLE ) { - ret = (T2D_UNDF | T2D_WARN); - } else { - /* angle between 2 Up bonds is between 120 and 180 degrees */ - /* direction of the (Alpha angle bisector) + 180 degrees */ - dBisector = (dBondDirection[nBondOrder[(first_Up ) % nNumNeigh]] + - dBondDirection[nBondOrder[(first_Up + 1 ) % nNumNeigh]] ) / 2.0 - one_pi; - if ( dBisector < 0.0 ) { - dBisector += two_pi; - } - if ( dAlpha < two_pi / 3.0 + MIN_ANGLE ) { - /* dAlpha is inside ( 2pi/3 - eps, 2pi/3 + eps ) interval */ - dLimit = MIN_ANGLE * 3.0 / 2.0; - } else { - dLimit = dAlpha * 3.0 / 2.0 - one_pi; - } - dAngle = dBondDirection[nBondOrder[(first_Up + 2 ) % nNumNeigh]]; - if ( dBisector - dAngle < -dLimit || - dBisector - dAngle > dLimit ) { - ret = (T2D_UNDF | T2D_WARN); - } else { - ret = T2D_OKAY; - } - } - } else { - ret = T2D_OKAY; - } - - break; - /* -------------------------- 3 Up ------------ */ - case 3: - ret = T2D_OKAY; - break; - /* -------------------------- other Up -------- */ - default: - return -1; - - } - - break; - - /************************************** 4 bonds ************************** - */ - case 4: - switch( num_Up ) { - /* -------------------------- 0 Up ------------ */ - case 0: - return T2D_UNDF; - /* -------------------------- 1 Up ------------ */ - case 1: - if ( num_Dn ) { - if ( nBondType[nBondOrder[(first_Up + 2) % nNumNeigh]] < 0 ) { - /* - * Up, In Plane, Dn, In Plane. Undefined if angle between - * two In Plane bonds is wuthin pi +/- 2*arcsine(1/sqrt(8)) interval - * That is, 138.5 to 221.4 degrees; for certainty the interval is - * increased by 5.7 degrees at each end to - * 134.8 to 227.1 degrees - */ - dAngle = dBondDirection[nBondOrder[(first_Up + 3) % nNumNeigh]] - - dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; - if ( dAngle < 0.0 ) { - dAngle += two_pi; - } - if ( fabs( dAngle - one_pi ) < dAngleAndPiMaxDiff + MIN_ANGLE ) { - ret = (T2D_UNDF | T2D_WARN); - } else { - ret = T2D_OKAY; - } - } else { - ret = T2D_OKAY; - } -#ifdef _DEBUG - if ( num_Dn != 1 ) /* debug only */ - return -1; -#endif - } else { - ret = T2D_OKAY; - dAngle = dBondDirection[nBondOrder[(first_Up + 3) % nNumNeigh]] - - dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; - if ( dAngle < 0.0 ) { - dAngle += two_pi; - } - if ( dAngle < one_pi - MIN_ANGLE ) { - ret |= T2D_WARN; - } - } - break; - /* -------------------------- 2 Up ------------ */ - case 2: - if ( bFixSp3Bug ) { - /* bug fix */ - if ( len_Up == 1 ) { - ret = T2D_OKAY; - } else { - dAngle = dBondDirection[nBondOrder[(first_Up + 3) % nNumNeigh]] - - dBondDirection[nBondOrder[(first_Up + 0) % nNumNeigh]]; - dAngle = fabs(two_pi - dAngle); - dAlpha = dBondDirection[nBondOrder[(first_Up + 2) % nNumNeigh]] - - dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; - dAlpha = fabs(dAlpha); - if ( dAngle < 2.0 * ZERO_ANGLE && dAlpha > MIN_ANGLE || - dAlpha < 2.0 * ZERO_ANGLE && dAngle > MIN_ANGLE ) { - ret = (T2D_OKAY | T2D_WARN); - } else { - ret = (T2D_UNDF | T2D_WARN); - } - } - } else { - /* original InChI v. 1 bug */ - if ( cur_len_Up == 1 ) { - ret = T2D_OKAY; - } else { - ret = (T2D_UNDF | T2D_WARN); - } - } - break; - - /* -------------------------- 3 Up ------------ */ - case 3: - ret = T2D_OKAY; - dAngle = dBondDirection[nBondOrder[(first_Up + 2) % nNumNeigh]] - - dBondDirection[nBondOrder[(first_Up + 0) % nNumNeigh]]; - if ( dAngle < 0.0 ) { - dAngle += two_pi; - } - if ( dAngle < one_pi - MIN_ANGLE ) { - ret |= T2D_WARN; - } - break; - /* -------------------------- 4 Up ------------ */ - case 4: - ret = (T2D_UNDF | T2D_WARN); - break; - /* -------------------------- other Up -------- */ - default: - return -1; /* program error */ - } - - if ( ret == T2D_OKAY ) { - /* check whether all bonds are inside a less than 180 degrees sector */ - for ( i = 0; i < nNumNeigh; i ++ ) { - dAngle = dBondDirection[nBondOrder[(i + nNumNeigh - 1) % nNumNeigh]] - - dBondDirection[nBondOrder[ i % nNumNeigh]]; - if ( dAngle < 0.0 ) { - dAngle += two_pi; - } - if ( dAngle < one_pi - MIN_ANGLE ) { - ret |= T2D_WARN; - break; - } - } - } - - break; - /*************************** other nuber of bonds ******************/ - default: - return -1; /* error */ - } - - return ret; - -#undef ZERO_ANGLE -} -/*************************************************************/ -double e_triple_prod_and_min_abs_sine2(double at_coord[][3], double central_at_coord[], int bAddedExplicitNeighbor, double *min_sine, int *bAmbiguous) -{ - double min_sine_value=9999.0, sine_value, min_edge_len, max_edge_len, min_edge_len_NoExplNeigh, max_edge_len_NoExplNeigh; - double s0, s1, s2, s3, e01, e02, e03, e12, e13, e23, tmp[3], e[3][3]; - double prod, ret, central_prod[4]; - int bLongEdges; - - if ( !min_sine ) { - return e_triple_prod( at_coord[0], at_coord[1], at_coord[2], NULL ); - } - - ret = e_triple_prod( at_coord[0], at_coord[1], at_coord[2], &sine_value ); - sine_value = MPY_SINE * fabs( sine_value ); - - e_diff3( at_coord[1], at_coord[0], e[2] ); - e_diff3( at_coord[0], at_coord[2], e[1] ); - e_diff3( at_coord[2], at_coord[1], e[0] ); - - /* lengths of the 6 edges of the tetrahedra */ - e03 = e_len3( at_coord[0] ); /* 1 */ - e13 = e_len3( at_coord[1] ); - e23 = e_len3( at_coord[2] ); /* includes added neighbor if bAddedExplicitNeighbor*/ - e02 = e_len3( e[1] ); /* includes added neighbor if bAddedExplicitNeighbor*/ - e12 = e_len3( e[0] ); /* includes added neighbor if bAddedExplicitNeighbor*/ - e01 = e_len3( e[2] ); - - /* min & max edge length */ - max_edge_len = - min_edge_len = e03; - - if ( min_edge_len > e13 ) - min_edge_len = e13; - if ( min_edge_len > e01 ) - min_edge_len = e01; - min_edge_len_NoExplNeigh = min_edge_len; - - if ( min_edge_len > e23 ) - min_edge_len = e23; - if ( min_edge_len > e02 ) - min_edge_len = e02; - if ( min_edge_len > e12 ) - min_edge_len = e12; - - if ( max_edge_len < e13 ) - max_edge_len = e13; - if ( max_edge_len < e01 ) - max_edge_len = e01; - max_edge_len_NoExplNeigh = max_edge_len; - - if ( max_edge_len < e23 ) - max_edge_len = e23; - if ( max_edge_len < e02 ) - max_edge_len = e02; - if ( max_edge_len < e12 ) - max_edge_len = e12; - - if ( !bAddedExplicitNeighbor ) { - min_edge_len_NoExplNeigh = min_edge_len; - max_edge_len_NoExplNeigh = max_edge_len; - } - - bLongEdges = bAddedExplicitNeighbor? - ( max_edge_len_NoExplNeigh < MAX_EDGE_RATIO * min_edge_len_NoExplNeigh ) : - ( max_edge_len < MAX_EDGE_RATIO * min_edge_len ); - - if ( sine_value > MIN_SINE && ( min_sine || bAmbiguous ) ) { - if ( min_sine ) { - prod = fabs( ret ); - /* tetrahedra height = volume(prod) / area of a plane(cross_prod) */ - /* (instead of a tetrahedra calculate parallelogram/parallelepiped area/volume) */ - - /* 4 heights from each of the 4 vertices to the opposite plane */ - s0 = prod / e_len3( e_cross_prod3( at_coord[1], at_coord[2], tmp ) ); - s1 = prod / e_len3( e_cross_prod3( at_coord[0], at_coord[2], tmp ) ); - s2 = prod / e_len3( e_cross_prod3( at_coord[0], at_coord[1], tmp ) ); - s3 = prod / e_len3( e_cross_prod3( e[0], e[1], tmp ) ); - /* abs. value of a sine of an angle between each tetrahedra edge and plane */ - /* sine = height / edge length */ - if ( (sine_value = s0/e01) < min_sine_value ) - min_sine_value = sine_value; - if ( (sine_value = s0/e02) < min_sine_value ) - min_sine_value = sine_value; - if ( (sine_value = s0/e03) < min_sine_value ) - min_sine_value = sine_value; - - if ( (sine_value = s1/e01) < min_sine_value ) - min_sine_value = sine_value; - if ( (sine_value = s1/e12) < min_sine_value ) - min_sine_value = sine_value; - if ( (sine_value = s1/e13) < min_sine_value ) - min_sine_value = sine_value; - - if ( (sine_value = s2/e02) < min_sine_value ) - min_sine_value = sine_value; - if ( (sine_value = s2/e12) < min_sine_value ) - min_sine_value = sine_value; - if ( (sine_value = s2/e23) < min_sine_value ) - min_sine_value = sine_value; - - if ( (sine_value = s3/e03) < min_sine_value ) - min_sine_value = sine_value; - if ( (sine_value = s3/e13) < min_sine_value ) - min_sine_value = sine_value; - if ( (sine_value = s3/e23) < min_sine_value ) - min_sine_value = sine_value; - /* actually use triple sine */ - *min_sine = sine_value = MPY_SINE * min_sine_value; - } - - if ( bAmbiguous && sine_value >= MIN_SINE ) { - /* check whether the central atom is outside the tetrahedra (0,0,0), at_coord[0,1,2] */ - /* compare the tetrahedra volume and the volume of a tetrahedra having central_at_coord[] vertex */ - int i; - e_diff3( central_at_coord, at_coord[0], tmp ); - central_prod[0] = e_triple_prod( at_coord[0], at_coord[1], central_at_coord, NULL ); - central_prod[1] = e_triple_prod( at_coord[1], at_coord[2], central_at_coord, NULL ); - central_prod[2] = e_triple_prod( at_coord[2], at_coord[0], central_at_coord, NULL ); - central_prod[3] = e_triple_prod( e[2], e[1], tmp, NULL ); - for ( i = 0; i <= 3; i ++ ) { - if ( central_prod[i] / ret < -MIN_SINE_OUTSIDE ) { - *bAmbiguous |= AMBIGUOUS_STEREO; - break; - } - } - } -#if( STEREO_CENTER_BONDS_NORM == 1 ) - - if ( bLongEdges && !bAddedExplicitNeighbor && max_edge_len >= MIN_LEN_STRAIGHT ) { - /* possible planar tetragon */ - if ( sine_value < MIN_SINE_SQUARE ) { - *min_sine = MIN_SINE / 2.0; /* force parity to be undefined */ - if ( bAmbiguous && !*bAmbiguous ) { - *bAmbiguous |= AMBIGUOUS_STEREO; - } - } - } - - if ( bLongEdges && sine_value < MIN_SINE_SQUARE && sine_value < MIN_SINE_EDGE * min_edge_len_NoExplNeigh ) { - *min_sine = MIN_SINE / 2.0; /* force parity to be undefined */ - if ( bAmbiguous && !*bAmbiguous ) { - *bAmbiguous |= AMBIGUOUS_STEREO; - } - } -#endif - - } else - if ( min_sine ) { - *min_sine = sine_value; - } - - return ret; -} -/*************************************************************/ -double e_triple_prod_and_min_abs_sine(double at_coord[][3], double *min_sine) -{ - double min_sine_value=9999.0, sine_value; - double prod=0.0; - - if ( !min_sine ) { - return e_triple_prod( at_coord[0], at_coord[1], at_coord[2], NULL ); - } - - prod = e_triple_prod( at_coord[0], at_coord[1], at_coord[2], &sine_value ); - sine_value = fabs( sine_value ); - min_sine_value = inchi_min( min_sine_value, sine_value ); - - prod = e_triple_prod( at_coord[1], at_coord[2], at_coord[0], &sine_value ); - sine_value = fabs( sine_value ); - min_sine_value = inchi_min( min_sine_value, sine_value ); - - prod = e_triple_prod( at_coord[2], at_coord[0], at_coord[1], &sine_value ); - sine_value = fabs( sine_value ); - min_sine_value = inchi_min( min_sine_value, sine_value ); - - *min_sine = min_sine_value; - - return prod; -} -/*************************************************************/ -/* Find if point (0,0,0)a and 3 atoms are in one plane */ -int are_3_vect_in_one_plane( double at_coord[][3], double min_sine) -{ - double actual_min_sine; - double prod; - prod = e_triple_prod_and_min_abs_sine( at_coord, &actual_min_sine); - return actual_min_sine <= min_sine; -} -/*************************************************************/ -/* Find if 4 atoms are in one plane */ -int e_are_4at_in_one_plane( double at_coord[][3], double min_sine) -{ - double actual_min_sine, min_actual_min_sine; - double coord[3][3], prod; - int i, k, j; - for ( k = 0; k < 4; k ++ ) { /* cycle added 4004-08-15 */ - for ( i = j = 0; i < 4; i ++ ) { - if ( i != k ) { - e_diff3( at_coord[i], at_coord[k], coord[j] ); - j ++; - } - } - prod = e_triple_prod_and_min_abs_sine( coord, &actual_min_sine); - if ( !k || actual_min_sine < min_actual_min_sine ) { - min_actual_min_sine = actual_min_sine; - } - } - return min_actual_min_sine <= min_sine; -} -/*************************************************************/ -int e_triple_prod_char( inchi_Atom *at, int at_1, int i_next_at_1, S_CHAR *z_dir1, - int at_2, int i_next_at_2, S_CHAR *z_dir2 ) -{ - inchi_Atom *at1, *at2; - double pnt[3][3], len; - int i; - int ret = 0; - - at1 = at + at_1; - at2 = at + at[at_1].neighbor[i_next_at_1]; - - pnt[0][0] = at2->x - at1->x; - pnt[0][1] = at2->y - at1->y; - pnt[0][2] = at2->z - at1->z; - - at2 = at + at_2; - at1 = at + at[at_2].neighbor[i_next_at_2]; - - pnt[1][0] = at2->x - at1->x; - pnt[1][1] = at2->y - at1->y; - pnt[1][2] = at2->z - at1->z; -/* - * resultant pnt vector directions: - * - * pnt[0] pnt[1] - * - * [at_1]---->[...] [...]---->[at_2] - * - * - * e_add3 below: (pnt[0] + pnt[1]) -> pnt[1] - */ - e_add3( pnt[0], pnt[1], pnt[1] ); - - - - for ( i = 0; i < 3; i ++ ) { - pnt[0][i] = (double)z_dir1[i]; - pnt[2][i] = (double)z_dir2[i]; - } - for ( i = 0; i < 3; i ++ ) { - len = e_len3( pnt[i] ); - if ( len < MIN_BOND_LEN ) { - goto exit_function; /* too short bond */ - } - e_mult3( pnt[i], 1.0/len, pnt[i] ); - } - len = 100.0*e_triple_prod(pnt[0], pnt[1], pnt[2], NULL ); -/* - * ^ pnt[0] - * | The orientation on this diagram - * | produces len = -100 - * [at_1]------>[at_2] - * pnt[1] / - * / - * / pnt[2] (up from the plane) - * v - * - * Note: len is invariant upon at_1 <--> at_2 transposition because - * triple product changes sign upon pnt[0]<-->pnt[2] transposition and - * triple product changes sign upon pnt[1]--> -pnt[1] change of direction: - * - * e_triple_prod(pnt[0], pnt[1], pnt[2], NULL ) = - * e_triple_prod(pnt[2], -pnt[1], pnt[0], NULL ) - * - */ - - ret = len >= 0.0? (int)floor(len+0.5) : -(int)floor(0.5-len); - -exit_function: - - return ret; -} - -#ifdef NEVER -/********************************************************************************************/ -int bCanInpAtomBeAStereoCenter( inchi_Atom *at, int cur_at ) -{ - -/************************************************************************************* - * current version - ************************************************************************************* - * Use #define to split the stereocenter description table into parts - * to make it easier to read - * - * --------- 4 single bonds stereocenters ------- - * - * | | | | | | - * -C- -Si- -Ge- -Sn- >As[+] >B[-] - * | | | | | | - */ -#define SZELEM1 "C\000","Si", "Ge", "Sn", "As", "B\000", -#define CCHARGE1 0, 0, 0, 0, 1, -1, -#define CNUMBONDSANDH1 4, 4, 4, 4, 4, 4, -#define CCHEMVALENCEH1 4, 4, 4, 4, 4, 4, -#define CHAS3MEMBRING1 0, 0, 0, 0, 0, 0, -#define CREQUIRDNEIGH1 0, 0, 0, 0, 3, 0, -/* - * --------------- S, Se stereocenters ---------- - * - * | | || | | || - * -S= =S= -S[+] >S[+] -Se= =Se= -Se[+] >Se[+] - * | | | | | | | | - */ -#define SZELEM2 "S\000","S\000","S\000","S\000","Se", "Se", "Se", "Se", -#define CCHARGE2 0, 0, 1, 1, 0, 0, 1, 1, -#define CNUMBONDSANDH2 3, 4, 3, 4, 3, 4, 3, 4, -#define CCHEMVALENCEH2 4, 6, 3, 5, 4, 6, 3, 5, -#define CHAS3MEMBRING2 0, 0, 0, 0, 0, 0, 0, 0, -#define CREQUIRDNEIGH2 3, 3, 3, 3, 3, 3, 3, 3, -/* - * ------------------ N, P stereocenters -------- - * - * X---Y - * | | \ / | | - * =N- >N[+] N >P[+] =P- - * | | | | | - */ -#define SZELEM3 "N\000","N\000","N\000","P\000","P\000", -#define CCHARGE3 0, 1, 0, 1, 0, -#define CNUMBONDSANDH3 4, 4, 3, 4, 4, -#define CCHEMVALENCEH3 5, 4, 3, 4, 5, -#define CHAS3MEMBRING3 0, 0, 1, 0, 0, -#define CREQUIRDNEIGH3 3, 3, 1, 3, 3, - - - - static char szElem[][3]={ SZELEM1 SZELEM2 SZELEM3 }; - static S_CHAR cCharge[]={ CCHARGE1 CCHARGE2 CCHARGE3 }; - static S_CHAR cNumBondsAndH[]={ CNUMBONDSANDH1 CNUMBONDSANDH2 CNUMBONDSANDH3 }; - static S_CHAR cChemValenceH[]={ CCHEMVALENCEH1 CCHEMVALENCEH2 CCHEMVALENCEH3 }; - static S_CHAR cHas3MembRing[]={ CHAS3MEMBRING1 CHAS3MEMBRING2 CHAS3MEMBRING3 }; - static S_CHAR cRequirdNeigh[]={ CREQUIRDNEIGH1 CREQUIRDNEIGH2 CREQUIRDNEIGH3 }; - - static int n = sizeof(szElem)/sizeof(szElem[0]); - /* reqired neighbor types (bitmap): - 0 => check bonds only - 1 => no terminal hydrogen atom neighbors - 2 => no terminal -X and -XH together (don't care the bond type, charge, radical) - (X = tautomeric endpoint atom) - Note: whenever cChemValenceH[] > cNumBondsAndH[] - the tautomeric and/or alternating bonds - are permitted - - */ - int i, ret = 0; - for ( i = 0; i < n; i++ ) { - if ( !strcmp( at[cur_at].elname, szElem[i]) && - at[cur_at].charge == cCharge[i] && - (!at[cur_at].radical || at[cur_at].radical == 1) && - at[cur_at].num_bonds + inchi_NUMH(at,cur_at) == cNumBondsAndH[i] && - at[cur_at].chem_bonds_valence+inchi_NUMH(at,cur_at) == cChemValenceH[i] && - (cHas3MembRing[i]? is_atom_in_3memb_ring( at, cur_at ) : 1) && - e_bInpAtomHasRequirdNeigh ( at, cur_at, cRequirdNeigh[i], cChemValenceH[i]-cNumBondsAndH[i]) ) { - ret = cNumBondsAndH[i]; - break; - } - } - return ret; -} - -#endif /* NEVER */ -/********************************************************************************************/ -int e_bCanInpAtomBeAStereoCenter( int cur_at, S_CHAR *cAtType ) -{ - switch ( cAtType[cur_at] ) { - case AtType_C4 : - case AtType_Si4 : - case AtType_Ge4 : - case AtType_Sn4 : - case AtType_B4m : - case AtType_S6 : - case AtType_S5p : - case AtType_Se6 : - case AtType_Se5p: - case AtType_N5 : - case AtType_N4p : - case AtType_P4p : - case AtType_P5 : - case AtType_As4p: - case AtType_As5 : - return 4; - - case AtType_S3p : - case AtType_Se3p: - case AtType_S4 : - case AtType_Se4 : - case AtType_N3r : - return 3; - } - return 0; -} -/****************************************************************/ -/* used for atoms adjacent to stereogenic bonds only */ -int e_bCanAtomHaveAStereoBond( inchi_Atom *at, int cur_at, S_CHAR *cAtType ) -{ - int i, neigh, nNumFound; - char *p; - static char sNeigh[] = "O;S;Se;Te;"; - switch ( cAtType[cur_at] ) { - case AtType_C3 : - case AtType_Si3: - case AtType_Ge3: - case AtType_Sn3: - case AtType_N3p: - case AtType_N3 : - return 3; - case AtType_Nns: - /* - | - found =N= - - if it has one neighbor =O or =S or =Se or =Te then return 3, otherwise return 1 - - */ - nNumFound = 0; - for ( i = 0; i < at[cur_at].num_bonds; i ++ ) { - if ( at[cur_at].bond_type[i] == INCHI_BOND_TYPE_DOUBLE ) { - neigh = (int)at[cur_at].neighbor[i]; - if ( (p = strstr(sNeigh, at[neigh].elname)) && - ';' == p[strlen(at[neigh].elname)] ) { - nNumFound ++; - } - } - } - return nNumFound==1? 3 : 1; - } - return 0; -} -/****************************************************************/ -int e_bCanAtomBeMiddleAllene( int cur_at, S_CHAR *cAtType ) -{ - switch ( cAtType[cur_at] ) { - case AtType_C2 : - case AtType_Si2: - case AtType_Ge2: - case AtType_Sn2: - return 2; - } - return 0; -} -/****************************************************************/ -int e_bCanAtomBeTerminalAllene( int cur_at, S_CHAR *cAtType ) -{ - switch ( cAtType[cur_at] ) { - case AtType_C3 : - case AtType_Si3: - case AtType_Ge3: - case AtType_Sn3: - return 3; - } - return 0; -} -/*******************************************************************************************/ -int e_FixSb0DParities( inchi_Atom *at, Stereo0D *pStereo, int chain_length, AT_NUM at_middle, - int at_1, int i_next_at_1, S_CHAR z_dir1[], S_CHAR z_dir1NM[], int bOnlyNM1, int bAnomaly1NM, int parity1, int parity1NM, - int at_2, int i_next_at_2, S_CHAR z_dir2[], S_CHAR z_dir2NM[], int bOnlyNM2, int bAnomaly2NM, int parity2, int parity2NM ) -{ - int i_neigh_at_1, i_neigh_at_2, i, j1, j2; - int j_neigh_at_1, j_neigh_at_2, j_next_at_1, j_next_at_2; /* positions after metal removal */ - inchi_Stereo0D *stereo0D = NULL; - int dot_prod_z = 0, dot_prod_zNM = 0, parity, parityNM; - - if ( (!parity1 || !parity2) && (!parity1NM || !parity2NM) ) - return 0; - - if ( ATOM_PARITY_WELL_DEF( parity1 ) && ATOM_PARITY_WELL_DEF( parity2 ) ) { - /* find how the whole bond parity depend on geometry */ - /* if dot_prod_z < 0 then bond_parity := 3-bond_parity */ - /* can be done only for a well-defined geometry */ - /******************************************************************** - * Case of bOnlyNonMetal: stereobond end atoms have valence > 3 - * therefore they will not be recognized until metals have - * been disconnected. Until then the assigned parities will - * not change: ReconcileCmlIncidentBondParities() in the dll - * ignores hypervanlence atoms marked as belonging to a stereobond. - * Explicit H are removed only after metal diconnection. - * - * However, there is a possibility that the normalization - * removes an explicit H thus reducing the valence to acceptable - * value for the bond to be treated as stereogenic. - * In this case a wrong INChI stereolayer for the reconnected - * structure will be produced. - * - * After the metal disconnection ReconcileAllCmlBondParities() - * will be called to reconcile newly appearing stereobonds - ********************************************************************/ - - dot_prod_z = (chain_length%2)? - e_triple_prod_char( at, at_1, i_next_at_1, z_dir1, at_2, i_next_at_2, z_dir2 ) : - e_dot_prodchar3(z_dir1, z_dir2); - - if ( abs(dot_prod_z) < MIN_DOT_PROD ) { - /* The geometry is not well-defined */ - parity1 = parity2 = INCHI_PARITY_UNDEFINED; - dot_prod_z = 0; - } - - } - if ( ATOM_PARITY_WELL_DEF( parity1NM ) && ATOM_PARITY_WELL_DEF( parity2NM ) ) { - /* find how the whole bond parity depend on geometry */ - /* if dot_prod_z < 0 then bond_parity := 3-bond_parity */ - /* can be done only for a well-defined geometry */ - /******************************************************************** - * Case of bOnlyNonMetal: stereobond end atoms have valence > 3 - * therefore they will not be recognized until metals have - * been disconnected. Until then the assigned parities will - * not change: ReconcileCmlIncidentBondParities() in the dll - * ignores hypervanlence atoms marked as belonging to a stereobond. - * Explicit H are removed only after metal diconnection. - * - * However, there is a possibility that the normalization - * removes an explicit H thus reducing the valence to acceptable - * value for the bond to be treated as stereogenic. - * In this case a wrong INChI stereolayer for the reconnected - * structure will be produced. - * - * After the metal disconnection ReconcileAllCmlBondParities() - * will be called to reconcile newly appearing stereobonds - ********************************************************************/ - - dot_prod_zNM = (chain_length%2)? - e_triple_prod_char( at, at_1, i_next_at_1, z_dir1NM, at_2, i_next_at_2, z_dir2NM ) : - e_dot_prodchar3(z_dir1NM, z_dir2NM); - - if ( abs(dot_prod_zNM) < MIN_DOT_PROD ) { - /* The geometry is not well-defined */ - parity1NM = parity2NM = INCHI_PARITY_UNDEFINED; - dot_prod_zNM = 0; - } - - } - - i_neigh_at_1 = i_neigh_at_2 = -1; - j_neigh_at_1 = j_neigh_at_2 = j_next_at_1 = j_next_at_2 = -1; - - /************************************************************************ - * - * The possibly stereogenic bond is defined by atoms at_1, at_2 and - * positions in their adjacency lists i_next_at_1, i_next_at_2. - * - * We need one more neighbor of at_1 and at_2 to establist cis or trans. - * The current API allows only same neighbors for both connected and - * metal-disconnected (or non-metal, NM) structures. Therefore if an - * atom has both metal and non-metal neighbors, select the non-metal one. - * - * In case of bOnlyNM we have to use ordering numbers of the bonds - * counted as if all metals have been disconnected. - * - * In all other cases we use real ordering numbers of the bonds - * to calculate the parities. - * - * The input parities meaning: - * =========================== - * EVEN=2: the atoms in the adjacency list are arranged clockwise as - * seen from the arrowhead of the z_dir vector - * - * The output parities meaning: - * ============================ - * EVEN=2: trans geometry. For more details see the API description. - * - **************************************************************************/ - - if ( bOnlyNM1 || ATOM_PARITY_WELL_DEF( parity1NM ) ) { - /* disconnected metal case */ - for ( i = j1 = 0; i < at[at_1].num_bonds; i ++ ) { - if ( IS_METAL(pStereo->cAtType[at[at_1].neighbor[i]]) ) { - continue; - } - if ( i == i_next_at_1 ) { - j_next_at_1 = j1; /* position of the stereo bond in adjacency list - when metals are disconnected */ - } else - if ( i_neigh_at_1 < 0 ) { - i_neigh_at_1 = i; /* position of the first non-metal neighbor (metal is connected) */ - j_neigh_at_1 = j1; /* position of the first non-metal neighbor (metal is disconnected) */ - } - j1 ++; - } - } else { - /* no metal case */ - j_neigh_at_1 = i_neigh_at_1 = (i_next_at_1==0); - j_next_at_1 = i_next_at_1; - j1 = at[at_1].num_bonds; - } - - if ( bOnlyNM2 || ATOM_PARITY_WELL_DEF( parity2NM ) ) { - for ( i = j2 = 0; i < at[at_2].num_bonds; i ++ ) { - if ( IS_METAL(pStereo->cAtType[at[at_2].neighbor[i]]) ) { - continue; - } - if ( i == i_next_at_2 ) { - j_next_at_2 = j2; - } else - if ( i_neigh_at_2 < 0 ) { - i_neigh_at_2 = i; - j_neigh_at_2 = j2; - } - j2 ++; - } - } else { - j_neigh_at_2 = i_neigh_at_2 = (i_next_at_2==0); - j_next_at_2 = i_next_at_2; - j2 = at[at_2].num_bonds; - } - - if ( i_neigh_at_1 < 0 || i_neigh_at_2 < 0 || - j_neigh_at_1 < 0 || j_neigh_at_2 < 0 || - j_next_at_1 < 0 || j_next_at_2 < 0 ) { - return 0; /* debugging */ - } - if ( parity1 || parity1NM ) { - /* create a new stereo descriptor */ - stereo0D = e_GetNewStereo( pStereo ); - if ( !stereo0D ) { - return -1; /* error */ - } - /* choose the smallest (earliest in the adjacency list) neighbor of at_1 */ - stereo0D->neighbor[0] = at[at_1].neighbor[i_neigh_at_1]; - /* stereibond atoms */ - stereo0D->neighbor[1] = at_1; - stereo0D->neighbor[2] = at_2; - /* choose the smallest neighbor of at_2 */ - stereo0D->neighbor[3] = at[at_2].neighbor[i_neigh_at_2]; - stereo0D->central_atom = at_middle; - stereo0D->type = (chain_length % 2)? INCHI_StereoType_Allene : INCHI_StereoType_DoubleBond; - - if ( dot_prod_z || dot_prod_zNM ) { - - int i_parity1 = i_neigh_at_1 + i_next_at_1 + (i_neigh_at_1 > i_next_at_1); - int j_parity1 = j_neigh_at_1 + j_next_at_1 + (j_neigh_at_1 > j_next_at_1); - int i_parity2 = i_neigh_at_2 + i_next_at_2 + (i_neigh_at_2 > i_next_at_2); - int j_parity2 = j_neigh_at_2 + j_next_at_2 + (j_neigh_at_2 > j_next_at_2); - - /* regular (connected) parity; parity1 and parity2 are well-defined only together */ - if ( ATOM_PARITY_WELL_DEF( parity1 ) ) { - parity = 2 - ( parity1 + parity2 + (dot_prod_z < 0) + - ((bOnlyNM1)? j_parity1 : i_parity1) + - ((bOnlyNM2)? j_parity2 : i_parity2) ) % 2; - } else { - parity = parity1; - } - - - /* disconnected parity; parity1NM and parity2NM are well-defined only together */ - if ( ATOM_PARITY_WELL_DEF( parity1NM ) ) { - parityNM = 2 - ( parity1NM + parity2NM + (dot_prod_zNM < 0) + - ((bOnlyNM1||bAnomaly1NM)? j_parity1 : i_parity1) + - ((bOnlyNM2||bAnomaly2NM)? j_parity2 : i_parity2) ) % 2; - } else { - parityNM = parity1NM; - } - } else { - parity = parity1; - parityNM = parity1NM; - } - stereo0D->parity = parity; - if ( parityNM != parity || bAnomaly1NM || bAnomaly2NM ) { - stereo0D->parity |= (parityNM << SB_PARITY_SHFT); - } - - return stereo0D->parity; - } - return 0; -} -/**************************************************************************/ -int e_nNumNonMetalNeigh( inchi_Atom *atom, int cur_at, Stereo0D *pStereo, int *i_ord_LastMetal ) -{ - int j, nNumNonMetal; - inchi_Atom *at = atom + cur_at; - int val = at->num_bonds; - int atype; - - *i_ord_LastMetal = -1; - nNumNonMetal = 0; - - atype = pStereo->cAtType[cur_at]; - - if ( IS_METAL(atype) ) - return 0; - for ( j = 0; j < val; j ++ ) { - atype = pStereo->cAtType[(int)at->neighbor[j]]; - if ( IS_METAL(atype) ) - *i_ord_LastMetal = j; - else - nNumNonMetal ++; - } - return nNumNonMetal; -} -/*====================================================================================================== - -e_half_stereo_bond_parity() General Description: - - A) find projections of 3 bonds on a reasonable plane defined - by a vector z_dir perpendicular to the plane - B) calculate parity (parity=2[EVEN]: neighbors are clockwise as seen from the arrowhead of z_dir[] vector) - -e_half_stereo_bond_parity() Detailed Description: - - 1) Find at_coord[] = vectors from the central atoms to its neighbors - 2) If only 2 neighbors are present, then create a reasonable 3rd neighbor - (an implicit H or a fictitious atom in case of =NX) coordinates - 3) Normalize at_coord[] to unit length - 4) Find unit vector pnt[2] perpendicular to the plane containing - at_coord[] arrow ends. - Even though it is not necessary, make z-coordinate of pnt[2] positive. - ** pnt[2] has the new z-axis direction ** - 5) Let pnt[0] = perpendicular to pnt[2] component of at_coord[0]; - Normalize pnt[0] to unit length. - ** pnt[0] has the new x-axis direction ** - 6) Let pnt[1] = pnt[2] x pnt[0] (cross-product); - ** pnt[1] has the new y-axis direction ** - 7) Find at_coord[] in the new xyz-basis and normalize their xy-projections - to a unit length - 8) In the new xy-plane find (counterclockwise) angles: - tmp1 = (from at_coord[0] to at_coord[1]) - tmp2 = (from at_coord[0] to at_coord[2]) - 9) Calculate the parity: if tmp1 < tmp2 then 1 (odd) else 2 (even) - (even: looking from the arrow end of the new z-axis, 0, 1, and 2 neighbors - are in clockwise order) - 10) Calculate z_dir = 100*pnt[2]. - - Note1. If z_dir vectors of atoms located at the opposite ends of a double bond have approximately - opposite directions (that is, their dot-product is negative) then the parity of the - stereogenic bond calculated from half-bond-parities should be inverted - - Note2. In case of a tetrahedral cumulene a triple product (z_dir1, (1->2), z_dir2) is used instead - of the dot-product. (1->2) is a vector from the atom#1 to the atom #2. This triple product - is invariant with respect to the atom numbering because it does not change upon (1,2) - permutation. - - Stereo ambiguity in case of 2 neighbors: - ---------------------------------------- - Undefined: single-double bond angle > pi - arcsin(0.03) = 178.28164199834454285275613218975 degrees - Ambiguous: single-double bond angle > 175 degrees = pi - 0.087156 Rad - - Return values - (cases: I=only in case of isotopic H atoms the neighbors are different, - N=in case of non-isotopic H atoms the neighbors are different) - - -4 = INCHI_PARITY_UNDEFINED => atom is adjacent to a stereogenic bond, but the geometry is undefined, I - -3 = INCHI_PARITY_UNKNOWN => atom is adjacent to a stereogenic bond, but the geometry is not known to the iuser, I - -2 =-INCHI_PARITY_EVEN => parity of an atom adjacent to a stereogenic bond, I - -1 =-INCHI_PARITY_ODD => parity of an atom adjacent to a stereogenic bond, I - 0 = INCHI_PARITY_NONE => the atom is not adjacent to a stereogenic bond - 1 = INCHI_PARITY_ODD => parity of an atom adjacent to a stereogenic bond, N&I - 2 = INCHI_PARITY_EVEN => parity of an atom adjacent to a stereogenic bond, N&I - 3 = INCHI_PARITY_UNKNOWN => atom is adjacent to a stereogenic bond, but the geometry is not known to the iuser, N&I - 4 = INCHI_PARITY_UNDEFINED => atom is adjacent to a stereogenic bond, but the geometry is undefined, N&I - - -=====================================================================================================*/ - -int e_half_stereo_bond_parity( inchi_Atom *at, int cur_at, S_CHAR *z_dir, int *bOnlyNonMetal, - int bPointedEdgeStereo, Stereo0D *pStereo ) -{ - double at_coord[MAX_NUM_STEREO_BOND_NEIGH][3], c, s, tmp[3], tmp1, tmp2, min_tmp, max_tmp, z; - double temp[3], pnt[3][3]; - int j, k, p0, p1, p2, next, num_z, nType, num_either_single, num_either_double; - int nNumExplictAttachments; - int bond_parity = INCHI_PARITY_UNDEFINED; - int num_H=0, num_eH=0, num_nH=0 /* = num_iso_H[0] */; - const double one_pi = 2.0*atan2(1.0 /* y */, 0.0 /* x */); - const double two_pi = 2.0*one_pi; - AT_NUM nSbNeighOrigAtNumb[MAX_NUM_STEREO_BOND_NEIGH]; - int bAmbiguousStereo = 0; - S_CHAR num_iso_eH[NUM_H_ISOTOPES+1]; /* count explicit H */ - - if ( z_dir && !z_dir[0] && !z_dir[1] && !z_dir[2] ) { - z_dir[2]=100; - } - /* *bOnlyNonMetal = 0; */ - if ( at[cur_at].num_bonds > MAX_NUM_STEREO_BOND_NEIGH || *bOnlyNonMetal ) { - int i_ord_LastMetal; - int nNumNonMetal = e_nNumNonMetalNeigh( at, cur_at, pStereo, &i_ord_LastMetal ); - if ( nNumNonMetal <= MAX_NUM_STEREO_BOND_NEIGH ) { - *bOnlyNonMetal = 1; - if ( 1 == nNumNonMetal ) { - return bond_parity; /* INCHI_PARITY_UNDEFINED */ - } - if ( 0 == nNumNonMetal ) { - return INCHI_PARITY_NONE; - } - } else { - return INCHI_PARITY_NONE; - } - } - memset(num_iso_eH, 0, sizeof(num_iso_eH)); - for ( j = 0; j < at[cur_at].num_bonds; j ++ ) { - next = at[cur_at].neighbor[j]; - switch( pStereo->cAtType[next] ) { - case AtType_TermH: - if ( 0 <= at[next].isotopic_mass && at[next].isotopic_mass <= NUM_H_ISOTOPES ) { - num_iso_eH[at[next].isotopic_mass] ++; - } else { - num_iso_eH[0] ++; - } - num_eH ++; - break; - case AtType_TermD: - num_iso_eH[2] ++; - num_eH ++; - break; - case AtType_TermT: - num_iso_eH[3] ++; - num_eH ++; - break; - } - } - - - num_H = inchi_NUMH2(at,cur_at) + num_eH; - if ( num_H > NUM_H_ISOTOPES ) - return 0; /* at least 2 H atoms are isotopically identical */ - - for ( j = 0, num_nH = num_H; j < NUM_H_ISOTOPES; j ++ ) { - if ( (k = (int)at[cur_at].num_iso_H[j+1]+(int)num_iso_eH[j+1]) > 1 ) { - return INCHI_PARITY_NONE; /* two or more identical isotopic H atoms */ - } - num_nH -= k; - } - /* at this point num_nH = number of non-isotopic H atoms */ - if ( num_nH > 1 ) - return INCHI_PARITY_NONE; /* two or more identical non-isotopic H atoms */ - if ( num_nH < 0 ) - return CT_ISO_H_ERR; /* program error */ /* */ - - /******************************************************************** - * Note. At this point all (implicit and explicit) isotopic - * terminal H neighbors are either different or not present. - ********************************************************************/ - - /* store neighbors coordinates */ - num_z = num_either_single = num_either_double = 0; - nNumExplictAttachments = 0; - for ( j = 0; j < at[cur_at].num_bonds; j ++ ) { - next = at[cur_at].neighbor[j]; - if ( *bOnlyNonMetal && IS_METAL(pStereo->cAtType[next]) ) - continue; - at_coord[nNumExplictAttachments][0] = at[next].x - at[cur_at].x; - at_coord[nNumExplictAttachments][1] = at[next].y - at[cur_at].y; - nSbNeighOrigAtNumb[nNumExplictAttachments] = next; - - z = e_get_z_coord( at, cur_at, j /*neighbor #*/, &nType, bPointedEdgeStereo ); - switch ( nType ) { - case ZTYPE_EITHER: - num_either_single ++; /* bond in "Either" direction. */ - break; - case ZTYPE_UP: - case ZTYPE_DOWN: - z = e_len2( at_coord[nNumExplictAttachments] ); - /* - z = sqrt( at_coord[nNumExplictAttachments][0]*at_coord[nNumExplictAttachments][0] - + at_coord[nNumExplictAttachments][1]*at_coord[nNumExplictAttachments][1] ); - */ - if ( nType == ZTYPE_DOWN ) - z = -z; - /* no break; here */ - case ZTYPE_3D: - num_z ++; - } - at_coord[nNumExplictAttachments][2] = z; - nNumExplictAttachments ++; - } - - if ( num_either_single ) { - bond_parity = INCHI_PARITY_UNKNOWN; /* single bond is 'unknown' */ - goto exit_function; - } - - /* nNumExplictAttachments is a total number of attachments */ - if ( nNumExplictAttachments == 2 ) { - /* create coordinates of the implicit hydrogen (or a fictitious atom in case of ==N-X ), */ - /* coord[2][], attached to the cur_at. */ - for ( j = 0; j < 3; j ++ ) { - at_coord[2][j] = - ( at_coord[0][j] + at_coord[1][j] ); - } - nSbNeighOrigAtNumb[nNumExplictAttachments] = -1; /* implicit H or lone pair */ - } - for ( j = 0; j < 3; j ++ ) { - tmp[j] = e_len3( at_coord[j] ); - } - min_tmp = inchi_min( tmp[0], inchi_min(tmp[1], tmp[2]) ); - max_tmp = inchi_max( tmp[0], inchi_max(tmp[1], tmp[2]) ); - if ( min_tmp < MIN_BOND_LEN || min_tmp < MIN_SINE*max_tmp ) { - /* all bonds or some of bonds are too short */ - goto exit_function; - } - /* normalize lengths to 1 */ - for ( j = 0; j < 3; j ++ ) { - e_mult3( at_coord[j], 1.0/tmp[j], at_coord[j] ); - } - - /* find projections of at_coord vector differences on the plane containing their arrowhead ends */ - for ( j = 0; j < 3; j ++ ) { - /* pnt[0..2] = {0-1, 1-2, 2-0} */ - tmp[j] = e_len3(e_diff3( at_coord[j], at_coord[(j+1)%3], pnt[j] )); - if ( tmp[j] < MIN_SINE ) { - goto exit_function; /* angle #i-cur_at-#j is too small */ - } - e_mult3( pnt[j], 1.0/tmp[j], pnt[j] ); /* 2003-10-06 */ - } - /* find pnt[p2], a vector perpendicular to the plane, and its length tmp[p2] */ - /* replace previous pnt[p2], tmp[p2] with new values; the old values do not have any additional */ - /* information because pnt[p0]+pnt[p1]+pnt[p2]=0 */ - /* 10-6-2003: a cross-product of one pair pnt[j], pnt[(j+1)%3] can be very small. Find the larges one */ - tmp1 = e_len3( e_cross_prod3( pnt[0], pnt[1], temp ) ); - for (j = 1, k = 0; j < 3; j ++ ) { - tmp2 = e_len3( e_cross_prod3( pnt[j], pnt[(j+1)%3], temp ) ); - if ( tmp2 > tmp1 ) { - tmp1 = tmp2; - k = j; - } - } - /* previously p0=0, p1=1, p2=2 */ - p0 = k; - p1 = (k+1)%3; - p2 = (k+2)%3; - tmp[p2] = e_len3( e_cross_prod3( pnt[p0], pnt[p1], pnt[p2] ) ); - if ( tmp[p2] < MIN_SINE*tmp[p0]*tmp[p1] ) { - goto exit_function; /* pnt[p0] is almost colinear to pnt[p1] */ - } - /* new basis: pnt[p0], pnt[p1], pnt[p2]; set z-coord sign and make abs(pnt[p2]) = 1 */ - e_mult3( pnt[p2], (pnt[p2][2]>0.0? 1.0:-1.0)/tmp[p2], pnt[p2] ); /* unit vector in the new z-axis direction */ - - min_tmp = e_dot_prod3( at_coord[0], pnt[p2] ); /* non-planarity measure (sine): hight of at_coord[] pyramid */ - e_mult3( pnt[p2], min_tmp, pnt[p0] ); /* vector height of the pyramid, ideally 0 */ - /* find new pnt[p0] = projection of at_coord[p0] on plane orthogonal to pnt[p2] */ - tmp[p0] = e_len3(e_diff3( at_coord[0], pnt[p0], pnt[p0] )); - e_mult3( pnt[p0], 1.0/tmp[p0], pnt[p0] ); /* new x axis basis vector */ - e_cross_prod3( pnt[p2], pnt[p0], pnt[p1] ); /* new y axis basis vector */ - /* find at_coord in the new basis of {pnt[p0], pnt[p1], pnt[p2]} */ - for ( j = 0; j < 3; j ++ ) { - e_copy3( at_coord[j], temp ); - for ( k = 0; k < 3; k ++ ) { - at_coord[j][k] = e_dot_prod3( temp, pnt[(k+p0)%3] ); - } - /* new xy plane projection length */ - tmp[j] = sqrt(at_coord[j][0]*at_coord[j][0] + at_coord[j][1]*at_coord[j][1]); - /* make new xy plane projection length = 1 */ - e_mult3( at_coord[j], 1.0/tmp[j], at_coord[j] ); - } - - s = fabs( at_coord[1][0]*at_coord[2][1] - at_coord[1][1]*at_coord[2][0] ); /* 1-2 sine */ - c = at_coord[1][0]*at_coord[2][0] + at_coord[1][1]*at_coord[2][1]; /* 1-2 cosine */ - if ( s < MIN_SINE && c > 0.5 ) { - goto exit_function; /* bonds to neigh. 1 and 2 have almost same direction; relative angles are undefined */ - } - c = at_coord[0][0]; /* cosine of the angle between new Ox axis and a bond to the neighbor 0. Should be 1 */ - s = at_coord[0][1]; /* sine. Should be 0 */ - /* turn vectors so that vector #1 (at_coord[0]) becomes {1, 0} */ - for ( j = 0; j < MAX_NUM_STEREO_BOND_NEIGH; j ++ ) { - tmp1 = c*at_coord[j][0] + s*at_coord[j][1]; - tmp2 = -s*at_coord[j][0] + c*at_coord[j][1]; - at_coord[j][0] = tmp1; - at_coord[j][1] = tmp2; - } - /* counterclockwise angles from the direction to neigh 0 to to directions to neighbors 1 and 2: */ - tmp1 = atan2( at_coord[1][1], at_coord[1][0] ); /* range -pi and +pi */ - tmp2 = atan2( at_coord[2][1], at_coord[2][0] ); - if ( tmp1 < 0.0 ) - tmp1 += two_pi; /* range 0 to 2*pi */ - if ( tmp2 < 0.0 ) - tmp2 += two_pi; - /*----------------------------------- - Example - 1 \ case tmp1 < tmp2 - \ parity is odd - \ (counterclockwise) - A------- 0 - / - / - 2 / - - ------------------------------------*/ - bond_parity = 2 - ( tmp1 < tmp2 ); - for ( j = 0; j < 3; j ++ ) { - z_dir[j] = (S_CHAR)(pnt[p2][j]>= 0.0? floor(0.5 + 100.0 * pnt[p2][j]) : - -floor(0.5 - 100.0 * pnt[p2][j])); /* abs(z_dir) = 100 */ - } - /* check for ambiguity */ - if ( nNumExplictAttachments > 2 ) { - min_tmp = inchi_min( tmp1, tmp2 ); - max_tmp = inchi_max( tmp1, tmp2 ); - if ( min_tmp > one_pi-MIN_SINE || max_tmp < one_pi+MIN_SINE || max_tmp-min_tmp > one_pi - MIN_SINE ) { - bAmbiguousStereo |= AMBIGUOUS_STEREO; - } else /* 3D ambiguity 8-28-2002 */ - if ( fabs(at_coord[0][2]) > MAX_SINE ) { /* all fabs(at_coord[j][2] (j=0..2) must be equal */ - bAmbiguousStereo |= AMBIGUOUS_STEREO; - } - } else - if ( nNumExplictAttachments == 2 ) { /* 10-6-2003: added */ - min_tmp = fabs(tmp1 - one_pi); - if ( min_tmp < MIN_SINE ) { - bond_parity = INCHI_PARITY_UNDEFINED; /* consider as undefined 10-6-2003 */ - } else - if ( min_tmp < MIN_ANGLE_DBOND ) { - bAmbiguousStereo |= AMBIGUOUS_STEREO; - } - } - - /* for 3 neighbors moving implicit H to the index=0 from index=2 position */ - /* can be done in 2 transpositions and does not change atom's parity */ - -exit_function: - return bond_parity; -} - -#define MAX_ALLENE_LEN 20 -/*************************************************************/ -int e_set_stereo_bonds_parity( Stereo0D *pStereo, inchi_Atom *at, int at_1, int bPointedEdgeStereo ) -{ - int j, k, next_at_1, i_next_at_1, i_next_at_2, at_2, next_at_2, num_stereo_bonds, bFound, bAllene; - int bond_type, num_2s_1, num_alt_1, bNxtStereobond, bCurStereobond, bNxtCumulene; - int num_stored_stereo_bonds, num_stored_isotopic_stereo_bonds; - int chain_length, num_chains, cur_chain_length; - int all_at_2[MAX_NUM_STEREO_BONDS]; - int all_pos_1[MAX_NUM_STEREO_BONDS], all_pos_2[MAX_NUM_STEREO_BONDS]; - S_CHAR all_unkn[MAX_NUM_STEREO_BONDS], all_chain_len[MAX_NUM_STEREO_BONDS]; - int all_middle_at[MAX_NUM_STEREO_BONDS]; - AT_NUM chain_atoms[MAX_ALLENE_LEN], at_middle; - int nUnknown; - int bOnlyNM1, bOnlyNM2; - - bCurStereobond = e_bCanAtomHaveAStereoBond( at, at_1, pStereo->cAtType ); - if ( !bCurStereobond ) - return 0; - - /* count bonds and find the second atom on the stereo bond */ - num_2s_1 = num_alt_1 = 0; - chain_length = 0; - num_chains = 0; - for ( i_next_at_1 = 0, num_stereo_bonds = 0; i_next_at_1 < at[at_1].num_bonds; i_next_at_1 ++ ) { - - at_2 = next_at_1 = at[at_1].neighbor[i_next_at_1]; - bond_type = at[at_1].bond_type[i_next_at_1]; - nUnknown = (at[at_1].bond_stereo[i_next_at_1] == INCHI_PARITY_UNKNOWN); - next_at_2 = at_1; - - bNxtStereobond = e_bCanAtomHaveAStereoBond( at, at_2, pStereo->cAtType ); - bNxtCumulene = 0; - cur_chain_length = 0; - - if ( bNxtStereobond + bCurStereobond > 3 ) { - /* this includes single (ring) bonds in -NH-CH=N(+)< that may be deprotonated to -N=C-N< */ - if ( bond_type == INCHI_BOND_TYPE_TRIPLE ) { - continue; - } - /* possibly stereogenic bond */ - } else { - bNxtCumulene = - e_bCanAtomBeMiddleAllene(at_2, pStereo->cAtType ) && - e_bCanAtomBeTerminalAllene( at_1, pStereo->cAtType ); - if ( !bNxtCumulene ) { - continue; - } - if ( bond_type != INCHI_BOND_TYPE_DOUBLE ) { - continue; - } - /* possibly allene or cumulenr */ - chain_atoms[cur_chain_length] = at_1; - chain_atoms[cur_chain_length+1] = at_2; - - /* - * Example of cumulene - * chain length = 2: >X=C=C=Y< - * | | | | - * 1st cumulene atom= at_1 | | at_2 =last cumlene chain atom - * next to at_1= next_at_1 next_at_2 =previous to at_2 - * - * chain length odd: stereocenter on the middle atom ( 1=> allene ) - * chain length even: "long stereogenic bond" - */ - while ( cur_chain_length < MAX_ALLENE_LEN-3 && - (bAllene = - e_bCanAtomBeMiddleAllene( at_2, pStereo->cAtType ))) { - k = ((int)at[at_2].neighbor[0]==next_at_2); /* opposite neighbor position */ - next_at_2 = at_2; - nUnknown += (at[at_2].bond_stereo[k] == INCHI_BOND_STEREO_DOUBLE_EITHER); - at_2 = (int)at[at_2].neighbor[k]; - cur_chain_length ++; /* count =C= atoms */ - chain_atoms[cur_chain_length+1] = at_2; - } - if ( cur_chain_length ) { - if ( bAllene /* at the end of the chain atom Y is =Y=, not =Y< or =Y- */ || - !e_bCanAtomBeTerminalAllene( at_2, pStereo->cAtType)) { - cur_chain_length = 0; - continue; /* ignore: does not fit cumulene description; go to check next at_1 neighbor */ - } - } - if ( !cur_chain_length && - !e_bCanAtomHaveAStereoBond( at, at_2, pStereo->cAtType ) ) { - continue; /* reject non-stereogenic bond to neighbor #i_next_at_1 */ - } - } - - /* check atom at the opposite end of possibly stereogenic bond */ - - bFound = ( at_1 > at_2 ); /* i_next_at_1 = at_1 stereogenic bond neighbor attachment number */ - - if ( bFound ) { - i_next_at_2 = -1; /* unassigned mark */ - for ( j = 0; j < at[at_2].num_bonds; j ++ ) { - if ( (int)at[at_2].neighbor[j] == next_at_2 ) { - i_next_at_2 = j; /* assigned */ - break; - } - } - if ( i_next_at_2 < 0 ) { - continue; - } - /* store the results */ - all_pos_1[num_stereo_bonds] = i_next_at_1; /* neighbor to at_1 position */ - all_pos_2[num_stereo_bonds] = i_next_at_2; /* neighbor to at_2 position */ - all_at_2[num_stereo_bonds] = at_2; /* at_2 */ - all_unkn[num_stereo_bonds] = nUnknown; /* stereogenic bond has Unknown configuration */ - /* allene/cumulene stuff */ - all_chain_len[num_stereo_bonds] = cur_chain_length; - all_middle_at[num_stereo_bonds] = (cur_chain_length%2)? chain_atoms[(cur_chain_length+1)/2] : -1; - num_stereo_bonds ++; - } - } - if ( !num_stereo_bonds || num_stereo_bonds > 3 /*|| num_chains > 1*/ ) { - return 0; /* At the end, we cannot be more than 1 cumulene chain. */ - } - - /* ================== calculate parities ====================== */ - /* find possibly stereo bonds and save them */ - num_stored_isotopic_stereo_bonds = 0; - num_stored_stereo_bonds = 0; - for ( k = 0; k < num_stereo_bonds; k ++ ) { - /* NM stands for non-metal */ - int parity_at_1, parity_at_2, parity_at_1NM=0, parity_at_2NM=0, bOnlyNM; - S_CHAR z_dir1[3], z_dir2[3], z_dir1NM[3], z_dir2NM[3]; /* 3D vectors for half stereo bond parity direction */ - int i_ord_LastMetal1, i_ord_LastMetal2, bAnomaly1NM, bAnomaly2NM; - - at_2 = all_at_2[k]; - i_next_at_1 = all_pos_1[k]; - i_next_at_2 = all_pos_2[k]; - nUnknown = all_unkn[k]; - at_middle = all_middle_at[k]; - cur_chain_length = all_chain_len[k]; - memset(z_dir1, 0, sizeof(z_dir1)); - memset(z_dir2, 0, sizeof(z_dir2)); - memset(z_dir1NM, 0, sizeof(z_dir1NM)); - memset(z_dir2NM, 0, sizeof(z_dir2NM)); - bOnlyNM1 = bOnlyNM2 = 0; - bAnomaly1NM = bAnomaly2NM = 0; - i_ord_LastMetal1 = i_ord_LastMetal2 = -1; - - if ( nUnknown ) { - parity_at_1 = parity_at_2 = parity_at_1NM = parity_at_2NM = INCHI_PARITY_UNKNOWN; - } else { - /*********************************************************************** - * - * Obtain each atom parity from the geometry - * - * Warning: case when dot-product - * (z_dir1,z_dir1NM) < 0 or (z_dir2,z_dir2NM) < 0 - * is not treated proprly. - * This case may happen especially when z_dir?[3] << 100 - * - ***********************************************************************/ - - parity_at_1 = e_half_stereo_bond_parity( at, at_1, z_dir1, &bOnlyNM1, bPointedEdgeStereo, pStereo ); - parity_at_2 = e_half_stereo_bond_parity( at, at_2, z_dir2, &bOnlyNM2, bPointedEdgeStereo, pStereo ); - - if ( RETURNED_ERROR(parity_at_1) || RETURNED_ERROR(parity_at_2) ) { - return CT_CALC_STEREO_ERR; - } - parity_at_1 = abs(parity_at_1); - parity_at_2 = abs(parity_at_2); - /* bond parities for the disconnected structure */ - if ( bOnlyNM1 ) { - parity_at_1NM = parity_at_1; - memcpy( z_dir1NM, z_dir1, sizeof(z_dir1NM) ); - parity_at_1 = INCHI_PARITY_NONE; - } else - if ( at[at_1].num_bonds == e_nNumNonMetalNeigh( at, at_1, pStereo, &i_ord_LastMetal1 ) ) { - parity_at_1NM = parity_at_1; /* no metal bond present */ - memcpy( z_dir1NM, z_dir1, sizeof(z_dir1NM) ); - } else { - bOnlyNM = 1; /* at_1 has a metal neighbor; in adjacency list - it is located in (zero-based) position i_ord_LastMetal1 */ - parity_at_1NM = e_half_stereo_bond_parity( at, at_1, z_dir1NM, &bOnlyNM, - bPointedEdgeStereo, pStereo ); - if ( RETURNED_ERROR(parity_at_1NM) ) { - return CT_CALC_STEREO_ERR; - } - parity_at_1NM = abs(parity_at_1NM); - /* - if ( ATOM_PARITY_WELL_DEF(parity_at_1NM) && !ATOM_PARITY_WELL_DEF(parity_at_1) ) { - memcpy( z_dir1, z_dir1NM, sizeof(z_dir1) ); - } - */ - if ( ATOM_PARITY_WELL_DEF(parity_at_1NM) && ATOM_PARITY_WELL_DEF(parity_at_1) && - 0 <= i_ord_LastMetal1 ) { - if ( 1 == i_ord_LastMetal1 % 2 && parity_at_1NM == parity_at_1 || - 0 == i_ord_LastMetal1 % 2 && parity_at_1NM != parity_at_1 ) { - /* abnormal geometry: all bonds in a sector < 180 degrees */ - /*parity_at_1NM = 3 - parity_at_1;*/ - bAnomaly1NM = 1; - } else { - parity_at_1 = parity_at_1NM; - bOnlyNM1 = 1; - } - } else - if ( ATOM_PARITY_WELL_DEF(parity_at_1NM) && !ATOM_PARITY_WELL_DEF(parity_at_1) && - 0 <= i_ord_LastMetal1 ) { - bAnomaly1NM = 1; /* 2005-02-01 force atom parity from non-metal neighbors */ - } - - } - - if ( bOnlyNM2 ) { - parity_at_2NM = parity_at_2; - memcpy( z_dir2NM, z_dir2, sizeof(z_dir2NM) ); - parity_at_2 = INCHI_PARITY_NONE; - } else - if ( at[at_2].num_bonds == e_nNumNonMetalNeigh( at, at_2, pStereo, &i_ord_LastMetal2 ) ) { - memcpy( z_dir2NM, z_dir2, sizeof(z_dir2NM) ); - parity_at_2NM = parity_at_2; /* no metal bond present */ - } else { - bOnlyNM = 1; - parity_at_2NM = e_half_stereo_bond_parity( at, at_2, z_dir2NM, &bOnlyNM, - bPointedEdgeStereo, pStereo ); - if ( RETURNED_ERROR(parity_at_2NM) ) { - return CT_CALC_STEREO_ERR; - } - parity_at_2NM = abs(parity_at_2NM); - if ( ATOM_PARITY_WELL_DEF(parity_at_2NM) && ATOM_PARITY_WELL_DEF(parity_at_2) && - 0 <= i_ord_LastMetal2 ) { - if ( 1 == i_ord_LastMetal2 % 2 && parity_at_2NM == parity_at_2 || - 0 == i_ord_LastMetal2 % 2 && parity_at_2NM != parity_at_2 ) { - /* abnormal geometry: all bonds in a sector < 180 degrees */ - /*parity_at_2NM = 3 - parity_at_2;*/ - bAnomaly2NM = 1; - } else { - parity_at_2 = parity_at_2NM; - bOnlyNM2 = 1; - } - } else - if ( ATOM_PARITY_WELL_DEF(parity_at_2NM) && !ATOM_PARITY_WELL_DEF(parity_at_2) && - 0 <= i_ord_LastMetal2 ) { - bAnomaly2NM = 1; /* 2005-02-01 force atom parity from non-metal neighbors */ - } - } - /* make both atoms have non-metal neighbors only */ - if ( (bOnlyNM1 || bAnomaly1NM) && ATOM_PARITY_WELL_DEF(parity_at_1NM) && - !(bOnlyNM2 || bAnomaly2NM) && ATOM_PARITY_WELL_DEF(parity_at_2NM) && - 0 <= i_ord_LastMetal2 ) { - - bAnomaly2NM = 1; - parity_at_2NM = 2 - (parity_at_2NM + i_ord_LastMetal2) % 2; - } else - if ( !(bOnlyNM1 || bAnomaly1NM) && ATOM_PARITY_WELL_DEF(parity_at_1NM) && - (bOnlyNM2 || bAnomaly2NM) && ATOM_PARITY_WELL_DEF(parity_at_2NM) && - 0 <= i_ord_LastMetal1 ) { - - bAnomaly1NM = 1; - parity_at_1NM = 2 - (parity_at_1NM + i_ord_LastMetal1) % 2; - } - - /* consistency */ - if ( parity_at_1 == INCHI_PARITY_NONE || parity_at_2 == INCHI_PARITY_NONE ) { - parity_at_1 = parity_at_2 = INCHI_PARITY_NONE; /* =zero */ - } else { - switch( !ATOM_PARITY_WELL_DEF( parity_at_1 ) + 2*!ATOM_PARITY_WELL_DEF( parity_at_2 ) ) { - case 1: - parity_at_2 = parity_at_1; /* set both to not-well-def */ - break; - case 2: - parity_at_1 = parity_at_2; /* set both to not-well-def */ - break; - case 3: - parity_at_1 = parity_at_2 = inchi_min(parity_at_1, parity_at_2); - break; - } - } - - if ( parity_at_1NM == INCHI_PARITY_NONE || parity_at_2NM == INCHI_PARITY_NONE ) { - parity_at_1NM = parity_at_2NM = INCHI_PARITY_NONE; /* =zero */ - } else { - switch( !ATOM_PARITY_WELL_DEF( parity_at_1NM ) + 2*!ATOM_PARITY_WELL_DEF( parity_at_2NM ) ) { - case 1: - parity_at_2NM = parity_at_1NM; /* set both to not-well-def */ - break; - case 2: - parity_at_1NM = parity_at_2NM; /* set both to not-well-def */ - break; - case 3: - parity_at_1NM = parity_at_2NM = inchi_min(parity_at_1NM, parity_at_2NM); - break; - } - } - if ( parity_at_1 != INCHI_PARITY_NONE && parity_at_1NM == INCHI_PARITY_NONE ) { - parity_at_1NM = parity_at_2NM = INCHI_PARITY_UNDEFINED; - } else - if ( parity_at_1 == INCHI_PARITY_NONE && parity_at_1NM != INCHI_PARITY_NONE ) { - parity_at_1 = parity_at_2 = INCHI_PARITY_UNDEFINED; - } - - - - if ( (parity_at_1 == INCHI_PARITY_UNDEFINED || parity_at_1 == INCHI_PARITY_NONE) && - (parity_at_1NM == INCHI_PARITY_UNDEFINED || parity_at_1NM == INCHI_PARITY_NONE) ) { - continue; - } - } - parity_at_1 = e_FixSb0DParities( at, pStereo, cur_chain_length, at_middle, - at_1, i_next_at_1, z_dir1, z_dir1NM, bOnlyNM1, bAnomaly1NM, parity_at_1, parity_at_1NM, - at_2, i_next_at_2, z_dir2, z_dir2NM, bOnlyNM2, bAnomaly2NM, parity_at_2, parity_at_2NM); - num_stored_stereo_bonds += (parity_at_1 != 0); - - } - return num_stored_stereo_bonds; -} - - -/*************************************************************** - * Get stereo atom parity for the current order of attachments - * The result in at[cur_at].parity is valid for previously removed - * explicit hydrogen atoms, including isotopic ones, that are located in at_removed_H[] - * The return value is a calculated parity. - */ -#define ADD_EXPLICIT_HYDROGEN_NEIGH 1 -#define ADD_EXPLICIT_LONE_PAIR_NEIGH 2 -int e_set_stereo_atom_parity( Stereo0D *pStereo, inchi_Atom *at, int cur_at, int bPointedEdgeStereo ) -{ - int j, k, next_at, num_z, j1, nType, num_eH, num_iH, tot_num_iso_H, nMustHaveNumNeigh, bAmbiguousStereo; - double z, sum_xyz[3], min_sine, triple_product; - double at_coord[MAX_NUM_STEREO_ATOM_NEIGH][3]; - double bond_len_xy[4], rmax, rmin; - double at_coord_center[3]; - int parity, out_parity, out_stereo_atom_parity, bAmbiguous = 0, bAddExplicitNeighbor = 0; - int b2D = 0, n2DTetrahedralAmbiguity = 0; - AT_NUM nSbNeighOrigAtNumb[MAX_NUM_STEREO_ATOM_NEIGH]; - S_CHAR num_iso_eH[NUM_H_ISOTOPES+1]; /* count explicit H */ - int bFixSp3Bug = 1; /* 1=> FixSp3Bug option for stereo bonds longer than 20 */ - - out_parity = out_stereo_atom_parity = INCHI_PARITY_NONE; - parity = INCHI_PARITY_NONE; - bAmbiguousStereo = 0; - memset(num_iso_eH, 0, sizeof(num_iso_eH)); - num_eH = 0; /* number of explicit H -- will be found later */ - num_iH = inchi_NUMH2(at,cur_at); /* implicit H */ - - if ( !(nMustHaveNumNeigh = e_bCanInpAtomBeAStereoCenter( cur_at, pStereo->cAtType ) ) || - num_iH > NUM_H_ISOTOPES - ) { - goto exit_function; - } - /* find explicit terminal H */ - memset(num_iso_eH, 0, sizeof(num_iso_eH)); - for ( j = 0; j < at[cur_at].num_bonds; j ++ ) { - next_at = at[cur_at].neighbor[j]; - switch(pStereo->cAtType[next_at] ) { - case AtType_TermH: - if ( 0 <= at[next_at].isotopic_mass && at[next_at].isotopic_mass <= NUM_H_ISOTOPES ) { - num_iso_eH[at[next_at].isotopic_mass] ++; - } else { - num_iso_eH[0] ++; - } - num_eH ++; - break; - case AtType_TermD: - num_iso_eH[2] ++; - num_eH ++; - break; - case AtType_TermT: - num_iso_eH[3] ++; - num_eH ++; - break; - } - } - - /* numbers of isotopic H atoms */ - for ( j = 0, tot_num_iso_H = 0; j < NUM_H_ISOTOPES; j ++ ) { - if ( at[cur_at].num_iso_H[j+1] + num_iso_eH[j+1] > 1 ) { - goto exit_function; /* two or more identical hydrogen isotopic neighbors */ - } - tot_num_iso_H += at[cur_at].num_iso_H[j+1] + num_iso_eH[j+1]; - } - - /* number of non-isotopic H atoms */ - if ( inchi_NUMH2(at,cur_at) + num_eH - tot_num_iso_H > 1 ) { - goto exit_function; /* two or more identical hydrogen non-isotopic neighbors */ - } - - /* coordinates initialization */ - num_z = 0; - sum_xyz[0] = sum_xyz[1] = sum_xyz[2] = 0.0; - - at_coord_center[0] = - at_coord_center[1] = - at_coord_center[2] = 0.0; - - /* fill out stereo center neighbors coordinates */ - /* and obtain the parity from the geometry */ - - /* add all coordinates of other neighboring atoms */ - for ( j = j1 = 0; j < at[cur_at].num_bonds; j ++, j1 ++ ) { - next_at = at[cur_at].neighbor[j]; - z = e_get_z_coord( at, cur_at, j, &nType, bPointedEdgeStereo ); - switch ( nType ) { - case ZTYPE_EITHER: - parity = INCHI_PARITY_UNKNOWN; /* unknown parity: bond in "Either" direction. */ - goto exit_function; - case ZTYPE_UP: - case ZTYPE_DOWN: - b2D ++; - case ZTYPE_3D: - num_z ++; - } - - nSbNeighOrigAtNumb[j1] = next_at+1; - at_coord[j1][0] = at[next_at].x-at[cur_at].x; - at_coord[j1][1] = at[next_at].y-at[cur_at].y; - bond_len_xy[j1] = e_len2(at_coord[j1]); - at_coord[j1][2] = (nType==ZTYPE_3D? z : - nType==ZTYPE_UP? bond_len_xy[j1] : - nType==ZTYPE_DOWN? -bond_len_xy[j1] : 0.0 ); - } - /* j1 is the number of explicit neighbors (that is, all neighbors except implicit H) */ - - b2D = (b2D == num_z && num_z); /* 1 => two-dimensional */ - - if ( MAX_NUM_STEREO_ATOM_NEIGH != at[cur_at].num_bonds+num_iH && - MAX_NUM_STEREO_ATOM_NEIGH-1 != at[cur_at].num_bonds+num_iH ) { - /* not enough geometry data to find the central atom parity */ - goto exit_function; - } - /* make all vector lengths equal to 1; exit if too short. 9-10-2002 */ - for ( j = 0; j < j1; j ++ ) { - z = e_len3( at_coord[j] ); - if ( z < MIN_BOND_LEN ) { - parity = INCHI_PARITY_UNDEFINED; - goto exit_function; - } -#if( STEREO_CENTER_BONDS_NORM == 1 ) - else { - e_mult3( at_coord[j], 1.0/z, at_coord[j] ); - } -#endif - rmax = j? inchi_max( rmax, z) : z; - rmin = j? inchi_min( rmin, z) : z; - } - if ( rmin / rmax < MIN_SINE ) { - /* bond ratio is too small */ - parity = INCHI_PARITY_UNDEFINED; - goto exit_function; - } - for ( j = 0; j < j1; j ++ ) { - e_add3( sum_xyz, at_coord[j], sum_xyz ); - } - - - - /* here j1 is a number of neighbors including explicit terminal isotopic H */ - /* num_iso_eH[0] = number of explicit non-isotopic hydrogen atom neighbors */ - j = j1; - /* Add Explicit Neighbor */ - if ( j1 == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { - /* add an explicit neighbor if possible */ - if ( nMustHaveNumNeigh == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { - bAddExplicitNeighbor = ADD_EXPLICIT_LONE_PAIR_NEIGH; - } else - if ( nMustHaveNumNeigh == MAX_NUM_STEREO_ATOM_NEIGH ) { - /* check whether an explicit non-isotopic hydrogen can be added */ - /* to an atom that is a stereogenic atom */ - bAddExplicitNeighbor = ADD_EXPLICIT_HYDROGEN_NEIGH; - } - } - - if ( bAddExplicitNeighbor ) { - /*********************************************************** - * May happen only if (j1 == MAX_NUM_STEREO_ATOM_NEIGH-1) - * 3 neighbors only, no H-neighbors. Create and add coordinates of an implicit H - * or a fake 4th neighbor, that is, a lone pair - */ - if ( parity == INCHI_PARITY_UNKNOWN ) { - goto exit_function; /* the user insists the parity is unknown and the isotopic */ - /* composition of the neighbors does not contradict */ - } else - if ( num_z == 0 || are_3_vect_in_one_plane(at_coord, MIN_SINE) ) { - /* "hydrogen down" rule is needed to resolve an ambiguity */ - if ( num_z > 0 ) { - bAmbiguous |= AMBIGUOUS_STEREO; - } -#if( APPLY_IMPLICIT_H_DOWN_RULE == 1 ) /* { */ - /* Although H should be at the top of the list, add it to the bottom. */ - /* This will be taken care of later by inverting parity 1<->2 */ - at_coord[j][0] = 0.0; - at_coord[j][1] = 0.0; -#if( STEREO_CENTER_BONDS_NORM == 1 ) - at_coord[j][2] = -1.0; -#else - at_coord[j][2] = -(bond_len_xy[0]+bond_len_xy[1]+bond_len_xy[2])/3.0; -#endif -#else /* } APPLY_IMPLICIT_H_DOWN_RULE { */ -#if (ALWAYS_SET_STEREO_PARITY == 1) - parity = INCHI_PARITY_EVEN; /* suppose atoms are pre-sorted (testing) */ -#else - parity = INCHI_PARITY_UNDEFINED; -#endif - goto exit_function; -#endif /* } APPLY_IMPLICIT_H_DOWN_RULE */ - } else { - /* we have enough information to find implicit hydrogen coordinates */ - e_copy3( sum_xyz, at_coord[j] ); - e_change_sign3( at_coord[j], at_coord[j] ); - z = e_len3( at_coord[j] ); - /* Comparing the original bond lengths to lenghts derived from normalized to 1 */ - /* This bug leads to pronouncing legitimate stereogenic atoms */ - /* connected by 3 bonds "undefined" if in a nicely drawn 2D structure */ - /* bond lengths are about 20 or greater. Reported by Reinhard Dunkel 2005-08-05 */ - if ( bFixSp3Bug ) { - /* coordinate scaling bug fixed here */ - if ( z > 1.0 ) { - rmax *= z; - } else { - rmin *= z; - } - } else { - /* original InChI v.1 bug */ - rmax = inchi_max( rmax, z ); - rmin = inchi_min( rmin, z ); - } - if ( z < MIN_BOND_LEN || rmin/rmax < MIN_SINE ) { - /* the new 4th bond is too short */ - parity = INCHI_PARITY_UNDEFINED; - goto exit_function; - } -#if( STEREO_CENTER_BOND4_NORM == 1 ) - else { - e_mult3( at_coord[j], 1.0/z, at_coord[j] ); - } -#endif - } - } else - if ( j1 != MAX_NUM_STEREO_ATOM_NEIGH ) { - if ( parity == INCHI_PARITY_UNKNOWN ) { - parity = -INCHI_PARITY_UNDEFINED; /* isotopic composition of H-neighbors contradicts 'unknown' */ - } - goto exit_function; - } else /* j1 == MAX_NUM_STEREO_ATOM_NEIGH */ - if ( num_z == 0 || e_are_4at_in_one_plane(at_coord, MIN_SINE) ) { - /* all four neighours in xy plane: undefined geometry. */ - if ( num_z > 0 ) { - bAmbiguous |= AMBIGUOUS_STEREO; - } - if ( parity != INCHI_PARITY_UNKNOWN ) { -#if (ALWAYS_SET_STEREO_PARITY == 1) - parity = INCHI_PARITY_EVEN; /* suppose atoms are pre-sorted (testing) */ -#else - /* all 4 bonds are in one plain */ - parity = INCHI_PARITY_UNDEFINED; -#endif - } - goto exit_function; - } - /*********************************************************** - * At this point we have 4 neighboring atoms. - * check for tetrahedral ambiguity in 2D case - */ - if ( b2D ) { - if ( 0 < (n2DTetrahedralAmbiguity = e_Get2DTetrahedralAmbiguity( at_coord, bAddExplicitNeighbor )) ) { - if ( T2D_WARN & n2DTetrahedralAmbiguity ) { - bAmbiguous |= AMBIGUOUS_STEREO; - } - if ( T2D_UNDF & n2DTetrahedralAmbiguity ) { - if ( parity != INCHI_PARITY_UNKNOWN ) { -#if (ALWAYS_SET_STEREO_PARITY == 1) - parity = INCHI_PARITY_EVEN; /* suppose atoms are pre-sorted (testing) */ -#else - parity = INCHI_PARITY_UNDEFINED; /* no parity */ -#endif - } - goto exit_function; - } - } else - if ( n2DTetrahedralAmbiguity < 0 ) { - bAmbiguous |= AMBIGUOUS_STEREO_ERROR; /* error */ - parity = INCHI_PARITY_UNDEFINED; - goto exit_function; - } - } - - /************************************************************/ - /* Move coordinates origin to the neighbor #0 */ - for ( j = 1; j < MAX_NUM_STEREO_ATOM_NEIGH; j ++ ) { - e_diff3(at_coord[j], at_coord[0], at_coord[j]); - } - e_diff3(at_coord_center, at_coord[0], at_coord_center); - - /******************************************************** - * find the central (cur_at) atom's parity - * (orientation of atoms #1-3 when looking from #0) - ********************************************************/ - triple_product = e_triple_prod_and_min_abs_sine2(&at_coord[1], at_coord_center, bAddExplicitNeighbor, &min_sine, &bAmbiguous); - if ( fabs(triple_product) > ZERO_FLOAT && (min_sine > MIN_SINE || fabs(min_sine) > ZERO_FLOAT && (n2DTetrahedralAmbiguity & T2D_OKAY ) ) ) { - /* Even => sorted in correct order, Odd=>transposed */ - parity = triple_product > 0.0? INCHI_PARITY_EVEN : INCHI_PARITY_ODD; - - /* for 4 attached atoms, moving the implicit H from index=3 to index=0 */ - /* can be done in odd number (3) transpositions: (23)(12)(01), which inverts the parity */ - if ( j1 == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { - parity = 3 - parity; - } - } else { -#if (ALWAYS_SET_STEREO_PARITY == 1) - parity = AT_PARITY_EVEN; /* suppose atoms are pre-sorted (testing) */ -#else - if ( num_z > 0 ) { - bAmbiguous |= AMBIGUOUS_STEREO; - } - parity = INCHI_PARITY_UNDEFINED; /* no parity: 4 bonds are in one plane. */ -#endif - } -exit_function: - - if ( parity ) { - bAmbiguousStereo |= bAmbiguous; - } - /* isotopic parity => parity */ - if ( parity < 0 ) - parity = -parity; - - if ( 0 < parity && parity < INCHI_PARITY_UNDEFINED ) { - inchi_Stereo0D *stereo0D; - if ( stereo0D = e_GetNewStereo( pStereo ) ) { - stereo0D->central_atom = cur_at; - stereo0D->parity = parity; - stereo0D->type = INCHI_StereoType_Tetrahedral; - k = 0; - if ( at[cur_at].num_bonds == 3 ) { - stereo0D->neighbor[k++] = cur_at; - } - for ( j = 0; j < at[cur_at].num_bonds; j ++ ) { - stereo0D->neighbor[k++] = at[cur_at].neighbor[j]; - } - } - } - return parity; - -} -#undef ADD_EXPLICIT_HYDROGEN_NEIGH -#undef ADD_EXPLICIT_LONE_PAIR_NEIGH - -/*************************************************************/ -inchi_Stereo0D *e_GetNewStereo( Stereo0D *pStereo ) -{ -#define DEFAULT_0D_STEREO_DELTA 64 - int delta = pStereo->delta_num_stereo0D > 0? pStereo->delta_num_stereo0D : DEFAULT_0D_STEREO_DELTA; - if ( pStereo->num_stereo0D >= pStereo->max_num_Stereo0D ) { - /*inchi_Stereo0D *pNew = (inchi_Stereo0D *)e_inchi_calloc( pStereo->max_num_Stereo0D + delta, sizeof(pNew[0]) );*/ - inchi_Stereo0D *pNew = e_CreateInchi_Stereo0D( pStereo->max_num_Stereo0D + delta ); - if ( pNew ) { - if ( pStereo->num_stereo0D > 0 ) { - memcpy( pNew, pStereo->stereo0D, pStereo->num_stereo0D * sizeof(pNew[0]) ); - } - /*e_inchi_free(pStereo->stereo0D);*/ - e_FreeInchi_Stereo0D( &pStereo->stereo0D ); - pStereo->stereo0D = pNew; - pStereo->max_num_Stereo0D += delta; - } - } - return pStereo->stereo0D + pStereo->num_stereo0D ++; -#undef DEFAULT_0D_STEREO_DELTA -} - -/*************************************************************/ -int set_0D_stereo_parities( inchi_Input *pInp, int bPointedEdgeStereo ) -{ - int num_3D_stereo_atoms=0, num_stereo_bonds=0; - - int i, is_stereo, num_stereo; - inchi_Atom* at = pInp->atom; - int num_at = pInp->num_atoms; - Stereo0D stereo; - Stereo0D *pStereo = &stereo; - - /********************************************************** - * - * Note: this parity reflects only relative positions of - * the atoms-neighbors and their ordering in the - * lists of neighbors. - * - * To obtain the actual parity, the parity of a number - * of neighbors transpositions (to obtain a sorted - * list of numbers assigned to the atoms) should be - * added. - * - **********************************************************/ - - /********************************************************************************* - - An example of parity=1 for stereogenic center, tetrahedral asymmetric atom - - - - (1) - | - | - [C] | - | - (2)------(0) - / - / - / - / - (3) - - - Notation: (n) is a tetrahedral atom neighbor; n is an index of a neighbor in - the central_at->neighbor[] array : neighbor atom number is central_at->neighbor[n]. - - (0)-(1), (0)-(2), (0)-(3) are lines connecting atom [C] neighbors to neighbor (0) - (0), (1) and (2) are in the plane - (0)-(3) is directed from the plain to the viewer - [C] is somewhere between (0), (1), (2), (3) - Since (1)-(2)-(3) are in a clockwise order when looking from (0), parity is 2, or even; - otherwise parity would be 1, or odd. - - ********************************************************************************** - - Examples of a stereogenic bond. - - Notation: [atom number], (index of a neighbor): - [1] and [2] are atoms connected by the stereogenic bond - numbers in () are indexes of neighbors of [1] or [2]. - (12 x 16)z = z-component of [1]-[2] and [1]-[6] cross-product - - atom [1] atom [2] - [8] [4] prod01 = (12 x 16)z < 0 prod01 = (21 x 24)z < 0 - \ / prod02 = (12 x 18)z > 0 prod02 = (21 x 25)z > 0 - (2) (1) 0 transpositions because 0 transpositions because - \ / double bond is in 0 posit. double bond is in 0 position - [1]==(0)(0)==[2] 0 = (prod01 > prod02) 0 = (prod01 > prod02) - / \ - (1) (2) result: parity = 2, even result: parity=2, even - / \ - [6] [5] - - - - atom [1] atom [2] - [8] [5] prod01 = (12 x 18)z > 0 prod01 = (21 x 24)z > 0 - \ / prod02 = (12 x 16)z < 0 prod02 = (21 x 25)z < 0 - (0) (2) 2 transpositions to move 1 transposition to move - \ / at [2] from 2 to 0 pos. at [1] from 1 to 0 position - [1]==(2)(1)==[2] 1 = (prod01 > prod02) 1 = (prod01 > prod02) - / \ - (1) (0) result: parity = (1+2) result: parity=(1+1) - / \ 2-(1+2)%2 = 1, odd 2-(1+1)%2 = 2, even - [6] [4] - - - *********************************************************************************** - Note: atoms' numbers [1], [2], [4],... are not used to calculate parity at this - point. They will be used for each numbering in the canonicalization. - Note: parity=3 for a stereo atom means entered undefined bond direction - parity=4 for an atom means parity cannot be determined from the given geometry - ***********************************************************************************/ - - if ( !at ) { - return -1; - } - - /* clear stereo descriptors */ - memset( pStereo, 0, sizeof(*pStereo) ); - pStereo->delta_num_stereo0D = num_at; - pStereo->cAtType = (S_CHAR *)e_inchi_calloc( num_at, sizeof(pStereo->cAtType[0]) ); - if (!pStereo->cAtType ) { - return -1; - } - /* atom stereo types */ - for ( i = 0; i < num_at; i ++ ) { - pStereo->cAtType[i] = e_GetElType( at, i ); - } - - - - /* calculate stereo descriptors */ - /* main cycle: set stereo parities */ - for( i = 0, num_stereo = 0; i < num_at; i ++ ) { - - if ( is_stereo = e_set_stereo_atom_parity( pStereo, at, i, bPointedEdgeStereo ) ) { - num_3D_stereo_atoms += ATOM_PARITY_WELL_DEF( is_stereo ); - num_stereo += ATOM_PARITY_WELL_DEF( is_stereo ); - } else { - is_stereo = e_set_stereo_bonds_parity( pStereo, at, i, bPointedEdgeStereo ); - if ( RETURNED_ERROR( is_stereo ) ) { - num_3D_stereo_atoms = is_stereo; - is_stereo = 0; - break; - } else { - num_stereo_bonds += is_stereo; - num_stereo += is_stereo; - } - } - } - /* - if ( (nMode & REQ_MODE_SC_IGN_ALL_UU ) - REQ_MODE_SC_IGN_ALL_UU - REQ_MODE_SB_IGN_ALL_UU - */ - if ( pStereo->cAtType ) { - e_inchi_free( pStereo->cAtType ); - } - if ( pInp->stereo0D ) { - } - - pInp->stereo0D = pStereo->stereo0D; - pInp->num_stereo0D = pStereo->num_stereo0D; - - return RETURNED_ERROR( num_3D_stereo_atoms )? num_3D_stereo_atoms : num_stereo; -} -/*****************************************************************/ -int Clear3D2Dstereo(inchi_Input *pInp) -{ - int i; - if ( !pInp->atom || !pInp->num_atoms ) - return 0; - for ( i = 0; i < pInp->num_atoms; i ++ ) { - pInp->atom[i].x = - pInp->atom[i].y = - pInp->atom[i].z = 0.0; - memset( pInp->atom[i].bond_stereo, 0, sizeof(pInp->atom[i].bond_stereo) ); - } - return 1; -} - +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +/* this file is used only in case of #define CREATE_0D_PARITIES */ +/* hardcoded bFixSp3Bug = 1 fixes sp3 bugs in original InChI v. 1.00 */ +/* Phosphine and Arsine sp3 stereo are not supported in this mode */ + +#include +#include +#include +#include +#include + + +#include "e_mode.h" + +#include "../../../../INCHI_BASE/src/inchi_api.h" + +#include "e_ctl_data.h" +#include "e_inchi_atom.h" +#include "e_ichisize.h" +#include "e_comdef.h" +#include "e_ichicomp.h" +#include "e_util.h" +#include "e_0dstereo.h" + + + +#define ALWAYS_SET_STEREO_PARITY 0 + +#define SB_PARITY_FLAG 0x38 /* disconnected structure has undef. parity */ +#define SB_PARITY_SHFT 3 +#define SB_PARITY_MASK 0x07 +#define SB_PARITY_1(X) (X & SB_PARITY_MASK) /* refers to connected structure */ +#define SB_PARITY_2(X) (((X) >> SB_PARITY_SHFT) & SB_PARITY_MASK) /* refers to connected structure */ + +#define inchi_NUMH(AT,CUR_AT) (AT[CUR_AT].num_iso_H[0]+AT[CUR_AT].num_iso_H[1]+AT[CUR_AT].num_iso_H[2]+AT[CUR_AT].num_iso_H[3]) +#define inchi_NUMH2(AT,CUR_AT) ((AT[CUR_AT].num_iso_H[0]>0?AT[CUR_AT].num_iso_H[0]:0) +AT[CUR_AT].num_iso_H[1]+AT[CUR_AT].num_iso_H[2]+AT[CUR_AT].num_iso_H[3]) +#define IS_METAL(a) (a == AtType_Metal || a == AtType_Sn4 || a == AtType_Sn3 || a == AtType_Sn2) + +typedef struct tagStereo0D { + inchi_Stereo0D *stereo0D; /* array of num_stereo0D 0D stereo elements or NULL */ + int num_stereo0D; /* number of 0D stereo elements */ + int max_num_Stereo0D; /* allocated length of stereo0D */ + int delta_num_stereo0D;/* allocation increments */ + S_CHAR *cAtType; +} Stereo0D; + +typedef enum tagAtType { + /* possible stereocenter */ + AtType_C4 = 1, /* >C< */ + AtType_Si4 = 2, /* >Si< */ + AtType_Ge4 = 3, /* >Ge< */ + AtType_Sn4 = 4, /* >Sn< */ + AtType_B4m = 5, /* B(-) */ + AtType_S4 = 6, /* =S< */ + AtType_S6 = 7, /* >S<, 2 double bonds */ + AtType_S3p = 8, /* -S(+)< */ + AtType_S5p = 9, /* >S(+)<, 1 double bond */ + AtType_Se4 =10, /* =Se< */ + AtType_Se6 =11, /* >Se<, 2 double bonds */ + AtType_Se3p =12, /* -Se(+)< */ + AtType_Se5p =13, /* >Se(+)<, 1 double bond */ + AtType_N5 =14, /* >N<, 1 double bond */ + AtType_N4p =15, /* >N(+)< */ + AtType_N3r =16, /* -N<| (N in a 3-member ring) */ + AtType_P4p =17, /* >N(+)< */ + AtType_P5 =18, /* >P<, 1 double bond */ + AtType_As4p =19, /* >As(+)< */ + AtType_As5 =20, /* >As<, 1 double bond */ + /* possible stereobond */ + AtType_C3 =21, /* =C<, =C/ */ + AtType_Si3 =22, + AtType_Ge3 =23, + AtType_Sn3 =24, + AtType_N3p =25, /* =N(+)< */ + AtType_N3 =26, /* =N/ */ + /* middle allene/cumulene */ + AtType_C2 =27, /* =C= */ + AtType_Si2 =28, /* =Si= */ + AtType_Ge2 =29, /* =Ge= */ + AtType_Sn2 =30, /* =Ge= */ + /* may become stereobond after charge or radical shift */ + AtType_Nns =31, + /* metal */ + AtType_Metal =32, + /* terminal H */ + AtType_TermH =33, + AtType_TermD =34, + AtType_TermT =35 +} AT_TYPE; + +typedef enum tagElType { + ElType_C = 1, + ElType_Si = 2, + ElType_Ge = 3, + ElType_Sn = 4, + ElType_B = 5, + ElType_S = 6, + ElType_Se = 7, + ElType_N = 8, + ElType_P = 9, + ElType_As =10, + ElType_H =11, + ElType_D =12, + ElType_T =13 +} EL_TYPE; + +inchi_Stereo0D *e_GetNewStereo( Stereo0D *pStereo ); + + +static int ee_extract_ChargeRadical( char *elname, int *pnRadical, int *pnCharge ); +static int ee_extract_H_atoms( char *elname, S_CHAR num_iso_H[] ); +static int e_GetElType( inchi_Atom *at, int cur_atom ); +static int e_bCanInpAtomBeAStereoCenter( int cur_at, S_CHAR *cAtType ); +static int e_nNumNonMetalNeigh( inchi_Atom *atom, int cur_at, Stereo0D *pStereo, int *i_ord_LastMetal ); + +void e_inchi_swap ( char *a, char *b, size_t width ); +int e_insertions_sort( void *base, size_t num, size_t width, int ( *compare )(const void *e1, const void *e2 ) ); +int e_bCanAtomHaveAStereoBond( inchi_Atom *at, int cur_at, S_CHAR *cAtType ); +int e_bCanAtomBeMiddleAllene( int cur_at, S_CHAR *cAtType ); +int e_bCanAtomBeTerminalAllene( int cur_at, S_CHAR *cAtType ); + +/**********************************************************************************/ +#define AMBIGUOUS_STEREO 1 +#define AMBIGUOUS_STEREO_ERROR 32 + + +#define AB_MAX_WELL_DEFINED_PARITY inchi_max(INCHI_PARITY_ODD, INCHI_PARITY_EVEN) /* 1, 2 => well defined parities, uncluding 'unknown' */ +#define AB_MIN_WELL_DEFINED_PARITY inchi_min(INCHI_PARITY_ODD, INCHI_PARITY_EVEN) /* min(INCHI_PARITY_ODD, INCHI_PARITY_EVEN) */ + +#define AMBIGUOUS_STEREO 1 + +#define MIN_DOT_PROD 50 /* min value of at->stereo_bond_z_prod[i] to define parity */ + +#define ATOM_PARITY_WELL_DEF(X) (AB_MIN_WELL_DEFINED_PARITY <= (X) && (X) <= AB_MAX_WELL_DEFINED_PARITY) +/**********************************************************************************/ + +#define CT_ERR_FIRST (-30000) +#define CT_OUT_OF_RAM (CT_ERR_FIRST- 2) /*(-30002) */ +#define CT_ISO_H_ERR (CT_ERR_FIRST- 9) /*(-30009) */ +#define CT_CALC_STEREO_ERR (CT_ERR_FIRST-15) /*(-30015) */ +#define CT_UNKNOWN_ERR (CT_ERR_FIRST-18) /*(-30018) */ + +#define CT_ERR_MIN CT_UNKNOWN_ERR +#define CT_ERR_MAX CT_ERR_FIRST + +#define RETURNED_ERROR(nVal) (CT_ERR_MIN<=(nVal) && (nVal)<=CT_ERR_MAX) +/**********************************************************************************/ +#define MAX_CUMULENE_LEN 2 /* max number of bonds in a cumulene chain - 1 */ +/**********************************************************************************/ +int ee_extract_ChargeRadical( char *elname, int *pnRadical, int *pnCharge ) +{ + char *q, *r, *p; + int nCharge=0, nRad = 0, charge_len = 0, k, nVal, nSign, nLastSign=1, len; + + p = elname; + + /* extract radicals & charges */ + while ( q = strpbrk( p, "+-^" ) ) { + switch ( *q ) { + case '+': + case '-': + for ( k = 0, nVal=0; (nSign = ('+' == q[k])) || (nSign = -('-' == q[k])); k++ ) { + nVal += (nLastSign = nSign); + charge_len ++; + } + if ( nSign = (int)strtol( q+k, &r, 10 ) ) { /* fixed 12-5-2001 */ + nVal += nLastSign * (nSign-1); + } + charge_len = r - q; + nCharge += nVal; + break; + /* case '.': */ /* singlet '.' may be confused with '.' in formulas like CaO.H2O */ + case '^': + nRad = 1; /* doublet here is 1. See below */ + charge_len = 1; + for ( k = 1; q[0] == q[k]; k++ ) { + nRad ++; + charge_len ++; + } + break; + } + memmove( q, q+charge_len, strlen(q+charge_len)+1 ); + } + len = strlen(p); + /* radical */ + if ( (q = strrchr( p, ':' )) && !q[1]) { + nRad = RADICAL_SINGLET; + q[0] = '\0'; + len --; + } else { + while( (q = strrchr( p, '.' )) && !q[1] ) { + nRad ++; + q[0] = '\0'; + len --; + } + + nRad = nRad == 1? RADICAL_DOUBLET : + nRad == 2? RADICAL_TRIPLET : 0; + } + *pnRadical = nRad; + *pnCharge = nCharge; + return ( nRad || nCharge ); +} +/***********************************************************************/ +int ee_extract_H_atoms( char *elname, S_CHAR num_iso_H[] ) +{ + int i, len, c, k, val, bExtracted = 0; + char *q; + i = 0; + len = (int)strlen(elname); + c = UCINT elname[0]; + while ( i < len ) { + switch ( c ) { + case 'H': + k = 0; + break; + case 'D': + k = 2; + break; + case 'T': + k = 3; + break; + default: + k = -1; + break; + } + q = elname+i+1; /* pointer to the next to elname[i] character */ + c = UCINT q[0]; + if ( k >= 0 && !islower( c ) ) { + /* found a hydrogen */ + bExtracted ++; + if ( isdigit( c ) ) { + val = (int)strtol( q, &q, 10 ); + /* q = pointer to the next to number of hydrogen atom(s) character */ + } else { + val = 1; + } + num_iso_H[k] += val; + /* remove the hydrogen atom from the string */ + len -= (q-elname)-i; + memmove( elname+i, q, len + 1 ); + /* c = UCINT elname[i]; */ + } else { + i ++; + } + c = UCINT elname[i]; /* moved here 11-04-2002 */ + } + return bExtracted; +} +/************************************************/ +#define MAX_BOND_TYPE 4 +int e_GetElType( inchi_Atom *at, int cur_atom ) +{ + char szEl[ATOM_EL_LEN]; + int nRadical, nCharge, bChargeOrRad, bH, bAddH = 0, nElType=0, bond_valence, valence, num_H=0; + S_CHAR num_iso_H[NUM_H_ISOTOPES+1]; + S_CHAR num_bonds[MAX_BOND_TYPE]; + int i; + int nRadicalValence = 0; + + if ( sizeof(at->num_iso_H) != sizeof(num_iso_H) || + sizeof(at->num_iso_H[0]) != sizeof(num_iso_H[0]) ) { + /* program error */ + return -1; + } + strcpy( szEl, at[cur_atom].elname); + memset( num_iso_H, 0, sizeof(num_iso_H) ); + bChargeOrRad = ee_extract_ChargeRadical( szEl, &nRadical, &nCharge ); + bH = ee_extract_H_atoms( szEl, num_iso_H ); + if ( !bChargeOrRad ) { + nRadical = at[cur_atom].radical; + nCharge = at[cur_atom].charge; + } + if ( !bH ) { + memcpy( num_iso_H, at[cur_atom].num_iso_H, sizeof(num_iso_H) ); + if ( bAddH = (num_iso_H[0] < 0 ) ) { + num_iso_H[0] = 0; + } + } + num_H = num_iso_H[0]+num_iso_H[1]+num_iso_H[2]+num_iso_H[3]; + + if (nRadical==INCHI_RADICAL_DOUBLET) { + nRadicalValence = 1; + } else + if ( nRadical==INCHI_RADICAL_TRIPLET || nRadical==INCHI_RADICAL_SINGLET ){ + nRadicalValence = 2; + } + + /* element type */ + if ( !strcmp( szEl, "C" ) ) { + nElType = ElType_C; + } else + if ( !strcmp( szEl, "Si" ) ) { + nElType = ElType_Si; + } else + if ( !strcmp( szEl, "Ge" ) ) { + nElType = ElType_Ge; + } else + if ( !strcmp( szEl, "Sn" ) ) { + nElType = ElType_Sn; + } else + if ( !strcmp( szEl, "B" ) ) { + nElType = ElType_B; + } else + if ( !strcmp( szEl, "S" ) ) { + nElType = ElType_S; + } else + if ( !strcmp( szEl, "Se" ) ) { + nElType = ElType_Se; + } else + if ( !strcmp( szEl, "N" ) ) { + nElType = ElType_N; + } else + if ( !strcmp( szEl, "P" ) ) { + nElType = ElType_P; + } else + if ( !strcmp( szEl, "As" ) ) { + nElType = ElType_As; + } else + if ( !szEl[0] ) { + if ( 1 == num_H && (num_iso_H[0] == 1 || num_iso_H[1] == 1) ) { + nElType = ElType_H; + } else + if ( 1 == num_H && num_iso_H[2] == 1 ) { + nElType = ElType_D; + } else + if ( 1 == num_H && num_iso_H[3] == 1 ) { + nElType = ElType_T; + } else { + return -1; + } + } else { + if ( e_is_element_a_metal( szEl ) ) { + return AtType_Metal; + } + return -1; /* no stereo */ + } + + /* atom type */ + memset( num_bonds, 0, sizeof(num_bonds) ); + bond_valence = 0; + valence = at[cur_atom].num_bonds; + for ( i = 0; i < valence; i ++ ) { + if ( 0 < at[cur_atom].bond_type[i] && at[cur_atom].bond_type[i] <= MAX_BOND_TYPE ) { + num_bonds[at[cur_atom].bond_type[i]-1] ++; + } + } + bond_valence = num_bonds[0] + 2*num_bonds[1] + 3*num_bonds[2]; + if ( num_bonds[3] ) { + if ( num_bonds[3] == 2 ) { + bond_valence += 3; /* -C= */ + } else + if ( num_bonds[3] == 3 ) { + bond_valence += 4; /* >C= */ + } else { + return -1; + } + } + + switch( nElType ) { + + case ElType_C: + case ElType_Si: + case ElType_Ge: + case ElType_Sn: + + if ( bAddH && bond_valence + num_H + (abs(nCharge)==1) + nRadicalValence == 4 ) { + bAddH = 0; /* no H will be added */ + } + + if ( bond_valence == valence && (valence==4 || valence == 3 && (bAddH || num_H==1)) && !nRadical ) { + switch( nElType ) { + case ElType_C: + return AtType_C4; + case ElType_Si: + return AtType_Si4; + case ElType_Ge: + return AtType_Ge4; + case ElType_Sn: + return AtType_Sn4; + } + } else + if ( bond_valence == valence && (valence==3 || valence == 2 && (bAddH || num_H==1)) && nRadical == INCHI_RADICAL_DOUBLET ) { + switch( nElType ) { + case ElType_C: + return AtType_C3; + case ElType_Si: + return -1; + case ElType_Ge: + return -1; + case ElType_Sn: + return AtType_Metal; + } + } else + if ( bond_valence == 4 && valence == 2 && !num_H && !nRadical ) { + /* two double bonds or single & triple */ + switch( nElType ) { + case ElType_C: + return AtType_C2; + case ElType_Si: + return AtType_Si2; + case ElType_Ge: + return AtType_Ge2; + case ElType_Sn: + return AtType_Sn2; + } + } else + if ( bond_valence == 3 && valence == 2 && !num_H && nRadical == INCHI_RADICAL_DOUBLET ) { + /* two double bonds or single & triple */ + switch( nElType ) { + case ElType_C: + return AtType_C2; + case ElType_Si: + return -1; + case ElType_Ge: + return -1; + case ElType_Sn: + return AtType_Metal; + } + } else + if ( bond_valence > valence && (valence==3 || valence == 2 && (bAddH || num_H==1)) && !nRadical ) { + /* "bond_valence > valence" instead of "bond_valence == valence+1" to accommodate + * erroneouse acceptance by 1.12Beta of stereo bond case when C has valence > 5 */ + switch( nElType ) { + case ElType_C: + return AtType_C3; + case ElType_Si: + return AtType_Si3; + case ElType_Ge: + return AtType_Ge3; + case ElType_Sn: + return AtType_Sn3; + } + } else + if ( bond_valence == valence+1 && bond_valence > 3 ) { + /* trying to accommodate hypervalence in coord compounds before disconnection */ + switch( nElType ) { + case ElType_C: + return AtType_C3; + case ElType_Si: + return -1; + case ElType_Ge: + return -1; + case ElType_Sn: + return AtType_Metal; + } + } else + if ( bond_valence == valence && bond_valence >= 2 && abs(nCharge) == 1 && 3 == (valence + (bAddH || num_H==1)) ) { + /* trying to accommodate C(-)-N(+) bond that may become double after ion pair removal. added 2004-01-31 */ + switch( nElType ) { + case ElType_C: + return AtType_C3; + case ElType_Si: + return AtType_Si3; + case ElType_Ge: + return -1; + case ElType_Sn: + return AtType_Metal; + } + } + return (nElType == ElType_Sn)? AtType_Metal : -1; + + case ElType_B: + if ( bond_valence == valence && (valence==4 || valence==3 && (bAddH || num_H==1)) && nCharge == -1 && !nRadical ) { + return AtType_B4m; + } + return -1; + + case ElType_S: + case ElType_Se: + if ( (valence == 3 && bond_valence == 3) && (nCharge == 1 || nRadical == INCHI_RADICAL_DOUBLET) ) { + switch( nElType ) { + case ElType_S: + return AtType_S3p; + case ElType_Se: + return AtType_Se3p; + } + } else + if ( (valence == 3 && bond_valence == 4) && nCharge == 0 && !nRadical ) { + switch( nElType ) { + case ElType_S: + return AtType_S4; + case ElType_Se: + return AtType_Se4; + } + } else + if ( (valence == 4 && bond_valence == 5) && (nCharge == 1 || nRadical == INCHI_RADICAL_DOUBLET) ) { + switch( nElType ) { + case ElType_S: + return AtType_S5p; + case ElType_Se: + return AtType_Se5p; + } + } else + if ( (valence == 4 && bond_valence == 6) && nCharge == 0 && !nRadical ) { + switch( nElType ) { + case ElType_S: + return AtType_S6; + case ElType_Se: + return AtType_S6; + } + } else { + return -1; + } + case ElType_N: + case ElType_P: + case ElType_As: + if ( bAddH && bond_valence + num_H - (abs(nCharge)==1? nCharge:0) + nRadicalValence == 3 ) { + bAddH = 0; /* no H will be added */ + } else + if ( bAddH && bond_valence + num_H == 5 ) { + bAddH = 0; /* no H will be added */ + } + if ( bond_valence == valence && (valence==4 || valence==3 && (bAddH || num_H==1)) && nCharge == 1 && !nRadical ) { + switch( nElType ) { + case ElType_N: + return AtType_N4p; + case ElType_P: + return AtType_P4p; + case ElType_As: + return AtType_As4p; + } + } else + if ( bond_valence == valence+1 && (valence==4 /*|| valence == 3*/) && nCharge == 0 ) { + switch( nElType ) { + case ElType_N: + return AtType_N5; + case ElType_P: + return AtType_P5; + case ElType_As: + return AtType_As5; + } + } else + if ( bond_valence == valence && valence==3 && nCharge == 0 && !nRadical ) { + switch( nElType ) { + case ElType_N: + { + AT_NUM neigh1, neigh2; + int j1, j2, bIn3MembRing = 0; + for ( j1 = 0; j1 < valence && !bIn3MembRing; j1 ++ ) { + neigh1 = at[cur_atom].neighbor[j1]; + for ( j2 = j1+1; j2 < valence && !bIn3MembRing; j2 ++ ) { + neigh2 = at[cur_atom].neighbor[j2]; + if ( e_is_in_the_slist( at[neigh1].neighbor, neigh2, at[neigh1].num_bonds ) ) { + bIn3MembRing ++; + } + } + } + return bIn3MembRing? AtType_N3r : AtType_Nns /* -1*/; + } + case ElType_P: + case ElType_As: + return -1; + } + } else + if ( bond_valence == valence+1 && valence==2 && nCharge == 0 && !nRadical ) { + switch( nElType ) { + case ElType_N: + return AtType_N3; + case ElType_P: + case ElType_As: + return -1; + } + } else + if ( bond_valence == valence+1 && valence==3 && nCharge == 0 && !nRadical ) { + /* reproduce 1.12Beta bug: =N< is accepted in stereogenic bonds */ + switch( nElType ) { + case ElType_N: + return AtType_N3; + case ElType_P: + case ElType_As: + return -1; + } + } else + if ( bond_valence == valence && valence==2 && nCharge == 0 && nRadical == INCHI_RADICAL_DOUBLET ) { + switch( nElType ) { + case ElType_N: + return AtType_N3; + case ElType_P: + case ElType_As: + return -1; + } + } else + if ( bond_valence == valence+1 && (valence==3 || valence == 2 && (bAddH || num_H==1)) && nCharge == 1 ) { + switch( nElType ) { + case ElType_N: + return AtType_N3p; + case ElType_P: + case ElType_As: + return -1; + } + } else + if ( bond_valence == valence && (valence==3 || valence == 2 && (bAddH || num_H==1)) && nCharge == 1 && nRadical == INCHI_RADICAL_DOUBLET ) { + switch( nElType ) { + case ElType_N: + return AtType_N3p; + case ElType_P: + case ElType_As: + return -1; + } + } else + if ( bond_valence == valence && valence == 2 && (bAddH || num_H==1) && nCharge == 0 || + bond_valence == valence && valence == 2 && nCharge == -1 ) { + switch( nElType ) { + case ElType_N: + return AtType_Nns; + case ElType_P: + case ElType_As: + return -1; + } + } else + if ( (bond_valence == valence+2 || bond_valence + nRadicalValence == valence+2) && valence==3 ) { + switch( nElType ) { + case ElType_N: + return AtType_Nns; /* -N<< may be in a stereogenic bond in case of double bond metal disconnection */ + case ElType_P: + case ElType_As: + return -1; + } + } + return -1; + + case ElType_H: + if ( valence == 1 ) + return AtType_TermH; + break; + case ElType_D: + if ( valence == 1 ) + return AtType_TermD; + break; + case ElType_T: + if ( valence == 1 ) + return AtType_TermT; + break; + } + + return -1; +} + + +/**********************************************************************************/ +void e_inchi_swap ( char *a, char *b, size_t width ) +{ + char tmp; + if ( a != b ) + while ( width-- ) { + tmp = *a; + *a++ = *b; + *b++ = tmp; + } +} +/**********************************************************************************/ +/* Sort by insertions */ +int e_insertions_sort( void *base, size_t num, size_t width, int ( *compare )(const void *e1, const void *e2 ) ) +{ + char *i, *j, *pk; + int num_trans = 0; + size_t k; + for( k=1, pk = (char*)base; k < num; k++, pk += width ) { + for( i = pk, j = pk + width; j > (char*)base && (*compare)(i,j) > 0; j=i, i -= width ) { + e_inchi_swap( i, j, width ); + num_trans ++; + } + } + return num_trans; +} + + +#define ZTYPE_DOWN (-1) /* should be equal to -ZTYPE_UP */ +#define ZTYPE_NONE 0 +#define ZTYPE_UP 1 /* should be equal to -ZTYPE_DOWN */ +#define ZTYPE_3D 3 +#define ZTYPE_EITHER 9999 + +/* criteria for ill-defined */ +#define MIN_ANGLE 0.10 /* 5.73 degrees */ +#define MIN_SINE 0.03 /* min edge/plane angle in case the tetrahedra has significantly different edge length */ +#define MIN_ANGLE_DBOND 0.087156 /* 5 degrees = max angle considered as too small for unambiguous double bond stereo */ +#define MIN_SINE_OUTSIDE 0.06 /* min edge/plane angle to determine whether the central atom is outside of the tetrahedra */ +#define MIN_SINE_SQUARE 0.125 /* min edge/plane angle in case the tetrahedra is somewhat close to a parallelogram */ +#define MIN_SINE_EDGE 0.167 /* min sine/(min.edge) ratio to avoid undefined in case of long edges */ +#define MIN_LEN_STRAIGHT 1.900 /* min length of two normalized to 1 bonds in a straight line */ +#define MAX_SINE 0.70710678118654752440084436210485 /* 1/sqrt(2)=sin(pi/4) */ +#define MIN_BOND_LEN 0.000001 +#define ZERO_LENGTH MIN_BOND_LEN +#define ZERO_FLOAT 1.0e-12 +#define BOND_PARITY_UNDEFINED 64 +#if( STEREO_CENTER_BONDS_NORM == 1 ) +#define MPY_SINE 1.00 /* was 3.0 */ +#define MAX_EDGE_RATIO 2.50 /* max max/min edge ratio for a tetrahedra close to a parallelogram */ +#else +#define MPY_SINE 3.00 +#define MAX_EDGE_RATIO 6.00 /* max max/min edge ratio for a tetrahedra close to a parallelogram */ +#endif +/* local prototypes */ +static double e_get_z_coord( inchi_Atom* at, int cur_atom, int neigh_no, int *nType,int bPointedEdgeStereo ); +static double e_len3( const double c[] ); +static double e_len2( const double c[] ); +static double* e_diff3( const double a[], const double b[], double result[] ); +static double* e_add3( const double a[], const double b[], double result[] ); +static double* e_mult3( const double a[], double b, double result[] ); +static double* e_copy3( const double a[], double result[] ); +static double* e_change_sign3( const double a[], double result[] ); +static double e_dot_prod3( const double a[], const double b[] ); +static int e_dot_prodchar3( const S_CHAR a[], const S_CHAR b[] ); +static double* e_cross_prod3( const double a[], const double b[], double result[] ); +static double e_triple_prod( double a[], double b[], double c[], double *sine_value ); +static double e_triple_prod_and_min_abs_sine(double at_coord[][3], double *min_sine); +static int are_3_vect_in_one_plane( double at_coord[][3], double min_sine); +static int e_triple_prod_char( inchi_Atom *at, int at_1, int i_next_at_1, S_CHAR *z_dir1, + int at_2, int i_next_at_2, S_CHAR *z_dir2 ); + +static int e_CompDble( const void *a1, const void *a2 ); +static int e_Get2DTetrahedralAmbiguity( double at_coord[][3], int bAddExplicitNeighbor ); +static double e_triple_prod_and_min_abs_sine2(double at_coord[][3], double central_at_coord[], int bAddedExplicitNeighbor, double *min_sine, int *bAmbiguous); +static int e_are_4at_in_one_plane( double at_coord[][3], double min_sine); +static int e_half_stereo_bond_parity( inchi_Atom *at, int cur_at, S_CHAR *z_dir, int *bOnlyNonMetal, int bPointedEdgeStereo, Stereo0D *pStereo ); +static int e_set_stereo_bonds_parity( Stereo0D *pStereo, inchi_Atom *at, int at_1, int bPointedEdgeStereo ); +static int e_set_stereo_atom_parity( Stereo0D *pStereo, inchi_Atom *at, int cur_at, int bPointedEdgeStereo ); +static int e_FixSb0DParities( inchi_Atom *at, Stereo0D *pStereo, int chain_length, AT_NUM at_middle, + int at_1, int i_next_at_1, S_CHAR z_dir1[], S_CHAR z_dir1NM[], int bOnlyNM1, int bAnomaly1NM, int parity1, int parity1NM, + int at_2, int i_next_at_2, S_CHAR z_dir2[], S_CHAR z_dir2NM[], int bOnlyNM2, int bAnomaly2NM, int parity2, int parity2NM ); + + +/******************************************************************/ + + +static double *pDoubleForSort; + +/**********************************************************************************/ +double e_get_z_coord( inchi_Atom* at, int cur_atom, int neigh_no, int *nType, int bPointedEdgeStereo ) +{ + int stereo_value = at[cur_atom].bond_stereo[neigh_no]; + int stereo_type = abs( stereo_value ); + int neigh = (int)at[cur_atom].neighbor[neigh_no]; + double z = at[neigh].z - at[cur_atom].z; + int bFlat; + + if ( bFlat = (fabs(z) < ZERO_LENGTH) ) { + int i; + for ( i = 0; i < at[cur_atom].num_bonds; i ++ ) { + if ( fabs(at[cur_atom].z - at[(int)at[cur_atom].neighbor[i]].z) > ZERO_LENGTH ) { + bFlat = 0; + break; + } + } + } + + if ( bFlat ) { + if ( !bPointedEdgeStereo || bPointedEdgeStereo * stereo_value >= 0 ) { + /* bPointedEdgeStereo > 0: define stereo from pointed end of the stereo bond only */ + /* bPointedEdgeStereo < 0: define stereo from wide end of the stereo bond only (case of removed H) */ + switch( stereo_type ) { + /* 1=Up (solid triangle), 6=Down (Dashed triangle), 4=Either (zigzag triangle) */ + case 0: /* No stereo */ + *nType = ZTYPE_NONE; + break; + case INCHI_BOND_STEREO_SINGLE_1UP: /* 1= Up */ + *nType = ZTYPE_UP; + break; + case INCHI_BOND_STEREO_SINGLE_1EITHER: /* 4 = Either */ + *nType = ZTYPE_EITHER; + break; + case INCHI_BOND_STEREO_SINGLE_1DOWN: /* 6 = Down */ + *nType = ZTYPE_DOWN; + break; + default: + *nType = ZTYPE_NONE; /* ignore unexpected values */ + } + if ( stereo_value < 0 && (*nType == ZTYPE_DOWN || *nType == ZTYPE_UP) ) + *nType = -*nType; + } else { + *nType = ZTYPE_NONE; /* no stereo */ + } + } else + if ( stereo_type == INCHI_BOND_STEREO_SINGLE_1EITHER && + ( !bPointedEdgeStereo || bPointedEdgeStereo * stereo_value >= 0 ) ) { + *nType = ZTYPE_EITHER; + } else { + *nType = ZTYPE_3D; + } + return z; +} +/******************************************************************/ +double e_len3( const double c[] ) +{ + return sqrt( c[0]*c[0] + c[1]*c[1] + c[2]*c[2] ); +} +/******************************************************************/ +double e_len2( const double c[] ) +{ + return sqrt( c[0]*c[0] + c[1]*c[1] ); +} +/******************************************************************/ +double* e_diff3( const double a[], const double b[], double result[] ) +{ + + result[0] = a[0] - b[0]; + result[1] = a[1] - b[1]; + result[2] = a[2] - b[2]; + + return result; +} +/******************************************************************/ +double* e_add3( const double a[], const double b[], double result[] ) +{ + result[0] = a[0] + b[0]; + result[1] = a[1] + b[1]; + result[2] = a[2] + b[2]; + + return result; +} +/******************************************************************/ +double* e_mult3( const double a[], double b, double result[] ) +{ + result[0] = a[0] * b; + result[1] = a[1] * b; + result[2] = a[2] * b; + + return result; +} +/*************************************************************/ +double* e_copy3( const double a[], double result[] ) +{ + result[0] = a[0]; + result[1] = a[1]; + result[2] = a[2]; + + return result; +} +/*************************************************************/ +double* e_change_sign3( const double a[], double result[] ) +{ + result[0] = -a[0]; + result[1] = -a[1]; + result[2] = -a[2]; + + return result; +} +/*************************************************************/ +double e_dot_prod3( const double a[], const double b[] ) +{ + return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; +} +/*************************************************************/ +int e_dot_prodchar3( const S_CHAR a[], const S_CHAR b[] ) +{ + int prod = ((int)a[0]*(int)b[0] + (int)a[1]*(int)b[1] + (int)a[2]*(int)b[2])/100; + if ( prod > 100 ) + prod = 100; + else + if ( prod < -100 ) + prod = -100; + return prod; +} +/*************************************************************/ +double* e_cross_prod3( const double a[], const double b[], double result[] ) +{ + double tmp[3]; + + tmp[0] = (a[1]*b[2]-a[2]*b[1]); + tmp[1] = -(a[0]*b[2]-a[2]*b[0]); + tmp[2] = (a[0]*b[1]-a[1]*b[0]); + + result[0] = tmp[0]; + result[1] = tmp[1]; + result[2] = tmp[2]; + + return result; +} +/*************************************************************/ +double e_triple_prod( double a[], double b[], double c[], double *sine_value ) +{ + double ab[3], dot_prod_ab_c, abs_c, abs_ab; + e_cross_prod3( a, b, ab ); + /* ab[0] = (a[1]*b[2]-a[2]*b[1]); */ + /* ab[1] = -(a[0]*b[2]-a[2]*b[0]); */ + /* ab[2] = (a[0]*b[1]-a[1]*b[0]); */ + dot_prod_ab_c = e_dot_prod3( ab, c ); + /* dot_prod_ab_c = ab[0]*c[0] + ab[1]*c[1] + ab[2]*c[2]; */ + if ( sine_value ) { + abs_c = e_len3( c ); + /* abs_c = sqrt( c[0]*c[0] + c[1]*c[1] + c[2]*c[2] ); */ + abs_ab = e_len3( ab ); + /* abs_ab = sqrt( ab[0]*ab[0] + ab[1]*ab[1] + ab[2]*ab[2] ); */ + + if ( abs_c > 1.e-7 /* otherwise c has zero length */ && abs_ab > 1.e-7 /* otherwise a is parallel to b*/ ) { + *sine_value = MPY_SINE * dot_prod_ab_c / ( abs_c * abs_ab); + /* *sine_value = dot_prod_ab_c / ( abs_c * abs_ab); */ + } else { + *sine_value = 0.0; + } + } + return dot_prod_ab_c; +} +/*************************************************************/ +int e_CompDble( const void *a1, const void *a2 ) +{ + double diff = pDoubleForSort[*(const int*)a1] - pDoubleForSort[*(const int*)a2]; + if ( diff > 0.0 ) + return 1; + if ( diff < 0.0 ) + return -1; + return 0; +} +/*************************************************************/ +#define T2D_OKAY 1 +#define T2D_WARN 2 +#define T2D_UNDF 4 +int e_Get2DTetrahedralAmbiguity( double at_coord[][3], int bAddExplicitNeighbor ) +{ +#define ZERO_ANGLE 0.000001 + const double one_pi = 2.0*atan2(1.0 /* y */, 0.0 /* x */); + const double two_pi = 2.0*one_pi; + const double dAngleAndPiMaxDiff = 2.0*atan2(1.0, sqrt(7.0)); /* min sine between 2 InPlane bonds */ + int nBondType[MAX_NUM_STEREO_ATOM_NEIGH], nBondOrder[MAX_NUM_STEREO_ATOM_NEIGH]; + double dBondDirection[MAX_NUM_STEREO_ATOM_NEIGH], dAngle, dAlpha, dLimit, dBisector; + int nNumNeigh = MAX_NUM_STEREO_ATOM_NEIGH - (bAddExplicitNeighbor != 0); + int i, num_Up, num_Dn, bPrev_Up, cur_len_Up, cur_first_Up, len_Up, first_Up; + int ret; + int bFixSp3Bug = 1; /* 1=> FixSp3Bug option for overlapping 2D stereo bonds */ + + ret = 0; + for ( i = 0, num_Up = num_Dn = 0; i < nNumNeigh; i ++ ) { + dAngle = atan2( at_coord[i][1], at_coord[i][0] ); /* range from -pi to +pi */ + if ( dAngle < 0.0 ) { + dAngle += two_pi; + } + dBondDirection[i] = dAngle; + nBondType[i] = (at_coord[i][2] > 0.0)? 1 : (at_coord[i][2] < 0.0)? -1 : 0; /* z-coord sign */ + if ( nBondType[i] > 0 ) { + num_Up ++; + } else + if ( nBondType[i] < 0 ) { + num_Dn ++; + } + nBondOrder[i] = i; + } + if ( num_Up < num_Dn ) { + for ( i = 0; i < nNumNeigh; i ++ ) { + nBondType[i] = -nBondType[i]; + } + e_inchi_swap( (char*)&num_Dn, (char*)&num_Up, sizeof(num_Dn) ); + } + if ( !num_Up ) { + return T2D_UNDF; + } + /* sort according to the bond orientations */ + pDoubleForSort = dBondDirection; + e_insertions_sort( nBondOrder, nNumNeigh, sizeof(nBondOrder[0]), e_CompDble ); + + /* find the longest contiguous sequence of Up bonds */ + if ( num_Up == nNumNeigh ) { + /* all bonds are Up */ + len_Up = cur_len_Up = nNumNeigh; /* added cur_len_Up initialization 1/8/2002 */ + first_Up = 0; + } else { + /* at least one bond is not Up */ + cur_len_Up = len_Up = bPrev_Up = 0; + /* prev. cycle header version --- + for ( i = 0; 1; i ++ ) { + if ( i >= nNumNeigh && !bPrev_Up ) { + break; + } + ----------} */ + /* look at all bonds and continue (circle therough the beginning) as long as the current bond is Up */ + for ( i = 0; i < nNumNeigh || bPrev_Up; i ++ ) { + if ( nBondType[nBondOrder[i % nNumNeigh]] > 0 ) { + if ( bPrev_Up ) { + cur_len_Up ++; /* uncrement number of Up bonds in current contiguous sequence of them */ + } else { + bPrev_Up = 1; /* start new contiguous sequence of Up bonds */ + cur_len_Up = 1; + cur_first_Up = i % nNumNeigh; + } + } else + if ( bPrev_Up ) { /* end of contiguous sequence of Up bonds */ + if ( cur_len_Up > len_Up ) { + first_Up = cur_first_Up; /* store the sequence because it is longer than the ptrvious one */ + len_Up = cur_len_Up; + } + bPrev_Up = 0; + } + } + } + if ( bFixSp3Bug ) { + /* check if the bonds with ordering numbers first_Up+len_Up and first_Up+len_Up+1 */ + /* have identical angles. In this case switch their order to enlarge the Up sequence */ + if ( nNumNeigh - len_Up >= 2 ) { + int next1, next2; + for ( i = 1; i < nNumNeigh - len_Up; i ++ ) { + next2 = (first_Up+len_Up + i) % nNumNeigh; /* the 2nd after Up sequence */ + if ( nBondType[nBondOrder[next2]] > 0 ) { + next1 = (first_Up+len_Up) % nNumNeigh; /* the 1st after Up sequence */ + dAngle = dBondDirection[nBondOrder[next1]] - dBondDirection[nBondOrder[next2]]; + if ( fabs(dAngle) < ZERO_ANGLE ) { + e_inchi_swap( (char*)&nBondOrder[next1], (char*)&nBondOrder[next2], sizeof(nBondOrder[0]) ); + len_Up ++; + break; + } + } + } + } + /* check whether the not-Up bond (located before the found first-Up) has */ + /* same angle as the Up bond that precedes this not-Up bond */ + if ( nNumNeigh - len_Up >= 2 ) { + int next1, next2; + for ( i = 1; i < nNumNeigh - len_Up; i ++ ) { + next2 = (first_Up+nNumNeigh - i - 1 ) % nNumNeigh; /* the 2nd before Up sequence */ + if ( nBondType[nBondOrder[next2]] > 0 ) { + next1 = (first_Up+nNumNeigh-1) % nNumNeigh; /* the 1st before Up sequence */ + dAngle = dBondDirection[nBondOrder[next1]] - dBondDirection[nBondOrder[next2]]; + if ( fabs(dAngle) < ZERO_ANGLE ) { + e_inchi_swap( (char*)&nBondOrder[next1], (char*)&nBondOrder[next2], sizeof(nBondOrder[0]) ); + first_Up = next1; + len_Up ++; + break; + } + } + } + } + } + /* Turn all the bonds around the center so that */ + /* the 1st Up bond has zero radian direction */ + dAlpha = dBondDirection[nBondOrder[first_Up]]; + for ( i = 0; i < nNumNeigh; i ++ ) { + if ( i == nBondOrder[first_Up] ) { + dBondDirection[i] = 0.0; + } else { + dAngle = dBondDirection[i] - dAlpha; + if ( dAngle < 0.0 ) { + dAngle += two_pi; + } + dBondDirection[i] = dAngle; + } + } + + /******************************************************** + * Process particular cases + ********************************************************/ + + switch( nNumNeigh ) { + + /************************ 3 bonds *********************** + */ + case 3: + switch( num_Up ) { + /* -------------------------- 0 Up ------------ */ + case 0: + return T2D_UNDF; + /* -------------------------- 1 Up ------------ */ + case 1: + if ( num_Dn ) { +#ifdef _DEBUG + if ( num_Dn != 1 ) /* debug only */ + return -1; +#endif + ret = (T2D_UNDF | T2D_WARN); + } else { + dAngle = dBondDirection[nBondOrder[(first_Up + 2) % nNumNeigh]] - + dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; + if ( dAngle < 0.0 ) { + dAngle += two_pi; + } + if ( dAngle - one_pi < -MIN_ANGLE || dAngle - one_pi > MIN_ANGLE ) { + ret = T2D_OKAY; + } else { + ret = (T2D_UNDF | T2D_WARN); + } + } + break; + /* -------------------------- 2 Up ------------ */ + case 2: + if ( num_Dn ) { + dAlpha = dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]] - + dBondDirection[nBondOrder[(first_Up ) % nNumNeigh]]; + if ( dAlpha < 0.0 ) { + dAlpha += two_pi; + } + if ( dAlpha > one_pi - MIN_ANGLE ) { + ret = T2D_OKAY; + } else + if ( dAlpha < two_pi / 3.0 - MIN_ANGLE ) { + ret = (T2D_UNDF | T2D_WARN); + } else { + /* angle between 2 Up bonds is between 120 and 180 degrees */ + /* direction of the (Alpha angle bisector) + 180 degrees */ + dBisector = (dBondDirection[nBondOrder[(first_Up ) % nNumNeigh]] + + dBondDirection[nBondOrder[(first_Up + 1 ) % nNumNeigh]] ) / 2.0 - one_pi; + if ( dBisector < 0.0 ) { + dBisector += two_pi; + } + if ( dAlpha < two_pi / 3.0 + MIN_ANGLE ) { + /* dAlpha is inside ( 2pi/3 - eps, 2pi/3 + eps ) interval */ + dLimit = MIN_ANGLE * 3.0 / 2.0; + } else { + dLimit = dAlpha * 3.0 / 2.0 - one_pi; + } + dAngle = dBondDirection[nBondOrder[(first_Up + 2 ) % nNumNeigh]]; + if ( dBisector - dAngle < -dLimit || + dBisector - dAngle > dLimit ) { + ret = (T2D_UNDF | T2D_WARN); + } else { + ret = T2D_OKAY; + } + } + } else { + ret = T2D_OKAY; + } + + break; + /* -------------------------- 3 Up ------------ */ + case 3: + ret = T2D_OKAY; + break; + /* -------------------------- other Up -------- */ + default: + return -1; + } + + break; + + /************************************** 4 bonds ************************** + */ + case 4: + switch( num_Up ) { + /* -------------------------- 0 Up ------------ */ + case 0: + return T2D_UNDF; + /* -------------------------- 1 Up ------------ */ + case 1: + if ( num_Dn ) { + if ( nBondType[nBondOrder[(first_Up + 2) % nNumNeigh]] < 0 ) { + /* + * Up, In Plane, Dn, In Plane. Undefined if angle between + * two In Plane bonds is wuthin pi +/- 2*arcsine(1/sqrt(8)) interval + * That is, 138.5 to 221.4 degrees; for certainty the interval is + * increased by 5.7 degrees at each end to + * 134.8 to 227.1 degrees + */ + dAngle = dBondDirection[nBondOrder[(first_Up + 3) % nNumNeigh]] - + dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; + if ( dAngle < 0.0 ) { + dAngle += two_pi; + } + if ( fabs( dAngle - one_pi ) < dAngleAndPiMaxDiff + MIN_ANGLE ) { + ret = (T2D_UNDF | T2D_WARN); + } else { + ret = T2D_OKAY; + } + } else { + ret = T2D_OKAY; + } +#ifdef _DEBUG + if ( num_Dn != 1 ) /* debug only */ + return -1; +#endif + } else { + ret = T2D_OKAY; + dAngle = dBondDirection[nBondOrder[(first_Up + 3) % nNumNeigh]] - + dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; + if ( dAngle < 0.0 ) { + dAngle += two_pi; + } + if ( dAngle < one_pi - MIN_ANGLE ) { + ret |= T2D_WARN; + } + } + break; + /* -------------------------- 2 Up ------------ */ + case 2: + if ( bFixSp3Bug ) { + /* bug fix */ + if ( len_Up == 1 ) { + ret = T2D_OKAY; + } else { + dAngle = dBondDirection[nBondOrder[(first_Up + 3) % nNumNeigh]] - + dBondDirection[nBondOrder[(first_Up + 0) % nNumNeigh]]; + dAngle = fabs(two_pi - dAngle); + dAlpha = dBondDirection[nBondOrder[(first_Up + 2) % nNumNeigh]] - + dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; + dAlpha = fabs(dAlpha); + if ( dAngle < 2.0 * ZERO_ANGLE && dAlpha > MIN_ANGLE || + dAlpha < 2.0 * ZERO_ANGLE && dAngle > MIN_ANGLE ) { + ret = (T2D_OKAY | T2D_WARN); + } else { + ret = (T2D_UNDF | T2D_WARN); + } + } + } else { + /* original InChI v. 1 bug */ + if ( cur_len_Up == 1 ) { + ret = T2D_OKAY; + } else { + ret = (T2D_UNDF | T2D_WARN); + } + } + break; + + /* -------------------------- 3 Up ------------ */ + case 3: + ret = T2D_OKAY; + dAngle = dBondDirection[nBondOrder[(first_Up + 2) % nNumNeigh]] - + dBondDirection[nBondOrder[(first_Up + 0) % nNumNeigh]]; + if ( dAngle < 0.0 ) { + dAngle += two_pi; + } + if ( dAngle < one_pi - MIN_ANGLE ) { + ret |= T2D_WARN; + } + break; + /* -------------------------- 4 Up ------------ */ + case 4: + ret = (T2D_UNDF | T2D_WARN); + break; + /* -------------------------- other Up -------- */ + default: + return -1; /* program error */ + } + + if ( ret == T2D_OKAY ) { + /* check whether all bonds are inside a less than 180 degrees sector */ + for ( i = 0; i < nNumNeigh; i ++ ) { + dAngle = dBondDirection[nBondOrder[(i + nNumNeigh - 1) % nNumNeigh]] - + dBondDirection[nBondOrder[ i % nNumNeigh]]; + if ( dAngle < 0.0 ) { + dAngle += two_pi; + } + if ( dAngle < one_pi - MIN_ANGLE ) { + ret |= T2D_WARN; + break; + } + } + } + + break; + /*************************** other nuber of bonds ******************/ + default: + return -1; /* error */ + } + + return ret; + +#undef ZERO_ANGLE +} +/*************************************************************/ +double e_triple_prod_and_min_abs_sine2(double at_coord[][3], double central_at_coord[], int bAddedExplicitNeighbor, double *min_sine, int *bAmbiguous) +{ + double min_sine_value=9999.0, sine_value, min_edge_len, max_edge_len, min_edge_len_NoExplNeigh, max_edge_len_NoExplNeigh; + double s0, s1, s2, s3, e01, e02, e03, e12, e13, e23, tmp[3], e[3][3]; + double prod, ret, central_prod[4]; + int bLongEdges; + + if ( !min_sine ) { + return e_triple_prod( at_coord[0], at_coord[1], at_coord[2], NULL ); + } + + ret = e_triple_prod( at_coord[0], at_coord[1], at_coord[2], &sine_value ); + sine_value = MPY_SINE * fabs( sine_value ); + + e_diff3( at_coord[1], at_coord[0], e[2] ); + e_diff3( at_coord[0], at_coord[2], e[1] ); + e_diff3( at_coord[2], at_coord[1], e[0] ); + + /* lengths of the 6 edges of the tetrahedra */ + e03 = e_len3( at_coord[0] ); /* 1 */ + e13 = e_len3( at_coord[1] ); + e23 = e_len3( at_coord[2] ); /* includes added neighbor if bAddedExplicitNeighbor*/ + e02 = e_len3( e[1] ); /* includes added neighbor if bAddedExplicitNeighbor*/ + e12 = e_len3( e[0] ); /* includes added neighbor if bAddedExplicitNeighbor*/ + e01 = e_len3( e[2] ); + + /* min & max edge length */ + max_edge_len = + min_edge_len = e03; + + if ( min_edge_len > e13 ) + min_edge_len = e13; + if ( min_edge_len > e01 ) + min_edge_len = e01; + min_edge_len_NoExplNeigh = min_edge_len; + + if ( min_edge_len > e23 ) + min_edge_len = e23; + if ( min_edge_len > e02 ) + min_edge_len = e02; + if ( min_edge_len > e12 ) + min_edge_len = e12; + + if ( max_edge_len < e13 ) + max_edge_len = e13; + if ( max_edge_len < e01 ) + max_edge_len = e01; + max_edge_len_NoExplNeigh = max_edge_len; + + if ( max_edge_len < e23 ) + max_edge_len = e23; + if ( max_edge_len < e02 ) + max_edge_len = e02; + if ( max_edge_len < e12 ) + max_edge_len = e12; + + if ( !bAddedExplicitNeighbor ) { + min_edge_len_NoExplNeigh = min_edge_len; + max_edge_len_NoExplNeigh = max_edge_len; + } + + bLongEdges = bAddedExplicitNeighbor? + ( max_edge_len_NoExplNeigh < MAX_EDGE_RATIO * min_edge_len_NoExplNeigh ) : + ( max_edge_len < MAX_EDGE_RATIO * min_edge_len ); + + if ( sine_value > MIN_SINE && ( min_sine || bAmbiguous ) ) { + if ( min_sine ) { + prod = fabs( ret ); + /* tetrahedra height = volume(prod) / area of a plane(cross_prod) */ + /* (instead of a tetrahedra calculate parallelogram/parallelepiped area/volume) */ + + /* 4 heights from each of the 4 vertices to the opposite plane */ + s0 = prod / e_len3( e_cross_prod3( at_coord[1], at_coord[2], tmp ) ); + s1 = prod / e_len3( e_cross_prod3( at_coord[0], at_coord[2], tmp ) ); + s2 = prod / e_len3( e_cross_prod3( at_coord[0], at_coord[1], tmp ) ); + s3 = prod / e_len3( e_cross_prod3( e[0], e[1], tmp ) ); + /* abs. value of a sine of an angle between each tetrahedra edge and plane */ + /* sine = height / edge length */ + if ( (sine_value = s0/e01) < min_sine_value ) + min_sine_value = sine_value; + if ( (sine_value = s0/e02) < min_sine_value ) + min_sine_value = sine_value; + if ( (sine_value = s0/e03) < min_sine_value ) + min_sine_value = sine_value; + + if ( (sine_value = s1/e01) < min_sine_value ) + min_sine_value = sine_value; + if ( (sine_value = s1/e12) < min_sine_value ) + min_sine_value = sine_value; + if ( (sine_value = s1/e13) < min_sine_value ) + min_sine_value = sine_value; + + if ( (sine_value = s2/e02) < min_sine_value ) + min_sine_value = sine_value; + if ( (sine_value = s2/e12) < min_sine_value ) + min_sine_value = sine_value; + if ( (sine_value = s2/e23) < min_sine_value ) + min_sine_value = sine_value; + + if ( (sine_value = s3/e03) < min_sine_value ) + min_sine_value = sine_value; + if ( (sine_value = s3/e13) < min_sine_value ) + min_sine_value = sine_value; + if ( (sine_value = s3/e23) < min_sine_value ) + min_sine_value = sine_value; + /* actually use triple sine */ + *min_sine = sine_value = MPY_SINE * min_sine_value; + } + + if ( bAmbiguous && sine_value >= MIN_SINE ) { + /* check whether the central atom is outside the tetrahedra (0,0,0), at_coord[0,1,2] */ + /* compare the tetrahedra volume and the volume of a tetrahedra having central_at_coord[] vertex */ + int i; + e_diff3( central_at_coord, at_coord[0], tmp ); + central_prod[0] = e_triple_prod( at_coord[0], at_coord[1], central_at_coord, NULL ); + central_prod[1] = e_triple_prod( at_coord[1], at_coord[2], central_at_coord, NULL ); + central_prod[2] = e_triple_prod( at_coord[2], at_coord[0], central_at_coord, NULL ); + central_prod[3] = e_triple_prod( e[2], e[1], tmp, NULL ); + for ( i = 0; i <= 3; i ++ ) { + if ( central_prod[i] / ret < -MIN_SINE_OUTSIDE ) { + *bAmbiguous |= AMBIGUOUS_STEREO; + break; + } + } + } +#if( STEREO_CENTER_BONDS_NORM == 1 ) + + if ( bLongEdges && !bAddedExplicitNeighbor && max_edge_len >= MIN_LEN_STRAIGHT ) { + /* possible planar tetragon */ + if ( sine_value < MIN_SINE_SQUARE ) { + *min_sine = MIN_SINE / 2.0; /* force parity to be undefined */ + if ( bAmbiguous && !*bAmbiguous ) { + *bAmbiguous |= AMBIGUOUS_STEREO; + } + } + } + + if ( bLongEdges && sine_value < MIN_SINE_SQUARE && sine_value < MIN_SINE_EDGE * min_edge_len_NoExplNeigh ) { + *min_sine = MIN_SINE / 2.0; /* force parity to be undefined */ + if ( bAmbiguous && !*bAmbiguous ) { + *bAmbiguous |= AMBIGUOUS_STEREO; + } + } +#endif + } else + if ( min_sine ) { + *min_sine = sine_value; + } + + return ret; +} +/*************************************************************/ +double e_triple_prod_and_min_abs_sine(double at_coord[][3], double *min_sine) +{ + double min_sine_value=9999.0, sine_value; + double prod=0.0; + + if ( !min_sine ) { + return e_triple_prod( at_coord[0], at_coord[1], at_coord[2], NULL ); + } + + prod = e_triple_prod( at_coord[0], at_coord[1], at_coord[2], &sine_value ); + sine_value = fabs( sine_value ); + min_sine_value = inchi_min( min_sine_value, sine_value ); + + prod = e_triple_prod( at_coord[1], at_coord[2], at_coord[0], &sine_value ); + sine_value = fabs( sine_value ); + min_sine_value = inchi_min( min_sine_value, sine_value ); + + prod = e_triple_prod( at_coord[2], at_coord[0], at_coord[1], &sine_value ); + sine_value = fabs( sine_value ); + min_sine_value = inchi_min( min_sine_value, sine_value ); + + *min_sine = min_sine_value; + + return prod; +} +/*************************************************************/ +/* Find if point (0,0,0)a and 3 atoms are in one plane */ +int are_3_vect_in_one_plane( double at_coord[][3], double min_sine) +{ + double actual_min_sine; + double prod; + prod = e_triple_prod_and_min_abs_sine( at_coord, &actual_min_sine); + return actual_min_sine <= min_sine; +} +/*************************************************************/ +/* Find if 4 atoms are in one plane */ +int e_are_4at_in_one_plane( double at_coord[][3], double min_sine) +{ + double actual_min_sine, min_actual_min_sine; + double coord[3][3], prod; + int i, k, j; + for ( k = 0; k < 4; k ++ ) { /* cycle added 4004-08-15 */ + for ( i = j = 0; i < 4; i ++ ) { + if ( i != k ) { + e_diff3( at_coord[i], at_coord[k], coord[j] ); + j ++; + } + } + prod = e_triple_prod_and_min_abs_sine( coord, &actual_min_sine); + if ( !k || actual_min_sine < min_actual_min_sine ) { + min_actual_min_sine = actual_min_sine; + } + } + return min_actual_min_sine <= min_sine; +} +/*************************************************************/ +int e_triple_prod_char( inchi_Atom *at, int at_1, int i_next_at_1, S_CHAR *z_dir1, + int at_2, int i_next_at_2, S_CHAR *z_dir2 ) +{ + inchi_Atom *at1, *at2; + double pnt[3][3], len; + int i; + int ret = 0; + + at1 = at + at_1; + at2 = at + at[at_1].neighbor[i_next_at_1]; + + pnt[0][0] = at2->x - at1->x; + pnt[0][1] = at2->y - at1->y; + pnt[0][2] = at2->z - at1->z; + + at2 = at + at_2; + at1 = at + at[at_2].neighbor[i_next_at_2]; + + pnt[1][0] = at2->x - at1->x; + pnt[1][1] = at2->y - at1->y; + pnt[1][2] = at2->z - at1->z; +/* + * resultant pnt vector directions: + * + * pnt[0] pnt[1] + * + * [at_1]---->[...] [...]---->[at_2] + * + * + * e_add3 below: (pnt[0] + pnt[1]) -> pnt[1] + */ + e_add3( pnt[0], pnt[1], pnt[1] ); + + + + for ( i = 0; i < 3; i ++ ) { + pnt[0][i] = (double)z_dir1[i]; + pnt[2][i] = (double)z_dir2[i]; + } + for ( i = 0; i < 3; i ++ ) { + len = e_len3( pnt[i] ); + if ( len < MIN_BOND_LEN ) { + goto exit_function; /* too short bond */ + } + e_mult3( pnt[i], 1.0/len, pnt[i] ); + } + len = 100.0*e_triple_prod(pnt[0], pnt[1], pnt[2], NULL ); +/* + * ^ pnt[0] + * | The orientation on this diagram + * | produces len = -100 + * [at_1]------>[at_2] + * pnt[1] / + * / + * / pnt[2] (up from the plane) + * v + * + * Note: len is invariant upon at_1 <--> at_2 transposition because + * triple product changes sign upon pnt[0]<-->pnt[2] transposition and + * triple product changes sign upon pnt[1]--> -pnt[1] change of direction: + * + * e_triple_prod(pnt[0], pnt[1], pnt[2], NULL ) = + * e_triple_prod(pnt[2], -pnt[1], pnt[0], NULL ) + * + */ + + ret = len >= 0.0? (int)floor(len+0.5) : -(int)floor(0.5-len); + +exit_function: + + return ret; +} + +#ifdef NEVER +/********************************************************************************************/ +int bCanInpAtomBeAStereoCenter( inchi_Atom *at, int cur_at ) +{ + +/************************************************************************************* + * current version + ************************************************************************************* + * Use #define to split the stereocenter description table into parts + * to make it easier to read + * + * --------- 4 single bonds stereocenters ------- + * + * | | | | | | + * -C- -Si- -Ge- -Sn- >As[+] >B[-] + * | | | | | | + */ +#define SZELEM1 "C\000","Si", "Ge", "Sn", "As", "B\000", +#define CCHARGE1 0, 0, 0, 0, 1, -1, +#define CNUMBONDSANDH1 4, 4, 4, 4, 4, 4, +#define CCHEMVALENCEH1 4, 4, 4, 4, 4, 4, +#define CHAS3MEMBRING1 0, 0, 0, 0, 0, 0, +#define CREQUIRDNEIGH1 0, 0, 0, 0, 3, 0, +/* + * --------------- S, Se stereocenters ---------- + * + * | | || | | || + * -S= =S= -S[+] >S[+] -Se= =Se= -Se[+] >Se[+] + * | | | | | | | | + */ +#define SZELEM2 "S\000","S\000","S\000","S\000","Se", "Se", "Se", "Se", +#define CCHARGE2 0, 0, 1, 1, 0, 0, 1, 1, +#define CNUMBONDSANDH2 3, 4, 3, 4, 3, 4, 3, 4, +#define CCHEMVALENCEH2 4, 6, 3, 5, 4, 6, 3, 5, +#define CHAS3MEMBRING2 0, 0, 0, 0, 0, 0, 0, 0, +#define CREQUIRDNEIGH2 3, 3, 3, 3, 3, 3, 3, 3, +/* + * ------------------ N, P stereocenters -------- + * + * X---Y + * | | \ / | | + * =N- >N[+] N >P[+] =P- + * | | | | | + */ +#define SZELEM3 "N\000","N\000","N\000","P\000","P\000", +#define CCHARGE3 0, 1, 0, 1, 0, +#define CNUMBONDSANDH3 4, 4, 3, 4, 4, +#define CCHEMVALENCEH3 5, 4, 3, 4, 5, +#define CHAS3MEMBRING3 0, 0, 1, 0, 0, +#define CREQUIRDNEIGH3 3, 3, 1, 3, 3, + + + + static char szElem[][3]={ SZELEM1 SZELEM2 SZELEM3 }; + static S_CHAR cCharge[]={ CCHARGE1 CCHARGE2 CCHARGE3 }; + static S_CHAR cNumBondsAndH[]={ CNUMBONDSANDH1 CNUMBONDSANDH2 CNUMBONDSANDH3 }; + static S_CHAR cChemValenceH[]={ CCHEMVALENCEH1 CCHEMVALENCEH2 CCHEMVALENCEH3 }; + static S_CHAR cHas3MembRing[]={ CHAS3MEMBRING1 CHAS3MEMBRING2 CHAS3MEMBRING3 }; + static S_CHAR cRequirdNeigh[]={ CREQUIRDNEIGH1 CREQUIRDNEIGH2 CREQUIRDNEIGH3 }; + + static int n = sizeof(szElem)/sizeof(szElem[0]); + /* reqired neighbor types (bitmap): + 0 => check bonds only + 1 => no terminal hydrogen atom neighbors + 2 => no terminal -X and -XH together (don't care the bond type, charge, radical) + (X = tautomeric endpoint atom) + Note: whenever cChemValenceH[] > cNumBondsAndH[] + the tautomeric and/or alternating bonds + are permitted + + */ + int i, ret = 0; + for ( i = 0; i < n; i++ ) { + if ( !strcmp( at[cur_at].elname, szElem[i]) && + at[cur_at].charge == cCharge[i] && + (!at[cur_at].radical || at[cur_at].radical == 1) && + at[cur_at].num_bonds + inchi_NUMH(at,cur_at) == cNumBondsAndH[i] && + at[cur_at].chem_bonds_valence+inchi_NUMH(at,cur_at) == cChemValenceH[i] && + (cHas3MembRing[i]? is_atom_in_3memb_ring( at, cur_at ) : 1) && + e_bInpAtomHasRequirdNeigh ( at, cur_at, cRequirdNeigh[i], cChemValenceH[i]-cNumBondsAndH[i]) ) { + ret = cNumBondsAndH[i]; + break; + } + } + return ret; +} + +#endif /* NEVER */ +/********************************************************************************************/ +int e_bCanInpAtomBeAStereoCenter( int cur_at, S_CHAR *cAtType ) +{ + switch ( cAtType[cur_at] ) { + case AtType_C4 : + case AtType_Si4 : + case AtType_Ge4 : + case AtType_Sn4 : + case AtType_B4m : + case AtType_S6 : + case AtType_S5p : + case AtType_Se6 : + case AtType_Se5p: + case AtType_N5 : + case AtType_N4p : + case AtType_P4p : + case AtType_P5 : + case AtType_As4p: + case AtType_As5 : + return 4; + + case AtType_S3p : + case AtType_Se3p: + case AtType_S4 : + case AtType_Se4 : + case AtType_N3r : + return 3; + } + return 0; +} +/****************************************************************/ +/* used for atoms adjacent to stereogenic bonds only */ +int e_bCanAtomHaveAStereoBond( inchi_Atom *at, int cur_at, S_CHAR *cAtType ) +{ + int i, neigh, nNumFound; + char *p; + static char sNeigh[] = "O;S;Se;Te;"; + switch ( cAtType[cur_at] ) { + case AtType_C3 : + case AtType_Si3: + case AtType_Ge3: + case AtType_Sn3: + case AtType_N3p: + case AtType_N3 : + return 3; + case AtType_Nns: + /* + | + found =N= + + if it has one neighbor =O or =S or =Se or =Te then return 3, otherwise return 1 + + */ + nNumFound = 0; + for ( i = 0; i < at[cur_at].num_bonds; i ++ ) { + if ( at[cur_at].bond_type[i] == INCHI_BOND_TYPE_DOUBLE ) { + neigh = (int)at[cur_at].neighbor[i]; + if ( (p = strstr(sNeigh, at[neigh].elname)) && + ';' == p[strlen(at[neigh].elname)] ) { + nNumFound ++; + } + } + } + return nNumFound==1? 3 : 1; + } + return 0; +} +/****************************************************************/ +int e_bCanAtomBeMiddleAllene( int cur_at, S_CHAR *cAtType ) +{ + switch ( cAtType[cur_at] ) { + case AtType_C2 : + case AtType_Si2: + case AtType_Ge2: + case AtType_Sn2: + return 2; + } + return 0; +} +/****************************************************************/ +int e_bCanAtomBeTerminalAllene( int cur_at, S_CHAR *cAtType ) +{ + switch ( cAtType[cur_at] ) { + case AtType_C3 : + case AtType_Si3: + case AtType_Ge3: + case AtType_Sn3: + return 3; + } + return 0; +} +/*******************************************************************************************/ +int e_FixSb0DParities( inchi_Atom *at, Stereo0D *pStereo, int chain_length, AT_NUM at_middle, + int at_1, int i_next_at_1, S_CHAR z_dir1[], S_CHAR z_dir1NM[], int bOnlyNM1, int bAnomaly1NM, int parity1, int parity1NM, + int at_2, int i_next_at_2, S_CHAR z_dir2[], S_CHAR z_dir2NM[], int bOnlyNM2, int bAnomaly2NM, int parity2, int parity2NM ) +{ + int i_neigh_at_1, i_neigh_at_2, i, j1, j2; + int j_neigh_at_1, j_neigh_at_2, j_next_at_1, j_next_at_2; /* positions after metal removal */ + inchi_Stereo0D *stereo0D = NULL; + int dot_prod_z = 0, dot_prod_zNM = 0, parity, parityNM; + + if ( (!parity1 || !parity2) && (!parity1NM || !parity2NM) ) + return 0; + + if ( ATOM_PARITY_WELL_DEF( parity1 ) && ATOM_PARITY_WELL_DEF( parity2 ) ) { + /* find how the whole bond parity depend on geometry */ + /* if dot_prod_z < 0 then bond_parity := 3-bond_parity */ + /* can be done only for a well-defined geometry */ + /******************************************************************** + * Case of bOnlyNonMetal: stereobond end atoms have valence > 3 + * therefore they will not be recognized until metals have + * been disconnected. Until then the assigned parities will + * not change: ReconcileCmlIncidentBondParities() in the dll + * ignores hypervanlence atoms marked as belonging to a stereobond. + * Explicit H are removed only after metal diconnection. + * + * However, there is a possibility that the normalization + * removes an explicit H thus reducing the valence to acceptable + * value for the bond to be treated as stereogenic. + * In this case a wrong INChI stereolayer for the reconnected + * structure will be produced. + * + * After the metal disconnection ReconcileAllCmlBondParities() + * will be called to reconcile newly appearing stereobonds + ********************************************************************/ + + dot_prod_z = (chain_length%2)? + e_triple_prod_char( at, at_1, i_next_at_1, z_dir1, at_2, i_next_at_2, z_dir2 ) : + e_dot_prodchar3(z_dir1, z_dir2); + + if ( abs(dot_prod_z) < MIN_DOT_PROD ) { + /* The geometry is not well-defined */ + parity1 = parity2 = INCHI_PARITY_UNDEFINED; + dot_prod_z = 0; + } + } + if ( ATOM_PARITY_WELL_DEF( parity1NM ) && ATOM_PARITY_WELL_DEF( parity2NM ) ) { + /* find how the whole bond parity depend on geometry */ + /* if dot_prod_z < 0 then bond_parity := 3-bond_parity */ + /* can be done only for a well-defined geometry */ + /******************************************************************** + * Case of bOnlyNonMetal: stereobond end atoms have valence > 3 + * therefore they will not be recognized until metals have + * been disconnected. Until then the assigned parities will + * not change: ReconcileCmlIncidentBondParities() in the dll + * ignores hypervanlence atoms marked as belonging to a stereobond. + * Explicit H are removed only after metal diconnection. + * + * However, there is a possibility that the normalization + * removes an explicit H thus reducing the valence to acceptable + * value for the bond to be treated as stereogenic. + * In this case a wrong INChI stereolayer for the reconnected + * structure will be produced. + * + * After the metal disconnection ReconcileAllCmlBondParities() + * will be called to reconcile newly appearing stereobonds + ********************************************************************/ + + dot_prod_zNM = (chain_length%2)? + e_triple_prod_char( at, at_1, i_next_at_1, z_dir1NM, at_2, i_next_at_2, z_dir2NM ) : + e_dot_prodchar3(z_dir1NM, z_dir2NM); + + if ( abs(dot_prod_zNM) < MIN_DOT_PROD ) { + /* The geometry is not well-defined */ + parity1NM = parity2NM = INCHI_PARITY_UNDEFINED; + dot_prod_zNM = 0; + } + } + + i_neigh_at_1 = i_neigh_at_2 = -1; + j_neigh_at_1 = j_neigh_at_2 = j_next_at_1 = j_next_at_2 = -1; + + /************************************************************************ + * + * The possibly stereogenic bond is defined by atoms at_1, at_2 and + * positions in their adjacency lists i_next_at_1, i_next_at_2. + * + * We need one more neighbor of at_1 and at_2 to establist cis or trans. + * The current API allows only same neighbors for both connected and + * metal-disconnected (or non-metal, NM) structures. Therefore if an + * atom has both metal and non-metal neighbors, select the non-metal one. + * + * In case of bOnlyNM we have to use ordering numbers of the bonds + * counted as if all metals have been disconnected. + * + * In all other cases we use real ordering numbers of the bonds + * to calculate the parities. + * + * The input parities meaning: + * =========================== + * EVEN=2: the atoms in the adjacency list are arranged clockwise as + * seen from the arrowhead of the z_dir vector + * + * The output parities meaning: + * ============================ + * EVEN=2: trans geometry. For more details see the API description. + * + **************************************************************************/ + + if ( bOnlyNM1 || ATOM_PARITY_WELL_DEF( parity1NM ) ) { + /* disconnected metal case */ + for ( i = j1 = 0; i < at[at_1].num_bonds; i ++ ) { + if ( IS_METAL(pStereo->cAtType[at[at_1].neighbor[i]]) ) { + continue; + } + if ( i == i_next_at_1 ) { + j_next_at_1 = j1; /* position of the stereo bond in adjacency list + when metals are disconnected */ + } else + if ( i_neigh_at_1 < 0 ) { + i_neigh_at_1 = i; /* position of the first non-metal neighbor (metal is connected) */ + j_neigh_at_1 = j1; /* position of the first non-metal neighbor (metal is disconnected) */ + } + j1 ++; + } + } else { + /* no metal case */ + j_neigh_at_1 = i_neigh_at_1 = (i_next_at_1==0); + j_next_at_1 = i_next_at_1; + j1 = at[at_1].num_bonds; + } + + if ( bOnlyNM2 || ATOM_PARITY_WELL_DEF( parity2NM ) ) { + for ( i = j2 = 0; i < at[at_2].num_bonds; i ++ ) { + if ( IS_METAL(pStereo->cAtType[at[at_2].neighbor[i]]) ) { + continue; + } + if ( i == i_next_at_2 ) { + j_next_at_2 = j2; + } else + if ( i_neigh_at_2 < 0 ) { + i_neigh_at_2 = i; + j_neigh_at_2 = j2; + } + j2 ++; + } + } else { + j_neigh_at_2 = i_neigh_at_2 = (i_next_at_2==0); + j_next_at_2 = i_next_at_2; + j2 = at[at_2].num_bonds; + } + + if ( i_neigh_at_1 < 0 || i_neigh_at_2 < 0 || + j_neigh_at_1 < 0 || j_neigh_at_2 < 0 || + j_next_at_1 < 0 || j_next_at_2 < 0 ) { + return 0; /* debugging */ + } + if ( parity1 || parity1NM ) { + /* create a new stereo descriptor */ + stereo0D = e_GetNewStereo( pStereo ); + if ( !stereo0D ) { + return -1; /* error */ + } + /* choose the smallest (earliest in the adjacency list) neighbor of at_1 */ + stereo0D->neighbor[0] = at[at_1].neighbor[i_neigh_at_1]; + /* stereibond atoms */ + stereo0D->neighbor[1] = at_1; + stereo0D->neighbor[2] = at_2; + /* choose the smallest neighbor of at_2 */ + stereo0D->neighbor[3] = at[at_2].neighbor[i_neigh_at_2]; + stereo0D->central_atom = at_middle; + stereo0D->type = (chain_length % 2)? INCHI_StereoType_Allene : INCHI_StereoType_DoubleBond; + + if ( dot_prod_z || dot_prod_zNM ) { + + int i_parity1 = i_neigh_at_1 + i_next_at_1 + (i_neigh_at_1 > i_next_at_1); + int j_parity1 = j_neigh_at_1 + j_next_at_1 + (j_neigh_at_1 > j_next_at_1); + int i_parity2 = i_neigh_at_2 + i_next_at_2 + (i_neigh_at_2 > i_next_at_2); + int j_parity2 = j_neigh_at_2 + j_next_at_2 + (j_neigh_at_2 > j_next_at_2); + + /* regular (connected) parity; parity1 and parity2 are well-defined only together */ + if ( ATOM_PARITY_WELL_DEF( parity1 ) ) { + parity = 2 - ( parity1 + parity2 + (dot_prod_z < 0) + + ((bOnlyNM1)? j_parity1 : i_parity1) + + ((bOnlyNM2)? j_parity2 : i_parity2) ) % 2; + } else { + parity = parity1; + } + + + /* disconnected parity; parity1NM and parity2NM are well-defined only together */ + if ( ATOM_PARITY_WELL_DEF( parity1NM ) ) { + parityNM = 2 - ( parity1NM + parity2NM + (dot_prod_zNM < 0) + + ((bOnlyNM1||bAnomaly1NM)? j_parity1 : i_parity1) + + ((bOnlyNM2||bAnomaly2NM)? j_parity2 : i_parity2) ) % 2; + } else { + parityNM = parity1NM; + } + } else { + parity = parity1; + parityNM = parity1NM; + } + stereo0D->parity = parity; + if ( parityNM != parity || bAnomaly1NM || bAnomaly2NM ) { + stereo0D->parity |= (parityNM << SB_PARITY_SHFT); + } + + return stereo0D->parity; + } + return 0; +} +/**************************************************************************/ +int e_nNumNonMetalNeigh( inchi_Atom *atom, int cur_at, Stereo0D *pStereo, int *i_ord_LastMetal ) +{ + int j, nNumNonMetal; + inchi_Atom *at = atom + cur_at; + int val = at->num_bonds; + int atype; + + *i_ord_LastMetal = -1; + nNumNonMetal = 0; + + atype = pStereo->cAtType[cur_at]; + + if ( IS_METAL(atype) ) + return 0; + for ( j = 0; j < val; j ++ ) { + atype = pStereo->cAtType[(int)at->neighbor[j]]; + if ( IS_METAL(atype) ) + *i_ord_LastMetal = j; + else + nNumNonMetal ++; + } + return nNumNonMetal; +} +/*====================================================================================================== + +e_half_stereo_bond_parity() General Description: + + A) find projections of 3 bonds on a reasonable plane defined + by a vector z_dir perpendicular to the plane + B) calculate parity (parity=2[EVEN]: neighbors are clockwise as seen from the arrowhead of z_dir[] vector) + +e_half_stereo_bond_parity() Detailed Description: + + 1) Find at_coord[] = vectors from the central atoms to its neighbors + 2) If only 2 neighbors are present, then create a reasonable 3rd neighbor + (an implicit H or a fictitious atom in case of =NX) coordinates + 3) Normalize at_coord[] to unit length + 4) Find unit vector pnt[2] perpendicular to the plane containing + at_coord[] arrow ends. + Even though it is not necessary, make z-coordinate of pnt[2] positive. + ** pnt[2] has the new z-axis direction ** + 5) Let pnt[0] = perpendicular to pnt[2] component of at_coord[0]; + Normalize pnt[0] to unit length. + ** pnt[0] has the new x-axis direction ** + 6) Let pnt[1] = pnt[2] x pnt[0] (cross-product); + ** pnt[1] has the new y-axis direction ** + 7) Find at_coord[] in the new xyz-basis and normalize their xy-projections + to a unit length + 8) In the new xy-plane find (counterclockwise) angles: + tmp1 = (from at_coord[0] to at_coord[1]) + tmp2 = (from at_coord[0] to at_coord[2]) + 9) Calculate the parity: if tmp1 < tmp2 then 1 (odd) else 2 (even) + (even: looking from the arrow end of the new z-axis, 0, 1, and 2 neighbors + are in clockwise order) + 10) Calculate z_dir = 100*pnt[2]. + + Note1. If z_dir vectors of atoms located at the opposite ends of a double bond have approximately + opposite directions (that is, their dot-product is negative) then the parity of the + stereogenic bond calculated from half-bond-parities should be inverted + + Note2. In case of a tetrahedral cumulene a triple product (z_dir1, (1->2), z_dir2) is used instead + of the dot-product. (1->2) is a vector from the atom#1 to the atom #2. This triple product + is invariant with respect to the atom numbering because it does not change upon (1,2) + permutation. + + Stereo ambiguity in case of 2 neighbors: + ---------------------------------------- + Undefined: single-double bond angle > pi - arcsin(0.03) = 178.28164199834454285275613218975 degrees + Ambiguous: single-double bond angle > 175 degrees = pi - 0.087156 Rad + + Return values + (cases: I=only in case of isotopic H atoms the neighbors are different, + N=in case of non-isotopic H atoms the neighbors are different) + + -4 = INCHI_PARITY_UNDEFINED => atom is adjacent to a stereogenic bond, but the geometry is undefined, I + -3 = INCHI_PARITY_UNKNOWN => atom is adjacent to a stereogenic bond, but the geometry is not known to the iuser, I + -2 =-INCHI_PARITY_EVEN => parity of an atom adjacent to a stereogenic bond, I + -1 =-INCHI_PARITY_ODD => parity of an atom adjacent to a stereogenic bond, I + 0 = INCHI_PARITY_NONE => the atom is not adjacent to a stereogenic bond + 1 = INCHI_PARITY_ODD => parity of an atom adjacent to a stereogenic bond, N&I + 2 = INCHI_PARITY_EVEN => parity of an atom adjacent to a stereogenic bond, N&I + 3 = INCHI_PARITY_UNKNOWN => atom is adjacent to a stereogenic bond, but the geometry is not known to the iuser, N&I + 4 = INCHI_PARITY_UNDEFINED => atom is adjacent to a stereogenic bond, but the geometry is undefined, N&I + + +=====================================================================================================*/ + +int e_half_stereo_bond_parity( inchi_Atom *at, int cur_at, S_CHAR *z_dir, int *bOnlyNonMetal, + int bPointedEdgeStereo, Stereo0D *pStereo ) +{ + double at_coord[MAX_NUM_STEREO_BOND_NEIGH][3], c, s, tmp[3], tmp1, tmp2, min_tmp, max_tmp, z; + double temp[3], pnt[3][3]; + int j, k, p0, p1, p2, next, num_z, nType, num_either_single, num_either_double; + int nNumExplictAttachments; + int bond_parity = INCHI_PARITY_UNDEFINED; + int num_H=0, num_eH=0, num_nH=0 /* = num_iso_H[0] */; + const double one_pi = 2.0*atan2(1.0 /* y */, 0.0 /* x */); + const double two_pi = 2.0*one_pi; + AT_NUM nSbNeighOrigAtNumb[MAX_NUM_STEREO_BOND_NEIGH]; + int bAmbiguousStereo = 0; + S_CHAR num_iso_eH[NUM_H_ISOTOPES+1]; /* count explicit H */ + + if ( z_dir && !z_dir[0] && !z_dir[1] && !z_dir[2] ) { + z_dir[2]=100; + } + /* *bOnlyNonMetal = 0; */ + if ( at[cur_at].num_bonds > MAX_NUM_STEREO_BOND_NEIGH || *bOnlyNonMetal ) { + int i_ord_LastMetal; + int nNumNonMetal = e_nNumNonMetalNeigh( at, cur_at, pStereo, &i_ord_LastMetal ); + if ( nNumNonMetal <= MAX_NUM_STEREO_BOND_NEIGH ) { + *bOnlyNonMetal = 1; + if ( 1 == nNumNonMetal ) { + return bond_parity; /* INCHI_PARITY_UNDEFINED */ + } + if ( 0 == nNumNonMetal ) { + return INCHI_PARITY_NONE; + } + } else { + return INCHI_PARITY_NONE; + } + } + memset(num_iso_eH, 0, sizeof(num_iso_eH)); + for ( j = 0; j < at[cur_at].num_bonds; j ++ ) { + next = at[cur_at].neighbor[j]; + switch( pStereo->cAtType[next] ) { + case AtType_TermH: + if ( 0 <= at[next].isotopic_mass && at[next].isotopic_mass <= NUM_H_ISOTOPES ) { + num_iso_eH[at[next].isotopic_mass] ++; + } else { + num_iso_eH[0] ++; + } + num_eH ++; + break; + case AtType_TermD: + num_iso_eH[2] ++; + num_eH ++; + break; + case AtType_TermT: + num_iso_eH[3] ++; + num_eH ++; + break; + } + } + + + num_H = inchi_NUMH2(at,cur_at) + num_eH; + if ( num_H > NUM_H_ISOTOPES ) + return 0; /* at least 2 H atoms are isotopically identical */ + + for ( j = 0, num_nH = num_H; j < NUM_H_ISOTOPES; j ++ ) { + if ( (k = (int)at[cur_at].num_iso_H[j+1]+(int)num_iso_eH[j+1]) > 1 ) { + return INCHI_PARITY_NONE; /* two or more identical isotopic H atoms */ + } + num_nH -= k; + } + /* at this point num_nH = number of non-isotopic H atoms */ + if ( num_nH > 1 ) + return INCHI_PARITY_NONE; /* two or more identical non-isotopic H atoms */ + if ( num_nH < 0 ) + return CT_ISO_H_ERR; /* program error */ /* */ + + /******************************************************************** + * Note. At this point all (implicit and explicit) isotopic + * terminal H neighbors are either different or not present. + ********************************************************************/ + + /* store neighbors coordinates */ + num_z = num_either_single = num_either_double = 0; + nNumExplictAttachments = 0; + for ( j = 0; j < at[cur_at].num_bonds; j ++ ) { + next = at[cur_at].neighbor[j]; + if ( *bOnlyNonMetal && IS_METAL(pStereo->cAtType[next]) ) + continue; + at_coord[nNumExplictAttachments][0] = at[next].x - at[cur_at].x; + at_coord[nNumExplictAttachments][1] = at[next].y - at[cur_at].y; + nSbNeighOrigAtNumb[nNumExplictAttachments] = next; + + z = e_get_z_coord( at, cur_at, j /*neighbor #*/, &nType, bPointedEdgeStereo ); + switch ( nType ) { + case ZTYPE_EITHER: + num_either_single ++; /* bond in "Either" direction. */ + break; + case ZTYPE_UP: + case ZTYPE_DOWN: + z = e_len2( at_coord[nNumExplictAttachments] ); + /* + z = sqrt( at_coord[nNumExplictAttachments][0]*at_coord[nNumExplictAttachments][0] + + at_coord[nNumExplictAttachments][1]*at_coord[nNumExplictAttachments][1] ); + */ + if ( nType == ZTYPE_DOWN ) + z = -z; + /* no break; here */ + case ZTYPE_3D: + num_z ++; + } + at_coord[nNumExplictAttachments][2] = z; + nNumExplictAttachments ++; + } + + if ( num_either_single ) { + bond_parity = INCHI_PARITY_UNKNOWN; /* single bond is 'unknown' */ + goto exit_function; + } + + /* nNumExplictAttachments is a total number of attachments */ + if ( nNumExplictAttachments == 2 ) { + /* create coordinates of the implicit hydrogen (or a fictitious atom in case of ==N-X ), */ + /* coord[2][], attached to the cur_at. */ + for ( j = 0; j < 3; j ++ ) { + at_coord[2][j] = - ( at_coord[0][j] + at_coord[1][j] ); + } + nSbNeighOrigAtNumb[nNumExplictAttachments] = -1; /* implicit H or lone pair */ + } + for ( j = 0; j < 3; j ++ ) { + tmp[j] = e_len3( at_coord[j] ); + } + min_tmp = inchi_min( tmp[0], inchi_min(tmp[1], tmp[2]) ); + max_tmp = inchi_max( tmp[0], inchi_max(tmp[1], tmp[2]) ); + if ( min_tmp < MIN_BOND_LEN || min_tmp < MIN_SINE*max_tmp ) { + /* all bonds or some of bonds are too short */ + goto exit_function; + } + /* normalize lengths to 1 */ + for ( j = 0; j < 3; j ++ ) { + e_mult3( at_coord[j], 1.0/tmp[j], at_coord[j] ); + } + + /* find projections of at_coord vector differences on the plane containing their arrowhead ends */ + for ( j = 0; j < 3; j ++ ) { + /* pnt[0..2] = {0-1, 1-2, 2-0} */ + tmp[j] = e_len3(e_diff3( at_coord[j], at_coord[(j+1)%3], pnt[j] )); + if ( tmp[j] < MIN_SINE ) { + goto exit_function; /* angle #i-cur_at-#j is too small */ + } + e_mult3( pnt[j], 1.0/tmp[j], pnt[j] ); /* 2003-10-06 */ + } + /* find pnt[p2], a vector perpendicular to the plane, and its length tmp[p2] */ + /* replace previous pnt[p2], tmp[p2] with new values; the old values do not have any additional */ + /* information because pnt[p0]+pnt[p1]+pnt[p2]=0 */ + /* 10-6-2003: a cross-product of one pair pnt[j], pnt[(j+1)%3] can be very small. Find the larges one */ + tmp1 = e_len3( e_cross_prod3( pnt[0], pnt[1], temp ) ); + for (j = 1, k = 0; j < 3; j ++ ) { + tmp2 = e_len3( e_cross_prod3( pnt[j], pnt[(j+1)%3], temp ) ); + if ( tmp2 > tmp1 ) { + tmp1 = tmp2; + k = j; + } + } + /* previously p0=0, p1=1, p2=2 */ + p0 = k; + p1 = (k+1)%3; + p2 = (k+2)%3; + tmp[p2] = e_len3( e_cross_prod3( pnt[p0], pnt[p1], pnt[p2] ) ); + if ( tmp[p2] < MIN_SINE*tmp[p0]*tmp[p1] ) { + goto exit_function; /* pnt[p0] is almost colinear to pnt[p1] */ + } + /* new basis: pnt[p0], pnt[p1], pnt[p2]; set z-coord sign and make abs(pnt[p2]) = 1 */ + e_mult3( pnt[p2], (pnt[p2][2]>0.0? 1.0:-1.0)/tmp[p2], pnt[p2] ); /* unit vector in the new z-axis direction */ + + min_tmp = e_dot_prod3( at_coord[0], pnt[p2] ); /* non-planarity measure (sine): hight of at_coord[] pyramid */ + e_mult3( pnt[p2], min_tmp, pnt[p0] ); /* vector height of the pyramid, ideally 0 */ + /* find new pnt[p0] = projection of at_coord[p0] on plane orthogonal to pnt[p2] */ + tmp[p0] = e_len3(e_diff3( at_coord[0], pnt[p0], pnt[p0] )); + e_mult3( pnt[p0], 1.0/tmp[p0], pnt[p0] ); /* new x axis basis vector */ + e_cross_prod3( pnt[p2], pnt[p0], pnt[p1] ); /* new y axis basis vector */ + /* find at_coord in the new basis of {pnt[p0], pnt[p1], pnt[p2]} */ + for ( j = 0; j < 3; j ++ ) { + e_copy3( at_coord[j], temp ); + for ( k = 0; k < 3; k ++ ) { + at_coord[j][k] = e_dot_prod3( temp, pnt[(k+p0)%3] ); + } + /* new xy plane projection length */ + tmp[j] = sqrt(at_coord[j][0]*at_coord[j][0] + at_coord[j][1]*at_coord[j][1]); + /* make new xy plane projection length = 1 */ + e_mult3( at_coord[j], 1.0/tmp[j], at_coord[j] ); + } + + s = fabs( at_coord[1][0]*at_coord[2][1] - at_coord[1][1]*at_coord[2][0] ); /* 1-2 sine */ + c = at_coord[1][0]*at_coord[2][0] + at_coord[1][1]*at_coord[2][1]; /* 1-2 cosine */ + if ( s < MIN_SINE && c > 0.5 ) { + goto exit_function; /* bonds to neigh. 1 and 2 have almost same direction; relative angles are undefined */ + } + c = at_coord[0][0]; /* cosine of the angle between new Ox axis and a bond to the neighbor 0. Should be 1 */ + s = at_coord[0][1]; /* sine. Should be 0 */ + /* turn vectors so that vector #1 (at_coord[0]) becomes {1, 0} */ + for ( j = 0; j < MAX_NUM_STEREO_BOND_NEIGH; j ++ ) { + tmp1 = c*at_coord[j][0] + s*at_coord[j][1]; + tmp2 = -s*at_coord[j][0] + c*at_coord[j][1]; + at_coord[j][0] = tmp1; + at_coord[j][1] = tmp2; + } + /* counterclockwise angles from the direction to neigh 0 to to directions to neighbors 1 and 2: */ + tmp1 = atan2( at_coord[1][1], at_coord[1][0] ); /* range -pi and +pi */ + tmp2 = atan2( at_coord[2][1], at_coord[2][0] ); + if ( tmp1 < 0.0 ) + tmp1 += two_pi; /* range 0 to 2*pi */ + if ( tmp2 < 0.0 ) + tmp2 += two_pi; + /*----------------------------------- + Example + 1 \ case tmp1 < tmp2 + \ parity is odd + \ (counterclockwise) + A------- 0 + / + / + 2 / + + ------------------------------------*/ + bond_parity = 2 - ( tmp1 < tmp2 ); + for ( j = 0; j < 3; j ++ ) { + z_dir[j] = (S_CHAR)(pnt[p2][j]>= 0.0? floor(0.5 + 100.0 * pnt[p2][j]) : + -floor(0.5 - 100.0 * pnt[p2][j])); /* abs(z_dir) = 100 */ + } + /* check for ambiguity */ + if ( nNumExplictAttachments > 2 ) { + min_tmp = inchi_min( tmp1, tmp2 ); + max_tmp = inchi_max( tmp1, tmp2 ); + if ( min_tmp > one_pi-MIN_SINE || max_tmp < one_pi+MIN_SINE || max_tmp-min_tmp > one_pi - MIN_SINE ) { + bAmbiguousStereo |= AMBIGUOUS_STEREO; + } else /* 3D ambiguity 8-28-2002 */ + if ( fabs(at_coord[0][2]) > MAX_SINE ) { /* all fabs(at_coord[j][2] (j=0..2) must be equal */ + bAmbiguousStereo |= AMBIGUOUS_STEREO; + } + } else + if ( nNumExplictAttachments == 2 ) { /* 10-6-2003: added */ + min_tmp = fabs(tmp1 - one_pi); + if ( min_tmp < MIN_SINE ) { + bond_parity = INCHI_PARITY_UNDEFINED; /* consider as undefined 10-6-2003 */ + } else + if ( min_tmp < MIN_ANGLE_DBOND ) { + bAmbiguousStereo |= AMBIGUOUS_STEREO; + } + } + + /* for 3 neighbors moving implicit H to the index=0 from index=2 position */ + /* can be done in 2 transpositions and does not change atom's parity */ + +exit_function: + return bond_parity; +} + +#define MAX_ALLENE_LEN 20 +/*************************************************************/ +int e_set_stereo_bonds_parity( Stereo0D *pStereo, inchi_Atom *at, int at_1, int bPointedEdgeStereo ) +{ + int j, k, next_at_1, i_next_at_1, i_next_at_2, at_2, next_at_2, num_stereo_bonds, bFound, bAllene; + int bond_type, num_2s_1, num_alt_1, bNxtStereobond, bCurStereobond, bNxtCumulene; + int num_stored_stereo_bonds, num_stored_isotopic_stereo_bonds; + int chain_length, num_chains, cur_chain_length; + int all_at_2[MAX_NUM_STEREO_BONDS]; + int all_pos_1[MAX_NUM_STEREO_BONDS], all_pos_2[MAX_NUM_STEREO_BONDS]; + S_CHAR all_unkn[MAX_NUM_STEREO_BONDS], all_chain_len[MAX_NUM_STEREO_BONDS]; + int all_middle_at[MAX_NUM_STEREO_BONDS]; + AT_NUM chain_atoms[MAX_ALLENE_LEN], at_middle; + int nUnknown; + int bOnlyNM1, bOnlyNM2; + + bCurStereobond = e_bCanAtomHaveAStereoBond( at, at_1, pStereo->cAtType ); + if ( !bCurStereobond ) + return 0; + + /* count bonds and find the second atom on the stereo bond */ + num_2s_1 = num_alt_1 = 0; + chain_length = 0; + num_chains = 0; + for ( i_next_at_1 = 0, num_stereo_bonds = 0; i_next_at_1 < at[at_1].num_bonds; i_next_at_1 ++ ) { + + at_2 = next_at_1 = at[at_1].neighbor[i_next_at_1]; + bond_type = at[at_1].bond_type[i_next_at_1]; + nUnknown = (at[at_1].bond_stereo[i_next_at_1] == INCHI_PARITY_UNKNOWN); + next_at_2 = at_1; + + bNxtStereobond = e_bCanAtomHaveAStereoBond( at, at_2, pStereo->cAtType ); + bNxtCumulene = 0; + cur_chain_length = 0; + + if ( bNxtStereobond + bCurStereobond > 3 ) { + /* this includes single (ring) bonds in -NH-CH=N(+)< that may be deprotonated to -N=C-N< */ + if ( bond_type == INCHI_BOND_TYPE_TRIPLE ) { + continue; + } + /* possibly stereogenic bond */ + } else { + bNxtCumulene = + e_bCanAtomBeMiddleAllene(at_2, pStereo->cAtType ) && + e_bCanAtomBeTerminalAllene( at_1, pStereo->cAtType ); + if ( !bNxtCumulene ) { + continue; + } + if ( bond_type != INCHI_BOND_TYPE_DOUBLE ) { + continue; + } + /* possibly allene or cumulenr */ + chain_atoms[cur_chain_length] = at_1; + chain_atoms[cur_chain_length+1] = at_2; + + /* + * Example of cumulene + * chain length = 2: >X=C=C=Y< + * | | | | + * 1st cumulene atom= at_1 | | at_2 =last cumlene chain atom + * next to at_1= next_at_1 next_at_2 =previous to at_2 + * + * chain length odd: stereocenter on the middle atom ( 1=> allene ) + * chain length even: "long stereogenic bond" + */ + while ( cur_chain_length < MAX_ALLENE_LEN-3 && + (bAllene = + e_bCanAtomBeMiddleAllene( at_2, pStereo->cAtType ))) { + k = ((int)at[at_2].neighbor[0]==next_at_2); /* opposite neighbor position */ + next_at_2 = at_2; + nUnknown += (at[at_2].bond_stereo[k] == INCHI_BOND_STEREO_DOUBLE_EITHER); + at_2 = (int)at[at_2].neighbor[k]; + cur_chain_length ++; /* count =C= atoms */ + chain_atoms[cur_chain_length+1] = at_2; + } + if ( cur_chain_length ) { + if ( bAllene /* at the end of the chain atom Y is =Y=, not =Y< or =Y- */ || + !e_bCanAtomBeTerminalAllene( at_2, pStereo->cAtType)) { + cur_chain_length = 0; + continue; /* ignore: does not fit cumulene description; go to check next at_1 neighbor */ + } + } + if ( !cur_chain_length && + !e_bCanAtomHaveAStereoBond( at, at_2, pStereo->cAtType ) ) { + continue; /* reject non-stereogenic bond to neighbor #i_next_at_1 */ + } + } + + /* check atom at the opposite end of possibly stereogenic bond */ + + bFound = ( at_1 > at_2 ); /* i_next_at_1 = at_1 stereogenic bond neighbor attachment number */ + + if ( bFound ) { + i_next_at_2 = -1; /* unassigned mark */ + for ( j = 0; j < at[at_2].num_bonds; j ++ ) { + if ( (int)at[at_2].neighbor[j] == next_at_2 ) { + i_next_at_2 = j; /* assigned */ + break; + } + } + if ( i_next_at_2 < 0 ) { + continue; + } + /* store the results */ + all_pos_1[num_stereo_bonds] = i_next_at_1; /* neighbor to at_1 position */ + all_pos_2[num_stereo_bonds] = i_next_at_2; /* neighbor to at_2 position */ + all_at_2[num_stereo_bonds] = at_2; /* at_2 */ + all_unkn[num_stereo_bonds] = nUnknown; /* stereogenic bond has Unknown configuration */ + /* allene/cumulene stuff */ + all_chain_len[num_stereo_bonds] = cur_chain_length; + all_middle_at[num_stereo_bonds] = (cur_chain_length%2)? chain_atoms[(cur_chain_length+1)/2] : -1; + num_stereo_bonds ++; + } + } + if ( !num_stereo_bonds || num_stereo_bonds > 3 /*|| num_chains > 1*/ ) { + return 0; /* At the end, we cannot be more than 1 cumulene chain. */ + } + + /* ================== calculate parities ====================== */ + /* find possibly stereo bonds and save them */ + num_stored_isotopic_stereo_bonds = 0; + num_stored_stereo_bonds = 0; + for ( k = 0; k < num_stereo_bonds; k ++ ) { + /* NM stands for non-metal */ + int parity_at_1, parity_at_2, parity_at_1NM=0, parity_at_2NM=0, bOnlyNM; + S_CHAR z_dir1[3], z_dir2[3], z_dir1NM[3], z_dir2NM[3]; /* 3D vectors for half stereo bond parity direction */ + int i_ord_LastMetal1, i_ord_LastMetal2, bAnomaly1NM, bAnomaly2NM; + + at_2 = all_at_2[k]; + i_next_at_1 = all_pos_1[k]; + i_next_at_2 = all_pos_2[k]; + nUnknown = all_unkn[k]; + at_middle = all_middle_at[k]; + cur_chain_length = all_chain_len[k]; + memset(z_dir1, 0, sizeof(z_dir1)); + memset(z_dir2, 0, sizeof(z_dir2)); + memset(z_dir1NM, 0, sizeof(z_dir1NM)); + memset(z_dir2NM, 0, sizeof(z_dir2NM)); + bOnlyNM1 = bOnlyNM2 = 0; + bAnomaly1NM = bAnomaly2NM = 0; + i_ord_LastMetal1 = i_ord_LastMetal2 = -1; + + if ( nUnknown ) { + parity_at_1 = parity_at_2 = parity_at_1NM = parity_at_2NM = INCHI_PARITY_UNKNOWN; + } else { + /*********************************************************************** + * + * Obtain each atom parity from the geometry + * + * Warning: case when dot-product + * (z_dir1,z_dir1NM) < 0 or (z_dir2,z_dir2NM) < 0 + * is not treated proprly. + * This case may happen especially when z_dir?[3] << 100 + * + ***********************************************************************/ + + parity_at_1 = e_half_stereo_bond_parity( at, at_1, z_dir1, &bOnlyNM1, bPointedEdgeStereo, pStereo ); + parity_at_2 = e_half_stereo_bond_parity( at, at_2, z_dir2, &bOnlyNM2, bPointedEdgeStereo, pStereo ); + + if ( RETURNED_ERROR(parity_at_1) || RETURNED_ERROR(parity_at_2) ) { + return CT_CALC_STEREO_ERR; + } + parity_at_1 = abs(parity_at_1); + parity_at_2 = abs(parity_at_2); + /* bond parities for the disconnected structure */ + if ( bOnlyNM1 ) { + parity_at_1NM = parity_at_1; + memcpy( z_dir1NM, z_dir1, sizeof(z_dir1NM) ); + parity_at_1 = INCHI_PARITY_NONE; + } else + if ( at[at_1].num_bonds == e_nNumNonMetalNeigh( at, at_1, pStereo, &i_ord_LastMetal1 ) ) { + parity_at_1NM = parity_at_1; /* no metal bond present */ + memcpy( z_dir1NM, z_dir1, sizeof(z_dir1NM) ); + } else { + bOnlyNM = 1; /* at_1 has a metal neighbor; in adjacency list + it is located in (zero-based) position i_ord_LastMetal1 */ + parity_at_1NM = e_half_stereo_bond_parity( at, at_1, z_dir1NM, &bOnlyNM, + bPointedEdgeStereo, pStereo ); + if ( RETURNED_ERROR(parity_at_1NM) ) { + return CT_CALC_STEREO_ERR; + } + parity_at_1NM = abs(parity_at_1NM); + /* + if ( ATOM_PARITY_WELL_DEF(parity_at_1NM) && !ATOM_PARITY_WELL_DEF(parity_at_1) ) { + memcpy( z_dir1, z_dir1NM, sizeof(z_dir1) ); + } + */ + if ( ATOM_PARITY_WELL_DEF(parity_at_1NM) && ATOM_PARITY_WELL_DEF(parity_at_1) && + 0 <= i_ord_LastMetal1 ) { + if ( 1 == i_ord_LastMetal1 % 2 && parity_at_1NM == parity_at_1 || + 0 == i_ord_LastMetal1 % 2 && parity_at_1NM != parity_at_1 ) { + /* abnormal geometry: all bonds in a sector < 180 degrees */ + /*parity_at_1NM = 3 - parity_at_1;*/ + bAnomaly1NM = 1; + } else { + parity_at_1 = parity_at_1NM; + bOnlyNM1 = 1; + } + } else + if ( ATOM_PARITY_WELL_DEF(parity_at_1NM) && !ATOM_PARITY_WELL_DEF(parity_at_1) && + 0 <= i_ord_LastMetal1 ) { + bAnomaly1NM = 1; /* 2005-02-01 force atom parity from non-metal neighbors */ + } + } + + if ( bOnlyNM2 ) { + parity_at_2NM = parity_at_2; + memcpy( z_dir2NM, z_dir2, sizeof(z_dir2NM) ); + parity_at_2 = INCHI_PARITY_NONE; + } else + if ( at[at_2].num_bonds == e_nNumNonMetalNeigh( at, at_2, pStereo, &i_ord_LastMetal2 ) ) { + memcpy( z_dir2NM, z_dir2, sizeof(z_dir2NM) ); + parity_at_2NM = parity_at_2; /* no metal bond present */ + } else { + bOnlyNM = 1; + parity_at_2NM = e_half_stereo_bond_parity( at, at_2, z_dir2NM, &bOnlyNM, + bPointedEdgeStereo, pStereo ); + if ( RETURNED_ERROR(parity_at_2NM) ) { + return CT_CALC_STEREO_ERR; + } + parity_at_2NM = abs(parity_at_2NM); + if ( ATOM_PARITY_WELL_DEF(parity_at_2NM) && ATOM_PARITY_WELL_DEF(parity_at_2) && + 0 <= i_ord_LastMetal2 ) { + if ( 1 == i_ord_LastMetal2 % 2 && parity_at_2NM == parity_at_2 || + 0 == i_ord_LastMetal2 % 2 && parity_at_2NM != parity_at_2 ) { + /* abnormal geometry: all bonds in a sector < 180 degrees */ + /*parity_at_2NM = 3 - parity_at_2;*/ + bAnomaly2NM = 1; + } else { + parity_at_2 = parity_at_2NM; + bOnlyNM2 = 1; + } + } else + if ( ATOM_PARITY_WELL_DEF(parity_at_2NM) && !ATOM_PARITY_WELL_DEF(parity_at_2) && + 0 <= i_ord_LastMetal2 ) { + bAnomaly2NM = 1; /* 2005-02-01 force atom parity from non-metal neighbors */ + } + } + /* make both atoms have non-metal neighbors only */ + if ( (bOnlyNM1 || bAnomaly1NM) && ATOM_PARITY_WELL_DEF(parity_at_1NM) && + !(bOnlyNM2 || bAnomaly2NM) && ATOM_PARITY_WELL_DEF(parity_at_2NM) && + 0 <= i_ord_LastMetal2 ) { + + bAnomaly2NM = 1; + parity_at_2NM = 2 - (parity_at_2NM + i_ord_LastMetal2) % 2; + } else + if ( !(bOnlyNM1 || bAnomaly1NM) && ATOM_PARITY_WELL_DEF(parity_at_1NM) && + (bOnlyNM2 || bAnomaly2NM) && ATOM_PARITY_WELL_DEF(parity_at_2NM) && + 0 <= i_ord_LastMetal1 ) { + + bAnomaly1NM = 1; + parity_at_1NM = 2 - (parity_at_1NM + i_ord_LastMetal1) % 2; + } + + /* consistency */ + if ( parity_at_1 == INCHI_PARITY_NONE || parity_at_2 == INCHI_PARITY_NONE ) { + parity_at_1 = parity_at_2 = INCHI_PARITY_NONE; /* =zero */ + } else { + switch( !ATOM_PARITY_WELL_DEF( parity_at_1 ) + 2*!ATOM_PARITY_WELL_DEF( parity_at_2 ) ) { + case 1: + parity_at_2 = parity_at_1; /* set both to not-well-def */ + break; + case 2: + parity_at_1 = parity_at_2; /* set both to not-well-def */ + break; + case 3: + parity_at_1 = parity_at_2 = inchi_min(parity_at_1, parity_at_2); + break; + } + } + + if ( parity_at_1NM == INCHI_PARITY_NONE || parity_at_2NM == INCHI_PARITY_NONE ) { + parity_at_1NM = parity_at_2NM = INCHI_PARITY_NONE; /* =zero */ + } else { + switch( !ATOM_PARITY_WELL_DEF( parity_at_1NM ) + 2*!ATOM_PARITY_WELL_DEF( parity_at_2NM ) ) { + case 1: + parity_at_2NM = parity_at_1NM; /* set both to not-well-def */ + break; + case 2: + parity_at_1NM = parity_at_2NM; /* set both to not-well-def */ + break; + case 3: + parity_at_1NM = parity_at_2NM = inchi_min(parity_at_1NM, parity_at_2NM); + break; + } + } + if ( parity_at_1 != INCHI_PARITY_NONE && parity_at_1NM == INCHI_PARITY_NONE ) { + parity_at_1NM = parity_at_2NM = INCHI_PARITY_UNDEFINED; + } else + if ( parity_at_1 == INCHI_PARITY_NONE && parity_at_1NM != INCHI_PARITY_NONE ) { + parity_at_1 = parity_at_2 = INCHI_PARITY_UNDEFINED; + } + + + + if ( (parity_at_1 == INCHI_PARITY_UNDEFINED || parity_at_1 == INCHI_PARITY_NONE) && + (parity_at_1NM == INCHI_PARITY_UNDEFINED || parity_at_1NM == INCHI_PARITY_NONE) ) { + continue; + } + } + parity_at_1 = e_FixSb0DParities( at, pStereo, cur_chain_length, at_middle, + at_1, i_next_at_1, z_dir1, z_dir1NM, bOnlyNM1, bAnomaly1NM, parity_at_1, parity_at_1NM, + at_2, i_next_at_2, z_dir2, z_dir2NM, bOnlyNM2, bAnomaly2NM, parity_at_2, parity_at_2NM); + num_stored_stereo_bonds += (parity_at_1 != 0); + } + return num_stored_stereo_bonds; +} + + +/*************************************************************** + * Get stereo atom parity for the current order of attachments + * The result in at[cur_at].parity is valid for previously removed + * explicit hydrogen atoms, including isotopic ones, that are located in at_removed_H[] + * The return value is a calculated parity. + */ +#define ADD_EXPLICIT_HYDROGEN_NEIGH 1 +#define ADD_EXPLICIT_LONE_PAIR_NEIGH 2 +int e_set_stereo_atom_parity( Stereo0D *pStereo, inchi_Atom *at, int cur_at, int bPointedEdgeStereo ) +{ + int j, k, next_at, num_z, j1, nType, num_eH, num_iH, tot_num_iso_H, nMustHaveNumNeigh, bAmbiguousStereo; + double z, sum_xyz[3], min_sine, triple_product; + double at_coord[MAX_NUM_STEREO_ATOM_NEIGH][3]; + double bond_len_xy[4], rmax, rmin; + double at_coord_center[3]; + int parity, out_parity, out_stereo_atom_parity, bAmbiguous = 0, bAddExplicitNeighbor = 0; + int b2D = 0, n2DTetrahedralAmbiguity = 0; + AT_NUM nSbNeighOrigAtNumb[MAX_NUM_STEREO_ATOM_NEIGH]; + S_CHAR num_iso_eH[NUM_H_ISOTOPES+1]; /* count explicit H */ + int bFixSp3Bug = 1; /* 1=> FixSp3Bug option for stereo bonds longer than 20 */ + + out_parity = out_stereo_atom_parity = INCHI_PARITY_NONE; + parity = INCHI_PARITY_NONE; + bAmbiguousStereo = 0; + memset(num_iso_eH, 0, sizeof(num_iso_eH)); + num_eH = 0; /* number of explicit H -- will be found later */ + num_iH = inchi_NUMH2(at,cur_at); /* implicit H */ + + if ( !(nMustHaveNumNeigh = e_bCanInpAtomBeAStereoCenter( cur_at, pStereo->cAtType ) ) || + num_iH > NUM_H_ISOTOPES + ) { + goto exit_function; + } + /* find explicit terminal H */ + memset(num_iso_eH, 0, sizeof(num_iso_eH)); + for ( j = 0; j < at[cur_at].num_bonds; j ++ ) { + next_at = at[cur_at].neighbor[j]; + switch(pStereo->cAtType[next_at] ) { + case AtType_TermH: + if ( 0 <= at[next_at].isotopic_mass && at[next_at].isotopic_mass <= NUM_H_ISOTOPES ) { + num_iso_eH[at[next_at].isotopic_mass] ++; + } else { + num_iso_eH[0] ++; + } + num_eH ++; + break; + case AtType_TermD: + num_iso_eH[2] ++; + num_eH ++; + break; + case AtType_TermT: + num_iso_eH[3] ++; + num_eH ++; + break; + } + } + + /* numbers of isotopic H atoms */ + for ( j = 0, tot_num_iso_H = 0; j < NUM_H_ISOTOPES; j ++ ) { + if ( at[cur_at].num_iso_H[j+1] + num_iso_eH[j+1] > 1 ) { + goto exit_function; /* two or more identical hydrogen isotopic neighbors */ + } + tot_num_iso_H += at[cur_at].num_iso_H[j+1] + num_iso_eH[j+1]; + } + + /* number of non-isotopic H atoms */ + if ( inchi_NUMH2(at,cur_at) + num_eH - tot_num_iso_H > 1 ) { + goto exit_function; /* two or more identical hydrogen non-isotopic neighbors */ + } + + /* coordinates initialization */ + num_z = 0; + sum_xyz[0] = sum_xyz[1] = sum_xyz[2] = 0.0; + + at_coord_center[0] = + at_coord_center[1] = + at_coord_center[2] = 0.0; + + /* fill out stereo center neighbors coordinates */ + /* and obtain the parity from the geometry */ + + /* add all coordinates of other neighboring atoms */ + for ( j = j1 = 0; j < at[cur_at].num_bonds; j ++, j1 ++ ) { + next_at = at[cur_at].neighbor[j]; + z = e_get_z_coord( at, cur_at, j, &nType, bPointedEdgeStereo ); + switch ( nType ) { + case ZTYPE_EITHER: + parity = INCHI_PARITY_UNKNOWN; /* unknown parity: bond in "Either" direction. */ + goto exit_function; + case ZTYPE_UP: + case ZTYPE_DOWN: + b2D ++; + case ZTYPE_3D: + num_z ++; + } + + nSbNeighOrigAtNumb[j1] = next_at+1; + at_coord[j1][0] = at[next_at].x-at[cur_at].x; + at_coord[j1][1] = at[next_at].y-at[cur_at].y; + bond_len_xy[j1] = e_len2(at_coord[j1]); + at_coord[j1][2] = (nType==ZTYPE_3D? z : + nType==ZTYPE_UP? bond_len_xy[j1] : + nType==ZTYPE_DOWN? -bond_len_xy[j1] : 0.0 ); + } + /* j1 is the number of explicit neighbors (that is, all neighbors except implicit H) */ + + b2D = (b2D == num_z && num_z); /* 1 => two-dimensional */ + + if ( MAX_NUM_STEREO_ATOM_NEIGH != at[cur_at].num_bonds+num_iH && + MAX_NUM_STEREO_ATOM_NEIGH-1 != at[cur_at].num_bonds+num_iH ) { + /* not enough geometry data to find the central atom parity */ + goto exit_function; + } + /* make all vector lengths equal to 1; exit if too short. 9-10-2002 */ + for ( j = 0; j < j1; j ++ ) { + z = e_len3( at_coord[j] ); + if ( z < MIN_BOND_LEN ) { + parity = INCHI_PARITY_UNDEFINED; + goto exit_function; + } +#if( STEREO_CENTER_BONDS_NORM == 1 ) + else { + e_mult3( at_coord[j], 1.0/z, at_coord[j] ); + } +#endif + rmax = j? inchi_max( rmax, z) : z; + rmin = j? inchi_min( rmin, z) : z; + } + if ( rmin / rmax < MIN_SINE ) { + /* bond ratio is too small */ + parity = INCHI_PARITY_UNDEFINED; + goto exit_function; + } + for ( j = 0; j < j1; j ++ ) { + e_add3( sum_xyz, at_coord[j], sum_xyz ); + } + + + + /* here j1 is a number of neighbors including explicit terminal isotopic H */ + /* num_iso_eH[0] = number of explicit non-isotopic hydrogen atom neighbors */ + j = j1; + /* Add Explicit Neighbor */ + if ( j1 == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { + /* add an explicit neighbor if possible */ + if ( nMustHaveNumNeigh == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { + bAddExplicitNeighbor = ADD_EXPLICIT_LONE_PAIR_NEIGH; + } else + if ( nMustHaveNumNeigh == MAX_NUM_STEREO_ATOM_NEIGH ) { + /* check whether an explicit non-isotopic hydrogen can be added */ + /* to an atom that is a stereogenic atom */ + bAddExplicitNeighbor = ADD_EXPLICIT_HYDROGEN_NEIGH; + } + } + + if ( bAddExplicitNeighbor ) { + /*********************************************************** + * May happen only if (j1 == MAX_NUM_STEREO_ATOM_NEIGH-1) + * 3 neighbors only, no H-neighbors. Create and add coordinates of an implicit H + * or a fake 4th neighbor, that is, a lone pair + */ + if ( parity == INCHI_PARITY_UNKNOWN ) { + goto exit_function; /* the user insists the parity is unknown and the isotopic */ + /* composition of the neighbors does not contradict */ + } else + if ( num_z == 0 || are_3_vect_in_one_plane(at_coord, MIN_SINE) ) { + /* "hydrogen down" rule is needed to resolve an ambiguity */ + if ( num_z > 0 ) { + bAmbiguous |= AMBIGUOUS_STEREO; + } +#if( APPLY_IMPLICIT_H_DOWN_RULE == 1 ) /* { */ + /* Although H should be at the top of the list, add it to the bottom. */ + /* This will be taken care of later by inverting parity 1<->2 */ + at_coord[j][0] = 0.0; + at_coord[j][1] = 0.0; +#if( STEREO_CENTER_BONDS_NORM == 1 ) + at_coord[j][2] = -1.0; +#else + at_coord[j][2] = -(bond_len_xy[0]+bond_len_xy[1]+bond_len_xy[2])/3.0; +#endif +#else /* } APPLY_IMPLICIT_H_DOWN_RULE { */ +#if (ALWAYS_SET_STEREO_PARITY == 1) + parity = INCHI_PARITY_EVEN; /* suppose atoms are pre-sorted (testing) */ +#else + parity = INCHI_PARITY_UNDEFINED; +#endif + goto exit_function; +#endif /* } APPLY_IMPLICIT_H_DOWN_RULE */ + } else { + /* we have enough information to find implicit hydrogen coordinates */ + e_copy3( sum_xyz, at_coord[j] ); + e_change_sign3( at_coord[j], at_coord[j] ); + z = e_len3( at_coord[j] ); + /* Comparing the original bond lengths to lenghts derived from normalized to 1 */ + /* This bug leads to pronouncing legitimate stereogenic atoms */ + /* connected by 3 bonds "undefined" if in a nicely drawn 2D structure */ + /* bond lengths are about 20 or greater. Reported by Reinhard Dunkel 2005-08-05 */ + if ( bFixSp3Bug ) { + /* coordinate scaling bug fixed here */ + if ( z > 1.0 ) { + rmax *= z; + } else { + rmin *= z; + } + } else { + /* original InChI v.1 bug */ + rmax = inchi_max( rmax, z ); + rmin = inchi_min( rmin, z ); + } + if ( z < MIN_BOND_LEN || rmin/rmax < MIN_SINE ) { + /* the new 4th bond is too short */ + parity = INCHI_PARITY_UNDEFINED; + goto exit_function; + } +#if( STEREO_CENTER_BOND4_NORM == 1 ) + else { + e_mult3( at_coord[j], 1.0/z, at_coord[j] ); + } +#endif + } + } else + if ( j1 != MAX_NUM_STEREO_ATOM_NEIGH ) { + if ( parity == INCHI_PARITY_UNKNOWN ) { + parity = -INCHI_PARITY_UNDEFINED; /* isotopic composition of H-neighbors contradicts 'unknown' */ + } + goto exit_function; + } else /* j1 == MAX_NUM_STEREO_ATOM_NEIGH */ + if ( num_z == 0 || e_are_4at_in_one_plane(at_coord, MIN_SINE) ) { + /* all four neighours in xy plane: undefined geometry. */ + if ( num_z > 0 ) { + bAmbiguous |= AMBIGUOUS_STEREO; + } + if ( parity != INCHI_PARITY_UNKNOWN ) { +#if (ALWAYS_SET_STEREO_PARITY == 1) + parity = INCHI_PARITY_EVEN; /* suppose atoms are pre-sorted (testing) */ +#else + /* all 4 bonds are in one plain */ + parity = INCHI_PARITY_UNDEFINED; +#endif + } + goto exit_function; + } + /*********************************************************** + * At this point we have 4 neighboring atoms. + * check for tetrahedral ambiguity in 2D case + */ + if ( b2D ) { + if ( 0 < (n2DTetrahedralAmbiguity = e_Get2DTetrahedralAmbiguity( at_coord, bAddExplicitNeighbor )) ) { + if ( T2D_WARN & n2DTetrahedralAmbiguity ) { + bAmbiguous |= AMBIGUOUS_STEREO; + } + if ( T2D_UNDF & n2DTetrahedralAmbiguity ) { + if ( parity != INCHI_PARITY_UNKNOWN ) { +#if (ALWAYS_SET_STEREO_PARITY == 1) + parity = INCHI_PARITY_EVEN; /* suppose atoms are pre-sorted (testing) */ +#else + parity = INCHI_PARITY_UNDEFINED; /* no parity */ +#endif + } + goto exit_function; + } + } else + if ( n2DTetrahedralAmbiguity < 0 ) { + bAmbiguous |= AMBIGUOUS_STEREO_ERROR; /* error */ + parity = INCHI_PARITY_UNDEFINED; + goto exit_function; + } + } + + /************************************************************/ + /* Move coordinates origin to the neighbor #0 */ + for ( j = 1; j < MAX_NUM_STEREO_ATOM_NEIGH; j ++ ) { + e_diff3(at_coord[j], at_coord[0], at_coord[j]); + } + e_diff3(at_coord_center, at_coord[0], at_coord_center); + + /******************************************************** + * find the central (cur_at) atom's parity + * (orientation of atoms #1-3 when looking from #0) + ********************************************************/ + triple_product = e_triple_prod_and_min_abs_sine2(&at_coord[1], at_coord_center, bAddExplicitNeighbor, &min_sine, &bAmbiguous); + if ( fabs(triple_product) > ZERO_FLOAT && (min_sine > MIN_SINE || fabs(min_sine) > ZERO_FLOAT && (n2DTetrahedralAmbiguity & T2D_OKAY ) ) ) { + /* Even => sorted in correct order, Odd=>transposed */ + parity = triple_product > 0.0? INCHI_PARITY_EVEN : INCHI_PARITY_ODD; + + /* for 4 attached atoms, moving the implicit H from index=3 to index=0 */ + /* can be done in odd number (3) transpositions: (23)(12)(01), which inverts the parity */ + if ( j1 == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { + parity = 3 - parity; + } + } else { +#if (ALWAYS_SET_STEREO_PARITY == 1) + parity = AT_PARITY_EVEN; /* suppose atoms are pre-sorted (testing) */ +#else + if ( num_z > 0 ) { + bAmbiguous |= AMBIGUOUS_STEREO; + } + parity = INCHI_PARITY_UNDEFINED; /* no parity: 4 bonds are in one plane. */ +#endif + } +exit_function: + + if ( parity ) { + bAmbiguousStereo |= bAmbiguous; + } + /* isotopic parity => parity */ + if ( parity < 0 ) + parity = -parity; + + if ( 0 < parity && parity < INCHI_PARITY_UNDEFINED ) { + inchi_Stereo0D *stereo0D; + if ( stereo0D = e_GetNewStereo( pStereo ) ) { + stereo0D->central_atom = cur_at; + stereo0D->parity = parity; + stereo0D->type = INCHI_StereoType_Tetrahedral; + k = 0; + if ( at[cur_at].num_bonds == 3 ) { + stereo0D->neighbor[k++] = cur_at; + } + for ( j = 0; j < at[cur_at].num_bonds; j ++ ) { + stereo0D->neighbor[k++] = at[cur_at].neighbor[j]; + } + } + } + return parity; +} +#undef ADD_EXPLICIT_HYDROGEN_NEIGH +#undef ADD_EXPLICIT_LONE_PAIR_NEIGH + +/*************************************************************/ +inchi_Stereo0D *e_GetNewStereo( Stereo0D *pStereo ) +{ +#define DEFAULT_0D_STEREO_DELTA 64 + int delta = pStereo->delta_num_stereo0D > 0? pStereo->delta_num_stereo0D : DEFAULT_0D_STEREO_DELTA; + if ( pStereo->num_stereo0D >= pStereo->max_num_Stereo0D ) { + /*inchi_Stereo0D *pNew = (inchi_Stereo0D *)e_inchi_calloc( pStereo->max_num_Stereo0D + delta, sizeof(pNew[0]) );*/ + inchi_Stereo0D *pNew = e_CreateInchi_Stereo0D( pStereo->max_num_Stereo0D + delta ); + if ( pNew ) { + if ( pStereo->num_stereo0D > 0 ) { + memcpy( pNew, pStereo->stereo0D, pStereo->num_stereo0D * sizeof(pNew[0]) ); + } + /*e_inchi_free(pStereo->stereo0D);*/ + e_FreeInchi_Stereo0D( &pStereo->stereo0D ); + pStereo->stereo0D = pNew; + pStereo->max_num_Stereo0D += delta; + } + } + return pStereo->stereo0D + pStereo->num_stereo0D ++; +#undef DEFAULT_0D_STEREO_DELTA +} + +/*************************************************************/ +int set_0D_stereo_parities( inchi_Input *pInp, int bPointedEdgeStereo ) +{ + int num_3D_stereo_atoms=0, num_stereo_bonds=0; + + int i, is_stereo, num_stereo; + inchi_Atom* at = pInp->atom; + int num_at = pInp->num_atoms; + Stereo0D stereo; + Stereo0D *pStereo = &stereo; + + /********************************************************** + * + * Note: this parity reflects only relative positions of + * the atoms-neighbors and their ordering in the + * lists of neighbors. + * + * To obtain the actual parity, the parity of a number + * of neighbors transpositions (to obtain a sorted + * list of numbers assigned to the atoms) should be + * added. + * + **********************************************************/ + + /********************************************************************************* + + An example of parity=1 for stereogenic center, tetrahedral asymmetric atom + + + + (1) + | + | + [C] | + | + (2)------(0) + / + / + / + / + (3) + + + Notation: (n) is a tetrahedral atom neighbor; n is an index of a neighbor in + the central_at->neighbor[] array : neighbor atom number is central_at->neighbor[n]. + + (0)-(1), (0)-(2), (0)-(3) are lines connecting atom [C] neighbors to neighbor (0) + (0), (1) and (2) are in the plane + (0)-(3) is directed from the plain to the viewer + [C] is somewhere between (0), (1), (2), (3) + Since (1)-(2)-(3) are in a clockwise order when looking from (0), parity is 2, or even; + otherwise parity would be 1, or odd. + + ********************************************************************************** + + Examples of a stereogenic bond. + + Notation: [atom number], (index of a neighbor): + [1] and [2] are atoms connected by the stereogenic bond + numbers in () are indexes of neighbors of [1] or [2]. + (12 x 16)z = z-component of [1]-[2] and [1]-[6] cross-product + + atom [1] atom [2] + [8] [4] prod01 = (12 x 16)z < 0 prod01 = (21 x 24)z < 0 + \ / prod02 = (12 x 18)z > 0 prod02 = (21 x 25)z > 0 + (2) (1) 0 transpositions because 0 transpositions because + \ / double bond is in 0 posit. double bond is in 0 position + [1]==(0)(0)==[2] 0 = (prod01 > prod02) 0 = (prod01 > prod02) + / \ + (1) (2) result: parity = 2, even result: parity=2, even + / \ + [6] [5] + + + + atom [1] atom [2] + [8] [5] prod01 = (12 x 18)z > 0 prod01 = (21 x 24)z > 0 + \ / prod02 = (12 x 16)z < 0 prod02 = (21 x 25)z < 0 + (0) (2) 2 transpositions to move 1 transposition to move + \ / at [2] from 2 to 0 pos. at [1] from 1 to 0 position + [1]==(2)(1)==[2] 1 = (prod01 > prod02) 1 = (prod01 > prod02) + / \ + (1) (0) result: parity = (1+2) result: parity=(1+1) + / \ 2-(1+2)%2 = 1, odd 2-(1+1)%2 = 2, even + [6] [4] + + + *********************************************************************************** + Note: atoms' numbers [1], [2], [4],... are not used to calculate parity at this + point. They will be used for each numbering in the canonicalization. + Note: parity=3 for a stereo atom means entered undefined bond direction + parity=4 for an atom means parity cannot be determined from the given geometry + ***********************************************************************************/ + + if ( !at ) { + return -1; + } + + /* clear stereo descriptors */ + memset( pStereo, 0, sizeof(*pStereo) ); + pStereo->delta_num_stereo0D = num_at; + pStereo->cAtType = (S_CHAR *)e_inchi_calloc( num_at, sizeof(pStereo->cAtType[0]) ); + if (!pStereo->cAtType ) { + return -1; + } + /* atom stereo types */ + for ( i = 0; i < num_at; i ++ ) { + pStereo->cAtType[i] = e_GetElType( at, i ); + } + + + + /* calculate stereo descriptors */ + /* main cycle: set stereo parities */ + for( i = 0, num_stereo = 0; i < num_at; i ++ ) { + + if ( is_stereo = e_set_stereo_atom_parity( pStereo, at, i, bPointedEdgeStereo ) ) { + num_3D_stereo_atoms += ATOM_PARITY_WELL_DEF( is_stereo ); + num_stereo += ATOM_PARITY_WELL_DEF( is_stereo ); + } else { + is_stereo = e_set_stereo_bonds_parity( pStereo, at, i, bPointedEdgeStereo ); + if ( RETURNED_ERROR( is_stereo ) ) { + num_3D_stereo_atoms = is_stereo; + is_stereo = 0; + break; + } else { + num_stereo_bonds += is_stereo; + num_stereo += is_stereo; + } + } + } + /* + if ( (nMode & REQ_MODE_SC_IGN_ALL_UU ) + REQ_MODE_SC_IGN_ALL_UU + REQ_MODE_SB_IGN_ALL_UU + */ + if ( pStereo->cAtType ) { + e_inchi_free( pStereo->cAtType ); + } + if ( pInp->stereo0D ) { + } + + pInp->stereo0D = pStereo->stereo0D; + pInp->num_stereo0D = pStereo->num_stereo0D; + + return RETURNED_ERROR( num_3D_stereo_atoms )? num_3D_stereo_atoms : num_stereo; +} +/*****************************************************************/ +int Clear3D2Dstereo(inchi_Input *pInp) +{ + int i; + if ( !pInp->atom || !pInp->num_atoms ) + return 0; + for ( i = 0; i < pInp->num_atoms; i ++ ) { + pInp->atom[i].x = + pInp->atom[i].y = + pInp->atom[i].z = 0.0; + memset( pInp->atom[i].bond_stereo, 0, sizeof(pInp->atom[i].bond_stereo) ); + } + return 1; +} diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/e_0dstereo.h b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_0dstereo.h similarity index 57% rename from INCHI-1-SRC/INCHI_API/inchi_main/e_0dstereo.h rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_0dstereo.h index bff4afd..c14f872 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_main/e_0dstereo.h +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_0dstereo.h @@ -1,62 +1,60 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __0DSTEREO_H__ -#define __0DSTEREO_H__ - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - -int Clear3D2Dstereo(inchi_Input *pInp); -int set_0D_stereo_parities( inchi_Input *pInp, int bPointedEdgeStereo ); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /* __0DSTEREO_H__ */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef __0DSTEREO_H__ +#define __0DSTEREO_H__ + + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + + +int Clear3D2Dstereo(inchi_Input *pInp); +int set_0D_stereo_parities( inchi_Input *pInp, int bPointedEdgeStereo ); + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + +#endif /* __0DSTEREO_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/e_comdef.h b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_comdef.h similarity index 69% rename from INCHI-1-SRC/INCHI_API/inchi_main/e_comdef.h rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_comdef.h index f35703b..963a806 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_main/e_comdef.h +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_comdef.h @@ -1,88 +1,86 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -/* common definitions -- do not change */ -#ifndef __COMDEF_H__ -#define __COMDEF_H__ - -/* input bond type definition */ -#define MIN_INPUT_BOND_TYPE INCHI_BOND_TYPE_SINGLE -#define MAX_INPUT_BOND_TYPE INCHI_BOND_TYPE_ALTERN - -/* MOlfile */ -#define INPUT_STEREO_SNGL_UP 1 -#define INPUT_STEREO_SNGL_EITHER 4 -#define INPUT_STEREO_SNGL_DOWN 6 -#define INPUT_STEREO_DBLE_EITHER 3 - -/* radical definitions */ -#define RADICAL_SINGLET 1 -#define RADICAL_DOUBLET 2 -#define RADICAL_TRIPLET 3 - - -/* BILLY 8/6/04 */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - - -int e_AddMOLfileError( char *pStrErr, const char *szMsg ); - -#define MOLFILE_ERR_FIN(err, new_err, err_fin, msg) \ - if ( !(err) && (new_err) ) { (err) = (new_err);} e_AddMOLfileError(pStrErr, (msg)); goto err_fin -#define MOLFILE_ERR_SET(err, new_err, msg) \ - if ( !(err) && (new_err) ) { (err) = (new_err);} e_AddMOLfileError(pStrErr, (msg)) - - - - -/* BILLY 8/6/04 */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -#endif /* __COMDEF_H__ */ - +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +/* common definitions -- do not change */ +#ifndef __COMDEF_H__ +#define __COMDEF_H__ + +/* input bond type definition */ +#define MIN_INPUT_BOND_TYPE INCHI_BOND_TYPE_SINGLE +#define MAX_INPUT_BOND_TYPE INCHI_BOND_TYPE_ALTERN + +/* MOlfile */ +#define INPUT_STEREO_SNGL_UP 1 +#define INPUT_STEREO_SNGL_EITHER 4 +#define INPUT_STEREO_SNGL_DOWN 6 +#define INPUT_STEREO_DBLE_EITHER 3 + +/* radical definitions */ +#define RADICAL_SINGLET 1 +#define RADICAL_DOUBLET 2 +#define RADICAL_TRIPLET 3 + + +/* BILLY 8/6/04 */ +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + + + +int e_AddMOLfileError( char *pStrErr, const char *szMsg ); + +#define MOLFILE_ERR_FIN(err, new_err, err_fin, msg) \ + if ( !(err) && (new_err) ) { (err) = (new_err);} e_AddMOLfileError(pStrErr, (msg)); goto err_fin +#define MOLFILE_ERR_SET(err, new_err, msg) \ + if ( !(err) && (new_err) ) { (err) = (new_err);} e_AddMOLfileError(pStrErr, (msg)) + + + + +/* BILLY 8/6/04 */ +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + +#endif /* __COMDEF_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/e_ctl_data.h b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ctl_data.h similarity index 85% rename from INCHI-1-SRC/INCHI_API/inchi_main/e_ctl_data.h rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ctl_data.h index e11064f..b89b138 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_main/e_ctl_data.h +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ctl_data.h @@ -1,223 +1,229 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __CTL_DATA_H__ -#define __CTL_DATA_H__ -#include "e_ichisize.h" -/***********************************************/ -#define STR_ERR_LEN 256 - -typedef struct tagStructData { - unsigned long ulStructTime; - int nErrorCode; - int nErrorType; - int nStructReadError; - int bChiralFlag; - char pStrErrStruct[STR_ERR_LEN]; - long fPtrStart; - long fPtrEnd; - /* debugging info */ -#if( bRELEASE_VERSION == 0 ) - int bExtract; -#endif - -} STRUCT_DATA; -/***********************************************/ - -#define MAX_NUM_PATHS 4 - - -/* SDF treatment */ -#define MAX_SDF_VALUE 255 /* max lenght of the SDFile data value */ -#define MAX_SDF_HEADER 64 /* max length of the SDFile data header */ - -/***********************************************/ - -/* bCalcInChIHash values */ -typedef enum tagInChIHashCalc -{ - INCHIHASH_NONE=0, - INCHIHASH_KEY=1, - INCHIHASH_KEY_XTRA1=2, - INCHIHASH_KEY_XTRA2=3, - INCHIHASH_KEY_XTRA1_XTRA2=4 -} -INCHI_HASH_CALC; -typedef struct tagInputParms { - char szSdfDataHeader[MAX_SDF_HEADER+1]; - char *pSdfLabel; - char *pSdfValue; - long lSdfId; - long lMolfileNumber; -#ifndef COMPILE_ANSI_ONLY - DRAW_PARMS dp; - PER_DRAW_PARMS pdp; - TBL_DRAW_PARMS tdp; -#endif -/* - -- Files -- - ip->path[0] => Input - ip->path[1] => Output (INChI) - ip->path[2] => Log - ip->path[3] => Problem structures - ip->path[4] => Errors file (ACD) - -*/ - const char *path[MAX_NUM_PATHS]; - int num_paths; - long first_struct_number; - long last_struct_number; - INPUT_TYPE nInputType; - INCHI_MODE nMode; - int bAbcNumbers; - /*int bXml;*/ - int bINChIOutputOptions; /* !(ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) */ - int bCtPredecessors; - int bXmlStarted; - int bDisplayEachComponentINChI; - - long msec_MaxTime; /* was ulMaxTime; max time to run ProsessOneStructure */ - long msec_LeftTime; - - /* unsigned long ulMaxTime; */ - unsigned long ulDisplTime; - int bDisplay; - int bDisplayIfRestoreWarnings; /* InChI->Struct debug */ - int bMergeAllInputStructures; - int bSaveWarningStructsAsProblem; - int bSaveAllGoodStructsAsProblem; - int bGetSdfileId; - int bGetMolfileNumber; /* read molfile number from the name line like "Structure #22" */ - int bCompareComponents; /* see flags CMP_COMPONENTS, etc. */ - int bDisplayCompositeResults; - int bDoNotAddH; - int bNoStructLabels; - int bChiralFlag; - int bAllowEmptyStructure; - /*^^^ */ - int bCalcInChIHash; - int bFixNonUniformDraw; /* correct non-uniformly drawn oxoanions and amidinium cations. */ - /*^^^ */ - INCHI_MODE bTautFlags; - INCHI_MODE bTautFlagsDone; - -#if( READ_INCHI_STRING == 1 ) - int bReadInChIOptions; -#endif - -/* post v.1 features */ -#if( UNDERIVATIZE == 1 ) - int bUnderivatize; -#endif -#if( RING2CHAIN == 1 ) - int bRing2Chain; -#endif -#if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) - int bIngnoreUnchanged; -#endif - -} INPUT_PARMS; - - -/*************************** INChI mode *******************************/ -/* ip->nMode */ -#define REQ_MODE_BASIC 0x000001 /* B */ -#define REQ_MODE_TAUT 0x000002 /* T */ -#define REQ_MODE_ISO 0x000004 /* I */ -#define REQ_MODE_NON_ISO 0x000008 /* NI */ -#define REQ_MODE_STEREO 0x000010 /* S */ -#define REQ_MODE_ISO_STEREO 0x000020 /* IS */ -#define REQ_MODE_NOEQ_STEREO 0x000040 /* SS */ -#define REQ_MODE_REDNDNT_STEREO 0x000080 /* RS */ -#define REQ_MODE_NO_ALT_SBONDS 0x000100 /* NASB */ -/* new 10-10-2003 */ -#define REQ_MODE_RELATIVE_STEREO 0x000200 /* REL All Relative Stereo */ -#define REQ_MODE_RACEMIC_STEREO 0x000400 /* RAC All Racemic Stereo */ -#define REQ_MODE_SC_IGN_ALL_UU 0x000800 /* IAUSC Ignore stereocenters if All Undef/Unknown */ -#define REQ_MODE_SB_IGN_ALL_UU 0x001000 /* IAUSC Ignore stereobonds if All Undef/Unknown */ -#define REQ_MODE_CHIR_FLG_STEREO 0x002000 /* SUCF If Chiral flag then Abs otherwise Rel stereo */ -/* end of 10-10-2003 */ -/*^^^ 2009-12-05 */ -#define REQ_MODE_DIFF_UU_STEREO 0x004000 /* SLUUD Make labels for unknown and undefined stereo different */ -/*^^^ 2009-12-05 */ - -#define REQ_MODE_MIN_SB_RING_MASK 0x0F0000 /* RSB */ -#define REQ_MODE_MIN_SB_RING_SHFT 16 - -#define REQ_MODE_DEFAULT (REQ_MODE_BASIC | REQ_MODE_TAUT | REQ_MODE_ISO | REQ_MODE_NON_ISO | REQ_MODE_STEREO) - -/*********** compare components flags **********************************/ -/* ip->bCompareComponents */ -#define CMP_COMPONENTS 0x0001 /* perform compare components */ -#define CMP_COMPONENTS_NONISO 0x0002 /* ignore isotopic */ -#define CMP_COMPONENTS_NONTAUT 0x0004 /* compare non-tautomeric */ - -/****************** chemical identifier member definitions *************/ -/* ip->bINChIOutputOptions */ -#define INCHI_OUT_NO_AUX_INFO 0x0001 /* do not output Aux Info */ -#define INCHI_OUT_SHORT_AUX_INFO 0x0002 /* output short version of Aux Info */ -#define INCHI_OUT_ONLY_AUX_INFO 0x0004 /* output only Aux Info */ -#define INCHI_OUT_EMBED_REC 0x0008 /* embed reconnected INChI into disconnected INChI */ -#define INCHI_OUT_SDFILE_ONLY 0x0010 /* save input data in a Molfile instead of creating INChI */ -#define INCHI_OUT_XML 0x0020 /* output xml INChI */ -#define INCHI_OUT_PLAIN_TEXT 0x0040 /* output plain text INChI */ -#define INCHI_OUT_PLAIN_TEXT_COMMENTS 0x0080 /* output plain text annotation */ -#define INCHI_OUT_XML_TEXT_COMMENTS 0x0100 /* output xml text annotation */ -#define INCHI_OUT_WINCHI_WINDOW 0x0200 /* output into wINChI text window */ -#define INCHI_OUT_TABBED_OUTPUT 0x0400 /* tab-delimited (only for plain text) */ -#define INCHI_OUT_SDFILE_ATOMS_DT 0x0800 /* SDfile output H isotopes as D and T */ -#define INCHI_OUT_SDFILE_SPLIT 0x1000 /* Split SDfile into components */ -/*^^^ */ -#define INCHI_OUT_FIX_TRANSPOSITION_CHARGE_BUG 0x2000 - /* used to accomodate FIX_TRANSPOSITION_CHARGE_BUG */ -/*^^^ */ -#define INCHI_OUT_STDINCHI 0x4000 -#define INCHI_OUT_SAVEOPT 0x8000 - -#define FLAG_INP_AT_CHIRAL 1 -#define FLAG_INP_AT_NONCHIRAL 2 -#define FLAG_SET_INP_AT_CHIRAL 4 -#define FLAG_SET_INP_AT_NONCHIRAL 8 - -/* unknown/undefined stereo - constants */ -#define AB_PARITY_UNKN 3 /* 3 => user marked as unknown parity */ -#define AB_PARITY_UNDF 4 /* 4 => parity cannot be defined because of symmetry or not well defined geometry */ - -#endif /* __CTL_DATA_H__ */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef __CTL_DATA_H__ +#define __CTL_DATA_H__ +#include "e_ichisize.h" +/***********************************************/ +#define STR_ERR_LEN 256 + +typedef struct tagStructData { + unsigned long ulStructTime; + int nErrorCode; + int nErrorType; + int nStructReadError; + int bChiralFlag; + char pStrErrStruct[STR_ERR_LEN]; + long fPtrStart; + long fPtrEnd; + /* debugging info */ +#if( bRELEASE_VERSION == 0 ) + int bExtract; +#endif +} STRUCT_DATA; +/***********************************************/ + +#define MAX_NUM_PATHS 4 + + +/* SDF treatment */ +#define MAX_SDF_VALUE 255 /* max lenght of the SDFile data value */ +#define MAX_SDF_HEADER 64 /* max length of the SDFile data header */ + +/***********************************************/ + +/* bCalcInChIHash values */ +typedef enum tagInChIHashCalc +{ + INCHIHASH_NONE=0, + INCHIHASH_KEY=1, + INCHIHASH_KEY_XTRA1=2, + INCHIHASH_KEY_XTRA2=3, + INCHIHASH_KEY_XTRA1_XTRA2=4 +} +INCHI_HASH_CALC; + +typedef struct tagInputParms { + char szSdfDataHeader[MAX_SDF_HEADER+1]; + char *pSdfLabel; + char *pSdfValue; + long lSdfId; + long lMolfileNumber; +#if ( defined( TARGET_LIB_FOR_WINCHI ) || defined(TARGET_EXE_STANDALONE) ) +#ifndef COMPILE_ANSI_ONLY + DRAW_PARMS dp; + PER_DRAW_PARMS pdp; + TBL_DRAW_PARMS tdp; +#endif +#endif +/* + -- Files -- + ip->path[0] => Input + ip->path[1] => Output (INChI) + ip->path[2] => Log + ip->path[3] => Problem structures + ip->path[4] => Errors file (ACD) + +*/ + const char *path[MAX_NUM_PATHS]; + int num_paths; + long first_struct_number; + long last_struct_number; + INPUT_TYPE nInputType; + INCHI_MODE nMode; + int bAbcNumbers; + /*int bXml;*/ + int bINChIOutputOptions; /* !(ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) */ + int bINChIOutputOptions2; /* v. 1.05 */ + int bCtPredecessors; + int bXmlStarted; + int bDisplayEachComponentINChI; + + long msec_MaxTime; /* was ulMaxTime; max time to run ProsessOneStructure */ + long msec_LeftTime; + + /* unsigned long ulMaxTime; */ + unsigned long ulDisplTime; + int bDisplay; + int bDisplayIfRestoreWarnings; /* InChI->Struct debug */ + int bMergeAllInputStructures; + int bSaveWarningStructsAsProblem; + int bSaveAllGoodStructsAsProblem; + int bGetSdfileId; + int bGetMolfileNumber; /* read molfile number from the name line like "Structure #22" */ + int bCompareComponents; /* see flags CMP_COMPONENTS, etc. */ + int bDisplayCompositeResults; + int bDoNotAddH; + int bNoStructLabels; + int bChiralFlag; + int bAllowEmptyStructure; + /*^^^ */ + int bLargeMolecules; /* v. 1.05 */ + int bPolymers; /* v. 1.05 */ + int bCalcInChIHash; + int bFixNonUniformDraw; /* correct non-uniformly drawn oxoanions and amidinium cations. */ + /*^^^ */ + INCHI_MODE bTautFlags; + INCHI_MODE bTautFlagsDone; + +#if( READ_INCHI_STRING == 1 ) + int bReadInChIOptions; +#endif + +/* post v.1 features */ +#if( UNDERIVATIZE == 1 ) + int bUnderivatize; +#endif +#if( RING2CHAIN == 1 ) + int bRing2Chain; +#endif +#if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) + int bIngnoreUnchanged; +#endif +} INPUT_PARMS; + + +/*************************** INChI mode *******************************/ +/* ip->nMode */ +#define REQ_MODE_BASIC 0x000001 /* B */ +#define REQ_MODE_TAUT 0x000002 /* T */ +#define REQ_MODE_ISO 0x000004 /* I */ +#define REQ_MODE_NON_ISO 0x000008 /* NI */ +#define REQ_MODE_STEREO 0x000010 /* S */ +#define REQ_MODE_ISO_STEREO 0x000020 /* IS */ +#define REQ_MODE_NOEQ_STEREO 0x000040 /* SS */ +#define REQ_MODE_REDNDNT_STEREO 0x000080 /* RS */ +#define REQ_MODE_NO_ALT_SBONDS 0x000100 /* NASB */ +/* new 10-10-2003 */ +#define REQ_MODE_RELATIVE_STEREO 0x000200 /* REL All Relative Stereo */ +#define REQ_MODE_RACEMIC_STEREO 0x000400 /* RAC All Racemic Stereo */ +#define REQ_MODE_SC_IGN_ALL_UU 0x000800 /* IAUSC Ignore stereocenters if All Undef/Unknown */ +#define REQ_MODE_SB_IGN_ALL_UU 0x001000 /* IAUSC Ignore stereobonds if All Undef/Unknown */ +#define REQ_MODE_CHIR_FLG_STEREO 0x002000 /* SUCF If Chiral flag then Abs otherwise Rel stereo */ +/* end of 10-10-2003 */ +/*^^^ 2009-12-05 */ +#define REQ_MODE_DIFF_UU_STEREO 0x004000 /* SLUUD Make labels for unknown and undefined stereo different */ +/*^^^ 2009-12-05 */ + +#define REQ_MODE_MIN_SB_RING_MASK 0x0F0000 /* RSB */ +#define REQ_MODE_MIN_SB_RING_SHFT 16 + +#define REQ_MODE_DEFAULT (REQ_MODE_BASIC | REQ_MODE_TAUT | REQ_MODE_ISO | REQ_MODE_NON_ISO | REQ_MODE_STEREO) + +/*********** compare components flags **********************************/ +/* ip->bCompareComponents */ +#define CMP_COMPONENTS 0x0001 /* perform compare components */ +#define CMP_COMPONENTS_NONISO 0x0002 /* ignore isotopic */ +#define CMP_COMPONENTS_NONTAUT 0x0004 /* compare non-tautomeric */ + +/****************** chemical identifier member definitions *************/ +/* ip->bINChIOutputOptions */ +#define INCHI_OUT_NO_AUX_INFO 0x0001 /* do not output Aux Info */ +#define INCHI_OUT_SHORT_AUX_INFO 0x0002 /* output short version of Aux Info */ +#define INCHI_OUT_ONLY_AUX_INFO 0x0004 /* output only Aux Info */ +#define INCHI_OUT_EMBED_REC 0x0008 /* embed reconnected INChI into disconnected INChI */ +#define INCHI_OUT_SDFILE_ONLY 0x0010 /* save input data in a Molfile instead of creating INChI */ +#define INCHI_OUT_XML 0x0020 /* output xml INChI */ +#define INCHI_OUT_PLAIN_TEXT 0x0040 /* output plain text INChI */ +#define INCHI_OUT_PLAIN_TEXT_COMMENTS 0x0080 /* output plain text annotation */ +#define INCHI_OUT_XML_TEXT_COMMENTS 0x0100 /* output xml text annotation */ +#define INCHI_OUT_WINCHI_WINDOW 0x0200 /* output into wINChI text window */ +#define INCHI_OUT_TABBED_OUTPUT 0x0400 /* tab-delimited (only for plain text) */ +#define INCHI_OUT_SDFILE_ATOMS_DT 0x0800 /* SDfile output H isotopes as D and T */ +#define INCHI_OUT_SDFILE_SPLIT 0x1000 /* Split SDfile into components */ +/*^^^ */ +#define INCHI_OUT_FIX_TRANSPOSITION_CHARGE_BUG 0x2000 + /* used to accomodate FIX_TRANSPOSITION_CHARGE_BUG */ +/*^^^ */ +#define INCHI_OUT_STDINCHI 0x4000 +#define INCHI_OUT_SAVEOPT 0x8000 + +#define INCHI_OUT_INCHI_GEN_ERROR 0x0001 /* v. 1.05 */ +#define INCHI_OUT_MISMATCH_AS_ERROR 0x0002 /* v. 1.05 */ + +#define FLAG_INP_AT_CHIRAL 1 +#define FLAG_INP_AT_NONCHIRAL 2 +#define FLAG_SET_INP_AT_CHIRAL 4 +#define FLAG_SET_INP_AT_NONCHIRAL 8 + +/* unknown/undefined stereo - constants */ +#define AB_PARITY_UNKN 3 /* 3 => user marked as unknown parity */ +#define AB_PARITY_UNDF 4 /* 4 => parity cannot be defined because of symmetry or not well defined geometry */ + +#endif /* __CTL_DATA_H__ */ diff --git a/INCHI-1-SRC/INCHI/common/ichi_io.c b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichi_io.c similarity index 80% rename from INCHI-1-SRC/INCHI/common/ichi_io.c rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichi_io.c index 1403f86..0f9de8a 100644 --- a/INCHI-1-SRC/INCHI/common/ichi_io.c +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichi_io.c @@ -1,1052 +1,1068 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include -#include "mode.h" -#include "ichi_io.h" -#include "ichicomp.h" -#include "util.h" - -#ifndef INCHI_ADD_STR_LEN -#define INCHI_ADD_STR_LEN 32768 -#endif - - -#ifdef TARGET_LIB_FOR_WINCHI -extern void (*FWPRINT) (const char * format, va_list argptr ); -#endif - - -/*^^^ Internal functions */ - -int inchi_ios_str_getc( INCHI_IOSTREAM *ios ); -char *inchi_ios_str_gets( char *szLine, int len, INCHI_IOSTREAM *ios ); -char *inchi_ios_str_getsTab( char *szLine, int len, INCHI_IOSTREAM *ios ); -int GetMaxPrintfLength( const char *lpszFormat, va_list argList); -char *inchi_fgetsTab( char *szLine, int len, FILE *f ); -int inchi_vfprintf( FILE* f, const char* lpszFormat, va_list argList ); - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - - INCHI_IOSTREAM OPERATIONS - - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Init INCHI_IOSTREAM -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void inchi_ios_init(INCHI_IOSTREAM* ios, int io_type, FILE *f) -{ - memset( ios, 0, sizeof(*ios) ); - switch (io_type) - { - case INCHI_IOSTREAM_FILE: ios->type = INCHI_IOSTREAM_FILE; - break; - case INCHI_IOSTREAM_STRING: - default: ios->type = INCHI_IOSTREAM_STRING; - break; - } - ios->f = f; - return; -} - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - If INCHI_IOSTREAM type is INCHI_IOSTREAM_STRING, - flush INCHI_IOSTREAM string buffer to file (if non-NULL); then free buffer. - If INCHI_IOSTREAM type is INCHI_IOSTREAM_FILE, just flush the file. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void inchi_ios_flush(INCHI_IOSTREAM* ios) -{ - - if (ios->type == INCHI_IOSTREAM_STRING) - { - if (ios->s.pStr) - { - if (ios->s.nUsedLength > 0) - { - if (ios->f) - { - fprintf(ios->f,"%-s", ios->s.pStr); - fflush(ios->f); - } - inchi_free(ios->s.pStr ); - ios->s.pStr = NULL; - ios->s.nUsedLength = ios->s.nAllocatedLength = ios->s.nPtr = 0; - } - } - } - - else if (ios->type == INCHI_IOSTREAM_FILE) - { - /* output to plain file: just flush it. */ - fflush(ios->f); - } - - return; -} - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - If INCHI_IOSTREAM type is INCHI_IOSTREAM_STRING, - flush INCHI_IOSTREAM string buffer to file (if non-NULL) and - another file f2 supplied as parameter (typically, it will be stderr); then free buffer. - If INCHI_IOSTREAM type is INCHI_IOSTREAM_FILE, just flush the both files. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void inchi_ios_flush2(INCHI_IOSTREAM* ios, FILE *f2) -{ - - if (ios->type == INCHI_IOSTREAM_STRING) - { - if (ios->s.pStr) - { - if (ios->s.nUsedLength > 0) - { - if (ios->f) - { - fprintf(ios->f,"%-s", ios->s.pStr); - fflush(ios->f); - } - if (f2!=ios->f) - fprintf(f2,"%-s", ios->s.pStr); - - inchi_free(ios->s.pStr ); - ios->s.pStr = NULL; - ios->s.nUsedLength = ios->s.nAllocatedLength = ios->s.nPtr = 0; - } - } - } - - else if (ios->type == INCHI_IOSTREAM_FILE) - { - /* output to plain file: just flush it. */ - if ( (ios->f) && (ios->f!=stderr) && (ios->f!=stdout) ) - fflush(ios->f); - if ( f2 && f2!=stderr && f2!=stdout) - fflush(f2); - - - } - - return; -} - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Close INCHI_IOSTREAM: free string buffer and close the file. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void inchi_ios_close(INCHI_IOSTREAM* ios) -{ - if (ios->s.pStr) - inchi_free(ios->s.pStr); - ios->s.pStr = NULL; - ios->s.nUsedLength = ios->s.nAllocatedLength = ios->s.nPtr = 0; - if ( (ios->f) && (ios->f!=stderr) && (ios->f!=stdout) && (ios->f!=stdin)) - fclose(ios->f); - return; -} - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Reset INCHI_IOSTREAM: set string buffer ptr to NULL (but do _not_ free memory)and close the file. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void inchi_ios_reset(INCHI_IOSTREAM* ios) -{ - ios->s.pStr = NULL; - ios->s.nUsedLength = ios->s.nAllocatedLength = ios->s.nPtr = 0; - if ( (ios->f) && (ios->f!=stderr) && (ios->f!=stdout) && (ios->f!=stdin)) - fclose(ios->f); - return; -} - - - - - -/*******************************************************************/ -int inchi_ios_str_getc(INCHI_IOSTREAM *ios) -{ - - if (ios->type==INCHI_IOSTREAM_STRING) - { - if ( ios->s.nPtr < ios->s.nUsedLength ) - { - return (int)ios->s.pStr[ios->s.nPtr++]; - } - return EOF; - } - - else if (ios->type==INCHI_IOSTREAM_FILE) - { - return fgetc( ios->f ); - } - - /* error */ - return EOF; -} - - - -/*******************************************************************/ -char *inchi_ios_str_gets(char *szLine, int len, INCHI_IOSTREAM *f) -{ -int length=0, c=0; - if ( -- len < 0 ) - { - return NULL; - } - while ( length < len && EOF != (c = inchi_ios_str_getc( f )) ) - { - szLine[length++] = (char)c; - if ( c == '\n' ) - break; - } - if ( !length && EOF == c ) - { - return NULL; - } - szLine[length] = '\0'; - return szLine; -} - - - -/********************************************************************************/ -/* read up to len or tab or LF; if empty read next until finds non-empty line */ -/* remove leading and trailing white spaces; keep zero termination */ -/********************************************************************************/ -char *inchi_ios_str_getsTab( char *szLine, int len, INCHI_IOSTREAM *f ) -{ -int length=0, c=0; - if ( --len < 0 ) - { - return NULL; - } - while ( length < len && EOF != (c = inchi_ios_str_getc(f)) ) - { - if ( c == '\t' ) - c = '\n'; - szLine[length++] = (char)c; - if ( c == '\n' ) - break; - } - if ( !length && EOF == c ) - { - return NULL; - } - szLine[length] = '\0'; - return szLine; -} - - -/*******************************************************************/ -int inchi_ios_gets( char *szLine, int len, INCHI_IOSTREAM *f, int *bTooLongLine ) -{ -int length; -char *p; - do - { - p = inchi_ios_str_gets( szLine, len-1, f ); - if ( !p ) - { - *bTooLongLine = 0; - return -1; /* end of file or cannot read */ - } - szLine[len-1] = '\0'; - /* - *bTooLongLine = !strchr( szLine, '\n' ); - */ - p = strchr( szLine, '\n' ); - *bTooLongLine = ( !p && ((int)strlen(szLine)) == len-2 ); - LtrimRtrim( szLine, &length ); - } while ( !length ); - - return length; -} - - -/*******************************************************************/ -/* read up to len or tab or LF; if empty read next until finds non-empty line */ -/* remove leading and trailing white spaces; keep zero termination */ -/*******************************************************************/ -int inchi_ios_getsTab( char *szLine, int len, INCHI_IOSTREAM *f, int *bTooLongLine ) -{ -int length; -char *p; - do - { - p = inchi_ios_str_getsTab( szLine, len-1, f ); - if ( !p ) - { - *bTooLongLine = 0; - return -1; /* end of file or cannot read */ - } - szLine[len-1] = '\0'; - /* - *bTooLongLine = !strchr( szLine, '\n' ); - */ - p = strchr( szLine, '\n' ); - *bTooLongLine = ( !p && ((int)strlen(szLine)) == len-2 ); - LtrimRtrim( szLine, &length ); - } while ( !length ); - return length; -} - -/*******************************************************************/ -int inchi_ios_getsTab1( char *szLine, int len, INCHI_IOSTREAM *f, int *bTooLongLine ) -{ -int length; -char *p; - /*do {*/ - p = inchi_ios_str_getsTab( szLine, len-1, f ); - if ( !p ) - { - *bTooLongLine = 0; - return -1; /* end of file or cannot read */ - } - szLine[len-1] = '\0'; - /* - *bTooLongLine = !strchr( szLine, '\n' ); - */ - p = strchr( szLine, '\n' ); - *bTooLongLine = ( !p && ((int)strlen(szLine)) == len-2 ); - LtrimRtrim( szLine, &length ); - /*} while ( !length );*/ - return length; -} - - - - - - -/*****************************************************************/ -int inchi_ios_print( INCHI_IOSTREAM * ios, const char* lpszFormat, ... ) -{ -int ret=0, ret2=0; -va_list argList; - - if (!ios) - return -1; - - if (ios->type == INCHI_IOSTREAM_STRING) - { - /* output to string buffer */ - int max_len; - my_va_start( argList, lpszFormat ); - max_len = GetMaxPrintfLength( lpszFormat, argList); - va_end( argList ); - if ( max_len >= 0 ) - { - if ( ios->s.nAllocatedLength - ios->s.nUsedLength <= max_len ) - { - /* enlarge output string */ - int nAddLength = inchi_max( INCHI_ADD_STR_LEN, max_len ); - char *new_str = - (char *)inchi_calloc( ios->s.nAllocatedLength + nAddLength, sizeof(new_str[0]) ); - if ( new_str ) - { - if ( ios->s.pStr ) - { - if ( ios->s.nUsedLength > 0 ) - memcpy( new_str, ios->s.pStr, sizeof(new_str[0])* ios->s.nUsedLength ); - inchi_free( ios->s.pStr ); - } - ios->s.pStr = new_str; - ios->s.nAllocatedLength += nAddLength; - } - else return -1; /* failed */ - } - /* output */ - my_va_start( argList, lpszFormat ); - ret = vsprintf( ios->s.pStr + ios->s.nUsedLength, lpszFormat, argList ); - va_end(argList); - if ( ret >= 0 ) - ios->s.nUsedLength += ret; -#ifdef TARGET_LIB_FOR_WINCHI - if( FWPRINT ) - { - my_va_start( argList, lpszFormat ); - FWPRINT( lpszFormat, argList ); - va_end( argList ); - } -#endif - return ret; - } - return -1; - } - - else if (ios->type == INCHI_IOSTREAM_FILE) - { - /* output to file */ - if (ios->f) - { - my_va_start( argList, lpszFormat ); - ret = vfprintf( ios->f, lpszFormat, argList ); - va_end( argList ); - } - else - { - my_va_start( argList, lpszFormat ); - ret2 = vfprintf( stdout, lpszFormat, argList ); - va_end( argList ); - } -#ifdef TARGET_LIB_FOR_WINCHI - if( FWPRINT ) - { - my_va_start( argList, lpszFormat ); - FWPRINT( lpszFormat, argList ); - va_end( argList ); - } -#endif - return ret? ret : ret2; - } - - - /* no output */ - return 0; -} - - - - -/**********************************************************************/ -/* This function's output should not be displayed in the output pane */ -/**********************************************************************/ -int inchi_ios_print_nodisplay( INCHI_IOSTREAM * ios, const char* lpszFormat, ... ) -{ -va_list argList; - - if (!ios) - return -1; - - if (ios->type == INCHI_IOSTREAM_STRING) - { - /* output to string buffer */ - int ret=0, max_len; - my_va_start( argList, lpszFormat ); - max_len = GetMaxPrintfLength( lpszFormat, argList); - va_end( argList ); - if ( max_len >= 0 ) - { - if ( ios->s.nAllocatedLength - ios->s.nUsedLength <= max_len ) - { - /* enlarge output string */ - int nAddLength = inchi_max( INCHI_ADD_STR_LEN, max_len ); - char *new_str = (char *)inchi_calloc( ios->s.nAllocatedLength + nAddLength, sizeof(new_str[0]) ); - if ( new_str ) - { - if ( ios->s.pStr ) - { - if ( ios->s.nUsedLength > 0 ) - { - memcpy( new_str, ios->s.pStr, sizeof(new_str[0])*ios->s.nUsedLength ); - } - inchi_free( ios->s.pStr ); - } - ios->s.pStr = new_str; - ios->s.nAllocatedLength += nAddLength; - } else - { - return -1; /* failed */ - } - } - /* output */ - my_va_start( argList, lpszFormat ); - ret = vsprintf( ios->s.pStr + ios->s.nUsedLength, lpszFormat, argList ); - va_end(argList); - if ( ret >= 0 ) - { - ios->s.nUsedLength += ret; - } - return ret; - } - return -1; - } - - else if (ios->type == INCHI_IOSTREAM_FILE) - { - my_va_start( argList, lpszFormat ); - inchi_print_nodisplay( ios->f, lpszFormat, argList); - va_end(argList); - } - - /* no output */ - return 0; -} - - - - -/*****************************************************************/ -/* Print to string buffer or to file+stderr */ -int inchi_ios_eprint( INCHI_IOSTREAM * ios, const char* lpszFormat, ... ) -{ -int ret=0, ret2=0; -va_list argList; - - if (!ios) - return -1; - - if (ios->type == INCHI_IOSTREAM_STRING) /* was #if ( defined(TARGET_API_LIB) || defined(INCHI_STANDALONE_EXE) ) */ - { - /* output to string buffer */ - int max_len, nAddLength = 0; - char *new_str = NULL; - - my_va_start( argList, lpszFormat ); - max_len = GetMaxPrintfLength( lpszFormat, argList); - va_end( argList ); - - if ( max_len >= 0 ) - { - if ( ios->s.nAllocatedLength - ios->s.nUsedLength <= max_len ) - { - /* enlarge output string */ - nAddLength = inchi_max( INCHI_ADD_STR_LEN, max_len ); - new_str = (char *)inchi_calloc( ios->s.nAllocatedLength + nAddLength, sizeof(new_str[0]) ); - if ( new_str ) - { - if ( ios->s.pStr ) - { - if ( ios->s.nUsedLength > 0 ) - { - memcpy( new_str, ios->s.pStr, sizeof(new_str[0])* ios->s.nUsedLength ); - } - inchi_free( ios->s.pStr ); - } - ios->s.pStr = new_str; - ios->s.nAllocatedLength += nAddLength; - } - else - { - return -1; /* failed */ - } - } - - /* output */ - my_va_start( argList, lpszFormat ); - ret = vsprintf( ios->s.pStr + ios->s.nUsedLength, lpszFormat, argList ); - va_end(argList); - if ( ret >= 0 ) - { - ios->s.nUsedLength += ret; - } - return ret; - } - return -1; - } - - else if (ios->type == INCHI_IOSTREAM_FILE) - { - if ( ios->f) - { - /* output to plain file */ - my_va_start( argList, lpszFormat ); - ret = inchi_vfprintf( ios->f, lpszFormat, argList ); - va_end( argList ); -/*^^^ No output to stderr from within dll or GUI program */ -#if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) ) - if ( ios->f != stderr ) - { - my_va_start( argList, lpszFormat ); - ret2 = vfprintf( stderr, lpszFormat, argList ); - va_end( argList ); - } -#endif - return ret? ret : ret2; - } - } - - /* no output */ - return 0; -} - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - - PLAIN FILE OPERATIONS - - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -/* Print to file, echoing to stderr */ -int inchi_fprintf( FILE* f, const char* lpszFormat, ... ) -{ -int ret=0, ret2=0; -va_list argList; - if (f) - { - my_va_start( argList, lpszFormat ); - ret = inchi_vfprintf( f, lpszFormat, argList ); - va_end( argList ); -/*^^^ No output to stderr from within dll or GUI program */ -#if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) ) - if ( f != stderr ) - { - my_va_start( argList, lpszFormat ); - ret2 = vfprintf( stderr, lpszFormat, argList ); - va_end( argList ); - } -#endif - return ret? ret : ret2; - } - return 0; -} - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -/* Print to file */ -int inchi_vfprintf( FILE* f, const char* lpszFormat, va_list argList ) -{ -int ret=0; - if ( f == stderr && lpszFormat && lpszFormat[0] && '\r' == lpszFormat[strlen(lpszFormat)-1] ) - { -#define CONSOLE_LINE_LEN 80 -#ifndef COMPILE_ANSI_ONLY - char szLine[CONSOLE_LINE_LEN]; - ret = _vsnprintf( szLine, CONSOLE_LINE_LEN-1, lpszFormat, argList ); - if ( ret < 0 ) - { - /* output is longer than the console line */ - /*^^^ Fixed bug: (CONSOLE_LINE_LEN-4) --> (CONSOLE_LINE_LEN-4-1) 11-22-08 IPl */ - strcpy(szLine+CONSOLE_LINE_LEN-5, "...\r"); - } - fputs( szLine, f ); -#else - ret = vfprintf( f, lpszFormat, argList ); -#endif -#undef CONSOLE_LINE_LEN - } - else - { - ret = vfprintf( f, lpszFormat, argList ); - } - return ret; -} - - - -/**********************************************************************/ -/* This function's output should not be displayed in the output pane */ -/**********************************************************************/ -int inchi_print_nodisplay( FILE* f, const char* lpszFormat, ... ) -{ -int ret=0; -va_list argList; -FILE* fi; - if (f) - fi = f; - else - fi = stdout; - my_va_start( argList, lpszFormat ); - ret = vfprintf( fi, lpszFormat, argList ); - return ret; -} - - - -#if ( FIX_READ_LONG_LINE_BUG == 1 ) -/********************************************************************/ -int inchi_fgetsLfTab( char *szLine, int len, FILE *f ) -{ - int length; - char *p; - char szSkip[256]; - int bTooLongLine = 0; - do { - p = inchi_fgetsTab( szLine, len, f ); - if ( !p ) { - return -1; /* end of file or cannot read */ - } - bTooLongLine = ( (int)strlen(szLine) == len-1 && szLine[len-2] != '\n' ); - LtrimRtrim( szLine, &length ); - } while ( !length ); - if ( bTooLongLine ) { - while ( p = inchi_fgetsTab( szSkip, sizeof(szSkip)-1, f ) ) { - if ( strchr( szSkip, '\n' ) ) - break; - } - } - return length; -} -#else -/********************************************************************/ -int inchi_fgetsLfTab( char *szLine, int len, FILE *f ) -{ - int length; - char *p; - char szSkip[256]; - int bTooLongLine = 0; - do { - p = inchi_fgetsTab( szLine, len-1, f ); - if ( !p ) { - return -1; /* end of file or cannot read */ - } - szLine[len-1] = '\0'; - /* - bTooLongLine = !strchr( szLine, '\n' ); - */ - bTooLongLine = ( !p && ((int)strlen(szLine)) == len-2 ); - LtrimRtrim( szLine, &length ); - } while ( !length ); - if ( bTooLongLine ) { - while ( p = inchi_fgetsTab( szSkip, sizeof(szSkip)-1, f ) ) { - szSkip[sizeof(szSkip)-1] = '\0'; - if ( strchr( szSkip, '\n' ) ) - break; - } - } - return length; -} -#endif - - -/*******************************************************************/ -/* read up to len or tab or LF; if empty read next until finds non-empty line */ -/* remove leading and trailing white spaces; keep zero termination */ -/*******************************************************************/ -char *inchi_fgetsTab( char *szLine, int len, FILE *f ) -{ - int length=0, c=0; - len --; - while ( length < len && EOF != (c = fgetc( f )) ) { - if ( c == '\t' ) - c = '\n'; - szLine[length++] = (char)c; - if ( c == '\n' ) - break; - } - if ( !length && EOF == c ) { - return NULL; - } - szLine[length] = '\0'; - return szLine; -} - - - -/******************************************************************/ -/* read not more than line_len bytes from an lf-terminated line */ -/* if input line is too long quietly ignore the rest of the line */ -char* inchi_fgetsLf( char* line, int line_len, FILE* inp ) -{ - char *p, *q; - memset( line, 0, line_len ); - if ( NULL != (p = fgets( line, line_len, inp ) ) && NULL == strchr(p, '\n' ) ){ - char temp[64]; /* bypass up to '\n' or up to end of file whichever comes first*/ - while ( NULL != fgets( temp, sizeof(temp), inp ) && NULL == strchr(temp,'\n') ) - ; - } - if ( p && (q = strchr(line, '\r')) ) { /* fix CR CR LF line terminator. */ - q[0] = '\n'; - q[1] = '\0'; - } - return p; -} - - - - - - - - - - -/***************************************************************** - * - * Estimate printf string length - * - * The code is based on Microsoft Knowledge Base article Q127038: - * "FIX: CString::Format Gives Assertion Failed, Access Violation" - * (Related to Microsoft Visual C++, 32-bit Editions, versions 2.0, 2.1) - * - *****************************************************************/ - -#define FORCE_ANSI 0x10000 -#define FORCE_UNICODE 0x20000 - -/* formatting (using wsprintf style formatting)*/ -int GetMaxPrintfLength( const char *lpszFormat, va_list argList) -{ - /*ASSERT(AfxIsValidString(lpszFormat, FALSE));*/ - const char * lpsz; - int nMaxLen, nWidth, nPrecision, nModifier, nItemLen; - - nMaxLen = 0; - /* make a guess at the maximum length of the resulting string */ - for ( lpsz = lpszFormat; *lpsz; lpsz ++ ) - { - /* handle '%' character, but watch out for '%%' */ - if (*lpsz != '%' || *( ++ lpsz ) == '%') - { - nMaxLen += 1; - continue; - } - - nItemLen = 0; - - /* handle '%' character with format */ - nWidth = 0; - for (; *lpsz; lpsz ++ ) - { - /* check for valid flags */ - if (*lpsz == '#') - nMaxLen += 2; /* for '0x' */ - else if (*lpsz == '*') - nWidth = va_arg(argList, int); - else if (*lpsz == '-' || *lpsz == '+' || *lpsz == '0' - || *lpsz == ' ') - ; - else /* hit non-flag character */ - break; - } - /* get width and skip it */ - if (nWidth == 0) - { - /* width indicated by */ - nWidth = atoi(lpsz); - for (; *lpsz && isdigit(*lpsz); lpsz ++ ) - ; - } - /*ASSERT(nWidth >= 0);*/ - if ( nWidth < 0 ) - goto exit_error; /* instead of exception */ - - nPrecision = 0; - if (*lpsz == '.') - { - /* skip past '.' separator (width.precision)*/ - lpsz ++; - - /* get precision and skip it*/ - if (*lpsz == '*') - { - nPrecision = va_arg(argList, int); - lpsz ++; - } - else - { - nPrecision = atoi(lpsz); - for (; *lpsz && isdigit(*lpsz); lpsz ++) - ; - } - if ( nPrecision < 0 ) - goto exit_error; /* instead of exception */ - } - - /* should be on type modifier or specifier */ - nModifier = 0; - switch (*lpsz) - { - /* modifiers that affect size */ - case 'h': - switch ( lpsz[1] ) { - case 'd': - case 'i': - case 'o': - case 'x': - case 'X': - case 'u': - /* short unsigned, short double, etc. -- added to the original MS example */ - /* ignore the fact that these modifiers do affect size */ - lpsz ++; - break; - default: - nModifier = FORCE_ANSI; - lpsz ++; - break; - } - break; - case 'l': - switch ( lpsz[1] ) { - case 'd': - case 'i': - case 'o': - case 'x': - case 'X': - case 'u': - case 'f': /* long float -- post ANSI C */ - /* long unsigned, long double, etc. -- added to the original MS example */ - /* ignore the fact that these modifiers do affect size */ - lpsz ++; - break; - default: - /* - nModifier = FORCE_UNICODE; - lpsz ++; - break; - */ - goto exit_error; /* no UNICODE, please */ - } - break; - /* modifiers that do not affect size */ - case 'F': - case 'N': - case 'L': - lpsz ++; - break; - } - - /* now should be on specifier */ - switch (*lpsz | nModifier) - { - /* single characters*/ - case 'c': - case 'C': - nItemLen = 2; - va_arg(argList, int); - break; - case 'c'|FORCE_ANSI: - case 'C'|FORCE_ANSI: - nItemLen = 2; - va_arg(argList, int); - break; - case 'c'|FORCE_UNICODE: - case 'C'|FORCE_UNICODE: - goto exit_error; /* no UNICODE, please */ - /* - nItemLen = 2; - va_arg(argList, wchar_t); - break; - */ - - /* strings*/ - case 's': - case 'S': - nItemLen = strlen(va_arg(argList, char*)); - nItemLen = inchi_max(1, nItemLen); - break; - case 's'|FORCE_ANSI: - case 'S'|FORCE_ANSI: - nItemLen = strlen(va_arg(argList, char*)); - nItemLen = inchi_max(1, nItemLen); - break; - - case 's'|FORCE_UNICODE: - case 'S'|FORCE_UNICODE: - goto exit_error; /* no UNICODE, please */ - /* - nItemLen = wcslen(va_arg(argList, wchar_t*)); - nItemLen = inchi_max(1, nItemLen); - break; - */ - - } - - /* adjust nItemLen for strings */ - if (nItemLen != 0) - { - nItemLen = inchi_max(nItemLen, nWidth); - if (nPrecision != 0) - nItemLen = inchi_min(nItemLen, nPrecision); - } - else - { - switch (*lpsz) - { - /* integers */ - case 'd': - case 'i': - case 'u': - case 'x': - case 'X': - case 'o': - va_arg(argList, int); - nItemLen = 32; - nItemLen = inchi_max(nItemLen, nWidth+nPrecision); - break; - - case 'e': - case 'f': - case 'g': - case 'G': - va_arg(argList, double); - nItemLen = 32; - nItemLen = inchi_max(nItemLen, nWidth+nPrecision); - break; - - case 'p': - va_arg(argList, void*); - nItemLen = 32; - nItemLen = inchi_max(nItemLen, nWidth+nPrecision); - break; - - /* no output */ - case 'n': - va_arg(argList, int*); - break; - - default: - /*ASSERT(FALSE);*/ /* unknown formatting option*/ - goto exit_error; /* instead of exception */ - } - } - - /* adjust nMaxLen for output nItemLen */ - nMaxLen += nItemLen; - } - return nMaxLen; - -exit_error: - return -1; /* wrong format */ -} - +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include +#include +#include +#include +#include +#include + + +#include "e_mode.h" + +#include "../../../../INCHI_BASE/src/inchi_api.h" + +#include "e_ctl_data.h" +#include "e_comdef.h" +#include "e_ichicomp.h" +#include "e_util.h" +#include "e_ichi_io.h" +#include "e_ichi_parms.h" + + +#define LtrimRtrim e_LtrimRtrim + +/********************************************** + * output " L=V" or " L missing" or "" + * The fprintf format string must contain %s%s%s%s + */ +char e_gsMissing[] = "is missing"; +char e_gsEmpty[] = ""; +char e_gsSpace[] = " "; +char e_gsEqual[] = "="; + + + + + + +/*******************************************************************/ +void e_PrintFileName( const char *fmt, FILE *output_file, const char *szFname ) +{ + fprintf( output_file, fmt, szFname ); +} + + + +#ifndef TARGET_API_LIB + + +#ifndef INCHI_ADD_STR_LEN +#define INCHI_ADD_STR_LEN 32768 +#endif + + +/*^^^ Internal functions */ + +int inchi_ios_str_getc( INCHI_IOSTREAM *ios ); +char *inchi_ios_str_gets( char *szLine, int len, INCHI_IOSTREAM *ios ); +char *inchi_ios_str_getsTab( char *szLine, int len, INCHI_IOSTREAM *ios ); +int GetMaxPrintfLength( const char *lpszFormat, va_list argList); +char *inchi_fgetsTab( char *szLine, int len, FILE *f ); +int inchi_vfprintf( FILE* f, const char* lpszFormat, va_list argList ); + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + + INCHI_IOSTREAM OPERATIONS + + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Init INCHI_IOSTREAM +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +void inchi_ios_init(INCHI_IOSTREAM* ios, int io_type, FILE *f) +{ + memset( ios, 0, sizeof(*ios) ); + switch (io_type) + { + case INCHI_IOSTREAM_TYPE_FILE: ios->type = INCHI_IOSTREAM_TYPE_FILE; + break; + case INCHI_IOSTREAM_TYPE_STRING: + default: ios->type = INCHI_IOSTREAM_TYPE_STRING; + break; + } + ios->f = f; + return; +} + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + If INCHI_IOSTREAM type is INCHI_IOSTREAM_TYPE_STRING, + flush INCHI_IOSTREAM string buffer to file (if non-NULL); then free buffer. + If INCHI_IOSTREAM type is INCHI_IOSTREAM_TYPE_FILE, just flush the file. +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +void inchi_ios_flush(INCHI_IOSTREAM* ios) +{ + if (ios->type == INCHI_IOSTREAM_TYPE_STRING) + { + if (ios->s.pStr) + { + if (ios->s.nUsedLength > 0) + { + if (ios->f) + { + fprintf(ios->f,"%-s", ios->s.pStr); + fflush(ios->f); + } + inchi_free(ios->s.pStr ); + ios->s.pStr = NULL; + ios->s.nUsedLength = ios->s.nAllocatedLength = ios->s.nPtr = 0; + } + } + } + else if (ios->type == INCHI_IOSTREAM_TYPE_FILE) + { + /* output to plain file: just flush it. */ + fflush(ios->f); + } + return; +} + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + If INCHI_IOSTREAM type is INCHI_IOSTREAM_TYPE_STRING, + flush INCHI_IOSTREAM string buffer to file (if non-NULL) and + another file f2 supplied as parameter (typically, it will be stderr); then free buffer. + If INCHI_IOSTREAM type is INCHI_IOSTREAM_TYPE_FILE, just flush the both files. +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +void inchi_ios_flush2(INCHI_IOSTREAM* ios, FILE *f2) +{ + if (ios->type == INCHI_IOSTREAM_TYPE_STRING) + { + if (ios->s.pStr) + { + if (ios->s.nUsedLength > 0) + { + if (ios->f) + { + fprintf(ios->f,"%-s", ios->s.pStr); + fflush(ios->f); + } + if (f2!=ios->f) + fprintf(f2,"%-s", ios->s.pStr); + + inchi_free(ios->s.pStr ); + ios->s.pStr = NULL; + ios->s.nUsedLength = ios->s.nAllocatedLength = ios->s.nPtr = 0; + } + } + } + else if (ios->type == INCHI_IOSTREAM_TYPE_FILE) + { + /* output to plain file: just flush it. */ + if ( (ios->f) && (ios->f!=stderr) && (ios->f!=stdout) ) + fflush(ios->f); + if ( f2 && f2!=stderr && f2!=stdout) + fflush(f2); + } + + return; +} + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Close INCHI_IOSTREAM: free string buffer and close the file. +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +void inchi_ios_close(INCHI_IOSTREAM* ios) +{ + if (ios->s.pStr) + inchi_free(ios->s.pStr); + ios->s.pStr = NULL; + ios->s.nUsedLength = ios->s.nAllocatedLength = ios->s.nPtr = 0; + if ( (ios->f) && (ios->f!=stderr) && (ios->f!=stdout) && (ios->f!=stdin)) + fclose(ios->f); + return; +} + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Reset INCHI_IOSTREAM: set string buffer ptr to NULL (but do _not_ free memory)and close the file. +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +void inchi_ios_reset(INCHI_IOSTREAM* ios) +{ + ios->s.pStr = NULL; + ios->s.nUsedLength = ios->s.nAllocatedLength = ios->s.nPtr = 0; + if ( (ios->f) && (ios->f!=stderr) && (ios->f!=stdout) && (ios->f!=stdin)) + fclose(ios->f); + return; +} + + + + + +/*******************************************************************/ +int inchi_ios_str_getc(INCHI_IOSTREAM *ios) +{ + if (ios->type==INCHI_IOSTREAM_TYPE_STRING) + { + if ( ios->s.nPtr < ios->s.nUsedLength ) + { + return (int)ios->s.pStr[ios->s.nPtr++]; + } + return EOF; + } + else if (ios->type==INCHI_IOSTREAM_TYPE_FILE) + { + return fgetc( ios->f ); + } + + /* error */ + return EOF; +} + + + +/*******************************************************************/ +char *inchi_ios_str_gets(char *szLine, int len, INCHI_IOSTREAM *f) +{ + int length=0, c=0; + if ( -- len < 0 ) + { + return NULL; + } + while ( length < len && EOF != (c = inchi_ios_str_getc( f )) ) + { + szLine[length++] = (char)c; + if ( c == '\n' ) + break; + } + if ( !length && EOF == c ) + { + return NULL; + } + szLine[length] = '\0'; + return szLine; +} + + + +/********************************************************************************/ +/* read up to len or tab or LF; if empty read next until finds non-empty line */ +/* remove leading and trailing white spaces; keep zero termination */ +/********************************************************************************/ +char *inchi_ios_str_getsTab( char *szLine, int len, INCHI_IOSTREAM *f ) +{ + int length=0, c=0; + if ( --len < 0 ) + { + return NULL; + } + while ( length < len && EOF != (c = inchi_ios_str_getc(f)) ) + { + if ( c == '\t' ) + c = '\n'; + szLine[length++] = (char)c; + if ( c == '\n' ) + break; + } + if ( !length && EOF == c ) + { + return NULL; + } + szLine[length] = '\0'; + return szLine; +} + + +/*******************************************************************/ +int inchi_ios_gets( char *szLine, int len, INCHI_IOSTREAM *f, int *bTooLongLine ) +{ + int length; + char *p; + do + { + p = inchi_ios_str_gets( szLine, len-1, f ); + if ( !p ) + { + *bTooLongLine = 0; + return -1; /* end of file or cannot read */ + } + szLine[len-1] = '\0'; + /* + *bTooLongLine = !strchr( szLine, '\n' ); + */ + p = strchr( szLine, '\n' ); + *bTooLongLine = ( !p && ((int)strlen(szLine)) == len-2 ); + LtrimRtrim( szLine, &length ); + } while ( !length ); + + return length; +} + + +/*******************************************************************/ +/* read up to len or tab or LF; if empty read next until finds non-empty line */ +/* remove leading and trailing white spaces; keep zero termination */ +/*******************************************************************/ +int inchi_ios_getsTab( char *szLine, int len, INCHI_IOSTREAM *f, int *bTooLongLine ) +{ + int length; + char *p; + do + { + p = inchi_ios_str_getsTab( szLine, len-1, f ); + if ( !p ) + { + *bTooLongLine = 0; + return -1; /* end of file or cannot read */ + } + szLine[len-1] = '\0'; + /* + *bTooLongLine = !strchr( szLine, '\n' ); + */ + p = strchr( szLine, '\n' ); + *bTooLongLine = ( !p && ((int)strlen(szLine)) == len-2 ); + LtrimRtrim( szLine, &length ); + } while ( !length ); + return length; +} + +/*******************************************************************/ +int inchi_ios_getsTab1( char *szLine, int len, INCHI_IOSTREAM *f, int *bTooLongLine ) +{ + int length; + char *p; + /*do {*/ + p = inchi_ios_str_getsTab( szLine, len-1, f ); + if ( !p ) + { + *bTooLongLine = 0; + return -1; /* end of file or cannot read */ + } + szLine[len-1] = '\0'; + /* + *bTooLongLine = !strchr( szLine, '\n' ); + */ + p = strchr( szLine, '\n' ); + *bTooLongLine = ( !p && ((int)strlen(szLine)) == len-2 ); + LtrimRtrim( szLine, &length ); + /*} while ( !length );*/ + return length; +} + + + + + + +/*****************************************************************/ +int inchi_ios_print( INCHI_IOSTREAM * ios, const char* lpszFormat, ... ) +{ + if (!ios) return -1; + + if (ios->type == INCHI_IOSTREAM_TYPE_STRING) + { + + /* output to string buffer */ + + int ret=0, max_len; + va_list argList; + my_va_start( argList, lpszFormat ); + max_len = GetMaxPrintfLength( lpszFormat, argList); + va_end( argList ); + + if ( max_len >= 0 ) + { + if ( ios->s.nAllocatedLength - ios->s.nUsedLength <= max_len ) + { + /* enlarge output string */ + int nAddLength = inchi_max( INCHI_ADD_STR_LEN, max_len ); + char *new_str = + (char *)inchi_calloc( ios->s.nAllocatedLength + nAddLength, sizeof(new_str[0]) ); + if ( new_str ) + { + if ( ios->s.pStr ) + { + if ( ios->s.nUsedLength > 0 ) + memcpy( new_str, ios->s.pStr, sizeof(new_str[0])* ios->s.nUsedLength ); + inchi_free( ios->s.pStr ); + } + ios->s.pStr = new_str; + ios->s.nAllocatedLength += nAddLength; + } + else return -1; /* failed */ + } + /* output */ + my_va_start( argList, lpszFormat ); + ret = vsprintf( ios->s.pStr + ios->s.nUsedLength, lpszFormat, argList ); + va_end(argList); + if ( ret >= 0 ) + ios->s.nUsedLength += ret; + return ret; + } + return -1; + } + + else if (ios->type == INCHI_IOSTREAM_TYPE_FILE) + { + /* output to file */ + int ret=0, ret2=0; + va_list argList; + if (ios->f) + { + my_va_start( argList, lpszFormat ); + ret = vfprintf( ios->f, lpszFormat, argList ); + va_end( argList ); + } + else + { + my_va_start( argList, lpszFormat ); + ret2 = vfprintf( stdout, lpszFormat, argList ); + va_end( argList ); + } +#ifdef BUILD_LIB_FOR_WINCHI + if( FWPRINT ) + { + my_va_start( argList, lpszFormat ); + FWPRINT( lpszFormat, argList ); + va_end( argList ); + } +#endif + return ret? ret : ret2; + } + + + /* no output */ + return 0; +} + + + + +/**********************************************************************/ +/* This function's output should not be displayed in the output pane */ +/**********************************************************************/ +int inchi_ios_print_nodisplay( INCHI_IOSTREAM * ios, const char* lpszFormat, ... ) +{ + if (!ios) return -1; + + if (ios->type == INCHI_IOSTREAM_TYPE_STRING) + { + /* output to string buffer */ + int ret=0, max_len; + va_list argList; + my_va_start( argList, lpszFormat ); + max_len = GetMaxPrintfLength( lpszFormat, argList); + va_end( argList ); + + if ( max_len >= 0 ) + { + if ( ios->s.nAllocatedLength - ios->s.nUsedLength <= max_len ) + { + /* enlarge output string */ + int nAddLength = inchi_max( INCHI_ADD_STR_LEN, max_len ); + char *new_str = (char *)inchi_calloc( ios->s.nAllocatedLength + nAddLength, sizeof(new_str[0]) ); + if ( new_str ) + { + if ( ios->s.pStr ) + { + if ( ios->s.nUsedLength > 0 ) + { + memcpy( new_str, ios->s.pStr, sizeof(new_str[0])*ios->s.nUsedLength ); + } + inchi_free( ios->s.pStr ); + } + ios->s.pStr = new_str; + ios->s.nAllocatedLength += nAddLength; + } else + { + return -1; /* failed */ + } + } + /* output */ + my_va_start( argList, lpszFormat ); + ret = vsprintf( ios->s.pStr + ios->s.nUsedLength, lpszFormat, argList ); + va_end(argList); + if ( ret >= 0 ) + { + ios->s.nUsedLength += ret; + } + return ret; + } + return -1; + } + + else if (ios->type == INCHI_IOSTREAM_TYPE_FILE) + { + /* output to file */ + int ret=0, ret2=0; + va_list argList; + if (ios->f) + { + my_va_start( argList, lpszFormat ); + ret = vfprintf( ios->f, lpszFormat, argList ); + va_end( argList ); + } + else + { + my_va_start( argList, lpszFormat ); + ret2 = vfprintf( stdout, lpszFormat, argList ); + va_end( argList ); + } + return ret? ret : ret2; + } + + /* no output */ + return 0; +} + + + + +/*****************************************************************/ +/* Print to string buffer or to file+stderr */ +int inchi_ios_eprint( INCHI_IOSTREAM * ios, const char* lpszFormat, ... ) +{ +int ret=0, ret2=0; +va_list argList; + + if (!ios) + return -1; + + if (ios->type == INCHI_IOSTREAM_TYPE_STRING) /* was #if ( defined(TARGET_API_LIB) || defined(BUILD_CINCHI_WITH_INCHIKEY) ) */ + { + /* output to string buffer */ + int max_len, nAddLength = 0; + char *new_str = NULL; + + my_va_start( argList, lpszFormat ); + max_len = GetMaxPrintfLength( lpszFormat, argList); + va_end( argList ); + + if ( max_len >= 0 ) + { + if ( ios->s.nAllocatedLength - ios->s.nUsedLength <= max_len ) + { + /* enlarge output string */ + nAddLength = inchi_max( INCHI_ADD_STR_LEN, max_len ); + new_str = (char *)inchi_calloc( ios->s.nAllocatedLength + nAddLength, sizeof(new_str[0]) ); + if ( new_str ) + { + if ( ios->s.pStr ) + { + if ( ios->s.nUsedLength > 0 ) + { + memcpy( new_str, ios->s.pStr, sizeof(new_str[0])* ios->s.nUsedLength ); + } + inchi_free( ios->s.pStr ); + } + ios->s.pStr = new_str; + ios->s.nAllocatedLength += nAddLength; + } + else + { + return -1; /* failed */ + } + } + + /* output */ + my_va_start( argList, lpszFormat ); + ret = vsprintf( ios->s.pStr + ios->s.nUsedLength, lpszFormat, argList ); + va_end(argList); + if ( ret >= 0 ) + { + ios->s.nUsedLength += ret; + } + return ret; + } + return -1; + } + + else if (ios->type == INCHI_IOSTREAM_TYPE_FILE) + { + if ( ios->f) + { + /* output to plain file */ + my_va_start( argList, lpszFormat ); + ret = inchi_vfprintf( ios->f, lpszFormat, argList ); + va_end( argList ); +#if ( !defined(TARGET_API_LIB) && !defined(BUILD_LIB_FOR_WINCHI) ) + /*^^^ No output to stderr from within dll or GUI program */ + if ( ios->f != stderr ) + { + my_va_start( argList, lpszFormat ); + ret2 = vfprintf( stderr, lpszFormat, argList ); + va_end( argList ); + } +#endif + return ret? ret : ret2; + } + } + + /* no output */ + return 0; +} + + + + + + + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + + PLAIN FILE OPERATIONS + + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ + +/* Print to file, echoing to stderr */ +int inchi_fprintf( FILE* f, const char* lpszFormat, ... ) +{ +int ret=0, ret2=0; +va_list argList; + if (f) + { + my_va_start( argList, lpszFormat ); + ret = inchi_vfprintf( f, lpszFormat, argList ); + va_end( argList ); +/*^^^ No output to stderr from within dll or GUI program */ +#if ( !defined(TARGET_API_LIB) && !defined(BUILD_LIB_FOR_WINCHI) ) + if ( f != stderr ) + { + my_va_start( argList, lpszFormat ); + ret2 = vfprintf( stderr, lpszFormat, argList ); + va_end( argList ); + } +#endif + return ret? ret : ret2; + } + return 0; +} + + + + +/* Print to file */ +int inchi_vfprintf( FILE* f, const char* lpszFormat, va_list argList ) +{ +int ret=0; + + if ( f == stderr && lpszFormat && lpszFormat[0] && '\r' == lpszFormat[strlen(lpszFormat)-1] ) + { +#define CONSOLE_LINE_LEN 80 +#ifndef COMPILE_ANSI_ONLY + char szLine[CONSOLE_LINE_LEN]; + ret = _vsnprintf( szLine, CONSOLE_LINE_LEN-1, lpszFormat, argList ); + if ( ret < 0 ) + { + /* output is longer than the console line */ + /*^^^ Fixed bug: (CONSOLE_LINE_LEN-4) --> (CONSOLE_LINE_LEN-4-1) 11-22-08 IPl */ + strcpy(szLine+CONSOLE_LINE_LEN-5, "...\r"); + } + fputs( szLine, f ); +#else + ret = vfprintf( f, lpszFormat, argList ); +#endif +#undef CONSOLE_LINE_LEN + } + else + { + ret = vfprintf( f, lpszFormat, argList ); + } + return ret; +} + + +#if ( FIX_READ_LONG_LINE_BUG == 1 ) +/********************************************************************/ +int inchi_fgetsLfTab( char *szLine, int len, FILE *f ) +{ + int length; + char *p; + char szSkip[256]; + int bTooLongLine = 0; + do { + p = inchi_fgetsTab( szLine, len, f ); + if ( !p ) { + return -1; /* end of file or cannot read */ + } + bTooLongLine = ( (int)strlen(szLine) == len-1 && szLine[len-2] != '\n' ); + LtrimRtrim( szLine, &length ); + } while ( !length ); + if ( bTooLongLine ) { + while ( p = inchi_fgetsTab( szSkip, sizeof(szSkip)-1, f ) ) { + if ( strchr( szSkip, '\n' ) ) + break; + } + } + return length; +} +#else +/********************************************************************/ +int inchi_fgetsLfTab( char *szLine, int len, FILE *f ) +{ + int length; + char *p; + char szSkip[256]; + int bTooLongLine = 0; + do { + p = inchi_fgetsTab( szLine, len-1, f ); + if ( !p ) { + return -1; /* end of file or cannot read */ + } + szLine[len-1] = '\0'; + /* + bTooLongLine = !strchr( szLine, '\n' ); + */ + bTooLongLine = ( !p && ((int)strlen(szLine)) == len-2 ); + LtrimRtrim( szLine, &length ); + } while ( !length ); + if ( bTooLongLine ) { + while ( p = inchi_fgetsTab( szSkip, sizeof(szSkip)-1, f ) ) { + szSkip[sizeof(szSkip)-1] = '\0'; + if ( strchr( szSkip, '\n' ) ) + break; + } + } + return length; +} +#endif + + +/*******************************************************************/ +/* read up to len or tab or LF; if empty read next until finds non-empty line */ +/* remove leading and trailing white spaces; keep zero termination */ +/*******************************************************************/ +char *inchi_fgetsTab( char *szLine, int len, FILE *f ) +{ + int length=0, c=0; + len --; + while ( length < len && EOF != (c = fgetc( f )) ) { + if ( c == '\t' ) + c = '\n'; + szLine[length++] = (char)c; + if ( c == '\n' ) + break; + } + if ( !length && EOF == c ) { + return NULL; + } + szLine[length] = '\0'; + return szLine; +} + + + +/******************************************************************/ +/* read not more than line_len bytes from an lf-terminated line */ +/* if input line is too long quietly ignore the rest of the line */ +char* inchi_fgetsLf( char* line, int line_len, FILE* inp ) +{ + char *p, *q; + memset( line, 0, line_len ); + if ( NULL != (p = fgets( line, line_len, inp ) ) && NULL == strchr(p, '\n' ) ){ + char temp[64]; /* bypass up to '\n' or up to end of file whichever comes first*/ + while ( NULL != fgets( temp, sizeof(temp), inp ) && NULL == strchr(temp,'\n') ) + ; + } + if ( p && (q = strchr(line, '\r')) ) { /* fix CR CR LF line terminator. */ + q[0] = '\n'; + q[1] = '\0'; + } + return p; +} + + + + + + + + + + +/***************************************************************** + * + * Estimate printf string length + * + * The code is based on Microsoft Knowledge Base article Q127038: + * "FIX: CString::Format Gives Assertion Failed, Access Violation" + * (Related to Microsoft Visual C++, 32-bit Editions, versions 2.0, 2.1) + * + *****************************************************************/ + +#define FORCE_ANSI 0x10000 +#define FORCE_UNICODE 0x20000 + +/* formatting (using wsprintf style formatting)*/ +int GetMaxPrintfLength( const char *lpszFormat, va_list argList) +{ + /*ASSERT(AfxIsValidString(lpszFormat, FALSE));*/ + const char * lpsz; + int nMaxLen, nWidth, nPrecision, nModifier, nItemLen; + + nMaxLen = 0; + /* make a guess at the maximum length of the resulting string */ + for ( lpsz = lpszFormat; *lpsz; lpsz ++ ) + { + /* handle '%' character, but watch out for '%%' */ + if (*lpsz != '%' || *( ++ lpsz ) == '%') + { + nMaxLen += 1; + continue; + } + + nItemLen = 0; + + /* handle '%' character with format */ + nWidth = 0; + for (; *lpsz; lpsz ++ ) + { + /* check for valid flags */ + if (*lpsz == '#') + nMaxLen += 2; /* for '0x' */ + else if (*lpsz == '*') + nWidth = va_arg(argList, int); + else if (*lpsz == '-' || *lpsz == '+' || *lpsz == '0' + || *lpsz == ' ') + ; + else /* hit non-flag character */ + break; + } + /* get width and skip it */ + if (nWidth == 0) + { + /* width indicated by */ + nWidth = atoi(lpsz); + for (; *lpsz && isdigit(*lpsz); lpsz ++ ) + ; + } + /*ASSERT(nWidth >= 0);*/ + if ( nWidth < 0 ) + goto exit_error; /* instead of exception */ + + nPrecision = 0; + if (*lpsz == '.') + { + /* skip past '.' separator (width.precision)*/ + lpsz ++; + + /* get precision and skip it*/ + if (*lpsz == '*') + { + nPrecision = va_arg(argList, int); + lpsz ++; + } + else + { + nPrecision = atoi(lpsz); + for (; *lpsz && isdigit(*lpsz); lpsz ++) + ; + } + if ( nPrecision < 0 ) + goto exit_error; /* instead of exception */ + } + + /* should be on type modifier or specifier */ + nModifier = 0; + switch (*lpsz) + { + /* modifiers that affect size */ + case 'h': + switch ( lpsz[1] ) { + case 'd': + case 'i': + case 'o': + case 'x': + case 'X': + case 'u': + /* short unsigned, short double, etc. -- added to the original MS example */ + /* ignore the fact that these modifiers do affect size */ + lpsz ++; + break; + default: + nModifier = FORCE_ANSI; + lpsz ++; + break; + } + break; + case 'l': + switch ( lpsz[1] ) { + case 'd': + case 'i': + case 'o': + case 'x': + case 'X': + case 'u': + case 'f': /* long float -- post ANSI C */ + /* long unsigned, long double, etc. -- added to the original MS example */ + /* ignore the fact that these modifiers do affect size */ + lpsz ++; + break; + default: + /* + nModifier = FORCE_UNICODE; + lpsz ++; + break; + */ + goto exit_error; /* no UNICODE, please */ + } + break; + /* modifiers that do not affect size */ + case 'F': + case 'N': + case 'L': + lpsz ++; + break; + } + + /* now should be on specifier */ + switch (*lpsz | nModifier) + { + /* single characters*/ + case 'c': + case 'C': + nItemLen = 2; + va_arg(argList, int); + break; + case 'c'|FORCE_ANSI: + case 'C'|FORCE_ANSI: + nItemLen = 2; + va_arg(argList, int); + break; + case 'c'|FORCE_UNICODE: + case 'C'|FORCE_UNICODE: + goto exit_error; /* no UNICODE, please */ + /* + nItemLen = 2; + va_arg(argList, wchar_t); + break; + */ + + /* strings*/ + case 's': + case 'S': + nItemLen = strlen(va_arg(argList, char*)); + nItemLen = inchi_max(1, nItemLen); + break; + case 's'|FORCE_ANSI: + case 'S'|FORCE_ANSI: + nItemLen = strlen(va_arg(argList, char*)); + nItemLen = inchi_max(1, nItemLen); + break; + + case 's'|FORCE_UNICODE: + case 'S'|FORCE_UNICODE: + goto exit_error; /* no UNICODE, please */ + /* + nItemLen = wcslen(va_arg(argList, wchar_t*)); + nItemLen = inchi_max(1, nItemLen); + break; + */ + } + + /* adjust nItemLen for strings */ + if (nItemLen != 0) + { + nItemLen = inchi_max(nItemLen, nWidth); + if (nPrecision != 0) + nItemLen = inchi_min(nItemLen, nPrecision); + } + else + { + switch (*lpsz) + { + /* integers */ + case 'd': + case 'i': + case 'u': + case 'x': + case 'X': + case 'o': + va_arg(argList, int); + nItemLen = 32; + nItemLen = inchi_max(nItemLen, nWidth+nPrecision); + break; + + case 'e': + case 'f': + case 'g': + case 'G': + va_arg(argList, double); + nItemLen = 32; + nItemLen = inchi_max(nItemLen, nWidth+nPrecision); + break; + + case 'p': + va_arg(argList, void*); + nItemLen = 32; + nItemLen = inchi_max(nItemLen, nWidth+nPrecision); + break; + + /* no output */ + case 'n': + va_arg(argList, int*); + break; + + default: + /*ASSERT(FALSE);*/ /* unknown formatting option*/ + goto exit_error; /* instead of exception */ + } + } + + /* adjust nMaxLen for output nItemLen */ + nMaxLen += nItemLen; + } + return nMaxLen; + +exit_error: + return -1; /* wrong format */ +} + + + +#endif diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/e_ichi_io.h b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichi_io.h similarity index 71% rename from INCHI-1-SRC/INCHI_API/inchi_main/e_ichi_io.h rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichi_io.h index 40dc6c5..a8ac2cc 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_main/e_ichi_io.h +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichi_io.h @@ -1,135 +1,127 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __E_ICHI_IO_H__ -#define __E_ICHI_IO_H__ - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - -#ifndef TARGET_API_LIB - - -#define inchi_ios_print e_inchi_ios_print - -#define GetMaxPrintfLength e_GetMaxPrintfLength -#define inchi_fgetsTab e_inchi_fgetsTab -#define inchi_vfprintf e_inchi_vfprintf -#define inchi_ios_str_getc e_chi_ios_str_getc -#define inchi_ios_str_gets e_chi_ios_str_gets -#define inchi_ios_str_getsTab e_chi_ios_str_getsTab - -#define inchi_fprintf e_inchi_fprintf - -#define inchi_fgetsLf e_inchi_fgetsLf -#define inchi_fgetsLfTab e_inchi_fgetsLfTab -#define inchi_ios_print_nodisplay e_inchi_ios_print_nodisplay -#define inchi_ios_eprint e_inchi_ios_eprint -#define inchi_ios_init e_inchi_ios_init -#define inchi_ios_flush e_inchi_ios_flush -#define inchi_ios_flush2 e_inchi_ios_flush2 -#define inchi_ios_close e_inchi_ios_close -#define inchi_ios_reset e_inchi_ios_reset -#define inchi_ios_gets e_inchi_ios_gets -#define inchi_ios_getsTab e_inchi_ios_getsTab -#define inchi_ios_getsTab1 e_inchi_ios_getsTab1 - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - - INCHI_IOSTREAM OPERATIONS - - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - -void inchi_ios_init(INCHI_IOSTREAM *ios, int io_type, FILE *f); -void inchi_ios_flush(INCHI_IOSTREAM *ios); -void inchi_ios_flush2(INCHI_IOSTREAM *ios, FILE *f2); -void inchi_ios_close(INCHI_IOSTREAM *ios); -void inchi_ios_reset(INCHI_IOSTREAM *ios); - -int inchi_ios_gets( char *szLine, int len, INCHI_IOSTREAM *ios, int *bTooLongLine ); -int inchi_ios_getsTab( char *szLine, int len, INCHI_IOSTREAM *ios, int *bTooLongLine ); -int inchi_ios_getsTab1( char *szLine, int len, INCHI_IOSTREAM *ios, int *bTooLongLine ); - -int inchi_ios_print( INCHI_IOSTREAM *ios, const char* lpszFormat, ... ); -int inchi_ios_print_nodisplay( INCHI_IOSTREAM *ios, const char* lpszFormat, ... ); - -/* Print to string buffer or to file+stderr */ -int inchi_ios_eprint( INCHI_IOSTREAM *ios, const char* lpszFormat, ... ); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - - PLAIN FILE OPERATIONS - - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -/* Print to file, echoing to stderr */ -int inchi_fprintf( FILE* f, const char* lpszFormat, ... ); - -char* inchi_fgetsLf( char* line, int line_len, FILE* inp ); -int inchi_fgetsLfTab( char *szLine, int len, FILE *f ); - - -#endif - - - -void e_PrintFileName( const char *fmt, FILE *output_file, const char *szFname ); -unsigned long e_ulMyGetTickCount( int bStart ); -unsigned long e_ulMyTickCountDiff( unsigned long ulTickEnd, unsigned long ulTickStart ); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /* __E_ICHI_IO_H__ */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef __E_ICHI_IO_H__ +#define __E_ICHI_IO_H__ + + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + + +#ifndef TARGET_API_LIB + + +#define inchi_ios_print e_inchi_ios_print + +#define GetMaxPrintfLength e_GetMaxPrintfLength +#define inchi_fgetsTab e_inchi_fgetsTab +#define inchi_vfprintf e_inchi_vfprintf +#define inchi_ios_str_getc e_chi_ios_str_getc +#define inchi_ios_str_gets e_chi_ios_str_gets +#define inchi_ios_str_getsTab e_chi_ios_str_getsTab + +#define inchi_fprintf e_inchi_fprintf + +#define inchi_fgetsLf e_inchi_fgetsLf +#define inchi_fgetsLfTab e_inchi_fgetsLfTab +#define inchi_ios_print_nodisplay e_inchi_ios_print_nodisplay +#define inchi_ios_eprint e_inchi_ios_eprint +#define inchi_ios_init e_inchi_ios_init +#define inchi_ios_flush e_inchi_ios_flush +#define inchi_ios_flush2 e_inchi_ios_flush2 +#define inchi_ios_close e_inchi_ios_close +#define inchi_ios_reset e_inchi_ios_reset +#define inchi_ios_gets e_inchi_ios_gets +#define inchi_ios_getsTab e_inchi_ios_getsTab +#define inchi_ios_getsTab1 e_inchi_ios_getsTab1 + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + + INCHI_IOSTREAM OPERATIONS + + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ + + +void inchi_ios_init(INCHI_IOSTREAM *ios, int io_type, FILE *f); +void inchi_ios_flush(INCHI_IOSTREAM *ios); +void inchi_ios_flush2(INCHI_IOSTREAM *ios, FILE *f2); +void inchi_ios_close(INCHI_IOSTREAM *ios); +void inchi_ios_reset(INCHI_IOSTREAM *ios); + +int inchi_ios_gets( char *szLine, int len, INCHI_IOSTREAM *ios, int *bTooLongLine ); +int inchi_ios_getsTab( char *szLine, int len, INCHI_IOSTREAM *ios, int *bTooLongLine ); +int inchi_ios_getsTab1( char *szLine, int len, INCHI_IOSTREAM *ios, int *bTooLongLine ); + +int inchi_ios_print( INCHI_IOSTREAM *ios, const char* lpszFormat, ... ); +int inchi_ios_print_nodisplay( INCHI_IOSTREAM *ios, const char* lpszFormat, ... ); + +/* Print to string buffer or to file+stderr */ +int inchi_ios_eprint( INCHI_IOSTREAM *ios, const char* lpszFormat, ... ); + + +/* + PLAIN FILE OPERATIONS +*/ + +/* Print to file, echoing to stderr */ +int inchi_fprintf( FILE* f, const char* lpszFormat, ... ); + +char* inchi_fgetsLf( char* line, int line_len, FILE* inp ); +int inchi_fgetsLfTab( char *szLine, int len, FILE *f ); + + +#endif + +void e_PrintFileName( const char *fmt, FILE *output_file, const char *szFname ); + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + + +#endif /* __E_ICHI_IO_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/e_ichi_parms.c b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichi_parms.c similarity index 64% rename from INCHI-1-SRC/INCHI_API/inchi_main/e_ichi_parms.c rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichi_parms.c index 9574f76..473bc98 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_main/e_ichi_parms.c +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichi_parms.c @@ -1,75 +1,74 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include -#include - -#include "e_mode.h" -#include "e_ctl_data.h" - -#include "inchi_api.h" -#include "e_ichi_io.h" -#include "e_ichi_parms.h" -#include "e_util.h" -#include "e_ichicomp.h" - -#define DetectInputINChIFileType e_DetectInputINChIFileType -#define ReadCommandLineParms e_ReadCommandLineParms -#define mystrncpy e_mystrncpy - - -#define LtrimRtrim e_LtrimRtrim -#define PrintInputParms e_PrintInputParms -#define OpenFiles e_OpenFiles -#define PrintFileName e_PrintFileName - -/*^^^ */ -#define HelpCommandLineParms e_HelpCommandLineParms -#define HelpCommandLineParmsReduced e_HelpCommandLineParmsReduced -/*^^^ */ - - - -#include "../inchi_dll/ichiparm.h" - +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include +#include +#include +#include +#include + +#include "e_mode.h" + +#include "../../../../INCHI_BASE/src/inchi_api.h" + +#include "e_ctl_data.h" +#include "e_ichi_io.h" +#include "e_ichi_parms.h" +#include "e_util.h" +#include "e_ichicomp.h" + +#define DetectInputINChIFileType e_DetectInputINChIFileType +#define ReadCommandLineParms e_ReadCommandLineParms +#define mystrncpy e_mystrncpy + + +#define LtrimRtrim e_LtrimRtrim +#define PrintInputParms e_PrintInputParms +#define OpenFiles e_OpenFiles +#define PrintFileName e_PrintFileName + +/*^^^ */ +#define HelpCommandLineParms e_HelpCommandLineParms +#define HelpCommandLineParmsReduced e_HelpCommandLineParmsReduced +/*^^^ */ + + +#define lrtrim e_LtrimRtrim +#include "../../../../INCHI_BASE/src/ichiparm.h" diff --git a/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichi_parms.h b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichi_parms.h new file mode 100644 index 0000000..5716499 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichi_parms.h @@ -0,0 +1,75 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef __ICHI_PARMS_H__ +#define __ICHI_PARMS_H__ + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + +int e_ReadCommandLineParms( int argc, + const char *argv[], + INPUT_PARMS *ip, + char *szSdfDataValue, + unsigned long *ulDisplTime, + int bReleaseVersion, + INCHI_IOSTREAM *log_file ); +int e_PrintInputParms( INCHI_IOSTREAM *log_file, + INPUT_PARMS *ip ); +int e_OpenFiles( FILE **inp_file, + FILE **output_file, + FILE **log_file, + FILE **prb_file, + INPUT_PARMS *ip ); +void e_HelpCommandLineParms(INCHI_IOSTREAM *f ); +#ifdef CREATE_INCHI_STEP_BY_STEP +void e_HelpCommandLineParmsReduced(INCHI_IOSTREAM *f ); +#endif + + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + +#endif /* __ICHI_PARMS_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/e_ichicomp.h b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichicomp.h similarity index 58% rename from INCHI-1-SRC/INCHI_API/inchi_main/e_ichicomp.h rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichicomp.h index b9d7c99..63de214 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_main/e_ichicomp.h +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichicomp.h @@ -1,92 +1,81 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHI_COMPAT_H__ -#define __INCHI_COMPAT_H__ - -/* compatibility */ - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -#if( defined(__GNUC__) && defined(__MINGW32__) && __GNUC__ == 3 && __GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ == 0 && defined(_WIN32) ) -/* replace with the proper definition for GNU gcc & MinGW-2.0.0-3 (mingw special 20020817-1), gcc 3.2 core */ -#define my_va_start(A,B) ((A)=(va_list)__builtin_next_arg(lpszFormat)) -#else -#define my_va_start va_start -#endif - - - -/* ANSI redefinitions */ -#ifdef COMPILE_ANSI_ONLY /* { */ -#ifndef __isascii -#define __isascii(val) ((unsigned)(val) <= 0x7F) -#endif - -/* #ifndef __GNUC__ */ -/* these non-ANSI functions are implemented in gcc */ -#include - /* this #include provides size_t definition */ - /* implementation is located in util.c */ -#if ( !defined(_MSC_VER) || defined(__STDC__) && __STDC__ == 1 ) -/* support (VC++ Language extensions) = OFF && defined(COMPILE_ANSI_ONLY) */ -int memicmp (const void*, const void*, size_t); -int stricmp( const char *s1, const char *s2 ); -char *_strnset( char *string, int c, size_t count ); -char *_strdup( const char *string ); -#endif -/* #endif */ - -#endif /* } */ - -#define inchi_max(a,b) (((a)>(b))?(a):(b)) -#define inchi_min(a,b) (((a)<(b))?(a):(b)) - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -#endif /* __INCHI_COMPAT_H__ */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef __INCHI_COMPAT_H__ +#define __INCHI_COMPAT_H__ + +/* compatibility */ + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + +#if( defined(__GNUC__) && defined(__MINGW32__) && __GNUC__ == 3 && __GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ == 0 && defined(_WIN32) ) +/* replace with the proper definition for GNU gcc & MinGW-2.0.0-3 (mingw special 20020817-1), gcc 3.2 core */ +#define my_va_start(A,B) ((A)=(va_list)__builtin_next_arg(lpszFormat)) +#else +#define my_va_start va_start +#endif + + + +/* ANSI redefinitions */ +#ifdef COMPILE_ANSI_ONLY /* { */ +#ifndef __isascii +#define __isascii(val) ((unsigned)(val) <= 0x7F) +#endif + +/* #ifndef __GNUC__ */ +/* these non-ANSI functions are implemented in gcc */ +#include + /* this #include provides size_t definition */ +#endif /* } */ + +#define inchi_max(a,b) (((a)>(b))?(a):(b)) +#define inchi_min(a,b) (((a)<(b))?(a):(b)) + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + +#endif /* __INCHI_COMPAT_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/e_ichierr.h b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichierr.h similarity index 65% rename from INCHI-1-SRC/INCHI_API/inchi_main/e_ichierr.h rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichierr.h index 1958f0a..e8cbf02 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_main/e_ichierr.h +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichierr.h @@ -1,71 +1,69 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHIERR_H__ -#define __INCHIERR_H__ - -#define _IS_OKAY 0 -#define _IS_WARNING 1 -#define _IS_ERROR 2 /* Microsoft defined its own IS_ERROR() macro */ -#define _IS_FATAL 3 -#define _IS_UNKNOWN 4 /* unknown error: used in INChI DLL only */ -#define _IS_EOF -1 /* end of file */ -#define _IS_SKIP -2 - - - -#define INCHI_INP_ERROR_ERR 40 -#define INCHI_INP_ERROR_RET (-1) - -#define INCHI_INP_FATAL_ERR 1 -#define INCHI_INP_FATAL_RET 0 - -#define INCHI_INP_EOF_ERR 11 -#define INCHI_INP_EOF_RET 0 - -#define LOG_MASK_WARN 1 -#define LOG_MASK_ERR 2 -#define LOG_MASK_FATAL 4 - -#define LOG_MASK_ALL (LOG_MASK_WARN | LOG_MASK_ERR | LOG_MASK_FATAL) -#define LOG_MASK_NO_WARN (LOG_MASK_ERR | LOG_MASK_FATAL) - - -#endif /* __INCHIERR_H__ */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef __INCHIERR_H__ +#define __INCHIERR_H__ + +#define _IS_OKAY 0 +#define _IS_WARNING 1 +#define _IS_ERROR 2 /* Microsoft defined its own IS_ERROR() macro */ +#define _IS_FATAL 3 +#define _IS_UNKNOWN 4 /* unknown error: used in INChI DLL only */ +#define _IS_EOF -1 /* end of file */ +#define _IS_SKIP -2 + + + +#define INCHI_INP_ERROR_ERR 40 +#define INCHI_INP_ERROR_RET (-1) + +#define INCHI_INP_FATAL_ERR 1 +#define INCHI_INP_FATAL_RET 0 + +#define INCHI_INP_EOF_ERR 11 +#define INCHI_INP_EOF_RET 0 + +#define LOG_MASK_WARN 1 +#define LOG_MASK_ERR 2 +#define LOG_MASK_FATAL 4 + +#define LOG_MASK_ALL (LOG_MASK_WARN | LOG_MASK_ERR | LOG_MASK_FATAL) +#define LOG_MASK_NO_WARN (LOG_MASK_ERR | LOG_MASK_FATAL) + +#endif /* __INCHIERR_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/e_ichimain.c b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichimain.c similarity index 87% rename from INCHI-1-SRC/INCHI_API/inchi_main/e_ichimain.c rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichimain.c index 3f1b34d..68b646c 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_main/e_ichimain.c +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichimain.c @@ -1,963 +1,962 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#if( defined( WIN32 ) && defined( _CONSOLE ) && defined(_MSC_VER) && _MSC_VER >= 800 ) -#define ADD_WIN_CTLC /* detect Ctrl-C under Win-32 and Microsoft Visual C ++ */ -#endif - -#if( defined( WIN32 ) && defined( _CONSOLE ) && defined(_MSC_VER) && _MSC_VER >= 800 && defined(ADD_WIN_CTLC) && !(defined(__STDC__) && __STDC__ == 1) ) -#include -#endif - - -/* #define CREATE_0D_PARITIES */ /* uncomment to replace coordinates and 2D-parirties with 0D-parities */ -/* in case of CREATE_0D_PARITIES, the hardcoded bFixSp3Bug = 1 fixes sp3 bugs in original InChI v. 1.00 */ -/* in case of CREATE_0D_PARITIES, the Phosphine and Arsine sp3 stereo options are not supported */ - -/* #define MAKE_INCHI_FROM_AUXINFO */ /* uncomment to create the second InChI out of AuxInfo and compare */ -#define INCHI_TO_STRUCTURE /* uncomment to convert Struct->InChI->Struct->InChI in case of /Inchi2Struct option */ -/* #define NEIGH_ONLY_ONCE */ /* comment out to include each bond in both neighboring atoms adjacency lists */ - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "e_mode.h" - -/* uncomment to test /InChI2InChI option */ -#define INCHI_TO_INCHI - - -#ifndef CREATE_INCHI_STEP_BY_STEP - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Use old (classic) library interface (see main() below) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -#include "inchi_api.h" -#include "e_ichi_io.h" - -#include "e_inchi_atom.h" -#include "e_ctl_data.h" -#include "e_ichi_parms.h" -#include "e_util.h" -#include "e_ichierr.h" -#include "e_readstru.h" -#include "e_ichicomp.h" -#ifdef CREATE_0D_PARITIES -#include "e_0dstereo.h" -#endif - - - -int e_MakeOutputHeader( char *pSdfLabel, char *pSdfValue, long lSdfId, long num_inp, char *pStr1, char *pStr2 ); -char *e_GetChiralFlagString( int bChiralFlagOn ); -static int e_bEnableCmdlineOption( char *szCmdLine, const char *szOption, int bEnable ); - -/* console-specific */ -/***************************************************************** - * - * Ctrl+C trap; works under Win32 + MS VC++ - * - *****************************************************************/ -#ifndef BUILD_LIB_FOR_WINCHI - -int e_bInterrupted = 0; -#if( defined( _WIN32 ) && defined( _CONSOLE ) ) - -#if( defined(_MSC_VER) && _MSC_VER >= 800 && defined(ADD_WIN_CTLC) && !(defined(__STDC__) && __STDC__ == 1) ) -BOOL WINAPI MyHandlerRoutine( - DWORD dwCtrlType /* control signal type */ - ) { - if ( dwCtrlType == CTRL_C_EVENT || - dwCtrlType == CTRL_BREAK_EVENT || - dwCtrlType == CTRL_CLOSE_EVENT || - dwCtrlType == CTRL_LOGOFF_EVENT ) { - e_bInterrupted = 1; - return TRUE; - } - return FALSE; -} -#endif -int e_WasInterrupted(void) { -#ifdef _DEBUG - if ( e_bInterrupted ) { - int stop=1; /* for debug only */ - } -#endif - return e_bInterrupted; -} - -#endif - -#define EXIT_ON_ERR -#define REPEAT_ALL 0 -/************** case insensitive strstr() ****************************/ -static char *e_stristr ( const char * string1, const char * string2 ) -{ - const char *str_target = string1; - const char *cur_string1_ptr, *cur_string2_ptr; - if ( !*string2 ) { - return (char *)string1; - } - while ( *str_target ) { - for ( cur_string1_ptr = str_target, cur_string2_ptr = string2; - *cur_string1_ptr && *cur_string2_ptr && - toupper(UCINT *cur_string1_ptr) == toupper(UCINT *cur_string2_ptr); - cur_string1_ptr++, cur_string2_ptr++ ) - ; - if ( !*cur_string2_ptr ) { - return (char *)str_target; - } - str_target++; - } - return NULL; -} -/********************************************************************/ -static int e_bEnableCmdlineOption( char *szCmdLine, const char *szOption, int bEnable ) -{ - int len = strlen(szOption)+1, num = 0; - char *pOpt = (char *)e_inchi_malloc(len+1); - char *p = szCmdLine, *q, *r; - if ( !pOpt ) - return -1; - strcpy( pOpt+1, szOption ); - pOpt[0] = INCHI_OPTION_PREFX; - while ( q = e_stristr(p, pOpt) ) { - r = q + len; - if ( bEnable > 0 && r[0] == '$' ) { - memmove( r, r+1, strlen(r+1)+1 ); - num ++; - } else - if ( 0 == bEnable && (!r[0] || r[0] == ' ' || r[0] == '\t') || - -1 == bEnable && (!r[0] || r[0] == ' ' || r[0] == '\t' || r[0] == ':' ) ) { - memmove( r+1, r, strlen(r)+1 ); - r[0] = '$'; - num ++; - } - p = q+1; - } - e_inchi_free( pOpt ); - return num; -} -/********************************************************************/ -int main( int argc, char *argv[ ] ) -{ - - STRUCT_DATA struct_data; - STRUCT_DATA *sd = &struct_data; - - long num_inp, num_err, num_output; - char szSdfDataValue[MAX_SDF_VALUE+1]; - const char *p1, *p2; - - unsigned long ulDisplTime = 0; /* infinite, milliseconds */ - unsigned long ulTotalProcessingTime = 0; - INPUT_PARMS inp_parms; - INPUT_PARMS *ip = &inp_parms; - char szInchiCmdLine[512]; - - inchi_Input inchi_inp, *pInp = &inchi_inp; - inchi_Output inchi_out, *pOut = &inchi_out; - - - int bReleaseVersion = bRELEASE_VERSION; -#define nStrLen 256 - char pStrInchiId[nStrLen], pStrLogId[nStrLen]; - int nRet = 0, nRet1, i, k, tot_len; - int inp_index, out_index; - long lSdfId; - int nStructNo; -#if ( defined(INCHI_TO_STRUCTURE) || defined(INCHI_TO_INCHI) ) - INPUT_TYPE nActualInputType = INPUT_NONE; - int bActualReadInChIOptions=0; -#endif - -#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) - int num_repeat = REPEAT_ALL; -#endif - -/*^^^ New: using InChIKey API */ -char ik_string[256]; /*^^^ Resulting InChIkey string */ -int ik_ret=0; /*^^^ InChIKey calculation result code */ -int xhash1, xhash2; -char szXtra1[256], szXtra2[256]; -/*^^^ */ - -/*^^^ Post-1.02b - moved here from below */ -int bTabbed=0; - -/*^^^ Post-1.02b */ -INCHI_IOSTREAM outputstr, logstr, prbstr, instr; -INCHI_IOSTREAM *out_stream=&outputstr, *log_stream=&logstr, *prb_stream=&prbstr, *inp_stream=&instr; - - - -#if( TRACE_MEMORY_LEAKS == 1 ) - _CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF); -/* for execution outside of the VC++ debugger uncomment one of the following two */ -/*#define MY_REPORT_FILE _CRTDBG_FILE_STDERR */ -/*#define MY_REPORT_FILE _CRTDBG_FILE_STDOUT */ -#ifdef MY_REPORT_FILE - _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_WARN, MY_REPORT_FILE ); - _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_ERROR, MY_REPORT_FILE ); - _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_ASSERT, MY_REPORT_FILE ); -#else - _CrtSetReportMode(_CRT_WARN | _CRT_ERROR, _CRTDBG_MODE_DEBUG); -#endif - /* turn on floating point exceptions */ -#if ( !defined(__STDC__) || __STDC__ != 1 ) - { - /* Get the default control word. */ - int cw = _controlfp( 0,0 ); - - /* Set the exception masks OFF, turn exceptions on. */ - /*cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_INEXACT|EM_ZERODIVIDE|EM_DENORMAL);*/ - cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE|EM_DENORMAL); - - /* Set the control word. */ - _controlfp( cw, MCW_EM ); - - } -#endif -#endif - -/***************************************************************** - * - * Set Ctrl+C trap; works under Win32 + MS VC++ - * - *****************************************************************/ -#if( defined( _WIN32 ) && defined( _CONSOLE ) && defined(_MSC_VER) && _MSC_VER >= 800 && defined(ADD_WIN_CTLC) && !(defined(__STDC__) && __STDC__ == 1)) - if ( SetConsoleCtrlHandler( MyHandlerRoutine, 1 ) ) { - ; /*ConsoleQuit = WasInterrupted*/; - } -#endif - - - -#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) -repeat: - - inchi_ios_close(inp_stream); - inchi_ios_close(log_stream); - inchi_ios_close(out_stream); - inchi_ios_close(prb_stream); - - -#endif - - /*^^^ Initialize I/O streams as files */ - inchi_ios_init(log_stream, INCHI_IOSTREAM_FILE, stderr); - inchi_ios_init(inp_stream, INCHI_IOSTREAM_FILE, NULL); - inchi_ios_init(out_stream, INCHI_IOSTREAM_FILE, NULL); - inchi_ios_init(prb_stream, INCHI_IOSTREAM_FILE, NULL); - - num_inp = 0; - num_err = 0; - num_output = 0; - - if ( argc < 2 || argc==2 && ( argv[1][0]==INCHI_OPTION_PREFX ) && - (!strcmp(argv[1]+1, "?") || !stricmp(argv[1]+1, "help") ) ) { - e_HelpCommandLineParms(log_stream); - return 0; - } - /* original input structure */ - memset( pInp, 0, sizeof(*pInp) ); - memset( pOut, 0, sizeof(*pOut) ); - - memset( szSdfDataValue , 0, sizeof( szSdfDataValue ) ); - - /* explicitly cast to (const char **) to avoid a warning about "incompatible pointer type":*/ - if ( 0 > e_ReadCommandLineParms( argc, (const char **)argv, ip, szSdfDataValue, &ulDisplTime, bReleaseVersion, log_stream) ) { - goto exit_function; - } - if ( !e_OpenFiles( &(inp_stream->f), &(out_stream->f), &(log_stream->f), &(prb_stream->f), ip ) ) { - goto exit_function; - } - if ( ip->bNoStructLabels ) { - ip->pSdfLabel = NULL; - ip->pSdfValue = NULL; - } else - if ( ip->nInputType == INPUT_INCHI_PLAIN || - ip->nInputType == INPUT_INCHI_XML || - ip->nInputType == INPUT_CMLFILE ) { - /* the input may contain both the header and the label of the structure */ - if ( !ip->pSdfLabel ) - ip->pSdfLabel = ip->szSdfDataHeader; - if ( !ip->pSdfValue ) - ip->pSdfValue = szSdfDataValue; - } - e_PrintInputParms( log_stream, ip ); - pStrInchiId[0] = '\0'; - /******************************************************************/ - /* Main cycle */ - /* read input structures and create their InChI */ - ulTotalProcessingTime = 0; - - out_index = 0; - /****************************** main cycle *************************/ - while ( !e_bInterrupted ) { - int bHasTimeout = 0; - if ( ip->last_struct_number && num_inp >= ip->last_struct_number ) { - nRet = _IS_EOF; /* simulate end of file */ - goto exit_function; - } - /* create command line */ - szInchiCmdLine[0] = '\0'; - for ( i = 1; i < argc; i ++ ) { - if ( argv[i] && INCHI_OPTION_PREFX == argv[i][0] && argv[i][1] ) { - /* omit certain options */ - if ( !memicmp( argv[i]+1, "start:", 6) || - !memicmp( argv[i]+1, "end:", 4) || - !stricmp( argv[i]+1, "Tabbed" ) - ) { - continue; - } - if ( !stricmp( argv[i]+1, "Inchi2Inchi" ) ) { -#ifdef INCHI_TO_INCHI - nActualInputType = INPUT_INCHI; - bActualReadInChIOptions = READ_INCHI_OUTPUT_INCHI; -#endif - continue; - } - if ( !stricmp( argv[i]+1, "Inchi2Struct" ) ) { -#ifdef INCHI_TO_STRUCTURE - nActualInputType = INPUT_INCHI; - bActualReadInChIOptions = READ_INCHI_TO_STRUCTURE; -#endif - continue; - } - if ( !memicmp( argv[i]+1, "w", 1 ) && isdigit( UCINT argv[i][2] ) ) { - bHasTimeout = 1; - } - /* add option to szInchiCmdLine */ - if ( strlen(szInchiCmdLine)+strlen(argv[i]) + 4 < sizeof(szInchiCmdLine) ) { - if ( szInchiCmdLine[0] ) - strcat( szInchiCmdLine, " " ); - k = ( !(k=strcspn( argv[i], " \t" )) || argv[i][k] ); /* k means enclose in "" */ - if ( k ) - strcat( szInchiCmdLine, "\"" ); - strcat( szInchiCmdLine, argv[i] ); - if ( k ) - strcat( szInchiCmdLine, "\"" ); - } else { - inchi_fprintf( stderr, "Too many options. Option \"%s\" ignored\n", argv[i] ); - } - } - } -#if ( defined(INCHI_TO_STRUCTURE) || defined(INCHI_TO_INCHI) ) - if ( nActualInputType == INPUT_INCHI ) { - /* disable other input types */ - e_bEnableCmdlineOption( szInchiCmdLine, "sdf", -1 ); - e_bEnableCmdlineOption( szInchiCmdLine, "mol", 0 ); - e_bEnableCmdlineOption( szInchiCmdLine, "cml", 0 ); - } -#endif - if ( !bHasTimeout ) { - /* add default timeout option -W60: 60 seconds */ - char szW60[] = " ?W60"; - szW60[1] = INCHI_OPTION_PREFX; - if ( strlen(szInchiCmdLine) + strlen( szW60 ) < sizeof(szInchiCmdLine) ) { - strcat( szInchiCmdLine, szW60 ); - } else { - inchi_fprintf( stderr, "Too many options. Option \"%s\" ignored\n", szW60 ); - } - } - /************************* skip input cycle **************************/ - while(!e_bInterrupted) { - inp_index = out_index; - /* read one structure from input */ - e_FreeInchi_Input( pInp ); - if ( num_inp > ip->first_struct_number ) { - inchi_fprintf( stderr, "\rStructure: %d.................\r", num_inp+1 ); - } -#if ( defined(INCHI_TO_STRUCTURE) || defined(INCHI_TO_INCHI) ) - if ( nActualInputType == INPUT_INCHI ) { - /* make e_ReadStructure() read the structures */ - ip->nInputType = INPUT_SDFILE; - } -#endif - nRet = e_ReadStructure( sd, ip, inp_stream, log_stream, out_stream, prb_stream, pInp, num_inp+1, - /* for CML:*/ inp_index, &out_index ); -#if ( defined(INCHI_TO_STRUCTURE) || defined(INCHI_TO_INCHI) ) - if ( nActualInputType != INPUT_NONE ) { - ip->nInputType = nActualInputType; - } -#endif - if ( _IS_SKIP != nRet ) { - lSdfId = ( ip->bGetSdfileId )? ip->lSdfId : 0; /* if requested then CAS r.n. otherwise struct. number*/ - nStructNo = ( ip->lMolfileNumber > 0 )? ip->lMolfileNumber : num_inp+1; - e_MakeOutputHeader( ip->pSdfLabel, ip->pSdfValue, lSdfId, nStructNo, pStrInchiId, pStrLogId ); - /* - if ( sd->pStrErrStruct && sd->pStrErrStruct[0] ) { - p1 = "; "; - p2 = sd->pStrErrStruct; - } else { - p1 = p2 = ""; - } - */ - } - /* e_ReadStructure() outputs the error/warning messages, so we do not need to re-output them here */ - switch ( nRet ) { - case _IS_FATAL: - num_inp ++; - num_err ++; - goto exit_function; - case _IS_EOF: - inchi_ios_eprint( log_stream, "\rStructure %d could not be read: Detected end of file. \r", num_inp+1 ); - goto exit_function; - case _IS_ERROR: - num_inp ++; - num_err ++; - continue; - case _IS_SKIP: - num_inp ++; - continue; - } - break; - } - if ( e_bInterrupted ) { - inchi_ios_eprint( log_stream, "\nStructure %d could not be read: User Quit.\n", num_inp+1 ); - num_err ++; - goto exit_function; - } - if ( nRet != _IS_WARNING ) { - inchi_fprintf( stderr, "\r%s \r", pStrLogId ); - } - /* chiral flag */ - /* ***************************************************************************** - * Chiral flags are set in: - * - RunICHI.c #1610 -- ReadTheStructure() -- cInChI, wInChI - * - e_IchiMain.c #273 -- main() -- C example of calling InChI dll (here) - * - inchi_dll.c #1662 -- ExtractOneStructure -- InChI dll code - *******************************************************************************/ - - p1 = NULL; - -#ifndef USE_STDINCHI_API - if ( (ip->nMode & REQ_MODE_CHIR_FLG_STEREO) && (ip->nMode & REQ_MODE_STEREO) && - ( ip->bChiralFlag & (FLAG_SET_INP_AT_CHIRAL | FLAG_SET_INP_AT_NONCHIRAL) ) ) { - ; /* cmd line has priority over the chiral flag in Molfile */ - } else - if ( sd->bChiralFlag & FLAG_INP_AT_CHIRAL ) { - p1 = e_GetChiralFlagString( 1 ); /* input file has chiral flag */ - } else - if ( (ip->nMode & REQ_MODE_CHIR_FLG_STEREO) && (ip->nMode & REQ_MODE_STEREO) || - (sd->bChiralFlag & FLAG_INP_AT_NONCHIRAL) ) { /* fix 04/05/2005 D.T.*/ - /* chiral flag requested (/SUCF) or input has non-chiral flag */ - p1 = e_GetChiralFlagString( 0 ); - } -#endif - if ( p1 ) { - if ( strlen(szInchiCmdLine) + strlen( p1 ) < sizeof(szInchiCmdLine) ) { - strcat( szInchiCmdLine, p1 ); - } else { - inchi_fprintf( stderr, "Too many options. Option \"%s\" ignored\n", p1 ); - } - } - /* create INChI for each connected component of the structure and optionally display them */ - /* output INChI for the whole structure */ -#ifndef USE_STDINCHI_API - FreeINCHI ( pOut ); -#else - FreeStdINCHI ( pOut ); -#endif - pInp->szOptions = szInchiCmdLine; -#ifdef CREATE_0D_PARITIES - if ( !pInp->stereo0D && !pInp->num_stereo0D ) { - int bPointedEdgeStereo = (0 != (TG_FLAG_POINTED_EDGE_STEREO & ip->bTautFlags)); - set_0D_stereo_parities( pInp, bPointedEdgeStereo ); - Clear3D2Dstereo(pInp); - } -#endif -#ifdef NEIGH_ONLY_ONCE - e_RemoveRedundantNeighbors( pInp ); -#endif - /******************************************** - * - * CREATE INCHI - * - ********************************************/ - -#ifndef USE_STDINCHI_API - nRet1 = GetINCHI( pInp, pOut ); -#else - nRet1 = GetStdINCHI( pInp, pOut ); -#endif - - ulTotalProcessingTime += sd->ulStructTime; - nRet = nRet1; -#ifdef INCHI_TO_INCHI - /* do not include -Inch2Inchi and =Inci2Struct options in command line options */ - if ( nActualInputType == INPUT_INCHI && (bActualReadInChIOptions & READ_INCHI_OUTPUT_INCHI) && - (nRet1 == inchi_Ret_OKAY || nRet1 == inchi_Ret_WARNING) ) { - inchi_InputINCHI inpInchi; - inchi_Output inchi_out2; - int nRet3; - inpInchi.szInChI = pOut->szInChI; - inpInchi.szOptions = pInp->szOptions; -/* e_bEnableCmdlineOption( szInchiCmdLine, "Inchi2Inchi", 1 );*/ - nRet3 = GetINCHIfromINCHI( &inpInchi, &inchi_out2 ); -/* e_bEnableCmdlineOption( szInchiCmdLine, "Inchi2Inchi", 0 );*/ - if (nRet3 == inchi_Ret_OKAY || nRet3 == inchi_Ret_WARNING) { -#ifndef USE_STDINCHI_API - FreeINCHI ( pOut ); -#else - FreeStdINCHI ( pOut ); -#endif - *pOut = inchi_out2; - nRet = nRet3; - } else { - /* same output in case of error */ -#ifndef USE_STDINCHI_API - FreeINCHI ( pOut ); -#else - FreeStdINCHI ( pOut ); -#endif - *pOut = inchi_out2; - nRet = nRet3; - } - } -#endif -#ifdef INCHI_TO_STRUCTURE - /* How to convert InChI back to structure */ - if (nActualInputType == INPUT_INCHI && (bActualReadInChIOptions & READ_INCHI_TO_STRUCTURE) && - (nRet1 == inchi_Ret_OKAY || nRet1 == inchi_Ret_WARNING) ) { - - inchi_InputINCHI inpInchi; - inchi_OutputStruct outStructure; - inchi_Input inchi_inp2; - inchi_Output inchi_out2; - int nRet3, nRet4; - - memset( &inpInchi, 0, sizeof(inpInchi) ); - memset( &outStructure, 0, sizeof(outStructure) ); - memset( &inchi_inp2, 0, sizeof(inchi_inp2) ); - memset( &inchi_out2, 0, sizeof(inchi_out2) ); - - - /* InChI -> Structure */ - inpInchi.szInChI = pOut->szInChI; /* use the original InChI as input */ - inpInchi.szOptions = szInchiCmdLine; - -/* e_bEnableCmdlineOption( szInchiCmdLine, "Inchi2Struct", 1 );*/ - nRet3 = GetStructFromINCHI( &inpInchi, &outStructure ); -/* e_bEnableCmdlineOption( szInchiCmdLine, "Inchi2Struct", 0 );*/ - - FreeINCHI( pOut ); /* we do not need the original InChI anymore */ - - if ( nRet3 == inchi_Ret_OKAY || nRet3 == inchi_Ret_WARNING ) { - /* convert structure back to InChI */ - inpInchi.szInChI = NULL; /* do not keep invalid pointers */ - /* prepare structure input: make input structure out of the reconstructed one */ - inchi_inp2.atom = outStructure.atom; - inchi_inp2.num_atoms = outStructure.num_atoms; - inchi_inp2.stereo0D = outStructure.stereo0D; - inchi_inp2.num_stereo0D = outStructure.num_stereo0D; - inchi_inp2.szOptions = szInchiCmdLine; - /* create InChI out of the reconstructed structure */ - nRet4 = GetINCHI( &inchi_inp2, &inchi_out2 ); - if ( nRet4 == inchi_Ret_OKAY || nRet4 == inchi_Ret_WARNING ) { - /* InChI of the reconstructed structure has been created */ - /* get rid of Struct->InChI message in inchi_out2 */ - /* and replace it with warning message from GetStructFromINCHI() */ - if ( inchi_out2.szMessage && inchi_out2.szMessage[0] ) { - inchi_out2.szMessage[0] = '\0'; - } - if ( nRet3 == inchi_Ret_WARNING ) { - /* prepare InChI->Structure warning for displaying: */ - /* exchange outStructure message pointer with that of inchi_out2 */ - char *p = inchi_out2.szMessage; - inchi_out2.szMessage = outStructure.szMessage; - outStructure.szMessage = p; - } - /* copy to pOut to use same output source code */ - /* as for Structure->InChI conversion output */ - *pOut = inchi_out2; - nRet1 = nRet4; /* InChI->Struct->InChI return value */ - nRet = nRet3; /* InChI->Struct return value */ - memset( &inchi_out2, 0, sizeof( inchi_out2) ); /* do not keep duplicated pointers */ - } else { - /* the last step in Structure->InChI->Structure->InChI failed */ - *pOut = inchi_out2; /* save the error output */ - nRet = nRet4; /* Reconstructed Struct->InChI error code */ - memset( &inchi_out2, 0, sizeof( inchi_out2) ); /* do not keep duplicated pointers */ - } - memset( &inchi_inp2, 0, sizeof(inchi_inp2) ); /* do not keep invalid pointers */ - } else { - /* error: Could not create Structure */ - inchi_ios_eprint( log_stream, "GetStructFromINCHI failed (%s)(%-d) %s\n", outStructure.szMessage? outStructure.szMessage:"???", nRet3, pStrLogId ); - } - FreeStructFromINCHI( &outStructure ); - } -#endif - -#ifdef MAKE_INCHI_FROM_AUXINFO - /************************************************** - * - * CREATE one more INCHI FROM AuxInfo and compare - * - * it to to the first INCHI - * - * Note: This should double the elapsed CPU time - * - **************************************************/ - if ( nRet1 == inchi_Ret_OKAY || nRet1 == inchi_Ret_WARNING ) { - char *szInchiAuxInfo = pOut->szAuxInfo, *p1 /* shadowing previous definition */; - int bDoNotAddH = ip->bDoNotAddH; - int nRet2; - inchi_Input inchi_inp2, *pInp2 = &inchi_inp2; - inchi_Output inchi_out2, *pOut2 = &inchi_out2; - InchiInpData idat; - /* setup input for Get_inchi_Input_FromAuxInfo */ - idat.pInp = pInp2; - pInp2->szOptions = NULL; /* not needed */ - - /* Make InChI input out of AuxInfo */ - nRet2 = Get_inchi_Input_FromAuxInfo( szInchiAuxInfo, bDoNotAddH, &idat ); - - if ( inchi_Ret_OKAY == nRet2 || inchi_Ret_WARNING == nRet2 ) { - /* set chiral flag */ - p1 = NULL; - if ( (ip->nMode & REQ_MODE_CHIR_FLG_STEREO) && (ip->nMode & REQ_MODE_STEREO) && - ( ip->bChiralFlag & (FLAG_SET_INP_AT_CHIRAL | FLAG_SET_INP_AT_NONCHIRAL) ) ) { - ; /* cmd line has priority over the chiral flag in Molfile */ - } else - if ( idat.bChiral & FLAG_INP_AT_CHIRAL ) { /* fix 04/05/2005 D.T.*/ - p1 = e_GetChiralFlagString( 1 ); /* input file has chiral flag */ - } else - if ( (ip->nMode & REQ_MODE_CHIR_FLG_STEREO) && (ip->nMode & REQ_MODE_STEREO) || - (idat.bChiral & FLAG_INP_AT_NONCHIRAL) ) { /* fix 04/05/2005 D.T.*/ - /* chiral flag requested (/SUCF) or input has non-chiral flag */ - p1 = e_GetChiralFlagString( 0 ); - } - if ( p1 ) { - if ( strlen(szInchiCmdLine) + strlen( p1 ) < sizeof(szInchiCmdLine) ) { - strcat( szInchiCmdLine, p1 ); - } else { - inchi_fprintf( stderr, "Too many options. Option \"%s\" ignored\n", p1 ); - } - } - - /* Make InChI out of InChI input out of AuxInfo */ - pInp2->szOptions = pInp->szOptions; - /* pInp2->szOptions =" /OutputSDF"; */ - memset( pOut2, 0, sizeof(*pOut2) ); - nRet2 = GetINCHI( pInp2, pOut2 ); - /* Compare the two InChI */ - if ( nRet2 == inchi_Ret_OKAY || nRet2 == inchi_Ret_WARNING ) { - if ( !pOut2->szInChI || strcmp( pOut->szInChI, pOut2->szInChI ) ) { - inchi_fprintf( stderr, "InChI from AuxInfo Is Different!\n" ); - } - } else { - inchi_fprintf( stderr, "InChI from AuxInfo could not be produced: \"%s\"\n", pOut2->szMessage ); - } - FreeINCHI( pOut2 ); - } - Free_inchi_Input( pInp2 ); - } -#endif - - /*****************************/ - /* output err/warn */ - /*****************************/ - if ( pOut->szMessage && pOut->szMessage[0] ) { - p1 = "; "; - p2 = pOut->szMessage; - } else { - p1 = p2 = ""; - } - switch ( nRet ) { - case inchi_Ret_UNKNOWN: - case inchi_Ret_FATAL: /* fatal processing error -- typically, memory allocation error */ - num_inp ++; - num_err ++; -#if( defined(EXIT_ON_ERR) && defined(REPEAT_ALL) && REPEAT_ALL > 0 ) - num_repeat = 0; -#endif - inchi_ios_eprint( log_stream, "Fatal Error (No INChI%s%s) %s\n", p1, p2, pStrLogId ); - inchi_ios_eprint( log_stream, "Log start:---------------------\n%s\nLog end--------------------\n", pOut->szLog? pOut->szLog : "Log is missing" ); - goto exit_function; - case inchi_Ret_EOF: /* typically, no input structure provided or help requested */ - /* output previous structure number and message */ - inchi_ios_eprint( log_stream, "End of file detected after structure %d\n", num_inp ); - goto exit_function; - case inchi_Ret_ERROR: - num_inp ++; - num_err ++; - inchi_ios_eprint( log_stream, "Error (No INChI%s%s) %s\n", p1, p2, pStrLogId ); -#if( defined(EXIT_ON_ERR) && defined(REPEAT_ALL) && REPEAT_ALL > 0 ) - num_repeat = 0; - goto exit_function; -#endif - continue; - case inchi_Ret_SKIP: - num_inp ++; - inchi_ios_eprint( log_stream, "Skipped %s\n", pStrLogId ); - goto exit_function; - case inchi_Ret_OKAY: - break; - case inchi_Ret_WARNING: - if ( p2 && p2[0] ) { - inchi_ios_eprint( log_stream, "Warning (%s) %s\n", p2, pStrLogId ); - } - break; /* ok */ - } - - num_inp ++; - tot_len = 0; - - /*^^^ Post-1.02b - moved from below */ - bTabbed = 0 != ( ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT ); - - if ( pOut->szInChI && pOut->szInChI[0] ) { - if (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY ) { - /*****************************/ - /* output SDfile */ - /*****************************/ - char *start; - unsigned len; - int bAddLabel = 0; - /*******************************************************************************/ - /* output a SDfile. pOut->szInChI contains Molfile ending with "$$$$\n" line. */ - /* Replace the 1st line with the structure number */ - /* Replace the last line with the SDfile header, label, and new "$$$$\n" line */ - /*******************************************************************************/ - /* 1. remove the 1st line (later replace it with the actual structure number) */ - if ( start = strchr( pOut->szInChI, '\n' ) ) { - inchi_ios_print( out_stream, "Structure #%ld", nStructNo ); - } else { - start = pOut->szInChI; - } - /* 2. SDfile header and data: write zero to the 1st byte of - * the last line "$$$$\n" to remove this line with purpose to relpace it */ - if ( ip->pSdfLabel && ip->pSdfLabel[0] && ip->pSdfValue && ip->pSdfValue[0] && - (len = strlen(start)) && len > 5 && '$' == start[len-5] && '\n' == start[len-6] ) { - start[len-5] = '\0'; - bAddLabel = 1; - } - /* 3. Output the whole Molfile */ - inchi_ios_print( out_stream, "%s", start ); - if ( bAddLabel ) { - inchi_ios_print( out_stream, "> <%s>\n%s\n\n$$$$\n", ip->pSdfLabel, ip->pSdfValue ); - } - - } else { - /*****************************/ - /* output InChI */ - /*****************************/ - - int bAuxInfo = !( ip->bINChIOutputOptions & INCHI_OUT_ONLY_AUX_INFO ) && - pOut->szAuxInfo && pOut->szAuxInfo[0]; - /*^^^ Post-1.02b - correctly treat tabbed output with InChIKey */ - int bAuxOrKey = bAuxInfo || ( ip->bCalcInChIHash != INCHIHASH_NONE ); - - const char *pLF = "\n"; - const char *pTAB = bTabbed? "\t" : pLF; - if ( !ip->bNoStructLabels ) { - /* or print a previously created label string */ - inchi_ios_print( out_stream, "%s%s", pStrInchiId, pTAB ); - } - /* output INChI Identifier */ - /*^^^ Post-1.02b */ - /* inchi_ios_print( out_stream, "%s%s", pOut->szInChI, bAuxInfo? pTAB : pLF );*/ - inchi_ios_print( out_stream, "%s%s", pOut->szInChI, bAuxOrKey? pTAB : pLF ); - /* output INChI Aux Info */ - if ( bAuxInfo ) { - inchi_ios_print(out_stream, "%s%s",pOut->szAuxInfo, ip->bCalcInChIHash? pTAB : pLF); - } - - /*^^^ Calculate InChIKey */ - if ( ip->bCalcInChIHash != INCHIHASH_NONE ) - { - xhash1 = xhash2 = 0; - if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1 ) || - ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) - xhash1 = 1; - if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA2 ) || - ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) - xhash2 = 1; - - ik_ret = GetINCHIKeyFromINCHI(pOut->szInChI, xhash1, xhash2, - ik_string, szXtra1, szXtra2); - if (ik_ret==INCHIKEY_OK) - { - /* NB: correctly treat tabbed output with InChIKey & hash extensions */ - char csep = '\n'; - if ( ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT ) - csep = '\t'; - inchi_ios_print(out_stream, "InChIKey=%-s",ik_string); - if ( xhash1 ) - inchi_ios_print(out_stream, "%cXHash1=%-s",csep,szXtra1); - if ( xhash2 ) - inchi_ios_print(out_stream, "%cXHash2=%-s",csep,szXtra2); - inchi_ios_print(out_stream, "\n"); - } - else - { - /*^^^ Post-1.02b - add LF if output is tabbed and key generation failed */ - if (bTabbed) - inchi_ios_print(out_stream, "\n"); - - inchi_ios_eprint(log_stream, "Warning: could not compute InChIKey for #%-d ", - num_inp ); - switch(ik_ret) - { - case INCHIKEY_UNKNOWN_ERROR : - inchi_ios_eprint(log_stream, "(invalid key length requested)\n"); - break; - case INCHIKEY_EMPTY_INPUT: - inchi_ios_eprint(log_stream, "(got an empty string)\n"); - break; - case INCHIKEY_INVALID_INCHI_PREFIX: - case INCHIKEY_INVALID_INCHI: - case INCHIKEY_INVALID_STD_INCHI: - inchi_ios_eprint(log_stream, "(got non-InChI string)\n"); - break; - case INCHIKEY_NOT_ENOUGH_MEMORY: - inchi_ios_eprint(log_stream, "(not enough memory to treat the string)\n"); - break; - default:inchi_ios_eprint(log_stream, "(internal program error)\n"); - break; - } - } - - } - - - } - } - } - if ( e_bInterrupted ) { - inchi_ios_eprint( log_stream, "\nStructure %d could not be processed: User Quit.\n", num_inp+1 ); - num_err ++; - goto exit_function; - } - -exit_function: - inchi_ios_eprint( log_stream, "\nProcessed %ld structure%s, %ld error%s.\n", - num_inp, (num_inp==1)?"":"s", num_err, (num_err==1)?"":"s" ); - - - e_FreeInchi_Input( pInp ); -#ifndef USE_STDINCHI_API - FreeINCHI ( pOut ); -#else - FreeStdINCHI ( pOut ); -#endif - -#if( ADD_CMLPP == 1 ) - /* BILLY 8/6/04 */ - /* free CML memory */ - FreeCml (); - FreeCmlDoc( 1 ); -#endif - - - inchi_ios_close(inp_stream); - inchi_ios_close(log_stream); - inchi_ios_close(out_stream); - inchi_ios_close(prb_stream); - - - for ( i = 0; i < MAX_NUM_PATHS; i ++ ) { - if ( ip->path[i] ) { - e_inchi_free( (char*) ip->path[i] ); /* cast deliberately discards 'const' qualifier */ - ip->path[i] = NULL; - } - } - -#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) - if ( --num_repeat > 0 ) { - goto repeat; - } -#endif - - - return 0; -} - - -#endif - -/**********************************************************/ -int e_MakeOutputHeader( char *pSdfLabel, char *pSdfValue, long lSdfId, long num_inp, char *pStr1, char *pStr2 ) -{ - int tot_len1 = 0, tot_len2 = 0; - pStr1[0] = '\0'; - if ( !(pSdfLabel && pSdfLabel[0]) && !(pSdfValue && pSdfValue[0]) ) { - tot_len1 = sprintf( pStr1, "Structure: %ld", num_inp ); - tot_len2 = sprintf( pStr2, "structure #%ld", num_inp ); - } else { - tot_len1 = sprintf( pStr1, "Structure: %ld.%s%s%s%s", - num_inp, - SDF_LBL_VAL(pSdfLabel, pSdfValue) ); - - tot_len2 = sprintf( pStr2, "structure #%ld.%s%s%s%s", - num_inp, - SDF_LBL_VAL(pSdfLabel, pSdfValue) ); - if ( lSdfId ) { - tot_len1 += sprintf( pStr1 + tot_len1, ":%ld", lSdfId ); - tot_len2 += sprintf( pStr2 + tot_len2, ":%ld", lSdfId ); - } - } - return tot_len1; -} -/************************************************************/ -char *e_GetChiralFlagString( int bChiralFlagOn ) -{ - static char szChiralFlag[64]; - szChiralFlag[0] = ' '; - szChiralFlag[1] = INCHI_OPTION_PREFX; - sprintf( szChiralFlag+2, "ChiralFlag%s", bChiralFlagOn? "On":"Off" ); - return szChiralFlag; -} - - -#endif /*^^^ CREATE_INCHI_STEP_BY_STEP */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#if( defined( WIN32 ) && defined( _CONSOLE ) && defined(_MSC_VER) && _MSC_VER >= 800 ) +#define ADD_WIN_CTLC /* detect Ctrl-C under Win-32 and Microsoft Visual C ++ */ +#endif + +#if( defined( WIN32 ) && defined( _CONSOLE ) && defined(_MSC_VER) && _MSC_VER >= 800 && defined(ADD_WIN_CTLC) && !(defined(__STDC__) && __STDC__ == 1) ) +#include +#endif + + +/* #define CREATE_0D_PARITIES */ /* uncomment to replace coordinates and 2D-parirties with 0D-parities */ +/* in case of CREATE_0D_PARITIES, the hardcoded bFixSp3Bug = 1 fixes sp3 bugs in original InChI v. 1.00 */ +/* in case of CREATE_0D_PARITIES, the Phosphine and Arsine sp3 stereo options are not supported */ + +/* #define MAKE_INCHI_FROM_AUXINFO */ /* uncomment to create the second InChI out of AuxInfo and compare */ +#define INCHI_TO_STRUCTURE /* uncomment to convert Struct->InChI->Struct->InChI in case of /Inchi2Struct option */ +/* #define NEIGH_ONLY_ONCE */ /* comment out to include each bond in both neighboring atoms adjacency lists */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "e_mode.h" + +/* uncomment to test /InChI2InChI option */ +#define INCHI_TO_INCHI + + +#ifndef CREATE_INCHI_STEP_BY_STEP + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Use old (classic) library interface (see main() below) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ + +#include "../../../../INCHI_BASE/src/inchi_api.h" + +#include "e_ichi_io.h" + +#include "e_inchi_atom.h" +#include "e_ctl_data.h" +#include "e_ichi_parms.h" +#include "e_util.h" +#include "e_ichierr.h" +#include "e_readstru.h" +#include "e_ichicomp.h" +#ifdef CREATE_0D_PARITIES +#include "e_0dstereo.h" +#endif + + + + +/* TESTIK */ +void dump_inchi_Input( inchi_Input *pinp, INCHI_IOSTREAM *pout ); + + +int e_MakeOutputHeader( char *pSdfLabel, char *pSdfValue, long lSdfId, long num_inp, char *pStr1, char *pStr2 ); +char *e_GetChiralFlagString( int bChiralFlagOn ); +static int e_bEnableCmdlineOption( char *szCmdLine, const char *szOption, int bEnable ); + +/* console-specific */ +/***************************************************************** + * + * Ctrl+C trap; works under Win32 + MS VC++ + * + *****************************************************************/ +#ifndef BUILD_LIB_FOR_WINCHI + +int e_bInterrupted = 0; +#if( defined( _WIN32 ) && defined( _CONSOLE ) ) + +#if( defined(_MSC_VER) && _MSC_VER >= 800 && defined(ADD_WIN_CTLC) && !(defined(__STDC__) && __STDC__ == 1) ) +BOOL WINAPI MyHandlerRoutine( + DWORD dwCtrlType /* control signal type */ + ) { + if ( dwCtrlType == CTRL_C_EVENT || + dwCtrlType == CTRL_BREAK_EVENT || + dwCtrlType == CTRL_CLOSE_EVENT || + dwCtrlType == CTRL_LOGOFF_EVENT ) { + e_bInterrupted = 1; + return TRUE; + } + return FALSE; +} +#endif +int e_WasInterrupted(void) { +#ifdef _DEBUG + if ( e_bInterrupted ) { + int stop=1; /* for debug only */ + } +#endif + return e_bInterrupted; +} + +#endif + +#define EXIT_ON_ERR +#define REPEAT_ALL 0 +/************** case insensitive strstr() ****************************/ +static char *e_stristr ( const char * string1, const char * string2 ) +{ + const char *str_target = string1; + const char *cur_string1_ptr, *cur_string2_ptr; + if ( !*string2 ) { + return (char *)string1; + } + while ( *str_target ) { + for ( cur_string1_ptr = str_target, cur_string2_ptr = string2; + *cur_string1_ptr && *cur_string2_ptr && + toupper(UCINT *cur_string1_ptr) == toupper(UCINT *cur_string2_ptr); + cur_string1_ptr++, cur_string2_ptr++ ) + ; + if ( !*cur_string2_ptr ) { + return (char *)str_target; + } + str_target++; + } + return NULL; +} +/********************************************************************/ +static int e_bEnableCmdlineOption( char *szCmdLine, const char *szOption, int bEnable ) +{ + int len = strlen(szOption)+1, num = 0; + char *pOpt = (char *)e_inchi_malloc(len+1); + char *p = szCmdLine, *q, *r; + if ( !pOpt ) + return -1; + strcpy( pOpt+1, szOption ); + pOpt[0] = INCHI_OPTION_PREFX; + while ( q = e_stristr(p, pOpt) ) { + r = q + len; + if ( bEnable > 0 && r[0] == '$' ) { + memmove( r, r+1, strlen(r+1)+1 ); + num ++; + } else + if ( 0 == bEnable && (!r[0] || r[0] == ' ' || r[0] == '\t') || + -1 == bEnable && (!r[0] || r[0] == ' ' || r[0] == '\t' || r[0] == ':' ) ) { + memmove( r+1, r, strlen(r)+1 ); + r[0] = '$'; + num ++; + } + p = q+1; + } + e_inchi_free( pOpt ); + return num; +} +/********************************************************************/ +int main( int argc, char *argv[ ] ) +{ + + STRUCT_DATA struct_data; + STRUCT_DATA *sd = &struct_data; + + long num_inp, num_err, num_output; + char szSdfDataValue[MAX_SDF_VALUE+1]; + const char *p1, *p2; + + unsigned long ulDisplTime = 0; /* infinite, milliseconds */ + unsigned long ulTotalProcessingTime = 0; + INPUT_PARMS inp_parms; + INPUT_PARMS *ip = &inp_parms; + char szInchiCmdLine[512]; + + inchi_Input inchi_inp, *pInp = &inchi_inp; + inchi_Output inchi_out, *pOut = &inchi_out; + + + int bReleaseVersion = bRELEASE_VERSION; +#define nStrLen 256 + char pStrInchiId[nStrLen], pStrLogId[nStrLen]; + int nRet = 0, nRet1, i, k, tot_len; + int inp_index, out_index; + long lSdfId; + int nStructNo; +#if ( defined(INCHI_TO_STRUCTURE) || defined(INCHI_TO_INCHI) ) + INPUT_TYPE nActualInputType = INPUT_NONE; + int bActualReadInChIOptions=0; +#endif + +#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) + int num_repeat = REPEAT_ALL; +#endif + +/*^^^ New: using InChIKey API */ +char ik_string[256]; /*^^^ Resulting InChIkey string */ +int ik_ret=0; /*^^^ InChIKey calculation result code */ +int xhash1, xhash2; +char szXtra1[256], szXtra2[256]; +/*^^^ */ + +/*^^^ Post-1.02b - moved here from below */ +int bTabbed=0; + +/*^^^ Post-1.02b */ +INCHI_IOSTREAM outputstr, logstr, prbstr, instr; +INCHI_IOSTREAM *out_stream=&outputstr, *log_stream=&logstr, *prb_stream=&prbstr, *inp_stream=&instr; + + + +#if( TRACE_MEMORY_LEAKS == 1 ) + _CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF); +/* for execution outside of the VC++ debugger uncomment one of the following two */ +/*#define MY_REPORT_FILE _CRTDBG_FILE_STDERR */ +/*#define MY_REPORT_FILE _CRTDBG_FILE_STDOUT */ +#ifdef MY_REPORT_FILE + _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_WARN, MY_REPORT_FILE ); + _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ERROR, MY_REPORT_FILE ); + _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ASSERT, MY_REPORT_FILE ); +#else + _CrtSetReportMode(_CRT_WARN | _CRT_ERROR, _CRTDBG_MODE_DEBUG); +#endif + /* turn on floating point exceptions */ +#if ( !defined(__STDC__) || __STDC__ != 1 ) + { + /* Get the default control word. */ + int cw = _controlfp( 0,0 ); + + /* Set the exception masks OFF, turn exceptions on. */ + /*cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_INEXACT|EM_ZERODIVIDE|EM_DENORMAL);*/ + cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE|EM_DENORMAL); + + /* Set the control word. */ + _controlfp( cw, MCW_EM ); + } +#endif +#endif + +/***************************************************************** + * + * Set Ctrl+C trap; works under Win32 + MS VC++ + * + *****************************************************************/ +#if( defined( _WIN32 ) && defined( _CONSOLE ) && defined(_MSC_VER) && _MSC_VER >= 800 && defined(ADD_WIN_CTLC) && !(defined(__STDC__) && __STDC__ == 1)) + if ( SetConsoleCtrlHandler( MyHandlerRoutine, 1 ) ) { + ; /*ConsoleQuit = WasInterrupted*/; + } +#endif + + + +#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) +repeat: + + inchi_ios_close(inp_stream); + inchi_ios_close(log_stream); + inchi_ios_close(out_stream); + inchi_ios_close(prb_stream); + + +#endif + + /*^^^ Initialize I/O streams as files */ + inchi_ios_init(log_stream, INCHI_IOSTREAM_TYPE_FILE, stderr); + inchi_ios_init(inp_stream, INCHI_IOSTREAM_TYPE_FILE, NULL); + inchi_ios_init(out_stream, INCHI_IOSTREAM_TYPE_FILE, NULL); + inchi_ios_init(prb_stream, INCHI_IOSTREAM_TYPE_FILE, NULL); + + num_inp = 0; + num_err = 0; + num_output = 0; + + if ( argc < 2 || argc==2 && ( argv[1][0]==INCHI_OPTION_PREFX ) && + (!strcmp(argv[1]+1, "?") || !e_inchi_stricmp(argv[1]+1, "help") ) ) { + e_HelpCommandLineParms(log_stream); + return 0; + } + /* original input structure */ + memset( pInp, 0, sizeof(*pInp) ); + memset( pOut, 0, sizeof(*pOut) ); + + memset( szSdfDataValue , 0, sizeof( szSdfDataValue ) ); + + /* explicitly cast to (const char **) to avoid a warning about "incompatible pointer type":*/ + if ( 0 > e_ReadCommandLineParms( argc, (const char **)argv, ip, szSdfDataValue, &ulDisplTime, bReleaseVersion, log_stream) ) { + goto exit_function; + } + if ( !e_OpenFiles( &(inp_stream->f), &(out_stream->f), &(log_stream->f), &(prb_stream->f), ip ) ) { + goto exit_function; + } + if ( ip->bNoStructLabels ) { + ip->pSdfLabel = NULL; + ip->pSdfValue = NULL; + } else + if ( ip->nInputType == INPUT_INCHI_PLAIN || + ip->nInputType == INPUT_INCHI_XML || + ip->nInputType == INPUT_CMLFILE ) { + /* the input may contain both the header and the label of the structure */ + if ( !ip->pSdfLabel ) + ip->pSdfLabel = ip->szSdfDataHeader; + if ( !ip->pSdfValue ) + ip->pSdfValue = szSdfDataValue; + } + e_PrintInputParms( log_stream, ip ); + pStrInchiId[0] = '\0'; + /******************************************************************/ + /* Main cycle */ + /* read input structures and create their InChI */ + ulTotalProcessingTime = 0; + + out_index = 0; + /****************************** main cycle *************************/ + while ( !e_bInterrupted ) { + int bHasTimeout = 0; + if ( ip->last_struct_number && num_inp >= ip->last_struct_number ) { + nRet = _IS_EOF; /* simulate end of file */ + goto exit_function; + } + /* create command line */ + szInchiCmdLine[0] = '\0'; + for ( i = 1; i < argc; i ++ ) { + if ( argv[i] && INCHI_OPTION_PREFX == argv[i][0] && argv[i][1] ) { + /* omit certain options */ + if ( !e_inchi_memicmp( argv[i]+1, "start:", 6) || + !e_inchi_memicmp( argv[i]+1, "end:", 4) || + !e_inchi_stricmp( argv[i]+1, "Tabbed" ) + ) { + continue; + } + if ( !e_inchi_stricmp( argv[i]+1, "Inchi2Inchi" ) ) { +#ifdef INCHI_TO_INCHI + nActualInputType = INPUT_INCHI; + bActualReadInChIOptions = READ_INCHI_OUTPUT_INCHI; +#endif + continue; + } + if ( !e_inchi_stricmp( argv[i]+1, "Inchi2Struct" ) ) { +#ifdef INCHI_TO_STRUCTURE + nActualInputType = INPUT_INCHI; + bActualReadInChIOptions = READ_INCHI_TO_STRUCTURE; +#endif + continue; + } + if ( !e_inchi_memicmp( argv[i]+1, "w", 1 ) && isdigit( UCINT argv[i][2] ) ) { + bHasTimeout = 1; + } + /* add option to szInchiCmdLine */ + if ( strlen(szInchiCmdLine)+strlen(argv[i]) + 4 < sizeof(szInchiCmdLine) ) { + if ( szInchiCmdLine[0] ) + strcat( szInchiCmdLine, " " ); + k = ( !(k=strcspn( argv[i], " \t" )) || argv[i][k] ); /* k means enclose in "" */ + if ( k ) + strcat( szInchiCmdLine, "\"" ); + strcat( szInchiCmdLine, argv[i] ); + if ( k ) + strcat( szInchiCmdLine, "\"" ); + } else { + inchi_fprintf( stderr, "Too many options. Option \"%s\" ignored\n", argv[i] ); + } + } + } +#if ( defined(INCHI_TO_STRUCTURE) || defined(INCHI_TO_INCHI) ) + if ( nActualInputType == INPUT_INCHI ) { + /* disable other input types */ + e_bEnableCmdlineOption( szInchiCmdLine, "sdf", -1 ); + e_bEnableCmdlineOption( szInchiCmdLine, "mol", 0 ); + e_bEnableCmdlineOption( szInchiCmdLine, "cml", 0 ); + } +#endif + if ( !bHasTimeout ) { + /* add default timeout option -W60: 60 seconds */ + char szW60[] = " ?W60"; + szW60[1] = INCHI_OPTION_PREFX; + if ( strlen(szInchiCmdLine) + strlen( szW60 ) < sizeof(szInchiCmdLine) ) { + strcat( szInchiCmdLine, szW60 ); + } else { + inchi_fprintf( stderr, "Too many options. Option \"%s\" ignored\n", szW60 ); + } + } + /************************* skip input cycle **************************/ + while(!e_bInterrupted) { + inp_index = out_index; + /* read one structure from input */ + e_FreeInchi_Input( pInp ); + if ( num_inp > ip->first_struct_number ) { + inchi_fprintf( stderr, "\rStructure: %d.................\r", num_inp+1 ); + } +#if ( defined(INCHI_TO_STRUCTURE) || defined(INCHI_TO_INCHI) ) + if ( nActualInputType == INPUT_INCHI ) { + /* make e_ReadStructure() read the structures */ + ip->nInputType = INPUT_SDFILE; + } +#endif + nRet = e_ReadStructure( sd, ip, inp_stream, log_stream, out_stream, prb_stream, pInp, num_inp+1, + /* for CML:*/ inp_index, &out_index ); +#if ( defined(INCHI_TO_STRUCTURE) || defined(INCHI_TO_INCHI) ) + if ( nActualInputType != INPUT_NONE ) { + ip->nInputType = nActualInputType; + } +#endif + if ( _IS_SKIP != nRet ) { + lSdfId = ( ip->bGetSdfileId )? ip->lSdfId : 0; /* if requested then CAS r.n. otherwise struct. number*/ + nStructNo = ( ip->lMolfileNumber > 0 )? ip->lMolfileNumber : num_inp+1; + e_MakeOutputHeader( ip->pSdfLabel, ip->pSdfValue, lSdfId, nStructNo, pStrInchiId, pStrLogId ); + /* + if ( sd->pStrErrStruct && sd->pStrErrStruct[0] ) { + p1 = "; "; + p2 = sd->pStrErrStruct; + } else { + p1 = p2 = ""; + } + */ + } + /* e_ReadStructure() outputs the error/warning messages, so we do not need to re-output them here */ + switch ( nRet ) { + case _IS_FATAL: + num_inp ++; + num_err ++; + goto exit_function; + case _IS_EOF: + inchi_ios_eprint( log_stream, "\rStructure %d could not be read: Detected end of file. \r", num_inp+1 ); + goto exit_function; + case _IS_ERROR: + num_inp ++; + num_err ++; + if ( ip->bINChIOutputOptions & INCHI_OUT_STDINCHI ) + inchi_ios_print( out_stream, "InChI=1S//\n"); + else + inchi_ios_print( out_stream, "InChI=1//\n"); + continue; + case _IS_SKIP: + num_inp ++; + continue; + } + break; + } + if ( e_bInterrupted ) { + inchi_ios_eprint( log_stream, "\nStructure %d could not be read: User Quit.\n", num_inp+1 ); + num_err ++; + goto exit_function; + } + if ( nRet != _IS_WARNING ) { + inchi_fprintf( stderr, "\r%s \r", pStrLogId ); + } + + /* chiral flag */ + p1 = NULL; + +#ifndef USE_STDINCHI_API + if ( (ip->nMode & REQ_MODE_CHIR_FLG_STEREO) && (ip->nMode & REQ_MODE_STEREO) && + ( ip->bChiralFlag & (FLAG_SET_INP_AT_CHIRAL | FLAG_SET_INP_AT_NONCHIRAL) ) ) { + ; /* cmd line has priority over the chiral flag in Molfile */ + } else + if ( sd->bChiralFlag & FLAG_INP_AT_CHIRAL ) { + p1 = e_GetChiralFlagString( 1 ); /* input file has chiral flag */ + } else + if ( (ip->nMode & REQ_MODE_CHIR_FLG_STEREO) && (ip->nMode & REQ_MODE_STEREO) || + (sd->bChiralFlag & FLAG_INP_AT_NONCHIRAL) ) { /* fix 04/05/2005 D.T.*/ + /* chiral flag requested (/SUCF) or input has non-chiral flag */ + p1 = e_GetChiralFlagString( 0 ); + } +#endif + if ( p1 ) { + if ( strlen(szInchiCmdLine) + strlen( p1 ) < sizeof(szInchiCmdLine) ) { + strcat( szInchiCmdLine, p1 ); + } else { + inchi_fprintf( stderr, "Too many options. Option \"%s\" ignored\n", p1 ); + } + } + /* create INChI for each connected component of the structure and optionally display them */ + /* output INChI for the whole structure */ +#ifndef USE_STDINCHI_API + FreeINCHI ( pOut ); +#else + FreeStdINCHI ( pOut ); +#endif + pInp->szOptions = szInchiCmdLine; +#ifdef CREATE_0D_PARITIES + if ( !pInp->stereo0D && !pInp->num_stereo0D ) { + int bPointedEdgeStereo = (0 != (TG_FLAG_POINTED_EDGE_STEREO & ip->bTautFlags)); + set_0D_stereo_parities( pInp, bPointedEdgeStereo ); + Clear3D2Dstereo(pInp); + } +#endif +#ifdef NEIGH_ONLY_ONCE + e_RemoveRedundantNeighbors( pInp ); +#endif + + + /******************************************** + * + * CREATE INCHI + * + ********************************************/ + + + nRet1 = GetINCHI( pInp, pOut ); + + + ulTotalProcessingTime += sd->ulStructTime; + nRet = nRet1; + + + +#ifdef INCHI_TO_INCHI + + if ( nActualInputType == INPUT_INCHI && + (bActualReadInChIOptions & READ_INCHI_OUTPUT_INCHI) && + (nRet1 == inchi_Ret_OKAY || nRet1 == inchi_Ret_WARNING) + ) + { + inchi_InputINCHI inpInchi; + inchi_Output inchi_out2; + int nRet3; + + inpInchi.szInChI = pOut->szInChI; + inpInchi.szOptions = pInp->szOptions; + + + nRet3 = GetINCHIfromINCHI( &inpInchi, &inchi_out2 ); + + + FreeINCHI ( pOut ); + *pOut = inchi_out2; + nRet = nRet3; + } +#endif + + + +#ifdef INCHI_TO_STRUCTURE + + /* How to convert InChI back to structure */ + + if ( nActualInputType == INPUT_INCHI && + (bActualReadInChIOptions & READ_INCHI_TO_STRUCTURE) && + (nRet1 == inchi_Ret_OKAY || nRet1 == inchi_Ret_WARNING) + ) + { + + inchi_InputINCHI inpInchi; + inchi_OutputStruct outStructure; + inchi_Input inchi_inp2; + inchi_Output inchi_out2; + int nRet3, nRet4; + + memset( &inpInchi, 0, sizeof(inpInchi) ); + memset( &outStructure, 0, sizeof(outStructure) ); + memset( &inchi_inp2, 0, sizeof(inchi_inp2) ); + memset( &inchi_out2, 0, sizeof(inchi_out2) ); + + /* Use the original InChI as input */ + inpInchi.szInChI = pOut->szInChI; + inpInchi.szOptions = szInchiCmdLine; + + + nRet3 = GetStructFromINCHI( &inpInchi, &outStructure ); + + + FreeINCHI( pOut ); /* we do not need the original InChI anymore */ + + if ( nRet3 == inchi_Ret_OKAY || nRet3 == inchi_Ret_WARNING ) +{ + /* convert structure back to InChI */ + inpInchi.szInChI = NULL; /* do not keep invalid pointers */ + /* prepare structure input: make input structure out of the reconstructed one */ + inchi_inp2.atom = outStructure.atom; + inchi_inp2.num_atoms = outStructure.num_atoms; + inchi_inp2.stereo0D = outStructure.stereo0D; + inchi_inp2.num_stereo0D = outStructure.num_stereo0D; + inchi_inp2.szOptions = szInchiCmdLine; + /* create InChI out of the reconstructed structure */ + nRet4 = GetINCHI( &inchi_inp2, &inchi_out2 ); + if ( nRet4 == inchi_Ret_OKAY || nRet4 == inchi_Ret_WARNING ) { + /* InChI of the reconstructed structure has been created */ + /* get rid of Struct->InChI message in inchi_out2 */ + /* and replace it with warning message from GetStructFromINCHI() */ + if ( inchi_out2.szMessage && inchi_out2.szMessage[0] ) { + inchi_out2.szMessage[0] = '\0'; + } + if ( nRet3 == inchi_Ret_WARNING ) { + /* prepare InChI->Structure warning for displaying: */ + /* exchange outStructure message pointer with that of inchi_out2 */ + char *p = inchi_out2.szMessage; + inchi_out2.szMessage = outStructure.szMessage; + outStructure.szMessage = p; + } + /* copy to pOut to use same output source code */ + /* as for Structure->InChI conversion output */ + *pOut = inchi_out2; + nRet1 = nRet4; /* InChI->Struct->InChI return value */ + nRet = nRet3; /* InChI->Struct return value */ + memset( &inchi_out2, 0, sizeof( inchi_out2) ); /* do not keep duplicated pointers */ + } else { + /* the last step in Structure->InChI->Structure->InChI failed */ + *pOut = inchi_out2; /* save the error output */ + nRet = nRet4; /* Reconstructed Struct->InChI error code */ + memset( &inchi_out2, 0, sizeof( inchi_out2) ); /* do not keep duplicated pointers */ + } + memset( &inchi_inp2, 0, sizeof(inchi_inp2) ); /* do not keep invalid pointers */ + } else { + /* error: Could not create Structure */ + inchi_ios_eprint( log_stream, "GetStructFromINCHI failed (%s)(%-d) %s\n", outStructure.szMessage? outStructure.szMessage:"???", nRet3, pStrLogId ); + } + FreeStructFromINCHI( &outStructure ); + } +#endif + +#ifdef MAKE_INCHI_FROM_AUXINFO + /************************************************** + * + * CREATE one more INCHI FROM AuxInfo and compare + * + * it to to the first INCHI + * + * Note: This should double the elapsed CPU time + * + **************************************************/ + if ( nRet1 == inchi_Ret_OKAY || nRet1 == inchi_Ret_WARNING ) { + char *szInchiAuxInfo = pOut->szAuxInfo, *p1 /* shadowing previous definition */; + int bDoNotAddH = ip->bDoNotAddH; + int nRet2; + inchi_Input inchi_inp2, *pInp2 = &inchi_inp2; + inchi_Output inchi_out2, *pOut2 = &inchi_out2; + InchiInpData idat; + /* setup input for Get_inchi_Input_FromAuxInfo */ + idat.pInp = pInp2; + pInp2->szOptions = NULL; /* not needed */ + + /* Make InChI input out of AuxInfo */ + nRet2 = Get_inchi_Input_FromAuxInfo( szInchiAuxInfo, bDoNotAddH, &idat ); + + if ( inchi_Ret_OKAY == nRet2 || inchi_Ret_WARNING == nRet2 ) { + /* set chiral flag */ + p1 = NULL; + if ( (ip->nMode & REQ_MODE_CHIR_FLG_STEREO) && (ip->nMode & REQ_MODE_STEREO) && + ( ip->bChiralFlag & (FLAG_SET_INP_AT_CHIRAL | FLAG_SET_INP_AT_NONCHIRAL) ) ) { + ; /* cmd line has priority over the chiral flag in Molfile */ + } else + if ( idat.bChiral & FLAG_INP_AT_CHIRAL ) { /* fix 04/05/2005 D.T.*/ + p1 = e_GetChiralFlagString( 1 ); /* input file has chiral flag */ + } else + if ( (ip->nMode & REQ_MODE_CHIR_FLG_STEREO) && (ip->nMode & REQ_MODE_STEREO) || + (idat.bChiral & FLAG_INP_AT_NONCHIRAL) ) { /* fix 04/05/2005 D.T.*/ + /* chiral flag requested (/SUCF) or input has non-chiral flag */ + p1 = e_GetChiralFlagString( 0 ); + } + if ( p1 ) { + if ( strlen(szInchiCmdLine) + strlen( p1 ) < sizeof(szInchiCmdLine) ) { + strcat( szInchiCmdLine, p1 ); + } else { + inchi_fprintf( stderr, "Too many options. Option \"%s\" ignored\n", p1 ); + } + } + + /* Make InChI out of InChI input out of AuxInfo */ + pInp2->szOptions = pInp->szOptions; + /* pInp2->szOptions =" /OutputSDF"; */ + memset( pOut2, 0, sizeof(*pOut2) ); + nRet2 = GetINCHI( pInp2, pOut2 ); + /* Compare the two InChI */ + if ( nRet2 == inchi_Ret_OKAY || nRet2 == inchi_Ret_WARNING ) { + if ( !pOut2->szInChI || strcmp( pOut->szInChI, pOut2->szInChI ) ) { + inchi_fprintf( stderr, "InChI from AuxInfo Is Different!\n" ); + } + } else { + inchi_fprintf( stderr, "InChI from AuxInfo could not be produced: \"%s\"\n", pOut2->szMessage ); + } + FreeINCHI( pOut2 ); + } + Free_inchi_Input( pInp2 ); + } +#endif + + /*****************************/ + /* output err/warn */ + /*****************************/ + if ( pOut->szMessage && pOut->szMessage[0] ) { + p1 = "; "; + p2 = pOut->szMessage; + } else { + p1 = p2 = ""; + } + switch ( nRet ) { + case inchi_Ret_UNKNOWN: + case inchi_Ret_FATAL: /* fatal processing error -- typically, memory allocation error */ + num_inp ++; + num_err ++; +#if( defined(EXIT_ON_ERR) && defined(REPEAT_ALL) && REPEAT_ALL > 0 ) + num_repeat = 0; +#endif + inchi_ios_eprint( log_stream, "Fatal Error (No INChI%s%s) %s\n", p1, p2, pStrLogId ); + inchi_ios_eprint( log_stream, "Log start:---------------------\n%s\nLog end--------------------\n", pOut->szLog? pOut->szLog : "Log is missing" ); + goto exit_function; + case inchi_Ret_EOF: /* typically, no input structure provided or help requested */ + /* output previous structure number and message */ + inchi_ios_eprint( log_stream, "End of file detected after structure %d\n", num_inp ); + goto exit_function; + case inchi_Ret_ERROR: + num_inp ++; + num_err ++; + inchi_ios_eprint( log_stream, "Error (No INChI%s%s) %s\n", p1, p2, pStrLogId ); + + if ( ip->bINChIOutputOptions & INCHI_OUT_STDINCHI ) + inchi_ios_print( out_stream, "InChI=1S//\n"); + else + inchi_ios_print( out_stream, "InChI=1//\n"); + +#if( defined(EXIT_ON_ERR) && defined(REPEAT_ALL) && REPEAT_ALL > 0 ) + num_repeat = 0; + goto exit_function; +#endif + continue; + case inchi_Ret_SKIP: + num_inp ++; + inchi_ios_eprint( log_stream, "Skipped %s\n", pStrLogId ); + goto exit_function; + case inchi_Ret_OKAY: + break; + case inchi_Ret_WARNING: + if ( p2 && p2[0] ) { + inchi_ios_eprint( log_stream, "Warning (%s) %s\n", p2, pStrLogId ); + } + break; /* ok */ + } + + num_inp ++; + tot_len = 0; + + /*^^^ Post-1.02b - moved from below */ + bTabbed = 0 != ( ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT ); + + if ( pOut->szInChI && pOut->szInChI[0] ) { + if (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY ) { + /*****************************/ + /* output SDfile */ + /*****************************/ + char *start; + unsigned len; + int bAddLabel = 0; + /*******************************************************************************/ + /* output a SDfile. pOut->szInChI contains Molfile ending with "$$$$\n" line. */ + /* Replace the 1st line with the structure number */ + /* Replace the last line with the SDfile header, label, and new "$$$$\n" line */ + /*******************************************************************************/ + /* 1. remove the 1st line (later replace it with the actual structure number) */ + if ( start = strchr( pOut->szInChI, '\n' ) ) { + inchi_ios_print( out_stream, "Structure #%ld", nStructNo ); + } else { + start = pOut->szInChI; + } + /* 2. SDfile header and data: write zero to the 1st byte of + * the last line "$$$$\n" to remove this line with purpose to relpace it */ + if ( ip->pSdfLabel && ip->pSdfLabel[0] && ip->pSdfValue && ip->pSdfValue[0] && + (len = strlen(start)) && len > 5 && '$' == start[len-5] && '\n' == start[len-6] ) { + start[len-5] = '\0'; + bAddLabel = 1; + } + /* 3. Output the whole Molfile */ + inchi_ios_print( out_stream, "%s", start ); + if ( bAddLabel ) { + inchi_ios_print( out_stream, "> <%s>\n%s\n\n$$$$\n", ip->pSdfLabel, ip->pSdfValue ); + } + } else { + /*****************************/ + /* output InChI */ + /*****************************/ + + int bAuxInfo = !( ip->bINChIOutputOptions & INCHI_OUT_ONLY_AUX_INFO ) && + pOut->szAuxInfo && pOut->szAuxInfo[0]; + /*^^^ Post-1.02b - correctly treat tabbed output with InChIKey */ + int bAuxOrKey = bAuxInfo || ( ip->bCalcInChIHash != INCHIHASH_NONE ); + + const char *pLF = "\n"; + const char *pTAB = bTabbed? "\t" : pLF; + if ( !ip->bNoStructLabels ) { + /* or print a previously created label string */ + inchi_ios_print( out_stream, "%s%s", pStrInchiId, pTAB ); + } + /* output INChI Identifier */ + /*^^^ Post-1.02b */ + /* inchi_ios_print( out_stream, "%s%s", pOut->szInChI, bAuxInfo? pTAB : pLF );*/ + inchi_ios_print( out_stream, "%s%s", pOut->szInChI, bAuxOrKey? pTAB : pLF ); + /* output INChI Aux Info */ + if ( bAuxInfo ) { + inchi_ios_print(out_stream, "%s%s",pOut->szAuxInfo, ip->bCalcInChIHash? pTAB : pLF); + } + + /*^^^ Calculate InChIKey */ + if ( ip->bCalcInChIHash != INCHIHASH_NONE ) + { + xhash1 = xhash2 = 0; + if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1 ) || + ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) + xhash1 = 1; + if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA2 ) || + ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) + xhash2 = 1; + + ik_ret = GetINCHIKeyFromINCHI(pOut->szInChI, xhash1, xhash2, + ik_string, szXtra1, szXtra2); + if (ik_ret==INCHIKEY_OK) + { + /* NB: correctly treat tabbed output with InChIKey & hash extensions */ + char csep = '\n'; + if ( ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT ) + csep = '\t'; + inchi_ios_print(out_stream, "InChIKey=%-s",ik_string); + if ( xhash1 ) + inchi_ios_print(out_stream, "%cXHash1=%-s",csep,szXtra1); + if ( xhash2 ) + inchi_ios_print(out_stream, "%cXHash2=%-s",csep,szXtra2); + inchi_ios_print(out_stream, "\n"); + } + else + { + /*^^^ Post-1.02b - add LF if output is tabbed and key generation failed */ + if (bTabbed) + inchi_ios_print(out_stream, "\n"); + + inchi_ios_eprint(log_stream, "Warning: could not compute InChIKey for #%-d ", + num_inp ); + switch(ik_ret) + { + case INCHIKEY_UNKNOWN_ERROR : + inchi_ios_eprint(log_stream, "(invalid key length requested)\n"); + break; + case INCHIKEY_EMPTY_INPUT: + inchi_ios_eprint(log_stream, "(got an empty string)\n"); + break; + case INCHIKEY_INVALID_INCHI_PREFIX: + case INCHIKEY_INVALID_INCHI: + case INCHIKEY_INVALID_STD_INCHI: + inchi_ios_eprint(log_stream, "(got non-InChI string)\n"); + break; + case INCHIKEY_NOT_ENOUGH_MEMORY: + inchi_ios_eprint(log_stream, "(not enough memory to treat the string)\n"); + break; + default:inchi_ios_eprint(log_stream, "(internal program error)\n"); + break; + } + } + } + } + } + } + if ( e_bInterrupted ) { + inchi_ios_eprint( log_stream, "\nStructure %d could not be processed: User Quit.\n", num_inp+1 ); + num_err ++; + goto exit_function; + } + +exit_function: + inchi_ios_eprint( log_stream, "\nProcessed %ld structure%s, %ld error%s.\n", + num_inp, (num_inp==1)?"":"s", num_err, (num_err==1)?"":"s" ); + + + e_FreeInchi_Input( pInp ); +#ifndef USE_STDINCHI_API + FreeINCHI ( pOut ); +#else + FreeStdINCHI ( pOut ); +#endif + + + inchi_ios_close(inp_stream); + inchi_ios_close(log_stream); + inchi_ios_close(out_stream); + inchi_ios_close(prb_stream); + + + for ( i = 0; i < MAX_NUM_PATHS; i ++ ) { + if ( ip->path[i] ) { + e_inchi_free( (char*) ip->path[i] ); /* cast deliberately discards 'const' qualifier */ + ip->path[i] = NULL; + } + } + +#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) + if ( --num_repeat > 0 ) { + goto repeat; + } +#endif + + + return 0; +} + + +#endif + +/**********************************************************/ +int e_MakeOutputHeader( char *pSdfLabel, char *pSdfValue, long lSdfId, long num_inp, char *pStr1, char *pStr2 ) +{ + int tot_len1 = 0, tot_len2 = 0; + pStr1[0] = '\0'; + if ( !(pSdfLabel && pSdfLabel[0]) && !(pSdfValue && pSdfValue[0]) ) { + tot_len1 = sprintf( pStr1, "Structure: %ld", num_inp ); + tot_len2 = sprintf( pStr2, "structure #%ld", num_inp ); + } else { + tot_len1 = sprintf( pStr1, "Structure: %ld.%s%s%s%s", + num_inp, + SDF_LBL_VAL(pSdfLabel, pSdfValue) ); + + tot_len2 = sprintf( pStr2, "structure #%ld.%s%s%s%s", + num_inp, + SDF_LBL_VAL(pSdfLabel, pSdfValue) ); + if ( lSdfId ) { + tot_len1 += sprintf( pStr1 + tot_len1, ":%ld", lSdfId ); + tot_len2 += sprintf( pStr2 + tot_len2, ":%ld", lSdfId ); + } + } + return tot_len1; +} +/************************************************************/ +char *e_GetChiralFlagString( int bChiralFlagOn ) +{ + static char szChiralFlag[64]; + szChiralFlag[0] = ' '; + szChiralFlag[1] = INCHI_OPTION_PREFX; + sprintf( szChiralFlag+2, "ChiralFlag%s", bChiralFlagOn? "On":"Off" ); + return szChiralFlag; +} + + +#endif /*^^^ CREATE_INCHI_STEP_BY_STEP */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/e_ichimain_a.c b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichimain_a.c similarity index 86% rename from INCHI-1-SRC/INCHI_API/inchi_main/e_ichimain_a.c rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichimain_a.c index 73e9b61..eadfd6c 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_main/e_ichimain_a.c +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichimain_a.c @@ -1,998 +1,984 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Demo caller using modularized library interface (see main() below) - -Activated if (CREATE_INCHI_STEP_BY_STEP==1) [e_mode.h] - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - - - -#if( defined( WIN32 ) && defined( _CONSOLE ) && defined(_MSC_VER) && _MSC_VER >= 800 ) -#define ADD_WIN_CTLC /* detect Ctrl-C under Win-32 and Microsoft Visual C ++ */ -#endif - -#if( defined( WIN32 ) && defined( _CONSOLE ) && defined(_MSC_VER) && _MSC_VER >= 800 && defined(ADD_WIN_CTLC) && !(defined(__STDC__) && __STDC__ == 1) ) -#include -#endif - - -/* #define CREATE_0D_PARITIES */ /* uncomment to replace coordinates and 2D-parirties with 0D-parities */ -/* in case of CREATE_0D_PARITIES, the hardcoded bFixSp3Bug = 1 fixes sp3 bugs in original InChI v. 1.00 */ -/* in case of CREATE_0D_PARITIES, the Phosphine and Arsine sp3 stereo options are not supported */ - -#define NEIGH_ONLY_ONCE /* comment out to include each bond in both neighboring atoms adjacency lists */ - - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "e_mode.h" -#ifdef CREATE_INCHI_STEP_BY_STEP - -#include "e_ctl_data.h" - -#include "inchi_api.h" - - -#include "e_inchi_atom.h" -#include "e_ichi_parms.h" -#include "e_util.h" -#include "e_ichi_io.h" -#include "e_ichierr.h" -#include "e_readstru.h" -#include "e_ichicomp.h" -#ifdef CREATE_0D_PARITIES -#include "e_0dstereo.h" -#endif - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Local prototypes. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -int e_MakeOutputHeader(char *pSdfLabel, char *pSdfValue, long lSdfId, long num_inp, char *pStr1, char *pStr2); -char *e_GetChiralFlagString( int bChiralFlagOn ); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Console-specific -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -/*^^^ Ctrl+C trap; works under Win32 + MS VC++ */ -int e_bInterrupted = 0; -int stop = 0; -#if( defined( _WIN32 ) && defined( _CONSOLE ) ) -#if( defined(_MSC_VER) && _MSC_VER >= 800 && defined(ADD_WIN_CTLC) && !(defined(__STDC__) && __STDC__ == 1) ) -BOOL WINAPI MyHandlerRoutine(DWORD dwCtrlType) /* control signal type */ -{ - if ( dwCtrlType == CTRL_C_EVENT || - dwCtrlType == CTRL_BREAK_EVENT || - dwCtrlType == CTRL_CLOSE_EVENT || - dwCtrlType == CTRL_LOGOFF_EVENT ) { - e_bInterrupted = 1; - return TRUE; - } - return FALSE; -} -#endif -int e_WasInterrupted(void) -{ -#ifdef _DEBUG - if ( e_bInterrupted ) stop=1; /* for debug only */ -#endif - return e_bInterrupted; -} - -#endif - - -#define EXIT_ON_ERR - - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -main(): caller program demonstrating 'another InChI library interface' - and InChIKey calculation - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -int main( int argc, char *argv[ ] ) -{ -#define nStrLen 256 -int bReleaseVersion = bRELEASE_VERSION; - -char szSdfDataValue[MAX_SDF_VALUE+1]; -char szInchiCmdLine[512]; -char pStrInchiId[nStrLen], pStrLogId[nStrLen]; -const char *p1, *p2; - - -int retcode= 0, retcode1 = 0, i, k, tot_len; -int inp_index, out_index; -int nStructNo; - -long lSdfId; -long num_inp, num_err, num_output; - -INPUT_PARMS inp_parms, *ip = &inp_parms; -STRUCT_DATA struct_data, *sd = &struct_data; - - -INCHI_IOSTREAM outputstr, logstr, prbstr, instr; -INCHI_IOSTREAM *out_stream=&outputstr, *log_stream=&logstr, *prb_stream=&prbstr, *inp_stream=&instr; - -inchi_Input inchi_inp, *pInp = &inchi_inp; -inchi_Output genout, *pResults = &genout; -INCHIGEN_DATA inchi_gendata, *pGenData = &inchi_gendata; -INCHIGEN_HANDLE HGen = NULL; - -char *pinfo; -int len_mess=0, len_mess1, mshift=0, was_print = 0; -char ik_string[256]; /*^^^ Resulting InChIKey string */ -int ik_ret=0; /*^^^ InChIKey-calc result code */ -int xhash1, xhash2; -char szXtra1[256], szXtra2[256]; - -unsigned long ulDisplTime = 0; /* infinite, milliseconds */ -time_t elapsed; -/*^^^ Post-1.02b - moved from below */ -int bTabbed=0; - - - -/*^^^ Set debug output */ - -#if (TRACE_MEMORY_LEAKS == 1) - - _CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF); - /* for execution outside of the VC++ debugger uncomment one of the following two */ - /*#define MY_REPORT_FILE _CRTDBG_FILE_STDERR */ - /*#define MY_REPORT_FILE _CRTDBG_FILE_STDOUT */ -#ifdef MY_REPORT_FILE - _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_WARN, MY_REPORT_FILE ); - _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_ERROR, MY_REPORT_FILE ); - _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_ASSERT, MY_REPORT_FILE ); -#else - _CrtSetReportMode(_CRT_WARN | _CRT_ERROR, _CRTDBG_MODE_DEBUG); -#endif - /* turn on floating point exceptions */ -#if ( !defined(__STDC__) || __STDC__ != 1 ) - { - /* Get the default control word. */ - int cw = _controlfp( 0,0 ); - - /* Set the exception masks OFF, turn exceptions on. */ - /*cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_INEXACT|EM_ZERODIVIDE|EM_DENORMAL);*/ - cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE|EM_DENORMAL); - - /* Set the control word. */ - _controlfp( cw, MCW_EM ); - } -#endif - -#endif /* #if (TRACE_MEMORY_LEAKS == 1) */ - - - - -/*^^^ Set Ctrl+C trap under Win32 + MS VC++ */ - -#if( defined( _WIN32 ) && defined( _CONSOLE ) && defined(_MSC_VER) && _MSC_VER >= 800 && defined(ADD_WIN_CTLC) && !(defined(__STDC__) && __STDC__ == 1)) - if ( SetConsoleCtrlHandler( MyHandlerRoutine, 1 ) ) - { - ; /*ConsoleQuit = WasInterrupted*/; - } -#endif - - - - num_inp = 0; - num_err = 0; - num_output = 0; - - elapsed = time(NULL); - - /*^^^ Set original input structure */ - memset(pInp, 0, sizeof(*pInp)); - memset(pResults, 0, sizeof(*pResults)); - memset(pGenData, 0, sizeof(*pGenData)); - memset(szSdfDataValue, 0, sizeof(szSdfDataValue)); - - /*^^^ Initialize I/O streams as string buffers so that they may be filled within dll; - also associate files with the streams and use inchi_ios_flush to flush buffer content */ - - inchi_ios_init(log_stream, INCHI_IOSTREAM_STRING, NULL); - inchi_ios_init(inp_stream, INCHI_IOSTREAM_STRING, NULL); - inchi_ios_init(out_stream, INCHI_IOSTREAM_STRING, NULL); - inchi_ios_init(prb_stream, INCHI_IOSTREAM_STRING, NULL); - - - /*^^^ Check command line */ - if ( argc < 2 || argc==2 && ( argv[1][0]==INCHI_OPTION_PREFX ) && - (!strcmp(argv[1]+1, "?") || !stricmp(argv[1]+1, "help") ) ) - { - /* e_HelpCommandLineParms(stdout); */ - e_HelpCommandLineParmsReduced(log_stream); - log_stream->f = stderr; - inchi_ios_flush(log_stream); - return 0; - } - - if ( 0 > e_ReadCommandLineParms( argc, (const char **)argv, ip, szSdfDataValue, &ulDisplTime, bReleaseVersion, log_stream ) ) - /*^^^ Explicitly cast to (const char **) to avoid a - warning about "incompatible pointer type":*/ - { - - goto exit_function; - } - - - /*^^^ Open files associated with I/O streams. */ - if ( !e_OpenFiles( &(inp_stream->f), &(out_stream->f), &(log_stream->f), &(prb_stream->f), ip ) ) - goto exit_function; - - - - /*^^^ Create InChI generator object. */ -#ifndef USE_STDINCHI_API - HGen = INCHIGEN_Create(); -#else - HGen = STDINCHIGEN_Create(); -#endif - - if (NULL==HGen) - { - /*^^^ Probably, out of RAM. Could do nothing. */ - inchi_fprintf( stderr, "Could not create InChI generator (out of RAM?)\n" ); - goto exit_function; - } - - - - - /*^^^ Set input labelling opts */ - if ( ip->bNoStructLabels ) - { - ip->pSdfLabel = NULL; - ip->pSdfValue = NULL; - } - else - if ( ip->nInputType == INPUT_INCHI_PLAIN || - ip->nInputType == INPUT_INCHI_XML || - ip->nInputType == INPUT_CMLFILE ) - { - /* the input may contain both the header and the label of the structure */ - if ( !ip->pSdfLabel ) ip->pSdfLabel = ip->szSdfDataHeader; - if ( !ip->pSdfValue ) ip->pSdfValue = szSdfDataValue; - } - e_PrintInputParms( log_stream, ip ); - inchi_ios_flush2(log_stream,stdout); - pStrInchiId[0] = '\0'; - - - - - /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Main cycle: read input structures and create their InChI - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - out_index = 0; - - - while (!e_bInterrupted) - { - - int bHasTimeout = 0; - if ( ip->last_struct_number && num_inp >= ip->last_struct_number ) - { - retcode = _IS_EOF; /* simulate end of file */ - goto exit_function; - } - - /*^^^ Create command line */ - szInchiCmdLine[0] = '\0'; - for ( i = 1; i < argc; i ++ ) - { - if ( argv[i] && INCHI_OPTION_PREFX == argv[i][0] && argv[i][1] ) - { - /*^^^ Omit certain options */ - if ( !memicmp( argv[i]+1, "start:", 6) || - !memicmp( argv[i]+1, "end:", 4) || - !stricmp( argv[i]+1, "Tabbed" ) - ) - { - continue; - } - - if ( !stricmp( argv[i]+1, "Inchi2Inchi" ) ) - { - inchi_ios_eprint( log_stream, "\nOption Inchi2Inchi is not supported (use classic interface). \n" ); - goto exit_function; - } - else if ( !stricmp( argv[i]+1, "Inchi2Struct" ) ) - { - inchi_ios_eprint( log_stream, "\nOption Inchi2Struct is not supported (use classic interface). \n" ); - goto exit_function; - } - - - if ( !memicmp( argv[i]+1, "w", 1 ) && isdigit( UCINT argv[i][2] ) ) - { - bHasTimeout = 1; - } - /*^^^ Add option to szInchiCmdLine */ - if ( strlen(szInchiCmdLine)+strlen(argv[i]) + 4 < sizeof(szInchiCmdLine) ) - { - if ( szInchiCmdLine[0] ) - strcat( szInchiCmdLine, " " ); - k = ( !(k=strcspn( argv[i], " \t" )) || argv[i][k] ); /* k means enclose in "" */ - if (k) - strcat( szInchiCmdLine, "\"" ); - strcat( szInchiCmdLine, argv[i] ); - if (k) - strcat( szInchiCmdLine, "\"" ); - } else - { - inchi_fprintf( stderr, "Too many options. Option \"%s\" ignored\n", argv[i] ); - } - } - } - if ( !bHasTimeout ) - { - /*^^^ Add default timeout option -W60: 60 seconds */ - char szW60[] = " ?W60"; - szW60[1] = INCHI_OPTION_PREFX; - if ( strlen(szInchiCmdLine) + strlen( szW60 ) < sizeof(szInchiCmdLine) ) - strcat( szInchiCmdLine, szW60 ); - else - inchi_fprintf( stderr, "Too many options. Option \"%s\" ignored\n", szW60 ); - } - /*^^^ End of command line deal */ - - - if ( ip->nInputType==INPUT_INCHI_PLAIN ) - { - inchi_ios_eprint( log_stream, "\nRestoring InChI from AuxInfo is not supported (use classic interface). \n" ); - goto exit_function; - } - - - /*^^^ Skip input cycle */ - while(!e_bInterrupted) - { - inp_index = out_index; - - - /*^^^ Read one structure from input */ - e_FreeInchi_Input( pInp ); - - - - retcode = e_ReadStructure( sd, ip, inp_stream, log_stream, out_stream, prb_stream, pInp, num_inp+1, - /* for CML:*/ inp_index, &out_index ); - - inchi_ios_flush2(log_stream,stdout); - - if ( _IS_SKIP != retcode) - { - lSdfId = ( ip->bGetSdfileId )? ip->lSdfId : 0; /* if requested then CAS r.n. otherwise struct. number*/ - nStructNo = ( ip->lMolfileNumber > 0 )? ip->lMolfileNumber : num_inp+1; - e_MakeOutputHeader( ip->pSdfLabel, ip->pSdfValue, lSdfId, nStructNo, pStrInchiId, pStrLogId ); - } - - /* e_ReadStructure() outputs the error/warning messages, so we do not need to re-output them here */ - switch (retcode) - { - case _IS_FATAL: - num_inp ++; - num_err ++; - goto exit_function; - case _IS_EOF: - inchi_ios_eprint( log_stream, "\rStructure %d could not be read: Detected end of file. \r", num_inp+1 ); - goto exit_function; - case _IS_ERROR: - num_inp ++; - num_err ++; - continue; - case _IS_SKIP: - num_inp ++; - continue; - } - break; - } - if ( e_bInterrupted ) - { - inchi_ios_eprint( log_stream, "\nStructure %d could not be read: User Quit.\n", num_inp+1 ); - inchi_ios_flush2(log_stream,stdout); - num_err ++; - goto exit_function; - } - - - /*^^^ Analyze the chiral flag */ - - p1 = NULL; -#ifndef USE_STDINCHI_API - if ( (ip->nMode & REQ_MODE_CHIR_FLG_STEREO) && (ip->nMode & REQ_MODE_STEREO) && - ( ip->bChiralFlag & (FLAG_SET_INP_AT_CHIRAL | FLAG_SET_INP_AT_NONCHIRAL) ) ) - ; /* cmd line has priority over the chiral flag in Molfile */ - - else if ( sd->bChiralFlag & FLAG_INP_AT_CHIRAL ) - p1 = e_GetChiralFlagString( 1 ); /* input file has chiral flag */ - - else if ( (ip->nMode & REQ_MODE_CHIR_FLG_STEREO) && (ip->nMode & REQ_MODE_STEREO) || - (sd->bChiralFlag & FLAG_INP_AT_NONCHIRAL) ) /* fix 04/05/2005 D.T.*/ - /* chiral flag requested (/SUCF) or input has non-chiral flag */ - p1 = e_GetChiralFlagString( 0 ); - - if (p1) - { - if ( strlen(szInchiCmdLine) + strlen( p1 ) < sizeof(szInchiCmdLine) ) - strcat( szInchiCmdLine, p1 ); - else - inchi_fprintf( stderr, "Too many options. Option \"%s\" ignored\n", p1 ); - } -#endif - pInp->szOptions = szInchiCmdLine; - - - /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - create INChI for each connected component of the structure and optionally - display them; output INChI for the whole structure. - Use compartmentalized library interface. - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -#ifdef CREATE_0D_PARITIES - if ( !pInp->stereo0D && !pInp->num_stereo0D ) - { - int bPointedEdgeStereo = (0 != (TG_FLAG_POINTED_EDGE_STEREO & ip->bTautFlags)); - set_0D_stereo_parities( pInp, bPointedEdgeStereo ); - Clear3D2Dstereo(pInp); - } -#endif -#ifdef NEIGH_ONLY_ONCE - e_RemoveRedundantNeighbors(pInp); -#endif - - len_mess = len_mess1 = 0; - pinfo = (char*) &(pGenData->pStrErrStruct); - was_print = 0; - - - - /*^^^ Set up */ - -#ifndef USE_STDINCHI_API - retcode = INCHIGEN_Setup( HGen, pGenData, pInp ); -#else - retcode = STDINCHIGEN_Setup( HGen, pGenData, pInp ); -#endif - - len_mess1 = strlen(pinfo); - if (len_mess1 > len_mess) - { - mshift = len_mess?1:0; - inchi_ios_eprint(log_stream, "*** %-s [Initialization] %-s\n", pStrLogId, pinfo+len_mess+mshift); - inchi_ios_flush2(log_stream,stdout); - len_mess = len_mess1; - was_print = 1; - } - - - - /*^^^ Normalization step */ - if ( (retcode == inchi_Ret_OKAY) || (retcode == inchi_Ret_WARNING) ) - { - -#ifndef USE_STDINCHI_API - retcode = INCHIGEN_DoNormalization(HGen, pGenData); -#else - retcode = STDINCHIGEN_DoNormalization(HGen, pGenData); -#endif - - len_mess1 = strlen(pinfo); - if (len_mess1 > len_mess) - { - mshift = len_mess?1:0; - inchi_ios_eprint(log_stream, "*** %-s [Normalization] %-s\n", pStrLogId, pinfo+len_mess+mshift); - inchi_ios_flush2(log_stream,stdout); - len_mess = len_mess1; - was_print = 1; - } - -#ifdef OUTPUT_NORMALIZATION_DATA - { - int ic, istruct, itaut, ia, ib, nc[2]; - NORM_ATOMS *inp_norm_data[TAUT_NUM]; /* = { &NormAtomsNontaut, &NormAtomsTaut}; */ - NORM_ATOM *atom; - nc[0] = pGenData->num_components[0]; - nc[1] = pGenData->num_components[1]; - inchi_ios_eprint(log_stream, "=== %-s Intermediate normalization data follow", pStrLogId); - for (istruct=0; istruct<2; istruct++) - { - /*^^^ Print results for 1) original/disconnected structure and - 2)reconnected structure */ - if (nc[istruct]>0) - { - if (istruct==0) - inchi_ios_eprint(log_stream, "\n\tOriginal/disconnected structure: "); - else - inchi_ios_eprint(log_stream, "\n\tReconnected structure: "); - inchi_ios_eprint(log_stream, "%-d component(s)\n", nc[istruct]); - inchi_ios_eprint(log_stream, "\t===============================================\n"); - - for (ic=0; ic < nc[istruct]; ic++) - { - /* Print results for each component of the structure */ - inp_norm_data[0] = &(pGenData->NormAtomsNontaut[istruct][ic]); - inp_norm_data[1] = &(pGenData->NormAtomsTaut[istruct][ic]); - for (itaut=0;itaut<2;itaut++) - { - /* Print results for 1) non-tautomeric and - 2) tautomeric version (if present) - of the component */ - if (NULL!=inp_norm_data[itaut]) - { - - if (inp_norm_data[itaut]->num_at>0) - { - if (itaut==0) - inchi_ios_eprint(log_stream, "\tComponent %-d, non-tautomeric:", ic+1); - else - inchi_ios_eprint(log_stream, "\tComponent %-d, tautomeric:", ic+1); - - inchi_ios_eprint(log_stream, "\t%-d atom(s)\n", inp_norm_data[itaut]->num_at); - - for (ia=0; ia< inp_norm_data[itaut]->num_at; ia++) - { - /*^^^ Print data for each atom */ - if (inp_norm_data[itaut]->at != NULL) - { - atom = &( inp_norm_data[itaut]->at[ia] ); - if (NULL!=atom) - { - /*^^^ Print: element, number, original number, no. of Hs, - charge, coordination number, valence */ - inchi_ios_eprint(log_stream, "\t\tatom %-s%-d (orig.%-s%-d) [H%-d] charge=%-+d CN=%-d val=%-d ", - atom->elname, ia+1, atom->elname, atom->orig_at_number, atom->num_H, atom->charge, - atom->valence, atom->chem_bonds_valence); - if (atom->valence > 0) - { - /*^^^ Neighbors */ - inchi_ios_eprint(log_stream, "nbrs { "); - for (ib=0; ib valence; ib++) - { - inchi_ios_eprint(log_stream, "%-d ", atom->neighbor[ib]+1); - } - inchi_ios_eprint(log_stream, "}"); - } - /* Indicate if atom shares Hs with others */ - if (atom->endpoint > 0) - { - inchi_ios_eprint(log_stream, "\n\t\t(in taut. group: %-d)", atom->endpoint); - } - inchi_ios_eprint(log_stream, "\n"); - } - } - } - } - - } - } - - } - } - } - - } - inchi_ios_flush(log_stream); -#endif - } - - /*^^^ Canonicalization step */ - if ( (retcode == inchi_Ret_OKAY) || (retcode == inchi_Ret_WARNING) ) - { - -#ifndef USE_STDINCHI_API - retcode = INCHIGEN_DoCanonicalization( HGen, pGenData ); -#else - retcode = STDINCHIGEN_DoCanonicalization( HGen, pGenData ); -#endif - - len_mess1 = strlen(pinfo); - if (len_mess1 > len_mess) - { - mshift = len_mess?1:0; - inchi_ios_eprint(log_stream, "*** %-s [Canonicalization] %-s\n", pStrLogId, pinfo+len_mess+mshift); - inchi_ios_flush2(log_stream,stdout); - len_mess = len_mess1; - was_print = 1; - } - } - - /*^^^ Serialization (final) step */ - if ( (retcode == inchi_Ret_OKAY) || (retcode == inchi_Ret_WARNING) ) - { - -#ifndef USE_STDINCHI_API - retcode1 = INCHIGEN_DoSerialization(HGen, pGenData, pResults); -#else - retcode1 = STDINCHIGEN_DoSerialization(HGen, pGenData, pResults); -#endif - - retcode = inchi_max(retcode , retcode1); - - - len_mess1 = strlen(pinfo); - if (len_mess1 > len_mess) - { - mshift = len_mess?1:0; - inchi_ios_eprint(log_stream, "*** %-s [Serialization] %-s\n", pStrLogId, pinfo+len_mess+mshift); - inchi_ios_flush2(log_stream,stdout); - was_print = 1; - } - - } - -/* - if (!was_print) - inchi_ios_eprint(log_stream, "*** %-s \r", pStrLogId); -*/ - - - /*^^^ Output err/warn */ - if ( pResults->szMessage && pResults->szMessage[0] ) { p1 = "; "; p2 = pResults->szMessage; } - else p1 = p2 = ""; - - - switch (retcode) - { - case inchi_Ret_UNKNOWN: - case inchi_Ret_FATAL: /* fatal processing error -- typically, memory allocation error */ - num_inp ++; - num_err ++; - inchi_ios_eprint( log_stream, "Fatal Error (No INChI%s%s) %s\n", p1, p2, pStrLogId ); - inchi_ios_eprint( log_stream, "Log start:---------------------\n%s\nLog end--------------------\n", pResults->szLog? pResults->szLog : "Log is missing" ); - /*^^^ Free InChI library memories */ -#ifndef USE_STDINCHI_API - INCHIGEN_Reset(HGen, pGenData, pResults); -#else - STDINCHIGEN_Reset(HGen, pGenData, pResults); -#endif - goto exit_function; - case inchi_Ret_EOF: /* typically, no input structure provided or help requested */ - /* output previous structure number and message */ - inchi_ios_eprint( log_stream, "End of file detected after structure %d\n", num_inp ); - goto exit_function; - case inchi_Ret_ERROR: - num_inp ++; - num_err ++; - inchi_ios_eprint( log_stream, "Error (No INChI%s%s) %s\n", p1, p2, pStrLogId ); - inchi_ios_flush2(log_stream,stdout); - /*^^^ Free InChI library memories */ -#ifndef USE_STDINCHI_API - INCHIGEN_Reset(HGen, pGenData, pResults); -#else - STDINCHIGEN_Reset(HGen, pGenData, pResults); -#endif - continue; - case inchi_Ret_SKIP: - num_inp ++; - inchi_ios_eprint( log_stream, "Skipped %s\n", pStrLogId ); - goto exit_function; - case inchi_Ret_OKAY: - break; - case inchi_Ret_WARNING: - /*^^^ Suppress warnings, we display them step by steps */ - /*^^^ - if ( p2 && p2[0] ) - inchi_ios_eprint( log_stream, "Warning (%s) %s\n", p2, pStrLogId ); - */ - break; /* ok */ - } - - num_inp ++; - tot_len = 0; - - /*^^^ Post-1.02b - here from below */ - bTabbed = 0 != ( ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT ); - - if ( pResults->szInChI && pResults->szInChI[0] ) - { - if (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY ) - { - /*^^^ output SDfile */ - char *start; - unsigned len; - int bAddLabel = 0; - /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - output a SDfile. pResults->szInChI contains Molfile ending with "$$$$\n" line. - Replace the 1st line with the structure number - Replace the last line with the SDfile header, label, and new "$$$$\n" line - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - - start = pResults->szInChI; - - /*^^^ 2. SDfile header and data: write zero to the 1st byte of - the last line "$$$$\n" to remove this line with purpose to relpace it */ - if ( ip->pSdfLabel && ip->pSdfLabel[0] && ip->pSdfValue && ip->pSdfValue[0] && - (len = strlen(start)) && len > 5 && '$' == start[len-5] && '\n' == start[len-6] ) { - start[len-5] = '\0'; - bAddLabel = 1; - } - - /*^^^ 3. Output the whole Molfile */ - inchi_ios_print( out_stream, "%s", start ); - if ( bAddLabel ) - { - inchi_ios_print( out_stream, "> <%s>\n%s\n\n$$$$\n", ip->pSdfLabel, ip->pSdfValue ); - } - - inchi_ios_flush(out_stream); - } /* if (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY ) */ - - else - - { - /*^^^ Print InChI */ - - int bAuxInfo = !( ip->bINChIOutputOptions & INCHI_OUT_ONLY_AUX_INFO ) && - pResults->szAuxInfo && pResults->szAuxInfo[0]; - - /*^^^ Post-1.02b - correctly treat tabbed output with InChIKey */ - int bAuxOrKey = bAuxInfo || ( ip->bCalcInChIHash != INCHIHASH_NONE ); - - const char *pLF = "\n"; - const char *pTAB = bTabbed? "\t" : pLF; - if ( !ip->bNoStructLabels ) - { - /* Print a previously created label string */ - inchi_ios_print(out_stream, "%s%s", pStrInchiId, pTAB); - } - - /*^^^ Print INChI Identifier */ - - /*^^^ Post-1.02b */ - inchi_ios_print(out_stream, "%s%s", pResults->szInChI, bAuxOrKey? pTAB : pLF); - /*^^^ Print INChI Aux Info */ - if ( bAuxInfo ) - inchi_ios_print(out_stream, "%s%s",pResults->szAuxInfo, ip->bCalcInChIHash? pTAB : pLF); - - inchi_ios_flush(out_stream); - } - - } /* if ( pResults->szInChI && pResults->szInChI[0] ) */ - - - /*^^^ Calculate InChIKey */ - if ( ip->bCalcInChIHash != INCHIHASH_NONE ) - { - xhash1 = xhash2 = 0; - if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1 ) || - ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) - xhash1 = 1; - if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA2 ) || - ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) - xhash2 = 1; - - ik_ret = GetINCHIKeyFromINCHI(pResults->szInChI, xhash1, xhash2, - ik_string, szXtra1, szXtra2); - if (ik_ret==INCHIKEY_OK) - { - /* NB: correctly treat tabbed output with InChIKey & hash extensions */ - char csep = '\n'; - if ( ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT ) - csep = '\t'; - inchi_ios_print(out_stream, "InChIKey=%-s",ik_string); - if ( xhash1 ) - inchi_ios_print(out_stream, "%cXHash1=%-s",csep,szXtra1); - if ( xhash2 ) - inchi_ios_print(out_stream, "%cXHash2=%-s",csep,szXtra2); - inchi_ios_print(out_stream, "\n"); - } - else - { - /*^^^ Post-1.02b - add LF if output is tabbed and key generation failed */ - if (bTabbed) - inchi_ios_print(out_stream, "\n"); - - inchi_ios_eprint(log_stream, "Warning: could not compute InChIKey for #%-d ", - num_inp); - switch(ik_ret) - { - case INCHIKEY_UNKNOWN_ERROR: - inchi_ios_eprint(log_stream, "(invalid key length requested)\n"); - break; - case INCHIKEY_EMPTY_INPUT: - inchi_ios_eprint(log_stream, "(got an empty string)\n"); - break; - case INCHIKEY_INVALID_INCHI_PREFIX: - case INCHIKEY_INVALID_INCHI: - case INCHIKEY_INVALID_STD_INCHI: - inchi_ios_eprint(log_stream, "(got non-InChI string)\n"); - break; - case INCHIKEY_NOT_ENOUGH_MEMORY: - inchi_ios_eprint(log_stream, "(not enough memory to treat the string)\n"); - break; - default:inchi_ios_eprint(log_stream, "(internal program error)\n"); - break; - } - } - inchi_ios_flush(out_stream); - inchi_ios_flush2(log_stream,stdout); - - } - - - - /*^^^ Free InChI library memories */ -#ifndef USE_STDINCHI_API - INCHIGEN_Reset(HGen, pGenData, pResults); -#else - STDINCHIGEN_Reset(HGen, pGenData, pResults); -#endif - - } /* while ( !e_bInterrupted ) */ - - - - - if (e_bInterrupted) - { - inchi_ios_eprint( log_stream, "\nStructure %d could not be processed: User Quit.\n", num_inp+1 ); - num_err ++; - goto exit_function; - } - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -exit_function: - - inchi_ios_eprint( log_stream, "\nProcessed %ld structure%s, %ld error%s.\n", - num_inp, (num_inp==1)?"":"s", num_err, (num_err==1)?"":"s" ); - - elapsed-= time(NULL); - inchi_ios_eprint( log_stream,"\nElapsed time: %ld\"\n", -elapsed); - - inchi_ios_flush2(log_stream,stdout); - - - - /*^^^ Freeing/closing */ - e_FreeInchi_Input( pInp ); - -#ifndef USE_STDINCHI_API - INCHIGEN_Destroy(HGen); -#else - STDINCHIGEN_Destroy(HGen); -#endif - -#if( ADD_CMLPP == 1 ) - /* BILLY 8/6/04 */ - /* free CML memory */ - FreeCml (); - FreeCmlDoc( 1 ); -#endif - for (i = 0; ipath[i] ) - { - e_inchi_free( (char*) ip->path[i] ); /* cast deliberately discards 'const' qualifier */ - ip->path[i] = NULL; - } - } - - inchi_ios_close(inp_stream); - inchi_ios_close(log_stream); - inchi_ios_close(out_stream); - inchi_ios_close(prb_stream); - - return 0; -} - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Local helper. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -int e_MakeOutputHeader( char *pSdfLabel, char *pSdfValue, long lSdfId, long num_inp, char *pStr1, char *pStr2 ) -{ - int tot_len1 = 0, tot_len2 = 0; - pStr1[0] = '\0'; - if ( !(pSdfLabel && pSdfLabel[0]) && !(pSdfValue && pSdfValue[0]) ) - { - tot_len1 = sprintf( pStr1, "Structure: %ld", num_inp ); - tot_len2 = sprintf( pStr2, "structure #%ld", num_inp ); - } - else - { - tot_len1 = sprintf( pStr1, "Structure: %ld.%s%s%s%s", - num_inp, SDF_LBL_VAL(pSdfLabel, pSdfValue) ); - - tot_len2 = sprintf( pStr2, "structure #%ld.%s%s%s%s", - num_inp, SDF_LBL_VAL(pSdfLabel, pSdfValue) ); - if (lSdfId) - { - tot_len1 += sprintf( pStr1 + tot_len1, ":%ld", lSdfId ); - tot_len2 += sprintf( pStr2 + tot_len2, ":%ld", lSdfId ); - } - } - return tot_len1; -} -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Local helper. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -char *e_GetChiralFlagString( int bChiralFlagOn ) -{ - static char szChiralFlag[64]; - szChiralFlag[0] = ' '; - szChiralFlag[1] = INCHI_OPTION_PREFX; - sprintf( szChiralFlag+2, "ChiralFlag%s", bChiralFlagOn? "On":"Off" ); - return szChiralFlag; -} - - - - -#endif /* CREATE_INCHI_STEP_BY_STEP */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Demo caller using modularized library interface (see main() below) + +Activated if (CREATE_INCHI_STEP_BY_STEP==1) [e_mode.h] + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ + + + + +#if( defined( WIN32 ) && defined( _CONSOLE ) && defined(_MSC_VER) && _MSC_VER >= 800 ) +#define ADD_WIN_CTLC /* detect Ctrl-C under Win-32 and Microsoft Visual C ++ */ +#endif + +#if( defined( WIN32 ) && defined( _CONSOLE ) && defined(_MSC_VER) && _MSC_VER >= 800 && defined(ADD_WIN_CTLC) && !(defined(__STDC__) && __STDC__ == 1) ) +#include +#endif + + +/* #define CREATE_0D_PARITIES */ /* uncomment to replace coordinates and 2D-parirties with 0D-parities */ +/* in case of CREATE_0D_PARITIES, the hardcoded bFixSp3Bug = 1 fixes sp3 bugs in original InChI v. 1.00 */ +/* in case of CREATE_0D_PARITIES, the Phosphine and Arsine sp3 stereo options are not supported */ + +#define NEIGH_ONLY_ONCE /* comment out to include each bond in both neighboring atoms adjacency lists */ + + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "e_mode.h" +#ifdef CREATE_INCHI_STEP_BY_STEP + +#include "e_ctl_data.h" + +#include "../../../../INCHI_BASE/src/inchi_api.h" + + +#include "e_inchi_atom.h" +#include "e_ichi_parms.h" +#include "e_util.h" +#include "e_ichi_io.h" +#include "e_ichierr.h" +#include "e_readstru.h" +#include "e_ichicomp.h" +#ifdef CREATE_0D_PARITIES +#include "e_0dstereo.h" +#endif + + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Local prototypes. +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +int e_MakeOutputHeader(char *pSdfLabel, char *pSdfValue, long lSdfId, long num_inp, char *pStr1, char *pStr2); +char *e_GetChiralFlagString( int bChiralFlagOn ); + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Console-specific +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ + +/*^^^ Ctrl+C trap; works under Win32 + MS VC++ */ +int e_bInterrupted = 0; +int stop = 0; +#if( defined( _WIN32 ) && defined( _CONSOLE ) ) +#if( defined(_MSC_VER) && _MSC_VER >= 800 && defined(ADD_WIN_CTLC) && !(defined(__STDC__) && __STDC__ == 1) ) +BOOL WINAPI MyHandlerRoutine(DWORD dwCtrlType) /* control signal type */ +{ + if ( dwCtrlType == CTRL_C_EVENT || + dwCtrlType == CTRL_BREAK_EVENT || + dwCtrlType == CTRL_CLOSE_EVENT || + dwCtrlType == CTRL_LOGOFF_EVENT ) { + e_bInterrupted = 1; + return TRUE; + } + return FALSE; +} +#endif +int e_WasInterrupted(void) +{ +#ifdef _DEBUG + if ( e_bInterrupted ) stop=1; /* for debug only */ +#endif + return e_bInterrupted; +} + +#endif + + +#define EXIT_ON_ERR + + + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +main(): caller program demonstrating 'another InChI library interface' + and InChIKey calculation + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ + +int main( int argc, char *argv[ ] ) +{ +#define nStrLen 256 +int bReleaseVersion = bRELEASE_VERSION; + +char szSdfDataValue[MAX_SDF_VALUE+1]; +char szInchiCmdLine[512]; +char pStrInchiId[nStrLen], pStrLogId[nStrLen]; +const char *p1, *p2; + + +int retcode= 0, retcode1 = 0, i, k, tot_len; +int inp_index, out_index; +int nStructNo; + +long lSdfId; +long num_inp, num_err, num_output; + +INPUT_PARMS inp_parms, *ip = &inp_parms; +STRUCT_DATA struct_data, *sd = &struct_data; + + +INCHI_IOSTREAM outputstr, logstr, prbstr, instr; +INCHI_IOSTREAM *out_stream=&outputstr, *log_stream=&logstr, *prb_stream=&prbstr, *inp_stream=&instr; + +inchi_Input inchi_inp, *pInp = &inchi_inp; +inchi_Output genout, *pResults = &genout; +INCHIGEN_DATA inchi_gendata, *pGenData = &inchi_gendata; +INCHIGEN_HANDLE HGen = NULL; + +char *pinfo; +int len_mess=0, len_mess1, mshift=0, was_print = 0; +char ik_string[256]; /*^^^ Resulting InChIKey string */ +int ik_ret=0; /*^^^ InChIKey-calc result code */ +int xhash1, xhash2; +char szXtra1[256], szXtra2[256]; + +unsigned long ulDisplTime = 0; /* infinite, milliseconds */ +time_t elapsed; +/*^^^ Post-1.02b - moved from below */ +int bTabbed=0; + + + +/*^^^ Set debug output */ + +#if (TRACE_MEMORY_LEAKS == 1) + + _CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF); + /* for execution outside of the VC++ debugger uncomment one of the following two */ + /*#define MY_REPORT_FILE _CRTDBG_FILE_STDERR */ + /*#define MY_REPORT_FILE _CRTDBG_FILE_STDOUT */ +#ifdef MY_REPORT_FILE + _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_WARN, MY_REPORT_FILE ); + _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ERROR, MY_REPORT_FILE ); + _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ASSERT, MY_REPORT_FILE ); +#else + _CrtSetReportMode(_CRT_WARN | _CRT_ERROR, _CRTDBG_MODE_DEBUG); +#endif + /* turn on floating point exceptions */ +#if ( !defined(__STDC__) || __STDC__ != 1 ) + { + /* Get the default control word. */ + int cw = _controlfp( 0,0 ); + + /* Set the exception masks OFF, turn exceptions on. */ + /*cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_INEXACT|EM_ZERODIVIDE|EM_DENORMAL);*/ + cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE|EM_DENORMAL); + + /* Set the control word. */ + _controlfp( cw, MCW_EM ); + } +#endif + +#endif /* #if (TRACE_MEMORY_LEAKS == 1) */ + + + + +/*^^^ Set Ctrl+C trap under Win32 + MS VC++ */ + +#if( defined( _WIN32 ) && defined( _CONSOLE ) && defined(_MSC_VER) && _MSC_VER >= 800 && defined(ADD_WIN_CTLC) && !(defined(__STDC__) && __STDC__ == 1)) + if ( SetConsoleCtrlHandler( MyHandlerRoutine, 1 ) ) + { + ; /*ConsoleQuit = WasInterrupted*/; + } +#endif + + + + num_inp = 0; + num_err = 0; + num_output = 0; + + elapsed = time(NULL); + + /*^^^ Set original input structure */ + memset(pInp, 0, sizeof(*pInp)); + memset(pResults, 0, sizeof(*pResults)); + memset(pGenData, 0, sizeof(*pGenData)); + memset(szSdfDataValue, 0, sizeof(szSdfDataValue)); + + /*^^^ Initialize I/O streams as string buffers so that they may be filled within dll; + also associate files with the streams and use inchi_ios_flush to flush buffer content */ + + inchi_ios_init(log_stream, INCHI_IOSTREAM_TYPE_STRING, NULL); + inchi_ios_init(inp_stream, INCHI_IOSTREAM_TYPE_STRING, NULL); + inchi_ios_init(out_stream, INCHI_IOSTREAM_TYPE_STRING, NULL); + inchi_ios_init(prb_stream, INCHI_IOSTREAM_TYPE_STRING, NULL); + + + /*^^^ Check command line */ + if ( argc < 2 || argc==2 && ( argv[1][0]==INCHI_OPTION_PREFX ) && + (!strcmp(argv[1]+1, "?") || !e_inchi_stricmp(argv[1]+1, "help") ) ) + { + /* e_HelpCommandLineParms(stdout); */ + e_HelpCommandLineParmsReduced(log_stream); + log_stream->f = stderr; + inchi_ios_flush(log_stream); + return 0; + } + + if ( 0 > e_ReadCommandLineParms( argc, (const char **)argv, ip, szSdfDataValue, &ulDisplTime, bReleaseVersion, log_stream ) ) + /*^^^ Explicitly cast to (const char **) to avoid a + warning about "incompatible pointer type":*/ + { + + goto exit_function; + } + + + /*^^^ Open files associated with I/O streams. */ + if ( !e_OpenFiles( &(inp_stream->f), &(out_stream->f), &(log_stream->f), &(prb_stream->f), ip ) ) + goto exit_function; + + + + /*^^^ Create InChI generator object. */ +#ifndef USE_STDINCHI_API + HGen = INCHIGEN_Create(); +#else + HGen = STDINCHIGEN_Create(); +#endif + + if (NULL==HGen) + { + /*^^^ Probably, out of RAM. Could do nothing. */ + inchi_fprintf( stderr, "Could not create InChI generator (out of RAM?)\n" ); + goto exit_function; + } + + + + + /*^^^ Set input labelling opts */ + if ( ip->bNoStructLabels ) + { + ip->pSdfLabel = NULL; + ip->pSdfValue = NULL; + } + else + if ( ip->nInputType == INPUT_INCHI_PLAIN || + ip->nInputType == INPUT_INCHI_XML || + ip->nInputType == INPUT_CMLFILE ) + { + /* the input may contain both the header and the label of the structure */ + if ( !ip->pSdfLabel ) ip->pSdfLabel = ip->szSdfDataHeader; + if ( !ip->pSdfValue ) ip->pSdfValue = szSdfDataValue; + } + e_PrintInputParms( log_stream, ip ); + inchi_ios_flush2(log_stream,stdout); + pStrInchiId[0] = '\0'; + + + + + /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Main cycle: read input structures and create their InChI + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ + + out_index = 0; + + + while (!e_bInterrupted) + { + + int bHasTimeout = 0; + if ( ip->last_struct_number && num_inp >= ip->last_struct_number ) + { + retcode = _IS_EOF; /* simulate end of file */ + goto exit_function; + } + + /*^^^ Create command line */ + szInchiCmdLine[0] = '\0'; + for ( i = 1; i < argc; i ++ ) + { + if ( argv[i] && INCHI_OPTION_PREFX == argv[i][0] && argv[i][1] ) + { + /*^^^ Omit certain options */ + if ( !e_inchi_memicmp( argv[i]+1, "start:", 6) || + !e_inchi_memicmp( argv[i]+1, "end:", 4) || + !e_inchi_stricmp( argv[i]+1, "Tabbed" ) + ) + { + continue; + } + + if ( !e_inchi_stricmp( argv[i]+1, "Inchi2Inchi" ) ) + { + inchi_ios_eprint( log_stream, "\nOption Inchi2Inchi is not supported (use classic interface). \n" ); + goto exit_function; + } + else if ( !e_inchi_stricmp( argv[i]+1, "Inchi2Struct" ) ) + { + inchi_ios_eprint( log_stream, "\nOption Inchi2Struct is not supported (use classic interface). \n" ); + goto exit_function; + } + + + if ( !e_inchi_memicmp( argv[i]+1, "w", 1 ) && isdigit( UCINT argv[i][2] ) ) + { + bHasTimeout = 1; + } + /*^^^ Add option to szInchiCmdLine */ + if ( strlen(szInchiCmdLine)+strlen(argv[i]) + 4 < sizeof(szInchiCmdLine) ) + { + if ( szInchiCmdLine[0] ) + strcat( szInchiCmdLine, " " ); + k = ( !(k=strcspn( argv[i], " \t" )) || argv[i][k] ); /* k means enclose in "" */ + if (k) + strcat( szInchiCmdLine, "\"" ); + strcat( szInchiCmdLine, argv[i] ); + if (k) + strcat( szInchiCmdLine, "\"" ); + } else + { + inchi_fprintf( stderr, "Too many options. Option \"%s\" ignored\n", argv[i] ); + } + } + } + if ( !bHasTimeout ) + { + /*^^^ Add default timeout option -W60: 60 seconds */ + char szW60[] = " ?W60"; + szW60[1] = INCHI_OPTION_PREFX; + if ( strlen(szInchiCmdLine) + strlen( szW60 ) < sizeof(szInchiCmdLine) ) + strcat( szInchiCmdLine, szW60 ); + else + inchi_fprintf( stderr, "Too many options. Option \"%s\" ignored\n", szW60 ); + } + /*^^^ End of command line deal */ + + + if ( ip->nInputType==INPUT_INCHI_PLAIN ) + { + inchi_ios_eprint( log_stream, "\nRestoring InChI from AuxInfo is not supported (use classic interface). \n" ); + goto exit_function; + } + + + /*^^^ Skip input cycle */ + while(!e_bInterrupted) + { + inp_index = out_index; + + + /*^^^ Read one structure from input */ + e_FreeInchi_Input( pInp ); + + + + retcode = e_ReadStructure( sd, ip, inp_stream, log_stream, out_stream, prb_stream, pInp, num_inp+1, + /* for CML:*/ inp_index, &out_index ); + + inchi_ios_flush2(log_stream,stdout); + + if ( _IS_SKIP != retcode) + { + lSdfId = ( ip->bGetSdfileId )? ip->lSdfId : 0; /* if requested then CAS r.n. otherwise struct. number*/ + nStructNo = ( ip->lMolfileNumber > 0 )? ip->lMolfileNumber : num_inp+1; + e_MakeOutputHeader( ip->pSdfLabel, ip->pSdfValue, lSdfId, nStructNo, pStrInchiId, pStrLogId ); + } + + /* e_ReadStructure() outputs the error/warning messages, so we do not need to re-output them here */ + switch (retcode) + { + case _IS_FATAL: + num_inp ++; + num_err ++; + goto exit_function; + case _IS_EOF: + inchi_ios_eprint( log_stream, "\rStructure %d could not be read: Detected end of file. \r", num_inp+1 ); + goto exit_function; + case _IS_ERROR: + num_inp ++; + num_err ++; + continue; + case _IS_SKIP: + num_inp ++; + continue; + } + break; + } + if ( e_bInterrupted ) + { + inchi_ios_eprint( log_stream, "\nStructure %d could not be read: User Quit.\n", num_inp+1 ); + inchi_ios_flush2(log_stream,stdout); + num_err ++; + goto exit_function; + } + + + /*^^^ Analyze the chiral flag */ + + p1 = NULL; +#ifndef USE_STDINCHI_API + if ( (ip->nMode & REQ_MODE_CHIR_FLG_STEREO) && (ip->nMode & REQ_MODE_STEREO) && + ( ip->bChiralFlag & (FLAG_SET_INP_AT_CHIRAL | FLAG_SET_INP_AT_NONCHIRAL) ) ) + ; /* cmd line has priority over the chiral flag in Molfile */ + + else if ( sd->bChiralFlag & FLAG_INP_AT_CHIRAL ) + p1 = e_GetChiralFlagString( 1 ); /* input file has chiral flag */ + + else if ( (ip->nMode & REQ_MODE_CHIR_FLG_STEREO) && (ip->nMode & REQ_MODE_STEREO) || + (sd->bChiralFlag & FLAG_INP_AT_NONCHIRAL) ) /* fix 04/05/2005 D.T.*/ + /* chiral flag requested (/SUCF) or input has non-chiral flag */ + p1 = e_GetChiralFlagString( 0 ); + + if (p1) + { + if ( strlen(szInchiCmdLine) + strlen( p1 ) < sizeof(szInchiCmdLine) ) + strcat( szInchiCmdLine, p1 ); + else + inchi_fprintf( stderr, "Too many options. Option \"%s\" ignored\n", p1 ); + } +#endif + pInp->szOptions = szInchiCmdLine; + + + /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + create INChI for each connected component of the structure and optionally + display them; output INChI for the whole structure. + Use compartmentalized library interface. + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ + +#ifdef CREATE_0D_PARITIES + if ( !pInp->stereo0D && !pInp->num_stereo0D ) + { + int bPointedEdgeStereo = (0 != (TG_FLAG_POINTED_EDGE_STEREO & ip->bTautFlags)); + set_0D_stereo_parities( pInp, bPointedEdgeStereo ); + Clear3D2Dstereo(pInp); + } +#endif +#ifdef NEIGH_ONLY_ONCE + e_RemoveRedundantNeighbors(pInp); +#endif + + len_mess = len_mess1 = 0; + pinfo = (char*) &(pGenData->pStrErrStruct); + was_print = 0; + + + + /*^^^ Set up */ + +#ifndef USE_STDINCHI_API + retcode = INCHIGEN_Setup( HGen, pGenData, pInp ); +#else + retcode = STDINCHIGEN_Setup( HGen, pGenData, pInp ); +#endif + + len_mess1 = strlen(pinfo); + if (len_mess1 > len_mess) + { + mshift = len_mess?1:0; + inchi_ios_eprint(log_stream, "*** %-s [Initialization] %-s\n", pStrLogId, pinfo+len_mess+mshift); + inchi_ios_flush2(log_stream,stdout); + len_mess = len_mess1; + was_print = 1; + } + + + + /*^^^ Normalization step */ + if ( (retcode == inchi_Ret_OKAY) || (retcode == inchi_Ret_WARNING) ) + { + +#ifndef USE_STDINCHI_API + retcode = INCHIGEN_DoNormalization(HGen, pGenData); +#else + retcode = STDINCHIGEN_DoNormalization(HGen, pGenData); +#endif + + len_mess1 = strlen(pinfo); + if (len_mess1 > len_mess) + { + mshift = len_mess?1:0; + inchi_ios_eprint(log_stream, "*** %-s [Normalization] %-s\n", pStrLogId, pinfo+len_mess+mshift); + inchi_ios_flush2(log_stream,stdout); + len_mess = len_mess1; + was_print = 1; + } + +#ifdef OUTPUT_NORMALIZATION_DATA + { + int ic, istruct, itaut, ia, ib, nc[2]; + NORM_ATOMS *inp_norm_data[TAUT_NUM]; /* = { &NormAtomsNontaut, &NormAtomsTaut}; */ + NORM_ATOM *atom; + nc[0] = pGenData->num_components[0]; + nc[1] = pGenData->num_components[1]; + inchi_ios_eprint(log_stream, "=== %-s Intermediate normalization data follow", pStrLogId); + for (istruct=0; istruct<2; istruct++) + { + /*^^^ Print results for 1) original/disconnected structure and + 2)reconnected structure */ + if (nc[istruct]>0) + { + if (istruct==0) + inchi_ios_eprint(log_stream, "\n\tOriginal/disconnected structure: "); + else + inchi_ios_eprint(log_stream, "\n\tReconnected structure: "); + inchi_ios_eprint(log_stream, "%-d component(s)\n", nc[istruct]); + inchi_ios_eprint(log_stream, "\t===============================================\n"); + + for (ic=0; ic < nc[istruct]; ic++) + { + /* Print results for each component of the structure */ + inp_norm_data[0] = &(pGenData->NormAtomsNontaut[istruct][ic]); + inp_norm_data[1] = &(pGenData->NormAtomsTaut[istruct][ic]); + for (itaut=0;itaut<2;itaut++) + { + /* Print results for 1) non-tautomeric and + 2) tautomeric version (if present) + of the component */ + if (NULL!=inp_norm_data[itaut]) + { + + if (inp_norm_data[itaut]->num_at>0) + { + if (itaut==0) + inchi_ios_eprint(log_stream, "\tComponent %-d, non-tautomeric:", ic+1); + else + inchi_ios_eprint(log_stream, "\tComponent %-d, tautomeric:", ic+1); + + inchi_ios_eprint(log_stream, "\t%-d atom(s)\n", inp_norm_data[itaut]->num_at); + + for (ia=0; ia< inp_norm_data[itaut]->num_at; ia++) + { + /*^^^ Print data for each atom */ + if (inp_norm_data[itaut]->at != NULL) + { + atom = &( inp_norm_data[itaut]->at[ia] ); + if (NULL!=atom) + { + /*^^^ Print: element, number, original number, no. of Hs, + charge, coordination number, valence */ + inchi_ios_eprint(log_stream, "\t\tatom %-s%-d (orig.%-s%-d) [H%-d] charge=%-+d CN=%-d val=%-d ", + atom->elname, ia+1, atom->elname, atom->orig_at_number, atom->num_H, atom->charge, + atom->valence, atom->chem_bonds_valence); + if (atom->valence > 0) + { + /*^^^ Neighbors */ + inchi_ios_eprint(log_stream, "nbrs { "); + for (ib=0; ib valence; ib++) + { + inchi_ios_eprint(log_stream, "%-d ", atom->neighbor[ib]+1); + } + inchi_ios_eprint(log_stream, "}"); + } + /* Indicate if atom shares Hs with others */ + if (atom->endpoint > 0) + { + inchi_ios_eprint(log_stream, "\n\t\t(in taut. group: %-d)", atom->endpoint); + } + inchi_ios_eprint(log_stream, "\n"); + } + } + } + } + } + } + } + } + } + } + inchi_ios_flush(log_stream); +#endif + } + + /*^^^ Canonicalization step */ + if ( (retcode == inchi_Ret_OKAY) || (retcode == inchi_Ret_WARNING) ) + { + +#ifndef USE_STDINCHI_API + retcode = INCHIGEN_DoCanonicalization( HGen, pGenData ); +#else + retcode = STDINCHIGEN_DoCanonicalization( HGen, pGenData ); +#endif + + len_mess1 = strlen(pinfo); + if (len_mess1 > len_mess) + { + mshift = len_mess?1:0; + inchi_ios_eprint(log_stream, "*** %-s [Canonicalization] %-s\n", pStrLogId, pinfo+len_mess+mshift); + inchi_ios_flush2(log_stream,stdout); + len_mess = len_mess1; + was_print = 1; + } + } + + /*^^^ Serialization (final) step */ + if ( (retcode == inchi_Ret_OKAY) || (retcode == inchi_Ret_WARNING) ) + { + +#ifndef USE_STDINCHI_API + retcode1 = INCHIGEN_DoSerialization(HGen, pGenData, pResults); +#else + retcode1 = STDINCHIGEN_DoSerialization(HGen, pGenData, pResults); +#endif + + retcode = inchi_max(retcode , retcode1); + + + len_mess1 = strlen(pinfo); + if (len_mess1 > len_mess) + { + mshift = len_mess?1:0; + inchi_ios_eprint(log_stream, "*** %-s [Serialization] %-s\n", pStrLogId, pinfo+len_mess+mshift); + inchi_ios_flush2(log_stream,stdout); + was_print = 1; + } + } + +/* + if (!was_print) + inchi_ios_eprint(log_stream, "*** %-s \r", pStrLogId); +*/ + + + /*^^^ Output err/warn */ + if ( pResults->szMessage && pResults->szMessage[0] ) { p1 = "; "; p2 = pResults->szMessage; } + else p1 = p2 = ""; + + + switch (retcode) + { + case inchi_Ret_UNKNOWN: + case inchi_Ret_FATAL: /* fatal processing error -- typically, memory allocation error */ + num_inp ++; + num_err ++; + inchi_ios_eprint( log_stream, "Fatal Error (No INChI%s%s) %s\n", p1, p2, pStrLogId ); + inchi_ios_eprint( log_stream, "Log start:---------------------\n%s\nLog end--------------------\n", pResults->szLog? pResults->szLog : "Log is missing" ); + /*^^^ Free InChI library memories */ +#ifndef USE_STDINCHI_API + INCHIGEN_Reset(HGen, pGenData, pResults); +#else + STDINCHIGEN_Reset(HGen, pGenData, pResults); +#endif + goto exit_function; + case inchi_Ret_EOF: /* typically, no input structure provided or help requested */ + /* output previous structure number and message */ + inchi_ios_eprint( log_stream, "End of file detected after structure %d\n", num_inp ); + goto exit_function; + case inchi_Ret_ERROR: + num_inp ++; + num_err ++; + inchi_ios_eprint( log_stream, "Error (No INChI%s%s) %s\n", p1, p2, pStrLogId ); + inchi_ios_flush2(log_stream,stdout); + /*^^^ Free InChI library memories */ +#ifndef USE_STDINCHI_API + INCHIGEN_Reset(HGen, pGenData, pResults); +#else + STDINCHIGEN_Reset(HGen, pGenData, pResults); +#endif + continue; + case inchi_Ret_SKIP: + num_inp ++; + inchi_ios_eprint( log_stream, "Skipped %s\n", pStrLogId ); + goto exit_function; + case inchi_Ret_OKAY: + break; + case inchi_Ret_WARNING: + /*^^^ Suppress warnings, we display them step by steps */ + /*^^^ + if ( p2 && p2[0] ) + inchi_ios_eprint( log_stream, "Warning (%s) %s\n", p2, pStrLogId ); + */ + break; /* ok */ + } + + num_inp ++; + tot_len = 0; + + /*^^^ Post-1.02b - here from below */ + bTabbed = 0 != ( ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT ); + + if ( pResults->szInChI && pResults->szInChI[0] ) + { + if (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY ) + { + /*^^^ output SDfile */ + char *start; + unsigned len; + int bAddLabel = 0; + /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + output a SDfile. pResults->szInChI contains Molfile ending with "$$$$\n" line. + Replace the 1st line with the structure number + Replace the last line with the SDfile header, label, and new "$$$$\n" line + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ + + + start = pResults->szInChI; + + /*^^^ 2. SDfile header and data: write zero to the 1st byte of + the last line "$$$$\n" to remove this line with purpose to relpace it */ + if ( ip->pSdfLabel && ip->pSdfLabel[0] && ip->pSdfValue && ip->pSdfValue[0] && + (len = strlen(start)) && len > 5 && '$' == start[len-5] && '\n' == start[len-6] ) { + start[len-5] = '\0'; + bAddLabel = 1; + } + + /*^^^ 3. Output the whole Molfile */ + inchi_ios_print( out_stream, "%s", start ); + if ( bAddLabel ) + { + inchi_ios_print( out_stream, "> <%s>\n%s\n\n$$$$\n", ip->pSdfLabel, ip->pSdfValue ); + } + + inchi_ios_flush(out_stream); + } /* if (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY ) */ + + else + + { + /*^^^ Print InChI */ + + int bAuxInfo = !( ip->bINChIOutputOptions & INCHI_OUT_ONLY_AUX_INFO ) && + pResults->szAuxInfo && pResults->szAuxInfo[0]; + + /*^^^ Post-1.02b - correctly treat tabbed output with InChIKey */ + int bAuxOrKey = bAuxInfo || ( ip->bCalcInChIHash != INCHIHASH_NONE ); + + const char *pLF = "\n"; + const char *pTAB = bTabbed? "\t" : pLF; + if ( !ip->bNoStructLabels ) + { + /* Print a previously created label string */ + inchi_ios_print(out_stream, "%s%s", pStrInchiId, pTAB); + } + + /*^^^ Print INChI Identifier */ + + /*^^^ Post-1.02b */ + inchi_ios_print(out_stream, "%s%s", pResults->szInChI, bAuxOrKey? pTAB : pLF); + /*^^^ Print INChI Aux Info */ + if ( bAuxInfo ) + inchi_ios_print(out_stream, "%s%s",pResults->szAuxInfo, ip->bCalcInChIHash? pTAB : pLF); + + inchi_ios_flush(out_stream); + } + } /* if ( pResults->szInChI && pResults->szInChI[0] ) */ + + + /*^^^ Calculate InChIKey */ + if ( ip->bCalcInChIHash != INCHIHASH_NONE ) + { + xhash1 = xhash2 = 0; + if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1 ) || + ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) + xhash1 = 1; + if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA2 ) || + ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) + xhash2 = 1; + + ik_ret = GetINCHIKeyFromINCHI(pResults->szInChI, xhash1, xhash2, + ik_string, szXtra1, szXtra2); + if (ik_ret==INCHIKEY_OK) + { + /* NB: correctly treat tabbed output with InChIKey & hash extensions */ + char csep = '\n'; + if ( ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT ) + csep = '\t'; + inchi_ios_print(out_stream, "InChIKey=%-s",ik_string); + if ( xhash1 ) + inchi_ios_print(out_stream, "%cXHash1=%-s",csep,szXtra1); + if ( xhash2 ) + inchi_ios_print(out_stream, "%cXHash2=%-s",csep,szXtra2); + inchi_ios_print(out_stream, "\n"); + } + else + { + /*^^^ Post-1.02b - add LF if output is tabbed and key generation failed */ + if (bTabbed) + inchi_ios_print(out_stream, "\n"); + + inchi_ios_eprint(log_stream, "Warning: could not compute InChIKey for #%-d ", + num_inp); + switch(ik_ret) + { + case INCHIKEY_UNKNOWN_ERROR: + inchi_ios_eprint(log_stream, "(invalid key length requested)\n"); + break; + case INCHIKEY_EMPTY_INPUT: + inchi_ios_eprint(log_stream, "(got an empty string)\n"); + break; + case INCHIKEY_INVALID_INCHI_PREFIX: + case INCHIKEY_INVALID_INCHI: + case INCHIKEY_INVALID_STD_INCHI: + inchi_ios_eprint(log_stream, "(got non-InChI string)\n"); + break; + case INCHIKEY_NOT_ENOUGH_MEMORY: + inchi_ios_eprint(log_stream, "(not enough memory to treat the string)\n"); + break; + default:inchi_ios_eprint(log_stream, "(internal program error)\n"); + break; + } + } + inchi_ios_flush(out_stream); + inchi_ios_flush2(log_stream,stdout); + } + + + + /*^^^ Free InChI library memories */ +#ifndef USE_STDINCHI_API + INCHIGEN_Reset(HGen, pGenData, pResults); +#else + STDINCHIGEN_Reset(HGen, pGenData, pResults); +#endif + } /* while ( !e_bInterrupted ) */ + + + + + if (e_bInterrupted) + { + inchi_ios_eprint( log_stream, "\nStructure %d could not be processed: User Quit.\n", num_inp+1 ); + num_err ++; + goto exit_function; + } + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +exit_function: + + inchi_ios_eprint( log_stream, "\nProcessed %ld structure%s, %ld error%s.\n", + num_inp, (num_inp==1)?"":"s", num_err, (num_err==1)?"":"s" ); + + elapsed-= time(NULL); + inchi_ios_eprint( log_stream,"\nElapsed time: %ld\"\n", -elapsed); + + inchi_ios_flush2(log_stream,stdout); + + + + /*^^^ Freeing/closing */ + e_FreeInchi_Input( pInp ); + +#ifndef USE_STDINCHI_API + INCHIGEN_Destroy(HGen); +#else + STDINCHIGEN_Destroy(HGen); +#endif + + for (i = 0; ipath[i] ) + { + e_inchi_free( (char*) ip->path[i] ); /* cast deliberately discards 'const' qualifier */ + ip->path[i] = NULL; + } + } + + inchi_ios_close(inp_stream); + inchi_ios_close(log_stream); + inchi_ios_close(out_stream); + inchi_ios_close(prb_stream); + + return 0; +} + + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Local helper. +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +int e_MakeOutputHeader( char *pSdfLabel, char *pSdfValue, long lSdfId, long num_inp, char *pStr1, char *pStr2 ) +{ + int tot_len1 = 0, tot_len2 = 0; + pStr1[0] = '\0'; + if ( !(pSdfLabel && pSdfLabel[0]) && !(pSdfValue && pSdfValue[0]) ) + { + tot_len1 = sprintf( pStr1, "Structure: %ld", num_inp ); + tot_len2 = sprintf( pStr2, "structure #%ld", num_inp ); + } + else + { + tot_len1 = sprintf( pStr1, "Structure: %ld.%s%s%s%s", + num_inp, SDF_LBL_VAL(pSdfLabel, pSdfValue) ); + + tot_len2 = sprintf( pStr2, "structure #%ld.%s%s%s%s", + num_inp, SDF_LBL_VAL(pSdfLabel, pSdfValue) ); + if (lSdfId) + { + tot_len1 += sprintf( pStr1 + tot_len1, ":%ld", lSdfId ); + tot_len2 += sprintf( pStr2 + tot_len2, ":%ld", lSdfId ); + } + } + return tot_len1; +} +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Local helper. +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +char *e_GetChiralFlagString( int bChiralFlagOn ) +{ + static char szChiralFlag[64]; + szChiralFlag[0] = ' '; + szChiralFlag[1] = INCHI_OPTION_PREFX; + sprintf( szChiralFlag+2, "ChiralFlag%s", bChiralFlagOn? "On":"Off" ); + return szChiralFlag; +} + + + + +#endif /* CREATE_INCHI_STEP_BY_STEP */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/e_ichisize.h b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichisize.h similarity index 65% rename from INCHI-1-SRC/INCHI_API/inchi_main/e_ichisize.h rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichisize.h index 4ee14da..068ce1a 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_main/e_ichisize.h +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichisize.h @@ -1,74 +1,73 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef ___INCHISIZE_H__ -#define ___INCHISIZE_H__ - - -typedef unsigned short AT_NUMB; -typedef unsigned short AT_RANK; -#define AT_RANK_MASK ((AT_RANK)~0) - -typedef signed short NUM_H; -#define MAX_ATOMS 1024 - -#define CHAR_MASK 0xFF - -typedef unsigned long INCHI_MODE; - -#define LEN_COORD 10 -#define NUM_COORD 3 -typedef char MOL_COORD[LEN_COORD*NUM_COORD + NUM_COORD-1]; /*copied 30 bytes from MOLfile */ - -typedef enum tagInputType { INPUT_NONE=0, INPUT_MOLFILE=1, INPUT_SDFILE=2, INPUT_INCHI_XML=3, INPUT_INCHI_PLAIN=4, INPUT_CMLFILE=5, INPUT_INCHI=6, INPUT_MAX } INPUT_TYPE; - -/* other types */ - -#define UCINT (int)(unsigned char) -#ifndef INCHI_US_CHAR_DEF -typedef signed char S_CHAR; -typedef unsigned char U_CHAR; -#define INCHI_US_CHAR_DEF -#endif - - - -#endif /* ___INCHISIZE_H__ */ - +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef ___INCHISIZE_H__ +#define ___INCHISIZE_H__ + + +typedef unsigned short AT_NUMB; +typedef unsigned short AT_RANK; +#define AT_RANK_MASK ((AT_RANK)~0) + +typedef signed short NUM_H; +#define MAX_ATOMS 32766 +#define NORMALLY_ALLOWED_INP_MAX_ATOMS 1024 + +#define CHAR_MASK 0xFF + +typedef unsigned long INCHI_MODE; + +#define LEN_COORD 10 +#define NUM_COORD 3 +typedef char MOL_COORD[LEN_COORD*NUM_COORD + NUM_COORD-1]; /*copied 30 bytes from MOLfile */ + +typedef enum tagInputType { INPUT_NONE=0, INPUT_MOLFILE=1, INPUT_SDFILE=2, INPUT_INCHI_XML=3, INPUT_INCHI_PLAIN=4, INPUT_CMLFILE=5, INPUT_INCHI=6, INPUT_MAX } INPUT_TYPE; + +/* other types */ + +#define UCINT (int)(unsigned char) +#ifndef INCHI_US_CHAR_DEF +typedef signed char S_CHAR; +typedef unsigned char U_CHAR; +#define INCHI_US_CHAR_DEF +#endif + + + +#endif /* ___INCHISIZE_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichitime.h b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichitime.h new file mode 100644 index 0000000..3112fd7 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_ichitime.h @@ -0,0 +1,102 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _E__ICHITIME_H_ +#define _E__ICHITIME_H_ + +#ifdef COMPILE_ANSI_ONLY + +#ifdef __FreeBSD__ +#include +#endif + +/* get times() */ +#ifdef INCHI_USETIMES +#include +#endif + +/*#include */ + +#include + +typedef struct e_tagInchiTime { + clock_t clockTime; +} e_inchiTime; + +#else + +/* Win32 _ftime(): */ +#include + +typedef struct e_tagInchiTime { + unsigned long clockTime; /* Time in seconds since midnight (00:00:00), January 1, 1970; + signed long overflow expected in 2038 */ + long millitime; /* milliseconds */ +} e_inchiTime; + +#endif + + +unsigned long e_ulMyGetTickCount( int bStart ); +unsigned long e_ulMyTickCountDiff( unsigned long ulTickEnd, unsigned long ulTickStart ); + +void e_inchiTimeGet( e_inchiTime *TickEnd ); +long e_inchiTimeMsecDiff( e_inchiTime *TickEnd, e_inchiTime *TickStart ); +void e_inchiTimeAddMsec( e_inchiTime *TickEnd, unsigned long nNumMsec ); +int bInchiTimeIsOver( e_inchiTime *TickEnd ); +long e_inchiTimeElapsed( e_inchiTime *TickStart ); + + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + + + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + + + +#endif /* _E__ICHITIME_H_ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/e_inchi_atom.c b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_inchi_atom.c similarity index 79% rename from INCHI-1-SRC/INCHI_API/inchi_main/e_inchi_atom.c rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_inchi_atom.c index 6e45b2c..a9b22a8 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_main/e_inchi_atom.c +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_inchi_atom.c @@ -1,118 +1,120 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - -#include "e_mode.h" - -#include "inchi_api.h" - -#include "e_inchi_atom.h" -#include "e_ichisize.h" -#include "e_util.h" -/******************************************************************************************************/ -void e_FreeInchi_Atom( inchi_Atom **at ) -{ - if ( at && *at ) { - e_inchi_free( *at ); - *at = NULL; - } -} -/******************************************************************************************************/ -void e_FreeInchi_Stereo0D( inchi_Stereo0D **stereo0D ) -{ - if ( stereo0D && *stereo0D ) { - e_inchi_free( *stereo0D ); - *stereo0D = NULL; - } -} -/******************************************************************************************************/ -inchi_Atom *e_CreateInchi_Atom( int num_atoms ) -{ - inchi_Atom *p = (inchi_Atom* ) e_inchi_calloc(num_atoms, sizeof(inchi_Atom) ); - return p; -} -/******************************************************************************************************/ -inchi_Stereo0D *e_CreateInchi_Stereo0D( int num_stereo0D ) -{ - return (inchi_Stereo0D* ) e_inchi_calloc(num_stereo0D, sizeof(inchi_Stereo0D) ); -} - - - -/******************************************************************************************************/ -void e_FreeInchi_Input( inchi_Input *inp_at_data ) -{ - e_FreeInchi_Atom( &inp_at_data->atom ); - e_FreeInchi_Stereo0D( &inp_at_data->stereo0D ); - memset( inp_at_data, 0, sizeof(*inp_at_data) ); -} - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -/* e_RemoveRedundantNeighbors: after calling it, the 'neighbor' in atomic - data for any atom 'i' will contain only atoms whose numbers < i */ -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -int e_RemoveRedundantNeighbors( inchi_Input *inp_at_data ) -{ - int i, j, k; - inchi_Atom *at = inp_at_data->atom; - int num_atoms = inp_at_data->num_atoms; - for ( i = 0; i < num_atoms; i ++ ) { - for ( j = k = 0; j < at[i].num_bonds; j ++ ) { - if ( at[i].neighbor[j] < i ) { - at[i].neighbor[k] = at[i].neighbor[j]; - at[i].bond_type[k] = at[i].bond_type[j]; - at[i].bond_stereo[k] = at[i].bond_stereo[j]; - k ++; - } - } - for ( j = k; j < at[i].num_bonds; j ++ ) { - at[i].neighbor[j] = 0; - at[i].bond_type[j] = 0; - at[i].bond_stereo[j] = 0; - } - at[i].num_bonds = k; - } - return 0; -} +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include +#include + +#include "e_mode.h" + +#include "../../../../INCHI_BASE/src/inchi_api.h" + +#include "e_inchi_atom.h" +#include "e_ichisize.h" +#include "e_util.h" + + + +/******************************************************************************************************/ +void e_FreeInchi_Atom( inchi_Atom **at ) +{ + if ( at && *at ) { + e_inchi_free( *at ); + *at = NULL; + } +} +/******************************************************************************************************/ +void e_FreeInchi_Stereo0D( inchi_Stereo0D **stereo0D ) +{ + if ( stereo0D && *stereo0D ) { + e_inchi_free( *stereo0D ); + *stereo0D = NULL; + } +} +/******************************************************************************************************/ +inchi_Atom *e_CreateInchi_Atom( int num_atoms ) +{ + inchi_Atom *p = (inchi_Atom* ) e_inchi_calloc(num_atoms, sizeof(inchi_Atom) ); + return p; +} +/******************************************************************************************************/ +inchi_Stereo0D *e_CreateInchi_Stereo0D( int num_stereo0D ) +{ + return (inchi_Stereo0D* ) e_inchi_calloc(num_stereo0D, sizeof(inchi_Stereo0D) ); +} + + + +/******************************************************************************************************/ +void e_FreeInchi_Input( inchi_Input *inp_at_data ) +{ + e_FreeInchi_Atom( &inp_at_data->atom ); + e_FreeInchi_Stereo0D( &inp_at_data->stereo0D ); + memset( inp_at_data, 0, sizeof(*inp_at_data) ); +} + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +/* e_RemoveRedundantNeighbors: after calling it, the 'neighbor' in atomic + data for any atom 'i' will contain only atoms whose numbers < i */ +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +int e_RemoveRedundantNeighbors( inchi_Input *inp_at_data ) +{ + int i, j, k; + inchi_Atom *at = inp_at_data->atom; + int num_atoms = inp_at_data->num_atoms; + for ( i = 0; i < num_atoms; i ++ ) { + for ( j = k = 0; j < at[i].num_bonds; j ++ ) { + if ( at[i].neighbor[j] < i ) { + at[i].neighbor[k] = at[i].neighbor[j]; + at[i].bond_type[k] = at[i].bond_type[j]; + at[i].bond_stereo[k] = at[i].bond_stereo[j]; + k ++; + } + } + for ( j = k; j < at[i].num_bonds; j ++ ) { + at[i].neighbor[j] = 0; + at[i].bond_type[j] = 0; + at[i].bond_stereo[j] = 0; + } + at[i].num_bonds = k; + } + return 0; +} diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/e_inchi_atom.h b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_inchi_atom.h similarity index 62% rename from INCHI-1-SRC/INCHI_API/inchi_main/e_inchi_atom.h rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_inchi_atom.h index e088133..6e6c267 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_main/e_inchi_atom.h +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_inchi_atom.h @@ -1,64 +1,63 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHI_ATOM_H__ -#define __INCHI_ATOM_H__ - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -void e_FreeInchi_Atom( inchi_Atom **at ); -void e_FreeInchi_Stereo0D( inchi_Stereo0D **stereo0D ); -inchi_Atom *e_CreateInchi_Atom( int num_atoms ); -inchi_Stereo0D *e_CreateInchi_Stereo0D( int num_stereo0D ); -void e_FreeInchi_Input( inchi_Input *inp_at_data ); -int e_RemoveRedundantNeighbors( inchi_Input *inp_at_data ); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /* __INCHI_ATOM_H__ */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef __INCHI_ATOM_H__ +#define __INCHI_ATOM_H__ + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + +void e_FreeInchi_Atom( inchi_Atom **at ); +void e_FreeInchi_Stereo0D( inchi_Stereo0D **stereo0D ); +inchi_Atom *e_CreateInchi_Atom( int num_atoms ); +inchi_Stereo0D *e_CreateInchi_Stereo0D( int num_stereo0D ); +void e_FreeInchi_Input( inchi_Input *inp_at_data ); +int e_RemoveRedundantNeighbors( inchi_Input *inp_at_data ); + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + + +#endif /* __INCHI_ATOM_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/e_inpdef.h b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_inpdef.h similarity index 70% rename from INCHI-1-SRC/INCHI_API/inchi_main/e_inpdef.h rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_inpdef.h index bdff541..8f1e854 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_main/e_inpdef.h +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_inpdef.h @@ -1,72 +1,71 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -/* input/output format */ -#ifndef __INPDEF_H__ -#define __INPDEF_H__ - - -/* BILLY 8/6/04 */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -int e_MolfileToInchi_Input( FILE *inp_molfile, inchi_Input *orig_at_data, int bMergeAllInputStructures, - int bDoNotAddH, int bAllowEmptyStructure, - const char *pSdfLabel, char *pSdfValue, long *lSdfId, - long *lMolfileNumber, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ); -int e_INChIToInchi_Input( INCHI_IOSTREAM *inp_molfile, inchi_Input *orig_at_data, int bMergeAllInputStructures, - int bDoNotAddH, int vABParityUnknown, INPUT_TYPE nInputType, - char *pSdfLabel, char *pSdfValue, long *lSdfId, INCHI_MODE *pInpAtomFlags, - int *err, char *pStrErr ); - -int e_CopyMOLfile(FILE *inp_file, long fPtrStart, long fPtrEnd, FILE *prb_file, long nNumb); - - -/* BILLY 8/6/04 */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -#endif /* __INPDEF_H__ */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +/* input/output format */ +#ifndef __INPDEF_H__ +#define __INPDEF_H__ + + +/* BILLY 8/6/04 */ +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + +int e_MolfileToInchi_Input( FILE *inp_molfile, inchi_Input *orig_at_data, int bMergeAllInputStructures, + int bDoNotAddH, int bAllowEmptyStructure, + const char *pSdfLabel, char *pSdfValue, long *lSdfId, + long *lMolfileNumber, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ); +int e_INChIToInchi_Input( INCHI_IOSTREAM *inp_molfile, inchi_Input *orig_at_data, int bMergeAllInputStructures, + int bDoNotAddH, int vABParityUnknown, INPUT_TYPE nInputType, + char *pSdfLabel, char *pSdfValue, long *lSdfId, INCHI_MODE *pInpAtomFlags, + int *err, char *pStrErr ); + +int e_CopyMOLfile(FILE *inp_file, long fPtrStart, long fPtrEnd, FILE *prb_file, long nNumb); + + +/* BILLY 8/6/04 */ +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + +#endif /* __INPDEF_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_main.h b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_main.h new file mode 100644 index 0000000..bee104f --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_main.h @@ -0,0 +1,46 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _ICE_MAIN_H_ +#define _ICE_MAIN_H_ + + + + +#endif /* _ICE_MAIN_H_ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/e_mode.h b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_mode.h similarity index 91% rename from INCHI-1-SRC/INCHI_API/inchi_main/e_mode.h rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_mode.h index 0c4b910..8cc16d6 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_main/e_mode.h +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_mode.h @@ -1,1073 +1,1069 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __MODE_H__ -#define __MODE_H__ - -#include - - - - -/*******************/ -/* */ -/* BUILD TARGETS */ -/* */ -/*******************/ - -/* Valid targets are: - -TARGET_EXE_STANDALONE - Stand-alone executable inchi-1[.exe] -TARGET_API_LIB - Library (libinchi) for using InChI API described in inchi_api.h -TARGET_EXE_USING_API - Executable (INCHI_MAIN) which uses API library (e.g., libinchi.dll) -TARGET_LIB_FOR_WINCHI - library for wInChI - - Select and uncomment one from the list below. */ - - -/* #define TARGET_EXE_STANDALONE 1 */ -/* #define TARGET_API_LIB */ -#define TARGET_EXE_USING_API -/* #define TARGET_LIB_FOR_WINCHI 1 */ - - -/****************************/ -/* */ -/* BUILD OPTIONS/FEATURES */ -/* */ -/****************************/ - -/* Possible options are: - -BUILD_LINK_AS_DLL - Link library as a Win32 DLL or to eliminate stricmp duplication - (use with TARGET_API_LIB or TARGET_EXE_USING_API) - -BUILD_WITH_ENG_OPTIONS - Expose engineering options - -BUILD_WITH_AMI - Turns on AMI (Allow Multiple Inputs) mode for standalone executable - - Select and uncomment whichever are necessary from the list below. */ - - -/* #define BUILD_LINK_AS_DLL */ - -/* #define BUILD_WITH_ENG_OPTIONS 1 */ - -#ifndef BUILD_WITH_AMI -/* this allows BUILD_WITH_AMI be #defined in a makefile */ -/* #define BUILD_WITH_AMI 1 */ -#endif -/* NB: AMI mode is only for stand-alone executable */ -#ifndef TARGET_EXE_STANDALONE -#ifdef BUILD_WITH_AMI -#undef BUILD_WITH_AMI -#endif -#endif - - - -/* CML input is not supported started from v. 1.04 */ -/* set ADD_CMLPPP to zero to override possble makefile define */ -#define ADD_CMLPP 0 -#if 0 /* obsolete */ -#ifndef ADD_CMLPP -/* this allows ADD_CMLPP be #defined in a makefile */ -#define ADD_CMLPP 1 -#endif -#if ( ADD_CMLPP == 1 ) -#ifdef USE_CMLPPDLL -/* 1200 is VC++ 6.0 version, 1300 is VC++ .NET; USE_CMLPPDLL may be #defined in a makefile*/ -#if ( defined(_WIN32) && defined(_MSC_VER) && _MSC_VER >= 1200 ) -#define MSC_DELAY_LOAD_CMLPPDLL -#endif -#endif -#endif -#endif - - - - -/*****************************/ -/* */ -/* COMPILE OPTIONS/FEATURES */ -/* */ -/*****************************/ - -/* Possible options are: - -COMPILE_ANSI_ONLY - Unconditionally force ANSI-89 C, no Win32 specific code - -COMPILE_ADD_NON_ANSI_FUNCTIONS - Use with COMPILE_ANSI_ONLY to add stricmp(), etc., see util.c - -COMPILE_ALL_CPP - allow C++ compilation/linkage of functions prototyped in .h files - -MS VC compiler pragmas - - Select and uncomment whichever are necessary from the list below. */ - - -#define COMPILE_ANSI_ONLY -#if ( !defined(_MSC_VER) || defined(TARGET_API_LIB)) /* non-Microsoft GNU C, BCC, etc. compilers */ -#ifndef COMPILE_ANSI_ONLY -#define COMPILE_ANSI_ONLY -#endif -#endif -#ifdef COMPILE_ANSI_ONLY -/*#define COMPILE_ADD_NON_ANSI_FUNCTIONS */ -#endif - -/* #define COMPILE_ALL_CPP */ - -#ifdef _MSC_VER -/* -========== disable MS VC++ 6.0 Level 4 compiler warnings: ============== - C4706: assignment within conditional expression - C4127: conditional expression is constant - C4244: '=' : conversion from 'int ' to '???', possible loss of data - C4267: '=' : conversion from 'size_t' to 'int', possible loss of data - C4701: local variable '???' may be used without having been initialized (removed) - C4514: unreferenced inline/local function has been removed (C++) - C4100: 'identifier' : unreferenced formal parameter - C4786: 'identifier' : identifier was truncated to 'number' characters in the debug information - C4996: 'identifier' was declared deprecated -======================================================================== -*/ - #pragma warning( disable : 4706 4127 4514 4100 4786 4996 4244 4267 ) -#endif - - - -/* Comment out the next line to use old (classic) library interface */ -/*#define CREATE_INCHI_STEP_BY_STEP 1*/ -#ifdef CREATE_INCHI_STEP_BY_STEP -/* Uncomment next line to output intermediate (normalization) results */ -/*#define OUTPUT_NORMALIZATION_DATA 1*/ -#endif -/* Uncomment next line to go with STDINCHI API calls */ -/* #define USE_STDINCHI_API 1*/ - -#ifndef CREATE_INCHI_STEP_BY_STEP -#define TARGET_ID_STRING ", Software version 1.04 (Library call example; classic interface) Build of September 9, 2011" -#else -#define TARGET_ID_STRING ", Software version 1.04 (Library call example; modularized interface) Build of September 9, 2011" -#endif - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - - - -/*********************/ -/* */ -/* INCHI ALGORITHM */ -/* */ -/*********************/ - -#define INCHI_VERSION "1" - -#if 0 /* obsolete */ -/*#define INCHI_VERSION "0.9Beta" */ -/*#define INCHI_VERSION "0.91Beta" */ /* 10-10-2002: sent to Jonathan Goodman */ -/*#define INCHI_VERSION "0.92Beta" */ /* 11-15-2002: added Hill notation; sent to S.Heller & S.Stein */ -/*#define INCHI_VERSION "0.93Beta" */ /* 12-09-2002: Fixed isotopic canon. bug & chiralanes; sent to S.Heller & A. McNaught */ -/*#define INCHI_VERSION "0.931Beta" */ /* Non-BNS without salts released to PMR 01-2003 */ -/*#define INCHI_VERSION "0.932Beta" */ /* Released to CAS 04-01-2003: - * - Improved taut. definitions as compared to 01-2003; - * - fixed bug: non-isotopic components' stereo missing from isotopic stereo - * - fixed bug: couldn't properly read Unix files (EOL = LF instead of CR/LF) - * (effective only for MS VC++, Borland and MinGW/GCC compiles that accept "rb" mode of fopen) - * DJGPP/GCC does not seem to need this fix. - */ -/*==== Release version ===*/ -/*#define INCHI_VERSION "0.94Beta" */ /* 02-27-2003: Balanced network search to find alt paths and non-stereo bonds; - Implemented salts disconnection; added (-) to taut groups */ -/*#define INCHI_VERSION "1.12Beta" */ /* 1.12: 07-06-2004: sort order: No H formula,..; Pointed end stereo ON, Aggressive (de)protonation OFF */ - /* 1.11: 05-19-2004: annotated plain text output, fixed bugs */ - /* 1.1: 04-08-2004: variable protonation version */ - /* 1.01: 12-23-2003 protected bonds, isotopic canonicalization in GetBaseCanonRanking() */ - /* 1.02: 01-26-2004 fixed new isotopic tgroup canon bug, molfile merge bug */ - -/*#define INCHI_VERSION "1.0RC"*/ /* 02-07-2005 v1.0 Release Candidate */ -#endif - -#define INCHI_NAME "InChI" -#if 0 /* obsolete */ -#define INCHI_REC_NAME "ReChI" -#endif - -#define INCHI_NAM_VER_DELIM "=" - -#ifdef _WIN32 -#define INCHI_OPTION_PREFX '/' -#define INCHI_PATH_DELIM '\\' -#else -#define INCHI_OPTION_PREFX '-' -#define INCHI_PATH_DELIM '/' -#endif - -#define INCHI_ALT_OPT_PREFIX '-' -#define INCHI_ACD_LABS_PREFIX '-' - -#define bRELEASE_VERSION 1 /* 1=> release version; comment out to disable */ -#ifndef bRELEASE_VERSION -#define bRELEASE_VERSION 0 /* 0=> debug version */ -#endif - - -/* display (non-canonical) c-groups, display orig at numbers */ -#if ( bRELEASE_VERSION == 1 ) -#define DISPLAY_DEBUG_DATA_C_POINT 0 /* disabled release version for now */ -#define DISPLAY_ORIG_AT_NUMBERS 1 /* 1 => in an uncanonicalized components display orig. atom numbers (default) */ -#else -#define DISPLAY_DEBUG_DATA_C_POINT 1 /* debug: 1=>display (non-canonically numbered) c-groups, 0=>do not display */ -#define DISPLAY_ORIG_AT_NUMBERS 1 /* 0 => in an uncanonicalized components display ordering atom numbers (debug) */ -#endif - -#if ( DISPLAY_DEBUG_DATA_C_POINT > 0 ) -#define DISPLAY_DEBUG_DATA DISPLAY_DEBUG_DATA_C_POINT -#endif - - - -/* BUG FIXES */ - -/**************************/ -/* bug fixes in v1.00 */ -/**************************/ -#define FIX_ChCh_STEREO_CANON_BUG 1 /* 1=> (NEEDED) */ -#define ADD_ChCh_STEREO_CANON_CHK 0 /* 1 is NOT needed; let it always be 0 */ -#define FIX_ChCh_CONSTIT_CANON_BUG 1 /* 1=> (NEEDED) */ -#define FIX_EITHER_STEREO_IN_AUX_INFO 1 /* 1=> fix bug: Either stereobond direction in Aux_Info; 0=> do not fix */ -#define FIX_NORM_BUG_ADD_ION_PAIR 1 /* 1=> (NEEDED) fix bug: Miscount number of charges when creating an ion pair */ -#define FIX_REM_PROTON_COUNT_BUG 1 /* 1=> (NEEDED) check for number of actually removed protons and issue an error if mismatch */ -#define FIX_READ_AUX_MEM_LEAK 1 -#define FIX_READ_LONG_LINE_BUG 1 /* 1=> (NEEDED) prevent failure when reading AuxInfo and InChI is too long */ -#define FIX_N_V_METAL_BONDS_GPF 1 /* 1=> (NEEDED) InChI v1 GPF bug fix */ -#define BNS_RAD_SEARCH 1 /* 1=> prevent normalization failures due to radical centers */ - -/*******************************/ -/* bug fixes in post-v1.00 */ -/*******************************/ -#define FIX_ODD_THINGS_REM_Plus_BUG 0 -#define FIX_N_MINUS_NORN_BUG 0 -#define FIX_CANCEL_CHARGE_COUNT_BUG 0 -#define FIX_2D_STEREO_BORDER_CASE 0 -#define FIX_REM_ION_PAIRS_Si_BUG 0 -#define FIX_STEREO_SCALING_BUG 0 -#define FIX_EMPTY_LAYER_BUG 0 -#define FIX_EITHER_DB_AS_NONSTEREO 0 -#define FIX_BOND23_IN_TAUT 0 -#define FIX_TACN_POSSIBLE_BUG 0 -#define FIX_KEEP_H_ON_NH_ANION 0 -#define FIX_AVOID_ADP 0 -/* may change InChI */ -#define FIX_NUM_TG 0 /* increase number of t-groups for isothiocyanate */ -/* changes InChI for isothiocyanate */ -#define FIX_CPOINT_BOND_CAP2 0 - -/*******************************/ -/* bug fixes in post-v1.02b */ -/*******************************/ - -#define FIX_ISO_FIXEDH_BUG 1 /* (2007-09-24) 1=> Fix bug: missing fixed-H iso segment in case of single removed D(+) */ -#define FIX_ISO_FIXEDH_BUG_READ 0 /* (2007-09-24) 1=> Accommodate this InChI bug in reading InChI */ -#define FIX_DALKE_BUGS 1 -#define FIX_TRANSPOSITION_CHARGE_BUG 1 /* (2008-01-02) fix bug that leads to missed charge in some cases when /o is present */ -#define FIX_I2I_STEREOCONVERSION_BUG 1 /* (2008-03-06) 1=> Fix bug of i2i conversion SAbs-->(SRel||Srac) */ -#define FIX_I2I_STEREOCONVERSION_BUG2 1 /* (2008-04-02) 1=> Fix bug of i2i conversion (missed empty /t) */ -#define FIX_I2I_STEREOCONVERSION_BUG3 1 /* (2008-04-10) 1=> Fix bug of i2i conversion */ - /* (missed repeating /s in FI after F for multi-component case) */ -#define FIX_TERM_H_CHRG_BUG 1 /* (2008-06-06) IPl) */ - /* fix bug: in some cases (dependent on ordering - numbers), moving a charge from terminal H to heavy - atom resulted in neutralizing H but not adjusting - charge of heavy atom */ - - -#if ( !defined(TARGET_API_LIB) && !defined(TARGET_EXE_USING_API) ) -#define I2S_MODIFY_OUTPUT 1 /* 1=> Allow various InChI2InChI output types from cInChI */ -#else -#define I2S_MODIFY_OUTPUT 0 /* 0=> Always */ -#endif - - -#define FIX_NP_MINUS_BUG 1 /* 2010-03-11 DCh */ - -/**************************/ -/* additions to v1.00 */ -/**************************/ -#define FIX_ADJ_RAD 0 - -#define SDF_OUTPUT_V2000 1 /* 1=>always output V2000 SDfile, 0=>only if needed */ -#define SDF_OUTPUT_DT 1 /* 1=> all option -SdfAtomsDT to output D and T into SDfile */ -#define CHECK_AROMBOND2ALT 1 /* 1=> check whether arom->alt bond conversion succeeded */ - -#ifdef TARGET_LIB_FOR_WINCHI -#define READ_INCHI_STRING 0 /* 1=> input InChI string and process it */ -#else -#define READ_INCHI_STRING 1 /* 1=> input InChI string and process it */ -#endif - -/****************************************************/ -/* disabled extra external calls to InChI algorithm */ -/****************************************************/ -#define INCLUDE_NORMALIZATION_ENTRY_POINT 0 - -/**************************/ -/* Normalization settings */ -/**************************/ - -/* post version 1 features */ -#define KETO_ENOL_TAUT 1 /* include keto-enol tautomerism */ -#define TAUT_15_NON_RING 1 /* 1,5 tautomerism with endpoints not in ring */ - -/* v.1.04 : still experimental but may be exposed (set to 1) */ -#define UNDERIVATIZE 0 /* split to possible underivatized fragments */ -#define RING2CHAIN 0 /* open rings R-C(-OH)-O-R => R-C(=O) OH-R */ - -/* post-2004-04-27 features */ -#define HAL_ACID_H_XCHG 1 /* allow iso H exchange to HX (X=halogen) and H2Y (Y=halcogen) */ -#define CANON_FIXH_TRANS 1 /* produce canonical fixed-H transposition */ -#define STEREO_WEDGE_ONLY 1 /* 1=> only pointed ends stereo bonds define stereo; 0=> both ends */ - -/* current new (with respect to v1.12 Beta) preprocessing */ -#define REMOVE_ION_PAIRS_EARLY 1 /* 1=> new preprocessing: step 1 before disconnecting metals in fix_odd_things() */ -#define REMOVE_ION_PAIRS_DISC_STRU 1 /* 1=> new post-preprocessing: remove charhes after metal disconnection */ -#define REMOVE_ION_PAIRS_FIX_BONDS 1 /* 1=> step2: set unchangeable bonds around removed ion pairs */ -#define S_VI_O_PLUS_METAL_FIX_BOND 1 /* 1=> count double bond M-O(+)=S as O=S in S(VI) ans S(VIII) fixing bonds */ -#define N_V_STEREOBONDS 1 /* 1=> detect stereobonds incident to N(V); 0 => don't */ -/* for testing */ -#define REMOVE_ION_PAIRS_ORIG_STRU 0 /* 0=> normal mode (default) - * 1=> testing mode only: remove ion pairs from the original structure - * to save the changes in the output Molfile (/OutputSDF) or AuxInfo - * NIP=No Ion Pairs - */ -/* salts treatment */ -#define DISCONNECT_SALTS 1 /* 1=>disconnect metal atoms from salts, 0=>dont */ -#define TEST_REMOVE_S_ATOMS 1 /* 1=>default: after merging into one group test & - * remove unreachable, - * 0=> old version: test only before merging into one t-group */ -#define CHARGED_SALTS_ONLY 1 /* 1=>(default)do not test far salts tautomerism if - * no negative charge(s) present */ -#define BNS_PROTECT_FROM_TAUT 1 /* 1=> do not allow testing of bonds to acetyl or nitro */ -#define BNS_MARK_EDGE_2_DISCONNECT 1 /* 1=> mark edge as temp forbidden instead of disconnection */ - -#define REPLACE_ALT_WITH_TAUT 1 /* 1 => replace alt bonds with tautomeric bonds in case of standard t-groups */ -#define MOVE_CHARGES 1 /* 1 => take moveable charges into account */ -#define NEUTRALIZE_ENDPOINTS 1 /* 1 => before checking whether an H is moveable make 2 endpoints neutral */ - /* implemented only if CHECK_TG_ALT_PATH = 0, defined in ichi_bns.c */ -#define FIX_H_CHECKING_TAUT 1 /* 1 => Fix moveable H or (-) before checking if taut. exchange is possible */ -#define ALWAYS_ADD_TG_ON_THE_FLY 1 /* 1 => disables radical calcellation by taut-charge movement */ -#define IGNORE_SINGLE_ENDPOINTS 1 /* 1 => see FindAccessibleEndPoints() in INChITaut.c */ - -/* recently added -- begin */ -#define INCL_NON_SALT_CANDIDATATES 1 /* 1=> allow H and (-) migrate between "acidic" O and - * other possible endpoints */ -#define SALT_WITH_PROTONS 1 /* 1=> (new new) include proton migrarion C-SH, =C-OH, NH+ */ -#define OPPOSITE_CHARGE_IN_CGROUP 1 /* 1=> allow N(-) in (+) c-group, 0=> disallow */ -#define MOVE_PPLUS_TO_REMOVE_PROTONS 0 /* 0=> default; 1=> (disabled) add P/P+ charge group during - * 'hard' proton removal */ -#define ADD_MOVEABLE_O_PLUS 1 /* 1=> allow charges on O(+) to move */ -/* recently added -- end */ - -#define DISCONNECT_METALS 1 /* make main layer disconnected */ -#define RECONNECT_METALS 0 /* 1=> by default add reconnected layer in case of coord. - * compound disconnection */ -#define CHECK_METAL_VALENCE 0 /* 1=> disconnect only metals that have abnormal valence */ -#define bREUSE_INCHI 1 /* 1=> do not recalulate INChI for components in reconnected - * structure that are same as in the connected one */ -#define OUTPUT_CONNECTED_METAL_ONLY 0 /* 0=> default; 1 => (debug) create only reconnected or - * initial struct. output */ -#define EMBED_REC_METALS_INCHI 1 /* 1=> (default) output Reconnected embedded in Disconnected INChI; - * 0=> separate output */ - -#define bOUTPUT_ONE_STRUCT_TIME 1 /* 1 => output each structure time (non-release only) */ - - - -/* constants and array sizes */ - -#define INCHI_NUM 2 /* = array size; member indexes: */ -#define INCHI_BAS 0 /* 0 => disconnected or normal */ -#define INCHI_REC 1 /* 1 => reconnected */ - -#define TAUT_NUM 2 /* = array size; member indexes: */ -#define TAUT_NON 0 /* 0 => normal structure */ -#define TAUT_YES 1 /* 1 => tautomeric */ -#define TAUT_INI 2 /* 2 => intermediate tautomeric structure */ -#define ALT_TAUT(X) ((X)>TAUT_YES? TAUT_YES : 1-(X)) /* was (1-(X)) */ - -/* INChI output modes */ -#define OUT_N1 0 /* non-tautomeric only */ -#define OUT_T1 1 /* tautomeric if present otherwise non-tautomeric */ -#define OUT_NT 2 /* only non-taut representations of tautomeric */ -#define OUT_TN 3 /* tautomeric if present otherwise non-tautomeric; - separately output non-taut representations of tautomeric if present */ -#define OUT_NN 4 /* only non-taut representations: non-taut else tautomeric */ - -/* OUT_TN = OUT_T1 + OUT_NT */ - -/* torture test */ - -#define TEST_RENUMB_ATOMS 0 /* 1 => heavy duty test by multiple renumbering of atoms */ -#define TEST_RENUMB_NEIGH 1 /* 1 => randomly permutate neighbors */ -#define TEST_RENUMB_SWITCH 0 /* 1 => display & output another (different) picture */ -#define TEST_RENUMB_ATOMS_SAVE_LONGEST 0 /* 1 => save the component with largest processing time into the problem file */ - - -/* stereo */ - -#define NEW_STEREOCENTER_CHECK 1 /* 1 => add new stereocenter categories (see bCanInpAtomBeAStereoCenter(...)) */ -#define MIN_SB_RING_SIZE 8 /* do not assume stereo bonds in rings containing 3..MIN_SB_RING_SIZE-1 atoms */ - -#define REMOVE_KNOWN_NONSTEREO 1 /* 1=> check in advance known stereo to remove parities from non-stereogenic elements */ -#define REMOVE_CALC_NONSTEREO 1 /* 1=> check new stereo numberings to remove parities from non-stereogenic elements */ -#define PROPAGATE_ILL_DEF_STEREO 1 /* 1=> if at least one of the pair of constitutionally identical (far) neighbors */ - /* (of the tested atom) has ill-defined stereo parity and another has any */ - /* stereo parity then set the parity of the tested atom to ill-defined value. */ - -#define ONLY_DOUBLE_BOND_STEREO 0 /* 1=> no alt bond stereo, no taut. bond attachment to stereo bond */ - /* 0=> allow other definitions (below) to be active */ -#define ONE_BAD_SB_NEIGHBOR 1 /* 1 => allow 1 "bad" bond type neighbor to a stereobond atom. 2004-06-02 */ - -/* more stereo settings */ -#define BREAK_ONE_MORE_SC_TIE 1 /* break one more tie when comparing possible stereocenter neighbors */ -#define BREAK_ALSO_NEIGH_TIE 0 /* post 1.12Beta 2004-08-20: if fixed neighbor has equ neighbors, fix the one with smaller canon. rank */ -#define BREAK_ALSO_NEIGH_TIE_ROTATE 1 /* post 1.12Beta 2004-09-02: break the second in 2nd psition; 1 works, 0 does not (example:MFCD01085607) */ - -#define STEREO_CENTER_BONDS_NORM 1 /* set length of the bonds around a stereocenter = 1 before getting the parity */ -#define STEREO_CENTER_BOND4_NORM 0 /* set length of the added bond around a stereocenter = 1 before getting the parity */ -#define NORMALIZE_INP_COORD 0 /* 0=>keep unchanged, 1 => make atom coordinates integer values, avg bond len=20 */ - -/* recent stereo */ -#define STEREO_WEDGE_ONLY 1 /* 1=> only pointed ends stereo bonds define stereo; 0=> both ends 1.12Beta */ -#define CHECK_C2v_S4_SYMM 0 /* post-1.12Beta 1=> check if a stereocenter has C2v or S4 symmetry; 0=>old mode */ - -#define EQL_H_NUM_TOGETHER 1 /* 1=> output 1-3,5H2 intead of 1-3H2,5H2 (CT_MODE_EQL_H_TOGETHER) */ -#define ABC_CT_NUM_CLOSURES 1 /* 1=> in coinnections compressed format output decimal number of closures instead of '-' */ - -/* temporary fix */ -#define SINGLET_IS_TRIPLET 1 /* 'singlet' means two electrons make a lone pair instead of 2 bonds - its effect on valence is same as the effect of a triplet */ - -/* defug: find structures where canonical partition is different from equitable */ -#define FIND_CANON_NE_EQUITABLE 0 /* 0=>normal mode */ - /* 1=> extract (set EXTR_FLAGS = (EXTR_CANON_NE_EQUITABLE)*/ - /* set cmd line options: /onlynonTAUT /: /UNCHARGEDACIDS:1 /DISCONSALT:0 /MOVEPOS:0 /DISCONMETAL:0 */ - -/* Debug: definitions for the extraction of the structures to the problem file */ - -/* definition of the flags for structure extraction to the - problem file (for debugging and non-standard searching) */ -#define EXTR_KNOWN_USED_TO_REMOVE_PARITY 0x000001 -#define EXTR_CALC_USED_TO_REMOVE_PARITY 0x000002 -#define EXTR_2EQL2CENTER_TO_REMOVE_PARITY 0x000004 -#define EXTR_HAS_ATOM_WITH_DEFINED_PARITY 0x000008 -#define EXTR_REMOVE_PARITY_WARNING 0x000010 -#define EXTR_SALT_WAS_DISCONNECTED 0x000020 -#define EXTR_SALT_PROTON_MOVED 0x000040 -#define EXTR_SALT_PROTON_MOVE_ERR_WARN 0x000080 -#define EXTR_METAL_WAS_DISCONNECTED 0x000100 -#define EXTR_METAL_WAS_NOT_DISCONNECTED 0x000200 -#define EXTR_NON_TRIVIAL_STEREO 0x000400 /* (Inv != Abs stereo) && (parities can't be obtained by inverting them) */ -#define EXTR_UNUSUAL_VALENCES 0x000800 -#define EXTR_HAS_METAL_ATOM 0x001000 -#define EXTR_TEST_TAUT3_SALTS_DONE 0x002000 /* non-oxygen t-points used to discover tautomerism of merged t-groups */ -#define EXTR_CANON_NE_EQUITABLE 0x004000 /* find structures where canonical partition is different from equitable */ -#define EXTR_HAS_PROTON_PN 0x008000 /* has movable H+ attached to N or P */ -#define EXTR_HAS_FEATURE 0x010000 /* found a feature */ -#define EXTR_TAUT_TREATMENT_CHARGES 0x020000 /* tautomeric treatment of charges */ -#define EXTR_TRANSPOSITION_EXAMPLES 0x040000 /* extract structures that have different mobile-H and fixed-H orders */ - -/* define conditions of structure extraction to the problem file */ -#define EXTR_MASK 0 /*EXTR_TAUT_TREATMENT_CHARGES*/ /*(EXTR_HAS_FEATURE)*/ /*(EXTR_UNUSUAL_VALENCES | EXTR_HAS_METAL_ATOM)*/ /* 0 to disable */ -#define EXTR_FLAGS 0 /*EXTR_TAUT_TREATMENT_CHARGES*/ /*(EXTR_HAS_FEATURE)*/ /*(EXTR_HAS_PROTON_PN)*/ /*(EXTR_UNUSUAL_VALENCES)*/ /*(EXTR_CANON_NE_EQUITABLE)*/ /*(EXTR_TEST_TAUT3_SALTS_DONE)*/ /*(EXTR_HAS_METAL_ATOM)*/ /* (EXTR_NON_TRIVIAL_STEREO)*/ /*(EXTR_METAL_WAS_DISCONNECTED)*/ /* (EXTR_REMOVE_PARITY_WARNING)*/ /*(EXTR_HAS_ATOM_WITH_DEFINED_PARITY) */ - - -#define ENTITY_REFS_IN_XML_MESSAGES 1 /* 1=> replace ' " < > & in error/warning messages with xml entity references */ - -/* added tautomeric structures */ - -#define TAUT_TROPOLONE_7 1 /* 1=> tautomeric 7-member rings ON */ -#define TAUT_TROPOLONE_5 1 /* 1=> taut. similar to tropolone, 5-member ring */ -#define TAUT_4PYRIDINOL_RINGS 1 /* 1=> OH-C5H4N rings tautomerism */ -#define TAUT_PYRAZOLE_RINGS 1 /* 1=> tautomerizm in pyrazole rings */ -/* limitation on tautomerism detection: */ -#define TAUT_IGNORE_EQL_ENDPOINTS 0 /* 0=> even though 2 endpoints belong to same t-group check - them to find more alt bonds (new) - 1=> ignore and do not check (old mode) */ -#define TAUT_RINGS_ATTACH_CHAIN 1 /* 1=> allow only chain attachments to tautomeric endpoints */ - /* (except pyrazole, where is no tautomeric attachment) */ - /* 0=> allow taut. attachments from same ring system. Default=1 */ - -#define FIND_RING_SYSTEMS 1 /* 1 => find and mark ring systems, blocks, cut-vertices */ - /* Needed for 5- and 6-member ring tautomers and in other places */ - -#define FIND_RINS_SYSTEMS_DISTANCES 0 /* 1 => find ring system and atom distance from terminal */ -#define USE_DISTANCES_FOR_RANKING 0 /* 1 => rank ring systems according to distances from terminal */ - -#define DISPLAY_RING_SYSTEMS 0 /* 1 => for debug only; displays: */ - /* "block no"/"ring system no"/"cut-vertex (num. intersecting blocks-1)" */ - /* instead of ranks */ -/* consistency */ - -#if ( bRELEASE_VERSION==1 && bOUTPUT_ONE_STRUCT_TIME==1) -#undef bOUTPUT_ONE_STRUCT_TIME -#define bOUTPUT_ONE_STRUCT_TIME 0 -#endif - -/* consistency: bRELEASE_VERSION==1 needs FIND_RING_SYSTEMS=1 */ -#if ( bRELEASE_VERSION==1 && FIND_RING_SYSTEMS!=1 ) -#ifdef FIND_RING_SYSTEMS -#undef FIND_RING_SYSTEMS -#endif -#define FIND_RING_SYSTEMS 1 -#endif - -/* consistency: FIND_RINS_SYSTEMS_DISTANCES needs FIND_RING_SYSTEMS */ -#if ( FIND_RING_SYSTEMS != 1 ) - -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) -#undef FIND_RINS_SYSTEMS_DISTANCES -#define FIND_RINS_SYSTEMS_DISTANCES 0 -#endif - -#endif - -/* consistency: USE_DISTANCES_FOR_RANKING and DISPLAY_RING_SYSTEMS need FIND_RINS_SYSTEMS_DISTANCES */ -#if ( FIND_RINS_SYSTEMS_DISTANCES != 1 ) - -#if ( USE_DISTANCES_FOR_RANKING == 1 ) -#undef USE_DISTANCES_FOR_RANKING -#define USE_DISTANCES_FOR_RANKING 0 -#endif - -#if ( DISPLAY_RING_SYSTEMS == 1 ) -#undef DISPLAY_RING_SYSTEMS -#define DISPLAY_RING_SYSTEMS 0 -#endif - -#endif - - -#if ( FIND_RING_SYSTEMS==1 && (TAUT_TROPOLONE_7==1 || TAUT_TROPOLONE_5==1 || TAUT_4PYRIDINOL_RINGS==1 || TAUT_PYRAZOLE_RINGS) ) -#define TAUT_OTHER 1 -#else -#define TAUT_OTHER 0 -#endif - -#define APPLY_IMPLICIT_H_DOWN_RULE 0 /* 1=> if 3 non-H atoms around stereocenter are in same plane */ - /* then add "down" hydrogen to obtain sterecenter oparity */ - /* 0=> Implicit H stereo is unknown if all bonds to 3 non-H atoms */ - /* are in XY plane */ -#define ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS 1 /* 1=> consider bond in an alternating circuit stereogenic */ - /* even though it has adjacent tautomeric atom(s) */ - -#define IGNORE_TGROUP_WITHOUT_H 1 /* ignore tautomeric groups containing charges only */ - -#if ( DISCONNECT_SALTS == 1 ) -#define REMOVE_TGROUP_CHARGE 0 /* 0: do not remove charge information from tautomeric groups */ -#else -#define REMOVE_TGROUP_CHARGE 1 /* 1: remove charge information from tautomeric groups */ -#endif - -#if ( REMOVE_TGROUP_CHARGE == 1 ) -#define INCHI_T_NUM_MOVABLE 1 -#else -#define INCHI_T_NUM_MOVABLE 2 -#endif - -/******************************************/ -/* define canonicalization modes here */ -/******************************************/ - -#define USE_AUX_RANKING 1 /* 1=> get auxiliary ranking to accelerate canonicalization of H layers */ -#define USE_AUX_RANKING_ALL 1 /* 1=> include all vertices in CellGetMinNode() selection 0=> only vertices with highest ranks */ - -#define USE_ISO_SORT_KEY_HFIXED 0 /* 0=> normal mode: merge isotopic taut H to isotopic atom sorting key in - taut H-fixed canonicalization; - 1=> add one more "string" iso_sort_Hfixed to the canonicalization */ - -/************************ - questionable behavior - ************************/ -#define REL_RAC_STEREO_IGN_1_SC 0 /* 1=> drop from InChI sp3 stereo in components that have a single stereocenter */ - /* 0=> old-old mode (all such sp3 stereo is in the Identifier) */ -/* internal definitions; see also REQ_MODE_BASIC etc in ichi.h */ -#define CMODE_CT 0x000001 -#define CMODE_ISO 0x000002 -#define CMODE_ISO_OUT 0x000004 /* obsolete ? */ -#define CMODE_STEREO 0x000008 -#define CMODE_ISO_STEREO 0x000010 -#define CMODE_TAUT 0x000020 -#define CMODE_NOEQ_STEREO 0x000040 /* 5-24-2002: do not use stereo equivalence to accelerate */ -#define CMODE_REDNDNT_STEREO 0x000080 /* 6-11-2002: do not check for redundant stereo elements */ -#define CMODE_NO_ALT_SBONDS 0x000100 /* 6-14-2002: do not assign stereo to alternating bonds */ -/* new 10-10-2003 */ -#define CMODE_RELATIVE_STEREO 0x000200 /* REL All Relative Stereo */ -#define CMODE_RACEMIC_STEREO 0x000400 /* RAC All Racemic Stereo */ -#define CMODE_SC_IGN_ALL_UU 0x000800 /* IAUSC Ignore stereocenters if All Undef/Unknown */ -#define CMODE_SB_IGN_ALL_UU 0x001000 /* IAUSC Ignore stereobonds if All Undef/Unknown */ -/* end of 10-10-2003 */ - -/* external definitions */ -#define CANON_MODE_CT (CMODE_CT) -#define CANON_MODE_TAUT (CMODE_CT|CMODE_TAUT) -#define CANON_MODE_ISO (CMODE_CT|CMODE_ISO|CMODE_ISO_OUT) -#define CANON_MODE_STEREO (CMODE_CT|CMODE_STEREO) -#define CANON_MODE_ISO_STEREO (CMODE_CT|CMODE_ISO|CMODE_ISO_OUT|CMODE_ISO_STEREO) - -#define CANON_MODE_MASK 0x00FF /* used to determine canonicalization mode */ - -/************************************************* - * from d_norm.c - */ - -/* implemented definitions for CT_ATOMID */ -#define CT_ATOMID_DONTINCLUDE 1 -#define CT_ATOMID_IS_INITRANK 2 -#define CT_ATOMID_IS_CURRANK 3 - -/*************************************** - * canonicalization settings I - ***************************************/ - -#define CANON_TAUTOMERS 1 /* 1=> process tautomers */ -#define HYDROGENS_IN_INIT_RANKS 1 /* 1=> include num_H in initial ranking */ - -#define DOUBLE_BOND_NEIGH_LIST 0 /* 1 => include double bond neighbor in NeighList 2 times */ -#define INCL_NON_6AROM 1 /* 1 => mark all arom. bonds; 0=>mark arom. bonds only in 6-member rings */ - -#define CT_SMALLEST /* minimal CT */ - -#define CT_NEIGH_SMALLER /* in CT, include neighbors with smaller ranks */ - -#define CT_ATOMID CT_ATOMID_IS_CURRANK /*CT_ATOMID_DONTINCLUDE */ - -#define CT_NEIGH_INCREASE /* in CT, neighbors ranks increase */ - -#define USE_SYMMETRY_TO_ACCELERATE 1 /*1 => for fast CT canonicalization, to avoid full enumeration */ - -/* dependent definitions due to settings */ - -#ifdef CT_SMALLEST -#define CT_GREATER_THAN > -#define CT_INITVALUE ~0 -#define BEST_PARITY 1 /* odd */ -#define WORSE_PARITY 2 -#else -#define CT_GREATER_THAN < -#define CT_INITVALUE 0 -#define BEST_PARITY 2 /* even */ -#define WORSE_PARITY 1 -#endif - -#ifdef CT_NEIGH_SMALLER -#define CT_NEIGH_SMALLER_THAN < -#else -#define CT_NEIGH_SMALLER_THAN > -#endif - -/* verify corectness of dependent settings */ -#if !defined( CT_ATOMID ) - #error You have to #define CT_ATOMID -#else -#if ( defined( CT_ATOMID ) && CT_ATOMID==CT_ATOMID_DONTINCLUDE ) - #error CT_DELIMITER should be #defined if CT_ATOMID is not included -#endif -#endif - -/*************************************** - * canonicalization settings II - ***************************************/ -/* from extr_ct.h */ -#define ALL_ALT_AS_AROMATIC 1 /* 1 => all altrnate bonds (even in cyclooctateraene) treat as aromatic */ - /* and set DOUBLE_BOND_NEIGH_LIST = 0 */ -#define ANY_ATOM_IN_ALT_CYCLE 1 /* 1=> accept any atom in alternating bond circuit, 0=>only some */ - -#define EXCL_ALL_AROM_BOND_PARITY 0 /* 1 => any arom atom cannot belong to stereo bond. */ - /* This has presedence over ADD_6MEMB_AROM_BOND_PARITY=1 */ - /* 0 => include arom bonds parities according to */ - /* ADD_6MEMB_AROM_BOND_PARITY definition */ - -#if ( EXCL_ALL_AROM_BOND_PARITY == 0 ) -#define ADD_6MEMB_AROM_BOND_PARITY 1 /* 1 => all arom bonds are stereo bonds */ - /* 0 => only those arom bonds which do not belong to */ - /* 6-member arom rings are stereo bonds */ -#else -#define ADD_6MEMB_AROM_BOND_PARITY 0 /* 0 => standard; 1 => meaningless: ignore parities of non-6-member ring alt. bonds */ -#endif - -#define CML_NUM_AT_IN_ATREF4 4 -#define MAX_NUM_STEREO_BONDS 3 -#define MAX_NUM_STEREO_BOND_NEIGH 3 -#define MIN_NUM_STEREO_BOND_NEIGH 2 - -#define MAX_NUM_STEREO_ATOM_NEIGH 4 -#define STEREO_AT_MARK 8 /* > MAX_NUM_STEREO_BONDS */ - -#if ( ONLY_DOUBLE_BOND_STEREO == 1 ) /* { */ - -#ifdef ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS -#undef ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS -#define ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS 0 -#endif - -#ifdef EXCL_ALL_AROM_BOND_PARITY -#undef EXCL_ALL_AROM_BOND_PARITY -#define EXCL_ALL_AROM_BOND_PARITY 1 -#endif - -#ifdef ADD_6MEMB_AROM_BOND_PARITY -#undef ADD_6MEMB_AROM_BOND_PARITY -#define ADD_6MEMB_AROM_BOND_PARITY 0 -#endif - -#endif /* } ONLY_DOUBLE_BOND_STEREO */ - -/* dependent definitions due to settings */ -#if ( ALL_ALT_AS_AROMATIC == 1 && DOUBLE_BOND_NEIGH_LIST != 0 ) -#undef DOUBLE_BOND_NEIGH_LIST -#define DOUBLE_BOND_NEIGH_LIST 0 -#endif - - -/************************************* - * Drawing - */ - -#define DRAW_AROM_TAUT 1 /* 1=> draw distinct aromatic & tautomer bonds, 0=> don't */ - -/******************************************************/ -/* C O M M O N D E F I N I T I O N S */ -/******************************************************/ - - -/* input bTautFlags flags */ -#define TG_FLAG_TEST_TAUT__ATOMS 0x00000001 /* find regular tautomerism */ -#define TG_FLAG_DISCONNECT_SALTS 0x00000002 /* DISCONNECT_SALTS disconnect */ -#define TG_FLAG_TEST_TAUT__SALTS 0x00000004 /* DISCONNECT_SALTS if possible find long-range H/(-) taut. on =C-OH, >C=O */ -#define TG_FLAG_MOVE_POS_CHARGES 0x00000008 /* MOVE_CHARGES allow long-range movement of N(+), P(+) charges */ -#define TG_FLAG_TEST_TAUT2_SALTS 0x00000010 /* TEST_REMOVE_S_ATOMS multi-attachement long-range H/(-) taut. on =C-OH, >C=O */ -#define TG_FLAG_ALLOW_NO_NEGTV_O 0x00000020 /* CHARGED_SALTS_ONLY=0 (debug) find long-range H-only tautomerism on =C-OH, >C=O */ -#define TG_FLAG_MERGE_TAUT_SALTS 0x00000040 /* DISCONNECT_SALTS merge all "salt"-t-groups and other =C-OH into one t-group */ - -#define TG_FLAG_ALL_TAUTOMERIC (TG_FLAG_TEST_TAUT__ATOMS| \ - TG_FLAG_TEST_TAUT__SALTS| \ - TG_FLAG_TEST_TAUT2_SALTS| \ - TG_FLAG_MERGE_TAUT_SALTS) - -#define TG_FLAG_DISCONNECT_COORD 0x00000080 /* find "coord. centers" and disconnect them */ -#define TG_FLAG_RECONNECT_COORD 0x00000100 /* reconnect disconnected "coord. centers" */ -#define TG_FLAG_CHECK_VALENCE_COORD 0x00000200 /* do not disconnect "coord. centers" with usual valence */ -#define TG_FLAG_MOVE_HPLUS2NEUTR 0x00000400 /* move protons to neutralize */ -#define TG_FLAG_VARIABLE_PROTONS 0x00000800 /* add/remove protons to neutralize */ -#define TG_FLAG_HARD_ADD_REM_PROTONS 0x00001000 /* add/remove protons to neutralize in hard way */ -#define TG_FLAG_POINTED_EDGE_STEREO 0x00002000 /* only pointed edge of stereo bond defines stereo */ -#if ( FIX_ADJ_RAD == 1 ) -#define TG_FLAG_FIX_ADJ_RADICALS 0x00004000 /* remove adjacent radical-doubletes, fix valence */ -#endif -#define TG_FLAG_PHOSPHINE_STEREO 0x00008000 /* add phosphine sp3 stereo */ -#define TG_FLAG_ARSINE_STEREO 0x00010000 /* add arsine sp3 stereo */ -#define TG_FLAG_H_ALREADY_REMOVED 0x00020000 /* processing structure restored from InChI */ -#define TG_FLAG_FIX_SP3_BUG 0x00040000 /* fix sp3 stereo bug: overlapping 2D stereo bond & coordinate scaling */ - -#define TG_FLAG_KETO_ENOL_TAUT 0x00080000 /* turn on keto-enol tautomerism detection */ -#define TG_FLAG_1_5_TAUT 0x00100000 /* turn on 1,5 tautomerism detection */ - -/*^^^ FB2 */ -#define TG_FLAG_FIX_ISO_FIXEDH_BUG 0x00200000 /* fix bug found after v.102b (isotopic H representation) */ -#define TG_FLAG_FIX_TERM_H_CHRG_BUG 0x00400000 /* fix bug found after v.102b (moving H charge in 'remove_terminal_HDT') */ - -/* output bTautFlags flags */ - -#define TG_FLAG_MOVE_HPLUS2NEUTR_DONE 0x00000001 /* protons have been moved to neutralize */ -#define TG_FLAG_TEST_TAUT__ATOMS_DONE 0x00000002 -#define TG_FLAG_DISCONNECT_SALTS_DONE 0x00000004 -#define TG_FLAG_TEST_TAUT__SALTS_DONE 0x00000008 /* multiple H tautomerism */ -#define TG_FLAG_MOVE_POS_CHARGES_DONE 0x00000010 -#define TG_FLAG_TEST_TAUT2_SALTS_DONE 0x00000020 /* merged t-groups */ -#define TG_FLAG_ALLOW_NO_NEGTV_O_DONE 0x00000040 -#define TG_FLAG_MERGE_TAUT_SALTS_DONE 0x00000080 /* added non-taut O to taut groups */ - -#define TG_FLAG_ALL_SALT_DONE (TG_FLAG_TEST_TAUT__SALTS_DONE | \ - TG_FLAG_TEST_TAUT2_SALTS_DONE | \ - TG_FLAG_MERGE_TAUT_SALTS_DONE ) - -#define TG_FLAG_DISCONNECT_COORD_DONE 0x00000100 /* found and disconnected "coord. centers" */ -#define TG_FLAG_CHECK_VALENCE_COORD_DONE 0x00000200 /* did not disconnect "coord. centers" with usual valence */ -#define TG_FLAG_MOVE_CHARGE_COORD_DONE 0x00000400 /* changed charge of a disconnected ligand to fit its valence */ -#define TG_FLAG_FIX_ODD_THINGS_DONE 0x00000800 /* fixed drawing ambiguities in fix_odd_things */ -#define TG_FLAG_TEST_TAUT3_SALTS_DONE 0x00001000 /* merged t-groups + non-O taut atoms */ -#define TG_FLAG_FOUND_SALT_CHARGES_DONE 0x00002000 /* not assigned: preprocessing detected possibility of salt-type tautomerism */ -#define TG_FLAG_FOUND_ISOTOPIC_H_DONE 0x00004000 /* preprocessing detected isotopic H on "good" heteroatoms or isotopic H(+) */ -#define TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE 0x00008000 /* preprocessing detected isotopic H on "good" heteroatoms or isotopic H(+) */ -#if ( FIX_ADJ_RAD == 1 ) -#define TG_FLAG_FIX_ADJ_RADICALS_DONE 0x00010000 -#endif - -#if ( READ_INCHI_STRING == 1 ) -#define READ_INCHI_OUTPUT_INCHI 0x00000001 -#define READ_INCHI_SPLIT_OUTPUT 0x00000002 -#define READ_INCHI_KEEP_BALANCE_P 0x00000004 -#define READ_INCHI_TO_STRUCTURE 0x00000008 -#endif - - - - -/*********/ -/* */ -/* I/O */ -/* */ -/*********/ - -typedef struct tagOutputString -{ - char *pStr; - int nAllocatedLength; - int nUsedLength; - int nPtr; -} INCHI_OUTPUT; - -typedef struct tagOutputStream -{ - /* output is directed either to resizable string buffer: */ - INCHI_OUTPUT s; - /* or to the plain file: */ - FILE* f; - int type; -} INCHI_IOSTREAM; -/* INCHI_IOSTREAM.type values */ -#define INCHI_IOSTREAM_NONE 0 -#define INCHI_IOSTREAM_STRING 1 -#define INCHI_IOSTREAM_FILE 2 - - - - -/***********/ -/* */ -/* DEBUG */ -/* */ -/***********/ - -#if ( defined(_WIN32) && defined(_DEBUG) && defined(_MSC_VER) /*&& !defined(COMPILE_ANSI_ONLY)*/ ) -/* debug: memory leaks tracking */ -#ifndef TARGET_LIB_FOR_WINCHI -#ifndef DO_NOT_TRACE_MEMORY_LEAKS -#define TRACE_MEMORY_LEAKS 1 /* 1=>trace, 0 => do not trace (Debug only) */ -#else -#define TRACE_MEMORY_LEAKS 0 -#endif -#else -#define TRACE_MEMORY_LEAKS 1 /* 1=>trace, **ALWAYS** =1 for TARGET_LIB_FOR_WINCHI */ -#endif -#else /* not MSC and not Debug */ -#define TRACE_MEMORY_LEAKS 0 /* 0: do not change */ -#endif - - -/* memory leaks tracking */ -#define INCHI_HEAPCHK /* default: no explicit heap checking during the execution */ - -#if ( TRACE_MEMORY_LEAKS == 1 ) -#ifdef _DEBUG - -#define inchi_malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) -#define inchi_calloc(c, s) _calloc_dbg(c, s, _NORMAL_BLOCK, __FILE__, __LINE__) -#define inchi_free(p) _free_dbg(p, _NORMAL_BLOCK) - -#ifdef TARGET_EXE_USING_API -/* INChI_MAIN specific */ -#define e_inchi_malloc(a) inchi_malloc(a) -#define e_inchi_calloc(a,b) inchi_calloc(a,b) -#define e_inchi_free(a) inchi_free(a) -#endif - -/*#define _CRTDBG_MAP_ALLOC*/ /* standard VC++ tool -- does not work with inchi_malloc(), etc */ - -#include - -/* to enable heap checking: #define CHECK_WIN32_VC_HEAP above #include "mode.h" in each source file or here */ -#ifdef CHECK_WIN32_VC_HEAP -/* -- Confirms the integrity of the memory blocks allocated in the debug heap -- */ -#undef INCHI_HEAPCHK -#define INCHI_HEAPCHK \ -do { \ - int tmp = _crtDbgFlag; \ - _crtDbgFlag |= _CRTDBG_ALLOC_MEM_DF; \ - _ASSERT( _CrtCheckMemory( ) ); \ - _crtDbgFlag = tmp; \ -} while(0); - -/* -- less thorough than _CrtCheckMemory() check: check minimal consistency of the heap -- */ -/* -#include -#define INCHI_HEAPCHK \ -do {\ - int heapstatus = _heapchk(); \ - _ASSERT( heapstatus != _HEAPBADBEGIN && heapstatus != _HEAPBADNODE && heapstatus != _HEAPBADPTR); \ -} while(0); -*/ -#endif - -#else -#undef TRACE_MEMORY_LEAKS -#define TRACE_MEMORY_LEAKS 0 -#endif /* _DEBUG */ -#endif /* TRACE_MEMORY_LEAKS */ - - - -/***********/ -/* */ -/* ALLOC */ -/* */ -/***********/ - -#ifdef TARGET_EXE_USING_API -/* INChI_MAIN specific */ -#ifndef inchi_malloc -#define inchi_malloc e_inchi_malloc -#endif -#ifndef inchi_calloc -#define inchi_calloc e_inchi_calloc -#endif -#ifndef inchi_free -#define inchi_free e_inchi_free -#endif - -#ifndef e_inchi_malloc -#define e_inchi_malloc malloc -#endif -#ifndef e_inchi_calloc -#define e_inchi_calloc calloc -#endif -#ifndef e_inchi_free -#define e_inchi_free(X) do{ if(X) free(X); }while(0) -#endif - -#else /* not TARGET_EXE_USING_API */ - -#ifndef inchi_malloc -#define inchi_malloc malloc -#endif -#ifndef inchi_calloc -#define inchi_calloc calloc -#endif -#ifndef inchi_free -#define inchi_free(X) do{ if(X) free(X); }while(0) -#endif - -#endif /* TARGET_EXE_USING_API */ - -/* allocation/deallocation */ -#define USE_ALLOCA 0 - -#if ( USE_ALLOCA == 1 ) -#define qmalloc(X) _alloca(X) -#define qfree(X) do{(X)=NULL;}while(0) -#else -#define qmalloc(X) inchi_malloc(X) -#define qfree(X) do{if(X){inchi_free(X);(X)=NULL;}}while(0) -#endif - -#if ( defined(_MSC_VER) && _MSC_VER >= 800 ) -#define fast_alloc(X) _alloca(X) -#define fast_free(X) -#else -#define fast_alloc(X) inchi_malloc(X) -#define fast_free(X) inchi_free(X) -#endif - -#define qzfree(X) do{if(X){inchi_free(X);(X)=NULL;}}while(0) - -/* rellocation */ - -#define MYREALLOC2(PTRTYPE1, PTRTYPE2, PTR1, PTR2, LEN1, LEN2, ERR) \ - do { \ - if( (LEN1) <= (LEN2) ) {\ - PTRTYPE1 * newPTR1 = (PTRTYPE1 *)inchi_calloc( (LEN2)+1, sizeof(PTRTYPE1) );\ - PTRTYPE2 * newPTR2 = (PTRTYPE2 *)inchi_calloc( (LEN2)+1, sizeof(PTRTYPE2) );\ - if ( newPTR1 && newPTR2 ) { \ - if ( (PTR1) && (LEN1) > 0 ) \ - (memcpy) ( newPTR1, (PTR1), (LEN1) * sizeof(PTRTYPE1) ); \ - if ( (PTR2) && (LEN1) > 0 ) \ - (memcpy) ( newPTR2, (PTR2), (LEN1) * sizeof(PTRTYPE2) ); \ - if ( PTR1 ) \ - inchi_free(PTR1); \ - if ( PTR2 ) \ - inchi_free(PTR2); \ - (PTR1) = newPTR1; \ - (PTR2) = newPTR2; \ - (LEN1) = (LEN2); \ - (ERR) = 0; \ - } else { \ - (ERR) = 1; \ - } \ - } else { (ERR) = 0; } \ - } while(0) - - - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /* __MODE_H__ */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef __MODE_H__ +#define __MODE_H__ + +#include + + + + +/*******************/ +/* */ +/* BUILD TARGETS */ +/* */ +/*******************/ + +/* Valid targets are: + +TARGET_EXE_STANDALONE + Stand-alone executable inchi-1[.exe] +TARGET_API_LIB + Library (libinchi) for using InChI API described in inchi_api.h +TARGET_EXE_USING_API + Executable (INCHI_MAIN) which uses API library (e.g., libinchi.dll) +TARGET_LIB_FOR_WINCHI + library for wInChI + + Select and uncomment one from the list below. */ + + +/* #define TARGET_EXE_STANDALONE 1 */ +/* #define TARGET_API_LIB */ + +/* #define TARGET_EXE_USING_API */ + +/* #define TARGET_LIB_FOR_WINCHI 1 */ + + +#ifndef TARGET_PLATFORM +#if defined(_WIN32) +#define TARGET_PLATFORM "Windows" +#else +#define TARGET_PLATFORM "Linux" +#endif +#endif + + +/****************************/ +/* */ +/* BUILD OPTIONS/FEATURES */ +/* */ +/****************************/ + +/* Possible options are: + +BUILD_LINK_AS_DLL + Link library as a Win32 DLL or to eliminate stricmp duplication + (use with TARGET_API_LIB or TARGET_EXE_USING_API) + +BUILD_WITH_ENG_OPTIONS + Expose engineering options + +BUILD_WITH_AMI + Turns on AMI (Allow Multiple Inputs) mode for standalone executable + + Select and uncomment whichever are necessary from the list below. */ + + +/* #define BUILD_LINK_AS_DLL */ + +/* #define BUILD_WITH_ENG_OPTIONS 1 */ + +#ifndef BUILD_WITH_AMI +/* this allows BUILD_WITH_AMI be #defined in a makefile */ +/* #define BUILD_WITH_AMI 1 */ +#endif +/* NB: AMI mode is only for stand-alone executable */ +#ifndef TARGET_EXE_STANDALONE +#ifdef BUILD_WITH_AMI +#undef BUILD_WITH_AMI +#endif +#endif + + + +/* CML input is not supported started from v. 1.04 */ +/* set ADD_CMLPPP to zero to override possble makefile define */ +#define ADD_CMLPP 0 + + +/*****************************/ +/* */ +/* COMPILE OPTIONS/FEATURES */ +/* */ +/*****************************/ + +/* Possible options are: + +COMPILE_ANSI_ONLY + Unconditionally force ANSI-89 C, no Win32 specific code + +COMPILE_ALL_CPP + allow C++ compilation/linkage of functions prototyped in .h files + +MS VC compiler pragmas + + Select and uncomment whichever are necessary from the list below. */ + + +#define COMPILE_ANSI_ONLY +#if ( !defined(_MSC_VER) || defined(TARGET_API_LIB)) /* non-Microsoft GNU C, BCC, etc. compilers */ +#ifndef COMPILE_ANSI_ONLY +#define COMPILE_ANSI_ONLY +#endif +#endif +#ifdef COMPILE_ANSI_ONLY +/*#define COMPILE_ADD_NON_ANSI_FUNCTIONS */ +#endif + +/* #define COMPILE_ALL_CPP */ + +#ifdef _MSC_VER +/* +========== disable MS VC++ 6.0 Level 4 compiler warnings: ============== + C4706: assignment within conditional expression + C4127: conditional expression is constant + C4244: '=' : conversion from 'int ' to '???', possible loss of data + C4267: '=' : conversion from 'size_t' to 'int', possible loss of data + C4701: local variable '???' may be used without having been initialized (removed) + C4514: unreferenced inline/local function has been removed (C++) + C4100: 'identifier' : unreferenced formal parameter + C4786: 'identifier' : identifier was truncated to 'number' characters in the debug information + C4996: 'identifier' was declared deprecated +======================================================================== +*/ + #pragma warning( disable : 4706 4127 4514 4100 4786 4996 4244 4267 ) +#endif + + + +/* Comment out the next line to use old (classic) library interface */ +/*#define CREATE_INCHI_STEP_BY_STEP 1*/ +#ifdef CREATE_INCHI_STEP_BY_STEP +/* Uncomment next line to output intermediate (normalization) results */ +/*#define OUTPUT_NORMALIZATION_DATA 1*/ +#endif +/* Uncomment next line to go with STDINCHI API calls */ +/* #define USE_STDINCHI_API 1*/ + +#ifndef CREATE_INCHI_STEP_BY_STEP +#define APP_DESCRIPTION "InChI version 1, Software v. 1.05 (Library call example, classic API)" +#else +#define APP_DESCRIPTION "InChI version 1, Software v. 1.05 (Library call example, modularized API)" +#endif + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + + + + +/*********************/ +/* */ +/* INCHI ALGORITHM */ +/* */ +/*********************/ + +#define INCHI_VERSION "1" + +#if 0 /* obsolete */ +/*#define INCHI_VERSION "0.9Beta" */ +/*#define INCHI_VERSION "0.91Beta" */ /* 10-10-2002: sent to Jonathan Goodman */ +/*#define INCHI_VERSION "0.92Beta" */ /* 11-15-2002: added Hill notation; sent to S.Heller & S.Stein */ +/*#define INCHI_VERSION "0.93Beta" */ /* 12-09-2002: Fixed isotopic canon. bug & chiralanes; sent to S.Heller & A. McNaught */ +/*#define INCHI_VERSION "0.931Beta" */ /* Non-BNS without salts released to PMR 01-2003 */ +/*#define INCHI_VERSION "0.932Beta" */ /* Released to CAS 04-01-2003: + * - Improved taut. definitions as compared to 01-2003; + * - fixed bug: non-isotopic components' stereo missing from isotopic stereo + * - fixed bug: couldn't properly read Unix files (EOL = LF instead of CR/LF) + * (effective only for MS VC++, Borland and MinGW/GCC compiles that accept "rb" mode of fopen) + * DJGPP/GCC does not seem to need this fix. + */ +/*==== Release version ===*/ +/*#define INCHI_VERSION "0.94Beta" */ /* 02-27-2003: Balanced network search to find alt paths and non-stereo bonds; + Implemented salts disconnection; added (-) to taut groups */ +/*#define INCHI_VERSION "1.12Beta" */ /* 1.12: 07-06-2004: sort order: No H formula,..; Pointed end stereo ON, Aggressive (de)protonation OFF */ + /* 1.11: 05-19-2004: annotated plain text output, fixed bugs */ + /* 1.1: 04-08-2004: variable protonation version */ + /* 1.01: 12-23-2003 protected bonds, isotopic canonicalization in GetBaseCanonRanking() */ + /* 1.02: 01-26-2004 fixed new isotopic tgroup canon bug, molfile merge bug */ + +/*#define INCHI_VERSION "1.0RC"*/ /* 02-07-2005 v1.0 Release Candidate */ +#endif + +#define INCHI_NAME "InChI" +#if 0 /* obsolete */ +#define INCHI_REC_NAME "ReChI" +#endif + +#define INCHI_NAM_VER_DELIM "=" + +#ifdef _WIN32 +#define INCHI_OPTION_PREFX '/' +#define INCHI_PATH_DELIM '\\' +#else +#define INCHI_OPTION_PREFX '-' +#define INCHI_PATH_DELIM '/' +#endif + +#define INCHI_ALT_OPT_PREFIX '-' +#define INCHI_ACD_LABS_PREFIX '-' + +#define bRELEASE_VERSION 1 /* 1=> release version; comment out to disable */ +#ifndef bRELEASE_VERSION +#define bRELEASE_VERSION 0 /* 0=> debug version */ +#endif + + +/*#define RELEASE_IS_FINAL 0*//* 1=> pre-release version; comment out to disable */ +#ifndef RELEASE_IS_FINAL +#define RELEASE_IS_FINAL 1 /* final release */ +#endif + + +/* display (non-canonical) c-groups, display orig at numbers */ +#if ( bRELEASE_VERSION == 1 ) +#define DISPLAY_DEBUG_DATA_C_POINT 0 /* disabled release version for now */ +#define DISPLAY_ORIG_AT_NUMBERS 1 /* 1 => in an uncanonicalized components display orig. atom numbers (default) */ +#else +#define DISPLAY_DEBUG_DATA_C_POINT 1 /* debug: 1=>display (non-canonically numbered) c-groups, 0=>do not display */ +#define DISPLAY_ORIG_AT_NUMBERS 1 /* 0 => in an uncanonicalized components display ordering atom numbers (debug) */ +#endif + +#if ( DISPLAY_DEBUG_DATA_C_POINT > 0 ) +#define DISPLAY_DEBUG_DATA DISPLAY_DEBUG_DATA_C_POINT +#endif + + + +/* BUG FIXES */ + +/**************************/ +/* bug fixes in v1.00 */ +/**************************/ +#define FIX_ChCh_STEREO_CANON_BUG 1 /* 1=> (NEEDED) */ +#define ADD_ChCh_STEREO_CANON_CHK 0 /* 1 is NOT needed; let it always be 0 */ +#define FIX_ChCh_CONSTIT_CANON_BUG 1 /* 1=> (NEEDED) */ +#define FIX_EITHER_STEREO_IN_AUX_INFO 1 /* 1=> fix bug: Either stereobond direction in Aux_Info; 0=> do not fix */ +#define FIX_NORM_BUG_ADD_ION_PAIR 1 /* 1=> (NEEDED) fix bug: Miscount number of charges when creating an ion pair */ +#define FIX_REM_PROTON_COUNT_BUG 1 /* 1=> (NEEDED) check for number of actually removed protons and issue an error if mismatch */ +#define FIX_READ_AUX_MEM_LEAK 1 +#define FIX_READ_LONG_LINE_BUG 1 /* 1=> (NEEDED) prevent failure when reading AuxInfo and InChI is too long */ +#define FIX_N_V_METAL_BONDS_GPF 1 /* 1=> (NEEDED) InChI v1 GPF bug fix */ +#define BNS_RAD_SEARCH 1 /* 1=> prevent normalization failures due to radical centers */ + +/*******************************/ +/* bug fixes in post-v1.00 */ +/*******************************/ +#define FIX_ODD_THINGS_REM_Plus_BUG 0 +#define FIX_N_MINUS_NORN_BUG 0 +#define FIX_CANCEL_CHARGE_COUNT_BUG 0 +#define FIX_2D_STEREO_BORDER_CASE 0 +#define FIX_REM_ION_PAIRS_Si_BUG 0 +#define FIX_STEREO_SCALING_BUG 0 +#define FIX_EMPTY_LAYER_BUG 0 +#define FIX_EITHER_DB_AS_NONSTEREO 0 +#define FIX_BOND23_IN_TAUT 0 +#define FIX_TACN_POSSIBLE_BUG 0 +#define FIX_KEEP_H_ON_NH_ANION 0 +#define FIX_AVOID_ADP 0 +/* may change InChI */ +#define FIX_NUM_TG 0 /* increase number of t-groups for isothiocyanate */ +/* changes InChI for isothiocyanate */ +#define FIX_CPOINT_BOND_CAP2 0 + +/*******************************/ +/* bug fixes in post-v1.02b */ +/*******************************/ + +#define FIX_ISO_FIXEDH_BUG 1 /* (2007-09-24) 1=> Fix bug: missing fixed-H iso segment in case of single removed D(+) */ +#define FIX_ISO_FIXEDH_BUG_READ 0 /* (2007-09-24) 1=> Accommodate this InChI bug in reading InChI */ +#define FIX_DALKE_BUGS 1 +#define FIX_TRANSPOSITION_CHARGE_BUG 1 /* (2008-01-02) fix bug that leads to missed charge in some cases when /o is present */ +#define FIX_I2I_STEREOCONVERSION_BUG 1 /* (2008-03-06) 1=> Fix bug of i2i conversion SAbs-->(SRel||Srac) */ +#define FIX_I2I_STEREOCONVERSION_BUG2 1 /* (2008-04-02) 1=> Fix bug of i2i conversion (missed empty /t) */ +#define FIX_I2I_STEREOCONVERSION_BUG3 1 /* (2008-04-10) 1=> Fix bug of i2i conversion */ + /* (missed repeating /s in FI after F for multi-component case) */ +#define FIX_TERM_H_CHRG_BUG 1 /* (2008-06-06) IPl) */ + /* fix bug: in some cases (dependent on ordering + numbers), moving a charge from terminal H to heavy + atom resulted in neutralizing H but not adjusting + charge of heavy atom */ + + +#if ( !defined(TARGET_API_LIB) && !defined(TARGET_EXE_USING_API) ) +#define I2S_MODIFY_OUTPUT 1 /* 1=> Allow various InChI2InChI output types from cInChI */ +#else +#define I2S_MODIFY_OUTPUT 0 /* 0=> Always */ +#endif + + +#define FIX_NP_MINUS_BUG 1 /* 2010-03-11 DCh */ + +/**************************/ +/* additions to v1.00 */ +/**************************/ +#define FIX_ADJ_RAD 0 + +#define SDF_OUTPUT_V2000 1 /* 1=>always output V2000 SDfile, 0=>only if needed */ +#define SDF_OUTPUT_DT 1 /* 1=> all option -SdfAtomsDT to output D and T into SDfile */ +#define CHECK_AROMBOND2ALT 1 /* 1=> check whether arom->alt bond conversion succeeded */ + +#ifdef TARGET_LIB_FOR_WINCHI +#define READ_INCHI_STRING 0 /* 1=> input InChI string and process it */ +#else +#define READ_INCHI_STRING 1 /* 1=> input InChI string and process it */ +#endif + +/****************************************************/ +/* disabled extra external calls to InChI algorithm */ +/****************************************************/ +#define INCLUDE_NORMALIZATION_ENTRY_POINT 0 + +/**************************/ +/* Normalization settings */ +/**************************/ + +/* post version 1 features */ +#define KETO_ENOL_TAUT 1 /* include keto-enol tautomerism */ +#define TAUT_15_NON_RING 1 /* 1,5 tautomerism with endpoints not in ring */ + +/* v.1.04 : still experimental but may be exposed (set to 1) */ +#define UNDERIVATIZE 0 /* split to possible underivatized fragments */ +#define RING2CHAIN 0 /* open rings R-C(-OH)-O-R => R-C(=O) OH-R */ + +/* post-2004-04-27 features */ +#define HAL_ACID_H_XCHG 1 /* allow iso H exchange to HX (X=halogen) and H2Y (Y=halcogen) */ +#define CANON_FIXH_TRANS 1 /* produce canonical fixed-H transposition */ +#define STEREO_WEDGE_ONLY 1 /* 1=> only pointed ends stereo bonds define stereo; 0=> both ends */ + +/* current new (with respect to v1.12 Beta) preprocessing */ +#define REMOVE_ION_PAIRS_EARLY 1 /* 1=> new preprocessing: step 1 before disconnecting metals in fix_odd_things() */ +#define REMOVE_ION_PAIRS_DISC_STRU 1 /* 1=> new post-preprocessing: remove charhes after metal disconnection */ +#define REMOVE_ION_PAIRS_FIX_BONDS 1 /* 1=> step2: set unchangeable bonds around removed ion pairs */ +#define S_VI_O_PLUS_METAL_FIX_BOND 1 /* 1=> count double bond M-O(+)=S as O=S in S(VI) ans S(VIII) fixing bonds */ +#define N_V_STEREOBONDS 1 /* 1=> detect stereobonds incident to N(V); 0 => don't */ +/* for testing */ +#define REMOVE_ION_PAIRS_ORIG_STRU 0 /* 0=> normal mode (default) + * 1=> testing mode only: remove ion pairs from the original structure + * to save the changes in the output Molfile (/OutputSDF) or AuxInfo + * NIP=No Ion Pairs + */ +/* salts treatment */ +#define DISCONNECT_SALTS 1 /* 1=>disconnect metal atoms from salts, 0=>dont */ +#define TEST_REMOVE_S_ATOMS 1 /* 1=>default: after merging into one group test & + * remove unreachable, + * 0=> old version: test only before merging into one t-group */ +#define CHARGED_SALTS_ONLY 1 /* 1=>(default)do not test far salts tautomerism if + * no negative charge(s) present */ +#define BNS_PROTECT_FROM_TAUT 1 /* 1=> do not allow testing of bonds to acetyl or nitro */ +#define BNS_MARK_EDGE_2_DISCONNECT 1 /* 1=> mark edge as temp forbidden instead of disconnection */ + +#define REPLACE_ALT_WITH_TAUT 1 /* 1 => replace alt bonds with tautomeric bonds in case of standard t-groups */ +#define MOVE_CHARGES 1 /* 1 => take moveable charges into account */ +#define NEUTRALIZE_ENDPOINTS 1 /* 1 => before checking whether an H is moveable make 2 endpoints neutral */ + /* implemented only if CHECK_TG_ALT_PATH = 0, defined in ichi_bns.c */ +#define FIX_H_CHECKING_TAUT 1 /* 1 => Fix moveable H or (-) before checking if taut. exchange is possible */ +#define ALWAYS_ADD_TG_ON_THE_FLY 1 /* 1 => disables radical calcellation by taut-charge movement */ +#define IGNORE_SINGLE_ENDPOINTS 1 /* 1 => see FindAccessibleEndPoints() in INChITaut.c */ + +/* recently added -- begin */ +#define INCL_NON_SALT_CANDIDATATES 1 /* 1=> allow H and (-) migrate between "acidic" O and + * other possible endpoints */ +#define SALT_WITH_PROTONS 1 /* 1=> (new new) include proton migrarion C-SH, =C-OH, NH+ */ +#define OPPOSITE_CHARGE_IN_CGROUP 1 /* 1=> allow N(-) in (+) c-group, 0=> disallow */ +#define MOVE_PPLUS_TO_REMOVE_PROTONS 0 /* 0=> default; 1=> (disabled) add P/P+ charge group during + * 'hard' proton removal */ +#define ADD_MOVEABLE_O_PLUS 1 /* 1=> allow charges on O(+) to move */ +/* recently added -- end */ + +#define DISCONNECT_METALS 1 /* make main layer disconnected */ +#define RECONNECT_METALS 0 /* 1=> by default add reconnected layer in case of coord. + * compound disconnection */ +#define CHECK_METAL_VALENCE 0 /* 1=> disconnect only metals that have abnormal valence */ +#define bREUSE_INCHI 1 /* 1=> do not recalulate INChI for components in reconnected + * structure that are same as in the connected one */ +#define OUTPUT_CONNECTED_METAL_ONLY 0 /* 0=> default; 1 => (debug) create only reconnected or + * initial struct. output */ +#define EMBED_REC_METALS_INCHI 1 /* 1=> (default) output Reconnected embedded in Disconnected INChI; + * 0=> separate output */ + +#define bOUTPUT_ONE_STRUCT_TIME 1 /* 1 => output each structure time (non-release only) */ + + + +/* constants and array sizes */ + +#define INCHI_NUM 2 /* = array size; member indexes: */ +#define INCHI_BAS 0 /* 0 => disconnected or normal */ +#define INCHI_REC 1 /* 1 => reconnected */ + +#define TAUT_NUM 2 /* = array size; member indexes: */ +#define TAUT_NON 0 /* 0 => normal structure */ +#define TAUT_YES 1 /* 1 => tautomeric */ +#define TAUT_INI 2 /* 2 => intermediate tautomeric structure */ +#define ALT_TAUT(X) ((X)>TAUT_YES? TAUT_YES : 1-(X)) /* was (1-(X)) */ + +/* INChI output modes */ +#define OUT_N1 0 /* non-tautomeric only */ +#define OUT_T1 1 /* tautomeric if present otherwise non-tautomeric */ +#define OUT_NT 2 /* only non-taut representations of tautomeric */ +#define OUT_TN 3 /* tautomeric if present otherwise non-tautomeric; + separately output non-taut representations of tautomeric if present */ +#define OUT_NN 4 /* only non-taut representations: non-taut else tautomeric */ + +/* OUT_TN = OUT_T1 + OUT_NT */ + +/* stereo */ + +#define NEW_STEREOCENTER_CHECK 1 /* 1 => add new stereocenter categories (see bCanInpAtomBeAStereoCenter(...)) */ +#define MIN_SB_RING_SIZE 8 /* do not assume stereo bonds in rings containing 3..MIN_SB_RING_SIZE-1 atoms */ + +#define REMOVE_KNOWN_NONSTEREO 1 /* 1=> check in advance known stereo to remove parities from non-stereogenic elements */ +#define REMOVE_CALC_NONSTEREO 1 /* 1=> check new stereo numberings to remove parities from non-stereogenic elements */ +#define PROPAGATE_ILL_DEF_STEREO 1 /* 1=> if at least one of the pair of constitutionally identical (far) neighbors */ + /* (of the tested atom) has ill-defined stereo parity and another has any */ + /* stereo parity then set the parity of the tested atom to ill-defined value. */ + +#define ONLY_DOUBLE_BOND_STEREO 0 /* 1=> no alt bond stereo, no taut. bond attachment to stereo bond */ + /* 0=> allow other definitions (below) to be active */ +#define ONE_BAD_SB_NEIGHBOR 1 /* 1 => allow 1 "bad" bond type neighbor to a stereobond atom. 2004-06-02 */ + +/* more stereo settings */ +#define BREAK_ONE_MORE_SC_TIE 1 /* break one more tie when comparing possible stereocenter neighbors */ +#define BREAK_ALSO_NEIGH_TIE 0 /* post 1.12Beta 2004-08-20: if fixed neighbor has equ neighbors, fix the one with smaller canon. rank */ +#define BREAK_ALSO_NEIGH_TIE_ROTATE 1 /* post 1.12Beta 2004-09-02: break the second in 2nd psition; 1 works, 0 does not (example:MFCD01085607) */ + +#define STEREO_CENTER_BONDS_NORM 1 /* set length of the bonds around a stereocenter = 1 before getting the parity */ +#define STEREO_CENTER_BOND4_NORM 0 /* set length of the added bond around a stereocenter = 1 before getting the parity */ +#define NORMALIZE_INP_COORD 0 /* 0=>keep unchanged, 1 => make atom coordinates integer values, avg bond len=20 */ + +/* recent stereo */ +#define STEREO_WEDGE_ONLY 1 /* 1=> only pointed ends stereo bonds define stereo; 0=> both ends 1.12Beta */ +#define CHECK_C2v_S4_SYMM 0 /* post-1.12Beta 1=> check if a stereocenter has C2v or S4 symmetry; 0=>old mode */ + +#define EQL_H_NUM_TOGETHER 1 /* 1=> output 1-3,5H2 intead of 1-3H2,5H2 (CT_MODE_EQL_H_TOGETHER) */ +#define ABC_CT_NUM_CLOSURES 1 /* 1=> in coinnections compressed format output decimal number of closures instead of '-' */ + +/* temporary fix */ +#define SINGLET_IS_TRIPLET 1 /* 'singlet' means two electrons make a lone pair instead of 2 bonds + its effect on valence is same as the effect of a triplet */ + +/* defug: find structures where canonical partition is different from equitable */ +#define FIND_CANON_NE_EQUITABLE 0 /* 0=>normal mode */ + /* 1=> extract (set EXTR_FLAGS = (EXTR_CANON_NE_EQUITABLE)*/ + /* set cmd line options: /onlynonTAUT /: /UNCHARGEDACIDS:1 /DISCONSALT:0 /MOVEPOS:0 /DISCONMETAL:0 */ + +/* Debug: definitions for the extraction of the structures to the problem file */ + +/* definition of the flags for structure extraction to the + problem file (for debugging and non-standard searching) */ +#define EXTR_KNOWN_USED_TO_REMOVE_PARITY 0x000001 +#define EXTR_CALC_USED_TO_REMOVE_PARITY 0x000002 +#define EXTR_2EQL2CENTER_TO_REMOVE_PARITY 0x000004 +#define EXTR_HAS_ATOM_WITH_DEFINED_PARITY 0x000008 +#define EXTR_REMOVE_PARITY_WARNING 0x000010 +#define EXTR_SALT_WAS_DISCONNECTED 0x000020 +#define EXTR_SALT_PROTON_MOVED 0x000040 +#define EXTR_SALT_PROTON_MOVE_ERR_WARN 0x000080 +#define EXTR_METAL_WAS_DISCONNECTED 0x000100 +#define EXTR_METAL_WAS_NOT_DISCONNECTED 0x000200 +#define EXTR_NON_TRIVIAL_STEREO 0x000400 /* (Inv != Abs stereo) && (parities can't be obtained by inverting them) */ +#define EXTR_UNUSUAL_VALENCES 0x000800 +#define EXTR_HAS_METAL_ATOM 0x001000 +#define EXTR_TEST_TAUT3_SALTS_DONE 0x002000 /* non-oxygen t-points used to discover tautomerism of merged t-groups */ +#define EXTR_CANON_NE_EQUITABLE 0x004000 /* find structures where canonical partition is different from equitable */ +#define EXTR_HAS_PROTON_PN 0x008000 /* has movable H+ attached to N or P */ +#define EXTR_HAS_FEATURE 0x010000 /* found a feature */ +#define EXTR_TAUT_TREATMENT_CHARGES 0x020000 /* tautomeric treatment of charges */ +#define EXTR_TRANSPOSITION_EXAMPLES 0x040000 /* extract structures that have different mobile-H and fixed-H orders */ + +/* define conditions of structure extraction to the problem file */ +#define EXTR_MASK 0 /*EXTR_TAUT_TREATMENT_CHARGES*/ /*(EXTR_HAS_FEATURE)*/ /*(EXTR_UNUSUAL_VALENCES | EXTR_HAS_METAL_ATOM)*/ /* 0 to disable */ +#define EXTR_FLAGS 0 /*EXTR_TAUT_TREATMENT_CHARGES*/ /*(EXTR_HAS_FEATURE)*/ /*(EXTR_HAS_PROTON_PN)*/ /*(EXTR_UNUSUAL_VALENCES)*/ /*(EXTR_CANON_NE_EQUITABLE)*/ /*(EXTR_TEST_TAUT3_SALTS_DONE)*/ /*(EXTR_HAS_METAL_ATOM)*/ /* (EXTR_NON_TRIVIAL_STEREO)*/ /*(EXTR_METAL_WAS_DISCONNECTED)*/ /* (EXTR_REMOVE_PARITY_WARNING)*/ /*(EXTR_HAS_ATOM_WITH_DEFINED_PARITY) */ + + +#define ENTITY_REFS_IN_XML_MESSAGES 1 /* 1=> replace ' " < > & in error/warning messages with xml entity references */ + +/* added tautomeric structures */ + +#define TAUT_TROPOLONE_7 1 /* 1=> tautomeric 7-member rings ON */ +#define TAUT_TROPOLONE_5 1 /* 1=> taut. similar to tropolone, 5-member ring */ +#define TAUT_4PYRIDINOL_RINGS 1 /* 1=> OH-C5H4N rings tautomerism */ +#define TAUT_PYRAZOLE_RINGS 1 /* 1=> tautomerizm in pyrazole rings */ +/* limitation on tautomerism detection: */ +#define TAUT_IGNORE_EQL_ENDPOINTS 0 /* 0=> even though 2 endpoints belong to same t-group check + them to find more alt bonds (new) + 1=> ignore and do not check (old mode) */ +#define TAUT_RINGS_ATTACH_CHAIN 1 /* 1=> allow only chain attachments to tautomeric endpoints */ + /* (except pyrazole, where is no tautomeric attachment) */ + /* 0=> allow taut. attachments from same ring system. Default=1 */ + +#define FIND_RING_SYSTEMS 1 /* 1 => find and mark ring systems, blocks, cut-vertices */ + /* Needed for 5- and 6-member ring tautomers and in other places */ + +#define FIND_RINS_SYSTEMS_DISTANCES 0 /* 1 => find ring system and atom distance from terminal */ +#define USE_DISTANCES_FOR_RANKING 0 /* 1 => rank ring systems according to distances from terminal */ + +#define DISPLAY_RING_SYSTEMS 0 /* 1 => for debug only; displays: */ + /* "block no"/"ring system no"/"cut-vertex (num. intersecting blocks-1)" */ + /* instead of ranks */ +/* consistency */ + +#if ( bRELEASE_VERSION==1 && bOUTPUT_ONE_STRUCT_TIME==1) +#undef bOUTPUT_ONE_STRUCT_TIME +#define bOUTPUT_ONE_STRUCT_TIME 0 +#endif + +/* consistency: bRELEASE_VERSION==1 needs FIND_RING_SYSTEMS=1 */ +#if ( bRELEASE_VERSION==1 && FIND_RING_SYSTEMS!=1 ) +#ifdef FIND_RING_SYSTEMS +#undef FIND_RING_SYSTEMS +#endif +#define FIND_RING_SYSTEMS 1 +#endif + +/* consistency: FIND_RINS_SYSTEMS_DISTANCES needs FIND_RING_SYSTEMS */ +#if ( FIND_RING_SYSTEMS != 1 ) + +#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) +#undef FIND_RINS_SYSTEMS_DISTANCES +#define FIND_RINS_SYSTEMS_DISTANCES 0 +#endif + +#endif + +/* consistency: USE_DISTANCES_FOR_RANKING and DISPLAY_RING_SYSTEMS need FIND_RINS_SYSTEMS_DISTANCES */ +#if ( FIND_RINS_SYSTEMS_DISTANCES != 1 ) + +#if ( USE_DISTANCES_FOR_RANKING == 1 ) +#undef USE_DISTANCES_FOR_RANKING +#define USE_DISTANCES_FOR_RANKING 0 +#endif + +#if ( DISPLAY_RING_SYSTEMS == 1 ) +#undef DISPLAY_RING_SYSTEMS +#define DISPLAY_RING_SYSTEMS 0 +#endif + +#endif + + +#if ( FIND_RING_SYSTEMS==1 && (TAUT_TROPOLONE_7==1 || TAUT_TROPOLONE_5==1 || TAUT_4PYRIDINOL_RINGS==1 || TAUT_PYRAZOLE_RINGS) ) +#define TAUT_OTHER 1 +#else +#define TAUT_OTHER 0 +#endif + +#define APPLY_IMPLICIT_H_DOWN_RULE 0 /* 1=> if 3 non-H atoms around stereocenter are in same plane */ + /* then add "down" hydrogen to obtain sterecenter oparity */ + /* 0=> Implicit H stereo is unknown if all bonds to 3 non-H atoms */ + /* are in XY plane */ +#define ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS 1 /* 1=> consider bond in an alternating circuit stereogenic */ + /* even though it has adjacent tautomeric atom(s) */ + +#define IGNORE_TGROUP_WITHOUT_H 1 /* ignore tautomeric groups containing charges only */ + +#if ( DISCONNECT_SALTS == 1 ) +#define REMOVE_TGROUP_CHARGE 0 /* 0: do not remove charge information from tautomeric groups */ +#else +#define REMOVE_TGROUP_CHARGE 1 /* 1: remove charge information from tautomeric groups */ +#endif + +#if ( REMOVE_TGROUP_CHARGE == 1 ) +#define INCHI_T_NUM_MOVABLE 1 +#else +#define INCHI_T_NUM_MOVABLE 2 +#endif + +/******************************************/ +/* define canonicalization modes here */ +/******************************************/ + +#define USE_AUX_RANKING 1 /* 1=> get auxiliary ranking to accelerate canonicalization of H layers */ +#define USE_AUX_RANKING_ALL 1 /* 1=> include all vertices in CellGetMinNode() selection 0=> only vertices with highest ranks */ + +#define USE_ISO_SORT_KEY_HFIXED 0 /* 0=> normal mode: merge isotopic taut H to isotopic atom sorting key in + taut H-fixed canonicalization; + 1=> add one more "string" iso_sort_Hfixed to the canonicalization */ + +/************************ + questionable behavior + ************************/ +#define REL_RAC_STEREO_IGN_1_SC 0 /* 1=> drop from InChI sp3 stereo in components that have a single stereocenter */ + /* 0=> old-old mode (all such sp3 stereo is in the Identifier) */ +/* internal definitions; see also REQ_MODE_BASIC etc in ichi.h */ +#define CMODE_CT 0x000001 +#define CMODE_ISO 0x000002 +#define CMODE_ISO_OUT 0x000004 /* obsolete ? */ +#define CMODE_STEREO 0x000008 +#define CMODE_ISO_STEREO 0x000010 +#define CMODE_TAUT 0x000020 +#define CMODE_NOEQ_STEREO 0x000040 /* 5-24-2002: do not use stereo equivalence to accelerate */ +#define CMODE_REDNDNT_STEREO 0x000080 /* 6-11-2002: do not check for redundant stereo elements */ +#define CMODE_NO_ALT_SBONDS 0x000100 /* 6-14-2002: do not assign stereo to alternating bonds */ +/* new 10-10-2003 */ +#define CMODE_RELATIVE_STEREO 0x000200 /* REL All Relative Stereo */ +#define CMODE_RACEMIC_STEREO 0x000400 /* RAC All Racemic Stereo */ +#define CMODE_SC_IGN_ALL_UU 0x000800 /* IAUSC Ignore stereocenters if All Undef/Unknown */ +#define CMODE_SB_IGN_ALL_UU 0x001000 /* IAUSC Ignore stereobonds if All Undef/Unknown */ +/* end of 10-10-2003 */ + +/* external definitions */ +#define CANON_MODE_CT (CMODE_CT) +#define CANON_MODE_TAUT (CMODE_CT|CMODE_TAUT) +#define CANON_MODE_ISO (CMODE_CT|CMODE_ISO|CMODE_ISO_OUT) +#define CANON_MODE_STEREO (CMODE_CT|CMODE_STEREO) +#define CANON_MODE_ISO_STEREO (CMODE_CT|CMODE_ISO|CMODE_ISO_OUT|CMODE_ISO_STEREO) + +#define CANON_MODE_MASK 0x00FF /* used to determine canonicalization mode */ + +/************************************************* + * from d_norm.c + */ + +/* implemented definitions for CT_ATOMID */ +#define CT_ATOMID_DONTINCLUDE 1 +#define CT_ATOMID_IS_INITRANK 2 +#define CT_ATOMID_IS_CURRANK 3 + +/*************************************** + * canonicalization settings I + ***************************************/ + +#define CANON_TAUTOMERS 1 /* 1=> process tautomers */ +#define HYDROGENS_IN_INIT_RANKS 1 /* 1=> include num_H in initial ranking */ + +#define DOUBLE_BOND_NEIGH_LIST 0 /* 1 => include double bond neighbor in NeighList 2 times */ +#define INCL_NON_6AROM 1 /* 1 => mark all arom. bonds; 0=>mark arom. bonds only in 6-member rings */ + +#define CT_SMALLEST /* minimal CT */ + +#define CT_NEIGH_SMALLER /* in CT, include neighbors with smaller ranks */ + +#define CT_ATOMID CT_ATOMID_IS_CURRANK /*CT_ATOMID_DONTINCLUDE */ + +#define CT_NEIGH_INCREASE /* in CT, neighbors ranks increase */ + +#define USE_SYMMETRY_TO_ACCELERATE 1 /*1 => for fast CT canonicalization, to avoid full enumeration */ + +/* dependent definitions due to settings */ + +#ifdef CT_SMALLEST +#define CT_GREATER_THAN > +#define CT_INITVALUE ~0 +#define BEST_PARITY 1 /* odd */ +#define WORSE_PARITY 2 +#else +#define CT_GREATER_THAN < +#define CT_INITVALUE 0 +#define BEST_PARITY 2 /* even */ +#define WORSE_PARITY 1 +#endif + +#ifdef CT_NEIGH_SMALLER +#define CT_NEIGH_SMALLER_THAN < +#else +#define CT_NEIGH_SMALLER_THAN > +#endif + +/* verify corectness of dependent settings */ +#if !defined( CT_ATOMID ) + #error You have to #define CT_ATOMID +#else +#if ( defined( CT_ATOMID ) && CT_ATOMID==CT_ATOMID_DONTINCLUDE ) + #error CT_DELIMITER should be #defined if CT_ATOMID is not included +#endif +#endif + +/*************************************** + * canonicalization settings II + ***************************************/ +/* from extr_ct.h */ +#define ALL_ALT_AS_AROMATIC 1 /* 1 => all altrnate bonds (even in cyclooctateraene) treat as aromatic */ + /* and set DOUBLE_BOND_NEIGH_LIST = 0 */ +#define ANY_ATOM_IN_ALT_CYCLE 1 /* 1=> accept any atom in alternating bond circuit, 0=>only some */ + +#define EXCL_ALL_AROM_BOND_PARITY 0 /* 1 => any arom atom cannot belong to stereo bond. */ + /* This has presedence over ADD_6MEMB_AROM_BOND_PARITY=1 */ + /* 0 => include arom bonds parities according to */ + /* ADD_6MEMB_AROM_BOND_PARITY definition */ + +#if ( EXCL_ALL_AROM_BOND_PARITY == 0 ) +#define ADD_6MEMB_AROM_BOND_PARITY 1 /* 1 => all arom bonds are stereo bonds */ + /* 0 => only those arom bonds which do not belong to */ + /* 6-member arom rings are stereo bonds */ +#else +#define ADD_6MEMB_AROM_BOND_PARITY 0 /* 0 => standard; 1 => meaningless: ignore parities of non-6-member ring alt. bonds */ +#endif + +#define CML_NUM_AT_IN_ATREF4 4 +#define MAX_NUM_STEREO_BONDS 3 +#define MAX_NUM_STEREO_BOND_NEIGH 3 +#define MIN_NUM_STEREO_BOND_NEIGH 2 + +#define MAX_NUM_STEREO_ATOM_NEIGH 4 +#define STEREO_AT_MARK 8 /* > MAX_NUM_STEREO_BONDS */ + +#if ( ONLY_DOUBLE_BOND_STEREO == 1 ) /* { */ + +#ifdef ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS +#undef ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS +#define ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS 0 +#endif + +#ifdef EXCL_ALL_AROM_BOND_PARITY +#undef EXCL_ALL_AROM_BOND_PARITY +#define EXCL_ALL_AROM_BOND_PARITY 1 +#endif + +#ifdef ADD_6MEMB_AROM_BOND_PARITY +#undef ADD_6MEMB_AROM_BOND_PARITY +#define ADD_6MEMB_AROM_BOND_PARITY 0 +#endif + +#endif /* } ONLY_DOUBLE_BOND_STEREO */ + +/* dependent definitions due to settings */ +#if ( ALL_ALT_AS_AROMATIC == 1 && DOUBLE_BOND_NEIGH_LIST != 0 ) +#undef DOUBLE_BOND_NEIGH_LIST +#define DOUBLE_BOND_NEIGH_LIST 0 +#endif + + +/************************************* + * Drawing + */ + +#define DRAW_AROM_TAUT 1 /* 1=> draw distinct aromatic & tautomer bonds, 0=> don't */ + +/******************************************************/ +/* C O M M O N D E F I N I T I O N S */ +/******************************************************/ + + +/* input bTautFlags flags */ +#define TG_FLAG_TEST_TAUT__ATOMS 0x00000001 /* find regular tautomerism */ +#define TG_FLAG_DISCONNECT_SALTS 0x00000002 /* DISCONNECT_SALTS disconnect */ +#define TG_FLAG_TEST_TAUT__SALTS 0x00000004 /* DISCONNECT_SALTS if possible find long-range H/(-) taut. on =C-OH, >C=O */ +#define TG_FLAG_MOVE_POS_CHARGES 0x00000008 /* MOVE_CHARGES allow long-range movement of N(+), P(+) charges */ +#define TG_FLAG_TEST_TAUT2_SALTS 0x00000010 /* TEST_REMOVE_S_ATOMS multi-attachement long-range H/(-) taut. on =C-OH, >C=O */ +#define TG_FLAG_ALLOW_NO_NEGTV_O 0x00000020 /* CHARGED_SALTS_ONLY=0 (debug) find long-range H-only tautomerism on =C-OH, >C=O */ +#define TG_FLAG_MERGE_TAUT_SALTS 0x00000040 /* DISCONNECT_SALTS merge all "salt"-t-groups and other =C-OH into one t-group */ + +#define TG_FLAG_ALL_TAUTOMERIC (TG_FLAG_TEST_TAUT__ATOMS| \ + TG_FLAG_TEST_TAUT__SALTS| \ + TG_FLAG_TEST_TAUT2_SALTS| \ + TG_FLAG_MERGE_TAUT_SALTS) + +#define TG_FLAG_DISCONNECT_COORD 0x00000080 /* find "coord. centers" and disconnect them */ +#define TG_FLAG_RECONNECT_COORD 0x00000100 /* reconnect disconnected "coord. centers" */ +#define TG_FLAG_CHECK_VALENCE_COORD 0x00000200 /* do not disconnect "coord. centers" with usual valence */ +#define TG_FLAG_MOVE_HPLUS2NEUTR 0x00000400 /* move protons to neutralize */ +#define TG_FLAG_VARIABLE_PROTONS 0x00000800 /* add/remove protons to neutralize */ +#define TG_FLAG_HARD_ADD_REM_PROTONS 0x00001000 /* add/remove protons to neutralize in hard way */ +#define TG_FLAG_POINTED_EDGE_STEREO 0x00002000 /* only pointed edge of stereo bond defines stereo */ +#if ( FIX_ADJ_RAD == 1 ) +#define TG_FLAG_FIX_ADJ_RADICALS 0x00004000 /* remove adjacent radical-doubletes, fix valence */ +#endif +#define TG_FLAG_PHOSPHINE_STEREO 0x00008000 /* add phosphine sp3 stereo */ +#define TG_FLAG_ARSINE_STEREO 0x00010000 /* add arsine sp3 stereo */ +#define TG_FLAG_H_ALREADY_REMOVED 0x00020000 /* processing structure restored from InChI */ +#define TG_FLAG_FIX_SP3_BUG 0x00040000 /* fix sp3 stereo bug: overlapping 2D stereo bond & coordinate scaling */ + +#define TG_FLAG_KETO_ENOL_TAUT 0x00080000 /* turn on keto-enol tautomerism detection */ +#define TG_FLAG_1_5_TAUT 0x00100000 /* turn on 1,5 tautomerism detection */ + +/*^^^ FB2 */ +#define TG_FLAG_FIX_ISO_FIXEDH_BUG 0x00200000 /* fix bug found after v.102b (isotopic H representation) */ +#define TG_FLAG_FIX_TERM_H_CHRG_BUG 0x00400000 /* fix bug found after v.102b (moving H charge in 'remove_terminal_HDT') */ + +/* output bTautFlags flags */ + +#define TG_FLAG_MOVE_HPLUS2NEUTR_DONE 0x00000001 /* protons have been moved to neutralize */ +#define TG_FLAG_TEST_TAUT__ATOMS_DONE 0x00000002 +#define TG_FLAG_DISCONNECT_SALTS_DONE 0x00000004 +#define TG_FLAG_TEST_TAUT__SALTS_DONE 0x00000008 /* multiple H tautomerism */ +#define TG_FLAG_MOVE_POS_CHARGES_DONE 0x00000010 +#define TG_FLAG_TEST_TAUT2_SALTS_DONE 0x00000020 /* merged t-groups */ +#define TG_FLAG_ALLOW_NO_NEGTV_O_DONE 0x00000040 +#define TG_FLAG_MERGE_TAUT_SALTS_DONE 0x00000080 /* added non-taut O to taut groups */ + +#define TG_FLAG_ALL_SALT_DONE (TG_FLAG_TEST_TAUT__SALTS_DONE | \ + TG_FLAG_TEST_TAUT2_SALTS_DONE | \ + TG_FLAG_MERGE_TAUT_SALTS_DONE ) + +#define TG_FLAG_DISCONNECT_COORD_DONE 0x00000100 /* found and disconnected "coord. centers" */ +#define TG_FLAG_CHECK_VALENCE_COORD_DONE 0x00000200 /* did not disconnect "coord. centers" with usual valence */ +#define TG_FLAG_MOVE_CHARGE_COORD_DONE 0x00000400 /* changed charge of a disconnected ligand to fit its valence */ +#define TG_FLAG_FIX_ODD_THINGS_DONE 0x00000800 /* fixed drawing ambiguities in fix_odd_things */ +#define TG_FLAG_TEST_TAUT3_SALTS_DONE 0x00001000 /* merged t-groups + non-O taut atoms */ +#define TG_FLAG_FOUND_SALT_CHARGES_DONE 0x00002000 /* not assigned: preprocessing detected possibility of salt-type tautomerism */ +#define TG_FLAG_FOUND_ISOTOPIC_H_DONE 0x00004000 /* preprocessing detected isotopic H on "good" heteroatoms or isotopic H(+) */ +#define TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE 0x00008000 /* preprocessing detected isotopic H on "good" heteroatoms or isotopic H(+) */ +#if ( FIX_ADJ_RAD == 1 ) +#define TG_FLAG_FIX_ADJ_RADICALS_DONE 0x00010000 +#endif + +#if ( READ_INCHI_STRING == 1 ) +#define READ_INCHI_OUTPUT_INCHI 0x00000001 +#define READ_INCHI_SPLIT_OUTPUT 0x00000002 +#define READ_INCHI_KEEP_BALANCE_P 0x00000004 +#define READ_INCHI_TO_STRUCTURE 0x00000008 +#endif + + + + +/*********/ +/* */ +/* I/O */ +/* */ +/*********/ + +typedef struct tagOutputString +{ + char *pStr; + int nAllocatedLength; + int nUsedLength; + int nPtr; +} INCHI_IOSTREAM_STRING; + +typedef struct tagOutputStream +{ + /* output is directed either to resizable string buffer: */ + INCHI_IOSTREAM_STRING s; + /* or to the plain file: */ + FILE* f; + int type; +} INCHI_IOSTREAM; +/* INCHI_IOSTREAM.type values */ +#define INCHI_IOSTREAM_TYPE_NONE 0 +#define INCHI_IOSTREAM_TYPE_STRING 1 +#define INCHI_IOSTREAM_TYPE_FILE 2 + + + + +/***********/ +/* */ +/* DEBUG */ +/* */ +/***********/ + +#if ( defined(_WIN32) && defined(_DEBUG) && defined(_MSC_VER) /*&& !defined(COMPILE_ANSI_ONLY)*/ ) +/* debug: memory leaks tracking */ +#ifndef TARGET_LIB_FOR_WINCHI +#ifndef DO_NOT_TRACE_MEMORY_LEAKS +#define TRACE_MEMORY_LEAKS 1 /* 1=>trace, 0 => do not trace (Debug only) */ +#else +#define TRACE_MEMORY_LEAKS 0 +#endif +#else +#define TRACE_MEMORY_LEAKS 1 /* 1=>trace, **ALWAYS** =1 for TARGET_LIB_FOR_WINCHI */ +#endif +#else /* not MSC and not Debug */ +#define TRACE_MEMORY_LEAKS 0 /* 0: do not change */ +#endif + + +/* memory leaks tracking */ +#define INCHI_HEAPCHK /* default: no explicit heap checking during the execution */ + +#if ( TRACE_MEMORY_LEAKS == 1 ) +#ifdef _DEBUG + +#define inchi_malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define inchi_calloc(c, s) _calloc_dbg(c, s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define inchi_free(p) _free_dbg(p, _NORMAL_BLOCK) + +#ifdef TARGET_EXE_USING_API +/* INChI_MAIN specific */ +#define e_inchi_malloc(a) inchi_malloc(a) +#define e_inchi_calloc(a,b) inchi_calloc(a,b) +#define e_inchi_free(a) inchi_free(a) +#endif + +/*#define _CRTDBG_MAP_ALLOC*/ /* standard VC++ tool -- does not work with inchi_malloc(), etc */ + +#include + +/* to enable heap checking: #define CHECK_WIN32_VC_HEAP above #include "mode.h" in each source file or here */ +#ifdef CHECK_WIN32_VC_HEAP +/* -- Confirms the integrity of the memory blocks allocated in the debug heap -- */ +#undef INCHI_HEAPCHK +#define INCHI_HEAPCHK \ +do { \ + int tmp = _crtDbgFlag; \ + _crtDbgFlag |= _CRTDBG_ALLOC_MEM_DF; \ + _ASSERT( _CrtCheckMemory( ) ); \ + _crtDbgFlag = tmp; \ +} while(0); + +/* -- less thorough than _CrtCheckMemory() check: check minimal consistency of the heap -- */ +/* +#include +#define INCHI_HEAPCHK \ +do {\ + int heapstatus = _heapchk(); \ + _ASSERT( heapstatus != _HEAPBADBEGIN && heapstatus != _HEAPBADNODE && heapstatus != _HEAPBADPTR); \ +} while(0); +*/ +#endif + +#else +#undef TRACE_MEMORY_LEAKS +#define TRACE_MEMORY_LEAKS 0 +#endif /* _DEBUG */ +#endif /* TRACE_MEMORY_LEAKS */ + + + +/***********/ +/* */ +/* ALLOC */ +/* */ +/***********/ + +#ifdef TARGET_EXE_USING_API +/* INChI_MAIN specific */ +#ifndef inchi_malloc +#define inchi_malloc e_inchi_malloc +#endif +#ifndef inchi_calloc +#define inchi_calloc e_inchi_calloc +#endif +#ifndef inchi_free +#define inchi_free e_inchi_free +#endif + +#ifndef e_inchi_malloc +#define e_inchi_malloc malloc +#endif +#ifndef e_inchi_calloc +#define e_inchi_calloc calloc +#endif +#ifndef e_inchi_free +#define e_inchi_free(X) do{ if(X) free(X); }while(0) +#endif + +#ifndef e_inchi_memicmp +#define e_inchi_memicmp inchi_memicmp +#endif +#ifndef e_inchi_stricmp +#define e_inchi_stricmp inchi_stricmp +#endif + +#else /* not TARGET_EXE_USING_API */ + +#ifndef inchi_malloc +#define inchi_malloc malloc +#endif +#ifndef inchi_calloc +#define inchi_calloc calloc +#endif +#ifndef inchi_free +#define inchi_free(X) do{ if(X) free(X); }while(0) +#endif + +#endif /* TARGET_EXE_USING_API */ + +/* allocation/deallocation */ +#define USE_ALLOCA 0 + +#if ( USE_ALLOCA == 1 ) +#define qmalloc(X) _alloca(X) +#define qfree(X) do{(X)=NULL;}while(0) +#else +#define qmalloc(X) inchi_malloc(X) +#define qfree(X) do{if(X){inchi_free(X);(X)=NULL;}}while(0) +#endif + +#if ( defined(_MSC_VER) && _MSC_VER >= 800 ) +#define fast_alloc(X) _alloca(X) +#define fast_free(X) +#else +#define fast_alloc(X) inchi_malloc(X) +#define fast_free(X) inchi_free(X) +#endif + +#define qzfree(X) do{if(X){inchi_free(X);(X)=NULL;}}while(0) + +/* rellocation */ + +#define MYREALLOC2(PTRTYPE1, PTRTYPE2, PTR1, PTR2, LEN1, LEN2, ERR) \ + do { \ + if( (LEN1) <= (LEN2) ) {\ + PTRTYPE1 * newPTR1 = (PTRTYPE1 *)inchi_calloc( (LEN2)+1, sizeof(PTRTYPE1) );\ + PTRTYPE2 * newPTR2 = (PTRTYPE2 *)inchi_calloc( (LEN2)+1, sizeof(PTRTYPE2) );\ + if ( newPTR1 && newPTR2 ) { \ + if ( (PTR1) && (LEN1) > 0 ) \ + (memcpy) ( newPTR1, (PTR1), (LEN1) * sizeof(PTRTYPE1) ); \ + if ( (PTR2) && (LEN1) > 0 ) \ + (memcpy) ( newPTR2, (PTR2), (LEN1) * sizeof(PTRTYPE2) ); \ + if ( PTR1 ) \ + inchi_free(PTR1); \ + if ( PTR2 ) \ + inchi_free(PTR2); \ + (PTR1) = newPTR1; \ + (PTR2) = newPTR2; \ + (LEN1) = (LEN2); \ + (ERR) = 0; \ + } else { \ + (ERR) = 1; \ + } \ + } else { (ERR) = 0; } \ + } while(0) + + + + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + + +#endif /* __MODE_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/e_mol2atom.c b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_mol2atom.c similarity index 90% rename from INCHI-1-SRC/INCHI_API/inchi_main/e_mol2atom.c rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_mol2atom.c index 93ce852..08054f3 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_main/e_mol2atom.c +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_mol2atom.c @@ -1,681 +1,677 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "e_mode.h" - -#include "inchi_api.h" - -#include "e_ctl_data.h" -#include "e_comdef.h" -#include "e_util.h" -#include "e_ichicomp.h" - -#include "e_readmol.h" -#include "e_inpdef.h" - -#if( ADD_CMLPP == 1 ) -#include "e_readcml.hpp" -#endif - -#include "e_inchi_atom.h" - - -#define MIN_STDATA_X_COORD 0.0 -#define MAX_STDATA_X_COORD 256.0 -#define MIN_STDATA_Y_COORD 0.0 -#define MAX_STDATA_Y_COORD 256.0 -#define MIN_STDATA_Z_COORD 0.0 -#define MAX_STDATA_Z_COORD 256.0 -#define MAX_STDATA_AVE_BOND_LENGTH 20.0 -#define MIN_STDATA_AVE_BOND_LENGTH 10.0 - - -/* local prototypes */ -inchi_Atom* mol_to_inchi_Atom( MOL_DATA* mol_data, int *num_atoms, int *num_bonds, inchi_Atom* at_inp, - int bDoNotAddH, int *err, char *pStrErr ); -int mol_to_inchi_Atom_xyz( MOL_DATA* mol_data, int num_atoms, inchi_Atom* at, int *err, char *pStrErr ); - -int MolfileToInchi_Atom( FILE *inp_molfile, int bDoNotAddH, inchi_Atom **at, int max_num_at, - int *num_dimensions, int *num_bonds, const char *pSdfLabel, char *pSdfValue, - long *Id, long *lMolfileNumber, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ); -long GetMolfileNumber( MOL_HEADER_BLOCK *pHdr ); - - - /* too long ave. bond length prevents structure from displaying */ - /* According to Steve, a standard bond length is 10. 9-24-97 DCh */ - /* Ave. bond length in MainLib is 20. Also fixed Average */ - /* bond length calculation by introducing num_avg_bonds */ - /* in mol_to_stdata(). 12-9-99 DCh. */ - -/******************************************************************************************************/ -inchi_Atom* mol_to_inchi_Atom( MOL_DATA* mol_data, int *num_atoms, int *num_bonds, inchi_Atom* at_inp, - int bDoNotAddH, int *err, char *pStrErr ) -{ - inchi_Atom *at = NULL; - /* char *bond_stereo = NULL; */ - AT_NUM *p1, *p2; - int i, a1, a2, n1, n2, bonds; - S_CHAR cBondStereo, cBondType; - S_CHAR cStereo1, cStereo2; - - *err = 0; - *num_atoms = *num_bonds = 0; - /* check if MOLfile contains atoms */ - if ( !mol_data || !mol_data->ctab.MolAtom || - 0 < mol_data->ctab.nNumberOfBonds && !mol_data->ctab.MolBond || - 0 >= (*num_atoms = mol_data->ctab.nNumberOfAtoms) ) { - /* MOLFILE_ERR_SET (*err, 0, "Empty structure"); */ - goto exit_function; /* no structure */ - } - /* allocate memory if necessary */ - if ( at_inp ) { - at = at_inp; - } else - if ( !(at = e_CreateInchi_Atom( *num_atoms ) ) ) { - *err = -1; - MOLFILE_ERR_FIN (*err, -1, exit_function, "Out of RAM"); - } - - /* copy atom info */ - for ( i = 0; i < *num_atoms; i ++ ) { - e_mystrncpy( at[i].elname, mol_data->ctab.MolAtom[i].szAtomSymbol, sizeof(at->elname) ); - /* at[i].chem_bonds_valence = mol_data->ctab.MolAtom[i].cValence; */ /* MOLfile valence; will change */ - at[i].isotopic_mass = mol_data->ctab.MolAtom[i].cMassDifference; - at[i].charge = mol_data->ctab.MolAtom[i].cCharge; - at[i].radical = mol_data->ctab.MolAtom[i].cRadical; - /* coordinates are copied in mol_to_inchi_Atom_xyz() */ -#if( SINGLET_IS_TRIPLET == 1 ) - if ( at[i].radical == RADICAL_SINGLET ) { - at[i].radical = RADICAL_TRIPLET; - } -#endif - /* removed parsing at[i].elname to extract H, charge, radical from the - Molfile alias record now this is done in the INChI dll */ - } - - /* copy bond info */ - for ( i = 0, bonds = 0; i < mol_data->ctab.nNumberOfBonds; i ++ ) { - cBondStereo = mol_data->ctab.MolBond[i].cBondStereo; - cBondType = mol_data->ctab.MolBond[i].cBondType; - a1 = mol_data->ctab.MolBond[i].nAtomNo1-1; - a2 = mol_data->ctab.MolBond[i].nAtomNo2-1; - - if ( a1 < 0 || a1 >= *num_atoms || - a2 < 0 || a2 >= *num_atoms || - a1 == a2 ) { - *err |= 1; /* bond for impossible atom number(s); ignored */ - MOLFILE_ERR_SET (*err, 0, "Bond to nonexistent atom"); - continue; - } - /* check for multiple bonds between same atoms */ - p1 = e_is_in_the_slist( at[a1].neighbor, (AT_NUM)a2, at[a1].num_bonds ); - p2 = e_is_in_the_slist( at[a2].neighbor, (AT_NUM)a1, at[a2].num_bonds ); - if ( (p1 || p2) && (p1 || at[a1].num_bonds < MAXVAL) && (p2 || at[a2].num_bonds < MAXVAL) ) { - n1 = p1? (p1 - at[a1].neighbor) : at[a1].num_bonds ++; - n2 = p2? (p2 - at[a2].neighbor) : at[a2].num_bonds ++; - MOLFILE_ERR_SET (*err, 0, "Multiple bonds between two atoms"); - *err |= 2; /* multiple bonds between atoms */ - } else - if ( !p1 && !p2 && at[a1].num_bonds < MAXVAL && at[a2].num_bonds < MAXVAL ) { - n1 = at[a1].num_bonds ++; - n2 = at[a2].num_bonds ++; - bonds ++; - } else { - char szMsg[64]; - *err |= 4; /* too large number of bonds. Some bonds ignored. */ - sprintf( szMsg, "Atom '%s' has more than %d bonds", - at[a1].num_bonds>= MAXVAL? at[a1].elname:at[a2].elname, MAXVAL ); - MOLFILE_ERR_SET (*err, 0, szMsg); - continue; - } - if ( cBondType < MIN_INPUT_BOND_TYPE || cBondType > MAX_INPUT_BOND_TYPE ) { - char szBondType[16]; - sprintf( szBondType, "%d", cBondType ); - cBondType = 1; - MOLFILE_ERR_SET (*err, 0, "Unrecognized bond type:"); - MOLFILE_ERR_SET (*err, 0, szBondType); - *err |= 8; /* Unrecognized Bond type replaced with single bond */ - } - /* bond type */ - at[a1].bond_type[n1] = - at[a2].bond_type[n2] = cBondType; - /* connection */ - at[a1].neighbor[n1] = (AT_NUM)a2; - at[a2].neighbor[n2] = (AT_NUM)a1; - /* stereo */ - switch ( cBondStereo ) { - case INPUT_STEREO_DBLE_EITHER: /* 3 */ - cStereo1 = INCHI_BOND_STEREO_DOUBLE_EITHER; - cStereo2 = INCHI_BOND_STEREO_DOUBLE_EITHER; - break; - case INPUT_STEREO_SNGL_UP: /* 1 */ - cStereo1 = INCHI_BOND_STEREO_SINGLE_1UP; - cStereo2 = INCHI_BOND_STEREO_SINGLE_2UP; - break; - case INPUT_STEREO_SNGL_EITHER: /* 4 */ - cStereo1 = INCHI_BOND_STEREO_SINGLE_1EITHER; - cStereo2 = INCHI_BOND_STEREO_SINGLE_2EITHER; - break; - case INPUT_STEREO_SNGL_DOWN: /* 6 */ - cStereo1 = INCHI_BOND_STEREO_SINGLE_1DOWN; - cStereo2 = INCHI_BOND_STEREO_SINGLE_2DOWN; - break; - case 0: - cStereo1 = INCHI_BOND_STEREO_NONE; - cStereo2 = INCHI_BOND_STEREO_NONE; - break; - default: - *err |= 16; /* Ignored unrecognized Bond stereo */ - MOLFILE_ERR_SET (*err, 0, "Unrecognized bond stereo"); - continue; - } - at[a1].bond_stereo[n1] = cStereo1; /* >0: the wedge (pointed) end is at this atom */ - at[a2].bond_stereo[n2] = cStereo2; /* <0: the wedge (pointed) end is at the opposite atom */ - } - *num_bonds = bonds; - - /* special Molfile valences */ - for ( a1 = 0; a1 < *num_atoms; a1 ++ ) { - int num_bond_type[MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE + 1], bond_type; - int chem_bonds_valence, valence; - if ( mol_data->ctab.MolAtom[a1].cValence && - (mol_data->ctab.MolAtom[a1].cValence != 15 || at[a1].num_bonds) ) { - /* Molfile contains special valence => calculate number of H */ - memset( num_bond_type, 0, sizeof(num_bond_type) ); - valence = mol_data->ctab.MolAtom[a1].cValence; /* save atom valence if available */ - for ( n1 = 0; n1 < at[a1].num_bonds; n1 ++ ) { - bond_type = at[a1].bond_type[n1] - MIN_INPUT_BOND_TYPE; - if ( bond_type < 0 || bond_type > MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE ) { - bond_type = 0; - MOLFILE_ERR_SET (*err, 0, "Unknown bond type in MOLfile assigned as a single bond"); - } - num_bond_type[ bond_type ] ++; - } - chem_bonds_valence = 0; - for ( n1 = 0; MIN_INPUT_BOND_TYPE + n1 <= 3 && MIN_INPUT_BOND_TYPE + n1 <= MAX_INPUT_BOND_TYPE; n1 ++ ) { - chem_bonds_valence += (MIN_INPUT_BOND_TYPE + n1) * num_bond_type[n1]; - } - if ( MIN_INPUT_BOND_TYPE <= INCHI_BOND_TYPE_ALTERN && INCHI_BOND_TYPE_ALTERN <= MAX_INPUT_BOND_TYPE && - ( n2 = num_bond_type[INCHI_BOND_TYPE_ALTERN-MIN_INPUT_BOND_TYPE] ) ) { - /* accept input aromatic bonds for now */ - switch ( n2 ) { - case 2: - chem_bonds_valence += 3; /* =A- */ - break; - case 3: - chem_bonds_valence += 4; /* =A< */ - break; - default: - /* if 1 or >= 4 aromatic bonds then replace such bonds with single bonds */ - for ( n1 = 0; n1 < at[a1].num_bonds; n1 ++ ) { - if ( at[a1].bond_type[n1] == INCHI_BOND_TYPE_ALTERN ) { - a2 = at[a1].neighbor[n1]; - p1 = e_is_in_the_slist( at[a2].neighbor, (AT_NUM)a1, at[a2].num_bonds ); - if ( p1 ) { - at[a1].bond_type[n1] = - at[a2].bond_type[p1-at[a2].neighbor] = INCHI_BOND_TYPE_SINGLE; - } else { - *err = -2; /* Program error */ - MOLFILE_ERR_SET (*err, 0, "Program error interpreting MOLfile"); - goto exit_function; /* no structure */ - } - } - } - chem_bonds_valence += n2; - *err |= 32; - MOLFILE_ERR_SET (*err, 0, "Atom has more than 3 aromatic bonds"); - break; - } - } - /************************************************************************************* - * - * Set number of hydrogen atoms - */ - if ( valence >= chem_bonds_valence ) { - at[a1].num_iso_H[0] = valence - chem_bonds_valence; - } - } else - if ( mol_data->ctab.MolAtom[a1].cAtomAliasedFlag ) { - at[a1].num_iso_H[0] = 0; - } else - if ( mol_data->ctab.MolAtom[a1].cValence == 15 && !at[a1].num_bonds ) { - at[a1].num_iso_H[0] = 0; - } else - if ( !bDoNotAddH ) { - at[a1].num_iso_H[0] = -1; - } - } - -exit_function:; - return at; -} -/******************************************************************************************************/ -int mol_to_inchi_Atom_xyz( MOL_DATA* mol_data, int num_atoms, inchi_Atom* at, int *err, char *pStrErr ) -{ - int i, num_dimensions=0; - int num_bonds; - double max_x=-1.0e32, max_y=-1.0e32, max_z=-1.0e32; - double min_x= 1.0e32, min_y= 1.0e32, min_z= 1.0e32; - double macheps = 1.0e-10, small_coeff = 0.00001; - double x_coeff, y_coeff, z_coeff, coeff, average_bond_length; - - /* *err = 0; */ - /* check if MOLfile contains atoms */ - if ( !mol_data || !mol_data->ctab.MolAtom || - 0 < mol_data->ctab.nNumberOfBonds && !mol_data->ctab.MolBond || - 0 >= (num_atoms = mol_data->ctab.nNumberOfAtoms) ) { - goto exit_function; /* no structure */ - } - /* copy atom info */ - for ( i = 0; i < num_atoms; i ++ ) { - max_x = inchi_max(mol_data->ctab.MolAtom[i].fX, max_x); - min_x = inchi_min(mol_data->ctab.MolAtom[i].fX, min_x); - max_y = inchi_max(mol_data->ctab.MolAtom[i].fY, max_y); - min_y = inchi_min(mol_data->ctab.MolAtom[i].fY, min_y); - max_z = inchi_max(mol_data->ctab.MolAtom[i].fZ, max_z); - min_z = inchi_min(mol_data->ctab.MolAtom[i].fZ, min_z); - } - - /* copy bond info */ - num_bonds = 0; - average_bond_length = 0.0; - for ( i = 0; i < mol_data->ctab.nNumberOfBonds; i ++ ) { - int a1 = mol_data->ctab.MolBond[i].nAtomNo1-1; - int a2 = mol_data->ctab.MolBond[i].nAtomNo2-1; - double dx = mol_data->ctab.MolAtom[a1].fX-mol_data->ctab.MolAtom[a2].fX; - double dy = mol_data->ctab.MolAtom[a1].fY-mol_data->ctab.MolAtom[a2].fY; - double dz = mol_data->ctab.MolAtom[a1].fZ-mol_data->ctab.MolAtom[a2].fZ; - - if ( a1 < 0 || a1 >= num_atoms || - a2 < 0 || a2 >= num_atoms || - a1 == a2 ) { - *err |= 1; /* bond for impossible atom number(s); ignored */ - MOLFILE_ERR_SET (*err, 0, "Bond to nonexistent atom"); - continue; - } - average_bond_length += sqrt( dx*dx + dy*dy + dz*dz ); - num_bonds ++; - } - - /* convert to integral coordinates */ - - if ( max_x - min_x <= small_coeff*(fabs(max_x) + fabs(min_x)) ) - x_coeff = 0.0; - else - x_coeff = (MAX_STDATA_X_COORD - MIN_STDATA_X_COORD)/(max_x - min_x); - - if ( max_y - min_y <= small_coeff*(fabs(max_y) + fabs(min_y)) ) - y_coeff = 0.0; - else - y_coeff = (MAX_STDATA_Y_COORD - MIN_STDATA_Y_COORD)/(max_y - min_y); - if ( max_z - min_z <= small_coeff*(fabs(max_z) + fabs(min_z)) ) - z_coeff = 0.0; - else - z_coeff = (MAX_STDATA_Z_COORD - MIN_STDATA_Z_COORD)/(max_z - min_z); - - num_dimensions = ((x_coeff > macheps || y_coeff >macheps ) && fabs(z_coeff) < macheps)? 2: - (fabs(z_coeff) > macheps)? 3: 0; - - switch ( num_dimensions ) { - case 0: - coeff = 0.0; - break; - case 2: - /* choose the smallest stretching coefficient */ - if ( x_coeff > macheps && y_coeff > macheps ) { - coeff = inchi_min( x_coeff, y_coeff ); - }else - if ( x_coeff > macheps ){ - coeff = x_coeff; - }else - if ( y_coeff > macheps ){ - coeff = y_coeff; - }else{ - coeff = 1.0; - } - break; - case 3: - /* choose the smallest stretching coefficient */ - if ( x_coeff > macheps && y_coeff > macheps ) { - coeff = inchi_min( x_coeff, y_coeff ); - coeff = inchi_min( coeff, z_coeff ); - }else - if ( x_coeff > macheps ){ - coeff = inchi_min( x_coeff, z_coeff ); - }else - if ( y_coeff > macheps ){ - coeff = inchi_min( y_coeff, z_coeff ); - }else{ - coeff = z_coeff; - } - break; - default: - coeff = 0.0; - } - - if ( num_bonds > 0 ) { - average_bond_length /= (double)num_bonds; - if ( average_bond_length * coeff > MAX_STDATA_AVE_BOND_LENGTH ) { - coeff = MAX_STDATA_AVE_BOND_LENGTH / average_bond_length; /* avoid too long bonds */ - } else - if ( average_bond_length * coeff < macheps ) { - coeff = 1.0; /* all lengths are of zero length */ - } else - if ( average_bond_length * coeff < MIN_STDATA_AVE_BOND_LENGTH ) { - coeff = MIN_STDATA_AVE_BOND_LENGTH / average_bond_length; /* avoid too short bonds */ - } - } -#if( NORMALIZE_INP_COORD == 1 ) - /* set integral coordinates */ - for ( i = 0; i < num_atoms; i ++ ) { - double x = mol_data->ctab.MolAtom[i].fX; - double y = mol_data->ctab.MolAtom[i].fY; - double z = mol_data->ctab.MolAtom[i].fZ; - x = (x - min_x)*coeff + MIN_STDATA_X_COORD; - y = (y - min_y)*coeff + MIN_STDATA_Y_COORD; - z = (z - min_z)*coeff + MIN_STDATA_Z_COORD; - /* floor() behavior is not well defined for negative arguments. - * Use positive arguments only to get nearest integer. - */ - at[i].x = ( x >= 0.0 )? (int)floor( x + 0.5 ) : -(int)floor( -x + 0.5 ); - at[i].y = ( y >= 0.0 )? (int)floor( y + 0.5 ) : -(int)floor( -y + 0.5 ); - at[i].z = ( z >= 0.0 )? (int)floor( z + 0.5 ) : -(int)floor( -z + 0.5 ); - } -#else - /* set input coordinates */ - for ( i = 0; i < num_atoms; i ++ ) { - double x = mol_data->ctab.MolAtom[i].fX; - double y = mol_data->ctab.MolAtom[i].fY; - double z = mol_data->ctab.MolAtom[i].fZ; - at[i].x = x; - at[i].y = y; - at[i].z = z; - } -#endif - -exit_function:; - return num_dimensions; -} -/****************************************************************************/ -long GetMolfileNumber( MOL_HEADER_BLOCK *pHdr ) -{ - static char sStruct[] = "Structure #"; - static char sINCHI[] = INCHI_NAME; - long lMolfileNumber = 0; - char *p, *q = NULL; - if ( pHdr ) { - if ( !memicmp( pHdr->szMoleculeName, sStruct, sizeof(sStruct)-1 ) ) { - p = pHdr->szMoleculeName + sizeof(sStruct)-1; - lMolfileNumber = strtol( p, &q, 10 ); - p = pHdr->szMoleculeLine2; - if ( !q || *q || - memicmp( p, sINCHI, sizeof(sINCHI)-1) || - !strstr( p+sizeof(sINCHI)-1, "SDfile Output" ) ) { - lMolfileNumber = 0; - } - } - } - return lMolfileNumber; -} - -/****************************************************************************/ -int MolfileToInchi_Atom( FILE *inp_molfile, int bDoNotAddH, inchi_Atom **at, int max_num_at, - int *num_dimensions, int *num_bonds, const char *pSdfLabel, char *pSdfValue, - long *Id, long *lMolfileNumber, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ) -{ - int num_atoms = 0; - MOL_DATA *mol_data = NULL; - MOL_HEADER_BLOCK OnlyHeaderBlock, *pOnlyHeaderBlock = NULL, *pHdr; - MOL_CTAB OnlyCtab, *pOnlyCtab = NULL; - char cSdfValueFirstChar = '\0'; -#ifdef CML_DEBUG - FILE *f_p; -#endif - if ( at ) { - pOnlyHeaderBlock = NULL; - if ( *at && max_num_at ) { - memset( *at, 0, max_num_at * sizeof(**at) ); - } - } else { - pOnlyHeaderBlock = &OnlyHeaderBlock; - pOnlyCtab = &OnlyCtab; - } - if ( pSdfValue ) { - cSdfValueFirstChar = pSdfValue[0]; - pSdfValue[0] = '\0'; - } - - mol_data = e_read_sdfile_segment(inp_molfile, pOnlyHeaderBlock, pOnlyCtab, 0, NULL, 0, Id, pSdfLabel, pSdfValue, err, pStrErr ); - - pHdr = ( mol_data && !pOnlyHeaderBlock )? &mol_data->hdr : - ( !mol_data && pOnlyHeaderBlock )? pOnlyHeaderBlock : NULL; - if ( lMolfileNumber && pHdr ) { - *lMolfileNumber = GetMolfileNumber( pHdr ); - } - if ( pSdfValue && - pSdfLabel && pSdfLabel[0] && pHdr ) { - if ( !stricmp(pSdfLabel, "MOLFILENAME") ) { - e_mystrncpy( pSdfValue, pHdr->szMoleculeName, MAX_SDF_VALUE+1 ); - e_LtrimRtrim( pSdfValue, NULL ); - } else - if ( !stricmp(pSdfLabel, "MOLFILELINE2") ) { - e_mystrncpy( pSdfValue, pHdr->szMoleculeLine2, MAX_SDF_VALUE+1 ); - e_LtrimRtrim( pSdfValue, NULL ); - } else - if ( !stricmp(pSdfLabel, "MOLFILECOMMENT") ) { - e_mystrncpy( pSdfValue, pHdr->szComment, MAX_SDF_VALUE+1 ); - e_LtrimRtrim( pSdfValue, NULL ); - } - if ( !pSdfValue[0] ) { - pSdfValue[0] = cSdfValueFirstChar; - } - } - - if ( mol_data && at && !*err ) { - /* *at points to an allocated memory */ - if ( *at && mol_data->ctab.nNumberOfAtoms <= max_num_at ) { - *at = mol_to_inchi_Atom( mol_data, &num_atoms, num_bonds, *at, bDoNotAddH, err, pStrErr ); - if ( *err >= 0 ) { - *num_dimensions = mol_to_inchi_Atom_xyz( mol_data, num_atoms, *at, err, pStrErr ); - } - } else - /* *at points to NULL */ - if ( !*at && mol_data->ctab.nNumberOfAtoms <= max_num_at ) { - *at = mol_to_inchi_Atom( mol_data, &num_atoms, num_bonds, *at, bDoNotAddH, err, pStrErr ); - if ( *err >= 0 ) { - *num_dimensions = mol_to_inchi_Atom_xyz( mol_data, num_atoms, *at, err, pStrErr ); - } - } else { - MOLFILE_ERR_SET (*err, 0, "Too many atoms"); - *err = 70; - num_atoms = -1; - } - if ( *err > 0 ) { - *err += 100; - } - /* 11-16-2004: use Chiral flag */ - if ( num_atoms > 0 && at && *at && mol_data && pInpAtomFlags ) { - if ( mol_data->ctab.cChiralFlag ) { - *pInpAtomFlags |= FLAG_INP_AT_CHIRAL; - } else { - *pInpAtomFlags |= FLAG_INP_AT_NONCHIRAL; - } - } - } else - if ( !at ) { - num_atoms = pOnlyCtab->nNumberOfAtoms; - } - - if ( !pOnlyHeaderBlock ) { - e_delete_mol_data( mol_data ); - } -#ifdef CML_DEBUG - puts ("MOL"); - f_p = fopen ("mol.dbg", "a"); - if (f_p) - { - PrintInpAtom (f_p, *at, num_atoms); - fclose (f_p); - } - else - { - puts ("Couldn't open file"); - } -#endif - - return num_atoms; -} -/**********************************************************************************/ -int e_MolfileToInchi_Input( FILE *inp_molfile, inchi_Input *orig_at_data, int bMergeAllInputStructures, - int bDoNotAddH, int bAllowEmptyStructure, - const char *pSdfLabel, char *pSdfValue, long *lSdfId, long *lMolfileNumber, - INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ) -{ - /* inp_ATOM *at = NULL; */ - int num_dimensions_new; - int num_inp_bonds_new; - int num_inp_atoms_new; - inchi_Atom *at_new = NULL; - inchi_Atom *at_old = NULL; - int nNumAtoms = 0; - int i, j; - - if ( pStrErr ) { - pStrErr[0] = '\0'; - } - - /*FreeOrigAtData( orig_at_data );*/ - - do { - - at_old = orig_at_data? orig_at_data->atom : NULL; /* save pointer to the previous allocation */ - num_inp_atoms_new = - MolfileToInchi_Atom( inp_molfile, bDoNotAddH, orig_at_data? &at_new:NULL, MAX_ATOMS, - &num_dimensions_new, &num_inp_bonds_new, - pSdfLabel, pSdfValue, lSdfId, lMolfileNumber, pInpAtomFlags, err, pStrErr ); - - - if ( num_inp_atoms_new <= 0 && !*err ) { - if ( !bAllowEmptyStructure ) { - MOLFILE_ERR_SET (*err, 0, "Empty structure"); /* the message will be issued by the InChI library */ - } - *err = 98; - } else - if ( orig_at_data && !num_inp_atoms_new && 10 < *err && *err < 20 && orig_at_data->num_atoms > 0 && bMergeAllInputStructures ) { - *err = 0; /* end of file */ - break; - } else - if ( num_inp_atoms_new > 0 && orig_at_data ) { - /* merge pOrigDataTmp + orig_at_data => pOrigDataTmp; */ - nNumAtoms = num_inp_atoms_new + orig_at_data->num_atoms; - if ( nNumAtoms >= MAX_ATOMS ) { - MOLFILE_ERR_SET (*err, 0, "Too many atoms"); - *err = 70; - orig_at_data->num_atoms = -1; - } else - if ( !at_old ) { - /* the first structure */ - orig_at_data->atom = at_new; - at_new = NULL; - orig_at_data->num_atoms = num_inp_atoms_new; - } else - if ( orig_at_data->atom = e_CreateInchi_Atom( nNumAtoms ) ) { - /* switch at_new <--> orig_at_data->at; */ - if ( orig_at_data->num_atoms ) { - memcpy( orig_at_data->atom, at_old, orig_at_data->num_atoms * sizeof(orig_at_data->atom[0]) ); - /* adjust numbering in the newly read structure */ - for ( i = 0; i < num_inp_atoms_new; i ++ ) { - for ( j = 0; j < at_new[i].num_bonds; j ++ ) { - at_new[i].neighbor[j] += orig_at_data->num_atoms; - } - } - } - e_FreeInchi_Atom( &at_old ); - /* copy newly read structure */ - memcpy( orig_at_data->atom + orig_at_data->num_atoms, - at_new, - num_inp_atoms_new * sizeof(orig_at_data->atom[0]) ); - /* add other things */ - orig_at_data->num_atoms += num_inp_atoms_new; - } else { - MOLFILE_ERR_SET (*err, 0, "Out of RAM"); - *err = -1; - } - } else - if ( num_inp_atoms_new > 0 ) { - nNumAtoms += num_inp_atoms_new; - } - e_FreeInchi_Atom( &at_new ); - - } while ( !*err && bMergeAllInputStructures ); - /* - if ( !*err ) { - orig_at_data->num_components = - MarkDisconnectedComponents( orig_at_data ); - if ( orig_at_data->num_components == 0 ) { - MOLFILE_ERR_SET (*err, 0, "No components found"); - *err = 99; - } - if ( orig_at_data->num_components < 0 ) { - MOLFILE_ERR_SET (*err, 0, "Too many components"); - *err = 99; - } - } - */ - e_FreeInchi_Atom( &at_new ); - if ( *err ) { - e_FreeInchi_Input( orig_at_data ); - } - if ( *err && !(10 < *err && *err < 20) && pStrErr && !pStrErr[0] ) { - MOLFILE_ERR_SET (*err, 0, "Unknown error"); /* */ - } - return orig_at_data? orig_at_data->num_atoms : nNumAtoms; -} - +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "e_mode.h" + +#include "../../../../INCHI_BASE/src/inchi_api.h" + +#include "e_ctl_data.h" +#include "e_comdef.h" +#include "e_util.h" +#include "e_ichicomp.h" + +#include "e_readmol.h" +#include "e_inpdef.h" + +#include "e_inchi_atom.h" + + +#define MIN_STDATA_X_COORD 0.0 +#define MAX_STDATA_X_COORD 256.0 +#define MIN_STDATA_Y_COORD 0.0 +#define MAX_STDATA_Y_COORD 256.0 +#define MIN_STDATA_Z_COORD 0.0 +#define MAX_STDATA_Z_COORD 256.0 +#define MAX_STDATA_AVE_BOND_LENGTH 20.0 +#define MIN_STDATA_AVE_BOND_LENGTH 10.0 + + +/* local prototypes */ +inchi_Atom* mol_to_inchi_Atom( MOL_DATA* mol_data, int *num_atoms, int *num_bonds, inchi_Atom* at_inp, + int bDoNotAddH, int *err, char *pStrErr ); +int mol_to_inchi_Atom_xyz( MOL_DATA* mol_data, int num_atoms, inchi_Atom* at, int *err, char *pStrErr ); + +int MolfileToInchi_Atom( FILE *inp_molfile, int bDoNotAddH, inchi_Atom **at, int max_num_at, + int *num_dimensions, int *num_bonds, const char *pSdfLabel, char *pSdfValue, + long *Id, long *lMolfileNumber, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ); +long GetMolfileNumber( MOL_HEADER_BLOCK *pHdr ); + + + /* too long ave. bond length prevents structure from displaying */ + /* According to Steve, a standard bond length is 10. 9-24-97 DCh */ + /* Ave. bond length in MainLib is 20. Also fixed Average */ + /* bond length calculation by introducing num_avg_bonds */ + /* in mol_to_stdata(). 12-9-99 DCh. */ + +/******************************************************************************************************/ +inchi_Atom* mol_to_inchi_Atom( MOL_DATA* mol_data, int *num_atoms, int *num_bonds, inchi_Atom* at_inp, + int bDoNotAddH, int *err, char *pStrErr ) +{ + inchi_Atom *at = NULL; + /* char *bond_stereo = NULL; */ + AT_NUM *p1, *p2; + int i, a1, a2, n1, n2, bonds; + S_CHAR cBondStereo, cBondType; + S_CHAR cStereo1, cStereo2; + + *err = 0; + *num_atoms = *num_bonds = 0; + /* check if MOLfile contains atoms */ + if ( !mol_data || !mol_data->ctab.MolAtom || + 0 < mol_data->ctab.nNumberOfBonds && !mol_data->ctab.MolBond || + 0 >= (*num_atoms = mol_data->ctab.nNumberOfAtoms) ) { + /* MOLFILE_ERR_SET (*err, 0, "Empty structure"); */ + goto exit_function; /* no structure */ + } + /* allocate memory if necessary */ + if ( at_inp ) { + at = at_inp; + } else + if ( !(at = e_CreateInchi_Atom( *num_atoms ) ) ) { + *err = -1; + MOLFILE_ERR_FIN (*err, -1, exit_function, "Out of RAM"); + } + + /* copy atom info */ + for ( i = 0; i < *num_atoms; i ++ ) { + e_mystrncpy( at[i].elname, mol_data->ctab.MolAtom[i].szAtomSymbol, sizeof(at->elname) ); + /* at[i].chem_bonds_valence = mol_data->ctab.MolAtom[i].cValence; */ /* MOLfile valence; will change */ + at[i].isotopic_mass = mol_data->ctab.MolAtom[i].cMassDifference; + at[i].charge = mol_data->ctab.MolAtom[i].cCharge; + at[i].radical = mol_data->ctab.MolAtom[i].cRadical; + /* coordinates are copied in mol_to_inchi_Atom_xyz() */ +#if( SINGLET_IS_TRIPLET == 1 ) + if ( at[i].radical == RADICAL_SINGLET ) { + at[i].radical = RADICAL_TRIPLET; + } +#endif + /* removed parsing at[i].elname to extract H, charge, radical from the + Molfile alias record now this is done in the INChI dll */ + } + + /* copy bond info */ + for ( i = 0, bonds = 0; i < mol_data->ctab.nNumberOfBonds; i ++ ) { + cBondStereo = mol_data->ctab.MolBond[i].cBondStereo; + cBondType = mol_data->ctab.MolBond[i].cBondType; + a1 = mol_data->ctab.MolBond[i].nAtomNo1-1; + a2 = mol_data->ctab.MolBond[i].nAtomNo2-1; + + if ( a1 < 0 || a1 >= *num_atoms || + a2 < 0 || a2 >= *num_atoms || + a1 == a2 ) { + *err |= 1; /* bond for impossible atom number(s); ignored */ + MOLFILE_ERR_SET (*err, 0, "Bond to nonexistent atom"); + continue; + } + /* check for multiple bonds between same atoms */ + p1 = e_is_in_the_slist( at[a1].neighbor, (AT_NUM)a2, at[a1].num_bonds ); + p2 = e_is_in_the_slist( at[a2].neighbor, (AT_NUM)a1, at[a2].num_bonds ); + if ( (p1 || p2) && (p1 || at[a1].num_bonds < MAXVAL) && (p2 || at[a2].num_bonds < MAXVAL) ) { + n1 = p1? (p1 - at[a1].neighbor) : at[a1].num_bonds ++; + n2 = p2? (p2 - at[a2].neighbor) : at[a2].num_bonds ++; + MOLFILE_ERR_SET (*err, 0, "Multiple bonds between two atoms"); + *err |= 2; /* multiple bonds between atoms */ + } else + if ( !p1 && !p2 && at[a1].num_bonds < MAXVAL && at[a2].num_bonds < MAXVAL ) { + n1 = at[a1].num_bonds ++; + n2 = at[a2].num_bonds ++; + bonds ++; + } else { + char szMsg[64]; + *err |= 4; /* too large number of bonds. Some bonds ignored. */ + sprintf( szMsg, "Atom '%s' has more than %d bonds", + at[a1].num_bonds>= MAXVAL? at[a1].elname:at[a2].elname, MAXVAL ); + MOLFILE_ERR_SET (*err, 0, szMsg); + continue; + } + if ( cBondType < MIN_INPUT_BOND_TYPE || cBondType > MAX_INPUT_BOND_TYPE ) { + char szBondType[16]; + sprintf( szBondType, "%d", cBondType ); + cBondType = 1; + MOLFILE_ERR_SET (*err, 0, "Unrecognized bond type:"); + MOLFILE_ERR_SET (*err, 0, szBondType); + *err |= 8; /* Unrecognized Bond type replaced with single bond */ + } + /* bond type */ + at[a1].bond_type[n1] = + at[a2].bond_type[n2] = cBondType; + /* connection */ + at[a1].neighbor[n1] = (AT_NUM)a2; + at[a2].neighbor[n2] = (AT_NUM)a1; + /* stereo */ + switch ( cBondStereo ) { + case INPUT_STEREO_DBLE_EITHER: /* 3 */ + cStereo1 = INCHI_BOND_STEREO_DOUBLE_EITHER; + cStereo2 = INCHI_BOND_STEREO_DOUBLE_EITHER; + break; + case INPUT_STEREO_SNGL_UP: /* 1 */ + cStereo1 = INCHI_BOND_STEREO_SINGLE_1UP; + cStereo2 = INCHI_BOND_STEREO_SINGLE_2UP; + break; + case INPUT_STEREO_SNGL_EITHER: /* 4 */ + cStereo1 = INCHI_BOND_STEREO_SINGLE_1EITHER; + cStereo2 = INCHI_BOND_STEREO_SINGLE_2EITHER; + break; + case INPUT_STEREO_SNGL_DOWN: /* 6 */ + cStereo1 = INCHI_BOND_STEREO_SINGLE_1DOWN; + cStereo2 = INCHI_BOND_STEREO_SINGLE_2DOWN; + break; + case 0: + cStereo1 = INCHI_BOND_STEREO_NONE; + cStereo2 = INCHI_BOND_STEREO_NONE; + break; + default: + *err |= 16; /* Ignored unrecognized Bond stereo */ + MOLFILE_ERR_SET (*err, 0, "Unrecognized bond stereo"); + continue; + } + at[a1].bond_stereo[n1] = cStereo1; /* >0: the wedge (pointed) end is at this atom */ + at[a2].bond_stereo[n2] = cStereo2; /* <0: the wedge (pointed) end is at the opposite atom */ + } + *num_bonds = bonds; + + /* special Molfile valences */ + for ( a1 = 0; a1 < *num_atoms; a1 ++ ) { + int num_bond_type[MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE + 1], bond_type; + int chem_bonds_valence, valence; + if ( mol_data->ctab.MolAtom[a1].cValence && + (mol_data->ctab.MolAtom[a1].cValence != 15 || at[a1].num_bonds) ) { + /* Molfile contains special valence => calculate number of H */ + memset( num_bond_type, 0, sizeof(num_bond_type) ); + valence = mol_data->ctab.MolAtom[a1].cValence; /* save atom valence if available */ + for ( n1 = 0; n1 < at[a1].num_bonds; n1 ++ ) { + bond_type = at[a1].bond_type[n1] - MIN_INPUT_BOND_TYPE; + if ( bond_type < 0 || bond_type > MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE ) { + bond_type = 0; + MOLFILE_ERR_SET (*err, 0, "Unknown bond type in MOLfile assigned as a single bond"); + } + num_bond_type[ bond_type ] ++; + } + chem_bonds_valence = 0; + for ( n1 = 0; MIN_INPUT_BOND_TYPE + n1 <= 3 && MIN_INPUT_BOND_TYPE + n1 <= MAX_INPUT_BOND_TYPE; n1 ++ ) { + chem_bonds_valence += (MIN_INPUT_BOND_TYPE + n1) * num_bond_type[n1]; + } + if ( MIN_INPUT_BOND_TYPE <= INCHI_BOND_TYPE_ALTERN && INCHI_BOND_TYPE_ALTERN <= MAX_INPUT_BOND_TYPE && + ( n2 = num_bond_type[INCHI_BOND_TYPE_ALTERN-MIN_INPUT_BOND_TYPE] ) ) { + /* accept input aromatic bonds for now */ + switch ( n2 ) { + case 2: + chem_bonds_valence += 3; /* =A- */ + break; + case 3: + chem_bonds_valence += 4; /* =A< */ + break; + default: + /* if 1 or >= 4 aromatic bonds then replace such bonds with single bonds */ + for ( n1 = 0; n1 < at[a1].num_bonds; n1 ++ ) { + if ( at[a1].bond_type[n1] == INCHI_BOND_TYPE_ALTERN ) { + a2 = at[a1].neighbor[n1]; + p1 = e_is_in_the_slist( at[a2].neighbor, (AT_NUM)a1, at[a2].num_bonds ); + if ( p1 ) { + at[a1].bond_type[n1] = + at[a2].bond_type[p1-at[a2].neighbor] = INCHI_BOND_TYPE_SINGLE; + } else { + *err = -2; /* Program error */ + MOLFILE_ERR_SET (*err, 0, "Program error interpreting MOLfile"); + goto exit_function; /* no structure */ + } + } + } + chem_bonds_valence += n2; + *err |= 32; + MOLFILE_ERR_SET (*err, 0, "Atom has more than 3 aromatic bonds"); + break; + } + } + /************************************************************************************* + * + * Set number of hydrogen atoms + */ + if ( valence >= chem_bonds_valence ) { + at[a1].num_iso_H[0] = valence - chem_bonds_valence; + } + } else + if ( mol_data->ctab.MolAtom[a1].cAtomAliasedFlag ) { + at[a1].num_iso_H[0] = 0; + } else + if ( mol_data->ctab.MolAtom[a1].cValence == 15 && !at[a1].num_bonds ) { + at[a1].num_iso_H[0] = 0; + } else + if ( !bDoNotAddH ) { + at[a1].num_iso_H[0] = -1; + } + } + +exit_function:; + return at; +} +/******************************************************************************************************/ +int mol_to_inchi_Atom_xyz( MOL_DATA* mol_data, int num_atoms, inchi_Atom* at, int *err, char *pStrErr ) +{ + int i, num_dimensions=0; + int num_bonds; + double max_x=-1.0e32, max_y=-1.0e32, max_z=-1.0e32; + double min_x= 1.0e32, min_y= 1.0e32, min_z= 1.0e32; + double macheps = 1.0e-10, small_coeff = 0.00001; + double x_coeff, y_coeff, z_coeff, coeff, average_bond_length; + + /* *err = 0; */ + /* check if MOLfile contains atoms */ + if ( !mol_data || !mol_data->ctab.MolAtom || + 0 < mol_data->ctab.nNumberOfBonds && !mol_data->ctab.MolBond || + 0 >= (num_atoms = mol_data->ctab.nNumberOfAtoms) ) { + goto exit_function; /* no structure */ + } + /* copy atom info */ + for ( i = 0; i < num_atoms; i ++ ) { + max_x = inchi_max(mol_data->ctab.MolAtom[i].fX, max_x); + min_x = inchi_min(mol_data->ctab.MolAtom[i].fX, min_x); + max_y = inchi_max(mol_data->ctab.MolAtom[i].fY, max_y); + min_y = inchi_min(mol_data->ctab.MolAtom[i].fY, min_y); + max_z = inchi_max(mol_data->ctab.MolAtom[i].fZ, max_z); + min_z = inchi_min(mol_data->ctab.MolAtom[i].fZ, min_z); + } + + /* copy bond info */ + num_bonds = 0; + average_bond_length = 0.0; + for ( i = 0; i < mol_data->ctab.nNumberOfBonds; i ++ ) { + int a1 = mol_data->ctab.MolBond[i].nAtomNo1-1; + int a2 = mol_data->ctab.MolBond[i].nAtomNo2-1; + double dx = mol_data->ctab.MolAtom[a1].fX-mol_data->ctab.MolAtom[a2].fX; + double dy = mol_data->ctab.MolAtom[a1].fY-mol_data->ctab.MolAtom[a2].fY; + double dz = mol_data->ctab.MolAtom[a1].fZ-mol_data->ctab.MolAtom[a2].fZ; + + if ( a1 < 0 || a1 >= num_atoms || + a2 < 0 || a2 >= num_atoms || + a1 == a2 ) { + *err |= 1; /* bond for impossible atom number(s); ignored */ + MOLFILE_ERR_SET (*err, 0, "Bond to nonexistent atom"); + continue; + } + average_bond_length += sqrt( dx*dx + dy*dy + dz*dz ); + num_bonds ++; + } + + /* convert to integral coordinates */ + + if ( max_x - min_x <= small_coeff*(fabs(max_x) + fabs(min_x)) ) + x_coeff = 0.0; + else + x_coeff = (MAX_STDATA_X_COORD - MIN_STDATA_X_COORD)/(max_x - min_x); + + if ( max_y - min_y <= small_coeff*(fabs(max_y) + fabs(min_y)) ) + y_coeff = 0.0; + else + y_coeff = (MAX_STDATA_Y_COORD - MIN_STDATA_Y_COORD)/(max_y - min_y); + if ( max_z - min_z <= small_coeff*(fabs(max_z) + fabs(min_z)) ) + z_coeff = 0.0; + else + z_coeff = (MAX_STDATA_Z_COORD - MIN_STDATA_Z_COORD)/(max_z - min_z); + + num_dimensions = ((x_coeff > macheps || y_coeff >macheps ) && fabs(z_coeff) < macheps)? 2: + (fabs(z_coeff) > macheps)? 3: 0; + + switch ( num_dimensions ) { + case 0: + coeff = 0.0; + break; + case 2: + /* choose the smallest stretching coefficient */ + if ( x_coeff > macheps && y_coeff > macheps ) { + coeff = inchi_min( x_coeff, y_coeff ); + }else + if ( x_coeff > macheps ){ + coeff = x_coeff; + }else + if ( y_coeff > macheps ){ + coeff = y_coeff; + }else{ + coeff = 1.0; + } + break; + case 3: + /* choose the smallest stretching coefficient */ + if ( x_coeff > macheps && y_coeff > macheps ) { + coeff = inchi_min( x_coeff, y_coeff ); + coeff = inchi_min( coeff, z_coeff ); + }else + if ( x_coeff > macheps ){ + coeff = inchi_min( x_coeff, z_coeff ); + }else + if ( y_coeff > macheps ){ + coeff = inchi_min( y_coeff, z_coeff ); + }else{ + coeff = z_coeff; + } + break; + default: + coeff = 0.0; + } + + if ( num_bonds > 0 ) { + average_bond_length /= (double)num_bonds; + if ( average_bond_length * coeff > MAX_STDATA_AVE_BOND_LENGTH ) { + coeff = MAX_STDATA_AVE_BOND_LENGTH / average_bond_length; /* avoid too long bonds */ + } else + if ( average_bond_length * coeff < macheps ) { + coeff = 1.0; /* all lengths are of zero length */ + } else + if ( average_bond_length * coeff < MIN_STDATA_AVE_BOND_LENGTH ) { + coeff = MIN_STDATA_AVE_BOND_LENGTH / average_bond_length; /* avoid too short bonds */ + } + } +#if( NORMALIZE_INP_COORD == 1 ) + /* set integral coordinates */ + for ( i = 0; i < num_atoms; i ++ ) { + double x = mol_data->ctab.MolAtom[i].fX; + double y = mol_data->ctab.MolAtom[i].fY; + double z = mol_data->ctab.MolAtom[i].fZ; + x = (x - min_x)*coeff + MIN_STDATA_X_COORD; + y = (y - min_y)*coeff + MIN_STDATA_Y_COORD; + z = (z - min_z)*coeff + MIN_STDATA_Z_COORD; + /* floor() behavior is not well defined for negative arguments. + * Use positive arguments only to get nearest integer. + */ + at[i].x = ( x >= 0.0 )? (int)floor( x + 0.5 ) : -(int)floor( -x + 0.5 ); + at[i].y = ( y >= 0.0 )? (int)floor( y + 0.5 ) : -(int)floor( -y + 0.5 ); + at[i].z = ( z >= 0.0 )? (int)floor( z + 0.5 ) : -(int)floor( -z + 0.5 ); + } +#else + /* set input coordinates */ + for ( i = 0; i < num_atoms; i ++ ) { + double x = mol_data->ctab.MolAtom[i].fX; + double y = mol_data->ctab.MolAtom[i].fY; + double z = mol_data->ctab.MolAtom[i].fZ; + at[i].x = x; + at[i].y = y; + at[i].z = z; + } +#endif + +exit_function:; + return num_dimensions; +} +/****************************************************************************/ +long GetMolfileNumber( MOL_HEADER_BLOCK *pHdr ) +{ + static char sStruct[] = "Structure #"; + static char sINCHI[] = INCHI_NAME; + long lMolfileNumber = 0; + char *p, *q = NULL; + if ( pHdr ) { + if ( !e_inchi_memicmp( pHdr->szMoleculeName, sStruct, sizeof(sStruct)-1 ) ) { + p = pHdr->szMoleculeName + sizeof(sStruct)-1; + lMolfileNumber = strtol( p, &q, 10 ); + p = pHdr->szMoleculeLine2; + if ( !q || *q || + e_inchi_memicmp( p, sINCHI, sizeof(sINCHI)-1) || + !strstr( p+sizeof(sINCHI)-1, "SDfile Output" ) ) { + lMolfileNumber = 0; + } + } + } + return lMolfileNumber; +} + + +int MolfileToInchi_Atom( FILE *inp_molfile, int bDoNotAddH, inchi_Atom **at, int max_num_at, + int *num_dimensions, int *num_bonds, const char *pSdfLabel, char *pSdfValue, + long *Id, long *lMolfileNumber, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ) +{ + int num_atoms = 0; + MOL_DATA *mol_data = NULL; + MOL_HEADER_BLOCK OnlyHeaderBlock, *pOnlyHeaderBlock = NULL, *pHdr; + MOL_CTAB OnlyCtab, *pOnlyCtab = NULL; + char cSdfValueFirstChar = '\0'; + + if ( at ) + { + pOnlyHeaderBlock = NULL; + if ( *at && max_num_at ) { + memset( *at, 0, max_num_at * sizeof(**at) ); + } + } + else + { + pOnlyHeaderBlock = &OnlyHeaderBlock; + pOnlyCtab = &OnlyCtab; + } + if ( pSdfValue ) + { + cSdfValueFirstChar = pSdfValue[0]; + pSdfValue[0] = '\0'; + } + + mol_data = e_read_sdfile_segment(inp_molfile, pOnlyHeaderBlock, pOnlyCtab, 0, NULL, 0, Id, pSdfLabel, pSdfValue, err, pStrErr ); + + if ( mol_data && !strcmp(mol_data->ctab.csCurrentCtabVersion, "V3000") ) + { + MOLFILE_ERR_SET (*err, 0, "Molfile V3000 input is not supported"); + *err = 71 + 100; + num_atoms = -1; + } + + pHdr = ( mol_data && !pOnlyHeaderBlock )? &mol_data->hdr : + ( !mol_data && pOnlyHeaderBlock )? pOnlyHeaderBlock : NULL; + if ( lMolfileNumber && pHdr ) { + *lMolfileNumber = GetMolfileNumber( pHdr ); + } + if ( pSdfValue && + pSdfLabel && pSdfLabel[0] && pHdr ) { + if ( !e_inchi_stricmp(pSdfLabel, "MOLFILENAME") ) { + e_mystrncpy( pSdfValue, pHdr->szMoleculeName, MAX_SDF_VALUE+1 ); + e_LtrimRtrim( pSdfValue, NULL ); + } else + if ( !e_inchi_stricmp(pSdfLabel, "MOLFILELINE2") ) { + e_mystrncpy( pSdfValue, pHdr->szMoleculeLine2, MAX_SDF_VALUE+1 ); + e_LtrimRtrim( pSdfValue, NULL ); + } else + if ( !e_inchi_stricmp(pSdfLabel, "MOLFILECOMMENT") ) { + e_mystrncpy( pSdfValue, pHdr->szComment, MAX_SDF_VALUE+1 ); + e_LtrimRtrim( pSdfValue, NULL ); + } + if ( !pSdfValue[0] ) { + pSdfValue[0] = cSdfValueFirstChar; + } + } + + if ( mol_data && at && !*err ) { + /* *at points to an allocated memory */ + if ( *at && mol_data->ctab.nNumberOfAtoms <= max_num_at ) { + *at = mol_to_inchi_Atom( mol_data, &num_atoms, num_bonds, *at, bDoNotAddH, err, pStrErr ); + if ( *err >= 0 ) { + *num_dimensions = mol_to_inchi_Atom_xyz( mol_data, num_atoms, *at, err, pStrErr ); + } + } else + /* *at points to NULL */ + if ( !*at && mol_data->ctab.nNumberOfAtoms <= max_num_at ) { + *at = mol_to_inchi_Atom( mol_data, &num_atoms, num_bonds, *at, bDoNotAddH, err, pStrErr ); + if ( *err >= 0 ) { + *num_dimensions = mol_to_inchi_Atom_xyz( mol_data, num_atoms, *at, err, pStrErr ); + } + } else { + MOLFILE_ERR_SET (*err, 0, "Too many atoms"); + *err = 70; + num_atoms = -1; + } + if ( *err > 0 ) { + *err += 100; + } + /* 11-16-2004: use Chiral flag */ + if ( num_atoms > 0 && at && *at && mol_data && pInpAtomFlags ) { + if ( mol_data->ctab.cChiralFlag ) { + *pInpAtomFlags |= FLAG_INP_AT_CHIRAL; + } else { + *pInpAtomFlags |= FLAG_INP_AT_NONCHIRAL; + } + } + } else + if ( !at ) { + num_atoms = pOnlyCtab->nNumberOfAtoms; + } + + if ( !pOnlyHeaderBlock ) { + e_delete_mol_data( mol_data ); + } + + return num_atoms; +} + + +int e_MolfileToInchi_Input( FILE *inp_molfile, inchi_Input *orig_at_data, int bMergeAllInputStructures, + int bDoNotAddH, int bAllowEmptyStructure, + const char *pSdfLabel, char *pSdfValue, long *lSdfId, long *lMolfileNumber, + INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ) +{ + /* inp_ATOM *at = NULL; */ + int num_dimensions_new; + int num_inp_bonds_new; + int num_inp_atoms_new; + inchi_Atom *at_new = NULL; + inchi_Atom *at_old = NULL; + int nNumAtoms = 0; + int i, j; + + if ( pStrErr ) { + pStrErr[0] = '\0'; + } + + /*FreeOrigAtData( orig_at_data );*/ + + do { + + at_old = orig_at_data? orig_at_data->atom : NULL; /* save pointer to the previous allocation */ + num_inp_atoms_new = + MolfileToInchi_Atom( inp_molfile, + bDoNotAddH, + orig_at_data? &at_new : NULL, + NORMALLY_ALLOWED_INP_MAX_ATOMS, /*MAX_ATOMS,*/ + &num_dimensions_new, &num_inp_bonds_new, + pSdfLabel, pSdfValue, + lSdfId, lMolfileNumber, + pInpAtomFlags, + err, pStrErr ); + + + if ( num_inp_atoms_new <= 0 && !*err ) { + if ( !bAllowEmptyStructure ) { + MOLFILE_ERR_SET (*err, 0, "Empty structure"); /* the message will be issued by the InChI library */ + } + *err = 98; + } else + if ( orig_at_data && !num_inp_atoms_new && 10 < *err && *err < 20 && orig_at_data->num_atoms > 0 && bMergeAllInputStructures ) { + *err = 0; /* end of file */ + break; + } else + if ( num_inp_atoms_new > 0 && orig_at_data ) { + /* merge pOrigDataTmp + orig_at_data => pOrigDataTmp; */ + nNumAtoms = num_inp_atoms_new + orig_at_data->num_atoms; + if ( nNumAtoms >= MAX_ATOMS ) { + MOLFILE_ERR_SET (*err, 0, "Too many atoms"); + *err = 70; + orig_at_data->num_atoms = -1; + } else + if ( !at_old ) { + /* the first structure */ + orig_at_data->atom = at_new; + at_new = NULL; + orig_at_data->num_atoms = num_inp_atoms_new; + } else + if ( orig_at_data->atom = e_CreateInchi_Atom( nNumAtoms ) ) { + /* switch at_new <--> orig_at_data->at; */ + if ( orig_at_data->num_atoms ) { + memcpy( orig_at_data->atom, at_old, orig_at_data->num_atoms * sizeof(orig_at_data->atom[0]) ); + /* adjust numbering in the newly read structure */ + for ( i = 0; i < num_inp_atoms_new; i ++ ) { + for ( j = 0; j < at_new[i].num_bonds; j ++ ) { + at_new[i].neighbor[j] += orig_at_data->num_atoms; + } + } + } + e_FreeInchi_Atom( &at_old ); + /* copy newly read structure */ + memcpy( orig_at_data->atom + orig_at_data->num_atoms, + at_new, + num_inp_atoms_new * sizeof(orig_at_data->atom[0]) ); + /* add other things */ + orig_at_data->num_atoms += num_inp_atoms_new; + } else { + MOLFILE_ERR_SET (*err, 0, "Out of RAM"); + *err = -1; + } + } else + if ( num_inp_atoms_new > 0 ) { + nNumAtoms += num_inp_atoms_new; + } + e_FreeInchi_Atom( &at_new ); + } while ( !*err && bMergeAllInputStructures ); + /* + if ( !*err ) { + orig_at_data->num_components = + MarkDisconnectedComponents( orig_at_data ); + if ( orig_at_data->num_components == 0 ) { + MOLFILE_ERR_SET (*err, 0, "No components found"); + *err = 99; + } + if ( orig_at_data->num_components < 0 ) { + MOLFILE_ERR_SET (*err, 0, "Too many components"); + *err = 99; + } + } + */ + e_FreeInchi_Atom( &at_new ); + if ( *err ) { + e_FreeInchi_Input( orig_at_data ); + } + if ( *err && !(10 < *err && *err < 20) && pStrErr && !pStrErr[0] ) { + MOLFILE_ERR_SET (*err, 0, "Unknown error"); /* */ + } + return orig_at_data? orig_at_data->num_atoms : nNumAtoms; +} diff --git a/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_mol_frmt.h b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_mol_frmt.h new file mode 100644 index 0000000..61d3894 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_mol_frmt.h @@ -0,0 +1,24 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * + */ + + + +#ifndef _E_MOL_FRMT_H_ +#define _E_MOL_FRMT_H_ + + + +/* + We borrow '../../INCHI_BASE/mol_frmt.c', that is include it into this app project. + Accordingly, we #include below 'mol_fmt.h' from InChI code base. +*/ + +#include "../../../../INCHI_BASE/src/mol_fmt.h" + + + +#endif /* _E_MOL_FRMT_H_ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/e_readinch.c b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_readinch.c similarity index 62% rename from INCHI-1-SRC/INCHI_API/inchi_main/e_readinch.c rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_readinch.c index 282101a..0307fe9 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_main/e_readinch.c +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_readinch.c @@ -1,90 +1,86 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include -#include -#include - -/* for use in the main program */ - - -#include "e_mode.h" - -#include "e_ichisize.h" - -#include "inchi_api.h" - -#include "e_ctl_data.h" -#include "e_inchi_atom.h" -#include "e_ichi_io.h" - - -#include "e_comdef.h" -#include "e_util.h" -#include "e_ichierr.h" -#include "e_ichicomp.h" -#include "e_readstru.h" -#include "e_inpdef.h" - -/* rename to avoid duplicated entry points */ - -#define mystrncpy e_mystrncpy -#define LtrimRtrim e_LtrimRtrim -#define FreeInchi_Atom e_FreeInchi_Atom -#define FreeInchi_Stereo0D e_FreeInchi_Stereo0D -#define CreateInchi_Atom e_CreateInchi_Atom -#define CreateInchi_Stereo0D e_CreateInchi_Stereo0D -#define FreeInchi_Input e_FreeInchi_Input -#define AddMOLfileError e_AddMOLfileError -#define is_in_the_slist e_is_in_the_slist -#define is_element_a_metal e_is_element_a_metal -#define INChIToInchi_Atom e_INChIToInchi_Atom -#define INChIToInchi_Input e_INChIToInchi_Input - -#define IGNORE_HEADERS -#define STATIC - -/* This contains executable code. Included in lReadAux.c, e_ReadINCH.c, ReadINCH.c, */ -#include "../inchi_dll/aux2atom.h" - +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include +#include +#include +#include +#include +#include + +/* for use in the main program */ + + +#include "e_mode.h" + +#include "../../../../INCHI_BASE/src/inchi_api.h" + +#include "e_ichisize.h" +#include "e_ctl_data.h" +#include "e_inchi_atom.h" +#include "e_ichi_io.h" + + +#include "e_comdef.h" +#include "e_util.h" +#include "e_ichierr.h" +#include "e_ichicomp.h" +#include "e_readstru.h" +#include "e_inpdef.h" + +/* rename to avoid duplicated entry points */ + +#define mystrncpy e_mystrncpy +#define LtrimRtrim e_LtrimRtrim +#define FreeInchi_Atom e_FreeInchi_Atom +#define FreeInchi_Stereo0D e_FreeInchi_Stereo0D +#define CreateInchi_Atom e_CreateInchi_Atom +#define CreateInchi_Stereo0D e_CreateInchi_Stereo0D +#define FreeInchi_Input e_FreeInchi_Input +#define AddMOLfileError e_AddMOLfileError +#define is_in_the_slist e_is_in_the_slist +#define is_element_a_metal e_is_element_a_metal +#define INChIToInchi_Atom e_INChIToInchi_Atom +#define INChIToInchi_Input e_INChIToInchi_Input + +#define IGNORE_HEADERS +#define STATIC + +#include "aux2atom.h" diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/lreadmol.h b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_readmol.c similarity index 92% rename from INCHI-1-SRC/INCHI_API/inchi_main/lreadmol.h rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_readmol.c index 667cf20..475b3a1 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_main/lreadmol.h +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_readmol.c @@ -1,1290 +1,1326 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -/* local prototypes */ -int bypass_sdf_data_items( FILE* inp, long *cas_reg_no, char* comment, int lcomment, char *name, int lname, int prev_err, - const char *pSdfLabel, char *pSdfValue, char *pStrErr ); -MOL_DATA* read_mol_file( FILE* inp, MOL_HEADER_BLOCK *OnlyHeaderBlock, MOL_CTAB *OnlyCtab, - int bGetOrigCoord, int *err, char *pStrErr ); - - -static int mol_read_hdr(MOL_HEADER_BLOCK *hdr, FILE* inp, char *pStrErr); -static int mol_read_counts_line( MOL_CTAB* ctab, FILE *inp, char *pStrErr ); -static int read_atom_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ); -static int read_bonds_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ); -static int read_stext_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ); -static int read_properties_block( MOL_CTAB* ctab, MOL_HEADER_BLOCK *pHdr, FILE *inp, int err, char *pStrErr ); - -static int identify_sdf_label( char* inp_line, const char *pSdfLabel ); -static long extract_cas_rn( char *line ); -static int mol_copy_check_empty( char* dest, char* source, int len, char **first_space ); -static int mol_read_datum(void* data, int field_len, int data_type, char** line_ptr); - -static int RemoveNonPrintable( char *line ); - - -/******/ -#ifndef MOLFILE_ERR_FIN -#define MOLFILE_ERR_FIN(err, new_err, err_fin, msg) \ - if ( !(err) && (new_err) ) { (err) = (new_err);} AddMOLfileError(pStrErr, (msg)); goto err_fin -#endif -#ifndef MOLFILE_ERR_SET -#define MOLFILE_ERR_SET(err, new_err, msg) \ - if ( !(err) && (new_err) ) { (err) = (new_err);} AddMOLfileError(pStrErr, (msg)) -#endif - -/*************************************************************************/ -int AddMOLfileError( char *pStrErr, const char *szMsg ) -{ - if ( pStrErr && szMsg && szMsg[0] ) { - int lenStrErr = strlen( pStrErr ); - int lenMsg = strlen( szMsg ); - char *p = strstr( pStrErr, szMsg ); - if ( p && (p==pStrErr || *(p-1) == ' ' && (*(p-2) == ';' || *(p-2) == ':' )) && - (p+lenMsg == pStrErr+lenStrErr || - p[lenMsg] == ';' && p[lenMsg+1] == ' ' || - p[lenMsg-1]==':' && p[lenMsg]==' ') ) { - return 1; /* reject duplicates */ - } - if ( lenStrErr + lenMsg + 2*(lenStrErr > 0) < STR_ERR_LEN ) { - /* enough room to add */ - if (lenStrErr > 0) { - if ( pStrErr[lenStrErr-1] != ':' ) { - strcat( pStrErr, ";" ); - } - strcat( pStrErr, " " ); - } - strcat( pStrErr, szMsg ); - return 1; - } - /* no room */ - if ( strstr( pStrErr, "..." ) ) { - return 0; /* no room mark has already been set */ - } - if ( lenStrErr + 3 < STR_ERR_LEN ) { - strcat( pStrErr, "..." ); - } - } - return 0; -} -/*************** static **********************************************************/ -int mol_copy_check_empty( char* dest, char* source, int len, char **first_space ) -{ - int i, c; /* required len >= 0; dest must have at least len+1 bytes */ - if ( len > 0 ) - strncpy( dest, source, len ); - dest[len]='\0'; - len = ( len > 0 )? (int)strlen( dest) : 0; - for ( i = (len-1); i >= 0 && 0 != (c = source[i]) && isspace(UCINT c); i-- ) - ; - *first_space = dest + (i+1); /* first blank or zero terminating byte in dest */ - return len; /* number of actually processed bytes; zero termination not included */ -} -/************* static ************************************************************/ -int mol_read_datum(void* data, int field_len, int data_type, char** line_ptr) -{ -/* 1. 'field_len' for MOL_STRING_DATA does not include trailing zero, - * that is actual length of the string pointed by 'data' - * should be at least field_len+1 bytes. - * For numerical data 'field_len' is length of input data field - * For numerical integral data field_len <= 0 means read up to first - * non-numeric character as strtod() does ("free format") - * 2. return value: for MOL_STRING_DATA: number of bytes excluding trailing zero - * for all others: 1=success; 0 = empty; -1= error - * 3. on exit *line_ptr points to the next byte after the last entered - */ - char *p = *line_ptr, *q, *p_end; - int i, ret=1, c, len; - long ldata; - double ddata; - - switch( data_type ) { - case MOL_STRING_DATA: - for ( i= 0; i < field_len && 0 != (c = p[i]) && isspace(UCINT c); i++ ) /* pass by all leading spaces */ - ; - len = mol_copy_check_empty( (char*)data, &p[i], field_len-i, &q ); - ret = ( q - (char*)data );/* actual data length */ - *q = '\0'; /* add zero termination to data if it is not there yet*/ - *line_ptr += (len+i); /* ptr to the 1st byte of the next input field or to zero termination */ - break; - - case MOL_CHAR_INT_DATA: - case MOL_SHORT_INT_DATA: - case MOL_LONG_INT_DATA: - { /* block start */ - char str[MOL_MAX_VALUE_LEN+1]; - ldata = 0L; - if ( field_len > MOL_MAX_VALUE_LEN ) { - ret = -1; - }else - if ( field_len > 0 ) { /* fixed length */ - *line_ptr += ( len = mol_copy_check_empty( str, p, field_len, &q ) ); - *q = '\0'; - if ( !len || !(q-str) ) { /* empty string */ - ret = 0; - }else - if ( (ldata=strtol(str,&p_end,10), p_end != q) ){ /* wrong data: incompletely interpreted */ - ret = -1; - } - }else{ /* free format: field_len <= 0 */ - ldata = strtol( p, &p_end, 10 ); - *line_ptr += ( len = p_end - p ); - if ( len == 0 ){ - ret = 0; - } - } - - switch( data_type ) { - case MOL_CHAR_INT_DATA: - if ( SCHAR_MIN <= ldata && ldata <= SCHAR_MAX ){ /* from || to &&: 11-19-96 */ - *(S_CHAR*)data = (S_CHAR)ldata; - }else{ - *(S_CHAR*)data = (S_CHAR)0; - ret = -1; - } - break; - case MOL_SHORT_INT_DATA: - if ( SHRT_MIN <= ldata && ldata <= SHRT_MAX ){ - *(S_SHORT*)data = (S_SHORT)ldata; - }else{ - *(S_SHORT*)data = (S_SHORT)0; - ret = -1; - } - break; - case MOL_LONG_INT_DATA: - if ( LONG_MIN < ldata && ldata < LONG_MAX ){ - *(long*)data = (long)ldata; - }else{ - *(long*)data = 0L; - ret = -1; - } - break; - default: - ret=-1; - } - - } /* block end */ - break; - case MOL_DOUBLE_DATA: - case MOL_FLOAT_DATA: - { /* block start */ - char str[MOL_MAX_VALUE_LEN+1]; - if ( field_len > MOL_MAX_VALUE_LEN ) { - ret = -1; - ddata = 0.0; - }else - if ( field_len > 0 ) { - *line_ptr += (len = mol_copy_check_empty( str, p, field_len, &q )); - *q = '\0'; - if ( !len || !(q-str) ) { /* empty string */ - ddata = 0.0; - ret = 0; - }else - if ( (ddata=strtod(str,&p_end), p_end != q) ){ /* wrong data */ - ret = -1; - } - }else{ /* free format */ - ddata = strtod( p, &p_end ); - *line_ptr += ( len = p_end - p ); - if ( len == 0 ){ - ret = 0; - } - } - switch(data_type){ - case MOL_DOUBLE_DATA: - if ( ddata != HUGE_VAL && /*ldata*/ ddata != -HUGE_VAL ){ /* replaced ldata with ddata 6-30-98 DCh */ - *(double*)data = ddata; - }else{ - *(double*)data = 0.0; - ret = -1; - } - break; - case MOL_FLOAT_DATA: - if ( fabs(ddata) <= (double)FLT_MIN ) { - *(float*)data = 0.0; - }else - if ( fabs(ddata) >= (double)FLT_MAX ) { - *(float*)data = 0.0; - ret = -1; - }else{ - *(float*)data = (float)ddata; - } - break; - } - } /* block end */ - break; - case MOL_JUMP_TO_RIGHT: - for ( i = 0; i < field_len && p[i]; i++ ) - ; - *line_ptr += i; - ret = i; - break; - default: - ret = -1; - } - return ret; -} -/************* static ************************************************************/ -int mol_read_hdr(MOL_HEADER_BLOCK *hdr, FILE* inp, char *pStrErr) -{ - /* All input lines can have are up 80 characters */ - /* Header Block */ - char line[MOLFILEINPLINELEN]; /* + cr +lf +zero termination + reserve */ - int err = 0, len; - const int line_len = sizeof(line); - char *p; - - /* memset( &hdr, 0, sizeof( MOL_HEADER_BLOCK ) ); */ - /*------------ header line #1: name ----------------*/ - if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ - err = 1; /* can't read the input file line */ - /* AddMOLfileError( pStrErr, "Can't read header block name line" ); */ - goto err_fin; - } - remove_one_lf( line ); - /* -- Disabled to relax strictness: allow > 80 chars names. - if ( line[MOLFILEMAXLINELEN] ){ - err = 2; // too long line - goto err_fin; - } - */ - len = mol_read_datum( hdr->szMoleculeName, sizeof(hdr->szMoleculeName)-1, MOL_STRING_DATA, &p ); - /*----------- header line #2 -----------------------*/ - if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ - err = 3; /* can't read the input file line */ - /* AddMOLfileError( pStrErr, "Can't read header block line 2" ); */ - goto err_fin; - } - remove_one_lf( line ); - /* -- Disabled to relax strictness: allow > 80 chars names. - if ( line[MOLFILEMAXLINELEN] ){ - err = 4; // too long input file line - goto err_fin; - } - */ - len = mol_read_datum( hdr->szUserInitials, sizeof(hdr->szUserInitials)-1, MOL_STRING_DATA, &p ); - len = mol_read_datum( hdr->szProgramName, sizeof(hdr->szProgramName)-1, MOL_STRING_DATA, &p ); - - /*------------ Relax strictness -----------------------*/ - len = mol_read_datum( &hdr->cMonth, 2, MOL_CHAR_INT_DATA, &p ); - len = mol_read_datum( &hdr->cDay, 2, MOL_CHAR_INT_DATA, &p ); - len = mol_read_datum( &hdr->cYear, 2, MOL_CHAR_INT_DATA, &p ); - len = mol_read_datum( &hdr->cHour, 2, MOL_CHAR_INT_DATA, &p ); - len = mol_read_datum( &hdr->cMinute, 2, MOL_CHAR_INT_DATA, &p ); - len = mol_read_datum( hdr->szDimCode, sizeof(hdr->szDimCode)-1, MOL_STRING_DATA, &p ); - len = mol_read_datum( &hdr->nScalingFactor1, 2, MOL_SHORT_INT_DATA, &p ); - len = mol_read_datum( &hdr->dScalingFactor2, 10, MOL_DOUBLE_DATA, &p ); - len = mol_read_datum( &hdr->dEnergy, 12, MOL_DOUBLE_DATA, &p ); - len = mol_read_datum( &hdr->lInternalRegistryNumber, 6, MOL_LONG_INT_DATA, &p ); - - /* save the whole line 2 */ - p = line; - len = mol_read_datum( hdr->szMoleculeLine2, sizeof(hdr->szMoleculeLine2)-1, MOL_STRING_DATA, &p ); - - - /*------------ header line #3: comment ----------------*/ - if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ - err = 7; /* can't read the line */ - /* AddMOLfileError( pStrErr, "Can't read header block comment line" ); */ - goto err_fin; - } - remove_one_lf( line ); - /* -- Disabled to relax strictness: allow > 80 chars comments. - if ( line[MOLFILEMAXLINELEN] ){ - err = 8; // too long line - goto err_fin; - } - */ - len = mol_read_datum( hdr->szComment, sizeof(hdr->szComment)-1, MOL_STRING_DATA, &p ); - -err_fin: - - return err; -} -/********** static *****************************************************/ -int RemoveNonPrintable( char *line ) -{ - int i, c, num = 0; - if ( line ) { - for ( i = 0; c = UCINT line[i]; i ++ ) { - /* assuming ASCII charset */ - if ( c < ' ' || c >= 0x7F ) { - line[i] = '.'; - num ++; - } - } - } - return num; -} -/************** static *************************************************/ -int mol_read_counts_line( MOL_CTAB* ctab, FILE *inp, char *pStrErr ) -{ - char *p; - char line[MOLFILEINPLINELEN]; - const int line_len = sizeof(line); - int err = 0, len; - - if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ - MOLFILE_ERR_FIN (err, 1, err_fin, "Cannot read counts line"); - /* can't read the input file line */ - } - remove_one_lf( line ); - if ( line[MOLFILEMAXLINELEN] ){ - MOLFILE_ERR_SET (err, 0, "Too long counts line"); /* too long input file line */ - } - if ( 0 > mol_read_datum( &ctab->nNumberOfAtoms, 3, MOL_SHORT_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->nNumberOfBonds, 3, MOL_SHORT_INT_DATA, &p ) -#if ( MOL_QUERY == MOL_PRESENT ) - || 0 > mol_read_datum( &ctab->nNumberOfAtomsLists, 3, MOL_SHORT_INT_DATA, &p ) -#else - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) -#endif - || 0 > mol_read_datum( NULL, /*obsolete*/ 3, MOL_JUMP_TO_RIGHT, &p ) - || 0 > mol_read_datum( &ctab->cChiralFlag, 3, MOL_CHAR_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->nNumberOfStextEntries, 3, MOL_SHORT_INT_DATA, &p ) -#if ( MOL_CPSS == MOL_PRESENT ) - || 0 > mol_read_datum( &ctab->nNumberOfReactionComponentsPlus1, 3, MOL_SHORT_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->nNumberOfReactants, 3, MOL_SHORT_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->nNumberOfProducts, 3, MOL_SHORT_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->nNumberOfIntermediates, 3, MOL_SHORT_INT_DATA, &p ) -#else - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) -#endif - || 0 > mol_read_datum( &ctab->nNumberOfPropertyLines, 3, MOL_SHORT_INT_DATA, &p ) ){ - err = 3; /* can't interpret counts line */ - MOLFILE_ERR_SET (err, 3, "Cannot interpret counts line:"); /* too long input file line */ - RemoveNonPrintable( line ); - AddMOLfileError(pStrErr, line); - goto err_fin; - } - len = mol_read_datum( ctab->csCurrentCtabVersion, sizeof(ctab->csCurrentCtabVersion)-1, MOL_STRING_DATA, &p ); -err_fin: - return err; -} - -/************ static *************************************************************/ -int read_atom_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ) -{ - char *p; - char line[MOLFILEINPLINELEN]; - const int line_len = sizeof(line); - S_SHORT i, chg; - static S_SHORT charge_val[] = {0, 3, 2, 1, 'R', -1, -2, -3}; - /* 0 1 2 3 4 5 6 7 */ - /* - if ( NULL == ctab->MolAtom ){ - err = 1; - goto err_fin; // internal error: MolAtom structure has not been allocated - } - */ - - for ( i = 0; i < ctab->nNumberOfAtoms; i++ ) { - - if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ - if ( !err ) { - MOLFILE_ERR_SET (err, 2, "Cannot read atom block line"); - } - break; - } - remove_one_lf( line ); - if ( line[MOLFILEMAXLINELEN] ){ - MOLFILE_ERR_SET (err, 0, "Too long atom block line"); - } - if ( err ) { - if ( !strcmp( line, SDF_END_OF_DATA ) ) { - err = -abs(err); - break; - } - continue; /* bypass the rest of the Atom block */ - } - if ( NULL != ctab->szCoord ) { - mystrncpy( ctab->szCoord[i], p, 31 ); /* original coordinates */ - } - - if ( NULL != ctab->MolAtom ) { - if ( 0 > mol_read_datum( &ctab->MolAtom[i].fX, 10, MOL_DOUBLE_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolAtom[i].fY, 10, MOL_DOUBLE_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolAtom[i].fZ, 10, MOL_DOUBLE_DATA, &p ) - || 0 > mol_read_datum( NULL, /* undescribed in article*/ 1, MOL_JUMP_TO_RIGHT, &p ) - || 0 == mol_read_datum( &ctab->MolAtom[i].szAtomSymbol, 3, MOL_STRING_DATA, &p ) /* was sizeof(ctab->MolAtom[0].szAtomSymbol)-1 */ -#ifdef TARGET_EXE_USING_API - || 0 > mol_read_datum( &ctab->MolAtom[i].cMassDifference, 2, MOL_SHORT_INT_DATA, &p ) -#else - || 0 > mol_read_datum( &ctab->MolAtom[i].cMassDifference, 2, MOL_CHAR_INT_DATA, &p ) -#endif - || 0 > mol_read_datum( &ctab->MolAtom[i].cCharge, 3, MOL_CHAR_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolAtom[i].cStereoParity, 3, MOL_CHAR_INT_DATA, &p ) -#if ( MOL_QUERY == MOL_PRESENT ) - || 0 > mol_read_datum( &ctab->MolAtom[i].cH_countPlus1, 3, MOL_CHAR_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolAtom[i].cStereoCare, 3, MOL_CHAR_INT_DATA, &p ) -#else - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) -#endif - || 0 > mol_read_datum( &ctab->MolAtom[i].cValence, 3, MOL_CHAR_INT_DATA, &p ) ) { - - err = 4; - MOLFILE_ERR_SET (err, 4, "Cannot interpret atom block line:"); - RemoveNonPrintable( line ); - AddMOLfileError(pStrErr, line); - if ( !strcmp( line, SDF_END_OF_DATA ) ) { - err = -abs(err); - break; - } - continue; /* can't interpret a first half of atom block line */ - } - if ( 2 == strlen(ctab->MolAtom[i].szAtomSymbol) && isupper(UCINT ctab->MolAtom[i].szAtomSymbol[1])) - ctab->MolAtom[i].szAtomSymbol[1] = (char)tolower(UCINT ctab->MolAtom[i].szAtomSymbol[1]); /* 5-4-99 DCh*/ - - if ( (chg = (S_SHORT) ctab->MolAtom[i].cCharge)< 0 || chg >= (int)(sizeof ( charge_val ) / sizeof( charge_val[0] )) ) { - /* ctab->MolAtom[i].cCharge = 0; */ /* error; ignore for now */ - ctab->MolAtom[i].cCharge = (S_CHAR)(4 - chg); /* allow greater charges to accommodate NCI structures. 8-20-2002 */ - ctab->MolAtom[i].cRadical = 0; - }else - if ( 'R' == (chg = charge_val[chg]) ){ - ctab->MolAtom[i].cCharge = 0; - ctab->MolAtom[i].cRadical = RADICAL_DOUBLET; - }else{ - ctab->MolAtom[i].cCharge = (S_CHAR)chg; /* actual charge value */ - ctab->MolAtom[i].cRadical = 0; - } -#ifdef TARGET_EXE_USING_API - if ( ctab->MolAtom[i].cMassDifference ) { /* e_ReadMOL.c specific */ - ctab->MolAtom[i].cMassDifference += ISOTOPIC_SHIFT_FLAG; - } -#endif - - if ( -#if ( MOL_CPSS == MOL_PRESENT ) - 0 > mol_read_datum( &ctab->MolAtom[i].cH0_designator, 3, MOL_CHAR_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolAtom[i].cReactionComponentType, 3, MOL_CHAR_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolAtom[i].cReactionComponentNumber, 3, MOL_CHAR_INT_DATA, &p ) -#else - 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) -#endif -#if ( MOL_REACT == MOL_PRESENT ) - || 0 > mol_read_datum( &ctab->MolAtom[i].nAtomAtomMappingNumber, 3, MOL_SHORT_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolAtom[i].cReactionComponentType, 3, MOL_CHAR_INT_DATA, &p ) -#else - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) -#endif -#if ( MOL_REACT == MOL_PRESENT || MOL_QUERY == MOL_PRESENT ) - || 0 > mol_read_datum( &ctab->MolAtom[i].cExactChargeFlag, 3, MOL_CHAR_INT_DATA, &p ) -#else - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) -#endif - ){ - err = 5; /* can't interpret a second half of atom block line */ - MOLFILE_ERR_SET (err, 5, "Cannot interpret atom block line:"); - RemoveNonPrintable( line ); - AddMOLfileError(pStrErr, line); - if ( !strcmp( line, SDF_END_OF_DATA ) ) { - err = -abs(err); - break; - } - continue; - } - } - } -/* err_fin: */ - return err; -} -/************ static *************************************************************/ -int read_bonds_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ) -{ - char *p; - char line[MOLFILEINPLINELEN]; - const int line_len = sizeof(line); - S_SHORT i; - /* - if ( NULL == ctab->MolBond ){ - err = 1; - goto err_fin; // internal error: memory has not been allocated for MolBond structure - } - */ - for ( i = 0; i < ctab->nNumberOfBonds; i++ ) { - - if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ - if ( !err ) { - MOLFILE_ERR_SET (err, 2, "Cannot read bond block line"); - } - break; - } - remove_one_lf( line ); - if ( line[MOLFILEMAXLINELEN] ){ - err = err? err : 3; /* too long input file line */ - } - if ( err ) { - if ( !strcmp( line, SDF_END_OF_DATA ) ) { - err = -abs(err); - break; - } - continue; - } - - if ( ctab->MolBond ) { - if ( 0 > mol_read_datum( &ctab->MolBond[i].nAtomNo1, 3, MOL_SHORT_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolBond[i].nAtomNo2, 3, MOL_SHORT_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolBond[i].cBondType, 3, MOL_CHAR_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolBond[i].cBondStereo, 3, MOL_CHAR_INT_DATA, &p ) -#if ( MOL_QUERY == MOL_PRESENT ) - || 0 > mol_read_datum( &ctab->MolBond[i].cBondTopology, 3, MOL_CHAR_INT_DATA, &p ) /* ring/chain */ -#else - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) -#endif -#if ( MOL_REACT == MOL_PRESENT ) - || 0 > mol_read_datum( &ctab->MolBond[i].cReactingCenterStatus, 3, MOL_CHAR_INT_DATA, &p ) -#else - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) -#endif - ){ - if ( !err ) { - /* can't interpret bonds block line */ - MOLFILE_ERR_SET (err, 4, "Cannot interpret bond block line:"); - RemoveNonPrintable( line ); - AddMOLfileError(pStrErr, line); - } - if ( !strcmp( line, SDF_END_OF_DATA ) ) { - err = -abs(err); - break; - } - } - } - } - /* err_fin: */ - return err; -} -/********** static ***************************************************************/ -int read_stext_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ) -{ - /* just pass by all stext enties without attemp to interpret */ - char *p; - char line[MOLFILEINPLINELEN]; - const int line_len = sizeof(line); - S_SHORT i; - - for ( i = 0; i < 2*ctab->nNumberOfStextEntries; i++ ) { - - if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ - if ( !err ) { - MOLFILE_ERR_FIN (err, 2, err_fin, "Cannot read STEXT block line"); - } - break; - /* can't read the input file line */ - } - /* - remove_one_lf( line ); - if ( line[MOLFILEMAXLINELEN] ){ - MOLFILE_ERR_SET (err, 2, "Warning: Too long STEXT block line"); - // too long input file line - } - */ - } -err_fin: - return err; -} -/************ static *************************************************************/ -int read_properties_block( MOL_CTAB* ctab, MOL_HEADER_BLOCK *pHdr, FILE *inp, int err, char *pStrErr ) -{ - enum { MULTI_LINE_MODE_NO_MODE, MULTI_LINE_MODE_ISIS_ALIAS }; - char *p; - char line[MOLFILEINPLINELEN]; - const int line_len = sizeof(line); - int nMultiLineMode = MULTI_LINE_MODE_NO_MODE, nAtomNumber=0; - S_SHORT i, j; - char charM[2]; - char szBlank[3]; - char szType[4]; - S_SHORT skip_lines=0; - S_SHORT num_entries; - S_SHORT num_atoms = ctab->nNumberOfAtoms; - - int charge_encountered = 0; - int radical_encountered = 0; - int isotope_encountered = 0; - /* - if ( NULL == ctab->MolAtom ){ - err = 1; - goto err_fin; internal error: memory has not been allocated for MolAtom structure - } - */ - for ( i = 0; ctab->csCurrentCtabVersion[0]? 1 : (i < ctab->nNumberOfPropertyLines); i++ ) { /* the last line should be M END */ - /* ctab->csCurrentCtabVersion[0] == 0: - exactly ctab->nNumberOfPropertyLines lines including M END */ - /* ctab->csCurrentCtabVersion[0] != 0: - read until M END line was encountered */ - if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ - if ( !err ) { - MOLFILE_ERR_SET (err, 2, "Cannot read properties block line"); - } - goto err_fin; - } - remove_one_lf( line ); - if ( line[MOLFILEMAXLINELEN] ){ - MOLFILE_ERR_SET (err, 3, "Too long properties block line"); - continue; - } - if ( skip_lines > 0 ) { - skip_lines --; - continue; - } - /* alias. */ - if ( nMultiLineMode == MULTI_LINE_MODE_ISIS_ALIAS && nAtomNumber ) { - int len; - nMultiLineMode = MULTI_LINE_MODE_NO_MODE; - if ( 0 >= (len=normalize_name( p )) ) { - nAtomNumber = 0; - continue; - } - if( 0 < len && len < (int)(sizeof(ctab->MolAtom->szAtomSymbol)) ) { - int nCharge, nRad; - MOL_ATOM* MolAtom = ctab->MolAtom + nAtomNumber-1; - /* ctab->MolAtom[nAtomNumber-1].cAtomAliasedFlag = 1; */ - /* extract radicals & charges */ - extract_ChargeRadical( p, &nRad, &nCharge ); - /* Aliased atom cannot have charge, radical & mass difference */ - /* in the atom table or "M CHG", "M RAD", "M ISO" */ - /* if ( nCharge ) */ - MolAtom->cCharge = (S_CHAR)nCharge; - /* if ( nRad ) */ - MolAtom->cRadical = (char)nRad; - - if ( 1 == len && 'D' == p[0] ) { - /* H isotope */ - p[0] = 'H'; -#ifdef TARGET_EXE_USING_API - MolAtom->cMassDifference=(1 + ISOTOPIC_SHIFT_FLAG); -#else - MolAtom->cMassDifference=1; -#endif - } else - if ( 1 == len && 'T' == p[0] ) { - /* H isotope */ - p[0] = 'H'; -#ifdef TARGET_EXE_USING_API - MolAtom->cMassDifference=(2 + ISOTOPIC_SHIFT_FLAG); -#else - MolAtom->cMassDifference=2; -#endif - } else - MolAtom->cMassDifference=0; - if ( strlen(p) < sizeof(ctab->MolAtom[0].szAtomSymbol) ) { - strcpy(MolAtom->szAtomSymbol, p); - } else { - strcpy(MolAtom->szAtomSymbol, "???"); - } - MolAtom->cAtomAliasedFlag ++; - } - skip_lines = 0; - nAtomNumber = 0; - continue; - } - - if ( 1 != mol_read_datum( charM, sizeof(charM) - 1, MOL_STRING_DATA, &p ) - || 0 != mol_read_datum( szBlank, sizeof(szBlank) - 1, MOL_STRING_DATA, &p ) /* must contain 0 bytes */ - || 0 >= mol_read_datum( szType, sizeof(szType) - 1, MOL_STRING_DATA, &p ) /* must contain 3 bytes */ - ) { - if ( !strcmp( line, SDF_END_OF_DATA ) ) { - err = err? -abs(err): -4; - break; - } - continue; /* ignore because cannot recognize */ - } - if ( charM[0] == 'V' ){ - skip_lines = 0; /* ISIS/Desktop Atom Value: one-line property */ - continue; - } - if ( charM[0] == 'G' ){ - skip_lines = 1; /* ISIS/Desktop Group abbreviation: two-line property */ - continue; - } - if ( charM[0] == 'A' ) { - if ( NULL != ctab->MolAtom && - 0 < ( nAtomNumber = (int)strtol(szType, NULL, 10) ) && - nAtomNumber <= ctab->nNumberOfAtoms ){ - /* Atom Alias [ISIS/Desktop] two-line property */ - nMultiLineMode = MULTI_LINE_MODE_ISIS_ALIAS; - continue; - } else { - nAtomNumber = 0; - skip_lines = 1; - continue; - } - } - if ( charM[0] == 'S' && !strcmp( szType, "SKP" ) ){ /* skip lines */ - if ( 0 >= mol_read_datum( &skip_lines, 3, MOL_SHORT_INT_DATA, &p ) ) { - skip_lines = 0; - } - continue; - } - if ( charM[0] != 'M' ) {/* cannot recognize a line */ - continue; - } - if ( !strcmp( szType, "REG" ) ) { - int len; - p = p + strspn( p, " " ); - len = strcspn( p, " " ); - len = inchi_min( len, MOL_MAX_VALUE_LEN ); - mol_read_datum( &pHdr->lInternalRegistryNumber, len, MOL_LONG_INT_DATA, &p ); - continue; - } - - if ( !strcmp( szType, "END" ) ){ - if ( ctab->csCurrentCtabVersion[0] ) - break; /* end of property lines */ - continue; - } - - if ( NULL == ctab->MolAtom ) - continue; /* ignore because the user requested to bypass all this stuff */ - - /*----------------------------------- charge: Generic */ - if ( !strcmp( szType, "CHG" ) && - 0 < mol_read_datum( &num_entries, 3, MOL_SHORT_INT_DATA, &p ) && - 1 <= num_entries && num_entries <= 8 ) { - S_SHORT atoms[8]; - S_SHORT charges[8]; - if ( !charge_encountered && !radical_encountered ) { - /* first charge or radical record clears all Atom Block */ - /* entered charge and radical data to zeroes */ - charge_encountered = -1; - } - for ( j = 0; j < num_entries; j++ ) { - if ( 0 > mol_read_datum( &atoms[j], 0, MOL_SHORT_INT_DATA, &p ) || - 0 > mol_read_datum( &charges[j], 0, MOL_SHORT_INT_DATA, &p ) || - atoms[j] <= 0 || atoms[j] > num_atoms || - charges[j] < -15 || charges[j] > 15 ) { - goto charge_error; - } - } - if ( charge_encountered == -1 ) { - for ( j = 0; j < num_atoms; j++ ) { - if ( !ctab->MolAtom[j].cAtomAliasedFlag ) /* do not clear aliased atoms.*/ - ctab->MolAtom[j].cCharge = ctab->MolAtom[j].cRadical = '\0'; - } - charge_encountered = 1; - } - for ( j = 0; j < num_entries; j++ ) { - if ( !ctab->MolAtom[atoms[j]-1].cAtomAliasedFlag ) /* do not change aliased atoms.*/ - ctab->MolAtom[atoms[j]-1].cCharge = (S_CHAR)charges[j]; - } - continue; - charge_error: - MOLFILE_ERR_SET (err, 0, "Charge not recognized:"); - RemoveNonPrintable( line ); - AddMOLfileError(pStrErr, line); - continue; /* ignore for now */ - } - /*-------------------------------------- radical: Generic */ - if ( !strcmp( szType, "RAD" ) && - 0 < mol_read_datum( &num_entries, 3, MOL_SHORT_INT_DATA, &p ) && - 1 <= num_entries && num_entries <= 8 ) { - S_SHORT atoms[8]; - S_SHORT radicals[8]; - if ( !charge_encountered && !radical_encountered ) { - /* first charge or radical record clears all Atom Block */ - /* entered charge and radical data to zeroes */ - radical_encountered = -1; - } - for ( j = 0; j < num_entries; j++ ) { - if ( 0 > mol_read_datum( &atoms[j], 0, MOL_SHORT_INT_DATA, &p ) || - 0 > mol_read_datum( &radicals[j], 0, MOL_SHORT_INT_DATA, &p ) || - atoms[j] <= 0 || atoms[j] > num_atoms || - radicals[j] < 0 || radicals[j] > 3 ) { - goto radical_error; - } - } - if ( radical_encountered == -1 ) { - for ( j = 0; j < num_atoms; j++ ) { - if ( !ctab->MolAtom[j].cAtomAliasedFlag ) /* do not clear aliased atoms. 5-3-99 DCh */ - ctab->MolAtom[j].cCharge = ctab->MolAtom[j].cRadical = '\0'; - } - radical_encountered = 1; - } - for ( j = 0; j < num_entries; j++ ) { - if ( !ctab->MolAtom[atoms[j]-1].cAtomAliasedFlag ) { /* do not change aliased atoms. 5-3-99 DCh */ - ctab->MolAtom[atoms[j]-1].cRadical = (S_CHAR)radicals[j]; - } - } - continue; - radical_error: - MOLFILE_ERR_SET (err, 0, "Radical not recognized:"); - RemoveNonPrintable( line ); - AddMOLfileError(pStrErr, line); - continue; /* ignore error for now */ - } - /*-------------------------------------- isotope: Generic */ - if ( !strcmp( szType, "ISO" ) && - 0 < mol_read_datum( &num_entries, 3, MOL_SHORT_INT_DATA, &p ) && - 1 <= num_entries && num_entries <= 8 ) { - S_SHORT atoms[8]; - S_SHORT iso_mass[8]; /* contains istotope mass number, not difference. 7-14-00 DCh. */ - if ( !isotope_encountered ) { - /* first charge or radical record clears all Atom Block */ - /* entered charge and radical data to zeroes */ - isotope_encountered = -1; - } - for ( j = 0; j < num_entries; j++ ) { - if ( 0 > mol_read_datum( &atoms[j], 0, MOL_SHORT_INT_DATA, &p ) || - 0 > mol_read_datum( &iso_mass[j], 0, MOL_SHORT_INT_DATA, &p ) || - atoms[j] <= 0 || atoms[j] > num_atoms - /*|| iso_mass[j] < -18 || iso_mass[j] > 12*/ ) { - /* goto isotope_error; */ - atoms[j] = -1; /* flag error */ - MOLFILE_ERR_SET (err, 0, "Isotopic data not recognized:"); - RemoveNonPrintable( line ); - AddMOLfileError(pStrErr, line); - continue; /* ignore isotopic error for now */ - } - } - if ( isotope_encountered == -1 ) { - for ( j = 0; j < num_atoms; j++ ) { - /*if ( !ctab->MolAtom[j].cAtomAliasedFlag )*/ /* clear even aliased atoms */ - ctab->MolAtom[j].cMassDifference = 0; - } - isotope_encountered = 1; - } - for ( j = 0; j < num_entries; j++ ) { - if ( atoms[j] <= 0 ) - continue; /* ignore isotopic error for now */ - if ( 1 /* !ctab->MolAtom[atoms[j]-1].cAtomAliasedFlag */) { - char *at = ctab->MolAtom[atoms[j]-1].szAtomSymbol; - if ( at[1] || at[0] != 'D' && at[0] != 'T' ) { /* D & T cannot have ISO */ - /* need atomic weight to calculate isotope difference. 7-14-00 DCh. */ -#ifdef TARGET_EXE_USING_API - /*^^^ Check added 5-10-2008 - IPl */ - if (iso_mass[j] > 0) - /* According to MDL specification, p.12, only a positive - integer is allowed. And yes, there appeared some MOL/SD - files contaning here a negative value. This manifested - in mismatch in InChI_MAIN vs. cInChI-1/stdinchi-1 results. - */ - ctab->MolAtom[atoms[j]-1].cMassDifference = iso_mass[j]; /* mass, not difference */ - -#else - int atw, atw_diff; - /*^^^ - NB: According to MDL specification, difference should be in - [-18; +12] range, not in [-19; +19] as is checked below. */ - if ( (atw = get_atw( at )) && abs( atw_diff = (int)iso_mass[j] - atw ) < 20 ) { - ctab->MolAtom[atoms[j]-1].cMassDifference = (char)(atw_diff? atw_diff : ZERO_ATW_DIFF); - } -#endif - } - } - } - continue; - } - } -err_fin: - return err; -} -/************ global *************************************************************/ -MOL_DATA* delete_mol_data( MOL_DATA* mol_data ) -{ - if ( mol_data ) { - if ( mol_data->ctab.MolAtom ) - inchi_free( mol_data->ctab.MolAtom ); - if ( mol_data->ctab.MolBond ) - inchi_free( mol_data->ctab.MolBond ); - if ( mol_data->ctab.szCoord ) - inchi_free( mol_data->ctab.szCoord ); - inchi_free( mol_data ); - mol_data = NULL; - } - return mol_data; -} -/************* global ************************************************************/ -/* Comletely ingnore STEXT block, queries, and 3D features - */ -MOL_DATA* read_mol_file( FILE* inp, MOL_HEADER_BLOCK *OnlyHeaderBlock, MOL_CTAB *OnlyCtab, - int bGetOrigCoord, int *err, char *pStrErr ) -{ - MOL_DATA* mol_data = NULL; - int ret = 0, prev_ret, bEndOfData = 0; - int bReadAll = ( OnlyHeaderBlock == NULL ); - MOL_CTAB ctab, *pCtab = NULL; - MOL_HEADER_BLOCK *pHdr = NULL; - - *err = 0; - if ( bReadAll ) { - if ( NULL == ( mol_data = ( MOL_DATA* )inchi_calloc( 1, sizeof(MOL_DATA) ) ) ){ - ret = 1; /* can't allocate mol_data structure */ - AddMOLfileError( pStrErr, "Out of RAM" ); - goto err_fin; - } - pHdr = &mol_data->hdr; - pCtab = &mol_data->ctab; - } else { - pHdr = OnlyHeaderBlock; - pCtab = OnlyCtab? OnlyCtab : &ctab; - memset( pHdr, 0, sizeof( MOL_HEADER_BLOCK ) ); - memset( pCtab, 0, sizeof( MOL_CTAB ) ); - } - pCtab->MolBond = NULL; - pCtab->MolAtom = NULL; - pCtab->szCoord = NULL; - - if ( 0 != ( ret = mol_read_hdr(pHdr, inp, pStrErr) ) ){ - ret += 10; - goto err_fin; /* most probably end of file */ - } - if ( 0 != ( ret = mol_read_counts_line( pCtab , inp, pStrErr) ) ){ - ret += 20; - goto err_fin; - } - - if ( bReadAll ) { - if ( NULL == ( mol_data->ctab.MolAtom = (MOL_ATOM*)inchi_calloc(inchi_max(mol_data->ctab.nNumberOfAtoms,1), sizeof(MOL_ATOM)) ) ){ - ret = 2; /* can't allocate MolAtom structure */ - MOLFILE_ERR_FIN (ret, 2, err_fin, "Out of RAM"); - } - if ( bGetOrigCoord && - NULL == ( mol_data->ctab.szCoord = (MOL_COORD*)inchi_calloc(inchi_max(mol_data->ctab.nNumberOfAtoms,1), sizeof(MOL_COORD)) ) ){ - ret = 2; /* can't allocate MolAtom structure */ - MOLFILE_ERR_FIN (ret, 2, err_fin, "Out of RAM"); - } - } - if ( 0 != ( ret = read_atom_block(pCtab, inp, ret, pStrErr) ) ){ - if ( ret < 0 ) { - ret = -ret; - bEndOfData = 1; - } - ret += 30; - /* goto err_fin; */ - } - - if ( bReadAll && ret < 30 ) { - if ( !bEndOfData && NULL == ( mol_data->ctab.MolBond = (MOL_BONDS*)inchi_calloc(inchi_max(mol_data->ctab.nNumberOfBonds,1), sizeof(MOL_BONDS)) ) ){ - ret = 3; /* can't allocate MolBond structure */ - MOLFILE_ERR_FIN (ret, 3, err_fin, "Out of RAM"); - } - } - prev_ret = ret; - if ( !bEndOfData && 0 != ( ret = read_bonds_block(pCtab, inp, ret, pStrErr) ) ){ - if ( ret < 0 ) { - ret = -ret; - bEndOfData = 1; - } - ret = prev_ret? prev_ret : ret + 40; - } - prev_ret = ret; - if ( !bEndOfData && 0 != ( ret = read_stext_block(pCtab, inp, ret, pStrErr) ) ){ - ret = prev_ret? prev_ret : ret + 50; - } - prev_ret = ret; - if ( !bEndOfData && 0 != ( ret = read_properties_block(pCtab, pHdr, inp, ret, pStrErr) ) ){ - if ( ret < 0 ) { - ret = -ret; - bEndOfData = 1; - } - ret = prev_ret? prev_ret : ret + 60; - } - -err_fin: - *err = bEndOfData? -ret : ret; - if ( bReadAll ) { - if ( ret ) - mol_data = delete_mol_data( mol_data ); /* delete all results */ - return mol_data; - } else { - if ( ret ) - return NULL; - else - return (MOL_DATA*)OnlyHeaderBlock; - } -} - -/******************************************************************/ -static const char sdf_data_hdr_name[] = "NAME"; -static const char sdf_data_hdr_comm[] = "COMMENT"; -enum { SDF_START, SDF_DATA_HEADER, SDF_DATA_HEADER_NAME - , SDF_DATA_HEADER_COMMENT, SDF_DATA_HEADER_CAS - , SDF_DATA_HEADER_USER, SDF_DATA_LINE - , SDF_END_OF_DATA_ITEM, SDF_EMPTY_LINE, SDF_END_OF_DATA_BLOCK }; -/********** static ********************************************************/ -long extract_cas_rn( char *line ) -{ - int i, j; - i = line[0] == '-'? 1 : 0; - for ( j = i; line[i]; i ++ ) { - if ( isdigit( UCINT line[i] ) ) { - line[j++] = line[i]; - } else - if ( line[i] != '-' ) { - break; - } - } - line[j] = '\0'; - return strtol( line, NULL, 10 ); -} -/********** static ********************************************************/ -int identify_sdf_label( char* inp_line, const char *pSdfLabel ) -{ - char line[MOLFILEMAXLINELEN]; - char *p, *q; - int i, j, len; - if ( (p = strchr( inp_line, '<' )) && - (q = strchr( p, '>' )) && - (len = q-p-1) > 0 && len < (int)sizeof(line) ) { - memcpy( line, p+1, len ); - line[len] = '\0'; - for ( i = 0; isspace( UCINT line[i] ); i ++ ) - ; - for ( j = len-1; j >= i && isspace( UCINT line[i] ); j -- ) - ; - len = j-i+1; - p = line+i; - if ( pSdfLabel && pSdfLabel[0] && len == (int)strlen(pSdfLabel) && !memicmp( p, pSdfLabel, len ) ) - return SDF_DATA_HEADER_USER; - if ( len == sizeof(sdf_data_hdr_name)-1 && !memicmp( p, sdf_data_hdr_name, len ) ) - return SDF_DATA_HEADER_NAME; - if ( len == sizeof(sdf_data_hdr_comm)-1 && !memicmp( p, sdf_data_hdr_comm, len ) ) - return SDF_DATA_HEADER_COMMENT; - if ( !memicmp( p, "CAS", 3 ) ) - return SDF_DATA_HEADER_CAS; - } - return SDF_DATA_HEADER; -} -/************* global *****************************************************/ -int bypass_sdf_data_items( FILE* inp, long *cas_reg_no, char* comment, - int lcomment, char *name, int lname, int prev_err, - const char *pSdfLabel, char *pSdfValue, char *pStrErr ) -{ - char line[MOLFILEINPLINELEN]; - const int line_len = sizeof(line); - int err = 0; - int current_state = SDF_START; - int n_blank_lines = 0; - int n_lines = 0; - char* p = NULL; - int bNeedsName = name && lname > 0 && !name[0]; - int bNeedsComm = comment && lcomment > 0 && !comment[0]; - int bNeedsUser = pSdfLabel && pSdfLabel[0] && pSdfValue; - int bNeedsCASrn = 0; - int bCASrnIsUser = 0; - - if ( cas_reg_no != NULL ) { - bNeedsCASrn = 1; - *cas_reg_no = 0; - bCASrnIsUser = (bNeedsUser && !memicmp(pSdfLabel,"CAS", 3)); - } - - while ( err == 0 && - current_state !=SDF_END_OF_DATA_BLOCK && - NULL != ( p = inchi_fgetsLf( line, line_len, inp ) ) ) { - - if ( !n_lines && !memcmp(line, "M END", 6) ) { - continue; /* allow subtle errors */ - } - n_lines++; - - remove_trailing_spaces( line ); - if ( line[MOLFILEMAXLINELEN] ){ - if ( current_state != SDF_DATA_HEADER && - current_state != SDF_DATA_LINE && - current_state != SDF_DATA_HEADER_NAME && - current_state != SDF_DATA_HEADER_USER && - current_state != SDF_DATA_HEADER_COMMENT ) { - line[MOLFILEMAXLINELEN] = '\0'; - if ( !prev_err ) { - MOLFILE_ERR_SET (err, 0, "Too long SData line truncated"); - } - } else { - /* allow long lines in SDF data. 9-29-00 DCh */ - line[MOLFILEMAXLINELEN] = '\0'; - } - } - - n_blank_lines += ( *line == '\0' ); - - switch( current_state ) { - - case SDF_START: - case SDF_END_OF_DATA_ITEM: - case SDF_EMPTY_LINE: /* Added 9-25-97 DCh */ - - if ( 0 == strcmp( line, SDF_END_OF_DATA ) ) { - current_state = SDF_END_OF_DATA_BLOCK; - } - else - if ( '>' == *line ) { - current_state = ( bNeedsName || bNeedsComm || bNeedsCASrn || bNeedsUser )? identify_sdf_label(line, pSdfLabel) : SDF_DATA_HEADER; - }else - if ( *line == '\0' ) { /* Added 9-25-97 DCh */ - /* Relax the strictness: Allow more than 1 empty line. */ - current_state=SDF_EMPTY_LINE; - } else - if ( !prev_err ) { - MOLFILE_ERR_SET (err, 3, "Unexpected SData header line:"); - RemoveNonPrintable( line ); - AddMOLfileError(pStrErr, line); - /* unexpected contents of data header line */ - } else { - err = 3; - } - break; - - case SDF_DATA_HEADER_NAME: - if ( bNeedsName && 0 < normalize_name( line ) ) { - bNeedsName = 0; - mystrncpy( name, line, lname ); - } - goto got_data_line; - - case SDF_DATA_HEADER_COMMENT: - if ( bNeedsComm && 0 < normalize_name( line ) ) { - bNeedsComm = 0; - mystrncpy( comment, line, lcomment ); - } - goto got_data_line; - - case SDF_DATA_HEADER_USER: - if ( bNeedsUser && 0 < normalize_name( line ) ) { - bNeedsUser = 0; - mystrncpy( pSdfValue, line, MAX_SDF_VALUE+1 ); - if ( bCASrnIsUser && bNeedsCASrn ) { - *cas_reg_no = extract_cas_rn( line ); - bNeedsCASrn = (0 == *cas_reg_no); - } - } - goto got_data_line; - - case SDF_DATA_HEADER_CAS: - if ( bNeedsCASrn && 0 < normalize_name( line ) ) { - *cas_reg_no = extract_cas_rn( line ); - bNeedsCASrn = (0 == *cas_reg_no); - } - goto got_data_line; - - case SDF_DATA_HEADER: - case SDF_DATA_LINE: -got_data_line: - current_state = *line? SDF_DATA_LINE : SDF_END_OF_DATA_ITEM; - break; - - } - } - if ( 0 == err && SDF_END_OF_DATA_BLOCK != current_state && NULL == p ) - ; /* err = 4; */ /* unexpected end of file: missing $$$$ */ - else - if (err && ( n_blank_lines == n_lines && *line == '\0' ) ) - err = 5; /* empty lines -- do not know when this can happen */ - - if ( err && err != 5 && current_state != SDF_END_OF_DATA_BLOCK && p ) { - /* bypass up to $$$$ */ - while ( ( p = inchi_fgetsLf( line, line_len, inp ) ) && memcmp( line, SDF_END_OF_DATA, 4 ) ) - ; - if ( p ) { - err = 9; /* bypassed to $$$$; non-fatal */ - AddMOLfileError(pStrErr, "Bypassing to next structure"); - } - - } - - return err; -} -/**************** global **************************************************/ -MOL_DATA* read_sdfile_segment(FILE* inp, MOL_HEADER_BLOCK *OnlyHeaderBlock, MOL_CTAB *OnlyCtab, - int bGetOrigCoord, - char *pname, int lname, - long *Id, const char *pSdfLabel, char *pSdfValue, - int *err, char *pStrErr ) -{ - MOL_DATA* mol_data = read_mol_file( inp, OnlyHeaderBlock, OnlyCtab, bGetOrigCoord, err, pStrErr ); - int err_bypass_sdf = 0; - - if ( pname && lname ) { - pname[0] = '\0'; - } - if ( Id ) { - *Id = 0L; /* ignore for now */ - } - /* if ( mol_data && !*err ) { */ - if ( *err < 0 ) { - *err = -*err; /* end of data encountered */ - } else { - err_bypass_sdf = bypass_sdf_data_items( inp, Id, NULL, 0, pname, lname, *err, pSdfLabel, pSdfValue, pStrErr ); - if ( err_bypass_sdf ) { - *err = err_bypass_sdf; /* important to continue to the next good structure */ - } - } - /* } */ - return mol_data; -} -/******************* global *********************************************************/ -int CopyMOLfile(FILE *inp_file, long fPtrStart, long fPtrEnd, FILE *prb_file, long lNumb) -{ - char line[MOLFILEINPLINELEN], *p; - long fPtr; - int ret = 1; - char szNumber[32]; - - if ( inp_file && prb_file && fPtrStart >= 0L && - fPtrEnd > fPtrStart && - 0 == fseek( inp_file, fPtrStart, SEEK_SET ) ) { - - while ( fPtrEnd > (fPtr = ftell(inp_file)) && fPtr >= 0L && - inchi_fgetsLf( line, sizeof(line)-1, inp_file ) ) { - line[sizeof(line)-1] = '\0'; /* unnecessary extra precaution */ - if ( fPtr == fPtrStart && lNumb ) { - int len; - LtrimRtrim( line, &len ); - len = sprintf( szNumber, "#%ld%s", lNumb, len?"/":"" ); - mystrncpy( line+len, line, sizeof(line)-len-1 ); - memcpy( line, szNumber, len ); - } - if ( !strchr(line, '\n') ) { - p = line+strlen(line); - p[0] = '\n'; - p[1] = '\0'; - } - fputs( line, prb_file ); - } - ret = fseek( inp_file, fPtrEnd, SEEK_SET ); - } - return ret; -} +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include "e_mode.h" + +#include "../../../../INCHI_BASE/src/inchi_api.h" + +#include "e_ctl_data.h" + +#include "e_ichisize.h" +#include "e_comdef.h" +#include "e_util.h" +#include "e_readmol.h" +#include "e_ichicomp.h" +#include "e_ichi_io.h" +#include "e_readstru.h" +#include "e_inpdef.h" + +#define AddMOLfileError e_AddMOLfileError +#define remove_one_lf e_remove_one_lf +#define RemoveNonPrintable e_RemoveNonPrintable +#define mystrncpy e_mystrncpy +#define read_mol_file e_read_mol_file +#define bypass_sdf_data_items e_bypass_sdf_data_items +#define extract_ChargeRadical e_extract_ChargeRadical +#define delete_mol_data e_delete_mol_data +#define remove_trailing_spaces e_remove_trailing_spaces +#define normalize_name e_normalize_name +#define read_sdfile_segment e_read_sdfile_segment +#define CopyMOLfile e_CopyMOLfile +#define LtrimRtrim e_LtrimRtrim + +#ifndef inchi_calloc +#define inchi_calloc e_inchi_calloc +#endif + +#ifndef inchi_free +#define inchi_free e_inchi_free +#endif + +/* + Former lreadmol.h follows below + + +#include "lreadmol.h" +*/ + + + +/* local prototypes */ +int bypass_sdf_data_items( FILE* inp, long *cas_reg_no, char* comment, int lcomment, char *name, int lname, int prev_err, + const char *pSdfLabel, char *pSdfValue, char *pStrErr ); +MOL_DATA* read_mol_file( FILE* inp, MOL_HEADER_BLOCK *OnlyHeaderBlock, MOL_CTAB *OnlyCtab, + int bGetOrigCoord, int *err, char *pStrErr ); + + +static int mol_read_hdr(MOL_HEADER_BLOCK *hdr, FILE* inp, char *pStrErr); +static int mol_read_counts_line( MOL_CTAB* ctab, FILE *inp, char *pStrErr ); +static int read_atom_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ); +static int read_bonds_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ); +static int read_stext_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ); +static int read_properties_block( MOL_CTAB* ctab, MOL_HEADER_BLOCK *pHdr, FILE *inp, int err, char *pStrErr ); + +static int identify_sdf_label( char* inp_line, const char *pSdfLabel ); +static long extract_cas_rn( char *line ); +static int mol_copy_check_empty( char* dest, char* source, int len, char **first_space ); +static int mol_read_datum(void* data, int field_len, int data_type, char** line_ptr); + +static int RemoveNonPrintable( char *line ); + + +/******/ +#ifndef MOLFILE_ERR_FIN +#define MOLFILE_ERR_FIN(err, new_err, err_fin, msg) \ + if ( !(err) && (new_err) ) { (err) = (new_err);} AddMOLfileError(pStrErr, (msg)); goto err_fin +#endif +#ifndef MOLFILE_ERR_SET +#define MOLFILE_ERR_SET(err, new_err, msg) \ + if ( !(err) && (new_err) ) { (err) = (new_err);} AddMOLfileError(pStrErr, (msg)) +#endif + +/*************************************************************************/ +int AddMOLfileError( char *pStrErr, const char *szMsg ) +{ + if ( pStrErr && szMsg && szMsg[0] ) { + int lenStrErr = strlen( pStrErr ); + int lenMsg = strlen( szMsg ); + char *p = strstr( pStrErr, szMsg ); + if ( p && (p==pStrErr || *(p-1) == ' ' && (*(p-2) == ';' || *(p-2) == ':' )) && + (p+lenMsg == pStrErr+lenStrErr || + p[lenMsg] == ';' && p[lenMsg+1] == ' ' || + p[lenMsg-1]==':' && p[lenMsg]==' ') ) { + return 1; /* reject duplicates */ + } + if ( lenStrErr + lenMsg + 2*(lenStrErr > 0) < STR_ERR_LEN ) { + /* enough room to add */ + if (lenStrErr > 0) { + if ( pStrErr[lenStrErr-1] != ':' ) { + strcat( pStrErr, ";" ); + } + strcat( pStrErr, " " ); + } + strcat( pStrErr, szMsg ); + return 1; + } + /* no room */ + if ( strstr( pStrErr, "..." ) ) { + return 0; /* no room mark has already been set */ + } + if ( lenStrErr + 3 < STR_ERR_LEN ) { + strcat( pStrErr, "..." ); + } + } + return 0; +} +/*************** static **********************************************************/ +int mol_copy_check_empty( char* dest, char* source, int len, char **first_space ) +{ + int i, c; /* required len >= 0; dest must have at least len+1 bytes */ + if ( len > 0 ) + strncpy( dest, source, len ); + dest[len]='\0'; + len = ( len > 0 )? (int)strlen( dest) : 0; + for ( i = (len-1); i >= 0 && 0 != (c = source[i]) && isspace(UCINT c); i-- ) + ; + *first_space = dest + (i+1); /* first blank or zero terminating byte in dest */ + return len; /* number of actually processed bytes; zero termination not included */ +} +/************* static ************************************************************/ +int mol_read_datum(void* data, int field_len, int data_type, char** line_ptr) +{ +/* 1. 'field_len' for MOL_STRING_DATA does not include trailing zero, + * that is actual length of the string pointed by 'data' + * should be at least field_len+1 bytes. + * For numerical data 'field_len' is length of input data field + * For numerical integral data field_len <= 0 means read up to first + * non-numeric character as strtod() does ("free format") + * 2. return value: for MOL_STRING_DATA: number of bytes excluding trailing zero + * for all others: 1=success; 0 = empty; -1= error + * 3. on exit *line_ptr points to the next byte after the last entered + */ + char *p = *line_ptr, *q, *p_end; + int i, ret=1, c, len; + long ldata; + double ddata; + + switch( data_type ) { + case MOL_STRING_DATA: + for ( i= 0; i < field_len && 0 != (c = p[i]) && isspace(UCINT c); i++ ) /* pass by all leading spaces */ + ; + len = mol_copy_check_empty( (char*)data, &p[i], field_len-i, &q ); + ret = ( q - (char*)data );/* actual data length */ + *q = '\0'; /* add zero termination to data if it is not there yet*/ + *line_ptr += (len+i); /* ptr to the 1st byte of the next input field or to zero termination */ + break; + + case MOL_CHAR_INT_DATA: + case MOL_SHORT_INT_DATA: + case MOL_LONG_INT_DATA: + { /* block start */ + char str[MOL_MAX_VALUE_LEN+1]; + ldata = 0L; + if ( field_len > MOL_MAX_VALUE_LEN ) { + ret = -1; + }else + if ( field_len > 0 ) { /* fixed length */ + *line_ptr += ( len = mol_copy_check_empty( str, p, field_len, &q ) ); + *q = '\0'; + if ( !len || !(q-str) ) { /* empty string */ + ret = 0; + }else + if ( (ldata=strtol(str,&p_end,10), p_end != q) ){ /* wrong data: incompletely interpreted */ + ret = -1; + } + }else{ /* free format: field_len <= 0 */ + ldata = strtol( p, &p_end, 10 ); + *line_ptr += ( len = p_end - p ); + if ( len == 0 ){ + ret = 0; + } + } + + switch( data_type ) { + case MOL_CHAR_INT_DATA: + if ( SCHAR_MIN <= ldata && ldata <= SCHAR_MAX ){ /* from || to &&: 11-19-96 */ + *(S_CHAR*)data = (S_CHAR)ldata; + }else{ + *(S_CHAR*)data = (S_CHAR)0; + ret = -1; + } + break; + case MOL_SHORT_INT_DATA: + if ( SHRT_MIN <= ldata && ldata <= SHRT_MAX ){ + *(S_SHORT*)data = (S_SHORT)ldata; + }else{ + *(S_SHORT*)data = (S_SHORT)0; + ret = -1; + } + break; + case MOL_LONG_INT_DATA: + if ( LONG_MIN < ldata && ldata < LONG_MAX ){ + *(long*)data = (long)ldata; + }else{ + *(long*)data = 0L; + ret = -1; + } + break; + default: + ret=-1; + } + } /* block end */ + break; + case MOL_DOUBLE_DATA: + case MOL_FLOAT_DATA: + { /* block start */ + char str[MOL_MAX_VALUE_LEN+1]; + if ( field_len > MOL_MAX_VALUE_LEN ) { + ret = -1; + ddata = 0.0; + }else + if ( field_len > 0 ) { + *line_ptr += (len = mol_copy_check_empty( str, p, field_len, &q )); + *q = '\0'; + if ( !len || !(q-str) ) { /* empty string */ + ddata = 0.0; + ret = 0; + }else + if ( (ddata=strtod(str,&p_end), p_end != q) ){ /* wrong data */ + ret = -1; + } + }else{ /* free format */ + ddata = strtod( p, &p_end ); + *line_ptr += ( len = p_end - p ); + if ( len == 0 ){ + ret = 0; + } + } + switch(data_type){ + case MOL_DOUBLE_DATA: + if ( ddata != HUGE_VAL && /*ldata*/ ddata != -HUGE_VAL ){ /* replaced ldata with ddata 6-30-98 DCh */ + *(double*)data = ddata; + }else{ + *(double*)data = 0.0; + ret = -1; + } + break; + case MOL_FLOAT_DATA: + if ( fabs(ddata) <= (double)FLT_MIN ) { + *(float*)data = 0.0; + }else + if ( fabs(ddata) >= (double)FLT_MAX ) { + *(float*)data = 0.0; + ret = -1; + }else{ + *(float*)data = (float)ddata; + } + break; + } + } /* block end */ + break; + case MOL_JUMP_TO_RIGHT: + for ( i = 0; i < field_len && p[i]; i++ ) + ; + *line_ptr += i; + ret = i; + break; + default: + ret = -1; + } + return ret; +} +/************* static ************************************************************/ +int mol_read_hdr(MOL_HEADER_BLOCK *hdr, FILE* inp, char *pStrErr) +{ + /* All input lines can have are up 80 characters */ + /* Header Block */ + char line[MOLFILEINPLINELEN]; /* + cr +lf +zero termination + reserve */ + int err = 0, len; + const int line_len = sizeof(line); + char *p; + + /* memset( &hdr, 0, sizeof( MOL_HEADER_BLOCK ) ); */ + /*------------ header line #1: name ----------------*/ + if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ + err = 1; /* can't read the input file line */ + /* AddMOLfileError( pStrErr, "Can't read header block name line" ); */ + goto err_fin; + } + remove_one_lf( line ); + /* -- Disabled to relax strictness: allow > 80 chars names. + if ( line[MOLFILEMAXLINELEN] ){ + err = 2; // too long line + goto err_fin; + } + */ + len = mol_read_datum( hdr->szMoleculeName, sizeof(hdr->szMoleculeName)-1, MOL_STRING_DATA, &p ); + /*----------- header line #2 -----------------------*/ + if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ + err = 3; /* can't read the input file line */ + /* AddMOLfileError( pStrErr, "Can't read header block line 2" ); */ + goto err_fin; + } + remove_one_lf( line ); + /* -- Disabled to relax strictness: allow > 80 chars names. + if ( line[MOLFILEMAXLINELEN] ){ + err = 4; // too long input file line + goto err_fin; + } + */ + len = mol_read_datum( hdr->szUserInitials, sizeof(hdr->szUserInitials)-1, MOL_STRING_DATA, &p ); + len = mol_read_datum( hdr->szProgramName, sizeof(hdr->szProgramName)-1, MOL_STRING_DATA, &p ); + + /*------------ Relax strictness -----------------------*/ + len = mol_read_datum( &hdr->cMonth, 2, MOL_CHAR_INT_DATA, &p ); + len = mol_read_datum( &hdr->cDay, 2, MOL_CHAR_INT_DATA, &p ); + len = mol_read_datum( &hdr->cYear, 2, MOL_CHAR_INT_DATA, &p ); + len = mol_read_datum( &hdr->cHour, 2, MOL_CHAR_INT_DATA, &p ); + len = mol_read_datum( &hdr->cMinute, 2, MOL_CHAR_INT_DATA, &p ); + len = mol_read_datum( hdr->szDimCode, sizeof(hdr->szDimCode)-1, MOL_STRING_DATA, &p ); + len = mol_read_datum( &hdr->nScalingFactor1, 2, MOL_SHORT_INT_DATA, &p ); + len = mol_read_datum( &hdr->dScalingFactor2, 10, MOL_DOUBLE_DATA, &p ); + len = mol_read_datum( &hdr->dEnergy, 12, MOL_DOUBLE_DATA, &p ); + len = mol_read_datum( &hdr->lInternalRegistryNumber, 6, MOL_LONG_INT_DATA, &p ); + + /* save the whole line 2 */ + p = line; + len = mol_read_datum( hdr->szMoleculeLine2, sizeof(hdr->szMoleculeLine2)-1, MOL_STRING_DATA, &p ); + + + /*------------ header line #3: comment ----------------*/ + if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ + err = 7; /* can't read the line */ + /* AddMOLfileError( pStrErr, "Can't read header block comment line" ); */ + goto err_fin; + } + remove_one_lf( line ); + /* -- Disabled to relax strictness: allow > 80 chars comments. + if ( line[MOLFILEMAXLINELEN] ){ + err = 8; // too long line + goto err_fin; + } + */ + len = mol_read_datum( hdr->szComment, sizeof(hdr->szComment)-1, MOL_STRING_DATA, &p ); + +err_fin: + + return err; +} +/********** static *****************************************************/ +int RemoveNonPrintable( char *line ) +{ + int i, c, num = 0; + if ( line ) { + for ( i = 0; c = UCINT line[i]; i ++ ) { + /* assuming ASCII charset */ + if ( c < ' ' || c >= 0x7F ) { + line[i] = '.'; + num ++; + } + } + } + return num; +} +/************** static *************************************************/ +int mol_read_counts_line( MOL_CTAB* ctab, FILE *inp, char *pStrErr ) +{ + char *p; + char line[MOLFILEINPLINELEN]; + const int line_len = sizeof(line); + int err = 0, len; + + if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ + MOLFILE_ERR_FIN (err, 1, err_fin, "Cannot read counts line"); + /* can't read the input file line */ + } + remove_one_lf( line ); + if ( line[MOLFILEMAXLINELEN] ){ + MOLFILE_ERR_SET (err, 0, "Too long counts line"); /* too long input file line */ + } + if ( 0 > mol_read_datum( &ctab->nNumberOfAtoms, 3, MOL_SHORT_INT_DATA, &p ) + || 0 > mol_read_datum( &ctab->nNumberOfBonds, 3, MOL_SHORT_INT_DATA, &p ) +#if ( MOL_QUERY == MOL_PRESENT ) + || 0 > mol_read_datum( &ctab->nNumberOfAtomsLists, 3, MOL_SHORT_INT_DATA, &p ) +#else + || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) +#endif + || 0 > mol_read_datum( NULL, /*obsolete*/ 3, MOL_JUMP_TO_RIGHT, &p ) + || 0 > mol_read_datum( &ctab->cChiralFlag, 3, MOL_CHAR_INT_DATA, &p ) + || 0 > mol_read_datum( &ctab->nNumberOfStextEntries, 3, MOL_SHORT_INT_DATA, &p ) +#if ( MOL_CPSS == MOL_PRESENT ) + || 0 > mol_read_datum( &ctab->nNumberOfReactionComponentsPlus1, 3, MOL_SHORT_INT_DATA, &p ) + || 0 > mol_read_datum( &ctab->nNumberOfReactants, 3, MOL_SHORT_INT_DATA, &p ) + || 0 > mol_read_datum( &ctab->nNumberOfProducts, 3, MOL_SHORT_INT_DATA, &p ) + || 0 > mol_read_datum( &ctab->nNumberOfIntermediates, 3, MOL_SHORT_INT_DATA, &p ) +#else + || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) + || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) + || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) + || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) +#endif + || 0 > mol_read_datum( &ctab->nNumberOfPropertyLines, 3, MOL_SHORT_INT_DATA, &p ) ){ + err = 3; /* can't interpret counts line */ + MOLFILE_ERR_SET (err, 3, "Cannot interpret counts line:"); /* too long input file line */ + RemoveNonPrintable( line ); + AddMOLfileError(pStrErr, line); + goto err_fin; + } + len = mol_read_datum( ctab->csCurrentCtabVersion, sizeof(ctab->csCurrentCtabVersion)-1, MOL_STRING_DATA, &p ); +err_fin: + return err; +} + +/************ static *************************************************************/ +int read_atom_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ) +{ + char *p; + char line[MOLFILEINPLINELEN]; + const int line_len = sizeof(line); + S_SHORT i, chg; + static S_SHORT charge_val[] = {0, 3, 2, 1, 'R', -1, -2, -3}; + /* 0 1 2 3 4 5 6 7 */ + /* + if ( NULL == ctab->MolAtom ){ + err = 1; + goto err_fin; // internal error: MolAtom structure has not been allocated + } + */ + + for ( i = 0; i < ctab->nNumberOfAtoms; i++ ) { + + if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ + if ( !err ) { + MOLFILE_ERR_SET (err, 2, "Cannot read atom block line"); + } + break; + } + remove_one_lf( line ); + if ( line[MOLFILEMAXLINELEN] ){ + MOLFILE_ERR_SET (err, 0, "Too long atom block line"); + } + if ( err ) { + if ( !strcmp( line, SDF_END_OF_DATA ) ) { + err = -abs(err); + break; + } + continue; /* bypass the rest of the Atom block */ + } + if ( NULL != ctab->szCoord ) { + mystrncpy( ctab->szCoord[i], p, 31 ); /* original coordinates */ + } + + if ( NULL != ctab->MolAtom ) { + if ( 0 > mol_read_datum( &ctab->MolAtom[i].fX, 10, MOL_DOUBLE_DATA, &p ) + || 0 > mol_read_datum( &ctab->MolAtom[i].fY, 10, MOL_DOUBLE_DATA, &p ) + || 0 > mol_read_datum( &ctab->MolAtom[i].fZ, 10, MOL_DOUBLE_DATA, &p ) + || 0 > mol_read_datum( NULL, /* undescribed in article*/ 1, MOL_JUMP_TO_RIGHT, &p ) + || 0 == mol_read_datum( &ctab->MolAtom[i].szAtomSymbol, 3, MOL_STRING_DATA, &p ) /* was sizeof(ctab->MolAtom[0].szAtomSymbol)-1 */ + || 0 > mol_read_datum( &ctab->MolAtom[i].cMassDifference, 2, MOL_SHORT_INT_DATA, &p ) + || 0 > mol_read_datum( &ctab->MolAtom[i].cCharge, 3, MOL_CHAR_INT_DATA, &p ) + || 0 > mol_read_datum( &ctab->MolAtom[i].cStereoParity, 3, MOL_CHAR_INT_DATA, &p ) +#if ( MOL_QUERY == MOL_PRESENT ) + || 0 > mol_read_datum( &ctab->MolAtom[i].cH_countPlus1, 3, MOL_CHAR_INT_DATA, &p ) + || 0 > mol_read_datum( &ctab->MolAtom[i].cStereoCare, 3, MOL_CHAR_INT_DATA, &p ) +#else + || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) + || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) +#endif + || 0 > mol_read_datum( &ctab->MolAtom[i].cValence, 3, MOL_CHAR_INT_DATA, &p ) ) { + + err = 4; + MOLFILE_ERR_SET (err, 4, "Cannot interpret atom block line:"); + RemoveNonPrintable( line ); + AddMOLfileError(pStrErr, line); + if ( !strcmp( line, SDF_END_OF_DATA ) ) { + err = -abs(err); + break; + } + continue; /* can't interpret a first half of atom block line */ + } + if ( 2 == strlen(ctab->MolAtom[i].szAtomSymbol) && isupper(UCINT ctab->MolAtom[i].szAtomSymbol[1])) + ctab->MolAtom[i].szAtomSymbol[1] = (char)tolower(UCINT ctab->MolAtom[i].szAtomSymbol[1]); /* 5-4-99 DCh*/ + + if ( (chg = (S_SHORT) ctab->MolAtom[i].cCharge)< 0 || chg >= (int)(sizeof ( charge_val ) / sizeof( charge_val[0] )) ) { + /* ctab->MolAtom[i].cCharge = 0; */ /* error; ignore for now */ + ctab->MolAtom[i].cCharge = (S_CHAR)(4 - chg); /* allow greater charges to accommodate NCI structures. 8-20-2002 */ + ctab->MolAtom[i].cRadical = 0; + }else + if ( 'R' == (chg = charge_val[chg]) ){ + ctab->MolAtom[i].cCharge = 0; + ctab->MolAtom[i].cRadical = RADICAL_DOUBLET; + }else{ + ctab->MolAtom[i].cCharge = (S_CHAR)chg; /* actual charge value */ + ctab->MolAtom[i].cRadical = 0; + } + if ( ctab->MolAtom[i].cMassDifference ) { /* e_ReadMOL.c specific */ + ctab->MolAtom[i].cMassDifference += ISOTOPIC_SHIFT_FLAG; + } + + if ( +#if ( MOL_CPSS == MOL_PRESENT ) + 0 > mol_read_datum( &ctab->MolAtom[i].cH0_designator, 3, MOL_CHAR_INT_DATA, &p ) + || 0 > mol_read_datum( &ctab->MolAtom[i].cReactionComponentType, 3, MOL_CHAR_INT_DATA, &p ) + || 0 > mol_read_datum( &ctab->MolAtom[i].cReactionComponentNumber, 3, MOL_CHAR_INT_DATA, &p ) +#else + 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) + || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) + || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) +#endif +#if ( MOL_REACT == MOL_PRESENT ) + || 0 > mol_read_datum( &ctab->MolAtom[i].nAtomAtomMappingNumber, 3, MOL_SHORT_INT_DATA, &p ) + || 0 > mol_read_datum( &ctab->MolAtom[i].cReactionComponentType, 3, MOL_CHAR_INT_DATA, &p ) +#else + || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) + || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) +#endif +#if ( MOL_REACT == MOL_PRESENT || MOL_QUERY == MOL_PRESENT ) + || 0 > mol_read_datum( &ctab->MolAtom[i].cExactChargeFlag, 3, MOL_CHAR_INT_DATA, &p ) +#else + || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) +#endif + ){ + err = 5; /* can't interpret a second half of atom block line */ + MOLFILE_ERR_SET (err, 5, "Cannot interpret atom block line:"); + RemoveNonPrintable( line ); + AddMOLfileError(pStrErr, line); + if ( !strcmp( line, SDF_END_OF_DATA ) ) { + err = -abs(err); + break; + } + continue; + } + } + } +/* err_fin: */ + return err; +} +/************ static *************************************************************/ +int read_bonds_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ) +{ + char *p; + char line[MOLFILEINPLINELEN]; + const int line_len = sizeof(line); + S_SHORT i; + /* + if ( NULL == ctab->MolBond ){ + err = 1; + goto err_fin; // internal error: memory has not been allocated for MolBond structure + } + */ + for ( i = 0; i < ctab->nNumberOfBonds; i++ ) { + + if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ + if ( !err ) { + MOLFILE_ERR_SET (err, 2, "Cannot read bond block line"); + } + break; + } + remove_one_lf( line ); + if ( line[MOLFILEMAXLINELEN] ){ + err = err? err : 3; /* too long input file line */ + } + if ( err ) { + if ( !strcmp( line, SDF_END_OF_DATA ) ) { + err = -abs(err); + break; + } + continue; + } + + if ( ctab->MolBond ) { + if ( 0 > mol_read_datum( &ctab->MolBond[i].nAtomNo1, 3, MOL_SHORT_INT_DATA, &p ) + || 0 > mol_read_datum( &ctab->MolBond[i].nAtomNo2, 3, MOL_SHORT_INT_DATA, &p ) + || 0 > mol_read_datum( &ctab->MolBond[i].cBondType, 3, MOL_CHAR_INT_DATA, &p ) + || 0 > mol_read_datum( &ctab->MolBond[i].cBondStereo, 3, MOL_CHAR_INT_DATA, &p ) +#if ( MOL_QUERY == MOL_PRESENT ) + || 0 > mol_read_datum( &ctab->MolBond[i].cBondTopology, 3, MOL_CHAR_INT_DATA, &p ) /* ring/chain */ +#else + || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) +#endif +#if ( MOL_REACT == MOL_PRESENT ) + || 0 > mol_read_datum( &ctab->MolBond[i].cReactingCenterStatus, 3, MOL_CHAR_INT_DATA, &p ) +#else + || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) +#endif + ){ + if ( !err ) { + /* can't interpret bonds block line */ + MOLFILE_ERR_SET (err, 4, "Cannot interpret bond block line:"); + RemoveNonPrintable( line ); + AddMOLfileError(pStrErr, line); + } + if ( !strcmp( line, SDF_END_OF_DATA ) ) { + err = -abs(err); + break; + } + } + } + } + /* err_fin: */ + return err; +} +/********** static ***************************************************************/ +int read_stext_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ) +{ + /* just pass by all stext enties without attemp to interpret */ + char *p; + char line[MOLFILEINPLINELEN]; + const int line_len = sizeof(line); + S_SHORT i; + + for ( i = 0; i < 2*ctab->nNumberOfStextEntries; i++ ) { + + if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ + if ( !err ) { + MOLFILE_ERR_FIN (err, 2, err_fin, "Cannot read STEXT block line"); + } + break; + /* can't read the input file line */ + } + /* + remove_one_lf( line ); + if ( line[MOLFILEMAXLINELEN] ){ + MOLFILE_ERR_SET (err, 2, "Warning: Too long STEXT block line"); + // too long input file line + } + */ + } +err_fin: + return err; +} +/************ static *************************************************************/ +int read_properties_block( MOL_CTAB* ctab, MOL_HEADER_BLOCK *pHdr, FILE *inp, int err, char *pStrErr ) +{ + enum { MULTI_LINE_MODE_NO_MODE, MULTI_LINE_MODE_ISIS_ALIAS }; + char *p; + char line[MOLFILEINPLINELEN]; + const int line_len = sizeof(line); + int nMultiLineMode = MULTI_LINE_MODE_NO_MODE, nAtomNumber=0; + S_SHORT i, j; + char charM[2]; + char szBlank[3]; + char szType[4]; + S_SHORT skip_lines=0; + S_SHORT num_entries; + S_SHORT num_atoms = ctab->nNumberOfAtoms; + + int charge_encountered = 0; + int radical_encountered = 0; + int isotope_encountered = 0; + /* + if ( NULL == ctab->MolAtom ){ + err = 1; + goto err_fin; internal error: memory has not been allocated for MolAtom structure + } + */ + for ( i = 0; ctab->csCurrentCtabVersion[0]? 1 : (i < ctab->nNumberOfPropertyLines); i++ ) { /* the last line should be M END */ + /* ctab->csCurrentCtabVersion[0] == 0: + exactly ctab->nNumberOfPropertyLines lines including M END */ + /* ctab->csCurrentCtabVersion[0] != 0: + read until M END line was encountered */ + if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ + if ( !err ) { + MOLFILE_ERR_SET (err, 2, "Cannot read properties block line"); + } + goto err_fin; + } + remove_one_lf( line ); + if ( line[MOLFILEMAXLINELEN] ){ + MOLFILE_ERR_SET (err, 3, "Too long properties block line"); + continue; + } + if ( skip_lines > 0 ) { + skip_lines --; + continue; + } + /* alias. */ + if ( nMultiLineMode == MULTI_LINE_MODE_ISIS_ALIAS && nAtomNumber ) { + int len; + nMultiLineMode = MULTI_LINE_MODE_NO_MODE; + if ( 0 >= (len=normalize_name( p )) ) { + nAtomNumber = 0; + continue; + } + if( 0 < len && len < (int)(sizeof(ctab->MolAtom->szAtomSymbol)) ) { + int nCharge, nRad; + MOL_ATOM* MolAtom = ctab->MolAtom + nAtomNumber-1; + /* ctab->MolAtom[nAtomNumber-1].cAtomAliasedFlag = 1; */ + /* extract radicals & charges */ + extract_ChargeRadical( p, &nRad, &nCharge ); + /* Aliased atom cannot have charge, radical & mass difference */ + /* in the atom table or "M CHG", "M RAD", "M ISO" */ + /* if ( nCharge ) */ + MolAtom->cCharge = (S_CHAR)nCharge; + /* if ( nRad ) */ + MolAtom->cRadical = (char)nRad; + + if ( 1 == len && 'D' == p[0] ) { + /* H isotope */ + p[0] = 'H'; + MolAtom->cMassDifference=(1 + ISOTOPIC_SHIFT_FLAG); + } else + if ( 1 == len && 'T' == p[0] ) { + /* H isotope */ + p[0] = 'H'; + MolAtom->cMassDifference=(2 + ISOTOPIC_SHIFT_FLAG); + } else + MolAtom->cMassDifference=0; + if ( strlen(p) < sizeof(ctab->MolAtom[0].szAtomSymbol) ) { + strcpy(MolAtom->szAtomSymbol, p); + } else { + strcpy(MolAtom->szAtomSymbol, "???"); + } + MolAtom->cAtomAliasedFlag ++; + } + skip_lines = 0; + nAtomNumber = 0; + continue; + } + + if ( 1 != mol_read_datum( charM, sizeof(charM) - 1, MOL_STRING_DATA, &p ) + || 0 != mol_read_datum( szBlank, sizeof(szBlank) - 1, MOL_STRING_DATA, &p ) /* must contain 0 bytes */ + || 0 >= mol_read_datum( szType, sizeof(szType) - 1, MOL_STRING_DATA, &p ) /* must contain 3 bytes */ + ) { + if ( !strcmp( line, SDF_END_OF_DATA ) ) { + err = err? -abs(err): -4; + break; + } + continue; /* ignore because cannot recognize */ + } + if ( charM[0] == 'V' ){ + skip_lines = 0; /* ISIS/Desktop Atom Value: one-line property */ + continue; + } + if ( charM[0] == 'G' ){ + skip_lines = 1; /* ISIS/Desktop Group abbreviation: two-line property */ + continue; + } + if ( charM[0] == 'A' ) { + if ( NULL != ctab->MolAtom && + 0 < ( nAtomNumber = (int)strtol(szType, NULL, 10) ) && + nAtomNumber <= ctab->nNumberOfAtoms ){ + /* Atom Alias [ISIS/Desktop] two-line property */ + nMultiLineMode = MULTI_LINE_MODE_ISIS_ALIAS; + continue; + } else { + nAtomNumber = 0; + skip_lines = 1; + continue; + } + } + if ( charM[0] == 'S' && !strcmp( szType, "SKP" ) ){ /* skip lines */ + if ( 0 >= mol_read_datum( &skip_lines, 3, MOL_SHORT_INT_DATA, &p ) ) { + skip_lines = 0; + } + continue; + } + if ( charM[0] != 'M' ) {/* cannot recognize a line */ + continue; + } + if ( !strcmp( szType, "REG" ) ) { + int len; + p = p + strspn( p, " " ); + len = strcspn( p, " " ); + len = inchi_min( len, MOL_MAX_VALUE_LEN ); + mol_read_datum( &pHdr->lInternalRegistryNumber, len, MOL_LONG_INT_DATA, &p ); + continue; + } + + if ( !strcmp( szType, "END" ) ){ + if ( ctab->csCurrentCtabVersion[0] ) + break; /* end of property lines */ + continue; + } + + if ( NULL == ctab->MolAtom ) + continue; /* ignore because the user requested to bypass all this stuff */ + + /*----------------------------------- charge: Generic */ + if ( !strcmp( szType, "CHG" ) && + 0 < mol_read_datum( &num_entries, 3, MOL_SHORT_INT_DATA, &p ) && + 1 <= num_entries && num_entries <= 8 ) { + S_SHORT atoms[8]; + S_SHORT charges[8]; + if ( !charge_encountered && !radical_encountered ) { + /* first charge or radical record clears all Atom Block */ + /* entered charge and radical data to zeroes */ + charge_encountered = -1; + } + for ( j = 0; j < num_entries; j++ ) { + if ( 0 > mol_read_datum( &atoms[j], 0, MOL_SHORT_INT_DATA, &p ) || + 0 > mol_read_datum( &charges[j], 0, MOL_SHORT_INT_DATA, &p ) || + atoms[j] <= 0 || atoms[j] > num_atoms || + charges[j] < -15 || charges[j] > 15 ) { + goto charge_error; + } + } + if ( charge_encountered == -1 ) { + for ( j = 0; j < num_atoms; j++ ) { + if ( !ctab->MolAtom[j].cAtomAliasedFlag ) /* do not clear aliased atoms.*/ + ctab->MolAtom[j].cCharge = ctab->MolAtom[j].cRadical = '\0'; + } + charge_encountered = 1; + } + for ( j = 0; j < num_entries; j++ ) { + if ( !ctab->MolAtom[atoms[j]-1].cAtomAliasedFlag ) /* do not change aliased atoms.*/ + ctab->MolAtom[atoms[j]-1].cCharge = (S_CHAR)charges[j]; + } + continue; + charge_error: + MOLFILE_ERR_SET (err, 0, "Charge not recognized:"); + RemoveNonPrintable( line ); + AddMOLfileError(pStrErr, line); + continue; /* ignore for now */ + } + /*-------------------------------------- radical: Generic */ + if ( !strcmp( szType, "RAD" ) && + 0 < mol_read_datum( &num_entries, 3, MOL_SHORT_INT_DATA, &p ) && + 1 <= num_entries && num_entries <= 8 ) { + S_SHORT atoms[8]; + S_SHORT radicals[8]; + if ( !charge_encountered && !radical_encountered ) { + /* first charge or radical record clears all Atom Block */ + /* entered charge and radical data to zeroes */ + radical_encountered = -1; + } + for ( j = 0; j < num_entries; j++ ) { + if ( 0 > mol_read_datum( &atoms[j], 0, MOL_SHORT_INT_DATA, &p ) || + 0 > mol_read_datum( &radicals[j], 0, MOL_SHORT_INT_DATA, &p ) || + atoms[j] <= 0 || atoms[j] > num_atoms || + radicals[j] < 0 || radicals[j] > 3 ) { + goto radical_error; + } + } + if ( radical_encountered == -1 ) { + for ( j = 0; j < num_atoms; j++ ) { + if ( !ctab->MolAtom[j].cAtomAliasedFlag ) /* do not clear aliased atoms. 5-3-99 DCh */ + ctab->MolAtom[j].cCharge = ctab->MolAtom[j].cRadical = '\0'; + } + radical_encountered = 1; + } + for ( j = 0; j < num_entries; j++ ) { + if ( !ctab->MolAtom[atoms[j]-1].cAtomAliasedFlag ) { /* do not change aliased atoms. 5-3-99 DCh */ + ctab->MolAtom[atoms[j]-1].cRadical = (S_CHAR)radicals[j]; + } + } + continue; + radical_error: + MOLFILE_ERR_SET (err, 0, "Radical not recognized:"); + RemoveNonPrintable( line ); + AddMOLfileError(pStrErr, line); + continue; /* ignore error for now */ + } + /*-------------------------------------- isotope: Generic */ + if ( !strcmp( szType, "ISO" ) && + 0 < mol_read_datum( &num_entries, 3, MOL_SHORT_INT_DATA, &p ) && + 1 <= num_entries && num_entries <= 8 ) + { + S_SHORT atoms[8]; + S_SHORT iso_mass[8]; /* contains istotope mass number, not difference. 7-14-00 DCh. */ + if ( !isotope_encountered ) { + /* first charge or radical record clears all Atom Block */ + /* entered charge and radical data to zeroes */ + isotope_encountered = -1; + } + for ( j = 0; j < num_entries; j++ ) { + if ( 0 > mol_read_datum( &atoms[j], 0, MOL_SHORT_INT_DATA, &p ) || + 0 > mol_read_datum( &iso_mass[j], 0, MOL_SHORT_INT_DATA, &p ) || + atoms[j] <= 0 || atoms[j] > num_atoms + /*|| iso_mass[j] < -18 || iso_mass[j] > 12*/ ) { + /* goto isotope_error; */ + atoms[j] = -1; /* flag error */ + MOLFILE_ERR_SET (err, 0, "Isotopic data not recognized:"); + RemoveNonPrintable( line ); + AddMOLfileError(pStrErr, line); + continue; /* ignore isotopic error for now */ + } + } + if ( isotope_encountered == -1 ) { + for ( j = 0; j < num_atoms; j++ ) { + /*if ( !ctab->MolAtom[j].cAtomAliasedFlag )*/ /* clear even aliased atoms */ + ctab->MolAtom[j].cMassDifference = 0; + } + isotope_encountered = 1; + } + for ( j = 0; j < num_entries; j++ ) { + if ( atoms[j] <= 0 ) + continue; /* ignore isotopic error for now */ + if ( 1 /* !ctab->MolAtom[atoms[j]-1].cAtomAliasedFlag */) { + char *at = ctab->MolAtom[atoms[j]-1].szAtomSymbol; + if ( at[1] || at[0] != 'D' && at[0] != 'T' ) { /* D & T cannot have ISO */ + /* need atomic weight to calculate isotope difference. 7-14-00 DCh. */ + /*^^^ Check added 5-10-2008 - IPl */ + if (iso_mass[j] > 0) + /* According to MDL specification, p.12, only a positive + integer is allowed. And yes, there appeared some MOL/SD + files contaning here a negative value. This manifested + in mismatch in InChI_MAIN vs. cInChI-1/stdinchi-1 results. + */ + ctab->MolAtom[atoms[j]-1].cMassDifference = iso_mass[j]; /* mass, not difference */ + } + } + } + continue; + } + /*-------------------------------------- this demo must now reject polymers */ + if ( !strcmp( szType, "STY" ) || + !strcmp( szType, "SAL" ) ) + { + if ( !err ) + { + MOLFILE_ERR_SET (err, 2, "This demo program can not treat polymers"); + } + goto err_fin; + } + } +err_fin: + return err; +} +/************ global *************************************************************/ +MOL_DATA* delete_mol_data( MOL_DATA* mol_data ) +{ + if ( mol_data ) { + if ( mol_data->ctab.MolAtom ) + inchi_free( mol_data->ctab.MolAtom ); + if ( mol_data->ctab.MolBond ) + inchi_free( mol_data->ctab.MolBond ); + if ( mol_data->ctab.szCoord ) + inchi_free( mol_data->ctab.szCoord ); + inchi_free( mol_data ); + mol_data = NULL; + } + return mol_data; +} +/************* global ************************************************************/ +/* Comletely ingnore STEXT block, queries, and 3D features + */ +MOL_DATA* read_mol_file( FILE* inp, MOL_HEADER_BLOCK *OnlyHeaderBlock, MOL_CTAB *OnlyCtab, + int bGetOrigCoord, int *err, char *pStrErr ) +{ + MOL_DATA* mol_data = NULL; + int ret = 0, prev_ret, bEndOfData = 0; + int bReadAll = ( OnlyHeaderBlock == NULL ); + MOL_CTAB ctab, *pCtab = NULL; + MOL_HEADER_BLOCK *pHdr = NULL; + + *err = 0; + if ( bReadAll ) { + if ( NULL == ( mol_data = ( MOL_DATA* )inchi_calloc( 1, sizeof(MOL_DATA) ) ) ){ + ret = 1; /* can't allocate mol_data structure */ + AddMOLfileError( pStrErr, "Out of RAM" ); + goto err_fin; + } + pHdr = &mol_data->hdr; + pCtab = &mol_data->ctab; + } else { + pHdr = OnlyHeaderBlock; + pCtab = OnlyCtab? OnlyCtab : &ctab; + memset( pHdr, 0, sizeof( MOL_HEADER_BLOCK ) ); + memset( pCtab, 0, sizeof( MOL_CTAB ) ); + } + pCtab->MolBond = NULL; + pCtab->MolAtom = NULL; + pCtab->szCoord = NULL; + + if ( 0 != ( ret = mol_read_hdr(pHdr, inp, pStrErr) ) ){ + ret += 10; + goto err_fin; /* most probably end of file */ + } + if ( 0 != ( ret = mol_read_counts_line( pCtab , inp, pStrErr) ) ){ + ret += 20; + goto err_fin; + } + + if ( bReadAll ) { + if ( NULL == ( mol_data->ctab.MolAtom = (MOL_ATOM*)inchi_calloc(inchi_max(mol_data->ctab.nNumberOfAtoms,1), sizeof(MOL_ATOM)) ) ){ + ret = 2; /* can't allocate MolAtom structure */ + MOLFILE_ERR_FIN (ret, 2, err_fin, "Out of RAM"); + } + if ( bGetOrigCoord && + NULL == ( mol_data->ctab.szCoord = (MOL_COORD*)inchi_calloc(inchi_max(mol_data->ctab.nNumberOfAtoms,1), sizeof(MOL_COORD)) ) ){ + ret = 2; /* can't allocate MolAtom structure */ + MOLFILE_ERR_FIN (ret, 2, err_fin, "Out of RAM"); + } + } + if ( 0 != ( ret = read_atom_block(pCtab, inp, ret, pStrErr) ) ){ + if ( ret < 0 ) { + ret = -ret; + bEndOfData = 1; + } + ret += 30; + /* goto err_fin; */ + } + + if ( bReadAll && ret < 30 ) { + if ( !bEndOfData && NULL == ( mol_data->ctab.MolBond = (MOL_BONDS*)inchi_calloc(inchi_max(mol_data->ctab.nNumberOfBonds,1), sizeof(MOL_BONDS)) ) ){ + ret = 3; /* can't allocate MolBond structure */ + MOLFILE_ERR_FIN (ret, 3, err_fin, "Out of RAM"); + } + } + prev_ret = ret; + if ( !bEndOfData && 0 != ( ret = read_bonds_block(pCtab, inp, ret, pStrErr) ) ){ + if ( ret < 0 ) { + ret = -ret; + bEndOfData = 1; + } + ret = prev_ret? prev_ret : ret + 40; + } + prev_ret = ret; + if ( !bEndOfData && 0 != ( ret = read_stext_block(pCtab, inp, ret, pStrErr) ) ){ + ret = prev_ret? prev_ret : ret + 50; + } + prev_ret = ret; + if ( !bEndOfData && 0 != ( ret = read_properties_block(pCtab, pHdr, inp, ret, pStrErr) ) ){ + if ( ret < 0 ) { + ret = -ret; + bEndOfData = 1; + } + ret = prev_ret? prev_ret : ret + 60; + } + +err_fin: + *err = bEndOfData? -ret : ret; + if ( bReadAll ) { + if ( ret ) + mol_data = delete_mol_data( mol_data ); /* delete all results */ + return mol_data; + } else { + if ( ret ) + return NULL; + else + return (MOL_DATA*)OnlyHeaderBlock; + } +} + +/******************************************************************/ +static const char sdf_data_hdr_name[] = "NAME"; +static const char sdf_data_hdr_comm[] = "COMMENT"; +enum { SDF_START, SDF_DATA_HEADER, SDF_DATA_HEADER_NAME + , SDF_DATA_HEADER_COMMENT, SDF_DATA_HEADER_CAS + , SDF_DATA_HEADER_USER, SDF_DATA_LINE + , SDF_END_OF_DATA_ITEM, SDF_EMPTY_LINE, SDF_END_OF_DATA_BLOCK }; +/********** static ********************************************************/ +long extract_cas_rn( char *line ) +{ + int i, j; + i = line[0] == '-'? 1 : 0; + for ( j = i; line[i]; i ++ ) { + if ( isdigit( UCINT line[i] ) ) { + line[j++] = line[i]; + } else + if ( line[i] != '-' ) { + break; + } + } + line[j] = '\0'; + return strtol( line, NULL, 10 ); +} +/********** static ********************************************************/ +int identify_sdf_label( char* inp_line, const char *pSdfLabel ) +{ + char line[MOLFILEMAXLINELEN]; + char *p, *q; + int i, j, len; + if ( (p = strchr( inp_line, '<' )) && + (q = strchr( p, '>' )) && + (len = q-p-1) > 0 && len < (int)sizeof(line) ) { + memcpy( line, p+1, len ); + line[len] = '\0'; + for ( i = 0; isspace( UCINT line[i] ); i ++ ) + ; + for ( j = len-1; j >= i && isspace( UCINT line[i] ); j -- ) + ; + len = j-i+1; + p = line+i; + if ( pSdfLabel && pSdfLabel[0] && len == (int)strlen(pSdfLabel) && !e_inchi_memicmp( p, pSdfLabel, len ) ) + return SDF_DATA_HEADER_USER; + if ( len == sizeof(sdf_data_hdr_name)-1 && !e_inchi_memicmp( p, sdf_data_hdr_name, len ) ) + return SDF_DATA_HEADER_NAME; + if ( len == sizeof(sdf_data_hdr_comm)-1 && !e_inchi_memicmp( p, sdf_data_hdr_comm, len ) ) + return SDF_DATA_HEADER_COMMENT; + if ( !e_inchi_memicmp( p, "CAS", 3 ) ) + return SDF_DATA_HEADER_CAS; + } + return SDF_DATA_HEADER; +} +/************* global *****************************************************/ +int bypass_sdf_data_items( FILE* inp, long *cas_reg_no, char* comment, + int lcomment, char *name, int lname, int prev_err, + const char *pSdfLabel, char *pSdfValue, char *pStrErr ) +{ + char line[MOLFILEINPLINELEN]; + const int line_len = sizeof(line); + int err = 0; + int current_state = SDF_START; + int n_blank_lines = 0; + int n_lines = 0; + char* p = NULL; + int bNeedsName = name && lname > 0 && !name[0]; + int bNeedsComm = comment && lcomment > 0 && !comment[0]; + int bNeedsUser = pSdfLabel && pSdfLabel[0] && pSdfValue; + int bNeedsCASrn = 0; + int bCASrnIsUser = 0; + + if ( cas_reg_no != NULL ) { + bNeedsCASrn = 1; + *cas_reg_no = 0; + bCASrnIsUser = (bNeedsUser && !e_inchi_memicmp(pSdfLabel,"CAS", 3)); + } + + while ( err == 0 && + current_state !=SDF_END_OF_DATA_BLOCK && + NULL != ( p = inchi_fgetsLf( line, line_len, inp ) ) ) { + + if ( !n_lines && !memcmp(line, "M END", 6) ) { + continue; /* allow subtle errors */ + } + n_lines++; + + remove_trailing_spaces( line ); + if ( line[MOLFILEMAXLINELEN] ){ + if ( current_state != SDF_DATA_HEADER && + current_state != SDF_DATA_LINE && + current_state != SDF_DATA_HEADER_NAME && + current_state != SDF_DATA_HEADER_USER && + current_state != SDF_DATA_HEADER_COMMENT ) { + line[MOLFILEMAXLINELEN] = '\0'; + if ( !prev_err ) { + MOLFILE_ERR_SET (err, 0, "Too long SData line truncated"); + } + } else { + /* allow long lines in SDF data. 9-29-00 DCh */ + line[MOLFILEMAXLINELEN] = '\0'; + } + } + + n_blank_lines += ( *line == '\0' ); + + switch( current_state ) { + + case SDF_START: + case SDF_END_OF_DATA_ITEM: + case SDF_EMPTY_LINE: /* Added 9-25-97 DCh */ + + if ( 0 == strcmp( line, SDF_END_OF_DATA ) ) { + current_state = SDF_END_OF_DATA_BLOCK; + } + else + if ( '>' == *line ) { + current_state = ( bNeedsName || bNeedsComm || bNeedsCASrn || bNeedsUser )? identify_sdf_label(line, pSdfLabel) : SDF_DATA_HEADER; + }else + if ( *line == '\0' ) { /* Added 9-25-97 DCh */ + /* Relax the strictness: Allow more than 1 empty line. */ + current_state=SDF_EMPTY_LINE; + } else + if ( !prev_err ) { + MOLFILE_ERR_SET (err, 3, "Unexpected SData header line:"); + RemoveNonPrintable( line ); + AddMOLfileError(pStrErr, line); + /* unexpected contents of data header line */ + } else { + err = 3; + } + break; + + case SDF_DATA_HEADER_NAME: + if ( bNeedsName && 0 < normalize_name( line ) ) { + bNeedsName = 0; + mystrncpy( name, line, lname ); + } + goto got_data_line; + + case SDF_DATA_HEADER_COMMENT: + if ( bNeedsComm && 0 < normalize_name( line ) ) { + bNeedsComm = 0; + mystrncpy( comment, line, lcomment ); + } + goto got_data_line; + + case SDF_DATA_HEADER_USER: + if ( bNeedsUser && 0 < normalize_name( line ) ) { + bNeedsUser = 0; + mystrncpy( pSdfValue, line, MAX_SDF_VALUE+1 ); + if ( bCASrnIsUser && bNeedsCASrn ) { + *cas_reg_no = extract_cas_rn( line ); + bNeedsCASrn = (0 == *cas_reg_no); + } + } + goto got_data_line; + + case SDF_DATA_HEADER_CAS: + if ( bNeedsCASrn && 0 < normalize_name( line ) ) { + *cas_reg_no = extract_cas_rn( line ); + bNeedsCASrn = (0 == *cas_reg_no); + } + goto got_data_line; + + case SDF_DATA_HEADER: + case SDF_DATA_LINE: +got_data_line: + current_state = *line? SDF_DATA_LINE : SDF_END_OF_DATA_ITEM; + break; + } + } + if ( 0 == err && SDF_END_OF_DATA_BLOCK != current_state && NULL == p ) + ; /* err = 4; */ /* unexpected end of file: missing $$$$ */ + else + if (err && ( n_blank_lines == n_lines && *line == '\0' ) ) + err = 5; /* empty lines -- do not know when this can happen */ + + if ( err && err != 5 && current_state != SDF_END_OF_DATA_BLOCK && p ) { + /* bypass up to $$$$ */ + while ( ( p = inchi_fgetsLf( line, line_len, inp ) ) && memcmp( line, SDF_END_OF_DATA, 4 ) ) + ; + if ( p ) { + err = 9; /* bypassed to $$$$; non-fatal */ + AddMOLfileError(pStrErr, "Bypassing to next structure"); + } + } + + return err; +} +/**************** global **************************************************/ +MOL_DATA* read_sdfile_segment(FILE* inp, MOL_HEADER_BLOCK *OnlyHeaderBlock, MOL_CTAB *OnlyCtab, + int bGetOrigCoord, + char *pname, int lname, + long *Id, const char *pSdfLabel, char *pSdfValue, + int *err, char *pStrErr ) +{ + MOL_DATA* mol_data = read_mol_file( inp, OnlyHeaderBlock, OnlyCtab, bGetOrigCoord, err, pStrErr ); + int err_bypass_sdf = 0; + + if ( pname && lname ) { + pname[0] = '\0'; + } + if ( Id ) { + *Id = 0L; /* ignore for now */ + } + /* if ( mol_data && !*err ) { */ + if ( *err < 0 ) { + *err = -*err; /* end of data encountered */ + } else { + err_bypass_sdf = bypass_sdf_data_items( inp, Id, NULL, 0, pname, lname, *err, pSdfLabel, pSdfValue, pStrErr ); + if ( err_bypass_sdf ) { + *err = err_bypass_sdf; /* important to continue to the next good structure */ + } + } + /* } */ + return mol_data; +} +/******************* global *********************************************************/ +int CopyMOLfile(FILE *inp_file, long fPtrStart, long fPtrEnd, FILE *prb_file, long lNumb) +{ + char line[MOLFILEINPLINELEN], *p; + long fPtr; + int ret = 1; + char szNumber[32]; + + if ( inp_file && prb_file && fPtrStart >= 0L && + fPtrEnd > fPtrStart && + 0 == fseek( inp_file, fPtrStart, SEEK_SET ) ) { + + while ( fPtrEnd > (fPtr = ftell(inp_file)) && fPtr >= 0L && + inchi_fgetsLf( line, sizeof(line)-1, inp_file ) ) { + line[sizeof(line)-1] = '\0'; /* unnecessary extra precaution */ + if ( fPtr == fPtrStart && lNumb ) { + int len; + LtrimRtrim( line, &len ); + len = sprintf( szNumber, "#%ld%s", lNumb, len?"/":"" ); + mystrncpy( line+len, line, sizeof(line)-len-1 ); + memcpy( line, szNumber, len ); + } + if ( !strchr(line, '\n') ) { + p = line+strlen(line); + p[0] = '\n'; + p[1] = '\0'; + } + fputs( line, prb_file ); + } + ret = fseek( inp_file, fPtrEnd, SEEK_SET ); + } + return ret; +} diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/e_readmol.h b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_readmol.h similarity index 90% rename from INCHI-1-SRC/INCHI_API/inchi_main/e_readmol.h rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_readmol.h index ff3a15f..a97b90c 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_main/e_readmol.h +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_readmol.h @@ -1,211 +1,210 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __READMOL_H__ -#define __READMOL_H__ - -/*************** read MOL file V2000.************************/ -/* ref: A.Dalby et al, "Description of Several Chemical Structure - * File Formats Used by Computer Programs Developed at Molecular - * Design Limited", J. Chem. Inf. Comput. Sci., 1992, 32, 244-255. - */ - -#if 0 -#define MOLFILEINPLINELEN 84 /* add cr, lf, double zero termination */ -#ifndef MOLFILEMAXLINELEN -#define MOLFILEMAXLINELEN 80 -#endif -#endif - -#define MOLFILEINPLINELEN 204 /* add cr, lf, double zero termination */ -#ifndef MOLFILEMAXLINELEN -#define MOLFILEMAXLINELEN 200 -#endif - - -#define MOL_PRESENT 1 -#define MOL_ABSENT 0 - -/* configuration */ -#define MOL_QUERY MOL_ABSENT -#define MOL_CPSS MOL_ABSENT -#define MOL_REACT MOL_ABSENT - -#define MOL_STRING_DATA 'S' -#define MOL_CHAR_INT_DATA 'C' -#define MOL_SHORT_INT_DATA 'N' -#define MOL_LONG_INT_DATA 'L' -#define MOL_DOUBLE_DATA 'D' -#define MOL_FLOAT_DATA 'F' -#define MOL_JUMP_TO_RIGHT 'J' -#define MOL_MAX_VALUE_LEN 32 /* max length of string containing a numerical value */ - - -#define SDF_END_OF_DATA "$$$$" - -/****************************************************************************/ -typedef struct tagMOL_HEADER_BLOCK { - /* Line #1 */ - char szMoleculeName[MOLFILEMAXLINELEN+1]; /* up to 80 characters */ - /* Line #2: optional */ - char szMoleculeLine2[MOLFILEMAXLINELEN+1]; /* the whole line2 -- up to 80 characters */ - char szUserInitials[3]; /* 2 bytes; char */ - char szProgramName[9]; /* 8 bytes; char */ - char cMonth; /* 2 bytes; integral */ - char cDay; /* 2 bytes; integral */ - char cYear; /* 2 bytes; integral */ - char cHour; /* 2 bytes; integral */ - char cMinute; /* 2 bytes; integral */ - char szDimCode[3]; /* 2 bytes: dimensional code; char */ - short nScalingFactor1; /* 2 bytes; I2 */ - double dScalingFactor2; /* 10 bytes, F10.5 */ - double dEnergy; /* 10 bytes, F10.5 */ - long lInternalRegistryNumber;/* 6 bytes, integral */ - /* Line #3: comment */ - char szComment[81]; -}MOL_HEADER_BLOCK; - -/****************************************************************************/ -typedef struct tagMOL_ATOM { - double fX; /* F10.5; Generic */ - double fY; /* F10.5; Generic */ - double fZ; /* F10.5; Generic */ - char szAtomSymbol[6]; /* aaa; Generic */ /* changed from 4 to 6 to match STDATA */ - S_SHORT cMassDifference; /* dd; (M_ISO) Generic: -3..+4 otherwise 0 or 127=most abund. isotope */ - S_CHAR cCharge; /* ccc; (M CHG), Generic: 1=+3,2=+2,3=+1,4=doublet,5=-1,6=-2,7=-3 */ - char cRadical; /* (M RAD) */ - char cStereoParity; /* sss; Generic */ -#if ( MOL_QUERY == MOL_PRESENT ) - char cH_countPlus1; /* hhh; Query; Hn means >= n H; H0 means no H */ - char cStereoCare; /* bbb; Query: 0=ignore; 1=must match */ -#endif - char cValence; /* vvv: 0=no marking; (1..14)=(1..14); 15=zero valence */ - /* number of bonds including bonds to implies H's */ -#if ( MOL_CPSS == MOL_PRESENT ) - char cH0_designator; /* HHH: CPSS */ - char cReactionComponentType; /* rrr: CPSS: 1=reactant, 2=product, 3=intermediate */ - char cReactionComponentNumber; /* iii: CPSS: 0 to (n-1) */ -#endif - -#if ( MOL_REACT == MOL_PRESENT ) - short nAtomAtomMappingNumber; /* mmm: Reaction: 1..255 */ - char cInversionRetentionFlag; /* nnn: 1=inverted,2=retained config.; 0=property not applied */ -#endif -#if ( MOL_REACT == MOL_PRESENT || MOL_QUERY == MOL_PRESENT ) - char cExactChargeFlag; /* eee: 1=charge on atom must match exactly, 0=property not applied */ -#endif - char cMyNumImpH; /* number of implicit H calculated for adding H to strings in STDATA */ - char cDisplayAtom; /* Do not hide element's name ( applies to C 7-25-98 DCh */ - char cAtomAliasedFlag; /* Do not remove charge/radical/isotope if it is in the alias. 9-3-99 DCh */ -} MOL_ATOM; - -/****************************************************************************/ -typedef struct tagMOL_BONDS { - short nAtomNo1; /* 111: First atom number: Generic */ - short nAtomNo2; /* 222: Second atom number: Generic */ - char cBondType; /* ttt: 1,2,3=single, double, triple; 4=aromatic; 5=single or double */ - /* 6=single or aromatic, 7=double or aromatic, 8=any. */ - /* values 4-8 are for SSS queries only */ - char cBondStereo; /* sss: Single bonds: 0=not stereo, 1=up, 4=either, 6=down */ - /* Double bonds: 0=use x,y,z to determine cis/trans */ - /* 3=cis or trans (either) */ - /* xxx: not used */ -#if ( MOL_QUERY == MOL_PRESENT ) - char cBondTopology; /* rrr: 0=either, 1=ring, 2=chain: SSS queries only */ -#endif -#if ( MOL_REACT == MOL_PRESENT ) - char cReactingCenterStatus; /* ccc: 0=unmarked, 1=a center, -1=not a center; Additional: */ - /* 2=no charge,4=bond made/broken,8=bond order changes */ - /* 12=4+8; 5=4+1, 9=8+1, 13=12+1 are also possible */ -#endif -} MOL_BONDS; -/****************************************************************************/ -typedef struct tagMOL_CTAB { - /* Line #1: Counts line */ - short nNumberOfAtoms; /* aaa; <= 999; Generic */ - short nNumberOfBonds; /* bbb; <= 999; Generic */ -#if ( MOL_QUERY == MOL_PRESENT ) - short nNumberOfAtomsLists; /* lll; <= 30; Query */ -#endif - /* fff; Obsolete */ - char cChiralFlag; /* ccc; 0 or 1; Generic */ - short nNumberOfStextEntries; /* sss; CPSS */ -#if ( MOL_CPSS == MOL_PRESENT ) - short nNumberOfReactionComponentsPlus1; /* xxx; CPSS */ - short nNumberOfReactants; /* rrr; CPSS */ - short nNumberOfProducts; /* ppp; CPSS */ - short nNumberOfIntermediates; /* iii; CPSS */ -#endif - short nNumberOfPropertyLines; /* mmm; Generic */ - char csCurrentCtabVersion[7]; /* vvvvvv; Generic; 'V2000' */ - /* The Atom Block */ - MOL_ATOM *MolAtom; - MOL_BONDS *MolBond; - MOL_COORD *szCoord; -} MOL_CTAB; - -typedef struct tagMOL_DATA { - MOL_HEADER_BLOCK hdr; - MOL_CTAB ctab; -} MOL_DATA; - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - -MOL_DATA* e_delete_mol_data( MOL_DATA* mol_data ); - -MOL_DATA* e_read_sdfile_segment(FILE* inp, MOL_HEADER_BLOCK *OnlyHeaderBlock, MOL_CTAB *OnlyCtab, - int bGetOrigCoord, - char *pname, int lname, - long *Id, const char *pSdfLabel, char *pSdfValue, - int *err, char *pStrErr ); - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -#endif /*__READMOL_H__*/ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef __READMOL_H__ +#define __READMOL_H__ + +/*************** read MOL file V2000.************************/ +/* ref: A.Dalby et al, "Description of Several Chemical Structure + * File Formats Used by Computer Programs Developed at Molecular + * Design Limited", J. Chem. Inf. Comput. Sci., 1992, 32, 244-255. + */ + +#if 0 +#define MOLFILEINPLINELEN 84 /* add cr, lf, double zero termination */ +#ifndef MOLFILEMAXLINELEN +#define MOLFILEMAXLINELEN 80 +#endif +#endif + +#define MOLFILEINPLINELEN 204 /* add cr, lf, double zero termination */ +#ifndef MOLFILEMAXLINELEN +#define MOLFILEMAXLINELEN 200 +#endif + + +#define MOL_PRESENT 1 +#define MOL_ABSENT 0 + +/* configuration */ +#define MOL_QUERY MOL_ABSENT +#define MOL_CPSS MOL_ABSENT +#define MOL_REACT MOL_ABSENT + +#define MOL_STRING_DATA 'S' +#define MOL_CHAR_INT_DATA 'C' +#define MOL_SHORT_INT_DATA 'N' +#define MOL_LONG_INT_DATA 'L' +#define MOL_DOUBLE_DATA 'D' +#define MOL_FLOAT_DATA 'F' +#define MOL_JUMP_TO_RIGHT 'J' +#define MOL_MAX_VALUE_LEN 32 /* max length of string containing a numerical value */ + + +#define SDF_END_OF_DATA "$$$$" + +/****************************************************************************/ +typedef struct tagMOL_HEADER_BLOCK { + /* Line #1 */ + char szMoleculeName[MOLFILEMAXLINELEN+1]; /* up to 80 characters */ + /* Line #2: optional */ + char szMoleculeLine2[MOLFILEMAXLINELEN+1]; /* the whole line2 -- up to 80 characters */ + char szUserInitials[3]; /* 2 bytes; char */ + char szProgramName[9]; /* 8 bytes; char */ + char cMonth; /* 2 bytes; integral */ + char cDay; /* 2 bytes; integral */ + char cYear; /* 2 bytes; integral */ + char cHour; /* 2 bytes; integral */ + char cMinute; /* 2 bytes; integral */ + char szDimCode[3]; /* 2 bytes: dimensional code; char */ + short nScalingFactor1; /* 2 bytes; I2 */ + double dScalingFactor2; /* 10 bytes, F10.5 */ + double dEnergy; /* 10 bytes, F10.5 */ + long lInternalRegistryNumber;/* 6 bytes, integral */ + /* Line #3: comment */ + char szComment[81]; +}MOL_HEADER_BLOCK; + +/****************************************************************************/ +typedef struct tagMOL_ATOM { + double fX; /* F10.5; Generic */ + double fY; /* F10.5; Generic */ + double fZ; /* F10.5; Generic */ + char szAtomSymbol[6]; /* aaa; Generic */ /* changed from 4 to 6 to match STDATA */ + S_SHORT cMassDifference; /* dd; (M_ISO) Generic: -3..+4 otherwise 0 or 127=most abund. isotope */ + S_CHAR cCharge; /* ccc; (M CHG), Generic: 1=+3,2=+2,3=+1,4=doublet,5=-1,6=-2,7=-3 */ + char cRadical; /* (M RAD) */ + char cStereoParity; /* sss; Generic */ +#if ( MOL_QUERY == MOL_PRESENT ) + char cH_countPlus1; /* hhh; Query; Hn means >= n H; H0 means no H */ + char cStereoCare; /* bbb; Query: 0=ignore; 1=must match */ +#endif + char cValence; /* vvv: 0=no marking; (1..14)=(1..14); 15=zero valence */ + /* number of bonds including bonds to implies H's */ +#if ( MOL_CPSS == MOL_PRESENT ) + char cH0_designator; /* HHH: CPSS */ + char cReactionComponentType; /* rrr: CPSS: 1=reactant, 2=product, 3=intermediate */ + char cReactionComponentNumber; /* iii: CPSS: 0 to (n-1) */ +#endif + +#if ( MOL_REACT == MOL_PRESENT ) + short nAtomAtomMappingNumber; /* mmm: Reaction: 1..255 */ + char cInversionRetentionFlag; /* nnn: 1=inverted,2=retained config.; 0=property not applied */ +#endif +#if ( MOL_REACT == MOL_PRESENT || MOL_QUERY == MOL_PRESENT ) + char cExactChargeFlag; /* eee: 1=charge on atom must match exactly, 0=property not applied */ +#endif + char cMyNumImpH; /* number of implicit H calculated for adding H to strings in STDATA */ + char cDisplayAtom; /* Do not hide element's name ( applies to C 7-25-98 DCh */ + char cAtomAliasedFlag; /* Do not remove charge/radical/isotope if it is in the alias. 9-3-99 DCh */ +} MOL_ATOM; + +/****************************************************************************/ +typedef struct tagMOL_BONDS { + short nAtomNo1; /* 111: First atom number: Generic */ + short nAtomNo2; /* 222: Second atom number: Generic */ + char cBondType; /* ttt: 1,2,3=single, double, triple; 4=aromatic; 5=single or double */ + /* 6=single or aromatic, 7=double or aromatic, 8=any. */ + /* values 4-8 are for SSS queries only */ + char cBondStereo; /* sss: Single bonds: 0=not stereo, 1=up, 4=either, 6=down */ + /* Double bonds: 0=use x,y,z to determine cis/trans */ + /* 3=cis or trans (either) */ + /* xxx: not used */ +#if ( MOL_QUERY == MOL_PRESENT ) + char cBondTopology; /* rrr: 0=either, 1=ring, 2=chain: SSS queries only */ +#endif +#if ( MOL_REACT == MOL_PRESENT ) + char cReactingCenterStatus; /* ccc: 0=unmarked, 1=a center, -1=not a center; Additional: */ + /* 2=no charge,4=bond made/broken,8=bond order changes */ + /* 12=4+8; 5=4+1, 9=8+1, 13=12+1 are also possible */ +#endif +} MOL_BONDS; +/****************************************************************************/ +typedef struct tagMOL_CTAB { + /* Line #1: Counts line */ + short nNumberOfAtoms; /* aaa; <= 999; Generic */ + short nNumberOfBonds; /* bbb; <= 999; Generic */ +#if ( MOL_QUERY == MOL_PRESENT ) + short nNumberOfAtomsLists; /* lll; <= 30; Query */ +#endif + /* fff; Obsolete */ + char cChiralFlag; /* ccc; 0 or 1; Generic */ + short nNumberOfStextEntries; /* sss; CPSS */ +#if ( MOL_CPSS == MOL_PRESENT ) + short nNumberOfReactionComponentsPlus1; /* xxx; CPSS */ + short nNumberOfReactants; /* rrr; CPSS */ + short nNumberOfProducts; /* ppp; CPSS */ + short nNumberOfIntermediates; /* iii; CPSS */ +#endif + short nNumberOfPropertyLines; /* mmm; Generic */ + char csCurrentCtabVersion[7]; /* vvvvvv; Generic; 'V2000' */ + /* The Atom Block */ + MOL_ATOM *MolAtom; + MOL_BONDS *MolBond; + MOL_COORD *szCoord; +} MOL_CTAB; + +typedef struct tagMOL_DATA { + MOL_HEADER_BLOCK hdr; + MOL_CTAB ctab; +} MOL_DATA; + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + + +MOL_DATA* e_delete_mol_data( MOL_DATA* mol_data ); + +MOL_DATA* e_read_sdfile_segment(FILE* inp, MOL_HEADER_BLOCK *OnlyHeaderBlock, MOL_CTAB *OnlyCtab, + int bGetOrigCoord, + char *pname, int lname, + long *Id, const char *pSdfLabel, char *pSdfValue, + int *err, char *pStrErr ); + + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + +#endif /*__READMOL_H__*/ diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/e_readstru.c b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_readstru.c similarity index 75% rename from INCHI-1-SRC/INCHI_API/inchi_main/e_readstru.c rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_readstru.c index 55a11d2..e480295 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_main/e_readstru.c +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_readstru.c @@ -1,304 +1,258 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include - -#include -#include -#include -#include -#include -#include - - -#include "e_mode.h" - -#include "e_ichisize.h" -#include "inchi_api.h" - -#include "ichitime.h" -#include "e_ctl_data.h" -#include "e_readstru.h" -#include "e_ichi_io.h" -#include "e_util.h" -#include "e_ichierr.h" -#include "e_inpdef.h" - -int e_TreatReadTheStructureErrors(STRUCT_DATA *sd, INPUT_PARMS *ip, - INCHI_IOSTREAM *inp_file, - INCHI_IOSTREAM* log_file, INCHI_IOSTREAM* output_file, INCHI_IOSTREAM* prb_file, - inchi_Input *pInp, long *num_inp ); -int e_GetInpStructErrorType( INPUT_PARMS *ip, int err, char *pStrErrStruct, int num_inp_atoms ); - -/*********************************************************************************************************/ -int e_ReadStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, - INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, - INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *prb_file, - inchi_Input *pInp, long num_inp, - /* for CML:*/ int inp_index, int *out_index ) -{ - inchiTime ulTStart; - int nRet = 0, nRet2 = 0; -#if( ADD_CMLPP == 1 ) - int bGetOrigCoord = 0; -#endif - INCHI_MODE InpAtomFlags = 0; - - /* vABParityUnknown holds actual value of an internal constant signifying */ - /* unknown parity: either the same as for undefined parity (default==standard) */ - /* or a specific one (non-std; requested by SLUUD switch). */ - int vABParityUnknown = AB_PARITY_UNDF; - if ( 0 != ( ip->nMode & REQ_MODE_DIFF_UU_STEREO) ) - { - /* Make labels for unknown and undefined stereo different */ - vABParityUnknown = AB_PARITY_UNKN; - } - - memset( sd, 0, sizeof(*sd) ); - switch ( ip->nInputType ) { - case INPUT_MOLFILE: - case INPUT_SDFILE: - if ( pInp ) { - if ( ip->pSdfValue && ip->pSdfValue[0] ) { - /* Added 07-29-2003 to avoid inheriting exact value from prev. structure - and to make reference to a (bad) structure with unknown ID Value */ - char *p, *q; /* q shadows prev declaration of const char *q */ - int n; - if ( (p = strrchr( ip->pSdfValue, '+' )) && - '[' == *(p-1) && 0 < (n=strtol(p+1,&q,10)) && q[0] && ']'==q[0] && !q[1] ) { - sprintf( p+1, "%d]", n+1 ); - } else { - strcat( ip->pSdfValue, " [+1]" ); - } - } - e_InchiTimeGet( &ulTStart ); - sd->fPtrStart = (inp_file->f == stdin || prb_file->f == NULL)? -1 : ftell( inp_file->f ); - /* read the original structure */ - nRet2 = e_MolfileToInchi_Input( inp_file->f, pInp, ip->bMergeAllInputStructures, - ip->bDoNotAddH, ip->bAllowEmptyStructure, - ip->pSdfLabel, ip->pSdfValue, &ip->lSdfId, &ip->lMolfileNumber, - &InpAtomFlags, &sd->nStructReadError, sd->pStrErrStruct ); - - sd->bChiralFlag |= InpAtomFlags; - if ( !ip->bGetSdfileId || ip->lSdfId == 999999) ip->lSdfId = 0; - if ( !ip->bGetMolfileNumber || ip->lMolfileNumber < 0 ) ip->lMolfileNumber = 0; - sd->fPtrEnd = (inp_file->f == stdin || prb_file->f == NULL)? -1 : ftell( inp_file->f ); - sd->ulStructTime += e_InchiTimeElapsed( &ulTStart ); - } else { - /* read the next original structure */ - int nStructReadError=0; - if ( !ip->bMergeAllInputStructures ) { - nRet2 = e_MolfileToInchi_Input( inp_file->f, NULL, 0, 0, 0, - NULL, NULL, NULL, NULL, NULL, &nStructReadError, NULL ); - if ( nRet2 <= 0 && 10 < nStructReadError && nStructReadError < 20 ) { - return _IS_EOF; - } - } else { - return _IS_EOF; - } - } - break; - case INPUT_INCHI_XML: - case INPUT_INCHI_PLAIN: - if ( pInp ) { - if ( ip->pSdfValue && ip->pSdfValue[0] ) { - /* Added 07-29-2003 to avoid inheriting exact value from prev. structure - and to make reference to a (bad) structure with unknown ID Value */ - char *p, *q; - int n; - if ( (p = strrchr( ip->pSdfValue, '+' )) && - '[' == *(p-1) && 0 < (n=strtol(p+1,&q,10)) && q[0] && ']'==q[0] && !q[1] ) { - sprintf( p+1, "%d]", n+1 ); - } else { - strcat( ip->pSdfValue, " [+1]" ); - } - } - e_InchiTimeGet( &ulTStart ); - sd->fPtrStart = (inp_file->f == stdin)? -1 : ftell( inp_file->f ); - /* read the original structure */ - nRet2 = e_INChIToInchi_Input( inp_file, pInp, ip->bMergeAllInputStructures, - ip->bDoNotAddH, vABParityUnknown, - ip->nInputType, ip->pSdfLabel, ip->pSdfValue, - &ip->lMolfileNumber,&InpAtomFlags, - &sd->nStructReadError, sd->pStrErrStruct ); - /*if ( !ip->bGetSdfileId || ip->lSdfId == 999999) ip->lSdfId = 0;*/ - sd->bChiralFlag |= InpAtomFlags; - sd->fPtrEnd = (inp_file->f == stdin)? -1 : ftell( inp_file->f ); - sd->ulStructTime += e_InchiTimeElapsed( &ulTStart ); - } else { - /* read the next original structure */ - int nStructReadError=0; - if ( !ip->bMergeAllInputStructures ) { - nRet2 = e_INChIToInchi_Input( inp_file, NULL, 0, 0, 0, - ip->nInputType, NULL, NULL, NULL, NULL, &nStructReadError, NULL ); - if ( nRet2 <= 0 && 10 < nStructReadError && nStructReadError < 20 ) { - return _IS_EOF; - } - } else { - return _IS_EOF; - } - } - break; - -#if( ADD_CMLPP == 1 ) - /* BILLY 8/6/04 */ - case INPUT_CMLFILE: - if ( pInp ) { - - e_InchiTimeGet( &ulTStart ); - /* - if ( inp_index >= 0 ) { - sd->fPtrStart = inp_index; - } else { - sd->fPtrStart = GetCmlStructIndex(); - } - */ - sd->fPtrStart = -1; /* disable "e_CopyMOLfile() for CML input files */ - sd->fPtrEnd = -1; - /* read the original structure */ - nRet = CmlfileToOrigAtom( inp_file, pInp, ip->bMergeAllInputStructures, - bGetOrigCoord, ip->bDoNotAddH, inp_index, out_index, - ip->pSdfLabel, ip->pSdfValue, &ip->lSdfId, - &sd->nStructReadError, sd->pStrErrStruct ); - - - sd->ulStructTime += e_InchiTimeElapsed( &ulTStart ); -#if( bRELEASE_VERSION == 0 ) - sd->bExtract |= pInp->bExtract; -#endif - } else { - /* read the next original structure */ - int nStructReadError=0; - if ( !ip->bMergeAllInputStructures ) { - nRet2 = CmlfileToOrigAtom( inp_file, NULL, 0, 0, 0, inp_index, out_index, - NULL, NULL, NULL, &nStructReadError, NULL ); - - if ( nRet2 <= 0 && 10 < nStructReadError && nStructReadError < 20 ) { - return _IS_EOF; - } - } else { - return _IS_EOF; - } - } - break; -#endif - - default: - nRet = _IS_FATAL; /* wrong file type */ - } - nRet2 = e_TreatReadTheStructureErrors( sd, ip, inp_file, log_file, output_file, prb_file, - pInp, &num_inp ); - if ( (!nRet || nRet == _IS_WARNING) && nRet2 ) - nRet = nRet2; - - return nRet; -} -/*****************************************************************************************************/ -int e_TreatReadTheStructureErrors( STRUCT_DATA *sd, INPUT_PARMS *ip, - INCHI_IOSTREAM *inp_file, - INCHI_IOSTREAM* log_file, INCHI_IOSTREAM* output_file, INCHI_IOSTREAM *prb_file, - inchi_Input *pInp, long *num_inp ) -{ - int nRet = _IS_OKAY; - /* End of file */ - if ( 10 < sd->nStructReadError && sd->nStructReadError < 20 ) { - nRet = _IS_EOF; - goto exit_function; /* end of file */ - } - /* Skipping the structures */ - if ( *num_inp < ip->first_struct_number ) { - if ( log_file->f != stderr ) { - inchi_fprintf( stderr, "\rSkipping structure #%ld.%s%s%s%s...\r", *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue)); - } - nRet = sd->nErrorType = _IS_SKIP; - goto exit_function; - } - - sd->nErrorType = e_GetInpStructErrorType( ip, sd->nStructReadError, sd->pStrErrStruct, pInp->num_atoms ); - - /* Fatal error */ - if ( sd->nErrorType == _IS_FATAL ) { - inchi_ios_eprint( log_file, "Fatal Error %d (aborted; %s) inp structure #%ld.%s%s%s%s\n", - sd->nStructReadError, sd->pStrErrStruct, *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); -#if( bRELEASE_VERSION == 1 || EXTR_FLAGS == 0 ) - if ( prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd && !ip->bSaveAllGoodStructsAsProblem ) { - e_CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, *num_inp); - } -#endif - /* goto exit_function; */ - } - /* Non-fatal errors: do not produce INChI */ - if ( sd->nErrorType == _IS_ERROR ) { /* 70 => too many atoms */ - inchi_ios_eprint( log_file, "Error %d (no INChI; %s) inp structure #%ld.%s%s%s%s\n", - sd->nStructReadError, sd->pStrErrStruct, *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); -#if( bRELEASE_VERSION == 1 || EXTR_FLAGS == 0 ) - if ( prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd && !ip->bSaveAllGoodStructsAsProblem) { - e_CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, *num_inp); - } -#endif - } - /* Warnings: try to produce INChI */ - if ( sd->nErrorType == _IS_WARNING ) { - inchi_ios_eprint( log_file, "Warning: (%s) inp structure #%ld.%s%s%s%s\n", - sd->pStrErrStruct, *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - } -exit_function: - if ( nRet <= _IS_OKAY && sd->nErrorType > 0 ) { - nRet = sd->nErrorType; - } - return nRet; -} -/**********************************************************************************************/ -int e_GetInpStructErrorType( INPUT_PARMS *ip, int err, char *pStrErrStruct, int num_inp_atoms ) -{ - if ( err && err == 9 ) - return _IS_ERROR; /* sdfile bypassed to $$$$ */ - if ( err && err < 30 ) - return _IS_FATAL; - if ( num_inp_atoms <= 0 || err ) { - if ( 98 == err && 0 == num_inp_atoms && ip->bAllowEmptyStructure ) - return _IS_OKAY /* _IS_WARNING*/; /* the warning will be issued by the dll */ - return _IS_ERROR; - } - if ( pStrErrStruct[0] ) - return _IS_WARNING; - return _IS_OKAY; -} +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include + +#include +#include +#include +#include +#include +#include + + +#include "e_mode.h" + +#include "e_ichisize.h" +#include "e_ichitime.h" + +#include "../../../../INCHI_BASE/src/inchi_api.h" + +#include "e_ctl_data.h" +#include "e_readstru.h" +#include "e_ichi_io.h" +#include "e_util.h" +#include "e_ichierr.h" +#include "e_inpdef.h" + +int e_TreatReadTheStructureErrors(STRUCT_DATA *sd, INPUT_PARMS *ip, + INCHI_IOSTREAM *inp_file, + INCHI_IOSTREAM* log_file, INCHI_IOSTREAM* output_file, INCHI_IOSTREAM* prb_file, + inchi_Input *pInp, long *num_inp ); +int e_GetInpStructErrorType( INPUT_PARMS *ip, int err, char *pStrErrStruct, int num_inp_atoms ); + +/*********************************************************************************************************/ +int e_ReadStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, + INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, + INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *prb_file, + inchi_Input *pInp, long num_inp, + /* for CML:*/ int inp_index, int *out_index ) +{ + e_inchiTime ulTStart; + int nRet = 0, nRet2 = 0; + INCHI_MODE InpAtomFlags = 0; + + /* vABParityUnknown holds actual value of an internal constant signifying */ + /* unknown parity: either the same as for undefined parity (default==standard) */ + /* or a specific one (non-std; requested by SLUUD switch). */ + int vABParityUnknown = AB_PARITY_UNDF; + if ( 0 != ( ip->nMode & REQ_MODE_DIFF_UU_STEREO) ) + { + /* Make labels for unknown and undefined stereo different */ + vABParityUnknown = AB_PARITY_UNKN; + } + + memset( sd, 0, sizeof(*sd) ); + switch ( ip->nInputType ) { + case INPUT_MOLFILE: + case INPUT_SDFILE: + if ( pInp ) { + if ( ip->pSdfValue && ip->pSdfValue[0] ) { + /* Added 07-29-2003 to avoid inheriting exact value from prev. structure + and to make reference to a (bad) structure with unknown ID Value */ + char *p, *q; /* q shadows prev declaration of const char *q */ + int n; + if ( (p = strrchr( ip->pSdfValue, '+' )) && + '[' == *(p-1) && 0 < (n=strtol(p+1,&q,10)) && q[0] && ']'==q[0] && !q[1] ) { + sprintf( p+1, "%d]", n+1 ); + } else { + strcat( ip->pSdfValue, " [+1]" ); + } + } + e_inchiTimeGet( &ulTStart ); + sd->fPtrStart = (inp_file->f == stdin || prb_file->f == NULL)? -1 : ftell( inp_file->f ); + /* read the original structure */ + nRet2 = e_MolfileToInchi_Input( inp_file->f, pInp, ip->bMergeAllInputStructures, + ip->bDoNotAddH, ip->bAllowEmptyStructure, + ip->pSdfLabel, ip->pSdfValue, &ip->lSdfId, &ip->lMolfileNumber, + &InpAtomFlags, &sd->nStructReadError, sd->pStrErrStruct ); + + sd->bChiralFlag |= InpAtomFlags; + if ( !ip->bGetSdfileId || ip->lSdfId == 999999) ip->lSdfId = 0; + if ( !ip->bGetMolfileNumber || ip->lMolfileNumber < 0 ) ip->lMolfileNumber = 0; + sd->fPtrEnd = (inp_file->f == stdin || prb_file->f == NULL)? -1 : ftell( inp_file->f ); + sd->ulStructTime += e_inchiTimeElapsed( &ulTStart ); + } else { + /* read the next original structure */ + int nStructReadError=0; + if ( !ip->bMergeAllInputStructures ) { + nRet2 = e_MolfileToInchi_Input( inp_file->f, NULL, 0, 0, 0, + NULL, NULL, NULL, NULL, NULL, &nStructReadError, NULL ); + if ( nRet2 <= 0 && 10 < nStructReadError && nStructReadError < 20 ) { + return _IS_EOF; + } + } else { + return _IS_EOF; + } + } + break; + case INPUT_INCHI_XML: + case INPUT_INCHI_PLAIN: + if ( pInp ) { + if ( ip->pSdfValue && ip->pSdfValue[0] ) { + /* Added 07-29-2003 to avoid inheriting exact value from prev. structure + and to make reference to a (bad) structure with unknown ID Value */ + char *p, *q; + int n; + if ( (p = strrchr( ip->pSdfValue, '+' )) && + '[' == *(p-1) && 0 < (n=strtol(p+1,&q,10)) && q[0] && ']'==q[0] && !q[1] ) { + sprintf( p+1, "%d]", n+1 ); + } else { + strcat( ip->pSdfValue, " [+1]" ); + } + } + e_inchiTimeGet( &ulTStart ); + sd->fPtrStart = (inp_file->f == stdin)? -1 : ftell( inp_file->f ); + /* read the original structure */ + nRet2 = e_INChIToInchi_Input( inp_file, pInp, ip->bMergeAllInputStructures, + ip->bDoNotAddH, vABParityUnknown, + ip->nInputType, ip->pSdfLabel, ip->pSdfValue, + &ip->lMolfileNumber,&InpAtomFlags, + &sd->nStructReadError, sd->pStrErrStruct ); + /*if ( !ip->bGetSdfileId || ip->lSdfId == 999999) ip->lSdfId = 0;*/ + sd->bChiralFlag |= InpAtomFlags; + sd->fPtrEnd = (inp_file->f == stdin)? -1 : ftell( inp_file->f ); + sd->ulStructTime += e_inchiTimeElapsed( &ulTStart ); + } else { + /* read the next original structure */ + int nStructReadError=0; + if ( !ip->bMergeAllInputStructures ) { + nRet2 = e_INChIToInchi_Input( inp_file, NULL, 0, 0, 0, + ip->nInputType, NULL, NULL, NULL, NULL, &nStructReadError, NULL ); + if ( nRet2 <= 0 && 10 < nStructReadError && nStructReadError < 20 ) { + return _IS_EOF; + } + } else { + return _IS_EOF; + } + } + break; + + default: + nRet = _IS_FATAL; /* wrong file type */ + } + nRet2 = e_TreatReadTheStructureErrors( sd, ip, inp_file, log_file, output_file, prb_file, + pInp, &num_inp ); + if ( (!nRet || nRet == _IS_WARNING) && nRet2 ) + nRet = nRet2; + + return nRet; +} +/*****************************************************************************************************/ +int e_TreatReadTheStructureErrors( STRUCT_DATA *sd, INPUT_PARMS *ip, + INCHI_IOSTREAM *inp_file, + INCHI_IOSTREAM* log_file, INCHI_IOSTREAM* output_file, INCHI_IOSTREAM *prb_file, + inchi_Input *pInp, long *num_inp ) +{ + int nRet = _IS_OKAY; + /* End of file */ + if ( 10 < sd->nStructReadError && sd->nStructReadError < 20 ) { + nRet = _IS_EOF; + goto exit_function; /* end of file */ + } + /* Skipping the structures */ + if ( *num_inp < ip->first_struct_number ) { + if ( log_file->f != stderr ) { + inchi_fprintf( stderr, "\rSkipping structure #%ld.%s%s%s%s...\r", *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue)); + } + nRet = sd->nErrorType = _IS_SKIP; + goto exit_function; + } + + sd->nErrorType = e_GetInpStructErrorType( ip, sd->nStructReadError, sd->pStrErrStruct, pInp->num_atoms ); + + /* Fatal error */ + if ( sd->nErrorType == _IS_FATAL ) { + inchi_ios_eprint( log_file, "Fatal Error %d (aborted; %s) inp structure #%ld.%s%s%s%s\n", + sd->nStructReadError, sd->pStrErrStruct, *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); +#if( bRELEASE_VERSION == 1 || EXTR_FLAGS == 0 ) + if ( prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd && !ip->bSaveAllGoodStructsAsProblem ) { + e_CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, *num_inp); + } +#endif + /* goto exit_function; */ + } + /* Non-fatal errors: do not produce INChI */ + if ( sd->nErrorType == _IS_ERROR ) { /* 70 => too many atoms */ + inchi_ios_eprint( log_file, "Error %d (no INChI; %s) inp structure #%ld.%s%s%s%s\n", + sd->nStructReadError, sd->pStrErrStruct, *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); +#if( bRELEASE_VERSION == 1 || EXTR_FLAGS == 0 ) + if ( prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd && !ip->bSaveAllGoodStructsAsProblem) { + e_CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, *num_inp); + } +#endif + } + /* Warnings: try to produce INChI */ + if ( sd->nErrorType == _IS_WARNING ) { + inchi_ios_eprint( log_file, "Warning: (%s) inp structure #%ld.%s%s%s%s\n", + sd->pStrErrStruct, *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); + } +exit_function: + if ( nRet <= _IS_OKAY && sd->nErrorType > 0 ) { + nRet = sd->nErrorType; + } + return nRet; +} +/**********************************************************************************************/ +int e_GetInpStructErrorType( INPUT_PARMS *ip, int err, char *pStrErrStruct, int num_inp_atoms ) +{ + if ( err && err == 9 ) + return _IS_ERROR; /* sdfile bypassed to $$$$ */ + if ( err && err < 30 ) + return _IS_FATAL; + if ( num_inp_atoms <= 0 || err ) { + if ( 98 == err && 0 == num_inp_atoms && ip->bAllowEmptyStructure ) + return _IS_OKAY /* _IS_WARNING*/; /* the warning will be issued by the dll */ + return _IS_ERROR; + } + if ( pStrErrStruct[0] ) + return _IS_WARNING; + return _IS_OKAY; +} diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/e_readstru.h b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_readstru.h similarity index 56% rename from INCHI-1-SRC/INCHI_API/inchi_main/e_readstru.h rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_readstru.h index f4e04c3..84df652 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_main/e_readstru.h +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_readstru.h @@ -1,66 +1,65 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __READSTRU_H__ -#define __READSTRU_H__ - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - - -int e_ReadStructure(STRUCT_DATA *sd, INPUT_PARMS *ip, - INCHI_IOSTREAM *inp_file, - INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *prb_file, - inchi_Input *pInp, long num_inp, /* for CML:*/ int inp_index, int *out_index ); - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /* __READSTRU_H__ */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef __READSTRU_H__ +#define __READSTRU_H__ + + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + + + +int e_ReadStructure(STRUCT_DATA *sd, INPUT_PARMS *ip, + INCHI_IOSTREAM *inp_file, + INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *prb_file, + inchi_Input *pInp, long num_inp, /* for CML:*/ int inp_index, int *out_index ); + + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + + +#endif /* __READSTRU_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_util.c b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_util.c new file mode 100644 index 0000000..9c3a6a7 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_util.c @@ -0,0 +1,661 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include +#include +#include +#include + +#include "e_mode.h" + +#include "../../../../INCHI_BASE/src/inchi_api.h" + +#include "e_ichitime.h" +#include "e_ichisize.h" +#include "e_comdef.h" +#include "e_util.h" +#include "e_ichicomp.h" + +#define extract_ChargeRadical e_extract_ChargeRadical +#define normalize_name e_normalize_name +/******************************************************************************************************/ +int extract_ChargeRadical( char *elname, int *pnRadical, int *pnCharge ) +{ + char *q, *r, *p; + int nCharge=0, nRad = 0, charge_len = 0, k, nVal, nSign, nLastSign=1, len; + + p = elname; + + /* extract radicals & charges */ + while ( q = strpbrk( p, "+-^" ) ) { + switch ( *q ) { + case '+': + case '-': + for ( k = 0, nVal=0; (nSign = ('+' == q[k])) || (nSign = -('-' == q[k])); k++ ) { + nVal += (nLastSign = nSign); + charge_len ++; + } + if ( nSign = (int)strtol( q+k, &r, 10 ) ) { /* fixed 12-5-2001 */ + nVal += nLastSign * (nSign-1); + } + charge_len = r - q; + nCharge += nVal; + break; + /* case '.': */ /* singlet '.' may be confused with '.' in formulas like CaO.H2O */ + case '^': + nRad = 1; /* doublet here is 1. See below */ + charge_len = 1; + for ( k = 1; q[0] == q[k]; k++ ) { + nRad ++; + charge_len ++; + } + break; + } + memmove( q, q+charge_len, strlen(q+charge_len)+1 ); + } + len = strlen(p); + /* radical */ + if ( (q = strrchr( p, ':' )) && !q[1]) { + nRad = RADICAL_SINGLET; + q[0] = '\0'; + len --; + } else { + while( (q = strrchr( p, '.' )) && !q[1] ) { + nRad ++; + q[0] = '\0'; + len --; + } + + nRad = nRad == 1? RADICAL_DOUBLET : + nRad == 2? RADICAL_TRIPLET : 0; + } + *pnRadical = nRad; + *pnCharge = nCharge; + return ( nRad || nCharge ); +} +/*****************************************************************/ +int normalize_name( char* name ) +{ + /* remove leading & trailing spaces; replace consecutive spaces with a single space */ + /* Treat non-printable characters (Greeks) as spaces. 11-23-99 DCh. */ + int i, len, n; + len = (int)strlen(name); + for ( i = 0, n = 0; i < len; i++ ) { + if ( isspace( UCINT name[i] ) /*|| !isprint( UCINT name[i] )*/ ) { + name[i] = ' '; /* exterminate tabs !!! */ + n++; + } else { + if ( n > 0 ) { + memmove( (void*) &name[i-n], (void*) &name[i], len-i+1 ); + i -= n; + len -= n; + } + n = -1; + } + } + if ( n == len ) /* empty line */ + name[len=0] = '\0'; + else + if ( ++n && n <= len ) { + len -= n; + name[len] = '\0'; + } + return len; +} +/************************************************/ +#ifndef e_inchi_malloc +void *e_inchi_malloc(size_t c) +{ + return malloc(c); +} +#endif +#ifndef e_inchi_calloc +void *e_inchi_calloc(size_t c, size_t n) +{ + return calloc(c,n); +} +#endif +#ifndef e_inchi_free +void e_inchi_free(void *p) +{ + if(p) { + free(p); /*added check if zero*/ + } +} +#endif + + +/***************************************************************************/ +/* Copies up to maxlen characters INCLUDING end null from source to target */ +/* Fills out the rest of the target with null bytes */ +int e_mystrncpy(char *target,const char *source,unsigned maxlen) +{ /* protected from non-zero-terminated source and overlapped target/source. 7-9-99 DCh. */ + const char *p; + unsigned len; + + if (target==NULL || maxlen == 0 || source == NULL) + return 0; + if ( p = (const char*)memchr(source, 0, maxlen) ) { + len = p-source; /* maxlen does not include the found zero termination */ + } else { + len = maxlen-1; /* reduced length does not include one more byte for zero termination */ + } + if ( len ) + memmove( target, source, len ); + /* target[len] = '\0'; */ + memset( target+len, 0, maxlen-len); /* zero termination */ + return 1; +} +/************************************************************************/ +/* Remove leading and trailing white spaces */ +char* e_LtrimRtrim( char *p, int* nLen ) +{ + int i, len=0; + if ( p && (len = strlen( p )) ) { + for ( i = 0; i < len && __isascii( p[i] ) && isspace( p[i] ); i++ ) + ; + if ( i ) + (memmove)( p, p+i, (len -= i)+1 ); + for ( ; 0 < len && __isascii( p[len-1] ) && isspace( p[len-1] ); len-- ) + ; + p[len] = '\0'; + } + if ( nLen ) + *nLen = len; + return p; +} + +/*************************************************************************/ +void e_remove_trailing_spaces( char* p ) +{ + int len; + for( len = (int)strlen( p ) - 1; len >= 0 && isspace( UCINT p[len] ); len-- ) + ; + p[++len] = '\0'; +} +/*************************************************************************/ +void e_remove_one_lf( char* p) +{ + size_t len; + if ( p && 0 < (len = strlen(p)) && p[len-1] == '\n' ){ + p[len-1] = '\0'; + if ( len >= 2 && p[len-2] == '\r' ) + p[len-2] = '\0'; + } +} +/*************************************************************************/ +S_SHORT *e_is_in_the_slist( S_SHORT *pathAtom, S_SHORT nNextAtom, int nPathLen ) +{ + for ( ; nPathLen && *pathAtom != nNextAtom; nPathLen--, pathAtom++ ) + ; + return nPathLen? pathAtom : NULL; +} +/************************************************/ +int e_is_element_a_metal( char szEl[] ) +{ + static char szMetals[] = "K;V;Y;W;U;" + "Li;Be;Na;Mg;Al;Ca;Sc;Ti;Cr;Mn;Fe;Co;Ni;Cu;Zn;Ga;Rb;Sr;Zr;" + "Nb;Mo;Tc;Ru;Rh;Pd;Ag;Cd;In;Sn;Sb;Cs;Ba;La;Ce;Pr;Nd;Pm;Sm;" + "Eu;Gd;Tb;Dy;Ho;Er;Tm;Yb;Lu;Hf;Ta;Re;Os;Ir;Pt;Au;Hg;Tl;Pb;" + "Bi;Po;Fr;Ra;Ac;Th;Pa;Np;Pu;Am;Cm;Bk;Cf;Es;Fm;Md;No;Lr;Rf;"; + int len = strlen(szEl); + char *p; + + if ( 0 < len && len <= 2 && + isalpha( UCINT szEl[0] ) && isupper( szEl[0] ) && + (p = strstr(szMetals, szEl) ) && p[len] == ';' ) { + + return 1; /*return AtType_Metal;*/ + } + return 0; +} + + + + + +#ifdef COMPILE_ANSI_ONLY + +static clock_t InchiClock(void); + +#ifdef INCHI_USETIMES +static clock_t InchiClock(void) +{ + struct tms buf; + clock_t c = times( &buf ); + if ( c != (clock_t)-1 ) { + return buf.tms_utime; + } + return 0; +} +#else +static clock_t InchiClock(void) +{ + clock_t c = clock(); + if ( c != (clock_t)-1 ) { + return c; + } + return 0; +} +#endif + +#define INCHI_MSEC(X) (long)((1000.0/(double)CLOCKS_PER_SEC)*(X)) +#define INCHI_CLOCK_T(X) (clock_t)( (double)(X) / 1000.0 * (double)CLOCKS_PER_SEC ) +const clock_t FullMaxClock = (clock_t)(-1); +const clock_t HalfMaxClock = (clock_t)(-1) / 2; +clock_t MaxPositiveClock = 0; +clock_t MinNegativeClock = 0; +clock_t HalfMaxPositiveClock = 0; +clock_t HalfMinNegativeClock = 0; + + + + + + + +static void FillMaxMinClock(void); /* keep compiler happy */ + +static void FillMaxMinClock(void) +{ /* assume clock_t is a signed integral value */ + if ( !MaxPositiveClock ) { + clock_t valPos=0, val1 = 1; + while ( 0 < ((val1 <<= 1), (val1 |= 1)) ) { + valPos = val1; + } + MaxPositiveClock = valPos; + MinNegativeClock = -valPos; + HalfMaxPositiveClock = MaxPositiveClock / 2; + HalfMinNegativeClock = MinNegativeClock / 2; + } +} + + +/******** returns difference TickEnd - TickStart in milliseconds **********/ +long InchiTimeMsecDiff( e_inchiTime *TickEnd, e_inchiTime *TickStart ) +{ + if ( FullMaxClock > 0 ) { + clock_t delta; + if ( !TickEnd || !TickStart ) + return 0; + /* clock_t is unsigned */ + if ( TickEnd->clockTime > TickStart->clockTime ) { + if ( TickEnd->clockTime > HalfMaxClock && + TickEnd->clockTime - TickStart->clockTime > HalfMaxClock ) { + /* overflow in TickStart->clockTime, actually TickStart->clockTime was later */ + delta = (FullMaxClock - TickEnd->clockTime) + TickStart->clockTime; + return -INCHI_MSEC(delta); + } + delta = TickEnd->clockTime - TickStart->clockTime; + return INCHI_MSEC(delta); + } else + if ( TickEnd->clockTime < TickStart->clockTime ) { + if ( TickStart->clockTime > HalfMaxClock && + TickStart->clockTime - TickEnd->clockTime > HalfMaxClock ) { + /* overflow in TickEnd->clockTime, actually TickEnd->clockTime was later */ + delta = (FullMaxClock - TickStart->clockTime) + TickEnd->clockTime; + return INCHI_MSEC(delta); + } + delta = TickStart->clockTime - TickEnd->clockTime; + return -INCHI_MSEC(delta); + } + return 0; /* TickEnd->clockTime == TickStart->clockTime */ + } else { + /* may happen under Win32 only where clock_t is SIGNED long */ + clock_t delta; + FillMaxMinClock( ); + if ( !TickEnd || !TickStart ) + return 0; + if ( TickEnd->clockTime >= 0 && TickStart->clockTime >= 0 || + TickEnd->clockTime <= 0 && TickStart->clockTime <= 0) { + delta = TickEnd->clockTime - TickStart->clockTime; + } else + if ( TickEnd->clockTime >= HalfMaxPositiveClock && + TickStart->clockTime <= HalfMinNegativeClock ) { + /* end is earlier than start */ + delta = (MaxPositiveClock - TickEnd->clockTime) + (TickStart->clockTime - MinNegativeClock); + delta = -delta; + } else + if ( TickEnd->clockTime <= HalfMinNegativeClock && + TickStart->clockTime >= HalfMaxPositiveClock ) { + /* start was earlier than end */ + delta = (MaxPositiveClock - TickStart->clockTime) + (TickEnd->clockTime - MinNegativeClock); + } else { + /* there was no overflow, clock passed zero */ + delta = TickEnd->clockTime - TickStart->clockTime; + } + return INCHI_MSEC(delta); + } +} +/******************* get elapsed time from TickStart ************************/ +long InchiTimeElapsed( e_inchiTime *TickStart ) +{ + e_inchiTime TickEnd; + if ( !TickStart ) + return 0; + e_inchiTimeGet( &TickEnd ); + return InchiTimeMsecDiff( &TickEnd, TickStart ); +} +/******************* add number of milliseconds to time *********************/ +void InchiTimeAddMsec( e_inchiTime *TickEnd, unsigned long nNumMsec ) +{ + clock_t delta; + if ( !TickEnd ) + return; + if ( FullMaxClock > 0 ) { + /* clock_t is unsigned */ + delta = INCHI_CLOCK_T(nNumMsec); + TickEnd->clockTime += delta; + } else { + /* may happen under Win32 only where clock_t is SIGNED long */ + /* clock_t is unsigned */ + FillMaxMinClock( ); + delta = INCHI_CLOCK_T(nNumMsec); + TickEnd->clockTime += delta; + } +} +/******************* check whether time has expired *********************/ +int bInchiTimeIsOver( e_inchiTime *TickStart ) +{ + if ( FullMaxClock > 0 ) { + clock_t clockCurrTime; + if ( !TickStart ) + return 0; + clockCurrTime = InchiClock(); + /* clock_t is unsigned */ + if ( TickStart->clockTime > clockCurrTime ) { + if ( TickStart->clockTime > HalfMaxClock && + TickStart->clockTime - clockCurrTime > HalfMaxClock ) { + /* overflow in clockCurrTime, actually clockCurrTime was later */ + return 1; + } + return 0; + } else + if ( TickStart->clockTime < clockCurrTime ) { + if ( clockCurrTime > HalfMaxClock && + clockCurrTime - TickStart->clockTime > HalfMaxClock ) { + /* overflow in TickStart->clockTime, actually TickStart->clockTime was later */ + return 0; + } + return 1; + } + return 0; /* TickStart->clockTime == clockCurrTime */ + } else { + /* may happen under Win32 only where clock_t is SIGNED long */ + clock_t clockCurrTime; + FillMaxMinClock( ); + if ( !TickStart ) + return 0; + clockCurrTime = InchiClock(); + if ( clockCurrTime >= 0 && TickStart->clockTime >= 0 || + clockCurrTime <= 0 && TickStart->clockTime <= 0) { + return (clockCurrTime > TickStart->clockTime); + } else + if ( clockCurrTime >= HalfMaxPositiveClock && + TickStart->clockTime <= HalfMinNegativeClock ) { + /* curr is earlier than start */ + return 0; + } else + if ( clockCurrTime <= HalfMinNegativeClock && + TickStart->clockTime >= HalfMaxPositiveClock ) { + /* start was earlier than curr */ + return 1; + } else { + /* there was no overflow, clock passed zero */ + return (clockCurrTime > TickStart->clockTime); + } + } +} + +#else + +/******** get current process time ****************************************/ +void e_inchiTimeGet( e_inchiTime *TickEnd ) +{ + if ( TickEnd ) { + struct _timeb timeb; + _ftime( &timeb ); + TickEnd->clockTime = (unsigned long)timeb.time; + TickEnd->millitime = (long)timeb.millitm; + } +} +/******** returns difference TickEnd - TickStart in milliseconds **********/ +long InchiTimeMsecDiff( e_inchiTime *TickEnd, e_inchiTime *TickStart ) +{ + long delta; + if ( !TickEnd || !TickStart ) { + return 0; + } + if ( TickEnd->clockTime >= TickStart->clockTime ) { + delta = (long)(TickEnd->clockTime - TickStart->clockTime); + delta *= 1000; + delta += TickEnd->millitime - TickStart->millitime; + } else { + delta = -(long)(TickStart->clockTime - TickEnd->clockTime); + delta *= 1000; + delta += TickEnd->millitime - TickStart->millitime; + } + return delta; +} +/******************* get elapsed time from TickStart ************************/ +long InchiTimeElapsed( e_inchiTime *TickStart ) +{ + e_inchiTime TickEnd; + if ( !TickStart ) + return 0; + e_inchiTimeGet( &TickEnd ); + return InchiTimeMsecDiff( &TickEnd, TickStart ); +} +/******************* add number of milliseconds to time *********************/ +void InchiTimeAddMsec( e_inchiTime *TickEnd, unsigned long nNumMsec ) +{ + long delta; + if ( !TickEnd ) + return; + TickEnd->clockTime += nNumMsec / 1000; + delta = nNumMsec % 1000 + TickEnd->millitime; + TickEnd->clockTime += delta / 1000; + TickEnd->millitime = delta % 1000; +} +/******************* check whether time has expired *********************/ +int bInchiTimeIsOver( e_inchiTime *TickEnd ) +{ + struct _timeb timeb; + if ( !TickEnd ) + return 0; + _ftime( &timeb ); + if ( TickEnd->clockTime > (unsigned long)timeb.time ) + return 0; + if ( TickEnd->clockTime < (unsigned long)timeb.time || + TickEnd->millitime < (long)timeb.millitm ) { + return 1; + } + return 0; +} +#endif + + + + +/******** returns difference TickEnd - TickStart in milliseconds **********/ +long e_inchiTimeMsecDiff( e_inchiTime *TickEnd, e_inchiTime *TickStart ) +{ + if ( FullMaxClock > 0 ) { + clock_t delta; + if ( !TickEnd || !TickStart ) + return 0; + /* clock_t is unsigned */ + if ( TickEnd->clockTime > TickStart->clockTime ) { + if ( TickEnd->clockTime > HalfMaxClock && + TickEnd->clockTime - TickStart->clockTime > HalfMaxClock ) { + /* overflow in TickStart->clockTime, actually TickStart->clockTime was later */ + delta = (FullMaxClock - TickEnd->clockTime) + TickStart->clockTime; + return -INCHI_MSEC(delta); + } + delta = TickEnd->clockTime - TickStart->clockTime; + return INCHI_MSEC(delta); + } else + if ( TickEnd->clockTime < TickStart->clockTime ) { + if ( TickStart->clockTime > HalfMaxClock && + TickStart->clockTime - TickEnd->clockTime > HalfMaxClock ) { + /* overflow in TickEnd->clockTime, actually TickEnd->clockTime was later */ + delta = (FullMaxClock - TickStart->clockTime) + TickEnd->clockTime; + return INCHI_MSEC(delta); + } + delta = TickStart->clockTime - TickEnd->clockTime; + return -INCHI_MSEC(delta); + } + return 0; /* TickEnd->clockTime == TickStart->clockTime */ + } else { + /* may happen under Win32 only where clock_t is SIGNED long */ + clock_t delta; + FillMaxMinClock( ); + if ( !TickEnd || !TickStart ) + return 0; + if ( TickEnd->clockTime >= 0 && TickStart->clockTime >= 0 || + TickEnd->clockTime <= 0 && TickStart->clockTime <= 0) { + delta = TickEnd->clockTime - TickStart->clockTime; + } else + if ( TickEnd->clockTime >= HalfMaxPositiveClock && + TickStart->clockTime <= HalfMinNegativeClock ) { + /* end is earlier than start */ + delta = (MaxPositiveClock - TickEnd->clockTime) + (TickStart->clockTime - MinNegativeClock); + delta = -delta; + } else + if ( TickEnd->clockTime <= HalfMinNegativeClock && + TickStart->clockTime >= HalfMaxPositiveClock ) { + /* start was earlier than end */ + delta = (MaxPositiveClock - TickStart->clockTime) + (TickEnd->clockTime - MinNegativeClock); + } else { + /* there was no overflow, clock passed zero */ + delta = TickEnd->clockTime - TickStart->clockTime; + } + return INCHI_MSEC(delta); + } +} +/******************* get elapsed time from TickStart ************************/ +long e_inchiTimeElapsed( e_inchiTime *TickStart ) +{ + e_inchiTime TickEnd; + if ( !TickStart ) + return 0; + e_inchiTimeGet( &TickEnd ); + return e_inchiTimeMsecDiff( &TickEnd, TickStart ); +} +/******************* add number of milliseconds to time *********************/ +void e_inchiTimeAddMsec( e_inchiTime *TickEnd, unsigned long nNumMsec ) +{ + clock_t delta; + if ( !TickEnd ) + return; + if ( FullMaxClock > 0 ) { + /* clock_t is unsigned */ + delta = INCHI_CLOCK_T(nNumMsec); + TickEnd->clockTime += delta; + } else { + /* may happen under Win32 only where clock_t is SIGNED long */ + /* clock_t is unsigned */ + FillMaxMinClock( ); + delta = INCHI_CLOCK_T(nNumMsec); + TickEnd->clockTime += delta; + } +} + + +/******** get current process time ****************************************/ +void e_inchiTimeGet( e_inchiTime *TickEnd ) +{ + TickEnd->clockTime = InchiClock(); +} + + + +/* + For compatibility: local implementation of non-ANSI (MS-specific) functions, prefixed with "inchi_" +*/ +#define __MYTOLOWER(c) ( ((c) >= 'A') && ((c) <= 'Z') ? ((c) - 'A' + 'a') : (c) ) +int e_inchi_memicmp( const void * p1, const void * p2, size_t length ) +{ + const U_CHAR *s1 = (const U_CHAR*)p1; + const U_CHAR *s2 = (const U_CHAR*)p2; + while ( length-- ) + { + if ( *s1 == *s2 || + __MYTOLOWER( (int)*s1 ) == __MYTOLOWER( (int)*s2 )) + { + s1 ++; + s2 ++; + } + else + { + return + __MYTOLOWER( (int)*s1 ) - __MYTOLOWER( (int)*s2 ); + } + } + + return 0; +} +int e_inchi_stricmp( const char *s1, const char *s2 ) +{ + while ( *s1 ) + { + if ( *s1 == *s2 || + __MYTOLOWER( (int)*s1 ) == __MYTOLOWER( (int)*s2 )) + { + s1 ++; + s2 ++; + } + else + { + return + __MYTOLOWER( (int)*s1 ) - __MYTOLOWER( (int)*s2 ); + } + } + + if ( *s2 ) + return -1; + + return 0; +} + +#undef __MYTOLOWER +/* + End of local implementation of non-ANSI (MS-specific) functions +*/ diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/e_util.h b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_util.h similarity index 72% rename from INCHI-1-SRC/INCHI_API/inchi_main/e_util.h rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_util.h index 727c579..1b41f13 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_main/e_util.h +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/src/e_util.h @@ -1,92 +1,94 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __UTIL_H__ -#define __UTIL_H__ - -/* BILLY 8/6/04 */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -int e_extract_ChargeRadical( char *elname, int *pnRadical, int *pnCharge ); -int e_normalize_name( char* name ); - -int e_mystrncpy(char *target,const char *source,unsigned maxlen); -char *e_LtrimRtrim( char *p, int* nLen ); -void e_remove_trailing_spaces( char* p ); -void e_remove_one_lf( char* p); -int e_is_element_a_metal( char szEl[] ); - -AT_NUMB *e_is_in_the_list( AT_NUMB *pathAtom, AT_NUMB nNextAtom, int nPathLen ); -S_SHORT *e_is_in_the_slist( S_SHORT *pathAtom, S_SHORT nNextAtom, int nPathLen ); -void e_SplitTime( unsigned long ulTotalTime, int *hours, int *minutes, int *seconds, int *mseconds ); - -/* allocator */ -#ifndef e_inchi_malloc -void *e_inchi_malloc(size_t c); -#endif -#ifndef e_inchi_calloc -void *e_inchi_calloc(size_t c, size_t n); -#endif -#ifndef e_inchi_free -void e_inchi_free(void *p); -#endif - - -extern char e_gsMissing[]; -extern char e_gsEmpty[]; -extern char e_gsSpace[]; -extern char e_gsEqual[]; -/* format string for SDF_LBL_VAL(L,V): %s%s%s%s (four strings) */ -#define SDF_LBL_VAL(L,V) ((L)&&(L)[0])?e_gsSpace:e_gsEmpty, ((L)&&(L)[0])?L:e_gsEmpty, ((L)&&(L)[0])? (((V)&&(V)[0])?e_gsEqual:e_gsSpace):e_gsEmpty, ((V)&&(V)[0])?V:((L)&&(L)[0])?e_gsMissing:e_gsEmpty - - -/* BILLY 8/6/04 */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -#endif /* __UTIL_H__*/ - +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef __UTIL_H__ +#define __UTIL_H__ + +/* BILLY 8/6/04 */ +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + +int e_extract_ChargeRadical( char *elname, int *pnRadical, int *pnCharge ); +int e_normalize_name( char* name ); + +int e_mystrncpy(char *target,const char *source,unsigned maxlen); +char *e_LtrimRtrim( char *p, int* nLen ); +void e_remove_trailing_spaces( char* p ); +void e_remove_one_lf( char* p); +int e_is_element_a_metal( char szEl[] ); + +AT_NUMB *e_is_in_the_list( AT_NUMB *pathAtom, AT_NUMB nNextAtom, int nPathLen ); +S_SHORT *e_is_in_the_slist( S_SHORT *pathAtom, S_SHORT nNextAtom, int nPathLen ); +void e_SplitTime( unsigned long ulTotalTime, int *hours, int *minutes, int *seconds, int *mseconds ); + +int e_inchi_memicmp( const void * p1, const void * p2, size_t length ); +int e_inchi_stricmp( const char *s1, const char *s2 ); + +/* allocator */ +#ifndef e_inchi_malloc +void *e_inchi_malloc(size_t c); +#endif +#ifndef e_inchi_calloc +void *e_inchi_calloc(size_t c, size_t n); +#endif +#ifndef e_inchi_free +void e_inchi_free(void *p); +#endif + + +extern char e_gsMissing[]; +extern char e_gsEmpty[]; +extern char e_gsSpace[]; +extern char e_gsEqual[]; +/* format string for SDF_LBL_VAL(L,V): %s%s%s%s (four strings) */ +#define SDF_LBL_VAL(L,V) ((L)&&(L)[0])?e_gsSpace:e_gsEmpty, ((L)&&(L)[0])?L:e_gsEmpty, ((L)&&(L)[0])? (((V)&&(V)[0])?e_gsEqual:e_gsSpace):e_gsEmpty, ((V)&&(V)[0])?V:((L)&&(L)[0])?e_gsMissing:e_gsEmpty + + + +/* BILLY 8/6/04 */ +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + +#endif /* __UTIL_H__*/ diff --git a/INCHI-1-SRC/INCHI_API/demos/inchi_main/vc9/inchi_main.aps b/INCHI-1-SRC/INCHI_API/demos/inchi_main/vc9/inchi_main.aps new file mode 100644 index 0000000..47774a3 Binary files /dev/null and b/INCHI-1-SRC/INCHI_API/demos/inchi_main/vc9/inchi_main.aps differ diff --git a/INCHI-1-SRC/INCHI_API/vc9/inchi_main/INCHI_MAIN.rc b/INCHI-1-SRC/INCHI_API/demos/inchi_main/vc9/inchi_main.rc similarity index 63% rename from INCHI-1-SRC/INCHI_API/vc9/inchi_main/INCHI_MAIN.rc rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/vc9/inchi_main.rc index 182bce6..16b2bef 100644 --- a/INCHI-1-SRC/INCHI_API/vc9/inchi_main/INCHI_MAIN.rc +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/vc9/inchi_main.rc @@ -1,109 +1,102 @@ -//Microsoft Developer Studio generated resource script. -// -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "afxres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (U.S.) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -#ifdef _WIN32 -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) -#endif //_WIN32 - -#ifndef _MAC -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,0,2,0 - PRODUCTVERSION 1,0,2,0 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x4L - FILETYPE 0x1L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "Comments", "IUPAC International Chemical Identifier (standard InChI) Library Call Example version 1 Software version 1.02 Build 01/10/2009\0" - VALUE "CompanyName", "\0" - VALUE "FileDescription", "InChI version 1 (1.02) Build 01/10/2009\0" - VALUE "FileVersion", "1.02\0" - VALUE "InternalName", "stdinchi_main.exe\0" - VALUE "LegalCopyright", "\0" - VALUE "LegalTrademarks", "\0" - VALUE "OriginalFilename", "stdinchi_main.exe\0" - VALUE "PrivateBuild", "\0" - VALUE "ProductName", "IUPAC International Chemical Identifier (standard InChI) Library Call Example version 1 Software version 1.02 Build 01/10/2009\0" - VALUE "ProductVersion", "1.0\0" - VALUE "SpecialBuild", "\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END - -#endif // !_MAC - - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE DISCARDABLE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE DISCARDABLE -BEGIN - "#include ""afxres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE DISCARDABLE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - -#endif // English (U.S.) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,5,0 + PRODUCTVERSION 1,0,5,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x21L +#else + FILEFLAGS 0x20L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "IUPAC International Chemical Identifier (InChI) version 1, Software version 1.05, Winter 2017. Demo program inchi_main." + VALUE "FileDescription", " Demo program inchi_main." + VALUE "FileVersion", "1.05" + VALUE "InternalName", "inchi_main.exe" + VALUE "OriginalFilename", "inchi_main.exe" + VALUE "ProductName", "IUPAC International Chemical Identifier (InChI) version 1, Software version 1.05,Winter 2017. Demo program inchi_main." + VALUE "ProductVersion", "1, 0, 5, 0" + VALUE "SpecialBuild", "RELEASE" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/INCHI-1-SRC/INCHI_API/vc9/inchi_main/inchi_main.sln b/INCHI-1-SRC/INCHI_API/demos/inchi_main/vc9/inchi_main.sln similarity index 91% rename from INCHI-1-SRC/INCHI_API/vc9/inchi_main/inchi_main.sln rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/vc9/inchi_main.sln index 68f661a..e1fd043 100644 --- a/INCHI-1-SRC/INCHI_API/vc9/inchi_main/inchi_main.sln +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/vc9/inchi_main.sln @@ -6,7 +6,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "inchi_main", "inchi_main.vc {5DAB1696-1795-49AA-BDE6-1771FBE25445} = {5DAB1696-1795-49AA-BDE6-1771FBE25445} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "inchi_dll", "..\inchi_dll\inchi_dll.vcproj", "{5DAB1696-1795-49AA-BDE6-1771FBE25445}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libinchi", "..\..\..\libinchi\vc9\libinchi.vcproj", "{5DAB1696-1795-49AA-BDE6-1771FBE25445}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/INCHI-1-SRC/INCHI_API/vc9/inchi_main/inchi_main.vcproj b/INCHI-1-SRC/INCHI_API/demos/inchi_main/vc9/inchi_main.vcproj similarity index 67% rename from INCHI-1-SRC/INCHI_API/vc9/inchi_main/inchi_main.vcproj rename to INCHI-1-SRC/INCHI_API/demos/inchi_main/vc9/inchi_main.vcproj index 38f4420..c27b186 100644 --- a/INCHI-1-SRC/INCHI_API/vc9/inchi_main/inchi_main.vcproj +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/vc9/inchi_main.vcproj @@ -21,8 +21,8 @@ @@ -44,13 +44,14 @@ @@ -117,7 +119,7 @@ @@ -375,79 +380,79 @@ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" > @@ -457,7 +462,7 @@ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" > diff --git a/INCHI-1-SRC/INCHI_API/demos/inchi_main/vc9/resource.h b/INCHI-1-SRC/INCHI_API/demos/inchi_main/vc9/resource.h new file mode 100644 index 0000000..a2b08ce --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/inchi_main/vc9/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by inchi_main.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/INCHI-1-SRC/INCHI_API/demos/mol2inchi/gcc/libinchi.map b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/gcc/libinchi.map new file mode 100644 index 0000000..323dc85 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/gcc/libinchi.map @@ -0,0 +1,4 @@ +{ +global: CheckINCHI; CheckINCHIKey; FreeINCHI; FreeStdINCHI; FreeStructFromINCHI; FreeStructFromStdINCHI; Free_inchi_Input; Free_std_inchi_Input; FreeStructFromINCHIEx; GetINCHI; GetINCHIKeyFromINCHI; GetINCHIfromINCHI; GetStdINCHI; GetStdINCHIKeyFromStdINCHI; GetStringLength; GetStructFromINCHI; GetStructFromStdINCHI; Get_inchi_Input_FromAuxInfo; Get_std_inchi_Input_FromAuxInfo; GetINCHIEx; GetStructFromINCHIEx; INCHIGEN_Create; INCHIGEN_Destroy; INCHIGEN_DoCanonicalization; INCHIGEN_DoNormalization; INCHIGEN_DoSerialization; INCHIGEN_Reset; INCHIGEN_Setup; STDINCHIGEN_Create; STDINCHIGEN_Destroy; STDINCHIGEN_DoCanonicalization; STDINCHIGEN_DoNormalization; STDINCHIGEN_DoSerialization; STDINCHIGEN_Reset; STDINCHIGEN_Setup; MakeINCHIFromMolfileText; IXA_STATUS_Create; IXA_STATUS_Clear; IXA_STATUS_Destroy; IXA_STATUS_HasError; IXA_STATUS_HasWarning; IXA_STATUS_GetCount; IXA_STATUS_GetSeverity; IXA_STATUS_GetMessage; IXA_MOL_Create; IXA_MOL_Clear; IXA_MOL_Destroy; IXA_MOL_ReadMolfile; IXA_MOL_ReadInChI; IXA_MOL_SetChiral; IXA_MOL_GetChiral; IXA_MOL_CreateAtom; IXA_MOL_SetAtomElement; IXA_MOL_SetAtomAtomicNumber; IXA_MOL_SetAtomMass; IXA_MOL_SetAtomCharge; IXA_MOL_SetAtomRadical; IXA_MOL_SetAtomHydrogens; IXA_MOL_SetAtomX; IXA_MOL_SetAtomY; IXA_MOL_SetAtomZ; IXA_MOL_CreateBond; IXA_MOL_SetBondType; IXA_MOL_SetBondWedge; IXA_MOL_SetDblBondConfig; IXA_MOL_CreateStereoTetrahedron; IXA_MOL_CreateStereoRectangle; IXA_MOL_CreateStereoAntiRectangle; IXA_MOL_SetStereoParity; IXA_MOL_GetNumAtoms; IXA_MOL_GetNumBonds; IXA_MOL_GetAtomId; IXA_MOL_GetBondId; IXA_MOL_GetAtomIndex; IXA_MOL_GetBondIndex; IXA_MOL_GetAtomNumBonds; IXA_MOL_GetAtomBond; IXA_MOL_GetCommonBond; IXA_MOL_GetBondAtom1; IXA_MOL_GetBondAtom2; IXA_MOL_GetAtomElement; IXA_MOL_GetAtomAtomicNumber; IXA_MOL_GetAtomMass; IXA_MOL_GetAtomCharge; IXA_MOL_GetAtomRadical; IXA_MOL_GetAtomHydrogens; IXA_MOL_GetAtomX; IXA_MOL_GetAtomY; IXA_MOL_GetAtomZ; IXA_MOL_GetBondType; IXA_MOL_GetBondWedge; IXA_MOL_GetDblBondConfig; IXA_MOL_GetNumStereos; IXA_MOL_GetStereoId; IXA_MOL_GetStereoIndex; IXA_MOL_GetStereoTopology; IXA_MOL_GetStereoCentralAtom; IXA_MOL_GetStereoCentralBond; IXA_MOL_GetStereoNumVertices; IXA_MOL_GetStereoVertex; IXA_MOL_GetStereoParity; IXA_INCHIBUILDER_Create; IXA_INCHIBUILDER_SetMolecule; IXA_INCHIBUILDER_GetInChI; IXA_INCHIBUILDER_GetInChIEx; IXA_INCHIBUILDER_GetAuxInfo; IXA_INCHIBUILDER_GetLog; IXA_INCHIBUILDER_Destroy; IXA_INCHIBUILDER_SetOption; IXA_INCHIBUILDER_SetOption_Stereo; IXA_INCHIBUILDER_SetOption_Timeout; IXA_INCHIKEYBUILDER_Create; IXA_INCHIKEYBUILDER_SetInChI; IXA_INCHIKEYBUILDER_GetInChIKey; IXA_INCHIKEYBUILDER_Destroy;local: *; +}; + diff --git a/INCHI-1-SRC/INCHI_API/demos/mol2inchi/gcc/makefile b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/gcc/makefile new file mode 100644 index 0000000..e59a040 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/gcc/makefile @@ -0,0 +1,194 @@ +# Comment out the next line to create so/dll only +CREATE_MAIN = 1 +# Comment out the next line to create mol2inchi executable (otherwise, inchi_maiin is created) +CALLER_IS_MOL2INCHI = 1 +# or define ISLINUX in command line: make ISLINUX=1 +ISLINUX = 1 +# Linux fpic option: replace -fPIC with -fpic if the latter works +# Comment out "LINUX_Z_RELRO =" if -z relro is not supported +# These options are needed to avoid the following SELinux message: +# "Error: cannot restore segment prot after reloc: Permission denied" +# In addition, inchi.map restricts set of expoorted from .so +# functions to those which belong to InChI API +LINUX_MAP = ,--version-script=libinchi.map +ifdef ISLINUX +LINUX_FPIC = -fPIC +LINUX_Z_RELRO = ,-z,relro +endif +# === version === +MAIN_VERSION = .1 +VERSION = $(MAIN_VERSION).05.00 +# === executable & library directory === +ifndef LIB_DIR + LIB_DIR = ../../../bin/Linux +endif +# === InChI Library name === +ifndef INCHI_LIB_NAME + INCHI_LIB_NAME = libinchi +endif +INCHI_LIB_PATHNAME = $(LIB_DIR)/$(INCHI_LIB_NAME) +# === Main program name ==== +ifndef API_CALLER_NAME + ifndef CALLER_IS_MOL2INCHI + API_CALLER_NAME = inchi_main$ + else + API_CALLER_NAME = mol2inchi$ + endif +endif +API_CALLER_PATHNAME = $(LIB_DIR)/$(API_CALLER_NAME) +# === Linker to create (Shared) InChI library ==== +ifndef SHARED_LINK + SHARED_LINK = gcc -shared +endif +# === Linker to create Main program ===== +ifndef LINKER + ifdef ISLINUX + LINKER_CWD_PATH = -Wl,-R,"" + endif + LINKER = gcc -s $(LINKER_CWD_PATH) +endif +ifndef P_LIBR + P_LIBR = ../../../libinchi/src/ +endif +ifndef P_LIBR_IXA + P_LIBR_IXA = ../../../libinchi/src/ixa/ +endif +ifndef P_BASE + P_BASE = ../../../../INCHI_BASE/src/ +endif +ifndef P_MAIN + ifndef CALLER_IS_MOL2INCHI + P_MAIN = ../src/ + else + P_MAIN = ../src/ + endif +endif +# === C Compiler =============== +ifndef C_COMPILER + C_COMPILER = gcc +endif +# === C Compiler Options ======= +ifndef C_OPTIONS + ifndef CALLER_IS_MOL2INCHI + C_OPTIONS = -ansi -O3 -c + else + C_OPTIONS = -O3 -c + endif + ifdef ISLINUX + ifndef C_SO_OPTIONS + C_SO_OPTIONS = $(LINUX_FPIC) -DTARGET_API_LIB -DCOMPILE_ANSI_ONLY + endif + endif + ifndef C_MAIN_OPTIONS + C_MAIN_OPTIONS = -DBUILD_LINK_AS_DLL -DTARGET_EXE_USING_API + endif +endif +ifdef CREATE_MAIN +ifndef CALLER_IS_MOL2INCHI +API_CALLER_SRCS = $(P_MAIN)e_0dstereo.c \ +$(P_MAIN)e_ichimain.c \ +$(P_MAIN)e_ichi_io.c \ +$(P_MAIN)e_ichi_parms.c \ +$(P_MAIN)e_inchi_atom.c \ +$(P_MAIN)e_mol2atom.c \ +$(P_MAIN)e_readinch.c \ +$(P_MAIN)e_readmol.c \ +$(P_MAIN)e_readstru.c \ +$(P_MAIN)e_util.c \ +$(P_MAIN)e_ichimain_a.c +API_CALLER_OBJS = e_0dstereo.o \ +e_ichimain.o \ +e_ichi_io.o \ +e_ichi_parms.o \ +e_inchi_atom.o \ +e_mol2atom.o \ +e_readinch.o \ +e_readmol.o \ +e_readstru.o \ +e_util.o \ +e_ichimain_a.o +else +API_CALLER_SRCS = $(P_MAIN)mol2inchi.c \ +$(P_MAIN)moreutil.c +API_CALLER_OBJS = mol2inchi.o \ +moreutil.o +endif +# === InChI Main Link rule ================ +$(API_CALLER_PATHNAME) : $(API_CALLER_OBJS) $(INCHI_LIB_PATHNAME).so$(VERSION) + $(LINKER) -o $(API_CALLER_PATHNAME) $(API_CALLER_OBJS) \ + $(INCHI_LIB_PATHNAME).so$(VERSION) -lm +# === InChI Main compile rule ============ +%.o: $(P_MAIN)%.c + $(C_COMPILER) $(C_MAIN_OPTIONS) $(C_OPTIONS) $< +endif +# === InChI Library Object files ============ +INCHI_LIB_OBJS = ichican2.o \ +ichicano.o \ +ichi_io.o \ +ichierr.o \ +ichicans.o \ +ichiisot.o \ +ichilnct.o \ +ichimak2.o \ +ichimake.o \ +ichimap1.o \ +ichimap2.o \ +ichimap4.o \ +ichinorm.o \ +ichiparm.o \ +ichiprt1.o \ +ichiprt2.o \ +ichiprt3.o \ +ichiqueu.o \ +ichiring.o \ +ichisort.o \ +ichister.o \ +ichitaut.o \ +ichi_bns.o \ +inchi_dll.o \ +ichiread.o \ +ichirvr1.o \ +ichirvr2.o \ +ichirvr3.o \ +ichirvr4.o \ +ichirvr5.o \ +ichirvr6.o \ +ichirvr7.o \ +ikey_dll.o \ +ikey_base26.o \ +inchi_dll_main.o \ +inchi_dll_a.o \ +inchi_dll_a2.o \ +inchi_dll_b.o \ +ixa_inchikey_builder.o \ +ixa_read_mol.o \ +ixa_status.o \ +ixa_builder.o \ +ixa_mol.o \ +ixa_read_inchi.o \ +mol_fmt1.o \ +mol_fmt2.o \ +mol_fmt3.o \ +mol2atom.o \ +mol_fmt4.o \ +readinch.o \ +runichi.o \ +runichi2.o \ +runichi3.o \ +runichi4.o \ +sha2.o \ +strutil.o \ +util.o +# === InChI Library link rule ========= +$(INCHI_LIB_PATHNAME).so$(VERSION): $(INCHI_LIB_OBJS) + $(SHARED_LINK) $(SHARED_LINK_PARM) -o $(INCHI_LIB_PATHNAME).so$(VERSION) \ +$(INCHI_LIB_OBJS) -Wl$(LINUX_MAP)$(LINUX_Z_RELRO),--no-undefined -Wl,-soname,$(INCHI_LIB_NAME).so$(MAIN_VERSION) -lm + ln -fs $(INCHI_LIB_NAME).so$(VERSION) \ +$(INCHI_LIB_PATHNAME).so$(MAIN_VERSION) +# === InChI Library compile rule ========= +%.o: $(P_LIBR)%.c + $(C_COMPILER) $(C_SO_OPTIONS) $(C_OPTIONS) $< +%.o: $(P_LIBR_IXA)%.c + $(C_COMPILER) $(C_SO_OPTIONS) $(C_OPTIONS) $< +%.o: $(P_BASE)%.c + $(C_COMPILER) $(C_SO_OPTIONS) $(C_OPTIONS) $< diff --git a/INCHI-1-SRC/INCHI_API/demos/mol2inchi/gcc/makefile32 b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/gcc/makefile32 new file mode 100644 index 0000000..52182d8 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/gcc/makefile32 @@ -0,0 +1,197 @@ +# This will create 32-bit executable under 64-bit Linux +# +# +# Comment out the next line to create so/dll only +CREATE_MAIN = 1 +# Comment out the next line to create mol2inchi executable (otherwise, inchi_maiin is created) +CALLER_IS_MOL2INCHI = 1 +# or define ISLINUX in command line: make ISLINUX=1 +ISLINUX = 1 +# Linux fpic option: replace -fPIC with -fpic if the latter works +# Comment out "LINUX_Z_RELRO =" if -z relro is not supported +# These options are needed to avoid the following SELinux message: +# "Error: cannot restore segment prot after reloc: Permission denied" +# In addition, inchi.map restricts set of expoorted from .so +# functions to those which belong to InChI API +LINUX_MAP = ,--version-script=libinchi.map +ifdef ISLINUX +LINUX_FPIC = -fPIC +LINUX_Z_RELRO = ,-z,relro +endif +# === version === +MAIN_VERSION = .1 +VERSION = $(MAIN_VERSION).05.00 +# === executable & library directory === +ifndef LIB_DIR + LIB_DIR = ../../../bin/Linux/32bit +endif +# === InChI Library name === +ifndef INCHI_LIB_NAME + INCHI_LIB_NAME = libinchi +endif +INCHI_LIB_PATHNAME = $(LIB_DIR)/$(INCHI_LIB_NAME) +# === Main program name ==== +ifndef API_CALLER_NAME + ifndef CALLER_IS_MOL2INCHI + API_CALLER_NAME = inchi_main$ + else + API_CALLER_NAME = mol2inchi$ + endif +endif +API_CALLER_PATHNAME = $(LIB_DIR)/$(API_CALLER_NAME) +# === Linker to create (Shared) InChI library ==== +ifndef SHARED_LINK + SHARED_LINK = gcc -shared +endif +# === Linker to create Main program ===== +ifndef LINKER + ifdef ISLINUX + LINKER_CWD_PATH = -Wl,-R,"" + endif + LINKER = gcc -s $(LINKER_CWD_PATH) +endif +ifndef P_LIBR + P_LIBR = ../../../libinchi/src/ +endif +ifndef P_LIBR_IXA + P_LIBR_IXA = ../../../libinchi/src/ixa/ +endif +ifndef P_BASE + P_BASE = ../../../../INCHI_BASE/src/ +endif +ifndef P_MAIN + ifndef CALLER_IS_MOL2INCHI + P_MAIN = ../src/ + else + P_MAIN = ../src/ + endif +endif +# === C Compiler =============== +ifndef C_COMPILER + C_COMPILER = gcc +endif +# === C Compiler Options ======= +ifndef C_OPTIONS + ifndef CALLER_IS_MOL2INCHI + C_OPTIONS = -m32 -ansi -O3 -c + else + C_OPTIONS = -m32 -O3 -c + endif + ifdef ISLINUX + ifndef C_SO_OPTIONS + C_SO_OPTIONS = $(LINUX_FPIC) -DTARGET_API_LIB -DCOMPILE_ANSI_ONLY + endif + endif + ifndef C_MAIN_OPTIONS + C_MAIN_OPTIONS = -DBUILD_LINK_AS_DLL -DTARGET_EXE_USING_API + endif +endif +ifdef CREATE_MAIN +ifndef CALLER_IS_MOL2INCHI +API_CALLER_SRCS = $(P_MAIN)e_0dstereo.c \ +$(P_MAIN)e_ichimain.c \ +$(P_MAIN)e_ichi_io.c \ +$(P_MAIN)e_ichi_parms.c \ +$(P_MAIN)e_inchi_atom.c \ +$(P_MAIN)e_mol2atom.c \ +$(P_MAIN)e_readinch.c \ +$(P_MAIN)e_readmol.c \ +$(P_MAIN)e_readstru.c \ +$(P_MAIN)e_util.c \ +$(P_MAIN)e_ichimain_a.c +API_CALLER_OBJS = e_0dstereo.o \ +e_ichimain.o \ +e_ichi_io.o \ +e_ichi_parms.o \ +e_inchi_atom.o \ +e_mol2atom.o \ +e_readinch.o \ +e_readmol.o \ +e_readstru.o \ +e_util.o \ +e_ichimain_a.o +else +API_CALLER_SRCS = $(P_MAIN)mol2inchi.c \ +$(P_MAIN)moreutil.c +API_CALLER_OBJS = mol2inchi.o \ +moreutil.o +endif +# === InChI Main Link rule ================ +$(API_CALLER_PATHNAME) : $(API_CALLER_OBJS) $(INCHI_LIB_PATHNAME).so$(VERSION) + $(LINKER) -m32 -o $(API_CALLER_PATHNAME) $(API_CALLER_OBJS) \ + $(INCHI_LIB_PATHNAME).so$(VERSION) -lm +# === InChI Main compile rule ============ +%.o: $(P_MAIN)%.c + $(C_COMPILER) $(C_MAIN_OPTIONS) $(C_OPTIONS) $< +endif +# === InChI Library Object files ============ +INCHI_LIB_OBJS = ichican2.o \ +ichicano.o \ +ichi_io.o \ +ichierr.o \ +ichicans.o \ +ichiisot.o \ +ichilnct.o \ +ichimak2.o \ +ichimake.o \ +ichimap1.o \ +ichimap2.o \ +ichimap4.o \ +ichinorm.o \ +ichiparm.o \ +ichiprt1.o \ +ichiprt2.o \ +ichiprt3.o \ +ichiqueu.o \ +ichiring.o \ +ichisort.o \ +ichister.o \ +ichitaut.o \ +ichi_bns.o \ +inchi_dll.o \ +ichiread.o \ +ichirvr1.o \ +ichirvr2.o \ +ichirvr3.o \ +ichirvr4.o \ +ichirvr5.o \ +ichirvr6.o \ +ichirvr7.o \ +ikey_dll.o \ +ikey_base26.o \ +inchi_dll_main.o \ +inchi_dll_a.o \ +inchi_dll_a2.o \ +inchi_dll_b.o \ +ixa_inchikey_builder.o \ +ixa_read_mol.o \ +ixa_status.o \ +ixa_builder.o \ +ixa_mol.o \ +ixa_read_inchi.o \ +mol_fmt1.o \ +mol_fmt2.o \ +mol_fmt3.o \ +mol2atom.o \ +mol_fmt4.o \ +readinch.o \ +runichi.o \ +runichi2.o \ +runichi3.o \ +runichi4.o \ +sha2.o \ +strutil.o \ +util.o +# === InChI Library link rule ========= +$(INCHI_LIB_PATHNAME).so$(VERSION): $(INCHI_LIB_OBJS) + $(SHARED_LINK) $(SHARED_LINK_PARM) -m32 -o $(INCHI_LIB_PATHNAME).so$(VERSION) \ +$(INCHI_LIB_OBJS) -Wl$(LINUX_MAP)$(LINUX_Z_RELRO),--no-undefined -Wl,-soname,$(INCHI_LIB_NAME).so$(MAIN_VERSION) -lm + ln -fs $(INCHI_LIB_NAME).so$(VERSION) \ +$(INCHI_LIB_PATHNAME).so$(MAIN_VERSION) +# === InChI Library compile rule ========= +%.o: $(P_LIBR)%.c + $(C_COMPILER) $(C_SO_OPTIONS) $(C_OPTIONS) $< +%.o: $(P_LIBR_IXA)%.c + $(C_COMPILER) $(C_SO_OPTIONS) $(C_OPTIONS) $< +%.o: $(P_BASE)%.c + $(C_COMPILER) $(C_SO_OPTIONS) $(C_OPTIONS) $< diff --git a/INCHI-1-SRC/INCHI_API/demos/mol2inchi/gcc/run_make_on_linux.sh b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/gcc/run_make_on_linux.sh new file mode 100644 index 0000000..a6d09d8 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/gcc/run_make_on_linux.sh @@ -0,0 +1,2 @@ +#!/bin/sh +make ISLINUX=1 diff --git a/INCHI-1-SRC/INCHI_API/demos/mol2inchi/readme.txt b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/readme.txt new file mode 100644 index 0000000..a6471e8 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/readme.txt @@ -0,0 +1,51 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 29, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +This directory contains example of using newly added in v. 1.05 +function MakeINCHIFromMolfileText(). + +The source codes are placed in sub-directory 'src'; +gcc/Linux makefiles in sub-directory 'gcc'; +MS VS 2008 project is placed in sub-directory 'vc9'. + +The created binaries are saved in upper-level directory 'bin'. + +The light-weight demo program mol2inchi reads input Molfile/SDFile and +generates InChI directly from that texts, bypassing creation of separate +internal data structures holding molecules. diff --git a/INCHI-1-SRC/INCHI_API/demos/mol2inchi/src/mol2inchi.c b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/src/mol2inchi.c new file mode 100644 index 0000000..fc560c7 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/src/mol2inchi.c @@ -0,0 +1,733 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#pragma warning( disable : 4996 ) + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "../../../../INCHI_BASE/src/inchi_api.h" +#include "mol2inchi.h" +#include "moreitil.h" + +#ifdef BUILD_WITH_ENG_OPTIONS +#include "shuffler.h" +/* local functions */ +int extract_MOL_counts_and_version(char *str, M2I_NUM *num_at, M2I_NUM *num_bo); +int extract_counts_from_MOL_V3000(char *str, M2I_NUM *num_at, M2I_NUM *num_bo); +int extract_name_from_MOL(char *str, char *name, size_t max_symbols); +double getCPUTime(void); +#endif + + + + + +/****************************************************************************/ +int main(int argc, char *argv[]) +{ +int retcode=0, result=0, nerrs=0, k; +char *fname=NULL; +FILE *f=NULL; +char *text=NULL; + +char *option1; +char *out=NULL, *log=NULL; +struct WorkerDetails wd; + +time_t begin; + +#if defined(_WIN32) +const char *platform="Windows"; +#else +const char *platform="Linux"; +#endif + + char banner[255]; + sprintf( banner, "%s\n%-s Build of %-s %-s%s\n", + APP_DESCRIPTION, + platform, __DATE__, __TIME__, + RELEASE_IS_FINAL?"":" *** pre-release, for evaluation only ***"); + + /* Parse command line + assume that the first item is input filename, + then mol2ichi own switches, then all the others + which should be passed to InChI calculation algorithm + */ + + memset( &wd, 0, sizeof(wd) ); + + fprintf( stderr, "%-s\n", banner); + if ( argc < 2 ) + { + print_help(); + retcode = 1; + goto finish; + } + + fprintf( stderr, "Started at "); + print_time(); + ctime(&begin); + + /* Set worker details */ + wd.get_inchikey = 0; + wd.do_not_print_inchi = 0; + wd.output_error_inchi = 0; + wd.do_benchmark = 0; + wd.nperms = 0; + wd.tmax_shuffle = 0; + wd.dtmax_shuffle = 0.0; + wd.verbose = 1; + wd.out = NULL; + wd.log = NULL; + wd.rstart = 1; + wd.rend = 999999999; + wd.nmol = 0; + wd.n_inchi_err = 0; + + /* limit calculations by 60 sec */ +#ifdef _WIN32 + strcpy(wd.options, "/W60"); +#else + strcpy(wd.options, "-W60"); +#endif + + fname = argv[1]; + for(k=2; k 0 ) + { + wd.nmol++; + if ( wd.nmol < wd.rstart ) + continue; + if ( wd.nmol > wd.rend ) + break; + nerrs = work_on_molfile_text ( text, &wd ); + } + + +finish: + + if ( retcode==0 ) + fprintf( stderr, "\nFinished OK at "); + else + fprintf( stderr, "\nFinished with exit code %-d at ", retcode); + + print_time(); + + fprintf( stderr, "Processed %lu structure(s) with %-ld error(s).\n", + wd.nmol, wd.n_inchi_err ); + if ( text ) + free( text ); + + return retcode; +} + + + +/* + Process one MOLFile text + + Optionally organize benchmarking and + repeat calculations with shuffled atoms +*/ +int work_on_molfile_text( const char* text, struct WorkerDetails * wd) +{ +int res=0, nerrs=0, shuffle_atoms=0; +int lenname=0, max_mname_symbols=79; +long irepeat=0; +double startTime=0.0; +double run_duration=0.0; +int nrepeats=0; +int have_problems=0; + +#ifdef BUILD_WITH_ENG_OPTIONS +M2I_NUM numbers[PERMAXATOMS]; +M2I_NUM nat = -1, nbonds = -1; +char mname[80]; +MFLines *mfdata=NULL; +long i; +int version; +#endif + +/* Warning: in this demo, we assume that printout (InChI, etc.) */ +/* does not exceed some large INCHIPRINTOUTSIZE */ +char output0[INCHIPRINTOUTSIZE], errstr0[4096]; +char ikey0[28]; + + ikey0[0] = output0[0] = errstr0[0] = '\0'; + +#ifdef BUILD_WITH_ENG_OPTIONS + /* Get info for printout */ + lenname = extract_name_from_MOL( (char *) text, mname, max_mname_symbols); + if ( !lenname ) + strcpy(mname, ""); + if ( wd->do_benchmark ) + { + startTime = getCPUTime( ); + } +#endif + + /* Perform a single run */ + nerrs = run_once_on_molfile_text( text, wd, ikey0, output0, errstr0); + + if ( errstr0[0] ) + fprintf( stderr, "%-s\n", errstr0); + +#ifndef BUILD_WITH_ENG_OPTIONS + fprintf( stdout, "%-s", output0 ); + fprintf( stdout, "\n"); + fflush(NULL); +#else + /* have_problems = extract_counts_from_MOL_V3000( (char *) text, &nat, &nbonds); */ + version = extract_MOL_counts_and_version( (char *) text, &nat, &nbonds); + have_problems = (version!=2000) && (version!=3000); + + if ( wd->do_benchmark ) + { + run_duration = getCPUTime( ) - startTime; + if ( wd->nperms > 0 ) + { + /* Permutations requested */ + nrepeats = wd->nperms; + + if ( nrepeats > 0 ) + { + if ( version!=3000 || nat<1 || nat>PERMAXATOMS ) + { + nrepeats = 0; + if ( wd->verbose >= 1 ) + fprintf( stderr, + "No shuffle for struct #%-ld: not V3000 data file or too many atoms (code %-d).\n", + wd->nmol, have_problems ); + } + } + + if ( nrepeats > 0 && wd->tmax_shuffle > 0 ) + { + /* Check single-run timing... */ + if ( run_duration >= wd->dtmax_shuffle ) + { + /* Single-run duration exceeds treshold, do not repeat */ + nrepeats = 0; + if ( wd->verbose >= 1 ) + fprintf( stderr, "No shuffle for struct #%-ld: too long run.\n", wd->nmol ); + } + } + } + } /* if ( wd->do_benchmark ) */ + + fprintf( stdout, "%-s\t%-s\t", output0, mname ); + + if ( wd->do_benchmark ) + { + fprintf( stdout, "#%-ld\t%-d\t%-d\t%-lf", + irepeat, nat, nbonds,run_duration ); + if ( ikey0[0] ) + fprintf( stdout, "%-s", (nrepeats < 1)?"\t---":"\t***" ); + } + fprintf( stdout, "\n"); + fflush(NULL); + + + if ( nerrs > 0 ) + { + /* Failed, nothing to do here anymore */ + return nerrs; + } + + if ( nrepeats < 1 ) + return 0; + + /* Repeat nrepeat times */ + + /* Prepare data structs for repeated calulations */ + for (i=0; iverbose >= 1 ) + { + if ( wd->verbose >= 2 ) +#pragma omp critical + { + fprintf( stderr, "%-s perm#%-.4ld { %-d", mname, irepeat, numbers[0]); + for (i=1; iverbose >= 10 ) + { + fprintf( stderr, "\n\n%-s\n\n", mftext); + } + } + } + + if ( wd->do_benchmark ) + startTime = getCPUTime( ); + + + res1 = run_once_on_molfile_text( (const char *) mftext, wd, ikey, output, errstr); + + + if ( wd->do_benchmark ) + { + run_duration = getCPUTime( ) - startTime; + } + if ( errstr[0] ) +#pragma omp critical + { + if ( wd->verbose >= 1 ) +#ifdef _OPENMP + fprintf( stderr, "(%-s) [thread %-d] \n", errstr, omp_get_thread_num()); +#else + fprintf( stderr, "(%-s)\n", errstr ); +#endif + } + + if ( wd->do_benchmark ) +#pragma omp critical + { + { + fprintf( stdout, "%-s\t%-s\t#%-ld\t%-d\t%-d\t%-lf", + output, mname, irepeat, nat, nbonds,run_duration ); + if ( ikey0[0] && ikey[0] ) + fprintf( stdout, "%-s", + strcmp(ikey,ikey0)?"\t!!!":"\t..." ); + fprintf( stdout, "\n"); + fflush(NULL); + } + } + + if ( mftext ) + free( mftext ); + } + + + if ( mfdata ) + del_mflines( mfdata ); + +#endif + + return 0; +} + + +/* Actual InChI calculation worker */ +int run_once_on_molfile_text( const char* mftext, + struct WorkerDetails* wd, + char *ikey, + char *output, + char *errstr) +{ +int res=0, errs=0; +inchi_Output iout, *result = &iout; +const char *delim = "\n"; + + memset( result, 0, sizeof(*result) ); + + /* Call API function to calculate InChI */ + res = MakeINCHIFromMolfileText( mftext, wd->options, result ); + + if ( res==mol2inchi_Ret_ERROR || + res==mol2inchi_Ret_ERROR_get || + res==mol2inchi_Ret_ERROR_comp ) + { + errs++; + wd->n_inchi_err++; + sprintf( errstr, "Error " ); + } + else if ( res==mol2inchi_Ret_WARNING ) + { + sprintf( errstr, "Warning " ); + } + if ( result->szLog && result->szLog[0] ) + sprintf( errstr, + "%-s structure #%-lu", + result->szLog, wd->nmol ); + + /* Print InChI and other stuff if InChI is not empty */ + if ( result->szInChI && result->szInChI[0] ) + { + /* Calculate and print InChIKey */ + if ( wd->get_inchikey ) + { + int ik_ret=0; /* InChIKey-calc result code */ + int xhash1, xhash2; + char szXtra1[256], szXtra2[256]; + xhash1 = xhash2 = 0; + + ik_ret = GetINCHIKeyFromINCHI( result->szInChI, + xhash1, + xhash2, + ikey, + szXtra1, + szXtra2); + if (ik_ret!=INCHIKEY_OK) + ikey[0]='\0'; + } + + sprintf(output, "Structure: %-lu%s%-s%s%-s%s%-s", + wd->nmol, + wd->do_not_print_inchi ? "" : delim, + wd->do_not_print_inchi ? "" : result->szInChI, + ikey[0] ? delim : "", + ikey[0] ? ikey : "", + result->szAuxInfo && result->szAuxInfo[0] ? delim : "", + result->szAuxInfo && result->szAuxInfo[0] ? result->szAuxInfo : "" + ); + } + + /* Reset output data structure */ + FreeINCHI ( result ); + + return errs; +} + + + +/* + Print program usage instructions +*/ +void print_help(void) +{ + fprintf( stderr, "Usage: \n"); + fprintf( stderr, "mol2inchi inputfilename [options]\n"); + fprintf( stderr, "Options:\n"); + fprintf( stderr, "\tSTART:n - start from SDF record n\n"); + fprintf( stderr, "\tEND:n - end when reached SDF record n\n"); + fprintf( stderr, "\tRECORD:n - process only SDF record n\n"); + fprintf( stderr, "\tNOINCHI - do not print InChI itself\n"); + fprintf( stderr, "\tKEY - calc and print InChIKey\n"); + fprintf( stderr, "\tPOLYMERS - treat polymers (experimental)\n"); +#ifdef BUILD_WITH_ENG_OPTIONS + fprintf( stderr, "\tBENCHMARK - collect timing\n"); + fprintf( stderr, "\tNSHUFFLE:n - reorder atoms n times and repeat\n"); + fprintf( stderr, "\tTSHUFFLE:t - do not repeat runs of t sec or longer\n"); + fprintf( stderr, "\tVERBOSE:n - > 0 means more output\n"); +#endif + fprintf( stderr, "\t[common InChI API options]\n"); +} + + +#ifdef BUILD_WITH_ENG_OPTIONS +/******************************************************************/ +int extract_name_from_MOL(char *str, char *name, size_t max_symbols) +{ +char *p; +size_t ncopied; +#define CNAMEL 4096 +char buf[CNAMEL]; + + if (str==NULL) + return -1; + if (strlen(str)<1) + return -1; + + p = get_substr_in_between(str, "", "\n", buf, CNAMEL-2, &ncopied); + + if ( ncopied ) + { + if ( ncopied > max_symbols ) + ncopied = max_symbols; + memcpy( name, buf, ncopied ); + name[ncopied++]='\0'; + } + + return (int) ncopied; +} + + +/***************************************************************************/ +int extract_counts_from_MOL_V3000(char *str, M2I_NUM *num_at, M2I_NUM *num_bo) +{ +size_t n, ncopied; +char *p, *pp, *next; +#define CBUFL 16 +char buf[CBUFL]; + + + *num_at = *num_bo = -1; + + if (str==NULL) + return -1; + if (strlen(str)<1) + return -2; + + p = str; + + n = 5; + while (n--) + while ((++p)[0]!='\n') ; + ++p; + + next = get_substr_in_between(p, "M V30 COUNTS ", " ", buf, CBUFL-2, &ncopied); + + if ( !ncopied ) + return -3; + if ( !strlen(buf) ) + return -4; + + *num_at = (M2I_NUM ) strtol(buf, NULL, 10); + if ( *num_at < 1) + return -5; + + pp = p + strlen("M V30 COUNTS ") + strlen(buf) +1; + buf[0] = '\0'; + next = get_substr_in_between(pp, "", " ", buf, CBUFL-2, &ncopied); + if ( ncopied ) + if ( strlen(buf) ) + *num_bo = (M2I_NUM ) strtol(buf, NULL, 10); + + return 0; +} + + + +/***************************************************************************/ +int extract_MOL_counts_and_version(char *str, M2I_NUM *num_at, M2I_NUM *num_bo) +{ +size_t n, ncopied; +char *p, *pp, *next; +#define CBUFL 16 +char buf[CBUFL]; +int version; +char *p2000=NULL; + + version = *num_at = *num_bo = -1; + + if (str==NULL) + return -1; + if (strlen(str)<1) + return -2; + + p = str; + + n = 3; + while (n--) + while ((++p)[0]!='\n') ; + ++p; + + p2000 = strstr(p, "V2000"); + if ( p2000) + { + int k; + char sna[4], snb[4]; + if ( strlen(p) < 6 ) + return version; + for (k=0; k<3; k++) + { + sna[k] = p[k]; + snb[k] = p[k+3]; + } + sna[3] = snb[3] = '\0'; + *num_at = (M2I_NUM ) strtol(sna, NULL, 10); + if ( *num_at < 1) + return -5; + *num_bo = (M2I_NUM ) strtol(snb, NULL, 10); + version = 2000; + return version; + } + + version = 3000; + p = str; + n = 5; + while (n--) + while ((++p)[0]!='\n') ; + ++p; + + next = get_substr_in_between(p, "M V30 COUNTS ", " ", buf, CBUFL-2, &ncopied); + + if ( !ncopied ) + return -3; + if ( !strlen(buf) ) + return -4; + + *num_at = (M2I_NUM ) strtol(buf, NULL, 10); + if ( *num_at < 1) + return -5; + + pp = p + strlen("M V30 COUNTS ") + strlen(buf) +1; + buf[0] = '\0'; + next = get_substr_in_between(pp, "", " ", buf, CBUFL-2, &ncopied); + if ( ncopied ) + if ( strlen(buf) ) + *num_bo = (M2I_NUM ) strtol(buf, NULL, 10); + + return version; +} +#endif diff --git a/INCHI-1-SRC/INCHI_API/demos/mol2inchi/src/mol2inchi.h b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/src/mol2inchi.h new file mode 100644 index 0000000..4cf228d --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/src/mol2inchi.h @@ -0,0 +1,92 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _MOL2INCHI_H_ +#define _MOL2INCHI_H_ + +#define MOLBUFSIZE 16777216 /* 16 MB */ +#define INCHIPRINTOUTSIZE 100000 + +#define APP_DESCRIPTION "InChI version 1, Software v. 1.05 (mol2inchi - Library call example, API v. 1.05)" + +/*#define RELEASE_IS_FINAL 0 *//* 1=> pre-release version; comment out to disable */ +#ifndef RELEASE_IS_FINAL +#define RELEASE_IS_FINAL 1 /* final release */ +#endif + + +/* +#define BUILD_WITH_ENG_OPTIONS 1 +*/ + + +struct WorkerDetails +{ + int do_not_print_inchi; + int get_inchikey; + int output_error_inchi; + int do_benchmark; + int nperms; + int verbose; + long tmax_shuffle; + double dtmax_shuffle; + char options[4096]; /* common ('old') API options */ + char* out; + char* log; + char* ikey; + long rstart; + long rend; + long nmol; + long n_inchi_err; +} WorkerDetails; + +/* Actual worker */ +int work_on_molfile_text( const char* text, struct WorkerDetails* details); + +/* Even more actual worker */ +int run_once_on_molfile_text( const char* mftext, struct WorkerDetails* details, + char *ikey, char *output, char *errstr); + +/* Print program usage instructions */ +void print_help(void); + +/* Calculate InChIKey for the given InChI string */ +int CalcPrintINCHIKEY( char *INCHI, char *ik_string ); + +#endif /* _MOL2INCHI_H_ */ diff --git a/INCHI-1-SRC/INCHI_API/demos/mol2inchi/src/moreitil.h b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/src/moreitil.h new file mode 100644 index 0000000..774b6a9 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/src/moreitil.h @@ -0,0 +1,70 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _MOREUTIL_H_ +#define _MOREUTIL_H_ + + +#define MOLMAXLINELEN 1024 + + + + +/* Just print date & time */ +void print_time(void); +/* For portability - use own version of stricmp and memicmp */ +int own_stricmp( const char *s1, const char *s2 ); +int own_memicmp( const void*, const void*, size_t ); +/* Yet anothe helper */ +char* get_substr_in_between(char *s, + char *pat1, char *pat2, + char *buf, + size_t max_symbols, size_t *copied); + + +/* (SD)File i/o related */ + +/* Read Molfile (SDFile segment) to text buffer */ +/* Return 1 if got something otherwise 0 */ +int get_next_molfile_as_text( FILE *f, char *buf , size_t buflen ); +/* Fgets which ensures single linefeed at the end */ +char* fgets_lf( char* line, int line_len, FILE *f ); + + +#endif /* _MOREUTIL_H_ */ diff --git a/INCHI-1-SRC/INCHI_API/demos/mol2inchi/src/moreutil.c b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/src/moreutil.c new file mode 100644 index 0000000..d47043c --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/src/moreutil.c @@ -0,0 +1,240 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#pragma warning( disable : 4996 ) + +#include +#include +#include +#include + + +#include "moreitil.h" + +typedef unsigned char U_CHAR; + +int replace_num_in_at_line( char *str, char *line, long num ); +int replace_nums_in_bo_line( char *str, char *line, int shuffled_numbers[] ); + + +/**************************/ +/* Just print date & time */ +void print_time(void) +{ +time_t tt; +char st[36]=""; + tt = time(&tt); + strncat(st,(char *) ctime(&tt), 32); + fprintf( stderr, "%-s",st); +} + + +/********************************************/ +/* For portability - own version of stricmp */ +#define __MYTOLOWER(c) ( ((c) >= 'A') && ((c) <= 'Z') ? ((c) - 'A' + 'a') : (c) ) +int own_stricmp( const char *s1, const char *s2 ) +{ + while ( *s1 ) + { + if ( *s1 == *s2 || + __MYTOLOWER( (int)*s1 ) == __MYTOLOWER( (int)*s2 )) + { + s1 ++; + s2 ++; + } + else + { + return + __MYTOLOWER( (int)*s1 ) - __MYTOLOWER( (int)*s2 ); + } + } + + if ( *s2 ) + return -1; + + return 0; +} + + +/*****************************************************************/ +int own_memicmp ( const void * p1, const void * p2, size_t length ) +{ + const U_CHAR *s1 = (const U_CHAR*)p1; + const U_CHAR *s2 = (const U_CHAR*)p2; + while ( length-- ) + { + if ( *s1 == *s2 || + __MYTOLOWER( (int)*s1 ) == __MYTOLOWER( (int)*s2 )) + { + s1 ++; + s2 ++; + } + else + { + return + __MYTOLOWER( (int)*s1 ) - __MYTOLOWER( (int)*s2 ); + } + } + + return 0; +} + + +/*********************************************/ +/* Extracts a substring sitting in 's' in + between patterns 'pat1' and 'pat2'. + Returns pointer to the end of pat2 plus 1. + Fills in copied, the actual n bytes found. + Null-terminated 'buf' contains not more + than 'max_symbols' leading characters + from found substring. +*/ +char* get_substr_in_between(char *s, + char *pat1, char *pat2, + char *buf, + size_t max_symbols, + size_t *copied) +{ +size_t n=0; +char *p1=NULL, *p1a=NULL, *p2 = NULL; + + *copied = 0; + + p1 = strstr(s, pat1); + if ( !p1) + return NULL; + + p1a = p1 + strlen(pat1); + + if ( !strcmp( pat2, "") ) + { + p2 = s + strlen(s) - 1; + n = 1; + } + else + { + p2 = strstr(p1a, pat2); + } + if ( !p2) + return NULL; + + n+= (int) (p2 - p1a); + + if ( n ) + { + *copied = n; + if ( *copied > max_symbols ) + *copied = max_symbols; + memcpy( buf, p1a, *copied ); + buf[(*copied)++] = '\0'; + } + + return p2+1; +} + + +/**************************************************/ +/* Fgets which ensures single linefeed at the end */ +char* fgets_lf( char* line, int line_len, FILE *f ) +{ +char *p=NULL, *q; + + /* Read from file */ + memset( line, 0, line_len ); + if ( NULL!=( p =fgets(line, line_len, f) ) && + NULL==strchr(p, '\n' ) ) + { + char temp[64]; + /* bypass up to '\n' or up to end of file whichever comes first*/ + while ( NULL!=fgets(temp, sizeof(temp), f) && + NULL==strchr(temp, '\n') ) + ; + } + + /* fix CR CR LF line terminator. */ + if ( p ) + { + if (q = strchr(line, '\r') ) + { + q[0] = '\n'; + q[1] = '\0'; + } + } + + return p; +} + + +/***************************************************************/ +/* Read Molfile (SDFile segment) to text buffer */ +/* Return 1 if got something, -1 if no room, otherwise 0 */ +int get_next_molfile_as_text( FILE *f, char *buf, size_t buflen ) +{ +char *p=NULL, line[MOLMAXLINELEN]; +size_t pos=0l, bufsize=0; +int empty = 1; + + while ( p=fgets_lf(line, MOLMAXLINELEN, f) ) + { + size_t n = strlen(line); + if ( n ) + { + if ( empty ) + empty = 0; /* Got something non-empty, do not mind the content */ + + if ( buflen < pos + n -1 ) + return -1; /* buffer buf is too small */ + memcpy( buf + pos, line , n ); + pos+= n; + /* Check if we at the end of Molfile segment */ + if ( line[0]=='$' && n>3 && + line[1]=='$' && line[2]=='$' && line[3]=='$' ) + break; + } + } + + + if ( empty ) + return 0; + else + { + buf[pos] = '\0'; + return 1; + } +} diff --git a/INCHI-1-SRC/INCHI_API/demos/mol2inchi/vc9/mol2inchi.aps b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/vc9/mol2inchi.aps new file mode 100644 index 0000000..75bb574 Binary files /dev/null and b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/vc9/mol2inchi.aps differ diff --git a/INCHI-1-SRC/INCHI_API/vc9/inchi_main/inchi_main1.rc b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/vc9/mol2inchi.rc similarity index 75% rename from INCHI-1-SRC/INCHI_API/vc9/inchi_main/inchi_main1.rc rename to INCHI-1-SRC/INCHI_API/demos/mol2inchi/vc9/mol2inchi.rc index d55c7af..b4a6a51 100644 --- a/INCHI-1-SRC/INCHI_API/vc9/inchi_main/inchi_main1.rc +++ b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/vc9/mol2inchi.rc @@ -1,113 +1,114 @@ -// Microsoft Visual C++ generated resource script. -// -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "afxres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// Russian resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS) -#ifdef _WIN32 -LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT -#pragma code_page(1251) -#endif //_WIN32 - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""afxres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - -#endif // Russian resources -///////////////////////////////////////////////////////////////////////////// - - -///////////////////////////////////////////////////////////////////////////// -// English (U.S.) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -#ifdef _WIN32 -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) -#endif //_WIN32 - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,0,4,1 - PRODUCTVERSION 1,0,4,1 - FILEFLAGSMASK 0x17L -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x4L - FILETYPE 0x1L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "Comments", "IUPAC International Chemical Identifier (InChI) version 1, Software version 1.04. Build of September 9, 2011. Example of library call." - VALUE "FileDescription", "InChI version 1 (1.04) Build of September 9, 2011" - VALUE "FileVersion", "1, 0, 4, 1" - VALUE "InternalName", "inchi_main" - VALUE "OriginalFilename", "inchi_main.exe" - VALUE "ProductName", "IUPAC International Chemical Identifier (InChI) version 1, Software version 1.04. Build of September 9, 2011. Example of library call. " - VALUE "ProductVersion", "1, 0, 4, 1" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END - -#endif // English (U.S.) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Russian resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS) +#ifdef _WIN32 +LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT +#pragma code_page(1251) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // Russian resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,5,0 + PRODUCTVERSION 1,0,5,0 + FILEFLAGSMASK 0x37L +#ifdef _DEBUG + FILEFLAGS 0x21L +#else + FILEFLAGS 0x20L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "IUPAC International Chemical Identifier (InChI) version 1, Software version 1.05, Winter 2017. Demo program mol2inchi." + VALUE "FileDescription", "Demo program mol2inchi." + VALUE "FileVersion", "1, 0, 5, 0" + VALUE "InternalName", "mol2inchi" + VALUE "OriginalFilename", "mol2inchi.exe" + VALUE "ProductName", "IUPAC International Chemical Identifier (InChI) version 1, Software version 1.05,Winter 2017. Demo program mol2inchi." + VALUE "ProductVersion", "1, 0, 5,0" + VALUE "SpecialBuild", "RELEASE" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/INCHI-1-SRC/INCHI_API/demos/mol2inchi/vc9/mol2inchi.sln b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/vc9/mol2inchi.sln new file mode 100644 index 0000000..43c7020 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/vc9/mol2inchi.sln @@ -0,0 +1,39 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mol2inchi", "mol2inchi.vcproj", "{8CB46818-493F-4256-B379-B01FD2C89993}" + ProjectSection(ProjectDependencies) = postProject + {5DAB1696-1795-49AA-BDE6-1771FBE25445} = {5DAB1696-1795-49AA-BDE6-1771FBE25445} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libinchi", "..\..\..\libinchi\vc9\libinchi.vcproj", "{5DAB1696-1795-49AA-BDE6-1771FBE25445}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8CB46818-493F-4256-B379-B01FD2C89993}.Debug|Win32.ActiveCfg = Debug|Win32 + {8CB46818-493F-4256-B379-B01FD2C89993}.Debug|Win32.Build.0 = Debug|Win32 + {8CB46818-493F-4256-B379-B01FD2C89993}.Debug|x64.ActiveCfg = Debug|x64 + {8CB46818-493F-4256-B379-B01FD2C89993}.Debug|x64.Build.0 = Debug|x64 + {8CB46818-493F-4256-B379-B01FD2C89993}.Release|Win32.ActiveCfg = Release|Win32 + {8CB46818-493F-4256-B379-B01FD2C89993}.Release|Win32.Build.0 = Release|Win32 + {8CB46818-493F-4256-B379-B01FD2C89993}.Release|x64.ActiveCfg = Release|x64 + {8CB46818-493F-4256-B379-B01FD2C89993}.Release|x64.Build.0 = Release|x64 + {5DAB1696-1795-49AA-BDE6-1771FBE25445}.Debug|Win32.ActiveCfg = Debug|Win32 + {5DAB1696-1795-49AA-BDE6-1771FBE25445}.Debug|Win32.Build.0 = Debug|Win32 + {5DAB1696-1795-49AA-BDE6-1771FBE25445}.Debug|x64.ActiveCfg = Debug|x64 + {5DAB1696-1795-49AA-BDE6-1771FBE25445}.Debug|x64.Build.0 = Debug|x64 + {5DAB1696-1795-49AA-BDE6-1771FBE25445}.Release|Win32.ActiveCfg = Release|Win32 + {5DAB1696-1795-49AA-BDE6-1771FBE25445}.Release|Win32.Build.0 = Release|Win32 + {5DAB1696-1795-49AA-BDE6-1771FBE25445}.Release|x64.ActiveCfg = Release|x64 + {5DAB1696-1795-49AA-BDE6-1771FBE25445}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/INCHI-1-SRC/INCHI_API/demos/mol2inchi/vc9/mol2inchi.vcproj b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/vc9/mol2inchi.vcproj new file mode 100644 index 0000000..6da6bb7 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/vc9/mol2inchi.vcproj @@ -0,0 +1,374 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/INCHI-1-SRC/INCHI_API/vc9/inchi_main/resource.h b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/vc9/resource.h similarity index 90% rename from INCHI-1-SRC/INCHI_API/vc9/inchi_main/resource.h rename to INCHI-1-SRC/INCHI_API/demos/mol2inchi/vc9/resource.h index dcf622d..8077875 100644 --- a/INCHI-1-SRC/INCHI_API/vc9/inchi_main/resource.h +++ b/INCHI-1-SRC/INCHI_API/demos/mol2inchi/vc9/resource.h @@ -1,6 +1,6 @@ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. -// Used by inchi_main1.rc +// Used by mol2inchi.rc // Next default values for new objects // diff --git a/INCHI-1-SRC/INCHI_API/demos/python_sample/mol2inchi.py b/INCHI-1-SRC/INCHI_API/demos/python_sample/mol2inchi.py new file mode 100644 index 0000000..ac350d7 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/python_sample/mol2inchi.py @@ -0,0 +1,269 @@ +import os +import sys +import string +import gzip +from optparse import OptionParser +from yapyinchi import * +# +# +# +############################################################################## +def show_record_results(rst, fout, flog): + # + inchi_result, ikey_result, sinchi, slog, smessage, saux, sikey, sxtra1, sxtra2, ierr = rst + # + if inchi_result != 0: + if (flog!=sys.stdout): + flog.write('\n RECORD %-d\t' % (num) ) + if (inchi_result==1): + flog.write('Warning : ') + else: + flog.write('InChI creation error (%-d) : ' % inchi_result) + flog.write('%-s' % smessage ) + if inchi_result <=1 : + # no error or just warning + #print('%-s' % sinchi) + fout.write('%-s\n' % sinchi) + # ... may compute InChIKey + if calc_key==1: + if ikey_result!=0: + if (fl!=sys.stdout): + flog.write('RECORD %-d\t' % (num) ) + # special case: reversal of InChI failed + # InChIKey is computed, but the warning issued + if (ikey_result==11): + flog.write('Warning: InChI reversal gets mismatching InChI string\n') + fout.write('InChIKey=%-s\n' % sikey) + else: + flog.write('Key calculation error (%-d)\n' % ikey_result) + else: + #print('InChIKey=%-s' % sikey ) + fout.write('InChIKey=%-s\n' % sikey ) + if (calc_xkey==1): + print('XHash1=%-s' % sxtra1 ) + print('XHash1=%-s' % sxtra2 ) + fout.write('XHash1=%-s\n' % sxtra1 ) + fout.write('XHash2=%-s\n' % sxtra2 ) + if show_auxinfo: + fout.write('%-s\n' % saux ) + return + + +############################################################################## +def retire(code): + print ("Something went wrong...") + + if ( code==1 ): + print ("Invalid command line") + elif ( code==2 ): + pass + elif( code==3 ): + print ("Unsupported platform") + elif( code==4 ): + print ("Could not access InChI library") + elif( code==5 ): + print ("Could not open input file") + elif( code==6 ): + print ("Could not open output file") + elif( code==7 ): + print ("Could not open log file") + else: + pass + + sys.exit(code) + + + +############################################################################## +def cl_parser(): + parser = OptionParser(usage="""\ + Usage: %prog [options] + """) + parser.add_option('-i', '--input', + type='string', action='store', + help='name of input SD file (required)') + parser.add_option('-o', '--output', + type='string', action='store', + help='name of output file (default=inchi_out.txt)') + parser.add_option('-l', '--log', + type='string', action='store', + help='name of log file (errors/warnings; default=stdout)') + parser.add_option('-s', '--start_record', + type='int', action='store', + help='starting number of record to be converted') + parser.add_option('-e', '--end_record', + type='int', action='store', + help='number of the last record to be converted') + parser.add_option('-x', '--aux', dest='aux', + help='print auxilary info', default=False, action='store_true') + parser.add_option('-k', '--key', dest='key', + help='calculate InChIKey', default=False, action='store_true') + parser.add_option('-z', '--xtra', dest='xtra', + help='calculate extra hash complementing InChIKey', default=False, action='store_true') + parser.add_option('-p', '--inchi_options', + type='string', action='store', + help='string with InChI options') + opts, args = parser.parse_args() + if not opts.input: + parser.print_help() + retire(1) + else: + inname = opts.input + if opts.output: + outname = opts.output + else: + outname = "inchi_out.txt" + if opts.log: + logname = opts.log + else: + logname = "" + + inchi_options = "" + platform = what_platform() + if platform=='Windows': + inchi_options = "/W60" + else: + if platform=='Linux': + inchi_options = "-W60" + + if opts.inchi_options: + inchi_options = inchi_options + opts.inchi_options + if not opts.aux: + show_auxinfo = 0 + else: + show_auxinfo = 1 + if not opts.key: + calc_key = 0 + else: + calc_key = 1 + if not opts.xtra: + calc_xkey = 0 + else: + calc_xkey = 1 + if opts.start_record: + startr = opts.start_record + else: + startr = -1 + if opts.end_record: + endr = opts.end_record + else: + endr = -1 + return [inname, outname, logname, inchi_options, show_auxinfo, calc_key, calc_xkey, startr, endr] +############################################################################## +def open_files( inname, outname, logname ): + try: + if os.path.splitext(inname)[-1]==".gz": + f = gzip.open(inname) + else: + f = open(inname, "rb") + except: + return ( None, 5 ) + # + try: + fout = open(outname, 'w') + except: + return ( None, 6 ) + if not fout: + return ( None, 6 ) + # + try: + if ( logname==""): + flog = sys.stderr + else: + flog = open(logname, 'w') + except: + return ( None, 7 ) + if not flog: + return ( None, 7 ) + + return ( (f, fout, flog), 0) + + + +############################################################################## +def banner(): + print ("This Python demo program reads SD file and calls InChI library") + print ("(libinchi.dll/libinchi.so.1) function MakeINCHIFromMolfileText()") + print ("to generate InChI string (also generates InChIKey).") + print ("Note: the example is provided for illustrative purposes only.\n") + + +############################################################################## +def clean_and_make_list_of_records(text): + try: + text = text.replace("\r\n", "\n") + # Split string by '$$$$' + content = text.split("$$$$") + # Remove surplus empty record appearing after the last $4 (if any) + if ( len(content[-1])<2 ): # \n + content = content[:-1] + except: + retire(97) + # Clean up segments: remove surplus head linefeed in records 1-n start + # ( it appears as we split at '$$$$' not '$$$$\n' ) + def del_head_lf(s): + if s: + if s[0]=="\n": + s = s[1:] + return s + content = [content[0]] + [del_head_lf(item) for item in content[1:]] + return content + + +############################################################################## +# +# MAIN +# +############################################################################## + + +banner() + +parameters = cl_parser() +if not parameters: + retire(1) + +inname, outname, logname, inchi_options, show_auxinfo, calc_key, calc_xkey, startr, endr = parameters + +inchi_api = load_inchi_library("") +if ( not inchi_api ): + retire(4) + +print ("\nProcessing " + inname) + +files, ierr = open_files( inname, outname, logname ) +if not files: + retire(ierr) + +f, fout, flog = files + + +print ("\nReading, please wait..") +text = f.read() +print ("..done.") +text = text.decode('utf-8') + +content = clean_and_make_list_of_records(text) + +num = nerr = 0 +for record in content: + num = num + 1 + + if (num0): + if (num>endr): + break + + flog.write ( "\nRecord # %-ld (%-ld chars)" % (num, len(record)) ) + #print ('{' + record + '}') + + results = inchify_molfile( record, inchi_options, show_auxinfo, calc_key, calc_xkey, inchi_api ) + + show_record_results( results, fout, flog ) + + inchi_result, ikey_result, sinchi, slog, smessage, saux, sikey, sxtra1, sxtra2, ierr = results + nerr = nerr + ierr + +#print ( "\nProcessed %-ld records; had %-ld errors." % (num, nerr)) +flog.write ( "\nCOMPLETED\nProcessed %-ld records with %-ld errors\n" % (num, nerr)) diff --git a/INCHI-1-SRC/INCHI_API/demos/python_sample/readme.txt b/INCHI-1-SRC/INCHI_API/demos/python_sample/readme.txt new file mode 100644 index 0000000..2e074af --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/python_sample/readme.txt @@ -0,0 +1,54 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +This directory contains Python demo application illustrating how the InChI +Software Library functions (API) may be called from within Python. +It reads SD/MOL input file and produces InChI strings and, optionally, +generates InChIKey's. + +The program uses Python 3; tested with Python 3.4.2 64 bit under Windows 7 64-bit +and Python 3.4.0 under Ubuntu 14.04 LTS. + +It is assumed that under MS Windows the InChI library name is 'libinchi.dll' +while under Linux it is '/usr/lib/libinchi.so.1'. + +Note that this Python program is not indended to be a production InChI generator. +The example is provided for illustrative purposes only. + +For usage instructions, type "mol2inchi.py" without parameters. diff --git a/INCHI-1-SRC/INCHI_API/demos/python_sample/run_test.cmd b/INCHI-1-SRC/INCHI_API/demos/python_sample/run_test.cmd new file mode 100644 index 0000000..cb35bb5 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/python_sample/run_test.cmd @@ -0,0 +1,2 @@ +# Use Python 3 +python mol2inchi.py -i test1.sdf -k diff --git a/INCHI-1-SRC/INCHI_API/demos/python_sample/run_test.sh b/INCHI-1-SRC/INCHI_API/demos/python_sample/run_test.sh new file mode 100644 index 0000000..c3d5d93 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/python_sample/run_test.sh @@ -0,0 +1,2 @@ +# Use Python 3 +python3 mol2inchi.py -i test1.sdf -k >test1.out diff --git a/INCHI-1-SRC/INCHI_API/demos/python_sample/test1.sdf b/INCHI-1-SRC/INCHI_API/demos/python_sample/test1.sdf new file mode 100644 index 0000000..9a61192 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/python_sample/test1.sdf @@ -0,0 +1,9905 @@ +10000001 + -OEChem-11110908022D + + 38 41 0 0 0 0 0 0 0999 V2000 + 7.6555 1.1275 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3648 1.0344 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5215 -0.2547 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.6783 -1.5439 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 4.6783 -3.1534 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -1.8486 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -2.8486 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4226 -0.8761 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -1.8486 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2619 -2.3486 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2164 0.1025 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -2.8486 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3731 -1.1867 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.9607 0.7703 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2658 0.4131 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2619 -2.3486 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -1.3486 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1174 -0.5189 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.9112 0.4596 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0596 1.3916 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -3.3486 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1091 1.7023 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8039 2.0595 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9029 2.6808 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5977 3.0380 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.6472 3.3486 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.5009 -1.7934 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.8328 1.3770 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2619 -2.9686 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8819 -2.3486 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2619 -1.7286 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -0.7286 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7067 -0.7115 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -3.9686 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.3932 1.8669 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3136 2.8734 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0592 3.4520 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5193 3.9553 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 19 1 0 0 0 0 + 2 22 1 0 0 0 0 + 3 15 2 0 0 0 0 + 4 8 1 0 0 0 0 + 4 9 1 0 0 0 0 + 4 10 1 0 0 0 0 + 5 10 2 0 0 0 0 + 5 12 1 0 0 0 0 + 6 7 2 0 0 0 0 + 6 17 1 0 0 0 0 + 7 21 1 0 0 0 0 + 8 11 2 0 0 0 0 + 8 13 1 0 0 0 0 + 9 12 1 0 0 0 0 + 9 17 2 0 0 0 0 + 10 16 1 0 0 0 0 + 11 14 1 0 0 0 0 + 11 15 1 0 0 0 0 + 12 21 2 0 0 0 0 + 13 18 2 0 0 0 0 + 13 27 1 0 0 0 0 + 14 19 2 0 0 0 0 + 14 28 1 0 0 0 0 + 15 20 1 0 0 0 0 + 16 29 1 0 0 0 0 + 16 30 1 0 0 0 0 + 16 31 1 0 0 0 0 + 17 32 1 0 0 0 0 + 18 19 1 0 0 0 0 + 18 33 1 0 0 0 0 + 20 22 1 0 0 0 0 + 20 23 2 0 0 0 0 + 21 34 1 0 0 0 0 + 22 24 2 0 0 0 0 + 23 25 1 0 0 0 0 + 23 35 1 0 0 0 0 + 24 26 1 0 0 0 0 + 24 36 1 0 0 0 0 + 25 26 2 0 0 0 0 + 25 37 1 0 0 0 0 + 26 38 1 0 0 0 0 +M END +> +10000001 + +> +1 + +> +522 + +> +4 + +> +0 + +> +3 + +> +AAADccB7oAAGAAAAAAAAAAAAAAAAAWAAAAA8YIAAAAAAAFgB/AAAHgIIAAAADA7BnyQ/0LcMEACqA7Z3ZACShC0ll6AZmCEwdtiIaPrB39GUpYhoiALIzecYiMCOAABAIAADAAAAAIBAAAYAAAAAAAAAAA== + +> +[5-chloro-2-(2-methylimidazo[4,5-d]pyridazin-1-yl)phenyl]-(2-chlorophenyl)methanone + +> +[5-chloro-2-(2-methyl-1-imidazo[4,5-d]pyridazinyl)phenyl]-(2-chlorophenyl)methanone + +> +[5-chloro-2-(2-methylimidazo[4,5-d]pyridazin-1-yl)phenyl]-(2-chlorophenyl)methanone + +> +[5-chloro-2-(2-methylimidazo[4,5-d]pyridazin-1-yl)phenyl]-(2-chlorophenyl)methanone + +> +[5-chloro-2-(2-methylimidazo[4,5-d]pyridazin-1-yl)phenyl]-(2-chlorophenyl)methanone + +> +InChI=1S/C19H12Cl2N4O/c1-11-24-16-9-22-23-10-18(16)25(11)17-7-6-12(20)8-14(17)19(26)13-4-2-3-5-15(13)21/h2-10H,1H3 + +> +ANOUMYXLUIDQNL-UHFFFAOYSA-N + +> +4 + +> +382.038816 + +> +C19H12Cl2N4O + +> +383.23078 + +> +CC1=NC2=CN=NC=C2N1C3=C(C=C(C=C3)Cl)C(=O)C4=CC=CC=C4Cl + +> +CC1=NC2=CN=NC=C2N1C3=C(C=C(C=C3)Cl)C(=O)C4=CC=CC=C4Cl + +> +60.7 + +> +382.038816 + +> +0 + +> +26 + +> +0 + +> +0 + +> +0 + +> +0 + +> +0 + +> +1 + +> +1 + +> +1 +5 +255 + +> +11 14 8 +12 21 8 +13 18 8 +14 19 8 +18 19 8 +20 22 8 +20 23 8 +22 24 8 +23 25 8 +24 26 8 +25 26 8 +4 10 8 +4 9 8 +5 10 8 +5 12 8 +6 17 8 +6 7 8 +7 21 8 +8 11 8 +8 13 8 +9 12 8 +9 17 8 + +$$$$ +10000002 + -OEChem-11110908022D + + 39 42 0 1 0 0 0 0 0999 V2000 + 6.3301 4.0000 0.0000 Br 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 1.0000 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 0.5000 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -0.5000 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 6.9939 0.0000 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -4.0000 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 1.0000 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 4.5981 -1.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -0.5000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 0.5000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 2.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -1.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4103 0.8047 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4103 -0.8047 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -2.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 2.5000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 2.5000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 3.5000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 3.5000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -2.5000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -2.5000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 4.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -3.5000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -3.5000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0611 1.3100 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.1995 -1.4749 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9966 -1.4749 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6540 -0.4174 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.2554 -1.1077 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.6029 1.3941 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.6029 -1.3941 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0010 2.1900 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1951 2.1900 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1951 3.8100 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2690 -2.1900 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 -2.1900 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 4.6200 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2690 -3.8100 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 -3.8100 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 18 1 0 0 0 0 + 2 10 2 0 0 0 0 + 3 7 1 0 0 0 0 + 3 9 1 0 0 0 0 + 3 13 1 0 0 0 0 + 4 8 1 0 0 0 0 + 4 10 1 0 0 0 0 + 4 12 1 0 0 0 0 + 5 13 2 0 0 0 0 + 5 14 1 0 0 0 0 + 6 23 2 0 0 0 0 + 6 24 1 0 0 0 0 + 7 10 1 0 0 0 0 + 7 11 1 0 0 0 0 + 7 25 1 0 0 0 0 + 8 9 1 0 0 0 0 + 8 26 1 0 0 0 0 + 8 27 1 0 0 0 0 + 9 14 2 0 0 0 0 + 11 16 2 0 0 0 0 + 11 17 1 0 0 0 0 + 12 15 1 0 0 0 0 + 12 28 1 0 0 0 0 + 12 29 1 0 0 0 0 + 13 30 1 0 0 0 0 + 14 31 1 0 0 0 0 + 15 20 2 0 0 0 0 + 15 21 1 0 0 0 0 + 16 18 1 0 0 0 0 + 16 32 1 0 0 0 0 + 17 19 2 0 0 0 0 + 17 33 1 0 0 0 0 + 18 22 2 0 0 0 0 + 19 22 1 0 0 0 0 + 19 34 1 0 0 0 0 + 20 23 1 0 0 0 0 + 20 35 1 0 0 0 0 + 21 24 2 0 0 0 0 + 21 36 1 0 0 0 0 + 22 37 1 0 0 0 0 + 23 38 1 0 0 0 0 + 24 39 1 0 0 0 0 +M END +> +10000002 + +> +1 + +> +456 + +> +3 + +> +0 + +> +3 + +> +AAADccB7oAAAEAAAAAAAAAAAAAAAAWAAAAA8WIAAAAAAAFgB8AAAHgBAAAABrCjBngQ9kJcMEACoAzV3dACCgC0xEqQJ2CG4dLiIaHLA3bGUIAhohgLIyjcYiICOAAAAEAAAAAAAAAAgAAAAAAAAAAAAAA== + +> +5-(3-bromophenyl)-7-(4-pyridylmethyl)-5,8-dihydroimidazo[1,5-a]pyrazin-6-one + +> +5-(3-bromophenyl)-7-(4-pyridylmethyl)-5,8-dihydroimidazo[1,5-a]pyrazin-6-one + +> +5-(3-bromophenyl)-7-(pyridin-4-ylmethyl)-5,8-dihydroimidazo[1,5-a]pyrazin-6-one + +> +5-(3-bromophenyl)-7-(pyridin-4-ylmethyl)-5,8-dihydroimidazo[1,5-a]pyrazin-6-one + +> +5-(3-bromophenyl)-7-(4-pyridylmethyl)-5,8-dihydroimidazo[1,5-a]pyrazin-6-one + +> +InChI=1S/C18H15BrN4O/c19-15-3-1-2-14(8-15)17-18(24)22(10-13-4-6-20-7-5-13)11-16-9-21-12-23(16)17/h1-9,12,17H,10-11H2 + +> +DUZSCKPDZXTNHU-UHFFFAOYSA-N + +> +2.1 + +> +382.042924 + +> +C18H15BrN4O + +> +383.2419 + +> +C1C2=CN=CN2C(C(=O)N1CC3=CC=NC=C3)C4=CC(=CC=C4)Br + +> +C1C2=CN=CN2C(C(=O)N1CC3=CC=NC=C3)C4=CC(=CC=C4)Br + +> +51 + +> +382.042924 + +> +0 + +> +24 + +> +0 + +> +1 + +> +0 + +> +0 + +> +0 + +> +1 + +> +1 + +> +1 +5 +255 + +> +11 16 8 +11 17 8 +15 20 8 +15 21 8 +16 18 8 +17 19 8 +18 22 8 +19 22 8 +20 23 8 +21 24 8 +3 13 8 +3 9 8 +5 13 8 +5 14 8 +6 23 8 +6 24 8 +7 11 3 +9 14 8 + +$$$$ +10000003 + -OEChem-11110908022D + + 40 42 0 1 0 0 0 0 0999 V2000 + 2.0000 -2.9239 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -3.9239 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2437 0.1693 0.0000 S 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9538 3.9239 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 1.5761 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 0.0761 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4347 1.5706 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 1.5761 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 2.0570 2.1639 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 0.5761 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.6750 2.1639 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3660 3.1149 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 0.0761 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 3.3660 3.1149 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -0.9239 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 0.5761 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -1.4239 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -1.4239 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -2.4239 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -2.4239 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -2.9239 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 0.5761 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.9128 0.9125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4128 1.7785 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.6234 1.1902 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4906 2.4160 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.7470 1.6269 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.2554 0.6837 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6540 -0.0065 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9850 1.6269 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2414 2.4160 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4308 3.7315 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.7596 3.2438 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 0.6961 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3291 -1.1139 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1350 -1.1139 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -0.5439 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1350 -2.7339 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5294 0.8477 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.6650 2.3449 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 19 1 0 0 0 0 + 2 21 1 0 0 0 0 + 3 22 1 0 0 0 0 + 3 23 1 0 0 0 0 + 4 14 2 0 0 0 0 + 5 16 2 0 0 0 0 + 6 16 1 0 0 0 0 + 6 22 1 0 0 0 0 + 6 37 1 0 0 0 0 + 7 22 2 0 0 0 0 + 7 24 1 0 0 0 0 + 8 9 1 0 0 0 0 + 8 10 1 0 0 0 0 + 8 11 1 0 0 0 0 + 8 25 1 0 0 0 0 + 9 12 1 0 0 0 0 + 9 26 1 0 0 0 0 + 9 27 1 0 0 0 0 + 10 13 1 0 0 0 0 + 10 28 1 0 0 0 0 + 10 29 1 0 0 0 0 + 11 14 1 0 0 0 0 + 11 30 1 0 0 0 0 + 11 31 1 0 0 0 0 + 12 14 1 0 0 0 0 + 12 32 1 0 0 0 0 + 12 33 1 0 0 0 0 + 13 15 1 0 0 0 0 + 13 16 1 0 0 0 0 + 13 34 1 0 0 0 0 + 15 17 2 0 0 0 0 + 15 18 1 0 0 0 0 + 17 19 1 0 0 0 0 + 17 35 1 0 0 0 0 + 18 20 2 0 0 0 0 + 18 36 1 0 0 0 0 + 19 21 2 0 0 0 0 + 20 21 1 0 0 0 0 + 20 38 1 0 0 0 0 + 23 24 2 0 0 0 0 + 23 39 1 0 0 0 0 + 24 40 1 0 0 0 0 +M END +> +10000003 + +> +1 + +> +480 + +> +3 + +> +1 + +> +5 + +> +AAADceB7MABGAAAAAAAAAAAAAAAAAeIAAAAwAAAAAAAAAAABwAAAHgYQAAAADQbF2iS9kZLIEAisArF3dAAC8KlxDzlJyk0YIoiIIDKBnxGEIAAokQIIiCcYiMCOhAAAIAAAAAQIAABAAAAACAAAAAAAAA== + +> +2-(3,4-dichlorophenyl)-3-(3-oxocyclopentyl)-N-thiazol-2-yl-propanamide + +> +2-(3,4-dichlorophenyl)-3-(3-oxocyclopentyl)-N-(2-thiazolyl)propanamide + +> +2-(3,4-dichlorophenyl)-3-(3-oxocyclopentyl)-N-(1,3-thiazol-2-yl)propanamide + +> +2-(3,4-dichlorophenyl)-3-(3-oxocyclopentyl)-N-(1,3-thiazol-2-yl)propanamide + +> +2-(3,4-dichlorophenyl)-3-(3-ketocyclopentyl)-N-thiazol-2-yl-propionamide + +> +InChI=1S/C17H16Cl2N2O2S/c18-14-4-2-11(9-15(14)19)13(8-10-1-3-12(22)7-10)16(23)21-17-20-5-6-24-17/h2,4-6,9-10,13H,1,3,7-8H2,(H,20,21,23) + +> +IICNFYZYAVVBJL-UHFFFAOYSA-N + +> +3.8 + +> +382.030954 + +> +C17H16Cl2N2O2S + +> +383.29214 + +> +C1CC(=O)CC1CC(C2=CC(=C(C=C2)Cl)Cl)C(=O)NC3=NC=CS3 + +> +C1CC(=O)CC1CC(C2=CC(=C(C=C2)Cl)Cl)C(=O)NC3=NC=CS3 + +> +59.1 + +> +382.030954 + +> +0 + +> +24 + +> +0 + +> +2 + +> +0 + +> +0 + +> +0 + +> +1 + +> +9 + +> +1 +5 +255 + +> +13 15 3 +15 17 8 +15 18 8 +17 19 8 +18 20 8 +19 21 8 +20 21 8 +23 24 8 +3 22 8 +3 23 8 +7 22 8 +7 24 8 +8 25 3 + +$$$$ +10000004 + -OEChem-11110908022D + + 30 28 0 1 0 0 0 0 0999 V2000 + 0.5883 0.5878 0.0000 Pt 0 2 0 0 0 0 0 0 0 0 0 0 + 0.8973 1.5388 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 + 1.3973 0.0000 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 + 3.1573 0.2788 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2.2690 7.6304 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 0.5369 6.6304 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 2.2690 6.6304 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 1.4030 6.1304 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 3.1350 6.1304 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4030 5.1304 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1350 5.1304 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.2690 4.6304 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.8973 1.5388 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.2063 0.5878 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.7321 6.9404 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.8660 5.8204 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7456 6.0227 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.3471 6.7130 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7924 5.2381 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.1909 4.5478 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.3471 4.5478 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7456 5.2381 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.8705 4.1554 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6675 4.1554 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.7321 7.9404 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8059 7.9404 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.5369 7.2504 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 6.3204 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5037 1.6677 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.8325 2.1554 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2 13 1 0 0 0 0 + 3 14 1 0 0 0 0 + 4 14 2 0 0 0 0 + 7 5 1 6 0 0 0 + 5 25 1 0 0 0 0 + 5 26 1 0 0 0 0 + 8 6 1 6 0 0 0 + 6 27 1 0 0 0 0 + 6 28 1 0 0 0 0 + 7 8 1 0 0 0 0 + 7 9 1 0 0 0 0 + 7 15 1 0 0 0 0 + 8 10 1 0 0 0 0 + 8 16 1 0 0 0 0 + 9 11 1 0 0 0 0 + 9 17 1 0 0 0 0 + 9 18 1 0 0 0 0 + 10 12 1 0 0 0 0 + 10 19 1 0 0 0 0 + 10 20 1 0 0 0 0 + 11 12 1 0 0 0 0 + 11 21 1 0 0 0 0 + 11 22 1 0 0 0 0 + 12 23 1 0 0 0 0 + 12 24 1 0 0 0 0 + 13 14 1 0 0 0 0 + 13 29 1 0 0 0 0 + 13 30 1 0 0 0 0 +M CHG 3 1 2 2 -1 3 -1 +M END +> +10000004 + +> +1 + +> +97.6 + +> +5 + +> +2 + +> +0 + +> +AAADceBzMAAAAAAAAAAABAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAHgAQAAAACCjhgAYACABAAAAIAACQCAIAAAAAAAAAAIBAAAABQAIAgAAAAAAAEAAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAA== + +> +(1R,2S)-cyclohexane-1,2-diamine; 2-oxidoacetate; platinum(2+) + +> +(1R,2S)-cyclohexane-1,2-diamine; 2-oxidoacetate; platinum(2+) + +> +(1R,2S)-cyclohexane-1,2-diamine; 2-oxidoacetate; platinum(2+) + +> +(1R,2S)-cyclohexane-1,2-diamine; 2-oxidanidylethanoate; platinum(2+) + +> +[(1R,2S)-2-aminocyclohexyl]amine; 2-oxidoacetate; platinum(2+) + +> +InChI=1S/C6H14N2.C2H3O3.Pt/c7-5-3-1-2-4-6(5)8;3-1-2(4)5;/h5-6H,1-4,7-8H2;1H2,(H,4,5);/q;-1;+2/p-1/t5-,6+;; + +> +KTDZYOFKSFAAAN-RUTFAPCESA-M + +> +383.080866 + +> +C8H16N2O3Pt + +> +383.30224 + +> +C1CCC(C(C1)N)N.C(C(=O)[O-])[O-].[Pt+2] + +> +C1CC[C@@H]([C@@H](C1)N)N.C(C(=O)[O-])[O-].[Pt+2] + +> +115 + +> +383.080866 + +> +0 + +> +14 + +> +2 + +> +0 + +> +0 + +> +0 + +> +0 + +> +3 + +> +1 + +> +1 +5 +255 + +> +7 5 6 +8 6 6 + +$$$$ +10000005 + -OEChem-11110908022D + + 49 52 0 0 0 0 0 0 0999 V2000 + 2.0002 -0.6914 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7320 -3.6916 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5984 0.8083 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7323 0.3084 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3304 0.8082 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4643 -0.6917 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3305 1.8082 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0216 3.6916 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.1854 2.2433 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8578 2.2433 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3305 2.8535 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.3486 1.7605 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2362 1.3314 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.1854 3.2088 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8578 3.2088 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0216 1.9405 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4644 0.3083 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5982 -1.1917 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3303 -1.1918 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7322 -0.6916 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -2.1917 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8662 -1.1915 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -2.6916 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8661 -2.1915 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -2.6914 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0216 4.3116 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2398 1.6257 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4502 2.0606 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.7371 2.6740 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.9869 3.3695 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1554 1.1714 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8674 2.1514 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7613 1.0018 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.9268 0.7941 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5748 3.1012 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.9733 3.7914 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0698 3.7914 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4684 3.1012 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3916 1.4430 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.6516 1.4430 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8673 0.4981 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0202 -1.7287 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8672 -1.5019 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.6403 -0.6549 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1350 -2.5017 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1954 0.6185 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.6900 -2.1545 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4630 -3.0014 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3100 -3.2284 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 22 1 0 0 0 0 + 2 23 1 0 0 0 0 + 3 17 2 0 0 0 0 + 4 20 1 0 0 0 0 + 4 46 1 0 0 0 0 + 5 7 1 0 0 0 0 + 5 17 1 0 0 0 0 + 5 41 1 0 0 0 0 + 6 17 1 0 0 0 0 + 6 18 1 0 0 0 0 + 6 19 1 0 0 0 0 + 7 11 1 0 0 0 0 + 7 12 1 0 0 0 0 + 7 13 1 0 0 0 0 + 8 11 1 0 0 0 0 + 8 14 1 0 0 0 0 + 8 15 1 0 0 0 0 + 8 26 1 0 0 0 0 + 9 12 1 0 0 0 0 + 9 14 1 0 0 0 0 + 9 16 1 0 0 0 0 + 9 27 1 0 0 0 0 + 10 13 1 0 0 0 0 + 10 15 1 0 0 0 0 + 10 16 1 0 0 0 0 + 10 28 1 0 0 0 0 + 11 29 1 0 0 0 0 + 11 30 1 0 0 0 0 + 12 31 1 0 0 0 0 + 12 32 1 0 0 0 0 + 13 33 1 0 0 0 0 + 13 34 1 0 0 0 0 + 14 35 1 0 0 0 0 + 14 36 1 0 0 0 0 + 15 37 1 0 0 0 0 + 15 38 1 0 0 0 0 + 16 39 1 0 0 0 0 + 16 40 1 0 0 0 0 + 18 20 1 0 0 0 0 + 18 21 2 0 0 0 0 + 19 42 1 0 0 0 0 + 19 43 1 0 0 0 0 + 19 44 1 0 0 0 0 + 20 22 2 0 0 0 0 + 21 23 1 0 0 0 0 + 21 45 1 0 0 0 0 + 22 24 1 0 0 0 0 + 23 24 2 0 0 0 0 + 24 25 1 0 0 0 0 + 25 47 1 0 0 0 0 + 25 48 1 0 0 0 0 + 25 49 1 0 0 0 0 +M END +> +10000005 + +> +1 + +> +502 + +> +2 + +> +2 + +> +2 + +> +AAADceB7MAAGAAAAAAAAAAAAAAAAAAAAAAAwYMGDAAAAAAABAAAAHgIQCAAADY6BmCQzxoPAAgCIAiRCUACCAAAhJ0AAiAANb4iKJiLTkpOEcAhs1hNY2CeQwNAOCCABAAKBGAAQQAaABQIwAAAAAAAAAA== + +> +3-(1-adamantyl)-1-(3,5-dichloro-2-hydroxy-4-methyl-phenyl)-1-methyl-urea + +> +3-(1-adamantyl)-1-(3,5-dichloro-2-hydroxy-4-methylphenyl)-1-methylurea + +> +3-(1-adamantyl)-1-(3,5-dichloro-2-hydroxy-4-methylphenyl)-1-methylurea + +> +3-(1-adamantyl)-1-(3,5-dichloro-2-hydroxy-4-methyl-phenyl)-1-methyl-urea + +> +3-(1-adamantyl)-1-(3,5-dichloro-2-hydroxy-4-methyl-phenyl)-1-methyl-urea + +> +InChI=1S/C19H24Cl2N2O2/c1-10-14(20)6-15(17(24)16(10)21)23(2)18(25)22-19-7-11-3-12(8-19)5-13(4-11)9-19/h6,11-13,24H,3-5,7-9H2,1-2H3,(H,22,25) + +> +BVFIQFLXSWARKY-UHFFFAOYSA-N + +> +5 + +> +382.121483 + +> +C19H24Cl2N2O2 + +> +383.31206 + +> +CC1=C(C=C(C(=C1Cl)O)N(C)C(=O)NC23CC4CC(C2)CC(C4)C3)Cl + +> +CC1=C(C=C(C(=C1Cl)O)N(C)C(=O)NC23CC4CC(C2)CC(C4)C3)Cl + +> +52.6 + +> +382.121483 + +> +0 + +> +25 + +> +0 + +> +0 + +> +0 + +> +0 + +> +0 + +> +1 + +> +12 + +> +1 +5 +255 + +> +18 20 8 +18 21 8 +20 22 8 +21 23 8 +22 24 8 +23 24 8 + +$$$$ +10000006 + -OEChem-11110908022D + + 46 50 0 1 0 0 0 0 0999 V2000 + 8.3942 3.8290 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3423 -3.8290 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3977 0.2418 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1389 -0.7242 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8291 1.9832 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5657 1.0006 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 5.3069 0.0347 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 3.0749 -0.0993 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 3.5749 -0.9653 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 2.1031 -0.3085 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9156 2.3899 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9337 2.9777 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9078 -1.7023 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2728 1.7077 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5658 -0.9312 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -1.2972 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0140 2.6736 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2388 1.4489 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.5317 -1.1901 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8586 -1.6384 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7211 3.3807 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.9459 2.1560 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7905 -2.1560 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1175 -2.6043 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.6871 3.1219 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0834 -2.8631 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6749 0.5935 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9749 -1.6582 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0370 0.3080 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4831 -0.3094 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.1678 2.9563 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.3492 2.6421 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.6634 1.8235 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5503 2.9129 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9985 3.5943 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.3171 3.0425 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.0356 -2.3090 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 -1.6072 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4152 2.8341 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3992 0.8500 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.9701 -0.7517 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2598 -1.4779 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.5607 3.9796 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5448 1.9955 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3894 -2.3165 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.6791 -3.0427 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 25 1 0 0 0 0 + 2 26 1 0 0 0 0 + 3 4 2 0 0 0 0 + 3 6 1 0 0 0 0 + 4 7 1 0 0 0 0 + 5 6 1 0 0 0 0 + 5 7 1 0 0 0 0 + 5 11 1 0 0 0 0 + 5 12 1 0 0 0 0 + 6 8 1 0 0 0 0 + 6 14 1 6 0 0 0 + 7 9 1 0 0 0 0 + 7 15 1 1 0 0 0 + 8 9 1 0 0 0 0 + 8 10 1 0 0 0 0 + 8 27 1 1 0 0 0 + 9 13 1 0 0 0 0 + 9 28 1 1 0 0 0 + 10 16 1 0 0 0 0 + 10 29 1 0 0 0 0 + 10 30 1 0 0 0 0 + 11 31 1 0 0 0 0 + 11 32 1 0 0 0 0 + 11 33 1 0 0 0 0 + 12 34 1 0 0 0 0 + 12 35 1 0 0 0 0 + 12 36 1 0 0 0 0 + 13 16 2 0 0 0 0 + 13 37 1 0 0 0 0 + 14 17 2 0 0 0 0 + 14 18 1 0 0 0 0 + 15 19 2 0 0 0 0 + 15 20 1 0 0 0 0 + 16 38 1 0 0 0 0 + 17 21 1 0 0 0 0 + 17 39 1 0 0 0 0 + 18 22 2 0 0 0 0 + 18 40 1 0 0 0 0 + 19 23 1 0 0 0 0 + 19 41 1 0 0 0 0 + 20 24 2 0 0 0 0 + 20 42 1 0 0 0 0 + 21 25 2 0 0 0 0 + 21 43 1 0 0 0 0 + 22 25 1 0 0 0 0 + 22 44 1 0 0 0 0 + 23 26 2 0 0 0 0 + 23 45 1 0 0 0 0 + 24 26 1 0 0 0 0 + 24 46 1 0 0 0 0 +M END +> +10000006 + +> +1 + +> +628 + +> +2 + +> +0 + +> +2 + +> +AAADceB7AAAGAAAAAAAAAAAAAAAAAZIEAAAwYIAAAAARAAABQAAAHAIIAAAAD4qBGCAyAIAAAACCAiBCAACCAAAgBQAIiAAABogIICKB0xGAIABggAAIiAcQgMAPhAAAAAAAAAAIAAAAAAAAASACCQAAAA== + +> +InChI=1S/C22H20Cl2N2/c1-20(2)21(14-6-10-16(23)11-7-14)18-4-3-5-19(18)22(20,26-25-21)15-8-12-17(24)13-9-15/h3-4,6-13,18-19H,5H2,1-2H3/t18-,19+,21-,22+/m1/s1 + +> +VAKKVZTZVIGLRB-KIZRIRGWSA-N + +> +6.5 + +> +382.100354 + +> +C22H20Cl2N2 + +> +383.3136 + +> +CC1(C2(C3CC=CC3C1(N=N2)C4=CC=C(C=C4)Cl)C5=CC=C(C=C5)Cl)C + +> +CC1([C@@]2([C@H]3CC=C[C@H]3[C@]1(N=N2)C4=CC=C(C=C4)Cl)C5=CC=C(C=C5)Cl)C + +> +24.7 + +> +382.100354 + +> +0 + +> +26 + +> +4 + +> +0 + +> +0 + +> +0 + +> +0 + +> +1 + +> +1 + +> +1 +5 +255 + +> +14 17 8 +14 18 8 +15 19 8 +15 20 8 +17 21 8 +18 22 8 +19 23 8 +20 24 8 +21 25 8 +22 25 8 +23 26 8 +24 26 8 +6 14 6 +7 15 5 +8 27 5 +9 28 5 + +$$$$ +10000007 + -OEChem-11110908022D + + 40 43 0 0 0 0 0 0 0999 V2000 + 2.3660 -0.4683 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -1.8343 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 + 3.3660 -2.2003 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9282 0.1657 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 -1.3343 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 1.6657 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0622 1.6657 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 12.4202 0.1310 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 0.1657 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 0.6657 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -0.8343 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 0.6657 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -0.8343 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.5263 1.6657 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 0.1657 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.5263 0.6657 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.7942 0.6657 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -1.3343 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 0.1657 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0622 0.6657 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6603 0.1657 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6603 2.1657 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.7942 1.6657 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.4202 2.2003 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -1.3343 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 2.1657 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.3263 1.6865 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.3263 0.6449 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 1.2857 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 -0.4543 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -1.9543 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1951 0.4757 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6603 -0.4543 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.7932 1.9757 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6603 2.7857 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2573 1.9757 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 12.4131 2.8203 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 2.7857 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 13.8620 1.9986 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 13.8620 0.3328 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 25 1 0 0 0 0 + 2 25 1 0 0 0 0 + 3 25 1 0 0 0 0 + 4 17 1 0 0 0 0 + 4 20 1 0 0 0 0 + 5 11 2 0 0 0 0 + 6 10 1 0 0 0 0 + 6 26 1 0 0 0 0 + 6 34 1 0 0 0 0 + 7 20 1 0 0 0 0 + 7 26 2 0 0 0 0 + 8 16 1 0 0 0 0 + 8 28 2 0 0 0 0 + 9 10 2 0 0 0 0 + 9 11 1 0 0 0 0 + 9 12 1 0 0 0 0 + 10 15 1 0 0 0 0 + 11 18 1 0 0 0 0 + 12 19 2 0 0 0 0 + 12 29 1 0 0 0 0 + 13 18 2 0 0 0 0 + 13 19 1 0 0 0 0 + 13 25 1 0 0 0 0 + 14 16 2 0 0 0 0 + 14 22 1 0 0 0 0 + 14 24 1 0 0 0 0 + 15 20 2 0 0 0 0 + 15 30 1 0 0 0 0 + 16 21 1 0 0 0 0 + 17 21 2 0 0 0 0 + 17 23 1 0 0 0 0 + 18 31 1 0 0 0 0 + 19 32 1 0 0 0 0 + 21 33 1 0 0 0 0 + 22 23 2 0 0 0 0 + 22 35 1 0 0 0 0 + 23 36 1 0 0 0 0 + 24 27 2 0 0 0 0 + 24 37 1 0 0 0 0 + 26 38 1 0 0 0 0 + 27 28 1 0 0 0 0 + 27 39 1 0 0 0 0 + 28 40 1 0 0 0 0 +M END +> +10000007 + +> +1 + +> +808 + +> +8 + +> +1 + +> +2 + +> +AAADccB7MYAAAAAAAAAAAAAAAAAAAAAAAAA+QIEAAAAAAACx8AAAHwAQAAAADAzBnhQ9xvLIFACoA7R3ZACCiCExIiAJ2KE+bNgMJuLEuZuMMChm0BnI6UeQ0KMOIAAAAgACAABAAAAEAAQAAAAAAAAAAA== + +> +(6E)-6-[4-(7-quinolyloxy)-1H-pyrimidin-6-ylidene]-3-(trifluoromethyl)cyclohexa-2,4-dien-1-one + +> +(6E)-6-[4-(7-quinolyloxy)-1H-pyrimidin-6-ylidene]-3-(trifluoromethyl)-1-cyclohexa-2,4-dienone + +> +(6E)-6-(4-quinolin-7-yloxy-1H-pyrimidin-6-ylidene)-3-(trifluoromethyl)cyclohexa-2,4-dien-1-one + +> +(6E)-6-(4-quinolin-7-yloxy-1H-pyrimidin-6-ylidene)-3-(trifluoromethyl)cyclohexa-2,4-dien-1-one + +> +(6E)-6-[4-(7-quinolyloxy)-1H-pyrimidin-6-ylidene]-3-(trifluoromethyl)cyclohexa-2,4-dien-1-one + +> +InChI=1S/C20H12F3N3O2/c21-20(22,23)13-4-6-15(18(27)8-13)17-10-19(26-11-25-17)28-14-5-3-12-2-1-7-24-16(12)9-14/h1-11H,(H,25,26)/b17-15+ + +> +OQBKDZLBCZODKE-BMRADRMJSA-N + +> +3.2 + +> +383.088161 + +> +C20H12F3N3O2 + +> +383.32339 + +> +C1=CC2=C(C=C(C=C2)OC3=CC(=C4C=CC(=CC4=O)C(F)(F)F)NC=N3)N=C1 + +> +C1=CC2=C(C=C(C=C2)OC3=C/C(=C\4/C=CC(=CC4=O)C(F)(F)F)/NC=N3)N=C1 + +> +63.6 + +> +383.088161 + +> +0 + +> +28 + +> +0 + +> +0 + +> +1 + +> +0 + +> +0 + +> +1 + +> +4 + +> +1 +5 +255 + +> +14 16 8 +14 22 8 +14 24 8 +16 21 8 +17 21 8 +17 23 8 +22 23 8 +24 27 8 +27 28 8 +8 16 8 +8 28 8 + +$$$$ +10000008 + -OEChem-11110908022D + + 47 50 0 0 0 0 0 0 0999 V2000 + 8.1912 0.8263 0.0000 Br 0 0 0 0 0 0 0 0 0 0 0 0 + 5.3194 -2.0343 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0506 2.0362 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5104 -1.4465 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8194 -0.4954 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0098 -2.9789 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.1284 -1.4465 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.8194 -0.4954 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7077 -2.0329 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2316 0.3136 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0157 -2.9780 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.6384 1.2271 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1439 -1.6787 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.5044 0.2893 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6981 -1.8029 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.3355 -3.7587 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.0561 1.9316 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.4573 2.9497 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8468 -0.9100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5249 0.0806 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -2.5676 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3208 -3.5521 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4683 2.7406 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.8695 3.7587 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9441 -3.5954 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6160 -3.1090 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.8010 -0.1324 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7176 0.6603 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0691 1.6731 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1524 0.8804 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3286 -2.2706 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3060 0.8767 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5128 -1.2112 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5344 -4.3459 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.2270 1.3356 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4988 1.6598 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8880 3.3957 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9713 2.6030 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4527 -1.0413 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.3939 -2.4369 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.9081 -4.0148 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.9667 2.3762 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.1039 3.2422 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9699 3.1051 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.3711 4.1231 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5051 4.2603 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.3680 3.3943 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 20 1 0 0 0 0 + 2 4 1 0 0 0 0 + 2 6 1 0 0 0 0 + 2 7 1 0 0 0 0 + 3 12 1 0 0 0 0 + 3 17 1 0 0 0 0 + 3 18 1 0 0 0 0 + 4 5 2 0 0 0 0 + 4 9 1 0 0 0 0 + 5 8 1 0 0 0 0 + 5 10 1 0 0 0 0 + 6 11 1 0 0 0 0 + 6 25 1 0 0 0 0 + 6 26 1 0 0 0 0 + 7 8 2 0 0 0 0 + 7 13 1 0 0 0 0 + 8 14 1 0 0 0 0 + 9 11 1 0 0 0 0 + 9 15 2 0 0 0 0 + 10 12 1 0 0 0 0 + 10 27 1 0 0 0 0 + 10 28 1 0 0 0 0 + 11 16 2 0 0 0 0 + 12 29 1 0 0 0 0 + 12 30 1 0 0 0 0 + 13 19 2 0 0 0 0 + 13 31 1 0 0 0 0 + 14 20 2 0 0 0 0 + 14 32 1 0 0 0 0 + 15 21 1 0 0 0 0 + 15 33 1 0 0 0 0 + 16 22 1 0 0 0 0 + 16 34 1 0 0 0 0 + 17 23 1 0 0 0 0 + 17 35 1 0 0 0 0 + 17 36 1 0 0 0 0 + 18 24 1 0 0 0 0 + 18 37 1 0 0 0 0 + 18 38 1 0 0 0 0 + 19 20 1 0 0 0 0 + 19 39 1 0 0 0 0 + 21 22 2 0 0 0 0 + 21 40 1 0 0 0 0 + 22 41 1 0 0 0 0 + 23 42 1 0 0 0 0 + 23 43 1 0 0 0 0 + 23 44 1 0 0 0 0 + 24 45 1 0 0 0 0 + 24 46 1 0 0 0 0 + 24 47 1 0 0 0 0 +M END +> +10000008 + +> +1 + +> +424 + +> +1 + +> +0 + +> +5 + +> +AAADceB7AAAAEAAAAAAAAAAAAAAAAWLAAAAwYAAAAAAWAFgB9AAAHABAAAABrAjBHgQywPMMEACgAyRiRACCgCAhAiQI2CA4ZLgIIOLAkZGEIAhggADIyhcQgMAOgAAgEAASAAAAAEAgACQAAAAAAAAAAA== + +> +2-(2-bromo-6H-isoindolo[2,1-a]indol-11-yl)-N,N-diethyl-ethanamine + +> +2-(2-bromo-6H-isoindolo[2,1-a]indol-11-yl)-N,N-diethylethanamine + +> +2-(2-bromo-6H-isoindolo[2,1-a]indol-11-yl)-N,N-diethylethanamine + +> +2-(2-bromo-6H-isoindolo[2,1-a]indol-11-yl)-N,N-diethyl-ethanamine + +> +2-(2-bromo-6H-isoindolo[2,1-a]indol-11-yl)ethyl-diethyl-amine + +> +InChI=1S/C21H23BrN2/c1-3-23(4-2)12-11-18-19-13-16(22)9-10-20(19)24-14-15-7-5-6-8-17(15)21(18)24/h5-10,13H,3-4,11-12,14H2,1-2H3 + +> +VHWYCPMXAANJRU-UHFFFAOYSA-N + +> +5.1 + +> +382.104461 + +> +C21H23BrN2 + +> +383.32472 + +> +CCN(CC)CCC1=C2C3=CC=CC=C3CN2C4=C1C=C(C=C4)Br + +> +CCN(CC)CCC1=C2C3=CC=CC=C3CN2C4=C1C=C(C=C4)Br + +> +8.2 + +> +382.104461 + +> +0 + +> +24 + +> +0 + +> +0 + +> +0 + +> +0 + +> +0 + +> +1 + +> +1 + +> +1 +5 +255 + +> +11 16 8 +13 19 8 +14 20 8 +15 21 8 +16 22 8 +19 20 8 +2 4 8 +2 7 8 +21 22 8 +4 5 8 +5 8 8 +7 13 8 +7 8 8 +8 14 8 +9 11 8 +9 15 8 + +$$$$ +10000009 + -OEChem-11110908022D + + 51 52 0 1 0 0 0 0 0999 V2000 + 6.0010 0.8100 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8671 -0.6900 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2690 -1.1900 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.4651 0.8100 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.4651 -1.1900 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2690 1.8100 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5369 0.8100 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5369 -1.1900 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0010 -2.1900 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8671 -2.6900 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.4651 1.8100 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7331 1.8100 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7331 0.8100 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 5.1350 0.3100 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 6.8671 0.3100 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 8.5991 0.3100 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 8.5991 -0.6900 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 4.2690 0.8100 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 5.1350 -0.6900 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 7.7331 -1.1900 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 3.4030 0.3100 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 3.4030 -0.6900 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 6.0010 -1.1900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7331 -2.1900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5991 2.3100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5991 3.3100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.2700 1.1200 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1350 0.9300 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 0.0000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5991 0.9300 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5991 -1.3100 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8059 1.1200 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1350 -1.3100 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 -1.5000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.4030 0.9300 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -0.3800 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 2.1200 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.6116 -1.2977 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2131 -0.6074 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.9451 -2.7726 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.3437 -2.0823 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.0021 0.5000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.4651 -1.8100 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 2.1200 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 0.5000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5369 -1.8100 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -2.5000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8671 -3.3100 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2191 3.3100 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5991 3.9300 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.9791 3.3100 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 14 1 1 6 0 0 0 + 15 1 1 1 0 0 0 + 2 15 1 0 0 0 0 + 2 20 1 0 0 0 0 + 3 19 1 0 0 0 0 + 3 22 1 0 0 0 0 + 16 4 1 1 0 0 0 + 4 42 1 0 0 0 0 + 17 5 1 1 0 0 0 + 5 43 1 0 0 0 0 + 18 6 1 1 0 0 0 + 6 44 1 0 0 0 0 + 21 7 1 6 0 0 0 + 7 45 1 0 0 0 0 + 8 22 1 0 0 0 0 + 8 46 1 0 0 0 0 + 9 23 1 0 0 0 0 + 9 47 1 0 0 0 0 + 10 24 1 0 0 0 0 + 10 48 1 0 0 0 0 + 11 25 2 0 0 0 0 + 13 12 1 6 0 0 0 + 12 25 1 0 0 0 0 + 12 37 1 0 0 0 0 + 13 15 1 0 0 0 0 + 13 16 1 0 0 0 0 + 13 27 1 0 0 0 0 + 14 18 1 0 0 0 0 + 14 19 1 0 0 0 0 + 14 28 1 0 0 0 0 + 15 29 1 0 0 0 0 + 16 17 1 0 0 0 0 + 16 30 1 0 0 0 0 + 17 20 1 0 0 0 0 + 17 31 1 0 0 0 0 + 18 21 1 0 0 0 0 + 18 32 1 0 0 0 0 + 19 23 1 1 0 0 0 + 19 33 1 0 0 0 0 + 20 24 1 1 0 0 0 + 20 34 1 0 0 0 0 + 21 22 1 0 0 0 0 + 21 35 1 0 0 0 0 + 22 36 1 0 0 0 0 + 23 38 1 0 0 0 0 + 23 39 1 0 0 0 0 + 24 40 1 0 0 0 0 + 24 41 1 0 0 0 0 + 25 26 1 0 0 0 0 + 26 49 1 0 0 0 0 + 26 50 1 0 0 0 0 + 26 51 1 0 0 0 0 +M END +> +10000009 + +> +1 + +> +480 + +> +11 + +> +8 + +> +5 + +> +AAADceByPAAAAAAAAAAAAAAAAAAAAAAAAAAkSAAAAAAAAAAAAAAAHgAQCAAACDzxgAcCCALABgAIAAEQEAAAAAAAAAAAAIAIAAATEAIAgAAnQAAHFgCXAAHwcA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + +> +N-[(2S,3R,4R,5R,6R)-4,5-dihydroxy-6-(hydroxymethyl)-2-[(2R,3S,4R,5R)-4,5,6-trihydroxy-2-(hydroxymethyl)tetrahydropyran-3-yl]oxy-tetrahydropyran-3-yl]acetamide + +> +N-[(2S,3R,4R,5R,6R)-4,5-dihydroxy-6-(hydroxymethyl)-2-[[(2R,3S,4R,5R)-4,5,6-trihydroxy-2-(hydroxymethyl)-3-tetrahydropyranyl]oxy]-3-tetrahydropyranyl]acetamide + +> +N-[(2S,3R,4R,5R,6R)-4,5-dihydroxy-6-(hydroxymethyl)-2-[(2R,3S,4R,5R)-4,5,6-trihydroxy-2-(hydroxymethyl)oxan-3-yl]oxyoxan-3-yl]acetamide + +> +N-[(2S,3R,4R,5R,6R)-4,5-dihydroxy-6-(hydroxymethyl)-2-[(2R,3S,4R,5R)-4,5,6-trihydroxy-2-(hydroxymethyl)oxan-3-yl]oxy-oxan-3-yl]ethanamide + +> +N-[(2S,3R,4R,5R,6R)-4,5-dihydroxy-6-methylol-2-[(2R,3S,4R,5R)-4,5,6-trihydroxy-2-methylol-tetrahydropyran-3-yl]oxy-tetrahydropyran-3-yl]acetamide + +> +InChI=1S/C14H25NO11/c1-4(18)15-7-9(20)8(19)5(2-16)25-14(7)26-12-6(3-17)24-13(23)11(22)10(12)21/h5-14,16-17,19-23H,2-3H2,1H3,(H,15,18)/t5-,6-,7-,8+,9-,10-,11-,12-,13?,14+/m1/s1 + +> +DRHGSSXSNNHAAL-ILLCOZOWSA-N + +> +-4.7 + +> +383.142761 + +> +C14H25NO11 + +> +383.3484 + +> +CC(=O)NC1C(C(C(OC1OC2C(OC(C(C2O)O)O)CO)CO)O)O + +> +CC(=O)N[C@@H]1[C@H]([C@H]([C@H](O[C@H]1O[C@@H]2[C@H](OC([C@@H]([C@H]2O)O)O)CO)CO)O)O + +> +198 + +> +383.142761 + +> +0 + +> +26 + +> +9 + +> +1 + +> +0 + +> +0 + +> +0 + +> +1 + +> +2 + +> +1 +5 +255 + +> +14 1 6 +15 1 5 +13 12 6 +19 23 5 +20 24 5 +16 4 5 +17 5 5 +18 6 5 +21 7 6 +22 8 3 + +$$$$ +10000010 + -OEChem-11110908022D + + 42 45 0 0 0 0 0 0 0999 V2000 + 10.7404 -1.4947 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 1.3100 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -1.6900 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -1.6900 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 -0.1900 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0622 -1.1900 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 2.3100 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 9.7942 -0.1900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.7942 -1.1900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.3241 -0.6900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7404 0.1147 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9282 0.3100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.3241 -0.6900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0622 -0.1900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9282 -1.6900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 0.3100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 0.3100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.8241 0.1760 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.8241 -1.5560 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -0.1900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 1.3100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 0.3100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.8241 0.1760 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.8241 -1.5560 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 1.8100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 1.3100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.3241 -0.6900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -1.1900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 1.8100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.9331 0.7041 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9282 0.9300 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9282 -2.3100 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 -0.8100 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 12.5141 0.7130 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 12.5141 -2.0930 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0010 1.6200 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1951 -0.0000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 14.1341 0.7130 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 14.1341 -2.0930 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 2.4300 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 14.9441 -0.6900 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -2.3100 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 9 1 0 0 0 0 + 1 10 1 0 0 0 0 + 2 17 2 0 0 0 0 + 3 28 1 0 0 0 0 + 3 42 1 0 0 0 0 + 4 28 2 0 0 0 0 + 5 16 1 0 0 0 0 + 5 17 1 0 0 0 0 + 5 33 1 0 0 0 0 + 6 14 2 0 0 0 0 + 6 15 1 0 0 0 0 + 7 29 3 0 0 0 0 + 8 9 1 0 0 0 0 + 8 11 1 0 0 0 0 + 8 12 2 0 0 0 0 + 9 15 2 0 0 0 0 + 10 11 2 0 0 0 0 + 10 13 1 0 0 0 0 + 11 30 1 0 0 0 0 + 12 14 1 0 0 0 0 + 12 31 1 0 0 0 0 + 13 18 2 0 0 0 0 + 13 19 1 0 0 0 0 + 14 17 1 0 0 0 0 + 15 32 1 0 0 0 0 + 16 20 2 0 0 0 0 + 16 21 1 0 0 0 0 + 18 23 1 0 0 0 0 + 18 34 1 0 0 0 0 + 19 24 2 0 0 0 0 + 19 35 1 0 0 0 0 + 20 22 1 0 0 0 0 + 20 28 1 0 0 0 0 + 21 25 2 0 0 0 0 + 21 36 1 0 0 0 0 + 22 26 2 0 0 0 0 + 22 37 1 0 0 0 0 + 23 27 2 0 0 0 0 + 23 38 1 0 0 0 0 + 24 27 1 0 0 0 0 + 24 39 1 0 0 0 0 + 25 26 1 0 0 0 0 + 25 40 1 0 0 0 0 + 26 29 1 0 0 0 0 + 27 41 1 0 0 0 0 +M END +> +10000010 + +> +1 + +> +668 + +> +6 + +> +2 + +> +4 + +> +AAADccB7OAAAAAAAAAAAAAAAAAAAASAAAAA8YIAAAAAAAFgB/AAAHgAQCAAADAzBngQ8ztLIFkC4B7X3XASiiCA3YiAI2CG/bNgO5nrEtbuXeajm1BHY+c+83/P+CAACAAACAAAQAAQAAAQAAAAAAAAAAA== + +> +5-cyano-2-[(2-phenylfuro[2,3-c]pyridine-5-carbonyl)amino]benzoic acid + +> +5-cyano-2-[[oxo-(2-phenyl-5-furo[2,3-c]pyridinyl)methyl]amino]benzoic acid + +> +5-cyano-2-[(2-phenylfuro[2,3-c]pyridine-5-carbonyl)amino]benzoic acid + +> +5-cyano-2-[(2-phenylfuro[2,3-c]pyridin-5-yl)carbonylamino]benzoic acid + +> +5-cyano-2-[(2-phenylfuro[2,3-c]pyridine-5-carbonyl)amino]benzoic acid + +> +InChI=1S/C22H13N3O4/c23-11-13-6-7-17(16(8-13)22(27)28)25-21(26)18-9-15-10-19(29-20(15)12-24-18)14-4-2-1-3-5-14/h1-10,12H,(H,25,26)(H,27,28) + +> +VFXRRBWXDNYJLD-UHFFFAOYSA-N + +> +3.9 + +> +383.090606 + +> +C22H13N3O4 + +> +383.35632 + +> +C1=CC=C(C=C1)C2=CC3=CC(=NC=C3O2)C(=O)NC4=C(C=C(C=C4)C#N)C(=O)O + +> +C1=CC=C(C=C1)C2=CC3=CC(=NC=C3O2)C(=O)NC4=C(C=C(C=C4)C#N)C(=O)O + +> +116 + +> +383.090606 + +> +0 + +> +29 + +> +0 + +> +0 + +> +0 + +> +0 + +> +0 + +> +1 + +> +3 + +> +1 +5 +255 + +> +1 10 8 +1 9 8 +10 11 8 +12 14 8 +13 18 8 +13 19 8 +16 20 8 +16 21 8 +18 23 8 +19 24 8 +20 22 8 +21 25 8 +22 26 8 +23 27 8 +24 27 8 +25 26 8 +6 14 8 +6 15 8 +8 11 8 +8 12 8 +8 9 8 +9 15 8 + +$$$$ +10000011 + -OEChem-11110908022D + + 45 47 0 0 0 0 0 0 0999 V2000 + 2.9380 -0.2852 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9100 0.9936 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2682 -1.7852 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 + 6.4021 -3.2852 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9380 -3.2852 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 3.8611 1.3026 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4791 1.3026 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 4.6701 -3.2852 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4021 -2.2852 0.0000 N 0 3 0 0 0 0 0 0 0 0 0 0 + 2.0000 3.7671 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 4.6701 0.7148 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.6701 -0.2852 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.1701 2.2536 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1701 2.2536 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.8041 -0.7852 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5361 -0.7852 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5361 -1.7852 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.6701 -2.2852 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5823 3.0626 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.8041 -1.7852 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.7579 3.0626 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9890 3.9762 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5878 2.9581 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.4013 4.7852 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.8041 -3.7852 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0720 -0.7852 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4067 4.6807 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.8041 -4.7852 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0731 -0.4752 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.2671 -2.0952 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.1223 3.5642 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2563 3.4271 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2595 2.6982 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.6056 4.0410 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2070 -3.5952 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3356 2.3917 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4493 1.4084 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.6534 5.3516 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.7620 -0.2483 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.5351 -1.0952 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3820 -1.3221 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0423 5.1823 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1841 -4.7852 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.8041 -5.4052 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.4241 -4.7852 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 15 1 0 0 0 0 + 1 26 1 0 0 0 0 + 2 6 1 0 0 0 0 + 2 37 1 0 0 0 0 + 3 9 1 0 0 0 0 + 4 9 2 0 0 0 0 + 5 25 2 0 0 0 0 + 6 11 1 0 0 0 0 + 6 13 1 0 0 0 0 + 7 11 2 0 0 0 0 + 7 14 1 0 0 0 0 + 8 18 1 0 0 0 0 + 8 25 1 0 0 0 0 + 8 35 1 0 0 0 0 + 9 17 1 0 0 0 0 + 10 23 1 0 0 0 0 + 10 27 2 0 0 0 0 + 11 12 1 0 0 0 0 + 12 15 1 0 0 0 0 + 12 16 2 0 0 0 0 + 13 14 2 0 0 0 0 + 13 19 1 0 0 0 0 + 14 21 1 0 0 0 0 + 15 20 2 0 0 0 0 + 16 17 1 0 0 0 0 + 16 29 1 0 0 0 0 + 17 18 2 0 0 0 0 + 18 20 1 0 0 0 0 + 19 22 1 0 0 0 0 + 19 23 2 0 0 0 0 + 20 30 1 0 0 0 0 + 21 31 1 0 0 0 0 + 21 32 1 0 0 0 0 + 21 33 1 0 0 0 0 + 22 24 2 0 0 0 0 + 22 34 1 0 0 0 0 + 23 36 1 0 0 0 0 + 24 27 1 0 0 0 0 + 24 38 1 0 0 0 0 + 25 28 1 0 0 0 0 + 26 39 1 0 0 0 0 + 26 40 1 0 0 0 0 + 26 41 1 0 0 0 0 + 27 42 1 0 0 0 0 + 28 43 1 0 0 0 0 + 28 44 1 0 0 0 0 + 28 45 1 0 0 0 0 +M CHG 2 3 -1 9 1 +M END +> +10000011 + +> +1 + +> +573 + +> +7 + +> +2 + +> +4 + +> +AAADceB7uAAAAAAAAAAAAAAAAAAAAWAAAAA8QAAAAAAAAAAB8AAAHgAUCAAADAzBnwY/1pbYVACpAzd3dwCCiC0xMqAp2CA+fJqMbuLE+ZuUMChu1JvI6Cew0CMOCEAAQgAIACAQgACEABAAQAAAAAAAAA== + +> +N-[4-[1-hydroxy-4-methyl-5-(3-pyridyl)imidazol-2-yl]-5-methoxy-2-nitro-phenyl]acetamide + +> +N-[4-[1-hydroxy-4-methyl-5-(3-pyridyl)-2-imidazolyl]-5-methoxy-2-nitrophenyl]acetamide + +> +N-[4-(1-hydroxy-4-methyl-5-pyridin-3-ylimidazol-2-yl)-5-methoxy-2-nitrophenyl]acetamide + +> +N-[4-(1-hydroxy-4-methyl-5-pyridin-3-yl-imidazol-2-yl)-5-methoxy-2-nitro-phenyl]ethanamide + +> +N-[4-[1-hydroxy-4-methyl-5-(3-pyridyl)imidazol-2-yl]-5-methoxy-2-nitro-phenyl]acetamide + +> +InChI=1S/C18H17N5O5/c1-10-17(12-5-4-6-19-9-12)22(25)18(20-10)13-7-15(23(26)27)14(21-11(2)24)8-16(13)28-3/h4-9,25H,1-3H3,(H,21,24) + +> +JQLIGMOWNYBVQO-UHFFFAOYSA-N + +> +2.3 + +> +383.122969 + +> +C18H17N5O5 + +> +383.35808 + +> +CC1=C(N(C(=N1)C2=CC(=C(C=C2OC)NC(=O)C)[N+](=O)[O-])O)C3=CN=CC=C3 + +> +CC1=C(N(C(=N1)C2=CC(=C(C=C2OC)NC(=O)C)[N+](=O)[O-])O)C3=CN=CC=C3 + +> +132 + +> +383.122969 + +> +0 + +> +28 + +> +0 + +> +0 + +> +0 + +> +0 + +> +0 + +> +1 + +> +3 + +> +1 +5 +255 + +> +10 23 8 +10 27 8 +12 15 8 +12 16 8 +13 14 8 +15 20 8 +16 17 8 +17 18 8 +18 20 8 +19 22 8 +19 23 8 +22 24 8 +24 27 8 +6 11 8 +6 13 8 +7 11 8 +7 14 8 + +$$$$ +10000012 + -OEChem-11110908022D + + 69 64 0 1 0 0 0 0 0999 V2000 + 8.2323 0.0000 0.0000 Ti 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3663 -0.5001 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0982 0.5001 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.7323 -0.8660 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7322 0.8661 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6342 -0.5002 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 6.5002 -0.0001 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 6.0001 0.8658 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0001 0.8658 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.9642 0.0002 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.2324 -1.7320 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.2320 1.7321 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5001 1.7318 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5001 1.7317 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.7682 -0.0003 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6344 -1.5003 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4645 -0.8657 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.8304 0.5004 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.7325 -2.5980 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3665 -2.2322 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0979 2.2323 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7318 2.5980 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.0000 2.5977 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 2.5976 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.9386 0.4383 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.9033 -0.8101 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.5826 1.0780 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.8924 1.4764 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5612 0.8101 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0423 -0.3290 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4222 0.3290 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.4176 0.6537 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1078 0.2552 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.5259 -0.4383 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7940 -1.2937 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.6705 1.2938 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0827 1.9439 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.3924 2.3424 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9175 1.5196 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.6078 1.1211 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0781 0.5367 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2312 0.3097 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.4582 -0.5372 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0144 -1.5003 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6344 -2.1203 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2543 -1.5002 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.9276 -1.1758 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7746 -1.4025 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.0013 -0.5555 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.1404 -0.0365 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.3673 0.8105 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.5203 1.0373 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.1956 -2.9081 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0426 -3.1349 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2694 -2.2880 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0564 -1.6953 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8297 -2.5423 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.6766 -2.7691 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.4080 1.6954 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.6347 2.5424 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.7878 2.7691 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.2686 2.9082 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4216 3.1349 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1949 2.2879 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5826 2.8098 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8923 3.2083 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.9999 3.2176 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.3800 2.5975 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0001 1.9776 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2 7 1 0 0 0 0 + 2 26 1 0 0 0 0 + 3 10 1 0 0 0 0 + 3 29 1 0 0 0 0 + 4 11 1 0 0 0 0 + 4 30 1 0 0 0 0 + 5 12 1 0 0 0 0 + 5 31 1 0 0 0 0 + 6 7 1 0 0 0 0 + 6 15 1 0 0 0 0 + 6 16 1 0 0 0 0 + 7 8 1 0 0 0 0 + 7 25 1 0 0 0 0 + 8 9 1 0 0 0 0 + 8 27 1 0 0 0 0 + 8 28 1 0 0 0 0 + 9 13 1 0 0 0 0 + 9 32 1 0 0 0 0 + 9 33 1 0 0 0 0 + 10 17 1 0 0 0 0 + 10 18 1 0 0 0 0 + 10 34 1 0 0 0 0 + 11 19 1 0 0 0 0 + 11 20 1 0 0 0 0 + 11 35 1 0 0 0 0 + 12 21 1 0 0 0 0 + 12 22 1 0 0 0 0 + 12 36 1 0 0 0 0 + 13 14 1 0 0 0 0 + 13 37 1 0 0 0 0 + 13 38 1 0 0 0 0 + 14 23 1 0 0 0 0 + 14 39 1 0 0 0 0 + 14 40 1 0 0 0 0 + 15 41 1 0 0 0 0 + 15 42 1 0 0 0 0 + 15 43 1 0 0 0 0 + 16 44 1 0 0 0 0 + 16 45 1 0 0 0 0 + 16 46 1 0 0 0 0 + 17 47 1 0 0 0 0 + 17 48 1 0 0 0 0 + 17 49 1 0 0 0 0 + 18 50 1 0 0 0 0 + 18 51 1 0 0 0 0 + 18 52 1 0 0 0 0 + 19 53 1 0 0 0 0 + 19 54 1 0 0 0 0 + 19 55 1 0 0 0 0 + 20 56 1 0 0 0 0 + 20 57 1 0 0 0 0 + 20 58 1 0 0 0 0 + 21 59 1 0 0 0 0 + 21 60 1 0 0 0 0 + 21 61 1 0 0 0 0 + 22 62 1 0 0 0 0 + 22 63 1 0 0 0 0 + 22 64 1 0 0 0 0 + 23 24 1 0 0 0 0 + 23 65 1 0 0 0 0 + 23 66 1 0 0 0 0 + 24 67 1 0 0 0 0 + 24 68 1 0 0 0 0 + 24 69 1 0 0 0 0 +M END +> +10000012 + +> +1 + +> +178 + +> +5 + +> +4 + +> +6 + +> +AAADcfB6OAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgAACAAACBThgAYCAAMAAgAAAAAAAAAAAAAAAAAAAAAIAAAAEAIAgAAEQAAEAAAAAAGQwCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + +> +1-(dimethylamino)heptan-1-ol; propan-2-ol; titanium + +> +1-(dimethylamino)-1-heptanol; 2-propanol; titanium + +> +1-(dimethylamino)heptan-1-ol; propan-2-ol; titanium + +> +1-(dimethylamino)heptan-1-ol; propan-2-ol; titanium + +> +1-(dimethylamino)heptan-1-ol; propan-2-ol; titanium + +> +InChI=1S/C9H21NO.3C3H8O.Ti/c1-4-5-6-7-8-9(11)10(2)3;3*1-3(2)4;/h9,11H,4-8H2,1-3H3;3*3-4H,1-2H3; + +> +KTMRZYPQKLRPGJ-UHFFFAOYSA-N + +> +387.282806 + +> +C18H45NO4Ti + +> +387.4212 + +> +CCCCCCC(N(C)C)O.CC(C)O.CC(C)O.CC(C)O.[Ti] + +> +CCCCCCC(N(C)C)O.CC(C)O.CC(C)O.CC(C)O.[Ti] + +> +84.2 + +> +387.282806 + +> +0 + +> +24 + +> +0 + +> +1 + +> +0 + +> +0 + +> +0 + +> +5 + +> +1 + +> +1 2 6 +1 3 6 +1 4 6 +1 5 6 + +> +1 +5 +255 + +> +1 2 4 +1 3 4 +1 4 4 +1 5 4 +7 2 3 + +$$$$ +10000013 + -OEChem-11110908022D + + 32 31 0 1 0 0 0 0 0999 V2000 + 7.1962 -1.0950 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0622 0.4050 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -0.0950 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 0.4050 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 0.4050 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 -0.0950 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 3.7321 -0.0950 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 0.4050 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9282 -0.0950 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0622 1.4050 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -0.0950 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0656 -0.5699 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.8626 -0.5699 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7287 0.8799 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.9316 0.8799 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9966 0.8799 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.1996 0.8799 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4600 -0.5200 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.3335 -0.5699 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.1306 -0.5699 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.2646 0.8799 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4675 0.8799 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.6182 -0.6319 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.4651 -0.4050 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2382 0.4419 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.6822 1.4050 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0622 2.0250 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4422 1.4050 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.6900 0.4419 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 -0.4050 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3100 -0.6319 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.6592 -1.4050 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 6 1 0 0 0 0 + 1 32 1 0 0 0 0 + 2 6 1 0 0 0 0 + 2 9 1 0 0 0 0 + 2 10 1 0 0 0 0 + 3 4 1 0 0 0 0 + 3 5 1 0 0 0 0 + 3 12 1 0 0 0 0 + 3 13 1 0 0 0 0 + 4 6 1 0 0 0 0 + 4 14 1 0 0 0 0 + 4 15 1 0 0 0 0 + 5 7 1 0 0 0 0 + 5 16 1 0 0 0 0 + 5 17 1 0 0 0 0 + 6 18 1 0 0 0 0 + 7 8 1 0 0 0 0 + 7 19 1 0 0 0 0 + 7 20 1 0 0 0 0 + 8 11 1 0 0 0 0 + 8 21 1 0 0 0 0 + 8 22 1 0 0 0 0 + 9 23 1 0 0 0 0 + 9 24 1 0 0 0 0 + 9 25 1 0 0 0 0 + 10 26 1 0 0 0 0 + 10 27 1 0 0 0 0 + 10 28 1 0 0 0 0 + 11 29 1 0 0 0 0 + 11 30 1 0 0 0 0 + 11 31 1 0 0 0 0 +M END +> +10000013 + +> +1 + +> +83.6 + +> +2 + +> +1 + +> +6 + +> +AAADceByIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgAACAAACADhgAYCAAMAAgAAAAAAAAAAAAAAAAAAAAAIAAAAEAIAgAAEQAAEAAAAAAGQwCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + +> +1-(dimethylamino)heptan-1-ol + +> +1-(dimethylamino)-1-heptanol + +> +1-(dimethylamino)heptan-1-ol + +> +1-(dimethylamino)heptan-1-ol + +> +1-(dimethylamino)heptan-1-ol + +> +InChI=1S/C9H21NO/c1-4-5-6-7-8-9(11)10(2)3/h9,11H,4-8H2,1-3H3 + +> +UYIPYDFMIUXXIR-UHFFFAOYSA-N + +> +2.7 + +> +159.162314 + +> +C9H21NO + +> +159.26914 + +> +CCCCCCC(N(C)C)O + +> +CCCCCCC(N(C)C)O + +> +23.5 + +> +159.162314 + +> +0 + +> +11 + +> +0 + +> +1 + +> +0 + +> +0 + +> +0 + +> +1 + +> +1 + +> +1 +5 +255 + +> +6 18 3 + +$$$$ +10000014 + -OEChem-11110908022D + + 47 49 0 1 0 0 0 0 0999 V2000 + 5.4641 2.3877 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 2.3877 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 + 9.7942 -1.1123 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0622 -2.1123 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 -0.6123 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0622 0.8877 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9282 0.3877 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 8.9282 -0.6123 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 9.7942 0.8877 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6603 0.3877 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0622 -1.1123 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.7942 1.8877 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.5263 0.8877 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.5263 1.8877 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6603 2.3877 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.4202 0.3530 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 0.3877 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.4202 2.4223 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.3263 0.8669 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.3263 1.9085 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 0.8877 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 0.3877 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 0.8877 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 1.8877 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 0.3877 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 2.3877 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 0.8877 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 1.8877 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9282 1.0077 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.4651 -0.3023 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6603 -0.2323 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0622 1.5077 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8501 -0.5297 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4516 -1.2200 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2573 2.1977 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6603 3.0077 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.7942 -1.7323 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 12.4131 -0.2670 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 12.4131 3.0423 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 13.8620 0.5548 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 13.8620 2.2206 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5252 -2.4223 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 1.5077 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -0.2323 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -0.2323 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 3.0077 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3291 0.5777 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 24 1 0 0 0 0 + 2 28 1 0 0 0 0 + 8 3 1 6 0 0 0 + 3 37 1 0 0 0 0 + 4 11 1 0 0 0 0 + 4 42 1 0 0 0 0 + 5 17 2 0 0 0 0 + 7 6 1 6 0 0 0 + 6 17 1 0 0 0 0 + 6 32 1 0 0 0 0 + 7 8 1 0 0 0 0 + 7 9 1 0 0 0 0 + 7 29 1 0 0 0 0 + 8 11 1 0 0 0 0 + 8 30 1 0 0 0 0 + 9 10 2 0 0 0 0 + 9 12 1 0 0 0 0 + 10 13 1 0 0 0 0 + 10 31 1 0 0 0 0 + 11 33 1 0 0 0 0 + 11 34 1 0 0 0 0 + 12 15 2 0 0 0 0 + 12 35 1 0 0 0 0 + 13 14 1 0 0 0 0 + 13 16 2 0 0 0 0 + 14 15 1 0 0 0 0 + 14 18 2 0 0 0 0 + 15 36 1 0 0 0 0 + 16 19 1 0 0 0 0 + 16 38 1 0 0 0 0 + 17 21 1 0 0 0 0 + 18 20 1 0 0 0 0 + 18 39 1 0 0 0 0 + 19 20 2 0 0 0 0 + 19 40 1 0 0 0 0 + 20 41 1 0 0 0 0 + 21 22 2 0 0 0 0 + 21 43 1 0 0 0 0 + 22 23 1 0 0 0 0 + 22 44 1 0 0 0 0 + 23 24 2 0 0 0 0 + 23 25 1 0 0 0 0 + 24 26 1 0 0 0 0 + 25 27 2 0 0 0 0 + 25 45 1 0 0 0 0 + 26 28 2 0 0 0 0 + 26 46 1 0 0 0 0 + 27 28 1 0 0 0 0 + 27 47 1 0 0 0 0 +M END +> +10000014 + +> +1 + +> +544 + +> +5 + +> +3 + +> +6 + +> +AAADceB6MYAAAAAAAAAAAAAAAAAAAAAAAAAwYMAAAAAAAADBVAAAHwAQCAAADDzhmA4wAMLAAgCIAiFSEACCAAAgAAAIiIAIBMgLMCKAkRGEYAhm1gGZiYeQwPAOwAACAAAQAACAAAQAACAAAAAAAAAAAA== + +> +(E)-3-(2,4-difluorophenyl)-N-[(1R,2S)-2,3-dihydroxy-1-(2-naphthyl)propyl]prop-2-enamide + +> +(E)-3-(2,4-difluorophenyl)-N-[(1R,2S)-2,3-dihydroxy-1-(2-naphthalenyl)propyl]-2-propenamide + +> +(E)-3-(2,4-difluorophenyl)-N-[(1R,2S)-2,3-dihydroxy-1-naphthalen-2-ylpropyl]prop-2-enamide + +> +(E)-3-(2,4-difluorophenyl)-N-[(1R,2S)-2,3-dihydroxy-1-naphthalen-2-yl-propyl]prop-2-enamide + +> +(E)-3-(2,4-difluorophenyl)-N-[(1R,2S)-2,3-dihydroxy-1-(2-naphthyl)propyl]acrylamide + +> +InChI=1S/C22H19F2NO3/c23-18-9-7-15(19(24)12-18)8-10-21(28)25-22(20(27)13-26)17-6-5-14-3-1-2-4-16(14)11-17/h1-12,20,22,26-27H,13H2,(H,25,28)/b10-8+/t20-,22-/m1/s1 + +> +RNBZZVYTDGGCHB-AZLYJIEISA-N + +> +3.3 + +> +383.1333 + +> +C22H19F2NO3 + +> +383.387966 + +> +C1=CC=C2C=C(C=CC2=C1)C(C(CO)O)NC(=O)C=CC3=C(C=C(C=C3)F)F + +> +C1=CC=C2C=C(C=CC2=C1)[C@H]([C@@H](CO)O)NC(=O)/C=C/C3=C(C=C(C=C3)F)F + +> +69.6 + +> +383.1333 + +> +0 + +> +28 + +> +2 + +> +0 + +> +1 + +> +0 + +> +0 + +> +1 + +> +2 + +> +1 +5 +255 + +> +10 13 8 +12 15 8 +13 14 8 +13 16 8 +14 15 8 +14 18 8 +16 19 8 +18 20 8 +19 20 8 +23 24 8 +23 25 8 +24 26 8 +25 27 8 +26 28 8 +27 28 8 +8 3 6 +7 6 6 +9 10 8 +9 12 8 + +$$$$ +10000015 + -OEChem-11110908022D + + 49 52 0 1 0 0 0 0 0999 V2000 + 7.2825 -3.6457 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.7929 -2.9183 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 5.3788 -2.4500 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2.2948 -0.7689 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2.7031 1.0315 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0052 2.8540 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4447 2.1025 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 5.8786 1.2015 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 6.8786 1.2015 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5021 0.4197 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2795 -0.5553 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2551 0.4197 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3786 -0.9892 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4776 -0.5553 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2847 -2.0265 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.1491 -1.1287 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1408 -2.6199 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0793 -2.1680 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.3686 0.8393 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8610 -1.3179 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.4866 0.4101 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.8802 -1.3140 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0080 2.9287 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.3015 -3.8297 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.2697 -0.5464 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5741 3.8297 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2926 -3.4463 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -1.7244 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2084 1.7265 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4372 1.4705 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7406 1.8059 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0607 0.1507 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8886 0.9044 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.7046 -0.8533 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8264 2.1488 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.3652 1.4592 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1269 -1.8780 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.6097 -1.8719 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.1278 -4.4248 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.8751 -4.0650 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.1327 4.0987 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0155 3.5607 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.3051 4.3883 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.9102 -3.4997 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.6749 -3.3928 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2391 -4.0639 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4075 -1.5417 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.8173 -2.3169 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5925 -1.9072 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 17 1 0 0 0 0 + 1 24 1 0 0 0 0 + 2 18 1 0 0 0 0 + 2 24 1 0 0 0 0 + 3 15 1 0 0 0 0 + 3 27 1 0 0 0 0 + 4 25 1 0 0 0 0 + 4 28 1 0 0 0 0 + 5 21 2 0 0 0 0 + 6 23 2 0 0 0 0 + 8 7 1 1 0 0 0 + 7 23 1 0 0 0 0 + 7 35 1 0 0 0 0 + 8 9 1 0 0 0 0 + 8 12 1 0 0 0 0 + 8 29 1 0 0 0 0 + 9 10 1 0 0 0 0 + 9 30 1 0 0 0 0 + 9 31 1 0 0 0 0 + 10 11 1 0 0 0 0 + 10 32 1 0 0 0 0 + 10 33 1 0 0 0 0 + 11 13 1 0 0 0 0 + 11 16 2 0 0 0 0 + 12 14 1 0 0 0 0 + 12 19 2 0 0 0 0 + 13 14 1 0 0 0 0 + 13 15 2 0 0 0 0 + 14 20 2 0 0 0 0 + 15 17 1 0 0 0 0 + 16 18 1 0 0 0 0 + 16 34 1 0 0 0 0 + 17 18 2 0 0 0 0 + 19 21 1 0 0 0 0 + 19 36 1 0 0 0 0 + 20 22 1 0 0 0 0 + 20 37 1 0 0 0 0 + 21 25 1 0 0 0 0 + 22 25 2 0 0 0 0 + 22 38 1 0 0 0 0 + 23 26 1 0 0 0 0 + 24 39 1 0 0 0 0 + 24 40 1 0 0 0 0 + 26 41 1 0 0 0 0 + 26 42 1 0 0 0 0 + 26 43 1 0 0 0 0 + 27 44 1 0 0 0 0 + 27 45 1 0 0 0 0 + 27 46 1 0 0 0 0 + 28 47 1 0 0 0 0 + 28 48 1 0 0 0 0 + 28 49 1 0 0 0 0 +M END +> +10000015 + +> +1 + +> +754 + +> +6 + +> +1 + +> +3 + +> +AAADceB6OAAAAAAAAAAAAAAAAAAAASAAAAAwAAAABkgAAEgBAAAAHgAQAAAADCzBmAcyDoLABACIAqFSEAKCCAAgIAAAiIBOiMgdJyKEsR6kMCIl1hWOqYeQ8P8OoAABCAAYQABAAAIQADCAAAAAAAAAAA== + +> +InChI=1S/C21H21NO6/c1-11(23)22-15-6-4-12-8-18-20(28-10-27-18)21(26-3)19(12)13-5-7-17(25-2)16(24)9-14(13)15/h5,7-9,15H,4,6,10H2,1-3H3,(H,22,23)/t15-/m0/s1 + +> +DCYAJVOKJAFSES-HNNXBMFYSA-N + +> +0.7 + +> +383.136887 + +> +C21H21NO6 + +> +383.39454 + +> +CC(=O)NC1CCC2=CC3=C(C(=C2C4=CC=C(C(=O)C=C14)OC)OC)OCO3 + +> +CC(=O)N[C@H]1CCC2=CC3=C(C(=C2C4=CC=C(C(=O)C=C14)OC)OC)OCO3 + +> +83.1 + +> +383.136887 + +> +0 + +> +28 + +> +1 + +> +0 + +> +0 + +> +0 + +> +0 + +> +1 + +> +2 + +> +1 +5 +255 + +> +11 13 8 +11 16 8 +12 14 8 +12 19 8 +13 15 8 +14 20 8 +15 17 8 +16 18 8 +17 18 8 +19 21 8 +20 22 8 +21 25 8 +22 25 8 +8 7 5 + +$$$$ +10000016 + -OEChem-11110908022D + + 49 51 0 1 0 0 0 0 0999 V2000 + 9.0281 2.8737 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.3210 5.8305 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 10.1529 4.3397 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -1.3675 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -5.8675 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -4.3675 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -1.3675 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0622 3.1325 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 8.3210 4.0985 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 7.1962 2.6325 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2869 3.8397 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 1.6325 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8210 4.9645 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 1.1325 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 0.1325 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 1.6325 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -0.3675 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 1.1325 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 0.1325 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -1.8675 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -2.8675 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -3.3675 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -3.3675 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -4.3675 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -4.3675 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -4.8675 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -4.8675 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -6.3675 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.1431 2.5178 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.6984 4.5903 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.5856 2.5249 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.9841 3.2152 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8067 1.7402 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4082 1.0499 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3460 4.5660 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3460 5.3630 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8671 -0.1775 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0110 6.3675 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 2.2525 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0611 1.4425 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0611 -0.1775 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0010 -1.6775 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1951 -3.0575 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0010 -3.0575 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0010 -4.6775 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -5.4875 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.6900 -5.8305 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3100 -6.9044 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 -6.6775 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 8 1 0 0 0 0 + 1 11 1 0 0 0 0 + 2 13 1 0 0 0 0 + 2 38 1 0 0 0 0 + 3 11 2 0 0 0 0 + 4 20 2 0 0 0 0 + 5 27 1 0 0 0 0 + 5 28 1 0 0 0 0 + 6 27 2 0 0 0 0 + 7 17 1 0 0 0 0 + 7 20 1 0 0 0 0 + 7 42 1 0 0 0 0 + 8 9 1 0 0 0 0 + 8 10 1 6 0 0 0 + 8 29 1 0 0 0 0 + 9 11 1 0 0 0 0 + 9 13 1 1 0 0 0 + 9 30 1 0 0 0 0 + 10 12 1 0 0 0 0 + 10 31 1 0 0 0 0 + 10 32 1 0 0 0 0 + 12 14 1 0 0 0 0 + 12 33 1 0 0 0 0 + 12 34 1 0 0 0 0 + 13 35 1 0 0 0 0 + 13 36 1 0 0 0 0 + 14 15 2 0 0 0 0 + 14 16 1 0 0 0 0 + 15 17 1 0 0 0 0 + 15 37 1 0 0 0 0 + 16 18 2 0 0 0 0 + 16 39 1 0 0 0 0 + 17 19 2 0 0 0 0 + 18 19 1 0 0 0 0 + 18 40 1 0 0 0 0 + 19 41 1 0 0 0 0 + 20 21 1 0 0 0 0 + 21 22 2 0 0 0 0 + 21 23 1 0 0 0 0 + 22 24 1 0 0 0 0 + 22 43 1 0 0 0 0 + 23 25 2 0 0 0 0 + 23 44 1 0 0 0 0 + 24 26 2 0 0 0 0 + 24 27 1 0 0 0 0 + 25 26 1 0 0 0 0 + 25 45 1 0 0 0 0 + 26 46 1 0 0 0 0 + 28 47 1 0 0 0 0 + 28 48 1 0 0 0 0 + 28 49 1 0 0 0 0 +M END +> +10000016 + +> +1 + +> +579 + +> +6 + +> +2 + +> +8 + +> +AAADceB6OAAAAAAAAAAAAAAAAABIAAAAAAAwYAAAAAAAAAABQAAAHgAQCAAADRyhmAIyyILABgCIAiXSWACCAAAlAgAIiAEAbMgINjbAtZmGcQhl9AHI6YeY3+KOAAACQAAAAAAAAASAAAAAAAAAAAAAAA== + +> +methyl 3-[[3-[2-[(2R,3R)-3-(hydroxymethyl)-4-oxo-oxetan-2-yl]ethyl]phenyl]carbamoyl]benzoate + +> +3-[[3-[2-[(2R,3R)-3-(hydroxymethyl)-4-oxo-2-oxetanyl]ethyl]anilino]-oxomethyl]benzoic acid methyl ester + +> +methyl 3-[[3-[2-[(2R,3R)-3-(hydroxymethyl)-4-oxooxetan-2-yl]ethyl]phenyl]carbamoyl]benzoate + +> +methyl 3-[[3-[2-[(2R,3R)-3-(hydroxymethyl)-4-oxo-oxetan-2-yl]ethyl]phenyl]carbamoyl]benzoate + +> +3-[[3-[2-[(2R,3R)-4-keto-3-methylol-oxetan-2-yl]ethyl]phenyl]carbamoyl]benzoic acid methyl ester + +> +InChI=1S/C21H21NO6/c1-27-20(25)15-6-3-5-14(11-15)19(24)22-16-7-2-4-13(10-16)8-9-18-17(12-23)21(26)28-18/h2-7,10-11,17-18,23H,8-9,12H2,1H3,(H,22,24)/t17-,18-/m1/s1 + +> +HTEQBYAEAQGXPZ-QZTJIDSGSA-N + +> +2.8 + +> +383.136887 + +> +C21H21NO6 + +> +383.39454 + +> +COC(=O)C1=CC=CC(=C1)C(=O)NC2=CC=CC(=C2)CCC3C(C(=O)O3)CO + +> +COC(=O)C1=CC=CC(=C1)C(=O)NC2=CC=CC(=C2)CC[C@@H]3[C@H](C(=O)O3)CO + +> +102 + +> +383.136887 + +> +0 + +> +28 + +> +2 + +> +0 + +> +0 + +> +0 + +> +0 + +> +1 + +> +2 + +> +1 +5 +255 + +> +14 15 8 +14 16 8 +15 17 8 +16 18 8 +17 19 8 +18 19 8 +21 22 8 +21 23 8 +22 24 8 +23 25 8 +24 26 8 +25 26 8 +8 10 6 +9 13 5 + +$$$$ +10000017 + -OEChem-11110908022D + + 50 49 0 0 0 0 0 0 0999 V2000 + 0.8660 4.2845 0.0000 S 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3890 3.1200 0.0000 P 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3890 4.1200 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3890 3.1200 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3890 2.1200 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.3890 3.1200 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 3.7845 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 + 1.3660 3.4185 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 0.3660 5.1506 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1210 7.1200 0.0000 N 0 3 0 0 0 0 0 0 0 0 0 0 + 9.1210 6.1200 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1210 8.1200 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.1210 7.1200 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.1210 7.1200 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.2550 5.6200 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.9871 5.6200 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.2550 4.6200 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.9871 4.6200 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1210 4.1200 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.8890 3.9860 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.5230 1.6200 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8890 3.9860 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.5230 0.6200 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.7321 4.7845 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.7410 8.1200 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1210 8.7400 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5010 8.1200 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.1210 6.5000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7410 7.1200 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.1210 7.7400 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.1210 7.7400 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5010 7.1200 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.1210 6.5000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7181 5.9300 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.5240 5.9300 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.5240 4.3100 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1210 3.5000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.7813 4.5966 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4716 4.1981 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.9124 1.5123 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3109 2.2026 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8890 4.6060 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8890 3.3660 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2690 3.9860 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.9030 0.6200 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.5230 0.0000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1430 0.6200 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0421 4.2476 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.2690 5.0945 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4221 5.3215 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 7 1 0 0 0 0 + 1 8 2 0 0 0 0 + 1 9 2 0 0 0 0 + 1 24 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 4 1 0 0 0 0 + 2 5 1 0 0 0 0 + 2 6 2 0 0 0 0 + 3 17 1 0 0 0 0 + 4 20 1 0 0 0 0 + 5 21 1 0 0 0 0 + 10 11 1 0 0 0 0 + 10 12 1 0 0 0 0 + 10 13 1 0 0 0 0 + 10 14 1 0 0 0 0 + 11 15 2 0 0 0 0 + 11 16 1 0 0 0 0 + 12 25 1 0 0 0 0 + 12 26 1 0 0 0 0 + 12 27 1 0 0 0 0 + 13 28 1 0 0 0 0 + 13 29 1 0 0 0 0 + 13 30 1 0 0 0 0 + 14 31 1 0 0 0 0 + 14 32 1 0 0 0 0 + 14 33 1 0 0 0 0 + 15 17 1 0 0 0 0 + 15 34 1 0 0 0 0 + 16 18 2 0 0 0 0 + 16 35 1 0 0 0 0 + 17 19 2 0 0 0 0 + 18 19 1 0 0 0 0 + 18 36 1 0 0 0 0 + 19 37 1 0 0 0 0 + 20 22 1 0 0 0 0 + 20 38 1 0 0 0 0 + 20 39 1 0 0 0 0 + 21 23 1 0 0 0 0 + 21 40 1 0 0 0 0 + 21 41 1 0 0 0 0 + 22 42 1 0 0 0 0 + 22 43 1 0 0 0 0 + 22 44 1 0 0 0 0 + 23 45 1 0 0 0 0 + 23 46 1 0 0 0 0 + 23 47 1 0 0 0 0 + 24 48 1 0 0 0 0 + 24 49 1 0 0 0 0 + 24 50 1 0 0 0 0 +M CHG 2 7 -1 10 1 +M END +> +10000017 + +> +1 + +> +386 + +> +7 + +> +0 + +> +7 + +> +AAADceByOAJAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAABAAAAHgQAACAACAyhkAayxoMAARKAQCRCQPCCAAAhIgAIiAAObIgIJiLAkZOEMAhmwBlIyAeQUAMAAAAAAgAAAAAAAAAEAAAAAAAAAAAAAA== + +> +(3-diethoxyphosphoryloxyphenyl)-trimethyl-ammonium; methanesulfonate + +> +(3-diethoxyphosphoryloxyphenyl)-trimethylammonium; methanesulfonate + +> +(3-diethoxyphosphoryloxyphenyl)-trimethylazanium; methanesulfonate + +> +(3-diethoxyphosphoryloxyphenyl)-trimethyl-azanium; methanesulfonate + +> +(3-diethoxyphosphoryloxyphenyl)-trimethyl-ammonium mesylate + +> +InChI=1S/C13H23NO4P.CH4O3S/c1-6-16-19(15,17-7-2)18-13-10-8-9-12(11-13)14(3,4)5;1-5(2,3)4/h8-11H,6-7H2,1-5H3;1H3,(H,2,3,4)/q+1;/p-1 + +> +UTVQMKYHKFLPLX-UHFFFAOYSA-M + +> +383.116759 + +> +C14H26NO7PS + +> +383.397501 + +> +CCOP(=O)(OCC)OC1=CC=CC(=C1)[N+](C)(C)C.CS(=O)(=O)[O-] + +> +CCOP(=O)(OCC)OC1=CC=CC(=C1)[N+](C)(C)C.CS(=O)(=O)[O-] + +> +102 + +> +383.116759 + +> +0 + +> +24 + +> +0 + +> +0 + +> +0 + +> +0 + +> +0 + +> +2 + +> +1 + +> +1 +5 +255 + +> +11 15 8 +11 16 8 +15 17 8 +16 18 8 +17 19 8 +18 19 8 + +$$$$ +10000018 + -OEChem-11110908022D + + 46 50 0 0 0 0 0 0 0999 V2000 + 7.2622 2.1784 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9942 2.1784 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8602 0.6784 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2622 -0.8216 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 8.8652 -0.9887 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0039 -1.8525 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 8.1282 -0.3216 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3961 -0.3216 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4714 -1.7935 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4601 -1.8965 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3961 0.6784 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.1282 0.6784 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2622 1.1784 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5022 -0.8563 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8506 -2.6222 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5961 -0.3425 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5022 1.2130 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.8966 -2.8355 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5961 0.6992 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9942 1.1784 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2653 -3.5710 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.2952 -3.6784 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7320 -0.8458 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7359 -1.8458 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8641 -0.3491 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8602 2.6784 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8718 -2.3491 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -0.8525 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8602 3.6784 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.4719 -0.8609 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5094 -1.4763 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2347 -2.5508 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5094 1.8330 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.5140 -2.8926 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0604 1.0112 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8994 -4.0715 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5500 -4.2436 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2740 -2.1537 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8617 0.2709 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.0723 2.0958 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4708 2.7860 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8742 -2.9691 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4619 -0.5445 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8602 4.2984 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2402 3.6784 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4802 3.6784 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 13 2 0 0 0 0 + 2 20 1 0 0 0 0 + 2 26 1 0 0 0 0 + 3 20 2 0 0 0 0 + 4 7 1 0 0 0 0 + 4 8 1 0 0 0 0 + 4 9 1 0 0 0 0 + 5 7 1 0 0 0 0 + 5 10 1 0 0 0 0 + 5 30 1 0 0 0 0 + 6 27 2 0 0 0 0 + 6 28 1 0 0 0 0 + 7 12 2 0 0 0 0 + 8 11 2 0 0 0 0 + 8 14 1 0 0 0 0 + 9 10 1 0 0 0 0 + 9 15 2 0 0 0 0 + 10 18 2 0 0 0 0 + 11 13 1 0 0 0 0 + 11 17 1 0 0 0 0 + 12 13 1 0 0 0 0 + 12 20 1 0 0 0 0 + 14 16 2 0 0 0 0 + 14 31 1 0 0 0 0 + 15 21 1 0 0 0 0 + 15 32 1 0 0 0 0 + 16 19 1 0 0 0 0 + 16 23 1 0 0 0 0 + 17 19 2 0 0 0 0 + 17 33 1 0 0 0 0 + 18 22 1 0 0 0 0 + 18 34 1 0 0 0 0 + 19 35 1 0 0 0 0 + 21 22 2 0 0 0 0 + 21 36 1 0 0 0 0 + 22 37 1 0 0 0 0 + 23 24 2 0 0 0 0 + 23 25 1 0 0 0 0 + 24 27 1 0 0 0 0 + 24 38 1 0 0 0 0 + 25 28 2 0 0 0 0 + 25 39 1 0 0 0 0 + 26 29 1 0 0 0 0 + 26 40 1 0 0 0 0 + 26 41 1 0 0 0 0 + 27 42 1 0 0 0 0 + 28 43 1 0 0 0 0 + 29 44 1 0 0 0 0 + 29 45 1 0 0 0 0 + 29 46 1 0 0 0 0 +M END +> +10000018 + +> +1 + +> +700 + +> +6 + +> +1 + +> +4 + +> +AAADceB7MAAAAAAAAAAAAAAAAAAAAWAAAAA8YIEAAAAAAFiB0AAAHgAQAAAADAzhmgY/yJPIFACoArT3TACCgCA1AiAI2CE4dNiIZH7ItZGWIYhssALI6eeY2eOewAAAQAACACCAAACAAAQAQAAAAAAAAA== + +> +ethyl 5-oxo-2-(4-pyridyl)-7H-benzimidazolo[1,2-a]quinoline-6-carboxylate + +> +5-oxo-2-(4-pyridyl)-7H-benzimidazolo[1,2-a]quinoline-6-carboxylic acid ethyl ester + +> +ethyl 5-oxo-2-pyridin-4-yl-7H-benzimidazolo[1,2-a]quinoline-6-carboxylate + +> +ethyl 5-oxo-2-pyridin-4-yl-7H-benzimidazolo[1,2-a]quinoline-6-carboxylate + +> +5-keto-2-(4-pyridyl)-7H-benzimidazolo[1,2-a]quinoline-6-carboxylic acid ethyl ester + +> +InChI=1S/C23H17N3O3/c1-2-29-23(28)20-21(27)16-8-7-15(14-9-11-24-12-10-14)13-19(16)26-18-6-4-3-5-17(18)25-22(20)26/h3-13,25H,2H2,1H3 + +> +VNEZLZQSWUDRIV-UHFFFAOYSA-N + +> +4.1 + +> +383.126991 + +> +C23H17N3O3 + +> +383.39938 + +> +CCOC(=O)C1=C2NC3=CC=CC=C3N2C4=C(C1=O)C=CC(=C4)C5=CC=NC=C5 + +> +CCOC(=O)C1=C2NC3=CC=CC=C3N2C4=C(C1=O)C=CC(=C4)C5=CC=NC=C5 + +> +71.5 + +> +383.126991 + +> +0 + +> +29 + +> +0 + +> +0 + +> +0 + +> +0 + +> +0 + +> +1 + +> +4 + +> +1 +5 +255 + +> +10 18 8 +11 13 8 +11 17 8 +12 13 8 +14 16 8 +15 21 8 +16 19 8 +17 19 8 +18 22 8 +21 22 8 +23 24 8 +23 25 8 +24 27 8 +25 28 8 +4 7 8 +4 8 8 +4 9 8 +5 10 8 +5 7 8 +6 27 8 +6 28 8 +7 12 8 +8 11 8 +8 14 8 +9 10 8 +9 15 8 + +$$$$ +10000019 + -OEChem-11110908022D + + 49 50 0 1 0 0 0 0 0999 V2000 + 7.7331 0.8100 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 10.3312 2.3100 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.4651 3.8100 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2690 -0.1900 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8671 2.3100 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0010 -0.1900 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 3.4030 -3.6900 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5369 -5.1900 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2690 -5.1900 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7331 2.8100 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 7.7331 3.8100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5991 2.3100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8671 4.3100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5991 4.3100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8671 1.3100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0010 0.8100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.4651 2.8100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8671 5.3100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5991 5.3100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7331 5.8100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1350 -0.6900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1350 -1.6900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2690 -2.1900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0010 -2.1900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2690 -3.1900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0010 -3.1900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1350 -3.6900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.4030 -4.6900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7331 2.1900 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.2006 1.8351 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9976 1.8351 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 2.6200 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 4.0000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1360 4.0000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.7890 1.3926 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.3905 0.7023 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 5.6200 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1360 5.6200 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7331 6.4300 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.5380 -0.5000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -1.8800 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.5380 -1.8800 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.8681 2.6200 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.5380 -3.5000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1350 -4.3100 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5369 -5.8100 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -4.8800 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2690 -5.8100 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8059 -4.8800 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 15 2 0 0 0 0 + 2 17 1 0 0 0 0 + 2 43 1 0 0 0 0 + 3 17 2 0 0 0 0 + 4 21 2 0 0 0 0 + 5 10 1 0 0 0 0 + 5 15 1 0 0 0 0 + 5 32 1 0 0 0 0 + 6 16 1 0 0 0 0 + 6 21 1 0 0 0 0 + 6 40 1 0 0 0 0 + 7 25 1 0 0 0 0 + 7 28 2 0 0 0 0 + 8 28 1 0 0 0 0 + 8 46 1 0 0 0 0 + 8 47 1 0 0 0 0 + 9 28 1 0 0 0 0 + 9 48 1 0 0 0 0 + 9 49 1 0 0 0 0 + 10 11 1 0 0 0 0 + 10 12 1 0 0 0 0 + 10 29 1 0 0 0 0 + 11 13 2 0 0 0 0 + 11 14 1 0 0 0 0 + 12 17 1 0 0 0 0 + 12 30 1 0 0 0 0 + 12 31 1 0 0 0 0 + 13 18 1 0 0 0 0 + 13 33 1 0 0 0 0 + 14 19 2 0 0 0 0 + 14 34 1 0 0 0 0 + 15 16 1 0 0 0 0 + 16 35 1 0 0 0 0 + 16 36 1 0 0 0 0 + 18 20 2 0 0 0 0 + 18 37 1 0 0 0 0 + 19 20 1 0 0 0 0 + 19 38 1 0 0 0 0 + 20 39 1 0 0 0 0 + 21 22 1 0 0 0 0 + 22 23 2 0 0 0 0 + 22 24 1 0 0 0 0 + 23 25 1 0 0 0 0 + 23 41 1 0 0 0 0 + 24 26 2 0 0 0 0 + 24 42 1 0 0 0 0 + 25 27 2 0 0 0 0 + 26 27 1 0 0 0 0 + 26 44 1 0 0 0 0 + 27 45 1 0 0 0 0 +M END +> +10000019 + +> +1 + +> +585 + +> +7 + +> +5 + +> +8 + +> +AAADceB7uAAAAAAAAAAAAAAAAAAAAAAAAAAwYAAAAAAAAAABQAAAHgAQCAAADCjBmAQxyILAAgCoAiXSfACCAAEkAgAIiIGIZMgIYDqAlbGUYQhmlgCIyYecyKCOAAAAQAAAAAAAAACAAAAAAAAAAAAAAA== + +> +3-[[2-[(3-guanidinobenzoyl)amino]acetyl]amino]-3-phenyl-propanoic acid + +> +3-[[2-[[(3-guanidinophenyl)-oxomethyl]amino]-1-oxoethyl]amino]-3-phenylpropanoic acid + +> +3-[[2-[[3-(diaminomethylideneamino)benzoyl]amino]acetyl]amino]-3-phenylpropanoic acid + +> +3-[2-[[3-[bis(azanyl)methylideneamino]phenyl]carbonylamino]ethanoylamino]-3-phenyl-propanoic acid + +> +3-[[2-[(3-guanidinobenzoyl)amino]acetyl]amino]-3-phenyl-propionic acid + +> +InChI=1S/C19H21N5O4/c20-19(21)23-14-8-4-7-13(9-14)18(28)22-11-16(25)24-15(10-17(26)27)12-5-2-1-3-6-12/h1-9,15H,10-11H2,(H,22,28)(H,24,25)(H,26,27)(H4,20,21,23) + +> +XWMGBLCMRLASDS-UHFFFAOYSA-N + +> +0.1 + +> +383.159354 + +> +C19H21N5O4 + +> +383.40114 + +> +C1=CC=C(C=C1)C(CC(=O)O)NC(=O)CNC(=O)C2=CC(=CC=C2)N=C(N)N + +> +C1=CC=C(C=C1)C(CC(=O)O)NC(=O)CNC(=O)C2=CC(=CC=C2)N=C(N)N + +> +160 + +> +383.159354 + +> +0 + +> +28 + +> +0 + +> +1 + +> +0 + +> +0 + +> +0 + +> +1 + +> +8 + +> +1 +5 +255 + +> +10 11 3 +11 13 8 +11 14 8 +13 18 8 +14 19 8 +18 20 8 +19 20 8 +22 23 8 +22 24 8 +23 25 8 +24 26 8 +25 27 8 +26 27 8 + +$$$$ +10000020 + -OEChem-11110908022D + + 44 47 0 0 0 0 0 0 0999 V2000 + 3.4030 -1.4595 0.0000 S 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4250 3.0466 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5309 -2.4595 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9030 -0.5935 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9030 -2.3256 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8365 -0.6718 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 6.9473 -3.2643 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5369 -0.9595 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 8.2364 -0.4981 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.6415 0.4097 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.9473 -1.6548 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0010 -1.9595 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.6357 0.3018 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2579 -0.7043 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0010 -2.9595 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9734 -1.1651 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5309 -2.4595 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1350 -1.4595 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.2380 1.3247 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2690 -1.9595 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.2263 1.1087 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1350 -3.4595 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7479 -1.0833 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2690 -2.9595 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.8285 2.1317 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8227 2.0237 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0156 3.8536 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8439 -0.2428 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9065 -1.7815 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1350 -0.8395 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1399 -3.8536 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.6216 1.3916 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.8427 1.0418 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1350 -4.0795 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -3.2695 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.0030 -0.5182 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.3130 -1.3384 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4928 -1.6484 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.1889 2.5240 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5369 -0.3395 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -1.2695 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.5159 3.4874 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.3817 4.3539 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5152 4.2197 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 4 2 0 0 0 0 + 1 5 2 0 0 0 0 + 1 8 1 0 0 0 0 + 1 20 1 0 0 0 0 + 2 25 1 0 0 0 0 + 2 27 1 0 0 0 0 + 3 17 2 0 0 0 0 + 6 13 1 0 0 0 0 + 6 16 1 0 0 0 0 + 6 23 1 0 0 0 0 + 7 15 1 0 0 0 0 + 7 17 1 0 0 0 0 + 7 31 1 0 0 0 0 + 8 40 1 0 0 0 0 + 8 41 1 0 0 0 0 + 9 10 1 0 0 0 0 + 9 14 1 0 0 0 0 + 9 16 2 0 0 0 0 + 10 13 2 0 0 0 0 + 10 19 1 0 0 0 0 + 11 12 1 0 0 0 0 + 11 14 2 0 0 0 0 + 11 17 1 0 0 0 0 + 12 15 2 0 0 0 0 + 12 18 1 0 0 0 0 + 13 21 1 0 0 0 0 + 14 28 1 0 0 0 0 + 15 22 1 0 0 0 0 + 16 29 1 0 0 0 0 + 18 20 2 0 0 0 0 + 18 30 1 0 0 0 0 + 19 25 2 0 0 0 0 + 19 32 1 0 0 0 0 + 20 24 1 0 0 0 0 + 21 26 2 0 0 0 0 + 21 33 1 0 0 0 0 + 22 24 2 0 0 0 0 + 22 34 1 0 0 0 0 + 23 36 1 0 0 0 0 + 23 37 1 0 0 0 0 + 23 38 1 0 0 0 0 + 24 35 1 0 0 0 0 + 25 26 1 0 0 0 0 + 26 39 1 0 0 0 0 + 27 42 1 0 0 0 0 + 27 43 1 0 0 0 0 + 27 44 1 0 0 0 0 +M END +> +10000020 + +> +1 + +> +732 + +> +5 + +> +2 + +> +3 + +> +AAADceB7OABAAAAAAAAAAAAAAAAAAWLAAAAwYAAAAAAAAFgB9AAAHgQQQAAADAzB3gY+x/PMFAKoAzV3VHDCiDAxIiAI2Dk+bNgMJvLEtZuEMChk1BHI6Ye82PKOgEQBgAACAAAAiAMAAAQAAAAAAAAAAA== + +> +(3Z)-3-[(5-methoxy-1-methyl-indol-3-yl)methylene]-2-oxo-indoline-5-sulfonamide + +> +(3Z)-3-[(5-methoxy-1-methyl-3-indolyl)methylene]-2-oxo-5-indolinesulfonamide + +> +(3Z)-3-[(5-methoxy-1-methylindol-3-yl)methylidene]-2-oxo-1H-indole-5-sulfonamide + +> +(3Z)-3-[(5-methoxy-1-methyl-indol-3-yl)methylidene]-2-oxo-1H-indole-5-sulfonamide + +> +(3Z)-2-keto-3-[(5-methoxy-1-methyl-indol-3-yl)methylene]indoline-5-sulfonamide + +> +InChI=1S/C19H17N3O4S/c1-22-10-11(14-8-12(26-2)3-6-18(14)22)7-16-15-9-13(27(20,24)25)4-5-17(15)21-19(16)23/h3-10H,1-2H3,(H,21,23)(H2,20,24,25)/b16-7- + +> +GNCKEUJKZQDWAH-APSNUPSMSA-N + +> +1.5 + +> +383.093977 + +> +C19H17N3O4S + +> +383.42098 + +> +CN1C=C(C2=C1C=CC(=C2)OC)C=C3C4=C(C=CC(=C4)S(=O)(=O)N)NC3=O + +> +CN1C=C(C2=C1C=CC(=C2)OC)/C=C\3/C4=C(C=CC(=C4)S(=O)(=O)N)NC3=O + +> +103 + +> +383.093977 + +> +0 + +> +27 + +> +0 + +> +0 + +> +1 + +> +0 + +> +0 + +> +1 + +> +2 + +> +1 +5 +255 + +> +10 13 8 +10 19 8 +12 15 8 +12 18 8 +13 21 8 +15 22 8 +18 20 8 +19 25 8 +20 24 8 +21 26 8 +22 24 8 +25 26 8 +6 13 8 +6 16 8 +9 10 8 +9 16 8 + +$$$$ +10000021 + -OEChem-11110908022D + + 44 46 0 0 0 0 0 0 0999 V2000 + 9.7942 -0.1900 0.0000 S 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -0.1900 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 11.5263 0.8100 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -1.6900 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0622 2.8100 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 11.5263 -2.1900 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 12.3923 -0.6900 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 9.7942 -2.1900 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 13.2583 -2.1900 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 1.3100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9282 1.3100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 0.8100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0622 0.8100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.7942 0.8100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 -0.1900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 1.3100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 2.3100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9282 2.3100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6603 -0.6900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -0.1900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -0.6900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 0.8100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6603 -1.6900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.5263 -0.1900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.3923 -1.6900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -0.6900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -0.6900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0622 0.1900 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4048 0.7023 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.0063 1.3926 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8671 -0.5000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 1.9300 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.6592 2.6200 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.4651 2.6200 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -1.3100 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0611 1.1200 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.5263 -2.8100 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2573 -1.8800 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.7942 -2.8100 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 13.2583 -2.8100 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 13.7953 -1.8800 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3100 -1.2269 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 -1.0000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.6900 -0.1531 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 14 1 0 0 0 0 + 1 19 1 0 0 0 0 + 2 26 1 0 0 0 0 + 2 27 1 0 0 0 0 + 3 24 2 0 0 0 0 + 4 26 2 0 0 0 0 + 5 17 1 0 0 0 0 + 5 18 2 0 0 0 0 + 6 23 1 0 0 0 0 + 6 25 1 0 0 0 0 + 6 37 1 0 0 0 0 + 7 24 1 0 0 0 0 + 7 25 2 0 0 0 0 + 8 23 1 0 0 0 0 + 8 38 1 0 0 0 0 + 8 39 1 0 0 0 0 + 9 25 1 0 0 0 0 + 9 40 1 0 0 0 0 + 9 41 1 0 0 0 0 + 10 12 1 0 0 0 0 + 10 13 1 0 0 0 0 + 10 17 2 0 0 0 0 + 11 13 2 0 0 0 0 + 11 14 1 0 0 0 0 + 11 18 1 0 0 0 0 + 12 15 2 0 0 0 0 + 12 16 1 0 0 0 0 + 13 28 1 0 0 0 0 + 14 29 1 0 0 0 0 + 14 30 1 0 0 0 0 + 15 21 1 0 0 0 0 + 15 31 1 0 0 0 0 + 16 22 2 0 0 0 0 + 16 32 1 0 0 0 0 + 17 33 1 0 0 0 0 + 18 34 1 0 0 0 0 + 19 23 2 0 0 0 0 + 19 24 1 0 0 0 0 + 20 21 2 0 0 0 0 + 20 22 1 0 0 0 0 + 20 26 1 0 0 0 0 + 21 35 1 0 0 0 0 + 22 36 1 0 0 0 0 + 27 42 1 0 0 0 0 + 27 43 1 0 0 0 0 + 27 44 1 0 0 0 0 +M END +> +10000021 + +> +1 + +> +643 + +> +7 + +> +3 + +> +6 + +> +AAADceB7sABAAAAAAAAAAAAAAAAAAAAAAAA8QIAAAAAAAAABwAAAHgQQAAAADADF2ga/iJLIFAioAjH3fACCgCE1ADAJ2CEoTNiIJvLgtZmEMQhkwAHo6ceYyPCOwAAAAAAAAACAAAAAAAAAAAAAAAAAAA== + +> +methyl 4-[5-[(2,6-diamino-4-oxo-1H-pyrimidin-5-yl)sulfanylmethyl]-3-pyridyl]benzoate + +> +4-[5-[[(2,6-diamino-4-oxo-1H-pyrimidin-5-yl)thio]methyl]-3-pyridyl]benzoic acid methyl ester + +> +methyl 4-[5-[(2,6-diamino-4-oxo-1H-pyrimidin-5-yl)sulfanylmethyl]pyridin-3-yl]benzoate + +> +methyl 4-[5-[[2,6-bis(azanyl)-4-oxo-1H-pyrimidin-5-yl]sulfanylmethyl]pyridin-3-yl]benzoate + +> +4-[5-[[(2,6-diamino-4-keto-1H-pyrimidin-5-yl)thio]methyl]-3-pyridyl]benzoic acid methyl ester + +> +InChI=1S/C18H17N5O3S/c1-26-17(25)12-4-2-11(3-5-12)13-6-10(7-21-8-13)9-27-14-15(19)22-18(20)23-16(14)24/h2-8H,9H2,1H3,(H5,19,20,22,23,24) + +> +UOQJMHJERKBWEP-UHFFFAOYSA-N + +> +0.9 + +> +383.10521 + +> +C18H17N5O3S + +> +383.42428 + +> +COC(=O)C1=CC=C(C=C1)C2=CC(=CN=C2)CSC3=C(NC(=NC3=O)N)N + +> +COC(=O)C1=CC=C(C=C1)C2=CC(=CN=C2)CSC3=C(NC(=NC3=O)N)N + +> +133 + +> +383.10521 + +> +0 + +> +27 + +> +0 + +> +0 + +> +0 + +> +0 + +> +0 + +> +1 + +> +17 + +> +1 +5 +255 + +> +10 13 8 +10 17 8 +11 13 8 +11 18 8 +12 15 8 +12 16 8 +15 21 8 +16 22 8 +19 23 8 +19 24 8 +20 21 8 +20 22 8 +5 17 8 +5 18 8 +6 23 8 +6 25 8 +7 24 8 +7 25 8 + +$$$$ +10000022 + -OEChem-11110908022D + + 44 47 0 0 0 0 0 0 0999 V2000 + 4.5274 3.2747 0.0000 S 0 0 0 0 0 0 0 0 0 0 0 0 + 5.3923 0.0037 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 12.0220 -2.1604 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 + 10.7304 -3.3144 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7328 1.1599 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3518 2.9192 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0321 2.3622 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3991 0.4142 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 11.0431 -2.3646 0.0000 N 0 3 0 0 0 0 0 0 0 0 0 0 + 3.3424 0.9512 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0274 1.7359 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3219 1.1599 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 2.1505 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.7029 2.9192 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7183 2.6870 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0274 1.7359 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.3364 2.6870 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.7123 0.9512 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0547 2.1505 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.3780 0.6183 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.3374 3.3144 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0443 -0.1274 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.0232 0.0767 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.7316 -1.0773 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6895 -0.6690 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.3979 -1.8230 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.3768 -1.6189 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1161 0.3740 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.8724 0.6294 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.7081 1.0720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3034 0.5401 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.6208 2.6410 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4518 1.8609 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9426 3.4910 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.1805 3.2531 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4487 1.9031 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5719 1.2072 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5267 3.9048 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7470 3.5038 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9278 3.1251 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.2171 0.6656 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.1247 -1.2039 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.2964 -0.5424 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2040 -2.4119 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 15 1 0 0 0 0 + 1 17 1 0 0 0 0 + 2 18 2 0 0 0 0 + 3 9 1 0 0 0 0 + 4 9 2 0 0 0 0 + 5 8 1 0 0 0 0 + 5 18 1 0 0 0 0 + 5 19 1 0 0 0 0 + 6 17 1 0 0 0 0 + 6 19 2 0 0 0 0 + 7 19 1 0 0 0 0 + 7 21 1 0 0 0 0 + 7 36 1 0 0 0 0 + 8 20 2 0 0 0 0 + 9 27 1 0 0 0 0 + 10 11 1 0 0 0 0 + 10 12 1 0 0 0 0 + 10 28 1 0 0 0 0 + 10 29 1 0 0 0 0 + 11 15 2 0 0 0 0 + 11 16 1 0 0 0 0 + 12 13 1 0 0 0 0 + 12 30 1 0 0 0 0 + 12 31 1 0 0 0 0 + 13 14 1 0 0 0 0 + 13 32 1 0 0 0 0 + 13 33 1 0 0 0 0 + 14 15 1 0 0 0 0 + 14 34 1 0 0 0 0 + 14 35 1 0 0 0 0 + 16 17 2 0 0 0 0 + 16 18 1 0 0 0 0 + 20 22 1 0 0 0 0 + 20 37 1 0 0 0 0 + 21 38 1 0 0 0 0 + 21 39 1 0 0 0 0 + 21 40 1 0 0 0 0 + 22 23 2 0 0 0 0 + 22 24 1 0 0 0 0 + 23 25 1 0 0 0 0 + 23 41 1 0 0 0 0 + 24 26 2 0 0 0 0 + 24 42 1 0 0 0 0 + 25 27 2 0 0 0 0 + 25 43 1 0 0 0 0 + 26 27 1 0 0 0 0 + 26 44 1 0 0 0 0 +M CHG 2 3 -1 9 1 +M END +> +10000022 + +> +1 + +> +656 + +> +6 + +> +1 + +> +3 + +> +AAADceB7sABAAAAAAAAAAAAAAAAAASAAAAAwYIAAAAAAAEgBwAAAHgQcAAAADAjB2AQzwYLQQAitAjVzdwCTAIFkChA/iJEoZNqIILqg3ZGEIYh4iQIoyUcYiMCOiAAAAAAAAAAQAAAAAAgAAAAAAAAAAA== + +> +2-(methylamino)-3-[(E)-(4-nitrophenyl)methyleneamino]-5,6,7,8-tetrahydrobenzothiopheno[2,3-d]pyrimidin-4-one + +> +2-(methylamino)-3-[(E)-(4-nitrophenyl)methyleneamino]-5,6,7,8-tetrahydrobenzothiopheno[2,3-d]pyrimidin-4-one + +> +2-(methylamino)-3-[(E)-(4-nitrophenyl)methylideneamino]-5,6,7,8-tetrahydro-[1]benzothiolo[2,3-d]pyrimidin-4-one + +> +2-(methylamino)-3-[(E)-(4-nitrophenyl)methylideneamino]-5,6,7,8-tetrahydro-[1]benzothiolo[2,3-d]pyrimidin-4-one + +> +2-(methylamino)-3-[(E)-(4-nitrobenzylidene)amino]-5,6,7,8-tetrahydrobenzothiopheno[2,3-d]pyrimidin-4-one + +> +InChI=1S/C18H17N5O3S/c1-19-18-21-16-15(13-4-2-3-5-14(13)27-16)17(24)22(18)20-10-11-6-8-12(9-7-11)23(25)26/h6-10H,2-5H2,1H3,(H,19,21)/b20-10+ + +> +CRNWKLJTGHLOJV-KEBDBYFISA-N + +> +3.4 + +> +383.10521 + +> +C18H17N5O3S + +> +383.42428 + +> +CNC1=NC2=C(C3=C(S2)CCCC3)C(=O)N1N=CC4=CC=C(C=C4)[N+](=O)[O-] + +> +CNC1=NC2=C(C3=C(S2)CCCC3)C(=O)N1/N=C/C4=CC=C(C=C4)[N+](=O)[O-] + +> +100 + +> +383.10521 + +> +0 + +> +27 + +> +0 + +> +0 + +> +1 + +> +0 + +> +0 + +> +1 + +> +4 + +> +1 +5 +255 + +> +1 15 8 +1 17 8 +11 15 8 +11 16 8 +16 17 8 +16 18 8 +22 23 8 +22 24 8 +23 25 8 +24 26 8 +25 27 8 +26 27 8 +5 18 8 +5 19 8 +6 17 8 +6 19 8 + +$$$$ +10000023 + -OEChem-11110908022D + + 53 55 0 1 0 0 0 0 0999 V2000 + 5.0841 -2.2731 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7675 -3.4400 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.6129 -2.5513 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4084 1.2084 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7105 3.0308 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1499 2.2793 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5838 1.3783 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 6.5838 1.3783 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2073 0.5965 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9603 0.5965 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.9848 -0.3784 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0838 -0.8123 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1829 -0.3784 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.9900 -1.8497 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8544 -0.9519 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0738 1.0161 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5662 -1.1411 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8461 -2.4431 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7845 -1.9912 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1919 0.5870 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5854 -1.1372 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9749 -0.3695 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.7133 3.1055 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -0.5920 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2794 4.0065 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9978 -3.2694 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5916 -4.0065 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.5122 -2.1139 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9677 1.4477 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1424 1.6473 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4459 1.9828 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7659 0.3275 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5939 1.0812 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5317 2.3256 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4098 -0.6764 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0705 1.6361 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8322 -1.7012 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.3149 -1.6950 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.8620 0.0124 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.1380 -1.1965 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.3955 -0.7300 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.8380 4.2755 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0104 4.5651 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.7208 3.7375 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6155 -3.3229 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.3801 -3.2159 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9443 -3.8871 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2403 -4.5174 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.1025 -4.3577 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.9428 -3.4956 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.7834 -2.6715 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.0698 -1.8428 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2411 -1.5564 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 14 1 0 0 0 0 + 1 26 1 0 0 0 0 + 2 18 1 0 0 0 0 + 2 27 1 0 0 0 0 + 3 19 1 0 0 0 0 + 3 28 1 0 0 0 0 + 4 20 2 0 0 0 0 + 5 23 2 0 0 0 0 + 7 6 1 1 0 0 0 + 6 23 1 0 0 0 0 + 6 34 1 0 0 0 0 + 7 8 1 0 0 0 0 + 7 10 1 0 0 0 0 + 7 29 1 0 0 0 0 + 8 9 1 0 0 0 0 + 8 30 1 0 0 0 0 + 8 31 1 0 0 0 0 + 9 11 1 0 0 0 0 + 9 32 1 0 0 0 0 + 9 33 1 0 0 0 0 + 10 13 1 0 0 0 0 + 10 16 2 0 0 0 0 + 11 12 1 0 0 0 0 + 11 15 2 0 0 0 0 + 12 13 1 0 0 0 0 + 12 14 2 0 0 0 0 + 13 17 2 0 0 0 0 + 14 18 1 0 0 0 0 + 15 19 1 0 0 0 0 + 15 35 1 0 0 0 0 + 16 20 1 0 0 0 0 + 16 36 1 0 0 0 0 + 17 21 1 0 0 0 0 + 17 37 1 0 0 0 0 + 18 19 2 0 0 0 0 + 20 22 1 0 0 0 0 + 21 22 2 0 0 0 0 + 21 38 1 0 0 0 0 + 22 24 1 0 0 0 0 + 23 25 1 0 0 0 0 + 24 39 1 0 0 0 0 + 24 40 1 0 0 0 0 + 24 41 1 0 0 0 0 + 25 42 1 0 0 0 0 + 25 43 1 0 0 0 0 + 25 44 1 0 0 0 0 + 26 45 1 0 0 0 0 + 26 46 1 0 0 0 0 + 26 47 1 0 0 0 0 + 27 48 1 0 0 0 0 + 27 49 1 0 0 0 0 + 27 50 1 0 0 0 0 + 28 51 1 0 0 0 0 + 28 52 1 0 0 0 0 + 28 53 1 0 0 0 0 +M END +> +10000023 + +> +1 + +> +722 + +> +5 + +> +1 + +> +4 + +> +AAADceB6OAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAABkgAAAABAAAAHgAQAAAADCzBmAYyBoLABACIAqFSEACCCAAgIAAAiIEOiMgNJyKEsRqEMCIl1hWKqYeQ8P8OoAABCAAYQABAAAIQADCAAAAAAAAAAA== + +> +N-[(7S)-1,2,3-trimethoxy-10-methyl-9-oxo-6,7-dihydro-5H-benzo[a]heptalen-7-yl]acetamide + +> +N-[(7S)-1,2,3-trimethoxy-10-methyl-9-oxo-6,7-dihydro-5H-benzo[a]heptalen-7-yl]acetamide + +> +N-[(7S)-1,2,3-trimethoxy-10-methyl-9-oxo-6,7-dihydro-5H-benzo[a]heptalen-7-yl]acetamide + +> +N-[(7S)-1,2,3-trimethoxy-10-methyl-9-oxo-6,7-dihydro-5H-benzo[a]heptalen-7-yl]ethanamide + +> +N-[(7S)-9-keto-1,2,3-trimethoxy-10-methyl-6,7-dihydro-5H-benzo[a]heptalen-7-yl]acetamide + +> +InChI=1S/C22H25NO5/c1-12-6-8-15-16(11-18(12)25)17(23-13(2)24)9-7-14-10-19(26-3)21(27-4)22(28-5)20(14)15/h6,8,10-11,17H,7,9H2,1-5H3,(H,23,24)/t17-/m0/s1 + +> +OMDYBEVVCSGYQW-KRWDZBQOSA-N + +> +1.7 + +> +383.173273 + +> +C22H25NO5 + +> +383.4376 + +> +CC1=CC=C2C(=CC1=O)C(CCC3=CC(=C(C(=C32)OC)OC)OC)NC(=O)C + +> +CC1=CC=C2C(=CC1=O)[C@H](CCC3=CC(=C(C(=C32)OC)OC)OC)NC(=O)C + +> +73.9 + +> +383.173273 + +> +0 + +> +28 + +> +1 + +> +0 + +> +0 + +> +0 + +> +0 + +> +1 + +> +2 + +> +1 +5 +255 + +> +10 13 8 +10 16 8 +11 12 8 +11 15 8 +12 14 8 +13 17 8 +14 18 8 +15 19 8 +16 20 8 +17 21 8 +18 19 8 +20 22 8 +21 22 8 +7 6 5 + +$$$$ +10000024 + -OEChem-11110908022D + + 53 55 0 0 0 0 0 0 0999 V2000 + 3.7339 0.1474 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5000 2.5222 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0000 -0.0759 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 3.0603 1.3141 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5797 -2.8782 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0000 2.6562 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0000 1.6562 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.8660 1.1562 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7321 1.6562 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7321 2.6562 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.8660 3.1562 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.6260 1.1215 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5000 0.7901 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0000 1.6562 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.6260 3.1908 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5321 1.6353 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.6144 0.1216 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5321 2.6770 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4746 -0.3884 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7427 -0.3684 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9076 -0.8374 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5000 2.5222 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4631 -1.3883 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7311 -1.3683 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5913 -1.8783 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1415 -1.4802 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 3.3882 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4399 -3.3882 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4675 0.6812 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2646 0.6812 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2646 3.6311 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4675 3.6311 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.4631 2.9662 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.6188 3.8108 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0678 1.3233 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0678 2.9890 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0151 -0.0846 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2094 -0.0522 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.4902 -0.6254 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2176 -1.3744 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6077 1.9116 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.9174 2.3101 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9964 -1.7045 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.1906 -1.6721 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5400 -1.9552 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6665 -1.8787 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.7430 -1.0053 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5369 3.6982 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 3.0782 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.6900 3.9251 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.1237 -3.9215 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9732 -3.7044 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.7561 -2.8549 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 13 1 0 0 0 0 + 1 21 1 0 0 0 0 + 2 14 1 0 0 0 0 + 2 22 1 0 0 0 0 + 3 13 2 0 0 0 0 + 4 14 2 0 0 0 0 + 5 25 1 0 0 0 0 + 5 28 1 0 0 0 0 + 6 7 1 0 0 0 0 + 6 11 1 0 0 0 0 + 6 33 1 0 0 0 0 + 7 8 1 0 0 0 0 + 7 13 1 0 0 0 0 + 7 14 1 0 0 0 0 + 8 9 1 0 0 0 0 + 8 29 1 0 0 0 0 + 8 30 1 0 0 0 0 + 9 10 2 0 0 0 0 + 9 12 1 0 0 0 0 + 10 11 1 0 0 0 0 + 10 15 1 0 0 0 0 + 11 31 1 0 0 0 0 + 11 32 1 0 0 0 0 + 12 16 2 0 0 0 0 + 12 17 1 0 0 0 0 + 15 18 2 0 0 0 0 + 15 34 1 0 0 0 0 + 16 18 1 0 0 0 0 + 16 35 1 0 0 0 0 + 17 19 2 0 0 0 0 + 17 20 1 0 0 0 0 + 18 36 1 0 0 0 0 + 19 23 1 0 0 0 0 + 19 37 1 0 0 0 0 + 20 24 2 0 0 0 0 + 20 38 1 0 0 0 0 + 21 26 1 0 0 0 0 + 21 39 1 0 0 0 0 + 21 40 1 0 0 0 0 + 22 27 1 0 0 0 0 + 22 41 1 0 0 0 0 + 22 42 1 0 0 0 0 + 23 25 2 0 0 0 0 + 23 43 1 0 0 0 0 + 24 25 1 0 0 0 0 + 24 44 1 0 0 0 0 + 26 45 1 0 0 0 0 + 26 46 1 0 0 0 0 + 26 47 1 0 0 0 0 + 27 48 1 0 0 0 0 + 27 49 1 0 0 0 0 + 27 50 1 0 0 0 0 + 28 51 1 0 0 0 0 + 28 52 1 0 0 0 0 + 28 53 1 0 0 0 0 +M END +> +10000024 + +> +1 + +> +525 + +> +6 + +> +1 + +> +8 + +> +AAADceB6OAAAAAAAAAAAAAAAAAAAAAAAAAA8YIAAAAAAAACxQAAAHgAQAAAADIzhmAYyDoLABACIAiDSCACCCAAgIAAIiIGODIgOJjaMsTuHMChl8BGYqAeYyPCOoAACAAAQAABAAAQAACAAAAAAAAAAAA== + +> +diethyl 5-(4-methoxyphenyl)-2,4-dihydro-1H-isoquinoline-3,3-dicarboxylate + +> +5-(4-methoxyphenyl)-2,4-dihydro-1H-isoquinoline-3,3-dicarboxylic acid diethyl ester + +> +diethyl 5-(4-methoxyphenyl)-2,4-dihydro-1H-isoquinoline-3,3-dicarboxylate + +> +diethyl 5-(4-methoxyphenyl)-2,4-dihydro-1H-isoquinoline-3,3-dicarboxylate + +> +5-(4-methoxyphenyl)-2,4-dihydro-1H-isoquinoline-3,3-dicarboxylic acid diethyl ester + +> +InChI=1S/C22H25NO5/c1-4-27-20(24)22(21(25)28-5-2)13-19-16(14-23-22)7-6-8-18(19)15-9-11-17(26-3)12-10-15/h6-12,23H,4-5,13-14H2,1-3H3 + +> +CRVMXOYIXCFDJP-UHFFFAOYSA-N + +> +3.5 + +> +383.173273 + +> +C22H25NO5 + +> +383.4376 + +> +CCOC(=O)C1(CC2=C(C=CC=C2CN1)C3=CC=C(C=C3)OC)C(=O)OCC + +> +CCOC(=O)C1(CC2=C(C=CC=C2CN1)C3=CC=C(C=C3)OC)C(=O)OCC + +> +73.9 + +> +383.173273 + +> +0 + +> +28 + +> +0 + +> +0 + +> +0 + +> +0 + +> +0 + +> +1 + +> +1 + +> +1 +5 +255 + +> +10 15 8 +12 16 8 +15 18 8 +16 18 8 +17 19 8 +17 20 8 +19 23 8 +20 24 8 +23 25 8 +24 25 8 +9 10 8 +9 12 8 + +$$$$ +10000025 + -OEChem-11110908022D + + 53 55 0 1 0 0 0 0 0999 V2000 + 7.8301 0.3170 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0981 -1.6830 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2321 -0.1830 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 2.1830 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 2.1830 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 0.6830 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 5.9641 -0.1830 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 6.8301 0.3170 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 6.4641 -1.0490 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3301 1.1830 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0981 -0.6830 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4641 -1.0490 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.9641 -1.9151 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 1.6830 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2321 -2.1830 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2321 -3.1830 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 3.1830 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0981 -3.6830 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.3660 -3.6830 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 3.6830 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0981 -4.6830 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.3660 -4.6830 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 4.6830 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 3.1830 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2321 -5.1830 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 5.1830 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 3.6830 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 4.6830 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.9906 -0.2819 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7741 -0.5121 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8051 1.5815 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8552 1.5815 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8441 0.6830 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4641 -1.6690 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0841 -1.0490 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4641 -0.4290 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4272 -1.6051 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6541 -2.4520 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.5010 -2.2251 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0200 -1.6004 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.6215 -2.2907 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2087 3.0753 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8101 3.7656 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6350 -3.3730 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8291 -3.3730 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6350 -4.9930 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8291 -4.9930 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2690 4.9930 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 2.5630 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2321 -5.8030 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 5.8030 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 3.3730 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 4.9930 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8 1 1 1 0 0 0 + 1 10 1 0 0 0 0 + 2 11 1 0 0 0 0 + 2 15 1 0 0 0 0 + 3 11 2 0 0 0 0 + 4 14 1 0 0 0 0 + 4 17 1 0 0 0 0 + 5 14 2 0 0 0 0 + 7 6 1 6 0 0 0 + 6 14 1 0 0 0 0 + 6 33 1 0 0 0 0 + 7 8 1 0 0 0 0 + 7 9 1 0 0 0 0 + 7 11 1 0 0 0 0 + 8 10 1 0 0 0 0 + 8 29 1 0 0 0 0 + 9 12 1 0 0 0 0 + 9 13 1 0 0 0 0 + 9 30 1 0 0 0 0 + 10 31 1 0 0 0 0 + 10 32 1 0 0 0 0 + 12 34 1 0 0 0 0 + 12 35 1 0 0 0 0 + 12 36 1 0 0 0 0 + 13 37 1 0 0 0 0 + 13 38 1 0 0 0 0 + 13 39 1 0 0 0 0 + 15 16 1 0 0 0 0 + 15 40 1 0 0 0 0 + 15 41 1 0 0 0 0 + 16 18 2 0 0 0 0 + 16 19 1 0 0 0 0 + 17 20 1 0 0 0 0 + 17 42 1 0 0 0 0 + 17 43 1 0 0 0 0 + 18 21 1 0 0 0 0 + 18 44 1 0 0 0 0 + 19 22 2 0 0 0 0 + 19 45 1 0 0 0 0 + 20 23 2 0 0 0 0 + 20 24 1 0 0 0 0 + 21 25 2 0 0 0 0 + 21 46 1 0 0 0 0 + 22 25 1 0 0 0 0 + 22 47 1 0 0 0 0 + 23 26 1 0 0 0 0 + 23 48 1 0 0 0 0 + 24 27 2 0 0 0 0 + 24 49 1 0 0 0 0 + 25 50 1 0 0 0 0 + 26 28 2 0 0 0 0 + 26 51 1 0 0 0 0 + 27 28 1 0 0 0 0 + 27 52 1 0 0 0 0 + 28 53 1 0 0 0 0 +M END +> +10000025 + +> +1 + +> +525 + +> +5 + +> +1 + +> +10 + +> +AAADceB6OAAAAAAAAAAAAAAAEgAAAAAAAAAwYAAAAAAAAAABQAAAHgAQAAAADZyhmAIyCILABACIAiDSGAACAAAgAAAIiAGACIkKJjaAMTiPMAAn9AEYqAfCwCAOgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + +> +benzyl (2S)-2-(benzyloxycarbonylamino)-3-methyl-2-[(2S)-oxiran-2-yl]butanoate + +> +(2S)-2-(benzyloxycarbonylamino)-3-methyl-2-[(2S)-2-oxiranyl]butanoic acid benzyl ester + +> +benzyl (2S)-3-methyl-2-[(2S)-oxiran-2-yl]-2-(phenylmethoxycarbonylamino)butanoate + +> +phenylmethyl (2S)-3-methyl-2-[(2S)-oxiran-2-yl]-2-(phenylmethoxycarbonylamino)butanoate + +> +(2S)-2-(benzyloxycarbonylamino)-3-methyl-2-[(2S)-oxiran-2-yl]butyric acid benzyl ester + +> +InChI=1S/C22H25NO5/c1-16(2)22(19-15-26-19,20(24)27-13-17-9-5-3-6-10-17)23-21(25)28-14-18-11-7-4-8-12-18/h3-12,16,19H,13-15H2,1-2H3,(H,23,25)/t19-,22+/m1/s1 + +> +KUUZHJQGCWPUOI-KNQAVFIVSA-N + +> +3.7 + +> +383.173273 + +> +C22H25NO5 + +> +383.4376 + +> +CC(C)C(C1CO1)(C(=O)OCC2=CC=CC=C2)NC(=O)OCC3=CC=CC=C3 + +> +CC(C)[C@]([C@H]1CO1)(C(=O)OCC2=CC=CC=C2)NC(=O)OCC3=CC=CC=C3 + +> +77.2 + +> +383.173273 + +> +0 + +> +28 + +> +2 + +> +0 + +> +0 + +> +0 + +> +0 + +> +1 + +> +2 + +> +1 +5 +255 + +> +8 1 5 +16 18 8 +16 19 8 +18 21 8 +19 22 8 +20 23 8 +20 24 8 +21 25 8 +22 25 8 +23 26 8 +24 27 8 +26 28 8 +27 28 8 +7 6 6 + +$$$$ +10000026 + -OEChem-11110908022D + + 53 56 0 1 0 0 0 0 0999 V2000 + 7.1568 0.1100 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2398 -4.0694 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2473 -0.3006 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.1419 -3.4514 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7336 1.0376 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.3198 3.3950 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 3.8420 1.4464 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 3.5832 2.4124 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 5.3772 0.2033 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 6.1862 1.6046 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 2.0101 0.6876 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.7513 1.6535 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2908 0.6100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1693 -0.7749 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.9294 2.2737 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8198 4.2610 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.9124 -1.4440 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8804 1.9647 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.7045 -2.4221 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0883 0.9865 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8635 -1.1350 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.6236 2.6338 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4477 -3.0913 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0394 0.6775 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.6066 -1.8041 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.5746 2.3248 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3987 -2.7823 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.7825 1.3466 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0620 0.6254 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.3632 3.2334 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.8590 -0.1869 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7650 1.3824 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.2222 0.1050 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4188 0.5012 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.1600 1.4671 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.5392 2.2362 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8407 -1.3007 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5944 -0.5426 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4408 2.6554 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2204 2.8211 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2829 4.5710 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1298 4.7979 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.3568 3.9510 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1149 -2.6137 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.6276 0.5717 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.9924 -0.5285 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4947 3.2403 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.1963 -1.6125 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.0354 2.7396 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6501 -4.2610 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8370 -0.4922 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0130 -4.0578 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.1943 1.4525 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 13 2 0 0 0 0 + 2 23 1 0 0 0 0 + 2 50 1 0 0 0 0 + 3 24 1 0 0 0 0 + 3 51 1 0 0 0 0 + 4 27 1 0 0 0 0 + 4 52 1 0 0 0 0 + 5 28 1 0 0 0 0 + 5 53 1 0 0 0 0 + 6 7 1 0 0 0 0 + 6 8 1 0 0 0 0 + 6 16 1 0 0 0 0 + 7 9 1 0 0 0 0 + 7 11 1 0 0 0 0 + 7 29 1 1 0 0 0 + 8 10 1 0 0 0 0 + 8 12 1 0 0 0 0 + 8 30 1 6 0 0 0 + 9 13 1 0 0 0 0 + 9 14 1 0 0 0 0 + 9 31 1 0 0 0 0 + 10 13 1 0 0 0 0 + 10 15 1 0 0 0 0 + 10 32 1 0 0 0 0 + 11 12 1 0 0 0 0 + 11 33 1 0 0 0 0 + 11 34 1 0 0 0 0 + 12 35 1 0 0 0 0 + 12 36 1 0 0 0 0 + 14 17 1 0 0 0 0 + 14 37 1 0 0 0 0 + 14 38 1 0 0 0 0 + 15 18 1 0 0 0 0 + 15 39 1 0 0 0 0 + 15 40 1 0 0 0 0 + 16 41 1 0 0 0 0 + 16 42 1 0 0 0 0 + 16 43 1 0 0 0 0 + 17 19 2 0 0 0 0 + 17 21 1 0 0 0 0 + 18 20 2 0 0 0 0 + 18 22 1 0 0 0 0 + 19 23 1 0 0 0 0 + 19 44 1 0 0 0 0 + 20 24 1 0 0 0 0 + 20 45 1 0 0 0 0 + 21 25 2 0 0 0 0 + 21 46 1 0 0 0 0 + 22 26 2 0 0 0 0 + 22 47 1 0 0 0 0 + 23 27 2 0 0 0 0 + 24 28 2 0 0 0 0 + 25 27 1 0 0 0 0 + 25 48 1 0 0 0 0 + 26 28 1 0 0 0 0 + 26 49 1 0 0 0 0 +M END +> +10000026 + +> +1 + +> +525 + +> +6 + +> +4 + +> +4 + +> +AAADceB6OAAAAAAAAAAAAAAAAAAAAWAAAAA8YIAABgAAAAABQAAAHgAACAAADSzBmAQyBoMAAgCIAqBSAAACAAAgIAAIiAEOiIgJJjqCkRKEcAEkwBGJmAeYyPCOIAABAAAAQABAAAIAAACAAAAAAAAAAA== + +> +(1S,5R)-2,4-bis[(3,4-dihydroxyphenyl)methyl]-8-methyl-8-azabicyclo[3.2.1]octan-3-one + +> +(1S,5R)-2,4-bis[(3,4-dihydroxyphenyl)methyl]-8-methyl-8-azabicyclo[3.2.1]octan-3-one + +> +(1S,5R)-2,4-bis[(3,4-dihydroxyphenyl)methyl]-8-methyl-8-azabicyclo[3.2.1]octan-3-one + +> +(1S,5R)-2,4-bis[(3,4-dihydroxyphenyl)methyl]-8-methyl-8-azabicyclo[3.2.1]octan-3-one + +> +(1S,5R)-8-methyl-2,4-diprotocatechuyl-8-azabicyclo[3.2.1]octan-3-one + +> +InChI=1S/C22H25NO5/c1-23-16-4-5-17(23)15(9-13-3-7-19(25)21(27)11-13)22(28)14(16)8-12-2-6-18(24)20(26)10-12/h2-3,6-7,10-11,14-17,24-27H,4-5,8-9H2,1H3/t14?,15?,16-,17+ + +> +VPUBYIHEOHBDNM-UCUFBTAOSA-N + +> +2.9 + +> +383.173273 + +> +C22H25NO5 + +> +383.4376 + +> +CN1C2CCC1C(C(=O)C2CC3=CC(=C(C=C3)O)O)CC4=CC(=C(C=C4)O)O + +> +CN1[C@@H]2CC[C@H]1C(C(=O)C2CC3=CC(=C(C=C3)O)O)CC4=CC(=C(C=C4)O)O + +> +101 + +> +383.173273 + +> +0 + +> +28 + +> +2 + +> +2 + +> +0 + +> +0 + +> +0 + +> +1 + +> +998 + +> +1 +5 +255 + +> +10 15 3 +17 19 8 +17 21 8 +18 20 8 +18 22 8 +19 23 8 +20 24 8 +21 25 8 +22 26 8 +23 27 8 +24 28 8 +25 27 8 +26 28 8 +7 29 5 +8 30 6 +9 14 3 + +$$$$ +10000027 + -OEChem-11110908022D + + 42 43 0 0 0 0 0 0 0999 V2000 + 3.0000 1.5670 0.0000 S 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3301 2.0670 0.0000 S 0 0 0 0 0 0 0 0 0 0 0 0 + 4.7321 0.5670 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0000 1.5670 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 3.0000 2.5670 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8301 1.2010 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8301 2.9330 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5981 -2.9330 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4641 1.5670 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 3.0000 0.5670 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5981 0.0670 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4641 0.5670 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.8660 0.0670 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5981 -0.9330 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4641 -1.4330 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3301 0.0670 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.1340 0.0670 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3301 -0.9330 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.8660 -0.9330 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 1.5670 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.1340 -0.9330 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.0000 -1.4330 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4641 -2.4330 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.1962 2.5670 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3301 -2.9330 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0611 -1.2430 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.9272 1.8770 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8671 0.3770 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.5970 0.3770 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8671 -1.2430 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.4030 -1.2430 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 2.1870 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.3800 1.5670 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 0.9470 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.5970 -1.2430 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.0000 -2.0530 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5062 2.0301 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.7331 2.8770 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8862 3.1039 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8671 -3.2430 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.6401 -2.3961 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0201 -3.4699 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 4 2 0 0 0 0 + 1 5 2 0 0 0 0 + 1 10 1 0 0 0 0 + 1 20 1 0 0 0 0 + 2 6 2 0 0 0 0 + 2 7 2 0 0 0 0 + 2 9 1 0 0 0 0 + 2 24 1 0 0 0 0 + 3 11 1 0 0 0 0 + 3 13 1 0 0 0 0 + 8 23 2 0 0 0 0 + 9 12 1 0 0 0 0 + 9 27 1 0 0 0 0 + 10 13 1 0 0 0 0 + 10 17 2 0 0 0 0 + 11 12 2 0 0 0 0 + 11 14 1 0 0 0 0 + 12 16 1 0 0 0 0 + 13 19 2 0 0 0 0 + 14 15 2 0 0 0 0 + 14 26 1 0 0 0 0 + 15 18 1 0 0 0 0 + 15 23 1 0 0 0 0 + 16 18 2 0 0 0 0 + 16 28 1 0 0 0 0 + 17 21 1 0 0 0 0 + 17 29 1 0 0 0 0 + 18 30 1 0 0 0 0 + 19 22 1 0 0 0 0 + 19 31 1 0 0 0 0 + 20 32 1 0 0 0 0 + 20 33 1 0 0 0 0 + 20 34 1 0 0 0 0 + 21 22 2 0 0 0 0 + 21 35 1 0 0 0 0 + 22 36 1 0 0 0 0 + 23 25 1 0 0 0 0 + 24 37 1 0 0 0 0 + 24 38 1 0 0 0 0 + 24 39 1 0 0 0 0 + 25 40 1 0 0 0 0 + 25 41 1 0 0 0 0 + 25 42 1 0 0 0 0 +M END +> +10000027 + +> +1 + +> +676 + +> +7 + +> +1 + +> +6 + +> +AAADceB6OABgAAAAAAAAAAAAAAAAAAAAAAAwYAAAAAAAAAABQAAAHgQQQAAADAyB2ACyx4BABAqIAqRSQHDCCAAkKhAIiBsHbMgOJjKktZuDOSDk0BEI6YeaybCeCAABAAAAMAAQAAIAAABgAAAAAAAAAA== + +> +N-[4-acetyl-2-(2-methylsulfonylphenoxy)phenyl]methanesulfonamide + +> +N-[4-acetyl-2-(2-methylsulfonylphenoxy)phenyl]methanesulfonamide + +> +N-[4-acetyl-2-(2-methylsulfonylphenoxy)phenyl]methanesulfonamide + +> +N-[4-ethanoyl-2-(2-methylsulfonylphenoxy)phenyl]methanesulfonamide + +> +N-[4-acetyl-2-(2-mesylphenoxy)phenyl]methanesulfonamide + +> +InChI=1S/C16H17NO6S2/c1-11(18)12-8-9-13(17-25(3,21)22)15(10-12)23-14-6-4-5-7-16(14)24(2,19)20/h4-10,17H,1-3H3 + +> +NNXRNGYWQJUILQ-UHFFFAOYSA-N + +> +1.3 + +> +383.049729 + +> +C16H17NO6S2 + +> +383.43928 + +> +CC(=O)C1=CC(=C(C=C1)NS(=O)(=O)C)OC2=CC=CC=C2S(=O)(=O)C + +> +CC(=O)C1=CC(=C(C=C1)NS(=O)(=O)C)OC2=CC=CC=C2S(=O)(=O)C + +> +107 + +> +383.049729 + +> +0 + +> +25 + +> +0 + +> +0 + +> +0 + +> +0 + +> +0 + +> +1 + +> +7 + +> +1 +5 +255 + +> +10 13 8 +10 17 8 +11 12 8 +11 14 8 +12 16 8 +13 19 8 +14 15 8 +15 18 8 +16 18 8 +17 21 8 +19 22 8 +21 22 8 + +$$$$ +10000028 + -OEChem-11110908022D + + 50 53 0 0 0 0 0 0 0999 V2000 + 6.6865 -1.4859 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.6865 2.5141 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.1929 1.6066 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9544 -0.4859 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 5.8204 0.0141 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.8204 1.0141 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5525 0.0141 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.6865 -0.4859 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5525 1.0141 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.6865 1.5141 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9544 -1.4859 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9696 -0.3122 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1777 1.7802 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4464 -0.5206 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4464 1.5488 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5197 2.7198 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.3525 -0.0067 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.3525 1.0349 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.8204 -1.9859 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.3268 -1.0782 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0884 -1.9859 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.6276 0.6275 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8769 3.4859 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.8204 -2.9859 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3420 -0.9046 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0884 -2.9859 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6428 0.8011 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9544 -3.4859 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 0.0351 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4393 -1.1405 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4393 2.1687 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.9182 3.1948 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0566 2.4098 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8882 -0.3188 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8882 1.3470 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3574 -1.6759 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5388 -1.6608 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5515 -1.6759 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0261 1.1024 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2234 -1.7959 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2234 2.8241 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.3518 3.8844 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.4019 3.0874 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.4784 3.9608 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3574 -3.2959 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.9435 -1.3796 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5515 -3.2959 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4307 1.3837 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9544 -4.1059 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.3894 0.1427 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 8 1 0 0 0 0 + 1 40 1 0 0 0 0 + 2 10 1 0 0 0 0 + 2 41 1 0 0 0 0 + 3 13 2 0 0 0 0 + 4 5 1 0 0 0 0 + 4 11 1 0 0 0 0 + 4 12 1 0 0 0 0 + 5 6 1 0 0 0 0 + 5 8 2 0 0 0 0 + 6 10 2 0 0 0 0 + 6 13 1 0 0 0 0 + 7 8 1 0 0 0 0 + 7 9 1 0 0 0 0 + 7 14 2 0 0 0 0 + 9 10 1 0 0 0 0 + 9 15 2 0 0 0 0 + 11 19 2 0 0 0 0 + 11 21 1 0 0 0 0 + 12 20 2 0 0 0 0 + 12 22 1 0 0 0 0 + 13 16 1 0 0 0 0 + 14 17 1 0 0 0 0 + 14 30 1 0 0 0 0 + 15 18 1 0 0 0 0 + 15 31 1 0 0 0 0 + 16 23 1 0 0 0 0 + 16 32 1 0 0 0 0 + 16 33 1 0 0 0 0 + 17 18 2 0 0 0 0 + 17 34 1 0 0 0 0 + 18 35 1 0 0 0 0 + 19 24 1 0 0 0 0 + 19 36 1 0 0 0 0 + 20 25 1 0 0 0 0 + 20 37 1 0 0 0 0 + 21 26 2 0 0 0 0 + 21 38 1 0 0 0 0 + 22 27 2 0 0 0 0 + 22 39 1 0 0 0 0 + 23 42 1 0 0 0 0 + 23 43 1 0 0 0 0 + 23 44 1 0 0 0 0 + 24 28 2 0 0 0 0 + 24 45 1 0 0 0 0 + 25 29 2 0 0 0 0 + 25 46 1 0 0 0 0 + 26 28 1 0 0 0 0 + 26 47 1 0 0 0 0 + 27 29 1 0 0 0 0 + 27 48 1 0 0 0 0 + 28 49 1 0 0 0 0 + 29 50 1 0 0 0 0 +M END +> +10000028 + +> +1 + +> +522 + +> +4 + +> +2 + +> +5 + +> +AAADceB6MAAAAAAAAAAAAAAAAAAAAAAAAAAwYMGAAAAAAADBVAAAHgAACAAADAyBmAAyxsMAAgCIAqRSQACCAAAlIgAIiAEFbMgKJn7SlZOEcchm4BnY2Ufa2POOyQADQgAaEACSAAaEADQgAAAAAAAAAA== + +> +1-[1,4-dihydroxy-3-(N-phenylanilino)-2-naphthyl]propan-1-one + +> +1-[1,4-dihydroxy-3-(N-phenylanilino)-2-naphthyl]-1-propanone + +> +1-[1,4-dihydroxy-3-(N-phenylanilino)naphthalen-2-yl]propan-1-one + +> +1-[3-(diphenylamino)-1,4-dihydroxy-naphthalen-2-yl]propan-1-one + +> +1-[1,4-dihydroxy-3-(N-phenylanilino)-2-naphthyl]propan-1-one + +> +InChI=1S/C25H21NO3/c1-2-21(27)22-23(25(29)20-16-10-9-15-19(20)24(22)28)26(17-11-5-3-6-12-17)18-13-7-4-8-14-18/h3-16,28-29H,2H2,1H3 + +> +NWQLXALWSUXZMB-UHFFFAOYSA-N + +> +6.4 + +> +383.152144 + +> +C25H21NO3 + +> +383.43914 + +> +CCC(=O)C1=C(C2=CC=CC=C2C(=C1N(C3=CC=CC=C3)C4=CC=CC=C4)O)O + +> +CCC(=O)C1=C(C2=CC=CC=C2C(=C1N(C3=CC=CC=C3)C4=CC=CC=C4)O)O + +> +60.8 + +> +383.152144 + +> +0 + +> +29 + +> +0 + +> +0 + +> +0 + +> +0 + +> +0 + +> +1 + +> +30 + +> +1 +5 +255 + +> +11 19 8 +11 21 8 +12 20 8 +12 22 8 +14 17 8 +15 18 8 +17 18 8 +19 24 8 +20 25 8 +21 26 8 +22 27 8 +24 28 8 +25 29 8 +26 28 8 +27 29 8 +5 6 8 +5 8 8 +6 10 8 +7 14 8 +7 8 8 +7 9 8 +9 10 8 +9 15 8 + +$$$$ +10000029 + -OEChem-11110908022D + + 53 56 0 1 0 0 0 0 0999 V2000 + 5.7909 -4.1643 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9430 -6.2334 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 2.0456 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5033 1.1892 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 3.2218 -4.4733 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5308 -5.4244 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 3.0067 0.3174 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0308 -3.8855 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 3.2218 -3.2978 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8398 -3.2978 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.4900 4.6494 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.4841 4.7578 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.0797 5.5614 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5308 -2.3467 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5308 -2.3467 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.6882 5.7367 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.8203 6.2334 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8398 -4.4733 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9933 3.7815 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5308 -5.4244 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9962 -1.4528 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0655 -1.4528 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.2708 -4.1643 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.4967 2.9174 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5100 -0.5467 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5516 -0.5467 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.0000 2.0495 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5033 1.1854 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6554 -3.0456 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9118 -3.8347 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1498 -3.8347 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4062 -3.0456 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8707 4.6795 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.4865 4.1378 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1009 4.6954 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.7134 6.0616 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5440 5.2493 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2786 5.5474 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9382 6.3041 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.1828 6.7364 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.3579 6.6465 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5168 4.1782 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5199 3.3811 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8953 -5.9260 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3762 -1.4599 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6854 -1.4599 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4623 -3.5747 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.6811 -3.9727 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0792 -4.7540 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9731 2.5207 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9701 3.3178 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8637 -0.0109 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3867 0.3150 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 18 2 0 0 0 0 + 2 20 2 0 0 0 0 + 3 27 2 0 0 0 0 + 4 28 2 0 0 0 0 + 5 8 1 0 0 0 0 + 5 20 1 0 0 0 0 + 5 23 1 0 0 0 0 + 6 18 1 0 0 0 0 + 6 20 1 0 0 0 0 + 6 44 1 0 0 0 0 + 7 25 1 0 0 0 0 + 7 28 1 0 0 0 0 + 7 53 1 0 0 0 0 + 8 9 1 0 0 0 0 + 8 10 1 0 0 0 0 + 8 18 1 0 0 0 0 + 9 14 1 0 0 0 0 + 9 29 1 0 0 0 0 + 9 30 1 0 0 0 0 + 10 15 1 0 0 0 0 + 10 31 1 0 0 0 0 + 10 32 1 0 0 0 0 + 11 12 1 0 0 0 0 + 11 13 1 0 0 0 0 + 11 19 1 0 0 0 0 + 11 33 1 0 0 0 0 + 12 16 1 0 0 0 0 + 12 34 1 0 0 0 0 + 12 35 1 0 0 0 0 + 13 17 1 0 0 0 0 + 13 36 1 0 0 0 0 + 13 37 1 0 0 0 0 + 14 15 2 0 0 0 0 + 14 21 1 0 0 0 0 + 15 22 1 0 0 0 0 + 16 17 1 0 0 0 0 + 16 38 1 0 0 0 0 + 16 39 1 0 0 0 0 + 17 40 1 0 0 0 0 + 17 41 1 0 0 0 0 + 19 24 1 0 0 0 0 + 19 42 1 0 0 0 0 + 19 43 1 0 0 0 0 + 21 25 2 0 0 0 0 + 21 45 1 0 0 0 0 + 22 26 2 0 0 0 0 + 22 46 1 0 0 0 0 + 23 47 1 0 0 0 0 + 23 48 1 0 0 0 0 + 23 49 1 0 0 0 0 + 24 27 1 0 0 0 0 + 24 50 1 0 0 0 0 + 24 51 1 0 0 0 0 + 25 26 1 0 0 0 0 + 26 52 1 0 0 0 0 + 27 28 1 0 0 0 0 +M END +> +10000029 + +> +1 + +> +687 + +> +4 + +> +2 + +> +5 + +> +AAADceB7OAAAAAAAAAAAAAAAAAAAAeMEAAAwAAAAAAAAAGABAAAAHgAQAAAADYyBmAQzwIPAAACIAqVSUACCAAAhAgAIiAGIZIiIYDLAkbGUIAholyLIyAcYiMCOiAAAQAAQAAAQAACAACAAASAACAAAAA== + +> +4-cyclopentyl-N-(1-methyl-2,4-dioxo-spiro[imidazolidine-5,2'-indane]-5'-yl)-2-oxo-butanamide + +> +4-cyclopentyl-N-(1-methyl-2,4-dioxo-5'-spiro[imidazolidine-5,2'-indane]yl)-2-oxobutanamide + +> +4-cyclopentyl-N-(1'-methyl-2',4'-dioxospiro[1,3-dihydroindene-2,5'-imidazolidine]-5-yl)-2-oxobutanamide + +> +4-cyclopentyl-N-(1'-methyl-2',4'-dioxo-spiro[1,3-dihydroindene-2,5'-imidazolidine]-5-yl)-2-oxo-butanamide + +> +4-cyclopentyl-N-(2,4-diketo-1-methyl-spiro[imidazolidine-5,2'-indane]-5'-yl)-2-keto-butyramide + +> +InChI=1S/C21H25N3O4/c1-24-20(28)23-19(27)21(24)11-14-7-8-16(10-15(14)12-21)22-18(26)17(25)9-6-13-4-2-3-5-13/h7-8,10,13H,2-6,9,11-12H2,1H3,(H,22,26)(H,23,27,28) + +> +KTFGMODFESPYDR-UHFFFAOYSA-N + +> +2.5 + +> +383.184506 + +> +C21H25N3O4 + +> +383.4409 + +> +CN1C(=O)NC(=O)C12CC3=C(C2)C=C(C=C3)NC(=O)C(=O)CCC4CCCC4 + +> +CN1C(=O)NC(=O)C12CC3=C(C2)C=C(C=C3)NC(=O)C(=O)CCC4CCCC4 + +> +95.6 + +> +383.184506 + +> +0 + +> +28 + +> +0 + +> +1 + +> +0 + +> +0 + +> +0 + +> +1 + +> +12 + +> +1 +5 +255 + +> +14 15 8 +14 21 8 +15 22 8 +21 25 8 +22 26 8 +25 26 8 +8 18 3 + +$$$$ +10000030 + -OEChem-11110908022D + + 53 57 0 0 0 0 0 0 0999 V2000 + 7.0075 -3.3460 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 0.4714 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0534 2.5593 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 5.8485 -2.0588 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 3.2364 -2.0563 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9301 -1.1106 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0290 -0.3605 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7320 0.4744 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0395 -2.6466 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5395 -3.5126 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5395 -3.5126 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2305 -2.0588 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5395 -1.1078 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5395 -1.1078 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7996 -2.3678 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5427 -1.6987 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7338 -0.5256 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4938 -2.0077 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3348 -0.7206 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2369 -1.3386 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0780 -0.0514 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.7722 0.3087 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8652 0.9729 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7232 -0.0003 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8634 1.9729 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.6714 2.5621 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.3608 3.5126 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3608 3.5109 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9569 -3.3006 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.4319 -4.1232 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.1221 -3.3006 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6472 -4.1232 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.1460 -0.9789 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4747 -0.4912 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.9531 -1.5071 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8711 -2.5572 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9824 -2.3894 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.2027 -2.5552 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7600 -0.9528 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0063 -0.1948 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8118 -1.1063 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.5655 -1.8644 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5894 0.3303 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.3690 0.4960 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2684 0.7854 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2836 0.6904 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.0632 0.8561 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.5316 -0.5900 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.3129 -0.1919 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.9148 0.5893 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2614 2.3716 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7243 4.0149 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.9955 4.0119 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 15 2 0 0 0 0 + 2 23 2 0 0 0 0 + 3 25 1 0 0 0 0 + 3 28 1 0 0 0 0 + 4 9 1 0 0 0 0 + 4 13 1 0 0 0 0 + 4 15 1 0 0 0 0 + 5 6 1 0 0 0 0 + 5 12 1 0 0 0 0 + 5 36 1 0 0 0 0 + 6 17 2 0 0 0 0 + 7 20 1 0 0 0 0 + 7 21 1 0 0 0 0 + 7 22 1 0 0 0 0 + 8 17 1 0 0 0 0 + 8 23 1 0 0 0 0 + 8 45 1 0 0 0 0 + 9 10 1 0 0 0 0 + 9 11 1 0 0 0 0 + 9 12 1 0 0 0 0 + 10 11 1 0 0 0 0 + 10 29 1 0 0 0 0 + 10 30 1 0 0 0 0 + 11 31 1 0 0 0 0 + 11 32 1 0 0 0 0 + 12 14 2 0 0 0 0 + 13 14 1 0 0 0 0 + 13 33 1 0 0 0 0 + 13 34 1 0 0 0 0 + 14 17 1 0 0 0 0 + 15 16 1 0 0 0 0 + 16 18 1 0 0 0 0 + 16 19 1 0 0 0 0 + 16 35 1 0 0 0 0 + 18 20 1 0 0 0 0 + 18 37 1 0 0 0 0 + 18 38 1 0 0 0 0 + 19 21 1 0 0 0 0 + 19 39 1 0 0 0 0 + 19 40 1 0 0 0 0 + 20 41 1 0 0 0 0 + 20 42 1 0 0 0 0 + 21 43 1 0 0 0 0 + 21 44 1 0 0 0 0 + 22 24 1 0 0 0 0 + 22 46 1 0 0 0 0 + 22 47 1 0 0 0 0 + 23 25 1 0 0 0 0 + 24 48 1 0 0 0 0 + 24 49 1 0 0 0 0 + 24 50 1 0 0 0 0 + 25 26 2 0 0 0 0 + 26 27 1 0 0 0 0 + 26 51 1 0 0 0 0 + 27 28 2 0 0 0 0 + 27 52 1 0 0 0 0 + 28 53 1 0 0 0 0 +M END +> +10000030 + +> +1 + +> +631 + +> +5 + +> +2 + +> +4 + +> +AAADceB7sAAAAAAAAAAAAAAAGAAAAWLEgAAsAAAAAAAWAAAB4AAAHgAYAAAADYzhngYztJPjBECqA69y8AKSDAIlIAAduSHuTNiOZjLE/f+XnSj0zxPY6YecFwIOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + +> +N-[5-(1-ethylpiperidine-4-carbonyl)spiro[1,4-dihydropyrrolo[3,4-c]pyrazole-6,1'-cyclopropane]-3-yl]furan-2-carboxamide + +> +N-[5-[(1-ethyl-4-piperidinyl)-oxomethyl]-3-spiro[1,4-dihydropyrrolo[3,4-c]pyrazole-6,1'-cyclopropane]yl]-2-furancarboxamide + +> +N-[5-(1-ethylpiperidine-4-carbonyl)spiro[1,4-dihydropyrrolo[3,4-c]pyrazole-6,1'-cyclopropane]-3-yl]furan-2-carboxamide + +> +N-[5-(1-ethylpiperidin-4-yl)carbonylspiro[1,4-dihydropyrrolo[3,4-c]pyrazole-6,1'-cyclopropane]-3-yl]furan-2-carboxamide + +> +N-[5-(1-ethylisonipecotoyl)spiro[1,4-dihydropyrrolo[3,4-c]pyrazole-6,1'-cyclopropane]-3-yl]-2-furamide + +> +InChI=1S/C20H25N5O3/c1-2-24-9-5-13(6-10-24)19(27)25-12-14-16(20(25)7-8-20)22-23-17(14)21-18(26)15-4-3-11-28-15/h3-4,11,13H,2,5-10,12H2,1H3,(H2,21,22,23,26) + +> +RGJGHEGPMXEARE-UHFFFAOYSA-N + +> +0.9 + +> +383.19574 + +> +C20H25N5O3 + +> +383.4442 + +> +CCN1CCC(CC1)C(=O)N2CC3=C(C24CC4)NN=C3NC(=O)C5=CC=CO5 + +> +CCN1CCC(CC1)C(=O)N2CC3=C(C24CC4)NN=C3NC(=O)C5=CC=CO5 + +> +94.5 + +> +383.19574 + +> +0 + +> +28 + +> +0 + +> +0 + +> +0 + +> +0 + +> +0 + +> +1 + +> +8 + +> +1 +5 +255 + +> +12 14 8 +14 17 8 +25 26 8 +26 27 8 +27 28 8 +3 25 8 +3 28 8 +5 12 8 +5 6 8 +6 17 8 + +$$$$ +10000031 + -OEChem-11110908022D + + 50 53 0 0 0 0 0 0 0999 V2000 + 5.0611 3.7130 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.5474 2.3748 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4904 0.2870 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5963 2.0657 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8564 3.3258 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8564 3.3258 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3564 1.7870 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3564 0.7870 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.1654 2.3748 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1165 2.0657 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4904 -0.7130 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6243 -1.2130 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3564 -1.2130 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8596 2.7349 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.3244 1.0876 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4904 -2.7130 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6243 -2.2130 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3564 -2.2130 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8532 2.7349 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.8107 2.4259 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.2754 0.7786 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9021 2.4259 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.0186 1.4477 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4904 -3.7130 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.6942 1.4477 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1590 3.0950 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.7431 1.1387 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.2079 2.7860 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 1.8078 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.9670 0.8946 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5684 0.2044 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.9534 0.5970 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4674 1.4593 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0874 -0.9030 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8933 -0.9030 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.7307 3.3413 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.8636 0.6727 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0874 -2.5230 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8933 -2.5230 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.2714 2.8407 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4043 0.1721 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.6082 1.2561 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.8704 -3.7130 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1104 -3.7130 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4904 -4.3330 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.1550 1.0329 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.2879 3.7014 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6142 0.5322 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.7472 3.2008 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4103 1.6162 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 19 2 0 0 0 0 + 2 4 1 0 0 0 0 + 2 6 1 0 0 0 0 + 2 7 1 0 0 0 0 + 3 8 1 0 0 0 0 + 3 11 1 0 0 0 0 + 3 32 1 0 0 0 0 + 4 19 1 0 0 0 0 + 4 33 1 0 0 0 0 + 5 6 2 0 0 0 0 + 5 9 1 0 0 0 0 + 7 8 1 0 0 0 0 + 7 9 2 0 0 0 0 + 8 30 1 0 0 0 0 + 8 31 1 0 0 0 0 + 9 10 1 0 0 0 0 + 10 14 2 0 0 0 0 + 10 15 1 0 0 0 0 + 11 12 2 0 0 0 0 + 11 13 1 0 0 0 0 + 12 17 1 0 0 0 0 + 12 34 1 0 0 0 0 + 13 18 2 0 0 0 0 + 13 35 1 0 0 0 0 + 14 20 1 0 0 0 0 + 14 36 1 0 0 0 0 + 15 21 2 0 0 0 0 + 15 37 1 0 0 0 0 + 16 17 2 0 0 0 0 + 16 18 1 0 0 0 0 + 16 24 1 0 0 0 0 + 17 38 1 0 0 0 0 + 18 39 1 0 0 0 0 + 19 22 1 0 0 0 0 + 20 23 2 0 0 0 0 + 20 40 1 0 0 0 0 + 21 23 1 0 0 0 0 + 21 41 1 0 0 0 0 + 22 25 2 0 0 0 0 + 22 26 1 0 0 0 0 + 23 42 1 0 0 0 0 + 24 43 1 0 0 0 0 + 24 44 1 0 0 0 0 + 24 45 1 0 0 0 0 + 25 27 1 0 0 0 0 + 25 46 1 0 0 0 0 + 26 28 2 0 0 0 0 + 26 47 1 0 0 0 0 + 27 29 2 0 0 0 0 + 27 48 1 0 0 0 0 + 28 29 1 0 0 0 0 + 28 49 1 0 0 0 0 + 29 50 1 0 0 0 0 +M END +> +10000031 + +> +1 + +> +509 + +> +4 + +> +2 + +> +6 + +> +AAADceB7oAAAAAAAAAAAAAAAAAAAAWAAAAAwYMAAAAAAAAAB1AAAHgAYAAAADAjBngQywJLiAACqAyVyUACSBAQlEgAauKE4dNgIYLLA1ZGUIQhgmADIyYcYiICOCAAAAAAAAAAQAAAAAAAAAAAAAAAAAA== + +> +N-[5-[(4-methylanilino)methyl]-4-phenyl-triazol-1-yl]benzamide + +> +N-[5-[(4-methylanilino)methyl]-4-phenyl-1-triazolyl]benzamide + +> +N-[5-[(4-methylanilino)methyl]-4-phenyltriazol-1-yl]benzamide + +> +N-[5-[[(4-methylphenyl)amino]methyl]-4-phenyl-1,2,3-triazol-1-yl]benzamide + +> +N-[4-phenyl-5-(p-toluidinomethyl)triazol-1-yl]benzamide + +> +InChI=1S/C23H21N5O/c1-17-12-14-20(15-13-17)24-16-21-22(18-8-4-2-5-9-18)25-27-28(21)26-23(29)19-10-6-3-7-11-19/h2-15,24H,16H2,1H3,(H,26,29) + +> +FTYBLYWDUVCPNG-UHFFFAOYSA-N + +> +4.5 + +> +383.17461 + +> +C23H21N5O + +> +383.44574 + +> +CC1=CC=C(C=C1)NCC2=C(N=NN2NC(=O)C3=CC=CC=C3)C4=CC=CC=C4 + +> +CC1=CC=C(C=C1)NCC2=C(N=NN2NC(=O)C3=CC=CC=C3)C4=CC=CC=C4 + +> +71.8 + +> +383.17461 + +> +0 + +> +29 + +> +0 + +> +0 + +> +0 + +> +0 + +> +0 + +> +1 + +> +2 + +> +1 +5 +255 + +> +10 14 8 +10 15 8 +11 12 8 +11 13 8 +12 17 8 +13 18 8 +14 20 8 +15 21 8 +16 17 8 +16 18 8 +2 6 8 +2 7 8 +20 23 8 +21 23 8 +22 25 8 +22 26 8 +25 27 8 +26 28 8 +27 29 8 +28 29 8 +5 6 8 +5 9 8 +7 9 8 + +$$$$ +10000032 + -OEChem-11110908022D + + 50 53 0 0 0 0 0 0 0999 V2000 + 7.1962 1.2953 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 2.7953 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 8.1667 5.7898 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -3.2047 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -3.2047 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 9.6449 5.1317 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 4.2953 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 3.2953 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -0.2047 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 1.2953 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0622 4.7953 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -1.2047 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 0.2953 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 1.7953 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 0.2953 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 1.7953 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -1.7047 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 1.2953 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -2.7047 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -1.7047 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9757 4.3886 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -2.7047 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1449 5.9977 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -4.2047 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -4.7047 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -4.7047 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -5.7047 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -5.7047 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -6.2047 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.5856 4.1876 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.9841 4.8779 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8067 3.4030 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4082 2.7127 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.7932 3.1053 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0010 -0.0147 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1951 -0.0147 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 2.4153 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1951 -1.3947 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1951 1.6053 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7060 6.2047 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0010 -1.3947 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1046 3.7821 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3291 -2.8947 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0010 -3.0147 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.3970 6.5641 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2690 -4.3947 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 -4.3947 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2690 -6.0147 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 -6.0147 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -6.8247 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 14 2 0 0 0 0 + 2 8 1 0 0 0 0 + 2 14 1 0 0 0 0 + 2 34 1 0 0 0 0 + 3 11 1 0 0 0 0 + 3 23 1 0 0 0 0 + 3 40 1 0 0 0 0 + 4 19 1 0 0 0 0 + 4 24 1 0 0 0 0 + 4 43 1 0 0 0 0 + 5 19 2 0 0 0 0 + 5 22 1 0 0 0 0 + 6 21 1 0 0 0 0 + 6 23 2 0 0 0 0 + 7 8 1 0 0 0 0 + 7 11 1 0 0 0 0 + 7 30 1 0 0 0 0 + 7 31 1 0 0 0 0 + 8 32 1 0 0 0 0 + 8 33 1 0 0 0 0 + 9 12 1 0 0 0 0 + 9 13 2 0 0 0 0 + 9 15 1 0 0 0 0 + 10 13 1 0 0 0 0 + 10 14 1 0 0 0 0 + 10 16 2 0 0 0 0 + 11 21 2 0 0 0 0 + 12 17 2 0 0 0 0 + 12 20 1 0 0 0 0 + 13 35 1 0 0 0 0 + 15 18 2 0 0 0 0 + 15 36 1 0 0 0 0 + 16 18 1 0 0 0 0 + 16 37 1 0 0 0 0 + 17 19 1 0 0 0 0 + 17 38 1 0 0 0 0 + 18 39 1 0 0 0 0 + 20 22 2 0 0 0 0 + 20 41 1 0 0 0 0 + 21 42 1 0 0 0 0 + 22 44 1 0 0 0 0 + 23 45 1 0 0 0 0 + 24 25 2 0 0 0 0 + 24 26 1 0 0 0 0 + 25 27 1 0 0 0 0 + 25 46 1 0 0 0 0 + 26 28 2 0 0 0 0 + 26 47 1 0 0 0 0 + 27 29 2 0 0 0 0 + 27 48 1 0 0 0 0 + 28 29 1 0 0 0 0 + 28 49 1 0 0 0 0 + 29 50 1 0 0 0 0 +M END +> +10000032 + +> +1 + +> +510 + +> +4 + +> +3 + +> +7 + +> +AAADceB7oAAAAAAAAAAAAAAAAAAAAWAAAAA8YIAAAAAAAAAB9AAAHgAQAAAADAjBngQ98JbJkACoAzd3dACCgC21EqAJ2aE4dNiIaHLA3ZGUIQholgLIyacYiMCOgAACAAAAAAAAAAQAAAAAAAAAAAAAAA== + +> +3-(2-anilino-4-pyridyl)-N-[2-(1H-imidazol-5-yl)ethyl]benzamide + +> +3-(2-anilino-4-pyridyl)-N-[2-(1H-imidazol-5-yl)ethyl]benzamide + +> +3-(2-anilinopyridin-4-yl)-N-[2-(1H-imidazol-5-yl)ethyl]benzamide + +> +N-[2-(1H-imidazol-5-yl)ethyl]-3-(2-phenylazanylpyridin-4-yl)benzamide + +> +3-(2-anilino-4-pyridyl)-N-[2-(1H-imidazol-5-yl)ethyl]benzamide + +> +InChI=1S/C23H21N5O/c29-23(26-12-10-21-15-24-16-27-21)19-6-4-5-17(13-19)18-9-11-25-22(14-18)28-20-7-2-1-3-8-20/h1-9,11,13-16H,10,12H2,(H,24,27)(H,25,28)(H,26,29) + +> +UBFXEQOUIIFPIY-UHFFFAOYSA-N + +> +3.8 + +> +383.17461 + +> +C23H21N5O + +> +383.44574 + +> +C1=CC=C(C=C1)NC2=NC=CC(=C2)C3=CC(=CC=C3)C(=O)NCCC4=CN=CN4 + +> +C1=CC=C(C=C1)NC2=NC=CC(=C2)C3=CC(=CC=C3)C(=O)NCCC4=CN=CN4 + +> +82.7 + +> +383.17461 + +> +0 + +> +29 + +> +0 + +> +0 + +> +0 + +> +0 + +> +0 + +> +1 + +> +8 + +> +1 +5 +255 + +> +10 13 8 +10 16 8 +11 21 8 +12 17 8 +12 20 8 +15 18 8 +16 18 8 +17 19 8 +20 22 8 +24 25 8 +24 26 8 +25 27 8 +26 28 8 +27 29 8 +28 29 8 +3 11 8 +3 23 8 +5 19 8 +5 22 8 +6 21 8 +6 23 8 +9 13 8 +9 15 8 + +$$$$ +10000033 + -OEChem-11110908022D + + 54 57 0 1 0 0 0 0 0999 V2000 + 2.0000 1.2872 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 + 8.7615 -0.5246 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2098 1.1485 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2779 -0.2658 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 10.9640 -1.6320 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7052 -2.5980 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.7274 -0.7834 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 12.5372 -3.3568 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9732 -3.5980 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.5934 -0.2834 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.7960 -2.3909 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4732 -2.7319 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5027 0.4414 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5367 0.7002 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 6.5708 0.9590 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7956 1.6661 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3120 1.9249 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.8637 0.2519 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.7615 1.9249 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0885 2.3732 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.3461 2.1837 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8978 0.5107 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.6390 1.4766 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0203 2.8909 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3473 3.3391 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.6730 1.7355 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.3132 3.5980 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9659 1.0284 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7458 -3.2166 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.6465 -0.1687 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 12.3251 -3.9394 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 13.1285 -3.5432 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4362 -3.9080 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2831 -4.1349 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.1751 -0.0689 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.3302 0.2780 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 13.3873 -2.5773 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 13.0080 -1.8082 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.2025 -2.1741 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.9254 -3.0224 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.6790 -0.4262 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7504 2.3633 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0242 -0.3470 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1999 1.4865 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4896 2.2127 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1856 2.7826 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.4594 0.0723 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.6192 3.0513 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.9089 3.7775 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9351 2.2974 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1652 2.0911 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4737 4.1968 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.7039 0.4664 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.4738 0.6727 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 28 1 0 0 0 0 + 7 2 1 1 0 0 0 + 2 13 1 0 0 0 0 + 3 13 2 0 0 0 0 + 14 4 1 6 0 0 0 + 4 41 1 0 0 0 0 + 5 10 1 0 0 0 0 + 5 11 1 0 0 0 0 + 5 12 1 0 0 0 0 + 6 7 1 0 0 0 0 + 6 8 1 0 0 0 0 + 6 9 1 0 0 0 0 + 6 29 1 0 0 0 0 + 7 10 1 0 0 0 0 + 7 30 1 0 0 0 0 + 8 11 1 0 0 0 0 + 8 31 1 0 0 0 0 + 8 32 1 0 0 0 0 + 9 12 1 0 0 0 0 + 9 33 1 0 0 0 0 + 9 34 1 0 0 0 0 + 10 35 1 0 0 0 0 + 10 36 1 0 0 0 0 + 11 37 1 0 0 0 0 + 11 38 1 0 0 0 0 + 12 39 1 0 0 0 0 + 12 40 1 0 0 0 0 + 13 14 1 0 0 0 0 + 14 15 1 0 0 0 0 + 14 16 1 0 0 0 0 + 15 17 2 0 0 0 0 + 15 18 1 0 0 0 0 + 16 19 2 0 0 0 0 + 16 20 1 0 0 0 0 + 17 21 1 0 0 0 0 + 17 42 1 0 0 0 0 + 18 22 2 0 0 0 0 + 18 43 1 0 0 0 0 + 19 24 1 0 0 0 0 + 19 44 1 0 0 0 0 + 20 25 2 0 0 0 0 + 20 45 1 0 0 0 0 + 21 23 2 0 0 0 0 + 21 46 1 0 0 0 0 + 22 23 1 0 0 0 0 + 22 47 1 0 0 0 0 + 23 26 1 0 0 0 0 + 24 27 2 0 0 0 0 + 24 48 1 0 0 0 0 + 25 27 1 0 0 0 0 + 25 49 1 0 0 0 0 + 26 28 1 0 0 0 0 + 26 50 1 0 0 0 0 + 26 51 1 0 0 0 0 + 27 52 1 0 0 0 0 + 28 53 1 0 0 0 0 + 28 54 1 0 0 0 0 +M END +> +10000033 + +> +1 + +> +522 + +> +5 + +> +1 + +> +7 + +> +AAADceB6MQAAAAAAAAAAAAAAAAAAAAAAAAA8eLECAAAAAAABQAAAHwAACAAADVThmAYwCIMABgCIAiDSCAICAAAgAAAIiAFICIgLJjKAlRCHcAAkwAGbmAe4yPCOwAAAAAAAAACAAAAAAAAAAAAAAAAAAA== + +> +[(3R)-quinuclidin-3-yl] (2S)-2-[4-(2-fluoroethyl)phenyl]-2-hydroxy-2-phenyl-acetate + +> +(2S)-2-[4-(2-fluoroethyl)phenyl]-2-hydroxy-2-phenylacetic acid [(3R)-3-quinuclidinyl] ester + +> +[(8R)-1-azabicyclo[2.2.2]octan-8-yl] (2S)-2-[4-(2-fluoroethyl)phenyl]-2-hydroxy-2-phenylacetate + +> +[(8R)-1-azabicyclo[2.2.2]octan-8-yl] (2S)-2-[4-(2-fluoroethyl)phenyl]-2-hydroxy-2-phenyl-ethanoate + +> +(2S)-2-[4-(2-fluoroethyl)phenyl]-2-hydroxy-2-phenyl-acetic acid [(3R)-quinuclidin-3-yl] ester + +> +InChI=1S/C23H26FNO3/c24-13-10-17-6-8-20(9-7-17)23(27,19-4-2-1-3-5-19)22(26)28-21-16-25-14-11-18(21)12-15-25/h1-9,18,21,27H,10-16H2/t21-,23-/m0/s1 + +> +AXYNUUVRRMNNRY-GMAHTHKFSA-N + +> +3.9 + +> +383.189672 + +> +C23H26FNO3 + +> +383.455843 + +> +C1CN2CCC1C(C2)OC(=O)C(C3=CC=CC=C3)(C4=CC=C(C=C4)CCF)O + +> +C1CN2CCC1[C@H](C2)OC(=O)[C@](C3=CC=CC=C3)(C4=CC=C(C=C4)CCF)O + +> +49.8 + +> +383.189672 + +> +0 + +> +28 + +> +2 + +> +0 + +> +0 + +> +0 + +> +0 + +> +1 + +> +1 + +> +1 +5 +255 + +> +15 17 8 +15 18 8 +16 19 8 +16 20 8 +17 21 8 +18 22 8 +19 24 8 +7 2 5 +20 25 8 +21 23 8 +22 23 8 +24 27 8 +25 27 8 +14 4 6 + +$$$$ +10000034 + -OEChem-11110908022D + + 57 62 0 1 0 0 0 0 0999 V2000 + 3.6123 -1.7485 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.1220 2.4736 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8206 -0.3502 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -1.1157 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 5.7662 -0.4838 0.0000 N 0 0 3 0 0 0 0 0 0 0 0 0 + 5.2241 0.1764 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 6.5137 1.7935 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 5.6230 1.2344 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 6.1446 0.1764 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 6.7185 0.8961 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 4.6502 0.8961 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 3.7400 0.7589 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 7.5048 1.5821 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8878 -0.6804 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6844 2.1928 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1884 2.4195 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.2728 1.2508 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 4.8550 1.7935 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5981 0.6248 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2730 -0.2588 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.4038 -0.0979 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9777 -0.8176 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 8.0680 2.1482 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1166 1.5407 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2689 -1.3483 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.8010 2.8284 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6235 -1.8975 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.2582 -2.8284 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2542 2.0002 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.6574 -0.1720 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.9012 0.3036 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0721 1.5192 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.9202 2.0424 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4354 0.9660 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8724 -1.3002 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4956 -0.8033 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0709 2.6776 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2978 2.6776 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4713 2.9712 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.6942 2.7939 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0850 1.0003 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.6757 -0.4252 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.3108 -0.8777 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8747 0.2253 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9935 -0.5628 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.3642 -1.3023 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.6013 1.9273 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.7300 2.0255 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6318 1.1542 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.7330 -1.6600 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.5806 -1.8842 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8049 -1.0366 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.3935 2.6456 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.6631 3.4328 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.6810 -2.6019 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0316 -3.4055 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8353 -3.0549 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 22 1 1 1 0 0 0 + 1 27 1 0 0 0 0 + 2 18 2 0 0 0 0 + 3 19 2 0 0 0 0 + 4 27 2 0 0 0 0 + 5 9 1 0 0 0 0 + 5 20 1 0 0 0 0 + 5 25 1 0 0 0 0 + 6 8 1 0 0 0 0 + 6 9 1 0 0 0 0 + 6 11 1 0 0 0 0 + 6 14 1 0 0 0 0 + 7 8 1 0 0 0 0 + 7 10 1 0 0 0 0 + 7 15 1 0 0 0 0 + 7 16 1 0 0 0 0 + 8 13 1 0 0 0 0 + 8 29 1 6 0 0 0 + 9 10 1 0 0 0 0 + 9 30 1 0 0 0 0 + 10 19 1 0 0 0 0 + 10 31 1 0 0 0 0 + 11 12 1 0 0 0 0 + 11 18 1 0 0 0 0 + 11 32 1 6 0 0 0 + 12 20 1 0 0 0 0 + 12 21 1 0 0 0 0 + 12 24 1 1 0 0 0 + 13 17 1 0 0 0 0 + 13 33 1 0 0 0 0 + 13 34 1 0 0 0 0 + 14 22 1 0 0 0 0 + 14 35 1 0 0 0 0 + 14 36 1 0 0 0 0 + 15 18 1 0 0 0 0 + 15 37 1 0 0 0 0 + 15 38 1 0 0 0 0 + 16 23 1 0 0 0 0 + 16 39 1 0 0 0 0 + 16 40 1 0 0 0 0 + 17 19 1 0 0 0 0 + 17 23 1 0 0 0 0 + 17 41 1 6 0 0 0 + 20 42 1 0 0 0 0 + 20 43 1 0 0 0 0 + 21 22 1 0 0 0 0 + 21 44 1 0 0 0 0 + 21 45 1 0 0 0 0 + 22 46 1 0 0 0 0 + 23 26 2 0 0 0 0 + 24 47 1 0 0 0 0 + 24 48 1 0 0 0 0 + 24 49 1 0 0 0 0 + 25 50 1 0 0 0 0 + 25 51 1 0 0 0 0 + 25 52 1 0 0 0 0 + 26 53 1 0 0 0 0 + 26 54 1 0 0 0 0 + 27 28 1 0 0 0 0 + 28 55 1 0 0 0 0 + 28 56 1 0 0 0 0 + 28 57 1 0 0 0 0 +M END +> +10000034 + +> +1 + +> +865 + +> +5 + +> +0 + +> +2 + +> +AAADceB6OAAAAAAAAAAAAAAAAAAAAYAAAAA8YMGDBgAWAFjAAAAAHgAAAAAADzzhgAYCCAMABACIAoDSCAAAAAAgAAAAAAEIAAgAABoAgQAGAAAEgACAAAG8z/D/gAAAAAAAAADAAAYAACAAASAACQAAAA== + +> +InChI=1S/C23H29NO4/c1-11-6-22-9-15(26)19-21(3)7-13(28-12(2)25)8-23(19)16(22)5-14(11)18(27)17(22)20(23)24(4)10-21/h13-14,16-17,19-20H,1,5-10H2,2-4H3/t13-,14-,16+,17?,19+,20?,21-,22?,23?/m0/s1 + +> +CKNLFSFBGRRFCB-URIDCEOUSA-N + +> +1.3 + +> +383.209658 + +> +C23H29NO4 + +> +383.48066 + +> +CC(=O)OC1CC2(CN(C3C4C(=O)C5CC6C3(C1)C2C(=O)CC64CC5=C)C)C + +> +CC(=O)O[C@H]1C[C@]2(CN(C3C4C(=O)[C@H]5C[C@H]6C3(C1)[C@@H]2C(=O)CC64CC5=C)C)C + +> +63.7 + +> +383.209658 + +> +0 + +> +28 + +> +5 + +> +4 + +> +0 + +> +0 + +> +0 + +> +1 + +> +2 + +> +1 +5 +255 + +> +22 1 5 +10 19 3 +11 32 6 +12 24 5 +17 41 6 +9 5 3 +6 14 3 +7 15 3 +8 29 6 + +$$$$ +10000035 + -OEChem-11110908022D + + 57 60 0 1 0 0 0 0 0999 V2000 + 7.7619 0.9733 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2619 0.1073 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9889 0.0841 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9889 3.5946 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2619 1.8393 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 10.0938 -0.6515 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 10.5716 -2.6001 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8350 -1.6175 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 8.2619 0.1073 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 11.8259 0.3485 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0031 -0.8586 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.3259 -0.5176 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.3526 0.3144 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4671 -3.5946 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.4852 -3.0068 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7619 0.9733 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2619 1.8393 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 6.7619 2.7054 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7619 2.7054 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.6783 1.0346 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.6783 2.6441 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 1.3393 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 2.3393 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.2619 3.5714 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 0.8393 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 2.8393 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 1.3393 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 2.3393 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.6280 -2.3902 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.6707 0.5734 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 12.3628 0.6585 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.5159 0.8854 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4118 -0.6722 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7910 -1.4412 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 12.8736 -0.2270 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 12.5965 -1.0754 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.9515 0.1539 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.5131 0.9133 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.7538 0.4749 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4023 -4.2112 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.0837 -3.6594 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8505 -3.5298 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.2330 -3.5732 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 12.0516 -3.2590 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.7373 -2.4404 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.9519 2.3763 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8695 3.3159 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.1793 2.9174 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.6542 2.0948 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.3445 2.4933 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7249 3.8814 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5719 4.1083 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.7988 3.2614 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 0.2193 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 3.4593 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 1.0293 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 2.6493 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9 1 1 1 0 0 0 + 1 16 1 0 0 0 0 + 2 16 2 0 0 0 0 + 3 20 2 0 0 0 0 + 4 21 2 0 0 0 0 + 5 17 1 0 0 0 0 + 5 20 1 0 0 0 0 + 5 21 1 0 0 0 0 + 6 7 1 0 0 0 0 + 6 9 1 0 0 0 0 + 6 10 1 0 0 0 0 + 6 13 1 6 0 0 0 + 7 8 1 0 0 0 0 + 7 14 1 0 0 0 0 + 7 15 1 0 0 0 0 + 8 11 1 0 0 0 0 + 8 12 1 0 0 0 0 + 8 29 1 1 0 0 0 + 9 11 1 0 0 0 0 + 9 30 1 0 0 0 0 + 10 12 1 0 0 0 0 + 10 31 1 0 0 0 0 + 10 32 1 0 0 0 0 + 11 33 1 0 0 0 0 + 11 34 1 0 0 0 0 + 12 35 1 0 0 0 0 + 12 36 1 0 0 0 0 + 13 37 1 0 0 0 0 + 13 38 1 0 0 0 0 + 13 39 1 0 0 0 0 + 14 40 1 0 0 0 0 + 14 41 1 0 0 0 0 + 14 42 1 0 0 0 0 + 15 43 1 0 0 0 0 + 15 44 1 0 0 0 0 + 15 45 1 0 0 0 0 + 16 17 1 0 0 0 0 + 17 18 1 6 0 0 0 + 17 46 1 0 0 0 0 + 18 19 1 0 0 0 0 + 18 47 1 0 0 0 0 + 18 48 1 0 0 0 0 + 19 24 1 0 0 0 0 + 19 49 1 0 0 0 0 + 19 50 1 0 0 0 0 + 20 22 1 0 0 0 0 + 21 23 1 0 0 0 0 + 22 23 1 0 0 0 0 + 22 25 2 0 0 0 0 + 23 26 2 0 0 0 0 + 24 51 1 0 0 0 0 + 24 52 1 0 0 0 0 + 24 53 1 0 0 0 0 + 25 27 1 0 0 0 0 + 25 54 1 0 0 0 0 + 26 28 1 0 0 0 0 + 26 55 1 0 0 0 0 + 27 28 2 0 0 0 0 + 27 56 1 0 0 0 0 + 28 57 1 0 0 0 0 +M END +> +10000035 + +> +1 + +> +662 + +> +4 + +> +0 + +> +6 + +> +AAADceB6OAAAAAAAAAAAAAAAAAAAAeMEAAAwYAAAAAAAAFgBAAAAHgAAAAAADzzhmAYyCIMABACIAiHSGACCAAAkAAAIiAGIBMgKIDKAlTGHIQhkhiCYiYedyPCPgAAAAAAQAACAAAIAADAAAYAADAAAAA== + +> +[(1S,2R,4S)-1,7,7-trimethylnorbornan-2-yl] (2R)-2-(1,3-dioxoisoindolin-2-yl)pentanoate + +> +(2R)-2-(1,3-dioxo-2-isoindolinyl)pentanoic acid [(1S,2R,4S)-1,7,7-trimethyl-2-norbornanyl] ester + +> +[(1S,4S,6R)-1,7,7-trimethyl-6-bicyclo[2.2.1]heptanyl] (2R)-2-(1,3-dioxoisoindol-2-yl)pentanoate + +> +[(1S,4S,6R)-1,7,7-trimethyl-6-bicyclo[2.2.1]heptanyl] (2R)-2-(1,3-dioxoisoindol-2-yl)pentanoate + +> +(2R)-2-phthalimidovaleric acid [(1S,2R,4S)-1,7,7-trimethylnorbornan-2-yl] ester + +> +InChI=1S/C23H29NO4/c1-5-8-17(24-19(25)15-9-6-7-10-16(15)20(24)26)21(27)28-18-13-14-11-12-23(18,4)22(14,2)3/h6-7,9-10,14,17-18H,5,8,11-13H2,1-4H3/t14-,17+,18+,23+/m0/s1 + +> +SSNHICHHJKGBRM-ZFTZODIFSA-N + +> +5.1 + +> +383.209658 + +> +C23H29NO4 + +> +383.48066 + +> +CCCC(C(=O)OC1CC2CCC1(C2(C)C)C)N3C(=O)C4=CC=CC=C4C3=O + +> +CCC[C@H](C(=O)O[C@@H]1C[C@@H]2CC[C@]1(C2(C)C)C)N3C(=O)C4=CC=CC=C4C3=O + +> +63.7 + +> +383.209658 + +> +0 + +> +28 + +> +4 + +> +0 + +> +0 + +> +0 + +> +0 + +> +1 + +> +1 + +> +1 +5 +255 + +> +9 1 5 +17 18 6 +22 23 8 +22 25 8 +23 26 8 +25 27 8 +26 28 8 +27 28 8 +6 13 6 +8 29 5 + +$$$$ +10000036 + -OEChem-11110908022D + + 57 59 0 1 0 0 0 0 0999 V2000 + 6.3052 -0.3890 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -2.0961 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 -2.0961 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -5.0961 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -1.0961 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 0.3181 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 3.8910 -0.3890 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.3052 1.0253 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.3052 -0.3890 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.8910 1.0253 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0464 1.9912 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -2.0961 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9250 0.7664 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.7535 2.6983 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -2.5961 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -2.5961 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4947 3.6642 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -3.5961 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -3.5961 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -4.0961 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5287 3.9230 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2018 4.3713 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2699 4.8890 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.9429 5.3373 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -2.5961 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 -2.5961 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9770 5.5961 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -5.5961 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.4526 0.0494 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.4526 -0.8274 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6608 0.5174 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.8671 1.2873 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.3988 1.3809 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.6290 1.5872 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.6908 2.4991 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.4845 1.7292 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.7646 1.3653 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3262 0.6060 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.0855 0.1676 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.1091 2.1904 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3154 2.9603 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1951 -3.9061 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0010 -3.9061 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0903 3.4846 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8006 4.2109 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.6710 5.0494 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3814 5.7757 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.6900 -2.0591 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 -2.9061 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3100 -3.1330 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8862 -3.1330 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7331 -2.9061 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5062 -2.0591 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8166 6.1949 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.4221 -5.0591 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1951 -5.9061 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0421 -6.1330 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 9 2 0 0 0 0 + 2 15 1 0 0 0 0 + 2 25 1 0 0 0 0 + 3 16 1 0 0 0 0 + 3 26 1 0 0 0 0 + 4 20 1 0 0 0 0 + 4 28 1 0 0 0 0 + 5 7 1 0 0 0 0 + 5 9 1 0 0 0 0 + 5 12 1 0 0 0 0 + 6 7 1 0 0 0 0 + 6 8 1 0 0 0 0 + 6 9 1 0 0 0 0 + 6 10 1 0 0 0 0 + 7 29 1 0 0 0 0 + 7 30 1 0 0 0 0 + 8 11 1 0 0 0 0 + 8 31 1 0 0 0 0 + 8 32 1 0 0 0 0 + 10 13 1 0 0 0 0 + 10 33 1 0 0 0 0 + 10 34 1 0 0 0 0 + 11 14 1 0 0 0 0 + 11 35 1 0 0 0 0 + 11 36 1 0 0 0 0 + 12 15 2 0 0 0 0 + 12 16 1 0 0 0 0 + 13 37 1 0 0 0 0 + 13 38 1 0 0 0 0 + 13 39 1 0 0 0 0 + 14 17 1 0 0 0 0 + 14 40 1 0 0 0 0 + 14 41 1 0 0 0 0 + 15 18 1 0 0 0 0 + 16 19 2 0 0 0 0 + 17 21 2 0 0 0 0 + 17 22 1 0 0 0 0 + 18 20 2 0 0 0 0 + 18 42 1 0 0 0 0 + 19 20 1 0 0 0 0 + 19 43 1 0 0 0 0 + 21 23 1 0 0 0 0 + 21 44 1 0 0 0 0 + 22 24 2 0 0 0 0 + 22 45 1 0 0 0 0 + 23 27 2 0 0 0 0 + 23 46 1 0 0 0 0 + 24 27 1 0 0 0 0 + 24 47 1 0 0 0 0 + 25 48 1 0 0 0 0 + 25 49 1 0 0 0 0 + 25 50 1 0 0 0 0 + 26 51 1 0 0 0 0 + 26 52 1 0 0 0 0 + 26 53 1 0 0 0 0 + 27 54 1 0 0 0 0 + 28 55 1 0 0 0 0 + 28 56 1 0 0 0 0 + 28 57 1 0 0 0 0 +M END +> +10000036 + +> +1 + +> +493 + +> +4 + +> +0 + +> +9 + +> +AAADceB6OAAAAAAAAAAAAAAAAABYAAAAAAAwYAAAAAAAAAABQAAAHgAAAAAADgzBmAYyxoMABACIAiVSUACCCAAhIAAIiAEPTIgOJzrEsZuFMChlxhXY6Ae46MyPAEAACAAAEAAAgAAQAAAgAAAAAAAAAA== + +> +3-ethyl-3-(3-phenylpropyl)-1-(2,4,6-trimethoxyphenyl)azetidin-2-one + +> +3-ethyl-3-(3-phenylpropyl)-1-(2,4,6-trimethoxyphenyl)-2-azetidinone + +> +3-ethyl-3-(3-phenylpropyl)-1-(2,4,6-trimethoxyphenyl)azetidin-2-one + +> +3-ethyl-3-(3-phenylpropyl)-1-(2,4,6-trimethoxyphenyl)azetidin-2-one + +> +3-ethyl-3-(3-phenylpropyl)-1-(2,4,6-trimethoxyphenyl)azetidin-2-one + +> +InChI=1S/C23H29NO4/c1-5-23(13-9-12-17-10-7-6-8-11-17)16-24(22(23)25)21-19(27-3)14-18(26-2)15-20(21)28-4/h6-8,10-11,14-15H,5,9,12-13,16H2,1-4H3 + +> +RZSADZPNOGPQHK-UHFFFAOYSA-N + +> +4.5 + +> +383.209658 + +> +C23H29NO4 + +> +383.48066 + +> +CCC1(CN(C1=O)C2=C(C=C(C=C2OC)OC)OC)CCCC3=CC=CC=C3 + +> +CCC1(CN(C1=O)C2=C(C=C(C=C2OC)OC)OC)CCCC3=CC=CC=C3 + +> +48 + +> +383.209658 + +> +0 + +> +28 + +> +0 + +> +1 + +> +0 + +> +0 + +> +0 + +> +1 + +> +1 + +> +1 +5 +255 + +> +12 15 8 +12 16 8 +15 18 8 +16 19 8 +17 21 8 +17 22 8 +18 20 8 +19 20 8 +21 23 8 +22 24 8 +23 27 8 +24 27 8 +6 10 3 + +$$$$ +10000037 + -OEChem-11110908022D + + 54 57 0 0 0 0 0 0 0999 V2000 + 4.9889 -2.2657 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.2619 -3.9746 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2619 -0.5105 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 4.6783 0.2942 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -0.0105 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2619 -0.5105 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -1.0105 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.6783 -1.3152 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9889 1.2448 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7619 -1.3765 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 0.4895 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.3211 1.9891 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -1.5105 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -0.0105 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -1.0105 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7619 -1.3765 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2619 -2.2425 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.6318 2.9396 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.2619 -2.2425 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7619 -3.1086 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7619 -3.1086 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9639 3.6839 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6103 3.1458 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2619 -3.9746 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2746 4.6344 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.9209 4.0963 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.7619 -4.8406 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2531 4.8406 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7619 -4.8406 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.1542 0.1001 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8445 -0.2984 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5956 1.3726 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 1.1095 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9385 1.5012 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7742 2.2811 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -2.1305 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 0.2995 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 -1.3205 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0719 -0.8396 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6419 -2.2425 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.8819 -2.2425 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4519 -3.6455 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.3572 3.5560 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0243 2.6843 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1542 -3.3640 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8445 -3.7625 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.8605 5.0959 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.5276 4.2242 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8695 -5.4512 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1793 -5.0527 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4457 5.4299 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7619 -5.4606 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.3819 -4.8406 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7619 -4.2206 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 8 2 0 0 0 0 + 2 21 1 0 0 0 0 + 2 24 1 0 0 0 0 + 3 4 1 0 0 0 0 + 3 6 1 0 0 0 0 + 3 8 1 0 0 0 0 + 4 5 1 0 0 0 0 + 4 9 2 0 0 0 0 + 5 7 1 0 0 0 0 + 5 11 2 0 0 0 0 + 6 10 1 0 0 0 0 + 6 30 1 0 0 0 0 + 6 31 1 0 0 0 0 + 7 8 1 0 0 0 0 + 7 13 2 0 0 0 0 + 9 12 1 0 0 0 0 + 9 32 1 0 0 0 0 + 10 16 2 0 0 0 0 + 10 17 1 0 0 0 0 + 11 14 1 0 0 0 0 + 11 33 1 0 0 0 0 + 12 18 1 0 0 0 0 + 12 34 1 0 0 0 0 + 12 35 1 0 0 0 0 + 13 15 1 0 0 0 0 + 13 36 1 0 0 0 0 + 14 15 2 0 0 0 0 + 14 37 1 0 0 0 0 + 15 38 1 0 0 0 0 + 16 19 1 0 0 0 0 + 16 39 1 0 0 0 0 + 17 20 2 0 0 0 0 + 17 40 1 0 0 0 0 + 18 22 2 0 0 0 0 + 18 23 1 0 0 0 0 + 19 21 2 0 0 0 0 + 19 41 1 0 0 0 0 + 20 21 1 0 0 0 0 + 20 42 1 0 0 0 0 + 22 25 1 0 0 0 0 + 22 43 1 0 0 0 0 + 23 26 2 0 0 0 0 + 23 44 1 0 0 0 0 + 24 27 1 0 0 0 0 + 24 45 1 0 0 0 0 + 24 46 1 0 0 0 0 + 25 28 2 0 0 0 0 + 25 47 1 0 0 0 0 + 26 28 1 0 0 0 0 + 26 48 1 0 0 0 0 + 27 29 1 0 0 0 0 + 27 49 1 0 0 0 0 + 27 50 1 0 0 0 0 + 28 51 1 0 0 0 0 + 29 52 1 0 0 0 0 + 29 53 1 0 0 0 0 + 29 54 1 0 0 0 0 +M END +> +10000037 + +> +1 + +> +557 + +> +2 + +> +0 + +> +7 + +> +AAADceB6MAAAAAAAAAAAAAAAAAAAAWAAAAAwYMAAAAAAAFgBUAAAHgAAAAAADAzhmAYyBoMABACIAiVSUACCCAAkIgAIiAEODMgMJjKEtRuGOSjkxhGIqYec2IKOoAAAAAAQAABAAAAAACAAAAAAAAAAAA== + +> +(3E)-3-phenethylidene-2-[(4-propoxyphenyl)methyl]isoindolin-1-one + +> +(3E)-3-phenethylidene-2-[(4-propoxyphenyl)methyl]-1-isoindolinone + +> +(3E)-3-phenethylidene-2-[(4-propoxyphenyl)methyl]isoindol-1-one + +> +(3E)-3-phenethylidene-2-[(4-propoxyphenyl)methyl]isoindol-1-one + +> +(3E)-3-phenethylidene-2-(4-propoxybenzyl)isoindolin-1-one + +> +InChI=1S/C26H25NO2/c1-2-18-29-22-15-12-21(13-16-22)19-27-25(17-14-20-8-4-3-5-9-20)23-10-6-7-11-24(23)26(27)28/h3-13,15-17H,2,14,18-19H2,1H3/b25-17+ + +> +JLSZXXQMLPAVQC-KOEQRZSOSA-N + +> +5.7 + +> +383.188529 + +> +C26H25NO2 + +> +383.4822 + +> +CCCOC1=CC=C(C=C1)CN2C(=CCC3=CC=CC=C3)C4=CC=CC=C4C2=O + +> +CCCOC1=CC=C(C=C1)CN2/C(=C/CC3=CC=CC=C3)/C4=CC=CC=C4C2=O + +> +29.5 + +> +383.188529 + +> +0 + +> +29 + +> +0 + +> +0 + +> +1 + +> +0 + +> +0 + +> +1 + +> +1 + +> +1 +5 +255 + +> +10 16 8 +10 17 8 +11 14 8 +13 15 8 +14 15 8 +16 19 8 +17 20 8 +18 22 8 +18 23 8 +19 21 8 +20 21 8 +22 25 8 +23 26 8 +25 28 8 +26 28 8 +5 11 8 +5 7 8 +7 13 8 + +$$$$ +10000038 + -OEChem-11110908022D + + 54 56 0 0 0 0 0 0 0999 V2000 + 3.7321 1.7500 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 1.7500 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 0.7500 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -0.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -1.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5981 -0.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5981 -0.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 0.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -2.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -3.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 -1.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 0.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 -3.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 -2.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -3.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 -0.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 -4.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0622 -1.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -4.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 -0.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -5.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0622 -0.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 2.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 3.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 3.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 3.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 4.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 4.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 5.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0611 -2.0600 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5981 -0.1300 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9781 -0.7500 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5981 -1.3700 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5981 -0.1300 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2181 -0.7500 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5981 -1.3700 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1541 1.2869 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0010 1.0600 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.7741 0.2131 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8671 -3.4400 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 -2.8700 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0611 -3.4400 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.7932 -0.4400 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8671 -5.0600 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5991 -2.0600 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0611 -5.0600 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 0.3700 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -5.8700 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5991 -0.4400 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 3.4400 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2690 3.4400 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 5.0600 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2690 5.0600 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 5.8700 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 3 1 0 0 0 0 + 1 23 1 0 0 0 0 + 2 23 2 0 0 0 0 + 3 8 2 0 0 0 0 + 4 5 1 0 0 0 0 + 4 6 1 0 0 0 0 + 4 7 1 0 0 0 0 + 4 8 1 0 0 0 0 + 5 9 2 0 0 0 0 + 5 30 1 0 0 0 0 + 6 31 1 0 0 0 0 + 6 32 1 0 0 0 0 + 6 33 1 0 0 0 0 + 7 34 1 0 0 0 0 + 7 35 1 0 0 0 0 + 7 36 1 0 0 0 0 + 8 12 1 0 0 0 0 + 9 10 1 0 0 0 0 + 9 11 1 0 0 0 0 + 10 13 2 0 0 0 0 + 10 15 1 0 0 0 0 + 11 14 2 0 0 0 0 + 11 16 1 0 0 0 0 + 12 37 1 0 0 0 0 + 12 38 1 0 0 0 0 + 12 39 1 0 0 0 0 + 13 17 1 0 0 0 0 + 13 40 1 0 0 0 0 + 14 18 1 0 0 0 0 + 14 41 1 0 0 0 0 + 15 19 2 0 0 0 0 + 15 42 1 0 0 0 0 + 16 20 2 0 0 0 0 + 16 43 1 0 0 0 0 + 17 21 2 0 0 0 0 + 17 44 1 0 0 0 0 + 18 22 2 0 0 0 0 + 18 45 1 0 0 0 0 + 19 21 1 0 0 0 0 + 19 46 1 0 0 0 0 + 20 22 1 0 0 0 0 + 20 47 1 0 0 0 0 + 21 48 1 0 0 0 0 + 22 49 1 0 0 0 0 + 23 24 1 0 0 0 0 + 24 25 2 0 0 0 0 + 24 26 1 0 0 0 0 + 25 27 1 0 0 0 0 + 25 50 1 0 0 0 0 + 26 28 2 0 0 0 0 + 26 51 1 0 0 0 0 + 27 29 2 0 0 0 0 + 27 52 1 0 0 0 0 + 28 29 1 0 0 0 0 + 28 53 1 0 0 0 0 + 29 54 1 0 0 0 0 +M END +> +10000038 + +> +1 + +> +566 + +> +3 + +> +0 + +> +7 + +> +AAADceB6MAAAAAAAAAAAAAAAAAAAAAAAAAAwYMAAAAAAAAABUAAAHgAEAAAADgiBmAAyCIAQAACoAyDyCAACAAAkAAAIiAEwCMgIJjKAFRCAMQAkwAEIiYeIyOCPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + +> +[(E)-(1,2,2-trimethyl-4,4-diphenyl-but-3-enylidene)amino] benzoate + +> +benzoic acid [(E)-(1,2,2-trimethyl-4,4-diphenylbut-3-enylidene)amino] ester + +> +[(E)-(3,3-dimethyl-5,5-diphenylpent-4-en-2-ylidene)amino] benzoate + +> +[(E)-(3,3-dimethyl-5,5-diphenyl-pent-4-en-2-ylidene)amino] benzoate + +> +benzoic acid [(E)-(1,2,2-trimethyl-4,4-diphenyl-but-3-enylidene)amino] ester + +> +InChI=1S/C26H25NO2/c1-20(27-29-25(28)23-17-11-6-12-18-23)26(2,3)19-24(21-13-7-4-8-14-21)22-15-9-5-10-16-22/h4-19H,1-3H3/b27-20+ + +> +UKYKXQXQIHNEJS-NHFJDJAPSA-N + +> +7 + +> +383.188529 + +> +C26H25NO2 + +> +383.4822 + +> +CC(=NOC(=O)C1=CC=CC=C1)C(C)(C)C=C(C2=CC=CC=C2)C3=CC=CC=C3 + +> +C/C(=N\OC(=O)C1=CC=CC=C1)/C(C)(C)C=C(C2=CC=CC=C2)C3=CC=CC=C3 + +> +38.7 + +> +383.188529 + +> +0 + +> +29 + +> +0 + +> +0 + +> +1 + +> +0 + +> +0 + +> +1 + +> +1 + +> +1 +5 +255 + +> +10 13 8 +10 15 8 +11 14 8 +11 16 8 +13 17 8 +14 18 8 +15 19 8 +16 20 8 +17 21 8 +18 22 8 +19 21 8 +20 22 8 +24 25 8 +24 26 8 +25 27 8 +26 28 8 +27 29 8 +28 29 8 + +$$$$ +10000039 + -OEChem-11110908022D + + 57 59 0 1 0 0 0 0 0999 V2000 + 2.8660 0.2500 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 -1.7500 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -5.7500 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 1.2500 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 3.2500 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -1.2500 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 1.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 1.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 0.2500 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 3.7321 2.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 2.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -0.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -0.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 4.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -1.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 4.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 4.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -1.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 5.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 5.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -2.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 6.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -3.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -3.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -4.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -4.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -4.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -6.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1215 1.8577 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5200 1.1674 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6762 1.1674 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0747 1.8577 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1350 0.5600 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5200 3.3326 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1215 2.6423 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0747 2.6423 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6762 3.3326 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0747 -0.3577 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6762 0.3326 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8535 -1.1423 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2521 -1.8326 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2690 -1.5600 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1951 4.4400 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0010 4.4400 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6540 -1.1674 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.2554 -1.8577 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1951 6.0600 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0010 6.0600 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8671 -1.4400 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 6.8700 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2690 -2.9400 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 -2.9400 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2690 -4.5600 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 -4.5600 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.6900 -5.7131 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 -6.5600 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3100 -6.7869 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 13 2 0 0 0 0 + 2 15 1 0 0 0 0 + 2 49 1 0 0 0 0 + 3 27 1 0 0 0 0 + 3 28 1 0 0 0 0 + 4 7 1 0 0 0 0 + 4 8 1 0 0 0 0 + 4 9 1 0 0 0 0 + 5 10 1 0 0 0 0 + 5 11 1 0 0 0 0 + 5 14 1 0 0 0 0 + 6 13 1 0 0 0 0 + 6 18 1 0 0 0 0 + 6 42 1 0 0 0 0 + 7 10 1 0 0 0 0 + 7 29 1 0 0 0 0 + 7 30 1 0 0 0 0 + 8 11 1 0 0 0 0 + 8 31 1 0 0 0 0 + 8 32 1 0 0 0 0 + 9 12 1 0 0 0 0 + 9 13 1 0 0 0 0 + 9 33 1 0 0 0 0 + 10 34 1 0 0 0 0 + 10 35 1 0 0 0 0 + 11 36 1 0 0 0 0 + 11 37 1 0 0 0 0 + 12 15 1 0 0 0 0 + 12 38 1 0 0 0 0 + 12 39 1 0 0 0 0 + 14 16 2 0 0 0 0 + 14 17 1 0 0 0 0 + 15 40 1 0 0 0 0 + 15 41 1 0 0 0 0 + 16 19 1 0 0 0 0 + 16 43 1 0 0 0 0 + 17 20 2 0 0 0 0 + 17 44 1 0 0 0 0 + 18 21 1 0 0 0 0 + 18 45 1 0 0 0 0 + 18 46 1 0 0 0 0 + 19 22 2 0 0 0 0 + 19 47 1 0 0 0 0 + 20 22 1 0 0 0 0 + 20 48 1 0 0 0 0 + 21 23 2 0 0 0 0 + 21 24 1 0 0 0 0 + 22 50 1 0 0 0 0 + 23 25 1 0 0 0 0 + 23 51 1 0 0 0 0 + 24 26 2 0 0 0 0 + 24 52 1 0 0 0 0 + 25 27 2 0 0 0 0 + 25 53 1 0 0 0 0 + 26 27 1 0 0 0 0 + 26 54 1 0 0 0 0 + 28 55 1 0 0 0 0 + 28 56 1 0 0 0 0 + 28 57 1 0 0 0 0 +M END +> +10000039 + +> +1 + +> +454 + +> +5 + +> +2 + +> +8 + +> +AAADceB7MAAAAAAAAAAAAAAAAAAAAAAAAAA8YIAAAAAAAAABQAAAHgAQCAAADCzhmAYyxoPABgCIAiVSUACCCAAhIgAIiIGObIgMdjLEsbuUcChm1hHI6Aey0AIOIAAAAAAAAABAAAAAAAAAAAAAAAAAAA== + +> +4-hydroxy-N-[(4-methoxyphenyl)methyl]-2-(4-phenylpiperazin-1-yl)butanamide + +> +4-hydroxy-N-[(4-methoxyphenyl)methyl]-2-(4-phenyl-1-piperazinyl)butanamide + +> +4-hydroxy-N-[(4-methoxyphenyl)methyl]-2-(4-phenylpiperazin-1-yl)butanamide + +> +4-hydroxy-N-[(4-methoxyphenyl)methyl]-2-(4-phenylpiperazin-1-yl)butanamide + +> +4-hydroxy-N-p-anisyl-2-(4-phenylpiperazino)butyramide + +> +InChI=1S/C22H29N3O3/c1-28-20-9-7-18(8-10-20)17-23-22(27)21(11-16-26)25-14-12-24(13-15-25)19-5-3-2-4-6-19/h2-10,21,26H,11-17H2,1H3,(H,23,27) + +> +OTTABHZTXCDGMJ-UHFFFAOYSA-N + +> +2.4 + +> +383.220892 + +> +C22H29N3O3 + +> +383.48396 + +> +COC1=CC=C(C=C1)CNC(=O)C(CCO)N2CCN(CC2)C3=CC=CC=C3 + +> +COC1=CC=C(C=C1)CNC(=O)C(CCO)N2CCN(CC2)C3=CC=CC=C3 + +> +65 + +> +383.220892 + +> +0 + +> +28 + +> +0 + +> +1 + +> +0 + +> +0 + +> +0 + +> +1 + +> +2 + +> +1 +5 +255 + +> +14 16 8 +14 17 8 +16 19 8 +17 20 8 +19 22 8 +20 22 8 +21 23 8 +21 24 8 +23 25 8 +24 26 8 +25 27 8 +26 27 8 +9 4 3 + +$$$$ +10000040 + -OEChem-11110908022D + + 57 59 0 0 0 0 0 0 0999 V2000 + 6.3301 1.7500 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -4.2500 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 6.2500 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -1.2500 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -3.2500 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 4.7500 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -1.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -1.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -2.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -2.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -0.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 0.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -4.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 1.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -4.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -4.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -5.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -5.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -6.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 2.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -4.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 3.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 3.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 4.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 4.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 4.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 5.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 6.2500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0747 -1.8577 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6762 -1.1674 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5200 -1.1674 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1215 -1.8577 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0747 -2.6423 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6762 -3.3326 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5200 -3.3326 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1215 -2.6423 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9875 -0.3577 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.3860 0.3326 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0747 0.3577 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6762 -0.3326 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8535 1.1423 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2520 1.8326 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0010 -4.4400 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1951 -6.0600 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0010 -6.0600 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -6.8700 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.6900 -4.2131 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 -5.0600 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3100 -5.2869 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9272 2.9400 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7331 2.9400 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7331 4.5600 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 5.3700 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0611 4.4400 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0421 6.7869 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1951 6.5600 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.4221 5.7131 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 14 1 0 0 0 0 + 1 20 1 0 0 0 0 + 2 15 1 0 0 0 0 + 2 21 1 0 0 0 0 + 3 27 2 0 0 0 0 + 4 7 1 0 0 0 0 + 4 8 1 0 0 0 0 + 4 11 1 0 0 0 0 + 5 9 1 0 0 0 0 + 5 10 1 0 0 0 0 + 5 13 1 0 0 0 0 + 6 24 1 0 0 0 0 + 6 27 1 0 0 0 0 + 6 54 1 0 0 0 0 + 7 9 1 0 0 0 0 + 7 29 1 0 0 0 0 + 7 30 1 0 0 0 0 + 8 10 1 0 0 0 0 + 8 31 1 0 0 0 0 + 8 32 1 0 0 0 0 + 9 33 1 0 0 0 0 + 9 34 1 0 0 0 0 + 10 35 1 0 0 0 0 + 10 36 1 0 0 0 0 + 11 12 1 0 0 0 0 + 11 37 1 0 0 0 0 + 11 38 1 0 0 0 0 + 12 14 1 0 0 0 0 + 12 39 1 0 0 0 0 + 12 40 1 0 0 0 0 + 13 15 1 0 0 0 0 + 13 16 2 0 0 0 0 + 14 41 1 0 0 0 0 + 14 42 1 0 0 0 0 + 15 17 2 0 0 0 0 + 16 18 1 0 0 0 0 + 16 43 1 0 0 0 0 + 17 19 1 0 0 0 0 + 17 44 1 0 0 0 0 + 18 19 2 0 0 0 0 + 18 45 1 0 0 0 0 + 19 46 1 0 0 0 0 + 20 22 1 0 0 0 0 + 20 23 2 0 0 0 0 + 21 47 1 0 0 0 0 + 21 48 1 0 0 0 0 + 21 49 1 0 0 0 0 + 22 24 2 0 0 0 0 + 22 50 1 0 0 0 0 + 23 25 1 0 0 0 0 + 23 51 1 0 0 0 0 + 24 26 1 0 0 0 0 + 25 26 2 0 0 0 0 + 25 52 1 0 0 0 0 + 26 53 1 0 0 0 0 + 27 28 1 0 0 0 0 + 28 55 1 0 0 0 0 + 28 56 1 0 0 0 0 + 28 57 1 0 0 0 0 +M END +> +10000040 + +> +1 + +> +468 + +> +5 + +> +1 + +> +8 + +> +AAADceB7MAAAAAAAAAAAAAAAAAAAAAAAAAA8YIAAAAAAAAABQAAAHgAQAAAACAzhkAYyxoPABACIACVSUACCCAAhIgAIiAAPbIgOZiLEsZuXOCjm1BnY6AeQUAMAAAAAAgAAEAAAAAAEAAAgAAAAAAAAAA== + +> +N-[3-[3-[4-(2-methoxyphenyl)piperazin-1-yl]propoxy]phenyl]acetamide + +> +N-[3-[3-[4-(2-methoxyphenyl)-1-piperazinyl]propoxy]phenyl]acetamide + +> +N-[3-[3-[4-(2-methoxyphenyl)piperazin-1-yl]propoxy]phenyl]acetamide + +> +N-[3-[3-[4-(2-methoxyphenyl)piperazin-1-yl]propoxy]phenyl]ethanamide + +> +N-[3-[3-[4-(2-methoxyphenyl)piperazino]propoxy]phenyl]acetamide + +> +InChI=1S/C22H29N3O3/c1-18(26)23-19-7-5-8-20(17-19)28-16-6-11-24-12-14-25(15-13-24)21-9-3-4-10-22(21)27-2/h3-5,7-10,17H,6,11-16H2,1-2H3,(H,23,26) + +> +PDKUFFOWESQNQA-UHFFFAOYSA-N + +> +3 + +> +383.220892 + +> +C22H29N3O3 + +> +383.48396 + +> +CC(=O)NC1=CC(=CC=C1)OCCCN2CCN(CC2)C3=CC=CC=C3OC + +> +CC(=O)NC1=CC(=CC=C1)OCCCN2CCN(CC2)C3=CC=CC=C3OC + +> +54 + +> +383.220892 + +> +0 + +> +28 + +> +0 + +> +0 + +> +0 + +> +0 + +> +0 + +> +1 + +> +2 + +> +1 +5 +255 + +> +13 15 8 +13 16 8 +15 17 8 +16 18 8 +17 19 8 +18 19 8 +20 22 8 +20 23 8 +22 24 8 +23 25 8 +24 26 8 +25 26 8 + +$$$$ +10000041 + -OEChem-11110908022D + + 54 58 0 1 0 0 0 0 0999 V2000 + 10.4111 -1.5491 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0294 -1.2191 0.0000 N 0 0 3 0 0 0 0 0 0 0 0 0 + 8.2971 1.1820 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 12.9475 3.1369 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0224 -2.4112 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 5.0559 -1.4118 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 2.9567 0.1012 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0316 -3.1369 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.9959 -2.2185 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 2.7672 -0.8807 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9491 0.2244 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 0.3923 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.7304 1.0752 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.6789 -2.9489 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7598 -0.5361 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.7575 -0.8354 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5281 0.4795 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.5101 -0.1152 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2876 0.8597 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4111 -0.5491 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.9111 1.6415 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.3120 -0.1153 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.9111 1.6415 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.5346 0.8597 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.0646 -0.8354 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.5251 1.1820 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.0623 -0.5361 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.2941 0.4794 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.7363 2.1594 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2435 -2.9904 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.3885 -1.9350 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.4183 -3.0464 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.1807 -3.7387 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5763 -2.0006 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.1798 -0.6822 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4914 -1.4360 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5106 0.4872 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7774 0.8201 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.1805 0.9854 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4069 0.5727 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.8195 -0.2009 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.3344 1.2155 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5901 1.6792 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.1265 0.9350 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2260 -3.3724 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.1023 -3.4018 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.1317 -2.5255 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9025 -1.4382 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.9363 0.6645 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.6421 2.2001 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.1801 2.2001 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.9196 -1.4383 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 13.5151 -0.9596 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 13.8858 0.6644 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 20 2 0 0 0 0 + 2 6 1 0 0 0 0 + 2 9 1 0 0 0 0 + 2 15 1 0 0 0 0 + 3 17 2 0 0 0 0 + 3 19 1 0 0 0 0 + 4 29 3 0 0 0 0 + 5 8 1 0 0 0 0 + 5 9 1 0 0 0 0 + 5 10 1 0 0 0 0 + 5 30 1 0 0 0 0 + 6 8 1 0 0 0 0 + 6 11 1 0 0 0 0 + 6 31 1 0 0 0 0 + 7 10 1 0 0 0 0 + 7 11 1 0 0 0 0 + 7 12 1 0 0 0 0 + 7 13 1 0 0 0 0 + 8 32 1 0 0 0 0 + 8 33 1 0 0 0 0 + 9 14 1 0 0 0 0 + 9 34 1 0 0 0 0 + 10 35 1 0 0 0 0 + 10 36 1 0 0 0 0 + 11 37 1 0 0 0 0 + 11 38 1 0 0 0 0 + 12 39 1 0 0 0 0 + 12 40 1 0 0 0 0 + 12 41 1 0 0 0 0 + 13 42 1 0 0 0 0 + 13 43 1 0 0 0 0 + 13 44 1 0 0 0 0 + 14 45 1 0 0 0 0 + 14 46 1 0 0 0 0 + 14 47 1 0 0 0 0 + 15 16 2 0 0 0 0 + 15 17 1 0 0 0 0 + 16 18 1 0 0 0 0 + 16 48 1 0 0 0 0 + 17 49 1 0 0 0 0 + 18 19 2 0 0 0 0 + 18 20 1 0 0 0 0 + 19 21 1 0 0 0 0 + 20 22 1 0 0 0 0 + 21 23 2 0 0 0 0 + 21 50 1 0 0 0 0 + 22 24 1 0 0 0 0 + 22 25 2 0 0 0 0 + 23 24 1 0 0 0 0 + 23 51 1 0 0 0 0 + 24 26 2 0 0 0 0 + 25 27 1 0 0 0 0 + 25 52 1 0 0 0 0 + 26 28 1 0 0 0 0 + 26 29 1 0 0 0 0 + 27 28 2 0 0 0 0 + 27 53 1 0 0 0 0 + 28 54 1 0 0 0 0 +M END +> +10000041 + +> +1 + +> +748 + +> +4 + +> +0 + +> +1 + +> +AAADceB7IAAAAAAAAAAAAAAAAAAAAWAAAAA8YIAABcgAAAABwAAAHgAAAAAADyzBngQ+wJMIEAC4B7R3RACigCA1EiAI2CE4dNgI4PrAlZGUIYhggADIyc8cicCPgAACAAAQAAAAAASAACAAAAAAAAAAAA== + +> +InChI=1S/C25H25N3O/c1-15-17-9-18(12-25(2,3)11-17)28(15)19-10-22-23(27-14-19)8-7-20-16(13-26)5-4-6-21(20)24(22)29/h4-8,10,14-15,17-18H,9,11-12H2,1-3H3 + +> +GFNRKMWOOLHGRN-UHFFFAOYSA-N + +> +5.2 + +> +383.199762 + +> +C25H25N3O + +> +383.4855 + +> +CC1C2CC(N1C3=CN=C4C=CC5=C(C=CC=C5C(=O)C4=C3)C#N)CC(C2)(C)C + +> +CC1C2CC(N1C3=CN=C4C=CC5=C(C=CC=C5C(=O)C4=C3)C#N)CC(C2)(C)C + +> +57 + +> +383.199762 + +> +0 + +> +29 + +> +0 + +> +3 + +> +0 + +> +0 + +> +0 + +> +1 + +> +1 + +> +1 +5 +255 + +> +15 16 8 +15 17 8 +16 18 8 +18 19 8 +18 20 8 +19 21 8 +20 22 8 +21 23 8 +22 24 8 +22 25 8 +23 24 8 +24 26 8 +25 27 8 +26 28 8 +27 28 8 +3 17 8 +3 19 8 +5 8 3 +6 8 3 +9 14 3 + +$$$$ +10000042 + -OEChem-11110908022D + + 54 57 0 0 0 0 0 0 0999 V2000 + 6.3301 -0.5173 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0622 2.4827 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -2.5173 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0622 5.4827 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0622 3.4827 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9282 3.9827 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9282 4.9827 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -2.0173 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 1.9827 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 3.9827 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -1.0173 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 -3.5173 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 4.9827 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 0.4827 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8222 3.4480 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1962 0.9827 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -4.0173 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8222 5.5173 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 2.4827 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7282 3.9619 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 0.9827 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7282 5.0035 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 1.9827 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 5.4827 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -3.5173 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -5.0173 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -4.0173 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -5.5173 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -5.0173 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6762 -2.5999 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0747 -1.9097 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5991 2.1727 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.6592 3.6727 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8101 -4.0999 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2087 -3.4097 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2521 -0.4347 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8535 -1.1250 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0611 -2.2073 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8150 2.8281 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7331 0.6727 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8150 6.1373 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 3.1027 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.2639 3.6498 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9272 0.6727 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.2639 5.3156 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9272 2.2927 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.6401 6.0196 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.7932 5.7927 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0201 4.9457 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -2.8973 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2690 -5.3273 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 -3.7073 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -6.1373 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 -5.3273 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 11 1 0 0 0 0 + 1 14 1 0 0 0 0 + 2 5 1 0 0 0 0 + 2 9 1 0 0 0 0 + 2 32 1 0 0 0 0 + 3 8 1 0 0 0 0 + 3 12 1 0 0 0 0 + 3 38 1 0 0 0 0 + 4 7 1 0 0 0 0 + 4 13 2 0 0 0 0 + 5 6 1 0 0 0 0 + 5 10 2 0 0 0 0 + 6 7 1 0 0 0 0 + 6 15 2 0 0 0 0 + 7 18 2 0 0 0 0 + 8 11 1 0 0 0 0 + 8 30 1 0 0 0 0 + 8 31 1 0 0 0 0 + 9 16 2 0 0 0 0 + 9 19 1 0 0 0 0 + 10 13 1 0 0 0 0 + 10 33 1 0 0 0 0 + 11 36 1 0 0 0 0 + 11 37 1 0 0 0 0 + 12 17 1 0 0 0 0 + 12 34 1 0 0 0 0 + 12 35 1 0 0 0 0 + 13 24 1 0 0 0 0 + 14 16 1 0 0 0 0 + 14 21 2 0 0 0 0 + 15 20 1 0 0 0 0 + 15 39 1 0 0 0 0 + 16 40 1 0 0 0 0 + 17 25 2 0 0 0 0 + 17 26 1 0 0 0 0 + 18 22 1 0 0 0 0 + 18 41 1 0 0 0 0 + 19 23 2 0 0 0 0 + 19 42 1 0 0 0 0 + 20 22 2 0 0 0 0 + 20 43 1 0 0 0 0 + 21 23 1 0 0 0 0 + 21 44 1 0 0 0 0 + 22 45 1 0 0 0 0 + 23 46 1 0 0 0 0 + 24 47 1 0 0 0 0 + 24 48 1 0 0 0 0 + 24 49 1 0 0 0 0 + 25 27 1 0 0 0 0 + 25 50 1 0 0 0 0 + 26 28 2 0 0 0 0 + 26 51 1 0 0 0 0 + 27 29 2 0 0 0 0 + 27 52 1 0 0 0 0 + 28 29 1 0 0 0 0 + 28 53 1 0 0 0 0 + 29 54 1 0 0 0 0 +M END +> +10000042 + +> +1 + +> +467 + +> +4 + +> +2 + +> +8 + +> +AAADceB7IAAAAAAAAAAAAAAAAAAAAAAAAAA8YMEAAAAAAACx9AAAHgAQAAAADAzhngYyxvLIFACgAyRiRACCiCAhIiAImKA+bJgOJuLE8duHPCjm0BnY6AeQ0IMOAAAAAgACAAAAAAAEAAQAAAAAAAAAAA== + +> +N-[3-[2-(benzylamino)ethoxy]phenyl]-2-methyl-quinolin-4-amine + +> +N-[3-[2-(benzylamino)ethoxy]phenyl]-2-methyl-4-quinolinamine + +> +N-[3-[2-(benzylamino)ethoxy]phenyl]-2-methylquinolin-4-amine + +> +2-methyl-N-[3-[2-(phenylmethylamino)ethoxy]phenyl]quinolin-4-amine + +> +benzyl-[2-[3-[(2-methyl-4-quinolyl)amino]phenoxy]ethyl]amine + +> +InChI=1S/C25H25N3O/c1-19-16-25(23-12-5-6-13-24(23)27-19)28-21-10-7-11-22(17-21)29-15-14-26-18-20-8-3-2-4-9-20/h2-13,16-17,26H,14-15,18H2,1H3,(H,27,28) + +> +KQXDPLXNOGCKIJ-UHFFFAOYSA-N + +> +5.2 + +> +383.199762 + +> +C25H25N3O + +> +383.4855 + +> +CC1=NC2=CC=CC=C2C(=C1)NC3=CC(=CC=C3)OCCNCC4=CC=CC=C4 + +> +CC1=NC2=CC=CC=C2C(=C1)NC3=CC(=CC=C3)OCCNCC4=CC=CC=C4 + +> +46.2 + +> +383.199762 + +> +0 + +> +29 + +> +0 + +> +0 + +> +0 + +> +0 + +> +0 + +> +1 + +> +8 + +> +1 +5 +255 + +> +10 13 8 +14 16 8 +14 21 8 +15 20 8 +17 25 8 +17 26 8 +18 22 8 +19 23 8 +20 22 8 +21 23 8 +25 27 8 +26 28 8 +27 29 8 +28 29 8 +4 13 8 +4 7 8 +5 10 8 +5 6 8 +6 15 8 +6 7 8 +7 18 8 +9 16 8 +9 19 8 + +$$$$ +10000043 + -OEChem-11110908022D + + 57 59 0 1 0 0 0 0 0999 V2000 + 6.2781 0.4164 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9174 4.3441 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.6783 -1.6909 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 4.6783 -3.3003 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -1.4956 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -2.9956 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -4.4956 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9889 -0.7403 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 4.3211 0.0040 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.9674 -0.5341 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 4.6318 0.9545 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9639 1.6988 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2746 2.6493 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.6353 -1.2784 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -1.9956 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2619 -2.4956 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.6067 3.3936 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 3.7321 -2.9956 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2566 0.6226 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6282 3.1874 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -3.4956 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5673 1.5731 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -1.9956 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8994 2.3174 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5458 1.7794 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2101 3.2680 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.8564 2.7299 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.1886 3.4742 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.3823 -0.8682 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7742 0.2960 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9385 -0.4839 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5534 -0.0727 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1787 0.6625 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0143 1.4424 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.4170 1.9908 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5814 1.2109 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8215 2.3573 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.6571 3.1372 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.1738 -1.6925 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0494 -1.7399 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0968 -0.8644 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.8819 -2.4956 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1927 3.8551 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2772 0.0030 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8704 0.5353 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.5004 3.7941 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0216 3.0595 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.7561 2.5807 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5033 4.8056 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 -1.6856 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2928 2.1896 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9598 1.3179 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3291 -4.8056 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.4030 -4.8056 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7960 3.7294 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.4631 2.8577 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.3812 4.0635 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10 1 1 1 0 0 0 + 1 19 1 0 0 0 0 + 2 17 1 0 0 0 0 + 2 49 1 0 0 0 0 + 3 8 1 0 0 0 0 + 3 15 1 0 0 0 0 + 3 16 1 0 0 0 0 + 4 16 2 0 0 0 0 + 4 18 1 0 0 0 0 + 5 15 2 0 0 0 0 + 5 23 1 0 0 0 0 + 6 21 1 0 0 0 0 + 6 23 2 0 0 0 0 + 7 21 1 0 0 0 0 + 7 53 1 0 0 0 0 + 7 54 1 0 0 0 0 + 8 9 1 6 0 0 0 + 8 10 1 0 0 0 0 + 8 29 1 0 0 0 0 + 9 11 1 0 0 0 0 + 9 30 1 0 0 0 0 + 9 31 1 0 0 0 0 + 10 14 1 0 0 0 0 + 10 32 1 0 0 0 0 + 11 12 1 0 0 0 0 + 11 33 1 0 0 0 0 + 11 34 1 0 0 0 0 + 12 13 1 0 0 0 0 + 12 35 1 0 0 0 0 + 12 36 1 0 0 0 0 + 13 17 1 0 0 0 0 + 13 37 1 0 0 0 0 + 13 38 1 0 0 0 0 + 14 39 1 0 0 0 0 + 14 40 1 0 0 0 0 + 14 41 1 0 0 0 0 + 15 18 1 0 0 0 0 + 16 42 1 0 0 0 0 + 17 20 1 0 0 0 0 + 17 43 1 0 0 0 0 + 18 21 2 0 0 0 0 + 19 22 1 0 0 0 0 + 19 44 1 0 0 0 0 + 19 45 1 0 0 0 0 + 20 46 1 0 0 0 0 + 20 47 1 0 0 0 0 + 20 48 1 0 0 0 0 + 22 24 2 0 0 0 0 + 22 25 1 0 0 0 0 + 23 50 1 0 0 0 0 + 24 26 1 0 0 0 0 + 24 51 1 0 0 0 0 + 25 27 2 0 0 0 0 + 25 52 1 0 0 0 0 + 26 28 2 0 0 0 0 + 26 55 1 0 0 0 0 + 27 28 1 0 0 0 0 + 27 56 1 0 0 0 0 + 28 57 1 0 0 0 0 +M END +> +10000043 + +> +1 + +> +449 + +> +6 + +> +2 + +> +10 + +> +AAADceB7sAAAAAAAAAAAAAAAAAAAAWAAAAA8QAAAAAAAAFgB/AAAHgAQCAAADDzhnwY38L9MFgCgAyZjZACCgC0xEKAJ2CAoXJiKfiLA+ViXdAgswAPYqCeQwKAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + +> +(7R,8S)-7-(6-aminopurin-9-yl)-8-benzyloxy-nonan-2-ol + +> +(7R,8S)-7-(6-aminopurin-9-yl)-8-benzyloxy-2-nonanol + +> +(7R,8S)-7-(6-aminopurin-9-yl)-8-phenylmethoxynonan-2-ol + +> +(7R,8S)-7-(6-aminopurin-9-yl)-8-phenylmethoxy-nonan-2-ol + +> +(7R,8S)-7-adenin-9-yl-8-benzoxy-nonan-2-ol + +> +InChI=1S/C21H29N5O2/c1-15(27)8-6-7-11-18(16(2)28-12-17-9-4-3-5-10-17)26-14-25-19-20(22)23-13-24-21(19)26/h3-5,9-10,13-16,18,27H,6-8,11-12H2,1-2H3,(H2,22,23,24)/t15?,16-,18+/m0/s1 + +> +WIGQBFAJTZVETE-BSRYDQRCSA-N + +> +2.8 + +> +383.232125 + +> +C21H29N5O2 + +> +383.48726 + +> +CC(CCCCC(C(C)OCC1=CC=CC=C1)N2C=NC3=C2N=CN=C3N)O + +> +C[C@@H]([C@@H](CCCCC(C)O)N1C=NC2=C1N=CN=C2N)OCC3=CC=CC=C3 + +> +99.1 + +> +383.232125 + +> +0 + +> +28 + +> +2 + +> +1 + +> +0 + +> +0 + +> +0 + +> +1 + +> +3 + +> +1 +5 +255 + +> +10 1 5 +15 18 8 +18 21 8 +17 2 3 +22 24 8 +22 25 8 +24 26 8 +25 27 8 +26 28 8 +27 28 8 +3 15 8 +3 16 8 +4 16 8 +4 18 8 +5 15 8 +5 23 8 +6 21 8 +6 23 8 +8 9 6 + +$$$$ +10000044 + -OEChem-11110908022D + + 67 65 0 1 0 0 0 0 0999 V2000 + 6.1661 0.8660 0.0000 P 0 0 3 0 0 0 0 0 0 0 0 0 + 0.5369 4.8059 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0321 1.3660 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 5.3001 0.3660 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6661 1.7321 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 + 6.6661 0.0000 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1350 5.3059 0.0000 N 0 3 0 0 0 0 0 0 0 0 0 0 + 4.0010 5.8059 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8671 5.3059 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.2690 4.8059 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.7331 5.8059 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.6350 4.4399 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6350 6.1719 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.5991 5.3059 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4651 5.8059 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4030 5.3059 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.3312 5.3059 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1972 5.8059 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.0632 5.3059 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.9292 5.8059 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.7953 5.3059 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.6613 5.8059 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.5273 5.3059 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8982 0.8660 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.7642 1.3660 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.6025 6.2809 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.3996 6.2809 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.4685 4.8310 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2656 4.8310 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.8705 4.3310 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6675 4.3310 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.1316 6.2809 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.3346 6.2809 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.1719 4.7499 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9450 3.9030 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.0981 4.1299 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0981 5.8619 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.3250 6.7089 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1719 6.4819 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2006 4.8310 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.9976 4.8310 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0666 6.2809 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8637 6.2809 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.7297 4.8310 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.9326 4.8310 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.0044 5.7809 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.8015 5.7809 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.5957 6.2809 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.7987 6.2809 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4617 4.8310 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.6647 4.8310 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.3278 6.2809 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.5307 6.2809 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.3967 4.8310 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 12.1938 4.8310 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 5.1159 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 12.2628 6.2809 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 13.0598 6.2809 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 13.2173 4.7690 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 14.0643 4.9959 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 13.8373 5.8429 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.2967 0.3911 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4996 0.3911 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4542 1.9030 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.3011 1.6760 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0742 0.8291 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.7631 0.6760 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 3 1 0 0 0 0 + 1 4 1 0 0 0 0 + 1 5 1 0 0 0 0 + 1 6 2 0 0 0 0 + 2 16 1 0 0 0 0 + 2 56 1 0 0 0 0 + 3 24 1 0 0 0 0 + 4 67 1 0 0 0 0 + 7 8 1 0 0 0 0 + 7 10 1 0 0 0 0 + 7 12 1 0 0 0 0 + 7 13 1 0 0 0 0 + 8 9 1 0 0 0 0 + 8 26 1 0 0 0 0 + 8 27 1 0 0 0 0 + 9 11 1 0 0 0 0 + 9 28 1 0 0 0 0 + 9 29 1 0 0 0 0 + 10 16 1 0 0 0 0 + 10 30 1 0 0 0 0 + 10 31 1 0 0 0 0 + 11 14 1 0 0 0 0 + 11 32 1 0 0 0 0 + 11 33 1 0 0 0 0 + 12 34 1 0 0 0 0 + 12 35 1 0 0 0 0 + 12 36 1 0 0 0 0 + 13 37 1 0 0 0 0 + 13 38 1 0 0 0 0 + 13 39 1 0 0 0 0 + 14 15 1 0 0 0 0 + 14 40 1 0 0 0 0 + 14 41 1 0 0 0 0 + 15 17 1 0 0 0 0 + 15 42 1 0 0 0 0 + 15 43 1 0 0 0 0 + 16 46 1 0 0 0 0 + 16 47 1 0 0 0 0 + 17 18 1 0 0 0 0 + 17 44 1 0 0 0 0 + 17 45 1 0 0 0 0 + 18 19 1 0 0 0 0 + 18 48 1 0 0 0 0 + 18 49 1 0 0 0 0 + 19 20 1 0 0 0 0 + 19 50 1 0 0 0 0 + 19 51 1 0 0 0 0 + 20 21 1 0 0 0 0 + 20 52 1 0 0 0 0 + 20 53 1 0 0 0 0 + 21 22 1 0 0 0 0 + 21 54 1 0 0 0 0 + 21 55 1 0 0 0 0 + 22 23 1 0 0 0 0 + 22 57 1 0 0 0 0 + 22 58 1 0 0 0 0 + 23 59 1 0 0 0 0 + 23 60 1 0 0 0 0 + 23 61 1 0 0 0 0 + 24 25 1 0 0 0 0 + 24 62 1 0 0 0 0 + 24 63 1 0 0 0 0 + 25 64 1 0 0 0 0 + 25 65 1 0 0 0 0 + 25 66 1 0 0 0 0 +M CHG 2 5 -1 7 1 +M END +> +10000044 + +> +1 + +> +250 + +> +5 + +> +2 + +> +15 + +> +AAADcfB6OAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgAACCAACADhgAYCAAMAAxAAQAAAAIAAAAAAAAAAAAAIAAACEAIAgAAEAAAAAACQAAEQgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + +> +dodecyl-(2-hydroxyethyl)-dimethyl-ammonium; ethyl hydrogen phosphate + +> +dodecyl-(2-hydroxyethyl)-dimethylammonium; ethyl hydrogen phosphate + +> +dodecyl-(2-hydroxyethyl)-dimethylazanium; ethyl hydrogen phosphate + +> +dodecyl-(2-hydroxyethyl)-dimethyl-azanium; ethyl hydrogen phosphate + +> +ethyl hydrogen phosphate; 2-hydroxyethyl-lauryl-dimethyl-ammonium + +> +InChI=1S/C16H36NO.C2H7O4P/c1-4-5-6-7-8-9-10-11-12-13-14-17(2,3)15-16-18;1-2-6-7(3,4)5/h18H,4-16H2,1-3H3;2H2,1H3,(H2,3,4,5)/q+1;/p-1 + +> +SVVVDWDNCUGWMJ-UHFFFAOYSA-M + +> +383.28006 + +> +C18H42NO5P + +> +383.503541 + +> +CCCCCCCCCCCC[N+](C)(C)CCO.CCOP(=O)(O)[O-] + +> +CCCCCCCCCCCC[N+](C)(C)CCO.CCOP(=O)(O)[O-] + +> +89.8 + +> +383.28006 + +> +0 + +> +25 + +> +0 + +> +0 + +> +0 + +> +0 + +> +0 + +> +2 + +> +1 + +> +1 +5 +255 + +$$$$ +10000045 + -OEChem-11110908022D + + 52 54 0 1 0 0 0 0 0999 V2000 + 4.5981 1.4827 0.0000 S 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5981 1.4827 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5981 1.4827 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 0.4827 0.0000 N 0 0 3 0 0 0 0 0 0 0 0 0 + 6.3301 -2.5173 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2641 4.0035 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -0.0173 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -0.0173 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -1.0173 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 -1.0173 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 2.4827 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -1.5173 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 2.9827 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3301 -1.5173 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -2.5173 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 2.9827 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4641 3.9827 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 3.9827 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3580 2.4480 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 4.4827 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -3.0173 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -3.0173 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3580 4.5173 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2641 2.9619 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.7321 -4.0173 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0000 -4.0173 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -4.5173 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1215 -0.1250 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5200 0.5653 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6762 0.5653 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0747 -0.1250 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.3426 -0.9097 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.9441 -1.5999 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2520 -1.5999 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8535 -0.9097 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.6540 -0.9347 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.2554 -1.6250 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.5422 -0.9347 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.9407 -1.6250 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1951 2.6727 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1951 4.2927 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3509 1.8280 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.5981 5.1027 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2690 -2.7073 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 -2.7073 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3509 5.1373 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.7932 -2.8273 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8671 -2.8273 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7998 2.6498 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2690 -4.3273 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4631 -4.3273 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8660 -5.1373 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 2 0 0 0 0 + 1 3 2 0 0 0 0 + 1 4 1 0 0 0 0 + 1 11 1 0 0 0 0 + 4 7 1 0 0 0 0 + 4 8 1 0 0 0 0 + 5 14 1 0 0 0 0 + 5 47 1 0 0 0 0 + 5 48 1 0 0 0 0 + 6 23 2 0 0 0 0 + 6 24 1 0 0 0 0 + 7 9 1 0 0 0 0 + 7 28 1 0 0 0 0 + 7 29 1 0 0 0 0 + 8 10 1 0 0 0 0 + 8 30 1 0 0 0 0 + 8 31 1 0 0 0 0 + 9 12 1 0 0 0 0 + 9 32 1 0 0 0 0 + 9 33 1 0 0 0 0 + 10 14 1 0 0 0 0 + 10 34 1 0 0 0 0 + 10 35 1 0 0 0 0 + 11 13 1 0 0 0 0 + 11 16 2 0 0 0 0 + 12 15 1 0 0 0 0 + 12 36 1 0 0 0 0 + 12 37 1 0 0 0 0 + 13 17 2 0 0 0 0 + 13 19 1 0 0 0 0 + 14 38 1 0 0 0 0 + 14 39 1 0 0 0 0 + 15 21 2 0 0 0 0 + 15 22 1 0 0 0 0 + 16 18 1 0 0 0 0 + 16 40 1 0 0 0 0 + 17 20 1 0 0 0 0 + 17 23 1 0 0 0 0 + 18 20 2 0 0 0 0 + 18 41 1 0 0 0 0 + 19 24 2 0 0 0 0 + 19 42 1 0 0 0 0 + 20 43 1 0 0 0 0 + 21 25 1 0 0 0 0 + 21 44 1 0 0 0 0 + 22 26 2 0 0 0 0 + 22 45 1 0 0 0 0 + 23 46 1 0 0 0 0 + 24 49 1 0 0 0 0 + 25 27 2 0 0 0 0 + 25 50 1 0 0 0 0 + 26 27 1 0 0 0 0 + 26 51 1 0 0 0 0 + 27 52 1 0 0 0 0 +M END +> +10000045 + +> +1 + +> +528 + +> +5 + +> +1 + +> +9 + +> +AAADceB7MABAAAAAAAAAAAAAAAAAAAAAAAA8YIAAAAAAAACx9AAAHAQQQAAADADBWgQ8gdJIEAKgAjBnRHDCgDAxAiAI2Lg4ZJgIIOLA0ZGEIAhgkADIyAcQgIAOgAAAgAAUAAAAAAEAACgAAAAAAAAAAA== + +> +N-(3-aminopropyl)-N-(3-phenylpropyl)isoquinoline-5-sulfonamide + +> +N-(3-aminopropyl)-N-(3-phenylpropyl)-5-isoquinolinesulfonamide + +> +N-(3-aminopropyl)-N-(3-phenylpropyl)isoquinoline-5-sulfonamide + +> +N-(3-azanylpropyl)-N-(3-phenylpropyl)isoquinoline-5-sulfonamide + +> +N-(3-aminopropyl)-N-(3-phenylpropyl)isoquinoline-5-sulfonamide + +> +InChI=1S/C21H25N3O2S/c22-13-6-16-24(15-5-9-18-7-2-1-3-8-18)27(25,26)21-11-4-10-19-17-23-14-12-20(19)21/h1-4,7-8,10-12,14,17H,5-6,9,13,15-16,22H2 + +> +KDOGJVBOXOUZHY-UHFFFAOYSA-N + +> +3 + +> +383.166748 + +> +C21H25N3O2S + +> +383.5071 + +> +C1=CC=C(C=C1)CCCN(CCCN)S(=O)(=O)C2=CC=CC3=C2C=CN=C3 + +> +C1=CC=C(C=C1)CCCN(CCCN)S(=O)(=O)C2=CC=CC3=C2C=CN=C3 + +> +76.3 + +> +383.166748 + +> +0 + +> +27 + +> +0 + +> +0 + +> +0 + +> +0 + +> +0 + +> +1 + +> +1 + +> +1 +5 +255 + +> +11 13 8 +11 16 8 +13 17 8 +13 19 8 +15 21 8 +15 22 8 +16 18 8 +17 20 8 +17 23 8 +18 20 8 +19 24 8 +21 25 8 +22 26 8 +25 27 8 +26 27 8 +6 23 8 +6 24 8 + +$$$$ diff --git a/INCHI-1-SRC/INCHI_API/demos/python_sample/yapyinchi.py b/INCHI-1-SRC/INCHI_API/demos/python_sample/yapyinchi.py new file mode 100644 index 0000000..472b50b --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/python_sample/yapyinchi.py @@ -0,0 +1,185 @@ +''' +Yet another Python (3) to InChI Software Library interface +''' +import os +import sys +import string +from ctypes import * +# +# +# +# +############################################################################## +# +# +# +############################################################################## +def what_platform(): + ''' Returns 'Windows' or 'Linux' ''' + opsys = sys.platform + if (opsys[:3]=='win'): + return "Windows" + elif (opsys[:5]=='linux'): + return "Linux" + else: + return "Unsupported" +# +# +# +def load_inchi_library(path): + # + platform = what_platform() + print ("Platform =",platform) + libname = "" + if (platform=="Windows"): + libname = path + "libinchi.dll" + elif (platform=="Linux"): + libname = path + "libinchi.so.1" #"/usr/lib/libinchi.so.1" + else: + return None + # + print (libname) + try: + print ("Loading InChI Software library", libname) + libinchi = cdll.LoadLibrary(libname) + print ("..loaded.") + makeinchi = libinchi.MakeINCHIFromMolfileText + freeinchi = libinchi.FreeINCHI + getikey = libinchi.GetINCHIKeyFromINCHI + except: + return None + # + if not makeinchi: + return None + if not freeinchi: + return None + if not getikey: + return None + # + return (makeinchi, freeinchi, getikey) +# +# +# +############################################################################## +# +# +# +class inchi_Output(Structure): + # zero-terminated C-strings allocated by GetINCHI() + # to deallocate all of them call FreeINCHI() (see below) + _fields_ = [("szInChI", POINTER(c_char) ), + ("szAuxInfo", POINTER(c_char) ), + ("szMessage", POINTER(c_char) ), + ("szLog", POINTER(c_char) ) ] +# +# +# +############################################################################## +# +# +# +def calc_inchikey( sinchi, calc_xkey, inchi_api ): + # + sikey = sxtra1 = sxtra2 = "" + makeinchi, freeinchi, getikey = inchi_api + # + c_sinchi = c_char_p(sinchi.encode('utf-8')) + # + # Fixed length buffers + # + szIKey = create_string_buffer(256) + szXtra1 = create_string_buffer(256) + szXtra2 = create_string_buffer(256) + # + ikey_result = getikey(c_sinchi, calc_xkey, calc_xkey, szIKey, szXtra1, szXtra2) + if ikey_result==0 or ikey_result==11: + # 11 is a special case: + # reversal of InChI failed, InChIKey is computed with warning issued + sikey = cast( szIKey, c_char_p).value.decode("utf-8") + if (calc_xkey==1): + sxtra1 = cast( szXtra1, c_char_p).value.decode("utf-8") + sxtra2 = cast( szXtra2, c_char_p).value.decode("utf-8") + # + return ( ikey_result, sikey, sxtra1, sxtra2 ) +############################################################################## +# +# +# +def inchify_molfile( molfile, inchi_options, show_auxinfo, calc_key, calc_xkey, inchi_api ): + # + makeinchi, freeinchi, getikey = inchi_api + sinchi = slog = smessage = saux = sikey = sxtra1 = sxtra2 = "" + inchi_result = ikey_result = ierr = 0 + # + # Prepare inchi_Output + # + # + # Fixed length buffers + # + szIKey = create_string_buffer(256) + szXtra1 = create_string_buffer(256) + szXtra2 = create_string_buffer(256) + # + # Variable lenght buffers (will be reallocated by libinchi) + # + szInChI = create_string_buffer(1) + szAuxInfo = create_string_buffer(1) + szMessage = create_string_buffer(1) + szLog = create_string_buffer(1) + ioutput = inchi_Output( szInChI, szAuxInfo, szMessage, szLog ) + # + #Make InChI + # + c_molfile = c_char_p(molfile.encode('utf-8')) + c_inchi_options = c_char_p(inchi_options.encode('utf-8')) + # + inchi_result = makeinchi( c_molfile, c_inchi_options, byref(ioutput) ) + # + sinchi = cast(ioutput.szInChI, c_char_p).value.decode("utf-8") + slog = cast(ioutput.szLog, c_char_p).value.decode("utf-8").strip() + smessage = cast(ioutput.szMessage, c_char_p).value.decode("utf-8").strip() + saux = cast(ioutput.szAuxInfo, c_char_p).value.decode("utf-8") + # + # + if inchi_result > 1: + ierr = 1 + else: + # no error or just warning ==> may compute InChIKey if requested + if calc_key==1: + ikey_result = getikey(ioutput.szInChI, calc_xkey, calc_xkey, szIKey, szXtra1, szXtra2) + if ikey_result==0 or ikey_result==11: + # 11 is a special case: + # reversal of InChI failed, InChIKey is computed with warning issued + sikey = cast( szIKey, c_char_p).value.decode("utf-8") + if (calc_xkey==1): + sxtra1 = cast( szXtra1, c_char_p).value.decode("utf-8") + sxtra2 = cast( szXtra2, c_char_p).value.decode("utf-8") + # + # + # + # Deallocate memory reallocated by inchi dll + # + # Note the trick with None (Python's NULL) assignnment to pointers + # which have not been actually allocated (related strings are zero-length) + # within C library libinchi. + # We passed Python buffer_strings casted to char *, not real pointers + # so they could not be inited with NULL (as they would be in C)... + # So if say szLog or szMessage were actually not used in libinchi.dll + # and were not allocated, they should be massaged to become NULL at the end, + # in order to avoid unnecessary/illegal freeing upon call of freeinchi(). + # + # -- all above is valid on no error. + # + #print (ioutput.szInChI, ioutput.szAuxInfo, ioutput.szLog, ioutput.szMessage) + #print ( inchi_result, "{" + sinchi +"}, {" + slog +"}, {" + smessage +"}, {" + saux + "}" ) + # + # + # + if len(sinchi)==0: + ioutput.szInChI = None # Python's NULL + if len(slog)==0: + ioutput.szLog = None + if len(smessage)==0: + ioutput.szMessage = None + freeinchi(byref(ioutput)) + return ( inchi_result, ikey_result, sinchi, slog, smessage, saux, sikey, sxtra1, sxtra2, ierr) diff --git a/INCHI-1-SRC/INCHI_API/demos/readme.txt b/INCHI-1-SRC/INCHI_API/demos/readme.txt new file mode 100644 index 0000000..5f2957e --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/readme.txt @@ -0,0 +1,47 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +This directory contains examples of InChI API usage, for C +('inchi_main', 'mol2inchi', 'test_ixa'); see projects +for MS Visual Studio 2008 in 'vc9' and for gcc/Linux +in 'gcc' subdirs) and Python 3 ('python_sample'). + +Note that all the projects in addition to corresponding +demo executable create also a necessary 'libinchi' library +(.dll or .so) in the same upper-level directory 'bin'. diff --git a/INCHI-1-SRC/INCHI_API/demos/test_ixa/gcc/libinchi.map b/INCHI-1-SRC/INCHI_API/demos/test_ixa/gcc/libinchi.map new file mode 100644 index 0000000..323dc85 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/test_ixa/gcc/libinchi.map @@ -0,0 +1,4 @@ +{ +global: CheckINCHI; CheckINCHIKey; FreeINCHI; FreeStdINCHI; FreeStructFromINCHI; FreeStructFromStdINCHI; Free_inchi_Input; Free_std_inchi_Input; FreeStructFromINCHIEx; GetINCHI; GetINCHIKeyFromINCHI; GetINCHIfromINCHI; GetStdINCHI; GetStdINCHIKeyFromStdINCHI; GetStringLength; GetStructFromINCHI; GetStructFromStdINCHI; Get_inchi_Input_FromAuxInfo; Get_std_inchi_Input_FromAuxInfo; GetINCHIEx; GetStructFromINCHIEx; INCHIGEN_Create; INCHIGEN_Destroy; INCHIGEN_DoCanonicalization; INCHIGEN_DoNormalization; INCHIGEN_DoSerialization; INCHIGEN_Reset; INCHIGEN_Setup; STDINCHIGEN_Create; STDINCHIGEN_Destroy; STDINCHIGEN_DoCanonicalization; STDINCHIGEN_DoNormalization; STDINCHIGEN_DoSerialization; STDINCHIGEN_Reset; STDINCHIGEN_Setup; MakeINCHIFromMolfileText; IXA_STATUS_Create; IXA_STATUS_Clear; IXA_STATUS_Destroy; IXA_STATUS_HasError; IXA_STATUS_HasWarning; IXA_STATUS_GetCount; IXA_STATUS_GetSeverity; IXA_STATUS_GetMessage; IXA_MOL_Create; IXA_MOL_Clear; IXA_MOL_Destroy; IXA_MOL_ReadMolfile; IXA_MOL_ReadInChI; IXA_MOL_SetChiral; IXA_MOL_GetChiral; IXA_MOL_CreateAtom; IXA_MOL_SetAtomElement; IXA_MOL_SetAtomAtomicNumber; IXA_MOL_SetAtomMass; IXA_MOL_SetAtomCharge; IXA_MOL_SetAtomRadical; IXA_MOL_SetAtomHydrogens; IXA_MOL_SetAtomX; IXA_MOL_SetAtomY; IXA_MOL_SetAtomZ; IXA_MOL_CreateBond; IXA_MOL_SetBondType; IXA_MOL_SetBondWedge; IXA_MOL_SetDblBondConfig; IXA_MOL_CreateStereoTetrahedron; IXA_MOL_CreateStereoRectangle; IXA_MOL_CreateStereoAntiRectangle; IXA_MOL_SetStereoParity; IXA_MOL_GetNumAtoms; IXA_MOL_GetNumBonds; IXA_MOL_GetAtomId; IXA_MOL_GetBondId; IXA_MOL_GetAtomIndex; IXA_MOL_GetBondIndex; IXA_MOL_GetAtomNumBonds; IXA_MOL_GetAtomBond; IXA_MOL_GetCommonBond; IXA_MOL_GetBondAtom1; IXA_MOL_GetBondAtom2; IXA_MOL_GetAtomElement; IXA_MOL_GetAtomAtomicNumber; IXA_MOL_GetAtomMass; IXA_MOL_GetAtomCharge; IXA_MOL_GetAtomRadical; IXA_MOL_GetAtomHydrogens; IXA_MOL_GetAtomX; IXA_MOL_GetAtomY; IXA_MOL_GetAtomZ; IXA_MOL_GetBondType; IXA_MOL_GetBondWedge; IXA_MOL_GetDblBondConfig; IXA_MOL_GetNumStereos; IXA_MOL_GetStereoId; IXA_MOL_GetStereoIndex; IXA_MOL_GetStereoTopology; IXA_MOL_GetStereoCentralAtom; IXA_MOL_GetStereoCentralBond; IXA_MOL_GetStereoNumVertices; IXA_MOL_GetStereoVertex; IXA_MOL_GetStereoParity; IXA_INCHIBUILDER_Create; IXA_INCHIBUILDER_SetMolecule; IXA_INCHIBUILDER_GetInChI; IXA_INCHIBUILDER_GetInChIEx; IXA_INCHIBUILDER_GetAuxInfo; IXA_INCHIBUILDER_GetLog; IXA_INCHIBUILDER_Destroy; IXA_INCHIBUILDER_SetOption; IXA_INCHIBUILDER_SetOption_Stereo; IXA_INCHIBUILDER_SetOption_Timeout; IXA_INCHIKEYBUILDER_Create; IXA_INCHIKEYBUILDER_SetInChI; IXA_INCHIKEYBUILDER_GetInChIKey; IXA_INCHIKEYBUILDER_Destroy;local: *; +}; + diff --git a/INCHI-1-SRC/INCHI_API/demos/test_ixa/gcc/makefile b/INCHI-1-SRC/INCHI_API/demos/test_ixa/gcc/makefile new file mode 100644 index 0000000..a396f20 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/test_ixa/gcc/makefile @@ -0,0 +1,194 @@ +# Comment out the next line to create so/dll only +CREATE_MAIN = 1 +# Comment out the next line to create test_ixa executable (otherwise, inchi_maiin is created) +CALLER_IS_TEST_IXA = 1 +# or define ISLINUX in command line: make ISLINUX=1 +ISLINUX = 1 +# Linux fpic option: replace -fPIC with -fpic if the latter works +# Comment out "LINUX_Z_RELRO =" if -z relro is not supported +# These options are needed to avoid the following SELinux message: +# "Error: cannot restore segment prot after reloc: Permission denied" +# In addition, inchi.map restricts set of expoorted from .so +# functions to those which belong to InChI API +LINUX_MAP = ,--version-script=libinchi.map +ifdef ISLINUX +LINUX_FPIC = -fPIC +LINUX_Z_RELRO = ,-z,relro +endif +# === version === +MAIN_VERSION = .1 +VERSION = $(MAIN_VERSION).05.00 +# === executable & library directory === +ifndef LIB_DIR + LIB_DIR = ../../../bin/Linux +endif +# === InChI Library name === +ifndef INCHI_LIB_NAME + INCHI_LIB_NAME = libinchi +endif +INCHI_LIB_PATHNAME = $(LIB_DIR)/$(INCHI_LIB_NAME) +# === Main program name ==== +ifndef API_CALLER_NAME + ifndef CALLER_IS_TEST_IXA + API_CALLER_NAME = inchi_main$ + else + API_CALLER_NAME = test_ixa$ + endif +endif +API_CALLER_PATHNAME = $(LIB_DIR)/$(API_CALLER_NAME) +# === Linker to create (Shared) InChI library ==== +ifndef SHARED_LINK + SHARED_LINK = gcc -shared +endif +# === Linker to create Main program ===== +ifndef LINKER + ifdef ISLINUX + LINKER_CWD_PATH = -Wl,-R,"" + endif + LINKER = gcc -s $(LINKER_CWD_PATH) +endif +ifndef P_LIBR + P_LIBR = ../../../libinchi/src/ +endif +ifndef P_LIBR_IXA + P_LIBR_IXA = ../../../libinchi/src/ixa/ +endif +ifndef P_BASE + P_BASE = ../../../../INCHI_BASE/src/ +endif +ifndef P_MAIN + ifndef CALLER_IS_TEST_IXA + P_MAIN = ../src/ + else + P_MAIN = ../src/ + endif +endif +# === C Compiler =============== +ifndef C_COMPILER + C_COMPILER = gcc +endif +# === C Compiler Options ======= +ifndef C_OPTIONS + ifndef CALLER_IS_TEST_IXA + C_OPTIONS = -ansi -O3 -c + else + C_OPTIONS = -O3 -c + endif + ifdef ISLINUX + ifndef C_SO_OPTIONS + C_SO_OPTIONS = $(LINUX_FPIC) -DTARGET_API_LIB -DCOMPILE_ANSI_ONLY + endif + endif + ifndef C_MAIN_OPTIONS + C_MAIN_OPTIONS = -DBUILD_LINK_AS_DLL -DTARGET_EXE_USING_API + endif +endif +ifdef CREATE_MAIN +ifndef CALLER_IS_TEST_IXA +API_CALLER_SRCS = $(P_MAIN)e_0dstereo.c \ +$(P_MAIN)e_ichimain.c \ +$(P_MAIN)e_ichi_io.c \ +$(P_MAIN)e_ichi_parms.c \ +$(P_MAIN)e_inchi_atom.c \ +$(P_MAIN)e_mol2atom.c \ +$(P_MAIN)e_readinch.c \ +$(P_MAIN)e_readmol.c \ +$(P_MAIN)e_readstru.c \ +$(P_MAIN)e_util.c \ +$(P_MAIN)e_ichimain_a.c +API_CALLER_OBJS = e_0dstereo.o \ +e_ichimain.o \ +e_ichi_io.o \ +e_ichi_parms.o \ +e_inchi_atom.o \ +e_mol2atom.o \ +e_readinch.o \ +e_readmol.o \ +e_readstru.o \ +e_util.o \ +e_ichimain_a.o +else +API_CALLER_SRCS = $(P_MAIN)test_ixa.c \ +$(P_MAIN)moreutil.c +API_CALLER_OBJS = test_ixa.o \ +moreutil.o +endif +# === InChI Main Link rule ================ +$(API_CALLER_PATHNAME) : $(API_CALLER_OBJS) $(INCHI_LIB_PATHNAME).so$(VERSION) + $(LINKER) -o $(API_CALLER_PATHNAME) $(API_CALLER_OBJS) \ + $(INCHI_LIB_PATHNAME).so$(VERSION) -lm +# === InChI Main compile rule ============ +%.o: $(P_MAIN)%.c + $(C_COMPILER) $(C_MAIN_OPTIONS) $(C_OPTIONS) $< +endif +# === InChI Library Object files ============ +INCHI_LIB_OBJS = ichican2.o \ +ichicano.o \ +ichi_io.o \ +ichierr.o \ +ichicans.o \ +ichiisot.o \ +ichilnct.o \ +ichimak2.o \ +ichimake.o \ +ichimap1.o \ +ichimap2.o \ +ichimap4.o \ +ichinorm.o \ +ichiparm.o \ +ichiprt1.o \ +ichiprt2.o \ +ichiprt3.o \ +ichiqueu.o \ +ichiring.o \ +ichisort.o \ +ichister.o \ +ichitaut.o \ +ichi_bns.o \ +inchi_dll.o \ +ichiread.o \ +ichirvr1.o \ +ichirvr2.o \ +ichirvr3.o \ +ichirvr4.o \ +ichirvr5.o \ +ichirvr6.o \ +ichirvr7.o \ +ikey_dll.o \ +ikey_base26.o \ +inchi_dll_main.o \ +inchi_dll_a.o \ +inchi_dll_a2.o \ +inchi_dll_b.o \ +ixa_inchikey_builder.o \ +ixa_read_mol.o \ +ixa_status.o \ +ixa_builder.o \ +ixa_mol.o \ +ixa_read_inchi.o \ +mol_fmt1.o \ +mol_fmt2.o \ +mol_fmt3.o \ +mol2atom.o \ +mol_fmt4.o \ +readinch.o \ +runichi.o \ +runichi2.o \ +runichi3.o \ +runichi4.o \ +sha2.o \ +strutil.o \ +util.o +# === InChI Library link rule ========= +$(INCHI_LIB_PATHNAME).so$(VERSION): $(INCHI_LIB_OBJS) + $(SHARED_LINK) $(SHARED_LINK_PARM) -o $(INCHI_LIB_PATHNAME).so$(VERSION) \ +$(INCHI_LIB_OBJS) -Wl$(LINUX_MAP)$(LINUX_Z_RELRO),-soname,$(INCHI_LIB_NAME).so$(MAIN_VERSION) -lm + ln -fs $(INCHI_LIB_NAME).so$(VERSION) \ +$(INCHI_LIB_PATHNAME).so$(MAIN_VERSION) +# === InChI Library compile rule ========= +%.o: $(P_LIBR)%.c + $(C_COMPILER) $(C_SO_OPTIONS) $(C_OPTIONS) $< +%.o: $(P_LIBR_IXA)%.c + $(C_COMPILER) $(C_SO_OPTIONS) $(C_OPTIONS) $< +%.o: $(P_BASE)%.c + $(C_COMPILER) $(C_SO_OPTIONS) $(C_OPTIONS) $< diff --git a/INCHI-1-SRC/INCHI_API/demos/test_ixa/gcc/makefile32 b/INCHI-1-SRC/INCHI_API/demos/test_ixa/gcc/makefile32 new file mode 100644 index 0000000..d1f5fde --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/test_ixa/gcc/makefile32 @@ -0,0 +1,197 @@ +# This will create 32-bit executable under 64-bit Linux +# +# +# Comment out the next line to create so/dll only +CREATE_MAIN = 1 +# Comment out the next line to create test_ixa executable (otherwise, inchi_maiin is created) +CALLER_IS_TEST_IXA = 1 +# or define ISLINUX in command line: make ISLINUX=1 +ISLINUX = 1 +# Linux fpic option: replace -fPIC with -fpic if the latter works +# Comment out "LINUX_Z_RELRO =" if -z relro is not supported +# These options are needed to avoid the following SELinux message: +# "Error: cannot restore segment prot after reloc: Permission denied" +# In addition, inchi.map restricts set of expoorted from .so +# functions to those which belong to InChI API +LINUX_MAP = ,--version-script=libinchi.map +ifdef ISLINUX +LINUX_FPIC = -fPIC +LINUX_Z_RELRO = ,-z,relro +endif +# === version === +MAIN_VERSION = .1 +VERSION = $(MAIN_VERSION).05.00 +# === executable & library directory === +ifndef LIB_DIR + LIB_DIR = ../../../bin/Linux/32bit +endif +# === InChI Library name === +ifndef INCHI_LIB_NAME + INCHI_LIB_NAME = libinchi +endif +INCHI_LIB_PATHNAME = $(LIB_DIR)/$(INCHI_LIB_NAME) +# === Main program name ==== +ifndef API_CALLER_NAME + ifndef CALLER_IS_TEST_IXA + API_CALLER_NAME = inchi_main$ + else + API_CALLER_NAME = test_ixa$ + endif +endif +API_CALLER_PATHNAME = $(LIB_DIR)/$(API_CALLER_NAME) +# === Linker to create (Shared) InChI library ==== +ifndef SHARED_LINK + SHARED_LINK = gcc -shared +endif +# === Linker to create Main program ===== +ifndef LINKER + ifdef ISLINUX + LINKER_CWD_PATH = -Wl,-R,"" + endif + LINKER = gcc -s $(LINKER_CWD_PATH) +endif +ifndef P_LIBR + P_LIBR = ../../../libinchi/src/ +endif +ifndef P_LIBR_IXA + P_LIBR_IXA = ../../../libinchi/src/ixa/ +endif +ifndef P_BASE + P_BASE = ../../../../INCHI_BASE/src/ +endif +ifndef P_MAIN + ifndef CALLER_IS_TEST_IXA + P_MAIN = ../src/ + else + P_MAIN = ../src/ + endif +endif +# === C Compiler =============== +ifndef C_COMPILER + C_COMPILER = gcc +endif +# === C Compiler Options ======= +ifndef C_OPTIONS + ifndef CALLER_IS_TEST_IXA + C_OPTIONS = -m32 -ansi -O3 -c + else + C_OPTIONS = -m32 -O3 -c + endif + ifdef ISLINUX + ifndef C_SO_OPTIONS + C_SO_OPTIONS = $(LINUX_FPIC) -DTARGET_API_LIB -DCOMPILE_ANSI_ONLY + endif + endif + ifndef C_MAIN_OPTIONS + C_MAIN_OPTIONS = -DBUILD_LINK_AS_DLL -DTARGET_EXE_USING_API + endif +endif +ifdef CREATE_MAIN +ifndef CALLER_IS_TEST_IXA +API_CALLER_SRCS = $(P_MAIN)e_0dstereo.c \ +$(P_MAIN)e_ichimain.c \ +$(P_MAIN)e_ichi_io.c \ +$(P_MAIN)e_ichi_parms.c \ +$(P_MAIN)e_inchi_atom.c \ +$(P_MAIN)e_mol2atom.c \ +$(P_MAIN)e_readinch.c \ +$(P_MAIN)e_readmol.c \ +$(P_MAIN)e_readstru.c \ +$(P_MAIN)e_util.c \ +$(P_MAIN)e_ichimain_a.c +API_CALLER_OBJS = e_0dstereo.o \ +e_ichimain.o \ +e_ichi_io.o \ +e_ichi_parms.o \ +e_inchi_atom.o \ +e_mol2atom.o \ +e_readinch.o \ +e_readmol.o \ +e_readstru.o \ +e_util.o \ +e_ichimain_a.o +else +API_CALLER_SRCS = $(P_MAIN)test_ixa.c \ +$(P_MAIN)moreutil.c +API_CALLER_OBJS = test_ixa.o \ +moreutil.o +endif +# === InChI Main Link rule ================ +$(API_CALLER_PATHNAME) : $(API_CALLER_OBJS) $(INCHI_LIB_PATHNAME).so$(VERSION) + $(LINKER) -m32 -o $(API_CALLER_PATHNAME) $(API_CALLER_OBJS) \ + $(INCHI_LIB_PATHNAME).so$(VERSION) -lm +# === InChI Main compile rule ============ +%.o: $(P_MAIN)%.c + $(C_COMPILER) $(C_MAIN_OPTIONS) $(C_OPTIONS) $< +endif +# === InChI Library Object files ============ +INCHI_LIB_OBJS = ichican2.o \ +ichicano.o \ +ichi_io.o \ +ichierr.o \ +ichicans.o \ +ichiisot.o \ +ichilnct.o \ +ichimak2.o \ +ichimake.o \ +ichimap1.o \ +ichimap2.o \ +ichimap4.o \ +ichinorm.o \ +ichiparm.o \ +ichiprt1.o \ +ichiprt2.o \ +ichiprt3.o \ +ichiqueu.o \ +ichiring.o \ +ichisort.o \ +ichister.o \ +ichitaut.o \ +ichi_bns.o \ +inchi_dll.o \ +ichiread.o \ +ichirvr1.o \ +ichirvr2.o \ +ichirvr3.o \ +ichirvr4.o \ +ichirvr5.o \ +ichirvr6.o \ +ichirvr7.o \ +ikey_dll.o \ +ikey_base26.o \ +inchi_dll_main.o \ +inchi_dll_a.o \ +inchi_dll_a2.o \ +inchi_dll_b.o \ +ixa_inchikey_builder.o \ +ixa_read_mol.o \ +ixa_status.o \ +ixa_builder.o \ +ixa_mol.o \ +ixa_read_inchi.o \ +mol_fmt1.o \ +mol_fmt2.o \ +mol_fmt3.o \ +mol2atom.o \ +mol_fmt4.o \ +readinch.o \ +runichi.o \ +runichi2.o \ +runichi3.o \ +runichi4.o \ +sha2.o \ +strutil.o \ +util.o +# === InChI Library link rule ========= +$(INCHI_LIB_PATHNAME).so$(VERSION): $(INCHI_LIB_OBJS) + $(SHARED_LINK) $(SHARED_LINK_PARM) -m32 -o $(INCHI_LIB_PATHNAME).so$(VERSION) \ +$(INCHI_LIB_OBJS) -Wl$(LINUX_MAP)$(LINUX_Z_RELRO),-soname,$(INCHI_LIB_NAME).so$(MAIN_VERSION) -lm + ln -fs $(INCHI_LIB_NAME).so$(VERSION) \ +$(INCHI_LIB_PATHNAME).so$(MAIN_VERSION) +# === InChI Library compile rule ========= +%.o: $(P_LIBR)%.c + $(C_COMPILER) $(C_SO_OPTIONS) $(C_OPTIONS) $< +%.o: $(P_LIBR_IXA)%.c + $(C_COMPILER) $(C_SO_OPTIONS) $(C_OPTIONS) $< +%.o: $(P_BASE)%.c + $(C_COMPILER) $(C_SO_OPTIONS) $(C_OPTIONS) $< diff --git a/INCHI-1-SRC/INCHI_API/demos/test_ixa/gcc/run_make_on_linux.sh b/INCHI-1-SRC/INCHI_API/demos/test_ixa/gcc/run_make_on_linux.sh new file mode 100644 index 0000000..a6d09d8 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/test_ixa/gcc/run_make_on_linux.sh @@ -0,0 +1,2 @@ +#!/bin/sh +make ISLINUX=1 diff --git a/INCHI-1-SRC/INCHI_API/demos/test_ixa/readme.txt b/INCHI-1-SRC/INCHI_API/demos/test_ixa/readme.txt new file mode 100644 index 0000000..aee7518 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/test_ixa/readme.txt @@ -0,0 +1,52 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +This directory contains example of using newly added in v. 1.05 +Extensible InChI API (IXA). + +The demo program test_ixa reads input Molfile/SDFile and +generates InChI; it also demonstrates an organization of +verification round-trip "Structure->InChI->Structure->InChI" +via InChI API calls. + +The source codes are placed in sub-directory 'src'; +gcc/Linux makefiles in sub-directory 'gcc'; +MS VS 2008 project is placed in sub-directory 'vc9'. + +The created binaries are saved in upper-level directory 'bin'. diff --git a/INCHI-1-SRC/INCHI_API/demos/test_ixa/src/moreitil.h b/INCHI-1-SRC/INCHI_API/demos/test_ixa/src/moreitil.h new file mode 100644 index 0000000..bfcbfec --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/test_ixa/src/moreitil.h @@ -0,0 +1,71 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _MOREUTIL_H_ +#define _MOREUTIL_H_ + + +#define MOLMAXLINELEN 1024 + + + + +/* Just print date & time */ +void print_time(void); +/* For portability - use own version of stricmp and memicmp */ +int own_stricmp( const char *s1, const char *s2 ); +int own_memicmp( const void*, const void*, size_t ); +/* Yet anothe helper */ +char* get_substr_in_between(char *s, + char *pat1, char *pat2, + char *buf, + size_t max_symbols, size_t *copied); + + +/* (SD)File i/o related */ + +/* Read Molfile (SDFile segment) to text buffer */ +/* Return 1 if got something otherwise 0 */ +int get_next_molfile_as_text( FILE *f, char *buf , size_t buflen ); +/* Fgets which ensures single linefeed at the end */ +char* fgets_lf( char* line, int line_len, FILE *f ); + +int is_empty_text(char *buf); + +#endif /* _MOREUTIL_H_ */ diff --git a/INCHI-1-SRC/INCHI_API/demos/test_ixa/src/moreutil.c b/INCHI-1-SRC/INCHI_API/demos/test_ixa/src/moreutil.c new file mode 100644 index 0000000..2334ad4 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/test_ixa/src/moreutil.c @@ -0,0 +1,251 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#pragma warning( disable : 4996 ) + +#include +#include +#include +#include +#include + + +#include "moreitil.h" + +typedef unsigned char U_CHAR; + +int replace_num_in_at_line( char *str, char *line, long num ); +int replace_nums_in_bo_line( char *str, char *line, int shuffled_numbers[] ); + + +/**************************/ +/* Just print date & time */ +void print_time(void) +{ +time_t tt; +char st[36]=""; + tt = time(&tt); + strncat(st,(char *) ctime(&tt), 32); + fprintf( stderr, "%-s",st); +} + + +/********************************************/ +/* For portability - own version of stricmp */ +#define __MYTOLOWER(c) ( ((c) >= 'A') && ((c) <= 'Z') ? ((c) - 'A' + 'a') : (c) ) +int own_stricmp( const char *s1, const char *s2 ) +{ + while ( *s1 ) + { + if ( *s1 == *s2 || + __MYTOLOWER( (int)*s1 ) == __MYTOLOWER( (int)*s2 )) + { + s1 ++; + s2 ++; + } + else + { + return + __MYTOLOWER( (int)*s1 ) - __MYTOLOWER( (int)*s2 ); + } + } + + if ( *s2 ) + return -1; + + return 0; +} + + +/*****************************************************************/ +int own_memicmp ( const void * p1, const void * p2, size_t length ) +{ + const U_CHAR *s1 = (const U_CHAR*)p1; + const U_CHAR *s2 = (const U_CHAR*)p2; + while ( length-- ) + { + if ( *s1 == *s2 || + __MYTOLOWER( (int)*s1 ) == __MYTOLOWER( (int)*s2 )) + { + s1 ++; + s2 ++; + } + else + { + return + __MYTOLOWER( (int)*s1 ) - __MYTOLOWER( (int)*s2 ); + } + } + + return 0; +} + + +/*********************************************/ +/* Extracts a substring sitting in 's' in + between patterns 'pat1' and 'pat2'. + Returns pointer to the end of pat2 plus 1. + Fills in copied, the actual n bytes found. + Null-terminated 'buf' contains not more + than 'max_symbols' leading characters + from found substring. +*/ +char* get_substr_in_between(char *s, + char *pat1, char *pat2, + char *buf, + size_t max_symbols, + size_t *copied) +{ +size_t n=0; +char *p1=NULL, *p1a=NULL, *p2 = NULL; + + *copied = 0; + + p1 = strstr(s, pat1); + if ( !p1) + return NULL; + + p1a = p1 + strlen(pat1); + + if ( !strcmp( pat2, "") ) + { + p2 = s + strlen(s) - 1; + n = 1; + } + else + { + p2 = strstr(p1a, pat2); + } + if ( !p2) + return NULL; + + n+= (int) (p2 - p1a); + + if ( n ) + { + *copied = n; + if ( *copied > max_symbols ) + *copied = max_symbols; + memcpy( buf, p1a, *copied ); + buf[(*copied)++] = '\0'; + } + + return p2+1; +} + + +/**************************************************/ +/* Fgets which ensures single linefeed at the end */ +char* fgets_lf( char* line, int line_len, FILE *f ) +{ +char *p=NULL, *q; + + /* Read from file */ + memset( line, 0, line_len ); + if ( NULL!=( p =fgets(line, line_len, f) ) && + NULL==strchr(p, '\n' ) ) + { + char temp[64]; + /* bypass up to '\n' or up to end of file whichever comes first*/ + while ( NULL!=fgets(temp, sizeof(temp), f) && + NULL==strchr(temp, '\n') ) + ; + } + + /* fix CR CR LF line terminator. */ + if ( p ) + { + if (q = strchr(line, '\r') ) + { + q[0] = '\n'; + q[1] = '\0'; + } + } + + return p; +} + + +/***************************************************************/ +/* Read Molfile (SDFile segment) to text buffer */ +/* Return 1 if got something, -1 if no room, otherwise 0 */ +int get_next_molfile_as_text( FILE *f, char *buf, size_t buflen ) +{ +char *p=NULL, line[MOLMAXLINELEN]; +size_t pos=0l, bufsize=0; +int empty = 1; + + while ( p=fgets_lf(line, MOLMAXLINELEN, f) ) + { + size_t n = strlen(line); + if ( n ) + { + if ( empty ) + empty = 0; /* Got something non-empty, do not mind the content */ + + if ( buflen < pos + n -1 ) + return -1; /* buffer buf is too small */ + memcpy( buf + pos, line , n ); + pos+= n; + /* Check if we at the end of Molfile segment */ + if ( line[0]=='$' && n>3 && + line[1]=='$' && line[2]=='$' && line[3]=='$' ) + break; + } + } + + + if ( empty ) + return 0; + else + { + buf[pos] = '\0'; + return 1; + } +} + +int is_empty_text(char *buf) +{ + while ( *buf ) + { + if ( !isspace(*buf++) ) + return 0; + } + return 1; +} diff --git a/INCHI-1-SRC/INCHI_API/demos/test_ixa/src/test_ixa.c b/INCHI-1-SRC/INCHI_API/demos/test_ixa/src/test_ixa.c new file mode 100644 index 0000000..48fad92 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/test_ixa/src/test_ixa.c @@ -0,0 +1,496 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 +* January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include +#include +#include + +#include "../../../../INCHI_BASE/src/inchi_api.h" +#include "test_ixa.h" +#include "moreitil.h" + + +/* +Check for errors and warnings in the status object and output them, then clear +the status object. The errors and warnings have been dealt with and are no longer +needed. +*/ + +static int CheckStatus(IXA_STATUS_HANDLE hStatus, long nrecord) +{ + int result = 1; + + int count = IXA_STATUS_GetCount(hStatus); + int index; + for (index = 0; index < count; index++) + { + switch (IXA_STATUS_GetSeverity(hStatus, index)) + { + case IXA_STATUS_ERROR: + result = 0; + fprintf(stderr, "\nERROR: %s : structure %-ld", IXA_STATUS_GetMessage(hStatus, index), nrecord); + break; + case IXA_STATUS_WARNING: + fprintf(stderr, "\nWARNING: %s : structure %-ld", IXA_STATUS_GetMessage(hStatus, index), nrecord); + break; + } + } + + IXA_STATUS_Clear(hStatus); + return result; +} + +/* +Test whether the command line argument matches the given option name. This is little +more than a case insensitive compare. +*/ +static int OptionCompare(const char* pArg, + const char* pOption) +{ + if ((*pArg != '-') && (*pArg != '/')) + { + return 0; + } + + pArg++; + while (*pArg) + { + if ((*pArg != *pOption) && (tolower((int)*pArg) != tolower((int)*pOption))) + { + return 0; + } + + pArg++; + pOption++; + } + + if (*pOption) + { + return 0; + } + return 1; +} + +/* +Read the command line arguments and set options on the InChI builder accordingly. +*/ +static int ReadOptions(int argc, + const char* argv[], + IXA_BOOL* pKey, + IXA_BOOL* pRoundTrip, + IXA_BOOL* pGenerateAuxinfo, + IXA_BOOL* pVerbose, + char* pOptions, + IXA_STATUS_HANDLE hStatus, + IXA_INCHIBUILDER_HANDLE hBuilder) +{ + int index; + + *pKey = IXA_FALSE; + *pRoundTrip = IXA_FALSE; + *pGenerateAuxinfo = IXA_TRUE; + pOptions[0] = '\0'; + for (index = 3; index < argc; index++) + { + strcat(pOptions, " "); + strcat(pOptions, argv[index]); + + if (OptionCompare(argv[index], "RTrip")) + { + *pRoundTrip = IXA_TRUE; + } + else if (OptionCompare(argv[index], "Key")) + { + *pKey = IXA_TRUE; + } + else if (OptionCompare(argv[index], "Verbose")) + { + *pVerbose = IXA_TRUE; + } + else if (OptionCompare(argv[index], "SNon")) + { + IXA_INCHIBUILDER_SetOption_Stereo(hStatus, hBuilder, IXA_INCHIBUILDER_STEREOOPTION_SNon); + } + else if (OptionCompare(argv[index], "SRel")) + { + IXA_INCHIBUILDER_SetOption_Stereo(hStatus, hBuilder, IXA_INCHIBUILDER_STEREOOPTION_SRel); + } + else if (OptionCompare(argv[index], "SRac")) + { + IXA_INCHIBUILDER_SetOption_Stereo(hStatus, hBuilder, IXA_INCHIBUILDER_STEREOOPTION_SRac); + } + else if (OptionCompare(argv[index], "SUCF")) + { + IXA_INCHIBUILDER_SetOption_Stereo(hStatus, hBuilder, IXA_INCHIBUILDER_STEREOOPTION_SUCF); + } + else if (OptionCompare(argv[index], "NoLabels")) + { + /* Do nothing. This option is accepted but has no effect on behaviour. */ + } + else if (OptionCompare(argv[index], "NEWPSOFF")) + { + IXA_INCHIBUILDER_SetOption(hStatus, hBuilder, IXA_INCHIBUILDER_OPTION_NewPsOff, IXA_TRUE); + } + else if (OptionCompare(argv[index], "DoNotAddH")) + { + IXA_INCHIBUILDER_SetOption(hStatus, hBuilder, IXA_INCHIBUILDER_OPTION_DoNotAddH, IXA_TRUE); + } + else if (OptionCompare(argv[index], "SUU")) + { + IXA_INCHIBUILDER_SetOption(hStatus, hBuilder, IXA_INCHIBUILDER_OPTION_SUU, IXA_TRUE); + } + else if (OptionCompare(argv[index], "SLUUD")) + { + IXA_INCHIBUILDER_SetOption(hStatus, hBuilder, IXA_INCHIBUILDER_OPTION_SLUUD, IXA_TRUE); + } + else if (OptionCompare(argv[index], "FixedH")) + { + IXA_INCHIBUILDER_SetOption(hStatus, hBuilder, IXA_INCHIBUILDER_OPTION_FixedH, IXA_TRUE); + } + else if (OptionCompare(argv[index], "RecMet")) + { + IXA_INCHIBUILDER_SetOption(hStatus, hBuilder, IXA_INCHIBUILDER_OPTION_RecMet, IXA_TRUE); + } + else if (OptionCompare(argv[index], "KET")) + { + IXA_INCHIBUILDER_SetOption(hStatus, hBuilder, IXA_INCHIBUILDER_OPTION_KET, IXA_TRUE); + } + else if (OptionCompare(argv[index], "15T")) + { + IXA_INCHIBUILDER_SetOption(hStatus, hBuilder, IXA_INCHIBUILDER_OPTION_15T, IXA_TRUE); + } + else if (OptionCompare(argv[index], "AuxNone")) + { + IXA_INCHIBUILDER_SetOption(hStatus, hBuilder, IXA_INCHIBUILDER_OPTION_AuxNone, IXA_TRUE); + *pGenerateAuxinfo = IXA_FALSE; + } + else if (OptionCompare(argv[index], "WarnOnEmptyStructure")) + { + IXA_INCHIBUILDER_SetOption(hStatus, hBuilder, IXA_INCHIBUILDER_OPTION_WarnOnEmptyStructure, IXA_TRUE); + } + else if (OptionCompare(argv[index], "LargeMolecules")) + { + IXA_INCHIBUILDER_SetOption(hStatus, hBuilder, IXA_INCHIBUILDER_OPTION_LargeMolecules, IXA_TRUE); + } + else if (OptionCompare(argv[index], "SaveOpt")) + { + IXA_INCHIBUILDER_SetOption(hStatus, hBuilder, IXA_INCHIBUILDER_OPTION_SaveOpt, IXA_TRUE); + } + else + { + int timeout; + int is_timeout = 0; + if (((argv[index][0] == '-') || (argv[index][0] == '/')) && + ((argv[index][1] == 'W') || (argv[index][1] == 'w'))) + { + char* p_extra; + timeout = strtol(argv[index] + 2, &p_extra, 10); + if (*p_extra == '\0') + { + is_timeout = 1; + } + } + + if (is_timeout) + { + IXA_INCHIBUILDER_SetOption_Timeout(hStatus, hBuilder, timeout); + } + else + { + fprintf(stderr, "Option \"%s\" is not recognised.\n", argv[index]); + return 0; + } + } + } + return 1; +} + +/* +Main program. +*/ +void main(int argc, const char* argv[]) +{ + FILE* sdfile = NULL; + FILE* outfile = NULL; + char* buffer = NULL; + long index=0, nerr=0, nboom_rtrip=0, nmismatch_rtrip=0; + IXA_BOOL key; + IXA_BOOL generate_auxinfo = IXA_TRUE; + IXA_BOOL verbose = IXA_FALSE; + const char* inchi; + const char* auxinfo; + const char* inchikey; + IXA_BOOL round_trip; + char options[256]; + char* saved_inchi = NULL; + + IXA_STATUS_HANDLE status = NULL; + IXA_MOL_HANDLE molecule = NULL; + IXA_INCHIBUILDER_HANDLE inchi_builder = NULL; + IXA_INCHIKEYBUILDER_HANDLE key_builder = NULL; + +#if defined(_WIN32) +const char *platform="Windows"; +#else +const char *platform="Linux"; +#endif + char banner[255]; + sprintf( banner, "%s\n%-s Build of %-s %-s%s", + APP_DESCRIPTION, + platform, __DATE__, __TIME__, + RELEASE_IS_FINAL?"":" *** pre-release, for evaluation only ***"); + fprintf( stderr, "%-s\n", banner); + +#ifdef IXA_USES_NON_EX_CORE_API + fprintf( stderr, "Note: IXA API in current build uses non-extended core API calls\n"); +#else + fprintf( stderr, "Note: IXA API in current build uses extended core API calls\n"); +#endif + + if (argc < 3) + { + /* Not enough command line arguments have been provided. Output some help + information, then exit. */ + print_help(); + return; + } + + /* Open input and output files. */ + sdfile = fopen(argv[1],"rb"); + /* fopen_s(&sdfile, argv[1], "r"); */ + if (!sdfile) + { + fprintf(stderr, "Failed to open \"%s\" for reading\n", argv[1]); + goto cleanup; + } + outfile = fopen( argv[2], "w"); + /* fopen_s(&outfile, argv[2], "w"); */ + if (!outfile) + { + fprintf(stderr, "Failed to open \"%s\" for writing\n", argv[2]); + goto cleanup; + } + + /* Create status, molecule and InChI builder objects. */ + status = IXA_STATUS_Create(); + molecule = IXA_MOL_Create(status); + if (!CheckStatus(status, index)) goto cleanup; + inchi_builder = IXA_INCHIBUILDER_Create(status); + if (!CheckStatus(status, index)) goto cleanup; + + /* Read any command line options that may have been given and set up the InChI + builder accordingly. */ + if (!ReadOptions(argc, argv, &key, &round_trip, &generate_auxinfo, &verbose, options, status, inchi_builder)) goto cleanup; + + /* If InChI keys have been requested, create an InChI key builder object. */ + if (key) + { + key_builder = IXA_INCHIKEYBUILDER_Create(status); + if (!CheckStatus(status, index)) goto cleanup; + } + + /* Warning: in this demo, we just assume that molfile's size does not */ + /* exceed some (large) MOLBUFSIZE; no further checks are performed. */ + buffer = (char *) calloc( MOLBUFSIZE, sizeof(char) ); + if ( !buffer ) + { + fprintf(stderr, "Out of memory\n"); + goto cleanup; + } + + + /* Iterate the molecules in the input SD file and create an InChI for each one. */ + index = 0; + while ( get_next_molfile_as_text( sdfile, buffer, MOLBUFSIZE ) > 0 && + !is_empty_text( buffer ) ) + { + + /* Set up the molecule object with atoms and bonds to represent the data that + has just been read from the input file. */ + IXA_MOL_ReadMolfile(status, molecule, buffer); + + if (!CheckStatus(status,index+1)) { nerr++; goto endloop; } + + fprintf(outfile, "%-ld\t", index+1); + + /* Bind the molecule object to the InChI builder object. */ + IXA_INCHIBUILDER_SetMolecule(status, inchi_builder, molecule); + if (!CheckStatus(status,index+1)) { nerr++; goto endloop; } + + /* Retrieve the molecule's InChI and write it to the output file. */ + inchi = IXA_INCHIBUILDER_GetInChI(status, inchi_builder); + if (!CheckStatus(status,index+1)) { nerr++; goto endloop; } + + fprintf(outfile, "%s\t", inchi); + + if (generate_auxinfo) + { + /* AuxInfo has been requested. Retrieve the molecule's AuxInfo and write + it to the output file. */ + auxinfo = IXA_INCHIBUILDER_GetAuxInfo(status, inchi_builder); + if (!CheckStatus(status,index+1)) { nerr++; goto endloop; } + + fprintf(outfile, "%s\t", auxinfo); + } + + if (key_builder) + { + /* InChI keys have been requested. Retrieve the molecule's InChI key and + write it to the output file. */ + IXA_INCHIKEYBUILDER_SetInChI(status, key_builder, inchi); + if (!CheckStatus(status,index+1)) { nerr++; goto endloop; } + + inchikey = IXA_INCHIKEYBUILDER_GetInChIKey(status, key_builder); + if (!CheckStatus(status,index+1)) { nerr++; goto endloop; } + + fprintf(outfile, "InChIKey=%s\t", inchikey); + } + + if (round_trip) + { + /* Round trip tests have been requested. Turn the InChI that has just been + generated into a molecule and use that molecule as the basis for a new + InChI. The two InChIs should be identical. */ + saved_inchi = (char *) realloc(saved_inchi, strlen(inchi) + 1); + strcpy(saved_inchi, inchi); + IXA_MOL_ReadInChI(status, molecule, saved_inchi); + if (!CheckStatus(status,index+1)) + { + fprintf(stderr, "\n!!! Round trip failed: could not read InChI : structure %-ld\n", index+1); + { nboom_rtrip++; goto endloop; } + } + IXA_INCHIBUILDER_SetMolecule(status, inchi_builder, molecule); + if (!CheckStatus(status,index+1)) + { + fprintf(stderr, "\n!!! Round trip failed: could not set IXA mol from read InChI string : structure %-ld\n", index+1); + { nboom_rtrip++; goto endloop; } + } + /* strcpy(inchi,"NOTHING"); */ + inchi = IXA_INCHIBUILDER_GetInChI(status, inchi_builder); + if (!CheckStatus(status,index+1)) + { + fprintf(stderr, "\n!!! Round trip failed: could not generate InChI from IXA mol obtained from read InChI : structure %-ld\n", index+1); + { nboom_rtrip++; goto endloop; } + } + if (strcmp(inchi, saved_inchi) != 0) + { + fprintf(stderr, "\n!!! Round trip failed : structure %-ld\n", index+1); + fprintf(stderr, " OLD: %s\n", saved_inchi); + fprintf(stderr, " NEW: %s", inchi); + nmismatch_rtrip++; + } + else if ( verbose ) + { + fprintf(stderr, "\nRound trip OK : structure %-ld\n", index+1); + fprintf(stderr, " OLD: %s\n", saved_inchi); + fprintf(stderr, " NEW: %s", inchi); + } + } + +endloop: + /* Every so often write a dot to stdout so the user knows that this program has + not frozen. */ + fprintf( outfile, "\n" ); + if (index % 100 == 0) + { + fprintf(stderr, "."); + } + index++; + fflush(NULL); + } /* Main loop */ + + fprintf(stderr, "\n"); + + +cleanup: + + fprintf(stderr, "\nFinished processing %-ld molecules.\nGeneration (struct->InChI) errors: %-ld", + index, nerr); + if (round_trip) + { + fprintf( stderr, + "\nRound trip (struct->InChI->struct->InChI) problems: %-ld (%-ld failures, %-ld mismatches)", + nboom_rtrip + nmismatch_rtrip, nboom_rtrip, nmismatch_rtrip); + } + fprintf(stderr, ".\n"); + + /* Release resources prior to program exit. */ + if ( saved_inchi ) + free(saved_inchi); + if ( buffer ) + free(buffer); + if (sdfile) + fclose(sdfile); + if (outfile) + fclose(outfile); + IXA_INCHIKEYBUILDER_Destroy(NULL, key_builder); + IXA_INCHIBUILDER_Destroy(NULL, inchi_builder); + IXA_MOL_Destroy(NULL, molecule); + IXA_STATUS_Destroy(status); +} + + + +/************************************/ +/* Print program usage instructions */ +void print_help(void) +{ + fprintf(stderr, "\nUsage: test_ixa [/option[ /option...]]\n\n\n"); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " RTrip Do a round trip test for each InChI generated\n"); + fprintf(stderr, " Key Generate InChIKey\n"); + fprintf(stderr, " SNon Exclude stereo (default: include absolute stereo)\n"); + fprintf(stderr, " SRel Relative stereo\n"); + fprintf(stderr, " SRac Racemic stereo\n"); + fprintf(stderr, " SUCF Use Chiral Flag: On means Absolute stereo, Off - Relative\n"); + fprintf(stderr, " NEWPSOFF Both ends of wedge point to stereocenters (default: a narrow end)\n"); + fprintf(stderr, " DoNotAddH All H are explicit (default: add H according to usual valences)\n"); + fprintf(stderr, " SUU Always include omitted unknown/undefined stereo\n"); + fprintf(stderr, " SLUUD Make labels for unknown and undefined stereo different\n"); + fprintf(stderr, " FixedH Include Fixed H layer\n"); + fprintf(stderr, " RecMet Include reconnected metals results\n"); + fprintf(stderr, " KET Account for keto-enol tautomerism (experimental)\n"); + fprintf(stderr, " 15T Account for 1,5-tautomerism (experimental)\n"); + fprintf(stderr, " AuxNone Omit auxiliary information (default: Include)\n"); + fprintf(stderr, " WarnOnEmptyStructure Warn and produce empty InChI for empty structure\n"); + fprintf(stderr, " SaveOpt Save custom InChI creation options (non-standard InChI)\n"); + fprintf(stderr, " Wnumber Set time-out per structure in seconds; W0 means unlimited\n"); + fprintf(stderr, " LargeMolecules Treat molecules up to 32766 atoms (experimental)\n"); +} diff --git a/INCHI-1-SRC/INCHI_API/demos/test_ixa/src/test_ixa.h b/INCHI-1-SRC/INCHI_API/demos/test_ixa/src/test_ixa.h new file mode 100644 index 0000000..c5ad26e --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/test_ixa/src/test_ixa.h @@ -0,0 +1,74 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _TEST_IXA_H_ +#define _TEST_IXA_H_ + +#ifdef _MSC_VER +/* +========== disable MS VC++ 6.0 Level 4 compiler warnings: ============== + C4706: assignment within conditional expression + C4127: conditional expression is constant + C4244: '=' : conversion from 'int ' to '???', possible loss of data + C4267: '=' : conversion from 'size_t' to 'int', possible loss of data + C4701: local variable '???' may be used without having been initialized (removed) + C4514: unreferenced inline/local function has been removed (C++) + C4100: 'identifier' : unreferenced formal parameter + C4786: 'identifier' : identifier was truncated to 'number' characters in the debug information + C4090: 'function' : different 'const' qualifiers + C4996: 'identifier' was declared deprecated +======================================================================== +*/ +#pragma warning( disable : 4706 4127 4514 4100 4786 4090 4996 4244 4267 ) +#endif + +/* supposed maxsize of Molfile (within an SD File) */ +#define MOLBUFSIZE 16777216 /* 16 MB */ + +#define APP_DESCRIPTION "InChI version 1, Software v. 1.05 (test_ixa - Library call example, IXA API v. 1.05)" + +/*#define RELEASE_IS_FINAL 0 *//* 1=> pre-release version; comment out to disable */ +#ifndef RELEASE_IS_FINAL +#define RELEASE_IS_FINAL 1 /* final release */ +#endif + +/* Print program usage instructions */ +void print_help(void); + +#endif /* _TEST_IXA_H_ */ diff --git a/INCHI-1-SRC/INCHI_API/demos/test_ixa/vc9/resource.h b/INCHI-1-SRC/INCHI_API/demos/test_ixa/vc9/resource.h new file mode 100644 index 0000000..08c3dac --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/test_ixa/vc9/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by test_ixa.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/INCHI-1-SRC/INCHI_API/demos/test_ixa/vc9/test_ixa.aps b/INCHI-1-SRC/INCHI_API/demos/test_ixa/vc9/test_ixa.aps new file mode 100644 index 0000000..6b2390b Binary files /dev/null and b/INCHI-1-SRC/INCHI_API/demos/test_ixa/vc9/test_ixa.aps differ diff --git a/INCHI-1-SRC/INCHI_API/demos/test_ixa/vc9/test_ixa.rc b/INCHI-1-SRC/INCHI_API/demos/test_ixa/vc9/test_ixa.rc new file mode 100644 index 0000000..d3c21e0 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/test_ixa/vc9/test_ixa.rc @@ -0,0 +1,114 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Russian resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS) +#ifdef _WIN32 +LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT +#pragma code_page(1251) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // Russian resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,5,0 + PRODUCTVERSION 1,0,5,0 + FILEFLAGSMASK 0x37L +#ifdef _DEBUG + FILEFLAGS 0x21L +#else + FILEFLAGS 0x20L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "IUPAC International Chemical Identifier (InChI) version 1, Software version 1.05, Winter 2017. Demo program exemplifying use of InChI IXA API." + VALUE "FileDescription", "Demo program test_ixa." + VALUE "FileVersion", "1, 0, 5, 0" + VALUE "InternalName", "test_ixa" + VALUE "OriginalFilename", "test_ixa.exe" + VALUE "ProductName", "IUPAC International Chemical Identifier (InChI) version 1, Software version 1.05, Winter 2017. Demo program exemplifying use of InChI IXA API." + VALUE "ProductVersion", "1, 0, 5, 0" + VALUE "SpecialBuild", "RELEASE" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/INCHI-1-SRC/INCHI_API/demos/test_ixa/vc9/test_ixa.sln b/INCHI-1-SRC/INCHI_API/demos/test_ixa/vc9/test_ixa.sln new file mode 100644 index 0000000..b82cc69 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/test_ixa/vc9/test_ixa.sln @@ -0,0 +1,39 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_ixa", "test_ixa.vcproj", "{8CB46818-493F-4256-B379-B01FD2C89993}" + ProjectSection(ProjectDependencies) = postProject + {5DAB1696-1795-49AA-BDE6-1771FBE25445} = {5DAB1696-1795-49AA-BDE6-1771FBE25445} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libinchi", "..\..\..\libinchi\vc9\libinchi.vcproj", "{5DAB1696-1795-49AA-BDE6-1771FBE25445}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8CB46818-493F-4256-B379-B01FD2C89993}.Debug|Win32.ActiveCfg = Debug|Win32 + {8CB46818-493F-4256-B379-B01FD2C89993}.Debug|Win32.Build.0 = Debug|Win32 + {8CB46818-493F-4256-B379-B01FD2C89993}.Debug|x64.ActiveCfg = Debug|x64 + {8CB46818-493F-4256-B379-B01FD2C89993}.Debug|x64.Build.0 = Debug|x64 + {8CB46818-493F-4256-B379-B01FD2C89993}.Release|Win32.ActiveCfg = Release|Win32 + {8CB46818-493F-4256-B379-B01FD2C89993}.Release|Win32.Build.0 = Release|Win32 + {8CB46818-493F-4256-B379-B01FD2C89993}.Release|x64.ActiveCfg = Release|x64 + {8CB46818-493F-4256-B379-B01FD2C89993}.Release|x64.Build.0 = Release|x64 + {5DAB1696-1795-49AA-BDE6-1771FBE25445}.Debug|Win32.ActiveCfg = Debug|Win32 + {5DAB1696-1795-49AA-BDE6-1771FBE25445}.Debug|Win32.Build.0 = Debug|Win32 + {5DAB1696-1795-49AA-BDE6-1771FBE25445}.Debug|x64.ActiveCfg = Debug|x64 + {5DAB1696-1795-49AA-BDE6-1771FBE25445}.Debug|x64.Build.0 = Debug|x64 + {5DAB1696-1795-49AA-BDE6-1771FBE25445}.Release|Win32.ActiveCfg = Release|Win32 + {5DAB1696-1795-49AA-BDE6-1771FBE25445}.Release|Win32.Build.0 = Release|Win32 + {5DAB1696-1795-49AA-BDE6-1771FBE25445}.Release|x64.ActiveCfg = Release|x64 + {5DAB1696-1795-49AA-BDE6-1771FBE25445}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/INCHI-1-SRC/INCHI_API/demos/test_ixa/vc9/test_ixa.vcproj b/INCHI-1-SRC/INCHI_API/demos/test_ixa/vc9/test_ixa.vcproj new file mode 100644 index 0000000..70eb0ae --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/demos/test_ixa/vc9/test_ixa.vcproj @@ -0,0 +1,372 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/INCHI-1-SRC/INCHI_API/gcc_so_makefile/libinchi-pas.def b/INCHI-1-SRC/INCHI_API/gcc_so_makefile/libinchi-pas.def deleted file mode 100644 index cae9595..0000000 --- a/INCHI-1-SRC/INCHI_API/gcc_so_makefile/libinchi-pas.def +++ /dev/null @@ -1,72 +0,0 @@ -; With gcc under Win32 this produces same exported names, ordinal numbers, and -; calling conventions as libinchi.dll produced by the MS VC++ project -LIBRARY - libinchi.dll -EXPORTS -; -- Exported name ------------------- Internal name ---------------- ordinal - CheckINCHI = CheckINCHI @1 - CheckINCHIKey = CheckINCHIKey @2 - FreeINCHI = FreeINCHI @3 - FreeStdINCHI = FreeStdINCHI @4 - FreeStructFromINCHI = FreeStructFromINCHI @5 - FreeStructFromStdINCHI = FreeStructFromStdINCHI @6 - Free_inchi_Input = Free_inchi_Input @7 - Free_std_inchi_Input = Free_std_inchi_Input @8 - GetINCHI = GetINCHI @9 - GetINCHIKeyFromINCHI = GetINCHIKeyFromINCHI @10 - GetINCHIfromINCHI = GetINCHIfromINCHI @11 - GetStdINCHI = GetStdINCHI @12 - GetStdINCHIKeyFromStdINCHI = GetStdINCHIKeyFromStdINCHI @13 - GetStringLength = GetStringLength @14 - GetStructFromINCHI = GetStructFromINCHI @15 - GetStructFromStdINCHI = GetStructFromStdINCHI @16 - Get_inchi_Input_FromAuxInfo = Get_inchi_Input_FromAuxInfo @17 - Get_std_inchi_Input_FromAuxInfo = Get_std_inchi_Input_FromAuxInfo @18 - INCHIGEN_Create = INCHIGEN_Create @19 - INCHIGEN_Destroy = INCHIGEN_Destroy @20 - INCHIGEN_DoCanonicalization = INCHIGEN_DoCanonicalization @21 - INCHIGEN_DoNormalization = INCHIGEN_DoNormalization @22 - INCHIGEN_DoSerialization = INCHIGEN_DoSerialization @23 - INCHIGEN_Reset = INCHIGEN_Reset @24 - INCHIGEN_Setup = INCHIGEN_Setup @25 - STDINCHIGEN_Create = STDINCHIGEN_Create @26 - STDINCHIGEN_Destroy = STDINCHIGEN_Destroy @27 - STDINCHIGEN_DoCanonicalization = STDINCHIGEN_DoCanonicalization @28 - STDINCHIGEN_DoNormalization = STDINCHIGEN_DoNormalization @29 - STDINCHIGEN_DoSerialization = STDINCHIGEN_DoSerialization @30 - STDINCHIGEN_Reset = STDINCHIGEN_Reset @31 - STDINCHIGEN_Setup = STDINCHIGEN_Setup @32 - -; ----------------- Pascal/stdcall calling convenions ------------------------ - _CheckINCHI@8 = pasc_CheckINCHI@8 @33 - _CheckINCHIKey@4 = pasc_CheckINCHIKey@4 @34 - _FreeINCHI@4 = pasc_FreeINCHI@4 @35 - _FreeStdINCHI@4 = pasc_FreeStdINCHI@4 @36 - _FreeStructFromINCHI@4 = pasc_FreeStructFromINCHI@4 @37 - _FreeStructFromStdINCHI@4 = pasc_FreeStructFromStdINCHI@4 @38 - _Free_inchi_Input@4 = pasc_Free_inchi_Input@4 @39 - _Free_std_inchi_Input@4 = pasc_Free_std_inchi_Input@4 @40 - _GetINCHI@8 = pasc_GetINCHI@8 @41 - _GetINCHIKeyFromINCHI@24 = pasc_GetINCHIKeyFromINCHI@24 @42 - _GetINCHIfromINCHI@8 = pasc_GetINCHIfromINCHI@8 @43 - _GetStdINCHI@8 = pasc_GetStdINCHI@8 @44 - _GetStdINCHIKeyFromStdINCHI@8 = pasc_GetStdINCHIKeyFromStdINCHI@8 @45 - _GetStringLength@4 = pasc_GetStringLength@4 @46 - _GetStructFromINCHI@8 = pasc_GetStructFromINCHI@8 @47 - _GetStructFromStdINCHI@8 = pasc_GetStructFromStdINCHI@8 @48 - _Get_inchi_Input_FromAuxInfo@16 = pasc_Get_inchi_Input_FromAuxInfo@16 @49 - _Get_std_inchi_Input_FromAuxInfo@12 = pasc_Get_std_inchi_Input_FromAuxInfo@12 @50 - _INCHIGEN_Create@0 = pasc_INCHIGEN_Create@0 @51 - _INCHIGEN_Destroy@4 = pasc_INCHIGEN_Destroy@4 @52 - _INCHIGEN_DoCanonicalization@8 = pasc_INCHIGEN_DoCanonicalization@8 @53 - _INCHIGEN_DoNormalization@8 = pasc_INCHIGEN_DoNormalization@8 @54 - _INCHIGEN_DoSerialization@12 = pasc_INCHIGEN_DoSerialization@12 @55 - _INCHIGEN_Reset@12 = pasc__INCHIGEN_Reset@12 @56 - _INCHIGEN_Setup@12 = pasc_INCHIGEN_Setup@12 @57 - _STDINCHIGEN_Create@0 = pasc_STDINCHIGEN_Create@0 @58 - _STDINCHIGEN_Destroy@4 = pasc_STDINCHIGEN_Destroy@4 @59 - _STDINCHIGEN_DoCanonicalization@8 = pasc_STDINCHIGEN_DoCanonicalization@8 @60 - _STDINCHIGEN_DoNormalization@8 = pasc_STDINCHIGEN_DoNormalization@8 @61 - _STDINCHIGEN_DoSerialization@12 = pasc_STDINCHIGEN_DoSerialization@12 @62 - _STDINCHIGEN_Reset@12 = pasc_STDINCHIGEN_Reset@12 @63 - _STDINCHIGEN_Setup@12 = pasc_STDINCHIGEN_Setup@12 @64 diff --git a/INCHI-1-SRC/INCHI_API/gcc_so_makefile/libinchi.def b/INCHI-1-SRC/INCHI_API/gcc_so_makefile/libinchi.def deleted file mode 100644 index 19fea7e..0000000 --- a/INCHI-1-SRC/INCHI_API/gcc_so_makefile/libinchi.def +++ /dev/null @@ -1,35 +0,0 @@ -LIBRARY - libinchi.dll -EXPORTS - CheckINCHI - CheckINCHIKey - FreeINCHI - FreeStdINCHI - FreeStructFromINCHI - FreeStructFromStdINCHI - Free_inchi_Input - Free_std_inchi_Input - GetINCHI - GetINCHIKeyFromINCHI - GetINCHIfromINCHI - GetStdINCHI - GetStdINCHIKeyFromStdINCHI - GetStringLength - GetStructFromINCHI - GetStructFromStdINCHI - Get_inchi_Input_FromAuxInfo - Get_std_inchi_Input_FromAuxInfo - INCHIGEN_Create - INCHIGEN_Destroy - INCHIGEN_DoCanonicalization - INCHIGEN_DoNormalization - INCHIGEN_DoSerialization - INCHIGEN_Reset - INCHIGEN_Setup - STDINCHIGEN_Create - STDINCHIGEN_Destroy - STDINCHIGEN_DoCanonicalization - STDINCHIGEN_DoNormalization - STDINCHIGEN_DoSerialization - STDINCHIGEN_Reset - STDINCHIGEN_Setup diff --git a/INCHI-1-SRC/INCHI_API/gcc_so_makefile/libinchi.map b/INCHI-1-SRC/INCHI_API/gcc_so_makefile/libinchi.map deleted file mode 100644 index 0526992..0000000 --- a/INCHI-1-SRC/INCHI_API/gcc_so_makefile/libinchi.map +++ /dev/null @@ -1,5 +0,0 @@ -{ -global: CheckINCHI; CheckINCHIKey; FreeINCHI; FreeStdINCHI; FreeStructFromINCHI; FreeStructFromStdINCHI; Free_inchi_Input; Free_std_inchi_Input; GetINCHI; GetINCHIKeyFromINCHI; GetINCHIfromINCHI; GetStdINCHI; GetStdINCHIKeyFromStdINCHI; GetStringLength; GetStructFromINCHI; GetStructFromStdINCHI; Get_inchi_Input_FromAuxInfo; Get_std_inchi_Input_FromAuxInfo; INCHIGEN_Create; INCHIGEN_Destroy; INCHIGEN_DoCanonicalization; INCHIGEN_DoNormalization; INCHIGEN_DoSerialization; INCHIGEN_Reset; INCHIGEN_Setup; STDINCHIGEN_Create; STDINCHIGEN_Destroy; STDINCHIGEN_DoCanonicalization; STDINCHIGEN_DoNormalization; STDINCHIGEN_DoSerialization; STDINCHIGEN_Reset; STDINCHIGEN_Setup; -local: *; -}; - diff --git a/INCHI-1-SRC/INCHI_API/gcc_so_makefile/makefile b/INCHI-1-SRC/INCHI_API/gcc_so_makefile/makefile deleted file mode 100644 index 92e5911..0000000 --- a/INCHI-1-SRC/INCHI_API/gcc_so_makefile/makefile +++ /dev/null @@ -1,226 +0,0 @@ -# Comment out the next line to create so/dll only -CREATE_MAIN = 1 - -ifdef windir -# Under Win32/MinGW/gcc to add functions compliant to Pascal/stdcall calling -# conventions and provide all functionality present in the produced by the -# MS VC++ 6.0 libinchi.dll -- uncomment the next line -# ADD_PASCAL_INCHI_CALLS = 1 -else -# Uncomment the following line in case of Linux -# or define ISLINUX in command line: make ISLINUX=1 -# ISLINUX = 1 -endif - -# Linux fpic option: replace -fPIC with -fpic if the latter works -# Comment out "LINUX_Z_RELRO =" if -z relro is not supported -# These options are needed to avoid the following SELinux message: -# "Error: cannot restore segment prot after reloc: Permission denied" -# In addition, inchi.map restricts set of expoorted from .so -# functions to those which belong to InChI API -ifndef windir -LINUX_MAP = ,--version-script=libinchi.map -ifdef ISLINUX -LINUX_FPIC = -fPIC -LINUX_Z_RELRO = ,-z,relro -endif -endif - -# === version === -MAIN_VERSION = .1 -VERSION = $(MAIN_VERSION).04.00 - -# === executable & library directory === -ifndef LIB_DIR - LIB_DIR = result -endif - -# === InChI Library name === -ifndef INCHI_LIB_NAME - INCHI_LIB_NAME = libinchi -endif -INCHI_LIB_PATHNAME = $(LIB_DIR)/$(INCHI_LIB_NAME) - -# === Main program nane ==== -ifndef INCHI_MAIN_NAME - ifdef windir - EXE = .exe - else - EXE = - endif - - INCHI_MAIN_NAME = inchi_main$(EXE) -endif -INCHI_MAIN_PATHNAME = $(LIB_DIR)/$(INCHI_MAIN_NAME) - -# === Linker to create (Shared) InChI library ==== -ifndef SHARED_LINK - SHARED_LINK = gcc -shared -endif - -# === Linker to create Main program ===== -ifndef LINKER - ifndef windir - ifdef ISLINUX - LINKER_CWD_PATH = -Wl,-R,"" - endif - endif - LINKER = gcc -s $(LINKER_CWD_PATH) -endif - -ifndef P_LIBR - P_LIBR = ../inchi_dll/ -endif - -ifndef P_MAIN - P_MAIN = ../inchi_main/ -endif - - -# === C Compiler =============== -ifndef C_COMPILER - C_COMPILER = gcc -endif - -# === C Compiler Options ======= -ifndef C_OPTIONS - C_OPTIONS = -ansi -O3 -c - ifndef windir - ifdef ISLINUX - ifndef C_SO_OPTIONS - C_SO_OPTIONS = $(LINUX_FPIC) - endif - endif - endif - ifndef C_MAIN_OPTIONS - C_MAIN_OPTIONS = -DBUILD_LINK_AS_DLL - endif -endif - -ifdef windir -ifdef ADD_PASCAL_INCHI_CALLS - INCHI_DEF = $(INCHI_LIB_NAME)-pas.def - SHARED_LINK_PARM = --add-stdcall-alias -else - INCHI_DEF = $(INCHI_LIB_NAME).def - SHARED_LINK_PARM = -endif -endif - -ifdef CREATE_MAIN - -INCHI_MAIN_SRCS = $(P_MAIN)e_0dstereo.c $(P_MAIN)e_ichimain.c \ - $(P_MAIN)e_ichi_io.c $(P_MAIN)e_ichi_parms.c \ - $(P_MAIN)e_inchi_atom.c $(P_MAIN)e_mol2atom.c \ - $(P_MAIN)e_readinch.c $(P_MAIN)e_readmol.c \ - $(P_MAIN)e_readstru.c $(P_MAIN)e_util.c \ - $(P_MAIN)e_ichimain_a.c - - -# === InChI Main object files ============ - -INCHI_MAIN_OBJS = e_0dstereo.o e_ichimain.o \ - e_ichi_io.o e_ichi_parms.o \ - e_inchi_atom.o e_mol2atom.o \ - e_readinch.o e_readmol.o \ - e_readstru.o e_util.o \ - e_ichimain_a.o - -# === InChI Main Link rule ================ - -ifdef windir - -$(INCHI_MAIN_PATHNAME) : $(INCHI_MAIN_OBJS) $(INCHI_LIB_PATHNAME).a - $(LINKER) -o $(INCHI_MAIN_PATHNAME) $(INCHI_MAIN_OBJS) \ - $(INCHI_LIB_PATHNAME).a -lm - -else - -$(INCHI_MAIN_PATHNAME) : $(INCHI_MAIN_OBJS) $(INCHI_LIB_PATHNAME).so$(VERSION) - $(LINKER) -o $(INCHI_MAIN_PATHNAME) $(INCHI_MAIN_OBJS) \ - $(INCHI_LIB_PATHNAME).so$(VERSION) -lm - -endif - -# === InChI Main compile rule ============ - -%.o: $(P_MAIN)%.c - $(C_COMPILER) $(C_MAIN_OPTIONS) $(C_OPTIONS) $< - -endif - - -# === InChI Library Source files ============ - -INCHI_LIB_SRCS = $(P_LIBR)ichican2.c $(P_LIBR)ichicano.c \ - $(P_LIBR)ichicans.c $(P_LIBR)ichiisot.c \ - $(P_LIBR)ichilnct.c $(P_LIBR)ichimak2.c \ - $(P_LIBR)ichimake.c $(P_LIBR)ichimap1.c \ - $(P_LIBR)ichimap2.c $(P_LIBR)ichimap4.c \ - $(P_LIBR)ichinorm.c $(P_LIBR)ichiparm.c \ - $(P_LIBR)ichiprt1.c $(P_LIBR)ichiprt2.c \ - $(P_LIBR)ichiprt3.c $(P_LIBR)ichiqueu.c \ - $(P_LIBR)ichiring.c $(P_LIBR)ichisort.c \ - $(P_LIBR)ichister.c $(P_LIBR)ichitaut.c \ - $(P_LIBR)ichi_bns.c $(P_LIBR)inchi_dll.c \ - $(P_LIBR)ichiread.c $(P_LIBR)ichirvr1.c \ - $(P_LIBR)ichirvr2.c $(P_LIBR)ichirvr3.c \ - $(P_LIBR)ichirvr4.c $(P_LIBR)ichirvr5.c \ - $(P_LIBR)ichirvr6.c $(P_LIBR)ichirvr7.c \ - $(P_LIBR)inchi_dll_main.c $(P_LIBR)runichi.c \ - $(P_LIBR)inchi_dll_a.c $(P_LIBR)inchi_dll_a2.c \ - $(P_LIBR)ikey_dll.c $(P_LIBR)ikey_base26.c \ - $(P_LIBR)ichi_io.c \ - $(P_LIBR)sha2.c $(P_LIBR)strutil.c \ - $(P_LIBR)util.c - -# === InChI Library Object files ============ - -INCHI_LIB_OBJS = ichican2.o ichicano.o \ - ichicans.o ichiisot.o \ - ichilnct.o ichimak2.o \ - ichimake.o ichimap1.o \ - ichimap2.o ichimap4.o \ - ichinorm.o ichiparm.o \ - ichiprt1.o ichiprt2.o \ - ichiprt3.o ichiqueu.o \ - ichiring.o ichisort.o \ - ichister.o ichitaut.o \ - ichi_bns.o inchi_dll.o \ - ichiread.o ichirvr1.o \ - ichirvr2.o ichirvr3.o \ - ichirvr4.o ichirvr5.o \ - ichirvr6.o ichirvr7.o \ - inchi_dll_main.o runichi.o \ - inchi_dll_a.o inchi_dll_a2.o \ - ikey_dll.o ikey_base26.o \ - ichi_io.o \ - sha2.o strutil.o \ - util.o - -# === InChI Library link rule ========= - -ifdef windir - -$(INCHI_LIB_PATHNAME).a: $(INCHI_LIB_OBJS) - $(SHARED_LINK) -o $(INCHI_LIB_PATHNAME).dll $(INCHI_DEF) \ - $(INCHI_LIB_OBJS) \ - -Wl,--out-implib,$(INCHI_LIB_PATHNAME).a,-soname,$(INCHI_LIB_PATHNAME).dll - -else - -$(INCHI_LIB_PATHNAME).so$(VERSION): $(INCHI_LIB_OBJS) - $(SHARED_LINK) $(SHARED_LINK_PARM) -o \ - $(INCHI_LIB_PATHNAME).so$(VERSION) \ - $(INCHI_LIB_OBJS) \ - -Wl$(LINUX_MAP)$(LINUX_Z_RELRO),-soname,$(INCHI_LIB_NAME).so$(MAIN_VERSION) - ln -fs $(INCHI_LIB_NAME).so$(VERSION) \ - $(INCHI_LIB_PATHNAME).so$(MAIN_VERSION) - -endif - -# === InChI Library compile rule ========= - -%.o: $(P_LIBR)%.c - $(C_COMPILER) $(C_SO_OPTIONS) $(C_OPTIONS) $< - diff --git a/INCHI-1-SRC/INCHI_API/gcc_so_makefile/readme.txt b/INCHI-1-SRC/INCHI_API/gcc_so_makefile/readme.txt deleted file mode 100644 index 0c6376a..0000000 --- a/INCHI-1-SRC/INCHI_API/gcc_so_makefile/readme.txt +++ /dev/null @@ -1,114 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - - -This directory contains a gcc makefile for INCHI_MAIN + INCHI_DLL code -to create a InChI library as a shared object, as well as the demo executable -calling the library (Linux). - -This makefile was tested with gcc v. 4.1.2 20061115 (Debian 4.1.1-21) -under Debian Etch and gcc v. 4.2.4 under Ubuntu 2.6.24-24-server. - - -How to run make under Linux ---------------------------- -make ISLINUX=1 - -This would restrict a list of exported entry points to those described -in inchi_api.h (see file inchi.map), add CWD to the inchi_main shared -object search path, and, most importantly, eliminate refusal to load -the shared library which otherwise would produce this message: - -"error while loading shared libraries: libinchi.so.1: - cannot restore segment prot after reloc: Permission denied" - -under SELinux default settings. - -Note: it may be necessary to copy symlink libinchi.so.1 to /usr/lib. - - -General -------- -This makefile creates InChI library and a dynamically linked -to it demo application, inchi_main. - -Note that the inchi_main is just a sample which is not supposed -to be used for the production. - -The binaries consist of: - -1) The main program -- a Molfile/InChI Aux Info reader -that creates a chemical structure representation suitable for -the InChI library API, feeds it into the InChI library and -outputs the results. -The code is located in the ../inchi_main sub-directory. - -2) The InChI library that creates the standard InChI identifier, -standard InChIKey, Auxiliary information, and error/warning messages. -The code is located in the ../inchi_dll sub-directory - -The makefile should be placed into a directory that has a common parent -with INCHI_MAIN and INCHI_DLL as it is in the zip file containing -the source code and this makefile. - -Under Linux the makefile creates: - inchi_main -- the main program (InChI software library - demo application) and - libinchi.so.1.04.00 -- the shared object and a link libinchi.so.1 to it - libinchi.so.1 -- a symbolic link to libinchi.so.1.04.00 - -in the "result" subdirectory of this makefile directory out of source -code located in "INCHI_DLL" and "INCHI_MAIN" directories. - -Files result/libinchi.so.1.04.00.gz and result/inchi_main.gz -contain Linux i386 binaries created with the included makefile. - -The InChI software library demo application, inchi_main, needs -libinchi.so.1, a symbolic link to libinchi.so.1.04.00. - - - -========= - LINKS -========= - -IUPAC http://www.iupac.org/inchi -InChI Trust http://www.inchi-trust.org -InChI discussion group https://lists.sourceforge.net/lists/listinfo/inchi-discuss diff --git a/INCHI-1-SRC/INCHI_API/gcc_so_makefile/result/inchi_main.gz b/INCHI-1-SRC/INCHI_API/gcc_so_makefile/result/inchi_main.gz deleted file mode 100644 index f3f2cab..0000000 Binary files a/INCHI-1-SRC/INCHI_API/gcc_so_makefile/result/inchi_main.gz and /dev/null differ diff --git a/INCHI-1-SRC/INCHI_API/gcc_so_makefile/result/readme.txt b/INCHI-1-SRC/INCHI_API/gcc_so_makefile/result/readme.txt deleted file mode 100644 index 1a8fe53..0000000 --- a/INCHI-1-SRC/INCHI_API/gcc_so_makefile/result/readme.txt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - - -========= - FILES -========= - -readme.txt This file - -libinchi.so.1.04.00.gz - InChI Library - Linux shared object (gzipped; 32bit) - -inchi_main.gz Demo application for Linux (gzipped; 32bit) - - - -Note ----- -The InChI software library demo application, inchi_main, -needs libinchi.so.1, a symbolic link to libinchi.so.1.04.00. -This link may be created in the folder containing inchi_main. - diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/extr_ct.h b/INCHI-1-SRC/INCHI_API/inchi_dll/extr_ct.h deleted file mode 100644 index f728025..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/extr_ct.h +++ /dev/null @@ -1,285 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __EXTR_CT_H__ -#define __EXTR_CT_H__ - -#include "mode.h" -#include "ichisize.h" - -struct AtData { - char element[3]; - int maxvalence; -}; - - - -#define NUM_CHEM_ELEMENTS 127 /* well above number of known chem. elements */ - - -#define AT_ISO_SORT_KEY_MULT 32 /* up to 32 identical hydrogen isotopes */ - /* (similar to T_GROUP_ISOWT_MULT) */ - /* changed from 16 9-12-2003 */ -typedef long AT_ISO_SORT_KEY; /* signed, should hold up to 4096*max_iso_diff */ - /* (similar to T_GROUP_ISOWT) */ -/* - = num_1H + AT_ISO_SORT_KEY_MULT*(num_D + AT_ISO_SORT_KEY_MULT*(num_T+AT_ISO_SORT_KEY_MULT*iso_atw_diff)) -*/ - -/* typedef signed char AT_ISOTOPIC; */ /* + or - */ -typedef struct tagStereoCarb { - AT_NUMB at_num; - U_CHAR parity; -} AT_STEREO_CARB; -typedef struct tagStereoDble { - AT_NUMB at_num1; - AT_NUMB at_num2; - U_CHAR parity; -} AT_STEREO_DBLE; - -typedef struct tagIsotopicAtom { - AT_NUMB at_num; - NUM_H num_1H; - NUM_H num_D; - NUM_H num_T; - NUM_H iso_atw_diff; -} AT_ISOTOPIC; - -typedef AT_NUMB AT_STEREO; - -#define BYTE_BITS 8 /* number of bits in one byte */ - -#define BOND_MASK 0xf /* 4 bits */ -#define BOND_BITS 4 /* 3 or 4 does not matter; 2 is too small for BOND_TAUTOM */ -#define BOND_ADD (BOND_BITS==2?-1:0) /* subtract 1 from bonds stored in CT */ - - -typedef struct tagAtom { - char elname[ATOM_EL_LEN]; - AT_NUMB neighbor[MAXVAL]; /* changed to unsigned 2-2-95. D.Ch. */ - AT_NUMB init_rank; /* also used in remove_terminal_HDT() to save orig. at. number */ - AT_NUMB orig_at_number; - AT_NUMB orig_compt_at_numb; - /* low 3 bits=bond type; - high 5 bits (in case of cut-vertex atom) = an attached part number - */ - U_CHAR bond_type[MAXVAL]; - U_CHAR el_number; /* periodic table number = charge of the nucleus = number of the protons */ - /* U_CHAR hill_type; */ /* number in psudo hill order */ - S_CHAR valence; - S_CHAR chem_bonds_valence; /* 8-24-00 to treat tautomer centerpoints, etc. */ - S_CHAR num_H; /* first not including D, T; add_DT_to_num_H() includes. */ - S_CHAR num_iso_H[NUM_H_ISOTOPES]; /* num 1H, 2H(D), 3H(T) */ - S_CHAR cFlags; - S_CHAR iso_atw_diff; /* abs(iso_atw_diff) < 127 or 31 - ??? */ - AT_ISO_SORT_KEY iso_sort_key; /* = num_1H + AT_ISO_SORT_KEY_MULT^1*num_D - + AT_ISO_SORT_KEY_MULT^2*num_T - + AT_ISO_SORT_KEY_MULT^3*iso_atw_diff - */ - S_CHAR charge; - S_CHAR radical; /* 1=>doublet(.), 2=> triplet as singlet (:) ???? why are they same ???? */ - S_CHAR marked; - - AT_NUMB endpoint; /* tautomer analysis. If != 0 then the hydrogens & (-)charge are in the tautomer group. */ - - /* - Pairs stereo_bond_neighbor[] and stereo_bond_neighbor2[], etc - initially refer to non-isotopic and isotopic cases, respectively. - To use same stereo processing code these arrays are swapped when - switching from non-isotopic to isotopic processing and back. - */ - AT_NUMB stereo_bond_neighbor[MAX_NUM_STEREO_BONDS]; /* Original number of an opposite atom */ - AT_NUMB stereo_bond_neighbor2[MAX_NUM_STEREO_BONDS]; /* (stereo bond neighbor) +1; */ - S_CHAR stereo_bond_ord[MAX_NUM_STEREO_BONDS]; /* Ordering number of a bond/neighbor in the direction to the */ - S_CHAR stereo_bond_ord2[MAX_NUM_STEREO_BONDS]; /* stereo bond opposite atom (important for cumulenes); */ - S_CHAR stereo_bond_z_prod[MAX_NUM_STEREO_BONDS]; /* Relative atom-neighbors */ - S_CHAR stereo_bond_z_prod2[MAX_NUM_STEREO_BONDS]; /* double bond planes orientation; */ - S_CHAR stereo_bond_parity[MAX_NUM_STEREO_BONDS]; /* parity + MULT_STEREOBOND*chain_length, */ - S_CHAR stereo_bond_parity2[MAX_NUM_STEREO_BONDS]; /* where: */ - /* - parity (Mask 0x07=BITS_PARITY): - - 0 = AB_PARITY_NONE = not a stereo bond - 1/2 = AB_PARITY_ODD/EVEN = bond parity defined from initial ranks - 3 = AB_PARITY_UNKN = geometry is unknown to the user - 4 = AB_PARITY_UNDF = not enough geometry info to find the parity - 6 = AB_PARITY_CALC = calculate later from the neighbor ranks; some ot them can be - replaced with AB_PARITY_ODD/EVEN after equivalence ranks have been determined - - length (Mask 0x38=MASK_CUMULENE_LEN, length=stereo_bond_parity[i]/MULT_STEREOBOND): - - 0 => double or alternating stereogenic bond - 1 => cumulene with 2 double bonds (stereogenic center) - 2 => cumulene with 3 double bonds (stereogenic bond) - length <= (MAX_CUMULENE_LEN=2) - bit KNOWN_PARITIES_EQL = 0x40: all pairs of const. equ. atoms are connected by stereo bonds - and these bonds have identical parities - */ - - S_CHAR parity; /* -- Mask 0x07=BITS_PARITY: -- - 0 = AB_PARITY_NONE => no parity; also parity&0x38 = 0 - 1 = AB_PARITY_ODD => odd parity - 2 = AB_PARITY_EVEN => even parity - 3 = AB_PARITY_UNKN => user marked as unknown parity - 4 = AB_PARITY_UNDF => parity cannot be defined because of symmetry or not well defined geometry - */ - S_CHAR parity2; /* parity including parity due to isotopic terminal H */ - /* bit msks: 0x07 => known parity (1,2,3,4) or AB_PARITY_CALC=6, AB_PARITY_IISO = 6 */ - /* 0x40 => KNOWN_PARITIES_EQL */ - S_CHAR stereo_atom_parity; /* similar to stereo_bond_parity[]: known in advance AB_PARITY_* value + KNOWN_PARITIES_EQL bit */ - S_CHAR stereo_atom_parity2; - S_CHAR final_parity; /* defined by equivalence ranks */ - S_CHAR final_parity2; /* defined by equivalence ranks, incl. due to terminal isotopic H */ - S_CHAR bAmbiguousStereo; - S_CHAR bHasStereoOrEquToStereo; - S_CHAR bHasStereoOrEquToStereo2; -#if ( FIND_RING_SYSTEMS == 1 ) - S_CHAR bCutVertex; - AT_NUMB nRingSystem; - AT_NUMB nNumAtInRingSystem; - AT_NUMB nBlockSystem; -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) - AT_NUMB nDistanceFromTerminal; -#endif -#endif - S_CHAR z_dir[3]; - -} sp_ATOM ; - -#define BOND_SINGLE BOND_TYPE_SINGLE /* 1 */ -#define BOND_DOUBLE BOND_TYPE_DOUBLE /* 2 */ -#define BOND_TRIPLE BOND_TYPE_TRIPLE /* 3 */ -#define BOND_ALTERN BOND_TYPE_ALTERN /* 4 single/double */ - -#define BOND_ALT_123 5 /* single/double/triple */ -#define BOND_ALT_13 6 /* single/triple */ -#define BOND_ALT_23 7 /* double/triple */ -#define BOND_TAUTOM 8 -#define BOND_ALT12NS 9 -#define BOND_NUMDIF 9 /* number of different kinds of bonds */ - -#define BOND_TYPE_MASK 0x0f - -#define BOND_MARK_ALL 0xf0 /* complement to BOND_TYPE_MASK */ - -#define BOND_MARK_ALT12 0x10 -#define BOND_MARK_ALT123 0x20 -#define BOND_MARK_ALT13 0x30 -#define BOND_MARK_ALT23 0x40 -#define BOND_MARK_ALT12NS 0x50 /* 1 or 2, non-stereo */ -#define BOND_MARK_MASK 0x70 - -#define ACTUAL_ORDER(PBNS, IAT,IBOND, BTYPE) ( ((PBNS) && (PBNS)->edge && (PBNS)->vert &&\ - ((BTYPE)==BOND_ALT_123 || (BTYPE)==BOND_ALT_13 || (BTYPE)==BOND_ALT_23))? (PBNS)->edge[(PBNS)->vert[IAT].iedge[IBOND]].flow+BOND_TYPE_SINGLE:(BTYPE)) - - -#define BITS_PARITY 0x07 /* mask to retrieve half-bond parity */ -#define MASK_CUMULENE_LEN 0x38 /* mask to retrieve (cumulene chain length - 1)*MULT_STEREOBOND */ -#define KNOWN_PARITIES_EQL 0x40 /* parity is same for all pairs of constit. equivalent atoms */ -#define MAX_CUMULENE_LEN 2 /* max number of bonds in a cumulene chain - 1 */ - -#define MULT_STEREOBOND 0x08 /* multiplier for cumulene chain length - odd length => chiral, even length => stereogenic bond */ - -#define MAKE_BITS_CUMULENE_LEN(X) ((X)*MULT_STEREOBOND) -#define GET_BITS_CUMULENE_LEN(X) ((X)&MASK_CUMULENE_LEN) -#define BOND_CHAIN_LEN(X) (GET_BITS_CUMULENE_LEN(X)/MULT_STEREOBOND) /* 0 => double bond, 1 => allene, 2 => cumulene,..*/ -#define IS_ALLENE_CHAIN(X) ((GET_BITS_CUMULENE_LEN(X)/MULT_STEREOBOND)%2) - -/* atom or bond parity value definitions */ -#define AB_PARITY_NONE 0 /* 0 => no parity; also parity&0x38 = 0 */ -#define AB_PARITY_ODD 1 /* 1 => odd parity */ -#define AB_PARITY_EVEN 2 /* 2 => even parity */ -#define AB_PARITY_UNKN 3 /* 3 => user marked as unknown parity */ -#define AB_PARITY_UNDF 4 /* 4 => parity cannot be defined because of symmetry or not well defined geometry */ -#define AB_PARITY_IISO 5 /* 5 => no parity because of identical atoms */ -#define AB_PARITY_CALC 6 /* 6 => calculate parity later */ -#define AB_PARITY_0D 8 /* 8 => bit signifies 0D case -- not used */ - -#define AB_INV_PARITY_BITS (AB_PARITY_ODD ^ AB_PARITY_EVEN) - - -#define AB_MAX_KNOWN_PARITY 4 /* precalculated from const. equivalence parities */ -#define AB_MIN_KNOWN_PARITY 1 - -#define AB_MAX_PART_DEFINED_PARITY 3 /* 1, 2, 3 => defined parities, uncluding 'unknown' */ -#define AB_MIN_PART_DEFINED_PARITY 1 /* min(AB_PARITY_ODD, AB_PARITY_EVEN, AB_PARITY_UNKN) */ - -#define AB_MAX_WELL_DEFINED_PARITY 2 /* 1, 2 => well defined parities, uncluding 'unknown' */ -#define AB_MIN_WELL_DEFINED_PARITY 1 /* min(AB_PARITY_ODD, AB_PARITY_EVEN) */ - -#define AB_MIN_ILL_DEFINED_PARITY 3 -#define AB_MAX_ILL_DEFINED_PARITY 4 - -#define AB_MAX_ANY_PARITY 4 -#define AB_MIN_ANY_PARITY 1 - -#define AMBIGUOUS_STEREO 1 -#define AMBIGUOUS_STEREO_ATOM 2 -#define AMBIGUOUS_STEREO_BOND 4 -#define AMBIGUOUS_STEREO_ATOM_ISO 8 -#define AMBIGUOUS_STEREO_BOND_ISO 16 -#define AMBIGUOUS_STEREO_ERROR 32 - - -#define MIN_DOT_PROD 50 /* min value of at->stereo_bond_z_prod[i] to define parity */ - -#define ATOM_PARITY_VAL(X) (X) -#define ATOM_PARITY_PART_DEF(X) (AB_MIN_PART_DEFINED_PARITY <= (X) && (X) <= AB_MAX_PART_DEFINED_PARITY) -#define ATOM_PARITY_ILL_DEF(X) (AB_MIN_ILL_DEFINED_PARITY <= (X) && (X) <= AB_MAX_ILL_DEFINED_PARITY) -#define ATOM_PARITY_KNOWN(X) (AB_MIN_KNOWN_PARITY <= (X) && (X) <= AB_MAX_KNOWN_PARITY) -#define ATOM_PARITY_WELL_DEF(X) (AB_MIN_WELL_DEFINED_PARITY <= (X) && (X) <= AB_MAX_WELL_DEFINED_PARITY) -#define ATOM_PARITY_NOT_UNKN(X) (ATOM_PARITY_KNOWN(X) && (X) != AB_PARITY_UNKN) - -#define PARITY_VAL(X) ((X) & BITS_PARITY) -#define PARITY_PART_DEF(X) (AB_MIN_PART_DEFINED_PARITY <= PARITY_VAL(X) && PARITY_VAL(X) <= AB_MAX_PART_DEFINED_PARITY) -#define PARITY_ILL_DEF(X) (AB_MIN_ILL_DEFINED_PARITY <= PARITY_VAL(X) && PARITY_VAL(X) <= AB_MAX_ILL_DEFINED_PARITY) -#define PARITY_KNOWN(X) (AB_MIN_KNOWN_PARITY <= PARITY_VAL(X) && PARITY_VAL(X) <= AB_MAX_KNOWN_PARITY) -#define PARITY_WELL_DEF(X) (AB_MIN_WELL_DEFINED_PARITY <= PARITY_VAL(X) && PARITY_VAL(X) <= AB_MAX_WELL_DEFINED_PARITY) -#define PARITY_CALCULATE(X) (AB_PARITY_CALC == PARITY_VAL(X)) -#define BOND_PARITY_PART_DEFINED(X) (PARITY_PART_DEF(X) || PARITY_CALCULATE(X)) -#define BOND_PARITY_PART_KNOWN(X) (PARITY_KNOWN(X) || PARITY_CALCULATE(X)) -#define ALL_BUT_PARITY(X) ((X)&~BITS_PARITY) - -#define ALWAYS_SET_STEREO_PARITY 0 -#define NO_ISOLATED_NON_6RING_AROM_BOND 0 /* for Yuri */ -#define SAVE_6_AROM_CENTERS 0 /* for Yuri */ - -#endif /* __EXTR_CT_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichi_bns.h b/INCHI-1-SRC/INCHI_API/inchi_dll/ichi_bns.h deleted file mode 100644 index aa44e90..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichi_bns.h +++ /dev/null @@ -1,466 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHI_BNS_H___ -#define __INCHI_BNS_H___ - -#define BN_MAX_ALTP 16 -/*#define MAX_VERTEX 1024*/ /* including s; if vert[] has num_vert then MAX_VERTEX has (2*num_vert+2+FIRST_INDX) elements */ - -/* forward declarations */ - -struct BalancedNetworkStructure; -struct BalancedNetworkData; -struct tagTautomerGroupsInfo; -struct tagChargeGroupsInfo; -struct BN_AtomsAtTautGroup; -struct tagSaltChargeCandidate; - -/* define BNS types */ - -typedef S_SHORT Vertex; -typedef S_SHORT EdgeIndex; -typedef S_SHORT Edge[2]; /* Edge[0] = vertex1, Edge[1] = iedge or -(1+vertex1) if vertex2 = s or t */ -typedef S_SHORT BNS_IEDGE; -typedef S_SHORT EdgeFlow; -typedef S_SHORT VertexFlow; - - -#define BNS_EDGE_FORBIDDEN_MASK 1 -#define BNS_EDGE_FORBIDDEN_TEMP 2 -#define BNS_EDGE_FORBIDDEN_TEST 4 - -/* BNS vertex types */ - -#define BNS_VERT_TYPE_ATOM 0x0001 -#define BNS_VERT_TYPE_ENDPOINT 0x0002 /* attribute */ -#define BNS_VERT_TYPE_TGROUP 0x0004 -#define BNS_VERT_TYPE_C_POINT 0x0008 -#define BNS_VERT_TYPE_C_GROUP 0x0010 -#define BNS_VERT_TYPE_SUPER_TGROUP 0x0020 -#define BNS_VERT_TYPE_TEMP 0x0040 - -#define BNS_VERT_TYPE__AUX 0x0080 /* vertex added to build charge substructures */ -#define BNS_VERT_TYPE_C_NEGATIVE 0x0100 /* negative charge group; attribute, should be used with BNS_VERT_TYPE_C_GROUP */ -#define BNS_VERT_TYPE_ACID 0x0200 /* only for this type are allowed paths: t_group-atom-c_group_neg (path_TACN) */ -#define BNS_VERT_TYPE_CARBON_GR 0x0400 /* charge of carbon atom; should be used with BNS_VT_C_POS, BNS_VT_C_NEG */ -#define BNS_VERT_TYPE_METAL_GR 0x0800 /* metal atom group; may be used alone or with BNS_VT_M_POS, BNS_VT_M_NEG */ - -#define BNS_VERT_TYPE_ANY_GROUP (BNS_VERT_TYPE_TGROUP | BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_SUPER_TGROUP) - -/* InChI->Structure */ - -#define BNS_VT_C_POS BNS_VERT_TYPE_C_GROUP /* positive charge group, heteroat */ -#define BNS_VT_C_NEG (BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE) /* negative charge group, heteroat */ -#define BNS_VT_C_POS_C (BNS_VT_C_POS | BNS_VERT_TYPE_CARBON_GR) /* positive charge group, C, Si, Ge, Sn */ -#define BNS_VT_C_NEG_C (BNS_VT_C_NEG | BNS_VERT_TYPE_CARBON_GR) /* negative charge group, C, Si, Ge, Sn */ -#define BNS_VT_C_POS_M (BNS_VT_C_POS | BNS_VERT_TYPE_METAL_GR) /* positive charge group, metal */ -#define BNS_VT_C_NEG_M (BNS_VT_C_NEG | BNS_VERT_TYPE_METAL_GR) /* negative charge group, metal */ -#define BNS_VT_M_GROUP BNS_VERT_TYPE_METAL_GR /* metal-group, flower vertex */ - -#define BNS_VT_C_POS_ALL (BNS_VERT_TYPE_SUPER_TGROUP | BNS_VERT_TYPE_C_GROUP) /* supergroup (+) */ -#define BNS_VT_C_NEG_ALL (BNS_VT_C_POS_ALL | BNS_VERT_TYPE_C_NEGATIVE) /* supergroup (-) */ - -#define BNS_VT_CHRG_STRUCT (BNS_VERT_TYPE__AUX | BNS_VERT_TYPE_TEMP) /* ChargeStruct vertex */ -#define BNS_VT_YVCONNECTOR BNS_VERT_TYPE__AUX /* group connection */ - -#define IS_BNS_VT_C_OR_CSUPER_GR(X) ((X) & BNS_VT_C_POS) -#define IS_BNS_VT_C_GR(X) (((X) & BNS_VT_C_POS_ALL) == BNS_VERT_TYPE_C_GROUP) -#define IS_BNS_VT_CM_GR(X) (((X) & BNS_VT_C_POS_M) == BNS_VT_C_POS_M) /* metal charge group */ -#define IS_BNS_VT_M_GR(X) ((X) == BNS_VERT_TYPE_METAL_GR ) /* metal flower base or vertices */ -#define IS_BNS_VT_YVCONNECTOR(X) (((X) & BNS_VERT_TYPE__AUX) && !((X) & BNS_VERT_TYPE_TEMP)) -#define IS_BNS_VT_CHRG_STRUCT(X) (((X) & BNS_VERT_TYPE__AUX) && ((X) & BNS_VERT_TYPE_TEMP)) -#define IS_BNS_VT_ATOM(X) ((X) & BNS_VERT_TYPE_ATOM) - -#define BNS_ADD_SUPER_TGROUP 1 /* reserve one more edge for a t-group to connect to a single super-t-group */ -#define NUM_KINDS_OF_GROUPS 2 /* 1 accounts for t-group kind, one more 1 accounts for c-group kind */ - -#define BNS_ADD_ATOMS 2 /* max. number of fictitious atoms to add (except t-gtoups) */ -#define BNS_ADD_EDGES 1 /* max. number of edges to add to each atom (except edges to a t-group or c-group) */ - -typedef enum tagAltPathConst { - iALTP_MAX_LEN, /* 0 */ - iALTP_FLOW, /* 1 */ - iALTP_PATH_LEN, /* 2 */ - iALTP_START_ATOM, /* 3 */ - iALTP_END_ATOM, /* 4 */ - iALTP_NEIGHBOR, /* 5 */ - iALTP_HDR_LEN = iALTP_NEIGHBOR -} ALT_CONST; - -#define ALTP_PATH_LEN(altp) (altp)[iALTP_PATH_LEN].number /* number of bonds = number of atoms-1*/ -#define ALTP_END_ATOM(altp) (altp)[iALTP_END_ATOM].number -#define ALTP_START_ATOM(altp) (altp)[iALTP_START_ATOM].number -#define ALTP_THIS_ATOM_NEIGHBOR(altp,X) (altp)[iALTP_NEIGHBOR+(X)].ineigh[0] /* 0 <= X < path_len */ -#define ALTP_NEXT_ATOM_NEIGHBOR(altp,X) (altp)[iALTP_NEIGHBOR+(X)].ineigh[1] -#define ALTP_CUR_THIS_ATOM_NEIGHBOR(altp) (altp)[iALTP_NEIGHBOR+ALTP_PATH_LEN(altp)].ineigh[0] /* 0 <= X < path_len */ -#define ALTP_CUR_NEXT_ATOM_NEIGHBOR(altp) (altp)[iALTP_NEIGHBOR+ALTP_PATH_LEN(altp)].ineigh[1] -#define ALTP_NEXT(altp) (++ALTP_PATH_LEN(altp)) -#define ALTP_PREV(altp) (--ALTP_PATH_LEN(altp)) -#define ALTP_MAY_ADD(altp) (iALTP_NEIGHBOR + (altp)[iALTP_PATH_LEN].number < (altp)[iALTP_MAX_LEN].number) -#define ALTP_ALLOCATED_LEN(altp) (altp)[iALTP_MAX_LEN].number -#define ALTP_DELTA(altp) (altp)[iALTP_FLOW].flow[0] -#define ALTP_OVERFLOW(altp) (altp)[iALTP_FLOW].flow[1] - -#define Vertex_s 0 -#define Vertex_t 1 - -#define NO_VERTEX -2 -#define BLOSSOM_BASE -1 - -#define ADD_CAPACITY_RADICAL 1 /* add capacity to radical */ - -#define MAX_BOND_EDGE_CAP 2 /* triple bond */ -#define AROM_BOND_EDGE_CAP 1 -#define MAX_TGROUP_EDGE_CAP 2 /* -NH2 provides max. capacity */ - -/* edge to s or t */ -#define EDGE_FLOW_ST_MASK 0x3fff /* mask for flow */ -#define EDGE_FLOW_ST_PATH 0x4000 /* mark: the edge belongs to the augmenting path */ - -/* edges between other vertices */ -/* EdgeFlow defined as S_SHORT; change from S_CHAR made 9-23-2005 */ -#define EDGE_FLOW_MASK 0x3fff /* mask for flow */ -#define EDGE_FLOW_PATH 0x4000 /* mark: the edge belongs to the augmenting path */ - -/*********************************************************************************/ -#if ( ADD_CAPACITY_RADICAL == 1 ) /* { */ -/* -- do not treat triplets as moving dots -- 2004-02-18 -- -#define MAX_AT_FLOW(X) (((X).chem_bonds_valence - (X).valence)+\ - ((is_centerpoint_elem((X).el_number)||get_endpoint_valence((X).el_number))?\ - (((X).radical==RADICAL_DOUBLET)+2*((X).radical==RADICAL_TRIPLET)):0)) -*/ -#define MAX_AT_FLOW(X) (((X).chem_bonds_valence - (X).valence)+\ - ((is_centerpoint_elem((X).el_number)||get_endpoint_valence((X).el_number))?\ - (((X).radical==RADICAL_DOUBLET)/*+2*((X).radical==RADICAL_TRIPLET)*/):0)) - - -#else /* } ADD_CAPACITY_RADICAL { */ - -#define MAX_AT_FLOW(X) (((X).chem_bonds_valence - (X).valence) - -#endif /* } ADD_CAPACITY_RADICAL */ - -/**************************** BNS_EDGE ************************************/ -typedef struct BnsEdge { - AT_NUMB neighbor1; /* the smaller neighbor */ - AT_NUMB neighbor12; /* neighbor1 ^ neighbor2 */ - AT_NUMB neigh_ord[2]; /* ordering number of the neighbor: [0]: atneighbor */ - EdgeFlow cap; /* Edge capacity */ - EdgeFlow cap0; /* Initial edge capacity */ - EdgeFlow flow; /* Edge flow */ - EdgeFlow flow0; /* Initial flow */ - /*S_CHAR delta; */ - S_CHAR pass; /* number of times changed in AugmentEdge() */ - S_CHAR forbidden; -} BNS_EDGE; - -/**************************** BNS_ST_EDGE ************************************/ -typedef struct BnsStEdge { - VertexFlow cap; /* Edge capacity */ - VertexFlow cap0; /* Initial edge capacity */ - VertexFlow flow; /* Edge flow */ - VertexFlow flow0; /* Initial edge flow */ - S_CHAR pass; /* number of times changed in AugmentEdge() */ - /*S_CHAR delta; */ -} BNS_ST_EDGE; - -/**************************** BNS_VERTEX ************************************/ -typedef struct BnsVertex { - - BNS_ST_EDGE st_edge; /* 0,1 capacity and flow of the edge to s or t */ - AT_NUMB type; /* 2, atom, t-group, or added atom: BNS_VERT_TYPE_TGROUP, etc. */ - AT_NUMB num_adj_edges; /* 3, actual number of neighbors incl. t-groups, excl. s or t */ - AT_NUMB max_adj_edges; /* 4, including reserved */ - /*S_CHAR path_neigh[2];*/ /* 5 found path information */ - /* indexes of Edges */ - BNS_IEDGE *iedge; /* 6 a pointer to the array of edge indexes adjacent to this vertex */ -}BNS_VERTEX; - -/**************************** BNS_ALT_PATH ************************************/ -typedef union BnsAltPath { - VertexFlow flow[2]; - Vertex number; - AT_NUMB ineigh[2]; -} BNS_ALT_PATH; - -/**************************** BN_STRUCT ************************************/ -typedef struct BalancedNetworkStructure { - - int num_atoms; /* number of real atoms */ - /*int len_atoms; */ /* size of filled with real atoms portion of the BNS_VERTEX data */ - int num_added_atoms; /* number of added fictitious atoms */ - int nMaxAddAtoms; /* max. number of atoms to add (not including t-groups) */ - int num_c_groups; /* number of added c-groups */ - int num_t_groups; /* number of added t-groups */ - int num_vertices; /* total number currently in effect; includes t-groups and added atoms */ - /*int len_vertices; */ /* allocation size for BNS_VERTEX data */ - int num_bonds; /* number of real bonds/2 = number of edges between real atoms */ - int num_edges; /* number of currently in effect */ - int num_iedges; /* added 9-16-2005; used only in InChI Reversing */ - int num_added_edges; /* number of added edges (not including edges to t-groups) */ - int nMaxAddEdges; /* max. number edges of add to each atom (not including edges to t-groups) */ - - int max_vertices; /* allocation size for BNS_VERTEX structures */ - int max_edges; /* allocation size for edge[]; iedge has length 2*max_edges */ - int max_iedges; /* allocation size for iedge */ - - int tot_st_cap; /* not used, only calculated */ - int tot_st_flow; /* not used, only calculated */ - - int len_alt_path; /* length of alt_path[] */ - - int bNotASimplePath; /* alternating path traversed same bond 2 times in opposite directions */ - int bChangeFlow; /* actually change flow */ - - BNS_VERTEX *vert; /* vertices */ - BNS_EDGE *edge; /* edges */ - BNS_IEDGE *iedge; - BNS_ALT_PATH *alt_path; /* current altp[] element */ - BNS_ALT_PATH *altp[BN_MAX_ALTP]; /* keep alt. paths */ - - int max_altp; - int num_altp; - - INCHI_MODE *pbTautFlags; /* carry it through all functions; never NULL */ - INCHI_MODE *pbTautFlagsDone; /* carry it through all functions; never NULL */ - AT_NUMB type_TACN; /* BNS_VERT_TYPE_ACID: if non-zero than only for it path type_T-type_TACN-type_CN allowed */ - AT_NUMB type_T; /* BNS_VERT_TYPE_TGROUP */ - AT_NUMB type_CN; /* BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE */ - S_CHAR edge_forbidden_mask; - -} BN_STRUCT; - -/********************* BN_DATA *******************************************/ -typedef enum tagBnsRadSrchMode { - RAD_SRCH_NORM = 0, /* normal search for normalization */ - RAD_SRCH_FROM_FICT = 1 /* search from fict. vertices to atoms */ -} BRS_MODE; -typedef struct BalancedNetworkData { - Vertex *BasePtr; /*[MAX_VERTEX]; pointer toward the base of C(v) */ - Edge *SwitchEdge; /*[MAX_VERTEX]; a pair of vertices and an edge, implemented here as [*][2] array */ - S_CHAR *Tree; /*[MAX_VERTEX]; indicates presence in ScanQ, T, T', s-reachability */ - Vertex *ScanQ; /*[MAX_VERTEX]; contains the set S of s-reachable vertices */ - int QSize; /* index of the last element added to ScanQ */ - Vertex *Pu; /*[MAX_VERTEX/2+1] */ - Vertex *Pv; /*[MAX_VERTEX/2+1] */ - int max_num_vertices; /* allocation size of all except Pu, Pv */ - int max_len_Pu_Pv; /* allocation size of Pu and Pv */ -#if ( BNS_RAD_SEARCH == 1 ) - Vertex *RadEndpoints; /*[MAX_VERTEX*/ - int nNumRadEndpoints; - EdgeIndex *RadEdges; - int nNumRadEdges; - int nNumRadicals; - BRS_MODE bRadSrchMode; /* 1 => connect fict. vertices-radicals to the accessible atoms */ -#endif -} BN_DATA; - -/* internal array size */ -#define MAX_ALT_AATG_ARRAY_LEN 127 -/* detected endpoint markings */ -#define AATG_MARK_IN_PATH 1 /* atom in path detected by the BNS */ -#define AATG_MARK_WAS_IN_PATH 2 /* found to be in path before next level */ -/* output */ -#define AATG_MARK_MAIN_TYPE 4 /* atom O-"salt" */ -#define AATG_MARK_OTHER_TYPE 8 /* other atom to be tested */ - -struct tagTautomerGroupsInfo; /* forward declaration */ - -/******************** atoms in alt path through taut group ****************/ -typedef struct BN_AtomsAtTautGroup { - int nAllocLen; - int nNumFound; - int nNumMainAdj2Tgroup; - int nNumOthersAdj2Tgroup; - AT_NUMB *nEndPoint; /* original t-group number */ - S_CHAR *nMarkedAtom; /* atom mark, see AATG_MARK_* */ - int *nAtTypeTotals; - struct tagTautomerGroupsInfo *t_group_info; -} BN_AATG; - - -/************ store changes in flow and capacity to test a bond ****************/ - -typedef struct tagBNS_FLOW_CHANGES { - BNS_IEDGE iedge; - EdgeFlow flow; - EdgeFlow cap; - Vertex v1; - VertexFlow cap_st1; - VertexFlow flow_st1; - Vertex v2; - VertexFlow cap_st2; - VertexFlow flow_st2; -} BNS_FLOW_CHANGES; - - -#define ALT_PATH_MODE_TAUTOM 1 -#define ALT_PATH_MODE_CHARGE 2 -#define ALT_PATH_MODE_4_SALT 3 /* mark alt bonds along the path */ -#define ALT_PATH_MODE_4_SALT2 4 /* mark alt bonds along the path, path to taut. group fict. vertex if exists */ -#define ALT_PATH_MODE_REM2H_CHG 5 /* remove 2 H along alt. path AH-=-BH => A=-=B and change bonds to alternating */ -#define ALT_PATH_MODE_ADD2H_CHG 6 /* add 2 H along alt. path A=-=B => AH-=-BH and change bonds to alternating */ -#define ALT_PATH_MODE_REM2H_TST 7 /* test-remove 2 H along alt. path AH-=-BH => A=-=B; restore changed bonds */ -#define ALT_PATH_MODE_ADD2H_TST 8 /* test-add 2 H along alt. path A=-=B => AH-=-BH; restore changed bonds */ -#define ALT_PATH_MODE_REM_PROTON 9 /* remove proton, adjust bonds, charges, H-counts 2004-03-05 */ -#if ( KETO_ENOL_TAUT == 1 ) -#define ALT_PATH_MODE_TAUTOM_KET 10 /* same as ALT_PATH_MODE_TAUTOM, applies to C=-OH or CH-=O; H may be (-) */ -#endif - -typedef U_SHORT bitWord; -#define BIT_WORD_MASK ((bitWord)~0) - -typedef struct tagNodeSet { - bitWord **bitword; - int num_set; /* number of sets */ - int len_set; /* number of bitWords in each set */ -} NodeSet; - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - -/********************************************************************************* - bChangeFlow: - 1 => change flow inside the BNS search - 3 => change flow inside the BNS search and undo the flow change in the BNS structure here - 4 => change bonds in the structure according to the flow - 8 => make altern. bonds in the structure - - Note: (bChangeFlow & 1) == 1 is needed for multiple runs -**********************************************************************************/ - -/* "EF" = "Edge Flow" */ -#define BNS_EF_CHNG_FLOW 1 /* change Balanced Network (BN) flow inside the BNS search */ -#define BNS_EF_RSTR_FLOW 2 /* undo BN flow changes after BNS */ -#define BNS_EF_CHNG_RSTR (BNS_EF_CHNG_FLOW | BNS_EF_RSTR_FLOW) -#define BNS_EF_CHNG_BONDS 4 /* change bonds in the structure according to the BN flow */ -#define BNS_EF_ALTR_BONDS 8 /* make altern. bonds in the structure if the flow has changed */ -#define BNS_EF_UPD_RAD_ORI 16 /* update BN flow0 & Atom radical values: - flow0 := flow, radical:=st_cap - st_flow */ -#define BNS_EF_SET_NOSTEREO 32 /* in combination with BNS_EF_ALTR_BONDS only: - ALT12 bond cannot be stereogenic */ -#define BNS_EF_UPD_H_CHARGE 64 /* update charges and H-counts according to change flow to c- and t-group vertices */ - -#define BNS_EF_SAVE_ALL (BNS_EF_CHNG_FLOW | BNS_EF_CHNG_BONDS | BNS_EF_UPD_RAD_ORI) -#define BNS_EF_ALTR_NS (BNS_EF_ALTR_BONDS | BNS_EF_SET_NOSTEREO) - -#define BNS_EF_RAD_SRCH 128 /* search for rafical paths closures */ - - - -int SetBitCreate( void ); -int NodeSetCreate( NodeSet *pSet, int n, int L ); -void NodeSetFree( NodeSet *pSet ); - -int IsNodeSetEmpty( NodeSet *cur_nodes, int k); -int DoNodeSetsIntersect( NodeSet *cur_nodes, int k1, int k2); -void AddNodeSet2ToNodeSet1( NodeSet *cur_nodes, int k1, int k2); -void NodeSetFromRadEndpoints( NodeSet *cur_nodes, int k, /*Node *v*/ Vertex RadEndpoints[], int num_v); -void RemoveFromNodeSet( NodeSet *cur_nodes, int k, Vertex v[], int num_v); -int AddNodesToRadEndpoints( NodeSet *cur_nodes, int k, Vertex RadEndpoints[], Vertex vRad, int nStart, int nLen ); - - -int nExists2AtMoveAltPath( struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, - struct BN_AtomsAtTautGroup *pAATG, inp_ATOM *at, int num_atoms, - int jj2, int jj1, struct tagSaltChargeCandidate *s_candidate, int nNumCandidates, - AT_NUMB *nForbiddenAtom, int nNumForbiddenAtoms); -int bExistsAltPath( struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, - struct BN_AtomsAtTautGroup *pAATG, inp_ATOM *at, int num_atoms, int nVertDoubleBond, int nVertSingleBond, int path_type ); -int bExistsAnyAltPath( struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, - inp_ATOM *at, int num_atoms, int nVertDoubleBond, int nVertSingleBond, int path_type ); -int AddTGroups2BnStruct( struct BalancedNetworkStructure *pBNS, inp_ATOM *at, int num_atoms, - struct tagTautomerGroupsInfo *tgi ); -int AddSuperTGroup2BnStruct( struct BalancedNetworkStructure *pBNS, inp_ATOM *at, int num_atoms, - struct tagTautomerGroupsInfo *tgi ); -int AddCGroups2BnStruct( struct BalancedNetworkStructure *pBNS, inp_ATOM *at, int num_atoms, - struct tagChargeGroupsInfo *cgi ); - -int ReInitBnStruct( struct BalancedNetworkStructure *pBNS, inp_ATOM *at, int num_at, int bRemoveGroupsFromAtoms ); -int ReInitBnStructAddGroups( struct BalancedNetworkStructure *pBNS, inp_ATOM *at, int num_atoms, - struct tagTautomerGroupsInfo *tgi, struct tagChargeGroupsInfo *cgi ); - - -int DisconnectTestAtomFromTGroup( struct BalancedNetworkStructure *pBNS, int v1, int *pv2, BNS_FLOW_CHANGES *fcd ); -int DisconnectTGroupFromSuperTGroup( struct BalancedNetworkStructure *pBNS, int v1, int *pv1, int *pv2, - BNS_FLOW_CHANGES *fcd ); -int ReconnectTestAtomToTGroup( struct BalancedNetworkStructure *pBNS, int v1, int v2, int ie, BNS_FLOW_CHANGES *fcd ); - -int bIsHardRemHCandidate( inp_ATOM *at, int i, int *cSubType ); - -/* moved from ichi_bns.c 2005-08-23 */ -int RunBalancedNetworkSearch( BN_STRUCT *pBNS, BN_DATA *pBD, int bChangeFlow ); -BN_STRUCT* AllocateAndInitBnStruct( inp_ATOM *at, int num_atoms, int nMaxAddAtoms, int nMaxAddEdges, int max_altp, int *num_changed_bonds ); -BN_STRUCT* DeAllocateBnStruct( BN_STRUCT *pBNS ); -int ReInitBnStructAltPaths( BN_STRUCT *pBNS ); -int ReInitBnStructForMoveableAltBondTest( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms ); -void ClearAllBnDataVertices( Vertex *v, Vertex value, int size ); -void ClearAllBnDataEdges( Edge *e, Vertex value, int size ); -BN_DATA *DeAllocateBnData( BN_DATA *pBD ); -BN_DATA *AllocateAndInitBnData( int max_num_vertices ); -int ReInitBnData( BN_DATA *pBD ); -int SetForbiddenEdges( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int edge_forbidden_mask ); -/* main function: find augmenting path */ -int BalancedNetworkSearch ( BN_STRUCT* pBNS, BN_DATA *pBD, int bChangeFlow ); - -int SetRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode ); -int SetRadEndpoints2( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode ); - -int RemoveRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at ); - -int AddRemoveProtonsRestr( inp_ATOM *at, int num_atoms, int *num_protons_to_add, - int nNumProtAddedByRestr, INCHI_MODE bNormalizationFlags, - int num_tg, int nChargeRevrs, int nChargeInChI ); -int AddRemoveIsoProtonsRestr( inp_ATOM *at, int num_atoms, NUM_H num_protons_to_add[], int num_tg ); - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /* __INCHI_BNS_H___ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichi_io.h b/INCHI-1-SRC/INCHI_API/inchi_dll/ichi_io.h deleted file mode 100644 index 40e3771..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichi_io.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - - INCHI_IOSTREAM OPERATIONS - - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - -void inchi_ios_init(INCHI_IOSTREAM *ios, int io_type, FILE *f); -void inchi_ios_flush(INCHI_IOSTREAM *ios); -void inchi_ios_flush2(INCHI_IOSTREAM *ios, FILE *f2); -void inchi_ios_close(INCHI_IOSTREAM *ios); -void inchi_ios_reset(INCHI_IOSTREAM *ios); - -int inchi_ios_gets( char *szLine, int len, INCHI_IOSTREAM *ios, int *bTooLongLine ); -int inchi_ios_getsTab( char *szLine, int len, INCHI_IOSTREAM *ios, int *bTooLongLine ); -int inchi_ios_getsTab1( char *szLine, int len, INCHI_IOSTREAM *ios, int *bTooLongLine ); - -int inchi_ios_print( INCHI_IOSTREAM *ios, const char* lpszFormat, ... ); -int inchi_ios_print_nodisplay( INCHI_IOSTREAM *ios, const char* lpszFormat, ... ); - -/* Print to string buffer or to file+stderr */ -int inchi_ios_eprint( INCHI_IOSTREAM *ios, const char* lpszFormat, ... ); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - - PLAIN FILE OPERATIONS - - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -/* Print to file, echoing to stderr */ -int inchi_fprintf( FILE* f, const char* lpszFormat, ... ); -int inchi_print_nodisplay( FILE* f, const char* lpszFormat, ... ); - -char* inchi_fgetsLf( char* line, int line_len, FILE* inp ); -int inchi_fgetsLfTab( char *szLine, int len, FILE *f ); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - - - diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichican2.c b/INCHI-1-SRC/INCHI_API/inchi_dll/ichican2.c deleted file mode 100644 index 020bb73..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichican2.c +++ /dev/null @@ -1,5168 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include - -/* #define CHECK_WIN32_VC_HEAP */ -#include "mode.h" - -#include "ichi.h" -#include "util.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "inpdef.h" -#include "ichinorm.h" -#include "ichicant.h" -#include "ichicano.h" -#include "ichicomn.h" - -#include "ichicomp.h" - -#define MAX_CELLS 1024 -#define MAX_NODES 1024 -#define MAX_SET_SIZE 2048 /*16384*/ -#define MAX_LAYERS 7 - -#define INFINITY 0x3FFF -#define EMPTY_CT 0 -#define EMPTY_H_NUMBER (INFINITY-1) -#define BASE_H_NUMBER ((INFINITY-1)/2) -#define EMPTY_ISO_SORT_KEY LONG_MAX - -#define SEPARATE_CANON_CALLS 0 - -/* #define INCHI_CANON_USE_HASH */ -#define INCHI_CANON_MIN - -/****************************************************************/ -#ifdef INCHI_CANON_USE_HASH -typedef unsigned long U_INT_32; -typedef unsigned char U_INT_08; -typedef U_INT_32 CtHash; -CtHash hash_mark_bit; -#endif - -/* -- moved to ichi_bns.h -- -typedef U_SHORT bitWord; -#define BIT_WORD_MASK ((bitWord)~0) -*/ - -static bitWord *bBit = NULL; -static int num_bit = 0; -/*bitWord mark_bit; */ /* highest bit in AT_NUMB */ -/*bitWord mask_bit; */ /* ~mark_bit */ - -AT_NUMB rank_mark_bit; -AT_NUMB rank_mask_bit; - - -typedef AT_NUMB Node; -typedef NEIGH_LIST Graph; -/* -typedef struct tagGraph { - int dummy; -} Graph; -*/ -typedef struct tagUnorderedPartition { - /* AT_NUMB *next; */ /* links */ - AT_NUMB *equ2; /* mcr */ -} UnorderedPartition; - -typedef struct tagCell { - int first; /* index of the first cell element in Partition::AtNumber[] */ - int next; /* next after the last index */ - int prev; /* position of the previously returned cell element */ -} Cell; - -#ifdef NEVER /* moved to ichi_bns.h */ -typedef struct tagNodeSet { - bitWord **bitword; - int num_set; /* number of sets */ - int len_set; /* number of bitWords in each set */ -} NodeSet; -#endif - -typedef struct tagTransposition { - AT_NUMB *nAtNumb; -} Transposition; - - -typedef struct tagCTable { - AT_RANK *Ctbl; /* connection table */ - /* Format-atoms: atom_rank[k] neigh_rank[k][1]...neigh_rank[k][n] - 1) atom_rank[k1] < atom_rank[k2] <=> k1 < k2 - where 2) atom_rank[k] > neigh_rank[k][i], i=1..n - 3) neigh_rank[k][i] < neigh_rank[k][j] <=> i < j - - Format-tgroup: tgroup_rank[k] endpoint_rank[k][1]...endpoint_rank[k][n] - where 1) tgroup_rank[k1] < tgroup_rank[k2] <=> k1 < k2 - 2) endpoint_rank[k][i] < endpoint_rank[k][j] <=> i < j - - Note: tgroup_rank[k] > endpoint_rank[k][j] for all j by construction - */ - - int lenCt; /* used length */ - int nLenCTAtOnly; /* to split Ctnl comparison in case of bDigraph != 0 */ - int maxlenCt; /* allocated length of Ctbl */ - int maxPos; /* allocated length of nextCtblPos */ - int maxVert; /* max number of vertices to separate atoms from taut groups */ - int lenPos; /* first unused element of nextCtblPos */ - AT_RANK *nextAtRank; /* rank (k value) after the last node of the Ctbl portion*/ - AT_NUMB *nextCtblPos; /* first unused element of Ctbl */ - - /* hydrogen atoms fixed in tautomeric representation: - compare before diff sign inversion: (+) <=> Ct1->() > Ct2->() */ - NUM_H *NumH; - int lenNumH; /* used length */ - int maxlenNumH; /* n + T_NUM_NO_ISOTOPIC*(n_tg-n) + 1 */ - - /* hydrogen atoms fixed in non-tautomeric representation only: - compare before diff sign inversion: (+) <=> Ct1->() > Ct2->() */ - NUM_H *NumHfixed; - /*int lenNumHfixed; */ /* used length */ - /*int maxlenNumHfixed; */ /* max length = n+1 */ - - /* isotopic atoms (without tautomeric H) and isotopic tautomeric groups */ - /* note: AT_ISO_SORT_KEY and T_GROUP_ISOWT are identical types: long */ - AT_ISO_SORT_KEY *iso_sort_key; - int len_iso_sort_key; /* used length */ - int maxlen_iso_sort_key; /* max length = n_tg+1 */ - - S_CHAR *iso_exchg_atnos; - int len_iso_exchg_atnos; - int maxlen_iso_exchg_atnos; - - /* isotopic hydrogen atoms fixed in non-tautomeric representation only */ -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - AT_ISO_SORT_KEY *iso_sort_key_Hfixed; - int len_iso_sort_key_Hfixed; /* used length */ - int maxlen_iso_sort_key_Hfixed; /* max length = n+1 */ -#endif - -#ifdef INCHI_CANON_USE_HASH - CtHash *hash; -#endif -} ConTable; -/**************************************************************/ -typedef struct tagkLeast { - int k; - int i; -} kLeast; -/*************local prototypes **********************************/ -int CanonGraph( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ); - -void CtPartFill( Graph *G, CANON_DATA *pCD, Partition *p, - ConTable *Ct, int k, int n, int n_tg ); -void CtPartClear( ConTable *Ct, int k ); -void CtPartInfinity( ConTable *Ct, S_CHAR *cmp, int k ); -int CtPartCompare( ConTable *Ct1, ConTable *Ct2, S_CHAR *cmp, - kLeast *kLeastForLayer, int k, int bOnlyCommon, int bSplitTautCompare ); -int CtFullCompare( ConTable *Ct1, ConTable *Ct2, int bOnlyCommon, int bSplitTautCompare ); -void CtPartCopy( ConTable *Ct1 /* to */, ConTable *Ct2 /* from */, int k ); -void CtFullCopy( ConTable *Ct1, ConTable *Ct2 ); - -int CtFullCompareLayers( kLeast *kLeastForLayer ); -int CtCompareLayersGetFirstDiff( kLeast *kLeast_rho, int nOneAdditionalLayer, - int *L_rho, int *I_rho, int *k_rho ); -int CtPartCompareLayers( kLeast *kLeast_rho, int L_rho_fix_prev, int nOneAdditionalLayer ); -void UpdateCompareLayers( kLeast kLeastForLayer[], int hzz ); -int GetOneAdditionalLayer( CANON_DATA *pCD, ConTable *pzb_rho_fix ); - -void CleanNumH( NUM_H *NumH, int len ); -int CleanCt( AT_RANK *Ct, int len ); -void CleanIsoSortKeys( AT_ISO_SORT_KEY * isk, int len ); -void MergeCleanIsoSortKeys( AT_ISO_SORT_KEY * isk1, AT_ISO_SORT_KEY * isk2, int len ); - -int UnorderedPartitionJoin( UnorderedPartition *p1, UnorderedPartition *p2, int n ); -Node GetUnorderedPartitionMcrNode( UnorderedPartition *p1, Node v ); -int nJoin2Mcrs2( AT_RANK *nEqArray, AT_RANK n1, AT_RANK n2 ); -AT_RANK nGetMcr2( AT_RANK *nEqArray, AT_RANK n ); -int AllNodesAreInSet( NodeSet *cur_nodes, int lcur_nodes, NodeSet *set, int lset ); -void NodeSetFromVertices( NodeSet *cur_nodes, int l, Node *v, int num_v); -void CellMakeEmpty( Cell *baseW, int k ); -Node CellGetMinNode( Partition *p, Cell *W, Node v, CANON_DATA *pCD ); -int CellGetNumberOfNodes( Partition *p, Cell *W ); -int CellIntersectWithSet( Partition *p, Cell *W, NodeSet *Mcr, int l ); - -int PartitionColorVertex( Graph *G, Partition *p, Node v, int n, int n_tg, int n_max, int bDigraph, int nNumPrevRanks ); -void PartitionCopy( Partition *To, Partition *From, int n ); -int PartitionSatisfiesLemma_2_25( Partition *p, int n ); -void PartitionGetTransposition( Partition *pFrom, Partition *pTo, int n, Transposition *gamma ); -void PartitionGetMcrAndFixSet( Partition *p, NodeSet *Mcr, NodeSet *Fix, int n, int l ); -int PartitionGetFirstCell( Partition *p, Cell *baseW, int k, int n ); -int PartitionIsDiscrete( Partition *p, int n); -void PartitionFree( Partition *p ); -int PartitionCreate( Partition *p, int n); -void UnorderedPartitionMakeDiscrete( UnorderedPartition *p, int n); -void UnorderedPartitionFree( UnorderedPartition *p ); -int UnorderedPartitionCreate( UnorderedPartition *p, int n ); -void CTableFree( ConTable *Ct ); -int CTableCreate( ConTable *Ct, int n, CANON_DATA *pCD ); -void TranspositionFree( Transposition *p ); -int TranspositionCreate( Transposition *p, int n ); -void TranspositionGetMcrAndFixSetAndUnorderedPartition( Transposition *gamma, NodeSet *McrSet, NodeSet *FixSet, int n, int l, UnorderedPartition *p ); - -void insertions_sort_NeighList_AT_NUMBERS2( NEIGH_LIST base, AT_RANK *nRank, AT_RANK max_rj ); -int WriteGraph( Graph *G, int n, int gnum, char *fname, char *fmode ); - -int SetInitialRanks2( int num_atoms, ATOM_INVARIANT2* pAtomInvariant2, AT_RANK *nNewRank, AT_RANK *nAtomNumber ); -void FillOutAtomInvariant2( sp_ATOM* at, int num_atoms, int num_at_tg, ATOM_INVARIANT2* pAtomInvariant, - int bIgnoreIsotopic, int bHydrogensInRanks, int bHydrogensFixedInRanks, - int bDigraph, int bTautGroupsOnly, T_GROUP_INFO *t_group_info ); - -int GetCanonRanking2( int num_atoms, int num_at_tg, int num_max, int bDigraph, sp_ATOM* at, - AT_RANK **pRankStack, int nNumPrevRanks, - AT_RANK *nSymmRank, AT_RANK *nCanonRank, - NEIGH_LIST *NeighList, AT_RANK *nTempRank, - CANON_STAT* pCS ); - - -#if ( SEPARATE_CANON_CALLS == 1 ) -/* for profiling purposes */ - -int CanonGraph01( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) -{ - return - CanonGraph( n, n_tg, n_max, bDigraph, G, pi , - nSymmRank, nCanonRank, nAtomNumberCanon, - pCD, pCC, - pp_zb_rho_inp, pp_zb_rho_out ); -} -int CanonGraph02( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) -{ - return - CanonGraph( n, n_tg, n_max, bDigraph, G, pi , - nSymmRank, nCanonRank, nAtomNumberCanon, - pCD, pCC, - pp_zb_rho_inp, pp_zb_rho_out ); -} -int CanonGraph03( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) -{ - return - CanonGraph( n, n_tg, n_max, bDigraph, G, pi , - nSymmRank, nCanonRank, nAtomNumberCanon, - pCD, pCC, - pp_zb_rho_inp, pp_zb_rho_out ); -} -int CanonGraph04( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) -{ - return - CanonGraph( n, n_tg, n_max, bDigraph, G, pi , - nSymmRank, nCanonRank, nAtomNumberCanon, - pCD, pCC, - pp_zb_rho_inp, pp_zb_rho_out ); -} -int CanonGraph05( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) -{ - return - CanonGraph( n, n_tg, n_max, bDigraph, G, pi , - nSymmRank, nCanonRank, nAtomNumberCanon, - pCD, pCC, - pp_zb_rho_inp, pp_zb_rho_out ); -} -int CanonGraph06( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) -{ - return - CanonGraph( n, n_tg, n_max, bDigraph, G, pi , - nSymmRank, nCanonRank, nAtomNumberCanon, - pCD, pCC, - pp_zb_rho_inp, pp_zb_rho_out ); -} -int CanonGraph07( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) -{ - return - CanonGraph( n, n_tg, n_max, bDigraph, G, pi , - nSymmRank, nCanonRank, nAtomNumberCanon, - pCD, pCC, - pp_zb_rho_inp, pp_zb_rho_out ); -} -int CanonGraph08( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) -{ - return - CanonGraph( n, n_tg, n_max, bDigraph, G, pi , - nSymmRank, nCanonRank, nAtomNumberCanon, - pCD, pCC, - pp_zb_rho_inp, pp_zb_rho_out ); -} -int CanonGraph09( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) -{ - return - CanonGraph( n, n_tg, n_max, bDigraph, G, pi , - nSymmRank, nCanonRank, nAtomNumberCanon, - pCD, pCC, - pp_zb_rho_inp, pp_zb_rho_out ); -} -int CanonGraph10( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) -{ - return - CanonGraph( n, n_tg, n_max, bDigraph, G, pi , - nSymmRank, nCanonRank, nAtomNumberCanon, - pCD, pCC, - pp_zb_rho_inp, pp_zb_rho_out ); -} -int CanonGraph11( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) -{ - return - CanonGraph( n, n_tg, n_max, bDigraph, G, pi , - nSymmRank, nCanonRank, nAtomNumberCanon, - pCD, pCC, - pp_zb_rho_inp, pp_zb_rho_out ); -} -int CanonGraph12( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) -{ - return - CanonGraph( n, n_tg, n_max, bDigraph, G, pi , - nSymmRank, nCanonRank, nAtomNumberCanon, - pCD, pCC, - pp_zb_rho_inp, pp_zb_rho_out ); -} -#else - -#define CanonGraph01 CanonGraph -#define CanonGraph02 CanonGraph -#define CanonGraph03 CanonGraph -#define CanonGraph04 CanonGraph -#define CanonGraph05 CanonGraph -#define CanonGraph06 CanonGraph -#define CanonGraph07 CanonGraph -#define CanonGraph08 CanonGraph -#define CanonGraph09 CanonGraph -#define CanonGraph10 CanonGraph -#define CanonGraph11 CanonGraph -#define CanonGraph12 CanonGraph - -#endif - -#ifdef INCHI_CANON_USE_HASH -/****************************************************************/ -static call_fill_crc32_data = 1; -static U_INT_32 crc32_data[256]; - -void fill_crc32_data() -{ - U_INT_32 c; - int n, k; - - for (n = 0; n < 256; n++) - { - c = (U_INT_32)n; - for (k = 0; k < 8; k++) - c = c & 1 ? 0xEDB88320L ^ (c >> 1) : c >> 1; - crc32_data[n] = c; - } - call_fill_crc32_data = 0; -} -/****************************************************************/ -unsigned long add2crc32( unsigned long crc32, AT_NUMB n ) -{ - U_INT_08 chr; - if (call_fill_crc32_data) { - fill_crc32_data(); - } - chr = n % 128; - crc32 = crc32_data[((int)crc32 ^ (int)chr) & 0xff] ^ (crc32 >> 8); - chr = n / 128; - crc32 = crc32_data[((int)crc32 ^ (int)chr) & 0xff] ^ (crc32 >> 8); - return crc32; -} -#endif -/****************************************************************/ -int TranspositionCreate( Transposition *p, int n ) -{ - p->nAtNumb = (AT_NUMB*)inchi_calloc( n, sizeof(p->nAtNumb[0]) ); - if ( p->nAtNumb ) { - return 1; - } - return 0; -} -/****************************************************************/ -void TranspositionFree( Transposition *p ) -{ - if ( p && p->nAtNumb ) { - inchi_free( p->nAtNumb ); - p->nAtNumb = NULL; - } -} -/****************************************************************/ -int NodeSetCreate( NodeSet *pSet, int n, int L ) -{ - int i, len; - - len = (n+ num_bit - 1)/num_bit; - - pSet->bitword = (bitWord**)inchi_calloc(L, sizeof(pSet->bitword[0])); - - if ( !pSet->bitword ) { - return 0; - } - pSet->bitword[0] = (bitWord*)inchi_calloc(len*L, sizeof(pSet->bitword[0][0])); - if ( !pSet->bitword[0] ) { - /* cleanup */ - inchi_free( pSet->bitword ); - pSet->bitword = NULL; - return 0; /* failed */ - } - for ( i = 1; i < L; i ++ ) { - pSet->bitword[i] = pSet->bitword[i-1]+len; - } - pSet->len_set = len; - pSet->num_set = L; - return 1; -} -/****************************************************************/ -void NodeSetFree( NodeSet *pSet ) -{ - if ( pSet && pSet->bitword ) { - if ( pSet->bitword[0] ) { - inchi_free( pSet->bitword[0] ); - } - inchi_free( pSet->bitword ); - pSet->bitword = NULL; - } -} -/****************************************************************/ -int CTableCreate( ConTable *Ct, int n, CANON_DATA *pCD ) -{ - int maxlenCt = pCD->nMaxLenLinearCT + 1; /* add one element for CtPartInfinity() */ - int maxlenNumH = pCD->NumH? (pCD->maxlenNumH + 1) : 0; - int maxlenNumHfixed = pCD->NumHfixed? (pCD->maxlenNumHfixed + 1) : 0; - int maxlenIso = pCD->maxlen_iso_sort_key? (pCD->maxlen_iso_sort_key+1) : 0; - int maxlenIsoExchg = pCD->iso_exchg_atnos? (pCD->maxlen_iso_exchg_atnos+1) : 0; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - int maxlenIsoHfixed = pCD->maxlen_iso_sort_key_Hfixed? (pCD->maxlen_iso_sort_key_Hfixed+1):0; -#endif - - memset( Ct, 0, sizeof(Ct[0]) ); - - Ct->maxVert = n; - - n ++; - - Ct->Ctbl = (AT_RANK*) inchi_calloc(maxlenCt, sizeof(Ct->Ctbl[0]) ); - Ct->nextCtblPos = (AT_NUMB*) inchi_calloc(n, sizeof(Ct->nextCtblPos[0]) ); - Ct->nextAtRank = (AT_RANK*) inchi_calloc(n, sizeof(Ct->nextAtRank[0]) ); - if ( maxlenNumH ) { - Ct->NumH = (NUM_H *) inchi_calloc(maxlenNumH, sizeof(Ct->NumH[0])); - } - if ( maxlenNumHfixed ) { - Ct->NumHfixed = (NUM_H *) inchi_calloc(maxlenNumHfixed, sizeof(Ct->NumH[0])); - } - if ( maxlenIso ) { - Ct->iso_sort_key = (AT_ISO_SORT_KEY *)inchi_calloc(maxlenIso, sizeof(Ct->iso_sort_key[0])); - } - if ( maxlenIsoExchg ) { - Ct->iso_exchg_atnos = (S_CHAR *)inchi_calloc( maxlenIsoExchg, sizeof(Ct->iso_exchg_atnos[0])); - } -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - if ( maxlenIsoHfixed ) { - Ct->iso_sort_key_Hfixed = (AT_ISO_SORT_KEY *)inchi_calloc(maxlenIsoHfixed, sizeof(Ct->iso_sort_key_Hfixed[0])); - } -#endif -#ifdef INCHI_CANON_USE_HASH - Ct->hash = (CtHash*) inchi_calloc(n, sizeof(Ct->hash[0]) ); -#endif - Ct->lenCt = 0; - Ct->nLenCTAtOnly = pCD->nLenCTAtOnly; - Ct->maxlenCt = maxlenCt; - Ct->lenNumH = 0; - Ct->maxlenNumH = maxlenNumH; - Ct->len_iso_sort_key = 0; - Ct->maxlen_iso_sort_key = maxlenIso; - Ct->len_iso_exchg_atnos = 0; - Ct->maxlen_iso_exchg_atnos = maxlenIso; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - Ct->len_iso_sort_key_Hfixed = 0; - Ct->maxlen_iso_sort_key_Hfixed = maxlenIsoHfixed; -#endif - Ct->maxPos = n; - Ct->lenPos = 0; - Ct->nextAtRank[0] = 0; - Ct->nextCtblPos[0] = 0; - if ( Ct->Ctbl && Ct->nextCtblPos && - (!maxlenNumH || Ct->NumH) && - (!maxlenNumHfixed || Ct->NumHfixed ) ) { - return 1; - } - return 0; -} -/****************************************************************/ -void CTableFree( ConTable *Ct ) -{ - if ( Ct ) { - if ( Ct->Ctbl ) - inchi_free( Ct->Ctbl ); - if ( Ct->nextCtblPos ) - inchi_free( Ct->nextCtblPos ); - if ( Ct->nextAtRank ) - inchi_free( Ct->nextAtRank ); - if ( Ct->NumH ) - inchi_free( Ct->NumH ); - if ( Ct->NumHfixed ) - inchi_free( Ct->NumHfixed ); - if ( Ct->iso_sort_key ) - inchi_free( Ct->iso_sort_key ); - if ( Ct->iso_exchg_atnos ) - inchi_free( Ct->iso_exchg_atnos ); -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - if ( Ct->iso_sort_key_Hfixed ) - inchi_free( Ct->iso_sort_key_Hfixed ); -#endif -#ifdef INCHI_CANON_USE_HASH - if ( Ct->hash ) - inchi_free( Ct->hash ); -#endif - memset( Ct, 0, sizeof( Ct[0] ) ); - } -} -/****************************************************************/ -int UnorderedPartitionCreate( UnorderedPartition *p, int n ) -{ - p->equ2 = (AT_NUMB*)inchi_calloc( n, sizeof(p->equ2[0])); - /* p->next = (AT_NUMB*)inchi_calloc( n, sizeof(p->next[0])); */ - if ( p->equ2 /*&& p->next*/ ) - return 1; - return 0; -} -/****************************************************************/ -void UnorderedPartitionFree( UnorderedPartition *p ) -{ - if (p->equ2) inchi_free(p->equ2); - /* if (p->next) inchi_free(p->next); */ - p->equ2 = NULL; - /* p->next = NULL; */ -} -/****************************************************************/ -void UnorderedPartitionMakeDiscrete( UnorderedPartition *p, int n) -{ - int i; - for ( i = 0; i < n; i ++ ) { - p->equ2[i] = (AT_NUMB)i; - /* p->next[i] = INFINITY; */ - } - INCHI_HEAPCHK -} -/****************************************************************/ -int PartitionCreate( Partition *p, int n) -{ - p->AtNumber = (AT_NUMB*)inchi_calloc( n, sizeof(p->AtNumber[0])); - p->Rank = (AT_RANK*)inchi_calloc( n, sizeof(p->Rank[0])); - if ( p->AtNumber && p->Rank ) { - return 1; - } - return 0; -} -/****************************************************************/ -void PartitionFree( Partition *p ) -{ - if ( p ) { - if ( p->AtNumber ) { - inchi_free( p->AtNumber ); - p->AtNumber = NULL; - } - if ( p->Rank ) { - inchi_free( p->Rank ); - p->Rank = NULL; - } - } -} -/****************************************************************/ -int PartitionIsDiscrete( Partition *p, int n) -{ - int i; - AT_RANK r; - for ( i = 0, r = 1; i < n; i ++, r ++ ) { - if ( r != (rank_mask_bit & p->Rank[p->AtNumber[i]]) ) { - INCHI_HEAPCHK - return 0; - } - } - INCHI_HEAPCHK - return 1; -} -/****************************************************************/ -int PartitionGetFirstCell( Partition *p, Cell *baseW, int k, int n ) -{ - int i; - AT_RANK r; - Cell *W = baseW+k-1; - - i = (k > 1)? baseW[k-2].first+1 : 0; - if ( i < n ) { - /* bypass single vertex cells */ - for ( r = (AT_RANK)(i+1); i < n && r == (rank_mask_bit & p->Rank[(int)p->AtNumber[i]]); i ++, r++ ) - ; - } - if ( i < n ) { - W->first = i; - for ( r = (rank_mask_bit & p->Rank[(int)p->AtNumber[i]]), i++ ; - i < n && r == (rank_mask_bit & p->Rank[(int)p->AtNumber[i]]); - i ++ ) - ; - W->next = i; - INCHI_HEAPCHK - return (W->next - W->first); - } - W->first = INFINITY; - W->next = 0; - INCHI_HEAPCHK - return 0; -} -/****************************************************************/ -void CellMakeEmpty( Cell *baseW, int k ) -{ - k --; - baseW[k].first = INFINITY; - baseW[k].next = 0; - baseW[k].prev = -1; - INCHI_HEAPCHK -} -/****************************************************************/ -void NodeSetFromVertices( NodeSet *cur_nodes, int l, Node *v, int num_v) -{ - bitWord *Bits = cur_nodes->bitword[l-1]; - int len = cur_nodes->len_set*sizeof(bitWord); - int i, j; - - memset( Bits, 0, len ); - - for ( i = 0; i < num_v; i ++ ) { - j = (int)v[i]-1; - Bits[ j / num_bit ] |= bBit[ j % num_bit ]; - } - INCHI_HEAPCHK -} -/****************************************************************/ -int AllNodesAreInSet( NodeSet *cur_nodes, int lcur_nodes, NodeSet *set, int lset ) -{ - int i; - int n = cur_nodes->len_set; - bitWord *BitsNode = cur_nodes->bitword[lcur_nodes-1]; - bitWord *BitsSet = set->bitword[lset-1]; - /* find any BitsNode[i] bit not in BitsSet[i] */ - for ( i = 0; i < n; i ++ ) { - if ( BitsNode[i] & ~BitsSet[i] ) { - INCHI_HEAPCHK - return 0; - } - } - INCHI_HEAPCHK - return 1; -} -/****************************************************************/ -void PartitionGetMcrAndFixSet( Partition *p, NodeSet *Mcr, NodeSet *Fix, int n, int l ) -{ - int i, j1, j2; - AT_RANK r, r1; - bitWord *McrBits = Mcr->bitword[l-1]; - bitWord *FixBits = Fix->bitword[l-1]; - int len = Mcr->len_set*sizeof(bitWord); - - memset( McrBits, 0, len ); - memset( FixBits, 0, len ); - for ( i = 0, r = 1; i < n; i ++, r ++ ) { - if ( r == (r1=(rank_mask_bit&p->Rank[j1=(int)p->AtNumber[i]])) ) { - FixBits[j1 / num_bit] |= bBit[j1 % num_bit]; - McrBits[j1 / num_bit] |= bBit[j1 % num_bit]; - } else { - for ( r = r1; i+1 < n && r == (rank_mask_bit&p->Rank[j2=(int)p->AtNumber[i+1]]); i ++ ) { - if ( j1 > j2 ) { - j1 = j2; - } - } - McrBits[j1 / num_bit] |= bBit[j1 % num_bit]; - } - } - INCHI_HEAPCHK -} -/************* used in ichi_bns.c ********************************/ -void NodeSetFromRadEndpoints( NodeSet *cur_nodes, int k, /*Node *v*/ Vertex RadEndpoints[], int num_v) -{ - bitWord *Bits = cur_nodes->bitword[k]; - int len = cur_nodes->len_set*sizeof(bitWord); - int i, j; - - memset( Bits, 0, len ); - - for ( i = 1; i < num_v; i += 2 ) { - j = (int)RadEndpoints[i]; - Bits[ j / num_bit ] |= bBit[ j % num_bit ]; - } -} -/************* used in ichi_bns.c ********************************/ -void RemoveFromNodeSet( NodeSet *cur_nodes, int k, Vertex v[], int num_v) -{ - if ( cur_nodes->bitword ) { - bitWord *Bits = cur_nodes->bitword[k]; - /*int len = cur_nodes->len_set*sizeof(bitWord);*/ - int i, j; - - for ( i = 0; i < num_v; i ++ ) { - j = (int) v[i]; - Bits[ j / num_bit ] &= ~bBit[ j % num_bit ]; - } - } -} -/************* used in ichi_bns.c ********************************/ -int DoNodeSetsIntersect( NodeSet *cur_nodes, int k1, int k2) -{ - if ( cur_nodes->bitword ) { - bitWord *Bits1 = cur_nodes->bitword[k1]; - bitWord *Bits2 = cur_nodes->bitword[k2]; - int len = cur_nodes->len_set; - int i; - - for ( i = 0; i < len; i ++ ) { - if ( Bits1[i] & Bits2[i] ) - return 1; - } - } - return 0; -} -/************* used in ichi_bns.c ********************************/ -int IsNodeSetEmpty( NodeSet *cur_nodes, int k) -{ - if ( cur_nodes->bitword ) { - bitWord *Bits = cur_nodes->bitword[k]; - int len = cur_nodes->len_set; - int i; - - for ( i = 0; i < len; i ++ ) { - if ( Bits[i] ) - return 0; - } - } - return 1; -} -/************* used in ichi_bns.c ********************************/ -void AddNodeSet2ToNodeSet1( NodeSet *cur_nodes, int k1, int k2) -{ - if ( cur_nodes->bitword ) { - bitWord *Bits1 = cur_nodes->bitword[k1]; - bitWord *Bits2 = cur_nodes->bitword[k2]; - int len = cur_nodes->len_set; - int i; - - for ( i = 0; i < len; i ++ ) { - Bits1[i] |= Bits2[i]; - } - } -} -/************* used in ichi_bns.c ********************************/ -int AddNodesToRadEndpoints( NodeSet *cur_nodes, int k, Vertex RadEndpoints[], Vertex vRad, int nStart, int nLen ) -{ - int n = nStart; - if ( cur_nodes->bitword ) { - bitWord *Bits = cur_nodes->bitword[k]; - int len = cur_nodes->len_set; - int i, j; - Vertex v; - - for ( i = 0, v = 0; i < len; i ++ ) { - if ( Bits[i] ) { - for ( j = 0; j < num_bit; j ++, v ++ ) { - if ( Bits[i] & bBit[j] ) { - if ( n >= nLen ) { - return -1; /* overflow */ - } - RadEndpoints[n ++] = vRad; - RadEndpoints[n ++] = v; - } - } - } else { - v += num_bit; - } - } - } - return n; -} -/****************************************************************/ -void PartitionGetTransposition( Partition *pFrom, Partition *pTo, int n, Transposition *gamma ) -{ - int i; - for ( i = 0; i < n; i ++ ) { - gamma->nAtNumb[(int)pFrom->AtNumber[i]] =pTo->AtNumber[i]; - } - INCHI_HEAPCHK -} -/**************************************************************************************/ -/* Get minimal set (class) representative and partially compress the partitioning */ -/* mcr = minimal class representative. */ -AT_RANK nGetMcr2( AT_RANK *nEqArray, AT_RANK n ) -{ - AT_RANK n1, n2, mcr; /* recursive version is much shorter. */ - INCHI_HEAPCHK - n1=nEqArray[(int)n]; - if ( n == n1 ) { - return n; - } - /* 1st pass: find mcr */ - while ( n1 != (n2=nEqArray[(int)n1])) { - n1 = n2; - } - /* 2nd pass: copy mcr to each element of the set starting from nEqArray[n] */ - mcr = n1; - n1 = n; - while ( /*n1*/ mcr != (n2=nEqArray[(int)n1]) ) { - nEqArray[(int)n1]=mcr; - n1 = n2; - } - INCHI_HEAPCHK - return ( mcr ); -} -/**************************************************************************************/ -/* Join 2 sets (classes) that have members n1 and n2 */ -int nJoin2Mcrs2( AT_RANK *nEqArray, AT_RANK n1, AT_RANK n2 ) -{ - n1 = nGetMcr2( nEqArray, n1 ); - n2 = nGetMcr2( nEqArray, n2 ); - if ( n1 < n2 ) { - nEqArray[n2] = n1; - INCHI_HEAPCHK - return 1; /* a change has been made */ - } - if ( n2 < n1 ) { - nEqArray[n1] = n2; - INCHI_HEAPCHK - return 1; /* a change has been made */ - } - INCHI_HEAPCHK - return 0; /* no changes */ -} -/****************************************************************/ -Node GetUnorderedPartitionMcrNode( UnorderedPartition *p1, Node v ) -{ - Node ret = (Node)(1+ nGetMcr2( p1->equ2, (AT_RANK)(v-1) )); - INCHI_HEAPCHK - return ret; -} -/****************************************************************/ -/* change p2 to (p2 v p1) */ -int UnorderedPartitionJoin( UnorderedPartition *p1, UnorderedPartition *p2, int n ) -{ - int i, j; - int nNumChanges = 0; - for ( i = 0; i < n; i ++ ) { - if ( (j=(int)p1->equ2[i]) == i || p2->equ2[(int)i] == p2->equ2[(int)j] ) { - continue; - } - nNumChanges += nJoin2Mcrs2(p2->equ2, (AT_NUMB)i, (AT_NUMB)j ); - } - INCHI_HEAPCHK - return nNumChanges; -} -/****************************************************************/ -int PartitionSatisfiesLemma_2_25( Partition *p, int n ) -{ - int nPartitionSize = 0; - int nNumNonTrivialCells = 0; - AT_RANK r; - int i, num; - for ( i = num = 0, r=1; i < n; i ++, r++ ) { - if ( (rank_mask_bit & p->Rank[(int)p->AtNumber[i]]) == r ) { - nPartitionSize ++; - if ( num ) { - /* num+1 = cell size > 1 */ - nNumNonTrivialCells ++; - num = 0; - } - } else { - num ++; - } - } - /* check Lemma_2_25 conditions */ - if ( n <= nPartitionSize+4 || - n == nPartitionSize + nNumNonTrivialCells || - n == nPartitionSize + nNumNonTrivialCells + 1 ) { - return 1; - } - return 0; -} -/****************************************************************/ -void PartitionCopy( Partition *To, Partition *From, int n ) -{ - int i; - memcpy( To->AtNumber, From->AtNumber, n*sizeof(To->AtNumber[0])); - memcpy( To->Rank, From->Rank, n*sizeof(To->AtNumber[0])); - for ( i = 0; i < n; i ++ ) { - To->Rank[i] &= rank_mask_bit; - } - INCHI_HEAPCHK -} -/****************************************************************/ -/* makes new equitable partition (p+1) out of p; first reduce the rank of vertex v */ -int PartitionColorVertex( Graph *G, Partition *p, Node v, int n, int n_tg, int n_max, int bDigraph, int nNumPrevRanks ) -{ - int nNumNewRanks, i, j; - long lNumNeighListIter = 0; - AT_RANK rv, r; - AT_NUMB s, sv; - for ( i = 1; i <= 2; i ++ ) { - if ( !p[i].AtNumber ) { - p[i].AtNumber = (AT_NUMB *) inchi_malloc(n_max*sizeof(p[0].AtNumber[0])); - } - if ( !p[i].Rank ) { - p[i].Rank = (AT_RANK *) inchi_malloc(n_max*sizeof(p[0].Rank[0])); - } - if ( !p[i].AtNumber || !p[i].Rank ) { - INCHI_HEAPCHK - return CT_OUT_OF_RAM; - } - } - PartitionCopy( p+1, p, n_tg ); - sv = v-1; /* atom number we are looking for */ - if ( sv >= (AT_NUMB) n_tg ) { - INCHI_HEAPCHK - return CT_CANON_ERR; /* !!! severe program error: sv not found !!! */ - } - rv = p[1].Rank[(int)sv]; /* rank of this atom */ - /* second, locate sv among all vertices that have same rank as v */ - s = n_max + 1; /* always greater than sv; this initialization is needed only to keep the compiler happy */ - for ( j = (int)rv-1; 0 <= j && rv == (r = p[1].Rank[(int)(s=p[1].AtNumber[j])]) && s != sv; j -- ) - ; - if ( s != sv ) { - INCHI_HEAPCHK - return CT_CANON_ERR; /* !!! severe program error: sv not found !!! */ - } - /* shift preceding atom numbers to the right to fill the gap after removing sv */ - r = rv-1; /* initialization only to keep compiler happy */ - for ( i = j--; 0 <= j && rv == (r = p[1].Rank[(int)(s=p[1].AtNumber[j])]); i = j, j -- ) { - p[1].AtNumber[i] = s; - } - r = (i > 0)? (r+1):1; /* new reduced rank = (next lower rank)+1 or 1 */ - /* insert sv and adjust its rank */ - p[1].AtNumber[i] = sv; - p[1].Rank[(int)sv] = r; - - - /* make equitable partition */ - if ( bDigraph ) { - - /* - nNumNewRanks = DifferentiateRanks2( n_tg, G, - nNumPrevRanks+1, p[1].Rank, p[2].Rank, - p[1].AtNumber, &lNumNeighListIter, 1 ); - */ - nNumNewRanks = DifferentiateRanks4( n_tg, G, - nNumPrevRanks+1, p[1].Rank, p[2].Rank /* temp array */, - p[1].AtNumber, (AT_RANK)n, &lNumNeighListIter ); - - - } else { - /* - nNumNewRanks = DifferentiateRanks2( n_tg, G, - nNumPrevRanks+1, p[1].Rank, p[2].Rank, - p[1].AtNumber, &lNumNeighListIter, 1 ); - */ - nNumNewRanks = DifferentiateRanks3( n_tg, G, - nNumPrevRanks+1, p[1].Rank, p[2].Rank /* temp array */, - p[1].AtNumber, &lNumNeighListIter ); - } - INCHI_HEAPCHK - - return nNumNewRanks; -} -typedef struct tagNodeValues { - NUM_H NumH; - AT_ISO_SORT_KEY iso_sort_key; - NUM_H NumHfixed; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - AT_ISO_SORT_KEY iso_sort_key_Hfixed; -#endif - AT_NUMB nAtNumber; -} NV; - -/****************************************************************/ -/* return min node > vPrev or INFINITY if not found */ -/* Input: v = previous atom number + 1 or 0 on first call*/ -Node CellGetMinNode( Partition *p, Cell *W, Node v, CANON_DATA *pCD ) -{ - AT_NUMB i; - AT_NUMB uCurAtNumb, uMinAtNumb = INFINITY; - /* in case of emty cell: (W->first=INFINITY) > (W->next=0); returns INFINITY */ - if ( W->first > W->next ) { - return INFINITY; - } -#if ( USE_AUX_RANKING == 1 ) - if ( pCD && pCD->nAuxRank ) - { - AT_RANK uMinAuxRank, uCurAuxRank; - int nCurAtNumb; -#if ( USE_AUX_RANKING_ALL == 1 ) - AT_RANK uInpAuxRank; - int nInpAtNumb, nMinAtNumb; -#endif - for ( i = W->first; i < W->next; i ++ ) { - uCurAtNumb = p->AtNumber[(int)i]; - if ( !(p->Rank[(int)uCurAtNumb] & rank_mark_bit) ) { - break; /* found the first unmarked yet node */ - } - } - if ( i == W->next ) { - return INFINITY; - } -#if ( USE_AUX_RANKING_ALL == 1 ) - /*==== vertex ordering definition === - * vertex v1 < v2 <=> (AuxRank(v1)==AuxRank(v2) && AtNumb(v1) < AtNumb(v2)) || (AuxRank(v1) < AuxRank(v2)) - * vertex v1 > v2 <=> (AuxRank(v1)==AuxRank(v2) && AtNumb(v1) > AtNumb(v2)) || (AuxRank(v1) > AuxRank(v2)) - * vertex v1 = v2 <=> (AuxRank(v1)==AuxRank(v2) && AtNumb(v1) == AtNumb(v2)) - */ - - /* set initial vMin so that vMin > any vertex */ - uMinAuxRank = INFINITY; - nMinAtNumb = INFINITY; - /* set vInp */ - if ( v ) { - nInpAtNumb = (int)v - 1; /* previous vertex */ - uInpAuxRank = pCD->nAuxRank[nInpAtNumb]; - } else { - nInpAtNumb = -1; /* less than any vertex */ - uInpAuxRank = 0; - } - /* find vMin = min { vCur : (vCur > vInp) && (vCur in W) } */ - for ( ; i < W->next; i ++ ) { - nCurAtNumb = (int)p->AtNumber[(int)i]; - if ( !(p->Rank[nCurAtNumb] & rank_mark_bit) ) { - /* vertex nCurAtNumb is not marked, find whether it fits the conditions */ - uCurAuxRank = pCD->nAuxRank[nCurAtNumb]; - if ( uCurAuxRank == uInpAuxRank && nCurAtNumb > nInpAtNumb || uCurAuxRank > uInpAuxRank ) { - /* here vCur > vInp */ - if ( uCurAuxRank == uMinAuxRank && nCurAtNumb < nMinAtNumb ) { - /* vCur < vMin (1) */ - nMinAtNumb = nCurAtNumb; - } else - if ( uCurAuxRank < uMinAuxRank ) { - /* vCur < vMin (2) */ - uMinAuxRank = uCurAuxRank; - nMinAtNumb = nCurAtNumb; - } - } - } - } - uMinAtNumb = (nMinAtNumb==INFINITY)? INFINITY : (AT_NUMB)nMinAtNumb; -#else - if ( v ) { - nCurAtNumb = (int)v-1; - /* any valid found node must have nAuxRank == uMinAuxRank */ - uMinAuxRank = pCD->nAuxRank[nCurAtNumb]; - } else { - /* any valid found node must have minimal uMinAuxRank from pCD->nAuxRank[] */ - uMinAuxRank = INFINITY; /* undefined */ - } - - for ( ; i < W->next; i ++ ) { - uCurAtNumb = p->AtNumber[(int)i]; - nCurAtNumb = (int)uCurAtNumb; - if ( uCurAtNumb >= v && !(p->Rank[nCurAtNumb] & rank_mark_bit) ) { - uCurAuxRank = pCD->nAuxRank[nCurAtNumb]; - if ( v ) { - /* get next node */ - /* find node with smallest uCurAtNumb among nodes with aux. ranks equal to uMinAuxRank */ - if ( uCurAuxRank == uMinAuxRank && uCurAtNumb < uMinAtNumb ) { - uMinAtNumb = uCurAtNumb; - } - } else { - /* get first node */ - /* find node with smallest smallest uCurAtNumb among nodes with smallest aux. ranks */ - if ( uMinAuxRank > uCurAuxRank ) { - uMinAuxRank = uCurAuxRank; - uMinAtNumb = uCurAtNumb; - } else - if ( uMinAuxRank == uCurAuxRank && uCurAtNumb < uMinAtNumb ) { - uMinAtNumb = uCurAtNumb; - } - } - } - } -#endif - } else -#endif /* } USE_AUX_RANKING */ - { - for ( i = W->first; i < W->next; i ++ ) { - uCurAtNumb = p->AtNumber[(int)i]; - if ( uCurAtNumb >= v && !(p->Rank[(int)uCurAtNumb] & rank_mark_bit) && uCurAtNumb < uMinAtNumb ) { - uMinAtNumb = uCurAtNumb; - } - } - } - if ( uMinAtNumb != INFINITY ) uMinAtNumb ++; - INCHI_HEAPCHK - return uMinAtNumb; -} -/****************************************************************/ -int CellGetNumberOfNodes( Partition *p, Cell *W ) -{ - int first = W->first; - int next = W->next; - int i, num; - for ( i = first, num = 0; i < next; i ++ ) { - if ( !( rank_mark_bit & p->Rank[(int)p->AtNumber[i]] ) ) { - num++; - } - } - INCHI_HEAPCHK - return num; -} -/****************************************************************/ -int CellIntersectWithSet( Partition *p, Cell *W, NodeSet *Mcr, int l ) -{ - bitWord *McrBits = Mcr->bitword[l-1]; - int first = W->first; - int next = W->next; - int i, j, k; - if ( first >= next ) { /* for testing only */ - return 0; - } - for ( i = first, k = 0; i < next; i ++ ) { - j = (int)p->AtNumber[i]; - if ( !(McrBits[ j / num_bit ] & bBit[ j % num_bit ]) ) { /* BC: reading uninit memory ???-not examined yet */ - k += !(p->Rank[j] & rank_mark_bit); /* for testing only */ - p->Rank[j] |= rank_mark_bit; - } - } - INCHI_HEAPCHK - return k; -} -/****************************************************************/ -void CtPartClear( ConTable *Ct, int k ) -{ - int start; - int len; - /* connection table */ - start = k>1? Ct->nextCtblPos[k-1] : 0; - len = Ct->lenCt - start; - if ( len > 0 ) { - memset( Ct->Ctbl + start, 0, (Ct->lenCt - start)*sizeof(Ct->Ctbl[0]) ); - } - Ct->lenCt = start; - Ct->lenPos = k; - - INCHI_HEAPCHK -} -/**********************************************************************************/ -/* Sort neighbors according to ranks in ascending order */ -void insertions_sort_NeighList_AT_NUMBERS2( NEIGH_LIST base, AT_RANK *nRank, AT_RANK max_rj ) -{ - AT_NUMB *i, *j, *pk, tmp, rj; - int k, num = (int)*base++; - for( k=1, pk = base; k < num; k++, pk ++ ) { - i = pk; - j = i + 1; - rj = (rank_mask_bit & nRank[(int)*j]); - if ( rj < max_rj ) { - while ( j > base && rj < (rank_mask_bit & nRank[(int)*i])) { - tmp = *i; - *i = *j; - *j = tmp; - j = i --; - } - } - } - INCHI_HEAPCHK -} -/****************************************************************/ -/* may need previous Lambda */ -void CtPartFill( Graph *G, CANON_DATA *pCD, Partition *p, - ConTable *Ct, int k, int n, int n_tg ) - /* k = (new index in Ct->nextAtRank[] and Ct->nextCtblPos[]) + 1 */ -{ - int startCtbl; - int startAtOrd; - AT_RANK r, rj, nn, j, rj_prev; - int i, m; -#ifdef INCHI_CANON_USE_HASH - CtHash hash = 0; -#endif - static int count; /* for debug only */ - count ++; - - - INCHI_HEAPCHK - - k --; - if ( k ) { - startCtbl = Ct->nextCtblPos[k-1]; - startAtOrd = Ct->nextAtRank[k-1]-1; /* here p->Rank[p->AtNumber[r-1]] = r */ - } else { - startCtbl = 0; - startAtOrd = 0; - } - /******* well-defined (by fixed ranks) part of the connection table ************/ - r = (rank_mask_bit & p->Rank[(int)p->AtNumber[startAtOrd]]); - for ( i = startAtOrd; i < n_tg && r == (rank_mask_bit&p->Rank[m=(int)p->AtNumber[i]]); i++, r ++ ) { - Ct->Ctbl[startCtbl++] = r; - insertions_sort_NeighList_AT_NUMBERS2( G[m], p->Rank, r ); - nn = G[m][0]; /* number of neighbors */ - rj_prev = 0; /* debug only */ -#ifdef INCHI_CANON_USE_HASH - hash = add2crc32( hash, (AT_NUMB)(r + n) ); -#endif - for ( j = 1; j <= nn && (rj=(rank_mask_bit&p->Rank[(int)G[m][j]])) < r; j ++ ) { - Ct->Ctbl[startCtbl++] = rj; -#ifdef INCHI_CANON_USE_HASH - hash = add2crc32( hash, rj ); -#endif -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - /* debug only */ - if ( rj < rj_prev ) { - int stop = 1; /* */ - } -#endif - rj_prev = rj; - } - } - - INCHI_HEAPCHK - - /****************** well-defined part of base hydrogen atoms *******************/ - if ( pCD->NumH && Ct->NumH ) { - nn = inchi_min(n, i); - for ( j = startAtOrd; j < nn; j ++ ) { /* atoms */ - Ct->NumH[j] = pCD->NumH[p->AtNumber[j]]; - } - for ( ; j < i; j ++ ) { /* t-groups */ - int data_pos = n + T_NUM_NO_ISOTOPIC * ((int)p->AtNumber[j] - n); - for ( m = 0; m < T_NUM_NO_ISOTOPIC; m ++ ) { - Ct->NumH[nn ++] = pCD->NumH[data_pos ++]; - } - } - Ct->lenNumH = nn; - } else { - Ct->lenNumH = 0; - } - - INCHI_HEAPCHK - - /****************** well-defined part of fixed hydrogen atoms *******************/ - if ( pCD->NumHfixed && Ct->NumHfixed ) { - nn = inchi_min(n, i); - for ( j = startAtOrd; j < nn; j ++ ) { - Ct->NumHfixed[j] = pCD->NumHfixed[p->AtNumber[j]]; - - INCHI_HEAPCHK - - } - /* Ct->lenNumHfixed = nn; */ - } else { - ;/* Ct->lenNumHfixed = 0; */ - } - - INCHI_HEAPCHK - - /****************** well-defined part of isotopic keys ***************************/ - if ( pCD->iso_sort_key && Ct->iso_sort_key ) { - for ( j = startAtOrd; j < i; j ++ ) { - Ct->iso_sort_key[j] = pCD->iso_sort_key[p->AtNumber[j]]; - } - Ct->len_iso_sort_key = i; - } else { - Ct->len_iso_sort_key = 0; - } - - INCHI_HEAPCHK - - /****************** well-defined part of isotopic iso_exchg_atnos ***************************/ - if ( pCD->iso_exchg_atnos && Ct->iso_exchg_atnos ) { - for ( j = startAtOrd; j < i; j ++ ) { - Ct->iso_exchg_atnos[j] = pCD->iso_exchg_atnos[p->AtNumber[j]]; - } - Ct->len_iso_exchg_atnos = i; - } else { - Ct->len_iso_exchg_atnos = 0; - } - - INCHI_HEAPCHK - /******** well-defined part of isotopic keys for fixed hydrogen atoms ************/ -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - if ( pCD->iso_sort_key_Hfixed && Ct->iso_sort_key_Hfixed ) { - nn = inchi_min(n, i); - for ( j = startAtOrd; j < nn; j ++ ) { - Ct->iso_sort_key_Hfixed[j] = pCD->iso_sort_key_Hfixed[p->AtNumber[j]]; - } - Ct->len_iso_sort_key_Hfixed = nn; - } else { - Ct->len_iso_sort_key_Hfixed = 0; - } -#endif - - INCHI_HEAPCHK - - Ct->lenCt = startCtbl; /* not aways increases */ - Ct->nextCtblPos[k] = startCtbl; - Ct->nextAtRank[k] = r; - Ct->lenPos = k+1; - /* the rest of the CTable */ -#ifdef INCHI_CANON_USE_HASH - while ( i < n ) { - r = (rank_mask_bit&p->Rank[m=(int)p->AtNumber[i]]); - hash = add2crc32( hash, (AT_NUMB)(r + n) ); - r++; - insertions_sort_NeighList_AT_NUMBERS2( G[m], p->Rank, r ); - nn = G[m][0]; - rj_prev = 0; /* debug only */ - for ( j = 1; j <= nn && (rj=(rank_mask_bit&p->Rank[(int)G[m][j]])) < r; j ++ ) { - hash = add2crc32( hash, rj ); - } - i ++; - } - Ct->hash[k] = hash; -#endif - - INCHI_HEAPCHK -} -/****************************************************************/ -void CtPartInfinity( ConTable *Ct, S_CHAR *cmp, int k ) -{ - int startCtbl; - /*int startAtOrd;*/ - k --; - if ( k ) { - startCtbl = Ct->nextCtblPos[k-1]; - /*startAtOrd = Ct->nextAtRank[k-1]-1;*/ /* here p->Rank[p->AtNumber[r-1]] = r */ - if ( cmp ) { - memset( cmp, 0, k*sizeof(cmp[0]) ); - } - } else { - startCtbl = 0; - /*startAtOrd = 0;*/ - } - if ( !startCtbl || Ct->Ctbl[startCtbl-1] != EMPTY_CT ) { - Ct->Ctbl[startCtbl] = EMPTY_CT; - } - INCHI_HEAPCHK -} -/****************************************************************/ -/* Return value: - -1 <=> *Lambda1 < *Lambda2 - 0 <=> *Lambda1 = *Lambda2 - +1 <=> *Lambda1 > *Lambda2 - - Input: k+1 = value of level at which the comparison is executed - (that is, in the calling program k(caller) = k+1) - - Stars (*) below mark the differences: - - bSplitTautCompare != 0 => directed graph; compare: - non-tautomeric part of CT in layer 0; (*) - non-tautomeric H in layer 1; (*) - tautomeric part of CT & H in layer 2; (*) - fixed H in layer 3; - isotopic atoms, non-taut - H & t-groups in layer 4; - fixed isotopic H in layer 5; <- move to layer 4 - - bSplitTautCompare == 0 => undirected graph; compare: - full CT in Layer 0; (*) - taut and non-taut H in Layer 1; (*) - * nothing * in layer 2; (*) - fixed H in layer 3; - isotopic atoms, non-taut - H & t-groups in layer 4; - fixed isotopic H in layer 5; <- move to layer 4 - -*/ -int CtPartCompare( ConTable *Ct1, ConTable *Ct2, S_CHAR *cmp, - kLeast *kLeastForLayer, int k, int bOnlyCommon, int bSplitTautCompare ) -{ - int startCt1, endCt1, startCt2, endCt2; /*endCt,*/ - int startAt1, endAt1, startAt2, endAt2; /*endCt,*/ - int midCt /* end of atoms only Ct */, midNumH=0 /* end of atoms only NumH */, maxVert; - int diff, i, k1, k2, lenNumH, len_iso_sort_key, /*mid_iso_sort_key,*/ midAt; - int nLayer = 0; - - k --; - i = -1; - /* set kLeastForLayer[nLayer].k = (k+1) or -(k+1) - kLeastForLayer[nLayer].i = iDiff - if all the conditions are met: - 1) kLeastForLayer[nLayer].k = 0 - 2) diff==0 for all layers < nLayer - - sign: - if the final diff < 0 then kLeastForLayer[nLayer].k = -(k+1) else - if the final diff > 0 then kLeastForLayer[nLayer].k = +(k+1) - - k+1 instead of k takes into account k--; statememt above) - - meaning: - ======== - abs(kLeastForLayer[nLayer].k) is the greatest level k at which - difference at layer nLayer are zeroes of hidden by differences in smaller nLayer. - - "Hidden by difference in smaller level" means that nLayer of comparison - has not been reached because the difference was discovered at a previous layer. - - - Lambda vs zf_zeta comparison - ============================================= - accept only diff == 0 - - Lambda vs pzb_rho and pzb_rho_fix comparison - ============================================= - Maintain kLeastForLayer[] and kLeastForLayerFix[] - - The algorithm provides that pzb_rho(m-1) < pzb_rho(m) <= pzb_rho_fix - - Definition: pzb_rho(m-1) < pzb_rho(m) means that - ----------------------------------------------- - pzb_rho(m-1)[nLayerCurr] == pzb_rho(m)[nLayerCurr] for nLayerCurr = 0..nLayerDiff-1 - pzb_rho(m-1)[nLayerDiff] < pzb_rho(m)[nLayerDiff] - - Definition: pzb_rho(m-1)[nLayerDiff] < pzb_rho(m)[nLayerDiff] means that - ------------------------------------------------------------------------- - pzb_rho(m-1)[nLayerDiff][i] == pzb_rho(m)[nLayerDiff][i] for i=0..iDdiff-1 - pzb_rho(m-1)[nLayerDiff][iDdiff] < pzb_rho(m)[nLayerDiff][iDdiff] - - This defines nLayerDiff(pzb1, pzb2) where pszb1 = pzb_rho(a), pzb2=pzb_rho(b) (a= 0 *) && - ((L_fix < L_rho) || (L_fix == L_rho && I_fix < I_rho)) - => - qzb_rho_fix = kLeastForLayerFix[L_fix].k if prevoiusly qzb_rho_fix == 0 - - b) otherwise do not change qzb_rho_fix, except the following: - - c) Special case L_rho == L_fix && I_rho == I_fix. Let L=L_rho, I = I_rho. - - Compare 3 valirs: Lambda[L][I], pzb_rho(m)[L][I], pzb_rho_fix[L][I] - The algorithm provides pzb_rho(m)[L][I] < pzb_rho_fix[L][I] - (pzb_rho(m)[L][I]==pzb_rho_fix[L][I] <=> pzb_rho(m)[L][I]==pzb_rho_fix[L][I] - is impossible by construction) - There are 3 possibilities: - c1) Lambda[L][I] < pzb_rho(m)[L][I] < pzb_rho_fix[L][I] <=> - kLeastForLayer[L].k < 0 && kLeastForLayerFix[L].k < 0 - => qzb_rho := kLeastForLayer[L].k, reject too small Lambda - c2) pzb_rho(m)[L][I] < Lambda[L][I] < pzb_rho_fix[L][I] - kLeastForLayer[L].k > 0 && kLeastForLayerFix[L].k < 0 - => qzb_rho := kLeastForLayer[L].k, accept Lambda, rho:=nu - c3) pzb_rho(m)[L][I] < pzb_rho_fix[L][I] < Lambda[L][I] - kLeastForLayer[L].k > 0 && kLeastForLayerFix[L].k > 0 - => qzb_rho_fix := kLeastForLayerFix[L].k, reject too big Lambda - - Case - kLeastForLayer[L].k < 0 && kLeastForLayerFix[L].k > 0 is impossible - because it means - pzb_rho_fix < Lambda < pzb_rho(m) <=> pzb_rho_fix < pzb_rho(m) - - - Case (c3) occurs in case of (a) - Case (c1) - - 2. Comparison Lambda vs pzb_rho before reaching discrete partition - ---------------------------------------------------------------------- - a) (L_rho < L_fix) || (L_rho == L_fix && I_rho < I_fix) => - - Lambda differs from pzb_rho(m) in the part of pzb_rho(m) that will never change - qzb_rho = kLeastForLayer[L_rho].k; reject Labmda or accept pzb_rho(m+1):=Labmda - - b) (L_rho == L_fix && I_rho > I_fix) && kLeastForLayer[L_rho].k < 0 - Lambda < pzb_rho(m), therefore - qzb_rho = kLeastForLayer[L_rho].k; reject Labmda - - c) (L_rho > L_fix) => - qzb_rho := 0 because more significant difference may be discovered - in layer < L_rho later. The final comparison may be needed at the - level of discrete partition. - - - */ - - if ( cmp ) { - for ( i = 0; i <= k && !cmp[i]; i++ ) - ; - if ( i < k ) { - cmp[k] = cmp[i]; - return (int)cmp[i]; - } - } - k1 = Ct1->lenPos-1; - k2 = Ct2->lenPos-1; - -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - if ( k > k1 || k > k2 ) { - int stop = 1; - } -#endif - diff = 0; - - if ( k ) { - startCt1 = Ct1->nextCtblPos[k-1]; - startCt2 = Ct2->nextCtblPos[k-1]; - startAt1 = Ct1->nextAtRank[k-1]-1; - startAt2 = Ct2->nextAtRank[k-1]-1; - } else { - startCt1 = startCt2 = 0; - startAt1 = startAt2 = 0; - } - - endCt1 = Ct1->nextCtblPos[k]; - endCt2 = Ct2->nextCtblPos[k]; - endAt1 = (int)Ct1->nextAtRank[k]-1; - endAt2 = (int)Ct2->nextAtRank[k]-1; - - maxVert = inchi_min(Ct1->maxVert, Ct2->maxVert); - -#ifdef INCHI_CANON_USE_HASH - if ( !diff ) { - if ( Ct1->hash[k] > Ct2->hash[k] ) - diff = 1; - else - if ( Ct1->hash[k] < Ct2->hash[k] ) - diff = -1; - } - if ( diff ) { - goto done; - } -#endif - - /************************** lengths **************************************************/ - if ( diff = -(startCt1 - startCt2) ) { - /* comparing two INFINITY terminations */ - if ( bOnlyCommon && - startCt1 >= Ct1->nLenCTAtOnly && startCt2 >= Ct2->nLenCTAtOnly && - Ct1->Ctbl[startCt1] == EMPTY_CT && Ct2->Ctbl[startCt2] == EMPTY_CT ) { - return 0; - } - if ( bOnlyCommon ) { - startCt1 = startCt2 = inchi_min(startCt1, startCt2); - startAt1 = startAt2 = inchi_min(startAt1, startAt2); - if ( Ct1->lenCt == Ct2->lenCt ) { - endCt1 = endCt2 = inchi_max(endCt1, endCt2); - endAt1 = endAt2 = inchi_max(endAt1, endAt2); - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; - } -#endif - } else - /* comparing (taut tail) vs INFINITY termination -- ??? */ - if ( startCt1 > startCt2 && - Ct1->maxVert > Ct2->maxVert && - startAt2 == Ct2->maxVert ) { - return 0; - } else { - goto done; - } - } - - lenNumH = Ct1->lenNumH; - len_iso_sort_key = Ct1->len_iso_sort_key; - - if ( diff = -(endCt1 - endCt2) ) { /* negative sign reproduces results for NSC=28393 */ - if ( bOnlyCommon ) { - endCt1 = endCt2 = inchi_min(endCt1, endCt2); - endAt1 = endAt2 = inchi_min(endAt1, endAt2); - lenNumH = inchi_min(Ct1->lenNumH, Ct2->lenNumH); - len_iso_sort_key = inchi_min(Ct1->len_iso_sort_key, Ct1->len_iso_sort_key); - } else - /* take care of case when comparing tautomeric vs non-tautomeric: - since (taut)->maxVert > (non-taut)->maxVert, --??? - (taut)->maxlenCt > (non-taut)->maxlenCt --!!! - compare up to min out of the two, ignoring INFINITY in the last position */ - if ( endCt1 > endCt2 && Ct1->maxlenCt > Ct2->maxlenCt ) { - if ( endAt2 == Ct2->maxVert + 1 ) { - /* remove INFINITY termination of the shorter CT */ - /* should never happen */ - endAt2 --; - len_iso_sort_key = lenNumH = endAt1 = endAt2; - endCt2 --; - endCt1 = endCt2; - diff = 0; - } else - if ( endAt2 == Ct2->maxVert ) { - /* remove INFINITY termination of CT */ - len_iso_sort_key = lenNumH = endAt1 = endAt2; - endCt1 = endCt2; - diff = 0; - } else { - goto done; - } - } else { - goto done; - } - } - - if ( bSplitTautCompare ) { - midCt = inchi_min(Ct1->nLenCTAtOnly, Ct2->nLenCTAtOnly); - if ( midCt > endCt1 ) { - midCt = endCt1; - } - midAt = inchi_min(maxVert, endAt1); - } else { - midCt = endCt1; - midAt = endAt1; - } - - /*endCt = min(endCt1, endCt2);*/ - /*************************************************************************/ - /************ layer 0: connection table without tautomeric groups ********/ - /*************************************************************************/ - for ( i = startCt1; i < midCt && Ct1->Ctbl[i] == Ct2->Ctbl[i]; i ++ ) - /*for ( i = startCt1; i < endCt && !(diff = (int)Ct1->Ctbl[i] - (int)Ct2->Ctbl[i]); i ++ )*/ - ; - if ( i < midCt ) { - diff = (int)Ct1->Ctbl[i] - (int)Ct2->Ctbl[i]; - goto done; - } - /*************************************************************************/ - /******** layer 1 NumH: H atoms without tautomeric H *********************/ - /*************************************************************************/ - nLayer ++; - /*============= check limits for consistency ==========*/ - if ( diff = -(startAt1 - startAt2) ) { - goto done; /* should not happen */ - } - if ( diff = -(endAt1 - endAt2) ) { - goto done; /* should not happen */ - } - /*============= comparison =============================*/ - if ( Ct1->NumH && Ct2->NumH ) { - if ( endAt1 < maxVert ) { - midNumH = lenNumH = endAt1; - } else - if ( bSplitTautCompare ) { - midNumH = maxVert; - } else { - midNumH = lenNumH; - } - /* lenNumH = (endAt2 >= maxVert)? lenNumH : endAt2; */ - /* endAt1 = (endAt2 == n)? lenNumH : endAt2; */ - - for ( i = startAt1; i < midNumH && Ct1->NumH[i] == Ct2->NumH[i]; i ++ ) - ; - if ( i < midNumH ) { - diff = (int)Ct1->NumH[i] - (int)Ct2->NumH[i]; - goto done; - } - } - /*************************************************************************/ - /************** layer 2: tautomeric part of CT and tautomeric H **********/ - /*************************************************************************/ - nLayer ++; - for ( i = midCt; i < endCt1 && Ct1->Ctbl[i] == Ct2->Ctbl[i]; i ++ ) - ; /* compare tautomeric groups part of CT */ - if ( i < endCt1 ) { - diff = (int)Ct1->Ctbl[i] - (int)Ct2->Ctbl[i]; - goto done; - } - if ( Ct1->NumH && Ct2->NumH ) { - for ( i = midNumH; i < lenNumH && Ct1->NumH[i] == Ct2->NumH[i]; i ++ ) - ; /* compare tautomeric H */ - if ( i < lenNumH ) { - diff = (int)Ct1->NumH[i] - (int)Ct2->NumH[i]; - i += endCt1 - midCt; - goto done; - } - } - /*************************************************************************/ - /************** layer 3: Fixed H atoms ***********************************/ - /*************************************************************************/ - nLayer ++; - if ( Ct1->NumHfixed && Ct2->NumHfixed ) { - for ( i = startAt1; i < midAt && Ct1->NumHfixed[i] == Ct2->NumHfixed[i]; i ++ ) - ; - if ( i < midAt ) { - diff = (int)Ct1->NumHfixed[i] - (int)Ct2->NumHfixed[i]; - goto done; - } - } - /*************************************************************************/ - /************** layer 4: isotopic atoms H, incl. tautomeric **************/ - /*************************************************************************/ - nLayer ++; - if ( Ct1->iso_sort_key && Ct2->iso_sort_key ) { - for ( i = startAt1; i < endAt1 && Ct1->iso_sort_key[i] == Ct2->iso_sort_key[i]; i ++ ) - ; - if ( i < endAt1 ) { - diff = Ct1->iso_sort_key[i] > Ct2->iso_sort_key[i]? 1:-1; - goto done; - } - } - if ( Ct1->iso_exchg_atnos && Ct2->len_iso_exchg_atnos ) { - for ( i = startAt1; i < endAt1 && Ct1->iso_exchg_atnos[i] == Ct2->iso_exchg_atnos[i]; i ++ ) - ; - if ( i < endAt1 ) { - diff = Ct1->iso_exchg_atnos[i] > Ct2->iso_exchg_atnos[i]? 1:-1; - goto done; - } - } -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - /*************************************************************************/ - /************** layer 6: Fixed isotopic H atoms **************************/ - /*************************************************************************/ - nLayer ++; - if ( Ct1->iso_sort_key_Hfixed && Ct2->iso_sort_key_Hfixed ) { - for ( i = startAt1; i < midAt && Ct1->iso_sort_key_Hfixed[i] == Ct2->iso_sort_key_Hfixed[i]; i ++ ) - ; - if ( i < midAt ) { - diff = Ct1->iso_sort_key_Hfixed[i] > Ct2->iso_sort_key_Hfixed[i]? 1:-1; - goto done; - } - } -#endif - -done: -#ifdef INCHI_CANON_MIN - diff = -diff; -#endif - - if ( diff ) { - diff = (diff > 0)? (nLayer+1) : -(nLayer+1); /* return the discovered difference layer number >= 1 */ - if ( kLeastForLayer ) { -#if ( bRELEASE_VERSION != 1 ) - if ( abs(kLeastForLayer[nLayer].k) > k+1 ) { /* for debug only */ - int stop = 1; /* */ - } -#endif - if ( !kLeastForLayer[nLayer].k ) { - kLeastForLayer[nLayer].k = (diff > 0)? (k+1) : -(k+1); - kLeastForLayer[nLayer].i = i; - } - if ( nLayer /* && !bOnlyCommon */) { - diff = 0; - } - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; /* for debug only */ - } -#endif - if ( cmp ) { - cmp[k] = (diff > 0)? 1 : (diff < 0)? -1 : 0; - } - return diff; -} -/**************************************************************************************************************/ -int CtFullCompare( ConTable *Ct1, ConTable *Ct2, int bOnlyCommon, int bSplitTautCompare ) -{ - int startCt1, endCt1, startCt2, endCt2; /*endCt,*/ - int startAt1, endAt1, startAt2, endAt2; /*endCt,*/ - int midCt /* end of atoms only in Ctbl */, - midNumH = 0 /* end of atoms only NumH */, - midAt /* end of atoms only */; - int diff, i, k1, k2, lenNumH1, lenNumH2, lenNumH, maxVert /* min num atoms */; - int len_iso_sort_key1, len_iso_sort_key2, len_iso_sort_key /*, mid_iso_sort_key*/; - int nLayer = 0; - - k1 = Ct1->lenPos-1; - k2 = Ct2->lenPos-1; - - diff = 0; - - startCt1 = startCt2 = 0; - startAt1 = startAt2 = 0; - - endCt1 = Ct1->nextCtblPos[k1]; - endCt2 = Ct2->nextCtblPos[k2]; - endAt1 = (int)Ct1->nextAtRank[k1]-1; - endAt2 = (int)Ct2->nextAtRank[k2]-1; - - maxVert = inchi_min(Ct1->maxVert, Ct2->maxVert); - - if ( bOnlyCommon ) { - endCt1 = inchi_min(endCt1, endCt2); - endCt1 = endCt2 = inchi_min(endCt1, Ct1->lenCt); - endAt1 = endAt2 = inchi_min(endAt1, endAt2); - if ( Ct1->Ctbl[endCt1] == EMPTY_CT || Ct1->Ctbl[endCt1] == 0 || - Ct2->Ctbl[endCt1] == EMPTY_CT || Ct2->Ctbl[endCt1] == 0 ) { - endCt1 = endCt2 = endCt1-1; - } - lenNumH = - lenNumH1 = - lenNumH2 = inchi_min(Ct1->lenNumH, Ct2->lenNumH); - len_iso_sort_key = - len_iso_sort_key1 = - len_iso_sort_key2 = inchi_min(Ct1->len_iso_sort_key, Ct1->len_iso_sort_key); - } else { - if ( Ct1->Ctbl[endCt1-1] == EMPTY_CT ) { - endCt1 --; - } - if ( Ct2->Ctbl[endCt2-1] == EMPTY_CT ) { - endCt2 --; - } - lenNumH1 = Ct1->lenNumH; - lenNumH2 = Ct2->lenNumH; - lenNumH = inchi_min(lenNumH1, lenNumH2); - len_iso_sort_key1 = Ct1->len_iso_sort_key; - len_iso_sort_key2 = Ct2->len_iso_sort_key; - len_iso_sort_key = inchi_min(len_iso_sort_key1, len_iso_sort_key2); - } - - if ( diff = -(endCt1 - endCt2) ) { /* negative sign reproduces results for NSC=28393 */ - goto done; - } - - if ( bSplitTautCompare ) { - midCt = inchi_min(Ct1->nLenCTAtOnly, Ct2->nLenCTAtOnly); - if ( midCt > endCt1 ) { - midCt = endCt1; - } - midAt = inchi_min(maxVert, endAt1); - } else { - midCt = endCt1; - midAt = endAt1; - } - - /*************************************************************************/ - /************ layer 0: connection table without tautomeric groups ********/ - /*************************************************************************/ - for ( i = startCt1; i < midCt && Ct1->Ctbl[i] == Ct2->Ctbl[i]; i ++ ) - ; - if ( i < midCt ) { - diff = (int)Ct1->Ctbl[i] - (int)Ct2->Ctbl[i]; - goto done; - } - /*************************************************************************/ - /************* layer 1: H atoms without tautomeric H *********************/ - /*************************************************************************/ - nLayer ++; - if ( Ct1->NumH && Ct2->NumH ) { - if ( diff = -(lenNumH1 - lenNumH2) ) { /* negative sign reproduces results for NSC=28393 */ - goto done; - } - if ( endAt1 < maxVert ) { - midNumH = lenNumH1 = endAt1; - } else - if ( bSplitTautCompare ) { - midNumH = maxVert; - } else { - midNumH = lenNumH1; - } - for ( i = startAt1; i < midNumH && Ct1->NumH[i] == Ct2->NumH[i]; i ++ ) - ; - if ( i < midNumH ) { - diff = (int)Ct1->NumH[i] - (int)Ct2->NumH[i]; - goto done; - } - } - /*************************************************************************/ - /************** layer 2: tautomeric part of CT and tautomeric H **********/ - /*************************************************************************/ - nLayer ++; - for ( i = midCt; i < endCt1 && Ct1->Ctbl[i] == Ct2->Ctbl[i]; i ++ ) - ; /* compare tautomeric groups part of CT */ - if ( i < endCt1 ) { - diff = (int)Ct1->Ctbl[i] - (int)Ct2->Ctbl[i]; - goto done; - } - if ( Ct1->NumH && Ct2->NumH ) { - for ( i = midNumH; i < lenNumH1 && Ct1->NumH[i] == Ct2->NumH[i]; i ++ ) - ; /* compare tautomeric H */ - if ( i < lenNumH1 ) { - diff = (int)Ct1->NumH[i] - (int)Ct2->NumH[i]; - goto done; - } - } - /*************************************************************************/ - /************** layer 3: Fixed H atoms ***********************************/ - /*************************************************************************/ - nLayer ++; - if ( Ct1->NumHfixed && Ct2->NumHfixed ) { - for ( i = startAt1; i < endAt1 && Ct1->NumHfixed[i] == Ct2->NumHfixed[i]; i ++ ) - ; - if ( i < endAt1 ) { - diff = (int)Ct1->NumHfixed[i] - (int)Ct2->NumHfixed[i]; - goto done; - } - } - /*************************************************************************/ - /************** layer 4: isotopic atoms, H and isotopic taut H ***********/ - /*************************************************************************/ - nLayer ++; - if ( Ct1->iso_sort_key && Ct2->iso_sort_key ) { - if ( diff = -(len_iso_sort_key1 - len_iso_sort_key2) ) { /* negative sign reproduces results for NSC=28393 */ - goto done; - } - for ( i = startAt1; i < endAt1 && Ct1->iso_sort_key[i] == Ct2->iso_sort_key[i]; i ++ ) - ; - if ( i < endAt1 ) { - diff = Ct1->iso_sort_key[i] > Ct2->iso_sort_key[i]? 1:-1; - goto done; - } - } -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - /*************************************************************************/ - /************** layer 6: Fixed isotopic H atoms **************************/ - /*************************************************************************/ - nLayer ++; - if ( Ct1->iso_sort_key_Hfixed && Ct2->iso_sort_key_Hfixed ) { - for ( i = startAt1; i < midAt && Ct1->iso_sort_key_Hfixed[i] == Ct2->iso_sort_key_Hfixed[i]; i ++ ) - ; - if ( i < midAt ) { - diff = Ct1->iso_sort_key_Hfixed[i] > Ct2->iso_sort_key_Hfixed[i]? 1:-1; - goto done; - } - } -#endif - -done: -#ifdef INCHI_CANON_MIN - diff = -diff; -#endif - - if ( diff ) { - diff = (diff > 0)? (nLayer+1) : -(nLayer+1); /* return the discovered difference layer number >= 1 */ - } - return diff; -} -/****************************************************************/ -int CtFullCompareLayers( kLeast *kLeastForLayer ) -{ - int iLayer; - /* check for the rejection condition: Lambda > zb_rho_fix */ - for ( iLayer = 0; iLayer < MAX_LAYERS; iLayer ++ ) { - if ( kLeastForLayer[iLayer].k ) { - return (kLeastForLayer[iLayer].k > 0)? (iLayer+1) : -(iLayer+1); - } - } - return 0; -} -/**************************************************************/ -int CtCompareLayersGetFirstDiff( kLeast *kLeast_rho, int nOneAdditionalLayer, - int *L_rho, int *I_rho, int *k_rho ) -{ - int iLayer; - if ( kLeast_rho ) { - for ( iLayer = 0; iLayer < MAX_LAYERS; iLayer ++ ) { - if ( kLeast_rho[iLayer].k ) { - *L_rho = iLayer; - *I_rho = kLeast_rho[iLayer].i; - *k_rho = kLeast_rho[iLayer].k; - break; - } - } - if ( iLayer == MAX_LAYERS ) { - if ( nOneAdditionalLayer ) { - *L_rho = nOneAdditionalLayer; /* ??? subtract 1 ??? */ - *I_rho = -1; - *k_rho = 0; - return 0; /* difference may be in the first additional layer */ - } else { - *L_rho = INFINITY; - *I_rho = -1; - *k_rho = 0; - return 0; /* no difference found */ - } - } else { - return 1; /* difference in a real layer */ - } - } else { - return -1; /* no input, should not happen */ - } -} -/**************************************************************/ -int CtPartCompareLayers( kLeast *kLeast_rho, int L_rho_fix_prev, int nOneAdditionalLayer ) -{ - int L_rho, I_rho, k_rho; - if ( 0 < CtCompareLayersGetFirstDiff( kLeast_rho, nOneAdditionalLayer, &L_rho, &I_rho, &k_rho ) && - /* differences has been found in a real layer or all real layers are identical */ - L_rho <= L_rho_fix_prev ) { - /* in this layer pzb_rho == pzb_rho_fix or in the previous real layer */ - return k_rho > 0? (L_rho+1) : -(L_rho+1); - } - return 0; -} -/****************************************************************/ -void UpdateCompareLayers( kLeast kLeastForLayer[], int hzz ) -{ - int i; - if ( kLeastForLayer ) { - for ( i = 0; i < MAX_LAYERS; i ++ ) { - if ( abs(kLeastForLayer[i].k) >= hzz ) { - kLeastForLayer[i].k = 0; - kLeastForLayer[i].i = 0; - } - } - } -} - -/****************************************************************/ -void CtPartCopy( ConTable *Ct1 /* to */, ConTable *Ct2 /* from */, int k ) -{ - int startCt1, startCt2, endCt1, endCt2; - int len2, len2H, len2Hfixed, len2iso_sort_key, len2iso_exchg_atnos, i; - int startAt1, endAt1, startAt2, endAt2; /*endCt,*/ -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - int len2iso_sort_key_Hfixed; -#endif - k --; - if ( k ) { - startCt1 = Ct1->nextCtblPos[k-1]; - startCt2 = Ct2->nextCtblPos[k-1]; - startAt1 = Ct1->nextAtRank[k-1]-1; - startAt2 = Ct2->nextAtRank[k-1]-1; - } else { - startCt1 = startCt2 = 0; - startAt1 = startAt2 = 0; - } - - endCt1 = Ct1->nextCtblPos[k]; - endCt2 = Ct2->nextCtblPos[k]; - endAt1 = (int)Ct1->nextAtRank[k]-1; - endAt2 = (int)Ct2->nextAtRank[k]-1; - - len2 = endCt2-startCt2; - /* len = min(len1, len2); */ -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - if ( startCt1 != startCt2 || startAt1 != startAt2 ) { - int stop = 1; - } -#endif - - /* copy connection table: Ctbl */ - for ( i = 0; i < len2; i ++ ) { - Ct1->Ctbl[startCt1+i] = Ct2->Ctbl[startCt2+i]; - } - /* copy number of H: NumH */ - len2H = 0; - if ( Ct1->NumH && Ct2->NumH ) { - len2H = endAt2-startAt2; - if ( endAt2 > Ct2->maxVert ) { - len2H = Ct2->lenNumH - startAt2; - } - for ( i = 0; i < len2H; i ++ ) { - Ct1->NumH[startAt1+i] = Ct2->NumH[startAt2+i]; - } - } - /* copy number of fixed H */ - len2Hfixed = 0; - if ( Ct1->NumHfixed && Ct2->NumHfixed ) { - len2Hfixed = endAt2-startAt2; - for ( i = 0; i < len2Hfixed; i ++ ) { - Ct1->NumHfixed[startAt1+i] = Ct2->NumHfixed[startAt2+i]; - } - } - /* copy isotopic keys */ - len2iso_sort_key = 0; - if ( Ct1->iso_sort_key && Ct2->iso_sort_key ) { - len2iso_sort_key = endAt2-startAt2; - for ( i = 0; i < len2iso_sort_key; i ++ ) { - Ct1->iso_sort_key[startAt1+i] = Ct2->iso_sort_key[startAt2+i]; - } - } - len2iso_exchg_atnos = 0; - if ( Ct1->iso_exchg_atnos && Ct2->iso_exchg_atnos ) { - len2iso_exchg_atnos = endAt2-startAt2; - for ( i = 0; i < len2iso_exchg_atnos; i ++ ) { - Ct1->iso_exchg_atnos[startAt1+i] = Ct2->iso_exchg_atnos[startAt2+i]; - } - } -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - len2iso_sort_key_Hfixed = 0; - if ( Ct1->iso_sort_key_Hfixed && Ct2->iso_sort_key_Hfixed ) { - len2iso_sort_key_Hfixed = endAt2-startAt2; - for ( i = 0; i < len2iso_sort_key; i ++ ) { - Ct1->iso_sort_key_Hfixed[startAt1+i] = Ct2->iso_sort_key_Hfixed[startAt2+i]; - } - } -#endif - Ct1->lenCt = startCt1 + len2; - Ct1->nextCtblPos[k] = startCt1 + len2; - Ct1->nextAtRank[k] = Ct2->nextAtRank[k]; - if ( len2H ) { - Ct1->lenNumH = startAt1 + len2H; - } - /* - if ( len2Hfixed ) { - Ct1->lenNumHfixed = startAt1 + len2Hfixed; - } - */ - if ( len2iso_sort_key ) { - Ct1->len_iso_sort_key = startAt1 + len2iso_sort_key; - } - if ( len2iso_exchg_atnos ) { - Ct1->len_iso_exchg_atnos = startAt1 + len2iso_exchg_atnos; - } - -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - if ( len2iso_sort_key_Hfixed ) { - Ct1->len_iso_sort_key_Hfixed = startAt1 + len2iso_sort_key_Hfixed; - } -#endif -#ifdef INCHI_CANON_USE_HASH - Ct1->hash[k] = Ct2->hash[k]; -#endif - Ct1->lenPos = k+1; - INCHI_HEAPCHK -} -/****************************************************************/ -void CtFullCopy( ConTable *Ct1, ConTable *Ct2 ) -{ - /* Ct1 does not have INFINITY termination */ - int k; - for ( k = 0; k < Ct2->lenPos; k ++ ) { - CtPartCopy( Ct1 /* to */, Ct2 /* from */, k+1 ); - } -} -/****************************************************************/ -void TranspositionGetMcrAndFixSetAndUnorderedPartition( Transposition *gamma, NodeSet *McrSet, NodeSet *FixSet, int n, int l, UnorderedPartition *p ) -{ - int i, j, k, mcr, num; - AT_RANK next; - bitWord *McrBits = McrSet->bitword[l-1]; - bitWord *FixBits = FixSet->bitword[l-1]; - int len = McrSet->len_set*sizeof(bitWord); - - memset( McrBits, 0, len ); - memset( FixBits, 0, len ); - for ( i = 0; i < n; i ++ ) { - p->equ2[i] = INFINITY; /* for debug only */ - } - - for ( i = 0; i < n; i ++ ) { - j = (int)(next = gamma->nAtNumb[i]); - if ( j == i ) { - FixBits[ i / num_bit ] |= bBit[ i % num_bit ]; - McrBits[ i / num_bit ] |= bBit[ i % num_bit ]; - /* p->next[i] = INFINITY; */ /* no link to same orbit points */ - p->equ2[i] = next; /* fixed point */ - } else - if ( !(rank_mark_bit & next) ) { - gamma->nAtNumb[i] |= rank_mark_bit; - mcr = inchi_min(j, i); - num = 0; - /* mark all nodes in the cycle to ignore later; find mcr */ - while( !(rank_mark_bit & (next = gamma->nAtNumb[j])) ) { - gamma->nAtNumb[j] |= rank_mark_bit; - j = (int)next; - if ( mcr > j ) { - mcr = j; - } - num ++; - } - McrBits[ mcr / num_bit ] |= bBit[mcr % num_bit]; /* save mcr */ - /* fill out the unordered partition, the mcr first, other in the cycle after that */ - p->equ2[mcr] = mcr; - for ( k = mcr; mcr != (j = (int)(rank_mask_bit & gamma->nAtNumb[k])); k = j ) { - p->equ2[j] = mcr; - } - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - /* for debug only */ - for ( i = 0; i < n; i ++ ) { - if ( p->equ2[i] >= n ) { - int stop = 1; - } - } -#endif - /* remove the marks */ - for ( i = 0; i < n; i ++ ) { - gamma->nAtNumb[i] &= rank_mask_bit; - } - INCHI_HEAPCHK -} -/****************************************************************/ -int SetBitCreate( void ) -{ - bitWord b1, b2; - AT_NUMB n1, n2; -#ifdef INCHI_CANON_USE_HASH - CtHash h1, h2; -#endif - int i; - - if ( bBit ) { - INCHI_HEAPCHK - return 0; /* already created */ - } - - b1 = 1; - num_bit = 1; - for ( b1=1, num_bit=1; b1 < (b2 = (bitWord)((b1 << 1)& BIT_WORD_MASK)); b1 = b2, num_bit ++ ) - ; - bBit = (bitWord*)inchi_calloc( num_bit, sizeof(bitWord)); - if ( !bBit ) { - INCHI_HEAPCHK - return -1; /* failed */ - } - for ( i = 0, b1=1; i < num_bit; i++, b1 <<= 1 ) { - bBit[i] = b1; - } - - for ( n1 = 1; n1 < (n2 = (AT_RANK)((n1 << 1)& AT_RANK_MASK) ); n1 = n2 ) - ; - rank_mark_bit = n1; - rank_mask_bit = ~n1; - -#ifdef INCHI_CANON_USE_HASH - for ( h1 = 1; h1 < (h2 = (h1 << 1)); h1 = h2 ) - ; - hash_mark_bit = h1; -#endif - INCHI_HEAPCHK - return 1; -} -/****************************************************************/ -int SetBitFree( void ) -{ - if ( bBit ) { - inchi_free( bBit ); - bBit = NULL; - INCHI_HEAPCHK - return 1; /* success */ - } - INCHI_HEAPCHK - return 0; /* already destroyed */ -} -#ifdef NEVER /* { how to renumber a graph */ -/*********************************************************************/ -void RenumberTheGraph( int n, NEIGH_LIST *NeighList, AT_NUMB *old2new, - AT_NUMB *new2old, S_CHAR *mark, int bDone ) -{ - int i, k, j; - NEIGH_LIST nl; - - /* renumber neighbors */ - for ( i = 0; i < n; i ++ ) { - for ( j = 1; j <= NeighList[i][0]; j ++ ) { - NeighList[i][j] = old2new[NeighList[i][j]]; - } - } - /* rearrange NeighList in situ using new2old[] */ - for ( k = 0; k < n; k ++ ) { - if ( mark[k] & bDone ) - continue; - if ( k == ( j = new2old[k] ) ) { - mark[k] |= bDone; - continue; - } - /* transposition cycle */ - i = k; - nl = NeighList[k]; - do { - NeighList[i] = NeighList[j]; - mark[i] |= bDone; - i = j; - } while ( k != ( j = new2old[i] ) ); - NeighList[i] = nl; - mark[i] |= bDone; - } -#ifdef NEVER - /* rearrange NeighList in situ using old2new[] */ - s = 0; - for ( k = 0; k < n; k ++ ) { - if ( mark[k] & bDone ) - continue; - if ( k == ( j = old2new[k] ) ) { - mark[k] |= bDone; - continue; - } - /* transposition cycle */ - i = k; - /* NeighList[j] goes to ith position */ - nl2[s] = NeighList[j]; - s ^= 1; - do { - nl2[s] = NeighList[i]; - NeighList[i] = nl2[s ^= 1]; - mark[i] |= bDone; - i = j; - j = old2new[i]; - } while ( k != ( j = old2new[i] ) ); - NeighList[j] = nl2[s ^= 1]; - mark[j] |= bDone; - } -#endif -} -/***************************************************************************************/ -void RearrangeAtRankArray ( int n, AT_RANK *nRank, AT_NUMB *new2old, S_CHAR *mark, int bDone ) -{ - int i, k, j; - AT_RANK r; - /* rearrange the array in situ using new2old[] */ - for ( k = 0; k < n; k ++ ) { - if ( mark[k] & bDone ) - continue; - if ( k == ( j = new2old[k] ) ) { - mark[k] |= bDone; - continue; - } - /* transposition cycle */ - i = k; - r = nRank[k]; - do { - nRank[i] = nRank[j]; - mark[i] |= bDone; - i = j; - } while ( k != ( j = new2old[i] ) ); - nRank[i] = r; - mark[i] |= bDone; - } - -} -/***************************************************************************************/ -void RenumberAtNumbArray( int n, AT_NUMB *nAtNumb, AT_NUMB *old2new ) -{ - int i; - for ( i = 0; i < n; i ++ ) { - nAtNumb[i] = old2new[nAtNumb[i]]; - } -} -/****************************************************************/ -int GetCanonRanking2( int num_atoms, int num_at_tg, int num_max, int bDigraph, sp_ATOM* at, - AT_RANK **pRankStack, int nNumPrevRanks, - AT_RANK *nSymmRank, AT_RANK *nCanonRank, - NEIGH_LIST *NeighList, AT_RANK *nTempRank, - CANON_STAT* pCS ) -{ - void *pzb_rho=NULL; - int ret, cmp1=0, cmp2=0; - - int i, j, k, n; - AT_NUMB *old2new = NULL, *new2old = NULL, m, r1, r2; - S_CHAR *mark = NULL; - - int nMaxLenCt = pCS->nMaxLenLinearCT; - AT_RANK *pCt = pCS->LinearCT; - int nLenCt = pCS->nLenLinearCT; - AT_RANK *pCt0 = pCS->LinearCT; - int nLenCt0 = pCS->nLenLinearCT; - - - CANON_DATA CanonData; - CANON_DATA *pCD = &CanonData; - CANON_COUNTS CanonCounts; - CANON_COUNTS *pCC = &CanonCounts; - memset (pCD, 0, sizeof(pCD[0])); - memset (pCC, 0, sizeof(pCC[0])); - /* pointers */ - pCD->LinearCT = pCS->LinearCT; - /* variables - unchanged */ - pCD->ulTimeOutTime = pCS->ulTimeOutTime; - pCD->nMaxLenLinearCT = pCS->nMaxLenLinearCT; - /* return values & input/output */ - pCD->nLenLinearCT = pCS->nLenLinearCT; - - pCC->lNumBreakTies = pCS->lNumBreakTies; - pCC->lNumDecreasedCT = pCS->lNumDecreasedCT; - pCC->lNumRejectedCT = pCS->lNumRejectedCT; - pCC->lNumEqualCT = pCS->lNumEqualCT; - pCC->lNumTotCT = pCS->lNumTotCT; - - ret = CanonGraph( num_atoms, num_at_tg, num_max, bDigraph, NeighList, (Partition *)pRankStack, - nSymmRank, nCanonRank, pCS->nPrevAtomNumber, pCD, pCC, NULL, &pzb_rho ); - - pCS->nLenLinearCT = pCD->nLenLinearCT; - pCS->lNumBreakTies = pCC->lNumBreakTies; - pCS->lNumDecreasedCT = pCC->lNumDecreasedCT; - pCS->lNumRejectedCT = pCC->lNumRejectedCT; - pCS->lNumEqualCT = pCC->lNumEqualCT; - pCS->lNumTotCT = pCC->lNumTotCT; - - - /* save the connection table for comparison with the 2nd one */ - pCt0 = (AT_RANK*)inchi_calloc(nMaxLenCt, sizeof(pCt0[0])); - memcpy(pCt0, pCS->LinearCT, nMaxLenCt*sizeof(pCt0[0])); - nLenCt0 = pCS->nLenLinearCT; - - /**********************************************************/ - /* rearrange numbering to make canon. numbering the first */ - /**********************************************************/ - n = num_at_tg; - /* 1. get transpositions */ - old2new = (AT_NUMB*) inchi_calloc( n, sizeof(old2new[0]) ); - new2old = (AT_NUMB*) inchi_calloc( n, sizeof(new2old[0]) ); - mark = (S_CHAR *) inchi_calloc( n, sizeof(mark[0]) ); - for ( i = 0; i < n; i ++ ) { - /* forward transposition: at[i] -> at[old2new[i]] position */ - old2new[i] = m = nCanonRank[i]-1; - /* forward transposition: at[new2old[i]] -> at[i] position */ - new2old[m] = i; - } - /* rearrange input data according to the new numbering */ - RenumberTheGraph( n, NeighList, old2new, new2old, mark, 1 ); - RearrangeAtRankArray ( n, pRankStack[0], new2old, mark, 2 ); - RenumberAtNumbArray ( n, pRankStack[1], old2new ); - /* make sure the atom numbers are sorted */ - for ( i = k = 0, r1 = pRankStack[0][pRankStack[1][i]]; i < n; r1 = r2) { - for ( j = i++; i < n && r1 == (r2 = pRankStack[0][pRankStack[1][i]]); i ++ ) - ; - if ( i - j > 1 ) { - k += insertions_sort_AT_RANK( pRankStack[1]+j, i-j ); - } - } - - ret = CanonGraph( num_atoms, num_at_tg, num_max, bDigraph, NeighList, (Partition *)pRankStack, - nSymmRank, nCanonRank, pCS->nPrevAtomNumber, pCD, pCC, &pzb_rho, NULL ); - - pCS->nLenLinearCT = pCD->nLenLinearCT; - pCS->lNumBreakTies = pCC->lNumBreakTies; - pCS->lNumDecreasedCT = pCC->lNumDecreasedCT; - pCS->lNumRejectedCT = pCC->lNumRejectedCT; - pCS->lNumEqualCT = pCC->lNumEqualCT; - pCS->lNumTotCT = pCC->lNumTotCT; - - - /* compare the connection tables */ - cmp1 = nLenCt0 - pCS->nLenLinearCT; - cmp2 = memcmp( pCt0, pCS->LinearCT, pCS->nLenLinearCT*sizeof(pCt0[0])); -#ifdef _DEBUG - if ( cmp1 || cmp2 ) { - int stop = 1; - } -#endif - /**********************************************************/ - /* rearrange numbering back to the original numbering */ - /**********************************************************/ - /* restore the input data to its original numbering */ - RenumberTheGraph( n, NeighList, new2old, old2new, mark, 4 ); - RearrangeAtRankArray ( n, pRankStack[0], old2new, mark, 8 ); - RenumberAtNumbArray ( n, pRankStack[1], new2old ); - /* rearrange the output data to the original numbering */ - RearrangeAtRankArray ( n, nCanonRank, old2new, mark, 16 ); - RenumberAtNumbArray ( n, pCS->nPrevAtomNumber, new2old ); - RearrangeAtRankArray ( n, nSymmRank, old2new, mark, 32 ); - - /* free memory */ - CTableFree( pzb_rho ); - if ( pzb_rho ) { - inchi_free( pzb_rho ); - } - inchi_free( old2new ); - inchi_free( new2old ); - inchi_free( mark ); - inchi_free( pCt0 ); - - return ret; -} -#endif /* } */ - -#define QZFIX_OK(X) ((X)<=0) - -int GetOneAdditionalLayer( CANON_DATA *pCD, ConTable *pzb_rho_fix ) -{ - int nLastLayer = -1, nNumLast = 0, nLayer = 0; - - if ( !pCD || !pzb_rho_fix ) { - return 0; - } - - nLayer ++; /* 1 */ - if ( pCD->NumH && !pzb_rho_fix->NumH ) { - nLastLayer = nLayer; - nNumLast ++; - } - nLayer ++; /* 2 */ - if ( pCD->nLenCTAtOnly < pCD->nLenLinearCT && pzb_rho_fix->nLenCTAtOnly == pzb_rho_fix->lenCt ) { - nLastLayer = nLayer; - nNumLast ++; - } - nLayer ++; /* 3 */ - if ( pCD->NumHfixed && !pzb_rho_fix->NumHfixed ) { - nLastLayer = nLayer; - nNumLast ++; - } - nLayer ++; /* 4 */ - if ( pCD->iso_sort_key && !pzb_rho_fix->iso_sort_key ) { - nLastLayer = nLayer; - nNumLast ++; - } - /* - nLayer ++; // 5 - if ( pCD->nLenCTAtOnly < pCD->nLenLinearCT && pCD->iso_sort_key && - (pzb_rho_fix->nLenCTAtOnly == pzb_rho_fix->lenCt || !pzb_rho_fix->iso_sort_key ) ) { - nLastLayer = nLayer; - nNumLast ++; - } - */ -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - nLayer ++; /* 6 */ - if ( pCD->iso_sort_key_Hfixed && !pzb_rho_fix->iso_sort_key_Hfixed ) { - nLastLayer = nLayer; - nNumLast ++; - } -#endif - if ( 1 == nNumLast ) { - return nLastLayer; - } - return 0; -} - - -/*#define QZFIX_OK(X) (!(X))*/ - -/********************** CanonGraph ************************************* - * A naive implementation of graph canonical numbering algorithm * - * from "Practical Graph Isomorphism" by Brendan D. McKay, * - * Congressus Numerantium, Vol. 30 (1981), pp. 45 - 87. * - * Note: Several typos fixed, added chem. struct. specifics * - ***********************************************************************/ - -/* on entry: pi[0] is equitable */ -/*******************************************************10/21/2003****** - * Later add optimization: if Aut(G) <= Aut(G0) due to some additional * - * layer of coloring applied to G and the following is known about G0: * * - * * - * 0) canonical numbering of G should be same as that of G0 * - * * - * 1) canonical numbering as v= v0[n] {vertex number v from * - * G0 canonical number n) * - * 2) orbits of Aut(G0) as UnorderedPartition theta0 * - * * - * then when choosing next v[i] for refining the partition consider * - * only vertices from the Aut(G0) orbit of V(i), that is, only such * - * v[i] that: * - * * - * GetUnorderedPartitionMcrNode( &theta0, v[i] ) == * - * GetUnorderedPartitionMcrNode( &theta0, v0[i] ) * - ***********************************************************************/ -int CanonGraph( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) -{ - /* bDigraph != 0 - means consider edges from atoms to t-groups - as directed, that is, do not include - t-group ranks in comparing neighbors - when refining partition - */ - - /* Always set - lab = true - dig = true - */ - - /* in the comments: - m = |zeta| - r = |rho| - - m < n or r < n in case pi[k] in P (i.e. satisfies Lemma 2.25) - - Just after passing point B: - =========================== - K = k-1 - wi = v[i], i = 1..K - Gamma(0) = Gamma = Aut(G)pi - Gamma(i) = Gamma(w1,w2,...,wi) pointwise stabilizer for i=1..K - zeta is a terminal node => - the coarsest equitable partition that fixes w1,...,wK is discrete => - Gamma(K)=1 - At point A only: - index = |Gamma(k-1)|/|Gamma(k)| - At points A and B: - size = |Gamma(k-1)| - theta = theta(Gamma(k-1)); - Gamma(k-1) = , where Y is the set of all automprhisms output up - to the present stage (in Step 10 = L10 ) - |Y| <= n - |theta| - */ - - AT_RANK *pCt = pCD->LinearCT; - /*int nMaxLenCt = pCD->nMaxLenLinearCT;*/ - int *nLenCt = &pCD->nLenLinearCT; - CANON_DATA *pCD1 = pCD; - - int i, k, k2, index, l, ok, ret=0, res; - int t_Lemma; /* hh: if pi[k-1] satisfies Lemma 2-25 then - t_Lemma = min{i| i=1..k && pi[i-1] satisfies Lemma 2-25}*/ - /* otherwise t_Lemma = k --> here this is always the case */ - int t_eq_zeta; /* ht: min{i|i=1..m && all terminal modes descended from or equal - to zeta(i) have been shown to be equivalent}. */ - int h_zeta; /* h: the longest common ancestor of zeta and nu is nu(h_zeta) */ - int h_rho; /* hb: the longest common ancestor of rho and nu is nu(h_rho) */ - int hz_rho; /* hzb: max{i|i=1..min(k,r) && Lambda(G,pi,nu(i)) == Lambda(G,pi,rho(i))} */ - int hz_zeta; /* hzf: max{i|i=1..min(k,m) && Lambda(G,pi,nu(i)) == Lambda(G,pi,zeta(i))} */ - int qzb_rho; /* Ct(Lambda[k]) - Ct(rho[k]) */ - double size; /* |Aut(G)| */ - int nNumLayers = (NULL != pCD->NumH) + (NULL != pCD->NumHfixed) + - /* (bDigraph && pCD->nLenLinearCT > pCD->nLenCTAtOnly)*/ /* ??? tautomeric */ - (NULL != pCD->iso_sort_key) -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - + (NULL != pCD->iso_sort_key_Hfixed) -#endif - ; - int dig = (bDigraph || nNumLayers); - int bSplitTautCompare = (bDigraph || nNumLayers); /* compare taut. H and tgroups connections after H */ - /* digraph: 1=>do not use Lemma 2.25, 0 => use */ - int lab = 1; /* label: 1=>find canonical numbering; - 0=>do not find canonical numbering, do not use rho */ - int r; /* |rho| */ - int bZetaEqRho = lab; - int bZetaIsomorph; - - long lNumEqlZeta; - - - const int L = MAX_SET_SIZE; - UnorderedPartition theta, theta_from_gamma; - Cell *W; /* W[i] is the first non-trivial cell of pi[i+1] */ - Node *v; /* v[i] is in W[i] to create T(G,pi,nu[i+1]) */ - Node tvc, tvh; - S_CHAR *e, *qzb=NULL; /* qzb = NULL always */ - /* current node CT */ - ConTable Lambda; - /* first leaf CT */ - ConTable zf_zeta; /* Ct for zeta, the first discovered terminal node */ - /* best leaf/node CT: find the greatest pzb_rho possibly subject to pzb_rho[k] <= pzb_rho_fix[k] condition */ - ConTable *pzb_rho = NULL; /* Ct for rho, the best discovered terminal node */ - /* fixed input CT: for all k pzb_rho[k] <= pzb_rho_fix[k]; at the end pzb_rho == pzb_rho_fix */ - ConTable *pzb_rho_fix = (pp_zb_rho_inp && *pp_zb_rho_inp)? *pp_zb_rho_inp:NULL; - - NodeSet Omega; /* MAX_SET_SIZE */ - NodeSet Phi; /* MAX_SET_SIZE */ - NodeSet cur_nodes; /* 1 each */ - Transposition gamma; - Partition zeta; /* the first discovered terminal node */ - Partition rho; /* the best discovered terminal node */ - int nNumFoundGenerators=0; - int qzb_rho_fix = 0; - int hzb_rho_fix = 0; - int bRhoIsDiscrete = 1; - kLeast kLeast_rho[MAX_LAYERS]; - kLeast kLeast_rho_fix[MAX_LAYERS]; - int nOneAdditionalLayer; - int pzb_rho_fix_reached = 0; - int L_rho_fix_prev = 0, I_rho_fix_prev=-1, k_rho_fix_prev=0; - - /* Note: Layered comparison should be consistent, especially in layer numbers. - Layered comparison is implemented in: - CtFullCompare() - CtPartCompare() - GetOneAdditionalLayer() - - The partial comparison results in kLeast[] are used in - CtFullCompareLayers() - CtPartCompareLayers() - CtCompareLayersGetFirstDiff() - UpdateCompareLayers() - */ - nOneAdditionalLayer = GetOneAdditionalLayer( pCD1, pzb_rho_fix ); - - - /* next 2 lines for debug only */ - /* num_g++; */ - /* WriteGraph( G, n_tg, num_g, "V:\\IChI_v10\\Gordon-Graphs\\hard\\k06g08v312-alt.dre", "a+" ); */ - - /* memory allocation */ - - if ( 0 > SetBitCreate() ) { - return -1; - } - if ( pzb_rho_fix && pzb_rho_fix->nLenCTAtOnly != pCD->nLenCTAtOnly ) { - /* consistency check */ - return -2; - } - ok = 1; - - - ok &= UnorderedPartitionCreate( &theta, n_tg ); - ok &= UnorderedPartitionCreate( &theta_from_gamma, n_tg ); - - ok &= (NULL != (W = (Cell*)inchi_calloc(n_tg, sizeof(W[0])))); - ok &= (NULL != (v = (Node*)inchi_calloc(n_tg, sizeof(v[0])))); - ok &= (NULL != (e = (S_CHAR*)inchi_calloc(n_tg, sizeof(e[0])))); - -/* - ok &= (NULL != (v = (Node*)inchi_calloc(n_tg, sizeof(W[0])))); - ok &= (NULL != (e = (S_CHAR*)inchi_calloc(n_tg, sizeof(W[0])))); -*/ - - /* ok &= (NULL != (qzb = (S_CHAR*)calloc(n_tg, sizeof(W[0])))); */ - ok &= CTableCreate( &Lambda, n, pCD ); - ok &= CTableCreate( &zf_zeta, n, pCD ); - ok &= ( (pzb_rho = (ConTable *)inchi_calloc( 1, sizeof( *pzb_rho ) ) ) && - CTableCreate( pzb_rho, n, pCD ) ); - - ok &= NodeSetCreate( &Omega, n_tg, MAX_SET_SIZE ); - ok &= NodeSetCreate( &Phi, n_tg, MAX_SET_SIZE ); - ok &= NodeSetCreate( &cur_nodes, n_tg, 1 ); - - ok &= PartitionCreate( &zeta, n_tg); - ok &= PartitionCreate( &rho, n_tg); - ok &= TranspositionCreate( &gamma, n_tg ); - - INCHI_HEAPCHK - - -/*L1:*/ - k = 1; - size = 1.0; - h_zeta = hz_rho = index = l = 0; - - if ( !ok ) { - goto exit_function; /* initialization failed */ - } - - UnorderedPartitionMakeDiscrete(&theta, n_tg); - t_Lemma = 2; - - pCC->lNumBreakTies = 0; - pCC->lNumDecreasedCT = 0; - pCC->lNumRejectedCT = 0; - pCC->lNumEqualCT = 1; - pCC->lNumTotCT = 0; - lNumEqlZeta = 1; - - hzb_rho_fix = 1; - - memset( kLeast_rho, 0, sizeof(kLeast_rho) ); - memset( kLeast_rho_fix, 0, sizeof(kLeast_rho_fix) ); - - if ( PartitionIsDiscrete( &pi[k-1], n_tg ) ) { - /* added the following 3 lines to the original to create Ct */ - PartitionCopy( &rho, &pi[k-1], n_tg ); - CtPartFill( G, pCD, &pi[k-1], pzb_rho, 1, n, n_tg ); - CtPartInfinity( pzb_rho, qzb, 2 ); - pCC->lNumTotCT ++; - r = k; - /* goto L18; */ - goto exit_function; - } - if ( !dig && PartitionSatisfiesLemma_2_25( &pi[0], n ) ) - t_Lemma = 1; - /* - PartitionGetFirstCell( &pi[k-1], &W[k-1], k, n ); - v[k-1] = CellGetMinNode( &pi[k-1], &W[k-1], 0, pCD1 ); - CtPartClear( &Lambda, 1 ); - e[k-1] = 0; - */ - CtPartClear( &Lambda, 1 ); - INCHI_HEAPCHK - -/* L2: reach the first leaf and save it in zeta and rho */ - while( k ) { - /* the two next lines intentionally switched */ - /* Create equitable partition in pi[k] */ - PartitionGetFirstCell( &pi[k-1], W, k, n ); - v[k-1] = CellGetMinNode( &pi[k-1], &W[k-1], 0, pCD1 ); - e[k-1] = 0; - if ( dig || !PartitionSatisfiesLemma_2_25(&pi[k-1], n) ) - t_Lemma = k+1; - /* e[k-1] = 0; */ - { Node vv = v[k-1]; - if ( 0 > (ret=PartitionColorVertex( G, &pi[k-1], vv /*v[k-1]*/, n, n_tg, n_max, bDigraph, 0 )) ) { - goto exit_error; - }} - pCC->lNumBreakTies ++; - k ++; - CtPartFill( G, pCD, &pi[k-1], &Lambda, k-1, n, n_tg ); - /* return -1; *//* debug only */ - /* if(h_zeta==0)goto L5; L5: */ - /* the first terminal node has not been reached yet */ - /* search for the predefined numbering */ - if ( pzb_rho_fix && QZFIX_OK(qzb_rho_fix) ) { - qzb_rho_fix = CtPartCompare( &Lambda, pzb_rho_fix, qzb, kLeast_rho_fix, k-1, 1, bSplitTautCompare ); - if ( QZFIX_OK(qzb_rho_fix) ) { - hzb_rho_fix = k; - } - } - - if ( lab && QZFIX_OK(qzb_rho_fix) ) /* DCh */ - CtPartCopy( pzb_rho, &Lambda, k-1 ); - CtPartCopy( &zf_zeta, &Lambda, k-1 ); - /*goto L4; L4:*/ - if ( PartitionIsDiscrete( &pi[k-1], n ) ) { - break; /* goto L7; */ - } - /* goto L2; */ - } - pCC->lNumTotCT ++; - /* L7; L7: */ - /* if ( h_zeta == 0 ) goto L18; L18:*/ - h_zeta = t_eq_zeta = hz_zeta = k; - CtPartInfinity( &zf_zeta, NULL, k ); - /******************** <<<===== B **************************/ - PartitionCopy( &zeta, &pi[k-1], n_tg ); - if ( lab ) { - if ( pzb_rho_fix ) { - if ( 0 == qzb_rho_fix ) { - qzb_rho_fix = CtFullCompare( &Lambda, pzb_rho_fix, 1, bSplitTautCompare ); - if ( qzb_rho_fix > 0 ) { - hzb_rho_fix = 1; - } - } - if ( hzb_rho_fix > 1 ) { - PartitionCopy( &rho, &pi[hzb_rho_fix-1], n_tg ); - /*CtPartInfinity( pzb_rho, qzb, k );*/ - } - hz_rho = h_rho = hzb_rho_fix; - bRhoIsDiscrete = (hzb_rho_fix == k); - if ( bRhoIsDiscrete ) { - CtPartInfinity( pzb_rho, qzb, k ); - pzb_rho_fix_reached = !qzb_rho_fix; - CtCompareLayersGetFirstDiff( kLeast_rho_fix, nOneAdditionalLayer, - &L_rho_fix_prev, &I_rho_fix_prev, &k_rho_fix_prev ); - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; - } -#endif - } else { - PartitionCopy( &rho, &pi[k-1], n_tg ); - hz_rho = h_rho = k; - CtPartInfinity( pzb_rho, qzb, k ); - } - qzb_rho = 0; - } - r = k; - v[k-1] = INFINITY; /* DCh */ - CellMakeEmpty( W, k ); /* DCh */ - k --; - goto L13; - - -L2: - /* the two next lines intentionally switched */ - /* Create equitable partition in pi[k] */ - if ( 0 > (ret=PartitionColorVertex( G, &pi[k-1], v[k-1], n, n_tg, n_max, bDigraph, 0 )) ) { - goto exit_error; - } - pCC->lNumBreakTies ++; - k ++; - CtPartFill( G, pCD, &pi[k-1], &Lambda, k-1, n, n_tg ); - e[k-1] = 0; /* moved */ - v[k-1] = INFINITY; /* added by DCh. */ - CellMakeEmpty( W, k ); /* DCh */ - - if ( hz_zeta == k-1 && 0 == CtPartCompare( &Lambda, &zf_zeta, NULL, NULL, k-1, 0, bSplitTautCompare ) ) { - hz_zeta = k; /* max{k|Lambda(G,pi,nu(k))==Lambda(G,pi,zeta) } */ - } /* added */ - - /* -- old code --- - if ( pzb_rho_fix && QZFIX_OK(qzb_rho_fix) ) { - qzb_rho_fix = CtPartCompare( &Lambda, pzb_rho_fix, qzb, kLeast_rho_fix, k-1, 1, bSplitTautCompare ); - if ( QZFIX_OK(qzb_rho_fix) ) { - hzb_rho_fix = k; - } else { - pCC->lNumRejectedCT ++; - } - } - */ - - /* --- new code ---*/ - if ( pzb_rho_fix && !qzb_rho_fix ) { - qzb_rho_fix = CtPartCompare( &Lambda, pzb_rho_fix, qzb, kLeast_rho_fix, k-1, 1, bSplitTautCompare ); - if ( !qzb_rho_fix && bRhoIsDiscrete ) { - qzb_rho_fix = CtPartCompareLayers( kLeast_rho_fix, L_rho_fix_prev, nOneAdditionalLayer ); - -#if ( FIX_ChCh_CONSTIT_CANON_BUG == 1 ) - if ( qzb_rho_fix ) { - int L_rho_fix_diff = abs(qzb_rho_fix)-1; - if ( L_rho_fix_diff < L_rho_fix_prev || - L_rho_fix_diff == L_rho_fix_prev && kLeast_rho_fix[L_rho_fix_diff].i < I_rho_fix_prev ) { - qzb_rho_fix = L_rho_fix_diff+1; /* positive difference will be rejected */ - } - } -#endif - -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - if ( qzb_rho_fix ) { - int stop = 1; /* debug only */ - } -#endif - } - if ( !QZFIX_OK(qzb_rho_fix) ) { - pCC->lNumRejectedCT ++; - } - } - if ( pzb_rho_fix && QZFIX_OK(qzb_rho_fix) ) { - hzb_rho_fix = k; - } - /* if (!lab) goto L3; */ - if ( lab && QZFIX_OK(qzb_rho_fix) ) { - /* once the difference has been found it is meaningful as long as k increments */ - /* cur_qzb2 = CtPartCompare( &Lambda, pzb_rho, qzb, k-1 ); */ /* rho compare */ - if ( hz_rho == k-1 && !qzb_rho && bRhoIsDiscrete ) { - int qzb_rho_temp = 0; - qzb_rho = CtPartCompare( &Lambda, pzb_rho, qzb, kLeast_rho, k-1, 0, bSplitTautCompare ); - /* old code */ - if ( !qzb_rho && pzb_rho_fix_reached && - nOneAdditionalLayer && 0 > kLeast_rho[nOneAdditionalLayer].k ) { - qzb_rho_temp = -(nOneAdditionalLayer+1); - /* qzb_rho = -(nOneAdditionalLayer+1); *//* early rejection */ - } - /* new code */ - if ( !qzb_rho && bRhoIsDiscrete ) { - qzb_rho = CtPartCompareLayers( kLeast_rho, L_rho_fix_prev, 0 ); -#if ( FIX_ChCh_CONSTIT_CANON_BUG == 1 ) - if ( qzb_rho ) { - int L_rho_diff = abs(qzb_rho)-1; - if ( L_rho_diff < L_rho_fix_prev || - L_rho_diff == L_rho_fix_prev && kLeast_rho[L_rho_diff].i < I_rho_fix_prev ) { - qzb_rho = -(L_rho_diff+1); /* negative difference will be rejected */ - } - } -#endif - } - /* compare old results to new */ -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - if ( qzb_rho_temp && qzb_rho_temp != qzb_rho ) { - int stop = 1; /* */ - } -#endif - if ( !qzb_rho ) { - hz_rho = k; - } else - if ( qzb_rho < 0 ) { - pCC->lNumRejectedCT ++; - } - } - if ( qzb_rho > 0 || !qzb_rho && !bRhoIsDiscrete ) { - /* found better rho */ - if ( !nNumLayers ) { - CtPartCopy( pzb_rho, &Lambda, k-1 ); - } - } - } -/*L3:*/ - /*if ( hz_rho == k || (lab && qzb_rho >= 0 ) )*/ - /*if ( hz_zeta == k || hz_rho == k || (lab && qzb_rho >= 0 ) ) goto L4; else goto L6;*/ - if ( hz_zeta == k || hz_rho == k || (lab && qzb_rho >= 0 && QZFIX_OK(qzb_rho_fix) ) ) { - /*L4: check for possible isomorphism or found a better rho */ - if ( PartitionIsDiscrete( &pi[k-1], n ) ) { - pCC->lNumTotCT ++; - goto L7; - } - PartitionGetFirstCell( &pi[k-1], W, k, n ); - v[k-1] = CellGetMinNode( &pi[k-1], &W[k-1], 0, pCD1 ); - if ( !dig && PartitionSatisfiesLemma_2_25(&pi[k-1], n) ) { - ; /* found additional isomprphism */ - } else { - t_Lemma = k+1; - } - e[k-1] = 0; /* created new cell W[k-1] */ - goto L2; - } -L6: - /* a better rho or no good node was found at this level; return to smaller k */ - k2 = k; - k = inchi_min(t_Lemma-1, inchi_max(t_eq_zeta-1, hz_rho)); - if ( k2 == t_Lemma ) - goto L13; - /* store isomorphism found from Lemma 2.25. should be dig=0 !!! */ - if ( dig ) { -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - int stop = 1; -#endif - goto L13; - } - l = inchi_min(l+1, L); - PartitionGetMcrAndFixSet( &pi[t_Lemma-1], &Omega, &Phi, n_tg, l ); - goto L12; -L7: - /* from L4: pi[k-1] is discrete */ - if ( h_zeta == 0 ) { - /*goto L18;*/ /* error. the first T(nu) leaf was found */ - ret = CT_CANON_ERR; - goto exit_error; - } - if ( k != hz_zeta ) - goto L8; - /* here zeta^gamma == nu */ - /* if ( G^gamma == G ) goto L10; */ - if ( 0 == (res=CtFullCompare( &Lambda, &zf_zeta, 0, bSplitTautCompare )) ) { - PartitionGetTransposition( &zeta, &pi[k-1], n_tg, &gamma ); - bZetaIsomorph = 1; /* for testing only */ - lNumEqlZeta ++; - goto L10; - } else -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - { - int stop = 1; - } -#endif - /* !!! we should never come here !!! */ - if ( !nNumLayers ) { - ret = -2; - goto exit_error; - } - -L8: /* here nu is discrete: check rho for being a bettere leaf or isomorphism */ - /*if ( !lab || qzb_rho < 0 || !QZFIX_OK(qzb_rho_fix) )*/ - if ( !lab || qzb_rho < 0 && ( !pzb_rho_fix || qzb_rho_fix > 0 ) ) - goto L6; - if ( pzb_rho_fix && kLeast_rho_fix && 0 == qzb_rho_fix ) { - /* check for the rejection condition: Lambda > zb_rho_fix */ - if ( kLeast_rho_fix ) { - int qzb_rho_fix_alt; - qzb_rho_fix = CtFullCompareLayers( kLeast_rho_fix ); - /* for debug only */ - qzb_rho_fix_alt = CtFullCompare( &Lambda, pzb_rho_fix, 1, bSplitTautCompare ); - if ( qzb_rho_fix != qzb_rho_fix_alt ) { -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - int stop = 1; -#endif - qzb_rho_fix = qzb_rho_fix_alt; - } - /* end debug */ - } else { - qzb_rho_fix = CtFullCompare( &Lambda, pzb_rho_fix, 1, bSplitTautCompare ); - } - if ( !pzb_rho_fix_reached ) { - pzb_rho_fix_reached = !qzb_rho_fix; - } - if ( 0 < qzb_rho_fix ) { - /* Lambda > pzb_rho_fix, ignore this node */ - /* hzb_rho_fix = min( hzb_rho_fix, hz_rho ); */ /* ??? */ - qzb_rho_fix = 0; - goto L6; - } - qzb_rho_fix = 0; - } - - if ( qzb_rho < 0 ) - goto L6; - if ( qzb_rho > 0 || !bRhoIsDiscrete ) - goto L9; /* note: p67 says k > PartitionSize( &rho, n ) */ - if ( k < r ) { - goto L9; /* cannot understand it... */ - } - - /* !!! we should never come here if G(nu) != G(rho): CtPartCompare must be enough !!! */ - - /* if ( G(nu) > G(rho) ) goto L9; */ - if ( kLeast_rho ) { - int cur_qzb_alt; - qzb_rho = CtFullCompareLayers( kLeast_rho ); - /* for debug only */ - cur_qzb_alt = CtFullCompare( &Lambda, pzb_rho, 0, bSplitTautCompare ); - if ( qzb_rho != cur_qzb_alt ) { -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - int stop = 1; -#endif - qzb_rho = cur_qzb_alt; - } - /* end debug */ - } else { - qzb_rho = CtFullCompare( &Lambda, pzb_rho, 0, bSplitTautCompare ); - } - /* qzb_rho difference can be due to layers 1..MAX_LAYERS-1 only */ - if ( 0 < qzb_rho ) { - /* CtFullCompare( &Lambda, pzb_rho, 0, bSplitTautCompare ); */ - qzb_rho = 0; - goto L9; - } - /* if ( G(nu) < G(rho) ) goto L6; */ - if ( 0 > qzb_rho ) { - qzb_rho = 0; - goto L6; - } - /* nu^gamma == rho */ - if ( r != k ) { /* if() is for debug only */ - r = k; - } - PartitionGetTransposition( &pi[k-1], &rho, n_tg, &gamma ); - bZetaIsomorph = 0; /* DCh */ - pCC->lNumEqualCT ++; - goto L10; -L9: - /* rho := nu; */ - PartitionCopy( &rho, &pi[k-1], n_tg ); - if ( nNumLayers ) { - CtFullCopy( pzb_rho, &Lambda ); - } - bZetaEqRho = 0; - qzb_rho = 0; - CtCompareLayersGetFirstDiff( kLeast_rho_fix, nOneAdditionalLayer, - &L_rho_fix_prev, &I_rho_fix_prev, &k_rho_fix_prev ); - memset( kLeast_rho, 0, sizeof(kLeast_rho) ); - h_rho = hz_rho = k; - CtPartInfinity( pzb_rho, qzb, k ); - pCC->lNumDecreasedCT ++; - pCC->lNumEqualCT = 1; - bRhoIsDiscrete = 1; - goto L6; - -L10: /* discrete pi[k-1] && G^gamma == G */ - - pCC->lNumEqualCT += bZetaEqRho || !(bZetaIsomorph || qzb_rho); - l = inchi_min(l+1, L); - /* Omega[l] := mcr(gamma); - Phi[l] := fix(gamma); - */ - TranspositionGetMcrAndFixSetAndUnorderedPartition( &gamma, &Omega, &Phi, n_tg, l, &theta_from_gamma ); - - /* - if ( theta(gamma) <= theta ) goto L11; - theta := theta v theta(gamma); - UnorderedPartitionJoin() returns 0 if theta_from_gamma is finer than theta, - which means no changes in theta: theta_from_gamma ^ theta == theta. - */ - if ( !UnorderedPartitionJoin( &theta_from_gamma, &theta, n_tg ) ) - goto L11; /* no new isomorphism found */ - /* Output gamma (it is the Aut(G) generator) -- omitted -- */ - nNumFoundGenerators ++; - /* if ( tvc in mcr(theta) ) goto L11; */ - if ( tvc == GetUnorderedPartitionMcrNode( &theta, tvc ) ) - goto L11; - k = h_zeta; - goto L13; -L11: - k = lab? h_rho : h_zeta; /***Changed*** originally was k = h_rho; */ -L12: - /* if ( e[k-1] == 1 ) */ -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - if ( e[k-1] == 1 && v[k-1] == INFINITY ) { - int stop = 1; /* testing only */ - } -#endif - if ( e[k-1] == 1 && v[k-1] != INFINITY ) { /* INFINITY for testing only */ - CellIntersectWithSet( &pi[k-1], &W[k-1], &Omega, l ); - } -L13: - - if ( UserAction && USER_ACTION_QUIT == (*UserAction)() || - ConsoleQuit && (*ConsoleQuit)() ) { - ret = CT_USER_QUIT_ERR; - goto exit_error; - } - if ( bInchiTimeIsOver(pCD->ulTimeOutTime) ) { - ret = CT_TIMEOUT_ERR; - goto exit_error; - } - - if ( k == 0 ) - goto exit_function; /* stop */ - - if ( lab && k < h_rho ) { /***Added***/ - h_rho = k; - } - if ( k > h_zeta ) { - if ( v[k-1] == INFINITY ) {/*** Added by DCh for testing only ****/ - k --; - goto L13; - } - goto L17; - } - if ( k == h_zeta ) - goto L14; - h_zeta = k; - tvc = tvh = CellGetMinNode( &pi[k-1], &W[k-1], 0, pCD1 ); -L14: - /* if v[k] and tvh are in the same cell of theta then index ++ */ - if ( GetUnorderedPartitionMcrNode( &theta, v[k-1] ) == - GetUnorderedPartitionMcrNode( &theta, tvh ) ) { - index ++; - } - v[k-1] = CellGetMinNode( &pi[k-1], &W[k-1], v[k-1], pCD1 ); - - if ( v[k-1] == INFINITY ) - goto L16; - if ( v[k-1] != GetUnorderedPartitionMcrNode( &theta, v[k-1] ) ) - goto L14; -L15: - t_Lemma = inchi_min(t_Lemma, k+1); - hz_zeta = inchi_min(hz_zeta, k); -/* - if ( lab && hz_rho >= k ) { - hz_rho = k; - qzb_rho = 0; - } -*/ - if ( lab ) { - if ( hz_rho >= k /*-1*/ ) - qzb_rho = 0; - if ( hz_rho > k ) - hz_rho = k; - UpdateCompareLayers( kLeast_rho, hz_rho ); - } - if ( pzb_rho_fix ) { - if ( hzb_rho_fix >= k /*-1*/ ) - qzb_rho_fix = 0; - if ( hzb_rho_fix > k ) - hzb_rho_fix = k; - UpdateCompareLayers( kLeast_rho_fix, hzb_rho_fix ); - } - - goto L2; -L16: - if ( t_eq_zeta == k+1 && index == CellGetNumberOfNodes( &pi[k-1], &W[k-1] ) ) - t_eq_zeta = k; - size *= (double)index; - /******************** <<<===== A **************************/ - /* passed K times after passing point A. At these passes - k = K, K-1, ..., 1 in this order - */ - index = 0; - k --; - goto L13; -L17: - /* if ( e[k-1] == 0 ) */ -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - if ( e[k-1] == 0 && v[k-1] == INFINITY ) { /* testing only */ - int stop = 1; /* */ - } -#endif - /* - if ( e[k] == 0 set W[k] = Intersection(W[k], Omega[i]) for each i = 1..l, - such that {v[1]..v[k-1]} in Phi[i] - */ - if ( e[k-1] == 0 && v[k-1] != INFINITY ) /* Added v[k-1]!=... DCh */ - { - NodeSetFromVertices( &cur_nodes, 1, v, k-1 ); - for ( i = 1; i <= l; i ++ ) { - if ( AllNodesAreInSet( &cur_nodes, 1, &Phi, i ) ) { - CellIntersectWithSet( &pi[k-1], &W[k-1], &Omega, i ); - } - } - } - e[k-1] = 1; - v[k-1] = CellGetMinNode( &pi[k-1], &W[k-1], v[k-1], pCD1 ); - if ( v[k-1] != INFINITY ) - goto L15; - k --; - goto L13; -/* L18: see above */ - -exit_function: - /* CtPartFill( G, pCD, &rho, pzb_rho, 1, n, n_tg ); */ - if ( !bRhoIsDiscrete ) { - ret = CT_CANON_ERR; - goto exit_error; - } - if ( pzb_rho_fix ) { - qzb_rho_fix = CtFullCompare( pzb_rho_fix, pzb_rho, 1, bSplitTautCompare ); - if ( qzb_rho_fix ) { - ret = CT_CANON_ERR; - goto exit_error; - } - } - /* SymmRank */ - memset( nSymmRank, 0, n_tg * sizeof(nSymmRank[0]) ); - for ( i = 0; i < n_tg; i ++ ) { - k = rho.AtNumber[i]; - k2 = (int)GetUnorderedPartitionMcrNode( &theta, (AT_NUMB)(k+1) ) - 1; - if ( !nSymmRank[k2] || nSymmRank[k2] > rho.Rank[k] ) { - nSymmRank[k2] = rho.Rank[k]; - } - } - for ( i = 0; i < n_tg; i ++ ) { - k = rho.AtNumber[i]; - k2 = (int)GetUnorderedPartitionMcrNode( &theta, (AT_NUMB)(k+1) ) - 1; - nSymmRank[k] = nSymmRank[k2]; - } - /* CanonRank, nAtomNumberCanon */ - memcpy( nCanonRank, rho.Rank, n_tg * sizeof(nCanonRank[0]) ); - memcpy( nAtomNumberCanon, rho.AtNumber, n_tg * sizeof(nAtomNumberCanon[0]) ); - /* LinearCT */ - *nLenCt = pzb_rho->lenCt-1; - if ( pCt ) { - memcpy( pCt, pzb_rho->Ctbl, *nLenCt*sizeof(pCt[0]) ); - } - pCC->lNumTotCT = pCC->lNumDecreasedCT + pCC->lNumRejectedCT + pCC->lNumEqualCT; - pCC->dGroupSize = size; - pCC->lNumGenerators = nNumFoundGenerators; - pCC->lNumStoredIsomorphisms = l; - /* Note: check nNumFoundGenerators */ - - if ( pp_zb_rho_out && !*pp_zb_rho_out ) { - *pp_zb_rho_out = pzb_rho; - pzb_rho = NULL; - } - -exit_error: - INCHI_HEAPCHK - - UnorderedPartitionFree( &theta ); - UnorderedPartitionFree( &theta_from_gamma ); - if ( W ) inchi_free( W ); - if ( v ) inchi_free( v ); - if ( e ) inchi_free( e ); - if ( qzb ) inchi_free( qzb ); - CTableFree( &Lambda ); - CTableFree( &zf_zeta ); - if ( pzb_rho ) { - CTableFree( pzb_rho ); - inchi_free( pzb_rho ); - pzb_rho = NULL; - } - -/* CTableFree( &zf_zeta2 ); */ - - - NodeSetFree( &Omega ); - NodeSetFree( &Phi ); - /* NodeSetFree( &mcr_theta, n, 1 ); */ - NodeSetFree( &cur_nodes ); - - PartitionFree( &zeta ); -/* PartitionFree( &zeta2 ); */ - PartitionFree( &rho ); - TranspositionFree( &gamma ); - - - return ret; -} -/********************************************************************************************** - * SetInitialRanks2: Set initial ranks in nRank according to pAtomInvariant[] values - * Make sure enough prines have been generated. - **********************************************************************************************/ -/* Upon exit: */ -/* nAtomNumber[i]: number (from 0) of an atom in the ith (from 0) position of the sorted order */ -/* nNewRank[i]: initial rank of the atom[i] based on atom invariants; from 1 to num_atoms */ -/* Return value: Number of different ranks */ -int SetInitialRanks2( int num_atoms, ATOM_INVARIANT2* pAtomInvariant2, AT_RANK *nNewRank, AT_RANK *nAtomNumber ) -{ - int i, nNumDiffRanks; - AT_RANK nCurrentRank; - - for ( i = 0; i < num_atoms; i++ ) - nAtomNumber[i] = (AT_RANK)i; - - /* global for qsort */ - pAtomInvariant2ForSort = pAtomInvariant2; - - qsort( nAtomNumber, num_atoms, sizeof(nAtomNumber[0]), CompAtomInvariants2 ); - - /* nNewRank[i]: non-decreading order; do not increment nCurrentRank */ - /* if consecutive sorted atom invariants are identical */ - - for ( i=num_atoms-1, nCurrentRank=nNewRank[nAtomNumber[i]] = (AT_RANK)num_atoms, nNumDiffRanks = 1; 0 < i ; i -- ) { - /* Note: CompAtomInvariants2Only() in following line implicitly reads pAtomInvariant2 pointed by pAtomInvariant2ForSort */ - if ( CompAtomInvariants2Only( &nAtomNumber[i-1], &nAtomNumber[i] ) ) { - nNumDiffRanks ++; - nCurrentRank = (AT_RANK)i; - } - nNewRank[nAtomNumber[i - 1]] = nCurrentRank; - } - - - return nNumDiffRanks; -} - -/****************************************************************************/ -void FillOutAtomInvariant2( sp_ATOM* at, int num_atoms, int num_at_tg, ATOM_INVARIANT2* pAtomInvariant, - int bIgnoreIsotopic, int bHydrogensInRanks, int bHydrogensFixedInRanks, - int bDigraph, int bTautGroupsOnly, T_GROUP_INFO *t_group_info ) -{ - int i, k, j, i_t_group; - /* tautomers */ - T_GROUP *t_group=NULL; - int num_t_groups = 0; - int num_tautomer_iso = 0; -#define ELEM_NAME_LEN 2 - char ChemElements[ELEM_NAME_LEN*NUM_CHEM_ELEMENTS+ELEM_NAME_LEN]; - char CurElement[ELEM_NAME_LEN + ELEM_NAME_LEN], *pCurElem; - int nNumChemElements = 0; - int nNumHydrogenAtoms = 0; - int nNumCarbonAtoms = 0; - memset( ChemElements, 0, sizeof(ChemElements) ); - memset( CurElement, 0, sizeof(CurElement) ); - nNumChemElements = 0; - - if ( num_at_tg > num_atoms && t_group_info ) { - t_group = t_group_info->t_group; - num_t_groups = t_group_info->num_t_groups; - num_tautomer_iso = t_group_info->bIgnoreIsotopic? 0 : T_NUM_ISOTOPIC; - } - - if ( !bTautGroupsOnly ) { - - for ( i = 0; i < num_atoms; i ++ ) { - if ( !strcmp( at[i].elname, "C" ) ) { - nNumCarbonAtoms ++; - } else - if ( !strcmp( at[i].elname, "H" ) || - !strcmp( at[i].elname, "D" ) || - !strcmp( at[i].elname, "T" ) ) { - nNumHydrogenAtoms ++; - } else { - CurElement[0] = at[i].elname[0]; - CurElement[1] = at[i].elname[1]? at[i].elname[1] : ' '; - if ( ! (pCurElem = strstr( ChemElements, CurElement ) ) ) { - strcat( ChemElements, CurElement ); - nNumChemElements ++; - } - } - } - if ( nNumChemElements > 1 ) { - qsort( ChemElements, nNumChemElements, ELEM_NAME_LEN, CompChemElemLex ); - } - if ( nNumCarbonAtoms ) { - if ( nNumChemElements ) { - memmove( ChemElements + ELEM_NAME_LEN, ChemElements, ELEM_NAME_LEN*nNumChemElements ); - } - ChemElements[0] = 'C'; - ChemElements[1] = ' '; - nNumChemElements ++; - } - if ( nNumHydrogenAtoms ) { - ChemElements[ ELEM_NAME_LEN*nNumChemElements ] = 'H'; - ChemElements[ ELEM_NAME_LEN*nNumChemElements+1 ] = ' '; - nNumChemElements ++; - } - - - /* general */ - for ( i = 0; i < num_atoms; i ++ ) { - memset( &pAtomInvariant[i], 0, sizeof(pAtomInvariant[0]) ); - CurElement[0] = at[i].elname[0]; - CurElement[1] = at[i].elname[1]? at[i].elname[1] : ' '; - pCurElem = strstr( ChemElements, CurElement ); - if ( pCurElem ) { - j = (pCurElem - ChemElements)/ELEM_NAME_LEN + 1; - } else { - j = nNumChemElements; /* must be D or T */ - } - /* at[i].hill_type = (U_CHAR) j; */ - pAtomInvariant[i].val[AT_INV_HILL_ORDER] = j; - - pAtomInvariant[i].val[AT_INV_NUM_CONNECTIONS] = at[i].valence; - if ( bHydrogensInRanks ) { - pAtomInvariant[i].val[AT_INV_NUM_H] = ((t_group && at[i].endpoint>0)? 0 : at[i].num_H); - } - if ( bHydrogensFixedInRanks ) { - pAtomInvariant[i].val[AT_INV_NUM_H_FIX] = ((t_group && at[i].endpoint>0)? at[i].num_H : 0); - } - if ( !bDigraph && t_group && (i_t_group = (int)at[i].endpoint-1) >= 0 && i_t_group < num_t_groups ) { - pAtomInvariant[i].val[AT_INV_NUM_TG_ENDPOINTS] = t_group[i_t_group].nNumEndpoints; - for ( j = 0; j < T_NUM_NO_ISOTOPIC; j ++ ) { - pAtomInvariant[i].val[AT_INV_TG_NUMBERS+j] = t_group[i_t_group].num[j]; - } - for ( j = 0; j < num_tautomer_iso; j ++ ) { - pAtomInvariant[i].val[AT_INV_TAUT_ISO+j] = t_group[i_t_group].num[j + T_NUM_NO_ISOTOPIC]; - } - } - pAtomInvariant[i].iso_sort_key = bIgnoreIsotopic? 0 : at[i].iso_sort_key; - } - } else { - /* fill tautomeric groups only */ - memset ( pAtomInvariant, 0, num_at_tg*sizeof(pAtomInvariant[0]) ); - } - /**************************************/ - /* tautomeric groups */ - /**************************************/ - for ( i = num_atoms; i < num_at_tg; i ++ ) { - - k = i - num_atoms; - memset( &pAtomInvariant[i], 0, sizeof(pAtomInvariant[0]) ); - if ( !t_group ) - continue; - /* make sure ranks of t-groups are larger than that of any atom */ - /* greater than for any real atom */ - pAtomInvariant[i].val[AT_INV_HILL_ORDER] = bTautGroupsOnly? num_at_tg : nNumChemElements+1; - /* greater than for any real atom */ - pAtomInvariant[i].val[AT_INV_NUM_CONNECTIONS] = MAXVAL+1; - if ( k < num_t_groups ) { - pAtomInvariant[i].val[AT_INV_NUM_TG_ENDPOINTS] = t_group[k].nNumEndpoints; - for ( j = 0; j < T_NUM_NO_ISOTOPIC; j ++ ) { - pAtomInvariant[i].val[AT_INV_TAUT_ISO+j] = t_group[k].num[j]; - } - for ( j = 0; j < num_tautomer_iso; j ++ ) { - pAtomInvariant[i].val[AT_INV_TAUT_ISO+j] = t_group[k].num[j + T_NUM_NO_ISOTOPIC]; - } - } - } -} -/*****************************************************************************/ -void CleanNumH( NUM_H *NumH, int len ) -{ - int i; - if ( NumH ) { - for ( i = 0; i < len; i ++ ) { - if ( NumH[i] == EMPTY_H_NUMBER ) { - NumH[i] = 0; - } else { - NumH[i] -= BASE_H_NUMBER; - } - } - } -} -/*****************************************************************************/ -int CleanCt( AT_RANK *Ct, int len ) -{ - if ( Ct && Ct[len] == EMPTY_CT ) { - Ct[len] = 0; - return 1; - } - return 0; -} -/*****************************************************************************/ -void CleanIsoSortKeys( AT_ISO_SORT_KEY * isk, int len ) -{ - int i; - if ( isk ) { - for ( i = 0; i < len; i ++ ) { - if ( isk[i] == EMPTY_ISO_SORT_KEY ) { - isk[i] = 0; - } - } - } -} -/*****************************************************************************/ -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) -void MergeCleanIsoSortKeys( AT_ISO_SORT_KEY * isk1, AT_ISO_SORT_KEY * isk2, int len ) -{ - int i; - AT_ISO_SORT_KEY k1, k2; - if ( isk1 && isk2 ) { - for ( i = 0; i < len; i ++ ) { - k1 = (isk1[i] == EMPTY_ISO_SORT_KEY)? 0 : isk1[i]; - k2 = (isk2[i] == EMPTY_ISO_SORT_KEY)? 0 : isk2[i]; - isk1[i] = k1 | k2; - } - } else - if ( isk1 ) { - CleanIsoSortKeys( isk1, len ); - } -} -#endif -#define FREE_CONTABLE(X) if (X) {CTableFree(X);inchi_free(X);} -#define FREE_ARRAY(X) if (X) inchi_free(X); -/*****************************************************************************/ -void DeAllocBCN( BCN *pBCN ) -{ - int i, k; - FTCN *ftcn; - if ( !pBCN ) - return; - if ( pBCN->pRankStack ) { - for ( i = 0; i < pBCN->nMaxLenRankStack; i ++ ) { - FREE_ARRAY( pBCN->pRankStack[i] ) - } - FREE_ARRAY( pBCN->pRankStack ) - } - for ( k = 0; k < TAUT_NUM; k ++ ) { - ftcn = pBCN->ftcn + k; - FreeNeighList( ftcn->NeighList ); - FREE_ARRAY( ftcn->LinearCt ) - PartitionFree( &ftcn->PartitionCt ); - FREE_ARRAY( ftcn->nSymmRankCt ) - FREE_ARRAY( ftcn->nNumHOrig ) - FREE_ARRAY( ftcn->nNumH ) - FREE_ARRAY( ftcn->nNumHOrigFixH ) - FREE_ARRAY( ftcn->nNumHFixH ) - PartitionFree( &ftcn->PartitionCtIso ); - FREE_ARRAY( ftcn->nSymmRankCtIso ) - FREE_ARRAY( ftcn->iso_sort_keys ) - FREE_ARRAY( ftcn->iso_sort_keysOrig ) - FREE_ARRAY( ftcn->iso_exchg_atnos ) - FREE_ARRAY( ftcn->iso_exchg_atnosOrig ) - } -} -#undef FREE_CONTABLE -#undef FREE_ARRAY - -/*****************************************************************************/ -#if ( bRELEASE_VERSION == 0 && FIND_CANON_NE_EQUITABLE == 1 ) -/* debug: find whether canonical equivalence is different from equitable partition */ -int bCanonIsFinerThanEquitablePartition( int num_atoms, sp_ATOM* at, AT_RANK *nSymmRank ) -{ - AT_RANK *nRank = NULL; - AT_RANK *nAtomNumber = NULL; - AT_RANK *nTempRank = NULL; - AT_RANK nCurSymm, nCurRank; - ATOM_INVARIANT2 *pAtomInvariant = NULL; - NEIGH_LIST *NeighList = NULL; - int nNumCurrRanks, i, is, ir, j; - long lCount; - int bIsNotSame = 0; - if ( at && nSymmRank ) { - if ( !(nRank = (AT_RANK*)inchi_calloc( num_atoms, sizeof(nRank[0]))) || - !(nAtomNumber = (AT_RANK*)inchi_calloc( num_atoms, sizeof(nAtomNumber[0]))) || - !(nTempRank = (AT_RANK*)inchi_calloc( num_atoms, sizeof(nTempRank[0]))) || - !(pAtomInvariant = (ATOM_INVARIANT2 *)inchi_calloc( num_atoms, sizeof(pAtomInvariant[0]))) - ) { - goto exit_err; - } - if ( !(NeighList = CreateNeighList( num_atoms, num_atoms, at, 0, NULL )) ) { - goto exit_err; - } - - FillOutAtomInvariant2( at, num_atoms, num_atoms, pAtomInvariant, 1 /*bIgnoreIsotopic*/, - 1 /*bHydrogensInRanks*/, 1 /*bHydrogensFixedInRanks*/, 0 /*bTaut=bDigraph*/, - 0 /* bTautGroupsOnly */, NULL /*t_group_info*/ ); - /* initial partitioning of a hydrogenless skeleton: create equitable partition (assign initial ranks) */ - nNumCurrRanks = SetInitialRanks2( num_atoms, pAtomInvariant, nRank, nAtomNumber ); - - lCount = 0; - /* make equitable partition in pBCN->pRankStack[0,1] */ - nNumCurrRanks = DifferentiateRanks2( num_atoms, NeighList, - nNumCurrRanks, nRank, - nTempRank, nAtomNumber, &lCount, 0 /* 0 means use qsort */ ); - /* at this point the equitable partition is in nRank; the order of atoms is in nAtomNumber*/ - /* compare */ - nCurSymm = nCurRank = 0; - for ( i = 0; i < num_atoms; i ++ ) { - j = (int)nAtomNumber[i]; - if ( nCurSymm != nSymmRank[j] ) { - nCurSymm = nSymmRank[j]; - is = i; - } - if ( nCurRank != nRank[j] ) { - nCurRank = nRank[j]; - ir = i; - } - if ( is != ir ) { - bIsNotSame = 1; - break; - } - } - } -exit_err: - if ( nRank ) - inchi_free( nRank ); - if ( nAtomNumber ) - inchi_free( nAtomNumber ); - if ( nTempRank ) - inchi_free( nTempRank ); - if ( pAtomInvariant ) - inchi_free( pAtomInvariant ); - if ( NeighList ) - FreeNeighList( NeighList ); - return bIsNotSame; -} -#endif -/*****************************************************************************/ -int GetBaseCanonRanking( int num_atoms, int num_at_tg, sp_ATOM* at[], - T_GROUP_INFO *t_group_info, ATOM_SIZES s[], BCN *pBCN, - struct tagInchiTime *ulTimeOutTime, int bFixIsoFixedH ) -{ - int ret = 0; - int iBase; /* base structure index, always valid; = TAUT_YES except special fully non-taut mode */ - int iOther; /* other than basic structure index, usually non-taut; may be = iBase */ - int bReqNonTaut; /* 1 => requested non-tautomeric results */ - int bReqTaut; /* 1 => requested tautomeric results and the base structure is tautomeric */ - int bChanged; - sp_ATOM *at_base = NULL; - sp_ATOM *at_other = NULL; - int bTautIgnoreIsotopic = 0; - /*int bIgnoreIsotopic = 0;*/ - int nNumCurrRanks = 0; - int nMaxLenRankStack = 0; - int num_max = num_at_tg; - long lCount; - /* local allocations */ - ATOM_INVARIANT2 *pAtomInvariant = NULL; - NEIGH_LIST *NeighList[TAUT_NUM]; - ConTable *Ct_Temp = NULL; - - /* initial partition for canonicalization */ - AT_RANK *nRank = NULL; - AT_NUMB *nAtomNumber = NULL; - - /* canonicalization output */ - - ConTable *Ct_NoH = NULL; - AT_RANK *nCanonRankNoH = NULL; - AT_NUMB *nAtomNumberCanonNoH = NULL; - AT_RANK *nSymmRankNoH = NULL; - - ConTable *Ct_NoTautH = NULL; - AT_RANK *nSymmRankNoTautH = NULL; - AT_RANK *nCanonRankNoTautH = NULL; - AT_NUMB *nAtomNumberCanonNoTautH = NULL; - NUM_H *numHNoTautH = NULL; - int lenNumHNoTautH; - int maxlenNumHNoTautH; - - ConTable *Ct_Base = NULL; - AT_RANK *nSymmRankBase = NULL; - AT_RANK *nCanonRankBase = NULL; - AT_NUMB *nAtomNumberCanonBase = NULL; - NUM_H *numH = NULL; - int lenNumH; - int maxlenNumH = 0; - -#if ( USE_AUX_RANKING == 1 ) - AT_RANK *nRankAux = NULL; - AT_NUMB *nAtomNumberAux = NULL; - ATOM_INVARIANT2 *pAtomInvariantAux= NULL; -#endif - - - ConTable *Ct_FixH = NULL; - AT_RANK *nSymmRankFixH = NULL; - AT_RANK *nCanonRankFixH = NULL; - AT_NUMB *nAtomNumberCanonFixH = NULL; - NUM_H *NumHfixed = NULL; - int maxlenNumHfixed; - - /* isotopic canonicalization */ - - ConTable *Ct_NoTautHIso = NULL; - AT_RANK *nSymmRankNoTautHIso = NULL; - AT_RANK *nCanonRankNoTautHIso = NULL; - AT_NUMB *nAtomNumberCanonNoTautHIso = NULL; - AT_ISO_SORT_KEY *iso_sort_key_NoTautH = NULL; - int maxlen_iso_sort_key_NoTautH; - int len_iso_sort_key_NoTautH; - int num_iso_NoTautH, num_iso_NoAuxBase; - - ConTable *Ct_BaseIso = NULL; - AT_RANK *nSymmRankBaseIso = NULL; - AT_RANK *nCanonRankBaseIso = NULL; - AT_NUMB *nAtomNumberCanonBaseIso = NULL; - - AT_ISO_SORT_KEY *iso_sort_keyBase = NULL; - int maxlen_iso_sort_keyBase; - int len_iso_sort_keyBase; - - int bUseIsoAuxBase[TAUT_NUM]; - S_CHAR *iso_exchg_atnos = NULL; - int len_iso_exchg_atnos; - int maxlen_iso_exchg_atnos; - int num_iso_Base; - - AT_ISO_SORT_KEY iso_sort_key; - - ConTable *Ct_FixHIso = NULL; - AT_RANK *nSymmRankFixHIso = NULL; - AT_RANK *nCanonRankFixHIso = NULL; - AT_NUMB *nAtomNumberCanonFixHIso = NULL; - -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - AT_ISO_SORT_KEY iso_sort_key2; - AT_ISO_SORT_KEY *iso_sort_key_Hfixed = NULL; - int maxlen_iso_sort_key_Hfixed; - int len_iso_sort_key_Hfixed; - int num_iso_Hfixed; -#endif - - AT_RANK *nTempRank = NULL; - - CANON_DATA pCD[3]; /* = &CanonData; */ - CANON_COUNTS CanonCounts; - CANON_COUNTS *pCC = &CanonCounts; - - int i, j, k, m; - int nCanonFlags[2]; - - /*^^^ */ - int iflag; - - memset (pCD, 0, sizeof(pCD)); - memset (pCC, 0, sizeof(pCC[0])); - memset ( bUseIsoAuxBase, 0, sizeof(bUseIsoAuxBase) ); - memset ( nCanonFlags, 0, sizeof(nCanonFlags) ); - NeighList[TAUT_NON] = NULL; - NeighList[TAUT_YES] = NULL; - - /* select base structure, find whether it is tautomeric or not */ - if ( at[TAUT_YES] && s[TAUT_YES].nLenCT && - t_group_info && (s[TAUT_YES].nLenLinearCTTautomer > 0 && /* ordinary tautomerism */ - t_group_info->t_group && t_group_info->num_t_groups > 0 || - /* protons have been moved */ - (t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) || - /* tautomerism due to possible isotopic proton exchange */ - t_group_info->nNumIsotopicEndpoints > 1 && - (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE|TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE)) ) ) { - /* tautomeric: (1) has tautomeric atoms OR - (2) H-atoms have been rearranged due to proton addition/removal OR - (3) Found isotopic H-atoms on tautomeric or hetero atoms - */ - iBase = TAUT_YES; - bReqTaut = 1; - bUseIsoAuxBase[iBase] = (s[iBase].nLenIsotopicEndpoints > 1) && - (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE|TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE)); - if ( at[TAUT_NON] && s[TAUT_NON].nLenCT ) { - iOther = TAUT_NON; /* tautomeric and non-tautomeric */ - bReqNonTaut = 1; - } else { - iOther = iBase; /* tautomeric only */ - bReqNonTaut = 0; - } - } else - if ( at[TAUT_NON] && s[TAUT_NON].nLenCT ) { - /* force pure non-tautomeric processing; happens for testing only */ - iBase = TAUT_NON; - bReqTaut = 0; - iOther = iBase; - bReqNonTaut = 1; - num_at_tg = num_atoms; - } else - if ( at[TAUT_YES] && s[TAUT_YES].nLenCT ) { - /* although the user requested tautomeric processing, tautomerism has not been found */ - /* however, the results should be saved in the TAUT_YES elements of the arrays */ - iBase = TAUT_YES; - bReqTaut = 0; - bUseIsoAuxBase[iBase] = (s[iBase].nLenIsotopicEndpoints > 1); - iOther = iBase; - bReqNonTaut = 1; - num_at_tg = num_atoms; - } else { - ret = CT_UNKNOWN_ERR; - goto exit_error; - } - if ( bReqTaut ) { - /* save "process isotopic" mark; temporarily set it to NO */ - bTautIgnoreIsotopic = t_group_info->bIgnoreIsotopic; - t_group_info->bIgnoreIsotopic = 1; - } - lenNumH = num_atoms; - - /* isotopic canonicalization */ - num_iso_NoTautH = 0; - len_iso_sort_key_NoTautH = 0; - maxlen_iso_sort_key_NoTautH = 0; - num_iso_Base = 0; - len_iso_sort_keyBase = 0; - maxlen_iso_sort_keyBase = 0; - len_iso_exchg_atnos = 0; - maxlen_iso_exchg_atnos = 0; - len_iso_exchg_atnos = 0; - maxlen_iso_exchg_atnos = 0; - -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - num_iso_Hfixed = - len_iso_sort_key_Hfixed = - maxlen_iso_sort_key_Hfixed = 0; -#endif - - /* prepare initial data */ - at_base = at[iBase]; - at_other = at[iOther]; - pAtomInvariant = (ATOM_INVARIANT2 *)inchi_calloc( num_max, sizeof(pAtomInvariant[0]) ); - nSymmRankNoH = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankNoH[0] ) ); - nCanonRankNoH = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankNoH[0] ) ); - nAtomNumberCanonNoH = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonNoH[0]) ); - nRank = (AT_RANK *) inchi_calloc( num_max, sizeof(nRank[0] ) ); - nAtomNumber = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumber[0]) ); - nTempRank = (AT_RANK *) inchi_calloc( num_max, sizeof(nTempRank[0] ) ); - - if ( !pAtomInvariant || - !nSymmRankNoH || !nCanonRankNoH || !nAtomNumberCanonNoH || - !nRank || !nAtomNumber || !nTempRank ) { - goto exit_error_alloc; - } -#if ( USE_AUX_RANKING == 1 ) - nRankAux = (AT_RANK *) inchi_calloc( num_max, sizeof(nRankAux[0] ) ); - nAtomNumberAux = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberAux[0] ) ); - pAtomInvariantAux = (ATOM_INVARIANT2 *) inchi_malloc( num_max * sizeof(pAtomInvariantAux[0]) ); - if ( !nRankAux || !nAtomNumberAux || !pAtomInvariantAux ) { - goto exit_error_alloc; - } -#endif - - if ( bReqTaut ) { - if ( !(NeighList[TAUT_YES] = CreateNeighList( num_atoms, num_at_tg, at_base, 0, t_group_info )) ) - goto exit_error_alloc; - /* needed for the hydrogenless structure */ - if ( !(NeighList[TAUT_NON] = CreateNeighList( num_atoms, num_atoms, at_base, 0, NULL )) ) - goto exit_error_alloc; - } else { - if ( !(NeighList[TAUT_NON] = CreateNeighList( num_atoms, num_atoms, at_base, 0, NULL )) ) - goto exit_error_alloc; - NeighList[TAUT_YES] = NULL; - INCHI_HEAPCHK - } - - /* avoid memory leaks in case of error */ - /* - pBCN->ftcn[TAUT_NON].NeighList = NeighList[TAUT_NON]; - pBCN->ftcn[TAUT_YES].NeighList = NeighList[TAUT_YES]; - */ - pBCN->nMaxLenRankStack = 0; - pBCN->num_max = num_max; /* allocated nRank[] arrays lengths in pRankStack */ - pBCN->num_at_tg = num_at_tg; /* all of the following arrays have this length */ - pBCN->num_atoms = num_atoms; - pBCN->ulTimeOutTime = ulTimeOutTime; - - /* initial partitioning of a hydrogenless skeleton: fill out the inveriant */ - FillOutAtomInvariant2( at_base, num_atoms, num_atoms, pAtomInvariant, 1 /*bIgnoreIsotopic*/, - 0 /*bHydrogensInRanks*/, 0 /*bHydrogensFixedInRanks*/, 0 /*bTaut=bDigraph*/, - 0 /* bTautGroupsOnly */, NULL /*t_group_info*/ ); - /* initial partitioning of a hydrogenless skeleton: create equitable partition (assign initial ranks) */ - nNumCurrRanks = SetInitialRanks2( num_atoms, pAtomInvariant, nRank, nAtomNumber ); - - lCount = 0; - /* make equitable partition in pBCN->pRankStack[0,1] */ - nNumCurrRanks = DifferentiateRanks2( num_atoms, NeighList[TAUT_NON], - nNumCurrRanks, nRank, - nTempRank, nAtomNumber, &lCount, 0 /* 0 means use qsort */ ); - - /* allocate partition stack */ - nMaxLenRankStack = 2*(num_at_tg-nNumCurrRanks) + 8; /* was 2*(...) + 6 */ - pBCN->pRankStack = (AT_RANK **) inchi_calloc( nMaxLenRankStack, sizeof(pBCN->pRankStack[0]) ); - if ( !pBCN->pRankStack ) { - pBCN->nMaxLenRankStack = 0; /* avoid memory leaks in case of error */ - goto exit_error_alloc; - } - pBCN->nMaxLenRankStack = nMaxLenRankStack; /* avoid memory leaks in case of error */ - /* init partition stack */ - pBCN->pRankStack[0] = nRank; - pBCN->pRankStack[1] = nAtomNumber; - - /********************************************************************************************/ - /* get NoH/no taut groups canonical numbering, connection table, and equivalence partition */ - /********************************************************************************************/ - - /* pointers */ - pCD[iOther].LinearCT = NULL; - pCD[iOther].NumH = NULL; - pCD[iOther].NumHfixed = NULL; - pCD[iOther].iso_sort_key = NULL; - pCD[iOther].iso_exchg_atnos = NULL; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iOther].iso_sort_key_Hfixed = NULL; -#endif - /* variables - unchanged */ - pCD[iOther].ulTimeOutTime = pBCN->ulTimeOutTime; - pCD[iOther].nMaxLenLinearCT = s[iOther].nLenCTAtOnly + 1; - /* return values & input/output */ - pCD[iOther].nLenLinearCT = s[iOther].nLenCTAtOnly; - pCD[iOther].nLenCTAtOnly = s[iOther].nLenCTAtOnly; - pCD[iOther].lenNumH = 0; - pCD[iOther].lenNumHfixed = 0; - pCD[iOther].len_iso_sort_key = 0; - pCD[iOther].maxlen_iso_sort_key = 0; - pCD[iOther].len_iso_exchg_atnos = 0; - pCD[iOther].maxlen_iso_exchg_atnos = 0; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iOther].len_iso_sort_key_Hfixed = 0; - pCD[iOther].maxlen_iso_sort_key_Hfixed = 0; -#endif - ret = CanonGraph01( num_atoms, num_atoms, num_max, 0, NeighList[TAUT_NON], (Partition *)pBCN->pRankStack, - nSymmRankNoH, nCanonRankNoH, nAtomNumberCanonNoH, pCD+iOther, pCC, NULL, &Ct_NoH ); - if ( ret < 0 ) { - goto exit_error; - } - /* update initial partitioning */ - nNumCurrRanks = FixCanonEquivalenceInfo( num_atoms, nSymmRankNoH, nRank, nTempRank, nAtomNumber, &bChanged ); - /* repartition if necessary */ - if ( bChanged & 3 ) { - if ( Ct_NoH ) { - CTableFree( Ct_NoH ); - inchi_free( Ct_NoH ); - Ct_NoH = NULL; - } - pCD[iOther].nCanonFlags |= CANON_FLAG_NO_H_RECANON; - - ret = CanonGraph02( num_atoms, num_atoms, num_max, 0, NeighList[TAUT_NON], (Partition *)pBCN->pRankStack, - nSymmRankNoH, nCanonRankNoH, nAtomNumberCanonNoH, pCD+iOther, pCC, NULL, &Ct_NoH ); - if ( ret < 0 ) { - goto exit_error; - } - } - /********************************************************************************/ - /* get NoTautH canonical numbering, connection table, and equivalence partition */ - /********************************************************************************/ - maxlenNumHNoTautH = num_atoms + 1; - nSymmRankNoTautH = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankNoTautH[0] ) ); - nCanonRankNoTautH = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankNoTautH[0] ) ); - nAtomNumberCanonNoTautH = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonNoTautH[0]) ); - numHNoTautH = (NUM_H *) inchi_calloc( maxlenNumHNoTautH, sizeof(numHNoTautH[0]) ); - if ( !numHNoTautH || !nSymmRankNoTautH || !nCanonRankNoTautH || !nAtomNumberCanonNoTautH ) { - goto exit_error_alloc; - } - /* find number of H atoms attached to not-a-tautomeric-endpoint atoms */ - for ( i = 0; i < num_atoms; i ++ ) { - numHNoTautH[i] = (!at_base[i].endpoint && at_base[i].num_H)? at_base[i].num_H+BASE_H_NUMBER : EMPTY_H_NUMBER; - } - /* pointers */ - pCD[iOther].LinearCT = NULL; - pCD[iOther].NumH = numHNoTautH; - pCD[iOther].NumHfixed = NULL; - pCD[iOther].iso_sort_key = NULL; - pCD[iOther].iso_exchg_atnos = NULL; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iOther].iso_sort_key_Hfixed = NULL; -#endif - /* variables - unchanged */ - pCD[iOther].ulTimeOutTime = pBCN->ulTimeOutTime; - pCD[iOther].nMaxLenLinearCT = s[iOther].nLenCTAtOnly + 1; - pCD[iOther].maxlenNumH = maxlenNumHNoTautH; - /* return values & input/output */ - pCD[iOther].nLenLinearCT = s[iOther].nLenCTAtOnly; - pCD[iOther].nLenCTAtOnly = s[iOther].nLenCTAtOnly; - pCD[iOther].lenNumH = lenNumHNoTautH = num_atoms; - pCD[iOther].lenNumHfixed = 0; - pCD[iOther].len_iso_sort_key = 0; - pCD[iOther].maxlen_iso_sort_key = 0; - pCD[iOther].len_iso_exchg_atnos = 0; - pCD[iOther].maxlen_iso_exchg_atnos = 0; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iOther].len_iso_sort_key_Hfixed = 0; - pCD[iOther].maxlen_iso_sort_key_Hfixed = 0; -#endif - pCD[iOther].nAuxRank = NULL; - - /* check whether we need NoTautH cononicalization */ - memset( nTempRank, 0, num_max * sizeof(nTempRank[0]) ); - for ( i = 0; i < num_atoms; i ++ ) { - if ( nTempRank[nSymmRankNoH[i]-1] < i ) { - nTempRank[nSymmRankNoH[i]-1] = i; /* greatest class representative */ - } - } - for ( i = 0; i < num_atoms; i ++ ) { - if ( numHNoTautH[i] != numHNoTautH[nTempRank[nSymmRankNoH[i]-1]] ) { - pCD[iOther].nCanonFlags |= CANON_FLAG_NO_TAUT_H_DIFF; - break; /* atoms so far found to be equivalent have different number of H; the canonicalization is needed */ - } - } - /* i = 0; *//* debug: force to call the canonicalization */ - if ( i < num_atoms ) { - /* needs canonicalization */ - /* get aux canonical ranking of the structure with attached H */ -#if ( USE_AUX_RANKING == 1 ) - /* refine no-H partition according to not-a-taut-H distribution */ - memset( pAtomInvariantAux, 0, num_max * sizeof(pAtomInvariantAux[0]) ); - for ( i = 0; i < num_atoms; i ++ ) { - pAtomInvariantAux[i].val[0] = nSymmRankNoH[i]; - pAtomInvariantAux[i].val[1] = numHNoTautH[i]; /* additional differentiation: not-a-taut-H distribution */ - } - /* initial partitioning */ - nNumCurrRanks = SetInitialRanks2( num_atoms, pAtomInvariantAux, nRankAux, nAtomNumberAux ); - /* make equitable partition */ - nNumCurrRanks = DifferentiateRanks2( num_atoms, NeighList[TAUT_NON], - nNumCurrRanks, nRankAux, - nTempRank, nAtomNumberAux, &lCount, 0 /* 0 means use qsort */ ); - /* to accelerate do not call CanonGraph() to find really equivalent atoms */ - pCD[iOther].nAuxRank = nRankAux; -#endif - - ret = CanonGraph03( num_atoms, num_atoms, num_max, 1 /* digraph?? was 0 */, NeighList[TAUT_NON], (Partition *)pBCN->pRankStack, - nSymmRankNoTautH, nCanonRankNoTautH, nAtomNumberCanonNoTautH, pCD+iOther, pCC, &Ct_NoH, &Ct_NoTautH ); - if ( ret < 0 ) { - goto exit_error; - } - /* in case of non-tautomeric structure the final results are in: - - nSymmRankNoTautH - nCanonRankNoTautH - nAtomNumberCanonNoTautH - Ct_NoTautH - numHNoTautH (original H positions) - */ - } else { - /* copy the results of the previous (no H) canonicalization */ - /* in this case numHNoTautH[] is not needed for the next canonicalization(s) */ - if ( (Ct_Temp = (ConTable *)inchi_calloc( 1, sizeof( *Ct_Temp ) ) ) && - CTableCreate( Ct_Temp, num_atoms, pCD+iOther) ) { - CtFullCopy( Ct_Temp, Ct_NoH ); - /* since Ct_NoH does not have Ct_NoH->NumH we have to fill out Ct_Temp->NumH separately */ - for ( i = 0; i < num_atoms; i ++ ) { - Ct_Temp->NumH[nCanonRankNoH[i]-1] = numHNoTautH[i]; - /*Ct_Temp->NumH[i] = numHNoTautH[nAtomNumberCanonNoH[i]]; -- alternative */ - } - Ct_Temp->lenNumH = num_atoms; - } else { - goto exit_error_alloc; - } - Ct_NoTautH = Ct_Temp; - Ct_Temp = NULL; - memcpy( nSymmRankNoTautH, nSymmRankNoH, num_atoms*sizeof(nSymmRankNoTautH[0]) ); - memcpy( nCanonRankNoTautH, nCanonRankNoH, num_atoms*sizeof(nCanonRankNoTautH[0]) ); - memcpy( nAtomNumberCanonNoTautH, nAtomNumberCanonNoH, num_atoms*sizeof(nAtomNumberCanonNoTautH[0]) ); - } - /* in case of non-tautomeric component this is the final result */ - /* i = CtFullCompare( Ct_NoTautH, Ct_Temp, num_atoms, 0, 0 );*/ - - /*******************************************************************************************/ - /* If only Isotopic atoms and isotopic H, tautomerism has not been found: */ - /* get isotopic canonical numbering, connection table, and equivalence partition */ - /*******************************************************************************************/ - - if ( s[iOther].num_isotopic_atoms && !s[iOther].bIgnoreIsotopic && !bReqTaut && bReqNonTaut ) { - - maxlen_iso_sort_key_NoTautH = num_atoms+1; - nSymmRankNoTautHIso = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankNoTautHIso[0] ) ); - nCanonRankNoTautHIso = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankNoTautHIso[0] ) ); - nAtomNumberCanonNoTautHIso = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonNoTautHIso[0]) ); - iso_sort_key_NoTautH = (AT_ISO_SORT_KEY *) inchi_calloc( maxlen_iso_sort_key_NoTautH, sizeof(iso_sort_key_NoTautH[0]) ); - - if ( !nSymmRankNoTautHIso || !nCanonRankNoTautHIso || !nAtomNumberCanonNoTautHIso || !iso_sort_key_NoTautH ) { - goto exit_error_alloc; - } - - /* fill out isotopic non-tautomeric keys */ - num_iso_NoTautH = 0; - for ( i = 0; i < num_atoms; i ++ ) { - if ( at_base[i].endpoint ) { - /* should not happen */ - iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, 0, 0, 0); - } else { - iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, at_base[i].num_iso_H[0], at_base[i].num_iso_H[1], at_base[i].num_iso_H[2]); - } - if ( iso_sort_key ) { - iso_sort_key_NoTautH[i] = iso_sort_key; - num_iso_NoTautH ++; - } else { - iso_sort_key_NoTautH[i] = EMPTY_ISO_SORT_KEY; - } - } - /* pointers */ - pCD[iOther].LinearCT = NULL; /* LinearCT; */ - pCD[iOther].NumH = numHNoTautH; - pCD[iOther].NumHfixed = NULL; - pCD[iOther].iso_sort_key = iso_sort_key_NoTautH; - pCD[iOther].iso_exchg_atnos = NULL; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iOther].iso_sort_key_Hfixed = NULL; -#endif - /* variables - unchanged */ - pCD[iOther].ulTimeOutTime = pBCN->ulTimeOutTime; - pCD[iOther].nMaxLenLinearCT = s[iOther].nLenCTAtOnly + 1; - pCD[iOther].maxlenNumH = maxlenNumHNoTautH; - /* return values & input/output */ - pCD[iOther].nLenLinearCT = s[iOther].nLenCTAtOnly; - pCD[iOther].nLenCTAtOnly = s[iOther].nLenCTAtOnly; - pCD[iOther].lenNumH = lenNumHNoTautH /*= num_atoms*/; - pCD[iOther].lenNumHfixed = 0; - pCD[iOther].len_iso_sort_key = len_iso_sort_key_NoTautH = num_atoms; - pCD[iOther].maxlen_iso_sort_key = maxlen_iso_sort_key_NoTautH; - pCD[iOther].len_iso_exchg_atnos = 0; - pCD[iOther].maxlen_iso_exchg_atnos = 0; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iOther].len_iso_sort_key_Hfixed = 0; - pCD[iOther].maxlen_iso_sort_key_Hfixed = 0; -#endif - pCD[iOther].nAuxRank = NULL; - - if ( num_iso_NoTautH ) { - /* check whether we need NoTautH cononicalization */ - memset( nTempRank, 0, num_max * sizeof(nTempRank[0]) ); - for ( i = 0; i < num_atoms; i ++ ) { - if ( nTempRank[nSymmRankNoTautH[i]-1] < i ) { - nTempRank[nSymmRankNoTautH[i]-1] = i; /* greatest class representative */ - } - } - for ( i = 0; i < num_atoms; i ++ ) { - if ( iso_sort_key_NoTautH[i] != iso_sort_key_NoTautH[nTempRank[nSymmRankNoTautH[i]-1]] ) { - pCD[iOther].nCanonFlags |= CANON_FLAG_ISO_ONLY_NON_TAUT_DIFF; - break; /* atoms so far found to be equivalent differ in isotopes; the canonicalization is needed */ - } - } - } else { - i = num_atoms; - } - /* i = 0; *//* debug: force to call the canonicalization */ - if ( i < num_atoms ) { - /* we need canonicalization */ - /* get aux canonical ranking of the structure with isotopic non-tautomeric H */ - -#if ( USE_AUX_RANKING == 1 ) - /* refine no-taut-H partition according to non-taut H isotopic distribution */ - memset( pAtomInvariantAux, 0, num_max * sizeof(pAtomInvariantAux[0]) ); - for ( i = 0; i < num_atoms; i ++ ) { - pAtomInvariantAux[i].val[0] = nSymmRankNoTautH[i]; - pAtomInvariantAux[i].iso_sort_key = iso_sort_key_NoTautH[i]; /* additional differentiation */ - } - /* initial ranks for non-taut H isotopic distribution */ - nNumCurrRanks = SetInitialRanks2( num_atoms, pAtomInvariantAux, nRankAux, nAtomNumberAux ); - /* make equitable */ - nNumCurrRanks = DifferentiateRanks2( num_atoms, NeighList[TAUT_NON], - nNumCurrRanks, nRankAux, - nTempRank, nAtomNumberAux, &lCount, 0 /* 0 means use qsort */ ); - /* to accelerate do not call CanonGraph() to find really equivalent atoms */ - pCD[iOther].nAuxRank = nRankAux; -#endif - - ret = CanonGraph04( num_atoms, num_atoms, num_max, 1 /* digraph?? was 0 */, NeighList[TAUT_NON], (Partition *)pBCN->pRankStack, - nSymmRankNoTautHIso, nCanonRankNoTautHIso, nAtomNumberCanonNoTautHIso, pCD+iOther, pCC, &Ct_NoTautH, &Ct_NoTautHIso ); - if ( ret < 0 ) { - goto exit_error; - } - /* in case of non-tautomeric structure the final results are in: - - nSymmRankNoTautHIso - nCanonRankNoTautHIso - nAtomNumberCanonNoTautHIso - Ct_NoTautHIso - iso_sort_key_NoTautH (original isotopic atom positions) - */ - } else { - /* copy the results of the previous (no taut H) canonicalization */ - /* in this case numHNoTautH[] is not needed for the next canonicalization(s) */ - if ( (Ct_Temp = (ConTable *)inchi_calloc( 1, sizeof( *Ct_Temp ) ) ) && - CTableCreate( Ct_Temp, num_atoms, pCD+iOther) ) { - CtFullCopy( Ct_Temp, Ct_NoTautH ); - /* since Ct_NoTautH does not have Ct_NoTautH->iso_sort_key we have to fill out Ct_Temp->iso_sort_key separately */ - for ( i = 0; i < num_atoms; i ++ ) { - Ct_Temp->iso_sort_key[nCanonRankNoTautH[i]-1] = iso_sort_key_NoTautH[i]; - } - Ct_Temp->len_iso_sort_key = num_atoms; - } else { - goto exit_error_alloc; - } - Ct_NoTautHIso = Ct_Temp; - Ct_Temp = NULL; - memcpy( nSymmRankNoTautHIso, nSymmRankNoTautH, num_atoms*sizeof(nSymmRankNoTautHIso[0]) ); - memcpy( nCanonRankNoTautHIso, nCanonRankNoTautH, num_atoms*sizeof(nCanonRankNoTautHIso[0]) ); - memcpy( nAtomNumberCanonNoTautHIso, nAtomNumberCanonNoTautH, num_atoms*sizeof(nAtomNumberCanonNoTautHIso[0]) ); - } - /* in case of non-tautomeric component this is the final result */ - /* i = CtFullCompare( Ct_NoTautHIso, Ct_Temp, num_atoms, 0, 0 );*/ - } - - - if ( bReqTaut ) { - /*****************************************************************************/ - /* Tautomeric Structure Canonicalizaton: */ - /* get base canonical numbering, connection table, and equivalence partition */ - /*****************************************************************************/ - /* find H atoms attached to non-tautomeric-endpoints and to tautomeric endpoints */ - maxlenNumH = num_atoms + T_NUM_NO_ISOTOPIC*(num_at_tg-num_atoms) + 1; /* including negative charges */ - nSymmRankBase = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankBase[0] ) ); - nCanonRankBase = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankBase[0] ) ); - nAtomNumberCanonBase = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonBase[0]) ); - numH = (NUM_H *) inchi_calloc( maxlenNumH, sizeof(numH[0]) ); - if ( !numH || !nSymmRankBase || !nCanonRankBase || !nAtomNumberCanonBase ) { - goto exit_error_alloc; - } - /* non-tautomeric H counts */ - for ( i = 0; i < num_atoms; i ++ ) { - numH[i] = (!at_base[i].endpoint && at_base[i].num_H)? at_base[i].num_H+BASE_H_NUMBER : EMPTY_H_NUMBER; - } - /* tautomeric H and negative charge counts */ - for ( i = k = num_atoms; i < num_at_tg; i ++ ) { - m = i-num_atoms; - for ( j = 0; j < T_NUM_NO_ISOTOPIC; j ++ ) { - /* non-zeroes for j=1 are negative charge counts; T_NUM_NO_ISOTOPIC=2 entry per t-group */ - numH[k ++] = t_group_info->t_group[m].num[j]? t_group_info->t_group[m].num[j]+BASE_H_NUMBER : EMPTY_H_NUMBER; - } - } - /* pointers */ - pCD[iBase].LinearCT = NULL; - pCD[iBase].NumH = numH; /* num_atoms non-tautomeric H; num_tg pairs of H and (-) in t-groups */ - pCD[iBase].NumHfixed = NULL; - pCD[iBase].iso_sort_key = NULL; - pCD[iBase].iso_exchg_atnos = NULL; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iBase].iso_sort_key_Hfixed = NULL; -#endif - /* variables - unchanged */ - pCD[iBase].ulTimeOutTime = pBCN->ulTimeOutTime; - pCD[iBase].nMaxLenLinearCT = s[iBase].nLenCT + 1; - pCD[iBase].maxlenNumH = maxlenNumH; - /* return values & input/output */ - pCD[iBase].nLenLinearCT = s[iBase].nLenCT; - pCD[iBase].nLenCTAtOnly = s[iBase].nLenCTAtOnly; - pCD[iBase].lenNumH = lenNumH = k; - pCD[iBase].lenNumHfixed = 0; - pCD[iBase].len_iso_sort_key = 0; - pCD[iBase].maxlen_iso_sort_key = 0; - pCD[iBase].len_iso_exchg_atnos = 0; - pCD[iBase].maxlen_iso_exchg_atnos = 0; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iBase].len_iso_sort_key_Hfixed = 0; - pCD[iBase].maxlen_iso_sort_key_Hfixed = 0; -#endif - pCD[iBase].nAuxRank = NULL; - - /* make sure the initial partition is equitable (at this point t-groups do not have ranks yet) */ - FillOutAtomInvariant2( at_base, num_atoms, num_at_tg, pAtomInvariant, 1 /*bIgnoreIsotopic*/, - 0 /*bHydrogensInRanks*/, 0 /*bHydrogensFixedInRanks*/, 1 /*bTaut=bDigraph*/, - 1 /* bTautGroupsOnly */, t_group_info ); - for ( i = 0; i < num_atoms; i ++ ) { - pAtomInvariant[i].val[0] = pBCN->pRankStack[0][i]; - } - /* initial ranks for t-group(s) only */ - nNumCurrRanks = SetInitialRanks2( num_at_tg, pAtomInvariant, nRank, nAtomNumber ); - /* make equitable, call digraph procedure; - pBCN->pRankStack[0] is nRank, pBCN->pRankStack[1] is nAtomNumber - This should only split ranks of tautomeric groups */ - nNumCurrRanks = DifferentiateRanks4( num_at_tg, NeighList[TAUT_YES], - nNumCurrRanks, pBCN->pRankStack[0], nTempRank /* temp array */, - pBCN->pRankStack[1], (AT_RANK)num_atoms, &lCount ); -#if ( USE_AUX_RANKING == 1 ) - /* refine no-H partition according to non-taut H distribution */ - memset( pAtomInvariantAux, 0, num_max * sizeof(pAtomInvariantAux[0]) ); - for ( i = 0; i < num_atoms; i ++ ) { - pAtomInvariantAux[i].val[0] = nSymmRankNoTautH[i]; - pAtomInvariantAux[i].val[1] = numH[i]; /* additional differentiation */ - } - for ( j = i; i < num_at_tg; i ++ ) { - pAtomInvariantAux[i].val[0] = nRank[i]; - } - - /* initial ranks for t-group(s) */ - nNumCurrRanks = SetInitialRanks2( num_at_tg, pAtomInvariantAux, nRankAux, nAtomNumberAux ); - /* make equitable, call digraph procedure */ - nNumCurrRanks = DifferentiateRanks4( num_at_tg, NeighList[TAUT_YES], - nNumCurrRanks, nRankAux, nTempRank /* temp array */, - nAtomNumberAux, (AT_RANK)num_atoms, &lCount ); - /* to accelerate do not call CanonGraph() to find really equivalent atoms */ - pCD[iBase].nAuxRank = nRankAux; -#endif - - - ret = CanonGraph05( num_atoms, num_at_tg, num_max, 1 /* digraph*/, NeighList[TAUT_YES], (Partition *)pBCN->pRankStack, - nSymmRankBase, nCanonRankBase, nAtomNumberCanonBase, pCD+iBase, pCC, &Ct_NoTautH, &Ct_Base ); - if ( ret < 0 ) { - goto exit_error; - } - - /* tautomeric isotopic structure */ - /**************************************************************************************/ - /* Isotopic atoms and isotopic H atoms and isotopic tautomeric groups */ - /* get isotopic canonical numbering, connection table, and equivalence partition */ - /**************************************************************************************/ - if ( s[iBase].num_isotopic_atoms && !s[iBase].bIgnoreIsotopic || - s[iBase].bHasIsotopicTautGroups && !bTautIgnoreIsotopic || - bUseIsoAuxBase[iBase] && !bTautIgnoreIsotopic ) { - - t_group_info->bIgnoreIsotopic = bTautIgnoreIsotopic; - - nSymmRankBaseIso = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankBaseIso[0] ) ); - nCanonRankBaseIso = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankBaseIso[0] ) ); - nAtomNumberCanonBaseIso = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonBaseIso[0]) ); - if ( bUseIsoAuxBase[iBase] ) { - maxlen_iso_exchg_atnos = num_max+1; - iso_exchg_atnos = (S_CHAR *) inchi_calloc( maxlen_iso_exchg_atnos, sizeof(iso_exchg_atnos[0]) ); - } - maxlen_iso_sort_keyBase = num_max+1; /* num_at_tg+1;*/ - iso_sort_keyBase = (AT_ISO_SORT_KEY *) inchi_calloc( maxlen_iso_sort_keyBase, sizeof(iso_sort_keyBase[0]) ); - if ( !nSymmRankBaseIso || !nCanonRankBaseIso || !nAtomNumberCanonBaseIso || - !iso_sort_keyBase || - maxlen_iso_exchg_atnos && !iso_exchg_atnos ) { - goto exit_error_alloc; - } - /* atoms */ - num_iso_NoTautH = 0; - num_iso_NoAuxBase = 0; - if ( iso_exchg_atnos ) { - len_iso_exchg_atnos = num_at_tg; - } - for ( i = 0; i < num_atoms; i ++ ) { - if ( at_base[i].endpoint || iso_exchg_atnos && (at_base[i].cFlags & AT_FLAG_ISO_H_POINT) ) { - /* tautomeric or may have exchangeable isotopic H */ - iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, 0, 0, 0); - if ( iso_exchg_atnos ) { - num_iso_NoAuxBase += !at_base[i].endpoint; /* these non-taut atom may exchange isotopic H as tautomeric atoms do */ - } - } else { - /* non-mobile H */ - iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, at_base[i].num_iso_H[0], at_base[i].num_iso_H[1], at_base[i].num_iso_H[2]); - if ( iso_exchg_atnos ) { - iso_exchg_atnos[i] = 1; /* atom cannot have exchangable isotopic H atom(s) */ - } - } - if ( iso_sort_key ) { - num_iso_NoTautH ++; - iso_sort_keyBase[i] = iso_sort_key; - } else { - iso_sort_keyBase[i] = EMPTY_ISO_SORT_KEY; - } - } - /* check marking and count of non-taut atoms that may exchange isotopic H -- debug only */ - if ( iso_exchg_atnos ) { - if ( num_iso_NoAuxBase != t_group_info->nIsotopicEndpointAtomNumber[0] ) { - ret = CT_ISOCOUNT_ERR; - goto exit_error; - } - for ( i = 1; i <= num_iso_NoAuxBase; i ++ ) { - j = t_group_info->nIsotopicEndpointAtomNumber[i]; - if ( at_base[j].endpoint || !(at_base[j].cFlags & AT_FLAG_ISO_H_POINT) ) { - ret = CT_ISOCOUNT_ERR; - goto exit_error; - } - } - } - /* t-groups */ - num_iso_Base = 0; - if ( iso_exchg_atnos ) { - for ( i = num_atoms; i < num_at_tg; i ++ ) { - iso_sort_keyBase[i] = EMPTY_ISO_SORT_KEY; /* new mode: do not provide info about isotopic tautomeric H */ - } - } else { - for ( i = num_atoms; i < num_at_tg; i ++ ) { /* should not happen anymore */ - m = i-num_atoms; - if ( iso_sort_key = t_group_info->t_group[m].iWeight ) { - /* old approach: each t-group has its own isotopic "weight" */ - num_iso_Base ++; - iso_sort_keyBase[i] = iso_sort_key; - } else { - iso_sort_keyBase[i] = EMPTY_ISO_SORT_KEY; - } - } - } - if ( !num_iso_NoAuxBase && iso_exchg_atnos ) { - /* all atoms that may exchange isotopic H are either tautomeric or not present */ - inchi_free( iso_exchg_atnos ); - iso_exchg_atnos = NULL; - len_iso_exchg_atnos = 0; - maxlen_iso_exchg_atnos = 0; - } - if ( !num_iso_NoTautH && !num_iso_Base && iso_sort_keyBase ) { - /* no isotopic atoms present */ - inchi_free( iso_sort_keyBase ); - iso_sort_keyBase = NULL; - maxlen_iso_sort_keyBase = 0; - } else { - len_iso_sort_keyBase = num_at_tg; - } - if ( !iso_exchg_atnos && !iso_sort_keyBase ) { - /* no isotopic part at all or only tautomeric groups */ - inchi_free( nSymmRankBaseIso ); nSymmRankBaseIso = NULL; - inchi_free( nCanonRankBaseIso ); nCanonRankBaseIso = NULL; - inchi_free( nAtomNumberCanonBaseIso ); nAtomNumberCanonBaseIso = NULL; - } else { - /* proceed with tautomeric isotopic canonicalization */ - /* pointers */ - pCD[iBase].LinearCT = NULL; - pCD[iBase].NumH = numH; /* num_atoms non-tautomeric H; num_tg pairs of H and (-) in t-groups */ - pCD[iBase].NumHfixed = NULL; - pCD[iBase].iso_sort_key = iso_sort_keyBase; - pCD[iBase].iso_exchg_atnos = iso_exchg_atnos; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iBase].iso_sort_key_Hfixed = NULL; -#endif - /* variables - unchanged */ - pCD[iBase].ulTimeOutTime = pBCN->ulTimeOutTime; - pCD[iBase].nMaxLenLinearCT = s[iBase].nLenCT + 1; - pCD[iBase].maxlenNumH = maxlenNumH; - /* return values & input/output */ - pCD[iBase].nLenLinearCT = s[iBase].nLenCT; - pCD[iBase].nLenCTAtOnly = s[iBase].nLenCTAtOnly; - pCD[iBase].lenNumH = lenNumH /* = k */; - pCD[iBase].lenNumHfixed = 0; - pCD[iBase].len_iso_sort_key = len_iso_sort_keyBase; - pCD[iBase].maxlen_iso_sort_key = maxlen_iso_sort_keyBase; - pCD[iBase].len_iso_exchg_atnos = len_iso_exchg_atnos; - pCD[iBase].maxlen_iso_exchg_atnos = maxlen_iso_exchg_atnos; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iBase].len_iso_sort_key_Hfixed = 0; - pCD[iBase].maxlen_iso_sort_key_Hfixed = 0; -#endif - pCD[iBase].nAuxRank = NULL; - - if ( num_iso_NoTautH || num_iso_Base || num_iso_NoAuxBase ) { - /* check whether we need actual canonicalization */ - memset( nTempRank, 0, num_max * sizeof(nTempRank[0]) ); - for ( i = 0; i < num_at_tg; i ++ ) { - if ( nTempRank[nSymmRankBase[i]-1] < i ) { - nTempRank[nSymmRankBase[i]-1] = i; /* greatest class representative */ - } - } - for ( i = 0; i < num_at_tg; i ++ ) { - if ( (iso_sort_keyBase? (iso_sort_keyBase[i] != iso_sort_keyBase[nTempRank[nSymmRankBase[i]-1]]):0) || - (iso_exchg_atnos? (iso_exchg_atnos[i] != iso_exchg_atnos[nTempRank[nSymmRankBase[i]-1]]):0)) { - pCD[iBase].nCanonFlags |= CANON_FLAG_ISO_TAUT_DIFF; - break; /* atoms so far found to be equivalent have different number of H; the canonicalization is needed */ - } - } - } else { - i = num_at_tg; /* should not happen */ - } - /* i = 0; *//* debug: force to call the canonicalization */ - if ( i < num_at_tg ) { - /* we need canonicalization */ - /* get aux canonical ranking of the structure with isotopic non-tautomeric H */ - - #if ( USE_AUX_RANKING == 1 ) - /* refine no-taut-H partition according to non-taut H + t-groups isotopic distribution */ - memset( pAtomInvariantAux, 0, num_max * sizeof(pAtomInvariantAux[0]) ); - for ( i = 0; i < num_at_tg; i ++ ) { - pAtomInvariantAux[i].val[0] = nSymmRankBase[i]; - pAtomInvariantAux[i].iso_sort_key = iso_sort_keyBase? iso_sort_keyBase[i] : 0; /* additional differentiation */ - pAtomInvariantAux[i].iso_aux_key = iso_exchg_atnos? iso_exchg_atnos[i] : 0; - } - /* initial ranks for non-taut H isotopic distribution */ - nNumCurrRanks = SetInitialRanks2( num_at_tg, pAtomInvariantAux, nRankAux, nAtomNumberAux ); - /* make equitable, not a digraph procedure */ - nNumCurrRanks = DifferentiateRanks2( num_at_tg, NeighList[TAUT_YES], - nNumCurrRanks, nRankAux, - nTempRank, nAtomNumberAux, &lCount, 0 /* 0 means first use qsort */ ); - /* to accelerate do not call CanonGraph() to find really equivalent atoms */ - pCD[iBase].nAuxRank = nRankAux; - #endif - - - ret = CanonGraph06( num_atoms, num_at_tg, num_max, 1 /* digraph */, NeighList[TAUT_YES], (Partition *)pBCN->pRankStack, - nSymmRankBaseIso, nCanonRankBaseIso, nAtomNumberCanonBaseIso, pCD+iBase, pCC, &Ct_Base, &Ct_BaseIso ); - if ( ret < 0 ) { - goto exit_error; - } - /* in case of a tautomeric structure the final results are in: - - nSymmRankBaseIso - nCanonRankBaseIso - nAtomNumberCanonBaseIso - Ct_BaseIso - iso_sort_keyBase (original isotopic atom & t-group positions) - Ct_BaseIso->iso_exchg_atnos: 0=>can exchange isotopic H, including tautomeric atoms - iso_exchg_atnos : same, in order of t_group_info->nIsotopicEndpointAtomNumber[] - */ - } else { - /* copy the results of the previous (no taut H) canonicalization */ - /* in this case numHNoTautH[] is not needed for the next canonicalization(s) */ - if ( (Ct_Temp = (ConTable *)inchi_calloc( 1, sizeof( *Ct_Temp ) ) ) && - CTableCreate( Ct_Temp, num_atoms, pCD+iBase) ) { - CtFullCopy( Ct_Temp, Ct_Base ); - /* since Ct_Base does not have Ct_Base->iso_sort_key we - have to fill out Ct_Temp->iso_sort_key separately */ - if ( iso_sort_keyBase ) { - for ( i = 0; i < num_at_tg; i ++ ) { - Ct_Temp->iso_sort_key[nCanonRankBase[i]-1] = iso_sort_keyBase[i]; - } - Ct_Temp->len_iso_sort_key = num_at_tg; - } else { - Ct_Temp->len_iso_sort_key = 0; - } - if ( iso_exchg_atnos ) { - for ( i = 0; i < num_atoms; i ++ ) { - Ct_Temp->iso_exchg_atnos[nCanonRankBase[i]-1] = iso_exchg_atnos[i]; - } - Ct_Temp->len_iso_exchg_atnos = num_at_tg; - } else { - Ct_Temp->len_iso_exchg_atnos = 0; - } - } else { - goto exit_error_alloc; - } - Ct_BaseIso = Ct_Temp; - Ct_Temp = NULL; - memcpy( nSymmRankBaseIso, nSymmRankBase, num_at_tg*sizeof(nSymmRankBaseIso[0]) ); - memcpy( nCanonRankBaseIso, nCanonRankBase, num_at_tg*sizeof(nCanonRankBaseIso[0]) ); - memcpy( nAtomNumberCanonBaseIso, nAtomNumberCanonBase, num_at_tg*sizeof(nAtomNumberCanonBaseIso[0]) ); - } - /* in case of non-tautomeric component this is the final result */ - /* i = CtFullCompare( Ct_BaseIso, Ct_Temp, num_at_tg, 0, 0 );*/ - - t_group_info->bIgnoreIsotopic = 1; - } - } - } - - /**********************************************************************************/ - /* get "fixed H" canonical numbering, connection table, and equivalence partition */ - /**********************************************************************************/ - - if ( bReqTaut && bReqNonTaut ) { - maxlenNumHfixed = num_atoms + 1; - nSymmRankFixH = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankFixH[0] ) ); - nCanonRankFixH = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankFixH[0] ) ); - nAtomNumberCanonFixH = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonFixH[0]) ); - NumHfixed = (NUM_H *) inchi_calloc( maxlenNumHfixed, sizeof(NumHfixed[0]) ); - if ( !NumHfixed || !nSymmRankFixH || !nCanonRankFixH || !nAtomNumberCanonFixH ) { - goto exit_error_alloc; - } - for ( i = 0; i < num_atoms; i ++ ) { - /* fixed and non-tautomeric H different in taut and non-taut structures */ - if ( at_base[i].endpoint ) { - NumHfixed[i] = at_other[i].num_H? at_other[i].num_H+BASE_H_NUMBER : EMPTY_H_NUMBER; - } else - if ( at_other[i].num_H != at_base[i].num_H ) { - NumHfixed[i] = (NUM_H)at_other[i].num_H - (NUM_H)at_base[i].num_H + BASE_H_NUMBER; - } else { - NumHfixed[i] = EMPTY_H_NUMBER; - } - } - /* pointers */ - pCD[iOther].LinearCT = NULL; /* LinearCT; */ - pCD[iOther].NumH = numHNoTautH; - pCD[iOther].NumHfixed = NumHfixed;/* variables - unchanged */ - pCD[iOther].iso_sort_key = NULL; - pCD[iOther].iso_exchg_atnos = NULL; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iOther].iso_sort_key_Hfixed = NULL; -#endif - pCD[iOther].ulTimeOutTime = pBCN->ulTimeOutTime; - pCD[iOther].nMaxLenLinearCT = s[iOther].nLenCTAtOnly + 1; - pCD[iOther].maxlenNumH = maxlenNumHNoTautH; - pCD[iOther].maxlenNumHfixed = maxlenNumHfixed; - /* return values & input/output */ - pCD[iOther].nLenLinearCT = s[iOther].nLenCTAtOnly; - pCD[iOther].nLenCTAtOnly = s[iOther].nLenCTAtOnly; - pCD[iOther].lenNumH = lenNumHNoTautH = num_atoms; - pCD[iOther].lenNumHfixed = num_atoms; - pCD[iOther].len_iso_sort_key = 0; - pCD[iOther].maxlen_iso_sort_key = 0; - pCD[iOther].len_iso_exchg_atnos = 0; - pCD[iOther].maxlen_iso_exchg_atnos = 0; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iOther].len_iso_sort_key_Hfixed = 0; - pCD[iOther].maxlen_iso_sort_key_Hfixed = 0; -#endif - pCD[iOther].nAuxRank = NULL; - -#if ( USE_AUX_RANKING == 1 ) - if ( !nRankAux ) - nRankAux = (AT_RANK *) inchi_calloc( num_max, sizeof(nRankAux[0] ) ); - if ( !nAtomNumberAux ) - nAtomNumberAux = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberAux[0] ) ); - if ( !pAtomInvariantAux ) - pAtomInvariantAux = (ATOM_INVARIANT2 *) inchi_malloc( num_max * sizeof(pAtomInvariantAux[0]) ); - - if ( !nRankAux || !nAtomNumberAux || - !pAtomInvariantAux ) { - goto exit_error_alloc; - } - /* refine no-H partition according to non-taut H distribution */ - memset( pAtomInvariantAux, 0, num_max * sizeof(pAtomInvariantAux[0]) ); - for ( i = 0; i < num_atoms; i ++ ) { - pAtomInvariantAux[i].val[0] = nSymmRankBase[i]; - pAtomInvariantAux[i].val[1] = NumHfixed[i]; /* additional differentiation */ - } - - /* initial ranks for t-group(s) */ - nNumCurrRanks = SetInitialRanks2( num_atoms, pAtomInvariantAux, nRankAux, nAtomNumberAux ); - /* make equitable, digraph procedure */ - nNumCurrRanks = DifferentiateRanks2( num_atoms, NeighList[TAUT_NON], - nNumCurrRanks, nRankAux, - nTempRank, nAtomNumberAux, &lCount, 0 /* 0 means use qsort */ ); - /* to accelerate do not call CanonGraph() to find really equivalent atoms */ - pCD[iOther].nAuxRank = nRankAux; -#endif - - ret = CanonGraph07( num_atoms, num_atoms, num_max, 0, NeighList[TAUT_NON], (Partition *)pBCN->pRankStack, - nSymmRankFixH, nCanonRankFixH, nAtomNumberCanonFixH, pCD+iOther, pCC, &Ct_NoTautH, &Ct_FixH ); - if ( ret < 0 ) { - goto exit_error; - } - - /*******************************************************************************************/ - /* get "fixed H" isotopic canonical numbering, connection table, and equivalence partition */ - /*******************************************************************************************/ - iflag = s[iBase].num_isotopic_atoms && !s[iBase].bIgnoreIsotopic || - s[iBase].bHasIsotopicTautGroups && !bTautIgnoreIsotopic; - if (bFixIsoFixedH) /* #if ( FIX_ISO_FIXEDH_BUG == 1 ) */ - /* fix bug when iso H was removed as a proton and fixed-H isotopic layer is missing - 2008-09-24 DT*/ - iflag = iflag || s[iOther].num_isotopic_atoms && !s[iOther].bIgnoreIsotopic; - if (iflag) { - -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - maxlen_iso_sort_key_Hfixed = -#endif - maxlen_iso_sort_key_NoTautH = num_atoms+1; - nSymmRankFixHIso = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankFixHIso[0] ) ); - nCanonRankFixHIso = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankFixHIso[0] ) ); - nAtomNumberCanonFixHIso = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonFixHIso[0]) ); -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - iso_sort_key_Hfixed = (AT_ISO_SORT_KEY *) inchi_calloc( maxlen_iso_sort_key_Hfixed, sizeof(iso_sort_key_Hfixed[0]) ); -#endif - iso_sort_key_NoTautH = (AT_ISO_SORT_KEY *) inchi_calloc( maxlen_iso_sort_key_NoTautH, sizeof(iso_sort_key_NoTautH[0]) ); - - if ( !nSymmRankFixHIso || !nCanonRankFixHIso || !nAtomNumberCanonFixHIso || -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - !iso_sort_key_Hfixed || -#endif - !iso_sort_key_NoTautH ) { - goto exit_error_alloc; - } - -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - /* fill out isotopic non-tautomeric keys */ - for ( i = 0; i < num_atoms; i ++ ) { - if ( at_base[i].endpoint ) { - iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, 0, 0, 0); - iso_sort_key2 = make_iso_sort_key( 0, at_base[i].num_iso_H[0], at_base[i].num_iso_H[1], at_base[i].num_iso_H[2]); - } else { - iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, at_base[i].num_iso_H[0], at_base[i].num_iso_H[1], at_base[i].num_iso_H[2]); - iso_sort_key2 = 0; - } - if ( iso_sort_key ) { - iso_sort_key_NoTautH[i] = iso_sort_key; - num_iso_NoTautH ++; - } else { - iso_sort_key_NoTautH[i] = EMPTY_ISO_SORT_KEY; - } - if ( iso_sort_key2 ) { - num_iso_Hfixed ++; - iso_sort_key_Hfixed[i] = iso_sort_key2; - } else { - iso_sort_key_Hfixed[i] = EMPTY_ISO_SORT_KEY; - } - } -#else - /* fill out isotopic non-tautomeric keys */ - for ( i = 0; i < num_atoms; i ++ ) { - - if (bFixIsoFixedH) /* #if ( FIX_ISO_FIXEDH_BUG == 1 ) */ - { - /* fix bug when iso H was removed as a proton and fixed-H isotopic layer is missing - 2008-09-24 DT*/ - if ( at_other ) - { - iso_sort_key = make_iso_sort_key( at_other[i].iso_atw_diff, at_other[i].num_iso_H[0], at_other[i].num_iso_H[1], at_other[i].num_iso_H[2]); - } - else - { - iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, at_base[i].num_iso_H[0], at_base[i].num_iso_H[1], at_base[i].num_iso_H[2]); - } - } - else - iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, at_base[i].num_iso_H[0], at_base[i].num_iso_H[1], at_base[i].num_iso_H[2]); - - - - - if ( iso_sort_key ) { - iso_sort_key_NoTautH[i] = iso_sort_key; - num_iso_NoTautH ++; - } else { - iso_sort_key_NoTautH[i] = EMPTY_ISO_SORT_KEY; - } - } -#endif - /* pointers */ - pCD[iOther].LinearCT = NULL; /* LinearCT; */ - pCD[iOther].NumH = numHNoTautH; - pCD[iOther].NumHfixed = NumHfixed;/* variables - unchanged */ - pCD[iOther].iso_sort_key = iso_sort_key_NoTautH; - pCD[iOther].iso_exchg_atnos = NULL; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iOther].iso_sort_key_Hfixed = iso_sort_key_Hfixed; -#endif - pCD[iOther].ulTimeOutTime = pBCN->ulTimeOutTime; - pCD[iOther].nMaxLenLinearCT = s[iOther].nLenCTAtOnly + 1; - pCD[iOther].maxlenNumH = maxlenNumHNoTautH; - pCD[iOther].maxlenNumHfixed = maxlenNumHfixed; - /* return values & input/output */ - pCD[iOther].nLenLinearCT = s[iOther].nLenCTAtOnly; - pCD[iOther].nLenCTAtOnly = s[iOther].nLenCTAtOnly; - pCD[iOther].lenNumH = lenNumHNoTautH = num_atoms; - pCD[iOther].lenNumHfixed = num_atoms; - pCD[iOther].len_iso_sort_key = len_iso_sort_key_NoTautH = num_atoms; - pCD[iOther].maxlen_iso_sort_key = maxlen_iso_sort_key_NoTautH; - pCD[iOther].len_iso_exchg_atnos = 0; - pCD[iOther].maxlen_iso_exchg_atnos = 0; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iOther].len_iso_sort_key_Hfixed = len_iso_sort_key_Hfixed = num_atoms; - pCD[iOther].maxlen_iso_sort_key_Hfixed = maxlen_iso_sort_key_Hfixed; -#endif - pCD[iOther].nAuxRank = NULL; - -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - if ( num_iso_Hfixed || num_iso_NoTautH ) -#else - if ( num_iso_NoTautH ) -#endif - { - /* check whether we need NoTautH cononicalization */ - memset( nTempRank, 0, num_max * sizeof(nTempRank[0]) ); - for ( i = 0; i < num_atoms; i ++ ) { - if ( nTempRank[nSymmRankFixH[i]-1] < i ) { - nTempRank[nSymmRankFixH[i]-1] = i; /* greatest class representative */ - } - } - for ( i = 0; i < num_atoms; i ++ ) { -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - if ( iso_sort_key_Hfixed[i] != iso_sort_key_Hfixed[nTempRank[nSymmRankFixH[i]-1]] ) - break; -#endif - if ( iso_sort_key_NoTautH[i] != iso_sort_key_NoTautH[nTempRank[nSymmRankFixH[i]-1]]) - break; /* atoms so far found to be equivalent have different isotopic shifts; the canonicalization is needed */ - } - } else { - i = num_atoms; /* should not happen */ - } - /* i = 0; *//* debug: force to call the canonicalization */ - if ( i < num_atoms ) { - pCD[iOther].nCanonFlags |= CANON_FLAG_ISO_FIXED_H_DIFF; - /* we need canonicalization */ - /* get aux canonical ranking of the structure with isotopic non-tautomeric H */ - -#if ( USE_AUX_RANKING == 1 ) - /* refine fixed-taut-H partition according to the isotopic distribution */ - memset( pAtomInvariantAux, 0, num_max * sizeof(pAtomInvariantAux[0]) ); - for ( i = 0; i < num_atoms; i ++ ) { - pAtomInvariantAux[i].val[0] = nSymmRankFixH[i]; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - iso_sort_key = 0; - if (iso_sort_key_NoTautH[i]!=EMPTY_ISO_SORT_KEY) - iso_sort_key |= iso_sort_key_NoTautH[i]; - if (iso_sort_key_Hfixed[i] !=EMPTY_ISO_SORT_KEY) - iso_sort_key |= iso_sort_key_Hfixed[i]; - if ( !iso_sort_key ) - iso_sort_key = EMPTY_ISO_SORT_KEY; -#else - iso_sort_key = iso_sort_key_NoTautH[i]; -#endif - pAtomInvariantAux[i].iso_sort_key = iso_sort_key; /* additional differentiation */ - } - - /* initial ranks for non-taut H isotopic distribution */ - nNumCurrRanks = SetInitialRanks2( num_atoms, pAtomInvariantAux, nRankAux, nAtomNumberAux ); - /* make equitable, digraph procedure */ - nNumCurrRanks = DifferentiateRanks2( num_atoms, NeighList[TAUT_NON], - nNumCurrRanks, nRankAux, - nTempRank, nAtomNumberAux, &lCount, 0 /* 0 means use qsort */ ); - /* to accelerate do not call CanonGraph() to find really equivalent atoms */ - pCD[iOther].nAuxRank = nRankAux; -#endif - - - ret = CanonGraph08( num_atoms, num_atoms, num_max, 1 /* digraph?? was 0 */, NeighList[TAUT_NON], (Partition *)pBCN->pRankStack, - nSymmRankFixHIso, nCanonRankFixHIso, nAtomNumberCanonFixHIso, pCD+iOther, pCC, &Ct_FixH, &Ct_FixHIso ); - if ( ret < 0 ) { - goto exit_error; - } - /* in case of non-tautomeric structure the final results are in: - - nSymmRankFixHIso - nCanonRankFixHIso - nAtomNumberCanonFixHIso - Ct_FixHIso - iso_sort_keyBase ([0..num_atoms] original isotopic atom positions) - iso_sort_key_Hfixed (original fixed tautomeric H distribution) - */ - } else { - /* copy the results of the previous (no taut H) canonicalization */ - /* in this case numHNoTautH[] is not needed for the next canonicalization(s) */ - if ( (Ct_Temp = (ConTable *)inchi_calloc( 1, sizeof( *Ct_Temp ) ) ) && - CTableCreate( Ct_Temp, num_atoms, pCD+iOther) ) { - CtFullCopy( Ct_Temp, Ct_FixH ); - /* since Ct_FixH does not have Ct_FixH->iso_sort_key and Ct_FixH->iso_sort_key_Hfixed we - have to fill out Ct_Temp->iso_sort_key and Ct_Temp->iso_sort_key_Hfixed separately */ - for ( i = 0; i < num_atoms; i ++ ) { - Ct_Temp->iso_sort_key[nCanonRankFixH[i]-1] = iso_sort_key_NoTautH[i]; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - Ct_Temp->iso_sort_key_Hfixed[nCanonRankFixH[i]-1] = iso_sort_key_Hfixed[i]; -#endif - } - Ct_Temp->len_iso_sort_key = num_atoms; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - Ct_Temp->len_iso_sort_key_Hfixed = num_atoms; -#endif - /*Ct_Temp->lenNumH = num_atoms;*/ - } else { - goto exit_error_alloc; - } - Ct_FixHIso = Ct_Temp; - Ct_Temp = NULL; - memcpy( nSymmRankFixHIso, nSymmRankFixH, num_atoms*sizeof(nSymmRankFixHIso[0]) ); - memcpy( nCanonRankFixHIso, nCanonRankFixH, num_atoms*sizeof(nCanonRankFixHIso[0]) ); - memcpy( nAtomNumberCanonFixHIso, nAtomNumberCanonFixH, num_atoms*sizeof(nAtomNumberCanonFixHIso[0]) ); - } - /* in case of non-tautomeric component this is the final result */ - /* i = CtFullCompare( Ct_NoTautHIso, Ct_Temp, num_atoms, 0, 0 );*/ - } - } /* "fixed H" canonical numbering */ - - /* consistency check: compare canonical connection tables, H-atoms, isotopic H & taut groups */ - ret = 0; - ret |= (Ct_NoH->lenCt != Ct_NoTautH->lenCt) || memcmp(Ct_NoH->Ctbl, Ct_NoTautH->Ctbl, Ct_NoH->lenCt * sizeof(Ct_NoH->Ctbl[0])); - if ( bReqTaut ) { - if ( Ct_FixH ) { - ret |= (Ct_NoTautH->lenCt != Ct_FixH->lenCt) || memcmp(Ct_NoTautH->Ctbl, Ct_FixH->Ctbl, Ct_NoTautH->lenCt * sizeof(Ct_NoTautH->Ctbl[0])); - ret |= (Ct_NoTautH->lenNumH != Ct_FixH->lenNumH) || memcmp( Ct_NoTautH->NumH, Ct_FixH->NumH, Ct_NoTautH->lenNumH*sizeof(Ct_Base->NumH[0])); - } - ret |= (Ct_NoTautH->lenCt > Ct_Base->lenCt) || memcmp(Ct_NoTautH->Ctbl, Ct_Base->Ctbl, Ct_NoTautH->lenCt * sizeof(Ct_NoTautH->Ctbl[0])); - ret |= (Ct_NoTautH->lenNumH > Ct_Base->lenNumH) || memcmp( Ct_NoTautH->NumH, Ct_Base->NumH, Ct_NoTautH->lenNumH*sizeof(Ct_Base->NumH[0])); - } - - /* isotopic canonicalization */ - if ( Ct_NoTautHIso ) { - ret |= (Ct_NoH->lenCt != Ct_NoTautHIso->lenCt) || memcmp(Ct_NoH->Ctbl, Ct_NoTautHIso->Ctbl, Ct_NoH->lenCt * sizeof(Ct_NoH->Ctbl[0])); - ret |= (Ct_NoTautH->lenNumH != Ct_NoTautHIso->lenNumH) || memcmp( Ct_NoTautH->NumH, Ct_NoTautHIso->NumH, Ct_NoTautH->lenNumH*sizeof(Ct_Base->NumH[0])); - } else - if ( Ct_BaseIso ) { - ret |= (Ct_BaseIso->lenCt != Ct_Base->lenCt) || memcmp(Ct_BaseIso->Ctbl, Ct_Base->Ctbl, Ct_BaseIso->lenCt * sizeof(Ct_BaseIso->Ctbl[0])); - ret |= (Ct_BaseIso->lenNumH != Ct_Base->lenNumH) || memcmp( Ct_BaseIso->NumH, Ct_Base->NumH, Ct_BaseIso->lenNumH*sizeof(Ct_BaseIso->NumH[0])); - if ( Ct_FixHIso ) { - ret |= (Ct_FixHIso->lenCt > Ct_BaseIso->lenCt) || memcmp(Ct_FixHIso->Ctbl, Ct_BaseIso->Ctbl, Ct_FixHIso->lenCt * sizeof(Ct_FixHIso->Ctbl[0])); - ret |= (Ct_FixHIso->lenNumH > Ct_BaseIso->lenNumH) || memcmp( Ct_FixHIso->NumH, Ct_BaseIso->NumH, Ct_FixHIso->lenNumH*sizeof(Ct_BaseIso->NumH[0])); - } - } - - if ( ret ) { - goto exit_error; - } - - if ( bReqTaut ) { - /* restore save "process isotopic" mark; temporarily set it to NO */ - t_group_info->bIgnoreIsotopic = bTautIgnoreIsotopic; - } - - - /* output the canonicalization results */ - pBCN->num_max = num_max; - pBCN->num_at_tg = num_at_tg; - pBCN->num_atoms = num_atoms; - - pBCN->ftcn[TAUT_NON].NeighList = NeighList[TAUT_NON]; NeighList[TAUT_NON] = NULL; - pBCN->ftcn[TAUT_YES].NeighList = NeighList[TAUT_YES]; NeighList[TAUT_YES] = NULL; - - if ( bReqTaut ) { /* tautomeric results */ - /* base tautomeric structure, iBase = TAUT_YES */ - - pBCN->ftcn[TAUT_YES].num_at_tg = num_at_tg; - pBCN->ftcn[TAUT_YES].num_atoms = num_atoms; - - pBCN->ftcn[TAUT_YES].LinearCt = Ct_Base->Ctbl; Ct_Base->Ctbl = NULL; - pBCN->ftcn[TAUT_YES].nLenLinearCtAtOnly = s[iBase].nLenCTAtOnly; - pBCN->ftcn[TAUT_YES].nMaxLenLinearCt = s[iBase].nLenCT+1; - pBCN->ftcn[TAUT_YES].nLenLinearCt = s[iBase].nLenCT; - - pBCN->ftcn[TAUT_YES].PartitionCt.Rank = nCanonRankBase; nCanonRankBase = NULL; - pBCN->ftcn[TAUT_YES].PartitionCt.AtNumber = nAtomNumberCanonBase; nAtomNumberCanonBase = NULL; - pBCN->ftcn[TAUT_YES].nSymmRankCt = nSymmRankBase; nSymmRankBase = NULL; - - pBCN->ftcn[TAUT_YES].nNumHOrig = numH; numH = NULL; - pBCN->ftcn[TAUT_YES].nNumH = Ct_Base->NumH; Ct_Base->NumH = NULL; - pBCN->ftcn[TAUT_YES].nLenNumH = inchi_min(maxlenNumH, Ct_Base->maxlenNumH); - - /* fixed H structure: exists only if the structure is tautomeric */ - pBCN->ftcn[TAUT_YES].nNumHOrigFixH = NULL; - pBCN->ftcn[TAUT_YES].nNumHFixH = NULL; - pBCN->ftcn[TAUT_YES].nLenNumHFixH = 0; - pBCN->ftcn[TAUT_YES].nCanonFlags |= pCD[iBase].nCanonFlags; - - CleanNumH( pBCN->ftcn[TAUT_YES].nNumHOrig, pBCN->ftcn[TAUT_YES].nLenNumH ); - CleanNumH( pBCN->ftcn[TAUT_YES].nNumH, pBCN->ftcn[TAUT_YES].nLenNumH ); - CleanCt ( pBCN->ftcn[TAUT_YES].LinearCt, pBCN->ftcn[TAUT_YES].nLenLinearCt ); - - /* isotopic canonicalization */ - if ( Ct_BaseIso ) { - pBCN->ftcn[TAUT_YES].PartitionCtIso.Rank = nCanonRankBaseIso; nCanonRankBaseIso = NULL; - pBCN->ftcn[TAUT_YES].PartitionCtIso.AtNumber = nAtomNumberCanonBaseIso; nAtomNumberCanonBaseIso = NULL; - pBCN->ftcn[TAUT_YES].nSymmRankCtIso = nSymmRankBaseIso; nSymmRankBaseIso = NULL; - pBCN->ftcn[TAUT_YES].iso_sort_keys = Ct_BaseIso->iso_sort_key; Ct_BaseIso->iso_sort_key = NULL; - pBCN->ftcn[TAUT_YES].iso_sort_keysOrig = iso_sort_keyBase; iso_sort_keyBase = NULL; - pBCN->ftcn[TAUT_YES].len_iso_sort_keys = len_iso_sort_keyBase; - pBCN->ftcn[TAUT_YES].iso_exchg_atnos = Ct_BaseIso->iso_exchg_atnos; Ct_BaseIso->iso_exchg_atnos = NULL; - pBCN->ftcn[TAUT_YES].iso_exchg_atnosOrig = iso_exchg_atnos; iso_exchg_atnos = NULL; - - CleanIsoSortKeys( pBCN->ftcn[TAUT_YES].iso_sort_keys, pBCN->ftcn[TAUT_YES].len_iso_sort_keys ); - CleanIsoSortKeys( pBCN->ftcn[TAUT_YES].iso_sort_keysOrig, pBCN->ftcn[TAUT_YES].len_iso_sort_keys ); - } - - } /* tautomeric results */ - - if ( bReqNonTaut ) { /* non-tautomeric results */ - /* TAUT_NON if tautomeric + non-tautomeric or special non-taut request - TAUT_YES if the structure happened to be non-tautomeric while user requested tautomeric processing - In both cases the correct index is iOther. TAUT_NON replaced with iOther 4-2-2004 */ - - if ( !bReqTaut ) { - /* rearrange the results for a non-tautomeric structure */ - nSymmRankFixH = nSymmRankNoTautH; nSymmRankNoTautH = NULL; - nCanonRankFixH = nCanonRankNoTautH; nCanonRankNoTautH = NULL; - nAtomNumberCanonFixH = nAtomNumberCanonNoTautH; nAtomNumberCanonNoTautH = NULL; - Ct_FixH = Ct_NoTautH; Ct_NoTautH = NULL; - /* isotopic canonicalization */ - nSymmRankFixHIso = nSymmRankNoTautHIso; nSymmRankNoTautHIso = NULL; - nCanonRankFixHIso = nCanonRankNoTautHIso; nCanonRankNoTautHIso = NULL; - nAtomNumberCanonFixHIso = nAtomNumberCanonNoTautHIso; nAtomNumberCanonNoTautHIso = NULL; - Ct_FixHIso = Ct_NoTautHIso; Ct_NoTautHIso = NULL; - - if ( iOther == TAUT_YES && pBCN->ftcn[TAUT_NON].NeighList && !pBCN->ftcn[TAUT_YES].NeighList ) { - /* here only non-taut results go to pBCN->ftcn[TAUT_YES] - Since non-taut NeighList is always in pBCN->ftcn[TAUT_NON].NeighList, move it to - pBCN->ftcn[TAUT_YES].NeighList. 2004-04-02. - */ - pBCN->ftcn[TAUT_YES].NeighList = pBCN->ftcn[TAUT_NON].NeighList; - pBCN->ftcn[TAUT_NON].NeighList = NULL; - } - } - pBCN->ftcn[iOther].num_at_tg = num_atoms; - pBCN->ftcn[iOther].num_atoms = num_atoms; - - pBCN->ftcn[iOther].LinearCt = Ct_FixH->Ctbl; Ct_FixH->Ctbl = NULL; - pBCN->ftcn[iOther].nLenLinearCtAtOnly = s[iOther].nLenCTAtOnly; - pBCN->ftcn[iOther].nMaxLenLinearCt = s[iOther].nLenCTAtOnly+1; - pBCN->ftcn[iOther].nLenLinearCt = s[iOther].nLenCTAtOnly; - - pBCN->ftcn[iOther].PartitionCt.Rank = nCanonRankFixH; nCanonRankFixH = NULL; - pBCN->ftcn[iOther].PartitionCt.AtNumber = nAtomNumberCanonFixH; nAtomNumberCanonFixH = NULL; - pBCN->ftcn[iOther].nSymmRankCt = nSymmRankFixH; nSymmRankFixH = NULL; - - pBCN->ftcn[iOther].nNumHOrig = numHNoTautH; numHNoTautH = NULL; - pBCN->ftcn[iOther].nNumH = Ct_FixH->NumH; Ct_FixH->NumH = NULL; - pBCN->ftcn[iOther].nLenNumH = inchi_min(maxlenNumHNoTautH,Ct_FixH->maxlenNumH); - - /* fixed H structure: exists only if the structure is tautomeric */ - pBCN->ftcn[iOther].nNumHOrigFixH = NumHfixed; NumHfixed = NULL; - pBCN->ftcn[iOther].nNumHFixH = Ct_FixH->NumHfixed; Ct_FixH->NumHfixed = NULL; - pBCN->ftcn[iOther].nLenNumHFixH = num_atoms; - pBCN->ftcn[iOther].nCanonFlags |= pCD[iOther].nCanonFlags; - - /* original H */ - CleanNumH( pBCN->ftcn[iOther].nNumHOrig, pBCN->ftcn[iOther].nLenNumH ); - CleanNumH( pBCN->ftcn[iOther].nNumHOrigFixH, pBCN->ftcn[iOther].nLenNumH ); - /* canonical H positions */ - CleanNumH( pBCN->ftcn[iOther].nNumH, pBCN->ftcn[iOther].nLenNumH ); - CleanNumH( pBCN->ftcn[iOther].nNumHFixH, pBCN->ftcn[iOther].nLenNumH ); - /* connection table */ - CleanCt( pBCN->ftcn[iOther].LinearCt, pBCN->ftcn[iOther].nLenLinearCt ); - - /* isotopic canonicalization */ - if ( Ct_FixHIso ) { - pBCN->ftcn[iOther].PartitionCtIso.Rank = nCanonRankFixHIso; nCanonRankFixHIso = NULL; - pBCN->ftcn[iOther].PartitionCtIso.AtNumber = nAtomNumberCanonFixHIso; nAtomNumberCanonFixHIso = NULL; - pBCN->ftcn[iOther].nSymmRankCtIso = nSymmRankFixHIso; nSymmRankFixHIso = NULL; - pBCN->ftcn[iOther].iso_sort_keys = Ct_FixHIso->iso_sort_key; Ct_FixHIso->iso_sort_key = NULL; - pBCN->ftcn[iOther].iso_sort_keysOrig = iso_sort_key_NoTautH; iso_sort_key_NoTautH = NULL; - pBCN->ftcn[iOther].len_iso_sort_keys = len_iso_sort_key_NoTautH; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - MergeCleanIsoSortKeys( pBCN->ftcn[iOther].iso_sort_keys, Ct_FixHIso->iso_sort_key_Hfixed, pBCN->ftcn[iOther].len_iso_sort_keys ); - MergeCleanIsoSortKeys( pBCN->ftcn[iOther].iso_sort_keysOrig, iso_sort_key_Hfixed, pBCN->ftcn[iOther].len_iso_sort_keys ); -#else - CleanIsoSortKeys( pBCN->ftcn[iOther].iso_sort_keys, pBCN->ftcn[iOther].len_iso_sort_keys ); - CleanIsoSortKeys( pBCN->ftcn[iOther].iso_sort_keysOrig, pBCN->ftcn[iOther].len_iso_sort_keys ); -#endif - } - - } /* non-tautomeric results */ - goto exit_function; - -exit_error_alloc: - ret = CT_OUT_OF_RAM; - goto exit_function; - -exit_error: - if ( !RETURNED_ERROR(ret) ) { - ret = CT_CANON_ERR; - } - goto exit_function; - -exit_function: - -#define FREE_CONTABLE(X) if (X) {CTableFree(X);inchi_free(X);} -#define FREE_ARRAY(X) if (X) inchi_free(X); - - FreeNeighList( NeighList[TAUT_NON] ); - FreeNeighList( NeighList[TAUT_YES] ); - - FREE_CONTABLE( Ct_NoH ) - FREE_CONTABLE( Ct_NoTautH ) - FREE_CONTABLE( Ct_Base ) - FREE_CONTABLE( Ct_FixH ) - FREE_CONTABLE( Ct_Temp ) - /* isotopic canonicalization */ - FREE_CONTABLE( Ct_NoTautHIso ) - FREE_CONTABLE( Ct_BaseIso ) - FREE_CONTABLE( Ct_FixHIso ) - - /* free the first two pointers from pBCN->pRankStack */ - FREE_ARRAY( nRank ) - FREE_ARRAY( nAtomNumber ) - - if ( pBCN->pRankStack ) { - pBCN->pRankStack[0] = - pBCN->pRankStack[1] = NULL; - } - -#if ( USE_AUX_RANKING == 1 ) - FREE_ARRAY( nRankAux ) - FREE_ARRAY( nAtomNumberAux ) - FREE_ARRAY( pAtomInvariantAux ) -#endif - - FREE_ARRAY( pAtomInvariant ) - - FREE_ARRAY( nCanonRankNoH ) - FREE_ARRAY( nAtomNumberCanonNoH ) - FREE_ARRAY( nSymmRankNoH ) - - FREE_ARRAY( nSymmRankNoTautH ) - FREE_ARRAY( nCanonRankNoTautH ) - FREE_ARRAY( nAtomNumberCanonNoTautH ) - FREE_ARRAY( numHNoTautH ) - - FREE_ARRAY( nSymmRankBase ) - FREE_ARRAY( nCanonRankBase ) - FREE_ARRAY( nAtomNumberCanonBase ) - FREE_ARRAY( numH ) - - FREE_ARRAY( nSymmRankFixH ) - FREE_ARRAY( nCanonRankFixH ) - FREE_ARRAY( nAtomNumberCanonFixH ) - FREE_ARRAY( NumHfixed ) - - /* isotopic canonicalization */ - - FREE_ARRAY( nSymmRankNoTautHIso ) - FREE_ARRAY( nCanonRankNoTautHIso ) - FREE_ARRAY( nAtomNumberCanonNoTautHIso ) - FREE_ARRAY( iso_sort_key_NoTautH ) - - FREE_ARRAY( nSymmRankBaseIso ) - FREE_ARRAY( nCanonRankBaseIso ) - FREE_ARRAY( nAtomNumberCanonBaseIso ) - FREE_ARRAY( iso_sort_keyBase ) - FREE_ARRAY( iso_exchg_atnos ) - - FREE_ARRAY( nSymmRankFixHIso ) - FREE_ARRAY( nCanonRankFixHIso ) - FREE_ARRAY( nAtomNumberCanonFixHIso ) -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - FREE_ARRAY( iso_sort_key_Hfixed ) -#endif - - FREE_ARRAY( nTempRank ) - -#undef FREE_CONTABLE -#undef FREE_ARRAY - - return ret; -} diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichicano.c b/INCHI-1-SRC/INCHI_API/inchi_dll/ichicano.c deleted file mode 100644 index 381b9a8..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichicano.c +++ /dev/null @@ -1,2252 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include - -#include "mode.h" - -#include "ichitime.h" -#include "ichi.h" -#include "util.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "inpdef.h" -#include "ichinorm.h" -#include "ichicant.h" -#include "ichicano.h" -#include "ichicomn.h" - -#include "ichicomp.h" - -/**************************************************************************** - * - * Globals for sorting - */ - -const NEIGH_LIST *pNeighList_RankForSort; -const ATOM_INVARIANT2 *pAtomInvariant2ForSort; -const AT_NUMB *pNeighborsForSort; -const AT_RANK *pn_RankForSort; - -AT_RANK nMaxAtNeighRankForSort; - -int nNumCompNeighborsRanksCountEql; - - -#define tsort insertions_sort - -/* local prototypes */ - - -void FillOutAtomInvariant( sp_ATOM* at, int num_atoms, int num_at_tg, ATOM_INVARIANT* pAtomInvariant, CANON_STAT* pCS ); - -int Canon_INChI1( int num_atoms, int num_at_tg, sp_ATOM* at, CANON_STAT* pCS, INCHI_MODE nMode); -int Canon_INChI2( int num_atoms, int num_at_tg, sp_ATOM* at, CANON_STAT* pCS, INCHI_MODE nMode); -int Canon_INChI3(int num_atoms, int num_at_tg, sp_ATOM* at, - CANON_STAT* pCS, INCHI_MODE nMode, int bTautFtcn); - - -#ifdef COMPILE_ANSI_ONLY - -static clock_t InchiClock(void); - -#ifdef INCHI_USETIMES -static clock_t InchiClock(void) -{ - struct tms buf; - clock_t c = times( &buf ); - if ( c != (clock_t)-1 ) { - return buf.tms_utime; - } - return 0; -} -#else -static clock_t InchiClock(void) -{ - clock_t c = clock(); - if ( c != (clock_t)-1 ) { - return c; - } - return 0; -} -#endif - -#define INCHI_MSEC(X) (long)((1000.0/(double)CLOCKS_PER_SEC)*(X)) -#define INCHI_CLOCK_T(X) (clock_t)( (double)(X) / 1000.0 * (double)CLOCKS_PER_SEC ) -const clock_t FullMaxClock = (clock_t)(-1); -const clock_t HalfMaxClock = (clock_t)(-1) / 2; -clock_t MaxPositiveClock = 0; -clock_t MinNegativeClock = 0; -clock_t HalfMaxPositiveClock = 0; -clock_t HalfMinNegativeClock = 0; - -static void FillMaxMinClock(void); /* keep compiler happy */ - -static void FillMaxMinClock(void) -{ - if ( !MaxPositiveClock ) { - clock_t valPos=0, val1 = 1; - while ( 0 < ((val1 <<= 1), (val1 |= 1)) ) { - valPos = val1; - } - MaxPositiveClock = valPos; - MinNegativeClock = -valPos; - HalfMaxPositiveClock = MaxPositiveClock / 2; - HalfMinNegativeClock = MinNegativeClock / 2; - } -} - - -/******** get current process time ****************************************/ -void InchiTimeGet( inchiTime *TickEnd ) -{ - TickEnd->clockTime = InchiClock(); -} -/******** returns difference TickEnd - TickStart in milliseconds **********/ -long InchiTimeMsecDiff( inchiTime *TickEnd, inchiTime *TickStart ) -{ - if ( FullMaxClock > 0 ) { - clock_t delta; - if ( !TickEnd || !TickStart ) - return 0; - /* clock_t is unsigned */ - if ( TickEnd->clockTime > TickStart->clockTime ) { - if ( TickEnd->clockTime > HalfMaxClock && - TickEnd->clockTime - TickStart->clockTime > HalfMaxClock ) { - /* overflow in TickStart->clockTime, actually TickStart->clockTime was later */ - delta = (FullMaxClock - TickEnd->clockTime) + TickStart->clockTime; - return -INCHI_MSEC(delta); - } - delta = TickEnd->clockTime - TickStart->clockTime; - return INCHI_MSEC(delta); - } else - if ( TickEnd->clockTime < TickStart->clockTime ) { - if ( TickStart->clockTime > HalfMaxClock && - TickStart->clockTime - TickEnd->clockTime > HalfMaxClock ) { - /* overflow in TickEnd->clockTime, actually TickEnd->clockTime was later */ - delta = (FullMaxClock - TickStart->clockTime) + TickEnd->clockTime; - return INCHI_MSEC(delta); - } - delta = TickStart->clockTime - TickEnd->clockTime; - return -INCHI_MSEC(delta); - } - return 0; /* TickEnd->clockTime == TickStart->clockTime */ - } else { - /* may happen under Win32 only where clock_t is SIGNED long */ - clock_t delta; - FillMaxMinClock( ); - if ( !TickEnd || !TickStart ) - return 0; - if ( TickEnd->clockTime >= 0 && TickStart->clockTime >= 0 || - TickEnd->clockTime <= 0 && TickStart->clockTime <= 0) { - delta = TickEnd->clockTime - TickStart->clockTime; - } else - if ( TickEnd->clockTime >= HalfMaxPositiveClock && - TickStart->clockTime <= HalfMinNegativeClock ) { - /* end is earlier than start */ - delta = (MaxPositiveClock - TickEnd->clockTime) + (TickStart->clockTime - MinNegativeClock); - delta = -delta; - } else - if ( TickEnd->clockTime <= HalfMinNegativeClock && - TickStart->clockTime >= HalfMaxPositiveClock ) { - /* start was earlier than end */ - delta = (MaxPositiveClock - TickStart->clockTime) + (TickEnd->clockTime - MinNegativeClock); - } else { - /* there was no overflow, clock passed zero */ - delta = TickEnd->clockTime - TickStart->clockTime; - } - return INCHI_MSEC(delta); - } -} -/******************* get elapsed time from TickStart ************************/ -long InchiTimeElapsed( inchiTime *TickStart ) -{ - inchiTime TickEnd; - if ( !TickStart ) - return 0; - InchiTimeGet( &TickEnd ); - return InchiTimeMsecDiff( &TickEnd, TickStart ); -} -/******************* add number of milliseconds to time *********************/ -void InchiTimeAddMsec( inchiTime *TickEnd, unsigned long nNumMsec ) -{ - clock_t delta; - if ( !TickEnd ) - return; - if ( FullMaxClock > 0 ) { - /* clock_t is unsigned */ - delta = INCHI_CLOCK_T(nNumMsec); - TickEnd->clockTime += delta; - } else { - /* may happen under Win32 only where clock_t is SIGNED long */ - /* clock_t is unsigned */ - FillMaxMinClock( ); - delta = INCHI_CLOCK_T(nNumMsec); - TickEnd->clockTime += delta; - } -} -/******************* check whether time has expired *********************/ -int bInchiTimeIsOver( inchiTime *TickStart ) -{ - if ( FullMaxClock > 0 ) { - clock_t clockCurrTime; - if ( !TickStart ) - return 0; - clockCurrTime = InchiClock(); - /* clock_t is unsigned */ - if ( TickStart->clockTime > clockCurrTime ) { - if ( TickStart->clockTime > HalfMaxClock && - TickStart->clockTime - clockCurrTime > HalfMaxClock ) { - /* overflow in clockCurrTime, actually clockCurrTime was later */ - return 1; - } - return 0; - } else - if ( TickStart->clockTime < clockCurrTime ) { - if ( clockCurrTime > HalfMaxClock && - clockCurrTime - TickStart->clockTime > HalfMaxClock ) { - /* overflow in TickStart->clockTime, actually TickStart->clockTime was later */ - return 0; - } - return 1; - } - return 0; /* TickStart->clockTime == clockCurrTime */ - } else { - /* may happen under Win32 only where clock_t is SIGNED long */ - clock_t clockCurrTime; - FillMaxMinClock( ); - if ( !TickStart ) - return 0; - clockCurrTime = InchiClock(); - if ( clockCurrTime >= 0 && TickStart->clockTime >= 0 || - clockCurrTime <= 0 && TickStart->clockTime <= 0) { - return (clockCurrTime > TickStart->clockTime); - } else - if ( clockCurrTime >= HalfMaxPositiveClock && - TickStart->clockTime <= HalfMinNegativeClock ) { - /* curr is earlier than start */ - return 0; - } else - if ( clockCurrTime <= HalfMinNegativeClock && - TickStart->clockTime >= HalfMaxPositiveClock ) { - /* start was earlier than curr */ - return 1; - } else { - /* there was no overflow, clock passed zero */ - return (clockCurrTime > TickStart->clockTime); - } - } -} - -#else - -/******** get current process time ****************************************/ -void InchiTimeGet( inchiTime *TickEnd ) -{ - if ( TickEnd ) { - struct _timeb timeb; - _ftime( &timeb ); - TickEnd->clockTime = (unsigned long)timeb.time; - TickEnd->millitime = (long)timeb.millitm; - } -} -/******** returns difference TickEnd - TickStart in milliseconds **********/ -long InchiTimeMsecDiff( inchiTime *TickEnd, inchiTime *TickStart ) -{ - long delta; - if ( !TickEnd || !TickStart ) { - return 0; - } - if ( TickEnd->clockTime >= TickStart->clockTime ) { - delta = (long)(TickEnd->clockTime - TickStart->clockTime); - delta *= 1000; - delta += TickEnd->millitime - TickStart->millitime; - } else { - delta = -(long)(TickStart->clockTime - TickEnd->clockTime); - delta *= 1000; - delta += TickEnd->millitime - TickStart->millitime; - } - return delta; -} -/******************* get elapsed time from TickStart ************************/ -long InchiTimeElapsed( inchiTime *TickStart ) -{ - inchiTime TickEnd; - if ( !TickStart ) - return 0; - InchiTimeGet( &TickEnd ); - return InchiTimeMsecDiff( &TickEnd, TickStart ); -} -/******************* add number of milliseconds to time *********************/ -void InchiTimeAddMsec( inchiTime *TickEnd, unsigned long nNumMsec ) -{ - long delta; - if ( !TickEnd ) - return; - TickEnd->clockTime += nNumMsec / 1000; - delta = nNumMsec % 1000 + TickEnd->millitime; - TickEnd->clockTime += delta / 1000; - TickEnd->millitime = delta % 1000; -} -/******************* check whether time has expired *********************/ -int bInchiTimeIsOver( inchiTime *TickEnd ) -{ - struct _timeb timeb; - if ( !TickEnd ) - return 0; - _ftime( &timeb ); - if ( TickEnd->clockTime > (unsigned long)timeb.time ) - return 0; - if ( TickEnd->clockTime < (unsigned long)timeb.time || - TickEnd->millitime < (long)timeb.millitm ) { - return 1; - } - return 0; -} -#endif - - -/****************************************************************************/ -/* length of canonic representation in sizeof(AT_NUMB) units */ -int GetCanonLengths( int num_at, sp_ATOM* at, ATOM_SIZES *s, T_GROUP_INFO *t_group_info ) -{ /* include taut. groups as additional "atoms" to the connection table 07-22-2002 */ - int i, nNumCT, nNumBonds, nNumTBonds=0, nNumDblBondsStereo=0, nNumAsymCarbStereo=0, nNumIsotopic=0; - T_GROUP *t_group = (s->nLenLinearCTTautomer && t_group_info)? t_group_info->t_group : NULL; - for (nNumBonds = 0, i = 0; i < num_at; i ++ ) { - nNumBonds += at[i].valence; - if ( at[i].iso_sort_key ) { - nNumIsotopic ++; /* not including tautomeric endpoints that are isotopic only due to mobile atoms */ - } - - if ( at[i].parity > 0 ) { /* ignore hydrogen isotope parities in at[i].parity2 */ - int j = 0, nStereoBondsToAtom=0; /* number of stereo double bonds at this atom */ - int k; - for ( ; j < MAX_NUM_STEREO_BONDS && (k=at[i].stereo_bond_neighbor[j]); j ++ ) { - nStereoBondsToAtom += (at[k-1].parity > 0); - } - nNumDblBondsStereo += nStereoBondsToAtom; - nNumAsymCarbStereo += !j; - } - } - nNumDblBondsStereo /= 2; - nNumBonds /= 2; - - s->nLenBonds = inchi_max( s->nLenBonds, nNumBonds ); - nNumCT = nNumBonds; /* total number of neighbors in the CT */ - -#if ( CT_ATOMID != CT_ATOMID_DONTINCLUDE ) - nNumCT += num_at; -#endif - - s->nLenCTAtOnly = inchi_max(s->nLenCTAtOnly, nNumCT); - - if ( t_group ) { - for ( i = 0; i < t_group_info->num_t_groups; i ++ ) { - nNumTBonds += t_group[i].nNumEndpoints; - } - nNumCT += nNumTBonds; -#if ( CT_ATOMID != CT_ATOMID_DONTINCLUDE ) - nNumCT += t_group_info->num_t_groups; -#endif - } - - nNumCT = inchi_max( 1, nNumCT ); /* keep GetBaseCanonRanking() happy */ - s->nLenCT = inchi_max(s->nLenCT, nNumCT); - s->nLenIsotopic = inchi_max(s->nLenIsotopic, nNumIsotopic); - s->nLenLinearCTStereoDble = inchi_max(s->nLenLinearCTStereoDble, nNumDblBondsStereo); - s->nLenLinearCTStereoCarb = inchi_max(s->nLenLinearCTStereoCarb, nNumAsymCarbStereo); - if ( t_group_info ) - s->nLenIsotopicEndpoints = inchi_max(s->nLenIsotopicEndpoints, t_group_info->nNumIsotopicEndpoints); - - return 0; -} -/****************************************************************************/ -int DeAllocateCS( CANON_STAT *pCS ) -{ -#define LOCAL_FREE(X) do{if(X){inchi_free(X); X=NULL;}}while(0) - - /* connection table */ - LOCAL_FREE( pCS->LinearCT ); - LOCAL_FREE( pCS->nCanonOrd ); - LOCAL_FREE( pCS->nSymmRank ); - LOCAL_FREE( pCS->nNum_H ); - LOCAL_FREE( pCS->nNum_H_fixed ); - LOCAL_FREE( pCS->nExchgIsoH ); - /* isotopic */ - LOCAL_FREE( pCS->LinearCTIsotopic ); - LOCAL_FREE( pCS->nSymmRankIsotopic ); - LOCAL_FREE( pCS->nCanonOrdIsotopic ); - /* isotopic tautomeric */ - LOCAL_FREE( pCS->LinearCTIsotopicTautomer ); - LOCAL_FREE( pCS->nCanonOrdIsotopicTaut ); - LOCAL_FREE( pCS->nSymmRankIsotopicTaut ); - /* stereo */ - LOCAL_FREE( pCS->LinearCTStereoDble ); - LOCAL_FREE( pCS->LinearCTStereoCarb ); - LOCAL_FREE( pCS->LinearCTStereoDbleInv ); - LOCAL_FREE( pCS->LinearCTStereoCarbInv ); - LOCAL_FREE( pCS->nCanonOrdStereo ); - LOCAL_FREE( pCS->nCanonOrdStereoInv ); - LOCAL_FREE( pCS->nCanonOrdStereoTaut ); - /* isotopic stereo */ - LOCAL_FREE( pCS->LinearCTIsotopicStereoDble ); - LOCAL_FREE( pCS->LinearCTIsotopicStereoCarb ); - LOCAL_FREE( pCS->LinearCTIsotopicStereoDbleInv ); - LOCAL_FREE( pCS->LinearCTIsotopicStereoCarbInv ); - LOCAL_FREE( pCS->bRankUsedForStereo ); - LOCAL_FREE( pCS->bAtomUsedForStereo ); - - LOCAL_FREE( pCS->nCanonOrdIsotopicStereo ); - LOCAL_FREE( pCS->nCanonOrdIsotopicStereoInv ); - LOCAL_FREE( pCS->nCanonOrdIsotopicStereoTaut ); - /* tautomeric part of the connection table */ - LOCAL_FREE( pCS->LinearCTTautomer ); - LOCAL_FREE( pCS->nCanonOrdTaut ); - LOCAL_FREE( pCS->nSymmRankTaut ); - - LOCAL_FREE( pCS->LinearCT2 ); - - /* for establishing constitutional equivalence */ - LOCAL_FREE( pCS->nPrevAtomNumber ); - - FreeNeighList( pCS->NeighList ); - pCS->NeighList = NULL; - - /* set zero lengths */ - pCS->nMaxLenLinearCTStereoDble = 0; - pCS->nLenLinearCTStereoDble = 0; - pCS->nMaxLenLinearCTStereoCarb = 0; - pCS->nLenLinearCTStereoCarb = 0; - pCS->nMaxLenLinearCTIsotopicStereoDble = 0; - pCS->nLenLinearCTIsotopicStereoDble = 0; - pCS->nMaxLenLinearCTIsotopicStereoCarb = 0; - pCS->nLenLinearCTIsotopicStereoCarb = 0; - pCS->nMaxLenLinearCTTautomer = 0; - pCS->nLenLinearCTTautomer = 0; - pCS->nMaxLenLinearCTIsotopic = 0; - pCS->nLenLinearCTIsotopic = 0; - pCS->nMaxLenLinearCTIsotopicTautomer = 0; - pCS->nLenLinearCTIsotopicTautomer = 0; - - /* set canon numbering lengths to zero */ - pCS->nLenCanonOrd = 0; - pCS->nLenCanonOrdIsotopic = 0; - pCS->nLenCanonOrdIsotopicTaut = 0; - pCS->nLenCanonOrdStereo = 0; - pCS->nLenCanonOrdStereoTaut = 0; - pCS->nLenCanonOrdIsotopicStereo = 0; - pCS->nLenCanonOrdIsotopicStereoTaut = 0; - pCS->nLenCanonOrdTaut = 0; - - return 0; - -#undef LOCAL_FREE -} -/****************************************************************************/ -int AllocateCS( CANON_STAT *pCS, int num_at, int num_at_tg, int nLenCT, int nLenCTAtOnly, - int nLenLinearCTStereoDble, int nLenLinearCTIsotopicStereoDble, - int nLenLinearCTStereoCarb, int nLenLinearCTIsotopicStereoCarb, - int nLenLinearCTTautomer, int nLenLinearCTIsotopicTautomer, - int nLenIsotopic, INCHI_MODE nMode, BCN *pBCN ) -{ -#define pCS_CALLOC(PTR,TYPE,LEN) (pCS->PTR=(TYPE*)inchi_calloc((size_t)(LEN),sizeof(*pCS->PTR))) - - int num_err = 0; - int num_t_groups = num_at_tg - num_at; - - pCS->nMode = nMode; - /* connection table */ - if ( (nMode & CMODE_CT) && nLenCT > 0 ) { - num_err += !pCS_CALLOC(LinearCT, AT_NUMB, nLenCT); - pCS->nMaxLenLinearCT = - pCS->nLenLinearCT = nLenCT; - pCS->nLenLinearCTAtOnly = nLenCTAtOnly; - num_err += !pCS_CALLOC(nCanonOrd, AT_RANK, num_at_tg); - num_err += !pCS_CALLOC(nSymmRank, AT_RANK, num_at_tg); - if ( pBCN ) { - num_err += !pCS_CALLOC(nNum_H, S_CHAR, num_at); - num_err += !pCS_CALLOC(nNum_H_fixed, S_CHAR, num_at); - num_err += !pCS_CALLOC(nExchgIsoH, S_CHAR, num_at); - } - } - /* isotopic */ - if ( (nMode & CMODE_ISO) && nLenIsotopic > 0 ) { - num_err += !pCS_CALLOC(LinearCTIsotopic, AT_ISOTOPIC, nLenIsotopic); - pCS->nMaxLenLinearCTIsotopic = - pCS->nLenLinearCTIsotopic = nLenIsotopic; - } - /* isotopic tautomeric */ - if ( (nMode & CMODE_ISO) && CANON_MODE_TAUT == (nMode & CANON_MODE_TAUT) ) { - if ( nLenLinearCTIsotopicTautomer > 0 ) { - num_err += !pCS_CALLOC(LinearCTIsotopicTautomer, AT_ISO_TGROUP, nLenLinearCTIsotopicTautomer); - pCS->nMaxLenLinearCTIsotopicTautomer = - pCS->nLenLinearCTIsotopicTautomer = nLenLinearCTIsotopicTautomer; - } - if ( num_t_groups > 0 ) { - num_err += !pCS_CALLOC(nCanonOrdIsotopicTaut, AT_RANK, num_t_groups); - num_err += !pCS_CALLOC(nSymmRankIsotopicTaut, AT_RANK, num_t_groups); - } - } - /* isotopic atoms & t-groups */ - if ( (nMode & CMODE_ISO) /*&& nLenIsotopic > 0*/ || - (nMode & CMODE_ISO) && CANON_MODE_TAUT == (nMode & CANON_MODE_TAUT) && nLenLinearCTIsotopicTautomer > 0 - ) { - num_err += !pCS_CALLOC(nSymmRankIsotopic, AT_RANK, num_at_tg); - num_err += !pCS_CALLOC(nCanonOrdIsotopic, AT_RANK, num_at_tg); - } - /* stereo */ - if ( (nMode & CMODE_STEREO) && nLenLinearCTStereoDble > 0 ) { - num_err += !pCS_CALLOC(LinearCTStereoDble, AT_STEREO_DBLE, nLenLinearCTStereoDble); - num_err += !pCS_CALLOC(LinearCTStereoDbleInv, AT_STEREO_DBLE, nLenLinearCTStereoDble); - pCS->nLenLinearCTStereoDbleInv = - pCS->nMaxLenLinearCTStereoDble = - pCS->nLenLinearCTStereoDble = nLenLinearCTStereoDble; - } - if ( (nMode & CMODE_STEREO) && nLenLinearCTStereoCarb > 0 ) { - num_err += !pCS_CALLOC(LinearCTStereoCarb, AT_STEREO_CARB, nLenLinearCTStereoCarb); - num_err += !pCS_CALLOC(LinearCTStereoCarbInv, AT_STEREO_CARB, nLenLinearCTStereoCarb); - pCS->nLenLinearCTStereoCarbInv = - pCS->nMaxLenLinearCTStereoCarb = - pCS->nLenLinearCTStereoCarb = nLenLinearCTStereoCarb; - } - if ( (nMode & CMODE_STEREO) && (nLenLinearCTStereoDble > 0 || nLenLinearCTStereoCarb > 0 ) ) { - num_err += !pCS_CALLOC(nCanonOrdStereo, AT_RANK, num_at_tg); - num_err += !pCS_CALLOC(nCanonOrdStereoInv, AT_RANK, num_at_tg); - if ( (nMode & CMODE_TAUT) && nLenLinearCTTautomer > 0 && num_t_groups > 0 ) { - num_err += !pCS_CALLOC(nCanonOrdStereoTaut, AT_RANK, num_t_groups); - } - } - /* isotopic stereo */ - if ( (nMode & CMODE_ISO_STEREO) && nLenLinearCTIsotopicStereoDble > 0 ) { - num_err += !pCS_CALLOC(LinearCTIsotopicStereoDble, AT_STEREO_DBLE, nLenLinearCTIsotopicStereoDble); - num_err += !pCS_CALLOC(LinearCTIsotopicStereoDbleInv, AT_STEREO_DBLE, nLenLinearCTIsotopicStereoDble); - pCS->nLenLinearCTIsotopicStereoDbleInv = - pCS->nMaxLenLinearCTIsotopicStereoDble = - pCS->nLenLinearCTIsotopicStereoDble = nLenLinearCTIsotopicStereoDble; - } - if ( (nMode & CMODE_ISO_STEREO) && nLenLinearCTIsotopicStereoCarb > 0 ) { - num_err += !pCS_CALLOC(LinearCTIsotopicStereoCarb, AT_STEREO_CARB, nLenLinearCTIsotopicStereoCarb); - num_err += !pCS_CALLOC(LinearCTIsotopicStereoCarbInv, AT_STEREO_CARB, nLenLinearCTIsotopicStereoCarb); - pCS->nLenLinearCTIsotopicStereoCarbInv = - pCS->nMaxLenLinearCTIsotopicStereoCarb = - pCS->nLenLinearCTIsotopicStereoCarb = nLenLinearCTIsotopicStereoCarb; - } - if ( (nMode & CMODE_ISO_STEREO) && (nLenLinearCTIsotopicStereoDble > 0 || nLenLinearCTIsotopicStereoCarb > 0 ) ) { - num_err += !pCS_CALLOC(nCanonOrdIsotopicStereo, AT_RANK, num_at_tg); - num_err += !pCS_CALLOC(nCanonOrdIsotopicStereoInv, AT_RANK, num_at_tg); - if ( (nMode & CMODE_TAUT) && nLenLinearCTTautomer > 0 && num_t_groups > 0 ) { - num_err += !pCS_CALLOC(nCanonOrdIsotopicStereoTaut, AT_RANK, num_t_groups); - } - } - if ( (nMode & CMODE_STEREO) && (nLenLinearCTStereoDble > 0 || nLenLinearCTStereoCarb > 0 ) || - (nMode & CMODE_ISO_STEREO) && (nLenLinearCTIsotopicStereoDble > 0 || nLenLinearCTIsotopicStereoCarb > 0 ) ) { - num_err += !pCS_CALLOC(bRankUsedForStereo, S_CHAR, num_at); - num_err += !pCS_CALLOC(bAtomUsedForStereo, S_CHAR, num_at); - } - /* tautomeric part of the connection table */ - if ( (nMode & CMODE_CT) && (nMode & CMODE_TAUT) && nLenLinearCTTautomer > 0 ) { - num_err += !pCS_CALLOC(LinearCTTautomer, AT_TAUTOMER, nLenLinearCTTautomer); - pCS->nMaxLenLinearCTTautomer = - pCS->nLenLinearCTTautomer = nLenLinearCTTautomer; - if ( num_t_groups > 0 ) { - num_err += !pCS_CALLOC(nCanonOrdTaut, AT_RANK, num_t_groups); - num_err += !pCS_CALLOC(nSymmRankTaut, AT_RANK, num_t_groups); - } - } - - if ( nMode & CMODE_CT ) - num_err += !pCS_CALLOC(LinearCT2, AT_NUMB, nLenCT); - - /* for establishing constitutional equivalence */ - num_err += !pCS_CALLOC(nPrevAtomNumber, AT_RANK, num_at_tg); - - /* set canon numbering lengths to zero */ - pCS->nLenCanonOrd = 0; - pCS->nLenCanonOrdIsotopic = 0; - pCS->nLenCanonOrdIsotopicTaut = 0; - pCS->nLenCanonOrdStereo = 0; - pCS->nLenCanonOrdStereoTaut = 0; - pCS->nLenCanonOrdIsotopicStereo = 0; - pCS->nLenCanonOrdIsotopicStereoTaut = 0; - pCS->nLenCanonOrdTaut = 0; - - - if ( num_err ) { - DeAllocateCS( pCS ); - return CT_OUT_OF_RAM; /* */ - } - return 0; - -#undef pCS_CALLOC -} -/****************************************************************************/ -#define COMPARE_WITH_CT(CT, CTLEN, VALUE, CONDITION) \ - if ( CONDITION ) { \ - if ( (VALUE) CT_GREATER_THAN (CT)[CTLEN] ) \ - return 1; /* not a minimal CT */ \ - (CONDITION) = (VALUE) == (CT)[CTLEN]; \ - } \ - (CT)[CTLEN] = VALUE; \ - (CTLEN)++ - -#define COMPARE_WITH_CTVAL(CTVAL, VALUE, CONDITION) \ - if ( CONDITION ) { \ - if ( (VALUE) CT_GREATER_THAN (CTVAL) ) \ - return 1; /* not a minimal CT */ \ - (CONDITION) = (VALUE) == (CTVAL); \ - } \ - (CTVAL) = VALUE - -#define COMPARE_WITH_CT2(CT, CTLEN, VALUE, CONDITION, OPER) \ - if ( CONDITION ) { \ - if ( (VALUE) CT_GREATER_THAN (CT)[CTLEN] ) { \ - (OPER); \ - return 1; /* not a minimal CT */ \ - } \ - (CONDITION) = (VALUE) == (CT)[CTLEN]; \ - } \ - (CT)[CTLEN] = VALUE; \ - (CTLEN)++ - -/****************************************************************************/ -int FillIsotopicAtLinearCT(int num_atoms, sp_ATOM* at, - const AT_RANK *nAtomNumber, - AT_ISOTOPIC *LinearCTIsotopic, - int nMaxLenLinearCTIsotopic, int *pnLenLinearCTIsotopic) -{ - /* at[i].init_rank = initial ranks before canonizing */ - /* nRank[i] = new ordering number for atoms: nRank=1,2,.. */ - /* nAtomNumber[r] = orig. atom number= 0,1,... for r = nRank-1 */ - /* nRank[nAtomNumber[r]] = r; r = 0,1,... */ - /* nAtomNumber[nRank[i]-1] = i; */ - - int i, k, rank; - int nLinearCTIsotopicLen=0; - - /* the following parts of the "name" should be compared */ - /* after the connection table comparison is done */ - /* to avoid wrong difference sign. So, these parts */ - /* go to a separate buffers. */ - if ( LinearCTIsotopic && nMaxLenLinearCTIsotopic > 0 ) { - memset( LinearCTIsotopic, 0, nMaxLenLinearCTIsotopic * sizeof(LinearCTIsotopic[0]) ); - } else { - return 0; - } - - /* rank = nRank[nAtomNumber[rank-1]] -- proposed atoms canon. numbers */ - for ( rank = 1; rank <= num_atoms; rank ++ ) { - - i = (int)nAtomNumber[rank-1]; /* current atom */ - - /**************************************************** - add isotopic atom info to LinearCTIsotopic - *****************************************************/ - - /* if the atom itself is not isotopic then add it only if */ - /* the atom is not an endpoint AND has attached T or D or 1H. */ - k = ( !at[i].endpoint && !(at[i].cFlags & AT_FLAG_ISO_H_POINT) && (at[i].num_iso_H[0] || at[i].num_iso_H[1] || at[i].num_iso_H[2]) ); - if ( at[i].iso_atw_diff || k ) { - if ( CHECK_OVERFLOW(nLinearCTIsotopicLen, nMaxLenLinearCTIsotopic) ) - return CT_OVERFLOW; /* */ - LinearCTIsotopic[nLinearCTIsotopicLen].at_num = (AT_RANK)rank; - LinearCTIsotopic[nLinearCTIsotopicLen].iso_atw_diff = at[i].iso_atw_diff; - LinearCTIsotopic[nLinearCTIsotopicLen].num_1H = (NUM_H)(k? at[i].num_iso_H[0] : 0); - LinearCTIsotopic[nLinearCTIsotopicLen].num_D = (NUM_H)(k? at[i].num_iso_H[1] : 0); - LinearCTIsotopic[nLinearCTIsotopicLen].num_T = (NUM_H)(k? at[i].num_iso_H[2] : 0); - nLinearCTIsotopicLen ++; - } - - } /* end of cycle over all atoms. */ - - if ( LinearCTIsotopic ) { - if ( *pnLenLinearCTIsotopic ) { - if ( *pnLenLinearCTIsotopic != nLinearCTIsotopicLen ) - return CT_LEN_MISMATCH; /* */ - }else - *pnLenLinearCTIsotopic = nLinearCTIsotopicLen; - } - - /* Return value: >0 => OK */ - return nLinearCTIsotopicLen; -} - -/****************************************************************************/ -int FillTautLinearCT2(int num_atoms, int num_at_tg, int bIsoTaut, - const AT_RANK *nRank, const AT_RANK *nAtomNumber, - const AT_RANK *nSymmRank, const AT_RANK *nRankIso, - const AT_RANK *nAtomNumberIso, const AT_RANK *nSymmRankIso, - AT_TAUTOMER *LinearCTTautomer, - int nMaxLenLinearCTTautomer, int *pnLenLinearCTTautomer, - AT_ISO_TGROUP *LinearCTIsotopicTautomer, - int nMaxLenLinearCTIsotopicTautomer, - int *pnLenLinearCTIsotopicTautomer, - T_GROUP_INFO *t_group_info) -{ - /* nRank[i] = Canonical numbers of atoms,.. */ - /* nAtomNumber[r] = orig. atom number= 0,1,... for r = nRank-1 */ - /* nRank[nAtomNumber[r]] = r; r = 0,1,... */ - /* nAtomNumber[nRank[i]-1] = i; */ - - T_GROUP *t_group; - - int i, j, len=0, g, num_num, offset, max_len = 0, len_iso=0; - const static int max_num_num = sizeof(t_group->num)/sizeof(t_group->num[0]); - const static int max_num_iso = sizeof(LinearCTIsotopicTautomer->num)/sizeof(LinearCTIsotopicTautomer->num[0])+T_NUM_NO_ISOTOPIC; - - /**************************************************************************** - - Tautomeric groups 07-22-2002, modified 12-2003 - - ****************************************************************************/ - - if ( num_at_tg > num_atoms && t_group_info && t_group_info->num_t_groups ) { - int num_t_groups = t_group_info->num_t_groups; - AT_NUMB *tGroupNumber = t_group_info->tGroupNumber; - AT_NUMB *tSymmRank = tGroupNumber + TGSO_SYMM_RANK*num_t_groups; /* equivalence */ - AT_NUMB *tiSymmRank = tGroupNumber + TGSO_SYMM_IRANK*num_t_groups; - AT_NUMB *tiGroupNumber = tGroupNumber + TGSO_SYMM_IORDER*num_t_groups; - AT_RANK nOffset = (AT_RANK)num_atoms; - /* Fill Canonical ranks and Symmetry Ranks */ - /* memcpy( tPrevGroupNumber, tGroupNumber, num_t_groups*sizeof(tPrevGroupNumber[0])); */ - for ( i = num_atoms, j = 0; i < num_at_tg; i ++, j ++ ) { - /* tPrevGroupNumber[j] = */ - tGroupNumber[j] = nAtomNumber[i] - nOffset; - tSymmRank[j] = nSymmRank[i] - nOffset; - if ( bIsoTaut ) { - tiGroupNumber[j] = nAtomNumberIso[i] - nOffset; - tiSymmRank[j] = nSymmRankIso[i] - nOffset; - } - } - /* Sort enpoints within each tautomeric group according to the canonical ranks */ - pn_RankForSort = nRank; - for ( i = 0; i < num_t_groups; i ++ ) { - qsort( t_group_info->nEndpointAtomNumber + (int)t_group_info->t_group[i].nFirstEndpointAtNoPos, - t_group_info->t_group[i].nNumEndpoints, - sizeof(t_group_info->nEndpointAtomNumber[0]), - CompRank ); - } - /* fill out LinearCTTautomer */ - if ( nMaxLenLinearCTTautomer ) { - max_len = T_GROUP_HDR_LEN * t_group_info->num_t_groups + t_group_info->nNumEndpoints+1; - if ( max_len > nMaxLenLinearCTTautomer ) - return CT_OVERFLOW; /* */ - } - - /**************************************************************** - * tautomer group format (#: is an offset) - **************************************************************** - * HEADER (T_GROUP_HDR_LEN=3+3iso) - * 0: N = number of endpoints ( t_group->nNumEndpoints ) - * 1: number of mobile groups ( t_group->num[0] ) - * 2: number of neg. charges ( t_group->num[1] ) {note: T_NUM_NO_ISOTOPIC=2} - * ENDPOINT RANKS - * 3..N+2: sorted tautomer group endpoint ranks; the sorting order is in - * t_group_info->nEndpointAtomNumber[t_group->nFirstEndpointAtNoPos+j], j=0..N-1 - * - * End mark : N==0 - ****************************************************************/ - /* num_num = t_group_info->bIgnoreIsotopic? T_NUM_NO_ISOTOPIC : max_num_num; */ - num_num = max_num_num; /* always include isotopic info; ignore it at the CT comparison step. */ - for ( i = 0; i < t_group_info->num_t_groups; i ++ ) { - g = tGroupNumber[i]; /* ith tautomeric group number in canonical order */ - t_group = t_group_info->t_group + g; - /******************************************************* - * Tautomer non-isotopic part: LinearCTTautomer - *******************************************************/ - /* check length */ - if ( CHECK_OVERFLOW(len + T_GROUP_HDR_LEN + t_group->nNumEndpoints, max_len) ) - return CT_OVERFLOW; /* */ - - /* t_group header: number of endpoints */ - LinearCTTautomer[len++] = t_group->nNumEndpoints; - /* t_group header: */ - /* (a) number of mobile groups in the t_group (number of H + number of (-) ) and */ - /* (b) number of mobile negative charges (-) in the t_group */ - for ( j = 0; j < T_NUM_NO_ISOTOPIC; j ++ ) { - LinearCTTautomer[len++] = t_group->num[j]; - } - /* t_group endpoint ranks link the group to the tautomeric endpoint atoms in the structure */ - /* according to their ranks */ - for ( j = 0, offset = t_group->nFirstEndpointAtNoPos; j < t_group->nNumEndpoints; j ++ ) { - LinearCTTautomer[len++] = nRank[(int)t_group_info->nEndpointAtomNumber[offset+j]]; - } - } - if ( nMaxLenLinearCTTautomer ) { - LinearCTTautomer[len++] = 0; /* or CT_INITVALUE ??? */ - if ( len != max_len ) { - len = -len; /* program error */ /* */ - } else - if ( *pnLenLinearCTTautomer && *pnLenLinearCTTautomer != len ) { - return CT_LEN_MISMATCH; - } else { - *pnLenLinearCTTautomer = len; - } - } else { - *pnLenLinearCTTautomer = 0; - } - /****************************************************************** - * Isotopic Tautomeric mobile groups part: LinearCTIsotopicTautomer - ******************************************************************/ - if ( nMaxLenLinearCTIsotopicTautomer && !t_group_info->nNumIsotopicEndpoints ) { - for ( i = 0; i < t_group_info->num_t_groups; i ++ ) { - g = tiGroupNumber[i]; /* ith tautomeric group number in canonical order */ - t_group = t_group_info->t_group + g; - /* find if mobile hydrogens are isotopic */ - if ( !t_group->iWeight ) { - continue; /* no isotopic H */ - } - if ( CHECK_OVERFLOW(len_iso, nMaxLenLinearCTIsotopicTautomer) ) - return CT_OVERFLOW; /* */ - for ( j = T_NUM_NO_ISOTOPIC; j < max_num_num && j < max_num_iso; j ++ ) { - /* num_T, num_D, num_1H */ - LinearCTIsotopicTautomer[len_iso].num[j-T_NUM_NO_ISOTOPIC] = t_group->num[j]; - } - /* link to tautomer group LinearCTTautomer[i]: */ - LinearCTIsotopicTautomer[len_iso++].tgroup_num = (AT_NUMB)(i + 1); /* t_group isotopic rank */ - } - } - if ( nMaxLenLinearCTIsotopicTautomer ) { - if ( *pnLenLinearCTIsotopicTautomer && *pnLenLinearCTIsotopicTautomer != len_iso ) { - return CT_LEN_MISMATCH; - } - *pnLenLinearCTIsotopicTautomer = len_iso; - } else { - *pnLenLinearCTIsotopicTautomer = 0; - } - - } - return len; -} -/**************************************************************************** - * - * Update a linear connection table out of final ranks - */ -int UpdateFullLinearCT( int num_atoms, int num_at_tg, sp_ATOM* at, AT_RANK *nRank, AT_RANK *nAtomNumber, - CANON_STAT* pCS, int bFirstTime ) -{ - /* at[i].init_rank = initial ranks before canonizing */ - /* nRank[i] = new ordering number for atoms: nRank=1,2,.. */ - /* nAtomNumber[r] = orig. atom number= 0,1,... for r = nRank-1 */ - /* nRank[nAtomNumber[r]] = r; r = 0,1,... */ - /* nAtomNumber[nRank[i]-1] = i; */ - - AT_NUMB nNeighborNumber[MAXVAL]; - int i, j, k, num_neigh, rank, bCompare; /*, nRetVal; */ - - T_GROUP_INFO *t_group_info = NULL; - T_GROUP *t_group = NULL; - AT_NUMB *nEndpointAtomNumber = NULL; - - int nCTLen=0, nCTLenAtOnly=0; - - AT_NUMB r_neigh; - AT_NUMB *LinearCT = pCS->LinearCT; - - /* the following parts of the "name" should be compared */ - /* after the connection table comparison is done */ - /* to avoid wrong difference sign. So, these parts */ - /* go to a separate buffers. */ - /* -- currently not used at all at all -- */ - -#if CT_ATOMID != CT_ATOMID_DONTINCLUDE - AT_NUMB r0_at_type; -#endif - - bCompare = bFirstTime? 0 : 1; - - if ( num_at_tg > num_atoms ) { - t_group_info = pCS->t_group_info; - t_group = t_group_info->t_group; - } else { - t_group_info = NULL; - t_group = NULL; - } - - /**********************************************************************/ - /* */ - /* CYCLE 1: FILL OUT CONNECTION TABLE(S) FOR ALL ATOMS */ - /* ** NOT INCLUDING ISOTOPIC ATOMS AND 1H, 2H(D), 3H(T) ** */ - /* */ - /* rank = nRank[nAtomNumber[rank-1]] -- proposed atoms canon. numbers */ - /**********************************************************************/ - for ( rank = 1; rank <= num_atoms; rank ++ ) { - i = (int)nAtomNumber[rank-1]; /* current atom */ -#if ( CT_ATOMID == CT_ATOMID_IS_CURRANK ) - r0_at_type = (AT_NUMB)rank; /* current Rank */ -#else -#if ( CT_ATOMID == CT_ATOMID_IS_INITRANK ) - r0_at_type = (AT_NUMB)at[i].init_rank; /* chemical + neighborhood ID */ -#else -#if ( CT_ATOMID == CT_ATOMID_DONTINCLUDE ) -#else - #error Undefined or wrong definition of CT_ATOMID -#endif -#endif -#endif - - /* add atom to the CT */ -#if ( CT_ATOMID != CT_ATOMID_DONTINCLUDE ) - if ( CHECK_OVERFLOW(nCTLen, pCS->nMaxLenLinearCT) ) - return CT_OVERFLOW; /* */ - COMPARE_WITH_CT(LinearCT, nCTLen, r0_at_type, bCompare); -#endif - /******************************************************* - add neighbors and (if required) bonds to CT - ********************************************************/ - - /* sort neighbors */ - num_neigh = at[i].valence; - for ( k = 0; k < num_neigh; k ++) { - nNeighborNumber[k] = (AT_NUMB)k; - } - pNeighborsForSort = at[i].neighbor; - pn_RankForSort = nRank; - insertions_sort( nNeighborNumber, (size_t)num_neigh, sizeof(nNeighborNumber[0]), CompNeighborsAT_NUMBER ); - - for ( k = 0; k < num_neigh; k ++) { - /* rank = (new current atom Rank) */ - if ( (int)(r_neigh = (AT_NUMB)nRank[(int)at[i].neighbor[(int)nNeighborNumber[k]]]) - CT_NEIGH_SMALLER_THAN rank ) { - if ( CHECK_OVERFLOW(nCTLen, pCS->nMaxLenLinearCT) ) - return CT_OVERFLOW; /* */ - COMPARE_WITH_CT( LinearCT, nCTLen, r_neigh, bCompare); - } - } - - /* add CT row delimiter */ - - } /* end of cycle over all atoms. */ - - nCTLenAtOnly = nCTLen; - - /************************************************************** - - Tautomeric groups 07-22-2002 - - ***************************************************************/ - - for ( rank = num_atoms + 1; rank <= num_at_tg; rank ++ ) { - j = (int)nAtomNumber[rank-1]; /* current "atom" */ - i = j - num_atoms; /* current t-group */ -#if ( CT_ATOMID == CT_ATOMID_IS_CURRANK ) - r0_at_type = (AT_NUMB)rank; /* current Rank */ -#else -#if ( CT_ATOMID == CT_ATOMID_IS_INITRANK ) - r0_at_type = (AT_NUMB)rank; /* current Rank or (AT_NUMB)at[i].init_rank; ==> chemical + neighborhood ID */ -#else -#if ( CT_ATOMID == CT_ATOMID_DONTINCLUDE ) -#else - #error Undefined or wrong definition of CT_ATOMID -#endif -#endif -#endif - - /* add atom to the CT */ -#if ( CT_ATOMID != CT_ATOMID_DONTINCLUDE ) - if ( CHECK_OVERFLOW(nCTLen, pCS->nMaxLenLinearCT) ) - return CT_OVERFLOW; /* */ - COMPARE_WITH_CT(LinearCT, nCTLen, r0_at_type, bCompare); -#endif - - /******************************************************* - add neighbors and (if required) bonds to CT - ********************************************************/ - - /* sort endpoints */ - nEndpointAtomNumber = t_group_info->nEndpointAtomNumber+(int)t_group[i].nFirstEndpointAtNoPos; - pn_RankForSort = nRank; - num_neigh = (int)t_group[i].nNumEndpoints; - insertions_sort( nEndpointAtomNumber, (size_t)num_neigh, sizeof(nEndpointAtomNumber[0]), CompRank); - - for ( k = 0; k < num_neigh; k ++) { - /* rank = (new current atom Rank) */ - if ( (int)(r_neigh = (AT_NUMB)nRank[(int)nEndpointAtomNumber[k]]) - CT_NEIGH_SMALLER_THAN rank ) { - if ( CHECK_OVERFLOW(nCTLen, pCS->nMaxLenLinearCT) ) - return CT_OVERFLOW; /* */ - COMPARE_WITH_CT( LinearCT, nCTLen, r_neigh, bCompare); - } - } - } /* end of cycle over all tautomeric groups. */ - - /* compare bonds types */ - /* compare elements */ - - if ( LinearCT ) { - - if ( pCS->nLenLinearCT ) { - if ( pCS->nLenLinearCT != nCTLen ) - return CT_LEN_MISMATCH; /* */ - } else { - pCS->nLenLinearCT = nCTLen; - } - - if ( pCS->nLenLinearCT ) { - if ( pCS->nLenLinearCTAtOnly != nCTLenAtOnly ) - return CT_LEN_MISMATCH; /* */ - } else { - pCS->nLenLinearCTAtOnly = nCTLenAtOnly; - } - - } - - /* Return: 0=> identical CT; -1=> new CT is smaller than the previous one */ - return (bCompare-1); -} - -/*********************************************************************************************/ -/* if (*bChanged & 1) then nSymmRank has been rearranged because for some r - min{i: r=nSymmRank[nAtomNumber[i]]}+1 != r - if (*bChanged & 2) then ranks nTempRank[] from nSymmRank[] differ from input nCurrRank[] - - on exit: - - nSymmRank[] have been updated if (*bChanged & 1) - nCurrRank[] have been updated if (*bChanged & 1) - nTempRank[] is always same as nCurrRank[] - nAtomNumber[] have been sorted so that - (i < j) <=> (nSymmRank[nAtomNumber[i]] <= nSymmRank[nAtomNumber[j]]) -*/ -int FixCanonEquivalenceInfo( int num_at_tg, AT_RANK *nSymmRank, AT_RANK *nCurrRank, - AT_RANK *nTempRank, AT_NUMB *nAtomNumber, int *bChanged) -{ - int nNumDiffRanks, bChangeSymmRank, bChangeCurrRank=0; - /* sort equivalence information */ - /* - int i; - for ( i = 0; i < num_at_tg; i ++ ) { - nAtomNumber[i] = i; - } - */ - pn_RankForSort = nSymmRank; /* minimal class representatives: min ranks for equiv. atoms */ - qsort( nAtomNumber, num_at_tg, sizeof(nAtomNumber[0]), CompRanksOrd ); - - /* convert equivalence information nSymmRank[] into ranks array nTempRank[] */ - /* eq. info contains min. possible ranks for eq. atoms; nCurrRank contains max. possible ranks */ - nNumDiffRanks = SortedEquInfoToRanks( nSymmRank/*inp*/, nTempRank/*out*/, nAtomNumber, num_at_tg, &bChangeSymmRank ); - /* check whether nCurrRank is same as new initial ranks calculated from nSymmRank[] */ - bChangeCurrRank = memcmp( nCurrRank, nTempRank, num_at_tg*sizeof(nTempRank[0])); - - /*----------------------------------------------------------------------- - if ( bChangeSymmRank || bChangeCurrRank ) { - This is the case when the initial equitable partitioning does not produce - constitutionally equivalent classes of atoms. - Rebuild nSymmRank[] according to the new nCurrRank[] := nTempRank[] - For such structures the found canonical numbers of the constitutionally equivalent atoms - are not contiguous (see nCanonRank and nSymmRank examples below). Here arrays - nCurrRank, nAtomNumber, and nSymmRank are changed so that later the - contiguous canonical numbers for equivalent atoms can be obtained - (see GetCanonRanking under - "III. Get final canonical numbering (no stereo, no isotopic)". - - Example: for CAS=37520-11-9 (ID=21247: Ethane, 1,2-dicyclopropyl-), - - the numbers are the "final canon. numbers, nCanonRank" - 1 - - HC 7 5 3 - | \ - | >CH--CH2 CH - | / \ / | - HC H2C--CH< | - \ | - 2 6 8 CH - - 4 - - the arrays (arranged according to ordering in nAtomNumberTemp) are: - before SortedEquInfoToRanks after SortedRanksToEquInfo - orig. atom nos.,nAtomNumberTemp: {4 5 6 7 0 1 2 3} {4 5 6 7 0 1 2 3} - order numbers for sorted ranks: {0 1 2 3 4 5 6 7} {0 1 2 3 4 5 6 7} - canonical numbering, nCanonRank: {1 2 5 6 3 4 7 8} {1 2 5 6 3 4 7 8} - constit. equivalence, nSymmRank: {1 1 1 1 3 3 7 7} {1 1 1 1 5 5 7 7} used later - initial equivalence, nCurrRank: {6 6 6 6 6 6 8 8} {4 4 4 4 6 6 8 8} used later - initial numbering, nAtomNumber: {2 3 4 7 0 1 6 7} {0 1 2 3 4 5 6 7} used later - final, no stereo, no isotopic, after III. GetCanonRanking: - final canon. numbers, nCanonRank: {1 2 3 4 5 6 7 8} final - } - ----------------------------------------------------------------------------------*/ - if ( bChangeCurrRank ) { - memcpy( nCurrRank, nTempRank, num_at_tg*sizeof(nCurrRank[0]) ); - } - if ( bChangeSymmRank ) { - SortedRanksToEquInfo( nSymmRank/*out*/, nTempRank/*inp*/, nAtomNumber, num_at_tg ); - } - if ( bChanged ) { - *bChanged = (0 != bChangeSymmRank) | 2*(0 != bChangeCurrRank); - } - return nNumDiffRanks; -} -/* isotopic canonicalization */ -/*********************************************************************** - * - * Canon_INChI (former GetCanonRankingUsingEquivInfo) - * - */ -int Canon_INChI3(int num_atoms, int num_at_tg, sp_ATOM* at, - CANON_STAT* pCS, INCHI_MODE nMode, int bTautFtcn) -{ -/**************************************************************** - -0. Initiation, Prepare initial ranks for GetCanonRanking() - -I. Find constitutionally equivalent atoms and possibly canonical numbering -I.1 Set tautomer=On, stereo=isotopic=Off -I.2 GetCanonRanking(): Find constitutionally equivalent atoms and possibly canonical numbering -1.3 Fix canonical equivalence info if needed (if the fix is needed then the numbering is not canonical) - -II. Get final non-isotopic canonical numbering. Simultaneously obtain non-minimal isotopic and stereo CTs - GetCanonRanking() with pCS->bKeepSymmRank = 1 - FillOutStereoParities() (create initial stereo descriptors) - save non-isotopic canonicalization final results - hide isotopic and tautomeric results (for historical reasons only) - - -III. Find constitutionally equivalent isotopic atoms (for isotopic stereo canonicalization) -III.1 Allocate more memory -III.2 fill allocated memory with the initial data -III.3 duplicate, save old and add isotopic info to the new pCS->t_group_info -III.4 Prepare initial isotopic ranks for GetCanonRanking() -III.5 GetCanonRanking() to Find constitutionally equivalent ISOTOPIC atoms and tautomer groups -III.6 Fix canonical isotopic equivalence information and derive ranks out of it - -IV. Prepare a second Rank/AtomNumber Stack for mapping. - -V. Optimize isotopic part (optimized) - map_isotopic_atoms2() - save isotopic canonical numbering - -VI. Optimize stereo descriptors (optimized) - map_stereo_bonds4() - - -VII. Optimize isotopic stereo descriptors (optimized) - SwitchAtomStereoAndIsotopicStereo() - SetCtToIsotopicStereo() - FillOutStereoParities() - SetUseAtomForStereo() - map_stereo_bonds4() - - SwitchAtomStereoAndIsotopicStereo() - SetCtToNonIsotopicStereo() - - - - -*****************************************************************/ - - int nRet = 0, i, n; - - - /******************************************************** - input non-stereo canonical info - ********************************************************/ - BCN *pBCN = pCS->pBCN; - FTCN *ftcn = pBCN->ftcn + bTautFtcn; - - /******************************************************** - set mode flags - ********************************************************/ - /* tautomeric structure */ - int bTaut = (num_at_tg > num_atoms) && pCS->t_group_info && pCS->t_group_info->num_t_groups && pCS->t_group_info->t_group; - /* special case: induced by exchangable isotopic H inequivalence of atoms in formally non-tautomeric structure */ - int bIsoXchgH = pCS->t_group_info && pCS->t_group_info->nNumIsotopicEndpoints > 1 && - pCS->t_group_info->nIsotopicEndpointAtomNumber && pCS->t_group_info->nIsotopicEndpointAtomNumber[0] && - (pCS->t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE|TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE)) - /* && (ftcn->nCanonFlags & CANON_FLAG_ISO_TAUT_DIFF)*/; - int bHasIsotopicCanonData = (ftcn->PartitionCtIso.AtNumber && ftcn->PartitionCtIso.Rank && ftcn->nSymmRankCtIso); - /* bHasIsotopicCanonData==0 means - * (1) No isotopic atoms in the component OR - * (2) the component has only exchangable isotopic H that do not change canonical numbering and equivalence. - */ - T_GROUP_INFO *t_group_info1 = bTaut? pCS->t_group_info : NULL; - /*int bIsoXchgH = t_group_info1 && t_group_info1->nNumIsotopicEndpoints && t_group_info1->nIsotopicEndpointAtomNumber;*/ - /* isotopic canonicalization */ - int bCanonIsotopic = bHasIsotopicCanonData && ( nMode & CMODE_ISO ) && ( pCS->LinearCTIsotopic || pCS->LinearCTIsotopicTautomer || bIsoXchgH ); - /* stereo canonicalization */ - int bCanonStereo = ( nMode & CMODE_STEREO ) && ( pCS->LinearCTStereoDble || pCS->LinearCTStereoCarb ); - /* stereo isotopic canonicalization */ - int bCanonIsoStereo = bHasIsotopicCanonData && ( nMode & CMODE_ISO_STEREO ) && (pCS->LinearCTIsotopicStereoDble || pCS->LinearCTIsotopicStereoCarb) && bCanonIsotopic; - int bIsoTaut = (bTaut && bCanonIsotopic); - - int bIgnoreIsotopicInputGroups; - int bIgnoreIsotopicInputAtoms; - - AT_RANK **pRankStack1 = pBCN->pRankStack; - int nRankStackLen = pBCN->nMaxLenRankStack; - int num_max = pBCN->num_max; /* allocation lengths in *pRankStack1[] */ - NEIGH_LIST *NeighList = ftcn->NeighList; - - int nNumCurrRanks = 0; - AT_RANK *nTempRank = NULL; - - AT_RANK *nSymmRank = NULL; - - AT_RANK *nAtomNumber = NULL; - AT_RANK *nRank = NULL; - - AT_RANK **pRankStack2 = NULL; - AT_RANK *nCanonRankStereo = NULL; - AT_RANK *nCanonRankStereoInv = NULL; - AT_RANK *nSymmStereo = NULL; - - AT_RANK *nCanonRankIsotopicStereo = NULL; - AT_RANK *nCanonRankIsotopicStereoInv = NULL; - - CUR_TREE *cur_tree = NULL; - CUR_TREE CurrentTree; - - - /*AT_ISO_TGROUP *LinearCTIsotopicTautomer = NULL; */ - - - CANON_STAT CS2; - CANON_STAT* pCS2 = &CS2; - - inchiTime ulStartTime, ulEndTime; - /*=========== Mode Bits (low 8 bits, bit 0 is Least Significant Bit) =========== - - Mode Bits Description - '0' c 0 Only one connection table canonicalization - '1' C 1 Recalculate CT using fixed nSymmRank - '2' i 1|2 Isotopic canonicalization (internal) - '3' I 1|2|4 Isotopic canonicalization (output) - '4' s 1|8 Stereo canonicalization - '5' S 1|2|4|16 Stereo isotopic canonicalization - '6' A 1|2|4|8|16 Output All - - --- high 8 bits ---- - --- obsolete, only historical interest. ------ - 1-2 : 0 => at[i].init_rank from Morgan+NeighList - 1 => at[i].init_rank from Atom Invariants - 2 => at[i].init_rank from nSymmRank[] - (at[i].init_rank is included in LinearCT - depending on CT_ATOMID definition) - 3 : 1 => Get Stereo canonical info - 4 : 1 => Get Isotopic canonical info - 5 : 1 => Get Charge/Radical canonical info - ==================================================================*/ - /*int nOutputMode = 0;*/ /* obsolete */ - - - int bSwitchedAtomToIsotopic = 0; - - - /* vABParityUnknown holds actual value of an internal constant signifying */ - /* unknown parity: either the same as for undefined parity (default==standard) */ - /* or a specific one (non-std; requested by SLUUD switch). */ - int vABParityUnknown = AB_PARITY_UNDF; - if ( 0 != ( nMode & REQ_MODE_DIFF_UU_STEREO) ) - { - /* Make labels for unknown and undefined stereo different */ - vABParityUnknown = AB_PARITY_UNKN; - } - - - InchiTimeGet( &ulStartTime ); - - - - - *pCS2 = *pCS; /* save input information and pointers to allocated memory */ - - /* set "ignore isotopic differences in tautomer groups" true */ - if ( bTaut ) { - /* save request for isotopic tautomeric groups */ - bIgnoreIsotopicInputGroups = pCS->t_group_info->bIgnoreIsotopic; - pCS->t_group_info->bIgnoreIsotopic = 1; - } else { - bIgnoreIsotopicInputGroups = 1; - } - /* save request for isotopic name */ - bIgnoreIsotopicInputAtoms = pCS->bIgnoreIsotopic; - /* set "ignore isotopic differences in atoms" true */ - pCS->bIgnoreIsotopic = 1; - - - /* save non-isotopic and isotopic canonicalization results */ - pCS->nCanonFlags = ftcn->nCanonFlags; - /* 1. non-isotopic */ - - /* linear CT, H */ - memcpy( pCS->LinearCT, ftcn->LinearCt, ftcn->nLenLinearCt * sizeof(pCS->LinearCT[0]) ); - if ( pCS->nNum_H && ftcn->nNumH ) { - for ( i = 0; i < num_atoms; i ++ ) { - pCS->nNum_H[i] = /*(S_CHAR)*/(CHAR_MASK & ftcn->nNumH[i]); - } - } - if ( pCS->nNum_H_fixed && ftcn->nNumHFixH ) { - for ( i = 0; i < num_atoms; i ++ ) { - pCS->nNum_H_fixed[i] = /*(S_CHAR)*/(CHAR_MASK & ftcn->nNumHFixH[i]); - } - } - pCS->nLenLinearCT = ftcn->nLenLinearCt; - pCS->nLenLinearCTAtOnly = ftcn->nLenLinearCtAtOnly; - - /* save non-isotopic atoms equivalence and numbering */ - if ( pCS->nSymmRank ) { - memcpy( pCS->nSymmRank, ftcn->nSymmRankCt, num_at_tg * sizeof(pCS->nSymmRank[0]) ); - } - if ( pCS->nCanonOrd ) { - memcpy( pCS->nCanonOrd, ftcn->PartitionCt.AtNumber, num_at_tg * sizeof(pCS->nCanonOrd[0]) ); - pCS->nLenCanonOrd = num_atoms; - } - if ( ftcn->iso_exchg_atnos && pCS->nExchgIsoH ) { - for ( i = 0; i < num_atoms; i ++ ) { - pCS->nExchgIsoH[i] = !ftcn->iso_exchg_atnos[i]; /* (pCS->nExchgIsoH[i]==1) => tautomeric or hetero atoms that may exchange isotopic H */ - } - } - /* 2. isotopic */ - - if ( bCanonIsotopic ) { - /* linear CT, num_H are same as non-isotopic */ - /* save atoms equivalence and numbering */ - if ( pCS->nSymmRankIsotopic ) { - memcpy( pCS->nSymmRankIsotopic, ftcn->nSymmRankCtIso, num_at_tg * sizeof(pCS->nSymmRankIsotopic[0])); - } - if ( pCS->nCanonOrdIsotopic ) { - memcpy( pCS->nCanonOrdIsotopic, ftcn->PartitionCtIso.AtNumber, num_at_tg * sizeof(pCS->nCanonOrdIsotopic[0]) ); - pCS->nLenCanonOrdIsotopic = num_at_tg; - } - nRet = FillIsotopicAtLinearCT( num_atoms, at, ftcn->PartitionCtIso.AtNumber, - pCS->LinearCTIsotopic, pCS->nMaxLenLinearCTIsotopic, &pCS->nLenLinearCTIsotopic ); - if ( RETURNED_ERROR(nRet) ) { - goto exit_function; - } - if ( nRet < 0 ) { - nRet = CT_TAUCOUNT_ERR; - goto exit_function; - } - } else { - pCS->nMaxLenLinearCTIsotopic = 0; - pCS->nMaxLenLinearCTIsotopicTautomer = 0; - } - - /* fill out tautomeric groups, isotopic and non-isotopic tautomeric CT and t_group_info1->tGroupNumber */ - if ( bTaut ) { - bIsoTaut = bIsoTaut && ftcn->PartitionCtIso.Rank && - ftcn->PartitionCtIso.AtNumber && ftcn->nSymmRankCtIso; - nRet = FillTautLinearCT2( num_atoms, num_at_tg, bIsoTaut, - ftcn->PartitionCt.Rank, ftcn->PartitionCt.AtNumber, ftcn->nSymmRankCt, - ftcn->PartitionCtIso.Rank, ftcn->PartitionCtIso.AtNumber, ftcn->nSymmRankCtIso, - pCS->LinearCTTautomer, pCS->nMaxLenLinearCTTautomer, &pCS->nLenLinearCTTautomer, - pCS->LinearCTIsotopicTautomer, pCS->nMaxLenLinearCTIsotopicTautomer, &pCS->nLenLinearCTIsotopicTautomer, - t_group_info1 ); - - if ( RETURNED_ERROR(nRet) ) { - goto exit_function; - } - if ( nRet <= 0 ) { - nRet = CT_TAUCOUNT_ERR; - goto exit_function; - } else { - /* tautomeric groups: save non-isotopic symmetry & t_group order */ - int num_t_groups = t_group_info1->num_t_groups; - AT_NUMB *tGroupNumber = t_group_info1->tGroupNumber; - AT_NUMB *tSymmRank = tGroupNumber + TGSO_SYMM_RANK*num_t_groups; - if ( pCS->nSymmRankTaut ) { - memcpy( pCS->nSymmRankTaut, tSymmRank, num_t_groups * sizeof(pCS->nSymmRank[0]) ); /* fixed 5-23-02 */ - } - if ( pCS->nCanonOrdTaut ) { - memcpy( pCS->nCanonOrdTaut, tGroupNumber, num_t_groups * sizeof(pCS->nCanonOrdTaut[0]) ); - pCS->nLenCanonOrdTaut = num_t_groups; - } - if ( bCanonIsotopic /*&& pCS->nLenLinearCTIsotopicTautomer*/ ) { - /* tautomeric groups: save isotopic symmetry & t_group order */ - /*AT_NUMB ntRankOffset = (AT_RANK)num_atoms;*/ - AT_NUMB *tiSymmRank = tGroupNumber + TGSO_SYMM_IRANK*num_t_groups; - AT_NUMB *tiGroupNumber = tGroupNumber + TGSO_SYMM_IORDER*num_t_groups; - if ( pCS->nSymmRankIsotopicTaut ) { - memcpy( pCS->nSymmRankIsotopicTaut, tiSymmRank, num_t_groups * sizeof(pCS->nSymmRankIsotopicTaut[0]) ); - } - memcpy( pCS->nCanonOrdIsotopicTaut, tiGroupNumber, num_t_groups * sizeof(pCS->nCanonOrdIsotopicTaut[0]) ); - pCS->nLenCanonOrdIsotopicTaut = num_t_groups; - } - } - } - /* save connection table if requested */ - if ( pCS->LinearCT2 ) { - memcpy( pCS->LinearCT2, pCS->LinearCT, sizeof(pCS->LinearCT2[0])*pCS->nLenLinearCT ); - pCS->nLenLinearCT2 = pCS->nLenLinearCT; - pCS->nLenLinearCTAtOnly2 = pCS->nLenLinearCTAtOnly; - } - - if ( num_atoms <= 1 ) { - bCanonStereo = 0; /* a sinle atom + possibly terminal hydrogen atoms */ - if ( num_atoms < 1 || !at[0].parity2 ) { - bCanonIsoStereo = 0; /* structure; for example Cl- or CH4 */ - } - } - - if ( !bCanonStereo && !(bCanonIsotopic && bCanonIsoStereo) ) { - goto exit_function; /* skip stereo canonicalization */ - } - - - - /********************************************************** - Mode - ***********************************************************/ - nMode = nMode & CANON_MODE_MASK; - - /* memory allocation */ - - nAtomNumber = (AT_RANK *)qmalloc(num_max*sizeof(*nAtomNumber)); - nRank = (AT_RANK *)qmalloc(num_max*sizeof(*nRank)); - nTempRank = (AT_RANK *)qmalloc(num_max*sizeof(*nTempRank)); - nSymmRank = (AT_RANK *)qmalloc(num_max*sizeof(*nSymmRank)); - /*********************************************** - 0.1 Initialization - ************************************************/ - - - if ( !NeighList || !nAtomNumber || !nTempRank || - !nRank || !pCS->LinearCT ) { - nRet = CT_OUT_OF_RAM; /* program error */ /* */ - goto exit_function; - } - - pCS->NeighList = NeighList; - - *pCS2 = *pCS; /* save input information and pointers to allocated memory */ - - if ( !(nMode & CMODE_NOEQ_STEREO) && (bCanonStereo || bCanonIsoStereo ) ) { - /* will be used to discover vertex equivalences in stereo canonicalization */ - memset( &CurrentTree, 0, sizeof(CurrentTree) ); - cur_tree = &CurrentTree; - } - - - pCS->bCmpStereo = 0; - pCS->bCmpIsotopicStereo = 0; - - - if ( bCanonStereo || bCanonIsoStereo ) { - int ii, nn; - - /* stereo or isotopic canonicalization: we need a second set of ranks for mapping */ - /* (isotopic atoms or stereo can only increase nNumCurrRanks) */ - pRankStack2 = (AT_RANK **) inchi_calloc( nRankStackLen, sizeof(AT_RANK *) ); - if ( pRankStack2 ) { - /* prepare for ranks reuse */ - for ( nn = 2; nn < nRankStackLen && pRankStack1[nn]; nn ++ ) { - pRankStack1[nn][0] = 0; /* means ranks have to be calculated */ - } - /* reuse memory to reduce number of allocations: */ - /* move last half of pointers from pRankStack1 to pRankStack2 */ - /* The first 2 elements will be assigned separately */ - if ( (nn = (nn-2)/2) > 0 ) { - for ( ii = 2+nn; ii < nRankStackLen && pRankStack1[ii]; ii ++ ) { - pRankStack2[ii-nn] = pRankStack1[ii]; - pRankStack1[ii] = NULL; - } - } - } else { - nRet = CT_OUT_OF_RAM; /* */ - goto exit_function; /* program error */ - } - } - - if ( bCanonStereo ) { - - /* *pCS2 = *pCS; */ /* save input information and pointers to allocated memory */ - - /* initial ranking for non-isotopic mapping */ - memcpy( nAtomNumber, ftcn->PartitionCt.AtNumber, num_at_tg * sizeof(nAtomNumber[0]) ); - memcpy( nRank, ftcn->PartitionCt.Rank, num_at_tg * sizeof(nRank[0]) ); - memcpy( nSymmRank, ftcn->nSymmRankCt, num_at_tg * sizeof(nSymmRank[0]) ); - - /* nSymmRank changes if canonical numbers of constitutionally equivalent atoms are not contiguous */ - nNumCurrRanks = FixCanonEquivalenceInfo( num_at_tg, nSymmRank /* in&out*/, - nRank, nTempRank /* out */, nAtomNumber /* in&out */, NULL); - /* atom numbers in canonical order */ - memcpy( pCS->nPrevAtomNumber, ftcn->PartitionCt.AtNumber, num_at_tg * sizeof(nAtomNumber[0]) ); - - /* fill stereo part of the connection table with initial (not optimized) parities */ - /* input - pCS->LinearCTStereoDble - pCS->LinearCTStereoCarb - pCS->nMaxLenLinearCTStereoCarb - pCS->nMaxLenLinearCTStereoDble - */ - nRet = FillOutStereoParities( at, num_atoms, ftcn->PartitionCt.Rank, ftcn->PartitionCt.AtNumber, - nRank, nAtomNumber, pCS, 0 /* bIsotopic */ ); - /* output - pCS->LinearCTStereoDble - pCS->LinearCTStereoCarb - pCS2->nLenLinearCTStereoCarb - pCS2->nLenLinearCTStereoDble - */ - if ( RETURNED_ERROR( nRet ) ) { - goto exit_function; - } - if ( nRet < 0 ) { - nRet = CT_STEREOCOUNT_ERR; - goto exit_function; - } - - /*************************************************************** - * - * VI. Optimize non-isotopic stereo descriptors (optimized) - * - ***************************************************************/ - - /* allocate memory for stereo canonicalization */ - - if ( !nCanonRankStereo ) - nCanonRankStereo = (AT_RANK *) qmalloc(num_max*sizeof(*nCanonRankStereo)); - if ( !nSymmStereo && !(nMode & CMODE_NOEQ_STEREO) ) - nSymmStereo = (AT_RANK *) qmalloc((num_max+1)*sizeof(*nSymmStereo)); - if ( !(nMode & CMODE_NOEQ_STEREO) && 0 > CurTreeAlloc( cur_tree, num_at_tg ) ) { - nRet = CT_OUT_OF_RAM; /* */ - goto exit_function; - } - /* check allocations and assign first 2 elements of pRankStack2 */ - if ( pRankStack1 && pRankStack2 && - nCanonRankStereo && - /* nCurrRankStereo && nAtomNumberCurrStereo &&*/ - (nSymmStereo || (nMode & CMODE_NOEQ_STEREO)) ) { - pRankStack1[0] = pRankStack2[0] = nRank; - pRankStack1[1] = pRankStack2[1] = nAtomNumber; - } else { - nRet = CT_OUT_OF_RAM; /* */ - goto exit_function; - } - - /**************************************************************** - * - * VI-A. Optimize non-isotopic non-inverted stereo descriptors - * - ****************************************************************/ - - /* set the 1st ranks in the rest of the stack to zero: prepare for ranks reuse */ - for ( n = 2; n < nRankStackLen && pRankStack1[n]; n ++ ) { - pRankStack1[n][0] = 0; /* means ranks have to be recalculated */ - } - /* set the 1st ranks to zero: prepare for ranks reuse */ - for ( n = 2; n < nRankStackLen && pRankStack2[n]; n ++ ) { - pRankStack2[n][0] = 0; /* means ranks have to be recalculated */ - } - - /* for debugging or statistics */ - pCS->lNumBreakTies = - pCS->lNumNeighListIter= - pCS->lNumTotCT = - pCS->lNumDecreasedCT = - pCS->lNumRejectedCT = - pCS->lNumEqualCT = 0; - pCS->bKeepSymmRank = 0; - pCS->bFirstCT = 1; /* To fill out nCanonRankStereo[] in map_stero_atoms2() */ - - /****************************************************************************** - nCanonRank contains input canonical numbering - nCanonRankStereo will be filled with a transposition of canonical numbering - which (1) keeps connection table unchanged and - (2) provides minimal stereo descriptors in - pCS->LinearCTStereoDble (length=pCS->nLenLinearCTStereoDble) - pCS->LinearCTStereoCarb (length=pCS->nLenLinearCTStereoCarb) - */ - nRet = map_stereo_bonds4 - ( at, num_atoms, num_at_tg, num_max, 0, ftcn->PartitionCt.Rank, ftcn->PartitionCt.AtNumber, - nCanonRankStereo, nSymmRank, - pRankStack1, pRankStack2, nTempRank, nNumCurrRanks, - nSymmStereo, NeighList, pCS, cur_tree, 0 /* nNumMappedBonds */, - vABParityUnknown); - - if ( RETURNED_ERROR( nRet ) ) { - if ( nRet == CT_TIMEOUT_ERR ) - goto exit_function; - else - goto exit_function; /* program error */ - } else { - int bFailed = 0; - if ( !nRet ) { - bFailed = 1; /* progrm error */ - pCS2->nLenLinearCTStereoCarb = - pCS->nLenLinearCTStereoCarb = -abs(pCS->nLenLinearCTStereoCarb); - pCS2->nLenLinearCTStereoDble = - pCS->nLenLinearCTStereoDble = -abs(pCS->nLenLinearCTStereoDble); - nRet = CT_STEREOCOUNT_ERR; /* */ - goto exit_function; /* program error */ - } else { - /* save non-isotopic lengths */ - pCS2->nLenLinearCTStereoDble = pCS->nLenLinearCTStereoDble; - pCS2->nLenLinearCTStereoCarb = pCS->nLenLinearCTStereoCarb; - nRet = 0; - } - - /* save stereo canonical numbering */ - if ( pCS->nCanonOrdStereo ) { - for ( i = n = 0; i < num_at_tg; i ++ ) { - if ( nCanonRankStereo[i] && (int)nCanonRankStereo[i] <= num_at_tg ) { - pCS->nCanonOrdStereo[ (int)nCanonRankStereo[i] - 1 ] = (AT_NUMB)i; - } else { - bFailed ++; - } - } - pCS->nLenCanonOrdStereo = ( bFailed )? -num_atoms : num_atoms; - } - /* save stereo tautomer groups numbering */ - if ( bTaut && pCS->nCanonOrdStereoTaut ) { - if ( 0 < (nRet = SortTautomerGroupsAndEndpoints( t_group_info1, num_atoms, num_at_tg, nCanonRankStereo ) ) ) { - /*non-isotopic contains symmetry ranks */ - int num_t_groups = t_group_info1->num_t_groups; - AT_NUMB *tGroupNumber = t_group_info1->tGroupNumber; - /*AT_NUMB *tiSymmRank = tGroupNumber + TGSO_SYMM_IRANK*num_t_groups; */ - memcpy( pCS->nCanonOrdStereoTaut, tGroupNumber, num_t_groups*sizeof(pCS->nCanonOrdStereoTaut[0]) ); - pCS->nLenCanonOrdStereoTaut = ( bFailed ) ? - -num_t_groups : num_t_groups; - } else - if ( RETURNED_ERROR( nRet ) ) { - goto exit_function; - } else { - nRet = 0; - } - /*SortTautomerGroupsAndEndpoints( t_group_info1, nCanonRank ); */ /* ??? return to non-isotopic canonical numbering */ - } - } - - /**************************************************** - * - * VI-B. Optimize INVERTED stereo descriptors - * - ****************************************************/ - if ( !nCanonRankStereoInv ) - nCanonRankStereoInv = (AT_RANK *) qmalloc(num_max*sizeof(*nCanonRankStereoInv)); - if ( !nCanonRankStereoInv ) { - nRet = CT_OUT_OF_RAM; /* */ - goto exit_function; - } - /* copy previous non-isotopic stereo canonicalization results to Inv initial data */ - /* assign pointers */ - pCS->LinearCTStereoDble = pCS2->LinearCTStereoDbleInv; - pCS->LinearCTStereoCarb = pCS2->LinearCTStereoCarbInv; - - /* copy the lengths */ - pCS2->nLenLinearCTStereoDbleInv = - pCS->nLenLinearCTStereoDbleInv = - pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTStereoDble; - - pCS2->nLenLinearCTStereoCarbInv = - pCS->nLenLinearCTStereoCarbInv = - pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTStereoCarb; - - if ( pCS->nLenLinearCTStereoDble > 0 || pCS->nLenLinearCTStereoCarb > 0 ) { - /* copy previous results, the canonical stereo CT */ - memcpy( pCS->LinearCTStereoDble, pCS2->LinearCTStereoDble, pCS->nLenLinearCTStereoDble*sizeof(pCS->LinearCTStereoDble[0]) ); - memcpy( pCS->LinearCTStereoCarb, pCS2->LinearCTStereoCarb, pCS->nLenLinearCTStereoCarb*sizeof(pCS->LinearCTStereoCarb[0]) ); - } - memcpy( nCanonRankStereoInv, nCanonRankStereo, num_max * sizeof(nCanonRankStereoInv[0]) ); - if ( pCS->nCanonOrdStereoInv && pCS->nCanonOrdStereo ) { - /* in case there is nothing to invert */ - memcpy( pCS->nCanonOrdStereoInv, pCS->nCanonOrdStereo, num_at_tg*sizeof(pCS->nCanonOrdStereoInv[0])); - } - - /****************************** - * - * Invert stereo - * - ******************************/ - - /********************************************************************************* - * Create initial approximation for the minimization of the stereo descriptors: - * invert stereogenic atom parities, one parity in each allene, all parities in - * pCS->LinearCTStereoCarb and allene parities in pCS->nLenLinearCTStereoDble - */ - nRet = InvertStereo( at, num_at_tg, nCanonRankStereo, nTempRank, pCS, 1 /* bInvertLinearCTStereo */ ); - if ( RETURNED_ERROR( nRet ) ) { - goto exit_function; - } else - if ( nRet > 0 ) { - /* InvertStereo() has done some changes */ - nRet = 0; - /* FillOutStereoParities() has already been called to fill out these 2 LinearCTs */ - - /* set the 1st ranks in the rest of the stack to zero: prepare for ranks reuse */ - for ( n = 2; n < nRankStackLen && pRankStack1[n]; n ++ ) { - pRankStack1[n][0] = 0; /* means ranks have to be recalculated */ - } - /* set the 1st ranks to zero: prepare for ranks reuse */ - for ( n = 2; n < nRankStackLen && pRankStack2[n]; n ++ ) { - pRankStack2[n][0] = 0; /* means ranks have to be recalculated */ - } - - /* for debugging or statistics */ - pCS->lNumBreakTies = - pCS->lNumNeighListIter= - pCS->lNumTotCT = - pCS->lNumDecreasedCT = - pCS->lNumRejectedCT = - pCS->lNumEqualCT = 0; - pCS->bKeepSymmRank = 0; - pCS->bFirstCT = 1; /* To fill out nCanonRankStereo[] in map_stero_atoms2() */ - - /****************************************************************************** - ftcn->PartitionCt.Rank contains input canonical numbering - nCanonRankStereoInv will be filled with a transposition of canonical numbering - which (1) keeps connection table unchanged and - (2) provides minimal stereo descriptors in - pCS->LinearCTStereoDble (length=pCS->nLenLinearCTStereoDble) - pCS->LinearCTStereoCarb (length=pCS->nLenLinearCTStereoCarb) - ******************************************************************************/ - nRet = map_stereo_bonds4 - ( at, num_atoms, num_at_tg, num_max, 0, ftcn->PartitionCt.Rank, ftcn->PartitionCt.AtNumber, - nCanonRankStereoInv, nSymmRank, - pRankStack1, pRankStack2, nTempRank, nNumCurrRanks, nSymmStereo, - NeighList, pCS, cur_tree, 0, - vABParityUnknown); - if ( RETURNED_ERROR( nRet ) ) { - if ( nRet == CT_TIMEOUT_ERR ) - goto exit_function; - else - goto exit_function; /* program error */ - } else { - int bFailed = 0; - if ( !nRet ) { - bFailed = 1; /* progrm error */ - pCS2->nLenLinearCTStereoCarb = - pCS->nLenLinearCTStereoCarb = -abs(pCS->nLenLinearCTStereoCarb); - pCS2->nLenLinearCTStereoDble = - pCS->nLenLinearCTStereoDble = -abs(pCS->nLenLinearCTStereoDble); - nRet = CT_STEREOCOUNT_ERR; /* */ - goto exit_function; /* program error */ - } - - /* save non-isotopic pointers & lengths for INVERTED stereo */ - pCS->nLenLinearCTStereoDbleInv = - pCS2->nLenLinearCTStereoDbleInv = pCS->nLenLinearCTStereoDble; - pCS->nLenLinearCTStereoCarbInv = - pCS2->nLenLinearCTStereoCarbInv = pCS->nLenLinearCTStereoCarb; - - /* restore pointers and lengths to non-inverted stereo */ - /* -- this is needed for InvertStereo() back, see below */ - pCS->LinearCTStereoDble = pCS2->LinearCTStereoDble; - pCS->LinearCTStereoCarb = pCS2->LinearCTStereoCarb; - pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTStereoDble; - pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTStereoCarb; - /* consistency check */ - if ( pCS->nLenLinearCTStereoDbleInv != pCS->nLenLinearCTStereoDble || - pCS->nLenLinearCTStereoCarbInv != pCS->nLenLinearCTStereoCarb ) { - nRet = CT_CALC_STEREO_ERR; - goto exit_function; /* program error */ - } - /****************************** - * - * Invert stereo back - * - ****************************** - * (make sure that pointers - * pCS->LinearCTStereoCarb, - * pCS->LinearCTStereoDble - * and corresponding lengths - * have been restored) - ******************************/ - /********************************************************************************* - * invert only stereogenic atom parities and one parity in each allene, DO NOT - * change parities in pCS->LinearCTStereoCarb and pCS->nLenLinearCTStereoDble - */ - nRet = InvertStereo( at, num_at_tg, nCanonRankStereo, nTempRank, pCS, 0 ); - if ( RETURNED_ERROR( nRet ) ) { - goto exit_function; - } - nRet = 0; - - - /* save stereo canonical numbering */ - if ( pCS->nCanonOrdStereoInv ) { - for ( i = n = 0; i < num_at_tg; i ++ ) { - if ( nCanonRankStereoInv[i] && (int)nCanonRankStereoInv[i] <= num_at_tg ) { - pCS->nCanonOrdStereoInv[ (int)nCanonRankStereoInv[i] - 1 ] = (AT_NUMB)i; - } else { - bFailed ++; - } - } - pCS->nLenCanonOrdStereo = ( bFailed )? -num_atoms : num_atoms; - } - - /* compare inverted and non-inverted stereo */ - pCS->bCmpStereo = 2 + CompareLinCtStereo( - pCS->LinearCTStereoDbleInv, pCS->nLenLinearCTStereoDbleInv, - pCS->LinearCTStereoCarbInv, pCS->nLenLinearCTStereoCarbInv, - pCS->LinearCTStereoDble, pCS->nLenLinearCTStereoDble, - pCS->LinearCTStereoCarb, pCS->nLenLinearCTStereoCarb - ); - - } - } else - if ( 0 == nRet ) { - /* nothing has been done, restore pointers and lengths for stereo */ - pCS->LinearCTStereoDble = pCS2->LinearCTStereoDble; - pCS->LinearCTStereoCarb = pCS2->LinearCTStereoCarb; - pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTStereoDble; - pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTStereoCarb; - } - - - } - /* restore "ignore isotopic differences in tautomer groups" */ - if ( bTaut ) { - /* save request for isotopic tautomeric groups */ - pCS->t_group_info->bIgnoreIsotopic = bIgnoreIsotopicInputGroups; - } - /* restore request for isotopic name */ - pCS->bIgnoreIsotopic = bIgnoreIsotopicInputAtoms; - - if ( bCanonIsoStereo && bCanonIsotopic ) { - - /**************************************************************** - * - * VII. Optimize isotopic stereo descriptors (optimized) - * - ****************************************************************/ - /* - pCS->LinearCTIsotopic = NULL; - */ - - /* initial ranking for isotopic mapping */ - memcpy( nAtomNumber, ftcn->PartitionCtIso.AtNumber, num_at_tg * sizeof(nAtomNumber[0]) ); - memcpy( nRank, ftcn->PartitionCtIso.Rank, num_at_tg * sizeof(nRank[0]) ); - memcpy( nSymmRank, ftcn->nSymmRankCtIso, num_at_tg * sizeof(nSymmRank[0]) ); - - /* nSymmRank will change if canonical numbers of of constitutionally equivalent atoms are not contiguous */ - nNumCurrRanks = FixCanonEquivalenceInfo( num_at_tg, nSymmRank /* in&out*/, - nRank, nTempRank /* out */, nAtomNumber /* in&out */, NULL); - - memcpy( pCS->nPrevAtomNumber, ftcn->PartitionCtIso.AtNumber, num_at_tg * sizeof(nAtomNumber[0]) ); - - /* allocate memory for optimized stereo canonicalization */ - /* for stereo canonical numbering to be found. */ - if ( !nCanonRankIsotopicStereo ) - nCanonRankIsotopicStereo = (AT_RANK *) qmalloc(num_max*sizeof(*nCanonRankIsotopicStereo)); - if ( !nSymmStereo && !(nMode & CMODE_NOEQ_STEREO) ) - nSymmStereo = (AT_RANK *) qmalloc((num_max+1)*sizeof(*nSymmStereo)); - - if ( !(nMode & CMODE_NOEQ_STEREO) && CurTreeAlloc( cur_tree, num_at_tg ) ) { - nRet = CT_OUT_OF_RAM; /* */ - goto exit_function; - } - /* check allocations and assign first 2 elements of pRankStack2 */ - if ( pRankStack1 && pRankStack2 && - nCanonRankIsotopicStereo && - (nSymmStereo || (nMode & CMODE_NOEQ_STEREO)) ) { - - pRankStack1[0] = pRankStack2[0] = nRank; /* pRankStack1[0,1] shall be unchanged */ - pRankStack1[1] = pRankStack2[1] = nAtomNumber; - } else { - nRet = CT_OUT_OF_RAM; /* */ - goto exit_function; - } - - /****************************************************************** - Important: fill out a list of stereo atoms and bonds including - those which are stereo due to isotopic atoms only and create - LinearCT stereo descriptors for the canonical numbering - ******************************************************************/ - - - /* at[] has certain members for non-isotopic and isotopic stereo; switch them */ - SwitchAtomStereoAndIsotopicStereo( at, num_atoms, &bSwitchedAtomToIsotopic ); - /* prepare stereo connection tables' pointers */ - SetCtToIsotopicStereo( pCS, pCS2 ); - - nRet = FillOutStereoParities( at, num_atoms, ftcn->PartitionCtIso.Rank, ftcn->PartitionCtIso.AtNumber, - nRank, nAtomNumber, pCS, 1 /* bIsotopic */); - if (RETURNED_ERROR(nRet)) { - goto exit_function; /* program error */ - } else - if ( !nRet ) { - /* no isotopic stereo */ - pCS2->nLenLinearCTIsotopicStereoDble = pCS->nLenLinearCTIsotopicStereoDble = 0; - pCS2->nLenLinearCTIsotopicStereoCarb = pCS->nLenLinearCTIsotopicStereoCarb = 0; - pCS->nLenCanonOrdIsotopicStereo = 0; - pCS->nLenCanonOrdIsotopicStereoTaut = 0; - pCS2->nLenLinearCTIsotopicStereoDbleInv = pCS->nLenLinearCTIsotopicStereoDbleInv = 0; - pCS2->nLenLinearCTIsotopicStereoCarbInv = pCS->nLenLinearCTIsotopicStereoCarbInv = 0; - goto bypass_isotopic_stereo; - } else { - nRet = 0; /* not an error */ - } - - - - /************************************************************* - * - * VII-A. Optimize non-inverted isotopic stereo descriptors - * - *************************************************************/ - - /* set the 1st ranks in the rest of the stack to zero: prepare for ranks reuse */ - for ( n = 2; n < nRankStackLen && pRankStack1[n]; n ++ ) { - pRankStack1[n][0] = 0; /* means ranks have to be recalculated */ - } - /* set the 1st ranks to zero: prepare for ranks reuse */ - for ( n = 2; n < nRankStackLen && pRankStack2[n]; n ++ ) { - pRankStack2[n][0] = 0; /* means ranks have to be recalculated */ - } - - /* for debugging or statistics */ - pCS->lNumBreakTies = - pCS->lNumNeighListIter= - pCS->lNumTotCT = - pCS->lNumDecreasedCT = - pCS->lNumRejectedCT = - pCS->lNumEqualCT = 0; - pCS->bKeepSymmRank = 0; - pCS->bFirstCT = 1; /* To fill out nCanonRankStereo[] in map_stero_atoms2() */ - - /************************************************************************************** - nCanonRankIsotopic contains input canonical numbering - nCanonRankIsotopicStereo will be filled with a transposition of canonical numbering - that (1) keeps connection table unchanged and - (2) provides minimal stereo descriptors in - pCS->LinearCTStereoDble (length=pCS->nLenLinearCTStereoDble) - pCS->LinearCTStereoCarb (length=pCS->nLenLinearCTStereoCarb) - ***************************************************************************************/ - nRet = map_stereo_bonds4 - ( at, num_atoms, num_at_tg, num_max, 0, ftcn->PartitionCtIso.Rank, - ftcn->PartitionCtIso.AtNumber, - nCanonRankIsotopicStereo, nSymmRank, - pRankStack1, pRankStack2, nTempRank, nNumCurrRanks, - nSymmStereo, NeighList, pCS, cur_tree, 0, - vABParityUnknown); - if ( RETURNED_ERROR( nRet ) ) { - goto exit_function; - } else { - int bFailed = 0; - - if ( !nRet ) { - bFailed = 1; /* program error */ - pCS2->nLenLinearCTIsotopicStereoDble = - pCS->nLenLinearCTIsotopicStereoDble = -abs(pCS->nLenLinearCTStereoDble); - pCS2->nLenLinearCTIsotopicStereoCarb = - pCS->nLenLinearCTIsotopicStereoCarb = -abs(pCS->nLenLinearCTStereoCarb); - nRet = CT_STEREOCOUNT_ERR; /* */ - goto exit_function; /* program error */ - } else { - /* save isotopic lengths */ - pCS->nLenLinearCTIsotopicStereoDble = - pCS2->nLenLinearCTIsotopicStereoDble = pCS->nLenLinearCTStereoDble; - pCS->nLenLinearCTIsotopicStereoCarb = - pCS2->nLenLinearCTIsotopicStereoCarb = pCS->nLenLinearCTStereoCarb; - - /* save stereo canonical numbering */ - if ( pCS->nCanonOrdIsotopicStereo ) { - for ( i = n = 0; i < num_at_tg; i ++ ) { - if ( nCanonRankIsotopicStereo[i] && (int)nCanonRankIsotopicStereo[i] <= num_at_tg ) { - pCS->nCanonOrdIsotopicStereo[ (int)nCanonRankIsotopicStereo[i] - 1 ] = (AT_NUMB)i; - } else { - bFailed ++; - } - } - pCS->nLenCanonOrdIsotopicStereo = bFailed? -num_atoms : num_atoms; - } - /* save stereo tautomer groups numbering */ - if ( pCS->nCanonOrdIsotopicStereoTaut ) { - if ( 0 < (nRet=SortTautomerGroupsAndEndpoints( t_group_info1, num_atoms, num_at_tg, nCanonRankIsotopicStereo ) ) ) { - /*non-isotopic contains symmetry ranks */ - int num_t_groups = t_group_info1->num_t_groups; - AT_NUMB *tGroupNumber = t_group_info1->tGroupNumber; - /*AT_NUMB *tiSymmRank = tGroupNumber + TGSO_SYMM_IRANK*num_t_groups; */ - memcpy( pCS->nCanonOrdIsotopicStereoTaut, tGroupNumber, num_t_groups*sizeof(pCS->nCanonOrdIsotopicStereoTaut[0]) ); - pCS->nLenCanonOrdIsotopicStereoTaut = bFailed? -num_t_groups:num_t_groups; - - /*SortTautomerGroupsAndEndpoints( t_group_info1, nCanonRank ); */ /* ??? return to non-isotopic canonical numbering */ - } else - if ( RETURNED_ERROR( nRet ) ) { - goto exit_function; - } else { - nRet = 0; - } - } - } - } - - /********************************************************** - * - * VII-B. Optimize INVERTED isotopic stereo descriptors - * - **********************************************************/ - if ( !nCanonRankIsotopicStereoInv ) - nCanonRankIsotopicStereoInv = (AT_RANK *) qmalloc(num_max*sizeof(*nCanonRankIsotopicStereoInv)); - if ( !nCanonRankIsotopicStereoInv ) { - nRet = CT_OUT_OF_RAM; /* */ - goto exit_function; - } - /* copy previous isotopic stereo canonicalization results to Inv initial data */ - /* assign pointers */ - pCS->LinearCTStereoDble = pCS2->LinearCTIsotopicStereoDbleInv; /* enable stereo */ - pCS->LinearCTStereoCarb = pCS2->LinearCTIsotopicStereoCarbInv; - - - /* copy the lengths */ - pCS2->nLenLinearCTIsotopicStereoDbleInv = - pCS->nLenLinearCTStereoDbleInv = - pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTIsotopicStereoDble; - - pCS2->nLenLinearCTIsotopicStereoCarbInv = - pCS->nLenLinearCTStereoCarbInv = - pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTIsotopicStereoCarb; - - if ( pCS->nLenLinearCTStereoDble > 0 || pCS->nLenLinearCTStereoCarb > 0 ) { - /* copy previous results, the canonical stereo CT */ - memcpy( pCS->LinearCTStereoDble, pCS2->LinearCTIsotopicStereoDble, pCS->nLenLinearCTStereoDble*sizeof(pCS->LinearCTStereoDble[0]) ); - memcpy( pCS->LinearCTStereoCarb, pCS2->LinearCTIsotopicStereoCarb, pCS->nLenLinearCTStereoCarb*sizeof(pCS->LinearCTStereoCarb[0]) ); - } - memcpy( nCanonRankIsotopicStereoInv, nCanonRankIsotopicStereo, num_max * sizeof(nCanonRankIsotopicStereoInv[0]) ); - if ( pCS->nCanonOrdIsotopicStereoInv && pCS->nCanonOrdIsotopicStereo ) { - /* in case there is nothing to invert */ - memcpy( pCS->nCanonOrdIsotopicStereoInv, pCS->nCanonOrdIsotopicStereo, num_at_tg*sizeof(pCS->nCanonOrdIsotopicStereoInv[0])); - } - - /****************************** - * - * Invert isotopic stereo - * - ******************************/ - - /********************************************************************************* - * Create initial approximation for the minimization of the stereo descriptors: - * invert stereogenic atom parities, one parity in each allene, all parities in - * pCS->LinearCTStereoCarb and allene parities in pCS->nLenLinearCTStereoDble - */ - nRet = InvertStereo( at, num_at_tg, nCanonRankIsotopicStereo, nTempRank, pCS, 1 ); - if ( RETURNED_ERROR( nRet ) ) { - goto exit_function; - } else - if ( nRet > 0 ) { - /* InvertStereo() has done some changes */ - nRet = 0; - /* FillOutStereoParities() has already been called to fill out these 2 LinearCTs */ - - /* set the 1st ranks in the rest of the stack to zero: prepare for ranks reuse */ - for ( n = 2; n < nRankStackLen && pRankStack1[n]; n ++ ) { - pRankStack1[n][0] = 0; /* means ranks have to be recalculated */ - } - /* set the 1st ranks to zero: prepare for ranks reuse */ - for ( n = 2; n < nRankStackLen && pRankStack2[n]; n ++ ) { - pRankStack2[n][0] = 0; /* means ranks have to be recalculated */ - } - - /* for debugging or statistics */ - pCS->lNumBreakTies = - pCS->lNumNeighListIter= - pCS->lNumTotCT = - pCS->lNumDecreasedCT = - pCS->lNumRejectedCT = - pCS->lNumEqualCT = 0; - pCS->bKeepSymmRank = 0; - pCS->bFirstCT = 1; /* To fill out nCanonRankStereo[] in map_stero_atoms2() */ - - /************************************************************************************** - nCanonRankIsotopic contains input canonical numbering - nCanonRankIsotopicStereo will be filled with a transposition of canonical numbering - that (1) keeps connection table unchanged and - (2) provides minimal stereo descriptors in - pCS->LinearCTStereoDble (length=pCS->nLenLinearCTStereoDble) - pCS->LinearCTStereoCarb (length=pCS->nLenLinearCTStereoCarb) - */ - nRet = map_stereo_bonds4 - ( at, num_atoms, num_at_tg, num_max, 0, ftcn->PartitionCtIso.Rank, ftcn->PartitionCtIso.AtNumber, - nCanonRankIsotopicStereoInv, nSymmRank, - pRankStack1, pRankStack2, nTempRank, nNumCurrRanks, - nSymmStereo, NeighList, pCS, cur_tree, 0, - vABParityUnknown); - if ( RETURNED_ERROR( nRet ) ) { - if ( nRet == CT_TIMEOUT_ERR ) - goto exit_function; - else - goto exit_function; /* program error */ - } else { - int bFailed = 0; - - if ( !nRet ) { - bFailed = 1; /* program error */ - pCS2->nLenLinearCTIsotopicStereoDble = - pCS->nLenLinearCTIsotopicStereoDble = -abs(pCS->nLenLinearCTStereoDble); - pCS2->nLenLinearCTIsotopicStereoCarb = - pCS->nLenLinearCTIsotopicStereoCarb = -abs(pCS->nLenLinearCTStereoCarb); - nRet = CT_STEREOCOUNT_ERR; /* */ - goto exit_function; /* program error */ - } - /* save isotopic pointers & lengths for INVERTED stereo */ - - /* save isotopic lengths */ - pCS->nLenLinearCTIsotopicStereoDbleInv = - pCS2->nLenLinearCTIsotopicStereoDbleInv = pCS->nLenLinearCTStereoDble; - pCS->nLenLinearCTIsotopicStereoCarbInv = - pCS2->nLenLinearCTIsotopicStereoCarbInv = pCS->nLenLinearCTStereoCarb; - - /* restore pointers and lengths to non-inverted isotopic stereo */ - /* -- this is needed for InvertStereo() back, see below */ - pCS->LinearCTStereoDble = pCS2->LinearCTIsotopicStereoDble; - pCS->LinearCTStereoCarb = pCS2->LinearCTIsotopicStereoCarb; - pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTIsotopicStereoDble; - pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTIsotopicStereoCarb; - - /* consistency check */ - if ( pCS->nLenLinearCTIsotopicStereoDbleInv != pCS->nLenLinearCTIsotopicStereoDble || - pCS->nLenLinearCTIsotopicStereoCarbInv != pCS->nLenLinearCTIsotopicStereoCarb ) { - nRet = CT_CALC_STEREO_ERR; - goto exit_function; /* program error */ - } - /****************************** - * - * Invert stereo back - * - ****************************** - * (make sure that pointers - * pCS->LinearCTStereoCarb, - * pCS->LinearCTStereoDble - * and corresponding lengths - * have been restored) - ******************************/ - nRet = InvertStereo( at, num_at_tg, nCanonRankIsotopicStereo, nTempRank, pCS, 0 ); - if ( RETURNED_ERROR( nRet ) ) { - goto exit_function; - } - nRet = 0; - - /* save stereo canonical numbering */ - if ( pCS->nCanonOrdIsotopicStereoInv ) { - for ( i = n = 0; i < num_at_tg; i ++ ) { - if ( nCanonRankIsotopicStereoInv[i] && (int)nCanonRankIsotopicStereoInv[i] <= num_at_tg ) { - pCS->nCanonOrdIsotopicStereoInv[ (int)nCanonRankIsotopicStereoInv[i] - 1 ] = (AT_NUMB)i; - } else { - bFailed ++; - } - } - pCS->nLenCanonOrdIsotopicStereo = bFailed? -num_atoms : num_atoms; - } - /* compare inverted and non-inverted isotopic stereo */ - pCS->bCmpIsotopicStereo = 2 + CompareLinCtStereo( - pCS->LinearCTIsotopicStereoDbleInv, pCS->nLenLinearCTIsotopicStereoDbleInv, - pCS->LinearCTIsotopicStereoCarbInv, pCS->nLenLinearCTIsotopicStereoCarbInv, - pCS->LinearCTIsotopicStereoDble, pCS->nLenLinearCTIsotopicStereoDble, - pCS->LinearCTIsotopicStereoCarb, pCS->nLenLinearCTIsotopicStereoCarb - ); - - } - } else - if ( 0 == nRet ) { - /* nothing has been done, restore pointers and lengths for stereo */ - pCS->LinearCTStereoDble = pCS2->LinearCTIsotopicStereoDble; - pCS->LinearCTStereoCarb = pCS2->LinearCTIsotopicStereoCarb; - pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTIsotopicStereoDble; - pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTIsotopicStereoCarb; - } - -bypass_isotopic_stereo:; /* ??? */ - - pCS->LinearCTIsotopic = pCS2->LinearCTIsotopic; - } - - - -exit_function: - - if ( bSwitchedAtomToIsotopic ) { - SwitchAtomStereoAndIsotopicStereo( at, num_atoms, &bSwitchedAtomToIsotopic ); - SetCtToNonIsotopicStereo( pCS, pCS2 ); /* ??? */ - } - - /* restore non-isotopic connection table */ - if ( pCS->LinearCT2 ) { - inchi_swap( (char*)&pCS->LinearCT, (char*)&pCS->LinearCT2, sizeof(pCS->LinearCT) ); - inchi_swap( (char*)&pCS->nLenLinearCT, (char*)&pCS->nLenLinearCT2, sizeof(pCS->nLenLinearCT) ); - inchi_swap( (char*)&pCS->nLenLinearCTAtOnly, (char*)&pCS->nLenLinearCTAtOnly2, sizeof(pCS->nLenLinearCTAtOnly) ); - } - - /* free memory */ - i = 2; - if ( pRankStack1 ) { - pRankStack1[0] = - pRankStack1[1] = NULL; /* deallocated separately */ - for ( ; i < nRankStackLen && pRankStack1[i]; i ++ ) - ; - } - if ( pRankStack1 && pRankStack2 ) { - for ( n = 2; n < nRankStackLen && pRankStack2[n]; n ++ ) { - if ( i < nRankStackLen - 1 ) { - pRankStack1[i++] = pRankStack2[n]; - } else { - inchi_free( pRankStack2[n] ); - } - } - inchi_free( pRankStack2 ); - } - - pCS->NeighList = NULL; /* keep the pointer in pBCN->ftcn[bTaut].NeighList for further deallocation */ - qfree ( nAtomNumber ); - qfree ( nTempRank ); - qfree ( nRank ); - qfree ( nSymmRank ); - - qfree( nSymmStereo ); - CurTreeFree( cur_tree ); -/* memory leak fix */ -/* - qfree ( nCurrRankIsotopicStereo ); - qfree ( nAtomNumberCurrIsotopicStereo); -*/ - qfree ( nCanonRankIsotopicStereo ); - qfree ( nCanonRankIsotopicStereoInv ); - - qfree( nCanonRankStereo ); - qfree( nCanonRankStereoInv ); - - InchiTimeGet( &ulEndTime ); - pCS->lTotalTime = InchiTimeMsecDiff(&ulEndTime, &ulStartTime); - return (nRet >= -1)? num_atoms : nRet; /* cannot easily get number of ranks for now */ - -} - -/**************************************************************************************/ -int Canon_INChI(int num_atoms, int num_at_tg, sp_ATOM* at, - CANON_STAT* pCS, INCHI_MODE nMode, int bTautFtcn) -{ - if ( pCS->pBCN && !pCS->NeighList ) { - /* new version */ - return Canon_INChI3( num_atoms, num_at_tg, at, pCS, nMode, bTautFtcn); - } - return CT_CANON_ERR; -} diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichicano.h b/INCHI-1-SRC/INCHI_API/inchi_dll/ichicano.h deleted file mode 100644 index 3c498ee..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichicano.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHICANO_H__ -#define __INCHICANO_H__ - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - -int GetCanonLengths(int num_at, sp_ATOM* at, ATOM_SIZES *s, T_GROUP_INFO *t_group_info ); - -int AllocateCS(CANON_STAT *pCS, int num_at, int num_at_tg, int nLenCT, int nLenCTAtOnly, - int nLenLinearCTStereoDble, int nLenLinearCTIsotopicStereoDble, - int nLenLinearCTStereoCarb, int nLenLinearCTIsotopicStereoCarb, - int nLenLinearCTTautomer, int nLenLinearCTIsotopicTautomer, - int nLenIsotopic, INCHI_MODE nMode, BCN *pBCN ); - -int DeAllocateCS(CANON_STAT *pCS ); - -void DeAllocBCN(BCN *pBCN ); - -int Canon_INChI(int num_atoms, int num_at_tg, sp_ATOM* at, - CANON_STAT* pCS, INCHI_MODE nMode, int bTautFtcn); -int GetBaseCanonRanking(int num_atoms, int num_at_tg, sp_ATOM* at[], - T_GROUP_INFO *t_group_info, ATOM_SIZES s[], BCN *pBCN, - struct tagInchiTime *ulTimeOutTime, int bFixIsoFixedH ); -int bCanonIsFinerThanEquitablePartition( int num_atoms, sp_ATOM* at, AT_RANK *nSymmRank ); -int UpdateFullLinearCT(int num_atoms, int num_at_tg, sp_ATOM* at, AT_RANK *nRank, AT_RANK *nAtomNumber, - CANON_STAT* pCS, int bFirstTime ); - -int FixCanonEquivalenceInfo(int num_at_tg, AT_RANK *nSymmRank, AT_RANK *nCurrRank, - AT_RANK *nTempRank, AT_NUMB *nAtomNumber, int *bChanged); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /* __INCHICANO_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichicans.c b/INCHI-1-SRC/INCHI_API/inchi_dll/ichicans.c deleted file mode 100644 index 9648484..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichicans.c +++ /dev/null @@ -1,1662 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - - -#include "mode.h" - -#include "incomdef.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichicant.h" -#include "ichicomn.h" -#include "ichister.h" - -#include "ichicomp.h" - - - - -typedef struct tagStereoBondNeighbor { /* *n = sort key */ - AT_RANK nRank; /* *1 opposite atom rank; equal ranks mean constit. equivalence */ - AT_RANK nNeighRank1; /* rank of the neighbor in the direction to the opposite atom */ - AT_RANK nNeighRank2; /* rank of the opposite atom neighbor in the direction to the current atom */ - AT_RANK num; /* number of same type bonds to constitutionally equivalent neighbors */ - AT_RANK num_any_parity; /* at least one atom has parity in 1..4 range */ - AT_RANK num_defined_parity; /* number of neighbors with defined parity <= num */ - /* AT_RANK num_undef_parity; */ - /* AT_RANK num_unkn_parity; */ - AT_RANK what2do; - U_CHAR cumulene_len; /* high nimble bits: (cumulene length - 1) */ - U_CHAR bond_type; /* *2 all same, not a real bond type */ -} STEREO_BOND_NEIGH; - -/* local prototypes */ -int SetHalfStereoBondIllDefPariy( sp_ATOM *at, int jn, /* atom number*/ int k1 /* stereo bond number*/, int new_parity ); -int RemoveHalfStereoBond( sp_ATOM *at, int jn, /* atom number*/ int k1 /* stereo bond number*/ ); -int SetKnownStereoBondParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, - const AT_RANK *nRank, const AT_RANK *nAtomNumber ); -int MarkKnownEqualStereoBondParities( sp_ATOM *at, int num_atoms, - const AT_RANK *nRank, const AT_RANK *nAtomNumber ); -int GetNextNeighborAndRank( sp_ATOM *at, AT_RANK cur, AT_RANK prev, AT_RANK *n, AT_RANK *cr, const AT_RANK *nCanonRank ); -int GetAndCheckNextNeighbors( sp_ATOM *at, AT_RANK cur1, AT_RANK prev1, AT_RANK cur2, AT_RANK prev2, - AT_RANK *n1, AT_RANK *n2, AT_RANK *nVisited1, AT_RANK *nVisited2, - const AT_RANK *nRank, const AT_RANK *nCanonRank ); -AT_RANK PathsHaveIdenticalKnownParities( sp_ATOM *at, AT_RANK prev1, AT_RANK cur1, AT_RANK prev2, AT_RANK cur2, - AT_RANK *nVisited1, AT_RANK *nVisited2, - const AT_RANK *nRank, const AT_RANK *nCanonRank, AT_RANK nLength ); -int RemoveKnownNonStereoBondParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, - const AT_RANK *nRank, CANON_STAT *pCS); -int SetKnownStereoCenterParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, - const AT_RANK *nRank, const AT_RANK *nAtomNumber ); -int RemoveKnownNonStereoCenterParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, - const AT_RANK *nRank, CANON_STAT *pCS); -int MarkKnownEqualStereoCenterParities( sp_ATOM *at, int num_atoms, - const AT_RANK *nRank, const AT_RANK *nAtomNumber ); - -/**********************************************************************************/ -/* Depth First Search for an atom with parity */ -int find_atoms_with_parity( sp_ATOM *at, S_CHAR *visited, int from_atom, int cur_atom ) -{ - int i, next_atom; - if ( visited[cur_atom] ) - return 0; - if ( at[cur_atom].parity ) - return 1; - visited[cur_atom] = 1; - for ( i = 0; i < at[cur_atom].valence; i ++ ) { - next_atom = at[cur_atom].neighbor[i]; - if ( next_atom != from_atom && find_atoms_with_parity( at, visited, cur_atom, next_atom ) ) - return 1; - } - return 0; -} -/**********************************************************************************/ -int SetHalfStereoBondIllDefPariy( sp_ATOM *at, int jn, /* atom number*/ int k1 /* stereo bond number*/, int new_parity ) -{ - int parity; - if ( k1 < MAX_NUM_STEREO_BOND_NEIGH && at[jn].stereo_bond_neighbor[k1] ) { - parity = at[jn].stereo_bond_parity[k1] ^ PARITY_VAL(at[jn].stereo_bond_parity[k1]); - at[jn].stereo_bond_parity[k1] = parity | PARITY_VAL(new_parity); - at[jn].parity = PARITY_VAL(new_parity); - return 1; /* success */ - } - return 0; /* failed */ -} - -/**********************************************************************************/ -int RemoveHalfStereoBond( sp_ATOM *at, int jn, /* atom number*/ int k1 /* stereo bond number*/ ) -{ - int k2; - if ( k1 < MAX_NUM_STEREO_BOND_NEIGH && at[jn].stereo_bond_neighbor[k1] ) { - for ( k2 = k1; k2+1 < MAX_NUM_STEREO_BOND_NEIGH; k2++ ) { - at[jn].stereo_bond_neighbor[k2] = at[jn].stereo_bond_neighbor[k2+1]; - at[jn].stereo_bond_ord[k2] = at[jn].stereo_bond_ord[k2+1]; - at[jn].stereo_bond_z_prod[k2] = at[jn].stereo_bond_z_prod[k2+1]; - at[jn].stereo_bond_parity[k2] = at[jn].stereo_bond_parity[k2+1]; - } - at[jn].stereo_bond_neighbor[k2] = 0; - at[jn].stereo_bond_ord[k2] = 0; - at[jn].stereo_bond_z_prod[k2] = 0; - at[jn].stereo_bond_parity[k2] = 0; - - if ( !at[jn].stereo_bond_neighbor[0] ) { /* curled braces added 6-6-2002 */ - at[jn].parity = 0; - at[jn].stereo_atom_parity = 0; - at[jn].final_parity = 0; - /* at[jn].bHasStereoOrEquToStereo = 0; */ - } - return 1; /* success */ - } - return 0; /* failed */ -} - -/**********************************************************************************/ -int SetOneStereoBondIllDefParity( sp_ATOM *at, int jc, /* atom number*/ int k /* stereo bond ord. number*/, int new_parity ) -{ - int k1, ret=0, kn, jn = (int)at[jc].stereo_bond_neighbor[k]-1; - - /* opposite end */ - for ( k1 = kn = ret = 0; k1 < MAX_NUM_STEREO_BOND_NEIGH && (kn=at[jn].stereo_bond_neighbor[k1]); k1++ ) { - if ( kn - 1 == jc ) { - ret = SetHalfStereoBondIllDefPariy( at, jn, /* atom number*/ k1 /* stereo bond number*/, new_parity ); - break; - } - } - if ( ret ) { - ret = SetHalfStereoBondIllDefPariy( at, jc, k, new_parity ); - } - return ret; -} - -/**********************************************************************************/ -int RemoveOneStereoBond( sp_ATOM *at, int jc, /* atom number*/ int k /* stereo bond number*/ ) -{ - int k1, ret=0, kn, jn = (int)at[jc].stereo_bond_neighbor[k]-1; - - /* opposite end */ - for ( k1 = kn = ret = 0; k1 < MAX_NUM_STEREO_BOND_NEIGH && (kn=at[jn].stereo_bond_neighbor[k1]); k1++ ) { - if ( kn - 1 == jc ) { - ret = RemoveHalfStereoBond( at, jn, k1 ); - break; - } - } - if ( ret ) { - ret = RemoveHalfStereoBond( at, jc, k ); - } - return ret; -} -/**********************************************************************************/ -int RemoveOneStereoCenter( sp_ATOM *at, int jc /* atom number*/ ) -{ - if ( at[jc].parity ) { - at[jc].parity = 0; /* remove parity */ - at[jc].stereo_atom_parity = 0; - at[jc].final_parity = 0; - /* at[jc].bHasStereoOrEquToStereo = 0; */ - return 1; - } - return 0; /* failed: not a stereo center */ -} -/**********************************************************************************/ -/* Remove stereo parity from centers having constitutionally equivalent */ -/* cut-vertex neighbors whose attachments do not have stereogenic elements. */ -/* Currently checks ALL constitutionally equivalent neighbors. */ -/* To optimize, check only one. */ -int UnmarkNonStereo( sp_ATOM *at, int num_atoms, const AT_RANK *nRank, const AT_RANK *nAtomNumber, int bIsotopic ) -{ - int i, i1, i2, j, k, k1, k2, kn /* neigh*/, val, ic/* center*/, jc, num_implicit_H; - int num_neighbors_with_parity, num_no_parity_atoms, num_removed_parities=-1, num_removed_parities0; - AT_RANK nNeighborNumber[MAX_NUM_STEREO_ATOM_NEIGH]; - AT_RANK nPrevAtomRank, nPrevNeighRank; - S_CHAR *visited = (S_CHAR *) inchi_malloc(num_atoms * sizeof(visited[0])); - if ( !visited ) - goto exit_function; - num_removed_parities = 0; - num_no_parity_atoms = 0; - - do { - num_removed_parities0 = num_removed_parities; - for ( i = i1 = 0, nPrevAtomRank = 0; i <= num_atoms; i++ ) { - /* bounds violation check (i!=num_atoms) added 6-21-2002 */ - if ( i == num_atoms || nPrevAtomRank != nRank[j = nAtomNumber[i]] - /* at[j].parity && 1 < at[j].valence && at[j].valence < MAX_NUM_STEREO_ATOM_NEIGH*/ ) { - - /* end of constitutionally equivalent atoms sequence */ - - /* nPrevRank = nRank[j]; */ - i2 = i; - if ( i2 - i1 > num_no_parity_atoms /*&& at[jc = nAtomNumber[i1]].parity*/ ) { - /* at[nAtomNumber[i1]]..at[nAtomNumber[i2-1]] are constitutionally equivalent and some of them have parity */ - jc = nAtomNumber[i1]; - num_no_parity_atoms = 0; - val = at[jc].valence; /* all equivalent atoms have equal valences, etc. (except parities) */ - num_implicit_H = at[jc].endpoint? 0 : at[jc].num_H; - /* Only atoms with valence <= MAX_NUM_STEREO_ATOM_NEIGH may have parity. However, check: */ - if ( val + num_implicit_H > MAX_NUM_STEREO_ATOM_NEIGH ) { - continue; /* program error ??? */ /* */ - } - for ( k = 0; k < val; k ++ ) { - nNeighborNumber[k] = k; /* initialize an array of indexes for sorting */ - } - /* check parities */ - for ( ic = i1; ic < i2; ic ++ ) { - jc = nAtomNumber[ic]; - /* sort neighbors according to their canon. equivalence ranks */ - pNeighborsForSort = at[jc].neighbor; - pn_RankForSort = nRank; - insertions_sort( nNeighborNumber, val, sizeof(nNeighborNumber[0]), CompNeighborsAT_NUMBER ); - num_neighbors_with_parity = -1; /* non-zero */ - for ( k = k1 = 0, nPrevNeighRank = 0; k <= val; k ++ ) { - if ( k == val || nPrevNeighRank != nRank[at[jc].neighbor[nNeighborNumber[k]]] ) { - k2 = k; - if ( k2 - k1 > 1 ) { - /* found 2 or more constitutionally equivalent neighbors */ - /* Check if they have only non-stereogenic neighbors */ - for ( kn = k1, num_neighbors_with_parity = 0; kn < k2; kn ++ ) { - memset( visited, 0, num_atoms * sizeof(visited[0])); - visited[jc] = 1; /* starting point; the only atom with parity */ - num_neighbors_with_parity += - find_atoms_with_parity( at, visited, jc, (int)at[jc].neighbor[nNeighborNumber[kn]] ); - } - } - if ( !num_neighbors_with_parity ) { - break; /* at[jc] cannot have defined parity */ - } - if ( k + 1 < val ) { - k1 = k; /* at least 2 more neighbors left */ - nPrevNeighRank = nRank[at[jc].neighbor[nNeighborNumber[k]]]; - } else { - break; - } - } - } - if ( num_implicit_H > 1 ) { - if ( bIsotopic && (at[jc].num_iso_H[0] > 1 || - at[jc].num_iso_H[1] > 1 || - at[jc].num_iso_H[2] > 1 ) || - num_implicit_H > NUM_H_ISOTOPES || - !bIsotopic - ) { - num_neighbors_with_parity = 0; - } - } - /* increment if: */ - /* (a) constitutionally equivalent neighbors do exist, and */ - /* (b) all constitutionally equivalent neighbors do not have parity, and */ - /* (c) all constitutionally equivalent neighbors are not connected to atoms with parity */ - num_no_parity_atoms += !num_neighbors_with_parity; - } - if ( num_no_parity_atoms == i2 - i1 ) { - /* all atoms at[nAtomNumber[i1]]..at[nAtomNumber[i2-1]] cannot be */ - /* stereo centers or belong to stereo bonds */ - for ( ic = i1; ic < i2; ic ++ ) { - int jn; - jc = nAtomNumber[ic]; - at[jc].parity = 0; /* remove parity */ - at[jc].stereo_atom_parity = 0; - at[jc].final_parity = 0; - at[jc].bHasStereoOrEquToStereo = 0; - /* remove stereo bonds */ - for ( k = 0; k < MAX_NUM_STEREO_BOND_NEIGH && (jn=at[jc].stereo_bond_neighbor[k]); k++ ) { - jn--; /* stereo bond neighbor */ - /* opposite end */ - for ( k1 = 0; k1 < MAX_NUM_STEREO_BOND_NEIGH && (kn=at[jn].stereo_bond_neighbor[k1]); k1++ ) { - if ( kn - 1 == jc ) { - RemoveHalfStereoBond( at, jn, k1 ); - break; - } - } - /* at at[jc] stereo bond end; since references to all at[jc] */ - /* stereo bond neighbors are to be removed, do not shift them */ - at[jc].stereo_bond_neighbor[k] = 0; - at[jc].stereo_bond_ord[k] = 0; - at[jc].stereo_bond_z_prod[k] = 0; - at[jc].stereo_bond_parity[k] = 0; - - } - - } - num_removed_parities += num_no_parity_atoms; - } - - } - if ( i < num_atoms ) { - nPrevAtomRank = nRank[j]; - i1 = i; - } - num_no_parity_atoms = 0; - } - num_no_parity_atoms += (i < num_atoms && !at[j].parity); - } - } while ( num_removed_parities != num_removed_parities0 ); - -exit_function: - if ( visited ) - inchi_free( visited ); - return num_removed_parities; -} -/********************************************************************************** - * - * Add stereo descriptor(s) for atom #i - * - **********************************************************************************/ -int FillSingleStereoDescriptors(sp_ATOM *at, int i, int num_trans, const AT_RANK *nRank - , AT_STEREO_CARB *LinearCTStereoCarb, int *nStereoCarbLen, int nMaxStereoCarbLen - , AT_STEREO_DBLE *LinearCTStereoDble, int *nStereoDbleLen, int nMaxStereoDbleLen - , int bAllene ) -{ - if ( !LinearCTStereoDble && !LinearCTStereoCarb ) { - return 0; /* return immediately if no stereo have been requested */ - } - - /*************************************************** - add stereo centers and stereo bonds to the CT - ***************************************************/ - if ( at[i].parity || at[i].stereo_bond_neighbor[0] ) { - AT_RANK r_neigh, rank = nRank[i]; - AT_NUMB nNeighborNumber2[MAXVAL]; - unsigned parity; - int k; - int num_allene = 0; - if ( ATOM_PARITY_WELL_DEF(at[i].parity) && num_trans < 0 ) { - /* number of neighbors transpositions to the sorted order is unknown. Find it. */ - /* If parity is not well-defined then doing this is a waste of time */ - int num_neigh = at[i].valence; - for ( k = 0; k < num_neigh; k ++) { - nNeighborNumber2[k] = k; - } - pNeighborsForSort = at[i].neighbor; - pn_RankForSort = nRank; - num_trans=insertions_sort( nNeighborNumber2, num_neigh, sizeof(nNeighborNumber2[0]), CompNeighborsAT_NUMBER ); -#ifndef CT_NEIGH_INCREASE - num_trans += ((num_neigh*(num_neigh-1))/2)%2; /* get correct parity for ascending order */ -#endif - } - - /* stereo bonds */ - if ( LinearCTStereoDble && at[i].stereo_bond_neighbor[0] ) { - - /* HalfStereoBondParity( sp_ATOM *at, int at_no1, int i_sb_neigh, AT_RANK *nRank ) */ - AT_NUMB nStereoNeighNumber[MAX_NUM_STEREO_BONDS], nStereoNeigh[MAX_NUM_STEREO_BONDS], n; - int num_stereo, stereo_neigh, stereo_neigh_ord, stereo_bond_parity; - for ( num_stereo = 0; - num_stereo < MAX_NUM_STEREO_BONDS && - (n=at[i].stereo_bond_neighbor[num_stereo]); num_stereo ++ ) { - nStereoNeighNumber[num_stereo] = num_stereo; - nStereoNeigh[num_stereo] = n-1; - num_allene += IS_ALLENE_CHAIN(at[i].stereo_bond_parity[num_stereo]); - } - if ( bAllene > 0 && !num_allene || bAllene == 0 && num_allene ) { - return 0; - } - /* sort stereo bonds according to the ranks of the neighbors */ - pNeighborsForSort = nStereoNeigh; - pn_RankForSort = nRank; - insertions_sort( nStereoNeighNumber, num_stereo, sizeof(nStereoNeighNumber[0]), CompNeighborsAT_NUMBER ); - - /* process stereo bonds one by one */ - for ( k = 0; k < num_stereo; k ++ ) { - stereo_neigh = nStereoNeigh[stereo_neigh_ord=(int)nStereoNeighNumber[k]]; - if ( (r_neigh = (AT_NUMB)nRank[stereo_neigh]) CT_NEIGH_SMALLER_THAN rank ) { - /* accept only neighbors that have smaller ranks */ - stereo_bond_parity = PARITY_VAL(at[i].stereo_bond_parity[stereo_neigh_ord]); - if ( stereo_bond_parity == AB_PARITY_NONE ) - continue; - - /* stereo_neigh = at[i].stereo_bond_neighbor[nStereoNeighNumber[k]]-1; */ - if ( ATOM_PARITY_KNOWN(stereo_bond_parity) ) { - parity = stereo_bond_parity; - } else - if ( ATOM_PARITY_WELL_DEF(at[i].parity) && - ATOM_PARITY_WELL_DEF(at[stereo_neigh].parity) && - MIN_DOT_PROD <= abs(at[i].stereo_bond_z_prod[stereo_neigh_ord]) ) { - /* bond parity can be calculated */ - int half_parity1, half_parity2, j, nn, stereo_neigh_ord2; - stereo_neigh_ord2 = -1; - for ( j = 0; j < MAX_NUM_STEREO_BONDS && - (nn=(int)at[stereo_neigh].stereo_bond_neighbor[j]); j++ ) { - if ( i+1 == nn ) { - /* found the opposite end of the stereo bond */ - stereo_neigh_ord2 = j; - break; - } - } - if ( stereo_neigh_ord2 >= 0 ) { - half_parity1 = HalfStereoBondParity( at, i, stereo_neigh_ord, nRank ); - half_parity2 = HalfStereoBondParity( at, stereo_neigh, stereo_neigh_ord2, nRank ); - if ( ATOM_PARITY_WELL_DEF(half_parity1) && - ATOM_PARITY_WELL_DEF(half_parity2) ) { - parity = 2 - ( half_parity1 + half_parity2 - + (at[i].stereo_bond_z_prod[stereo_neigh_ord] < 0))%2; - } else { - return CT_STEREOBOND_ERROR; /* */ - } - } else { - return CT_STEREOBOND_ERROR; /* */ - } - } else { - /* parity cannot be calculated: not enough info or 'unknown' */ - if ( AB_PARITY_NONE == (parity = inchi_max(at[i].parity, at[stereo_neigh].parity)) ) - continue; - if ( ATOM_PARITY_WELL_DEF(parity) ) - parity = AB_PARITY_UNDF; /* should not happen */ - } - if ( CHECK_OVERFLOW(*nStereoDbleLen, nMaxStereoDbleLen) ) - return CT_OVERFLOW; /* */ - /* first stereo bond atom */ - LinearCTStereoDble[*nStereoDbleLen].at_num1 = rank; - /* second stereo bond atom (opposite end) */ - LinearCTStereoDble[*nStereoDbleLen].at_num2 = r_neigh; - /* bond parity */ - LinearCTStereoDble[*nStereoDbleLen].parity = parity; - (*nStereoDbleLen) ++; - } - } - } - - - /* stereo carbon */ - if ( bAllene > 0 ) { - return 0; - } - if ( LinearCTStereoCarb && !at[i].stereo_bond_neighbor[0] ) { - if ( CHECK_OVERFLOW(*nStereoCarbLen, nMaxStereoCarbLen) ) - return CT_OVERFLOW; /* */ - /* stereo atom rank */ - LinearCTStereoCarb[*nStereoCarbLen].at_num = rank; - /* stereo atom parity */ - parity = ATOM_PARITY_WELL_DEF(at[i].parity)? (2 - (at[i].parity + num_trans)%2) : at[i].parity; - LinearCTStereoCarb[*nStereoCarbLen].parity = parity; - (*nStereoCarbLen) ++; - } - - } - - return 0; -} -/**********************************************************************************/ -void SwitchAtomStereoAndIsotopicStereo( sp_ATOM *at, int num_atoms, int *bSwitched ) -{ - int i; - /* switch atom stereo data */ - for ( i = 0; i < num_atoms; i ++ ) { - inchi_swap( (char*)&at[i].parity, (char*)&at[i].parity2, sizeof( at[i].parity ) ); - inchi_swap( (char*)&at[i].final_parity, (char*)&at[i].final_parity2, sizeof( at[i].final_parity ) ); - inchi_swap( (char*)&at[i].stereo_atom_parity, (char*)&at[i].stereo_atom_parity2, sizeof( at[i].stereo_atom_parity ) ); - inchi_swap( (char*)&at[i].bHasStereoOrEquToStereo, (char*)&at[i].bHasStereoOrEquToStereo2, sizeof( at[i].bHasStereoOrEquToStereo ) ); - - inchi_swap( (char*)at[i].stereo_bond_neighbor, (char*)at[i].stereo_bond_neighbor2, sizeof( at[i].stereo_bond_neighbor ) ); - inchi_swap( (char*)at[i].stereo_bond_ord, (char*)at[i].stereo_bond_ord2, sizeof( at[i].stereo_bond_ord ) ); - inchi_swap( (char*)at[i].stereo_bond_z_prod, (char*)at[i].stereo_bond_z_prod2, sizeof( at[i].stereo_bond_z_prod ) ); - inchi_swap( (char*)at[i].stereo_bond_parity, (char*)at[i].stereo_bond_parity2, sizeof( at[i].stereo_bond_parity ) ); - } - *bSwitched = !*bSwitched; -} -/**********************************************************************************/ -void SetCtToIsotopicStereo( CANON_STAT *pCS, CANON_STAT *pCS2 ) -{ - pCS->LinearCTStereoDble = pCS2->LinearCTIsotopicStereoDble; /* enable stereo */ - pCS->LinearCTStereoCarb = pCS2->LinearCTIsotopicStereoCarb; - - pCS->LinearCTStereoDbleInv = pCS2->LinearCTIsotopicStereoDbleInv; /* enable inv. stereo */ - pCS->LinearCTStereoCarbInv = pCS2->LinearCTIsotopicStereoCarbInv; - pCS->nMaxLenLinearCTStereoDble = pCS2->nMaxLenLinearCTIsotopicStereoDble; - pCS->nMaxLenLinearCTStereoCarb = pCS2->nMaxLenLinearCTIsotopicStereoCarb; - - pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTIsotopicStereoDble; - pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTIsotopicStereoCarb; -} -/**********************************************************************************/ -void SetCtToNonIsotopicStereo( CANON_STAT *pCS, CANON_STAT *pCS2 ) -{ - pCS->LinearCTStereoDble = pCS2->LinearCTStereoDble; /* enable stereo */ - pCS->LinearCTStereoCarb = pCS2->LinearCTStereoCarb; - - pCS->LinearCTStereoDbleInv = pCS2->LinearCTStereoDbleInv; /* enable inv. stereo */ - pCS->LinearCTStereoCarbInv = pCS2->LinearCTStereoCarbInv; - pCS->nMaxLenLinearCTStereoDble = pCS2->nMaxLenLinearCTStereoDble; - pCS->nMaxLenLinearCTStereoCarb = pCS2->nMaxLenLinearCTStereoCarb; - - pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTStereoDble; - pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTStereoCarb; - - pCS->nLenLinearCTIsotopicStereoDble = pCS2->nLenLinearCTIsotopicStereoDble; - pCS->nLenLinearCTIsotopicStereoCarb = pCS2->nLenLinearCTIsotopicStereoCarb; -} - -/**********************************************************************************/ -int FillAllStereoDescriptors( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, const AT_RANK *nAtomNumberCanon, CANON_STAT *pCS ) -{ - int ret=0, i; - /* initialize zero lengths */ - pCS->nLenLinearCTStereoCarb = 0; - pCS->nLenLinearCTStereoDble = 0; - - /* fill atom by atom */ - for ( i = 0; !ret && i < num_atoms; i ++ ) { - ret = FillSingleStereoDescriptors( at, (int)nAtomNumberCanon[i], -1, nCanonRank - , pCS->LinearCTStereoCarb, &pCS->nLenLinearCTStereoCarb, pCS->nMaxLenLinearCTStereoCarb - , pCS->LinearCTStereoDble, &pCS->nLenLinearCTStereoDble, pCS->nMaxLenLinearCTStereoDble - , 0 /* bAllene */ ); - } - for ( i = 0; !ret && i < num_atoms; i ++ ) { - ret = FillSingleStereoDescriptors( at, (int)nAtomNumberCanon[i], -1, nCanonRank - , pCS->LinearCTStereoCarb, &pCS->nLenLinearCTStereoCarb, pCS->nMaxLenLinearCTStereoCarb - , pCS->LinearCTStereoDble, &pCS->nLenLinearCTStereoDble, pCS->nMaxLenLinearCTStereoDble - , 1 /* bAllene */); - } - - return ret; -} -/**********************************************************************************/ -/* Find stereo bond parities known in advance */ -int SetKnownStereoBondParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, - const AT_RANK *nRank, const AT_RANK *nAtomNumber ) -{ - int i, j, n, m, j1, k, num_neigh1, num_neigh2, iMax1, parity; - int trans_i1, trans_i2, trans_k1, trans_k2, prev_trans, trans_k, num_set; - int i1, i2, k1, k2, n1, n2, m1, m2, /*stereo_bond_parity,*/ cumulene_len; - AT_RANK nAtomRank1, nAtomRank2, nAtom1NeighRank; - AT_RANK nNeighRank1[MAX_NUM_STEREO_BONDS], nNeighRank2[MAX_NUM_STEREO_BONDS]; - AT_RANK nNeighCanonRank1[MAX_NUM_STEREO_BONDS], nNeighCanonRank2[MAX_NUM_STEREO_BONDS]; - for ( i1 = 0, num_set = 0; i1 < num_atoms; i1 ++ ) { - if ( !at[i1].parity || !at[i1].stereo_bond_neighbor[0] ) { - continue; - } - - if ( !PARITY_WELL_DEF(at[i1].parity) ) { - continue; - } - nAtomRank1 = nRank[i1]; - iMax1 = (int)nAtomRank1-1; - num_neigh1 = at[i1].valence; - for ( n1 = 0; n1 < MAX_NUM_STEREO_BONDS && (i2=(int)at[i1].stereo_bond_neighbor[n1]); n1++ ) { - i2 --; - /* found a stereo bond at[i1]-at[i2] adjacent to at[i1] */ - for ( n2 = 0, m=0; n2 < MAX_NUM_STEREO_BONDS && (m=(int)at[i2].stereo_bond_neighbor[n2]) && m-1 != i1; n2++ ) - ; /* locate stereo bond (#n2) at the opposite atom at[i2] */ - if ( m-1 != i1 || at[i1].stereo_bond_parity[n1] != at[i2].stereo_bond_parity[n2] ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - if ( i1 < i2 ) { - continue; /* do not process same bond 2 times */ - } - - if ( PARITY_KNOWN(at[i1].stereo_bond_parity[n1]) || !PARITY_VAL(at[i1].stereo_bond_parity[n1]) ) { - continue; - } - if ( !PARITY_WELL_DEF(at[i1].parity) || !PARITY_WELL_DEF(at[i2].parity) ) { - continue; - } - if ( PARITY_VAL(at[i1].stereo_bond_parity[n1]) != AB_PARITY_CALC ) { - continue; /* ?? program error ?? should not happen */ /* */ - } - /*stereo_bond_parity = PARITY_VAL(at[i1].stereo_bond_parity[n1]);*/ - cumulene_len = BOND_CHAIN_LEN(at[i1].stereo_bond_parity[n1]); - nAtomRank2 = nRank[i2]; - nAtom1NeighRank = nRank[(int)at[i1].neighbor[(int)at[i1].stereo_bond_ord[n1]]]; - num_neigh2 = at[i2].valence; - /* store ranks of at[i1] stereo bond neighbors except one connected by a stereo bond */ - k = (int)at[i1].stereo_bond_ord[n1]; - trans_i1 = 0; - for ( i = j = 0; i < num_neigh1; i ++ ) { - if ( i != k ) { - nNeighRank1[j] = nRank[(int)at[i1].neighbor[i]]; - j ++; - } - } - if ( j == 2 ) { - if ( nNeighRank1[0] == nNeighRank1[1] ) { - /* neighbors are constitutionally identical, can't find bond parity */ - continue; - } - trans_i1 = insertions_sort(nNeighRank1, j, sizeof(nNeighRank1[0]), comp_AT_RANK); - } - /* store ranks of at[i2] stereo bond neighbors except one connected by a stereo bond */ - k = (int)at[i2].stereo_bond_ord[n2]; - trans_i2 = 0; - for ( i = j = 0; i < num_neigh2; i ++ ) { - if ( i != k ) { - nNeighRank2[j] = nRank[(int)at[i2].neighbor[i]]; - j ++; - } - } - if ( j == 2 ) { - if ( nNeighRank2[0] == nNeighRank2[1] ) { - /* neighbors are constitutionally identical, can't find bond parity */ - continue; - } - trans_i2 = insertions_sort(nNeighRank2, j, sizeof(nNeighRank2[0]), comp_AT_RANK); - } - prev_trans = -1; - trans_k1 = -2; - trans_k = -4; /* 2004-04-28 */ - /* find all pairs of atoms that can be mapped on at[i1], at[i2] pair */ - for ( j1 = 0; j1 <= iMax1 && nAtomRank1==nRank[k1=(int)nAtomNumber[iMax1-j1]]; j1 ++ ) { - /* at[k1] is constitutionally equivalent to at[i1] */ - /* find all at[k1] neighbors that have rank nAtomRank2; */ - /* then find at[k2] constitutionally equivalent at at[i2] */ - if ( at[k1].valence != num_neigh1 ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - for ( m1 = 0; m1 < num_neigh1; m1 ++ ) { - int prev, next, len; - if ( nAtom1NeighRank != nRank[k2=(int)at[k1].neighbor[m1]] ) { - continue; - } - m2 = -1; /* undefined yet */ - prev = k1; - len = 0; - if ( cumulene_len ) { - for ( len=0, next = (int)at[k1].neighbor[m1]; len < cumulene_len; len ++ ) { - if ( at[next].valence == 2 && !at[next].num_H ) { - j = ((int)at[next].neighbor[0] == prev); - prev = next; - next = at[next].neighbor[j]; - } else { - break; /* cannot continue */ - } - } - if ( len != cumulene_len || nAtomRank2 != nRank[next] ) { - continue; /* not found */ - } - k2 = next; - } - if ( at[k2].valence != num_neigh2 ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - /* store canon. ranks of at[k1] neighbors */ /* use i,j,k,m,n */ - for ( n = j = 0; n < num_neigh1; n ++ ) { - if ( n != m1 ) { - i=(int)at[k1].neighbor[n]; - for ( m = 0; m < num_neigh1-1; m ++ ) { - if ( nRank[i] == nNeighRank1[m] ) { - nNeighCanonRank1[m] = nCanonRank[i]; - j ++; - break; - } - } - } - } - if ( j != num_neigh1-1 ) { - return CT_STEREOCOUNT_ERR; /* */ - } - if ( j == 2 ) { - trans_k1 = insertions_sort(nNeighCanonRank1, j, sizeof(nNeighCanonRank1[0]), comp_AT_RANK); - } else { - trans_k1 = 0; - } - /* store canon. ranks of at[k2] neighbors */ /* use i,j,k,m,n */ - for ( n = j = 0; n < num_neigh2; n ++ ) { - i=(int)at[k2].neighbor[n]; - if ( i == prev ) { /* neighbor belongs to the stereobond */ - m2 = n; - } else { - for ( m = 0; m < num_neigh2-1; m ++ ) { - if ( nRank[i] == nNeighRank2[m] ) { - nNeighCanonRank2[m] = nCanonRank[i]; - j ++; - break; - } - } - } - } - if ( j != num_neigh2-1 || m2 < 0 ) { - return CT_STEREOCOUNT_ERR; /* */ - } - if ( j == 2 ) { - trans_k2 = insertions_sort(nNeighCanonRank2, j, sizeof(nNeighCanonRank2[0]), comp_AT_RANK); - } else { - trans_k2 = 0; - } - trans_k = (trans_k1 + trans_k2)%2; - if ( prev_trans < 0 ) { - prev_trans = trans_k; - } else - if ( prev_trans != trans_k ) { /* was != trans_k1, changed 9-23-2003 */ - break; /* different number of transpositions */ - } - } /* end of the second atom mapping cycle */ - if ( prev_trans >= 0 && prev_trans != trans_k ) { /* was != trans_k1, changed 9-23-2003 */ - break; - } - } /* end of the first atom mapping cycle */ - if ( prev_trans == trans_k ) { /* was == trans_k1, changed 9-23-2003 */ - int z_prod; - /* all mappings of canonical numbers on the */ - /* stereo bond at[i1]-at[i2] produce equivalent numberings. */ - /* Therefore the stereo bond parity is known at this time. */ - /* parity_1 = at[i1].parity + (trans_i1 + trans_k1 + num_neigh1 - 1) + (int)at[i1].stereo_bond_ord[n1] */ - /* expression in parentheses is equivalent to rank[first neigh] > rank[second neigh] */ - /* same for parity_2. */ - /* parity_2 = at[i2].parity + (trans_i2 + trans_k2 + num_neigh2 - 1) + (int)at[i2].stereo_bond_ord[n2] */ - /* Sum of the two parities (without stereo_bond_z_prod) is: */ - parity = (at[i1].parity + at[i2].parity + prev_trans + trans_i1 + trans_i2 - + num_neigh1 + num_neigh2 - + (int)at[i1].stereo_bond_ord[n1] + (int)at[i2].stereo_bond_ord[n2] ) %2; - z_prod = at[i1].stereo_bond_z_prod[n1]; - if ( MIN_DOT_PROD > abs(z_prod)) { - parity = AB_PARITY_UNDF; /* undefined because of geometry */ - } else { - parity = (z_prod > 0)? 2 - parity : 1 + parity; - } - at[i1].stereo_bond_parity[n1] = ALL_BUT_PARITY(at[i1].stereo_bond_parity[n1]) | parity; - at[i2].stereo_bond_parity[n2] = ALL_BUT_PARITY(at[i2].stereo_bond_parity[n2]) | parity; - num_set ++; - } - } - } - return num_set; -} -/**********************************************************************************/ -/* Find stereo center parities known in advance */ -int MarkKnownEqualStereoBondParities( sp_ATOM *at, int num_atoms, - const AT_RANK *nRank, const AT_RANK *nAtomNumber ) -{ - int j, n, m, j1, num_neigh1, num_neigh2, iMax1; - int num_set, /*num_sb1, num_sb2,*/ bDifferentParities; - int i1, i2, k1, k2, n1, n2, m1, m2, s1, s2, stereo_bond_parity, stereo_bond_parity2, cumulene_len; - AT_RANK nAtomRank1, nAtomRank2, nAtom1NeighRank, nAtom2NeighRank; - num_set = 0; - for ( i1 = 0, num_set = 0; i1 < num_atoms; i1 ++ ) { - if ( !at[i1].parity || !at[i1].stereo_bond_neighbor[0] ) { - continue; - } - nAtomRank1 = nRank[i1]; - iMax1 = (int)nAtomRank1-1; - num_neigh1 = at[i1].valence; - /* count stereogenic bonds adjacent to at[i1] */ - for ( n1 = 0; n1 < MAX_NUM_STEREO_BONDS && at[i1].stereo_bond_neighbor[n1]; n1++ ) - ; - /*num_sb1 = n1;*/ - /* search for bonds possibly constitutionally equivalent to each of the adjacent bonds */ - /* and find if all of them have same already known parity */ - for ( n1 = 0, i2 = 0; n1 < MAX_NUM_STEREO_BONDS && (i2=(int)at[i1].stereo_bond_neighbor[n1]); n1++ ) { - i2 --; - nAtomRank2 = nRank[i2]; - if ( nAtomRank2 < nAtomRank1 || nAtomRank2 == nAtomRank1 && i1 < i2 ) { - /* An attempt to reduce unnecessary repetitions. */ - /* We still have repetitions because we do not accumulate a list of */ - /* processed (nAtomRank2, nAtomRank1) pairs. */ - continue; - } - bDifferentParities = -1; /* parities have not been compared yet */ - /* found a stereo bond at[i1]-at[i2] (adjacent to at[i1]) */ - /* - if ( !PARITY_KNOWN(at[i1].stereo_bond_parity[n1]) || (at[i1].stereo_bond_parity[n1] & KNOWN_PARITIES_EQL) ) { - continue; - } - */ - if ( at[i1].stereo_bond_parity[n1] & KNOWN_PARITIES_EQL ) { - continue; - } - /* stereo bond has known or unknown parity; we have not checked it yet */ - for ( n2 = 0; n2 < MAX_NUM_STEREO_BONDS && at[i2].stereo_bond_neighbor[n2]; n2++ ) - ; - /*num_sb2 = n2;*/ - for ( n2 = 0, m = 0; n2 < MAX_NUM_STEREO_BONDS && (m=(int)at[i2].stereo_bond_neighbor[n2]) && m-1 != i1; n2++ ) - ; - if ( m-1 != i1 || at[i1].stereo_bond_parity[n1] != at[i2].stereo_bond_parity[n2] ) { - return CT_STEREOCOUNT_ERR; /* program error: stereo bonds data in two directions are different */ /* */ - } - stereo_bond_parity = PARITY_VAL(at[i1].stereo_bond_parity[n1]); - cumulene_len = BOND_CHAIN_LEN(at[i1].stereo_bond_parity[n1]); - nAtom1NeighRank = nRank[(int)at[i1].neighbor[(int)at[i1].stereo_bond_ord[n1]]]; - nAtom2NeighRank = nRank[(int)at[i2].neighbor[(int)at[i2].stereo_bond_ord[n2]]]; - num_neigh2 = at[i2].valence; - /* find all pairs of atoms that possibly can be mapped on at[i1], at[i2] pair */ - /* (we may also find pairs that cannot be mapped, but we cannot miss any pair */ - /* that can be mapped) */ - for ( j1 = 0; j1 <= iMax1 && nAtomRank1==nRank[k1=(int)nAtomNumber[iMax1-j1]]; j1 ++ ) { - /* at[k1] is constitutionally equivalent to at[i1] */ - /* find all at[k1] stereo bond neighbors at[k2] that have rank nAtomRank2; */ - /* then find at[k2] constitutionally equivalent at at[i2] */ - if ( at[k1].valence != num_neigh1 ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - if ( !at[k1].bHasStereoOrEquToStereo ) { - at[k1].bHasStereoOrEquToStereo = 1; - } - /* -- do not check number of stereo bonds, check bonds themselves -- - for ( s1 = 0; s1 < MAX_NUM_STEREO_BONDS && at[k1].stereo_bond_neighbor[s1]; s1++ ) - ; - if ( num_sb1 != s1 ) { - bDifferentParities = 1; - } - */ - for ( m1 = 0; m1 < num_neigh1; m1 ++ ) { - /* Looking for at[k1] neighbor with nRank=nAtom1NeighRank. */ - /* This neighbor may be on the bond constit. equivalent to at[i1]-at[i2] stereo bond */ - /* (or may be constit. equivalent an adjacent to at[i1] atom in a stereogenic cumulene chain) */ - int prev, next, len; - if ( nAtom1NeighRank != nRank[k2=(int)at[k1].neighbor[m1]] ) { - continue; - } - /* found at[k1] neighbor with nRank=nAtom1NeighRank */ - m2 = -1; /* undefined yet */ - prev = k1; - len = 0; - /* if cumulene then bypass the cumulene chain */ - if ( cumulene_len ) { - for ( len=0, next = (int)at[k1].neighbor[m1]; len < cumulene_len; len ++ ) { - if ( at[next].valence == 2 && !at[next].num_H ) { - j = ((int)at[next].neighbor[0] == prev); - prev = next; - next = at[next].neighbor[j]; - } else { - break; /* cannot continue: end of cumulene chain */ - } - } - if ( len != cumulene_len || nAtomRank2 != nRank[next] ) { - continue; /* cumulene chain not found at this neighbor */ - } - if ( nAtom2NeighRank != nRank[prev] ) { - /* continue; */ /* ??? program error ??? If not, must be a very rare event */ - return CT_STEREOCOUNT_ERR; /* */ - } - k2 = next; - } - - /* a connected pair of constit. equivalent atoms found */ - if ( at[k2].valence != num_neigh2 ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - for ( n = 0; n < num_neigh2; n ++ ) { - if ( prev == (int)at[k2].neighbor[n] ) { - m2 = n; /* found bond from the opposite end of a possibly stereogenic bond */ - break; - } - } - if ( m2 < 0 ) { - return CT_STEREOCOUNT_ERR; /* program error: opposite direction bond not found */ /* */ - } - if ( !at[k2].bHasStereoOrEquToStereo ) { - at[k2].bHasStereoOrEquToStereo = 1; - } - - /* check if atoms at[k1] and at[k2] are connected by a stereo bond */ - for ( s1 = 0, m = 0; s1 < MAX_NUM_STEREO_BONDS && (m=(int)at[k1].stereo_bond_neighbor[s1]) && m-1 != k2; s1++ ) - ; - if ( m-1 != k2 ) { - bDifferentParities = 1; /* cannot find the stereo bond */ - at[k1].bHasStereoOrEquToStereo = - at[k2].bHasStereoOrEquToStereo = 2; - continue; - } - /* -- do not check number of stereo bonds, check bonds themselves -- - for ( s2 = 0; s2 < MAX_NUM_STEREO_BONDS && at[k2].stereo_bond_neighbor[s2]; s2++ ) - ; - if ( num_sb2 != s2 ) { - bDifferentParities = 1; - continue; - } - */ - for ( s2 = 0, m = 0; s2 < MAX_NUM_STEREO_BONDS && (m=(int)at[k2].stereo_bond_neighbor[s2]) && m-1 != k1; s2++ ) - ; - if ( m-1 != k1 ) { - /* - bDifferentParities = 1; // cannot find the stereo bond - continue; - */ - return CT_STEREOCOUNT_ERR; /* program error: opposite direction bond not found */ /* */ - } - if ( at[k1].stereo_bond_parity[s1] != at[k2].stereo_bond_parity[s2] ) { - bDifferentParities = 1; - continue; - } - stereo_bond_parity2 = PARITY_VAL(at[k1].stereo_bond_parity[s1]); - if ( stereo_bond_parity2 != stereo_bond_parity ) { - bDifferentParities = 1; - continue; - } - if ( stereo_bond_parity2 == stereo_bond_parity && bDifferentParities < 0 ) { - bDifferentParities = 0; - } - } - } - /* mark equal parities */ - if ( 0 == bDifferentParities && PARITY_KNOWN( stereo_bond_parity ) ) { - for ( j1 = 0; j1 <= iMax1 && nAtomRank1==nRank[k1=(int)nAtomNumber[iMax1-j1]]; j1 ++ ) { - /* at[k1] is constitutionally equivalent to at[i1] */ - for ( s1 = 0, k2 = 0; s1 < MAX_NUM_STEREO_BONDS && (k2=(int)at[k1].stereo_bond_neighbor[s1]); s1++ ) { - k2--; - if ( nRank[k2] == nAtomRank2 ) { - int b1, b2; - for ( s2 = 0, m = 0; s2 < MAX_NUM_STEREO_BONDS && (m=(int)at[k2].stereo_bond_neighbor[s2]) - && m-1 != k1; s2++ ) - ; - if ( m-1 != k1 ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - /* mark the stereo bonds */ - b1 = !(at[k1].stereo_bond_parity[s1] & KNOWN_PARITIES_EQL); - b2 = !(at[k2].stereo_bond_parity[s2] & KNOWN_PARITIES_EQL); - if ( 2 == b1 + b2 ) { - at[k1].stereo_bond_parity[s1] |= KNOWN_PARITIES_EQL; - at[k2].stereo_bond_parity[s2] |= KNOWN_PARITIES_EQL; - num_set ++; - } else - if ( b1 || b2 ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - } - } - } - } - } - } - return num_set; -} -#if ( REMOVE_KNOWN_NONSTEREO == 1 ) /* { */ -/**********************************************************************************/ -/* Return next atom number (and its canon. rank) on the path prev->cur->next */ -/* in order of ascending canonical ranks of the next atoms: *cr(output) > *cr(input) */ -/* To start the sequence let *cr=0 */ -/* If no more neighbors available the return value = 0; if successgul then the return value = 1. */ -int GetNextNeighborAndRank( sp_ATOM *at, AT_RANK cur, AT_RANK prev, AT_RANK *n, AT_RANK *cr, const AT_RANK *nCanonRank ) -{ - int i, val; - AT_RANK cr1 = MAX_ATOMS+1, j, j1=MAX_ATOMS+1, crj; - - for ( i = 0, val = at[(int)cur].valence; i < val; i ++ ) { - if ( (j=at[cur].neighbor[i]) != prev && - cr1 > (crj=nCanonRank[(int)j]) && crj > *cr ) { - cr1 = crj; - j1 = j; - } - } - if ( cr1 <= MAX_ATOMS ) { - *cr = cr1; - *n = (AT_RANK)j1; - return 1; - } - return 0; /* program error */ /* */ -} - -/**********************************************************************************/ -/* Find next pair of neighbors having the next greater canon. rank */ -/* The neighbors should be constitutionally identical and traversed simultaneouly or not traversed at all */ -/* If a bond cur1-*n1 or cur2-*n2 is a stereo bond then reject if their stereo bond parities are different or */ -/* cannot be calculated without breaking ties. */ -int GetAndCheckNextNeighbors( sp_ATOM *at, AT_RANK cur1, AT_RANK prev1, AT_RANK cur2, AT_RANK prev2, - AT_RANK *n1, AT_RANK *n2, AT_RANK *nVisited1, AT_RANK *nVisited2, - const AT_RANK *nRank, const AT_RANK *nCanonRank ) -{ - AT_RANK cr1, cr2, s1, s2; - int i1, i2, k1, k2; - cr1 = ( *n1 > MAX_ATOMS )? 0 : nCanonRank[(int)*n1]; - cr2 = ( *n2 > MAX_ATOMS )? 0 : nCanonRank[(int)*n2]; - if ( !GetNextNeighborAndRank( at, cur1, prev1, n1, &cr1, nCanonRank ) || - !GetNextNeighborAndRank( at, cur2, prev2, n2, &cr2, nCanonRank ) || - nRank[(int)*n1] != nRank[(int)*n2] || nVisited1[(int)*n1] != nVisited2[(int)*n2] ) { - return 0; /* program error; no breakpoint here */ /* */ - } - - /* Even though the bond or cumulene might have already been checked, check it: this is */ - /* the only place we can check stereo bonds and cumulenes that are not edges of the DFS tree */ - /* The code works both for a stereo bond and a stereogenic cumulene. */ - for ( i1 = 0, k1 = 0; i1 < MAX_NUM_STEREO_BONDS && - (s1=at[cur1].stereo_bond_neighbor[i1]) && - !(k1=(at[cur1].neighbor[(int)at[cur1].stereo_bond_ord[i1]] == *n1)); i1 ++ ) - ; - for ( i2 = 0, k2 = 0; i2 < MAX_NUM_STEREO_BONDS && - (s2=at[cur2].stereo_bond_neighbor[i2]) && - !(k2=(at[cur2].neighbor[(int)at[cur2].stereo_bond_ord[i2]] == *n2)); i2 ++ ) - ; - if ( k1 != k2 ) { - return 0; /* possibly not an error: constit. equivalent atoms on a stereo bond and not on a stereo bond */ - } - if ( k1 /* yes, it is a stero bond */ && - ( at[cur1].stereo_bond_parity[i1] != at[cur2].stereo_bond_parity[i2] || - /* PARITY_KNOWN (at[cur1].stereo_bond_parity[i1] ) */ /* replaced 08-13-2002 with the next: */ - !PARITY_WELL_DEF (at[cur1].stereo_bond_parity[i1] ) /* it suffices to check only one parity */ - ) ) { - return 0; /* different or (currently) unknown stereo bond parities */ - } - return 1; /* stereo bonds have known parities */ -} - -/**********************************************************************************/ -/* Simultaneously DFS-traverse 2 paths starting at the bonds prev1->cur1 and prev2->cur2 */ -/* The two paths MUST go through the pairs of constitutionally identical atoms, each atom being on one path. */ -/* Reject if encountered atoms having currently unknown (without breaking ties) */ -/* parities or having different known or unknown or undefined parities. */ -/* Save length of the path into nVisited[cur. atom number]. */ -/* Only one nVisited[] array is sufficient because the paths from the beginning are in different ring systems. */ -AT_RANK PathsHaveIdenticalKnownParities( sp_ATOM *at, AT_RANK prev1, AT_RANK cur1, AT_RANK prev2, AT_RANK cur2, - AT_RANK *nVisited1, AT_RANK *nVisited2, - const AT_RANK *nRank, const AT_RANK *nCanonRank, AT_RANK nLength ) -{ - int k; - AT_RANK n1, n2; - - nLength ++; /* number of successfully traversed pairs of atoms */ - nVisited1[cur1] = nLength; - nVisited2[cur2] = nLength; - /* the atoms must be either both stereogenic and have well-defined parities or non-stereogenic at all. */ - if ( at[cur1].stereo_atom_parity != at[cur2].stereo_atom_parity || - at[cur1].stereo_atom_parity && !PARITY_WELL_DEF (at[cur1].stereo_atom_parity) - ) { - return 0; /* Reject: Different or unknown in advance parities */ - } - - if ( at[cur1].valence != at[cur2].valence ) { - return 0; /* program error */ /* */ - } - if ( at[cur1].valence == 1 ) { - return nLength; /* so far success */ - } - - - for ( k = 1, n1 = MAX_ATOMS+1, n2=MAX_ATOMS+1; k < at[cur1].valence; k ++ ) { - /* start from 1: since we do not go back, we have only (at[cur1].valence-1) bonds to try */ - if ( !GetAndCheckNextNeighbors( at, cur1, prev1, cur2, prev2, - &n1, &n2, nVisited1, nVisited2, nRank, nCanonRank ) ) { - return 0; /* different neighbors */ - } - /* In a DFS we do not traverse already visited atoms */ - if ( !nVisited1[n1] ) { /* recursion */ - if ( ! (nLength = PathsHaveIdenticalKnownParities( at, cur1, n1, cur2, n2, nVisited1, nVisited2, nRank, nCanonRank, nLength ) ) ) { - return 0; - } - } - } - /* To be on a safe side, recheck after all nVisited[] have been set */ - for ( k = 1, n1 = MAX_ATOMS+1, n2=MAX_ATOMS+1; k < at[cur1].valence; k ++ ) { - /* start from 1: since we do not go back, we have only (at[cur1].valence-1) bonds to try */ - if ( !GetAndCheckNextNeighbors( at, cur1, prev1, cur2, prev2, - &n1, &n2, nVisited1, nVisited2, nRank, nCanonRank ) ) { - return 0; /* different neighbors */ - } - } - - - return nLength; - -} - -/**********************************************************************************/ -/* Remove stereo marks from the bonds that are known to be non-stereo */ -/* (compare neighbors if they are attached by cut-edges) */ -int RemoveKnownNonStereoBondParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, - const AT_RANK *nRank, CANON_STAT *pCS) -{ - int j, n, m, ret; - - int i1, n1, s2; - AT_RANK nAtomRank1, nAtomRank2, neigh[3], opposite_atom, *nVisited = NULL; - ret = 0; - for ( i1 = 0; i1 < num_atoms; i1 ++ ) { - if ( at[i1].valence != 3 || !at[i1].stereo_bond_neighbor[0] ) { - continue; - } - for ( n1 = 0; n1 < MAX_NUM_STEREO_BONDS && (s2=at[i1].stereo_bond_neighbor[n1]); n1++ ) { - if ( !PARITY_CALCULATE(at[i1].stereo_bond_parity[n1]) && PARITY_WELL_DEF(at[i1].stereo_bond_parity[n1]) ) { - continue; - } - opposite_atom = (AT_RANK)(s2-1); - /* s2 = at[i1].neighbor[m=(int)at[i1].stereo_bond_ord[n1]]; */ - m=(int)at[i1].stereo_bond_ord[n1]; - for ( j = 0, n = 0; j < at[i1].valence; j ++ ) { - /* if ( at[i1].neighbor[j] != s2 ) */ - if ( j != m ) - { - neigh[n++] = at[i1].neighbor[j]; - } - } - if ( n > 2 ) { - ret = CT_STEREOBOND_ERROR; /* */ - goto exit_function; - } - if ( n != 2 || nRank[(int)neigh[0]] != nRank[(int)neigh[1]] ) { - continue; /* may happen if another half-bond has not a defined parity */ - } - if ( at[i1].nRingSystem == at[(int)neigh[0]].nRingSystem ) { - continue; /* no more ring system membership check is necessary because */ - } /* the two neighbors are to be constitutionally equivalent atoms */ - if ( !nVisited && !(nVisited = (AT_RANK*) inchi_malloc( sizeof(nVisited[0])*num_atoms ) ) ) { - ret = CT_OUT_OF_RAM; /* */ - goto exit_function; - } - memset( nVisited, 0, sizeof(nVisited[0])*num_atoms ); - nVisited[i1] = 1; - if ( PathsHaveIdenticalKnownParities( at, (AT_RANK)i1, neigh[0], (AT_RANK)i1, neigh[1], nVisited, nVisited, nRank, nCanonRank, 1 ) ) { - if ( !RemoveOneStereoBond( at, i1, /* atom number*/ n1 /* stereo bond number*/ ) ) { - ret = CT_STEREOBOND_ERROR; /* */ - goto exit_function; - } - n1 --; /* cycle counter may temporarily become negative */ - /* Remove from pCS */ - nAtomRank1 = inchi_max( nCanonRank[i1], nCanonRank[opposite_atom]); - nAtomRank2 = inchi_min( nCanonRank[i1], nCanonRank[opposite_atom]); - for ( n = 0, m = pCS->nLenLinearCTStereoDble-1; n <= m; n ++ ) { - if ( pCS->LinearCTStereoDble[n].at_num1 == nAtomRank1 && - pCS->LinearCTStereoDble[n].at_num2 == nAtomRank2 ) { - if ( n < m ) { /* remove pCS->LinearCTStereoDble[n] */ - memmove( pCS->LinearCTStereoDble + n, - pCS->LinearCTStereoDble + n + 1, - (m-n)*sizeof(pCS->LinearCTStereoDble[0]) ); - } - pCS->nLenLinearCTStereoDble --; -#if ( bRELEASE_VERSION == 0 ) - pCS->bExtract |= EXTR_KNOWN_USED_TO_REMOVE_PARITY; -#endif - m = -1; /* set flag "found" */ - break; - } - } - if ( m >= 0) { - ret = CT_STEREOCOUNT_ERR; /* bond not found */ - goto exit_function; - } - ret ++; /* number of removed known in advance non-stereo bonds */ - } - } - } - -exit_function: - - if ( nVisited ) { - inchi_free( nVisited ); - } - return ret; -} -#endif /* } REMOVE_KNOWN_NONSTEREO */ -/**********************************************************************************/ -/* Find stereo center parities known in advance */ -int SetKnownStereoCenterParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, - const AT_RANK *nRank, const AT_RANK *nAtomNumber ) -{ - int i, j, n, m, j1, k, num_neigh, iMax, trans_i, trans_k, prev_trans, num_set; - AT_RANK nAtomRank; - AT_RANK nNeighRank[MAX_NUM_STEREO_ATOM_NEIGH]; - AT_RANK nNeighCanonRank[MAX_NUM_STEREO_ATOM_NEIGH]; - for ( i = 0, num_set = 0; i < num_atoms; i ++ ) { - if ( !at[i].parity || at[i].stereo_bond_neighbor[0] ) { - continue; - } - if ( at[i].stereo_atom_parity != AB_PARITY_CALC || !PARITY_WELL_DEF(at[i].parity) ) { - continue; - } - num_neigh = at[i].valence; - for ( j = 0; j < num_neigh; j ++ ) { - nNeighRank[j] = nRank[(int)at[i].neighbor[j]]; - } - nAtomRank = nRank[i]; - if ( num_neigh == 1 ) { /* other neighbors must be implicit H */ - at[i].stereo_atom_parity = at[i].parity; - trans_i = 0; - } else { - /* sort constitutional equivalence ranks of the neighbors */ - trans_i = insertions_sort(nNeighRank, num_neigh, sizeof(nNeighRank[0]), comp_AT_RANK); - for ( j = 1; j < num_neigh; j ++ ) { - if ( nNeighRank[j-1] == nNeighRank[j] ) - break; /* at[i] has consitutionally identical neighbors */ - } - if ( j < num_neigh ) { - /* at least 2 neighbors are const. identical; parity cannot be calculated at this time */ - continue; /* try next stereo atom */ - } - } - prev_trans = -1; - trans_k = 0; - /* find neighbors of constitutionally equivalent stereo centers */ - /* and at[i] parities in case those centers are mapped on at[i] */ - for ( iMax = (int)nAtomRank-1, j1 = 0; j1 <= iMax && nAtomRank==nRank[k=(int)nAtomNumber[iMax-j1]]; j1 ++ ) { - /* at[k] is constitutionally equivalent to at[i] */ - if ( (int)at[k].valence != num_neigh ) { - return CT_STEREOCOUNT_ERR; /* */ - } - /* -- commented out to accept non-stereogenic atoms since -- - -- they may participate in mapping stereocenters 12-16-2003 -- - if ( !PARITY_VAL(at[k].parity) ) { - continue; // not a stereogenic atom - } - */ - for ( j = 0, m = 0; m < num_neigh; m ++ ) { - for ( n = 0; n < num_neigh; n ++ ) { - if ( nRank[(int)at[k].neighbor[n]] == nNeighRank[m] ) { - /* save canonical numbers (ranks) of the neighbors in - * order of increasing constit. equivalence ranks */ - nNeighCanonRank[m] = nCanonRank[(int)at[k].neighbor[n]]; - j ++; - break; - } - } - } - if ( j != num_neigh ) { - return CT_STEREOCOUNT_ERR; /* */ - } - trans_k = insertions_sort(nNeighCanonRank, num_neigh, sizeof(nNeighCanonRank[0]), comp_AT_RANK); - trans_k %= 2; - if ( prev_trans < 0 ) { - prev_trans = trans_k; - } else - if ( trans_k != prev_trans ) { - /* different mappings may produce different parities. Cannot find the parity at this time */ - /* this may happen when a set of constit. equivalent atoms has non-contiguous canonical numbers */ - break; - } - } - if ( trans_k == prev_trans ) { - at[i].stereo_atom_parity = 2 - (at[i].parity + trans_i + prev_trans ) % 2; - num_set ++; - } - } - return num_set; -} - -#if ( REMOVE_KNOWN_NONSTEREO == 1 ) /* { */ -/**********************************************************************************/ -/* DFS along paths starting from the stereocenter through pairs of cut-edges */ -int RemoveKnownNonStereoCenterParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, - const AT_RANK *nRank, CANON_STAT *pCS) -{ - int i, j, n, m, k, num_neigh, ret = 0; - /*AT_RANK nAtomRank;*/ - AT_RANK nNeighRank[MAX_NUM_STEREO_ATOM_NEIGH], nNeighOrd[MAX_NUM_STEREO_ATOM_NEIGH]; - - AT_RANK *nVisited = NULL; - - for ( i = 0; i < num_atoms; i ++ ) { - if ( !at[i].parity || at[i].stereo_bond_neighbor[0] ) { - continue; - } - if ( !PARITY_CALCULATE(at[i].stereo_atom_parity) && PARITY_WELL_DEF(at[i].stereo_atom_parity) ) { - continue; - } - num_neigh = at[i].valence; - for ( j = 0; j < num_neigh; j ++ ) { - nNeighRank[j] = nRank[(int)at[i].neighbor[j]]; - nNeighOrd[j] = j; - } - /*nAtomRank = nRank[i];*/ - if ( num_neigh == 1 ) { - continue; - } - pn_RankForSort = nNeighRank; - insertions_sort(nNeighOrd, num_neigh, sizeof(nNeighRank[0]), CompRanksOrd); - for ( j = k = 1; k && j < num_neigh; j ++ ) { - if ( at[i].nRingSystem != at[(int)at[i].neighbor[(int)nNeighOrd[j]]].nRingSystem && - /* no more ring system membership check is necessary because */ - /* the two neighbors are to be constitutionally equivalent atoms: */ - nNeighRank[nNeighOrd[j-1]] == nNeighRank[nNeighOrd[j]] ) { - k = j; - do { - if ( !nVisited && !(nVisited = (AT_RANK*) inchi_malloc( sizeof(nVisited[0])*num_atoms ) ) ) { - ret = CT_OUT_OF_RAM; /* */ - goto exit_function; - } - memset( nVisited, 0, sizeof(nVisited[0])*num_atoms ); - nVisited[i] = 1; - if ( PathsHaveIdenticalKnownParities( at, (AT_RANK)i, at[i].neighbor[(int)nNeighOrd[j-1]], - (AT_RANK)i, at[i].neighbor[(int)nNeighOrd[k]], - nVisited, nVisited, nRank, nCanonRank, 1 ) ) { - at[i].parity = 0; /* remove parity */ - at[i].stereo_atom_parity = 0; - at[i].final_parity = 0; - /* at[i].bHasStereoOrEquToStereo = 0; */ - for ( n = 0, m = pCS->nLenLinearCTStereoCarb-1; n <= m; n ++ ) { - if ( pCS->LinearCTStereoCarb[n].at_num == nCanonRank[i] ) { - if ( n < m ) { /* remove pCS->LinearCTStereoCarb[n] */ - memmove( pCS->LinearCTStereoCarb + n, - pCS->LinearCTStereoCarb + n + 1, - (m-n)*sizeof(pCS->LinearCTStereoCarb[0]) ); - } - pCS->nLenLinearCTStereoCarb --; - k = 0; -#if ( bRELEASE_VERSION == 0 ) - pCS->bExtract |= EXTR_KNOWN_USED_TO_REMOVE_PARITY; -#endif - break; - } - } - if ( k ) { - ret = CT_STEREOCOUNT_ERR; /* */ - goto exit_function; - } - ret ++; - break; - } - } while ( ++k < num_neigh && nNeighRank[nNeighOrd[j-1]] == nNeighRank[nNeighOrd[k]] ); - } - } - } - -exit_function: - - if ( nVisited ) { - inchi_free( nVisited ); - } - - return ret; -} -#endif /* } REMOVE_KNOWN_NONSTEREO */ -/**********************************************************************************/ -/* Find stereo center parities known in advance */ -int MarkKnownEqualStereoCenterParities( sp_ATOM *at, int num_atoms, - const AT_RANK *nRank, const AT_RANK *nAtomNumber ) -{ - int i, j1, k, num_centers, iMax, bDifferentParities; - AT_RANK nAtomRank; - int parity, parity_k; - num_centers = 0; - for ( i = 0; i < num_atoms; i ++ ) { - if ( !at[i].parity || at[i].stereo_bond_neighbor[0] ) { - continue; - } - if ( at[i].bHasStereoOrEquToStereo ) { - continue; /* already marked */ - } - if ( /*!PARITY_KNOWN(at[i].stereo_atom_parity) ||*/ (at[i].stereo_atom_parity & KNOWN_PARITIES_EQL) ) { - continue; - } - parity = PARITY_VAL(at[i].stereo_atom_parity); - if ( parity == AB_PARITY_NONE ) { - continue; - } - nAtomRank = nRank[i]; - bDifferentParities = -1; - /* find constitutionally equivalent stereo centers and compare their known at this time parities */ - for ( iMax = (int)nAtomRank-1, j1 = 0; j1 <= iMax && nAtomRank==nRank[k=(int)nAtomNumber[iMax-j1]]; j1 ++ ) { - /* at[k] is constitutionally equivalent to at[i] */ - parity_k = PARITY_VAL(at[k].stereo_atom_parity); - if ( parity_k != parity ) { - bDifferentParities = 1; - } else - if ( parity_k == parity && bDifferentParities < 0 ) { - bDifferentParities = 0; - } - if ( !parity_k ) { - at[k].bHasStereoOrEquToStereo = 2; - } else - if ( !at[k].bHasStereoOrEquToStereo ) { - at[k].bHasStereoOrEquToStereo = 1; - } - } - if ( 0 == bDifferentParities && PARITY_KNOWN( parity ) ) { - for ( iMax = (int)nAtomRank-1, j1 = 0; j1 <= iMax && nAtomRank==nRank[k=(int)nAtomNumber[iMax-j1]]; j1 ++ ) { - /* at[k] is constitutionally equivalent to at[i] */ - at[k].stereo_atom_parity |= KNOWN_PARITIES_EQL; - num_centers ++; - } - } - } - return num_centers; -} -/*****************************************************************************/ -/* invert known parities in at[] and in pCS->LinearCTStereoDble */ -/* pCS->LinearCTStereoCarb */ -/* nCanonRank[] contains canonical ranks used to fill pCS->LinearCTStereo... */ -/* nAtomNumberCanon[] will be filled with atom numbers in canonical order */ -/*****************************************************************************/ -int InvertStereo( sp_ATOM *at, int num_at_tg, - AT_RANK *nCanonRank, AT_RANK *nAtomNumberCanon, - CANON_STAT *pCS, int bInvertLinearCTStereo ) -{ - int i, j, j1, j2, num_changes, parity, cumulene_len; - num_changes = 0; - for ( i = 0; i < num_at_tg; i ++ ) { - nAtomNumberCanon[(int)nCanonRank[i]-1] = i; - } - for ( i = 0; i < pCS->nLenLinearCTStereoCarb; i ++ ) { - parity = pCS->LinearCTStereoCarb[i].parity; - if ( ATOM_PARITY_WELL_DEF( parity ) ) { - j = nAtomNumberCanon[(int)pCS->LinearCTStereoCarb[i].at_num-1]; - if ( PARITY_WELL_DEF(at[j].parity) ) { - at[j].parity ^= AB_INV_PARITY_BITS; - } else { - goto exit_error; /* inconsistency */ - } - if ( bInvertLinearCTStereo ) { - pCS->LinearCTStereoCarb[i].parity ^= AB_INV_PARITY_BITS; - } - num_changes ++; - if ( PARITY_WELL_DEF(at[j].stereo_atom_parity) ) { - at[j].stereo_atom_parity ^= AB_INV_PARITY_BITS; - } - if ( PARITY_WELL_DEF(at[j].final_parity) ) { - at[j].final_parity ^= AB_INV_PARITY_BITS; - } - } - } - for ( i = 0; i < pCS->nLenLinearCTStereoDble; i ++ ) { - parity = pCS->LinearCTStereoDble[i].parity; - if ( ATOM_PARITY_WELL_DEF( parity ) ) { - j1 = nAtomNumberCanon[(int)pCS->LinearCTStereoDble[i].at_num1-1]; - cumulene_len = BOND_CHAIN_LEN(at[j1].stereo_bond_parity[0]); - if ( cumulene_len % 2 ) { - /* invert only in case of allene */ - j2 = nAtomNumberCanon[(int)pCS->LinearCTStereoDble[i].at_num2-1]; - /* checks for debug only */ - if ( 1 < MAX_NUM_STEREO_BONDS ) { - if ( at[j1].stereo_bond_neighbor[1] || - at[j2].stereo_bond_neighbor[1] ) { - goto exit_error; /* inconsitency: atom has more than one cumulene bond */ - } - } - if ( cumulene_len != BOND_CHAIN_LEN(at[j2].stereo_bond_parity[0]) || - j1+1 != at[j2].stereo_bond_neighbor[0] || - j2+1 != at[j1].stereo_bond_neighbor[0] ) { - goto exit_error; /* inconsitency: atoms should refer to each other */ - } - /* invert parities */ - if ( PARITY_WELL_DEF(at[j1].parity) && PARITY_WELL_DEF(at[j2].parity) ) { - j = inchi_min( j1, j2 ); - at[j].parity ^= AB_INV_PARITY_BITS; /* for reversability always invert only atom with the smaller number */ - } else { - goto exit_error; /* inconsistency */ - } - if ( bInvertLinearCTStereo ) { - pCS->LinearCTStereoDble[i].parity ^= AB_INV_PARITY_BITS; - } - num_changes ++; - if ( PARITY_WELL_DEF(at[j1].stereo_bond_parity[0]) ) { - at[j1].stereo_bond_parity[0] ^= AB_INV_PARITY_BITS; - } - if ( PARITY_WELL_DEF(at[j2].stereo_bond_parity[0]) ) { - at[j2].stereo_bond_parity[0] ^= AB_INV_PARITY_BITS; - } - } - } - } - - return num_changes; - -exit_error: - - return CT_STEREOCOUNT_ERR; -} -/**********************************************************************************/ -/* Make sure atoms stereo descriptors fit molecular symmetry and remove */ -/* parity from obviously non-stereo atoms and bonds */ -int FillOutStereoParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, const AT_RANK *nAtomNumberCanon, - const AT_RANK *nRank, const AT_RANK *nAtomNumber, CANON_STAT *pCS, int bIsotopic ) -{ - int ret; - /* unmark atoms with 2 or more constitutionally equivalent neighbors */ - /* such that there is no path through them to an atom with parity */ - ret = UnmarkNonStereo( at, num_atoms, nRank, nAtomNumber, bIsotopic ); - if ( ret < 0 ) - return ret; /* program error? */ /* */ - ret = FillAllStereoDescriptors( at, num_atoms, nCanonRank, nAtomNumberCanon, pCS ); /* ret<0: error */ - if ( !ret ) { - ret = pCS->nLenLinearCTStereoCarb + pCS->nLenLinearCTStereoDble; - } - if ( ret < 0 ) { - return ret; /* program error? */ /* */ - } - - if ( ret >= 0 ) { - int ret2; - ret2 = SetKnownStereoCenterParities( at, num_atoms, nCanonRank, nRank, nAtomNumber ); - if ( ret2 >= 0 ) { - ret2 = MarkKnownEqualStereoCenterParities( at, num_atoms, nRank, nAtomNumber ); - } - if ( ret2 >= 0 ) { - ret2 = SetKnownStereoBondParities( at, num_atoms, nCanonRank, nRank, nAtomNumber ); - if ( ret2 >= 0 ) { - ret2 = MarkKnownEqualStereoBondParities( at, num_atoms, nRank, nAtomNumber); - } - } -#if ( REMOVE_KNOWN_NONSTEREO == 1 ) /* { */ - if ( ret2 >= 0 ) { - int ret3; - do { - ret2 = RemoveKnownNonStereoCenterParities( at, num_atoms, nCanonRank, nRank, pCS); - if ( ret2 >= 0 ) { - ret3 = RemoveKnownNonStereoBondParities( at, num_atoms, nCanonRank, nRank, pCS); - ret2 = ret3 >= 0? ret2+ret3 : ret3; - } - } - while( ret2 > 0 ); - } - if ( RETURNED_ERROR( ret2 ) ) { - ret = ret2; - } -#endif /* } REMOVE_KNOWN_NONSTEREO */ - } - - return ret; /* non-zero means error */ -} -/**********************************************************************************/ -int GetStereoNeighborPos( sp_ATOM *at, int iAt1, int iAt2 ) -{ - int k1; - AT_RANK sNeigh = (AT_RANK)(iAt2+1); - AT_RANK s; - for ( k1 = 0; k1 < MAX_NUM_STEREO_BONDS && (s = at[iAt1].stereo_bond_neighbor[k1]); k1 ++ ) { - if ( s == sNeigh ) { - return k1; - } - } - return -1; /* neighbor not found */ -} - -/**********************************************************************************/ -/* Extracted from FillSingleStereoDescriptors(...) */ -/**********************************************************************************/ -int GetStereoBondParity(sp_ATOM *at, int i, int n, AT_RANK *nRank ) -{ -int k1, k2, s, parity; - - if ( at[i].stereo_bond_neighbor[0] ) { - for ( k1 = 0; k1 < MAX_NUM_STEREO_BONDS && (s = (int)at[i].stereo_bond_neighbor[k1]); k1 ++ ) { - if ( --s == n ) { - goto neigh1_found; - } - } - return -1; /* error: not a stereo neighbor */ -neigh1_found: - if ( PARITY_KNOWN( at[i].stereo_bond_parity[k1] ) ) { - return PARITY_VAL( at[i].stereo_bond_parity[k1] ); - } - for ( k2 = 0; k2 < MAX_NUM_STEREO_BONDS && (s = (int)at[n].stereo_bond_neighbor[k2]); k2 ++ ) { - if ( --s == i ) { - goto neigh2_found; - } - } - return -1; /* error: not a stereo neighbor */ -neigh2_found:; - } else { - return -1; /* error: not a stereo bond */ - } - - if ( ATOM_PARITY_WELL_DEF(at[i].parity) && - ATOM_PARITY_WELL_DEF(at[n].parity) && - MIN_DOT_PROD <= abs(at[i].stereo_bond_z_prod[k1]) ) { - /* bond parity can be calculated */ - int half_parity1, half_parity2; - /* check whether all neighbors are defined */ - - - half_parity1 = HalfStereoBondParity( at, i, k1, nRank ); - half_parity2 = HalfStereoBondParity( at, n, k2, nRank ); - if ( !half_parity1 || !half_parity2 ) - return 0; /* ranks undefined or not a stereo bond */ - if ( ATOM_PARITY_WELL_DEF(half_parity1) && - ATOM_PARITY_WELL_DEF(half_parity2) ) { - parity = 2 - ( half_parity1 + half_parity2 - + (at[i].stereo_bond_z_prod[k1] < 0))%2; - } else { - return CT_STEREOBOND_ERROR; /* */ - } - } else { - /* parity cannot be calculated: not enough info or 'unknown' */ - if ( AB_PARITY_NONE != (parity = inchi_max(at[i].parity, at[n].parity)) ) { - parity = AB_PARITY_UNDF; /* should not happen */ - } - } - return parity; -} - - - - -/**********************************************************************************/ -/* Extracted from FillSingleStereoDescriptors(...) */ -/**********************************************************************************/ -int GetPermutationParity( sp_ATOM *at, AT_RANK nAvoidNeighbor, AT_RANK *nCanonRank ) -{ - AT_RANK nNeighRank[MAX_NUM_STEREO_ATOM_NEIGH]; - int j, k, parity; - if ( at->valence > MAX_NUM_STEREO_ATOM_NEIGH ) { - parity = -1; /* error */ - } else { - for ( j = k = 0; j < at->valence; j ++ ) { - if ( at->neighbor[j] != nAvoidNeighbor ) { - nNeighRank[k++] = nCanonRank[(int)at->neighbor[j]]; - } - } - if ( k ) { - parity = insertions_sort( nNeighRank, k, sizeof(nNeighRank[0]), comp_AT_RANK); - if ( nNeighRank[0] ) { - parity = 2 - parity % 2; - } else { - parity = 0; /* not all ranks are known */ - } - } else { - /* special case: HX= with implicit H */ - parity = 2; - } - } - return parity; -} -/**********************************************************************************/ -int GetStereoCenterParity(sp_ATOM *at, int i, AT_RANK *nRank ) -{ - AT_NUMB nNeighborNumber2[MAXVAL]; - int parity; - int k, num_trans; - - if ( !at[i].parity ) { - return 0; /* not a stereo center */ - } - if ( at[i].stereo_bond_neighbor[0] ) { - return -1; /* a stereo bond atom, not a stereo center */ - } - - if ( ATOM_PARITY_WELL_DEF(at[i].parity) ) { - /* number of neighbors transpositions to the sorted order is unknown. Find it. */ - /* If parity is not well-defined then doing this is a waste of time */ - int num_neigh = at[i].valence; - for ( k = 0; k < num_neigh; k ++) { - if ( !nRank[(int)at[i].neighbor[k]] ) - return 0; /* stereo at[i] does not belong to the traversed part of the structure */ - nNeighborNumber2[k] = k; - } - pNeighborsForSort = at[i].neighbor; - pn_RankForSort = nRank; - num_trans=insertions_sort( nNeighborNumber2, num_neigh, sizeof(nNeighborNumber2[0]), CompNeighborsAT_NUMBER ); -#ifndef CT_NEIGH_INCREASE - num_trans += ((num_neigh*(num_neigh-1))/2)%2; /* get correct parity for ascending order */ -#endif - parity = 2 - (at[i].parity + num_trans)%2; - } else { - parity = at[i].parity; - } - - return parity; -} - diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichicomp.h b/INCHI-1-SRC/INCHI_API/inchi_dll/ichicomp.h deleted file mode 100644 index d5451f9..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichicomp.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHI_COMPAT_H__ -#define __INCHI_COMPAT_H__ - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - /* compatibility */ - -#if ( defined(__GNUC__) && defined(__MINGW32__) && __GNUC__ == 3 && __GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ == 0 && defined(_WIN32) ) -/* replace with the proper definition for GNU gcc & MinGW-2.0.0-3 (mingw special 20020817-1), gcc 3.2 core */ -#define my_va_start(A,B) ((A)=(va_list)__builtin_next_arg(lpszFormat)) -#else -#define my_va_start va_start -#endif - - - -/* ANSI redefinitions */ -#ifdef COMPILE_ANSI_ONLY /* { */ -#ifndef __isascii -#define __isascii(val) ((unsigned)(val) <= 0x7F) -#endif - -/* #ifndef __GNUC__ */ -/* these non-ANSI functions are implemented in gcc */ -#include - /* this #include provides size_t definition */ - /* implementation is located in util.c */ -/*#if ( !defined(_MSC_VER) || defined(__STDC__) && __STDC__ == 1 )*/ - -#if ( defined(COMPILE_ADD_NON_ANSI_FUNCTIONS) || defined(__STDC__) && __STDC__ == 1 ) - -/* support (VC++ Language extensions) = OFF && defined(COMPILE_ANSI_ONLY) */ -int memicmp (const void*, const void*, size_t); -int stricmp( const char *s1, const char *s2 ); -char *_strnset( char *string, int c, size_t count ); -char *_strdup( const char *string ); -#endif -/* #endif */ - -#endif /* } */ - -#define inchi_max(a,b) (((a)>(b))?(a):(b)) -#define inchi_min(a,b) (((a)<(b))?(a):(b)) - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /* __INCHI_COMPAT_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichidrp.h b/INCHI-1-SRC/INCHI_API/inchi_dll/ichidrp.h deleted file mode 100644 index 89c9f63..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichidrp.h +++ /dev/null @@ -1,180 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHIDRP_H__ -#define __INCHIDRP_H__ - -#ifndef COMPILE_ANSI_ONLY /* { */ -/******************************************** - * Parameters for the structure drawing - ********************************************/ -#define TDP_LEN_LBL 16 /* length of a label (label: Req., Shown, Found) */ -/* #define TDP_NUM_LBL 3 */ /* number of labels */ -/* #define TDP_NUM_PAR 3 */ /* number of types per label (types: B/T, I/N, S) */ -typedef enum tagTblTypes {itBASIC, itISOTOPIC, itSTEREO, TDP_NUM_PAR} TBL_TYPES; /* types */ -typedef enum tagTblLabels{ ilSHOWN, TDP_NUM_LBL} TBL_LABELS; /* labels */ -typedef struct tagTblDrawPatms { - char ReqShownFoundTxt[TDP_NUM_LBL][TDP_LEN_LBL]; - char ReqShownFound[TDP_NUM_LBL][TDP_NUM_PAR]; - int nOrientation; /* 10*degrees: 0 or 2700 */ - int bDrawTbl; -} TBL_DRAW_PARMS; -/*********************************************/ -typedef struct tagDrawParmsSettings { - TBL_DRAW_PARMS *tdp; - unsigned long ulDisplTime; - int bOrigAtom; - int nFontSize; -} SET_DRAW_PARMS; /* input only: how to draw or calculate */ -/*********************************************/ -typedef struct tagReturnedDrawParms { - int bEsc; -} RET_DRAW_PARMS; -/*********************************************/ -typedef struct tagPersistDrawParms { - int rcPict[4]; -} PER_DRAW_PARMS; /* saved between displaying different structures */ -/*********************************************/ -typedef struct tagDrawParms { - SET_DRAW_PARMS sdp; /* how to draw: fill on the 1st call */ - RET_DRAW_PARMS rdp; /* returned when drawing window is closed */ - PER_DRAW_PARMS *pdp; /* persistent: save between calls (window size) */ -#ifndef TARGET_LIB_FOR_WINCHI -#ifndef COMPILE_ANSI_ONLY - AT_NUMB *nEquLabels; /* num_inp_atoms elements, value>0 marks atoms in the set #value */ - AT_NUMB nNumEquSets; /* max mark value */ - AT_NUMB nCurEquLabel; /* current mark */ -#endif -#endif -} DRAW_PARMS; /* Settings: How to draw the structure */ - -#endif /* } COMPILE_ANSI_ONLY */ - -#define MAX_NUM_PATHS 4 - - -typedef enum tagInputType { INPUT_NONE=0, INPUT_MOLFILE=1, INPUT_SDFILE=2, INPUT_INCHI_XML=3, INPUT_INCHI_PLAIN=4, INPUT_CMLFILE=5, INPUT_INCHI=6, INPUT_MAX } INPUT_TYPE; - -/* bCalcInChIHash values */ -typedef enum tagInChIHashCalc -{ - INCHIHASH_NONE=0, - INCHIHASH_KEY=1, - INCHIHASH_KEY_XTRA1=2, - INCHIHASH_KEY_XTRA2=3, - INCHIHASH_KEY_XTRA1_XTRA2=4 -} -INCHI_HASH_CALC; - -typedef struct tagInputParms { - char szSdfDataHeader[MAX_SDF_HEADER+1]; - char *pSdfLabel; - char *pSdfValue; - long lSdfId; - long lMolfileNumber; -#ifndef COMPILE_ANSI_ONLY - DRAW_PARMS dp; - PER_DRAW_PARMS pdp; - TBL_DRAW_PARMS tdp; -#endif -/* - -- Files -- - ip->path[0] => Input - ip->path[1] => Output (INChI) - ip->path[2] => Log - ip->path[3] => Problem structures - ip->path[4] => Errors file (ACD) - -*/ - const char *path[MAX_NUM_PATHS]; - int num_paths; - long first_struct_number; - long last_struct_number; - INPUT_TYPE nInputType; - INCHI_MODE nMode; - int bAbcNumbers; - /*int bXml;*/ - int bINChIOutputOptions; /* !(ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) */ - int bCtPredecessors; - int bXmlStarted; - int bDisplayEachComponentINChI; - - long msec_MaxTime; /* was ulMaxTime; max time to run ProsessOneStructure */ - long msec_LeftTime; - - long ulDisplTime; /* not used: max structure or question display time */ - int bDisplay; - int bDisplayIfRestoreWarnings; /* InChI->Struct debug */ - int bMergeAllInputStructures; - int bSaveWarningStructsAsProblem; - int bSaveAllGoodStructsAsProblem; - int bGetSdfileId; - int bGetMolfileNumber; /* read molfile number from the name line like "Structure #22" */ - int bCompareComponents; /* see flags CMP_COMPONENTS, etc. */ - int bDisplayCompositeResults; - int bDoNotAddH; - int bNoStructLabels; - int bChiralFlag; - int bAllowEmptyStructure; - /*^^^ */ - int bCalcInChIHash; - int bFixNonUniformDraw; /* correct non-uniformly drawn oxoanions and amidinium cations. */ - /*^^^ */ - INCHI_MODE bTautFlags; - INCHI_MODE bTautFlagsDone; - -#if ( READ_INCHI_STRING == 1 ) - int bReadInChIOptions; -#endif - -/* post v.1 features */ -#if ( UNDERIVATIZE == 1 ) - int bUnderivatize; -#endif -#if ( RING2CHAIN == 1 ) - int bRing2Chain; -#endif -#if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) - int bIngnoreUnchanged; -#endif - -} INPUT_PARMS; - -#endif /* __INCHIDRP_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichimain.h b/INCHI-1-SRC/INCHI_API/inchi_dll/ichimain.h deleted file mode 100644 index 5e31c23..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichimain.h +++ /dev/null @@ -1,224 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHIMAIN_H__ -#define __INCHIMAIN_H__ - -#define ESC_KEY 27 -#define INCHI_SEGM_BUFLEN 64000 - -/********************************************************************/ -typedef struct tagStructData { - unsigned long ulStructTime; - int nErrorCode; - int nErrorType; - int nStructReadError; - char pStrErrStruct[STR_ERR_LEN]; - long fPtrStart; /* or number of processed structures */ - long fPtrEnd; /* or number of errors */ - int bXmlStructStarted; - int bUserQuit; - int bUserQuitComponent; - int bUserQuitComponentDisplay; - int bChiralFlag; - /* information related to normal or disconnected layers */ - int num_taut[INCHI_NUM]; - int num_non_taut[INCHI_NUM]; - INCHI_MODE bTautFlags[INCHI_NUM]; /* reconnected does not have TG_FLAG_DISCONNECT_COORD_DONE flag */ - INCHI_MODE bTautFlagsDone[INCHI_NUM]; /* reconnected does not have TG_FLAG_DISCONNECT_COORD_DONE flag */ - int num_components[INCHI_NUM]; /* number of allocated INChI, INChI_Aux data structures */ - /* debugging info */ -#if ( bRELEASE_VERSION == 0 ) - int bExtract; -#endif - -} STRUCT_DATA; - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -int process_single_input( int argc, char *argv[ ] ); - -int ReadCommandLineParms(int argc, const char *argv[], INPUT_PARMS *ip, - char *szSdfDataValue, unsigned long *ulDisplTime, - int bReleaseVersion, INCHI_IOSTREAM *log_file); -void HelpCommandLineParms(INCHI_IOSTREAM *f); -int OpenFiles( FILE **inp_file, FILE **output_file, FILE **log_file, FILE **prb_file, INPUT_PARMS *ip ); -#ifndef COMPILE_ANSI_ONLY -int DisplayStructure( inp_ATOM *at, int num_at, int num_removed_H, int bAdd_DT_to_num_H, int nNumRemovedProtons, NUM_H nNumRemovedProtonsIsotopic[], - int bIsotopic, int j /*bTautomeric*/, INChI **cur_INChI, INChI_Aux **cur_INChI_Aux, - int bAbcNumbers, DRAW_PARMS *dp, INCHI_MODE nMode, char *szTitle ); -void FillTableParms( SET_DRAW_PARMS *sdp, INChI **cur_INChI, INChI_Aux **cur_INChI_Aux, INCHI_MODE nMode, int bShowIsotopic, int bShowTaut ); -void FillCompositeTableParms( SET_DRAW_PARMS *sdp, AT_NUMB StereoFlags, - INCHI_MODE nMode, int bShowIsotopic, int bShowTaut ); -#endif -int PrintInputParms(INCHI_IOSTREAM *log_file, INPUT_PARMS *ip); -const char *ErrMsg( int nErrorCode ); -int SortAndPrintINChI(INCHI_IOSTREAM *output_file, - char *pStr, int nStrLen, - INCHI_IOSTREAM *log_file, - INPUT_PARMS *ip, - ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data, - COMP_ATOM_DATA composite_norm_data[INCHI_NUM][TAUT_NUM+1], - ORIG_STRUCT *pOrigStruct, int num_components[INCHI_NUM], - int num_non_taut[INCHI_NUM], int num_taut[INCHI_NUM], - INCHI_MODE bTautFlags[INCHI_NUM], INCHI_MODE bTautFlagsDone[INCHI_NUM], - NORM_CANON_FLAGS *pncFlags, long num_inp, - PINChI2 *pINChI[INCHI_NUM], - PINChI_Aux2 *pINChI_Aux[INCHI_NUM], - int *pSortPrintINChIFlags, unsigned char save_opt_bits); -void FreeAllINChIArrays(PINChI2 *pINChI[INCHI_NUM], - PINChI_Aux2 *pINChI_Aux[INCHI_NUM], - int num_components[2]); -void FreeINChIArrays(PINChI2 *pINChI, - PINChI_Aux2 *pINChI_Aux, - int num_components ); -void SplitTime(unsigned long ulTotalTime, - int *hours, int *minutes, int *seconds, int *mseconds ); - -int ReadTheStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, INCHI_IOSTREAM *inp_file, ORIG_ATOM_DATA *orig_inp_data, - int inp_index, int *out_index ); -int TreatReadTheStructureErrors( STRUCT_DATA *sd, INPUT_PARMS *ip, int nLogMask, - INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, - INCHI_IOSTREAM *output_file, - INCHI_IOSTREAM *prb_file, - ORIG_ATOM_DATA *orig_inp_data, - long *num_inp, - char *pStr, int nStrLen ); -int GetOneComponent(STRUCT_DATA *sd, INPUT_PARMS *ip, - INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, - INP_ATOM_DATA *inp_cur_data, - ORIG_ATOM_DATA *orig_inp_data, - int i, long num_inp, char *pStr, int nStrLen ); -int CreateOneComponentINChI(STRUCT_DATA *sd, INPUT_PARMS *ip, - INP_ATOM_DATA *inp_cur_data, ORIG_ATOM_DATA *orig_inp_data, - PINChI2 *pINChI, PINChI_Aux2 *pINChI_Aux, int iINChI, - int i, long num_inp, INP_ATOM_DATA **inp_norm_data, - NORM_CANON_FLAGS *pncFlags, INCHI_IOSTREAM *log_file); -int TreatCreateOneComponentINChIError(STRUCT_DATA *sd, INPUT_PARMS *ip, - ORIG_ATOM_DATA *orig_inp_data, - int i, long num_inp, - INCHI_IOSTREAM *inp_file, - INCHI_IOSTREAM *log_file, - INCHI_IOSTREAM *output_file, - INCHI_IOSTREAM *prb_file, - char *pStr, int nStrLen ); -int TreatCreateINChIWarning(STRUCT_DATA *sd, INPUT_PARMS *ip, - ORIG_ATOM_DATA *orig_inp_data, - long num_inp, - INCHI_IOSTREAM *inp_file, - INCHI_IOSTREAM *log_file, - INCHI_IOSTREAM *output_file, - INCHI_IOSTREAM *prb_file, - char *pStr, int nStrLen ); - -#if ( TEST_RENUMB_ATOMS == 1 || READ_INCHI_STRING == 1 ) /* { */ -int CompareINChI( INChI *i1, INChI *i2, INChI_Aux *a1, INChI_Aux *a2 ); -#endif - -void eat_keyboard_input( void ); -int user_quit( const char *msg, unsigned long ulMaxTime ); - -int GetOneStructure(STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, - INCHI_IOSTREAM *inp_file, - INCHI_IOSTREAM *log_file, - INCHI_IOSTREAM *output_file, - INCHI_IOSTREAM *prb_file, - ORIG_ATOM_DATA *orig_inp_data, - long *num_inp, char *pStr, int nStrLen, - STRUCT_FPTRS *struct_fptrs ); -int ProcessOneStructure(STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, - PINChI2 *pINChI2[INCHI_NUM], - PINChI_Aux2 *pINChI_Aux2[INCHI_NUM], - INCHI_IOSTREAM *inp_file, - INCHI_IOSTREAM *log_file, - INCHI_IOSTREAM *output_file, - INCHI_IOSTREAM *prb_file, - ORIG_ATOM_DATA *orig_inp_data, - ORIG_ATOM_DATA *prep_inp_data, - long num_inp, char *pStr, int nStrLen, - unsigned char save_opt_bits); - -int CreateOneStructureINChI(STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, - PINChI2 *pINChI2[INCHI_NUM], - PINChI_Aux2 *pINChI_Aux2[INCHI_NUM], - int iINChI, - INCHI_IOSTREAM *inp_file, - INCHI_IOSTREAM *log_file, - INCHI_IOSTREAM *output_file, - INCHI_IOSTREAM *prb_file, - ORIG_ATOM_DATA *orig_inp_data, - ORIG_ATOM_DATA *prep_inp_data, - COMP_ATOM_DATA composite_norm_data2[][TAUT_NUM+1], - long num_inp, char *pStr, int nStrLen, - NORM_CANON_FLAGS *pncFlags ); - -int bIsStructChiral(PINChI2 *pINChI2[INCHI_NUM], int num_components[]); -int PreprocessOneStructure(STRUCT_DATA *sd, INPUT_PARMS *ip, - ORIG_ATOM_DATA *orig_inp_data, - ORIG_ATOM_DATA *prep_inp_data ); -int FillOutOrigStruct(ORIG_ATOM_DATA *orig_inp_data, - ORIG_STRUCT *pOrigStruct, - STRUCT_DATA *sd); -void FreeOrigStruct( ORIG_STRUCT *pOrigStruct); - - -int ReadWriteInChI(INCHI_IOSTREAM *pInp, INCHI_IOSTREAM *pOut, INCHI_IOSTREAM *pLog, - INPUT_PARMS *ip_inp, - STRUCT_DATA *sd_inp, - /* the following are InChI library-specific parameters */ - inp_ATOM **at, int *num_at, - char *szMsg, int nMsgLen, - unsigned long WarningFlags[2][2]); - -int CompareHillFormulasNoH(const char *f1, const char *f2, int *num_H1, int *num_H2); - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /* __INCHIMAIN_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichimake.h b/INCHI-1-SRC/INCHI_API/inchi_dll/ichimake.h deleted file mode 100644 index c5b4d90..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichimake.h +++ /dev/null @@ -1,240 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHIMAKE_H__ -#define __INCHIMAKE_H__ - -/***********************************************************************/ -/* replace all ' ' delimiters with ',' */ -#define ITEM_DELIMETER "," -#define EXTRA_SPACE "" -#define COMMA_EXTRA_SPACE "," -#define LEN_EXTRA_SPACE 0 - -/**********************************************************************************************/ -/* nCtMode for output INChI */ - -#define CT_MODE_NO_ORPHANS 1 /* no orphans, that CT should have only atoms with neighbors */ -#define CT_MODE_ABC_NUMBERS 2 -#define CT_MODE_ATOM_COUNTS 4 -#define CT_MODE_PREDECESSORS 8 -#define CT_MODE_EQL_H_TOGETHER 16 -#define CT_MODE_ABC_NUM_CLOSURES 32 /* in CT_MODE_ABC_NUMBERS output AB1AC2AB instead of AB-AC-A-B */ - - -/*************** Macros for retrieving requested INChI and INChI_Aux *****************************/ -/* S->pINChI[TAUT_YES] has info: */ -#define HAS_T(S) (S->pINChI[TAUT_YES] && S->pINChI[TAUT_YES]->nNumberOfAtoms) -/* S->pINChI[TAUT_NON] has info: */ -#define HAS_N(S) (S->pINChI[TAUT_NON] && S->pINChI[TAUT_NON]->nNumberOfAtoms) - -/* S->pINChI[TAUT_YES] has tautomeric info: */ -#define HAS_TT(S) (S->pINChI[TAUT_YES] && S->pINChI[TAUT_YES]->nNumberOfAtoms && S->pINChI[TAUT_YES]->lenTautomer>0) -/* S->pINChI[TAUT_YES] has non-taitomeric info: */ -#define HAS_TN(S) (S->pINChI[TAUT_YES] && S->pINChI[TAUT_YES]->nNumberOfAtoms && !S->pINChI[TAUT_YES]->lenTautomer) -/* S->pINChI[TAUT_NON] has non-tautomeric info: */ -#define HAS_NN(S) (S->pINChI[TAUT_NON] && S->pINChI[TAUT_NON]->nNumberOfAtoms && !S->pINChI[TAUT_NON]->lenTautomer) -#define GET_II(M,S) ((M==OUT_N1)? (HAS_TN(S)? TAUT_YES : HAS_NN(S)? TAUT_NON : -1): \ - (M==OUT_T1 || M==OUT_TN)? (HAS_T(S) ? TAUT_YES : HAS_N(S) ? TAUT_NON : -1): \ - (M==OUT_NN)? (HAS_NN(S)? TAUT_NON : HAS_TN(S)? TAUT_YES : -1): \ - (M==OUT_NT)? ((HAS_TT(S) && HAS_NN(S)) ? TAUT_NON : -1) : -1) - -/*********************************/ -/* Equivalence flags definitions */ -/*********************************/ - -/* What is equal (ii = INChI_ITEM) */ -#define iiSTEREO 0x0001 /* stereo (sp2 or sp3) */ -#define iiSTEREO_INV 0x0002 /* inverted stereo (sp3) */ -#define iiNUMB 0x0004 /* numbering or inverted stereo numbering */ -#define iiEQU 0x0008 /* equivalence info */ -/* derived: - (iiSTEREO_INV | iiNUMB) = numbering of inverted stereo -*/ - -/* Additional info to what is equal (INCHI_ITEM_TYPE = iit) */ -#define iitISO 0x0010 /* Isotopic */ -#define iitNONTAUT 0x0020 /* Non-tautomeric */ -/* derived: - (iitISO | iitNONTAUT) = isotopic non-tautomeric -*/ - -/* Where is the equivalent item located (INChI_ITEM_EQUAL_TO = iiEq2) */ -#define iiEq2NONTAUT 0x0040 /* non-tautomeric */ -#define iiEq2ISO 0x0080 /* isotopic */ -#define iiEq2INV 0x0100 /* equal to inverted (stereo sp3) or to numbering of inverted stereo */ - -#define iiEmpty 0x0200 /* item is empty while in the preceding layer the item is not empty */ - -/*********************** Printing strings external declarations *******************************/ - -extern const char sCompDelim[]; - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -/**********************************************************************************************/ -int FillOutINChI( INChI *pINChI, INChI_Aux *pINChI_Aux, - int num_atoms, int num_at_tg, int num_removed_H, - sp_ATOM *at, inp_ATOM *norm_at, CANON_STAT *pCS, int bTautomeric, - INCHI_MODE nUserMode, char *pStrErrStruct ); - - - -int MakeHillFormulaString( char *szHillFormula, char *szLinearCT, int nLen_szLinearCT, int *bOverflow); - -int bHasOrigInfo( ORIG_INFO *OrigInfo, int num_atoms ); -int EqlOrigInfo( INChI_Aux *a1, INChI_Aux *a2 ); - -int MakeAbcNumber( char *szString, int nStringLen, const char *szLeadingDelim, int nValue ); -int MakeDecNumber( char *szString, int nStringLen, const char *szLeadingDelim, int nValue ); -int MakeCtStringNew( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, - S_CHAR *nNum_H, int num_atoms, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); -int MakeCtStringOld( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); -int MakeCtString( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, - S_CHAR *nNum_H, int num_atoms, /* both parameters are not used here */ - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); -int MakeTautString( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); -int MakeEquString( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); -int MakeIsoAtomString( INChI_IsotopicAtom *IsotopicAtom, int nNumberOfIsotopicAtoms, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); -int MakeIsoTautString( INChI_IsotopicTGroup *IsotopicTGroup, int nNumberOfIsotopicTGroups, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); -int MakeIsoHString( int num_iso_H[], char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); -int MakeStereoString( AT_NUMB *at1, AT_NUMB *at2, S_CHAR *parity, int bAddDelim, int nLenCT, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); -int MakeCRVString( ORIG_INFO *OrigInfo, int nLenCT, int bAddDelim, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); -int MakeMult( int mult, const char *szTailingDelim, char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow); -int MakeDelim( const char *szTailingDelim, char *szLinearCT, int nLen_szLinearCT, int *bOverflow); -int MakeEqStr( const char *szTailingDelim, int mult, char *szLinearCT, int nLen_szLinearCT, int *bOverflow); -int MakeHString( int bAddDelim, S_CHAR *LinearCT, int nLenCT, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow ); -AT_NUMB *GetDfsOrder4CT( AT_NUMB *LinearCT, int nLenCT, S_CHAR *nNum_H, int num_atoms, int nCtMode ); - -const char *EquString( int EquVal ); - - -int str_HillFormula(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int num_components, int bUseMulipliers); -int str_HillFormula2(INCHI_SORT *pINChISort /* non-taut */, INCHI_SORT *pINChISort2 /* taut */, - char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int num_components, int bUseMulipliers); -int str_Connections(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int ATOM_MODE, int num_components, int bUseMulipliers); -int str_H_atoms(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int ATOM_MODE, int TAUT_MODE, - int num_components, int bUseMulipliers); -int str_Charge2(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); -int str_Sp2(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); -int str_IsoSp2(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); -int str_Sp3(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bRelRac, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); -int str_IsoSp3(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bRelRac, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); -int str_StereoAbsInv(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int num_components); -int str_IsoStereoAbsInv(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int num_components); -int str_IsoAtoms(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bAbcNumbers, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); -int str_FixedH_atoms(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int ATOM_MODE, int num_components, int bUseMulipliers); - -int str_AuxNumb(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions); -int str_AuxEqu(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); -int str_AuxTgroupEqu(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bUseMulipliers); -int str_AuxIsoTgroupEqu(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bOmitRepetitions, int bUseMulipliers); -int str_AuxInvSp3(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); -int str_AuxInvSp3Numb(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions); -int str_AuxIsoNumb(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions); -int str_AuxIsoEqu(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); -int str_AuxInvIsoSp3(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers); -int str_AuxInvIsoSp3Numb(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions); -int str_AuxChargeRadVal(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bUseMulipliers); -int bin_AuxTautTrans(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, - AT_NUMB **pTrans_n, AT_NUMB **pTrans_s, int bOutType, int num_components); -int str_AuxTautTrans(AT_NUMB *nTrans_n, AT_NUMB *nTrans_s, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int TAUT_MODE, int num_components); -int CompareTautNonIsoPartOfINChI( const INChI *i1, const INChI *i2 ); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /*__INCHIMAKE_H__*/ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichimap2.c b/INCHI-1-SRC/INCHI_API/inchi_dll/ichimap2.c deleted file mode 100644 index 99be79e..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichimap2.c +++ /dev/null @@ -1,2907 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - - -#include "mode.h" - -#include "incomdef.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichicant.h" -#include "ichicomn.h" - -#include "ichicomp.h" - -#define MAP_MODE_STD 0 /* Standard approach: switch 2 neighbors */ -#define MAP_MODE_C2v 1 /* Check for C2v reflection leading to parity inversion */ -#define MAP_MODE_C2 2 /* Check for C2 rotation preserving parities */ -#define MAP_MODE_S4 3 /* Check for S4 rotation/reflection leading to parity inversion */ -/* important: MAP_MODE_STD < (MAP_MODE_C2v, MAP_MODE_C2) < MAP_MODE_S4 */ - -/* local prototypes */ -void DeAllocateForNonStereoRemoval( AT_RANK **nAtomNumberCanon1, AT_RANK **nAtomNumberCanon2, - NEIGH_LIST **nl, NEIGH_LIST **nl1, NEIGH_LIST **nl2, AT_RANK **nVisited1, AT_RANK **nVisited2 ); -int AllocateForNonStereoRemoval( sp_ATOM *at, int num_atoms, const AT_RANK *nSymmRank, AT_RANK *nCanonRank, - AT_RANK **nAtomNumberCanon1, AT_RANK **nAtomNumberCanon2, - NEIGH_LIST **nl, NEIGH_LIST **nl1, NEIGH_LIST **nl2, AT_RANK **nVisited1, AT_RANK **nVisited2 ); -AT_RANK GetMinNewRank(AT_RANK *nAtomRank, AT_RANK *nAtomNumb, AT_RANK nRank1 ); -int BreakNeighborsTie( sp_ATOM *at, int num_atoms, int num_at_tg, int ib, int ia, - AT_RANK *neigh_num, int in1, int in2, int mode, - AT_RANK **pRankStack1, AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, - const AT_RANK *nSymmRank, AT_RANK *nCanonRank, NEIGH_LIST *nl1, NEIGH_LIST *nl2, long *lNumIter ); -int CheckNextSymmNeighborsAndBonds( sp_ATOM *at, AT_RANK cur1, AT_RANK cur2, AT_RANK n1, AT_RANK n2, - AT_RANK *nAvoidCheckAtom, AT_RANK *nVisited1, AT_RANK *nVisited2, - AT_RANK *nVisitOrd1, AT_RANK *nVisitOrd2, const AT_RANK *nRank1, const AT_RANK *nRank2 ); -int CreateCheckSymmPaths( sp_ATOM *at, AT_RANK prev1, AT_RANK cur1, AT_RANK prev2, AT_RANK cur2, - AT_RANK *nAvoidCheckAtom, AT_RANK *nVisited1, AT_RANK *nVisited2, - AT_RANK *nVisitOrd1, AT_RANK *nVisitOrd2, - NEIGH_LIST *nl1, NEIGH_LIST *nl2, const AT_RANK *nRank1, const AT_RANK *nRank2, - AT_RANK *nCanonRank, AT_RANK *nLength, int *bParitiesInverted, int mode ); -int CalculatedPathsParitiesAreIdentical( sp_ATOM *at, int num_atoms, const AT_RANK *nSymmRank, - AT_RANK *nCanonRank, AT_RANK *nAtomNumberCanon, AT_RANK *nAtomNumberCanon1, AT_RANK *nAtomNumberCanon2, - AT_RANK *nVisited1, AT_RANK *nVisited2, - AT_RANK prev_sb_neigh, AT_RANK cur, AT_RANK next1, AT_RANK next2, int nNeighMode, - int bParitiesInverted, int mode, CANON_STAT *pCS, - int vABParityUnknown); -int RemoveCalculatedNonStereoBondParities( sp_ATOM *at, int num_atoms, int num_at_tg, - AT_RANK **pRankStack1, AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, - AT_RANK *nCanonRank, const AT_RANK *nSymmRank, - AT_RANK *nAtomNumberCanon, AT_RANK *nAtomNumberCanon1, AT_RANK *nAtomNumberCanon2, - NEIGH_LIST *nl, NEIGH_LIST *nl1, NEIGH_LIST *nl2, - AT_RANK *nVisited1, AT_RANK *nVisited2, CANON_STAT *pCS, - int vABParityUnknown); -int RemoveCalculatedNonStereoCenterParities( sp_ATOM *at, int num_atoms, int num_at_tg, - AT_RANK **pRankStack1, AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, - AT_RANK *nCanonRank, const AT_RANK *nSymmRank, - AT_RANK *nAtomNumberCanon, AT_RANK *nAtomNumberCanon1, AT_RANK *nAtomNumberCanon2, - NEIGH_LIST *nl, NEIGH_LIST *nl1, NEIGH_LIST *nl2, - AT_RANK *nVisited1, AT_RANK *nVisited2, CANON_STAT *pCS, - int vABParityUnknown); - -int SortNeighLists3( int num_atoms, AT_RANK *nRank, NEIGH_LIST *NeighList, AT_RANK *nAtomNumber ); - - - - - -/************************************************************************************** - * - * Convert sorted equivalence information (nSymmRank) to ranks (nRank) - * nSymmRank and nRank may point to the same array - * - */ -int SortedEquInfoToRanks( const AT_RANK* nSymmRank, AT_RANK* nRank, const AT_RANK* nAtomNumber, int num_atoms, int *bChanged ) -{ - AT_RANK rNew, rOld, nNumDiffRanks; - int i, j, nNumChanges = 0; - for ( i = num_atoms-1, j = (int)nAtomNumber[i], - rOld = nSymmRank[j], rNew = nRank[j] = (AT_RANK)num_atoms, - nNumDiffRanks = 1; - i > 0; - i -- ) { - - j = (int)nAtomNumber[i-1]; - - if ( nSymmRank[j] != rOld ) { - nNumDiffRanks ++; - rNew = (AT_RANK)i; - nNumChanges += (rOld != rNew+1); - rOld = nSymmRank[j]; - } - - nRank[j] = rNew; - } - if ( bChanged ) { - *bChanged = (0 != nNumChanges); - } - return nNumDiffRanks; -} -/************************************************************************************** - * - * Convert sorted ranks (nRank) to sorted equivalence information (nSymmRank) - * nSymmRank and nRank may point to the same array - * - */ -int SortedRanksToEquInfo( AT_RANK* nSymmRank, const AT_RANK* nRank, const AT_RANK* nAtomNumber, int num_atoms ) -{ - AT_RANK rNew, rOld, nNumDiffRanks; - int i, j; - for ( i = 1, j = (int)nAtomNumber[0], - rOld = nRank[j], rNew = nSymmRank[j] = 1, - nNumDiffRanks = 1; - i < num_atoms; - i ++ ) { - j = (int)nAtomNumber[i]; - if ( nRank[j] != rOld ) { - nNumDiffRanks ++; - rNew = (AT_RANK)(i+1); - rOld = nRank[j]; - } - nSymmRank[j] = rNew; - } - return nNumDiffRanks; -} - -/**************************************************************************************/ -void switch_ptrs( AT_RANK **p1, AT_RANK **p2 ) -{ - AT_RANK *tmp = *p1; - *p1 = *p2; - *p2 = tmp; -} - -/**************************************************************************************/ -/* Set ranks from the products vector and previous ranks */ -/* nRank[] and nNewRank[] should refer to different arrays for now */ -/**************************************************************************************/ -int SetNewRanksFromNeighLists3( int num_atoms, NEIGH_LIST *NeighList, AT_RANK *nRank, - AT_RANK *nNewRank, AT_RANK *nAtomNumber ) -{ - int i, j, nNumDiffRanks, nNumNewRanks; - AT_RANK r1, r2; - /* -- nAtomNumber[] is already properly set -- - for ( i = 0; i < num_atoms; i++ ) { - nAtomNumber[i] = (AT_RANK)i; - } - */ - /* set globals for qsort */ - pNeighList_RankForSort = NeighList; - pn_RankForSort = nRank; - nNumDiffRanks = 0; - nNumNewRanks = 0; - - memset(nNewRank, 0, num_atoms*sizeof(nNewRank[0])); - - /* sorting */ - for ( i = 0, r1 = 1; i < num_atoms; r1++ ) { - if ( r1 == (r2 = nRank[j=(int)nAtomNumber[i]]) ) { - nNewRank[j] = r2; - nNumDiffRanks ++; - i ++; - continue; - } - r1 = r2; - insertions_sort_AT_NUMBERS( nAtomNumber+i, (int)r2-i, CompNeighLists ); - /*insertions_sort( nAtomNumber+i, r2-i, sizeof( nAtomNumber[0] ), CompNeighLists );*/ - j = r2-1; - nNewRank[(int)nAtomNumber[j]] = r2; - nNumDiffRanks ++; - while( j > i ) { - if ( CompareNeighListLex( NeighList[(int)nAtomNumber[j-1]], - NeighList[(int)nAtomNumber[j]], nRank ) ) { - r2 = j; - nNumDiffRanks ++; - nNumNewRanks ++; - } - j --; - nNewRank[(int)nAtomNumber[j]] = r2; - } - i = r1; - } - return nNumNewRanks? -nNumDiffRanks : nNumDiffRanks; -} -/**************************************************************************************/ -/* Set ranks from the products vector and previous ranks */ -/* When comparing neigh lists ignore ranks > max_at_no */ -/* nRank[] and nNewRank[] should refer to different arrays for now */ -/**************************************************************************************/ -int SetNewRanksFromNeighLists4( int num_atoms, NEIGH_LIST *NeighList, AT_RANK *nRank, - AT_RANK *nNewRank, AT_RANK *nAtomNumber, AT_RANK nMaxAtRank ) -{ - int i, j, nNumDiffRanks, nNumNewRanks; - AT_RANK r1, r2; - /* -- nAtomNumber[] is already properly set -- - for ( i = 0; i < num_atoms; i++ ) { - nAtomNumber[i] = (AT_RANK)i; - } - */ - /* set globals for CompNeighListsUpToMaxRank */ - pNeighList_RankForSort = NeighList; - pn_RankForSort = nRank; - nNumDiffRanks = 0; - nNumNewRanks = 0; - nMaxAtNeighRankForSort = nMaxAtRank; - - memset(nNewRank, 0, num_atoms*sizeof(nNewRank[0])); - - /* sorting */ - for ( i = 0, r1 = 1; i < num_atoms; r1++ ) { - if ( r1 == (r2 = nRank[j=(int)nAtomNumber[i]]) ) { - /* non-tied rank: singleton */ - nNewRank[j] = r2; - nNumDiffRanks ++; - i ++; - continue; - } - /* tied rank r2 - r2-i atoms have rank r2 - next atom after them is in position r2 - */ - r1 = r2; - insertions_sort_AT_NUMBERS( nAtomNumber+i, (int)r2-i, CompNeighListsUpToMaxRank ); - /*insertions_sort( nAtomNumber+i, r2-i, sizeof( nAtomNumber[0] ), CompNeighListsUpToMaxRank );*/ - j = r2-1; /* prepare cycle backward, from j to i step -1 */ - nNewRank[(int)nAtomNumber[j]] = r2; - nNumDiffRanks ++; - while( j > i ) { - if ( CompareNeighListLexUpToMaxRank( NeighList[nAtomNumber[j-1]], - NeighList[nAtomNumber[j]], nRank, nMaxAtRank ) ) { - r2 = j; - nNumDiffRanks ++; - nNumNewRanks ++; - } - j --; - nNewRank[(int)nAtomNumber[j]] = r2; - } - i = r1; - } - return nNumNewRanks? -nNumDiffRanks : nNumDiffRanks; -} -/**************************************************************************************/ -/* Set ranks from the products vector and previous ranks */ -/* nRank[] and nNewRank[] should refer to different arrays for now */ -/**************************************************************************************/ -int SetNewRanksFromNeighLists( int num_atoms, NEIGH_LIST *NeighList, AT_RANK *nRank, AT_RANK *nNewRank, - AT_RANK *nAtomNumber, int bUseAltSort, int ( *comp )(const void *, const void *) ) -{ - int i, nNumDiffRanks; - AT_RANK nCurrentRank; - /* -- nAtomNumber[] is already properly set -- - for ( i = 0; i < num_atoms; i++ ) { - nAtomNumber[i] = (AT_RANK)i; - } - */ - /* set globals for qsort */ - pNeighList_RankForSort = NeighList; - pn_RankForSort = nRank; - - /* sorting */ - if ( bUseAltSort & 1 ) - tsort( nAtomNumber, num_atoms, sizeof( nAtomNumber[0] ), comp /*CompNeighListRanksOrd*/ ); - else - qsort( nAtomNumber, num_atoms, sizeof( nAtomNumber[0] ), comp /*CompNeighListRanksOrd*/ ); - - for ( i=num_atoms-1, nCurrentRank=nNewRank[(int)nAtomNumber[i]] = (AT_RANK)num_atoms, nNumDiffRanks = 1; - 0 < i ; - i -- ) { - /* Note: CompNeighListRanks() in following line implicitly reads nRank pointed by pn_RankForSort */ - if ( CompNeighListRanks( &nAtomNumber[i-1], &nAtomNumber[i] ) ) { - nNumDiffRanks ++; - nCurrentRank = (AT_RANK)i; - } - nNewRank[(int)nAtomNumber[i - 1]] = nCurrentRank; - } - - return nNumDiffRanks; -} -/**************************************************************************************/ -/* Sort NeighList[] lists of neighbors according to the ranks of the neighbors */ -/**************************************************************************************/ -void SortNeighListsBySymmAndCanonRank( int num_atoms, NEIGH_LIST *NeighList, const AT_RANK *nSymmRank, const AT_RANK *nCanonRank ) -{ - int i; - for ( i = 0; i < num_atoms; i ++ ) { - insertions_sort_NeighListBySymmAndCanonRank( NeighList[i], nSymmRank, nCanonRank ); - } -} -/**************************************************************************************/ -int SortNeighLists2( int num_atoms, AT_RANK *nRank, NEIGH_LIST *NeighList, AT_RANK *nAtomNumber ) -{ - int k, i; - AT_RANK nPrevRank = 0; - /* - * on entry nRank[nAtomNumber[k]] <= nRank[nAtomNumber[k+1]] ( k < num_atoms-1 ) - * nRank[nAtomNumber[k]] >= k+1 ( k < num_atoms ) - * nRank[nAtomNumber[k]] == k+1 if this nRank value is not tied OR if - * nRank[nAtomNumber[k]] < nRank[nAtomNumber[k+1]] OR if k = num_atoms-1. - * - */ - for ( k = 0; k < num_atoms; k ++ ) { - i = nAtomNumber[k]; - if ( (nRank[i] != k+1 || nRank[i] == nPrevRank) && NeighList[i][0] > 1 ) { - /* nRank[i] is tied (duplicated) */ - insertions_sort_NeighList_AT_NUMBERS( NeighList[i], nRank ); - } - nPrevRank = nRank[i]; - } - return 0; -} -/**************************************************************************************/ -int SortNeighLists3( int num_atoms, AT_RANK *nRank, NEIGH_LIST *NeighList, AT_RANK *nAtomNumber ) -{ - int k, i; - AT_RANK nPrevRank = 0; - /* - * on entry nRank[nAtomNumber[k]] <= nRank[nAtomNumber[k+1]] ( k < num_atoms-1 ) - * nRank[nAtomNumber[k]] >= k+1 ( k < num_atoms ) - * nRank[nAtomNumber[k]] == k+1 if this nRank value is not tied OR if - * nRank[nAtomNumber[k]] < nRank[nAtomNumber[k+1]] OR if k = num_atoms-1. - * - */ - for ( k = 0; k < num_atoms; k ++ ) { - i = nAtomNumber[k]; - if ( (nRank[i] != k+1 || nRank[i] == nPrevRank) && NeighList[i][0] > 1 ) { - /* nRank[i] is tied (duplicated) */ - insertions_sort_NeighList_AT_NUMBERS3( NeighList[i], nRank ); - } - nPrevRank = nRank[i]; - } - return 0; -} -/************************************************************************************** - * - * Differentiate2 - * - * Note: on entry nAtomNumber[] must contain a valid transposition of num_atoms length - * for example, nAtomNumber[i] = i; - * Note2: this version does not calculate neighbor lists for non-tied ranks - */ -int DifferentiateRanks2( int num_atoms, NEIGH_LIST *NeighList, - int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, - AT_RANK *nAtomNumber, long *lNumIter, int bUseAltSort ) -{ - /*int nNumPrevRanks;*/ - - /* SortNeighLists2 needs sorted ranks */ - pn_RankForSort = pnCurrRank; - if ( bUseAltSort & 1 ) - tsort( nAtomNumber, num_atoms, sizeof(nAtomNumber[0]), CompRank /* CompRanksOrd*/ ); - else - qsort( nAtomNumber, num_atoms, sizeof(nAtomNumber[0]), CompRanksOrd ); - - do { - *lNumIter += 1; - /*nNumPrevRanks = nNumCurrRanks;*/ - switch_ptrs( &pnCurrRank, &pnPrevRank ); - SortNeighLists2( num_atoms, pnPrevRank, NeighList, nAtomNumber ); - /* the following call creates pnCurrRank out of pnPrevRank */ - nNumCurrRanks = SetNewRanksFromNeighLists( num_atoms, NeighList, pnPrevRank, pnCurrRank, nAtomNumber, - 1, CompNeighListRanksOrd ); - } while ( /*nNumPrevRanks != nNumCurrRanks ||*/ memcmp( pnPrevRank, pnCurrRank, num_atoms*sizeof(pnCurrRank[0]) ) ); - - return nNumCurrRanks; -} -/************************************************************************************** - * - * Differentiate3 - * - * Note: on entry nAtomNumber[] must contain a valid transposition of num_atoms length - * for example, nAtomNumber[i] = i; - * Note2: this version does not calculate neighbor lists for non-tied ranks - */ -int DifferentiateRanks3( int num_atoms, NEIGH_LIST *NeighList, - int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, - AT_RANK *nAtomNumber, long *lNumIter ) -{ -/* - static long count = 0; - count ++; - if ( count == 103 ) { - int stop=1; - } -*/ - - /* SortNeighLists3 needs sorted ranks: ranks/atnumbers must have been already sorted */ - do { - *lNumIter += 1; - switch_ptrs( &pnCurrRank, &pnPrevRank ); - SortNeighLists3( num_atoms, pnPrevRank, NeighList, nAtomNumber ); - /* the following call creates pnCurrRank out of pnPrevRank */ - nNumCurrRanks = SetNewRanksFromNeighLists3( num_atoms, NeighList, pnPrevRank, - pnCurrRank, nAtomNumber); - } while ( nNumCurrRanks < 0 /* memcmp( pnPrevRank, pnCurrRank, num_atoms*sizeof(pnCurrRank[0]) )*/ ); - - return nNumCurrRanks; -} -/************************************************************************************** - * - * Differentiate4: ignore neighbors with rank > num_atoms - * - * Note: on entry nAtomNumber[] must contain a valid transposition of num_atoms length - * for example, nAtomNumber[i] = i; - * Note2: this version does not sort neighbor lists for non-tied ranks - */ -int DifferentiateRanks4( int num_atoms, NEIGH_LIST *NeighList, - int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, - AT_RANK *nAtomNumber, AT_RANK nMaxAtRank, long *lNumIter ) -{ -/* - static long count = 0; - count ++; - if ( count == 103 ) { - int stop=1; - } -*/ - /* SortNeighLists4 needs sorted ranks: ranks/atnumbers must have been already sorted */ - do { - *lNumIter += 1; - switch_ptrs( &pnCurrRank, &pnPrevRank ); - SortNeighLists3( num_atoms, pnPrevRank, NeighList, nAtomNumber ); - /* the following call creates pnCurrRank out of pnPrevRank */ - nNumCurrRanks = SetNewRanksFromNeighLists4( num_atoms, NeighList, pnPrevRank, - pnCurrRank, nAtomNumber, nMaxAtRank ); - } while ( nNumCurrRanks < 0 /* memcmp( pnPrevRank, pnCurrRank, num_atoms*sizeof(pnCurrRank[0]) )*/ ); - - return nNumCurrRanks; -} -/************************************************************************************** - * - * DifferentiateBasic (sort according to ranks only) - * - * Note: on entry nAtomNumber[] must contain a valid transposition of num_atoms length - * for example, nAtomNumber[i] = i; - * Note2: this version does not calculate neighbor lists for non-tied ranks - */ -int DifferentiateRanksBasic( int num_atoms, NEIGH_LIST *NeighList, - int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, - AT_RANK *nAtomNumber, long *lNumIter, int bUseAltSort ) -{ - int nNumPrevRanks; - - /* SortNeighLists2 needs sorted ranks */ - pn_RankForSort = pnCurrRank; - if ( bUseAltSort & 1 ) - tsort( nAtomNumber, num_atoms, sizeof(nAtomNumber[0]), CompRank ); - else - qsort( nAtomNumber, num_atoms, sizeof(nAtomNumber[0]), CompRank ); - - do { - *lNumIter += 1; - nNumPrevRanks = nNumCurrRanks; - switch_ptrs( &pnCurrRank, &pnPrevRank ); - SortNeighLists2( num_atoms, pnPrevRank, NeighList, nAtomNumber ); - /* the following call creates pnCurrRank out of pnPrevRank */ - nNumCurrRanks = SetNewRanksFromNeighLists( num_atoms, NeighList, pnPrevRank, pnCurrRank, nAtomNumber, bUseAltSort, CompNeighListRanks ); - } while ( nNumPrevRanks != nNumCurrRanks || memcmp( pnPrevRank, pnCurrRank, num_atoms*sizeof(pnCurrRank[0]) ) ); - return nNumCurrRanks; -} - -/************************************************************************************** - * For the purpose of mapping an atom to an atom: - * (a) find number of tied ranks - * (b) if number of tied ranks > 1 then: - * 1) find the rank for breaking a tie - * 2) allocate memory for breaking the tie if it has not been allocated - * 3) find out if atom 1 ("from") has already been mapped - * Return value: - * < 0: error - * = 1: has already been mapped, to tie to break - * > 1: we need to break a tie - */ -int NumberOfTies( AT_RANK **pRankStack1, AT_RANK **pRankStack2, int length, - int at_no1, int at_no2, AT_RANK *nNewRank, int *bAddStack, int *bMapped1 ) -{ - - AT_RANK *nRank1 = *pRankStack1++; - AT_RANK *nAtomNumber1 = *pRankStack1++; /* ranks for mapping "1", "from" */ - - AT_RANK *nRank2 = *pRankStack2++; - AT_RANK *nAtomNumber2 = *pRankStack2++; /* ranks for mapping "2", "to" */ - - AT_RANK r, *pTempArray; - - int iMax, i, i1, i2; - - *bAddStack = 0; - *bMapped1 = 0; - *nNewRank = 0; - r = nRank1[at_no1]; - if ( r != nRank2[at_no2] ) - return CT_MAPCOUNT_ERR; /* atoms cannot be mapped onto each other: they have different ranks */ /* */ - iMax = r - 1; - /* find i1 and i2 = numbers of ranks in nRank1[] and nRank2[] equal to r: */ - for ( i1 = 1; i1 <= iMax && r == nRank1[nAtomNumber1[iMax-i1]]; i1 ++ ) - ; - for ( i2 = 1; i2 <= iMax && r == nRank2[nAtomNumber2[iMax-i2]]; i2 ++ ) - ; - if ( i2 != i1 ) - return CT_MAPCOUNT_ERR; /* program error: must be identical number of equal ranks */ /* */ - /* found i1 equal rank(s); preceding (smaller) non-equal rank is r-i1 */ - /* To break the tie we have to reduce the rank r to r-i1+1 */ - - /************ Note ******************************* - * IF ( i=r-1 && 0 <= i && i < num_atoms AND - * nRank[nAtomNumber1[i]] == r ) - * THEN: - * nRank[nAtomNumber1[i+1]] > r; (if i+1 < num_atoms) - * nRank[nAtomNumber1[i-1]] <= r; (if i > 0) - * - * IF r = nRank[i] THEN - * nRank[nAtomNumber1[r-1]] == r - * nRank[nAtomNumber1[r-i-1]] <= nRank[nAtomNumber1[r-i]] (for 1 <= i < r ) - */ - if ( i1 > 1 ) { - /* int bAtFromHasAlreadyBeenMapped = 0; */ - *nNewRank = r - i1 + 1; - /* grab an existing or allocate a new array */ - /* we need 4 arrays: 2 for ranks + 2 for numbers */ - for ( i = 0; i < 4; i ++ ) { - if ( i < 2 ) { - pTempArray = *pRankStack1; - *bMapped1 += (pTempArray && pTempArray[0]); - } else { - pTempArray = *pRankStack2; - } - if ( !pTempArray && !(pTempArray = (AT_RANK *) inchi_malloc(length))) - return CT_OUT_OF_RAM; /* out of RAM */ /* */ - /* copy "to" contents */ - switch( i ) { - case 2: - memcpy( pTempArray, nRank2, length ); - break; - case 3: - memcpy( pTempArray, nAtomNumber2, length ); - break; - } - if ( i < 2 ) - *pRankStack1 ++ = pTempArray; - else { - *pRankStack2 ++ = pTempArray; - } - } - *bAddStack = 2; /* to break the tie we added 2 more arrays to pRankStack1 and pRankStack2 */ - } - return i1; -} - - -/************************************************************************************** - * - * - * - * Stereo Mappings - * - * - * - **************************************************************************************/ - -/************************************************************************************** - * Parity for a half of a stereo bond. If both halfs have the same parity - * then the bond is "trans" (E,-,1), otherwise it is "cis" (Z,+,2). - * The advantage of this approach is: The bond parity does not depend on the - * rank of the atom located on the opposite end of the stereogenic bond. - * As the result all bond parities of, for example, benzene, can be calculated - * from equivalence ranks only, without any mappings. - * - * Input: at_no1 = number of atom for which the half-bond parity is calculated - * i_sb_neigh = ordering number of the stereo bond in at->stereo_bond_neighbor[] - * - * Returns: 0=> no parity can be found; 1=> odd parity; 2=> even parity - * - */ -int HalfStereoBondParity( sp_ATOM *at, int at_no1, int i_sb_neigh, const AT_RANK *nRank ) -{ -/* - Suppose neighbors #0,#1,#2 have ranks a, b, c. Remove rank of the neighbor connected - by the stereogenic bond (NCSB) from the a, b, c list and denote the two left as r[0], r[1], - in the same order. Let iNCSB be an ordering number (0,1,or 2) of the NCSB. - Assume the neighbor connected by the stereogenic bond has infinite positive rank. - Position the half-bond so that the stereogenic bond neighbor is to the right from the atom (see below) - - Definition. - =========== - if rank(X) != rank(Y) then Half-bond parity = (rank(X) > rank(Y)), that is, - Y - \ if ( rank(X) < rank(Y) ) then Half-bond parity is Even - C==NCSB if ( rank(X) > rank(Y) ) then Half-bond parity is Odd - / if ( rank(X) = rank(Y) ) then Half-bond parity cannot be defined - X - - 1 2 1 - \ \ \ - C==NCSB C==NCSB C==NCSB C==NCSB - / / / - 2 1 1 - - Parity = 1 Parity = 1 Parity = 2 Parity = 2 - (Odd) (Odd) (Even) or 0 (Even) or 0 - - Half-bond parity = (iNCSB + (r[0] > r[1]) + (Atom C geometric parity))%2 - - Consider the following cases to prove the formula: - - Case 1: 3 explicit neighbors - ============================ - If (1) atom's geometric parity = even (which means neighbors #0, #1, #2 are located clockwise), - and (2) neighbors other than NCSB have different ranks, then, - assuming that NCSB always has the largest (infinite) rank (this is consistent with - the assumption that implicit hydrogens have smallest ranks), we have 3 possibilities: - - c a b - \ \ \ - C==a C==b C==c - / / / - b c a - - iNCSB = 0 1 2 - Half-bond parity = b>c ab (0=even, 1=odd) - r[0]>r[1] r[0]r[1] - Half-bond parity - for all 3 cases = (iNCSB + (r[0] > r[1]))%2 - - The following slight modification will work for both odd and even geometric parity: - - Half-bond parity = (iNCSB + (r[0] > r[1]) + (Atom C geometric parity))%2 - - even parity (0) => atom above the bond has lower rank than the atom below the bond. - - - Case 2: 2 explicit neighbors - ============================ - One implicit hydrogen atom H or hydrogen isotope (implicit rank=0). Assume r[1]=0 - - H a Note. The same method - \ \ works for - C==a C==b - / / N==a and a - b H / \ - b N==b - iNCSB = 0 1 - Half-bond parity = b>0 a<0 - (r[1]=0, r[0]>0) r[0]>r[1] r[0] r[1]) + (Atom C geometric parity))%2 - - Case 3: 1 explicit neighbor (NCSB) - ================================== - Two implicit hydrogens, (number of neighbors on non-streogenic bonds)==0: - - Atom C geometric parity: Even Odd Note. The same method - works for - D H - \ \ Even and Odd - C==a C==a - / / H N==a - H D \ / - N==a H - iNCSB = 0 0 - Half-bond parity = (0<0)=0 (0<0)+1 = 1 - (r[1]=0, r[0]=0) r[1] r[1]) + (Atom C geometric parity))%2 - -*/ - int i, j, k, iNeigh, parity, at1_parity, at_no2; - AT_RANK r[MAX_NUM_STEREO_BOND_NEIGH]; - - if ( at[at_no1].valence > MAX_NUM_STEREO_BOND_NEIGH || ( at1_parity = at[at_no1].parity ) <= 0 ) { - return 0; - } - if ( !PARITY_WELL_DEF( at1_parity ) ) { - if ( PARITY_KNOWN( at1_parity ) ) { - return at1_parity; - } - return -at1_parity; - } - if ( 0 > i_sb_neigh || i_sb_neigh >= MAX_NUM_STEREO_BOND_NEIGH ) { - return CT_STEREOBOND_ERROR; /* */ - } - for ( i = 0; i <= i_sb_neigh; i ++ ) { - if ( !at[at_no1].stereo_bond_neighbor[i] ) { - return CT_STEREOBOND_ERROR; /* */ - } - } - at_no2 = at[at_no1].neighbor[(int)at[at_no1].stereo_bond_ord[i_sb_neigh]]; - memset( r, 0, sizeof( r ) ); - for ( i = j = 0, iNeigh = -1; i < at[at_no1].valence; i ++ ) { - if ( (k = (int)at[at_no1].neighbor[i]) == at_no2 ) { - iNeigh = i; - } else { - r[j++] = nRank[k]; - } - } - if ( iNeigh < 0 || iNeigh != at[at_no1].stereo_bond_ord[i_sb_neigh] ) { - return CT_STEREOBOND_ERROR; /* */ - } - if ( j > 0 && !r[0] || j > 1 && !r[1] ) - return 0; /* undefined ranks */ - - if ( j == 2 && r[0] == r[1] || iNeigh < 0 ) { - parity = AB_PARITY_CALC; /* cannot calculate bond parity without additional breaking ties. */ - } else { - parity = 2 - (at[at_no1].parity + iNeigh + (r[1] < r[0])) % 2; - } - return parity; -} -/**************************************************************************************/ -int parity_of_mapped_half_bond( int from_at, int to_at, int from_neigh, int to_neigh, - sp_ATOM *at, EQ_NEIGH *pEN, - const AT_RANK *nCanonRankFrom, const AT_RANK *nRankFrom, const AT_RANK *nRankTo ) -{ - int i, j, k, num_neigh; - int to_sb_neigh_ord, from_sb_neigh_ord, parity; - AT_RANK r_to[MAX_NUM_STEREO_BOND_NEIGH], at_no_to[MAX_NUM_STEREO_BOND_NEIGH]; - AT_RANK r_canon_from[MAX_NUM_STEREO_BOND_NEIGH], at_no_from[MAX_NUM_STEREO_BOND_NEIGH]; - AT_RANK r, r_sb_neigh; - - for ( i = 0; i < MAX_NUM_STEREO_BOND_NEIGH; i ++ ) { - r_to[i] = r_canon_from[i] = 0; - } - - if ( pEN ) { - memset( pEN, 0, sizeof(*pEN)); - } - - /* for debug only */ - if ( nRankFrom[from_at] != nRankTo[to_at] || - nRankFrom[from_neigh] != nRankTo[to_neigh] || - at[to_at].valence != at[from_at].valence ) { - return 0; /* program error: both atoms must be mapped */ /* */ - } - - parity = PARITY_VAL(at[to_at].parity); - num_neigh = at[to_at].valence; - - if ( num_neigh > MAX_NUM_STEREO_BOND_NEIGH || num_neigh < MIN_NUM_STEREO_BOND_NEIGH ) { - /* 2 neighbors are possible in case of stereo bond with implicit H */ - /* or a stereocenter -CHD- with an implicit H */ - if ( num_neigh == 1 && at[to_at].stereo_bond_neighbor[0] ) { - /* 1 neighbor can happen in case of a terminal =CHD */ - if ( PARITY_WELL_DEF(parity) ) - return 2 - parity % 2; - else - if ( parity ) - return parity; - else - return AB_PARITY_UNDF; /* undefined parity */ - } - return 0; /* program error */ /* */ - } - if ( ATOM_PARITY_KNOWN(parity) ) { - if ( !ATOM_PARITY_WELL_DEF(parity) ) - return parity; - } else - if ( parity ) { - return 0; /* parity; */ - } else { - return 0; /* AB_PARITY_UNDF; */ /* possibly program error: undefined parity */ - } - /* locate at[to_at].stereo_bond_neighbor[] ordering numbers */ - for ( i = 0, to_sb_neigh_ord=-1; i < MAX_NUM_STEREO_BONDS && (k=(int)at[to_at].stereo_bond_neighbor[i]); i ++ ) { - if ( k == to_neigh+1 ) { - to_sb_neigh_ord = i; - break; - } - } - if ( to_sb_neigh_ord < 0 ) { - return 0; /* program error: not a stereo bond */ /* */ - } - to_sb_neigh_ord = (int)at[to_at].stereo_bond_ord[to_sb_neigh_ord]; - r_sb_neigh = nRankTo[(int)at[to_at].neighbor[to_sb_neigh_ord]]; - for ( i = j = 0; i < num_neigh; i ++ ) { - if ( i != to_sb_neigh_ord ) { - r_to[j] = nRankTo[(int)(at_no_to[j]=at[to_at].neighbor[i])]; - if ( r_sb_neigh == r_to[j] ) { - return 0; /* stereo bond atoms are not fully mapped */ - } - j ++; - } - } - if ( j+1 != num_neigh ) { - return 0; /* program error */ /* */ - } - if ( j == 1 ) { - /* only one neighbor; no mapping needed */ - return 2-(parity+1+to_sb_neigh_ord)%2; - } - if ( j != 2 ) { - return 0; /* program error: j can be only 0, 1, or 2 */ /* */ - } - - if ( r_to[0] == r_to[1] ) { - /* double bond neighbors need to be mapped */ - j = 0; - from_sb_neigh_ord = -1; - for ( i = 0; i < num_neigh; i ++ ) { - k = at[from_at].neighbor[i]; - r = nRankFrom[k]; - if ( r == r_sb_neigh ) { - from_sb_neigh_ord = i; /* we need this value only for error-checking */ - } else - if ( r == r_to[0] ) { - r_canon_from[j] = nCanonRankFrom[k]; - at_no_from[j] = (AT_RANK)k; - j ++; - } else { - return 0; /* program error: unexpected rank, not fully mapped adjacent to the stereo bond atoms */ /* */ - } - } - if ( from_sb_neigh_ord < 0 || j != 2 ) { - return 0; /* program error: rank of a neighbor not found */ /* */ - } - if ( pEN ) { /* j == 2 */ - pEN->to_at[0] = at_no_to[0]; - pEN->to_at[1] = at_no_to[1]; - pEN->num_to = 2; /* number of stored in pEN->to_at[] central atom neighbors */ - pEN->rank = r_to[0]; /* mapping rank of the tied neighbors */ - /* i := index of the smaller out of r_canon_from[1] and r_canon_from[0] */ - i = (r_canon_from[1] < r_canon_from[0]); - pEN->from_at = at_no_from[i]; - pEN->canon_rank = r_canon_from[i]; - } - return -((int)r_to[0]); - } - /* double bond neighbors a mapped: r_to[0] != r_to[1] */ - from_sb_neigh_ord = -1; - for ( i = 0; i < num_neigh; i ++ ) { - k = at[from_at].neighbor[i]; - r = nRankFrom[k]; - if ( r == r_sb_neigh ) { - from_sb_neigh_ord = i; /* we need this value only for error-checking */ - } else - if ( r == r_to[0] ) { - r_canon_from[0] = nCanonRankFrom[k]; - /* at_no_from[0] = (AT_RANK)k; */ - } else - if ( r == r_to[1] ) { - r_canon_from[1] = nCanonRankFrom[k]; - /* at_no_from[1] = (AT_RANK)k; */ - } else { - return 0; /* program error: unexpected rank, not fully mapped adjacent to the stereo bond atoms */ /* */ - } - } - if ( !r_canon_from[0] || !r_canon_from[1] || from_sb_neigh_ord < 0 ) { - return 0; /* program error: neighbor rank not found */ /* */ - } - return 2 - (parity + to_sb_neigh_ord + (r_canon_from[1] */ - if ( num_neigh > MAX_NUM_STEREO_ATOM_NEIGH || num_neigh < 2 ) { - /* 2 neighbors are possible in case of stereo bond with implicit H */ - /* or a stereocenter >CHD with two implicit H */ - if ( num_neigh == 1 ) { - /* 1 neighbor can happen in case of a terminal -CHDT or =CHD */ - if ( at[to_at].parity ) - return at[to_at].parity; - else - return AB_PARITY_UNDF; /* undefined parity */ - } - return 0; /* program error */ /* */ - } - for ( i = 0; i < num_neigh; i ++ ) { /* initialization of locals */ - nNeighNumberTo[i] = - nNeighNumberFrom[i] = i; - nNeighRankTo[i] = nRankTo[(int)at[to_at].neighbor[i]]; /* mapping rank */ - nNeighRankFrom[i] = nRankFrom[j=(int)at[from_at].neighbor[i]]; /* mapping rank */ - nNeighRankFromCanon[i] = nCanonRankFrom[j]; /* canonical number */ - } - - pn_RankForSort = nNeighRankFrom; - nNumCompNeighborsRanksCountEql = 0; /* sort mapping ranks-from */ - num_trans_from = insertions_sort( nNeighNumberFrom, num_neigh, sizeof(nNeighNumberFrom[0]), CompNeighborsRanksCountEql ); - - if ( nNumCompNeighborsRanksCountEql ) { - /* At least 2 neighbors have equal mapping ranks (are tied). */ - /* Find tied from-neighbors with minimal canonical rank (nCanonRankFrom[]) */ - r_canon_from_min = MAX_ATOMS+1; /* max possible rank + 1 */ - for ( i = 1, r = 0, r1 = nNeighRankFrom[neigh1=nNeighNumberFrom[0]]; i < num_neigh; i ++, r1 = r2, neigh1 = neigh2 ) { - r2 = nNeighRankFrom[neigh2=nNeighNumberFrom[i]]; - if ( r2 == r1 ) { - /* found neighbors with tied ranks */ - if ( r != r2 ) { - /* the 1st pair of neighbor with this rank */ - r = r2; - if ( (r_canon_from=nNeighRankFromCanon[neigh1]) < r_canon_from_min ) { - r_canon_from_min = r_canon_from; /* min canon rank */ - neigh_canon_from_min = neigh1; /* neighbor number */ - } - } - if ( (r_canon_from=nNeighRankFromCanon[neigh2]) < r_canon_from_min ) { - r_canon_from_min = r_canon_from; - neigh_canon_from_min = neigh2; - } - } - } - if ( r ) { - /* neighbors with tied ranks have been found => parity cannot be determined without additional mapping */ - /* find to-neighbors on which neigh_canon_from_min can be mapped */ - r1 = nNeighRankFrom[neigh_canon_from_min]; - if ( pEN ) { - for ( i = j = 0; i < num_neigh; i ++ ) { - if ( r1 == nNeighRankTo[i] ) { - pEN->to_at[j++] = at[to_at].neighbor[i]; - } - } - insertions_sort( pEN->to_at, j, sizeof(pEN->to_at[0]), CompRanksInvOrd ); - pEN->num_to = j; /* number of stored in pEN->to_at[] central atom neighbors */ - pEN->from_at = at[from_at].neighbor[neigh_canon_from_min]; /* neighbor with min. canon number */ - pEN->rank = r1; /* mapping rank of the tied neighbors */ - pEN->canon_rank = r_canon_from_min; /* canon. rank of the pEN->from_at */ - } else { - /* debug only */ - for ( i = j = 0; i < num_neigh; i ++ ) { - if ( r1 == nNeighRankTo[i] ) { - j++; - } - } - } - /* debug only */ - if ( j <= 1 || !r1 || r_canon_from_min > MAX_ATOMS ) { - return 0; /* program error */ /* */ - } - return -r; /* means parity cannot be determined */ - } - return 0; /* program error */ - } - /* All neighbors have different mapping ranks; */ - /* therefore no additional mapping of the neighbors is necessary */ - if ( !ATOM_PARITY_WELL_DEF(at[to_at].parity) ) - return at[to_at].parity; /* unknown parity or cannot be determined */ - - pn_RankForSort = nNeighRankTo; - num_trans_to = insertions_sort( nNeighNumberTo, num_neigh, sizeof(nNeighNumberTo[0]), CompNeighborsRanksCountEql ); - - /* Map canonical ranks of neighbors. Mapped on each other "to" and "from" atoms have equal mapping ranks */ - for ( i = 0; i < num_neigh; i ++ ) { - if ( nNeighRankTo[j=nNeighNumberTo[i]] != nNeighRankFrom[k=nNeighNumberFrom[i]] ) - return 0; /* program error: mapping ranks not equal, from_at neigborhood cannot be mapped on to_at neighbood. */ /* */ - nNeighRankToCanon[j] = nNeighRankFromCanon[k]; /* potential problem: other atom(s) may have same mapping rank and */ - /* different canon. rank(s). */ - /* we may save some memory by eliminating nNeighRankFromCanon[]: */ - /* nNeighRankToCanon[j] = nCanonRankFrom[at[from_at].neighbor[k]] */ - } - - pn_RankForSort = nNeighRankToCanon; - num_trans_to += insertions_sort( nNeighNumberTo, num_neigh, sizeof(nNeighNumberTo[0]), CompNeighborsRanksCountEql ); -#ifndef CT_NEIGH_INCREASE - num_trans_to += ((num_neigh*(num_neigh-1))/2)%2; /* get correct parity for ascending order of canon. numbers */ -#endif - - return 2 - (num_trans_to + at[to_at].parity)%2; -} - -/************************************************************************************** - * - * Phase II: map canonicaly numbrered structure onto itself - * to obtain a minimal or maximal stereo part of the CT - * - **************************************************************************************/ - -int ClearPreviousMappings( AT_RANK **pRankStack1 ) -{ - int i; - for ( i = 0; pRankStack1[i]; i ++ ) { - pRankStack1[i][0] = 0; - } - return i; - -} -/**************************************************************************************/ -/* map one atom ("from") onto another ("to"): untie their mapping ranks if they are tied. */ -int map_an_atom2( int num_atoms, int num_max, int at_no1/*from*/, int at_no2/*to*/, - AT_RANK *nTempRank, - int nNumMappedRanks, int *pnNewNumMappedRanks, - CANON_STAT *pCS, - NEIGH_LIST *NeighList, - AT_RANK **pRankStack1, AT_RANK **pRankStack2, int *bAddStack ) -{ - AT_RANK *nRank1, *nAtomNumber1; /* ranks for mapping "1", "from" */ - AT_RANK *nRank2, *nAtomNumber2; /* ranks for mapping "2", "to" */ - AT_RANK *nNewRank1=NULL, *nNewAtomNumber1=NULL; /* ranks for mapping "1", "from" */ - AT_RANK *nNewRank2=NULL, *nNewAtomNumber2=NULL; /* ranks for mapping "2", "to" */ - int length = num_max*sizeof(AT_RANK); - int nNewNumRanks2, nNewNumRanks1; - int i, bAtFromHasAlreadyBeenMapped, nNumTies; - AT_RANK nNewRank; - - nNumTies = NumberOfTies( pRankStack1, pRankStack2, length, at_no1, at_no2, &nNewRank, bAddStack, &bAtFromHasAlreadyBeenMapped ); - - if ( RETURNED_ERROR(nNumTies) ) - return nNumTies; /* error */ - - nRank1 = *pRankStack1++; - nAtomNumber1 = *pRankStack1++; /* ranks for mapping "1", "from" */ - - nRank2 = *pRankStack2++; - nAtomNumber2 = *pRankStack2++; /* ranks for mapping "2", "to" */ - - if ( nNumTies > 1 ) { - - nNewRank1 = *pRankStack1++; - nNewAtomNumber1 = *pRankStack1++; /* ranks for mapping "1", "from" */ - - nNewRank2 = *pRankStack2++; - nNewAtomNumber2 = *pRankStack2++; /* ranks for mapping "2", "to" */ - /* break a tie for "to" */ - memcpy( nNewRank2, nRank2, length ); - memcpy( nNewAtomNumber2, nAtomNumber2, length ); - nNewRank2[at_no2] = nNewRank; - nNewNumRanks2 = DifferentiateRanks2( num_atoms, NeighList, - nNumMappedRanks, nNewRank2, nTempRank, - nNewAtomNumber2, &pCS->lNumNeighListIter, 1 ); - pCS->lNumBreakTies ++; - - /* Check whether the old mapping can be reused */ - if ( 2 == bAtFromHasAlreadyBeenMapped && nNewRank == nNewRank1[at_no1] ) { - for ( i = 0; i < num_atoms; i ++ ) { - if ( nNewRank1[nNewAtomNumber1[i]] != nNewRank2[nNewAtomNumber2[i]] ) { - bAtFromHasAlreadyBeenMapped = 0; /* It cannot. */ - break; - } - } - } else { - bAtFromHasAlreadyBeenMapped = 0; - } - if ( 2 != bAtFromHasAlreadyBeenMapped ) { - /* break a tie for "from" */ - for ( i = 0; pRankStack1[i]; i ++ ) { - pRankStack1[i][0] = 0; - } - memcpy( nNewRank1, nRank1, length ); - memcpy( nNewAtomNumber1, nAtomNumber1, length ); /* GPF: bad nAtomNumber1 */ - nNewRank1[at_no1] = nNewRank; - nNewNumRanks1 = DifferentiateRanks2( num_atoms, NeighList, - nNumMappedRanks, nNewRank1, nTempRank, - nNewAtomNumber1, &pCS->lNumNeighListIter, 1 ); - pCS->lNumBreakTies ++; - } else { - nNewNumRanks1 = nNewNumRanks2; - } - - if ( nNewNumRanks1 != nNewNumRanks2 ) - return CT_MAPCOUNT_ERR; /* program error */ /* */ - *pnNewNumMappedRanks = nNewNumRanks2; - /* debug only */ - for ( i = 0; i < num_atoms; i ++ ) { - if ( nNewRank1[nNewAtomNumber1[i]] != nNewRank2[nNewAtomNumber2[i]] ) { - return CT_MAPCOUNT_ERR; /* program error */ /* */ - } - } - } else { - *pnNewNumMappedRanks = nNumMappedRanks; - } - return ( nNewRank1 )? nNewRank1[at_no1] : nRank1[at_no1]; /* mapping rank value */ -} - -/**************************************************************************************/ -int might_change_other_atom_parity( sp_ATOM *at, int num_atoms, int at_no, AT_RANK *nRank2, AT_RANK *nRank1 ) -{ - int i, j, neighbor_no; - for ( i = 0; i < num_atoms; i ++ ) { - if ( nRank2[i] != nRank1[i] ) { - if ( i != at_no /*&& ATOM_PARITY_WELL_DEF(at[i].parity)*/ - && at[i].bHasStereoOrEquToStereo - && !(at[i].stereo_atom_parity & KNOWN_PARITIES_EQL ) - && !at[i].stereo_bond_neighbor[0] - ) { - - return 1; /* may have changed stereo atoms order */ - } - for ( j = 0; j < at[i].valence; j ++ ) { - neighbor_no = at[i].neighbor[j]; - if ( neighbor_no != at_no - /*&& ATOM_PARITY_WELL_DEF(at[neighbor_no].parity)*/ - && at[neighbor_no].bHasStereoOrEquToStereo - && !(at[neighbor_no].stereo_atom_parity & KNOWN_PARITIES_EQL ) - && !at[neighbor_no].stereo_bond_neighbor[0] - ) - return 1; /* may have changed stereo atom parity */ - } - } - } - return 0; -} -/**************************************************************************************/ -#if ( REMOVE_CALC_NONSTEREO == 1 ) /* { */ -/**************************************************************************************/ -void DeAllocateForNonStereoRemoval( AT_RANK **nAtomNumberCanon1, AT_RANK **nAtomNumberCanon2, - NEIGH_LIST **nl, NEIGH_LIST **nl1, NEIGH_LIST **nl2, AT_RANK **nVisited1, AT_RANK **nVisited2 ) -{ - if ( *nAtomNumberCanon1 ) { - inchi_free( *nAtomNumberCanon1 ); - *nAtomNumberCanon1 = NULL; - } - if ( *nAtomNumberCanon2 ) { - inchi_free( *nAtomNumberCanon2 ); - *nAtomNumberCanon2 = NULL; - } - if ( *nl ) { - FreeNeighList( *nl ); - *nl = 0; - } - if ( *nl1 ) { - FreeNeighList( *nl1 ); - *nl1 = 0; - } - if ( *nl2 ) { - FreeNeighList( *nl2 ); - *nl2 = 0; - } - if ( *nVisited1 ) { - inchi_free( *nVisited1 ); - *nVisited1 = NULL; - } - if ( *nVisited2 ) { - inchi_free( *nVisited2 ); - *nVisited2 = NULL; - } - -} -/**************************************************************************************/ -int AllocateForNonStereoRemoval( sp_ATOM *at, int num_atoms, const AT_RANK *nSymmRank, AT_RANK *nCanonRank, - AT_RANK **nAtomNumberCanon1, AT_RANK **nAtomNumberCanon2, - NEIGH_LIST **nl, NEIGH_LIST **nl1, NEIGH_LIST **nl2, AT_RANK **nVisited1, AT_RANK **nVisited2 ) -{ - DeAllocateForNonStereoRemoval( nAtomNumberCanon1, nAtomNumberCanon2, nl, nl1, nl2, nVisited1, nVisited2 ); - *nAtomNumberCanon1 = (AT_RANK *) inchi_malloc( num_atoms * sizeof(**nAtomNumberCanon1) ); - *nAtomNumberCanon2 = (AT_RANK *) inchi_malloc( num_atoms * sizeof(**nAtomNumberCanon2) ); - *nl = CreateNeighList( num_atoms, num_atoms, at, 0, NULL ); - *nl1 = CreateNeighList( num_atoms, num_atoms, at, 0, NULL ); - *nl2 = CreateNeighList( num_atoms, num_atoms, at, 0, NULL ); - *nVisited1 = (AT_RANK *) inchi_malloc( num_atoms * sizeof(**nVisited1) ); - *nVisited2 = (AT_RANK *) inchi_malloc( num_atoms * sizeof(**nVisited2) ); - - if ( !*nl || !*nl1 || !*nl2 || !*nVisited1 || !*nVisited2 || !*nAtomNumberCanon1 || !*nAtomNumberCanon2 ) { - DeAllocateForNonStereoRemoval( nAtomNumberCanon1, nAtomNumberCanon2, nl, nl1, nl2, nVisited1, nVisited2 ); - return 0; - } - /* Sort neighbors according to symm. ranks (primary key) and canon. ranks (secondary key), in descending order */ - SortNeighListsBySymmAndCanonRank( num_atoms, *nl, nSymmRank, nCanonRank ); - SortNeighListsBySymmAndCanonRank( num_atoms, *nl1, nSymmRank, nCanonRank ); - SortNeighListsBySymmAndCanonRank( num_atoms, *nl2, nSymmRank, nCanonRank ); - return 1; -} -/**************************************************************************************/ -AT_RANK GetMinNewRank(AT_RANK *nAtomRank, AT_RANK *nAtomNumb, AT_RANK nRank1 ) -{ - int i; - AT_RANK nRank2; - for ( i = (int)nRank1-1; 0 <= i && nRank1 == (nRank2 = nAtomRank[(int)nAtomNumb[i]]); i -- ) - ; - if ( i >= 0 ) - nRank2 ++; - else - nRank2 = 1; - return nRank2; -} -/**************************************************************************************/ -int BreakNeighborsTie( sp_ATOM *at, int num_atoms, int num_at_tg, int ib, int ia, - AT_RANK *neigh_num, int in1, int in2, int mode, - AT_RANK **pRankStack1, AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, - const AT_RANK *nSymmRank, AT_RANK *nCanonRank, NEIGH_LIST *nl1, NEIGH_LIST *nl2, long *lNumIter ) -{ - AT_RANK nRank1, nRank2; - int nNumDiffRanks, nNumDiffRanks1, nNumDiffRanks2, i; - int n1 = (int)neigh_num[in1]; - int n2 = (int)neigh_num[in2]; - int other_neigh[2], other_neig_ord[2], num_other_neigh; - /* asymmetric calculation */ - - if ( mode == MAP_MODE_S4 && in1 || /* for S4 we need only (in1,in2) = (0,1) (0,2) (0,3) pairs of neighbors */ - mode != MAP_MODE_STD && at[ia].valence != MAX_NUM_STEREO_ATOM_NEIGH || - mode != MAP_MODE_STD && nSymmRank[n1] != nSymmRank[n2] ) { - return 0; - } - /* 1. Create initial ranks from equivalence information stored in nSymmRank */ - memcpy( pRankStack1[0], nSymmRank, num_at_tg * sizeof(pRankStack1[0][0]) ); - pn_RankForSort = pRankStack1[0]; - tsort( pRankStack1[1], num_at_tg, sizeof(pRankStack1[1][0]), CompRanksOrd ); - nNumDiffRanks = SortedEquInfoToRanks( pRankStack1[0]/*inp*/, pRankStack1[0]/*out*/, pRankStack1[1], num_at_tg, NULL ); - - /* other neighbors */ - num_other_neigh = 0; - if ( at[ia].valence <= MAX_NUM_STEREO_ATOM_NEIGH && mode ) { - for ( i = 0; i < at[ia].valence; i ++ ) { - if ( i != in1 && i != in2 ) { - other_neigh[num_other_neigh] = (int)neigh_num[i]; - other_neig_ord[num_other_neigh] = i; - num_other_neigh ++; - } - } - } - if ( mode != MAP_MODE_STD && nSymmRank[other_neigh[0]] != nSymmRank[other_neigh[1]] || - mode == MAP_MODE_S4 && nSymmRank[n1] != nSymmRank[other_neigh[1]] ) { - return 0; - } - - /* 2. Fix at[ia] */ - if ( pRankStack1[0][ia] != nSymmRank[ia] ) { - /* at[ia] is constitutionally equivalent to some other atom. Fix at[ia]. */ - pRankStack1[0][ia] = nSymmRank[ia]; - nNumDiffRanks = DifferentiateRanksBasic( num_at_tg, NeighList, - nNumDiffRanks, pRankStack1[0], nTempRank, - pRankStack1[1], lNumIter, 1 ); - } - /* 3. In case of a double bond/cumulene only: */ - /* fix at[ib] -- the opposite double bond/cumulene atom */ - if ( ib < num_atoms ) { - /* find the smallest possible rank */ - nRank1 = pRankStack1[0][ib]; - nRank2 = GetMinNewRank(pRankStack1[0], pRankStack1[1], nRank1 ); - /* if the rank is smaller than pRankStack1[0][ib] then fix at[ib] */ - if ( nRank2 != nRank1 ) { - pRankStack1[0][ib] = nRank2; - nNumDiffRanks = DifferentiateRanksBasic( num_at_tg, NeighList, - nNumDiffRanks, pRankStack1[0], nTempRank, - pRankStack1[1], lNumIter, 1 ); - } - } - - /************************************************************************************** - * Note: It may (or may not?) make sense to fix "other neighbors": - * in case of a stereo center fix neighbors other than n1, n2 - * in case of a double bond/cumulene fix the opposite atom neighbors - * The ranks assigned to the other neighbors in case of their equivalence - * should be in the ascending order of their canonical ranks ???? - * *** For now we do not fix other neighbors *** - **************************************************************************************/ - - /* 4. Check whether the neighbors still have equal ranks */ - if ( pRankStack1[0][n1] != pRankStack1[0][n2] ) { - return 0; /* the two neighbors are not constitutionally equivalent */ - } - /* 5. Find new smallest possible rank for n1 and n2 */ - nRank1 = pRankStack1[0][n1]; - nRank2 = GetMinNewRank(pRankStack1[0], pRankStack1[1], nRank1 ); - - /* 6. Copy the results to the 2nd eq. rank arrays */ - memcpy( pRankStack2[0], pRankStack1[0], num_at_tg * sizeof(pRankStack2[0][0]) ); - memcpy( pRankStack2[1], pRankStack1[1], num_at_tg * sizeof(pRankStack2[0][0]) ); - - /* 7. Break neighbor tie: map n1(1) <--> n2(2) */ - pRankStack1[0][n1] = nRank2; - nNumDiffRanks1 = DifferentiateRanksBasic( num_at_tg, NeighList, - nNumDiffRanks, pRankStack1[0], nTempRank, - pRankStack1[1], lNumIter, 1 ); - - pRankStack2[0][n2] = nRank2; - nNumDiffRanks2 = DifferentiateRanksBasic( num_at_tg, NeighList, - nNumDiffRanks, pRankStack2[0], nTempRank, - pRankStack2[1], lNumIter, 1 ); - - if ( nNumDiffRanks1 != nNumDiffRanks2 ) { - return -1; /* */ - } - if ( mode == MAP_MODE_C2v || mode == MAP_MODE_C2 ) { - /* Check for C2v reflection leading to parity inversion (mode=1) or C2 rotation (mode=2) */ - AT_RANK nRank10, nRank20; - int nn1, nn2; - /* - * C2v & C2: map - * n1(1) <--> n2(2) -- at this point already done - * n1(2) <--> n2(1) --> do at i = 0 - * - * C2v: other neighbors must be unmoved: map - * other_neigh[0](1) <--> other_neigh[0](2) - * other_neigh[1](1) <--> other_neigh[1](2) - * - * C2: other neighbors should be mapped on each other - * other_neigh[0](1) <--> other_neigh[1](2) - * other_neigh[1](1) <--> other_neigh[0](2) - */ - for ( i = 0; i <= 2; i ++ ) { - if ( i == 0 ) { - /* C2v & C2. Map n2(1) <--> n1(2) */ - nn1 = n2; - nn2 = n1; - } else - if ( mode == MAP_MODE_C2v ) { /* was '=', pointed by WDI */ - /* i = 1 or 2 - * C2v. Other neighbors must be unmoved: map - * i=1: other_neigh[0](1) <--> other_neigh[0](2) - * i=2: other_neigh[1](1) <--> other_neigh[1](2) - */ - nn1 = other_neigh[i-1]; /* 0 or 1 */ - nn2 = other_neigh[i-1]; /* 0 or 1 */ - } else - if ( mode == MAP_MODE_C2 ) { /* was '=', pointed by WDI */ - /* i = 1 or 2 - * C2. Other neighbors should be mapped on each other - * i=1: other_neigh[0](1) <--> other_neigh[1](2) - * i=2: other_neigh[1](1) <--> other_neigh[0](2) - */ - nn1 = other_neigh[i-1]; /* 0 or 1 */ - nn2 = other_neigh[2-i]; /* 1 or 0 */ - } else { - return -1; /* program error */ - } - /* map nn1(1) <--> nn2(2) */ - nRank10 = pRankStack1[0][nn1]; - nRank20 = pRankStack2[0][nn2]; - nRank1 = GetMinNewRank(pRankStack1[0], pRankStack1[1], nRank10 ); - nRank2 = GetMinNewRank(pRankStack2[0], pRankStack2[1], nRank20 ); - if ( nRank10 == nRank20 && nRank1 == nRank2 ) { - if ( nRank10 == nRank1 ) { - ;/* atoms are already mapped */ - } else { - /* need additional mapping: ranks are not fixed yet */ - pRankStack1[0][nn1] = nRank1; - nNumDiffRanks1 = DifferentiateRanksBasic( num_at_tg, NeighList, - nNumDiffRanks, pRankStack1[0], nTempRank, - pRankStack1[1], lNumIter, 1 ); - pRankStack2[0][nn2] = nRank2; - nNumDiffRanks2 = DifferentiateRanksBasic( num_at_tg, NeighList, - nNumDiffRanks, pRankStack2[0], nTempRank, - pRankStack2[1], lNumIter, 1 ); - if ( nNumDiffRanks1 != nNumDiffRanks2 ) { - return -1; /* */ - } - } - } else { - return 0; /* mapping is not possible */ - } - } - } - if ( mode == MAP_MODE_S4 ) { - /* - * Check for S4 reflection/rotation leading to parity inversion (mode=3) - * - * At this point n1(1) <--> n2(2) have been mapped and n1 has index in1 = 0 - * Below indexes in neigh_num[] are in brackets; [i] means neigh_num[i]. - * Numbers (#) in parentheses refer to pRankStack# - * - * in2=1: [0](1) <--> [1](2) mapping has been done; add more mappings: - * [1](1) <--> [2](2) [x]=[2] - * [2](1) <--> [3](2) [y]=[3] - * [3](1) <--> [0](2) - * this will succeed if C2 axis crosses middle of [0]-[2] and [1]-[3] lines - * - * in2=2: [0](1) <--> [2](2) mapping has been done; add more mappings: - * [2](1) <--> [3](2) [x]=[3] - * [3](1) <--> [1](2) [y]=[1] - * [1](1) <--> [0](2) - * this will succeed if C2 axis crosses middle of [0]-[3] and [1]-[2] lines - * - * in2=3: [0](1) <--> [3](2) mapping has been done; add more mappings: - * [3](1) <--> [1](2) [x]=[1] - * [1](1) <--> [2](2) [y]=[2] - * [2](1) <--> [0](2) - * this will succeed if C2 axis crosses middle of [0]-[1] and [2]-[3] lines - * - * In general: - * [in1](1) <--> [in2](2) - * [in2](1) <--> [x] (2) i=0 - * [x] (1) <--> [y] (2) i=1 - * [y] (1) <--> [in1](2) i=2 - * - * in1=0 always - * ===== how to find x, y from in2 ==== - * in2=1 => x,y = 2, 3 or [x] = other_neigh[0], [y] = other_neigh[1] - * in2=2 => x,y = 3, 1 or [x] = other_neigh[1], [y] = other_neigh[0] - * in2=3 => x,y = 1, 2 or [x] = other_neigh[0], [y] = other_neigh[1] - * ==================================== - */ - AT_RANK nRank10, nRank20; - int nn1, nn2; - for ( i = 0; i <= 2; i ++ ) { - switch( i ) { - case 0: /* [in2](1) <--> [x](2); */ - nn1 = n2; /* [in2] */ - nn2 = other_neigh[1-in2%2]; /* [x] */ - break; - case 1: /* [x](1) <--> [y](2) */ - nn1 = other_neigh[1-in2%2]; /* [x] */ - nn2 = other_neigh[ in2%2]; /* [y] */ - break; - case 2: - nn1 = other_neigh[ in2%2]; /* [y] */ - nn2 = n1; /* [in1] */ - break; - default: - return -1; /* program error */ - } - /* map nn1(1) <--> nn2(2) */ - nRank10 = pRankStack1[0][nn1]; - nRank20 = pRankStack2[0][nn2]; - nRank1 = GetMinNewRank(pRankStack1[0], pRankStack1[1], nRank10 ); - nRank2 = GetMinNewRank(pRankStack2[0], pRankStack2[1], nRank20 ); - if ( nRank10 == nRank20 && nRank1 == nRank2 ) { - if ( nRank10 == nRank1 ) { - ;/* atoms are already mapped */ - } else { - /* need additional mapping: ranks are not fixed yet */ - pRankStack1[0][nn1] = nRank1; - nNumDiffRanks1 = DifferentiateRanksBasic( num_at_tg, NeighList, - nNumDiffRanks, pRankStack1[0], nTempRank, - pRankStack1[1], lNumIter, 1 ); - pRankStack2[0][nn2] = nRank2; - nNumDiffRanks2 = DifferentiateRanksBasic( num_at_tg, NeighList, - nNumDiffRanks, pRankStack2[0], nTempRank, - pRankStack2[1], lNumIter, 1 ); - if ( nNumDiffRanks1 != nNumDiffRanks2 ) { - return -1; /* */ - } - } - } else { - return 0; /* mapping is not possible */ - } - } - } - - - -#if ( BREAK_ONE_MORE_SC_TIE == 1 ) /* { */ - /* Check for a very highly symmetrical stereo center 12-06-2002 */ - if ( ib >= num_atoms && at[ia].valence == MAX_NUM_STEREO_ATOM_NEIGH ) { - int num_eq; - nRank1 = pRankStack1[0][n2]; - for ( i = 0, num_eq = 0; i < at[ia].valence; i ++ ) { - num_eq += ( nRank1 == pRankStack1[0][at[ia].neighbor[i]]); - } - if ( num_eq == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { - for ( i = (int)nRank1-1; 0 <= i && nRank1 == (nRank2 = pRankStack1[0][(int)pRankStack1[1][i]]); i -- ) - ; - if ( i >= 0 ) - nRank2 ++; - else - nRank2 = 1; - - /* 7a. Break another neighbor tie */ - - nNumDiffRanks = nNumDiffRanks1; - - pRankStack1[0][n2] = nRank2; - nNumDiffRanks1 = DifferentiateRanksBasic( num_at_tg, NeighList, - nNumDiffRanks, pRankStack1[0], nTempRank, - pRankStack1[1], lNumIter, 1 ); - - pRankStack2[0][n1] = nRank2; - nNumDiffRanks2 = DifferentiateRanksBasic( num_at_tg, NeighList, - nNumDiffRanks, pRankStack2[0], nTempRank, - pRankStack2[1], lNumIter, 1 ); - } - } - - if ( nNumDiffRanks1 != nNumDiffRanks2 ) { - return -1; /* */ - } -#endif /* } BREAK_ONE_MORE_SC_TIE */ - -#if ( BREAK_ALSO_NEIGH_TIE == 1 ) - /* check whether neighbor's neighbors are tied and untie them */ - if ( at[n1].nRingSystem == at[n2].nRingSystem && ib >= num_atoms ) { - AT_RANK NeighNeighList[MAX_NUM_STEREO_ATOM_NEIGH+1]; - int m, neigh1=-1, neigh2=-1; - nRank1 = nRank2 = 0; - /* n1 */ - NeighNeighList[0] = at[n1].valence-1; /* for insertions_sort_NeighListBySymmAndCanonRank() */ - for ( i = 0, m = 1; i < at[n1].valence; i ++ ) { - int neigh = at[n1].neighbor[i]; - if ( neigh != ia ) { - NeighNeighList[m ++] = neigh; - } - } - insertions_sort_NeighListBySymmAndCanonRank( NeighNeighList, pRankStack1[0], nCanonRank ); - for ( m = 2; m < at[n1].valence; m ++ ) { - if ( pRankStack1[0][NeighNeighList[m]] == pRankStack1[0][NeighNeighList[m-1]] ) { - neigh1 = NeighNeighList[m-1]; - break; - } - } - /* n2 */ - NeighNeighList[0] = at[n2].valence-1; /* for insertions_sort_NeighListBySymmAndCanonRank() */ - for ( i = 0, m = 1; i < at[n2].valence; i ++ ) { - int neigh = at[n2].neighbor[i]; - if ( neigh != ia ) { - NeighNeighList[m ++] = neigh; - } - } - insertions_sort_NeighListBySymmAndCanonRank( NeighNeighList, pRankStack2[0], nCanonRank ); - for ( m = 2; m < at[n2].valence; m ++ ) { - if ( pRankStack2[0][NeighNeighList[m]] == pRankStack2[0][NeighNeighList[m-1]] ) { -#if ( BREAK_ALSO_NEIGH_TIE_ROTATE == 1 ) - neigh2 = NeighNeighList[m]; /* [m] to obtain same axis orientation around ia= 0 && neigh2 >= 0 && pRankStack1[0][neigh1] == pRankStack2[0][neigh2] ) { - /* neighbors' neighbors are tied */ - nRank1 = pRankStack1[0][neigh1]; - nRank2 = GetMinNewRank(pRankStack1[0], pRankStack1[1], nRank1 ); - - /* Break neighbor's neighbor tie */ - - nNumDiffRanks = nNumDiffRanks1; - - pRankStack1[0][neigh1] = nRank2; - nNumDiffRanks1 = DifferentiateRanksBasic( num_at_tg, NeighList, - nNumDiffRanks, pRankStack1[0], nTempRank, - pRankStack1[1], lNumIter, 1 ); - - pRankStack2[0][neigh2] = nRank2; - nNumDiffRanks2 = DifferentiateRanksBasic( num_at_tg, NeighList, - nNumDiffRanks, pRankStack2[0], nTempRank, - pRankStack2[1], lNumIter, 1 ); - } - } -#endif - - - /* for debug only */ - for ( i = 0; i < num_at_tg; i ++ ) { - if ( pRankStack1[0][(int)pRankStack1[1][i]] != pRankStack2[0][(int)pRankStack2[1][i]] ) { - return -1; /* */ - } - } - /* Resort lists of neighbors */ - SortNeighListsBySymmAndCanonRank( num_atoms, nl1, pRankStack1[0], nCanonRank ); - SortNeighListsBySymmAndCanonRank( num_atoms, nl2, pRankStack2[0], nCanonRank ); - - return nNumDiffRanks1+1; -} - -/**************************************************************************************/ -int CheckNextSymmNeighborsAndBonds( sp_ATOM *at, AT_RANK cur1, AT_RANK cur2, AT_RANK n1, AT_RANK n2, - AT_RANK *nAvoidCheckAtom, AT_RANK *nVisited1, AT_RANK *nVisited2, - AT_RANK *nVisitOrd1, AT_RANK *nVisitOrd2, const AT_RANK *nRank1, const AT_RANK *nRank2 ) -{ - AT_RANK s1, s2; - int i1, i2, k1, k2; - if ( nRank1[n1] != nRank2[n2] ) { - return -1; /* parallel traversal in stereo removal failed */ /* */ - } - switch ( !nVisited1[n1] + !nVisited2[n2] ) { - case 0: - if ( nVisited1[n1] != n2+1 || nVisited2[n2] != n1+1 ) { - return -1; /* 0; */ /* possibly error???: we have come to an alreardy traversed pair and */ - /* found that the pair previously has not been traversed synchroneously. */ - } /* -- Happens in C60. */ - break; - case 1: - return -1; /* 0; */ /* possibly error: one is zero, another is not a zero. Happens in C60 */ - - /* case 2: */ - /* both are zero, OK. */ - } - - if ( nVisitOrd1[n1] != nVisitOrd2[n2] ) { - return -1; /* 0; */ /* different DFS trees */ - } - /* at[n1] and at[n2] are next to at[cur1] and at[cur2] respectively */ - /* Even though the bond might have already been checked, check whether */ - /* it is a stereo bond/cumulene. If it is, check the bond/cumulene parity. */ - - /* Even though the bond or cumulene might have already been checked, check it: this is */ - /* the only place we can check stereo bonds and cumulenes that are not edges of the DFS tree */ - /* The code works both for a stereo bond and a stereogenic cumulene. */ - - for ( i1 = 0, k1 = 0; i1 < MAX_NUM_STEREO_BONDS && - (s1=at[cur1].stereo_bond_neighbor[i1]) && - !(k1=(at[cur1].neighbor[(int)at[cur1].stereo_bond_ord[i1]] == n1)); i1 ++ ) - ; - for ( i2 = 0, k2 = 0; i2 < MAX_NUM_STEREO_BONDS && - (s2=at[cur2].stereo_bond_neighbor[i2]) && - !(k2=(at[cur2].neighbor[(int)at[cur2].stereo_bond_ord[i2]] == n2)); i2 ++ ) - ; - - /* -- this does not work in case of cumulenes -- - for ( i1 = 0, k1 = 0; i1 < MAX_NUM_STEREO_BONDS && (s1=at[cur1].stereo_bond_neighbor[i1]) && !(k1=(s1-1 == n1)); i1 ++ ) - ; - for ( i2 = 0, k2 = 0; i2 < MAX_NUM_STEREO_BONDS && (s2=at[cur2].stereo_bond_neighbor[i2]) && !(k2=(s2-1 == n2)); i2 ++ ) - ; - */ - - if ( k1 != k2 ) { - return 0; /* not an error: a stereo bond and not a stereo bond */ - } - if ( k1 ) { - /* here k1 == k2 */ - int bCheckBond1, bCheckBond2; - s1 --; - s2 --; - - bCheckBond1 = (cur1 != nAvoidCheckAtom[0] || s1 != nAvoidCheckAtom[1]) && - (cur1 != nAvoidCheckAtom[1] || s1 != nAvoidCheckAtom[0]); - bCheckBond2 = (cur2 != nAvoidCheckAtom[0] || s2 != nAvoidCheckAtom[1]) && - (cur2 != nAvoidCheckAtom[1] || s2 != nAvoidCheckAtom[0]); - - if ( bCheckBond1 != bCheckBond2 ) - return 0; - - if ( !bCheckBond1 && !bCheckBond2 ) { - return 1; /* do not go any further in this direction */ - } - - if ( at[cur1].stereo_bond_parity[i1] != at[cur2].stereo_bond_parity[i2] ) { - /* different values of at[].stereo_bond_parity: definitely different bonds */ - /* known parities */ - if ( PARITY_KNOWN(at[cur1].stereo_bond_parity[i1] ) && - PARITY_KNOWN(at[cur2].stereo_bond_parity[i2] ) ) { - return 0; /* different currently known stereo bond parities */ - } -#if ( PROPAGATE_ILL_DEF_STEREO != 1 ) - /* well defined and to be calculated from the ranks */ - if ( !(PARITY_CALCULATE(at[cur1].stereo_bond_parity[i1]) && PARITY_WELL_DEF (at[cur2].stereo_bond_parity[i2]) || - PARITY_WELL_DEF (at[cur1].stereo_bond_parity[i1]) && PARITY_CALCULATE(at[cur2].stereo_bond_parity[i2]) || - PARITY_CALCULATE(at[cur1].stereo_bond_parity[i1]) && PARITY_CALCULATE(at[cur2].stereo_bond_parity[i2]) ) ) { - /* do not reject if: "well defined" and "calculate" or "calculate" and "calculate" */ - return 0; - } -#endif - } - -#if ( PROPAGATE_ILL_DEF_STEREO != 1 ) - if ( (cur1 != cur2 || s1 != s2) && (cur1 != s2 || cur2 != s1) ) { - /* two different stereo bonds */ - if ( PARITY_ILL_DEF( at[cur1].stereo_bond_parity[i1] ) || - PARITY_ILL_DEF( at[cur2].stereo_bond_parity[i2] ) ) { - return 0; - } - } -#endif - } - return 1; /* stereo bonds to n1 and n2 have same known parities or are not stereo bonds */ -} -/**************************************************************************************/ -int CreateCheckSymmPaths( sp_ATOM *at, AT_RANK prev1, AT_RANK cur1, AT_RANK prev2, AT_RANK cur2, - AT_RANK *nAvoidCheckAtom, AT_RANK *nVisited1, AT_RANK *nVisited2, - AT_RANK *nVisitOrd1, AT_RANK *nVisitOrd2, - NEIGH_LIST *nl1, NEIGH_LIST *nl2, const AT_RANK *nRank1, const AT_RANK *nRank2, - AT_RANK *nCanonRank, AT_RANK *nLength, int *bParitiesInverted, int mode ) -{ - int k, k1, k2, ret=0, bParitiesInvertedZero=0, *pbParitiesInverted; - AT_RANK n1, n2; - - nVisited1[cur1] = cur2+1; /* symmetrically exchange atom numbers */ - nVisited2[cur2] = cur1+1; - - (*nLength) ++; - - nVisitOrd1[cur1] = *nLength; /* save DFS visit order */ - nVisitOrd2[cur2] = *nLength; - - /* new version allows all inverted parities */ - if ( PARITY_WELL_DEF(at[cur1].stereo_atom_parity) && - PARITY_WELL_DEF(at[cur2].stereo_atom_parity) ) { - if ( *bParitiesInverted < 0 ) { - *bParitiesInverted = (at[cur1].stereo_atom_parity + at[cur2].stereo_atom_parity) % 2; - } else - if ( *bParitiesInverted != (at[cur1].stereo_atom_parity + at[cur2].stereo_atom_parity) % 2 ) { - return 0; /* Different known in advance parities have wrong "inverted" relation */ - } - } else - if ( PARITY_KNOWN(at[cur1].stereo_atom_parity) && - PARITY_KNOWN(at[cur2].stereo_atom_parity) && - at[cur1].stereo_atom_parity != at[cur2].stereo_atom_parity ) { - return 0; /* Different known in advance parities */ - } - - if ( cur1 != cur2 && - !at[cur1].stereo_bond_neighbor[0] && !at[cur2].stereo_bond_neighbor[0] && - PARITY_KNOWN(at[cur1].parity) != PARITY_KNOWN(at[cur2].parity) ) { - return 0; /* one atom is stereogenic, another (presumably equivalent) is not. 9-11-2002 */ - } -#if ( PROPAGATE_ILL_DEF_STEREO != 1 ) - if ( cur1 != cur2 && - (PARITY_ILL_DEF(at[cur1].stereo_atom_parity) || - PARITY_ILL_DEF(at[cur2].stereo_atom_parity)) - ) { - return 0; /* Cannot detect whether the paths are same or different */ - } -#endif - - if ( at[cur1].valence != at[cur2].valence ) { - return CT_REMOVE_STEREO_ERR; /* program error */ /* */ - } - if ( at[cur1].valence == 1 ) { - return 1; /* so far success */ - } - - if ( nl1[(int)cur1][0] != nl2[(int)cur2][0] || nl1[(int)cur1][0] != at[cur1].valence ) { - return CT_REMOVE_STEREO_ERR; /* error: different valences */ /* */ - } - - - for ( k = 1, k1 = 1, k2 = 1; k < at[cur1].valence; k ++, k1 ++, k2 ++ ) { - if ( (n1 = nl1[(int)cur1][k1]) == prev1 ) { - n1 = nl1[(int)cur1][++k1]; /* don't go back */ - } - if ( (n2 = nl2[(int)cur2][k2]) == prev2 ) { - n2 = nl2[(int)cur2][++k2]; /* don't go back */ - } - - if ( 0 >= (ret = CheckNextSymmNeighborsAndBonds( at, cur1, cur2, n1, n2, nAvoidCheckAtom, - nVisited1, nVisited2, nVisitOrd1, nVisitOrd2, nRank1, nRank2 ) ) ) { - return ret; /* different neighbors or bonds */ - } - - if ( !nVisited1[n1] ) { /* recursion */ - /* allow all inverted parities only inside a single ring system containing the starting point */ - pbParitiesInverted = (at[cur1].nRingSystem == at[n1].nRingSystem)? bParitiesInverted:&bParitiesInvertedZero; - if ( 0 >= (ret = CreateCheckSymmPaths( at, cur1, n1, cur2, n2, nAvoidCheckAtom, - nVisited1, nVisited2, nVisitOrd1, nVisitOrd2, - nl1, nl2, nRank1, nRank2, nCanonRank, nLength, pbParitiesInverted, mode ) ) ) { - return ret; - } - } - } - return 1; /* Success */ - -} -/**************************************************************************************/ -/* Compare parities */ -#define MAX_OTHER_NEIGH 2 -/* nNeighMode */ -#define NEIGH_MODE_RING 1 -#define NEIGH_MODE_CHAIN 2 - -#define CHECKING_STEREOCENTER 1 -#define CHECKING_STEREOBOND 2 - -#define COMP_STEREO_SUCCESS 1 -#define NOT_WELL_DEF_UNKN 2 -#define NOT_WELL_DEF_UNDF 4 - -#define PARITY_IMPOSSIBLE 999 -/************************************************************************************** - Note: the following C2v/S4 stereo center symmetry recognition - is not included in the final InChI version released in April 2005 - It is disabled in the mode.h (CHECK_C2v_S4_SYMM = 0) - As the result, the only central atom in S4 or atoms on C2v axis - may have pasrity (-) even though these atoms are not stereogenic. - - Reason: Not finished/tested yet - ************************************************************************************** - - In case of stereocenter with 2 pairs of constitutionally identical neighbors : - - G(n) > H(m) means group G has n elements; group H has m elements and - group H is a subgroup of G - - Td(24) > D2d(8> > D2(4) - > S4(4) > C2(2) -- Test for S4 - > C2v(4) > C2(2) -- Test for C2v - > Cs(2) - - Td(24) > C3v(6) > C3(3) -- does not have 2 pairs of constitutionally identical neighbors - > Cs(2) - - The pair of atoms to check for the existence of a steregenic atom: X, Y - - X Y - \ / - C - / \ - A B - - Conditions to check: - - (a) Old #0: Map canonical numbers X1 <--> Y2 - Traverse DFS from X and Y - If all parities vs. canon. numbers unchanged except that of C - then C is not stereogenic - - (b) C2v #1: discover ACB symmetry plain Cv - o Map canonical numbers X1 <--> Y2, Fix(Ai), Fix(Bi) - o Make sure that after mapping X1 <--> Y2 the atoms Ai and - Bi still have equal mapping ranks - Traverse DFS from X and Y - In this case canonical numbers will be reflected in plane ACB if it exists. - o Criterion of the presence of the symmetry plain is: - --> all stereogenic atoms and allenes parities are inverted - (c) C2v #2: discover vertical axis C2 - o Map canonical numbers X1 <--> Y2 and A1 <--> B2 - o Make sure that after mapping X1 <--> Y2 the atoms Ai and - Bi still have equal mapping ranks - o Traverse DFS from X1 and Y2 - In this case canonical numbers will be rotated by - 180 degrees around the vertical axis - (this may be considered as a superposition of two Cv - reflections in perpendicular vertical planes) - o Criterion of the presence of the C2 axis is: - --> all stereogenic atoms and allenes parities are not changed - (d) S4 #3: discover axis horizontal S4 axis - o Map canonical numbers X1 <--> Y2, Y1 <--> A2, A1 <--> B2, B1 <--> X2 - o Traverse DFS from X1 and Y2 - In this case the canonical numbers will be rotated by - 90 degrees and reflected in a horizontal plane. - 3 attempts corrresponding to transpositions 0132, 0213, 0321 - are sufficient (XY=01,02,03) - o Criterion of the presence of the S4 symmetry axis is: - --> all stereogenic atoms and allenes parities are inverted - -***************************************************************************************/ - -/**************************************************************************************/ -int CalculatedPathsParitiesAreIdentical( sp_ATOM *at, int num_atoms, const AT_RANK *nSymmRank, - AT_RANK *nCanonRank, AT_RANK *nAtomNumberCanon, - AT_RANK *nAtomNumberCanon1, AT_RANK *nAtomNumberCanon2, - AT_RANK *nVisited1, AT_RANK *nVisited2, - AT_RANK prev_sb_neigh, AT_RANK cur, AT_RANK next1, AT_RANK next2, int nNeighMode, - int bParitiesInverted, int mode, CANON_STAT *pCS, - int vABParityUnknown) -{ - int i, i01, i02, i11, i12, i21, i22, k, parity, parity1, parity2, parity12, num_other_neigh; - int nNumEqStereogenic, nCheckingMode, not_well_def_parities; - AT_RANK other_neigh[MAX_NUM_STEREO_ATOM_NEIGH], neigh, r1, r2; - int nNumComparedCenters = 0, nNumComparedBonds = 0, bCurParityInv1=0 /*, bCurParityInv2=0*/; - int bCurRotated=0, nNumDiff=0, nNumInv=0; - int s1, s2; - - nCheckingMode = ( prev_sb_neigh < num_atoms )? CHECKING_STEREOBOND : CHECKING_STEREOCENTER; - not_well_def_parities = 0; - nNumEqStereogenic = 0; - - if ( nNeighMode != NEIGH_MODE_RING && - bParitiesInverted != 0 || abs(bParitiesInverted) != 1 ) { - bParitiesInverted = 0; - } - - if ( bParitiesInverted ) { - for ( i = 0, i11 = i22 = 0; i < num_atoms; i ++ ) { - /* count number of atoms that have not been visited */ - i11 += !nVisited1[i]; - i22 += !nVisited2[i]; - nAtomNumberCanon1[i] = MAX_ATOMS+1; /* mark unchanged */ - nAtomNumberCanon2[i] = MAX_ATOMS+1; /* mark unchanged */ - } - if ( i11 || i22 ) { - if ( bParitiesInverted == 1 ) - return 0; /* only a part of the structure has been inverted */ - else - bParitiesInverted = 0; - } - } else { - for ( i = 0; i < num_atoms; i ++ ) { - nAtomNumberCanon1[i] = MAX_ATOMS+1; /* mark unchanged */ - nAtomNumberCanon2[i] = MAX_ATOMS+1; /* mark unchanged */ - } - } - if ( bParitiesInverted > 0 && !(mode == MAP_MODE_C2v || mode == MAP_MODE_S4) || - bParitiesInverted == 0 && !(mode == MAP_MODE_C2 || mode == MAP_MODE_STD)) { - return 0; - } - /************************************************************************************** - * The following discussion assumes that the canonical numbers are - * switched for some pairs of constitutionally identical atoms - * in such a way that the new numbering is an equivalent to the - * nCanonRank[] canonical numbering (the transposition belongs to the - * automorphism group of the chemical structure having no stereo). - * At this point non-zero elements of nVisited1[] and nVisited2[] - * together contain transposition P of the atom numbers. - * and P2 respectively of the ordering atom numbers: nVisitedi[k] = Pi(k)+1; - * In this implementation: - * P1(k)=k for all k - * P2(cur)=cur, P2(next1)=next2, P2(next2)=next1 - * - * Below we call one of the numberings "old", another "new". - * - * *IF* the old and the new canonical numberings produce same parities for stereogenic - * elements for the same canonical number(s) - * (that is, old_parity(canon_number) == new_parity(canon_number) - * *except* the currently being tested stereocenter at[cur] or stereobond/cumulene - * at[cur]=at[prev_sb_neigh], whose parity MUST be inverted - * - * *THEN* the stereocenter or stereobond/cumulene is not stereogenic with one - * - * *EXCEPTION* If the currently tested stereogenic element is constitutionally - * equivalent to two or more other stereogenic elements that have been - * permuted then the currently tested one is still stereogenic. - **************************************************************************************/ - - /* - * 1. replace the assigned in each of the parallel traversals atom numbers - * with the canon. ranks corresponding to the atom numbers in the - * currently numbered atoms at[]. - * One of obtained this way canonical numberings (probably nVisited1[]) - * is same as the nCanonRank[] because usually nVisited1[i] = i+1 or 0 - */ - for ( i = 0; i < num_atoms; i ++ ) { - - if ( nVisited1[i] ) { - /* canonical number of the atom mapped on atom #i in 'left' path */ - nVisited1[i] = nCanonRank[ (int)nVisited1[i] - 1 ]; - /* reverse: atom # from the mapped canonical rank in 'left' path */ - nAtomNumberCanon1[nVisited1[i] - 1] = i; - } - if ( nVisited2[i] ) { - /* canonical number of the atom mapped on atom #i in 'right' path */ - nVisited2[i] = nCanonRank[ (int)nVisited2[i] - 1 ]; - /* reverse: atom # from the mapped canonical rank in 'right' path */ - nAtomNumberCanon2[nVisited2[i] - 1] = i; - } - /* if 'left' and 'right' path do not have atoms in common except the - starting atom (and in case of stereobond, the end atom) some of - nVisitedi[i] elements may be zero. - */ - } - - /* - * if started with a stereobond then check whether its parity has changed. - * If yes then continue, otherwise parities are different - * - * if started with a stereo center then prev_sb_neigh = MAX_ATOMS+1 - * - * If the transposition of next1 and next2 changes only the parity of the starting stereo atom or stereo bond - * then the stereo bond or stereo atom is not stereogenic - * - * The exception: the stereogenic elememt in question is equivalent - * to two or more traversed other stereogenic elememts - * (see nNumEqStereogenic below, case similar to trimethylcyclopropane: - * 3 or more constitutionally equivalent stereogenic elements) - */ - if ( nCheckingMode == CHECKING_STEREOBOND ) { - /****************************************************************************** - * - * Possibly stereogenic starting bond or cumulene at[cur]-at[prev_sb_neigh] - * - *******************************************************************************/ - /* checking the starting stereo bond */ - if ( nVisited1[prev_sb_neigh] || nVisited2[prev_sb_neigh] ) { - /* the bond or cumulene is in the ring and the opposite atom has been visited */ - if ( nVisited1[prev_sb_neigh] != nVisited2[prev_sb_neigh] || - nCanonRank[prev_sb_neigh] != nVisited2[prev_sb_neigh] ) { - return 0; /* error: we came back to the same bond/cumulene and */ /* */ - /* assigned different canon. ranks to the opposite atom. */ - } - if ( at[prev_sb_neigh].valence + at[prev_sb_neigh].num_H > 3 ) - return 0; /* at[prev_sb_neigh] atom can not be adjacent to a stereo bond/cumulene */ - /* or does not have 3 attachments (hydrogens are not considered here) */ - for ( i = 0, k = 0; i < MAX_NUM_STEREO_BONDS && - (neigh=at[prev_sb_neigh].stereo_bond_neighbor[i]) && !(k=(neigh-1 == cur)); i ++ ) - ; - if ( !k ) { - return -1; /* program error: could not locate stereogenic bond mark on the opposite atom */ - } - k = (int)at[prev_sb_neigh].stereo_bond_ord[i]; /* seq. number of the double or cumulene bond on at[prev_sb_neigh] */ - - for ( i = 0, num_other_neigh = 0; i < at[prev_sb_neigh].valence && num_other_neigh <= MAX_OTHER_NEIGH; i ++ ) { - if ( i != k ) { /* do not include the double or cumulene bond */ - other_neigh[num_other_neigh ++] = at[prev_sb_neigh].neighbor[i]; - } - } - if ( num_other_neigh + at[prev_sb_neigh].num_H > MAX_OTHER_NEIGH ) { - return CT_STEREOCOUNT_ERR; /* */ - } - for ( i = 0; i < num_other_neigh; i ++ ) { - k = (int)other_neigh[i]; - if ( nVisited1[k] && nVisited1[k] != nCanonRank[k] ) { - return 0; /* parity of the statring stereo bond/cumulene has not changed. */ - } - if ( nVisited2[k] && nVisited2[k] != nCanonRank[k] ) { - return 0; /* parity of the statring stereo bond/cumulene has not changed. */ - } - } - } - } - if ( nCheckingMode == CHECKING_STEREOCENTER ) { - /************************************************** - * - * Possibly stereogenic starting atom at[cur] - * - **************************************************/ - /* checking the starting stereo center */ - for ( i = 0, num_other_neigh = 0; i < at[cur].valence && num_other_neigh <= MAX_OTHER_NEIGH; i ++ ) { - neigh = at[cur].neighbor[i]; - if ( neigh != next1 && neigh != next2 ) { - other_neigh[num_other_neigh ++] = neigh; - } - } - if ( num_other_neigh + at[cur].num_H > MAX_OTHER_NEIGH ) { - return CT_STEREOCOUNT_ERR; /* */ - } - /* - if ( bParitiesInverted && at[cur].valence == MAX_NUM_STEREO_ATOM_NEIGH ) { - if ( nVisited1[other_neigh[0]] == nCanonRank[other_neigh[0]] || - nVisited2[other_neigh[0]] == nCanonRank[other_neigh[0]] || - nVisited1[other_neigh[1]] == nCanonRank[other_neigh[1]] || - nVisited2[other_neigh[1]] == nCanonRank[other_neigh[1]] ) { - bParitiesInverted = 0; - bCurRotated = 1; - } - } - */ - /* bParitiesInverted = -1 means no predefined stereocenter has been checked */ - if ( bParitiesInverted && at[cur].valence == MAX_NUM_STEREO_ATOM_NEIGH ) { - /* special case: 4 canonically eq. neighbors */ - int canon_parity, parity_vis_1, parity_vis_2; - canon_parity = GetPermutationParity( at+cur, MAX_ATOMS+1, nCanonRank ); - parity_vis_1 = GetPermutationParity( at+cur, MAX_ATOMS+1, nVisited1 ); - parity_vis_2 = GetPermutationParity( at+cur, MAX_ATOMS+1, nVisited2 ); - if ( parity_vis_1 != parity_vis_2 ) { - return 0; - } - if ( bParitiesInverted == 1 && parity_vis_1 == canon_parity ) { - return 0; /* not a typical case of inversion during the mapping of D4h stereocenter */ - } else - if ( bParitiesInverted == -1 ) { - if ( parity_vis_1 == canon_parity ) { - bParitiesInverted = 0; - } else { - bParitiesInverted = 1; - } - } - } - /* at this point bParitiesInverted >= 0 */ - if ( !bParitiesInverted && !bCurRotated ) { - for ( i = 0; i < num_other_neigh; i ++ ) { - k = (int)other_neigh[i]; - if ( nVisited1[k] && nVisited1[k] != nCanonRank[k] ) { - return 0; /* parity of the statring stereo center has not changed. */ - } - if ( nVisited2[k] && nVisited2[k] != nCanonRank[k] ) { - return 0; /* parity of the statring stereo center has not changed. */ - } - } - } - } - - /***************************************************** - * Check other (non-starting) stereo centers - ******************************************************/ - for ( i = 0; i < pCS->nLenLinearCTStereoCarb; i ++, nNumComparedCenters += (k > 0) ) { - r1 = pCS->LinearCTStereoCarb[i].at_num; - i01 = nAtomNumberCanon[r1-1]; /* ord. number of the atom that has canon rank r1 */ - - i11 = nAtomNumberCanon1[r1-1]; /* = (MAX_ATOMS+1) > num_atoms if the atom has not been traversed */ - i12 = nAtomNumberCanon2[r1-1]; /* = otherwise < num_atoms */ - - s1 = (i11 < num_atoms); /* 1 => the center was traversed on path #1 */ - s2 = (i12 < num_atoms); /* 1 => the center was traversed on path #2 */ - - bCurParityInv1 = (bParitiesInverted && - at[cur].nRingSystem == at[i11].nRingSystem && - at[cur].nRingSystem == at[i12].nRingSystem ); - - - k = 0; - - /* check whether the two stereo centers (they can be one and the same atom) have been traversed */ - if ( !s1 && !s2 ) { - continue; /* Both stereo centers have not been traversed; check the next pair. */ - } - - if ( nCheckingMode == CHECKING_STEREOCENTER ) { - /* check whether the stereocenters are the starting stereocenter */ - switch( (cur == i11) + (cur == i12) ) { - case 2: - continue; /* do not recheck the starting atom */ - case 1: - return -1; /* possibly program error */ /* */ - /* case 0: */ - /* break; */ /* the stereo centers are not the sarting stereo center */ - } - if ( cur == i01 ) { - return -1; /* program error: in this case at least one of the i11, i12 must be == cur */ /* */ - } - } - - if ( nNeighMode == NEIGH_MODE_RING ) { - if ( i11 != i12 && !bCurParityInv1 ) { - return -1; /* failed: the two stereo atoms have not been traversed synchronously */ - } - if ( !at[i11].parity || !at[i12].parity ) { - return 0; /* another atom does not have parity (it might have been removed) 9-11-2002 */ - } - } - if ( nNeighMode == NEIGH_MODE_CHAIN ) { - if ( s1+s2 != 1 ) { - return -1; /* program error: only one out of s1 and s2 must be 1, another must be 0. */ - } - if ( s1 && !at[i11].parity || s2 && !at[i12].parity ) { - return 0; /* another atom does not have parity (it might have been removed) 9-11-2002 */ - } - } - - parity = pCS->LinearCTStereoCarb[i].parity; - if ( nNeighMode == NEIGH_MODE_RING && (i11 != i01) && (i12 != i01) || - /* in NEIGH_MODE_RING case we know that i11 == i12 except bCurParityInv1 == 1 */ - nNeighMode == NEIGH_MODE_CHAIN - /* in NEIGH_MODE_CHAIN case here we always have 2 different atoms */ - ) { - /**************************************************************** - * Case of two transposed atoms or a circular permutation in D4h - */ - parity1 = s1? GetStereoCenterParity( at, i11, nVisited1 ) : PARITY_IMPOSSIBLE; - parity2 = s2? GetStereoCenterParity( at, i12, nVisited2 ) : PARITY_IMPOSSIBLE; - if ( !ATOM_PARITY_KNOWN(parity1) && !ATOM_PARITY_KNOWN(parity2) ) { - return -1; /* should not happen: must have been detected at the time of the traversal */ - } - if ( s1 && s2 ) { - if ( bCurParityInv1 ) { - int parity1orig = GetStereoCenterParity( at, i11, nCanonRank ); - int parity2orig = GetStereoCenterParity( at, i12, nCanonRank ); - if ( i11 == i12 || - (parity1 == parity1orig || parity2 == parity2orig || parity1 != parity2) && - ATOM_PARITY_WELL_DEF(parity1) || - parity1 != parity2 && (!ATOM_PARITY_WELL_DEF(parity1) || - !ATOM_PARITY_WELL_DEF(parity2)) ) - /*return -1; */ /* should be different atoms with inverted parities */ - nNumDiff ++; - } else { - if ( i11 != i12 || parity1 != parity2 ) - return -1; /* program error: must be the same atom */ - } - } - parity12 = s1? parity1 : parity2; - - if ( ATOM_PARITY_WELL_DEF(parity) && parity == parity12 ) { - /* symmetrical neighbors have well-defined equal parities */ - k ++; - if ( nCheckingMode == CHECKING_STEREOCENTER && nNeighMode == NEIGH_MODE_RING ) { - /* all 3: cur, i01, i11 are different atoms (here i11==i12) */ - /* here nSymmRank[i01]==nSymmRank[i11] due to the parallel traversal */ - if ( nSymmRank[cur] == nSymmRank[i01] ) { - nNumEqStereogenic ++; /* all 3 are equ */ - } - } - } else - if ( ATOM_PARITY_WELL_DEF(parity) && ATOM_PARITY_WELL_DEF(parity12) ) { - /* apparently different well-defined parities */ - if ( !bCurParityInv1 ) { - nNumInv ++; - /* return 0; */ - } - } else { -#if ( PROPAGATE_ILL_DEF_STEREO == 1 ) - /* at least one parity is ill-defined. Use parity1 and parity2 to temporarily save bitmaps */ - parity1 = (parity ==vABParityUnknown /*AB_PARITY_UNKN*/)? NOT_WELL_DEF_UNKN : - (parity ==AB_PARITY_UNDF)? NOT_WELL_DEF_UNDF : 0; - parity2 = (parity12==vABParityUnknown /*AB_PARITY_UNKN*/)? NOT_WELL_DEF_UNKN : - (parity12==AB_PARITY_UNDF)? NOT_WELL_DEF_UNDF : 0; - if ( parity1 | parity2 ) { - not_well_def_parities |= ( parity1 | parity2 ); - k ++; - } else { - return -1; /* program error */ /* */ - } -#else - return 0; -#endif - } - } else - if ( i11 == i01 && i12 == i01 ) { - /********************************************************************/ - /* i11 == i12 are same atom as i01, nNeighMode == NEIGH_MODE_RING */ - if ( !s1 || !s2 ) { - return -1; - } - /* the parity of the new neighbors permutation must be same as the old one */ - /* this must work for well-defined and ill-defined parities. */ - /* actual parity (that includes the geometry) is not important here. */ - /* old permutation */ - parity = GetPermutationParity( at+i01, MAX_ATOMS+1, nCanonRank ); - /* new parmutation */ - parity1 = GetPermutationParity( at+i01, MAX_ATOMS+1, nVisited1 ); - parity2 = GetPermutationParity( at+i01, MAX_ATOMS+1, nVisited2 ); - if ( parity != parity1 || parity != parity2 ) { - return 0; - } - k ++; - } else { - /* nNeighMode == NEIGH_MODE_RING and only one out of the two (i11 == i01) (i12 == i01) is true */ - return -1; - } - /* nNumComparedCenters += (k > 0); */ - } - if ( bCurRotated || nNumDiff || nNumInv ) { - return 0; - } - - /* !!!! Add here bParitiesInverted == 1 case !!!! */ - /******************************************************/ - /* Check other (non-starting) stereo bonds/cumulenes */ - /******************************************************/ - for ( i = 0; i < pCS->nLenLinearCTStereoDble; i ++, nNumComparedBonds += (k > 0) ) { - r1 = pCS->LinearCTStereoDble[i].at_num1; - r2 = pCS->LinearCTStereoDble[i].at_num2; - i01 = nAtomNumberCanon[r1-1]; /* ord. number of the atom that originally has canon rank r1 */ - i02 = nAtomNumberCanon[r2-1]; /* ord. number of the atom that originally has canon rank r2 */ - - i11 = nAtomNumberCanon1[r1-1]; /* ord. number of the atom that got canon rank r1 during the parallel traversal */ - i12 = nAtomNumberCanon1[r2-1]; /* ord. number of the atom that got canon rank r2 during the parallel traversal */ - - i21 = nAtomNumberCanon2[r1-1]; - i22 = nAtomNumberCanon2[r2-1]; - - - s1 = (i11 < num_atoms && i12 < num_atoms); - s2 = (i21 < num_atoms && i22 < num_atoms); - - k = 0; - - /* check whether the two stereo bonds/allenes (they can be one and the same) have been traversed */ - if ( !s1 && !s2 ) { - continue; /* Both stereo bonds/cumulenes have not been traversed; check the next pair. */ - } - - if ( nCheckingMode == CHECKING_STEREOBOND ) { - switch ( (i11 == cur && i12 == prev_sb_neigh || i12 == cur && i11 == prev_sb_neigh) + - (i21 == cur && i22 == prev_sb_neigh || i22 == cur && i21 == prev_sb_neigh) ) { - case 2: - continue; /* do not recheck the starting bond/cumulene */ - case 1: - return -1; /* possibly program error */ /* */ - /* case 0: */ - /* break; */ /* the stereo centers are not the sarting stereo center */ - } - if ( (i01 == cur && i02 == prev_sb_neigh) || (i02 == cur && i01 == prev_sb_neigh) ) { - return -1; /* program error: in this case at least one of the i1x, i2x must be == cur */ /* */ - } - } - - if ( nNeighMode == NEIGH_MODE_RING ) { - if ( (i11 != i21 || i12 != i22) && (i11 != i22 || i12 != i21) ) { - return -1; /* failed: the two bonds/cumulenes have not been traversed synchronously */ - } - if ( 0 > GetStereoNeighborPos( at, i11, i12 ) ) { - return 0; /* another bond is not stereo (the stereo might have been removed) 9-11-2002 */ - } - - } - if ( nNeighMode == NEIGH_MODE_CHAIN ) { - if ( s1+s2 != 1 ) { - return -1; /* program error: only one out of s1 and s2 must be 1, another must be 0. */ - } - if ( s1 && 0 > GetStereoNeighborPos( at, i11, i12 ) || - s2 && 0 > GetStereoNeighborPos( at, i21, i22 ) ) { - return 0; /* another bond is not stereo (the stereo might have been removed) 9-11-2002 */ - } - } - - parity = pCS->LinearCTStereoDble[i].parity; - /* bMustBeIdentical = ATOM_PARITY_ILL_DEF(parity); */ - /* nNumEqStereogenic = 0; */ - - if ( nNeighMode == NEIGH_MODE_RING && (i11 != i01 || i12 != i02) && (i11 != i02 || i12 != i01) || - nNeighMode == NEIGH_MODE_CHAIN /* in NEIGH_MODE_CHAIN case here we always have 2 different atoms */ - ) { - /*******************************************/ - /* case of two transposed bonds/cumulenes */ - parity1 = s1? GetStereoBondParity( at, i11, i12, nVisited1 ) : PARITY_IMPOSSIBLE; - parity2 = s2? GetStereoBondParity( at, i21, i22, nVisited2 ) : PARITY_IMPOSSIBLE; - if ( !ATOM_PARITY_KNOWN(parity1) && !ATOM_PARITY_KNOWN(parity2) ) { - return -1; /* should not happen: must have been detected at the time of traversal */ - } - if ( s1 && s2 && ((i11 != i21 || i12 != i22) && (i11 != i22 || i12 != i21) || parity1 != parity2 ) ) { - return -1; /* program error: must be the same bond/cumulene */ - } - parity12 = s1? parity1 : parity2; - if ( ATOM_PARITY_WELL_DEF(parity) && parity == parity12 ) { - /* symmetrical neighbors have well-defined equal parities */ - k ++; - if ( nCheckingMode == CHECKING_STEREOBOND && nNeighMode == NEIGH_MODE_RING ) { - /* all 3 bonds: cur-prev_sb_neigh, i01-i02, i11-i12 are different */ - /* (here == compared as unordered pairs) */ - if ( nSymmRank[cur] == nSymmRank[i01] && nSymmRank[prev_sb_neigh] == nSymmRank[i02] || - nSymmRank[cur] == nSymmRank[i02] && nSymmRank[prev_sb_neigh] == nSymmRank[i01] ) { - nNumEqStereogenic ++; - } - } - } else - if ( ATOM_PARITY_WELL_DEF(parity) && ATOM_PARITY_WELL_DEF(parity12) ) { - /* apparently different well-defined parities */ - return 0; - } else { - /* at least one parity is ill-defined. Use parity1 and parity2 to temporarily save bitmaps */ -#if ( PROPAGATE_ILL_DEF_STEREO == 1 ) - parity1 = (parity ==vABParityUnknown /*AB_PARITY_UNKN*/)? NOT_WELL_DEF_UNKN : - (parity ==AB_PARITY_UNDF)? NOT_WELL_DEF_UNDF : 0; - parity2 = (parity12==vABParityUnknown /*AB_PARITY_UNKN*/)? NOT_WELL_DEF_UNKN : - (parity12==AB_PARITY_UNDF)? NOT_WELL_DEF_UNDF : 0; - if ( parity1 | parity2 ) { - not_well_def_parities |= ( parity1 | parity2 ); - k ++; - } else { - return -1; /* program error */ - } -#else - return 0; -#endif - } - } else { - /*****************************************************************************************/ - /* i11-i12 and i21-i22 are same as i01-i02 bond/cumulene, nNeighMode == NEIGH_MODE_RING */ - AT_NUMB n1, n2; - int j; - if ( !s1 || !s2 ) { - return -1; - } - /* find neighbors along the stereo bond/cumulene */ - for ( j = 0, n1 = MAX_ATOMS+1; j < MAX_NUM_STEREO_BOND_NEIGH && at[i01].stereo_bond_neighbor[j]; j ++ ) { - if ( (int)at[i01].stereo_bond_neighbor[j] == i02+1 ) { - n1 = at[i01].neighbor[ (int)at[i01].stereo_bond_ord[j] ]; - break; - } - } - for ( j = 0, n2 = MAX_ATOMS+1; j < MAX_NUM_STEREO_BOND_NEIGH && at[i02].stereo_bond_neighbor[j]; j ++ ) { - if ( (int)at[i02].stereo_bond_neighbor[j] == i01+1 ) { - n2 = at[i02].neighbor[ (int)at[i02].stereo_bond_ord[j] ]; - break; - } - } - if ( n1 > MAX_ATOMS || n2 > MAX_ATOMS ) { - return CT_REMOVE_STEREO_ERR; - } - /* the parity of the new neighbors permutation must be same as the old one */ - /* this must work for well-defined and ill-defined parities. */ - /* actual parity (that includes the geometry) is not important here. */ - /* old permutation */ - parity = GetPermutationParity( at+i01, n1, nCanonRank) + GetPermutationParity( at+i02, n2, nCanonRank); - /* new parmutation */ - parity1 = GetPermutationParity( at+i01, n1, nVisited1 ) + GetPermutationParity( at+i02, n2, nVisited1 ); - parity2 = GetPermutationParity( at+i01, n1, nVisited2 ) + GetPermutationParity( at+i02, n2, nVisited2 ); - if ( parity %2 != parity1 % 2 || parity1 % 2 != parity2 % 2 ) { - return 0; - } - k ++; - } - - /* nNumComparedBonds += ( k > 0 ); */ - } - - if ( nNumEqStereogenic > 0 ) { - /* case similar to trimethylcyclopropane: 3 constitutionally equivalent stereogenic elements */ - /* the transposition does not change the parities */ -#if ( bRELEASE_VERSION == 0 ) - pCS->bExtract |= EXTR_2EQL2CENTER_TO_REMOVE_PARITY; -#endif - return 0; - } -/* ========================================================================================= - Note - ==== - At this point the comparison is complete and no difference sufficient to establish - absence of stereo parity has been found. - However, non-zero not_well_def_parities means that an ill-defined parity was - compared to an ill-defined or well-defined parity. This means that the parity - of the atom or bond being checked cannot be well-defined anymore. - ========================================================================================*/ - - - not_well_def_parities |= COMP_STEREO_SUCCESS; - - return not_well_def_parities; - - /* Add 1 to indicate success. The stereogenic elements might have been */ - /* removed while checking existence of the previous atom/bond stereo */ - /* return (nNumComparedCenters + nNumComparedBonds + 1); */ -} -/********************************************************************************/ -/* Remove stereo marks from the bonds that are calculated to be non-stereo */ -/* Such bonds must have 2 constitutionally equivalent attachments */ -/* (can find two canonical numberings that change only one stereo bond parity) */ -int RemoveCalculatedNonStereoBondParities( sp_ATOM *at, int num_atoms, int num_at_tg, - AT_RANK **pRankStack1, AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, - AT_RANK *nCanonRank, const AT_RANK *nSymmRank, - AT_RANK *nAtomNumberCanon, AT_RANK *nAtomNumberCanon1, AT_RANK *nAtomNumberCanon2, - NEIGH_LIST *nl, NEIGH_LIST *nl1, NEIGH_LIST *nl2, - AT_RANK *nVisited1, AT_RANK *nVisited2, - CANON_STAT *pCS, - int vABParityUnknown) -{ - int j, n, m, ret, ret1, ret2, ret_failed=0; - - int i1, n1, s2; /* n1 must be SIGNED integer */ - AT_RANK nAtomRank1, nAtomRank2, neigh[3], nAvoidCheckAtom[2], opposite_atom, nLength; - int nNeighMode = NEIGH_MODE_CHAIN; - int nNumEqRingNeigh = 0, bRingNeigh, bSymmNeigh, bParitiesInverted; - NEIGH_LIST *nl01, *nl02; - const AT_RANK *nSymmRank1, *nSymmRank2; - - ret = 0; - -second_pass: - - for ( i1 = 0; i1 < num_atoms && !RETURNED_ERROR(ret_failed); i1 ++ ) { - if ( at[i1].valence != 3 || !at[i1].stereo_bond_neighbor[0] ) { - continue; - } - for ( n1 = 0; n1 < MAX_NUM_STEREO_BONDS && !RETURNED_ERROR(ret_failed) && (s2=at[i1].stereo_bond_neighbor[n1]); n1++ ) { - if ( !PARITY_CALCULATE(at[i1].stereo_bond_parity[n1]) && PARITY_WELL_DEF(at[i1].stereo_bond_parity[n1]) ) { - continue; - } - opposite_atom = (AT_RANK)(s2-1); - s2 = at[i1].neighbor[(int)at[i1].stereo_bond_ord[n1]]; /* different from opposite_atom in case of a cumulene */ - for ( j = 1, n = 0; j <= (int)at[i1].valence; j ++ ) { - if ( nl[i1][j] != s2 ) { - neigh[n++] = nl[i1][j]; /* sorting guarantees that canon. rank of neigh[0] is greater or equal */ - } - } - if ( n != 2 ) { - ret = CT_STEREOBOND_ERROR; /* */ - goto exit_function; - } - if ( nSymmRank[(int)neigh[0]] != nSymmRank[(int)neigh[1]] ) { - continue; /* may happen if another half-bond has not a defined parity */ - } - - bRingNeigh = (at[(int)neigh[0]].nRingSystem == at[(int)neigh[1]].nRingSystem); - switch ( nNeighMode ) { - case NEIGH_MODE_CHAIN: - if ( bRingNeigh ) { - nNumEqRingNeigh ++; - continue; - } - nl01 = nl; - nl02 = nl; - nSymmRank1 = nSymmRank; - nSymmRank2 = nSymmRank; - break; - - case NEIGH_MODE_RING: - if ( !bRingNeigh ) - continue; - /* break a tie between the two contitutionally equivalent neighbors, */ - /* refine the two partitions, sort neighbors lists nl1, nl2 */ - bSymmNeigh = BreakNeighborsTie( at, num_atoms, num_at_tg, opposite_atom, i1, - neigh, 0, 1, 0, - pRankStack1, pRankStack2, nTempRank, NeighList, nSymmRank, nCanonRank, - nl1, nl2, &pCS->lNumNeighListIter ); - if ( bSymmNeigh <= 0 ) { - if ( ret_failed > bSymmNeigh ) - ret_failed = bSymmNeigh; - continue; - } - nl01 = nl1; - nl02 = nl2; - nSymmRank1 = pRankStack1[0]; - nSymmRank2 = pRankStack2[0]; - break; - default: - return CT_STEREOCOUNT_ERR; /* */ - } - - /* initialize arrays */ - memset( nVisited1, 0, sizeof(nVisited1[0])*num_atoms ); - memset( nVisited2, 0, sizeof(nVisited2[0])*num_atoms ); - memset( nAtomNumberCanon1, 0, sizeof(nAtomNumberCanon1[0])*num_atoms ); - memset( nAtomNumberCanon2, 0, sizeof(nAtomNumberCanon2[0])*num_atoms ); - nLength = 1; - nVisited1[i1] = i1+1; /* start atoms are the same */ - nVisited2[i1] = i1+1; - nAtomNumberCanon1[i1] = nLength; - nAtomNumberCanon2[i1] = nLength; - nAvoidCheckAtom[0] = i1; - nAvoidCheckAtom[1] = opposite_atom; - bParitiesInverted = (nNeighMode == NEIGH_MODE_RING && - IS_ALLENE_CHAIN(at[i1].stereo_bond_parity[n1]) && - PARITY_CALCULATE(at[i1].stereo_bond_parity[n1]) && - at[i1].nRingSystem == at[opposite_atom].nRingSystem && - at[opposite_atom].valence==MAX_NUM_STEREO_BONDS)? -1 : 0; - ret1 = ret2 = 0; - if ( 0 < (ret1=CreateCheckSymmPaths( at, (AT_RANK)i1, neigh[0], (AT_RANK)i1, neigh[1], nAvoidCheckAtom, - nVisited1, nVisited2, nAtomNumberCanon1, nAtomNumberCanon2, - nl01, nl02, nSymmRank1, nSymmRank2, nCanonRank, &nLength, &bParitiesInverted, 0 ) ) && - 0 < (ret2=CalculatedPathsParitiesAreIdentical( at, num_atoms, nSymmRank, - nCanonRank, nAtomNumberCanon, nAtomNumberCanon1, nAtomNumberCanon2, - nVisited1, nVisited2, opposite_atom, (AT_RANK)i1, - neigh[0], neigh[1], nNeighMode, bParitiesInverted, 0, - pCS, vABParityUnknown ) ) ) { - if ( ret2 & ( NOT_WELL_DEF_UNKN | NOT_WELL_DEF_UNDF ) ) { - /* possibly change the parity to unknown or undefined */ - int new_parity = (ret2 & NOT_WELL_DEF_UNKN)? vABParityUnknown /*AB_PARITY_UNKN*/: AB_PARITY_UNDF; - if ( PARITY_ILL_DEF(at[i1].stereo_bond_parity[n1]) && PARITY_VAL(at[i1].stereo_bond_parity[n1]) > new_parity || - PARITY_CALCULATE(at[i1].stereo_bond_parity[n1]) ) { - /* set new unknown or undefined parity */ - SetOneStereoBondIllDefParity( at, i1, /* atom number*/ n1 /* stereo bond ord. number*/, new_parity ); - /* change in pCS */ - nAtomRank1 = inchi_max( nCanonRank[i1], nCanonRank[opposite_atom]); - nAtomRank2 = inchi_min( nCanonRank[i1], nCanonRank[opposite_atom]); - for ( n = 0, m = pCS->nLenLinearCTStereoDble-1; n <= m; n ++ ) { - if ( pCS->LinearCTStereoDble[n].at_num1 == nAtomRank1 && - pCS->LinearCTStereoDble[n].at_num2 == nAtomRank2 ) { - pCS->LinearCTStereoDble[n].parity = new_parity; -#if ( bRELEASE_VERSION == 0 ) - pCS->bExtract |= EXTR_CALC_USED_TO_REMOVE_PARITY; -#endif - m = -1; - break; - } - } - if ( m >= 0 ) { - ret = CT_STEREOCOUNT_ERR; /* */ - goto exit_function; - } - ret ++; - } - } else { - /* remove the parity */ - if ( !RemoveOneStereoBond( at, i1, /* atom number*/ n1 /* stereo bond ord. number*/ ) ) { - ret = CT_STEREOBOND_ERROR; /* */ - goto exit_function; - } - n1 --; /* cycle counter may temporarily become negative */ - /* Remove from the pCS */ - nAtomRank1 = inchi_max( nCanonRank[i1], nCanonRank[opposite_atom]); - nAtomRank2 = inchi_min( nCanonRank[i1], nCanonRank[opposite_atom]); - for ( n = 0, m = pCS->nLenLinearCTStereoDble-1; n <= m; n ++ ) { - if ( pCS->LinearCTStereoDble[n].at_num1 == nAtomRank1 && - pCS->LinearCTStereoDble[n].at_num2 == nAtomRank2 ) { - if ( n < m ) { /* remove pCS->LinearCTStereoDble[n] */ - memmove( pCS->LinearCTStereoDble + n, - pCS->LinearCTStereoDble + n + 1, - (m-n)*sizeof(pCS->LinearCTStereoDble[0]) ); - } - pCS->nLenLinearCTStereoDble --; -#if ( bRELEASE_VERSION == 0 ) - pCS->bExtract |= EXTR_CALC_USED_TO_REMOVE_PARITY; -#endif - m = -1; - break; - } - } - if ( m >= 0 ) { - ret = CT_STEREOCOUNT_ERR; /* */ - goto exit_function; - } - ret ++; - } - } else { - if ( !ret_failed ) { - ret_failed = (ret1<0)? ret1 : (ret2<0)? ret2 : 0; - } - if ( !RETURNED_ERROR(ret_failed) ) { - if ( RETURNED_ERROR( ret1 ) ) - ret_failed = ret1; - else - if ( RETURNED_ERROR( ret2 ) ) - ret_failed = ret2; - } - } - } - } - if ( nNeighMode == NEIGH_MODE_CHAIN && nNumEqRingNeigh && !RETURNED_ERROR(ret_failed) ) { - nNeighMode = NEIGH_MODE_RING; - goto second_pass; - } - -exit_function: - - return RETURNED_ERROR(ret_failed)? ret_failed : ret_failed? -(ret_failed+1) : ret; -} -/****************************************************************************/ -/* Remove stereo marks from the atoms that are calculated to be non-stereo */ -/* (can find two numberings that change only one stereo center parity) */ -int RemoveCalculatedNonStereoCenterParities( sp_ATOM *at, int num_atoms, int num_at_tg, - AT_RANK **pRankStack1, AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, - AT_RANK *nCanonRank, const AT_RANK *nSymmRank, - AT_RANK *nAtomNumberCanon, AT_RANK *nAtomNumberCanon1, AT_RANK *nAtomNumberCanon2, - NEIGH_LIST *nl, NEIGH_LIST *nl1, NEIGH_LIST *nl2, - AT_RANK *nVisited1, AT_RANK *nVisited2, - CANON_STAT *pCS, - int vABParityUnknown) -{ - int j, n, m, ret; - - int i, k, ret1, ret2, ret_failed=0, mode, max_mode; - AT_RANK nAtomRank1, neigh[MAX_NUM_STEREO_ATOM_NEIGH], nAvoidCheckAtom[2], nLength; - int nNeighMode = NEIGH_MODE_CHAIN; - int nNumEqRingNeigh = 0, bRingNeigh, bSymmNeigh, bParitiesInverted; - NEIGH_LIST *nl01, *nl02; - const AT_RANK *nSymmRank1, *nSymmRank2; - - ret = 0; - -second_pass: - for ( i = 0; i < num_atoms && !RETURNED_ERROR(ret_failed); i ++ ) { - if ( !at[i].parity || at[i].stereo_bond_neighbor[0] ) { - continue; - } - if ( at[i].valence > MAX_NUM_STEREO_ATOM_NEIGH ) { - continue; /* error: stereo center cannot have more than 4 neighbors */ /* */ - } - /* at[i1] is a stereo center */ - if ( !PARITY_CALCULATE(at[i].stereo_atom_parity) && !PARITY_ILL_DEF(at[i].stereo_atom_parity) ) { - continue; - } - /* neighbors sorted according to symm. ranks (primary key) and canon. ranks (secondary key), in descending order */ - /* sorting guarantees that for two constit. equ. neighbors canon. ranks of the first is greater */ - /* !!! previously (but not anymore) the canon. rank of neigh[0] was greater than the others !!! */ - for ( j = 0; j < at[i].valence; j ++ ) { - neigh[j] = nl[i][j+1]; /* sorting does NOT guarantee that canon. rank of neigh[0] is greater than others */ - } - /* - * mode = 0 => Standard approach: switch 2 neighbors - * 1 => Check for C2v reflection leading to parity inversion - * 2 => Check for C2 rotation preserving parities - * 3 => Check for S4 rotation/reflection leading to parity inversion - */ -#if ( CHECK_C2v_S4_SYMM == 1 ) - if ( nNeighMode = NEIGH_MODE_RING && at[i].valence == 4 && - nSymmRank[(int)neigh[0]] == nSymmRank[(int)neigh[1]] && - nSymmRank[(int)neigh[2]] == nSymmRank[(int)neigh[3]] && - !at[i].bCutVertex - ) { - if ( nSymmRank[(int)neigh[1]] == nSymmRank[(int)neigh[2]] ) { - max_mode = MAP_MODE_S4; - } else { - max_mode = inchi_max(MAP_MODE_C2v, MAP_MODE_C2); - } - } else { - max_mode = MAP_MODE_STD; - } -#else - max_mode = MAP_MODE_STD; -#endif - for ( j = 0; j < at[i].valence && at[i].parity && !RETURNED_ERROR(ret_failed); j ++ ) { - for ( k = j+1; k < at[i].valence && at[i].parity && !RETURNED_ERROR(ret_failed); k ++ ) { - for ( mode = 0; mode <= max_mode && at[i].parity && !RETURNED_ERROR(ret_failed); mode ++ ) { - if ( nSymmRank[(int)neigh[j]] != nSymmRank[(int)neigh[k]] ) { - continue; /* the two neighbors are not constitutionally identical */ - } - bRingNeigh = (at[(int)neigh[j]].nRingSystem == at[(int)neigh[k]].nRingSystem); - switch ( nNeighMode ) { - case NEIGH_MODE_CHAIN: - if ( bRingNeigh ) { - nNumEqRingNeigh ++; - continue; - } - nl01 = nl; - nl02 = nl; - nSymmRank1 = nSymmRank; - nSymmRank2 = nSymmRank; - break; - case NEIGH_MODE_RING: - if ( !bRingNeigh ) - continue; - /* break a tie between the two contitutionally equivalent neighbors, */ - /* refine the two partitions, sort neighbors lists nl1, nl2 */ - bSymmNeigh = BreakNeighborsTie( at, num_atoms, num_at_tg, MAX_ATOMS+1, i, - neigh, j, k, mode, - pRankStack1, pRankStack2, nTempRank, NeighList, nSymmRank, nCanonRank, - nl1, nl2, &pCS->lNumNeighListIter ); - if ( bSymmNeigh <= 0 ) { - if ( ret_failed > bSymmNeigh ) - ret_failed = bSymmNeigh; - continue; - } - nl01 = nl1; - nl02 = nl2; - nSymmRank1 = pRankStack1[0]; - nSymmRank2 = pRankStack2[0]; - break; - default: - return CT_STEREOCOUNT_ERR; /* */ - } - - /* initialize arrays */ - memset( nVisited1, 0, sizeof(nVisited1[0])*num_atoms ); - memset( nVisited2, 0, sizeof(nVisited2[0])*num_atoms ); - memset( nAtomNumberCanon1, 0, sizeof(nAtomNumberCanon1[0])*num_atoms ); - memset( nAtomNumberCanon2, 0, sizeof(nAtomNumberCanon2[0])*num_atoms ); - nLength = 1; - nVisited1[i] = i+1; /* start atom is same */ - nVisited2[i] = i+1; - nAtomNumberCanon1[i] = nLength; - nAtomNumberCanon2[i] = nLength; - nAvoidCheckAtom[0] = i; - nAvoidCheckAtom[1] = MAX_ATOMS+1; - - bParitiesInverted = (mode==MAP_MODE_C2v || mode==MAP_MODE_S4)? -1 : 0; - /* - if (nNeighMode==NEIGH_MODE_RING && at[i].valence==MAX_NUM_STEREO_ATOM_NEIGH) { - AT_RANK other_neigh[2]; - int n; - for ( m = n = 0; m < MAX_NUM_STEREO_ATOM_NEIGH; m ++ ) { - if ( at[i].neighbor[m] != neigh[j] && at[i].neighbor[m] != neigh[k] ) - other_neigh[n++] = at[i].neighbor[m]; - } - if ( nSymmRank[(int)other_neigh[0]] == nSymmRank[(int)other_neigh[1]] ) - bParitiesInverted = -1; - } - */ - /* allow matching inverted centers only in case all equivalent neighbors in same ring system */ - - ret2 = 0; /* initilize. 1/8/2002 */ - - if ( 0 < (ret1 = CreateCheckSymmPaths( at, (AT_RANK)i, neigh[j], (AT_RANK)i, neigh[k], - nAvoidCheckAtom, - nVisited1, nVisited2, nAtomNumberCanon1, nAtomNumberCanon2, - nl01, nl02, nSymmRank1, nSymmRank2, nCanonRank, &nLength, - &bParitiesInverted, mode ) ) && - 0 < (ret2 = CalculatedPathsParitiesAreIdentical( at, num_atoms, nSymmRank, - nCanonRank, nAtomNumberCanon, nAtomNumberCanon1, nAtomNumberCanon2, - nVisited1, nVisited2, (AT_RANK)MAX_ATOMS, (AT_RANK)i, - neigh[j], neigh[k], nNeighMode, - bParitiesInverted, mode, pCS, - vABParityUnknown) ) ) { - if ( ret2 & ( NOT_WELL_DEF_UNKN | NOT_WELL_DEF_UNDF ) ) { - /* possibly change the parity to unknown or undefined */ - int new_parity = (ret2 & NOT_WELL_DEF_UNKN)? vABParityUnknown /*AB_PARITY_UNKN*/: AB_PARITY_UNDF; - if ( PARITY_ILL_DEF(at[i].stereo_atom_parity) && - PARITY_VAL(at[i].stereo_atom_parity) > new_parity || - PARITY_CALCULATE(at[i].stereo_atom_parity) ) { - /* set new unknown or undefined parity */ - at[i].stereo_atom_parity = (at[i].stereo_atom_parity ^ PARITY_VAL(at[i].stereo_atom_parity)) | PARITY_VAL(new_parity); - at[i].parity = PARITY_VAL(new_parity); - /* Remove from pCS */ - nAtomRank1 = nCanonRank[i]; - for ( n = 0, m = pCS->nLenLinearCTStereoCarb-1; n <= m; n ++ ) { - if ( pCS->LinearCTStereoCarb[n].at_num == nAtomRank1 ) { - pCS->LinearCTStereoCarb[n].parity = PARITY_VAL(new_parity); - #if ( bRELEASE_VERSION == 0 ) - pCS->bExtract |= EXTR_CALC_USED_TO_REMOVE_PARITY; - #endif - m = -1; - break; - } - } - if ( m >= 0 ) { - ret = CT_STEREOCOUNT_ERR; /* */ - goto exit_function; - } - ret ++; /* number of removed or set unknown/undefined parities */ - } - } else { - RemoveOneStereoCenter( at, i /* atom number*/ ); - /* Remove from pCS */ - nAtomRank1 = nCanonRank[i]; - for ( n = 0, m = pCS->nLenLinearCTStereoCarb-1; n <= m; n ++ ) { - if ( pCS->LinearCTStereoCarb[n].at_num == nAtomRank1 ) { - if ( n < m ) { /* remove pCS->LinearCTStereoDble[n] */ - memmove( pCS->LinearCTStereoCarb + n, - pCS->LinearCTStereoCarb + n + 1, - (m-n)*sizeof(pCS->LinearCTStereoCarb[0]) ); - } - pCS->nLenLinearCTStereoCarb --; - #if ( bRELEASE_VERSION == 0 ) - pCS->bExtract |= EXTR_CALC_USED_TO_REMOVE_PARITY; - #endif - m = -1; - break; - } - } - if ( m >= 0 ) { - ret = CT_STEREOCOUNT_ERR; /* */ - goto exit_function; - } - ret ++; /* number of removed or set unknown/undefined parities */ - } - } else { - if ( !ret_failed ) { - if ( ret1 < 0 ) { - ret_failed = ret1; - } else - if ( ret2 < 0 ) { - ret_failed = ret2; - } - } - if ( !RETURNED_ERROR(ret_failed) ) { - if ( RETURNED_ERROR( ret1 ) ) - ret_failed = ret1; - else - if ( RETURNED_ERROR( ret2 ) ) - ret_failed = ret2; - } - } - } - } - } - } - if ( nNeighMode == NEIGH_MODE_CHAIN && nNumEqRingNeigh && !RETURNED_ERROR(ret_failed) ) { - nNeighMode = NEIGH_MODE_RING; - goto second_pass; - } - -exit_function: - - return RETURNED_ERROR(ret_failed)? ret_failed : ret_failed? -(ret+1) : ret; -} - -/**************************************************************************************/ -int RemoveCalculatedNonStereo( sp_ATOM *at, int num_atoms, int num_at_tg, - AT_RANK **pRankStack1, AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, - const AT_RANK *nSymmRank, AT_RANK *nCanonRank, - AT_RANK *nAtomNumberCanon, CANON_STAT *pCS, - int vABParityUnknown) -{ - NEIGH_LIST *nl = NULL, *nl1 = NULL, *nl2 = NULL; - AT_RANK *nVisited1 = NULL, *nVisited2 = NULL, *nAtomNumberCanon1 = NULL, *nAtomNumberCanon2 = NULL; - int nNumRemoved = 0, nTotRemoved = 0, ret = 0, ret1 = 0, ret2 = 0; - - if ( !AllocateForNonStereoRemoval( at, num_atoms, nSymmRank, nCanonRank, - &nAtomNumberCanon1, &nAtomNumberCanon2, - &nl, &nl1, &nl2, &nVisited1, &nVisited2 ) ) { - return CT_OUT_OF_RAM; /* */ - } - - do { - nNumRemoved = 0; - /* bonds */ - ret = RemoveCalculatedNonStereoBondParities( at, num_atoms, num_at_tg, - pRankStack1, pRankStack2, nTempRank, NeighList, - nCanonRank, nSymmRank, - nAtomNumberCanon, nAtomNumberCanon1, nAtomNumberCanon2, - nl, nl1, nl2, nVisited1, nVisited2, pCS, - vABParityUnknown); - if ( RETURNED_ERROR( ret ) ) { - goto exit_function; - } - if ( ret < 0 ) { - if ( ret < ret1 ) { /* */ - ret1 = ret; - } - ret = - ( ret + 1 ); /* number of removed */ - } - nNumRemoved += ret; - - /* centers */ - ret = RemoveCalculatedNonStereoCenterParities( at, num_atoms, num_at_tg, - pRankStack1, pRankStack2, nTempRank, NeighList, - nCanonRank, nSymmRank, - nAtomNumberCanon, nAtomNumberCanon1, nAtomNumberCanon2, - nl, nl1, nl2, nVisited1, nVisited2, pCS, - vABParityUnknown); - if ( RETURNED_ERROR( ret ) ) { - goto exit_function; - } - if ( ret < 0 ) { - if ( ret < ret2 ) { /* */ - ret2 = ret; - } - ret = - ( ret + 1 ); /* number of removed */ - } - nNumRemoved += ret; - - nTotRemoved += nNumRemoved; - - } while ( nNumRemoved ); - - if ( !RETURNED_ERROR( ret1 ) && !RETURNED_ERROR( ret2 ) ) { - ret = inchi_min( ret1, ret2 ); - ret = (ret >= 0)? nTotRemoved : -(1+nTotRemoved); - } - -exit_function: - - DeAllocateForNonStereoRemoval( &nAtomNumberCanon1, &nAtomNumberCanon2, &nl, &nl1, &nl2, &nVisited1, &nVisited2 ); - - return ret; -} -#endif /* } REMOVE_CALC_NONSTEREO */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichimap4.c b/INCHI-1-SRC/INCHI_API/inchi_dll/ichimap4.c deleted file mode 100644 index b4c18d2..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichimap4.c +++ /dev/null @@ -1,1669 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - -#include "mode.h" - -#include "incomdef.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichicant.h" -#include "ichicomn.h" - -#include "ichicomp.h" - - - - -#define SB_DEPTH 6 -/************************************************ - map_stereo_bonds4 and map_stereo_atoms4 use - the following members of CANON_STAT *pCS: - - pCS->bKeepSymmRank // ??? almost unused, replaced with nSymmStereo != NULL ??? - pCS->bFirstCT - pCS->bStereoIsBetter - pCS->lNumNeighListIter - pCS->lNumBreakTies - pCS->lNumRejectedCT - pCS->lNumTotCT - pCS->lNumEqualCT - pCS->lNumDecreasedCT - pCS->bExtract (bRELEASE_VERSION == 0) - pCS->ulTimeOutTime - - pCS->bRankUsedForStereo - pCS->bAtomUsedForStereo - - pCS->LinearCTStereoDble - pCS->LinearCTStereoCarb - pCS->nLenLinearCTStereoCarb - pCS->nLenLinearCTStereoDble - - pCS->nPrevAtomNumber - ************************************************/ -/********************************************************************************/ -int map_stereo_bonds4 ( - sp_ATOM *at, int num_atoms, int num_at_tg, int num_max, int bAllene, - const AT_RANK *nCanonRankFrom, const AT_RANK *nAtomNumberCanonFrom, /* non-stereo canon ranking */ - AT_RANK *nCanonRankTo, /* output canonical stereo numbering*/ - const AT_RANK *nSymmRank, AT_RANK **pRankStack1/*from*/, AT_RANK **pRankStack2/*to*/, - AT_RANK *nTempRank, int nNumMappedRanksInput, - AT_RANK *nSymmStereo, NEIGH_LIST *NeighList, - CANON_STAT *pCS, CUR_TREE *cur_tree, int nNumMappedBonds, - int vABParityUnknown) -{ - int nTotSuccess = 0; /* 1=>full mapping has been completed; - * 2=>obtained a better stereo; - * 4=>restart (stereo bond or atom removed from the stereo CT) - */ - int tpos1; - AT_STEREO_DBLE prevBond; - tpos1 = CurTreeGetPos( cur_tree ); - -total_restart: - - if ( !nNumMappedBonds ) { - - memset( pCS->bRankUsedForStereo, 0, sizeof( pCS->bRankUsedForStereo[0] )*num_atoms ); - SetUseAtomForStereo( pCS->bAtomUsedForStereo, at, num_atoms ); - - if ( pCS->bFirstCT && nSymmStereo && !pCS->bKeepSymmRank ) { - int i; - for ( i = 0; i < num_at_tg; i ++ ) { - /* nSymmStereo[i] = min. {k | at[k] stereo eq. to at[i]} */ - nSymmStereo[i] = i; /* for union-join to keep track of stereo-equivalent atoms */ - } - } - } - - - if ( nNumMappedBonds < pCS->nLenLinearCTStereoDble ) { - - int at_rank1, at_rank2, bStereoIsBetterWasSetHere; - /* AT_RANK *nRankFrom=*pRankStack1++, AT_RANK *nAtomNumberFrom=pRankStack1++; */ - /* AT_RANK *nRankTo =*pRankStack2++, AT_RANK *nAtomNumberTo =pRankStack2++; */ - AT_RANK canon_min1, canon_min2; - int bFirstCanonRank; - int i, j, j1, j2, at_from1, at_from2, at_to1, at_to2, iMax, c; - int nStackPtr[SB_DEPTH], nNumMappedRanks[SB_DEPTH], LastMappedTo1; - int istk, istk2, istk3, bAddStack, nNumAtTo1Success; - int ret1, ret2, parity1, parity2; - - AT_RANK at_rank_canon1; /* = pCS->LinearCTStereoDble[nNumMappedBonds].at_num1; */ /* canonical numbers of atoms */ - AT_RANK at_rank_canon2; /* = pCS->LinearCTStereoDble[nNumMappedBonds].at_num2; */ /* adjacent to the stereogenic bond */ - int nNumChoices, nNumUnkn, nNumUndf, nNumBest, nNumWorse, nNumCalc, sb_parity_calc; - int stereo_bond_parity, prev_stereo_bond_parity, pass, bAllParitiesIdentical, bAllParitiesIdentical2; - AT_STEREO_DBLE prevBond2; - - prevBond = pCS->LinearCTStereoDble[nNumMappedBonds]; - bFirstCanonRank=1; - canon_min1=canon_min2=0; -/* - // find candidates for atom_from1, atom_to1; they must have identical mapping ranks - at_rank1=pRankStack1[0][at_from1=nAtomNumberCanonFrom[(int)at_rank_canon1 - 1]]; // rank "from" for mapping - at_rank2=pRankStack1[0][at_from2=nAtomNumberCanonFrom[(int)at_rank_canon2 - 1]]; // rank "from" for mapping -*/ - if ( nNumMappedBonds ) { - at_rank_canon1 = pCS->LinearCTStereoDble[nNumMappedBonds-1].at_num1; - at_rank_canon2 = pCS->LinearCTStereoDble[nNumMappedBonds-1].at_num2; - } else { - at_rank_canon1 = 0; - at_rank_canon2 = 0; - } - goto bypass_next_canon_ranks_check; - -next_canon_ranks: - - /* Save time: avoid calling Next_SB_At_CanonRanks2() */ - if ( !pCS->bStereoIsBetter /* ??? && !pCS->bFirstCT ???*/ && - at_rank_canon1 > pCS->LinearCTStereoDble[nNumMappedBonds].at_num1 || - at_rank_canon1 == pCS->LinearCTStereoDble[nNumMappedBonds].at_num1 && - at_rank_canon2 >= pCS->LinearCTStereoDble[nNumMappedBonds].at_num2 ) { - - if ( !nTotSuccess ) { - pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond; - } - CurTreeSetPos( cur_tree, tpos1 ); - return nTotSuccess; - } - -bypass_next_canon_ranks_check: - - CurTreeSetPos( cur_tree, tpos1 ); - - /* find next available canon. numbers for a stereogenic bond pair of atoms */ - /* process allenes AFTER all double bonds and odd-number-of-double-bonds cumulenes */ - if ( !(ret1 = Next_SB_At_CanonRanks2( &at_rank_canon1, &at_rank_canon2, /* canonical numbers */ - &canon_min1, &canon_min2, - &bFirstCanonRank, pCS->bAtomUsedForStereo, - pRankStack1, pRankStack2, - nCanonRankFrom, nAtomNumberCanonFrom, - at, num_atoms, bAllene ) ) ) { - /* failed to find next stereo bond to assign parity */ - if ( !bAllene && bFirstCanonRank ) { - /* all stereobond have been processed; try to find allene to continue */ - AT_RANK at_rank_canon1_Allene = 0, canon_min1_Allene = 0; - AT_RANK at_rank_canon2_Allene = 0, canon_min2_Allene = 0; - if ( ret1 = Next_SB_At_CanonRanks2( &at_rank_canon1_Allene, &at_rank_canon2_Allene, - &canon_min1_Allene, &canon_min2_Allene, - &bFirstCanonRank, pCS->bAtomUsedForStereo, - pRankStack1, pRankStack2, - nCanonRankFrom, nAtomNumberCanonFrom, - at, num_atoms, 1 ) ) { - at_rank_canon1 = at_rank_canon1_Allene; - at_rank_canon2 = at_rank_canon2_Allene; - canon_min1 = canon_min1_Allene; - canon_min2 = canon_min2_Allene; - bAllene = 1; /* switch to allenes */ - } - } - } - - if ( !ret1 || !pCS->bStereoIsBetter && - (at_rank_canon1 > pCS->LinearCTStereoDble[nNumMappedBonds].at_num1 || - at_rank_canon1 == pCS->LinearCTStereoDble[nNumMappedBonds].at_num1 && - at_rank_canon2 > pCS->LinearCTStereoDble[nNumMappedBonds].at_num2 ) ) { - /* new ranks provide greater pCS->LinearCTStereoDble[nNumMappedBonds] and therefore rejected */ - if ( !nTotSuccess ) { - pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond; /* restore stereo bond CT for the current bond */ - } - return nTotSuccess; - } - /* current stereo bond initialization */ - nNumChoices = 0; - nNumUnkn = 0; - nNumUndf = 0; - nNumBest = 0; - nNumWorse = 0; - nNumCalc = 0; - pass=0; - prev_stereo_bond_parity = 0; - - at_rank1=pRankStack1[0][at_from1=nAtomNumberCanonFrom[(int)at_rank_canon1 - 1]]; /* atom 1 rank "from" for mapping */ - at_rank2=pRankStack1[0][at_from2=nAtomNumberCanonFrom[(int)at_rank_canon2 - 1]]; /* atom 2 rank "from" for mapping */ - /* we are going to map bond (at[at_from1], at[at_from2]) and - canonical ranks of its atoms (at_rank_canon1, at_rank_canon2) - onto a stereogenic bond (at[at_to1], at[at_to2]) - */ - iMax = at_rank1-1; - /* test correctness: sorted pRankStack2[0][] and pRankStack1[0][] should have same ranks for both atoms */ - if ( at_rank1 != pRankStack2[0][pRankStack2[1][at_rank1-1]] || - at_rank2 != pRankStack2[0][pRankStack2[1][at_rank2-1]] ) { - /* program error: "from" and "to" mapping ranks are not equal */ - return CT_STEREOCOUNT_ERR; /* */ - } - /* -- do not check stereo features of "from" atoms: - -- in case of "bond/charge isomerism" they may be missing. - if ( !at[at_from1].stereo_bond_neighbor[0] || - !at[at_from2].stereo_bond_neighbor[0] ) - return CT_STEREOCOUNT_ERR; // program error - */ - - /* find out if we have a choice in mapping: check all possible pairs (at_to1, at_to2) - such that at_from1 is possibly constitutionally equivalent to at_to1, at_from2 to at_to2 */ - for ( j1 = 0; j1 <= iMax && at_rank1 == pRankStack2[0][at_to1=pRankStack2[1][iMax-j1]]; j1 ++ ) { - if ( !at[at_to1].stereo_bond_neighbor[0] ) - continue; /* at_to1 does not belong to a stereo bond */ - for( j2 = 0; j2 < MAX_NUM_STEREO_BONDS && - (at_to2 =at[at_to1].stereo_bond_neighbor[j2]); j2 ++ ) { - at_to2 --; - if ( pRankStack1[0][at_from2] != pRankStack2[0][at_to2] ) - continue; /* at_from2 cannot be mapped on at_to2 */ - stereo_bond_parity = PARITY_VAL(at[at_to1].stereo_bond_parity[j2]); - i = 0; - switch(stereo_bond_parity) { - - case AB_PARITY_UNDF: nNumUndf ++; - break; /* 4 */ - case AB_PARITY_UNKN: nNumUnkn ++; - break; /* 3 (occurs if forced different to UNDF)*/ - - case BEST_PARITY: nNumBest ++; break; /* 1 */ - case WORSE_PARITY: nNumWorse ++; break; /* 2 */ - case AB_PARITY_CALC: nNumCalc ++; break; /* 6 */ - case AB_PARITY_NONE: i ++; break; /* 0 */ - } - nNumChoices += !i; - } - } - if ( nNumChoices != nNumCalc + nNumUndf + nNumUnkn + nNumBest + nNumWorse ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - if ( !nNumChoices ) { - goto next_canon_ranks; - } - /* Determine the first parity to search */ - sb_parity_calc = ( nNumCalc > 0 )? BEST_PARITY : 0; - - /* ============================================================== - Search sequence: sb_parity_calc stereo_bond_parity - ============================================================== - BEST_PARITY (calc) BEST_PARITY BEST_PARITY - BEST_PARITY (known) BEST_PARITY WORSE_PARITY or 0 - WORSE_PARITY (calc) WORSE_PARITY WORSE_PARITY - WORSE_PARITY (known) WORSE_PARITY 0 - AB_PARITY_UNKN(known) AB_PARITY_UNKN 0 - AB_PARITY_UNDF(known) AB_PARITY_UNDF 0 - - if (sb_parity_calc==stereo_bond_parity) then "calc" else "known" - */ - -repeat_all: - - if ( !nNumMappedBonds ) - pCS->bStereoIsBetter = 0; /* the first stereo feature in the canonical CT; moved here 7-13-2002 */ - - if ( !pass ++ ) { - /* select the smallest (best) parity to search */ - if ( sb_parity_calc ) { - stereo_bond_parity = BEST_PARITY; - } else { - stereo_bond_parity = nNumBest? BEST_PARITY : - nNumWorse? WORSE_PARITY : - nNumUnkn? AB_PARITY_UNKN : - nNumUndf? AB_PARITY_UNDF : AB_PARITY_NONE; - } - } else { - /* second pass: since the first pass failed, search for a worse result */ - prev_stereo_bond_parity = stereo_bond_parity; - i = NextStereoParity2Test( &stereo_bond_parity, &sb_parity_calc, - nNumBest, nNumWorse, nNumUnkn, nNumUndf, nNumCalc, vABParityUnknown); - switch ( i ) { - case 0: - break; /* obtained next parity to test */ - case 1: - goto next_canon_ranks; - default: - return i; /* program error */ - } - } - if ( stereo_bond_parity == AB_PARITY_NONE ) { - /* error? */ - return CT_STEREOCOUNT_ERR; /* */ - } - /* check if the new requested parity is good (small) enough */ - if ( !pCS->bStereoIsBetter ) { - c = CompareLinCtStereoDoubleToValues( nTotSuccess? pCS->LinearCTStereoDble+nNumMappedBonds : &prevBond, - at_rank_canon1, at_rank_canon2, (U_CHAR)stereo_bond_parity ); - if ( c < 0 ) { - if ( !nTotSuccess ) { - pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond; - } - CurTreeSetPos( cur_tree, tpos1 ); - return nTotSuccess; - } - } - - bAllParitiesIdentical = 0; - bAllParitiesIdentical2 = 0; - LastMappedTo1 = -1; - bStereoIsBetterWasSetHere = 0; - istk = istk2 = istk3 = 0; - - if ( !nNumMappedBonds && prev_stereo_bond_parity != stereo_bond_parity ) - pCS->bStereoIsBetter = 0; /* the first stereo feature in the canonical CT; moved here 5-24-2002 */ - - if ( prev_stereo_bond_parity != stereo_bond_parity ) { - CurTreeSetPos( cur_tree, tpos1 ); /* start over */ - } - - /* Mapping: here at_rank1 = nRankTo, at_to1 = nAtomNumberTo */ - for ( j1 = 0; j1 <= iMax && at_rank1 == pRankStack2[0][at_to1=pRankStack2[1][iMax-j1]]; j1 ++ ) { - nNumAtTo1Success = 0; - if ( !at[at_to1].stereo_bond_neighbor[0] ) - continue; /* at_to1 does not belong to a stereo bond */ - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) && - 1 == CurTreeIsLastAtomEqu( cur_tree, at_to1, nSymmStereo ) ) { - /* at_to1 is known to be stereogenically equivalent to another atom tried with at_rank_canon1 */ - continue; - } - bAllParitiesIdentical2 = 0; - for( j2 = 0; j2 < MAX_NUM_STEREO_BONDS && (at_to2 =at[at_to1].stereo_bond_neighbor[j2]); j2 ++ ) { - EQ_NEIGH EN1[2], EN2[2]; - int bond_parity, num1, num2; - AT_RANK at_rank_canon_n1, at_rank_canon_n2; - - at_to2 --; - if ( pRankStack1[0][at_from2] != pRankStack2[0][at_to2] ) - continue; /* at_from2 cannot be mapped on at_to2 even without mapping at_from1 to at_to1 */ - - /* check whether the bond parity corresponds to the requested bond parity */ - if ( PARITY_KNOWN(at[at_to1].stereo_bond_parity[j2]) ) { - if ( stereo_bond_parity == sb_parity_calc ) { - continue; /* requested parity to be calculated, found known parity */ - } - if ( stereo_bond_parity != PARITY_VAL(at[at_to1].stereo_bond_parity[j2]) ) { - continue; /* parity differs from the requested parity */ - } - } else - if ( PARITY_CALCULATE( at[at_to1].stereo_bond_parity[j2]) ) { - if ( stereo_bond_parity != sb_parity_calc ) { - continue; /* requested known parity, found parity to be calculated */ - } - } else { - return CT_STEREOCOUNT_ERR; /* unknown parity type */ /* */ - } - /* initialize stack pointer nStackPtr[istk] for "hand-made" recursion */ - /* stacks are pRankStack1[], pRankStack2[], nNumMappedRanks[] */ - istk = 0; - nStackPtr[0] = 0; - nNumMappedRanks[0] = nNumMappedRanksInput; - bAddStack = 0; - bAllParitiesIdentical = ((at[at_to1].stereo_bond_parity[j2] & KNOWN_PARITIES_EQL )) && - PARITY_KNOWN(at[at_to1].stereo_bond_parity[j2]); - - if ( !bAllParitiesIdentical && !nNumCalc && - (!nNumUndf + !nNumUnkn + !nNumBest + !nNumWorse )==3) { - /* only one kind of bond parity is present; check whether all parities are really same */ - bAllParitiesIdentical = All_SB_Same( at_rank_canon1, at_rank_canon2, /* canonical numbers */ - pRankStack1, pRankStack2, - nAtomNumberCanonFrom, at ); - if ( bAllParitiesIdentical < 0 ) { - return CT_STEREOCOUNT_ERR; /* */ - } - } - - /***************************************************************** - * do the mapping only if parities are not same - */ - if ( !bAllParitiesIdentical ) { - /* map atom 1 or reuse previous mapping */ - if ( LastMappedTo1 != at_to1 ) { - /* avoid repetitve mapping to the same first at_to1 using LastMappedTo1 variable */ - /* map atom 1 */ - ret1 = map_an_atom2( num_at_tg, num_max, at_from1, at_to1, - nTempRank, nNumMappedRanks[istk], &nNumMappedRanks[istk+1], pCS, - NeighList, pRankStack1+nStackPtr[istk], pRankStack2+nStackPtr[istk], - &bAddStack ); - if ( RETURNED_ERROR(ret1) ) { - return ret1; /* error */ - } - nStackPtr[istk+1] = nStackPtr[istk]+bAddStack; - LastMappedTo1 = at_to1; - if ( bAddStack ) { - if ( tpos1 == CurTreeGetPos( cur_tree ) || - 0 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { - CurTreeAddRank( cur_tree, at_rank_canon1 ); - } - CurTreeAddAtom( cur_tree, at_to1 ); - } - } - istk ++; /* = 1 */ - /* check if we can map atom 2 */ - if ( pRankStack1[nStackPtr[istk]][at_from2] != pRankStack2[nStackPtr[istk]][at_to2] ) { - /* - * This may happen when: - * A) Charge/bond isomerism, for example cyclopentadiene(-), or - * B) possibly stereogenic bond in an alternating ring has heighbors - * in 2 symmetrically attached rings. - * Such an alternating bond cannot be mapped on possibly stereogenic bond - * that has neighbors belonging to 1 of the symmetrically attached rings only. - * For example: - * A---B---C---D If all atoms are Carbons then B, C, F, G are constitutionally - * || || || || equivalent. However, bonds B-C, F-G are not equivalent to - * || || || || B-F and C-G and cannot be mapped on them. - * E---F---G---H If at_from1=B, at_from2=F, at_to1=B, then at_from2 cannot be mapped on at_to2=C - * If at_from1=B, at_from2=F, at_to1=C, then at_from2 cannot be mapped on at_to2=B - * etc. - */ - if ( sb_parity_calc != stereo_bond_parity) { - /* can be passed only once for each bond */ - nNumChoices --; - nNumUndf -= (stereo_bond_parity == AB_PARITY_UNDF); - nNumUnkn -= (stereo_bond_parity == AB_PARITY_UNKN); - nNumBest -= (stereo_bond_parity == BEST_PARITY); - nNumWorse-= (stereo_bond_parity == WORSE_PARITY); - /* nNumCalc = nNumChoices - (nNumUndf + nNumUnkn + nNumBest + nNumWorse); */ - } else - if ( sb_parity_calc == BEST_PARITY ) { - /* can be passed 2 times: for BEST_PARITY and WORSE_PARITY in this order */ - nNumChoices --; /* do not repeate for WORSE_PARITY */ - nNumCalc --; - } - continue; /* Happens for ID=80036,80253,91354,95532,101532,103788 */ - } - if ( nStackPtr[istk] > nStackPtr[istk-1] ) { - bAllParitiesIdentical2 = All_SB_Same( at_rank_canon1, at_rank_canon2, - pRankStack1+nStackPtr[istk], pRankStack2+nStackPtr[istk], - nAtomNumberCanonFrom, at ); - if ( bAllParitiesIdentical2 < 0 ) { - return CT_STEREOBOND_ERROR; /* */ - } - } else { - bAllParitiesIdentical2 = 0; - } - if ( bAllParitiesIdentical2 ) { - /* do no mapping when all equivalent bonds have same parity */ - /* stereo_bond_parity = PARITY_VAL(at[at_to1].stereo_bond_parity[j2]); */ - ClearPreviousMappings( pRankStack1+nStackPtr[istk]+2 ); - } else { - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon2 ) && - 1 == CurTreeIsLastAtomEqu( cur_tree, at_to2, nSymmStereo ) ) { - continue; - } - /* map atom 2 */ - ret2 = map_an_atom2( num_at_tg, num_max, at_from2, at_to2, - nTempRank, nNumMappedRanks[istk], &nNumMappedRanks[istk+1], pCS, - NeighList, pRankStack1+nStackPtr[istk], pRankStack2+nStackPtr[istk], - &bAddStack ); - if ( RETURNED_ERROR(ret2) ) { - return ret2; /* program error */ - } - nStackPtr[istk+1] = nStackPtr[istk]+bAddStack; - istk ++; /* = 2 */ - if ( bAddStack ) { - if ( tpos1 == CurTreeGetPos( cur_tree ) || - 0 == CurTreeIsLastRank( cur_tree, at_rank_canon2 ) ) { - CurTreeAddRank( cur_tree, at_rank_canon2 ); - } - CurTreeAddAtom( cur_tree, at_to2 ); - } - } - } else { - /* do no mapping when all equivalent bonds have same parity */ - /* stereo_bond_parity = PARITY_VAL(at[at_to1].stereo_bond_parity[j2]); */ - ClearPreviousMappings( pRankStack1+2 ); - } - - /* we have a precalculated (known) bond parity */ - - - /************************************************************ - * - * Known Bond Parity case: do not map stereo bond neighbors - */ - if ( stereo_bond_parity != sb_parity_calc ) /* parity is known */ - { - /* accept bond parity and do not map the neighbors */ - bond_parity = stereo_bond_parity; - /* same code as under " make a decision to accept current mapping" comment below */ - /* with one exception: istk instead of istk3 */ - c = CompareLinCtStereoDoubleToValues( pCS->LinearCTStereoDble+nNumMappedBonds, - at_rank_canon1, at_rank_canon2, (U_CHAR)bond_parity ); - if ( c < 0 && !pCS->bStereoIsBetter ) { - - /* reject */ - - pCS->lNumRejectedCT ++; - /* remove failed atom2 from the tree */ - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon2 ) ) { - CurTreeRemoveIfLastAtom( cur_tree, at_to2 ); - CurTreeRemoveLastRankIfNoAtoms( cur_tree ); - } - continue; /* to next at_to2; Reject this at_to2: not a minimal CT. */ - } else { - - /* accept */ - - if ( c > 0 && !pCS->bStereoIsBetter ) { - /* bond entry is less than the previusly found */ - pCS->bStereoIsBetter = bStereoIsBetterWasSetHere = 1; - prevBond2 = pCS->LinearCTStereoDble[nNumMappedBonds]; - } - pCS->LinearCTStereoDble[nNumMappedBonds].at_num1 = at_rank_canon1; - pCS->LinearCTStereoDble[nNumMappedBonds].at_num2 = at_rank_canon2; - pCS->LinearCTStereoDble[nNumMappedBonds].parity = bond_parity; - /* recursive call */ - pCS->bRankUsedForStereo[at_from1] ++; - pCS->bRankUsedForStereo[at_from2] ++; - if ( !bAllParitiesIdentical ) { - pCS->bAtomUsedForStereo[at_to1] --; - pCS->bAtomUsedForStereo[at_to2] --; - } - ret2 = map_stereo_bonds4 ( at, num_atoms, num_at_tg, num_max, bAllene, nCanonRankFrom, nAtomNumberCanonFrom, nCanonRankTo, - nSymmRank, pRankStack1+nStackPtr[istk], pRankStack2+nStackPtr[istk], - nTempRank, nNumMappedRanks[istk], nSymmStereo, NeighList, - pCS, cur_tree, nNumMappedBonds+1 , - vABParityUnknown); - if ( !bAllParitiesIdentical ) { - pCS->bAtomUsedForStereo[at_to1] ++; - pCS->bAtomUsedForStereo[at_to2] ++; - } - pCS->bRankUsedForStereo[at_from1] --; - pCS->bRankUsedForStereo[at_from2] --; - if ( ret2 == 4 ) { - if ( nNumMappedBonds ) { - return ret2; - } else { - pCS->bFirstCT = 1; - goto total_restart; - } - } - - if ( RETURNED_ERROR(ret2) ) { - if ( ret2 == CT_TIMEOUT_ERR ) - return ret2; - else - return ret2; /* program error */ - } - if ( ret2 > 0 ) { - nTotSuccess |= 1; - nNumAtTo1Success ++; - if ( bStereoIsBetterWasSetHere || (ret2 & 2) ) { - CurTreeKeepLastAtomsOnly( cur_tree, tpos1, 1 ); /* start over */ - nTotSuccess |= 2; /* Obtained a smaller CT */ - } - } else { - if ( bStereoIsBetterWasSetHere ) { /* rollback */ - pCS->bStereoIsBetter = 0; - pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond2; - } - /* remove failed atom2 from the tree */ - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon2 ) ) { - CurTreeRemoveIfLastAtom( cur_tree, at_to2 ); - CurTreeRemoveLastRankIfNoAtoms( cur_tree ); - } - /* - if ( 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { - CurTreeRemoveLastAtom( cur_tree, at_to1 ); - CurTreeRemoveLastRankIfNoAtoms( cur_tree ); - } - */ - } - bStereoIsBetterWasSetHere = 0; - } - if ( bAllParitiesIdentical || bAllParitiesIdentical2 ) { - break; /* j2 cycle, at_to2 (no need to repeat) */ - } - continue; /* to next at_to2 */ - } - /*************************************************************************** - * - * Unknown Bond Parity case: may need to map stereo bond neighbors - * - **************************************************************************** - * Ranks are not known in advance - * check if at_from1/at_to1 half-bond has neighbors with equal mapping ranks - */ - - parity1 = parity_of_mapped_half_bond( at_from1, at_to1, at_from2, at_to2, at, &EN1[0], - nCanonRankFrom, pRankStack1[nStackPtr[istk]], pRankStack2[nStackPtr[istk]] ); - /* old approach -- before E/Z parities - parity1 = parity_of_mapped_atom2( at_from1, at_to1, at, &EN1[0], - nCanonRankFrom, pRankStack1[nStackPtr[istk]], pRankStack2[nStackPtr[istk]] ); - */ - /* the following commented out statement is not needed here. */ - /* parity2 = parity_of_mapped_atom2( at_from2, at_to2, at, &EN2[0], - nCanonRankFrom, pRankStack1[nStackPtr[istk]], - pRankStack2[nStackPtr[istk]] ); - */ - if ( !parity1 ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - num1 = parity1 > 0? 1:2; /* parity < 0 means additional mapping is needed to set parity */ - /* --- try all possible mappings of the stereo bond ending atoms' neighbors --- */ - at_rank_canon_n1 = 0; - at_rank_canon_n2 = 0; - for ( i = 0; i < num1; i ++ ) { - int at_from_n1, at_to_n1, at_no_n1_num_success = 0; - istk2 = istk; - if ( num1 == 2 ) { - at_rank_canon_n1 = nCanonRankFrom[EN1[0].from_at]; - /* an additional neighbor mapping is necessary; */ - /* we need to map only one at_from1 neighbor to make all neighbors have different ranks */ - - at_from_n1 = EN1[0].from_at; - at_to_n1 = EN1[0].to_at[i]; - - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n1 ) && - 1 == CurTreeIsLastAtomEqu( cur_tree, at_to_n1, nSymmStereo ) ) - continue; - /* - if ( nSymmStereo && !pCS->bFirstCT ) { - if ( i && nSymmStereo[at_to_n1] == nSymmStereo[(int)EN1[0].to_at[0]] ) { - continue; // do not test stereo equivalent atoms except the first one - } - } - */ - /* neighbors are tied. Untie them by breaking a tie on ONE of them. */ - ret1 = map_an_atom2( num_at_tg, num_max, at_from_n1, at_to_n1, - nTempRank, nNumMappedRanks[istk2], &nNumMappedRanks[istk2+1], pCS, - NeighList, pRankStack1+nStackPtr[istk2], pRankStack2+nStackPtr[istk2], - &bAddStack ); - if ( RETURNED_ERROR(ret1) ) { - return ret1; /* program error */ /* */ - } - nStackPtr[istk2+1] = nStackPtr[istk2] + bAddStack; - istk2 ++; /* <= 3 */ - /* debug */ - if ( istk2 >= SB_DEPTH ) { - return CT_OVERFLOW; /* program error */ /* */ - } - if ( bAddStack ) { - if ( tpos1 == CurTreeGetPos( cur_tree ) || - 0 == CurTreeIsLastRank( cur_tree, at_rank_canon_n1 ) ) { - CurTreeAddRank( cur_tree, at_rank_canon_n1 ); - } - CurTreeAddAtom( cur_tree, at_to_n1 ); - } - - - /* now that all at_from1 neighbors have been mapped the parity must be defined */ - parity1 = parity_of_mapped_half_bond( at_from1, at_to1, at_from2, at_to2, at, &EN1[1], - nCanonRankFrom, pRankStack1[nStackPtr[istk2]], pRankStack2[nStackPtr[istk2]] ); - if ( parity1 <= 0 ) - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } else { - nNumMappedRanks[istk2+1] = nNumMappedRanks[istk2]; - nStackPtr[istk2+1] = nStackPtr[istk2]; - istk2 ++; /* <= 3 */ - } - - /* check if at_from2/at_to2 half-bond has neighbors with equal mapping ranks */ - parity2 = parity_of_mapped_half_bond( at_from2, at_to2, at_from1, at_to1, at, &EN2[0], - nCanonRankFrom, pRankStack1[nStackPtr[istk2]], pRankStack2[nStackPtr[istk2]] ); - if ( !parity2 ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - num2 = parity2 > 0? 1:2; - at_rank_canon_n2 = 0; - for ( j = 0; j < num2; j ++ ) { - int at_from_n2, at_to_n2; - istk3 = istk2; - if ( num2 == 2 ) { - at_rank_canon_n2 = nCanonRankFrom[EN2[0].from_at]; - /* we need to map only one at_from2 neighbor to make its neighbors have different ranks */ - at_from_n2 = EN2[0].from_at; - at_to_n2 = EN2[0].to_at[j]; - - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n2 ) && - 1 == CurTreeIsLastAtomEqu( cur_tree, at_to_n2, nSymmStereo ) ) - continue; - - /* - if ( nSymmStereo && !pCS->bFirstCT ) { - if ( j && nSymmStereo[at_to_n2] == nSymmStereo[(int)EN2[0].to_at[0]] ) { - continue; // do not test stereo equivalent atoms except the first one - } - } - */ - /* neighbors are tied. Untie them by breaking a tie on ONE of them. */ - ret1 = map_an_atom2( num_at_tg, num_max, at_from_n2, at_to_n2, - nTempRank, nNumMappedRanks[istk3], &nNumMappedRanks[istk3+1], pCS, - NeighList, pRankStack1+nStackPtr[istk3], - pRankStack2+nStackPtr[istk3], - &bAddStack ); - if ( RETURNED_ERROR(ret1) ) { - return ret1; /* program error */ - } - nStackPtr[istk3+1] = nStackPtr[istk3]+bAddStack; - istk3 ++; /* <= 4 */ - - if ( bAddStack ) { - if ( tpos1 == CurTreeGetPos( cur_tree ) || - 0 == CurTreeIsLastRank( cur_tree, at_rank_canon_n2 ) ) { - CurTreeAddRank( cur_tree, at_rank_canon_n2 ); - } - CurTreeAddAtom( cur_tree, at_to_n2 ); - } - - parity2 = parity_of_mapped_half_bond( at_from2, at_to2, at_from1, at_to1, at, &EN2[1], - nCanonRankFrom, pRankStack1[nStackPtr[istk3]], pRankStack2[nStackPtr[istk3]] ); - if ( parity2 <= 0 ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - } else { - /* no additional mapping is needed to set atom's parity */ - nNumMappedRanks[istk3+1] = nNumMappedRanks[istk3]; - nStackPtr[istk3+1] = nStackPtr[istk3]; - istk3 ++; /* <= 4 */ - } - - /******************************************************************* - * at this point the stereo bond is fully mapped to find its parity - *******************************************************************/ - - if ( parity1 <= 0 || parity2 <= 0 ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - - /* find current bond parity AB_PARITY_ODD */ - if ( ATOM_PARITY_WELL_DEF(parity1) && ATOM_PARITY_WELL_DEF(parity2) ) { - bond_parity = 2 - (parity1 + parity2)%2; - } else { - bond_parity = inchi_max(parity1, parity2); - } - if ( ATOM_PARITY_WELL_DEF(bond_parity) && at[at_to1].stereo_bond_z_prod[j2] < 0 ) - bond_parity = 2 - (bond_parity+1)%2; /* invert the bond parity */ - - - /******************************************************** - * make a decision whether to accept the current mapping - */ - c = CompareLinCtStereoDoubleToValues( pCS->LinearCTStereoDble+nNumMappedBonds, - at_rank_canon1, at_rank_canon2, (U_CHAR)bond_parity ); - if ( sb_parity_calc != bond_parity || - c < 0 && !pCS->bStereoIsBetter ) { - /* reject */ - pCS->lNumRejectedCT ++; - /* remove failed atom2 from the tree */ - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n2 ) ) { - CurTreeRemoveIfLastAtom( cur_tree, at_to_n2 ); - CurTreeRemoveLastRankIfNoAtoms( cur_tree ); - } - continue; /* Reject: not a minimal CT. */ - - } else { - - /* try to accept */ - - if ( c > 0 && !pCS->bStereoIsBetter ) { - /* bond_parity is less than the previusly found */ - pCS->bStereoIsBetter = bStereoIsBetterWasSetHere = 1; - prevBond2 = pCS->LinearCTStereoDble[nNumMappedBonds]; - } - /* accept */ - pCS->LinearCTStereoDble[nNumMappedBonds].at_num1 = at_rank_canon1; - pCS->LinearCTStereoDble[nNumMappedBonds].at_num2 = at_rank_canon2; - pCS->LinearCTStereoDble[nNumMappedBonds].parity = bond_parity; - /* recursive call */ - pCS->bRankUsedForStereo[at_from1] ++; - pCS->bRankUsedForStereo[at_from2] ++; - pCS->bAtomUsedForStereo[at_to1] --; - pCS->bAtomUsedForStereo[at_to2] --; - ret2 = map_stereo_bonds4 ( at, num_atoms, num_at_tg, num_max, bAllene, nCanonRankFrom, nAtomNumberCanonFrom, nCanonRankTo, - nSymmRank, pRankStack1+nStackPtr[istk3], pRankStack2+nStackPtr[istk3], - nTempRank, nNumMappedRanks[istk3], nSymmStereo, NeighList, - pCS, cur_tree, nNumMappedBonds+1 , - vABParityUnknown); - pCS->bRankUsedForStereo[at_from1] --; - pCS->bRankUsedForStereo[at_from2] --; - pCS->bAtomUsedForStereo[at_to1] ++; - pCS->bAtomUsedForStereo[at_to2] ++; - if ( ret2 == 4 ) { - if ( nNumMappedBonds ) { - return ret2; - } else { - pCS->bFirstCT = 1; - goto total_restart; - } - } - if ( RETURNED_ERROR(ret2) ) { - if ( ret2 == CT_TIMEOUT_ERR ) - return ret2; - else - return ret2; /* program error */ - } - if ( ret2 > 0 ) { - nTotSuccess |= 1; - nNumAtTo1Success ++; - if ( bStereoIsBetterWasSetHere || (ret2 & 2) ) { - CurTreeKeepLastAtomsOnly( cur_tree, tpos1, 1 ); /* start over */ - nTotSuccess |= 2; /* Obtained a smaller CT */ - } - at_no_n1_num_success ++; - } else { - if ( bStereoIsBetterWasSetHere ) { /* rollback */ - pCS->bStereoIsBetter = 0; - pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond2; - } - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n2 ) ) { - CurTreeRemoveIfLastAtom( cur_tree, at_to_n2 ); - CurTreeRemoveLastRankIfNoAtoms( cur_tree ); - } - } - bStereoIsBetterWasSetHere = 0; - } - } /* end choices in mapping neighbors of the 2nd half-bond */ - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n2 ) ) { - CurTreeRemoveLastRank( cur_tree ); - } - /* added 2006-07-20 */ - if ( !at_no_n1_num_success && tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n1 ) ) { - CurTreeRemoveIfLastAtom( cur_tree, at_to_n1 ); - } - - } /* end choices in mapping neighbors of the 1st half-bond */ - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n1 ) ) { - CurTreeRemoveLastRank( cur_tree ); - } - } /* end of choices in mapping at_from2 */ - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon2 ) ) { - CurTreeRemoveLastRank( cur_tree ); - } - if ( !nNumAtTo1Success ) { - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { - CurTreeRemoveIfLastAtom( cur_tree, at_to1 ); - CurTreeRemoveLastRankIfNoAtoms( cur_tree ); - } - } - if ( bAllParitiesIdentical /*&& !nSymmStereo*/ ) { - break; - } - } /* end of choices in mapping at_from1 */ - - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { - CurTreeRemoveLastRank( cur_tree ); - } else - /* CurTree consistecy check (debug only) */ - if ( tpos1 != CurTreeGetPos( cur_tree ) ) { - return CT_STEREOCOUNT_ERR; /* */ - } - - if ( !nTotSuccess || stereo_bond_parity == sb_parity_calc ) { - goto repeat_all; /* repeat with next parity if no success or with the same parity, now known */ - } - - /* Previously the control flow never came here... */ - if ( !nTotSuccess ) { - pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond; - CurTreeSetPos( cur_tree, tpos1 ); - /* Occurs when atoms are not really equvalent ( -O= without positive charge in "aromatic" ring) */ - return 0; /* Happens for ID=92439,100318,100319 when EXCL_ALL_AROM_BOND_PARITY=0 and - * nNumChoices=0. - * Results from impossible previous mapping of symmetric relatively - * to a central ring aromatic circles while central ring is not symmetrical due to - * alternate bonds (in the central ring number of pi-electrons, atoms and bonds - * are symmetrical). - * Does not happen when alternate bonds of the central ring - * are treated as aromatic by attaching a (+) charge to the oxygen. - */ - } - } else - - { - int ret; - - if ( !nNumMappedBonds ) { - pCS->bStereoIsBetter = 0; /* the first stereo feature in the canonical CT has not been processed yet */ - } - - if ( nNumMappedBonds < pCS->nLenLinearCTStereoDble ) { - prevBond = pCS->LinearCTStereoDble[nNumMappedBonds]; - } - - /* all stereo bonds have been mapped; now start processing stereo atoms... */ - ret = map_stereo_atoms4 ( at, num_atoms, num_at_tg, num_max, nCanonRankFrom, nAtomNumberCanonFrom, nCanonRankTo, - nSymmRank, pRankStack1, pRankStack2, nTempRank, nNumMappedRanksInput, - nSymmStereo, NeighList, pCS, cur_tree, 0 , vABParityUnknown); - if ( ret == 4 ) { - if ( nNumMappedBonds ) { - return ret; - } else { - pCS->bFirstCT = 1; - goto total_restart; - } - } - if ( RETURNED_ERROR(ret) ) { - if ( ret == CT_TIMEOUT_ERR ) - return ret; - else - return ret; /* program error */ - } - if ( ret > 0 ) { - nTotSuccess |= 1; - if ( ret & 2 ) { - CurTreeKeepLastAtomsOnly( cur_tree, tpos1, 1 ); /* start over */ - nTotSuccess |= 2; /* Obtained a smaller CT */ - } - } - } - if ( !nTotSuccess && pCS->nLenLinearCTStereoDble && - nNumMappedBonds < pCS->nLenLinearCTStereoDble ) { - pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond; - } - return nTotSuccess; /* ok */ -} - - - - - - - -/**************************************************************************************** - */ -int map_stereo_atoms4 ( - sp_ATOM *at, int num_atoms, int num_at_tg, int num_max, - const AT_RANK *nCanonRankFrom, const AT_RANK *nAtomNumberCanonFrom, AT_RANK *nCanonRankTo, /* canonical numbering to be mapped */ - const AT_RANK *nSymmRank, AT_RANK **pRankStack1/*from*/, AT_RANK **pRankStack2/*to*/, - AT_RANK *nTempRank, int nNumMappedRanksInput, - AT_RANK *nSymmStereo, NEIGH_LIST *NeighList, - CANON_STAT *pCS, CUR_TREE *cur_tree, int nNumMappedAtoms , - int vABParityUnknown ) -{ -/* - * Do not check whether "from" atoms have any stereo features. - */ - int nTotSuccess = 0; - AT_STEREO_CARB prevAtom; - int tpos1; - - tpos1 = CurTreeGetPos( cur_tree ); - - if ( nNumMappedAtoms < pCS->nLenLinearCTStereoCarb ) { - /* AT_RANK *nRankFrom=*pRankStack1++, AT_RANK *nAtomNumberFrom=pRankStack1++; */ - /* AT_RANK *nRankTo =*pRankStack2++, AT_RANK *nAtomNumberTo =pRankStack2++; */ - int j1, at_from1, at_to1, /*at_from2, at_to2,*/ iMax, lvl, bStereoIsBetterWasSetHere; - int istk, istk2, bAddStack, nNumAtTo1Success, c, bFirstTime=1, bAllParitiesIdentical; - EQ_NEIGH EN[5], *pEN; - int nStackPtr[5], nMappedRanks[5], j[5], *nSP, *nMR, bLastLvlFailed; - - AT_RANK at_rank_canon1, cr[5], at_to[5]; - AT_RANK canon_rank1_min = 0; - int at_rank1; /* rank for mapping */ - int nNumChoices, nNumUnkn, nNumUndf, nNumWorse, nNumBest, nNumCalc; - int stereo_center_parity, prev_stereo_center_parity, sb_parity_calc, pass; - AT_STEREO_CARB prevAtom2; - - prevAtom = pCS->LinearCTStereoCarb[nNumMappedAtoms]; /* save to restore in case of failure */ - at_rank_canon1 = nNumMappedAtoms? pCS->LinearCTStereoCarb[nNumMappedAtoms-1].at_num:0; - - goto bypass_next_canon_rank_check; - -next_canon_rank: - - if ( !pCS->bStereoIsBetter /*??? && !pCS->bFirstCT ???*/ && - at_rank_canon1 >= pCS->LinearCTStereoCarb[nNumMappedAtoms].at_num) { - /* cannot find next available canonical number */ - if ( !nTotSuccess ) { - pCS->LinearCTStereoCarb[nNumMappedAtoms] = prevAtom; /* restore because of failure */ - } - CurTreeSetPos( cur_tree, tpos1 ); - return nTotSuccess; - } - -bypass_next_canon_rank_check: - - CurTreeSetPos( cur_tree, tpos1 ); - - /* find next available canon. number for a stereogenic atom */ - if ( !Next_SC_At_CanonRank2( &at_rank_canon1, &canon_rank1_min, &bFirstTime, - pCS->bAtomUsedForStereo, pRankStack1, pRankStack2, - nAtomNumberCanonFrom, num_atoms ) || - !pCS->bStereoIsBetter && - at_rank_canon1 > pCS->LinearCTStereoCarb[nNumMappedAtoms].at_num) { - /* cannot find next available canonical number */ - if ( !nTotSuccess ) { - pCS->LinearCTStereoCarb[nNumMappedAtoms] = prevAtom; /* restore because of failure */ - } - return nTotSuccess; - } - - nNumChoices = 0; - nNumUnkn = 0; - nNumUndf = 0; - nNumBest = 0; - nNumWorse = 0; - nNumCalc = 0; - pass = 0; - prev_stereo_center_parity = 0; - - /* get mapping rank for the canon. number */ - at_rank1 = pRankStack1[0][at_from1=(int)nAtomNumberCanonFrom[at_rank_canon1 - 1]]; - iMax = at_rank1-1; - /* for debug only */ - if ( at_rank1 != pRankStack2[0][pRankStack2[1][at_rank1-1]] ) - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - - /* count special parities of the not mapped yet "to" atoms */ - for ( j1 = 0; j1 <= iMax && at_rank1 == pRankStack2[0][at_to1 =pRankStack2[1][iMax-j1]]; j1 ++ ) { - if ( !at[at_to1].stereo_bond_neighbor[0] && pCS->bAtomUsedForStereo[at_to1] == STEREO_AT_MARK ) { - int no_choice = 0; - stereo_center_parity = PARITY_VAL(at[at_to1].stereo_atom_parity); - switch(stereo_center_parity) { - - case AB_PARITY_UNDF: nNumUndf ++; break; /* 4 */ - - case AB_PARITY_UNKN: nNumUnkn ++; - break; /* 3 */ - - case BEST_PARITY: nNumBest ++; break; /* 1 */ - case WORSE_PARITY: nNumWorse ++; break; /* 2 */ - case AB_PARITY_CALC: nNumCalc ++; break; - case AB_PARITY_NONE: no_choice ++; break; /* 0 */ - } - nNumChoices += !no_choice; - } - } - if ( nNumChoices != nNumCalc + nNumUndf + nNumUnkn + nNumBest + nNumWorse ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - if ( !nNumChoices ) { - goto next_canon_rank; - } - /* Determine the first parity to search */ - sb_parity_calc = ( nNumCalc > 0 )? BEST_PARITY : 0; - - /* ============================================================== - Search sequence: sb_parity_calc stereo_center_parity - ============================================================== - BEST_PARITY (calc) BEST_PARITY BEST_PARITY - BEST_PARITY (known) BEST_PARITY WORSE_PARITY or 0 - WORSE_PARITY (calc) WORSE_PARITY WORSE_PARITY - WORSE_PARITY (known) WORSE_PARITY 0 - AB_PARITY_UNKN(known) AB_PARITY_UNKN 0 - AB_PARITY_UNDF(known) AB_PARITY_UNDF 0 - - if (sb_parity_calc==stereo_center_parity) then "calc" else "known" - */ - -repeat_all: - - if ( !pass ++ ) { - /* select the smallest parity to search */ - if ( sb_parity_calc ) { - stereo_center_parity = BEST_PARITY; - } else { - stereo_center_parity = nNumBest? BEST_PARITY : - nNumWorse? WORSE_PARITY : - nNumUnkn? AB_PARITY_UNKN : - nNumUndf? AB_PARITY_UNDF : AB_PARITY_NONE; - } - } else { - prev_stereo_center_parity = stereo_center_parity; - j1 = NextStereoParity2Test( &stereo_center_parity, &sb_parity_calc, - nNumBest, nNumWorse, nNumUnkn, nNumUndf, nNumCalc, - vABParityUnknown ); - switch ( j1 ) { - case 0: - break; /* obtained next parity to test */ - case 1: - goto next_canon_rank; - default: - return j1; /* program error */ - } - } - if ( stereo_center_parity == AB_PARITY_NONE ) { - /* error? */ - return CT_STEREOCOUNT_ERR; /* */ - } - /* check if the new requested parity is small enough */ - if ( !pCS->bStereoIsBetter ) { - c = CompareLinCtStereoAtomToValues( nTotSuccess? pCS->LinearCTStereoCarb+nNumMappedAtoms : &prevAtom, - at_rank_canon1, (U_CHAR)stereo_center_parity ); - if ( c < 0 ) { - if ( !nTotSuccess ) { - pCS->LinearCTStereoCarb[nNumMappedAtoms] = prevAtom; - } - CurTreeSetPos( cur_tree, tpos1 ); - return nTotSuccess; - } - } - - - bAllParitiesIdentical = 0; - bStereoIsBetterWasSetHere = 0; - istk = istk2 = 0; - CurTreeSetPos( cur_tree, tpos1 ); /* start over */ - /* - if ( prev_stereo_center_parity != stereo_center_parity ) { - CurTreeSetPos( cur_tree, tpos1 ); - } - */ /* nRankTo nAtomNumberTo */ - for ( j1 = 0; j1 <= iMax && at_rank1 == pRankStack2[0][at_to1 =pRankStack2[1][iMax-j1]]; j1 ++ ) { - int ret, ret1, ret2, parity1; - nNumAtTo1Success = 0; - /* - if ( !(at[at_to1].stereo_atom_parity && !at[at_to1].stereo_bond_neighbor[0] && - pCS->bAtomUsedForStereo[at_to1] == STEREO_AT_MARK ) ) - */ - if ( !at[at_to1].stereo_atom_parity || at[at_to1].stereo_bond_neighbor[0] || - pCS->bAtomUsedForStereo[at_to1] != STEREO_AT_MARK ) /* simplify 12-17-2003 */ - continue; - /* Do not map on non-stereogenic atom constitutionally - * equivalent to a steregenic atom. Here - * at[at_to1] is not a sterereo center; | | - * bonds tautomerism is a usual cause. -P(+)-CH=P- - * For example, consider a fragment: | | - * The two atoms P may be constitutionally - * equivalent, P(+) may be seen as a stereocenter - * while another P has a double bond (Now such a P(V) IS a stereocenter). - */ - /* check whether the stereocenter parity corresponds to the requested stereocenter parity */ - if ( PARITY_KNOWN(at[at_to1].stereo_atom_parity) ) { - if ( stereo_center_parity == sb_parity_calc ) { - continue; /* requested parity to be calculated, found known parity */ - } - if ( stereo_center_parity != PARITY_VAL(at[at_to1].stereo_atom_parity) ) { - continue; /* parity differs from the requested parity */ - } - } else - if ( PARITY_CALCULATE( at[at_to1].stereo_atom_parity) ) { - if ( stereo_center_parity != sb_parity_calc ) { - continue; /* requested known parity, found patity to be calculated */ - } - } else { - return CT_STEREOCOUNT_ERR; /* unknown parity type */ - } - - bAllParitiesIdentical = (( at[at_to1].stereo_atom_parity & KNOWN_PARITIES_EQL ) && - PARITY_KNOWN(at[at_to1].stereo_atom_parity)); - - if ( !bAllParitiesIdentical && !nNumCalc && - (!nNumUndf + !nNumUnkn + !nNumBest + !nNumWorse)==3 ) { - /* only one kind of stereocenter parity is present; check whether all parities are really same */ - bAllParitiesIdentical = All_SC_Same( at_rank_canon1, /* canonical number */ - pRankStack1, pRankStack2, - nAtomNumberCanonFrom, at ); - if ( bAllParitiesIdentical < 0 ) { - return CT_STEREOCOUNT_ERR; - } - } - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) && - 1 == CurTreeIsLastAtomEqu( cur_tree, at_to1, nSymmStereo ) ) - continue; - - /* initialize stack pointer nStackPtr[istk] for "hand-made" recursion */ - /* stacks are pRankStack1[], pRankStack2[], nNumMappedRanks[] */ - istk = 0; - nStackPtr[istk] = 0; - nMappedRanks[istk] = nNumMappedRanksInput; - bAddStack = 0; - /* if all equivalent atoms have same known parity, do not map any of them here */ - if ( !bAllParitiesIdentical ) { - /* map the central atom */ - /* this mapping is always possible */ - ret1 = map_an_atom2( num_at_tg, num_max, at_from1, at_to1, - nTempRank, nMappedRanks[istk], &nMappedRanks[istk+1], pCS, - NeighList, pRankStack1+nStackPtr[istk], pRankStack2+nStackPtr[istk], - &bAddStack ); - if ( RETURNED_ERROR(ret1) ) { - return ret1; /* error */ - } - nStackPtr[istk+1] = nStackPtr[istk] + bAddStack; - istk ++; - } else { - ClearPreviousMappings( pRankStack1+2 ); /* precaution */ - } - - /********************************************************************************* - * - * Unknown Stereocenter Parity case: possibly need to map stereo center neighbors - */ - if ( stereo_center_parity == sb_parity_calc ) - { - /* find out the parity */ - parity1 = parity_of_mapped_atom2( at_from1, at_to1, at, &EN[istk], - nCanonRankFrom, pRankStack1[nStackPtr[istk]], - pRankStack2[nStackPtr[istk]] ); - /* if parity is well-defined then returned EN[istk].num_to=0 */ - if ( !parity1 ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - if ( !EN[istk].num_to && parity1 != sb_parity_calc ) { - continue; /* looking for the parity value = sb_parity_calc */ - } - - } else { - /* Known parity */ - parity1 = stereo_center_parity; - EN[istk].num_to = 0; - } - - /*********************************************************************** - * no need to map the neighbors: parity is known or has been calculated - */ - if ( stereo_center_parity == sb_parity_calc && !EN[istk].num_to || - /* now well-defined, but unknown in advance atom parity OR */ - stereo_center_parity != sb_parity_calc ) - /* known in advance parity = stereo_center_parity */ - { - /* do not need to map the neighbors */ - c = CompareLinCtStereoAtomToValues( pCS->LinearCTStereoCarb+nNumMappedAtoms, - at_rank_canon1, (U_CHAR)parity1 ); - if ( c < 0 && !pCS->bStereoIsBetter ) { - /* reject */ - pCS->lNumRejectedCT ++; - continue; /* Reject: not a minimal CT. Should not happen */ - } else { - /* accept */ - - if ( bAddStack ) { - if ( tpos1 == CurTreeGetPos( cur_tree ) || - 0 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { - CurTreeAddRank( cur_tree, at_rank_canon1 ); - } - CurTreeAddAtom( cur_tree, at_to1 ); - } - - if ( c > 0 && !pCS->bStereoIsBetter ) { - /* stereo center entry is less than the previusly found */ - pCS->bStereoIsBetter = bStereoIsBetterWasSetHere = 1; - prevAtom2 = pCS->LinearCTStereoCarb[nNumMappedAtoms]; - } - pCS->LinearCTStereoCarb[nNumMappedAtoms].parity = parity1; - pCS->LinearCTStereoCarb[nNumMappedAtoms].at_num = at_rank_canon1; - pCS->bRankUsedForStereo[at_from1] = 3; -#if ( FIX_ChCh_STEREO_CANON_BUG == 1 ) - if ( !bAllParitiesIdentical ) -#endif - pCS->bAtomUsedForStereo[at_to1] -= STEREO_AT_MARK; - - ret = map_stereo_atoms4 ( at, num_atoms, num_at_tg, num_max, nCanonRankFrom, nAtomNumberCanonFrom, nCanonRankTo, - nSymmRank, pRankStack1+nStackPtr[istk], pRankStack2+nStackPtr[istk], - nTempRank, nMappedRanks[istk], nSymmStereo, NeighList, - pCS, cur_tree, nNumMappedAtoms+1 , - vABParityUnknown); - pCS->bRankUsedForStereo[at_from1] = 0; -#if ( FIX_ChCh_STEREO_CANON_BUG == 1 ) - if ( !bAllParitiesIdentical ) -#endif - pCS->bAtomUsedForStereo[at_to1] += STEREO_AT_MARK; - if ( ret == 4 ) { - return ret; - } - if ( RETURNED_ERROR(ret) ) { - if ( ret == CT_TIMEOUT_ERR ) - return ret; - else - return ret; /* program error */ - } - if ( ret > 0 ) { - nTotSuccess |= 1; - nNumAtTo1Success ++; - if ( bStereoIsBetterWasSetHere || (ret & 2) ) { - CurTreeKeepLastAtomsOnly( cur_tree, tpos1, 1 ); /* start over */ - nTotSuccess |= 2; /* Obtained a smaller CT */ - } - } else { - if ( bStereoIsBetterWasSetHere ) { - pCS->bStereoIsBetter = 0; - pCS->LinearCTStereoCarb[nNumMappedAtoms] = prevAtom2; - } - /* remove failed atom1 from the tree */ - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { - CurTreeRemoveIfLastAtom( cur_tree, at_to1 ); - CurTreeRemoveLastRankIfNoAtoms( cur_tree ); - } - - } - bStereoIsBetterWasSetHere = 0; - } - /* - if ( (at[at_to1].stereo_atom_parity & KNOWN_PARITIES_EQL ) && - ATOM_PARITY_KNOWN(stereo_center_parity) && !nSymmStereo ) { // ??? add && !nSymmStereo ??? - break; // do not repeat for the same kind of stereo atom with the parity known in advance - } - */ - if ( bAllParitiesIdentical ) { - break; /* do not repeat for the same kind of stereo atom with the parity known in advance */ - } - continue; - - } - - /*************************************************** - * - * Need to map the neighbors - */ - if ( stereo_center_parity != sb_parity_calc ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - /* -- has already been calculated -- - parity1 = parity_of_mapped_atom2( at_from1, at_to1, at, &EN[istk], - nCanonRankFrom, pRankStack1[nStackPtr[istk]], pRankStack2[nStackPtr[istk]] ); - */ - if ( !parity1 ) { - return CT_STEREOCOUNT_ERR; /* 1/25/2002 */ /* */ - } - - if ( bAddStack ) { - if ( tpos1 == CurTreeGetPos( cur_tree ) || - 0 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { - CurTreeAddRank( cur_tree, at_rank_canon1 ); - } - CurTreeAddAtom( cur_tree, at_to1 ); - } - /****************************************************** - * Need to fix the neighbors to define the atom parity - ******************************************************/ - /* a recursion replaced with the hand-made stack */ - lvl = 0; /* the "recursion" depth level */ - nSP = &nStackPtr[istk]; - nMR = &nMappedRanks[istk]; - pEN = &EN[istk]; - bLastLvlFailed = 0; - - /* entering "recursion" depth level lvl */ -next_lvl: - if ( pEN[lvl].num_to ) { - /* Found tied neighbors. Try all transpositions of the tied neighbors. - * j is a number of the "to" tied neighbor in the pEN[lvl].to_at[*] to - * which the pEN[lvl].from_at "from" neighbor's canonical number is mapped - */ - j[lvl] = 0; -next_j: - cr[lvl] = nCanonRankFrom[pEN[lvl].from_at]; - at_to[lvl] = pEN[lvl].to_at[j[lvl]]; - if ( j[lvl] && tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, cr[lvl] ) && - 1 == CurTreeIsLastAtomEqu( cur_tree, at_to[lvl], nSymmStereo ) ) { - lvl ++; - bLastLvlFailed = 0; - goto backup; /* do not test stereo equivalent atoms except the first one */ - } - - ret2 = map_an_atom2( num_at_tg, num_max, - pEN[lvl].from_at, /* from */ - pEN[lvl].to_at[j[lvl]], /* to */ - nTempRank, nMR[lvl], &nMR[lvl+1], pCS, - NeighList, pRankStack1+nSP[lvl], pRankStack2+nSP[lvl], - &bAddStack ); - if ( RETURNED_ERROR(ret2) ) { - return ret2; /* program error */ - } - - /* next recursion depth level */ - if ( bAddStack ) { - if ( tpos1 == CurTreeGetPos( cur_tree ) || - 0 == CurTreeIsLastRank( cur_tree, cr[lvl] ) ) { - CurTreeAddRank( cur_tree, cr[lvl] ); - } - CurTreeAddAtom( cur_tree, at_to[lvl] ); - } - nSP[lvl+1] = nSP[lvl] + bAddStack; - lvl ++; /* upon increment lvl = number of additionally mapped neighbors - * (entering next recursion level) */ - /* check if the mapping has defined the parity */ - parity1 = parity_of_mapped_atom2( at_from1, at_to1, at, &pEN[lvl], - nCanonRankFrom, pRankStack1[nSP[lvl]], pRankStack2[nSP[lvl]] ); - if ( !parity1 ) { - return CT_STEREOCOUNT_ERR; /* 1/25/2002 */ /* */ - } - if ( parity1 < 0 ) { - goto next_lvl; /* we need at least one more mapping to define the parity */ - } - - /********************************************************** - * - * Check the parity - * - ********************************************************** - * make a decision whether to accept the current mapping */ - - c = CompareLinCtStereoAtomToValues( pCS->LinearCTStereoCarb+nNumMappedAtoms, - at_rank_canon1, (U_CHAR)parity1 ); - if ( sb_parity_calc != parity1 || - c < 0 && !pCS->bStereoIsBetter ) { - pCS->lNumRejectedCT ++; - bLastLvlFailed = 1; - } else - /* the parity has been defined (all neighbors have untied ranks) */ - /* if ( bAcceptAllParities || parity1 == BEST_PARITY ) */ - { - /********************************************************************* - * - * Process the parity here. We are at the top of the recursion stack. - * - *********************************************************************/ - /* try to accept current neighbors mapping */ - if ( c > 0 && !pCS->bStereoIsBetter ) { - pCS->bStereoIsBetter = bStereoIsBetterWasSetHere = 1; - prevAtom2 = pCS->LinearCTStereoCarb[nNumMappedAtoms]; - } - pCS->LinearCTStereoCarb[nNumMappedAtoms].parity = parity1; - pCS->LinearCTStereoCarb[nNumMappedAtoms].at_num = at_rank_canon1; - pCS->bRankUsedForStereo[at_from1] = 3; - pCS->bAtomUsedForStereo[at_to1] -= STEREO_AT_MARK; - - ret = map_stereo_atoms4 ( at, num_atoms, num_at_tg, num_max, nCanonRankFrom, nAtomNumberCanonFrom, nCanonRankTo, - nSymmRank, pRankStack1+nSP[lvl], pRankStack2+nSP[lvl], - nTempRank, nMR[lvl], nSymmStereo, NeighList, - pCS, cur_tree, nNumMappedAtoms+1 , - vABParityUnknown ); - pCS->bRankUsedForStereo[at_from1] = 0; - pCS->bAtomUsedForStereo[at_to1] += STEREO_AT_MARK; - if ( ret == 4 ) { - return ret; - } - if ( RETURNED_ERROR(ret) ) { - if ( ret == CT_TIMEOUT_ERR ) - return ret; - else - return ret; /* program error */ - } - if ( ret > 0 ) { - nTotSuccess |= 1; - nNumAtTo1Success ++; - if ( bStereoIsBetterWasSetHere || (ret & 2) ) { - CurTreeKeepLastAtomsOnly( cur_tree, tpos1, 1 ); /* start over */ - nTotSuccess |= 2; /* Obtained a smaller CT */ - } - } else { - if ( bStereoIsBetterWasSetHere ) { - pCS->bStereoIsBetter = 0; - pCS->LinearCTStereoCarb[nNumMappedAtoms] = prevAtom2; - } - bLastLvlFailed = 1; - } - bStereoIsBetterWasSetHere = 0; - - /* avoid redundant repetitions: */ - /* check if neighbors mappings have altered another stereo center parity */ - if ( !nSymmStereo && !might_change_other_atom_parity( at, num_atoms, at_to1, - pRankStack2[nSP[lvl]] /* ranks after neigbors mapping */, - pRankStack2[nStackPtr[istk]] /* ranks before the mapping neighbors */) ) { - goto done; - } - } - /* Continue the cycle. Go to the previous "recursion" level */ -backup: - while (lvl -- > 0 ) { - - j[lvl] ++; /* next neighbor at this level */ - if ( j[lvl] < pEN[lvl].num_to ) { - if ( bLastLvlFailed ) { - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, cr[lvl] ) ) { - CurTreeRemoveIfLastAtom( cur_tree, at_to[lvl] ); - CurTreeRemoveLastRankIfNoAtoms( cur_tree ); - } - bLastLvlFailed = 0; - } - /* Done with this level. Go back one level */ - goto next_j; - } - /* remove failed atom from the tree */ - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, cr[lvl] ) ) { - CurTreeRemoveLastRank( cur_tree ); - } - } - goto done; - } else { - cr[lvl] = 0; - } - -done:; /* at this point lvl=0. */ - if ( !nNumAtTo1Success ) { - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { - CurTreeRemoveIfLastAtom( cur_tree, at_to1 ); - CurTreeRemoveLastRankIfNoAtoms( cur_tree ); - } - } - } /* end of stereo atom mapping cycle */ - - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { - CurTreeRemoveLastRank( cur_tree ); - } else - /* CurTree consistency check (debug only) */ - if ( tpos1 != CurTreeGetPos( cur_tree ) ) { - return CT_STEREOCOUNT_ERR; /* */ - } - - if ( !nTotSuccess || stereo_center_parity == sb_parity_calc ) { - goto repeat_all; /* repeat with next parity if no success or with the same parity, now known */ - } - - } else { - - /**************************************************** - * - * All stereogenic atoms and bonds have been mapped - * - ****************************************************/ - - if ( UserAction && USER_ACTION_QUIT == (*UserAction)() || - ConsoleQuit && (*ConsoleQuit)() ) { - return CT_USER_QUIT_ERR; - } - - if ( pCS->bStereoIsBetter || pCS->bFirstCT ) { - /* All stereo atoms have been mapped. Current stereo name is better than all previous. - * Create new numbering for the new CT - * break all remaining "from" ties - */ - int i1, ret; - AT_RANK rc, n1, n2; - ret=BreakAllTies( num_at_tg, num_max, pRankStack1, NeighList, nTempRank, pCS); - if ( RETURNED_ERROR( ret ) ) { - return ret; - } - /* break all remaining "from" ties */ - ret=BreakAllTies( num_at_tg, num_max, pRankStack2, NeighList, nTempRank, pCS); - if ( RETURNED_ERROR( ret ) ) { - return ret; - } - /* move stack pointers to the "nAtomNumber[*]" after all ties are broken */ - pRankStack1 += 2; - pRankStack2 += 2; - /* Now final mapping ranks of "to" atom (*pRankStack2)[i] and "from" atom (*pRankStack1)[i] - * are equal and all ranks are different, that is, we have a full mapping - * Copy so far best canonical numbering from "from" to "to". - */ - memset( pCS->nPrevAtomNumber, 0, num_at_tg*sizeof(pCS->nPrevAtomNumber[0]) ); - for ( i1 = 0; i1 < num_at_tg; i1 ++ ) { - n1 = pRankStack1[1][i1]; - rc = nCanonRankFrom[n1]; /* new canon. rank */ - n2 = pRankStack2[1][i1]; /* orig. atom number */ - nCanonRankTo[n2] = rc; /* assign new canon. number to the atom */ - /* use this array to find stereo-equivalent atoms */ - pCS->nPrevAtomNumber[rc-1] = n2; /* ord. number of the atom having canon. rank = rc */ - nSymmStereo[i1] = i1; /* restart search for stereo equivalent atoms */ - /* check mapping correctness */ - if ( pRankStack1[0][n1] != pRankStack2[0][n2] || - nSymmRank[n1] != nSymmRank[n2] ) { - return CT_STEREO_CANON_ERR; /* stereo mapping error */ - } - } - /* statistics */ - pCS->lNumTotCT ++; - pCS->lNumEqualCT = 1; - pCS->lNumDecreasedCT ++; - pCS->bStereoIsBetter = 0; /* prepare to start over */ - nTotSuccess = 1; - pCS->bFirstCT = 0; -#if ( REMOVE_CALC_NONSTEREO == 1 ) /* { */ - if ( !(pCS->nMode & CMODE_REDNDNT_STEREO ) ) { - i1 = RemoveCalculatedNonStereo( at, num_atoms, num_at_tg, - pRankStack1, pRankStack2, nTempRank, NeighList, - nSymmRank, nCanonRankTo, pCS->nPrevAtomNumber, pCS, - vABParityUnknown); - if ( RETURNED_ERROR( i1 ) ) { - return i1; - } - if ( i1 < 0 ) { -#if ( bRELEASE_VERSION == 0 ) - pCS->bExtract |= EXTR_REMOVE_PARITY_WARNING; -#endif - i1 = -(1+i1); - } - if ( i1 > 0 ) { - return 4; /* total restart: due to newly found stereo equivalence */ - /* the length of the stereo CT has changed */ - } - } -#endif /* } REMOVE_CALC_NONSTEREO */ - pRankStack1 -= 2; - pRankStack2 -= 2; - } else { - /* current stereo name is same as previous. We do not need a full mapping. */ - if ( nSymmStereo ) { - int num_changes = 0; - AT_RANK r, n1, n2, r_max, cr; - r_max = (AT_RANK)num_at_tg; - for ( r = 1; r <= r_max; r ++ ) { - if ( bUniqueAtNbrFromMappingRank( pRankStack1, r, &n1 ) ) { - if ( bUniqueAtNbrFromMappingRank( pRankStack2, r, &n2 ) ) { - /* atoms at[n1], at[n2] have identical untied mapping rank r */ - cr = nCanonRankFrom[(int)n1]-1; /* (new at[n2] canonical rank)-1 */ - /* pCS->nPrevAtomNumber[(int)cr] = */ - /* previous ordering number of an atom with the canon. rank = cr+1; */ - /* make this atom equivalent to atom at[n2]: */ - num_changes += nJoin2Mcrs( nSymmStereo, pCS->nPrevAtomNumber[(int)cr], n2 ); - } else { - return CT_MAPCOUNT_ERR; /* mapping ranks must be either both tied or untied. */ /* */ - } - } - } - if ( num_changes ) { /* compress trees to stars */ - for ( r = r_max-1; r; r -- ) { - nGetMcr( nSymmStereo, r ); - } - } - } - /* statistics */ - pCS->lNumEqualCT ++; - pCS->lNumTotCT ++; - nTotSuccess = 1; - } - if ( bInchiTimeIsOver( pCS->ulTimeOutTime ) ) { - return CT_TIMEOUT_ERR; - } - } - if ( !nTotSuccess && nNumMappedAtoms < pCS->nLenLinearCTStereoCarb ) { - pCS->LinearCTStereoCarb[nNumMappedAtoms] = prevAtom; - CurTreeSetPos( cur_tree, tpos1 ); - } - return nTotSuccess; /* return to the previous level of the recursion. */ -} diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichinorm.c b/INCHI-1-SRC/INCHI_API/inchi_dll/ichinorm.c deleted file mode 100644 index 810514d..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichinorm.c +++ /dev/null @@ -1,3346 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - -#include "mode.h" - -#include "inpdef.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "ichierr.h" -#include "util.h" - -#include "ichicomp.h" - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -/* defined in ichisort.c, prototype in ichicomn.h */ -int insertions_sort_AT_RANK( AT_RANK *base, int num ); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -/* local prototypes */ -int cmp_iso_atw_diff_component_no( const void *a1, const void *a2 ); - -/********************************************************************************/ -int cmp_iso_atw_diff_component_no( const void *a1, const void *a2 ) -{ - int ret = (int)((const inp_ATOM*)a1)->iso_atw_diff - (int)((const inp_ATOM*)a2)->iso_atw_diff; - if ( !ret ) /* make the sort stable */ - ret = (int)((const inp_ATOM*)a1)->component - (int)((const inp_ATOM*)a2)->component; - return ret; -} -typedef struct tagTreeAtom { - AT_NUMB neighbor[MAXVAL]; /* positions (from 0) of the neighbors in the inp_ATOM array */ - S_CHAR valence; /* number of bonds = number of neighbors */ - AT_NUMB nRingSystem; - AT_NUMB nBlockSystem; - S_CHAR bCutVertex; -} tre_ATOM; - -#if ( FIND_RING_SYSTEMS == 1 ) /* { */ -/********************************************************************************/ -int MarkRingSystemsInp( inp_ATOM *at, int num_atoms, int start ) -{ - AT_NUMB *nStackAtom = NULL; - int nTopStackAtom=-1; - AT_NUMB *nRingStack = NULL; - int nTopRingStack=-1; /* was AT_NUMB */ - AT_NUMB *nDfsNumber = NULL; - AT_NUMB *nLowNumber = NULL; - S_CHAR *cNeighNumb = NULL; - AT_NUMB nDfs; -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) - AT_NUMB nRs, *nRsConnect = NULL; - int k; - AT_NUMB *tree = NULL; - int nNumConnect, nMaxNumConnect, nLenConnect; -#endif - AT_NUMB nNumAtInRingSystem; - int i, j, u, /*start,*/ nNumRingSystems, nNumStartChildren; - - /* allocate arrays */ - nStackAtom = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nStackAtom[0])); - nRingStack = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nRingStack[0])); - nDfsNumber = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nDfsNumber[0])); - nLowNumber = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nLowNumber[0])); - cNeighNumb = (S_CHAR *) inchi_malloc(num_atoms*sizeof(cNeighNumb[0])); -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) - nRsConnect = (AT_NUMB *)inchi_calloc(3*num_atoms+3,sizeof(nRsConnect[0])); -#endif - /* check allocation */ - if ( !nStackAtom || !nRingStack || !nDfsNumber || !nLowNumber || !cNeighNumb -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) - || !nRsConnect -#endif - ) { - nNumRingSystems = CT_OUT_OF_RAM; /* program error */ /* */ - goto exit_function; - } - - /******************************************** - * - * Find Cut-vertices & Blocks - * - ********************************************/ - - /* initiation */ - /*start = 0;*/ - nNumRingSystems = 0; - u = start; /* start atom */ - nDfs = 0; - nTopStackAtom =-1; - nTopRingStack =-1; - memset( nDfsNumber, 0, num_atoms*sizeof(nDfsNumber[0])); - memset( cNeighNumb, 0, num_atoms*sizeof(cNeighNumb[0])); - /* push the start atom on the stack */ - nLowNumber[u] = nDfsNumber[u] = ++nDfs; - nStackAtom[++nTopStackAtom] = (AT_NUMB)u; - nRingStack[++nTopRingStack] = (AT_NUMB)u; - - nNumStartChildren = 0; - - do { - /* advance */ -advance_block: - /*if ( (int)at[i=nStackAtom[nTopStackAtom]].valence > (j = (int)cNeighNumb[i]) )*/ - /* replaced due to missing sequence point */ - if ( i=(int)nStackAtom[nTopStackAtom], j = (int)cNeighNumb[i], (int)at[i].valence > j ) - { - cNeighNumb[i] ++; - u = (int)at[i].neighbor[j]; - if ( !nDfsNumber[u] ) { - /* tree edge, 1st visit -- advance */ - nStackAtom[++nTopStackAtom] = (AT_NUMB)u; - nRingStack[++nTopRingStack] = (AT_NUMB)u; - nLowNumber[u] = nDfsNumber[u] = ++nDfs; - nNumStartChildren += (i == start); - } else - if ( !nTopStackAtom || u != (int)nStackAtom[nTopStackAtom-1] ) { /* may comment out ? */ - /* back edge: u is not a predecessor of i */ - if ( nDfsNumber[u] < nDfsNumber[i] ) { - /* Back edge, 1st visit: u is an ancestor of i. Compare */ - if ( nLowNumber[i] > nDfsNumber[u] ) { - nLowNumber[i] = nDfsNumber[u]; - } - } - } /* may comment out ? */ - goto advance_block; - } else { - cNeighNumb[i] = 0; - } - - /* back up */ - if ( i != start ) { - u = (int)nStackAtom[nTopStackAtom-1]; /* predecessor of i */ - if ( nLowNumber[i] >= nDfsNumber[u] ) { - /* output the block */ - nNumRingSystems ++; - at[u].nBlockSystem = nNumRingSystems; - if ( u != start || nNumStartChildren > 1 ) { - at[u].bCutVertex += 1; - } - while ( nTopRingStack >= 0 ) { - j = nRingStack[nTopRingStack--]; - at[j].nBlockSystem = nNumRingSystems; /* mark the atom */ - if ( i == j ) { - break; - } - } - } else - if ( nLowNumber[u] > nLowNumber[i] ) { - /* inherit */ - nLowNumber[u] = nLowNumber[i]; - } - } - } while ( --nTopStackAtom >= 0 ); - - - /******************************************** - * - * Find Ring Systems - * Including chain atoms X: A-X-B, where the bonds (of any kind) are bridges. - * - ********************************************/ - - /* initiation */ - /*start = 0;*/ - nNumRingSystems = 0; - u = start; /* start atom */ - nDfs = 0; - nTopStackAtom =-1; - nTopRingStack =-1; - memset( nDfsNumber, 0, num_atoms*sizeof(nDfsNumber[0])); - memset( cNeighNumb, 0, num_atoms*sizeof(cNeighNumb[0])); - /* push the start atom on the stack */ - nLowNumber[u] = nDfsNumber[u] = ++nDfs; - nStackAtom[++nTopStackAtom] = (AT_NUMB)u; - nRingStack[++nTopRingStack] = (AT_NUMB)u; -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) - nNumConnect = nLenConnect = nMaxNumConnect = 0; -#endif - - do { - /* advance */ -advance_ring: - /*if ( (int)at[i=nStackAtom[nTopStackAtom]].valence > (j = (int)cNeighNumb[i]) )*/ - /* replaced due to missing sequence point */ - if ( i=(int)nStackAtom[nTopStackAtom], j = (int)cNeighNumb[i], (int)at[i].valence > j ) - { - cNeighNumb[i] ++; - u = (int)at[i].neighbor[j]; - if ( !nDfsNumber[u] ) { - /* tree edge, 1st visit -- advance */ - nStackAtom[++nTopStackAtom] = (AT_NUMB)u; - nRingStack[++nTopRingStack] = (AT_NUMB)u; - nLowNumber[u] = nDfsNumber[u] = ++nDfs; - } else - if ( !nTopStackAtom || u != (int)nStackAtom[nTopStackAtom-1] ) { - /* back edge: u is not a predecessor of i */ - if ( nDfsNumber[u] < nDfsNumber[i] ) { - /* Back edge, 1st visit: u is ancestor of i. Compare */ - if ( nLowNumber[i] > nDfsNumber[u] ) { - nLowNumber[i] = nDfsNumber[u]; - } - } - } - goto advance_ring; - } else { - cNeighNumb[i] = 0; - } - - /* back up */ - if ( nDfsNumber[i] == nLowNumber[i] ) { - /* found a ring system */ - nNumRingSystems ++; - /* unwind nRingStack[] down to i */ -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) - nNumConnect = 2; - /* data structure: for each ring system nRsConnect[] contains: - * 1) nNumConnect+1 = (number of already discovered neighboring "ring systems" + 1)+1 - * 2) nNumAtInRingSystem - * 3) (nNumConnect-1) numbers (IDs) of neighboring ring systems. - * BFS guarantees that each neighboring ring system is encountered only one time - * Number of all neighboring ring systems = (nNumConnect-1)+1 = nNumConnect - * (One additional ring system is where the BFS retracts from the vertex #i, - * except when i=DFS root node. In the latter case there is/are only (nNumConnect-1) - * neighboring ring system(s). - */ -#endif - /* count atoms in a ring system */ - for ( nNumAtInRingSystem = 0, j = nTopRingStack; 0 <= j; j -- ) { - nNumAtInRingSystem ++; - if ( i == (int)nRingStack[j] ) { - break; - } - } - while ( nTopRingStack >= 0 ) { - j = (int)nRingStack[nTopRingStack--]; - at[j].nRingSystem = (AT_NUMB)nNumRingSystems; /* ring system id */ - at[j].nNumAtInRingSystem = nNumAtInRingSystem; -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) - for ( k = 0; k < at[j].valence; k ++ ) { - if ( (nRs = at[at[j].neighbor[k]].nRingSystem) && (int)nRs != nNumRingSystems ) { - nRsConnect[nLenConnect + (nNumConnect++)] = nRs; /* adjacent ring system id */ - } - } -#endif - if ( i == j ) { - /* reached atom on the top of nStackAtom[] stack */ - break; - } - } -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) - nRsConnect[nLenConnect] = nNumConnect; - nRsConnect[nLenConnect+1] = nNumAtInRingSystem; - nLenConnect += nNumConnect; - if ( nMaxNumConnect < nNumConnect ) { - /* max number of neighboring ring systems */ - nMaxNumConnect = nNumConnect; - } -#endif - } else - if ( nTopStackAtom > 0 ) { - j = (int)nStackAtom[nTopStackAtom-1]; - /* inherit nLowNumber */ - if ( nLowNumber[j] > nLowNumber[i] ) { - nLowNumber[j] = nLowNumber[i]; - } - } - } while ( --nTopStackAtom >= 0 ); - -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) /* normally disabled */ - nMaxNumConnect ++; - if ( nNumRingSystems > 1 ) { - int nCol = nMaxNumConnect+1; - int nNumInSyst= nMaxNumConnect; - int nMaxNeigh = nMaxNumConnect-1; -#define T(a,b) tree[(a)*nCol+b] - if ( tree = (AT_NUMB *)inchi_calloc( nCol * (nNumRingSystems+1), sizeof(tree[0])) ) { - int len, neigh; - /* reuse previous allocations */ - AT_NUMB *nNumVisitedNeighbors = nStackAtom; - AT_NUMB *nDistanceFromTerminal = nRingStack; - AT_NUMB *nCurrActiveRingSystem = nDfsNumber; - AT_NUMB *nNextActiveRingSystem = nLowNumber; - int nNumCurrActiveRingSystems, nNumNextActiveRingSystems, pass; - /* build a "condensation graph (actually, a tree)" in which - * each vertex corresponds to a ring system T(row, col) = T(ring syst, neighbors) - * Number of rows = column length = max. number of ring system neighbors + 2 - * Number of cols = row length = number of ring systems + 1 - * Neighboring ring systems are contiguously stored in a row - * T(i,0) = number of neighbors, 1 <= i <= nNumRingSystems; - * T(i,k) = number of a neighboring ring system, 1 <= k <= T(i,0) - * T(i,nCol-1) = number of atoms in the system #i - */ - for ( i = 1, j = 0; len=nRsConnect[j]; i ++ ) { - T(i, nNumInSyst) = nRsConnect[j+1]; - for ( k = 2; k < len; k ++ ) { - neigh = nRsConnect[j+k]; - if ( T(i,0) < nMaxNeigh && T(neigh,0) < nMaxNeigh ) { - T(i,0) ++; - T(neigh,0) ++; - T(i,T(i,0)) = neigh; - T(neigh,T(neigh,0)) = i; - } else { - nNumRingSystems = CT_OVERFLOW; /* program error */ /* */ - goto exit_function; - } - } - j += len; - } - /* clear memory */ - memset( nNumVisitedNeighbors, 0, nNumRingSystems*sizeof(nNumVisitedNeighbors[0]) ); - memset( nDistanceFromTerminal, 0, nNumRingSystems*sizeof(nDistanceFromTerminal[0]) ); - memset( nCurrActiveRingSystem, 0, nNumRingSystems*sizeof(nCurrActiveRingSystem[0]) ); - memset( nNextActiveRingSystem, 0, nNumRingSystems*sizeof(nNextActiveRingSystem[0]) ); - nNumNextActiveRingSystems = 0; - for ( i = 0; i < nNumRingSystems; i ++ ) { - if ( 1 == T(i+1,0) ) { - nNextActiveRingSystem[i] = 1; /* number of traversed neighbors + 1 */ - nDistanceFromTerminal[i] = 1; - nNumNextActiveRingSystems ++; - } else { - nNextActiveRingSystem[i] = 0; - nDistanceFromTerminal[i] = 0; - } - nNumVisitedNeighbors[i] = 0; - } - - /* nCurrActiveRingSystem[i] = a sum of: - * 1) +1 if it is or was active - * 2) +(number of neighbors from which it was reached) - * 3) +1 if it was left and not active anymore - */ - pass = 0; - do { - nNumCurrActiveRingSystems = nNumNextActiveRingSystems; - nNumNextActiveRingSystems = 0; - memcpy( nCurrActiveRingSystem, nNextActiveRingSystem, - nNumRingSystems*sizeof(nNextActiveRingSystem[0])); - for ( i = 0; i < nNumRingSystems; i ++ ) { - if ( T(i+1,0) == nCurrActiveRingSystem[i] ) { - /* on the previous pass currently active ring system i+1 bas been reached - * from all neighbors except one; - * the neighbors from which it was reached have - * T(neigh,0)+1 == nCurrActiveRingSystem[i] - * this ring system has not been left yet - */ - for ( k = 1, len=T(i+1,0); k <= len; k ++ ) { - neigh = (int)T(i+1,k); - if ( T(neigh,0) >= nCurrActiveRingSystem[neigh-1] ) { - if ( 0 == pass ) { - nDistanceFromTerminal[i] = 1; - } - break; - } - } - if ( k <= len ) { - /* neigh was not reached from at least 2 neighbors - * walk along -R- chain (T(neigh,0)==2) up to - * 1) a terminal system, not including it or - * 2) a branching point. - * - * pass = 0: started from terminal systems: - * reach the branching point. - * If chain system next to a terminal system has already been reached - * then walk along it according to Note below - * - * pass > 0: started from branching points - * 2a) If the branching point has not been reached from 2 or more neighbors, - * then include it - * 2b) If the branching point has not been reached from 1 neighbor only, - * then do not include it: it will be a starting point later - * Note: if a chain atom already has nDistanceFromTerminal[i] > 0, then - * the last atom should be the one such that - * its nDistanceFromTerminal[]+1>= nDistanceFromTerminal[] of the - * next in the chain - */ - int bOk = 0; - k = i+1; /* starting point */ - if ( 0 == pass && T(k,nNumInSyst) > 1 ) { - nNumNextActiveRingSystems ++; /* request next pass */ - continue; /* stop a the terminal ring system */ - } - while( 2 == T(neigh,0) ) { - /* walk along a chain */ - if ( !nNextActiveRingSystem[neigh-1] ) { - nNextActiveRingSystem[neigh-1] = 1; /* make neighbor active */ - } else - if ( nDistanceFromTerminal[k-1]+1 <= nDistanceFromTerminal[neigh-1] ) { - /* walking along the chain; already have had a walk */ - /* in the opposite direction at this pass */ - } else { - /* k is the last; neigh (it is a bridge -X-) has not been reached */ - bOk = 1; - break; - } - nNextActiveRingSystem[k-1] ++; /* leave system k */ - if ( nNextActiveRingSystem[neigh-1] < T(neigh,0) ) { - nNextActiveRingSystem[neigh-1] ++; /* add one connection to neigh */ - } - nDistanceFromTerminal[neigh-1] = nDistanceFromTerminal[k-1]+1; - j = (T(neigh,1)==k)? 2:1; - k = neigh; - neigh = T(k,j); /* next in the chain */ - nNumNextActiveRingSystems ++; - if ( T(k,nNumInSyst) > 1 ) { - bOk = 1; - break; /* stop on a ring system */ - } - } - /* neigh is a terminal or a bridge or a branching point */ - if ( 2 > T(neigh,0) ) { - /* neighbor is a terminal atom */ - if ( 1 < pass ) { - nNumRingSystems = CT_UNKNOWN_ERR; /* error (debug only) */ /* */ - goto exit_function; - } - continue; - } - if ( 2 == T(neigh,0) ) { - /* neighbor is a bridge */ - continue; - } - /* neighbor is a branching point */ - if ( T(neigh,0) > nCurrActiveRingSystem[neigh-1] ) { - /* move to the neigh (make neigh active): on previous pass it */ - /* has not been reached from 2 or more neighbors */ - if ( !nNextActiveRingSystem[neigh-1] ) { - nNextActiveRingSystem[neigh-1] = 1; - } - if ( nDistanceFromTerminal[neigh-1] < nDistanceFromTerminal[k-1]+1 ) { - nDistanceFromTerminal[neigh-1] = nDistanceFromTerminal[k-1]+1; - } - nNextActiveRingSystem[k-1] ++; /* leave system k */ - if ( nNextActiveRingSystem[neigh-1] < T(neigh,0) ) { - nNextActiveRingSystem[neigh-1] ++; /* add one connection to neigh */ - } - nNumNextActiveRingSystems ++; - } - } - } - } - pass ++; - } while ( nNumNextActiveRingSystems ); - - for ( i = 0; i < num_atoms; i ++ ) { - at[i].nDistanceFromTerminal = nDistanceFromTerminal[(int)at[i].nRingSystem-1]; - } - - inchi_free( tree ); - tree = NULL; -#undef T - } else { - nNumRingSystems = CT_OUT_OF_RAM; /* error */ /* */ - goto exit_function; - } - } -#endif - - -exit_function: - if ( nStackAtom ) - inchi_free( nStackAtom ); - if ( nRingStack ) - inchi_free( nRingStack ); - if ( nDfsNumber ) - inchi_free( nDfsNumber ); - if ( nLowNumber ) - inchi_free( nLowNumber ); - if ( cNeighNumb ) - inchi_free( cNeighNumb ); -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) - if ( nRsConnect ) - inchi_free( nRsConnect ); - if ( tree ) - inchi_free( tree ); -#endif - return nNumRingSystems; -} - - -#endif /* } FIND_RING_SYSTEMS */ - -/********************************************************************************/ -/* Return value: new number of atoms > 0 or -1=out of RAM */ -int remove_terminal_HDT( int num_atoms, inp_ATOM *at, int bFixTermHChrg ) -{ - AT_NUMB *new_ord; - inp_ATOM *new_at; - char *p; - const static char szHDT[]="HDT"; - const static int kMax = sizeof(szHDT); /* = 4 */ - int ret = -1; - int num_hydrogens=0, num_H = 0; /* number of terminal H, D, T */ - int i, j, k, n, m; - int val; - AT_RANK new_HydrogenAt_order[NUM_H_ISOTOPES+1]; - AT_RANK new_OtherNeigh_order[MAXVAL]; - S_CHAR old_trans[MAX_NUM_STEREO_BONDS]; - - int num_OtherNeigh, num_HydrogenAt; - - new_ord=(AT_NUMB *)inchi_calloc(num_atoms, sizeof(new_ord[0])); /* changed malloc to calloc 9-11-2003 */ - new_at =(inp_ATOM *) inchi_malloc(sizeof(new_at[0]) *num_atoms); - if (!new_ord || !new_at) - goto exit_function; - - /* move H. D, T to the end of the list of atoms */ - for ( i = 0; i < num_atoms; i ++ ) - { - at[i].component = i; /* temporarily save original numbering */ - /* get k = temp. hydrogen isotope/non-hydrogen atom type: */ - /* k=0:H, k=2:D, k=3:T, k=4=kMax: not a hydrogen */ - k = at[i].elname[1]? kMax : (p=(char*)strchr(szHDT, at[i].elname[0]))? p-szHDT : kMax; - /* set hydrogen isotope atw differences */ - /* Notes: k-value of isotopic H is incremented to correct iso_atw_diff value later. */ - /* 1H isotope cannot be detected here. */ - if ( k == ATW_H || k == ATW_H+1 ) - { - /* D or T, k = 1 or 2 */ - at[i].elname[0] = 'H'; /* hydrogen isotope */ - at[i].iso_atw_diff = ++k; /* increment k to make k = iso_atw_diff ( 2 for D, 3 for T ) */ - } - num_H += (k != kMax && at[i].valence == 1 && at[i].chem_bonds_valence == 1 && !NUMH(at,i) ); - } - - /* special case: HD, HT, DT, HH: the only non-isotopic H or - * the lightest isotopic H out of two is removed - * to become implicit (make the heavier H the "central atom"). - * Note: This must be consistent with mol_to_atom() - * treatment of isotopic Hn aliases. - */ - if ( 2 == num_H && 2 == num_atoms && !NUMH(at,0) && !NUMH(at,1) ) - { - - if ( at[0].iso_atw_diff >= at[1].iso_atw_diff ) { - new_ord[0] = 0; - new_ord[1] = 1; - } else { - new_ord[0] = 1; - new_ord[1] = 0; - } - if ( at[new_ord[1]].charge ) { - at[new_ord[0]].charge += at[new_ord[1]].charge; - at[new_ord[1]].charge = 0; - } - new_at[new_ord[0]] = at[0]; - new_at[new_ord[1]] = at[1]; - num_hydrogens = 1; - - } - else - { - /* general case except H-H */ - for ( i = 0; i < num_atoms; i ++ ) - { - k = (at[i].elname[1] || NUMH(at,i))? kMax : (at[i].elname[0]=='H')? at[i].iso_atw_diff : kMax; - if ( k < kMax && at[i].valence == 1 && at[i].chem_bonds_valence == 1 && - /* the order of comparison is important */ - ((n=(int)at[i].neighbor[0]) > i /* at[n] has not been encountered yet*/ || - (int)new_ord[n] < num_atoms - num_hydrogens) /* at[n] might have been encountered; it has not been moved */ ) - { - /* found an explicit terminal hydrogen */ - num_hydrogens ++; - if ( k==0 && ATW_H <= at[i].iso_atw_diff && at[i].iso_atw_diff < ATW_H+NUM_H_ISOTOPES ) - { - k = at[i].iso_atw_diff; /* H isotope has already been marked above or elsewhere */ - } - if ( at[i].charge ) - { - /* transfer charge from the hydrogen */ - at[n].charge += at[i].charge; - at[i].charge = 0; - if (bFixTermHChrg) - { - /*^^^^^ Fixed bug (July 6, 2008 IPl) : - if terminal H was charged (not neutralized before call of remove_terminal_HDT) - and had an ordering number > than that of heavy-atom neighbour, then - charge on neighbour atom was not adjusted (though charge on H was removed). - ^^^^^ */ - if ( i > n ) - /* new_at[new_ord[n]] has been created and filled already */ - new_at[new_ord[n]].charge = at[n].charge; - } - /*^^^^^ */ - } - new_ord[i] = num_atoms - num_hydrogens; /* move hydrogens to the end of the list */ - } - else - { - /* atom is not an explicit terminal hydrogen */ - new_ord[i] = i - num_hydrogens; /* adjust non-hydrogens positions */ - } - - /* copy atom to the new position */ - new_at[new_ord[i]] = at[i]; - - } /* i */ - - } /* general case except H-H */ - - if ( num_hydrogens ) { - int num_others = num_atoms-num_hydrogens; /* atoms which are not terminal H, D, T */ - if ( num_hydrogens > 1 ) { - /* sort hydrogen isotopes in ascending order, */ - /* orig, numbers being the secondary sorting key */ - qsort( new_at+num_others, num_hydrogens, sizeof(new_at[0]), cmp_iso_atw_diff_component_no ); - } - /* save new numbering of hydrogen atoms using temporarily saved orig numbering */ - for ( i = num_others; i < num_atoms; i ++ ) { - new_ord[(int)new_at[i].component] = i; - } - - /* renumber neighbors according to new_ord[] and detach terminal hydrogens */ - for ( i = 0; i < num_others; i++ ) { - memset( new_HydrogenAt_order, 0, sizeof(new_HydrogenAt_order) ); - memset( new_OtherNeigh_order, 0, sizeof(new_OtherNeigh_order) ); - num_OtherNeigh = 0; - num_HydrogenAt = 0; - num_H = 0; - - for ( m = 0; m < MAX_NUM_STEREO_BONDS && new_at[i].sb_parity[m]; m ++ ) { - old_trans[m] = 2 - (new_at[i].sn_ord[m] + new_at[i].sb_ord[m] + (new_at[i].sn_ord[m] > new_at[i].sb_ord[m]))%2; - } - - for ( k = j = val = 0; k < new_at[i].valence; k++ ) { - if ( num_others <= ( n = new_ord[new_at[i].neighbor[k]] ) ) { - /* discovered neighbor = disconnected explicit hydrogen - * i = new atom new_at[i] ordering number - * n = new number of the explicit H - * k = ordering number of the explicit H in new_at[i] adjacency list - */ - if ( 0 < new_at[n].iso_atw_diff && new_at[n].iso_atw_diff < ATW_H+NUM_H_ISOTOPES ) { - /* make explicit isotopic H implicit */ - new_at[i].num_iso_H[new_at[n].iso_atw_diff-1] ++; /* isotopic H */ - num_HydrogenAt += !new_HydrogenAt_order[new_at[n].iso_atw_diff]; - new_HydrogenAt_order[new_at[n].iso_atw_diff] = k+1; - } else { - /* make explicit non-isotopic H implicit */ - new_at[i].num_H ++; /* non-isotopic H */ - num_HydrogenAt += !num_H; - num_H ++; - new_HydrogenAt_order[0] = k+1; - } - /* decrement chem. bonds valence because one bond is removed */ - new_at[i].chem_bonds_valence = inchi_max( 0, new_at[i].chem_bonds_valence-1 ); - new_at[n].neighbor[0] = i; /* update removed hydrogen neighbor number */ - if ( new_at[i].sb_parity[0] ) { - /* if the removed H is an SB neighbor then mark it as removed */ - for ( m = 0; m < MAX_NUM_STEREO_BONDS && new_at[i].sb_parity[m]; m ++ ) { - if ( k == (int)new_at[i].sn_ord[m] ) { - new_at[i].sn_ord[m] = -(new_at[n].iso_atw_diff+1); - /* means the SB neighbor has been removed; (-4)=H, (-3)=1H, (-2)=D, (-1)=T */ - } - } - } - } else { - /* discovered a regular (not an explicit H) neighbor */ - if ( new_at[i].sb_parity[0] ) { - if ( num_OtherNeigh < MAX_NUM_STEREO_BONDS ) { - new_OtherNeigh_order[num_OtherNeigh] = k+1; - } - num_OtherNeigh ++; /* increment outside of if() to detect overflow */ - if ( val != k ) { - /* store new stereobond and sb-neighbor ordering numbers */ - for ( m = 0; m < MAX_NUM_STEREO_BONDS && new_at[i].sb_parity[m]; m ++ ) { - if ( k == (int)new_at[i].sb_ord[m] ) - new_at[i].sb_ord[m] = val; - else - if ( k == (int)new_at[i].sn_ord[m] ) - new_at[i].sn_ord[m] = val; - } - } - } - new_at[i].neighbor[val] = new_ord[new_at[i].neighbor[k]]; - new_at[i].bond_type[val] = new_at[i].bond_type[k]; - new_at[i].bond_stereo[val] = new_at[i].bond_stereo[k]; - val ++; - } - } - if ( new_at[i].valence > val && new_at[i].sb_parity[0] ) { - if ( num_HydrogenAt == new_at[i].valence - val && num_HydrogenAt + num_OtherNeigh <= MAXVAL ) { - /* recalculate parity so that it would describe neighbor sequence H,1H,D,T,neigh[0],neigh[1]... */ - memmove( new_OtherNeigh_order + num_HydrogenAt, new_OtherNeigh_order, num_OtherNeigh*sizeof(new_OtherNeigh_order[0])); - for ( k = 0, j = 1; k <= NUM_H_ISOTOPES; k ++ ) { - if ( new_HydrogenAt_order[k] ) { - new_OtherNeigh_order[num_HydrogenAt - j] = new_HydrogenAt_order[k]; - for ( m = 0; m < MAX_NUM_STEREO_BONDS && new_at[i].sb_parity[m]; m ++ ) { - if ( (int)new_at[i].sn_ord[m] == -(k+1) ) { - new_at[i].sn_ord[m] = -j; - /* negative means explicit H isotope ord are - (contiguously) in front of the adjacency list */ - } - } - j ++; - } - } - /* at this point new_OtherNeigh_order[] contains - incremented old ordering numbers in new order */ - k = insertions_sort_AT_RANK( new_OtherNeigh_order, num_HydrogenAt + num_OtherNeigh ); - k = k%2; /* seems to be of no use */ - /*if ( k ) {*/ - /* - for ( m = 0; m < MAX_NUM_STEREO_BONDS && new_at[i].sb_parity[m]; m ++ ) { - if ( PARITY_WELL_DEF(new_at[i].sb_parity[m]) ) { - if ( old_trans[m] != 2 - (4 + new_at[i].sn_ord[m] + new_at[i].sb_ord[m] + (new_at[i].sn_ord[m] > new_at[i].sb_ord[m]))%2 ) { - new_at[i].sb_parity[m] = 3 - new_at[i].sb_parity[m]; - } - } - } - */ - /*}*/ - } -#ifdef _DEBUG - else { - /* error */ - int stop = 1; - } -#endif - } - new_at[i].valence = val; - } - memcpy( at, new_at, sizeof(at[0])*num_atoms ); - ret = num_others; - } else { - ret = num_atoms; - } -exit_function: - if ( new_ord ) - inchi_free ( new_ord ); - if ( new_at ) - inchi_free ( new_at ); - return ret; -} -/************************************************************************/ -int add_DT_to_num_H( int num_atoms, inp_ATOM *at ) -/* assume num_1H, num_D and num_T are not included in num_H */ -{ - int i, j; - for ( i = 0; i < num_atoms; i ++ ) { - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) - at[i].num_H += at[i].num_iso_H[j]; - } - return 0; -} -/***************************************************************/ -/* not used --- -int FixAromaticOxygenAndSulfur( inp_ATOM *atom ) -{ - if ( !atom->elname[1] && (atom->elname[0]=='O' || atom->elname[0]=='S') && - atom->valence==2 && !atom->charge && !atom->radical && - atom->bond_type[0] + atom->bond_type[1] == 3 ) { - atom->charge = 1; - return 1; // fixed - } - return 0; -} -*/ -/******************************************************************** - - InChI post-version 1.01 features implementation - (v. 1.04 : underivatize is still experimental and for engineering mode) - - ********************************************************************/ -#if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) - -static U_CHAR el_number_O; -static U_CHAR el_number_C; -static U_CHAR el_number_N; -static U_CHAR el_number_P; -static U_CHAR el_number_S; -static U_CHAR el_number_Si; -static U_CHAR el_number_F; -static U_CHAR el_number_Cl; -static U_CHAR el_number_Br; -static U_CHAR el_number_I; -static U_CHAR el_number_B; - -typedef struct tagAtPair { - AT_NUMB at[2]; /* at[0] < at[1] */ -} R2C_ATPAIR; - -int DisconnectInpAtBond( inp_ATOM *at, AT_NUMB *nOldCompNumber, int iat, int neigh_ord ); -int ExtractConnectedComponent( inp_ATOM *at, int num_at, int component_number, inp_ATOM *component_at ); -int mark_arom_bonds( inp_ATOM *at, int num_atoms ); - -void set_R2C_el_numbers( void ); -int UnMarkDisconnectedComponents( ORIG_ATOM_DATA *orig_inp_data ); -int UnMarkRingSystemsInp( inp_ATOM *at, int num_atoms ); -int UnMarkOtherIndicators( inp_ATOM *at, int num_atoms ); -int UnMarkOneComponent( inp_ATOM *at, int num_atoms ); -int subtract_DT_from_num_H( int num_atoms, inp_ATOM *at ); -int add_inp_ATOM( inp_ATOM *at, int len_at, int len_cur, inp_ATOM *add, int len_add ); -int cmp_r2c_atpair( const void *p1, const void *p2 ); -int has_atom_pair( R2C_ATPAIR *ap, int num_ap, AT_NUMB at1, AT_NUMB at2 ); -int mark_atoms_ap( inp_ATOM *at, AT_NUMB start, R2C_ATPAIR *ap, int num_ap, int num, AT_NUMB cFlags ); - -/********************************************************************/ -void set_R2C_el_numbers( void ) -{ - if ( !el_number_O ) { - el_number_O = (U_CHAR)get_periodic_table_number( "O" ); - el_number_C = (U_CHAR)get_periodic_table_number( "C" ); - el_number_N = (U_CHAR)get_periodic_table_number( "N" ); - el_number_P = (U_CHAR)get_periodic_table_number( "P" ); - el_number_S = (U_CHAR)get_periodic_table_number( "S" ); - el_number_Si = (U_CHAR)get_periodic_table_number( "Si" ); - el_number_F = (U_CHAR)get_periodic_table_number( "F" ); - el_number_Cl = (U_CHAR)get_periodic_table_number( "Cl" ); - el_number_Br = (U_CHAR)get_periodic_table_number( "Br" ); - el_number_I = (U_CHAR)get_periodic_table_number( "I" ); - el_number_B = (U_CHAR)get_periodic_table_number( "B" ); - } -} -/***************************************************************/ -int UnMarkDisconnectedComponents( ORIG_ATOM_DATA *orig_inp_data ) -{ - int i; - for ( i = 0; i < orig_inp_data->num_inp_atoms; i ++ ) { - orig_inp_data->at[i].orig_compt_at_numb = 0; - orig_inp_data->at[i].component = 0; - } - if ( orig_inp_data->nCurAtLen ) { - inchi_free( orig_inp_data->nCurAtLen ); - orig_inp_data->nCurAtLen = NULL; - } - if ( orig_inp_data->nOldCompNumber ) { - inchi_free( orig_inp_data->nOldCompNumber ); - orig_inp_data->nOldCompNumber = NULL; - } - orig_inp_data->num_components = 0; - return 0; -} -/***************************************************************/ -int UnMarkRingSystemsInp( inp_ATOM *at, int num_atoms ) -{ - int i; - for ( i = 0; i < num_atoms; i ++ ) { - at[i].bCutVertex = 0; - at[i].nRingSystem = 0; - at[i].nNumAtInRingSystem = 0; - at[i].nBlockSystem = 0; - } - return 0; -} -/***************************************************************/ -int UnMarkOtherIndicators( inp_ATOM *at, int num_atoms ) -{ - int i; - for ( i = 0; i < num_atoms; i ++ ) { - at[i].at_type = 0; - at[i].cFlags = 0; - } - return 0; -} -/***************************************************************/ -int UnMarkOneComponent( inp_ATOM *at, int num_atoms ) -{ - int i; - for ( i = 0; i < num_atoms; i ++ ) { - at[i].orig_compt_at_numb = 0; - at[i].component = 0; - } - return 0; -} -/***************************************************************/ -int subtract_DT_from_num_H( int num_atoms, inp_ATOM *at ) -/* assume num_1H, num_D and num_T are included in num_H */ -{ - int i, j; - for ( i = 0; i < num_atoms; i ++ ) { - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) - at[i].num_H -= at[i].num_iso_H[j]; - } - return 0; -} -/***************************************************************/ -int add_inp_ATOM( inp_ATOM *at, int len_at, int len_cur, inp_ATOM *add, int len_add ) -{ - int i, j; - inp_ATOM *a; - /* chack correctness */ - if ( len_cur < 0 ) - return len_cur; - if ( len_add < 0 ) - return len_add; - if ( len_cur + len_add > len_at ) - return -1; - /* copy */ - memcpy( at+len_cur, add, len_add*sizeof(at[0]) ); - /* modify */ - if ( len_cur ) { - a = at + len_cur; - for ( i = 0; i < len_add; i ++ ) { - for ( j = 0; j < a[i].valence; j ++ ) { - a[i].neighbor[j] += len_cur; - } - } - } - return len_cur + len_add; -} -/****************************************************************************/ -int mark_arom_bonds( inp_ATOM *at, int num_atoms ) -{ - INCHI_MODE bTautFlags=0, bTautFlagsDone = 0; - inp_ATOM *at_fixed_bonds_out = NULL; - T_GROUP_INFO *t_group_info = NULL; - int ret; - ret = mark_alt_bonds_and_taut_groups ( at, at_fixed_bonds_out, num_atoms, - t_group_info, &bTautFlags, &bTautFlagsDone ); - return ret; - -} - -/********************************************************************/ -int cmp_r2c_atpair( const void *p1, const void *p2 ) -{ - const R2C_ATPAIR *ap1 = (const R2C_ATPAIR *)p1; - const R2C_ATPAIR *ap2 = (const R2C_ATPAIR *)p2; - int diff = (int)ap1->at[0] - (int)ap2->at[0]; - if ( !diff ) { - diff = (int)ap1->at[1] - (int)ap2->at[1]; - } - return diff; -} -/***************************************************************/ -int has_atom_pair( R2C_ATPAIR *ap, int num_ap, AT_NUMB at1, AT_NUMB at2 ) -{ - R2C_ATPAIR ap1; - int i1, i2, i3, diff; - int n = at1 > at2; - - ap1.at[n] = at1; - ap1.at[1-n] = at2; - i1 = 0; - i2 = num_ap-1; - /* search for ap1 by simple bisections */ - do { - i3 = (i1 + i2)/2; - if ( !(diff = cmp_r2c_atpair(&ap1, ap+i3) ) ) { - return i3+1; /* found => positive number */ - } else - if ( diff > 0 ) { - i1 = i3 + 1; - } else { - i2 = i3 - 1; - } - }while ( i2 >= i1 ); - return 0; /* not found */ -} - -/***************************************************************/ -/* DFS search for atoms that do not have a flag */ -int mark_atoms_ap( inp_ATOM *at, AT_NUMB start, R2C_ATPAIR *ap, int num_ap, int num, AT_NUMB cFlags ) -{ - if ( !at[start].at_type ) { - int i; - AT_NUMB neigh; - at[start].at_type = cFlags; - num ++; - for ( i = 0; i < at[start].valence; i ++ ) { - neigh = at[start].neighbor[i]; - if ( has_atom_pair( ap, num_ap, start, neigh ) ) - continue; - num = mark_atoms_ap( at, neigh, ap, num_ap, num, cFlags ); - } - } - return num; /* number of atoms traversed forward from at[start] */ -} - -#endif /* RING2CHAIN || UNDERIVATIZE */ - -#if ( UNDERIVATIZE == 1 ) -/***************************************************************/ - -#ifdef NEVER -typedef struct tagAtTypeBitmap { - AT_NUMB ord1 : 5; /* up to 2^5-1 = 31 = 0x0037 */ - AT_NUMB ord2 : 5; - AT_NUMB type : 6; /* up to 2^6-1 = 63 = 0x0077 */ -} AtTypeBitmap; -typedef union tagAtTypeUnion { - AT_NUMB num; - AtTypeBitmap bits; -} AtTypeUnion; -#endif - -#define DERIV_AT_LEN 4 -typedef struct tagDerivAttachment { - char typ[DERIV_AT_LEN]; - char ord[DERIV_AT_LEN]; - char num[DERIV_AT_LEN]; -} DERIV_AT; - - -#define DERIV_BRIDGE_O 0x0001 /* R1-O-R2 => R1-OH + HO-R2 */ -#define DERIV_BRIDGE_NH 0x0002 /* R1-NH-R2 amine */ -#define DERIV_AMINE_tN 0x0004 /* R1-N(-R2)-R3 tertiary amine */ -#define DERIV_RING_O 0x0008 /* -O- in a ring */ -#define DERIV_RING_NH 0x0010 /* -NH- in a ring */ -#define DERIV_UNMARK 0x0040 /* unmark the cut */ -#define DERIV_NOT 0x1000 /* cannot be a derivatization agent atom */ - -#define DERIV_DUPLIC 0x0080 /* duplicated disconnection */ - -#define DERIV_RING (DERIV_RING_O | DERIV_RING_NH) - -#define MAX_AT_DERIV 12 -#define NOT_AT_DERIV 99 -#define MIN_AT_LEFT_DERIV 3 -#define NO_ORD_VAL 0x0037 - -#define CFLAG_MARK_BRANCH 1 /* for main derivative traversal */ -#define CFLAG_MARK_BLOCK 2 /* for block detection */ -#define CFLAG_MARK_BLOCK_INV ((char)~(CFLAG_MARK_BLOCK)) /* for block detection */ -#define COUNT_ALL_NOT_DERIV 1 /* 1=> count ALL atoms that are not in deriv. agents */ - /* 0=> only atoms that are not in DERIV_RING */ - -int mark_atoms_cFlags( inp_ATOM *at, int start, int num, char cFlags ); -int un_mark_atoms_cFlags( inp_ATOM *at, int start, int num, char cFlags, char cInvFlags ); -int is_C_or_S_DB_O( inp_ATOM *at, int i ); -int is_C_unsat_not_arom( inp_ATOM *at, int i ); -int is_C_Alk( inp_ATOM *at, int i, char cFlags ); -int is_Si_IV( inp_ATOM *at, int i ); -int is_P_TB_N( inp_ATOM *at, int i ); -int is_possibly_deriv_neigh( inp_ATOM *at, int iat, int iord, int type, char cFlags ); -int get_traversed_deriv_type( inp_ATOM *at, DERIV_AT *da, int k, DERIV_AT *da1, char cFlags ); -int add_to_da( DERIV_AT *da, DERIV_AT *add ); -int mark_atoms_deriv( inp_ATOM *at, DERIV_AT *da, int start, int num, char cFlags, int *pbFound ); -int count_one_bond_atoms( inp_ATOM *at, DERIV_AT *da, int start, int ord, char cFlags, int *bFound ); -int is_silyl( inp_ATOM *at, int start, int ord_prev ); -int is_Me_or_Et( inp_ATOM *at, int start, int ord_prev ); -int is_CF3_or_linC3F7( inp_ATOM *at, int start, int ord_prev ); -int is_phenyl( inp_ATOM *at, int start, int ord_prev ); -int is_deriv_ring( inp_ATOM *at, int start, int num_atoms, DERIV_AT *da1, int idrv ); -int is_deriv_chain( inp_ATOM *at, int start, int num_atoms, DERIV_AT *da1, int idrv ); -int is_deriv_chain_or_ring( inp_ATOM *at, int start, int num_atoms, DERIV_AT *da1, int *idrv ); -int remove_deriv( DERIV_AT *da1, int idrv ); -int remove_deriv_mark( DERIV_AT *da1, int idrv ); -int EliminateDerivNotInList( inp_ATOM *at, DERIV_AT *da, int num_atoms ); -int make_single_cut( inp_ATOM *at, DERIV_AT *da, int iat, int icut ); - -/***************************************************************/ -/* DFS search for atoms that do not have a flag */ -int mark_atoms_cFlags( inp_ATOM *at, int start, int num, char cFlags ) -{ - if ( !(at[start].cFlags & cFlags) ) { - int i; - at[start].cFlags |= cFlags; - num ++; - for ( i = 0; i < at[start].valence; i ++ ) { - num = mark_atoms_cFlags( at, at[start].neighbor[i], num, cFlags ); - } - } - return num; /* number of atoms traversed forward from at[start] */ -} -/***************************************************************/ -/* DFS search for atoms that do have a flag */ -int un_mark_atoms_cFlags( inp_ATOM *at, int start, int num, char cFlags, char cInvFlags ) -{ - if ( at[start].cFlags & cFlags ) { - int i; - at[start].cFlags &= cInvFlags; - num ++; - for ( i = 0; i < at[start].valence; i ++ ) { - num = un_mark_atoms_cFlags( at, at[start].neighbor[i], num, cFlags, cInvFlags ); - } - } - return num; /* number of atoms traversed forward from at[start] */ -} -/***************************************************************/ -int is_C_or_S_DB_O( inp_ATOM *at, int i ) -{ - int j, neigh; - if ( at[i].el_number != el_number_C && - at[i].el_number != el_number_S || - at[i].charge || at[i].radical ) - return 0; - for ( j = 0; j < at[i].valence; j ++ ) { - neigh = at[i].neighbor[j]; - if ( (at[neigh].el_number == el_number_O || - at[neigh].el_number == el_number_S ) && - !at[neigh].num_H && 1 == at[neigh].valence && - 2 == at[neigh].chem_bonds_valence ) { - return 1; - } - } - return 0; -} -/***************************************************************/ -int is_C_unsat_not_arom( inp_ATOM *at, int i ) -{ - int j, neigh, num_arom, num_DB; - if ( at[i].el_number != el_number_C || - at[i].valence == at[i].chem_bonds_valence || - at[i].valence+1 < at[i].chem_bonds_valence || - at[i].chem_bonds_valence + at[i].num_H != 4 || - at[i].charge || at[i].radical ) - return 0; - num_arom = num_DB = 0; - for ( j = 0; j < at[i].valence; j ++ ) { - neigh = at[i].neighbor[j]; - num_arom += at[i].bond_type[j] == BOND_TYPE_ALTERN; - if ( (at[neigh].el_number == el_number_O || - at[neigh].el_number == el_number_S ) && - !at[neigh].num_H && 1 == at[neigh].valence && - 2 == at[neigh].chem_bonds_valence ) { - continue; - } - num_DB += at[i].bond_type[j] == BOND_TYPE_DOUBLE; - } - return num_DB && !num_arom; -} -/***************************************************************/ -int is_C_Alk( inp_ATOM *at, int i, char cFlags ) -{ - if ( at[i].el_number == el_number_C && - at[i].valence == at[i].chem_bonds_valence ) { - int j, k; - U_CHAR el; - for ( j = 0; j < at[i].valence; j ++ ) { - k = at[i].neighbor[j]; - if ( at[k].cFlags & cFlags ) - continue; - el = at[k].el_number; - if ( el != el_number_C && - el != el_number_F && - el != el_number_Cl && - el != el_number_Br && - el != el_number_I ) { - return 0; - } - } - return 1; - } - return 0; -} -/***************************************************************/ -int is_Si_IV( inp_ATOM *at, int i ) -{ - if ( at[i].el_number != el_number_Si || - at[i].charge || at[i].radical || at[i].valence != 4 || at[i].chem_bonds_valence != 4 ) - return 0; - return 1; -} -/***************************************************************/ -int is_P_TB_N( inp_ATOM *at, int i ) -{ - int j, k; - if ( at[i].el_number != el_number_P || at[i].chem_bonds_valence - at[i].valence != 2 ) - return 0; - for ( j = 0; j < at[i].valence; j ++ ) { - k = at[i].neighbor[j]; - if ( at[k].el_number == el_number_N && at[k].valence == 1 && at[k].chem_bonds_valence == 3 ) - return 1; - } - return 0; -} -/***************************************************************/ -int is_possibly_deriv_neigh( inp_ATOM *at, int iat, int iord, int type, char cFlags ) -{ - int neigh = at[iat].neighbor[iord]; - int neigh_from = -1; - U_CHAR el = at[neigh].el_number; - int bOk = 0; - switch ( type ) { - case DERIV_BRIDGE_O: - neigh_from = at[iat].neighbor[!iord]; - /* -> A--O--B -> traversing from A(neigh_from) to B(neigh); may we cut O--B bond? */ - /* do not cut bond "---" in A=Si(IV), B(=O), B=C: Si(IV)-O---B(=O) */ - if ( !(is_C_or_S_DB_O( at, neigh ) && is_Si_IV( at, neigh_from )) && - !is_C_unsat_not_arom( at, neigh ) ) { - bOk = ( el == el_number_C || - el == el_number_Si || - el == el_number_S || - el == el_number_P ); - } - break; - case DERIV_BRIDGE_NH: - /* -> A--NH--B -> traversing from A(neigh_from) to B(neigh); may we cut NH--B bond? */ - bOk = ( is_C_or_S_DB_O( at, neigh ) || - is_C_Alk( at, neigh, cFlags ) || - is_Si_IV( at, neigh ) || - is_P_TB_N( at, neigh ) ) && !(is_C_unsat_not_arom( at, neigh )); - break; - case DERIV_AMINE_tN: - bOk = ( is_C_or_S_DB_O( at, neigh ) || - is_C_Alk( at, neigh, cFlags ) || - is_Si_IV( at, neigh ) || - is_P_TB_N( at, neigh ) ) && !(is_C_unsat_not_arom( at, neigh )); - break; - } - return bOk; -} -/***************************************************************/ -/* determines derivative type on the forward step of the DFS */ -/***************************************************************/ -int get_traversed_deriv_type( inp_ATOM *at, DERIV_AT *da, int k, DERIV_AT *da1, char cFlags ) -{ - int i, j, m, nBlockSystemFrom, nOrdBack1, nOrdBack2, nOrdBack3, nBackType1, nBackType2; - memset( da1, 0, sizeof(*da1) ); - if ( at[k].cFlags & cFlags ) { - return 0; - } - for ( m = 0; m < at[k].valence && !(at[(int)at[k].neighbor[m]].cFlags & cFlags); m ++ ) - ; - if ( m == at[k].valence ) - return -1; /* error: at least one neighbor must have cFlags */ - if ( at[k].valence == 1 && at[k].num_H && ( - at[k].el_number == el_number_O || - at[k].el_number == el_number_N || - at[k].el_number == el_number_S || - at[k].el_number == el_number_P ) ) { - return DERIV_NOT; - } - if ( is_el_a_metal( at[k].el_number ) ) { - return DERIV_NOT; - } -#ifdef NEVER - if ( at[k].el_number == el_number_N && at[k].valence >= 2 && at[k].chem_bonds_valence <= 3 ) { - return DERIV_NOT; /* prohibit -N-, -N=, allow -N# as in isocyano -N#C or NO2 */ - } -#endif - /* m is the ord of the bond from which at[k] was reached first time */ - if ( at[k].nNumAtInRingSystem == 1 && (at[k].el_number == el_number_O || at[k].el_number == el_number_S) && - at[k].valence == 2 && at[k].chem_bonds_valence == 2 && - !at[k].num_H && !at[k].charge && !at[k].radical) { - /* -> A--O--B -> traversing from A to B; cut O--B */ - /* check for carboxy A(=O)-O-B and A--O--B(=O) */ - /* int has_A_CO = is_C_or_S_DB_O( at, at[k].neighbor[m] ); */ - int has_B_CO = is_C_or_S_DB_O( at, at[k].neighbor[!m] ); - int is_A_Si_IV = is_Si_IV( at, at[k].neighbor[m] ); - /* int is_B_Si_IV = is_Si_IV( at, at[k].neighbor[!m] );*/ - if ( is_A_Si_IV && has_B_CO ) { - ; /* do not cut bond --- in A=Si(IV), B(=O), B=C: Si(IV)-O---B(=O) */ - } else - if ( is_possibly_deriv_neigh( at, k, !m, DERIV_BRIDGE_O, cFlags ) ) { - da1->ord[0] = !m; /* ord of neighbor B, not traversed yet */ - da1->typ[0] = DERIV_BRIDGE_O; /* type */ - return DERIV_BRIDGE_O; /* R-O-R */ - } - } - if ( at[k].bCutVertex && at[k].el_number == el_number_N && - at[k].valence == 2 && at[k].chem_bonds_valence == at[k].valence && - at[k].valence+at[k].num_H == 3 && !at[k].charge && !at[k].radical) { - da1->ord[0] = !m; /* ord of neighbor B, not traversed yet */ - da1->typ[0] = DERIV_BRIDGE_NH; /* type */ - return DERIV_BRIDGE_NH; /* R1-N(-R2)-R3 or R1-NH-R2 amine */ - } - if ( at[k].bCutVertex && at[k].el_number == el_number_N && - at[k].valence == 3 && at[k].chem_bonds_valence == at[k].valence && - at[k].valence+at[k].num_H == 3 && !at[k].charge && !at[k].radical) { - int rm1 = ( at[at[k].neighbor[m]].nRingSystem == at[at[k].neighbor[(m+1)%3]].nRingSystem ); - int rm2 = ( at[at[k].neighbor[m]].nRingSystem == at[at[k].neighbor[(m+2)%3]].nRingSystem ); - int r12 = ( at[at[k].neighbor[(m+1)%3]].nRingSystem == at[at[k].neighbor[(m+2)%3]].nRingSystem ); - int ord[2]= {-1, -1}; - i = 0; /* type: tertriary amine */ - switch( rm1 + rm2 + r12 ) { - case 0: - /* -N< has no ring bonds */ - if ( is_possibly_deriv_neigh( at, k, (m+1)%3, DERIV_AMINE_tN, cFlags ) ) { - ord[i ++] = (m+1)%3; /* ord of a non-ring neighbor, not traversed yet */ - } - if ( is_possibly_deriv_neigh( at, k, (m+2)%3, DERIV_AMINE_tN, cFlags ) ) { - ord[i ++] = (m+2)%3; /* ord of another non-ring neighbor, not traversed yet */ - } - if ( i == 2 && ord[0] > ord[1] ) { - int tmp = ord[0]; - ord[0] = ord[1]; - ord[1] = tmp; - } - break; - - case 1: - if ( rm1 && is_possibly_deriv_neigh( at, k, (m+2)%3, DERIV_AMINE_tN, cFlags ) ) { - ord[i++] = (m+2)%3; /* ord of a single non-ring neighbor, not traversed yet */ - } else - if ( rm2 && is_possibly_deriv_neigh( at, k, (m+1)%3, DERIV_AMINE_tN, cFlags ) ) { - ord[i++] = (m+1)%3; /* ord of a single non-ring neighbor, not traversed yet */ - } - } - for ( j = 0; j < i; j ++ ) { - da1->ord[j] = ord[j]; - da1->typ[j] = DERIV_AMINE_tN; - } - if ( i ) { - return DERIV_AMINE_tN; - } - return 0; /* all neighbors or two untraversed edges are in one ring system */ - } - if ( at[k].bCutVertex && /* DD */ - at[k].valence == at[k].chem_bonds_valence && - (!at[k].num_H || at[k].el_number == el_number_C && 1 == at[k].num_H) && - !at[k].charge && !at[k].radical && - (at[k].el_number == el_number_C && at[k].valence+at[k].num_H == 4 || - at[k].el_number == el_number_Si && at[k].valence == 4 || - at[k].el_number == el_number_B && at[k].valence == 3) ) { - - /* entering path: ->X--O--DD - --X--O - | \ / DD = C, Si, B - | DD - | / \ O = O, NH - --Y--O - X, Y -- ignored for now - */ - nBlockSystemFrom = 0; - nBackType1 = nBackType2 = 0; - nOrdBack1 = nOrdBack2 = nOrdBack3 = -1; - j=(int)at[k].neighbor[m]; - if ( (at[j].el_number == el_number_O || at[j].el_number == el_number_S) && at[j].valence == 2 && - at[j].chem_bonds_valence == at[j].valence && - at[j].nNumAtInRingSystem >= 5 && - !at[j].num_H && !at[j].charge && !at[j].radical ) { - nBackType1 = DERIV_RING_O; - nBlockSystemFrom = at[j].nBlockSystem; - nOrdBack1 = m; /* predecessor */ - } else - if ( at[j].el_number == el_number_N && at[j].valence == 2 && - at[j].chem_bonds_valence == at[j].valence && - at[j].nNumAtInRingSystem >= 5 && - 1==at[j].num_H && !at[j].charge && !at[j].radical ) { - nBackType1 = DERIV_RING_NH; - nBlockSystemFrom = at[j].nBlockSystem; - nOrdBack1 = m; /* predecessor */ - } - if ( nBlockSystemFrom ) { - int num1, num2, bFound; - at[k].cFlags |= CFLAG_MARK_BLOCK; - num1 = mark_atoms_cFlags( at, at[k].neighbor[nOrdBack1], 1, CFLAG_MARK_BLOCK ); - for ( i = 0; i < at[k].valence; i ++ ) { - if ( i == nOrdBack1 ) - continue; - j=(int)at[k].neighbor[i]; - bFound = 0; - if ( at[j].cFlags & CFLAG_MARK_BLOCK ) { - if ( (at[j].el_number == el_number_O || at[j].el_number == el_number_S) && at[j].valence == 2 && - at[j].chem_bonds_valence == at[j].valence && - at[j].nNumAtInRingSystem >= 5 && - !at[j].num_H && !at[j].charge && !at[j].radical ) { - bFound = 1; - if ( nOrdBack2 < 0 ) { - nOrdBack2 = i; /* predecessor #2 */ - nBackType2 = DERIV_RING_O; - } else { - nOrdBack3 = i; /* predecessor #3 -- should not happen */ - } - } - if ( at[j].el_number == el_number_N && at[j].valence == 2 && - at[j].chem_bonds_valence == at[j].valence && - at[j].nNumAtInRingSystem >= 5 && - 1==at[j].num_H && !at[j].charge && !at[j].radical ) { - bFound = 1; - if ( nOrdBack2 < 0 ) { - nOrdBack2 = i; /* predecessor #2 */ - nBackType2 = DERIV_RING_NH; - } else { - nOrdBack3 = i; /* predecessor #3 -- should not happen */ - } - } - if ( !bFound ) { - nOrdBack3 = 99; /* reject: wrong neighboring atom in the same block */ - break; - } - } - } - num2 = un_mark_atoms_cFlags( at, k, 0, CFLAG_MARK_BLOCK, CFLAG_MARK_BLOCK_INV ); - if ( num1 != num2 ) { - return -1; /* mark_atoms_cFlags() program error */ - } - if ( nOrdBack2 >= 0 && nOrdBack3 < 0 ) { - if ( nOrdBack1 < nOrdBack2 ) { - da1->ord[0] = nOrdBack1; /* ord of a ring neighbor, traversed */ - da1->typ[0] = nBackType1; - da1->ord[1] = nOrdBack2; /* ord of another ring neighbor, not traversed yet */ - da1->typ[1] = nBackType2; - } else { - da1->ord[0] = nOrdBack2; /* ord of a ring neighbor, traversed */ - da1->typ[0] = nBackType2; - da1->ord[1] = nOrdBack1; /* ord of another ring neighbor, not traversed yet */ - da1->typ[1] = nBackType1; - } - return nBackType1 | nBackType2; - } - } - } - return 0; -} -/***************************************************************/ -int add_to_da( DERIV_AT *da, DERIV_AT *add ) -{ - /* if add has more than 1 element the elements are in ascending add.ord[] order */ - int i, j, len; - for ( len = 0; len < DERIV_AT_LEN && da->typ[len]; len ++ ) - ; - for ( j = 0; j < DERIV_AT_LEN && add->typ[j]; j ++ ) { - for ( i = 0; i < len; i ++ ) { - if ( add->ord[j] == da->ord[i] ) { - if ( add->typ[j] != da->typ[i] ) { - return -1; /* error, should not happen */ - } - if ( add->num[j] != da->num[i] ) { - return -2; /* error, should not happen */ - } - break; - } - } - if ( i == len ) { - if ( len < DERIV_AT_LEN ) { - da->ord[i] = add->ord[j]; - da->typ[i] = add->typ[j]; - da->num[i] = add->num[j]; - len ++; - } else { - return -3; /* overflow, should not happen */ - } - } - } - return 0; -} -/***************************************************************/ -/* DFS search for atoms that do not have a flag */ -int mark_atoms_deriv( inp_ATOM *at, DERIV_AT *da, int start, int num, char cFlags, int *pbFound ) -{ - int i, nFound=0; - DERIV_AT da1; - if ( !(at[start].cFlags & cFlags) ) { - if ( DERIV_NOT == get_traversed_deriv_type( at, da, start, &da1, cFlags ) ) { - nFound ++; /* at[start] cannot belong to a derivatizing agent */ - } - num ++; - at[start].cFlags |= cFlags; - if ( da1.typ[0] ) { - /* possibly a derivatization agent attachment point. */ - /* check neighbors that have not been traversed yet */ - int n1=0, n2=0, i1=-1, i2=-1, nFound1=0, nFound2=0; - switch( da1.typ[0] ) { - case DERIV_BRIDGE_O: - case DERIV_BRIDGE_NH: - n1 = mark_atoms_deriv( at, da, at[start].neighbor[(int)da1.ord[0]], 0, cFlags, &nFound1 ); - if ( n1 > MAX_AT_DERIV || nFound1 ) { - da1.typ[0] = 0; - } else { - da1.num[0] = n1; - nFound ++; - } - break; - case DERIV_AMINE_tN: - n1 = mark_atoms_deriv( at, da, at[start].neighbor[i1=da1.ord[0]], 0, cFlags, &nFound1 ); - if ( da1.typ[1] ) { - n2 = mark_atoms_deriv( at, da, at[start].neighbor[i2=da1.ord[1]], 0, cFlags, &nFound2 ); - } - if ( 0 < n1 && n1 <= MAX_AT_DERIV && !nFound1 ) { - da1.num[0] = n1; - i = 1; - nFound ++; - } else { - da1.ord[0] = da1.ord[1]; - da1.num[0] = da1.num[1]; - da1.typ[0] = da1.typ[1]; - da1.typ[1] = 0; - i = 0; - } - if ( 0 < n2 && n2 <= MAX_AT_DERIV && !nFound2 ) { - da1.num[i] = n2; - nFound ++; - } else { - da1.typ[i] = 0; - } - break; - case DERIV_RING_O: - case DERIV_RING_NH: - for ( i = 0; i < at[start].valence; i ++ ) { - if ( i != da1.ord[0] && i != da1.ord[1] && !(at[at[start].neighbor[i]].cFlags & cFlags) ) { - if ( !n1 ) - n1 = mark_atoms_deriv( at, da, at[start].neighbor[i1=i], 0, cFlags, &nFound1 ); - else - n2 = mark_atoms_deriv( at, da, at[start].neighbor[i2=i], 0, cFlags, &nFound2 ); - } - } - if ( n1+n2+1 > MAX_AT_DERIV || nFound1 || nFound2 ) { - da1.typ[1] = da1.typ[0] = 0; - } else { - da1.num[0] = n1; - da1.num[1] = n2; - nFound ++; - } - break; - } - if ( n1 < 0 ) - return n1; - if ( n2 < 0 ) - return n2; /* errors */ - - if ( i = add_to_da( da+start, &da1 ) ) { - return i; /* error */ - } - - *pbFound += nFound1 + nFound2 + nFound; - num += n1 + n2; - } else { - *pbFound += nFound; - } - for ( i = 0; i < at[start].valence; i ++ ) { - num = mark_atoms_deriv( at, da, at[start].neighbor[i], num, cFlags, pbFound ); - if ( num < 0 ) - return num; - } - } - /* *pbFound = number of derivatizer attachment points traversed forward from at[start] */ - return num; /* number of atoms traversed forward from at[start] */ -} -/***************************************************************/ -int count_one_bond_atoms( inp_ATOM *at, DERIV_AT *da, int start, int ord, char cFlags, int *bFound ) -{ - int num = 0; - if ( !(at[at[start].neighbor[ord]].cFlags & cFlags) ) { - at[at[start].neighbor[ord]].cFlags |= cFlags; - num ++; - num = mark_atoms_deriv( at, da, start, num, cFlags, bFound ); - } - return num; -} -/*************************************************************** - List of allowed derivatives - - Legend: - - ->- marks the bond to be disconnexted: X->-Y => XD + TY - where TY is a derivatizing agent eventually to be discarded - - Allowed Derivative Types List - ============================= - - DERIV_BRIDGE_O, DERIV_BRIDGE_NH, DERIV_AMINE_tN - ----------------------------------------------- - CH3 CH3 CH3 CH3 CH3 - | | | | | - X->-Si--CH3 X->-Si---Si--CH3 X->-Si----C--CH3 X= O, NH, N - | | | | | - CH3 CH3 CH3 CH3 CH3 - - 4 atoms 7 atoms 7 atoms is_silyl() - - - - - O O O F O - || || || | || - R--C--O->-CH3 R--C--O->-CH2--CH3 R--C--O->-C--F R--C--O->-CF2-CF2-CF3 - | - F - - - - 1 atom 2 atoms 3 atoms 10 atoms - is_Me_or_Et() is_Me_or_Et() is_CF3_or_linC3F7() - - - A. DERIV_BRIDGE_NH, DERIV_AMINE_tN - ----------------------------------- - - - O O O F O - || || || | || - N->-C--CH3 N->-C--CH2--CH3 N->-C---C--F N->-C--CF2-CF2-CF3 - | - F - - - - 3 atoms 5 atoms 8 atoms 12 atoms - is_Me_or_Et() is_Me_or_Et() is_CF3_or_linC3F7() - - DERIV_RING_O (da contains >B- or >C< or >CH- atom) - ------------ - - C----O R----O R----O - | \ | \ CH3 | \ - | > | > / | > - | \ | \ / | \ - | B--CH3 | C | CH--Ph - | / | / \ | / - | > | > \ | > - | / | / CH3 | / - C----O R----O R----O - - 5-member 5 or 6-member 5 or 6-member - - - 2 atoms 3 atoms 7 atoms - - DERIV_RING_NH - ------------ - - None in the list - -***************************************************************/ -int is_silyl( inp_ATOM *at, int start, int ord_prev ) -{ - int i, neigh, num_Me=0, iC_IV=-1, iSi_IV=-1, i_C_or_Si_IV; - - if ( at[start].el_number != el_number_Si || at[start].valence != 4 || - at[start].valence != at[start].chem_bonds_valence || - at[start].charge || at[start].radical ) - return 0; - for ( i = 0; i < at[start].valence; i ++ ) { - if ( i == ord_prev ) - continue; - neigh = at[start].neighbor[i]; - if ( at[neigh].charge || at[neigh].radical || - at[neigh].valence != at[neigh].chem_bonds_valence) - return 0; - if ( at[neigh].valence == 4 ) { - if ( at[neigh].el_number == el_number_C && iC_IV < 0 && iSi_IV < 0 ) - iC_IV = neigh; - else - if ( at[neigh].el_number == el_number_Si && iC_IV < 0 && iSi_IV < 0 ) - iSi_IV = neigh; - else - return 0; - } else - if ( at[neigh].valence == 1 && - at[neigh].valence == at[neigh].chem_bonds_valence && - at[neigh].el_number == el_number_C && at[neigh].num_H == 3 ) { - num_Me ++; - } else { - return 0; - } - } - if ( num_Me == 3 && iC_IV < 0 && iSi_IV < 0 ) - return 1; /* Si(CH3)3 */ - - /* next C(IV) or Si(IV) */ - i_C_or_Si_IV = iC_IV >= 0? iC_IV : iSi_IV >= 0? iSi_IV : -1; - if ( num_Me != 2 || i_C_or_Si_IV < 0 ) - return 0; - - num_Me = 0; - for ( i = 0; i < at[i_C_or_Si_IV].valence; i ++ ) { - neigh = at[i_C_or_Si_IV].neighbor[i]; - if ( neigh == start ) - continue; - if ( at[neigh].charge || at[neigh].radical || - at[neigh].valence != at[neigh].chem_bonds_valence) - return 0; - if ( at[neigh].valence == 1 && - at[neigh].valence == at[neigh].chem_bonds_valence && - at[neigh].el_number == el_number_C && at[neigh].num_H == 3 ) { - num_Me ++; - } else { - return 0; - } - } - if ( num_Me == 3 ) - return 2; /* Si(CH3)2Si/C(CH3)3 */ - return 0; -} -/****************************************************************/ -int is_Me_or_Et( inp_ATOM *at, int start, int ord_prev ) -{ - int i, neigh = -1; - if ( at[start].el_number != el_number_C || at[start].valence > 2 || - at[start].valence != at[start].chem_bonds_valence || - at[start].chem_bonds_valence + at[start].num_H != 4 || - at[start].charge || at[start].radical ) - return 0; - for ( i = 0; i < at[start].valence; i ++ ) { - if ( i == ord_prev ) - continue; - if ( neigh >= 0 ) - return 0; - - neigh = at[start].neighbor[i]; - if ( at[neigh].el_number != el_number_C || at[neigh].valence > 1 || - at[neigh].valence != at[neigh].chem_bonds_valence || - at[neigh].chem_bonds_valence + at[neigh].num_H != 4 || - at[neigh].charge || at[neigh].radical ) - return 0; - } - return 1 + (neigh >= 0); -} -#ifdef NEVER -/**************************************************************** - CF3 - -CF3 or -CF< - CF3 -*****************************************************************/ -int is_CF3_or_isoC3F7( inp_ATOM *at, int start, int ord_prev ) -{ - int i, k, num_C_IV, iC_IV[2], neigh, num_F, iC; - if ( at[start].el_number != el_number_C || at[start].valence != 4 || - at[start].valence != at[start].chem_bonds_valence || - at[start].chem_bonds_valence + at[start].num_H != 4 || - at[start].charge || at[start].radical ) - return 0; - - iC_IV[0] = iC_IV[1] = num_F = 0; - - for ( i = num_C_IV = 0; i < at[start].valence; i ++ ) { - if ( i == ord_prev ) - continue; - - neigh = at[start].neighbor[i]; - if ( at[neigh].valence != at[neigh].chem_bonds_valence || - at[neigh].charge || at[neigh].radical ) - return 0; - if ( at[neigh].el_number == el_number_F ) { - if ( at[neigh].chem_bonds_valence + at[neigh].num_H != 1 ) - return 0; - num_F ++; - } else - if ( at[neigh].el_number == el_number_C && - at[neigh].valence == 4 && - !at[neigh].num_H && !at[neigh].charge && !at[neigh].radical && num_C_IV < 2 ) { - - if ( num_C_IV > 1 ) - return 0; - - iC_IV[num_C_IV++] = neigh; - } - } - if ( !num_C_IV && 3 == num_F ) - return 1; /* -CF3 */ - if ( 2 != num_C_IV || 1 != num_F ) - return 0; - - /* detect iso-C3F7 */ - for ( k = 0; k < num_C_IV; k ++ ) { - iC = iC_IV[k]; - num_F = 0; - for ( i = 0; i < at[iC].valence; i ++ ) { - neigh = at[iC].neighbor[i]; - if ( neigh == start ) - continue; - if ( at[neigh].valence != at[neigh].chem_bonds_valence || - at[neigh].charge || at[neigh].radical ) - return 0; - if ( at[neigh].el_number == el_number_F ) { - if ( at[neigh].chem_bonds_valence + at[neigh].num_H != 1 ) - return 0; - num_F ++; - } else { - return 0; - } - } - if ( num_F != 3 ) - return 0; - } - return 2; /* iso-C3F7 */ -} -#endif -/**************************************************************/ -int is_CF3_or_linC3F7( inp_ATOM *at, int start, int ord_prev ) -{ - int i, num_C_IV, iC_IV, neigh, num_F, num_C=0; - AT_NUMB *p; - - while( num_C < 4 ) { - - if ( at[start].el_number != el_number_C || at[start].valence != 4 || - at[start].valence != at[start].chem_bonds_valence || - at[start].chem_bonds_valence + at[start].num_H != 4 || - at[start].charge || at[start].radical ) - return 0; - - iC_IV = num_F = 0; - - for ( i = num_C_IV = 0; i < at[start].valence; i ++ ) { - if ( i == ord_prev ) - continue; - - neigh = at[start].neighbor[i]; - if ( at[neigh].valence != at[neigh].chem_bonds_valence || - at[neigh].charge || at[neigh].radical ) - return 0; - if ( at[neigh].el_number == el_number_F ) { - if ( at[neigh].chem_bonds_valence + at[neigh].num_H != 1 ) - return 0; - num_F ++; - } else - if ( at[neigh].el_number == el_number_C && - at[neigh].valence == 4 && - !at[neigh].num_H && !at[neigh].charge && !at[neigh].radical && num_C_IV < 2 ) { - - if ( num_C_IV ) - return 0; - - iC_IV = neigh; - num_C_IV++; - } - } - if ( num_C_IV + num_F != 3 ) - return 0; - - num_C ++; /* found -CF2-C or -CF3 */ - if ( !num_C_IV ) - break; /* -CF3 */ - - /* treat next C(IV) as a new start atom */ - if ( p = is_in_the_list( at[iC_IV].neighbor, (AT_NUMB) start, at[iC_IV].valence ) ) { - start = iC_IV; - ord_prev = p - at[iC_IV].neighbor; - } else { - return -1; /* program error */ - } - } - return num_C == 1? 1 : num_C == 3? 2 : 0; -} -/****************************************************************/ -int is_phenyl( inp_ATOM *at, int start, int ord_prev ) -{ - int k, neigh, cur_at, ord; - if ( at[start].el_number != el_number_C || at[start].valence != 3 || - at[start].valence+1 != at[start].chem_bonds_valence || - at[start].chem_bonds_valence + at[start].num_H != 4 || - at[start].charge || at[start].radical ) - return 0; - - ord = (ord_prev + 1) % 3; - cur_at = start; - - for ( k = 0; k < 5; k ++ ) { - neigh = at[cur_at].neighbor[ord]; - if ( at[neigh].el_number != el_number_C || at[neigh].valence != 2 || - at[neigh].valence+1 != at[neigh].chem_bonds_valence || - at[neigh].chem_bonds_valence + at[neigh].num_H != 4 || - at[neigh].charge || at[neigh].radical ) - return 0; - ord = (at[neigh].neighbor[0] == cur_at); - cur_at = neigh; - } - return (at[cur_at].neighbor[ord] == start); -} -/****************************************************************/ -int is_deriv_ring( inp_ATOM *at, int start, int num_atoms, DERIV_AT *da1, int idrv ) -{ - int i, k, neigh_at[2], prev_ord[2], neigh, is_B=0, is_C=0; - AT_NUMB *p; - if ( da1->typ[idrv] != DERIV_RING_O || da1->typ[idrv+1] != DERIV_RING_O ) - return 0; - if ( at[start].charge || at[start].radical || at[start].valence != at[start].chem_bonds_valence ) - return 0; - if ( at[start].el_number == el_number_B && at[start].valence == 3 ) - is_B = 1; - else - if ( at[start].el_number == el_number_C && (at[start].valence == 3 || at[start].valence == 4) && - at[start].chem_bonds_valence == at[start].valence && - at[start].num_H + at[start].chem_bonds_valence == 4 ) - is_C = 1; - else - return 0; - /* locate bonds connecting >B- or >C< or >C- to the rest of the derivative */ - for ( i = k = 0; i < at[start].valence; i ++ ) { - if ( i != da1->ord[idrv] && i != da1->ord[idrv+1] ) { - neigh = at[start].neighbor[i]; - if ( k < 2 && (p = is_in_the_list( at[neigh].neighbor, (AT_NUMB) start, at[neigh].valence)) ) { - neigh_at[k] = neigh; - prev_ord[k] = p - at[neigh].neighbor; - k ++; - } else { - return -1; /* program error */ - } - } - } - if ( is_B && k == 1 && is_Me_or_Et( at, neigh_at[0], prev_ord[0]) ) - return 1; - if ( is_C && k == 1 && at[start].num_H == 1 && is_phenyl( at, neigh_at[0], prev_ord[0]) ) - return 1; - if ( is_C && k == 2 && is_Me_or_Et( at, neigh_at[0], prev_ord[0]) && - is_Me_or_Et( at, neigh_at[1], prev_ord[1]) ) - return 1; - - return 0; -} -/****************************************************************/ -int is_deriv_chain( inp_ATOM *at, int start, int num_atoms, DERIV_AT *da1, int idrv ) -{ - int i, k, prev_ord, neigh, iC, iO, iNxt; - AT_NUMB *p; - if ( !da1->typ[idrv] || (da1->typ[idrv] & DERIV_RING) ) - return 0; - if ( at[start].charge || at[start].radical || at[start].valence != at[start].chem_bonds_valence ) - return 0; - - neigh = at[start].neighbor[(int)da1->ord[idrv]]; - p = is_in_the_list( at[neigh].neighbor, (AT_NUMB) start, at[neigh].valence); - if ( !p ) - return -1; /* program error */ - prev_ord = p - at[neigh].neighbor; - - /* eliminate silyl possibility */ - if ( is_silyl( at, neigh, prev_ord ) ) - return 1; - - if ( da1->typ[idrv] == DERIV_BRIDGE_O ) { - /* check acetyl */ - iC = at[start].neighbor[!da1->ord[idrv]]; - if ( at[iC].charge || at[iC].radical || at[iC].num_H || - at[iC].el_number != el_number_C || at[iC].valence != 3 || - at[iC].valence+1 != at[iC].chem_bonds_valence ) - return 0; - for ( i = k = 0; i < at[iC].valence; i ++ ) { - iO = at[iC].neighbor[i]; - if ( at[iO].charge || at[iO].radical || at[iO].num_H || - at[iO].el_number != el_number_O || at[iO].valence != 1 || - at[iO].valence+1 != at[iO].chem_bonds_valence ) - continue; - k ++; /* number of =O */ - } - if ( k != 1 ) - return 0; - /* check derivative */ - return ( is_Me_or_Et( at, neigh, prev_ord ) || - is_CF3_or_linC3F7( at, neigh, prev_ord ) ); - } - - if ( da1->typ[idrv] == DERIV_BRIDGE_NH || da1->typ[idrv] == DERIV_AMINE_tN ) { - /* check acetyl */ - iNxt = -1; - iC = at[start].neighbor[(int)da1->ord[idrv]]; - if ( at[iC].charge || at[iC].radical || at[iC].num_H || - at[iC].el_number != el_number_C || at[iC].valence != 3 || - at[iC].valence+1 != at[iC].chem_bonds_valence ) - return 0; - for ( i = k = 0; i < at[iC].valence; i ++ ) { - iO = at[iC].neighbor[i]; - if ( at[iO].charge || at[iO].radical || at[iO].num_H || - at[iO].el_number != el_number_O || at[iO].valence != 1 || - at[iO].valence+1 != at[iO].chem_bonds_valence ) { - if ( iO != start ) { - if ( iNxt < 0 ) - iNxt = iO; - else - return 0; - } - continue; - } - k ++; /* number of =O */ - } - if ( k != 1 || iNxt < 0 ) - return 0; - /* find bond from iNxt to iC */ - p = is_in_the_list( at[iNxt].neighbor, (AT_NUMB) iC, at[iNxt].valence); - if ( !p ) - return -1; /* program error */ - prev_ord = p - at[iNxt].neighbor; - /* check derivative */ - return ( is_Me_or_Et( at, iNxt, prev_ord ) || - is_CF3_or_linC3F7( at, iNxt, prev_ord ) ); - } - return 0; -} -/****************************************************************/ -int is_deriv_chain_or_ring( inp_ATOM *at, int start, int num_atoms, DERIV_AT *da1, int *idrv ) -{ - int i, ret = -1; - if ( da1->typ[*idrv] & DERIV_RING ) { - /* find the first ord of this derivative; ord of ring derivatives are in pairs */ - int j = -1; - for ( i = 0; i < DERIV_AT_LEN && da1->typ[i]; i ++ ) { - if ( da1->typ[i] & DERIV_RING ) { - if ( i == *idrv || i+1 == *idrv ) { - *idrv = j = i; - break; - } - i ++; /* bypass the second bond to the same derivatization agent */ - } - } - /* check consistency */ - if ( j == -1 || j+1 >= DERIV_AT_LEN || - !(da1->typ[j] & DERIV_RING) || !(da1->typ[j+1] & DERIV_RING) ) { - ret = -1; /* program error */ - } else { - ret = is_deriv_ring( at, start, num_atoms, da1, j ); - } - } else - if ( da1->typ[*idrv] ) { - ret = is_deriv_ring( at, start, num_atoms, da1, *idrv ); - } - return ret; -} -/******************************************************/ -int remove_deriv( DERIV_AT *da1, int idrv ) -{ - int i, j, ret = -1; - if ( da1->typ[idrv] & DERIV_RING ) { - /* find the first ord of this derivative; ord of ring derivatives are in pairs */ - j = -1; - for ( i = 0; i < DERIV_AT_LEN && da1->typ[i]; i ++ ) { - if ( da1->typ[i] & DERIV_RING ) { - if ( i == idrv || i+1 == idrv ) { - j = i; - break; - } - i ++; /* bypass the second bond to the same derivatization agent */ - } - } - /* delete if data are consistent */ - if ( j >= 0 && j+1 < DERIV_AT_LEN && (da1->typ[j] & DERIV_RING) && (da1->typ[j+1] & DERIV_RING) ) { - for ( ; j < DERIV_AT_LEN-2 && da1->typ[j+2]; j ++ ) { - da1->typ[j] = da1->typ[j+2]; - da1->num[j] = da1->num[j+2]; - da1->ord[j] = da1->ord[j+2]; - } - for ( ; j < DERIV_AT_LEN; j ++ ) { - da1->typ[j] = 0; - da1->num[j] = 0; - da1->ord[j] = 0; - } - ret = 0; - } - } else { - j = idrv; - - for ( ; j < DERIV_AT_LEN-1 && da1->typ[j+1]; j ++ ) { - da1->typ[j] = da1->typ[j+1]; - da1->num[j] = da1->num[j+1]; - da1->ord[j] = da1->ord[j+1]; - } - for ( ; j < DERIV_AT_LEN; j ++ ) { - da1->typ[j] = 0; - da1->num[j] = 0; - da1->ord[j] = 0; - } - ret = 0; - } - return ret; -} -/******************************************************/ -int remove_deriv_mark( DERIV_AT *da1, int idrv ) -{ - int i, j, ret = -1; - if ( da1->typ[idrv] & DERIV_RING ) { - /* find the first ord of this derivative; ord of ring derivatives are in pairs */ - j = -1; - for ( i = 0; i < DERIV_AT_LEN && da1->typ[i]; i ++ ) { - if ( da1->typ[i] & DERIV_RING ) { - if ( i == idrv || i+1 == idrv ) { - j = i; - break; - } - i ++; /* bypass the second bond to the same derivatization agent */ - } - } - /* delete if data are consistent */ - if ( j >= 0 && j+1 < DERIV_AT_LEN && (da1->typ[j] & DERIV_RING) && (da1->typ[j+1] & DERIV_RING) ) { - da1->typ[j] |= DERIV_DUPLIC; - da1->typ[j+1] |= DERIV_DUPLIC; - ret = 0; - } - } else { - j = idrv; - da1->typ[j] |= DERIV_DUPLIC; - ret = 0; - } - return ret; -} -/****************************************************************/ -int EliminateDerivNotInList( inp_ATOM *at, DERIV_AT *da, int num_atoms ) -{ - int i, j, num_da, num_cuts=0, ret=0; - for ( i = 0; i < num_atoms; i ++ ) { - if ( !da[i].typ[0] ) - continue; - /* count deriative attachments */ - for ( num_da = 0; num_da < DERIV_AT_LEN && da[i].typ[num_da]; num_da ++ ) - ; - if ( num_da > 2 ) - return -1; /* should not happen */ - if ( num_da == 2 && da[i].typ[0] != da[i].typ[1] ) { - da[i].typ[0] = da[i].typ[1] = 0; /* do not allow */ - continue; - } - if ( da[i].typ[0] & DERIV_RING ) { - ret = 0; - if ( num_da == 2 && 1 + da[i].num[0] + da[i].num[1] <= MAX_AT_DERIV && - 0 < (ret=is_deriv_ring( at, i, num_atoms, da+i, 0 ) ) ) { - num_cuts += 2; - } else - if ( ret < 0 ) { - return ret; - } else { - da[i].typ[0] = da[i].typ[1] = 0; /* not a derivative */ - } - } else { - ret = 0; - if ( da[i].num[0] <= MAX_AT_DERIV && 0 < (ret = is_deriv_chain( at, i, num_atoms, da+i, 0 )) ) { - num_cuts ++; - j = 1; - } else - if ( ret < 0 ) { - return ret; - } else { - da[i].ord[0] = da[i].ord[1]; - da[i].num[0] = da[i].num[1]; - da[i].typ[0] = da[i].typ[1]; - da[i].typ[1] = 0; - j = 0; - } - if ( da[i].typ[j] && da[i].num[j] <= MAX_AT_DERIV && - 0 < (ret = is_deriv_chain( at, i, num_atoms, da+i, j )) ) { - num_cuts ++; - } else - if ( ret < 0 ) { - return ret; - } else { - da[i].typ[j] = 0; - } - } - } - return num_cuts; -} -/***************************************************************/ -int make_single_cut( inp_ATOM *at, DERIV_AT *da, int iat, int icut ) -{ - int ret = -1; /* error flag */ - int iord = (int)da[iat].ord[icut]; /* ord of the bond in iat */ - if ( da[iat].typ[icut] & DERIV_DUPLIC ) { - return 0; - } else - if ( iord < 0 ) { - return -1; /* program error */ - } else { - /* find other da[] that affect at[iat] */ - int jat = at[iat].neighbor[iord]; /* opposite atom */ - AT_NUMB *p = is_in_the_list( at[jat].neighbor, (AT_NUMB) iat, at[jat].valence ); - int jord = p? (p - at[jat].neighbor) : -1; - int i, iD=1, iT=2; - if ( jord < 0 ) { - return -1; /* program error */ - } - ret = DisconnectInpAtBond( at, NULL, iat, iord ); - if ( ret == 1 ) { - if ( da[iat].typ[icut] & DERIV_RING ) { - /* at[jat] belongs to the main structure */ - at[jat].num_H ++; /* add D to the main structure */ - at[jat].num_iso_H[iD] ++; - at[iat].num_H ++; /* add T to the derivatizing fragment */ - at[iat].num_iso_H[iT] ++; - } else { - at[iat].num_H ++; /* add D to the main structure */ - at[iat].num_iso_H[iD] ++; - at[jat].num_H ++; /* add T to the derivatizing fragment */ - at[jat].num_iso_H[iT] ++; - } - /* adjust ord for other bonds */ - for ( i = 0; i < DERIV_AT_LEN && da[iat].typ[i]; i ++ ) { - if ( da[iat].ord[i] == iord ) { - da[iat].ord[i] = -(1+da[iat].ord[i]); /* mark the bond being disconnected */ - } else - if ( da[iat].ord[i] > iord ) { - da[iat].ord[i] --; - } - } - for ( i = 0; i < DERIV_AT_LEN && da[jat].typ[i]; i ++ ) { - if ( da[jat].ord[i] == jord ) { - /* opposite atom needs the same bond to be disconnected */ - if ( da[iat].num[icut] == da[jat].num[i] ) { - iD=2; /* mark both as fragments */ - } else - if ( da[iat].num[icut] > da[jat].num[i] ) { - iD = 2; /* greater as a main structure */ - iT = 1; /* mark smaller as a derivatizing fragment */ - } - da[jat].ord[i] = -(1+da[jat].ord[i]); - da[jat].typ[i] |= DERIV_DUPLIC; - } else - if ( da[jat].ord[i] > jord ) { - da[jat].ord[i] --; - } - } - } - } - return ret; -} -/***************************************************************/ -int underivatize( ORIG_ATOM_DATA *orig_inp_data ) -{ -#define REMOVE_CUT_DERIV 1 /* remove disconnected derivatizing agents */ - int ret = 0, i, j, k, m, n, num_atoms, num_components, i_component, nFound, num, cur_num_at, len; - int num_cuts, num_ring_cuts, num_cut_pieces, num_cuts_to_check; - inp_ATOM *at = orig_inp_data->at; - INP_ATOM_DATA *inp_cur_data = NULL; - DERIV_AT *da = NULL; - int nTotNumCuts = 0; - - set_R2C_el_numbers( ); - /* prepare */ - num_atoms = remove_terminal_HDT( orig_inp_data->num_inp_atoms, at, 1 ); - /*^^^^^ always accomodate accomodate FIX_TERM_H_CHRG_BUG - IPl, July 2008*/ - orig_inp_data->num_inp_atoms = num_atoms; - - /* initialize */ - UnMarkDisconnectedComponents( orig_inp_data ); - num_cuts = 0; - /* mark */ - num_components = MarkDisconnectedComponents( orig_inp_data, 0 ); - inp_cur_data = (INP_ATOM_DATA *)inchi_calloc( num_components, sizeof(inp_cur_data[0]) ); - for ( i_component = 0; i_component < num_components; i_component ++ ) { - CreateInpAtomData( inp_cur_data+i_component, orig_inp_data->nCurAtLen[i_component], 0 ); - inp_cur_data[i_component].num_at = ExtractConnectedComponent( orig_inp_data->at, orig_inp_data->num_inp_atoms, i_component+1, inp_cur_data[i_component].at ); - /* error processing */ - if ( inp_cur_data[i_component].num_at <= 0 || orig_inp_data->nCurAtLen[i_component] != inp_cur_data[i_component].num_at ) { - ret = -(i_component+1); /* severe error */ - goto exit_function; - } - /* initialize */ - num_atoms = inp_cur_data[i_component].num_at; - at = inp_cur_data[i_component].at; - add_DT_to_num_H( num_atoms, at ); - - UnMarkRingSystemsInp( at, num_atoms ); - UnMarkOtherIndicators( at, num_atoms ); - UnMarkOneComponent( at, num_atoms ); - MarkRingSystemsInp( at, num_atoms, 0 ); - ret = mark_arom_bonds( at, num_atoms ); - if ( ret < 0 ) { - goto exit_function; - } - ret = 0; - if ( da ) inchi_free( da ); - da = (DERIV_AT *)inchi_calloc( num_atoms, sizeof(da[0]) ); - - /* detect derivatives */ - nFound = 0; - for ( i = 0; i < num_atoms; i ++ ) { - if ( at[i].bCutVertex && !da[i].typ[0] ) { - for ( k = 0; k < at[i].valence; k ++ ) { - num = count_one_bond_atoms( at, da, i, k, CFLAG_MARK_BRANCH, &nFound ); - UnMarkOtherIndicators( at, num_atoms ); - if ( num < 0 ) { - ret = num; /* severe error */ - goto exit_function; - } - } - } - } - /* prepare cuts: remove cuts that are not to be done */ - /* in addition, count ring cuts DERIV_RING */ - num_ring_cuts = 0; - num_cuts = 0; - num_cut_pieces = 0; - for ( i = num = 0; i < num_atoms; i ++ ) { - for ( len = 0; len < MAX_AT_DERIV && da[i].typ[len]; len ++ ) - ; - switch( len ) { - case 0: - continue; - case 1: - /* single cut: unconditional */ - num_cuts += 1; - num_cut_pieces += 1; - continue; - case 2: - if ( (da[i].typ[0] & DERIV_RING) && (da[i].typ[1] & DERIV_RING) ) { - /* double cut, unconditional */ - num_ring_cuts += 2; - num_cuts += 2; - num_cut_pieces += 1; - continue; - } else - if ( da[i].typ[0] == DERIV_AMINE_tN && da[i].typ[1] == DERIV_AMINE_tN ) { - /* double cut, unconditional */ - num_cuts += 2; - num_cut_pieces += 2; - continue; - } - if ( da[i].typ[0] == da[i].typ[1] ) { - /* DERIV_BRIDGE_O or DERIV_BRIDGE_NH; cut off the smallest */ - if ( 0 == is_deriv_chain( at, i, num_atoms, da+i, 0 ) ) { - da[i].num[0] = NOT_AT_DERIV; - } - if ( 0 == is_deriv_chain( at, i, num_atoms, da+i, 1 ) ) { - da[i].num[1] = NOT_AT_DERIV; - } - if ( da[i].num[0] > da[i].num[1] ) { - da[i].num[0] = da[i].num[1]; - da[i].ord[0] = da[i].ord[1]; - da[i].typ[0] = da[i].typ[1]; - da[i].typ[1] = 0; - num_cuts += 1; - num_cut_pieces += 1; - } else - if ( da[i].num[0] < da[i].num[1] ) { - da[i].typ[1] = 0; - num_cuts += 1; - num_cut_pieces += 1; - } else { - /* attachments have same size: ignore both */ - /* ??? check for standard derivatizations ??? */ - da[i].typ[0] = 0; - da[i].typ[1] = 0; - } - continue; - } - ret = -88; - goto exit_function; /* unexpected */ - case 3: - if ( da[i].typ[0] == da[i].typ[1] && - da[i].typ[0] == da[i].typ[2] && - da[i].typ[0] == DERIV_AMINE_tN ) { - int x, y, z; - if ( 0 == is_deriv_chain( at, i, num_atoms, da+i, 0 ) ) { - da[i].num[0] = NOT_AT_DERIV; - } - if ( 0 == is_deriv_chain( at, i, num_atoms, da+i, 1 ) ) { - da[i].num[1] = NOT_AT_DERIV; - } - if ( 0 == is_deriv_chain( at, i, num_atoms, da+i, 2 ) ) { - da[i].num[2] = NOT_AT_DERIV; - } - - x = (da[i].num[0] < da[i].num[1])? 0 : 1; - x = (da[i].num[x] < da[i].num[2])? x : 2; /* min */ - z = (da[i].num[0] < da[i].num[1])? 1 : 0; - z = (da[i].num[x] < da[i].num[2])? 2 : z; /* max */ - y = ((x+1)^(z+1))-1; /* median */ - - - if (da[i].num[x] == da[i].num[z] ) { - /* no cuts */ - da[i].typ[0] = 0; - da[i].typ[1] = 0; - da[i].typ[2] = 0; - continue; /* all deriv. agents have same size, ignore */ - /* ??? check for standard derivatizations ??? */ - } else - if ( da[i].num[x] == da[i].num[y] ) { - /* two smallest */ - switch( z ) { - case 0: - da[i].num[0] = da[i].num[1]; - da[i].ord[0] = da[i].ord[1]; - da[i].typ[0] = da[i].typ[1]; - - da[i].num[1] = da[i].num[2]; - da[i].ord[1] = da[i].ord[2]; - da[i].typ[1] = da[i].typ[2]; - break; - case 1: - da[i].num[1] = da[i].num[2]; - da[i].ord[1] = da[i].ord[2]; - da[i].typ[1] = da[i].typ[2]; - break; - case 2: - break; - } - da[i].typ[2] = 0; - num_cuts += 2; - num_cut_pieces += 2; - } else { - /* one smallest */ - if ( x ) { - da[i].num[0] = da[i].num[x]; - da[i].ord[0] = da[i].ord[x]; - da[i].typ[0] = da[i].typ[x]; - } - da[i].typ[1] = 0; - da[i].typ[2] = 0; - num_cuts += 1; - num_cut_pieces += 1; - } - continue; - } - ret = -88; - goto exit_function; /* unexpected */ - case 4: - if ( (da[i].typ[0] & DERIV_RING) && (da[i].typ[1] & DERIV_RING) && - (da[i].typ[2] & DERIV_RING) && (da[i].typ[3] & DERIV_RING) ) { - int n01 = inchi_max( da[i].num[0], da[i].num[1] ); - int n23 = inchi_max( da[i].num[2], da[i].num[3] ); - if ( n01 < n23 && 0 < is_deriv_ring( at, i, num_atoms, da+i, 0) ) { - da[i].typ[2] = 0; - da[i].typ[3] = 0; - num_cuts += 2; - num_ring_cuts += 2; - num_cut_pieces += 1; - } else - if ( n01 > n23 && 0 < is_deriv_ring( at, i, num_atoms, da+i, 2) ) { - da[i].num[0] = da[i].num[2]; - da[i].ord[0] = da[i].ord[2]; - da[i].typ[0] = da[i].typ[2]; - - da[i].num[1] = da[i].num[3]; - da[i].ord[1] = da[i].ord[3]; - da[i].typ[1] = da[i].typ[3]; - - da[i].typ[2] = 0; - da[i].typ[3] = 0; - num_cuts += 2; - num_ring_cuts += 2; - num_cut_pieces += 1; - } else { - da[i].typ[0] = 0; - da[i].typ[1] = 0; - da[i].typ[2] = 0; - da[i].typ[3] = 0; - } - continue; - } - ret = -88; - goto exit_function; /* unexpected */ - } - } - - /* eliminate cases when - da[i1].typ[j1] && da[i2].typ[j2] && - at[i1].neighbor[da[i1].ord[j1]] == i2 && at[i2].neighbor[da[i2].ord[j2]] == i1 - that is, same bond is in the da[] elements of the adjacent atoms */ - nFound = 0; /* number of cuts to remove */ - for ( i = 0; i < num_atoms; i ++ ) { - for ( j = 0; j < MAX_AT_DERIV && da[i].typ[j]; j ++ ) { - if ( da[i].typ[j] & DERIV_DUPLIC ) - continue; - n = at[i].neighbor[(int)da[i].ord[j]]; - if ( n < i ) - continue; - for ( k = 0; k < MAX_AT_DERIV && da[n].typ[k]; k ++ ) { - if ( da[n].typ[k] & DERIV_DUPLIC ) - continue; - m = at[n].neighbor[(int)da[n].ord[k]]; - if ( m == i ) { - /* same bond in da[i].typ[j] and da[n].typ[k] */ - /* check whether both derivatives are acceptable */ - int k1=k, j1=j; - int ret_i = is_deriv_chain_or_ring( at, i, num_atoms, da+i, &j1 ); - int ret_n = is_deriv_chain_or_ring( at, n, num_atoms, da+n, &k1 ); - if ( ret_i < 0 ) { - ret = ret_i; - goto exit_function; - } - if ( ret_n < 0 ) { - ret = ret_n; - goto exit_function; - } - if ( !ret_i || ret_i && ret_n ) { - if ( da[i].typ[j1] & DERIV_RING ) { - num_cuts -= 2; - num_ring_cuts -= 2; - } else { - num_cuts -= 1; - } - num_cut_pieces -= 1; - if (ret = remove_deriv_mark( da+i, j1 )) { - goto exit_function; - } - nFound ++; - } - if ( !ret_n || ret_i && ret_n ) { - if ( da[n].typ[k1] & DERIV_RING ) { - num_cuts -= 2; - num_ring_cuts -= 2; - } else { - num_cuts -= 1; - } - num_cut_pieces -= 1; - if ( ret = remove_deriv_mark( da+n, k1 ) ) { - goto exit_function; - } - nFound ++; - } - } - } - } - } - if ( nFound ) { - for ( i = 0; i < num_atoms; i ++ ) { - for ( j = 0; j < MAX_AT_DERIV && da[i].typ[j]; j ++ ) { - /* attn: j is changed inside the cycle body */ - if ( da[i].typ[j] & DERIV_DUPLIC ) { - if (ret = remove_deriv( da+i, j )) { - goto exit_function; - } - j --; - } - } - } - } - - /* make sure DERIV_RING type disconnections increase */ - /* number of components by the number of disconnected derivateves */ - /* Avoid cases like these: - - O--R--O DO--R--OD - / \ - R--X Y--R => R--XT2 T2Y--R - \ / - O--R--O DO--R--OD - - - - O--O DO--OD - / \ - R--X--O---Y--R => R--X OD2 Y--R - T2 T2 - - */ - /* count DERIV_RING-type attachments */ -#if ( COUNT_ALL_NOT_DERIV == 1 ) - num_cuts_to_check = num_cuts; -#else - num_cuts_to_check = num_ring_cuts; -#endif - if ( num_cuts_to_check >= 2 ) - { - /* check */ - R2C_ATPAIR *ap = (R2C_ATPAIR *) inchi_malloc( num_cuts_to_check * sizeof(ap[0]) ); - AT_NUMB comp_num; - int /*n,*/ m_at, m_ord; - AT_NUMB at1, at2; - if ( !ap ) { - ret = -1; - goto exit_function; /* malloc failure */ - } -repeat_without_deriv_ring: - comp_num = 0; - /* fill out the array of bonds to be cut */ - for ( i = j = 0; i < num_atoms; i ++ ) { - if ( (da[i].typ[0] & DERIV_RING) && (da[i].typ[1] & DERIV_RING) && - da[i].num[0] <= MAX_AT_DERIV && da[i].num[1] <= MAX_AT_DERIV ) { - if ( j+1 >= num_cuts_to_check ) { - ret = -2; - goto exit_r2c_num; /* wrong number of cuts = num */ - } - for ( k = 0; k < 2; k ++ ) { - at1 = i; - at2 = at[i].neighbor[(int)da[i].ord[k]]; - n = ( at1 > at2 ); - ap[j].at[n] = at1; - ap[j].at[1-n] = at2; /* ap[j].at[0] < ap[j].at[1] */ - j ++; - } - if ( 0 < cmp_r2c_atpair( ap+j-2, ap+j-1 ) ) { - R2C_ATPAIR ap1 = ap[j-2]; - ap[j-2] = ap[j-1]; - ap[j-1] = ap1; /* sort each pair */ - } - } -#if ( COUNT_ALL_NOT_DERIV == 1 ) - else { - for ( k = 0; k < DERIV_AT_LEN && da[i].typ[k]; k ++ ) { - if ( j >= num_cuts_to_check || (da[i].typ[k] & DERIV_RING) ) { - ret = -2; - goto exit_r2c_num; /* wrong number of cuts = num or wrong type */ - } - at1 = i; - at2 = at[i].neighbor[(int)da[i].ord[k]]; - n = ( at1 > at2 ); - ap[j].at[n] = at1; - ap[j].at[1-n] = at2; /* ap[j].at[0] < ap[j].at[1] */ - j ++; - } - } -#endif - - } - if ( j != num_cuts_to_check ) { - ret = -3; - goto exit_r2c_num; /* wrong number of cuts = num */ - } - /* !!!!!!!! check that there are no derivatives inside a derivative */ - comp_num = 0; /* here it is the number of removed cuts */ - for ( i = 0; i < num_cuts_to_check; i += j ) { - for ( j = n = 0; j < 2; j ++ ) { - int atj = (int)ap[i].at[j]; - if ( da[atj].typ[0] && at[atj].neighbor[(int)da[atj].ord[0]] == ap[i].at[1-j] ) { - k = j; - n ++; - m_at = atj; - m_ord = 0; - } else - if ( da[atj].typ[1] && at[atj].neighbor[(int)da[atj].ord[1]] == ap[i].at[1-j] ) { - k = j; - n ++; - m_at = atj; - m_ord = 1; - } - } - if ( n != 1 ) { - ret = -3; - goto exit_r2c_num; /* wrong atom pair */ - } - if ( (da[m_at].typ[m_ord] & DERIV_RING) ) { - n = (int)ap[i].at[k]; /* atom inside the derivation attachment */ - j = 2; /* number of bonds to cut */ - if ( i+j > num_cuts_to_check || (int)ap[i+1].at[0] != n && (int)ap[i+1].at[1] != n ) { - ret = -3; - goto exit_r2c_num; /* wrong atom pair */ - } - } else { - n = ap[i].at[1-k]; /* atom inside the derivation attachment */ - j = 1; /* number of bonds to cut */ - } - - /* at[n] belongs to the derivation agent */ - cur_num_at = mark_atoms_ap( at, n, ap+i, j, 0, 1 ); - for ( k = 0; k < num_cuts_to_check; k ++ ) { - if ( k == i || k==i+j-1 ) - continue; - if ( at[(int)ap[k].at[0]].at_type || at[(int)ap[k].at[1]].at_type ) { - /* unmark the cut: found a cut inside the derivatizing agent */ - da[m_at].typ[m_ord] |= DERIV_UNMARK; - num_cuts -= 1; - num_cut_pieces -= 1; - if ( j == 2 ) { - da[m_at].typ[1-m_ord] |= DERIV_UNMARK; - num_cuts -= 1; - num_ring_cuts -= 2; - } - comp_num ++; - break; - } - } - UnMarkOtherIndicators( at, num_atoms ); - } - if ( comp_num ) { - for ( i = 0; i < num_atoms; i ++ ) { - if ( da[i].typ[0] & DERIV_UNMARK ) { - da[i].num[0] = da[i].num[1]; - da[i].ord[0] = da[i].ord[1]; - da[i].typ[0] = da[i].typ[1]; - da[i].typ[1] = 0; - j = 0; - } else { - j = 1; - } - if ( da[i].typ[j] & DERIV_UNMARK ) { - da[i].typ[j] = 0; - } - } -#if ( COUNT_ALL_NOT_DERIV == 1 ) - num_cuts_to_check = num_cuts; -#else - num_cuts_to_check = num_ring_cuts; -#endif - if ( num_cuts < 0 || num_ring_cuts < 0 || num_cut_pieces < 0 ) { - ret = -3; - goto exit_r2c_num; /* wrong number of cuts = num */ - } - goto repeat_without_deriv_ring; - } - - /* sort the bonds for subsequent searching by bisections */ - if ( num_cuts_to_check > 1 ) { - qsort( ap, num_cuts_to_check, sizeof(ap[0]), cmp_r2c_atpair); - } - /* mark components to be disconnected */ - comp_num = 0; /* number of components */ - cur_num_at = 0; /* number of atoms left after disconnecting the derivatizing agent */ - UnMarkOtherIndicators( at, num_atoms ); - for ( i = 0; i < num_cuts_to_check; i ++ ) { - n = 0; - for ( j = 0; j < 2; j ++ ) { - if ( da[(int)ap[i].at[j]].typ[0] ) { - k = j; - n ++; - } - } - if ( n != 1 ) { - ret = -3; - goto exit_r2c_num; /* wrong atom pair */ - } - n = ap[i].at[k]; /* marked atom */ - if ( (da[n].typ[0] & DERIV_RING) ) { - n = ap[i].at[1-k]; - } - /* at[n] belongs to the derivation agent */ - if ( !at[n].at_type ) { - comp_num ++; - cur_num_at = mark_atoms_ap( at, n, ap, num_cuts_to_check, cur_num_at, comp_num ); - } - } - if ( comp_num > 1 ) { - /* eliminate offending DERIV_RING type derivatives */ - if ( num_ring_cuts <= 2 ) { - ret = -99; - goto exit_r2c_num; - } - n = 0; - for ( i = j = 0; i < num_atoms; i ++ ) { - if ( (da[i].typ[0] & DERIV_RING) && (da[i].typ[1] & DERIV_RING) ) { - int at1a = at[i].neighbor[(int)da[i].ord[0]]; - int at2a = at[i].neighbor[(int)da[i].ord[1]]; - if ( at[at1a].at_type != at[at2a].at_type ) { - da[i].typ[0] = 0; /* eliminate this cut */ - da[i].typ[1] = 0; - n ++; - num_cuts_to_check -= 2; - num_cuts -= 2; - num_ring_cuts -= 2; - num_cut_pieces -= 1; - } - } - } - if ( n > 0 && num_cuts_to_check > 2 ) { - goto repeat_without_deriv_ring; - } - } - ret = 0; -exit_r2c_num: - inchi_free( ap ); - UnMarkOtherIndicators( at, num_atoms ); - if ( ret < 0 || num_cuts_to_check >= 2 && cur_num_at < MIN_AT_LEFT_DERIV ) { - goto exit_function; /* unexpected error or nothing left */ - } - } - - if ( !num_cuts ) { - continue; /*goto exit_function;*/ - } - /* eliminate derivatives that are not in the list */ - num_cuts = EliminateDerivNotInList( at, da, num_atoms ); - if ( num_cuts < 0 ) { - ret = num_cuts; - goto exit_function; - } - - - /* make cuts */ - num_cuts = 0; - for ( i = num = 0; i < num_atoms; i ++ ) { - for ( len = 0; len < MAX_AT_DERIV && da[i].typ[len]; len ++ ) - ; - switch( len ) { - case 0: - continue; - case 1: - /* single cut: unconditional */ - make_single_cut( at, da, i, 0 ); - num_cuts += 1; - continue; - case 2: - if ( (da[i].typ[0] & DERIV_RING) && (da[i].typ[1] & DERIV_RING) || - da[i].typ[0] == DERIV_AMINE_tN && da[i].typ[1] == DERIV_AMINE_tN ) { - /* double cut, unconditional */ - make_single_cut( at, da, i, 1 ); - make_single_cut( at, da, i, 0 ); - num_cuts += 1; - continue; - } - if ( da[i].typ[0] == da[i].typ[1] ) { - /* DERIV_BRIDGE_O or DERIV_BRIDGE_NH; cut off the smallest */ - if ( da[i].num[0] > da[i].num[1] ) { - make_single_cut( at, da, i, 1 ); - num_cuts += 1; - } else - if ( da[i].num[0] < da[i].num[1] ) { - make_single_cut( at, da, i, 0 ); - num_cuts += 1; - } - continue; - } - ret = -88; - goto exit_function; /* unexpected */ - case 3: - if ( da[i].typ[0] == da[i].typ[1] && - da[i].typ[0] == da[i].typ[2] && - da[i].typ[0] == DERIV_AMINE_tN ) { - int x, y, z; - x = (da[i].num[0] < da[i].num[1])? 0 : 1; - x = (da[i].num[x] < da[i].num[2])? x : 2; /* min */ - z = (da[i].num[0] < da[i].num[1])? 1 : 0; - z = (da[i].num[x] < da[i].num[2])? 2 : z; /* max */ - y = ((x+1)^(z+1))-1; /* median */ - if (da[i].num[x] == da[i].num[z] ) - continue; /* all deriv. agents have same size */ - /* two smallest */ - if ( da[i].num[x] == da[i].num[y] && x < y ) { - int t = x; /* first cut x > y */ - x = y; - y = t; - } - make_single_cut( at, da, i, x ); - num_cuts += 1; - if ( da[i].num[x] == da[i].num[y] ) { - /* equally small */ - make_single_cut( at, da, i, y ); - num_cuts += 1; - } - continue; - } - ret = -88; - goto exit_function; /* unexpected */ - case 4: - if ( (da[i].typ[0] & DERIV_RING) && (da[i].typ[1] & DERIV_RING) && - (da[i].typ[2] & DERIV_RING) && (da[i].typ[3] & DERIV_RING) ) { - int n01 = inchi_max( da[i].num[0], da[i].num[1] ); - int n23 = inchi_max( da[i].num[2], da[i].num[3] ); - if ( n01 < n23 ) { - make_single_cut( at, da, i, 1 ); - make_single_cut( at, da, i, 0 ); - num_cuts += 1; - } else - if ( n01 > n23 ) { - make_single_cut( at, da, i, 3 ); - make_single_cut( at, da, i, 2 ); - num_cuts += 1; - } - continue; - } - } - } - nTotNumCuts += num_cuts; -#if ( REMOVE_CUT_DERIV == 1 ) /* normally YES */ - if ( num_cuts ) { - /* remove marked with Tritium disconnected derivative attachments */ - ORIG_ATOM_DATA Orig_inp_data1, *orig_inp_data1; - INP_ATOM_DATA *inp_cur_data1 = NULL; - int num_components1, i_component1, num_component_left=0; - orig_inp_data1 = &Orig_inp_data1; - memset( orig_inp_data1, 0, sizeof(orig_inp_data1[0]) ); - UnMarkRingSystemsInp( at, num_atoms ); - UnMarkOtherIndicators( at, num_atoms ); - UnMarkOneComponent( at, num_atoms ); - for (i = 0; i < num_atoms; i ++ ) { - orig_inp_data1->num_inp_bonds += at[i].valence; - } - orig_inp_data1->num_inp_bonds /= 2; - orig_inp_data1->num_inp_atoms = num_atoms; - orig_inp_data1->at = at; /* = from inp_cur_data[i_component].at */ - num_components1 = MarkDisconnectedComponents( orig_inp_data1, 0 ); - inp_cur_data1 = (INP_ATOM_DATA *)inchi_calloc( num_components1, sizeof(inp_cur_data1[0]) ); - /* extract components and discard disconnected derivatizing agents */ - for ( i_component1 = 0; i_component1 < num_components1; i_component1 ++ ) { - CreateInpAtomData( inp_cur_data1+i_component1, orig_inp_data1->nCurAtLen[i_component1], 0 ); - inp_cur_data1[i_component1].num_at = ExtractConnectedComponent( orig_inp_data1->at, orig_inp_data1->num_inp_atoms, - i_component1+1, inp_cur_data1[i_component1].at ); - /* error processing */ - if ( inp_cur_data1[i_component1].num_at <= 0 || orig_inp_data1->nCurAtLen[i_component1] != inp_cur_data1[i_component1].num_at ) { - ret = -(i_component1+1); /* severe error */ - break; - } - /* if the component has tritium then discard: it is a derivatizing agent */ - for (i = 0; i < inp_cur_data1[i_component1].num_at; i ++ ) { - if ( inp_cur_data1[i_component1].at[i].num_iso_H[1] ) { - inp_cur_data1[i_component1].at[i].num_iso_H[1] = 0; /* remove deuterium */ - } - if ( inp_cur_data1[i_component1].at[i].num_iso_H[2] ) { - FreeInpAtomData( inp_cur_data1+i_component1 ); - break; - } - } - } - /* merge components into one -- must be only one */ - for ( i_component1 = 0, num_atoms = 0; i_component1 < num_components1; i_component1 ++ ) { - num_atoms += inp_cur_data1[i_component1].num_at; - } - at = (inp_ATOM *) inchi_calloc( num_atoms, sizeof(at[0]) ); - cur_num_at = 0; - for ( i_component1 = 0; i_component1 < num_components1; i_component1 ++ ) { - /* clean and prepare */ - if ( !inp_cur_data1[i_component1].num_at ) - continue; /* removed derivatizing object */ - /*UnMarkOneComponent( inp_cur_data1[i_component1].at, inp_cur_data1[i_component1].num_at );*/ - /* merge one by one */ - cur_num_at = add_inp_ATOM( at, num_atoms, cur_num_at, inp_cur_data1[i_component1].at, inp_cur_data1[i_component1].num_at ); - FreeInpAtomData( inp_cur_data1+i_component1 ); /* cleanup */ - num_component_left ++; - } - /* replace the component */ - /* order of the following two statements is critically important */ - UnMarkDisconnectedComponents( orig_inp_data1 ); /* orig_inp_data1->at is same as inp_cur_data[i_component].at */ - FreeInpAtomData( inp_cur_data+i_component ); /* cleanup the original component */ - inp_cur_data[i_component].at = at; - inp_cur_data[i_component].num_at = cur_num_at; - inchi_free( inp_cur_data1 ); - } -#endif - } - if ( nTotNumCuts ) { - /* merge components into one */ - for ( i = 0, num_atoms = 0; i < num_components; i ++ ) { - num_atoms += inp_cur_data[i].num_at; - } - at = (inp_ATOM *) inchi_calloc( num_atoms, sizeof(at[0]) ); - cur_num_at = 0; - for ( i = 0; i < num_components; i ++ ) { - /* clean and prepare */ - UnMarkRingSystemsInp( inp_cur_data[i].at, inp_cur_data[i].num_at ); - UnMarkOtherIndicators( inp_cur_data[i].at, inp_cur_data[i].num_at ); - UnMarkOneComponent( inp_cur_data[i].at, inp_cur_data[i].num_at ); - subtract_DT_from_num_H( inp_cur_data[i].num_at, inp_cur_data[i].at ); - /* merge one by one */ - cur_num_at = add_inp_ATOM( at, num_atoms, cur_num_at, inp_cur_data[i].at, inp_cur_data[i].num_at ); - } - /* replace orig_inp_data */ - if ( cur_num_at == num_atoms ) { - inchi_free( orig_inp_data->at ); - orig_inp_data->at = at; - orig_inp_data->num_inp_atoms = cur_num_at; - if ( orig_inp_data->szCoord ) { - inchi_free( orig_inp_data->szCoord ); - orig_inp_data->szCoord = NULL; - } - UnMarkDisconnectedComponents( orig_inp_data ); - } else { - if ( at ) { - inchi_free( at ); - at = NULL; - } - ret = -999; /* num atoms mismatch */ - } - } -exit_function: - if ( da ) { - inchi_free( da ); - da = NULL; - } - for ( i_component = 0; i_component < num_components; i_component ++ ) { - FreeInpAtomData( inp_cur_data+i_component ); - } - inchi_free( inp_cur_data ); - inp_cur_data = NULL; - - return ret? ret : nTotNumCuts; -} - -#endif /* UNDERIVATIZE */ -/********************************************************************/ -#if ( RING2CHAIN == 1 ) -/* - type=1 (incl sugars: W=O, A=C(sat), Z=C(sat), Y=O, B=C(sat)-OH - - A---W A---WH - / | / - | | ---> | - \ | \ - B---Z---YH B---Z===Y - | | - | | - C(opt) C(opt) - - type=2 [not implemented] - - R---W R---WH - / \ / - | Z ---> | Z - \ / \ // - R---YH R---Y - -*/ -#define R2C_EMPTY 127 -typedef struct tagRing2Chain { /* atom Z */ - char type; /* 1 => sugar-like */ - char ordW; /* ordering number of W-neighbor; bond to break; H to add */ - char ordY; /* ordering number of YH-neighbor; bond to increment; H to remove */ - char ordC; /* atom B = C(sat) */ - char ordCopt; /* if exists, saturated C connected by a chain-bond to Z */ -} R2C_AT; - -int detect_r2c_Zatom( inp_ATOM *at, R2C_AT *da, int iZ ); -int cut_ring_to_chain( inp_ATOM *at, R2C_AT *da, int iZ ); - -/********************************************************************/ -int detect_r2c_Zatom( inp_ATOM *at, R2C_AT *da, int iZ ) -{ - int i, j, neigh, neighneigh, nRingSystem, num_found; - R2C_AT da1; - if ( at[iZ].valence > 4 ) - return 0; - if ( at[iZ].valence != at[iZ].chem_bonds_valence ) - return 0; /* approach limitation: no double bonds */ - - if ( at[iZ].el_number != el_number_C ) - return 0; /* sugar-specific */ - - if ( at[iZ].nNumAtInRingSystem < 5 ) - return 0; /* not in a suitable ring */ - - if ( !at[iZ].bCutVertex ) - return 0; /* recognize only type 1 for now */ - - nRingSystem = at[iZ].nRingSystem; - memset ( &da1, R2C_EMPTY, sizeof(da1) ); - - for ( i = 0, num_found = 0; i < at[iZ].valence; i ++ ) { - neigh = at[iZ].neighbor[i]; - if ( at[neigh].charge || at[neigh].radical ) - return 0; - if ( at[neigh].el_number == el_number_O && - at[neigh].valence == 1 && - at[neigh].chem_bonds_valence == 1 && - at[neigh].num_H == 1 ) { - /* found Z-OH, i.e. Z-YH */ - if ( da1.ordY == R2C_EMPTY ) { - da1.ordY = i; - num_found ++; - continue; - } else { - return 0; - } - } - if ( at[neigh].el_number == el_number_O && - at[neigh].valence == 2 && - at[neigh].chem_bonds_valence == 2 && - at[neigh].num_H == 0 && - at[neigh].nRingSystem == nRingSystem ) { - /* found Z-O-, i.e. Z-W- */ - if ( da1.ordW == R2C_EMPTY ) { - /* j = index of the oppozite to at[iZ] neighbor of at[neigh] */ - j = (at[neigh].neighbor[0] == iZ); - neighneigh = at[neigh].neighbor[j]; - if ( at[neighneigh].valence != at[neighneigh].chem_bonds_valence || - at[neighneigh].el_number != el_number_C ) - return 0; /* sugar-specific */ - da1.ordW = i; - num_found ++; - continue; - } else { - return 0; - } - } - if ( at[neigh].el_number == el_number_C && - at[neigh].valence > 2 && - at[neigh].chem_bonds_valence == at[neigh].valence && - at[neigh].num_H <= 1 && - at[neigh].nRingSystem == nRingSystem ) { - /* sugar-specfic: carbon in the ring should have -OH neighbor */ - int iOH; - for ( j = 0; j < at[neigh].valence; j ++ ) { - iOH = at[neigh].neighbor[j]; - if ( at[iOH].el_number == el_number_O && - at[iOH].valence == 1 && - at[iOH].chem_bonds_valence == 1 && - at[iOH].num_H == 1 && - !at[iOH].charge && !at[iOH].radical ) { - if ( da1.ordC == R2C_EMPTY ) { - da1.ordC = i; - num_found ++; - break; - } else { - return 0; - } - } - } - if ( j < at[neigh].valence ) - continue; - } - if ( at[neigh].el_number == el_number_C && - at[neigh].chem_bonds_valence == at[neigh].valence && - at[neigh].nRingSystem != nRingSystem ) { - /* extra carbon neighbor of Z */ - if ( da1.ordCopt == R2C_EMPTY ) { - da1.ordCopt = i; - continue; - } - } - return 0; /* unexpectd neighbor */ - } - if (num_found == 3) { - da1.type = 1; - da[iZ] = da1; - return 1; /* disconnection found */ - } - return 0; -} -/********************************************************************/ -int cut_ring_to_chain( inp_ATOM *at, R2C_AT *da, int iZ ) -{ - int ret = -1; /* error flag */ - int iordW = (int)da[iZ].ordW; /* ord of the bond in iZ */ - int iordY = (int)da[iZ].ordY; /* ord of the bond in iZ */ - int iordC = (int)da[iZ].ordC; - int iW, iY, num_iso_H, i, jordZ; - AT_NUMB *p; - - if ( da[iZ].type != 1 ) { - return 0; - } - if ( 0 > iordW || iordW >= at[iZ].valence || - 0 > iordY || iordY >= at[iZ].valence || - 0 > iordC || iordC >= at[iZ].valence /* suger-specific*/) { - return -1; /* program error */ - } - /* find other da[] that affect at[iZ] */ - iW = at[iZ].neighbor[iordW]; /* opposite atom to disconnect and add H */ - iY = at[iZ].neighbor[iordY]; /* opposite atom to increment the bond and remove H*/ - if ( !at[iY].num_H || at[iZ].bond_type[iordY] != BOND_TYPE_SINGLE ) { - return -2; /* program error */ - } - /* increment at[iZ]--at[iY] bond */ - p = is_in_the_list( at[iY].neighbor, (AT_NUMB) iZ, at[iY].valence ); - if ( !p ) { - return -3; /* program error */ - } - jordZ = p - at[iY].neighbor; - at[iZ].bond_type[iordY] ++; - at[iZ].chem_bonds_valence ++; - at[iY].bond_type[jordZ] ++; - at[iY].chem_bonds_valence ++; - - /* disconnect at[iZ]--at[iW] bond */ - ret = DisconnectInpAtBond( at, NULL, iZ, iordW ); - if ( ret != 1 ) { - return -4; /* program error */ - } - /* disconnection succeeded */ - /* transfer H from at[iY] to at[iW] */ - num_iso_H = NUM_ISO_H(at, iY); - if ( at[iY].num_H == num_iso_H ) { - for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { - if ( at[iY].num_iso_H[i] ) { - at[iY].num_iso_H[i] --; - at[iW].num_iso_H[i] ++; - } - } - } - at[iY].num_H --; - at[iW].num_H ++; - return 1; -} -/********************************************************************/ -int Ring2Chain( ORIG_ATOM_DATA *orig_inp_data ) -{ - int ret = 0, i, j, n, num_atoms, num_components, nFound, num, num_cuts, iZ, cur_num_at; - inp_ATOM *at = orig_inp_data->at; - INP_ATOM_DATA *inp_cur_data = NULL; - R2C_AT *da = NULL; - - set_R2C_el_numbers( ); - /* prepare */ - num_atoms = remove_terminal_HDT( orig_inp_data->num_inp_atoms, at, 1 ); - /*^^^^^ always accomodate accomodate FIX_TERM_H_CHRG_BUG - IPl, July 2008*/ - orig_inp_data->num_inp_atoms = num_atoms; - - /* initialize */ - UnMarkDisconnectedComponents( orig_inp_data ); - num_cuts = 0; - /* mark */ - num_components = MarkDisconnectedComponents( orig_inp_data, 0 ); - inp_cur_data = (INP_ATOM_DATA *)inchi_calloc( num_components, sizeof(inp_cur_data[0]) ); - iZ = -1; - for ( j = 0; j < num_components; j ++ ) { - CreateInpAtomData( inp_cur_data+j, orig_inp_data->nCurAtLen[j], 0 ); - inp_cur_data[j].num_at = ExtractConnectedComponent( orig_inp_data->at, orig_inp_data->num_inp_atoms, j+1, inp_cur_data[j].at ); - /* error processing */ - if ( inp_cur_data[j].num_at <= 0 || orig_inp_data->nCurAtLen[j] != inp_cur_data[j].num_at ) { - ret = -(j+1); /* severe error */ - goto exit_function; - } - /* initialize */ - num_atoms = inp_cur_data[j].num_at; - at = inp_cur_data[j].at; - add_DT_to_num_H( num_atoms, at ); - - UnMarkRingSystemsInp( at, num_atoms ); - UnMarkOtherIndicators( at, num_atoms ); - UnMarkOneComponent( at, num_atoms ); - MarkRingSystemsInp( at, num_atoms, 0 ); - ret = mark_arom_bonds( at, num_atoms ); - if ( ret < 0 ) { - goto exit_function; - } - ret = 0; - if ( da ) inchi_free( da ); - da = (R2C_AT *)inchi_calloc( num_atoms, sizeof(da[0]) ); - - /* detect ring-to-chain possibilities */ - nFound = 0; - for ( i = 0, num=0; i < num_atoms; i ++ ) { - if ( at[i].bCutVertex /* type 1 specific*/ && !da[i].type ) { - num += (n=detect_r2c_Zatom( at, da, i )); - if ( n == 1 ) - iZ = i; - UnMarkOtherIndicators( at, num_atoms ); - } - } - - if ( num == 1 ) { - /* convert ring to chain: make single cut */ - ret = cut_ring_to_chain( at, da, iZ ); - if ( ret < 0 ) { - goto exit_function; - } - num_cuts += (ret == 1); - } else - if ( num ) { - /* allocate an array of bonds to be cut */ - R2C_ATPAIR *ap = (R2C_ATPAIR *) inchi_malloc( sizeof(ap[0]) * num ); - AT_NUMB comp_num = 0; - if ( !ap ) { - ret = -1; /* malloc failure */ - goto exit_function; - } - /* fill out the array of bonds to be cut */ - for ( i = j = 0; i < num_atoms; i ++ ) { - if ( da[i].type == 1 ) { - AT_NUMB at1 = i; - AT_NUMB at2 = at[i].neighbor[(int)da[i].ordW]; - if ( j >= num ) { - ret = -2; - goto exit_r2c_num; /* wrong number of cuts = num */ - } - n = ( at1 > at2 ); - ap[j].at[n] = at1; - ap[j].at[1-n] = at2; /* ap[j].at[0] < ap[j].at[1] */ - j ++; - } - } - if ( j != num ) { - ret = -3; - goto exit_r2c_num; /* wrong number of cuts = num */ - } - /* sort the bonds for subsequent searching by bisections */ - qsort( ap, num, sizeof(ap[0]), cmp_r2c_atpair); - /* mark components to be disconnected */ - for ( i = 0; i < num; i ++ ) { - for ( j = 0; j < 2; j ++ ) { - if ( !at[ap[i].at[j]].at_type ) { - comp_num ++; - mark_atoms_ap( at, (int)ap[i].at[j], ap, num, 0, comp_num ); - } - } - } - /* convert ring to chain */ - for ( i = 0; i < num; i ++ ) { - int i1 = ap[i].at[0]; - int i2 = ap[i].at[1]; - iZ = -1; - if ( at[i1].at_type == at[i2].at_type ) { - /* by definition, there are no adjacent iZ atoms; one iZ atom per bond */ - if ( da[i1].type == 1 && at[i1].neighbor[(int)da[i1].ordW] == i2 ) { - iZ = i1; - } else - if ( da[i2].type == 1 && at[i2].neighbor[(int)da[i2].ordW] == i1 ) { - iZ = i2; - } else { - ret = -3; - goto exit_r2c_num; - } - ret = cut_ring_to_chain( at, da, iZ ); - if ( ret < 0 ) { - goto exit_r2c_num; - } - num_cuts += (ret == 1); - } - } - ret = 0; -exit_r2c_num: - inchi_free( ap ); - UnMarkOtherIndicators( at, num_atoms ); - if ( ret < 0 ) { - goto exit_function; - } - } - } - if ( num_cuts ) { - /* merge components into one */ - for ( i = 0, num_atoms = 0; i < num_components; i ++ ) { - num_atoms += inp_cur_data[i].num_at; - } - at = (inp_ATOM *) inchi_calloc( num_atoms, sizeof(at[0]) ); - cur_num_at = 0; - for ( i = 0; i < num_components; i ++ ) { - /* clean and prepare */ - UnMarkRingSystemsInp( inp_cur_data[i].at, inp_cur_data[i].num_at ); - UnMarkOtherIndicators( inp_cur_data[i].at, inp_cur_data[i].num_at ); - UnMarkOneComponent( inp_cur_data[i].at, inp_cur_data[i].num_at ); - subtract_DT_from_num_H( inp_cur_data[i].num_at, inp_cur_data[i].at ); - /* merge one by one */ - cur_num_at = add_inp_ATOM( at, num_atoms, cur_num_at, inp_cur_data[i].at, inp_cur_data[i].num_at ); - } - /* replace orig_inp_data */ - if ( cur_num_at == num_atoms ) { - inchi_free( orig_inp_data->at ); - orig_inp_data->at = at; - orig_inp_data->num_inp_atoms = cur_num_at; - if ( orig_inp_data->szCoord ) { - inchi_free( orig_inp_data->szCoord ); - orig_inp_data->szCoord = NULL; - } - UnMarkDisconnectedComponents( orig_inp_data ); - } else { - if ( at ) { - inchi_free( at ); - at = NULL; - } - ret = -999; /* num atoms mismatch */ - } - } -exit_function: - if ( da ) { - inchi_free( da ); - da = NULL; - } - for ( j = 0; j < num_components; j ++ ) { - FreeInpAtomData( inp_cur_data+j ); - } - inchi_free( inp_cur_data ); - inp_cur_data = NULL; - - return ret? ret : num_cuts; -} -#endif /* RING2CHAIN */ - diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichinorm.h b/INCHI-1-SRC/INCHI_API/inchi_dll/ichinorm.h deleted file mode 100644 index ca19873..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichinorm.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHINORM_H__ -#define __INCHINORM_H__ - - -#include "mode.h" -#include "ichi_bns.h" - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -/* main normalization procedure */ -int mark_alt_bonds_and_taut_groups (inp_ATOM *at, inp_ATOM *at_fixed_bonds_out, int num_atoms, - T_GROUP_INFO *t_group_info, INCHI_MODE *inpbTautFlags, INCHI_MODE *inpbTautFlagsDone); - -int MarkTautomerGroups(inp_ATOM *at, int num_atoms, - T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD); -int MarkChargeGroups(inp_ATOM *at, int num_atoms, - C_GROUP_INFO *c_group_info, T_GROUP_INFO *t_group_info, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD); -int MarkSaltChargeGroups(inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, - T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD); -int MarkSaltChargeGroups2(inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, - T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD); -int MergeSaltTautGroups(inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, - T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, - struct BalancedNetworkStructure *pBNS); -int MakeIsotopicHGroup(inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, - T_GROUP_INFO *t_group_info); - -int remove_terminal_HDT(int num_atoms, inp_ATOM *at, int bFixTermHChrg); -int RemoveExcessiveImplicitH(int num_atoms, int num_removed_H, inp_ATOM *at); -int add_DT_to_num_H(int num_atoms, inp_ATOM *at); -int MarkRingSystemsInp(inp_ATOM *at, int num_atoms, int start); -int free_t_group_info(T_GROUP_INFO *t_group_info); -int make_a_copy_of_t_group_info(T_GROUP_INFO *t_group_info, - T_GROUP_INFO *t_group_info_orig); -int set_tautomer_iso_sort_keys(T_GROUP_INFO *t_group_info); -int CountTautomerGroups(sp_ATOM *at, int num_atoms, T_GROUP_INFO *t_group_info); -int CountTautomerGroupsInpAt(inp_ATOM *at, int num_atoms, T_GROUP_INFO *t_group_info); -int SortTautomerGroupsAndEndpoints(T_GROUP_INFO *t_group_info, - int num_atoms, int num_at_tg, AT_RANK *nRank); -int FillIsotopicAtLinearCT(int num_atoms, sp_ATOM* at, - const AT_RANK *nAtomNumber, - AT_ISOTOPIC *LinearCTIsotopic, - int nMaxLenLinearCTIsotopic, int *pnLenLinearCTIsotopic); -int FillTautLinearCT2(int num_atoms, int num_at_tg, int bIsoTaut, - const AT_RANK *nRank, const AT_RANK *nAtomNumber, - const AT_RANK *nSymmRank, const AT_RANK *nRankIso, - const AT_RANK *nAtomNumberIso, const AT_RANK *nSymmRankIso, - AT_TAUTOMER *LinearCTTautomer, - int nMaxLenLinearCTTautomer, int *pnLenLinearCTTautomer, - AT_ISO_TGROUP *LinearCTIsotopicTautomer, - int nMaxLenLinearCTIsotopicTautomer, - int *pnLenLinearCTIsotopicTautomer, - T_GROUP_INFO *t_group_info); - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -#endif /* __INCHINORM_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichiprt1.c b/INCHI-1-SRC/INCHI_API/inchi_dll/ichiprt1.c deleted file mode 100644 index 3691e56..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichiprt1.c +++ /dev/null @@ -1,4171 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include - -#include "mode.h" - -#include "inpdef.h" -#include "ichi.h" -#include "strutil.h" -#include "util.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "ichicant.h" -#include "ichicano.h" -#include "ichicomn.h" -#include "ichister.h" - -#include "ichicomp.h" -#include "ichimain.h" -#include "ichimake.h" - -#include "ichi_io.h" - -int PrintXmlStartTag(char *pStr, - int indent, int bEnd, const char *tag, - const char *l1, int v1, const char *l2, int v2, - const char *l3, int v3, const char *l4, int v4, - const char *l5, int v5, const char *l6, int v6); -int Needs2addXmlEntityRefs(const char *s ); -int AddXmlEntityRefs(const char *p, char *d ); -#if ( TEST_RENUMB_ATOMS == 1 ) /* { */ -int CompareStereoINChI( INChI_Stereo *s1, INChI_Stereo *s2 ); -#endif - -int str_LineStart(const char *tag, char *tag2, int val2, char *pStr, int ind ); -int str_LineEnd(const char *tag, int tot_len, int nStrLen, - int *bOverflow, char *pStr, int ind, int bPlainTextTags ); -int CleanOrigCoord(MOL_COORD szCoord, int delim ); -int WriteOrigCoord(int num_inp_atoms, MOL_COORD *szMolCoord, int *i, - char *szBuf, int buf_len); -int WriteOrigAtoms(int num_inp_atoms, inp_ATOM *at, int *i, - char *szBuf, int buf_len, - STRUCT_DATA *sd); -int WriteOrigBonds(int num_inp_atoms, inp_ATOM *at, int *i, - char *szBuf, int buf_len, - STRUCT_DATA *sd); - -void GetSaveOptLetters(unsigned char save_opt_bits, char* let1, char* let2); - - -char VER_STRING[64]; - -const char sCompDelim[] = ";"; /* component delimiter */ -const char sIdenticalValues[] = "*"; /* identical component */ -const char x_space[] = " "; - -/* xml output: words & additional tags */ -const char x_inchi[] = INCHI_NAME; -const char x_inchi_ver[] = "version"; /* "InChI.version"; */ -const char x_curr_ver[] = INCHI_VERSION; - -const char x_structure[] = "structure"; -const char x_number[] = "number"; -const char x_header[] = "id.name"; -const char x_value[] = "id.value"; - -const char x_empty[] = ""; - -const char x_type[] = "type"; - -const char x_message[] = "message"; -const char x_text[] = "value"; - -const char x_ferr[] = "fatal (aborted)"; -const char x_err[] = "error (no InChI)"; -const char x_warn[] = "warning"; - -const char x_basic[] = "identifier"; -const char x_tautomeric[] = "mobile-H"; -const char x_reconnected[] = "reconnected"; - -const char x_ver[] = "version"; - -const char x_type_alpha[] = "alpha"; -const char x_type_numer[] = "numeric"; -const char x_type_predec[] = "sct"; -const char x_type_normal[] = "normal"; -const char x_type_short[] = "compressed"; -const char x_basic_layer[] = "basic"; - -const char x_aux_basic[] = "identifier.auxiliary-info"; -const char x_aux_comm[] = "!-- This section is NOT a part of the identifier, it is not unique --"; - -const char x_ign_uu_sp2[] = "omit_undef_dbond"; -const char x_ign_uu_sp3[] = "omit_undef_sp3"; - -const char x_line_opening[] = "<"; -const char x_line_closing[] = ""; - -const char x_abs[] = "1"; -const char x_rel[] = "2"; -const char x_rac[] = "3"; - -#define MAX_TAG_LEN 64 - -typedef struct tagInchiTag -{ - const char *szPlainLabel; - const char *szPlainComment; - const char *szXmlLabel; - int bAlwaysOutput; -} INCHI_TAG; - -/* identifier */ -const INCHI_TAG IdentLbl[] = -{ - /* prefixes: may be combined in this order */ -/* IL_FIXH_ORD, */ { "/", "fixed_H", "fixed-H", 0 }, /* fixed H */ -/* IL_ISOT_ORD, */ { "/", "isotopic", "isotopic", 0 }, /* isotopic */ -/* IL_STER_ORD, */ { "/", "stereo", "stereo", 0 }, /* stereo */ - /* items */ -/* IL_VERS_ORD, */ { "" , "version", "version", 1 }, -/* IL_FML__ORD, */ { "/", "formula", "formula", 1 }, /* basic part formula */ -/* IL_CONN_ORD, */ { "/c", "connections", "connections", 1 }, -/* IL_ALLH_ORD, */ { "/h", "H_atoms", "H", 1 }, -/* IL_CHRG_ORD, */ { "/q", "charge", "charge", 1 }, -/* IL_PROT_ORD, */ { "/p", "protons", "protons", 0 }, - /* stereo */ -/* IL_DBND_ORD, */ { "/b", "dbond", "dbond", 0 }, -/* IL_SP3S_ORD, */ { "/t", "sp3", "sp3", 0 }, -/* IL_INVS_ORD, */ { "/m", "sp3:inverted", "abs.inverted", 0 }, /* mirrored */ -/* IL_TYPS_ORD, */ { "/s", "type (1=abs, 2=rel, 3=rac)", "type", 0 }, /* stereo type */ - /* isotopic */ -/* IL_ATMS_ORD, */ { "/i", "atoms", "atoms", 1 }, - /* isotopic mobile H only */ -/* IL_XCGA_ORD, */ { "/h", "exchangeable_H", "H-isotopic", 1 }, - /* fixed H only */ -/* IL_FMLF_ORD, */ { "/f", "formula", "formula", 1 }, /* fixed H formula */ -/* IL_HFIX_ORD, */ { "/h", "H_fixed" , "H-fixed" , 1 }, /* fixed-H */ -/* IL_TRNS_ORD, */ { "/o", "transposition", "transposition", 0 }, /* order */ -/* IL_REC__ORD, */ { "/r", "reconnected bond(s) to metal(s) formula", "formula", 0 } -}; - -/* - - Parsing plain text InChI (FML is a chemical formula) - ======================== - - 1.12Beta/FML /i /f[FML] /i [/o] /rFML /i /f[FML] /i [/o] end - | | | | | | | | | -Labels | chqpbtms | hbtms | hqbtms | btms | chqpbtms | hbtms | hqbtms | btms | -inside: | | | | | | | | | - | non-iso- | iso- | fix- | iso- | non-iso- | iso- | fix- | iso- | -meaning: | topic | topic | ed H | topic | topic | topic | ed H | topic | - |----------+-------+--------+---------|----------+-------+--------+---------| - | mobile-H | fixed-H | mobile-H | fixed-H | - |----------+-------+--------+---------|----------+-------+--------+---------| - | | | - | normal or disconected metal | reconnected bonds to metal | - |_____________________________________|_____________________________________| - - meanings of h: - - /h - immobile H & mobile H group(s) - /i/h - exchangeable isotopic H (common) - /f/h - fixed-H - /f/i/h - never happens - -*/ - -typedef enum tagIdentLblOrd -{ - IL_FIXH_ORD, - IL_ISOT_ORD, - IL_STER_ORD, - - IL_VERS_ORD, - IL_FML__ORD, - IL_CONN_ORD, - IL_ALLH_ORD, - IL_CHRG_ORD, - IL_PROT_ORD, - - IL_DBND_ORD, - IL_SP3S_ORD, - IL_INVS_ORD, - IL_TYPS_ORD, - - IL_ATMS_ORD, - - IL_XCGA_ORD, - - IL_FMLF_ORD, - IL_HFIX_ORD, - IL_TRNS_ORD, - IL_REC__ORD, - - IL_MAX_ORD /* max number of tags */ -} IDENT_LBL_ORD; - -typedef enum tagIdentLblBit -{ - IL_FIXH = 1 << IL_FIXH_ORD, - IL_ISOT = 1 << IL_ISOT_ORD, - IL_STER = 1 << IL_STER_ORD, - - IL_VERS = 1 << IL_VERS_ORD, - IL_FML_ = 1 << IL_FML__ORD, - IL_CONN = 1 << IL_CONN_ORD, - IL_ALLH = 1 << IL_ALLH_ORD, - IL_CHRG = 1 << IL_CHRG_ORD, - IL_PROT = 1 << IL_PROT_ORD, - - IL_DBND = 1 << IL_DBND_ORD, - IL_SP3S = 1 << IL_SP3S_ORD, - IL_INVS = 1 << IL_INVS_ORD, - IL_TYPS = 1 << IL_TYPS_ORD, - - IL_ATMS = 1 << IL_ATMS_ORD, - - IL_XCGA = 1 << IL_XCGA_ORD, - - IL_FMLF = 1 << IL_FMLF_ORD, - IL_HFIX = 1 << IL_HFIX_ORD, - IL_TRNS = 1 << IL_TRNS_ORD, - IL_REC_ = 1 << IL_REC__ORD - -} IDENT_LBL_BIT; - -/* aux info */ -const INCHI_TAG AuxLbl[] = -{ - /* prefixes may be combined in this order */ -/* AL_FIXH_ORD, */ { "/", "fixed_H", "fixed-H", 0 }, /* fixed-H */ -/* AL_ISOT_ORD, */ { "/", "isotopic", "isotopic", 0 }, /* isotopic */ -/* AL_STER_ORD, */ { "/", "abs_stereo_inverted", "stereo.abs.inverted", 0 }, /* inv abs sp3 stereo */ -/* AL_REVR_ORD, */ { "/", "reversibility", "reversibility", 0 }, /* reversibility */ - /* items */ -/* AL_VERS_ORD, */ { "", "version", "version", 1 }, -/* AL_NORM_ORD, */ { "/", "normalization_type", "norm-type", 1 }, -/* AL_ANBR_ORD, */ { "/N:", "original_atom_numbers", "atom.orig-nbr", 1 }, -/* AL_AEQU_ORD, */ { "/E:", "atom_equivalence", "atom.equivalence", 0 }, -/* AL_GEQU_ORD, */ { "/gE:", "group_equivalence", "group.equivalence", 0 }, - /* inv abs sp3 stereo */ -/* AL_SP3I_ORD, */ { "/it:", "sp3", "sp3", 0 }, -/* AL_SP3N_ORD, */ { "/iN:", "original_atom_numbers", "atom.orig-nbr", 0 }, - -/* AL_CRV__ORD, */ { "/CRV:", "charge_radical_valence", "charges-rad-val", 0 }, - /* reversibility */ -/* AL_ATMR_ORD, */ { "/rA:", "atoms", "atoms", 0 }, -/* AL_BNDR_ORD, */ { "/rB:", "bonds", "bonds", 0 }, -/* AL_XYZR_ORD, */ { "/rC:", "xyz", "xyz", 0 }, - /* fixed-H only */ -/* AL_FIXN_ORD, */ { "/F:", "original_atom_numbers", "atom.orig-nbr", 1 }, - /* isotopic only */ -/* AL_ISON_ORD, */ { "/I:", "original_atom_numbers", "atom.orig-nbr", 1 }, - -/* AL_REC__ORD, */ { "/R:", "reconnected bond(s) to metal(s) part", "", 1 } - -}; - -typedef enum tagAuxLblOrd -{ - AL_FIXH_ORD, - AL_ISOT_ORD, - AL_STER_ORD, - AL_REVR_ORD, - - AL_VERS_ORD, - AL_NORM_ORD, - AL_ANBR_ORD, - AL_AEQU_ORD, - AL_GEQU_ORD, - - AL_SP3I_ORD, - AL_SP3N_ORD, - - AL_CRV__ORD, - - AL_ATMR_ORD, - AL_BNDR_ORD, - AL_XYZR_ORD, - - AL_FIXN_ORD, - - AL_ISON_ORD, - - AL_REC__ORD, - - AL_MAX_ORD /* max number of tags */ - -} AUX_LBL_ORD; - - -typedef enum tagAuxLblBit -{ - AL_FIXH = 1 << AL_FIXH_ORD, - AL_ISOT = 1 << AL_ISOT_ORD, - AL_STER = 1 << AL_STER_ORD, - AL_REVR = 1 << AL_REVR_ORD, - - AL_VERS = 1 << AL_VERS_ORD, - AL_NORM = 1 << AL_NORM_ORD, - AL_ANBR = 1 << AL_ANBR_ORD, - AL_AEQU = 1 << AL_AEQU_ORD, - AL_GEQU = 1 << AL_GEQU_ORD, - - AL_SP3I = 1 << AL_SP3I_ORD, - AL_SP3N = 1 << AL_SP3N_ORD, - - AL_CRV_ = 1 << AL_CRV__ORD, - - AL_ATMR = 1 << AL_ATMR_ORD, - AL_BNDR = 1 << AL_BNDR_ORD, - AL_XYZR = 1 << AL_XYZR_ORD, - - AL_FIXN = 1 << AL_FIXN_ORD, - - AL_ISON = 1 << AL_ISON_ORD, - - AL_REC_ = 1 << AL_REC__ORD - -} AUX_LBL_BIT; - -const int MAX_TAG_NUM = inchi_max((int)IL_MAX_ORD, (int)AL_MAX_ORD); - -char *szGetTag(const INCHI_TAG *Tag, int nTag, int bTag, char *szTag, int *bAlways); - -#define SP(N) (x_space+sizeof(x_space)-1-(N)) -/**********************************************************************************************/ -typedef struct tagXmlEntityRef -{ - char nChar; - const char *pRef; -} X_REF; -const X_REF xmlRef[] = { {'<', "<"}, {'&', "&"}, {'>', ">"}, {'"', """}, {'\'', "'"}, {0, NULL}, }; -const char szRefChars[sizeof(xmlRef)/sizeof(xmlRef[0])] = {'<', '&', '>', '"', '\'', '\0' }; -/**********************************************************************************************/ -int PrintXmlStartTag(char *pStr, int indent, int bEnd, const char *tag, - const char *l1, int v1, const char *l2, int v2, - const char *l3, int v3, const char *l4, int v4, - const char *l5, int v5, const char *l6, int v6) -{ - int len=0; - if ( tag ) { - len += sprintf( pStr+len, "%s<%s", SP(indent), tag); - } - if ( l1 ) { - len += sprintf( pStr+len, " %s=\"%d\"", l1, v1); - } - if ( l2 ) { - len += sprintf( pStr+len, " %s=\"%d\"", l2, v2); - } - if ( l3 ) { - len += sprintf( pStr+len, " %s=\"%d\"", l3, v3); - } - if ( l4 ) { - len += sprintf( pStr+len, " %s=\"%d\"", l4, v4); - } - if ( l5 ) { - len += sprintf( pStr+len, " %s=\"%d\"", l5, v5); - } - if ( l6 ) { - len += sprintf( pStr+len, " %s=\"%d\"", l6, v6); - } - if ( (bEnd & 3) ) { - len += sprintf( pStr+len, "%s%s", (bEnd & 1)?"/":"", (bEnd & 2)?">":""); - } - return len; -} - -/**********************************************************************************************/ -int Needs2addXmlEntityRefs( const char *s ) -{ - int len = 0; - const X_REF *q = xmlRef, *r; - const char *p; - if ( s && *s ) { - for ( q = xmlRef, len = 0; q->nChar; q ++ ) { - for ( p = s; p = strchr( p, q->nChar ); p ++ ) { - if ( q->nChar == '&' ) { - for ( r = xmlRef; r->nChar; r ++ ) { - if ( !memcmp( p, r->pRef, strlen(r->pRef) ) ) - goto DoNotSubstitute; - } - } - len += strlen(q->pRef)-1; -DoNotSubstitute:; - } - } - if ( len ) { - len += strlen( s ); - } - } - return len; -} - -/**********************************************************************************************/ -int AddXmlEntityRefs( const char *p, char *d ) -{ - int len_d, n; - const X_REF *q = xmlRef, *r; - - len_d = 0; - while ( *p ) { - n = strcspn( p, szRefChars ); - if ( n > 0 ) { - /* first n characters of p do not contain referenced chars; copy them */ - strncpy( d+len_d, p, n ); /* does not have zero termination */ - len_d += n; /* new destination length */ - p += n; /* position of the referenced char in the source */ - } - if ( *p ) { - if ( *p == '&' ) { - for ( r = xmlRef; r->nChar; r ++ ) { - if ( !memcmp( p, r->pRef, strlen(r->pRef) ) ) { - d[len_d++] = *p; - goto DoNotSubstitute; - } - } - } - q = xmlRef + (strchr( szRefChars, UCINT *p) - szRefChars); - strcpy( d+len_d, q->pRef ); /* add entity reference and zero termination */ - len_d += strlen( d + len_d ); /* new destination length */ -DoNotSubstitute: - p ++; - } else { - d[len_d] = '\0'; /* add zero termination */ - } - - } - return len_d; -} - -/**********************************************************************************************/ -int OutputINChIXmlRootStartTag( INCHI_IOSTREAM *output_file ) -{ - char pStr[128]; - sprintf( pStr, "<%s %s=\"%s\">", x_inchi, x_inchi_ver, x_curr_ver ); - inchi_ios_print_nodisplay( output_file, "%s\n", pStr ); - return 0; -} - -/**********************************************************************************************/ -int OutputINChIXmlRootEndTag( INCHI_IOSTREAM *output_file ) -{ - char pStr[128]; - sprintf( pStr, "", x_inchi ); - inchi_ios_print_nodisplay( output_file, "%s\n", pStr ); - return 0; -} - -/**********************************************************************************************/ -int OutputINChIXmlStructStartTag( INCHI_IOSTREAM *output_file, char *pStr, int ind /* indent*/, - int nStrLen, int bNoStructLabels, - int num_input_struct, const char *szSdfLabel, const char *szSdfValue ) -{ - char szBuf[64]; - int nEstLen1; - int nEstLen2; - int ret = 0; - int tot_len; - char *pSdfLabel = NULL, *pSdfValue = NULL, *p; - /* substitute special characters (see szRefChars[]) with xml Entity References */ - int len; - if ( bNoStructLabels ) { - /* no labela at all */ - inchi_ios_print( output_file, "%s\n", "" ); /* empty line */ - tot_len = 0; - tot_len += sprintf(pStr+tot_len, "%s<%s", SP(ind), x_structure); - tot_len += sprintf(pStr+tot_len, ">" ); - inchi_ios_print( output_file, "%s\n", pStr ); - ret = 1; /* success */ - } else - if ( !(szSdfLabel && szSdfLabel[0]) && !(szSdfValue && szSdfValue[0]) ) { - /* only structure number if present */ - inchi_ios_print( output_file, "%s\n", "" ); /* empty line */ - tot_len = 0; - tot_len += sprintf(pStr+tot_len, "%s<%s", SP(ind), x_structure); - if ( num_input_struct > 0 ) { - tot_len += sprintf(pStr+tot_len, " %s=\"%d\"", x_number, num_input_struct); - } - tot_len += sprintf(pStr+tot_len, ">" ); - inchi_ios_print( output_file, "%s\n", pStr ); - ret = 1; /* success */ - } else { - if ( len = Needs2addXmlEntityRefs( szSdfLabel ) ) { - if ( p = (char*) inchi_malloc( len+1 ) ) { - AddXmlEntityRefs( szSdfLabel, p ); - szSdfLabel = pSdfLabel = p; - } - } - if ( len = Needs2addXmlEntityRefs( szSdfValue ) ) { - if ( p = (char*) inchi_malloc( len+1 ) ) { - AddXmlEntityRefs( szSdfValue, p ); - szSdfValue = pSdfValue = p; - } - } - nEstLen1 = ind + 1 + sizeof(x_structure)-1 - + 1 + sizeof(x_number)-1 + 1 + sprintf(szBuf,"\"%d\"", num_input_struct) + 2; - nEstLen2 = 1 + sizeof(x_header)-1 + 1 + 2 + (szSdfLabel? strlen(szSdfLabel):0) - + 1 + sizeof(x_value) -1 + 1 + 2 + (szSdfValue? strlen(szSdfValue):0) + 2; - if ( nEstLen1 <= nStrLen ) { - inchi_ios_print( output_file, "%s\n", "" ); /* empty line */ - tot_len = 0; - tot_len += sprintf(pStr+tot_len, "%s<%s", SP(ind), x_structure); - tot_len += sprintf(pStr+tot_len, " %s=\"%d\"", x_number, num_input_struct); - if ( nEstLen1 + nEstLen2 <= nStrLen ) { - tot_len += sprintf(pStr+tot_len, " %s=\"%s\"", x_header, szSdfLabel? szSdfLabel:x_empty); - tot_len += sprintf(pStr+tot_len, " %s=\"%s\"", x_value, szSdfValue? szSdfValue:x_empty); - } - tot_len += sprintf(pStr+tot_len, ">" ); - inchi_ios_print( output_file, "%s\n", pStr ); - ret = 1; /* success */ - } - if ( pSdfValue ) { - inchi_free ( pSdfValue ); - } - if ( pSdfLabel ) { - inchi_free( pSdfLabel ); - } - } - return ret; /* 0 => Buffer overflow */ -} - -/**********************************************************************************************/ -int OutputINChIXmlStructEndTag( INCHI_IOSTREAM *output_file, char *pStr, int nStrLen, int ind ) -{ - if ( output_file && pStr ) - { - int nEstLen1 = ind + 1 + 1 + sizeof(x_structure)-1 + 2; - if ( nEstLen1 <= nStrLen ) - { - sprintf(pStr, "%s", SP(ind), x_structure); - inchi_ios_print( output_file, "%s\n", pStr ); - return 1; - } - } - return 0; -} - -/**********************************************************************************************/ -int OutputINChIXmlError( INCHI_IOSTREAM *output_file, char *pStr, int nStrLen, int ind, - /*int nErrorNumber,*/ char *pErrorText, int bError ) -{ - /* char szBuf[64]; */ - const char *pErr; - char *pNewErrorText=NULL, *szErrorText = pErrorText; - int nEstLen, len=0, ret = 0; - - switch( bError ) { - case _IS_WARNING: - pErr = x_warn; - break; - case _IS_ERROR: - pErr = x_err; - break; - default: /* _IS_FATAL */ - pErr = x_ferr; - break; - } - -#if ( ENTITY_REFS_IN_XML_MESSAGES == 1 ) - /* insert xml entity references if necessary */ - if ( len = Needs2addXmlEntityRefs( szErrorText ) ) { - if ( pNewErrorText = (char*) inchi_malloc( len+1 ) ) { - AddXmlEntityRefs( szErrorText, pNewErrorText ); - szErrorText = pNewErrorText; - } - } -#else - szErrorText = pErrorText; -#endif - - - nEstLen = ind + 1 + sizeof(x_message)-1 - + 1 + sizeof(x_type)-1 + 1 + 1 + strlen(pErr)-1 - /* + 1 + sizeof(x_code)-1 + 1 + sprintf(szBuf, "%d", nErrorNumber) */ - + 1 + sizeof(x_text)-1 + 1 + 1 + strlen(szErrorText) + 2; - if ( nEstLen <= nStrLen ) { - /* - sprintf( pStr, "%s<%s %s=\"%s\" %s=\"%d\" %s=\"%s\"/>", - SP(ind), x_message, x_type, pErr, x_code, nErrorNumber, x_text, szErrorText ); - */ - sprintf( pStr, "%s<%s %s=\"%s\" %s=\"%s\"/>", - SP(ind), x_message, x_type, pErr, x_text, szErrorText ); - inchi_ios_print( output_file, "%s\n", pStr ); - /* - pErrorText[0] = '\0'; // do not repeat same output - */ - ret = 1; - } - if ( pNewErrorText ) - inchi_free( pNewErrorText ); - return ret; - -} - -/**********************************************************************************************/ -int OutputINChIPlainError( INCHI_IOSTREAM *output_file, char *pStr, int nStrLen, - char *pErrorText, int bError ) -{ - /* char szBuf[64]; */ - const char *pErr; - char *pNewErrorText=NULL, *szErrorText = pErrorText; - int nEstLen, ret = 0; - - switch( bError ) { - case _IS_WARNING: - pErr = x_warn; - break; - case _IS_ERROR: - pErr = x_err; - break; - default: /* _IS_FATAL */ - pErr = x_ferr; - break; - } - /* <%s: >, x_message */ - nEstLen = sizeof(x_message)-1 + 1 + 1 - /* <%s=\"%s\">, x_type, pErr */ - + sizeof(x_type)-1 + 1 + 1 + strlen(pErr) + 1 - /* < %s=\"%s\"\n>, x_text, szErrorText */ - + 1 + sizeof(x_text)-1 + 1 + 1 + strlen(szErrorText) + 1 + 1; - if ( nEstLen < nStrLen ) { - sprintf( pStr, "%s: %s=\"%s\" %s=\"%s\"", - x_message, x_type, pErr, x_text, szErrorText ); - inchi_ios_print( output_file, "%s\n", pStr ); - ret = 1; - } - if ( pNewErrorText ) - inchi_free( pNewErrorText ); - return ret; - -} - -/**************************************************************************/ - -#ifndef OUT_TN /* defined in mode.h; quoted here for reference purposes only */ - -#define OUT_N1 0 /* non-tautomeric only */ -#define OUT_T1 1 /* tautomeric if present otherwise non-tautomeric */ -#define OUT_NT 2 /* only non-taut representations of tautomeric */ -#define OUT_TN 3 /* tautomeric if present otherwise non-tautomeric; - sepatately output non-taut representations of tautomeric if present */ -/* OUT_TN = OUT_T1 + OUT_NT */ -#endif - - -/******************************************************************/ -const char *EquString( int EquVal ) -{ - int bFrom = EquVal & (iiSTEREO | iiSTEREO_INV | iiNUMB | iiEQU ); - int bType = EquVal & (iitISO | iitNONTAUT ); - int bEq2 = EquVal & (iiEq2NONTAUT | iiEq2ISO | iiEq2INV ); - const char *r = ""; - -#if ( FIX_EMPTY_LAYER_BUG == 1 ) - int bEmpty= EquVal & iiEmpty; - if ( bEmpty ) { - r = "e"; - return r; - } -#endif - - switch ( bFrom ) { - - case iiSTEREO: /* ------------ Stereo --------------------*/ - switch ( bType ) { - case iitISO: /* iso main stereo =... */ - switch( bEq2 ) { - case 0: - r = "m"; /* iso main stereo = main stereo */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - case iitNONTAUT: /* non-taut stereo =... */ - switch( bEq2 ) { - case 0: - r = "m"; /* non-taut stereo = main stereo */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - case (iitNONTAUT | iitISO): /* iso non-taut stereo = ... */ - switch( bEq2 ) { - case 0: - r = "m"; /* iso non-taut stereo = main stereo */ - break; - case iiEq2ISO: - r = "M"; /* iso non-taut stereo = main iso stereo */ - break; - case iiEq2NONTAUT: - r = "n"; /* iso non-taut stereo = non-taut stereo */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - - case iiSTEREO_INV: /*---------- Inverted Aux Stereo ------*/ - if ( bEq2 & iiEq2INV ) { /* stereo = Inverted(another stereo) */ - bEq2 &= ~iiEq2INV; - switch( bType ) { - case 0: /* main = ...*/ - switch( bEq2 ) { - case 0: - r = "im"; /* main = Inv(main) */ - break; - case iiEq2ISO: - r = "iM"; /* main = Inv(main iso) */ - break; - case iiEq2NONTAUT: - r = "in"; /* maim = Inv(non-taut) */ - break; - case (iiEq2NONTAUT | iiEq2ISO): - r = "iN"; /* maim = Inv(non-taut iso ) */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - case iitISO: /* main iso = ...*/ - switch( bEq2 ) { - case 0: - r = "im"; /* main iso = Inv(main) */ - break; - case iiEq2ISO: - r = "iM"; /* main iso = Inv(main iso) */ - break; - case iiEq2NONTAUT: - r = "in"; /* maim iso = Inv(non-taut) */ - break; - case (iiEq2NONTAUT | iiEq2ISO): - r = "iN"; /* maim = Inv(non-taut iso ) */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - case iitNONTAUT: /* non-taut = ... */ - switch( bEq2 ) { - case 0: - r = "im"; /* non-taut = Inv(main) */ - break; - case iiEq2ISO: - r = "iM"; /* non-taut = Inv(main iso) */ - break; - case iiEq2NONTAUT: - r = "in"; /* non-taut = Inv(non-taut) */ - break; - case (iiEq2NONTAUT | iiEq2ISO): - r = "iN"; /* non-taut = Inv(non-taut iso ) */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - case (iitNONTAUT | iitISO): - switch( bEq2 ) { - case 0: - r = "im"; /* non-taut iso = Inv(main) */ - break; - case iiEq2ISO: - r = "iM"; /* non-taut iso = Inv(main iso) */ - break; - case iiEq2NONTAUT: - r = "in"; /* non-taut iso = Inv(non-taut) */ - break; - case (iiEq2NONTAUT | iiEq2ISO): - r = "iN"; /* non-taut iso = Inv(non-taut iso ) */ - break; - default: - r = "??"; /* should not happen */ - } - break; - default: - r = "??"; /* should not happen */ - break; - } - - } else { /* Inv stereo = another (non-inverted) stereo */ - - switch( bType ) { - case iitISO: /* main iso = ...*/ - switch( bEq2 ) { - case 0: - r = "m"; /* main = (inverted aux) main */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - case iitNONTAUT: /* non-taut = ... */ - switch( bEq2 ) { - case 0: - r = "m"; /* non-taut = (inverted aux) main */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - case (iitNONTAUT | iitISO): /* non-taut iso = ...*/ - switch( bEq2 ) { - case 0: - r = "m"; /* non-taut iso = (inverted aux) main */ - break; - case iiEq2ISO: - r = "M"; /* non-taut iso = (inverted aux) main iso */ - break; - case iiEq2NONTAUT: - r = "n"; /* non-taut iso = (inverted aux) non-taut */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - default: - r = "??"; /* should not happen */ - break; - } - } - break; - - case ( iiNUMB | iiSTEREO_INV): /*------------- Inv Stereo Numbering ------------*/ - switch( bType ) { - case 0: /* inv stereo numb main = ...*/ - switch( bEq2 ) { - case 0: - r = "m"; /* inv stereo numb main = main numb */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - case iitISO: /* inv stereo iso numb main = ...*/ - switch( bEq2 ) { - case 0: - r = "m"; /* inv stereo iso numb main = main numb */ - break; - case iiEq2INV: - r = "im"; /* inv stereo iso numb main = InvStereo(main) numb */ - break; - case iiEq2ISO: - r = "M"; /* inv stereo iso numb main = isotopic main numb */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - case iitNONTAUT: /* inv stereo numb non-taut = ... */ - switch( bEq2 ) { - case 0: - r = "m"; /* inv stereo numb non-taut = main numb */ - break; - case iiEq2NONTAUT: - r = "n"; /* inv stereo numb non-taut = non-taut numb */ - break; - case iiEq2INV: - r = "im"; /* inv stereo numb non-taut = InvStereo(main) numb */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - case (iitNONTAUT | iitISO): /* inv stereo numb non-taut iso = ... */ - switch( bEq2 ) { - case 0: - r = "m"; /* inv stereo numb non-taut iso = main numb */ - break; - case iiEq2ISO: - r = "M"; /* inv stereo numb non-taut iso = main numb iso */ - break; - case (iiEq2ISO | iiEq2INV): - r = "iM"; /* inv stereo numb non-taut iso = InvStereo(main iso) numb */ - break; - case iiEq2NONTAUT: - r = "n"; /* inv stereo numb non-taut iso = non-taut numb */ - break; - case (iiEq2NONTAUT | iiEq2ISO): - r = "N"; /* inv stereo numb non-taut iso = non-taut iso numb */ - break; - case iiEq2INV: - r = "im"; /* inv stereo numb non-taut iso = InvStereo(main) numb */ - break; - case (iiEq2NONTAUT | iiEq2INV): - r = "in"; /* inv stereo numb non-taut iso = InvStereo(non-taut) numb ) */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - - case iiNUMB: /*------------- Canonical Numbering ------------*/ - switch( bType ) { - case 0: /* numb main = ...*/ - r = "??"; /* should not happen */ - break; - case iitISO: /* iso numb main = ...*/ - switch( bEq2 ) { - case 0: - r = "m"; /* iso numb main = main numb */ - break; - default: - r = "??"; /* should not happen */ - } - break; - case iitNONTAUT: /* numb non-taut = ... */ - switch( bEq2 ) { - case 0: - r = "m"; /* numb non-taut = main numb */ - break; - default: - r = "??"; /* should not happen */ - } - break; - case (iitNONTAUT | iitISO): /* numb non-taut iso = ... */ - switch( bEq2 ) { - case 0: - r = "m"; /* numb non-taut iso = main numb */ - break; - case iiEq2ISO: - r = "M"; /* numb non-taut iso = main numb iso */ - break; - case iiEq2NONTAUT: - r = "n"; /* numb non-taut iso = non-taut numb */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - - case iiEQU: /*------------- Atom Equivalence ------------*/ - switch( bType ) { - case 0: /* equivalence main = ...*/ - r = "??"; /* should not happen */ - break; - case iitISO: /* equivalence main iso = ...*/ - switch( bEq2 ) { - case 0: - r = "m"; /* equivalence main = main equ */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - case iitNONTAUT: /* equivalence non-taut = ... */ - switch( bEq2 ) { - case 0: - r = "m"; /* equivalence non-taut = main equ */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - case (iitNONTAUT | iitISO): /* equivalence non-taut iso = ... */ - switch( bEq2 ) { - case 0: - r = "m"; /* equivalence non-taut iso = main equ */ - break; - case iiEq2ISO: - r = "M"; /* equivalence non-taut iso = main iso equ */ - break; - case iiEq2NONTAUT: - r = "n"; /* equivalence non-taut iso = non-taut equ */ - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - default: - r = "??"; /* should not happen */ - break; - } - break; - default: - r = "??"; /* should not happen */ - break; - } - return r; -} - -/**********************************************************************************************/ - -#define OUT_NONTAUT OUT_NN /* was OUT_NT until 2004-04-07 */ - - -/**********************************************************************************************/ -int OutputINChI2(char *pStr, int nStrLen, - INCHI_SORT *pINChISortTautAndNonTaut2[][TAUT_NUM], - int iINChI, - ORIG_STRUCT *pOrigStruct, - int bDisconnectedCoord, int bOutputType, int bINChIOutputOptions, - int bXml, int bAbcNumbers,int bCtPredecessors, int bNoStructLabels, - int num_components2[], - int num_non_taut2[], int num_taut2[], - INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *log_file, - int num_input_struct, - const char *szSdfLabel, const char *szSdfValue, long lSdfId, - int *pSortPrintINChIFlags, - unsigned char save_opt_bits) -{ - int bINChIOutputOptions0 = bINChIOutputOptions & ~(INCHI_OUT_XML | INCHI_OUT_PLAIN_TEXT | INCHI_OUT_PLAIN_TEXT_COMMENTS); - int bINChIOutputOptionsCur; - int bCurOption, ret, i; - - ret = 0; - - for ( i = 0; i < 3; i ++ ) - { - switch( i ) - { - case 0: - bCurOption = INCHI_OUT_XML; - break; - case 1: - bCurOption = INCHI_OUT_PLAIN_TEXT; - break; - case 2: - bCurOption = INCHI_OUT_PLAIN_TEXT_COMMENTS; - break; - default: - continue; - } - if ( bINChIOutputOptions & bCurOption ) - { - bINChIOutputOptionsCur = bINChIOutputOptions0 | bCurOption; - if ( i != 1 ) - { - bINChIOutputOptionsCur &= ~INCHI_OUT_TABBED_OUTPUT; - } - ret |= OutputINChI1( pStr, nStrLen, - pINChISortTautAndNonTaut2, - iINChI, - pOrigStruct, - bDisconnectedCoord, bOutputType, bINChIOutputOptionsCur, - bXml, bAbcNumbers,bCtPredecessors, bNoStructLabels, - num_components2, - num_non_taut2, num_taut2, - output_file, log_file, - num_input_struct, - szSdfLabel, szSdfValue, lSdfId, - pSortPrintINChIFlags, - save_opt_bits); - } - } - - return ret; -} - -/**********************************************************************************/ -char *szGetTag( const INCHI_TAG *Tag, int nTag, int bTag, char *szTag, int *bAlways ) -{ - int i, j, bit, num, len; - if ( 0 < nTag && nTag < 3 ) { - /* no plain text comments: pick up the last tag */ - for ( i = 0, j = -1, bit = 1; i < MAX_TAG_NUM; i ++, bit <<= 1 ) { - if ( bTag & bit ) { - j = i; - } - } - if ( j >= 0 ) { - strcpy( szTag, nTag == 1? Tag[j].szXmlLabel : nTag == 2? Tag[j].szPlainLabel : "???" ); - if ( nTag != 2 ) { - *bAlways = Tag[j].bAlwaysOutput; - } - return szTag; - } - } else - if ( nTag == 3 ) { - /* plain text with comments */ - szTag[0] = '{'; - szTag[1] = '\0'; - for ( i = 0, j = -1, bit = 1, num=0; i < MAX_TAG_NUM; i ++, bit <<= 1 ) { - if ( bTag & bit ) { - j = i; - if ( num ++ ) { - strcat( szTag, ":" ); - } - strcat( szTag, Tag[i].szPlainComment ); - } - } - if ( num ) { - strcat( szTag, "}" ); - num = strlen( Tag[j].szPlainLabel ); - len = strlen( szTag ); - if ( len ) { - memmove( szTag + num, szTag, len+1 ); - memcpy( szTag, Tag[j].szPlainLabel, num ); - } else { - strcpy ( szTag, Tag[j].szPlainLabel ); - } - *bAlways = Tag[j].bAlwaysOutput; - } else { - strcpy( szTag, "???" ); - } - return szTag; - } - strcpy( szTag, "???" ); - return szTag; -} - - -/***************************************************************************************/ -/* sorting in descending order: return -1 if *p1 > *p2, return +1 if *p1 < *p2 */ -/***************************************************************************************/ -int OutputINChI1(char *pStr, int nStrLen, - INCHI_SORT *pINChISortTautAndNonTaut2[][TAUT_NUM], - int iINChI, - ORIG_STRUCT *pOrigStruct, - int bDisconnectedCoord, int bOutputType, int bINChIOutputOptions, - int bXml, int bAbcNumbers,int bCtPredecessors, int bNoStructLabels, - int num_components2[], int num_non_taut2[], int num_taut2[], - INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *log_file, - int num_input_struct, - const char *szSdfLabel, const char *szSdfValue, long lSdfId, - int *pSortPrintINChIFlags, - unsigned char save_opt_bits) -{ -/* - bINChIOutputOptions bits: - - INCHI_OUT_NO_AUX_INFO 0x0001 do not output Aux Info - INCHI_OUT_SHORT_AUX_INFO 0x0002 output short version of Aux Info - INCHI_OUT_ONLY_AUX_INFO 0x0004 output only Aux Info - INCHI_OUT_EMBED_REC 0x0008 embed reconnected INChI into disconnected INChI - -*/ - - /*int ATOM_MODE = ((bAbcNumbers?2:0)|5|(bCtPredecessors?8:0));*/ - int ATOM_MODE = ((bAbcNumbers?CT_MODE_ABC_NUMBERS:0) - | CT_MODE_ATOM_COUNTS - | CT_MODE_NO_ORPHANS -#if ( EQL_H_NUM_TOGETHER == 1 ) - | CT_MODE_EQL_H_TOGETHER -#endif -#if ( ABC_CT_NUM_CLOSURES == 1 ) - | (bAbcNumbers && bCtPredecessors? CT_MODE_ABC_NUM_CLOSURES:0) -#endif - | (bCtPredecessors?CT_MODE_PREDECESSORS:0)); - - int TAUT_MODE = (bAbcNumbers?CT_MODE_ABC_NUMBERS:0); - char sDifSegs[DIFL_LENGTH][DIFS_LENGTH]; - /* bOutputType = - TAUT_YES => tautomeric only (if no tautomeric components then no output; - TAUT_NON => only non-tautomeric output (if no non-taut present then no output; - TAUT_BOTH => tautomeric and non-tautomeric */ - - int i, j, ii, jj, /*ii2, jj2,*/ tot_len, tot_len2, bOverflow, bEmbeddedOutputCalled=0; - int bIsotopic, bTautIsoHNum, bTautIsoAt, bHasIsotopicAtoms[TAUT_NUM]; - int bStereoSp2[TAUT_NUM], bStereoSp3[TAUT_NUM]; - int bIsotopicStereoSp2[TAUT_NUM], bIsotopicStereoSp3[TAUT_NUM]; - int bStereoAbsInverted[TAUT_NUM], bIsotopicStereoAbsInverted[TAUT_NUM]; - int bStereoAbs[TAUT_NUM], bIsotopicStereoAbs[TAUT_NUM]; - int bAtomEqu[TAUT_NUM], bTautEqu[TAUT_NUM], bIsotopicAtomEqu[TAUT_NUM], bIsotopicTautEqu[TAUT_NUM]; - int bInvStereo[TAUT_NUM], bInvIsotopicStereo[TAUT_NUM]; - int bInvStereoOrigNumb[TAUT_NUM], bInvIsotopicStereoOrigNumb[TAUT_NUM], bIsotopicOrigNumb[TAUT_NUM]; - int bTautomeric, bNonTautomeric, bTautomericAcid, bHardAddRemProton, iCurTautMode; - int bRequestedRacemicStereo=0, bRequestedRelativeStereo = 0, bRelRac; - int bRacemicStereo[TAUT_NUM], bRelativeStereo[TAUT_NUM]; - int bIsotopicRacemicStereo[TAUT_NUM], bIsotopicRelativeStereo[TAUT_NUM]; - int bChargesRadVal[TAUT_NUM], bOrigCoord[TAUT_NUM]; - int bIgn_UU_Sp3[TAUT_NUM], bIgn_UU_Sp2[TAUT_NUM]; - int bIgn_UU_Sp3_Iso[TAUT_NUM], bIgn_UU_Sp2_Iso[TAUT_NUM]; - int ind, inc, bNonTautIsIdenticalToTaut = 1; - int bNonTautNonIsoIdentifierNotEmpty = 0, bNonTautIsoIdentifierNotEmpty = 0; - INCHI_SORT **pINChISortTautAndNonTaut = pINChISortTautAndNonTaut2[iINChI]; - INCHI_SORT *pINChISort =pINChISortTautAndNonTaut[TAUT_YES]; - INCHI_SORT *pINChISort2=pINChISortTautAndNonTaut[TAUT_YES]; - INCHI_SORT *is, *is2; - INChI *pINChI /*, *pINChI2*/; - INChI_Aux *pINChI_Aux = NULL; - - - int ret = 0; /* 0=>failed, 1=>success */ - int bOutType = bOutputType; /* ??? */ - int nTag; - int bTautomericOutputAllowed, bSecondNonTautPass; - int num_components = num_components2[iINChI]; - int num_comp[TAUT_NUM], max_num_comp; - int num_iso_H[NUM_H_ISOTOPES], bHasIsoH; - int nNumRemovedProtons, nNumMovedProtons; - int bTautAndNonTaut, bTautIsNonTaut; - - int bAlways = 0; - int bUseMulipliers = 1; - int bOmitRepetitions = 1; - int bPlainTextTags = 2; /* 0 => no plain tags, 1=> plain text tags, 2=>plaintext tags without consecutive // */ - int bPlainText = 0 != (bINChIOutputOptions & (INCHI_OUT_PLAIN_TEXT | INCHI_OUT_PLAIN_TEXT_COMMENTS)); - int bPlainTextCommnts = 0 != (bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT_COMMENTS); - int bPlainTabbedOutput; - int bTag1, bTag2, bTag3, bFhTag; /* tag bits */ - int nCurINChISegment, nSegmAction; - char szTag1[MAX_TAG_LEN], szTag2[MAX_TAG_LEN], szTag3[MAX_TAG_LEN]; - const char *pLF, *pTAB; - - /*^^^ 15 April, 2008 */ - int bFixTranspChargeBug = 0; -#if ( FIX_TRANSPOSITION_CHARGE_BUG == 1 ) /* 2008-01-02 */ - if ( INCHI_OUT_FIX_TRANSPOSITION_CHARGE_BUG & bINChIOutputOptions ) - bFixTranspChargeBug = 1; -#endif - /*^^^ 15 April, 2008 */ - - bXml = 0 != (bINChIOutputOptions & INCHI_OUT_XML); - nTag = bPlainTextCommnts? 3 : bPlainText? 2 : bXml? 1 : 0; /* tag type */ - ind = bXml? 1 : -1; - inc = bXml? 1 : -1; - pLF = bPlainTextCommnts? "\n" : "\0"; - bFhTag = 0; - bPlainTabbedOutput = 0 != (bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT) && - bPlainText && !bXml && !bPlainTextCommnts; -#if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) ) - pTAB = bPlainTabbedOutput? "\t" : "\n"; -#else - pTAB = "\n"; -#endif - - - memset( sDifSegs, DIFV_BOTH_EMPTY, sizeof(sDifSegs) ); - - if ( !pStr ) { - inchi_ios_eprint(log_file, - "Cannot allocate output buffer. No output for structure #%d.%s%s%s%s\n", - num_input_struct, SDF_LBL_VAL(szSdfLabel, szSdfValue)); - return ret; - } - - bSecondNonTautPass = 0; -/* -- commented out to allow empty InChI -- - if (!num_components ) - { - return 0; - } -*/ - - /* init version string */ - if ( !VER_STRING[0] ) - { - strcpy(VER_STRING, "(V"); - strcat(VER_STRING, INCHI_VERSION); - strcat(VER_STRING, ")"); - } - for ( i = 0; i < TAUT_NUM; i ++ ) - { - bHasIsotopicAtoms[i] = num_comp[i] = - bStereoSp2[i] = bStereoSp3[i] = - bIsotopicStereoSp2[i] = bIsotopicStereoSp3[i] = - bIsotopicOrigNumb[i] = - bStereoAbs[i] = bIsotopicStereoAbs[i] = - bStereoAbsInverted[i] = bIsotopicStereoAbsInverted[i] = - bRacemicStereo[i] = bRelativeStereo[i] = - bIsotopicRacemicStereo[i] = bIsotopicRelativeStereo[i] = - bAtomEqu[i] = bTautEqu[i] = - bIsotopicAtomEqu[i] = bIsotopicTautEqu[i] = - bInvStereo[i] = bInvIsotopicStereo[i] = - bInvStereoOrigNumb[i] = bInvIsotopicStereoOrigNumb[i] = - bIgn_UU_Sp3[i] = bIgn_UU_Sp2[i] = - bIgn_UU_Sp3_Iso[i] = bIgn_UU_Sp2_Iso[i] = - bChargesRadVal[i] = bOrigCoord[i] = 0; - } - - /* find if it is isotopic */ - bIsotopic = bTautomeric = bNonTautomeric = bTautomericAcid = - bHardAddRemProton = bTautIsoHNum = bTautIsoAt = 0; - bTautAndNonTaut = bTautIsNonTaut = 0; - /* - x = bStereo, bStereoSp2, bStereoSp3, bStereoAbsInverted, - bIsotopicStereo, bIsotopicStereoSp2, bIsotopicStereoSp3, bIsotopicStereoAbsInverted - - OUT_N1: x[TAUT_NON] refers to non-tautomeric only - OUT_T1: x[TAUT_YES] refers to tautomeric if exists otherwise non-tautomeric - OUT_NT: x[TAUT_NON] refers to non-taut representations of tautomeric - OUT_TN: x[TAUT_YES] refers to tautomeric if exists otherwise non-tautomeric - x[TAUT_NON] refers to non-taut representations of tautomeric - */ - - memset( num_iso_H, 0, sizeof(num_iso_H) ); - nNumRemovedProtons = 0; - nNumMovedProtons = 0; - bHasIsoH = 0; - bTautomericOutputAllowed = (bOutType==OUT_T1 || bOutType== OUT_TN); - pINChISort=pINChISortTautAndNonTaut[bTautomericOutputAllowed? TAUT_YES : TAUT_NON]; - is = pINChISort; - is2 = (bOutType== OUT_TN)? pINChISortTautAndNonTaut[TAUT_NON] : NULL; - - for ( i = 0, is2 = pINChISortTautAndNonTaut[TAUT_NON]; i < num_components; i ++, is ++, is2? is2++:NULL ) - { - CompINChILayers( is, is2, sDifSegs, bFixTranspChargeBug ); - bNonTautIsIdenticalToTaut = bNonTautIsIdenticalToTaut && !CompINChITautVsNonTaut(is, is2, 1); - if ( is && (pINChI_Aux = is->pINChI_Aux[TAUT_YES]) ) - { - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) - { - bHasIsoH += abs(pINChI_Aux->nNumRemovedIsotopicH[j]); - num_iso_H[j] += pINChI_Aux->nNumRemovedIsotopicH[j]; - } - nNumRemovedProtons += pINChI_Aux->nNumRemovedProtons; - nNumMovedProtons += abs(pINChI_Aux->nNumRemovedProtons); - } - if ( bTautomericOutputAllowed ) - { - /* check for removed isotopic H */ - for ( j = TAUT_YES; j < TAUT_NUM; j ++ ) - { - switch ( bOutType ) { - case OUT_N1: /* x[TAUT_NON]: non-tautomeric only -- never happens */ - jj = GET_II(bOutType,is); - if ( jj != j ) - continue; - ii = TAUT_NON; - break; - case OUT_T1: /* x[TAUT_YES]: tautomeric if present otherwise non-tautomeric */ - jj = GET_II(bOutType,is); - if ( jj != j ) - continue; - ii = TAUT_YES; - break; - case OUT_NT: /* x[TAUT_NON]: only non-taut representations of tautomeric -- never happens */ - jj = GET_II(bOutType,is); - if ( jj != j ) - continue; - ii = TAUT_NON; - break; - /* main path of control flow */ - case OUT_TN: /* x[TAUT_YES]: tautomeric if present otherwise non-tautomeric; - * x[TAUT_NON]: non-taut only if tautomeric is present */ - jj = ( j == TAUT_YES )? GET_II(OUT_T1,is) : ( j == TAUT_NON )? GET_II(OUT_NT,is) : -1; - if ( jj == TAUT_YES ) - { - /* Fix12 */ - if ( is->pINChI[jj]->lenTautomer > 0 ) - { - bTautAndNonTaut += (!is->pINChI[jj]->bDeleted && HAS_N(is)); - } else - { - bTautIsNonTaut ++; - } - } - if ( jj < 0 ) - continue; - ii = j; - break; - default: - continue; - } - if ( jj != j ) - continue; - if ( (pINChI = is->pINChI[jj]) && pINChI->nNumberOfAtoms > 0 && (pINChI_Aux = is->pINChI_Aux[jj]) ) - { - bTautIsoHNum += (pINChI_Aux->nNumRemovedIsotopicH[0] + - pINChI_Aux->nNumRemovedIsotopicH[1] + - pINChI_Aux->nNumRemovedIsotopicH[2]); - bTautIsoAt += (pINChI->nNumberOfIsotopicAtoms>0 || pINChI->nNumberOfIsotopicTGroups > 0 ); - } - } - } - } - sDifSegs[DIFL_M ][DIFS_p_PROTONS] = nNumRemovedProtons? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; - sDifSegs[DIFL_MI][DIFS_h_H_ATOMS] = bHasIsoH? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; - - MarkUnusedAndEmptyLayers( sDifSegs ); - - - - bNonTautIsIdenticalToTaut = bNonTautIsIdenticalToTaut && !bTautIsoHNum; - /*********************************************************************************************/ - for ( i = 0, is = pINChISort; i < num_components; i ++, is ++ ) - { - int bCurIso, bCurStereo, bCurIsoStereo, bCurHasIsoStereo /* Fix14 */, bCurTaut /*, bCurTaut2*/; - int bCompExists, bCurIsoHPos, bCurIsoHStereo; - int bCurStereoSp2, bCurIsoStereoSp2, bCurStereoSp3, bCurIsoStereoSp3, bCurIsoStereoSp3Inv; - int bCurRacemic, bCurRelative, bCurIsoRacemic, bCurIsoRelative; - bCompExists = 0; - for ( j = TAUT_NON; j < TAUT_NUM; j ++ ) - { - switch ( bOutType ) { - case OUT_N1: /* x[TAUT_NON]: non-tautomeric only */ - jj = GET_II(bOutType,is); - if ( jj != j ) - continue; - ii = TAUT_NON; - break; - case OUT_T1: /* x[TAUT_YES]: tautomeric if present otherwise non-tautomeric */ - jj = GET_II(bOutType,is); - if ( jj != j ) - continue; - ii = TAUT_YES; - break; - case OUT_NT: /* x[TAUT_NON]: only non-taut representations of tautomeric */ - jj = GET_II(bOutType,is); - if ( jj != j ) - continue; - ii = TAUT_NON; - break; - /* main control flow comes here: requested both mobile and fixed H results */ - case OUT_TN: /* x[TAUT_YES]: tautomeric if present otherwise non-tautomeric; - * x[TAUT_NON]: non-taut only if tautomeric is present */ - jj = ( j == TAUT_YES )? GET_II(OUT_T1,is) : ( j == TAUT_NON )? GET_II(OUT_NT,is) : -1; - if ( jj < 0 ) - { - /* Fix12 */ - if ( bTautAndNonTaut && bTautIsNonTaut && - j == TAUT_NON && 0 <= (jj = GET_II(OUT_T1,is)) && - !is->pINChI[jj]->bDeleted && !is->pINChI[jj]->lenTautomer ) - { - ; /* the requested non-tautomeric component is in tautomeric position - (is->pINChI[TAUT_YES]); - process it also as non-tautomeric if Fixed-H layer was requested */ - } - else - { - continue; - } - } - ii = j; /* ii is what we wanted; jj is what we found (0 = TAUT_NON: fixed_H, 1 = TAUT_YES: mobile_H) */ - /* -- not used 2004-09-16 --- - if ( is2 ) { - jj2 = ( j == TAUT_YES )? GET_II(OUT_T1,is2) : ( j == TAUT_NON )? GET_II(OUT_NT,is2) : -1; - if ( jj2 >= 0 ) { - ii2 = j; - } else { - ii2 = -1; - } - } else { - jj2 = ii2 = -1; - } - -----------------------------*/ - break; - default: - continue; - } - if ( (pINChI = is->pINChI[jj]) && pINChI->nNumberOfAtoms > 0 ) - { - /*pINChI_Aux = is->pINChI_Aux[jj];*/ - bCompExists ++; - bCurTaut = (pINChI->lenTautomer > 0); - bCurIso = (pINChI->nNumberOfIsotopicAtoms>0 || pINChI->nNumberOfIsotopicTGroups > 0 ); - bCurIsoHPos = (pINChI->nPossibleLocationsOfIsotopicH && pINChI->nPossibleLocationsOfIsotopicH[0] > 1 || pINChI->lenTautomer > 1); - /* present isotopic H + their possible positions AND/OR isotopic atoms */ - bCurIsoHStereo = bCurIsoHPos && (bTautIsoHNum || bTautIsoAt) || bCurIso; - if ( jj == j && pINChI->bDeleted ) - { - num_comp[j] --; - if ( bCurTaut ) - { - bTautomeric |= 1; /* tautomeric representation is present */ - bNonTautomeric |= HAS_N(is); - } - bIsotopic |= bCurIso; - continue; /* deleted H(+) in tautomeric representation */ - } - bCurStereoSp2 = pINChI->Stereo && (pINChI->Stereo->nNumberOfStereoBonds > 0); - bCurHasIsoStereo = - bCurStereoSp3 = pINChI->Stereo && (pINChI->Stereo->nNumberOfStereoCenters > 0 ); - bCurIsoStereoSp2 = bCurIsoHStereo && pINChI->StereoIsotopic && (pINChI->StereoIsotopic->nNumberOfStereoBonds > 0); - bCurIsoStereoSp3 = bCurIsoHStereo && pINChI->StereoIsotopic && (pINChI->StereoIsotopic->nNumberOfStereoCenters > 0); - bCurIsoStereoSp3Inv = bCurIsoStereoSp3 && pINChI->StereoIsotopic->nCompInv2Abs; /* inversion changes sp3 stereo */ - bRequestedRacemicStereo |= (0!=(pINChI->nFlags & INCHI_FLAG_RAC_STEREO)); - - bRequestedRelativeStereo |= (0!=(pINChI->nFlags & INCHI_FLAG_REL_STEREO)); - /* check whether isotopic stereo is same as non-isotopic; if same than do not output isotopic stereo */ - if ( bCurStereoSp2 && bCurIsoStereoSp2 ) - { - bCurIsoStereoSp2 = !Eql_INChI_Stereo( pINChI->Stereo, EQL_SP2, pINChI->StereoIsotopic, EQL_SP2, 0 ); - } - if ( bCurStereoSp3 && bCurIsoStereoSp3 ) - { - /* bCurIsoStereoSp3=0 means (iso stereo sp3) = (non-iso stereo sp3) or (iso stereo sp3) = Inv(non-iso stereo sp3) */ - bCurIsoStereoSp3 = !Eql_INChI_Stereo( pINChI->Stereo, EQL_SP3, pINChI->StereoIsotopic, EQL_SP3, - (pINChI->nFlags & INCHI_FLAG_RAC_STEREO) || (pINChI->nFlags & INCHI_FLAG_REL_STEREO) ); - if ( !bCurIsoStereoSp3 ) { - /* inversion changes iso sp3 differently from non-iso sp3 Fix11 */ - bCurIsoStereoSp3Inv &= (pINChI->StereoIsotopic->nCompInv2Abs != pINChI->Stereo->nCompInv2Abs); - } - } - - bCurRelative = bRequestedRelativeStereo && bCurStereoSp3; -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - bCurRelative = bCurRelative && - (pINChI->Stereo->nNumberOfStereoCenters > 1 ) && - (pINChI->Stereo->nCompInv2Abs != 0) && -#endif - - - - bCurIsoRelative = bRequestedRelativeStereo && (bCurIsoStereoSp3 || bCurIsoStereoSp3Inv); -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - bCurIsoRelative = bCurIsoRelative && - (pINChI->StereoIsotopic->nNumberOfStereoCenters > 1 ) && - (pINChI->StereoIsotopic->nCompInv2Abs != 0) && -#endif - - - bCurRacemic = bRequestedRacemicStereo && bCurStereoSp3; -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - bCurRacemic = bCurRacemic && - (pINChI->Stereo->nCompInv2Abs != 0) && - (pINChI->Stereo->nNumberOfStereoCenters > 0 ) ? - pINChI->Stereo->nNumberOfStereoCenters : 0; -#endif - - bCurIsoRacemic = bRequestedRacemicStereo && (bCurIsoStereoSp3 || bCurIsoStereoSp3Inv); -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - bCurIsoRacemic = bCurIsoRacemic & - (pINChI->StereoIsotopic->nCompInv2Abs != 0) && - (pINChI->StereoIsotopic->nNumberOfStereoCenters > 0 ) ? - pINChI->StereoIsotopic->nNumberOfStereoCenters : 0; -#endif - if ( bRequestedRelativeStereo ) - { - bCurStereoSp3 = bCurRelative || bCurStereoSp3 && (pINChI->Stereo->nNumberOfStereoCenters > 1 ); /* Fix11 */ - bCurIsoStereoSp3 = bCurIsoRelative ? bCurIsoStereoSp3 : 0; - } - else - { - if ( bRequestedRacemicStereo ) - { - bCurStereoSp3 = bCurRacemic > 1 || bCurStereoSp3 && (pINChI->Stereo->nNumberOfStereoCenters > 1 ); /* Fix11 */ - bCurIsoStereoSp3 = bCurIsoRacemic > 1? bCurIsoStereoSp3 : 0; - } - } - bCurStereo = bCurStereoSp2 || bCurStereoSp3; - bCurIsoStereo = bCurIsoStereoSp2 || bCurIsoStereoSp3; - - bIsotopic |= bCurIso; - bHasIsotopicAtoms[ii] |= bCurIso; - bStereoSp2[ii] |= bCurStereoSp2; - bStereoSp3[ii] |= bCurStereoSp3; - bIgn_UU_Sp3[ii] |= !bCurStereoSp3 && (pINChI->nFlags & INCHI_FLAG_SC_IGN_ALL_UU); - bIgn_UU_Sp2[ii] |= !bCurStereoSp2 && (pINChI->nFlags & INCHI_FLAG_SB_IGN_ALL_UU); - bIsotopicStereoSp2[ii] |= bCurIsoStereoSp2; - bIsotopicStereoSp3[ii] |= bCurIsoStereoSp3; - bIgn_UU_Sp3_Iso[ii] |= !bCurIsoStereoSp3 && (pINChI->nFlags & INCHI_FLAG_SC_IGN_ALL_ISO_UU); - bIgn_UU_Sp2_Iso[ii] |= !bCurIsoStereoSp2 && (pINChI->nFlags & INCHI_FLAG_SB_IGN_ALL_ISO_UU); - bStereoAbs[ii] |= bCurStereoSp3 && (pINChI->Stereo->nCompInv2Abs != 0); - bStereoAbsInverted[ii] |= bCurStereoSp3 && (pINChI->Stereo->nCompInv2Abs < 0); - /* Fix08: missing isotopic inverted flag if isotopic = inverted non-isotopic */ - bIsotopicStereoAbsInverted[ii] |= bCurIsoStereoSp3 && (pINChI->StereoIsotopic->nCompInv2Abs < 0) || - !bCurIsoStereoSp3 && pINChI->StereoIsotopic && pINChI->Stereo && - pINChI->StereoIsotopic->nCompInv2Abs && - pINChI->StereoIsotopic->nCompInv2Abs != pINChI->Stereo->nCompInv2Abs; - /* Fix 11: missing /s1 if only isotopic stereo is inverted */ - bIsotopicStereoAbs[ii] |= bCurIsoStereoSp3 && (pINChI->StereoIsotopic->nCompInv2Abs != 0) || - !bCurIsoStereoSp3 && pINChI->StereoIsotopic && pINChI->Stereo && - pINChI->StereoIsotopic->nCompInv2Abs && - pINChI->StereoIsotopic->nCompInv2Abs != pINChI->Stereo->nCompInv2Abs; - - bRelativeStereo[ii] |= bCurRelative; - bIsotopicRelativeStereo[ii] |= bCurIsoRelative; - bRacemicStereo[ii] |= bCurRacemic; - bIsotopicRacemicStereo[ii] |= bCurIsoRacemic; - - bTautomericAcid |= (0!=(pINChI->nFlags & INCHI_FLAG_ACID_TAUT)); - bHardAddRemProton |= (0!=(pINChI->nFlags & INCHI_FLAG_HARD_ADD_REM_PROTON)); - if ( bCurTaut ) - { - bTautomeric |= 1; /* tautomeric representation is present */ - /* does tautomeric structure have also a non-tautomeric repesentation? */ - bNonTautomeric |= HAS_N(is); - } - - /* auxiliary info */ - if ( !(bINChIOutputOptions & INCHI_OUT_NO_AUX_INFO) && (pINChI_Aux = is->pINChI_Aux[jj]) ) - { - /* detect presence of constitutional equivalence onfo */ - int bCurEqu, bCurTautEqu=0, bCurIsoEqu=0, bCurIsoTautEqu=0; /* Fix15-disabled */ - bAtomEqu[ii] |= (bCurEqu = bHasEquString( pINChI_Aux->nConstitEquNumbers, - pINChI_Aux->nNumberOfAtoms)); - if ( bCurTaut ) - { - bTautEqu[ii] |= (bCurTautEqu = bHasEquString( pINChI_Aux->nConstitEquTGroupNumbers, - pINChI_Aux->nNumberOfTGroups)); - } - if ( bCurIso ) - { - bIsotopicAtomEqu[ii] |= (bCurIsoEqu = bHasEquString( pINChI_Aux->nConstitEquIsotopicNumbers, - pINChI_Aux->nNumberOfAtoms)) /*|| bCurEqu*/; - if ( bCurTaut ) - { - bIsotopicTautEqu[ii] |= (bCurIsoTautEqu = bHasEquString( pINChI_Aux->nConstitEquIsotopicTGroupNumbers, - pINChI_Aux->nNumberOfTGroups)) /*|| bCurTautEqu*/; - } - /* non-zero if isotopic numbering for inverted isotopic stereo is different */ - bIsotopicOrigNumb[ii] |= bCurHasIsoStereo && /* Fix14 */ - pINChI_Aux->nOrigAtNosInCanonOrdInv && - pINChI_Aux->nIsotopicOrigAtNosInCanonOrd && - (0 != memcmp( pINChI_Aux->nOrigAtNosInCanonOrdInv, - pINChI_Aux->nIsotopicOrigAtNosInCanonOrd, - sizeof(pINChI_Aux->nOrigAtNosInCanonOrdInv[0]) - * pINChI_Aux->nNumberOfAtoms)); - - } - /* inverted stereo */ - if ( bCurStereoSp3 && pINChI->Stereo->nCompInv2Abs ) - { - bInvStereo[ii] |= 1; - bInvStereoOrigNumb[ii] |= pINChI_Aux->nOrigAtNosInCanonOrd && - pINChI_Aux->nOrigAtNosInCanonOrdInv && - (0 != memcmp( pINChI_Aux->nOrigAtNosInCanonOrd, - pINChI_Aux->nOrigAtNosInCanonOrdInv, - sizeof(pINChI_Aux->nOrigAtNosInCanonOrd[0]) - * pINChI_Aux->nNumberOfAtoms)); - } - /* inverted isotopic stereo */ - if ( bCurIsoStereoSp3 && pINChI->StereoIsotopic->nCompInv2Abs ) - { - bInvIsotopicStereo[ii] |= 1; - bInvIsotopicStereoOrigNumb[ii] |= pINChI_Aux->nIsotopicOrigAtNosInCanonOrd && - pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv && - (0 != memcmp( pINChI_Aux->nIsotopicOrigAtNosInCanonOrd, - pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv, - sizeof(pINChI_Aux->nIsotopicOrigAtNosInCanonOrd[0]) - * pINChI_Aux->nNumberOfAtoms)); - } - if ( pINChI_Aux->OrigInfo && bHasOrigInfo(pINChI_Aux->OrigInfo, pINChI_Aux->nNumberOfAtoms) ) - { - bChargesRadVal[ii] |= 1; - } - } - } - } - if ( bCompExists ) - { - for ( j = TAUT_NON; j < TAUT_NUM; j ++ ) - { - num_comp[j] ++; - } - } - } - if ( bTautomeric /*&& bTautomericAcid*/ ) /* "&& bTautomericAcid" commented out 2004-06-02 */ - { - bTautomeric += bTautomericAcid; /* long-range tautomerism */ - bTautomeric += (bHardAddRemProton? 4 : 0); - } - if ( bRequestedRacemicStereo || bRequestedRelativeStereo ) - { - /* do not output inverted stereo info */ - for ( i = 0; i < TAUT_NUM; i ++ ) - { - /* Fix11 */ - bStereoAbsInverted[i] = - bStereoAbs[i] = - bInvStereo[i] = - bInvStereoOrigNumb[i] = 0; - /* bIsotopicRelativeStereo[i]=0 may happen because iso stereo is same or inverted non-iso stereo */ - bIsotopicStereoAbsInverted[i] = - bIsotopicStereoAbs[i] = - bInvIsotopicStereo[i] = - bInvIsotopicStereoOrigNumb[i] = 0; - /* -- commented out: Fix11-- - if ( bRacemicStereo[i] || bRelativeStereo[i] ) - { - bStereoAbsInverted[i] = - bStereoAbs[i] = - bInvStereo[i] = - bInvStereoOrigNumb[i] = 0; - } - if ( bIsotopicRacemicStereo[i] || bIsotopicRelativeStereo[i] ) - { - bIsotopicStereoAbsInverted[i] = - bIsotopicStereoAbs[i] = - bInvIsotopicStereo[i] = - bInvIsotopicStereoOrigNumb[i] = 0; - } - */ - } - } - - - iCurTautMode = bOutType == OUT_N1? TAUT_NON: /* only non-taut */ - bOutType == OUT_T1? TAUT_YES: /* tautomeric if present, otherwise non-tautomeric */ - bOutType == OUT_NT? TAUT_NON: /* only non-taut representations of tautomeric */ - bOutType == OUT_TN? TAUT_YES: /* tautomeric if present otherwise non-tautomeric; */ - -1; /* separately output non-taut representations of tautomeric if present */ - - if ( iCurTautMode < 0 ) - { - return 0; /* error */ - } - - if ( bXml ) - { - ind += inc* (1+iINChI); - } - - bOverflow = 0; - - num_components = num_comp[iCurTautMode]; - - max_num_comp = inchi_max(num_comp[TAUT_NON], num_comp[TAUT_YES]); - - if ( bINChIOutputOptions & INCHI_OUT_ONLY_AUX_INFO ) - { - goto output_aux_info; - } - - nCurINChISegment = DIFL_M; - - /****************************************** - * - * Structure (Compound) Header - * - ******************************************/ - if ( bXml ) - { - /* -- moved to the line above goto output_aux_info; - ind += inc* (1+iINChI); - */ - /* basic title, version */ - if ( INCHI_BAS == iINChI ) - { - inchi_ios_print( output_file, "\n" ); /* empty line */ - } - tot_len = sprintf(pStr, "%s<%s %s=\"%s\"", - SP(ind), x_basic, x_ver, x_curr_ver); - if ( INCHI_REC == iINChI || INCHI_BAS == iINChI && bDisconnectedCoord ) - { - tot_len += sprintf(pStr+tot_len, " %s=\"%d\"", x_reconnected, iINChI ); - } - if ( bAbcNumbers || bCtPredecessors ) - { - const char *pNumber = ""; - const char *pDelim = ""; - const char *pCtType = ""; - if ( bAbcNumbers && bCtPredecessors ) - { - pNumber = x_type_short; - } - else - { - pNumber = bAbcNumbers? x_type_alpha : x_type_numer; - pDelim = (bAbcNumbers && bCtPredecessors)? "-":""; - pCtType = bCtPredecessors? x_type_predec:""; - } - /* type */ - tot_len += sprintf(pStr+tot_len, " %s=\"%s%s%s\"", x_type, pNumber, pDelim, pCtType); - } - sprintf(pStr+tot_len,">"); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - } - else - if ( INCHI_BAS == iINChI ) - { - /* eliminate empty line in plain text output */ - if ( bNoStructLabels ) - { - ; -/* -- removed empty line before InChI --- -#ifndef TARGET_API_LIB - inchi_ios_print( output_file, "\n" ); -#else - ; -#endif -*/ - } - else - { - if ( !(szSdfLabel && szSdfLabel[0]) && !(szSdfValue && szSdfValue[0]) ) - { - tot_len = sprintf( pStr, "%sStructure: %d", pLF, num_input_struct ); - inchi_ios_print( output_file, "%s%s", pStr, pTAB ); - } - else - { - tot_len = sprintf( pStr, "%sStructure: %d.%s%s%s%s", - pLF, - num_input_struct, - SDF_LBL_VAL(szSdfLabel, szSdfValue) ); - if ( lSdfId ) - { - tot_len --; - tot_len += sprintf( pStr + tot_len, ":%ld", lSdfId ); - } - inchi_ios_print( output_file, "%s%s", pStr, pTAB ); - } - } - /* inchi_ios_print( output_file, "%s%s=%s", pLF, (FLAG_SORT_PRINT_ReChI_PREFIX & *pSortPrintINChIFlags)? INCHI_REC_NAME : INCHI_NAME, pLF ); */ - inchi_ios_print( output_file, "%s%s=%s", pLF, INCHI_NAME, pLF ); - } - - - /***************************************************** - * - * version (10-29-2003) - * - ****************************************************/ - if ( INCHI_BAS == iINChI || !(bINChIOutputOptions & INCHI_OUT_EMBED_REC) /* || !bXml */) - { - /* xml: only if the first or not embedded; plain: always */ - szGetTag( IdentLbl, nTag, bTag1 = IL_VERS, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - tot_len += sprintf(pStr + tot_len, "%s", x_curr_ver); - - /* 10-17-2008 Add 'standard' flag if necessary */ - if ( bINChIOutputOptions & INCHI_OUT_STDINCHI ) - tot_len += sprintf(pStr + tot_len, "S"); - - /*if ( bXml ) {*/ /* avoid leading slash in plain output */ - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - /*}*/ - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - - - /***************************************************** - * - * atoms, connection tables and tautomeric info - * - ****************************************************/ - /******************* constitution: dot-disconnected Hill formulas: */ - if ( num_components2[0] || num_components2[1] ) - { - szGetTag( IdentLbl, nTag, bTag1 = INCHI_REC == iINChI? IL_REC_ : IL_FML_, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - tot_len = str_HillFormula(pINChISort, pStr, nStrLen, tot_len, - &bOverflow, bOutType, num_components, bUseMulipliers); - - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, 1 ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - /**************** semicolon/dot-disconnected connection tables */ - szGetTag( IdentLbl, nTag, bTag1 = IL_CONN, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - tot_len2 = str_Connections(pINChISort, pStr, nStrLen, tot_len, - &bOverflow, bOutType, ATOM_MODE, num_components, bUseMulipliers); - /* current version does not output empty (";;;;") connectivity */ - if ( tot_len != tot_len2 /*|| !bXml*/ ) { /* 2004-06-30: never output empty connection table */ - tot_len = tot_len2; - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -2, bPlainTextTags ) ) - goto exit_function; /* pStr overfow */ - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - /************** hydrogen atoms; do not output empty */ - if ( INCHI_SEGM_FILL == INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_h_H_ATOMS] ) ) - { - szGetTag( IdentLbl, nTag, bTag1 = IL_ALLH, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - tot_len2 = str_H_atoms(pINChISort, pStr, nStrLen, tot_len, - &bOverflow, bOutType, ATOM_MODE, TAUT_MODE, - num_components, bUseMulipliers); - if ( tot_len != tot_len2 /*|| !bXml*/ ) { /* 2004-06-21: never output empty */ - tot_len = tot_len2; - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -2, 1 ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - } - - - bFhTag = 0; - - -repeat_INChI_output: - - /***************************************************** - * charge - */ - - nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_q_CHARGE] ); - if ( nSegmAction ) - { - szGetTag( IdentLbl, nTag, bTag1 = IL_CHRG | bFhTag, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - if ( INCHI_SEGM_FILL == nSegmAction ) - { - tot_len = str_Charge2(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, num_components, - bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); - bNonTautNonIsoIdentifierNotEmpty += bSecondNonTautPass; - } - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); - } - } - - /***************************************************** - * removed protons - */ - if ( iCurTautMode == TAUT_YES && !bSecondNonTautPass ) - { - - nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_p_PROTONS] ); - if ( nSegmAction ) - { - szGetTag( IdentLbl, nTag, bTag1 = IL_PROT | bFhTag, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - tot_len += sprintf( pStr + tot_len, "%+d", nNumRemovedProtons ); - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); - } - } - - } - - - /************************************************** - * - * non-isotopic stereo - */ - - { - int i; - i = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_t_SATOMS] ); - i = i; - } - - if ( INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_b_SBONDS] ) || - INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_t_SATOMS] ) || - INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_m_SP3INV] ) || - INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_s_STYPE] ) ) - { - /* stereo */ - szGetTag( IdentLbl, nTag, bTag1 = IL_STER | bFhTag, szTag1, &bAlways ); - if ( bXml ) - { - str_LineStart( szTag1, NULL, 0, pStr, ind ); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - } - - /* sp2 */ - /*if ( bStereoSp2[iCurTautMode] )*/ - if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_b_SBONDS] ) ) - { - szGetTag( IdentLbl, nTag, bTag2 = bTag1 | IL_DBND, szTag2, &bAlways ); - tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); - if ( INCHI_SEGM_FILL == nSegmAction ) - { - tot_len = str_Sp2(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, - bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); - bNonTautNonIsoIdentifierNotEmpty += bSecondNonTautPass; - } - if ( str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); /* sp2 */ - } - } - - /* sp3 */ - /*if ( bStereoSp3[iCurTautMode] )*/ - if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_t_SATOMS] ) ) - { - bRelRac = bRelativeStereo[iCurTautMode] || bRacemicStereo[iCurTautMode]; - szGetTag( IdentLbl, nTag, bTag2 = bTag1 | IL_SP3S, szTag2, &bAlways ); - tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); - if ( INCHI_SEGM_FILL == nSegmAction ) { - tot_len = str_Sp3(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, bRelRac, - bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); - bNonTautNonIsoIdentifierNotEmpty += bSecondNonTautPass; - } - - if (str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags )) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); /* sp3 */ - } - } - - /* bStereoAbsInverted[iCurTautMode] */ - - /* if ( bStereoAbs[iCurTautMode] ) */ - if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_m_SP3INV] ) ) - { - szGetTag( IdentLbl, nTag, bTag2 = bTag1 | IL_INVS, szTag2, &bAlways ); - tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); - if ( INCHI_SEGM_FILL == nSegmAction ) { - tot_len = str_StereoAbsInv(pINChISort, pStr, nStrLen, tot_len, - &bOverflow, bOutType, num_components); - bNonTautNonIsoIdentifierNotEmpty += bSecondNonTautPass; - } - - if (str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags )) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); /* stereo-abs-inv */ - } - } - - /* stereo type */ - /*if ( bRacemicStereo[iCurTautMode] || bRelativeStereo[iCurTautMode] || bStereoAbs[iCurTautMode] )*/ - if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_s_STYPE] ) ) - { - const char *p_stereo = bRelativeStereo[iCurTautMode]? x_rel : - bRacemicStereo[iCurTautMode] ? x_rac : x_abs; - szGetTag( IdentLbl, nTag, bTag2 = bTag1 | IL_TYPS, szTag2, &bAlways ); - tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); - if ( INCHI_SEGM_FILL == nSegmAction ) { - tot_len += MakeDelim( p_stereo, pStr + tot_len, nStrLen-tot_len, &bOverflow); - bNonTautNonIsoIdentifierNotEmpty += bSecondNonTautPass; - } - if (str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags )) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); /* no abs, inv or racemic stereo */ - } - - if ( bXml ) - { - /* close stereo */ - ind -= inc; - if ( str_LineEnd( szTag1, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "////" ); /* sp3, sp2, abs-inv, stereo.type */ - } - } - - - /**************************************************** - * - * Isotopic canonical results - * - ****************************************************/ - nCurINChISegment ++; /* switch from M to MI or from F to FI */ - - /*if ( bIsotopic || !bSecondNonTautPass && bHasIsoH )*/ - if ( INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_i_IATOMS] ) ) - { - /* isotopic #1: composition -- atoms -- do not output in xml if empty */ - szGetTag( IdentLbl, nTag, bTag1 = IL_ISOT | bFhTag, szTag1, &bAlways ); - if ( bXml ) - { - str_LineStart( szTag1, NULL, 0, pStr, ind ); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - } - /* isotopic atoms without mobile H. - * Fixed 2004-06-15: always output if not bXml. Note: - * Previous condition if( bHasIsotopicAtoms[iCurTautMode] || bIsotopic && !bXml) - * did not optput /i in case of only mobile isotopic H - */ - if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_i_IATOMS] ) ) - { - szGetTag( IdentLbl, nTag, bTag2 = bTag1 | IL_ATMS, szTag2, &bAlways ); - tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); - /*if ( bHasIsotopicAtoms[iCurTautMode] )*/ - if ( INCHI_SEGM_FILL == nSegmAction ) - { - tot_len2 = str_IsoAtoms(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, bAbcNumbers, - bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); - bNonTautIsoIdentifierNotEmpty += bSecondNonTautPass; - } - else - { - tot_len2 = tot_len; - } - - tot_len = tot_len2; - if ( str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - - } - - /* isotopic #1a: composition -- exchangeable isotopic H (mobile H only) */ - /*if ( !bSecondNonTautPass && bHasIsoH )*/ - if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_h_H_ATOMS] ) ) - { - szGetTag( IdentLbl, nTag, bTag2 = bTag1 | IL_XCGA, szTag2, &bAlways ); - tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); - tot_len += MakeIsoHString( num_iso_H, pStr + tot_len, nStrLen-tot_len, TAUT_MODE, &bOverflow); - bNonTautIsoIdentifierNotEmpty += bSecondNonTautPass; - if ( str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - - /*************************************************** - * - * Isotopic stereo - * - ***************************************************/ - - /*if ( bIsotopicStereo[iCurTautMode] )*/ - if ( INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_b_SBONDS] ) || - INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_t_SATOMS] ) || - INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_m_SP3INV] ) || - INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_s_STYPE] ) ) - { - /* stereo */ - szGetTag( IdentLbl, nTag, bTag2 = bTag1 | IL_STER, szTag2, &bAlways ); - if ( bXml ) - { - str_LineStart( szTag2, NULL, 0, pStr, ind ); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - } - - /************************ - isotopic #2: sp2 - ************************/ - /*if ( bIsotopicStereoSp2[iCurTautMode] )*/ - if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_b_SBONDS] ) ) - { - szGetTag( IdentLbl, nTag, bTag3 = bTag2 | IL_DBND, szTag3, &bAlways ); - tot_len = str_LineStart( szTag3, NULL, 0, pStr, ind ); - if ( INCHI_SEGM_FILL == nSegmAction ) { - tot_len = str_IsoSp2(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, - bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); - bNonTautIsoIdentifierNotEmpty += bSecondNonTautPass; - } - if ( str_LineEnd( szTag3, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); /* iso sp2 */ - } - } - - /************************ - isotopic #3: sp3 - ************************/ - /*if ( bIsotopicStereoSp3[iCurTautMode] )*/ - if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_t_SATOMS] ) ) - { - bRelRac = bIsotopicRelativeStereo[iCurTautMode] || bIsotopicRacemicStereo[iCurTautMode]; - szGetTag( IdentLbl, nTag, bTag3 = bTag2 | IL_SP3S, szTag3, &bAlways ); - tot_len = str_LineStart( szTag3, NULL, 0, pStr, ind ); - if ( INCHI_SEGM_FILL == nSegmAction ) { - tot_len = str_IsoSp3(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, bRelRac, - bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); - bNonTautIsoIdentifierNotEmpty += bSecondNonTautPass; - } - if ( str_LineEnd( szTag3, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "/" ); /* iso-sp3 */ - } - } - - /* isotopic #4: abs inverted */ - if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_m_SP3INV] ) ) - { - szGetTag( IdentLbl, nTag, bTag3 = bTag2 | IL_INVS, szTag3, &bAlways ); - tot_len = str_LineStart( szTag3, NULL, 0, pStr, ind ); - if ( INCHI_SEGM_FILL == nSegmAction ) { - tot_len = str_IsoStereoAbsInv(pINChISort, pStr, nStrLen, tot_len, - &bOverflow, bOutType, num_components); - bNonTautIsoIdentifierNotEmpty += bSecondNonTautPass; - } - if ( str_LineEnd( szTag3, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "/" ); - } - } - - /* isotopic #5: stereo type. Do not output if it has already been output in non-iso */ - if ( nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_s_STYPE] ) ) - { - const char *p_stereo = bIsotopicRelativeStereo[iCurTautMode]? x_rel : - bIsotopicRacemicStereo[iCurTautMode] ? x_rac : x_abs; - szGetTag( IdentLbl, nTag, bTag3 = bTag2 | IL_TYPS, szTag3, &bAlways ); - tot_len = str_LineStart( szTag3, NULL, 0, pStr, ind ); - if ( INCHI_SEGM_FILL == nSegmAction ) { - tot_len += MakeDelim( p_stereo, pStr + tot_len, nStrLen-tot_len, &bOverflow); - bNonTautIsoIdentifierNotEmpty += bSecondNonTautPass; - } - if ( str_LineEnd( szTag3, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "/" ); /* no abs, inv or racemic stereo */ - } - if ( bXml ) - { - /************************ - close isotopic stereo - ************************/ - ind -= inc; - if ( str_LineEnd( szTag2, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - } - else - { - if ( !bXml ) - { - /* no isotopic stereo */ - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "////" ); /* sp3, sp2, abs-inv, stereo.type */ - } - } - - /* close isotopic */ - if ( bXml ) - { - ind -= inc; - if ( str_LineEnd( szTag1, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "///" ); /* isotopic composition, sp2, sp3 */ - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "//" ); /* inv or racemic stereo */ - } - } - -#if ( CANON_FIXH_TRANS == 1 ) - if ( bOutType == OUT_NONTAUT && bOutputType == OUT_TN && bSecondNonTautPass && - INCHI_SEGM_FILL == INChI_SegmentAction( sDifSegs[DIFL_F][DIFS_o_TRANSP] )) - { - /* find and print non-tautomeric components transposition, if non-trivial */ - AT_NUMB *nTrans_n, *nTrans_s; - - if ( 0 < bin_AuxTautTrans(pINChISort, pINChISort2, &nTrans_n, &nTrans_s, bOutType, num_components) ) - { - /* a non-trivial transposition does exist; output start tag */ - szGetTag( IdentLbl, nTag, bTag1 = IL_TRNS | bFhTag, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - /* print the transposition, cycle after cycle */ - tot_len = str_AuxTautTrans(nTrans_n, nTrans_s, pStr, nStrLen, tot_len, - &bOverflow, TAUT_MODE, num_components); - bNonTautIsoIdentifierNotEmpty += bSecondNonTautPass; - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - /* detected transposition */ - *pSortPrintINChIFlags |= (INCHI_BAS == iINChI)? FLAG_SORT_PRINT_TRANSPOS_BAS : - FLAG_SORT_PRINT_TRANSPOS_REC; - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "/" ); - } - } - } -#endif - - - /************************************************************** - At this point the INChI part of the output has been done. - If this INChI is tautomeric and non-tautomeric results exist - then we need to output non-tautomeric data: - fixed H, - stereo, - isotopic - isotopic stereo - ***************************************************************/ - if ( bOutType == OUT_TN && !bSecondNonTautPass && - bNonTautIsIdenticalToTaut && bTautomeric && bNonTautomeric ) - { - /* Fixed-H layer is empty in the Identifier */ - *pSortPrintINChIFlags |= (INCHI_BAS == iINChI)? FLAG_SORT_PRINT_NO_NFIX_H_BAS : - FLAG_SORT_PRINT_NO_NFIX_H_REC; - *pSortPrintINChIFlags |= (INCHI_BAS == iINChI)? FLAG_SORT_PRINT_NO_IFIX_H_BAS : - FLAG_SORT_PRINT_NO_IFIX_H_REC; - } - - if ( bOutType == OUT_TN && !bNonTautIsIdenticalToTaut /* added 2004-10-04 Fix16 */ -#ifdef OLD_ITEM_DISCOVERY - && bTautomeric && bNonTautomeric -#endif - && INChI_SegmentAction( sDifSegs[DIFL_F][DIFS_f_FORMULA] ) - /* special case: removed isolated H(+): */ - /* || iCurTautMode == TAUT_YES && num_comp[TAUT_YES] < num_comp[TAUT_NON] && - 0 < num_comp[TAUT_NON]*/ - ) - { - /* add the second (non-tautomeric) output */ - bOutType = OUT_NONTAUT; /* pick up only non-tautomeric representation of tautomeric */ - iCurTautMode = TAUT_NON; - pINChISort = pINChISortTautAndNonTaut[TAUT_NON]; - bSecondNonTautPass = 1; - nCurINChISegment = DIFL_F; - num_components = num_comp[iCurTautMode]; /* number of components could change due to removal of isolated H(+) from tautomeric */ - bFhTag = IL_FIXH; - szGetTag( IdentLbl, nTag, bTag1 = bFhTag, szTag1, &bAlways ); - if ( bXml ) - { - /* open non-tautomeric */ - str_LineStart( szTag1, NULL, 0, pStr, ind ); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - } - /***** constitution non-taut: dot-disconnected Hill formulas: -- only if different */ - szGetTag( IdentLbl, nTag, bTag1 = IL_FMLF | bFhTag, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_f_FORMULA] ); - if ( INCHI_SEGM_FILL == nSegmAction ) - { - tot_len2 = str_HillFormula2(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, num_components, bUseMulipliers); - bNonTautNonIsoIdentifierNotEmpty += bSecondNonTautPass; - } - else - { - tot_len2 = tot_len; - } - tot_len = tot_len2; - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - - nSegmAction = INChI_SegmentAction( sDifSegs[nCurINChISegment][DIFS_h_H_ATOMS] ); - if ( INCHI_SEGM_FILL == nSegmAction ) - { - szGetTag( IdentLbl, nTag, bTag1 = IL_HFIX | bFhTag, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); /* open H-fixed */ - /* output the second non-tautomeric item: fixed H -- do not output in xml if empty */ - tot_len2 = str_FixedH_atoms(pINChISort, pStr, nStrLen, tot_len, - &bOverflow, bOutType, ATOM_MODE, num_components, bUseMulipliers); - tot_len = tot_len2; - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -nSegmAction, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - bNonTautNonIsoIdentifierNotEmpty += bSecondNonTautPass; - } - goto repeat_INChI_output; - } - else - { - if ( bOutType == OUT_NONTAUT && bOutputType == OUT_TN && bSecondNonTautPass /* && bTautomeric && bNonTautomeric*/ ) - { - /* the second (non-taut) output has been done; restore variables */ - bOutType = OUT_TN; - iCurTautMode = TAUT_YES; - pINChISort = pINChISortTautAndNonTaut[TAUT_YES]; - bSecondNonTautPass = 0; - num_components = num_comp[iCurTautMode]; - if ( !bNonTautNonIsoIdentifierNotEmpty ) - { - /* Fixed-H layer is empty in the Identifier */ - *pSortPrintINChIFlags |= (INCHI_BAS == iINChI)? FLAG_SORT_PRINT_NO_NFIX_H_BAS : - FLAG_SORT_PRINT_NO_NFIX_H_REC; - } - if ( !bNonTautIsoIdentifierNotEmpty ) - { - /* Fixed-H layer is empty in the Identifier */ - *pSortPrintINChIFlags |= (INCHI_BAS == iINChI)? FLAG_SORT_PRINT_NO_IFIX_H_BAS : - FLAG_SORT_PRINT_NO_IFIX_H_REC; - } - if ( bXml ) - { - /* close non-tautomeric */ - ind -= inc; - szGetTag( IdentLbl, nTag, bTag1 = bFhTag, szTag1, &bAlways ); - if ( str_LineEnd( szTag1, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - bFhTag = 0; - } - } - - - /************************************************ - * output INChI of the reconnected structure * - ************************************************/ - bEmbeddedOutputCalled = 0; - if ( bDisconnectedCoord && INCHI_BAS == iINChI && - (bINChIOutputOptions & INCHI_OUT_EMBED_REC) && num_components2[INCHI_REC] ) - { - int nRet; - bEmbeddedOutputCalled = 1; - - if ( !bXml ) - { - /* output blank line before /R: in case of bPlainTextCommnts=1 */ - inchi_ios_print( output_file, "%s", pLF ); - } - /* end of disconnected INChI output */ - - nRet = OutputINChI1( pStr, nStrLen, - pINChISortTautAndNonTaut2, - INCHI_REC, NULL, - 0 /*bDisconnectedCoord*/, bOutputType, - bINChIOutputOptions | INCHI_OUT_NO_AUX_INFO, - bXml, bAbcNumbers, bCtPredecessors, bNoStructLabels, - num_components2, num_non_taut2, num_taut2, - output_file, log_file, - num_input_struct, - szSdfLabel, szSdfValue, lSdfId, - pSortPrintINChIFlags, - save_opt_bits); - if ( !nRet ) - goto exit_function; /* error */ - } - - if ( bXml ) - { - /* close INChI identifier (basic) */ - ind -= inc; - if ( str_LineEnd( x_basic, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - else - { - /* save InChI creation options if requested ...*/ - if ( !bEmbeddedOutputCalled) - { - if ( bINChIOutputOptions & INCHI_OUT_SAVEOPT ) - { - /* ... and not std-InChI output */ - if ( 0 == (bINChIOutputOptions & INCHI_OUT_STDINCHI) ) - { - char let1, let2; - GetSaveOptLetters(save_opt_bits, &let1, &let2); - inchi_ios_print( output_file, "\\%c%c", let1, let2 ); - } - } - } - - if ( !bEmbeddedOutputCalled && !bPlainTextCommnts ) - { /* plain text comment earlier ended with LF */ - inchi_ios_print( output_file, "%s%s", - (!num_components2[0] && !num_components2[1])? "//":"", /* empty InChI=// */ - (bINChIOutputOptions & INCHI_OUT_NO_AUX_INFO)? "\n" : pTAB ); - /* end of INChI= output */ - } - - - } - -output_aux_info: - - bFhTag = 0; - - if( !(bINChIOutputOptions & INCHI_OUT_NO_AUX_INFO) ) - { - /* output aux info */ - - /************************************************************* - * - * Aux info non-isotopic - * - *************************************************************/ - - num_components = num_comp[iCurTautMode]; - if ( bXml ) - { - /* aux. info header */ - /* empty line if INChI output has been printed */ - if ( !(bINChIOutputOptions & INCHI_OUT_ONLY_AUX_INFO) ) - { - inchi_ios_print( output_file, "\n" ); - } - /* basic.aux-info title, version */ - tot_len = sprintf(pStr, "%s<%s %s=\"%s\"", - SP(ind), x_aux_basic, x_ver, x_curr_ver ); - if ( INCHI_REC == iINChI || INCHI_BAS == iINChI && bDisconnectedCoord ) - { - tot_len += sprintf(pStr+tot_len, " %s=\"%d\"", x_reconnected, iINChI ); - } - if ( bAbcNumbers ) - { - /* type */ - const char *pNumber = x_type_short; - tot_len += sprintf(pStr+tot_len, " %s=\"%s\"", x_type, pNumber); - } - - sprintf(pStr+tot_len,">"); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - if ( !(bINChIOutputOptions & INCHI_OUT_ONLY_AUX_INFO) ) - { - /* comment */ - tot_len = sprintf( pStr, "%s<%s>", SP(ind), x_aux_comm ); - inchi_ios_print( output_file, "%s\n", pStr ); - } - } - else - { - if ( INCHI_BAS == iINChI ) - { - tot_len = sprintf( pStr, "AuxInfo=" ); /* in wINChI window, separate INChI: from AuxInfo: with blank line */ - inchi_ios_print( output_file, "%s%s%s", - /* blank line before AuxInfo in winchi window unless it is an annotation */ - (bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW) ? "\n":"", - pStr, - pLF); - szGetTag( AuxLbl, nTag, bTag1 = AL_VERS, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - tot_len += sprintf(pStr + tot_len, "%s", x_curr_ver); - /* avoid leading slash in plain output */ - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( INCHI_REC == iINChI ) - { - szGetTag( AuxLbl, nTag, bTag1 = AL_REC_, szTag1, &bAlways ); - inchi_ios_print( output_file, "%s%s", szTag1, pLF ); - } - } - - } - /* normalization type */ - if ( num_components2[0] || num_components2[1] ) - { - szGetTag( AuxLbl, nTag, bTag1 = AL_NORM, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - tot_len += sprintf( pStr + tot_len, "%d", (bTautomeric && bTautomericOutputAllowed)? bTautomeric : 0); - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - - - -repeat_INChI_Aux_output: - - /************************************************************** - * Original atom numbers in order of canonical numbers - **************************************************************/ - if ( num_components2[0] || num_components2[1] ) - { - szGetTag( AuxLbl, nTag, bTag1 = (bSecondNonTautPass? AL_FIXN : AL_ANBR) | bFhTag, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - /* original numbering output */ - tot_len = str_AuxNumb(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, - bSecondNonTautPass, bOmitRepetitions); - - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - /********************************************** - * Symmetry numbers (constit. equivalence) - **********************************************/ - if ( bAtomEqu[iCurTautMode] ) - { - /* aux equ atoms */ - /* 1. Compare to tautomeric equivalence (in case of second, non-taut, pass only) */ - /* 2. Compare to the previous component if (1) failed to find equivalence */ - szGetTag( AuxLbl, nTag, bTag1 = AL_AEQU | bFhTag, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - tot_len = str_AuxEqu(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, - bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); - - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "/" ); - } - } - /***************************************************** - * Tautomeric groups equivalence - *****************************************************/ - if ( bTautomericOutputAllowed && bTautomeric && bTautEqu[iCurTautMode] && !bSecondNonTautPass ) - { - /***************************************************** - * Tautomeric groups constitutional equivalence - */ - /* aux tgroup equ */ - szGetTag( AuxLbl, nTag, bTag1 = AL_GEQU | bFhTag, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - tot_len = str_AuxTgroupEqu(pINChISort, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, - num_components, bUseMulipliers); - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - else - { - if ( !bXml && bTautomericOutputAllowed && bTautomeric ) - { - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "/" ); - } - } - - /**************************************************** - * Inverted stereo -- sp3 only + canonical numbering - ****************************************************/ - if ( bInvStereo[iCurTautMode] ) - { - szGetTag( AuxLbl, nTag, bTag1 = AL_STER | bFhTag, szTag1, &bAlways ); - if ( bXml ) - { - /*************************** - inv stereo start tag - ****************************/ - str_LineStart( szTag1, NULL, 0, pStr, ind ); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - } - /**************************** - inverted sp3 start tag - *****************************/ - szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_SP3I, szTag2, &bAlways ); - tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); - tot_len = str_AuxInvSp3(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, - bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); - if ( str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - - /************************************* - inverted sp3 canonical numbering - **************************************/ - if ( bInvStereoOrigNumb[iCurTautMode] ) - { - szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_SP3N, szTag2, &bAlways ); - tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); - tot_len = str_AuxInvSp3Numb(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, - bSecondNonTautPass, bOmitRepetitions); - if ( str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); - } - } - - if ( bXml ) - { - /* close sp3 inv */ - ind -= inc; - if ( str_LineEnd( szTag1, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "//" ); - } /* Inverted stereo -- sp3 only + canonical numbering */ - } - - - /* omitted undefined/unknown non-isotopic stereo */ - if ( bXml ) - { - if ( bIgn_UU_Sp2[iCurTautMode] || bIgn_UU_Sp3[iCurTautMode] ) - { - /* */ - szGetTag( IdentLbl, nTag, bTag1 = IL_STER, szTag1, &bAlways ); - tot_len = PrintXmlStartTag( pStr, ind, 3, szTag1, - (bIgn_UU_Sp2[iCurTautMode])? x_ign_uu_sp2 : NULL, 1, - (bIgn_UU_Sp3[iCurTautMode])? x_ign_uu_sp3 : NULL, 1, - NULL, 0, NULL, 0, NULL, 0, NULL, 0 ); - inchi_ios_print( output_file, "%s\n", pStr ); - } - } - - /*************************************************************** - * - * Additional information: charges, radicals, - * special valences, coordinates - * - ***************************************************************/ - /************************************************************** - * - * Aux info isotopic - * - **************************************************************/ -repeat_INChI_Aux_Iso_output: - /* if InChI Fixed-H isotopic is empty then do not output corresponding AuxInfo */ - i = bSecondNonTautPass && - (*pSortPrintINChIFlags & ((INCHI_BAS == iINChI)? FLAG_SORT_PRINT_NO_IFIX_H_BAS : - FLAG_SORT_PRINT_NO_IFIX_H_REC )); - - if ( bIsotopic && !i && - (bIsotopicOrigNumb[iCurTautMode] || - bIsotopicAtomEqu[iCurTautMode] || - bTautomericOutputAllowed && bTautomeric && bIsotopicTautEqu[iCurTautMode] || - bInvIsotopicStereo[iCurTautMode] || - bXml && ( bIgn_UU_Sp3_Iso[iCurTautMode] || bIgn_UU_Sp2_Iso[iCurTautMode] ) ) ) - { - /*************************************/ - /* isotopic aux info header */ - /*************************************/ - szGetTag( AuxLbl, nTag, bTag1 = AL_ISOT | bFhTag, szTag1, &bAlways ); - if ( bXml ) - { - str_LineStart( szTag1, NULL, 0, pStr, ind ); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - } - else - { - pStr[tot_len = 0] = '\0'; - } - /***************************************************************** - * Original atom numbers in order of isotopic canonical numbers - *****************************************************************/ - szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_ISON, szTag2, &bAlways ); - if ( bIsotopicOrigNumb[iCurTautMode] ) - { - tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); - tot_len = str_AuxIsoNumb(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, - bSecondNonTautPass, bOmitRepetitions); - if ( str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( !bXml ) - { - /*if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" );*/ - inchi_ios_print( output_file, "%s%s", szTag2, pLF ); /* mark isotopic output */ - } - } - - /*************************/ - /* Isotopic symmetry */ - /*************************/ - if ( bIsotopicAtomEqu[iCurTautMode] ) - { - /* atoms */ - szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_AEQU, szTag2, &bAlways ); - tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); - tot_len = str_AuxIsoEqu(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, - bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); - if ( str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -2/*was -1: Fix15*/, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "/" ); - } - } - - /********************************/ - /* Tautomeric groups, isotopic */ - /********************************/ - if ( bTautomericOutputAllowed && bTautomeric && bIsotopicTautEqu[iCurTautMode] ) - { - /********************************************/ - /* Isotopic tautomeric groups equivalence */ - /********************************************/ - szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_GEQU, szTag2, &bAlways ); - tot_len = str_LineStart( szTag2, NULL, 0, pStr, ind ); - tot_len = str_AuxIsoTgroupEqu(pINChISort, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, - bOmitRepetitions, bUseMulipliers); - if ( str_LineEnd( szTag2, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -2/*was -1: Fix15*/, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( !bXml && bTautomericOutputAllowed && bTautomeric ) - { - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "/" ); - } - } - - /************************************* - * Isotopic inverted stereo - *************************************/ - if ( bInvIsotopicStereo[iCurTautMode] ) - { - szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_STER, szTag2, &bAlways ); - if ( bXml ) - { - /************************************ - inv isotopic stereo start tag - *************************************/ - str_LineStart( szTag2, NULL, 0, pStr, ind ); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - } - /************************************* - inverted isotopic sp3 start tag - **************************************/ - szGetTag( AuxLbl, nTag, bTag3 = bTag2 | AL_SP3I, szTag3, &bAlways ); - tot_len = str_LineStart( szTag3, NULL, 0, pStr, ind ); - tot_len = str_AuxInvIsoSp3(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, - bSecondNonTautPass, bOmitRepetitions, bUseMulipliers); - if ( str_LineEnd( szTag3, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - /********************************************* - inverted isotopic sp3 canonical numbering - **********************************************/ - if ( bInvIsotopicStereoOrigNumb[iCurTautMode] ) - { - szGetTag( AuxLbl, nTag, bTag3 = bTag2 | AL_SP3N, szTag3, &bAlways ); - tot_len = str_LineStart( szTag3, NULL, 0, pStr, ind ); - tot_len = str_AuxInvIsoSp3Numb(pINChISort, pINChISort2, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, num_components, - bSecondNonTautPass, bOmitRepetitions); - if ( str_LineEnd( szTag3, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "/" ); - } - } - if ( bXml ) - { - /* close sp3 inv */ - ind -= inc; - if ( str_LineEnd( szTag2, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - } - else - { - if ( !bXml ) - { - if ( bPlainTextTags == 1 ) - inchi_ios_print( output_file, "//" ); - } - } - - /* totally omitted undefined/unknown isotopic stereo */ - if ( bXml ) - { - if ( bIgn_UU_Sp3_Iso[iCurTautMode] || bIgn_UU_Sp2_Iso[iCurTautMode] ) - { - /* */ - szGetTag( IdentLbl, nTag, bTag1 = IL_STER, szTag1, &bAlways ); - tot_len = PrintXmlStartTag( pStr, ind, 3, szTag1, - (bIgn_UU_Sp2_Iso[iCurTautMode])? x_ign_uu_sp2 : NULL, 1, - (bIgn_UU_Sp3_Iso[iCurTautMode])? x_ign_uu_sp3 : NULL, 1, - NULL, 0, NULL, 0, NULL, 0, NULL, 0 ); - inchi_ios_print( output_file, "%s\n", pStr ); - } - } - - - if ( bXml ) - { - /***************** close isotopic ***********************/ - ind -= inc; - if ( str_LineEnd( szTag1, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - } /* Aux info isotopic */ - - -#if ( CANON_FIXH_TRANS != 1 ) - if ( bSecondNonTautPass ) - { - /* find and print non-tautomeric components transposition, if non-trivial */ - AT_NUMB *nTrans_n, *nTrans_s; - if ( 0 < bin_AuxTautTrans(pINChISort, pINChISort2, &nTrans_n, &nTrans_s, bOutType, num_components) ) { - /* a non-trivial transposition does exist; output start tag */ - tot_len = str_LineStart( tag=x_aux_trans, NULL, 0, pStr, ind ); - /* print the transposition, cycle after cycle */ - tot_len = str_AuxTautTrans(nTrans_n, nTrans_s, pStr, nStrLen, tot_len, - &bOverflow, TAUT_MODE, num_components); - if ( str_LineEnd( bXml? tag:p_aux_at_inv_nbr, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - /* detected transposition */ - *pSortPrintINChIFlags |= (INCHI_BAS == iINChI)? FLAG_SORT_PRINT_TRANSPOS_BAS : - FLAG_SORT_PRINT_TRANSPOS_REC; - } else - if ( !bXml ) { - if ( bPlainTextTags == 1 ) inchi_ios_print( output_file, "/" ); - } - } -#endif - - /************************************************************** - At this point the INChI_Aux part of the output has been completed. - If this INChI is tautomeric and non-tautomeric results exist - then we need to output non-tautomeric auxilialy data - (same as above excluding tautomeric information) - Currently this is enabled for xml output only - ***************************************************************/ - - if ( bOutType == OUT_TN && bTautomeric && bNonTautomeric && - /* Check whether the Fixed-H layer is empty */ - (*pSortPrintINChIFlags & ((INCHI_BAS == iINChI)? FLAG_SORT_PRINT_NO_NFIX_H_BAS : - FLAG_SORT_PRINT_NO_NFIX_H_REC )) && - (*pSortPrintINChIFlags & ((INCHI_BAS == iINChI)? FLAG_SORT_PRINT_NO_IFIX_H_BAS : - FLAG_SORT_PRINT_NO_IFIX_H_REC )) - ) - { - bNonTautomeric = 0; /* bNonTautIdentifierNotEmpty == 0 => no fixed H info 02-10-2995 */ - } - - if ( bOutType == OUT_TN && bTautomeric && bNonTautomeric ) - { - /* add the second (non-tautomeric) output */ - bOutType = OUT_NONTAUT; - iCurTautMode = TAUT_NON; - pINChISort = pINChISortTautAndNonTaut[TAUT_NON]; - bSecondNonTautPass = 1; - num_components = num_comp[iCurTautMode]; - bFhTag = AL_FIXH; - if ( bXml ) - { - szGetTag( AuxLbl, nTag, bTag1 = bFhTag, szTag1, &bAlways ); - str_LineStart( szTag1, NULL, 0, pStr, ind ); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - } - else - { - pStr[tot_len=0] = '\0'; - } - - /* if InChI Fixed-H isotopic is empty then do not output corresponding AuxInfo */ - if ( !(*pSortPrintINChIFlags & - ((INCHI_BAS == iINChI)? FLAG_SORT_PRINT_NO_NFIX_H_BAS : - FLAG_SORT_PRINT_NO_NFIX_H_REC )) - ) - { - goto repeat_INChI_Aux_output; - } - else - { - goto repeat_INChI_Aux_Iso_output; - } - } - else - { - if ( bOutType == OUT_NONTAUT && bOutputType == OUT_TN && bTautomeric && bNonTautomeric ) - { - /* the second (non-taut) output has been done; restore variables */ - bOutType = OUT_TN; - iCurTautMode = TAUT_YES; - pINChISort = pINChISortTautAndNonTaut[TAUT_YES]; - bSecondNonTautPass = 0; - /* set correct num components for the reversibility info 02-10-2005 */ - num_components = num_comp[iCurTautMode]; - if ( bXml ) - { - /* close non-tautomeric */ - szGetTag( AuxLbl, nTag, bTag1 = bFhTag, szTag1, &bAlways ); - ind -= inc; - if ( str_LineEnd( szTag1, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - bFhTag = 0; - } - } - - - /***************************************/ - /* charges, radicals, unusual valences */ - /***************************************/ - if ( !bSecondNonTautPass && bChargesRadVal[iCurTautMode] ) - { - /* aux equ atoms */ - /* 1. Compare to tautomeric equivalence (in case of second, non-taut, pass only) */ - /* 2. Compare to the previous component if (1) failed to find equivalence */ - szGetTag( AuxLbl, nTag, bTag1 = AL_CRV_ | bFhTag, szTag1, &bAlways ); - tot_len = str_LineStart( szTag1, NULL, 0, pStr, ind ); - tot_len = str_AuxChargeRadVal(pINChISort, pStr, nStrLen, tot_len, - &bOverflow, bOutType, TAUT_MODE, - num_components, bUseMulipliers); - if ( str_LineEnd( szTag1, tot_len, nStrLen, &bOverflow, pStr, bXml? 0 : -1, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s%s", pStr, pLF ); - } - - /* output the original input structure -- quick fix */ - if ( !bSecondNonTautPass && pOrigStruct && pOrigStruct->num_atoms && - pOrigStruct->szAtoms && pOrigStruct->szBonds && pOrigStruct->szCoord ) - { - int length, cur_pos, line_len, last_pos, nMaxLineLen; - char *p; - nMaxLineLen = inchi_min( 80, nStrLen ); /* restrict line length to 80 characters */ - /********************** - reversibility info - **********************/ - szGetTag( AuxLbl, nTag, bTag1 = AL_REVR | bFhTag, szTag1, &bAlways ); - if ( bXml ) - { - str_LineStart( szTag1, NULL, 0, pStr, ind ); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - } - /* === atoms === */ - szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_ATMR, szTag2, &bAlways ); - if ( bXml ) - { - str_LineStart( szTag2, NULL, 0, pStr, ind ); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - /* first line indent */ - strcpy( pStr, SP(ind)); - tot_len = ind; - } - else - { - pStr[tot_len = 0] = '\0'; - inchi_ios_print( output_file, "%s%s", szTag2, pStr ); - } - p = pOrigStruct->szAtoms; - length = strlen( p ); - line_len = nMaxLineLen - tot_len; - for ( cur_pos = 0; cur_pos < length; cur_pos = last_pos ) - { - if ( length - cur_pos >= line_len ) - { - last_pos = cur_pos + line_len; - /* search backward for the nearest first atom letter (always uppercase) */ - while ( cur_pos < last_pos && !isupper( UCINT p[last_pos] ) ) { - last_pos --; - } - } - else - { - last_pos = length; - } - if ( last_pos > cur_pos ) - { - memcpy( pStr + tot_len, p+cur_pos, last_pos - cur_pos ); - pStr[tot_len + last_pos - cur_pos] = '\0'; - inchi_ios_print( output_file, "%s%s", pStr, !bXml && bPlainTextTags? "" : "\n" ); - } - else - { - break; - } - } - if ( bXml ) - { - ind -= inc; - pStr[0] = '\0'; - if ( str_LineEnd( szTag2, 0, nMaxLineLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - else - { - if ( pLF[0] ) - { - inchi_ios_print( output_file, "%s", pLF ); - } - } - - - /* === bonds === */ - szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_BNDR, szTag2, &bAlways ); - if ( bXml ) - { - str_LineStart( szTag2, NULL, 0, pStr, ind ); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - /* first line indent */ - strcpy( pStr, SP(ind)); - tot_len = ind; - } - else - { - pStr[tot_len = 0] = '\0'; - inchi_ios_print( output_file, "%s%s", szTag2, pStr ); - } - - p = pOrigStruct->szBonds; - length = strlen( p ); - line_len = nMaxLineLen - tot_len; - for ( cur_pos = 0; cur_pos < length; cur_pos = last_pos ) - { - if ( length - cur_pos >= line_len ) - { - last_pos = cur_pos + line_len - 1; - /* search backward for the nearest first bond delimiter ";" */ - while ( cur_pos < last_pos && p[last_pos] != ';' ) - { - last_pos --; - } - if ( cur_pos < last_pos ) - { - last_pos ++; /* include ';' at the end of the line */ - } - } - else - { - last_pos = length; - } - if ( last_pos > cur_pos ) - { - memcpy( pStr + tot_len, p+cur_pos, last_pos - cur_pos ); - pStr[tot_len + last_pos - cur_pos] = '\0'; - inchi_ios_print( output_file, "%s%s", pStr, !bXml && bPlainTextTags? "" : "\n" ); - } - else - { - break; - } - } - - if ( bXml ) - { - ind -= inc; - pStr[0] = '\0'; - if ( str_LineEnd( szTag2, 0, nMaxLineLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - else - { - if ( pLF[0] ) - { - inchi_ios_print( output_file, "%s", pLF ); - } - } - - - /* === coordinates === */ - szGetTag( AuxLbl, nTag, bTag2 = bTag1 | AL_XYZR, szTag2, &bAlways ); - if ( bXml ) - { - str_LineStart( szTag2, NULL, 0, pStr, ind ); - inchi_ios_print( output_file, "%s\n", pStr ); - ind += inc; - /* first line indent */ - strcpy( pStr, SP(ind)); - tot_len = ind; - } - else - { - pStr[tot_len = 0] = '\0'; - inchi_ios_print( output_file, "%s%s", szTag2, pStr ); - } - - p = pOrigStruct->szCoord; - length = strlen( p ); - line_len = nMaxLineLen - tot_len; - for ( cur_pos = 0; cur_pos < length; cur_pos = last_pos ) - { - if ( length - cur_pos >= line_len ) - { - last_pos = cur_pos + line_len - 1; - /* search backward for the nearest first coord. delimiter ";" */ - while ( cur_pos < last_pos && p[last_pos] != ';' ) - { - last_pos --; - } - if ( cur_pos < last_pos ) - { - last_pos ++; /* include ';' at the end of the line */ - } - } - else - { - last_pos = length; - } - if ( last_pos > cur_pos ) - { - memcpy( pStr + tot_len, p+cur_pos, last_pos - cur_pos ); - pStr[tot_len + last_pos - cur_pos] = '\0'; - inchi_ios_print( output_file, "%s%s", pStr, !bXml && bPlainTextTags? "" : "\n" ); - } - else - { - break; - } - } - - if ( bXml ) - { - ind -= inc; - pStr[0] = '\0'; - if ( str_LineEnd( szTag2, 0, nMaxLineLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - else - { - if ( pLF[0] ) - { - inchi_ios_print( output_file, "%s", pLF ); - } - } - - if ( bXml ) - { - /*************************** - close reversibility info - ***************************/ - ind -= inc; - if ( str_LineEnd( szTag1, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - } - - - - /************************************************ - * output INChI_Aux of the reconnected structure * - ************************************************/ - bEmbeddedOutputCalled = 0; - if ( bDisconnectedCoord && INCHI_BAS == iINChI && (bINChIOutputOptions & INCHI_OUT_EMBED_REC) && - num_components2[INCHI_REC] && !(bINChIOutputOptions & INCHI_OUT_NO_AUX_INFO) ) - { - int nRet; - bEmbeddedOutputCalled = 1; - if ( !bXml ) - { - inchi_ios_print( output_file, "%s", pLF ); - } - - nRet = OutputINChI1(pStr, nStrLen, - pINChISortTautAndNonTaut2, - INCHI_REC, - NULL, - 0 /*bDisconnectedCoord*/, bOutputType, - INCHI_OUT_ONLY_AUX_INFO | bINChIOutputOptions, - bXml, bAbcNumbers, bCtPredecessors, bNoStructLabels, - num_components2, - num_non_taut2, num_taut2, - output_file, log_file, - num_input_struct, - szSdfLabel, szSdfValue, lSdfId, - pSortPrintINChIFlags, - save_opt_bits); - if ( !nRet ) - goto exit_function; /* error */ - } - - /* close INChI_Aux */ - if ( bXml ) - { - ind -= inc; - if ( str_LineEnd( x_aux_basic, 0, nStrLen, &bOverflow, pStr, ind, bPlainTextTags ) ) - goto exit_function; - inchi_ios_print( output_file, "%s", pStr ); - } - else - { - if ( !bEmbeddedOutputCalled && !bPlainTextCommnts ) - { - inchi_ios_print( output_file, "%s\n", (!num_components2[0] && !num_components2[1])? "//":"" ); - /* plain text comment earlier ended with LF */ - } - } - - - /* in wINChI window, separate AuxInfo: from InChIKey: with blank line */ - inchi_ios_print( output_file, "%s", - (bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW) ? "\n":""); - - - - - } /* end of output aux info */ - - - ret = 1; -exit_function: - - if ( bOverflow ) - { - strcpy( pStr, "Output buffer overflow"); - if ( bXml ) - { - OutputINChIXmlError( output_file, pStr, nStrLen, ind /*, 0*/ /* err number */, pStr, _IS_FATAL ); - } - else - { - inchi_ios_print( output_file, "\nFATAL ERROR: %s\n", pStr ); - } - } - - /* inchi_free( pStr ); */ - return ret; - - -} /* OutputINChI1 */ - - - -/***************************************************************/ -int str_LineStart( const char *tag, char *tag2, int val2, char *pStr, int ind ) -{ - int tot_len = 0; - if ( ind >= 0 ) { - if ( ind > 0 ) { - /* xml: indent */ - memset( pStr + tot_len, ' ', ind ); - tot_len += ind; - } - /* xml: tag */ - strcpy( pStr + tot_len, x_line_opening ); - strcat( pStr + tot_len, tag ); - if ( tag2 ) { - tot_len += strlen(pStr + tot_len); - tot_len += sprintf( pStr + tot_len, " %s=\"%d\"%s", tag2, val2, x_close_line ); - - } else { - strcat( pStr + tot_len, x_close_line ); - tot_len += strlen(pStr + tot_len); - } - } else { - pStr[tot_len] = '\0'; - } - return tot_len; -} -/***************************************************************/ -int str_LineEnd( const char *tag, int tot_len, int nStrLen, int *bOverflow, char *pStr, int ind, int bPlainTextTags ) -{ - static int add_tag_len = sizeof(x_line_closing)-1 + sizeof(x_close_line)-1; - int tag_len; - /* check buffer overflow */ - if ( *bOverflow ) - return 1; - if ( ind >= 0 ) { /* xml */ - tag_len = ind + add_tag_len + strlen(tag); - if ( tot_len + tag_len < nStrLen - 2 ) { - /* output " \n" */ - tot_len += sprintf( pStr + tot_len, "%s%s%s%s\n", SP(ind), x_line_closing, tag, x_close_line ); - } else { - *bOverflow += 1; - return 1; - } - } else { /* plain */ - pStr[tot_len] = '\0'; /* add zero termination 2004-04-26 */ - /* insert plain text tag if: - (a) pStr has non-zero length, or - (b) ind < -1 - */ - - if ( pStr[0] || ind < -1 ) { - tag_len = bPlainTextTags? strlen( tag ):0; - if ( tot_len + tag_len < nStrLen - 2 ) { - if ( tag_len > 0 ) { - /* insert plain text tag */ - memmove( pStr+tag_len, pStr, tot_len + 1 ); - memcpy( pStr, tag, tag_len ); - } - } else { - *bOverflow += 1; - return 1; - } - }/* else - if ( bPlainTextTags == 1 ) { - strcpy( pStr, "/" ); - }*/ - } - return 0; -} - - - -/**********************************************************************************************/ -int CleanOrigCoord( MOL_COORD szCoord, int delim ) -{ -#define MIN_BOND_LENGTH (1.0e-6) - char szVal[LEN_COORD+1]; - MOL_COORD szBuf; - char *q; - int len, last, fst, dec_pnt, num_zer=0, len_buf = 0, e; - int k, i; - double coord; - - for ( k = 0; k < NUM_COORD*LEN_COORD; k += LEN_COORD ) { - memcpy( szVal, szCoord+k, LEN_COORD ); - szVal[LEN_COORD] = '\0'; - LtrimRtrim(szVal, &len); - coord = strtod(szVal, &q); - if ( MIN_BOND_LENGTH > fabs(coord) ) { - strcpy( szVal, "0" ); - len = 1; - num_zer ++; - } else { - len = q - szVal; - /* last = (last mantissa digit position + 1) */ - if ( (q = strchr(szVal, 'e')) || (q = strchr(szVal, 'E')) || - (q = strchr(szVal, 'd')) || (q = strchr(szVal, 'D')) ) { - /* floating point */ - last = q - szVal; - /* remove (+) and leading zeroes from the exponent */ - e = (int)strtol( szVal+last+1, &q, 10 ); /* exponent */ - if ( e ) { - /* new exp; update the length */ - len = last+1+sprintf( szVal+last+1, "%d", e ); /* print exp without leading zeroes and '+' */ - } else { - /* exponent is zero */ - len = last; - } - } else { - last = len; - } - /* fst = (first mantissa digit); fst=1 if the sign is present, otherwise 0 */ - fst = (szVal[0]!='.' && !isdigit( UCINT szVal[0] )); - /* dec_pnt = (decimal point position) or last */ - if ( q = strchr(szVal, '.') ) { - dec_pnt = q - szVal; - } else { - dec_pnt = last; - } - last -= 1; /* last mantissa digit position */ - /* remove trailing zeroes in the range dec_pnt+1..last-1 */ - for ( i = last; dec_pnt < i && '0' == szVal[i]; i -- ) - ; - if ( i == dec_pnt ) { - i --; /* remove decimal point, too */ - } - if ( i < last ) { - memmove( szVal+i+1, szVal+last+1, len-last ); - len -= last-i; - } - /* remove leading zeroes */ - for ( i = fst; i < len && '0' == szVal[i]; i ++ ) - ; - if ( i > fst ) { - memmove( szVal + fst, szVal+i, len-fst ); - len -= i-fst; - } - } - if ( len_buf ) - szBuf[len_buf++] = delim; - memcpy( szBuf + len_buf, szVal, len ); /* does not copy zero termination*/ - len_buf += len; - } - /* zero termination */ - if ( len_buf < (int)sizeof(MOL_COORD) ) { - memset( szBuf+len_buf, 0, sizeof(MOL_COORD) - len_buf); - } - memcpy( szCoord, szBuf, sizeof(MOL_COORD) ); - return num_zer; -#undef MIN_BOND_LENGTH -} - -/******************************************************************************************/ -int WriteOrigCoord( int num_inp_atoms, MOL_COORD *szMolCoord, int *i, char *szBuf, int buf_len ) -{ - - int j, num_zer, len, cur_len; - char *p; - MOL_COORD szCurCoord; - cur_len = 0; - for ( j = *i; j < num_inp_atoms; ) { - memcpy( szCurCoord, szMolCoord[j], sizeof(szCurCoord)); - num_zer = CleanOrigCoord( szCurCoord, ',' ); - if ( NUM_COORD == num_zer ) { - len = 0; - } else { - if ( p = (char *)memchr( szCurCoord, '\0', sizeof(szCurCoord)) ) { - len = p - szCurCoord; - } else { - len = sizeof(szCurCoord); - } - } - if ( len + cur_len + 1 < buf_len ) { - if ( len ) { - memcpy( szBuf + cur_len, szCurCoord, len * sizeof(szBuf[0]) ); - } - szBuf[cur_len += len] = ';'; - cur_len ++; - j ++; - } else { - break; - } - } - szBuf[cur_len] = '\0'; - *i = j; /* next item */ - return cur_len; -} -/******************************************************************************************/ -/* - number of atoms - [c|n] chiral/nonchiral - - Element - #valence - +/-[charge>1] - .#rad (#rad=1, 2, 3: singlet, doulet, triplet) - [.]i#iso_mass - [.]{o|e|u|?} atom parity = {1:2:3:4} - [.]h[#of 1H>1] - [.]d[#of 2H>1] - [.]t[#of 3H>1] - - Note: . occurs only once and only if radical or 1-character element -*/ -int WriteOrigAtoms( int num_inp_atoms, inp_ATOM *at, int *i, char *szBuf, int buf_len, STRUCT_DATA *sd) -{ - int j, k, n, len, len0, cur_len, val, bonds_val, mw, parity, num_trans, is_ok, b_self; - static char szIsoH[] = "hdt"; - char szCurAtom[32]; - AT_NUMB nNeighOrder[MAXVAL], neigh; - - cur_len = 0; - if ( 0 == *i ) { - cur_len = sprintf( szBuf, "%d%s", num_inp_atoms, - (sd->bChiralFlag & FLAG_INP_AT_CHIRAL)? "c" : - (sd->bChiralFlag & FLAG_INP_AT_NONCHIRAL)? "n" : "" ); - } - for ( j = *i; j < num_inp_atoms; ) { - /* tetrahedral parity treatment */ - parity = 0; - num_trans = 0; - if ( at[j].p_parity ) { - /* verify neighbors */ - is_ok = 1; - b_self = 0; - for ( n = 0, k = 0; n < MAX_NUM_STEREO_ATOM_NEIGH; n ++ ) { - neigh = at[j].p_orig_at_num[n]-1; - if ( is_in_the_list( at[j].neighbor, neigh, at[j].valence ) && - at[neigh].orig_at_number == at[j].p_orig_at_num[n] ) { - /* real neighbor */ - nNeighOrder[k ++] = at[j].p_orig_at_num[n]; - } else - if ( (int)neigh == j && at[neigh].orig_at_number == at[j].p_orig_at_num[n] ) { - /* central atom is a neighbor */ - num_trans = n; /* move this neighbor to 0 position permutation parity */ - b_self ++; - } else { - is_ok = 0; - break; - } - } - if ( is_ok && b_self <= 1 && b_self + k == MAX_NUM_STEREO_ATOM_NEIGH ) { - num_trans += insertions_sort( nNeighOrder, k, sizeof(nNeighOrder[0]), comp_AT_RANK ); - if ( ATOM_PARITY_WELL_DEF( at[j].p_parity ) ) { - parity = 2 - (num_trans + at[j].p_parity) % 2; - } else - if ( ATOM_PARITY_ILL_DEF( at[j].p_parity ) ) { - parity = at[j].p_parity; - } else { - ; /* invalid atom parity */ - } - } else { - ;/* add error message here */ - } - } - - len = len0 = strlen( at[j].elname ); - memcpy( szCurAtom, at[j].elname, len ); - bonds_val = nBondsValenceInpAt( at+j, NULL, NULL ); - - if ( (val=needed_unusual_el_valence( at[j].el_number, at[j].charge, at[j].radical, - at[j].chem_bonds_valence, bonds_val, at[j].num_H, at[j].valence )) || - at[j].charge || at[j].radical || at[j].iso_atw_diff || NUM_ISO_H(at,j) || parity ) { - /* valence */ - if ( val ) { - len += sprintf( szCurAtom + len, "%d", val > 0? val : 0 ); - } - /* charge */ - if ( val = at[j].charge ) { - szCurAtom[len++] = val>0? '+' : '-'; - if ( (val = abs(val)) > 1 ) { - len += sprintf( szCurAtom + len, "%d", val ); - } - } - /* radical */ - if ( val = at[j].radical ) { - len += sprintf(szCurAtom + len, ".%d", val); - } - /* isotopic shift */ - if ( val = at[j].iso_atw_diff ) { - mw = get_atw_from_elnum( at[j].el_number ); - if ( val == 1 ) - val = mw; - else - if ( val > 0 ) - val = mw + val -1; - else - val = mw + val; - len += sprintf( szCurAtom + len, "%si%d", len == len0? ".":"", val ); - } - /* parity */ - if ( parity ) { - len += sprintf( szCurAtom + len, "%s%s", len == len0? ".":"", - parity == AB_PARITY_ODD? "o" : - parity == AB_PARITY_EVEN? "e" : - parity == AB_PARITY_UNKN? "u" : - parity == AB_PARITY_UNDF? "?" : "" ); - } - /* implicit isotopic H */ - if ( NUM_ISO_H(at,j) ) { - for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) { - if ( val = at[j].num_iso_H[k] ) { - len += sprintf( szCurAtom + len, "%s%c", len == len0? ".":"", szIsoH[k] ); - if ( val > 1 ) { - len += sprintf(szCurAtom + len, "%d", val); - } - } - } - } - } - if ( len + cur_len < buf_len ) { - memcpy( szBuf + cur_len, szCurAtom, len ); - cur_len += len; - j ++; - } else { - break; - } - szBuf[cur_len] = '\0'; - *i = j; - - } - return cur_len; -} -/******************************************************************************************/ -/* - bpA;bpAbpA... - -b = bond type: -============= -w = undefined stereo, double -s = single -d = double -t = triple -a = aromatic -p = up from the current atom to the neighbor -P = uP from the neighbor to the current atom -v = undefined stereo Either, single from the current atom to the neighbor -V = undefined stereo Either, single from the neighbor to the current atom -n = down from the current atom to the neighbor -N = dowN from the neighbor to the current atom - -p = bond parity: -================ -- = odd -+ = even -u = unknown -? = undefined - = no parity (empty) - - -A = neighbor orig. atom number -=============== -neighbor orig. atom number < number of the current atom -Number of the current atom: 2 until first ";", 3 until 2nd ";", etc. - -*/ - -/************************************************************************************/ -/* output bonds in ascending order of the neighboring atom original numbers */ -int WriteOrigBonds( int num_inp_atoms, inp_ATOM *at, int *i, char *szBuf, int buf_len, STRUCT_DATA *sd) -{ - int j, k, k2, kk, len, cur_len, j2=0, bond_stereo, bond_char, bond_parity, bond_parityNM, num_trans; - char szCurBonds[7*MAXVAL+2]; /* num_neigh*(1 byte bond type + 2 bytes for bond parity up to 4 digits per neighbor number) + at the end one ';' */ - AT_RANK nNeighOrder[MAXVAL]; - int chain_len, pnxt_atom, pinxt2cur, pinxt_sb_parity_ord; - int chain_len2, pnxt_atom2, pinxt2cur2, pinxt_sb_parity_ord2, m1, m2; - int pcur_atom, picur2nxt, picur_sb_parity_ord; - - cur_len = 0; - for ( j = *i; j < num_inp_atoms; ) { - len = 0; - if ( at[j].valence > 1 ) { - for ( k = 0; k < at[j].valence; k ++ ) { - nNeighOrder[k] = k; - } - pn_RankForSort = at[j].neighbor; - num_trans = insertions_sort( nNeighOrder, at[j].valence, sizeof(nNeighOrder[0]), CompRank ); - } else { - num_trans = 0; - nNeighOrder[0] = 0; - } - for ( kk = 0; kk < at[j].valence; kk ++ ) { - k = nNeighOrder[kk]; - j2 = at[j].neighbor[k]; - bond_parity = 0; - bond_parityNM = 0; - if ( j2 < j ) { - bond_stereo = at[j].bond_stereo[k]; - switch( at[j].bond_type[k] ) { - case BOND_TYPE_SINGLE: - switch( bond_stereo ) { - case STEREO_SNGL_UP: - bond_char = 'p'; - break; - case -STEREO_SNGL_UP: - bond_char = 'P'; - break; - case STEREO_SNGL_DOWN: - bond_char = 'n'; - break; - case -STEREO_SNGL_DOWN: - bond_char = 'N'; - break; -#if ( FIX_EITHER_STEREO_IN_AUX_INFO == 1 ) - case STEREO_SNGL_EITHER: - bond_char = 'v'; - break; - case -STEREO_SNGL_EITHER: - bond_char = 'V'; - break; -#else - case STEREO_SNGL_EITHER: - case -STEREO_SNGL_EITHER: - bond_char = 'v'; - break; -#endif - default: - bond_char = 's'; - break; - } - break; - case BOND_TYPE_DOUBLE: - switch( bond_stereo ) { - case STEREO_DBLE_EITHER: - case -STEREO_DBLE_EITHER: - bond_char = 'w'; - break; - default: - bond_char = 'd'; - break; - } - break; - case BOND_TYPE_TRIPLE: - bond_char = 't'; - break; - case BOND_TYPE_ALTERN: - bond_char = 'a'; - break; - default: - bond_char = 's'; - break; - } - /* check for allene/cumulene */ - k2 = is_in_the_list( at[j2].neighbor, (AT_NUMB)j, at[j2].valence ) - at[j2].neighbor; - chain_len = chain_len2 = 0; - if ( at[j].sb_parity[0] ) { - for ( m1 = 0; m1 < MAX_NUM_STEREO_BONDS && at[j].sb_parity[m1]; m1 ++ ) { - if ( k == at[j].sb_ord[m1] ) { - chain_len = get_opposite_sb_atom( at, j, k, - &pnxt_atom, &pinxt2cur, &pinxt_sb_parity_ord ); - break; - } - } - } - if ( at[j2].sb_parity[0] ) { - for ( m2 = 0; m2 < MAX_NUM_STEREO_BONDS && at[j2].sb_parity[m2]; m2 ++ ) { - if ( k2 == at[j2].sb_ord[m2] ) { - chain_len2 = get_opposite_sb_atom( at, j2, k2, - &pnxt_atom2, &pinxt2cur2, &pinxt_sb_parity_ord2 ); - break; - } - } - } - if ( chain_len == 1 && chain_len2 == 1 || /* regular stereobond */ - chain_len > 1 && j > pnxt_atom ) { /* j is a cumulene endpoint */ - int m; - pcur_atom = j; /* pcur_atom > pnxt_atom */ - picur2nxt = k; - picur_sb_parity_ord = -1; - for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[pcur_atom].sb_parity[m]; m ++ ) { - if ( at[pcur_atom].sb_ord[m] == k ) { - picur_sb_parity_ord = m; - break; - } - } - chain_len2 = 0; - } else - if ( chain_len2 > 1 && j2 > pnxt_atom2 ) { /* j2 is a cumulene endpoint */ - int m; - pcur_atom = j2; - picur2nxt = k2; - pnxt_atom = pnxt_atom2; - pinxt2cur = pinxt2cur2; - pinxt_sb_parity_ord = pinxt_sb_parity_ord2; - picur_sb_parity_ord = -1; - for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[pcur_atom].sb_parity[m]; m ++ ) { - if ( at[pcur_atom].sb_ord[m] == k2 ) - picur_sb_parity_ord = m; - } - chain_len = chain_len2; - chain_len2 = 0; - } else { - chain_len = chain_len2 = 0; - } - - /*len += sprintf( szCurBonds + len, "%c%d", bond_char, val+1);*/ - if ( chain_len ) { - /* both atoms belong to a stereo bond */ - int kc; - int p1, p2, p1NM, p2NM, neigh, neigh1, neigh2, bHasMetal, bWellDef; - int bNeighSwitched1, bNeighSwitched2; - - p1 = SB_PARITY_1(at[pcur_atom].sb_parity[picur_sb_parity_ord]); - p1NM = SB_PARITY_2(at[pcur_atom].sb_parity[picur_sb_parity_ord]); - p2 = SB_PARITY_1(at[pnxt_atom].sb_parity[pinxt_sb_parity_ord]); - p2NM = SB_PARITY_2(at[pnxt_atom].sb_parity[pinxt_sb_parity_ord]); - - bWellDef = ATOM_PARITY_WELL_DEF(p1) && ATOM_PARITY_WELL_DEF(p2); - bHasMetal = ATOM_PARITY_WELL_DEF(p1NM) && ATOM_PARITY_WELL_DEF(p2NM); - - bNeighSwitched1 = bNeighSwitched2 = 0; - - if ( bWellDef || bHasMetal ) { - - neigh1 = num_inp_atoms; - for ( kc = 0; kc < at[pcur_atom].valence; kc ++ ) { - if ( kc == picur2nxt ) - continue; - neigh = at[pcur_atom].neighbor[kc]; - if ( bHasMetal && is_el_a_metal( at[neigh].el_number ) ) - continue; - if ( neigh < neigh1 ) - neigh1 = neigh; - } - if ( neigh1 < num_inp_atoms ) { - bNeighSwitched1 = (neigh1 != at[pcur_atom].neighbor[(int)at[pcur_atom].sn_ord[picur_sb_parity_ord]]); - } else { - AddMOLfileError(sd->pStrErrStruct, "Cannot find 0D stereobond neighbor"); - /* - sd->nStructReadError = 99; - sd->nErrorType = _IS_ERROR; - */ - - } - - neigh2 = num_inp_atoms; - for ( kc = 0; kc < at[pnxt_atom].valence; kc ++ ) { - if ( kc == pinxt2cur ) - continue; - neigh = at[pnxt_atom].neighbor[kc]; - if ( bHasMetal && is_el_a_metal( at[neigh].el_number ) ) - continue; - if ( neigh < neigh2 ) - neigh2 = neigh; - } - if ( neigh2 < num_inp_atoms ) { - bNeighSwitched2 = (neigh2 != at[pnxt_atom].neighbor[(int)at[pnxt_atom].sn_ord[pinxt_sb_parity_ord]]); - } else { - AddMOLfileError(sd->pStrErrStruct, "Cannot find 0D stereobond neighbor"); - /* - sd->nStructReadError = 99; - sd->nErrorType = _IS_ERROR; - */ - - } - - if ( neigh1 < num_inp_atoms && neigh2 < num_inp_atoms ) { - if ( ATOM_PARITY_WELL_DEF(p1) && ATOM_PARITY_WELL_DEF(p2) ) { - bond_parity = 2 - (p1 + p2 + bNeighSwitched1 + bNeighSwitched2) % 2; - } else { - bond_parity = inchi_min( p1, p2 ); - } - - if ( bHasMetal ) { - bond_parityNM = 2 - (p1NM + p2NM + bNeighSwitched1 + bNeighSwitched2) % 2; - } else - if ( p1NM && p2NM ) { - bond_parityNM = inchi_min( p1NM, p2NM ); - } - } - } else { - if ( p1 && p2 ) { - bond_parity = inchi_min( p1, p2 ); - } - if ( p1NM && p2NM ) { - bond_parityNM = inchi_min( p1NM, p2NM ); - } - if ( bond_parityNM && !bond_parity ) { - bond_parity = AB_PARITY_UNDF; - } - } - } - len += sprintf( szCurBonds + len, "%c%s%s%d", - - bond_char, - - (bond_parity == AB_PARITY_ODD)? "-" : - (bond_parity == AB_PARITY_EVEN)? "+" : - (bond_parity == AB_PARITY_UNKN)? "u" : - (bond_parity == AB_PARITY_UNDF)? "?" : "", - - (bond_parityNM == AB_PARITY_ODD)? "-" : - (bond_parityNM == AB_PARITY_EVEN)? "+" : - (bond_parityNM == AB_PARITY_UNKN)? "u" : - (bond_parityNM == AB_PARITY_UNDF)? "?" : "", - - j2+1); - } - } - if ( len + cur_len + 2 < buf_len ) { - memcpy( szBuf + cur_len, szCurBonds, len ); - cur_len += len; - szBuf[ cur_len ++ ] = ';'; - j ++; - } else { - break; - } - } - szBuf[cur_len] = '\0'; - *i = num_inp_atoms>0? j : 0; - return cur_len; -} - - -#define ORIG_STR_BUFLEN (7*MAXVAL+2) /* > 7*MAXVAL+2 = 142 */ -/******************************************************************************************/ -int FillOutOrigStruct( ORIG_ATOM_DATA *orig_inp_data, ORIG_STRUCT *pOrigStruct, STRUCT_DATA *sd ) -{ - char szBuf[ORIG_STR_BUFLEN]; - int i, len, len_coord, len_atoms, len_bonds; - /* coordinates */ - len_coord = i = 0; - - if (orig_inp_data->szCoord) { - - while ( len = WriteOrigCoord( orig_inp_data->num_inp_atoms, - orig_inp_data->szCoord, &i, szBuf, sizeof(szBuf) )) { - len_coord += len; - } - pOrigStruct->szCoord = (char*) inchi_malloc( (len_coord + 1)*sizeof(pOrigStruct->szCoord[0]) ); - i = 0; - if ( pOrigStruct->szCoord && - len_coord == WriteOrigCoord( orig_inp_data->num_inp_atoms, - orig_inp_data->szCoord, &i, pOrigStruct->szCoord, len_coord+1 ) && - i == orig_inp_data->num_inp_atoms ) { - /* success */ - if ( orig_inp_data->szCoord ) { - inchi_free( orig_inp_data->szCoord ); - orig_inp_data->szCoord = NULL; - } - } else { - return -1; - } - - } - - /* atoms */ - len_atoms = i = 0; - while ( len = WriteOrigAtoms( orig_inp_data->num_inp_atoms, - orig_inp_data->at, &i, szBuf, sizeof(szBuf), sd)) { - len_atoms += len; - if ( !orig_inp_data->num_inp_atoms ) - break; - } - pOrigStruct->szAtoms = (char*) inchi_malloc( (len_atoms + 1)*sizeof(pOrigStruct->szAtoms[0]) ); - i = 0; - if ( pOrigStruct->szAtoms && - len_atoms == WriteOrigAtoms( orig_inp_data->num_inp_atoms, - orig_inp_data->at, &i, pOrigStruct->szAtoms, len_atoms+1, sd ) && - i == orig_inp_data->num_inp_atoms ) { - ; /* success */ - } else { - return -1; - } - /* bonds */ - len_bonds = 0; - i = 1; - while ( len = WriteOrigBonds( orig_inp_data->num_inp_atoms, - orig_inp_data->at, &i, szBuf, sizeof(szBuf), NULL)) { - len_bonds += len; - if ( !orig_inp_data->num_inp_atoms ) - break; - } - pOrigStruct->szBonds = (char*) inchi_malloc( (len_bonds + 2)*sizeof(pOrigStruct->szBonds[0]) ); - i = 1; - if ( pOrigStruct->szBonds && - len_bonds == WriteOrigBonds( orig_inp_data->num_inp_atoms, - orig_inp_data->at, &i, pOrigStruct->szBonds, len_bonds+2, sd ) && - i == orig_inp_data->num_inp_atoms ) { - ; /* success */ - } else { - return -1; - } - pOrigStruct->num_atoms = orig_inp_data->num_inp_atoms; - return 0; -} -/*****************************************************************/ -void FreeOrigStruct( ORIG_STRUCT *pOrigStruct) -{ - if ( pOrigStruct ) { - if ( pOrigStruct->szAtoms ) - inchi_free( pOrigStruct->szAtoms ); - if ( pOrigStruct->szBonds ) - inchi_free( pOrigStruct->szBonds ); - if ( pOrigStruct->szCoord ) - inchi_free( pOrigStruct->szCoord ); - memset( pOrigStruct, 0, sizeof(*pOrigStruct) ); - - } -} - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get the two letters encoding the saved InChI creation options. - -The first one encodes RecMet/FixedH/SUU/SLUUD options. -Each of options is a binary switch {ON,OFF}, so it totals to 2*2*2*2=16 values -which are encoded by capital letters ‘A’ through ‘P’. - -The second character encodes experimental (InChI 1 extension) options KET and 15T. -Each of these options is a binary switch ON/OFF, so there are 2*2=4 combinations, -currently encoded by ‘A’ through ‘D’. -Note that anything but 'A' here would indicate "extended" InChI 1 Also, there is a -reservation for future needs: the 2nd memo char may accommodate two more ON/OFF -binary options (at 26-base encoding). -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void GetSaveOptLetters(unsigned char save_opt_bits, char* let1, char* let2) -{ -const char a2p[]="ABCDEFGHIJKLMNOP"; - /* SaveOptBits layout: {unused|unused|Ket|15T|RecMet|FixedH|SUU|SLUUD} */ - *let1 = a2p [ (size_t) ( save_opt_bits & 0x0f ) ]; - *let2 = a2p [ (size_t) ( (save_opt_bits & 0x30) >> 4 ) ]; -} diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichiprt3.c b/INCHI-1-SRC/INCHI_API/inchi_dll/ichiprt3.c deleted file mode 100644 index 6348749..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichiprt3.c +++ /dev/null @@ -1,3222 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include - -#include "mode.h" - -#include "inpdef.h" -#include "ichi.h" -#include "strutil.h" -#include "util.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "ichicant.h" -#include "ichicano.h" -#include "ichicomn.h" - -#include "ichicomp.h" -#include "ichimain.h" -#include "ichimake.h" - - -/***************************************************************************/ -int str_Sp2(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) -{ - int i, ii, ii2; - INCHI_SORT *is, *is2, *is0, *is20; - INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; - INChI_Stereo *Stereo, *Stereo_Prev, *Stereo_Taut, *Stereo_Taut_Prev; - int mult, eq2prev, eq2taut, eq2tautPrev, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - - pINChI_Taut = NULL; - pINChI_Prev = NULL; - pINChI_Taut_Prev = NULL; - mult = 0; - bNext = 0; - is = NULL; - is2 = NULL; - is0 = pINChISort; - is20 = bSecondNonTautPass? pINChISort2 : NULL; - eq2taut = 0; /* may be non-zero only on the 2nd (non-taut) pass */ - eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; - for ( i = 0; i <= num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; - /*================ compare sp2 to previous =====================*/ - if ( bSecondNonTautPass ) { - /* component that was output on the 1st pass */ - pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; - } - /*========= if bSecondNonTautPass then compare non-iso non-taut stereo to non-iso taut ========*/ - eq2taut = 0; -#if ( FIX_EMPTY_LAYER_BUG == 1 ) - if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions && pINChI && pINChI_Taut ) { - Stereo = pINChI->Stereo; - Stereo_Taut = pINChI_Taut->Stereo; - eq2taut = Stereo && Stereo_Taut && - Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Taut, EQL_SP2, 0 ); - eq2taut = eq2taut? (iiSTEREO | iitNONTAUT) : 0; - - if ( !eq2taut && - !Eql_INChI_Stereo( Stereo, EQL_SP2, NULL, EQL_EXISTS, 0 ) && - Eql_INChI_Stereo( Stereo_Taut, EQL_SP2, NULL, EQL_EXISTS, 0 ) ) { - eq2taut = iiEmpty; /* the current is empty while the preceding (taut) is not */ - } - } -#else - if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions ) { - eq2taut = pINChI && pINChI_Taut && - (Stereo = pINChI->Stereo) && (Stereo_Taut = pINChI_Taut->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Taut, EQL_SP2, 0 ); - eq2taut = eq2taut? (iiSTEREO | iitNONTAUT) : 0; - } -#endif - if ( eq2taut ) { - /* we may be here only in case of the second (non-taut) pass */ - /* current non-taut stereo has been found to be same as tautomeric */ - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - /* previous component exists; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( (Stereo_Prev = pINChI_Prev->Stereo) && Stereo_Prev->nNumberOfStereoBonds > 0 ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - - tot_len += MakeStereoString( Stereo_Prev->nBondAtom1, Stereo_Prev->nBondAtom2, - Stereo_Prev->b_parity, - 0, Stereo_Prev->nNumberOfStereoBonds, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - } else - if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - /* previous non-taut component exists only in taut list */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - } - /* we have found pINChI->Stereo sp2 same as in pINChI_Taut */ - /* output this (current) equivalence as '*', that is, same as tautomeric */ - /* that was printed on the 1st pass. */ - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output the previous one */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ - pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ - mult = 0; - eq2tautPrev = 1; /* pINChI_Prev sp2 does not exist */ - } else - if ( eq2tautPrev ) { - /* at this point pINChI_Prev does not exist; however, pINChI */ - /*might have been discovered and it is different from pINChI_Taut */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - eq2tautPrev = 0; - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; - } else { - /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp2 */ - eq2prev =bUseMulipliers && - pINChI && pINChI_Prev && - (Stereo = pINChI->Stereo) && (Stereo_Prev = pINChI_Prev->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Prev, EQL_SP2, 0 ); - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - /* pINChI sp2 info is either different or trivial. Output pINChI_Prev anyway */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - if ( (Stereo_Prev = pINChI_Prev->Stereo) && Stereo_Prev->nNumberOfStereoBonds > 0 ) { - /* pINChI_Prev exists and has sp2 info */ - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - - tot_len += MakeStereoString( Stereo_Prev->nBondAtom1, Stereo_Prev->nBondAtom2, - Stereo_Prev->b_parity, - 0, Stereo_Prev->nNumberOfStereoBonds, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - /* else sp2 info is not present in pINChI_Prev */ - } else - if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - if ( (Stereo_Taut_Prev = pINChI_Taut_Prev->Stereo) && Stereo_Taut_Prev->nNumberOfStereoBonds > 0 ) { - /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ - /* and it has non-trivial sp2 info */ - /* - tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); - */ - ;/* pINChI_Taut_Prev sp2 info was output in the main stereo section */ - } else { - ; /* pINChI_Taut_Prev exists and has not sp2 info */ - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; /* */ - } -#endif - } - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; /* we do not know whether the item is empty */ - } - } - return tot_len; -} - -/***************************************************************************/ -int str_Sp3(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bRelRac, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) -{ - int i, ii, ii2; - INCHI_SORT *is, *is2, *is0, *is20; - INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; - INChI_Stereo *Stereo, *Stereo_Prev, *Stereo_Taut, *Stereo_Taut_Prev; - int mult, eq2prev, eq2taut, eq2tautPrev, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - pINChI_Taut = NULL; - pINChI_Prev = NULL; - pINChI_Taut_Prev = NULL; - mult = 0; - bNext = 0; - is = NULL; - is2 = NULL; - is0 = pINChISort; - is20 = bSecondNonTautPass? pINChISort2 : NULL; - eq2taut = 0; /* may be non-zero only on the 2nd (non-taut) pass */ - eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) -#else - bRelRac = 0; -#endif - for ( i = 0; i <= num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; - /*================ compare sp3 to previous =====================*/ - if ( bSecondNonTautPass ) { - /* component that was output on the 1st pass */ - pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; - } - /*========= if bSecondNonTautPass then compare non-iso non-taut stereo to non-iso taut ========*/ - eq2taut = 0; -#if ( FIX_EMPTY_LAYER_BUG == 1 ) - if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions && pINChI && pINChI_Taut ) { - Stereo = pINChI->Stereo; - Stereo_Taut = pINChI_Taut->Stereo; - eq2taut = Stereo && Stereo_Taut && - Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Taut, EQL_SP3, bRelRac ); - eq2taut = eq2taut? (iiSTEREO | iitNONTAUT) : 0; - if ( !eq2taut && - !Eql_INChI_Stereo( Stereo, EQL_SP3, NULL, EQL_EXISTS, 0 ) && - Eql_INChI_Stereo( Stereo_Taut, EQL_SP3, NULL, EQL_EXISTS, 0 ) ) { - eq2taut = iiEmpty; /* the current is empty while the preceding (taut) is not */ - } - } -#else - if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions ) { - eq2taut = pINChI && pINChI_Taut && - (Stereo = pINChI->Stereo) && (Stereo_Taut = pINChI_Taut->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Taut, EQL_SP3, bRelRac ); - eq2taut = eq2taut? (iiSTEREO | iitNONTAUT) : 0; - } -#endif - if ( eq2taut ) { - /* we may be here only in case of the second (non-taut) pass */ - /* current non-taut stereo has been found to be same as tautomeric */ - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - /* previous component exists; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( (Stereo_Prev = pINChI_Prev->Stereo) && Stereo_Prev->nNumberOfStereoCenters > 0 ) { - /* non-empty item */ - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - - tot_len += MakeStereoString( Stereo_Prev->nNumber, NULL, - Stereo_Prev->t_parity, - 0, Stereo_Prev->nNumberOfStereoCenters, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - } else - if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - /* previous non-taut component exists only in taut list */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - } - /* we have found pINChI->Stereo sp3 same as in pINChI_Taut */ - /* output this (current) equivalence as '*', that is, same as tautomeric */ - /* that was printed on the 1st pass. */ - - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - - pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ - pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ - mult = 0; - eq2tautPrev = 1; /* pINChI_Prev sp2 does not exist */ - } else - if ( eq2tautPrev ) { - /* at this point pINChI_Prev does not exist; however, pINChI */ - /*might have been discovered and it is different from pINChI_Taut */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - eq2tautPrev = 0; - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; - } else { - /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp3 */ - /*================ compare sp3 to previous =====================*/ - eq2prev =bUseMulipliers && - pINChI && pINChI_Prev && - (Stereo = pINChI->Stereo) && (Stereo_Prev = pINChI_Prev->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Prev, EQL_SP3, bRelRac ); - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - if ( (Stereo_Prev = pINChI_Prev->Stereo) && Stereo_Prev->nNumberOfStereoCenters > bRelRac ) { - /* pINChI_Prev exists and has sp3 info */ - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - - tot_len += MakeStereoString( Stereo_Prev->nNumber, NULL, Stereo_Prev->t_parity, - 0, Stereo_Prev->nNumberOfStereoCenters, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - /* else sp3 info is not present in pINChI_Prev */ - } else - if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - if ( (Stereo_Taut_Prev = pINChI_Taut_Prev->Stereo) && Stereo_Taut_Prev->nNumberOfStereoCenters > bRelRac ) { - /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ - /* and it has non-trivial sp3 info. This info has already been printed in the main section */ - /* - tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); - */ - ; /* pINChI_Taut_Prev sp3 info was output in the main stereo section */ - } else { - ; /* pINChI_Taut_Prev exists and has not sp3 info */ - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; /* */ - } -#endif - } - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; /* we do not know whether the item is empty */ - } - } - return tot_len; -} -/***************************************************************************/ -int str_IsoAtoms(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bAbcNumbers, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) -{ - int i, ii, ii2; - INCHI_SORT *is, *is2, *is0, *is20; - INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; - int mult, eq2prev, eq2taut, eq2tautPrev, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - pINChI_Taut = NULL; - pINChI_Prev = NULL; - pINChI_Taut_Prev = NULL; - mult = 0; - bNext = 0; - is = NULL; - is2 = NULL; - is0 = pINChISort; - is20 = bSecondNonTautPass? pINChISort2 : NULL; - eq2taut = 0; /* may be non-zero only on the 2nd (non-taut) pass */ - eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; - for ( i = 0; i <= num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; - /*================ compare isotopic info to previous component =====================*/ - if ( bSecondNonTautPass ) { - /* component that was output on the 1st pass */ - pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; - } - /*========= if bSecondNonTautPass then compare iso non-taut to taut non-iso ========*/ - eq2taut = 0; - if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions ) { - eq2taut = Eql_INChI_Isotopic( pINChI, pINChI_Taut ); - eq2taut = eq2taut? (iiNUMB | iitNONTAUT) : 0; - } - if ( eq2taut ) { - /* we may be here only in case of the second (non-taut) pass */ - /* current non-taut isotopic info has been found to be same as current tautomeric */ - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - /* previous component exists; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Prev && (pINChI_Prev->nNumberOfIsotopicAtoms > 0 || - pINChI_Prev->nNumberOfIsotopicTGroups > 0) ) { - /* non-empty item */ - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - /* Isotopic atoms */ - if ( pINChI_Prev->nNumberOfIsotopicAtoms > 0 && nStrLen-tot_len > 2 && !*bOverflow ) { /* dereferenced bOverflow 2004-06-07 */ - tot_len += MakeIsoAtomString( pINChI_Prev->IsotopicAtom, pINChI_Prev->nNumberOfIsotopicAtoms, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - /* Isotopic tautomeric groups */ - if ( pINChI_Prev->nNumberOfIsotopicTGroups > 0 && nStrLen-tot_len > 3 && !*bOverflow ) { - tot_len += MakeDelim( bAbcNumbers? ITEM_DELIMETER : "(", pStr + tot_len, nStrLen-tot_len, bOverflow); - tot_len += MakeIsoTautString( pINChI_Prev->IsotopicTGroup, pINChI_Prev->nNumberOfIsotopicTGroups, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - if ( !bAbcNumbers ) { - tot_len += MakeDelim( ")", pStr + tot_len, nStrLen-tot_len, bOverflow); - } - } - } - } else - if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - /* previous non-taut component exists only in taut list */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - } - /* we have found pINChI isotopic info to be same as in pINChI_Taut */ - /* output this (current) equivalence as '*', that is, same as tautomeric */ - /* that was printed on the 1st pass. */ - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - pINChI_Prev = NULL; /* pINChI_Prev isotopic info does not exist since */ - pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ - mult = 0; - eq2tautPrev = 1; /* pINChI_Prev isotopic info does not exist */ - } else - if ( eq2tautPrev ) { - /* at this point pINChI_Prev does not exist; however, pINChI */ - /* might have been discovered and it is different from pINChI_Taut */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - eq2tautPrev = 0; - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; - } else { - /*================ compare iso composition to previous =====================*/ - /* check whether pINChI and pINChI_Prev have non-zero identical isotopic info */ - eq2prev =bUseMulipliers && Eql_INChI_Isotopic( pINChI, pINChI_Prev ); - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - /* pINChI isotopic info is either different or empty. Output pINChI_Prev anyway */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - if ( (pINChI_Prev->nNumberOfIsotopicAtoms > 0 || - pINChI_Prev->nNumberOfIsotopicTGroups > 0) ) { - /* pINChI_Prev exists and has isotopic info */ - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - /* Isotopic atoms */ - if ( pINChI_Prev->nNumberOfIsotopicAtoms > 0 && nStrLen-tot_len > 2 && !*bOverflow ) { - tot_len += MakeIsoAtomString( pINChI_Prev->IsotopicAtom, pINChI_Prev->nNumberOfIsotopicAtoms, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - /* Isotopic tautomeric groups */ - if ( pINChI_Prev->nNumberOfIsotopicTGroups > 0 && nStrLen-tot_len > 3 && !*bOverflow ) { - tot_len += MakeDelim( bAbcNumbers? ITEM_DELIMETER : "(", pStr + tot_len, nStrLen-tot_len, bOverflow); - tot_len += MakeIsoTautString( pINChI_Prev->IsotopicTGroup, pINChI_Prev->nNumberOfIsotopicTGroups, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - if ( !bAbcNumbers ) { - tot_len += MakeDelim( ")", pStr + tot_len, nStrLen-tot_len, bOverflow); - } - } - } - /* else isotopic info is not present in pINChI_Prev */ - } else - if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - if ( (pINChI_Taut_Prev->nNumberOfIsotopicAtoms > 0 || - pINChI_Taut_Prev->nNumberOfIsotopicTGroups > 0) ) { - /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ - /* and it has non-trivial isotopic info */ - /* - tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); - */ - ;/* pINChI_Taut_Prev isotopic info was output in the main isotopic section */ - } else { - ; /* pINChI_Taut_Prev exists and has not isotopic info */ - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; /* */ - } -#endif - } - /* Fix17: moved here 2004-10-08 */ - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; /* we do not know whether the item is empty */ - } - /* Fix17: moved from here 2004-10-08 - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; - */ - } - return tot_len; -} -/***************************************************************************/ -int str_IsoSp2(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) -{ - int i, ii, ii2; - INCHI_SORT *is, *is2, *is0, *is20; - INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; - INChI_Stereo *Stereo, *Stereo_Prev, *Stereo_Taut, *Stereo_Taut_Prev; - int mult, eq2prev, eq2taut, eq2tautPrev, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - pINChI_Taut = NULL; - pINChI_Prev = NULL; - pINChI_Taut_Prev = NULL; - mult = 0; - bNext = 0; - is = NULL; - is2 = NULL; - is0 = pINChISort; - is20 = bSecondNonTautPass? pINChISort2 : NULL; - eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ - eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; - for ( i = 0; i <= num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; - /*================ compare sp2 to previous =====================*/ - if ( bSecondNonTautPass ) { - /* component that was output on the 1st pass */ - pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; - } - eq2taut = 0; - /*========= if bSecondNonTautPass then compare iso non-taut stereo to other stereo ========*/ - if ( bSecondNonTautPass && bOmitRepetitions ) { - /* compare non-tautomeric isotopic to: - * a) non-tautomeric non-isotopic - * b) tautomeric non-isotopic - * c) tautomeric isotopic - */ - /* a) compare non-tautomeric isotopic to non-tautomeric non-isotopic */ - if ( !eq2taut ) { - eq2taut = pINChI && - /* non-taut isotopic */ /* non-taut non-isotopic */ - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Taut, EQL_SP2, 0 ); - /* stereo isotopic non-taut = non-taut (stereo) */ - eq2taut = eq2taut? (iiSTEREO | iitISO | iitNONTAUT | iiEq2NONTAUT ) : 0; - } - /* b) compare non-tautomeric isotopic to tautomeric non-isotopic */ - if ( !eq2taut ) { - eq2taut = pINChI && pINChI_Taut && - /* non-taut isotopic */ /* taut non-isotopic */ - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Taut, EQL_SP2, 0 ); - /* stereo isotopic non-taut = taut (stereo) */ - eq2taut = eq2taut? (iiSTEREO | iitISO | iitNONTAUT ) : 0; - } - /* c) compare non-tautomeric isotopic to tautomeric isotopic */ - if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions ) { - eq2taut = pINChI && pINChI_Taut && - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Taut, EQL_SP2, 0 ); - /* stereo isotopic non-taut = isotopic taut (stereo) */ - eq2taut = eq2taut? (iiSTEREO | iitISO | iitNONTAUT | iiEq2ISO) : 0; - } -#if ( FIX_EMPTY_LAYER_BUG == 1 ) - if ( !eq2taut && pINChI && !((Stereo = pINChI->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP2, NULL, EQL_EXISTS, 0 )) ) { - /* component has no stereo; check whether it has stereo in the preceding layers */ - if ( pINChI_Taut && (Stereo_Taut = pINChI_Taut->Stereo) && /* F is not empty */ - Eql_INChI_Stereo( Stereo_Taut, EQL_SP2, NULL, EQL_EXISTS, 0 ) || - !(pINChI_Taut && (Stereo_Taut = pINChI_Taut->Stereo) && /* M is empty and ... */ - Eql_INChI_Stereo( Stereo_Taut, EQL_SP2, NULL, EQL_EXISTS, 0 )) && - (pINChI_Taut && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && /* ... MI is not empty */ - Eql_INChI_Stereo( Stereo_Taut, EQL_SP2, NULL, EQL_EXISTS, 0 )) ) { - - eq2taut = iiEmpty; /* the component has stereo in the preceding layer */ - } - } -#endif - } else - /*========= if not bSecondNonTautPass then compare iso taut stereo to non-iso taut ========*/ - if ( !bSecondNonTautPass && bOmitRepetitions ) { - /* compare tautomeric isotopic to tautomeric non-isotopic */ - if ( !eq2taut ) { - eq2taut = pINChI && - /* taut isotopic */ /* taut non-isotopic */ - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Taut, EQL_SP2, 0 ); - /* stereo isotopic taut = taut (stereo) */ - eq2taut = eq2taut? (iiSTEREO | iitISO ) : 0; -#if ( FIX_EMPTY_LAYER_BUG == 1 ) - if ( !eq2taut && pINChI && !((Stereo = pINChI->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP2, NULL, EQL_EXISTS, 0 ) ) ) { - /* component has no MI stereo; check whether it has stereo in the preceding layer M */ - if ( (Stereo_Taut = pINChI->Stereo) && - Eql_INChI_Stereo( Stereo_Taut, EQL_SP2, NULL, EQL_EXISTS, 0 ) ) { - eq2taut = iiEmpty; /* the component has stereo in the preceding layer */ - } - } -#endif - } - } - if ( eq2taut ) { - /* we may be here only in case of the current layer found equal in another layer the same component */ - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - /* previous component exists; output it before output the current component */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( (Stereo_Prev = pINChI_Prev->StereoIsotopic) && Stereo_Prev->nNumberOfStereoBonds > 0 ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - - tot_len += MakeStereoString( Stereo_Prev->nBondAtom1, Stereo_Prev->nBondAtom2, - Stereo_Prev->b_parity, - 0, Stereo_Prev->nNumberOfStereoBonds, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - } else - if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - /* previous non-taut component exists only in taut list */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - /* do not output stereo of non-tautomeric in non-taut layer: it has been output in the main layer */ - } - /* we have found another (previously printed) layer of the current component equal to this layer */ - /* output this (current) equivalence mark = EquString(eq2taut) */ - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ - pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ - mult = 0; - eq2tautPrev = 1; /* pINChI_Prev and pINChI_Taut_Prev have already been output */ - } else - if ( eq2tautPrev ) { - /* at this point pINChI_Prev does not exist; however, pINChI */ - /*might have been discovered and it is different from pINChI_Taut */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - eq2tautPrev = 0; - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; - } else { - /* current layer is different from previously printed layers of the current component */ - /* compare the current layer to this layer of the previous component: */ - /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp2 */ - /*================ compare iso sp2 to previous =====================*/ - eq2prev =bUseMulipliers && - pINChI && pINChI_Prev && - (Stereo = pINChI->StereoIsotopic) && (Stereo_Prev = pINChI_Prev->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Prev, EQL_SP2, 0 ); - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - /* the current layer is different from this layer of the previous component */ - /* therefore print the current layer */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - if ( (Stereo_Prev = pINChI_Prev->StereoIsotopic) && Stereo_Prev->nNumberOfStereoBonds > 0 ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - - tot_len += MakeStereoString( Stereo_Prev->nBondAtom1, Stereo_Prev->nBondAtom2, - Stereo_Prev->b_parity, - 0, Stereo_Prev->nNumberOfStereoBonds, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - /* else sp2 info is not present in pINChI_Prev */ - } else - /* do not print pINChI_Prev because it either do not exist of have already been printed */ - if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - if ( (Stereo_Taut_Prev = pINChI_Taut_Prev->StereoIsotopic) && Stereo_Taut_Prev->nNumberOfStereoBonds > 0 ) { - /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ - /* and it has non-trivial sp2 info */ - /* - tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); - */ - ;/* pINChI_Taut_Prev sp3 info was output in the main stereo section */ - } else { - ; /* pINChI_Taut_Prev exists and has not sp2 info */ - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; /* */ - } -#endif - } - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; /* we do not know whether the item is empty */ - } - } - return tot_len; -} -/******************************************************************************************/ -int str_IsoSp3(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bRelRac, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) -{ - int i, ii, ii2; - INCHI_SORT *is, *is2, *is0, *is20; - INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; - INChI_Stereo *Stereo, *Stereo_Prev, *Stereo_Taut, *Stereo_Taut_Prev; - int mult, eq2prev, eq2taut, eq2tautPrev, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - pINChI_Taut = NULL; - pINChI_Prev = NULL; - pINChI_Taut_Prev = NULL; - mult = 0; - bNext = 0; - is = NULL; - is2 = NULL; - is0 = pINChISort; - is20 = bSecondNonTautPass? pINChISort2 : NULL; - eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ - eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) -#else - bRelRac = 0; -#endif - for ( i = 0; i <= num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; - /*================ compare sp2 to previous =====================*/ - if ( bSecondNonTautPass ) { - /* component that was output on the 1st pass */ - pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; - } - eq2taut = 0; - /*========= if bSecondNonTautPass then compare iso non-taut stereo to other stereo ========*/ - if ( bSecondNonTautPass && bOmitRepetitions ) { - /* compare non-tautomeric isotopic to: - * a) non-tautomeric non-isotopic - * b) tautomeric non-isotopic - * c) tautomeric isotopic - */ - /* a) compare non-tautomeric isotopic to non-tautomeric non-isotopic */ - if ( !eq2taut ) { - eq2taut = pINChI && /* non-taut isotopic */ /* non-taut non-isotopic */ - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Taut, EQL_SP3, bRelRac ); - /* stereo isotopic non-taut = non-taut (stereo) */ - eq2taut = eq2taut? (iiSTEREO | iitISO | iitNONTAUT | iiEq2NONTAUT ) : 0; - } - /* b) compare non-tautomeric isotopic to tautomeric non-isotopic */ - if ( !eq2taut ) { - eq2taut = pINChI && pINChI_Taut && - /* non-taut isotopic */ /* taut non-isotopic */ - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Taut, EQL_SP3, bRelRac ); - /* stereo isotopic non-taut = taut (stereo) */ - eq2taut = eq2taut? (iiSTEREO | iitISO | iitNONTAUT ) : 0; - } - /* c) compare non-tautomeric isotopic to tautomeric isotopic */ - if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions ) { - eq2taut = pINChI && pINChI_Taut && - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Taut, EQL_SP3, bRelRac ); - /* stereo isotopic non-taut = isotopic taut (stereo) */ - eq2taut = eq2taut? (iiSTEREO | iitISO | iitNONTAUT | iiEq2ISO) : 0; - } -#if ( FIX_EMPTY_LAYER_BUG == 1 ) - if ( !eq2taut && pINChI && !((Stereo = pINChI->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP3, NULL, EQL_EXISTS, 0 )) ) { - /* component has no stereo; check whether it has stereo in the preceding layers */ - if ( pINChI_Taut && (Stereo_Taut = pINChI_Taut->Stereo) && /* F is not empty */ - Eql_INChI_Stereo( Stereo_Taut, EQL_SP3, NULL, EQL_EXISTS, 0 ) || - !(pINChI_Taut && (Stereo_Taut = pINChI_Taut->Stereo) && /* M is empty and ... */ - Eql_INChI_Stereo( Stereo_Taut, EQL_SP3, NULL, EQL_EXISTS, 0 )) && - (pINChI_Taut && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && /* ... MI is not empty */ - Eql_INChI_Stereo( Stereo_Taut, EQL_SP3, NULL, EQL_EXISTS, 0 )) ) { - - eq2taut = iiEmpty; /* the component has stereo in the preceding layer */ - } - } -#endif - } else - /*========= if not bSecondNonTautPass then compare iso taut stereo to non-iso taut ========*/ - if ( !bSecondNonTautPass && bOmitRepetitions ) { - /* compare tautomeric isotopic to tautomeric non-isotopic */ - if ( !eq2taut ) { - eq2taut = pINChI && - /* taut isotopic */ /* taut non-isotopic */ - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Taut, EQL_SP3, bRelRac ); - /* stereo isotopic taut = taut (stereo) */ - eq2taut = eq2taut? (iiSTEREO | iitISO ) : 0; -#if ( FIX_EMPTY_LAYER_BUG == 1 ) - if ( !eq2taut && pINChI && !((Stereo = pINChI->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP3, NULL, EQL_EXISTS, 0 ) ) ) { - /* component has no MI stereo; check whether it has stereo in the preceding layer M */ - if ( (Stereo_Taut = pINChI->Stereo) && - Eql_INChI_Stereo( Stereo_Taut, EQL_SP3, NULL, EQL_EXISTS, 0 ) ) { - eq2taut = iiEmpty; /* the component has stereo in the preceding layer */ - } - } -#endif - } - } - if ( eq2taut ) { - /* we may be here only in case of the current layer found equal in another layer the same component */ - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - /* previous component exists; output it before output the current component */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( (Stereo_Prev = pINChI_Prev->StereoIsotopic) && Stereo_Prev->nNumberOfStereoCenters > bRelRac ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - - tot_len += MakeStereoString( Stereo_Prev->nNumber, NULL, Stereo_Prev->t_parity, - 0, Stereo_Prev->nNumberOfStereoCenters, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - } else - if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - /* previous non-taut component exists only in taut list */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - /* do not output stereo of non-tautomeric in non-taut layer: it has been output in the main layer */ - } - /* we have found another (previously printed) layer of the current component equal to this layer */ - /* output this (current) equivalence mark = EquString(eq2taut) */ - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ - pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ - mult = 0; - eq2tautPrev = 1; /* pINChI_Prev and pINChI_Taut_Prev have already been output */ - } else - if ( eq2tautPrev ) { - /* at this point pINChI_Prev does not exist; however, pINChI */ - /*might have been discovered and it is different from pINChI_Taut */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - eq2tautPrev = 0; - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; - } else { - /* current layer is different from previously printed layers of the current component */ - /* compare the current layer to this layer of the previous component: */ - /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp2 */ - /*================ compare iso sp3 to previous =====================*/ - eq2prev =bUseMulipliers && pINChI && pINChI_Prev && - (Stereo = pINChI->StereoIsotopic) && (Stereo_Prev = pINChI_Prev->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Prev, EQL_SP3, bRelRac ); - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - if ( (Stereo_Prev = pINChI_Prev->StereoIsotopic) && Stereo_Prev->nNumberOfStereoCenters > bRelRac ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - - tot_len += MakeStereoString( Stereo_Prev->nNumber, NULL, Stereo_Prev->t_parity, - 0, Stereo_Prev->nNumberOfStereoCenters, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - /* else sp3 info is not present in pINChI_Prev */ - } else - /* do not print pINChI_Prev because it either do not exist of have already been printed */ - if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - if ( (Stereo_Taut_Prev = pINChI_Taut_Prev->StereoIsotopic) && Stereo_Taut_Prev->nNumberOfStereoCenters > bRelRac ) { - /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ - /* and it has non-trivial sp2 info */ - /* - tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); - */ - ;/* pINChI_Taut_Prev sp3 info was output in the main stereo section */ - } else { - ; /* pINChI_Taut_Prev exists and has not sp3 info */ - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; /* */ - } -#endif - } - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; /* we do not know whether the item is empty */ - } - } - return tot_len; -} -/***************************************************************************/ -int str_AuxEqu(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) -{ - int i, ii, ii2; - INCHI_SORT *is, *is2, *is0, *is20; - INChI_Aux *pINChI_Aux = NULL, *pINChI_Aux_Prev, *pINChI_Aux_Taut, *pINChI_Aux_Taut_Prev; - int mult, eq2prev, eq2taut, eq2tautPrev, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - pINChI_Aux_Prev = NULL; - pINChI_Aux_Taut = NULL; - pINChI_Aux_Taut_Prev = NULL; - - mult = 0; - bNext = 0; - is = NULL; - is2 = NULL; - is0 = pINChISort; - is20 = bSecondNonTautPass? pINChISort2 : NULL; - eq2taut = 0; /* may be non-zero only on the 2nd (non-taut) pass */ - eq2tautPrev = 1; /* pINChI_Aux_Prev (previous pINChI_Aux) does not exist */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; - for ( i = 0; i <= num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - pINChI_Aux = (i < num_components && (is = is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI_Aux[ii] : NULL; - if ( bSecondNonTautPass ) { - /* component that was output on the 1st pass */ - pINChI_Aux_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI_Aux[ii2] : NULL; - } - /*================ compare non-iso non-taut equivalence info to non-iso taut ========*/ - eq2taut = bSecondNonTautPass && bOmitRepetitions && - Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU, pINChI_Aux_Taut, EQL_EQU ); - eq2taut = eq2taut? (iiEQU | iitNONTAUT) : 0; - if ( eq2taut ) { - /* we may be here only in case of the second (non-taut) pass */ - /* current non-taut equivalence has been found to be same as tautomeric */ - if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { - /* previous component exists */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( bHasEquString( pINChI_Aux_Prev->nConstitEquNumbers, pINChI_Aux_Prev->nNumberOfAtoms) ) { - /* output previous component(s) equivalence since it was found to be non-trivial */ - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - tot_len += MakeEquString( pINChI_Aux_Prev->nConstitEquNumbers, pINChI_Aux_Prev->nNumberOfAtoms, 0, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } else { - ; /* pINChI_Aux_Prev exists and has only trivial equivalence info */ - } - } else - if ( pINChI_Aux_Taut_Prev && pINChI_Aux_Taut_Prev->nNumberOfAtoms ) { - /* previous non-taut component exists only in taut list */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - } - /* we have found pINChI_Aux->nConstitEquNumbers same as in pINChI_Aux_Taut */ - /* output this (current) equivalence as '*', that is, same as tautomeric */ - /* that was printed on the 1st pass. */ - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - pINChI_Aux_Prev = NULL; /* pINChI_Aux_Prev does not exist since */ - pINChI_Aux_Taut_Prev = NULL; /* pINChI_Aux has just been printed */ - mult = 0; - eq2tautPrev = 1; - } else - if ( eq2tautPrev ) { - /* at this point pINChI_Aux_Prev does not exist; however, pINChI_Aux */ - /*might have been discovered and it is different from pINChI_Aux_Taut */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - eq2tautPrev = 0; - pINChI_Aux_Prev = pINChI_Aux; - pINChI_Aux_Taut_Prev = pINChI_Aux_Taut; - mult = 0; - } else { - /* check whether pINChI_Aux and pINChI_Aux_Prev have identical non-trivial equivalence info */ - eq2prev = bUseMulipliers && - Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU, pINChI_Aux_Prev, EQL_EQU ); - if ( eq2prev ) { - /* eq. info is same and non-trivial */ - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - /* pINChI_Aux eq. info is either different or trivial. Output pINChI_Aux_Prev anyway */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { - if ( bHasEquString( pINChI_Aux_Prev->nConstitEquNumbers, pINChI_Aux_Prev->nNumberOfAtoms) ) { - /* pINChI_Aux_Prev exists and has equivalence info */ - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - tot_len += MakeEquString( pINChI_Aux_Prev->nConstitEquNumbers, pINChI_Aux_Prev->nNumberOfAtoms, 0, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } else { - ; /* pINChI_Aux_Prev exists and has only trivial equivalence info */ - } - } else - if ( bSecondNonTautPass && pINChI_Aux_Taut_Prev && pINChI_Aux_Taut_Prev->nNumberOfAtoms ) { - if ( bHasEquString( pINChI_Aux_Taut_Prev->nConstitEquNumbers, pINChI_Aux_Taut_Prev->nNumberOfAtoms) ) { - /* since pINChI_Aux_Prev does not exist, pINChI_Aux_Taut_Prev is non-tautomeric */ - /* and it has non-trivial equivalence info. This info has already been printed in the main section */ - /* - tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); - */ - } else { - ; /* pINChI_Aux_Taut_Prev exists and has only trivial equivalence info */ - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; /* */ - } -#endif - } - pINChI_Aux_Prev = pINChI_Aux; - pINChI_Aux_Taut_Prev = pINChI_Aux_Taut; - mult = 0; /* we do not know whether the item is empty */ - } - } - return tot_len; -} -/******************************************************************************************/ -int str_AuxInvSp3(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) -{ - int i, ii, ii2; - INCHI_SORT *is, *is2, *is0, *is20; - INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; - INChI_Stereo *Stereo, *Stereo_Prev, *Stereo_Taut, *Stereo_Taut_Prev; - int mult, eq2prev, eq2taut, eq2tautPrev, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - /*************** - inverted sp3 - ****************/ - pINChI_Taut = NULL; - pINChI_Prev = NULL; - pINChI_Taut_Prev = NULL; - mult = 0; - bNext = 0; - is = NULL; - is2 = NULL; - is0 = pINChISort; - is20 = bSecondNonTautPass? pINChISort2 : NULL; - eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ - eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; - for ( i = 0; i <= num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; - /*================ compare sp2 to previous =====================*/ - if ( bSecondNonTautPass ) { - /* component that was output on the 1st pass */ - pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; - } - eq2taut = 0; - /*========= if bSecondNonTautPass then compare iso non-taut stereo to other stereo ========*/ - if ( bSecondNonTautPass && bOmitRepetitions ) { - /* compare non-tautomeric inverted to: - * a) tautomeric inverted - * b) Inverted(tautomeric) - * c) Inverted(non-tautomeric) - */ - /* a) compare non-tautomeric inverted to tautomeric inverted */ - if ( !eq2taut ) { - eq2taut = pINChI && pINChI_Taut && - /* non-taut inverted */ /* taut invertedc */ - (Stereo = pINChI->Stereo) && (Stereo_Taut = pINChI_Taut->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3_INV, 0 ); - /* stereo-inv non-taut = taut (stereo-inv) */ - eq2taut = eq2taut? (iiSTEREO_INV | iitNONTAUT ) : 0; - } - /* b) compare non-tautomeric inverted to Inverted(tautomeric stereo) */ - if ( !eq2taut ) { - eq2taut = pINChI && pINChI_Taut && - (Stereo = pINChI->Stereo) && (Stereo_Taut = pINChI_Taut->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3, 0 ); - /* stereo-inv non-taut = Inv(taut stereo) */ - eq2taut = eq2taut? (iiSTEREO_INV | iitNONTAUT | iiEq2INV) : 0; - } - /* c) compare non-tautomeric inverted to Inverted(non-tautomeric stereo) */ - if ( !eq2taut ) { - eq2taut = pINChI && - (Stereo = pINChI->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo, EQL_SP3, 0 ); - /* stereo-inv non-taut = Inv(non-taut stereo) */ - eq2taut = eq2taut? (iiSTEREO_INV | iitNONTAUT | iiEq2INV | iiEq2NONTAUT) : 0; - } -#if ( FIX_EMPTY_LAYER_BUG == 1 ) - if ( !eq2taut && pINChI && pINChI_Taut && - !((Stereo = pINChI->Stereo) && Eql_INChI_Stereo( Stereo, EQL_SP3_INV, NULL, EQL_EXISTS, 0 ))) { - if ( (Stereo_Taut = pINChI_Taut->Stereo) && - Eql_INChI_Stereo( Stereo_Taut, EQL_SP3, NULL, EQL_EXISTS, 0 ) ) { - - eq2taut = iiEmpty; /* the current is empty while the preceding (taut) is not */ - } - } -#endif - } else - /*========= if not bSecondNonTautPass then compare inv taut stereo to various taut stereo ========*/ - if ( !bSecondNonTautPass && bOmitRepetitions ) { - /* compare tautomeric inverted to Invetred(tautomeric) */ - if ( !eq2taut ) { - eq2taut = pINChI && - (Stereo = pINChI->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo, EQL_SP3, 0 ); - /* stereo isotopic taut = taut (stereo) */ - eq2taut = eq2taut? (iiSTEREO_INV | iiEq2INV ) : 0; - } - } - if ( eq2taut ) { - /* we may be here only in case of the current layer found equal in another layer the same component */ - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - /* previous component exists; output it before output the current component */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( (Stereo_Prev = pINChI_Prev->Stereo) && Stereo_Prev->nNumberOfStereoCenters > 0 ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - - tot_len += MakeStereoString( Stereo_Prev->nNumber, NULL, Stereo_Prev->t_parityInv, - 0, Stereo_Prev->nNumberOfStereoCenters, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - } else - if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - /* previous non-taut component exists only in taut list */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - /* do not output stereo of non-tautomeric in non-taut layer: it has been output in the main layer */ - } - /* we have found another (previously printed) layer of the current component equal to this layer */ - /* output this (current) equivalence mark = EquString(eq2taut) */ - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ - pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ - mult = 0; - eq2tautPrev = 1; /* pINChI_Prev and pINChI_Taut_Prev have already been output */ - } else - if ( eq2tautPrev ) { - /* at this point pINChI_Prev does not exist; however, pINChI */ - /*might have been discovered and it is different from pINChI_Taut */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - eq2tautPrev = 0; - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; - } else { - /* current layer is different from previously printed layers of the current component */ - /* compare the current layer to this layer of the previous component: */ - /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp2 */ - /*================ compare iso sp3 to previous =====================*/ - eq2prev =bUseMulipliers && - pINChI && pINChI_Prev && - /* do both have stereo? */ - (Stereo = pINChI->Stereo) && (Stereo_Prev = pINChI_Prev->Stereo) && - /* is their inverted stereo same? */ - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Prev, EQL_SP3_INV, 0 ); - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - if ( (Stereo_Prev = pINChI_Prev->Stereo) && - Stereo_Prev->nNumberOfStereoCenters > 0 && Stereo_Prev->nCompInv2Abs ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - - tot_len += MakeStereoString( Stereo_Prev->nNumberInv, NULL, Stereo_Prev->t_parityInv, - 0, Stereo_Prev->nNumberOfStereoCenters, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - /* else sp3 info is not present in pINChI_Prev */ - } else - /* do not print pINChI_Prev because it either do not exist of have already been printed */ - if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - if ( (Stereo_Taut_Prev = pINChI_Taut_Prev->Stereo) && - Stereo_Taut_Prev->nNumberOfStereoCenters > 0 && Stereo_Taut_Prev->nCompInv2Abs ) { - /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ - /* and it has non-trivial inv sp3 info. It has already been printed in the main section */ - /* - tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); - */ - ;/* pINChI_Taut_Prev sp3 info was output in the main stereo section */ - } else { - ; /* pINChI_Taut_Prev exists and has not sp3 info */ - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; /* */ - } -#endif - } - pINChI_Prev = pINChI; - mult = 0; /* we do not know whether the item is empty */ - } - } - return tot_len; -} -/***************************************************************************/ -int str_AuxInvSp3Numb(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions) -{ - int i, ii, ii2; - INCHI_SORT *is, *is0 /*, *is2*/; - INChI *pINChI, *pINChI_Taut; - INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev, *pINChI_Aux_Taut; - INChI_Stereo *Stereo, *Stereo_Taut; - int eq2taut, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - /************************************************** - * specificity of numbering: there is no previous * - * component because no repetition is possible * - **************************************************/ - pINChI = NULL; - pINChI_Taut = NULL; - pINChI_Aux = NULL; - pINChI_Aux_Taut = NULL; - pINChI_Aux_Prev = NULL; - bNext = 0; - is = NULL; - is0 = pINChISort; - /*is2 = bSecondNonTautPass? pINChISort2 : NULL;*/ - eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; - for ( i = 0; i < num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - is=is0+i; - pINChI = (0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; - pINChI_Aux = pINChI? is->pINChI_Aux[ii] : NULL; - /*================ to compare to previously printed =====================*/ - if ( bSecondNonTautPass ) { - /* component that was printed on the 1st pass */ - pINChI_Taut = (0 <= (ii2=GET_II(OUT_T1,is)))? is->pINChI[ii2] : NULL; - pINChI_Aux_Taut = pINChI_Taut? is->pINChI_Aux[ii2] : NULL; - } - - eq2taut = 0; - /*========= if bSecondNonTautPass then compare inv non-taut stereo to other stereo ========*/ - if ( bSecondNonTautPass && bOmitRepetitions && - pINChI && (Stereo = pINChI->Stereo) && Stereo->nCompInv2Abs ) { - /* compare non-tautomeric inverted stereo numbering to: - * a) tautomeric numbering - * b) non-tautomeric numbering - * c) tautomeric inverted stereo numbering - */ - /* a) compare non-tautomeric inverted stereo numbering to tautomeric numbering */ - if ( !eq2taut ) { - eq2taut = pINChI_Taut && - Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV, pINChI_Aux_Taut, EQL_NUM ); - /* stereo-inv numbering non-taut = taut numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iiNUMB | iitNONTAUT ) : 0; - } - /* b) compare non-tautomeric inverted stereo numbering to non-tautomeric numbering */ - if ( !eq2taut ) { - eq2taut = - Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV, pINChI_Aux, EQL_NUM ); - /* stereo-inv numb. non-taut = non-taut numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iiNUMB | iitNONTAUT | iiEq2NONTAUT ) : 0; - } - /* c) compare non-tautomeric inverted stereo numbering to tautomeric inverted stereo numbering */ - if ( !eq2taut ) { - eq2taut = pINChI_Taut && - (Stereo_Taut = pINChI_Taut->Stereo) && Stereo_Taut->nCompInv2Abs && - Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV, pINChI_Aux_Taut, EQL_NUM_INV ); - /* stereo-inv numb. non-taut = taut inv stereo numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iiNUMB | iitNONTAUT | iiEq2INV ) : 0; - } - } else - /*========= if not bSecondNonTautPass then compare inv taut stereo numb to taut numb ========*/ - if ( !bSecondNonTautPass && bOmitRepetitions && - pINChI && (Stereo = pINChI->Stereo) && Stereo->nCompInv2Abs ) { - /* compare tautomeric inverted stereo numbering to tautomeric numbering */ - if ( !eq2taut ) { - eq2taut = - Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV, pINChI_Aux, EQL_NUM ); - /* stereo-inv numbering (taut) = taut numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iiNUMB ) : 0; - } - } - if ( eq2taut ) { - /* we have found another (previously printed) layer of the current component equal to this layer */ - /* output this (current) equivalence mark = EquString(eq2taut) */ - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - /* current layer is different from previously printed layers of the current component */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI && pINChI_Aux && pINChI_Aux->nNumberOfAtoms && - (Stereo = pINChI->Stereo) && Stereo->nNumberOfStereoCenters && - Stereo->nCompInv2Abs && pINChI_Aux->nOrigAtNosInCanonOrdInv ) { - tot_len += MakeCtString( pINChI_Aux->nOrigAtNosInCanonOrdInv, - pINChI_Aux->nNumberOfAtoms, 0, NULL, 0, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - /* else inv stereo info is not present in pINChI */ - } - } - if ( multPrevEquStr && pPrevEquStr ) { - /* the new EqStr of the last item has not been printed; output it now */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - return tot_len; -} -/***************************************************************************/ -int str_AuxIsoNumb(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions) -{ - int i, ii, ii2; - INCHI_SORT *is, *is0 /*, *is2*/; - INChI *pINChI, *pINChI_Taut; - INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev, *pINChI_Aux_Taut; - int eq2taut, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - /************************************************** - * specificity of numbering: there is no previous * - * component because no repetition is possible * - **************************************************/ - pINChI = NULL; /* not used here, for debug only */ - pINChI_Taut = NULL; /* not used here, for debug only */ - pINChI_Aux = NULL; - pINChI_Aux_Taut = NULL; - pINChI_Aux_Prev = NULL; - bNext = 0; - is = NULL; - is0 = pINChISort; - /*is2 = bSecondNonTautPass? pINChISort2 : NULL;*/ - eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; - for ( i = 0; i < num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - is=is0+i; - pINChI_Aux = (i < num_components && 0 <= (ii=GET_II(bOutType,is)))? is->pINChI_Aux[ii] : NULL; - /*================ to compare to previously printed =====================*/ - if ( bSecondNonTautPass ) { - pINChI_Aux_Taut = (0 <= (ii2=GET_II(OUT_T1,is)))? is->pINChI_Aux[ii2] : NULL; - } - eq2taut = 0; - /*========= if bSecondNonTautPass then compare iso non-taut numb to other numb ========*/ - if ( bSecondNonTautPass && bOmitRepetitions && pINChI_Aux && pINChI_Aux->bIsIsotopic ) { - /* compare non-tautomeric isotopic numbering to: - * a) tautomeric numbering - * b) non-tautomeric numbering - * c) tautomeric isotopic numbering - */ - /* a) compare non-tautomeric isotopic numbering to tautomeric numbering */ - if ( !eq2taut ) { - eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_ISO, pINChI_Aux_Taut, EQL_NUM ); - /* numbering non-taut isotopic = taut numbering */ - eq2taut = eq2taut? ( iiNUMB | iitNONTAUT | iitISO ) : 0; - } - /* b) compare non-tautomeric isotopic numbering to non-tautomeric numbering */ - if ( !eq2taut ) { - eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_ISO, pINChI_Aux, EQL_NUM ); - /* numbering non-taut isotopic = non-taut numbering */ - eq2taut = eq2taut? ( iiNUMB | iitNONTAUT | iitISO | iiEq2NONTAUT ) : 0; - } - /* c) compare non-tautomeric isotopic numbering to tautomeric isotopic numbering */ - if ( !eq2taut ) { - eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_ISO, pINChI_Aux_Taut, EQL_NUM_ISO ); - /* numbering non-taut isotopic = taut isotopic numbering */ - eq2taut = eq2taut? ( iiNUMB | iitNONTAUT | iitISO | iiEq2ISO ) : 0; - } - } else - /*========= if not bSecondNonTautPass then compare inv taut stereo numb to taut numb ========*/ - if ( !bSecondNonTautPass && bOmitRepetitions && pINChI_Aux && pINChI_Aux->bIsIsotopic ) { - /* compare tautomeric isotopic numbering to tautomeric non-isotopic numbering */ - if ( !eq2taut ) { - eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_ISO, pINChI_Aux, EQL_NUM_ISO ); - /* stereo-inv numbering (taut) = taut numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iiNUMB ) : 0; - } - } - if ( eq2taut ) { - /* we have found another (previously printed) layer of the current component equal to this layer */ - /* output this (current) equivalence mark = EquString(eq2taut) */ - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - /* current layer is different from previously printed layers of the current component */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Aux && pINChI_Aux->nNumberOfAtoms && pINChI_Aux->bIsIsotopic && - pINChI_Aux->nIsotopicOrigAtNosInCanonOrd ) { - tot_len += MakeCtString( pINChI_Aux->nIsotopicOrigAtNosInCanonOrd, - pINChI_Aux->nNumberOfAtoms, 0, NULL, 0, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - /* else isotopic numbering is not present in pINChI */ - } - } - if ( multPrevEquStr && pPrevEquStr ) { - /* the new EqStr of the last item has not been printed; output it now */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - return tot_len; -} -/***************************************************************************/ -int str_AuxIsoEqu(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) -{ - int i, ii, ii2; - INCHI_SORT *is, *is2, *is0, *is20; - INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev, *pINChI_Aux_Taut, *pINChI_Aux_Taut_Prev; - int mult, eq2prev, eq2taut, eq2tautPrev, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - pINChI_Aux = NULL; - pINChI_Aux_Prev = NULL; - pINChI_Aux_Taut = NULL; - pINChI_Aux_Taut_Prev = NULL; - mult = 0; - bNext = 0; - is = NULL; - is2 = NULL; - is0 = pINChISort; - is20 = bSecondNonTautPass? pINChISort2 : NULL; - eq2taut = 0; /* may be non-zero only on the 2nd (non-taut) pass */ - eq2tautPrev = 1; /* pINChI_Aux_Prev (previous pINChI_Aux) does not exist */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; - for ( i = 0; i <= num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - pINChI_Aux = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI_Aux[ii] : NULL; - if ( bSecondNonTautPass ) { - /* component that was output on the 1st pass */ - pINChI_Aux_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI_Aux[ii2] : NULL; - } - /*================ compare iso non-taut equivalence info to non-iso taut ========*/ - eq2taut = 0; - if ( bSecondNonTautPass && bOmitRepetitions && pINChI_Aux && pINChI_Aux->bIsIsotopic ) { - /************************************************** - * compare isotopic non-tautomeric equivalence to: - * a) tautomeric - * b) non-tautomeric - * c) isotopic tautomeric - */ - if ( !eq2taut ) { - /* compare isotopic non-tautomeric equivalence to tautomeric */ - eq2taut = Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_ISO, pINChI_Aux_Taut, EQL_EQU ); - /* equ non-taut isotopic = tautomeric*/ - eq2taut = eq2taut? (iiEQU | iitNONTAUT | iitISO) : 0; - } - if ( !eq2taut ) { - /* compare isotopic non-tautomeric equivalence to non-tautomeric */ - eq2taut = Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_ISO, pINChI_Aux, EQL_EQU ); - /* equ non-taut isotopic = non-tautomeric*/ - eq2taut = eq2taut? (iiEQU | iitNONTAUT | iitISO | iiEq2NONTAUT) : 0; - } - if ( !eq2taut ) { - /* compare isotopic non-tautomeric equivalence to isotopic tautomeric */ - eq2taut = Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_ISO, pINChI_Aux_Taut, EQL_EQU_ISO ); - /* equ non-taut isotopic = isotopic tautomeric*/ - eq2taut = eq2taut? (iiEQU | iitNONTAUT | iitISO | iiEq2ISO) : 0; - } - } else - if ( !bSecondNonTautPass && bOmitRepetitions && pINChI_Aux && pINChI_Aux->bIsIsotopic ) { - /************************************************** - * compare isotopic tautomeric equivalence to: - * a) non-isotopic tautomeric - */ - if ( !eq2taut ) { - /* compare isotopic tautomeric equivalence to tautomeric */ - eq2taut = Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_ISO, pINChI_Aux, EQL_EQU ); - /* equ taut-isotopic = tautomeric*/ - eq2taut = eq2taut? (iiEQU | iitISO) : 0; - } - } - if ( eq2taut ) { - /* we may be here only in case of the second (non-taut) pass */ - /* current non-taut equivalence has been found to be same as tautomeric */ - if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { - /* previous component exists */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( bHasEquString( pINChI_Aux_Prev->nConstitEquIsotopicNumbers, pINChI_Aux_Prev->nNumberOfAtoms) ) { - /* output previous component(s) equivalence since it was found to be non-trivial */ - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - tot_len += MakeEquString( pINChI_Aux_Prev->nConstitEquIsotopicNumbers, pINChI_Aux_Prev->nNumberOfAtoms, 0, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } else { - ; /* pINChI_Aux_Prev exists and has only trivial equivalence info */ - } - } else - if ( pINChI_Aux_Taut_Prev && pINChI_Aux_Taut_Prev->nNumberOfAtoms ) { - /* previous non-taut component exists only in taut list */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - } - /* we have found pINChI_Aux->pINChI_Aux->nConstitEquIsotopicNumbers same as in pINChI_Aux_Taut */ - /* output this (current) equivalence as '*', that is, same as tautomeric */ - /* that was printed on the 1st pass. */ - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - pINChI_Aux_Prev = NULL; /* pINChI_Aux_Prev does not exist since */ - pINChI_Aux_Taut_Prev = NULL; /* pINChI_Aux has just been printed */ - mult = 0; - eq2tautPrev = 1; - } else - if ( eq2tautPrev ) { - /* at this point pINChI_Aux_Prev does not exist; however, pINChI_Aux */ - /*might have been discovered and it is different from pINChI_Aux_Taut */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - eq2tautPrev = 0; - pINChI_Aux_Prev = pINChI_Aux; - pINChI_Aux_Taut_Prev = pINChI_Aux_Taut; - mult = 0; - } else { - /* check whether pINChI_Aux and pINChI_Aux_Prev have identical non-trivial equivalence info */ - eq2prev = bUseMulipliers && Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_ISO, pINChI_Aux_Prev, EQL_EQU_ISO ); - if ( eq2prev ) { - /* eq. info is same and non-trivial */ - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - /* pINChI_Aux eq. info is either different or trivial. Output pINChI_Aux_Prev anyway */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { - if ( bHasEquString( pINChI_Aux_Prev->nConstitEquIsotopicNumbers, pINChI_Aux_Prev->nNumberOfAtoms) ) { - /* pINChI_Aux_Prev exists and has equivalence info */ - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - tot_len += MakeEquString( pINChI_Aux_Prev->nConstitEquIsotopicNumbers, pINChI_Aux_Prev->nNumberOfAtoms, 0, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } else { - ; /* pINChI_Aux_Prev exists and has only trivial equivalence info */ - } - } else - if ( bSecondNonTautPass && pINChI_Aux_Taut_Prev && pINChI_Aux_Taut_Prev->nNumberOfAtoms ) { - if ( bHasEquString( pINChI_Aux_Taut_Prev->nConstitEquIsotopicNumbers, pINChI_Aux_Taut_Prev->nNumberOfAtoms) ) { - /* since pINChI_Aux_Prev does not exist, pINChI_Aux_Taut_Prev is non-tautomeric */ - /* and it has non-trivial equivalence info. This info has already been printed in the main section */ - /* - tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); - */ - } else { - ; /* pINChI_Aux_Taut_Prev exists and has only trivial equivalence info */ - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; /* */ - } -#endif - } - pINChI_Aux_Prev = pINChI_Aux; - pINChI_Aux_Taut_Prev = pINChI_Aux_Taut; - mult = 0; /* we do not know whether the item is empty */ - } - } - return tot_len; -} -/******************************************************************************************/ -int str_AuxInvIsoSp3(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) -{ - int i, ii, ii2; - INCHI_SORT *is, *is2, *is0, *is20; - INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; - INChI_Stereo *Stereo, *Stereo_Prev, *Stereo_Taut, *Stereo_Taut_Prev; - int mult, eq2prev, eq2taut, eq2tautPrev, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - /******************************** - inverted isotopic sp3 - *********************************/ - pINChI_Taut = NULL; - pINChI_Prev = NULL; - pINChI_Taut_Prev = NULL; - mult = 0; - bNext = 0; - is = NULL; - is2 = NULL; - is0 = pINChISort; - is20 = bSecondNonTautPass? pINChISort2 : NULL; - eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ - eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; - for ( i = 0; i <= num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; - /*================ compare sp2 to previous =====================*/ - if ( bSecondNonTautPass ) { - /* component that was output on the 1st pass */ - pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; - } - eq2taut = 0; - /*========= if bSecondNonTautPass then compare iso non-taut stereo to other stereo ========*/ - if ( bSecondNonTautPass && bOmitRepetitions && pINChI && pINChI->nNumberOfIsotopicAtoms+pINChI->nNumberOfIsotopicTGroups > 0 ) { - /* compare non-tautomeric isotopic inverted to: - * a) tautomeric inverted - * b) *non-tautomeric inverted - * c) *isotopic tautomeric inverted - * d) Inverted(tautomeric) - * e) *Inverted(tautomeric isotopic) - * f) Inverted(non-tautomeric) - * g) *Inverted(non-tautomeric isotopic) - */ - /* a) compare non-tautomeric isotopic inverted to tautomeric inverted */ - if ( !eq2taut ) { - eq2taut = pINChI && pINChI_Taut && - /* non-taut inverted */ /* taut invertedc */ - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3_INV, 0 ); - /* stereo-inv isotopic non-taut = taut (stereo-inv) */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT ) : 0; - } - /* b) compare non-tautomeric isotopic inverted to non-tautomeric inverted */ - if ( !eq2taut ) { - eq2taut = pINChI && /* it is non-taut non-iso stereo */ - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3_INV, 0 ); - /* stereo-inv isotopic non-taut = non-taut stereo-inv */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT | iiEq2NONTAUT) : 0; - } - /* c) compare non-tautomeric isotopic inverted to isotopic tautomeric inverted */ - if ( !eq2taut ) { - eq2taut = pINChI && pINChI_Taut && - /* non-taut iso. inverted */ /* taut iso. inverted */ - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3_INV, 0 ); - /* stereo-inv isotopic non-taut = taut iso. stereo-inv */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT | iiEq2ISO ) : 0; - } - /* d) compare non-tautomeric inverted to Inverted(tautomeric stereo) */ - if ( !eq2taut ) { - eq2taut = pINChI && pINChI_Taut && - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3_INV, 0 ); - /* stereo-inv isotopic non-taut = Inv(non-iso taut stereo) */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT | iiEq2INV) : 0; - } - /* e) compare non-tautomeric inverted to Inverted(isotopic tautomeric stereo) */ - if ( !eq2taut ) { - eq2taut = pINChI && pINChI_Taut && - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3, 0 ); - /* stereo-inv isotopic non-taut = Inv(iso taut stereo) */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT | iiEq2INV | iiEq2ISO) : 0; - } - /* f) compare non-tautomeric isotopic inverted to Inverted(non-tautomeric stereo) */ - if ( !eq2taut ) { - eq2taut = pINChI && /* it is non-taut non-iso stereo */ - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3, 0 ); - /* stereo-inv isotopic non-taut = Inv(non-taut stereo) */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT | iiEq2INV | iiEq2NONTAUT) : 0; - } - /* g) compare non-tautomeric isotopic inverted to Inverted(non-tautomeric isotopic stereo) */ - if ( !eq2taut ) { - eq2taut = pINChI && /* it is non-taut non-iso stereo */ - (Stereo = pINChI->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo, EQL_SP3, 0 ); - /* stereo-inv isotopic non-taut = Inv( iso non-taut stereo) */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT | iiEq2INV | iiEq2ISO | iiEq2NONTAUT) : 0; - } -#if ( FIX_EMPTY_LAYER_BUG == 1 ) - if ( !eq2taut && pINChI && !((Stereo = pINChI->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, NULL, EQL_EXISTS, 0 )) ) { - /* component has no stereo; check whether it has stereo in the preceding layers */ - if ( pINChI_Taut && (Stereo_Taut = pINChI_Taut->Stereo) && /* F is not empty */ - Eql_INChI_Stereo( Stereo_Taut, EQL_SP3_INV, NULL, EQL_EXISTS, 0 ) || - !(pINChI_Taut && (Stereo_Taut = pINChI_Taut->Stereo) && /* M is empty and ... */ - Eql_INChI_Stereo( Stereo_Taut, EQL_SP3_INV, NULL, EQL_EXISTS, 0 )) && - (pINChI_Taut && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && /* ... MI is not empty */ - Eql_INChI_Stereo( Stereo_Taut, EQL_SP3_INV, NULL, EQL_EXISTS, 0 )) ) { - - eq2taut = iiEmpty; /* the component has stereo in the preceding layer */ - } - } -#endif - } else - /*========= if not bSecondNonTautPass then compare inv taut stereo to various stereo ========*/ - if ( !bSecondNonTautPass && bOmitRepetitions && pINChI && - (pINChI->nNumberOfIsotopicAtoms > 0 || - pINChI->nNumberOfIsotopicTGroups > 0 || - pINChI->nPossibleLocationsOfIsotopicH && pINChI->nPossibleLocationsOfIsotopicH[0] > 1) ) { - /* compare tautomeric isotopic stereo-inverted to: - * a) tautomeric stereo-inverted - * b) Inverted(tautomeric stereo) - * c) Inverted(tautomeric isotopic stereo) - */ - /* a) compare tautomeric isotopic stereo-inverted to tautomeric stereo-inverted */ - if ( !eq2taut ) { - eq2taut = pINChI && - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3_INV, 0 ); - /* stereo-inv isotopic taut = taut stereo-inv */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO ) : 0; - } - /* b) compare tautomeric isotopic stereo-inverted to Inverted(tautomeric stereo) */ - if ( !eq2taut ) { - eq2taut = pINChI && - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3, 0 ); - /* stereo-inv isotopic taut = Inv(taut stereo) */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiEq2INV ) : 0; - } - /* c) compare tautomeric isotopic stereo-inverted to Inverted(tautomeric isotopic stereo) */ - if ( !eq2taut ) { - eq2taut = pINChI && - (Stereo = pINChI->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo, EQL_SP3, 0 ); - /* stereo-inv isotopic taut = Inv(taut iso stereo) */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiEq2INV | iiEq2ISO ) : 0; - } -#if ( FIX_EMPTY_LAYER_BUG == 1 ) - if ( !eq2taut && pINChI && !((Stereo = pINChI->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, NULL, EQL_EXISTS, 0 ) ) ) { - /* component has no MI stereo; check whether it has stereo in the preceding layer M */ - if ( (Stereo_Taut = pINChI->Stereo) && - Eql_INChI_Stereo( Stereo_Taut, EQL_SP3_INV, NULL, EQL_EXISTS, 0 ) ) { - eq2taut = iiEmpty; /* the component has stereo in the preceding layer */ - } - } -#endif - } - if ( eq2taut ) { - /* we may be here only in case of the current layer found equal in another layer the same component */ - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - /* previous component exists; output it before output the current component */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( (Stereo_Prev = pINChI_Prev->StereoIsotopic) && Stereo_Prev->nNumberOfStereoCenters > 0 ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - - tot_len += MakeStereoString( Stereo_Prev->nNumber, NULL, Stereo_Prev->t_parityInv, - 0, Stereo_Prev->nNumberOfStereoCenters, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - } else - if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - /* previous non-taut component exists only in taut list */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - /* do not output stereo of non-tautomeric in non-taut layer: it has been output in the main layer */ - } - /* we have found another (previously printed) layer of the current component equal to this layer */ - /* output this (current) equivalence mark = EquString(eq2taut) */ - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ - pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ - mult = 0; - eq2tautPrev = 1; /* pINChI_Prev and pINChI_Taut_Prev have already been output */ - } else - if ( eq2tautPrev ) { - /* at this point pINChI_Prev does not exist; however, pINChI */ - /*might have been discovered and it is different from pINChI_Taut */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - eq2tautPrev = 0; - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; - } else { - /* current layer is different from previously printed layers of the current component */ - /* compare the current layer to this layer of the previous component: */ - /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp2 */ - /*================ compare iso sp3 to previous =====================*/ - eq2prev =bUseMulipliers && - pINChI && pINChI->nNumberOfIsotopicAtoms + pINChI->nNumberOfIsotopicTGroups > 0 && - pINChI_Prev && pINChI_Prev->nNumberOfIsotopicAtoms + pINChI_Prev->nNumberOfIsotopicTGroups > 0 && - /* do both have stereo? */ - (Stereo = pINChI->StereoIsotopic) && (Stereo_Prev = pINChI_Prev->StereoIsotopic) && - /* is their inverted stereo same? */ - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Prev, EQL_SP3_INV, 0 ); - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms && pINChI_Prev->nNumberOfIsotopicAtoms + pINChI_Prev->nNumberOfIsotopicTGroups > 0 ) { - if ( (Stereo_Prev = pINChI_Prev->StereoIsotopic) && - Stereo_Prev->nNumberOfStereoCenters > 0 && Stereo_Prev->nCompInv2Abs ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - - tot_len += MakeStereoString( Stereo_Prev->nNumberInv, NULL, Stereo_Prev->t_parityInv, - 0, Stereo_Prev->nNumberOfStereoCenters, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - /* else sp3 info is not present in pINChI_Prev */ - } else - /* do not print pINChI_Prev because it either do not exist of have already been printed */ - if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - if ( (Stereo_Taut_Prev = pINChI_Taut_Prev->StereoIsotopic) && - Stereo_Taut_Prev->nNumberOfStereoCenters > 0 && Stereo_Taut_Prev->nCompInv2Abs ) { - /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ - /* and it has non-trivial inv sp3 info. It has already been printed in the main section */ - /* - tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); - */ - ;/* pINChI_Taut_Prev sp3 info was output in the main stereo section */ - } else { - ; /* pINChI_Taut_Prev exists and has not sp3 info */ - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; /* */ - } -#endif - } - pINChI_Prev = pINChI; - mult = 0; /* we do not know whether the item is empty */ - } - } - return tot_len; -} -/***************************************************************************/ -int str_AuxInvIsoSp3Numb(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions) -{ - int i, ii, ii2; - INCHI_SORT *is, *is0 /*, *is2*/; - INChI *pINChI, *pINChI_Taut; - INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev, *pINChI_Aux_Taut; - INChI_Stereo *Stereo, *Stereo_Taut; - int eq2taut, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - /************************************************** - * specificity of numbering: there is no previous * - * component because no repetition is possible * - **************************************************/ - pINChI = NULL; - pINChI_Taut = NULL; - pINChI_Aux = NULL; - pINChI_Aux_Taut = NULL; - pINChI_Aux_Prev = NULL; - bNext = 0; - is = NULL; - /* is2 = NULL;*/ - is0 = pINChISort; - /* is20 = bSecondNonTautPass? pINChISort2 : NULL;*/ - eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; - for ( i = 0; i < num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - is=is0+i; - pINChI = (0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; - pINChI_Aux = pINChI? is->pINChI_Aux[ii] : NULL; - /*================ to compare to previously printed =====================*/ - if ( bSecondNonTautPass ) { - /* component that was printed on the 1st pass */ - pINChI_Taut = (0 <= (ii2=GET_II(OUT_T1,is)))? is->pINChI[ii2] : NULL; - pINChI_Aux_Taut = pINChI_Taut? is->pINChI_Aux[ii2] : NULL; - } - eq2taut = 0; - /*========= if bSecondNonTautPass then compare iso non-taut stereo to other stereo ========*/ - if ( bSecondNonTautPass && bOmitRepetitions && pINChI && pINChI_Aux && pINChI_Aux->bIsIsotopic && - (Stereo = pINChI->StereoIsotopic) && Stereo->nCompInv2Abs && - pINChI_Aux->nNumberOfAtoms > 0 && pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv ) { - /* compare isotopic non-tautomeric inverted stereo numbering to: - * a) tautomeric numbering - * b) non-tautomeric numbering - * c) *tautomeric isotopic numbering - * d) *non-tautomeric isotopic numbering - * e) tautomeric inverted stereo numbering - * f) *non-tautomeric inverted stereo numbering - * g) tautomeric isotopic inverted stereo numbering - */ - /* a) compare isotopic non-tautomeric inverted stereo numbering to tautomeric numbering */ - if ( !eq2taut ) { - eq2taut = pINChI_Taut && - Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux_Taut, EQL_NUM ); - /* stereo-inv isotopic numbering non-taut = taut numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT ) : 0; - } - /* b) compare isotopic non-tautomeric inverted stereo numbering to non-tautomeric numbering */ - if ( !eq2taut ) { - eq2taut = - Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux, EQL_NUM ); - /* stereo-inv isotopic numb. non-taut = non-taut numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT | iiEq2NONTAUT ) : 0; - } - /* c) compare isotopic non-tautomeric inverted stereo numbering to tautomeric isotopic numbering */ - if ( !eq2taut ) { - eq2taut = pINChI_Taut && - Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux_Taut, EQL_NUM_ISO ); - /* stereo-inv isotopic numb. non-taut = taut iso numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT | iiEq2ISO ) : 0; - } - /* d) compare isotopic non-tautomeric inverted stereo numbering to non-tautomeric isotopic numbering */ - if ( !eq2taut ) { - eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux, EQL_NUM_ISO ); - /* stereo-inv isotopic numb. non-taut = non-taut isotopic numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT | iiEq2NONTAUT | iiEq2ISO) : 0; - } - /* e) compare isotopic non-tautomeric inverted stereo numbering to tautomeric inverted stereo numbering */ - if ( !eq2taut ) { - eq2taut = pINChI_Taut && pINChI_Aux_Taut && - (Stereo_Taut = pINChI_Taut->Stereo) && Stereo_Taut->nCompInv2Abs && - Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux_Taut, EQL_NUM_INV ); - /* stereo-inv isotopic numbering non-taut = stereo-inv taut numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT | iiEq2INV) : 0; - } - /* f) compare isotopic non-tautomeric inverted stereo numbering to non-tautomeric inverted stereo numbering */ - if ( !eq2taut ) { - eq2taut = - (Stereo_Taut = pINChI->StereoIsotopic) && Stereo_Taut->nCompInv2Abs && - Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux, EQL_NUM_INV ); - /* stereo-inv isotopic numbering non-taut = stereo-inv non-taut numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT | iiEq2INV | iiEq2NONTAUT) : 0; - } - - /* g) compare isotopic non-tautomeric inverted stereo numbering to tautomeric isotopic inverted stereo numbering */ - if ( !eq2taut ) { - eq2taut = pINChI_Taut && - (Stereo_Taut = pINChI_Taut->StereoIsotopic) && Stereo_Taut->nCompInv2Abs && - Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux_Taut, EQL_NUM_INV | EQL_NUM_ISO ); - /* stereo-inv isotopic numbering non-taut = stereo-inv iso taut numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT | iiEq2INV | iiEq2ISO) : 0; - } - } else - /*========= if not bSecondNonTautPass then compare inv taut stereo numb to taut numb ========*/ - if ( !bSecondNonTautPass && bOmitRepetitions && pINChI && pINChI_Aux && pINChI_Aux->bIsIsotopic && - (Stereo = pINChI->StereoIsotopic) && Stereo->nCompInv2Abs && - pINChI_Aux->nNumberOfAtoms > 0 && pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv ) { - /* compare isotopic tautomeric inverted stereo numbering to: - * a) tautomeric numbering - * b) tautomeric isotopic numbering - * c) tautomeric inverted stereo numbering - */ - /* a) compare isotopic tautomeric inverted stereo numbering to tautomeric numbering */ - if ( !eq2taut ) { - eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux, EQL_NUM ); - /* stereo-inv isotopic numbering (taut) = taut numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB ) : 0; - } - /* b) compare isotopic tautomeric inverted stereo numbering to tautomeric isotopic numbering */ - if ( !eq2taut ) { - eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux, EQL_NUM_ISO ); - /* stereo-inv isotopic numbering(taut) = isotopic taut numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iiEq2ISO ) : 0; - } - /* b) compare isotopic tautomeric inverted stereo numbering to tautomeric inverted stereo numbering */ - if ( !eq2taut ) { - eq2taut = (Stereo_Taut = pINChI->Stereo) && Stereo->nCompInv2Abs && - Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux, EQL_NUM_INV ); - /* stereo-inv isotopic numbering (taut) = taut stereo-inv numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iiEq2INV ) : 0; - } - } - if ( eq2taut ) { - /* we have found another (previously printed) layer of the current component equal to this layer */ - /* output this (current) equivalence mark = EquString(eq2taut) */ - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - /* current layer is different from previously printed layers of the current component */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI && pINChI_Aux && pINChI_Aux->bIsIsotopic && pINChI_Aux->nNumberOfAtoms && - (Stereo = pINChI->StereoIsotopic) && Stereo->nNumberOfStereoCenters && - Stereo->nCompInv2Abs && pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv ) { - tot_len += MakeCtString( pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv, - pINChI_Aux->nNumberOfAtoms, 0, NULL, 0, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - /* else isotopic inv stereo info is not present in pINChI */ - } - } - if ( multPrevEquStr && pPrevEquStr ) { - /* the new EqStr of the last item has not been printed; output it now */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - return tot_len; -} -/***************************************************************************/ -int str_HillFormula(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int num_components, int bUseMulipliers) -{ - int i, ii; - INCHI_SORT *is, *is0; - INChI *pINChI, *pINChI_Prev; - int mult, eq2prev, bNext; - - if ( !(is0 = pINChISort) ) { - return tot_len; - } - i = 0; - pINChI_Prev = (0 <= (ii=GET_II(bOutType,is0)))? is0->pINChI[ii] : NULL; - mult = 0; - bNext = 0; - for ( i++; i <= num_components; i ++ ) { - pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; - eq2prev = bUseMulipliers && - pINChI && pINChI_Prev && pINChI->szHillFormula && pINChI_Prev->szHillFormula && - pINChI->szHillFormula[0] && !strcmp(pINChI_Prev->szHillFormula, pINChI->szHillFormula); - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - if ( bNext ++ ) { - tot_len += MakeDelim( ".", pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Prev && pINChI_Prev->szHillFormula && pINChI_Prev->szHillFormula[0] ) { - tot_len += MakeMult( mult+1, "", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - tot_len += MakeHillFormulaString( pINChI_Prev->szHillFormula, pStr + tot_len, - nStrLen-tot_len, bOverflow); - } - } - pINChI_Prev = pINChI; - mult = 0; /* we do not know whether the item is empty */ - } - return tot_len; -} -/***************************************************************************/ -int str_HillFormula2(INCHI_SORT *pINChISort /* non-taut */, INCHI_SORT *pINChISort2 /* taut */, - char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int num_components, int bUseMulipliers) -{ - int i, ii, ii2; - INCHI_SORT *is, *is2, *is0, *is20; - INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; - int mult, eq2prev, bNext, bEqToTaut, tot_len_inp = tot_len; - - is = NULL; - is2 = NULL; - is0 = pINChISort; - is20 = pINChISort2; - i = 0; - - pINChI_Prev = (0 <= (ii=GET_II(bOutType,is0)))? is0->pINChI[ii] : NULL; - pINChI_Taut_Prev = (0 <= (ii2=GET_II(OUT_T1,is20)))? is20->pINChI[ii2] : NULL; - mult = 0; - bNext = 0; - bEqToTaut = 1; - bEqToTaut = bEqToTaut && - pINChI_Prev && pINChI_Taut_Prev && !pINChI_Taut_Prev->bDeleted && - pINChI_Prev->szHillFormula && pINChI_Taut_Prev->szHillFormula && - !strcmp(pINChI_Prev->szHillFormula, pINChI_Taut_Prev->szHillFormula); - for ( i++; i <= num_components; i ++ ) { - pINChI = (i < num_components && (is=is0+i, 0 <= (ii =GET_II(bOutType,is))))? is->pINChI[ii] : NULL; - pINChI_Taut = (i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; - if ( bEqToTaut && (pINChI || pINChI_Taut) ) { - bEqToTaut = pINChI && pINChI_Taut && !pINChI_Taut->bDeleted && - pINChI->szHillFormula && pINChI_Taut->szHillFormula && - !strcmp(pINChI->szHillFormula, pINChI_Taut->szHillFormula); - } - eq2prev = bUseMulipliers && - pINChI && pINChI_Prev && pINChI->szHillFormula && pINChI_Prev->szHillFormula && - pINChI->szHillFormula[0] && !strcmp(pINChI_Prev->szHillFormula, pINChI->szHillFormula); - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - if ( bNext ++ ) { - tot_len += MakeDelim( ".", pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Prev && pINChI_Prev->szHillFormula && pINChI_Prev->szHillFormula[0] ) { - tot_len += MakeMult( mult+1, "", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - tot_len += MakeHillFormulaString( pINChI_Prev->szHillFormula, pStr + tot_len, - nStrLen-tot_len, bOverflow); - } - } - pINChI_Prev = pINChI; - mult = 0; /* we do not know whether the item is empty */ - } - if ( bEqToTaut ) { - pStr[tot_len=tot_len_inp] = '\0'; - } - return tot_len; -} -/***************************************************************************/ -int str_Connections(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int ATOM_MODE, int num_components, int bUseMulipliers) -{ - int i, ii; - INCHI_SORT *is, *is0; - INChI *pINChI, *pINChI_Prev; - int mult, eq2prev, bNext, tot_len_inp, nNumEmpty; - - if ( !(is0 = pINChISort) ) { - return tot_len; - } - i = 0; - pINChI_Prev = (0 <= (ii=GET_II(bOutType,is0)))? is0->pINChI[ii] : NULL; - is = NULL; - mult = 0; - bNext = 0; - tot_len_inp = tot_len; - nNumEmpty = 0; - for ( i++; i <= num_components; i ++ ) { - pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; - eq2prev = bUseMulipliers && - pINChI && pINChI_Prev && pINChI->lenConnTable > 1 && - pINChI_Prev->lenConnTable==pINChI->lenConnTable && - !memcmp( pINChI_Prev->nConnTable, pINChI->nConnTable, - pINChI_Prev->lenConnTable*sizeof(pINChI->nConnTable[0]) ); - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else - if ( pINChI_Prev ) { - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Prev && pINChI_Prev->lenConnTable > 1 ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - tot_len += MakeCtStringNew( pINChI_Prev->nConnTable, pINChI_Prev->lenConnTable, 0, - NULL, pINChI_Prev->nNumberOfAtoms, - pStr + tot_len, nStrLen-tot_len, ATOM_MODE, bOverflow); - } else { - nNumEmpty ++; - } - } - pINChI_Prev = pINChI; - mult = 0; /* we do not know whether the item is empty */ - } - if ( nNumEmpty == num_components && tot_len > tot_len_inp ) { - tot_len = tot_len_inp; - pStr[tot_len] = '\0'; - } - return tot_len; -} -/***************************************************************************/ -int str_H_atoms(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int ATOM_MODE, int TAUT_MODE, - int num_components, int bUseMulipliers) -{ - int i, j, ii, len_H; - INCHI_SORT *is, *is0; - INChI *pINChI, *pINChI_Prev; - int mult, eq2prev, bNext, bNotEmpty, nNumEmpty, tot_len_inp; - - nNumEmpty = 0; - tot_len_inp = tot_len; - is0 = pINChISort; - is = NULL; - i = 0; - pINChI_Prev = (0 <= (ii=GET_II(bOutType,is0)))? is0->pINChI[ii] : NULL; - mult = 0; - bNext = 0; - for ( i++; i <= num_components; i ++) { - pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; - /*========== compare to previous ============*/ - eq2prev = bUseMulipliers && - pINChI && pINChI_Prev && (pINChI->nNumberOfAtoms > 0 || pINChI->lenTautomer>1) && - pINChI_Prev->nNumberOfAtoms==pINChI->nNumberOfAtoms && - (!pINChI_Prev->nNumberOfAtoms || !memcmp( pINChI_Prev->nNum_H, pINChI->nNum_H, - pINChI_Prev->nNumberOfAtoms*sizeof(pINChI->nNum_H[0]) ) ) && - !CompareTautNonIsoPartOfINChI( pINChI_Prev, pINChI ); - - if ( eq2prev && pINChI_Prev->lenTautomer <= 1 ) { - /* make sure it is not empty */ - eq2prev = 0; - for ( j = 0; j < pINChI_Prev->nNumberOfAtoms; j ++ ) { - if ( pINChI_Prev->nNum_H[j] ) { - eq2prev = 1; - break; - } - } - } - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else - if ( pINChI_Prev ) { - /* delimiter */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - /* verify non-empty */ - bNotEmpty = 0; - if ( pINChI_Prev ) { - bNotEmpty = (pINChI_Prev->lenTautomer > 1); - if ( !bNotEmpty ) { - for ( j = 0; j < pINChI_Prev->nNumberOfAtoms; j ++ ) { - if ( pINChI_Prev->nNum_H[j] ) { - bNotEmpty = 1; - break; - } - } - } - } - if ( bNotEmpty ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - /* H-atoms */ - tot_len += (len_H = MakeHString( 0, pINChI_Prev->nNum_H, pINChI_Prev->nNumberOfAtoms, - pStr + tot_len, nStrLen-tot_len, ATOM_MODE, bOverflow )); - /* tautomeric groups */ - tot_len += MakeTautString( pINChI_Prev->nTautomer, pINChI_Prev->lenTautomer, (0!=len_H), - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } else { - nNumEmpty ++; - } - } - pINChI_Prev = pINChI; - mult = 0; /* we do not know whether the item is empty */ - } - if ( nNumEmpty == num_components && tot_len > tot_len_inp ) { - tot_len = tot_len_inp; - pStr[tot_len] = '\0'; - } - return tot_len; -} -/***************************************************************************/ -int str_Charge2(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) -{ - int i, ii, ii2; - INCHI_SORT *is, *is2, *is0, *is20; - INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; - int nTotalCharge, nTotalCharge_Prev, nTotalCharge_Taut, nTotalCharge_Taut_Prev; - int mult, eq2prev, eq2taut, eq2tautPrev, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - pINChI_Taut = NULL; - pINChI_Prev = NULL; - pINChI_Taut_Prev = NULL; - mult = 0; - bNext = 0; - is = NULL; - is2 = NULL; - is0 = pINChISort; - is20 = bSecondNonTautPass? pINChISort2 : NULL; - eq2taut = 0; /* may be non-zero only on the 2nd (non-taut) pass */ - eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; - for ( i = 0; i <= num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; - /*================ compare sp3 to previous =====================*/ - if ( bSecondNonTautPass ) { - /* component that was output on the 1st pass */ - pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; - } - /*========= if bSecondNonTautPass then compare non-iso non-taut stereo to non-iso taut ========*/ - eq2taut = 0; - if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions ) { - eq2taut = pINChI && pINChI_Taut && !pINChI_Taut->bDeleted && - (nTotalCharge = pINChI->nTotalCharge) && (nTotalCharge_Taut = pINChI_Taut->nTotalCharge) && - nTotalCharge == nTotalCharge_Taut; - eq2taut = eq2taut? (iiEQU | iitNONTAUT) : 0; - } - if ( eq2taut ) { - /* we may be here only in case of the second (non-taut) pass */ - /* current non-taut stereo has been found to be same as tautomeric */ - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - /* previous component exists; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( nTotalCharge_Prev = pINChI_Prev->nTotalCharge ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - tot_len += sprintf( pStr + tot_len, "%+d", nTotalCharge_Prev ); - } - } else - if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms && !pINChI_Taut_Prev->bDeleted ) { - /* previous non-taut component exists only in taut list */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - } - /* we have found pINChI->nTotalCharge same as in pINChI_Taut */ - /* output this (current) equivalence as '*', that is, same as tautomeric */ - /* that was printed on the 1st pass. */ - - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - - pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ - pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ - mult = 0; - eq2tautPrev = 1; /* pINChI_Prev sp2 does not exist */ - } else - if ( eq2tautPrev ) { - /* at this point pINChI_Prev does not exist; however, pINChI */ - /*might have been discovered and it is different from pINChI_Taut */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - eq2tautPrev = 0; - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; - } else { - /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp3 */ - /*================ compare sp3 to previous =====================*/ - eq2prev =bUseMulipliers && - pINChI && pINChI_Prev && - (nTotalCharge = pINChI->nTotalCharge) && (nTotalCharge_Prev = pINChI_Prev->nTotalCharge) && - nTotalCharge == nTotalCharge_Prev; - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - if ( nTotalCharge_Prev = pINChI_Prev->nTotalCharge ) { - /* pINChI_Prev exists and has charge info */ - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - tot_len += sprintf( pStr + tot_len, "%+d", nTotalCharge_Prev ); - } - /* else charge is not present in pINChI_Prev */ - } else - if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms && !pINChI_Taut_Prev->bDeleted ) { - if ( nTotalCharge_Taut_Prev = pINChI_Taut_Prev->nTotalCharge ) { - /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ - /* and it has charge info. This info has already been printed in the main section */ - /* - tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); - */ - ; /* pINChI_Taut_Prev sp3 info was output in the main stereo section */ - } else { - ; /* pINChI_Taut_Prev exists and has not sp3 info */ - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; /* */ - } -#endif - } - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; /* we do not know whether the item is empty */ - } - } - return tot_len; -} -/***************************************************************************/ -int str_FixedH_atoms(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int ATOM_MODE, int num_components, int bUseMulipliers) -{ - int i, j, ii, nNumEmpty; - INCHI_SORT *is, *is0; - INChI *pINChI, *pINChI_Prev; - int mult, eq2prev, bNext, bNotEmpty, tot_len_inp; - - is = NULL; - is0 = pINChISort; - i = 0; - pINChI_Prev = (0 <= (ii=GET_II(bOutType,is0)))? is0->pINChI[ii] : NULL; - mult = 0; - bNext = 0; - nNumEmpty = 0; - tot_len_inp = tot_len; - for ( i++; i <= num_components; i ++ ) { - /* only non-tautomeric representation of tautomeric */ - pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; - /*================ compare fixed H to previous =====================*/ - eq2prev =bUseMulipliers && - pINChI && pINChI_Prev && pINChI->nNumberOfAtoms > 0 && - pINChI_Prev->nNumberOfAtoms==pINChI->nNumberOfAtoms && - !memcmp( pINChI_Prev->nNum_H_fixed, pINChI->nNum_H_fixed, - pINChI_Prev->nNumberOfAtoms*sizeof(pINChI->nNum_H_fixed[0]) ); - if ( eq2prev ) { - /* make sure it is not empty */ - eq2prev = 0; - for ( j = 0; j < pINChI_Prev->nNumberOfAtoms; j ++ ) { - if ( pINChI_Prev->nNum_H_fixed[j] ) { - eq2prev = 1; - break; - } - } - } - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - /* print pINChI_Prev */ - /* delimiter */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Prev ) { - /* verify it is not empty */ - bNotEmpty = 0; - for ( j = 0; j < pINChI_Prev->nNumberOfAtoms; j ++ ) { - if ( pINChI_Prev->nNum_H_fixed[j] ) { - bNotEmpty = 1; - break; - } - } - if ( bNotEmpty ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - /* H-atoms-fixed */ - tot_len += MakeHString( 0, pINChI_Prev->nNum_H_fixed, pINChI_Prev->nNumberOfAtoms, - pStr + tot_len, nStrLen-tot_len, ATOM_MODE, bOverflow ); - } else { - nNumEmpty ++; - } - } - } - pINChI_Prev = pINChI; - mult = 0; /* we do not know whether the item is empty */ - } - if ( nNumEmpty == num_components && tot_len > tot_len_inp ) { - tot_len = tot_len_inp; - pStr[tot_len] = '\0'; - } - return tot_len; -} -/***************************************************************************/ -int str_AuxNumb(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions) -{ - int i, ii, ii2; - INCHI_SORT *is, *is0 /*, *is2*/; - INChI *pINChI, *pINChI_Taut=NULL; - INChI_Aux *pINChI_Aux, *pINChI_Aux_Taut=NULL; - int eq2taut, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - bNext = 0; - /*is2 = bSecondNonTautPass? pINChISort2 : NULL;*/ - eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; - is = NULL; - if ( !(is0 = pINChISort) ) { - return tot_len; - } - for ( i = 0; i < num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - is=is0+i; - pINChI = ( 0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; - pINChI_Aux = pINChI? is->pINChI_Aux[ii] : NULL; - /*================ to compare to previously printed =====================*/ - if ( bSecondNonTautPass ) { - /* component that was printed on the 1st pass */ - pINChI_Taut = (0 <= (ii2=GET_II(OUT_T1,is)))? is->pINChI[ii2] : NULL; - pINChI_Aux_Taut = pINChI_Taut? is->pINChI_Aux[ii2] : NULL; - } - eq2taut = 0; - /*========= if bSecondNonTautPass then compare iso non-taut stereo to other stereo ========*/ - if ( bSecondNonTautPass && bOmitRepetitions && pINChI && pINChI_Aux && pINChI_Aux->nNumberOfAtoms > 0 ) { - /* compare non-tautomeric numbering to: - * a) tautomeric numbering - */ - /* a) compare non-tautomeric numbering to tautomeric numbering */ - if ( !eq2taut ) { - eq2taut = pINChI_Taut && !pINChI_Taut->bDeleted && - Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM, pINChI_Aux_Taut, EQL_NUM ); - /* numbering non-taut = taut numbering */ - eq2taut = eq2taut? ( iiNUMB | iitNONTAUT ) : 0; - } - } - if ( eq2taut ) { - /* we have found another (previously printed) layer of the current component equal to this layer */ - /* output this (current) equivalence mark = EquString(eq2taut) */ - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - /* current layer is different from previously printed layers of the current component */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI && pINChI_Aux && pINChI_Aux->nNumberOfAtoms ) { - tot_len += MakeCtString( pINChI_Aux->nOrigAtNosInCanonOrd, - pINChI_Aux->nNumberOfAtoms, 0, NULL, 0, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - } - } - if ( multPrevEquStr && pPrevEquStr ) { - /* the new EqStr of the last item has not been printed; output it now */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - return tot_len; -} -/***************************************************************************/ -int str_AuxTgroupEqu(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bUseMulipliers) -{ - int i, ii; - INCHI_SORT *is, *is0; - INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev; - int mult, eq2prev, bNext; - - is0 = pINChISort; - is = NULL; - i = 0; - pINChI_Aux_Prev = (0 <= (ii=GET_II(bOutType,is0)))? is0->pINChI_Aux[ii] : NULL; - mult = 0; - bNext = 0; - for ( i++; i <= num_components; i ++ ) { - pINChI_Aux = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI_Aux[ii] : NULL; - eq2prev = bUseMulipliers && - Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_TG, pINChI_Aux_Prev, EQL_EQU_TG ); - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfTGroups && - bHasEquString( pINChI_Aux_Prev->nConstitEquTGroupNumbers, pINChI_Aux_Prev->nNumberOfTGroups) ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - tot_len += MakeEquString( pINChI_Aux_Prev->nConstitEquTGroupNumbers, pINChI_Aux_Prev->nNumberOfTGroups, 0, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - } - pINChI_Aux_Prev = pINChI_Aux; - mult = 0; /* we do not know whether the item is empty */ - } - return tot_len; -} - -/***************************************************************************/ -int str_AuxChargeRadVal(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bUseMulipliers) -{ - int i, ii; - INCHI_SORT *is, *is0; - INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev; - int mult, eq2prev, bNext; - - pINChI_Aux_Prev = NULL; - mult = 0; - bNext = 0; - is = NULL; - is0 = pINChISort; - for ( i = 0; i <= num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - pINChI_Aux = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI_Aux[ii] : NULL; - /* check whether pINChI_Aux and pINChI_Aux_Prev have identical info */ - eq2prev = bUseMulipliers && - EqlOrigInfo( pINChI_Aux, pINChI_Aux_Prev ); - if ( eq2prev ) { - /* eq. info is same and non-trivial */ - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else - if ( i ) { - /* pINChI_Aux info is either different or trivial. Output pINChI_Aux_Prev anyway */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { - if ( bHasOrigInfo( pINChI_Aux_Prev->OrigInfo, pINChI_Aux_Prev->nNumberOfAtoms ) ) { - /* pINChI_Aux_Prev exists and has orig. info info */ - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - tot_len += MakeCRVString( pINChI_Aux_Prev->OrigInfo, pINChI_Aux_Prev->nNumberOfAtoms, 0, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } else { - ; /* pINChI_Aux_Prev exists and has only trivial info */ - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; /* */ - } -#endif - } - pINChI_Aux_Prev = pINChI_Aux; - mult = 0; /* we do not know whether the item is empty */ - } - return tot_len; -} -/******************************************************************************************/ -int bin_AuxTautTrans(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, - AT_NUMB **pTrans_n, AT_NUMB **pTrans_s, int bOutType, int num_components) -{ - int i, ii, ii2, ret; - INCHI_SORT *is, *is2, *is0, *is20; - INChI *pINChI, *pINChI_Taut; - AT_NUMB *nTrans_n = NULL; - AT_NUMB *nTrans_s = NULL; - - ret = 0; - is0 = pINChISort; - is20 = pINChISort2; - /* pass 1: save new non-taut numbering */ - for ( i = 0; i < num_components; i ++ ) { - is=is0+i; - is2=is20+i; - pINChI = ( 0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; - pINChI_Taut = ( 0 <= (ii2=GET_II(OUT_T1,is2)))? is2->pINChI[ii2] : NULL; - if ( pINChI && pINChI->nNumberOfAtoms > 0 && - pINChI_Taut && pINChI_Taut->nNumberOfAtoms > 0 && - /* different components save equal new ord. numbers: */ - is->ord_number != is2->ord_number ) { - if ( (nTrans_n && nTrans_s) || - (nTrans_n = (AT_NUMB *)inchi_calloc( num_components+1, sizeof(nTrans_n[0]))) && - (nTrans_s = (AT_NUMB *)inchi_calloc( num_components+1, sizeof(nTrans_s[0]))) ) { - /* new ordering number for original non-tautomeric component number is->ord_number */ - nTrans_n[is->ord_number] = /*nTrans_t[is2->ord_number] =*/ i+1; - } - } - } - if ( nTrans_n && nTrans_s ) { - /* pass 2: get new taut numbering, retrieve new non-taut and save the transposition */ - for ( i = 0; i < num_components; i ++ ) { - is=is0+i; - is2=is20+i; - pINChI = ( 0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; - pINChI_Taut = ( 0 <= (ii2=GET_II(OUT_T1,is2)))? is2->pINChI[ii2] : NULL; - if ( pINChI && pINChI->nNumberOfAtoms > 0 && - pINChI_Taut && pINChI_Taut->nNumberOfAtoms > 0 && - is->ord_number != is2->ord_number && - nTrans_n[is2->ord_number] ) { - /* nTrans_n[is2->ord_number] is new ordering number of - the non-taut representation of the tautomeric component - that has new ord number i+1 and orig ordering number is2->ord_number. - Old numbers start from 0, new start from 1 - */ - - /* n = nTrans_s[t]: taut component #t is in position #n of the non-taut representation */ - nTrans_s[i+1] = nTrans_n[is2->ord_number]; - } - } - *pTrans_n = nTrans_n; - *pTrans_s = nTrans_s; - ret = 1; - } else { - if ( nTrans_n ) { - inchi_free( nTrans_n ); - ret = -1; - } - if ( nTrans_s ) { - inchi_free( nTrans_s ); - ret = -1; - } - } - return ret; -} -/******************************************************************************************/ -int str_AuxTautTrans(AT_NUMB *nTrans_n, AT_NUMB *nTrans_s, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int TAUT_MODE, int num_components) -{ - int i, k, len, j; - - if ( nTrans_n && nTrans_s ) { - /* print the transposition, cycle after cycle */ - for ( i = 1; i <= num_components; i ++ ) { - if ( nTrans_s[i] ) { - /* get one cycle of the transposition */ - for ( j = i, len = 0; (k = nTrans_s[j]); j = k, len ++ ) { - nTrans_n[len] = j; /* save the transposition */ - nTrans_s[j] = 0; /* clear used element to avoid repetitions */ - } - /* print one cycle of the transposition */ - tot_len += MakeDelim( "(", pStr + tot_len, nStrLen-tot_len, bOverflow); - tot_len += MakeCtString( nTrans_n, len, 0, NULL, 0, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - tot_len += MakeDelim( ")", pStr + tot_len, nStrLen-tot_len, bOverflow); - } - } - } - if ( nTrans_n ) - inchi_free( nTrans_n ); - if ( nTrans_s ) - inchi_free( nTrans_s ); - return tot_len; -} -/***************************************************************************/ -int str_StereoAbsInv(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int num_components) -{ - int i, j, ii; - INCHI_SORT *is, *is0; - INChI_Stereo *Stereo; - INChI *pINChI; - - is = NULL; - is0 = pINChISort; - - for ( i = 0; !*bOverflow && i < num_components; i ++ ) { - is=is0+i; - pINChI = (0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; - if ( pINChI && (Stereo = pINChI->Stereo) && (j=Stereo->nCompInv2Abs) ) { - tot_len += MakeDelim( j<0? "1":"0", pStr + tot_len, nStrLen-tot_len, bOverflow); - } else { - tot_len += MakeDelim( ".", pStr + tot_len, nStrLen-tot_len, bOverflow); - } - } - - return tot_len; -} -/***************************************************************************/ -int str_IsoStereoAbsInv(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int num_components) -{ - int i, j, ii; - INCHI_SORT *is, *is0; - INChI_Stereo *Stereo; - INChI *pINChI; - - is = NULL; - is0 = pINChISort; - - for ( i = 0; !*bOverflow && i < num_components; i ++ ) { - is=is0+i; - pINChI = (0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; - if ( pINChI && (Stereo = pINChI->StereoIsotopic) && (j=Stereo->nCompInv2Abs) ) { - tot_len += MakeDelim( j<0? "1":"0", pStr + tot_len, nStrLen-tot_len, bOverflow); - } else { - tot_len += MakeDelim( ".", pStr + tot_len, nStrLen-tot_len, bOverflow); - } - } - - return tot_len; -} -/***************************************************************************/ -int str_AuxIsoTgroupEqu(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bOmitRepetitions, int bUseMulipliers) -{ - int i, ii; - INCHI_SORT *is, *is0; - INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev; - int mult, eq2prev, eq2taut, eq2tautPrev, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - pINChI_Aux = NULL; - pINChI_Aux_Prev = NULL; - mult = 0; - bNext = 0; - is = NULL; - is0 = pINChISort; - eq2taut = 0; /* equal to non-isotopic equivalence */ - eq2tautPrev = 1; /* pINChI_Aux_Prev (previous pINChI_Aux) does not exist */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; - for ( i = 0; i <= num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - pINChI_Aux = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI_Aux[ii] : NULL; - /*================ compare iso non-taut equivalence info to non-iso taut ========*/ - eq2taut = 0; - if ( bOmitRepetitions && pINChI_Aux && pINChI_Aux->bIsIsotopic ) { - /************************************************** - * compare isotopic tautomeric equivalence to: - * a) non-isotopic tautomeric - */ - /* compare isotopic t-group equivalence to non-isotopic */ - eq2taut = Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_TG | EQL_EQU_ISO, pINChI_Aux, EQL_EQU_TG ); - /* equ taut-isotopic = tautomeric, same as for isotopic atom equivalence info*/ - eq2taut = eq2taut? (iiEQU | iitISO) : 0; - } - if ( eq2taut ) { - /* current isotopic t-group equivalence has been found to be same as non-isotopic */ - if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { - /* previous component exists */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( bHasEquString( pINChI_Aux_Prev->nConstitEquIsotopicTGroupNumbers, pINChI_Aux_Prev->nNumberOfTGroups) ) { - /* output previous component(s) equivalence since it was found to be non-trivial */ - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - tot_len += MakeEquString( pINChI_Aux_Prev->nConstitEquIsotopicTGroupNumbers, pINChI_Aux_Prev->nNumberOfTGroups, 0, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } else { - ; /* pINChI_Aux_Prev exists and does not have non-trivial t-group equivalence info */ - } - } - /* we have found pINChI_Aux->pINChI_Aux->nConstitEquIsotopicTGroupNumbers same as in pINChI_Aux->nConstitEquTGroupNumbers */ - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - pINChI_Aux_Prev = NULL; /* pINChI_Aux_Prev has already been output */ - mult = 0; - eq2tautPrev = 1; - } else - if ( eq2tautPrev ) { - /* at this point pINChI_Aux_Prev does not exist; however, pINChI_Aux */ - /* might have been discovered and it may be different from non-isotopic */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - eq2tautPrev = 0; - pINChI_Aux_Prev = pINChI_Aux; - mult = 0; - } else { - /* check whether pINChI_Aux and pINChI_Aux_Prev have identical non-trivial isotopic t-group equivalence info */ - eq2prev = bUseMulipliers && Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_TG | EQL_EQU_ISO, pINChI_Aux_Prev, EQL_EQU_TG | EQL_EQU_ISO ); - if ( eq2prev ) { - /* eq. info is same and non-trivial */ - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - /* pINChI_Aux eq. info is either different or trivial. Output pINChI_Aux_Prev anyway */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { - if ( bHasEquString( pINChI_Aux_Prev->nConstitEquIsotopicTGroupNumbers, pINChI_Aux_Prev->nNumberOfTGroups) ) { - /* pINChI_Aux_Prev exists and has equivalence info */ - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - tot_len += MakeEquString( pINChI_Aux_Prev->nConstitEquIsotopicTGroupNumbers, pINChI_Aux_Prev->nNumberOfTGroups, 0, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } else { - ; /* pINChI_Aux_Prev exists and has only trivial equivalence info */ - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; /* */ - } -#endif - } - pINChI_Aux_Prev = pINChI_Aux; - mult = 0; /* we do not know whether the item is empty */ - } - } - return tot_len; -} diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichiread.c b/INCHI-1-SRC/INCHI_API/inchi_dll/ichiread.c deleted file mode 100644 index 99e9d58..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichiread.c +++ /dev/null @@ -1,8139 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include - -/* #define CHECK_WIN32_VC_HEAP */ -#include "mode.h" - -#if ( READ_INCHI_STRING == 1 ) - -#include "ichicomp.h" -#include "ichi.h" -#include "ichitime.h" -#include "util.h" -#include "strutil.h" -#include "ichi_io.h" - -/* reverse InChI */ -#include "ichimain.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichister.h" -#include "strutil.h" -#include "ichisize.h" -#include "ichiring.h" -#include "ichinorm.h" -#include "ichierr.h" - -#include "ichirvrs.h" - - -/*^^^ */ -#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_STANDALONE) ) -#include "inchi_api.h" -#endif -/*^^^ */ - - - -typedef struct tagLine -{ - char *str; - int len; - int len_alloc; - int c; -} SEGM_LINE; -#define SEGM_LINE_ADD 128 - -typedef struct tagOneLinkedBond -{ - AT_NUMB neigh; /* canonical number of a neighbor */ - AT_NUMB prev; /* position of the previous neighbor in the list */ -} ONE_LINKED_BOND; - -typedef struct tagLinkedBonds -{ - ONE_LINKED_BOND *pBond; - int len; - int len_alloc; -}LINKED_BONDS; -#define LINKED_BOND_ADD 128 - -typedef enum tagModeProtonIsoExchgH -{ - MODE_PIXH_UNDEFINED, /* 0 */ - MODE_PIXH_ADD_TO_FIRST, /* 1 */ - MODE_PIXH_ADD_TO_EACH, /* 2 */ - MODE_PIXH_ADD_A_PIXH_COMPONENT, /* 3 */ - MODE_PIXH_KEEP_TOTALS /* 4 */ -} MODE_PIXH; - - -/* local prototypes */ -static int GetInChIFormulaNumH(INChI *pInChI, int *nNumH); -static int GetInChINumH(INChI *pInChI, int *nNumH); -static int GetInChIIsoH(INChI *pInChI, int nNumIsotopicH[NUM_H_ISOTOPES]); - -static int getInChIChar(INCHI_IOSTREAM *pInp); -static int AddInChIChar(INCHI_IOSTREAM *pInp, SEGM_LINE *Line, const char *pszToken); -static int AddLinkedBond(AT_NUMB at1, AT_NUMB at2, AT_NUMB num_at, LINKED_BONDS *pLB); -static int bInChIHasReconnectedMetal(INChI *pInChI); -static int SetProtonsAndXchgIsoH(int bInChI2Structure, - int bReqSplitOutputInChI, - int bReqProtonsForEachComponent, - int bReqNonTaut, int bReqStereo, - int num_components[INCHI_NUM], - MODE_PIXH nModeProtonIsoExchgH[INCHI_NUM], - InpInChI *OneInput); -#if ( FIX_DALKE_BUGS == 1 ) -static int SetHillFormFromInChI(InpInChI *OneInput); -#endif - -static int nGetInChISegment(INCHI_IOSTREAM *pInp, SEGM_LINE *Line, const char *pszToken); - -static int CopySegment(INChI *pInChITo, INChI *pInChIFrom, int StereoType, - int bIsotopicTo, int bIsotopicFrom); -static int nFillOutProtonMobileH(INChI *pInChI); -static int nProtonCopyIsotopicInfo(INChI *pInChI_to, INChI *pInChI_from); -static int CopyAtomNumbers(INChI *pInChI_To, int bIsoTo, INChI *pInChI_From, int bIsoFrom); - -static int ParseSegmentFormula(const char *str, int bMobileH, INChI *pInpInChI[], - int nNumComponents[]); -static int ParseSegmentConnections(const char *str, int bMobileH, INChI **pInpInChI, - int *pnNumComponents, int *pbAbc); -static int ParseSegmentMobileH(const char *str, int bMobileH, - INChI *pInpInChI[], int pnNumComponents[], int *pbAbc); -static int ParseSegmentCharge(const char *str, int bMobileH, - INChI *pInpInChI[], int nNumComponents[]); -static int ParseSegmentProtons(const char *str, int bMobileH, - REM_PROTONS nNumProtons[], int nNumComponents[]); -static int ParseSegmentSp2(const char *str, int bMobileH, - INChI *pInpInChI[], int nNumComponents[], int state, int *pbAbc); -static int ParseSegmentSp3(const char *str, int bMobileH, - INChI *pInpInChI[], int nNumComponents[], int state, int *pbAbc); -static int ParseSegmentSp3m(const char *str, int bMobileH, - INChI *pInpInChI[], int nNumComponents[], int state); -static int bIsSp3LayerNotEmpty(INChI *pInpInChI[], int bMobileH, - int bIso, int nNumComponents); -static int ParseSegmentSp3s(const char *str, int bMobileH, - INChI *pInpInChI[], int s[TAUT_NUM][2], int ppnNumComponents[], int state); -static int ParseSegmentIsoAtoms(const char *str, int bMobileH, INChI *pInpInChI[], - int nNumComponents[], int state, int *pbAbc); -static int ParseSegmentIsoExchgH(const char *str, int bMobileH, REM_PROTONS nNumProtons[], - int nNumComponents[], int state, int *pbAbc); -static int ParseSegmentPerm(const char *str, int bMobileH, INChI *pInpInChI[], - int ppnNumComponents[], int state, int *pbAbc); -#if ( FIX_ISO_FIXEDH_BUG_READ == 1 ) -static int bIsoMayBeArranged(int bInchi2Struct, int iso_diff[NUM_H_ISOTOPES], - REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM], - INChI *pInpInChI[INCHI_NUM][TAUT_NUM], int nNumComponents[INCHI_NUM][TAUT_NUM], int iINChI); -#endif - -static int ReadInChILine(INCHI_IOSTREAM *pInp, SEGM_LINE *pLine, - char **pStr, int *pState, - INChI *pInpInChI[INCHI_NUM][TAUT_NUM], - int nNumComponents[INCHI_NUM][TAUT_NUM], - REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM], - int s[INCHI_NUM][TAUT_NUM][2], - int *bStdFormat, - int *bInputHasSaveOpt, unsigned char *inp_save_opt_bits); - -int InChILine2Data(INCHI_IOSTREAM *pInp, SEGM_LINE *pLine, - char **pStr, int *pState, int *nErr, - INChI *pInpInChI[INCHI_NUM][TAUT_NUM], - int nNumComponents[INCHI_NUM][TAUT_NUM], - REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM], - int s[INCHI_NUM][TAUT_NUM][2], - int bReadCoord, int bInchi2Struct, - INCHI_MODE nMode, - int *bStdFormat, - int *bInputHasSaveOpt, unsigned char *inp_save_opt_bits); - -static int ReadInChICoord(INCHI_IOSTREAM *pInp, SEGM_LINE *pLine, int *pState, - INChI *pInpInChI[INCHI_NUM][TAUT_NUM], - int nNumComponents[INCHI_NUM][TAUT_NUM]); - -static int OutputInChIAsRequested(INCHI_IOSTREAM *pOut, INCHI_IOSTREAM *pLog, - ICHICONST INPUT_PARMS *ip_inp, - STRUCT_DATA *sd_inp, - InpInChI *OneInput, - int num_components[INCHI_NUM], - MODE_PIXH nModeProtonIsoExchgH[INCHI_NUM], - long num_inp, unsigned char save_opt_bits); - -static int ParseAuxSegmentVersion(const char *str, int bMobileH, INChI *pInpInChI[], - int ppnNumComponents[], int state); -static int ParseAuxSegmentNumbers(const char *str, int bMobileH, INChI *pInpInChI[], - int ppnNumComponents[], int state, int *pbAbc); -static int ParseAuxSegmentAtomEqu(const char *str, int bMobileH, INChI *pInpInChI[], - int ppnNumComponents[], int state); -static int ParseAuxSegmentGroupEqu(const char *str, int bMobileH, INChI *pInpInChI[], - int ppnNumComponents[], int state); -static int ParseAuxSegmentSp3Inv(const char *str, int bMobileH, INChI *pInpInChI[], - int ppnNumComponents[], int state); -static int ParseAuxSegmentSp3InvNumbers(const char *str, int bMobileH, INChI *pInpInChI[], - int ppnNumComponents[], int state); -static int ParseAuxSegmentReverseCRV(const char *str, int bMobileH, INChI *pInpInChI[], - int ppnNumComponents[], int state); -static int ParseAuxSegmentReverseAtoms(const char *str, int bMobileH, INChI *pInpInChI[], - int ppnNumComponents[], int state); -static int ParseAuxSegmentReverseBonds(const char *str, int bMobileH, INChI *pInpInChI[], - int ppnNumComponents[], int state); -static int ParseAuxSegmentReverseXYZ(const char *str, int bMobileH, XYZ_COORD **ppXYZ, - INChI *pInpInChI[], int ppnNumComponents[], int state); -static int AddAuxSegmentCoord(int nRet, XYZ_COORD *pXYZ, int nLenXYZ, - INChI *pInpInChI[INCHI_NUM][TAUT_NUM], - int nNumComponents[INCHI_NUM][TAUT_NUM]); - -static const char *getInchiStateReadErr(int stat); -static const char *getInchiErrName(int nErr); - -#define SEG_END '/' -/* the following 2 definitions are used to allow tab-delimited InChI input - 2008-11-17 DT */ -#define INCHI_INP_EOL(X) ((X)=='\n' || (X)=='\r' || (X)=='\t') -/*#define INCHI_TOKEN "/\n\r\t"*/ -#define INCHI_TOKEN "/\n\r\t\\" - -typedef enum tagInChI_STATE -{ - /* M */ - IST_MOBILE_H_FORMULA, /* 0 */ - IST_MOBILE_H_CONNECTIONS, /* 1 */ - IST_MOBILE_H, /* 2 */ - IST_MOBILE_H_CHARGE, /* 3 */ - IST_MOBILE_H_PROTONS, /* 4 */ - IST_MOBILE_H_SP2, /* 5 */ - IST_MOBILE_H_SP3, /* 6 */ - IST_MOBILE_H_SP3_M, /* 7 */ - IST_MOBILE_H_SP3_S, /* 8 */ - - /* Fork */ - IST_MOBILE_H_ISO_LAYER_FORK, /* 9 */ - - /* MI */ - IST_MOBILE_H_ISO_ATOMS, /* 10 */ - IST_MOBILE_H_ISO_EXCH_H, /* 11 */ - IST_MOBILE_H_ISO_SP2, /* 12 */ - IST_MOBILE_H_ISO_SP3, /* 13 */ - IST_MOBILE_H_ISO_SP3_M, /* 14 */ - IST_MOBILE_H_ISO_SP3_S, /* 15 */ - - /* Fork */ - IST_FIXED_H_LAYER_FORK, /* 16 */ - - /* F */ - IST_FIXED_H_FORMULA, /* 17 */ - IST_FIXED_H, /* 18 */ - IST_FIXED_H_CHARGE, /* 19 */ - IST_FIXED_H_SP2, /* 20 */ - IST_FIXED_H_SP3, /* 21 */ - IST_FIXED_H_SP3_M, /* 22 */ - IST_FIXED_H_SP3_S, /* 23 */ - IST_FIXED_H_PERMUTATION, /* 24 */ - - /* Fork */ - IST_FIXED_H_ISO_LAYER_FORK, /* 25 */ - - /* FI */ - IST_FIXED_H_ISO_ATOMS, /* 26 */ - IST_FIXED_H_ISO_LAYER, /* 27 */ - IST_FIXED_H_ISO_SP2, /* 28 */ - IST_FIXED_H_ISO_SP3, /* 29 */ - IST_FIXED_H_ISO_SP3_M, /* 30 */ - IST_FIXED_H_ISO_SP3_S, /* 31 */ - IST_FIXED_H_ISO_PERMUTATION, /* 32 */ - - /* Reconnected */ - IST_RECONNECTED_LAYER_FORK, /* 33 */ - IST_RECONNECTED_FORMULA, /* 34 */ - - /* Other reading errors */ - IST_MATERIAL_BALANCE_ERROR, /* 35 */ - - IST_END = -1 -}INCHI_STATE; - -#define IST_HAPPENED_IN_RECMET 100 - -typedef struct tagInchiReadErrMsg -{ - int stat; - const char *msg; -} INCHI_READ_ERR_MSG; - -ICHICONST INCHI_READ_ERR_MSG irErrMsg[] = -{ - /* M */ - {IST_MOBILE_H_FORMULA, "MOBILE_H_FORMULA" }, - {IST_MOBILE_H_CONNECTIONS, "MOBILE_H_CONNECTIONS" }, - {IST_MOBILE_H, "MOBILE_H" }, - {IST_MOBILE_H_CHARGE, "MOBILE_H_CHARGE" }, - {IST_MOBILE_H_PROTONS, "MOBILE_H_PROTONS" }, - {IST_MOBILE_H_SP2, "MOBILE_H_SP2" }, - {IST_MOBILE_H_SP3, "MOBILE_H_SP3" }, - {IST_MOBILE_H_SP3_M, "MOBILE_H_SP3_/m" }, - {IST_MOBILE_H_SP3_S, "MOBILE_H_SP3_/s" }, - - /* Fork */ - {IST_MOBILE_H_ISO_LAYER_FORK, "MOBILE_H_ISO_LAYER_FORK" }, - - /* MI */ - {IST_MOBILE_H_ISO_ATOMS, "MOBILE_H_ISO_ATOMS" }, - {IST_MOBILE_H_ISO_EXCH_H, "MOBILE_H_ISO_EXCH_H" }, - {IST_MOBILE_H_ISO_SP2, "MOBILE_H_ISO_SP2" }, - {IST_MOBILE_H_ISO_SP3, "MOBILE_H_ISO_SP3" }, - {IST_MOBILE_H_ISO_SP3_M, "MOBILE_H_ISO_SP3_/m" }, - {IST_MOBILE_H_ISO_SP3_S, "MOBILE_H_ISO_SP3_/s" }, - - /* Fork */ - {IST_FIXED_H_LAYER_FORK, "FIXED_H_LAYER_FORK" }, - - /* F */ - {IST_FIXED_H_FORMULA, "FIXED_H_FORMULA" }, - {IST_FIXED_H, "FIXED_H" }, - {IST_FIXED_H_CHARGE, "FIXED_H_CHARGE" }, - {IST_FIXED_H_SP2, "FIXED_H_SP2" }, - {IST_FIXED_H_SP3, "FIXED_H_SP3" }, - {IST_FIXED_H_SP3_M, "FIXED_H_SP3_/m" }, - {IST_FIXED_H_SP3_S, "FIXED_H_SP3_/s" }, - {IST_FIXED_H_PERMUTATION, "FIXED_H_PERMUTATION" }, - - /* Fork */ - {IST_FIXED_H_ISO_LAYER_FORK, "FIXED_H_ISO_LAYER_FORK" }, - - /* FI */ - {IST_FIXED_H_ISO_ATOMS, "FIXED_H_ISO_ATOMS" }, - {IST_FIXED_H_ISO_LAYER, "FIXED_H_ISO_LAYER" }, - {IST_FIXED_H_ISO_SP2, "FIXED_H_ISO_SP2" }, - {IST_FIXED_H_ISO_SP3, "FIXED_H_ISO_SP3" }, - {IST_FIXED_H_ISO_SP3_M, "FIXED_H_ISO_SP3_m" }, - {IST_FIXED_H_ISO_SP3_S, "FIXED_H_ISO_SP3_s" }, - {IST_FIXED_H_ISO_PERMUTATION, "FIXED_H_ISO_PERMUTATION" }, - - /* Reconnected */ - {IST_RECONNECTED_LAYER_FORK, "RECONNECTED_LAYER_FORK" }, - {IST_RECONNECTED_FORMULA, "RECONNECTED_FORMULA" }, - - {IST_MATERIAL_BALANCE_ERROR, "MATERIAL_BALANCE" }, - - {IST_END, "Unknown Error" } -}; - - - -typedef enum tagCopySegmentType -{ - CPY_SP2, - CPY_SP3, - CPY_SP3_M, - CPY_SP3_S, - CPY_ISO_AT -} COPY_SEG_TYPE; - -#define NSTRLEN 64000 -#define MAX_MSG_LEN 512 -#define MAX_MSG_BUF_LEN 128 - - -/*************************************************************************************/ -const char *getInchiStateReadErr(int stat) -{ - int i, bRecMet = 0; - static char szMsg[128]; - if ( stat >= IST_HAPPENED_IN_RECMET ) - { - bRecMet = 1; - stat -= IST_HAPPENED_IN_RECMET; - } - for ( i = 0; 0 <= irErrMsg[i].stat && stat != irErrMsg[i].stat; i ++ ) - ; - sprintf(szMsg, -#if ( FIX_DALKE_BUGS == 1 ) - "%s%.100s", -#else - "%s%s", -#endif - irErrMsg[i].msg, bRecMet? ", Reconnected layer" : ""); - return szMsg; -} - -/**************************************************************************************/ -const char *getInchiErrName(int nErr) -{ - switch ( nErr ) - { - case RI_ERR_ALLOC: - return "Allocation failed"; - case RI_ERR_PROGR: - return "Program error"; - case RI_ERR_SYNTAX: - return "Syntax error"; - case RI_ERR_EOL: - return "End of line"; - } - return "Unknown error"; -} -#if ( FIX_DALKE_BUGS == 1 ) -/*****************************************************************************************/ -int SetHillFormFromInChI(InpInChI *OneInput) -{ - int iINChI, iTaut, iComp, num_diff; - INChI *pINChI; - char *szHillFormulaOld; - for ( iINChI = 0, num_diff = 0; iINChI < INCHI_NUM; iINChI ++ ) - { - for ( iTaut = TAUT_NON; iTaut < TAUT_NUM; iTaut ++ ) - { - for ( iComp = 0; iComp < OneInput->nNumComponents[iINChI][iTaut]; iComp ++ ) - { - pINChI = &OneInput->pInpInChI[iINChI][iTaut][iComp]; - if ( !pINChI->nNumberOfAtoms || pINChI->bDeleted || !pINChI->szHillFormula || !pINChI->szHillFormula[0] ) - { - continue; - } - szHillFormulaOld = pINChI->szHillFormula; - pINChI->szHillFormula = AllocateAndFillHillFormula(pINChI); - num_diff += !pINChI->szHillFormula || !pINChI->szHillFormula[0] || strcmp(pINChI->szHillFormula, szHillFormulaOld); - inchi_free(szHillFormulaOld); - } - } - } - return num_diff; -} -#endif - - - -/********************** main entry point **********************************************/ - -int ReadWriteInChI(INCHI_IOSTREAM *pInp, INCHI_IOSTREAM *pOut, INCHI_IOSTREAM *pLog, - INPUT_PARMS *ip_inp, - STRUCT_DATA *sd_inp, - /* the following are InChI library-specific parameters */ - inp_ATOM **at, int *num_at, - char *szMsg, int nMsgLen, unsigned long WarningFlags[2][2]) -{ - InpInChI OneInput; - int i, j, nReadStatus, ret, nErr, iINChI; - char *strHdr=NULL; - char *szCurHdr = NULL; - int num_components[INCHI_NUM]; - int bReqNonTaut = (0 != ((ip_inp->nMode & REQ_MODE_BASIC) && - (ip_inp->nMode & REQ_MODE_TAUT))); - /* - int bReqRecmet = (0 != ((ip->bTautFlags & TG_FLAG_RECONNECT_COORD) && - (ip->bTautFlags & TG_FLAG_DISCONNECT_COORD))); - */ - int bReqStereo = (0 != (ip_inp->nMode & REQ_MODE_STEREO)); - int bHasSomeReconnected = 0, bHasSomeFixedH = 0, bHasMetal = 0; - int nModeFlagsStereo = 0, bTautFlags = 0; /* InChI creation flags modifications derived from current InChI */ - MODE_PIXH nModeProtonIsoExchgH[INCHI_NUM]; - - NORM_CANON_FLAGS ncFlags; - NORM_CANON_FLAGS *pncFlags = &ncFlags; - INPUT_PARMS ip_cur, *ip; - STRUCT_DATA sd_cur, *sd; - int nMessageLen = MAX_MSG_LEN; - char szMessage[MAX_MSG_LEN]; - int nInitLenMessage; - - int pState, bStereoType; - int bReqProtonsForEachComponent = 0; - int bReqSplitOutputInChI = 0; - SEGM_LINE Line; - SEGM_LINE *pLine = &Line; - long ulProcessingTime = 0; - inchiTime ulTStart; - long num_processed = 0, num_errors = 0; - int bPlainTabbedOutput; - const char *pTAB; - -#ifdef TARGET_API_LIB - const int bInChI2Structure = 0 != (ip_inp->bReadInChIOptions & READ_INCHI_TO_STRUCTURE); - const int bInChI2InChI = 0 != (ip_inp->bReadInChIOptions & READ_INCHI_OUTPUT_INCHI); -#else - const int bInChI2Structure = 0 != (ip_inp->bReadInChIOptions & READ_INCHI_TO_STRUCTURE); - const int bInChI2InChI = 0 != (ip_inp->bReadInChIOptions & READ_INCHI_OUTPUT_INCHI); -#endif - const int bReadCoord = bInChI2Structure; - long num_inp=0; - - int bInputInStdFormat=0; - int bInputHasSaveOpt=0; - unsigned char inp_save_opt_bits=0; - unsigned char save_opt_bits=0; - - ret = 0; - nReadStatus = RI_ERR_EOL; - - memset(szMessage, 0, sizeof(szMessage)); - memset(&OneInput, 0, sizeof(OneInput)); - memset(pLine, 0, sizeof(pLine[0])); - if ( szMsg ) - szMsg[0] = '\0'; - - - while( nReadStatus != RI_ERR_EOF ) - { - - for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) - { - for ( j = 0; j < TAUT_NUM; j ++ ) - { - if ( OneInput.nNumProtons[iINChI][j].pNumProtons ) - { - inchi_free(OneInput.nNumProtons[iINChI][j].pNumProtons); - OneInput.nNumProtons[iINChI][j].pNumProtons = NULL; - } - } - } - - memset(&OneInput, 0, sizeof(OneInput)); - memset(pncFlags, 0, sizeof(*pncFlags)); - bStereoType = 0; - ip_cur = *ip_inp; - ip = &ip_cur; - sd_cur = *sd_inp; - sd = &sd_cur; - bReqSplitOutputInChI = 0 != (ip->bReadInChIOptions & READ_INCHI_SPLIT_OUTPUT); - bReqProtonsForEachComponent = bReqSplitOutputInChI && - 0 != (READ_INCHI_KEEP_BALANCE_P & ip->bReadInChIOptions); - - bPlainTabbedOutput = 0 != (ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT); -#if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) ) - pTAB = bPlainTabbedOutput? "\t" : "\n"; -#else - pTAB = "\n"; -#endif - - - if ( bInChI2Structure ) - { -#if ( bRELEASE_VERSION == 1 ) - bReqNonTaut = 1; /* bReqNonTaut=0 ignores Fixed-H layer in input InChI, for testing only */ -#endif - /* bReqRecmet = 1; */ - bReqStereo = 1; - bReqSplitOutputInChI = 1; - bReqProtonsForEachComponent = bReqNonTaut; - ip->bTautFlags |= (TG_FLAG_DISCONNECT_COORD | TG_FLAG_RECONNECT_COORD); - ip->nMode |= (REQ_MODE_BASIC | REQ_MODE_TAUT | REQ_MODE_STEREO | REQ_MODE_ISO_STEREO | REQ_MODE_ISO); - } - - - /************************************************************************/ - /* Read InChI string */ - /************************************************************************/ - InchiTimeGet(&ulTStart); - - - nReadStatus = InChILine2Data(pInp, pLine, &strHdr, &pState, &nErr, OneInput.pInpInChI, - OneInput.nNumComponents, OneInput.nNumProtons, - OneInput.s, bReadCoord, bInChI2Structure, - ip_inp->nMode, - &bInputInStdFormat,&bInputHasSaveOpt,&inp_save_opt_bits); - - - - ulProcessingTime += InchiTimeElapsed(&ulTStart); - - - if ( (nReadStatus == RI_ERR_EOL || nReadStatus == RI_ERR_EOF) && !nErr && - OneInput.nNumComponents[INCHI_BAS][TAUT_YES] - + OneInput.nNumComponents[INCHI_BAS][TAUT_NON] ) - { - /* InChI has been successfully read */ - - ret = 0; - num_inp ++; - bHasSomeReconnected = 0; - bHasSomeFixedH = 0; - - /* Does not allow conversion non-standard->standard */ - /* (force target to be non-standard also) */ - if ( ip_inp->bINChIOutputOptions & INCHI_OUT_STDINCHI ) - { - if ( !bInputInStdFormat ) /* Input InChI is a non-standard one */ - { - ip->bINChIOutputOptions &= ~INCHI_OUT_STDINCHI; - if ( szCurHdr && szCurHdr[0] ) - inchi_ios_eprint( pLog, "Warning: forced conversion to non-standard InChI for non-std input, %s\n", szCurHdr ); - else - inchi_ios_eprint( pLog, "Warning: forced conversion to non-standard InChI for non-std input, Structure %ld\n", num_inp ); - } - } - - if ( ip->bINChIOutputOptions & INCHI_OUT_SAVEOPT ) - { - if (!bInputHasSaveOpt) - { - /* Does not allow to create SaveOpt if the source lacks appendix */ - ip->bINChIOutputOptions &= ~INCHI_OUT_SAVEOPT; - if ( szCurHdr && szCurHdr[0] ) - inchi_ios_eprint( pLog, "Warning: ignore SaveOpt request for SaveOpt-less input, %s\n", szCurHdr ); - else - inchi_ios_eprint( pLog, "Warning: ignore SaveOpt request for SaveOpt-less input, Structure %ld\n", num_inp ); - } - else - { - /* Analyze existing and prepare new SaveOpt appendix */ - - if ( 0 != ( ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) - { - /* RecMet requested */ - if ( 0 != (inp_save_opt_bits & SAVE_OPT_RECMET) ) - { - save_opt_bits |= SAVE_OPT_RECMET; - } - else - { - ip->bTautFlags &= ~TG_FLAG_RECONNECT_COORD; - if ( szCurHdr && szCurHdr[0] ) - inchi_ios_eprint( pLog, "Warning: input created w/o RecMet - ignoring RecMet request, %s\n", szCurHdr ); - else - inchi_ios_eprint( pLog, "Warning: input created w/o RecMet - ignoring RecMet request, Structure %ld\n", num_inp ); - } - } - if ( 0 != (ip->nMode & REQ_MODE_BASIC) ) - { - /* FixedH requested */ - if ( 0 != (inp_save_opt_bits & SAVE_OPT_FIXEDH) ) - save_opt_bits |= SAVE_OPT_FIXEDH; - else - { - ip->nMode &= ~REQ_MODE_BASIC; - if ( szCurHdr && szCurHdr[0] ) - inchi_ios_eprint( pLog, "Warning: input created w/o FixedH - ignoring FixedH request, %s\n", szCurHdr ); - else - inchi_ios_eprint( pLog, "Warning: input created w/o FixedH - ignoring FixedH request, Structure %ld\n", num_inp ); - } - } - /* Copy from source SaveOpt the bits which we do not touch */ - /* while converting InChI: SUU SLUUD KET 15T */ - if ( 0 != ( inp_save_opt_bits & SAVE_OPT_SUU) ) - save_opt_bits |= SAVE_OPT_SUU; - if ( 0 != ( inp_save_opt_bits & SAVE_OPT_SLUUD) ) - save_opt_bits |= SAVE_OPT_SLUUD; - if ( 0 != ( inp_save_opt_bits & SAVE_OPT_KET) ) - save_opt_bits |= SAVE_OPT_KET; - if ( 0 != ( inp_save_opt_bits & SAVE_OPT_15T) ) - save_opt_bits |= SAVE_OPT_15T; - /* Check if /SNon requested and turn OFF stereo bits if so */ - if ( ! (ip->nMode & REQ_MODE_STEREO) ) - { - save_opt_bits &= ~SAVE_OPT_SUU; - save_opt_bits &= ~SAVE_OPT_SLUUD; - } - } - } - - - -#ifndef TARGET_API_LIB - /* - inchi_ios_eprint(stderr, "%ld: %s\r", num_inp, strHdr? strHdr : ""); - inchi_ios_eprint(pLog, "%ld: %s\n", num_inp, strHdr? strHdr : ""); - */ - - if ( !ip->bNoStructLabels && !(bInChI2Structure && (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY)) ) - { - /* Added 2nd item: Do not output this extra line into the output SDfile. 2008-11-17 DCh */ - if ( strHdr && strstr(strHdr, "Structure:")) - { - inchi_ios_print(pOut, "%s%s", strHdr, pTAB); /* output header */ -#if ( FIX_DALKE_BUGS == 1 ) -#else - sprintf(szMessage, "%s (%ld)", strHdr? strHdr : "", num_inp); -#endif - } - else - { - /* - inchi_ios_print(pOut, "Structure %ld (%s)%s", num_inp, strHdr? strHdr : "", pTAB); - sprintf(szMessage, "Structure %ld (%s)%s", num_inp, strHdr? strHdr : "" , pTAB); - */ - inchi_ios_print(pOut, "Structure: %ld. (%s)%s", num_inp, strHdr? strHdr : "No struct name" , pTAB); /* output header */ -#if ( FIX_DALKE_BUGS == 1 ) -#else - sprintf(szMessage, "Structure: %ld. (%s)%s", num_inp, strHdr? strHdr : "No struct name", pTAB); -#endif - } - if ( strHdr && strHdr[0] ) - { - strncpy(ip->szSdfDataHeader, strHdr, sizeof(ip->szSdfDataHeader)); - ip->szSdfDataHeader[sizeof(ip->szSdfDataHeader)-1] = '\0'; - ip->pSdfLabel = NULL; - ip->pSdfValue = ip->szSdfDataHeader; - } - else - { - ip->pSdfValue = NULL; - ip->szSdfDataHeader[0] = '\0'; - } - } - -#if ( FIX_DALKE_BUGS == 1 ) - sprintf(szMessage, "%ld: %.400s", num_inp, strHdr? strHdr : ""); -#else - sprintf(szMessage, "%ld: %s", num_inp, strHdr? strHdr : ""); -#endif -#endif - - nInitLenMessage = strlen(szMessage); - if ( strHdr ) - { - szCurHdr = strHdr; - strHdr = NULL; - } - if ( szCurHdr && ip && ip->first_struct_number > 0 ) - { - /* check whether the structure should be skipped */ - static char szStruct[] = "Structure:"; - char *pStrNum = strstr(szCurHdr, szStruct); - long cur_struct_number; - if ( pStrNum ) - { - pStrNum += sizeof(szStruct)-1; /* -1 takes care of the string terminal zero */ - cur_struct_number = inchi_strtol(pStrNum, NULL, 10); - if ( cur_struct_number ) - { - OneInput.num_inp = cur_struct_number; - } - /* process request to bypass first several InChIs */ - if ( cur_struct_number > 0 && cur_struct_number < ip->first_struct_number ) - { - -#if ( !defined(TARGET_API_LIB) && !defined(TARGET_EXE_STANDALONE) ) - inchi_fprintf(stderr, "Skipping %s\r", szMessage); -#endif - FreeInpInChI(&OneInput); - if ( szCurHdr ) - { - inchi_free(szCurHdr); - szCurHdr = NULL; - } - INCHI_HEAPCHK - continue; - } - } - } - - num_processed ++; - - /* In case of splitting InChI into separate components */ - /* decide whether to keep /p in each component or */ - /* output /p and /i/h as a separate component */ - /* Note: if InChI is not to be splitted DO NOT create */ - /* a separate component for /p, /i/h: it would be a bug*/ - - InchiTimeGet(&ulTStart); - - INCHI_HEAPCHK - ret = SetProtonsAndXchgIsoH(bInChI2Structure, bReqSplitOutputInChI, bReqProtonsForEachComponent, - bReqNonTaut, bReqStereo, num_components, nModeProtonIsoExchgH, &OneInput); - INCHI_HEAPCHK - if ( ret < 0 ) - { - num_errors ++; - goto exit_error; - } - - sd->num_components[INCHI_BAS] = num_components[INCHI_BAS]; - sd->num_components[INCHI_REC] = num_components[INCHI_REC]; - - /* do we have reconnected InChI ? */ - if ( (OneInput.nNumComponents[INCHI_REC][TAUT_YES] || - OneInput.nNumComponents[INCHI_REC][TAUT_NON]) && - (ip->bTautFlags & TG_FLAG_RECONNECT_COORD) && - (ip->bTautFlags & TG_FLAG_DISCONNECT_COORD) ) - { - /* needed for InChI string output to include reconnected InChI */ - sd->bTautFlagsDone[0] |= TG_FLAG_DISCONNECT_COORD_DONE; - bHasSomeReconnected = 1; - } - /* Do we have fixed H InChI ? */ - if ( bReqNonTaut && - /*OneInput.nNumComponents[bHasSomeReconnected?INCHI_REC:INCHI_BAS][TAUT_NON]*/ - (OneInput.nNumComponents[INCHI_REC][TAUT_NON] || - OneInput.nNumComponents[INCHI_BAS][TAUT_NON]) ) - { - bHasSomeFixedH = 1; - } - - ulProcessingTime += InchiTimeElapsed(&ulTStart); - - - - if ( bInChI2Structure && !bInChI2InChI ) - { - /**********************************************************************/ - /* InChi --> Structure */ - /**********************************************************************/ - - - int bINChIOutputOptions = -#if ( I2S_MODIFY_OUTPUT == 1 ) - /* transfer user's InChI output options to serialization 10-12-2007 */ - ip_inp->bINChIOutputOptions & ( - INCHI_OUT_NO_AUX_INFO | /* do not output Aux Info */ - INCHI_OUT_SHORT_AUX_INFO | /* output short version of Aux Info */ - INCHI_OUT_ONLY_AUX_INFO | /* output only Aux Info */ - /* INCHI_OUT_EMBED_REC |*/ /* embed reconnected INChI into disconnected INChI */ - INCHI_OUT_SDFILE_ONLY | /* save input data in a Molfile instead of creating INChI */ - INCHI_OUT_XML | /* output xml INChI */ - INCHI_OUT_PLAIN_TEXT | /* output plain text INChI */ - INCHI_OUT_PLAIN_TEXT_COMMENTS | /* output plain text annotation */ - INCHI_OUT_XML_TEXT_COMMENTS | /* output xml text annotation */ - /* INCHI_OUT_WINCHI_WINDOW |*/ /* output into wINChI text window */ - INCHI_OUT_TABBED_OUTPUT | /* tab-delimited (only for plain text) */ - INCHI_OUT_SDFILE_ATOMS_DT | /* SDfile output H isotopes as D and T */ - INCHI_OUT_SDFILE_SPLIT | /* Split SDfile into components */ - 0); - - -#else - 0; -#endif - - - SRM srm; /* rules how to handle bonds to metal atoms */ - StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM]; - - - /* prepare parameters */ - - InchiTimeGet(&ulTStart); - - if ( bInputInStdFormat ) - { - if ( ip_inp->bINChIOutputOptions & INCHI_OUT_STDINCHI ) - bINChIOutputOptions |= INCHI_OUT_STDINCHI; - } - else - { - if ( ip_inp->bINChIOutputOptions & INCHI_OUT_SAVEOPT ) - bINChIOutputOptions |= INCHI_OUT_SAVEOPT; - } - - - - memset(pStruct, 0, sizeof(pStruct)); - - /* structure restore parms */ - SetUpSrm(&srm); - - /* eliminate Fixed-H InChI that are exactly same as the corresponding Mobile-H structures */ - RemoveFixHInChIIdentical2MobH(&OneInput); - - /*-- recheck layers after the elimination; get optional stereo flags --*/ - ret = DetectInpInchiCreationOptions(&OneInput, - &bHasSomeReconnected, &bHasMetal, - &bHasSomeFixedH, &nModeFlagsStereo, - &bTautFlags); - if ( ret < 0 ) - { - AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, - "Error in detecting input InChI options", "; "); - num_errors ++; - goto dealloc; - } - if ( bHasSomeFixedH && !bReqNonTaut ) - { - bHasSomeFixedH = 0; - } - /*---------------- set stereo flags ---------------------*/ - ip->nMode &= ~(REQ_MODE_STEREO | REQ_MODE_ISO_STEREO | - REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO | - REQ_MODE_CHIR_FLG_STEREO | - REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU); - ip->nMode |= nModeFlagsStereo; - - /* Remove Phosphine and Arsine Stereo Flags */ - ip->bTautFlags &= ~TG_FLAG_PHOSPHINE_STEREO; - ip->bTautFlags &= ~TG_FLAG_ARSINE_STEREO; - ip->bTautFlags &= ~TG_FLAG_FIX_SP3_BUG; - - ip->bTautFlags |= bTautFlags; - - /* mark Disconnected InChI components that are exactly came as Reconnected ones */ - /* Disconnected will have a negative number of the reconnected component */ - /* Reconnected will have a positive number of the disconnected component */ - MarkDisconectedIdenticalToReconnected (&OneInput); - - /*****************************************************************************/ - /* pay attention to: */ - /* 1) .nLink < 0 in Disonnected which means InChI is same as in Reconnected */ - /* The component in Reconnected has .nLink pointing to the Disconnected; */ - /* each .nLink = (1+component index) or -(1+component index) */ - /* In the future .nLink>0 in Disconnected shall point to the Reconnectrd */ - /* component from which it was created */ - /* 2) Currently reversed structures from Disconnected components are created */ - /* and abandoned if Reconnected layer exists */ - /* 3) Connect/disconnect H depends on the presence of atom/bond parity */ - /* The combined Mobile/Fixed-H parity should be set for Fixed-H components*/ - /* 4) No comparison of the Disconnected layer is done if Reconnected exists */ - /* 5) Reading InChI was not fully tested in case one component has stereo in */ - /* both Mobile-H and Fixed-H layers while another component has stereo */ - /* only in Mobile-H layer */ - /*****************************************************************************/ - - /* main conversion InChI->Structure for each component and */ - /* after that pStruct[iRec][iMobH][iComponent].at2 is the structure, */ - /* pStruct[iRec][iMobH][iComponent].RevInChI full InChI for the structure */ - /* In case of both Fixed-H and Mobile-H layers the results are in iMobH=0 */ - /* In case of only Mobile-H/Main layer the results are in iMobH=1 */ - ulProcessingTime += InchiTimeElapsed(&ulTStart); - - sd->ulStructTime = 0; - ret = AllInchiToStructure(ip, sd, num_inp, szCurHdr, &srm, bHasSomeFixedH, pStruct, &OneInput); - - ulProcessingTime += sd->ulStructTime; - InchiTimeGet(&ulTStart); - /* ret < 0 is error code; ret > 0 is number of errors */ - /* in pStruct[iInchiRec][iMobileH][iComponent].nError */ - - if ( ret) - { - /* conversion error */ - num_errors ++; - goto dealloc; - } - - - /* an attempt to fix the numumber of removed protons in case of Mobile-H */ - if ( !OneInput.nNumProtons[INCHI_BAS][TAUT_YES].pNumProtons && - !OneInput.nNumProtons[INCHI_REC][TAUT_YES].pNumProtons ) - { - ret = AddProtonAndIsoHBalanceToMobHStruct(ip, sd, num_inp, bHasSomeFixedH, szCurHdr, pStruct, &OneInput); - if ( ret < 0 ) - { - AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "Add/Remove protons error", "; "); - num_errors ++; - goto dealloc; - } - } - - /* compare InChI from the Reversed Structure to the original input InChI */ - - ret = CompareAllOrigInchiToRevInChI(pStruct, &OneInput, bHasSomeFixedH, num_inp, szCurHdr); - if ( ret < 0 ) - { - AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "InChI compare error", "; "); - num_errors ++; - goto dealloc; - } - - ret = CompareAllDisconnectedOrigInchiToRevInChI(pStruct, &OneInput, bHasSomeFixedH, - num_inp, szCurHdr); - if ( ret < 0 ) - { - AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "InChI compare2 error", "; "); - num_errors ++; - goto dealloc; - } - - if ( WarningFlags ) - { - for ( i = 0; i < 2; i ++ ) - { - for ( j = 0; j < TAUT_NUM; j ++ ) - { - WarningFlags[i][j] = (unsigned long)OneInput.CompareInchiFlags[i][j]; - } - } - } - - ulProcessingTime += InchiTimeElapsed(&ulTStart); - -#ifndef COMPILE_ANSI_ONLY - ret = DisplayStructureComponents(ip, sd, num_inp, szCurHdr, - &srm, bReqNonTaut, pStruct, &OneInput); - if ( ret < 0 ) - { - AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "Display structure error", "; "); - } -#endif - InchiTimeGet(&ulTStart); - ret = MergeStructureComponents(ip, sd, num_inp, szCurHdr, &srm, bReqNonTaut, pStruct, &OneInput); - ulProcessingTime += InchiTimeElapsed(&ulTStart); - if ( ret < 0 ) - { - AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "Merge Components error", "; "); - num_errors ++; - goto dealloc; - } - - - -#ifdef TARGET_API_LIB -/*------------- for debug only ------------------- - InchiTimeGet(&ulTStart); - ret = OutputInChIOutOfStrFromINChI(ip, sd, num_inp, 0, - pOut, pLog, &OneInput, - save_opt_bits); - ulProcessingTime += InchiTimeElapsed(&ulTStart); - if ( ret < 0 ) { - AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "Restored structure to InChI conversion failed", "; "); - goto dealloc; - } --------------------------------------------------*/ - if ( at && num_at ) - { - *at = OneInput.atom; - *num_at = OneInput.num_atoms; - OneInput.atom = NULL; - } -#else - InchiTimeGet(&ulTStart); - ret = OutputInChIOutOfStrFromINChI(ip, sd, num_inp, bINChIOutputOptions, - pOut, /*pLog*/ NULL, &OneInput, - bHasSomeFixedH, save_opt_bits); - ulProcessingTime += InchiTimeElapsed(&ulTStart); - if ( ret < 0 ) - { - AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "Restored structure to InChI conversion error", "; "); - num_errors ++; - goto dealloc; - } -#endif - - if ( szMessage ) - { - int len; - InchiTimeGet(&ulTStart); - FillOutCompareMessage(szMessage, nMessageLen, OneInput.CompareInchiFlags[0]); - if ( OneInput.CompareInchiFlags[1][0] || OneInput.CompareInchiFlags[1][1] ) - { - AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "Disconnected: ", "; "); - FillOutCompareMessage(szMessage, nMessageLen, OneInput.CompareInchiFlags[1]); - } - /* add a metal warning */ - if ( bHasMetal && nInitLenMessage < (len=(int)strlen(szMessage)) ) - { - char szMetal[] = " (Metal compound)"; - int shift; - if ( len + (int)sizeof(szMetal) > nMessageLen ) { - len = nMessageLen - (int)sizeof(szMetal); - } - shift = nInitLenMessage + (int)sizeof(szMetal) - 1; - memmove(szMessage+shift, szMessage + nInitLenMessage, (len-nInitLenMessage)*sizeof(szMessage[0])); - memcpy(szMessage + nInitLenMessage, szMetal, sizeof(szMetal)-sizeof(szMessage[0])); - szMessage[shift+len-nInitLenMessage] = '\0'; - } - ulProcessingTime += InchiTimeElapsed(&ulTStart); - } - - ret = 0; - - /* deallocate */ -dealloc: - if ( ret ) - { - if ( ret > 0 ) - { - int iRec, iMob, iComp, nComp, len; - char szTemp[128]; - AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "*Conversion failed on component(s)", "; "); - len = strlen(szMessage); - for ( iRec = 0; iRec < INCHI_NUM; iRec ++ ) - { - for ( iMob = bHasSomeFixedH? TAUT_NON : TAUT_YES; iMob < TAUT_NUM; iMob ++ ) - { - nComp = OneInput.nNumComponents[iRec][iMob]; - if ( !pStruct[iRec][iMob] ) - { - continue; - } - for ( iComp = 0; iComp < nComp; iComp ++ ) - { - if ( pStruct[iRec][iMob][iComp].nError ) - { - char *szFormula = OneInput.pInpInChI[iRec][iMob][iComp].szHillFormula; - sprintf (szTemp, -#if ( FIX_DALKE_BUGS == 1 ) - " %s%s%d(%.96s)", -#else - " %s%s%d(%s)", -#endif - !bHasSomeReconnected? "" : iRec? "R" : "D", - !bHasSomeFixedH? "": iMob? "M" : "F", - iComp + 1, szFormula? szFormula : "???"); - AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, szTemp, NULL); - } - } - } - } - } - else - { - if ( ret == CT_USER_QUIT_ERR ) - { - AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "*Terminated by the user*", "; "); - } else { - AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "*Conversion failed*", "; "); - } - } - } - InchiTimeGet(&ulTStart); - /* print one structure report */ - if ( szMsg && nMsgLen > 1 ) - { - int len = inchi_min( (int)strlen(szMessage), nMsgLen-1); - if ( len > 0 ) - { - memcpy( szMsg, szMessage, len); - szMsg[len] = '\0'; - } - else - { - szMsg[0] = '\0'; - } - } - if ( nInitLenMessage < (int)strlen(szMessage) ) - { - inchi_ios_eprint(pLog, "%s\n", szMessage); - } -#ifndef TARGET_API_LIB - else - { - /*^^^inchi_ios_eprint( stderr, "%s\r", szMessage );*/ - inchi_fprintf( stderr, "%s\r", szMessage ); - } -#endif - - - FreeStrFromINChI( pStruct, OneInput.nNumComponents ); - FreeInpInChI( &OneInput ); - if ( szCurHdr ) - { - inchi_free( szCurHdr ); - szCurHdr = NULL; - } - INCHI_HEAPCHK - ulProcessingTime += InchiTimeElapsed( &ulTStart ); - if ( ret < 0 ) - { - goto exit_error; - } - - } /* if ( bInChI2Structure && !bInChI2InChI ) */ - - - - else if ( !bInChI2Structure && bInChI2InChI ) - { - /**********************************************************************/ - /* InChi --> InChI string(s) */ - /**********************************************************************/ - - int tmp = ip->bNoStructLabels; - - InchiTimeGet( &ulTStart ); - - ip->bNoStructLabels = 1; - INCHI_HEAPCHK - ip->pSdfValue = NULL; - ip->pSdfLabel = NULL; -#if ( FIX_DALKE_BUGS == 1 ) - SetHillFormFromInChI( &OneInput ); -#endif - - - ret = OutputInChIAsRequested(pOut, pLog, ip, sd, &OneInput, - num_components, nModeProtonIsoExchgH, - num_inp, save_opt_bits); - - -#if ( !defined(TARGET_API_LIB) && defined(TARGET_EXE_STANDALONE) ) - /*^^^ calculate InChIKey if requested */ - /* However, do not calculat/write it if this function is called from within dll */ - { - char ik_string[256]; /*^^^ Resulting InChIKey string */ - int ik_ret=0; /*^^^ InChIKey-calc result code */ - int xhash1, xhash2; - char szXtra1[65], szXtra2[65]; - - inchi_ios_flush2(pLog, stderr); - - /*^^^ post-1.02b addition - correctly treat tabbed output with InChIKey */ - if ( ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT ) - if ( ip->bCalcInChIHash != INCHIHASH_NONE ) - if (pOut->s.pStr) - if (pOut->s.nUsedLength>0) - if (pOut->s.pStr[pOut->s.nUsedLength-1]=='\n') - /* replace LF with TAB */ - pOut->s.pStr[pOut->s.nUsedLength-1] = '\t'; - - - if ( ip->bCalcInChIHash != INCHIHASH_NONE ) - { - char *buf = NULL; - size_t slen = pOut->s.nUsedLength; - extract_inchi_substring(&buf, pOut->s.pStr, slen); - - if (NULL!=buf) - { - xhash1 = xhash2 = 0; - if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1 ) || - ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) - xhash1 = 1; - if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA2 ) || - ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) - xhash2 = 1; - ik_ret = GetINCHIKeyFromINCHI(buf, xhash1, xhash2, - ik_string, szXtra1, szXtra2); - inchi_free(buf); - } - else - ik_ret = INCHIKEY_NOT_ENOUGH_MEMORY; - - - if (ik_ret==INCHIKEY_OK) - { - inchi_ios_print(pOut, "InChIKey=%-s\n",ik_string); - } - else - { - inchi_ios_print(pLog, "Warning (Could not compute InChIKey: ", num_inp); - switch(ik_ret) - { - case INCHIKEY_UNKNOWN_ERROR: - inchi_ios_print(pLog, "unresolved error)"); - break; - case INCHIKEY_EMPTY_INPUT: - inchi_ios_print(pLog, "got an empty string)"); - break; - case INCHIKEY_INVALID_INCHI_PREFIX: - case INCHIKEY_INVALID_INCHI: - case INCHIKEY_INVALID_STD_INCHI: - inchi_ios_print(pLog, "got non-InChI string)"); - break; - case INCHIKEY_NOT_ENOUGH_MEMORY: - inchi_ios_print(pLog, "not enough memory to treat the string)"); - break; - default:inchi_ios_print(pLog, "internal program error)"); - break; - } - inchi_ios_print(pLog, " structure #%-lu.\n", num_inp); - if ( ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT ) - inchi_ios_print(pOut, "\n"); - } /* if (ip->bCalcInChIHash!=INCHIHASH_NONE) */ - - - inchi_ios_flush(pOut); - inchi_ios_flush2(pLog, stderr); - - } - - else - inchi_ios_flush(pOut); - } /* calculate InChIKey if requested */ -#endif - - ip->bNoStructLabels = tmp; - -#ifndef TARGET_API_LIB - if ( ret < 0 ) - { - if ( szCurHdr && szCurHdr[0] ) - { - inchi_ios_eprint( pLog, "Error %d creating InChI string %s\n", ret, szCurHdr ); - } - else - { - inchi_ios_eprint( pLog, "Error %d creating InChI string, Structure %ld\n", ret, num_inp ); - } - num_errors ++; - } -#if ( !defined(TARGET_API_LIB) && !defined(TARGET_EXE_STANDALONE) ) - else - if ( szCurHdr && szCurHdr[0] ) - { - inchi_fprintf( stderr, "%s\r", szCurHdr ); - } -#endif -#endif - - if ( szCurHdr ) - { - inchi_free( szCurHdr ); - szCurHdr = NULL; - } - - INCHI_HEAPCHK - ulProcessingTime += InchiTimeElapsed( &ulTStart ); - - } /* if ( !bInChI2Structure && bInChI2InChI ) */ - - else - { - inchi_ios_eprint( pLog, "\nWrong command line options: expected Inch2Struct or Inchi2Inchi\n", num_inp ); - break; - } - - - if ( nReadStatus == RI_ERR_EOF ) - { - break; - } - - - } /* InChI has been successfully read */ - - else - - { - - /* InChI could not be read */ - if ( nReadStatus == RI_ERR_EOF && nErr == 0 && pState == 0 && !strHdr ) - { - inchi_ios_eprint( pLog, "\nEnd of file detected after structure %ld. \n", num_inp ); - } - else - { - /* output InChI parsing error message */ - char szHdrSimulation[128]; - num_inp ++; - sprintf( szHdrSimulation, "Structure: %ld", num_inp ); - inchi_ios_eprint( pLog, "\n%s %s (%d) in %s (%d)\n", strHdr? strHdr : szHdrSimulation, getInchiErrName(nErr), nErr, getInchiStateReadErr(pState), pState ); - num_errors ++; - num_processed ++; - } - if ( strHdr ) - { - inchi_free( strHdr ); - strHdr = NULL; - } - if ( szCurHdr ) - { - inchi_free( szCurHdr ); - szCurHdr = NULL; - } - FreeInpInChI( &OneInput ); - } - - -#ifdef TARGET_EXE_STANDALONE -#ifndef TARGET_API_LIB - inchi_ios_flush(pOut); - inchi_ios_flush2(pLog, stderr); -#endif -#endif - - -#ifdef TARGET_API_LIB - break; /* exit after the 1st structure */ -#endif - - - } /* while */ - - - - -exit_error: - - FreeInpInChI( &OneInput ); - if ( strHdr ) - { - inchi_free( strHdr ); - strHdr = NULL; - } - if ( pLine->str ) - { - inchi_free( pLine->str ); - } - if ( szCurHdr ) - { - inchi_free( szCurHdr ); - szCurHdr = NULL; - } - - INCHI_HEAPCHK - - if ( sd_inp ) - { - sd_inp->ulStructTime = ulProcessingTime; - sd_inp->fPtrStart = num_processed; - sd_inp->fPtrEnd = num_errors; - } - return ret; -} - - - -/**********************************************************************************************/ -int OutputInChIAsRequested(INCHI_IOSTREAM *pOut, INCHI_IOSTREAM *pLog, - ICHICONST INPUT_PARMS *ip_inp, - STRUCT_DATA *sd_inp, - InpInChI *OneInput, - int num_components[INCHI_NUM], - MODE_PIXH nModeProtonIsoExchgH[INCHI_NUM], - long num_inp, unsigned char save_opt_bits) -{ - int j, k, k1, k2, ret2=0, iINChI, iINChI1, iINChI2; - PINChI2 *pINChI[INCHI_NUM]; - PINChI_Aux2 *pINChI_Aux[INCHI_NUM]; - int bReqNonTaut; - int bHasSomeReconnected; - - INPUT_PARMS ip_local; - STRUCT_DATA sd_local; - INPUT_PARMS *ip = &ip_local; - STRUCT_DATA *sd = &sd_local; - NORM_CANON_FLAGS ncFlags; - NORM_CANON_FLAGS *pncFlags = &ncFlags; - const int nStrLen = NSTRLEN; - char *pStr = NULL; - int nRet1, bSortPrintINChIFlags; - int bReqSplitOutputInChI; - int nNumOutputComponents; - - nRet1 = 0; - k1 = k2 = 0; - memset( pncFlags, 0, sizeof(*pncFlags) ); - memset( pINChI, 0, sizeof(pINChI) ); - memset( pINChI_Aux, 0, sizeof(pINChI_Aux) ); - - *ip = *ip_inp; - *sd = *sd_inp; - bHasSomeReconnected = 0; - bSortPrintINChIFlags = 0; - nNumOutputComponents = 0; - bReqNonTaut = (0 != (ip->nMode & REQ_MODE_BASIC)); - bReqSplitOutputInChI = (0 != (ip->bReadInChIOptions & READ_INCHI_SPLIT_OUTPUT)); - - INCHI_HEAPCHK - - if ( num_components[INCHI_BAS] ) - { - MYREALLOC2(PINChI2, PINChI_Aux2, pINChI[INCHI_BAS], pINChI_Aux[INCHI_BAS], num_components[INCHI_BAS], num_components[INCHI_BAS], k1); - } - if ( num_components[INCHI_REC] ) - { - MYREALLOC2(PINChI2, PINChI_Aux2, pINChI[INCHI_REC], pINChI_Aux[INCHI_REC], num_components[INCHI_REC], num_components[INCHI_REC], k2); - } - pStr = (char*) inchi_malloc( nStrLen * sizeof(pStr[0]) ); - - INCHI_HEAPCHK - - if ( k1 || k2 || !pStr ) - { - ret2 = RI_ERR_ALLOC; - goto exit_error; - } - - if ( num_components[INCHI_REC] && - (ip->bTautFlags & TG_FLAG_RECONNECT_COORD) && - (ip->bTautFlags & TG_FLAG_DISCONNECT_COORD) ) - { - sd->bTautFlagsDone[0] |= TG_FLAG_DISCONNECT_COORD_DONE; - bHasSomeReconnected = 1; - } - - - for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) - { - for ( j = 0; j < TAUT_NUM; j ++ ) - { - if ( bReqNonTaut || j != TAUT_NON && OneInput->pInpInChI[iINChI][j] ) - { - for ( k = 0; k < num_components[iINChI]; k ++ ) - { - /* allocate InChI & AuxInfo */ - if ( !(pINChI[iINChI][k][j] = (INChI *) inchi_calloc(1, sizeof(INChI)) ) ) - { - ret2 = RI_ERR_ALLOC; - goto exit_error; - } - if ( !(pINChI_Aux[iINChI][k][j] = (INChI_Aux *) inchi_calloc(1, sizeof(INChI_Aux)) ) ) - { - ret2 = RI_ERR_ALLOC; - goto exit_error; - } - /* copy InChI & AuxInfo */ - if ( k < OneInput->nNumComponents[iINChI][j] ) - { - - /* copy InChI */ - *pINChI[iINChI][k][j] = OneInput->pInpInChI[iINChI][j][k]; - memset(&OneInput->pInpInChI[iINChI][j][k], 0, sizeof(OneInput->pInpInChI[iINChI][j][k])); - INCHI_HEAPCHK - /* take care of protons in AuxInfo */ - - if ( nModeProtonIsoExchgH[iINChI] == MODE_PIXH_ADD_TO_EACH && j == TAUT_YES ) - { - pINChI_Aux[iINChI][k][j]->nNumRemovedProtons = - OneInput->nNumProtons[iINChI][j].pNumProtons[k].nNumRemovedProtons; - for ( k1 = 0; k1 < NUM_H_ISOTOPES; k1 ++ ) - { - pINChI_Aux[iINChI][k][j]->nNumRemovedIsotopicH[k1] = - OneInput->nNumProtons[iINChI][j].pNumProtons[k].nNumRemovedIsotopicH[k1]; - } - INCHI_HEAPCHK - } - else if ( !k && nModeProtonIsoExchgH[iINChI] == MODE_PIXH_ADD_TO_FIRST || - k+1 == OneInput->nNumComponents[iINChI][j] && - nModeProtonIsoExchgH[iINChI] == MODE_PIXH_ADD_A_PIXH_COMPONENT ) - { - /* add protons and exchangeable isotopic H to the first component's AuxInfo */ - pINChI_Aux[iINChI][k][j]->nNumRemovedProtons = OneInput->nNumProtons[iINChI][j].nNumRemovedProtons; - for ( k1 = 0; k1 < NUM_H_ISOTOPES; k1 ++ ) - { - pINChI_Aux[iINChI][k][j]->nNumRemovedIsotopicH[k1] = - OneInput->nNumProtons[iINChI][j].nNumRemovedIsotopicH[k1]; - } - INCHI_HEAPCHK - } - else - { - pINChI_Aux[iINChI][k][j]->bDeleted = pINChI[iINChI][k][j]->bDeleted; - } - - if ( j == TAUT_YES && pINChI[iINChI][k][j] && pINChI[iINChI][k][j]->nNumberOfAtoms && - !pINChI[iINChI][k][j]->nNum_H_fixed ) - { - /* serializer crashes if it is not allocated */ - pINChI[iINChI][k][j]->nNum_H_fixed = (S_CHAR *)inchi_calloc(pINChI[iINChI][k][j]->nNumberOfAtoms+1, sizeof(pINChI[0][0][0]->nNum_H_fixed[0]) ); - } - - if ( j == TAUT_YES && k < OneInput->nNumComponents[iINChI][TAUT_NON] && - pINChI[iINChI][k][j] && pINChI[iINChI][k][j]->nNumberOfAtoms && - pINChI[iINChI][k][TAUT_NON] && pINChI[iINChI][k][TAUT_NON]->nNumberOfAtoms && - !CompareReversedINChI( pINChI[iINChI][k][j], pINChI[iINChI][k][TAUT_NON], NULL, NULL ) ) { - pINChI[iINChI][k][TAUT_NON]->nNumberOfAtoms = 0; /* eliminate non-taut equal to taut */ - } - - - } - else - { - /* extra component, usually it is a Mobile H component */ - /* corresponding to a free proton component in Fixed H */ - pINChI[iINChI][k][j]->bDeleted = 1; - pINChI_Aux[iINChI][k][j]->bDeleted = 1; - } - - } /* k */ - - } /* if ( bReqNonTaut || j != TAUT_NON && OneInput->pInpInChI[iINChI][j] ) */ - - if ( OneInput->pInpInChI[iINChI][j] ) - { - INCHI_HEAPCHK - inchi_free(OneInput->pInpInChI[iINChI][j]); - OneInput->pInpInChI[iINChI][j] = NULL; - } - - } /* j */ - - } /* iINChI */ - - if ( bReqSplitOutputInChI ) - { - if ( bHasSomeReconnected ) - { - iINChI1 = INCHI_REC; /* only reconnected */ - iINChI2 = INCHI_NUM; - sd->num_components[INCHI_BAS] = sd->num_components[INCHI_REC]; - } - else - { - iINChI1 = 0; /* only disconnected */ - iINChI2 = iINChI1+1; - } - sd->num_components[INCHI_REC] = 0; /* treat reconnected as connected */ - nNumOutputComponents = sd->num_components[INCHI_BAS]; - } - else - { - iINChI1 = 0; - iINChI2 = INCHI_NUM; - nNumOutputComponents = 1; - } - - - for ( k1 = 0, k2 = (bReqSplitOutputInChI? k1+1 : nNumOutputComponents); k1 < k2 && k1 < nNumOutputComponents; k1=k2, k2 ++ ) - { - - if ( bReqSplitOutputInChI ) - { - sd->num_components[INCHI_BAS] = 1; - sd->num_components[INCHI_REC] = 0; - /* additional data */ - sd->num_non_taut[INCHI_BAS] = - sd->num_taut[INCHI_BAS] = - sd->num_non_taut[INCHI_REC] = - sd->num_taut[INCHI_REC] = 0; - iINChI = iINChI1; - for ( j = 0; j < TAUT_NUM && sd->num_components[iINChI]; j ++ ) - { - for ( k = k1; k < k2; k ++ ) - { - /* find where the current processed structure is located */ - int cur_is_in_non_taut = (pINChI[iINChI][k][TAUT_NON] && pINChI[iINChI][k][TAUT_NON]->nNumberOfAtoms>0); - int cur_is_in_taut = (pINChI[iINChI][k][TAUT_YES] && pINChI[iINChI][k][TAUT_YES]->nNumberOfAtoms>0); - int cur_is_non_taut = cur_is_in_non_taut && 0 == pINChI[iINChI][k][TAUT_NON]->lenTautomer || - cur_is_in_taut && 0 == pINChI[iINChI][k][TAUT_YES]->lenTautomer; - int cur_is_taut = cur_is_in_taut && 0 < pINChI[iINChI][k][TAUT_YES]->lenTautomer; - if ( cur_is_non_taut + cur_is_taut ) - { - /* count tautomeric and non-tautomeric components of the structures */ - /* - int j1 = cur_is_in_non_taut? TAUT_NON:TAUT_YES; - int j2 = cur_is_in_taut? TAUT_YES:TAUT_NON; - */ - sd->num_non_taut[INCHI_BAS] += cur_is_non_taut; - sd->num_taut[INCHI_BAS] += cur_is_taut; - } - } - } - INCHI_HEAPCHK - } - else - { - sd->num_components[INCHI_BAS] = inchi_max( OneInput->nNumComponents[INCHI_BAS][TAUT_YES], - OneInput->nNumComponents[INCHI_BAS][TAUT_NON] ); - sd->num_components[INCHI_REC] = inchi_max( OneInput->nNumComponents[INCHI_REC][TAUT_YES], - OneInput->nNumComponents[INCHI_REC][TAUT_NON] ); - - /* additional data needed for SortAndPrintINChI() */ - for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) - { - sd->num_non_taut[iINChI] = - sd->num_taut[iINChI] = 0; - for ( j = 0; j < TAUT_NUM && sd->num_components[iINChI]; j ++ ) - { - for ( k = k1; k < k2; k ++ ) - { - /* find where the current processed structure is located */ - int cur_is_in_non_taut = (pINChI[iINChI][k][TAUT_NON] && pINChI[iINChI][k][TAUT_NON]->nNumberOfAtoms>0); - int cur_is_in_taut = (pINChI[iINChI][k][TAUT_YES] && pINChI[iINChI][k][TAUT_YES]->nNumberOfAtoms>0); - int cur_is_non_taut = cur_is_in_non_taut && 0 == pINChI[iINChI][k][TAUT_NON]->lenTautomer || - cur_is_in_taut && 0 == pINChI[iINChI][k][TAUT_YES]->lenTautomer; - int cur_is_taut = cur_is_in_taut && 0 < pINChI[iINChI][k][TAUT_YES]->lenTautomer; - if ( cur_is_non_taut + cur_is_taut ) { - /* count tautomeric and non-tautomeric components of the structures */ - /* - int j1 = cur_is_in_non_taut? TAUT_NON:TAUT_YES; - int j2 = cur_is_in_taut? TAUT_YES:TAUT_NON; - */ - sd->num_non_taut[iINChI] += cur_is_non_taut; - sd->num_taut[iINChI] += cur_is_taut; - } - } - } - } - INCHI_HEAPCHK - } - if ( bReqSplitOutputInChI ) - { - /* output components one by one (for splitting input InChI into components) */ - PINChI2 *pInChI_2[INCHI_NUM]; - PINChI_Aux2 *pInChI_Aux_2[INCHI_NUM]; - INChI *pInChI_1[1][2]; - INChI_Aux *pInChI_Aux_1[1][2]; - memset( pInChI_2, 0, sizeof(pInChI_2) ); - memset( pInChI_Aux_2, 0, sizeof(pInChI_Aux_2) ); - - for ( j = 0; j < TAUT_NUM; j ++ ) - { - pInChI_1[0][j] = pINChI[iINChI1][k1][j]; - pInChI_Aux_1[0][j] = pINChI_Aux[iINChI1][k1][j]; - } - pInChI_2[INCHI_BAS] = pInChI_1; - pInChI_Aux_2[INCHI_BAS] = pInChI_Aux_1; - /* make sure purely reconnected InChI is marked as ReChI, not InChI */ - if ( bHasSomeReconnected && - (bInChIHasReconnectedMetal( pInChI_1[0][TAUT_YES] ) || - bInChIHasReconnectedMetal( pInChI_1[0][TAUT_NON] ) ) ) - { - bSortPrintINChIFlags = FLAG_SORT_PRINT_ReChI_PREFIX; - } - else - { - bSortPrintINChIFlags = 0; - } - INCHI_HEAPCHK - nRet1 = SortAndPrintINChI(pOut, pStr, nStrLen, pLog, ip, NULL /*orig_inp_data*/, NULL /*prep_inp_data*/, - NULL /*composite_norm_data*/, NULL /*pOrigStruct*/, - sd->num_components, sd->num_non_taut, sd->num_taut, - sd->bTautFlags, sd->bTautFlagsDone, pncFlags, num_inp, - pInChI_2, pInChI_Aux_2, &bSortPrintINChIFlags, - save_opt_bits); - INCHI_HEAPCHK - } - else - { - INCHI_HEAPCHK - bSortPrintINChIFlags = 0; - nRet1 = SortAndPrintINChI(pOut, pStr, nStrLen, pLog, ip, NULL /*orig_inp_data*/, NULL /*prep_inp_data*/, - NULL /*composite_norm_data*/, NULL /*pOrigStruct*/, - sd->num_components, sd->num_non_taut, sd->num_taut, - sd->bTautFlags, sd->bTautFlagsDone, pncFlags, num_inp, - pINChI, pINChI_Aux, &bSortPrintINChIFlags, - save_opt_bits); - INCHI_HEAPCHK - } - if ( nRet1 == _IS_FATAL || nRet1 == _IS_ERROR ) - { - break; - } - } - - - INCHI_HEAPCHK - FreeAllINChIArrays( pINChI, pINChI_Aux, num_components ); - INCHI_HEAPCHK - - for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) - { - for ( j = 0; j < TAUT_NUM; j ++ ) - { - if ( OneInput->nNumProtons[iINChI][j].pNumProtons ) - { - inchi_free( OneInput->nNumProtons[iINChI][j].pNumProtons ); - OneInput->nNumProtons[iINChI][j].pNumProtons = NULL; - } - } - } - - INCHI_HEAPCHK - if ( nRet1 == _IS_FATAL || nRet1 == _IS_ERROR ) - { - ret2 = RI_ERR_PROGR; - } - -exit_error: - if ( pStr ) - inchi_free( pStr ); - return ret2; -} - -/**************************************************************************************/ -int GetNumNeighborsFromInchi( INChI *pInChI, AT_NUMB nAtNumber ) -{ - int i, j, n_vertex, n_neigh, nNumNeigh, bTautAtom, nNumH, nTotNumNeigh, num_atoms; - AT_NUMB taut_at_number; - nAtNumber -= 1; - nNumNeigh = 0; /* number of bonds */ - bTautAtom = 0; /* 1 if atom belongs to a Mobile-H group */ - nNumH = 0; /* number of terminal neighbors H */ - num_atoms = pInChI->nNumberOfAtoms; - /* from RestoreAtomConnectionsSetStereo() */ - /* Connection table structure: - Vert(1) [, Neigh(11), Neigh(12),...], Vert(2) [, Neigh(2,1), Neigh(2,2),...] ... - where Neigh(i,1) < Neigh(i,2) <... < Vert(i); - Vert(i) < Vert(i+1) - */ - for ( i = 1, n_vertex = pInChI->nConnTable[0]-1; i < pInChI->lenConnTable; i ++ ) { - if ( (n_neigh = pInChI->nConnTable[i]-1) < n_vertex ) { - /* vertex - neighbor connection */ - nNumNeigh += ( nAtNumber == n_vertex || nAtNumber == n_neigh ); - } else - /* n_neigh is the next vertex */ - if ( (n_vertex = n_neigh) >= num_atoms ) { - return RI_ERR_PROGR; - } - } - /* is atom tautomeric, from GetTgroupInfoFromInChI() */ - if ( pInChI && pInChI->lenTautomer > 1 && pInChI->nTautomer && pInChI->nTautomer[0] > 0 ) { - int itg, len_tg; - int tot_len_tg = pInChI->lenTautomer - T_GROUP_HDR_LEN*pInChI->nTautomer[0] - 1; /* number of endpoints */ - j = 1; /* index in pInChI->nTautomer[] */ - i = 0; /* index in ti->nEndpointAtomNumber[] */ - for ( itg = 0; itg < pInChI->nTautomer[0]; itg ++ ) { - len_tg = pInChI->nTautomer[j]; /* t-group length not including pInChI->nTautomer[j] */ - j += T_GROUP_HDR_LEN; /* skip t-group header */ - len_tg -= T_GROUP_HDR_LEN-1; - for( ; 0 < len_tg --; j ++, i ++ ) { - taut_at_number = pInChI->nTautomer[j]-1; /* Mobile-H group atom number */ - bTautAtom += (taut_at_number == nAtNumber); - } - } - if ( i != tot_len_tg ) { - return RI_ERR_PROGR; - } - } - /* count hydrogen neighbors */ - if ( pInChI->nNum_H ) { - nNumH = pInChI->nNum_H[nAtNumber]; - } - /* conclusion: if not tautomeric then return positive number, otherwise add 1000 */ - nTotNumNeigh = nNumNeigh + nNumH; - if ( bTautAtom ) { - nTotNumNeigh += 1000; - } - return nTotNumNeigh; -} -/**************************************************************************************/ -int CountStereoTypes( INChI *pInChI, int *num_known_SB, int *num_known_SC, - int *num_unk_und_SB, int *num_unk_und_SC, - int *num_SC_PIII, int *num_SC_AsIII) -{ - static U_CHAR el_number_P=0, el_number_As=0; - INChI_Stereo *Stereo; - int i, ret; - AT_NUMB nAtNumber; - U_CHAR el_number; - if ( !pInChI->nNumberOfAtoms || pInChI->bDeleted ) { - return 0; /* no InChI */ - } - Stereo = (pInChI->StereoIsotopic && - (pInChI->StereoIsotopic->nNumberOfStereoBonds + - pInChI->StereoIsotopic->nNumberOfStereoCenters ))? pInChI->StereoIsotopic: - (pInChI->Stereo && - (pInChI->Stereo->nNumberOfStereoBonds + - pInChI->Stereo->nNumberOfStereoCenters ))? pInChI->Stereo : NULL; - if ( !Stereo ) { - return 1; /* No Stereo */ - } - /* one-time initialization */ - if ( !el_number_P ) { - el_number_P = (U_CHAR)get_periodic_table_number( "P" ); - el_number_As = (U_CHAR)get_periodic_table_number( "As" ); - } - /* count SB and cumulenes */ - for ( i = 0; i < Stereo->nNumberOfStereoBonds; i ++ ) { - if ( ATOM_PARITY_WELL_DEF(Stereo->b_parity[i]) ) { - (*num_known_SB) ++; - } else { - (*num_unk_und_SB) ++; - } - } - /* count SC and allenes */ - for ( i = 0; i < Stereo->nNumberOfStereoCenters; i ++ ) { - if ( !(nAtNumber = Stereo->nNumber[i]) || nAtNumber > pInChI->nNumberOfAtoms ) { - return RI_ERR_PROGR; /* wrong data, should never happen */ - } - if ( ATOM_PARITY_WELL_DEF(Stereo->t_parity[i]) ) { - (*num_known_SC) ++; - } else { - (*num_unk_und_SC) ++; - } - el_number = pInChI->nAtom[nAtNumber-1]; - if ( el_number != el_number_P && el_number != el_number_As ) { - continue; - } - ret = GetNumNeighborsFromInchi( pInChI, nAtNumber ); - if ( ret < 0 ) { - return ret; - } - if ( 3 == ret ) { - *num_SC_PIII += (el_number_P == el_number); - *num_SC_AsIII += (el_number_As == el_number); - } - } - return 2; /* Has Stereo */ -} -/**************************************************************************************/ -int bInpInchiComponentExists( InpInChI *pOneInput, int iInChI, int bMobileH, int k ) -{ - if ( INCHI_BAS != iInChI && iInChI != INCHI_REC || - TAUT_NON != bMobileH && TAUT_YES != bMobileH || k < 0 ) { - return 0; - } - return ( k < pOneInput->nNumComponents[iInChI][bMobileH] && - pOneInput->pInpInChI[iInChI][bMobileH] && - pOneInput->pInpInChI[iInChI][bMobileH][k].nNumberOfAtoms > 0 && - !pOneInput->pInpInChI[iInChI][bMobileH][k].bDeleted ); -} -/**************************************************************************************/ -int bInpInchiComponentDeleted( InpInChI *pOneInput, int iInChI, int bMobileH, int k ) -{ - if ( INCHI_BAS != iInChI && iInChI != INCHI_REC || - TAUT_NON != bMobileH && TAUT_YES != bMobileH || k < 0 ) { - return 0; - } - return ( k < pOneInput->nNumComponents[iInChI][bMobileH] && - pOneInput->pInpInChI[iInChI][bMobileH] && - pOneInput->pInpInChI[iInChI][bMobileH][k].nNumberOfAtoms > 0 && - pOneInput->pInpInChI[iInChI][bMobileH][k].bDeleted ); -} -/**************************************************************************************/ -int bRevInchiComponentExists( StrFromINChI *pStruct, int iInChI, int bMobileH, int k ) -{ - if ( !pStruct || /*!pStruct->at2 ||*/ !pStruct->num_atoms || - INCHI_BAS != iInChI && iInChI != INCHI_REC || - TAUT_NON != bMobileH && TAUT_YES != bMobileH || k < 0 ) { - return 0; - } - return ( k < pStruct->RevInChI.num_components[iInChI] && - pStruct->RevInChI.pINChI[iInChI] && - pStruct->RevInChI.pINChI[iInChI][k][bMobileH] && - pStruct->RevInChI.pINChI[iInChI][k][bMobileH]->nNumberOfAtoms > 0 && - !pStruct->RevInChI.pINChI[iInChI][k][bMobileH]->bDeleted ); -} - -/**************************************************************************************/ -int bRevInchiComponentDeleted( StrFromINChI *pStruct, int iInChI, int bMobileH, int k ) -{ - if ( !pStruct || /*!pStruct->at2 ||*/ !pStruct->num_atoms || - INCHI_BAS != iInChI && iInChI != INCHI_REC || - TAUT_NON != bMobileH && TAUT_YES != bMobileH || k < 0 ) { - return 0; - } - return ( k < pStruct->RevInChI.num_components[iInChI] && - pStruct->RevInChI.pINChI[iInChI] && - pStruct->RevInChI.pINChI[iInChI][k][bMobileH] && - pStruct->RevInChI.pINChI[iInChI][k][bMobileH]->nNumberOfAtoms > 0 && - pStruct->RevInChI.pINChI[iInChI][k][bMobileH]->bDeleted ); -} - -/**************************************************************************************/ -int DetectInpInchiCreationOptions ( InpInChI *pOneInput, int *bHasReconnected, int *bHasMetal, - int *bHasFixedH, int *nModeFlagsStereo, int *bTautFlagsStereo ) -{ - int ret=0, bHasStereo; - int nModeFlagsValue=0, bTautFlagsValue; /* stereo flags */ - int iInChI, iMobileH, bIso, k, max_components, num_components; - INChI *pInChI; - int num_known_SB /*Stereo Bonds & Cumulenes >C==C==C==C< */; - int num_known_SC /* Stereo Centers & Allenes >C=C=C< */; - int num_unk_und_SB, num_unk_und_SC; - int num_SC_PIII, num_SC_AsIII; /* has Phosphine or Arsine stereo center(s) */ - - *bHasReconnected = *bHasFixedH = *nModeFlagsStereo = *bTautFlagsStereo = 0; - nModeFlagsValue = bTautFlagsValue = bHasStereo = 0; - num_known_SB = num_known_SC = num_unk_und_SB = num_unk_und_SC = num_SC_PIII = num_SC_AsIII = 0; - *bHasMetal = 0; - - for ( iInChI = 0; iInChI < INCHI_NUM; iInChI ++ ) - { - for ( iMobileH = 0; iMobileH < TAUT_NUM; iMobileH ++ ) - { - for ( bIso = 1; !nModeFlagsValue && 0 <= bIso; bIso -- ) - { - switch( pOneInput->s[iInChI][iMobileH][bIso] ) - { - case 1: /* SABS */ - nModeFlagsValue |= REQ_MODE_STEREO | REQ_MODE_ISO_STEREO; - break; - case 2: - nModeFlagsValue |= REQ_MODE_STEREO | REQ_MODE_ISO_STEREO | REQ_MODE_RELATIVE_STEREO; - break; - case 3: - nModeFlagsValue |= REQ_MODE_STEREO | REQ_MODE_ISO_STEREO | REQ_MODE_RACEMIC_STEREO; - } - } - - max_components = pOneInput->pInpInChI[iInChI][iMobileH]? - pOneInput->nNumComponents[iInChI][iMobileH] : 0; - - for ( k = num_components = 0; k < max_components; k ++ ) - { - pInChI = pOneInput->pInpInChI[iInChI][iMobileH] + k; - ret = CountStereoTypes(pInChI, - &num_known_SB, &num_known_SC, - &num_unk_und_SB, &num_unk_und_SC, - &num_SC_PIII, &num_SC_AsIII); - if ( ret < 0 ) - { - return ret; /* error */ - } - bHasStereo += (ret == 2); - if ( (ret > 0) ) - { - /* ret == 0 => Empty InChI, 1=> No Stereo, 2=> Has Stereo */ - num_components ++; - *bHasReconnected |= ( iInChI == INCHI_REC ); - *bHasFixedH |= ( iMobileH == TAUT_NON ); - } - *bHasMetal |= bInChIHasReconnectedMetal( pInChI ); - } - } - } - - if ( (nModeFlagsValue & REQ_MODE_RELATIVE_STEREO) && (nModeFlagsValue & REQ_MODE_RACEMIC_STEREO) ) - { - return RI_ERR_SYNTAX; - } - if ( bHasStereo && !nModeFlagsValue ) /* REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU*/ - { - /* inversion does not change the stereo or no stereo at all */ - nModeFlagsValue = REQ_MODE_STEREO | REQ_MODE_ISO_STEREO; /* Abs */ - } - - if ( !num_known_SB && num_unk_und_SB ) - { - ; /* full SUU option or SB part of it */ - } else - { - nModeFlagsValue |= REQ_MODE_SB_IGN_ALL_UU; /* ignore Unknown/Undefind SB if no well-defined SB exist */ - } - - if ( !num_known_SC && num_unk_und_SC ) - { - ; /* full SUU option or SB part of it */ - } else - { - nModeFlagsValue |= REQ_MODE_SC_IGN_ALL_UU; /* ignore Unknown/Undefind SC if no well-defined SB exist */ - } - - /* Phosphine and Arsine Stereo */ - if ( num_SC_PIII ) - { - bTautFlagsValue |= TG_FLAG_PHOSPHINE_STEREO; - } - /* Phosphine and Arsine Stereo */ - if ( num_SC_AsIII ) - { - bTautFlagsValue |= TG_FLAG_ARSINE_STEREO; - } - - *nModeFlagsStereo = nModeFlagsValue; - *bTautFlagsStereo = bTautFlagsValue; - - return 0; -} - -/******************************************************************************************************/ -int bInChIHasReconnectedMetal( INChI *pInChI ) -{ - int i; - if ( pInChI && !pInChI->bDeleted && pInChI->nNumberOfAtoms && pInChI->nAtom ) - { - for ( i = 0; i < pInChI->nNumberOfAtoms; i ++ ) - { - if ( is_el_a_metal( (int)pInChI->nAtom[i] ) ) - { - if ( pInChI->nNumberOfAtoms > 1 || pInChI->nNum_H && pInChI->nNum_H[0] ) - { - return 1; - } - } - } - } - return 0; -} - -/*****************************************************************************************/ -int SetProtonsAndXchgIsoH( int bInChI2Structure, int bReqSplitOutputInChI, int bReqProtonsForEachComponent, - int bReqNonTaut, int bReqStereo, int num_components[INCHI_NUM], - MODE_PIXH nModeProtonIsoExchgH[INCHI_NUM], InpInChI *OneInput ) -{ - int j, k, k1, ret2=0, iINChI; - int bAvailableProtonsForEachComponent, bAvailableProtonsTotal; - - INCHI_HEAPCHK - - num_components[INCHI_BAS] = num_components[INCHI_REC] = 0; - - for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) { - nModeProtonIsoExchgH[iINChI] = MODE_PIXH_UNDEFINED; - /* are totals of /p and/or /i/h available ? */ - bAvailableProtonsTotal = 0 != OneInput->nNumProtons[iINChI][TAUT_YES].nNumRemovedProtons; - for ( k1 = 0; k1 < NUM_H_ISOTOPES; k1 ++ ) { - bAvailableProtonsTotal |= 0 !=OneInput->nNumProtons[iINChI][TAUT_YES].nNumRemovedIsotopicH[k1]; - } - /* are /p and/or /i/h available for each component ? */ - bAvailableProtonsForEachComponent = (NULL != OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons); - /* decision: add /p to each component, add total to the 1st, add total as one more component */ - /* In case of bInChI2Structure just keep totals if not available for each component */ - if ( bInChI2Structure ) { - nModeProtonIsoExchgH[iINChI] = bAvailableProtonsForEachComponent? - MODE_PIXH_ADD_TO_EACH: - MODE_PIXH_KEEP_TOTALS; - } else - if ( !bReqSplitOutputInChI ) { - nModeProtonIsoExchgH[iINChI] = bAvailableProtonsForEachComponent? - MODE_PIXH_ADD_TO_EACH : - MODE_PIXH_ADD_TO_FIRST; - } else - if ( !bAvailableProtonsForEachComponent ) { - nModeProtonIsoExchgH[iINChI] = bAvailableProtonsTotal? - MODE_PIXH_ADD_A_PIXH_COMPONENT : - MODE_PIXH_ADD_TO_FIRST; - } else - /* bAvailableProtonsForEachComponent && bReqSplitOutputInChI */ - if ( bReqProtonsForEachComponent ) { - nModeProtonIsoExchgH[iINChI] = MODE_PIXH_ADD_TO_EACH; - } else { - nModeProtonIsoExchgH[iINChI] = bReqNonTaut? - MODE_PIXH_ADD_TO_EACH : - MODE_PIXH_ADD_A_PIXH_COMPONENT; - } - /* remove unneeded data: protons for each component */ - if ( bAvailableProtonsForEachComponent && - nModeProtonIsoExchgH[iINChI] != MODE_PIXH_ADD_TO_EACH ) { - inchi_free( OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons ); - OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons = NULL; - bAvailableProtonsForEachComponent = 0; - } - /* remove unneeded data: total protons all components */ - if ( bAvailableProtonsTotal && nModeProtonIsoExchgH[iINChI] == MODE_PIXH_ADD_TO_EACH ) { - OneInput->nNumProtons[iINChI][TAUT_YES].nNumRemovedProtons = 0; - for ( k1 = 0; k1 < NUM_H_ISOTOPES; k1 ++ ) { - OneInput->nNumProtons[iINChI][TAUT_YES].nNumRemovedIsotopicH[k1] = 0; - } - bAvailableProtonsTotal = 0; - } - /* remove unneeded data: Fixed-H InChI; no protons data exist for Fixed-H */ - if ( !bReqNonTaut && OneInput->nNumComponents[iINChI][TAUT_NON] ) { - j = TAUT_NON; - for ( k = 0; k < OneInput->nNumComponents[iINChI][j]; k ++ ) { - Free_INChI_Members( &OneInput->pInpInChI[iINChI][j][k] ); - } - inchi_free( OneInput->pInpInChI[iINChI][j] ); - OneInput->pInpInChI[iINChI][j] = NULL; - OneInput->nNumComponents[iINChI][j] = 0; - } -#ifdef NEVER - /* remove unneeded data: Mobile-H InChI ????? */ - if ( bReqNonTaut && OneInput->nNumComponents[iINChI][TAUT_NON] ) { - j = TAUT_YES; - for ( k = 0; k < OneInput->nNumComponents[iINChI][j]; k ++ ) { - Free_INChI_Members( &OneInput->pInpInChI[iINChI][j][k] ); - } - inchi_free( OneInput->pInpInChI[iINChI][j] ); - OneInput->pInpInChI[iINChI][j] = NULL; - OneInput->nNumComponents[iINChI][j] = 0; - nModeProtonIsoExchgH[iINChI] = MODE_PIXH_UNDEFINED; - if ( OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons ) { - inchi_free( OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons); - OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons = NULL; - } - } -#endif - /* add one more component containing only /p and /i/h */ - if ( nModeProtonIsoExchgH[iINChI] == MODE_PIXH_ADD_A_PIXH_COMPONENT && - OneInput->nNumComponents[iINChI][TAUT_YES] || - /* always add one deleted component if no non-taut InChI is available */ - bInChI2Structure && !bAvailableProtonsForEachComponent && - !OneInput->nNumComponents[iINChI][TAUT_NON] && - OneInput->nNumComponents[iINChI][TAUT_YES] ) { - int nPrevLen, nLen=0; - j = TAUT_YES; - nPrevLen = OneInput->nNumComponents[iINChI][j]; - for ( k = 0; k < nPrevLen; k ++ ) { - nLen += !OneInput->pInpInChI[iINChI][j][k].bDeleted; - } - if ( nLen == nPrevLen ) { - /* add one more component */ - INChI *pInChI = (INChI *)inchi_calloc( nLen+1, sizeof(*pInChI) ); - if ( !pInChI ) { - ret2 = RI_ERR_ALLOC; - goto exit_error; - } - memcpy( pInChI, OneInput->pInpInChI[iINChI][j], nLen*sizeof(*pInChI) ); - inchi_free( OneInput->pInpInChI[iINChI][j] ); - OneInput->pInpInChI[iINChI][j] = pInChI; - } - OneInput->nNumComponents[iINChI][j] = nLen+1; - - for ( k = nLen; k < nPrevLen; k ++ ) { - Free_INChI_Members( &OneInput->pInpInChI[iINChI][j][k] ); - memset( &OneInput->pInpInChI[iINChI][j][k], 0, sizeof(OneInput->pInpInChI[iINChI][j][k])); - } - /* mark the last component as a proton */ - if ( 0 > (ret2 = nFillOutProtonMobileH( OneInput->pInpInChI[iINChI][j]+nLen ) ) ) { - goto exit_error; - } - } - INCHI_HEAPCHK - - /* remove unneeded Stereo and/or Fixed H */ - if ( !bReqStereo ) { - for ( j = 0; j < TAUT_NUM; j ++ ) { - for ( k = 0; k < OneInput->nNumComponents[iINChI][j]; k ++ ) { - if ( OneInput->pInpInChI[iINChI][j][k].Stereo ) { - Free_INChI_Stereo(OneInput->pInpInChI[iINChI][j][k].Stereo); - inchi_free( OneInput->pInpInChI[iINChI][j][k].Stereo ); - OneInput->pInpInChI[iINChI][j][k].Stereo = NULL; - } - if ( OneInput->pInpInChI[iINChI][j][k].StereoIsotopic ) { - Free_INChI_Stereo(OneInput->pInpInChI[iINChI][j][k].StereoIsotopic); - inchi_free( OneInput->pInpInChI[iINChI][j][k].StereoIsotopic ); - OneInput->pInpInChI[iINChI][j][k].StereoIsotopic = NULL; - } - INCHI_HEAPCHK - } - } - } - } - - num_components[INCHI_BAS] = inchi_max( OneInput->nNumComponents[INCHI_BAS][TAUT_YES], - OneInput->nNumComponents[INCHI_BAS][TAUT_NON] ); - num_components[INCHI_REC] = inchi_max( OneInput->nNumComponents[INCHI_REC][TAUT_YES], - OneInput->nNumComponents[INCHI_REC][TAUT_NON] ); - -exit_error: - return ret2; -} -/******************************************************************************************/ -int GetInChIFormulaNumH( INChI *pInChI, int *nNumH ) -{ /* get number of H including bridging hydrogen atoms */ - const char *p, *q; - *nNumH = 0; - if ( pInChI->szHillFormula ) { - for ( p = strchr( pInChI->szHillFormula, 'H'); p; p = strchr(p, 'H') ) { - p ++; - if ( !islower( UCINT *p ) ) { - /* found hydrogen in the formula */ - if ( isdigit( UCINT *p ) ) { - *nNumH += (int)inchi_strtol( p, &q, 10 ); - p = q; - } else { - *nNumH += 1; - } - } - } - } - return 0; -} -/******************************************************************************************/ -int GetInChINumH( INChI *pInChI, int *nNumH ) -{ - int i, j, nNumTautGroups, iTautGroup, nTautGroupLen, lenTautomer; - *nNumH = 0; - for ( i = 0; i < pInChI->nNumberOfAtoms; i ++ ) { - *nNumH += ( pInChI->nAtom[i] == EL_NUMBER_H ); /* bridging H */ - *nNumH += pInChI->nNum_H[i]; - } - /* earlier nNum_H_fixed[] should have been added to pInChI->nNum_H[] */ - /* - if ( pInChI->nNum_H_fixed ) { - for ( i = 0; i < pInChI->nNumberOfAtoms; i ++ ) { - *nNumH += pInChI->nNum_H_fixed[i]; - } - } - */ - if ( pInChI->lenTautomer > 3 && pInChI->nTautomer ) { - lenTautomer = pInChI->lenTautomer; - j = 0; - nNumTautGroups = pInChI->nTautomer[j ++]; - for ( iTautGroup = 0; j < lenTautomer && iTautGroup < nNumTautGroups; iTautGroup ++, j += nTautGroupLen ) { - nTautGroupLen = pInChI->nTautomer[j]+1; - *nNumH += pInChI->nTautomer[j+1]; - } - if ( iTautGroup != nNumTautGroups || j != lenTautomer ) { - return RI_ERR_PROGR; - } - } - if ( pInChI->nNum_H_fixed && (pInChI->lenTautomer || pInChI->nTautomer) ) { - return RI_ERR_PROGR; - } - return 0; -} -/******************************************************************************************/ -int GetInChIIsoH( INChI *pInChI, int nNumIsotopicH[NUM_H_ISOTOPES] ) -{ - int i; - for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { - nNumIsotopicH[i] = 0; - } - for ( i = 0; i < pInChI->nNumberOfIsotopicAtoms; i ++ ) { - if ( pInChI->IsotopicAtom[i].nIsoDifference > 0 && - pInChI->IsotopicAtom[i].nIsoDifference <= NUM_H_ISOTOPES ) { - if ( !pInChI->nAtom || - !pInChI->IsotopicAtom[i].nAtomNumber || - pInChI->IsotopicAtom[i].nAtomNumber > pInChI->nNumberOfAtoms ) { - return RI_ERR_PROGR; - } - if ( pInChI->nAtom[pInChI->IsotopicAtom[i].nAtomNumber-1] == EL_NUMBER_H ) { - /* isotopic H in connection table */ - nNumIsotopicH[ pInChI->IsotopicAtom[i].nIsoDifference-1 ] ++; - } - } - nNumIsotopicH[0] += pInChI->IsotopicAtom[i].nNum_H; - nNumIsotopicH[1] += pInChI->IsotopicAtom[i].nNum_D; - nNumIsotopicH[2] += pInChI->IsotopicAtom[i].nNum_T; - } - return 0; -} -/******************************************************************************************/ -typedef struct tagNumElem -{ - int num; - /* - int iso; - */ -} NUM_ELEM; - - -/***************************************************************************************/ -int InChILine2Data(INCHI_IOSTREAM *pInp, SEGM_LINE *pLine, - char **pStr, int *pState, int *nErr, - INChI *pInpInChI[INCHI_NUM][TAUT_NUM], - int nNumComponents[INCHI_NUM][TAUT_NUM], - REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM], - int s[INCHI_NUM][TAUT_NUM][2], - int bReadCoord, int bInchi2Struct, INCHI_MODE nMode, - int *bStdFormat, int *bInputHasSaveOpt, unsigned char *inp_save_opt_bits) -{ - int iINChI, i, j, k, m, len1, len2, ret2 = 0, retAux = 0, stateAux = 0; - int ret, tot_charge[INCHI_NUM][TAUT_NUM]; - int i1, i2, i3; - int kc; - NUM_ELEM *num_elem[INCHI_NUM][TAUT_NUM]; - - -#if ( FIX_I2I_STEREOCONVERSION_BUG == 1 ) -/* (2008-03-06) 1=> Fix bug of i2i conversion SAbs-->(SRel||Srac) */ -/* (converter does not placed proper stereo to output) */ - - /* set new stereo type as requested by conversion option */ - int target_stereo_type = 1; - if (nMode & REQ_MODE_RELATIVE_STEREO) - target_stereo_type = 2; - else if (nMode & REQ_MODE_RACEMIC_STEREO) - target_stereo_type = 3; -#endif - - - memset( num_elem, 0, sizeof(num_elem) ); - - ret = ReadInChILine(pInp, pLine, pStr, pState, pInpInChI, - nNumComponents, nNumProtons, s, - bStdFormat, bInputHasSaveOpt, inp_save_opt_bits); - - -#if ( FIX_I2I_STEREOCONVERSION_BUG == 1 ) - /* modify stereo type for layers as requested */ - if (target_stereo_type > 1) - for (i1=0;i1SAbs, SRac=>SAbs */ - s[i1][i2][i3] = target_stereo_type; - } -#endif - - - *nErr = 0; - if ( (ret == RI_ERR_EOL) && - nNumComponents[INCHI_BAS][TAUT_YES] - + nNumComponents[INCHI_BAS][TAUT_NON] && bReadCoord ) { - retAux = ReadInChICoord( pInp, pLine, &stateAux, pInpInChI, nNumComponents ); - } - if ( (ret == RI_ERR_EOL || ret == RI_ERR_EOF) && - nNumComponents[INCHI_BAS][TAUT_YES] - + nNumComponents[INCHI_BAS][TAUT_NON] ) { - /* post-processing: add omitted layers */ - *pState = IST_MATERIAL_BALANCE_ERROR; - for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) - { - - for ( j = 0; j < TAUT_NUM; j ++ ) - { - /* for Mobile/Fixed H (j) ... */ - - int bIsotopic, bStereoType, bStereoTypeAlt; - int nMH2FH_AltInv=0, nFH2iFH_AltInv=0 /*, niMH2iFH_AltInv=0, nMH2iMH_AltInv=0*/; - int jAlt = ALT_TAUT(j); - INCHI_MODE nFlags = 0, nFlagsAlt = 0; - /* get stereo type: ABS, REL, RAC, or nothing */ - tot_charge[iINChI][j] = 0; - for ( bIsotopic = bStereoType = bStereoTypeAlt = 0; bIsotopic < 2; bIsotopic ++ ) { - if ( !bStereoType || bStereoType < s[iINChI][j][bIsotopic] ) { - bStereoType = s[iINChI][j][bIsotopic]; - } - if ( !bStereoTypeAlt || bStereoTypeAlt < s[iINChI][jAlt][bIsotopic] ) { - bStereoTypeAlt = s[iINChI][jAlt][bIsotopic]; - } - nFlags = bStereoType ==2? INCHI_FLAG_REL_STEREO : bStereoType ==3? INCHI_FLAG_RAC_STEREO : 0; - nFlagsAlt = bStereoTypeAlt==2? INCHI_FLAG_REL_STEREO : bStereoTypeAlt==3? INCHI_FLAG_RAC_STEREO : 0; - } - /* set stereo type to each component */ - /* add missing nNum_H and nConnTable */ - if ( nNumComponents[iINChI][j] ) { - num_elem[iINChI][j] = (NUM_ELEM *)inchi_calloc( nElDataLen+1, sizeof(num_elem[0][0][0]) ); - if ( !num_elem[iINChI][j] ) { - ret2 = RI_ERR_ALLOC; - goto exit_function; - } - } - for ( k = 0; k < nNumComponents[iINChI][j]; k ++ ) - { - /* for each component k ... */ - - if ( pInpInChI[iINChI][j] ) { - INChI *pInChI = &pInpInChI[iINChI][j][k]; - INChI *pInChI_Alt = (knNumberOfAtoms)? pInpInChI[iINChI][jAlt]:NULL;*/ /* 2007-09-25 DT */ - pInpInChI[iINChI][jAlt][k].nNumberOfAtoms)? &pInpInChI[iINChI][jAlt][k]:NULL; - - if ( nFlags ) { - pInChI->nFlags |= nFlags; - } else - if ( j == TAUT_NON && !nFlags && nFlagsAlt ) { - pInChI->nFlags |= nFlagsAlt; - } - /**** add empty immobile H (nNum_H) if it is missing ****/ - if ( !pInChI->nNum_H && - !(pInChI->nNum_H = (S_CHAR *)inchi_calloc( pInChI->nNumberOfAtoms+1, sizeof(pInChI->nNum_H[0]) ) ) ) { - ret2 = RI_ERR_ALLOC; - goto exit_function; - } - /**** add single atom nConnTable if it is missing ****/ - if ( !pInChI->nConnTable ) { - AT_NUMB *pCT; - int lenCT; - if ( j == TAUT_NON && k < nNumComponents[iINChI][TAUT_YES] && - (pCT = pInpInChI[iINChI][TAUT_YES][k].nConnTable) && - (lenCT = pInpInChI[iINChI][TAUT_YES][k].lenConnTable) > 0) { - if ( !(pInChI->nConnTable = (AT_NUMB *)inchi_calloc( lenCT+1, sizeof(pInChI->nConnTable[0]) ) ) ) { - ret2 = RI_ERR_ALLOC; - goto exit_function; - } - memcpy( pInChI->nConnTable, pCT, lenCT*sizeof(pInChI->nConnTable[0]) ); - pInChI->lenConnTable = lenCT; - } else { - if ( j == TAUT_YES && pInChI->nNumberOfAtoms > 1 ) { - *pState = IST_MOBILE_H_CONNECTIONS + (iINChI==INCHI_REC? IST_HAPPENED_IN_RECMET : 0); - ret2 = RI_ERR_SYNTAX; - goto exit_function; - } - if ( !(pInChI->nConnTable = (AT_NUMB *)inchi_calloc( pInChI->nNumberOfAtoms+1, sizeof(pInChI->nConnTable[0]) ) ) ) { - ret2 = RI_ERR_ALLOC; - goto exit_function; - } - pInChI->lenConnTable = 1; - pInChI->nConnTable[0] = 1; - } - } else - if ( pInChI->nConnTable && !pInChI->lenConnTable && pInChI->nNumberOfAtoms == 1 ) { - pInChI->nConnTable[0] = 1; - pInChI->lenConnTable = 1; - } - /**** copy charge: Mobile H --> Fixed H; ****/ - if ( j == TAUT_NON ) { - /* - if ( pInChI->nTotalCharge == NO_VALUE_INT ) { - pInChI->nTotalCharge = 0; - } else - */ - if ( !pInChI->nTotalCharge && k < nNumComponents[iINChI][TAUT_YES] ) { - INChI *pAltInChI = &pInpInChI[iINChI][TAUT_YES][k]; /* Mobile H InChI */ - if ( pAltInChI->nTotalCharge && pAltInChI->nTotalCharge != NO_VALUE_INT ) { - pInChI->nTotalCharge = pAltInChI->nTotalCharge; - } - } - } - /***** Fixed H: add pInChI->nNum_H_fixed to pInChI->nNum_H ****/ - if ( j == TAUT_NON && pInChI->nNum_H && pInChI->nNum_H_fixed ) { - for ( m = 0; m < pInChI->nNumberOfAtoms; m ++ ) { - pInChI->nNum_H[m] += pInChI->nNum_H_fixed[m]; - } - } - /***** copy isotopic atoms: Mobile H --> Fixed H ******/ - if ( j == TAUT_YES && pInChI->nNumberOfIsotopicAtoms && - k < nNumComponents[iINChI][TAUT_NON] ) { - INChI *pAltInChI = &pInpInChI[iINChI][TAUT_NON][k]; /* Fixed H InChI */ - - if ( !pAltInChI->nNumberOfIsotopicAtoms ) { - ret2=CopySegment( pAltInChI, pInChI, CPY_ISO_AT, 0, 0); - if ( ret2 < 0 ) { - goto exit_function; - } - } - } - /**** copy coordinates: Mobile H --> Fixed H ******/ - if ( j == TAUT_YES && pInChI->IsotopicTGroup && - k < nNumComponents[iINChI][TAUT_NON] ) { - INChI *pAltInChI = &pInpInChI[iINChI][TAUT_NON][k]; /* Fixed H InChI */ - - if ( !pAltInChI->IsotopicTGroup ) { - XYZ_COORD *pxyz = (XYZ_COORD *)inchi_calloc( pInChI->nNumberOfAtoms, sizeof(pxyz[0])); - if ( pxyz ) { - memcpy( pxyz, pInChI->IsotopicTGroup, pInChI->nNumberOfAtoms * sizeof(pxyz[0]) ); - pAltInChI->IsotopicTGroup = (INChI_IsotopicTGroup *)pxyz; - } else { - ret2 = RI_ERR_ALLOC; - goto exit_function; - } - } - } - - /******************************************************** - * * - * Restore omitted stereo seqments * - * * - * order of restoring: * - * * - * 1. Fixed H (F) -> (FI) Isotopic Fixed H * - * 2. Mobile H (M) -> (F) Fixed H * - * 3. Isotopic Mobile H (MI) -> (FI) Isotopic Fixed H * - * 4. Mobile H (M) -> (MI) Isotopic Mobile H * - * * - ********************************************************/ - - /***** (4) copy stereo: Mobile H --> isotopic Mobile H ******/ - if ( j == TAUT_YES ) { - int bIso = pInChI->nNumberOfIsotopicAtoms || - (pInChI->StereoIsotopic && - pInChI->StereoIsotopic->nNumberOfStereoCenters - + pInChI->StereoIsotopic->nNumberOfStereoBonds) || - pInChI_Alt && pInChI_Alt->nNumberOfIsotopicAtoms; - - /* non-isotopic Mobile H => isotopic Mobile H */ - if ( bIso ) { - if ( pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters && - (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->t_parity) ) { - if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3, 1, 0)) || - (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) && - 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3_M, 1, 0))) { - goto exit_function; - } - if ( (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { - if ( pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT ) { - pInChI->Stereo->nCompInv2Abs = s[iINChI][j][0]>0? 2 : 0; - } - if ( pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { - pInChI->StereoIsotopic->nCompInv2Abs = s[iINChI][j][1]>0? 2 : 0; - } - } - } else - /* copy sp3 inversion info: non-isotopic Mobile H => isotopic Mobile H */ - if ( pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters && - pInChI->StereoIsotopic && pInChI->StereoIsotopic->nNumberOfStereoCenters && - pInChI->Stereo->nCompInv2Abs ) { - if ( (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) && - pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT && - pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { - pInChI->Stereo->nCompInv2Abs = s[iINChI][j][0]>0? 2 : 0; - pInChI->StereoIsotopic->nCompInv2Abs = s[iINChI][j][1]>0? 2 : 0; - } else - if (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs ) { - pInChI->StereoIsotopic->nCompInv2Abs = pInChI->Stereo->nCompInv2Abs; - } - } - } - if ( bIso && - pInChI->Stereo && pInChI->Stereo->nNumberOfStereoBonds && - (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->b_parity) ) { - if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP2, 1, 0)) ) { - goto exit_function; - } - } - } - /***** (0) set nCompInv2Abs to Fixed-H *********************************/ - if ( j == TAUT_NON ) { - if ( pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters && - pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT ) { - /* case of /sN and /t... in non-isotopic Mobile-H, no /s in non-isotopic Fixed-H */ - if ( !s[iINChI][j][0] && s[iINChI][jAlt][0]>0 && /* /sN is not present in F and is present in M */ - pInChI_Alt && pInChI_Alt->Stereo && pInChI_Alt->Stereo->nNumberOfStereoCenters ) { - /* inherit from Mobile-H */ - /* /s1 in M and MI; /m1 or /m0 in MI; /m. in M; no /m in F. Inherit MI->FI. Added 10-15-2007 */ - if ( pInChI_Alt->Stereo->nCompInv2Abs == 0 && /* M: /m. ; means no /m for this component */ - pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT && /* F: no /m segment for all components */ - pInChI_Alt->StereoIsotopic && /* MI: present */ - pInChI_Alt->StereoIsotopic->nCompInv2Abs != 0 && - pInChI_Alt->StereoIsotopic->nCompInv2Abs != NO_VALUE_INT && /* MI: /m0 or /m1 */ - !s[iINChI][j][0] && !s[iINChI][j][1] && /* F, FI: no /s */ - s[iINChI][jAlt][0] == 1 && s[iINChI][jAlt][1] == 1 /* M, MI: /s1 and /s1 */ - ) { - /* copy /m from MI to FI */ - if ( 0 > (ret2 = CopySegment( pInChI, pInChI_Alt, CPY_SP3_M, 1, 1)) ) { - goto exit_function; - } - } else - /* the following if(){...} was added to fix m1 bug 2007-09-25 DT */ - if ( pInChI_Alt->Stereo->nCompInv2Abs != NO_VALUE_INT && s[iINChI][jAlt][0] == 1 ) { - pInChI->Stereo->nCompInv2Abs = pInChI_Alt->Stereo->nCompInv2Abs; - } else - /* M and MI contain /sN and /sN, N=2,3. Added 10-15-2007 */ - if ( pInChI_Alt->Stereo->nCompInv2Abs == NO_VALUE_INT && - pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT && - !s[iINChI][j][0] && !s[iINChI][j][1] && - (s[iINChI][jAlt][0] & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) && - (s[iINChI][jAlt][1] & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { - int bIso = pInChI->nNumberOfIsotopicAtoms || - (pInChI->StereoIsotopic && - pInChI->StereoIsotopic->nNumberOfStereoCenters - + pInChI->StereoIsotopic->nNumberOfStereoBonds) || - pInChI_Alt && pInChI_Alt->nNumberOfIsotopicAtoms; - if ( bIso ){ - if ( !pInChI_Alt->StereoIsotopic && /* create zero/NULL-initialized pInChI_Alt->StereoIsotopic */ - 0 > (ret2 = CopySegment( pInChI_Alt, pInChI_Alt, CPY_SP3_M, 1, -1))) { - goto exit_function; - } - pInChI_Alt->StereoIsotopic->nCompInv2Abs = 2; /* MI: /m1 or /m0 */ - pInChI_Alt->Stereo->nCompInv2Abs = 0; /* M: /m. ; no /m for this component */ - pInChI->Stereo->nCompInv2Abs = NO_VALUE_INT+1; /* FI: Stereo->CompInv2Abs=0, StereoIsotopic->CompInv2Abs=1 or -1 */ - } else { - pInChI->Stereo->nCompInv2Abs = 2; /* F: /m1 or /m0, omitted from InChI as a repetition */ - pInChI_Alt->Stereo->nCompInv2Abs = 2; /* M: /m1 or /m0; in Srel/SRac case the value = 2 */ - } - } else { - pInChI->Stereo->nCompInv2Abs = 2; /* F: /m1 or /m0, omitted from InChI as a repetition */ - pInChI_Alt->Stereo->nCompInv2Abs = 2; /* M: /m1 or /m0; in Srel/SRac case the value = 2 */ - } - } else - /* case of /sN in Isotopic Fixed-H only, /t... in Fixed-H, no /m (2007-08-27 DT) */ - if ( !s[iINChI][j][0] && !s[iINChI][jAlt][0] && /* /sN in Fixed-H isotopic only */ - (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) && - !(pInChI->StereoIsotopic && pInChI->StereoIsotopic->nNumberOfStereoCenters) && - /*!(pInChI_Alt && pInChI_Alt->Stereo && pInChI_Alt->Stereo->nNumberOfStereoCenters) &&*/ - !(pInChI_Alt && pInChI_Alt->StereoIsotopic && pInChI_Alt->StereoIsotopic->nNumberOfStereoCenters) ) { - pInChI->Stereo->nCompInv2Abs = NO_VALUE_INT+1; /* Stereo->CompInv2Abs=0, StereoIsotopic->CompInv2Abs=1 or -1 */ - } else { - pInChI->Stereo->nCompInv2Abs = s[iINChI][j][0]>0? 2 : 0; - } - } - } - /***** (1) copy stereo: non-isotopic Fixed H --> isotopic Fixed H ******/ - if ( j == TAUT_NON ) { - int bIso = pInChI->nNumberOfIsotopicAtoms || - (pInChI->StereoIsotopic && - pInChI->StereoIsotopic->nNumberOfStereoCenters - + pInChI->StereoIsotopic->nNumberOfStereoBonds) || - pInChI_Alt && pInChI_Alt->nNumberOfIsotopicAtoms; - /* non-isotopic Fixed H => isotopic Fixed H */ - if ( bIso ) { - if ( pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters && - (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->t_parity) ) { - /* -- replaced 2007-08-27 by (aaa), see below -- DT - if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3, 1, 0)) || - !(pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) && - 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3_M, 1, 0))) { - goto exit_function; - } - */ - /*----------- replacement (aaa) begin 2007-08-27 DT */ - if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3, 1, 0)) ) { - goto exit_function; - } - if ( pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT+1 ) { - pInChI->Stereo->nCompInv2Abs = 0; - pInChI->StereoIsotopic->nCompInv2Abs = 2; - } else - if ( !(pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) && - 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3_M, 1, 0))) { - goto exit_function; - } - /*----------- replacement (aaa) end 2007-08-27 DT */ - if ( (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { - if ( pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT ) { - pInChI->Stereo->nCompInv2Abs = s[iINChI][j][0]>0? 2 : 0; - } - if ( pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { - pInChI->StereoIsotopic->nCompInv2Abs = s[iINChI][j][1]>0? 2 : 0; - } - } -#ifdef NEVER - if ( (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) && - !s[iINChI][j][0] && s[iINChI][j][0]>0 ) { - /* copied Rel/Rac stereo to Iso; /s is in Iso /s is not in non-Iso */ - /* this means all difference in stereo is in inversion */ - if ( pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT && - pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { - pInChI->Stereo->nCompInv2Abs = 0; /* missing */ - pInChI->StereoIsotopic->nCompInv2Abs = 2; /* unusual value */ - } - } -#endif - } else - /* copy sp3 inversion info: non-isotopic Fixed H --> isotopic Fixed H */ - if ( pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters && - pInChI->StereoIsotopic && pInChI->StereoIsotopic->nNumberOfStereoCenters && - pInChI->Stereo->nCompInv2Abs ) { - if ( (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) && - pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT && - pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { - pInChI->Stereo->nCompInv2Abs = s[iINChI][j][0]>0? 2 : 0; - pInChI->StereoIsotopic->nCompInv2Abs = s[iINChI][j][1]>0? 2 : 0; - } else - if (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) { - pInChI->StereoIsotopic->nCompInv2Abs = pInChI->Stereo->nCompInv2Abs; - } - } - } - if ( bIso && - pInChI->Stereo && pInChI->Stereo->nNumberOfStereoBonds && - (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->b_parity) ) { - if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP2, 1, 0)) ) { - goto exit_function; - } - } - } - - /***** copy stereo: Mobile H --> Fixed H ******/ - if ( j == TAUT_NON && k < nNumComponents[iINChI][TAUT_YES] ) { - INChI *pAltInChI = &pInpInChI[iINChI][TAUT_YES][k]; /* Mobile H InChI */ - int bIso = pInChI->nNumberOfIsotopicAtoms || - (pInChI->StereoIsotopic && - pInChI->StereoIsotopic->nNumberOfStereoCenters - + pInChI->StereoIsotopic->nNumberOfStereoBonds) || - pAltInChI && ( - pAltInChI->nNumberOfIsotopicAtoms || - (pAltInChI->StereoIsotopic && - pAltInChI->StereoIsotopic->nNumberOfStereoCenters - + pAltInChI->StereoIsotopic->nNumberOfStereoBonds) ); - int bNo_InChI_t = (!pInChI->Stereo || !pInChI->Stereo->t_parity); - int bNo_InChI_m = (!pInChI->Stereo || NO_VALUE_INT == pInChI->Stereo->nCompInv2Abs); - - /* (2) non-isotopic Mobile H => non-isotopic Fixed H */ - if ( pAltInChI->Stereo && pAltInChI->Stereo->nNumberOfStereoCenters && - (!pInChI->Stereo || !pInChI->Stereo->t_parity) ) - { -#if ( FIX_I2I_STEREOCONVERSION_BUG2 == 1 ) - /* (2008-04-02) 1=> Fix bug of i2i conversion SAbs-->(SRel||Srac) */ - /* (converter skipped empty '/t' or sometimes produced an excess one */ - - /* check whether t stereo is actually present */ - int bHave_t_stereo = 1; - if (pInChI->Stereo) - bHave_t_stereo = pInChI ->Stereo->nNumberOfStereoCenters; - /* account for stereobonds present */ - if ( bHave_t_stereo < 1 ) - if ( pInChI->Stereo->nNumberOfStereoBonds > 0 ) - bHave_t_stereo=1; - /* copy stereo anyway ... */ -#endif - if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3, 0, 0)) || - (!pInChI->Stereo->nCompInv2Abs || NO_VALUE_INT == pInChI->Stereo->nCompInv2Abs) && - 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3_M, 0, 0)) ) - { - goto exit_function; - } - -#if ( FIX_I2I_STEREOCONVERSION_BUG2 == 1 ) - /* ... correct just copied stereo if applicable */ - if ( (s[iINChI][j][0] < 1) && - (bHave_t_stereo <1 ) && - (pAltInChI->Stereo->nNumberOfStereoCenters > 0) && - (s[iINChI][jAlt][0] < 1) ) - { - /* (2010-02-28) if not all stereo centers are unknown/undefined */ - /* at which condition stereo still should present .. */ - int all_UU = 1; - for (kc=0; kcStereo->nNumberOfStereoCenters; kc++) - { - if ( (pAltInChI->Stereo->t_parity[kc] != AB_PARITY_UNKN) && - (pAltInChI->Stereo->t_parity[kc] != AB_PARITY_UNDF) ) - { - all_UU=0; - break; - } - } - if (!all_UU) - pInChI->Stereo->nNumberOfStereoCenters = 0; - } -#endif - - /* in case of missing nCompInv2Abs, 2005-05-10 */ - if ( (pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT) && - (nFlagsAlt & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { - if ( s[iINChI][jAlt][0] > 0 && s[iINChI][j][0] > 0 ) { - /* suppose once in a while only non-taut stereo changes if inverted */ - pAltInChI->Stereo->nCompInv2Abs = (++nMH2FH_AltInv)%2? 2:0; - pInChI->Stereo->nCompInv2Abs = 2; - } else - /* Mobile-H: /t.. /sN; Mobile-H isotopic: /sN (n=2 or 3), not /t...; Fixed-H layer is present, has no /t, no /i/t */ - /* Mobile-H /sN was caused by another component that would have same /mN in all layers */ - /* therefore, in case of Abs. Stereo, Mobile-H stereo isotopic stereo would have /m1 */ - /* In case of Rel/Rac stereo, since no /m1 could occur in Mobile-H isotopic, */ - /* no pAltInChI->StereoIsotopic or pInChI->StereoIsotopic have been created yet. */ - /* added 10-11-2007 to fix i2i bug for Rel/Rac stereo */ - if ( nNumComponents[iINChI][j] > 1 && - bNo_InChI_t && bNo_InChI_m /* no /t... or /mN in Fixed-H */ && !nFlags && - !(pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->t_parity) && - !(pInChI->StereoIsotopic && pInChI->StereoIsotopic->t_parity) && - s[iINChI][j][0]==0 && s[iINChI][j][1] == 0 && - /* /sN, N=2 or 3 only in Mobile-H AND Mobile-H isotopic */ - (s[iINChI][jAlt][0] & ((INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO))) && - (s[iINChI][jAlt][1] & ((INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO))) ) - { - if ( bIso ) { - /* create two zero/NULL-initialized isotopic stereo if they do not exist */ - if ( !pInChI->StereoIsotopic && 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3_M, 1, -1)) - /* -- the following will be created later, in TAUT_YES part of the code -- */ - || !pAltInChI->StereoIsotopic && 0 > (ret2 = CopySegment( pAltInChI, pAltInChI, CPY_SP3_M, 1, -1)) ) { - goto exit_function; - } - /* same value = 2 for MI and FI; here we assign only FI */ - pInChI->StereoIsotopic->nCompInv2Abs = 2; - pInChI->Stereo->nCompInv2Abs = 0; - /* -- the following will NOT be assigned later, in TAUT_YES part of the code -- */ - pAltInChI->StereoIsotopic->nCompInv2Abs = 2; - pAltInChI->Stereo->nCompInv2Abs = 0; - /* */ - } else { - if ( NO_VALUE_INT == pInChI->Stereo->nCompInv2Abs && - NO_VALUE_INT == pAltInChI->Stereo->nCompInv2Abs ) { - pInChI->Stereo->nCompInv2Abs = 2; - pAltInChI->Stereo->nCompInv2Abs = 2; - } - } - } else - if ( (s[iINChI][jAlt][0] > 0 || s[iINChI][j][0] > 0) && s[iINChI][j][0] >= 0 ) - pInChI->Stereo->nCompInv2Abs = 2; - else - /* Mobile-H: /t..., no /sN; Mobile-H isotopic: /s2 or /s3, not /t; Fixed-H layer is present, has no /t, no /i/t */ - /* therefore, in case of Abs. Stereo, Mobile-H stereo isotopic stereo would have /m1 */ - /* In case of Rel/Rac stereo, since no /m1 could occur in Mobile-H isotopic, */ - /* no pAltInChI->StereoIsotopic or pInChI->StereoIsotopic have been created yet. */ - /* added 10-10-2007 to fix i2i bug for Rel/Rac stereo */ - if ( bIso && bNo_InChI_t && bNo_InChI_m /* no /t... or /mN in Fixed-H */ && !nFlags && - !(pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->t_parity) && - !(pInChI->StereoIsotopic && pInChI->StereoIsotopic->t_parity) && - s[iINChI][jAlt][0]==0 && s[iINChI][j][0]==0 && s[iINChI][j][1] == 0 && - /* /sN, N=2 or 3 only in Mobile-H isotopic */ - (s[iINChI][jAlt][1] & ((INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO))) ) - { - /* create two zero/NULL-initialized isotopic stereo if they do not exist */ - if ( !pInChI->StereoIsotopic && 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3_M, 1, -1)) - /* -- the following will be created later, in TAUT_YES part of the code -- */ - /*|| !pAltInChI->StereoIsotopic && 0 > (ret2 = CopySegment( pAltInChI, pAltInChI, CPY_SP3_M, 1, -1))*/ ) { - goto exit_function; - } - /* same value = 2 for MI and FI; here we assign only FI */ - pInChI->StereoIsotopic->nCompInv2Abs = 2; - pInChI->Stereo->nCompInv2Abs = 0; - /* -- the following will be assigned later, in TAUT_YES part of the code -- */ - /* - pAltInChI->StereoIsotopic->nCompInv2Abs = 2; - pAltInChI->Stereo->nCompInv2Abs = 0; - */ - } else - pInChI->Stereo->nCompInv2Abs = 0; - if ( !(pInChI->nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { - pInChI->nFlags |= ((nFlagsAlt|nFlags) & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)); - } - } - - } else - /* copy sp3 inversion info: non-isotopic Mobile H => non-isotopic Fixed H */ - if ( pAltInChI->Stereo && pAltInChI->Stereo->nNumberOfStereoCenters && - pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters && - pAltInChI->Stereo->nCompInv2Abs && - (!pInChI->Stereo->nCompInv2Abs || NO_VALUE_INT == pInChI->Stereo->nCompInv2Abs) ) { - if ( !(nFlagsAlt && !nFlags ) || NO_VALUE_INT == pInChI->Stereo->nCompInv2Abs ) { - /* ??? */ - pInChI->Stereo->nCompInv2Abs = pAltInChI->Stereo->nCompInv2Abs; - } - } - - /* use same rule to copy stereobonds */ - if ( pAltInChI->Stereo && pAltInChI->Stereo->nNumberOfStereoBonds && - (!pInChI->Stereo || !pInChI->Stereo->b_parity) ) { - if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP2, 0, 0)) ) { - goto exit_function; - } - } - /* (3) isotopic Mobile H -> isotopic Fixed H */ - /* if !FH_Stereo && !MH_Stereo && MH_IsoStereo!=NULL && FH_IsoStereo==NULL */ - if ( bIso ) { - if ( !(pInChI->Stereo && pInChI->Stereo->t_parity) && /* !FH_Stereo */ - !(pAltInChI->Stereo && pAltInChI->Stereo->t_parity) && /* !MH_Stereo */ - (pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->nNumberOfStereoCenters) && /* MH_IsoStereo */ - (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->t_parity) ) { /* !FH_IsoStereo */ - /* copy sp3 iso stereo MI->FI (/t) and, if FH nCompInv2Abs (/m) is missing, copy it, too, MI->FI */ - if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3, 1, 1)) || - (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) && - 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3_M, 1, 1)) ) { - goto exit_function; - } - /* in case of missing nCompInv2Abs, Relative or Racemic stereo 2005-05-10 */ - if ( pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT && - (nFlagsAlt & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { - pInChI->StereoIsotopic->nCompInv2Abs = s[iINChI][jAlt][1]>0? 2 : 0; - if ( !(pInChI->nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { - pInChI->nFlags |= (nFlagsAlt & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)); - } - } - } else - /* copy sp3 inversion info only: isotopic Mobile H -> isotopic Fixed H */ - if ( !(pInChI->Stereo && pInChI->Stereo->t_parity) && /* !FH_Stereo /t */ - !(pAltInChI->Stereo && pAltInChI->Stereo->t_parity) && /* !MH_Stereo /t */ - (pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->nNumberOfStereoCenters) && /* MH_IsoStereo /t */ - (pInChI->StereoIsotopic && pInChI->StereoIsotopic->nNumberOfStereoCenters) && /* FH_IsoStereo /t */ - pAltInChI->StereoIsotopic->nCompInv2Abs && /* MH_IsoStereo /m */ - (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) ) { /* !FH_IsoStereo /m */ - /* added 02-09-2006 */ - if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3_M, 1, 1)) ) { - goto exit_function; - } - } - /* use same rule to copy stereobonds */ - if ( !(pInChI->Stereo && pInChI->Stereo->b_parity) && - !(pAltInChI->Stereo && pAltInChI->Stereo->b_parity) && - (pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->nNumberOfStereoBonds) && - (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->b_parity) ) { - if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP2, 1, 1)) ) { - goto exit_function; - } - } - - /* (4) Copy Fixed-H -> isotopic Fixed-H */ - /* if FH_Stereo && !MH_IsoStereo && && !FH_IsoStereo */ - if ( (pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters) && /* FH_Stereo /t */ - !(pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->t_parity) && /* !MH_IsoStereo /t */ - !(pInChI->StereoIsotopic && pInChI->StereoIsotopic->t_parity) ) { /* !FH_IsoStereo /t */ - - /* added 10-10-2007 DT: copy MH_Iso /m => FH_Iso /m to fix i2i bug for Abs stereo */ - /* InChI string contains: MH(/t...), MH_Iso(/mN, no /t), FH(no /t /m), FH_Iso(no /t /m) */ - if ( pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->nCompInv2Abs && /* MH_IsoStereo /m */ - bNo_InChI_t && - NO_VALUE_INT != pAltInChI->StereoIsotopic->nCompInv2Abs && /* undef FH_IsoStereo /m */ - !(pInChI->StereoIsotopic && NO_VALUE_INT != pInChI->StereoIsotopic->nCompInv2Abs)) { - if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3_M, 1, 1))) { - goto exit_function; - } - } - - /* added 05-09-2006: copy sp3 FH=>FH_Iso */ - if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3, 1, 0)) || - (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) && - 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3_M, 1, 0)) ) { - goto exit_function; - } - /* in case of missing nCompInv2Abs, Relative or Racemic stereo, /sN in Fixed-H, 2005-05-10 */ - if ( pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT && - (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { - if ( s[iINChI][j][0] > 0 && s[iINChI][j][1] > 0 ) { - /* suppose once in a while only non-taut stereo changes if inverted */ - pInChI->StereoIsotopic->nCompInv2Abs = 2; - pInChI->Stereo->nCompInv2Abs = (++nFH2iFH_AltInv)%2? 2:0; - } else - if ( (s[iINChI][j][0] > 0 || s[iINChI][j][1] > 0) && s[iINChI][j][1] >= 0 ) /* ??? != NO_VALUE_INT ??? */ - pInChI->StereoIsotopic->nCompInv2Abs = 2; - else - pInChI->StereoIsotopic->nCompInv2Abs = 0; - if ( !(pInChI->nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { - pInChI->nFlags |= (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)); - } - } - - } else - /* copy sp3 inversion info only: Fixed-H -> isotopic Fixed H */ - if ( (pInChI->Stereo && pInChI->Stereo->t_parity) && - !(pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->t_parity) && - (pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->nNumberOfStereoCenters) && - (pInChI->StereoIsotopic && pInChI->StereoIsotopic->nNumberOfStereoCenters) && - pInChI->Stereo->nCompInv2Abs && - (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) ) { - /* added 05-09-2006 */ - if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3_M, 1, 0)) ) { - goto exit_function; - } - } - } - if ( bIso && - !(pInChI->Stereo && pInChI->Stereo->nNumberOfStereoBonds) && - !(pAltInChI->Stereo && pAltInChI->Stereo->nNumberOfStereoBonds) && - (pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->nNumberOfStereoBonds) && - (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->b_parity) ) { - if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP2, 1, 1)) ) { - goto exit_function; - } - } - } - } - } /* end of component cycle (k) */ - } /* end of Mobile/Fixed H cycle (j) */ - - /**** replace NO_VALUE_INT with zeroes in all Mobile & Fixed H components ****/ - for ( j = 0; j < TAUT_NUM; j ++ ) { - for ( k = 0; k < nNumComponents[iINChI][j]; k ++ ) { - if ( pInpInChI[iINChI][j] ) { - INChI *pInChI = &pInpInChI[iINChI][j][k]; - if ( pInChI->nTotalCharge == NO_VALUE_INT ) { - pInChI->nTotalCharge = 0; - } - if ( pInChI->Stereo && pInChI->StereoIsotopic && - pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { - if ( pInChI->Stereo->nNumberOfStereoCenters && - pInChI->Stereo->nCompInv2Abs != NO_VALUE_INT ) { - pInChI->StereoIsotopic->nCompInv2Abs = pInChI->Stereo->nCompInv2Abs; - } - } - /* Add special nCompInv2Abs=2 to force /s2 or /s3 in InChI output */ - if ( pInChI->Stereo && pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT ) { - if ( pInChI->nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO) && - pInChI->Stereo->nNumberOfStereoCenters ) { - pInChI->Stereo->nCompInv2Abs = (s[iINChI][j][0]>0 /*|| s[iINChI][j][1]>0*/)? 2 : 0; /* we do not know the real value */ - } else { - pInChI->Stereo->nCompInv2Abs = 0; - } - } - if ( pInChI->StereoIsotopic && pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { - if ( pInChI->nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO) && - pInChI->StereoIsotopic->nNumberOfStereoCenters ) { - pInChI->StereoIsotopic->nCompInv2Abs = s[iINChI][j][1]>0? 2 : 0; /* we do not know the real value */ - } else { - pInChI->StereoIsotopic->nCompInv2Abs = 0; - } - } - /* added 02-07-2006 */ - if ( pInChI->Stereo && pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT || - pInChI->StereoIsotopic && pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { - ret2 = RI_ERR_PROGR; - goto exit_function; - } - if ( !pInChI->bDeleted && pInChI->nNumberOfAtoms ) { - tot_charge[iINChI][j] += pInChI->nTotalCharge; - for ( m = 0; m < pInChI->nNumberOfAtoms; m ++ ) { - if ( pInChI->nAtom[m] < EL_NUMBER_H || pInChI->nAtom[m] > nElDataLen ) { - ret2 = RI_ERR_PROGR; - goto exit_function; - } - /* all atoms except H */ - if ( pInChI->nAtom[m] > EL_NUMBER_H ) { - num_elem[iINChI][j][pInChI->nAtom[m]].num ++; - } - } - if ( 0 > (ret2 = GetInChINumH( pInChI, &m ) ) ) { - goto exit_function; - } - num_elem[iINChI][j][EL_NUMBER_H].num += m; - } - } - } - } - - - for ( j = 0; j < TAUT_NUM; j ++ ) { - for ( k = 0; k < nNumComponents[iINChI][j]; k ++ ) - { - if ( pInpInChI[iINChI][j] ) - { - INChI *pInChI = &pInpInChI[iINChI][j][k]; - if ( pInChI->Stereo && !pInChI->Stereo->nNumberOfStereoCenters ) - { - pInChI->Stereo->nCompInv2Abs = 0; - } - if ( pInChI->StereoIsotopic && !pInChI->StereoIsotopic->nNumberOfStereoCenters ) - { - pInChI->StereoIsotopic->nCompInv2Abs = 0; - } - } - } - } - - -#if ( FIX_I2I_STEREOCONVERSION_BUG3 == 1 ) -/* (2008-04-10) 1=> Fix bug of i2i conversion */ -/* (missed repeating /s in FI after F for multi-component case) */ - if (nNumComponents[iINChI][TAUT_NON]>1) /* if multi-component */ - if ( !s[iINChI][TAUT_YES][0] && !s[iINChI][TAUT_YES][1] )/* if no /s in M, MI */ - if ( (s[iINChI][TAUT_NON][0]>1) && (s[iINChI][TAUT_NON][1]>1) ) /* if /srel/srac in both F, FI */ - if ( s[iINChI][TAUT_NON][0] == s[iINChI][TAUT_NON][1] ) /* if same stereo in F and FI */ - /* we assume that at least one component in F has no actual stereo */ - /* and place deliberately 0 to appropriate place */ - for ( k = 0; k < nNumComponents[iINChI][TAUT_NON]; k ++ ) - { - INChI *pInChI = &pInpInChI[iINChI][TAUT_NON][k]; - if (pInChI->Stereo->nCompInv2Abs!=0) - { - pInChI->Stereo->nCompInv2Abs = 0; - goto fini; - } - } -fini: ; -#endif - - - if ( num_elem[iINChI][TAUT_YES] ) { - tot_charge[iINChI][TAUT_YES] += nNumProtons[iINChI][TAUT_YES].nNumRemovedProtons; - num_elem[iINChI][TAUT_YES][EL_NUMBER_H].num += nNumProtons[iINChI][TAUT_YES].nNumRemovedProtons; - } - - /**** Count H and isotopic H in Mobile and Fixed H represntations of components */ - /* if at least one component has Fixed-H layer then all components have Fixed-H */ - /* layer; those whose Fixed-H layer is empty have Fixed-H layer same as Mobile-H layer */ - if ( nNumComponents[iINChI][TAUT_NON] ) { - /* only if both Mobile and Fixed H exist */ - int nFormulaH[TAUT_NUM], nNumH[TAUT_NUM], nCharge[TAUT_NUM], nNumIsotopicH[TAUT_NUM][NUM_H_ISOTOPES]; - int nRemovedCharge, nRemovedH, nRemovedIsotopicH[NUM_H_ISOTOPES], nFoundRemovedIsoH; - int nTotRemovedProtons, nTotRemovedIsotopicH[NUM_H_ISOTOPES], bExists[TAUT_NUM]; - INChI *pInChI[TAUT_NUM]; - nTotRemovedProtons = 0; - memset( nTotRemovedIsotopicH, 0, sizeof(nTotRemovedIsotopicH) ); - len2 = inchi_max( nNumComponents[iINChI][TAUT_YES], nNumComponents[iINChI][TAUT_NON] ); - - for ( k = 0; k < len2; k ++ ) { - /* k is a component index */ - for ( j = 0; j < TAUT_NUM; j ++ ) { - /* j is 0=TAUT_NON or 1=TAUT_YES */ - pInChI[j] = NULL; /* initialization 2006-03 */ - bExists[j] = (k < nNumComponents[iINChI][j]) && - pInpInChI[iINChI][j][k].nNumberOfAtoms && - !pInpInChI[iINChI][j][k].bDeleted; - } - if ( !bExists[TAUT_NON] ) { - /* TAUT_YES does not exist for a proton (H+) in TAUT_NON */ - ret2 = RI_ERR_SYNTAX; - goto exit_function; - } - /* at this point at least one of Mobile[k] and Fixed[k] real InChI exists */ - /* initialize for counting removed protons and isotopic H from kth Mobile-H component */ - for ( j = 0; j < TAUT_NUM; j ++ ) { - if ( bExists[j] ) { - pInChI[j] = &pInpInChI[iINChI][j][k]; /* BC: reading uninit memory (fixed?) */ - } - nFormulaH[j] = 0; - nNumH[j] = 0; - nCharge[j] = 0; - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - nNumIsotopicH[j][m] = 0; - } - } - /* extract number of H, isotopic H, and charge */ - for ( j = 0; j < TAUT_NUM; j ++ ) { - if ( !bExists[j] ) - continue; - if ( 0 > (ret2 = GetInChIFormulaNumH( pInChI[j], &nFormulaH[j] )) || - 0 > (ret2 = GetInChINumH( pInChI[j], &nNumH[j] )) || - 0 > (ret2 = GetInChIIsoH( pInChI[j], nNumIsotopicH[j] )) ) { - goto exit_function; - } - nCharge[j] = pInChI[j]->nTotalCharge; - } - for ( j = 0; j < TAUT_NUM; j ++ ) { - if ( !bExists[j] ) - continue; - if ( nFormulaH[j] != nNumH[j] ) { - ret2 = RI_ERR_SYNTAX; - goto exit_function; - } - } - nFoundRemovedIsoH = 0; - nRemovedCharge = nCharge[TAUT_NON] - nCharge[TAUT_YES]; - nRemovedH = nNumH[TAUT_NON] - nNumH[TAUT_YES]; - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - nFoundRemovedIsoH += 0 != (nRemovedIsotopicH[m] = nNumIsotopicH[TAUT_NON][m] - - nNumIsotopicH[TAUT_YES][m] ); - } - if ( nRemovedCharge != nRemovedH ) { - ret2 = RI_ERR_SYNTAX; - goto exit_function; - } - if ( nRemovedCharge || nFoundRemovedIsoH ) { - COMPONENT_REM_PROTONS *pNumProtons; - if ( !nNumProtons[iINChI][TAUT_YES].pNumProtons ) { - /* allocate only if needed */ - nNumProtons[iINChI][TAUT_YES].pNumProtons = - (COMPONENT_REM_PROTONS *) inchi_calloc(len2, - sizeof(nNumProtons[0][0].pNumProtons[0])); - if ( !nNumProtons[iINChI][TAUT_YES].pNumProtons ) { - ret2 = RI_ERR_ALLOC; - goto exit_function; - } - } - pNumProtons = nNumProtons[iINChI][TAUT_YES].pNumProtons+k; - pNumProtons->nNumRemovedProtons = nRemovedH; - nTotRemovedProtons += nRemovedH; - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - pNumProtons->nNumRemovedIsotopicH[m] = nRemovedIsotopicH[m]; - nTotRemovedIsotopicH[m] += nRemovedIsotopicH[m]; - } - /* make sure the Mobile-H InChI has nTautomer */ - if ( pInChI[TAUT_YES] && bExists[TAUT_YES] ) { - if ( !pInChI[TAUT_YES]->lenTautomer ) { - pInChI[TAUT_YES]->lenTautomer = 1; - } - if ( !pInChI[TAUT_YES]->nTautomer ) { - pInChI[TAUT_YES]->nTautomer = (AT_NUMB *)inchi_calloc(pInChI[TAUT_YES]->lenTautomer, sizeof(pInChI[0]->nTautomer[0]) ); - } - } - } - } - if ( nNumProtons[iINChI][TAUT_YES].pNumProtons ) { - /* check consistency */ -#if ( FIX_ISO_FIXEDH_BUG_READ == 1 ) - int iso_diff[NUM_H_ISOTOPES], iso_diff_tot=0; -#endif - if ( nTotRemovedProtons != nNumProtons[iINChI][TAUT_YES].nNumRemovedProtons ) { - ret2 = RI_ERR_SYNTAX; - goto exit_function; - } -#if ( FIX_ISO_FIXEDH_BUG_READ == 1 ) - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - iso_diff[m] = nNumProtons[iINChI][TAUT_YES].nNumRemovedIsotopicH[m]-nTotRemovedIsotopicH[m]; - if ( iso_diff[m] < 0 ) - { - ret2 = RI_ERR_SYNTAX; - goto exit_function; - } else { - /* InChI-1.02b bug: nTotRemovedIsotopicH[m] < nNumProtons[iINChI][TAUT_YES].nNumRemovedIsotopicH[m] */ - /* in non-tautomeric components where D(+) or T(+) was removed from -NH(+)= or =OH(+) */ - iso_diff_tot += iso_diff[m]; - } - } - if ( iso_diff_tot ) { - if ( 0 > bIsoMayBeArranged( bInchi2Struct, iso_diff, nNumProtons, pInpInChI, nNumComponents, iINChI )) { - ret2 = RI_ERR_SYNTAX; - goto exit_function; - } - } -#else - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - if ( nTotRemovedIsotopicH[m] != nNumProtons[iINChI][TAUT_YES].nNumRemovedIsotopicH[m] ) { - ret2 = RI_ERR_SYNTAX; - goto exit_function; - } - } -#endif - } - } - - /* make Mobile H and Fixed H InChI arrays have same length */ - len2 = len1 = 0; - if ( nNumComponents[iINChI][TAUT_YES] < nNumComponents[iINChI][TAUT_NON] ) { - j = TAUT_YES; /* less components in Mobile-H layer */ - len2 = nNumComponents[iINChI][TAUT_NON]; - len1 = nNumComponents[iINChI][TAUT_YES]; - } else - if ( nNumComponents[iINChI][TAUT_YES] > nNumComponents[iINChI][TAUT_NON] ) { - j = TAUT_NON; /* less components in Fixed-H layer */ - len2 = nNumComponents[iINChI][TAUT_YES]; - len1 = nNumComponents[iINChI][TAUT_NON]; - } - /* always len1 <= len2; if Mobile-H and Fixed-H have same number of components then len1=len2=0 */ - if ( len2 && len1 ) { - INChI *pInChI = (INChI *)inchi_calloc( len2, sizeof( pInChI[0] ) ); - if ( !pInChI ) { - ret2 = RI_ERR_ALLOC; - goto exit_function; - } - memcpy( pInChI, pInpInChI[iINChI][j], len1 * sizeof( pInChI[0] ) ); - inchi_free( pInpInChI[iINChI][j] ); - pInpInChI[iINChI][j] = pInChI; - nNumComponents[iINChI][j] = len2; - for ( ; len1 < len2; len1 ++ ) { - if ( j == TAUT_YES ) { - /* mark added to Mobile H layer components as deleted protons */ - if ( 0 > (ret2 = nFillOutProtonMobileH( pInpInChI[iINChI][j]+len1 ) ) ) { - goto exit_function; - } - if ( 0 > (ret2 = nProtonCopyIsotopicInfo( pInpInChI[iINChI][j]+len1/* to */, - pInpInChI[iINChI][TAUT_NON]+len1/* from */ ) ) ) { - goto exit_function; - } - } else { - /* mark added to Fixed H layer components as empty deleted */ - /* this should not happen */ - pInChI[len1].bDeleted = 1; - } - } - } - } /* end of iINChI cycle */ - /* check balances */ - for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) { - for ( i = iINChI; i < INCHI_NUM; i ++ ) { - for ( j = 0; j < TAUT_NUM; j ++ ) { - for ( k = j; k < TAUT_NUM; k ++ ) { - if ( (iINChI!=i || j !=k) && num_elem[iINChI][j] && num_elem[i][k] ) { - if ( tot_charge[iINChI][j] != tot_charge[i][k] ) { - ret2 = RI_ERR_SYNTAX; - goto exit_function; - } - for ( m = 0; m <= nElDataLen; m ++ ) { - if ( num_elem[iINChI][j][m].num != num_elem[i][k][m].num ) { - ret2 = RI_ERR_SYNTAX; - goto exit_function; - } - } - /* - if ( memcmp( num_elem[iINChI], num_elem[i][k], (nElDataLen+1)*sizeof(num_elem[0][0][0]) ) { - ret2 = RI_ERR_SYNTAX; - goto exit_function; - } - */ - } - } - } - } - } - - - } else { - ret2 = ret; - } -exit_function: - for ( i = 0; i < INCHI_NUM; i ++ ) { - for ( j = 0; j < TAUT_NUM; j ++ ) { - if ( num_elem[i][j] ) { - inchi_free( num_elem[i][j] ); - num_elem[i][j] = NULL; - } - } - } - *nErr = (ret2 < 0 && ret2 != RI_ERR_EOL)? ret2 : 0; - return ret; -} - -/**************************************************************************************/ -#if ( FIX_ISO_FIXEDH_BUG_READ == 1 ) -#undef TAUT_YES -int bIsoMayBeArranged( int bInchi2Struct, int iso_diff[NUM_H_ISOTOPES], REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM], - INChI *pInpInChI[INCHI_NUM][TAUT_NUM], int nNumComponents[INCHI_NUM][TAUT_NUM], int iINChI ) -{ -const int TAUT_YES = 1; - int i, k, m, n_found=0, n_found_at_in_component, n_found_H_in_component, i_iso_at, num_iso_H=0, num_iso_H_orig, num_add_iso_H, orig_add_H; - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - num_iso_H += iso_diff[m]; - } - num_iso_H_orig = num_iso_H; - for ( k = 0; k < nNumComponents[iINChI][TAUT_YES] && k < nNumComponents[iINChI][TAUT_NON]; k ++ ) { - INChI *pInChI = &pInpInChI[iINChI][TAUT_NON][k]; - INChI *pInChITaut = &pInpInChI[iINChI][TAUT_YES][k]; - if ( pInChITaut->bDeleted || pInChI->bDeleted || - pInChITaut->nNumberOfIsotopicAtoms > 0 || - pInChITaut->lenTautomer > 1 && pInChITaut->nTautomer && pInChITaut->nTautomer[0] > 0 || - NULL == nNumProtons[iINChI][TAUT_YES].pNumProtons || - nNumProtons[iINChI][TAUT_YES].pNumProtons[k].nNumRemovedProtons <= 0 || - pInChI->nNumberOfIsotopicAtoms > 0 || - nNumProtons[iINChI][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH[0] || - nNumProtons[iINChI][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH[1] || - nNumProtons[iINChI][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH[2] - ) { - continue; - } - /* check if fixed-H has isotopic H; count the possibilities */ - orig_add_H = nNumProtons[iINChI][TAUT_YES].pNumProtons[k].nNumRemovedProtons; - n_found_at_in_component = 0; /* number of atoms that may accept isotopic H */ - n_found_H_in_component = 0; - for ( i = 0; i < pInChI->nNumberOfAtoms; i ++ ) { - int nNumRemovedH = (int)pInChI->nNum_H[i] - (int)pInChITaut->nNum_H[i]; - if ( nNumRemovedH > 0) { - n_found_at_in_component ++; - n_found_H_in_component += nNumRemovedH; - } - } - if ( n_found_at_in_component > 0 && num_iso_H > 0 && bInchi2Struct ) { - pInChI->IsotopicAtom = (INChI_IsotopicAtom *)calloc(inchi_min(n_found_at_in_component, num_iso_H), sizeof(pInChI->IsotopicAtom[0])); - } - for ( i = 0, i_iso_at = 0; i < pInChI->nNumberOfAtoms; i ++ ) { - int nNumRemovedH = (int)pInChI->nNum_H[i] - (int)pInChITaut->nNum_H[i]; - n_found += nNumRemovedH; /* found H removed in mobile-H layer */ - if ( nNumRemovedH > 0 && num_iso_H > 0 && orig_add_H ) { - for ( m = 0; m < NUM_H_ISOTOPES && 0 < num_iso_H && 0 < orig_add_H && 0 < nNumRemovedH; m ++ ) { - if ( iso_diff[m] > 0 ) { - num_add_iso_H = inchi_min( iso_diff[m], nNumRemovedH ); /* atom limit */ - if ( num_add_iso_H > orig_add_H ) /* component limit */ - num_add_iso_H = orig_add_H; - iso_diff[m] -= num_add_iso_H; /* update tot removed single isotope H limit */ - num_iso_H -= num_add_iso_H; /* update tot removed isotopic H limit */ - orig_add_H -= num_add_iso_H; /* update component limit */ - nNumRemovedH -= num_add_iso_H; /* update atom limit */ - nNumProtons[iINChI][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH[m] += num_add_iso_H; - if ( pInChI->IsotopicAtom ) { - pInChI->IsotopicAtom[i_iso_at].nAtomNumber = i+1; - switch( m ) { - case 0: - pInChI->IsotopicAtom[i_iso_at].nNum_H += num_add_iso_H; - break; - case 1: - pInChI->IsotopicAtom[i_iso_at].nNum_D += num_add_iso_H; - break; - case 2: - pInChI->IsotopicAtom[i_iso_at].nNum_T += num_add_iso_H; - break; - } - } - } - } - if ( pInChI->IsotopicAtom ) { - i_iso_at ++; - } - } - } - if ( pInChI->IsotopicAtom && i_iso_at ) { - pInChI->nNumberOfIsotopicAtoms = i_iso_at; - } - } - if ( n_found - num_iso_H >= 0 ) { - /* Success. Arrange isotopic H between components */ - - } - - return n_found - num_iso_H_orig; /* >0 => ambiguous reconstruction, 0 => unambiguous, <0 => impossible */ -} -#define TAUT_YES 1 -#endif - -/******************************************************************************************************/ -typedef enum tagAuxInfoState { - AST_VERSION, /* 0 */ - - AST_MOBILE_H_NUMBERS, /* 1 /N: */ - AST_MOBILE_H_ATOM_EQ, /* 2 /E: */ - AST_MOBILE_H_GROUP_EQ, /* 3 /gE: */ - AST_MOBILE_H_SP3_INV, /* 4 /it: */ - AST_MOBILE_H_SP3_INV_NUMBERS, /* 5 /iN: */ - - AST_MOBILE_H_ISO_LAYER_FORK, /* 6 */ - - AST_MOBILE_H_ISO_NUMBERS, /* 7 /I: */ - AST_MOBILE_H_ISO_ATOM_EQ, /* 8 /E: */ - AST_MOBILE_H_ISO_GROUP_EQ, /* 9 /gE: */ - AST_MOBILE_H_ISO_SP3_INV, /* 10 /it: */ - AST_MOBILE_H_ISO_SP3_INV_NUMBERS, /* 11 /iN: */ - - AST_FIXED_H_LAYER_FORK, /* 12 */ - - AST_FIXED_H_NUMBERS, /* 13 /F: */ - AST_FIXED_H_ATOM_EQ, /* 14 /E: */ - AST_FIXED_H_SP3_INV, /* 15 /it: */ - AST_FIXED_H_SP3_INV_NUMBERS, /* 16 /iN: */ - - AST_FIXED_H_ISO_LAYER_FORK, /* 17 */ - - AST_FIXED_H_ISO_NUMBERS, /* 18 /I: */ - AST_FIXED_H_ISO_ATOM_EQ, /* 19 /E: */ - AST_FIXED_H_ISO_SP3_INV, /* 20 /it: */ - AST_FIXED_H_ISO_SP3_INV_NUMBERS, /* 21 /iN: */ - - AST_REVERSE_INFO_CRV, /* 22 /CRV: */ - AST_REVERSE_INFO_ATOMS, /* 23 /rA: */ - AST_REVERSE_INFO_BONDS, /* 24 /rB: */ - AST_REVERSE_INFO_XYZ, /* 25 /rC: */ - - AST_RECONNECTED_LAYER_FORK, /* 26 /R: */ - AST_RECONNECTED_LAYER_NUMBERS /* 27 */ - -}AUX_INFO_STATE; - -/************************************************************************************/ -int ParseAuxSegmentVersion( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) -{ - const char *q; - if ( isdigit( UCINT *str ) && (inchi_strtol( str, &q, 10), !*q) ) { - return 1; - } - return RI_ERR_SYNTAX; -} -/************************************************************************************/ -int CopyAtomNumbers( INChI *pInChI_To, int bIsoTo, INChI *pInChI_From, int bIsoFrom ) -{ - AT_NUMB *pTo, *pFrom; - if ( !pInChI_To || !pInChI_From || pInChI_To->bDeleted || pInChI_From->bDeleted || - !pInChI_To->nNumberOfAtoms || !pInChI_From->nNumberOfAtoms || - pInChI_To->nNumberOfAtoms != pInChI_From->nNumberOfAtoms || - !pInChI_From->nPossibleLocationsOfIsotopicH ) { - return RI_ERR_PROGR; - } - if ( !pInChI_To->nPossibleLocationsOfIsotopicH ) { - pInChI_To->nPossibleLocationsOfIsotopicH = (AT_NUMB *)inchi_calloc( 2*pInChI_To->nNumberOfAtoms, - sizeof(pInChI_To->nPossibleLocationsOfIsotopicH[0])); - if ( !pInChI_To->nPossibleLocationsOfIsotopicH ) { - return RI_ERR_ALLOC; - } - } - pTo = pInChI_To->nPossibleLocationsOfIsotopicH + (bIsoTo? 0 : pInChI_To->nNumberOfAtoms ); - pFrom = pInChI_From->nPossibleLocationsOfIsotopicH + (bIsoFrom? 0 : pInChI_To->nNumberOfAtoms ); - if ( pTo == pFrom ) { - return RI_ERR_PROGR; - } - memcpy( pTo, pFrom, pInChI_To->nNumberOfAtoms*sizeof(pTo[0]) ); - return 1; -} -/************************************************************************************/ -int ParseAuxSegmentNumbers( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state, int *pbAbc ) -{ - int bIso = 0, iComponent = 0, nNumComponents, bIso_From, bAltInChIExists; - INChI *pInChI = NULL, *pAltInChI = NULL, *pInChI_From = NULL; - const char *p, *q, *pStart, *pEnd, *t; - static const char mult_type[] = "mnM"; - int val, ret, k, mpy_component, num; - AT_NUMB *pNumb; - int base = 10; - /* save isotopic numbering into the first nNumberOfAtoms elements of INChI::nPossibleLocationsOfIsotopicH */ - /* save non-isotopic numbering into the second half of nNumberOfAtoms elements of INChI::nPossibleLocationsOfIsotopicH */ - - switch( state ) { - case AST_MOBILE_H_NUMBERS: - if ( bMobileH != TAUT_YES ) - return RI_ERR_PROGR; - if ( memcmp( str, "N:", 2 ) ) - return 0; - break; - case AST_FIXED_H_NUMBERS: - if ( bMobileH != TAUT_NON ) - return RI_ERR_PROGR; - if ( memcmp( str, "F:", 2 ) ) - return 0; - break; - case AST_MOBILE_H_ISO_NUMBERS: - if ( bMobileH != TAUT_YES ) - return RI_ERR_PROGR; - if ( memcmp( str, "I:", 2 ) ) - return 0; - bIso = 1; - break; - case AST_FIXED_H_ISO_NUMBERS: - if ( bMobileH != TAUT_NON ) - return RI_ERR_PROGR; - if ( memcmp( str, "I:", 2 ) ) - return 0; - bIso = 1; - break; - default: - return RI_ERR_PROGR; - } - pStart = str+2; - if ( !*pStart ) { - return 1; - } - iComponent = 0; - nNumComponents = ppnNumComponents[bMobileH]; - - bAltInChIExists = (NULL != pInpInChI[ALT_TAUT(bMobileH)]); - while( 1 ) { - /* cycle over components */ - if ( !(pEnd = strchr( pStart, ';' )) ) { - pEnd = pStart + strlen(pStart); - } - /* check */ - if ( !pInpInChI[bMobileH] ) { - return 1; /* invalid aux info */ - } - pInChI = pInpInChI[bMobileH] + iComponent; - pAltInChI = pInpInChI[ALT_TAUT(bMobileH)] + iComponent; - if ( (isdigit(UCINT *pStart) && - 0 < (val = (int)inchi_strtol( pStart, &q, 10)) || - (q = pStart, val=1) )&& - (t=strchr(mult_type, *q)) && q+1 == pEnd ) { - - /* process the abbreviation */ - - pInChI_From = NULL; - - switch( bMobileH ) { - - case TAUT_YES: - switch ( bIso ) { - case 0: - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - case 1: - if ( *q != 'm' ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - /* isotopic Mobile-H <-- non-isotopic Mobile H */ - pInChI_From = pInChI; - bIso_From = 0; - break; - default: - ret = RI_ERR_PROGR; - goto exit_function; - } - break; - - case TAUT_NON: - switch ( *q ) { - case 'm': /* same as mobile H */ - switch( bIso ) { - case 0: /* from Mobile-H not isotopic */ - pInChI_From = bAltInChIExists? pAltInChI:NULL; - bIso_From = 0; - break; - - case 1: - pInChI_From = bAltInChIExists? pAltInChI:NULL;; - bIso_From = 1; - break; - default: - ret = RI_ERR_PROGR; - goto exit_function; - } - break; - case 'n': /* same as non-isotopic Fixed-H */ - switch ( bIso ) { - case 0: - ret = 1; /*RI_ERR_SYNTAX;*/ - goto exit_function; - case 1: - pInChI_From = pInChI; - bIso_From = 0; - default: - ret = RI_ERR_PROGR; - goto exit_function; - } - break; - case 'M': /* same as isotopic Mobile-H */ - switch ( bIso ) { - case 0: - ret = RI_ERR_SYNTAX; - goto exit_function; - case 1: - pInChI_From = bAltInChIExists? pAltInChI:NULL;; - bIso_From = 1; - break; - default: - ret = RI_ERR_PROGR; - goto exit_function; - } - break; - default: - ret = 1; /*RI_ERR_SYNTAX;*/ - goto exit_function; - } - break; - - } - /* copy */ - if ( pInChI_From ) { - for ( k = 0; k < val; k ++ ) { - CopyAtomNumbers( pInChI+k, bIso, pInChI_From+k, bIso_From ); - } - } - mpy_component = val; - } else { - mpy_component = 1; - p = pStart; - pNumb = pInChI->nPossibleLocationsOfIsotopicH; - if ( !pNumb ) { - pNumb = (AT_NUMB *)inchi_calloc( 2*pInChI->nNumberOfAtoms, sizeof(pNumb[0] ) ); - if ( !pNumb ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - pInChI->nPossibleLocationsOfIsotopicH = pNumb; - } - pNumb += bIso? 0 : pInChI->nNumberOfAtoms; - if ( pStart < pEnd && *pbAbc == -1 ) { - /* check if compressed InChI */ - *pbAbc = isupper( UCINT *pStart)? 1 : 0; - } - base = (*pbAbc==1)? ALPHA_BASE : 10; - - if ( *pbAbc == 1 ) { - for ( k = 0, p = pStart; k < pInChI->nNumberOfAtoms && p < pEnd; k ++, p ++ ) { - num = (AT_NUMB)inchi_strtol( p, &q, base ); - if ( num <= 0 || p == q ) { - ret = RI_ERR_SYNTAX; - goto exit_function; - } - pNumb[k] = (AT_NUMB)num; - p = q; - if ( p == pEnd ) { - break; /* main end of cycle */ - } - } - } else { - for ( k = 0, p = pStart; k < pInChI->nNumberOfAtoms && p < pEnd; k ++, p ++ ) { - pNumb[k] = (AT_NUMB)inchi_strtol( p, &q, 10 ); - p = q; - if ( p == pEnd ) { - break; /* main end of cycle */ - } else - if ( *p != ',' ) { - ret = RI_ERR_SYNTAX; - goto exit_function; - } - } - } - if ( p != pEnd || k+1 != pInChI->nNumberOfAtoms ) { - ret = RI_ERR_SYNTAX; - goto exit_function; - } - } - - iComponent += mpy_component; - if ( *pEnd ) { - pStart = pEnd+1; - continue; - } else { - break; - } - } - - if ( nNumComponents != iComponent ) { - ret = 1; /*RI_ERR_SYNTAX;*/ /* syntax error */ - goto exit_function; - } - ret = iComponent + 1; - -exit_function: - return ret; - -} -/**********************************************************************************************************/ -int ParseAuxSegmentAtomEqu( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) -{ - switch( state ) { - case AST_MOBILE_H_ATOM_EQ: - if ( bMobileH != TAUT_YES ) - return RI_ERR_PROGR; - if ( memcmp( str, "E:", 2 ) ) - return 0; - break; - case AST_MOBILE_H_ISO_ATOM_EQ: - if ( bMobileH != TAUT_YES ) - return RI_ERR_PROGR; - if ( memcmp( str, "E:", 2 ) ) - return 0; - break; - case AST_FIXED_H_ATOM_EQ: - if ( bMobileH != TAUT_NON ) - return RI_ERR_PROGR; - if ( memcmp( str, "E:", 2 ) ) - return 0; - break; - case AST_FIXED_H_ISO_ATOM_EQ: - if ( bMobileH != TAUT_NON ) - return RI_ERR_PROGR; - if ( memcmp( str, "E:", 2 ) ) - return 0; - break; - default: - return RI_ERR_PROGR; - } - return 1; -} -/***********************************************************************************************************/ -int ParseAuxSegmentGroupEqu( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) -{ - switch( state ) { - case AST_MOBILE_H_GROUP_EQ: - if ( bMobileH != TAUT_YES ) - return RI_ERR_PROGR; - if ( memcmp( str, "gE:", 3 ) ) - return 0; - break; - case AST_MOBILE_H_ISO_GROUP_EQ: - if ( bMobileH != TAUT_YES ) - return RI_ERR_PROGR; - if ( memcmp( str, "gE:", 3 ) ) - return 0; - break; - default: - return RI_ERR_PROGR; - } - return 1; -} -/***********************************************************************************************************/ -int ParseAuxSegmentSp3Inv( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) -{ - switch( state ) { - case AST_MOBILE_H_SP3_INV: - if ( bMobileH != TAUT_YES ) - return RI_ERR_PROGR; - if ( memcmp( str, "it:", 3 ) ) - return 0; - break; - case AST_MOBILE_H_ISO_SP3_INV: - if ( bMobileH != TAUT_YES ) - return RI_ERR_PROGR; - if ( memcmp( str, "it:", 3 ) ) - return 0; - break; - case AST_FIXED_H_SP3_INV: - if ( bMobileH != TAUT_NON ) - return RI_ERR_PROGR; - if ( memcmp( str, "it:", 3 ) ) - return 0; - break; - case AST_FIXED_H_ISO_SP3_INV: - if ( bMobileH != TAUT_NON ) - return RI_ERR_PROGR; - if ( memcmp( str, "it:", 3 ) ) - return 0; - break; - default: - return RI_ERR_PROGR; - } - return 1; -} -/***********************************************************************************************************/ -int ParseAuxSegmentSp3InvNumbers( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) -{ - switch( state ) { - case AST_MOBILE_H_SP3_INV_NUMBERS: - if ( bMobileH != TAUT_YES ) - return RI_ERR_PROGR; - if ( memcmp( str, "iN:", 3 ) ) - return 0; - break; - case AST_MOBILE_H_ISO_SP3_INV_NUMBERS: - if ( bMobileH != TAUT_YES ) - return RI_ERR_PROGR; - if ( memcmp( str, "iN:", 3 ) ) - return 0; - break; - case AST_FIXED_H_SP3_INV_NUMBERS: - if ( bMobileH != TAUT_NON ) - return RI_ERR_PROGR; - if ( memcmp( str, "iN:", 3 ) ) - return 0; - break; - case AST_FIXED_H_ISO_SP3_INV_NUMBERS: - if ( bMobileH != TAUT_NON ) - return RI_ERR_PROGR; - if ( memcmp( str, "iN:", 3 ) ) - return 0; - break; - default: - return RI_ERR_PROGR; - } - return 1; -} -/***********************************************************************************************************/ -int ParseAuxSegmentReverseCRV( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) -{ - switch( state ) { - case AST_REVERSE_INFO_CRV: - if ( memcmp( str, "CRV:", 4 ) ) - return 0; - break; - default: - return RI_ERR_PROGR; - } - return 1; -} -/***********************************************************************************************************/ -int ParseAuxSegmentReverseAtoms( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) -{ - switch( state ) { - case AST_REVERSE_INFO_ATOMS: - if ( memcmp( str, "rA:", 3 ) ) - return 0; - break; - default: - return RI_ERR_PROGR; - } - return 1; -} -/***********************************************************************************************************/ -int ParseAuxSegmentReverseBonds( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) -{ - switch( state ) { - case AST_REVERSE_INFO_BONDS: - if ( memcmp( str, "rB:", 3 ) ) - return 0; - break; - default: - return RI_ERR_PROGR; - } - return 1; -} -/***********************************************************************************************************/ -int ParseAuxSegmentReverseXYZ( const char *str, int bMobileH, XYZ_COORD **ppXYZ, INChI *pInpInChI[], int ppnNumComponents[], int state ) -{ - const char *pStart, *p, *q; - XYZ_COORD *pXYZ = NULL; - int nLenXYZ=0, i, j; - switch( state ) { - case AST_REVERSE_INFO_XYZ: - if ( memcmp( str, "rC:", 3 ) ) - return 0; - break; - default: - return RI_ERR_PROGR; - } - pStart = str+3; - /* count coordinates */ - for ( p = pStart, nLenXYZ = 0; *p; p ++ ) { - nLenXYZ += ( *p == ';' ); - } - if ( !nLenXYZ ) { - return RI_ERR_SYNTAX; - } - if ( NULL == (pXYZ = (XYZ_COORD *)inchi_calloc( nLenXYZ, sizeof(pXYZ[0]) )) ) { - return RI_ERR_ALLOC; - } - for ( p = pStart, i = 0; *p && i < nLenXYZ; p ++, i ++ ) { - for ( j = 0; j < 3; j ++ ) { - pXYZ[i].xyz[j] = inchi_strtod( p, &q ); - p = q + (*q == ',' ); - } - if ( *p != ';' ) { - break; - } - } - if ( i != nLenXYZ || *p ) { - return RI_ERR_SYNTAX; - } - *ppXYZ = pXYZ; - return nLenXYZ+1; -} -/************************************************************************************/ -int AddAuxSegmentCoord( int nRet, XYZ_COORD *pXYZ, int nLenXYZ, INChI *pInpInChI[INCHI_NUM][TAUT_NUM], - int nNumComponents[INCHI_NUM][TAUT_NUM] ) -{ - int iINChI, j, k, n, m, numAt[TAUT_NUM], num_at, nNumMissingNumbers = 0, ret = 0; - INChI *pInChI = NULL; - INChI *pAltInChI = NULL; - XYZ_COORD *pxyz; - - /* propagate numberings */ - for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) { - for ( j = TAUT_YES; TAUT_NON <= j; j -- ) { - for ( k = 0; k < nNumComponents[iINChI][j]; k ++ ) { - int jj = ALT_TAUT(j); - pInChI = pInpInChI[iINChI][j] + k; - pAltInChI = (k < nNumComponents[iINChI][jj])? pInpInChI[iINChI][jj] + k : NULL; - numAt[j] = ( !pInChI->bDeleted )? pInChI->nNumberOfAtoms : 0; - numAt[jj] = ( pAltInChI && !pAltInChI->bDeleted )? pAltInChI->nNumberOfAtoms : 0; - switch( j ) { - case TAUT_YES: - if ( !numAt[j] ) { - break; /* component does not exist */ - } - if ( !pInChI->nPossibleLocationsOfIsotopicH ) { - nNumMissingNumbers ++; - break; - } - if ( !pInChI->nPossibleLocationsOfIsotopicH[0] ) { - if ( pInChI->nPossibleLocationsOfIsotopicH[numAt[j]] ) { - /* copy from non-isotopic (2nd half of the at. numbers array) to the isotopic (1st half) */ - ret = CopyAtomNumbers( pInChI, 1, pInChI, 0 ); - if ( ret < 0 ) { - goto exit_function; - } - } else { - inchi_free( pInChI->nPossibleLocationsOfIsotopicH ); - pInChI->nPossibleLocationsOfIsotopicH = NULL; - nNumMissingNumbers ++; - } - } - break; - - case TAUT_NON: - if ( !numAt[j] ) { - break; /* component does not exist */ - } - if ( !pInChI->nPossibleLocationsOfIsotopicH ) { - /* trying to get numbers from Mobile-H component */ - if ( !numAt[jj] || !(pAltInChI->nPossibleLocationsOfIsotopicH) ) { - nNumMissingNumbers ++; - break; - } - if ( pAltInChI->nPossibleLocationsOfIsotopicH[0] ) { - ret = CopyAtomNumbers( pInChI, 1, pAltInChI, 1 ); - if ( ret < 0 ) { - goto exit_function; - } - } else - if ( pAltInChI->nPossibleLocationsOfIsotopicH[numAt[jj]] ) { - ret = CopyAtomNumbers( pInChI, 1, pAltInChI, 0 ); - if ( ret < 0 ) { - goto exit_function; - } - } else { - /* pAltInChI->nPossibleLocationsOfIsotopicH should have */ - /* been deallocated on previous TAUT_YES pass */ - ret = RI_ERR_PROGR; - goto exit_function; - } - } else - if ( !pInChI->nPossibleLocationsOfIsotopicH[0] ) { - if ( pInChI->nPossibleLocationsOfIsotopicH[numAt[j]] ) { - /* copy from non-isotopic to isotopic */ - ret = CopyAtomNumbers( pInChI, 1, pInChI, 0 ); - if ( ret < 0 ) { - goto exit_function; - } - } else { - inchi_free( pInChI->nPossibleLocationsOfIsotopicH ); - pInChI->nPossibleLocationsOfIsotopicH = NULL; - nNumMissingNumbers ++; - } - } - break; - } - } - } - } - /* add coordinates */ - for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) { - for ( j = 0; j < TAUT_NUM; j ++ ) { - for ( k = 0; k < nNumComponents[iINChI][j]; k ++ ) { - pInChI = pInpInChI[iINChI][j] + k; - num_at = ( !pInChI->bDeleted )? pInChI->nNumberOfAtoms : 0; - if ( !num_at ) { - if ( pInChI->nPossibleLocationsOfIsotopicH ) { - inchi_free( pInChI->nPossibleLocationsOfIsotopicH ); - pInChI->nPossibleLocationsOfIsotopicH = NULL; - } - continue; - } - if ( !pInChI->nPossibleLocationsOfIsotopicH ) { - continue; - } - if ( iINChI == INCHI_BAS && num_at == 1 && - pInChI->szHillFormula && !strcmp(pInChI->szHillFormula, "H") && - (int)pInChI->nPossibleLocationsOfIsotopicH[0]-1 >= nLenXYZ ) { - ; /* a single atom H disconnected from a metal atom has no coordinates */ - } else { - /* add atom coordinates */ - pxyz = (XYZ_COORD *)inchi_calloc( num_at, sizeof(pxyz[0])); - if ( !pxyz ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - for ( n = 0; n < num_at; n ++ ) { - m = (int)pInChI->nPossibleLocationsOfIsotopicH[n]-1; - if ( m < 0 || m >= nLenXYZ ) { - inchi_free( pxyz ); - ret = RI_ERR_SYNTAX; - goto exit_function; - } - pxyz[n] = pXYZ[m]; - } - pInChI->IsotopicTGroup = (INChI_IsotopicTGroup *)pxyz; - } - inchi_free( pInChI->nPossibleLocationsOfIsotopicH ); - pInChI->nPossibleLocationsOfIsotopicH = NULL; - } - } - } - ret = nRet; /* normal exit */ - -exit_function: - return ret; -} -/************************************************************************************/ -int ReadInChICoord( INCHI_IOSTREAM *pInp, - SEGM_LINE *pLine, int *pState, - INChI *pInpInChI[INCHI_NUM][TAUT_NUM], - int nNumComponents[INCHI_NUM][TAUT_NUM] ) -{ - int c, fst, ret=RI_ERR_ALLOC; - int bMobileH = TAUT_YES, bReconn = INCHI_BAS; - const char szToken[] = INCHI_TOKEN; - int state=-1, prev_state=-1; - XYZ_COORD *pXYZ = NULL; - int nLenXYZ = 0; - int bAbc = -1; /* initially undefined */ - - *pState = 0; - - INCHI_HEAPCHK - /* Get "InChI=1/" */ - if ( pLine->len ) { - c = pLine->c; - } else { - c = nGetInChISegment( pInp, pLine, szToken ); - } - if ( c == RI_ERR_EOF && !pLine->len && !pLine->str[0] ) { - ret = c; - pLine->len = 0; - goto exit_error; - } - if ( pLine->len == 0 || c != SEG_END && c != RI_ERR_EOF && !INCHI_INP_EOL(c) ) { - *pState = -1; - pLine->len = 0; - ret = RI_ERR_PROGR; - goto exit_error; - } - if ( memcmp(pLine->str, "AuxInfo=", 8) ) { - *pState = -1; - return c; - } - state = AST_VERSION; - ret = 1; /* means read the next segment */ - do { - /* read the next segment up to the '/' */ - INCHI_HEAPCHK - if ( ret < 0 ) { - *pState = prev_state; - break; - } - prev_state = state + (bReconn? IST_HAPPENED_IN_RECMET : 0); - /* prev_state = state;*/ - if ( 0 < ret ) { - /* read next segment */ - if ( c != RI_ERR_EOF && c != SEG_END ) { - /* abnormal reading result; should not happen */ - while ( c != RI_ERR_EOF && !INCHI_INP_EOL(c) ) { - /* bypass to the end of line or file */ - c = getInChIChar(pInp); - } - ret = (c == RI_ERR_EOF)? RI_ERR_EOF : RI_ERR_EOL; /* end of line */ - pLine->len = 0; - pLine->c = ret; - break; - } - if ( c == RI_ERR_EOF ) { - ret = RI_ERR_EOF; /* end of line */ - break; - } - if ( c == SEG_END ) { - c = nGetInChISegment( pInp, pLine, szToken ); - } - if ( c < 0 ) { - goto exit_error; /* error */ - } - if ( !pLine->len ) { - ret = RI_ERR_EOL; /* end of line */ - break; - } - fst = UCINT pLine->str[0]; - } - /* process the seqment */ - switch ( state ) { - case AST_VERSION: - /* Mobile H */ - bMobileH = TAUT_YES; - ret = ParseAuxSegmentVersion( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_MOBILE_H_NUMBERS; - break; - case AST_MOBILE_H_NUMBERS: - ret = ParseAuxSegmentNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = AST_MOBILE_H_ATOM_EQ; - break; - case AST_MOBILE_H_ATOM_EQ: - ret = ParseAuxSegmentAtomEqu( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_MOBILE_H_GROUP_EQ; - break; - case AST_MOBILE_H_GROUP_EQ: - ret = ParseAuxSegmentGroupEqu( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_MOBILE_H_SP3_INV; - break; - case AST_MOBILE_H_SP3_INV: - ret = ParseAuxSegmentSp3Inv( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_MOBILE_H_SP3_INV_NUMBERS; - break; - case AST_MOBILE_H_SP3_INV_NUMBERS: - ret = ParseAuxSegmentSp3InvNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_MOBILE_H_ISO_LAYER_FORK; - break; - case AST_MOBILE_H_ISO_LAYER_FORK: - if ( !memcmp( pLine->str, "I:", 2 ) ) { - state = AST_MOBILE_H_ISO_NUMBERS; - } else - if ( !memicmp( pLine->str, "F:", 2 ) ) { - state = AST_FIXED_H_NUMBERS; - bMobileH = TAUT_NON; - } else - if ( /*bReconn == INCHI_BAS &&*/ !memicmp( pLine->str, "CRV:", 4 ) ) { - state = AST_REVERSE_INFO_CRV; - } else - if ( bReconn == INCHI_BAS && !memicmp( pLine->str, "rA:", 3 ) ) { - state = AST_REVERSE_INFO_ATOMS; - } else - if ( bReconn == INCHI_BAS && !memicmp( pLine->str, "R:", 3 ) ) { - ret = 1; /* read the next segment */ - state = AST_VERSION; - bMobileH = TAUT_YES; - bReconn = INCHI_REC; - } else { - ret = RI_ERR_SYNTAX; - } - break; - /* Mobile H, isotopic */ - case AST_MOBILE_H_ISO_NUMBERS: - ret = ParseAuxSegmentNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = AST_MOBILE_H_ISO_ATOM_EQ; - break; - case AST_MOBILE_H_ISO_ATOM_EQ: - ret = ParseAuxSegmentAtomEqu( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_MOBILE_H_ISO_GROUP_EQ; - break; - case AST_MOBILE_H_ISO_GROUP_EQ: - ret = ParseAuxSegmentGroupEqu( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_MOBILE_H_ISO_SP3_INV; - break; - case AST_MOBILE_H_ISO_SP3_INV: - ret = ParseAuxSegmentSp3Inv( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_MOBILE_H_ISO_SP3_INV_NUMBERS; - break; - case AST_MOBILE_H_ISO_SP3_INV_NUMBERS: - ret = ParseAuxSegmentSp3InvNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_FIXED_H_LAYER_FORK; - break; - case AST_FIXED_H_LAYER_FORK: - if ( !memicmp( pLine->str, "F:", 2 ) ) { - state = AST_FIXED_H_NUMBERS; - bMobileH = TAUT_NON; - } else - if ( /*bReconn == INCHI_BAS &&*/ !memicmp( pLine->str, "CRV:", 4 ) ) { - state = AST_REVERSE_INFO_CRV; - } else - if ( bReconn == INCHI_BAS && !memicmp( pLine->str, "rA:", 3 ) ) { - state = AST_REVERSE_INFO_ATOMS; - } else - if ( bReconn == INCHI_BAS && !memicmp( pLine->str, "R:", 3 ) ) { - ret = 1; /* read the next segment */ - state = AST_VERSION; - bMobileH = TAUT_YES; - bReconn = INCHI_REC; - } else { - ret = RI_ERR_SYNTAX; - } - break; - case AST_FIXED_H_NUMBERS: - ret = ParseAuxSegmentNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = AST_FIXED_H_ATOM_EQ; - break; - case AST_FIXED_H_ATOM_EQ: - ret = ParseAuxSegmentAtomEqu( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_FIXED_H_SP3_INV; - break; - case AST_FIXED_H_SP3_INV: - ret = ParseAuxSegmentSp3Inv( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_FIXED_H_SP3_INV_NUMBERS; - break; - case AST_FIXED_H_SP3_INV_NUMBERS: - ret = ParseAuxSegmentSp3InvNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_FIXED_H_ISO_LAYER_FORK; - break; - case AST_FIXED_H_ISO_LAYER_FORK: - if ( !memcmp( pLine->str, "I:", 2 ) ) { - state = AST_FIXED_H_ISO_NUMBERS; - } else - if ( /*bReconn == INCHI_BAS &&*/ !memicmp( pLine->str, "CRV:", 4 ) ) { - state = AST_REVERSE_INFO_CRV; - } else - if ( bReconn == INCHI_BAS && !memicmp( pLine->str, "rA:", 3 ) ) { - state = AST_REVERSE_INFO_ATOMS; - } else - if ( bReconn == INCHI_BAS && !memicmp( pLine->str, "R:", 3 ) ) { - ret = 1; /* read the next segment */ - state = AST_VERSION; - bMobileH = TAUT_YES; - bReconn = INCHI_REC; - } else { - ret = RI_ERR_SYNTAX; - } - break; - case AST_FIXED_H_ISO_NUMBERS: - ret = ParseAuxSegmentNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = AST_FIXED_H_ISO_ATOM_EQ; - break; - case AST_FIXED_H_ISO_ATOM_EQ: - ret = ParseAuxSegmentAtomEqu( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_FIXED_H_SP3_INV; - break; - case AST_FIXED_H_ISO_SP3_INV: - ret = ParseAuxSegmentSp3Inv( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_FIXED_H_ISO_SP3_INV_NUMBERS; - break; - case AST_FIXED_H_ISO_SP3_INV_NUMBERS: - ret = ParseAuxSegmentSp3InvNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_REVERSE_INFO_CRV; - break; - case AST_REVERSE_INFO_CRV: - ret = ParseAuxSegmentReverseCRV( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - /* state = (bReconn == INCHI_BAS)? AST_REVERSE_INFO_ATOMS : AST_RECONNECTED_LAYER_FORK;*/ - state = AST_REVERSE_INFO_ATOMS; - break; - case AST_REVERSE_INFO_ATOMS: - ret = ParseAuxSegmentReverseAtoms( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_REVERSE_INFO_BONDS; - break; - case AST_REVERSE_INFO_BONDS: - ret = ParseAuxSegmentReverseBonds( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_REVERSE_INFO_XYZ; - break; - case AST_REVERSE_INFO_XYZ: - ret = ParseAuxSegmentReverseXYZ( pLine->str, bMobileH, &pXYZ, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_RECONNECTED_LAYER_FORK; - if ( ret > 0 ) { - nLenXYZ = ret - 1; - } - break; - case AST_RECONNECTED_LAYER_FORK: - if ( bReconn == INCHI_BAS && !memicmp( pLine->str, "R:", 3 ) ) { - ret = 1; /* read the next segment */ - state = AST_VERSION; - bMobileH = TAUT_YES; - bReconn = INCHI_REC; - } else { - ret = RI_ERR_SYNTAX; - } - break; - } - } while( c >= 0 ); - - ret = AddAuxSegmentCoord( ret, pXYZ, nLenXYZ, pInpInChI, nNumComponents ); - -exit_error: - if ( pXYZ ) { - inchi_free( pXYZ ); - } - if ( ret >= 0 || c == RI_ERR_EOF || c == RI_ERR_EOL ) { - pLine->len = 0; - } - - return ret; -} -/******************************************************************************************************/ -int ReadInChILine(INCHI_IOSTREAM *pInp, SEGM_LINE *pLine, - char **pStr, int *pState, - INChI *pInpInChI[INCHI_NUM][TAUT_NUM], - int nNumComponents[INCHI_NUM][TAUT_NUM], - REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM], - int s[INCHI_NUM][TAUT_NUM][2], - int *bStdFormat, - int *bInputHasSaveOpt, unsigned char *inp_save_opt_bits) -{ - int c, fst, ret=RI_ERR_ALLOC, len; - int bMobileH = TAUT_YES, bReconn = INCHI_BAS; - const char szToken[] = INCHI_TOKEN; - char *p; - int state=-1, prev_state=-1; - int bAbc = -1; /* -1=> undefined, 0=> decimal, 1=> abc (compressed) */ - - const int len_std_prefix=8; - size_t k=0; - unsigned char let1, let2; - const char a2p[]="ABCDEFGHIJKLMNOP"; - - /* memset( pLine, 0, sizeof( pLine[0] ) ); */ - *pState = 0; - -next_line: - INCHI_HEAPCHK - /* Got "InChI=1/" */ - if ( pLine->len ) - { - c = pLine->c; - } - else - { - INCHI_HEAPCHK - c = nGetInChISegment( pInp, pLine, szToken ); - INCHI_HEAPCHK - } - if ( c == RI_ERR_EOF && !pLine->len && !pLine->str[0] ) - { - ret = c; - goto exit_function; - } - INCHI_HEAPCHK - - if ( pLine->len == 0 || c != SEG_END && c != RI_ERR_EOF || !(p = strstr(pLine->str, "InChI=1")) ) - { - if ( pLine->str && pLine->str == strstr ( pLine->str, "Structure" ) ) - { - if ( *pStr ) - { - INCHI_HEAPCHK - inchi_free( *pStr ); - } - *pStr = pLine->str; - /* bypass to the end of the 'Structure nnn' line */ - memset( pLine, 0, sizeof( pLine[0] ) ); - while ( c && !INCHI_INP_EOL(c) ) - { - c = getInChIChar(pInp); - } - goto next_line; - } - /* bypass to the end of unrecognized line */ - while ( c != RI_ERR_EOF && !INCHI_INP_EOL(c) ) - { - c = getInChIChar(pInp); - } - pLine->len = 0; - INCHI_HEAPCHK - goto next_line; - } - - - /* Check if got a standard InChI */ - if ( ( pLine->len == len_std_prefix ) && (pLine->str[len_std_prefix-1]=='S') ) - *bStdFormat=1; - else - *bStdFormat=0; - - - state=IST_MOBILE_H_FORMULA; - ret = 1; /* means read the next segment */ - do { - /* read the next segment up to the '/' */ - INCHI_HEAPCHK - if ( ret < 0 ) - { - *pState = prev_state; - break; - } - prev_state = state + (bReconn? IST_HAPPENED_IN_RECMET : 0); - if ( 0 < ret ) - { - /* read next segment */ - if ( c != RI_ERR_EOF && c != SEG_END ) - { - /* abnormal reading result; should not happen */ - /* unless we got backslash-SaveOpt */ - if ( c=='\\' ) - { - /* May be SaveOpt */ - *bInputHasSaveOpt = 1; - } - k = 0; - while ( c != RI_ERR_EOF && !INCHI_INP_EOL(c) ) - { - /* bypass to the end of line or file */ - c = getInChIChar(pInp); - k++; - if ( k==1 ) let1 = c; - else if ( k==2 ) let2 = c; - } - if ( k != 3) - { - /* not a valid SaveOpt which must be of two chars */ - *bInputHasSaveOpt = 0; - let1 = let2 = '\0'; - } - else - { - /* may be SaveOpt - analyze the content */ - if ( ( let2 >= 'A') && ( let2 <= 'D') ) /* letter-2 OK */ - { - *bInputHasSaveOpt = 0; - *inp_save_opt_bits = 0; - for (k=0; k<16; k++) - { - if ( a2p[k] == let1) /* letter-1 OK */ - { - *inp_save_opt_bits = (unsigned char) k; - *bInputHasSaveOpt = 1; - break; - } - } - if ( *bInputHasSaveOpt ) - { - if ( let2=='B' || let2=='D' ) - *inp_save_opt_bits |= SAVE_OPT_15T; - if ( let2=='C' || let2=='D' ) - *inp_save_opt_bits |= SAVE_OPT_KET; - } - } - } - - ret = (c == RI_ERR_EOF)? RI_ERR_EOF : RI_ERR_EOL; /* end of line */ - pLine->len = 0; - pLine->c = ret; - break; /* exit */ - } - if ( c == RI_ERR_EOF ) { - ret = RI_ERR_EOF; /* end of line */ - break; - } - if ( c == SEG_END ) { - c = nGetInChISegment( pInp, pLine, szToken ); - } - if ( c < 0 ) { - goto exit_error; /* error */ - } - if ( !pLine->len ) { - ret = RI_ERR_EOL; /* end of line */ - break; - } - fst = UCINT pLine->str[0]; - } - /* process the seqment */ - switch ( state ) { - - /* Mobile H, M */ /* / */ - case IST_MOBILE_H_FORMULA: - bMobileH = TAUT_YES; - ret = ParseSegmentFormula( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn] ); - state = IST_MOBILE_H_CONNECTIONS; - break; - case IST_MOBILE_H_CONNECTIONS: /* /c */ - ret = ParseSegmentConnections( pLine->str, bMobileH, &pInpInChI[bReconn][bMobileH], &nNumComponents[bReconn][bMobileH], &bAbc ); - state = IST_MOBILE_H; - break; - case IST_MOBILE_H: /* /h */ - ret = ParseSegmentMobileH( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], &bAbc ); - state = IST_MOBILE_H_CHARGE; - break; - case IST_MOBILE_H_CHARGE: /* /q */ - ret = ParseSegmentCharge( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn] ); - state = IST_MOBILE_H_PROTONS; - break; - case IST_MOBILE_H_PROTONS: /* /p */ - ret = ParseSegmentProtons( pLine->str, bMobileH, nNumProtons[bReconn], nNumComponents[bReconn] ); - state = IST_MOBILE_H_SP2; - break; - case IST_MOBILE_H_SP2: /* /b */ - ret = ParseSegmentSp2( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = IST_MOBILE_H_SP3; - break; - case IST_MOBILE_H_SP3: /* t */ - ret = ParseSegmentSp3( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = IST_MOBILE_H_SP3_M; - break; - case IST_MOBILE_H_SP3_M: /* /m */ - ret = ParseSegmentSp3m( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = IST_MOBILE_H_SP3_S; - break; - case IST_MOBILE_H_SP3_S: /* /s */ - ret = ParseSegmentSp3s( pLine->str, bMobileH, pInpInChI[bReconn], s[bReconn], nNumComponents[bReconn], state ); - state = IST_MOBILE_H_ISO_LAYER_FORK; - break; - case IST_MOBILE_H_ISO_LAYER_FORK: - /* find layer type after M */ - ret = 0; - switch( pLine->str[0] ) { - case 'i': - state = IST_MOBILE_H_ISO_ATOMS; /* MI */ - break; - case 'f': - state = IST_FIXED_H_FORMULA; /* F */ - break; - case 'r': - state = IST_RECONNECTED_FORMULA; /* reconnected */ - break; - default: - ret = RI_ERR_SYNTAX; - } - if ( INCHI_INP_EOL(c) && ret == 0 && !pLine->str[1] ) { - prev_state = state + (bReconn? IST_HAPPENED_IN_RECMET : 0); - ret = RI_ERR_SYNTAX; /* empty layer /i or /f or /r at the end of InChI line */ - } else - if ( !ret && state != IST_MOBILE_H_ISO_ATOMS ) { - len = strlen( pLine->str ); - if ( len > 1 ) { - memmove( pLine->str, pLine->str+1, len ); - } else { - ret = 1; /* read the next segment */ - } - } - break; - /* Mobile H, isotopic, MI */ - case IST_MOBILE_H_ISO_ATOMS: /* i */ - ret = ParseSegmentIsoAtoms( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = IST_MOBILE_H_ISO_EXCH_H; - break; - case IST_MOBILE_H_ISO_EXCH_H: /* /i/h */ - ret = ParseSegmentIsoExchgH( pLine->str, bMobileH, nNumProtons[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = IST_MOBILE_H_ISO_SP2; - break; - case IST_MOBILE_H_ISO_SP2: /* /i/b */ - ret = ParseSegmentSp2( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = IST_MOBILE_H_ISO_SP3; - break; - case IST_MOBILE_H_ISO_SP3: /* /i/t */ - ret = ParseSegmentSp3( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = IST_MOBILE_H_ISO_SP3_M; - break; - case IST_MOBILE_H_ISO_SP3_M: /* /i/m */ - ret = ParseSegmentSp3m( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = IST_MOBILE_H_ISO_SP3_S; - - - break; - case IST_MOBILE_H_ISO_SP3_S: /* /i/s */ - ret = ParseSegmentSp3s( pLine->str, bMobileH, pInpInChI[bReconn], s[bReconn], nNumComponents[bReconn], state ); - state = IST_FIXED_H_LAYER_FORK; - break; - case IST_FIXED_H_LAYER_FORK: - /* find layer type after MI */ - ret = 0; - switch( pLine->str[0] ) { - case 'f': - state = IST_FIXED_H_FORMULA; /* F */ - break; - case 'r': - state = IST_RECONNECTED_FORMULA; /* reconnected */ - break; - default: - ret = RI_ERR_SYNTAX; - } - if ( INCHI_INP_EOL(c) && ret == 0 && !pLine->str[1] ) { - prev_state = state + (bReconn? IST_HAPPENED_IN_RECMET : 0); - ret = RI_ERR_SYNTAX; /* empty layer /f or /r at the end of InChI line */ - } else - if ( !ret ) { - len = strlen( pLine->str ); - if ( len > 1 ) { - memmove( pLine->str, pLine->str+1, len ); - } else { - ret = 1; /* read the next segment */ - } - } - break; - - /* Fixed H, F */ - case IST_FIXED_H_FORMULA: - bMobileH = TAUT_NON; - ret = ParseSegmentFormula( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn] ); - state = IST_FIXED_H; - break; - case IST_FIXED_H: /* /f/h */ - ret = ParseSegmentMobileH( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], &bAbc ); - state = IST_FIXED_H_CHARGE; - break; - case IST_FIXED_H_CHARGE: /* /f/q */ - ret = ParseSegmentCharge( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn] ); - state = IST_FIXED_H_SP2; - break; - case IST_FIXED_H_SP2: /* /f/b */ - ret = ParseSegmentSp2( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = IST_FIXED_H_SP3; - break; - case IST_FIXED_H_SP3: /* /f/t */ - ret = ParseSegmentSp3( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = IST_FIXED_H_SP3_M; - break; - case IST_FIXED_H_SP3_M: /* /f/m */ - ret = ParseSegmentSp3m( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = IST_FIXED_H_SP3_S; - break; - case IST_FIXED_H_SP3_S: /* /f/s */ - ret = ParseSegmentSp3s( pLine->str, bMobileH, pInpInChI[bReconn], s[bReconn], nNumComponents[bReconn], state ); - state = IST_FIXED_H_PERMUTATION; - break; - case IST_FIXED_H_PERMUTATION: /* /f/o */ - ret = ParseSegmentPerm( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = IST_FIXED_H_ISO_LAYER_FORK; - break; - case IST_FIXED_H_ISO_LAYER_FORK: - /* find layer type after M */ - ret = 0; - switch( pLine->str[0] ) { - case 'i': - state = IST_FIXED_H_ISO_ATOMS; /* FI */ - break; - case 'r': - state = IST_RECONNECTED_FORMULA; /* reconnected */ - break; - default: - ret = RI_ERR_SYNTAX; - } - if ( INCHI_INP_EOL(c) && ret == 0 && !pLine->str[1] ) { - prev_state = state + (bReconn? IST_HAPPENED_IN_RECMET : 0); - ret = RI_ERR_SYNTAX; /* empty layer /i or /r at the end of InChI line */ - } else - if ( !ret && state != IST_FIXED_H_ISO_ATOMS ) { - len = strlen( pLine->str ); - if ( len > 1 ) { - memmove( pLine->str, pLine->str+1, len ); - } else { - ret = 1; /* read the next segment */ - } - } - break; - - /* Fixed H, isotopic, FI */ - case IST_FIXED_H_ISO_ATOMS: /* /f/i */ - ret = ParseSegmentIsoAtoms( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = IST_FIXED_H_ISO_SP2; - break; - case IST_FIXED_H_ISO_SP2: /* /f/i/b */ - ret = ParseSegmentSp2( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = IST_FIXED_H_ISO_SP3; - break; - case IST_FIXED_H_ISO_SP3: /* /f/i/t */ - ret = ParseSegmentSp3( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = IST_FIXED_H_ISO_SP3_M; - break; - case IST_FIXED_H_ISO_SP3_M: /* /f/i/m */ - ret = ParseSegmentSp3m( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = IST_FIXED_H_ISO_SP3_S; - break; - case IST_FIXED_H_ISO_SP3_S: /* /f/i/s */ - ret = ParseSegmentSp3s( pLine->str, bMobileH, pInpInChI[bReconn], s[bReconn], nNumComponents[bReconn], state ); - state = IST_FIXED_H_ISO_PERMUTATION; - break; - case IST_FIXED_H_ISO_PERMUTATION: /* /f/i/o */ - ret = ParseSegmentPerm( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = IST_RECONNECTED_LAYER_FORK; - break; - case IST_RECONNECTED_LAYER_FORK: - /* find layer type after FI */ - ret = 0; - switch( pLine->str[0] ) { - case 'r': - state = IST_RECONNECTED_FORMULA; /* reconnected */ - break; - default: - ret = RI_ERR_SYNTAX; - } - if ( INCHI_INP_EOL(c) && ret == 0 && !pLine->str[1] ) { - prev_state = state + (bReconn? IST_HAPPENED_IN_RECMET : 0); - ret = RI_ERR_SYNTAX; /* empty layer /r at the end of InChI line */ - } else - if ( !ret ) { - len = strlen( pLine->str ); - if ( len > 1 ) { - memmove( pLine->str, pLine->str+1, len ); - } else { - ret = 1; /* read the next segment */ - } - } - break; - case IST_RECONNECTED_FORMULA: - bReconn = INCHI_REC; - bMobileH = TAUT_YES; - state = IST_MOBILE_H_FORMULA; - break; - } - - - } while( c >= 0 ); - -exit_function:; -exit_error:; - - INCHI_HEAPCHK - - if ( ret >= 0 || c == RI_ERR_EOF || c == RI_ERR_EOL ) { - pLine->len = 0; - } - return ret; -} -/****************************************************************************************/ -int ParseSegmentIsoExchgH( const char *str, int bMobileH, REM_PROTONS nNumProtons[], int pnNumComponents[], int state, int *pbAbc ) -{ - /* Pass 1: count bonds and find actual numbers of atom */ - const char *p, *q, *pStart, *pEnd; - int ret=0, num, i, i_prev; - static char abc_h[] = "hdt"; - - if ( str[0] != 'h' ) - return 0; - - pStart = str+1; - - if ( !(bMobileH==TAUT_YES && state == IST_MOBILE_H_ISO_EXCH_H ) ) { - return RI_ERR_PROGR; /* program error */ - } - - if ( !(pEnd = strchr( pStart, ';' )) ) { - pEnd = pStart + strlen(pStart); - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p = pStart; - - if ( p < pEnd && *pbAbc == -1 ) { - /* check if compressed InChI */ - /* compressed: /hNtNdNh where N is a decimal number */ - /* uncompressed: /hT[n]D[n]H[n] where n > 1 is a decimal number */ - *pbAbc = isdigit( UCINT *p)? 1 : 0; - } - - if ( *pbAbc == 1 ) { - i_prev = (int)sizeof(abc_h); - while ( p < pEnd ) { - num = (int)inchi_strtol( p, &q, 10 ); - if ( 0 >= num || p == q || q >= pEnd ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p = strchr( abc_h, *q); - if ( p && (i=p-abc_h) < i_prev ) { - nNumProtons[bMobileH].nNumRemovedIsotopicH[i] = (NUM_H)num; - p = q+1; - i_prev = i; - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - } else { - if ( *p == 'T' ) { - nNumProtons[bMobileH].nNumRemovedIsotopicH[2] = 1; - p ++; - if ( isdigit( UCINT p[0]) ) { - nNumProtons[bMobileH].nNumRemovedIsotopicH[2] = (NUM_H)inchi_strtol( p, &q, 10 ); - p = q; - } - } - if ( *p == 'D' ) { - nNumProtons[bMobileH].nNumRemovedIsotopicH[1] = 1; - p ++; - if ( isdigit( UCINT p[0]) ) { - nNumProtons[bMobileH].nNumRemovedIsotopicH[1] = (NUM_H)inchi_strtol( p, &q, 10 ); - p = q; - } - } - if ( *p == 'H' ) { - nNumProtons[bMobileH].nNumRemovedIsotopicH[0] = 1; - p ++; - if ( isdigit( UCINT p[0]) ) { - nNumProtons[bMobileH].nNumRemovedIsotopicH[0] = (NUM_H)inchi_strtol( p, &q, 10 ); - p = q; - } - } - } - if ( p != pEnd ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - ret = 1; - -exit_function: - return ret; - -} -/****************************************************************************************/ -int ParseSegmentPerm( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state, int *pbAbc ) -{ - int nNumComponents, iComponent1, iComponent2, numTrans; - const char *p, *q, *pStart, *pEnd, *pPermStart, *pPermEnd; - int ret=0; - INChI *pInChI = pInpInChI[bMobileH]; /* bMobileH should be TAUT_NON = 0 */ - INChI tmp; - int base = 10; - - - if ( str[0] != 'o' ) - return 0; - - pStart = str+1; - nNumComponents = ppnNumComponents[bMobileH]; - - if ( !(bMobileH==TAUT_NON && ( state == IST_FIXED_H_PERMUTATION || state == IST_FIXED_H_ISO_PERMUTATION) ) ) { - return RI_ERR_PROGR; /* program error */ - } - - if ( !(pEnd = strchr( pStart, ';' )) ) { - pEnd = pStart + strlen(pStart); - } else { - return RI_ERR_SYNTAX; /* syntax error */ - } - while( pStart < pEnd ) { - /* cycle over components; rearrange Fixed H components in order of Mobile H components */ - /* if /o(1,2,3) then reaarange Fixed H components in this way: tmp<-1, 1<-2, 2<-3, 3<-tmp */ - if ( *pStart != '(' ) { - ret = RI_ERR_SYNTAX; - goto exit_function; - } - pPermStart = pStart + 1; - memset( &tmp, 0, sizeof(tmp) ); /* initialization 2006-03 */ - if ( !(pPermEnd = strchr( pPermStart, ')' )) || pPermEnd == pPermStart ) { - ret = RI_ERR_SYNTAX; - goto exit_function; - } - - if ( pPermStart < pPermEnd && *pbAbc == -1 ) { - /* check if compressed InChI */ - *pbAbc = isupper( UCINT *pPermStart)? 1 : 0; - } - base = (*pbAbc==1)? ALPHA_BASE : 10; - - /* permutation cycle */ - if ( *pbAbc == 1 ) { - for ( p = pPermStart, iComponent2 = numTrans = 0; p < pPermEnd; iComponent2 = iComponent1, p = q ) { - /* get first atom number */ - if ( 0 >= (iComponent1 = (int)inchi_strtol( p, &q, base )) || iComponent1 > nNumComponents ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( iComponent2 ) { - pInChI[iComponent2-1] = pInChI[iComponent1-1]; - numTrans ++; - } else { - tmp = pInChI[iComponent1-1]; /* on the 1st pass save Component1 */ - } - } - } else { - for ( p = pPermStart, iComponent2 = numTrans = 0; p < pPermEnd; iComponent2 = iComponent1, p = q + (*q==',') ) { - /* get first atom number */ - if ( !isdigit( UCINT *p ) ) { - ret = RI_ERR_SYNTAX; - goto exit_function; - } - if ( !(iComponent1 = (int)inchi_strtol( p, &q, 10 )) || iComponent1 > nNumComponents ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( iComponent2 ) { - pInChI[iComponent2-1] = pInChI[iComponent1-1]; - numTrans ++; - } else { - tmp = pInChI[iComponent1-1]; /* on the 1st pass save Component1 */ - } - } - } - pInChI[iComponent2-1] = tmp; - if ( !numTrans || p != pPermEnd ) { - ret = RI_ERR_SYNTAX; - goto exit_function; - } else { - pStart = p+1; - } - } - ret = 1; - -exit_function: - return ret; - -} -/****************************************************************************************/ -int ParseSegmentIsoAtoms( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state, int *pbAbc ) -{ - int i, mpy_component, val; - int nNumComponents, iComponent, len, iAtom; - AT_NUMB nAtom1; - const char *p, *q, *t, *pStart, *pEnd, *r; - int ret=0; - INChI *pInChI = pInpInChI[bMobileH]; - INChI *pInChIFrom=NULL; - INChI_IsotopicAtom **pIsotopicAtom = NULL; - INChI_IsotopicAtom isoAtom; - - const char mult_type[] = "mnMNe"; - const char parity_type[] = "-+TDH"; - int bIsoFrom, nCpyType = CPY_ISO_AT; - int base = 10; - - if ( str[0] != 'i' ) - return 0; - - pStart = str+1; - iComponent = 0; - nNumComponents = ppnNumComponents[bMobileH]; - - if ( !(bMobileH==TAUT_YES && state == IST_MOBILE_H_ISO_ATOMS || - bMobileH==TAUT_NON && state == IST_FIXED_H_ISO_ATOMS ) ) { - return RI_ERR_PROGR; /* program error */ - } - if ( !*pStart ) { - return nNumComponents+1; /* no isotopic atoms */ - } - - while( 1 ) { - /* cycle over components */ - if ( !(pEnd = strchr( pStart, ';' )) ) { - pEnd = pStart + strlen(pStart); - } - if ( (p = strchr(pStart, '*')) && p < pEnd ) { - mpy_component = (int)inchi_strtol( pStart, &q, 10 ); - if ( p != q ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } -#if (FIX_DALKE_BUGS == 1) - if ( iComponent + mpy_component > nNumComponents ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } -#endif - p ++; /* move to the 1st character of the component */ - } else - if ( (isdigit(*pStart) && - 0 < (val = (int)inchi_strtol( pStart, &q, 10)) || - (q = pStart, val=1))&& - (t=strchr(mult_type, *q)) && q+1 == pEnd ) { - /* process the abbreviation */ - ret = 0; -#if (FIX_DALKE_BUGS == 1) - if ( iComponent + val > nNumComponents ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } -#endif - bIsoFrom = 0; - switch( bMobileH ) { - case TAUT_YES: - ret = RI_ERR_SYNTAX; - break; - case TAUT_NON: - if ( *q == 'm' ) { - /* copy from mobile H to fixed H */ - pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; - } else - if ( *q == 'e' ) { - /* copy from mobile H to isotopic mobile H */ - pInChIFrom = pInChI; - bIsoFrom = -1; /* empty */ - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - } - break; - default: - ret = RI_ERR_SYNTAX; - break; - } - if ( ret < 0 ) { - goto exit_function; - } - /* copy */ - for ( i = 0; i < val; i ++ ) { - ret = CopySegment( pInChI+iComponent+i, pInChIFrom+iComponent+i, nCpyType, 0, bIsoFrom ); - if ( !ret ) { - ret = RI_ERR_SYNTAX; - } - if ( ret < 0 ) { - goto exit_function; - } - } - iComponent += val; - /* continue to the next component(s) */ - if ( *pEnd ) { - pStart = pEnd+1; - continue; - } else { - break; - } - } else { - mpy_component = 1; - p = pStart; - } - pStart = p; - pIsotopicAtom = &pInChI[iComponent].IsotopicAtom; - if ( *pIsotopicAtom ) { - ret = RI_ERR_PROGR; /* program error */ - goto exit_function; - } - - if ( p < pEnd && *pbAbc == -1 ) { - /* check if compressed InChI */ - *pbAbc = isupper( UCINT *p)? 1 : 0; - } - base = (*pbAbc==1)? ALPHA_BASE : 10; - - -one_more_time: - if ( *pbAbc == 1 ) { - /* process the componnt: At[+/-Charge]TDH,... */ - /* pass 1: find number of stereoatoms */ - for ( p = pStart, iAtom = 0; p < pEnd; iAtom ++ ) { - nAtom1 = (AT_NUMB)inchi_strtol( p, &p, base ); - if ( !nAtom1 || - nAtom1 > pInChI[iComponent].nNumberOfAtoms ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - memset( &isoAtom, 0, sizeof(isoAtom) ); - isoAtom.nAtomNumber = nAtom1; - isoAtom.nIsoDifference = (NUM_H)inchi_strtol( p, &q, 10 ); /* alway in abc */ - if ( p == q ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p = q; - if ( *p == 't' ) { - isoAtom.nNum_T = 1; - p ++; - if ( isdigit( UCINT *p) ) { - isoAtom.nNum_T = (NUM_H)inchi_strtol( p, &q, 10 ); - p = q; - } - } - if ( *p == 'd' ) { - isoAtom.nNum_D = 1; - p ++; - if ( isdigit( UCINT *p) ) { - isoAtom.nNum_D = (NUM_H)inchi_strtol( p, &q, 10 ); - p = q; - } - } - if ( *p == 'h' ) { - isoAtom.nNum_H = 1; - p ++; - if ( isdigit( UCINT *p) ) { - isoAtom.nNum_H = (NUM_H)inchi_strtol( p, &q, 10 ); - p = q; - } - } - if ( p > pEnd || !isoAtom.nIsoDifference && !isoAtom.nNum_T && !isoAtom.nNum_D && !isoAtom.nNum_H ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( *pIsotopicAtom ) { - pIsotopicAtom[0][iAtom] = isoAtom; - } - } - } else { - /* process the componnt: At[+/-Charge]TDH,... */ - /* pass 1: find number of stereoatoms */ - for ( p = pStart, iAtom = 0; p < pEnd; iAtom ++ ) { - nAtom1 = (AT_NUMB)inchi_strtol( p, &q, 10 ); - p = q; - if ( !nAtom1 || - nAtom1 > pInChI[iComponent].nNumberOfAtoms || - !(r = strchr( parity_type, *p) ) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - memset( &isoAtom, 0, sizeof(isoAtom) ); - isoAtom.nAtomNumber = nAtom1; - if ( p[0] == '+' && isdigit( UCINT p[1]) ) { - isoAtom.nIsoDifference = (NUM_H)inchi_strtol( p+1, &q, 10 ); - if ( isoAtom.nIsoDifference >= 0 ) isoAtom.nIsoDifference ++; - p = q; - } else - if ( p[0] == '-' && isdigit( UCINT p[1]) ) { - isoAtom.nIsoDifference = -(NUM_H)inchi_strtol( p+1, &q, 10 ); - if ( isoAtom.nIsoDifference == 0 ) isoAtom.nIsoDifference ++; - p = q; - } - if ( *p == 'T' ) { - isoAtom.nNum_T = 1; - p ++; - if ( isdigit( UCINT *p) ) { - isoAtom.nNum_T = (NUM_H)inchi_strtol( p, &q, 10 ); - p = q; - } - } - if ( *p == 'D' ) { - isoAtom.nNum_D = 1; - p ++; - if ( isdigit( UCINT *p) ) { - isoAtom.nNum_D = (NUM_H)inchi_strtol( p, &q, 10 ); - p = q; - } - } - if ( *p == 'H' ) { - isoAtom.nNum_H = 1; - p ++; - if ( isdigit( UCINT *p) ) { - isoAtom.nNum_H = (NUM_H)inchi_strtol( p, &q, 10 ); - p = q; - } - } - if ( !isoAtom.nIsoDifference && !isoAtom.nNum_T && !isoAtom.nNum_D && !isoAtom.nNum_H ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( p < pEnd ) { - if ( *p == ',' ) { - p ++; - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - if ( *pIsotopicAtom ) { - pIsotopicAtom[0][iAtom] = isoAtom; - } - } - } - if ( p != pEnd ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - - if ( !*pIsotopicAtom ) { - /* end of the 1st pass */ - len = iAtom; - /* memory allocation */ - if ( !(*pIsotopicAtom = (INChI_IsotopicAtom *) inchi_calloc( len+1, sizeof(**pIsotopicAtom) ) ) ) { - ret = RI_ERR_ALLOC; /* memory allocation failed */ - goto exit_function; - } - goto one_more_time; /* goto the 2nd pass */ - } else { - /* 2nd pass */ - if ( len != iAtom ) { - ret = RI_ERR_PROGR; /* program error */ - goto exit_function; - } - pInChI[iComponent].nNumberOfIsotopicAtoms = len; - } - - /* multiplier */ - for ( i = 1; i < mpy_component; i ++ ) { - ret = CopySegment( pInChI+iComponent+i, pInChI+iComponent, nCpyType, 0, 0 ); - if ( !ret ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - } - if ( ret < 0 ) { - goto exit_function; - } - } - - iComponent += mpy_component; - if ( *pEnd ) { - pStart = pEnd+1; - continue; - } else { - break; - } - } - if ( nNumComponents != iComponent ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - ret = iComponent + 1; - -exit_function: - return ret; - -} -/****************************************************************************************/ -int ParseSegmentSp3s( const char *str, int bMobileH, INChI *pInpInChI[], int s[TAUT_NUM][2], int ppnNumComponents[], int state ) -{ - /* Pass 1: count bonds and find actual numbers of atom */ - int nNumComponents, iComponent, val; - const char *p, *q, *pStart, *pEnd; - int ret=0; - INChI *pInChI = pInpInChI[bMobileH]; - INChI_Stereo **pStereo = NULL; - - int bIso = (state==IST_MOBILE_H_ISO_SP3_S || state==IST_FIXED_H_ISO_SP3_S); - - if ( !bIso && state != IST_MOBILE_H_SP3_S && state != IST_FIXED_H_SP3_S ) { - return RI_ERR_PROGR; /* program error */ - } - - if ( str[0] != 's' ) - return 0; - - pStart = str+1; - iComponent = 0; - nNumComponents = ppnNumComponents[bMobileH]; - - /*if ( !(pEnd = strchr( pStart, ';' )) )*/ /* 2007-09-25 DT */ - if ( !(pEnd = strchr( pStart, '/' )) ){ - pEnd = pStart + strlen(pStart); - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p = pStart; - if ( pEnd == pStart ) { - /* create empty sp3 segment */ - int len = 0; - s[bMobileH][bIso] = NO_VALUE_INT; /* empty */ - /* create empty sp3 segment */ - for ( iComponent = 0; iComponent < nNumComponents; iComponent ++ ) { - pStereo = bIso? &pInChI[iComponent].StereoIsotopic : &pInChI[iComponent].Stereo; - if ( !*pStereo ) { - if ( !(*pStereo = (INChI_Stereo *) inchi_calloc( 1, sizeof(**pStereo) ) ) ) { - ret = RI_ERR_ALLOC; /* memory allocation failed */ - goto exit_function; - } - } - pStereo[0]->nCompInv2Abs = 0; /* deliberately empty */ - - if ( pStereo[0]->nNumberOfStereoCenters ) { - ret = RI_ERR_SYNTAX; /* syntax error: "/s" without a digit describes "no stereo" */ - goto exit_function; - } - /* allocate empty sp3 stereo */ - if ( !pStereo[0]->t_parity && - !(pStereo[0]->t_parity = (S_CHAR *)inchi_calloc( len+1, sizeof(pStereo[0]->b_parity[0]) ) ) || - !pStereo[0]->nNumber && - !(pStereo[0]->nNumber = (AT_NUMB *)inchi_calloc( len+1, sizeof(pStereo[0]->nNumber[0]) ) ) ) { - /* cleanup */ - if ( pStereo[0]->t_parity ) { - INCHI_HEAPCHK - inchi_free( pStereo[0]->t_parity ); - pStereo[0]->t_parity = NULL; - } - if ( pStereo[0]->nNumber ) { - INCHI_HEAPCHK - inchi_free( pStereo[0]->nNumber ); - pStereo[0]->nNumber = NULL; - } - ret = RI_ERR_ALLOC; /* memory allocation failed */ - goto exit_function; - } - } - ret = nNumComponents+1; - } else { - val = (int)inchi_strtol( p, &q, 10 ); - if ( q == pEnd && 1 <= val && val <= 3 ) { - s[bMobileH][bIso] = val; - ret = nNumComponents+1; - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - } - } -exit_function: - return ret; -} -/****************************************************************************************/ -int bIsSp3LayerNotEmpty( INChI *pInpInChI[], int bMobileH, int bIso, int nNumComponents ) -{ - INChI *pInChI; - INChI_Stereo *pStereo; - int iComponent, num_not_empty = 0; - - if ( pInpInChI[bMobileH] ) { - for ( iComponent = 0; iComponent < nNumComponents; iComponent ++ ) { - pInChI = pInpInChI[bMobileH] + iComponent; - if ( pInChI->bDeleted || !pInChI->nNumberOfAtoms ) { - continue; - } - pStereo = bIso? pInChI->StereoIsotopic : pInChI->Stereo; - if ( pStereo && pStereo->nNumberOfStereoCenters > 0 && pStereo->nNumber && pStereo->t_parity ) { - num_not_empty ++; - } - } - } - return num_not_empty; -} -/****************************************************************************************/ -int ParseSegmentSp3m( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) -{ - /* Pass 1: count bonds and find actual numbers of atom */ - int nNumComponents, iComponent; - const char *p, *pStart, *pEnd; - int ret=0; - INChI *pInChI = pInpInChI[bMobileH]; - INChI_Stereo **pStereo = NULL; - - int bIso = (state==IST_MOBILE_H_ISO_SP3_M || state==IST_FIXED_H_ISO_SP3_M); - - if ( !bIso && state != IST_MOBILE_H_SP3_M && state != IST_FIXED_H_SP3_M ) { - return RI_ERR_PROGR; /* program error */ - } - nNumComponents = ppnNumComponents[bMobileH]; - - if ( str[0] != 'm' ) { - /* /m is missing: check whether we have to inherit /m from a preceding stereo layer */ - INChI_Stereo *pStereoFrom, *pStereoTo; - INChI *pInChIFrom; - int nNumCopied = 0, bMobileHFrom=-1, bIsoFrom=-1; - if ( bMobileH && !bIso ) { - return 0; /* Main non-isotopic cannot inherit: it has no preceding layer */ - } else - if ( !bMobileH && !bIso ) { - /* fixed-H non-isotopic (F) inherits from Mobile-H non-isotopic (M) */ - bMobileHFrom = TAUT_YES; - bIsoFrom = 0; - } else - if ( bMobileH && bIso ) { - /* Mobile-H isotopic (MI) inherits from Mobile-H non-isotopic (M) */ - bMobileHFrom = TAUT_YES; - bIsoFrom = 0; - } else - if ( !bMobileH && bIso ) { - /* Fixed-H isotopic (FI) inherits from Fixed-H non-isotopic (F) */ - bMobileHFrom = TAUT_NON; - bIsoFrom = 0; - /* if Sp3 is empty in F as well as in M, then inherit from MI */ - if ( !bIsSp3LayerNotEmpty( pInpInChI, TAUT_NON, 0, ppnNumComponents[TAUT_NON /*bMobileH*/] ) /* F */ && - !bIsSp3LayerNotEmpty( pInpInChI, TAUT_YES, 0, ppnNumComponents[TAUT_YES /*bMobileH*/] ) /* M */ ) { - bMobileHFrom = TAUT_YES; - bIsoFrom = 1; - } - } - if ( bMobileHFrom < 0 || bIsoFrom < 0 ) { - return RI_ERR_PROGR; - } - if ( !bIsSp3LayerNotEmpty( pInpInChI, bMobileHFrom, bIsoFrom, ppnNumComponents[/*bMobileH*/ bMobileHFrom] ) ) { - /* nothing to copy; check whether it should have inherited from a preceding layer */ - if ( !bMobileHFrom && bIsoFrom || bMobileHFrom && !bIsoFrom ) { - /* MI or F inherit stereo from M */ - bMobileHFrom = TAUT_YES; - bIsoFrom = 0; - if ( !bIsSp3LayerNotEmpty( pInpInChI, bMobileHFrom, bIsoFrom, ppnNumComponents[bMobileHFrom /*bMobileH*/] ) ) { - return 0; - } - } else { - return 0; - } - } - nNumComponents = inchi_min( ppnNumComponents[bMobileH], ppnNumComponents[bMobileHFrom] ); - for ( iComponent = 0; iComponent < nNumComponents; iComponent ++ ) { - pInChIFrom = pInpInChI[bMobileHFrom] + iComponent; - pInChI = pInpInChI[bMobileH] + iComponent; - if ( pInChIFrom->nNumberOfAtoms > 0 && !pInChIFrom->bDeleted && - pInChI->nNumberOfAtoms > 0 && !pInChI->bDeleted ) { - pStereoFrom = bIsoFrom? pInChIFrom->StereoIsotopic : pInChIFrom->Stereo; - pStereoTo = bIso? pInChI->StereoIsotopic : pInChI->Stereo; - if ( pStereoFrom && pStereoTo ) { - pStereoTo->nCompInv2Abs = pStereoFrom->nCompInv2Abs; - nNumCopied ++; - } - } - } - return 0; /* return value > 0 means the non-/m segment has been processed here */ - } - - pStart = str+1; - iComponent = 0; - - /*if ( !(pEnd = strchr( pStart, ';' )) )*/ /* 2007-09-25 DT */ - if ( !(pEnd = strchr( pStart, '/' )) ) { - pEnd = pStart + strlen(pStart); - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p = pStart; - if ( pEnd == pStart ) { - /* create empty sp3 segment */ - int len = 0; - for ( iComponent = 0; iComponent < nNumComponents; iComponent ++ ) { - INChI *pIsoInChI = &pInChI[iComponent]; - pStereo = bIso? &pIsoInChI->StereoIsotopic : &pIsoInChI->Stereo; - if ( !*pStereo ) { - if ( !(*pStereo = (INChI_Stereo *) inchi_calloc( 1, sizeof(**pStereo) ) ) ) { - ret = RI_ERR_ALLOC; /* memory allocation failed */ - goto exit_function; - } - } - pStereo[0]->nCompInv2Abs = NO_VALUE_INT; /* deliberately empty */ -#ifdef NEVER - if ( pStereo[0]->nNumberOfStereoCenters ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } -#endif - /* allocate empty sp3 stereo */ - if ( !pStereo[0]->t_parity && - !(pStereo[0]->t_parity = (S_CHAR *)inchi_calloc( len+1, sizeof(pStereo[0]->b_parity[0]) ) ) || - !pStereo[0]->nNumber && - !(pStereo[0]->nNumber = (AT_NUMB *)inchi_calloc( len+1, sizeof(pStereo[0]->nNumber[0]) ) ) ) { - /* cleanup */ - if ( pStereo[0]->t_parity ) { - INCHI_HEAPCHK - inchi_free( pStereo[0]->t_parity ); - pStereo[0]->t_parity = NULL; - } - if ( pStereo[0]->nNumber ) { - INCHI_HEAPCHK - inchi_free( pStereo[0]->nNumber ); - pStereo[0]->nNumber = NULL; - } - ret = RI_ERR_ALLOC; /* memory allocation failed */ - goto exit_function; - } - } - ret = nNumComponents+1; - } else { - while( p < pEnd && iComponent < nNumComponents ) { - /* cycle over components */ - pStereo = bIso? &pInChI[iComponent].StereoIsotopic : &pInChI[iComponent].Stereo; - if ( *p != '.' && !*pStereo ) { - if ( !(*pStereo = (INChI_Stereo *) inchi_calloc( 1, sizeof(**pStereo) ) ) ) { - ret = RI_ERR_ALLOC; /* memory allocation failed */ - goto exit_function; - } - } - switch( *p ) { - case '1': - pStereo[0]->nCompInv2Abs = -1; - break; - case '0': - pStereo[0]->nCompInv2Abs = 1; - break; - case '.': - if ( *pStereo ) { - pStereo[0]->nCompInv2Abs = 0; - } - break; - default: - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - iComponent ++; - p ++; - } - if ( p != pEnd || iComponent != nNumComponents ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - ret = nNumComponents+1; - } -exit_function: - return ret; -} -/****************************************************************************************/ -int ParseSegmentSp3( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state, int *pbAbc ) -{ - /* Pass 1: count bonds and find actual numbers of atom */ - int i, mpy_component, val; - int nNumComponents, iComponent, len, iAtom; - AT_NUMB nAtom1; - int atomParity; - const char *p, *q, *t, *pStart, *pEnd, *r; - int ret=0; - INChI *pInChI = pInpInChI[bMobileH]; - INChI *pInChIFrom=NULL; - /* - INChI_Stereo *Stereo = NULL; - INChI_Stereo *StereoOther = NULL; - */ - INChI_Stereo **pStereo = NULL; - - const char mult_type[] = "mnMNe"; - const char parity_type[] = "-+u?"; - int bIsoTo, bIsoFrom, nCpyType = CPY_SP3; - int bIso = (state==IST_MOBILE_H_ISO_SP3 || state==IST_FIXED_H_ISO_SP3); - int base = 10; - - if ( !bIso && state != IST_MOBILE_H_SP3 && state != IST_FIXED_H_SP3 ) { - return RI_ERR_PROGR; /* program error */ - } - - if ( str[0] != 't' ) - return 0; - - pStart = str+1; - iComponent = 0; - nNumComponents = ppnNumComponents[bMobileH]; - - if ( !*pStart ) { - /* create empty sp3 segment */ - int len0 = 0; - for ( iComponent = 0; iComponent < nNumComponents; iComponent ++ ) { - INChI *pIsoInChI = &pInChI[iComponent]; - pStereo = bIso? &pIsoInChI->StereoIsotopic : &pIsoInChI->Stereo; - if ( !*pStereo ) { - if ( !(*pStereo = (INChI_Stereo *) inchi_calloc( 1, sizeof(**pStereo) ) ) ) { - ret = RI_ERR_ALLOC; /* memory allocation failed */ - goto exit_function; - } - } - /* allocate empty sp3 stereo */ - if ( !pStereo[0]->b_parity && - !(pStereo[0]->b_parity = (S_CHAR *)inchi_calloc( len0+1, sizeof(pStereo[0]->b_parity[0]) ) ) || - !pStereo[0]->nBondAtom1 && - !(pStereo[0]->nBondAtom1 = (AT_NUMB *)inchi_calloc( len0+1, sizeof(pStereo[0]->nBondAtom1[0]) ) ) || - !pStereo[0]->nBondAtom2 && - !(pStereo[0]->nBondAtom2 = (AT_NUMB *)inchi_calloc( len0+1, sizeof(pStereo[0]->nBondAtom2[0]) ) ) ) { - /* cleanup */ - if ( pStereo[0]->b_parity ) { - INCHI_HEAPCHK - inchi_free( pStereo[0]->b_parity ); - pStereo[0]->b_parity = NULL; - } - if ( pStereo[0]->nBondAtom1 ) { - INCHI_HEAPCHK - inchi_free( pStereo[0]->nBondAtom1 ); - pStereo[0]->nBondAtom1 = NULL; - } - if ( pStereo[0]->nBondAtom2 ) { - INCHI_HEAPCHK - inchi_free( pStereo[0]->nBondAtom2 ); - pStereo[0]->nBondAtom2 = NULL; - } - ret = RI_ERR_ALLOC; /* memory allocation failed */ - goto exit_function; - } - pStereo[0]->nCompInv2Abs = NO_VALUE_INT; - } - ret = nNumComponents+1; - goto exit_function; - } - - while( 1 ) { - /* cycle over components */ - if ( !(pEnd = strchr( pStart, ';' )) ) { - pEnd = pStart + strlen(pStart); - } - if ( (isdigit(*pStart) && - 0 < (val = (int)inchi_strtol( pStart, &q, 10)) || - (q = pStart, val=1))&& - (t=strchr(mult_type, *q)) && q+1 == pEnd ) { - /* process the abbreviation */ - ret = 0; -#if (FIX_DALKE_BUGS == 1) - if ( iComponent + val > nNumComponents ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } -#endif - switch( bMobileH ) { - case TAUT_YES: - switch( state ) { - case IST_MOBILE_H_ISO_SP3: - if ( *q == 'm' ) { - /* copy from mobile H to isotopic mobile H */ - pInChIFrom = pInChI; - bIsoTo = 1; - bIsoFrom = 0; - } else - if ( *q == 'e' ) { - /* copy from mobile H to isotopic mobile H */ - pInChIFrom = pInChI; - bIsoTo = 1; - bIsoFrom = -1; /* empty */ - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - } - break; - default: - ret = RI_ERR_SYNTAX; - break; - } - break; - case TAUT_NON: - switch( state ) { - case IST_FIXED_H_SP3: - if ( *q == 'm' ) { - /* copy from mobile H to fixed H */ - pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; - bIsoTo = 0; - bIsoFrom = 0; - } else - if ( *q == 'e' ) { - /* copy from mobile H to isotopic mobile H */ - pInChIFrom = pInChI; - bIsoTo = 1; - bIsoFrom = -1; /* empty */ - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - } - break; - case IST_FIXED_H_ISO_SP3: - if ( *q == 'm' ) { - /* copy from mobile H to fixed isotopic H */ - pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; - bIsoTo = 1; - bIsoFrom = 0; - } else - if ( *q == 'M' ) { - /* copy from isotopic mobile H to fixed isotopic H */ - pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; - bIsoTo = 1; - bIsoFrom = 1; - } else - if ( *q == 'n' ) { - /* copy from fixed H to fixed isotopic H */ - pInChIFrom = pInChI; - bIsoTo = 1; - bIsoFrom = 0; - } else - if ( *q == 'e' ) { - /* copy from mobile H to isotopic mobile H */ - pInChIFrom = pInChI; - bIsoTo = 1; - bIsoFrom = -1; /* empty */ - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - } - break; - default: - ret = RI_ERR_SYNTAX; - break; - } - break; - - default: - ret = RI_ERR_SYNTAX; - break; - - } - if ( ret < 0 ) { - goto exit_function; - } - /* copy */ - for ( i = 0; i < val; i ++ ) { - ret = CopySegment( pInChI+iComponent+i, pInChIFrom+iComponent+i, nCpyType, bIsoTo, bIsoFrom ); - if ( !ret ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - } - if ( ret < 0 ) { - goto exit_function; - } - if ( bIsoFrom >= 0 ) { - INChI_Stereo *pStereoTo = bIsoTo? pInChI[iComponent+i].StereoIsotopic : pInChI[iComponent+i].Stereo; - if ( pStereoTo ) { - pStereoTo->nCompInv2Abs = NO_VALUE_INT; /* in case there in no /m segment after this */ - } - } - } - - mpy_component = val; - goto end_main_cycle; - - } else - /* regular multiplier */ - if ( (p = strchr(pStart, '*')) && p < pEnd ) { - mpy_component = (int)inchi_strtol( pStart, &q, 10 ); - if ( p != q ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p ++; /* move to the 1st character of the component */ - } else { - mpy_component = 1; - p = pStart; - } -#if (FIX_DALKE_BUGS == 1) - if ( iComponent + mpy_component > nNumComponents ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } -#endif - pStart = p; - if ( p < pEnd && *pbAbc == -1 ) { - /* check if compressed InChI */ - *pbAbc = isupper( UCINT *p)? 1 : 0; - } - base = (*pbAbc==1)? ALPHA_BASE : 10; - /* process the componnt: at1p,at1p,... */ - /* pass 1: find number of stereoatoms */ - if ( *pbAbc == 1 ) { - for ( p = pStart, iAtom = 0; p < pEnd; iAtom ++ ) { - if ( (nAtom1 = (AT_NUMB)inchi_strtol( p, &p, base ) ) && - (atomParity = (int)inchi_strtol( p, &p, 10), - AB_MIN_KNOWN_PARITY <= atomParity && atomParity <= AB_MAX_KNOWN_PARITY) ) { - ; /* okay */ - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( nAtom1 > pInChI[iComponent].nNumberOfAtoms ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - } else { - for ( p = pStart, iAtom = 0; p < pEnd; iAtom ++, p += (*p == ',') ) { - nAtom1 = (AT_NUMB)inchi_strtol( p, &q, 10 ); - p = q+1; - if ( !nAtom1 || - nAtom1 > pInChI[iComponent].nNumberOfAtoms || - !(r = strchr( parity_type, *q) ) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - } - if ( p != pEnd ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - len = iAtom; - - /* memory allocation */ - - pStereo = bIso? &pInChI[iComponent].StereoIsotopic : &pInChI[iComponent].Stereo; - - if ( !*pStereo ) { - if ( !(*pStereo = (INChI_Stereo *) inchi_calloc( 1, sizeof(**pStereo) ) ) ) { - ret = RI_ERR_ALLOC; /* memory allocation failed */ - goto exit_function; - } - } - if ( pStereo[0]->t_parity || pStereo[0]->nNumberOfStereoCenters || - pStereo[0]->nNumber ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - /* allocate sp3 stereo */ - if ( !(pStereo[0]->t_parity = (S_CHAR *)inchi_calloc( len+1, sizeof(pStereo[0]->b_parity[0]) ) ) || - !(pStereo[0]->nNumber = (AT_NUMB *)inchi_calloc( len+1, sizeof(pStereo[0]->nNumber[0]) ) ) ) { - /* cleanup */ - if ( pStereo[0]->t_parity ) { - INCHI_HEAPCHK - inchi_free( pStereo[0]->t_parity ); - pStereo[0]->t_parity = NULL; - } - if ( pStereo[0]->nNumber ) { - INCHI_HEAPCHK - inchi_free( pStereo[0]->nNumber ); - pStereo[0]->nNumber = NULL; - } - ret = RI_ERR_ALLOC; /* memory allocation failed */ - goto exit_function; - } - - /* pass 2: store stereocenters */ - if ( *pbAbc == 1 ) { - for ( p = pStart, iAtom = 0; p < pEnd; iAtom ++ ) { - if ( (nAtom1 = (AT_NUMB)inchi_strtol( p, &p, base ) ) && - (atomParity = (int)inchi_strtol( p, &p, 10), - AB_MIN_KNOWN_PARITY <= atomParity && atomParity <= AB_MAX_KNOWN_PARITY) ) { - ; /* okay */ - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( nAtom1 > pInChI[iComponent].nNumberOfAtoms ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - pStereo[0]->t_parity[iAtom] = atomParity; - pStereo[0]->nNumber[iAtom] = nAtom1; - if ( iAtom && !(pStereo[0]->nNumber[iAtom-1] < nAtom1) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - } else { - for ( p = pStart, iAtom = 0; p < pEnd; iAtom ++, p += (*p==',') ) { - nAtom1 = (AT_NUMB)inchi_strtol( p, &q, 10 ); - if ( !(r = strchr( parity_type, *q) ) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p = q+1; - atomParity = (r - parity_type) + 1; - pStereo[0]->t_parity[iAtom] = atomParity; - pStereo[0]->nNumber[iAtom] = nAtom1; - if ( iAtom && !(pStereo[0]->nNumber[iAtom-1] < nAtom1) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - } - pStereo[0]->nNumberOfStereoCenters = iAtom; - /*if ( iAtom ) {*/ - pStereo[0]->nCompInv2Abs = NO_VALUE_INT; /* unknown yet */ - /*}*/ - - if ( p != pEnd ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - - /* multiplier */ - for ( i = 1; i < mpy_component; i ++ ) { - ret = CopySegment( pInChI+iComponent+i, pInChI+iComponent, nCpyType, bIso, bIso ); - if ( !ret ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - } - if ( ret < 0 ) { - goto exit_function; - } - ret = CopySegment( pInChI+iComponent+i, pInChI+iComponent, CPY_SP3_M, bIso, bIso ); - if ( !ret ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - } - if ( ret < 0 ) { - goto exit_function; - } - } - -end_main_cycle: - iComponent += mpy_component; - if ( *pEnd ) { - pStart = pEnd+1; - continue; - } else { - break; - } - } - if ( nNumComponents != iComponent ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - ret = iComponent + 1; - -exit_function: - return ret; - -} -/****************************************************************************************/ -int ParseSegmentSp2( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state, int *pbAbc ) -{ - /* Pass 1: count bonds and find actual numbers of atom */ - int i, mpy_component, val; - int nNumComponents, iComponent, len, iBond; - AT_NUMB nAtom1, nAtom2; - int bondParity; - const char *p, *q, *t, *pStart, *pEnd, *r; - int ret=0; - INChI *pInChI = pInpInChI[bMobileH]; - INChI *pInChIFrom=NULL; - /* - INChI_Stereo *Stereo = NULL; - INChI_Stereo *StereoOther = NULL; - */ - INChI_Stereo **pStereo = NULL; - - const char mult_type[] = "mnMNe"; - const char parity_type[] = "-+u?"; - int bIsoTo, bIsoFrom, nCpyType = CPY_SP2; - int bIso = (state==IST_MOBILE_H_ISO_SP2 || state==IST_FIXED_H_ISO_SP2); - int base = 10; - - if ( !bIso && state != IST_MOBILE_H_SP2 && state != IST_FIXED_H_SP2 ) { - return RI_ERR_PROGR; /* program error */ - } - - if ( str[0] != 'b' ) - return 0; - - pStart = str+1; - iComponent = 0; - nNumComponents = ppnNumComponents[bMobileH]; - - if ( !*pStart ) { - /* creaste empty sp3 segment which means no sp3 */ - for ( iComponent = 0; iComponent < nNumComponents; iComponent ++ ) { - INChI *pIsoInChI = &pInChI[iComponent]; - pStereo = bIso? &pIsoInChI->StereoIsotopic : &pIsoInChI->Stereo; - if ( *pStereo && (pStereo[0]->b_parity || pStereo[0]->nNumberOfStereoBonds || - pStereo[0]->nBondAtom1 || pStereo[0]->nBondAtom2 ) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - /* allocate empty sp3 stereo */ - ret = CopySegment( pIsoInChI, NULL, CPY_SP2, bIso, -1); - if ( ret < 0 ) { - goto exit_function; - } - } - ret = nNumComponents+1; - goto exit_function; - } - - while( 1 ) { - - /* cycle over components */ - if ( !(pEnd = strchr( pStart, ';' )) ) { - pEnd = pStart + strlen(pStart); - } - - if ( (isdigit(*pStart) && - 0 < (val = (int)inchi_strtol( pStart, &q, 10)) || - (q = pStart, val=1))&& - (t=strchr(mult_type, *q)) && q+1 == pEnd ) { - /* process the abbreviation */ - ret = 0; -#if (FIX_DALKE_BUGS == 1) - if ( iComponent + val > nNumComponents ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } -#endif - switch( bMobileH ) { - case TAUT_YES: - switch( state ) { - case IST_MOBILE_H_ISO_SP2: - if ( *q == 'm' ) { - /* copy from mobile H to isotopic mobile H */ - pInChIFrom = pInChI; - bIsoTo = 1; - bIsoFrom = 0; - } else - if ( *q == 'e' ) { - /* copy from mobile H to isotopic mobile H */ - pInChIFrom = pInChI; - bIsoTo = 1; - bIsoFrom = -1; /* empty */ - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - } - break; - default: - ret = RI_ERR_SYNTAX; - break; - } - break; - case TAUT_NON: - switch( state ) { - case IST_FIXED_H_SP2: - if ( *q == 'm' ) { - /* copy from mobile H to fixed H */ - pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; - bIsoTo = 0; - bIsoFrom = 0; - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - } - break; - case IST_FIXED_H_ISO_SP2: - if ( *q == 'm' ) { - /* copy from mobile H to fixed isotopic H */ - pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; - bIsoTo = 1; - bIsoFrom = 0; - } else - if ( *q == 'M' ) { - /* copy from isotopic mobile H to fixed isotopic H */ - pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; - bIsoTo = 1; - bIsoFrom = 1; - } else - if ( *q == 'n' ) { - /* copy from fixed H to fixed isotopic H */ - pInChIFrom = pInChI; - bIsoTo = 1; - bIsoFrom = 0; - } else - if ( *q == 'e' ) { - /* copy from mobile H to isotopic mobile H */ - pInChIFrom = pInChI; - bIsoTo = 1; - bIsoFrom = -1; /* empty */ - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - } - break; - default: - ret = RI_ERR_SYNTAX; - break; - } - break; - - default: - ret = RI_ERR_SYNTAX; - break; - - } - if ( ret < 0 ) { - goto exit_function; - } - /* copy */ - for ( i = 0; i < val; i ++ ) { - ret = CopySegment( pInChI+iComponent+i, pInChIFrom+iComponent+i, nCpyType, bIsoTo, bIsoFrom ); - if ( !ret ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - } - if ( ret < 0 ) { - goto exit_function; - } - } - mpy_component = val; - goto end_main_cycle; - - } else - /* regular multiplier */ - if ( (p = strchr(pStart, '*')) && p < pEnd ) { - mpy_component = (int)inchi_strtol( pStart, &q, 10 ); - if ( p != q ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - - p ++; /* move to the 1st character of the component */ - } else { - mpy_component = 1; - p = pStart; - } -#if (FIX_DALKE_BUGS == 1) - if ( iComponent + mpy_component > nNumComponents ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } -#endif - pStart = p; - if ( p < pEnd && *pbAbc == -1 ) { - /* check if compressed InChI */ - *pbAbc = isupper( UCINT *p)? 1 : 0; - } - base = (*pbAbc==1)? ALPHA_BASE : 10; - if ( *pbAbc == 1 ) { - /* process the componnt: at1-at2p,at1-at2p,... */ - /* pass 1: find number of stereobonds */ - for ( p = pStart, iBond = 0; p < pEnd; iBond ++ ) { - /* atoms 1, 2, and parity */ - if ( (nAtom1 = (AT_NUMB)inchi_strtol( p, &p, base ) ) && - (nAtom2 = (AT_NUMB)inchi_strtol( p, &p, base ) ) && - (bondParity = (int)inchi_strtol( p, &p, 10), - AB_MIN_KNOWN_PARITY <= bondParity && bondParity <= AB_MAX_KNOWN_PARITY) ) { - ; /* okay */ - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( nAtom1 <= nAtom2 || - nAtom1 > pInChI[iComponent].nNumberOfAtoms ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - } else { - /* process the componnt: at1-at2p,at1-at2p,... */ - /* pass 1: find number of stereobonds */ - for ( p = pStart, iBond = 0; p < pEnd; iBond ++, p += (*p==',') ) { - nAtom1 = (AT_NUMB)inchi_strtol( p, &q, 10 ); - if ( *q != '-' ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p = q+1; - nAtom2 = (AT_NUMB)inchi_strtol( p, &q, 10 ); - if ( !nAtom1 || !nAtom2 || - nAtom1 <= nAtom2 || - nAtom1 > pInChI[iComponent].nNumberOfAtoms || - !(r = strchr( parity_type, *q) ) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p = q+1; - } - } - - if ( p != pEnd ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - len = iBond; - - /* memory allocation */ - - pStereo = bIso? &pInChI[iComponent].StereoIsotopic : &pInChI[iComponent].Stereo; - - if ( !*pStereo ) { - if ( !(*pStereo = (INChI_Stereo *) inchi_calloc( 1, sizeof(**pStereo) ) ) ) { - ret = RI_ERR_ALLOC; /* memory allocation failed */ - goto exit_function; - } - } - if ( pStereo[0]->b_parity || pStereo[0]->nNumberOfStereoBonds || - pStereo[0]->nBondAtom1 || pStereo[0]->nBondAtom2 ) { - ret = RI_ERR_SYNTAX; /* syntax error: bonds have already been allocated */ - goto exit_function; - } - /* allocate sp2 stereo */ - if ( !(pStereo[0]->b_parity = (S_CHAR *)inchi_calloc( len+1, sizeof(pStereo[0]->b_parity[0]) ) ) || - !(pStereo[0]->nBondAtom1 = (AT_NUMB *)inchi_calloc( len+1, sizeof(pStereo[0]->nBondAtom1[0]) ) ) || - !(pStereo[0]->nBondAtom2 = (AT_NUMB *)inchi_calloc( len+1, sizeof(pStereo[0]->nBondAtom2[0]) ) ) ) { - /* cleanup */ - if ( pStereo[0]->b_parity ) { - INCHI_HEAPCHK - inchi_free( pStereo[0]->b_parity ); - pStereo[0]->b_parity = NULL; - } - if ( pStereo[0]->nBondAtom1 ) { - INCHI_HEAPCHK - inchi_free( pStereo[0]->nBondAtom1 ); - pStereo[0]->nBondAtom1 = NULL; - } - if ( pStereo[0]->nBondAtom2 ) { - INCHI_HEAPCHK - inchi_free( pStereo[0]->nBondAtom2 ); - pStereo[0]->nBondAtom2 = NULL; - } - INCHI_HEAPCHK - ret = RI_ERR_ALLOC; /* memory allocation failed */ - goto exit_function; - } - - /* pass 2: store stereobonds */ - if ( *pbAbc == 1 ) { - for ( p = pStart, iBond = 0; p < pEnd; iBond ++ ) { - if ( (nAtom1 = (AT_NUMB)inchi_strtol( p, &p, base ) ) && - (nAtom2 = (AT_NUMB)inchi_strtol( p, &p, base ) ) && - (bondParity = (int)inchi_strtol( p, &p, 10), - AB_MIN_KNOWN_PARITY <= bondParity && bondParity <= AB_MAX_KNOWN_PARITY) ) { - ; /* okay */ - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - pStereo[0]->b_parity[iBond] = bondParity; - pStereo[0]->nBondAtom1[iBond] = nAtom1; - pStereo[0]->nBondAtom2[iBond] = nAtom2; - - if ( iBond && - !(pStereo[0]->nBondAtom1[iBond-1] < nAtom1 || - pStereo[0]->nBondAtom1[iBond-1] == nAtom1 && - pStereo[0]->nBondAtom2[iBond-1] < nAtom2 ) ) { - ret = RI_ERR_SYNTAX; /* syntax error: wrong bond order */ - goto exit_function; - } - } - } else { - for ( p = pStart, iBond = 0; p < pEnd; iBond ++, p += (*p==',') ) { - nAtom1 = (AT_NUMB)inchi_strtol( p, &q, 10 ); - if ( *q != '-' ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p = q+1; - nAtom2 = (AT_NUMB)inchi_strtol( p, &q, 10 ); - if ( !(r = strchr( parity_type, *q) ) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p = q+1; - bondParity = (r - parity_type) + 1; - pStereo[0]->b_parity[iBond] = bondParity; - pStereo[0]->nBondAtom1[iBond] = nAtom1; - pStereo[0]->nBondAtom2[iBond] = nAtom2; - - if ( iBond && - !(pStereo[0]->nBondAtom1[iBond-1] < nAtom1 || - pStereo[0]->nBondAtom1[iBond-1] == nAtom1 && - pStereo[0]->nBondAtom2[iBond-1] < nAtom2 ) ) { - ret = RI_ERR_SYNTAX; /* syntax error: wrong bond order */ - goto exit_function; - } - } - } - pStereo[0]->nNumberOfStereoBonds = iBond; - - if ( p != pEnd ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - - /* multiplier */ - for ( i = 1; i < mpy_component; i ++ ) { - ret = CopySegment( pInChI+iComponent+i, pInChI+iComponent, nCpyType, bIso, bIso ); - if ( ret < 0 ) { - goto exit_function; - } - } - -end_main_cycle: - iComponent += mpy_component; - if ( *pEnd ) { - pStart = pEnd+1; - continue; - } else { - break; - } - } - if ( nNumComponents != iComponent ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - ret = iComponent + 1; - -exit_function: - return ret; - -} -/****************************************************************************************/ -int ParseSegmentProtons( const char *str, int bMobileH, REM_PROTONS nNumProtons[], int ppnNumComponents[] ) -{ - /* Pass 1: count bonds and find actual numbers of atom */ - int val; - const char *q, *pStart, *pEnd; - int ret; - - if ( str[0] != 'p' ) - return 0; - - pStart = str+1; - - while( 1 ) { - /* cycle over components */ - if ( !(pEnd = strchr( pStart, ';' )) ) { - pEnd = pStart + strlen(pStart); - } - - if ( pStart[0] == '+' && isdigit( UCINT pStart[1] ) ) { - val = (int)inchi_strtol( pStart+1, &q, 10 ); - } else - if ( pStart[0] == '-' && isdigit( UCINT pStart[1] ) ) { - val = -(int)inchi_strtol( pStart+1, &q, 10 ); - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( !val ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - nNumProtons[bMobileH].nNumRemovedProtons = val; - if ( *pEnd || q != pEnd ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } else { - break; - } - } - ret = 1; - -exit_function: - return ret; - -} -/****************************************************************************************/ -int ParseSegmentCharge( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[] ) -{ - /* Pass 1: count bonds and find actual numbers of atom */ - int i, mpy_component, val; - int nNumComponents, iComponent; - const char *p, *q, *t, *pStart, *pEnd; - int ret; - INChI *pInChI = pInpInChI[bMobileH]; - const char mult_type[] = "mnMNe"; - - if ( str[0] != 'q' ) { - return 0; - } - - pStart = str+1; - iComponent = 0; - nNumComponents = ppnNumComponents[bMobileH]; - - if ( !*pStart && bMobileH == TAUT_NON ) { - for ( i = 0; i < nNumComponents; i ++ ) { - pInChI[i].nTotalCharge = NO_VALUE_INT; - } - return nNumComponents+1; - } - - while( 1 ) { - /* cycle over components */ - if ( !(pEnd = strchr( pStart, ';' )) ) { - pEnd = pStart + strlen(pStart); - } - - if ( (isdigit(UCINT *pStart) && - 0 < (val = (int)inchi_strtol( pStart, &q, 10)) || - (q = pStart, val=1) )&& - (t=strchr(mult_type, *q)) && q+1 == pEnd ) { - /* process the abbreviation */ - - switch( bMobileH ) { - - case TAUT_YES: - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - - case TAUT_NON: - if ( *q != 'm' || - iComponent + val > nNumComponents || - iComponent + val > ppnNumComponents[TAUT_YES] ) { - - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - for ( i = 0; i < val; i ++ ) { - /* avoid 0 which means "omitted" */ - pInChI[iComponent+i].nTotalCharge = pInpInChI[TAUT_YES][iComponent+i].nTotalCharge? - pInpInChI[TAUT_YES][iComponent+i].nTotalCharge : - NO_VALUE_INT; - } - mpy_component = val; - goto end_main_cycle; - - default: - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - }else - if ( (p = strchr(pStart, '*')) && p < pEnd ) { - mpy_component = (int)inchi_strtol( pStart, &q, 10 ); - if ( p != q ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p ++; - } else { - mpy_component = 1; - p = pStart; - } -#if ( FIX_DALKE_BUGS == 1 ) - if ( mpy_component + iComponent > nNumComponents || mpy_component <= 0 ) { - ret = RI_ERR_SYNTAX; /* syntax error: too many components in charge layer */ - goto exit_function; - } -#endif - pStart = p; - - if ( pStart < pEnd ) { - if ( pStart[0] == '+' && isdigit( UCINT pStart[1] ) ) { - val = (int)inchi_strtol( pStart+1, &q, 10 ); - pStart = q; - } else - if ( pStart[0] == '-' && isdigit( UCINT pStart[1] ) ) { - val = -(int)inchi_strtol( pStart+1, &q, 10 ); - pStart = q; - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } -#if ( FIX_DALKE_BUGS == 1 ) - if ( val < -256 || val > 256 ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } -#endif - if ( !val ) { - if ( pStart != pEnd ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( bMobileH == TAUT_NON ) { - val = NO_VALUE_INT; /* avoid 0 which means "omitted" */ - } - } - } else { - val = NO_VALUE_INT; - } - for ( i = 0; i < mpy_component; i ++ ) { - pInChI[iComponent+i].nTotalCharge = val; - } - -end_main_cycle: - iComponent += mpy_component; - if ( *pEnd ) { - pStart = pEnd+1; - continue; - } else { - break; - } - } - - if ( nNumComponents != iComponent ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - ret = iComponent + 1; - -exit_function: - return ret; - -} -/****************************************************************************************/ -int ParseSegmentMobileH( const char *str, int bMobileH, INChI *pInpInChI[], int pnNumComponents[], int *pbAbc ) -{ -#define nNum_H( ICOMPONENT ) ((bMobileH==TAUT_YES)? pInChI[ICOMPONENT].nNum_H : pInChI[ICOMPONENT].nNum_H_fixed) - /* Pass 1: count bonds and find actual numbers of atom */ - int i, mpy_component, num_H, num_Minus, val, num_Atoms, numCtAtoms, tg_alloc_len, len, len2; - int num_H_component, num_H_formula, num_taut_H_component, num_H_InChI, ret2; - int nNumComponents, iComponent, nNumBonds, lenTautomer, tg_pos_Tautomer, iTGroup; - const char *p, *q, *h, *t, *p1, *pTaut, *pStart, *pEnd; - AT_NUMB curAtom, nxtAtom; - int num_open, state, ret, nAltMobileH = ALT_TAUT(bMobileH); - INChI *pInChI = pInpInChI[bMobileH]; - INChI *pAltInChI = pInpInChI[nAltMobileH]; - int base = 10; - - num_H = -999; /* impossible value */ - num_Minus = -999; /* impossible value */ - tg_pos_Tautomer = -999; /* impossible value */ - - /* number of immobile H is always allocated; immobile H are present in M layer only */ - nNumComponents = pnNumComponents[bMobileH]; - for ( i = 0; i < nNumComponents; i ++ ) { - len = pInChI[i].nNumberOfAtoms; - if ( bMobileH == TAUT_NON && i < pnNumComponents[nAltMobileH] ) { - if ( len < pAltInChI[i].nNumberOfAtoms ) { - len = pAltInChI[i].nNumberOfAtoms; - if ( pInChI[i].nNum_H ) { - inchi_free( pInChI[i].nNum_H ); - pInChI[i].nNum_H = NULL; - } - } - } - len ++; - if ( !pInChI[i].nNum_H && /* allocate immobile H segment if it has not been allocated yet */ - !(pInChI[i].nNum_H = (S_CHAR *)inchi_calloc( len, sizeof(pInChI[0].nNum_H[0]) )) ) { - ret = RI_ERR_ALLOC; /* allocation error */ - goto exit_function; - } - /* copy immobile H from Mobile-H layer to Fixed-H layer */ - if ( bMobileH == TAUT_NON && i < pnNumComponents[nAltMobileH] ) { - memcpy( pInChI[i].nNum_H, pAltInChI[i].nNum_H, (len-1) * sizeof(pInChI[0].nNum_H[0]) ); - } - } - - if ( str[0] != 'h' ) - return 0; - - /* Read Hydrogen info in 1 pass */ - pStart = str+1; - iComponent = 0; - nNumComponents = pnNumComponents[bMobileH]; - - while( 1 ) { - /* cycle over components */ - if ( !(pEnd = strchr( pStart, ';' )) ) { - pEnd = pStart + strlen(pStart); - } - if ( (p = strchr(pStart, '*')) && p < pEnd ) { - mpy_component = (int)inchi_strtol( pStart, &q, 10 ); -#if ( FIX_DALKE_BUGS == 1 ) - if ( p != q || !isdigit(UCINT *pStart) ) /* prevent non-positive multipliers */ -#else - if ( p != q ) -#endif - { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p ++; - } else { - mpy_component = 1; - p = pStart; - } - pStart = p; - /* Pass 1.1 parse a component */ - num_open = 0; - state='\0'; /* initial state */ - nNumBonds = 0; - curAtom = 0; - numCtAtoms = pInChI[iComponent].nNumberOfAtoms; - if ( bMobileH == TAUT_NON && iComponent < pnNumComponents[nAltMobileH] ) { - numCtAtoms = pAltInChI[iComponent].nNumberOfAtoms; - } - - if ( p < pEnd && *pbAbc == -1 ) { - /* check if compressed InChI */ - *pbAbc = (*p == ',' || isupper( UCINT *p))? 1 : 0; - } - base = (*pbAbc==1)? ALPHA_BASE : 10; - - /* immobile H */ - t = pTaut = (*pbAbc==1)? strchr( p, ',' ) : strchr( p, '(' ); /* locate the first tautomer group character */ - - if ( t && bMobileH == TAUT_NON ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( !pTaut || pTaut > pEnd ) { - pTaut = pEnd; - t = NULL; /* found no tautomeric group for this component */ - } - for ( i = 0; i < mpy_component; i ++ ) { - if ( bMobileH == TAUT_NON ) { - /* allocate nNum_H_fixed */ - if ( pInChI[iComponent+i].nNum_H_fixed ) { - ret = RI_ERR_PROGR; /* program error */ - goto exit_function; - } - if ( iComponent+i < pnNumComponents[nAltMobileH] ) { - len = inchi_max(pInChI[iComponent+i].nNumberOfAtoms, pAltInChI[iComponent+i].nNumberOfAtoms)+1; - } else { - len = pInChI[iComponent+i].nNumberOfAtoms + 1; - } - pInChI[iComponent+i].nNum_H_fixed = (S_CHAR *)inchi_calloc( len, sizeof(pInChI[0].nNum_H_fixed[0]) ); - if ( !pInChI[iComponent+i].nNum_H_fixed ) { - ret = RI_ERR_ALLOC; /* allocation error */ - goto exit_function; - } - /* compare nAtom */ - if ( iComponent+i < pnNumComponents[nAltMobileH] ) { - len2 = inchi_min(pInChI[iComponent+i].nNumberOfAtoms, pAltInChI[iComponent+i].nNumberOfAtoms); - if ( pInChI[iComponent+i].nAtom && len2 ) { - /* check */ - if ( memcmp( pInChI[iComponent+i].nAtom, pAltInChI[iComponent+i].nAtom, len2 * sizeof(pInChI[0].nAtom[0]) ) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - /* allocate and copy atom if bridging H are present */ - if ( pInChI[iComponent+i].nNumberOfAtoms < pAltInChI[iComponent+i].nNumberOfAtoms ) { - if ( pInChI[iComponent+i].nAtom ) - inchi_free( pInChI[iComponent+i].nAtom ); - if ( !(pInChI[iComponent+i].nAtom = (U_CHAR *)inchi_calloc( len, sizeof(pInChI[0].nAtom[0]) ) ) ) { - ret = RI_ERR_ALLOC; /* allocation error */ - goto exit_function; - } - if ( len > 1 ) { - memcpy( pInChI[iComponent+i].nAtom, pAltInChI[iComponent+i].nAtom, (len-1) * sizeof(pInChI[0].nAtom[0]) ); - } - /* correct number of atoms including bridging H */ - pInChI[iComponent+i].nNumberOfAtoms = pAltInChI[iComponent+i].nNumberOfAtoms; - } - } - } - } - - if ( *pbAbc == 1 ) { - /* read numbers of H: XnYn... or XYn... */ - p = pStart; - tg_alloc_len = 0; - num_H_component = num_taut_H_component = 0; - while ( p < pTaut ) { - /* syntax check: atom number */ - if ( !*p || !isupper( UCINT *p ) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( curAtom = nxtAtom = (int)inchi_strtol( p, &q, base ) ) { - p = q; - if ( isupper( UCINT *p ) ) { - nxtAtom = (int)inchi_strtol( p, &q, base ); - p = q; - } - } - if ( curAtom > nxtAtom || nxtAtom > numCtAtoms || p > pTaut ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - /* number of H, may be negative */ - if ( !(num_H = (int)inchi_strtol( p, &q, 10 )) || q > pTaut ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p = q; - /* set number of H */ - for ( i = curAtom; i <= nxtAtom; i ++ ) { - nNum_H(iComponent)[i-1] = num_H; - num_H_component += num_H; - } - } - if ( p != pTaut ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } else { - /* read numbers of H: 1-2,3H2,4,5H3 */ - p = pStart; - tg_alloc_len = 0; - num_H_component = num_taut_H_component = 0; - while ( p < pTaut ) { - /* syntax check: atom number */ - if ( !*p || !isdigit( UCINT *p ) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - /* number of H */ - h = p + strcspn( p, "Hh" ); - /*h = strchr( p, 'H' );*/ - if ( !*h || h >= pTaut ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - - /* - p = pTaut; - h = NULL; - break; */ /* no more H found */ - } - num_H = (*h == 'H')? 1 : (*h == 'h')? -1 : 0; - if ( !num_H ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( h[1] && isdigit( UCINT h[1] ) ) { - num_H *= (int)inchi_strtol( h+1, &p1, 10 ); - } else { - p1 = h+1; /* next set of immobile H */ - } - if ( *p1 == ',' ) { - p1 ++; /* next H-subsegment; otherwise (H or ; or end of the segment */ - } - /* list of atoms that have num_H */ - while ( p < h ) { - if ( !*p || !isdigit( UCINT *p ) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - nxtAtom = curAtom = (int)inchi_strtol( p, &q, 10 ); - if ( *q == '-' ) { - nxtAtom = (int)inchi_strtol( q+1, &q, 10 ); - } - /* consitency check */ - if ( !curAtom || curAtom > numCtAtoms || - nxtAtom < curAtom || nxtAtom > numCtAtoms ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - /* set number of H */ - for ( i = curAtom; i <= nxtAtom; i ++ ) { - nNum_H(iComponent)[i-1] = num_H; - num_H_component += num_H; - } - /* move to the next atom number if any */ - p = q; - if ( *p == ',' ) { - p ++; - } - } - - if ( p == h ) { - p = p1; - } else - if ( p == pTaut ) { - break; - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - } - - INCHI_HEAPCHK - /* ) -> (, H, N, [-, N,], AtNum,... AtNum) */ - lenTautomer = 0; - if ( p = t ) { - if ( *pbAbc == 1 ) { - /* tautomeric groups: pass 1 */ - iTGroup = 0; - state = ')'; /* init as if the prev. t-group just ended */ - num_Atoms = 0; - /* Tautomeric info storage */ - /* NumGroups; ((NumAt+2, NumH, Num(-), At1..AtNumAt),...); {INCHI_T_NUM_MOVABLE = 2} */ - /* Allocated length: [5*nNumberOfAtoms/2+1], see Alloc_INChI(...) */ - if ( *p == ',' ) { /* start t-group */ - p ++; - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - while ( p < pEnd ) { - /* start t-group */ - if ( !isdigit( UCINT *p ) || !(num_H = (int)inchi_strtol( p, &q, 10 ) ) || q > pEnd ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p = q; - num_Minus = 0; - if ( *p == '-' ) { - p ++; - if ( isdigit( UCINT *p ) ) { - num_Minus = (int)inchi_strtol( p, &q, 10 ); - p = q; - } else { - num_Minus = 1; - } - } - if ( p >= pEnd ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( !tg_alloc_len ) { - /* - --- header --- - [num_t_groups] - --- one t-group: --- - [len=group length no including this value] - [num_H] - [num_(-)] - [Endpoint(1),...,Endpoint(len-2)] - --- next t-group --- - ... - - Max. size = 1 + 3*max_num_t_groups + max_num_endpoints - - max_num_t_groups = num_at/2 - max_num_endpoints = num_at - - Max. size = 1 + 3*(num_at/2) + num_at = 1 + (5*num_at)/2 - 5 = 3 + INCHI_T_NUM_MOVABLE = 3 + num_types_of_attachments - - This does not include zero termination! - - */ - tg_alloc_len = ((3+INCHI_T_NUM_MOVABLE)*pInChI[iComponent].nNumberOfAtoms)/2+1; - for ( i = 0; i < mpy_component; i ++ ) { - pInChI[iComponent+i].nTautomer = (AT_NUMB*)inchi_calloc( tg_alloc_len+1, sizeof(pInChI->nTautomer[0])); - if ( !pInChI[iComponent+i].nTautomer ) { - ret = RI_ERR_ALLOC; /* allocation error */ - goto exit_function; - } - pInChI[iComponent+i].lenTautomer = 0; - } - tg_pos_Tautomer = 1; /* number atoms (NumAt+2) position */ - } else { - /* next t-group */ - tg_pos_Tautomer = lenTautomer; - } - if ( tg_pos_Tautomer+3 >= tg_alloc_len ) { - ret = RI_ERR_PROGR; /* wrong tautomer array length */ - goto exit_function; - } - pInChI[iComponent].nTautomer[tg_pos_Tautomer+1] = num_H; - pInChI[iComponent].nTautomer[tg_pos_Tautomer+2] = num_Minus; - lenTautomer = tg_pos_Tautomer+3; /* first atom number position */ - num_taut_H_component += num_H; - - while ( p < pEnd && isupper( UCINT *p) ) { - /* read list of tautomeric atoms */ - val = (int)inchi_strtol( p, &q, base ); - if ( lenTautomer >= tg_alloc_len || val > numCtAtoms ) { - ret = RI_ERR_PROGR; /* wrong tautomer array length */ - goto exit_function; - } - num_Atoms ++; - pInChI[iComponent].nTautomer[lenTautomer ++] = val; - p = q; - } - if ( !num_Atoms || p < pEnd && !isdigit( UCINT *p) ) { - ret = RI_ERR_PROGR; /* wrong tautomer array length */ - goto exit_function; - } - iTGroup ++; - pInChI[iComponent].nTautomer[tg_pos_Tautomer] = lenTautomer - tg_pos_Tautomer - 1; /* length of the rest of the t-group */ - pInChI[iComponent].lenTautomer = lenTautomer; - } - if ( !iTGroup || p != pEnd ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - pInChI[iComponent].nTautomer[0] = iTGroup; - - } else { - /* tautomeric groups: pass 1 */ - iTGroup = 0; - state = ')'; /* init as if the prev. t-group just ended */ - num_Atoms = 0; - /* Tautomeric info storage */ - /* NumGroups; ((NumAt+2, NumH, Num(-), At1..AtNumAt),...); {INCHI_T_NUM_MOVABLE = 2} */ - /* Allocated length: [5*nNumberOfAtoms/2+1], see Alloc_INChI(...) */ - while ( p < pEnd ) { - /* t-group */ - switch ( *p ) { - case '(': /* start t-group */ - switch ( state ) { - case ')': - state = *p ++; - num_H = 0; - num_Minus = 0; - continue; - default: - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - case ')': /* end t-group */ - switch ( state ) { - case 'A': /* previuos was atom number */ - if ( !tg_alloc_len ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - iTGroup ++; - state = *p ++; - pInChI[iComponent].nTautomer[tg_pos_Tautomer] = lenTautomer - tg_pos_Tautomer - 1; /* length of the rest of the t-group */ - pInChI[iComponent].lenTautomer = lenTautomer; - continue; - default: - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - case 'H': /* number of H */ - switch ( state ) { - case '(': - state = *p ++; - num_H = 1; - continue; - default: - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - case '-': /* number of (-) */ - switch ( state ) { - case 'N': /* previous was number of H */ - case 'H': /* previous was H */ - state = *p ++; - num_Minus = 1; - continue; - default: - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - case ',': - switch ( state ) { - case 'N': /* previous was number of H */ - case 'H': /* previous was H */ - case '-': /* previuos was - */ - case 'M': /* previous was number of (-) */ - /* the next must be the first tautomeric atom number; save num_H & num_Minus */ - if ( num_H <= 0 && num_Minus <= 0 ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( !tg_alloc_len ) { - /* - --- header --- - [num_t_groups] - --- one t-group: --- - [len=group length no including this value] - [num_H] - [num_(-)] - [Endpoint(1),...,Endpoint(len-2)] - --- next t-group --- - ... - - Max. size = 1 + 3*max_num_t_groups + max_num_endpoints - - max_num_t_groups = num_at/2 - max_num_endpoints = num_at - - Max. size = 1 + 3*(num_at/2) + num_at = 1 + (5*num_at)/2 - 5 = 3 + INCHI_T_NUM_MOVABLE = 3 + num_types_of_attachments - - This does not include zero termination! - - */ - tg_alloc_len = ((3+INCHI_T_NUM_MOVABLE)*pInChI[iComponent].nNumberOfAtoms)/2+1; - for ( i = 0; i < mpy_component; i ++ ) { - pInChI[iComponent+i].nTautomer = (AT_NUMB*)inchi_calloc( tg_alloc_len+1, sizeof(pInChI->nTautomer[0])); - if ( !pInChI[iComponent+i].nTautomer ) { - ret = RI_ERR_ALLOC; /* allocation error */ - goto exit_function; - } - pInChI[iComponent+i].lenTautomer = 0; - } - tg_pos_Tautomer = 1; /* number atoms (NumAt+2) position */ - } else { - /* next t-group */ - tg_pos_Tautomer = lenTautomer; - } - if ( tg_pos_Tautomer+3 >= tg_alloc_len ) { - ret = RI_ERR_PROGR; /* wrong tautomer array length */ - goto exit_function; - } - pInChI[iComponent].nTautomer[tg_pos_Tautomer+1] = num_H; - pInChI[iComponent].nTautomer[tg_pos_Tautomer+2] = num_Minus; - lenTautomer = tg_pos_Tautomer+3; /* first atom number position */ - num_taut_H_component += num_H; - state = *p ++; - continue; - case 'A': /* previuos was atom number */ - state = *p ++; - continue; - default: - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - default: - if ( isdigit( UCINT *p ) ) { - val = (int)inchi_strtol( p, &q, 10 ); - if ( val <= 0 ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p = q; - switch( state ) { - case 'H': - num_H = val; - state = 'N'; - continue; - case '-': - num_Minus = val; - state = 'M'; - continue; - case ',': - if ( lenTautomer >= tg_alloc_len || val > numCtAtoms ) { - ret = RI_ERR_PROGR; /* wrong tautomer array length */ - goto exit_function; - } - num_Atoms ++; - pInChI[iComponent].nTautomer[lenTautomer ++] = val; - state = 'A'; - continue; - default: - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - - } - } - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - if ( !iTGroup || state != ')' ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - pInChI[iComponent].nTautomer[0] = iTGroup; - } - } - /* check num_H in components; for bMobileH=TAUT_NON, pInChI->nNum_H_fixed[] has not been added to pInChI->nNum_H[] yet */ - if ( 0 > ( ret2 = GetInChIFormulaNumH( pInChI+iComponent, &num_H_formula) ) || - 0 > ( ret2 = GetInChINumH( pInChI+iComponent, &num_H_InChI ) ) ) { - ret = ret2; - goto exit_function; - } - if ( num_H_formula != num_H_InChI + (bMobileH==TAUT_NON? num_H_component:0) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - - /* duplicate according to multipolication */ - for ( i = 1; i < mpy_component; i ++ ) { - memcpy( nNum_H(iComponent+i), nNum_H(iComponent), pInChI[iComponent+i].nNumberOfAtoms * sizeof(nNum_H(0)[0]) ); - /* - memcpy( pInChI[iComponent+i].nNum_H, pInChI[iComponent].nNum_H, - pInChI[iComponent+i].nNumberOfAtoms * sizeof(pInChI[0].nNum_H[0]) ); - */ - if ( pInChI[iComponent+i].nTautomer && pInChI[iComponent].nTautomer && pInChI[iComponent].lenTautomer ) { - memcpy( pInChI[iComponent+i].nTautomer, pInChI[iComponent].nTautomer, - pInChI[iComponent].lenTautomer * sizeof(pInChI[0].nTautomer[0]) ); - pInChI[iComponent+i].lenTautomer = pInChI[iComponent].lenTautomer; - } - /* check num_H in components */ - if ( 0 > ( ret2 = GetInChIFormulaNumH( pInChI+iComponent+i, &num_H_formula) ) || - 0 > ( ret2 = GetInChINumH( pInChI+iComponent+i, &num_H_InChI ) ) ) { - ret = ret2; - goto exit_function; - } - if ( num_H_formula != num_H_InChI + (bMobileH==TAUT_NON? num_H_component:0) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - - /* prepare for the next component */ - iComponent += i; - if ( *pEnd ) { -#if (FIX_DALKE_BUGS == 1) - /* prevent crash on extra trailing ';' */ - if ( iComponent >= nNumComponents ) { - ret = RI_ERR_SYNTAX; /* syntax error: extra component */ - goto exit_function; - } -#endif - pStart = pEnd+1; - } else - break; - } - if ( nNumComponents != iComponent ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - ret = iComponent + 1; - -exit_function: - INCHI_HEAPCHK - return ret; - -} - -/****************************************************************************************/ -int ParseSegmentConnections( const char *str, int bMobileH, INChI **pInpInChI, int *pnNumComponents, int *pbAbc ) -{ -#define LAST_AT_LEN 256 - /* Pass 1: count bonds and find actual numbers of atom */ - int i, j, k, m, c, mpy_component; - int nNumComponents, iComponent, nNumAtoms, nNumBonds, lenConnTable, iBond; - const char *p, *q, *pStart, *pEnd; - AT_NUMB last_atom[LAST_AT_LEN], curAtom, maxAtom; - int num_open, state, ret, base; - INChI *pInChI = *pInpInChI; - LINKED_BONDS LB; - LINKED_BONDS *pLB = &LB; /* a list of linked lists of bonds, for each atom */ - AT_NUMB neighbor[MAXVAL]; - int bPrevVersion = -1; - - iComponent = 0; - if ( str[0] != 'c' ) { - if ( !pInChI && !*pnNumComponents ) { - int lenFormula = 1; - /* component has no formula; allocate InChI */ - lenConnTable = 0; - nNumComponents = 1; - /* allocate InChI */ - if ( !(pInChI = *pInpInChI = (INChI *)inchi_calloc( nNumComponents, sizeof(INChI) ) ) ) { - return RI_ERR_ALLOC; /* alloc failure */ - } - /* allocate empty formula */ - pInChI[iComponent].szHillFormula = (char *)inchi_calloc( lenFormula+1, sizeof(pInChI[0].szHillFormula[0]) ); - if ( !pInChI[iComponent].szHillFormula ) { - ret = RI_ERR_ALLOC; /* allocation failure */ - goto exit_function; - } - /* allocate empty connection table */ - pInChI[iComponent].nConnTable = (AT_NUMB *)inchi_calloc( lenConnTable+1, sizeof(pInChI[0].nConnTable[0]) ); - if ( !pInChI[iComponent].nConnTable ) { - ret = RI_ERR_ALLOC; /* allocation failure */ - goto exit_function; - } - pInChI[iComponent].lenConnTable = lenConnTable; - *pnNumComponents = nNumComponents; - } else { - lenConnTable = 1; - nNumComponents = *pnNumComponents; - for ( i = 0; i < nNumComponents; i ++ ) { - /* allocate 1 atom connection table */ - if ( pInChI[i].nConnTable ) { - inchi_free( pInChI[i].nConnTable ); - } - pInChI[i].nConnTable = (AT_NUMB *)inchi_calloc( lenConnTable+1, sizeof(pInChI[0].nConnTable[0]) ); - if ( !pInChI[i].nConnTable ) { - ret = RI_ERR_ALLOC; /* allocation failure */ - goto exit_function; - } - pInChI[i].nConnTable[0] = 1; - pInChI[i].lenConnTable = lenConnTable; - } - } - return 0; - } - - /* Pass 1. Re-Count atoms, count bonds */ - pStart = str+1; - nNumComponents = *pnNumComponents; -#if (FIX_DALKE_BUGS == 1) - /* prevent crash on too many components */ - if ( nNumComponents > MAX_ATOMS ) { - ret = RI_ERR_SYNTAX; /* syntax error: extra component */ - goto exit_function; - } -#endif - memset( pLB, 0, sizeof(pLB[0]) ); - - while( 1 ) { - /* cycle over components */ - if ( !(pEnd = strchr( pStart, ';' )) ) { - pEnd = pStart + strlen(pStart); - } - if ( (p = strchr(pStart, '*')) && p < pEnd ) { - mpy_component = (int)inchi_strtol( pStart, &q, 10 ); - if ( p != q -#if (FIX_DALKE_BUGS == 1) - || !isdigit( UCINT *pStart ) -#endif - ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p ++; - } else { - mpy_component = 1; - p = pStart; - } -#if (FIX_DALKE_BUGS == 1) - if ( iComponent + mpy_component > MAX_ATOMS ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } -#endif - pStart = p; - /* Pass 1.1 parse a component */ - num_open = 0; - memset( last_atom, 0, sizeof(last_atom) ); - state='\0'; /* initial state */ - maxAtom = 0; - nNumBonds = 0; - curAtom = 0; - if ( p < pEnd && *pbAbc == -1 ) { - /* check if compressed InChI */ - *pbAbc = isupper( UCINT *p)? 1 : 0; - } - base = *pbAbc? ALPHA_BASE : 10; - - if ( *pbAbc == 1 ) { - nNumAtoms = 1; - while ( p < pEnd ) { - if ( *p == '-' ) { - if ( bPrevVersion == -1 ) { - /* previous InChI version */ - bPrevVersion = 1; - } else - if ( bPrevVersion != 1 ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - nNumAtoms --; - p ++; - } - if ( isdigit( UCINT *p ) ) { - if ( bPrevVersion == -1 ) { - /* curreny InChI, version 1 */ - bPrevVersion = 0; - } else - if ( bPrevVersion != 0 ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - nNumAtoms -= inchi_strtol( p, &p, 10 ); /* bypass digits */ - } - if ( *p != '-' && ( curAtom = (AT_NUMB)inchi_strtol( p, &q, base ) ) ) { - nNumAtoms ++; - nNumBonds ++; - p = q; - if ( maxAtom < curAtom ) - maxAtom = curAtom; - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - if ( maxAtom < nNumAtoms && nNumBonds ) { - maxAtom = nNumAtoms; - } - } else { - while ( p < pEnd ) { - /* atom number */ - c = UCINT *p ++; - switch ( c ) { - case '(': - case ')': - case ',': - case '-': - if ( state != 'N' ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - state = c; - num_open += (c=='(') - (c==')'); - if ( num_open < 0 ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - break; - default: - if ( isdigit( c ) && (curAtom = (AT_NUMB)inchi_strtol( p-1, &q, 10 )) ) { - p = q; - switch( state ) { - case '(': - case ')': - case ',': - case '-': - nNumBonds ++; - case '\0': - if ( maxAtom < curAtom ) - maxAtom = curAtom; - state = 'N'; - break; - default: - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - break; - } - } - if ( num_open ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - /* syntax error: parentheses do not match */ - } - } - /* Save the results and allocate memory */ - nNumAtoms = (int)maxAtom; /* 0 if empty connection table and no bonds present */ - lenConnTable = nNumAtoms + nNumBonds; - /* connection table format: At1[,Neigh11,Neigh12,...],At2[,Neigh21,Neigh22,...],AtN[NeighN1,NeighN2,...] */ - /* where AtK > NeighK1 > NeighK2,...; At(K) < At(K+1); the length = num.atoms + num.bonds */ - for ( i = 0; i < mpy_component; i ++ ) { - /* check number of atoms: the difference may be due to bridging H */ - if ( (j = pInChI[iComponent+i].nNumberOfAtoms) < nNumAtoms ) { - /* reallocate */ - U_CHAR *nAtomTmp = (U_CHAR *) inchi_malloc( nNumAtoms + 1 ); - if ( !nAtomTmp ) { - ret = RI_ERR_ALLOC; /* allocation failure */ - goto exit_function; - } - memcpy( nAtomTmp, pInChI[iComponent+i].nAtom, sizeof(nAtomTmp[0])*j); - while ( j < nNumAtoms ) { - nAtomTmp[j ++] = EL_NUMBER_H; /* bridging H */ - } - nAtomTmp[j] = '\0'; - INCHI_HEAPCHK - if ( pInChI[iComponent+i].nAtom ) { - inchi_free( pInChI[iComponent+i].nAtom ); - } - pInChI[iComponent+i].nAtom = nAtomTmp; - pInChI[iComponent+i].nNumberOfAtoms = nNumAtoms; - } else - if ( j > nNumAtoms && (lenConnTable || j != 1) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - /* allocate connection table */ - if ( pInChI[iComponent+i].nConnTable ) { - inchi_free( pInChI[iComponent+i].nConnTable ); - } - if ( !nNumAtoms && !nNumBonds && !lenConnTable ) { - lenConnTable = 1; /* one atom, no bonds */ - } - pInChI[iComponent+i].nConnTable = (AT_NUMB *)inchi_calloc( lenConnTable+1, sizeof(pInChI[0].nConnTable[0]) ); - if ( !pInChI[iComponent+i].nConnTable ) { - ret = RI_ERR_ALLOC; /* allocation failure */ - goto exit_function; - } - pInChI[iComponent+i].lenConnTable = lenConnTable; - } - - /* Pass 1.2 parse a component and extract the bonds */ - num_open = 0; - memset( last_atom, 0, sizeof(last_atom) ); - state='\0'; /* initial state */ - iBond = 0; - p = pStart; - pLB->len = 0; - - if ( *pbAbc == 1 ) { - /* compressed */ - int num_neigh; - num_open = 0; - last_atom[num_open] = 2; - while ( p < pEnd ) { - if ( last_atom[num_open] > maxAtom ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( isupper( UCINT *p ) ) { - curAtom = (AT_NUMB)inchi_strtol( p, &q, base ); - if ( ret = AddLinkedBond( last_atom[num_open], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { - goto exit_function; - } - p = q; - if ( bPrevVersion == 1 ) { - while ( p < pEnd && *p == '-' ) { - p ++; - if ( curAtom = (AT_NUMB)inchi_strtol( p, &q, base ) ) { - if ( ret = AddLinkedBond( last_atom[num_open], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { - goto exit_function; - } - p = q; - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - } else - if ( bPrevVersion == 0 && isdigit( *p ) ) { - num_neigh = (int)inchi_strtol( p, &q, 10 ); - p = q; - while( num_neigh -- && p < pEnd ) { - if ( curAtom = (AT_NUMB)inchi_strtol( p, &q, base ) ) { - if ( ret = AddLinkedBond( last_atom[num_open], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { - goto exit_function; - } - p = q; - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - } - last_atom[num_open] ++; - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - } else { - while ( p < pEnd ) { - /* each atom number except the first means a new bond */ - c = UCINT *p ++; - switch ( c ) { - case '(': - case ')': - case ',': - case '-': - switch ( state ) { - case 'N': - state = c; - break; - default: - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - break; - default: - if ( isdigit( c ) && (curAtom = (AT_NUMB)inchi_strtol( p-1, &q, 10 )) ) { - p = q; - switch( state ) { - case '\0': - last_atom[num_open] = curAtom; - state = 'N'; - break; - case '(': - if ( ret = AddLinkedBond( last_atom[num_open], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { - goto exit_function; - } - if ( ++ num_open >= LAST_AT_LEN ) { - ret = RI_ERR_PROGR; /* program error: buffer overflow */ - goto exit_function; - } - last_atom[num_open] = curAtom; - state = 'N'; - break; - - case ')': - if ( !num_open ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( ret = AddLinkedBond( last_atom[--num_open], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { - goto exit_function; - } - last_atom[num_open] = curAtom; - state = 'N'; - break; - - case ',': - if ( !num_open ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( ret = AddLinkedBond( last_atom[num_open-1], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { - goto exit_function; - } - last_atom[num_open] = curAtom; - state = 'N'; - break; - case '-': - if ( ret = AddLinkedBond( last_atom[num_open], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { - goto exit_function; - } - last_atom[num_open] = curAtom; - state = 'N'; - break; - default: - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - break; - } - } - } - /* store the bonds in connection table */ - if ( lenConnTable > 1 ) { - for ( i = 0, m = 0; i < nNumAtoms; i ++ ) { - k = 0; - if ( j = pLB->pBond[i+1].prev ) { - while( k < MAXVAL ) { - neighbor[k++] = pLB->pBond[j].neigh; - if ( j == i+1 ) - break; - j = pLB->pBond[j].prev; - } - } - if ( j != i+1 ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - /* sort the neighbors */ - insertions_sort_AT_NUMB( neighbor, k ); - pInChI[iComponent].nConnTable[m ++] = i+1; /* atom number */ - for ( j = 0; j < k && (int)neighbor[j] <= i; j ++ ) { - pInChI[iComponent].nConnTable[m ++] = neighbor[j]; - } - } - if ( m != lenConnTable ) { - ret = RI_ERR_PROGR; /* program error */ - goto exit_function; - } - } else { - pInChI[iComponent].nConnTable[0] = 1; /* single atom */ - } - /* duplicate if needed */ - for ( i = 1; i < mpy_component; i ++ ) { - /* - if ( pInChI[iComponent+i].nConnTable ) { - inchi_free( pInChI[iComponent+i].nConnTable ); - } - pInChI[iComponent+i].nConnTable = (AT_NUMB *)inchi_calloc( lenConnTable+1, sizeof(pInChI[0].nConnTable[0]) ); - if ( !pInChI[iComponent+i].nConnTable ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - */ - if ( !pInChI[iComponent+i].nConnTable || pInChI[iComponent+i].lenConnTable != lenConnTable ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - memcpy ( pInChI[iComponent+i].nConnTable, pInChI[iComponent].nConnTable, lenConnTable*sizeof(pInChI[0].nConnTable[0])); - } - /* prepare for the next connection table */ - iComponent += i; - if ( *pEnd ) - pStart = pEnd+1; - else - break; - - } - ret = iComponent; - -exit_function: - if ( pLB->pBond ) { - INCHI_HEAPCHK - inchi_free( pLB->pBond ); - } - return ret; - -#undef LAST_AT_LEN -} -/****************************************************************************************/ -int nFillOutProtonMobileH( INChI *pInChI ) -{ - int len = 1; - pInChI->bDeleted = 1; - /* formula */ - if ( !pInChI->szHillFormula && - !( pInChI->szHillFormula = (char *) inchi_calloc( len+1, sizeof(pInChI->szHillFormula[0]) ) ) ) { - return RI_ERR_ALLOC; /* alloc failure */ - } - strcpy( pInChI->szHillFormula, "H" ); - pInChI->nNumberOfAtoms = 1; - - /* atoms */ - if ( !pInChI->nAtom && - !(pInChI->nAtom = (U_CHAR *) inchi_calloc( len+1, sizeof(pInChI->nAtom[0]) ) ) ) { - return RI_ERR_ALLOC; /* alloc failure */ - } - pInChI->nAtom[0] = 1; - /* charge */ - pInChI->nTotalCharge = 1; - /* connection table */ - if ( !pInChI->nConnTable && - !(pInChI->nConnTable = (AT_NUMB *) inchi_calloc( len+1, sizeof(pInChI->nConnTable[0]) ) ) ) { - return RI_ERR_ALLOC; /* alloc failure */ - } - pInChI->nConnTable[0] = 1; - pInChI->lenConnTable = len; - /* tautomer */ - if ( !pInChI->nTautomer && - !(pInChI->nTautomer = (AT_NUMB *) inchi_calloc( len+1, sizeof(pInChI->nTautomer[0]) ) ) ) { - return RI_ERR_ALLOC; /* alloc failure */ - } - /* nNum_H */ - if ( !pInChI->nNum_H && - !(pInChI->nNum_H = (S_CHAR *) inchi_calloc( len+1, sizeof(pInChI->nNum_H[0]) ) ) ) { - return RI_ERR_ALLOC; /* alloc failure */ - } - pInChI->nNum_H[0] = 0; - - pInChI->nTautomer[0] = 0; - pInChI->lenTautomer = 1; - return 0; -} -/****************************************************************************************/ -int nProtonCopyIsotopicInfo( INChI *pInChI_to, INChI *pInChI_from ) -{ - if ( pInChI_from->nNumberOfIsotopicAtoms ) { - if ( pInChI_to->nNumberOfIsotopicAtoms && - pInChI_from->nNumberOfIsotopicAtoms > pInChI_to->nNumberOfIsotopicAtoms ) { - - inchi_free( pInChI_to->IsotopicAtom ); - pInChI_to->IsotopicAtom = NULL; - pInChI_to->nNumberOfIsotopicAtoms = 0; - } - if ( !pInChI_to->IsotopicAtom && - !(pInChI_to->IsotopicAtom = - (INChI_IsotopicAtom *)inchi_calloc(pInChI_from->nNumberOfIsotopicAtoms, - sizeof(pInChI_to->IsotopicAtom[0]) ) ) ) { - return RI_ERR_ALLOC; - } - pInChI_to->nNumberOfIsotopicAtoms = pInChI_from->nNumberOfIsotopicAtoms; - memcpy( pInChI_to->IsotopicAtom, pInChI_from->IsotopicAtom, - pInChI_from->nNumberOfIsotopicAtoms * sizeof(pInChI_to->IsotopicAtom[0]) ); - } else { - if ( pInChI_to->IsotopicAtom ) inchi_free( pInChI_to->IsotopicAtom ); - pInChI_to->IsotopicAtom = NULL; - pInChI_to->nNumberOfIsotopicAtoms = 0; - } - return 0; -} -/****************************************************************************************/ -int ParseSegmentFormula( const char *str, int bMobileH, INChI *pInpInChI[], int pnNumComponents[] ) -{ - int i, j, mpy_component, mpy_atom, len, el_number; - int nNumComponents = 0, iComponent, nNumAtoms, nNumAtomsAndH, iAtom, nNumH, nAltMobileH = ALT_TAUT(bMobileH); - const char *p, *q, *e, *pStart, *pEnd; - INChI *pInChI; - char szEl[3]; - - nNumAtoms = -999; /* impossible value */ - - /* Pass 1. Count components */ - pStart = str; - while( 1 ) { - if ( !(pEnd = strchr( pStart, '.' )) ) { - pEnd = pStart + strlen(pStart); - } - p = pStart; - if ( isdigit( *p ) ) { - mpy_component = (int)inchi_strtol( p, &q, 10 ); - p = q; - } else { - mpy_component = 1; - } - if ( !mpy_component ) - break; - if ( !isupper( UCINT *p ) ) { - break; /* not a formula layer */ - } - if ( pEnd == p ) - break; /* zero length formula */ - nNumComponents += mpy_component; - if ( *pEnd ) - pStart = pEnd+1; - else - break; - } - pnNumComponents[bMobileH] = nNumComponents; - -#if ( FIX_DALKE_BUGS == 1 ) - if ( nNumComponents > MAX_ATOMS ) { - return RI_ERR_SYNTAX; /* syntax error */ - } -#endif - /* exit or error check */ - if ( !nNumComponents ) { - if ( !*pStart || islower( UCINT *pStart ) ) { - INCHI_HEAPCHK - if ( bMobileH == TAUT_NON && 0 < ( nNumComponents = pnNumComponents[nAltMobileH]) ) { - /* allocate InChI */ - if ( !( pInChI = (INChI *)inchi_calloc( nNumComponents, sizeof(INChI) ) ) ) { - return RI_ERR_ALLOC; /* alloc failure */ - } - pInpInChI[bMobileH] = pInChI; - pnNumComponents[bMobileH] = nNumComponents; - for ( i = 0; i < nNumComponents; i ++ ) { - /* copy number of atoms */ - len = pInpInChI[bMobileH][i].nNumberOfAtoms = pInpInChI[nAltMobileH][i].nNumberOfAtoms; - /* copy atoms */ - len = (len+1)*sizeof(pInpInChI[0][0].nAtom[0]); - if ( pInpInChI[bMobileH][i].nAtom ) { - inchi_free( pInpInChI[bMobileH][i].nAtom ); - } - if ( pInpInChI[bMobileH][i].nAtom = (U_CHAR *) inchi_malloc( (len + 1) * sizeof(pInpInChI[0][0].nAtom[0]) ) ) { - memcpy(pInpInChI[bMobileH][i].nAtom, pInpInChI[nAltMobileH][i].nAtom, len); - pInpInChI[bMobileH][i].nAtom[len] = 0; - } else { - return RI_ERR_ALLOC; /* alloc failure */ - } - /* copy Hill formula */ - len = strlen( pInpInChI[nAltMobileH][i].szHillFormula)+1; - if ( pInpInChI[bMobileH][i].szHillFormula ) { - inchi_free( pInpInChI[bMobileH][i].szHillFormula ); - } - if ( pInpInChI[bMobileH][i].szHillFormula = (char *) inchi_malloc( inchi_max(len,2) ) ) { - memcpy( pInpInChI[bMobileH][i].szHillFormula, pInpInChI[nAltMobileH][i].szHillFormula, len); - } else { - return RI_ERR_ALLOC; /* alloc failure */ - } - } - - } else - if ( bMobileH == TAUT_YES ) { - int ret; - /* allocate InChI */ - nNumComponents = 1; - /* InChI */ - pnNumComponents[bMobileH] = nNumComponents; - if ( !( pInChI = (INChI *)inchi_calloc( nNumComponents, sizeof(INChI) ) ) ) { - return RI_ERR_ALLOC; /* alloc failure */ - } - pInpInChI[bMobileH] = pInChI; - ret = nFillOutProtonMobileH( pInChI ); - if ( ret < 0 ) { - return ret; - } - } - return 0; - } - return RI_ERR_SYNTAX; /* syntax error */ - } - if ( *pEnd ) { - return RI_ERR_SYNTAX; /* syntax error */ - } - - /* allocate InChI */ - if ( !( pInpInChI[bMobileH] = (INChI *)inchi_calloc( nNumComponents, sizeof(INChI) ) ) ) { - return RI_ERR_ALLOC; /* alloc failure */ - } - pInChI = pInpInChI[bMobileH]; - - /* Pass 2. Count elements, save formulas and elements */ - pStart = str; - iComponent = 0; - while( 1 ) { - if ( !(pEnd = strchr( pStart, '.' )) ) { - pEnd = pStart + strlen(pStart); - } - p = pStart; - if ( isdigit( UCINT *p ) ) { - mpy_component = (int)inchi_strtol( p, &q, 10 ); - p = q; - } else { - mpy_component = 1; - } -#if ( FIX_DALKE_BUGS == 1 ) - if ( iComponent + mpy_component > MAX_ATOMS ) { - return RI_ERR_SYNTAX; /* syntax error */ - } -#endif - len = pEnd-p; - for ( i = 0; i < mpy_component; i ++ ) { - if ( pInChI[iComponent+i].szHillFormula ) { - inchi_free( pInChI[iComponent+i].szHillFormula ); - } - pInChI[iComponent+i].szHillFormula = (char*) inchi_malloc( inchi_max(len,1)+1 ); - memcpy( pInChI[iComponent].szHillFormula, p, len ); - pInChI[iComponent+i].szHillFormula[len] = '\0'; - if ( !i ) { - /* Pass 2.1 Parse formula and count atoms except H */ - nNumAtoms = 0; - nNumH = 0; - nNumAtomsAndH = 0; - e = pInChI[iComponent].szHillFormula; - while ( *e ) { - if ( !isupper( UCINT *e ) ) { - return RI_ERR_SYNTAX; - } - j = 0; - szEl[j ++] = *e ++; - if ( *e && islower( UCINT *e ) ) - szEl[j ++] = *e ++; - szEl[j ++] = '\0'; - if ( *e && isdigit( UCINT *e ) ) { - mpy_atom = (int)inchi_strtol( e, &q, 10 ); - e = q; - } else { - mpy_atom = 1; - } - if ( !mpy_atom ) { - return RI_ERR_SYNTAX; - } - if ( szEl[0] == 'H' && !szEl[1] ) { - nNumH += mpy_atom; - continue; /* ignore H in counting number of atoms */ - } - - nNumAtoms += mpy_atom; - } -#if ( FIX_DALKE_BUGS == 1 ) - if ( nNumAtoms > MAX_ATOMS ) { - return RI_ERR_SYNTAX; /* syntax error */ - } -#endif - nNumAtomsAndH = nNumAtoms? nNumAtoms : (nNumH > 0); - pInChI[iComponent+i].nNumberOfAtoms = nNumAtomsAndH; - if ( pInChI[iComponent+i].nAtom ) { - inchi_free( pInChI[iComponent+i].nAtom ); - } - pInChI[iComponent+i].nAtom = (U_CHAR *) inchi_malloc((nNumAtomsAndH+1)*sizeof(pInChI[0].nAtom[0])); - if ( !pInChI[iComponent+i].nAtom ) - return RI_ERR_ALLOC; /* failed allocation */ - /* Pass 2.2 Store elements; this assumes no bridging H. Bridging H will be found in connection table, /c */ - iAtom = 0; - if ( nNumAtoms > 0 ) { - e = pInChI[iComponent+i].szHillFormula; - while ( *e ) { - if ( !isupper( UCINT *e ) ) { - return RI_ERR_SYNTAX; - } - j = 0; - szEl[j ++] = *e ++; - if ( *e && islower( UCINT *e ) ) - szEl[j ++] = *e ++; - szEl[j ++] = '\0'; - if ( *e && isdigit( UCINT *e ) ) { - mpy_atom = (int)inchi_strtol( e, &q, 10 ); - e = q; - } else { - mpy_atom = 1; - } - if ( !mpy_atom ) { - return RI_ERR_SYNTAX; - } - if ( szEl[0] == 'H' && !szEl[1] ) - continue; /* ignore H */ - el_number = get_periodic_table_number( szEl ); - if ( el_number == ERR_ELEM ) { - return RI_ERR_SYNTAX; /* wrong element */ - } - while ( mpy_atom -- ) { - if ( iAtom >= nNumAtoms ) { - return RI_ERR_PROGR; /* program error */ - } - pInChI[iComponent+i].nAtom[iAtom ++] = (U_CHAR)el_number; - } - } - } else - if ( nNumH > 0 ) { - pInChI[iComponent+i].nAtom[iAtom ++] = EL_NUMBER_H; - nNumAtoms = 1; - } - pInChI[iComponent+i].nAtom[iAtom] = '\0'; - if ( nNumAtoms != iAtom ) { - return RI_ERR_PROGR; /* program error */ - } - } else { - /* Copy duplicated formula */ - strcpy(pInChI[iComponent+i].szHillFormula, pInChI[iComponent].szHillFormula); - /* Copy atoms in the duplicated formula */ - pInChI[iComponent+i].nNumberOfAtoms = nNumAtoms; - if ( pInChI[iComponent+i].nAtom ) { - inchi_free( pInChI[iComponent+i].nAtom ); - } - pInChI[iComponent+i].nAtom = (U_CHAR *) inchi_malloc(nNumAtoms+1); - if ( !pInChI[iComponent+i].nAtom ) - return RI_ERR_ALLOC; /* failed allocation */ - memcpy( pInChI[iComponent+i].nAtom, pInChI[iComponent].nAtom, nNumAtoms+1 ); - } - } - iComponent += i; - if ( *pEnd ) { - if ( *pEnd != '.' ) { - return RI_ERR_SYNTAX; /* syntax error */ - } - pStart = pEnd+1; - } else - break; - } - - if ( iComponent != nNumComponents ) { - return RI_ERR_PROGR; /* program error */ - } - if ( bMobileH == TAUT_NON ) { - /* at this point the exact number of atoms including bridging H is known from TAUT_YES */ - for ( i = 0; i < nNumComponents && i < pnNumComponents[nAltMobileH]; i ++ ) { - if ( pInpInChI[bMobileH][i].nNumberOfAtoms < (len=pInpInChI[nAltMobileH][i].nNumberOfAtoms) ) { - /* there are bridging H in this component */ - if ( pInpInChI[nAltMobileH][i].nAtom ) { - U_CHAR *nAtom = (U_CHAR *) inchi_malloc( (len+1) * sizeof(nAtom[0]) ); - if ( !nAtom ) { - return RI_ERR_ALLOC; - } - memcpy( nAtom, pInpInChI[nAltMobileH][i].nAtom, len*sizeof(nAtom[0]) ); - nAtom[ len ] = 0; - if ( pInpInChI[bMobileH][i].nAtom ) { - inchi_free( pInpInChI[bMobileH][i].nAtom ); - } - pInpInChI[bMobileH][i].nAtom = nAtom; - } - pInpInChI[bMobileH][i].nNumberOfAtoms = len; - } - } - } - - return nNumComponents+1; -} - -/****************************************************************************************/ -int CopySegment( INChI *pInChITo, INChI *pInChIFrom, int SegmentType, int bIsotopicTo, int bIsotopicFrom) -{ - int ret = RI_ERR_ALLOC; - int len; - - - if ( SegmentType==CPY_SP2 || - SegmentType==CPY_SP3 || - SegmentType==CPY_SP3_M || - SegmentType==CPY_SP3_S ) { - - INChI_Stereo **pstereoTo = NULL; - INChI_Stereo *stereoFrom = bIsotopicFrom==1? pInChIFrom->StereoIsotopic : - bIsotopicFrom==0? pInChIFrom->Stereo : NULL; - if ( stereoFrom || bIsotopicFrom < 0 ) { - if ( SegmentType==CPY_SP2 ) { - if ( bIsotopicFrom < 0 || - stereoFrom->b_parity && - stereoFrom->nBondAtom1 && - stereoFrom->nBondAtom2 ) { - - len = (bIsotopicFrom < 0)? 0 : stereoFrom->nNumberOfStereoBonds; - pstereoTo = bIsotopicTo? &pInChITo->StereoIsotopic : &pInChITo->Stereo; - if ( !pstereoTo[0] ) { - if ( !(pstereoTo[0] = (INChI_Stereo *)inchi_calloc( 1, sizeof(**pstereoTo))) ) { - goto exit_function; - } - } - if ( pstereoTo[0]->nNumberOfStereoBonds > 0 || pstereoTo[0]->b_parity || - pstereoTo[0]->nBondAtom1 || pstereoTo[0]->nBondAtom2 ) { - ret = RI_ERR_SYNTAX; /* stereo already exists */ - goto exit_function; - } - /* allocate sp2 stereo */ - if ( !(pstereoTo[0]->b_parity = (S_CHAR *)inchi_calloc( len+1, sizeof(pstereoTo[0]->b_parity[0]) ) ) || - !(pstereoTo[0]->nBondAtom1 = (AT_NUMB *)inchi_calloc( len+1, sizeof(pstereoTo[0]->nBondAtom1[0]) ) ) || - !(pstereoTo[0]->nBondAtom2 = (AT_NUMB *)inchi_calloc( len+1, sizeof(pstereoTo[0]->nBondAtom2[0]) ) ) ) { - /* cleanup */ - if ( pstereoTo[0]->b_parity ) { - INCHI_HEAPCHK - inchi_free( pstereoTo[0]->b_parity ); - pstereoTo[0]->b_parity = NULL; - } - if ( pstereoTo[0]->nBondAtom1 ) { - INCHI_HEAPCHK - inchi_free( pstereoTo[0]->nBondAtom1 ); - pstereoTo[0]->nBondAtom1 = NULL; - } - if ( pstereoTo[0]->nBondAtom2 ) { - INCHI_HEAPCHK - inchi_free( pstereoTo[0]->nBondAtom2 ); - pstereoTo[0]->nBondAtom2 = NULL; - } - INCHI_HEAPCHK - goto exit_function; - } - /* copy stereo */ - if ( bIsotopicFrom >= 0 && len ) { - memcpy( pstereoTo[0]->b_parity, stereoFrom->b_parity, (len+1)*sizeof(pstereoTo[0]->b_parity[0]) ); - memcpy( pstereoTo[0]->nBondAtom1, stereoFrom->nBondAtom1, (len+1)*sizeof(pstereoTo[0]->nBondAtom1[0]) ); - memcpy( pstereoTo[0]->nBondAtom2, stereoFrom->nBondAtom2, (len+1)*sizeof(pstereoTo[0]->nBondAtom2[0]) ); - } - pstereoTo[0]->nNumberOfStereoBonds = len; - - return len+1; - } else { - return 0; - } - } else - if ( SegmentType==CPY_SP3 ) { - if ( bIsotopicFrom < 0 || - stereoFrom->t_parity && - stereoFrom->nNumber ) { - - len = (bIsotopicFrom < 0)? 0 : stereoFrom->nNumberOfStereoCenters; - pstereoTo = bIsotopicTo? &pInChITo->StereoIsotopic : &pInChITo->Stereo; - if ( !pstereoTo[0] ) { - if ( !(pstereoTo[0] = (INChI_Stereo *)inchi_calloc( 1, sizeof(**pstereoTo))) ) { - goto exit_function; - } - } - if ( pstereoTo[0]->nNumberOfStereoCenters > 0 || pstereoTo[0]->t_parity || - pstereoTo[0]->nNumber ) { - ret = RI_ERR_SYNTAX; /* stereo already exists */ - goto exit_function; - } - /* allocate sp3 stereo */ - if ( !(pstereoTo[0]->t_parity = (S_CHAR *)inchi_calloc( len+1, sizeof(pstereoTo[0]->b_parity[0]) ) ) || - !(pstereoTo[0]->nNumber = (AT_NUMB *)inchi_calloc( len+1, sizeof(pstereoTo[0]->nBondAtom1[0]) ) ) ) { - /* cleanup */ - if ( pstereoTo[0]->t_parity ) { - inchi_free( pstereoTo[0]->t_parity ); - pstereoTo[0]->t_parity = NULL; - } - if ( pstereoTo[0]->nNumber ) { - inchi_free( pstereoTo[0]->nNumber ); - pstereoTo[0]->nNumber = NULL; - } - goto exit_function; - } - /* copy stereo */ - if ( bIsotopicFrom >= 0 && len ) { - memcpy( pstereoTo[0]->t_parity, stereoFrom->t_parity, (len+1)*sizeof(pstereoTo[0]->t_parity[0]) ); - memcpy( pstereoTo[0]->nNumber, stereoFrom->nNumber, (len+1)*sizeof(pstereoTo[0]->nNumber[0]) ); - } - pstereoTo[0]->nNumberOfStereoCenters = len; - return len+1; - } else { - return 0; - } - } else - if ( SegmentType==CPY_SP3_M ) { - pstereoTo = bIsotopicTo? &pInChITo->StereoIsotopic : &pInChITo->Stereo; - if ( !pstereoTo[0] ) { - if ( !(pstereoTo[0] = (INChI_Stereo *)inchi_calloc( 1, sizeof(**pstereoTo))) ) { - goto exit_function; - } - } - if ( pstereoTo[0]->nCompInv2Abs && NO_VALUE_INT != pstereoTo[0]->nCompInv2Abs ) { - ret = RI_ERR_SYNTAX; /* stereo already exists */ - goto exit_function; - } - if ( bIsotopicFrom < 0 ) { - pstereoTo[0]->nCompInv2Abs = 0; - } else { - pstereoTo[0]->nCompInv2Abs = stereoFrom->nCompInv2Abs; - } - return 1; - } else - /* use bTrivialInv to save /s1, /s2, /s3 */ - if ( SegmentType==CPY_SP3_S ) { - pstereoTo = bIsotopicFrom? &pInChITo->StereoIsotopic : &pInChITo->Stereo; - if ( !pstereoTo[0] ) { - if ( !(pstereoTo[0] = (INChI_Stereo *)inchi_calloc( 1, sizeof(**pstereoTo))) ) { - goto exit_function; - } - } - if ( pstereoTo[0]->bTrivialInv ) { - ret = RI_ERR_SYNTAX; /* stereo already exists */ - goto exit_function; - } - pstereoTo[0]->bTrivialInv = stereoFrom->bTrivialInv; - if ( bIsotopicFrom < 0 ) { - pstereoTo[0]->bTrivialInv = 0; - } else { - pstereoTo[0]->bTrivialInv = stereoFrom->bTrivialInv; - } - return 1; - } - } - return 0; /* nothing to copy */ - } else - if ( SegmentType == CPY_ISO_AT ) { - int nNumberOfIsotopicAtoms = pInChIFrom->nNumberOfIsotopicAtoms; - INChI_IsotopicAtom **pIsotopicAtomTo = NULL; - INChI_IsotopicAtom *IsotopicAtomFrom = pInChIFrom->IsotopicAtom; - if ( bIsotopicFrom < 0 || IsotopicAtomFrom ) { - len = (bIsotopicFrom < 0)? 0 : nNumberOfIsotopicAtoms; - pIsotopicAtomTo = &pInChITo->IsotopicAtom; - if ( !*pIsotopicAtomTo ) { - if ( !( *pIsotopicAtomTo = (INChI_IsotopicAtom *)inchi_calloc( len+1, sizeof(**pIsotopicAtomTo) ) ) ) { - goto exit_function; - } - } - if ( pInChITo->nNumberOfIsotopicAtoms ) { - ret = RI_ERR_SYNTAX; /* stereo already exists */ - goto exit_function; - } - if ( bIsotopicFrom >= 0 && len ) { - memcpy( *pIsotopicAtomTo, IsotopicAtomFrom, (len+1)*sizeof(**pIsotopicAtomTo) ); - } - pInChITo->nNumberOfIsotopicAtoms = len; - return len+1; - } - return 0; - } - ret = RI_ERR_PROGR; /* program error */ -exit_function: - return ret; - - -} - -/**********************************************************************************/ -/* Sort neighbors in ascending order */ -int insertions_sort_AT_NUMB( AT_NUMB *base, int num ) -{ - AT_NUMB *i, *j, *pk, tmp; - int k, num_trans = 0; - for( k=1, pk = base; k < num; k++, pk ++ ) { - for( j = (i = pk) + 1, tmp = *j; j > base && *i > tmp; j=i, i -- ) { - *j = *i; - num_trans ++; - } - *j = tmp; - } - return num_trans; -} - - -/* read */ - -int getInChIChar( INCHI_IOSTREAM *pInp ) -{ - if (pInp->type==INCHI_IOSTREAM_STRING) - { - /* input from string */ - if ( pInp->s.nPtr < pInp->s.nUsedLength ) - return (int) pInp->s.pStr[pInp->s.nPtr++]; - return RI_ERR_EOF; - } - - else - { - /* input from plain file */ - int c; -#if ( defined(_MSC_VER)&&defined(_WIN32) || defined(__BORLANDC__)&&defined(__WIN32__) || defined(__GNUC__)&&defined(__MINGW32__)&&defined(_WIN32) ) - do - { - c = getc( pInp->f ); - if ( c == EOF ) - { - c = RI_ERR_EOF; - break; - } - } - while( c == '\r' ); -#else - c = getc( pInp->f ); - if ( c == EOF ) - { - c = RI_ERR_EOF; - } -#endif - return c; - } - - -} - -int AddInChIChar( INCHI_IOSTREAM *pInp, SEGM_LINE *Line, const char *pszToken ) -{ - int c = getInChIChar( pInp ); - /* - while ( c == '\r' ) { - c = getInChIChar( pInp ); - } - */ - INCHI_HEAPCHK - if ( Line->len + 2 >= Line->len_alloc ) { - char *str = (char *) inchi_calloc( Line->len_alloc + SEGM_LINE_ADD, sizeof(str[0]) ); - INCHI_HEAPCHK - if ( str ) { - if ( Line->len > 0 && Line->str ) { - memcpy( str, Line->str, sizeof(str[0]) * Line->len ); - Line->len_alloc += SEGM_LINE_ADD; - inchi_free( Line->str ); - INCHI_HEAPCHK - } else { - Line->len_alloc += SEGM_LINE_ADD; - } - Line->str = str; - } else { - c = RI_ERR_ALLOC; /* fatal error */ - goto exit_function; - } - } - INCHI_HEAPCHK - if ( c < 0 ) { - Line->str[Line->len] = '\0'; - INCHI_HEAPCHK - c = RI_ERR_SYNTAX; /* fatal error: wrong char */ - goto exit_function; - } - if ( c && strchr( pszToken, c ) ) { - Line->str[Line->len] = '\0'; - INCHI_HEAPCHK - c = -(c+2); - goto exit_function; - } else - if ( !c && !Line->len ) { - Line->str[Line->len] = c; - INCHI_HEAPCHK - } else { - Line->str[Line->len ++] = c; - INCHI_HEAPCHK - } -exit_function: - INCHI_HEAPCHK - return c; -} -int nGetInChISegment( INCHI_IOSTREAM *pInp, SEGM_LINE *Line, const char *pszToken ) -{ - int c; - Line->len = 0; - while( 0 < (c = AddInChIChar( pInp, Line, pszToken ) ) ) - ; - if ( c < - 2 ) { - c = -(c+2); - } - Line->c = c; - return c; -} -/********************************************************************************/ -/* add one more bond to the linked lists for both neighbors */ -int AddLinkedBond( AT_NUMB at1, AT_NUMB at2, AT_NUMB num_at, LINKED_BONDS *pLB ) -{ - int nReqLen = inchi_max( 2*num_at+2, pLB->len + 2 ); - AT_NUMB prev; - if ( pLB->len_alloc <= nReqLen ) { - /*int nNewLen = nReqLen + (nReqLen + LINKED_BOND_ADD - 1)%LINKED_BOND_ADD + LINKED_BOND_ADD;*/ - int nNewLen = nReqLen - nReqLen%LINKED_BOND_ADD + 2*LINKED_BOND_ADD; - ONE_LINKED_BOND *pBond = (ONE_LINKED_BOND *)inchi_calloc( nNewLen, sizeof(pBond[0]) ); - if ( !pBond ) - return RI_ERR_ALLOC; /* allocation error */ - if ( pLB->pBond && pLB->len ) { - memcpy( pBond, pLB->pBond, pLB->len*sizeof(pBond[0]) ); - } - if ( pLB->pBond ) - inchi_free( pLB->pBond ); - pLB->pBond = pBond; - pLB->len_alloc = nNewLen; - } - if ( !pLB->len ) { - pLB->len = num_at+1; - memset( pLB->pBond, 0, (num_at+1)*sizeof(pLB->pBond[0]) ); - } - - prev = pLB->pBond[at1].prev; /* position of the last neighbor of at1 in the pLB->pBond */ - if ( !prev ) { - pLB->pBond[at1].neigh = at2; - pLB->pBond[at1].prev = at1; - } else { - pLB->pBond[pLB->len].neigh = at2; - pLB->pBond[pLB->len].prev = prev; - pLB->pBond[at1].prev = pLB->len ++; - } - - prev = pLB->pBond[at2].prev; /* position of the last neighbor of at2 in the pLB->pBond */ - if ( !prev ) { - pLB->pBond[at2].neigh = at1; - pLB->pBond[at2].prev = at2; - } else { - pLB->pBond[pLB->len].neigh = at1; - pLB->pBond[pLB->len].prev = prev; - pLB->pBond[at2].prev = pLB->len ++; - } - return 0; -} - -#endif /* READ_INCHI_STRING */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichiring.c b/INCHI-1-SRC/INCHI_API/inchi_dll/ichiring.c deleted file mode 100644 index 894677e..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichiring.c +++ /dev/null @@ -1,358 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - -#include "mode.h" - -#include "inpdef.h" -#include "extr_ct.h" -#include "ichiring.h" - -/* local prototypes */ -int GetMinRingSize( inp_ATOM* atom, QUEUE *q, AT_RANK *nAtomLevel, S_CHAR *cSource, AT_RANK nMaxRingSize ); - - - - -/*******************************************************************/ -/* add to the queue */ -int QueueAdd( QUEUE *q, QINT_TYPE *Val ); -/* read & remove from the queue */ -int QueueGet( QUEUE *q, QINT_TYPE *Val ); -/* read from the queue */ -int QueueGetAny( QUEUE *q, QINT_TYPE *, int ord ); -/* initialize the queue */ -int QueueReinit( QUEUE *q ); -/* current queue length */ -int QueueLength( QUEUE *q ); -/* number of used queue internal elements */ -int QueueWrittenLength( QUEUE *q ); - - -#if ( QUEUE_QINT == 1 ) /* { */ - -QUEUE *QueueCreate( int nTotLength, int nSize ) -{ - QUEUE *q = NULL; - QINT_TYPE *Val = NULL; - if ( nTotLength < 1 || nSize != (int)sizeof(QINT_TYPE) || - !(q = (QUEUE *) inchi_calloc( 1, sizeof(QUEUE)) ) || - !(Val = (QINT_TYPE *) inchi_calloc( nTotLength, nSize) )) { - if ( q ) inchi_free(q); - return NULL; - } - q->Val = Val; - /* q->nSize = nSize; */ - q->nTotLength = nTotLength; - return q; -} -int QueueAdd( QUEUE *q, QINT_TYPE *Val ) -{ - if ( q && Val && q->nLength < q->nTotLength ) { - q->Val[ (q->nFirst + q->nLength) % q->nTotLength ] = *Val; - q->nLength ++; - return q->nLength; - } - return -1; -} -int QueueGet( QUEUE *q, QINT_TYPE *Val ) -{ - if ( q && Val && q->nLength > 0 ) { - *Val = q->Val[ q->nFirst ]; - /* new: do not allow to overwrite the retrieved value */ - q->nFirst = (q->nFirst == q->nTotLength - 1)? 0 : q->nFirst + 1; - q->nLength --; - /* -- old -- - if ( -- q->nLength ) { - q->nFirst = (q->nFirst == q->nTotLength - 1)? 0 : q->nFirst + 1; - } - */ - return q->nLength; - } - return -1; -} -int QueueGetAny( QUEUE *q, QINT_TYPE *Val, int ord ) -{ - if ( 0 <= ord && ord < q->nTotLength ) { - *Val = q->Val[ ord ]; - return 1; /* success */ - } else { - return -1; /* error */ - } -} - -#else /* } QUEUE_QINT == 1 { */ - -QUEUE *QueueCreate( int nTotLength, int nSize ) -{ - QUEUE *q = NULL; - QINT_TYPE *Val = NULL; - if ( nTotLength < 1 || nSize < 1 || - !(q = (QUEUE *) inchi_calloc( 1, sizeof(QUEUE)) ) || - !(Val = (QINT_TYPE *) inchi_calloc( nTotLength, nSize) )) { - if ( q ) inchi_free(q); - return NULL; - } - q->Val = Val; - q->nSize = nSize; - q->nTotLength = nTotLength; - return q; -} -int QueueAdd( QUEUE *q, QINT_TYPE *Val ) -{ - if ( q && Val && q->nLength < q->nTotLength ) { - memcpy( (char*)q->Val + ((q->nFirst + q->nLength) % q->nTotLength)*q->nSize, Val, q->nSize); - q->nLength ++; - return q->nLength; - } - return -1; -} -int QueueGet( QUEUE *q, QINT_TYPE *Val ) -{ - if ( q && Val && q->nLength > 0 ) { - memcpy( Val, (char*)q->Val + q->nFirst * q->nSize, q->nSize); - if ( -- q->nLength ) { - q->nFirst = (q->nFirst == q->nTotLength - 1)? 0 : q->nFirst + 1; - } - return q->nLength; - } - return -1; -} -int QueueGetAny( QUEUE *q, QINT_TYPE *Val, int ord ) -{ - if ( 0 <= ord && ord < q->nTotLength ) { - memcpy( Val, (char*)q->Val + ord * q->nSize, q->nSize); - return 1; /* success */ - } else { - return -1; /* error */ - } -} - -#endif /* } QUEUE_QINT == 1 */ - -QUEUE *QueueDelete( QUEUE *q ) -{ - if ( q ) { - if ( q->Val ) inchi_free(q->Val); - inchi_free( q ); - } - return NULL; -} -int QueueReinit( QUEUE *q ) -{ - if ( q ) { - q->nFirst = 0; - q->nLength = 0; - /* memset( q->Val, 0, q->nTotLength*sizeof(q->Val[0])); */ /* for debug only */ - return q->nTotLength; - } - return -1; -} -int QueueLength( QUEUE *q ) -{ - if ( q ) { - return q->nLength; - } else { - return 0; - } -} -int QueueWrittenLength( QUEUE *q ) -{ - if ( q ) { - int len = q->nFirst+q->nLength; - return (len > q->nTotLength)? q->nTotLength : len; - } else { - return 0; - } -} - -/**********************************************************************************/ -/* BFS: Breadth First Search */ -int GetMinRingSize( inp_ATOM* atom, QUEUE *q, AT_RANK *nAtomLevel, S_CHAR *cSource, AT_RANK nMaxRingSize ) -{ - int qLen, i, j; - AT_RANK nCurLevel, nRingSize, nMinRingSize=MAX_ATOMS+1; - qInt at_no, next; - int iat_no, inext; - - while ( qLen = QueueLength( q ) ) { - /* traverse the next level (next outer ring) */ - for ( i = 0; i < qLen; i ++ ) { - if ( 0 <= QueueGet( q, &at_no ) ) { - iat_no = (int)at_no; - nCurLevel = nAtomLevel[iat_no] + 1; - if ( 2*nCurLevel > nMaxRingSize + 4 ) { - /* 2*nCurLevel = nRingSize + 3 + k, k = 0 or 1 */ - if ( nMinRingSize < MAX_ATOMS+1 ) { - return (nMinRingSize >= nMaxRingSize)? 0 : nMinRingSize; - } - return 0; /* min. ring size > nMaxRingSize */ - } - for ( j = 0; j < atom[iat_no].valence; j ++ ) { - next = (qInt)atom[iat_no].neighbor[j]; - inext = (int)next; - if ( !nAtomLevel[inext] ) { - /* the at_no neighbor has not been traversed yet. Add it to the queue */ - if ( 0 <= QueueAdd( q, &next ) ) { - nAtomLevel[inext] = nCurLevel; - cSource[inext] = cSource[iat_no]; /* keep the path number */ - } else { - return -1; /* error */ - } - } else - if ( nAtomLevel[inext]+1 >= nCurLevel && - cSource[inext] != cSource[iat_no] - /* && cSource[(int)next] != -1 */ - ) { - /* found a ring closure */ - /* debug */ - if ( cSource[inext] == -1 ) { - return -1; /* error */ - } - if ( (nRingSize = nAtomLevel[inext] + nCurLevel - 2) < nMinRingSize ) { - nMinRingSize = nRingSize; - } - /* return (nRingSize >= nMaxRingSize)? 0 : nRingSize; */ - } - } - } else { - return -1; /* error */ - } - } - } - - if ( nMinRingSize < MAX_ATOMS+1 ) { - return (nMinRingSize >= nMaxRingSize)? 0 : nMinRingSize; - } - - return 0; -} -/*******************************************************************/ -/* Return value: - 0: nMaxRingSize < 3 or - min. ring size >= nMaxRingSize or - not a ring bond (the last is currently impossible: bond is known to belong to a ring system. - n>0: min. ring size < nMaxRingSize - n<0: error - - Input: - atom[] - at_no number of the 1st atom adjacent to the bond - neigh_ord ordering number of the bond in question: at[at_no].bond_type[neigh_ord] - q queue structure - nAtomLevel work array, DFS distance - cSource work array, origin mark -*/ - -int is_bond_in_Nmax_memb_ring( inp_ATOM* atom, int at_no, int neigh_ord, QUEUE *q, AT_RANK *nAtomLevel, S_CHAR *cSource, AT_RANK nMaxRingSize ) -{ - int nMinRingSize = -1, i; - qInt n; - int nTotLen; - - if ( nMaxRingSize < 3 ) { - return 0; - } - - QueueReinit( q ); - - /* mark the starting atom */ - nAtomLevel[at_no] = 1; - cSource[at_no] = -1; - /* add neighbors */ - for ( i = 0; i < atom[at_no].valence; i ++ ) { - n = (qInt)atom[at_no].neighbor[i]; - nAtomLevel[(int)n] = 2; - cSource[(int)n] = 1 + (i==neigh_ord); - QueueAdd( q, &n ); - } - - nMinRingSize = GetMinRingSize( atom, q, nAtomLevel, cSource, nMaxRingSize ); - /* cleanup */ - nTotLen = QueueWrittenLength( q ); - for ( i = 0; i < nTotLen; i ++ ) { - if ( 0 < QueueGetAny( q, &n, i ) ) { - nAtomLevel[(int)n] = 0; - cSource[(int)n] = 0; - } - } - nAtomLevel[at_no] = 0; - cSource[at_no] = 0; - - -/* - if ( nAtomLevel ) - inchi_free ( nAtomLevel ); - if ( cSource ) - inchi_free ( cSource ); - QueueDelete( q ); -*/ - return nMinRingSize; -} -/*******************************************************************/ -int is_atom_in_3memb_ring( inp_ATOM* atom, int at_no ) -{ - AT_NUMB neigh_neigh; - int i, j, k, val, val_neigh, neigh; - - if ( atom[at_no].nNumAtInRingSystem < 3 ) { - return 0; - } - - for ( i = 0, val = atom[at_no].valence; i < val; i ++ ) { - neigh = (int)atom[at_no].neighbor[i]; - if ( atom[at_no].nRingSystem != atom[neigh].nRingSystem ) - continue; - for ( j = 0, val_neigh = atom[neigh].valence; j < val_neigh; j ++ ) { - neigh_neigh = atom[neigh].neighbor[j]; - if ( (int)neigh_neigh == at_no ) - continue; - for ( k = 0; k < val; k ++ ) { - if ( atom[at_no].neighbor[k] == neigh_neigh ) { - return 1; - } - } - } - } - return 0; -} diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichiring.h b/INCHI-1-SRC/INCHI_API/inchi_dll/ichiring.h deleted file mode 100644 index 7ee2498..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichiring.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHIRING_H__ -#define __INCHIRING_H__ -#define QUEUE_QINT 1 -typedef AT_RANK qInt; /* queue optimization: known type */ - -#if ( QUEUE_QINT == 1 ) -#define QINT_TYPE qInt -#else -#define QINT_TYPE void -#endif - -typedef struct tagQieue { - QINT_TYPE *Val; - int nTotLength; - int nFirst; /* element to remove if nLength > 0 */ - int nLength; /* (nFirst + nLength) is next free position */ -#if ( QUEUE_QINT != 1 ) - int nSize; -#endif -}QUEUE; - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -QUEUE *QueueCreate( int nTotLength, int nSize ); -QUEUE *QueueDelete( QUEUE *q ); -int is_bond_in_Nmax_memb_ring( inp_ATOM* atom, int at_no, int neigh_ord, QUEUE *q, AT_RANK *nAtomLevel, S_CHAR *cSource, AT_RANK nMaxRingSize ); -int is_atom_in_3memb_ring( inp_ATOM* atom, int at_no ); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /* __INCHIRING_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichirvr1.c b/INCHI-1-SRC/INCHI_API/inchi_dll/ichirvr1.c deleted file mode 100644 index 4234e16..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichirvr1.c +++ /dev/null @@ -1,4953 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - -/*^^^ */ -/*#define CHECK_WIN32_VC_HEAP*/ -#include "mode.h" - -#if ( READ_INCHI_STRING == 1 ) - -#include "ichi.h" -#include "ichitime.h" - -#include "inpdef.h" -#include "ichimain.h" -#include "ichierr.h" -#include "incomdef.h" -#include "ichiring.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "util.h" - -#include "ichicomp.h" -#include "ichister.h" - -#include "ichi_bns.h" - -#include "strutil.h" - -#include "ichirvrs.h" - - -/************************************************************************************************** - - ChargeStruct fictitios structures MY_CONST CN_LIST cnList[*] - ============================================================ - - - bond flow (+) => Positive charge c-group - ----------------- (-) => Negative charge c-group - Single 0 (+C) => Positive charge group for C, Si, Ge, Sn, Pb - Double 1 (-C) => Negative charge group for C, Si, Ge, Sn, Pb - Triple 2 (.) => additional one unit of st_cap - - A) Interpretation: - - X-(-) or X=(+) => zero charge - X=(-) or X-(+) => charge = -1 or +1, respectively - - B) Information to keep: - - ordering zero-based number of the edge - to (+) or (-) from the Interpretation (A) section - - vCap = vertex cap - vFlow = vertex flow - val = number of edges incident to the vertex - neigh = 1-based ordering number of the adjacent vertex; 0 => no more adjacent vertices - cap = cap of the edge to the adjacent vertex - flow = flow of the edge to the adjacent vertex - - atom (c-point) always has number 1 - c-group(s) always are the last vertices - always adjacent_neigh_number > vertex_number, that is, neigh > vertex - - Contribution to the Total Charge: - ---------------------------------- - edge_cap(+) - edge_flow(+) - edge_flow(-) - Delta(+) - Delta(-) - - where edge_cap(+) is edge capacity to c-group (+); - edge_flow(+) is edge capacity to c-group (?), (?)= (+) or (-); - Delta(?) = st_cap(?) - st_floe(?) of the c-group vertex (?), (?)= (+) or (-); - -***************************************************************************************************/ -/************************************************************************************************** - - Important: - vCap and vFlow Note: since metal charge group (vert. 2-4) MUST be registered before - marked with empty the "metal flower" (5-8) all charge group vertex numbers are - comments for vertices less than metal flower vertices: (2,3,4) < (5,6,7,8) - 1 and 5(M) should This MAY be neded for c-group enumeration. The order is: - be set separately t-groups, c-groups, M-flower. All types BNS_VT_M_GROUP allows to avoid duplications. - - 3(+) - || (Metal) - || \|/ init charge=0; MAX_METAL_CHARGE = 16 - 4(-) 5(M) 2 -Fe- CAP(BOND_TO_BNS_VT_M_GROUP) = NUM_BONDS*CAP - \ | / | - \ | / - 1 X(V), V=valence -*/ -MY_CONST C_NODE cnMe[5] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM,0/**/ ,0/**/,3}, {{ 2, 16,0, 0 },{ 4, 16,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_CHRG_STRUCT,16, 16, 2}, {{ 3, 16,0,16 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_C_POS_M, 16, 16, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 2 }, { 0, 0,0, 0 }} }, /* 3 */ - { {BNS_VT_C_NEG_M, 0+16, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ - { {BNS_VT_M_GROUP, 0/**/ ,0/**/,3}, {{ 1, 3,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ -}; -/* -#define cn_bits_Me (-1) -*/ - -/************************************************************************************************** - c=2 5(+.) - _____ / (PNPN) - 4=====3 |||| init charge=0 - c=2 \ / c=1 -N- - 2 | - |||| - 1 X+(V), X(V+1), X+(V+2), X(V+3); V=valence -*/ -MY_CONST C_NODE cnPNPN[5] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 3, 3, 1}, {{ 2, 3,0, 3 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_CHRG_STRUCT, 3, 3, 3}, {{ 3, 1,0, 0 },{ 4, 2,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 5, 1,0, 0 },{ 4, 2,0, 2 }, { 0, 0,0, 0 }} }, /* 3 */ - { {BNS_VT_CHRG_STRUCT, 2, 2, 2}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ - { {BNS_VT_C_POS, 1+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ -}; -/* -#define cn_bits_PNPN MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_P, cn_bits_N) -*/ -/************************************************************************************************** - 5(+) - c=1 // (NPNP) - 4=====3 |||| init charge=0 - c=1 \ / c=2 -N- - 2 | - |||| - 1 X(V), X+(V+1), X(V+2), X+(V+3); V=valence -*/ -MY_CONST C_NODE cnNPNP[5] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 3, 3, 1}, {{ 2, 3,0, 3 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_CHRG_STRUCT, 3, 3, 3}, {{ 3, 2,0, 0 },{ 4, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 5, 1,0, 1 },{ 4, 1,0, 1 }, { 0, 0,0, 0 }} }, /* 3 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ - { {BNS_VT_C_POS, 0+1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ -}; -/* -#define cn_bits_NPNP MAKE_CN_BITS(cn_bits_N, cn_bits_P, cn_bits_N, cn_bits_P) -*/ -/********************* end new ********************************************************************/ - - -/************************************************************************************************** - 5(+) - // (NPN) - 4=====3 ||| init charge=0 - \ / -N- - 2 | - ||| - 1 X(V), X+(V+1), X(V+2); V=valence -*/ -MY_CONST C_NODE cnNPN[5] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 2, 2, 1}, {{ 2, 2,0, 2 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 3, 1,0, 0 },{ 4, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 5, 1,0, 1 },{ 4, 1,0, 1 }, { 0, 0,0, 0 }} }, /* 3 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ - { {BNS_VT_C_POS, 1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ -}; -/* -#define cn_bits_NPN MAKE_CN_BITS(cn_bits_N, cn_bits_P, cn_bits_N, 0) -*/ -/************************************************************************************************** - 5(+.) - / (PNP) - 4=====3 ||| init charge=0 - \ / -Cl- - 2 /\ - ||| - 1 X+(V), X(V+1), X+(V+2); V=valence -*/ -MY_CONST C_NODE cnPNP[5] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 2, 2, 1}, {{ 2, 2,0, 2 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 3, 1,0, 0 },{ 4, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 3}, {{ 5, 1,0, 0 },{ 4, 1,0, 1 }, { 0, 0,0, 0 }} }, /* 3 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ - { {BNS_VT_C_POS, 1+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ -}; -/* -#define cn_bits_PNP MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_P, 0) -*/ -/************************************************************************************************** - - (MNP) - \ / init charge=0 - N(.) - 3(-) 2(+) / \ - \ // - 1(.) X-(V), X(V+1), X+(V+2); V=valence -*/ -MY_CONST C_NODE cnMNP[3] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 2, 1, 2}, {{ 2, 1,0, 1 },{ 3, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_C_POS, 1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} } /* 3 */ -}; -/* -#define cn_bits_MNP MAKE_CN_BITS(cn_bits_M, cn_bits_N, cn_bits_P, 0) -*/ -#ifdef NEVER -/************************** not used ************************************************************** - (PNM) - 5(-) 4(+) \\ / - \ // B(.) init charge=0 - 3 2 / \ - \\ / - 1(.) X+(V), X(V+1), X+(V+2); V=valence -*/ -MY_CONST C_NODE cnPNM[5] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 2, 1, 2}, {{ 2, 1,0, 0 },{ 3, 1,0, 1 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 4, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 5, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 3 */ - { {BNS_VT_C_POS, 1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ - { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ -}; - -#define cn_bits_PNM MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_M, 0) - -#endif - -/************************************************************************************************** - 4(-) 3(+.) (PNM) - \ / ||| init charge=0 - 2 --P-- - ||| / \ - 1 X-(V), X(V+1), X+(V+2); V=valence -*/ -MY_CONST C_NODE cnPNM[4] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 2, 2, 1}, {{ 2, 2,0, 2 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 3, 1,0, 0 },{ 4, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_C_POS, 1+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 3 */ - { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ -}; /* explanaton of vCap: ^ ^ */ -/* additional dot:/ \ capacity of the edge to (+) or (-) vertex */ -/* -#define cn_bits_PNM MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_M, 0) -*/ -/************************************************************************************************** - 5(+C) - // init charge=0 - 6(-C) 4 - \ / (EN) E=either +1 or -1 - 3 | - || -C(.)- - 2 | - | - 1(.) X-(V), X+(V), X(V+1); V=valence -*/ -MY_CONST C_NODE cnEN[6] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 1, 0, 1}, {{ 2, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 3, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 3}, {{ 4, 1,0, 0 },{ 6, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 3 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 5, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ - { {BNS_VT_C_POS_C, 0+1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ - { {BNS_VT_C_NEG_C, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} } /* 6 */ -}; -/* -#define cn_bits_EN MAKE_CN_BITS(cn_bits_P | cn_bits_M, cn_bits_N, 0, 0) -*/ -/************************************************************************************************** - 5(-) - / (NMN) init charge=0 - 4=====3 ||| - \ / -X- - 2 /\ - ||| - 1 X(V), X-(V+1), X(V+2); V=valence -*/ -MY_CONST C_NODE cnNMN[5] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 2, 2, 1}, {{ 2, 2,0, 2 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 3, 1,0, 0 },{ 4, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 3}, {{ 5, 1,0, 0 },{ 4, 1,0, 1 }, { 0, 0,0, 0 }} }, /* 3 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ - { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} } /* 5 */ -}; -/* -#define cn_bits_NMN MAKE_CN_BITS(cn_bits_N, cn_bits_M, cn_bits_N, 0) -*/ -/************************************************************************************************** - 4(+) - // (NE) E=either +1 or -1 - 5(-) 3 || - \ / -X- init charge=0 - 2 | - || - 1 X(V), X+(V+1), X-(V+1); V=valence -*/ -MY_CONST C_NODE cnNE[5] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 1, 1, 1}, {{ 2, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 3}, {{ 3, 1,0, 0 },{ 5, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 4, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 3 */ - { {BNS_VT_C_POS, 0+1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ - { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} } /* 5 */ -}; -/* -#define cn_bits_NE MAKE_CN_BITS(cn_bits_N, cn_bits_P | cn_bits_M, 0, 0) -*/ -/************************************************************************************************** -6(-) 5(+) - \ // (NEN) - 4=====3 ||| init charge=0 - \ / -X- - 2 | - ||| - 1 X(V), X+(V+1), X-(V+1), X(V+2); V=valence -*/ -MY_CONST C_NODE cnNEN[6] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 2, 2, 1}, {{ 2, 2,0, 2 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 3, 1,0, 0 },{ 4, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 5, 1,0, 1 },{ 4, 1,0, 1 }, { 0, 0,0, 0 }} }, /* 3 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 3}, {{ 6, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ - { {BNS_VT_C_POS, 0+1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ - { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 6 */ -}; -/* -#define cn_bits_NEN MAKE_CN_BITS(cn_bits_N, cn_bits_M | cn_bits_N, cn_bits_N, 0) -*/ -/*=======================================================*/ -/************************************************************************************************** - (NP) - || - -X- init charge=0 - 2(+) | - || - 1 X(V), X+(V+1); V=valence -*/ -MY_CONST C_NODE cnNP[2] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 1, 1, 1}, {{ 2, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_C_POS, 0+1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ -}; -/* -#define cn_bits_NP MAKE_CN_BITS(cn_bits_N, cn_bits_P, 0, 0) -*/ -/************************************************************************************************** - (PN) - 3(+.) || init charge=0 [because cap(+)-flow(+)-Delta=1-0-1=0] - | -X- - 2 | - || - 1 X+(V), X(V+1); V=valence -*/ -MY_CONST C_NODE cnPN[3] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 1, 1, 1}, {{ 2, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 3, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_C_POS, 1+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 3 */ -}; -/* -#define cn_bits_PN MAKE_CN_BITS(cn_bits_P, cn_bits_N, 0, 0) -*/ -/************************************************************************************************** - (NM) - 3(-) || init charge=0 - | -X- - 2 | - || - 1 X(V), X-(V+1); V=valence -*/ -MY_CONST C_NODE cnNM[3] = { - /* vertex type vCap vFlow; val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 1, 1, 1}, {{ 2, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 3, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 3 */ -}; -/* -#define cn_bits_NM MAKE_CN_BITS(cn_bits_N, cn_bits_M, 0, 0) -*/ -/************************************************************************************************** - (MN) - | - -X- init charge=0 - 2(-) | - | - 1(.) X-(V), X(V+1); V=valence -*/ -MY_CONST C_NODE cnMN[2] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 1, 0, 1}, {{ 2, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ -}; -/* -#define cn_bits_MN MAKE_CN_BITS(cn_bits_M, cn_bits_N, 0, 0) -*/ -/************************************************************************************************** - (P) - | - -X- init charge=0 - 2(+.) | - | - 1 X+(V); V=valence; all chemical (real) bonds to X have cap=0 -*/ -MY_CONST C_NODE cnP_[2] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 0, 0, 1}, {{ 2, 1,1, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_C_POS, 1+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ -}; -/* -#define cn_bits_P_ MAKE_CN_BITS(cn_bits_P, 0, 0, 0) -*/ -#ifdef NEVER -/************************************************************************************************** - (M) - | - -X- init charge=-1 on atom - 2(-) | - | - 1(.) X+(V); V=valence; all chemical (real) bonds to X have cap=0 -*/ -MY_CONST C_NODE cnM_[2] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 1, 0, 1}, {{ 2, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ -}; -#endif - -MY_CONST C_NODE cnM_[1] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 0, 0, 0}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ -}; -/* -#define cn_bits_M_ MAKE_CN_BITS(cn_bits_M, 0, 0, 0) -*/ -/************************************************************************************************** - - - -X- init charge=0 - | - - 1 X(V); V=valence; -*/ -MY_CONST C_NODE cnN_[1] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 0, 0, 0}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ -}; -/* -#define cn_bits_N_ MAKE_CN_BITS(cn_bits_N, 0, 0, 0) -*/ -/**************************************************************************************************/ - - -MY_CONST CN_LIST cnList[] = { - {cnPNPN, cn_bits_PNPN, 0, sizeof(cnPNPN)/sizeof(cnPNPN[0])}, /* 0 */ - {cnNPNP, cn_bits_NPNP, 0, sizeof(cnNPNP)/sizeof(cnNPNP[0])}, /* 1 */ - {cnNPN, cn_bits_NPN, 0, sizeof(cnNPN)/sizeof(cnNPN[0])}, /* 2 */ - {cnPNP, cn_bits_PNP, 0, sizeof(cnPNP)/sizeof(cnPNP[0])}, /* 3 */ - {cnMNP, cn_bits_MNP, 0, sizeof(cnMNP)/sizeof(cnMNP[0])}, /* 4 */ - {cnPNM, cn_bits_PNM, 0, sizeof(cnPNM)/sizeof(cnPNM[0])}, /* 5 */ - {cnEN, cn_bits_EN , 0, sizeof(cnEN)/sizeof(cnEN[0])}, /* 6 */ - {cnNMN, cn_bits_NMN, 0, sizeof(cnNMN)/sizeof(cnNMN[0])}, /* 7 */ - {cnNE, cn_bits_NE , 0, sizeof(cnNE)/sizeof(cnNE[0])}, /* 8 */ - {cnNEN, cn_bits_NEN, 0, sizeof(cnNEN)/sizeof(cnNEN[0])}, /* 9 */ - {cnNP, cn_bits_NP, 0, sizeof(cnNP)/sizeof(cnNP[0])}, /* 10 */ - {cnPN, cn_bits_PN, 0, sizeof(cnPN)/sizeof(cnPN[0])}, /* 11 */ - {cnNM, cn_bits_NM, 0, sizeof(cnNM)/sizeof(cnNM[0])}, /* 12 */ - {cnMN, cn_bits_MN, 0, sizeof(cnMN)/sizeof(cnMN[0])}, /* 13 */ - {cnP_, cn_bits_P_, 0, sizeof(cnP_)/sizeof(cnP_[0])}, /* 14 */ - {cnM_, cn_bits_M_, -1, sizeof(cnM_)/sizeof(cnM_[0])}, /* 15 */ - {cnN_, cn_bits_N_, 0, sizeof(cnN_)/sizeof(cnN_[0])}, /* 16 */ - {cnMe, cn_bits_Me, 0, sizeof(cnMe)/sizeof(cnMe[0])} /* 17 */ -}; - -#define cnListIndexMe (17) /* index of {cnMe, cn_bits_Me,... } element of cnList[] */ - -int cnListNumEl = (int)(sizeof(cnList)/sizeof(cnList[0])); -/**********************************************************************/ -void clear_t_group_info( T_GROUP_INFO *ti ) -{ - if ( !ti ) { - return; - } else { - T_GROUP *t_group = ti->t_group; - int max_num_t_groups = ti->max_num_t_groups; - AT_NUMB *tGroupNumber = ti->tGroupNumber; - int num_t_groups = ti->num_t_groups; - AT_NUMB *nEndpointAtomNumber = ti->nEndpointAtomNumber; - int nNumEndpoints = ti->nNumEndpoints; - AT_NUMB *nIsotopicEndpointAtomNumber = ti->nIsotopicEndpointAtomNumber; - int nNumIsotopicEndpoints = ti->nNumIsotopicEndpoints; - memset( ti, 0, sizeof(*ti) ); - if ( t_group ) { - memset( t_group, 0, sizeof(t_group[0])*max_num_t_groups ); - } else { - max_num_t_groups = 0; - } - if ( tGroupNumber ) { - memset( tGroupNumber, 0, sizeof(tGroupNumber[0])*num_t_groups ); - } else { - num_t_groups = 0; - } - if ( nEndpointAtomNumber ) { - memset( nEndpointAtomNumber, 0, sizeof(nEndpointAtomNumber[0])*nNumEndpoints ); - } else { - nNumEndpoints = 0; - } - if ( nIsotopicEndpointAtomNumber ) { - memset( nIsotopicEndpointAtomNumber, 0, sizeof(nIsotopicEndpointAtomNumber[0])*nNumIsotopicEndpoints ); - } else { - nNumIsotopicEndpoints = 0; - } - ti->t_group = t_group; - ti->max_num_t_groups = max_num_t_groups; - ti->tGroupNumber = tGroupNumber; - ti->num_t_groups = num_t_groups; - ti->nEndpointAtomNumber = nEndpointAtomNumber; - ti->nNumEndpoints = nNumEndpoints; - ti->nIsotopicEndpointAtomNumber = nIsotopicEndpointAtomNumber; - ti->nNumIsotopicEndpoints = nNumIsotopicEndpoints; - } - return; -} -/******************************************************************************************************/ -int GetTgroupInfoFromInChI( T_GROUP_INFO *ti, inp_ATOM *at, AT_NUMB *endpoint, INChI *pInChI ) -{ - int ret, i, j, k, itg, num_atoms, len_tg, bIso, num_t_groups; - AT_NUMB *tGroupNumber = NULL; - AT_NUMB *tSymmRank = NULL; - AT_NUMB *tiSymmRank = NULL; - AT_NUMB *tiGroupNumber = NULL; - - ret = 0; - - clear_t_group_info( ti ); - if ( pInChI && pInChI->lenTautomer > 1 && pInChI->nTautomer && pInChI->nTautomer[0] > 0 ) { - num_atoms = pInChI->nNumberOfAtoms; - bIso = pInChI->IsotopicAtom && pInChI->nNumberOfIsotopicAtoms; - num_t_groups = pInChI->nTautomer[0]; - len_tg = pInChI->lenTautomer - T_GROUP_HDR_LEN*pInChI->nTautomer[0] - 1; /* number of endpoints */ - - /* allocation ti->t_group */ - if ( ti->max_num_t_groups != num_atoms/2+1 || !ti->t_group ) { - ti->max_num_t_groups = num_atoms/2+1; - if ( ti->t_group ) - inchi_free( ti->t_group ); - ti->t_group = (T_GROUP *)inchi_calloc( ti->max_num_t_groups, sizeof(ti->t_group[0])); - } - /* allocation ti->tGroupNumber */ - if ( ti->num_t_groups != num_t_groups || !ti->tGroupNumber ) { - ti->num_t_groups = num_t_groups; - if ( ti->tGroupNumber ) - inchi_free( ti->tGroupNumber ); - ti->tGroupNumber = (AT_NUMB *)inchi_calloc((ti->num_t_groups+1)*TGSO_TOTAL_LEN, sizeof(ti->tGroupNumber[0])); - } - /* allocation ti->tGroupNumber */ - if ( len_tg != ti->nNumEndpoints || !ti->nEndpointAtomNumber ) { - ti->nNumEndpoints = len_tg; - if ( ti->nEndpointAtomNumber ) - inchi_free( ti->nEndpointAtomNumber ); - ti->nEndpointAtomNumber = (AT_NUMB *)inchi_calloc(len_tg+1, sizeof(ti->nEndpointAtomNumber[0])); - } - - - /* check */ - if ( !ti->t_group || !ti->tGroupNumber || !ti->nEndpointAtomNumber ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - tGroupNumber = ti->tGroupNumber; - tSymmRank = tGroupNumber + TGSO_SYMM_RANK * ti->num_t_groups; /* equivalence; cannot restore */ - tiSymmRank = tGroupNumber + TGSO_SYMM_IRANK * ti->num_t_groups; - tiGroupNumber = tGroupNumber + TGSO_SYMM_IORDER * ti->num_t_groups; - - - INCHI_HEAPCHK - j = 1; /* index in pInChI->nTautomer[] */ - i = 0; /* index in ti->nEndpointAtomNumber[] */ - for ( itg = 0; itg < pInChI->nTautomer[0]; itg ++ ) { - len_tg = pInChI->nTautomer[j]; /* t-group length not including pInChI->nTautomer[j] */ - ti->t_group[itg].num[0] = pInChI->nTautomer[j+1]+pInChI->nTautomer[j+2]; /* num mobile H & (-) */ - ti->t_group[itg].num[1] = pInChI->nTautomer[j+2]; /* num mobile (-) */ - tGroupNumber[itg] = tiGroupNumber[itg] = itg; /* index */ - ti->t_group[itg].nGroupNumber = /*tSymmRank[itg] = tiSymmRank[itg] =*/ itg+1; /* t-group number */ - j += T_GROUP_HDR_LEN; /* skip t-group header */ - len_tg -= T_GROUP_HDR_LEN-1; - - ti->t_group[itg].nNumEndpoints = len_tg; - ti->t_group[itg].nFirstEndpointAtNoPos = i; - - for( ; 0 < len_tg --; j ++, i ++ ) { - k = ti->nEndpointAtomNumber[i] = pInChI->nTautomer[j]-1; - if ( at ) { - at[k].endpoint = itg+1; - } - if ( endpoint ) { - endpoint[k] = itg+1; - } - } - } - if ( i != ti->nNumEndpoints ) { - ret = RI_ERR_PROGR; - } - INCHI_HEAPCHK - } -exit_function: - return ret; -} -/******************************************************************************************************/ -int FillOutpStructEndpointFromInChI( INChI *pInChI, AT_NUMB **pEndpoint ) -{ - int num_at = pInChI->nNumberOfAtoms; - AT_NUMB *endpoint = *pEndpoint; - int itg, i, j, k, len_tg; - - if ( !endpoint && !(endpoint = (AT_NUMB*) inchi_malloc(num_at * sizeof(endpoint[0]) ) ) ) { - return RI_ERR_ALLOC; - } - memset( endpoint, 0, num_at * sizeof(endpoint[0]) ); - if ( pInChI->lenTautomer <= 1 || !pInChI->nTautomer ) { - goto exit_function; - } - j = 1; /* index in pInChI->nTautomer[] */ - i = 0; /* index in ti->nEndpointAtomNumber[] */ - for ( itg = 0; itg < pInChI->nTautomer[0]; itg ++ ) { - len_tg = pInChI->nTautomer[j]; /* t-group length not including pInChI->nTautomer[j] */ - j += T_GROUP_HDR_LEN; /* skip t-group header */ - len_tg -= T_GROUP_HDR_LEN-1; - /* ti->t_group[itg].nNumEndpoints = len_tg; */ - for( ; 0 < len_tg --; j ++, i ++ ) { - k = pInChI->nTautomer[j]-1; - endpoint[k] = itg+1; - } - } -exit_function: - *pEndpoint = endpoint; - return 0; -} - - -/************************************************************************************/ -int cmp_charge_val( const void *a1, const void *a2 ) -{ - const CHARGE_VAL *p1 = (const CHARGE_VAL *) a1; - const CHARGE_VAL *p2 = (const CHARGE_VAL *) a2; - int diff; - - if ( diff = (int)p1->nValence - (int)p2->nValence ) /* smaller valence first */ - return diff; - if ( diff = abs((int)p1->nCharge) - abs((int)p2->nCharge )) /* smaller abs charge first */ - return diff; - if ( diff = (int)p2->nCharge - (int)p1->nCharge ) /* (+) first, (-) second */ - return diff; - return (int)p1->nValenceOrderingNumber - (int)p2->nValenceOrderingNumber; -} -/************************************************************************************/ -int bMayBeACationInMobileHLayer( inp_ATOM *at, VAL_AT *pVA, int iat, int bMobileH ) -{ - static const char szEl[] = "N;P;O;S;Se;Te;"; - static const char cVal[] = {4,4,3,3, 3, 3, 0}; - static char en[8]; - static int ne; - int i, j, neigh; - char *p; - if ( !bMobileH || !at[iat].num_H ) { - return 1; - } - if ( !ne ) { /* one time initialization */ - const char *b, *e; - int len; - char elname[ATOM_EL_LEN]; - for ( b = szEl; e = strchr( b, ';'); b = e+1 ) { - len = e-b; - memcpy( elname, b, len ); - elname[len] = '\0'; - en[ne++] = get_periodic_table_number( elname ); - } - en[ne] = '\0'; - } - if ( p = (char *)memchr( en, at[iat].el_number, ne ) ) { - i = p - en; - /* >B(-)< exception */ - if ( at[iat].valence + at[iat].num_H <= cVal[i] ) { - for ( j = 0; j < at[iat].valence; j ++ ) { - neigh = at[iat].neighbor[j]; - if ( at[neigh].valence == 4 && at[neigh].chem_bonds_valence == 4 && !at[neigh].num_H && - pVA[neigh].cNumValenceElectrons == 3 && pVA[neigh].cPeriodicRowNumber == 1 ) { - return 1; - } - } - return 0; - } - } - return 1; -} -/************************************************************************************/ -int clean_charge_val( CHARGE_VAL *pChargeVal, int len, inp_ATOM *atom, VAL_AT *pVA, - int iat, int bIsMetal, int bMobileH, AT_NUMB *endpoint ) -{ - inp_ATOM *at = atom + iat; - int nPeriodicNum = at->el_number; - int num_bonds = at->valence; - int min_valence = at->valence + at->num_H; - /* in fixed-H case treat tautomeric -O as tautomeric to avoid #O(+) */ - int bTautomeric = (at->endpoint != 0); - int bFixedHTautomeric = !bMobileH && (endpoint && endpoint[iat] && - pVA[iat].cNumValenceElectrons == 6 && 1==num_bonds && - !at->num_H && !bIsMetal); - /* int bIsMetal = is_el_a_metal( nPeriodicNum );*/ - int bDoNotAddH = do_not_add_H( nPeriodicNum ); - int nPeriod, nNumEqAbsCharges; - int nNumValenceEl = get_sp_element_type( nPeriodicNum, &nPeriod ) - 1; - - int i, j; - - if ( !len ) - return len; - - insertions_sort( pChargeVal, len, sizeof(pChargeVal[0]), cmp_charge_val ); - /* metals -- very preliminary code */ - if ( bIsMetal && bDoNotAddH ) { - /* keep the 1st found */ - return inchi_min( 1, len ); - } - /* Mobile-H layer cannot have H on positively charged N, P (all IV), O, S, Se, Te (all III) */ - - /* - if ( abs( pChargeVal[0].nCharge ) > 1 && pChargeVal[0].nValence >= min_valence ) { - return inchi_min( 1, len ); - } - */ - nNumEqAbsCharges = 0; - for ( i = j = 0; i < len && j < (nNumEqAbsCharges? 3+nNumEqAbsCharges:4); i ++ ) { - /* for now accept only charge = 0, -1, +1 */ - if ( abs( pChargeVal[i].nCharge ) > 1 ) { - continue; - } - if ( BOND_TYPE_TRIPLE + BOND_TYPE_DOUBLE * (min_valence - 1) < pChargeVal[i].nValence ) { - continue; /* not more than one triple and the rest - double bonds per atom */ - } - if ( (bTautomeric || j && bFixedHTautomeric) && pChargeVal[i].nCharge < 0 ) { - continue; /* negative charge must be included in the tautomeric group */ - } - if ( (bTautomeric || bFixedHTautomeric) && pChargeVal[i].nCharge > 0 ) { - continue; /* positive charge for now cannot reach a tautomeric group */ - } - if ( j && !bMayBeACationInMobileHLayer( atom, pVA, iat, bMobileH ) && pChargeVal[i].nCharge > 0 ) { - if ( i+1 < len && - pChargeVal[i].nValence == pChargeVal[i+1].nValence && - pChargeVal[i].nCharge == -pChargeVal[i+1].nCharge ) { - /* (-) if exists is always after (+) */ - i += 1; /* also skip the next element */ - } - continue; /* in case of Mobile-H, a hydrogen cannot be on a (+)-charged heteroatom */ - } - /* accept same valence opposite charges only for C and its group in Periodic Table */ - if ( j && !bTautomeric && - pChargeVal[i].nValence == pChargeVal[j-1].nValence && - pChargeVal[i].nCharge == -pChargeVal[j-1].nCharge ) { - if ( nNumValenceEl == VALUE_OCTET/2 && pChargeVal[i].nCharge && !nNumEqAbsCharges ) { - pChargeVal[j ++] = pChargeVal[i]; - nNumEqAbsCharges ++; - } - continue; - } - /* do not accept valence=5 for neutral NHn in case of not Mobile-H 2005-01-26 ???? */ - if ( nNumValenceEl == 5 && nPeriod == 1 && at->num_H && - j && !bMobileH && - pChargeVal[i].nValence == 5 && !pChargeVal[i].nCharge ) { - continue; - } - /* do not accept gaps in allowed valences */ - if ( j && pChargeVal[i].nValence > pChargeVal[j-1].nValence+1 ) { - break; - } - pChargeVal[j ++] = pChargeVal[i]; - } - len = j; - if ( !nNumEqAbsCharges && num_bonds < 3 && len == 4 ) { - len --; /* prohibit =S# where # is a triple bond */ - } - return len; - - -} -/************************************************************************************ -int GetAtomRestoreInfo( inp_ATOM *atom, int iat, VAL_AT *pVArray ) - - pVA->cDoNotAddH - pVA->cMetal - pVA->cNumValenceElectrons - pVA->cPeriodicRowNumber - pVA->cInitFreeValences - pVA->cnListIndex = index+1 - -return value: - -1 => error - 0 => do not know what to do; leave the atom unchanged - 1 => success -*************************************************************************************/ -int GetAtomRestoreInfo( inp_ATOM *atom, int iat, VAL_AT *pVArray, ICHICONST SRM *pSrm, int bMobileH, AT_NUMB *endpoint ) -{ -/* #defines from util.c */ -#define MIN_ATOM_CHARGE (-2) -#define MAX_ATOM_CHARGE 2 -#define NEUTRAL_STATE (-MIN_ATOM_CHARGE) -#define NUM_ATOM_CHARGES (MAX_ATOM_CHARGE - MIN_ATOM_CHARGE + 1) -#define MAX_NUM_VALENCES 5 /* max. number + 1 to provide zero termination */ - - int i, j, j2, k, k2, charge, cur_charge, num_non_bonding_electrons; - int nNumStates, nNumSelectedStates, num_H, num_bonds; - int nOctetNeutralValenceExcess, nFirstNeutralValenceExcess; - int nFoundNeutralValenceExcess, nFoundNeutralValenceOrdNumber; - int nLastFoundValenceOrdNumber, nLastFoundValenceState; - int cn_bits, cn_bits_array[5], len_cn_bits_array; - inp_ATOM *at = atom+iat; - VAL_AT *pVA = pVArray + iat; - int nPeriodicNum = at->el_number; - int cur_chem_valence, cur_chem_valence_fixed, min_chem_valence, known_chem_valence; - int metal_bonds_chem_valence, not_metal_bonds_chem_valence, alt_bonds_delta_valence, bonds_chem_valence, bond_type; - CHARGE_VAL ChargeVal[NUM_ATOM_CHARGES*MAX_NUM_VALENCES]; - - memset( ChargeVal, 0, sizeof(ChargeVal) ); - - pVA->cDoNotAddH = do_not_add_H( nPeriodicNum ); /* InChI never adds H to this atom */ - /*pVA->cMetal = is_el_a_metal( nPeriodicNum );*/ /* the atom is a metal */ - - /* count bonds to metal atoms; metals have already been marked */ - metal_bonds_chem_valence = not_metal_bonds_chem_valence = alt_bonds_delta_valence = 0; - if ( pVA->cMetal ) { - j = at->valence; /* all bonds to metal */ - for ( i = k = j2 = k2 = 0; i < at->valence; i ++ ) { - bond_type = (at->bond_type[i] & BOND_TYPE_MASK); - if ( bond_type <= BOND_TYPE_TRIPLE ) { - metal_bonds_chem_valence += inchi_max(BOND_TYPE_SINGLE, bond_type); - } else { - metal_bonds_chem_valence += BOND_TYPE_SINGLE; - k ++; /* count alternating bonds */ - } - } - } else { - for ( i = j = j2 = k = k2 = 0; i < at->valence; i ++ ) { - bond_type = (at->bond_type[i] & BOND_TYPE_MASK); - if ( pVArray[ (int)at->neighbor[i] ].cMetal ) { - j ++; /* number of bonds to metal atoms */ - if ( bond_type <= BOND_TYPE_TRIPLE ) { - metal_bonds_chem_valence += inchi_max(BOND_TYPE_SINGLE, bond_type); - } else { - metal_bonds_chem_valence += BOND_TYPE_SINGLE; - k ++; /* count alternating bonds */ - } - } else { - j2 ++; - if ( bond_type <= BOND_TYPE_TRIPLE ) { - not_metal_bonds_chem_valence += inchi_max(BOND_TYPE_SINGLE, bond_type); - } else { - not_metal_bonds_chem_valence += BOND_TYPE_SINGLE; - k2 ++; /* count alternating bonds */ - } - } - } - } - bonds_chem_valence = metal_bonds_chem_valence + not_metal_bonds_chem_valence; - if ( at->chem_bonds_valence > bonds_chem_valence ) { - if ( at->chem_bonds_valence - bonds_chem_valence > 1 ) { - at->chem_bonds_valence = bonds_chem_valence + 1; /* should not happen */ - } - alt_bonds_delta_valence = at->chem_bonds_valence - bonds_chem_valence; - } - - pVA->cNumBondsToMetal = j; - - if ( nPeriodicNum == EL_NUMBER_H ) { - /* ignore bridging H; ??? later add ??? */ - return 0; - } - - num_H = at->num_H; - num_bonds = at->valence; - - if ( !num_bonds && !num_H ) { - return 0; /* do not know the answer: isolated atom */ - } - /* at the beginning all bonds are single */ - min_chem_valence = num_bonds + num_H; - cur_chem_valence = bonds_chem_valence + alt_bonds_delta_valence + num_H; /* includes double & alternating bond contribution */ - - /* number of non-bonding electrons in case of all single bonds */ - num_non_bonding_electrons = (int)pVA->cNumValenceElectrons - min_chem_valence; - /* Octet rule: charge = bonds_valence + NumValenceElectrons - 8 */ - charge = min_chem_valence + (int)pVA->cNumValenceElectrons - VALUE_OCTET; /* wrong */ - - /* typical (ad hoc) minimal neutral valence */ - known_chem_valence = ( pVA->cNumValenceElectrons > VALUE_OCTET/2 )? - VALUE_OCTET - pVA->cNumValenceElectrons : - pVA->cNumValenceElectrons; - /* excess of typical valence over all-single-bonds valence */ - nOctetNeutralValenceExcess = known_chem_valence - min_chem_valence; - /* (NB=num.bonds, NV=neutral valence, NVX=neutral valence excess, LFVS=last found valence state, val.=valence) - - element NB knownFst octet Last octetNVX firstNVX foundNVX chargeLFVS LFVS - valence val. NV>= - - -B 1 3 3 3 2 2 = 2 +2 - >B 2 3 3 3 1 1 = 1 +1 - >B- 3 3 3 3 0 0 = 0 0 - >B< 4 3 3 3 -1 -1 <> N/A -1 - - -C 1 4 4 4 3 3 = 3 N/A - >C 2 4 4 4 2 2 = 2 +2 (-2) - >C- 3 4 4 4 1 1 = 1 +1 (-1) - >C< 4 4 4 4 0 0 = 0 0 - C(V) 5 4 4 N/A -1 -1 <> N/A N/A - - -Si 1 4 4 4 3 3 = 3 N/A - >Si 2 4 4 4 2 2 = 2 +2 (-2) - >Si- 3 4 4 4 1 1 = 1 +1 (-1) - >Si- 4 4 4 4 0 0 = 0 0 - Si(V) 5 4 4 N/A -1 -1 <> N/A -1 - - -N 1 3 3 3 2 2 = 2 -2 - >N 2 3 3 3 1 1 = 1 -1 - >N- 3 3 3 3 0 0 = 0 0 (+2) - >N< 4 3 3 5 -1 -1 <> 1 +1 - N(V) 5 3 3 5 -2 -2 <> 0 0 - N(VI) 6 3 3 N/A -3 -3 <> N/A N/A - N(VII) 7 3 3 N/A -4 -4 <> N/A N/A - - -P 1 3 3 3 2 2 = 2 -2 - >P 2 3 3 3 1 1 = 1 -1 - >P- 3 3 3 3 0 0 = 0 0 (-2, +2) - >P< 4 3 3 5 -1 -1 <> 1 +1 (-1) - P(V) 5 3 3 5 -2 -2 <> 0 0 (-2) - P(VI) 6 3 3 N/A -3 -3 <> N/A -1 - P(VII) 7 3 3 N/A -4 -4 <> N/A -2 - P(VIII) 8 3 3 N/A -5 -5 <> N/A N/A - - -O 1 2 2 2 1 1 = 1 -1 - >O 2 2 2 2 0 0 = 0 0 - >O- 3 2 2 N/A -1 -1 <> N/A +1 - >O< 4 2 2 N/A -2 -2 <> N/A +2 - O(V) 5 2 2 N/A -3 -3 <> N/A +1 - O(VI) 6 2 2 N/A -4 -4 <> N/A N/A - - -S 1 2 2 2 1 1 = 1 -1 - >S 2 2 2 2 0 0 = 0 0 NPNP - prohibit - >S- 3 2 2 4 -1 -1 <> 1 +1 (-1) PNPN - >S< 4 2 2 4 -2 -2 <> 0 0 (+2) - S(V) 5 2 2 6 -3 -3 <> 1 +1 (-1) - S(VI) 6 2 2 6 -4 -4 <> 0 0 - S(VII) 7 2 2 N/A -5 -5 <> 0 -1 - S(VIII) 8 2 2 N/A -6 -6 <> N/A N/A - - -F 1 1 1 1 0 0 = 0 0 - >F 2 1 1 1 -1 -1 <> N/A +1 - >F- 3 1 1 1 -2 -2 <> N/A +2 - >F< 4 1 1 1 -3 -3 <> N/A N/A - F(V) 5 1 1 1 -4 -4 <> N/A +2 - F(VI) 6 1 1 1 -5 -5 <> N/A N/A - - -Cl 1 1 1 1 0 0 = 0 0 NPNP - prohibit - >Cl 2 1 1 3 -1 -1 <> 1 +1 PNPN - prohibit - >Cl- 3 1 1 3 -2 -2 <> 0 0 (+2) NPNP - >Cl< 4 1 1 5 -3 -3 <> 1 +1 PNPN - Cl(V) 5 1 1 5 -4 -4 <> 0 0 - Cl(VI) 6 1 1 7 -5 -5 <> 1 +1 - Cl(VII) 7 1 1 7 -6 -6 <> 0 0 - Cl(VIII) 8 1 1 N/A -7 -7 <> N/A N/A - - - NB = num_bonds+num_H - - knownFst valence = nFirstNeutralValenceExcess + min_chem_valence - octet val. = nOctetNeutralValenceExcess + min_chem_valence - Last NV>= = nFoundNeutralValenceExcess + min_chem_valence - - octetNVX = nOctetNeutralValenceExcess - firstNVX = nFirstNeutralValenceExcess - foundNVX = nFoundNeutralValenceExcess - - chargeLFVS = ChargeVal[nLastFoundValenceState].nCharge - - */ - /* minimal known neutral atom valence; different for Sn(2/4), Tl(1/3), Pb(2/4): (known/typical ad hoc) */ - known_chem_valence = get_el_valence( nPeriodicNum, 0, 0 ); - - if ( pSrm->bMetalAddFlower ) { - /* bond orders of bonds to metal may be as they are (pSrm->nMetalInitBondOrder==1) - or decreased by one (pSrm->nMetalInitBondOrder==0) - nMetalInitBondOrder == nMetalMinBondOrder + nMetalInitEdgeFlow - */ - cur_chem_valence_fixed = cur_chem_valence - pVA->cNumBondsToMetal * (1-pSrm->nMetalInitBondOrder); - pVA->cInitOrigValenceToMetal = metal_bonds_chem_valence; - pVA->cInitValenceToMetal = metal_bonds_chem_valence - pVA->cNumBondsToMetal * (1-pSrm->nMetalInitBondOrder); - pVA->cInitFlowToMetal = pVA->cInitValenceToMetal - pVA->cNumBondsToMetal * pSrm->nMetalMinBondOrder; - if ( pVA->cMetal ) { - pVA->cInitFreeValences += alt_bonds_delta_valence; - } - if ( pSrm->nMetalInitEdgeFlow < pSrm->nMetalInitBondOrder - pSrm->nMetalMinBondOrder ) { - /* single bond has zero initial flow + 2 radicals at incident atoms */ - if ( pVA->cInitFlowToMetal <= pVA->cNumBondsToMetal ) { - if ( pVA->cMetal ) { - pVA->cInitFreeValences += pVA->cInitFlowToMetal; - } - pVA->cInitFlowToMetal = 0; - } else { - if ( pVA->cMetal ) { - pVA->cInitFreeValences += pVA->cNumBondsToMetal * (1 - pSrm->nMetalInitEdgeFlow); - } - pVA->cInitFlowToMetal -= pVA->cNumBondsToMetal * (1 - pSrm->nMetalInitEdgeFlow); - } - } - - } else { - /* treat metal atoms as ordinary non-metal atoms */ - cur_chem_valence_fixed = cur_chem_valence; - pVA->cInitFlowToMetal = metal_bonds_chem_valence - pVA->cNumBondsToMetal; - pVA->cInitValenceToMetal = metal_bonds_chem_valence; - pVA->cInitOrigValenceToMetal = metal_bonds_chem_valence; - } - - - if ( pVA->cMetal && pSrm->bMetalAddFlower ) { - pVA->cnListIndex = cnListIndexMe + 1; - /* - pVA->cInitOrigValenceToMetal += alt_bonds_delta_valence; - pVA->cInitValenceToMetal += alt_bonds_delta_valence; - pVA->cInitFreeValences = (pSrm->nMetalInitBondOrder + alt_bonds_delta_valence - - (pSrm->nMetalMinBondOrder + pSrm->nMetalInitEdgeFlow)) * pVA->cNumBondsToMetal; - */ - return 0; /* metal */ - } - - if ( !known_chem_valence ) { - /* a noble gas like He, Ne, ... */ - pVA->cInitFreeValences = at->chem_bonds_valence - at->valence; - return TREAT_ATOM_AS_METAL; /* do not know anything about this atom; needs 2nd pass */ - } - - nFirstNeutralValenceExcess = known_chem_valence - min_chem_valence; - - nFoundNeutralValenceExcess = NO_VALUE_INT; - nFoundNeutralValenceOrdNumber = NO_VALUE_INT; - nLastFoundValenceOrdNumber = NO_VALUE_INT; - nLastFoundValenceState = NO_VALUE_INT; - - /* find the lowest known valence >= all-single-bonds valence */ - for ( cur_charge = MIN_ATOM_CHARGE, nNumStates = 0; cur_charge <= MAX_ATOM_CHARGE; cur_charge ++ ) { - for ( i = 0; i < MAX_NUM_VALENCES; i ++ ) { - known_chem_valence = get_el_valence( nPeriodicNum, cur_charge, i ); - if ( cur_chem_valence_fixed > known_chem_valence || !known_chem_valence ) { - continue; /* known valence < all-single-bonds valence */ - } - if ( BOND_TYPE_TRIPLE + BOND_TYPE_DOUBLE * (num_bonds - 1) + num_H < known_chem_valence ) { - continue; /* not more than one triple and the rest - double bonds per atom */ - } - /* keep all found */ - ChargeVal[nNumStates].nValence = known_chem_valence; - ChargeVal[nNumStates].nCharge = cur_charge; - ChargeVal[nNumStates].nValenceOrderingNumber = i; - if ( !cur_charge && nFoundNeutralValenceExcess == NO_VALUE_INT ) { - /* neutral state; compare to the lowest typical valence */ - nFoundNeutralValenceExcess = known_chem_valence - min_chem_valence; - nFoundNeutralValenceOrdNumber = i; - } - if ( min_chem_valence == known_chem_valence ) { - if ( nLastFoundValenceState == NO_VALUE_INT ) { - /* accept the first found */ - nLastFoundValenceState = nNumStates; - } else - if ( abs( ChargeVal[nLastFoundValenceState].nCharge ) >= abs( cur_charge ) ) { - /* accept smaller abs(charge); if abs(charges) are same, accept (+) */ - nLastFoundValenceState = nNumStates; - } - } - nNumStates ++; - } - } - /***********************************************************************************/ - /* select only appropriate charge & valence so that a suitable ChargeStruct exists */ - /***********************************************************************************/ - - nNumSelectedStates = clean_charge_val( ChargeVal, nNumStates, atom, pVArray, iat, pVA->cMetal, bMobileH, endpoint ); - - if ( !nNumSelectedStates ) { - return TREAT_ATOM_AS_METAL; /* nothing to do */ - } - /***********************************************************************************/ - /* Find an appropriate ChargeStruct index for the ChargeVal found */ - /***********************************************************************************/ - cn_bits = 0; - memset( cn_bits_array, 0, sizeof(cn_bits_array) ); - /***** set bits identifying a suitable ChargeStruct ******/ - for ( i = len_cn_bits_array = 0; i < nNumSelectedStates && len_cn_bits_array < 4; i ++ ) { - switch( ChargeVal[i].nCharge ) { - case -1: - cn_bits_array[len_cn_bits_array] |= cn_bits_M; /* Minus 1 */ - break; - case 0: - cn_bits_array[len_cn_bits_array] |= cn_bits_N; /* Neutral */ - break; - case 1: - cn_bits_array[len_cn_bits_array] |= cn_bits_P; /* Plus 1 */ - break; - default: - return RI_ERR_PROGR; /* program error */ - } - if ( i+1 < nNumSelectedStates && - ChargeVal[i].nValence == ChargeVal[i+1].nValence && - ChargeVal[i].nCharge && - ChargeVal[i].nCharge == -ChargeVal[i+1].nCharge ) { - ; /* add opposite charge to the same element of cn_bits_array[] */ - } else { - len_cn_bits_array ++; - } - } - if ( !len_cn_bits_array || len_cn_bits_array > 4 ) { - return RI_ERR_PROGR; /* program error */ - } - /* accommodate added 4-state ChargeStruct: +/- cannot be in case of 4 states */ - if ( len_cn_bits_array + 1 == nNumSelectedStates && nNumSelectedStates == 4 ) { - len_cn_bits_array --; - nNumSelectedStates --; - cn_bits_array[len_cn_bits_array] = 0; - } - /* fix for terminal hydrogenless -C as in isocyano or CO: there is no just cnE_[] ChargeStruct */ - - if ( len_cn_bits_array == 1 && - cn_bits_array[0] == (cn_bits_P | cn_bits_M) && - ChargeVal[0].nValence + 1 > BOND_TYPE_TRIPLE + BOND_TYPE_DOUBLE * (num_bonds - 1) + num_H ) { - cn_bits_array[len_cn_bits_array ++] = cn_bits_N; - ChargeVal[nNumSelectedStates].nValence = ChargeVal[nNumSelectedStates-1].nValence; - ChargeVal[nNumSelectedStates].nCharge = 0; - ChargeVal[nNumSelectedStates].nValenceOrderingNumber = 0; - } - -make_cn_bits: - cn_bits = MAKE_CN_BITS(cn_bits_array[0], cn_bits_array[1], cn_bits_array[2], cn_bits_array[3]); - /*********** find ChargeStructure **************/ - for ( i = 0, j = -1; i < cnListNumEl; i ++ ) { - if ( cnList[i].bits == cn_bits ) { - j = i; - break; /* found */ - } - } - if ( j < 0 ) { - /* ChargeStructure was not found */ - if ( 1 < len_cn_bits_array && len_cn_bits_array + 1 == nNumSelectedStates ) { - /* a pair of opposite charges was combined */ - len_cn_bits_array --; - cn_bits_array[len_cn_bits_array] = 0; - goto make_cn_bits; - } else - if ( nNumSelectedStates == 4 ) { - /* reduce number of states */ - len_cn_bits_array --; - cn_bits_array[len_cn_bits_array] = 0; - nNumSelectedStates --; - goto make_cn_bits; - } - return RI_ERR_PROGR; /* charge structure not found */ - } - /********** ChargeStructure has been found **********/ - pVA->cnListIndex = j+1; /* charge structure index + 1 */ - pVA->cInitCharge = cnList[j].nInitialCharge; - /********** Calculate "Free Valence" ****************/ -#if ( ALLOW_METAL_BOND_ZERO == 1 ) - -#if ( INIT_METAL_BOND_ZERO == 1 ) - if ( pVA->cMetal ) { - j = 0; - } else { - j = ChargeVal[0].nValence - cur_chem_valence_fixed; - } -#else - j = ChargeVal[0].nValence - cur_chem_valence_fixed; -#endif - -#else - j = ChargeVal[0].nValence - cur_chem_valence_fixed; -#endif - if ( j < 0 ) { - return RI_ERR_PROGR; /* program error */ - } - pVA->cInitFreeValences = j; /* number of initial unsatisfied valences; should be combined with */ - /* (cap - flow) of vertex=0 in the charge structure[pVA->cnListIndex-1] */ - return 1; /* success */ - -#undef MIN_ATOM_CHARGE -#undef MAX_ATOM_CHARGE -#undef NEUTRAL_STATE -#undef NUM_ATOM_CHARGES -#undef MAX_NUM_VALENCES - -} - - -#ifdef NEVER -/******************************************************************************************************/ -int get_bonds_valences( int nPeriodicNum, int bonds_valence, int num_H, VAL_AT *pVA ) -{ - int i, j, charge, chem_valence, known_chem_valence; -#define MAX_NUM_VALENCES 5 /* defined in util.c */ - - memset( pVA, 0, sizeof( pVA[0] ) ); - - if ( !bonds_valence && !num_H ) - return 0; /* do not know the answer */ - - chem_valence = bonds_valence + num_H; - for ( charge = VAL_MIN_CHARGE; charge <= VAL_MAX_CHARGE; charge ++ ) { - for ( i = 0, j = 0; i < MAX_NUM_VALENCES, j < VAL_NUMBER; i ++ ) { - if ( chem_valence <= (known_chem_valence = get_el_valence( nPeriodicNum, charge, i ) ) ) { - if ( !charge ) { - pVA->cValence[j][VAL_NEUTR_ORDER] = i+1; - } - pVA->cValence[j++][charge+VAL_BASE] = known_chem_valence - num_H; - } - } - } - pVA->cDoNotAddH = do_not_add_H( nPeriodicNum ); - pVA->cMetal = is_el_a_metal( nPeriodicNum ); - return pVA->cValence[0][VAL_BASE]; /* 0 means do not know the answer */ -#undef MAX_NUM_VALENCES -} -#endif -/*********** calculate s or p-element type ************/ -int get_sp_element_type( int nPeriodicNumber, int *nRow ) -/* - num el - el neg - 1 => H ATYPE_H 1 1 21 - 2 => Li, Na, K, Rb, Cs, Fr ATYPE_Na 2 1 10 09 08 08 07 - 3 => Be, Mg, Ca, Sr, Ba, Ra ATYPE_Mg 3 2 15 12 10 10 09 - 4 => B, Al, Ga, In, Tl ATYPE_B 4 3 20 15 18 17 18 - 5 => C, Si, Ge, Sn, Pb ATYPE_C 5 4 25 18 18 18 18 - 6 => N, P, As, Sb, Bi ATYPE_N 6 5 30 21 20 19 19 - 7 => O, S, Se, Te, Po ATYPE_O 7 6 35 25 24 21 20 - 8 => F, Cl, Br, I, At ATYPE_Cl 8 7 40 30 28 25 22 - -number of valence electrons = (type>1)? type-1: type - - */ -{ - int row = 0, type = 0; - if ( nPeriodicNumber == 1 ) { - type = 1; /* H: 1 */ - row = 0; - } else - if ( nPeriodicNumber == 2 ) { - type = 0; row = 0; - } else - if ( nPeriodicNumber <= 10 ) { - /* Li: 2, Be: 3, B: 4, C: 5, N: 6, O: 7, F: 8, Ne: 9; later subtract 1 */ - type = nPeriodicNumber-1; row = 1; - } else - if ( nPeriodicNumber <= 18 ) { - type = nPeriodicNumber - 9; row = 2; - } else - if ( nPeriodicNumber <= 20 ) { - type = nPeriodicNumber - 17; row = 3; - } else - if ( nPeriodicNumber <= 30 ) { - type = 0; row = 3; - } else - if ( nPeriodicNumber <= 36 ) { - type = nPeriodicNumber - 27; row = 3; - } else - if ( nPeriodicNumber <= 38 ) { - type = nPeriodicNumber - 35; row = 4; - } else - if ( nPeriodicNumber <= 48 ) { - type = 0; row = 4; - } else - if ( nPeriodicNumber <= 54 ) { - type = nPeriodicNumber - 45; row = 4; - } else - if ( nPeriodicNumber <= 56 ) { - type = nPeriodicNumber - 53; row = 5; - } else - if ( nPeriodicNumber <= 80 ) { - type = 0; row = 5; - } else - if ( nPeriodicNumber <= 86 ) { - type = nPeriodicNumber - 77; row = 5; - } else - if ( nPeriodicNumber <= 88 ) { - type = nPeriodicNumber - 85; row = 6; - } else { - type = 0; row = 6; - } - *nRow = row; - return type==9? 0 : type; -} -/******************************************************************************************************/ -int ReallocTCGroups( ALL_TC_GROUPS *pTCGroups, int nAdd ) -{ - TC_GROUP *pTCGroup = (TC_GROUP *) inchi_malloc( sizeof(pTCGroup[0])*(pTCGroups->max_tc_groups + nAdd) ); - if ( pTCGroup ) { - if ( pTCGroups->num_tc_groups ) { - memcpy( pTCGroup, pTCGroups->pTCG, sizeof(pTCGroup[0])*pTCGroups->num_tc_groups ); - } - memset( pTCGroup + pTCGroups->max_tc_groups, 0, sizeof(pTCGroup[0])*nAdd ); - if ( pTCGroups->pTCG ) { - inchi_free( pTCGroups->pTCG ); - } - pTCGroups->pTCG = pTCGroup; - pTCGroups->max_tc_groups += nAdd; - return 0; - } - return RI_ERR_ALLOC; -} -/******************************************************************************************************/ -int RegisterTCGroup( ALL_TC_GROUPS *pTCGroups, int nGroupType, int nGroupOrdNum, - int nVertexCap, int nVertexFlow, int nEdgeCap, int nEdgeFlow, int nNumEdges) -{ - int i, ret = 0; - /* search */ - for ( i = 0; i < pTCGroups->num_tc_groups; i ++ ) { - if ( pTCGroups->pTCG[i].type == nGroupType && - pTCGroups->pTCG[i].ord_num == nGroupOrdNum ) { - break; - } - } - if ( i == pTCGroups->num_tc_groups ) { - /* add one more group */ - if ( pTCGroups->num_tc_groups == pTCGroups->max_tc_groups ) { - ret = ReallocTCGroups( pTCGroups, INC_NUM_TCGROUPS ); - if ( ret ) { - goto exit_function; - } - } - ret = i+1; /* added new group */ - pTCGroups->num_tc_groups ++; - pTCGroups->pTCG[i].type = nGroupType; - pTCGroups->pTCG[i].ord_num = nGroupOrdNum; - } - pTCGroups->pTCG[i].num_edges += nNumEdges; - - pTCGroups->pTCG[i].st_cap += nVertexCap; - pTCGroups->pTCG[i].st_flow += nVertexFlow; - - pTCGroups->pTCG[i].edges_cap += nEdgeCap; - pTCGroups->pTCG[i].edges_flow += nEdgeFlow; - -exit_function: - return ret; -} -/******************************************************************************************************/ -int nTautEndpointEdgeCap( inp_ATOM *at, VAL_AT *pVA, int i ) -{ - /* There are 3 sources of cap-flow = number of unsatisfied valences: - ----------------------------------------------------------------- - 1. pVA[i].cInitFreeValences - 2. pCN[0].v.cap - pCN[0].v.flow - 3. st[i].chem_bonds_valence - SUM(SINGLE, DOUBLE, TRIPLE bond orders) - Reasons: (a) This sum will not include 'ALTERN' bonds - (b) until now at[i].chem_bonds_valence was used as a - number of satisfied valences. In case of adjacent - stereobonds marked as BOND_TYPE_ALTERN the value of - at[i].chem_bonds_valence may be = at[i].valence+1. - 4. Since tautomerism is defined for a neutral atom, do not add - initial flows from the atom to the ChargeStruct - CORRECTION: tautomeric endpoints do not have ChargeStruct. - - */ - int j, k, nEdgeCap, bonds_valence, stereo_bond_excess_valence; - MY_CONST C_NODE *pCN = pVA[i].cnListIndex>0? cnList[pVA[i].cnListIndex-1].pCN:NULL; - - /* 1: free valences to reach the minimum known atom valence */ - nEdgeCap = pVA[i].cInitFreeValences; - /* 2: atom free valence in the ChargeStruct */ - if ( pCN ) { - nEdgeCap += pCN[0].v.cap - pCN[0].v.flow; /* normally should not happen */ - } - /* 3: atom free valence due to known from stereochemistry stereogenic bond types */ - /* - for ( j = 0, bonds_valence = 0; j < at[i].valence; j ++ ) { - if ( at[i].bond_type[j] <= BOND_TYPE_TRIPLE ) { - bonds_valence += at[i].bond_type[j]; - } - } - */ - /* bonds > SINGLE are assumed fixed stereobonds; fixed bond cannot increase t-group edge flow */ - for ( stereo_bond_excess_valence=0, j = 0; j < MAX_NUM_STEREO_BONDS && at[i].sb_parity[j]; j ++ ) { - k = at[i].sb_ord[j]; - if ( at[i].bond_type[k] < BOND_TYPE_TRIPLE ) { - stereo_bond_excess_valence += at[i].bond_type[k] - BOND_TYPE_SINGLE; - } - } - /* - bonds_valence = (at[i].chem_bonds_valence - bonds_valence) + (bonds_valence -at[i].valence - stereo_bond_excess_valence); - */ - bonds_valence = (at[i].chem_bonds_valence - at[i].valence) - stereo_bond_excess_valence; - - - /*---- add 1, 2, 3 ----*/ - if ( bonds_valence >= 0 ) { - nEdgeCap += bonds_valence; - } else { - nEdgeCap = RI_ERR_PROGR; - } - return nEdgeCap; -} -/******************************************************************************************************/ -/* If Metal flowers are allowed ( pSrm->bMetalAddFlower != 0), then: */ -/* */ -/* bond to a metal atom min_bond_order[i] = pSrm->nMetalMinBondOrder */ -/* taut endpoint - metal min_bond_order[i] = pSrm->nMetal2EndpointMinBondOrder */ -/* single bond to metal atom: initial_bond_order[i] = pSrm->nMetalInitBondOrder */ -/* n-order bond to metal atom initial_bond_order[i] = pSrm->nMetalInitBondOrder + n-1 */ -/* = bond_order[i]-BOND_TYPE_SINGLE+pSrm->nMetalInitBondOrder */ -/* single t-endpoint--atom bond initial_bond_order[i] = pSrm->nMetal2EndpointInitBondOrder */ -/* n-order t-endpoint--metal bond initial_bond_order[i] = pSrm->nMetal2EndpointInitBondOrder+n-1 */ -/* = bond_order[i]-BOND_TYPE_SINGLE+pSrm->nMetal2EndpointInitBondOrder*/ -/* */ -/* Exceptions from simple atom-metal conditions: */ -/* 1. Atom is a tautomeric endpoint: use pSrm->nMetal2Endpoint* instead of pSrm->nMetal* */ -/* 2. Atom is sp3-stereogenic and pSrm->bFixStereoBonds != 0: use atom-atom rules */ -/* 3. Atom has a sp2-stereo and pSrm->bFixStereoBonds != 0: use atom-atom rules */ -/* */ -/* Atom-atom rules (applies to all atoms if pSrm->bMetalAddFlower=0) */ -/* */ -/* min_bond_order[i] = BOND_TYPE_SINGLE (BOND_TYPE_SINGLE = 1) */ -/* initial_bond_order[i] = bond_type[i] */ -/* */ -/* General rules: */ -/* initial_bond_flow[i] = initial_bond_order[i]-min_bond_order[i] */ -/* atom[k] initial_st_cap = at[k].chem_bonds_valence - SUM{i; initial_bond_order[i]} */ -/* bond_cap[i] = BOND_TYPE_TRIPLE - min_bond_order[i] */ -/* (reason: quadruple and higher order bonds are not allowed) */ -/* Exception: in case of metal-atom bond, if pSrm->nMetal2EndpointInitEdgeFlow = 0 AND */ -/* pSrm->nMetalInitBondOrder - pSrm->nMetalMinBondOrder = 1 then */ -/* reduce bond to metal order by 1 and increase st_cap of both neighbors by 1: */ -/* initial_bond_flow[i] --; metal_initial_st_cap += num_bonds; */ -/* ==== Note: ONLY the INCREASE is already included in pVA->cInitFreeValences of both atoms */ -/* */ -/* Notes: initial_st_cap does not include: */ -/* 1. atom[k] additional st_cap from ChargeStruct pCN[0].v.cap */ -/* 2. pVA[k].cInitFreeValences due to a difference between the smallest known valence and st_cap */ -/* */ -/* here k=atom at[k] index, */ -/* i=bond index; i = 0..at[k].valence; */ -/* SUM{i; M[i]} is a sum of M[i] over all i */ -/* bond_order[i] = at[k].bond_type[i] >= BOND_TYPE_SINGLE - input bond order */ -/******************************************************************************************************/ - -/***************** new *************************************************************************************/ - -int BondFlowMaxcapMinorder( inp_ATOM *atom, VAL_AT *pVA, ICHICONST SRM *pSrm, int iat, int ineigh, - int *pnMaxcap, int *pnMinorder, int *pbNeedsFlower ) -{ - int nFlow, nMaxcap, nMinorder, nInitorder, bNeedsFlower = 0; - inp_ATOM *at = atom + iat; - int neigh = at->neighbor[ineigh]; - int bond_type = at->bond_type[ineigh] & BOND_TYPE_MASK; - int nMetal = (0 != pVA[iat].cMetal) + (0 != pVA[neigh].cMetal); - int nEndpoint = (0 != at->endpoint) + (0 != atom[neigh].endpoint); - int nStereo = (at->p_parity || at->sb_parity[0]) + (atom[neigh].p_parity || atom[neigh].sb_parity[0]); - - if ( bond_type > BOND_TYPE_TRIPLE ) { - bond_type = BOND_TYPE_SINGLE; - } - /* M=metal, A=non-metal atom, e=endpoint */ - if ( nStereo && pSrm->bFixStereoBonds || !nMetal || !pSrm->bMetalAddFlower ) { - /* atom-atom rules, no metal atoms involved (1: A-A, A-Ae, Ae-Ae) */ - nMinorder = BOND_TYPE_SINGLE; - nInitorder = bond_type; - nFlow = nInitorder - nMinorder; - } else - if ( nMetal && !nEndpoint ) { - /* M-a, M-M */ - /* atom - metal or metal-metal, none of them is an endpoint (2: M-M, M-A) */ - nMinorder = pSrm->nMetalMinBondOrder; - nInitorder = pSrm->nMetalInitBondOrder + bond_type - BOND_TYPE_SINGLE; - nFlow = nInitorder - nMinorder; - if ( !pSrm->nMetalInitEdgeFlow && - pSrm->nMetalInitBondOrder > pSrm->nMetalMinBondOrder && - nFlow > 0 ) { - /* reduce initial flow by 1 and increase st_cap on metal by 1 */ - nFlow --; - } - bNeedsFlower = (0 != pVA[iat].cMetal); - } else - if ( pVA[iat].cMetal && !at->endpoint && !pVA[neigh].cMetal && atom[neigh].endpoint|| - pVA[neigh].cMetal && !atom[neigh].endpoint && !pVA[iat].cMetal && at->endpoint ) { - /* M-ae */ - /* metal connected to a non-metal endpoint (3: M-Ae) */ - nMinorder = pSrm->nMetal2EndpointMinBondOrder; - nInitorder = pSrm->nMetal2EndpointInitBondOrder + bond_type - BOND_TYPE_SINGLE; - nFlow = nInitorder - nMinorder; - if ( !pSrm->nMetal2EndpointInitEdgeFlow && - pSrm->nMetal2EndpointInitBondOrder > pSrm->nMetal2EndpointMinBondOrder && - nFlow > 0 ) { - /* reduce initial flow by 1 and increase st_cap on metal by 1 */ - nFlow --; - } - bNeedsFlower = (0 != pVA[iat].cMetal); - } else { - /* endpoint is metal => no flower (4: M-Me, Me-Me, Me-A, Me-Ae) */ - nMinorder = pSrm->nMetal2EndpointMinBondOrder; - nInitorder = pSrm->nMetal2EndpointInitBondOrder + bond_type - BOND_TYPE_SINGLE; - nFlow = nInitorder - nMinorder; - if ( !pSrm->nMetal2EndpointInitEdgeFlow && - pSrm->nMetal2EndpointInitBondOrder > pSrm->nMetal2EndpointMinBondOrder && - nFlow > 0 ) { - /* reduce initial flow by 1 and increase st_cap on metal by 1 */ - nFlow --; - } - bNeedsFlower = (pVA[iat].cMetal && !at->endpoint); - } - nMaxcap = BOND_TYPE_TRIPLE - nMinorder; - if ( pnMaxcap ) { - *pnMaxcap = nMaxcap; - } - if ( pnMinorder ) { - *pnMinorder = nMinorder; - } - if ( pbNeedsFlower ) { - *pbNeedsFlower = bNeedsFlower; - } - return nFlow; -} -/*********** new *******************************************************************************************/ -int AtomStcapStflow( inp_ATOM *atom, VAL_AT *pVA, ICHICONST SRM *pSrm, int iat, int *pnStcap, int *pnStflow, - EdgeFlow *pnMGroupEdgeCap, EdgeFlow *pnMGroupEdgeFlow ) -{ - int ineigh, bFlower; - int nStflow=0, nMaxBondCap, nMinBondOrder, bNeedsFlower = 0; - int valence = atom[iat].valence; - int nStcap = atom[iat].chem_bonds_valence; - int nMGroupEdgeCap = 0, nMGroupEdgeFlow = 0, nFlow; - - if ( pSrm->bMetalAddFlower ) { - nStcap -= pVA[iat].cInitOrigValenceToMetal - pVA[iat].cInitValenceToMetal; - } - - for ( ineigh = 0; ineigh < valence; ineigh ++ ) { - nFlow = BondFlowMaxcapMinorder( atom, pVA, pSrm, iat, ineigh, &nMaxBondCap, &nMinBondOrder, &bFlower ); - nStflow += nFlow; - nStcap -= nMinBondOrder; - if ( bFlower ) { - bNeedsFlower ++; - nMGroupEdgeFlow += nFlow; - nMGroupEdgeCap += BOND_TYPE_TRIPLE - nMinBondOrder + pSrm->nMetalMaxCharge_D; - } - } - if ( pnStcap ) { - *pnStcap = bNeedsFlower? nStflow : nStcap; /* initially, metal atoms are not radicals */ - } - if ( pnStflow ) { - *pnStflow = nStflow; - } - if ( pnMGroupEdgeFlow ) { - *pnMGroupEdgeFlow = nMGroupEdgeCap - nMGroupEdgeFlow; - } - if ( pnMGroupEdgeCap ) { - *pnMGroupEdgeCap = nMGroupEdgeCap; - } - return bNeedsFlower; /* number of variable bonds to metal */ -} -/************************************************************************************** -int nCountBnsSizes( inp_ATOM *at, int num_at, int nAddEdges2eachAtom, int nAddVertices, - T_GROUP_INFO *ti, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups ) - -fills out totals: - - pTCGroups->num_atoms = number of atoms - pTCGroups->num_bonds = number of bonds between atoms - pTCGroups->num_tgroups = number of tautomeric groups - pTCGroups->num_tgroup_edges = number of edges to tautomeric groups - pTCGroups->tgroup_charge = total charge on thautomeric atoms (negative) - pTCGroups->num_tc_groups = total number of all groups - pTCGroups->nVertices = total number of vertices excluding groups interconnections - pTCGroups->nEdges = total number of edges excluding groups interconnections - -creates entries for the groups and adds to each group: - - TC_GROUP::type = BNS_VERT_TYPE_TGROUP, BNS_VT_C_POS, BNS_VT_C_NEG, BNS_VT_C_POS_C, BNS_VT_C_NEG_C - TC_GROUP::ord_num = ordering number within the type, e.g. t-group number - TC_GROUP::st_cap = all from the atoms in ChargeStruct or tautomeric group info. - TC_GROUP::st_flow = all from the atoms in ChargeStruct (0 for t-groups). - TC_GROUP::num_edges = number of edges to the atoms or ChargeStruct vertices. - TC_GROUP::edges_cap = sum of all incoming edge caps; see also nTautEndpointEdgeCap(..). - TC_GROUP::edges_flow = sum of all incoming edge flows; 0 for t-groups. - - TC_GROUP::nVertexNumber - NO FILLED WITH ANYTHING - - Note: the nDelta = st_cap - st_flow needs to be preserved when adding more vertices - -Return value: =0 => success - <0 => error - **************************************************************************************/ -int nCountBnsSizes( inp_ATOM *at, int num_at, int nAddEdges2eachAtom, int nAddVertices, - T_GROUP_INFO *ti, VAL_AT *pVA, ICHICONST SRM *pSrm, ALL_TC_GROUPS *pTCGroups ) -{ - int i, j, n, k, ret = 0, nBonds, nOtherEdges, nVertices, bMetalAtoms, bNeedsFlower; - int nTgroupEdges, nTgroupEdgesFromTg, nTotNegChargInTgroups, cap, flow; - MY_CONST C_NODE *pCN = NULL; - nVertices = nBonds = nOtherEdges = nTgroupEdges = nTgroupEdgesFromTg = nTotNegChargInTgroups = 0; - - /* count metal atoms and electrons */ - for ( i = 0; i < num_at; i ++ ) { - pTCGroups->num_metal_atoms += (pVA[i].cMetal != 0); - pTCGroups->num_metal_bonds += pVA[i].cNumBondsToMetal; - pTCGroups->total_electrons += at[i].el_number; - pTCGroups->total_electrons_metals += pVA[i].cMetal? at[i].el_number : 0; - } - pTCGroups->total_electrons -= pTCGroups->total_charge; - pTCGroups->num_metal_bonds /= 2; - - /* register tautomeric groups */ - for ( i = 0; i < ti->num_t_groups; i ++ ) { - ret = RegisterTCGroup( pTCGroups, BNS_VERT_TYPE_TGROUP, ti->t_group[i].nGroupNumber, - ti->t_group[i].num[0] /* st_cap */, 0 /* st_flow */, - 0 /* edge cap */, 0 /* edge flow */, ti->t_group[i].nNumEndpoints /* num Edges */ ); - if ( ret < 0 ) { - goto exit_function; - } - /* edges to tautomeric groups */ - nOtherEdges += ti->t_group[i].nNumEndpoints; - nTgroupEdgesFromTg += ti->t_group[i].nNumEndpoints; - /* total negative charge in t-groups */ - nTotNegChargInTgroups += ti->t_group[i].num[1]; - if ( ret > 0 ) { - /* should always happen since this is the first time this t-group is added */ - j = ret-1; - pTCGroups->pTCG[j].tg_num_H = ti->t_group[i].num[0] - ti->t_group[i].num[1]; - pTCGroups->pTCG[j].tg_num_Minus = ti->t_group[i].num[1]; - } - } - - bMetalAtoms = 0; - -repeat_for_metals: - - /* count vertices and register ChargeValence groups */ - /* for now an atom may belong either to a t-group or to a ChargeValence group, but not to both */ - for ( i = 0; i < num_at; i ++ ) { - /* number of bonds */ - nBonds += at[i].valence; - /* Process ChargeStruct vertices and edges */ - if ( pVA[i].cnListIndex ) { - /* count vertices & edges in the ChargeValence Substructure attached to an atom */ - /* Important: unlike inp_ATOM, each edge e appears in pCN[*].e[*] only ONE time */ - int len = cnList[j = pVA[i].cnListIndex-1].len; - int bits = cnList[j].bits; - int type, neigh_type, metal_group_number; - pCN = cnList[j].pCN; - - /* first process all non-metals, after that -- all metals */ - if ( (bits != cn_bits_Me) != !bMetalAtoms ) { - continue; - } - metal_group_number = 0; - for ( j = 0; j < len; j ++) { - type = pCN[j].v.type; /* ChargeStruct vertex type: atom is the first, c-groups are last */ - - /* process all pCN[j] neighbors */ - for ( k = 0; k < MAX_CN_VAL && (n = pCN[j].e[k].neigh); k ++ ) { - nOtherEdges ++; /* edges inside ChargeStruct */ - n --; /* neighbor vertex position inside cnList[j].pCN */ - neigh_type = pCN[n].v.type; /* type of the neighboring atom */ - - if ( IS_BNS_VT_C_GR(neigh_type) ) { - /* register this edge to a CN-group vertex */ - cap = !bMetalAtoms? pCN[j].e[k].cap : pCN[j].e[k].cap? pSrm->nMetalMaxCharge_D : 0; - flow = !bMetalAtoms? pCN[j].e[k].flow : pCN[j].e[k].flow? pSrm->nMetalMaxCharge_D : 0; - - ret = RegisterTCGroup( pTCGroups, neigh_type, 0 /* ord_num*/, - 0 /* st_cap */, 0 /* st_flow */, - cap /* edge cap*/, flow /* edge flow */, 1 /* nNumEdges*/); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret > 0 ) { - /* the group has just been created; add one more edge to (+/-) or supergroup */ - ret = RegisterTCGroup( pTCGroups, neigh_type, 0 /* ord_num*/, - 0 /* st_cap */, 0 /* st_flow */, - 0 /* edge cap*/, 0/* edge flow*/, 1 /* nNumEdges*/); - if ( ret < 0 ) { - goto exit_function; - } - nOtherEdges ++; - } - } - - if ( IS_BNS_VT_C_GR(type) ) { - /* register this edge to a CN-group vertex; normally this does not happen */ - cap = !bMetalAtoms? pCN[j].e[k].cap : pCN[j].e[k].cap? pSrm->nMetalMaxCharge_D : 0; - flow = !bMetalAtoms? pCN[j].e[k].flow : pCN[j].e[k].flow? pSrm->nMetalMaxCharge_D : 0; - ret = RegisterTCGroup( pTCGroups, type, 0 /* ord_num*/, - 0 /* st_cap */, 0 /* st_flow */, - cap /* edge cap*/, flow /* edge flow */, 1 /* nNumEdges*/); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret > 0 ) { - /* the group has just been created; add one more edge to (+/-) or supergroup */ - ret = RegisterTCGroup( pTCGroups, type, 0 /* ord_num*/, - 0 /* st_cap */, 0 /* st_flow */, - 0 /* edge cap*/, 0/* edge flow*/, 1 /* nNumEdges*/); - if ( ret < 0 ) { - goto exit_function; - } - nOtherEdges ++; - } - } - } /* end of the current vertex pCN[j] neighbors */ - - /* process pCN[j] vertex */ - - if ( type & BNS_VERT_TYPE_ATOM ) { - continue; /* do not count regular atoms here */ - } - if ( IS_BNS_VT_CHRG_STRUCT(type) ) { - nVertices ++; - continue; - } - - if ( pSrm->bMetalAddFlower && IS_BNS_VT_M_GR( type ) ) { - /* special treatment: flow and cap are known as well as structure */ - /* initial bond valence to metal is either 0 or 1 */ - EdgeFlow nEdgeFlow, nEdgeCap; - bNeedsFlower = AtomStcapStflow( at, pVA, pSrm, i, NULL /*pnStcap*/, NULL /*pnStflow*/, - &nEdgeCap, &nEdgeFlow ); - if ( !bNeedsFlower ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - /* - GetAtomToMCGroupInitEdgeCapFlow( &nEdgeCap, &nEdgeFlow, pSrm, at, pVA, i ); - GetAtomToMCGroupInitEdgeCapFlow( &nEdgeCap, &nEdgeFlow, pSrm ); - */ - /* the 1st is the flower base */ - /* atom - G0 edge and G0 vertex */ - ret = RegisterTCGroup( pTCGroups, type, 0 /* ord_num*/, - /*pVA[i].cInitFreeValences*/ 0 /* st_cap */, 0 /* st_flow */, - (int)nEdgeCap, (int)nEdgeFlow, 1 /* nNumEdges*/); - if ( ret < 0 ) { - goto exit_function; - } - /* count edge atom-G0 */ - nOtherEdges ++; - if ( ret > 0 ) { - /* first time registration: add G0-G1 and G0-G2 edges to G0 */ - ret = RegisterTCGroup( pTCGroups, type, 0 /* ord_num*/, - 0 /* st_cap */, 0 /* st_flow */, - 0,/* edge cap*/ 0 /*edge flow*/, 2 /* nNumEdges*/); - - if ( ret < 0 ) { - goto exit_function; - } - /* first time registration: add G1; it has 3 edges */ - ret = RegisterTCGroup( pTCGroups, type, 1 /* ord_num*/, - 0 /* st_cap */, 0 /* st_flow */, - 0,/* edge cap*/ 0 /*edge flow*/, 3 /* nNumEdges*/); - - if ( ret <= 0 ) { - ret = !ret? RI_ERR_PROGR : ret; - goto exit_function; - } - /* first time registration: add G2; it has 3 edges */ - ret = RegisterTCGroup( pTCGroups, type, 2 /* ord_num*/, - 0 /* st_cap */, 0 /* st_flow */, - 0,/* edge cap*/ 0 /*edge flow*/, 3 /* nNumEdges*/); - - if ( ret <= 0 ) { - ret = !ret? RI_ERR_PROGR : ret; - goto exit_function; - } - /* first time registration: add G3; it has 2 edges */ - ret = RegisterTCGroup( pTCGroups, type, 3 /* ord_num*/, - 0 /* st_cap */, 0 /* st_flow */, - 0,/* edge cap*/ 0 /*edge flow*/, 2 /* nNumEdges*/); - - if ( ret <= 0 ) { - ret = !ret? RI_ERR_PROGR : ret; - goto exit_function; - } - /* count added metal flower vertices: G0, G1, G2, G3 */ - nVertices += 4; - /* count added metal flower edges: C0-C1, C0-C2, C1-C2, C1-C3, C2-C3 */ - nOtherEdges += 5; - /* add connections of G0 to G1 and G2 */ - } - continue; - } - - nVertices ++; /* count BNS_VT_C_POS* types; all contain BNS_VERT_TYPE_C_GROUP bit */ - if ( !IS_BNS_VT_C_GR(type) ) { /* check */ - ret = RI_ERR_PROGR; - goto exit_function; - } - /* add st_cap and st_flow for a charge group */ - cap = !bMetalAtoms? pCN[j].v.cap : pCN[j].v.cap? pSrm->nMetalMaxCharge_D : 0; - flow = !bMetalAtoms? pCN[j].v.flow : pCN[j].v.flow? pSrm->nMetalMaxCharge_D : 0; - ret = RegisterTCGroup( pTCGroups, type, 0 /* ord_num*/, - cap /* st-cap*/, flow /* st-flow */, - 0 /* edge cap */, 0 /* edge flow */, 0 /* edges already counted */ ); - if ( ret < 0 ) { - goto exit_function; - } - } - } else { - pCN = NULL; - } - /* count edge caps to t-groups */ - if ( at[i].endpoint ) { - int nEdgeCap = nTautEndpointEdgeCap( at, pVA, i ); - nTgroupEdges ++; - if ( nEdgeCap < 0 ) { - ret = nEdgeCap; - goto exit_function; - } - /* add number of unsatisfied valences for a t-group; the unknown flow = 0 */ - ret = RegisterTCGroup( pTCGroups, BNS_VERT_TYPE_TGROUP, at[i].endpoint, - 0 /* st_cap */, 0 /* st_flow */, - nEdgeCap /* edge cap */, 0 /* edge flow */, - 0 /* t-group edges have already been counted */ ); - if ( ret < 0 ) { - goto exit_function; - } - - } - } - if ( !bMetalAtoms && pTCGroups->num_metal_atoms ) { - bMetalAtoms = 1; - nBonds = 0; /* added 2006-05-15 */ - goto repeat_for_metals; - } - - /* count real atoms and bonds */ - nBonds /= 2; - pTCGroups->num_atoms = num_at; - pTCGroups->num_bonds = nBonds; - - pTCGroups->num_tgroups = ti->num_t_groups; - pTCGroups->num_tgroup_edges = nTgroupEdges; - pTCGroups->tgroup_charge = -nTotNegChargInTgroups; - - if ( 0 <= ret && nTgroupEdgesFromTg != nTgroupEdges ) { - ret = BNS_PROGRAM_ERR; - } - - nVertices += num_at; - - - /* count other vertices */ - nVertices += ti->num_t_groups; - nBonds += nOtherEdges; - - /* return edges and vertices */ - pTCGroups->nVertices = nVertices; - pTCGroups->nEdges = nBonds; - -exit_function: - return ret; -} - -/**************************************************************** - int nAddSuperCGroups( ALL_TC_GROUPS *pTCGroups ) - - 1. adds BNS_VT_C_POS_ALL and BNS_VT_C_NEG_ALL ONLY if both - {TCG_Plus0 and TCG_Plus_C0} and/or - {TCG_Minus0 and TCG_Minus_C0} are present, respectively - - 2. fills pTCGroups->nGroup[]: - - pTCGroups->nGroup[k] < 0 => does not exist - pTCGroups->nGroup[k] = i => the group is pTCGroups->pTCG[i] - - where group group - k = type number - TCG_Plus0 BNS_VT_C_POS 0 - TCG_Plus1, BNS_VT_C_POS 1 - TCG_Minus0, BNS_VT_C_NEG 0 - TCG_Minus1, BNS_VT_C_NEG 1 - TCG_Plus_C0, BNS_VT_C_POS_C 0 - TCG_Plus_C1, BNS_VT_C_POS_C 1 - TCG_Minus_C0, BNS_VT_C_NEG_C 0 - TCG_Minus_C1, BNS_VT_C_NEG_C 1 - TCG_Plus, BNS_VT_C_POS_ALL 0 - TCG_Minus, BNS_VT_C_NEG_ALL 0 - -only groups with number 0 are processed - - 3. If only one of the groups in pairs mentioned in (1) above - is present then - - pTCGroups->nGroup[TCG_Plus] := pTCGroups->nGroup[TCG_Plus0] or - pTCGroups->nGroup[TCG_Plus] := pTCGroups->nGroup[TCG_Plus_C0]; - an additional BNS_VT_C_POS_ALL vertex is not created - - same for pTCGroups->nGroup[TCG_Minus] and BNS_VT_C_NEG_ALL - - 4. Adds to these new "supergroups" (TCG_Plus, TCG_Minus) - descriptions in pTCGroups->pTCG[k] - st_cap, st_flow, edges cap and flow from the corresponding - groups {TCG_Plus0 and TCG_Plus_C0}. Same for the Minus groups. - Stores indexes k in - pTCGroups->nGroup[TCG_Plus], pTCGroups->nGroup[TCG_Minus] - - ****************************************************************/ -int nAddSuperCGroups( ALL_TC_GROUPS *pTCGroups ) -{ - int i, k, n, n1, n2, n3, nNumTg = 0, ret = 0, nNumToConnect; - - for ( i = 0; i < pTCGroups->num_tc_groups; i ++ ) { - if ( pTCGroups->pTCG[i].type & BNS_VERT_TYPE_TGROUP ) { - nNumTg ++; - continue; /* t-group */ - } - if ( IS_BNS_VT_C_GR(pTCGroups->pTCG[i].type) || - IS_BNS_VT_M_GR(pTCGroups->pTCG[i].type) ) { - /* ChargeValence (cn) group */ - switch( pTCGroups->pTCG[i].type ) { - case BNS_VT_C_POS: - k = TCG_Plus0; - break; - case BNS_VT_C_NEG: - k = TCG_Minus0; - break; - case BNS_VT_C_POS_C: - k = TCG_Plus_C0; - break; - case BNS_VT_C_NEG_C: - k = TCG_Minus_C0; - break; - case BNS_VT_C_POS_M: - k = TCG_Plus_M0; - break; - case BNS_VT_C_NEG_M: - k = TCG_Minus_M0; - break; - case BNS_VT_M_GROUP: - switch( pTCGroups->pTCG[i].ord_num ) { - case 0: - k = TCG_MeFlower0; - break; - case 1: - k = TCG_MeFlower1; - break; - case 2: - k = TCG_MeFlower2; - break; - case 3: - k = TCG_MeFlower3; - break; - default: - ret = RI_ERR_PROGR; /* unexpected group type */ - goto exit_function; - } - break; - - default: - ret = RI_ERR_PROGR; /* unexpected group type */ - goto exit_function; - } - if ( pTCGroups->nGroup[k] >= 0 || pTCGroups->pTCG[i].ord_num && !IS_BNS_VT_M_GR(pTCGroups->pTCG[i].type) ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - pTCGroups->nGroup[k] = i; /* ordering number of the Charge group, starting from 0 */ - } - } - /* add (+) supergroup */ - n1 = pTCGroups->nGroup[TCG_Plus0]; - n2 = pTCGroups->nGroup[TCG_Plus_C0]; - n3 = pTCGroups->nGroup[TCG_Plus_M0]; - nNumToConnect = (n1>=0) + (n2>=0) + (n3>=0); - if ( nNumToConnect ) { - /* if both groups are present then add a supergroup */ - ret = RegisterTCGroup( pTCGroups, BNS_VT_C_POS_ALL, 0, - 0 /* st_cap */, - 0 /* st_flow */, - 0 /* edge cap */, - 0 /* edge flow */, - 1+nNumToConnect /* one more edge to connect to an additional (+/-) vertex */ ); - - if ( ret <= 0 ) { - ret = !ret? RI_ERR_PROGR : ret; - goto exit_function; - } - pTCGroups->nGroup[TCG_Plus] = ret - 1; /* newly added group number */ - pTCGroups->nVertices += 2; /* two vertices including itself */ - pTCGroups->nEdges += 1 + nNumToConnect; /* one more edge to connect to an additional (+/-) vertex */ - } - /* add (-) supergroup */ - n1 = pTCGroups->nGroup[TCG_Minus0]; - n2 = pTCGroups->nGroup[TCG_Minus_C0]; - n3 = pTCGroups->nGroup[TCG_Minus_M0]; - nNumToConnect = (n1>=0) + (n2>=0) + (n3>=0); - if ( nNumToConnect ) { - /* if both groups are present then add a supergroup */ - ret = RegisterTCGroup( pTCGroups, BNS_VT_C_NEG_ALL, 0, - 0 /* st_cap */, - 0 /* st_flow */, - 0 /* edge cap */, - 0 /* edge flow */, - 1+nNumToConnect /* one more edge to connect to an additional (+/-) vertex */ ); - - if ( ret < 0 ) { - goto exit_function; - } - pTCGroups->nGroup[TCG_Minus] = ret - 1; /* newly added group number */ - pTCGroups->nVertices += 2; /* needs two vertices including itself */ - pTCGroups->nEdges += 1 + nNumToConnect; /* one more edge to connect to an additional (+/-) vertex */ - } - - /* add neutralization vertex: (+)-()=(-) connection */ - k = pTCGroups->nGroup[TCG_Minus]; - n = pTCGroups->nGroup[TCG_Plus]; - nNumToConnect = (k>=0) + (n>=0); - if ( nNumToConnect ) { - pTCGroups->nVertices += 1; - pTCGroups->nEdges += nNumToConnect; /* one edge per super-c-group */ - } - - ret = 0; - -exit_function: - return ret; -} -/*********************************************************************************/ -int AddTGroups2TCGBnStruct( BN_STRUCT *pBNS, StrFromINChI *pStruct, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, int nMaxAddEdges ) -{ - int ret = 0; - inp_ATOM *at = pStruct->at; - int num_atoms = pStruct->num_atoms; - int tot_st_cap, tot_st_flow; - /* ret = ReInitBnStruct( pBNS ); */ - if ( pTCGroups->num_tgroups /* tgi && tgi->num_t_groups && tgi->t_group*/ ) { - int i, k, endpoint, /*centerpoint,*/ fictpoint; - int num_tg = pTCGroups->num_tgroups; - int num_edges = pBNS->num_edges; - int num_vertices = pBNS->num_vertices; - BNS_VERTEX *vert_ficpoint, *vert_ficpoint_prev; /* fictitious vertex describing t-group */ - BNS_VERTEX *vert_endpoint; - BNS_EDGE *edge; /* edge between that vertex and the tautomeric endpoint */ - int nMaxTGroupNumber = 0; - /*ENDPOINT_INFO eif;*/ - - /* Debug: check overflow */ - if ( num_vertices + num_tg >= pBNS->max_vertices ) { - return BNS_VERT_EDGE_OVFL; - } - if ( num_edges + pTCGroups->num_tgroup_edges >= pBNS->max_edges ) { - return BNS_VERT_EDGE_OVFL; - } - /* find the largest t-group ID */ - for ( i = 0; i < pTCGroups->num_tc_groups; i ++ ) { - if ( pTCGroups->pTCG[i].type & BNS_VERT_TYPE_TGROUP ) { - k = pTCGroups->pTCG[i].ord_num; - if ( k <= 0 ) { - return BNS_CPOINT_ERR; /* t-group does not have a number or has a wrong number */ - } - if ( k > pTCGroups->num_tc_groups ) { - return BNS_CPOINT_ERR; /* t-group has a wrong number */ - } - if ( k != nMaxTGroupNumber + 1 ) { - return BNS_CPOINT_ERR; /* t-group numbers are not contiguously ascending */ - } - nMaxTGroupNumber = k; - } else { - break; /* t-groups are contiguous and first in the list */ - } - } - if ( i != num_tg ) { - return BNS_CPOINT_ERR; /* number of t-groups is wrong */ - } - /* since t-group IDs may be not contiguous, clear all vertices that will be added. - all-zeroes-vertex will be ignored by the BNS - */ - memset( pBNS->vert+num_vertices, 0, nMaxTGroupNumber*sizeof(pBNS->vert[0]) ); - /* initialize new fictitious vertices */ - vert_ficpoint_prev = pBNS->vert+num_vertices - 1; - - tot_st_cap = tot_st_flow = 0; - - for ( i = 0; i < num_tg; i ++, vert_ficpoint_prev = vert_ficpoint ) { - /* - vert_ficpoint-1 is the last vertex; - vert_ficpoint is the vertex that is being added - Note: nGroupNumber are not contiguous - */ - vert_ficpoint = pBNS->vert+num_vertices + pTCGroups->pTCG[i].ord_num - 1; - vert_ficpoint->iedge = vert_ficpoint_prev->iedge + vert_ficpoint_prev->max_adj_edges; - vert_ficpoint->max_adj_edges = pTCGroups->pTCG[i].num_edges+nMaxAddEdges+BNS_ADD_SUPER_TGROUP; - vert_ficpoint->num_adj_edges = 0; - vert_ficpoint->st_edge.flow = vert_ficpoint->st_edge.flow0 = 0; - vert_ficpoint->st_edge.cap = vert_ficpoint->st_edge.cap0 = pTCGroups->pTCG[i].st_cap; - tot_st_cap += pTCGroups->pTCG[i].st_cap; - vert_ficpoint->type = pTCGroups->pTCG[i].type; - pTCGroups->pTCG[i].nVertexNumber = vert_ficpoint - pBNS->vert; - } - - for ( endpoint = 0; endpoint < num_atoms; endpoint ++ ) { - if ( !at[endpoint].endpoint ) - continue; - fictpoint = at[endpoint].endpoint + num_vertices - 1; - vert_ficpoint = pBNS->vert + fictpoint; /* t-group vertex */ - vert_endpoint = pBNS->vert + endpoint; /* endpoint vertex */ - /* Debug: check overflow */ - if ( fictpoint >= pBNS->max_vertices || - num_edges >= pBNS->max_edges || - vert_ficpoint->num_adj_edges >= vert_ficpoint->max_adj_edges || - vert_endpoint->num_adj_edges >= vert_endpoint->max_adj_edges ) { - ret = BNS_VERT_EDGE_OVFL; - break; - } -#ifdef NEVER - /* obtain donor/acceptor info */ - if ( !nGetEndpointInfo( at, endpoint, &eif ) ) { - ret = BNS_BOND_ERR; - break; - } -#endif - vert_endpoint->type |= BNS_VERT_TYPE_ENDPOINT; -#ifdef NEVER - /* set capacity = 1 to the edges from the endpoint to the centerpoint(s) */ - for ( k = 0; k < vert_endpoint->num_adj_edges; k ++ ) { - int iedge = vert_endpoint->iedge[k]; - if ( !pBNS->edge[iedge].cap ) { - /* single bond, possibly between endpoint and centerpoint */ - centerpoint = (pBNS->edge[iedge].neighbor12 ^ endpoint); - if ( centerpoint < pBNS->num_atoms && - pBNS->vert[centerpoint].st_edge.cap >= 1 ) { - int bond_type = (at[endpoint].bond_type[k] & BOND_TYPE_MASK); - if (bond_type == BOND_TAUTOM || - bond_type == BOND_ALTERN || - bond_type == BOND_ALT12NS || - bond_type == BOND_SINGLE ) { - pBNS->edge[iedge].cap = 1; - } - } - } - } -#endif - /* create a new edge connecting endpoint to the new fictitious t-group vertex vert_ficpoint */ - edge = pBNS->edge + num_edges; - edge->cap = vert_endpoint->st_edge.cap - vert_endpoint->st_edge.flow; - edge->cap = inchi_min( edge->cap, MAX_TGROUP_EDGE_CAP ); - edge->cap = inchi_max( edge->cap, 0 ); - edge->flow = 0; - edge->pass = 0; -#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) - edge->forbidden &= pBNS->edge_forbidden_mask; -#endif - -#ifdef NEVER - /* later include case when the charge change allows the endpoint to become tautomeric */ - /* mark endoint having moveable H atom with flow=1 */ - - /* -- old "no charges" version -- */ - /* if (at[endpoint].chem_bonds_valence == at[endpoint].valence) */ - /* -- the following line takes charges into account -- */ - if ( eif.cDonor ) /* means the endpoint has an H-atom to donate */ - { - /* increment edge flow */ - edge->flow ++; - /* increment one vertex st-flow & cap */ - vert_ficpoint->st_edge.flow ++; - vert_ficpoint->st_edge.cap ++; - /* increment another vertex st-flow & cap */ - vert_endpoint->st_edge.flow ++; - vert_endpoint->st_edge.cap ++; - } -#endif - /* connect edge to endpoint and fictpoint and increment the counters of neighbors and edges */ - ret = ConnectTwoVertices( vert_endpoint, vert_ficpoint, edge, pBNS, 0 ); - if ( IS_BNS_ERROR( ret ) ) { - break; - } - num_edges ++; - edge->cap0 = edge->cap; - edge->flow0 = edge->flow; - pVA[endpoint].nTautGroupEdge = num_edges; /* edge index + 1 */ - } - - pBNS->num_edges = num_edges; - pBNS->num_vertices += nMaxTGroupNumber; - pBNS->num_t_groups = num_tg; - pBNS->tot_st_cap += tot_st_cap; - pBNS->tot_st_flow += tot_st_flow; - - } - return ret; -} -/*****************************************************************************************************/ -int ConnectTwoVertices( BNS_VERTEX *p1, BNS_VERTEX *p2, BNS_EDGE *e, BN_STRUCT *pBNS, int bClearEdge ) -{ - int ip1 = p1 - pBNS->vert; - int ip2 = p2 - pBNS->vert; - int ie = e - pBNS->edge; - /* debug: check bounds */ - if ( ip1 >= pBNS->max_vertices || ip1 < 0 || - ip2 >= pBNS->max_vertices || ip2 < 0 || - ie >= pBNS->max_edges || ie < 0 || - (p1->iedge - pBNS->iedge) < 0 || - (p1->iedge - pBNS->iedge) + p1->max_adj_edges > pBNS->max_iedges || - (p2->iedge - pBNS->iedge) < 0 || - (p2->iedge - pBNS->iedge) + p2->max_adj_edges > pBNS->max_iedges || - p1->num_adj_edges >= p1->max_adj_edges || - p2->num_adj_edges >= p2->max_adj_edges ) { - return BNS_VERT_EDGE_OVFL; - } - /* clear the edge */ - if ( bClearEdge ) { - memset( e, 0, sizeof(*e) ); - } else - if ( e->neighbor1 || e->neighbor12 ) { - return BNS_PROGRAM_ERR; - } - /* connect */ - e->neighbor1 = inchi_min( ip1, ip2 ); - e->neighbor12 = ip1 ^ ip2; - p1->iedge[p1->num_adj_edges] = ie; - p2->iedge[p2->num_adj_edges] = ie; - e->neigh_ord[ip1 > ip2] = p1->num_adj_edges ++; - e->neigh_ord[ip1 < ip2] = p2->num_adj_edges ++; - return 0; -} - -/*********************************************************************************************************** - METAL ATOMS' FLOWER - Provides a source/sink of "free valences" - *********************************************************************************************************** - - c1+...+cn = 2c+dc - total cap and flow of edges to the flower base from metal atoms - f1+...+fn = 2f+df they should allow changing bonds to metals from 0-order to triple - dc,df = 0 or 1 hence c=3*n, f=0 (initial zero bond order) or n - Gi=vertex(M-group) - Ci=its st_cap [C3,F3] C0 = F0 = 2c + 2D + dc (st_cap & st_flow) - Fi=its st_flow G3 C2 = F2 = c + 2D - / \ C1 = F1 = c + 2D + dc-df - ci=cap of edge i cx,fx/ \cy,fy C3 = F3 = 0 - fi=edge flow / \ Constraints - [C2,F2]/ cd,fd \[C1,F1] ----------------- - G2--------G1 fa+fb+2f+df=F0=C0 - \ / ca = c + 2D (edge cap) fa+fd =F2=C2 - ca,fa \ / cb,fb fa = c + D - f (edge flow) fb+fd =C1=F1 - \ / fi <= ci - G0 [C0,F0] cb = c + 2D + dc ----------------- - /\ fb = c + D + dc - (f + df) - ci=3, fi=0 or 1 c1,f1 /... \ cn,fn ------------------------------------ - / \ cd = c + 2D D is an arbitrary integer > 0 - all n Metal atoms: M1 ... Mn fd = f + D it allows to apply - C3++ (add st_flow to cancel radicals) - For each Mi add cap and flow=cap cx = cy = D D times. - to M-charge group fx = fy = 0 - -------------------------------------------------------------------------------------- - | f=0 | f=c, dc>=df | 0 <= 2f+df <= 2c+dc - edge +------------------------+-----------+----------+-------------+------------- - | flow | rescap | flow | rescap | flow | rescap - ----------+------------+-----------+-----------+----------+-------------+------------- - f1+..+fn | df | 2c+dc-df | 2c+df | dc-df | 2f+df | 2c-2f+dc-df - fa | c+D | D | D | c+D | c+D-f | c+D - fb | c+D+dc-df | D+df | D+dc-df | c+D+df | c+D+dc-f-df| c+D+df - fd | D | c+D | c+D | D | f+D | D - -------------------------------------------------------------------------------------- -***********************************************************************************************************/ -int AddRadicalToMetal( int *tot_st_cap, int *tot_st_flow, ICHICONST SRM *pSrm, BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups ) -{ - int iG0 = pTCGroups->nGroup[TCG_MeFlower0]; /* index in pTCGroups->pTCG[] */ - int iG1 = pTCGroups->nGroup[TCG_MeFlower1]; - int iG2 = pTCGroups->nGroup[TCG_MeFlower2]; - int iG3 = pTCGroups->nGroup[TCG_MeFlower3]; - int n = (iG0>=0) + (iG1>=0) + (iG2>=0) + (iG3>=0); - int vG0, vG1, vG2, vG3; /* M-vertex number */ - BNS_VERTEX *pG0=NULL, *pG1=NULL, *pG2=NULL, *pG3=NULL; - - if ( pTCGroups->num_metal_atoms && - pSrm->bMetalAddFlower && - *tot_st_cap % 2 && - n == 4 ) { - vG0 = pTCGroups->pTCG[iG0].nVertexNumber; - vG1 = pTCGroups->pTCG[iG1].nVertexNumber; - vG2 = pTCGroups->pTCG[iG2].nVertexNumber; - vG3 = pTCGroups->pTCG[iG3].nVertexNumber; - - pG0 = pBNS->vert+vG0; - pG1 = pBNS->vert+vG1; - pG2 = pBNS->vert+vG2; - pG3 = pBNS->vert+vG3; - - /* add 1 unit to metal flower st_cap */ - pG3->st_edge.cap ++; - pG3->st_edge.cap0 ++; - (*tot_st_cap) ++; - return 1; - } - return 0; -} -/***********************************************************************************************************/ -int ConnectMetalFlower( int *pcur_num_vertices, int *pcur_num_edges, - int *tot_st_cap, int *tot_st_flow, ICHICONST SRM *pSrm, - BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups ) -{ - int iG0 = pTCGroups->nGroup[TCG_MeFlower0]; /* index in pTCGroups->pTCG[] */ - int iG1 = pTCGroups->nGroup[TCG_MeFlower1]; - int iG2 = pTCGroups->nGroup[TCG_MeFlower2]; - int iG3 = pTCGroups->nGroup[TCG_MeFlower3]; - int n = (iG0>=0) + (iG1>=0) + (iG2>=0) + (iG3>=0); - int vG0, vG1, vG2, vG3; /* M-vertex number */ - int cur_num_edges = *pcur_num_edges; - int cur_num_vertices = *pcur_num_vertices; - BNS_VERTEX *pG0=NULL, *pG1=NULL, *pG2=NULL, *pG3=NULL; - BNS_EDGE *ea=NULL, *eb=NULL, *ed=NULL, *ex=NULL, *ey=NULL, *e; - int ia, ib, id, ix, iy; - int c, f, dc, df, ca, fa, cb, fb, cd, fd, cx, fx, cy, fy; - int C0, F0, C1, F1, C2, F2, C3, F3, D; - int ret = 0, i; - - if ( 0 == n ) { - goto exit_function; - } - if ( 4 != n ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - vG0 = pTCGroups->pTCG[iG0].nVertexNumber; - vG1 = pTCGroups->pTCG[iG1].nVertexNumber; - vG2 = pTCGroups->pTCG[iG2].nVertexNumber; - vG3 = pTCGroups->pTCG[iG3].nVertexNumber; - - pG0 = pBNS->vert+vG0; - pG1 = pBNS->vert+vG1; - pG2 = pBNS->vert+vG2; - pG3 = pBNS->vert+vG3; - - /* count G0 edges cap and flow (currently only atoms are connected to G0) */ - for ( i = 0, c = 0, f = 0; i < pG0->num_adj_edges; i ++ ) { - e = pBNS->edge + pG0->iedge[i]; - c += e->cap; - f += e->flow; - } - - /* consistency checks */ - if ( !IS_BNS_VT_M_GR(pTCGroups->pTCG[iG0].type) && - (pTCGroups->pTCG[iG0].edges_cap != pG0->st_edge.cap || - pTCGroups->pTCG[iG0].edges_flow != pG0->st_edge.flow) ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - if ( pTCGroups->pTCG[iG0].edges_cap != c || - pTCGroups->pTCG[iG0].edges_flow != f ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - - /* get new edges */ - - ea = pBNS->edge + (ia=cur_num_edges++); - eb = pBNS->edge + (ib=cur_num_edges++); - ed = pBNS->edge + (id=cur_num_edges++); - ex = pBNS->edge + (ix=cur_num_edges++); - ey = pBNS->edge + (iy=cur_num_edges++); - - /* connect vertices with edges */ - ret = ConnectTwoVertices( pG0, pG1, eb, pBNS, 1 ); - if ( IS_BNS_ERROR( ret ) ) { - goto exit_function; - } - ret = ConnectTwoVertices( pG0, pG2, ea, pBNS, 1 ); - if ( IS_BNS_ERROR( ret ) ) { - goto exit_function; - } - ret = ConnectTwoVertices( pG1, pG2, ed, pBNS, 1 ); - if ( IS_BNS_ERROR( ret ) ) { - goto exit_function; - } - ret = ConnectTwoVertices( pG1, pG3, ey, pBNS, 1 ); - if ( IS_BNS_ERROR( ret ) ) { - goto exit_function; - } - ret = ConnectTwoVertices( pG2, pG3, ex, pBNS, 1 ); - if ( IS_BNS_ERROR( ret ) ) { - goto exit_function; - } - - /* calculate caps and flows */ - - dc = c % 2; - c /= 2; - df = f % 2; - f /= 2; - - D = pSrm->nMetalFlowerParam_D; - - C0 = F0 = 2*c + 2*D + dc; - C1 = F1 = c + 2*D + dc - df; - C2 = F2 = c + 2*D; - C3 = F3 = 0; - - ca = c + 2*D; - fa = c + D - f; - - cb = c + 2*D + dc; - fb = c + D + dc - ( f + df ); - - cd = c + 2*D; - fd = f + D; - - cx = cy = D; - fx = fy = 0; - - /* check overflow */ - if ( C0 >= EDGE_FLOW_ST_MASK || F0 >= EDGE_FLOW_ST_MASK || - C1 >= EDGE_FLOW_ST_MASK || F1 >= EDGE_FLOW_ST_MASK || - C2 >= EDGE_FLOW_ST_MASK || F2 >= EDGE_FLOW_ST_MASK || - C3 >= EDGE_FLOW_ST_MASK || F3 >= EDGE_FLOW_ST_MASK ) { - return BNS_PROGRAM_ERR; /* cannot handle too large st-cap or st-flow */ - } - - /* set st caps and flows */ - - SetStCapFlow( pG0, tot_st_flow, tot_st_cap, C0, F0 ); - SetStCapFlow( pG1, tot_st_flow, tot_st_cap, C1, F1 ); - SetStCapFlow( pG2, tot_st_flow, tot_st_cap, C2, F2 ); - SetStCapFlow( pG3, tot_st_flow, tot_st_cap, C3, F3 ); - - SetEdgeCapFlow( ea, ca, fa ); - SetEdgeCapFlow( eb, cb, fb ); - SetEdgeCapFlow( ed, cd, fd ); - SetEdgeCapFlow( ex, cx, fx ); - SetEdgeCapFlow( ey, cy, fy ); - - - *pcur_num_edges = cur_num_edges; - *pcur_num_vertices = cur_num_vertices; - - ret = 0; - -exit_function: - - return ret; -} -/********************************************************************************/ -void SetEdgeCapFlow( BNS_EDGE *e, int edge_cap, int edge_flow ) -{ - e->cap = e->cap0 = edge_cap; - e->flow = e->flow0 = edge_flow; -} - -/********************************************************************************* - Add cap and flow to an edge - Add edge flow to the source vertex st_flow - Add edge cap & flow to the destination vertex cap and flow - *********************************************************************************/ -int AddEdgeFlow( int edge_cap, int edge_flow, BNS_EDGE *e01, BNS_VERTEX *pSrc /*src*/, - BNS_VERTEX *pDst/*dest*/, int *tot_st_cap, int *tot_st_flow ) -{ - /* overflow chaeck */ - if ( e01->cap < 0 || edge_cap < 0 || (int)e01->cap + edge_cap >= EDGE_FLOW_MASK ) { - return BNS_PROGRAM_ERR; - } - if ( pDst->st_edge.cap < 0 || (int)pDst->st_edge.cap + edge_cap >= EDGE_FLOW_ST_MASK || - pDst->st_edge.flow < 0 || (int)pDst->st_edge.flow + edge_flow >= EDGE_FLOW_ST_MASK || - pSrc->st_edge.cap < 0 || pSrc->st_edge.flow < 0 || - (int)pSrc->st_edge.flow + edge_flow >= EDGE_FLOW_ST_MASK ) { - return BNS_PROGRAM_ERR; - } - /* add flow */ - e01->cap += edge_cap; - e01->flow += edge_flow; - e01->cap0 = e01->cap; - e01->flow0 = e01->flow; - - pDst->st_edge.cap += edge_cap; - pDst->st_edge.cap0 = pDst->st_edge.cap; - *tot_st_cap += edge_cap; - - pDst->st_edge.flow += edge_flow; - pDst->st_edge.flow0 = pDst->st_edge.flow; - *tot_st_flow += edge_flow; - - pSrc->st_edge.flow += edge_flow; - pSrc->st_edge.flow0 = pSrc->st_edge.flow; - *tot_st_flow += edge_flow; - -/* - pDst->st_edge.cap += e01->cap; - pDst->st_edge.cap0 = pDst->st_edge.cap; - *tot_st_cap += e01->cap; - - pDst->st_edge.flow += e01->flow; - pDst->st_edge.flow0 = pDst->st_edge.flow; - *tot_st_flow += e01->flow; - - pSrc->st_edge.flow += e01->flow; - pSrc->st_edge.flow0 = pSrc->st_edge.flow; - *tot_st_flow += e01->flow; -*/ - return 0; -} -/************************************************************** - (+) and (-) group V - connection - ================================ - - BNS_VERT_TYPE__AUX (+/-)-connection - (v) st_cap = - / \ st_flow = (cap0 - Delta0 - flow0) + (cap1 - Delta1 -flow1) - / \ - / \ cap = cap1 - / \ flow = (cap1 - Delta1 - flow1) - / \ - (-) (+) st_cap = cap1 - / \ / \ st_flow = cap1 - Delta1 - /cap0 \ /cap1 \ - flow0 flow1 - -*************************************************************** - - (+) supergroup Y - connection - ============================== - - (+) BNS_VT_C_POS_ALL (+) supergroup - Delta0 | ============== - not shown | cap = cap0+cap1 - | flow = flow0+flow1-Delta0-Delta1 - BNS_VERT_TYPE__AUX (y) <------------------ additional vertex: st_cap = cap0+cap1 - / \ st_flow = cap0+cap1 - cap=cap0 / \ cap = cap1 - flow=cap0-flow0 / \ flow = cap1 - flow1 - Delta1 - -Delta0 / \ - not-C (+) (+) Carbons st_cap = cap1 - BNS_VT_C_POS / \ / \ BNS_VT_C_POS_C st_flow = cap1 - Delta1 - / \ / \ - totals cap0 cap1 = sum of all cap going up into (+) from atoms or ChargeStruct - to (+): flow0 flow1= sum of all flow going up into (+) - Delta0 Delta1 = st_cap(+)-st_flow(+) before connection - Observations - ============ - A. Any Delta > 0 on (+) or (-) group decreases total (signed) charge by Delta - - B. Any alt path from an atom through ChargeStruct to an atom - does not change the total charge - - C. st_flow(+/-) = cap(+)-flow(+)-Delta(+) + cap(-)-flow(-)-Delta(-) = - = charge(+) + |max (-) charge| + charge(-) = const - (charge conservation) - - D. To decrease total charge: increase st_cap on (+) or (-) group, including supergroup - E. To increase total charge: increase st_cap on any (y) or (v)-connecting vertex - - F. To cancel charges: - 1. Forbid (+/-)-(+) or (+/-)-(-) edge - 2. Add delta>0 to (+/-) st_cap - 3. Add same delta to (+) or (-) st_cap - - -****************************************************************/ - -/************************************************************************************ - j2,j3 < j1 < j0 - - (+/-) <---- next step; if does not exist then - / \ st_cap1' := st_flow1' - / \ - / \ st_cap1' := cap01' - pv1 (+)super st_flow1':= cap01'-flow01' = flow02'+flow03' - j1 | - | cap01' := st_cap0' - | flow01':= st_cap0'-flow02'-flow03' - | - | st_cap0' := - ( ) pv0,j0 st_flow0':= cap2+st_cap3 - / \ - / \ cap03' = cap3 - / \ flow03' = cap3 - flow3 - Delta3 - / \ - st_cap2, st_flow2 (+) (+C) st_cap3' := cap3 - pv2,j2 pv3,j3 st_flow3' := cap3-Delta3 - / \ / \ Delta3 := st_cap3 - st_flow3 - cap2, flow2 cap3, flow3 = sums of incoming - **************************************************************************************/ - -int ConnectSuperCGroup( int nSuperCGroup, int nAddGroups[], int num_add, - int *pcur_num_vertices, int *pcur_num_edges, - int *tot_st_cap, int *tot_st_flow, - BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups ) -{ - BNS_EDGE **e0X = NULL, *e; - BNS_VERTEX **pvX = NULL, *pv0=NULL, *pv1=NULL, *pv=NULL; - int *jX = NULL, *iX = NULL; - int i, j, num_groups, j0, i1, j1, iXX, ret = 0, fst=0; - int cur_num_vertices = *pcur_num_vertices; - int cur_num_edges = *pcur_num_edges; - - if ( nSuperCGroup >= 0 ) { - i1 = pTCGroups->nGroup[nSuperCGroup]; /* the supergroup */ - if ( i1 < 0 ) - return 0; - } else { - i1 = -1; - fst = 1; - } - - for ( i = num_groups = 0; i < num_add; i ++ ) { - iXX = pTCGroups->nGroup[nAddGroups[i]]; - num_groups += (iXX >= 0 && iXX != i1); - } - if ( num_groups < 1 ) { /* Y connect only 2 or more groups; V connects even 1 group */ - return 0; - } - - e0X = (BNS_EDGE **)inchi_calloc( num_groups + 1, sizeof(e0X[0]) ); - pvX = (BNS_VERTEX **)inchi_calloc( num_groups + 1, sizeof(pvX[0]) ); - jX = (int *)inchi_calloc( num_groups + 1, sizeof(jX[0]) ); - iX = (int *)inchi_calloc( num_groups + 1, sizeof(iX[0]) ); - if ( !e0X || !pvX || !jX || !iX ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - /* create vert_ficpoint -- central Y-connection vertex */ - j0 = cur_num_vertices; - pv0 = pBNS->vert + j0; /* center of the Y-connection; has number j0 */ - pv0->iedge = (pv0 - 1)->iedge + (pv0 - 1)->max_adj_edges; - pv0->max_adj_edges = num_groups + 1 + BNS_ADD_EDGES; /* Y-connection num. edges */ - pv0->num_adj_edges = 0; /* nothing connected yet */ - pv0->type = BNS_VT_YVCONNECTOR; - cur_num_vertices ++; - - if ( fst == 0 ) { - /* find super c-group vertex pv1, number j1 */ - jX[0] = j1 = pTCGroups->pTCG[i1].nVertexNumber; - iX[0] = i1; - pvX[0] = pv1 = pBNS->vert + j1; - } - /* find other c-group vertices */ - for( i = 0, j = 1; i < num_add; i ++ ) { - iXX = pTCGroups->nGroup[nAddGroups[i]]; - if ( (iXX >= 0) && (iXX != i1) ) { - iX[j] = iXX; - jX[j] = pTCGroups->pTCG[iXX].nVertexNumber; - pvX[j] = pBNS->vert + jX[j]; - j ++; - } - } - - /* grab (num_groups+1) free edges */ - for ( i = fst; i <= num_groups; i ++ ) { - e = e0X[i] = pBNS->edge + cur_num_edges; - pv = pvX[i]; - j = jX[i]; - iXX = iX[i]; - /* connect all to pv0 */ - ret = ConnectTwoVertices( pv0, pv, e, pBNS, 1 ); - if ( IS_BNS_ERROR( ret ) ) { - goto exit_function; - } - if ( i ) { - /* from c-group to central Y-connecting vertex of from supergroup to (+/-) vertex */ - pTCGroups->pTCG[iX[i]].nForwardEdge = cur_num_edges; - } else { - /* from central Y-connecting vertex to supergroup */ - pTCGroups->pTCG[iX[i]].nBackwardEdge = cur_num_edges; - } - cur_num_edges ++; - } - /* set flow and cap for incoming into pv0 edges */ - for ( i = 1; i <= num_groups; i ++ ) { - int nDelta = pTCGroups->pTCG[iX[i]].st_cap - pTCGroups->pTCG[iX[i]].edges_cap; - int edge_cap = pTCGroups->pTCG[iX[i]].edges_cap + nDelta; /* added nDelta */ - int edge_flow = pTCGroups->pTCG[iX[i]].edges_cap-pTCGroups->pTCG[iX[i]].edges_flow /*-nDelta*/; - ret = AddEdgeFlow( edge_cap, edge_flow, - e0X[i], pvX[i]/*src*/, pv0 /* dest*/, tot_st_cap, tot_st_flow ); - if ( IS_BNS_ERROR( ret ) ) { - goto exit_function; - } - } - if ( fst == 0 ) { - /* set flow and cap for going out of pv0 and into pv1 edge */ - int edge_cap = pv0->st_edge.cap; - int edge_flow = pv0->st_edge.cap - pv0->st_edge.flow; - ret = AddEdgeFlow( pv0->st_edge.cap, pv0->st_edge.cap - pv0->st_edge.flow, - e0X[0], pv0/*src*/, pv1 /* dest*/, tot_st_cap, tot_st_flow ); - if ( IS_BNS_ERROR( ret ) ) { - goto exit_function; - } - pTCGroups->pTCG[iX[0]].edges_cap += edge_cap; - pTCGroups->pTCG[iX[0]].edges_flow += edge_flow; - pTCGroups->pTCG[iX[0]].st_cap += edge_cap; - pTCGroups->pTCG[iX[0]].st_flow += edge_flow; - } else { - /* no supergroup => change cap to flow */ - *tot_st_cap += pv0->st_edge.flow - pv0->st_edge.cap; - pv0->st_edge.cap += pv0->st_edge.flow - pv0->st_edge.cap; - pv0->st_edge.cap0 = pv0->st_edge.cap; - } - - *pcur_num_vertices = cur_num_vertices; - *pcur_num_edges = cur_num_edges; - ret = num_groups; -exit_function: - if ( e0X ) inchi_free( e0X ); - if ( pvX ) inchi_free( pvX ); - if ( jX ) inchi_free( jX ); - if ( iX ) inchi_free( iX ); - return ret; -} -/*********************************************************************************/ -void AddStCapFlow( BNS_VERTEX *vert_ficpoint, int *tot_st_flow, int *tot_st_cap, int cap, int flow ) -{ - vert_ficpoint->st_edge.flow += flow; - *tot_st_flow += flow; - vert_ficpoint->st_edge.cap += cap; - *tot_st_cap += cap; - - vert_ficpoint->st_edge.flow0 = vert_ficpoint->st_edge.flow; - vert_ficpoint->st_edge.cap0 = vert_ficpoint->st_edge.cap; -} -/*********************************************************************************/ -void SetStCapFlow( BNS_VERTEX *vert_ficpoint, int *tot_st_flow, int *tot_st_cap, int cap, int flow ) -{ - *tot_st_flow += flow - vert_ficpoint->st_edge.flow; - vert_ficpoint->st_edge.flow = flow; - *tot_st_cap += cap - vert_ficpoint->st_edge.cap; - vert_ficpoint->st_edge.cap = cap; - - vert_ficpoint->st_edge.flow0 = vert_ficpoint->st_edge.flow; - vert_ficpoint->st_edge.cap0 = vert_ficpoint->st_edge.cap; -} - -/********************************************************************************* -int AddCGroups2TCGBnStruct( BN_STRUCT *pBNS, StrFromINChI *pStruct, - VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups ) - - - *********************************************************************************/ -int AddCGroups2TCGBnStruct( BN_STRUCT *pBNS, StrFromINChI *pStruct, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, int nMaxAddEdges ) -{ - int ret = 0, ret1, ret2, ret3, bNeedsFlower; - inp_ATOM *at = pStruct->at; - int num_atoms = pStruct->num_atoms; - /*int num_tg = pTCGroups->num_tgroups;*/ - int num_cg = pTCGroups->num_tc_groups - pTCGroups->num_tgroups; - int fst_cg_vertex = pBNS->num_vertices; - int fst_cg_group = pTCGroups->num_tgroups; - int num_vertices = pBNS->num_vertices; - int num_edges = pBNS->num_edges; - int cg_charge = 0; - ICHICONST SRM *pSrm = pStruct->pSrm; - /* ret = ReInitBnStruct( pBNS ); */ - if ( num_cg > 0 ) { - /* if ( cgi && cgi->num_c_groups && cgi->c_group ) */ - int i, i1, i2, j, j1, j2, k, k1, k2, n, c_point, c_neigh, cap, flow; - int cur_num_vertices, cur_num_edges; - BNS_VERTEX *vert_ficpoint, *vert_ficpoint_prev, *vert_ficpoint_base; /* fictitious vertex describing charge c-group */ - BNS_VERTEX *pv1, *pv2; - BNS_EDGE *edge; /* edge between that vertex and the tautomeric c_point */ - int nMaxCGroupNumber = 0; - MY_CONST C_NODE *pCN; - int cn_len, cn_bits, bMetalAtoms; - int type; - int tot_st_cap, tot_st_flow; - int nAddGroups[16]; - - - /* Debug: check overflow */ - if ( num_vertices >= pBNS->max_vertices ) { - return BNS_VERT_EDGE_OVFL; - } - nMaxCGroupNumber = num_cg; - /* clear all vertices not used until now */ - memset( pBNS->vert+num_vertices, 0, (pBNS->max_vertices - num_vertices)*sizeof(pBNS->vert[0]) ); - tot_st_cap = pBNS->tot_st_cap; - tot_st_flow = pBNS->tot_st_flow; - /*****************************************/ - /* initialize new fictitious vertices */ - /* representing c-point groups, c-groups */ - /*****************************************/ - vert_ficpoint_prev = pBNS->vert + fst_cg_vertex - 1; - - for ( i = 0; i < num_cg; i ++ ) { - /* - vert_ficpoint-1 is the last vertex; - vert_ficpoint is the being added vertex - Note: nGroupNumber are not contiguous - */ - vert_ficpoint = vert_ficpoint_prev + 1; - vert_ficpoint->iedge = vert_ficpoint_prev->iedge + vert_ficpoint_prev->max_adj_edges; - vert_ficpoint->max_adj_edges = pTCGroups->pTCG[i+fst_cg_group].num_edges+nMaxAddEdges; - vert_ficpoint->num_adj_edges = 0; - - vert_ficpoint->st_edge.flow += pTCGroups->pTCG[i+fst_cg_group].st_flow; - tot_st_flow += pTCGroups->pTCG[i+fst_cg_group].st_flow; - vert_ficpoint->st_edge.cap += pTCGroups->pTCG[i+fst_cg_group].st_cap; - tot_st_cap += pTCGroups->pTCG[i+fst_cg_group].st_cap; - - vert_ficpoint->st_edge.flow0 = vert_ficpoint->st_edge.flow; - vert_ficpoint->st_edge.cap0 = vert_ficpoint->st_edge.cap; - - vert_ficpoint->type = pTCGroups->pTCG[i+fst_cg_group].type; - /* save the vertex number */ - pTCGroups->pTCG[i+fst_cg_group].nVertexNumber = vert_ficpoint - pBNS->vert; - - vert_ficpoint_prev = vert_ficpoint; /* keep track of iedges */ - } - cur_num_vertices = (vert_ficpoint_prev - pBNS->vert) + 1; - cur_num_edges = num_edges; - - /*************************************************************/ - /* pass 1: */ - /* create ChargeStruct for c-points and connect them to */ - /* the vertices representing c-point groups; */ - /* set final atom st_cap, st_flow */ - /*************************************************************/ - for ( c_point = 0; c_point < num_atoms; c_point ++ ) { - if ( !(k=pVA[c_point].cnListIndex) ) - continue; /* not a c-point */ - k --; - pCN = cnList[k].pCN; /* pointer to the ChargeStruct */ - cn_len = cnList[k].len; /* length of the ChargeStruct */ - cn_bits = cnList[k].bits; /* bits: for M-recognition */ - /* cn_bits = cnList[k].bits; */ /* ChargeStruct type */ - bMetalAtoms = (cn_bits == cn_bits_Me); - vert_ficpoint_base = vert_ficpoint_prev; /* add aux vertices after this */ - /* create disconnected auxiliary vertices of the at[c_point] ChargeStruct; add to them st_flow & st_cap */ - for ( i1 = 0; i1 < cn_len; i1 ++ ) { - if ( !IS_BNS_VT_CHRG_STRUCT(pCN[i1].v.type) ) { - continue; - } - /* the atom is always the first; the attached c-points are always the last */ - vert_ficpoint = vert_ficpoint_base + i1; /* i1 = 1, 2,.. less number of attached c-points */ - vert_ficpoint->iedge = vert_ficpoint_prev->iedge + vert_ficpoint_prev->max_adj_edges; - vert_ficpoint->max_adj_edges = pCN[i1].v.valence; /* do not add additional edges to aux vertices */ - vert_ficpoint->num_adj_edges = 0; - - cap = !bMetalAtoms? pCN[i1].v.cap : pCN[i1].v.cap? pSrm->nMetalMaxCharge_D : 0; - flow = !bMetalAtoms? pCN[i1].v.flow : pCN[i1].v.flow? pSrm->nMetalMaxCharge_D : 0; - - AddStCapFlow( vert_ficpoint, &tot_st_flow, &tot_st_cap, cap, flow ); - vert_ficpoint->type = pCN[i1].v.type; /* =BNS_VERT_TYPE__AUX */ - - vert_ficpoint_prev = vert_ficpoint; /* the last one will be vert_ficpoint for the next c-point */ - cur_num_vertices = (vert_ficpoint - pBNS->vert) + 1; - - if ( vert_ficpoint->iedge + vert_ficpoint->max_adj_edges - pBNS->iedge >= pBNS->max_iedges ) { - return BNS_VERT_EDGE_OVFL; - } - if ( cur_num_vertices >= pBNS->max_vertices ) { - return BNS_VERT_EDGE_OVFL; - } - } - /* connect the vertices with new edges, add edge flow and cap */ - for ( i1 = 0; i1 < cn_len; i1 ++ ) { - pv1 = NULL; - k1 = -1; - /* find vertex cooresponding to i1 */ - if ( pCN[i1].v.type & BNS_VERT_TYPE_ATOM ) { - pv1 = pBNS->vert+c_point; /* may be only one atom -- the current c_point at i1==0 */ - /* add atom vertex st_cap and st_flow */ - cap = !bMetalAtoms? pCN[i1].v.cap : pCN[i1].v.cap? pSrm->nMetalMaxCharge_D : 0; - flow = !bMetalAtoms? pCN[i1].v.flow : pCN[i1].v.flow? pSrm->nMetalMaxCharge_D : 0; - AddStCapFlow( pv1, &tot_st_flow, &tot_st_cap, cap, flow ); - } else - if ( IS_BNS_VT_C_GR(pCN[i1].v.type) ) { - /* find c-group vertex by looking for its type */ - for( j = 0; j < num_cg; j ++ ) { - if ( pCN[i1].v.type == pBNS->vert[fst_cg_vertex + j].type ) { - pv1 = pBNS->vert + fst_cg_vertex + j; - break; - } - } - /* index of the pTCGroups->pTCG[] */ - if ( pv1 ) { - k1 = j + fst_cg_group; - if ( pTCGroups->pTCG[k1].type != pCN[i1].v.type || - pTCGroups->pTCG[k1].ord_num ) { - return RI_ERR_PROGR; - } - } - } else - if ( IS_BNS_VT_M_GR( pCN[i1].v.type ) ) { - k1 = pTCGroups->nGroup[TCG_MeFlower0]; - if ( k1 < 0 || - pTCGroups->pTCG[k1].type != pCN[i1].v.type || - pTCGroups->pTCG[k1].ord_num || - !pSrm->bMetalAddFlower ) { - return RI_ERR_PROGR; - } - pv1 = pBNS->vert + pTCGroups->pTCG[k1].nVertexNumber; - } else - if ( IS_BNS_VT_CHRG_STRUCT(pCN[i1].v.type) ) { - /* aux vertex */ - pv1 = vert_ficpoint_base + i1; - } - if ( !pv1 ) { - return BNS_BOND_ERR; - } - - /* connect pairs of vertices with new edges */ - for ( k = 0; k < MAX_CN_VAL && (i2=pCN[i1].e[k].neigh); k ++ ) { - pv2 = NULL; - k2 = -1; - i2 --; /* neighbor */ - /* find vertex cooresponding to i2 */ - if ( pCN[i2].v.type & BNS_VERT_TYPE_ATOM ) { - pv2 = pBNS->vert+c_point; - cap = !bMetalAtoms? pCN[i2].v.cap : pCN[i2].v.cap? pSrm->nMetalMaxCharge_D : 0; - flow = !bMetalAtoms? pCN[i2].v.flow : pCN[i2].v.flow? pSrm->nMetalMaxCharge_D : 0; - /* add atom vertex st_cap and st_flow; this normally should not happen */ - AddStCapFlow( pv2, &tot_st_flow, &tot_st_cap, cap, flow ); - } else - if ( IS_BNS_VT_C_GR(pCN[i2].v.type) ) { - /* find c-group vertex by looking for its type */ - for( j = 0; j < num_cg; j ++ ) { - if ( pCN[i2].v.type == pBNS->vert[fst_cg_vertex + j].type ) { - pv2 = pBNS->vert + fst_cg_vertex + j; - break; - } - } - if ( pv2 ) { - k2 = j + fst_cg_group; - if ( pTCGroups->pTCG[k2].type != pCN[i2].v.type || - pTCGroups->pTCG[k2].ord_num ) { - return RI_ERR_PROGR; - } - } - } else - if ( IS_BNS_VT_M_GR( pCN[i2].v.type ) ) { - k2 = pTCGroups->nGroup[TCG_MeFlower0]; - if ( k2 < 0 || - pTCGroups->pTCG[k2].type != pCN[i2].v.type || - pTCGroups->pTCG[k2].ord_num || - !pSrm->bMetalAddFlower ) { - return RI_ERR_PROGR; - } - pv2 = pBNS->vert + pTCGroups->pTCG[k2].nVertexNumber; - } else - if ( IS_BNS_VT_CHRG_STRUCT(pCN[i2].v.type) ){ - pv2 = vert_ficpoint_base + i2; - } - - /* connect pv1 and pv2 */ - if ( !pv1 || !pv2 || pv1 == pv2 ) { - return BNS_BOND_ERR; - } - j1 = pv1 - pBNS->vert; - j2 = pv2 - pBNS->vert; - /* create a new edge connecting pv1 and pv2 */ - edge = pBNS->edge + cur_num_edges; - if ( IS_BNS_VT_M_GR( pCN[i1].v.type ) && IS_BNS_VT_ATOM( pCN[i2].v.type ) || - IS_BNS_VT_M_GR( pCN[i2].v.type ) && IS_BNS_VT_ATOM( pCN[i1].v.type ) ) { - /* at[c_point] is a metal or is treated as a metal; connect it to M-group */ - /* metal - M-group (i.e. Metal-Flower) edge */ - int nStCap, nStFlow; - bNeedsFlower = AtomStcapStflow( at, pVA, pSrm, c_point, &nStCap, &nStFlow, &edge->cap, &edge->flow ); - /* GetAtomToMCGroupInitEdgeCapFlow( &edge->cap, &edge->flow, pSrm, at, pVA, c_point ); */ - if ( !bNeedsFlower ) { - return RI_ERR_PROGR; - } - pVA[c_point].nMetalGroupEdge = cur_num_edges + 1; - /* pBNS->vert[c_point].st_edge.cap += edge->flow;*/ /* where was this done ???*/ - pBNS->vert[c_point].st_edge.flow += edge->flow; - pBNS->vert[c_point].st_edge.cap += edge->flow + pVA[c_point].cInitFreeValences; - pBNS->vert[c_point].st_edge.flow0 = pBNS->vert[c_point].st_edge.flow; - pBNS->vert[c_point].st_edge.cap0 = pBNS->vert[c_point].st_edge.cap; - tot_st_flow += edge->flow; - tot_st_cap += edge->flow + pVA[c_point].cInitFreeValences; - } else { - edge->cap = !bMetalAtoms? pCN[i1].e[k].cap : pCN[i1].e[k].cap? pSrm->nMetalMaxCharge_D : 0; - edge->flow = !bMetalAtoms? pCN[i1].e[k].flow : pCN[i1].e[k].flow? pSrm->nMetalMaxCharge_D : 0; - } - edge->forbidden = pCN[i1].e[k].bForbiddenEdge? BNS_EDGE_FORBIDDEN_MASK : 0; - /* c-group incoming edges cap and flow needed in ConnectSuperCGroup() */ - /* - if ( k1 >= 0 ) { - pTCGroups->pTCG[k1].edges_cap += pCN[i1].e[k].cap; - pTCGroups->pTCG[k1].edges_flow += pCN[i1].e[k].flow; - } - if ( k2 >= 0 ) { - pTCGroups->pTCG[k2].edges_cap += pCN[i1].e[k].cap; - pTCGroups->pTCG[k2].edges_flow += pCN[i1].e[k].flow; - } - */ - edge->pass = 0; -#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) - edge->forbidden &= pBNS->edge_forbidden_mask; -#endif - /* check edge overflow */ - if ( pv1->num_adj_edges >= pv1->max_adj_edges || - pv2->num_adj_edges >= pv2->max_adj_edges || - cur_num_edges >= pBNS->max_edges ) { - return BNS_VERT_EDGE_OVFL; - } - - /* connect edge to the incident vertices and increment the counters of neighbors and edges */ - ret = ConnectTwoVertices( pv1, pv2, edge, pBNS, 0 ); - if ( IS_BNS_ERROR( ret ) ) { - return ret; - } - edge->cap0 = edge->cap; - edge->flow0 = edge->flow; - /* save the edge index */ - type = IS_BNS_VT_C_GR(pv1->type)? pv1->type : - IS_BNS_VT_C_GR(pv2->type)? pv2->type : 0; - if ( type ) { - /* the edge connects to a c-group */ - if ( type & BNS_VERT_TYPE_C_NEGATIVE ) { - pVA[c_point].nCMinusGroupEdge = cur_num_edges+1; - } else { - pVA[c_point].nCPlusGroupEdge = cur_num_edges+1; - } - } - cur_num_edges ++; /* end of new edge creation */ - } - } - } - /*************************************************************/ - /* pass 2: */ - /* adjust bond cap, flow from the final atom st_cap, st_flow */ - /*************************************************************/ - for ( c_point = 0; c_point < num_atoms; c_point ++ ) { - int st_cap, st_cap2, max_edge_flow; - pv1 = pBNS->vert + c_point; /* atom vertex */ - st_cap = pv1->st_edge.cap; - for ( k = 0; k < pv1->num_adj_edges; k ++ ) { - edge = pBNS->edge + pv1->iedge[k]; /* incident edge */ - c_neigh = edge->neighbor12 ^ c_point; /* adjacent vertex */ - pv2 = pBNS->vert + c_neigh; - if ( c_neigh > c_point || !(pv2->type & BNS_VERT_TYPE_ATOM) ) { - continue; - } - /* adjacent vertex is an atom; the edge is a bond; process each bond only once */ - st_cap2 = pv2->st_edge.cap; - /* the edge flow <= min( incident atom st_caps) */ - max_edge_flow = inchi_min( st_cap, st_cap2 ); - /* bond order <= triple bond (flow=2) */ - if ( pSrm->bMetalAddFlower && !pSrm->nMetalMinBondOrder && - (pVA[c_point].cMetal && pVA[c_point].cNumBondsToMetal || - pVA[c_neigh].cMetal && pVA[c_neigh].cNumBondsToMetal) ) { - max_edge_flow = inchi_min( max_edge_flow, MAX_BOND_EDGE_CAP+1 ); - } else { - max_edge_flow = inchi_min( max_edge_flow, MAX_BOND_EDGE_CAP ); - } - if ( at[c_point].bond_type[k] == BOND_TYPE_SINGLE ) { - /* the bond has not been changed due to stereo */ - edge->cap = edge->cap0 = max_edge_flow; - } - } - } - /***********************************************************/ - /************** ************/ - /************** connect M-flower with new edges ************/ - /************** ************/ - /***********************************************************/ - ret = ConnectMetalFlower(&cur_num_vertices, &cur_num_edges, &tot_st_cap, &tot_st_flow, pSrm, pBNS, pTCGroups); - if ( ret < 0 ) { - goto exit_function; - } - /***********************************************************/ - /************** ************/ - /************** add additional vertices & edges ************/ - /************** to connect c-groups ************/ - /************** ************/ - /***********************************************************/ - /* (+) supergroup, Y-connection */ - k = 0; - nAddGroups[k ++] = TCG_Plus0; - nAddGroups[k ++] = TCG_Plus_C0; - nAddGroups[k ++] = TCG_Plus_M0; - ret1 = ConnectSuperCGroup( TCG_Plus, nAddGroups, k, &cur_num_vertices, &cur_num_edges, - &tot_st_cap, &tot_st_flow, pBNS, pTCGroups ); - /* (-) supergroup, Y-connection */ - k = 0; - nAddGroups[k ++] = TCG_Minus0; - nAddGroups[k ++] = TCG_Minus_C0; - nAddGroups[k ++] = TCG_Minus_M0; - ret2 = ConnectSuperCGroup( TCG_Minus, nAddGroups, k, &cur_num_vertices, &cur_num_edges, - &tot_st_cap, &tot_st_flow, pBNS, pTCGroups ); - /******** connect (+) and (-) ***************/ - k = 0; - nAddGroups[k ++] = TCG_Plus; - nAddGroups[k ++] = TCG_Minus; - ret3 = ConnectSuperCGroup( -1, nAddGroups, k, &cur_num_vertices, &cur_num_edges, - &tot_st_cap, &tot_st_flow, pBNS, pTCGroups ); - - /* Take care of the full charge */ - cg_charge = pTCGroups->total_charge - pTCGroups->tgroup_charge - pTCGroups->charge_on_atoms; - ret = 1; - if ( ret3 > 0 ) { - /* (+) and (-) or at least one of them have been connected */ - int nVertPlusMinus = cur_num_vertices - 1; - BNS_VERTEX *pVertPlusMinus = pBNS->vert + nVertPlusMinus; - BNS_VERTEX *pVertPlus = NULL, *pVertMinus = NULL, *pVert=NULL; - BNS_EDGE *pEdgePlus = NULL, *pEdgeMinus = NULL, *pEdge=NULL; - n = pTCGroups->nGroup[TCG_Plus] >= 0; /* (+)-supergroup exists */ - k = pTCGroups->nGroup[TCG_Minus] >= 0; /* (-)-supergroup exists */ - if ( pVertPlusMinus->num_adj_edges == 2 && k+n==2 ) { - pEdgePlus = pBNS->edge + pVertPlusMinus->iedge[0]; /* TCG_Plus was the 1st */ - pEdgeMinus = pBNS->edge + pVertPlusMinus->iedge[1]; /* TCG_Minus was the 2nd */ - } else - if ( pVertPlusMinus->num_adj_edges == 1 && k+n==1 ) { - if ( pTCGroups->nGroup[TCG_Plus] >= 0 ) { - pEdgePlus = pBNS->edge + pVertPlusMinus->iedge[0]; - } else - if ( pTCGroups->nGroup[TCG_Minus] >= 0 ) { - pEdgeMinus = pBNS->edge + pVertPlusMinus->iedge[0]; - } - } else - if ( k+n ) { - /* program error check */ - ret = BNS_BOND_ERR; - goto exit_function; - } - if ( pEdgePlus ) { - pVertPlus = pBNS->vert + (pEdgePlus->neighbor12 ^ nVertPlusMinus); - } - if ( pEdgeMinus ) { - pVertMinus = pBNS->vert + (pEdgeMinus->neighbor12 ^ nVertPlusMinus); - } - pVert = pVertPlus? pVertPlus : pVertMinus? pVertMinus : NULL; - pEdge = pEdgePlus? pEdgePlus : pEdgeMinus? pEdgeMinus : NULL; - if ( pEdgeMinus ) { - pTCGroups->nEdgeMinus = pEdgeMinus - pBNS->edge; - } - if ( pEdgePlus ) { - pTCGroups->nEdgePlus = pEdgePlus - pBNS->edge; - } - if ( pEdge ) { - pTCGroups->nEdge4charge = pEdge - pBNS->edge; - } - /* set total charge */ - if ( pVert && pEdge ) { - /* do not check rescaps for now */ - if ( cg_charge > 0 ) { - pVertPlusMinus->st_edge.cap += cg_charge; - tot_st_cap += cg_charge; - pVertPlusMinus->st_edge.cap0 = pVertPlusMinus->st_edge.cap; - } - if ( cg_charge < 0 ) { - pVert->st_edge.cap -= cg_charge; - tot_st_cap -= cg_charge; - pVert->st_edge.cap0 = pVert->st_edge.cap; - - if ( pEdge->cap - pEdge->flow + cg_charge < 0 ) { - /* 2006-02-06: increase edge capacity to avoid clogging */ - pEdge->cap = pEdge->flow - cg_charge; - } - } - pTCGroups->added_charge = cg_charge; - - } - if ( !cg_charge || (pVert && pEdge) ) { - ret = 2; - } - } - - AddRadicalToMetal( &tot_st_cap, &tot_st_flow, pSrm, pBNS, pTCGroups ); - - - pBNS->num_edges = cur_num_edges; - pBNS->num_vertices = cur_num_vertices; - pBNS->num_c_groups = num_cg; - pBNS->tot_st_cap = tot_st_cap; - pBNS->tot_st_flow = tot_st_flow; - - } -exit_function: - return ret; -} -/********************************************************************************/ -int nNumEdgesToCnVertex( MY_CONST C_NODE *pCN, int len, int v ) -{ - int i, j, n, num_edges, v1 = v+1; - for ( i = 0, num_edges = 0; i < len; i ++ ) { - for ( j = 0; j < MAX_CN_VAL && (n = pCN[i].e[j].neigh); j ++ ) { - num_edges += ( i == v || n == v1 ); - } - } - return num_edges; -} - -/********************************************************************************* -BN_STRUCT* AllocateAndInitTCGBnStruct( StrFromINChI *pStruct, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, - int nMaxAddAtoms, int nMaxAddEdges, - int max_altp, int *pNum_changed_bonds ) -allocate BN_STRUCT that has: - - pBNS->max_vertices = pTCGroups->nVertices + nMaxAddAtoms - pBNS->max_edges = pTCGroups->nEdges + - pBNS->max_vertices * (nMaxAddEdges + NUM_KINDS_OF_GROUPS) - pBNS->max_iedges = 2*pBNS->max_edges + pTCGroups->nAddIedges - - pBNS->len_alt_path = pBNS->max_vertices + iALTP_HDR_LEN + 1 + - max( pBNS->max_vertices/2, 16 ) - pBNS->max_altp = max_altp - -other members: - - pBNS->num_atoms = num_atoms; - pBNS->num_bonds = num_bonds; - pBNS->num_added_atoms = 0; - pBNS->num_t_groups = 0; - pBNS->num_c_groups = 0; - pBNS->nMaxAddAtoms = nMaxAddAtoms; - pBNS->nMaxAddEdges = nMaxAddEdges; - -atom vertices and bond edges: - --- vertex(atom) --- - st_cap = (at[].chem_bonds_valence - at[].valence) + pVA[].cInitFreeValences - st_flow = SUM{bond_orders; ALT_BOND counted as SINGLE} - at[].valence - --- edge(bond) --- - flow = bond_order - 1; for ALT_BOND flow = 0 - cap = min(min(st_cap of neighbors),2); for ALT_BOND cap = 1 - max number of edges per atom = number of bonds + - number of edges to ChargeStruct + - 1 (if atom is a tautomeric endpoint) + - nMaxAddEdges - --- NOTE --- - Here are not included nDelta(dots) from ChargeStruct and flow to ChargeStruct - - *********************************************************************************/ -BN_STRUCT* AllocateAndInitTCGBnStruct( StrFromINChI *pStruct, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, - int nMaxAddAtoms, int nMaxAddEdges, - int max_altp, int *pNum_changed_bonds ) -{ - inp_ATOM *at = pStruct->at; - int num_atoms = pStruct->num_atoms; - ICHICONST SRM *pSrm = pStruct->pSrm; - - BN_STRUCT *pBNS = NULL; - BNS_VERTEX *vert; - BNS_IEDGE *iedge; - - int neigh, num_changed_bonds=0; - U_CHAR bond_type, bond_mark; - int bNeedsFlower1, bNeedsFlower2, min_order; - - int i, j, k, m, n_edges, num_bonds, num_edges; - int f1, f2, c1, c2, edge_cap, edge_flow, st_cap, st_flow, flag_alt_bond; - int tot_st_cap, tot_st_flow; - int max_tg, max_edges, max_vertices, len_alt_path, max_iedges, num_iedges, num_altp; - - /* count vertices */ - max_tg = pTCGroups->num_tgroups; - /* +1 for a super-tautomeric group */ - /* max_vertices = num_atoms + nMaxAddAtoms + max_tg + 1; */ - max_vertices = pTCGroups->nVertices + nMaxAddAtoms; - - /* count edges */ - num_changed_bonds = 0; - num_bonds = pTCGroups->num_bonds; - - /* each atom has enough edges to belong to a tautomeric group + nMaxAddEdges */ - /* number of atoms is large enough to accommodate max. possible number of t-groups + nMaxAddAtoms */ - /* max_altp cannot be larger than BN_MAX_ALTP = 16 */ - num_edges = pTCGroups->nEdges; - /* +max_tg for edges between t-groups and super-tautomeric group */ - max_edges = num_edges + (nMaxAddEdges + NUM_KINDS_OF_GROUPS)*max_vertices; - max_iedges = 2*max_edges + pTCGroups->nAddIedges; - len_alt_path = max_vertices+iALTP_HDR_LEN + 1; /* may overflow if an edge is traversed in 2 directions */ - len_alt_path += inchi_max( max_vertices/2, 16 ); /* to avoid the overflow */ - - if ( !( pBNS = (BN_STRUCT *)inchi_calloc( 1, sizeof(BN_STRUCT)) ) || - !( pBNS->edge = (BNS_EDGE *)inchi_calloc( max_edges, sizeof(BNS_EDGE)) ) || - !( pBNS->vert = (BNS_VERTEX *)inchi_calloc( max_vertices,sizeof(BNS_VERTEX)) ) || - !( pBNS->iedge = (BNS_IEDGE *)inchi_calloc( max_iedges, sizeof(BNS_IEDGE)) ) ) { - return DeAllocateBnStruct( pBNS ); - } - /* alt path init (standard spell) */ - for ( num_altp = 0; num_altp < max_altp && num_altp < BN_MAX_ALTP; num_altp ++ ) { - if ( !( pBNS->altp[num_altp] = (BNS_ALT_PATH*)inchi_calloc( len_alt_path,sizeof(BNS_ALT_PATH))) ) { - return DeAllocateBnStruct( pBNS ); - } - ALTP_ALLOCATED_LEN(pBNS->altp[num_altp]) = len_alt_path; - pBNS->len_alt_path = len_alt_path; /* ??? duplication ??? */ - /* re-init */ - ALTP_DELTA(pBNS->altp[num_altp]) = 0; - ALTP_START_ATOM(pBNS->altp[num_altp]) = NO_VERTEX; - ALTP_END_ATOM(pBNS->altp[num_altp]) = NO_VERTEX; - ALTP_PATH_LEN(pBNS->altp[num_altp]) = 0; - } - pBNS->alt_path = NULL; - pBNS->num_altp = 0; - pBNS->max_altp = num_altp; - - - /* fill vertices (no connectivity) */ - iedge = pBNS->iedge; - num_iedges = 0; - tot_st_cap = tot_st_flow = 0; - for ( i = 0; i < num_atoms; i ++ ) { - /* count edges incident to pBNS->vert[i] */ - k = at[i].valence + (at[i].endpoint != 0) + (nMaxAddEdges /*+ NUM_KINDS_OF_GROUPS*/); - if ( (j = pVA[i].cnListIndex-1) >= 0 ) { - /* add number of neighbors in the ChargeStruct */ - k += nNumEdgesToCnVertex( cnList[j].pCN, cnList[j].len, 0 ); - } - /* set max number of edges for the vertex */ - pBNS->vert[i].max_adj_edges = k; - pBNS->vert[i].iedge = iedge; - iedge += k; - /* add atom vertex cap */ - st_cap = 0; - st_flow = 0; - bNeedsFlower1 = AtomStcapStflow( at, pVA, pSrm, i, &c1, &f1, NULL, NULL ); - /* pVA[i].cNumBondsToMetal = bNeedsFlower1; */ - /* GetAtomStCapFlow( at, pVA, pSrm, i, &c1, &f1 ); */ - st_cap += c1; - st_cap += bNeedsFlower1? 0 : pVA[i].cInitFreeValences; - pBNS->vert[i].st_edge.cap = st_cap; /* the 1st time st_cap is set */ - pBNS->vert[i].st_edge.cap0 = pBNS->vert[i].st_edge.cap; - tot_st_cap += st_cap; - } - num_iedges = iedge - pBNS->iedge; - if ( max_iedges - num_iedges < (nMaxAddEdges + NUM_KINDS_OF_GROUPS)*max_vertices ) { - return DeAllocateBnStruct( pBNS ); - } - - pBNS->num_atoms = num_atoms; /* number of real atoms */ - pBNS->num_added_atoms = 0; - pBNS->num_t_groups = 0; /* number of added t-groups */ - pBNS->num_c_groups = 0; - pBNS->nMaxAddAtoms = nMaxAddAtoms; - pBNS->nMaxAddEdges = nMaxAddEdges; - - pBNS->num_vertices = num_atoms; /* current number of vertices, in general a sum of - pBNS->num_atoms - pBNS->num_t_groups - number of c-groups - number of auxiliary vertices - pBNS->num_added_atoms - */ - pBNS->max_vertices = max_vertices; - - - pBNS->num_bonds = num_bonds; /* number of real edges (bonds) */ - pBNS->max_edges = max_edges; - pBNS->max_iedges = max_iedges; - - - /* - To remove t-groups and added atoms: - - for ( i = 0; i < pBNS->num_atoms; i ++ ) { - for ( j = pBNS->vert[i].num_adj_edges-1; 0 <= j; j -- ) { - k = pBNS->edge[pBNS->vert[i].iedge[j]].neighbor12 ^ i; - if ( pBNS->vert[k].type & BNS_VERT_TYPE_ATOM ) { - pBNS->vert[i].num_adj_edges = j+1; - break; - } - } - } - - pBNS->num_vertices = pBNS->num_atoms; - pBNS->num_edges = pBNS->num_bonds; - pBNS->num_added_atoms = 0; - pBNS->num_t_groups = 0; - pBNS->num_added_edges = 0; - - ALTP_DELTA(pBNS->alt_path) = 0; - ALTP_START_ATOM(pBNS->alt_path) = NO_VERTEX; - ALTP_END_ATOM(pBNS->alt_path) = NO_VERTEX; - ALTP_PATH_LEN(pBNS->alt_path) = 0; - - */ - - - /* add and fill edges and connectivity */ - for ( i = 0, n_edges = 0; i < num_atoms; i ++ ) { - vert = pBNS->vert + i; /* pointer to the ith vertex */ - st_cap = 0; - st_flow = 0; - flag_alt_bond = 0; - for ( j = 0; j < at[i].valence; j ++ ) { - neigh = at[i].neighbor[j]; - /* find this bond at the neighbor */ - for ( k = 0; k < at[neigh].valence; k ++ ) { - if ( at[neigh].neighbor[k] == i ) { - break; - } - } - bond_type = (at[i].bond_type[j] & BOND_TYPE_MASK); - bond_mark = (at[i].bond_type[j] & ~BOND_TYPE_MASK); - if ( bond_type != BOND_SINGLE && bond_type != BOND_DOUBLE && bond_type != BOND_TRIPLE ) { - /* make unknown bonds single */ - bond_type = BOND_SINGLE; - at[i].bond_type[j] = bond_mark | bond_type; - num_changed_bonds ++; - } - if ( neigh > i ) { - /* this is the first time we encounter this bond */ - bNeedsFlower1 = AtomStcapStflow( at, pVA, pSrm, i, &c1, &f1, NULL, NULL ); - /* GetAtomStCapFlow( at, pVA, pSrm, i, &c1, &f1 ); */ - c1 += bNeedsFlower1? 0 : pVA[i].cInitFreeValences; /* elevate cap to the lowest valence in ChargeStruct */ - bNeedsFlower2 = AtomStcapStflow( at, pVA, pSrm, neigh, &c2, &f2, NULL, NULL ); - /* GetAtomStCapFlow( at, pVA, pSrm, neigh, &c2, &f2 ); */ - c2 += bNeedsFlower2? 0 : pVA[neigh].cInitFreeValences; /* elevate cap to the lowest valence in ChargeStruct */ - /* at this point -O would have st_cap=st_flow=0 because the lowest valence=1 for charge=-1 */ - /* however, if -O belongs to a t-group its cap would be 1, flow = 0 */ - /*f1 = MAX_AT_FLOW(at[i]);*/ - /*f2 = MAX_AT_FLOW(at[neigh]);*/ - edge_flow = BondFlowMaxcapMinorder( at, pVA, pSrm, i, j, &edge_cap, &min_order, NULL); - - pBNS->edge[n_edges].neighbor1 = (AT_NUMB)i; - pBNS->edge[n_edges].neighbor12 = (AT_NUMB)(i ^ neigh); - pBNS->edge[n_edges].flow = - pBNS->edge[n_edges].flow0 = edge_flow; - pBNS->edge[n_edges].cap = - pBNS->edge[n_edges].cap0 = edge_cap; - pBNS->edge[n_edges].neigh_ord[0] = j; /* iedge to neigh index at vertex[i], i < neigh */ - pBNS->edge[n_edges].neigh_ord[1] = k; /* iedge to i index at vertex[neigh], i < neigh */ - pBNS->edge[n_edges].pass = 0; - pBNS->edge[n_edges].forbidden = 0; /* may be forbidden if edge_flow = 1: stereogenic fixed double bond */ - if ( bond_type == BOND_TYPE_DOUBLE ) { - /* forbid changing stereogenic double bonds */ - for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[i].sb_parity[m]; m ++ ) { - if ( at[i].sb_ord[m] == j ) { - pBNS->edge[n_edges].forbidden |= BNS_EDGE_FORBIDDEN_MASK; - break; - } - } - } - vert->iedge[j] = pBNS->vert[neigh].iedge[k] = n_edges ++; /* same iedge index as neighbor index in at[] */ - } else { - /* this is the second time we encounter this bond. It was stored at */ - int iedge2 = pBNS->vert[neigh].iedge[k]; - edge_cap = pBNS->edge[iedge2].cap; - edge_flow = pBNS->edge[iedge2].flow; - } - st_flow += edge_flow; - /* - st_cap += edge_cap; - */ - } - vert->num_adj_edges = j; - /* - vert->st_edge.cap = - vert->st_edge.cap0 = st_cap; - */ - vert->st_edge.flow = - vert->st_edge.flow0 = st_flow; - vert->type = BNS_VERT_TYPE_ATOM; - /* - tot_st_cap += vert->st_edge.cap; - */ - tot_st_flow += vert->st_edge.flow; - } - *pNum_changed_bonds = num_changed_bonds/2; - - pBNS->num_edges = n_edges; /* number of edges */ - pBNS->num_iedges = num_iedges; - pBNS->num_added_edges = 0; - - pBNS->tot_st_cap = tot_st_cap; - pBNS->tot_st_flow = tot_st_flow; - -/* exit_function: */ - - return pBNS; -} -/******************************************************************************************************/ -void IncrZeroBondsAndClearEndpts(inp_ATOM *at, int num_at, int iComponent ) -{ - int i, j; - for ( i = 0; i < num_at; i ++ ) { - at[i].endpoint = 0; - at[i].component = iComponent; - for ( j = 0; j < at[i].valence; j ++ ) { - if ( !at[i].bond_type[j] ) { - at[i].bond_type[j] = BOND_TYPE_SINGLE; - at[i].chem_bonds_valence += BOND_TYPE_SINGLE; - } - } - } -} -void IncrZeroBonds(inp_ATOM *at, int num_at, int iComponent ) -{ - int i, j; - for ( i = 0; i < num_at; i ++ ) { - at[i].component = iComponent; - for ( j = 0; j < at[i].valence; j ++ ) { - if ( !at[i].bond_type[j] ) { - at[i].bond_type[j] = BOND_TYPE_SINGLE; - at[i].chem_bonds_valence += BOND_TYPE_SINGLE; - } - } - } -} -void ClearEndpts(inp_ATOM *at, int num_at ) -{ - int i; - for ( i = 0; i < num_at; i ++ ) { - at[i].endpoint = 0; - } -} - - -/******************************************************************************************************/ -#define ANY_VERT_TYPE(X) (((X) & (BNS_VERT_TYPE_ATOM | BNS_VERT_TYPE_TGROUP | BNS_VERT_TYPE_C_GROUP)) && \ - !((X) & (BNS_VERT_TYPE_SUPER_TGROUP))) -#define GRP_VERT_TYPE(X) (((X) & (BNS_VERT_TYPE_TGROUP | BNS_VERT_TYPE_C_GROUP)) && \ - !((X) & (BNS_VERT_TYPE_SUPER_TGROUP))) -typedef struct tagVertexFlow { - int type; - Vertex v; - EdgeIndex e_In; - EdgeIndex e_Out; - EdgeFlow delta_In; - EdgeFlow delta_Out; - Vertex bUsed; /* indicates the charge edge belongs to already processed atom */ -} VF; -#define NUM_VF 3 -#define VF_USED_IN 1 -#define VF_USED_OUT 2 -#define VF_USED_ALL (VF_USED_IN | VF_USED_OUT) - -int GetDeltaChargeFromVF( BN_STRUCT *pBNS, VAL_AT *pVA, VF *vf ); -/******************************************************************************************************/ -int GetDeltaChargeFromVF( BN_STRUCT *pBNS, VAL_AT *pVA, VF *vf ) -{ - int i, v = NO_VERTEX; - int ieIn1 = (!(vf->bUsed & VF_USED_IN) && vf->e_In >= 0 && vf->delta_In )? vf->e_In+1 : NO_VERTEX; - int ieOut1 = (!(vf->bUsed & VF_USED_OUT) && vf->e_Out >= 0 && vf->delta_Out)? vf->e_Out+1 : NO_VERTEX; - int nInitCharge, nPlusFlow, nMinusFlow, nDeltaCharge, nNumDeltaCharge, eCPlus, eCMinus; - - if ( !(vf->type & BNS_VERT_TYPE_C_GROUP) || - (vf->type & BNS_VERT_TYPE_SUPER_TGROUP) || - (ieIn1 == NO_VERTEX && ieOut1 == NO_VERTEX ) ) { - return 0; - } - if ( vf->type & BNS_VERT_TYPE_C_NEGATIVE ) { - /* negative charge edge */ - for ( i = 0; i < pBNS->num_atoms; i ++ ) { - if ( pVA[i].nCMinusGroupEdge == ieIn1 || pVA[i].nCMinusGroupEdge == ieOut1 ) { - v = i; - break; - } - } - } else { - /* positive charge edge */ - for ( i = 0; i < pBNS->num_atoms; i ++ ) { - if ( pVA[i].nCPlusGroupEdge == ieIn1 || pVA[i].nCPlusGroupEdge == ieOut1 ) { - v = i; - break; - } - } - } - if ( v == NO_VERTEX ) - return 0; - - nInitCharge = pVA[v].cInitCharge; - nPlusFlow = nMinusFlow = 0; - nNumDeltaCharge = 0; - - if ( (eCPlus = pVA[v].nCPlusGroupEdge-1) >= 0 ) { - nPlusFlow = pBNS->edge[eCPlus].cap - - pBNS->edge[eCPlus].flow; - } - if ( (eCMinus = pVA[v].nCMinusGroupEdge-1) >= 0 ) { - nMinusFlow = -pBNS->edge[eCMinus].flow; - } - nInitCharge += nPlusFlow + nMinusFlow; - - nDeltaCharge = 0; - - if ( !(vf[0].bUsed & VF_USED_OUT) ) { - if ( vf[0].e_Out==eCPlus || vf[0].e_Out==eCMinus ) { - nDeltaCharge -= vf[0].delta_Out; - vf[0].bUsed |= VF_USED_OUT; - } - } - - if ( !(vf[0].bUsed & VF_USED_IN) ) { - if ( vf[0].e_In==eCPlus || vf[0].e_In==eCMinus ) { - nDeltaCharge -= vf[0].delta_In; - vf[0].bUsed |= VF_USED_IN; - } - } - if ( !nInitCharge && nDeltaCharge ) { - nNumDeltaCharge ++; - } else - if ( nInitCharge && 0 == nInitCharge + nDeltaCharge ) { - nNumDeltaCharge --; - } - return nNumDeltaCharge; -} -/******************************************************************************************************/ -int EvaluateChargeChanges( BN_STRUCT *pBNS, VAL_AT *pVA, int *pnDeltaH, int *pnDeltaCharge, int *pnNumVisitedAtoms ) -{ - int pass, i, j, v0, v1, v2, v, ineigh1, /*ineigh2,*/ vLast, n, delta, ret, ie, err = 0; - BNS_EDGE *edge; - int nDeltaH, nDeltaCharge, iPrev, nInitCharge, nPlusFlow, nMinusFlow; - int nNumDeltaH = 0; - int nNumDeltaCharge = 0; - int nNumVisitedAtoms = 0; - VF vf[NUM_VF+1]; - - *pnDeltaH = 0; - *pnDeltaCharge = 0; - *pnNumVisitedAtoms = 0; - - for ( pass = pBNS->num_altp-1, ret = 0; 0 <= pass; pass -- ) { - - pBNS->alt_path = pBNS->altp[pass]; - v1 = ALTP_START_ATOM(pBNS->alt_path); - n = ALTP_PATH_LEN(pBNS->alt_path); - delta = ALTP_DELTA(pBNS->alt_path); - vLast = ALTP_END_ATOM(pBNS->alt_path); - v0 = v2 = NO_VERTEX; - - memset( vf, 0, sizeof(vf) ); - for ( i = 0; i < (int)(sizeof(vf)/sizeof(vf[0])); i ++ ) { - vf[i].v = NO_VERTEX; /* = -2 */ - vf[i].e_In = NO_VERTEX; - vf[i].e_Out = NO_VERTEX; - } - iPrev = 0; - /* add to the queue */ - if ( ANY_VERT_TYPE(pBNS->vert[v1].type) ) { - if (pBNS->vert[v1].type & BNS_VERT_TYPE_ATOM) { - nNumVisitedAtoms ++; - } - vf[2].type = pBNS->vert[v1].type; - vf[2].v = v1; - iPrev = 2; - } - - nNumDeltaH = 0; - nNumDeltaCharge = 0; - nNumVisitedAtoms = 0; - - for ( i = 0; i < n; i ++, delta = -delta, v0 = v1, v1 = v2 ) { - ineigh1 = ALTP_THIS_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v1->v2 neighbor */ - /*ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i);*/ /* v2->v1 neighbor */ - edge = pBNS->edge + (ie=pBNS->vert[v1].iedge[ineigh1]); - /* follow the BN Structure, not the inp_ATOM, to take care of swithching to - t-groups, c-groups or other fictitious edges/vertices - */ - - if ( iPrev ) { - /* add exit delta and edge */ - vf[2].e_Out = ie; - vf[2].delta_Out = delta; - } - - v2 = edge->neighbor12 ^ v1; /* next vertex */ - if (pBNS->vert[v2].type & BNS_VERT_TYPE_ATOM) { - nNumVisitedAtoms ++; - } - - if ( (ANY_VERT_TYPE(pBNS->vert[v2].type) || i == n-1) && - (vf[0].type & BNS_VERT_TYPE_C_GROUP) && vf[0].bUsed != VF_USED_ALL ) { - /* unused vertex is about to be discarded */ - nNumDeltaCharge += GetDeltaChargeFromVF( pBNS, pVA, &vf[0] ); - } - - if ( ANY_VERT_TYPE(pBNS->vert[v2].type) ) { - /* shift the queue */ - vf[0] = vf[1]; - vf[1] = vf[2]; - vf[2] = vf[3]; /* make vf[2] empty */ - /* add next vertex */ - vf[2].v = v2; - vf[2].type = pBNS->vert[v2].type; - vf[2].e_In = ie; - vf[2].delta_In = delta; - iPrev = 2; /* indicates a newly added vertex */ - } else - if ( i == n-1 ) { - /* shift the queue */ - vf[0] = vf[1]; - vf[1] = vf[2]; - vf[2] = vf[3]; /* make vf[2] empty */ - iPrev = 1; /* indicates the last vertex */ - } else { - iPrev = 0; /* no new vertex has been added */ - } - - if ( iPrev && (vf[1].type & BNS_VERT_TYPE_ATOM)) { - /* a new vertex has just been added and */ - /* an atom is in the middle of the queue */ - EdgeIndex eCPlus, eCMinus; - v = vf[1].v; - nInitCharge = pVA[v].cInitCharge; - nPlusFlow = nMinusFlow = 0; - if ( (eCPlus = pVA[v].nCPlusGroupEdge-1) >= 0 ) { - nPlusFlow = pBNS->edge[eCPlus].cap - - pBNS->edge[eCPlus].flow; - } - if ( (eCMinus = pVA[v].nCMinusGroupEdge-1) >= 0 ) { - nMinusFlow = -pBNS->edge[eCMinus].flow; - } - nInitCharge += nPlusFlow + nMinusFlow; - - nDeltaH = nDeltaCharge = 0; - - if ( vf[0].type & BNS_VERT_TYPE_TGROUP ) { - nDeltaH -= delta; - } else - if ( (vf[0].type & BNS_VERT_TYPE_C_GROUP) && !(vf[0].bUsed & VF_USED_OUT) ) { - if ( vf[0].e_Out==eCPlus || vf[0].e_Out==eCMinus ) { - nDeltaCharge -= vf[0].delta_Out; - vf[0].bUsed |= VF_USED_OUT; - } - } - - if ( vf[2].type & BNS_VERT_TYPE_TGROUP ) { - nDeltaH += delta; - } else - if ( (vf[2].type & BNS_VERT_TYPE_C_GROUP) && !(vf[2].bUsed & VF_USED_IN) ) { - if ( vf[2].e_In==eCPlus || vf[2].e_In==eCMinus ) { - nDeltaCharge -= vf[2].delta_In; - vf[2].bUsed |= VF_USED_IN; - } - } - if ( !nInitCharge && nDeltaCharge ) { - nNumDeltaCharge ++; - } else - if ( nInitCharge && 0 == nInitCharge + nDeltaCharge ) { - nNumDeltaCharge --; - } - - nNumDeltaH += abs(nDeltaH); - /* nNumDeltaCharge += abs(nDeltaCharge); */ - vf[1].bUsed = VF_USED_ALL; - } - } - for ( j = 0; j < 3; j ++ ) { - nNumDeltaCharge += GetDeltaChargeFromVF( pBNS, pVA, &vf[j] ); - } - - *pnDeltaH += nNumDeltaH; - *pnDeltaCharge += nNumDeltaCharge; - *pnNumVisitedAtoms += nNumVisitedAtoms; - - - if ( v2 != vLast ) { - err = BNS_PROGRAM_ERR; - } - } - return err? err : ret; -} - -/******************************************************************************************************/ -int RunBnsTestOnce( BN_STRUCT *pBNS, BN_DATA *pBD, VAL_AT *pVA, Vertex *pvFirst, Vertex *pvLast, - int *pPathLen, int *pnDeltaH, int *pnDeltaCharge, int *pnNumVisitedAtoms ) -{ - int bChangeFlow = 0; /* do not change flow */ - int delta, ret, ret2, pass; - - ReInitBnStructAltPaths( pBNS ); - pass = 0; - pBNS->alt_path = pBNS->altp[pass]; - pBNS->num_altp = 0; - pBNS->bChangeFlow = 0; - delta=BalancedNetworkSearch ( pBNS, pBD, bChangeFlow ); - if ( delta > 0 ) { - pBNS->alt_path = pBNS->altp[pass]; - *pvFirst = ALTP_START_ATOM(pBNS->alt_path); - *pPathLen = ALTP_PATH_LEN(pBNS->alt_path); - *pvLast = ALTP_END_ATOM(pBNS->alt_path); - pBNS->num_altp ++; - ret2 = EvaluateChargeChanges( pBNS, pVA, pnDeltaH, pnDeltaCharge, pnNumVisitedAtoms ); - } else { - *pvFirst = NO_VERTEX; - *pPathLen = 0; - *pvLast = NO_VERTEX; - ret2 = 0; - } - ReInitBnStructAltPaths( pBNS ); - ret = ReInitBnData( pBD ); - return (delta >= 0 && ret > 0 )? -ret : delta; - -} -/******************************************************************************************************/ -int RunBnsRestoreOnce( BN_STRUCT *pBNS, BN_DATA *pBD, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups ) -{ - /* run BNS for the first time */ - int nTotalDelta = 0, ret = 0; - int nDelta; - ReInitBnStructAltPaths( pBNS ); - do { - nDelta = RunBalancedNetworkSearch( pBNS, pBD, BNS_EF_CHNG_FLOW ); - if ( IS_BNS_ERROR(nDelta) ) { - ret = nDelta; - goto exit_function; - } - nTotalDelta += nDelta; - ReInitBnStructAltPaths( pBNS ); - ret = ReInitBnData( pBD ); - if ( ret > 0 ) { - ret = -ret; - goto exit_function; - } - } while( nDelta > 0 && ret == 0 ); - pBNS->tot_st_flow += 2*nTotalDelta; - ret = nTotalDelta; -exit_function: - return ret; -} -/******************************************************************************************************/ -int comp_cc_cand( const void *a1, const void *a2 ) -{ - const CC_CAND *p1 = (const CC_CAND *) a1; - const CC_CAND *p2 = (const CC_CAND *) a2; - int ret; - if ( ret = (int)p2->cMetal - (int)p1->cMetal ) - return ret; /* metal first */ - if ( ret = (int)p2->cNumBondsToMetal - (int)p1->cNumBondsToMetal ) - return ret; /* connected to metal first */ - if ( ret = (int)p2->cPeriodicRowNumber - (int)p1->cPeriodicRowNumber ) - return ret; /* heaviest first */ - if ( ret = (int)p2->num_bonds - (int)p1->num_bonds ) - return ret; /* more bonds first */ - if ( ret = (int)p1->chem_valence - (int)p2->chem_valence ) - return ret; /* less bond order first */ - if ( !p1->cNumValenceElectrons && p2->cNumValenceElectrons ) - return -1; /* no valence electrons first */ - if ( !p2->cNumValenceElectrons && p1->cNumValenceElectrons ) - return -1; /* no valence electrons first */ - if ( (int)p2->cNumValenceElectrons - (int)p1->cNumValenceElectrons ) - return ret; /* more valence electrons first */ - ret = (int)p2->iat - (int)p1->iat; /* greater canon number first */ - return ret; -} -/***************************************************************************************************** -Locate E1=C-E2 where - e ev are the edges - - E1 and E2 are atoms that belong to the same t-group - C is an atom that does not belong to any t-group - e is a forbidden edge - ev is not a forbidden edge - - Make changes so that: - E1(d)-C(d)-E2 - - where (d) means doublet radical - -*/ -/**************************************************************************************************/ -int get_pVA_atom_type( VAL_AT *pVA, inp_ATOM *at, int iat, int bond_type ) -{ - int type = 0, val; - if ( pVA[iat].cNumValenceElectrons == 4 ) { - if ( pVA[iat].cPeriodicRowNumber == 1 ) { - type |= EL_TYPE_C; - } - } else - if ( pVA[iat].cNumValenceElectrons == 6 ) { - if ( pVA[iat].cPeriodicRowNumber == 1 ) { - type |= EL_TYPE_O; - } else - if ( pVA[iat].cPeriodicRowNumber < 5 ) { - type |= EL_TYPE_S; - } - if ( bond_type == BOND_TYPE_SINGLE && - (type & (EL_TYPE_O | EL_TYPE_S)) && - 1 == nNoMetalBondsValence(at, iat ) && - 1 == nNoMetalNumBonds(at, iat) ) { - type |= EL_TYPE_OSt; - } - } else - if ( pVA[iat].cNumValenceElectrons == 5 ) { - if ( pVA[iat].cPeriodicRowNumber == 1 ) { - type |= EL_TYPE_N; - } else { - type |= EL_TYPE_P; - } - } else - if ( !is_el_a_metal(pVA[iat].cPeriodicNumber) ) { - type |= EL_TYPE_X; - } - /* check for possibility to be a tautomeric endpoint (that is, be a Mobile H site) */ - val = get_endpoint_valence( at[iat].el_number ); - if ( val && val > at[iat].valence && !at[iat].radical && - -1 <= at[iat].charge && at[iat].charge <= 0 && - val == at[iat].chem_bonds_valence - at[iat].charge + at[iat].num_H ) { - type |= EL_TYPE_PT; - } - return type; -} - -/*************************************************************************************/ -int AllocEdgeList( EDGE_LIST *pEdges, int nLen ) -{ - switch( nLen ) { - case EDGE_LIST_FREE: - if ( NULL != pEdges->pnEdges ) { - inchi_free( pEdges->pnEdges ); - } - /* fall through */ - case EDGE_LIST_CLEAR: - memset( pEdges, 0, sizeof(*pEdges) ); - break; - default: - if ( nLen > 0 && nLen != pEdges->num_alloc ) { - EdgeIndex *tmp_edges = pEdges->pnEdges; - int tmp_num = pEdges->num_edges; - pEdges->pnEdges = (EdgeIndex *)inchi_calloc( nLen, sizeof(pEdges->pnEdges[0])); - if ( !pEdges->pnEdges ) { - return RI_ERR_ALLOC; - } - tmp_num = inchi_min( tmp_num, nLen ); - if ( tmp_edges && tmp_num > 0 ) { - memcpy( pEdges->pnEdges, tmp_edges, tmp_num * sizeof(pEdges->pnEdges[0]) ); - pEdges->num_edges = tmp_num; - } else { - pEdges->num_edges = 0; - } - if ( tmp_edges ) { - inchi_free( tmp_edges ); - } - pEdges->num_alloc = nLen; - return 0; - } - break; - } - return 0; -} -/********************************************************************/ -int AddToEdgeList( EDGE_LIST *pEdges, int iedge, int nAddLen ) -{ - if ( pEdges->num_alloc == pEdges->num_edges ) { - int ret; - if ( nAddLen <= 0 ) { - return RI_ERR_PROGR; - } - if ( ret = AllocEdgeList( pEdges, pEdges->num_alloc + nAddLen ) ) { - return ret; - } - } - pEdges->pnEdges[pEdges->num_edges ++] = (EdgeIndex)iedge; - return 0; -} -/********************************************************************/ -int RemoveFromEdgeListByIndex( EDGE_LIST *pEdges, int index ) -{ - int len; - if ( 0 <= (len = pEdges->num_edges - index - 1) ) { - if ( len ) { - memmove( pEdges->pnEdges+index, pEdges->pnEdges+index+1, len*sizeof(pEdges->pnEdges[0])); - } - pEdges->num_edges --; - pEdges->pnEdges[pEdges->num_edges] = 0; - return 0; - } - return -1; -} -/********************************************************************/ -int FindInEdgeList( EDGE_LIST *pEdges, int iedge ) -{ - int i; - EdgeIndex ie = iedge; - for ( i = pEdges->num_edges-1; 0 <= i; i -- ) { - if ( ie == pEdges->pnEdges[i] ) { - return i; - } - } - return -1; -} -/********************************************************************/ -int RemoveFromEdgeListByValue( EDGE_LIST *pEdges, int iedge ) -{ - int i, ret, n = 0; - EdgeIndex ie = iedge; - for ( i = pEdges->num_edges-1; 0 <= i; i -- ) { - if ( ie == pEdges->pnEdges[i] ) { - if ( ret = RemoveFromEdgeListByIndex( pEdges, i ) ) { - return ret; - } - n ++; - } - } - return n; -} -/********************************************************************/ -int AllocBfsQueue( BFS_Q *pQ, int num_at, int min_ring_size ) -{ - int ret = 0; - switch( num_at ) { - case BFS_Q_FREE: - if ( pQ->q ) { - pQ->q = QueueDelete( pQ->q ); - } - if ( pQ->nAtomLevel ) { - inchi_free( pQ->nAtomLevel ); - } - if ( pQ->cSource ) { - inchi_free( pQ->cSource ); - } - /* fall through */ - case BFS_Q_CLEAR: - memset( pQ, 0, sizeof( *pQ ) ); - return 0; - default: - if ( num_at <= 0 ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - if ( num_at > pQ->num_at ) { - if ( pQ->num_at ) { - AllocBfsQueue( pQ, BFS_Q_FREE, 0 ); - } - pQ->q = QueueCreate( num_at+1, sizeof(qInt) ); - pQ->nAtomLevel = (AT_RANK*)inchi_calloc( sizeof(pQ->nAtomLevel[0]), num_at ); - pQ->cSource = (S_CHAR *)inchi_calloc( sizeof(pQ->cSource[0]), num_at ); - if ( !pQ->q || !pQ->cSource || !pQ->nAtomLevel ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - pQ->num_at = num_at; - } - pQ->min_ring_size = min_ring_size; - } -exit_function: - return ret; -} -/*************************************************************************************/ -void RemoveForbiddenEdgeMask( BN_STRUCT *pBNS, EDGE_LIST *pEdges, int forbidden_edge_mask ) -{ - int i, mask = ~forbidden_edge_mask; - for ( i = 0; i < pEdges->num_edges; i ++ ) { - pBNS->edge[pEdges->pnEdges[i]].forbidden &= mask; - } -} -/*************************************************************************************/ -void SetForbiddenEdgeMask( BN_STRUCT *pBNS, EDGE_LIST *pEdges, int forbidden_edge_mask ) -{ - int i; - for ( i = 0; i < pEdges->num_edges; i ++ ) { - pBNS->edge[pEdges->pnEdges[i]].forbidden |= forbidden_edge_mask; - } -} -/******************************************************************************************************/ -void RemoveForbiddenBondFlowBits( BN_STRUCT *pBNS, int forbidden_edge_mask_int ) -{ - BNS_EDGE *e; - int i; - int inv_forbidden_edge_mask = ~forbidden_edge_mask_int; - for ( i = 0, e = pBNS->edge; i < pBNS->num_bonds; i ++, e ++ ) { - e->forbidden &= inv_forbidden_edge_mask; - } -} -/****************************************************************************************************** - upper vc - edge / - v1[i0]---v0 - \ / - \ / - \ / - v1[i1] - | - | - atom -*/ -int GetChargeFlowerUpperEdge( BN_STRUCT *pBNS, VAL_AT *pVA, int nChargeEdge ) -{ - int ret = NO_VERTEX, i, j, k, i0, i1; - Vertex v0, v1[3], vc, v_t, v; - BNS_EDGE *pe, *pe1[3], *pe_t; - BNS_VERTEX *pv0, *pv1[3], *pv_t; - - if ( nChargeEdge < 0 ) { - goto exit_function; - } - pe = pBNS->edge + nChargeEdge; - vc = pe->neighbor1; /* charge vertex */ - if ( !IS_BNS_VT_C_GR(pBNS->vert[vc].type) ) { - vc = vc ^ pe->neighbor12; - } - v0 = vc ^ pe->neighbor12; /* ChargeStruct vertex ? */ - pv0 = pBNS->vert + v0; - if ( IS_BNS_VT_ATOM(pv0->type) ) { - goto exit_function; /* no charge flower exists */ - } - /* 2 edges from v0 */ - for ( i = j = 0; i < pv0->num_adj_edges && j < 3; i ++ ) { - pe1[j] = pBNS->edge + pv0->iedge[i]; - if ( vc != ( v1[j] = pe1[j]->neighbor12 ^ v0 ) && - (pv1[j] = pBNS->vert + v1[j], - !IS_BNS_VT_ATOM(pv1[j]->type) && !IS_BNS_VT_C_GR(pv1[j]->type)) ) { - j ++; - } - } - if ( j != 2 || i != pv0->num_adj_edges ) { - goto exit_function; - } - - if ( pv1[1]->num_adj_edges == 2 && - pv1[0]->num_adj_edges == 3 ) { - i0 = 1; - i1 = 0; - } else - if ( pv1[0]->num_adj_edges == 2 && - pv1[1]->num_adj_edges == 3 ) { - i0 = 0; - i1 = 1; - } else { - goto exit_function; - } - /* additional check: traverse edges around v1[i1] */ - pv_t = pv1[i1]; - v_t = v1[i1]; - for ( i = k = 0; i < pv_t->num_adj_edges; i ++ ) { - pe_t = pBNS->edge + pv_t->iedge[i]; - v = pe_t->neighbor12 ^ v_t; /* v1[i1] neighbor */ - if ( v == v0 ) { - k += 1; - } - if ( v == v1[i0] ) { - k += 2; - } - if ( IS_BNS_VT_ATOM(pBNS->vert[v].type) ) { - k += 4; - } - } - if ( k != 7 ) { - goto exit_function; - } - ret = pe1[i0] - pBNS->edge; - -exit_function: - return ret; - -} -#if (INCLUDE_NORMALIZATION_ENTRY_POINT == 1 ) -/******************************************************************************************** -input: allocate (num_at+num_deleted_H) atoms in inp_ATOM *at_norm, *at_fixed_bonds_out - allocate t_group_info -*********************************************************************************************/ -int NormalizeStructure( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, - StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, - VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - inp_ATOM *at_norm, inp_ATOM *at_fixed_bonds_out, T_GROUP_INFO *t_group_info ) -{ - int i, ret, num_endpoints, nLenTaut; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - /* - T_GROUP_INFO tgi; - T_GROUP_INFO *t_group_info = &tgi; - inp_ATOM *at_fixed_bonds_out = NULL; - inp_ATOM *at_norm = NULL; - - at_norm = (inp_ATOM *)inchi_calloc( len_at, sizeof(at_norm[0]) ); - at_fixed_bonds_out = (inp_ATOM *)inchi_calloc( len_at, sizeof(at_fixed_bonds_out[0]) ); - if ( !at_norm || !at_fixed_bonds_out ) { - if ( at_norm ) inchi_free( at_norm ); - if ( at_fixed_bonds_out ) inchi_free( at_fixed_bonds_out ); - ret = RI_ERR_ALLOC; - goto exit_function; - } - */ -/* call normalization only */ - memset( t_group_info, 0, sizeof(t_group_info[0]) ); - t_group_info->tni.nNumRemovedExplicitH = pStruct->num_deleted_H; - t_group_info->bTautFlags = ip->bTautFlags; - t_group_info->bTautFlagsDone = 0; /* (ip->bTautFlagsDone | sd->bTautFlagsDone[INCHI_BAS]);*/ - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret < 0 ) { - goto exit_function; - } -#if ( FIND_RING_SYSTEMS == 1 ) - ret = MarkRingSystemsInp( at2, num_at, 0 ); - if ( ret < 0 ) { - goto exit_function; - } -#endif - memcpy( at_norm, at2, len_at * sizeof(at_norm[0]) ); - for ( i = 0, num_endpoints = 0; i < num_at; i ++ ) { - num_endpoints += (0 != at_norm[i].endpoint); - at_norm[i].endpoint = 0; - } - - ret = mark_alt_bonds_and_taut_groups ( at_norm, at_fixed_bonds_out, num_at, t_group_info, - NULL /* &inpbTautFlags*/, NULL /*inpbTautFlagsDone*/ ); - if ( ret < 0 ) { - goto exit_function;/* out of RAM or other normalization problem */ - } - /* after normalization, t_group_info->t_group[i].num[0] = number of H + number of (-) */ - /* t_group_info->t_group[i].num[1] = number of (-) */ - - /* --- count t-groups, remove (-)-only t-groups, replace -------------------------------*/ - /* t_group_info->t_group[i].num[0] with */ - /* t_group_info->t_group[i].num[0]-t_group_info->t_group[i].num[1] */ - nLenTaut = CountTautomerGroupsInpAt( at_norm, num_at, t_group_info ); - ret = nLenTaut; -exit_function: - return ret; -} -#endif -/******************************************************************************************************/ -int MakeOneInChIOutOfStrFromINChI2(ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd_inp, - BN_STRUCT *pBNS, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, - VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - T_GROUP_INFO **t_group_info, - inp_ATOM **at_norm, inp_ATOM **at_prep ) -{ - int ret; - INPUT_PARMS ip_loc, *ip; - STRUCT_DATA sd_loc, *sd; - - ip_loc = *ip_inp; - sd_loc = *sd_inp; - ip = &ip_loc; - sd = &sd_loc; - memset( sd, 0, sizeof(*sd) ); - /* create structure out of BNS */ - memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); - pStruct->at = at2; - ret = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret < 0 ) { - goto exit_function;/* out of RAM or other normalization problem */ - } - pStruct->at = at; - ret = MakeOneInChIOutOfStrFromINChI( ip, sd, pStruct, at2, at3, pTCGroups ); - if ( ret < 0 ) { - goto exit_function;/* out of RAM or other normalization problem */ - } - if ( at_norm ) { - *at_norm = pStruct->pOne_norm_data[0]->at; - } - if ( at_prep ) { - if ( pStruct->pOne_norm_data[0]->bTautPreprocessed && pStruct->pOne_norm_data[0]->at_fixed_bonds ) { - *at_prep = pStruct->pOne_norm_data[0]->at_fixed_bonds; - } else - /* get preprocessed structure in case of Fixed-H */ - if ( pStruct->iMobileH == TAUT_NON && pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->bTautPreprocessed ) { - *at_prep = pStruct->pOne_norm_data[1]->at_fixed_bonds; - } else { - *at_prep = NULL; - } - } - if ( t_group_info ) { - if ( pStruct->iMobileH == TAUT_YES && - pStruct->One_ti.num_t_groups && - pStruct->One_ti.t_group && pStruct->One_ti.nEndpointAtomNumber ) { - *t_group_info = &pStruct->One_ti; - } else { - *t_group_info = NULL; - } - } -exit_function: - return ret; -} - -/******************************************************************************************************/ -int MakeOneInChIOutOfStrFromINChI( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, StrFromINChI *pStruct, - inp_ATOM *at2, inp_ATOM *at3, ALL_TC_GROUPS *pTCGroups ) -{ - - INCHI_MODE bTautFlags = ip->bTautFlags | TG_FLAG_H_ALREADY_REMOVED; - INCHI_MODE bTautFlagsDone = 0; /*(ip->bTautFlagsDone | sd->bTautFlagsDone[INCHI_BAS]);*/ - INChI *cur_INChI[TAUT_NUM]; - INChI_Aux *cur_INChI_Aux[TAUT_NUM]; - int i, j, k; - int iComponent = pTCGroups->iComponent; - int len_at = pStruct->num_atoms + pStruct->num_deleted_H; - int num_atoms = pStruct->num_atoms; - long ulStructTime; - - INP_ATOM_DATA InpCurAtData; - INP_ATOM_DATA *inp_cur_data; - - INP_ATOM_DATA InpNormAtData, InpNormTautData; - INP_ATOM_DATA *inp_norm_data[TAUT_NUM]; /* = { &InpNormAtData, &InpNormTautData }; */ - - int bOrigCoord = 0; - int num_at, ret = RI_ERR_PROGR; - struct tagInchiTime ulMaxTime; - - T_GROUP_INFO *t_group_info = NULL; - /* initialization */ - inp_cur_data = &InpCurAtData; - inp_norm_data[TAUT_NON] = &InpNormAtData; - inp_norm_data[TAUT_YES] = &InpNormTautData; - - memset( inp_cur_data , 0, sizeof( *inp_cur_data ) ); - memset( inp_norm_data[TAUT_NON], 0, sizeof( *inp_norm_data[0] ) ); - memset( inp_norm_data[TAUT_YES], 0, sizeof( *inp_norm_data[0] ) ); - ulStructTime = sd->ulStructTime; - memset( sd, 0, sizeof(*sd) ); - - /* deallocate old results */ - free_t_group_info( &pStruct->One_ti ); - for ( k = 0; k < TAUT_NUM; k ++ ) { - Free_INChI( &pStruct->pOneINChI[k] ); - Free_INChI_Aux( &pStruct->pOneINChI_Aux[k] ); - if ( pStruct->pOne_norm_data[k] ) { - FreeInpAtomData( pStruct->pOne_norm_data[k] ); - inchi_free( pStruct->pOne_norm_data[k] ); - pStruct->pOne_norm_data[k] = NULL; - } - cur_INChI[k] = NULL; - cur_INChI_Aux[k] = NULL; - } - memcpy( at3, at2, sizeof(at3[0])*len_at ); - /* prepare the structure */ - IncrZeroBondsAndClearEndpts(at3, num_atoms, iComponent+1); - CopySt2At( at3, pStruct->st, pStruct->num_atoms ); - FixUnkn0DStereoBonds( at3, pStruct->num_atoms); - ret = ReconcileAllCmlBondParities( at3, pStruct->num_atoms, 0 ); - if ( ret < 0 ) { - goto exit_function; - } - if ( 0 < fix_odd_things( num_atoms, at3, 1, ip->bFixNonUniformDraw ) ) - { - if ( sd->nErrorType < _IS_WARNING ) - { - sd->nErrorType = _IS_WARNING; - } - sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ODD_THINGS_DONE; - } - /* allocate and set parameters */ - inp_cur_data->at = at3; - inp_cur_data->num_at = num_atoms; - inp_cur_data->num_removed_H = pStruct->num_deleted_H; - - bTautFlagsDone &= ~(TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE); - - if ( i = bNumHeterAtomHasIsotopicH( at3, num_atoms ) ) { - if ( i & 1 ) { - bTautFlagsDone |= TG_FLAG_FOUND_ISOTOPIC_H_DONE; - } - if ( i & 2 ) { - bTautFlagsDone |= TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE; - } - } - - memset( &ulMaxTime, 0, sizeof(ulMaxTime)); - - /* allocate memory for non-tautimeric (k=0) and tautomeric (k=1) results */ - for ( k = 0; k < TAUT_NUM; k ++ ) { - - if ( !pStruct->bMobileH || k == pStruct->bMobileH ) { - /* pStruct->bMobileH=0: k = 0, 1 => allow allocation of both Fixed-H and Mobile-H InChI - pStruct->bMobileH=1: k = 1 only => allow allocation of only Mobile-H InChI */ - int nAllocMode = (k==TAUT_YES? REQ_MODE_TAUT:0) | - (bTautFlagsDone & ( TG_FLAG_FOUND_ISOTOPIC_H_DONE | - TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE ))? - (ip->nMode & REQ_MODE_ISO):0; - - if ( k==TAUT_NON && (ip->nMode & REQ_MODE_BASIC ) || - k==TAUT_YES && (ip->nMode & REQ_MODE_TAUT ) ) { - /* alloc INChI and INChI_Aux only if ip->nMode allows this */ - cur_INChI[k] = Alloc_INChI( inp_cur_data->at, inp_cur_data->num_at, &inp_cur_data->num_bonds, - &inp_cur_data->num_isotopic, nAllocMode ); - cur_INChI_Aux[k] = Alloc_INChI_Aux( inp_cur_data->num_at, - inp_cur_data->num_isotopic, nAllocMode, bOrigCoord ); - if ( cur_INChI_Aux[k] ) { - cur_INChI_Aux[k]->bIsIsotopic = inp_cur_data->num_isotopic; - } - /* alloc memory for the output structure: non-tautomeric and tautomeric (for displaying) */ - CreateInpAtomData( inp_norm_data[k], inp_cur_data->num_at+inp_cur_data->num_removed_H, k ); - inp_norm_data[k]->num_removed_H = inp_cur_data->num_removed_H; - } else { - FreeInpAtomData( inp_norm_data[k] ); - } - } else { - FreeInpAtomData( inp_norm_data[k] ); - } - } - k = pStruct->bMobileH; - /* In case of Fixed-H we have to create InChI for both Fixed-H and Mobile-H */ - num_at = Create_INChI( cur_INChI, cur_INChI_Aux, NULL/* not used */, inp_cur_data->at, - inp_norm_data, - inp_cur_data->num_at+inp_cur_data->num_removed_H, - ip->nMode, &bTautFlags, &bTautFlagsDone, NULL /* &ulMaxTime*/, &pStruct->One_ti, sd->pStrErrStruct); - SetConnectedComponentNumber( inp_cur_data->at, inp_cur_data->num_at, iComponent+1 ); /* normalization alters structure component number */ - - /* detect InChI errors */ - - if ( num_at < 0 ) { - ret = num_at; - } else - if ( cur_INChI[k] && cur_INChI[k]->nErrorCode ) { - ret = cur_INChI[k]->nErrorCode; - } else - if ( cur_INChI_Aux[k] && cur_INChI_Aux[k]->nErrorCode ) { - ret = cur_INChI_Aux[k]->nErrorCode; - } else { - ret = 0; - } - - /* fill out the output */ - - if ( !ret ) { - int bMobileH = pStruct->bMobileH; - if ( bMobileH == TAUT_NON && - 0 == cur_INChI[TAUT_NON]->nNumberOfAtoms && - 0 < cur_INChI[TAUT_YES]->nNumberOfAtoms ) { - /* tautomerism or H(+) removal/addition was not discovered */ - bMobileH = TAUT_YES; - } - pStruct->nChargeRevrs = cur_INChI[TAUT_YES]->nTotalCharge; - - pStruct->pOneINChI[0] = cur_INChI[bMobileH]; - pStruct->pOneINChI_Aux[0] = cur_INChI_Aux[bMobileH]; - pStruct->nOneINChI_bMobileH = bMobileH; - cur_INChI[bMobileH] = NULL; /* remove pointer to avoid deallocation at exit_function */ - cur_INChI_Aux[bMobileH] = NULL; /* remove pointer to avoid deallocation at exit_function */ - - pStruct->nNumRemovedProtons = (pStruct->iMobileH == TAUT_YES)? pStruct->One_ti.tni.nNumRemovedProtons : 0; - - - /* set correct t-group numbers to endpoints */ - t_group_info = &pStruct->One_ti; - if ( t_group_info->num_t_groups && t_group_info->t_group && t_group_info->nEndpointAtomNumber ) { - inp_ATOM *at_norm = inp_norm_data[TAUT_YES]->at; - int num_at_norm = inp_norm_data[TAUT_YES]->num_at; - for ( i = 0; i < num_at_norm; i ++ ) { - at_norm[i].endpoint = 0; - } - for ( i = 0; i < t_group_info->num_t_groups; i ++ ) { - k = t_group_info->t_group[i].nFirstEndpointAtNoPos; - /* add number of mobile (-) to the number of mobile H */ - t_group_info->t_group[i].num[0] += t_group_info->t_group[i].num[1]; - for ( j = 0; j < t_group_info->t_group[i].nNumEndpoints; j ++, k ++ ) { - at_norm[t_group_info->nEndpointAtomNumber[k]].endpoint = t_group_info->t_group[i].nGroupNumber; - } - } - } - pStruct->pOne_norm_data[0] = (INP_ATOM_DATA *) inchi_malloc( sizeof(pStruct->pOne_norm_data[0][0]) ); - if ( pStruct->pOne_norm_data[0] ) { - memcpy( pStruct->pOne_norm_data[0], inp_norm_data[bMobileH], sizeof(pStruct->pOne_norm_data[0][0])); - memset( inp_norm_data[bMobileH], 0, sizeof(*inp_norm_data[0]) ); - } else { - ret = RI_ERR_ALLOC; - } - if ( bMobileH == TAUT_NON && cur_INChI[TAUT_YES]->nNumberOfAtoms > 0 ) { - int bMobileHalt = ALT_TAUT(bMobileH); /* = TAUT_YES */ - pStruct->pOneINChI[1] = cur_INChI[bMobileHalt]; - pStruct->pOneINChI_Aux[1] = cur_INChI_Aux[bMobileHalt]; - cur_INChI[bMobileHalt] = NULL; - cur_INChI_Aux[bMobileHalt] = NULL; - pStruct->pOne_norm_data[1] = (INP_ATOM_DATA *) inchi_malloc( sizeof(pStruct->pOne_norm_data[0][0]) ); - if ( pStruct->pOne_norm_data[1] ) { - memcpy( pStruct->pOne_norm_data[1], inp_norm_data[bMobileHalt], sizeof(pStruct->pOne_norm_data[0][0])); - memset( inp_norm_data[bMobileHalt], 0, sizeof(*inp_norm_data[0]) ); - } else { - ret = RI_ERR_ALLOC; - } - } - } else { -#if ( bRELEASE_VERSION != 1 ) -#ifndef TARGET_API_LIB - fprintf( stdout, "ERROR: Create_INChI returned %d\n", ret ); -#endif -#endif - } - -exit_function: - /* deallocate unused */ - for ( k = 0; k < TAUT_NUM; k ++ ) { - Free_INChI( &cur_INChI[k] ); - Free_INChI_Aux( &cur_INChI_Aux[k] ); - FreeInpAtomData( inp_norm_data[k] ); - } - sd->ulStructTime = ulStructTime; - - return ret; -} - -/****************************************************************************************************** -Input: - at[].num_H = total number of all terminal H connected to the atom - at[].num_iso_H[] = numbers of isotopic H among at[].num_H - Explicit H are disconnected - Calculate InChI with normalization only in MakeOneInChIOutOfStrFromINChI() - with (TG_FLAG_H_ALREADY_REMOVED & bTautFlags) != 0 -Output: - at[].num_H = number of implicit non-isotopic H connected to the atom - at[].num_iso_H[] = numbers of implicit isotopic H (not included in at[].num_H) - Explicit H are connected - Calculate InChI with full preprocessing MakeInChIOutOfStrFromINChI2() - with (TG_FLAG_H_ALREADY_REMOVED & bTautFlags) == 0 -*******************************************************************************************************/ -int ConnectDisconnectedH( inp_ATOM *at, int num_atoms, int num_deleted_H ) -{ - int i, j, k, n, m, num_H; - int tot_atoms = num_atoms + num_deleted_H; - - for ( i = num_atoms; i < tot_atoms; i = j ) { - k = at[i].neighbor[0]; /* a[k] is the atom connected to the explicit hydrogen at[i] */ - for ( j = i; j < tot_atoms && at[j].neighbor[0] == k; j ++ ) - ; - num_H = j-i; /* number of explicit H for at[k] */ - if ( num_H > at[k].num_H ) { - return RI_ERR_PROGR; - } - if ( num_H + at[k].valence > MAXVAL ) { - return RI_ERR_SYNTAX; - } - /* insert links to explicit H before all other links in the connection list */ - n = at[k].valence; - memmove( at[k].neighbor +num_H, at[k].neighbor, sizeof(at[k].neighbor[0]) * n ); - memmove( at[k].bond_stereo+num_H, at[k].bond_stereo, sizeof(at[k].bond_stereo[0]) * n ); - memmove( at[k].bond_type +num_H, at[k].bond_type , sizeof(at[k].bond_type[0]) * n ); - for ( n = 0; n < num_H; n ++ ) { - at[k].neighbor[n] = i + n; - at[k].bond_stereo[n] = 0; - at[k].bond_type[n] = BOND_TYPE_SINGLE; - } - for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[k].sb_parity[m]; m ++ ) { - at[k].sb_ord[m] += num_H; - if ( at[k].sn_ord[m] < 0 ) { - for ( n = i; n < j; n ++ ) { - if ( at[n].orig_at_number == at[k].sn_orig_at_num[m] ) { - at[k].sn_ord[m] = n-i; - break; - } - } - if ( n == j ) { - return RI_ERR_PROGR; - } - } else { - at[k].sn_ord[m] += num_H; - } - } - at[k].valence += num_H; - at[k].chem_bonds_valence += num_H; - at[k].num_H -= num_H; /* cannot be negative */ - /*memset( at[k].num_iso_H, 0, sizeof(at[0].num_iso_H) );*/ /* attached H must carry all isotopic shifts */ - for ( n = i; n < j; n ++ ) { - at[n].chem_bonds_valence = BOND_TYPE_SINGLE; - } - /* isotopic H */ - for ( m = j-1; i <= m && at[m].iso_atw_diff > 0 ; m -- ) { - if ( at[m].iso_atw_diff > NUM_H_ISOTOPES ) { - return RI_ERR_PROGR; - } - if ( 0 >= at[k].num_iso_H[(int)at[m].iso_atw_diff-1] -- ) { - return RI_ERR_PROGR; - } - } - - } - /* subtract isotopic H */ - for ( i = 0; i < num_atoms; i ++ ) { - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - at[i].num_H -= at[i].num_iso_H[m]; - } - if ( 0 > at[i].num_H ) { - return RI_ERR_PROGR; - } - } - - return tot_atoms; -} -/****************************************************************************************************** -Input: - at[].num_H = number of implicit non-isotopic H connected to the atom - at[].num_iso_H[] = numbers of implicit isotopic H (not included in at[].num_H) - Explicit H are connected - Calculate InChI with (TG_FLAG_H_ALREADY_REMOVED & bTautFlags) == 0 -Output: - at[].num_H = total number of all terminal H connected to the atom - at[].num_iso_H[] = numbers of isotopic H among at[].num_H - Explicit H are disconnected - Calculate InChI with (TG_FLAG_H_ALREADY_REMOVED & bTautFlags) != 0 -*******************************************************************************************************/ -int DisconnectedConnectedH( inp_ATOM *at, int num_atoms, int num_deleted_H ) -{ - int i, j, k, n, m, num_H, num_iso_H; - int tot_atoms = num_atoms + num_deleted_H; - - /* add implicit isotopic H to total implicit H */ - for ( i = 0; i < num_atoms; i ++ ) { - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - at[i].num_H += at[i].num_iso_H[m]; - } - } - for ( i = num_atoms; i < tot_atoms; i = j ) { - k = at[i].neighbor[0]; /* a[k] is the atom connected to the explicit hydrogen at[i] */ - for ( j = i; j < tot_atoms && at[j].neighbor[0] == k; j ++ ) { - at[j].chem_bonds_valence = 0; - } - num_H = j-i; /* number of explicit H for at[k] */ - /* verify correct number of explicit H */ - for ( n = 0; n < at[k].valence && at[k].neighbor[n] >= num_atoms; n ++ ) - ; - if ( n != num_H ) { - return RI_ERR_PROGR; - } - /* remove bonds to explicit H located in front of all other bonds in the connection list */ - n = (at[k].valence -= num_H); /* new number of bonds */ - at[k].chem_bonds_valence -= num_H; /* new no-H valence */ - if ( n ) { - memmove( at[k].neighbor, at[k].neighbor + num_H, sizeof(at[k].neighbor[0]) * n ); - memmove( at[k].bond_stereo, at[k].bond_stereo + num_H, sizeof(at[k].bond_stereo[0]) * n ); - memmove( at[k].bond_type, at[k].bond_type + num_H, sizeof(at[k].bond_type[0]) * n ); - } - /* clear the 'tails' */ - memset( at[k].neighbor+n, 0, sizeof(at[k].neighbor[0]) * num_H ); - memset( at[k].bond_stereo+n, 0, sizeof(at[k].bond_stereo[0]) * num_H ); - memset( at[k].bond_type+n, 0, sizeof(at[k].bond_type[0]) * num_H ); - - for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[k].sb_parity[m]; m ++ ) { - at[k].sb_ord[m] -= num_H; - if ( 0 <= at[k].sn_ord[m] && at[k].sn_ord[m] < num_H ) { - at[k].sn_ord[m] = -1; /* disconnected explicit H */ - } - } - /* add explicit isotopic H (already included in num_H) */ - for ( num_iso_H = 0, m = j-1; i <= m && at[m].iso_atw_diff > 0 ; m -- ) { - if ( at[m].iso_atw_diff > NUM_H_ISOTOPES ) { - return RI_ERR_PROGR; - } - at[k].num_iso_H[(int)at[m].iso_atw_diff-1] ++; - } - at[k].num_H += num_H; /* add all explicit H including isotopic */ - } - return tot_atoms; -} -/******************************************************************************************************/ -int MakeInChIOutOfStrFromINChI2( ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd_inp, StrFromINChI *pStruct, - int iComponent, int iAtNoOffset, long num_inp ) -{ - char szTitle[MAX_SDF_HEADER+MAX_SDF_VALUE+256]; - - int len, ret; - /* - PINChI2 *pINChI[INCHI_NUM]; - PINChI_Aux2 *pINChI_Aux[INCHI_NUM]; - */ - char pStr[256]; - INPUT_PARMS local_ip; - STRUCT_DATA local_sd; - INPUT_PARMS *ip = &local_ip; - STRUCT_DATA *sd = &local_sd; - - ORIG_ATOM_DATA OrigAtData; /* 0=> disconnected, 1=> original */ - ORIG_ATOM_DATA *orig_inp_data = &OrigAtData; - ORIG_ATOM_DATA PrepAtData[2]; /* 0=> disconnected, 1=> original */ - ORIG_ATOM_DATA *prep_inp_data = PrepAtData; - - *ip = *ip_inp; - ip->bDisplay = 0; - ip->bDisplayCompositeResults = 0; - ip->bDisplayEachComponentINChI = 0; - ip->bDisplayIfRestoreWarnings = 0; - ip->bINChIOutputOptions = INCHI_OUT_NO_AUX_INFO; - /* - if ( pStruct->bMobileH ) { - ip->nMode &= ~REQ_MODE_BASIC; - ip->nMode |= REQ_MODE_TAUT; - } else { - ip->nMode |= (REQ_MODE_TAUT | REQ_MODE_BASIC); - } - */ - memset( sd, 0, sizeof(*sd) ); - sd->fPtrStart = -1; - sd->fPtrEnd = -1; - /* - if ( ip->nMode & REQ_MODE_STEREO ) { - if ( ip->nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO) ) { - sd->bChiralFlag |= FLAG_INP_AT_NONCHIRAL; - } else { - sd->bChiralFlag |= FLAG_INP_AT_CHIRAL; - } - } - */ - memset( orig_inp_data , 0, sizeof( *orig_inp_data ) ); - memset( prep_inp_data , 0, 2*sizeof( *prep_inp_data ) ); - memset( pStruct->RevInChI.pINChI, 0, sizeof(pStruct->RevInChI.pINChI ) ); - memset( pStruct->RevInChI.pINChI_Aux, 0, sizeof(pStruct->RevInChI.pINChI_Aux) ); - memset( pStr, 0, sizeof(pStr) ); - memset( szTitle, 0, sizeof(szTitle) ); - - len = sizeof(orig_inp_data->at[0])*(pStruct->num_atoms + pStruct->num_deleted_H); - orig_inp_data->at = (inp_ATOM *) inchi_malloc( len ); - if ( orig_inp_data->at ) { - /*memcpy( orig_inp_data->at, pStruct->at2, len );*/ - /*ret = ConnectDisconnectedH( orig_inp_data->at, pStruct->num_atoms, pStruct->num_deleted_H );*/ - CopySt2At( pStruct->at2, pStruct->st, pStruct->num_atoms ); - ret = ConnectDisconnectedH( pStruct->at2, pStruct->num_atoms, pStruct->num_deleted_H ); - if ( ret < 0 ) { - goto exit_error; - } - orig_inp_data->num_inp_atoms = ret; - /* connections changed => reconcile parities even if they were reconciled before */ - /* remove t-group markings and increment zero-order bonds, - otherwise MakeInChIOutOfStrFromINChI2() woild fail */ - /* - IncrZeroBondsAndClearEndpts(pStruct->at2, pStruct->num_atoms, iComponent+1); - */ - IncrZeroBonds(pStruct->at2, pStruct->num_atoms, iComponent+1); - - /* CopySt2At() moved to the position before ConnectDisconnectedH() because - in case stereo exists only in Mobile-H layer and the processd here - component is restored in Fixed-H layer the parities needed by - ConnectDisconnectedH() must be there before calling - ConnectDisconnectedH() - */ - /*CopySt2At( pStruct->at2, pStruct->st, pStruct->num_atoms );*/ - - ret = ReconcileAllCmlBondParities( pStruct->at2, orig_inp_data->num_inp_atoms, 0 ); - if ( ret < 0 ) { - goto exit_error; - } - memcpy( orig_inp_data->at, pStruct->at2, len ); - ClearEndpts(orig_inp_data->at, pStruct->num_atoms); - if ( FixUnkn0DStereoBonds(orig_inp_data->at, pStruct->num_atoms) ) { - ret = ReconcileAllCmlBondParities( pStruct->at2, orig_inp_data->num_inp_atoms, 0 ); - if ( ret < 0 ) { - goto exit_error; - } - } - /* keep endpoint[] markings in at2[] for subsequent add/remove protons */ - } else { - ret = RI_ERR_ALLOC; - goto exit_error; - } - memset( sd->num_components, 0, sizeof(sd->num_components) ); - memset( sd->num_taut, 0, sizeof(sd->num_taut) ); - memset( sd->num_non_taut, 0, sizeof(sd->num_non_taut) ); - memset( sd->bTautFlagsDone, 0, sizeof(sd->bTautFlagsDone) ); - memset( sd->bTautFlags, 0, sizeof(sd->bTautFlags) ); - - ret = ProcessOneStructure( sd, ip, szTitle, pStruct->RevInChI.pINChI, pStruct->RevInChI.pINChI_Aux, - NULL /*inp_file*/, NULL /*log_file*/, NULL /*output_file*/, NULL /*prb_file*/, - orig_inp_data, prep_inp_data, - num_inp, pStr, sizeof(pStr), - 0 /* save_opt_bits */); - - memcpy(pStruct->RevInChI.num_components, sd->num_components, sizeof(pStruct->RevInChI.num_components) ); - memcpy(sd_inp->pStrErrStruct, sd->pStrErrStruct, sizeof(sd_inp->pStrErrStruct) ); - pStruct->RevInChI.nRetVal = ret; - /* translate returned value */ - if ( ret == _IS_ERROR || ret == _IS_FATAL || ret == _IS_UNKNOWN ) { - ret = RI_ERR_PROGR; - } else - if ( ret == _IS_OKAY ) { - ret = 0; - } else - if ( ret == _IS_WARNING ) { - ret = 1; - } else { - ret = RI_ERR_PROGR; - } - /* save total charge from Mobile-H layer */ - pStruct->nChargeRevrs = 0; - if ( ret >= 0 ) { - if ( bRevInchiComponentExists( pStruct, INCHI_REC, TAUT_YES, 0 ) ) { - pStruct->nChargeRevrs = pStruct->RevInChI.pINChI[INCHI_REC][0][TAUT_YES]->nTotalCharge; - } else - if ( bRevInchiComponentExists( pStruct, INCHI_BAS, TAUT_YES, 0 ) ) { - pStruct->nChargeRevrs = pStruct->RevInChI.pINChI[INCHI_BAS][0][TAUT_YES]->nTotalCharge; - } - } - - /* free structure data */ - FreeOrigAtData( orig_inp_data ); - FreeOrigAtData( prep_inp_data ); - FreeOrigAtData( prep_inp_data+1 ); - -exit_error: - return ret; -} -/******************************************************************************************************/ -int OutputInChIOutOfStrFromINChI(ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd_inp, - long num_inp, int bINChIOutputOptions, - INCHI_IOSTREAM *pout, INCHI_IOSTREAM *plog, - InpInChI *pOneInput, int bHasSomeFixedH, - unsigned char save_opt_bits) -{ - char szTitle[MAX_SDF_HEADER+MAX_SDF_VALUE+256]; - - int len, ret; -/* - PINChI2 *pINChI[INCHI_NUM]; - PINChI_Aux2 *pINChI_Aux[INCHI_NUM]; -*/ - REV_INCHI RevInChI; - int nStrLen = INCHI_SEGM_BUFLEN; - char *pStr = NULL; - - INPUT_PARMS local_ip; - STRUCT_DATA local_sd; - INPUT_PARMS *ip = &local_ip; - STRUCT_DATA *sd = &local_sd; - - ORIG_ATOM_DATA OrigAtData; /* 0=> disconnected, 1=> original */ - ORIG_ATOM_DATA *orig_inp_data = &OrigAtData; - ORIG_ATOM_DATA PrepAtData[2]; /* 0=> disconnected, 1=> original */ - ORIG_ATOM_DATA *prep_inp_data = PrepAtData; - - *ip = *ip_inp; - ip->bNoStructLabels = 1; - ip->bDisplay = 0; - ip->bDisplayCompositeResults = 0; - ip->bDisplayEachComponentINChI = 0; - ip->bDisplayIfRestoreWarnings = 0; -#if ( I2S_MODIFY_OUTPUT == 1 ) - if ( bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY ) - ip->bINChIOutputOptions = bINChIOutputOptions & ~(INCHI_OUT_PLAIN_TEXT | INCHI_OUT_XML | INCHI_OUT_PLAIN_TEXT_COMMENTS | INCHI_OUT_XML_TEXT_COMMENTS); - else - if ( bINChIOutputOptions & INCHI_OUT_XML ) - ip->bINChIOutputOptions = bINChIOutputOptions & ~(INCHI_OUT_PLAIN_TEXT | INCHI_OUT_SDFILE_ONLY) | INCHI_OUT_EMBED_REC; - else - if ( bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT ) - ip->bINChIOutputOptions = bINChIOutputOptions & ~(INCHI_OUT_XML | INCHI_OUT_SDFILE_ONLY) | INCHI_OUT_EMBED_REC; - else - if ( bINChIOutputOptions & (INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO | INCHI_OUT_ONLY_AUX_INFO | INCHI_OUT_TABBED_OUTPUT)) - ip->bINChIOutputOptions = (INCHI_OUT_PLAIN_TEXT | INCHI_OUT_EMBED_REC | bINChIOutputOptions); - else - ip->bINChIOutputOptions = (INCHI_OUT_PLAIN_TEXT | INCHI_OUT_EMBED_REC); -#else - ip->bINChIOutputOptions = (INCHI_OUT_PLAIN_TEXT | INCHI_OUT_EMBED_REC ); -#endif - - if ( bHasSomeFixedH ) - { - ip->nMode |= (REQ_MODE_TAUT | REQ_MODE_BASIC); - } else - { - ip->nMode &= ~REQ_MODE_BASIC; - ip->nMode |= REQ_MODE_TAUT; - } - - memset( sd, 0, sizeof(*sd) ); - sd->fPtrStart = -1; - sd->fPtrEnd = -1; - /* - if ( ip->nMode & REQ_MODE_STEREO ) { - if ( ip->nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO) ) { - sd->bChiralFlag |= FLAG_INP_AT_NONCHIRAL; - } else { - sd->bChiralFlag |= FLAG_INP_AT_CHIRAL; - } - } - */ - memset( orig_inp_data, 0, sizeof( *orig_inp_data ) ); - memset( prep_inp_data, 0, 2*sizeof( *prep_inp_data ) ); - memset( RevInChI.pINChI, 0, sizeof(RevInChI.pINChI ) ); - memset( RevInChI.pINChI_Aux, 0, sizeof(RevInChI.pINChI_Aux) ); - - len = sizeof(orig_inp_data->at[0]) * pOneInput->num_atoms; - orig_inp_data->at = (inp_ATOM *) inchi_malloc( len ); - orig_inp_data->szCoord = (MOL_COORD *)inchi_calloc( pOneInput->num_atoms, sizeof(orig_inp_data->szCoord[0])); - pStr = (char *)inchi_calloc( nStrLen, sizeof(char) ); - if ( orig_inp_data->at && orig_inp_data->szCoord && pStr ) { - int i, k; - memcpy( orig_inp_data->at, pOneInput->atom, len ); - orig_inp_data->num_inp_atoms = pOneInput->num_atoms; - ClearEndpts( orig_inp_data->at, orig_inp_data->num_inp_atoms ); - /* otherwise fails on CID=450438 */ - if ( FixUnkn0DStereoBonds(orig_inp_data->at, orig_inp_data->num_inp_atoms) ) { - ret = ReconcileAllCmlBondParities( orig_inp_data->at, orig_inp_data->num_inp_atoms, 0 ); - if ( ret < 0 ) { - goto exit_error; - } - } - /* To obtain rA,rB,rC in AuxInfo we have to emulate input coordinates; make all of them zeroes */ - for ( i = 0; i < pOneInput->num_atoms; i ++ ) { - for ( k = 0; k < NUM_COORD*LEN_COORD; k += LEN_COORD ) { - orig_inp_data->szCoord[i][k] = '0'; - } - } - } else { - ret = RI_ERR_ALLOC; - goto exit_error; - } - memset( sd->num_components, 0, sizeof(sd->num_components) ); - memset( sd->num_taut, 0, sizeof(sd->num_taut) ); - memset( sd->num_non_taut, 0, sizeof(sd->num_non_taut) ); - memset( sd->bTautFlagsDone, 0, sizeof(sd->bTautFlagsDone) ); - memset( sd->bTautFlags, 0, sizeof(sd->bTautFlags) ); - memset( szTitle, 0, sizeof(szTitle) ); - - ret = ProcessOneStructure(sd, ip, szTitle, RevInChI.pINChI, RevInChI.pINChI_Aux, - NULL /*inp_file*/, plog /*log_file*/, pout /*output_file*/, NULL /*prb_file*/, - orig_inp_data, prep_inp_data, - num_inp, pStr, nStrLen, - save_opt_bits); - memcpy(RevInChI.num_components, sd->num_components, sizeof(RevInChI.num_components) ); - /* - memcpy(sd_inp->pStrErrStruct, sd->pStrErrStruct, sizeof(sd_inp->pStrErrStruct) ); - */ - RevInChI.nRetVal = ret; - /* translate returned value */ - if ( ret == _IS_ERROR || ret == _IS_FATAL || ret == _IS_UNKNOWN ) { - ret = RI_ERR_PROGR; - } else - if ( ret == _IS_OKAY ) { - ret = 0; - } else - if ( ret == _IS_WARNING ) { - ret = 1; - } else { - ret = RI_ERR_PROGR; - } - - /* free structure data */ - FreeOrigAtData( orig_inp_data ); - FreeOrigAtData( prep_inp_data ); - FreeOrigAtData( prep_inp_data+1 ); - FreeAllINChIArrays( RevInChI.pINChI, - RevInChI.pINChI_Aux, - RevInChI.num_components ); - - -exit_error: - if ( pStr ) inchi_free( pStr ); - return ret; -} -#endif diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichirvr2.c b/INCHI-1-SRC/INCHI_API/inchi_dll/ichirvr2.c deleted file mode 100644 index 00c5083..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichirvr2.c +++ /dev/null @@ -1,6376 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - -/*^^^ */ -/*#define CHECK_WIN32_VC_HEAP*/ -#include "mode.h" - -#if ( READ_INCHI_STRING == 1 ) - -#include "ichi.h" -#include "ichitime.h" - -#include "inpdef.h" -#include "ichimain.h" -#include "ichierr.h" -#include "incomdef.h" -#include "ichiring.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "util.h" - -#include "ichicomp.h" -#include "ichister.h" - -#include "ichi_bns.h" - -#include "strutil.h" - -#include "ichirvrs.h" - -/******************************************************************************************************/ -void CopyAt2St( inp_ATOM *at, inp_ATOM_STEREO * st, int num_atoms ) -{ - int i; - for ( i = 0; i < num_atoms; i ++ ) { - if ( at[i].p_parity ) { - memcpy( st[i].p_orig_at_num, at[i].p_orig_at_num, sizeof(st[0].p_orig_at_num) ); - st[i].p_parity = at[i].p_parity; - } - if ( at[i].sb_parity[0] ) { - memcpy( st[i].sb_ord, at[i].sb_ord, sizeof(st[0].sb_ord) ); - memcpy( st[i].sb_parity, at[i].sb_parity, sizeof(st[0].sb_parity) ); - memcpy( st[i].sn_ord, at[i].sn_ord, sizeof(st[0].sn_ord) ); - memcpy( st[i].sn_orig_at_num, at[i].sn_orig_at_num, sizeof(st[0].sn_orig_at_num) ); - } - } -} -void CopySt2At( inp_ATOM *at, inp_ATOM_STEREO * st, int num_atoms ) -{ - int i; - if ( !st ) { - return; - } - for ( i = 0; i < num_atoms; i ++ ) { - if ( st[i].p_parity ) { - memcpy( at[i].p_orig_at_num, st[i].p_orig_at_num, sizeof(at[0].p_orig_at_num) ); - at[i].p_parity = st[i].p_parity; - } - if ( st[i].sb_parity[0] ) { - memcpy( at[i].sb_ord, st[i].sb_ord, sizeof(st[0].sb_ord) ); - memcpy( at[i].sb_parity, st[i].sb_parity, sizeof(at[0].sb_parity) ); - memcpy( at[i].sn_ord, st[i].sn_ord, sizeof(at[0].sn_ord) ); - memcpy( at[i].sn_orig_at_num, st[i].sn_orig_at_num, sizeof(at[0].sn_orig_at_num) ); - } - } -} - -/******************************************************************************************************/ -int RestoreAtomConnectionsSetStereo( StrFromINChI *pStruct, int iComponent, int iAtNoOffset, INChI *pInChI, INChI *pInChIMobH) -{ - inp_ATOM *at = NULL; - inp_ATOM_STEREO * st = NULL; - int num_atoms, i, jv, jn, n_vertex, n_neigh, num_H, parity; - int nNumDeletedH=0, iDeletedH=0, idelH1, idelH2, ret = 0, len; - int num_stereo_bonds, num_stereo_centers, num_stereo_bonds2, num_stereo_centers2; - INChI_Stereo *pStereo = NULL, *pStereo2 = NULL; - AT_NUMB nCumulene[MAX_CUMULENE_LEN+2]; - - num_atoms = pInChI->nNumberOfAtoms; - if ( num_atoms <= 0 ) { - return 0; - } - INCHI_HEAPCHK - /* atoms */ - pStruct->at = at = (inp_ATOM *) inchi_calloc ( num_atoms, sizeof(pStruct->at[0]) ); - if ( !at ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - pStruct->num_atoms = num_atoms; - /* charge */ - pStruct->charge = pInChI->nTotalCharge; - /* elements, terminal atoms H */ - for ( i = 0; i < num_atoms; i ++ ) { - at[i].el_number = pInChI->nAtom[i]; - if ( GetElementFormulaFromAtNum(UCINT pInChI->nAtom[i], at[i].elname ) ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - at[i].orig_at_number = iAtNoOffset + i+1; - at[i].orig_compt_at_numb = i + 1; - at[i].component = iComponent + 1; - num_H = pInChI->nNum_H[i]; - /* --- pInChI->nNum_H_fixed[i] was added to pInChI->nNum_H[i] --- - if ( pInChI->nNum_H_fixed ) { - num_H += pInChI->nNum_H_fixed[i]; - } - */ - at[i].num_H = num_H; - } - INCHI_HEAPCHK - /* connections */ - for ( i = 1, n_vertex = pInChI->nConnTable[0]-1; i < pInChI->lenConnTable; i ++ ) { - if ( (n_neigh = pInChI->nConnTable[i]-1) < n_vertex ) { - /* vertex - neighbor connection */ - jv = at[n_vertex].valence ++; - at[n_vertex].neighbor[jv] = n_neigh; - at[n_vertex].bond_type[jv] = BOND_TYPE_SINGLE; - at[n_vertex].chem_bonds_valence += at[n_vertex].bond_type[jv]; - /* neighbor - vertex connection */ - jn = at[n_neigh].valence ++; - at[n_neigh].neighbor[jn] = n_vertex; - at[n_neigh].bond_type[jn] = BOND_TYPE_SINGLE; - at[n_neigh].chem_bonds_valence += at[n_neigh].bond_type[jn]; - } else - if ( (n_vertex = n_neigh) >= num_atoms ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - } - INCHI_HEAPCHK - /* isotopic atoms */ - if ( pInChI->IsotopicAtom && pInChI->nNumberOfIsotopicAtoms ) { - for ( i = 0; i < pInChI->nNumberOfIsotopicAtoms; i ++ ) { - n_vertex = pInChI->IsotopicAtom[i].nAtomNumber-1; - at[n_vertex].iso_atw_diff = (char)pInChI->IsotopicAtom[i].nIsoDifference; - at[n_vertex].num_iso_H[0] = (char)pInChI->IsotopicAtom[i].nNum_H; - at[n_vertex].num_iso_H[1] = (char)pInChI->IsotopicAtom[i].nNum_D; - at[n_vertex].num_iso_H[2] = (char)pInChI->IsotopicAtom[i].nNum_T; - } - pStruct->bIsotopic |= 1; - } - INCHI_HEAPCHK - /* tautomeric groups */ - if ( ret = GetTgroupInfoFromInChI( &pStruct->ti, at, NULL, pInChI ) ) { - goto exit_function; - } - - /* coordinates: data from unused members: pInChI->IsotopicTGroup and InChI->nNumberOfIsotopicTGroups */ - if ( pInChI->IsotopicTGroup && !pInChI->nNumberOfIsotopicTGroups ) { - pStruct->pXYZ = (XYZ_COORD *) pInChI->IsotopicTGroup; - pInChI->IsotopicTGroup = NULL; - } - /* stereo */ - if ( pInChI->StereoIsotopic && - (pInChI->StereoIsotopic->nNumberOfStereoBonds + - pInChI->StereoIsotopic->nNumberOfStereoCenters) ) { - pStereo = pInChI->StereoIsotopic; - } else - if ( pInChI->Stereo && - (pInChI->Stereo->nNumberOfStereoBonds + - pInChI->Stereo->nNumberOfStereoCenters) ) { - pStereo = pInChI->Stereo; - } else { - pStereo = NULL; - } - /* stereo2: Mobile-H in addition to Fixed-H*/ - pStereo2 = NULL; - if ( pInChIMobH && pInChIMobH->nNumberOfAtoms ) { - if ( pInChIMobH->StereoIsotopic && - (pInChIMobH->StereoIsotopic->nNumberOfStereoBonds + - pInChIMobH->StereoIsotopic->nNumberOfStereoCenters) ) { - pStereo2 = pInChIMobH->StereoIsotopic; - } else - if ( pInChIMobH->Stereo && - (pInChIMobH->Stereo->nNumberOfStereoBonds + - pInChIMobH->Stereo->nNumberOfStereoCenters) ) { - pStereo2 = pInChIMobH->Stereo; - } - } - INCHI_HEAPCHK - - num_stereo_bonds = num_stereo_bonds2 = 0; - num_stereo_centers = num_stereo_centers2 = 0; - /* -- have already been done in the initialization -- - iDeletedH = 0; - nNumDeletedH = 0; - */ - if ( pStereo || pStereo2 ) { - /* count implicit H needed for parities and reallocate at[]; set at[n_vertex].at_type=1 for these atoms */ - int len1 = pStereo? pStereo->nNumberOfStereoCenters : 0; - int len2 = pStereo2? pStereo2->nNumberOfStereoCenters : 0; - int i2, diff, diff2; - for ( i = i2 = 0; i < len1 || i2 < len2; ) { - if ( i < len1 && i2 < len2 ) { - diff = (int)pStereo->nNumber[i] - (int)pStereo2->nNumber[i2]; - if ( diff <= 0 ) { - n_vertex = pStereo->nNumber[i]-1; - i ++; - i2 += !diff; - } else { - n_vertex = pStereo2->nNumber[i2]-1; - num_stereo_centers2 ++; - i2 ++; - } - } else - if ( i < len1 ) { - n_vertex = pStereo->nNumber[i]-1; - i ++; - } else { - n_vertex = pStereo2->nNumber[i2]-1; - num_stereo_centers2 ++; - i2 ++; - } - /* find whether it is an allene */ - if ( at[n_vertex].valence == 2 && - at[n_vertex].num_H == 0 && - bCanAtomBeMiddleAllene(at[n_vertex].elname, 0, 0) && - at[jv = at[n_vertex].neighbor[0]].valence + at[jv].num_H == 3 && - bCanAtomBeTerminalAllene(at[jv].elname, 0, 0) && - at[jn = at[n_vertex].neighbor[1]].valence + at[jn].num_H == 3 && - bCanAtomBeTerminalAllene(at[jn].elname, 0, 0) ) { - /* allene */ - if ( !at[jv].at_type && at[jv].num_H ) { - nNumDeletedH += at[jv].num_H; - at[jv].at_type ++; /* H should be added as an explicit H */ - } - if ( !at[jn].at_type && at[jn].num_H ) { - nNumDeletedH += at[jn].num_H; - at[jn].at_type ++; /* H should be added as an explicit H */ - } - } else { - /* stereogenic atom - sp3 */ - if ( !at[n_vertex].at_type && at[n_vertex].num_H ) { - nNumDeletedH += at[n_vertex].num_H; - at[n_vertex].at_type ++; /* H should be added as an explicit H */ - } - } - } - INCHI_HEAPCHK - len1 = pStereo? pStereo->nNumberOfStereoBonds : 0; - len2 = pStereo2? pStereo2->nNumberOfStereoBonds : 0; - for ( i = i2 = 0; i < len1 || i2 < len2; ) { - if ( i < len1 && i2 < len2 ) { - diff = (int)pStereo->nBondAtom1[i] - (int)pStereo2->nBondAtom1[i2]; - diff2 = (int)pStereo->nBondAtom2[i] - (int)pStereo2->nBondAtom2[i2]; - if ( diff < 0 || diff == 0 && diff2 <= 0) { - n_vertex = pStereo->nBondAtom1[i]-1; - n_neigh = pStereo->nBondAtom2[i]-1; - i ++; - i2 += !diff && !diff2; - } else { - n_vertex = pStereo2->nBondAtom1[i2]-1; - n_neigh = pStereo2->nBondAtom2[i2]-1; - num_stereo_bonds2 ++; - i2 ++; - } - } else - if ( i < len1 ) { - n_vertex = pStereo->nBondAtom1[i]-1; - n_neigh = pStereo->nBondAtom2[i]-1; - i ++; - } else { - n_vertex = pStereo2->nBondAtom1[i2]-1; - n_neigh = pStereo2->nBondAtom2[i2]-1; - num_stereo_bonds2 ++; - i2 ++; - } - if ( !is_in_the_list( at[n_vertex].neighbor, (AT_NUMB)n_neigh, at[n_vertex].valence ) ) { - /* must be a cumulene */ - if ( !bFindCumuleneChain( at, (AT_NUMB)n_vertex, (AT_NUMB)n_neigh, nCumulene, MAX_CUMULENE_LEN+1 ) ) { - ret = RI_ERR_SYNTAX; /* not a cumulene */ - goto exit_function; - } - } - if ( !at[n_vertex].at_type && at[n_vertex].num_H ) { - nNumDeletedH += at[n_vertex].num_H; - at[n_vertex].at_type ++; /* H should be added as an explicit H */ - } - if ( !at[n_neigh].at_type && at[n_neigh].num_H ) { - nNumDeletedH += at[n_neigh].num_H; - at[n_neigh].at_type ++; /* H should be added as an explicit H */ - } - } - INCHI_HEAPCHK - if ( nNumDeletedH ) { - /* add explicit H */ - inp_ATOM *at2 = (inp_ATOM *)inchi_calloc( num_atoms + nNumDeletedH, sizeof(at2[0]) ); - if ( !at2 ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - pStruct->num_deleted_H = nNumDeletedH; - memcpy( at2, at, num_atoms * sizeof(at2[0]) ); - inchi_free( at ); - pStruct->at = at = at2; - /* fill out deleted H atom info */ - for ( i = num_atoms; i < num_atoms + nNumDeletedH; i ++ ) { - strcpy( at[i].elname, "H" ); - at[i].el_number = EL_NUMBER_H; - at[i].orig_at_number = iAtNoOffset + i+1; - at[i].orig_compt_at_numb = i + 1; - at[i].component = iComponent + 1; - } - /* connect deleted H */ - for( i = 0; i < num_atoms; i ++ ) { - if ( at[i].at_type == 1 ) { - if ( 0 > (ret = AddExplicitDeletedH( at, i, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) { - goto exit_function; - } - } - } - } - INCHI_HEAPCHK - } - - if ( pStereo ) { - /* mark stereo centers, they have already been connected the added explicit H, if any */ - int bInvertedParity = (pStereo->nCompInv2Abs == -1); - for ( i = 0; i < pStereo->nNumberOfStereoCenters; i ++ ) { - n_vertex = pStereo->nNumber[i]-1; - parity = pStereo->t_parity[i]; - if ( bInvertedParity ) { - parity = (parity == AB_PARITY_EVEN)? AB_PARITY_ODD : (parity == AB_PARITY_ODD)? AB_PARITY_EVEN : parity; - } - /* find whether it is allene */ - if ( at[n_vertex].valence == 2 && - at[n_vertex].num_H == 0 && - bCanAtomBeMiddleAllene(at[n_vertex].elname, 0, 0) && - /* allene has exactly 2 double bonds */ - (jv = at[n_vertex].neighbor[0], at[jv].valence + at[jv].num_H == 3) && - bCanAtomBeTerminalAllene(at[jv].elname, 0, 0) && - (jn = at[n_vertex].neighbor[1], at[jn].valence + at[jn].num_H == 3) && - bCanAtomBeTerminalAllene(at[jn].elname, 0, 0) ) { - /* allene: add explicit H if implicit H are present */ - /* iDeletedH = current number of already added explicit H */ - /* idelH1 = index in at[] of the explicit H added to atom jv */ - if ( at[jv].num_H ) { - if ( 0 > (ret = AddExplicitDeletedH( at, jv, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) { - goto exit_function; - } - } else { - /* index of the stereo atom neighbor */ - idelH1 = at[jv].neighbor[at[jv].neighbor[0]==n_vertex]; - } - if ( at[jn].num_H ) { - /* iDeletedH = current number of already added explicit H */ - /* idelH2 = index of the explicit H added to atom jn */ - if ( 0 > (ret = AddExplicitDeletedH( at, jn, num_atoms, &iDeletedH, &idelH2, nNumDeletedH, pStereo2 != NULL ))) { - goto exit_function; - } - } else { - idelH2 = at[jn].neighbor[at[jn].neighbor[0]==n_vertex]; - } - /* allene: set bond types to double */ - /* - if ( 0 > (ret = set_bond_type( at, (AT_NUMB)n_vertex, (AT_NUMB)jv, BOND_TYPE_DOUBLE ) ) || - 0 > (ret = set_bond_type( at, (AT_NUMB)n_vertex, (AT_NUMB)jn, BOND_TYPE_DOUBLE ) ) ) { - goto exit_function; - } - */ - /* allene: make 0D parity */ - ret = set_cumulene_0D_parity( at, st, num_atoms, idelH1, jv, jn, idelH2, parity, 2 ); - if ( ret < 0 ) { - goto exit_function; - } - } else { - /* stereogenic sp3 atom */ - if ( at[n_vertex].num_H ) { - if ( 0 > (ret = AddExplicitDeletedH( at, n_vertex, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) { - goto exit_function; - } - } - ret = set_atom_0D_parity( at, st, num_atoms, nNumDeletedH, n_vertex, parity ); - if ( ret < 0 ) { - goto exit_function; - } - num_stereo_centers ++; - } - if ( ret < 0 ) { - goto exit_function; - } - } - INCHI_HEAPCHK - /* mark stereobonds */ - for ( i = 0; i < pStereo->nNumberOfStereoBonds; i ++ ) { - jv = pStereo->nBondAtom1[i]-1; - jn = pStereo->nBondAtom2[i]-1; - parity = pStereo->b_parity[i]; - if ( !is_in_the_list( at[jv].neighbor, (AT_NUMB)jn, at[jv].valence ) ) { - /* must be a cumulene */ - if ( !bFindCumuleneChain( at, (AT_NUMB)jv, (AT_NUMB)jn, nCumulene, MAX_CUMULENE_LEN+1 ) ) { - return RI_ERR_SYNTAX; /* not a cumulene */ - } - len = MAX_CUMULENE_LEN+1; - } else { - /* a regular double or alt bond */ - nCumulene[0] = jv; - nCumulene[1] = jn; - len = 1; /* cumulene length is number of bonds, not number of atoms */ - } - /* cumulene or double bond: add explicit H if implicit H are present */ - if ( at[jv].num_H ) { - if ( 0 > (ret = AddExplicitDeletedH( at, jv, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) { - goto exit_function; - } - } else { - /* double bond neighbor that has the smallest canonical number; it is either 0th or 1st */ - idelH1 = at[jv].neighbor[at[jv].neighbor[0]==nCumulene[1]]; - } - if ( at[jn].num_H ) { - if ( 0 > (ret = AddExplicitDeletedH( at, jn, num_atoms, &iDeletedH, &idelH2, nNumDeletedH, pStereo2 != NULL ))) { - goto exit_function; - } - } else { - idelH2 = at[jn].neighbor[at[jn].neighbor[0]==nCumulene[len-1]]; - } - if ( 0 > (ret = set_cumulene_0D_parity( at, st, num_atoms, idelH1, jv, jn, idelH2, parity, len )) ) { - goto exit_function; - } - } - INCHI_HEAPCHK - } - /* allocate memory for Mobile-H-only stereo */ - if ( num_stereo_centers2 + num_stereo_bonds2 ) { - if ( !(st = (inp_ATOM_STEREO *)inchi_calloc( num_atoms, sizeof(st[0])))) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - CopyAt2St( at, st, num_atoms ); - } - pStruct->st = st; - if ( num_stereo_centers2 ) { - /* In case of Fixed-H */ - /* mark additional Mobile-H stereo centers, they have already been connected the added explicit H, if any */ - int bInvertedParity = (pStereo2->nCompInv2Abs == -1); - for ( i = 0; i < pStereo2->nNumberOfStereoCenters; i ++ ) { - n_vertex = pStereo2->nNumber[i]-1; - parity = pStereo2->t_parity[i]; - if ( at[n_vertex].p_parity ) { - continue; /* the parity has already been set for Fixed-H */ - } - if ( bInvertedParity ) { - parity = (parity == AB_PARITY_EVEN)? AB_PARITY_ODD : (parity == AB_PARITY_ODD)? AB_PARITY_EVEN : parity; - } - /* find whether it is allene */ - if ( at[n_vertex].valence == 2 && - at[n_vertex].num_H == 0 && - bCanAtomBeMiddleAllene(at[n_vertex].elname, 0, 0) && - /* allene has exactly 2 double bonds */ - (jv = at[n_vertex].neighbor[0], at[jv].valence + at[jv].num_H == 3) && - bCanAtomBeTerminalAllene(at[jv].elname, 0, 0) && - (jn = at[n_vertex].neighbor[1], at[jn].valence + at[jn].num_H == 3) && - bCanAtomBeTerminalAllene(at[jn].elname, 0, 0) ) { - /* allene: add explicit H if implicit H are present */ - /* iDeletedH = current number of already added explicit H */ - /* idelH1 = index in at[] of the explicit H added to atom jv */ - if ( at[jv].num_H ) { - if ( 0 > (ret = AddExplicitDeletedH( at, jv, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) { - goto exit_function; - } - } else { - /* index of the stereo atom neighbor */ - idelH1 = at[jv].neighbor[at[jv].neighbor[0]==n_vertex]; - } - if ( at[jn].num_H ) { - /* iDeletedH = current number of already added explicit H */ - /* idelH2 = index of the explicit H added to atom jn */ - if ( 0 > (ret = AddExplicitDeletedH( at, jn, num_atoms, &iDeletedH, &idelH2, nNumDeletedH, pStereo2 != NULL ))) { - goto exit_function; - } - } else { - idelH2 = at[jn].neighbor[at[jn].neighbor[0]==n_vertex]; - } - /* allene: set bond types to double */ - /* - if ( 0 > (ret = set_bond_type( at, (AT_NUMB)n_vertex, (AT_NUMB)jv, BOND_TYPE_DOUBLE ) ) || - 0 > (ret = set_bond_type( at, (AT_NUMB)n_vertex, (AT_NUMB)jn, BOND_TYPE_DOUBLE ) ) ) { - goto exit_function; - } - */ - /* allene: make 0D parity */ - ret = set_cumulene_0D_parity( at, st, num_atoms, idelH1, jv, jn, idelH2, parity, 2 ); - if ( ret < 0 ) { - goto exit_function; - } - } else { - /* stereogenic sp3 atom */ - if ( at[n_vertex].num_H ) { - if ( 0 > (ret = AddExplicitDeletedH( at, n_vertex, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) { - goto exit_function; - } - } - ret = set_atom_0D_parity( at, st, num_atoms, nNumDeletedH, n_vertex, parity ); - if ( ret < 0 ) { - goto exit_function; - } - num_stereo_centers ++; - } - if ( ret < 0 ) { - goto exit_function; - } - } - } - if ( num_stereo_bonds2 ) { - /* In case of Fixed-H */ - /* mark additional Mobile-H stereobonds, they have already been connected the added explicit H, if any */ - for ( i = 0; i < pStereo2->nNumberOfStereoBonds; i ++ ) { - jv = pStereo2->nBondAtom1[i]-1; - jn = pStereo2->nBondAtom2[i]-1; - parity = pStereo2->b_parity[i]; - if ( !is_in_the_list( at[jv].neighbor, (AT_NUMB)jn, at[jv].valence ) ) { - /* must be a cumulene */ - if ( !bFindCumuleneChain( at, (AT_NUMB)jv, (AT_NUMB)jn, nCumulene, MAX_CUMULENE_LEN+1 ) ) { - return RI_ERR_SYNTAX; /* not a cumulene */ - } - len = MAX_CUMULENE_LEN+1; - } else { - /* a regular double or alt bond */ - nCumulene[0] = jv; - nCumulene[1] = jn; - len = 1; /* cumulene length is number of bonds, not number of atoms */ - } - /* cumulene or double bond: add explicit H if implicit H are present */ - if ( at[jv].num_H ) { - if ( 0 > (ret = AddExplicitDeletedH( at, jv, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) { - goto exit_function; - } - } else { - /* double bond neighbor that has the smallest canonical number */ - idelH1 = at[jv].neighbor[at[jv].neighbor[0]==nCumulene[1]]; - } - if ( at[jn].num_H ) { - if ( 0 > (ret = AddExplicitDeletedH( at, jn, num_atoms, &iDeletedH, &idelH2, nNumDeletedH, pStereo2 != NULL ))) { - goto exit_function; - } - } else { - idelH2 = at[jn].neighbor[at[jn].neighbor[0]==nCumulene[len-1]]; - } - if ( 0 > (ret = set_cumulene_0D_parity( at, st, num_atoms, idelH1, jv, jn, idelH2, parity, len )) ) { - goto exit_function; - } - } - - } - - - ret = num_atoms; - -exit_function: - return ret; -} -/*************************************************************/ -int SetStereoBondTypeFor0DParity( inp_ATOM *at, int i1, int m1 ) -{ - AT_NUMB nCumulene[MAX_CUMULENE_LEN+2]; - int j, n1, n2, k1, m2, ret, nLenCumulene = 0, bond_type; - k1 = at[i1].sb_ord[m1]; - n1 = i1; - nCumulene[nLenCumulene ++] = n1; - do { - n2 = at[n1].neighbor[k1]; /* next atom */ - nCumulene[nLenCumulene ++] = n2; - for (m2 = 0; m2 < MAX_NUM_STEREO_BONDS && at[n2].sb_parity[m2]; m2 ++ ) { - if ( n1 == at[n2].neighbor[(int)at[n2].sb_ord[m2]] ) { - /* found the endatom */ - goto found; - } - } - if ( at[n2].num_H || at[n2].valence != 2 || at[n2].endpoint ) { - break; /* not a middle cumulene */ - } - k1 = (at[n2].neighbor[0] == n1); - n1 = n2; - } while ( at[n1].valence == 2 && !at[n1].num_H && nLenCumulene < MAX_CUMULENE_LEN+2 && - bCanAtomBeMiddleAllene( at[n1].elname, at[n1].charge, at[n1].radical ) ); - return RI_ERR_SYNTAX; /* failed */ - -found: - if ( nLenCumulene == 2 ) { - bond_type = BOND_TYPE_STEREO; /* double bond or alternating bond */ - } else { - bond_type = BOND_TYPE_DOUBLE; /* cumulene or allene */ - } - - for ( j = 1; j < nLenCumulene; j ++ ) { - /* if bond_type = BOND_TYPE_DOUBLE then increments at->cham_bonds_valence: */ - /* at->cham_bonds_valence += BOND_TYPE_DOUBLE-BOND_TYPE_SINGLE */ - if ( 0 > (ret = set_bond_type( at, (AT_NUMB)nCumulene[j-1], (AT_NUMB)nCumulene[j], bond_type ) ) ) { - return RI_ERR_PROGR; /* failed */ - } - } - return nLenCumulene; -} -/******************************************************************************************************/ -int SetStereoBondTypesFrom0DStereo( StrFromINChI *pStruct, INChI *pInChI) -{ - INChI_Stereo *pStereo; - inp_ATOM *at = pStruct->at; - int num_atoms = pStruct->num_atoms; - int i, j, num_stereo_bonds, ret; - - if ( pInChI->StereoIsotopic && - (pInChI->StereoIsotopic->nNumberOfStereoBonds + - pInChI->StereoIsotopic->nNumberOfStereoCenters) ) { - pStereo = pInChI->StereoIsotopic; - } else - if ( pInChI->Stereo && - (pInChI->Stereo->nNumberOfStereoBonds + - pInChI->Stereo->nNumberOfStereoCenters) ) { - pStereo = pInChI->Stereo; - } else { - pStereo = NULL; - } - - /************************ set bond types separately from stereo *******************/ - if ( pStereo ) { - num_stereo_bonds = 0; - for ( i = 0; i < num_atoms; i ++ ) { - /* set BOND_TYPE_DOUBLE in allenes and cumulenes */ - /* set BOND_TYPE_STEREO in double bond stereo */ - for ( j = 0; j < MAX_NUM_STEREO_BONDS && at[i].sb_parity[j]; j ++ ) { - num_stereo_bonds ++; - if ( 0 > (ret = SetStereoBondTypeFor0DParity( at, i, j ) ) ) { - goto exit_function; - } - } - } - if ( num_stereo_bonds ) { - int num_bond_type_stereo; - int num_bond_type_altern; - AT_NUMB neigh; - /* replace adjacent BOND_TYPE_STEREO with BOND_TYPE_ALTERN */ - for ( i = 0; i < num_atoms; i ++ ) { - num_bond_type_stereo = 0; - num_bond_type_altern = 0; - for ( j = 0; j < at[i].valence; j ++ ) { - num_bond_type_stereo += ( at[i].bond_type[j] == BOND_TYPE_STEREO ); - num_bond_type_altern += ( at[i].bond_type[j] == BOND_TYPE_ALTERN ); - } - if ( num_bond_type_stereo + num_bond_type_altern > 1 && num_bond_type_stereo ) { - for ( j = 0; j < at[i].valence; j ++ ) { - if ( at[i].bond_type[j] == BOND_TYPE_STEREO ) { - neigh = at[i].neighbor[j]; - /* does not change at[i].chem_bond_valence in case of BOND_TYPE_ALTERN */ - if ( 0 > (ret = set_bond_type( at, (AT_NUMB)i, neigh, BOND_TYPE_ALTERN ) ) ) { - goto exit_function; - } - } - } - } - /* at this point only isolated stereo bonds have type BOND_TYPE_STEREO */ - } - /* increment at[i].chem_bonds_valence if at[i] has an altern. bond */ - /* replace BOND_TYPE_STEREO with BOND_TYPE_DOUBLE and increment */ - /* chem_bonds_valence of the adjacent atoms */ - for ( i = 0; i < num_atoms; i ++ ) { - num_bond_type_stereo = 0; - num_bond_type_altern = 0; - for ( j = 0; j < at[i].valence; j ++ ) { - num_bond_type_stereo += ( at[i].bond_type[j] == BOND_TYPE_STEREO ); - num_bond_type_altern += ( at[i].bond_type[j] == BOND_TYPE_ALTERN ); - } - if ( !num_bond_type_stereo && num_bond_type_altern ) { - /* an atom has only BOND_TYPE_ALTERN => adjacent BOND_TYPE_ALTERN case */ - at[i].chem_bonds_valence += 1; - } else - if ( num_bond_type_stereo == 1 ) { - /* isolated BOND_TYPE_STEREO => replace with BOND_TYPE_DOUBLE */ - for ( j = 0; j < at[i].valence; j ++ ) { - if ( at[i].bond_type[j] == BOND_TYPE_STEREO ) { - neigh = at[i].neighbor[j]; - /* replacing BOND_TYPE_STEREO with BOND_TYPE_DOUBLE */ - /* does not change at->chem_bonds_valence */ - if ( 0 > (ret = set_bond_type( at, (AT_NUMB)i, neigh, BOND_TYPE_DOUBLE ) ) ) { - goto exit_function; - } - at[i].chem_bonds_valence ++; - at[(int)neigh].chem_bonds_valence ++; - } - } - } else - if ( num_bond_type_stereo + num_bond_type_altern ) { - /* an atom still has both BOND_TYPE_STEREO and BOND_TYPE_ALTERN */ - ret = RI_ERR_PROGR; - goto exit_function; - } - } - INCHI_HEAPCHK - } - } - ret = 0; /* success */ -exit_function: - return ret; -} - -/******************************************************************************************************/ -int CopyBnsToAtom( StrFromINChI *pStruct, BN_STRUCT *pBNS, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int bAllowZeroBondOrder ) -{ - int i, j, atom_charge, left_charge, charge, ret = 0, v1, nMinorder; - int num_at = pStruct->num_atoms; - inp_ATOM *at = pStruct->at; - ICHICONST SRM *pSrm = pStruct->pSrm; - BNS_VERTEX *pv; - BNS_EDGE *pe; - int chem_bonds_valence, bond_order; - - atom_charge = left_charge = 0; - for ( i = 0; i < num_at; i ++ ) { - pv = pBNS->vert + i; - /* bonds */ - chem_bonds_valence = 0; - for ( j = 0; j < at[i].valence; j ++ ) { - pe = pBNS->edge + pv->iedge[j]; - BondFlowMaxcapMinorder( at, pVA, pSrm, i, j, NULL, &nMinorder, NULL ); - bond_order = pe->flow + nMinorder; - if ( !bAllowZeroBondOrder && !bond_order ) { - bond_order = 1; - } - chem_bonds_valence += bond_order; - at[i].bond_type[j] = bond_order; /* BOND_MARK_HIGHLIGHT */ - } - at[i].chem_bonds_valence = chem_bonds_valence; - /* charges (both may be present resulting in zero) */ - at[i].charge = pVA[i].cInitCharge; - if ( pVA[i].nCMinusGroupEdge ) { - pe = pBNS->edge + pVA[i].nCMinusGroupEdge - 1; - if ( charge = pe->flow ) { - at[i].charge -= charge; - atom_charge -= charge; - } - } - if ( pVA[i].nCPlusGroupEdge ) { - pe = pBNS->edge + pVA[i].nCPlusGroupEdge - 1; - if ( charge = pe->cap - pe->flow ) { - at[i].charge += charge; - atom_charge += charge; - } - } - if ( pv->st_edge.cap > pv->st_edge.flow ) { - at[i].radical = RADICAL_SINGLET + (pv->st_edge.cap - pv->st_edge.flow); - } - } - /* find charge excess */ - for ( i = num_at; i < pBNS->num_vertices; i ++ ) { - pv = pBNS->vert + i; - if ( charge = pv->st_edge.cap - pv->st_edge.flow ) { - if ( IS_BNS_VT_C_OR_CSUPER_GR(pv->type) ) { - left_charge -= charge; - } else - if ( IS_BNS_VT_YVCONNECTOR(pv->type) ) { - left_charge += charge; - } - } - } - /* tautomeric H and (-) */ - for ( i = 0; i < pBNS->num_t_groups; i ++ ) { - /* tautomeric groups are first non-atom vertices; - order of them is same as in pTCGroups->pTCG[] */ - int num_H = pTCGroups->pTCG[i].tg_num_H; - int num_Minus = pTCGroups->pTCG[i].tg_num_Minus; - int bMinusFirst = (pTCGroups->pTCG[i].tg_RestoreFlags & TGRF_MINUS_FIRST); - int num_at_add; - Vertex vMinus = NO_VERTEX; - pv = pBNS->vert + num_at + i; /* t-group vertex */ - if ( !(pv->type & BNS_VERT_TYPE_TGROUP) ) { - return RI_ERR_PROGR; - } - if ( pTCGroups->pTCG[i].tg_set_Minus > 0 && num_Minus > 0 ) { - vMinus = pTCGroups->pTCG[i].tg_set_Minus-1; - num_Minus --; - } - - if ( bMinusFirst ) { - for ( j = 0; j < pv->num_adj_edges; j ++ ) { - pe = pBNS->edge + pv->iedge[j]; - v1 = pe->neighbor1; - num_at_add = pe->flow; - if ( v1 == vMinus ) { - if ( num_at_add ) { - at[v1].charge = -1; /* no checking at[v1].charge == 0 for now ??? */ - num_at_add --; /* no checking num_at_add > 0 for now ??? */ - } else { - num_Minus ++; /* error ??? */ - } - vMinus = NO_VERTEX; - } - if ( num_at_add > 0 ) { - /* atom has tautomeric attachment; do not allow =N(-) */ - if ( num_Minus && !at[v1].charge && - at[v1].valence == at[v1].chem_bonds_valence ) { - at[v1].charge --; - num_at_add --; - num_Minus --; - } - if ( num_at_add > 0 ) { - at[v1].num_H += num_at_add; - num_H -= num_at_add; - num_at_add = 0; - } - } - at[v1].endpoint = i+1; - } - if ( (num_H+num_Minus != pv->st_edge.cap - pv->st_edge.flow) && (num_H || num_Minus || vMinus != NO_VERTEX) ) { - return RI_ERR_PROGR; - } - } else { - for ( j = pv->num_adj_edges-1; 0 <= j; j -- ) { - pe = pBNS->edge + pv->iedge[j]; - v1 = pe->neighbor1; - num_at_add = pe->flow; - if ( v1 == vMinus ) { - if ( num_at_add ) { - at[v1].charge = -1; /* no checking at[v1].charge == 0 for now ??? */ - num_at_add --; /* no checking num_at_add > 0 for now ??? */ - } else { - num_Minus ++; /* error ??? */ - } - vMinus = NO_VERTEX; - } - if ( num_at_add > 0 ) { - /* atom has tautomeric attachment; do not allow =N(-) */ - if ( num_Minus && !at[v1].charge && - at[v1].valence == at[v1].chem_bonds_valence ) { - at[v1].charge --; - num_at_add --; - num_Minus --; - } - if ( num_at_add > 0 ) { - at[v1].num_H += num_at_add; - num_H -= num_at_add; - num_at_add = 0; - } - } - at[v1].endpoint = i+1; - } - if ( (num_H+num_Minus != pv->st_edge.cap - pv->st_edge.flow) && (num_H || num_Minus || vMinus != NO_VERTEX) ) { - return RI_ERR_PROGR; - } - } - } - - return ret; -} -/******************************************************************************************************/ -int CheckBnsConsistency( StrFromINChI *pStruct, BN_STRUCT *pBNS, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int bNoRad ) -{ - int nOutput = 0; -#ifndef TARGET_API_LIB -#if ( bRELEASE_VERSION == 0 ) - char s[128]; - int i, j, atom_charge, left_charge, charge, excess_charge, ret = 0; - int v1, v2, flow, tot_st_flow, tot_st_cap, num_electrons, nNumMetalAtoms; - int num_at = pStruct->num_atoms; - inp_ATOM *at = pStruct->at; - BNS_VERTEX *pv; - BNS_EDGE *pe; -#ifdef _DEBUG - int bDebugOutput = 0; - bNoRad = 1; - bDebugOutput = 1; -#endif - /* count electrons and metals */ - num_electrons = -pTCGroups->total_charge; - nNumMetalAtoms = 0; - for ( i = 0; i < pTCGroups->num_tgroups; i ++ ) { - num_electrons += pTCGroups->pTCG[i].tg_num_H; - } - for ( i = 0; i < num_at; i ++ ) { - num_electrons += at[i].el_number + at[i].num_H; - nNumMetalAtoms += pVA[i].cMetal; - } - /* create output string */ - sprintf( s, "%d:%d%sM%02dv%da%de%db%d* ", - bNoRad, pTCGroups->iComponent+1, num_electrons%2?"O":"E", nNumMetalAtoms, - pBNS->num_vertices, num_at, pBNS->num_edges, pBNS->num_bonds ); - - - tot_st_flow = tot_st_cap = 0; - atom_charge = left_charge = 0; - if ( pBNS->num_atoms != num_at ) { - fprintf( stdout, "\n%sNum. atoms discrepancy: %d(BNS) vs. %d(at) ", s, pBNS->num_atoms, num_at); - nOutput ++; - } - /* check edges */ -#ifdef _DEBUG - if ( bDebugOutput && bNoRad ) { - fprintf( stderr, "\n\n------begin------------------------------------------------\n" ); - fprintf( stderr, "\n\fedge cap flow v1 v2\n\n" ); - /* xxxx xxxx/xxxx xxxx/xxxx xxxx xxxx */ - } -#endif - for ( i = 0; i < pBNS->num_edges; i ++ ) { - pe = pBNS->edge + i; - v1 = pe->neighbor1; - v2 = v1 ^ pe->neighbor12; - if ( pe->cap < pe->flow || pe->flow < 0 ) { - fprintf( stdout, "\n%sedge %d (%d-%d) has cap=%d flow=%d ", s, i, v1, v2, pe->cap, pe->flow ); - nOutput ++; - } -#ifdef _DEBUG - if ( bDebugOutput && bNoRad ) { - /* xxxx xxxx/xxxx xxxx/xxxx xxxx xxxx */ - fprintf( stderr, "%4d %4d/%-4d %4d/%-4d %4d %4d\n", i, pe->cap, pe->cap0, pe->flow, pe->flow0, v1, v2 ); - } -#endif - } - - - /* check vertices */ -#ifdef _DEBUG - if ( bDebugOutput && bNoRad ) { - fprintf( stderr, "\n\fvert st-cap st-flow type iedge : neigh\n\n" ); - /* xxxx xxxx/xxxx xxxx/xxxx 0xXXX xxxx : xxx */ - } -#endif - for ( i = 0; i < pBNS->num_vertices; i ++ ) { - pv = pBNS->vert + i; -#ifdef _DEBUG - if ( bDebugOutput && bNoRad ) { - /* xxxx xxxx/xxxx xxxx/xxxx 0xXXX xxxx : xxx */ - int j; - const char *s; - char sAtom[6]; - switch( pv->type ) { - case BNS_VERT_TYPE_ATOM: - sprintf( sAtom, "At %-2.2s", i < num_at? at[i].elname : "??" ); - s = sAtom; - break; - case BNS_VERT_TYPE_ATOM | BNS_VERT_TYPE_ENDPOINT: - s = "Endpt"; - break; - case BNS_VT_C_POS: - s = "(+) "; - break; - case BNS_VT_C_NEG: - s = "(-) "; - break; - case BNS_VT_C_POS_C: - s = "(+C) "; - break; - case BNS_VT_C_NEG_C: - s = "(-C) "; - break; - case BNS_VT_C_POS_M: - s = "(+M) "; - break; - case BNS_VT_C_NEG_M: - s = "(-M) "; - break; - case BNS_VT_C_POS_ALL: - s = "(+)Sg"; - break; - case BNS_VT_C_NEG_ALL: - s = "(-)Sg"; - break; - case BNS_VT_M_GROUP: - s = "M-grp"; - break; - case BNS_VERT_TYPE__AUX | BNS_VERT_TYPE_TEMP: - s = "ChStr"; - break; - case BNS_VERT_TYPE__AUX: - s = "Yconn"; - break; - case BNS_VERT_TYPE_TGROUP: - s = "T-grp"; - break; - default: - s = "Unkn."; - break; - } - fprintf( stderr, "%4d %4d/%-4d %4d/%-4d 0x%03X %5s", - i, pv->st_edge.cap, pv->st_edge.cap0, pv->st_edge.flow, pv->st_edge.flow0, - pv->type, s ); - for ( j = 0; j < pv->num_adj_edges; j ++ ) { - fprintf( stderr, " %2d", pv->iedge[j] ); - } - fprintf( stderr, ":" ); - for ( j = 0; j < pv->num_adj_edges; j ++ ) { - pe = pBNS->edge + pv->iedge[j]; - fprintf( stderr, " %2d", pe->neighbor12 ^ i ); - } - fprintf( stderr, "\n" ); - } -#endif - tot_st_flow += pv->st_edge.flow; - tot_st_cap += pv->st_edge.cap; - if ( pv->num_adj_edges > pv->max_adj_edges ) { - fprintf( stdout, "\n%s%s %d type 0x%X \"%s\" num_edges=%d > max=%d ", s, - i < num_at? "atom":"vertex", i, - pv->type, at[i].elname, pv->num_adj_edges, pv->max_adj_edges ); - nOutput ++; - } - if ( i < num_at ) { - /* charge on atoms */ - charge = pVA[i].cInitCharge; - if ( pVA[i].nCMinusGroupEdge ) { - pe = pBNS->edge + pVA[i].nCMinusGroupEdge - 1; - if ( pe->flow > 0 ) { - charge -= pe->flow; - } - } - if ( pVA[i].nCPlusGroupEdge ) { - pe = pBNS->edge + pVA[i].nCPlusGroupEdge - 1; - if ( pe->cap > pe->flow ) { - charge += pe->cap - pe->flow; - } - } - if ( bNoRad && pv->st_edge.flow != pv->st_edge.cap ) { - fprintf( stdout, "\n%s%s %d: type 0x%X \"%s\" unexpected st_cap=%d st_flow=%d ", s, - i < num_at? "atom":"vertex", i, - pv->type, at[i].elname, pv->st_edge.cap, pv->st_edge.flow ); - nOutput ++; - } else - if ( bNoRad && charge && !strcmp(at[i].elname, "C") ) { - /* ignore carbonyls */ - if ( i == 0 && num_at == 2 && !strcmp(at[1].elname, "O") && - !at[0].num_H && !at[1].num_H && !pTCGroups->total_charge) { - ; /* C(-)#O(+) structure */ - } else { - fprintf( stdout, "\n%s%s %d: type 0x%X \"%s\" charge=%d ", s, - i < num_at? "atom":"vertex", i, - pv->type, at[i].elname, charge ); - nOutput ++; - } - } - atom_charge += charge; - } else - if ( (charge = pv->st_edge.cap - pv->st_edge.flow) > 0 ) { - /* excess charge */ - if ( !bNoRad && IS_BNS_VT_C_OR_CSUPER_GR(pv->type) ) { - left_charge -= charge; - } else - if ( !bNoRad && IS_BNS_VT_YVCONNECTOR(pv->type) ) { - left_charge += charge; - } else - if ( !bNoRad && IS_BNS_VT_M_GR(pv->type) && - 0 <= (j=pTCGroups->nGroup[TCG_MeFlower3]) && - i == pTCGroups->pTCG[j].nVertexNumber ) { - ; /* additional "radical" on metal flower */ - } else - if ( !(pv->type & BNS_VERT_TYPE_TGROUP) || bNoRad ) { - /* t-groups before running BFS should have st_cap > st_flow */ - fprintf( stdout, "\n%s%s %d: type 0x%X unexpected st_cap=%d st_flow=%d ", s, - i < num_at? "atom":"vertex", i, - pv->type, pv->st_edge.cap, pv->st_edge.flow); - nOutput ++; - } - } - if ( pv->st_edge.cap < pv->st_edge.flow || pv->st_edge.flow < 0 ) { - fprintf( stdout, "\n%s%s %d: type 0x%X \"%s\" st_cap=%d st_flow=%d ", s, - i < num_at? "atom":"vertex", i, - pv->type, i < num_at? at[i].elname:"", pv->st_edge.cap, pv->st_edge.flow ); - nOutput ++; - } - /* check edge_flow vs. st_flow consistency */ - for( j = 0, flow = 0; j < pv->num_adj_edges; j ++ ) { - pe = pBNS->edge + pv->iedge[j]; - flow += pe->flow; - } - if ( flow != pv->st_edge.flow ) { - fprintf( stdout, "\n%s%s %d: type 0x%X \"%s\" st_flow=%d edge_flow=%d ", s, - i < num_at? "atom":"vertex", i, - pv->type, i < num_at? at[i].elname:"", pv->st_edge.flow, flow ); - nOutput ++; - } - } -#ifdef _DEBUG - if ( bDebugOutput && bNoRad ) { - fprintf( stderr, "\n------end--------------------------------------------------\n" ); - } -#endif - /* - if ( num_electrons %= 2 ) { - fprintf( stdout, "\n%d*Odd number of electrons (%d atoms) ", bNoRad, num_at ); - nOutput ++; - } - */ - /* tautomeric groups charge */ - for ( i = 0, charge = 0; i < pTCGroups->num_tgroups; i ++ ) { - charge -= pTCGroups->pTCG[i].tg_num_Minus; - } - /* compare */ - if ( charge != pTCGroups->tgroup_charge ) { - fprintf( stdout, "\n%sCounted t-group charge=%d while %d was saved ", s, - charge, pTCGroups->tgroup_charge); - nOutput ++; - } - /* add other charges */ - charge += atom_charge + left_charge; - excess_charge = pTCGroups->total_charge - pTCGroups->added_charge - pTCGroups->tgroup_charge; - if ( charge != pTCGroups->total_charge && excess_charge != pTCGroups->total_charge - charge ) { - fprintf( stdout, "\n%sCounted total charge=%d while %d was saved; excess charge=%d ", s, - charge, pTCGroups->total_charge, excess_charge ); - nOutput ++; - } - if ( tot_st_cap != pBNS->tot_st_cap || tot_st_flow != pBNS->tot_st_flow ) { - fprintf( stdout, "\n%sCounted/saved total st_flow=%d/%d st_cap=%d/%d ", s, - tot_st_flow, pBNS->tot_st_flow, tot_st_cap, pBNS->tot_st_cap ); - nOutput ++; - } - if ( nOutput ) { - fprintf( stdout, "\n" ); - } -#endif -#endif - return nOutput; -} - -/******************************************************************************************************/ -int AddExplicitDeletedH( inp_ATOM *at, int jv, int num_at, int *iDeletedH, int *iH, int nNumDeletedH, int bTwoStereo ) -{ - inp_ATOM *cur_H, *cur_at = at+jv; - int tot_num_iso_H = NUM_ISO_H(cur_at, 0); - int num_H = cur_at->num_H; - int iso_H = 0; - S_CHAR num_iso_H[NUM_H_ISOTOPES]; - int i; - - if ( !at[jv].at_type ) { - return RI_ERR_PROGR; - } - - if ( at[jv].at_type > 1 ) { - /* explicit hydrogens have already been added; find them */ - for ( i = 0; i < *iDeletedH; i ++ ) { - if ( at[num_at + i].neighbor[0] == jv ) { - *iH = num_at + i; /* return the first found H, it has the smallest canonical pseudo rank */ - return 0; - } - } - return RI_ERR_PROGR; - } - /* add all explicit H disconnected from at[jv] in order H, 1H, D, T */ - *iH = *iDeletedH + num_at; /* num_H includes all H, both isotopic and normal */ - for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { - num_iso_H[i] = at[jv].num_iso_H[i]; - } - for ( ; num_H && (*iDeletedH) < nNumDeletedH; (*iDeletedH) ++ ) { - cur_H = at + num_at + (*iDeletedH); /* first available empty atom will be this explicit H */ - cur_H->neighbor[cur_H->valence] = jv; /* connect this new atom H to the real atom */ - cur_H->bond_type[cur_H->valence] = BOND_TYPE_SINGLE; - cur_H->valence ++; - if ( num_H > tot_num_iso_H ) { - num_H --; - if ( num_H != tot_num_iso_H ) { - /* may happen when Mobile-H stereo included in Fixed-H processing */ - if ( bTwoStereo ) { - continue; - } else { - return RI_ERR_SYNTAX; /* two identical H neighbors of a stereo atom/bond */ - } - } - } else { - while ( iso_H < NUM_H_ISOTOPES && !num_iso_H[iso_H] ) - iso_H ++; - if ( iso_H < NUM_H_ISOTOPES ) { - cur_H->iso_atw_diff = iso_H + 1; /* isotopic shift + 1 */ - num_H --; - tot_num_iso_H --; - num_iso_H[iso_H] --; - if ( num_iso_H[iso_H] ) { - return RI_ERR_SYNTAX; /* two identical isotopic H neighbors of a stereo atom/bond */ - } - } else { - return RI_ERR_SYNTAX; /* not enough isotopic H */ - } - } - } - if ( num_H ) { - return RI_ERR_SYNTAX; - } - at[jv].at_type ++; /* at[jv].at_type==2 => explicit hydrogens have already been added */ - return 0; /* success */ -} -/******************************************************************************************************/ -int bFindCumuleneChain( inp_ATOM *at, AT_NUMB i1, AT_NUMB i2, AT_NUMB nCumulene[], int nMaxLen ) -/* nMaxLen = number of bonds in cumulene = 3 = MAX_CUMULENE_LEN+1 */ -/* nCumulene[nMaxLen+1] will contain cumulene chain >i1=x=y=i2< in this order */ -{ - int i, len, iat, nat; - nCumulene[0] = i1; - for ( i = 0; i < at[i1].valence; i ++ ) { - len = 0; - iat = i1; /* current */ - nat = at[i1].neighbor[i]; /* next */ - if ( len+1 == nMaxLen ) { - if ( nat == i2 ) { - nCumulene[++len] = nat; - return 1; /* success */ - } - continue; /* check next at[i1] neighbor */ - } - while ( at[nat].valence == 2 && - at[nat].num_H == 0 && - bCanAtomBeMiddleAllene(at[nat].elname, 0, 0) ) { - nCumulene[++len] = nat; - nat = at[nat].neighbor[at[nat].neighbor[0]==iat]; /* new next */ - if ( len+1 == nMaxLen ) { - if ( nat == i2 ) { - nCumulene[++len] = nat; - return 1; /* success */ - } - break; /* check next at[i1] neighbor */ - } - iat = nCumulene[len]; /* new current */ - } - } - return 0; /* failed */ -} -/******************************************************************************************************/ -int set_bond_type( inp_ATOM *at, AT_NUMB i1, AT_NUMB i2, int bType ) -{ - AT_NUMB *p1 = is_in_the_list( at[i1].neighbor, i2, at[i1].valence ); - AT_NUMB *p2 = is_in_the_list( at[i2].neighbor, i1, at[i2].valence ); - if ( p1 && p2 ) { - int j1 = p1 - at[i1].neighbor; - int j2 = p2 - at[i2].neighbor; - int bTypePrev = at[i1].bond_type[j1]; - at[i1].bond_type[j1] = bType; - at[i2].bond_type[j2] = bType; - if ( bTypePrev && bTypePrev <= BOND_TYPE_TRIPLE && - bType && bType <= BOND_TYPE_TRIPLE ) { - at[i1].chem_bonds_valence += bType - bTypePrev; - at[i2].chem_bonds_valence += bType - bTypePrev; - } - return 0; - } - return RI_ERR_SYNTAX; -} -/******************************************************************************************************/ -int set_cumulene_0D_parity( inp_ATOM *at, inp_ATOM_STEREO *st, int num_at, int idelH1, int i1, int i2, int idelH2, int parity, int len ) -{ - AT_NUMB nCumulene[MAX_CUMULENE_LEN+2]; - AT_NUMB *p1, *p2; - int m1, m2, parity1, parity2, sb_ord_m1, sb_ord_m2, k1, k2, num_neigh1, num_neigh2; - /* the following types must exactly match types in inp_ATOM and inp_ATOM_STEREO */ - S_CHAR *sb_ord1, *sn_ord1, *sb_parity1; - S_CHAR *sb_ord2, *sn_ord2, *sb_parity2; - AT_NUMB *sn_orig_at_num1; - AT_NUMB *sn_orig_at_num2; - - - if ( !bFindCumuleneChain( at, (AT_NUMB)i1, (AT_NUMB)i2, nCumulene, len ) ) { - return RI_ERR_SYNTAX; /* not an allene */ - } - /* stereo bond neighbors: index of a stereo bond in its end-atom adjacency lists */ - if ( (p1 = is_in_the_list( at[i1].neighbor, nCumulene[1], at[i1].valence )) && - (p2 = is_in_the_list( at[i2].neighbor, nCumulene[len-1], at[i2].valence )) ) { - sb_ord_m1 = p1 - at[i1].neighbor; /* indes of stereobond in the atom's adjacency list */ - sb_ord_m2 = p2 - at[i2].neighbor; - } else { - return RI_ERR_PROGR; - } - num_neigh1 = at[i1].valence + at[i1].num_H; - num_neigh2 = at[i2].valence + at[i2].num_H; - - if ( num_neigh1 < MIN_NUM_STEREO_BOND_NEIGH || num_neigh1 > MAX_NUM_STEREO_BOND_NEIGH || - num_neigh2 < MIN_NUM_STEREO_BOND_NEIGH || num_neigh2 > MAX_NUM_STEREO_BOND_NEIGH ) { - return RI_ERR_SYNTAX; - } - - - sb_ord1 = st? st[i1].sb_ord : at[i1].sb_ord; - sb_ord2 = st? st[i2].sb_ord : at[i2].sb_ord; - sb_parity1 = st? st[i1].sb_parity : at[i1].sb_parity; - sb_parity2 = st? st[i2].sb_parity : at[i2].sb_parity; - - /* find the first unoccupied locations in the stereobond 0D descriptor lists; check whether the stereo has already been set */ - for( m1 = k1 = 0; m1 < MAX_NUM_STEREO_BONDS && sb_parity1[m1] && !(k1 = sb_ord1[m1] == sb_ord_m1); m1 ++ ) - ; - for( m2 = k2 = 0; m2 < MAX_NUM_STEREO_BONDS && sb_parity2[m2] && !(k2 = sb_ord2[m2] == sb_ord_m2); m2 ++ ) - ; - if ( m1 == MAX_NUM_STEREO_BONDS || m2 == MAX_NUM_STEREO_BONDS ) { - return RI_ERR_SYNTAX; - } - if ( k1 && k2 ) { - return 0; /* the stereo descriptor of this bond/allene/cumulene has already been set */ - } - if ( k1 || k2 ) { - return RI_ERR_SYNTAX; /* only half of a bond was set */ - } - - sn_ord1 = st? st[i1].sn_ord : at[i1].sn_ord; - sn_ord2 = st? st[i2].sn_ord : at[i2].sn_ord; - sn_orig_at_num1 = st? st[i1].sn_orig_at_num : at[i1].sn_orig_at_num; - sn_orig_at_num2 = st? st[i2].sn_orig_at_num : at[i2].sn_orig_at_num; - - /* stereo bond neighbors connection index */ - sb_ord1[m1] = sb_ord_m1; - sb_ord2[m2] = sb_ord_m2; - /* stereo bond end atom neighbors */ - sn_orig_at_num1[m1] = at[idelH1].orig_at_number; - if ( idelH1 < num_at ) { - if ( p1 = is_in_the_list( at[i1].neighbor, (AT_NUMB)idelH1, at[i1].valence ) ) { - sn_ord1[m1] = p1 - at[i1].neighbor; - } else { - return RI_ERR_PROGR; - } - } else { - sn_ord1[m1] = -1; - } - - sn_orig_at_num2[m2] = at[idelH2].orig_at_number; - if ( idelH2 < num_at ) { - if ( p2 = is_in_the_list( at[i2].neighbor, (AT_NUMB)idelH2, at[i2].valence ) ) { - sn_ord2[m2] = p2 - at[i2].neighbor; - } else { - return RI_ERR_PROGR; - } - } else { - sn_ord2[m2] = -1; - } - if ( ATOM_PARITY_WELL_DEF(parity) ) { - /* special case: 2 bonds to sb atom => inverse parity because */ - /* InChI parity refers to the lone pair as a neighbor */ - int num_inv = (num_neigh1 == MIN_NUM_STEREO_BOND_NEIGH) + (num_neigh2 == MIN_NUM_STEREO_BOND_NEIGH); - if ( num_inv % 2 ) { - parity = (parity == AB_PARITY_EVEN)? AB_PARITY_ODD : AB_PARITY_EVEN; - } - parity1 = AB_PARITY_EVEN; - parity2 = (parity == AB_PARITY_EVEN)? AB_PARITY_EVEN : AB_PARITY_ODD; - } else { - parity1 = parity2 = parity; - } - sb_parity1[m1] = parity1; - sb_parity2[m2] = parity2; - - return 0; -} -/******************************************************************************************************/ -int set_atom_0D_parity( inp_ATOM *at, inp_ATOM_STEREO *st, int num_at, int num_deleted_H, int i1, int parity ) -{ - int m1=0, m2, i, j, tot_num_neigh; - /* the following types must exactly match types in inp_ATOM and inp_ATOM_STEREO */ - /* Given parity from InChI, the order of stereo center neighbors is: */ - /* 1. The stereocenter itself if the total number of neighbors is 3 (not 4) */ - /* 2. Explicit H: non-isotopic, isotopic in order ofascending atomic mass */ - /* Explicit H have already been sorted in this order */ - /* 3. Normal neighboring atoms, atom numbers (=canonical numbers from InChI - 1) in ascending order */ - /* Normal neighboring atoms have already been sorted in this order */ - S_CHAR *p_parity; - AT_NUMB *p_orig_at_num; - - if ( !st || !at[i1].p_parity ) { - m1 = 0; - p_parity = st? &st[i1].p_parity : &at[i1].p_parity; - p_orig_at_num = st? st[i1].p_orig_at_num : at[i1].p_orig_at_num; - - tot_num_neigh = at[i1].valence + at[i1].num_H; - if ( tot_num_neigh == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { - /* only 3 neighbors: the atom itself is the first neighbor */ - p_orig_at_num[m1 ++] = at[i1].orig_at_number; - } else - if ( tot_num_neigh != MAX_NUM_STEREO_ATOM_NEIGH ) { - return RI_ERR_PROGR; /* wrong number of members */ - } - m2 = m1 + (MAX_NUM_STEREO_ATOM_NEIGH - at[i1].valence); - /* stereoneighbors: deleted explicit atoms H first, in order of increasing isotopic mass */ - if ( at[i1].num_H ) { - for ( j = 0; m1 < m2 && j < num_deleted_H; j ++ ) { - if ( at[j + num_at].neighbor[0] == i1 ) { - p_orig_at_num[m1 ++] = at[j + num_at].orig_at_number; - } - } - } - if ( m1 + at[i1].valence != MAX_NUM_STEREO_ATOM_NEIGH ) { - return RI_ERR_PROGR; /* wrong number of members */ - } - /* stereoneighbors: other than explicit H atoms */ - for ( i = 0; i < at[i1].valence; i ++ ) { - m2 = at[i1].neighbor[i]; - p_orig_at_num[m1 ++] = at[m2].orig_at_number; - } - *p_parity = parity; - } - - return 0; -} -#if ( BNS_RAD_SEARCH == 1 ) -/******************************************************************************************************/ -int MoveRadToAtomsAddCharges( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int forbidden_mask ) -{ - int nNumRad, ret = 0, ret2; - int i, j, k, num_rad_not_atom, num_moved=0, num_candidates= 0, extra_charge=0, added_charge, delta; - BNS_EDGE *pEdge; - BNS_VERTEX *pv; - Vertex v1, v2; - S_SHORT *pnRad = NULL, *pnDelta = NULL; - CC_CAND *pCand = NULL; - int cnBits, bAtomRadRemoved = 0; - - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - - for ( i = pBNS->num_atoms, num_rad_not_atom=0; i < pBNS->num_vertices; i ++ ) { - num_rad_not_atom += pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow; - } - if ( !num_rad_not_atom ) { - goto exit_function; - } - /****************************************************/ - /* */ - /* Move radicals from ChargeStruct to atoms */ - /* */ - /****************************************************/ - - /* allocate memory to keep track of moved radicals */ - pnRad = (S_SHORT *) inchi_malloc(pBNS->num_vertices * sizeof(pnRad[0])); - pnDelta = (S_SHORT *)inchi_calloc(pBNS->num_atoms, sizeof(pnDelta[0])); - if ( !pnRad || !pnDelta ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - for ( i = 0; i < pBNS->num_vertices; i ++ ) { - pnRad[i] = pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow; - } - while( 1 ) { - /* remove radicals from atoms */ - for ( i = 0; i < pBNS->num_atoms; i ++ ) { - pnDelta[i] = pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow; - pBNS->vert[i].st_edge.cap -= pnDelta[i]; - bAtomRadRemoved += (0 != pnDelta[i]); - } - ret = SetRadEndpoints( pBNS, pBD, RAD_SRCH_FROM_FICT ); - if ( !ret ) { - break; - } - if ( ret < 0 ) { - goto exit_function; - } - nNumRad = ret; - for ( i = 0; i < nNumRad; i ++ ) { - pEdge = pBNS->edge + pBD->RadEdges[i]; - v1 = pEdge->neighbor1; - v2 = pEdge->neighbor12 ^ v1; - pBNS->vert[v1].st_edge.flow -= pEdge->flow; - pBNS->vert[v2].st_edge.flow -= pEdge->flow; - pBNS->tot_st_flow -= 2*pEdge->flow; - pEdge->flow = 0; - pEdge->forbidden |= forbidden_mask; - pBNS->edge_forbidden_mask |= forbidden_mask; - } - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret < 0 ) { - goto exit_function; - } else { - num_moved += ret; - } - RemoveRadEndpoints( pBNS, pBD, NULL ); - if ( ret == 0 ) { - break; /* could not move more radicals */ - } - if ( bAtomRadRemoved ) { - /* restore radicals to atoms */ - for ( i = 0; i < pBNS->num_atoms; i ++ ) { - pBNS->vert[i].st_edge.cap += pnDelta[i]; - } - bAtomRadRemoved = 0; - } - } - if ( bAtomRadRemoved ) { - /* restore radicals to atoms */ - for ( i = 0; i < pBNS->num_atoms; i ++ ) { - pBNS->vert[i].st_edge.cap += pnDelta[i]; - } - bAtomRadRemoved = 0; - } - pBNS->edge_forbidden_mask &= ~forbidden_mask; - - - /****************************************************/ - /* */ - /* Fix the charges */ - /* */ - /****************************************************/ - if ( num_moved ) { - /* find reqired charge */ - extra_charge = 0; - for ( i = pBNS->num_atoms, pv=pBNS->vert+i; i < pBNS->num_vertices; i ++, pv++ ) { - if ( delta = pv->st_edge.cap - pv->st_edge.flow ) { - if ( IS_BNS_VT_C_OR_CSUPER_GR(pv->type) ) { - extra_charge -= delta; - } else - if ( BNS_VERT_TYPE__AUX == pv->type ) { - extra_charge += delta; - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - } - } - if ( !extra_charge ) { - goto exit_function; - } - /* find differences */ - num_candidates = 0; - for ( i = 0; i < pBNS->num_vertices; i ++ ) { - pnRad[i] = (pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow) - pnRad[i]; - if ( pnRad[i] > 0 && i < pBNS->num_atoms && !pVA[i].nTautGroupEdge ) { - num_candidates ++; - } - } - } - if ( num_candidates > 0 ) { - pCand = (CC_CAND *)inchi_calloc( num_candidates, sizeof(pCand[0]) ); - if ( !pCand ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - /* create atom */ - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - - for ( i = 0, j = 0; i < pBNS->num_vertices; i ++ ) { - if ( pnRad[i] > 0 && i < pBNS->num_atoms && !pVA[i].nTautGroupEdge ) { - pCand[j].iat = i; - pCand[j].num_bonds = at2[i].valence; - pCand[j].chem_valence = at2[i].chem_bonds_valence; - pCand[j].cMetal = pVA[i].cMetal; - pCand[j].cNumBondsToMetal = pVA[i].cNumBondsToMetal; - pCand[j].cNumValenceElectrons = pVA[i].cNumValenceElectrons; - pCand[j].cPeriodicRowNumber = pVA[i].cPeriodicRowNumber; - pCand[j].el_number = at2[i].el_number; - cnBits = (pVA[i].cnListIndex > 0)? cnList[pVA[i].cnListIndex-1].bits : 0; - while ( cnBits > 0 ) { - pCand[j].cNumChargeStates ++; - cnBits >>= cn_bits_shift; - } - j ++; - } - } - if ( j > 1 ) { - qsort( pCand, j, sizeof(pCand[0]), comp_cc_cand ); - } - added_charge = 0; - - for ( k = 0; k < j; k ++ ) { - int rest_of_charge = extra_charge - added_charge; - int charge_per_left_atom = (abs(rest_of_charge) + j-k - 1)/(j-k); - int this_atom_add_charge = rest_of_charge > 0? charge_per_left_atom : -charge_per_left_atom; - pVA[pCand[k].iat].cInitCharge += this_atom_add_charge; - added_charge += this_atom_add_charge; - if ( this_atom_add_charge ) { - for ( i = pBNS->num_vertices-1, pv = pBNS->vert + i; this_atom_add_charge && pBNS->num_atoms <= i; i --, pv -- ) { - if ( delta = pv->st_edge.cap - pv->st_edge.flow ) { - if ( this_atom_add_charge < 0 && IS_BNS_VT_C_OR_CSUPER_GR(pv->type) ) { - if ( delta + this_atom_add_charge > 0 ) { - delta = -this_atom_add_charge; - } - pv->st_edge.cap -= delta; - pBNS->tot_st_cap -= delta; - this_atom_add_charge += delta; - } else - if ( this_atom_add_charge > 0 && BNS_VERT_TYPE__AUX == pv->type ) { - if ( delta > this_atom_add_charge ) { - delta = this_atom_add_charge; - } - pv->st_edge.cap -= delta; - pBNS->tot_st_cap -= delta; - this_atom_add_charge -= delta; - } - } - } - } - } - } - -exit_function: - if ( pnRad ) { - inchi_free( pnRad ); - } - if ( pnDelta ) { - inchi_free( pnDelta ); - } - if ( pCand ) { - inchi_free( pCand ); - } - return ret; -} -#endif -/**************************************************************************************************/ -typedef struct tagMobileHGroups { - AT_NUMB group_number; - AT_NUMB atom_number; - AT_NUMB atom_type_pVA; - S_CHAR ineigh; - S_CHAR bond_type; - S_CHAR forbidden; - /* S_CHAR el_type;*/ - S_CHAR endpoint_valence; - S_CHAR num_bonds; - S_CHAR bonds_valence; - S_CHAR num_bonds_non_metal; - S_CHAR bonds_valence_non_metal; - -} MOBILE_GR; - -typedef struct tagMobileGroupList { - AT_NUMB group_number; - AT_NUMB num; -} MGROUPS; -/**************************************************************************************************/ -int AdjustTgroupsToForbiddenEdges2( BN_STRUCT *pBNS, inp_ATOM *at, VAL_AT *pVA, - int num_atoms, int forbidden_mask ) -{ - int i, j, k; - int centerpoint_type, neigh_type; - int num_changes; - int num_donors, num_acceptors, num_donor_endpoints, num_acceptor_endpoints; - int neigh, tg_number, num_eql_mobile_gr, num_dif_mobile_gr, bond_type, has_mobile_H, has_mobile; - int num_forbidden, ind_forbidden, forbidden, num_N, num_O, num_P, num_S, num_OSt; - int val, delta_val, delta_met, num_bonds_non_metal, bonds_valence_non_metal; - int num_bonds, bonds_valence; - int inv_forbidden_mask = ~forbidden_mask; - MOBILE_GR MobileGr[MAXVAL]; - int num_endpoints; - MGROUPS MGroups[MAXVAL]; - int num_mgroups, num_diff_t_groups; - BNS_EDGE *e, *e1, *e2, *ev, *ev1, *ev2; - BNS_VERTEX *pv1, *pv2; - num_changes = 0; - /* search for possible centerpoints */ - for ( i = 0; i < num_atoms; i ++ ) { - - if ( at[i].chem_bonds_valence == at[i].valence || at[i].num_H || - at[i].endpoint || at[i].charge || at[i].radical || - !is_centerpoint_elem(at[i].el_number) || - !(centerpoint_type = get_pVA_atom_type( pVA, at, i, 0 )) || - 2 > (delta_val = at[i].chem_bonds_valence - (val = get_el_valence(at[i].el_number, 0, 0))) || - 2 > (delta_met = (bonds_valence_non_metal = nNoMetalBondsValence(at, i)) - val ) - ) { - continue; - } - - num_donors = num_acceptors = num_donor_endpoints = num_acceptor_endpoints = 0; - num_mgroups = num_endpoints = num_diff_t_groups = 0; - has_mobile = has_mobile_H = num_eql_mobile_gr = num_dif_mobile_gr = tg_number = 0; - ind_forbidden = -1; - num_forbidden = 0; - num_N = num_O = num_P = num_S = num_OSt = 0; - num_bonds_non_metal = nNoMetalNumBonds(at, i); - bonds_valence = at[i].chem_bonds_valence; - num_bonds = at[i].valence; - - for ( j = 0; j < at[i].valence; j ++ ) { - /* collect neighbors info */ - neigh = at[i].neighbor[j]; - val = get_endpoint_valence( at[neigh].el_number ); - forbidden = pBNS->edge[(int)pBNS->vert[i].iedge[j]].forbidden; - bond_type = (at[i].bond_type[j] & BOND_TYPE_MASK); - neigh_type = get_pVA_atom_type( pVA, at, neigh, bond_type); - if ( !forbidden && !at[neigh].endpoint ) { - /* save forbidden bonds */ - if ( is_el_a_metal(at[neigh].el_number) ) { - continue; - } - switch( bond_type ) { - case BOND_TYPE_SINGLE: - if ( !at[neigh].num_H && at[neigh].charge != -1 ) { - continue; /* not a donor */ - } - break; - case BOND_TYPE_DOUBLE: - if ( !neigh_type ) { - continue; - } - break; - default: - continue; - } - } - - MobileGr[num_endpoints].atom_number = neigh; - MobileGr[num_endpoints].ineigh = j; - MobileGr[num_endpoints].bond_type = bond_type; - MobileGr[num_endpoints].group_number = at[neigh].endpoint; - MobileGr[num_endpoints].endpoint_valence = val; - MobileGr[num_endpoints].forbidden = forbidden; - MobileGr[num_endpoints].atom_type_pVA = neigh_type; - MobileGr[num_endpoints].num_bonds = at[neigh].valence; - MobileGr[num_endpoints].bonds_valence = at[neigh].chem_bonds_valence; - MobileGr[num_endpoints].num_bonds_non_metal = nNoMetalNumBonds(at, neigh); - MobileGr[num_endpoints].bonds_valence_non_metal = nNoMetalBondsValence( at, neigh ); - - if ( forbidden & forbidden_mask ) { - num_forbidden ++; - ind_forbidden = num_endpoints; - } - num_O += 0 != (neigh_type & EL_TYPE_O) && at[neigh].valence == 1; /* ignore -O- */ - num_N += 0 != (neigh_type & EL_TYPE_N) && - !(at[neigh].valence == 3 && at[neigh].chem_bonds_valence == 3); /* ignore -N< */ - num_S += 0 != (neigh_type & EL_TYPE_S) && at[neigh].valence == 1; /* ignore -S- */ - num_P += 0 != (neigh_type & EL_TYPE_P) && - !(at[neigh].valence == 3 && at[neigh].chem_bonds_valence == 3); /* ignore -P< */ - num_OSt += 0 != (neigh_type & EL_TYPE_OSt); - num_acceptors += (bond_type == BOND_TYPE_DOUBLE) && (neigh_type & EL_TYPE_PT); - num_donors += (bond_type == BOND_TYPE_SINGLE) && (neigh_type & EL_TYPE_PT) && - (at[neigh].num_H || at[neigh].charge==-1 || at[neigh].endpoint); - if ( at[neigh].endpoint ) { - num_acceptor_endpoints += (bond_type == BOND_TYPE_DOUBLE); - num_donor_endpoints += (bond_type == BOND_TYPE_SINGLE); - if ( !tg_number ) { - tg_number = at[neigh].endpoint; - num_eql_mobile_gr = 1; - } else - if ( tg_number == at[neigh].endpoint ) { - num_eql_mobile_gr ++; - } else { - num_dif_mobile_gr ++; - } - } else - if ( bond_type == BOND_TYPE_SINGLE && val ) { - if ( at[neigh].endpoint ) { - has_mobile_H |= 1; - has_mobile |= 1; - } else { - has_mobile_H |= (0 != at[neigh].num_H); - has_mobile |= (0 != at[neigh].num_H) || (at[neigh].charge == -1); - } - } - num_endpoints ++; - - if ( at[neigh].endpoint || (neigh_type & EL_TYPE_PT) ) { - for ( k = 0; k < num_mgroups; k ++ ) { - if ( MGroups[k].group_number == at[neigh].endpoint ) { - MGroups[k].num ++; - break; - } - } - if ( k == num_mgroups ) { - MGroups[k].group_number = at[neigh].endpoint; - MGroups[k].num = 1; - num_mgroups ++; - num_diff_t_groups += (0 != at[neigh].endpoint); - } - } - } - if ( !num_acceptors || !num_donors || /* num_acceptors > 2 ||*/ - num_eql_mobile_gr == num_endpoints && !num_forbidden || - !tg_number && !has_mobile_H ) { - continue; /* nothing to do */ - } - -/* case_5_1: */ - /***************** determine the case ************************/ - if ( 3 == num_bonds_non_metal && - 4 == bonds_valence_non_metal && - (centerpoint_type == EL_TYPE_C) && - 2 == num_O && 1 == num_N+num_S && num_OSt && - 1 == num_forbidden && 3 == num_eql_mobile_gr ) { - /******************************************************** - *** InChI Tech. Man., Table 5, case 1 *** - ******************************************************** - 2 - OH OH X = N, S, Se, Te - / / f = fixed bond - e / / tg = Mobile-H vertex - HX---C --> X===C - ev2|| f \\ ev1 f \ - || \\ \ - tg------O OH - 1 - Problem: - XH, O, and O belong to the same Mobile-H group. - Fixed bond prevents the correct structure restoration: - H cannot migrate from X to O because HX-N bond is fixed - Solution: - Move H from X to allow XH-C bond change - (this unfixes the bond, see SetForbiddenEdges(...) ) - *********************************************************/ - int jXH = -1, jO1 = -1, jO2 = -1, n = 0; - for ( j = 0; j < num_endpoints; j ++ ) { - if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_S)) && - (MobileGr[j].forbidden == forbidden_mask) && - MobileGr[j].bond_type == BOND_TYPE_SINGLE && - jXH < 0 ) { - jXH = j; - n ++; - } else - if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_O && - MobileGr[j].num_bonds_non_metal == 1 && - !MobileGr[j].forbidden ) { - if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && jO1 < 0 ) { - jO1 = j; - n ++; - } else - if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && jO2 < 0 ) { - jO2 = j; - n ++; - } - } - } - if ( n != 3 ) { - goto case_5_2; - } - /* XH-C edge */ - e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jXH].ineigh]; - /* C=O edge */ - ev1 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jO1].ineigh]; - /* XH-tg edge */ - ev2 = pBNS->edge + pVA[MobileGr[jXH].atom_number].nTautGroupEdge - 1; - - if ( !ev1->flow || !ev2->flow ) { - goto case_5_2; - } - - /* do not remove forbidden edge bit */ - e->flow ++; - ev1->flow --; - ev2->flow --; - pBNS->vert[ev1->neighbor12 ^ i].st_edge.flow --; - pBNS->vert[ev2->neighbor12 ^ ev2->neighbor1].st_edge.flow --; - pBNS->tot_st_flow -= 2; - num_changes ++; - continue; - } -case_5_2: - /*********************************************************************/ - if ( 3 == num_bonds_non_metal && - 5 == bonds_valence_non_metal && - (centerpoint_type == EL_TYPE_N) && - 2 == num_O && 1 == num_N+num_S && - 1 == num_forbidden && 3 == num_eql_mobile_gr ) { - /******************************************************** - *** InChI Tech. Man., Table 5, case 2 *** - ******************************************************** - - O OH X = N, S, Se, Te - // / f = fixed bond - e // / tg = Mobile-H vertex - HX---N --> X===N - ev2|| f \\ ev1 f \\ - || \\ \\ - tg------O O - - Problem: - XH, O, and O belong to the same Mobile-H group. - Fixed bond prevents the correct structure restoration: - H cannot migrate from X to O because HX-N bond is fixed - Solution: - Move H from X to allow XH-N bond change - (this unfixes the bond, see SetForbiddenEdges(...) ) - *********************************************************/ - int jXH = -1, jO1 = -1, jO2 = -1, n = 0; - for ( j = 0; j < num_endpoints; j ++ ) { - if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_S)) && - (MobileGr[j].forbidden == forbidden_mask) && - MobileGr[j].bond_type == BOND_TYPE_SINGLE && - jXH < 0 ) { - jXH = j; - n ++; - } else - if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_O && - MobileGr[j].bond_type == BOND_TYPE_DOUBLE && - MobileGr[j].num_bonds_non_metal == 1 && - !MobileGr[j].forbidden ) { - if ( jO1 < 0 ) { - jO1 = j; - n ++; - } else - if ( jO2 < 0 ) { - jO2 = j; - n ++; - } - } - } - if ( n != 3 ) { - goto case_5_4; - } - /* XH-N edge */ - e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jXH].ineigh]; - /* N=O edge */ - ev1 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jO1].ineigh]; - /* XH-tg edge */ - ev2 = pBNS->edge + pVA[MobileGr[jXH].atom_number].nTautGroupEdge - 1; - - if ( !ev1->flow || !ev2->flow ) { - goto case_5_4; - } - /* do not remove forbidden edge bit */ - e->flow ++; - ev1->flow --; - ev2->flow --; - pBNS->vert[ev1->neighbor12 ^ i].st_edge.flow --; /* first =O vertex */ - pBNS->vert[ev2->neighbor12 ^ ev2->neighbor1].st_edge.flow --; /* taut group vertex tg */ - pBNS->tot_st_flow -= 2; - num_changes ++; - continue; - } -case_5_4: - /*********************************************************************/ - if ( 3 == num_bonds_non_metal && - 5 == bonds_valence_non_metal && - (centerpoint_type & (EL_TYPE_N | EL_TYPE_P)) && - 1 == num_O+num_S && 0 < num_N && 2 == (num_N + num_P) && - 1 == num_forbidden && num_O+num_S+num_N == num_eql_mobile_gr ) { - /******************************************************** - *** InChI Tech. Man., Table 5, case 4 *** - ******************************************************** - O = O, S, Se, Te - X X X = N, P, As - // // f = fixed bond - e // ev2 // tg = Mobile-H vertex - O===N --> HO---N - || f \ ev1 f \\ - || \ \\ - tg------NH N - - Problem: - O, NH, and possibly X belong to the same Mobile-H group. - Fixed bond prevents the correct structure restoration: - H cannot migrate from NH to O because O=N bond is fixed - Solution: - Move H from NH to O to allow O=N bond change - (this unfixes the bond, see fix_special_bonds(...) ) - *********************************************************/ - int jO = -1, jNH = -1, jX = -1, n = 0; - for ( j = 0; j < num_endpoints; j ++ ) { - if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) && - MobileGr[j].forbidden == forbidden_mask && - MobileGr[j].bond_type == BOND_TYPE_DOUBLE && - MobileGr[j].num_bonds_non_metal == 1 && - jO < 0 ) { - jO = j; - n ++; - } else - if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_P)) && - !MobileGr[j].forbidden ) { - if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && - (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N && jNH < 0 ) { - jNH = j; - n ++; - } else - if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && jX < 0 ) { - jX = j; - n ++; - } - } - } - if ( n != 3 ) { - goto case_5_6; - } - /* O=N edge */ - e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jO].ineigh]; - /* N-NH edge */ - ev1 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jNH].ineigh]; - /* N=X edge */ - ev2 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jX].ineigh]; - - if ( !e->flow ) { - goto case_5_6; - } - /* do not remove forbidden edge bit */ - e->flow --; - ev1->flow ++; - pBNS->vert[e->neighbor12 ^ i].st_edge.flow --; - pBNS->vert[ev1->neighbor12 ^ i].st_edge.flow --; - pBNS->tot_st_flow -= 2; - num_changes ++; - continue; - } -case_5_6: - /********* InChI Tech.Man. Table 5, case 6 **************/ - if ( 2 == delta_met && 4 == num_bonds_non_metal && - 5 == bonds_valence_non_metal && - 1 == num_forbidden && 1 < num_eql_mobile_gr && - !num_dif_mobile_gr && - (centerpoint_type & (EL_TYPE_N | EL_TYPE_P)) && - 1 <= num_N && 2 <= num_N+num_O+num_S && - 1 == num_acceptor_endpoints && 0 < num_donor_endpoints ) { - int jN = -1, njFix = 0, jFix[4], n = 0; - /* centerpoint is N, P, As, Sb - - input output - ----- ------ - end - po- - int - 2 - - X ZH X Z Z=N,O,S,Se,Te [terminal endpoint] - \ | \ || - \| f f \|| - Y---N===N--- Y---N---NH--- - e f - cen end no bond - ter po- fixed - po- int - int 1 tautomerism O==N--NH is allowed - - Problem: OH and =N- belong to a Mobile-H group, but - forbidden edge e does not allow them to be - tautomeric in the restored structure. - - Solution: - - 1. Decrement flow in edge e - 2. Decrement st_edge flow in N and N connected by e - 3. Fix all single order bonds to not terminal tautomeric N around N(centerpoint) - 4. Run BNS to establist new flow distribution - */ - /* fixed bond */ - for ( j = 0; j < num_endpoints; j ++ ) { - neigh = MobileGr[j].atom_number; - if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && - (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N && - MobileGr[j].num_bonds_non_metal == 2 && - MobileGr[j].bonds_valence_non_metal == 3 && - at[neigh].endpoint && - !at[neigh].num_H && !at[neigh].charge && - !at[neigh].radical && - (MobileGr[j].forbidden & forbidden_mask) && jN < 0 ) { - jN = j; - n ++; - } else - if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && - at[neigh].endpoint ) { - if ( MobileGr[j].num_bonds > 1 ) { - jFix[njFix ++] = j; - } - n ++; - } - } - - if ( jN < 0 || n < 2 || 1 + njFix == n ) { - goto case_5_7; /* nothing to do */ - } - - e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jN].ineigh]; /* fixed edge */ - if ( !e->flow ) { - goto case_5_7; - } - e->flow --; - pBNS->vert[i].st_edge.flow --; - pBNS->vert[e->neighbor12 ^ i].st_edge.flow --; - pBNS->tot_st_flow -= 2; - - for ( j = 0; j < njFix; j ++ ) { - /* edges to fix */ - ev = pBNS->edge + pBNS->vert[i].iedge[(int)MobileGr[jFix[j]].ineigh]; - ev->forbidden |= forbidden_mask; - } - num_changes ++; - continue; - } -case_5_7: - /*********************************************************************/ - if ( 3 == num_bonds_non_metal && - 4 == bonds_valence_non_metal && - (centerpoint_type == EL_TYPE_S) && - 2 == num_O+num_S && 1 == num_OSt && 1 == num_N && - 1 == num_forbidden && 3 == num_eql_mobile_gr && - MobileGr[ind_forbidden].bond_type == BOND_TYPE_SINGLE ) { - /******************************************************** - *** InChI Tech. Man., Table 5, case 7 *** - ******************************************************** - O = O, S, Se, Te - OH OH S = S, Se, Te - / / f = fixed bond - e /ev2 f / tg = Mobile-H vertex - HN---S --> N===S X = N or non-endpoint; - || f \\ \ - ev2|| \\ev1 \ - tg------O OH - N, O, O - Problem: ======= - O, NH, OH belong to the same Mobile-H group. - Fixed bond prevents the correct structure restoration: - H cannot migrate from NH to O because HN-S bond is fixed - Solution: - Move H from NH to =O to allow HN=S bond change by making a 2nd terminal -OH - (this unfixes the bond, see fix_special_bonds(...) ) - *********************************************************/ - int jO = -1, jNH = -1, jOH = -1, n = 0; - for ( j = 0; j < num_endpoints; j ++ ) { - if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N && - MobileGr[j].forbidden == forbidden_mask && - MobileGr[j].bond_type == BOND_TYPE_SINGLE && - MobileGr[j].num_bonds_non_metal <= 2 && - jNH < 0 ) { - jNH = j; - n ++; - } else - if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) && - !MobileGr[j].forbidden && - MobileGr[j].num_bonds_non_metal == 1 ) { - if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && - jO < 0 ) { - jO = j; - n ++; - } else - if ( jOH < 0 ) { - jOH = j; - n ++; - } - } - } - if ( n != 3 ) { - goto case_5_9a; - } - /* NH-S edge */ - e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jNH].ineigh]; - /* S=O edge */ - ev1 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jO].ineigh]; - /* XH-tg edge */ - ev2 = pBNS->edge + pVA[MobileGr[jNH].atom_number].nTautGroupEdge - 1; - - if ( !ev1->flow || !ev2->flow ) { - goto case_5_9a; - } - - /* do not remove forbidden edge bit */ - e->flow ++; - ev1->flow --; - ev2->flow --; - pBNS->vert[ev1->neighbor12 ^ i].st_edge.flow --; /* first =O vertex */ - pBNS->vert[ev2->neighbor12 ^ ev2->neighbor1].st_edge.flow --; /* taut group vertex tg */ - pBNS->tot_st_flow -= 2; - num_changes ++; - continue; - } -case_5_9a: - /*********************************************************************/ - if ( 3 == num_bonds_non_metal && - 4 == bonds_valence_non_metal && - (centerpoint_type == EL_TYPE_S) && - 1 == num_O+num_S && !num_OSt && 1 <= num_N && - 1 == num_forbidden && - num_O+num_S+num_N == num_eql_mobile_gr && - MobileGr[ind_forbidden].bond_type == BOND_TYPE_SINGLE ) { - /******************************************************** - *** InChI Tech. Man., Table 5, case 9a *** - ******************************************************** - O = O, S, Se, Te - X X S = S, Se, Te - / / f = fixed bond - / / tg = Mobile-H vertex - HN---S --> N===S X = N or non-endpoint; - || \\ e \ -X is not -O(terminal) - || f\\ f\ - tg------O OH - N, N, O or N, O - Problem: ================ - O, NH belong to the same Mobile-H group. - Fixed bond prevents the correct structure restoration: - H cannot migrate from NH to O because O=S bond is fixed - Solution: - Move H from NH to =O to allow O=S bond change by making a terminal -OH - (this unfixes the bond, see fix_special_bonds(...) ) - *********************************************************/ - int jO = -1, jNH = -1, jX = -1, n = 0; - for ( j = 0; j < num_endpoints; j ++ ) { - if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) && - MobileGr[j].forbidden == forbidden_mask && - MobileGr[j].bond_type == BOND_TYPE_DOUBLE && - MobileGr[j].num_bonds_non_metal == 1 && - jO < 0 ) { - jO = j; - n ++; - } else - if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N && - !MobileGr[j].forbidden && - MobileGr[j].bond_type == BOND_TYPE_SINGLE && - jNH < 0 ) { - jNH = j; - n ++; - } else - if ( jX < 0 ) { - jX = j; - n ++; - } - } - if ( jO < 0 || jNH < 0 ) { - goto case_5_8b_to_9b; - } - - e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[ind_forbidden].ineigh]; - if ( !e->flow ) { - goto case_5_8b_to_9b; - } - e->flow --; - pBNS->vert[e->neighbor1].st_edge.flow --; - pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --; - pBNS->tot_st_flow -= 2; - num_changes ++; - continue; - } -case_5_8b_to_9b: /* #1 */ - /*********************************************************************/ - if ( 3 == num_bonds_non_metal && - 4 == bonds_valence_non_metal && - (centerpoint_type == EL_TYPE_S) && - 0 == num_O+num_S && 2 == num_N && 0 == num_P && !num_OSt && - 1 == num_forbidden && - 1 == num_eql_mobile_gr && 0 == num_dif_mobile_gr && - 1 == num_donor_endpoints && 0 == num_acceptor_endpoints && - 1 == num_donors && 1 == num_acceptors && - MobileGr[ind_forbidden].bond_type == BOND_TYPE_SINGLE ) { - /******************************************************** - *** InChI Tech. Man., Table 5, case 8b->9b *** - ******************************************************** - ---> O = O, S, Se, Te - X X S = S, Se, Te - \ f/ \ f/ f = fixed bond - \ ev / C=====Z \ / C-----ZH tg = Mobile-H vertex - N===S | | N===S || || X = is N not an endpoint; - not \ | | \ || || -X is not terminal -O,-S,-Se,-Te or - an \ | e | \|| e || any N, P, As - endpoint NH=====tg N------tg - is an f N, N, X, fixed single - endpoint ===================== - - Problem: - N is not a Mobile-H endpoint, NH is a Mobile-H endpoint - Unfixed bond N==S prevents the correct structure restoration: - H can migrate from NH to N because N=S bond is not fixed - Solution: - Move H from NH to =Z to make N=S bond fixed (Table 5, case 9) - (this unfixes the bond, see fix_special_bonds(...) ) - *********************************************************/ - int jN = -1, jNH = -1, jX = -1, n = 0; - for ( j = 0; j < num_endpoints; j ++ ) { - if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N && - !(MobileGr[j].forbidden & forbidden_mask) ) { - if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && - !at[MobileGr[j].atom_number].endpoint && - jN < 0 ) { - jN = j; - n ++; - } else - if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && - MobileGr[j].num_bonds == 2 && - MobileGr[j].bonds_valence == 2 && - at[MobileGr[j].atom_number].endpoint && - jNH < 0 ) { - jNH = j; - n ++; - } - } else - if ( !((MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_P)) || - (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) && - MobileGr[j].num_bonds > 1 ) && - (MobileGr[j].forbidden & forbidden_mask) && - MobileGr[j].bond_type == BOND_TYPE_SINGLE && - jX < 0 ) { - jX = j; - n ++; - } - } - if ( n != 3 ) { - goto case_5_8c_to_9c; - } - - e = pBNS->edge + pVA[MobileGr[jNH].atom_number].nTautGroupEdge - 1; - if ( !e->flow ) { - goto case_5_8c_to_9c; /* should not happen ??? */ - } - e->flow --; - pBNS->vert[e->neighbor1].st_edge.flow --; - pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --; - pBNS->tot_st_flow -= 2; - e->forbidden |= forbidden_mask; - num_changes ++; - continue; - } -case_5_8c_to_9c: /* #2 */ - /*********************************************************************/ - if ( 3 == num_bonds_non_metal && - 4 == bonds_valence_non_metal && - (centerpoint_type == EL_TYPE_S) && - 0 == num_O+num_S && 3 == num_N && 0 == num_P && - 1 == num_forbidden && - 3 == num_eql_mobile_gr && 0 == num_dif_mobile_gr && - 2 == num_donor_endpoints && 1 == num_acceptor_endpoints && - 2 == num_donors && 1 == num_acceptors && - MobileGr[ind_forbidden].bond_type == BOND_TYPE_SINGLE ) { - /******************************************************** - *** InChI Tech. Man., Table 5, case 8c->9c *** - ******************************************************** - is an endpoint ---> O = O, S, Se, Te - NH2(X) NH2(X) S = S, Se, Te - \ / pv1 pv2 \ / f = fixed bond - \ 1 / C-----ZH \ / C=====Z tg = Mobile-H vertex - N===S || || N===S | | X = is N not an endpoint; - is an \f||ev1 ||ev2 \f | | -X is not terminal -O,-S,-Se,-Te or - endpoint \|| e || \ | e | any N, P, As - 2 N------tg NH=====tg C is a centerpoint of a t-group - is an f N, N, X, fixed single - endpoint ===================== - - Problem: - N is not a Mobile-H endpoint, NH is a Mobile-H endpoint - Unfixed bond N==S prevents the correct structure restoration: - H can migrate from NH to N because N=S bond is not fixed - Solution: - Move H from NH to =Z to make N=S bond fixed (Table 5, case 9) - (this unfixes the bond, see fix_special_bonds(...) ) - *********************************************************/ - int jN1 = -1, jN2 = -1, jX = -1, n = 0; - EdgeIndex ie, ie1, ie2; - for ( j = 0; j < num_endpoints; j ++ ) { - if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N && - !(MobileGr[j].forbidden & forbidden_mask) ) { - if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && - at[MobileGr[j].atom_number].endpoint && - jN1 < 0 ) { - jN1 = j; - n ++; - } else - if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && - MobileGr[j].num_bonds == 2 && - MobileGr[j].bonds_valence == 3 && - MobileGr[j].forbidden == forbidden_mask && - at[MobileGr[j].atom_number].endpoint && - jN2 < 0 ) { - jN2 = j; - n ++; - } else - if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && - MobileGr[j].num_bonds <= 2 && - MobileGr[j].bonds_valence <= 3 && - at[MobileGr[j].atom_number].endpoint && - jX < 0 ) { - jX = j; - n ++; - } - } - } - if ( n != 3 ) { - goto case_5_9b_to_8b; - } - - e = pBNS->edge + pVA[MobileGr[jN2].atom_number].nTautGroupEdge - 1; - if ( e->flow ) { - goto case_5_9b_to_8b; /* should not happen ??? */ - } - pv1 = pBNS->vert + e->neighbor1; /* must be jN2 */ - pv2 = pBNS->vert + (e->neighbor1 ^ e->neighbor12); - ie = e - pBNS->edge; - ie1 = ie2 = -1; - for ( j = 0; j < pv1->num_adj_edges; j ++ ) { - ev1 = pBNS->edge + pv1->iedge[j]; - if ( ev1->flow && !ev1->forbidden ) { - ie1 = ev1 - pBNS->edge; - pv1 = pBNS->vert + (ev1->neighbor12 ^ (pv1 - pBNS->vert)); - break; - } - } - for ( j = 0; j < pv2->num_adj_edges; j ++ ) { - ev2 = pBNS->edge + pv2->iedge[j]; - if ( ev2->flow && !ev2->forbidden ) { - ie2 = ev2 - pBNS->edge; - pv2 = pBNS->vert + (ev2->neighbor12 ^ (pv2 - pBNS->vert)); - break; - } - } - if ( ie1 < 0 || ie2 < 0 ) { - goto case_5_9b_to_8b; - } - e->flow ++; - e->forbidden |= forbidden_mask; - ev1->flow --; - ev2->flow --; - pv1->st_edge.flow --; - pv2->st_edge.flow --; - pBNS->tot_st_flow -= 2; - num_changes ++; - continue; - } -case_5_9b_to_8b: /* #3 */ - /*********************************************************************/ - if ( 3 == num_bonds_non_metal && - 4 == bonds_valence_non_metal && - (centerpoint_type == EL_TYPE_S) && - 0 == num_O+num_S && 2 == num_N && 0 == num_P && - 1 == num_forbidden && - 2 == num_eql_mobile_gr && 0 == num_dif_mobile_gr && - 1 == num_donor_endpoints && 1 == num_acceptor_endpoints && - 1 == num_donors && 1 == num_acceptors && - MobileGr[ind_forbidden].bond_type == BOND_TYPE_DOUBLE ) { - /******************************************************** - *** InChI Tech. Man., Table 5, case 9b->8b *** - ******************************************************** - ---> O = O, S, Se, Te - X is an X S = S, Se, Te - \ / endpoint \ / f = fixed bond - \ 1ev / C-----ZH \ / C=====Z tg = Mobile-H vertex - N===S || || N===S | | X = is N not an endpoint; - is an f \ || || f \ | | -X is not terminal -O,-S,-Se,-Te or - endpoint 2\|| e || \ | e | any N, P, As - N------tg NH=====tg - is an f N, N, X, fixed double - endpoint ===================== - - Problem: - N1 and N2 are Mobile-H endpoints and belong to the same Mobile-H group. - Fixed bond N1==S prevents the correct structure restoration: - H cannot migrate ZH->N2->N1 because N1=S bond is fixed - Solution: - Move H from ZH to N2 to make N1=S bond unfixed and fix S-X bond (Table 5, case 8) - (see fix_special_bonds(...) for details ) - *********************************************************/ - int jN1 = -1, jN2 = -1, jX = -1, n = 0; - EdgeIndex ie, ie1, ie2; - for ( j = 0; j < num_endpoints; j ++ ) { - if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N ) { - if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && - at[MobileGr[j].atom_number].endpoint && - (MobileGr[j].forbidden == forbidden_mask) && - jN1 < 0 ) { - jN1 = j; - n ++; - } else - if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && - MobileGr[j].num_bonds == 2 && - MobileGr[j].bonds_valence == 3 && - at[MobileGr[j].atom_number].endpoint && - !(MobileGr[j].forbidden & forbidden_mask) && - jN2 < 0 ) { - jN2 = j; - n ++; - } - } else - if ( !((MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_P)) || - (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) && - MobileGr[j].num_bonds > 1 ) && - !(MobileGr[j].forbidden & forbidden_mask) && - MobileGr[j].bond_type == BOND_TYPE_SINGLE && - jX < 0 ) { - jX = j; - n ++; - } - } - if ( jN1 < 0 || jN2 < 0 ) { - goto case_5_9c_to_8c; - } - - ev = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jN1].ineigh]; - e = pBNS->edge + pVA[MobileGr[jN2].atom_number].nTautGroupEdge - 1; - if ( e->flow ) { - goto case_5_9c_to_8c; /* should not happen ??? */ - } - pv1 = pBNS->vert + e->neighbor1; /* must be jN2 */ - pv2 = pBNS->vert + (e->neighbor1 ^ e->neighbor12); - ie = e - pBNS->edge; - ie1 = ie2 = -1; - ev->forbidden &= inv_forbidden_mask; - for ( j = 0; j < pv1->num_adj_edges; j ++ ) { - ev1 = pBNS->edge + pv1->iedge[j]; - if ( ev1->flow && !ev1->forbidden ) { - ie1 = ev1 - pBNS->edge; - pv1 = pBNS->vert + (ev1->neighbor12 ^ (pv1 - pBNS->vert)); - break; - } - } - for ( j = 0; j < pv2->num_adj_edges; j ++ ) { - ev2 = pBNS->edge + pv2->iedge[j]; - if ( ev2->flow && !ev2->forbidden ) { - ie2 = ev2 - pBNS->edge; - pv2 = pBNS->vert + (ev2->neighbor12 ^ (pv2 - pBNS->vert)); - break; - } - } - if ( ie1 < 0 || ie2 < 0 ) { - ev->forbidden |= forbidden_mask; /* failed; restore the forbidden bit */ - goto case_5_9c_to_8c; - } - e->flow ++; - e->forbidden |= forbidden_mask; - ev1->flow --; - ev2->flow --; - pv1->st_edge.flow --; - pv2->st_edge.flow --; - pBNS->tot_st_flow -= 2; - num_changes ++; - continue; - } -case_5_9c_to_8c: /* #4 */ - /*********************************************************************/ - if ( 3 == num_bonds_non_metal && - 4 == bonds_valence_non_metal && - (centerpoint_type == EL_TYPE_S) && - 0 == num_O+num_S && 3 == num_N && 0 == num_P && - 0 == num_forbidden && - 2 == num_diff_t_groups && 2 == num_mgroups && /* all neighbors belong to 2 t-groups */ - 3 == num_eql_mobile_gr + num_dif_mobile_gr && /* all 3 neighbors belong to t-groups */ - 2 == num_donor_endpoints && 1 == num_acceptor_endpoints && - 2 == num_donors && 1 == num_acceptors ) { - /******************************************************** - *** InChI Tech. Man., Table 5, case 8c->9c *** - ******************************************************** - is an endpoint ---> O = O, S, Se, Te - tg1 NH2(X) NH2(X) S = S, Se, Te - \ / pv1 pv2 \ / f = fixed bond - \(1) / C=====Z \ / C-----ZH tg = Mobile-H vertex - N===S | | N===S || || X = is N not an endpoint; - is an \ |ev1 | ev2 \ || || -X is not terminal -O,-S,-Se,-Te or - endpoint \ | e | \|| e || any N, P, As - tg1 (2)NH=====tg N------tg C is a centerpoint of a t-group - is an f N, N, X, fixed single - endpoint ===================== - tg2 - Problem: - N (1) and NH2 are Mobile-H group 1 endpoiints, NH (2) is a Mobile-H group 2 endpoint - Unfixed bonds N==S--NH(2) allows the two Mobile H groups to merge - hence prevents the correct structure restoration: - H can migrate from NH (2) to N (1) because S-NH(1) bond is not fixed - Solution: - Move H from NH(2) to =Z to make S-NH(2) bond fixed (Table 5, case 8c) - (this unfixes the bond, see fix_special_bonds(...) ) - *********************************************************/ - int jN1 = -1, jN2 = -1, jX = -1, n = 0; - /* find t-group that is represented by only one neighbor */ - for ( j = 0, k = 0; j < num_mgroups; j ++ ) { - if ( 1 == MGroups[k].num && MGroups[k].group_number ) { - k = MGroups[k].group_number; - break; - } - } - if ( !k ) { - goto case_5_9c_to_9d; - } - for ( j = 0; j < num_endpoints; j ++ ) { - if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N ) { - if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && - at[MobileGr[j].atom_number].endpoint && - at[MobileGr[j].atom_number].endpoint != k && - jN1 < 0 ) { - jN1 = j; - n ++; - } else - if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && - MobileGr[j].num_bonds == 2 && - MobileGr[j].bonds_valence == 2 && - at[MobileGr[j].atom_number].endpoint == k && - jN2 < 0 ) { - jN2 = j; - n ++; - } else - if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && - MobileGr[j].num_bonds <= 2 && - MobileGr[j].bonds_valence <= 3 && - at[MobileGr[j].atom_number].endpoint && - at[MobileGr[j].atom_number].endpoint != k && - jX < 0 ) { - jX = j; - n ++; - } - } - } - if ( n != 3 ) { - goto case_5_9c_to_9d; - } - - e = pBNS->edge + pVA[MobileGr[jN2].atom_number].nTautGroupEdge - 1; - if ( !e->flow ) { - goto case_5_9c_to_9d; /* should not happen ??? */ - } - e->flow --; - pBNS->vert[e->neighbor1].st_edge.flow --; - pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --; - pBNS->tot_st_flow -= 2; - e->forbidden |= forbidden_mask; - num_changes ++; - continue; - } -case_5_9c_to_9d: /* #6 */ - /*********************************************************************/ - if ( 3 == num_bonds_non_metal && - 4 == bonds_valence_non_metal && - (centerpoint_type == EL_TYPE_S) && - 0 == num_O+num_S && 3 == num_N && 0 == num_P && - 0 == num_forbidden && - 3 == num_mgroups && 2 == num_diff_t_groups && - 2 == num_donor_endpoints && 0 == num_acceptor_endpoints && - 2 == num_donors && 1 == num_acceptors ) { - /******************************************************** - *** InChI Tech. Man., Table 5, case 9b->8b *** - ******************************************************** - 3(X) ---> e2| O = O, S, Se, Te - NH---is an N====== S = S, Se, Te - \ / endpoint \ / f = fixed bond - \ 1 / C=====Z \ f / C-----Z tg = Mobile-H vertex - N===S | | N===S || || X = is N not an endpoint; - is an ev \ | | ev \ || || -X is not terminal -O,-S,-Se,-Te or - endpoint 2\ | e1 | \|| e1 || any N, P, As - NH=====tg N------tg - is an f N, N, X, fixed double - endpoint ===================== - - Problem: - N1, N2, and N3 are Mobile-H endpoints and belong to the same Mobile-H group. - Fixed bond N1==S prevents the correct structure restoration: - H cannot migrate N3->N2->N1 because N1=S bond is fixed - Solution: - Move mobile H to N2 and N3 to make N1=S bond unfixed (Table 5, case 9c) - (see fix_special_bonds(...) for details ) - *********************************************************/ - int jN1 = -1, jN2 = -1, jX = -1, n = 0; - for ( j = 0; j < num_endpoints; j ++ ) { - if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N ) { - if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && - !at[MobileGr[j].atom_number].endpoint && - !(MobileGr[j].forbidden & forbidden_mask) && - jN1 < 0 ) { - jN1 = j; - n ++; - } else - if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && - MobileGr[j].num_bonds == 2 && - MobileGr[j].bonds_valence <= 3 && - at[MobileGr[j].atom_number].endpoint && - !(MobileGr[j].forbidden & forbidden_mask) ) { - if ( jN2 < 0 ) { - jN2 = j; - n ++; - } else - if ( jX < 0 ) { - jX = j; - n ++; - } - } - } - } - if ( n != 3 ) { - goto case_end; - } - ev = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jN1].ineigh]; - if ( !e->flow ) { - goto case_end; - } - e1 = pBNS->edge + pVA[MobileGr[jN2].atom_number].nTautGroupEdge - 1; - if ( !e1->flow ) { - goto case_end; /* should not happen ??? */ - } - e2 = pBNS->edge + pVA[MobileGr[jX].atom_number].nTautGroupEdge - 1; - if ( !e2->flow ) { - goto case_end; /* should not happen ??? */ - } - /* take care of edge e1 */ - e = e1; - e->flow --; - pBNS->vert[e->neighbor1].st_edge.flow --; - pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --; - pBNS->tot_st_flow -= 2; - e->forbidden |= forbidden_mask; - num_changes ++; - /* take care of edge e2 */ - e = e2; - e->flow --; - pBNS->vert[e->neighbor1].st_edge.flow --; - pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --; - pBNS->tot_st_flow -= 2; - e->forbidden |= forbidden_mask; - num_changes ++; - /* take care of edge ev: do not let it change */ - ev->forbidden |= forbidden_mask; - continue; - } -case_end:; - } -/*exit_function:*/ - return num_changes; -} -/******************************************************************************************************/ -/* Replace ambiguous neutral (+)edge->flow=0, (-)edge->flow=1 with (+)edge->flow=1, (-)edge->flow=0 */ -/******************************************************************************************************/ -int RearrangePlusMinusEdgesFlow( BN_STRUCT *pBNS, BN_DATA *pBD, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, int forbidden_edge_mask ) -{ - int ret, ePlus, eMinus; - EDGE_LIST NewlyFixedEdges; - BNS_EDGE *pPlus, *pMinus; - int i, k1, k2, num_found, num_tot, delta, v1, v2; - - ret = 0; - - AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR ); - for ( i = 0, num_found = num_tot = 0; i < pBNS->num_atoms; i ++ ) { - eMinus = pVA[i].nCMinusGroupEdge - 1; - ePlus = pVA[i].nCPlusGroupEdge - 1; - num_tot += (eMinus >= 0) + (ePlus >= 0); - if ( eMinus >= 0 && ePlus >= 0 ) { - pPlus = pBNS->edge + ePlus; - pMinus = pBNS->edge + eMinus; - if ( (k1=pMinus->flow) > 0 && (k2=pPlus->cap-pPlus->flow) > 0 ) { - num_found ++; - } - } - } - if ( !num_found ) { - goto exit_function; - } - if ( ret = AllocEdgeList( &NewlyFixedEdges, num_tot + pBNS->num_bonds ) ) { - goto exit_function; - } - - for ( i = 0, num_found = num_tot = 0; i < pBNS->num_atoms; i ++ ) { - eMinus = pVA[i].nCMinusGroupEdge - 1; - ePlus = pVA[i].nCPlusGroupEdge - 1; - num_tot += (eMinus >= 0) + (ePlus >= 0); - if ( eMinus >= 0 && ePlus >= 0 ) { - pPlus = pBNS->edge + ePlus; - pMinus = pBNS->edge + eMinus; - if ( (k1=pMinus->flow) > 0 && (k2=pPlus->cap - pPlus->flow) > 0 ) { - /* rearrange */ - v1 = pMinus->neighbor1; - v2 = pMinus->neighbor12 ^ v1; - delta = inchi_min(k1,k2); - pMinus->flow -= delta; - pBNS->vert[v1].st_edge.flow -= delta; - pBNS->vert[v2].st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - } - /* fix charges */ - pPlus->forbidden |= forbidden_edge_mask; - pMinus->forbidden |= forbidden_edge_mask; - if ( (ret = AddToEdgeList( &NewlyFixedEdges, eMinus, 0 )) || - (ret = AddToEdgeList( &NewlyFixedEdges, ePlus, 0 ))) { - goto exit_function; - } - } else - if ( eMinus >= 0 ) { - /* fix charges */ - pMinus = pBNS->edge + eMinus; - pMinus->forbidden |= forbidden_edge_mask; - if ( ret = AddToEdgeList( &NewlyFixedEdges, eMinus, 0 )) { - goto exit_function; - } - } else - if ( ePlus >= 0 ) { - /* fix charges */ - pPlus = pBNS->edge + ePlus; - pPlus->forbidden |= forbidden_edge_mask; - if ( ret = AddToEdgeList( &NewlyFixedEdges, ePlus, 0 )) { - goto exit_function; - } - } - } - for ( i = 0; i < pBNS->num_bonds; i ++ ) { - pBNS->edge[i].forbidden |= forbidden_edge_mask; - if ( ret = AddToEdgeList( &NewlyFixedEdges, i, 0 )) { - goto exit_function; - } - } - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask ); - if ( ret < 0 ) { - goto exit_function; - } - -exit_function: - AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE ); - return ret; -} -/******************************************************************************************************/ -int IncrementZeroOrderBondsToHeteroat( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, - VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, - int forbidden_edge_mask) -{ -#define FIX_BOND_ADD_ALLOC 128 - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - BNS_EDGE *pe, *peZero, *peNeighMeigh = NULL, *peMeFlower; - BNS_VERTEX *pMeFlower = NULL, *pNeigh = NULL, *pNeighNeigh=NULL; - - int i, j, k, ret2, ret, bFixedCarbonCharges, num_changes, bSuccess; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - Vertex vMeFlower0, vNeigh, vNeighMeigh = NO_VERTEX; - - EDGE_LIST CarbonChargeEdges; - EDGE_LIST NewlyFixedEdges; - - ret = 0; - num_changes = 0; - - bFixedCarbonCharges = 0; - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR ); - - if ( !pTCGroups->num_metal_atoms || - 0 > (k=pTCGroups->nGroup[TCG_MeFlower0]) || - 0 > (vMeFlower0 = pTCGroups->pTCG[k].nVertexNumber)) { - goto exit_function; - } - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - for ( i = 0; i < num_at; i ++ ) { - if ( !pVA[i].cMetal || pVA[i].nMetalGroupEdge <= 0 ) { - continue; - } - peMeFlower = pBNS->edge + pVA[i].nMetalGroupEdge-1; - if ( vMeFlower0 != (peMeFlower->neighbor12 ^ i) ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - pMeFlower = pBNS->vert + vMeFlower0; - - for ( j = 0; j < at2[i].valence; j ++ ) { - if ( !peMeFlower->flow ) { - break; /* cannot do anything */ - } - if ( !(at2[i].bond_type[j] & BOND_TYPE_MASK) ) { - /* found a zero order bond */ - if ( !bFixedCarbonCharges ) { - /* do not let carbon atoms get charged */ - if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { - goto exit_function; - } - bFixedCarbonCharges ++; - } - peZero = pBNS->edge + pBNS->vert[i].iedge[j]; - if ( peZero->flow ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - /* fix other edges */ - for ( k = 0; k < at2[i].valence; k ++ ) { - pe = pBNS->edge + pBNS->vert[i].iedge[k]; - if ( pe->flow == 1 && !(pe->forbidden & forbidden_edge_mask) ) { - if ( ret = AddToEdgeList( &NewlyFixedEdges, pe - pBNS->edge, FIX_BOND_ADD_ALLOC )) { - goto exit_function; - } - pe->forbidden |= forbidden_edge_mask; - } - } - /* do not create =N(+)= in a ring or #O(+) terminal */ - for ( k = 0; k < num_at; k ++ ) { - if ( !pVA[k].cMetal && pVA[k].cNumValenceElectrons == 5 && - at2[k].valence == 2 && !at2[k].num_H && pVA[k].cMinRingSize <= 6 && - pVA[k].nCPlusGroupEdge > 0 && - (pe=pBNS->edge + pVA[k].nCPlusGroupEdge-1)->flow==1 && - !(pe->forbidden & forbidden_edge_mask)) { - - if ( ret = AddToEdgeList( &NewlyFixedEdges, pe - pBNS->edge, FIX_BOND_ADD_ALLOC )) { - goto exit_function; - } - pe->forbidden |= forbidden_edge_mask; - } - } - - /* metal's neighbor connected by a zero-order bond */ - pNeigh = pBNS->vert + (vNeigh = at2[i].neighbor[j]); - /*for ( k = 0; k < pNeigh->num_adj_edges; k ++ )*/ - for ( k = pNeigh->num_adj_edges-1; 0 <= k; k -- ) - { - peNeighMeigh = pBNS->edge + pNeigh->iedge[k]; - if ( !peNeighMeigh->flow ) { - continue; - } - vNeighMeigh = peNeighMeigh->neighbor12 ^ vNeigh; - if ( vNeighMeigh != i && vNeighMeigh != vMeFlower0 ) { - /* metal neighbor's neighbor connected by a not-zero-order bond */ - pNeighNeigh = pBNS->vert + vNeighMeigh; - break; /* found */ - } - } - if ( k < 0 ) { - continue; /* neighbor not found */ - } - peZero->flow ++; - peZero->forbidden |= forbidden_edge_mask; - peMeFlower->flow --; - peNeighMeigh->flow --; - pMeFlower->st_edge.flow --; - pNeighNeigh->st_edge.flow --; - pBNS->tot_st_flow -= 2; - /* test */ - bSuccess = 0; - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == vMeFlower0 && vPathStart == vNeighMeigh || - vPathEnd == vNeighMeigh && vPathStart == vMeFlower0) && abs(nDeltaCharge) <= 2 ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - (*pnNumRunBNS) ++; - *pnTotalDelta += ret; - num_changes ++; - bSuccess = ret; - } - if ( ret = AddToEdgeList( &NewlyFixedEdges, peZero - pBNS->edge, FIX_BOND_ADD_ALLOC )) { - goto exit_function; - } - } else { - peZero->flow --; - peZero->forbidden &= inv_forbidden_edge_mask; - peMeFlower->flow ++; - peNeighMeigh->flow ++; - pMeFlower->st_edge.flow ++; - pNeighNeigh->st_edge.flow ++; - pBNS->tot_st_flow += 2; - } - RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask ); - NewlyFixedEdges.num_edges = 0; - if ( bSuccess ) { - /* update at2[] */ - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - } - } - } - } - ret = num_changes; - -exit_function: - RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask ); - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE ); - return ret; -} -/*********************************************************************** - NH2 NH2 - \ \ - C==S(+)- => C(+)-S- where NH2 are not tautomeric - / / - NH2 NH2 -************************************************************************/ -int MovePlusFromS2DiaminoCarbon( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, - VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ - int i, j, k, ret, ret2, cur_success; - int delta; - EdgeIndex ePlusS, ePlusC, eMinusC, e; - BNS_VERTEX *pvS, *pvC, *pv1, *pv2; - BNS_EDGE *pePlusS, *pePlusC, *pe1, *pe2, *peCN[3], *peSC, *pe; - Vertex vC, vN; - - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - - Vertex vPathStart, vPathEnd, v1, v2; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - - EDGE_LIST AllChargeEdges; - - ret = 0; - cur_success = 0; - - AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - /* find (NH2)C=S(+) */ - for ( i = 0; i < num_at; i ++ ) { - if ( !pVA[i].cMetal && pVA[i].cNumValenceElectrons == 6 && - at2[i].valence == 2 && - (pvS = pBNS->vert+i)->st_edge.cap == pvS->st_edge.flow && - 0 <= (ePlusS = pVA[i].nCPlusGroupEdge-1) && !(pePlusS=pBNS->edge+ePlusS)->flow && /* S(+) */ - (pe1=pBNS->edge + pvS->iedge[0])->flow + - (pe2=pBNS->edge + pvS->iedge[1])->flow == 1 /* -S(+)= */ && - pVA[vC = (peSC=pe1->flow? pe1 : pe2)->neighbor12 ^ i].cNumValenceElectrons == 4 && - at2[vC].valence == 3 && - 0 <= (ePlusC=pVA[vC].nCPlusGroupEdge-1) && (pePlusC=pBNS->edge+ePlusC)->flow && - !(0 <= (eMinusC=pVA[vC].nCMinusGroupEdge-1) && pBNS->edge[eMinusC].flow ) ) { - /* found >C=S(+)- */ - pvC = pBNS->vert + vC; - for ( j = k = 0; j < at[vC].valence; j ++ ) { - if ( peSC != (peCN[k] = pBNS->edge + pvC->iedge[j]) && !peCN[k]->flow ) { - k ++; /* a single bond from C */ - } - } - if ( k != 2 ) { - continue; - } - for ( j = 0; j < k; j ++ ) { - vN = peCN[j]->neighbor12 ^ vC; - if ( pVA[vN].cNumValenceElectrons != 5 || - pBNS->vert[vN].st_edge.cap != pBNS->vert[vN].st_edge.flow || - at2[vN].num_H != 2 || - at2[vN].endpoint || (pStruct->endpoint && pStruct->endpoint[vN]) ) { - break; /* does not fit the pattern */ - } - } - if ( j != k ) { - continue; - } - /* fix all charges */ - if ( !AllChargeEdges.num_edges ) { - for ( j = 0; j < num_at; j ++ ) { - if ( 0 <= (e = pVA[j].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && - (ret = AddToEdgeList( &AllChargeEdges, e, 2*num_at )) ) { - goto exit_function; - } - if ( 0 <= (e = pVA[j].nCMinusGroupEdge-1) && !pBNS->edge[e].forbidden && - (ret = AddToEdgeList( &AllChargeEdges, e, 2*num_at )) ) { - goto exit_function; - } - } - } - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - pePlusS->forbidden &= ~forbidden_edge_mask; - pe = pePlusC; - if ( !pe->flow ) - continue; - delta = 1; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { - /* Remover (+)charge from S => nDeltaCharge == -1 */ - /* Flow change on pe (+)charge edge (atom S) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - (*pnNumRunBNS) ++; - cur_success ++; - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - } -exit_function: - AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); - return ret; -} -/******************************************************************************************************/ -int EliminateChargeSeparationOnHeteroatoms( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, - VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, - int forbidden_edge_mask, int forbidden_stereo_edge_mask) - /********* Avoid charge separation on heteroatoms ******************/ -{ - int i, j, k, ret, ret2, num_pos, num_neg, num_min=0, bFixedCarbonCharges; - int vPlusSuper; /* (+)super vertex */ - int ePlusSuper; /* edge from vPlusSuper to (+/-) */ - int vPlusMinus; /* (+/-) vertex */ - int nDeltaPlus1, nDeltaMinus1, delta; - BNS_VERTEX *pvPlusSuper, *pvPlusMinus; - BNS_EDGE *pEdge; - - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - - EDGE_LIST FixedLargeRingStereoEdges, CarbonChargeEdges; - - ret = 0; - - AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); - bFixedCarbonCharges = 0; - - if ( forbidden_stereo_edge_mask ) { - for ( i = 0; i < num_at; i ++ ) { - for ( j = 0; j < at2[i].valence; j ++ ) { - if ( pBNS->edge[k = pBNS->vert[i].iedge[j]].forbidden == forbidden_stereo_edge_mask ) { - int nMinRingSize = is_bond_in_Nmax_memb_ring( at2, i, j, pStruct->pbfsq->q, - pStruct->pbfsq->nAtomLevel, - pStruct->pbfsq->cSource, 99 /* max ring size */ ); - if ( 0 < nMinRingSize && (ret = AddToEdgeList( &FixedLargeRingStereoEdges, k, 64 ))) { - goto exit_function; - } - } - } - } - if ( !FixedLargeRingStereoEdges.num_edges ) { - goto exit_function; - } else { - /* allow stereobonds in rings change */ - RemoveForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask ); - } - } - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - /* count charges */ - num_pos = num_neg = 0; - for ( i = 0; i < num_at; i ++ ) { - if ( !pVA[i].cMetal && !at2[i].radical ) { - num_pos += ( at2[i].charge > 0 ); - num_neg += ( at2[i].charge < 0 ); - } - } - num_min = inchi_min( num_pos, num_neg ); - - - if ( num_min && - (k = pTCGroups->nGroup[TCG_Plus]) >= 0 && - (ePlusSuper = pTCGroups->pTCG[k].nForwardEdge) > 0 && - (vPlusSuper = pTCGroups->pTCG[k].nVertexNumber) >= num_at && - !(pEdge=pBNS->edge + ePlusSuper)->forbidden ) { - - vPlusMinus = pEdge->neighbor12 ^ vPlusSuper; - pvPlusSuper = pBNS->vert + vPlusSuper; - pvPlusMinus = pBNS->vert + vPlusMinus; - num_min = inchi_min( num_min, pEdge->flow ); - nDeltaPlus1 = pvPlusSuper->st_edge.cap - pvPlusSuper->st_edge.flow; - nDeltaMinus1 = pvPlusMinus->st_edge.cap - pvPlusMinus->st_edge.flow; - if ( num_min && (!nDeltaPlus1 && !nDeltaMinus1 ) ) { - if ( !bFixedCarbonCharges ) { /* 02-02-2006 */ - /* do not let carbon atoms get charged */ - if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { - goto exit_function; - } - bFixedCarbonCharges ++; - } - delta = 1; - pEdge->forbidden |= forbidden_edge_mask; - pBNS->edge_forbidden_mask |= forbidden_edge_mask; - for ( i = 0; i < num_min; i += delta ) { - /* cancel 1 pair of charges at a time */ - /* an attempt to cancel all at once may */ - /* convert a pair of N(IV)(+) into a pair of N(V) neutral with total charge reduced by 2 */ - pEdge->flow -= delta; - pvPlusSuper->st_edge.flow -= delta; - pvPlusMinus->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - /* test for charhe cancellation */ - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret == 1 && (vPathEnd == vPlusSuper && vPathStart == vPlusMinus || - vPathEnd == vPlusMinus && vPathStart == vPlusSuper) && nDeltaCharge < 0 ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else - if ( ret == 1 ) { - *pnTotalDelta += ret; - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - } else { - pEdge->flow += delta; - pvPlusSuper->st_edge.flow += delta; - pvPlusMinus->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - break; - } - } - num_min -= i; /* how many pairs of charges left */ - pEdge->forbidden &= inv_forbidden_edge_mask; - } - nDeltaPlus1 = pvPlusSuper->st_edge.cap - pvPlusSuper->st_edge.flow; - nDeltaMinus1 = pvPlusMinus->st_edge.cap - pvPlusMinus->st_edge.flow; - if ( num_min > 1 && (!nDeltaPlus1 && !nDeltaMinus1 ) ) { - delta = 2; - pEdge->forbidden |= forbidden_edge_mask; - pBNS->edge_forbidden_mask |= forbidden_edge_mask; - for ( i = 0; i < num_min; i += delta ) { - /* cancel 2 pairs of opposite charges at a time */ - /* 1. test cancellation of a pair of (+) charges */ - pvPlusSuper->st_edge.cap += delta; - pBNS->tot_st_cap += delta; - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret < 0 ) { - goto exit_function; - } - pvPlusSuper->st_edge.cap -= delta; - pBNS->tot_st_cap -= delta; - if ( ret != 1 || (vPathEnd != vPlusSuper || vPathStart != vPlusSuper) || nDeltaCharge >= 0 ) { - break; - } - /* 2. test cancellation of a pair of (-) charges */ - pvPlusMinus->st_edge.cap += delta; - pBNS->tot_st_cap += delta; - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret < 0 ) { - goto exit_function; - } - pvPlusMinus->st_edge.cap -= delta; - pBNS->tot_st_cap -= delta; - if ( ret != 1 || (vPathEnd != vPlusMinus || vPathStart != vPlusMinus) || nDeltaCharge >= 0 ) { - break; - } - /* 3. Actually cancel the pair of charges */ - pEdge->flow -= delta; - pvPlusSuper->st_edge.flow -= delta; - pvPlusMinus->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else - if ( ret == 2 ) { - *pnTotalDelta += ret; - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - } - num_min -= i; /* how many pairs of charges left */ - pEdge->forbidden &= inv_forbidden_edge_mask; - } - } - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at; -exit_function: - if ( bFixedCarbonCharges ) { - RemoveForbiddenEdgeMask(pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); - } - if ( forbidden_stereo_edge_mask && FixedLargeRingStereoEdges.num_edges ) { - SetForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask ); - } - AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_FREE ); - - return ret < 0? ret : num_min; -} -#if (MOVE_CHARGES_FROM_HETEREO_TO_METAL == 1 ) -/********************** not used *************************************************************************/ -int MoveChargeFromHeteroatomsToMetals( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, - VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, - int forbidden_edge_mask) - /********* Avoid charge separation on heteroatoms ******************/ -{ - int i, k, ret, ret2, num_pos, num_neg, num_min; - int vPlusSuper, vMinusSuper; /* (+), (-) super vertices */ - int ePlusSuper, eMinusSuper; /* edges from vPlusSuper or vMinusSuper to (+/-) */ - int vPlMn; /* (+/-) vertex */ - int vPlusHeteroat, vMinusHeteroat; /* (+), (-) heteroatom vertices */ - int ePlusHeteroat, eMinusHeteroat; /* edges from (+) or (-) heteroatom vertex to super (+) or (-) */ - int vPlusCarbons, vMinusCarbons; /* (+), (-) carbons vertices */ - int ePlusCarbons, eMinusCarbons; /* edges from (+), (-) carbons vertices to super (+) or (-) */ - int vPlusMetals, vMinusMetals; /* (+), (-) carbons vertices */ - int ePlusMetals, eMinusMetals; /* edges from (+), (-) carbons vertices to super (+) or (-) */ - int eMinusHeteroToSuper; /* edge (-)vHetero-[eMinusHeteroat]-Y-[eMinusHeteroToSuper]-(-)vPlusSuper */ - int v1, v2; - int nDeltaPlus1, nDeltaMinus1, delta; - BNS_VERTEX *pvPlusSuper, *pvMinusSuper, *pvPlMn; - BNS_VERTEX *pvPlusHeteroat, *pvMinusHeteroat, *pvPlusCarbons, *pvMinusCarbons; - BNS_VERTEX *pvPlusMetals, *pvMinusMetals; - BNS_EDGE *pEdgePlusHeteroat, *pEdgeMinusHeteroat, *pEdgeMinusHeteroToSuper; - BNS_EDGE *pEdgePlusCarbons, *pEdgeMinusCarbons, *pEdgePlusMetals, *pEdgeMinusMetals; - BNS_EDGE *pEdgePlusSuper, *pEdgeMinusSuper; - - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - - - ret = 0; - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - /* (+) */ - pEdgePlusSuper = NULL; - pvPlusSuper = NULL; - if ( (k = pTCGroups->nGroup[TCG_Plus]) >= 0 && - (ePlusSuper = pTCGroups->pTCG[k].nForwardEdge) > 0 && - (vPlusSuper = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { - pEdgePlusSuper = pBNS->edge + ePlusSuper; - pvPlusSuper = pBNS->vert + vPlusSuper; - } - pEdgePlusCarbons = NULL; - pvPlusCarbons = NULL; - if ( (k = pTCGroups->nGroup[TCG_Plus_C0] ) > 0 && - (ePlusCarbons = pTCGroups->pTCG[k].nForwardEdge) > 0 && - (vPlusCarbons = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { - pEdgePlusCarbons = pBNS->edge + ePlusCarbons; - pvPlusCarbons = pBNS->vert + vPlusCarbons; - } - pEdgePlusHeteroat = NULL; - pvPlusHeteroat = NULL; - if ( (k = pTCGroups->nGroup[TCG_Plus0] ) > 0 && - (ePlusHeteroat = pTCGroups->pTCG[k].nForwardEdge) > 0 && - (vPlusHeteroat = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { - pEdgePlusHeteroat = pBNS->edge + ePlusHeteroat; - pvPlusHeteroat = pBNS->vert + vPlusHeteroat; - } - pEdgePlusMetals = NULL; - pvPlusMetals = NULL; - if ( (k = pTCGroups->nGroup[TCG_Plus_M0] ) > 0 && - (ePlusMetals = pTCGroups->pTCG[k].nForwardEdge) > 0 && - (vPlusMetals = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { - pEdgePlusMetals = pBNS->edge + ePlusMetals; - pvPlusMetals = pBNS->vert + vPlusMetals; - } - /* (-) */ - pEdgeMinusSuper = NULL; - pvMinusSuper = NULL; - if ( (k = pTCGroups->nGroup[TCG_Minus]) >= 0 && - (eMinusSuper = pTCGroups->pTCG[k].nForwardEdge) > 0 && - (vMinusSuper = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { - pEdgeMinusSuper = pBNS->edge + eMinusSuper; - pvMinusSuper = pBNS->vert + vMinusSuper; - } - pEdgeMinusCarbons = NULL; - pvMinusCarbons = NULL; - if ( (k = pTCGroups->nGroup[TCG_Minus_C0] ) > 0 && - (eMinusCarbons = pTCGroups->pTCG[k].nForwardEdge) > 0 && - (vMinusCarbons = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { - pEdgeMinusCarbons = pBNS->edge + eMinusCarbons; - pvMinusCarbons = pBNS->vert + vMinusCarbons; - } - pEdgeMinusHeteroat = NULL; - pvMinusHeteroat = NULL; - pEdgeMinusHeteroToSuper = NULL; - if ( (k = pTCGroups->nGroup[TCG_Minus0] ) > 0 && - (eMinusHeteroat = pTCGroups->pTCG[k].nForwardEdge) > 0 && - (vMinusHeteroat = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { - BNS_VERTEX *pvYMinusHetero; - BNS_EDGE *pe; - int vYMinusHetero; - pEdgeMinusHeteroat = pBNS->edge + eMinusHeteroat; - pvMinusHeteroat = pBNS->vert + vMinusHeteroat; - /* next edge toward (-)super */ - if ( pvMinusSuper ) { - vYMinusHetero = pEdgeMinusHeteroat->neighbor12 ^ vMinusHeteroat; - pvYMinusHetero = pBNS->vert + vYMinusHetero; - for ( i = 0; i < pvYMinusHetero->num_adj_edges; i ++ ) { - pe = pBNS->edge + pvYMinusHetero->iedge[i]; - if ( (pe->neighbor12 ^ vYMinusHetero) == vMinusSuper ) { - pEdgeMinusHeteroToSuper = pe; - eMinusHeteroToSuper = pe - pBNS->edge; - break; - } - } - } - } - pEdgeMinusMetals = NULL; - pvMinusMetals = NULL; - if ( (k = pTCGroups->nGroup[TCG_Minus_M0] ) > 0 && - (eMinusMetals = pTCGroups->pTCG[k].nForwardEdge) > 0 && - (vMinusMetals = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { - pEdgeMinusMetals = pBNS->edge + eMinusMetals; - pvMinusMetals = pBNS->vert + vMinusMetals; - } - /* (+/-) */ - pvPlMn = NULL; - if ( pEdgePlusSuper ) { - vPlMn = pEdgePlusSuper->neighbor12 ^ vPlusSuper; - pvPlMn = pBNS->vert + vPlMn; - } else - if ( pEdgeMinusSuper ) { - vPlMn = pEdgeMinusSuper->neighbor12 ^ vMinusSuper; - pvPlMn = pBNS->vert + vPlMn; - } - num_pos = num_neg = 0; - /***************************************************************/ - /* Positive Charges */ - /***************************************************************/ - if ( pEdgePlusHeteroat && pEdgePlusMetals ) { - /* count charges */ - for ( i = 0; i < num_at; i ++ ) { - if ( !at2[i].radical && - at2[i].charge > 0 && - (k = pVA[i].nCPlusGroupEdge-1) >= 0 ) { - v1 = pBNS->edge[k].neighbor1; - v2 = pBNS->edge[k].neighbor1 ^ pBNS->edge[k].neighbor12; - if ( v1 == vPlusHeteroat || v2 == vPlusHeteroat ) { - num_pos ++; - } - } - } - /* attempt to move (+) from heteroatoms to metal atoms */ - num_min = inchi_min( num_pos, pEdgePlusHeteroat->flow ); - - nDeltaPlus1 = pvPlusSuper->st_edge.cap - pvPlusSuper->st_edge.flow; - nDeltaMinus1 = pvPlMn->st_edge.cap - pvPlMn->st_edge.flow; - if ( num_min && !nDeltaPlus1 && !nDeltaMinus1 ) { - if ( pEdgePlusSuper ) { - pEdgePlusSuper->forbidden |= forbidden_edge_mask; - } - if ( pEdgeMinusSuper ) { - pEdgeMinusSuper->forbidden |= forbidden_edge_mask; - } - if ( pEdgePlusCarbons ) { - pEdgePlusCarbons->forbidden |= forbidden_edge_mask; - } - if ( pEdgeMinusCarbons ) { - pEdgeMinusCarbons->forbidden |= forbidden_edge_mask; - } - if ( pEdgePlusHeteroat ) { - pEdgePlusHeteroat->forbidden |= forbidden_edge_mask; - } - if ( pEdgeMinusHeteroat ) { - pEdgeMinusHeteroat->forbidden |= forbidden_edge_mask; - } - delta = 1; - for ( i = 0; i < num_min; i += delta ) { - v1 = pEdgePlusHeteroat->neighbor1; - v2 = pEdgePlusHeteroat->neighbor12 ^ v1; - pEdgePlusHeteroat->flow -= delta; - pBNS->vert[v1].st_edge.flow -= delta; - pBNS->vert[v2].st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - /* test for charhe cancellation */ - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else - if ( ret == 1 ) { - *pnTotalDelta += ret; - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - } else { - pEdgePlusHeteroat->flow += delta; - pBNS->vert[v1].st_edge.flow += delta; - pBNS->vert[v2].st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - break; - } - } - if ( pEdgePlusSuper ) { - pEdgePlusSuper->forbidden &= inv_forbidden_edge_mask; - } - if ( pEdgeMinusSuper ) { - pEdgeMinusSuper->forbidden &= inv_forbidden_edge_mask; - } - if ( pEdgePlusCarbons ) { - pEdgePlusCarbons->forbidden &= inv_forbidden_edge_mask; - } - if ( pEdgeMinusCarbons ) { - pEdgeMinusCarbons->forbidden &= inv_forbidden_edge_mask; - } - if ( pEdgePlusHeteroat ) { - pEdgePlusHeteroat->forbidden &= inv_forbidden_edge_mask; - } - if ( pEdgeMinusHeteroat ) { - pEdgeMinusHeteroat->forbidden &= inv_forbidden_edge_mask; - } - } - } - /***************************************************************/ - /* Negative Charges */ - /***************************************************************/ - if ( pEdgeMinusHeteroToSuper && pEdgeMinusMetals ) { - /* count charges */ - for ( i = 0; i < num_at; i ++ ) { - if ( !at2[i].radical && - at2[i].charge < 0 && - (k = pVA[i].nCMinusGroupEdge-1) >= 0 ) { - v1 = pBNS->edge[k].neighbor1; - v2 = pBNS->edge[k].neighbor1 ^ pBNS->edge[k].neighbor12; - if ( v1 == vMinusHeteroat || v2 == vMinusHeteroat ) { - num_neg ++; - } - } - } - if ( num_neg ) { - /* attempt to move (+) from heteroatoms to metal atoms */ - num_min = inchi_min( num_neg, pEdgeMinusHeteroToSuper->flow ); - } - - nDeltaPlus1 = pvPlusSuper->st_edge.cap - pvPlusSuper->st_edge.flow; - nDeltaMinus1 = pvPlMn->st_edge.cap - pvPlMn->st_edge.flow; - if ( num_min && !nDeltaPlus1 && !nDeltaMinus1 ) { - if ( pEdgePlusSuper ) { - pEdgePlusSuper->forbidden |= forbidden_edge_mask; - } - if ( pEdgeMinusSuper ) { - pEdgeMinusSuper->forbidden |= forbidden_edge_mask; - } - if ( pEdgePlusCarbons ) { - pEdgePlusCarbons->forbidden |= forbidden_edge_mask; - } - if ( pEdgeMinusCarbons ) { - pEdgeMinusCarbons->forbidden |= forbidden_edge_mask; - } - if ( pEdgePlusHeteroat ) { - pEdgePlusHeteroat->forbidden |= forbidden_edge_mask; - } - if ( pEdgeMinusHeteroToSuper ) { - pEdgeMinusHeteroToSuper->forbidden |= forbidden_edge_mask; - } - delta = 1; - for ( i = 0; i < num_min; i += delta ) { - v1 = pEdgeMinusHeteroToSuper->neighbor1; - v2 = pEdgeMinusHeteroToSuper->neighbor12 ^ v1; - pEdgeMinusHeteroToSuper->flow -= delta; - pBNS->vert[v1].st_edge.flow -= delta; - pBNS->vert[v2].st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - /* test for charhe cancellation */ - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else - if ( ret == 1 ) { - *pnTotalDelta += ret; - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - } else { - pEdgeMinusHeteroToSuper->flow += delta; - pBNS->vert[v1].st_edge.flow += delta; - pBNS->vert[v2].st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - break; - } - } - if ( pEdgePlusSuper ) { - pEdgePlusSuper->forbidden &= inv_forbidden_edge_mask; - } - if ( pEdgeMinusSuper ) { - pEdgeMinusSuper->forbidden &= inv_forbidden_edge_mask; - } - if ( pEdgePlusCarbons ) { - pEdgePlusCarbons->forbidden &= inv_forbidden_edge_mask; - } - if ( pEdgeMinusCarbons ) { - pEdgeMinusCarbons->forbidden &= inv_forbidden_edge_mask; - } - if ( pEdgePlusHeteroat ) { - pEdgePlusHeteroat->forbidden &= inv_forbidden_edge_mask; - } - if ( pEdgeMinusHeteroToSuper ) { - pEdgeMinusHeteroToSuper->forbidden &= inv_forbidden_edge_mask; - } - } - } -exit_function: - return ret; -} -#endif -/******************************************************************************************************/ -int RestoreCyanoGroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - BNS_EDGE *pe; - - int i, j, ret2, ret; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - Vertex v1, v2; - - EDGE_LIST CarbonChargeEdges; - - ret = 0; - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - - for ( i = 0; i < num_at && 0 <= ret; i ++ ) { - if ( at2[i].valence == 1 && - at2[i].num_H == 0 && - at2[i].chem_bonds_valence == 2 && - at2[i].charge == -1 && - at2[i].radical == 0 && - pVA[i].cNumValenceElectrons == 5 && /* terminal N(-)=, P, As, Sb, Bi */ - pVA[i].nCMinusGroupEdge > 0 && - pVA[i].nTautGroupEdge == 0 && - at2[j=at2[i].neighbor[0]].valence == 2 && - at2[j].num_H == 0 && - at2[j].chem_bonds_valence == 4 && - at2[j].charge == 0 && - at2[j].radical == 0 && - pVA[j].cNumValenceElectrons == 4 && /* C or Si or Ge or Sn or Pb */ - pVA[i].cnListIndex > 0 && - cnList[pVA[i].cnListIndex-1].bits == cn_bits_MN ) { - /* found N(-)=C= */ - pe = pBNS->edge + (pVA[i].nCMinusGroupEdge-1); /* N#N(+) triple bond edge */ - - if ( !pe->flow ) { - continue; /* wrong atom ??? Strange... */ - } - v1 = pe->neighbor1; - v2 = pe->neighbor12 ^ v1; - pe->flow --; - pBNS->vert[v1].st_edge.flow --; - pBNS->vert[v2].st_edge.flow --; - pBNS->tot_st_flow -= 2; - pe->forbidden |= forbidden_edge_mask; - - /* do not let carbon atoms get charged */ - if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { - goto exit_function; - } - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - *pnTotalDelta += ret; - } else { - pe->flow ++; - pBNS->vert[v1].st_edge.flow ++; - pBNS->vert[v2].st_edge.flow ++; - pBNS->tot_st_flow += 2; - } - RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - - pe->forbidden &= inv_forbidden_edge_mask; /* unmask the edges */ - } - } - -exit_function: - - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); - return ret; -} -/******************************************************************************************************/ -int RestoreIsoCyanoGroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ -#define INC_EDGE_LIST 16 - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms, num_failed, num_success; - BNS_EDGE *pe; - Vertex v1, v2; - - int i, j, ret2, ret, bIsCarbon; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - EdgeIndex eNMinusEdge, eNPlusEdge, eNPlusEdge1, eN34Edge; - EdgeIndex eNFlowerEdge1; - - EDGE_LIST CarbonChargeEdges, AllChargeEdges, IsoCyanoCarbonChargeEdges; - - ret = 0; - num_failed = num_success = 0; - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); /* carbon charge edges */ - AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); /* heteroatom charge edges */ - AllocEdgeList( &IsoCyanoCarbonChargeEdges, EDGE_LIST_CLEAR ); /* C in C(+)#N(+) charge edges */ - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - /* 1st attempt: take care of C(+)#N(+)- => C(-)#N(+)- and remove 2 negative charges */ - /* This would produce nDeltaCharge = 2 */ - AllocEdgeList( &CarbonChargeEdges, 2*num_at ); - for ( i = 0; i < num_at && 0 <= ret; i ++ ) { - /* accumulate edges for subsequent fixing them */ - bIsCarbon = (pVA[i].cNumValenceElectrons == 4 && pVA[i].cPeriodicRowNumber == 1); - eNFlowerEdge1 = NO_VERTEX; - if ( (eNMinusEdge = pVA[i].nCMinusGroupEdge - 1)>= 0 && - !pBNS->edge[eNMinusEdge].forbidden ) { - if ( bIsCarbon ) { - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - } else - if ( !pVA[i].cMetal && !at2[i].endpoint && at2[i].charge != -1 ) { - if ( ret = AddToEdgeList( &AllChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - } - } - if ( (eNPlusEdge = pVA[i].nCPlusGroupEdge - 1)>= 0 && - !pBNS->edge[eNPlusEdge].forbidden ) { - if ( bIsCarbon ) { - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - } else - if ( !pVA[i].cMetal && !at2[i].endpoint ) { - if ( ret = AddToEdgeList( &AllChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( pVA[i].cNumValenceElectrons == 5 && - NO_VERTEX != (eNFlowerEdge1 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge )) && - !pBNS->edge[eNFlowerEdge1].flow ) { - if ( ret = AddToEdgeList( &AllChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - } - } - } - if ( bIsCarbon && - 0 <= eNMinusEdge && - 0 <= eNPlusEdge && - at2[i].valence == 1 && - at2[i].num_H == 0 && - at2[i].radical == 0 && - !pBNS->edge[eNMinusEdge].forbidden && - pBNS->edge[eNMinusEdge].flow == 0 && - !pBNS->edge[eNPlusEdge].forbidden && - pBNS->edge[eNPlusEdge].flow == 0 && /* found terminal C(+) */ - - at2[j=at2[i].neighbor[0]].valence == 2 && - at2[j].num_H == 0 && - at2[j].radical == 0 && - pVA[j].cNumValenceElectrons == 5 && - (eNPlusEdge1 = pVA[j].nCPlusGroupEdge - 1)>= 0 && - pBNS->edge[eNPlusEdge].flow == 0 ) { /* -N(+)- */ - -#ifdef NEVER /* I have not found a good reason to do this yet */ - /* fix (+) charge on -N(+)- as much as C charges are fixed */ - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - /* fix floer edge to prevent N(V) ??? */ - if ( NO_VERTEX != (eNFlowerEdge1 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge1 )) && - !pBNS->edge[eNFlowerEdge1].flow ) { - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - } -#endif - /* - Carbon(+) Carbon(-) - ChargeStruct: ChargeStruct: - - 5(+C) 5(+C) - / // - 6(-C) 4 6(-C) 4 - \ // \\ / - 3 3 - | | - 2 2 - || || - -C1- -C1- - | | - - 3-6 is (-) Charge Edge; 4-5 is (+) Charge Edge - - To convert the left pattern to the right one: - - We need to release these charge edges and decrement - edge 3-4 flow to change charge from (+) to (-) - - */ - - /* find vertices 4 and 5 */ - v1 = pBNS->edge[eNPlusEdge].neighbor1; /* one of two vertices incident with edge 4-5 */ - v2 = pBNS->edge[eNPlusEdge].neighbor12 ^ v1; - if ( IS_BNS_VT_C_GR(pBNS->vert[v1].type) ) { - /* v1 is 5(+C) */ - Vertex tmp = v1; - v1 = v2; - v2 = tmp; - } - /* v1 should be 4, v2 - 5(+C) */ - if ( !IS_BNS_VT_CHRG_STRUCT(pBNS->vert[v1].type) || pBNS->vert[v1].num_adj_edges != 2 ) { - continue; /* mismatch */ - } - /* find edge 3-4 */ - eN34Edge = pBNS->vert[v1].iedge[pBNS->vert[v1].iedge[0] == eNPlusEdge]; - if ( pBNS->edge[eN34Edge].forbidden || !pBNS->edge[eN34Edge].flow ) { - continue; - } - /* save 3 edges: 6-3, 4-5, and 3-4 in this order */ - if ( ret = AddToEdgeList( &IsoCyanoCarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &IsoCyanoCarbonChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &IsoCyanoCarbonChargeEdges, eN34Edge, INC_EDGE_LIST ) ) { - goto exit_function; - } - } - } - /* 1st attempt: move (-) charges from heteroatoms to C(+) */ - SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &IsoCyanoCarbonChargeEdges, forbidden_edge_mask ); - for ( i = IsoCyanoCarbonChargeEdges.num_edges-3; 0 <= i; i -= 3 ) { - eNMinusEdge = IsoCyanoCarbonChargeEdges.pnEdges[i]; - eNPlusEdge = IsoCyanoCarbonChargeEdges.pnEdges[i+1]; - eN34Edge = IsoCyanoCarbonChargeEdges.pnEdges[i+2]; - - pe = pBNS->edge + eN34Edge; - pe->forbidden |= forbidden_edge_mask; - if ( !pe->flow ) { - continue; /* already done */ - } - - v1 = pe->neighbor1; - v2 = pe->neighbor12 ^ v1; - pe->flow --; - pBNS->vert[v1].st_edge.flow --; - pBNS->vert[v2].st_edge.flow --; - pBNS->tot_st_flow -= 2; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= -2 ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - *pnTotalDelta += ret; - num_success ++; - } else { - pe->flow ++; - pBNS->vert[v1].st_edge.flow ++; - pBNS->vert[v2].st_edge.flow ++; - pBNS->tot_st_flow += 2; - pe->forbidden &= inv_forbidden_edge_mask; - num_failed ++; - } - } - if ( num_failed ) { - /* relax conditions: allow all heteroatoms to change charge */ - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - for ( i = IsoCyanoCarbonChargeEdges.num_edges-3; 0 <= i; i -= 3 ) { - eNMinusEdge = IsoCyanoCarbonChargeEdges.pnEdges[i]; - eNPlusEdge = IsoCyanoCarbonChargeEdges.pnEdges[i+1]; - eN34Edge = IsoCyanoCarbonChargeEdges.pnEdges[i+2]; - - pe = pBNS->edge + eN34Edge; - pe->forbidden |= forbidden_edge_mask; - if ( !pe->flow ) { - continue; /* already done */ - } - - v1 = pe->neighbor1; - v2 = pe->neighbor12 ^ v1; - pe->flow --; - pBNS->vert[v1].st_edge.flow --; - pBNS->vert[v2].st_edge.flow --; - pBNS->tot_st_flow -= 2; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= 2 ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - *pnTotalDelta += ret; - num_success ++; - } else { - pe->flow ++; - pBNS->vert[v1].st_edge.flow ++; - pBNS->vert[v2].st_edge.flow ++; - pBNS->tot_st_flow += 2; - pe->forbidden &= inv_forbidden_edge_mask; /* let it change if it wants */ - num_failed ++; - } - } - } - RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &IsoCyanoCarbonChargeEdges, forbidden_edge_mask ); - - -exit_function: - - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &IsoCyanoCarbonChargeEdges, EDGE_LIST_FREE ); - return ret; -#undef INC_EDGE_LIST -} - -/******************************************************************************************************/ -int FixMetal_Nminus_Ominus( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ -#define INC_EDGE_LIST 16 - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - int num_failed, num_success, n, nDeltaChargeMax, nMetalCharge; - BNS_EDGE *pe; - Vertex v1, v2; - - int i, j, k, ret2, ret; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - EdgeIndex e, eNMinusEdge, eNMinusEdge1, eNMinusEdge2, eNPlusEdge2; - - EDGE_LIST AllChargeEdges; - - ret = 0; - num_failed = num_success = 0; - AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - /* attepmt #1 N#N(+)-N => N(-)=N(+)=N */ - for ( i = 0; i < num_at && 0 <= ret; i ++ ) { - if ( at2[i].valence == 1 && - at2[i].num_H == 0 && - at2[i].radical == 0 && - pVA[i].cNumValenceElectrons == 6 && /* terminal -O */ - (eNMinusEdge = pVA[i].nCMinusGroupEdge - 1)>= 0 && pBNS->edge[eNMinusEdge].flow == 1 && - !pBNS->edge[eNMinusEdge].forbidden && /* terminal O(-) */ - - at2[j=at2[i].neighbor[0]].valence == 2 && - at2[j].num_H == 0 && - at2[j].radical == 0 && - pVA[j].cNumValenceElectrons == 5 && - (eNMinusEdge1 = pVA[j].nCMinusGroupEdge - 1)>= 0 && pBNS->edge[eNMinusEdge1].flow == 1 && - !pBNS->edge[eNMinusEdge1].forbidden && - - pVA[k=at2[j].neighbor[at2[j].neighbor[0]==i]].cMetal && - - (eNMinusEdge2 = pVA[k].nCMinusGroupEdge - 1)>= 0 && - !pBNS->edge[eNMinusEdge2].forbidden && - (eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1)>= 0 && - !pBNS->edge[eNPlusEdge2].forbidden ) { - - /* found M(q)-N(-)-O(-); convert to M(q-2)-N=O */ - - /* find all charge edges to fix */ - if ( 0 == AllChargeEdges.num_edges ) { - for ( n = 0; n < num_at; n ++ ) { - if ( (e = pVA[n].nCMinusGroupEdge - 1)>= 0 && - !pBNS->edge[e].forbidden ) { - if ( ret = AddToEdgeList( &AllChargeEdges, e, num_at ) ) { - goto exit_function; - } - } - if ( (e = pVA[n].nCPlusGroupEdge - 1)>= 0 && - !pBNS->edge[e].forbidden ) { - if ( ret = AddToEdgeList( &AllChargeEdges, e, num_at ) ) { - goto exit_function; - } - if ( pVA[n].cNumValenceElectrons == 6 && - NO_VERTEX != (e = GetChargeFlowerUpperEdge( pBNS, pVA, e )) && - pBNS->edge[e].flow == 0 ) { - if ( ret = AddToEdgeList( &AllChargeEdges, e, num_at ) ) { - goto exit_function; - } - } - } - } - } - - nMetalCharge = (pBNS->edge[eNPlusEdge2].cap - pBNS->edge[eNPlusEdge2].flow) - - pBNS->edge[eNMinusEdge2].flow; - if ( nMetalCharge == 0 ) { - /* change on O is invisible; charge from N(-) goes, charge comes to Metal */ - nDeltaChargeMax = 0; - } else - if ( nMetalCharge == 2 ) { - /* charges on Metal and N disappear */ - nDeltaChargeMax = -2; - } else { - /* charge from N disappears */ - nDeltaChargeMax = -1; - } - - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - pBNS->edge[eNMinusEdge1].forbidden &= inv_forbidden_edge_mask; - pBNS->edge[eNMinusEdge2].forbidden &= inv_forbidden_edge_mask; - pBNS->edge[eNPlusEdge2].forbidden &= inv_forbidden_edge_mask; - - pe = pBNS->edge + eNMinusEdge; /* must be already fixed as a charge edge */ - - v1 = pe->neighbor1; - v2 = pe->neighbor12 ^ v1; - pe->flow --; - pBNS->vert[v1].st_edge.flow --; - pBNS->vert[v2].st_edge.flow --; - pBNS->tot_st_flow -= 2; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) /*&& nDeltaCharge == nDeltaChargeMax*/ ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - *pnTotalDelta += ret; - num_success ++; - } else { - pe->flow ++; - pBNS->vert[v1].st_edge.flow ++; - pBNS->vert[v2].st_edge.flow ++; - pBNS->tot_st_flow += 2; - num_failed ++; - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - } - ret = num_success; - -exit_function: - - AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); - - return ret; -#undef INC_EDGE_LIST -} -/******************************************************************************************************/ -int RestoreNNNgroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ -#define INC_EDGE_LIST 16 - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms, num_failed, num_success, n, nDeltaChargeMax; - BNS_EDGE *pe; - Vertex v1, v2; - - int i, j, k, ret2, ret; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - EdgeIndex eNMinusEdge, eNPlusEdge, eNMinusEdge1, eNPlusEdge1, eNMinusEdge2, eNPlusEdge2; - EdgeIndex eNFlowerEdge1, eNFlowerEdge2; - - EDGE_LIST CarbonChargeEdges, AllChargeEdges, NNNChargeEdges, CurNNNChargeEdges, AllNNNTermAtoms, AllNIIIChargeEdges; - - ret = 0; - num_failed = num_success = 0; - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &NNNChargeEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &CurNNNChargeEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &AllNNNTermAtoms, EDGE_LIST_CLEAR ); - AllocEdgeList( &AllNIIIChargeEdges, EDGE_LIST_CLEAR ); - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - /* attepmt #1 N#N(+)-N => N(-)=N(+)=N: naive approach; expecting tp move (-) from some other atom */ - for ( i = 0; i < num_at && 0 <= ret; i ++ ) { - if ( at2[i].valence == 1 && - at2[i].num_H == 0 && - at2[i].chem_bonds_valence == 3 && - at2[i].charge == 0 && - at2[i].radical == 0 && - pVA[i].cNumValenceElectrons == 5 && /* terminal N# */ - (eNMinusEdge = pVA[i].nCMinusGroupEdge - 1)>= 0 && pBNS->edge[eNMinusEdge].flow == 0 && - !pBNS->edge[eNMinusEdge].forbidden && - at2[j=at2[i].neighbor[0]].valence == 2 && - at2[j].num_H == 0 && - at2[j].chem_bonds_valence == 4 && - at2[j].charge == 1 && - at2[j].radical == 0 && - pVA[j].cNumValenceElectrons == 5 && - (eNPlusEdge = pVA[j].nCPlusGroupEdge - 1)>= 0 && pBNS->edge[eNPlusEdge].flow == 0 && - !pBNS->edge[eNPlusEdge].forbidden && - at2[k=at2[j].neighbor[at2[j].neighbor[0]==i]].valence == 2 && - at2[k].num_H == 0 && - at2[k].chem_bonds_valence == 3 && - pVA[k].cNumValenceElectrons == 5 && - (eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1)>= 0 && pBNS->edge[eNPlusEdge2].flow == 1 && - !pBNS->edge[eNPlusEdge2].forbidden && - pVA[i].cnListIndex > 0 && - cnList[pVA[i].cnListIndex-1].bits == cn_bits_MN ) { - /* found N#N(+)-N~ where the last N (at2[k]) may be charged */ - pe = pBNS->edge + pBNS->vert[i].iedge[0]; /* N#N(+) triple bond edge */ - - v1 = pe->neighbor1; - v2 = pe->neighbor12 ^ v1; - pe->flow --; - pBNS->vert[v1].st_edge.flow --; - pBNS->vert[v2].st_edge.flow --; - pBNS->tot_st_flow -= 2; - - pe->forbidden |= forbidden_edge_mask; - pBNS->edge[eNPlusEdge].forbidden |= forbidden_edge_mask; - pBNS->edge[eNPlusEdge2].forbidden |= forbidden_edge_mask; - - - if ( !CarbonChargeEdges.num_edges ) { - /* do not let carbon atoms get charged */ - AllocEdgeList( &CarbonChargeEdges, INC_EDGE_LIST ); - if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { - goto exit_function; - } - } else { - SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - } - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= 0 ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - *pnTotalDelta += ret; - num_success ++; - /* fix charges on N(-)=N(+)=N- */ - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - } else { - pe->flow ++; - pBNS->vert[v1].st_edge.flow ++; - pBNS->vert[v2].st_edge.flow ++; - pBNS->tot_st_flow += 2; - num_failed ++; - } - RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - - pe->forbidden &= inv_forbidden_edge_mask; - pBNS->edge[eNPlusEdge].forbidden &= inv_forbidden_edge_mask; - pBNS->edge[eNPlusEdge2].forbidden &= inv_forbidden_edge_mask; - } - } - - /* 2nd attempt: take care of N#N(+)-N=-...=N-N(-) */ - /* This would produce nDeltaCharge >= 2 */ - - AllChargeEdges.num_edges = 0; - AllNNNTermAtoms.num_edges = 0; - NNNChargeEdges.num_edges = 0; - AllNIIIChargeEdges.num_edges = 0; - - for ( i = 0; i < num_at && 0 <= ret; i ++ ) { - if ( (eNMinusEdge = pVA[i].nCMinusGroupEdge - 1)>= 0 && - !pBNS->edge[eNMinusEdge].forbidden ) { - if ( ret = AddToEdgeList( &AllChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - } else { - eNMinusEdge = -1; - } - if ( (eNPlusEdge = pVA[i].nCPlusGroupEdge - 1)>= 0 && - !pBNS->edge[eNPlusEdge].forbidden ) { - if ( ret = AddToEdgeList( &AllChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( pVA[i].cNumValenceElectrons == 5 && at2[i].valence == 3 && at2[i].chem_bonds_valence == 3) { - if ( ret = AddToEdgeList( &AllNIIIChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - } - /* N flower edge */ - if ( pVA[i].cNumValenceElectrons == 5 && pVA[i].cPeriodicRowNumber == 1 && - NO_VERTEX != (eNFlowerEdge1 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge )) && - pBNS->edge[eNFlowerEdge1].flow == 0 && - ( ret = AddToEdgeList( &AllChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) ) { - goto exit_function; - } - } else { - eNPlusEdge = -1; - } - - if ( 0 <= eNMinusEdge && - at2[i].valence == 1 && - at2[i].num_H == 0 && - at2[i].radical == 0 && - pVA[i].cNumValenceElectrons == 5 && /* terminal N# */ - - at2[j=at2[i].neighbor[0]].valence == 2 && - at2[j].num_H == 0 && - at2[j].radical == 0 && - pVA[j].cNumValenceElectrons == 5 && - (eNMinusEdge1 = pVA[j].nCMinusGroupEdge - 1)>= 0 && - (eNPlusEdge1 = pVA[j].nCPlusGroupEdge - 1)>= 0 && - !pBNS->edge[eNMinusEdge1].forbidden && - !pBNS->edge[eNPlusEdge1].forbidden && - - at2[k=at2[j].neighbor[at2[j].neighbor[0]==i]].valence == 2 && - at2[k].num_H == 0 && - at2[k].radical == 0 && - pVA[k].cNumValenceElectrons == 5 && - (eNMinusEdge2 = pVA[k].nCMinusGroupEdge - 1)>= 0 && - (eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1)>= 0 && - !pBNS->edge[eNMinusEdge2].forbidden && - !pBNS->edge[eNPlusEdge2].forbidden && - - pVA[i].cnListIndex > 0 && - cnList[pVA[i].cnListIndex-1].bits == cn_bits_MN ) { - /* found N#N(+)-N~ or N(-)=N-N= where the last N (at2[k]) may be charged */ - - /* 1. N(-)=N(+)=N- */ - if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ - pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { - continue; /* already good */ - } - /* accumulate terminal atoms of all other NNN */ - if ( ret = AddToEdgeList( &AllNNNTermAtoms, i, INC_EDGE_LIST ) ) { - goto exit_function; - } - /* 2. N#N(+)-N= */ - if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ - pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { - /* unfix (-) edge on terminal N# */ - if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - continue; - } - /* 3. N(-)=N-N= */ - if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 1 && /* N */ - pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { - /* unfix (+) edge on middle N */ - if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - continue; - } - /* 4. N#N(+)-N(-)- */ - if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ - pBNS->edge[eNMinusEdge2].flow == 1 && pBNS->edge[eNPlusEdge2].flow == 1 /* N(-) */ ) { - /* unfix (-) edge on the 1st and 3rd N */ - if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) { - goto exit_function; - } - continue; - } - /* 5. N#N(+)-N(+)# */ - if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ - pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 0 /* N(+) */ ) { - /* unfix (-) edge on the 1st and (+) edge on the 3rd N */ - if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) { - goto exit_function; - } - continue; - } - } - } - /* try to fix each NNN */ - for ( n = AllNNNTermAtoms.num_edges-1; 0 <= n; n -- ) { - i = AllNNNTermAtoms.pnEdges[n]; - eNMinusEdge = pVA[i].nCMinusGroupEdge - 1; - /*eNPlusEdge = pVA[i].nCPlusGroupEdge - 1;*/ - j=at2[i].neighbor[0]; - eNMinusEdge1 = pVA[j].nCMinusGroupEdge - 1; - eNPlusEdge1 = pVA[j].nCPlusGroupEdge - 1; - k=at2[j].neighbor[at2[j].neighbor[0]==i]; - eNMinusEdge2 = pVA[k].nCMinusGroupEdge - 1; - eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1; - /*SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );*/ - /* 1. N(-)=N(+)=N- */ - if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ - pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { - - RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge ); - RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge1 ); - RemoveFromEdgeListByValue( &NNNChargeEdges, eNPlusEdge1 ); - RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge2 ); - RemoveFromEdgeListByValue( &NNNChargeEdges, eNPlusEdge2 ); - - pe = NULL; - } else /* 2. N#N(+)-N= */ - if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ - pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { - /* decrement triple bond on terminal N# */ - pe = pBNS->edge + pBNS->vert[i].iedge[0]; - } else - /* 3. N(-)=N-N= */ - if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 1 && /* N */ - pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { - /* decrement flow on (+) charge edge of the middle =N- */ - pe = pBNS->edge + eNPlusEdge1; - } else - /* 4. N#N(+)-N(-)- */ - if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ - pBNS->edge[eNMinusEdge2].flow == 1 && pBNS->edge[eNPlusEdge2].flow == 1 /* N(-) */ ) { - /* decrement triple bond on terminal N# */ - pe = pBNS->edge + pBNS->vert[i].iedge[0]; - } else - /* 5. N#N(+)-N(+)# */ - if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ - pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 0 /* N(+) */ ) { - /* decrement triple bond on terminal N# */ - pe = pBNS->edge + pBNS->vert[i].iedge[0]; - } else { - pe = NULL; /* unknown case */ - } - if ( pe ) { - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &NNNChargeEdges, forbidden_edge_mask ); - - v1 = pe->neighbor1; - v2 = pe->neighbor12 ^ v1; - pe->flow --; - pBNS->vert[v1].st_edge.flow --; - pBNS->vert[v2].st_edge.flow --; - pBNS->tot_st_flow -= 2; - - pe->forbidden |= forbidden_edge_mask; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) /*&& nDeltaCharge <= 2*/ ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - *pnTotalDelta += ret; - num_success ++; - /* fix charges on N(-)=N(+)=N- */ - RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge ); - RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge1 ); - RemoveFromEdgeListByValue( &NNNChargeEdges, eNPlusEdge1 ); - RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge2 ); - RemoveFromEdgeListByValue( &NNNChargeEdges, eNPlusEdge2 ); - } else { - pe->flow ++; - pBNS->vert[v1].st_edge.flow ++; - pBNS->vert[v2].st_edge.flow ++; - pBNS->tot_st_flow += 2; - num_failed ++; - } - pe->forbidden &= inv_forbidden_edge_mask; - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - } - - /* 3rd attempt */ - - /* - AllChargeEdges.num_edges = 0; - AllNNNTermAtoms.num_edges = 0; - NNNChargeEdges.num_edges = 0; - */ - for ( i = 0; i < num_at && 0 <= ret; i ++ ) { - - eNMinusEdge = pVA[i].nCMinusGroupEdge - 1; - /*eNPlusEdge = pVA[i].nCPlusGroupEdge - 1;*/ - - if ( 0 <= eNMinusEdge && - at2[i].valence == 1 && - at2[i].num_H == 0 && - at2[i].radical == 0 && - pVA[i].cNumValenceElectrons == 5 && /* terminal N# */ - - at2[j=at2[i].neighbor[0]].valence == 2 && - at2[j].num_H == 0 && - at2[j].radical == 0 && - pVA[j].cNumValenceElectrons == 5 && - (eNMinusEdge1 = pVA[j].nCMinusGroupEdge - 1)>= 0 && - (eNPlusEdge1 = pVA[j].nCPlusGroupEdge - 1)>= 0 && - !pBNS->edge[eNMinusEdge1].forbidden && - !pBNS->edge[eNPlusEdge1].forbidden && - - at2[k=at2[j].neighbor[at2[j].neighbor[0]==i]].valence == 2 && - at2[k].num_H == 0 && - at2[k].radical == 0 && - pVA[k].cNumValenceElectrons == 5 && - (eNMinusEdge2 = pVA[k].nCMinusGroupEdge - 1)>= 0 && - (eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1)>= 0 && - !pBNS->edge[eNMinusEdge2].forbidden && - !pBNS->edge[eNPlusEdge2].forbidden && - - pVA[i].cnListIndex > 0 && - cnList[pVA[i].cnListIndex-1].bits == cn_bits_MN ) { - - /* found N#N(+)-N~ or N(-)=N-N= where the last N (at2[k]) may be charged */ - NNNChargeEdges.num_edges = 0; - - eNFlowerEdge1 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge1 ); - eNFlowerEdge2 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge2 ); - - /* 1. N(-)=N(+)=N- */ - if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ - pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { - /* fix charges on N(-)=N(+)=N- */ - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) { - goto exit_function; - } - continue; /* already good */ - } - /* 2. N#N(+)-N= */ - if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ - pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { - /* unfix (-) edge on terminal N# */ - if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) { - goto exit_function; - } - pe = pBNS->edge + pBNS->vert[i].iedge[0]; - nDeltaChargeMax = 0; - nDeltaChargeMax = (num_failed && !num_success && pStruct->nNumRemovedProtonsMobHInChI > 0)? 2 : 0; - } else - /* 3. N(-)=N-N= */ - if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 1 && /* N */ - pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { - /* unfix (+) edge on middle N */ - if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( NO_VERTEX != eNFlowerEdge1 && - ( ret = AddToEdgeList( &NNNChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) ) { - goto exit_function; - } - /* decrement flow on (+) charge edge of the middle =N- */ - pe = pBNS->edge + eNPlusEdge1; - nDeltaChargeMax = 2; - } else - /* 4. N#N(+)-N(-)- */ - if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ - pBNS->edge[eNMinusEdge2].flow == 1 && pBNS->edge[eNPlusEdge2].flow == 1 /* N(-) */ ) { - /* unfix (-) edge on the 1st and 3rd N */ - if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) { - goto exit_function; - } - /* decrement triple bond on terminal N# */ - pe = pBNS->edge + pBNS->vert[i].iedge[0]; - nDeltaChargeMax = 0; - } else - /* 5. N#N(+)-N(+)# */ - if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ - pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 0 /* N(+) */ ) { - /* unfix (-) edge on the 1st and (+) edge on the 3rd N */ - if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) { - goto exit_function; - } - /* decrement triple bond on terminal N# */ - pe = pBNS->edge + pBNS->vert[i].iedge[0]; - nDeltaChargeMax = 0; - } else { - continue; - } - - if ( NO_VERTEX != eNFlowerEdge1 && !pBNS->edge[eNFlowerEdge1].flow ) { - if ( ret = AddToEdgeList( &NNNChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - } - if ( NO_VERTEX != eNFlowerEdge2 && !pBNS->edge[eNFlowerEdge2].flow ) { - if ( ret = AddToEdgeList( &NNNChargeEdges, eNFlowerEdge2, INC_EDGE_LIST ) ) { - goto exit_function; - } - } - - v1 = pe->neighbor1; - v2 = pe->neighbor12 ^ v1; - pe->flow --; - pBNS->vert[v1].st_edge.flow --; - pBNS->vert[v2].st_edge.flow --; - pBNS->tot_st_flow -= 2; - - pe->forbidden |= forbidden_edge_mask; - - if ( !CarbonChargeEdges.num_edges ) { - /* do not let carbon atoms get charged */ - AllocEdgeList( &CarbonChargeEdges, INC_EDGE_LIST ); - if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { - goto exit_function; - } - } else { - SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - } - SetForbiddenEdgeMask( pBNS, &NNNChargeEdges, forbidden_edge_mask ); - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= nDeltaChargeMax ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - *pnTotalDelta += ret; - num_success ++; - /* fix charges on N(-)=N(+)=N- */ - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) { - goto exit_function; - } - } else { - pe->flow ++; - pBNS->vert[v1].st_edge.flow ++; - pBNS->vert[v2].st_edge.flow ++; - pBNS->tot_st_flow += 2; - num_failed ++; - } - RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &NNNChargeEdges, forbidden_edge_mask ); - pe->forbidden &= inv_forbidden_edge_mask; - /*pBNS->edge[eNPlusEdge].forbidden &= inv_forbidden_edge_mask;*/ /* BC: array index out of range */ - } - } - RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - - -exit_function: - - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &NNNChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &CurNNNChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &AllNNNTermAtoms, EDGE_LIST_FREE ); - AllocEdgeList( &AllNIIIChargeEdges, EDGE_LIST_FREE ); - return ret; -#undef INC_EDGE_LIST -} -/******************************************************************************************************/ -int EliminateNitrogen5Val3Bonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ - int i, j, k, bForbiddenCarbonCharges, ret2, ret; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - EDGE_LIST CarbonChargeEdges; - - ret = 0; - bForbiddenCarbonCharges = 0; - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - - /* forbid creation of other N(V) atoms */ - /* fix single bonds to metals */ - for ( i = 0; i < num_at; i ++ ) { - if ( pVA[i].cNumValenceElectrons == 5 && - 0 <= (k = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 )) && - 1 == pBNS->edge[k].flow) { - pBNS->edge[k].forbidden |= forbidden_edge_mask; - } else - if ( pVA[i].cMetal ) { - for ( j = 0; j < at2[i].valence; j ++ ) { - if ( BOND_TYPE_SINGLE == (at2[i].bond_type[j] & BOND_TYPE_MASK) ) { - pBNS->edge[pBNS->vert[i].iedge[j]].forbidden |= forbidden_edge_mask; - } - } - } - } - - /*------------------------------------------------------------------------------ - (+) single line => flow = 0 (+)-(Y)=(+)super - 01 // double line => flow = 1 fix-> 01 // <-- fix - 1 --- 0 1 === 0 - \\ // edge eij connects vertices i < j: \ / 02 - 12 2 02 <--- edge number: e02 connects vertices v0 12 2(..) <- double 'radical' - | v0 and v2 | - =N= vertex N has number i =N= - | | - --------------------------------------------------------------------------------*/ - for ( i = 0; i < num_at; i ++ ) { - if ( pVA[i].cNumValenceElectrons == 5 && at2[i].valence == 3 && - at2[i].chem_bonds_valence == 5 && !at2[i].charge && !at2[i].radical && - !(at2[i].endpoint || pStruct->endpoint && pStruct->endpoint[i]) && pVA[i].cnListIndex > 0 && - cnList[pVA[i].cnListIndex-1].bits == cn_bits_NPN && - pVA[i].nCPlusGroupEdge > 0 ) { - - Vertex v, v0 = NO_VERTEX, v1 = NO_VERTEX, v2 = NO_VERTEX; - EdgeIndex iePlus, ie, ie12 = NO_VERTEX, ie02, ie01; - BNS_VERTEX *pv0, *pv1, *pv2 = NULL; - BNS_EDGE *pePlus, *pe, *pe12 = NULL, *pe02 = NULL, *pe01 = NULL; - Vertex vPathStart, vPathEnd; - int nPathLen; - int nDeltaH, nDeltaCharge, nNumVisitedAtoms; - - iePlus = pVA[i].nCPlusGroupEdge - 1; - pePlus = pBNS->edge + iePlus; - - v0 = IS_BNS_VT_C_GR( pBNS->vert[pePlus->neighbor1].type )? - (pePlus->neighbor1 ^ pePlus->neighbor12) : pePlus->neighbor1; - pv0 = pBNS->vert + v0; - for ( j = 0; j < pv0->num_adj_edges; j ++ ) { - ie = pv0->iedge[j]; - if ( ie == iePlus ) { - continue; - } - pe = pBNS->edge + ie; - if ( pe->flow == 1 && v2 == NO_VERTEX ) { - /* 0 - 2, edge 02 */ - v2 = pe->neighbor12 ^ v0; - pv2 = pBNS->vert + v2; - ie02 = ie; - pe02 = pe; - } else - if ( pe->flow == 0 && v1 == NO_VERTEX ) { - /* 0 - 1, edge 01 */ - v1 = pe->neighbor12 ^ v0; - pv1 = pBNS->vert + v2; - ie01 = ie; - pe01 = pe; - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - } - if ( v1 == NO_VERTEX || v2 == NO_VERTEX ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - for ( j = 0; j < pv2->num_adj_edges; j ++ ) { - ie = pv2->iedge[j]; - pe = pBNS->edge + ie; - v = pe->neighbor12 ^ v2; - if ( v == v0 || v == i ) { - continue; - } else - if ( v == v1 && pe->flow == 1 ) { - /* 1 - 2, edge 12 */ - ie12 = ie; - pe12 = pe; - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - } - if ( ie12 == NO_VERTEX ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - /* rearrange cap and flow, forbid 2 edges */ - pe01->flow = 1; - pe12->flow = 0; - pe02->flow = 0; - pv2->st_edge.flow -= 2; - pBNS->tot_st_flow -= 2; - pePlus->forbidden |= forbidden_edge_mask; - pe01->forbidden |= forbidden_edge_mask; - - if ( !bForbiddenCarbonCharges ) { - if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { - goto exit_function; - } - bForbiddenCarbonCharges = 1; - } - - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret == 1 && vPathEnd == v2 && vPathStart == v2 && nDeltaCharge <= (pVA[i].cNumBondsToMetal? 2:0) ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - } else { - pe01->flow = 0; - pe12->flow = 1; - pe02->flow = 1; - pv2->st_edge.flow += 2; - pBNS->tot_st_flow += 2; - } - pePlus->forbidden &= inv_forbidden_edge_mask; - pe01->forbidden &= inv_forbidden_edge_mask; - - if ( ret < 0 ) { - goto exit_function; - } else - if ( ret ) { - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - } - } - } -exit_function: - /* allow creation of other N(V) atoms */ - for ( i = 0; i < num_at; i ++ ) { - if ( pVA[i].cNumValenceElectrons == 5 && - 0 <= (k = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 )) && - 1 == pBNS->edge[k].flow && (pBNS->edge[k].forbidden & forbidden_edge_mask) ) { - pBNS->edge[k].forbidden &= inv_forbidden_edge_mask; - } else - if ( pVA[i].cMetal ) { - for ( j = 0; j < at2[i].valence; j ++ ) { - if ( BOND_TYPE_SINGLE == (at2[i].bond_type[j] & BOND_TYPE_MASK) ) { - pBNS->edge[pBNS->vert[i].iedge[j]].forbidden &= inv_forbidden_edge_mask; - } - } - } - } - RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); - return ret; -} -/******************************************************************************************************/ -int Convert_SIV_to_SVI(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ - int i, j, k, neigh, bForbiddenCarbonCharges, nFlowerEdge, delta, ret2, ret; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - EDGE_LIST CarbonChargeEdges, FlowerEdgesList; - - ret = 0; - bForbiddenCarbonCharges = 0; - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &FlowerEdgesList, EDGE_LIST_CLEAR ); - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - - /* forbid creation of other S(IV) atoms */ - /* fix single bonds to metals and (N(IV), flow=1), (S(IV), flow=0) */ - for ( i = 0; i < num_at; i ++ ) { - if ( (pVA[i].cNumValenceElectrons == 5 /* N(IV)*/ || pVA[i].cNumValenceElectrons == 6 /* S(VI)*/) && - 0 <= (k = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 )) && - !pBNS->edge[k].forbidden && - 6 == pVA[i].cNumValenceElectrons + pBNS->edge[k].flow ) { - - pBNS->edge[k].forbidden |= forbidden_edge_mask; - if ( ret = AddToEdgeList( &FlowerEdgesList, k, 64 )) { - goto exit_function; - } - } else - if ( pVA[i].cMetal ) { - for ( j = 0; j < at2[i].valence; j ++ ) { - if ( BOND_TYPE_SINGLE == (at2[i].bond_type[j] & BOND_TYPE_MASK) ) { - - pBNS->edge[k=pBNS->vert[i].iedge[j]].forbidden |= forbidden_edge_mask; - if ( ret = AddToEdgeList( &FlowerEdgesList, k, 64 )) { - goto exit_function; - } - } - } - } else - /* fix bonds to neighbors of S(IV) if they are not O,S,Se,Te with 2 or more bonds */ - /* exactly same if(..) as below */ - if ( pVA[i].cNumValenceElectrons == 6 && at2[i].valence == 4 && - at2[i].chem_bonds_valence == 4 && !at2[i].charge && !at2[i].radical && - !at2[i].endpoint && pVA[i].cnListIndex > 0 && - cnList[pVA[i].cnListIndex-1].bits == cn_bits_NPN && - 0 <= (nFlowerEdge = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 ) ) && - pBNS->edge[nFlowerEdge].flow > 0 ) { - - for ( j = 0; j < at2[i].valence; j ++ ) { - neigh = at2[i].neighbor[j]; - if ( pVA[neigh].cNumValenceElectrons != 6 && at2[neigh].valence > 1 ) { - k = pBNS->vert[i].iedge[j]; - if ( !pBNS->edge[k].forbidden ) { - if ( ret = AddToEdgeList( &FlowerEdgesList, k, 64 )) { - goto exit_function; - } - pBNS->edge[k].forbidden |= forbidden_edge_mask; - } - } - } - } - } - /*------------------------------------------------------------------------------ - example: struct #301, - | | disconnected porphyrin with four -SO3(-) - -S- => =S= - | | - - -------------------------------------------------------------------------------*/ - - /*------------------------------------------------------------------------------- - found: super(+)=(Y) super(+)=(Y) - \ \ - (+) single line => flow = 0 (+) (+) - 01 // double line => flow = 1 fix-> 01 // 01 // - 1 === 0 triple line => flow = 2 (.)1 --- 0(.) ---> 1 --- 0 - \ / edge eij connects vertices i 0 && - cnList[pVA[i].cnListIndex-1].bits == cn_bits_NPN && - /* 01 is nFlowerEdge */ - 0 <= (nFlowerEdge = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 ) ) && - pBNS->edge[nFlowerEdge].flow > 0 ) { - - Vertex v1 = NO_VERTEX, v2 = NO_VERTEX; - BNS_VERTEX *pv1, *pv2; - BNS_EDGE *pe; - Vertex vPathStart, vPathEnd; - int nPathLen; - int nDeltaH, nDeltaCharge, nNumVisitedAtoms; - - if ( !bForbiddenCarbonCharges ) { - if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { - goto exit_function; - } - bForbiddenCarbonCharges = 1; - } - - delta = 1; - pe = pBNS->edge + nFlowerEdge; /* edge 01 */ - pv1 = pBNS->vert + (v1 = pe->neighbor1); /* vertex 0 */ - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); /* vertex 1 */ - - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret == 1 && - (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && - nDeltaCharge <= (pVA[i].cNumBondsToMetal? 2:0) ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - } else { - pe->forbidden &= inv_forbidden_edge_mask; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - if ( ret < 0 ) { - goto exit_function; - } else - if ( ret ) { - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - /* store the fixed edge to unfix it upon exit */ - if ( ret = AddToEdgeList( &FlowerEdgesList, nFlowerEdge, 64 )) { - goto exit_function; - } - } - } - } -exit_function: - RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); - RemoveForbiddenEdgeMask( pBNS, &FlowerEdgesList, forbidden_edge_mask ); - AllocEdgeList( &FlowerEdgesList, EDGE_LIST_FREE ); - return ret; -} -/****************************************************************************************************** - - - =N(+)=O =N-O(-) - => - M(q) M(q+2) - -*******************************************************************************************************/ -int PlusFromDB_N_DB_O_to_Metal(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ - int i, j, k, n, bForbiddenCarbonCharges, delta, ret2, ret, num_NO, num_M; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - EDGE_LIST CarbonChargeEdges, NO_ChargeEdgeList, NO_EdgeList; - - Vertex v1, v2; - BNS_VERTEX *pv1, *pv2; - BNS_EDGE *pe; - Vertex vPathStart, vPathEnd; - int nPathLen; - int nDeltaH, nDeltaCharge, nNumVisitedAtoms; - - - if ( !pTCGroups->num_metal_atoms ) - return 0; - - ret = 0; - bForbiddenCarbonCharges = 0; - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); /* all charges */ - AllocEdgeList( &NO_ChargeEdgeList, EDGE_LIST_CLEAR ); /* charges to be changed */ - AllocEdgeList( &NO_EdgeList, EDGE_LIST_CLEAR ); /* N(+)=O edges */ - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - num_NO = num_M = 0; - /* forbid creation of other S(IV) atoms */ - /* fix single bonds to metals and (N(IV), flow=1), (S(IV), flow=0) */ - for ( i = 0; i < num_at; i ++ ) { - if ( !pVA[i].cMetal ) { - if ( (k = pVA[i].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { - if ( ret = AddToEdgeList( &CarbonChargeEdges, k, 64 ) ) { - goto exit_function; - } - } - if ( (k = pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { - if ( ret = AddToEdgeList( &CarbonChargeEdges, k, 64 ) ) { - goto exit_function; - } - } - } else { - num_M ++; - } - /* - if ( pVA[i].cMetal ) { - if ( (k = pVA[i].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { - if ( ret = AddToEdgeList( &NO_ChargeEdgeList, k, 64 ) ) { - goto exit_function; - } - } - if ( (k = pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { - if ( ret = AddToEdgeList( &NO_ChargeEdgeList, k, 64 ) ) { - goto exit_function; - } - } - } else - */ - if ( !pVA[i].cMetal && - pVA[i].cNumValenceElectrons == 6 && - at2[i].charge == 0 && !at2[i].num_H && - 1 == at2[i].valence && 2 == at2[i].chem_bonds_valence && - pVA[j=at2[i].neighbor[0]].cNumValenceElectrons == 5 && - at2[j].charge == 1 && !at2[j].num_H && - 2 == at2[j].valence && 4 == at2[j].chem_bonds_valence ) { - /* found =N(+)=O */ - if ( (k = pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden /* O */ && - (n = pVA[j].nCPlusGroupEdge -1) >= 0 && !pBNS->edge[j].forbidden /* N */ ) { - if ( (ret = AddToEdgeList( &NO_ChargeEdgeList, k, 64 ) ) || - (ret = AddToEdgeList( &NO_ChargeEdgeList, n, 64 ) ) ) { - goto exit_function; - } - k = pBNS->vert[i].iedge[0]; /* N(+)=O bond */ - if ( !pBNS->edge[k].forbidden ) { - if ( ret = AddToEdgeList( &NO_EdgeList, k, 64 ) ) { - goto exit_function; - } - num_NO ++; - } - } - } - } - if ( num_M && num_NO ) { - SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - SetForbiddenEdgeMask( pBNS, &NO_EdgeList, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &NO_ChargeEdgeList, forbidden_edge_mask ); - /* now only N(+), O(-) and metal charges are allowed to change */ - for ( i = 0; i < NO_EdgeList.num_edges; i ++ ) { - k = NO_EdgeList.pnEdges[i]; - delta = 1; - pe = pBNS->edge + k; /* edge N(+)=O */ - pv1 = pBNS->vert + (v1 = pe->neighbor1); /* vertex 0 */ - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); /* vertex 1 */ - - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret == 1 && - (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && - nDeltaCharge == 0 ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - } else { - pe->forbidden &= inv_forbidden_edge_mask; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - if ( ret < 0 ) { - goto exit_function; - } - } - } -exit_function: - RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &NO_EdgeList, forbidden_edge_mask ); - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &NO_EdgeList, EDGE_LIST_FREE ); - AllocEdgeList( &NO_ChargeEdgeList, EDGE_LIST_FREE ); - return ret; -} -/******************************************************************************************************/ -int MoveMobileHToAvoidFixedBonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ - int ret2, ret; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int nNumFixedEdges, nNumAdjEdges; - - ret = 0; - - if ( pTCGroups->num_tgroups ) { - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } -#if ( FIND_RING_SYSTEMS == 1 ) - ret2 = MarkRingSystemsInp( at2, num_at, 0 ); - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } -#endif - /* --- forbidden edges --- */ - ret2 = SetForbiddenEdges( pBNS, at2, num_at, forbidden_edge_mask ); - if ( ret2 < 0 ) { - ret2 = -(ret + 1); - } - nNumFixedEdges = ret2; - ret = AdjustTgroupsToForbiddenEdges2( pBNS, at2, pVA, num_at, forbidden_edge_mask ); - nNumAdjEdges = ret; - if ( ret ) { - pBNS->edge_forbidden_mask |= forbidden_edge_mask; - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else { - *pnTotalDelta += ret; - } - } - if ( nNumFixedEdges || nNumAdjEdges ) { - /* removes this edge mask from ALL edges */ - RemoveForbiddenBondFlowBits( pBNS, forbidden_edge_mask ); - } - } - -exit_function: - - return ret; -} -/******************************************************************************************************/ -/* Find and eliminate cases when Mobile H endpoint has radical on it (typical for wrong P(VI)(=O)3OH */ -int RemoveRadFromMobileHEndpoint(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ - int i, num_fixes, tot_num_fixes = 0; - - int ret2, ret; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - - int itg, j, k, n, m; - Vertex vtg1, endpoint0=NO_VERTEX, endpoint1, endpoint2, centerpoint; - Vertex centerpoint_found=NO_VERTEX; - BNS_VERTEX *ptg1, *pEndp0=NULL, *pEndp1, *pEndp2, *pCentp, *pCentp_found, *pEndp2_found=NULL; - BNS_EDGE *etg0=NULL, *etg1, *etg2, *ecp0, *ecp1, *ecp2; - BNS_EDGE *etg1_found=NULL, *ecp0_found=NULL, *ecp1_found=NULL, *ecp2_found=NULL; - int tgroup_number, num_endpoints; - - ret = 0; - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - while ( pBNS->tot_st_cap > pBNS->tot_st_flow && pTCGroups->num_tgroups ) { - num_fixes = 0; - for ( itg = 0; itg < pTCGroups->num_tgroups; itg ++ ) { - pCentp_found=NULL; - tgroup_number = pTCGroups->pTCG[itg].ord_num; - vtg1 = pTCGroups->pTCG[itg].nVertexNumber; /* taut group vertex index */ - ptg1 = pBNS->vert + vtg1; /* taut group vertex */ - num_endpoints = pTCGroups->pTCG[itg].num_edges; - for ( i = 0; i < num_endpoints; i ++ ) { - etg0 = pBNS->edge + ptg1->iedge[i]; /* edge from t-group to endpoint */ - endpoint0 = etg0->neighbor12 ^ vtg1; /* taut endpoint vertex index */ - pEndp0 = pBNS->vert + endpoint0; /* taut endpoint vertex (possible location of mobile H */ - if ( pEndp0->st_edge.cap > pEndp0->st_edge.flow ) { - /* radical endpoint1 has been detected */ - /* find a 1-3 centerpoint that has two or more endpoints */ - /* connected to the t-group vertex by edges with flow>0 and */ - /* to the centerpoint by edges with flow = 0 */ - /* after that: (1) increment etg1 flow to eliminate radical */ - /* (2) increment flow on one of the two other edges to the t-group */ - /* (3) increment st_cap on the found centerpoint */ - /* (4) rerun the BNS and re-create the structure */ - break; - } - } - if ( i == num_endpoints ) { - continue; - } - if ( i < num_endpoints ) { - /* tautomeric endpoint found; traverse its t-group edges */ - for ( j = 0; j < num_endpoints; j ++ ) { - if ( i == j ) { - continue; /* avoid the already found radical endpoint */ - } - etg1 = pBNS->edge + ptg1->iedge[j]; /* another edge from t-group to another endpoinr */ - endpoint1 = etg1->neighbor12 ^ vtg1; /* another endpoint vertex index */ - pEndp1 = pBNS->vert + endpoint1; /* another endpoint vertex */ - if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) { - continue; /* one more radical-endpoint! What is going on here??? */ - } - if ( !etg1->flow ) { - continue; /* avoid enpoints that do not have an attachment */ - } - if ( !(pEndp1->type & BNS_VERT_TYPE_ENDPOINT) ) { - continue; /* should not happen */ - } - /* traverse endpoint1 edges to find a single bond connecting it to the centerpoint */ - for ( k = 0; k < at2[endpoint1].valence; k ++ ) { - ecp1 = pBNS->edge + pEndp1->iedge[k]; - if ( ecp1->flow ) { - continue; - } - centerpoint = ecp1->neighbor12 ^ endpoint1; - pCentp = pBNS->vert + centerpoint; - /* traverse centerpoint edges to find a single bond to the 2nd endpoint */ - for ( n = 0; n < at2[centerpoint].valence; n ++ ) { - ecp2 = pBNS->edge + pCentp->iedge[n]; - if ( ecp2->flow ) { - continue; - } - endpoint2 = ecp2->neighbor12 ^ centerpoint; - if ( endpoint2 <= endpoint1 || !pVA[endpoint2].nTautGroupEdge ) { - continue; /* don't go back: neighbors are in order of ascending ord. numbers */ - } - pEndp2 = pBNS->vert + endpoint2; - if ( !(pEndp2->type & BNS_VERT_TYPE_ENDPOINT) ) { - continue; - } - etg2 = pBNS->edge + pVA[endpoint2].nTautGroupEdge - 1; - if ( !etg2->flow || (etg2->neighbor12 ^ endpoint2) != vtg1 ) { - continue; - } - /* we have found the path: - Endp1 Endp1 - etg1 // \ ecp1 etg1 / \\ ecp1 - etg0 // \ etg0 / \\ - Endp0-----tg1 Centp --> Endp0=====tg1 Centp - ^ \\ / \\ / - radical | etg2 \\ / ecp2 etg2 \\ / ecp2 - Endp2 Endp2 - */ - - /* compare centerpoints */ - if ( !pCentp_found || - /* try to avoid carbons */ - (pVA[centerpoint].cNumValenceElectrons != 4 || - pVA[centerpoint].cPeriodicRowNumber != 1) && - pVA[centerpoint_found].cNumValenceElectrons == 4 && - pVA[centerpoint_found].cPeriodicRowNumber == 1 || - /* try a better non-carbon */ - (pVA[centerpoint].cNumValenceElectrons != 4 || - pVA[centerpoint].cPeriodicRowNumber != 1 ) && - (at[centerpoint].valence > at[centerpoint_found].valence || - at[centerpoint].valence == at[centerpoint_found].valence && - at[centerpoint].el_number > at[centerpoint_found].el_number) ) { - - pCentp_found = pCentp; - etg1_found = etg1; - ecp1_found = ecp1; - centerpoint_found = centerpoint; - break; - } - } - } - } - } - if ( pCentp_found ) { - /* ---- (1) */ - etg0->flow ++; - pEndp0->st_edge.flow ++; - /* ---- (2) */ - etg1_found->flow --; - /* ---- (3) */ - ecp1_found->flow ++; - /* ---- (4) */ - pCentp_found->st_edge.flow ++; - pCentp_found->st_edge.cap ++; - - pBNS->tot_st_flow += 2; - pBNS->tot_st_cap += 1; - pCentp_found = NULL; - num_fixes ++; - tot_num_fixes ++; /* #1 Mob-H */ - continue; - } - - /* 2nd attempt: increment flow in centerpoint---radical_endpint edge */ - if ( i < num_endpoints ) { - /* tautomeric endpoint found; traverse its t-group edges */ - for ( j = 0; j < num_endpoints; j ++ ) { - if ( i == j ) { - continue; /* avoid the found radical endpoint */ - } - etg1 = pBNS->edge + ptg1->iedge[j]; - endpoint1 = etg1->neighbor12 ^ vtg1; - pEndp1 = pBNS->vert + endpoint1; /* another endpoint */ - if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) { - continue; /* one more radical-endpoint! What is going on here??? */ - } - if ( !etg1->flow ) { - continue; /* avoid enpoints that do not have an attachment */ - } - if ( !(pEndp1->type & BNS_VERT_TYPE_ENDPOINT) ) { - continue; /* should not happen */ - } - /* traverse endpoint1 edges to find the edge connecting it to the centerpoint */ - for ( k = 0; k < at2[endpoint1].valence; k ++ ) { - ecp1 = pBNS->edge + pEndp1->iedge[k]; - if ( ecp1->flow ) { - continue; - } - centerpoint = ecp1->neighbor12 ^ endpoint1; - pCentp = pBNS->vert + centerpoint; - if ( pCentp->type & BNS_VERT_TYPE_ENDPOINT ) { - continue; /* do not set another endpoint's valence = an unusual value */ - } - - /* traverse centerpoint edges to find edge connecting it to the endpoint0 */ - ecp2 = NULL; - pEndp2 = NULL; - for ( n = 0; n < at2[centerpoint].valence; n ++ ) { - ecp0 = pBNS->edge + pCentp->iedge[n]; - if ( ecp0->flow ) { - endpoint2 = ecp0->neighbor12 ^ centerpoint; - if ( (pBNS->vert[endpoint2].type & BNS_VERT_TYPE_ENDPOINT) ) { - continue; /* ignore endpoint2 if it is tautomeric endpoint */ - } - /* check whether ecp0 is stereogenic: if it is then we cannot decrement its flow */ - for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[centerpoint].sb_parity[m]; m ++ ) { - if ( at[centerpoint].sb_ord[m] == n ) { - endpoint2 = NO_VERTEX; - break; - } - } - if ( endpoint2 == NO_VERTEX ) { - continue; - } - pEndp2 = pBNS->vert + endpoint2; /* found */ - ecp2 = ecp0; - break; - } - } - for ( n = 0; n < at[centerpoint].valence; n ++ ) { - ecp0 = pBNS->edge + pCentp->iedge[n]; - if ( ecp0->flow ) { - continue; - } - if ( endpoint0 != (ecp0->neighbor12 ^ centerpoint) ) { - continue; - } - /* Found: - Endp2(not endpoint) Endp2(not radical) - || | - ||ecp2 |ecp2 - ecp0 || ecp1 ecp0 | ecp1 - Endp0----Centp----Endp1 Endp0====Centp----Endp1 - ^ \ / --> \ / - radical | \ / \ / - \ / \ / - etg0 \ / etg1 etg0 \ / etg1 - \ / \ / - tg1 tg1 - - */ - - /* compare centerpoints */ - if ( !pCentp_found || - /* try to avoid carbons */ - (pVA[centerpoint].cNumValenceElectrons != 4 || - pVA[centerpoint].cPeriodicRowNumber != 1) && - pVA[centerpoint_found].cNumValenceElectrons == 4 && - pVA[centerpoint_found].cPeriodicRowNumber == 1 || - /* try a better non-carbon */ - (pVA[centerpoint].cNumValenceElectrons != 4 || - pVA[centerpoint].cPeriodicRowNumber != 1 ) && - (at[centerpoint].valence > at[centerpoint_found].valence || - at[centerpoint].valence == at[centerpoint_found].valence && - at[centerpoint].el_number > at[centerpoint_found].el_number) ) { - - pCentp_found = pCentp; - etg1_found = etg1; - ecp0_found = ecp0; - centerpoint_found = centerpoint; - pEndp2_found = pEndp2; - ecp2_found = ecp2; - break; - } - } - } - } - } - if ( pCentp_found ) { - ecp0_found->flow ++; - if ( ecp0_found->cap < ecp0_found->flow ) { - ecp0_found->cap = ecp0_found->flow; - } - pEndp0->st_edge.flow ++; - if ( pEndp2_found && ecp2_found ) { - ecp2_found->flow --; - pEndp2_found->st_edge.flow --; - } else { - /* Endp2 not found */ - pCentp_found->st_edge.flow ++; - pCentp_found->st_edge.cap ++; - pBNS->tot_st_flow += 2; /* radical elimination */ - pBNS->tot_st_cap += 1; - } - - pCentp_found = NULL; - num_fixes ++; - tot_num_fixes ++; /* #2 Mob-H */ - continue; - } - /* 3rd attempt: find =C= and move radical to it */ - if ( i < num_endpoints ) { - int jj, delta, bNotFixed = 1; - Vertex vPathStart, vPathEnd, v1, v2; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - for ( jj = 0; jj < num_at && bNotFixed; jj ++ ) { - if ( at2[i].endpoint ) { - continue; - } - if ( 2 == at2[jj].valence && pBNS->vert[jj].st_edge.cap == pBNS->vert[jj].st_edge.flow && - 4 == pVA[jj].cNumValenceElectrons && - !(ecp0 = pBNS->edge + pBNS->vert[jj].iedge[0])->forbidden && - !(ecp1 = pBNS->edge + pBNS->vert[jj].iedge[1])->forbidden && - 1 == ecp0->flow && 1 == ecp1->flow && - !at2[(int)at2[i].neighbor[0]].sb_parity[0] && - !at2[(int)at2[i].neighbor[1]].sb_parity[0] ) { - /* found =C=; make a radical and try to cancel the two radicals */ - k = ecp0->neighbor12 ^ jj; - if ( at2[k].endpoint ) { - ecp0 = ecp1; - k = ecp0->neighbor12 ^ jj; - if ( at2[k].endpoint ) { - continue; - } - } - delta = 1; - /* decrement C valence */ - pBNS->vert[jj].st_edge.flow -= delta; - pBNS->vert[jj].st_edge.cap -= delta; - /* decrement bond order */ - ecp0->flow -= delta; - /* reflect the changes in at2[k] to make it a radical */ - pBNS->vert[k].st_edge.flow -= delta; - pBNS->tot_st_cap -= delta; - pBNS->tot_st_flow -= 2*delta; - - v1 = endpoint0; - v2 = k; - - ret2 = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret2 == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { - ret2 = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret2 > 0 ) { - num_fixes ++; - tot_num_fixes ++; /* #3 Mob-H */ - pBNS->vert[jj].st_edge.cap += delta; /* create radical on =C- */ - pBNS->tot_st_cap += delta; - pCentp_found = NULL; - bNotFixed = 0; /* exit from the cycle */ - break; - } - } else { - /* failed */ - pBNS->vert[jj].st_edge.flow += delta; - pBNS->vert[jj].st_edge.cap += delta; - /* decrement bond order */ - ecp0->flow += delta; - /* reflect the changes in at2[k] to make it a radical */ - pBNS->vert[k].st_edge.flow += delta; - pBNS->tot_st_cap += delta; - pBNS->tot_st_flow += 2*delta; - } - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - } - } - } - } - if ( !num_fixes ) { - break; - } - } - ret = tot_num_fixes; -exit_function: - pStruct->at = at; - memcpy( at2, at, len_at*sizeof(at2[0])); - return ret; -} -/******************************************************************************************************/ -/* Find and eliminate cases when Mobile H endpoint has radical on it (typical for wrong P(VI)(=O)3OH */ -int RemoveRadFromMobileHEndpointFixH(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ -#define IS_C(x) (NO_VERTEX != x && pVA[x].cNumValenceElectrons == 4 && pVA[x].cPeriodicRowNumber == 1) - int i, num_fixes, tot_num_fixes = 0; - - int ret2, ret; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - EDGE_LIST ChargeEdgeList, BondEdgeList; - int itg, j, k, n, m, num_endp; - Vertex endpoint0=NO_VERTEX, endpoint1, endpoint2=NO_VERTEX, centerpoint; - Vertex centerpoint_found=NO_VERTEX, endpoint2_found=NO_VERTEX; - BNS_VERTEX *pEndp0=NULL, *pEndp1, *pEndp2, *pCentp, *pCentp_found, *pEndp2_found=NULL; - BNS_EDGE *ecp0, *ecp1, *ecp2, *ecp0_found=NULL, *ecp1_found=NULL, *ecp2_found=NULL; - int tgroup_number, num_endpoints; - - ret = 0; - - if ( pStruct->iMobileH != TAUT_NON ) - return ret; - - AllocEdgeList( &ChargeEdgeList, EDGE_LIST_CLEAR); - AllocEdgeList( &BondEdgeList, EDGE_LIST_CLEAR); - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - while ( pBNS->tot_st_cap > pBNS->tot_st_flow && pStruct->ti.num_t_groups ) { - int iEndpoint = 0; - num_fixes = 0; - for ( itg = 0; itg < pStruct->ti.num_t_groups; iEndpoint += num_endpoints, itg ++ ) { - pCentp_found=NULL; - tgroup_number = pStruct->ti.t_group[itg].nGroupNumber; - num_endpoints = pStruct->ti.t_group[itg].nNumEndpoints; - for ( i = 0; i < num_endpoints; i ++ ) { - endpoint0 = pStruct->ti.nEndpointAtomNumber[iEndpoint+i]; - pEndp0 = pBNS->vert + endpoint0; /* taut endpoint vertex (possible location of mobile H */ - if ( pEndp0->st_edge.cap > pEndp0->st_edge.flow ) { - /* radical endpoint1 has been detected */ - /* find a 1-3 centerpoint that has two or more endpoints */ - /* connected to the t-group vertex by edges with flow>0 and */ - /* to the centerpoint by edges with flow = 0 */ - /* after that: (1) increment etg1 flow to eliminate radical */ - /* (2) increment flow on one of the two other edges to the t-group */ - /* (3) increment st_cap on the found centerpoint */ - /* (4) rerun the BNS and re-create the structure */ - break; - } - } - - /* 2nd attempt: increment flow in centerpoint---radical_endpoint edge */ - pCentp_found = NULL; - if ( i < num_endpoints ) { - /* tautomeric endpoint found; traverse its t-group edges */ - for ( j = 0; j < num_endpoints; j ++ ) { - if ( i == j ) { - continue; /* avoid the found radical endpoint */ - } - endpoint1 = pStruct->ti.nEndpointAtomNumber[iEndpoint+j]; - pEndp1 = pBNS->vert + endpoint1; /* another endpoint */ - - if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) { - continue; /* one more radical-endpoint! What is going on here??? */ - } - if ( !at2[endpoint1].num_H && at2[endpoint1].charge != -1 ) { - continue; /* avoid enpoints that do not have an attachment */ - } - if ( !pStruct->endpoint[endpoint1] ) { - continue; /* should not happen */ - } - /* traverse endpoint1 edges to find the edge connecting it to the centerpoint */ - for ( k = 0; k < pEndp1->num_adj_edges; k ++ ) { - ecp1 = pBNS->edge + pEndp1->iedge[k]; - if ( ecp1->flow ) { - continue; - } - centerpoint = ecp1->neighbor12 ^ endpoint1; - if ( centerpoint >= pBNS->num_atoms ) { - break; /* no more edges to atoms */ - } - pCentp = pBNS->vert + centerpoint; - if ( pStruct->endpoint[centerpoint] ) { - continue; /* do not set another endpoint's valence = an unusual value */ - } - /* traverse centerpoint edges to find edge connecting it to the endpoint0 */ - /* 1. Find a double bond to an endpoint */ - ecp2 = NULL; - pEndp2 = NULL; - for ( n = 0, num_endp = 0; n < at2[centerpoint].valence; n ++ ) { - ecp0 = pBNS->edge + pCentp->iedge[n]; - if ( ecp0->flow ) { - endpoint2 = ecp0->neighbor12 ^ centerpoint; - if ( pStruct->endpoint[endpoint2] /* ??? */ ) { - continue; - } - /* check whether ecp0 is stereogenic: if it is then we cannot decrement its flow */ - for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[centerpoint].sb_parity[m]; m ++ ) { - if ( at[centerpoint].sb_ord[m] == n ) { - endpoint2 = NO_VERTEX; - break; - } - } - if ( endpoint2 == NO_VERTEX ) { - continue; - } - pEndp2 = pBNS->vert + endpoint2; - ecp2 = ecp0; - break; - } - } - if ( !ecp2 ) { - continue; - } - /* 2. Find a single bond to an endpoint0 */ - for ( n = 0, num_endp = 0; n < at2[centerpoint].valence; n ++ ) { - ecp0 = pBNS->edge + pCentp->iedge[n]; - if ( ecp0->flow ) { - continue; - } - if ( endpoint0 != (ecp0->neighbor12 ^ centerpoint) ) { - continue; - } - /* Found: - Endp2 Endp2(not radical) - || | - ||ecp2 |ecp2 - ecp0 || ecp1 ecp0 | ecp1 - Endp0----Centp----Endp1 Endp0====Centp----Endp1 - ^ \ / --> \ / - radical | \ / \ / - \ / \ / - etg0 \ / etg1 etg0 \ / etg1 - \ / \ / - tg1 tg1 - - */ - - /* compare centerpoints */ - if ( !pCentp_found || - /* try to avoid carbons */ - (pVA[centerpoint].cNumValenceElectrons != 4 || - pVA[centerpoint].cPeriodicRowNumber != 1) && - pVA[centerpoint_found].cNumValenceElectrons == 4 && - pVA[centerpoint_found].cPeriodicRowNumber == 1 || - /* try a better non-carbon */ - (pVA[centerpoint].cNumValenceElectrons != 4 || - pVA[centerpoint].cPeriodicRowNumber != 1 ) && - (at[centerpoint].valence > at[centerpoint_found].valence || - at[centerpoint].valence == at[centerpoint_found].valence && - at[centerpoint].el_number > at[centerpoint_found].el_number) ) { - - pCentp_found = pCentp; - ecp0_found = ecp0; - centerpoint_found = centerpoint; - pEndp2_found = pEndp2; - ecp2_found = ecp2; - break; - } - } - } - } - } - /* decrement st_flow, st_cap on Endp2; decrement flow on ecp2; decrement st_flow on Centp */ - /* result: radicals on Endp0 and Centp => run BNS */ - if ( pCentp_found ) { - /* make ecp0 a double bond, make ecp2 a single bond, remove radical */ - ecp0_found->flow ++; - if ( ecp0_found->cap < ecp0_found->flow ) { - ecp0_found->cap = ecp0_found->flow; - } - pEndp0->st_edge.flow ++; - if ( pEndp2_found && ecp2_found ) { - ecp2_found->flow --; - pEndp2_found->st_edge.flow --; - } else { - /* Endp2 not found: only make ecp0 a double bond */ - pCentp_found->st_edge.flow ++; - pCentp_found->st_edge.cap ++; - pBNS->tot_st_flow += 2; /* radical elimination */ - pBNS->tot_st_cap += 1; - } - - pCentp_found = NULL; - num_fixes ++; /* #2 */ - tot_num_fixes ++; - continue; - } - /* 1st attempt */ - pCentp_found = NULL; - if ( i < num_endpoints ) { - /* tautomeric endpoint found; traverse its t-group edges */ - for ( j = 0; j < num_endpoints; j ++ ) { - if ( i == j ) { - continue; /* avoid the found radical endpoint */ - } - endpoint1 = pStruct->ti.nEndpointAtomNumber[iEndpoint+j]; - pEndp1 = pBNS->vert + endpoint1; /* another endpoint */ - if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) { - continue; /* one more radical-endpoint! What is going on here??? */ - } - if ( !at2[endpoint1].num_H && at2[endpoint1].charge != -1 ) { - continue; /* avoid enpoints that do not have an attachment */ - } - if ( !pStruct->endpoint[endpoint1] ) { - continue; /* should not happen */ - } - /* traverse endpoint1 edges to find the edge connecting it to the centerpoint */ - for ( k = 0; k < pEndp1->num_adj_edges; k ++ ) { - ecp1 = pBNS->edge + pEndp1->iedge[k]; - if ( ecp1->flow ) { - continue; - } - centerpoint = ecp1->neighbor12 ^ endpoint1; - if ( centerpoint >= pBNS->num_atoms ) { - break; - } - pCentp = pBNS->vert + centerpoint; - /* traverse centerpoint edges to find the 2nd endpoint */ - for ( n = 0, num_endp = 0; n < pCentp->num_adj_edges; n ++ ) { - ecp2 = pBNS->edge + pCentp->iedge[n]; - if ( ecp2->flow ) { - continue; - } - endpoint2 = ecp2->neighbor12 ^ centerpoint; - if ( endpoint2 >= pBNS->num_atoms ) { - break; - } - if ( !pStruct->endpoint[endpoint2] ) { - continue; - } - pEndp2 = pBNS->vert + endpoint2; - - if ( at2[endpoint2].num_H || at2[endpoint1].charge == -1 ) { - continue; - } - - /* we have found the path: - - Endp1 has no attachments, Endp2 has. - - Endp1 Endp1 - etg1 // \ ecp1 etg1 / \\ ecp1 - etg0 // \ etg0 / \\ - Endp0-----tg1 Centp --> Endp0=====tg1 Centp - ^ \\ / \\ / - radical | etg2 \\ / ecp2 etg2 \\ / ecp2 - Endp2 Endp2 - */ - - /* compare centerpoints */ - if ( !pCentp_found || - /* try to avoid carbons */ - (pVA[centerpoint].cNumValenceElectrons != 4 || - pVA[centerpoint].cPeriodicRowNumber != 1) && - pVA[centerpoint_found].cNumValenceElectrons == 4 && - pVA[centerpoint_found].cPeriodicRowNumber == 1 || - /* try a better non-carbon */ - (pVA[centerpoint].cNumValenceElectrons != 4 || - pVA[centerpoint].cPeriodicRowNumber != 1 ) && - (at[centerpoint].valence > at[centerpoint_found].valence || - at[centerpoint].valence == at[centerpoint_found].valence && - at[centerpoint].el_number > at[centerpoint_found].el_number) ) { - - pCentp_found = pCentp; - ecp1_found = ecp1; - centerpoint_found = centerpoint; - break; - } - } - } - } - } - if ( pCentp_found ) { - /* create a new radical at the centerpoint and try to cancel them */ - int delta = 1, ret3; - Vertex vPathStart, vPathEnd, v1, v2; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - - pCentp_found->st_edge.cap += delta; - pBNS->tot_st_cap += delta; - - v1 = pCentp_found - pBNS->vert; - v2 = pEndp0 - pBNS->vert; - ret3 = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret3 == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge % 2 == 0 ) { - ret3 = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret3 > 0 ) { - num_fixes ++; - tot_num_fixes ++; /* #1 */ - pCentp_found = NULL; - continue; - } - } else { - pCentp_found->st_edge.cap -= delta; - pBNS->tot_st_cap -= delta; - } - if ( ret3 < 0 ) { - ret = ret3; - goto exit_function; - } - } - /*---------------------------------------------------------------------------------------- - 3rd attempt: add radical to keep - ============== u,f=>unfixed, fixed edges (N electrons)%2 - (-) (-) (-) | - e0/ \\ e1 => u/ \\u => // \ v - / \\ ecp1 ecp2 / \\ u f // \ - C--X* Y(-)--C==Z C--X* Y(-)--C*--Z --X(-) Y===C---Z* - rad. endp not rad. endp rad not rad. endp not - Endp0 Endp1 endp endp to endp endp endp - Endp2 cancel - - Note: endpoints X and Y may belong to different t-groups - ----------------------------------------------------------------------------------------*/ - pCentp_found = NULL; - if ( i < num_endpoints ) { - int e0, e1; - if ( (e0=pVA[endpoint0].nCMinusGroupEdge-1)<0 || pBNS->edge[e0].forbidden ) { - continue; /* no negative charge on Endp0 is possible */ - } - /* a radical-tautomeric endpoint found; traverse all endpoints */ - for ( j = 0; j < pStruct->ti.nNumEndpoints; j ++ ) { - if ( iEndpoint+i == j ) { - continue; /* avoid the found radical endpoint */ - } - - endpoint1 = pStruct->ti.nEndpointAtomNumber[j]; - pEndp1 = pBNS->vert + endpoint1; /* another endpoint */ - - if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) { - continue; /* one more radical-endpoint! What is going on here??? */ - } - if ( ((e1=pVA[endpoint1].nCMinusGroupEdge-1)<0 || !pBNS->edge[e1].flow) || pBNS->edge[e1].forbidden ) { - continue; /* no negative charge on Endp1 */ - } - if ( !pStruct->endpoint[endpoint1] ) { - continue; /* should not happen */ - } - /* traverse endpoint1 edges to find the edge connecting it to the centerpoint */ - for ( k = 0; k < pEndp1->num_adj_edges; k ++ ) { - ecp1 = pBNS->edge + pEndp1->iedge[k]; /* e1C */ - if ( ecp1->flow || ecp1->forbidden ) { - continue; - } - centerpoint = ecp1->neighbor12 ^ endpoint1; - if ( centerpoint >= pBNS->num_atoms ) { - break; /* no more edges to atoms */ - } - pCentp = pBNS->vert + centerpoint; - if ( pStruct->endpoint[centerpoint] ) { - continue; /* do not set another endpoint's valence = an unusual value */ - } - /* traverse centerpoint edges to find edge connecting it to the endpoint0 */ - /* 1. Find a double bond to a not endpoint */ - ecp2 = NULL; - pEndp2 = NULL; - for ( n = 0, num_endp = 0; n < pCentp->num_adj_edges; n ++ ) { - ecp0 = pBNS->edge + pCentp->iedge[n]; - if ( ecp0->flow && !ecp0->forbidden ) { - endpoint2 = ecp0->neighbor12 ^ centerpoint; - if ( endpoint2 >= pBNS->num_atoms || pStruct->endpoint[endpoint2] ) { - continue; - } - /* check whether ecp0 is stereogenic: if it is then we cannot decrement its flow */ - for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[centerpoint].sb_parity[m]; m ++ ) { - if ( at[centerpoint].sb_ord[m] == n ) { - endpoint2 = NO_VERTEX; - break; - } - } - if ( endpoint2 == NO_VERTEX ) { - continue; - } - pEndp2 = pBNS->vert + endpoint2; - ecp2 = ecp0; /* e2C */ - break; - } - } - if ( !ecp2 ) - continue; - /* compare centerpoints */ - if ( !pCentp_found || - /* try to find carbons */ - !IS_C(endpoint2_found) && IS_C(endpoint2) || - IS_C(endpoint2_found) && IS_C(endpoint2) && - !IS_C(centerpoint_found) && IS_C(centerpoint) ) { - - pCentp_found = pCentp; - centerpoint_found = centerpoint; - endpoint2_found = endpoint2; - ecp2_found = ecp2; - ecp1_found = ecp1; - ecp0_found = pBNS->edge + e0; - break; - } - } - } - } - /* decrement st_flow, st_cap on Endp2; decrement flow on ecp2; decrement st_flow on Centp */ - /* result: radicals on Endp0 and Centp => run BNS */ - if ( pCentp_found ) { - Vertex vPathStart, vPathEnd, v1, v2; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - int delta; - - Vertex vEndp0 = ecp0_found->neighbor1; - Vertex vEndp1 = ecp1_found->neighbor12 ^ centerpoint_found; - Vertex vEndp2 = ecp2_found->neighbor12 ^ centerpoint_found; - BNS_EDGE *pe0 = ecp0_found; - BNS_EDGE *pe1 = pBNS->edge + (pVA[vEndp1].nCMinusGroupEdge - 1); - pEndp1 = pBNS->vert + vEndp1; - pEndp2 = pBNS->vert + vEndp2; - pCentp = pCentp_found; - if ( !ChargeEdgeList.num_alloc ) { - for ( n = 0; n < pStruct->num_atoms; n ++ ) { - if ( (k = pVA[n].nCMinusGroupEdge)>= 0 && !pBNS->edge[k].forbidden && - (ret = AddToEdgeList( &ChargeEdgeList, k, pStruct->num_atoms ) ) ) { - goto exit_function; - } - if ( (k = pVA[n].nCPlusGroupEdge)>= 0 && !pBNS->edge[k].forbidden && - (ret = AddToEdgeList( &ChargeEdgeList, k, pStruct->num_atoms ) ) ) { - goto exit_function; - } - } - } - if ( !BondEdgeList.num_alloc ) { - for ( n = 0; n < pBNS->num_bonds; n ++ ) { - if ( (ret = AddToEdgeList( &BondEdgeList, n, pBNS->num_bonds ) ) ) { - goto exit_function; - } - } - } - /* fix all bonds and charges */ - SetForbiddenEdgeMask( pBNS, &ChargeEdgeList, forbidden_edge_mask ); - SetForbiddenEdgeMask( pBNS, &BondEdgeList, forbidden_edge_mask ); - /* prepare flow for testing */ - delta = 1; - ecp2_found->flow -= delta; - pCentp->st_edge.flow -= delta; - pEndp2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - /* unfix edges to be changed */ - pe0->forbidden &= inv_forbidden_edge_mask; - pe1->forbidden &= inv_forbidden_edge_mask; - ecp1_found->forbidden &= inv_forbidden_edge_mask; - - pBNS->tot_st_cap += delta; - - v1 = vEndp0; - v2 = centerpoint_found; - ret2 = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret2 == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { - ret2 = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret2 > 0 ) { - num_fixes ++; - tot_num_fixes ++; /* #3 */ - pCentp_found = NULL; - } - } else { - /* roll back */ - ecp2_found->flow += delta; - pCentp->st_edge.flow += delta; - pEndp2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - RemoveForbiddenEdgeMask( pBNS, &ChargeEdgeList, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &BondEdgeList, forbidden_edge_mask ); - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - if ( !pCentp_found ) - continue; - } - - } - if ( !num_fixes ) { - break; - } - } - -/************ again ***********************************************************/ - while ( pBNS->tot_st_cap > pBNS->tot_st_flow && pStruct->ti.num_t_groups ) { - int iEndpoint = 0; - num_fixes = 0; - for ( itg = 0; itg < pStruct->ti.num_t_groups; iEndpoint += num_endpoints, itg ++ ) { - pCentp_found=NULL; - tgroup_number = pStruct->ti.t_group[itg].nGroupNumber; - num_endpoints = pStruct->ti.t_group[itg].nNumEndpoints; - for ( i = 0; i < num_endpoints; i ++ ) { - endpoint0 = pStruct->ti.nEndpointAtomNumber[iEndpoint+i]; - pEndp0 = pBNS->vert + endpoint0; /* taut endpoint vertex (possible location of mobile H */ - if ( pEndp0->st_edge.cap > pEndp0->st_edge.flow ) { - /* radical endpoint1 has been detected */ - /* find a 1-3 centerpoint that has two or more endpoints */ - /* connected to the t-group vertex by edges with flow>0 and */ - /* to the centerpoint by edges with flow = 0 */ - /* after that: (1) increment etg1 flow to eliminate radical */ - /* (2) increment flow on one of the two other edges to the t-group */ - /* (3) increment st_cap on the found centerpoint */ - /* (4) rerun the BNS and re-create the structure */ - break; - } - } - /* 4th attempt */ - if ( i < num_endpoints ) { - /* tautomeric endpoint found; traverse its t-group edges */ - pEndp2_found = NULL; - for ( j = 0; j < pEndp0->num_adj_edges; j ++ ) { - ecp0 = pBNS->edge + pEndp0->iedge[j]; - centerpoint = ecp0->neighbor12 ^ endpoint0; - if ( centerpoint >= pBNS->num_atoms || ecp0->flow || pStruct->endpoint[centerpoint] ) { - continue; /* ignore non-single bonds, orig. InChI endpoints, and fictitious atoms */ - } - pCentp = pBNS->vert + centerpoint; - for ( k = 0; k < pCentp->num_adj_edges; k ++ ) { - ecp1 = pBNS->edge + pCentp->iedge[k]; - endpoint1 = ecp1->neighbor12 ^ centerpoint; - if ( endpoint1 >= pBNS->num_atoms || !ecp1->flow || pStruct->endpoint[endpoint1] ) { - continue; /* ignore single bonds, orig. InChI endpoints, and fictitious atoms */ - } - pEndp1 = pBNS->vert + endpoint1; - if ( endpoint1 == endpoint0 || pEndp1->st_edge.cap != pEndp1->st_edge.flow ) { - continue; /* ignore radicals */ - } - if ( !pEndp2_found || - /* try to find carbons */ - !IS_C(endpoint2_found) && IS_C(endpoint1) || - IS_C(endpoint2_found) && IS_C(endpoint1) && - !IS_C(centerpoint_found) && IS_C(centerpoint) ) { - pEndp2_found = pEndp1; - pCentp_found = pCentp; - endpoint2_found = endpoint1; - centerpoint_found = centerpoint; - ecp1_found = ecp0; - ecp2_found = ecp1; - } - } - } - if ( pEndp2_found ) { - /* move radical from pEndp0 to pEndp2 */ - pEndp0->st_edge.flow ++; - ecp1_found->flow ++; - ecp2_found->flow --; - pEndp2_found->st_edge.flow --; - pEndp2_found = NULL; - pCentp_found = NULL; - num_fixes ++; /* #4 */ - tot_num_fixes ++; - continue; - } - } - } - if ( !num_fixes ) { - break; - } - } - - - ret = tot_num_fixes; -exit_function: - AllocEdgeList( &ChargeEdgeList, EDGE_LIST_FREE); - AllocEdgeList( &BondEdgeList, EDGE_LIST_FREE); - - pStruct->at = at; - memcpy( at2, at, len_at*sizeof(at2[0])); - return ret; -#undef IS_C -} -/************************************************************************************************/ -/* move (+) charges to >N- and other centerpoints */ -int MoveChargeToMakeCenerpoints(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ - int i, j, neigh, num_endpoints, tg_group=0, num_success; - int ret2, ret, delta; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - - /* for RunBnsTestOnce */ - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - - BNS_EDGE *pEdgePlus, *pEdgeMinus; - Vertex v1p, v2p, v1m, v2m; - BNS_VERTEX *pv1p, *pv2p, *pv1m, *pv2m; - - ret = 0; - num_success = 0; - /* to simplify, prepare new at[] from pBNS */ - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - - for ( i = 0; i < num_at; i ++ ) { - if ( pVA[i].cNumValenceElectrons != 4 && /* not C, Si, Ge */ - !pVA[i].cMetal && !pVA[i].nTautGroupEdge && - !at2[i].num_H && at2[i].valence >= 3 && - at2[i].valence == at2[i].chem_bonds_valence && - !at2[i].charge && pVA[i].nCPlusGroupEdge > 0 && - is_centerpoint_elem( at2[i].el_number ) ) { - for ( j = 0, num_endpoints = 0; j < at2[i].valence; j ++ ) { - neigh = at2[i].neighbor[j]; - if ( at2[neigh].endpoint ) { - if ( !num_endpoints ) { - tg_group = at2[neigh].endpoint; - } else - if ( tg_group != at2[neigh].endpoint ) { - break; /* not a centerpoint */ - } - num_endpoints ++; - } - } - if ( j == at2[i].valence && num_endpoints > 1 ) { - /* found possible centerpoint */ - pEdgePlus = pBNS->edge + (pVA[i].nCPlusGroupEdge-1); - pEdgeMinus = (pVA[i].nCMinusGroupEdge > 0)? pBNS->edge + (pVA[i].nCMinusGroupEdge-1) : NULL; - if ( pEdgePlus->flow + (pEdgeMinus? pEdgeMinus->flow : 0) != 1 ) { - continue; - } - v1p = pEdgePlus->neighbor1; - v2p = pEdgePlus->neighbor12 ^ v1p; - pv1p = pBNS->vert + v1p; - pv2p = pBNS->vert + v2p; - if ( pEdgeMinus ) { - v1m = pEdgeMinus->neighbor1; - v2m = pEdgeMinus->neighbor12 ^ v1m; - pv1m = pBNS->vert + v1m; - pv2m = pBNS->vert + v2m; - } else { - v1m = NO_VERTEX; - v2m = NO_VERTEX; - pv1m = NULL; - pv2m = NULL; - } - ret = 0; - /* set new flow to run BNS Search */ - if ( delta = pEdgePlus->flow ) { - /* positive charge <=> flow=0 on (=) edge */ - pEdgePlus->flow -= delta; - pv1p->st_edge.flow -= delta; - pv2p->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - pEdgePlus->forbidden |= forbidden_edge_mask; - if ( pEdgeMinus ) { - pEdgeMinus->forbidden |= forbidden_edge_mask; - } - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret == 1 && (vPathEnd == v1p && vPathStart == v2p || - vPathEnd == v2p && vPathStart == v1p) && - nDeltaCharge == -1 /* charge moving to this atom disappers*/ ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else - if ( ret == 1 ) { - *pnTotalDelta += ret; - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - } else { - ret = 0; - pEdgePlus->flow += delta; - pv1p->st_edge.flow += delta; - pv2p->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - pEdgePlus->forbidden &= inv_forbidden_edge_mask; - if ( pEdgeMinus ) { - pEdgeMinus->forbidden &= inv_forbidden_edge_mask; - } - } else - if ( pEdgeMinus && (delta == pEdgeMinus->flow) && pEdgePlus->flow == 0 ) { - /* positive charge <=> flow=0 on (=) edge and flow=0 on (-) edge */ - pEdgeMinus->flow -= delta; - pv1m->st_edge.flow -= delta; - pv2m->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - pEdgePlus->forbidden |= forbidden_edge_mask; - pEdgeMinus->forbidden |= forbidden_edge_mask; - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret == 1 && (vPathEnd == v1m && vPathStart == v2m || - vPathEnd == v2m && vPathStart == v1m) && - nDeltaCharge == -1 /* charge moving to this atom disappers*/ ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else - if ( ret == 1 ) { - *pnTotalDelta += ret; - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - } else { - ret = 0; - pEdgeMinus->flow += delta; - pv1m->st_edge.flow += delta; - pv2m->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - pEdgePlus->forbidden &= inv_forbidden_edge_mask; - pEdgeMinus->forbidden &= inv_forbidden_edge_mask; - } - if ( ret ) { - num_success ++; - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - } - } - } - } - ret = num_success; -exit_function: - return ret; -} -#endif diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichirvr3.c b/INCHI-1-SRC/INCHI_API/inchi_dll/ichirvr3.c deleted file mode 100644 index 2c61302..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichirvr3.c +++ /dev/null @@ -1,5530 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - -/*#define CHECK_WIN32_VC_HEAP*/ -#include "mode.h" - -#if ( READ_INCHI_STRING == 1 ) - -#include "ichi.h" -#include "ichitime.h" - -#include "inpdef.h" -#include "ichimain.h" -#include "ichierr.h" -#include "incomdef.h" -#include "ichiring.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "util.h" - -#include "ichicomp.h" -#include "ichister.h" - -#include "ichi_bns.h" - -#include "strutil.h" - -#include "ichirvrs.h" - -#define INC_ADD_EDGE 64 - -/* local types */ - -/* types for TgDiffHChgFH */ -#define fNumRPosChgH 0 /* number of positive charges on endpoints that have H in at2[] */ -#define fNumRPosChgU 1 /* number of positive charges on endpoints that have no H in at2[] */ -#define fNumRNegChgO 2 /* number of negative charges on O endpoints */ -#define fNumRNegChgN 3 /* number of negative charges on N endpoints */ -#define fNumRNeutrlH 4 /* number of neutral endp that have H in at2[] */ - -#define fNumNPosChgH 5 /* number of positive charges on endpoints that have H in atf[] */ -#define fNumNPosChgU 6 /* number of positive charges on endpoints that have no H in atf[] */ -#define fNumNNegChgO 7 /* number of negative charges on O endpoints */ -#define fNumNNegChgN 8 /* number of negative charges on N endpoints */ -#define fNumNNeutrlH 9 /* number of neutral endp that have H in atf[] */ - -#define fNumAllChgT 10 /* total number of fNum... */ - -typedef struct tagTgDiffHChgFH { - short itg; /* t-group index; endpoint = itg+1 */ - short nNumHInchi; /* number of H in t-group from orig. InChI */ - short nNumHRevrs; /* number of H in at2[] */ - short nNumHNorml; /* number of H in Normalized atfMobile_H_Revrs[] */ - short nNumMInchi; /* number of (-) in InChI */ - short nNumMRevrs; /* number of (-) in at2[] */ - short nNumMNorml; /* number of (-) in atf[] */ - short nNumPRevrs; /* number of (+) in at2[] */ - short nNumPNorml; /* number of (+) in Normalized atfMobile_H_Revrs[] */ - short n[fNumAllChgT]; /* all numbers */ - short i[fNumAllChgT]; /* all indices */ -} TgDiffHChgFH; - -/* local prototypes */ -static int FillTgDiffHChgFH( TgDiffHChgFH tdhc[], int max_tdhc, inp_ATOM at2[], inp_ATOM atf[], - AT_NUMB *nCanon2AtnoRevrs, VAL_AT *pVA, T_GROUP_INFO *ti, EDGE_LIST *pAtomIndList ); - - -/************************************************************/ -int bHas_N_V( inp_ATOM *at2, int num_atoms ) -{ - static U_CHAR el_number_N; - int i, num_found = 0; - if ( !el_number_N ) { - el_number_N = get_periodic_table_number( "N" ); - } - for ( i = 0; i < num_atoms; i ++ ) { - if ( at2[i].el_number == el_number_N && !at2[i].charge && - !at2[i].num_H && !at2[i].radical && - at2[i].chem_bonds_valence == 5 && - (at2[i].valence==3) ) { - num_found ++; - } - } - return num_found; -} -/*************************************************************************************/ -int FillTgDiffHChgFH( TgDiffHChgFH tdhc[], int max_tdhc, inp_ATOM at2[], - inp_ATOM atf[], AT_NUMB *nCanon2AtnoRevrs, VAL_AT *pVA, - T_GROUP_INFO *ti, EDGE_LIST *pAtomIndList ) -{ - - int i, j, iat, itg, itg_prev, num, itg_out, bOverflow; - EDGE_LIST IndList; /* type, itg */ - TgDiffHChgFH cur_tdhc; - AT_NUMB *pEndp0; - inp_ATOM *at2i, *atfi; - int typeR, typeN, type, ret = 0, nCurIndListLen; - - AllocEdgeList( &IndList, EDGE_LIST_CLEAR ); - pAtomIndList->num_edges = 0; - itg_out = 0; - bOverflow = 0; - memset( tdhc, 0, max_tdhc * sizeof(tdhc[0]) ); - - for ( itg = 0; itg < ti->num_t_groups; itg ++ ) { - memset( &cur_tdhc, 0, sizeof(cur_tdhc) ); - - cur_tdhc.itg = itg; - cur_tdhc.nNumHInchi = ti->t_group[itg].num[0] - ti->t_group[itg].num[1]; - cur_tdhc.nNumMInchi = ti->t_group[itg].num[1]; - - pEndp0 = ti->nEndpointAtomNumber + ti->t_group[itg].nFirstEndpointAtNoPos; - nCurIndListLen = IndList.num_edges; - for ( j = 0; j < ti->t_group[itg].nNumEndpoints; j ++ ) { - i = pEndp0[j]; - iat = nCanon2AtnoRevrs[i]; - - at2i = at2 + iat; - atfi = atf + iat; - - typeR = typeN = -1; - if ( at2i->charge == 1 ) { - if ( at2i->num_H ) { - typeR = fNumRPosChgH; - } else { - typeR = fNumRPosChgU; - } - cur_tdhc.nNumPRevrs ++; - } else - if ( at2i->charge == -1 ) { - if ( pVA[iat].cNumValenceElectrons == 6) { - typeR = fNumRNegChgO; - } else - if ( pVA[iat].cNumValenceElectrons == 5) { - typeR = fNumRNegChgN; - } - cur_tdhc.nNumMRevrs ++; - } else - if ( at2i->num_H && at2i->valence == at2i->chem_bonds_valence ) { - typeR = fNumRNeutrlH; - } - cur_tdhc.nNumHRevrs += at2i->num_H; - - if ( atfi->charge == 1 ) { - if ( atfi->num_H ) { - typeN = fNumNPosChgH; - } else { - typeN = fNumNPosChgU; - } - cur_tdhc.nNumPNorml ++; - } else - if ( atfi->charge == -1 ) { - if ( pVA[iat].cNumValenceElectrons == 6) { - typeN = fNumNNegChgO; - } else - if ( pVA[iat].cNumValenceElectrons == 5) { - typeN = fNumNNegChgN; - } - cur_tdhc.nNumMNorml ++; - } else - if ( atfi->num_H && atfi->valence == atfi->chem_bonds_valence ) { - typeN = fNumNNeutrlH; - } - cur_tdhc.nNumHNorml += atfi->num_H; - if ( at2[iat].charge < 0 || 0 < pVA[iat].nCPlusGroupEdge ) { - if ( typeR >= 0 && ( - (ret = AddToEdgeList( &IndList, typeR, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &IndList, itg, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &IndList, iat, INC_ADD_EDGE )) ) ) { - goto exit_function; - } - if ( typeN >= 0 && ( - (ret = AddToEdgeList( &IndList, typeN, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &IndList, itg, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &IndList, iat, INC_ADD_EDGE )) ) ) { - goto exit_function; - } - } - - } - if ( cur_tdhc.nNumHNorml == cur_tdhc.nNumHInchi && - cur_tdhc.nNumMNorml == cur_tdhc.nNumMInchi ) { - IndList.num_edges = nCurIndListLen; /* t-group seems to be correct */ - continue; - } - if ( itg_out < max_tdhc ) { - tdhc[itg_out ++] = cur_tdhc; - } else { - bOverflow |= 1; - IndList.num_edges = nCurIndListLen; - break; - } - } - /* fill out atom index list */ - if ( itg_out ) { - itg_prev = IndList.pnEdges[1]; /* the 1st saved t-group number */ - for ( type = 0; type < fNumAllChgT; type ++ ) { - j = 0; - for ( i = 0; i < itg_out; i ++ ) { - num = 0; - itg = tdhc[i].itg; - tdhc[i].i[type] = -999; /* empty */ - while( IndList.pnEdges[j+1] == itg ) { - if ( IndList.pnEdges[j] == type ) { - if ( !num ++ ) { - tdhc[i].i[type] = pAtomIndList->num_edges; - } - if ( ret = AddToEdgeList( pAtomIndList, IndList.pnEdges[j+2], INC_ADD_EDGE )) { - goto exit_function; - } - } - j += 3; - } - tdhc[i].n[type] = num; - } - } - } - ret = itg_out; -exit_function: - AllocEdgeList( &IndList, EDGE_LIST_FREE ); - return ret; - -/* -#undef fNumRPosChgH -#undef fNumRPosChgU -#undef fNumRNegChgO -#undef fNumRNegChgN - -#undef fNumNPosChgH -#undef fNumNPosChgU -#undef fNumNNegChgO -#undef fNumNNegChgN - -#undef fNumAllChgT -*/ -} - -/***********************************************************************************************/ -int FixFixedHRestoredStructure(ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, - StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, T_GROUP_INFO **ppt_group_info, inp_ATOM **ppat_norm, - inp_ATOM **ppat_prep, INChI *pInChI[], long num_inp, int bHasSomeFixedH, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask) -{ - /*--------- process extra or missing Fixed-H on non-tautomeric atoms ------*/ - /* at2 should be the most recently restored atom, Fixed-H */ - int i, j, k, delta, num_try, tot_succes, cur_success, ret = 0, bAllowedNFlowerEdges=0, num_zero_ret; - CMP2FHINCHI c2i; - CMP2FHINCHI *pc2i = &c2i; - - EDGE_LIST AllChargeEdges, CurrEdges, SFlowerEdges, NFlowerEdges, OtherNFlowerEdges, FixedLargeRingStereoEdges; - EDGE_LIST AllBondEdges; - - EdgeIndex e; - BNS_EDGE *pe; - Vertex v1, v2; - BNS_VERTEX *pv1, *pv2; - - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - - int nNumRunBNS = 0, forbidden_edge_mask_inv = ~forbidden_edge_mask; - - INCHI_HEAPCHK - - AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &CurrEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &NFlowerEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &SFlowerEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &OtherNFlowerEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &AllBondEdges, EDGE_LIST_CLEAR ); - - tot_succes = 0; - - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - - for ( i = 0; i < pStruct->num_atoms; i ++ ) { - if ( (e=pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - (ret = AddToEdgeList( &AllChargeEdges, e, INC_ADD_EDGE )) ) { - goto exit_function; - } - if ( (e=pVA[i].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - if ( ret = AddToEdgeList( &AllChargeEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - - /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ - if ( pVA[i].cNumValenceElectrons == 5 && !pVA[i].cMetal && /* N, P, As */ - NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e ))) { - - if ( pBNS->edge[j].forbidden ) { - continue; - } - - if ( pBNS->edge[j].flow ) { - if ( ret = AddToEdgeList( &AllChargeEdges, j, INC_ADD_EDGE ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NFlowerEdges, j, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else { - if ( ret = AddToEdgeList( &OtherNFlowerEdges, j, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } else - /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ - if ( pVA[i].cNumValenceElectrons == 6 && !pVA[i].cMetal && /* N, P, As */ - NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e ))) { - - if ( pBNS->edge[j].forbidden ) { - continue; - } - - if ( pBNS->edge[j].flow ) { - if ( ret = AddToEdgeList( &SFlowerEdges, j, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - - } - for ( j = 0; j < at2[i].valence; j ++ ) { - k = at2[i].neighbor[j]; - if ( k < i && !pBNS->edge[e=pBNS->vert[i].iedge[j]].forbidden ) { - if ( ret = AddToEdgeList( &AllBondEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - } - if ( forbidden_stereo_edge_mask ) { - for ( i = 0; i < pStruct->num_atoms; i ++ ) { - for ( j = 0; j < at2[i].valence; j ++ ) { - if ( pBNS->edge[k = pBNS->vert[i].iedge[j]].forbidden == forbidden_stereo_edge_mask ) { - int nMinRingSize = is_bond_in_Nmax_memb_ring( at2, i, j, pStruct->pbfsq->q, - pStruct->pbfsq->nAtomLevel, - pStruct->pbfsq->cSource, 99 /* max ring size */ ); - if ( 0 < nMinRingSize && (ret = AddToEdgeList( &FixedLargeRingStereoEdges, k, INC_ADD_EDGE ))) { - goto exit_function; - } - } - } - } - } - - INCHI_HEAPCHK - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - INCHI_HEAPCHK - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - - INCHI_HEAPCHK - - if ( !pc2i->bHasDifference || - !pc2i->len_c2at && pc2i->nNumTgRevrs == pc2i->nNumTgInChI && - pc2i->nNumEndpRevrs == pc2i->nNumRemHInChI && - pc2i->nNumEndpRevrs == pc2i->nNumEndpInChI && - !pc2i->nNumTgDiffMinus && !pc2i->nNumTgDiffH ) { - goto exit_function; /* nothing to do */ - } - - /*goto exit_function;*/ /* debug only*/ - - if ( pc2i->len_c2at >= 2 ) { - /*----------------------------------------------------*/ - /* case 01: restored: O=AB-O(-) original: (-)O-AB=O */ - /* FixH: 0 -1 -1 0 */ - /* MobH: 0 1 1 0 */ - /* non-taut non-taut */ - /* O = O, S, Se; charged atoms O are not tautomeric */ - /* Solution: move (-) from B-O(-) to O=A */ - /*----------------------------------------------------*/ - int num_DB_O = 0, num_SB_O_Minus = 0, iat; - short iat_DB_O[MAX_DIFF_FIXH], iat_SB_O_Minus[MAX_DIFF_FIXH]; - cur_success = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( pc2i->c2at[i].nValElectr == 6 /* && !pc2i->c2at[i].endptInChI -- mod#1*/ && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - if ( /* orig. InChI info: */ - num_SB_O_Minus < MAX_DIFF_FIXH && - pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI == 0 && - /* reversed structure info: */ - pc2i->c2at[i].nFixHRevrs == -1 && pc2i->c2at[i].nMobHRevrs == 1 && - pc2i->c2at[i].nAtChargeRevrs == -1 && !at2[iat].num_H && /* at2 is Fixed-H */ - at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 1 ) { - iat_SB_O_Minus[num_SB_O_Minus ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else - if ( /* orig. InChI info: */ - num_DB_O < MAX_DIFF_FIXH && - pc2i->c2at[i].nFixHInChI == -1 && pc2i->c2at[i].nMobHInChI == 1 && - /* reversed structure info: */ - pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && - pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && - at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 ) { - iat_DB_O[num_DB_O ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - } - if ( num_try = inchi_min( num_SB_O_Minus, num_DB_O ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_SB_O_Minus && cur_success < num_try; i ++ ) { - iat = iat_SB_O_Minus[i]; - pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; /* remove (-) from AB-O(-) */ - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Added (-)charge to O=AB => nDeltaCharge == -1 */ - /* Flow change on pe (-)charge edge (atom B-O(-)) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 01 */ - } - } else { - pe->forbidden &= forbidden_edge_mask_inv; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - CurrEdges.num_edges = 0; /* clear current edge list */ - } - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pc2i->len_c2at >= 1 ) { - /*--------------------------------------------------------------*/ - /* case 02: restored: -O(+)=AB-NH2 original: -O-AB=NH2(+) */ - /* FixH: 0 0 0 1 */ - /* MobH: 0 2 0 1 */ - /* O = P, As, Sb, O, S, Se, F, Cl, Br, I; not taut. in InChI */ - /* N = N, O, S, Se, Te; has H; tautomeric or not tautomeric */ - /* Solution: move (+) from O(+) to NH2 */ - /*--------------------------------------------------------------*/ - int num_DB_O_Plus = 0, num_SB_NH = 0, iat; - short iat_DB_O_Plus[MAX_DIFF_FIXH], iat_SB_NH[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : NULL; - cur_success = 0; - num_zero_ret = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( /* orig. InChI info: =NH2(+), =OH(+) */ - num_SB_NH < MAX_DIFF_FIXH && - (pc2i->c2at[i].nValElectr == 5 && pc2i->c2at[i].nPeriodNum == 1 || - pc2i->c2at[i].nValElectr == 6 ) /* N, O, S, Se, Te */ && - /*!pc2i->c2at[i].endptInChI &&*/ /* <=== relaxation */ - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pc2i->c2at[i].nFixHInChI>0 /*== 1 --modification#2*/ && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ - /* reversed structure info: */ - pc2i->c2at[i].nFixHRevrs == 0 && /* pc2i->c2at[i].nMobHRevrs == 0 &&*/ - pc2i->c2at[i].nAtChargeRevrs == 0 && at2[iat].num_H && - at2[iat].valence == at2[iat].chem_bonds_valence ) { - iat_SB_NH[num_SB_NH ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( /* in restored atom: charge=+1, no H, has double bond, P, As, O, S, Se, Te, F, Cl, Br, I */ - num_DB_O_Plus < MAX_DIFF_FIXH && - at2[iat].charge == 1 && !at2[iat].num_H && - at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - (pVA[iat].cNumValenceElectrons == 6 || pVA[iat].cNumValenceElectrons == 7 || - pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber > 1) && - /* in orig.InChI: not an endpoint, has no H */ - !pStruct->endpoint[i] && - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i] ) && - /* has (+) edge */ - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - iat_DB_O_Plus[num_DB_O_Plus ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( num_try = inchi_min( num_DB_O_Plus, num_SB_NH ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; -repeat_02_allow_NV: - for ( i = 0; i < num_SB_NH && cur_success < num_try; i ++ ) { - iat = iat_SB_NH[i]; - pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { - /* Removed charge from O(+) => nDeltaCharge == -1 */ - /* Flow change on pe (+)charge edge (atom NH2) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 02 */ - } - } else { - num_zero_ret += !ret; - pe->forbidden &= forbidden_edge_mask_inv; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - if ( num_zero_ret == num_try && !bAllowedNFlowerEdges && NFlowerEdges.num_edges ) { - RemoveForbiddenEdgeMask( pBNS, &NFlowerEdges, forbidden_edge_mask ); - bAllowedNFlowerEdges = 1; - goto repeat_02_allow_NV; - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - bAllowedNFlowerEdges = 0; - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pc2i->len_c2at >= 1 && pc2i->nNumTgRevrs == 1 && - (pc2i->nNumEndpRevrs > pc2i->nNumEndpInChI || pc2i->nNumTgInChI > 1) /* ADP in Revrs */ ) { - /*--------------------------------------------------------------*/ - /* case 03: restored: -N(-)-AB=O original: -N=AB-O(-) */ - /* FixH: 0 0 0 -1 */ - /* MobH: 0 0 0 1 */ - /* O = O, S, Se; N = N; */ - /* restored atoms are tautomeric; original atoms are not taut. */ - /* restored struct has 1 t-group; original has less endpoints */ - /* and possibly >1 t-groups */ - /* Solution: move (-) from N(-) to =O */ - /* these atoms are tautomeric in restored structure */ - /*--------------------------------------------------------------*/ - int num_SB_N_Minus = 0, num_DB_O = 0, iat; - short iat_SB_N_Minus[MAX_DIFF_FIXH], iat_DB_O[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - /* - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - */ - cur_success = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( /* orig. InChI info: -O(-) */ - num_DB_O < MAX_DIFF_FIXH && - pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ && - !pc2i->c2at[i].endptInChI && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pc2i->c2at[i].nFixHInChI == -1 && pc2i->c2at[i].nMobHInChI == 1 && - /* reversed structure info: */ - pc2i->c2at[i].endptRevrs && - pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && - pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && - at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 ) { - iat_DB_O[num_DB_O ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( /* in restored atom N: charge=-1, no H, has no double bond, endpoint */ - num_SB_N_Minus < MAX_DIFF_FIXH && - at2[iat].charge == -1 && /*!at2[iat].num_H &&*/ - at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && - at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint && - /* in orig.InChI: not an endpoint, has no H */ - /* !pStruct->endpoint[i] && */ - /* - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i] ) && - */ - /* has (-) edge */ - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - iat_SB_N_Minus[num_SB_N_Minus ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( num_try = inchi_min( num_SB_N_Minus, num_DB_O ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_SB_N_Minus && cur_success < num_try; i ++ ) { - iat = iat_SB_N_Minus[i]; - pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; /* 2006-03-03: changed from CPlusGroupEdge */ - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Added (-) charge to =O => nDeltaCharge == 1 */ - /* Flow change on pe (-)charge edge (atom -N(-)-) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 03 */ - } - } else { - pe->forbidden &= forbidden_edge_mask_inv; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pc2i->nNumTgRevrs == 1 && /* pc2i->nNumRemHInChI < 0 &&*/ - (pc2i->nNumEndpRevrs > pc2i->nNumEndpInChI || pc2i->nNumTgInChI > 1) /* ADP in Revrs */ ) { - /*--------------------------------------------------------------*/ - /* case 03a:restored: -N(-)-AB=O original: -N=AB-O(-) */ - /* FixH: 0 0 0 0 */ - /* MobH: 0 0 0 0 */ - /* O = O, S, Se; N = N; taut */ - /* restored atoms are tautomeric; original atom is; N may be. */ - /* restored struct has 1 t-group; original has less endpoints */ - /* and possibly >1 t-groups */ - /* Solution: move (-) from N(-) to =O */ - /* these atoms are tautomeric in restored structure */ - /*--------------------------------------------------------------*/ - int num_SB_N_Minus = 0, num_DB_O = 0, iat; - short iat_SB_N_Minus[MAX_DIFF_FIXH], iat_DB_O[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - S_CHAR *pnMobHInChI = (pInChI[1] && pInChI[1]->nNum_H)? pInChI[1]->nNum_H : - (pInChI[0] && pInChI[0]->nNum_H)? pInChI[0]->nNum_H : NULL; - S_CHAR *pnFixHInChI = pStruct->fixed_H; - - cur_success = 0; - CurrEdges.num_edges = 0; - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( /* in restored atom N: charge=-1, no H, has no double bond, endpoint */ - num_SB_N_Minus < MAX_DIFF_FIXH && - at2[iat].charge == -1 && /*!at2[iat].num_H &&*/ - at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && - at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint && - /* in orig.InChI: may be an endpoint, has no H */ - /* has (-) edge */ - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - iat_SB_N_Minus[num_SB_N_Minus ++] = iat; - } else - if ( num_DB_O < MAX_DIFF_FIXH && - at2[iat].charge == 0 && /*!at2[iat].num_H &&*/ - at2[iat].valence+1 == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - pVA[iat].cNumValenceElectrons == 6 && - at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint && /* endpoint in Reconstructed */ - (pStruct->endpoint[i] || /* endpoint or H(+) acceptor in original */ - pnMobHInChI && pnMobHInChI[i] == 1 && pnFixHInChI && pnFixHInChI[i] == -1 ) && - /* has (-) edge */ - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - iat_DB_O[num_DB_O ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( num_try = inchi_min( num_SB_N_Minus, num_DB_O ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - /* allow charge transfer to all found =O */ - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_SB_N_Minus && cur_success < num_try; i ++ ) { - iat = iat_SB_N_Minus[i]; - pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Added (-) charge to =O => nDeltaCharge == 1 */ - /* Flow change on pe (-)charge edge (atom -N(-)-) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 03a */ - } - } else { - pe->forbidden &= forbidden_edge_mask_inv; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pc2i->len_c2at >= 1 && pc2i->nNumTgInChI == 1 && /* ADP in InChI */ - (pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1) ) { - /*--------------------------------------------------------------*/ - /* case 04: restored: OH(+)=AB-O- OH- orig. HO-AB=O(+)- OH- */ - /* FixH: 1 0 0 1 0 1 */ - /* MobH: 0 0 1 0 0 0 */ - /* non-taut. taut taut */ - /* ADP: one t-group or more endpoints */ - /* O(+) = N, P, As, As, O, S, Se; OH = N, O, S, Se, Te */ - /* Solution: move (+) from O(+) to NH2 */ - /*--------------------------------------------------------------*/ - int num_SB_Neutr = 0, num_DB_Charged = 0, iat; - short iat_SB_Neutr[MAX_DIFF_FIXH], iat_DB_Charged[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - cur_success = 0; - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( /* in restored atom: charge=+1, has H, has double bond, N, O, S, Se, Te */ - num_DB_Charged < MAX_DIFF_FIXH && - at2[iat].charge == 1 && at2[iat].num_H && - at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - (pVA[iat].cNumValenceElectrons == 6 || - pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1) && - /* in orig.InChI: an endpoint, has fixed-H */ - pStruct->endpoint[i] && - (pStruct->fixed_H && pStruct->fixed_H[i]) && - /*!(nMobHInChI && nMobHInChI[i] ) &&*/ - /* has (+) edge */ - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - - iat_DB_Charged[num_DB_Charged ++] = iat; - - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else - if ( /* in restored atom: charge=0, has no H, has no double bond, N, P, O, S, Se, Te */ - num_SB_Neutr < MAX_DIFF_FIXH && - at2[iat].charge == 0 && !at2[iat].num_H && - at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - (pVA[iat].cNumValenceElectrons == 6 || - pVA[iat].cNumValenceElectrons == 5 ) && - /* in orig.InChI: an endpoint, has fixed-H */ - /* pStruct->endpoint[i] && */ - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i] ) && - /* has (+) edge */ - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && - 0 == pBNS->edge[e].forbidden ) { - - iat_SB_Neutr[num_SB_Neutr ++] = iat; - - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( num_try = inchi_min( num_SB_Neutr, num_DB_Charged ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_SB_Neutr && cur_success < num_try; i ++ ) { - iat = iat_SB_Neutr[i]; - pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { - /* Removed charge from O(+) => nDeltaCharge == -1 */ - /* Flow change on pe (+)charge edge (atom NH2) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 04 */ - } - } else { - pe->forbidden &= forbidden_edge_mask_inv; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pc2i->len_c2at > 1 ) { - /*--------------------------------------------------------------*/ - /* case 05: restored: O=AB-NH original:(-)O-AB=NH(+) */ - /* FixH: 0 0 -1 1 */ - /* MobH: 0 1 1 0 */ - /* O = O, S, Se; N = N, O, S, Se, Te; all atoms not tautomeric */ - /* Solution: Separate charges */ - /*--------------------------------------------------------------*/ - int num_DB_O = 0, num_SB_NH = 0, iat; - short iat_DB_O[MAX_DIFF_FIXH], iat_SB_NH[MAX_DIFF_FIXH]; - cur_success = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( /* orig. InChI info: =NH2(+), =OH(+) */ - num_SB_NH < MAX_DIFF_FIXH && - (pc2i->c2at[i].nValElectr == 5 && pc2i->c2at[i].nPeriodNum == 1 || - pc2i->c2at[i].nValElectr == 6 ) /* N, O, S, Se, Te */ && - !pc2i->c2at[i].endptInChI && - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pc2i->c2at[i].nFixHInChI == 1 && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ - /* reversed structure info: */ - pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs && - pc2i->c2at[i].nAtChargeRevrs == 0 && at2[iat].num_H && - !pc2i->c2at[i].endptRevrs && - at2[iat].valence == at2[iat].chem_bonds_valence ) { - iat_SB_NH[num_SB_NH ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else - if ( /* orig. InChI info: -O(-) */ - num_DB_O < MAX_DIFF_FIXH && - (pc2i->c2at[i].nValElectr == 6 ) /* O, S, Se, Te */ && - !pc2i->c2at[i].endptInChI && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pc2i->c2at[i].nFixHInChI == -1 && pc2i->c2at[i].nMobHInChI == 1 && - /* reversed structure info: */ - pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && - pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && - !pc2i->c2at[i].endptRevrs && - at2[iat].valence + 1 == at2[iat].chem_bonds_valence ) { - iat_DB_O[num_DB_O ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( num_try = inchi_min( num_DB_O, num_SB_NH ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_SB_NH && cur_success < num_try; i ++ ) { - iat = iat_SB_NH[i]; - pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Added charge to =O => nDeltaCharge == 1 */ - /* Flow change on pe (+)charge edge (atom NH2) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 05 */ - } - } else { - pe->forbidden &= forbidden_edge_mask_inv; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pStruct->fixed_H && pStruct->endpoint && pc2i->nChargeFixHInChI > 0 && pc2i->nChargeFixHInChI > pc2i->nChargeMobHInChI ) { - /*----------------------------------------------------------*/ - /* case 06c: restored -NH- or -NH(+) orig: -NH- */ - /* Fixed-H 1 1 0 */ - /* Mobile-H 0 0 1 */ - /* not tautomeric not tautomeric */ - /* has adjacent (+) */ - /* charges */ - /* Solution: move (+) charges to the -NH- unless it already*/ - /* N = N, O, S, Se, Te */ - /* has (+) charge blocked by adjacent (+) */ - /*----------------------------------------------------------*/ - int iat; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - /* - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - inp_ATOM *atfMobile_H_Revrs = pStruct->pOne_norm_data[TAUT_YES] && - pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds? - pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds : NULL; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : NULL; - */ - EDGE_LIST CurChargeEdges; - EdgeIndex e2; - cur_success = 0; - AllocEdgeList( &CurChargeEdges, EDGE_LIST_CLEAR ); - CurrEdges.num_edges = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - /* atoms -NH- from which H(+) were removed by the Normalization in orig. InChI */ - iat = pc2i->c2at[i].atomNumber; - if ( (pc2i->c2at[i].nValElectr == 6 || - pc2i->c2at[i].nValElectr == 5 && pc2i->c2at[i].nPeriodNum == 1) && - !pc2i->c2at[i].endptInChI && - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - if ( /* orig. InChI info: -NH- */ - pc2i->c2at[i].nFixHInChI == 1 && pc2i->c2at[i].nMobHInChI == 0 && - /* reversed structure info: */ - pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 1 && /* was not removed */ - /*pc2i->c2at[i].nAtChargeRevrs == 0 &&*/ at2[iat].num_H && /* at2 is Fixed-H */ - at2[iat].valence == at2[iat].chem_bonds_valence ) { - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - } - for ( i = 0; i < pStruct->num_atoms; i ++ ) { - /* find adjacent charged atoms */ - iat = nCanon2AtnoRevrs[i]; - if ( pStruct->endpoint[i] || at2[iat].charge != 1 || at2[iat].radical || pVA[iat].cMetal ) { - continue; - } - if ( 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && !pBNS->edge[e].flow && pVA[iat].cNumValenceElectrons >= 5 ) { - /* positively charged atom */ - for ( j = 0; j < at2[iat].valence; j ++ ) { - if ( at2[k=(int)at2[iat].neighbor[j]].charge == 1 && !pVA[k].cMetal && - 0 <= (e2=pVA[k].nCPlusGroupEdge-1) && !pBNS->edge[e2].forbidden && !pBNS->edge[e2].flow) { - if ( 0 > FindInEdgeList( &CurrEdges, e ) && - 0 > FindInEdgeList( &CurChargeEdges, e ) && - ( ret = AddToEdgeList( &CurChargeEdges, e, INC_ADD_EDGE ) ) ) { - goto exit_case_06c; - } - if ( 0 > FindInEdgeList( &CurrEdges, e2 ) && - 0 > FindInEdgeList( &CurChargeEdges, e2 ) && - ( ret = AddToEdgeList( &CurChargeEdges, e2, INC_ADD_EDGE ) ) ) { - goto exit_case_06c; - } - } - } - } - } - if ( num_try = inchi_min( CurrEdges.num_edges, CurChargeEdges.num_edges ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurChargeEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < CurrEdges.num_edges && cur_success < num_try; i ++ ) { - e = CurrEdges.pnEdges[i]; - pe = pBNS->edge + e; /* (+)charge edge of -NH- or -OH */ - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->flow -= delta; /* add (+) to -NHm */ - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { - /* Removed (+)charge from -NH- => nDeltaCharge == -1 */ - /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 06c */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } -exit_case_06c: - CurrEdges.num_edges = 0; /* clear current edge list */ - AllocEdgeList( &CurChargeEdges, EDGE_LIST_FREE ); - if ( ret < 0 ) { - goto exit_function; - } - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pc2i->len_c2at >= 2 ) { - /*------------------------------------------------------------*/ - /* case 06d: restored: XH(+)=-AB-NH orig.: XH-=AB=NH(+) */ - /* FixH: 1 1 0 0 1 1 */ - /* MobH: 0 taut 1 1 taut 0 */ - /* */ - /* */ - /* N = N, O, S, Se; atoms N are not tautomeric in orig InChI */ - /* X = N, O, S, Se, Te, F, Cl, Br, I; atom X is non-taut */ - /* Solution: move (+) from X to NH */ - /*------------------------------------------------------------*/ - int iat; - /* - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - inp_ATOM *atfMobile_H_Revrs = pStruct->pOne_norm_data[TAUT_YES] && - pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds? - pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds : - pStruct->pOne_norm_data[TAUT_NON]->at; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - */ - EDGE_LIST CurChargeEdges; - cur_success = 0; - AllocEdgeList( &CurChargeEdges, EDGE_LIST_CLEAR ); - CurrEdges.num_edges = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - /* XH(+) */ - if ( /* reconstructed: non-taut and (+) */ - (pc2i->c2at[i].nMobHRevrs+1 == pc2i->c2at[i].nFixHRevrs && - pc2i->c2at[i].nFixHRevrs > 0 && !pc2i->c2at[i].endptRevrs && - pc2i->c2at[i].nAtChargeRevrs == 1 && - /* original InChI: non-taut & has H or an endpoint, has Fixed H */ - (!pc2i->c2at[i].nFixHInChI && pc2i->c2at[i].nMobHInChI == pc2i->c2at[i].nFixHRevrs || - pc2i->c2at[i].nFixHInChI == pc2i->c2at[i].nFixHRevrs && pc2i->c2at[i].endptInChI )) && - 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && !pBNS->edge[e].flow) { - - if (ret = AddToEdgeList( &CurChargeEdges, e, INC_ADD_EDGE )) { - goto exit_case_06d; - } - } else - /* -NH- */ - if ( /* original InChI: has H and is not an endpoint */ - (pc2i->c2at[i].nMobHInChI+1 == pc2i->c2at[i].nFixHInChI && - pc2i->c2at[i].nFixHInChI > 0 && !pc2i->c2at[i].endptInChI && - pc2i->c2at[i].nAtChargeRevrs == 0 && - /* reconstructed InChI: non-taut & has H or an endpoint, has Fixed H */ - (!pc2i->c2at[i].nFixHRevrs && pc2i->c2at[i].nMobHRevrs == pc2i->c2at[i].nFixHInChI || - pc2i->c2at[i].nFixHRevrs == pc2i->c2at[i].nFixHInChI && pc2i->c2at[i].endptRevrs )) && - 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && - pBNS->edge[e].flow) { - - if (ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE )) { - goto exit_case_06d; - } - } - } - if ( num_try = inchi_min( CurrEdges.num_edges, CurChargeEdges.num_edges ) ) { - /* detected; attempt to fix */ - int bSFlowerEdgesMayBeForbidden = (SFlowerEdges.num_edges > 0); - int bSFlowerEdgesIsForbidden; - for ( bSFlowerEdgesIsForbidden = bSFlowerEdgesMayBeForbidden; - 0 <= bSFlowerEdgesIsForbidden; bSFlowerEdgesIsForbidden -- ) { - if ( bSFlowerEdgesIsForbidden ) { - /* on the 1st pass disallow -S(+)= => =S=, allow only -S(+)= => -S- */ - SetForbiddenEdgeMask( pBNS, &SFlowerEdges, forbidden_edge_mask ); - } - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurChargeEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < CurrEdges.num_edges && cur_success < num_try; i ++ ) { - e = CurrEdges.pnEdges[i]; - pe = pBNS->edge + e; /* (+)charge edge of -NH- or -OH */ - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->flow -= delta; /* add (+) to -NHm */ - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { - /* Removed (+)charge from -NH- => nDeltaCharge == -1 */ - /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 06d */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &SFlowerEdges, forbidden_edge_mask ); - } - - } -exit_case_06d: - CurrEdges.num_edges = 0; /* clear current edge list */ - AllocEdgeList( &CurChargeEdges, EDGE_LIST_FREE ); - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - - if ( pc2i->len_c2at >= 2 ) { - /*--------------------------------------------------------*/ - /* case 06: restored: NHn(+)=AB-NHm orig.: NHn-AB=NHm(+) */ - /* FixH: 1 0 0 1 */ - /* MobH: n-1 m n m-1 */ - /* N = N, O, S, Se; atoms N are not tautomeric */ - /* Solution: move (+) from NHn(+) to NHn */ - /*--------------------------------------------------------*/ - int num_DB_NHn_Plus = 0, num_SB_NHm_Neutr = 0, iat; - short iat_DB_NHn_Plus[MAX_DIFF_FIXH], iat_SB_NHm_Neutr[MAX_DIFF_FIXH]; - cur_success = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( (pc2i->c2at[i].nValElectr == 6 || - pc2i->c2at[i].nValElectr == 5 && pc2i->c2at[i].nPeriodNum == 1) && - !pc2i->c2at[i].endptInChI && - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - if ( /* orig. InChI info: NHm */ - num_SB_NHm_Neutr < MAX_DIFF_FIXH && - pc2i->c2at[i].nFixHInChI == 1 && /*pc2i->c2at[i].nMobHInChI == 0 &&*/ - /* reversed structure info: */ - pc2i->c2at[i].nFixHRevrs == 0 && /*pc2i->c2at[i].nMobHRevrs == 1 &&*/ - pc2i->c2at[i].nAtChargeRevrs == 0 && at2[iat].num_H && /* at2 is Fixed-H */ - at2[iat].valence == at2[iat].chem_bonds_valence ) { - iat_SB_NHm_Neutr[num_SB_NHm_Neutr ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else - if ( /* orig. InChI info: */ - num_DB_NHn_Plus < MAX_DIFF_FIXH && - pc2i->c2at[i].nFixHInChI == 0 && /*pc2i->c2at[i].nMobHInChI &&*/ - /* reversed structure info: */ - pc2i->c2at[i].nFixHRevrs == 1 && /*pc2i->c2at[i].nMobHRevrs == 0 &&*/ - pc2i->c2at[i].nAtChargeRevrs == 1 && at2[iat].num_H && - at2[iat].valence < at2[iat].chem_bonds_valence ) { - iat_DB_NHn_Plus[num_DB_NHn_Plus ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - } - if ( num_try = inchi_min( num_SB_NHm_Neutr, num_DB_NHn_Plus ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_SB_NHm_Neutr && cur_success < num_try; i ++ ) { - iat = iat_SB_NHm_Neutr[i]; - pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; /* add (+) to -NHm */ - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { - /* Removed (+)charge from -NHn => nDeltaCharge == -1 */ - /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 06 */ - } - } else { - pe->forbidden &= forbidden_edge_mask_inv; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( (pc2i->nNumTgInChI > pc2i->nNumTgRevrs && pc2i->nNumTgRevrs == 1 || - pc2i->nNumEndpInChI < pc2i->nNumEndpRevrs ) && - pStruct->nNumRemovedProtonsMobHInChI == pStruct->One_ti.tni.nNumRemovedProtons && - pStruct->fixed_H && pStruct->endpoint && pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds ) { - /*----------------------------------------------------------*/ - /* case 06a: restored: N'(+)=-AB-NH orig.: N'-=AB=NH(+) */ - /* FixH: 0 1 0 1 */ - /* MobH: 0 0 0 0 */ - /* single t-group multiple t-groups */ - /* N = N, O, S, Se; atoms N are not tautomeric */ - /* N' = N atom N' is not tautomeric */ - /* Solution: move (+) from N' to NH */ - /*----------------------------------------------------------*/ - int iat; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - /* - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - */ - inp_ATOM *atfMobile_H_Revrs = pStruct->pOne_norm_data[TAUT_YES] && - pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds? - pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds : NULL; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - EDGE_LIST CurChargeEdges; - cur_success = 0; - AllocEdgeList( &CurChargeEdges, EDGE_LIST_CLEAR ); - CurrEdges.num_edges = 0; - for ( i = 0; i < pStruct->num_atoms; i ++ ) { - iat = nCanon2AtnoRevrs[i]; - if ( pStruct->endpoint[i] ) { - continue; - } - /* -NH-, -OH */ - if ( pStruct->fixed_H[i] && !nMobHInChI[i] && - at2[iat].charge == 0 && at2[iat].radical == 0 && - 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && - (ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ))) { - goto exit_case_06a; - } else - /* >N(+)= */ - if ( at2[iat].charge == 1 && !at2[iat].num_H && - pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && - atfMobile_H_Revrs && atfMobile_H_Revrs[iat].charge == 0 && - 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && !pBNS->edge[e].flow && - (ret = AddToEdgeList( &CurChargeEdges, e, INC_ADD_EDGE ))) { - goto exit_case_06a; - } - } - if ( num_try = inchi_min( CurrEdges.num_edges, CurChargeEdges.num_edges ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurChargeEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < CurrEdges.num_edges && cur_success < num_try; i ++ ) { - e = CurrEdges.pnEdges[i]; - pe = pBNS->edge + e; /* (+)charge edge of -NH- or -OH */ - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->flow -= delta; /* add (+) to -NHm */ - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { - /* Removed (+)charge from -NH- => nDeltaCharge == -1 */ - /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 06a */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } -exit_case_06a: - CurrEdges.num_edges = 0; /* clear current edge list */ - AllocEdgeList( &CurChargeEdges, EDGE_LIST_FREE ); - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - if ( (pc2i->nNumTgInChI > pc2i->nNumTgRevrs && pc2i->nNumTgRevrs == 1 || - pc2i->nNumEndpInChI < pc2i->nNumEndpRevrs ) && - (pStruct->nNumRemovedProtonsMobHInChI == pStruct->One_ti.tni.nNumRemovedProtons || - pStruct->nNumRemovedProtonsMobHInChI > pStruct->One_ti.tni.nNumRemovedProtons ) && - pStruct->fixed_H && pStruct->endpoint && pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds ) { - /*----------------------------------------------------------*/ - /* case 06b: restored: X(+)=-AB-NH orig.: X-=AB=NH(+) */ - /* FixH: 0 1 1 0 1 */ - /* MobH: 0 0 t 0 0 */ - /* single t-group multiple t-groups */ - /* or no t-groupd */ - /* N = N, O, S, Se; atoms N are not tautomeric */ - /* X = O, S, Se, Te, F, Cl, Br, I; atom X is not tautomeric*/ - /* Solution: move (+) from X to NH */ - /*----------------------------------------------------------*/ - int iat; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - /* - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - */ - inp_ATOM *atfMobile_H_Revrs = pStruct->pOne_norm_data[TAUT_YES] && - pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds? - pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds : NULL; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - EDGE_LIST CurChargeEdges; - cur_success = 0; - AllocEdgeList( &CurChargeEdges, EDGE_LIST_CLEAR ); - CurrEdges.num_edges = 0; - for ( i = 0; i < pStruct->num_atoms; i ++ ) { - iat = nCanon2AtnoRevrs[i]; - if ( pStruct->endpoint[i] ) { - continue; - } - /* -NH-, -OH */ - if ( pStruct->fixed_H[i] && !nMobHInChI[i] && - at2[iat].charge == 0 && at2[iat].radical == 0 && - 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && - (ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ))) { - goto exit_case_06b; - } else - /* X(+)= */ - if ( at2[iat].charge == 1 && !at2[iat].num_H && - (pVA[iat].cNumValenceElectrons == 6 || pVA[iat].cPeriodicRowNumber == 7) && - atfMobile_H_Revrs && atfMobile_H_Revrs[iat].charge == 1 && - 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && !pBNS->edge[e].flow && - (ret = AddToEdgeList( &CurChargeEdges, e, INC_ADD_EDGE ))) { - goto exit_case_06b; - } - } - if ( num_try = inchi_min( CurrEdges.num_edges, CurChargeEdges.num_edges ) ) { - /* detected; attempt to fix */ - int bSFlowerEdgesMayBeForbidden = (SFlowerEdges.num_edges > 0); - int bSFlowerEdgesIsForbidden; - for ( bSFlowerEdgesIsForbidden = bSFlowerEdgesMayBeForbidden; - 0 <= bSFlowerEdgesIsForbidden; bSFlowerEdgesIsForbidden -- ) { - if ( bSFlowerEdgesIsForbidden ) { - /* on the 1st pass disallow -S(+)= => =S=, allow only -S(+)= => -S- */ - SetForbiddenEdgeMask( pBNS, &SFlowerEdges, forbidden_edge_mask ); - } - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurChargeEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < CurrEdges.num_edges && cur_success < num_try; i ++ ) { - e = CurrEdges.pnEdges[i]; - pe = pBNS->edge + e; /* (+)charge edge of -NH- or -OH */ - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->flow -= delta; /* add (+) to -NHm */ - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { - /* Removed (+)charge from -NH- => nDeltaCharge == -1 */ - /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 06b */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &SFlowerEdges, forbidden_edge_mask ); - } - - } -exit_case_06b: - CurrEdges.num_edges = 0; /* clear current edge list */ - AllocEdgeList( &CurChargeEdges, EDGE_LIST_FREE ); - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - - - if ( pc2i->nNumTgInChI > 1 && - (pStruct->nNumRemovedProtonsMobHInChI > 0 || pStruct->ti.tni.nNumRemovedProtons > 0 ) && - pStruct->fixed_H && pStruct->endpoint && - pStruct->pOne_norm_data[TAUT_YES] && pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds ) { - /*----------------------------------------------------------*/ - /* case 06e:restored: XHn(+)=-AB-YHm orig.: XHn-=AB=YHm(+) */ - /* FixH: 1 0 1 1 */ - /* MobH: 0 1 t t */ - /* non-taut atoms multiple t-groups */ - /* */ - /* 1. orig. t-group has more H on its endpoints counted */ - /* in atf and has no (+) on endpoint that has H */ - /* 2. orig. t-group has less H on its endpoints counted */ - /* in atf and has (+) on endpoint that has H */ - /* in reconstructed struct and less H in atf */ - /* Solution: move (+) from (2) to atom in (1) that has H */ - /* */ - /* tg1 reconstr: XHn and more H than in orig t-group */ - /* atf: XHn */ - /* tg2 reconstr: XHm(+) and less H than in */ - /* atf: XH(m-1) orig in t-group */ - /* */ - /* N = N, O, S, Se; atoms N are not tautomeric */ - /* X = O, S, Se, Te, F, Cl, Br, I; atom X is not tautomeric*/ - /* Solution: move (+) from X to NH */ - /*----------------------------------------------------------*/ - - int iat, nNumWrongTg, jjoffs, jj, nNum2RemovePlus, nNum2AddPlus, nNum2MovePlus; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - /* - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - */ - inp_ATOM *atfMobile_H_Revrs = pStruct->pOne_norm_data[TAUT_YES] && - pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds? - pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds : - pStruct->pOne_norm_data[TAUT_YES] && - pStruct->pOne_norm_data[TAUT_YES]->at? - pStruct->pOne_norm_data[TAUT_YES]->at : NULL; - /* - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - */ - EDGE_LIST CurChargeEdges /* source of (+)*/, EndpList; - TgDiffHChgFH tdhc[MAX_DIFF_FIXH]; - BNS_VERTEX *pv1n, *pv2n; - BNS_EDGE *pe1n, *pe2n; - Vertex v1n, v2n; - - cur_success = 0; - AllocEdgeList( &CurChargeEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &EndpList, EDGE_LIST_CLEAR ); - CurrEdges.num_edges = 0; /* receptors of (+) */ - if ( !atfMobile_H_Revrs ) { - goto exit_case_06e; - } - nNumWrongTg = FillTgDiffHChgFH( tdhc, MAX_DIFF_FIXH, at2, atfMobile_H_Revrs, - nCanon2AtnoRevrs, pVA, &pStruct->ti, &EndpList ); - if ( nNumWrongTg < 1 ) { - goto exit_case_06e; /* for now only transfer (+) from one Mobile-H group to another */ - } - nNum2RemovePlus = nNum2AddPlus = nNum2MovePlus = 0; - for ( i = 0; i < nNumWrongTg; i ++ ) { - /* detect t-group that has extra (+) on H */ - if ( tdhc[i].nNumHInchi > tdhc[i].nNumHNorml && - tdhc[i].nNumPRevrs > tdhc[i].nNumPNorml && tdhc[i].n[fNumRPosChgH] ) { - /* count how many (+) to remove */ - /* store XH(+) atom numbers */ - int nNumNeeded = inchi_min( tdhc[i].nNumHInchi-tdhc[i].nNumHNorml, tdhc[i].n[fNumRPosChgH]); - nNum2RemovePlus += nNumNeeded; - jjoffs = tdhc[i].i[ fNumRPosChgH ]; - for ( jj = 0; jj < tdhc[i].n[fNumRPosChgH]; jj ++ ) { - iat = EndpList.pnEdges[ jjoffs + jj ]; - e = pVA[iat].nCPlusGroupEdge-1; - if ( ret = AddToEdgeList( &CurChargeEdges, e, INC_ADD_EDGE ) ) { - goto exit_case_06e; - } - } - } else - /* detect t-group that needs (+) on XH to reduce number of H */ - if ( tdhc[i].nNumHInchi < tdhc[i].nNumHNorml && tdhc[i].n[fNumRNeutrlH] ) { - /* store XH atom numbers */ - int nNumNeeded = inchi_min( tdhc[i].nNumHNorml-tdhc[i].nNumHInchi, tdhc[i].n[fNumRNeutrlH]); - nNum2AddPlus += nNumNeeded; - jjoffs = tdhc[i].i[ fNumRNeutrlH ]; - for ( jj = 0; jj < tdhc[i].n[fNumRNeutrlH]; jj ++ ) { - iat = EndpList.pnEdges[ jjoffs + jj ]; - e = pVA[iat].nCPlusGroupEdge-1; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_case_06e; - } - } - } - } - nNum2MovePlus = inchi_min( nNum2RemovePlus, nNum2AddPlus ); - if ( CurrEdges.num_edges > 0 && CurChargeEdges.num_edges > 0 ) { - for ( i = 0; 0 < nNum2MovePlus && i < nNumWrongTg; i ++ ) { - /* detect t-group that has extra (+) on H */ - if ( tdhc[i].nNumHInchi > tdhc[i].nNumHNorml && - tdhc[i].nNumPRevrs > tdhc[i].nNumPNorml && tdhc[i].n[fNumRPosChgH] ) { - int nNum2Remove = tdhc[i].nNumHInchi - tdhc[i].nNumHNorml; - if ( nNum2Remove < tdhc[i].n[fNumRPosChgH] ) { - nNum2Remove = tdhc[i].n[fNumRPosChgH]; - } - /* store XH(+) atom numbers */ - jjoffs = tdhc[i].i[ fNumRPosChgH ]; - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - for ( jj = 0; 0 < nNum2MovePlus && 0 < nNum2Remove && jj < tdhc[i].n[fNumRPosChgH]; jj ++ ) { - iat = EndpList.pnEdges[ jjoffs + jj ]; - e = pVA[iat].nCPlusGroupEdge-1; - pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; - if ( pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - for ( j = pv1->num_adj_edges-1; 0 <= j; j -- ) { - pe1n = pBNS->edge + pv1->iedge[j]; - if ( pe1n->flow && !pe1n->forbidden ) { - pv1n = pBNS->vert + (v1n = pe1n->neighbor12 ^ v1); - break; - } - } - if ( j < 0 ) - continue; /* not found */ - - for ( j = pv2->num_adj_edges-2; 0 <= j; j -- ) { - pe2n = pBNS->edge + pv2->iedge[j]; - if ( pe2n->flow && !pe2n->forbidden ) { - pv2n = pBNS->vert + (v2n = pe2n->neighbor12 ^ v2); - break; - } - } - if ( j < 0 ) - continue; /* not found */ - delta = 1; - pe->flow += delta; - pe1n->flow -= delta; - pe2n->flow -= delta; - pv1n->st_edge.flow -= delta; - pv2n->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1n && vPathStart == v2n || - vPathEnd == v2n && vPathStart == v1n) && - (nDeltaCharge == 0 || nDeltaCharge == 1) ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - nNum2Remove --; - nNum2MovePlus --; - cur_success ++; /* 06e */ - } - } else { - pe->flow -= delta; - pe1n->flow += delta; - pe2n->flow += delta; - pv1n->st_edge.flow += delta; - pv2n->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_case_06e; - } - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - } - } -exit_case_06e: - CurrEdges.num_edges = 0; /* clear current edge list */ - AllocEdgeList( &CurChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &EndpList, EDGE_LIST_FREE ); - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - - - if ( pc2i->len_c2at >= 1 ) { - /*--------------------------------------------------------------*/ - /* case 07: restored: O(-)-AB=O original: O=AB-O(-) */ - /* FixH: 0 0 0 -1 */ - /* MobH: 0 0 0 1 */ - /* taut (non-taut) (taut) non-taut */ - /* taut (taut) (non-taut) non-taut */ - /* O = O, S, Se, Te */ - /* Solution: move (-) from O(-)-AB to AB=O */ - /*--------------------------------------------------------------*/ - int num_SB_O_Minus = 0, num_DB_O_Neutr = 0, iat; - short iat_SB_O_Minus[MAX_DIFF_FIXH], iat_DB_O_Neutr[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - cur_success = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( /* orig. InChI info: -O(-), non-taut */ - num_DB_O_Neutr < MAX_DIFF_FIXH && - pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ && - !pc2i->c2at[i].endptInChI && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pc2i->c2at[i].nFixHInChI == -1 && pc2i->c2at[i].nMobHInChI == 1 && - /* reversed structure info: */ - pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && - pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && - at2[iat].valence < at2[iat].chem_bonds_valence ) { - iat_DB_O_Neutr[num_DB_O_Neutr ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( /* in restored atom: charge=-1, no H, has single bond, O, S, Se, Te */ - num_SB_O_Minus < MAX_DIFF_FIXH && - at2[iat].charge == -1 && !at2[iat].num_H && - at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - pVA[iat].cNumValenceElectrons == 6 && - at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint && - /* in orig.InChI: not an endpoint, has no H */ - /*pStruct->endpoint[i] && -- modificatuion#1 */ - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i] ) && - /* has (-) edge */ - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - iat_SB_O_Minus[num_SB_O_Minus ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( num_try = inchi_min( num_SB_O_Minus, num_DB_O_Neutr ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_SB_O_Minus && cur_success < num_try; i ++ ) { - iat = iat_SB_O_Minus[i]; - pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Moved (-) charge to AB=O => nDeltaCharge == 1 */ - /* Flow change on pe (-)charge edge (O(-)-AB) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 07 */ - } - } else { - pe->forbidden &= forbidden_edge_mask_inv; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pc2i->len_c2at >= 1 ) { - /*--------------------------------------------------------------*/ - /* case 07a: restored: O(-)-N(V)B=O original: O=N(V)B-O(-) */ - /* FixH: 0 0 0 -1 */ - /* MobH: 0 0 0 1 */ - /* non-taut (non-taut) non-taut non-taut */ - /* non-taut (taut) non-taut non-taut */ - /* O = O, S, Se, Te */ - /* Solution: move (-) from O(-)-AB to AB=O */ - /*--------------------------------------------------------------*/ - int num_SB_O_Minus = 0, num_DB_O_Neutr = 0, iat, iN; - short iat_SB_O_Minus[MAX_DIFF_FIXH], iat_DB_O_Neutr[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - cur_success = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( /* orig. InChI info: -O(-), non-taut */ - num_DB_O_Neutr < MAX_DIFF_FIXH && - pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ && - !pc2i->c2at[i].endptInChI && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pc2i->c2at[i].nFixHInChI == -1 && pc2i->c2at[i].nMobHInChI == 1 && - /* reversed structure info: */ - pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && - pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && - at2[iat].valence < at2[iat].chem_bonds_valence ) { - iat_DB_O_Neutr[num_DB_O_Neutr ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( /* in restored atom: charge=-1, no H, has single bond, O, S, Se, Te */ - num_SB_O_Minus < MAX_DIFF_FIXH && - at2[iat].charge == -1 && !at2[iat].num_H && - at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - pVA[iat].cNumValenceElectrons == 6 && - /*at_Mobile_H_Revrs && !at_Mobile_H_Revrs[iat].endpoint &&*/ - /* in orig.InChI: not an endpoint, has no H */ - !pStruct->endpoint[i] && - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i] ) && - /* has N(V) neighbor */ - 1 == at2[iat].valence && at2[iN=at2[iat].neighbor[0]].chem_bonds_valence==5 && - !at2[iN].charge && pVA[iN].cNumValenceElectrons == 5 && - /* has (-) edge */ - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - iat_SB_O_Minus[num_SB_O_Minus ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( num_try = inchi_min( num_SB_O_Minus, num_DB_O_Neutr ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_SB_O_Minus && cur_success < num_try; i ++ ) { - iat = iat_SB_O_Minus[i]; - pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Moved (-) charge to AB=O => nDeltaCharge == 1 */ - /* Flow change on pe (-)charge edge (O(-)-AB) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 07 */ - } - } else { - pe->forbidden &= forbidden_edge_mask_inv; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - if ( /*(pc2i->len_c2at >= 1 || pc2i->nNumRemHRevrs) &&*/ pc2i->nNumTgInChI == 1 && /* ADP in InChI */ - (pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1) ) { - /*----------------------------------------------------------------*/ - /* case 08: restored: O(-)-AB=N- OH- orig. O=AB-N(-)- OH- */ - /* FixH: 1 0 0 0 0 1 */ - /* MobH: 0 0 1 0 0 0 */ - /* may be taut or not non-taut taut taut taut */ - /* ADP: one t-group or more endpoints */ - /* O(-) = S, Se, Te; N = N; */ - /* Solution: move (-) from O(-) to =N-; avoid stereogenic DB on N */ - /*----------------------------------------------------------------*/ - int num_DB_N_Neutr = 0, num_SB_O_Minus = 0, iat; - short iat_DB_N_Neutr[MAX_DIFF_FIXH], iat_SB_O_Minus[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - cur_success = 0; - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( /* in restored atom: charge=-1, has no H, has single bond, O, S, Se, Te */ - num_SB_O_Minus < MAX_DIFF_FIXH && - at2[iat].charge == -1 && !at2[iat].num_H && - at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - pVA[iat].cNumValenceElectrons == 6 && - /* in orig.InChI: an endpoint, may have fixed-H */ - pStruct->endpoint[i] && - /*!(pStruct->fixed_H && pStruct->fixed_H[i]) &&*/ - !(nMobHInChI && nMobHInChI[i] ) && - /* has (-) edge */ - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - - iat_SB_O_Minus[num_SB_O_Minus ++] = iat; - - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else - if ( /* in restored atom: charge=0, has no H, has double non-stereogenic bond, N */ - num_DB_N_Neutr < MAX_DIFF_FIXH && - at2[iat].charge == 0 && !at2[iat].num_H && !at2[iat].sb_parity[0] && - at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && - /* in orig.InChI: an endpoint, has no fixed-H */ - pStruct->endpoint[i] && - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i] ) && - /* has (-) edge */ - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && - 0 == pBNS->edge[e].forbidden ) { - - iat_DB_N_Neutr[num_DB_N_Neutr ++] = iat; - - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( num_try = inchi_min( num_DB_N_Neutr, num_SB_O_Minus ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - /* allow stereobonds in rings change */ - if ( forbidden_stereo_edge_mask ) - RemoveForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask ); - - delta = 1; - for ( i = 0; i < num_SB_O_Minus && cur_success < num_try; i ++ ) { - iat = iat_SB_O_Minus[i]; - pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Moved (-) charge to =N- => nDeltaCharge == 1 */ - /* Flow change on pe (-)charge edge (atom (-)O-) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 08 */ - } - } else { - pe->forbidden &= forbidden_edge_mask_inv; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - if ( forbidden_stereo_edge_mask ) - SetForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pc2i->len_c2at >= 2 ) { - /*--------------------------------------------------------*/ - /* case 09: restored: NH2(+)=C--NH2 orig.: NH2-C(+)-NH2 */ - /* FixH: 2 | 2 0 | 0 */ - /* MobH: 0 0 2 2 */ - /* N = N, taut taut non-taut non-taut*/ - /* Solution: move (+) from NH2(+) to C */ - /*--------------------------------------------------------*/ - int iat; - cur_success = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( (pc2i->c2at[i].nValElectr == 5 && pc2i->c2at[i].nPeriodNum == 1) && - /* orig. InChI info: */ - !pc2i->c2at[i].endptInChI && - pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI && - /* reversed structure info: */ - pc2i->c2at[i].endptRevrs && - pc2i->c2at[i].nFixHRevrs && !pc2i->c2at[i].nMobHRevrs && - pc2i->c2at[i].nAtChargeRevrs == 1 && - at2[iat].valence + 1 == at2[iat].chem_bonds_valence && - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - EdgeIndex eNC = NO_VERTEX, eCPlusC; - int iNH2, iatC, iatNH2, icNH2; - /* found NH2(+)=; locate =C< and find whether it has -NH2 neighbor */ - for ( j = 0; j < at2[iat].valence; j ++ ) { - if ( at2[iat].bond_type[j] == BOND_TYPE_DOUBLE ) - break; - } - if ( j == at2[iat].valence ) - continue; - eNC = pBNS->vert[iat].iedge[j]; /* edge NH2(+)=C */ - iatC = at2[iat].neighbor[j]; - if ( pVA[iatC].cNumValenceElectrons != 4 || pVA[iatC].cMetal || at2[iatC].charge || - at2[iatC].valence != 3 || at2[iatC].valence+1 != at2[iatC].chem_bonds_valence || - (eCPlusC=pVA[iatC].nCPlusGroupEdge-1) < 0 || pBNS->edge[eCPlusC].forbidden) - continue; - for ( j = 0; j < at2[iatC].valence; j ++ ) { - iatNH2 = at2[iatC].neighbor[j]; - if ( iatNH2 == iat || pVA[iatNH2].cNumValenceElectrons != 5 || - pVA[iatNH2].cPeriodicRowNumber != 1 || !at2[iatNH2].num_H || at2[iatNH2].charge) - continue; - icNH2 = pStruct->nAtno2Canon[0][iatNH2]; - for ( iNH2 = 0; iNH2 < pc2i->len_c2at; iNH2 ++ ) { - if ( iatNH2 == pc2i->c2at[iNH2].atomNumber ) - break; - } - if ( iNH2 == pc2i->len_c2at ) - continue; - - if ( (pc2i->c2at[iNH2].nValElectr == 5 && pc2i->c2at[iNH2].nPeriodNum == 1) && - /* orig. InChI info: */ - !pc2i->c2at[iNH2].endptInChI && - pc2i->c2at[iNH2].nFixHInChI == 0 && pc2i->c2at[iNH2].nMobHInChI && - /* reversed structure info: */ - pc2i->c2at[iNH2].endptRevrs && - pc2i->c2at[iNH2].nFixHRevrs && !pc2i->c2at[iNH2].nMobHRevrs && - pc2i->c2at[iNH2].nAtChargeRevrs == 0 && - at2[iatNH2].valence == at2[iatNH2].chem_bonds_valence ) { - /* we have found NH2(+)=, =C<, and bond between them */ - - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &CurrEdges, eCPlusC, INC_ADD_EDGE ) ) { - goto exit_function; - } - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - - pe = pBNS->edge + eNC; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; /* add (+) to -NHm */ - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { - /* Removed (+)charge from -NHn => nDeltaCharge == -1 */ - /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 09 */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - pe->forbidden &= forbidden_edge_mask_inv; - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - CurrEdges.num_edges = 0; /* clear current edge list */ - break; - } - } - } - } - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - - if ( pc2i->len_c2at >= 2 ) { - /*--------------------------------------------------------*/ - /* case 10: restored: NH2-X(+)-NH- orig.: NH2(+)=X-NH- */ - /* FixH: 0 0 2 1 */ - /* MobH: 2 1 0 0 */ - /* N = N,O,S,Se,Te non-taut non-taut taut taut */ - /* Solution: move (+) from X(+) to NH2 or NH */ - /*--------------------------------------------------------*/ - int iat; - cur_success = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - if ( pc2i->c2at[i].nValue ) - continue; - iat = pc2i->c2at[i].atomNumber; - if ( (pc2i->c2at[i].nValElectr == 6 || - pc2i->c2at[i].nValElectr == 5 && pc2i->c2at[i].nPeriodNum == 1) && - /* orig. InChI info: */ - pc2i->c2at[i].endptInChI && - pc2i->c2at[i].nFixHInChI && !pc2i->c2at[i].nMobHInChI && - /* reversed structure info: */ - !pc2i->c2at[i].endptRevrs && - !pc2i->c2at[i].nFixHRevrs && pc2i->c2at[i].nMobHRevrs && - pc2i->c2at[i].nAtChargeRevrs == 0 && - at2[iat].valence == at2[iat].chem_bonds_valence && - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - - EdgeIndex eCPlusC, eCPlusNH2, bContinue=1; - int iNH2, iatC, iatNH2, icNH2, j1, j2; - BNS_EDGE *pe_iat, *pe_iNH2; - /* found NH2- locate -X(+) and find whether it has another -NH2 neighbor */ - for ( j1 = 0; j1 < at2[iat].valence && bContinue; j1 ++ ) { - if ( at2[iat].bond_type[j1] == BOND_TYPE_SINGLE && - at2[iatC = at2[iat].neighbor[j1]].charge == 1 && - (4 <= pVA[iatC].cNumValenceElectrons && pVA[iatC].cNumValenceElectrons <= 6) && - at2[iatC].valence == at2[iatC].chem_bonds_valence && - (eCPlusC=pVA[iatC].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[eCPlusC].forbidden) { - /* found a candidate for X; find another NH2 */ - for ( j2 = 0; j2 < at2[iatC].valence && bContinue; j2 ++ ) { - if ( at2[iatC].bond_type[j2] == BOND_TYPE_SINGLE && - iat != (iatNH2 = at2[iatC].neighbor[j2]) && - at2[iatNH2].charge == 0 && at2[iatNH2].num_H && - (pVA[iatNH2].cNumValenceElectrons==5 || pVA[iatNH2].cNumValenceElectrons==6) && - at2[iatNH2].valence == at2[iatNH2].chem_bonds_valence && - (eCPlusNH2=pVA[iatNH2].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[eCPlusNH2].forbidden) { - for ( iNH2 = 0; iNH2 < pc2i->len_c2at; iNH2 ++ ) { - if ( iatNH2 != pc2i->c2at[iNH2].atomNumber || pc2i->c2at[iNH2].nValue ) - continue; - /* check the second -NH */ - icNH2 = pStruct->nAtno2Canon[0][iatNH2]; /* canon number -1 */ - if ( /* orig. InChI info: */ - pc2i->c2at[iNH2].endptInChI && - pc2i->c2at[iNH2].nFixHInChI && !pc2i->c2at[iNH2].nMobHInChI && - /* reversed structure info: */ - !pc2i->c2at[iNH2].endptRevrs && - !pc2i->c2at[iNH2].nFixHRevrs && pc2i->c2at[iNH2].nMobHRevrs && - pc2i->c2at[iNH2].nAtChargeRevrs == 0 ) { - /* we have found NH-X(+)-NH; remove charge from X(+) */ - pe_iat = pBNS->edge + pBNS->vert[iat].iedge[j1]; - pe_iNH2 = pBNS->edge + pBNS->vert[iatC].iedge[j2]; - /* pick up one of -NH to move (+) to it */ - if ( !pe_iat->forbidden && pBNS->edge[e].flow ) { - pe = pBNS->edge + e; - } else - if ( !pe_iNH2->forbidden && pBNS->edge[eCPlusNH2].flow ) { - pe = pBNS->edge + eCPlusNH2; - } else { - continue; /* none of the two -X(+)- bonds may be changed */ - } - if ( ret = AddToEdgeList( &CurrEdges, eCPlusC, INC_ADD_EDGE ) ) { - goto exit_function; - } - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - /*pe->forbidden |= forbidden_edge_mask;*/ - pe->flow -= delta; /* add (+) to -NHm */ - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { - /* Removed (+)charge from -NHn => nDeltaCharge == -1 */ - /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 10 */ - bContinue = 0; - pc2i->c2at[i].nValue = 1; /* mark as used */ - pc2i->c2at[iNH2].nValue = 1; /* mark as used */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - - /*pe->forbidden &= forbidden_edge_mask_inv;*/ - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - CurrEdges.num_edges = 0; /* clear current edge list */ - break; - } - } /* iNH2: pc2i->c2at[iNH2] cycle */ - } - } /* j2: iatC neighbors cycle */ - } - } /* j1: iat neighbors cycle */ - } - } /* i: pc2i->c2at[i] cycle */ - if ( cur_success ) { - /* - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - pc2i->c2at[i].nValue = 0; - } - */ - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( /*pc2i->len_c2at >= 1 &&*/ pc2i->nNumTgInChI == 1 && /* ADP in InChI */ - (pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1) ) { - /*--------------------------------------------------------------*/ - /* case 11: restored: NH(+)=AB-N< OH- orig. NH-AB=N(+)< OH- */ - /* FixH: 0 0 0 1 0 1 */ - /* MobH: 1 0 1 0 0 0 */ - /* non-taut. taut taut */ - /* ADP: one t-group or more endpoints */ - /* NH(+)= => N, O, S, Se; -N< => N */ - /* Solution: move (+) from NH(+) to -N< */ - /*--------------------------------------------------------------*/ - int num_SB_Neutr = 0, num_DB_Charged = 0, iat; - short iat_SB_Neutr[MAX_DIFF_FIXH], iat_DB_Charged[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - cur_success = 0; - /* search for NH(+)= */ - /* search for -N< */ - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( /* in restored atom: charge=0, has no H, has no double bond, N only */ - num_DB_Charged < MAX_DIFF_FIXH && - at2[iat].charge == 1 && at2[iat].num_H && - at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - (pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 || - pVA[iat].cNumValenceElectrons == 6 ) && - /* in orig.InChI: an endpoint, has fixed-H */ - /*pStruct->endpoint[i] &&*/ - (pStruct->fixed_H && pStruct->fixed_H[i]) && - /*!(nMobHInChI && nMobHInChI[i] ) &&*/ - /* has (+) edge */ - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && 0 == pBNS->edge[e].forbidden ) { - - iat_DB_Charged[num_DB_Charged ++] = iat; - /* - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - */ - } else - if ( /* in restored atom: charge=0, has no H, has no double bond, N only */ - num_SB_Neutr < MAX_DIFF_FIXH && - at2[iat].charge == 0 && !at2[iat].num_H && - at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - (pVA[iat].cNumValenceElectrons == 5 && - pVA[iat].cPeriodicRowNumber == 1 ) && - /* in orig.InChI: an endpoint, has fixed-H */ - /*pStruct->endpoint[i] &&*/ - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i] ) && - /* has (+) edge */ - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && 0 == pBNS->edge[e].forbidden ) { - - iat_SB_Neutr[num_SB_Neutr ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( num_try = inchi_min( num_SB_Neutr, num_DB_Charged ) ) { - /* detected; attempt to fix */ - BNS_VERTEX *pv1n, *pv2n; - BNS_EDGE *pe1n, *pe2n; - Vertex v1n, v2n; - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_DB_Charged && cur_success < num_try; i ++ ) { - iat = iat_DB_Charged[i]; - pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; - if ( pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - for ( j = pv1->num_adj_edges-1; 0 <= j; j -- ) { - pe1n = pBNS->edge + pv1->iedge[j]; - if ( pe1n->flow && !pe1n->forbidden ) { - pv1n = pBNS->vert + (v1n = pe1n->neighbor12 ^ v1); - break; - } - } - if ( j < 0 ) - continue; /* not found */ - - for ( j = pv2->num_adj_edges-2; 0 <= j; j -- ) { - pe2n = pBNS->edge + pv2->iedge[j]; - if ( pe2n->flow && !pe2n->forbidden ) { - pv2n = pBNS->vert + (v2n = pe2n->neighbor12 ^ v2); - break; - } - } - if ( j < 0 ) - continue; /* not found */ - - pe->flow += delta; - pe1n->flow -= delta; - pe2n->flow -= delta; - pv1n->st_edge.flow -= delta; - pv2n->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1n && vPathStart == v2n || - vPathEnd == v2n && vPathStart == v1n) && - (nDeltaCharge == 0 || nDeltaCharge == 1) ) { - /* before setting flows the structure could be: - [NH+ neigh, v1n]=e1n=[NH+,v1]-pe-[+,v2]=e2n=[another at or its chargeStruct] - or - - [NH+ or ChStr, v1n]=pe1n=[NH+ or ChStr, v1]-pe-[+,v2]=pe2n=[at2 or ChStr, v2n] - ^ ^ ^ - NH+(+)edge | N (+) edge: only - | these are not forbidden - | - hetero (+) vertex - - After setting flows (* mark radicals, =pe= is forbidden): - - *[NH+ or ChStr, v1n]-pe1n-[NH+ or ChStr, v1]=pe=[+,v2]-pe2n-[at2 or ChStr, v2n]* - ^ ^ ^ - NH+(+)edge | N (+) edge: only - | these are not forbidden - | - hetero (+) vertex - - Flow in - pe1n and pe2n will or will not change, depending on the structure. - - Consider what happens if pe2n changes. It may only increment. - If pe2n flow increments then another (+)edge flow dectrements. If - [at2 or ChStr, v2n] is at2 then at2 charge would change from (+) to 0, - and another N charge would change from 0 to (+), giving tot. change of - number of charges (-1)+(+1)=0. However, if [at2 or ChStr, v2n] is - ChargeStruct then at2 will not be on the alt path and only the creation - of another (+) will be detected. - */ - /* Removed charge from O(+) => nDeltaCharge == -1 */ - /* Flow change on pe (+)charge edge (atom NH2) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 11 */ - } - } else { - pe->flow -= delta; - pe1n->flow += delta; - pe2n->flow += delta; - pv1n->st_edge.flow += delta; - pv2n->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pc2i->len_c2at >= 1 && pc2i->nNumTgInChI == 1 && - pc2i->nNumRemHInChI >= -1 && /* 2006-03-03 */ - (pc2i->nNumEndpInChI > pc2i->nNumEndpRevrs || pc2i->nNumTgRevrs > 1) /* ADP in InChI */ ) { - /*--------------------------------------------------------------*/ - /* case 12: restored: O=AB-N< original: (-)O-AB=N(+)< */ - /* FixH: 0 0 0 0 */ - /* MobH: 0 0 0 0 */ - /* non-taut taut */ - /* O = O, S, Se, N; N = N; */ - /* restored atom O is not tautomeric; original atom O is taut. */ - /* original struct has 1 t-group; restored has less endpoints */ - /* and/or possibly >1 t-groups */ - /* Solution: separate charges between O= and -N< */ - /* allow moving charge to N(V) to make it N(IV)(+) */ - /*--------------------------------------------------------------*/ - int bOnly_N_V = 1; - cur_success = 0; - while( 1 ) { - int num_SB_N_Neutr = 0, num_DB_O = 0, iat, num_N_V=0, bN_V; - short iat_SB_N_Neutr[MAX_DIFF_FIXH], iat_DB_O[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - cur_success = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( /* orig. InChI info: -O(-) */ - num_DB_O < MAX_DIFF_FIXH && - (pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ || - pc2i->c2at[i].nValElectr == 5 && - pc2i->c2at[i].nPeriodNum == 1 /* N */ ) && - pc2i->c2at[i].endptInChI && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI == 0 && - /* reversed structure info: */ - !pc2i->c2at[i].endptRevrs && - pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && - pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && - ((pc2i->c2at[i].nValElectr == 6)? - (at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2): - (pc2i->c2at[i].nValElectr == 5)? - (at2[iat].valence == 2 && at2[iat].chem_bonds_valence == 3): - 0)) { - - iat_DB_O[num_DB_O ++] = iat; - /* - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - */ - } - } - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - bN_V = 0; - if ( /* in restored atom N: charge=0, no H, has no double bond, not an endpoint */ - num_SB_N_Neutr < MAX_DIFF_FIXH && - at2[iat].charge == 0 && !at2[iat].num_H && - (at2[iat].valence == at2[iat].chem_bonds_valence || - (bN_V = at2[iat].valence+2 == at2[iat].chem_bonds_valence)) && - !pVA[iat].cMetal && - pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && - !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint) && - /* in orig.InChI: not an endpoint, has no H */ - !pStruct->endpoint[i] && - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i]) && - /* has (+) edge */ - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - - if ( bOnly_N_V && bN_V && - NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e )) && - !pBNS->edge[j].forbidden && !pBNS->edge[j].flow ) { - if ( !num_N_V ) { - /* switch to N(V) only mode */ - CurrEdges.num_edges = 0; - num_SB_N_Neutr = 0; - } - iat_SB_N_Neutr[num_SB_N_Neutr ++] = iat; - num_N_V ++; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &CurrEdges, j, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else - if ( !num_N_V ) { - iat_SB_N_Neutr[num_SB_N_Neutr ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - /* in addition, permit N(V)=>N(IV)(+) change by allowing charge flower edge change flow */ - if ( bN_V && NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e )) && - !pBNS->edge[j].forbidden && !pBNS->edge[j].flow ) { - if ( ret = AddToEdgeList( &CurrEdges, j, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - } - } - if ( num_try = inchi_min( num_SB_N_Neutr, num_DB_O ) ) { - /* detected; attempt to fix */ - BNS_EDGE *pe_CMinus; - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_DB_O && cur_success < num_try; i ++ ) { - iat = iat_DB_O[i]; - pe_CMinus = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; - pe_CMinus->forbidden &= forbidden_edge_mask_inv; - - pe = pBNS->edge + pBNS->vert[iat].iedge[0]; /* double bond O=...*/ - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; /* change bond O=X to O(rad)-X(rad) */ - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 2 ) { - /* Added (-) charge to =O and (+) charge to N => nDeltaCharge == 2 */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 12 */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - pe->forbidden &= forbidden_edge_mask_inv; /* allow changes to O=X bond */ - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - break; - } else - if ( bOnly_N_V ) { - bOnly_N_V = 0; - } else { - break; - } - } - } - - if ( pc2i->nNumTgDiffMinus /*|| pc2i->nNumTgDiffH */ /* no ADP in InChI needed */ ) { - /*--------------------------------------------------------------*/ - /* | | */ - /* case 13: restored: O=AB=N= original: (-)O-AB-N(+)= */ - /* FixH: 0 0 0 0 */ - /* MobH: 0 0 0 0 */ - /* non-taut taut non-taut */ - /* O = O, S, Se, N; N = N, P, ... */ - /* t-group in original has same num. endpoints */ - /* same num_H and less (-) than in the restored structure */ - /* original atom O is tautomeric, N is not taut in both */ - /* original struct has 1 t-group; restored has less endpoints */ - /* and/or possibly >1 t-groups */ - /* Solution: separate charges between O= and -N< */ - /* allow moving charge to N(V) to make it N(IV)(+) */ - /*--------------------------------------------------------------*/ - int itg; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - - S_CHAR *num_Fixed_H_Revrs = pStruct->pOneINChI[0]->nNum_H_fixed? pStruct->pOneINChI[0]->nNum_H_fixed : NULL; - S_CHAR *pnMobHRevrs = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNum_H)? - pStruct->pOneINChI[1]->nNum_H : - (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)? - pStruct->pOneINChI[0]->nNum_H : NULL; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - cur_success = 0; - /* find whether this may help */ - for ( itg = 0; itg < pStruct->ti.num_t_groups && itg < pStruct->One_ti.num_t_groups; itg ++ ) { - if ( pStruct->ti.t_group[itg].nNumEndpoints == pStruct->One_ti.t_group[itg].nNumEndpoints && - pStruct->ti.t_group[itg].num[0] - pStruct->ti.t_group[itg].num[1] == - pStruct->One_ti.t_group[itg].num[0] - pStruct->One_ti.t_group[itg].num[1] && - pStruct->ti.t_group[itg].num[1] > pStruct->One_ti.t_group[itg].num[1]) { - /* restored InChI t-group has more (-) and same number of H */ - - int num_SB_N_Neutr = 0, num_DB_O = 0, iat; - short iat_SB_N_Neutr[MAX_DIFF_FIXH], iat_DB_O[MAX_DIFF_FIXH]; - cur_success = 0; - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( /* orig. InChI info: -O(-) */ - num_DB_O < MAX_DIFF_FIXH && - (pVA[i].cNumValenceElectrons == 6 /* O, S, Se, Te */ ) && - pStruct->endpoint[i] == itg+1 && - (e=pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i]) && - /* reversed structure info: */ - /*!pc2i->c2at[i].endptRevrs &&*/ - !(num_Fixed_H_Revrs && num_Fixed_H_Revrs[iat]) && - !(pnMobHRevrs && pnMobHRevrs[iat]) && - at2[iat].charge == 0 && at2[iat].num_H == 0 && - at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 ) { - - iat_DB_O[num_DB_O ++] = iat; - /* - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - */ - } else - if ( /* in restored atom N: charge=0, no H, has no double bond, not an endpoint */ - num_SB_N_Neutr < MAX_DIFF_FIXH && - at2[iat].charge == 0 && !at2[iat].num_H && - /*at2[iat].valence == at2[iat].chem_bonds_valence ||*/ - (at2[iat].valence==4 && at2[iat].chem_bonds_valence==5) && - !pVA[iat].cMetal && - pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber >= 1 && - !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint) && - /* in orig.InChI: not an endpoint, has no H */ - !pStruct->endpoint[i] && - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i]) && - /* has (+) edge */ - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - - iat_SB_N_Neutr[num_SB_N_Neutr ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( num_try = inchi_min( num_SB_N_Neutr, num_DB_O ) ) { - /* detected; attempt to fix */ - BNS_EDGE *pe_CMinus; - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_DB_O && cur_success < num_try; i ++ ) { - iat = iat_DB_O[i]; - pe_CMinus = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; - pe_CMinus->forbidden &= forbidden_edge_mask_inv; - - pe = pBNS->edge + pBNS->vert[iat].iedge[0]; /* double bond O=...*/ - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; /* change bond O=X to O(rad)-X(rad) */ - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 2 ) { - /* Added (-) charge to =O and (+) charge to N => nDeltaCharge == 2 */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 13 */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - pe->forbidden &= forbidden_edge_mask_inv; /* allow changes to O=X bond */ - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - break; - }/* else - if ( bOnly_N_V ) { - bOnly_N_V = 0; - } - */ - break; - } - } - } - - if ( (pc2i->nNumTgInChI <= 1 && - pc2i->nNumRemHInChI > pc2i->nNumRemHRevrs || pc2i->len_c2at) && - bHas_N_V( at2, pStruct->num_atoms) ) { - /*-----------------------------------------------------------------*/ - /* | | */ - /* case 14: restored:-N=AB=N=CD-XH original: (-)N-AB-N(+)=CD-XH */ - /* FixH: 0 0 0/1 0 1 */ - /* MobH: 0 0 1/0 0 0 */ - /* non-taut n/t any non any */ - /* taut */ - /* X = O, S, Se, N; N = N */ - /* t-group in original may have more (-) than in restored */ - /* same num_H and less (-) than in the restored structure */ - /* atom N(V)/N(IV)(+) is not taut in both */ - /* The following transformation should be possible: */ - /* | | */ - /* N=AB=N=CD-XH -> (-)N-AB-N-CD=XH(+) */ - /* This allows ADP to remove H(+) from -XH */ - /* As the result, the original structure has 0 or 1 t-group */ - /* Solution: separate charges between -N(III)= and N(V) */ - /*-----------------------------------------------------------------*/ - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - int num_N_V = 0, iat, i1, i2, i3, e1Flower, e1Plus, e2Plus, e2Minus, e3Plus; - int max_success = pc2i->nNumRemHInChI - pc2i->nNumRemHRevrs; - short iat_N_V_Array[MAX_DIFF_FIXH]; - EDGE_LIST iat_X_List, iat_N_III_List; - AllocEdgeList( &iat_X_List, EDGE_LIST_CLEAR ); - AllocEdgeList( &iat_N_III_List, EDGE_LIST_CLEAR ); - cur_success = 0; - ret = 0; - for ( i = 0; i < pStruct->num_atoms; i ++ ) { - iat = nCanon2AtnoRevrs[i]; - /* search for N(V), 3 bonds */ - if ( /* restored structure */ - num_N_V < MAX_DIFF_FIXH && - at2[iat].chem_bonds_valence == 5 && at2[iat].valence == 3 && - !at2[iat].charge && !at2[iat].radical && - pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && - !( at_Mobile_H_Revrs && at_Mobile_H_Revrs[i].endpoint ) && - !at2[iat].num_H && - (e = pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pBNS->edge[e].flow /* no charge */ && - NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e )) && !pBNS->edge[j].forbidden && - !pBNS->edge[j].flow /* neutral, valence=5 */ && - /* orig. InChI */ - !pStruct->endpoint[i] && - !(nMobHInChI && nMobHInChI[i]) && !pStruct->fixed_H[i] ) { - iat_N_V_Array[num_N_V ++] = iat; - } else - /* search for -N= */ - if ( /* restored structure */ - at2[iat].chem_bonds_valence == 3 && at2[iat].valence == 2 && - !at2[iat].charge && !at2[iat].radical && - pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && - !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[i].endpoint ) && - !at2[iat].num_H && - (e = pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - !pBNS->edge[e].flow /* no charge */ && - /* orig. InChI */ - /*!pStruct->endpoint[i] &&*/ - !(nMobHInChI && nMobHInChI[i]) && !pStruct->fixed_H[i] ) { - - if ( ret = AddToEdgeList( &iat_N_III_List, iat, 32 ) ) { - goto exit_case_14; - } - } else - /* search for -OH -NH-, -NH2 */ - if ( /* restored structure */ - at2[iat].chem_bonds_valence == at2[iat].valence && - !at2[iat].charge && !at2[iat].radical && - (pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 || - pVA[iat].cNumValenceElectrons == 6 ) && - at2[iat].num_H && - (e = pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pBNS->edge[e].flow /* no charge */ && - /* orig. InChI */ - !(nMobHInChI && nMobHInChI[i]) && pStruct->fixed_H[i] ) { - - if ( ret = AddToEdgeList( &iat_X_List, iat, 32 ) ) { - goto exit_case_14; - } - } - } - if ( !max_success ) { - max_success = inchi_min( num_N_V, iat_N_III_List.num_edges ); - max_success = inchi_min( max_success, iat_X_List.num_edges ); - } - if ( num_N_V && iat_N_III_List.num_edges && iat_X_List.num_edges ) { - for ( i1 = 0; i1 < num_N_V && cur_success < max_success; i1 ++ ) { - int iat_N_V = iat_N_V_Array[i1]; - if ( NO_VERTEX == iat_N_V || - 0 >= (e1Plus = pVA[iat_N_V].nCPlusGroupEdge-1) || - NO_VERTEX == (e1Flower = GetChargeFlowerUpperEdge( pBNS, pVA, e1Plus )) || - 1 != pBNS->edge[e1Plus].flow || - 0 != pBNS->edge[e1Flower].flow ) { - continue; - } - for ( i2 = iat_N_III_List.num_edges-1; 0 <= i2 && cur_success < max_success; i2 -- ) { - int iat_N_III = iat_N_III_List.pnEdges[i2]; - if ( NO_VERTEX == iat_N_III || - 0 >= (e2Minus = pVA[iat_N_III].nCMinusGroupEdge-1) || - 0 >= (e2Plus = pVA[iat_N_III].nCPlusGroupEdge-1) || - 0 != pBNS->edge[e2Minus].flow || - 1 != pBNS->edge[e2Plus].flow ) { - /* do not consider this atom anymore */ - iat_N_III_List.pnEdges[i2] = NO_VERTEX; - continue; - } - for ( i3 = iat_X_List.num_edges-1; 0 <= i3 && cur_success < max_success; i3 -- ) { - int iat_X = iat_X_List.pnEdges[i3]; - BNS_VERTEX *pv1n, *pv2n; - BNS_EDGE *pe1n, *pe2n, *pe1Plus, *pe2Minus, *pe3Plus; - Vertex v1n, v2n; - ret = 0; - if ( NO_VERTEX == iat_X || - 0 >= (e3Plus = pVA[iat_X].nCPlusGroupEdge-1) || - 1 != pBNS->edge[e3Plus].flow ) { - /* do not consider this atom anymore */ - iat_X_List.pnEdges[i3] = NO_VERTEX; - continue; - } - /* all is ready to check whether the following applies: - forbid changes of all charges and N,P,... flowers - allow to change edges: e2Minus, e3Plus - Increment flow in e1Flower - The result should be: increase in number of charges by 2 - */ - pe1Plus = pBNS->edge + e1Plus; /* N(V) positive charge edge */ - pe2Minus = pBNS->edge + e2Minus; /* =N- negative charge edge */ - pe3Plus = pBNS->edge + e3Plus; /* -XH positive charge edge */ - pe = pBNS->edge + e1Flower; /* N(V) flower edge */ - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - for ( j = pv1->num_adj_edges-1; 0 <= j; j -- ) { - pe1n = pBNS->edge + pv1->iedge[j]; - if ( pe1n->flow && !pe1n->forbidden && pe1n != pe1Plus ) { - pv1n = pBNS->vert + (v1n = pe1n->neighbor12 ^ v1); - break; - } - } - if ( j < 0 ) - continue; /* not found -- should not happen */ - for ( j = pv2->num_adj_edges-1; 0 <= j; j -- ) { /* was -2; changed 2006-2-28 12:35pm*/ - pe2n = pBNS->edge + pv2->iedge[j]; - if ( pe2n->flow && !pe2n->forbidden && pe2n != pe1Plus ) { - pv2n = pBNS->vert + (v2n = pe2n->neighbor12 ^ v2); - break; - } - } - if ( j < 0 ) - continue; /* not found -- should not happen */ - delta = 1; - pe->flow += delta; - pe1n->flow -= delta; - pe2n->flow -= delta; - pv1n->st_edge.flow -= delta; - pv2n->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - SetForbiddenEdgeMask( pBNS, &OtherNFlowerEdges, forbidden_edge_mask ); - - /* allow two charges to change */ - pe2Minus->forbidden &= forbidden_edge_mask_inv; - pe3Plus->forbidden &= forbidden_edge_mask_inv; - /* test #1 */ - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - INCHI_HEAPCHK - if ( ret < 0 ) { - goto exit_case_14; - } else - if ( ret == 1 && (vPathEnd == v1n && vPathStart == v2n || - vPathEnd == v2n && vPathStart == v1n) && - nDeltaCharge == 2 ) { - ; /* success */ - } else { - ret = 0; - } - /* restore BNS */ - pe2Minus->forbidden |= forbidden_edge_mask; - pe3Plus->forbidden |= forbidden_edge_mask; - pe->flow -= delta; - pe1n->flow += delta; - pe2n->flow += delta; - pv1n->st_edge.flow += delta; - pv2n->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - if ( ret == 1 ) { - /* test #2: check if charge separation is possible */ - pe->flow += delta; - pe1n->flow -= delta; - pe2n->flow -= delta; - pv1n->st_edge.flow -= delta; - pv2n->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - /* allow two charges (N(V) and N(III)) to change */ - pe2Minus->forbidden &= forbidden_edge_mask_inv; - pe1Plus->forbidden &= forbidden_edge_mask_inv; - /* test #2 */ - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret == 1 && (vPathEnd == v1n && vPathStart == v2n || - vPathEnd == v2n && vPathStart == v1n) && - nDeltaCharge == 2 ) { - /* success; actually change charges */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 14 */ - } - } - if ( ret <= 0 ) { - /* failed: restore BNS flow */ - pe->flow -= delta; - pe1n->flow += delta; - pe2n->flow += delta; - pv1n->st_edge.flow += delta; - pv2n->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &OtherNFlowerEdges, forbidden_edge_mask ); - if ( ret > 0 ) { - /* do not repeat for the same atoms */ - iat_N_V_Array[i1] = NO_VERTEX; - iat_N_III_List.pnEdges[i2] = NO_VERTEX; - iat_X_List.pnEdges[i3] = NO_VERTEX; - } - if ( ret < 0 ) { - goto exit_case_14; - } - if ( ret > 0 ) { - break; - } - } /* i3 cycle */ - if ( ret > 0 ) { - break; - } - } /* i2 cycle */ - } - } -exit_case_14: - AllocEdgeList( &iat_X_List, EDGE_LIST_FREE ); - AllocEdgeList( &iat_N_III_List, EDGE_LIST_FREE ); - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &OtherNFlowerEdges, forbidden_edge_mask ); - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( ret < 0 ) { - goto exit_function; - } - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - - if ( pc2i->nNumTgMRevrs > pc2i->nNumTgMInChI || - pc2i->nNumRemHRevrs < pc2i->nNumRemHInChI || - pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || - pc2i->nNumTgInChI <= 1 && pc2i->nNumTgRevrs > pc2i->nNumTgInChI ) { - /*--------------------------------------------------------------*/ - /* case 15: restored: -(+)O=AB-N< orig: -O-AB=N(+)< */ - /* (a) restored t-groups have more (-) than in original InChI */ - /* (b) Mobile-H charge: restored > original InChI *and* */ - /* removed H: restored < original InChI */ - /* (c) restored t-groups have less endpnoits than in orig InChI */ - /* O = O, S, Se, Te; N = N */ - /* Solution: move (+) from -O(+)= to -N< */ - /*--------------------------------------------------------------*/ - int num_SB_Neutr = 0, num_DB_Charged = 0, iat; - short iat_SB_Neutr[MAX_DIFF_FIXH], iat_DB_Charged[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - cur_success = 0; - /* search for -O(+)= */ - /* search for -N< */ - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( /* -O(+)= in restored atom: charge=1, has no H, a double bond */ - num_DB_Charged < MAX_DIFF_FIXH && - at2[iat].charge == 1 && !at2[iat].num_H && - at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - (pVA[iat].cNumValenceElectrons == 6 ) && - /* in orig.InChI: an endpoint, has fixed-H */ - /*pStruct->endpoint[i] &&*/ - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i] ) && - /* has (+) edge */ - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && 0 == pBNS->edge[e].forbidden ) { - - iat_DB_Charged[num_DB_Charged ++] = iat; - /* - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - */ - } else - if ( /* -N< in restored atom: charge=0, has no H, has no double bond, N only */ - num_SB_Neutr < MAX_DIFF_FIXH && - at2[iat].charge == 0 && !at2[iat].num_H && - at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - (pVA[iat].cNumValenceElectrons == 5 && - pVA[iat].cPeriodicRowNumber == 1 ) && - /* in orig.InChI: an endpoint, has fixed-H */ - /*pStruct->endpoint[i] &&*/ - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i] ) && - /* has (+) edge */ - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && 0 == pBNS->edge[e].forbidden ) { - - iat_SB_Neutr[num_SB_Neutr ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( num_try = inchi_min( num_SB_Neutr, num_DB_Charged ) ) { - /* detected; attempt to fix */ - BNS_VERTEX *pv1n, *pv2n; - BNS_EDGE *pe1n, *pe2n; - Vertex v1n, v2n; - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_DB_Charged && cur_success < num_try; i ++ ) { - iat = iat_DB_Charged[i]; - pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; - if ( pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - for ( j = pv1->num_adj_edges-1; 0 <= j; j -- ) { - pe1n = pBNS->edge + pv1->iedge[j]; - if ( pe1n->flow && !pe1n->forbidden ) { - pv1n = pBNS->vert + (v1n = pe1n->neighbor12 ^ v1); - break; - } - } - if ( j < 0 ) - continue; /* not found */ - - for ( j = pv2->num_adj_edges-1; 0 <= j; j -- ) { /* was -2; changed 2006-2-28 12:35pm*/ - pe2n = pBNS->edge + pv2->iedge[j]; - if ( pe2n->flow && !pe2n->forbidden ) { - pv2n = pBNS->vert + (v2n = pe2n->neighbor12 ^ v2); - break; - } - } - if ( j < 0 ) - continue; /* not found */ - - pe->flow += delta; - pe1n->flow -= delta; - pe2n->flow -= delta; - pv1n->st_edge.flow -= delta; - pv2n->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1n && vPathStart == v2n || - vPathEnd == v2n && vPathStart == v1n) && - (nDeltaCharge == 0 || nDeltaCharge == 1) ) { - /* Moved charge from O(+) to -N< => nDeltaCharge == 1 or 0 if pe2n = -N< charge edge */ - /* Flow change on pe (+)charge edge (atom NH2) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 15 */ - } - } else { - pe->flow -= delta; - pe1n->flow += delta; - pe2n->flow += delta; - pv1n->st_edge.flow += delta; - pv2n->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pc2i->nNumTgDiffMinus ) { - /*----------------------------------------------------------------*/ - /* case 16: restored: O=X-NH(-) orig.: O(-)-X=NH */ - /* t-group: (H,-) (2H) */ - /* O(-) = S, Se, Te; N = N; */ - /* Solution: move (-) from O(-) to -NH(-) */ - /*----------------------------------------------------------------*/ - int num_SB_N_Minus = 0, num_DB_O_Neutr = 0, iat, itg; - short iat_SB_N_Minus[MAX_DIFF_FIXH], iat_DB_O_Neutr[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - cur_success = 0; - for ( itg = 0; itg < pStruct->ti.num_t_groups && itg < pStruct->One_ti.num_t_groups; itg ++ ) { - if ( pStruct->ti.t_group[itg].nNumEndpoints != pStruct->One_ti.t_group[itg].nNumEndpoints || - pStruct->ti.t_group[itg].num[1] >= pStruct->One_ti.t_group[itg].num[1] ) { - continue; - } - CurrEdges.num_edges = num_SB_N_Minus = num_DB_O_Neutr = 0; - cur_success = 0; - for ( j = 0, k = pStruct->One_ti.t_group[itg].nFirstEndpointAtNoPos; - j < pStruct->One_ti.t_group[itg].nNumEndpoints; j ++ ) { - i = pStruct->One_ti.nEndpointAtomNumber[k+j]; /* canonical number in restored struct. */ - iat = nCanon2AtnoRevrs[i]; - if ( /* in restored atom: charge=0, has no H, has double bond, O, S, Se, Te */ - num_DB_O_Neutr < MAX_DIFF_FIXH && - at2[iat].charge == 0 && !at2[iat].num_H && - at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - pVA[iat].cNumValenceElectrons == 6 && - /* in orig.InChI: an endpoint, may have fixed-H */ - pStruct->endpoint[i] && - /*!(pStruct->fixed_H && pStruct->fixed_H[i]) &&*/ - !(nMobHInChI && nMobHInChI[i] ) && - /* has (-) edge */ - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - - iat_DB_O_Neutr[num_DB_O_Neutr ++] = iat; - - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - - } else - if ( /* in restored atom: charge=-1, has H, has double bond, N */ - num_SB_N_Minus < MAX_DIFF_FIXH && - at2[iat].charge == -1 && at2[iat].num_H && - at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && - /* in orig.InChI: an endpoint, has no fixed-H */ - pStruct->endpoint[i] && - (pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i] ) && - /* has (-) edge */ - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && - 0 == pBNS->edge[e].forbidden ) { - - iat_SB_N_Minus[num_SB_N_Minus ++] = iat; - /* - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - */ - } - } - if ( num_try = inchi_min( num_SB_N_Minus, num_DB_O_Neutr ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - /* allow stereobonds in rings change */ - /* - if ( forbidden_stereo_edge_mask ) - RemoveForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask ); - */ - delta = 1; - for ( i = 0; i < num_SB_N_Minus && cur_success < num_try; i ++ ) { - iat = iat_SB_N_Minus[i]; - pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - /*pe->forbidden |= forbidden_edge_mask;*/ - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Moved (-) charge to =O => nDeltaCharge == 1 */ - /* Flow change on pe (-)charge edge (atom -NH(-)) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 16 */ - } - } else { - pe->forbidden &= forbidden_edge_mask_inv; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - /* - if ( forbidden_stereo_edge_mask ) - SetForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask ); - */ - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - } - - if ( pc2i->nNumRemHInChI < pc2i->nNumRemHRevrs ) { - /*--------------------------------------------------------------*/ - /* case 17: restored: OH(+)=AB-O- orig. HO-AB=O(+)- */ - /* number of removed H: n+m n */ - /* OH(+) = N, O, S, Se; -O- = P,As,O,S,Se,Te,F,Cl,Br,I */ - /* Solution: move (+) from OH(+) to -O- */ - /*--------------------------------------------------------------*/ - int num_SB_Neutr = 0, num_DB_Charged = 0, iat; - short iat_SB_Neutr[MAX_DIFF_FIXH], iat_DB_Charged[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - cur_success = 0; - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( /* in restored atom: charge=+1, has H, has double bond, N, O, S, Se, Te */ - num_DB_Charged < MAX_DIFF_FIXH && - at2[iat].charge == 1 && at2[iat].num_H && - at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - (pVA[iat].cNumValenceElectrons == 6 || - pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1) && - /* has (+) edge */ - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - - iat_DB_Charged[num_DB_Charged ++] = iat; - /* - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - */ - } else - if ( /* in restored atom: charge=0, has no H, has no double bond, N, P, O, S, Se, Te */ - num_SB_Neutr < MAX_DIFF_FIXH && - at2[iat].charge == 0 && !at2[iat].num_H && - at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - (pVA[iat].cNumValenceElectrons == 6 || pVA[iat].cNumValenceElectrons == 7 || - pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber > 1 ) && - /* in orig.InChI: not an endpoint */ - !pStruct->endpoint[i] && - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i] ) && - /* has (+) edge */ - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && - 0 == pBNS->edge[e].forbidden ) { - - iat_SB_Neutr[num_SB_Neutr ++] = iat; - - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( num_try = inchi_min( num_SB_Neutr, num_DB_Charged ) ) { - BNS_VERTEX *pv1n, *pv2n; - BNS_EDGE *pe1n, *pe2n; - Vertex v1n, v2n; - - num_try = inchi_min( num_try, pc2i->nNumRemHRevrs-pc2i->nNumRemHInChI); - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_DB_Charged && cur_success < num_try; i ++ ) { - iat = iat_DB_Charged[i]; - pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; - if ( pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - for ( j = pv1->num_adj_edges-1; 0 <= j; j -- ) { - pe1n = pBNS->edge + pv1->iedge[j]; - if ( pe1n->flow && !pe1n->forbidden ) { - pv1n = pBNS->vert + (v1n = pe1n->neighbor12 ^ v1); - break; - } - } - if ( j < 0 ) - continue; /* not found */ - - for ( j = pv2->num_adj_edges-1; 0 <= j; j -- ) { /* was -2; changed 2006-2-28 12:35pm*/ - pe2n = pBNS->edge + pv2->iedge[j]; - if ( pe2n->flow && !pe2n->forbidden ) { - pv2n = pBNS->vert + (v2n = pe2n->neighbor12 ^ v2); - break; - } - } - if ( j < 0 ) - continue; /* not found */ - - pe->flow += delta; - pe1n->flow -= delta; - pe2n->flow -= delta; - pv1n->st_edge.flow -= delta; - pv2n->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1n && vPathStart == v2n || - vPathEnd == v2n && vPathStart == v1n) && - (nDeltaCharge == 0 || nDeltaCharge == 1) ) { - /* Moved charge from OH(+) to -O- => nDeltaCharge == 1 or 0 if pe2n = -O- charge edge */ - /* Flow change on pe (+)charge edge (atom OH(+)) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 17 */ - } - } else { - pe->flow -= delta; - pe1n->flow += delta; - pe2n->flow += delta; - pv1n->st_edge.flow += delta; - pv2n->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( (pc2i->nNumTgInChI && pStruct->endpoint && - pc2i->nNumTgMInChI > pc2i->nNumTgMRevrs && pc2i->nNumEndpInChI > pc2i->nNumEndpRevrs ) ) { - /*-----------------------------------------------------------------*/ - /* */ - /* case 18: restored:-N=AB-X -(-)N-AB-X(+) */ - /* FixH: 0 0 0 0 */ - /* MobH: 0 0 0 0 */ - /* non non taut non */ - /* taut taut taut */ - /* X = any heteroatom N=N */ - /* t-group in original has (Hn,-m) in the restored: (Hn,-m+1) */ - /* same num_H and more (-) than in the restored structure */ - /* atom X is not taut in both */ - /* Solution: separate charges between -N(III)= and X */ - /*-----------------------------------------------------------------*/ - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - int iat, e1, itg, max_success; - CurrEdges.num_edges = 0; - cur_success = 0; - ret = 0; - /* search for -N= */ - for ( itg = 0; itg < pStruct->ti.num_t_groups && itg < pStruct->One_ti.num_t_groups; itg ++ ) { - if ( pStruct->ti.t_group[itg].nNumEndpoints <= pStruct->One_ti.t_group[itg].nNumEndpoints || - pStruct->ti.t_group[itg].num[1] <= pStruct->One_ti.t_group[itg].num[1] ) { - continue; - } - CurrEdges.num_edges = 0; - cur_success = 0; - for ( j = 0, k = pStruct->ti.t_group[itg].nFirstEndpointAtNoPos; - j < pStruct->ti.t_group[itg].nNumEndpoints; j ++ ) { - i = pStruct->ti.nEndpointAtomNumber[k+j]; /* canonical number in restored struct. */ - iat = nCanon2AtnoRevrs[i]; - if ( !pStruct->endpoint[i] || !at_Mobile_H_Revrs || at_Mobile_H_Revrs[iat].endpoint || - pVA[i].cNumValenceElectrons != 5 || pVA[i].cPeriodicRowNumber != 1 || - 2 != at2[iat].valence || at2[iat].num_H || at2[iat].radical || - 0 <= (e1=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e1].flow || - 0 > (e=pVA[iat].nCMinusGroupEdge-1) || pBNS->edge[e].forbidden || pBNS->edge[e].flow ) { - continue; - } - /* found -N= */ - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( !(max_success = CurrEdges.num_edges) ) { - goto exit_case_18; - } - /* search for X */ - for ( i = 0; i < pStruct->num_atoms && cur_success < max_success; i ++ ) { - iat = nCanon2AtnoRevrs[i]; - if ( pStruct->endpoint[i] || !pVA[i].cNumValenceElectrons || pVA[i].cNumValenceElectrons == 4 || - at2[iat].num_H || at2[iat].radical || - 0 <= (e1=pVA[iat].nCMinusGroupEdge-1) && !pBNS->edge[e1].flow || - 0 > (e=pVA[iat].nCPlusGroupEdge-1) || pBNS->edge[e].forbidden || pBNS->edge[e].flow != 1 ) { - continue; - } - /* try to move the charge */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - SetForbiddenEdgeMask( pBNS, &OtherNFlowerEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - - pe = pBNS->edge + e; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - delta = 1; - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Created (-) charge on -N= => nDeltaCharge == 1 */ - /* Flow change on pe (+)charge edge (atom X) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 18 */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } -exit_case_18: - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &OtherNFlowerEdges, forbidden_edge_mask ); - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( ret < 0 ) { - goto exit_function; - } - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - if ( pc2i->len_c2at >= 1 ) { - /*--------------------------------------------------------------*/ - /* case 19 restored: M--OH original: M(-)==OH(+) */ - /* FixH: metal 0 1 */ - /* MobH: 1 0 */ - /* O = O, S, Se, Te; not taut. in InChI */ - /* In restored structure has H; tautomeric or not tautomeric */ - /* Solution: move (+) from -OH to M; charhe on M may vary */ - /*--------------------------------------------------------------*/ - int iat; - EdgeIndex eOHPlus, eMPlus, eMMinus, eOMBond; - BNS_EDGE *peOHPlus, *peMPlus, *peMMinus, *peOMBond; - int iatMetal, ChargeOnMetal, DeltaChargeExpected; - cur_success = 0; - num_zero_ret = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( /* orig. InChI info: =NH2(+), =OH(+) */ - (pc2i->c2at[i].nValElectr == 6 ) /* N, O, S, Se, Te */ && - /*!pc2i->c2at[i].endptInChI &&*/ /* <=== relaxation */ - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && - pc2i->c2at[i].nFixHInChI == 1 && pc2i->c2at[i].nMobHInChI == 0 && - /* reversed structure info: */ - pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 1 && - pc2i->c2at[i].nAtChargeRevrs == 0 && at2[iat].num_H && - at2[iat].valence == 1 && - at2[iat].valence == at2[iat].chem_bonds_valence && - /* metal atom */ - pVA[iatMetal=at2[iat].neighbor[0]].cMetal && - (eMPlus=pVA[iatMetal].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[eMPlus].forbidden && - (eMMinus=pVA[iatMetal].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[eMMinus].forbidden && - !pBNS->edge[eOMBond=pBNS->vert[iat].iedge[0]].forbidden - ) { - - /* -OH charge edges */ - if ( ret = AddToEdgeList( &CurrEdges, iat, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( CurrEdges.num_edges ) { - /* detected; fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - SetForbiddenEdgeMask( pBNS, &NFlowerEdges, forbidden_edge_mask ); - SetForbiddenEdgeMask( pBNS, &AllBondEdges, forbidden_edge_mask ); - for ( i = 0; i < CurrEdges.num_edges; i ++ ) { - /* v1 is -OH, v2 is adjacent to it Metal */ - iat = CurrEdges.pnEdges[i]; - iatMetal = at2[iat].neighbor[0]; - peOHPlus = pBNS->edge + (eOHPlus = pVA[iat].nCPlusGroupEdge-1); - peMPlus = pBNS->edge + (eMPlus = pVA[iatMetal].nCPlusGroupEdge-1); - peMMinus = pBNS->edge + (eMMinus = pVA[iatMetal].nCMinusGroupEdge-1); - peOMBond = pBNS->edge + (eOMBond =pBNS->vert[iat].iedge[0]); - /* remove forbidden edge masks */ - peMPlus->forbidden &= forbidden_edge_mask_inv; - peMMinus->forbidden &= forbidden_edge_mask_inv; - peOMBond->forbidden &= forbidden_edge_mask_inv; - - ChargeOnMetal = (peMPlus->cap - peMPlus->flow) - peMMinus->flow; - if ( 1 == ChargeOnMetal ) { - /* We are going to subtract 1 from the charge on Metal */ - /* Added (+)charge to -OH is not known to RunBnsTestOnce() */ - DeltaChargeExpected = -1; /* charge will become = 0 */ - } else - if ( 0 == ChargeOnMetal ) { - DeltaChargeExpected = 1; /* charge on Metal will be created */ - } else { - DeltaChargeExpected = 0; - } - - delta = 1; - pe = peOHPlus; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->flow -= delta; /* remove (-) from AB-O(-) */ - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == DeltaChargeExpected ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 19 */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - /* set forbidden edge masks back */ - peMPlus->forbidden |= forbidden_edge_mask; - peMMinus->forbidden |= forbidden_edge_mask; - peOMBond->forbidden |= forbidden_edge_mask; - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &NFlowerEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &AllBondEdges, forbidden_edge_mask ); - - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - } - if ( pc2i->len_c2at > 1 && pc2i->nNumTgRevrs && pc2i->nNumTgInChI) { - /*--------------------------------------------------------------*/ - /* case 20: restored: O(-)-AB=N- original: O=AB-N(-)- */ - /* FixH: 0 0 0 -1 */ - /* MobH: 0 0 0 1 */ - /* taut non-taut non-taut taut */ - /* or taut no H */ - /* no H */ - /* O = O, S, Se; N = N, O, S, Se, Te; */ - /* restored atoms are taut/non-taut; original are opposite. */ - /* Solution: move (-) from O(-) to =N- */ - /*--------------------------------------------------------------*/ - int num_SB_O_Minus = 0, num_DB_N = 0, iat; - short iat_SB_O_Minus[MAX_DIFF_FIXH], iat_DB_N[MAX_DIFF_FIXH]; - - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - /* - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - */ - cur_success = 0; - CurrEdges.num_edges = 0; /* clear current edge list */ - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( /* orig. InChI info: =O or -N= */ - num_DB_N < MAX_DIFF_FIXH && - pc2i->c2at[i].endptInChI && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pBNS->edge[e].flow == 0 && - pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI == 0 && - /* if more than 1 t-group are in orig. InChI then do not move (-) to N */ - (pc2i->nNumTgInChI == 1 || pc2i->c2at[i].nValElectr == 6) && - /* reversed structure info: */ - !pc2i->c2at[i].endptRevrs && - pc2i->c2at[i].nFixHRevrs == 0 && /*pc2i->c2at[i].nMobHRevrs == 0 &&*/ - pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && - at2[iat].valence + 1 == at2[iat].chem_bonds_valence ) { - iat_DB_N[num_DB_N ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else - if ( /* orig. InChI info: -O(-) */ - num_SB_O_Minus < MAX_DIFF_FIXH && - !pc2i->c2at[i].endptInChI && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pBNS->edge[e].flow == 1 && - pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI == 0 && - pc2i->c2at[i].nValElectr == 6 && - /* reversed structure info: */ - pc2i->c2at[i].endptRevrs && - pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && - pc2i->c2at[i].nAtChargeRevrs == -1 && !at2[iat].num_H && - at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 1 ) { - iat_SB_O_Minus[num_SB_O_Minus ++] = iat; - } - } - if ( !num_DB_N ) { - /* search among N that are tautomeric in both cases */ - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - if ( !pStruct->endpoint[i] ) { - continue; - } - iat = nCanon2AtnoRevrs[i]; - if ( /* in restored atom O: charge=-1, no H, has no double bond, endpoint */ - num_DB_N < MAX_DIFF_FIXH && - at2[iat].charge == 0 && !at2[iat].num_H && - at2[iat].valence + 1 == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - /* in orig.InChI: an endpoint, has no H */ - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - /*!(nMobHInChI && nMobHInChI[i] ) &&*/ - /* has (-) edge */ - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - !pBNS->edge[e].flow ) { - - iat_DB_N[num_DB_N ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - } - if ( num_try = inchi_min( num_SB_O_Minus, num_DB_N ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_SB_O_Minus && cur_success < num_try; i ++ ) { - iat = iat_SB_O_Minus[i]; - pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Added (-) charge to =N- => nDeltaCharge == 1 */ - /* Flow change on pe (-)charge edge (atom -O(-)) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 20 */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - if ( pc2i->len_c2at && pc2i->nNumTgRevrs && pc2i->nNumTgHInChI && pStruct->endpoint ) { - /*--------------------------------------------------------------*/ - /* O(-) O */ - /* | || */ - /* case 21: restored: R=S=O original: R-S=O */ - /* | | */ - /* O(-) O(-) */ - /* All O are taut R is not taut */ - /* */ - /* In addition, another atom O that should have been tautomeric */ - /* or has H(+) added in Mobile-H layer is not like that */ - /* O = O, S, Se; S=S, Se, Te */ - /* Solution: move (-) from O(-) to =O */ - /* these atoms are tautomeric in restored structure */ - /*--------------------------------------------------------------*/ - int num_SB_O_Minus = 0, num_DB_O = 0, iat, iS; - short iat_SB_O_Minus[MAX_DIFF_FIXH], iat_Central[MAX_DIFF_FIXH], iat_DB_O[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - /* - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - */ - CurrEdges.num_edges = 0; /* clear current edge list */ - cur_success = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( /* orig. InChI info: =O */ - num_DB_O < MAX_DIFF_FIXH && - pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ && - (pc2i->c2at[i].endptInChI || pc2i->c2at[i].nMobHInChI) && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pc2i->c2at[i].nFixHInChI == 0 && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ - /* reversed structure info: */ - !(pc2i->c2at[i].endptRevrs || pc2i->c2at[i].nMobHRevrs) && - pc2i->c2at[i].nFixHRevrs == 0 && - pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && - at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 ) { - iat_DB_O[num_DB_O ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - for ( i = 0; num_DB_O && i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - if ( !pStruct->endpoint[i] ) { - continue; - } - iat = nCanon2AtnoRevrs[i]; - if ( /* in restored atom O: charge=-1, no H, has no double bond, endpoint */ - num_SB_O_Minus < MAX_DIFF_FIXH && - at2[iat].charge == -1 && !at2[iat].num_H && - at2[iat].valence == 1 && at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - pVA[iat].cNumValenceElectrons == 6 && - (at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint) && - /* in orig.InChI: an endpoint, has no H */ - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - /*!(nMobHInChI && nMobHInChI[i] ) &&*/ - /* has (-) edge */ - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pBNS->edge[e].flow ) { - int nNumTautSB = 0, nNumTautDB = 0, nNumOtherDB = 0, nNumOtherSB = 0, nNumOthers = 0, nNumNegEndp = 0; - /* traverse neighbors of the centerpoint iS */ - iS = at2[i].neighbor[0]; - for ( j = 0; j < num_SB_O_Minus; j ++ ) { - if ( iat_Central[j] == iS ) - break; - } - if ( j < num_SB_O_Minus ) { - continue; /* have already been there */ - } - for ( j = 0; j < at[iS].valence; j ++ ) { - int bond_type = at2[iS].bond_type[j]; - k = at2[iS].neighbor[j]; - if ( k == i ) { - continue; - } - if ( pStruct->endpoint[k] == pStruct->endpoint[i] ) { - nNumTautSB += ( bond_type == BOND_TYPE_SINGLE ); - nNumTautDB += ( bond_type == BOND_TYPE_DOUBLE ); - } else - if ( bond_type == BOND_TYPE_DOUBLE ) { - nNumOtherDB ++; - } else - if ( bond_type == BOND_TYPE_SINGLE ) { - nNumOtherSB ++; - } else { - nNumOthers ++; - } - if ( at2[k].endpoint == at2[i].endpoint && at2[k].valence == 1 && - at2[k].charge == -1 && pVA[k].cNumValenceElectrons == 6 ) { - nNumNegEndp ++; - } - } - if ( !nNumTautSB ) { - continue; - } - if ( !( nNumOtherDB && nNumTautDB ) ) { - continue; /* ignore */ - } - - iat_SB_O_Minus[num_SB_O_Minus] = iat; - iat_Central[num_SB_O_Minus ++] = iS; - } - } - if ( num_try = inchi_min( num_SB_O_Minus, num_DB_O ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_SB_O_Minus && cur_success < num_try; i ++ ) { - iat = iat_SB_O_Minus[i]; - pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Added (-) charge to =O => nDeltaCharge == 1 */ - /* Flow change on pe (-)charge edge (atom -N(-)-) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 21 */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pc2i->len_c2at && pc2i->nNumTgRevrs && pc2i->nNumEndpInChI < pc2i->nNumEndpRevrs ) { - /*--------------------------------------------------------------*/ - /* O O */ - /* || || */ - /* case 21a:restored: R=S-R' =X original: R-S-R' -X(-) */ - /* | || */ - /* O(-) O(-) */ - /* All O and X are taut O and X are not taut */ - /* it is possible that X is R */ - /* */ - /* O = O, S, Se; S=S, Se, Te; X = N, O, S, Se, Te */ - /* Solution: move (-) from O(-) to =X */ - /* these atoms are tautomeric in restored structure */ - /*--------------------------------------------------------------*/ - int iat, iS; - /* - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - */ - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - /* - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - */ - EDGE_LIST OtherSO, CentralS, SOMinus, MinusAcceptord; - CurrEdges.num_edges = 0; /* clear current edge list */ - AllocEdgeList( &OtherSO, EDGE_LIST_CLEAR ); - AllocEdgeList( &CentralS, EDGE_LIST_CLEAR ); - AllocEdgeList( &SOMinus, EDGE_LIST_CLEAR ); - AllocEdgeList( &MinusAcceptord, EDGE_LIST_CLEAR ); - cur_success = 0; - if ( !at_Mobile_H_Revrs ) { - goto exit_case_21a; - } - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( /* orig. InChI info: -X(-) */ - /*num_DB_O < MAX_DIFF_FIXH &&*/ - /*pc2i->c2at[i].nValElectr == 6 */ /* O, S, Se, Te */ - !pc2i->c2at[i].endptInChI && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pc2i->c2at[i].nFixHInChI == 0 && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ - /* reversed structure info: */ - (pc2i->c2at[i].endptRevrs || pc2i->c2at[i].nMobHRevrs) && - pc2i->c2at[i].nFixHRevrs == 0 && - /*pc2i->c2at[i].nAtChargeRevrs == 0 &&*/ !at2[iat].num_H ) { - if ( pVA[iat].cNumValenceElectrons == 6 && at2[iat].charge == -1 && - pBNS->edge[e].flow && - at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 1 && - pVA[iS=(int)at2[iat].neighbor[0]].cNumValenceElectrons == 6 && pVA[iS].cPeriodicRowNumber > 1 && - at2[iS].valence >= 4 ) { - /* a candidate for S in -SO2- */ - int nNumTautSB = 0, nNumTautDB = 0, nNumOtherDB = 0, nNumOtherSB = 0; - int nNumOthers = 0, nNumNegEndp = 0, nNumEndpO = 0; - /* check whether we have already found it */ - if ( 0 <= FindInEdgeList( &CentralS, iS ) ) { - continue; - } - for ( j = 0; j < at[iS].valence; j ++ ) { - int bond_type = at2[iS].bond_type[j]; - k = at2[iS].neighbor[j]; - if ( k == iat ) { - continue; - } - if ( pc2i->c2at[i].endptRevrs == at_Mobile_H_Revrs[k].endpoint && !at2[k].endpoint ) { - nNumTautSB += ( bond_type == BOND_TYPE_SINGLE ); - nNumTautDB += ( bond_type == BOND_TYPE_DOUBLE ); - nNumEndpO += (pVA[k].cNumValenceElectrons == 6 && at2[k].valence == 1); - } else - if ( bond_type == BOND_TYPE_DOUBLE ) { - nNumOtherDB ++; - } else - if ( bond_type == BOND_TYPE_SINGLE ) { - nNumOtherSB ++; - } else { - nNumOthers ++; - } - if ( at2[k].endpoint == at2[i].endpoint && at2[k].valence == 1 && - at2[k].charge == -1 && pVA[k].cNumValenceElectrons == 6 ) { - nNumNegEndp ++; - } - } - if ( !nNumEndpO ) { - continue; - } - if ( nNumTautSB + nNumTautDB + nNumOtherDB <= nNumEndpO ) { - continue; /* ignore */ - } - /* collect double bond taut =O */ - for ( j = 0; j < at[iS].valence; j ++ ) { - int bond_type = at2[iS].bond_type[j]; - k = at2[iS].neighbor[j]; - if ( pc2i->c2at[i].endptRevrs == at_Mobile_H_Revrs[k].endpoint && - !at2[k].endpoint && pVA[k].cNumValenceElectrons == 6 && at2[k].valence == 1 && - 0 <= (e=pVA[k].nCMinusGroupEdge-1) && !pBNS->edge[e].forbidden ) { - if ( bond_type == BOND_TYPE_DOUBLE && !at2[k].charge && !pBNS->edge[e].flow) { - /* charges to be unchanged */ - if ( ret = AddToEdgeList( &OtherSO, e, INC_ADD_EDGE ) ) { - goto exit_case_21a; - } - } else - if ( bond_type == BOND_TYPE_SINGLE && at2[k].charge == -1 && pBNS->edge[e].flow ) { - /* charges to be removed */ - if ( ret = AddToEdgeList( &SOMinus, e, INC_ADD_EDGE ) ) { - goto exit_case_21a; - } - } - } - } - if ( ret = AddToEdgeList( &CentralS, iS, INC_ADD_EDGE ) ) { - goto exit_case_21a; - } - } else - if ( at2[iat].charge == 0 && !pBNS->edge[e].flow && - at2[iat].valence + 1 == at2[iat].chem_bonds_valence ) { - /* changeable charges */ - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - } - /* remove unchangeable from changeable */ - for ( i = 0; i < OtherSO.num_edges; i ++ ) { - RemoveFromEdgeListByValue( &CurrEdges, OtherSO.pnEdges[i] ); - } - - if ( num_try = inchi_min( SOMinus.num_edges, CurrEdges.num_edges ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < SOMinus.num_edges && cur_success < num_try; i ++ ) { - pe = pBNS->edge + SOMinus.pnEdges[i]; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - /*pe->forbidden |= forbidden_edge_mask;*/ - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Added (-) charge to =O => nDeltaCharge == 1 */ - /* Flow change on pe (-)charge edge (atom -N(-)-) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 21a */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } -exit_case_21a: - CurrEdges.num_edges = 0; /* clear current edge list */ - AllocEdgeList( &OtherSO, EDGE_LIST_FREE ); - AllocEdgeList( &CentralS, EDGE_LIST_FREE ); - AllocEdgeList( &SOMinus, EDGE_LIST_FREE ); - AllocEdgeList( &MinusAcceptord, EDGE_LIST_FREE ); - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pc2i->len_c2at ) { - /*------------------------------------------------------------------*/ - /* case 22: restored: N(-)=N(+)=C...=O orig: N#N-N=...-O(-) */ - /* im InChI -O(-) may have H(+) added by Normalization */ - /* or may be tautomeric */ - /* Solution: move (-) from N(-) to =O */ - /* */ - /*------------------------------------------------------------------*/ - int num_DB_O = 0, iat; - short iat_DB_O[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - int iN2, iC; - BNS_EDGE *peDB_O_Minus; - /* - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - */ - CurrEdges.num_edges = 0; /* clear current edge list */ - cur_success = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( /* orig. InChI info: =O */ - num_DB_O < MAX_DIFF_FIXH && - pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ && - (pc2i->c2at[i].endptInChI || pc2i->c2at[i].nMobHInChI) && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pc2i->c2at[i].nFixHInChI == 0 && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ - /* reversed structure info: */ - !(pc2i->c2at[i].endptRevrs || pc2i->c2at[i].nMobHRevrs) && - pc2i->c2at[i].nFixHRevrs == 0 && - pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && - at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 ) { - iat_DB_O[num_DB_O ++] = iat; - } - } - for ( i = 0; num_DB_O && i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( /* in restored atom O: charge=-1, no H, has no double bond, endpoint */ - at2[iat].charge == -1 && !at2[iat].num_H && - at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 && !pVA[iat].cMetal && - pVA[iat].cNumValenceElectrons == 5 && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pBNS->edge[e].flow && - !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint) && - pVA[iN2=at2[iat].neighbor[0]].cNumValenceElectrons == 5 && - at2[iat].bond_type[0] == BOND_TYPE_DOUBLE && - at2[iN2].charge == 1 && at2[iN2].valence == 2 && at2[iN2].chem_bonds_valence == 4 && - pVA[iC=at2[iN2].neighbor[at2[iN2].neighbor[0]==iN2]].cNumValenceElectrons == 4 ) { - - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( num_try = inchi_min( CurrEdges.num_edges, num_DB_O ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_DB_O && cur_success < num_try; i ++ ) { - iat = iat_DB_O[i]; - - peDB_O_Minus = pBNS->edge + (pVA[iat].nCMinusGroupEdge-1); - pe = pBNS->edge + pBNS->vert[iat].iedge[0]; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - peDB_O_Minus->forbidden &= forbidden_edge_mask_inv; - - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { - /* Added (-) charge to =O and removed from =N(-) => nDeltaCharge == 0 */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 22 */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - pe->forbidden &= forbidden_edge_mask_inv; - peDB_O_Minus->forbidden |= forbidden_edge_mask; - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - if ( pc2i->len_c2at && pc2i->nNumTgInChI == 1 ) { - /*------------------------------------------------------------------*/ - /* case 23: -NO2 are to be tautomeric but they are not AND */ - /* InChI has a SINGLE tautomeric group */ - /* */ - /* (-)O (-)O */ - /* Solution: convert \ \ */ - /* N-X=...-Z(-) => N(+)=X- ...=Z */ - /* // / */ - /* O (-)O */ - /* */ - /* O O */ - /* or \\ \\ */ - /* N-X=...-Z(-) => N=X- ...=Z */ - /* // / */ - /* O (-)O */ - /* */ - /* */ - /* (a) move (-) from other tautomeric atom to O in O=N-X */ - /* or from other atom that has to be tautomeric */ - /* but is not */ - /* (b) create (+) [ion pair creation] on N as in */ - /* */ - /* OH OH */ - /* / / */ - /* -C=N => =C-N(+) */ - /* \\ \\ */ - /* O O */ - /* */ - /*------------------------------------------------------------------*/ - int num_DB_O = 0, iat; - short iat_DB_O[MAX_DIFF_FIXH], iat_NO2[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - /* - inp_ATOM *atfMobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at_fixed_bonds)? - pStruct->pOne_norm_data[1]->at_fixed_bonds : NULL; - */ - S_CHAR *num_Fixed_H_Revrs = pStruct->pOneINChI[0]->nNum_H_fixed? pStruct->pOneINChI[0]->nNum_H_fixed : NULL; - S_CHAR *pnMobHRevrs = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNum_H)? - pStruct->pOneINChI[1]->nNum_H : - (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)? - pStruct->pOneINChI[0]->nNum_H : NULL; - int iN, one_success; - BNS_EDGE *peDB_O_Minus; - int neigh, nNumO, nNumOthers; -#define CHG_SET_NOOH 0 -#define CHG_SET_WRONG_TAUT 1 -#define CHG_SET_TAUT 2 -#define CHG_LAST_SET 2 /* the last index in trying */ -#define CHG_SET_O_FIXED 3 -#define CHG_SET_NUM 4 - EDGE_LIST ChangeableEdges[CHG_SET_NUM]; - memset( ChangeableEdges, 0, sizeof(ChangeableEdges) ); - /* equivalent to AllocEdgeList( &EdgeList, EDGE_LIST_CLEAR ); */ - /* - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - */ - CurrEdges.num_edges = 0; /* clear current edge list */ - cur_success = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( /* orig. InChI info: taut in orig. InChI =O located in -NO2 that is not taut in Reconstructed InChI */ - num_DB_O < MAX_DIFF_FIXH && - pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ && - (pc2i->c2at[i].endptInChI /*|| pc2i->c2at[i].nMobHInChI*/) && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pc2i->c2at[i].nFixHInChI == 0 && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ - /* reversed structure info: */ - !(pc2i->c2at[i].endptRevrs /*|| pc2i->c2at[i].nMobHRevrs*/) && - pc2i->c2at[i].nFixHRevrs == 0 && - pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && - at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 && - /* find whether it belongs to NO2 */ - pVA[iN=at2[iat].neighbor[0]].cNumValenceElectrons == 5 && - at2[iN].valence == 3 && (at2[iN].charge == 0 || at2[iN].charge == 1) && - at2[iN].chem_bonds_valence == 5 - at2[iN].charge ) { - /* find the second O */ - nNumO = nNumOthers = 0; - for ( k = 0; k < at2[iN].valence; k ++ ) { - neigh = at2[iN].neighbor[k]; - if ( neigh == iat ) { - continue; - } - if ( pVA[neigh].cNumValenceElectrons == 6 && - pStruct->endpoint[neigh] && - !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[neigh].endpoint) && - at2[neigh].valence == 1 && at2[neigh].num_H == 0 && - at2[neigh].radical == 0 && (at2[neigh].charge == 0 || at2[neigh].charge == -1) && - at2[neigh].chem_bonds_valence - at2[neigh].charge == 2) { - nNumO ++; - } else - if ( at2[iN].bond_type[k] == BOND_TYPE_SINGLE && - at2[neigh].valence > 1 && - at2[neigh].valence < at2[neigh].chem_bonds_valence ) { - nNumOthers ++; - } - } - if ( nNumO != 1 || nNumOthers != 1 ) { - continue; - } - for ( k = 0; k < num_DB_O; k ++ ) { - if ( iat_NO2[k] == iN ) { - break; - } - } - if ( k == num_DB_O ) { - iat_NO2[num_DB_O] = iN; - iat_DB_O[num_DB_O ++] = iat; - } - /* save the edge to avoid interference */ - if ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e, INC_ADD_EDGE ) ) { - goto exit_case_23; - } - } - } - if ( num_DB_O ) { - /* 1. search for =N(=O)-OH; assume =N(+)(-O(-))(-OH) does not happen */ - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - /* find O=N(V) */ - iat = nCanon2AtnoRevrs[i]; - if ( !pStruct->endpoint[i] || pVA[i].cNumValenceElectrons != 6 || - at2[iat].valence != 1 || at2[iat].charge || - 0 > (e = pVA[iat].nCMinusGroupEdge-1) || - at2[iat].num_H + at2[iat].chem_bonds_valence != 2 || - pVA[iN=at2[iat].neighbor[0]].cNumValenceElectrons != 5 || - 0 > (e = pVA[iN].nCPlusGroupEdge-1) || - pBNS->edge[e].forbidden || !pBNS->edge[e].flow || - at2[iN].charge || at2[iN].valence != 3 || at2[iN].chem_bonds_valence != 5) { - continue; - } - /* find the second O, -OH */ - nNumO = nNumOthers = 0; - for ( k = 0; k < at2[iN].valence; k ++ ) { - neigh = at2[iN].neighbor[k]; - if ( neigh == iat ) { - continue; - } - if ( pVA[neigh].cNumValenceElectrons == 6 && - pStruct->endpoint[neigh] && - at2[neigh].valence == 1 && at2[neigh].num_H == 1 && - at2[neigh].radical == 0 && (at2[neigh].charge == 0 ) ) { - nNumO ++; - } else - if ( at2[iN].bond_type[k] == BOND_TYPE_DOUBLE && - at2[neigh].valence >= 2 && - at2[neigh].valence < at2[neigh].chem_bonds_valence ) { - nNumOthers ++; - } - } - if ( nNumO != 1 || nNumOthers != 1 ) { - continue; - } - /* save edges to be changed */ - if ( (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NOOH], e, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e, INC_ADD_EDGE ))) { - goto exit_case_23; - } - if ( NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e )) && - (( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NOOH], j, INC_ADD_EDGE ) ) || - ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e, INC_ADD_EDGE ) ))) { - goto exit_case_23; - } - } - /* 2. search for (-) atoms that are tautomeric but should not be */ - /* or that got H from Normalization but they shouldn't */ - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( at2[iat].charge == -1 && - !pStruct->endpoint[i] && - (at_Mobile_H_Revrs && - (at_Mobile_H_Revrs[i].endpoint || at2[iat].num_H < at_Mobile_H_Revrs[i].num_H )) ) { - - if ( 0 <= (e = pVA[iat].nCMinusGroupEdge-1) && - 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e ) && - !pBNS->edge[e].forbidden && pBNS->edge[e].flow && - ( - ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_WRONG_TAUT], e, INC_ADD_EDGE ) ) || - ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e, INC_ADD_EDGE ) ) - ) ) { - goto exit_case_23; - } - } else - /* negatively charged atom in Reconstructed structure got H(+) from Normalization */ - /* and is not tautomeric; in the original structure it is tautomeric */ - if ( at2[iat].charge == -1 && - pStruct->endpoint[i] && - !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[i].endpoint) && - (num_Fixed_H_Revrs && num_Fixed_H_Revrs[i] == -1) && - (pnMobHRevrs && pnMobHRevrs[i] == 1) && - pStruct->fixed_H[i] == 0 ) { - - if ( 0 <= (e = pVA[iat].nCMinusGroupEdge-1) && - 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e ) && - !pBNS->edge[e].forbidden && pBNS->edge[e].flow && - ( - ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_WRONG_TAUT], e, INC_ADD_EDGE ) ) || - ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e, INC_ADD_EDGE ) ) - ) ) { - goto exit_case_23; - } - } - } - /* 3. Search for (-) atoms that are tautomeric */ - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( pStruct->endpoint[i] && - (at_Mobile_H_Revrs && at_Mobile_H_Revrs[i].endpoint) && - at2[iat].charge == -1 - /*&& pVA[i].cNumValenceElectrons == 6*/ ) { - if ( 0 <= (e = pVA[iat].nCMinusGroupEdge-1) && - !pBNS->edge[e].forbidden && pBNS->edge[e].flow && - 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e ) && - ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_TAUT], e, INC_ADD_EDGE ) ) ) { - goto exit_case_23; - } - } - } - /* ------- finally, try to move charges from O=N --------------*/ - for ( i = 0; i < num_DB_O; i ++ ) { - int nDeltaChargeExpected; - one_success = 0; - delta = 1; - iat = iat_DB_O[i]; - peDB_O_Minus = pBNS->edge + (pVA[iat].nCMinusGroupEdge-1); - pe = pBNS->edge + pBNS->vert[iat].iedge[0]; - - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - for ( k = 0; !one_success && k <= CHG_LAST_SET; k ++ ) { - if ( !ChangeableEdges[k].num_edges ) { - continue; - } - nDeltaChargeExpected = (k==CHG_SET_NOOH)? 2 : 0; - - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &ChangeableEdges[k], forbidden_edge_mask ); - /* allow (-) charge to move to N=O */ - peDB_O_Minus->forbidden &= forbidden_edge_mask_inv; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && - nDeltaCharge == nDeltaChargeExpected ) { - /* Move (-) charge to =O and remove it an endpoint => nDeltaCharge == 0 */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - one_success ++; /* 23 */ - } - } - INCHI_HEAPCHK - } - cur_success += one_success; - - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - pe->forbidden &= forbidden_edge_mask_inv; - - if ( !one_success ) { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - } - } -exit_case_23: - for ( i = 0; i < CHG_SET_NUM; i ++ ) { - AllocEdgeList( &ChangeableEdges[i], EDGE_LIST_FREE ); - } - - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } -#undef CHG_SET_NOOH -#undef CHG_SET_WRONG_TAUT -#undef CHG_SET_TAUT -#undef CHG_LAST_SET -#undef CHG_SET_O_FIXED -#undef CHG_SET_NUM - } - - if ( pc2i->len_c2at && pc2i->nNumTgInChI == 1 ) { - /*------------------------------------------------------------------*/ - /* case 24: InChI norm. -N(-)-N(+)(IV) => -N=N(V) prevents tauto- */ - /* merism on -N(-)- in case of ADP */ - /* */ - /* Solution: convert N(V)=N- ...=X -> N(IV)(+)-N=...-X(-)*/ - /* N(IV)(+)-N(-)-...=X */ - /* */ - /* Orig InChI taut taut, 1 t-group only(ADP?) */ - /* Reconstructed struct non-taut possibly not taut */ - /* */ - /* Details: 1a. store next to N(V) (+)edge its flower edge */ - /* 1b. store next to N(-) edge NO_VERTEX */ - /* 2. Release (-) edges of other missing endpoints or */ - /* all endpoints if no other is missing */ - /* 3. Decrement flow on (+) edge */ - /* if flower edge is stored then expect DeltaCharge=2*/ - /* otherwise DeltaCharge = 0 */ - /*------------------------------------------------------------------*/ - int iat; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - inp_ATOM *atf = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at_fixed_bonds)? - pStruct->pOne_norm_data[1]->at_fixed_bonds : NULL; - int iN, one_success; - EdgeIndex ef, e1; - BNS_EDGE *pef; -#define CHG_SET_MISSED_TAUT 0 -#define CHG_SET_OTHER_TAUT_O 1 -#define CHG_SET_OTHER_TAUT_N 2 -#define CHG_LAST_SET 2 /* the last index in trying */ -#define CHG_SET_NN 3 -#define CHG_SET_AVOID 4 -#define CHG_SET_NUM 5 - EDGE_LIST ChangeableEdges[CHG_SET_NUM]; - memset( ChangeableEdges, 0, sizeof(ChangeableEdges) ); - /* equivalent to AllocEdgeList( &EdgeList, EDGE_LIST_CLEAR ); */ - /* - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - */ - CurrEdges.num_edges = 0; /* clear current edge list */ - cur_success = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( /* orig. InChI info: -N=N(V) */ - pc2i->c2at[i].nValElectr == 5 /* N or P */ && - (pc2i->c2at[i].endptInChI /* only N */) && - (e1=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e1].forbidden && - pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI == 0 && - /* reversed structure info: */ - !pc2i->c2at[i].endptRevrs && - pc2i->c2at[i].nFixHRevrs == 0 && - pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && - at2[iat].valence == 2 && at2[iat].chem_bonds_valence == 3 && - /* find whether -N= has =N(V) neighbor; Note: operator comma: (A,B) returns B */ - (iN = at2[iat].neighbor[at2[iat].bond_type[0] != BOND_TYPE_DOUBLE], - pVA[iN].cNumValenceElectrons == 5) && - at2[iN].chem_bonds_valence == 5 && - at2[iN].charge == 0 && !at2[iN].num_H && !at2[iN].radical && - 0 <= (e=pVA[iN].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && - 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e )) { - - ef = GetChargeFlowerUpperEdge( pBNS, pVA, e ); /* == NO_VERTEX if N(V) has 4 bonds */ - if ( (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NN], e, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NN], ef, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NN], 1, INC_ADD_EDGE )) || /* expected nDeltaCharge */ - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e1, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], ef, INC_ADD_EDGE ))) { - goto exit_case_24; - } - /* mark -N= so that (-) will not be moved to it */ - if ( 0 <= (e = pVA[iat].nCMinusGroupEdge) && !pBNS->edge[e].forbidden && - 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) && - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE ))) { - goto exit_case_24; - } - } else - if ( /* orig. InChI info: -N(-)N(IV)(+) */ - atf && - pc2i->c2at[i].nValElectr == 5 /* N or P */ && - pc2i->c2at[i].endptInChI /* only N */ && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI == 0 && - /* reversed structure info: */ - !pc2i->c2at[i].endptRevrs && - pc2i->c2at[i].nFixHRevrs == 0 && - pc2i->c2at[i].nAtChargeRevrs == -1 && !at2[iat].num_H && - at2[iat].valence == 2 && at2[iat].chem_bonds_valence == 2 && - atf[iat].valence == 2 && atf[iat].chem_bonds_valence == 3 && - /* find whether -N= has =N(V) neighbor; Note: operator comma: (A,B) returns B */ - (iN=atf[iat].neighbor[atf[iat].bond_type[0] != BOND_TYPE_DOUBLE], - pVA[iN].cNumValenceElectrons == 5) && - at2[iN].charge == 1 && /* double bond neighbor */ - at2[iN].chem_bonds_valence == 4 && - atf[iN].charge == 0 && - atf[iN].chem_bonds_valence == 5 && /* InChI normalization created N(V)=N- out of N(IV)(+)-N(-)- */ - !at2[iN].num_H && !at2[iN].radical && - 0 <= (e=pVA[iat].nCMinusGroupEdge-1) && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && - 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) ) { - /* save (-) edge */ - if ( (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NN], e, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NN], NO_VERTEX, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NN], 1, INC_ADD_EDGE )) || /* expected nDeltaCharge */ - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE ))) { - goto exit_case_24; - } - } - } - if ( !ChangeableEdges[CHG_SET_NN].num_edges ) { - goto exit_case_24; - } - /* Collect all relevant tautomeric atoms */ - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - if ( !pStruct->endpoint[i] ) { - continue; - } - iat = nCanon2AtnoRevrs[i]; - if ( at2[iat].charge || at2[iat].radical || at2[iat].valence == at2[iat].chem_bonds_valence ) { - continue; /* cannot be an acceptor of (-) */ - } - if ( 0 > (e=pVA[iat].nCMinusGroupEdge-1) || pBNS->edge[e].forbidden || pBNS->edge[e].flow ) { - continue; - } - if ( 0 <= FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) ) { - continue; /* has already been used */ - } - /* missing endpoint */ - if ( !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint) ) { - if ( 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) && ( - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_MISSED_TAUT], e, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE )))) { - goto exit_case_24; - } - } else - /* endpoint O */ - if ( pVA[iat].cNumValenceElectrons == 6 ) { - if ( 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) && ( - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_OTHER_TAUT_O], e, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE )))){ - goto exit_case_24; - } - } else - /* endpoint N */ - if ( pVA[iat].cNumValenceElectrons == 5 ) { - if ( 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) && ( - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_OTHER_TAUT_N], e, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE )))){ - goto exit_case_24; - } - } - } - /* ------- finally, try to move charges from -N(-)-N(+) or to N(V) --------------*/ - for ( i = 0; i < ChangeableEdges[CHG_SET_NN].num_edges; i += 3 ) { - int nDeltaChargeExpected; - one_success = 0; - delta = 1; - pe = pBNS->edge + ChangeableEdges[CHG_SET_NN].pnEdges[i]; - pef = (NO_VERTEX != ChangeableEdges[CHG_SET_NN].pnEdges[i+1])? - pBNS->edge + ChangeableEdges[CHG_SET_NN].pnEdges[i+1] : NULL; - nDeltaChargeExpected = ChangeableEdges[CHG_SET_NN].pnEdges[i+2]; - - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - for ( k = 0; !one_success && k <= CHG_LAST_SET; k ++ ) { - if ( !ChangeableEdges[k].num_edges ) { - continue; - } - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &ChangeableEdges[k], forbidden_edge_mask ); - /* allow change of N(V) flower edge */ - if ( pef ) { - pef->forbidden &= forbidden_edge_mask_inv; - } - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && - nDeltaCharge == nDeltaChargeExpected ) { - /* Move (-) charge to =O and remove it an endpoint => nDeltaCharge == 0 */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - one_success ++; /* 24 */ - } - } - INCHI_HEAPCHK - } - cur_success += one_success; - - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - - if ( !one_success ) { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - } -exit_case_24: - for ( i = 0; i < CHG_SET_NUM; i ++ ) { - AllocEdgeList( &ChangeableEdges[i], EDGE_LIST_FREE ); - } - - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } -#undef CHG_SET_NN -#undef CHG_SET_MISSED_TAUT -#undef CHG_SET_OTHER_TAUT_O -#undef CHG_SET_OTHER_TAUT_N -#undef CHG_LAST_SET -#undef CHG_SET_AVOID -#undef CHG_SET_NUM - } - - /* pStruct->nNumRemovedProtonsMobHInChI == pc2i->nNumRemHInChI */ - - if ( pc2i->len_c2at && pc2i->nNumTgInChI == 1 && - pc2i->nNumRemHRevrs > pc2i->nNumRemHInChI && 0 > pc2i->nNumRemHInChI && - (pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || - pc2i->nNumTgRevrs > pc2i->nNumTgInChI ) ) { - /*------------------------------------------------------------------*/ - /* case 25: Restored InChI does not have 2 or more added protons */ - /* possibly taut. endpoints are missing */ - /* has -N(-O(-))-O(-) group(s) */ - /* Original InChI has only one t-group */ - /* */ - /* Solution: convert -N(-O(-))-O(-) -> -N(+)(=O)-O(-) */ - /* and direct 2(-) to the missing taut atoms*/ - /* at first attempt try to move (-) to N only */ - /* */ - /*------------------------------------------------------------------*/ - int iat; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - AT_NUMB *nAtno2CanonRevrs = pStruct->nAtno2Canon[0]; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - /* - inp_ATOM *atf = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at_fixed_bonds)? - pStruct->pOne_norm_data[1]->at_fixed_bonds : NULL; - */ - int iN, neigh, one_success; - EdgeIndex e1, bFirst; - BNS_EDGE *pef; -#define CHG_SET_MISSED_TAUT_1 0 -#define CHG_SET_MISSED_TAUT_ALL 1 -#define CHG_SET_OTHER_TAUT_1 2 -#define CHG_SET_OTHER_TAUT_ALL 3 -#define CHG_LAST_SET 3 /* the last index in trying */ -#define CHG_SET_NO_IN_NO2M2 4 -#define CHG_SET_AVOID 5 -#define CHG_SET_NUM 6 - EDGE_LIST ChangeableEdges[CHG_SET_NUM]; - memset( ChangeableEdges, 0, sizeof(ChangeableEdges) ); - /* equivalent to AllocEdgeList( &EdgeList, EDGE_LIST_CLEAR ); */ - /* - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - */ - CurrEdges.num_edges = 0; /* clear current edge list */ - cur_success = 0; - /* find all -N(-O(-))-O(-) */ - for ( i = 0; i < pStruct->num_atoms; i ++ ) { - iat = nCanon2AtnoRevrs[i]; - if ( pStruct->endpoint[i] ) { - if ( 0 > (e=pVA[iat].nCMinusGroupEdge-1) || pBNS->edge[e].forbidden || - 0 <= FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) ) { - continue; - } - bFirst = ( pVA[iat].cNumValenceElectrons == 5 && pc2i->nNumTgInChI == 1 || - pVA[iat].cNumValenceElectrons == 6 && pc2i->nNumTgInChI != 1 ); - /* many or no t-groups -> try O only first */ - /* single t-group -> try only N first */ - if ( !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[i].endpoint) ) { - /* missed tautomeric endpoint */ - if ( bFirst && - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_MISSED_TAUT_1], e, INC_ADD_EDGE ))) { - goto exit_case_25; - } - if (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_MISSED_TAUT_ALL], e, INC_ADD_EDGE )) { - goto exit_case_25; - } - } - if ( bFirst && - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_OTHER_TAUT_1], e, INC_ADD_EDGE ))) { - goto exit_case_25; - } - if (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_OTHER_TAUT_ALL], e, INC_ADD_EDGE )) { - goto exit_case_25; - } - if (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE )) { - goto exit_case_25; - } - } else - if ( at2[iat].valence == 1 && at2[iat].charge == -1 && - pVA[iat].cNumValenceElectrons == 6 && - pVA[iN=at2[iat].neighbor[0]].cNumValenceElectrons == 5 && /* -O(-) */ - !pStruct->endpoint[nAtno2CanonRevrs[iN]] && - at2[iN].valence == 3 && at2[iN].chem_bonds_valence == 3 && - !at2[iN].charge && !at2[iN].radical && - 0 <= (e=pVA[iN].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && - pBNS->edge[e].flow && /* NPlus edge */ - 0 <= (e1 = pVA[iat].nCMinusGroupEdge-1) && !pBNS->edge[e1].forbidden && - pBNS->edge[e1].flow && /* OMinus edge */ - 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) && - 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e1 )) { - /* found >N-O(-) */ - int nNumO = 0, nNumOthers = 0; - for ( k = 0; k < at2[iN].valence; k ++ ) { - neigh = at2[iN].neighbor[k]; - if ( neigh == iat ) { - continue; - } - if ( pVA[neigh].cNumValenceElectrons == 6 && - !pStruct->endpoint[neigh] && - at2[neigh].valence == 1 && at2[neigh].num_H == 0 && - at2[neigh].radical == 0 && at2[neigh].charge == -1 && - at2[neigh].chem_bonds_valence == 1 ) { - nNumO ++; - } else - if ( at2[iN].bond_type[k] == BOND_TYPE_SINGLE && - at2[neigh].valence > 1 && - at2[neigh].valence < at2[neigh].chem_bonds_valence ) { - nNumOthers ++; - } - } - if ( nNumO != 1 && nNumOthers != 1 ) { - continue; - } - /* save charge edges: NPlus first, OMinus second */ - if ( (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NO_IN_NO2M2], e, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NO_IN_NO2M2], e1, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e1, INC_ADD_EDGE ))) { - goto exit_case_25; - } - } - } - if ( !ChangeableEdges[CHG_SET_NO_IN_NO2M2].num_edges || - !ChangeableEdges[CHG_SET_OTHER_TAUT_ALL].num_edges ) { - goto exit_case_25; - } - /* ------- finally, try to move charges from -NO2(2-) or to tautomeric endpoints ----*/ - for ( i = 0; i < ChangeableEdges[CHG_SET_NO_IN_NO2M2].num_edges; i += 2 ) { - int nDeltaChargeExpected = 3; - /* change flow on O(-) to make it neutral; 3 new charges will be created: - N(+), and two (-) on InChI endpoints - alternatively, if we change flow on N to make N(+) then O(-) will - be nutralized (-1 charge) and two (-) charges on taut. endpoints will be - created (+2); the total change in this case would be (-1)+(+2) = +1 - */ - one_success = 0; - delta = 1; - pe = pBNS->edge + ChangeableEdges[CHG_SET_NO_IN_NO2M2].pnEdges[i+1]; /* O(-) edge */ - pef = pBNS->edge + ChangeableEdges[CHG_SET_NO_IN_NO2M2].pnEdges[i]; /* >N- (+) edge */ - - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - for ( k = 0; !one_success && k <= CHG_LAST_SET; k ++ ) { - if ( !ChangeableEdges[k].num_edges ) { - continue; - } - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &ChangeableEdges[k], forbidden_edge_mask ); - /* allow change of N(V) flower edge */ - pef->forbidden &= forbidden_edge_mask_inv; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && - nDeltaCharge == nDeltaChargeExpected ) { - /* Move (-) charge to =O and remove it an endpoint => nDeltaCharge == 0 */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - one_success ++; /* 24 */ - } - } - INCHI_HEAPCHK - } - cur_success += one_success; - - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - - if ( !one_success ) { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - } -exit_case_25: - for ( i = 0; i < CHG_SET_NUM; i ++ ) { - AllocEdgeList( &ChangeableEdges[i], EDGE_LIST_FREE ); - } - - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } -#undef CHG_SET_NN -#undef CHG_SET_MISSED_TAUT -#undef CHG_SET_OTHER_TAUT_O -#undef CHG_SET_OTHER_TAUT_N -#undef CHG_LAST_SET -#undef CHG_SET_AVOID -#undef CHG_SET_NUM - } - - -exit_function: - AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &CurrEdges, EDGE_LIST_FREE ); - AllocEdgeList( &NFlowerEdges, EDGE_LIST_FREE ); - AllocEdgeList( &SFlowerEdges, EDGE_LIST_FREE ); - AllocEdgeList( &OtherNFlowerEdges, EDGE_LIST_FREE ); - AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_FREE ); - AllocEdgeList( &AllBondEdges, EDGE_LIST_FREE ); - return ret < 0? ret : (pc2i->bHasDifference && tot_succes); -} -#endif diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichirvr7.c b/INCHI-1-SRC/INCHI_API/inchi_dll/ichirvr7.c deleted file mode 100644 index 42d71d1..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichirvr7.c +++ /dev/null @@ -1,2352 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include - -/*^^^ */ -/* #define CHECK_WIN32_VC_HEAP */ -#include "mode.h" - -#if ( READ_INCHI_STRING == 1 ) - -#include "ichicomp.h" -#include "ichi.h" -#include "ichitime.h" -#include "ichierr.h" -#include "util.h" -#include "strutil.h" - -/* reverse InChI */ -#include "ichimain.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichister.h" -#include "strutil.h" -#include "ichisize.h" -#include "ichiring.h" -#include "ichinorm.h" - -#include "ichirvrs.h" -#include "inchicmp.h" - -/******************************************************************************************************/ -int InChI2Atom( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, const char *szCurHdr, long num_inp, - StrFromINChI *pStruct, int iComponent, int iAtNoOffset, int bI2A_Flag, int bHasSomeFixedH, InpInChI *OneInput) -{ - int iINChI = (bI2A_Flag & I2A_FLAG_RECMET)? INCHI_REC : INCHI_BAS; - int bMobileH = (bI2A_Flag & I2A_FLAG_FIXEDH)? TAUT_NON : TAUT_YES; - INChI *pInChI[TAUT_NUM]; - int ret = 0; - - memset( pInChI, 0, sizeof(pInChI) ); - /* disconnected or reconnected */ - if ( iINChI == INCHI_REC ) { - if ( !OneInput->nNumComponents[iINChI][TAUT_YES] ) { - iINChI = INCHI_BAS; - } - } - if ( iComponent >= OneInput->nNumComponents[iINChI][TAUT_YES] ) { - return 0; /* component does not exist */ - } - /* mobile or fixed H */ - pStruct->bFixedHExists = 0; - if ( bMobileH == TAUT_NON ) { - if ( !OneInput->nNumComponents[iINChI][bMobileH] ) { - /* only one InChI exists (no mobile H) */ - bMobileH = TAUT_YES; - } - } - - if ( iComponent >= OneInput->nNumComponents[iINChI][bMobileH] ) { - return 0; /* component does not exist */ - } - /* pointer to the InChI that is going to be reversed */ - pInChI[0] = &OneInput->pInpInChI[iINChI][bMobileH][iComponent]; - pStruct->bMobileH = bMobileH; - pStruct->iINCHI = iINChI; - /* deleted component only in case Mobile-H and compound contains only protons */ - if ( pInChI[0]->bDeleted ) { - return 0; /* deleted component, presumably H(+) */ - } - - if ( bMobileH == TAUT_NON && OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons ) { - pStruct->nNumRemovedProtonsMobHInChI = - OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons[iComponent].nNumRemovedProtons; - } - - if ( bMobileH == TAUT_NON || bMobileH == TAUT_YES && OneInput->pInpInChI[iINChI][TAUT_NON] && - OneInput->pInpInChI[iINChI][TAUT_NON][iComponent].nNumberOfAtoms > 0 && - !OneInput->pInpInChI[iINChI][TAUT_NON][iComponent].bDeleted ) { - pStruct->bFixedHExists = 1; - } - if ( bMobileH == TAUT_NON && iComponent < OneInput->nNumComponents[iINChI][TAUT_YES] && - OneInput->pInpInChI[iINChI][TAUT_YES] && - OneInput->pInpInChI[iINChI][TAUT_YES][iComponent].nNumberOfAtoms > 0 && - !OneInput->pInpInChI[iINChI][TAUT_YES][iComponent].bDeleted ) { - /* pointer to the Mobile-H InChI if we are reversing Fixed-H InChI */ - pInChI[1] = &OneInput->pInpInChI[iINChI][TAUT_YES][iComponent]; - } - pStruct->num_inp_actual = OneInput->num_inp; - ret = OneInChI2Atom( ip, sd, szCurHdr, num_inp, pStruct, iComponent, iAtNoOffset, bHasSomeFixedH, pInChI); - return ret; /* same interpretation as in ProcessOneStructure ??? */ -} - -/*******************************************************************/ -void RemoveFixHInChIIdentical2MobH( InpInChI *pOneInput ) -{ - int iInchiRec, cur_num_comp, k; - /* eliminate Fixed-H InChI that are exactly came as the corresponding Mobile-H structures */ - for ( iInchiRec = 0; iInchiRec < INCHI_NUM; iInchiRec ++ ) { - cur_num_comp = inchi_min(pOneInput->nNumComponents[iInchiRec][TAUT_YES], - pOneInput->nNumComponents[iInchiRec][TAUT_NON]); - for ( k = 0; k < cur_num_comp; k ++ ) { - if ( !CompareReversedINChI( pOneInput->pInpInChI[iInchiRec][TAUT_YES]+k, - pOneInput->pInpInChI[iInchiRec][TAUT_NON]+k, NULL, NULL ) ) { - Free_INChI_Members( pOneInput->pInpInChI[iInchiRec][TAUT_NON]+k ); - memset( pOneInput->pInpInChI[iInchiRec][TAUT_NON]+k, 0, sizeof(pOneInput->pInpInChI[0][0][0]) ); - } - } - } -} -/*******************************************************************/ -int MarkDisconectedIdenticalToReconnected ( InpInChI *pOneInput ) -{ - /* mark Disconnected InChI components that are exactly came as Reconnected ones */ - /* Disconnected will have a negative number of the reconnected component */ - /* Reconnected will have a positive number of the disconnected component */ - int k1, k2, num_marked = 0; - for ( k1 = 0; k1 < inchi_max(pOneInput->nNumComponents[INCHI_BAS][TAUT_YES], - pOneInput->nNumComponents[INCHI_BAS][TAUT_NON]); k1 ++ ) { - for ( k2 = 0; k2 < inchi_max(pOneInput->nNumComponents[INCHI_REC][TAUT_YES], - pOneInput->nNumComponents[INCHI_REC][TAUT_NON]); k2 ++ ) { - int eqM = ( k1 < pOneInput->nNumComponents[INCHI_BAS][TAUT_YES] && - k2 < pOneInput->nNumComponents[INCHI_REC][TAUT_YES] && - !pOneInput->pInpInChI[INCHI_REC][TAUT_YES][k2].nLink && /* already linked */ - !pOneInput->pInpInChI[INCHI_BAS][TAUT_YES][k1].bDeleted && - pOneInput->pInpInChI[INCHI_BAS][TAUT_YES][k1].nNumberOfAtoms && - pOneInput->pInpInChI[INCHI_BAS][TAUT_YES][k1].nNumberOfAtoms == - pOneInput->pInpInChI[INCHI_REC][TAUT_YES][k2].nNumberOfAtoms && - !pOneInput->pInpInChI[INCHI_REC][TAUT_YES][k2].bDeleted && - !CompareReversedINChI( pOneInput->pInpInChI[INCHI_REC][TAUT_YES]+k2, - pOneInput->pInpInChI[INCHI_BAS][TAUT_YES]+k1, - NULL, NULL )); - int isF1 = (k1 < pOneInput->nNumComponents[INCHI_BAS][TAUT_NON] && - 0 == pOneInput->pInpInChI[INCHI_BAS][TAUT_NON][k1].bDeleted && - 0 < pOneInput->pInpInChI[INCHI_BAS][TAUT_NON][k1].nNumberOfAtoms ); - int isF2 = (k2 < pOneInput->nNumComponents[INCHI_REC][TAUT_NON] && - 0 == pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].bDeleted && - 0 < pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].nNumberOfAtoms ); - int eqF = isF1 && isF2 && - !pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].nLink && - pOneInput->pInpInChI[INCHI_BAS][TAUT_NON][k1].nNumberOfAtoms == - pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].nNumberOfAtoms && - !CompareReversedINChI( pOneInput->pInpInChI[INCHI_REC][TAUT_NON]+k2, - pOneInput->pInpInChI[INCHI_BAS][TAUT_NON]+k1, - NULL, NULL ); - if ( eqM && (!isF1 && !isF2 || eqF ) ) { - pOneInput->pInpInChI[INCHI_BAS][TAUT_YES][k1].nLink = -(k2+1); - pOneInput->pInpInChI[INCHI_REC][TAUT_YES][k2].nLink = (k1+1); - if ( eqF ) { - pOneInput->pInpInChI[INCHI_BAS][TAUT_NON][k1].nLink = -(k2+1); - pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].nLink = (k1+1); - } - num_marked ++; - break; /* equal InChI has been deleted from the disconnected layer, get next k1 */ - } - } - } - return num_marked; - -} -/**************************************************************/ -void SetUpSrm( SRM *pSrm ) -{ - /* structure restore parms !!!!! */ - memset( pSrm, 0, sizeof(pSrm[0]) ); - pSrm->bFixStereoBonds = FIX_STEREO_BOND_ORDER; - pSrm->nMetal2EndpointMinBondOrder = 1; - pSrm->nMetal2EndpointInitEdgeFlow = 0; - if ( METAL_FREE_CHARGE_VAL == 1 ) { - pSrm->bMetalAddFlower = 1; - /* the next 3 parameters: */ - /* 0, 0, 0 => all bonds 0, no init radical on metal */ - /* 0, 0, 1 => all bonds 0, init radical on metal */ - /* 0, 1, 0 => wrong */ - /* 0, 1, 1 => all bonds 1, no init radical on metal */ - /* 1, 0, 1 => min bond order 1, all bonds to metal have order 1 */ - /* 1, 1, 0 => wrong */ - /* 1, 1, 1 => wrong */ - pSrm->nMetalMinBondOrder = 0; - pSrm->nMetalInitEdgeFlow = 1; - pSrm->nMetalInitBondOrder = 1; - pSrm->bStereoRemovesMetalFlag = pSrm->bFixStereoBonds; - pSrm->nMetalFlowerParam_D = 16; - pSrm->nMetalMaxCharge_D = 16; - } else { - pSrm->bMetalAddFlower = 0; - pSrm->nMetalMinBondOrder = 1; - pSrm->nMetalInitEdgeFlow = 0; - pSrm->nMetalInitBondOrder = 1; - pSrm->bStereoRemovesMetalFlag = pSrm->bFixStereoBonds; - pSrm->nMetalFlowerParam_D = 16; - pSrm->nMetalMaxCharge_D = 0; - } - /* - pSrm->nMetalInitBondOrder = pSrm->nMetalMinBondOrder - + pSrm->nMetalInitEdgeFlow; - */ - pSrm->nMetal2EndpointInitBondOrder = pSrm->nMetal2EndpointMinBondOrder - + pSrm->nMetal2EndpointInitEdgeFlow; - -} -/**************************************************************************************/ -int MergeStructureComponents( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, char *szCurHdr, - ICHICONST SRM *pSrm, int bReqNonTaut, StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], - InpInChI *pOneInput ) -{ - int iInchiRec, iMobileH, iAlternH, num_components, tot_just_atoms, tot_removed_H, tot_atoms, cur_nA, cur_nH; - int k, i, j, ret, iCurAtomOffs, iNxtAtomOffs, iCurDelHOffs, iNxtDelHOffs, len, len2, iShiftH, icomp; - int *nAtomOffs=NULL, *nDelHOffs=NULL; - StrFromINChI *pStruct1; - inp_ATOM *at=NULL, *a; - - ret = 0; - pOneInput->num_atoms = 0; - /* select highest detail level */ - if ( num_components = pOneInput->nNumComponents[INCHI_REC][TAUT_NON] ) { - iInchiRec = INCHI_REC; - iMobileH = TAUT_NON; - } else - if ( num_components = pOneInput->nNumComponents[INCHI_REC][TAUT_YES] ) { - iInchiRec = INCHI_REC; - iMobileH = TAUT_YES; - } else - if ( num_components = pOneInput->nNumComponents[INCHI_BAS][TAUT_NON] ) { - iInchiRec = INCHI_BAS; - iMobileH = TAUT_NON; - } else - if ( num_components = pOneInput->nNumComponents[INCHI_BAS][TAUT_YES] ) { - iInchiRec = INCHI_BAS; - iMobileH = TAUT_YES; - } else { - return 0; /* no components available */ - } - - nAtomOffs = (int*) inchi_malloc((num_components+1) * sizeof(nAtomOffs[0])); - nDelHOffs = (int*) inchi_malloc((num_components+1) * sizeof(nDelHOffs[0])); - if ( !nAtomOffs || !nDelHOffs ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - /* count number of atoms and removed H */ - tot_just_atoms = tot_removed_H = tot_atoms = 0; - iAlternH = (iMobileH==TAUT_NON && pOneInput->nNumComponents[iInchiRec][TAUT_YES])? TAUT_YES : -1; - nAtomOffs[0] = nDelHOffs[0] = 0; - for ( k = 0; k < num_components; k ++ ) { - pStruct1 = pStruct[iInchiRec][iMobileH][k].num_atoms? pStruct[iInchiRec][iMobileH]+k : - iAlternH>=0 && - pStruct[iInchiRec][iAlternH][k].num_atoms? pStruct[iInchiRec][iAlternH]+k : NULL; - if ( !pStruct1 || !pStruct1->at2 || !pStruct1->num_atoms || pStruct1->bDeleted ) { - cur_nA = cur_nH = 0; - } else { - cur_nA = pStruct1->num_atoms; - cur_nH = pStruct1->num_deleted_H; - } - nAtomOffs[k+1] = nAtomOffs[k] + cur_nA; - nDelHOffs[k+1] = nDelHOffs[k] + cur_nH; - } - tot_just_atoms = nAtomOffs[num_components]; - /* shift all H to the end */ - for ( k = 0; k <= num_components; k ++ ) { - nDelHOffs[k] += tot_just_atoms; - } - tot_atoms = nDelHOffs[num_components]; - - /* merge atoms together: 1. Allocate */ - if ( NULL == (at = (inp_ATOM *) inchi_malloc( (tot_atoms+1) * sizeof(at[0]) ) ) ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - if ( !tot_atoms ) { - ret = 0; - goto exit_function; /* empty structure */ - } - /* merge atoms together: 2. Copy */ - for ( k = 0; k < num_components; k ++ ) { - pStruct1 = pStruct[iInchiRec][iMobileH][k].num_atoms? pStruct[iInchiRec][iMobileH]+k : - iAlternH>=0 && - pStruct[iInchiRec][iAlternH][k].num_atoms? pStruct[iInchiRec][iAlternH]+k : NULL; - if ( len = nAtomOffs[k+1] - nAtomOffs[k] ) { - memcpy( at + nAtomOffs[k], pStruct1->at2, len * sizeof(at[0]) ); - if ( len2 = nDelHOffs[k+1] - nDelHOffs[k] ) { - memcpy( at + nDelHOffs[k], pStruct1->at2+len, len2 * sizeof(at[0]) ); - } - } - } - /* merge atoms together: 3. Update atom numbers */ - icomp = 0; - for ( k = 0; k < num_components; k ++ ) { - iCurAtomOffs = nAtomOffs[k]; - iNxtAtomOffs = nAtomOffs[k+1]; - iCurDelHOffs = nDelHOffs[k]; - iNxtDelHOffs = nDelHOffs[k+1]; - len = nAtomOffs[k+1] - nAtomOffs[k]; /* number of atoms in a component excluding explicit H */ - iShiftH = iCurDelHOffs - len; - if ( !len ) { - continue; - } - icomp ++; /* current component number */ - /* update atoms */ - for ( i = iCurAtomOffs; i < iNxtAtomOffs; i ++ ) { - - a = at+i; - - a->endpoint = 0; - a->bAmbiguousStereo = 0; - a->at_type = 0; - a->bCutVertex = 0; - a->bUsed0DParity = 0; - a->cFlags = 0; - a->nBlockSystem = 0; - a->nNumAtInRingSystem = 0; - a->nRingSystem = 0; - - for ( j = 0; j < a->valence; j ++ ) { - if ( a->neighbor[j] < len ) { - a->neighbor[j] += iCurAtomOffs; /* atom */ - } else { - a->neighbor[j] += iShiftH; /* explicit H */ - } - } - a->orig_at_number += iCurAtomOffs; - a->component = icomp; - if ( a->p_parity ) { - for ( j = 0; j < MAX_NUM_STEREO_ATOM_NEIGH; j ++ ) { - if ( a->p_orig_at_num[j] <= len ) { - /* originally, orig_at_num = atom_index+1, therefore <= instead of < */ - a->p_orig_at_num[j] += iCurAtomOffs; - } else { - a->p_orig_at_num[j] += iShiftH; - } - } - } - for ( j = 0; j < MAX_NUM_STEREO_BONDS && a->sb_parity[j]; j ++ ) { - if ( a->sn_orig_at_num[j] <= len ) { - /* originally, orig_at_num = atom_index+1, therefore <= instead of < */ - a->sn_orig_at_num[j] += iCurAtomOffs; - } else { - a->sn_orig_at_num[j] += iShiftH; - } - } - } - /* update fixed-H */ - for ( i = iCurDelHOffs; i < iNxtDelHOffs; i ++ ) { - a = at+i; - a->neighbor[0] += iCurAtomOffs; - a->orig_at_number += iShiftH; - - } - } - /* save the results */ - pOneInput->atom = at; - pOneInput->num_atoms = tot_atoms; - at = NULL; - -exit_function: - if ( at ) inchi_free( at ); /* in case of failure */ - if ( nAtomOffs ) inchi_free( nAtomOffs ); - if ( nDelHOffs ) inchi_free( nDelHOffs ); - return ret; -} -#ifndef COMPILE_ANSI_ONLY -static PER_DRAW_PARMS pdp; -/******************************************************************************************************/ -int DisplayAllRestoredComponents( inp_ATOM *at, int num_at, const char *szCurHdr ) -{ - int ret; - char szTitle[512]; - DRAW_PARMS dp; - TBL_DRAW_PARMS tdp; - if ( num_at <= 0 ) { - return 0; - } - memset( &dp, 0, sizeof(dp)); - memset( &tdp, 0, sizeof(tdp) ); - //memset( &pdp, 0, sizeof(pdp) ); - dp.sdp.tdp = &tdp; - dp.pdp = &pdp; - dp.sdp.nFontSize = -9; - sprintf( szTitle, "All Components of Restored %s Structure", szCurHdr? szCurHdr : "(No structure name)"); - ret = DisplayStructure( at, num_at, 0 /* nNumDeletedH*/, 0 /*bAdd_DT_to_num_H*/, - 0 /*nNumRemovedProtons*/, NULL /*NUM_H *nNumRemovedProtonsIsotopic*/, - 1 /*int bIsotopic*/, 0 /*bTautomeric*/, - NULL /* pINChI */, NULL /* INChI_Aux **cur_INChI_Aux*/, - 0 /*bAbcNumbers*/, &dp, 0 /*INCHI_MODE nMode*/, szTitle ); - return 0; -} -/******************************************************************************************************/ -int DisplayOneRestoredComponent( StrFromINChI *pStruct, inp_ATOM *at, - int iComponent, int nNumComponents, int bMobileH, - const char *szCurHdr ) -{ - int ret, k; - int num_at = pStruct->num_atoms; - XYZ_COORD *pxyz = pStruct->pXYZ; - char szTitle[512]; - DRAW_PARMS dp; - TBL_DRAW_PARMS tdp; - int iInchiRec = pStruct->iInchiRec; - int iMobileH = pStruct->iMobileH; - INChI **pInChI = NULL; - INChI_Aux **pAux = NULL; - int nNumRemovedProtons = pAux? pAux[iMobileH]->nNumRemovedProtons : 0; - NUM_H *nNumRemovedProtonsIsotopic = pAux? pAux[iMobileH]->nNumRemovedIsotopicH : NULL; - - - if ( num_at <= 0 || !pxyz ) { - return 0; - } - if ( iInchiRec && !pStruct->RevInChI.pINChI_Aux[iInchiRec][0] ) { - iInchiRec = 0; - } - k = iMobileH; - if ( !bRevInchiComponentExists( pStruct, iInchiRec, k, 0 ) ) { - k = ALT_TAUT(k); - } - pInChI = pStruct->RevInChI.pINChI[iInchiRec][0]; - pAux = pStruct->RevInChI.pINChI_Aux[iInchiRec][0]; - - - memset( &dp, 0, sizeof(dp)); - memset( &tdp, 0, sizeof(tdp) ); - //memset( &pdp, 0, sizeof(pdp) ); - dp.sdp.tdp = &tdp; - dp.pdp = &pdp; - dp.sdp.nFontSize = -9; - sprintf( szTitle, "Restored %s Component %d of %d %c%c", - szCurHdr? szCurHdr : "(No structure name)", iComponent+1, nNumComponents, - pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F' ); - ret = DisplayStructure( at, num_at, 0 /* nNumDeletedH*/, 0 /*bAdd_DT_to_num_H*/, - nNumRemovedProtons, /*NULL*/ nNumRemovedProtonsIsotopic, - 1 /*int bIsotopic*/, k, - pInChI, pAux, - 0 /*bAbcNumbers*/, &dp, 0 /*INCHI_MODE nMode*/, szTitle ); - return 0; -} -/******************************************************************************************************/ -int DisplayRestoredComponent( StrFromINChI *pStruct, int iComponent, int iAtNoOffset, INChI *pInChI, const char *szCurHdr ) -{ - int i, ret; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - inp_ATOM *atom = pStruct->at2; - XYZ_COORD *pxyz = pStruct->pXYZ; - inp_ATOM *at = NULL; - char szTitle[512]; - DRAW_PARMS dp; - TBL_DRAW_PARMS tdp; - if ( !atom || num_at <= 0 || !pxyz ) { - return 0; - } - at = (inp_ATOM *)inchi_calloc( num_at + num_deleted_H, sizeof(at[0]) ); - if ( !at ) { - return RI_ERR_ALLOC; - } - memcpy( at, atom, (num_at + num_deleted_H) * sizeof(at[0]) ); - for ( i = 0; i < num_at; i ++ ) { - at[i].x = pxyz[i].xyz[0]; - at[i].y = pxyz[i].xyz[1]; - at[i].z = pxyz[i].xyz[2]; - } - memset( &dp, 0, sizeof(dp)); - memset( &tdp, 0, sizeof(tdp) ); - //memset( &pdp, 0, sizeof(pdp) ); - dp.sdp.tdp = &tdp; - dp.pdp = &pdp; - dp.sdp.nFontSize = -9; - sprintf( szTitle, "DBG Restored %s Component %d %c%c", szCurHdr? szCurHdr : "(No structure name)", iComponent+1, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F' ); - ret = DisplayStructure( at, num_at, 0 /* nNumDeletedH*/, 0 /*bAdd_DT_to_num_H*/, - 0 /*nNumRemovedProtons*/, NULL /*NUM_H *nNumRemovedProtonsIsotopic*/, - 1 /*int bIsotopic*/, 0 /*bTautomeric*/, - &pInChI, NULL /* INChI_Aux **cur_INChI_Aux*/, - 0 /*bAbcNumbers*/, &dp, 0 /*INCHI_MODE nMode*/, szTitle ); - inchi_free( at ); - return 0; -} -/**************************************************************************************/ -int DisplayStructureComponents( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, char *szCurHdr, - ICHICONST SRM *pSrm, int bReqNonTaut, StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], - InpInChI *pOneInput ) -{ - int iInchiRec, iMobileH, iCurMobH, iAlternH, num_components, tot_just_atoms, tot_removed_H, tot_atoms, cur_nA, cur_nH; - int k, i, j, ret, iCurAtomOffs, iNxtAtomOffs, iCurDelHOffs, iNxtDelHOffs, len, len2, iShiftH, icomp; - int *nAtomOffs=NULL, *nDelHOffs=NULL, bNoCoord=0, iNewCoord=0, nNewCoord=0; - double x_max=-1.0e16, x_min = 1.0e16, y_max=-1.0e16, y_min=1.0e16, delta = 0.0; - StrFromINChI *pStruct1; - inp_ATOM *at=NULL, *a; - - if (!ip->bDisplayCompositeResults && !ip->bDisplay ) { - return 0; - } - - ret = 0; - pOneInput->num_atoms = 0; - /* select highest detail level */ - if ( num_components = pOneInput->nNumComponents[INCHI_REC][TAUT_NON] ) { - iInchiRec = INCHI_REC; - iMobileH = TAUT_NON; - } else - if ( num_components = pOneInput->nNumComponents[INCHI_REC][TAUT_YES] ) { - iInchiRec = INCHI_REC; - iMobileH = TAUT_YES; - } else - if ( num_components = pOneInput->nNumComponents[INCHI_BAS][TAUT_NON] ) { - iInchiRec = INCHI_BAS; - iMobileH = TAUT_NON; - } else - if ( num_components = pOneInput->nNumComponents[INCHI_BAS][TAUT_YES] ) { - iInchiRec = INCHI_BAS; - iMobileH = TAUT_YES; - } else { - return 0; /* no components available */ - } - for ( k = 0; k < num_components; k ++ ) { - if ( pStruct[iInchiRec][iMobileH][k].bDeleted ) - break; - } - num_components = k; - - nAtomOffs = (int*) inchi_malloc((num_components+1) * sizeof(nAtomOffs[0])); - nDelHOffs = (int*) inchi_malloc((num_components+1) * sizeof(nDelHOffs[0])); - if ( !nAtomOffs || !nDelHOffs ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - /* count number of atoms and removed H */ - tot_just_atoms = tot_removed_H = tot_atoms = 0; - iAlternH = (iMobileH==TAUT_NON && pOneInput->nNumComponents[iInchiRec][TAUT_YES])? TAUT_YES : -1; - nAtomOffs[0] = nDelHOffs[0] = 0; - for ( k = 0; k < num_components; k ++ ) { - pStruct1 = pStruct[iInchiRec][iMobileH][k].num_atoms? pStruct[iInchiRec][iMobileH]+k : - iAlternH>=0 && - pStruct[iInchiRec][iAlternH][k].num_atoms? pStruct[iInchiRec][iAlternH]+k : NULL; - if ( !pStruct1 || !pStruct1->at2 || !pStruct1->num_atoms ) { - cur_nA = cur_nH = 0; - } else { - cur_nA = pStruct1->num_atoms; - cur_nH = pStruct1->num_deleted_H; - if ( cur_nA && !pStruct1->pXYZ ) { - if ( !k ) { - ret = 0; /* no coordinates available */ - goto exit_function; - } else { - bNoCoord ++; - } - } - } - nAtomOffs[k+1] = nAtomOffs[k] + cur_nA; - nDelHOffs[k+1] = nDelHOffs[k] + cur_nH; - } - tot_just_atoms = nAtomOffs[num_components]; - /* shift all H to the end */ - for ( k = 0; k <= num_components; k ++ ) { - nDelHOffs[k] += tot_just_atoms; - } - tot_atoms = nDelHOffs[num_components]; - - /* merge atoms together: 1. Allocate */ - if ( NULL == (at = (inp_ATOM *) inchi_malloc( (tot_atoms+1) * sizeof(at[0]) ) ) ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - if ( !tot_atoms ) { - ret = 0; - goto exit_function; /* empty structure */ - } - /* merge atoms together: 2. Copy */ - for ( k = 0; k < num_components; k ++ ) { - pStruct1 = pStruct[iInchiRec][iMobileH][k].num_atoms? pStruct[iInchiRec][iCurMobH=iMobileH]+k : - iAlternH>=0 && - pStruct[iInchiRec][iAlternH][k].num_atoms? pStruct[iInchiRec][iCurMobH=iAlternH]+k : NULL; - if ( len = nAtomOffs[k+1] - nAtomOffs[k] ) { - XYZ_COORD *pxyz = pStruct1->pXYZ; - len2 = nDelHOffs[k+1] - nDelHOffs[k]; /* do not separate H from the atom: we will not need them */ - iCurAtomOffs = nAtomOffs[k]; - a = at + iCurAtomOffs; - memcpy( a, pStruct1->at2, (len+len2) * sizeof(at[0]) ); - DisconnectedConnectedH( a, len, len2 ); - if ( pxyz ) { - for ( i = 0; i < len; i ++ ) { - a[i].x = pxyz[i].xyz[0]; - x_max = inchi_max( x_max, pxyz[i].xyz[0] ); - x_min = inchi_min( x_min, pxyz[i].xyz[0] ); - a[i].y = pxyz[i].xyz[1]; - y_max = inchi_max( y_max, pxyz[i].xyz[1] ); - y_min = inchi_min( y_min, pxyz[i].xyz[1] ); - a[i].z = pxyz[i].xyz[2]; - nNewCoord ++; - } - } else { - if ( !iNewCoord ) { - if ( !nNewCoord ) { - ret = 0; - goto exit_function; /* empty structure */ - } - delta = inchi_max(x_max - x_min, y_max - y_min); - if ( delta == 0.0 ) { - delta = 0.5 * (x_max+x_min); - if ( delta == 0.0 ) - delta = 1.0; - } else { - delta /= sqrt( (double)(nNewCoord+1) ); - } - } - for ( i = 0; i < len; i ++ ) { - a[i].x = x_max + delta; - a[i].y = y_max - iNewCoord * delta; - a[i].z = 0.0; - iNewCoord ++; - } - if ( pStruct1->pXYZ = (XYZ_COORD *)inchi_calloc(len, sizeof(pStruct1->pXYZ[0]) ) ) { - - for ( i = 0; i < len; i ++ ) { - pStruct1->pXYZ[i].xyz[0] = a[i].x; - pStruct1->pXYZ[i].xyz[1] = a[i].y; - pStruct1->pXYZ[i].xyz[2] = 0.0; - } - } - } - if ( ip->bDisplay || ip->bDisplayCompositeResults && 1 == num_components ) { - DisplayOneRestoredComponent( pStruct1, a, k, num_components, iCurMobH, szCurHdr ); - } - if ( !pxyz && pStruct1->pXYZ ) { - inchi_free( pStruct1->pXYZ ); - pStruct1->pXYZ = NULL; - } - } - } - /* merge atoms together: 3. Update atom numbers */ - icomp = 0; - if ( ip->bDisplayCompositeResults && num_components > 1 ) { - for ( k = 0; k < num_components; k ++ ) { - /* display each restored component if requested */ - iCurAtomOffs = nAtomOffs[k]; - iNxtAtomOffs = nAtomOffs[k+1]; - iCurDelHOffs = nDelHOffs[k]; - iNxtDelHOffs = nDelHOffs[k+1]; - len = nAtomOffs[k+1] - nAtomOffs[k]; /* number of atoms in a component excluding explicit H */ - iShiftH = iCurDelHOffs - len; - if ( !len ) { - continue; - } - icomp ++; /* current component number */ - /* update atoms */ - for ( i = iCurAtomOffs; i < iNxtAtomOffs; i ++ ) { - a = at+i; - for ( j = 0; j < a->valence; j ++ ) { - if ( a->neighbor[j] < len ) { - a->neighbor[j] += iCurAtomOffs; /* atom */ - } else { - ret = RI_ERR_PROGR; /* explicit H */ - goto exit_function; - } - } - a->orig_at_number += iCurAtomOffs; - } - } - tot_atoms = nAtomOffs[num_components]; - DisplayAllRestoredComponents( at, tot_atoms, szCurHdr ); - - } - -exit_function: - if ( at ) inchi_free( at ); /* in case of failure */ - if ( nAtomOffs ) inchi_free( nAtomOffs ); - if ( nDelHOffs ) inchi_free( nDelHOffs ); - return ret; -} -#endif -/**************************************************************************************/ -int AllInchiToStructure( ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd_inp, long num_inp, char *szCurHdr, - ICHICONST SRM *pSrm, int bHasSomeFixedH, StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], - InpInChI *pOneInput ) -{ - int iInchiRec, iMobileH, cur_num_comp, bCurI2A_Flag, k, ret, num_err; - INPUT_PARMS *ip, ip_loc; - STRUCT_DATA *sd, sd_loc; - long ulProcessingTime = 0; - inchiTime ulTStart; - - InchiTimeGet( &ulTStart ); - ip = &ip_loc; - *ip = *ip_inp; - sd = &sd_loc; - memset( sd, 0, sizeof(*sd)); - sd->ulStructTime = sd_inp->ulStructTime; - ret = 0; - num_err = 0; - for ( iInchiRec = 0; iInchiRec < INCHI_NUM; iInchiRec ++ ) { /* Disconnected/Connected */ - for ( iMobileH = 0; iMobileH < TAUT_NUM; iMobileH ++ ) { /* Mobile/Fixed H */ - cur_num_comp = pOneInput->nNumComponents[iInchiRec][iMobileH]; - if ( !cur_num_comp ) { - continue; - } - /* allocate memory for all existing components */ - pStruct[iInchiRec][iMobileH] = (StrFromINChI *)inchi_calloc( cur_num_comp, sizeof(pStruct[0][0][0])); - if ( !pStruct[iInchiRec][iMobileH] ) { - ret = RI_ERR_ALLOC; - goto exit_error; - } - /* set conversion mode */ - bCurI2A_Flag = (iMobileH? 0: I2A_FLAG_FIXEDH) | (iInchiRec? I2A_FLAG_RECMET : 0); - if ( iMobileH ) { - ip->nMode &= ~REQ_MODE_BASIC; - } else { - ip->nMode |= REQ_MODE_BASIC; - } - /* InChI --> structure conversion for all components except duplicated */ - for ( k = 0; k < cur_num_comp; k ++ ) { /* components */ - if ( !iMobileH && !pOneInput->pInpInChI[iInchiRec][iMobileH][k].nNumberOfAtoms || - pOneInput->pInpInChI[iInchiRec][iMobileH][k].bDeleted || - pOneInput->pInpInChI[iInchiRec][iMobileH][k].nLink < 0 ) { - - pStruct[iInchiRec][iMobileH][k].nLink = pOneInput->pInpInChI[iInchiRec][iMobileH][k].nLink; - pStruct[iInchiRec][iMobileH][k].bDeleted = pOneInput->pInpInChI[iInchiRec][iMobileH][k].bDeleted; - continue; /* do not create a structure out of an unavailable - Fixed-H InChI or out of the one present in Reconnected layer */ -#ifdef NEVER /* a wrong attempt to process deleted components here */ - if ( pStruct[iInchiRec][iMobileH][k].nLink = pOneInput->pInpInChI[iInchiRec][iMobileH][k].nLink ) { - continue; /* do not create a structure out of an unavailable - Fixed-H InChI or out of the one present in Reconnected layer */ - } else - if ( iMobileH && pOneInput->pInpInChI[iInchiRec][iMobileH][k].nNumberOfAtoms && - pOneInput->pInpInChI[iInchiRec][iMobileH][k].bDeleted && - pOneInput->pInpInChI[iInchiRec][iMobileH][0].bDeleted ) { - /* all components are protons */ - ; - } else { - continue; - } -#endif - } - if ( bHasSomeFixedH && iMobileH && k < pOneInput->nNumComponents[iInchiRec][TAUT_NON] && - pOneInput->pInpInChI[iInchiRec][TAUT_NON][k].nNumberOfAtoms ) { - continue; /* do not process Mobile-H if Fixed-H is requested and exists */ - } - pStruct[iInchiRec][iMobileH][k].pSrm = pSrm; - pStruct[iInchiRec][iMobileH][k].iInchiRec = iInchiRec; - pStruct[iInchiRec][iMobileH][k].iMobileH = iMobileH; - - /****************************************************/ - /* */ - /* Convert InChI of one component into a Structure */ - /* */ - /****************************************************/ - - ret = InChI2Atom( ip, sd, szCurHdr, num_inp, pStruct[iInchiRec][iMobileH]+k, k, - 0 /* AtNoOffset*/, bCurI2A_Flag, bHasSomeFixedH, pOneInput ); - pStruct[iInchiRec][iMobileH][k].nLink = pOneInput->pInpInChI[iInchiRec][iMobileH][k].nLink; - if ( ret < 0 ) { -#if ( bRELEASE_VERSION != 1 ) -#ifndef TARGET_API_LIB - /* !!! Conversion Error -- Ignore for now !!! */ - fprintf( stdout, "%ld %s Conversion failed: %d, %c%c comp %d\n", - num_inp, szCurHdr? szCurHdr : "Struct", ret, iInchiRec? 'R':'D', iMobileH? 'M':'F', k+1); -#endif -#endif - if ( ret == CT_USER_QUIT_ERR ) { - goto exit_error; - } - pStruct[iInchiRec][iMobileH][k].nError = ret; - ret = 0; /* force to ignore the errors for now !!!! */ - num_err ++; - } - } - } - } -exit_error: - ulProcessingTime += InchiTimeElapsed( &ulTStart ); - sd->ulStructTime += ulProcessingTime; - return ret<0? ret : num_err; -} -/**************************************************************************************/ -int AddProtonAndIsoHBalanceToMobHStruct( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, - long num_inp, int bHasSomeFixedH, char *szCurHdr, - StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], InpInChI *pOneInput) -{ - COMPONENT_REM_PROTONS nToBeRemovedByNormFromRevrs[INCHI_NUM]; - int nRemovedByNormFromRevrs[INCHI_NUM]; - int nRemovedByRevrs[INCHI_NUM]; - - int nDeltaFromDisconnected = 0, nRemovedProtonsByNormFromRevrs, nRemovedProtonsByRevrs, num_changes = 0; - NUM_H nIsoDeltaFromDisconnected[NUM_H_ISOTOPES]; - int iInchiRec, i, k, k1, ret = 0; - int nChargeInChI, nChargeRevrs; - - if ( bHasSomeFixedH ) { - return 0; /* 2005-03-01 */ - } - - /* num protons removed by InChI Normalization from the original structure */ - for ( i = 0; i < INCHI_NUM; i ++ ) { - nToBeRemovedByNormFromRevrs[i].nNumRemovedProtons = pOneInput->nNumProtons[i][TAUT_YES].nNumRemovedProtons; - for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) { - nToBeRemovedByNormFromRevrs[i].nNumRemovedIsotopicH[k] = pOneInput->nNumProtons[i][TAUT_YES].nNumRemovedIsotopicH[k]; - } - } - /* accumulate here num. protons removed by the normalization from the reversed structure */ - nRemovedByNormFromRevrs[INCHI_BAS] = - nRemovedByNormFromRevrs[INCHI_REC] = 0; - nRemovedByRevrs[INCHI_REC] = - nRemovedByRevrs[INCHI_BAS] = 0; - /* protons added/removed by InChI Normalization to/from Restored Structure might have been added by StructureRestore */ - for ( iInchiRec = 0; iInchiRec < INCHI_NUM; iInchiRec ++ ) { - for ( k = 0; k < pOneInput->nNumComponents[iInchiRec][TAUT_YES]; k ++ ) { - if ( !bInpInchiComponentExists( pOneInput, iInchiRec, TAUT_YES, k ) ) { - continue; - } - nRemovedProtonsByNormFromRevrs = 0; /* Num protons removed from the Restored Structure by InChI Normalization */ - nRemovedProtonsByRevrs = 0; /* Num protons removed by the Reconstruction from the Restored Structure */ - if ( iInchiRec == INCHI_REC || iInchiRec == INCHI_BAS && (k1=pStruct[iInchiRec][TAUT_YES][k].nLink) >= 0 ) { - - REV_INCHI *pRevInChI = &pStruct[iInchiRec][TAUT_YES][k].RevInChI; - INChI_Aux **pINChI_Aux2 = pRevInChI->pINChI_Aux[iInchiRec][0]; /* component 0*/ - INChI **pINChI_Revr = pRevInChI->pINChI[iInchiRec][0]; - INChI *pINChI_Orig = pOneInput->pInpInChI[iInchiRec][TAUT_YES]+k; - nChargeRevrs = pINChI_Revr? pINChI_Revr[TAUT_YES]->nTotalCharge : NO_VALUE_INT; - nChargeInChI = pINChI_Orig->nTotalCharge; - if ( pINChI_Aux2 ) { - nRemovedProtonsByNormFromRevrs = pINChI_Aux2[TAUT_YES]->nNumRemovedProtons; - } - nRemovedProtonsByRevrs = pStruct[iInchiRec][TAUT_YES][k].nNumRemovedProtonsByRevrs; - pStruct[iInchiRec][TAUT_YES][k].nChargeRevrs = nChargeRevrs; - pStruct[iInchiRec][TAUT_YES][k].nChargeInChI = nChargeInChI; - } else - if ( 0 <= ( k1 = -(1+pStruct[iInchiRec][TAUT_YES][k].nLink) ) ) { - REV_INCHI *pRevInChI = &pStruct[INCHI_REC][TAUT_YES][k1].RevInChI; - INChI_Aux **pINChI_Aux2 = pRevInChI->pINChI_Aux[INCHI_BAS][0]; /* component 0 */ - INChI **pINChI_Revr = pRevInChI->pINChI[INCHI_BAS][0]; - INChI *pINChI_Orig = pOneInput->pInpInChI[INCHI_REC][TAUT_YES]+k1; - nChargeRevrs = pINChI_Revr? pINChI_Revr[TAUT_YES]->nTotalCharge : NO_VALUE_INT; - nChargeInChI = pINChI_Orig->nTotalCharge; - if ( pINChI_Aux2 ) { - nRemovedProtonsByNormFromRevrs = pINChI_Aux2[TAUT_YES]->nNumRemovedProtons; - } - /* this component cannot be disconnected because it is same as in reconnected layer */ - nRemovedProtonsByRevrs = pStruct[INCHI_REC][TAUT_YES][k1].nNumRemovedProtonsByRevrs; - pStruct[iInchiRec][TAUT_YES][k1].nChargeRevrs = nChargeRevrs; - pStruct[iInchiRec][TAUT_YES][k1].nChargeInChI = nChargeInChI; - } - /* how many protons (to be removed by InChI Normalization) to add = - (proton balance in InChI} - - {number of protons known to be removed by InChI Normalization from Reconstructed structure} */ - nToBeRemovedByNormFromRevrs[iInchiRec].nNumRemovedProtons -= nRemovedProtonsByNormFromRevrs; - nRemovedByNormFromRevrs[iInchiRec] += nRemovedProtonsByNormFromRevrs; - nRemovedByRevrs[iInchiRec] += nRemovedProtonsByRevrs; - pStruct[iInchiRec][TAUT_YES][k].nRemovedProtonsByNormFromRevrs = nRemovedProtonsByNormFromRevrs; - } - } - - /* Since fixed-H layer is missing we need to add proton balance to the components */ - memset( nIsoDeltaFromDisconnected, 0, sizeof(nIsoDeltaFromDisconnected) ); - for ( iInchiRec = INCHI_REC; INCHI_BAS <= iInchiRec; iInchiRec -- ) { - /* - if ( !pOneInput->nNumComponents[iInchiRec][TAUT_NON] && - pOneInput->nNumComponents[iInchiRec][TAUT_YES] ) { - */ - int bHasRecMobH = (iInchiRec==INCHI_BAS && pOneInput->nNumComponents[INCHI_REC][TAUT_YES]); - /* bHasRecMobH means all components that could not be disconnected are in reconnected part */ - if ( iInchiRec==INCHI_BAS ) { - /* second pass: common structures have been changed */ - nToBeRemovedByNormFromRevrs[INCHI_BAS].nNumRemovedProtons += nDeltaFromDisconnected; - } - /* after proton removal InChI is recalculated */ - - ret = AddRemProtonsInRestrStruct( ip, sd, num_inp, bHasSomeFixedH, pStruct[iInchiRec][TAUT_YES], - pOneInput->nNumComponents[iInchiRec][TAUT_YES], - bHasRecMobH? pStruct[INCHI_REC][TAUT_YES] : NULL, - bHasRecMobH? pOneInput->nNumComponents[INCHI_REC][TAUT_YES]:0, - &nToBeRemovedByNormFromRevrs[iInchiRec].nNumRemovedProtons, - (iInchiRec==INCHI_REC)?&nDeltaFromDisconnected : NULL); - if ( ret < 0 ) { - goto exit_function; - } - num_changes += ret; - /* - } - */ - } - /* if fixed-H layer is missing then we need to add isotopic exchangeable proton balance to the components */ - for ( iInchiRec = INCHI_REC; INCHI_BAS <= iInchiRec; iInchiRec -- ) { - /* - if ( !pOneInput->nNumComponents[iInchiRec][TAUT_NON] && - pOneInput->nNumComponents[iInchiRec][TAUT_YES] ) { - */ - int bHasRecMobH = (iInchiRec==INCHI_BAS && pOneInput->nNumComponents[INCHI_REC][TAUT_YES]); - /* bHasRecMobH means all components that could not be disconnected are in reconnected part */ - if ( iInchiRec==INCHI_BAS ) { - /* second pass: common structures have been changed */ - for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) { - nToBeRemovedByNormFromRevrs[INCHI_BAS].nNumRemovedIsotopicH[k] += nIsoDeltaFromDisconnected[k]; - } - } - /* after proton removal InChI is recalculated */ - ret = AddRemIsoProtonsInRestrStruct( ip, sd, num_inp, bHasSomeFixedH, pStruct[iInchiRec][TAUT_YES], - pOneInput->nNumComponents[iInchiRec][TAUT_YES], - bHasRecMobH? pStruct[INCHI_REC][TAUT_YES] : NULL, - bHasRecMobH? pOneInput->nNumComponents[INCHI_REC][TAUT_YES]:0, - nToBeRemovedByNormFromRevrs[iInchiRec].nNumRemovedIsotopicH, - (iInchiRec==INCHI_REC)?nIsoDeltaFromDisconnected : NULL); - if ( ret < 0 ) { - goto exit_function; - } - num_changes += ret; - /* - } - */ - } - -exit_function: - return ret; -} - -/*************************************************************/ -void FreeStrFromINChI( StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], int nNumComponents[INCHI_NUM][TAUT_NUM] ) -{ - int iInchiRec, iMobileH, cur_num_comp, k, j; - StrFromINChI *pStruct1; - for ( iInchiRec = 0; iInchiRec < INCHI_NUM; iInchiRec ++ ) { - for ( iMobileH = 0; iMobileH < TAUT_NUM; iMobileH ++ ) { - cur_num_comp = nNumComponents[iInchiRec][iMobileH]; - if ( !cur_num_comp || !(pStruct1=pStruct[iInchiRec][iMobileH]) ) { - continue; - } - for ( k = 0; k < cur_num_comp; k ++ ) { - if ( pStruct1[k].at ) { - inchi_free(pStruct1[k].at); - } - if ( pStruct1[k].at2 ) { - inchi_free(pStruct1[k].at2); - } - if ( pStruct1[k].st ) { - inchi_free(pStruct1[k].st); - } - if ( pStruct1[k].pVA ) { - inchi_free(pStruct1[k].pVA); - } - /* - if ( pStruct1[k].ti.t_group ) { - inchi_free( pStruct1[k].ti.t_group ); - } - */ - if ( pStruct1[k].pXYZ ) { - inchi_free(pStruct1[k].pXYZ); - } - /*==== begin ====*/ - free_t_group_info( &pStruct1[k].ti ); - if ( pStruct1[k].endpoint ) { - inchi_free(pStruct1[k].endpoint); - } - if ( pStruct1[k].fixed_H ) { - inchi_free(pStruct1[k].fixed_H); - } - for ( j = 0; j < TAUT_NUM; j ++ ) { - if ( pStruct1[k].nAtno2Canon[j] ) - inchi_free( pStruct1[k].nAtno2Canon[j] ); - if ( pStruct1[k].nCanon2Atno[j] ) - inchi_free( pStruct1[k].nCanon2Atno[j] ); - } - /*===== end ======*/ - /* free INChI memory */ - FreeAllINChIArrays( pStruct1[k].RevInChI.pINChI, - pStruct1[k].RevInChI.pINChI_Aux, - pStruct1[k].RevInChI.num_components ); -#ifdef NEVER - /* don't do that: these are just pointers to OneInput structure members */ - Free_INChI( &pStruct1[k].pINChI ); - Free_INChI_Aux( &pStruct1[k].pINChI_Aux ); - if ( pStruct1[k].inp_norm_data ) { - FreeInpAtomData( pStruct1[k].inp_norm_data ); - inchi_free( pStruct1[k].inp_norm_data ); - } -#endif - } - inchi_free(pStruct[iInchiRec][iMobileH]); - pStruct[iInchiRec][iMobileH] = NULL; - } - } -} -/********************************************************************/ -void FreeInpInChI( InpInChI *pOneInput ) -{ - int iINChI, k, j; - for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) { - for ( j = 0; j < TAUT_NUM; j ++ ) { - if ( pOneInput->pInpInChI[iINChI][j] ) { - for ( k = 0; k < pOneInput->nNumComponents[iINChI][j]; k ++ ) { - Free_INChI_Members( &pOneInput->pInpInChI[iINChI][j][k] ); - } - inchi_free(pOneInput->pInpInChI[iINChI][j]); - pOneInput->pInpInChI[iINChI][j] = NULL; - } - if ( pOneInput->nNumProtons[iINChI][j].pNumProtons ) { - inchi_free( pOneInput->nNumProtons[iINChI][j].pNumProtons ); - pOneInput->nNumProtons[iINChI][j].pNumProtons = NULL; - } - } - } - if ( pOneInput->atom ) inchi_free(pOneInput->atom); - memset( pOneInput, 0, sizeof(*pOneInput) ); -} - -/***********************************************************************************************/ -int CompareAllOrigInchiToRevInChI(StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], InpInChI *pOneInput, int bReqNonTaut, - long num_inp, char *szCurHdr) -{ - int i, iInchiRec, iMobileH, iMobileHpStruct, num_components, iComponent, ret=0; - COMPONENT_REM_PROTONS nCurRemovedProtons, nNumRemovedProtons; - INChI *pInChI[TAUT_NUM]; - INCHI_MODE CompareInchiFlags[TAUT_NUM]; - memset( pOneInput->CompareInchiFlags[0], 0, sizeof(pOneInput->CompareInchiFlags[0]) ); - memset( &nNumRemovedProtons, 0, sizeof(nNumRemovedProtons) ); - - /* do we have reconnected InChI ?*/ - iInchiRec = INCHI_REC; - iMobileH = TAUT_NON; - if ( !pOneInput->nNumComponents[iInchiRec][TAUT_YES] && !pOneInput->nNumComponents[iInchiRec][TAUT_NON] ) { - iInchiRec = INCHI_BAS; - } - /* do we have Mobile or Fixed-H ? */ - if ( !pOneInput->nNumComponents[iInchiRec][TAUT_NON] || !bReqNonTaut ) { - iMobileH = TAUT_YES; /* index for pOneInput */ - } - /* if a restored structure has Fixed-H InChI then its mobile-H restored InChI is in Fixed-H pStruct */ - num_components = pOneInput->nNumComponents[iInchiRec][iMobileH]; - for ( iComponent = 0; iComponent < num_components; iComponent ++ ) { - int bMobileH = iMobileH; - pInChI[0] = pInChI[1] = NULL; - if ( pOneInput->pInpInChI[iInchiRec][bMobileH][iComponent].nNumberOfAtoms && - !pOneInput->pInpInChI[iInchiRec][bMobileH][iComponent].bDeleted ) { - /* the requested InChI layer exists */ - pInChI[0] = &pOneInput->pInpInChI[iInchiRec][bMobileH][iComponent]; - if ( bMobileH == TAUT_NON ) { - pInChI[1] = &pOneInput->pInpInChI[iInchiRec][TAUT_YES][iComponent]; - } - } else - if ( bMobileH == TAUT_NON && - pOneInput->pInpInChI[iInchiRec][TAUT_YES][iComponent].nNumberOfAtoms && - !pOneInput->pInpInChI[iInchiRec][TAUT_YES][iComponent].bDeleted ) { - /* the requested Fixed-H InChI layer does not exist; however, the Mobile-H does exist */ - bMobileH = TAUT_YES; /* only Mobile-H is available */ - pInChI[0] = &pOneInput->pInpInChI[iInchiRec][bMobileH][iComponent]; - } - memset( CompareInchiFlags, 0, sizeof(CompareInchiFlags) ); - memset( &nCurRemovedProtons, 0, sizeof(nCurRemovedProtons) ); - iMobileHpStruct = -#if ( bRELEASE_VERSION == 0 ) -#ifndef TARGET_API_LIB - /* legacy: reproduce old output */ - OldPrintCompareOneOrigInchiToRevInChI(pStruct[iInchiRec][bMobileH]+iComponent, pInChI, bMobileH, - iComponent, num_inp, szCurHdr); -#endif -#endif - /* one component comparison result bits */ - ret = CompareOneOrigInchiToRevInChI( pStruct[iInchiRec][bMobileH]+iComponent, pInChI, bMobileH, iComponent, - num_inp, szCurHdr, &nCurRemovedProtons, CompareInchiFlags); - if ( ret >= 0 ) { - /* no errors encountered -> accumulate removed protons from individual Mobile-H layers of components */ - nNumRemovedProtons.nNumRemovedProtons += nCurRemovedProtons.nNumRemovedProtons; - for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { - nNumRemovedProtons.nNumRemovedIsotopicH[i] += nCurRemovedProtons.nNumRemovedIsotopicH[i]; - } - /* accumulate compare bits */ - for ( i = 0; i < TAUT_NUM; i ++ ) { - pOneInput->CompareInchiFlags[0][i] |= CompareInchiFlags[i]; - } - } else { - goto exit_function; - } - } - if ( iMobileH == TAUT_YES ) { - if ( pOneInput->nNumProtons[iInchiRec][iMobileH].pNumProtons ) { - ret = RI_ERR_PROGR; /* in Mobile-H case proton balances are split between compoments */ - } else { - /* num removed protons in orig. InChI num removed protons in restored InChi */ - if ( nNumRemovedProtons.nNumRemovedProtons != pOneInput->nNumProtons[iInchiRec][iMobileH].nNumRemovedProtons ) { - /* restored structure InChI has less or more removed protons */ - pOneInput->CompareInchiFlags[0][TAUT_YES] |= INCHIDIFF_MOBH_PROTONS; -#if ( bRELEASE_VERSION == 0 ) - /* debug output only */ - { - int num_H_AddedByRevrs = pOneInput->nNumProtons[iInchiRec][iMobileH].nNumRemovedProtons - - nNumRemovedProtons.nNumRemovedProtons; - fprintf( stdout, "COMPARE_INCHI: %ld: %s %cM: Proton balance (Diff: %d, RevrsRem=%d)\n", - num_inp, szCurHdr? szCurHdr : "Struct", iInchiRec? 'R':'D', - pOneInput->nNumProtons[iInchiRec][iMobileH].nNumRemovedProtons,num_H_AddedByRevrs); - } -#endif - } - for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { - if ( nNumRemovedProtons.nNumRemovedIsotopicH[i] != pOneInput->nNumProtons[iInchiRec][TAUT_YES].nNumRemovedIsotopicH[i] ) { - pOneInput->CompareInchiFlags[0][TAUT_YES] |= INCHIDIFF_MOB_ISO_H; -#if ( bRELEASE_VERSION == 0 ) - /* debug output only */ - { - int num_H_AddedByRevrs = pOneInput->nNumProtons[iInchiRec][TAUT_YES].nNumRemovedIsotopicH[i] - - nNumRemovedProtons.nNumRemovedIsotopicH[i]; - fprintf( stdout, "COMPARE_INCHI: %ld: %s %cM: Iso Xchg %dH balance (Diff: %d, RevrsRem=%d)\n", - num_inp, szCurHdr? szCurHdr : "Struct", iInchiRec? 'R':'D', i+1, - pOneInput->nNumProtons[iInchiRec][TAUT_YES].nNumRemovedIsotopicH[i],num_H_AddedByRevrs); - } -#endif - } - } - } - } - -exit_function: - return ret; -} -/***********************************************************************************************/ -int CompareAllDisconnectedOrigInchiToRevInChI(StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], - InpInChI *pOneInput, int bHasSomeFixedH, - long num_inp, char *szCurHdr) -{ - int i, k, m, n, iInChI, iMobileH, bMobileH, ifk; - int num_components_D, num_components_R; - int nNumCompHaveSeparateProtons_D, nNumCompHaveSeparateProtons_R; - int num_fragments_D, num_fragments_R, num_fragments_DR, num_fragments, iComponent, ret; - int ifInChI, ifMobileH, bfMobileH, nLink; - COMPONENT_REM_PROTONS nNumRemovedProtons_D; /* removed from the disconnected layer of the Input InChI */ - COMPONENT_REM_PROTONS nNumRemovedProtons_D_all; /* if only totals are avalable */ - COMPONENT_REM_PROTONS nNumRemovedProtons_R; /* removed from disconnected layer of the reconstructed struct */ - COMPONENT_REM_PROTONS nNumRemovedProtons_R_all; - INCHI_MODE CompareInchiFlags[TAUT_NUM]; - StrFromINChI *pStruct1; - INChI_Aux *pINChI_Aux; - INCHI_SORT *pINChISort1 = NULL; /* from reversed structure */ - INCHI_SORT *pINChISort2 = NULL; /* original input InChI */ - int nNumNonTaut1=0, nNumNonTaut2=0; - - ret = 0; - memset( pOneInput->CompareInchiFlags[1], 0, sizeof(pOneInput->CompareInchiFlags[1]) ); - - /* count components that are not subject to disconnection */ - if ( !pOneInput->nNumComponents[INCHI_REC][TAUT_YES] && - !pOneInput->nNumComponents[INCHI_REC][TAUT_NON] ) { - return 0; /* nothing to do */ - } - - memset( &nNumRemovedProtons_D, 0, sizeof(nNumRemovedProtons_D) ); - memset( &nNumRemovedProtons_R, 0, sizeof(nNumRemovedProtons_R) ); - memset( &nNumRemovedProtons_D_all, 0, sizeof(nNumRemovedProtons_D_all) ); - memset( &nNumRemovedProtons_R_all, 0, sizeof(nNumRemovedProtons_R_all) ); - memset( CompareInchiFlags, 0, sizeof(CompareInchiFlags) ); - - num_components_D = inchi_max( pOneInput->nNumComponents[INCHI_BAS][TAUT_YES], - pOneInput->nNumComponents[INCHI_BAS][TAUT_NON] ); - num_components_R = inchi_max( pOneInput->nNumComponents[INCHI_REC][TAUT_YES], - pOneInput->nNumComponents[INCHI_REC][TAUT_NON] ); - /***********************************************************************************************/ - /* InpInChI: count fragments -- disconnected components that do not match reconnected */ - /* Accumulate removed H and isotopic H from ALL Fixed-H disconnected components except deleted */ - /* This segment collects info from the original InChI */ - /***********************************************************************************************/ - /*---- Original InChI ----*/ - num_fragments_D = 0; - iInChI = INCHI_BAS; - iMobileH = bHasSomeFixedH? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES; - nNumCompHaveSeparateProtons_D = 0; - - /* in case of Mobile-H components here are the proton totals from the original InChI disconn. layer */ - nNumRemovedProtons_D.nNumRemovedProtons = pOneInput->nNumProtons[iInChI][TAUT_YES].nNumRemovedProtons; - memcpy( nNumRemovedProtons_D.nNumRemovedIsotopicH, - pOneInput->nNumProtons[iInChI][TAUT_YES].nNumRemovedIsotopicH, - sizeof(nNumRemovedProtons_D.nNumRemovedIsotopicH) ); /* total for the disconnected layer */ - - for ( k = 0; k < num_components_D; k ++ ) { - bMobileH = iMobileH; - if ( !bInpInchiComponentExists( pOneInput, iInChI, bMobileH, k ) ) { - if ( bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) ) { - bMobileH = TAUT_YES; - } else { - continue; /* component is missing ??? */ - } - } - if ( 0 > (nLink = pOneInput->pInpInChI[iInChI][bMobileH][k].nLink) ) { - /* component in Disconnected layer is linked to the identical one in the Reconnected layer */ - if ( pOneInput->nNumProtons[INCHI_REC][TAUT_YES].pNumProtons ) { - nNumCompHaveSeparateProtons_D ++; - nLink = -(1+nLink); - nNumRemovedProtons_D.nNumRemovedProtons += pOneInput->nNumProtons[INCHI_REC][TAUT_YES].pNumProtons[nLink].nNumRemovedProtons; - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - nNumRemovedProtons_D.nNumRemovedIsotopicH[m] += pOneInput->nNumProtons[INCHI_REC][TAUT_YES].pNumProtons[nLink].nNumRemovedIsotopicH[m]; - } - } - continue; /* same as reconnected */ - } - /* component in the reconnected layer that was disconnected */ - nNumNonTaut2 += (bMobileH == TAUT_NON); - if ( pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons ) { - nNumCompHaveSeparateProtons_D ++; - nNumRemovedProtons_D.nNumRemovedProtons += pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons[k].nNumRemovedProtons; - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - nNumRemovedProtons_D.nNumRemovedIsotopicH[m] += pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH[m]; - } - } - num_fragments_D ++; /* number of disconnected fragments from original reconnected structure */ - } - /* in case of Mobile-H components here are the proton totals from the original InChI */ - /* - nNumRemovedProtons_D_all.nNumRemovedProtons = pOneInput->nNumProtons[iInChI][TAUT_YES].nNumRemovedProtons; - memcpy( nNumRemovedProtons_D_all.nNumRemovedIsotopicH, - pOneInput->nNumProtons[iInChI][TAUT_YES].nNumRemovedIsotopicH, - sizeof(nNumRemovedProtons_D_all.nNumRemovedIsotopicH) ); - - */ - /****************************************************************************************************/ - /* count fragments in reconstructed reconnected structure */ - /* accumulate removed H and isotopic H from ALL reconstructed reconnected components except deleted */ - /* This segment collects info from the reconstructed structure InChI */ - /****************************************************************************************************/ - /*---- InChI from the reconstructed reconnected structure ----*/ - num_fragments_R = 0; - iInChI = INCHI_REC; - iMobileH = bHasSomeFixedH? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES; - nNumCompHaveSeparateProtons_R = 0; - for ( k = 0; k < num_components_R; k ++ ) { - bMobileH = iMobileH; - if ( !bInpInchiComponentExists( pOneInput, iInChI, bMobileH, k ) ) { - if ( bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) ) { - bMobileH = TAUT_YES; - } else { - continue; /* component is missing ??? (Deleted proton in Mobile-H layer) */ - } - } - if ( 0 < pOneInput->pInpInChI[iInChI][bMobileH][k].nLink ) { - /* this reconstructed reconnected component was NOT DISCONNECTED */ - /* same component is in the disconnected layer, it has no metal atoms or is an isolated metal atom */ - pStruct1 = pStruct[iInChI][bMobileH]+k; - ifMobileH = TAUT_YES; /* Mobile-H Aux_Info contains number removed protons */ - ifInChI = INCHI_BAS; /* this component cannot be reconnected */ - ifk = 0; /* 0th component since it is InChI of a single component */ - /* The statement in the following line is *WRONG*, component number mixed with bMobileH: */ - /* in RevInchi, when only Mobile-H is present then its only non-NULL InChI has index 0==TAUT_NON */ - if ( bRevInchiComponentExists( pStruct1, ifInChI, ifMobileH, ifk ) ) { - /* count protons */ - pINChI_Aux = pStruct1->RevInChI.pINChI_Aux[ifInChI][ifk][ifMobileH]; - if ( pINChI_Aux ) { - nNumRemovedProtons_R.nNumRemovedProtons += pINChI_Aux->nNumRemovedProtons; - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - nNumRemovedProtons_R.nNumRemovedIsotopicH[m] += pINChI_Aux->nNumRemovedIsotopicH[m]; - } - } - } - nNumCompHaveSeparateProtons_R += bRevInchiComponentExists( pStruct1, ifInChI, ALT_TAUT(ifMobileH), ifk ); - continue; /* same as disconnected, has no metal atoms */ - } - /* this reconstructed reconnected component WAS DISCONNECTED; check its fragments */ - /* it does not have same component in the disconnected layer */ - pStruct1 = pStruct[iInChI][bMobileH]+k; - num_fragments = pStruct1->RevInChI.num_components[INCHI_BAS]; - ifInChI = INCHI_BAS; /* disconnected layer */ - ifMobileH = bHasSomeFixedH? TAUT_NON : TAUT_YES; - for ( ifk = 0; ifk < num_fragments; ifk ++ ) { - bfMobileH = ifMobileH; - if ( !bRevInchiComponentExists( pStruct1, ifInChI, bfMobileH, ifk ) ) { - if ( bRevInchiComponentExists( pStruct1, ifInChI, TAUT_YES, ifk ) ) { - bfMobileH = TAUT_YES; - } else { - continue; /* fragment does not exist ??? */ - } - } - nNumNonTaut1 += (bfMobileH == TAUT_NON); - nNumCompHaveSeparateProtons_R += (bfMobileH == TAUT_NON); - /* count protons from fragments made by metal disconnection */ - pINChI_Aux = pStruct1->RevInChI.pINChI_Aux[ifInChI][ifk][TAUT_YES]; - if ( pINChI_Aux ) { - nNumRemovedProtons_R.nNumRemovedProtons += pINChI_Aux->nNumRemovedProtons; - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - nNumRemovedProtons_R.nNumRemovedIsotopicH[m] += pINChI_Aux->nNumRemovedIsotopicH[m]; - } - } - num_fragments_R ++; /* number of disconnected fragments from reconstructed reconnected structure */ - } - } - /*---------------- special treatment of the last reconstructed component -----------------*/ - /*---------------- this may contain separate protons added by the reconstruction ---------*/ - k = num_components_R - 1; - pStruct1 = pStruct[iInChI][iMobileH]+k; - if ( iMobileH == TAUT_YES && !bHasSomeFixedH && - bInpInchiComponentDeleted( pOneInput, iInChI, iMobileH, k ) && - (num_fragments = pStruct1->RevInChI.num_components[INCHI_BAS]) ) { - - ifInChI = INCHI_BAS; /* disconnected layer */ - ifMobileH = TAUT_YES; - for ( ifk = 0; ifk < num_fragments; ifk ++ ) { - bfMobileH = ifMobileH; - if ( !bRevInchiComponentDeleted( pStruct1, ifInChI, bfMobileH, ifk ) ) { - continue; /* fragment does exist ??? Should not happen */ - } - /* - nNumNonTaut1 += (bfMobileH == TAUT_NON); - nNumCompHaveSeparateProtons_R += (bfMobileH == TAUT_NON); - */ - /* count protons from fragments made by metal disconnection */ - pINChI_Aux = pStruct1->RevInChI.pINChI_Aux[ifInChI][ifk][TAUT_YES]; - if ( pINChI_Aux ) { - nNumRemovedProtons_R.nNumRemovedProtons += pINChI_Aux->nNumRemovedProtons; - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - nNumRemovedProtons_R.nNumRemovedIsotopicH[m] += pINChI_Aux->nNumRemovedIsotopicH[m]; - } - } - /*num_fragments_R ++;*/ /* number of disconnected fragments from reconstructed reconnected structure */ - } - } - - - - num_fragments_DR = inchi_max( num_fragments_D, num_fragments_R ); - /* in case of correct reconstruction, num_fragments_D, num_fragments_R */ - - if ( !num_fragments_DR ) { - return 0; /* no component was disconnected */ - } - if ( num_fragments_D != num_fragments_R ) { - for ( i = 0; i < TAUT_NUM; i ++ ) { - if ( pOneInput->nNumComponents[INCHI_BAS][i] ) { - pOneInput->CompareInchiFlags[1][i] |= INCHIDIFF_PROBLEM; - } - } - return 1; /* severe error */ - } - - - pINChISort1 = (INCHI_SORT *)inchi_calloc(num_fragments_DR, sizeof(pINChISort1[0])); - pINChISort2 = (INCHI_SORT *)inchi_calloc(num_fragments_DR, sizeof(pINChISort2[0])); - if ( !pINChISort1 || !pINChISort2 ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - - /* accumulate original InChI of fragments -- disconnected components that do not match reconnected */ - iInChI = INCHI_BAS; - iMobileH = bHasSomeFixedH? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES; - for ( k = n = 0; k < num_components_D; k ++ ) { - bMobileH = iMobileH; - if ( !bInpInchiComponentExists( pOneInput, iInChI, bMobileH, k ) ) { - if ( bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) ) { - bMobileH = TAUT_YES; - } else { - continue; /* component is missing ??? (Deleted proton in Mobile-H layer) */ - } - } - if ( 0 > pOneInput->pInpInChI[iInChI][bMobileH][k].nLink ) { - continue; /* same as reconnected */ - } - /* the component exists in disconnected layer of the orig. InChI only: it is a fragment */ - pINChISort2[n].pINChI[bMobileH] = pOneInput->pInpInChI[iInChI][bMobileH] + k; - if ( bMobileH == TAUT_NON && - (bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) || - bInpInchiComponentDeleted( pOneInput, iInChI, TAUT_YES, k ) ) ) { - pINChISort2[n].pINChI[TAUT_YES] = pOneInput->pInpInChI[iInChI][TAUT_YES] + k; - } - /* the last sort key is a number of removed protons */ - pINChISort2[n].ord_number = pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons? - pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons[k].nNumRemovedProtons : 0; - pINChISort2[n].n1 = k; /* orig. InChI disconnected layer component number */ - pINChISort2[n].n2 = -1; /* no fragment index */ - n ++; - } - - /* accumulate fragments from the reconstructed structure */ - iInChI = INCHI_REC; - iMobileH = bHasSomeFixedH? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES; - for ( k = n = 0; k < num_components_R; k ++ ) { - bMobileH = iMobileH; - if ( !bInpInchiComponentExists( pOneInput, iInChI, bMobileH, k ) ) { - if ( bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) ) { - bMobileH = TAUT_YES; - } else { - continue; /* component is missing ??? (Deleted proton in Mobile-H layer) */ - } - } - /* the reconstructed structure */ - if ( 0 < pOneInput->pInpInChI[iInChI][bMobileH][k].nLink ) { - continue; /* same as disconnected, has no metal atoms */ - } - /* this reconstructed structure was disconnected */ - pStruct1 = pStruct[iInChI][bMobileH]+k; - num_fragments = pStruct1->RevInChI.num_components[INCHI_BAS]; - ifInChI = INCHI_BAS; - ifMobileH = bHasSomeFixedH? TAUT_NON : TAUT_YES; - for ( i = 0; i < num_fragments; i ++ ) { - bfMobileH = ifMobileH; - if ( !bRevInchiComponentExists( pStruct1, ifInChI, bfMobileH, i ) ) { - if ( bRevInchiComponentExists( pStruct1, ifInChI, TAUT_YES, i ) ) { - bfMobileH = TAUT_YES; - } else { - continue; /* component is missing ??? */ - } - } - pINChISort1[n].pINChI[bfMobileH] = pStruct1->RevInChI.pINChI[ifInChI][i][bfMobileH]; - if ( bfMobileH == TAUT_NON /*&& bRevInchiComponentExists( pStruct1, ifInChI, TAUT_YES, i )*/ ) { - pINChISort1[n].pINChI[TAUT_YES] = pStruct1->RevInChI.pINChI[ifInChI][i][TAUT_YES]; - /* remove Fixed-H InChI if is is identical to Mobile-H */ - /* do it exactly same way the identical components were removed from InpInChI */ - if ( !CompareReversedINChI( pINChISort1[n].pINChI[bfMobileH], - pINChISort1[n].pINChI[TAUT_YES], NULL, NULL ) ) { - pINChISort1[n].pINChI[bfMobileH] = NULL; /* remove Fixed-H layer */ - } else { - pINChISort1[n].ord_number = pStruct1->RevInChI.pINChI_Aux[ifInChI][i][TAUT_YES]->nNumRemovedProtons; - } - } - - pINChISort1[n].n1 = k; /* reconstructed reconnected structure component index */ - pINChISort1[n].n2 = i; /* index of a fragment made out of this component */ - n ++; - } - } - - /* sort fragment InChI before comparing them */ - qsort( pINChISort1, num_fragments_D, sizeof(pINChISort1[0]), CompINChITaut2 ); - qsort( pINChISort2, num_fragments_R, sizeof(pINChISort2[0]), CompINChITaut2 ); - - /* compare fragments -- components present in disconnected layer only */ - for ( iComponent = 0; iComponent < num_fragments_DR; iComponent ++ ) { - INChI *pInChI1[TAUT_NUM]; /* from reversed structure */ - INChI *pInChI2[TAUT_NUM]; /* original input InChI */ - for ( i = 0; i < TAUT_NUM; i ++ ) { - pInChI1[i] = pINChISort1[iComponent].pINChI[i]; - pInChI2[i] = pINChISort2[iComponent].pINChI[i]; - } - CompareTwoPairsOfInChI( pInChI1, pInChI2, !bHasSomeFixedH, CompareInchiFlags ); - } - - if ( /*nNumNonTaut1 && nNumNonTaut2 &&*/ bHasSomeFixedH ) { - if ( nNumCompHaveSeparateProtons_D || nNumCompHaveSeparateProtons_R ) { - /* for each component, compare number removed protons */ - /* comparison does not make sense if Disconnected Fixed-H layer is not present */ - for ( iComponent = 0; iComponent < num_fragments_DR; iComponent ++ ) { - NUM_H nNumRemovedIsotopicH1[NUM_H_ISOTOPES]; - NUM_H nNumRemovedIsotopicH2[NUM_H_ISOTOPES]; - - memset( nNumRemovedIsotopicH1, 0, sizeof(nNumRemovedIsotopicH1) ); - memset( nNumRemovedIsotopicH2, 0, sizeof(nNumRemovedIsotopicH2) ); - /* compare removed protons */ - if ( pINChISort1[iComponent].ord_number != pINChISort2[iComponent].ord_number ) { - CompareInchiFlags[TAUT_YES] |= INCHIDIFF_MOBH_PROTONS; /* diff number of removed protons */ - } - /* also compare removed isotopic atoms H */ - k = pINChISort2[iComponent].n1; /* input InChI, OneInput */ - if ( pOneInput->nNumProtons[INCHI_BAS][TAUT_YES].pNumProtons ) { - memcpy( nNumRemovedIsotopicH2, - pOneInput->nNumProtons[INCHI_BAS][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH, - sizeof( nNumRemovedIsotopicH2 ) ); - } - /* get fragments of reconstructed structure removed protons info */ - k = pINChISort1[iComponent].n1; /* restored component number */ - i = pINChISort1[iComponent].n2; /* subcomponent number */ - iInChI = INCHI_REC; - iMobileH = bHasSomeFixedH? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES; - bMobileH = iMobileH; - if ( !bInpInchiComponentExists( pOneInput, iInChI, bMobileH, k ) ) { - if ( bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) ) { - bMobileH = TAUT_YES; - } else { - goto compare_iso_H; - } - } - if ( pOneInput->pInpInChI[iInChI][bMobileH][k].nLink ) { - continue; - /* - ret = RI_ERR_PROGR; - goto exit_function; - */ - } - pStruct1 = pStruct[iInChI][bMobileH]+k; - num_fragments = pStruct1->RevInChI.num_components[INCHI_BAS]; - ifInChI = INCHI_BAS; - ifMobileH = bHasSomeFixedH? TAUT_NON : TAUT_YES; - if ( i < num_fragments ) { - bfMobileH = ifMobileH; - if ( !bRevInchiComponentExists( pStruct1, ifInChI, bfMobileH, i ) ) { - if ( bRevInchiComponentExists( pStruct1, ifInChI, TAUT_YES, i ) ) { - bfMobileH = TAUT_YES; - } else { - goto compare_iso_H; - } - } - memcpy( nNumRemovedIsotopicH1, - pStruct1->RevInChI.pINChI_Aux[ifInChI][i][TAUT_YES]->nNumRemovedIsotopicH, - sizeof( nNumRemovedIsotopicH1 ) ); - } -compare_iso_H: - if ( memcmp( nNumRemovedIsotopicH1, nNumRemovedIsotopicH2, sizeof( nNumRemovedIsotopicH1 ) ) ) { - CompareInchiFlags[TAUT_YES] |= INCHIDIFF_REM_ISO_H; - } - } - } - } else - /*if ( !nNumNonTaut1 && !nNumNonTaut2 || !bHasSomeFixedH )*/ { - /* compare totals for removed protons and isotopic H */ - if ( pOneInput->nNumProtons[INCHI_BAS][TAUT_YES].nNumRemovedProtons != - nNumRemovedProtons_R.nNumRemovedProtons ) { - CompareInchiFlags[TAUT_YES] |= INCHIDIFF_MOBH_PROTONS; - } - if ( memcmp( pOneInput->nNumProtons[INCHI_BAS][TAUT_YES].nNumRemovedIsotopicH, - nNumRemovedProtons_R.nNumRemovedIsotopicH, - sizeof( nNumRemovedProtons_R.nNumRemovedIsotopicH ) ) ) { - CompareInchiFlags[TAUT_YES] |= INCHIDIFF_REM_ISO_H; - } - } - - if ( !nNumNonTaut1 == !nNumNonTaut2 ) { - ; /* difference if(nNumNonTaut1 != nNumNonTaut2) will be caught in InChI comparison */ - } else - if ( nNumNonTaut1 ) { - /* reconstructed has Fixed-H while the original has not: extra Fixed-H layer */ - CompareInchiFlags[TAUT_YES] |= INCHIDIFF_WRONG_TAUT; - } else { - /* the original InChI has Fixed-H while the reconstructed one has not: missing Fixed-H layer */ - CompareInchiFlags[TAUT_YES] |= INCHIDIFF_NO_TAUT; - } - for ( i = 0; i < TAUT_NUM; i ++ ) { - pOneInput->CompareInchiFlags[1][i] |= CompareInchiFlags[i]; - } - - /* compare totals */ - if ( nNumRemovedProtons_R.nNumRemovedProtons != nNumRemovedProtons_D.nNumRemovedProtons ) { - CompareInchiFlags[TAUT_YES] |= INCHIDIFF_MOBH_PROTONS; /* diff number of removed protons */ - } - if ( memcmp( nNumRemovedProtons_R.nNumRemovedIsotopicH, - nNumRemovedProtons_D.nNumRemovedIsotopicH, - sizeof( nNumRemovedProtons_D.nNumRemovedIsotopicH ) ) ) { - CompareInchiFlags[TAUT_YES] |= INCHIDIFF_REM_ISO_H; - } - -exit_function: - - if ( pINChISort1 ) inchi_free( pINChISort1 ); - if ( pINChISort2 ) inchi_free( pINChISort2 ); - - return ret; -} -/******************************************************************************************************/ -int CompareTwoPairsOfInChI( INChI *pInChI1[TAUT_NUM], INChI *pInChI2[TAUT_NUM], - int bMobileH, INCHI_MODE CompareInchiFlags[] ) -{ - int iMobileH, err=0; - INCHI_MODE cmp; - for ( iMobileH = 0; iMobileH < TAUT_NUM; iMobileH ++ ) { - if ( !pInChI1[iMobileH] != !pInChI2[iMobileH] ) { - if ( iMobileH == TAUT_NON && - pInChI1[TAUT_YES] && pInChI1[TAUT_YES] ) { - CompareInchiFlags[iMobileH] |= INCHIDIFF_COMP_HLAYER; - } else { - CompareInchiFlags[iMobileH] |= INCHIDIFF_COMP_NUMBER; - } - continue; - } - if ( pInChI1[iMobileH] && pInChI2[iMobileH] ) { - cmp = CompareReversedINChI3( pInChI1[iMobileH], pInChI2[iMobileH], NULL, NULL, &err ); - if ( cmp ) { - CompareInchiFlags[iMobileH] |= cmp; - } - } - } - return err; -} -/******************************************************************************************************/ -int CompareOneOrigInchiToRevInChI(StrFromINChI *pStruct, INChI *pInChI[TAUT_NUM], int bMobileH, int iComponent, - long num_inp, char *szCurHdr, - COMPONENT_REM_PROTONS *nCurRemovedProtons, INCHI_MODE CompareInchiFlags[]) -{ - int ret = pStruct->RevInChI.nRetVal, err=0; - INCHI_MODE cmp; - if ( ret == _IS_OKAY || ret == _IS_WARNING ) { - /* ignore bMobileH for now */ - int i, i0, b /* created type */, b0 /* requested type*/, j, k; - /* pINChI[iINCHI][iComponent][bTaut] */ - /* i0 = requested Rec/Disconnected: 1/0 */ - /* i = what InChI creaded out of the restored structure */ - /* b0 = requested Mobile/Fixed-H: 1/0 */ - /* b = what InChI creaded out of the restored structure */ - i = i0 = pStruct->iINCHI; - b = b0 = pStruct->iMobileH; - if ( i == INCHI_REC && !pStruct->RevInChI.num_components[i] ) { - i = INCHI_BAS; - } - if ( b == TAUT_NON && (!pStruct->RevInChI.pINChI[i] || - !pStruct->RevInChI.pINChI[i][0][b] || - !pStruct->RevInChI.pINChI[i][0][b]->nNumberOfAtoms ) ) { - b = TAUT_YES; - } - if ( pStruct->bDeleted && (!pInChI[0] || pInChI[0]->bDeleted ) ) { - return 0; - } - - if ( pStruct->RevInChI.num_components[i] > 1 && - !pStruct->RevInChI.pINChI[i][1][b]->bDeleted || - pStruct->RevInChI.num_components[i] < 1 ) { - CompareInchiFlags[bMobileH] |= INCHIDIFF_COMP_NUMBER; - } - if ( b != b0 || b != bMobileH || b0 != bMobileH || i > i0 ) { - /* do not print messages about TAUT_YES instead of TAUT_NON */ - CompareInchiFlags[bMobileH] |= INCHIDIFF_COMP_HLAYER; - } - - if ( pStruct->RevInChI.num_components[i] ) { - /* compare InChI from restored structure; '0' in [i][0][b] is the first component */ - if ( b == TAUT_YES && pStruct->RevInChI.pINChI[i][0][b]->bDeleted && (!pInChI[0] || pInChI[0]->bDeleted ) ) { - /* the 1st component is made out of proton(s) and the input component is missing or also a proton */ - cmp = 0; - } else { - cmp = CompareReversedINChI3( pStruct->RevInChI.pINChI[i][0][b], pInChI[0], NULL, NULL, &err ); - if ( cmp ) { - CompareInchiFlags[bMobileH] |= cmp; - } - } - if ( b == b0 && b == TAUT_NON ) { - if ( pStruct->RevInChI.pINChI[i][0][TAUT_YES] && - !pStruct->RevInChI.pINChI[i][0][TAUT_YES]->bDeleted || - pInChI[1] && !pInChI[1]->bDeleted ) { - - /* in addition to fixed-H also compare mobile-H InChI */ - cmp = CompareReversedINChI3( pStruct->RevInChI.pINChI[i][0][TAUT_YES], pInChI[1], NULL, NULL, &err ); - if ( cmp ) { - CompareInchiFlags[TAUT_YES] |= cmp; - } - } - /* compare removed H */ - if ( pStruct->nNumRemovedProtonsMobHInChI != pStruct->RevInChI.pINChI_Aux[i][0][TAUT_YES]->nNumRemovedProtons ) { - CompareInchiFlags[TAUT_YES] |= INCHIDIFF_MOBH_PROTONS; - } - } - memset( nCurRemovedProtons, 0, sizeof(*nCurRemovedProtons) ); - for ( k = 0; k < pStruct->RevInChI.num_components[i]; k ++ ) { - if ( !k || pStruct->RevInChI.pINChI[i][k][TAUT_YES]->bDeleted ) { - /* get removed protons from the 1st component; add othere only if they are deleted protons */ - nCurRemovedProtons->nNumRemovedProtons += pStruct->RevInChI.pINChI_Aux[i][k][TAUT_YES]->nNumRemovedProtons; - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { - nCurRemovedProtons->nNumRemovedIsotopicH[j] += pStruct->RevInChI.pINChI_Aux[i][k][TAUT_YES]->nNumRemovedIsotopicH[j]; - } - } - } - } - } else { - CompareInchiFlags[bMobileH] |= INCHIDIFF_STR2INCHI_ERR; - } - return err; -} -/*************************************************************************************/ -INCHI_MODE CompareReversedStereoINChI3( INChI_Stereo *s1/* InChI from reversed struct */, INChI_Stereo *s2 /* input InChI */, ICR *picr) -{ - int ret = 0; - int j1, j2, num_eq, num_dif, num_extra_undf, num_miss_undf, num_in1_only, num_in2_only; - int bAddSb = !(picr->num_sb_undef_in1_only + picr->num_sb_in1_only + picr->num_sb_in2_only); - int bAddSc = !(picr->num_sc_undef_in1_only + picr->num_sc_in1_only + picr->num_sc_in2_only); - - int nNumSc1 = s1? s1->nNumberOfStereoCenters : 0; - int nNumSc2 = s2? s2->nNumberOfStereoCenters : 0; - int nNumSb1 = s1? s1->nNumberOfStereoBonds : 0; - int nNumSb2 = s2? s2->nNumberOfStereoBonds : 0; - - if ( (nNumSc1 || nNumSc1) && - ( nNumSc1 != nNumSc2 || - memcmp( s1->nNumber, s2->nNumber, nNumSc1*sizeof(s1->nNumber[0] ) ) || - memcmp( s1->t_parity, s2->t_parity, nNumSc1*sizeof(s1->t_parity[0]) ) ) ) { - - num_eq = num_dif = num_extra_undf = num_miss_undf = num_in1_only = num_in2_only = 0; - for ( j1 = j2 = 0; j1 < nNumSc1 && j2 < nNumSc2; ) { - if ( s1->nNumber[j1] == s2->nNumber[j2] ) { - if ( s1->t_parity[j1] == s2->t_parity[j2] ) { - num_eq ++; - } else { - num_dif ++; - } - j1 ++; - j2 ++; - } else - if ( s1->nNumber[j1] < s2->nNumber[j2] ) { - num_in1_only ++; - if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { - num_extra_undf ++; - } - if ( bAddSc ) { - if ( picr->num_sc_in1_only < ICR_MAX_SC_IN1_ONLY ) - picr->sc_in1_only[picr->num_sc_in1_only ++] = j1; - if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { - if ( picr->num_sc_undef_in1_only < ICR_MAX_SC_UNDF ) - picr->sc_undef_in1_only[picr->num_sc_undef_in1_only ++] = j1; - } - } - j1 ++; - } else { - num_in2_only ++; - if ( s2->t_parity[j2] == AB_PARITY_UNDF ) { - num_miss_undf ++; - } - if ( bAddSc ) { - if ( picr->num_sc_in2_only < ICR_MAX_SC_IN2_ONLY ) - picr->sc_in2_only[picr->num_sc_in2_only ++] = j2; - if ( s2->t_parity[j2] == AB_PARITY_UNDF ) { - if ( picr->num_sc_undef_in2_only < ICR_MAX_SC_UNDF ) - picr->sc_undef_in2_only[picr->num_sc_undef_in2_only ++] = j1; - } - } - j2 ++; - } - } - while ( j1 < nNumSc1 ) { - if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { - num_extra_undf ++; - } - num_in1_only ++; - if ( bAddSc ) { - if ( picr->num_sc_in1_only < ICR_MAX_SC_IN1_ONLY ) - picr->sc_in1_only[picr->num_sc_in1_only ++] = j1; - if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { - if ( picr->num_sc_undef_in1_only < ICR_MAX_SC_UNDF ) - picr->sc_undef_in1_only[picr->num_sc_undef_in1_only ++] = j1; - } - } - j1 ++; - } - while ( j2 < nNumSc2 ) { - if ( s2->t_parity[j2] == AB_PARITY_UNDF ) { - num_miss_undf ++; - } - num_in2_only ++; - if ( bAddSc ) { - if ( picr->num_sc_in2_only < ICR_MAX_SC_IN2_ONLY ) - picr->sc_in2_only[picr->num_sc_in2_only ++] = j2; - } - j2 ++; - } - if ( num_dif ) { - ret |= INCHIDIFF_SC_PARITY; - } - if ( num_in1_only ) { - if ( num_extra_undf ) { - ret |= INCHIDIFF_SC_EXTRA_UNDF; - } - if ( num_in1_only != num_extra_undf ) { - ret |= INCHIDIFF_SC_EXTRA; - } - } - if ( num_in2_only ) { - if ( num_miss_undf ) { - ret |= INCHIDIFF_SC_MISS_UNDF; - } - if ( num_in2_only != num_miss_undf ) { - ret |= INCHIDIFF_SC_MISS; - } - } - } - if ( s1 && s2 && (s2->nCompInv2Abs != 2) && s1->nCompInv2Abs != s2->nCompInv2Abs && s1->nCompInv2Abs && s2->nCompInv2Abs ) { - ret |= INCHIDIFF_SC_INV; /* 2007-07-13 DT: added (s2->nCompInv2Abs != 2) to fix bug reoprted by Yerin on 2007/02/28 */ - /* Bug description: falsely reported "Stereo centers/allenes: Falsely inverted" for /S2 or /S3 */ - } - - if ( (nNumSb1 || nNumSb2 ) && - (nNumSb1 != nNumSb2 || - memcmp( s1->nBondAtom1, s2->nBondAtom1, nNumSb1*sizeof(s1->nBondAtom1[0]) ) || - memcmp( s1->nBondAtom2, s2->nBondAtom2, nNumSb1*sizeof(s1->nBondAtom2[0]) ) || - memcmp( s1->b_parity, s2->b_parity, nNumSb1*sizeof(s1->b_parity[0]) ) ) ) { - - num_eq = num_dif = num_extra_undf = num_miss_undf = num_in1_only = num_in2_only = 0; - for ( j1 = j2 = 0; j1 < nNumSb1 && j2 < nNumSb2; ) { - if ( s1->nBondAtom1[j1] == s2->nBondAtom1[j2] && - s1->nBondAtom2[j1] == s2->nBondAtom2[j2] ) { - if ( s1->b_parity[j1] == s2->b_parity[j2] ) { - num_eq ++; - } else { - num_dif ++; - } - j1 ++; - j2 ++; - } else - if ( s1->nBondAtom1[j1] < s2->nBondAtom1[j2] || - s1->nBondAtom1[j1] == s2->nBondAtom1[j2] && s1->nBondAtom2[j1] < s2->nBondAtom2[j2]) { - num_in1_only ++; - if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { - num_extra_undf ++; - } - if ( bAddSb ) { - if ( picr->num_sb_in1_only < ICR_MAX_SB_IN1_ONLY ) - picr->sb_in1_only[picr->num_sb_in1_only ++] = j1; - if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { - if ( picr->num_sb_undef_in1_only < ICR_MAX_SB_UNDF ) - picr->sb_undef_in1_only[picr->num_sb_undef_in1_only ++] = j1; - } - } - j1 ++; - } else { - num_in2_only ++; - if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { - num_miss_undf ++; - } - if ( bAddSb ) { - if ( picr->num_sb_in2_only < ICR_MAX_SB_IN2_ONLY ) - picr->sb_in2_only[picr->num_sb_in2_only ++] = j2; - if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { - if ( picr->num_sb_undef_in2_only < ICR_MAX_SB_UNDF ) - picr->sb_undef_in2_only[picr->num_sb_undef_in2_only ++] = j1; - } - } - j2 ++; - } - } - while ( j1 < nNumSb1 ) { - num_in1_only ++; - if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { - num_extra_undf ++; - } - if ( bAddSb ) { - if ( picr->num_sb_in1_only < ICR_MAX_SB_IN1_ONLY ) - picr->sb_in1_only[picr->num_sb_in1_only ++] = j1; - if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { - if ( picr->num_sb_undef_in1_only < ICR_MAX_SB_UNDF ) - picr->sb_undef_in1_only[picr->num_sb_undef_in1_only ++] = j1; - } - } - j1 ++; - } - while ( j2 < nNumSb2 ) { - num_in2_only ++; - if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { - num_miss_undf ++; - } - if ( bAddSb ) { - if ( picr->num_sb_in2_only < ICR_MAX_SB_IN2_ONLY ) - picr->sb_in2_only[picr->num_sb_in2_only ++] = j2; - if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { - if ( picr->num_sb_undef_in2_only < ICR_MAX_SB_UNDF ) - picr->sb_undef_in2_only[picr->num_sb_undef_in2_only ++] = j1; - } - } - j2 ++; - } - if ( num_dif ) { - ret |= INCHIDIFF_SB_PARITY; - } - if ( num_in1_only ) { - if ( num_extra_undf ) { - ret |= INCHIDIFF_SB_EXTRA_UNDF; - } - if ( num_in1_only != num_extra_undf ) { - ret |= INCHIDIFF_SB_EXTRA; - } - } - if ( num_in2_only ) { - if ( num_miss_undf ) { - ret |= INCHIDIFF_SB_MISS_UNDF; - } - if ( num_in2_only != num_miss_undf ) { - ret |= INCHIDIFF_SB_MISS; - } - } - } - - return ret; -} -/*********************************************************************************************************/ -INCHI_MODE CompareReversedINChI3( INChI *i1 /* InChI from reversed struct */, INChI *i2 /* input InChI */, - INChI_Aux *a1, INChI_Aux *a2, int *err ) -{ - INCHI_MODE ret = 0; - INChI_Stereo *Stereo1=NULL, *Stereo2=NULL; - int n1, n2, m, j, j1, j2, ret2, num_H1, num_H2; - ICR icr; - ICR *picr = &icr; - - *err = 0; - - memset( picr, 0, sizeof(*picr) ); - - if ( i1 == NULL && i2 == NULL ) - return 0; - if ( (i1 == NULL) ^ (i2 == NULL) ) { - ret |= INCHIDIFF_PROBLEM; /* one InChI exists while another doesn't */ - goto exit_function; - } - - if ( i1->nErrorCode == i2->nErrorCode ) { - if ( i1->nErrorCode ) { - ret |= INCHIDIFF_PROBLEM; /* both InChI have same error codes */ - goto exit_function; - } - } else { - ret |= INCHIDIFF_PROBLEM; /* at least one InChI has an error code */ - goto exit_function; - } - - if ( i1->nNumberOfAtoms != i2->nNumberOfAtoms ) { - ret |= INCHIDIFF_NUM_AT; - goto exit_function; - } - if ( i1->nNumberOfAtoms > 0 ) { - if ( memcmp( i1->nAtom, i2->nAtom, i1->nNumberOfAtoms*sizeof(i1->nAtom[0]) ) ) { - ret |= INCHIDIFF_ATOMS; - goto exit_function; - } - /* INCHIDIFF_NON_TAUT_H, INCHIDIFF_MORE_FH, INCHIDIFF_LESS_FH */ - if ( memcmp( i1->nNum_H, i2->nNum_H, i1->nNumberOfAtoms*sizeof(i1->nNum_H[0]) ) ) { - ret |= INCHIDIFF_POSITION_H; - for ( j1 = 0; j1 < i1->nNumberOfAtoms; j1 ++ ) { - if ( i1->nNum_H[j1] != i2->nNum_H[j1] && picr->num_diff_pos_H < ICR_MAX_DIFF_FIXED_H ) { - picr->diff_pos_H_at[picr->num_diff_pos_H] = j1; - picr->diff_pos_H_nH[picr->num_diff_pos_H] = i1->nNum_H[j1] - i2->nNum_H[j1]; - picr->num_diff_pos_H ++; - } - } - } - /* fixed H */ - if ( i1->nNum_H_fixed || i2->nNum_H_fixed ) { - int bHasFixedH1 = 0, bHasFixedH2 = 0, i; - if ( i1->nNum_H_fixed ) { - for ( i = 0; i < i1->nNumberOfAtoms; i ++ ) { - if ( i1->nNum_H_fixed[i] ) { - bHasFixedH1 ++; - } - } - } - if ( i2->nNum_H_fixed ) { - for ( i = 0; i < i2->nNumberOfAtoms; i ++ ) { - if ( i2->nNum_H_fixed[i] ) { - bHasFixedH2 ++; - } - } - } - if ( bHasFixedH1 && !bHasFixedH2 ) { - for ( i = j = 0; i < i1->nNumberOfAtoms; i ++ ) { - if ( i1->nNum_H_fixed[i] ) { - if ( j < ICR_MAX_DIFF_FIXED_H ) { - picr->fixed_H_at1_more[j] = i; - picr->fixed_H_nH1_more[j] = i1->nNum_H_fixed[i]; - j ++; - } - } - } - picr->num_fixed_H1_more = j; - ret |= INCHIDIFF_MORE_FH; /* Extra Fixed-H */ - } else - if ( !bHasFixedH1 && bHasFixedH2 ) { - for ( i = j = 0; i < i2->nNumberOfAtoms; i ++ ) { - if ( i2->nNum_H_fixed[i] ) { - if ( j < ICR_MAX_DIFF_FIXED_H ) { - picr->fixed_H_at2_more[j] = i; - picr->fixed_H_nH2_more[j] = i2->nNum_H_fixed[i]; - j ++; - } - } - } - picr->num_fixed_H2_more = j; - ret |= INCHIDIFF_LESS_FH; /* Missed Fixed-H */ - } else - if ( bHasFixedH1 && bHasFixedH2 && - memcmp( i1->nNum_H_fixed, i2->nNum_H_fixed, i1->nNumberOfAtoms*sizeof(i1->nNum_H_fixed[0]) ) ) { - for ( i = j1 = j2 = 0; i < i1->nNumberOfAtoms; i ++ ) { - if ( i1->nNum_H_fixed[i] > i2->nNum_H_fixed[i] ) { - if ( j1 < ICR_MAX_DIFF_FIXED_H ) { - picr->fixed_H_at1_more[j1] = i; - picr->fixed_H_nH1_more[j1] = i1->nNum_H_fixed[i] - i2->nNum_H_fixed[i]; - j1 ++; - } - } else - if ( i1->nNum_H_fixed[i] < i2->nNum_H_fixed[i] ) { - if ( j2 < ICR_MAX_DIFF_FIXED_H ) { - picr->fixed_H_at2_more[j2] = i; - picr->fixed_H_nH2_more[j2] = i2->nNum_H_fixed[i] - i1->nNum_H_fixed[i]; - j2 ++; - } - } - } - ret |= (j1? INCHIDIFF_MORE_FH:0) | (j2? INCHIDIFF_LESS_FH:0); - picr->num_fixed_H1_more = j1; - picr->num_fixed_H2_more = j2; - } - } - } - /* compare formulas and H */ - num_H1 = 0; - num_H2 = 0; - ret2 = CompareHillFormulasNoH( i1->szHillFormula, i2->szHillFormula, &num_H1, &num_H2 ); - picr->tot_num_H1 = num_H1; - picr->tot_num_H2 = num_H2; - if ( ret2 ) { - ret |= INCHIDIFF_NUM_EL; - goto exit_function; - } - if ( num_H1 > num_H2 ) { - ret |= INCHIDIFF_MORE_H; - } - if ( num_H1 < num_H2 ) { - ret |= INCHIDIFF_LESS_H; - } - - if ( i1->lenConnTable != i2->lenConnTable ) { - ret |= INCHIDIFF_CON_LEN; - goto exit_function; - } else - if ( i1->lenConnTable > 0 && memcmp( i1->nConnTable, i2->nConnTable, i1->lenConnTable*sizeof(i1->nConnTable[0]) ) ) { - ret |= INCHIDIFF_CON_TBL; - goto exit_function; - } - /* output special cases: different number of t-groups, different sizes of t-groups, different endpoints */ - /* in isotopic or deprotonated cases i1->lenTautomer == 1 && i1->nTautomer[0] = 0 */ -/* - if ( i1->lenTautomer != i2->lenTautomer && (i1->lenTautomer > 1 || i2->lenTautomer > 1) ) { - ret |= INCHIDIFF_TAUT_LEN; - } -*/ - /* compare number of t-groups */ - n1 = i1->lenTautomer? i1->nTautomer[0] : 0; - n2 = i2->lenTautomer? i2->nTautomer[0] : 0; - if ( !n1 && n2 ) { - ret |= INCHIDIFF_NO_TAUT; - } else - if ( n1 && !n2 ) { - ret |= INCHIDIFF_WRONG_TAUT; - } else - if ( n1 == 1 && n2 > 1 ) { - ret |= INCHIDIFF_SINGLE_TG; - } else - if ( n1 > 1 && n2 == 1 ) { - ret |= INCHIDIFF_MULTIPLE_TG; - } else - if ( n1 != n2 ) { - ret |= INCHIDIFF_NUM_TG; - } - if ( n1 || n2 ) { - /* number of endpoints */ - int num1 = 0, num2 = 0, num_M1=0, num_M2=0; - int len, num_eq, num_in1_only, num_in2_only; - AT_NUMB *pe1 = (AT_NUMB *) inchi_malloc( (i1->lenTautomer+1) * sizeof(pe1[0]) ); - AT_NUMB *pe2 = (AT_NUMB *) inchi_malloc( (i2->lenTautomer+1) * sizeof(pe2[0]) ); - num_H1 = num_H2=0; - /* collect endpoints, H, (-) */ - if ( !pe1 || !pe2 ) { - if ( pe1 ) inchi_free( pe1 ); - if ( pe2 ) inchi_free( pe2 ); - *err = RI_ERR_ALLOC; /* allocation error */ - goto exit_function; - } - for ( m = 1; m < i1->lenTautomer; m += len ) { - len = i1->nTautomer[m ++]; - num_H1 += i1->nTautomer[m]; - num_M1 += i1->nTautomer[m+1]; - for ( j = 2; j < len; j ++ ) { - pe1[num1 ++] = i1->nTautomer[m + j]; - } - } - for ( m = 1; m < i2->lenTautomer; m += len ) { - len = i2->nTautomer[m ++]; - num_H2 += i2->nTautomer[m]; - num_M2 += i2->nTautomer[m+1]; - for ( j = 2; j < len; j ++ ) { - pe2[num2 ++] = i2->nTautomer[m + j]; - } - } - picr->num_taut_H1 = num_H1; - picr->num_taut_H2 = num_H2; - picr->num_taut_M1 = num_M1; - picr->num_taut_M2 = num_M2; - /* sort endpoints */ - insertions_sort_AT_NUMB( pe1, num1 ); - insertions_sort_AT_NUMB( pe2, num2 ); - /* compare */ - /* - if ( num1 < num2 ) { - ret |= INCHIDIFF_LESS_TG_ENDP; - } else - if ( num1 > num2 ) { - ret |= INCHIDIFF_MORE_TG_ENDP; - } - */ - /* compare all */ - num_eq = num_in1_only = num_in2_only = 0; - for ( j1 = j2 = 0; j1 < num1 && j2 < num2; ) { - if( pe1[j1] == pe2[j2] ) { - j1 ++; - j2 ++; - num_eq ++; - } else - if ( pe1[j1] < pe2[j1] ) { - if ( picr->num_endp_in1_only < ICR_MAX_ENDP_IN1_ONLY ) { - picr->endp_in1_only[picr->num_endp_in1_only ++] = pe1[j1]; - } - j1 ++; - num_in1_only ++; - } else { - if ( picr->num_endp_in2_only < ICR_MAX_ENDP_IN2_ONLY ) { - picr->endp_in2_only[picr->num_endp_in2_only ++] = pe2[j2]; - } - j2 ++; - num_in2_only ++; - } - } - while ( j1 < num1 ) { - if ( picr->num_endp_in1_only < ICR_MAX_ENDP_IN1_ONLY ) { - picr->endp_in1_only[picr->num_endp_in1_only ++] = pe1[j1]; - } - j1 ++; - num_in1_only ++; - } - while ( j2 < num2 ) { - if ( picr->num_endp_in2_only < ICR_MAX_ENDP_IN2_ONLY ) { - picr->endp_in2_only[picr->num_endp_in2_only ++] = pe2[j2]; - } - j2 ++; - num_in2_only ++; - } - if ( num_in1_only ) { - ret |= INCHIDIFF_EXTRA_TG_ENDP; - } - if ( num_in2_only ) { - ret |= INCHIDIFF_MISS_TG_ENDP; - } - if ( !num_in1_only && !num_in2_only && num_eq ) { - ; /* same t-groups endpoints */ - } else { - ret |= INCHIDIFF_DIFF_TG_ENDP; - } - inchi_free( pe1 ); - inchi_free( pe2 ); - - } - - if ( (i1->lenTautomer > 1 && i2->lenTautomer > 1) && - ( i1->lenTautomer != i2->lenTautomer || - memcmp( i1->nTautomer, i2->nTautomer, i1->lenTautomer*sizeof(i1->nTautomer[0]) ) ) ) - ret |= INCHIDIFF_TG; - - if ( i1->nNumberOfIsotopicAtoms != i2->nNumberOfIsotopicAtoms ) { - ret |= INCHIDIFF_NUM_ISO_AT; - } else - if ( i1->nNumberOfIsotopicAtoms > 0 && memcmp( i1->IsotopicAtom, i2->IsotopicAtom, i1->nNumberOfIsotopicAtoms*sizeof(i1->IsotopicAtom[0]) ) ) - ret |= INCHIDIFF_ISO_AT; - if ( i1->nTotalCharge != i2->nTotalCharge ) - ret |= INCHIDIFF_CHARGE; - if ( a1 && a1->nNumRemovedProtons && (!a2 || a2->nNumRemovedProtons != a1->nNumRemovedProtons) ) { - ret |= INCHIDIFF_REM_PROT; - } - if ( a1 && (!a2 || - a2->nNumRemovedIsotopicH[0] != a1->nNumRemovedIsotopicH[0] || - a2->nNumRemovedIsotopicH[1] != a1->nNumRemovedIsotopicH[1] || - a2->nNumRemovedIsotopicH[2] != a1->nNumRemovedIsotopicH[2]) ) { - ret |= INCHIDIFF_REM_ISO_H; - } - -/* - if ( i1->nPossibleLocationsOfIsotopicH && i2->nPossibleLocationsOfIsotopicH ) { - if ( i1->nPossibleLocationsOfIsotopicH[0] != i2->nPossibleLocationsOfIsotopicH[0] || - memcmp(i1->nPossibleLocationsOfIsotopicH, i2->nPossibleLocationsOfIsotopicH, - sizeof(i1->nPossibleLocationsOfIsotopicH[0])*i1->nPossibleLocationsOfIsotopicH[0]) ) - return 18; - } else - if ( !i1->nPossibleLocationsOfIsotopicH != !i2->nPossibleLocationsOfIsotopicH ) { - return 19; - } -*/ - if ( i1->StereoIsotopic && - i1->StereoIsotopic->nNumberOfStereoBonds + i1->StereoIsotopic->nNumberOfStereoCenters ) { - Stereo1 = i1->StereoIsotopic; - } else { - Stereo1 = i1->Stereo; - } - if ( i2->StereoIsotopic && - i2->StereoIsotopic->nNumberOfStereoBonds + i2->StereoIsotopic->nNumberOfStereoCenters ) { - Stereo2 = i2->StereoIsotopic; - } else { - Stereo2 = i2->Stereo; - } - ret |= CompareReversedStereoINChI3( Stereo1, Stereo2, picr ); - -exit_function: - - picr->flags = ret; - - return ret; -} -/* message group names */ -CMP_INCHI_MSG_GROUP CompareInchiMsgsGroup[] = { -{IDGRP_ERR, " Error:"}, -{IDGRP_H, " Hydrogens:"}, -{IDGRP_MOB_GRP, " Mobile-H groups:"}, -{IDGRP_ISO_AT, " Isotopic:"}, -{IDGRP_CHARGE, " Charge(s):"}, -{IDGRP_PROTONS, " Proton balance:"}, -{IDGRP_ISO_H, " Exchangeable isotopic H:"}, -{IDGRP_SC, " Stereo centers/allenes:"}, -{IDGRP_SB, " Stereobonds/cumulenes:"}, -{IDGRP_HLAYER, " Fixed-H layer:"}, -{IDGRP_COMP, " Number of components:"}, -{IDGRP_CONV_ERR," Conversion encountered:"}, -{IDGRP_ZERO, ""} -}; -/* messages */ -CMP_INCHI_MSG CompareInchiMsgs[] = { -{INCHIDIFF_PROBLEM ,IDGRP_ERR, " Wrong result" }, /*0x00000001, severe: at least one InChI does not exist */ -{INCHIDIFF_POSITION_H ,IDGRP_H, " Locations or number" }, /*0x00000002, difference in non-taut {Mobile-H} or all H {Fixed-H} location/number */ -{INCHIDIFF_MORE_FH ,IDGRP_H, " Fixed-H" }, /*0x00000004, extra fixed H */ -{INCHIDIFF_LESS_FH ,IDGRP_H, " Fixed-H" }, /*0x00000004, missing fixed H */ -{INCHIDIFF_MORE_H ,IDGRP_H, " Number" }, /*0x00000008, formulas differ in number of H */ -{INCHIDIFF_LESS_H ,IDGRP_H, " Number" }, /*0x00000008, formulas differ in number of H */ -{INCHIDIFF_NO_TAUT ,IDGRP_MOB_GRP, " Missing" }, /*0x00000010, restored structure has no taut groups while the original InChI has some */ -{INCHIDIFF_WRONG_TAUT ,IDGRP_MOB_GRP, " Falsely present" }, /*0x00000020, restored has tautomerism while the original does not have it */ -{INCHIDIFF_SINGLE_TG ,IDGRP_MOB_GRP, " One instead of multiple" }, /*0x00000040, restored has 1 taut. group while the original InChI has multiple tg */ -{INCHIDIFF_MULTIPLE_TG ,IDGRP_MOB_GRP, " Multiple instead of one" }, /*0x00000080, restored has multiple tg while the original InChI has only one tg */ -{INCHIDIFF_EXTRA_TG_ENDP,IDGRP_MOB_GRP, " Attachment points" }, /*0x00000100, extra tautomeric endpoint{s} in restored structure */ -{INCHIDIFF_MISS_TG_ENDP ,IDGRP_MOB_GRP, " Attachment points" }, /*0x00000100, one or more tg endpoint is not in the restored structure */ -{INCHIDIFF_DIFF_TG_ENDP ,IDGRP_MOB_GRP, " Attachment points" }, /*0x00000100, lists of tg endpoints are different */ -{INCHIDIFF_NUM_TG ,IDGRP_MOB_GRP, " Number" }, /*0x00000200, different number of tautomeric groups */ -{INCHIDIFF_TG ,IDGRP_MOB_GRP, " Do not match" }, /*0x00000200, different tautomeric groups */ -{INCHIDIFF_NUM_ISO_AT ,IDGRP_ISO_AT, " Atoms do not match" }, /*0x00000400, ?severe: restored struct. has different number of isotopic atoms */ -{INCHIDIFF_ISO_AT ,IDGRP_ISO_AT, " Atoms do not match" }, /*0x00000400, ?severe: restored struct. has different locations/isotopes of isotopic atoms */ -{INCHIDIFF_REM_ISO_H ,IDGRP_ISO_H, " Does not match for a component" }, /*0x00000800, isotopic H removed */ -{INCHIDIFF_MOB_ISO_H ,IDGRP_ISO_H, " Do not match" }, /*0x00001000, different number of mobile exchangeable isotopic H */ -{INCHIDIFF_CHARGE ,IDGRP_CHARGE, " Do not match" }, /*0x00002000, restored structure has different charge */ -{INCHIDIFF_REM_PROT ,IDGRP_PROTONS, " Does not match for a component" }, /*0x00004000, proton{s} removed/added from the restored structure */ -{INCHIDIFF_MOBH_PROTONS ,IDGRP_PROTONS, " Does not match" }, /*0x00008000, different proton balance */ -{INCHIDIFF_SC_INV ,IDGRP_SC, " Falsely inverted" }, /*0x00010000, restores structure has different inversion stereocenter mark */ -{INCHIDIFF_SC_PARITY ,IDGRP_SC, " Wrong parity" }, /*0x00020000, restored structure has stereoatoms or allenes with different parity */ -{INCHIDIFF_SC_EXTRA_UNDF,IDGRP_SC, " Extra undefined" }, /*0x00040000, restored structure has extra undefined stereocenter{s} */ -{INCHIDIFF_SC_EXTRA ,IDGRP_SC, " Extra known" }, /*0x00080000, restored structure has extra stereocenter{s} */ -{INCHIDIFF_SC_MISS_UNDF ,IDGRP_SC, " Missing undefined" }, /*0x00100000, restored structure has not some undefined stereocenter{s} */ -{INCHIDIFF_SC_MISS ,IDGRP_SC, " Missing known" }, /*0x00200000, restored structure has not some stereocenters that are not undefined */ -{INCHIDIFF_SB_PARITY ,IDGRP_SB, " Wrong parity" }, /*0x00400000, restored structure has stereobonds or cumulenes with different parity */ -{INCHIDIFF_SB_EXTRA_UNDF,IDGRP_SB, " Extra undefined" }, /*0x00800000, restored structure has extra undefined stereobond{s} */ -{INCHIDIFF_SB_EXTRA ,IDGRP_SB, " Missing known" }, /*0x01000000, restored structure has extra stereobond{s} */ -{INCHIDIFF_SB_MISS_UNDF ,IDGRP_SB, " Missing undefined" }, /*0x02000000, restored structure has not some undefined stereocenters */ -{INCHIDIFF_SB_MISS ,IDGRP_SB, " Missing known" }, /*0x04000000, restored structure has not some stereobonds that are not undefined */ -{INCHIDIFF_COMP_HLAYER ,IDGRP_HLAYER, " Missing or extra" }, /*0x08000000, Restored component has Mobile-H layer instead of both Mobile-H & Fixed-H or both instead of one */ -{INCHIDIFF_COMP_NUMBER ,IDGRP_COMP, " Does not match" }, /*0x10000000, wrong number of components */ -{INCHIDIFF_STR2INCHI_ERR,IDGRP_CONV_ERR," Error" }, /*0x20000000 Restored structure to InChI conversion error */ -{INCHIDIFF_ZERO ,IDGRP_ZERO, "" } -}; - -/*************************************************************************/ -int AddOneMsg( char *szMsg, int used_len, int tot_len, const char *szAddMsg, const char *szDelim ) -{ - const char ellip[] = "..."; - int len = strlen( szAddMsg ); - int len_delim = (used_len && szDelim)? strlen(szDelim) : 0; - int len_to_copy; - if ( len + len_delim + used_len < tot_len ) { - if ( len_delim ) { - strcpy( szMsg+used_len, szDelim ); - used_len += len_delim; - } - strcpy( szMsg+used_len, szAddMsg ); - used_len += len; - } else - if ( (len_to_copy = (tot_len - used_len - len_delim - (int)sizeof(ellip))) > 10 ) { - if ( len_delim ) { - strcpy( szMsg+used_len, szDelim ); - used_len += len_delim; - } - strncpy( szMsg+used_len, szAddMsg, len_to_copy ); - used_len += len_to_copy; - strcpy( szMsg+used_len, ellip ); - used_len += sizeof( ellip ) - 1; - } - return used_len; -} -/*************************************************************************/ -int FillOutCompareMessage( char *szMsg, int nLenMsg, INCHI_MODE bits[] ) -{ - int bMobileH, k, n, len = strlen( szMsg ); - int iPrevGrpIdx, iCurGrpIdx, bFound; - INCHI_MODE bit; - static const char *hdr = " Problems/mismatches:"; - char szOneMsg[256]; - if ( bits[TAUT_YES] || bits[TAUT_NON] ) { - if ( !strstr( szMsg, hdr ) ) { - len = AddOneMsg( szMsg, len, nLenMsg, hdr, NULL ); - } - for ( bMobileH = TAUT_YES; 0 <= bMobileH; bMobileH -- ) { - if ( bits[bMobileH] ) { - strcpy( szOneMsg, bMobileH==TAUT_YES? " Mobile-H(" : " Fixed-H(" ); - len = AddOneMsg( szMsg, len, nLenMsg, szOneMsg, NULL ); - } - bit = 1; - iPrevGrpIdx = -1; - do { - if ( bit & bits[bMobileH] ) { - /* search for the message */ - bFound = 0; - for ( k = 0; CompareInchiMsgs[k].nBit != INCHIDIFF_ZERO && !bFound; k ++ ) { - if ( bit & (INCHI_MODE)CompareInchiMsgs[k].nBit ) { - /* message found */ - for ( n = 0; CompareInchiMsgsGroup[n].nGroupID != IDGRP_ZERO; n ++ ) { - if ( CompareInchiMsgsGroup[n].nGroupID == CompareInchiMsgs[k].nGroupID ) { - iCurGrpIdx = n; - if ( iCurGrpIdx != iPrevGrpIdx ) { - if ( iPrevGrpIdx >= 0 ) { - len = AddOneMsg( szMsg, len, nLenMsg, ";", NULL ); - } - len = AddOneMsg( szMsg, len, nLenMsg, CompareInchiMsgsGroup[iCurGrpIdx].szGroupName, NULL ); - } - len = AddOneMsg( szMsg, len, nLenMsg, CompareInchiMsgs[k].szMsg, iCurGrpIdx == iPrevGrpIdx? ",":NULL ); - iPrevGrpIdx = iCurGrpIdx; - bFound = 1; - break; - } - } - } - } - } - bit <<= 1; - } while ( bit ); - if ( bits[bMobileH] ) { - len = AddOneMsg( szMsg, len, nLenMsg, ")", NULL ); - } - } - } - return len; -} - -#endif diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichister.h b/INCHI-1-SRC/INCHI_API/inchi_dll/ichister.h deleted file mode 100644 index 12c7a1f..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichister.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHISTER_H__ -#define __INCHISTER_H__ - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif -int bCanAtomBeAStereoCenter( char *elname, S_CHAR charge, S_CHAR radical ); -int bCanInpAtomBeAStereoCenter( inp_ATOM *at, int cur_at, int bPointedEdgeStereo ); -int bCanAtomHaveAStereoBond( char *elname, S_CHAR charge, S_CHAR radical ); -int bCanAtomBeTerminalAllene( char *elname, S_CHAR charge, S_CHAR radical ); -int bCanAtomBeMiddleAllene( char *elname, S_CHAR charge, S_CHAR radical ); -int bAtomHasValence3( char *elname, S_CHAR charge, S_CHAR radical ); -int set_stereo_parity( inp_ATOM* at, sp_ATOM* at_output, int num_at, int num_removed_H, - int *nMaxNumStereoAtoms, int *nMaxNumStereoBonds, INCHI_MODE nMode, - int bPointedEdgeStereo, int vABParityUnknown ); -int get_opposite_sb_atom( inp_ATOM *at, int cur_atom, int icur2nxt, - int *pnxt_atom, int *pinxt2cur, int *pinxt_sb_parity_ord ); - -#define PES_BIT_POINT_EDGE_STEREO 1 -#define PES_BIT_PHOSPHINE_STEREO 2 -#define PES_BIT_ARSINE_STEREO 4 -#define PES_BIT_FIX_SP3_BUG 8 - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -#endif /* __INCHISTER_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichitaut.c b/INCHI-1-SRC/INCHI_API/inchi_dll/ichitaut.c deleted file mode 100644 index ad7e4d5..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichitaut.c +++ /dev/null @@ -1,4567 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - - -#include "mode.h" - -#include "inpdef.h" -#include "extr_ct.h" -#include "inpdef.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "ichicant.h" -#include "ichicomn.h" - -#include "ichicomp.h" - -#include "util.h" - -#include "ichi_bns.h" - - -/* Local prototypes */ -int SetTautomericBonds( inp_ATOM *at, int nNumBondPos, T_BONDPOS *BondPos ); -int CompRankTautomer(const void* a1, const void* a2 ); -int RegisterEndPoints( T_GROUP_INFO *t_group_info, /* T_GROUP *t_group, int *pnum_t, int max_num_t,*/ - T_ENDPOINT *EndPoint, int nNumEndPoints, inp_ATOM *at, int num_atoms, C_GROUP_INFO *cgi - , struct BalancedNetworkStructure *pBNS ); -int cmpTGroupNumber( const void *a1, const void *a2 ); -int comp_candidates( const void *a1, const void *a2 ); -int MoveEndpoint( inp_ATOM *at, S_CANDIDATE *s_candidate, AT_NUMB endpoint, AT_NUMB *nTGroupNewNumbers, - AT_NUMB *nTGroupPosition, int nNewTGroupOrd, T_GROUP_INFO *t_group_info); - -int FindAccessibleEndPoints( T_ENDPOINT *EndPoint, int *nNumEndPoints, T_BONDPOS *BondPos, int *nNumBondPos, - struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, - inp_ATOM *at, int num_atoms, C_GROUP_INFO *cgi, int taut_mode ); - -/* Bits for GetChargeType */ - -#define C_SUBTYPE_CHARGED 0 -#define C_SUBTYPE_p_DONOR 1 /* new */ -#define C_SUBTYPE_p_ACCEPT 2 /* new */ -#define C_SUBTYPE_H_ACCEPT 4 -#define C_SUBTYPE_H_DONOR 8 -#define C_SUBTYPE_NEUTRAL 16 - - -/* Internal stack array size */ -#define MAX_STACK_ARRAY_LEN 127 -#define MAX_TGROUP_ARRAY_LEN 127 - -/* local prototypes */ -int GetChargeType( inp_ATOM *atom, int iat, S_CHAR *cChargeSubtype ); -int GetNeutralRepsIfNeeded( AT_NUMB *pri, AT_NUMB *prj, inp_ATOM *at, int num_atoms, T_ENDPOINT *EndPoint, int nNumEndPoints, C_GROUP_INFO *cgi ); -int bCanBeACPoint( inp_ATOM *at, S_CHAR cCharge, S_CHAR cChangeValence, S_CHAR neutral_bonds_valence, - S_CHAR neutral_valence, S_CHAR nEndpointValence, S_CHAR *cChargeSubtype ); -int CmpCCandidates( const void *a1, const void *a2 ); -int RegisterCPoints( C_GROUP *c_group, int *pnum_c, int max_num_c, T_GROUP_INFO *t_group_info, - int point1, int point2, int ctype, inp_ATOM *at, int num_atoms ); -int GetSaltChargeType( inp_ATOM *at, int at_no, T_GROUP_INFO *t_group_info, int *s_subtype ); -int GetOtherSaltChargeType( inp_ATOM *at, int at_no, T_GROUP_INFO *t_group_info, int *s_subtype, int bAccept_O ); -int MergeSaltTautGroupsBlind( inp_ATOM *at, int s_type, int num_atoms, S_GROUP_INFO *s_group_info, int nNumCandidates, - T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, - struct BalancedNetworkStructure *pBNS ); -int ConnectSaltTGroups2SuperTGroup( inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, int nNumCandidates, - T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, - struct BalancedNetworkStructure *pBNS, int *nNewTGroupNumber, int *vertSuperTGroup ); -int bDoNotMergeNonTautAtom(inp_ATOM *at, int at_no); -int GetOtherSaltType( inp_ATOM *at, int at_no, int *s_subtype ); - - -/*****************************************************************************/ -/* Tautomers: Sorting globals */ -AT_RANK *pn_tRankForSort; -/*****************************************************************************/ - - - -/*****************************************************************************/ -int is_centerpoint_elem( U_CHAR el_number ) -{ - static U_CHAR el_numb[12]; - static int len; - int i; - if ( !el_numb[0] && !len ) { - el_numb[len++] = (U_CHAR)get_periodic_table_number( "C" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "N" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "P" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "S" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "I" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "As" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "Sb" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "Se" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "Te" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "Cl" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "Br" ); - } - for ( i = 0; i < len; i ++ ) { - if ( el_numb[i] == el_number ) { - return 1; - } - } - return 0; -} -#if ( KETO_ENOL_TAUT == 1 ) /* post v.1 feature */ - - - -/*****************************************************************************/ -int is_centerpoint_elem_KET( U_CHAR el_number ) -{ - static U_CHAR el_numb[1]; - static int len; - int i; - if ( !el_numb[0] && !len ) { - el_numb[len++] = (U_CHAR)get_periodic_table_number( "C" ); - } - for ( i = 0; i < len; i ++ ) { - if ( el_numb[i] == el_number ) { - return 1; - } - } - return 0; -} -#endif - - - -/*****************************************************************************/ -int is_centerpoint_elem_strict( U_CHAR el_number ) -{ - static U_CHAR el_numb[6]; - static int len; - int i; - if ( !el_numb[0] && !len ) { - el_numb[len++] = (U_CHAR)get_periodic_table_number( "C" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "N" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "P" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "As" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "Sb" ); - } - for ( i = 0; i < len; i ++ ) { - if ( el_numb[i] == el_number ) { - return 1; - } - } - return 0; -} - - - -/*****************************************************************************/ -int get_endpoint_valence( U_CHAR el_number ) -{ - static U_CHAR el_numb[6]; - static int len, len2; - int i; - if ( !el_numb[0] && !len ) { - el_numb[len++] = (U_CHAR)get_periodic_table_number( "O" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "S" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "Se" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "Te" ); - len2 = len; - el_numb[len++] = (U_CHAR)get_periodic_table_number( "N" ); - } - for ( i = 0; i < len; i ++ ) { - if ( el_numb[i] == el_number ) { - return i < len2? 2 : 3; - } - } - return 0; -} - - - -/*****************************************************************************/ -#if ( KETO_ENOL_TAUT == 1 ) /* post v.1 feature */ -/*****************************************************************************/ -int get_endpoint_valence_KET( U_CHAR el_number ) -{ - static U_CHAR el_numb[2]; - static int len, len2; - int i; - if ( !el_numb[0] && !len ) { - el_numb[len++] = (U_CHAR)get_periodic_table_number( "O" ); - len2 = len; - el_numb[len++] = (U_CHAR)get_periodic_table_number( "C" ); - } - for ( i = 0; i < len; i ++ ) { - if ( el_numb[i] == el_number ) { - return i < len2? 2 : 4; - } - } - return 0; -} -#endif -/*****************************************************************************/ - - - -/*****************************************************************************/ -int AddAtom2num( AT_RANK num[], inp_ATOM *atom, int at_no, int bSubtract ) -{ /* bSubtract: 0=> add, 1=>subtract, 2=> fill */ - inp_ATOM *at = atom + at_no; - int k; - int nMobile = (at->charge == -1); - if ( bSubtract == 1 ) { - /* 1: subtract */ - num[1] -= nMobile; - nMobile += at->num_H; - num[0] -= nMobile; - for ( k = 0; k < T_NUM_ISOTOPIC; k ++ ) { - /* T (3H isotope) first because it has higher weight */ - num[T_NUM_NO_ISOTOPIC+k] -= at->num_iso_H[NUM_H_ISOTOPES-k-1]; - } - } else { - if ( bSubtract == 2 ) { - /* fill */ - memset( num, 0, (T_NUM_NO_ISOTOPIC + T_NUM_ISOTOPIC)*sizeof(num[0]) ); - } - /* else (0): add */ - num[1] += nMobile; - nMobile += at->num_H; - num[0] += nMobile; - for ( k = 0; k < T_NUM_ISOTOPIC; k ++ ) { - /* T (3H isotope) first because it has higher weight */ - num[T_NUM_NO_ISOTOPIC+k] += at->num_iso_H[NUM_H_ISOTOPES-k-1]; - } - } - return nMobile; -} - - - -/*****************************************************************************/ -void AddAtom2DA( AT_RANK num_DA[], inp_ATOM *atom, int at_no, int bSubtract ) -{ /* bSubtract: 0=> add, 1=>subtract, 2=> fill */ - inp_ATOM *at = atom + at_no; - int nDelta, nAcidic_O; - - if (at->charge < -1 || at->charge == 1 && !at->c_point || at->charge > 1 ) - return; - - nDelta = ( bSubtract == 1 )? -1 : 1; - - /* "Acidic" O, S, Se, Te recognition */ - if ( at->at_type & ATT_ACIDIC_CO ) { - nAcidic_O = nDelta; - } else { - nAcidic_O = 0; - } - - if ( bSubtract == 2 ) { /* 2: fill, otherwise add */ - memset( num_DA, 0, TG_NUM_DA * sizeof(num_DA[0]) ); - } - if ( at->charge <= 0 && at->valence == at->chem_bonds_valence || - /* neutral or negative donor */ - at->charge > 0 && at->valence + 1 == at->chem_bonds_valence - /* positively charged donor */ - ) { - if ( at->charge < 0 ) { - num_DA[TG_Num_dM] += nDelta; - num_DA[TG_Num_dO] += nAcidic_O; - } else - if ( at->num_H ) { - num_DA[TG_Num_dH] += nDelta; - num_DA[TG_Num_dO] += nAcidic_O; - } - } else - if ( at->charge <= 0 && at->valence + 1 == at->chem_bonds_valence || - at->charge > 0 && at->valence + 2 == at->chem_bonds_valence ) { - /* acceptor */ - if ( at->charge < 0 ) { - num_DA[TG_Num_aM] += nDelta; - } else - if ( at->num_H ) { - num_DA[TG_Num_aH] += nDelta; - } else { - num_DA[TG_Num_aO] += nAcidic_O; /* acidic O-acceptor has no H or charge */ - } - } - return; -} - - - -/*****************************************************************************/ -int AddEndPoint( T_ENDPOINT *pEndPoint, inp_ATOM *at, int iat ) -{ - pEndPoint->nAtomNumber = iat; - pEndPoint->nEquNumber = 0; - pEndPoint->nGroupNumber = at[iat].endpoint; - if ( at[iat].endpoint ) { - /* already an endpoint */ - memset( pEndPoint->num, 0, sizeof(pEndPoint->num) ); - } else { - /* not an endpoint yet, make it an endpoint */ - AddAtom2num( pEndPoint->num, at, iat, 2 ); /* fill */ - AddAtom2DA( pEndPoint->num_DA, at, iat, 2 ); - /* - nMobile = pEndPoint->num[1] = (at[iat].charge == -1); - nMobile = pEndPoint->num[0] = at[iat].num_H + nMobile; - for ( k = 0; k < T_NUM_ISOTOPIC; k ++ ) { - pEndPoint->num[T_NUM_NO_ISOTOPIC+k] = at[iat].num_iso_H[NUM_H_ISOTOPES-k-1]; - } - */ - } - return 0; -} - - - -/*****************************************************************************/ -int nGetEndpointInfo( inp_ATOM *atom, int iat, ENDPOINT_INFO *eif ) -{ - int nEndpointValence; - int nMobile; - S_CHAR cChargeSubtype; - - if ( atom[iat].radical && atom[iat].radical != RADICAL_SINGLET ) - return 0; /* a radical */ - if ( !(nEndpointValence = get_endpoint_valence( atom[iat].el_number )) ) - return 0; /* not an endpoint */ - if ( nEndpointValence <= atom[iat].valence ) - return 0; /* not an endpoint, for example >N(+)< or >N< or >O(+)- or >O- or >N- or -O- */ - - if ( atom[iat].charge == -1 || atom[iat].charge == 0 ) { - /* not a positive charge-point */ - if ( nEndpointValence < atom[iat].chem_bonds_valence ) - return 0; /* abnormal valence > standard endpoint valence */ - nMobile = atom[iat].num_H + (atom[iat].charge == -1); - if ( nMobile + atom[iat].chem_bonds_valence != nEndpointValence ) - return 0; /* non-standard endpoint valence */ - switch ( atom[iat].chem_bonds_valence - atom[iat].valence ) { - case 0: - eif->cDonor = 1; - eif->cAcceptor = 0; - break; - case 1: - eif->cDonor = 0; - eif->cAcceptor = 1; - break; - default: - return 0; - } - eif->cMobile = nMobile; - eif->cNeutralBondsValence = nEndpointValence-nMobile; - eif->cMoveableCharge = 0; -#if ( KETO_ENOL_TAUT == 1 ) - eif->cKetoEnolCode = 0; -#endif - return nEndpointValence; - } else - if ( atom[iat].c_point && - 0 <= GetChargeType( atom, iat, &cChargeSubtype ) && - ((int)cChargeSubtype & (C_SUBTYPE_H_ACCEPT|C_SUBTYPE_H_DONOR)) - ) { - /* charge-point */ - if ( cChargeSubtype & C_SUBTYPE_H_ACCEPT ) { - eif->cDonor = 0; - eif->cAcceptor = 1; - } else - if ( cChargeSubtype & C_SUBTYPE_H_DONOR ) { - eif->cDonor = 1; - eif->cAcceptor = 0; - } else { - return 0; - } - eif->cMobile = atom[iat].num_H; - eif->cNeutralBondsValence = nEndpointValence-atom[iat].num_H; - eif->cMoveableCharge = atom[iat].charge; -#if ( KETO_ENOL_TAUT == 1 ) - eif->cKetoEnolCode = 0; -#endif - return nEndpointValence; - } - return 0; -} - - - -/*****************************************************************************/ -#if ( KETO_ENOL_TAUT == 1 ) /* post v.1 feature */ -/*****************************************************************************/ -int nGetEndpointInfo_KET( inp_ATOM *atom, int iat, ENDPOINT_INFO *eif ) -{ - int nEndpointValence; - int nMobile; - S_CHAR cChargeSubtype; - - /* - static U_CHAR el_number_O, el_number_C; - - if ( !el_number_O ) { - el_number_O = (U_CHAR)get_periodic_table_number( "O" ); - el_number_C = (U_CHAR)get_periodic_table_number( "C" ); - } - */ - if ( atom[iat].radical && atom[iat].radical != RADICAL_SINGLET ) - return 0; /* a radical */ - if ( !(nEndpointValence = get_endpoint_valence_KET( atom[iat].el_number )) ) - return 0; /* not an endpoint; only O and C can be an endpoint for keto-enol tautomerism */ - if ( nEndpointValence <= atom[iat].valence ) - return 0; /* not an endpoint, for example >N(+)< or >N< or >O(+)- or >O- or >N- or -O- */ - if ( nEndpointValence == 4 && atom[iat].valence < 2 ) - return 0; /* exclude O==C--CH3 <=> HO--C==CH2 */ - if ( nEndpointValence == 2 && atom[iat].valence > 1 ) - return 0; /* exclude --O--C==CH-- */ - - if ( atom[iat].charge == -1 || atom[iat].charge == 0 ) { - /* not a positive charge-point */ - if ( nEndpointValence < atom[iat].chem_bonds_valence ) - return 0; /* abnormal valence > standard endpoint valence */ - nMobile = atom[iat].num_H + (atom[iat].charge == -1); - if ( nMobile + atom[iat].chem_bonds_valence != nEndpointValence ) - return 0; /* non-standard endpoint valence */ - switch ( atom[iat].chem_bonds_valence - atom[iat].valence ) { - case 0: - eif->cDonor = 1; - eif->cAcceptor = 0; - break; - case 1: - eif->cDonor = 0; - eif->cAcceptor = 1; - break; - default: - return 0; - } - eif->cMobile = nMobile; - eif->cNeutralBondsValence = nEndpointValence-nMobile; - eif->cMoveableCharge = 0; - eif->cKetoEnolCode = (nEndpointValence == 2)? 1 : (nEndpointValence == 4)? 2 : 0; - return nEndpointValence; - } else - if ( atom[iat].c_point && - 0 <= GetChargeType( atom, iat, &cChargeSubtype ) && - ((int)cChargeSubtype & (C_SUBTYPE_H_ACCEPT|C_SUBTYPE_H_DONOR)) - ) { - /* charge-point; currently only O for keto-enol tautomerism */ - if ( cChargeSubtype & C_SUBTYPE_H_ACCEPT ) { - eif->cDonor = 0; - eif->cAcceptor = 1; - } else - if ( cChargeSubtype & C_SUBTYPE_H_DONOR ) { - eif->cDonor = 1; - eif->cAcceptor = 0; - } else { - return 0; - } - eif->cMobile = atom[iat].num_H; - eif->cNeutralBondsValence = nEndpointValence-atom[iat].num_H; - eif->cMoveableCharge = atom[iat].charge; - eif->cKetoEnolCode = (nEndpointValence == 2)? 1 : (nEndpointValence == 4)? 2 : 0; - return nEndpointValence; - } - return 0; -} -#endif -/*****************************************************************************/ - - - -/*****************************************************************************/ -/* RegisterEndPoints ret>0 => new registration happened, */ -/* =0 => no changes, -1 => program error (debug) */ -/*****************************************************************************/ -int RegisterEndPoints( T_GROUP_INFO *t_group_info, - /* T_GROUP *t_group, int *pnum_t, int max_num_t,*/ - T_ENDPOINT *EndPoint, int nNumEndPoints, inp_ATOM *at, int num_atoms, - C_GROUP_INFO *cgi, struct BalancedNetworkStructure *pBNS ) -{ - T_GROUP *t_group = t_group_info->t_group; - int *pnum_t = &t_group_info->num_t_groups; - int max_num_t = t_group_info->max_num_t_groups; - int nNumZeroEqu, nNumNewTGroups; - AT_NUMB group, prev_group, prev_eqnum, nNextGroupNumber, nLeastGroupNumber; - int nNumGroups, num_t, difference; - int i, j, k, ret; - AT_NUMB nNewTgNumberStackArray[MAX_STACK_ARRAY_LEN+1]; - AT_NUMB nGroupNumberStackArray[MAX_STACK_ARRAY_LEN+1]; - AT_NUMB nGroupNewNumberStackArray[MAX_STACK_ARRAY_LEN+1]; - AT_NUMB *nNewTgNumber = nNewTgNumberStackArray; - AT_NUMB *nGroupNumber = nGroupNumberStackArray; - AT_NUMB *nGroupNewNumber = nGroupNewNumberStackArray; - - if ( nNumEndPoints <= 0 ) - return 0; /* nothing to do */ - num_t = *pnum_t; - difference = 0; - nNextGroupNumber = 0; - nNumZeroEqu = 0; - ret = 0; - /* find max group number; increment it to obtain next available group number */ - for ( i = 0; i < num_t; i ++ ) { - if ( nNextGroupNumber < t_group[i].nGroupNumber ) - nNextGroupNumber = t_group[i].nGroupNumber; - } - nNextGroupNumber ++; - - /* find min non-zero group number nLeastGroupNumber; - count zero EndPoint[i].nEquNumber - if all EndPoint[i].nGroupNumber are equal and non-zero then exit: nothing to do. - */ - nLeastGroupNumber = nNextGroupNumber; - prev_group = EndPoint[0].nGroupNumber; - prev_eqnum = EndPoint[0].nEquNumber; - for ( i = j = k = 0; i < nNumEndPoints; i ++ ) { - if ( group = EndPoint[i].nGroupNumber ) { - if ( group < nLeastGroupNumber ) { - nLeastGroupNumber = group; - } - } - j += (prev_group == EndPoint[i].nGroupNumber); /* count endpoints that belong to the 1st group */ - k += (prev_eqnum == EndPoint[i].nEquNumber); /* count endpoints that belongo to a group equivalent to the 1st group */ - nNumZeroEqu += !EndPoint[i].nEquNumber; /* count endpoints that have been processed by FindAccessibleEndPoints() */ - } - if ( j == nNumEndPoints && prev_group && k == nNumEndPoints ) { - /* all endpoints already belong to one t-group; - the last comparison is not needed for now - because EndPoint[i].nEquNumber cannot make - endpont partitioning finer - */ - return 0; - } - - nNumNewTGroups = 0; - - if ( !nNumZeroEqu ) { - /* EndPoint[] has been processed by FindAccessibleEndPoints; - * equal EndPoint[i].nEquNumber mark endpoints belonging to - * the same t-group - * Since now the next available t-group number, nNextGroupNumber, - * is known,replace fict. IDs assigned by FindAccessibleEndPoints - * with correct new t-group numbers. - */ - for ( i = 0; i < nNumEndPoints; i ++ ) { - if ( (group = EndPoint[i].nEquNumber) >= nNextGroupNumber ) { - /* replace fict. IDs assigned by FindAccessibleEndPoints() with new t-group numbers */ - /* these fict. IDs have values = (num_atoms+1), (num_atoms+2),...; they may be non-contiguous */ - for ( j = 0; j < nNumNewTGroups; j ++ ) { - if ( group == nGroupNewNumber[j] ) - break; - } - if ( j == nNumNewTGroups ) { - /* found new fict. ID = group */ - if ( j == MAX_STACK_ARRAY_LEN && nGroupNewNumber == nGroupNewNumberStackArray ) { - /* stack array overflow; allocate more memory than may be needed */ - nGroupNewNumber = (AT_NUMB *) inchi_malloc(nNumEndPoints*sizeof(nGroupNewNumber[0])); - if ( !nGroupNewNumber ) { - ret = -1; - goto exit_function; - } - memcpy( nGroupNewNumber, nGroupNewNumberStackArray, nNumNewTGroups*sizeof(nGroupNewNumber[0])); - } - /* save newly found fict. t-group ID to compare to the next values of EndPoint[].nEquNumber */ - nGroupNewNumber[j] = group; - nNumNewTGroups ++; - } - EndPoint[i].nEquNumber = nNextGroupNumber + j; - } - } /* after this point the values just stored in nGroupNewNumber[] will not - be used. However, the obtained nNumNewTGroups value will be used */ - } else - if ( nNumZeroEqu == nNumEndPoints ) { - /* EndPoint[] has NOT been processed by FindAccessibleEndPoints; - all atoms and t-groups to which endpoints belong should be merged into a single t-group - */ - if ( nLeastGroupNumber == nNextGroupNumber ) { - /* flag to create a new t-group: none of the found - * endpoints belong to an already known t-group - */ - nNumNewTGroups = 1; /* otherwise 0 */ - } - /* All EndPoint[*].nEquNumber are zeroes. All endpoints will - * belong to one new or old t-group; its ID is nLeastGroupNumber. - * Set EndPoint[i].nEquNumber = nLeastGroupNumber; - */ - for ( i = 0; i < nNumEndPoints; i ++ ) { - EndPoint[i].nEquNumber = nLeastGroupNumber; - } - } else { - ret = -1; /* program error: only some of EndPoint[i].nEquNumber are zero */ /* */ - goto exit_function; - } - - if ( nNumNewTGroups ) { - /* create new nNumNewTGroups t-group(s) */ - if ( num_t + nNumNewTGroups > max_num_t ) { - ret = -1; /* found too many t-groups */ /* */ - goto exit_function; - } - /* initialize new t-group(s) */ - memset( t_group + num_t, 0, nNumNewTGroups * sizeof(t_group[0]) ); - for ( i = 0; i < nNumNewTGroups; i ++ ) { - t_group[num_t+i].nGroupNumber = nNextGroupNumber + i; - } - } - - /* At this point: - * EndPoint[i].nGroupNumber == 0 => the endpoint atom does not belong to a t-group yet - * EndPoint[i].nGroupNumber > 0 => current t-group ID of the endpoint atom - * EndPoint[i].nEquNumber --> new ID of a tautomeric group of this endpoint atom - * EndPoint[i].nAtomNumber --> number of the endpoint atom - */ - - nNumGroups = 0; /* counts the groups to be renumbered */ - for ( i = j = 0; i < nNumEndPoints; i ++ ) { - if ( group = EndPoint[i].nGroupNumber ) { - if ( group == EndPoint[i].nEquNumber ) { - continue; /* ignore: the endpoint belongs to the same t-group as before */ - } - /* save information for renumbering of the existing t-groups */ - for ( j = 0; j < nNumGroups; j ++ ) { - if ( group == nGroupNumber[j] ) { - if ( EndPoint[i].nEquNumber != nGroupNewNumber[j] ) { - ret = -1; /* program error */ /* */ - goto exit_function; - } - break; - } - } - if ( j == nNumGroups ) { - /* discovered a new t-group number; store it together with its nEquNumber */ - if ( j == MAX_STACK_ARRAY_LEN ) { - if ( nGroupNewNumber == nGroupNewNumberStackArray ) { - nGroupNewNumber = (AT_NUMB *) inchi_malloc(nNumEndPoints*sizeof(nGroupNewNumber[0])); - if ( !nGroupNewNumber ) { - ret = -1; - goto exit_function; - } - memcpy( nGroupNewNumber, nGroupNewNumberStackArray, nNumGroups*sizeof(nGroupNewNumber[0])); - } - if ( nGroupNumber == nGroupNumberStackArray ) { - nGroupNumber = (AT_NUMB *) inchi_malloc(nNumEndPoints*sizeof(nGroupNumber[0])); - if ( !nGroupNumber ) { - ret = -1; - goto exit_function; - } - memcpy( nGroupNumber, nGroupNumberStackArray, nNumGroups*sizeof(nGroupNumber[0])); - } - } - - nGroupNumber[j] = group; /* old t-group ID */ - nGroupNewNumber[j] = EndPoint[i].nEquNumber; /* new t-group ID */ - nNumGroups ++; - } - } else { - /* add a new endpoint to the newly created or previously existing t-groups */ - group = EndPoint[i].nEquNumber; - if ( group >= nNextGroupNumber ) { - /* get index of a new t-group from equ number */ - j = num_t + group - nNextGroupNumber; /* newly assigned IDs are contiguous */ - } else { - /* old t-group */ - if ( j >= num_t || group != t_group[j].nGroupNumber ) { - /* search only if j is not a needed group index */ - for ( j = 0; j < num_t; j ++ ) { - if ( group == t_group[j].nGroupNumber ) - break; - } - if ( j == num_t ) { - ret = -1; /* program error: t-group not found */ /* */ - goto exit_function; - } - } - } - /* add aton to existing or new t-group */ - t_group[j].nNumEndpoints ++; - for ( k = 0; k < (int)(sizeof(t_group->num)/sizeof(t_group->num[0])); k ++ ) - t_group[j].num[k] += EndPoint[i].num[k]; - for ( k = 0; k < (int)(sizeof(t_group->num_DA)/sizeof(t_group->num_DA[0])); k ++ ) - t_group[j].num_DA[k] += EndPoint[i].num_DA[k]; - /* mark endpoint */ - at[EndPoint[i].nAtomNumber].endpoint = group; - difference ++; - } - } - - difference += nNumGroups; - num_t += nNumNewTGroups; - if ( !difference ) { - ret = 0; /* nothing to do. Not necessarily a program error: happens if all EndPoint[i].nGroupNumber==EndPoint[i].nEquNumber */ - goto exit_function; - } - - if ( nNumGroups ) { - /* prepare for renumbering: find max t-group number */ - for ( i = 0, nNextGroupNumber = 0; i < num_t; i ++ ) { - if ( nNextGroupNumber < t_group[i].nGroupNumber ) { - nNextGroupNumber = t_group[i].nGroupNumber; - } - } - } - /* renumber and merge t-groups */ - for ( i = 0; i < nNumGroups; i ++ ) { - int i1, i2; - AT_NUMB group1 = nGroupNumber[i]; - AT_NUMB group2 = nGroupNewNumber[i]; - /* add group1 to group2, then delete group1. */ - for ( j = 0, i1 = i2 = -1; j < num_t && (i1 < 0 || i2 < 0); j ++ ) { - if ( i1 < 0 && group1 == t_group[j].nGroupNumber ) - i1 = j; - if ( i2 < 0 && group2 == t_group[j].nGroupNumber ) - i2 = j; - } - if ( i1 < 0 || i2 < 0 ) { - ret = -1; /* program error */ /* */ - goto exit_function; - } - /* add t_group[i1] to t_group[i2] and remove t_group[i1] */ - for ( k = 0; k < (int)(sizeof(t_group->num)/sizeof(t_group->num[0])); k ++ ) - t_group[i2].num[k] += t_group[i1].num[k]; - for ( k = 0; k < (int)(sizeof(t_group->num_DA)/sizeof(t_group->num_DA[0])); k ++ ) - t_group[i2].num_DA[k] += t_group[i1].num_DA[k]; - t_group[i2].nNumEndpoints += t_group[i1].nNumEndpoints; - num_t --; - if ( num_t > i1 ) { - memmove( t_group+i1, t_group+i1+1, ( num_t - i1)*sizeof(t_group[0]) ); - } - } - - if ( nNumGroups ) { - /* there are groups to merge */ - if ( nNextGroupNumber >= MAX_STACK_ARRAY_LEN ) { - nNewTgNumber = (AT_NUMB *) inchi_malloc((nNextGroupNumber+1)*sizeof(*nNewTgNumber)); - if ( !nNewTgNumber ) { - ret = -1; - goto exit_function; /* error: out of RAM */ - } - } - memset( nNewTgNumber, 0, (nNextGroupNumber+1)*sizeof(*nNewTgNumber) ); - for ( i = 0; i < num_t; i ++ ) { - nNewTgNumber[t_group[i].nGroupNumber] = i+1; /* new t-group numbers */ - } - for ( j = 0; j < nNumGroups; j ++ ) { - if ( !nNewTgNumber[nGroupNumber[j]] && nNewTgNumber[nGroupNewNumber[j]] ) { - nNewTgNumber[nGroupNumber[j]] = nNewTgNumber[nGroupNewNumber[j]]; - } else { - ret = -1; /* program error: all new numbers must have been marked */ - goto exit_function; - } - } - /* renumber t-groups */ - for ( i = 0; i < num_t; i ++ ) { - t_group[i].nGroupNumber = nNewTgNumber[t_group[i].nGroupNumber]; - } -#if ( bRELEASE_VERSION != 1 ) - /* Check: debug only */ - for ( i = 1; i < num_t; i ++ ) { - if ( 1 != t_group[i].nGroupNumber - t_group[i-1].nGroupNumber ) { - ret = -1; /* debug */ - goto exit_function; - } - } -#endif - /* renumber endpoints */ - for ( i = 0; i < num_atoms; i ++ ) { - if ( group = at[i].endpoint ) { - if ( !(at[i].endpoint = nNewTgNumber[group]) || nNextGroupNumber <= nNewTgNumber[group] ) { - ret = -1; /* program error */ - goto exit_function; - } - } - } - } - if ( nNewTgNumber != nNewTgNumberStackArray ) { - inchi_free( nNewTgNumber ); - nNewTgNumber = nNewTgNumberStackArray; - } - if ( nGroupNumber != nGroupNumberStackArray ) { - inchi_free(nGroupNumber); - nGroupNumber = nGroupNumberStackArray; - } - if ( nGroupNewNumber != nGroupNewNumberStackArray ) { - inchi_free( nGroupNewNumber ); - nGroupNewNumber = nGroupNewNumberStackArray; - } - if ( !t_group_info->tGroupNumber ) { - t_group_info->tGroupNumber = (AT_NUMB *) inchi_malloc(2*max_num_t*sizeof(t_group_info->tGroupNumber[0])); - if ( !t_group_info->tGroupNumber ) { - ret = -1; - goto exit_function; - } - } - /* fill out t-group index 2004-02-27 */ - memset( t_group_info->tGroupNumber, 0, 2*max_num_t*sizeof(t_group_info->tGroupNumber[0]) ); - for ( i = 0; i < num_t; i ++ ) { - if ( t_group[i].nNumEndpoints && t_group[i].nGroupNumber ) - t_group_info->tGroupNumber[t_group[i].nGroupNumber] = i+1; - } - - if ( pBNS && (pBNS->tot_st_cap == pBNS->tot_st_flow || ALWAYS_ADD_TG_ON_THE_FLY) ) { - T_GROUP_INFO tgi; - int ret_bns; - memset( &tgi, 0, sizeof(tgi) ); - tgi.num_t_groups = num_t; - tgi.t_group = t_group; -#if ( KETO_ENOL_TAUT == 1 ) - tgi.bTautFlags |= (t_group_info->bTautFlags & TG_FLAG_KETO_ENOL_TAUT); /* needed in AddTGroups2BnStruct() */ -#endif - /* reinitialize BN Structure */ - ret_bns = ReInitBnStruct( pBNS, at, num_atoms, 0 ); - if ( IS_BNS_ERROR( ret_bns ) ) { - return ret_bns; - } - if ( *pBNS->pbTautFlags & TG_FLAG_MOVE_POS_CHARGES ) { - /* set new charge groups */ - ret_bns = AddCGroups2BnStruct( pBNS, at, num_atoms, cgi ); - if ( IS_BNS_ERROR( ret_bns ) ) { - return ret_bns; - } - } - /* set new tautomeric groups */ - ret_bns = AddTGroups2BnStruct( pBNS, at, num_atoms, &tgi ); - if ( IS_BNS_ERROR( ret_bns ) ) { - return ret_bns; - } - } - - *pnum_t = num_t; - return difference; - -exit_function: - if ( nNewTgNumber != nNewTgNumberStackArray ) { - inchi_free( nNewTgNumber ); - } - if ( nGroupNumber != nGroupNumberStackArray ) { - inchi_free(nGroupNumber); - } - if ( nGroupNewNumber != nGroupNewNumberStackArray ) { - inchi_free( nGroupNewNumber ); - } - return ret; -} - - - -/***************************************************************************** - * Change non-alternating and non-tautomeric bonds - * (that is, single and double bonds) to tautomeric - *****************************************************************************/ -int SetTautomericBonds( inp_ATOM *at, int nNumBondPos, T_BONDPOS *BondPos ) -{ - int k, n; - for ( k = n = 0; k < nNumBondPos; k ++ ) { - int neighbor_index = BondPos[k].neighbor_index; - int center = BondPos[k].nAtomNumber; - int bond_mark = at[center].bond_type[neighbor_index]; - int bond_type = bond_mark & ~BOND_MARK_ALL; - int neighbor; -#if ( REPLACE_ALT_WITH_TAUT == 1 ) - if ( bond_type != BOND_TAUTOM ) -#else - if ( bond_type != BOND_ALTERN && bond_type != BOND_TAUTOM ) -#endif - { - int ii; - /* change bond type to BOND_TAUTOM presering higher bits marks */ - bond_type = (bond_mark & BOND_MARK_ALL) | BOND_TAUTOM; - /* change center-neighbor bond */ - at[center].bond_type[neighbor_index] = bond_type; - neighbor = at[center].neighbor[neighbor_index]; - for ( ii = 0; ii < at[neighbor].valence; ii ++ ) { - if ( at[neighbor].neighbor[ii] == center ) { - /* neighbor-center bond found */ - at[neighbor].bond_type[ii] = bond_type; - break; - } - } - n ++; - } - } - return n; -} - - - -/*****************************************************************************/ -int GetNeutralRepsIfNeeded( AT_NUMB *pri, AT_NUMB *prj, inp_ATOM *at, int num_atoms, - T_ENDPOINT *EndPoint, int nNumEndPoints, C_GROUP_INFO *cgi ) -{ - AT_NUMB ri = *pri; - AT_NUMB rj = *prj; - int i, k; - AT_NUMB c_point, endpoint, r; - - if ( (c_point = at[ri].c_point) && (c_point == at[rj].c_point) && - (at[ri].charge == 1 || at[rj].charge == 1) && cgi && cgi->num_c_groups > 0 ) { - /* at[ri] and at[rj] belong to the same charge group, at least one is charged */ - /* MS VC++ 2005 reports unreachable code here ??? */ - for ( k = 0; k < cgi->num_c_groups; k ++ ) { - if ( cgi->c_group[k].nGroupNumber == c_point ) { - /* cgi->c_group[k] is found to be this charge group */ - if ( cgi->c_group[k].num_CPoints - cgi->c_group[k].num[0] < 2 ) { - /* Only one neutral in the c-group: we will not be able to neutralize both - when looking for the alt path to discover the tautomerism. - Therefore we need to find a neutral t-group representative */ - /* at[rj] */ - if ( endpoint = at[rj].endpoint ) { - for ( i = 0; i < nNumEndPoints; i ++ ) { - if ( (r=EndPoint[i].nAtomNumber) == *prj ) - continue; /* ignore at[*prj] */ - if ( at[r].endpoint != endpoint ) - continue; /* at[r] does not belong to the same t-group as at[*prj]; ignore the atom */ - if ( !at[r].c_point ) { - rj = r; /* found a neutral t-group representative */ - break; - } - if ( at[r].c_point != c_point && c_point == at[rj].c_point ) { - /* replace only once because of (c_point == at[rj].c_point) condition */ - rj = r; - } - } - if ( rj == *prj /*&& at[ri].endpoint*/ ) { - /* !!! "&& at[ri].endpoint": only between 2 t-groups 2004-02-27; - the change disabled due to undiscovered yet possibility of ambiguity*/ - /* no replacement has been found in EndPoint[]; try all atoms in the t-group */ - for ( i = 0; i < num_atoms; i ++ ) { - if ( at[i].endpoint != endpoint ) - continue; - if ( i == (int)*prj ) - continue; - if ( !at[i].c_point ) { - rj = (AT_NUMB)i; /* found neutral t-group representative */ - break; - } - if ( at[i].c_point != c_point && c_point == at[rj].c_point ) { - /* replace only once */ - rj = (AT_NUMB)i; - } - } - } - } - /* at[ri] */ - if ( endpoint = at[ri].endpoint ) { - for ( i = 0; i < nNumEndPoints; i ++ ) { - if ( (r=EndPoint[i].nAtomNumber) == *pri ) - continue; - if ( at[r].endpoint != endpoint ) - continue; - if ( !at[r].c_point ) { - ri = r; /* found neutral t-group representative */ - break; - } - if ( at[r].c_point != c_point && c_point == at[ri].c_point && - at[r].c_point != at[rj].c_point ) { - /* replace only once */ - ri = r; - } - } - if ( ri == *pri && at[rj].endpoint ) { - /* !!! "&& at[rj].endpoint": only between 2 t-groups 2004-02-27; - the change disabled due to undiscovered yet possibility of ambiguity */ - for ( i = 0; i < num_atoms; i ++ ) { - if ( at[i].endpoint != endpoint ) - continue; - if ( i == (int)*pri ) - continue; - if ( !at[i].c_point ) { - ri = (AT_NUMB)i; /* found neutral t-group representative */ - break; - } - if ( at[i].c_point != c_point && c_point == at[ri].c_point && - at[i].c_point != at[rj].c_point) { - /* replace only once */ - ri = (AT_NUMB)i; - } - } - } - } - - } - } - break; - } - *prj = rj; - *pri = ri; - } - return 0; -} - - - -/*****************************************************************************/ -int FindAccessibleEndPoints( T_ENDPOINT *EndPoint, int *nNumEndPoints, T_BONDPOS *BondPos, int *nNumBondPos, - struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, - inp_ATOM *at, int num_atoms, C_GROUP_INFO *cgi, int taut_mode ) -{ - AT_NUMB nTGroupRepresenative[MAXVAL], nTGroupEqu[MAXVAL], nTGEndPointNo[MAXVAL], ri, rj; - AT_NUMB nCurTGroupNumber, nMaxTGroupNumber, nNumTgroupNumbers, nMaxEquNumber; - int i, j, k, nNumDiffTGroupNumbers = 0, nNumFoundEqu, nErr; - - if ( *nNumEndPoints != *nNumBondPos ) - return 0; - /* collect all group numbers. Fill EndPoint[i].nEquNumber */ - for ( i = 0; i < *nNumEndPoints; i ++ ) { - nCurTGroupNumber = EndPoint[i].nEquNumber = EndPoint[i].nGroupNumber; /* initial equivalence */ - if ( nCurTGroupNumber ) { - /* found endpoint that already belongs to a t-group */ - for ( j = 0; j < nNumDiffTGroupNumbers; j ++ ) { - if ( nTGroupEqu[j] == nCurTGroupNumber ) - break; - } - if ( j == nNumDiffTGroupNumbers ) { - nTGroupRepresenative[nNumDiffTGroupNumbers] = EndPoint[i].nAtomNumber; - nTGroupEqu[nNumDiffTGroupNumbers] = EndPoint[i].nGroupNumber; - nTGEndPointNo[nNumDiffTGroupNumbers] = i; - nNumDiffTGroupNumbers ++; - } - } - } - - - /* check whether each pair belongs to the same t-group and establish the equivalence(s) */ - for ( i = 0, nNumFoundEqu=0; i < nNumDiffTGroupNumbers; i ++ ) { - for ( j = i+1; j < nNumDiffTGroupNumbers; j ++ ) { - ri = nTGroupRepresenative[i]; - rj = nTGroupRepresenative[j]; - /* both at[ri] and at[rj] are known to belong to tautomeric groups */ - GetNeutralRepsIfNeeded( &ri, &rj, at, num_atoms, EndPoint, *nNumEndPoints, cgi ); - nErr = bExistsAnyAltPath( pBNS, pBD, at, num_atoms, ri, rj, taut_mode ); - if ( IS_BNS_ERROR(nErr) ) - return nErr; - if ( 0 == nErr ) - continue; /* alt path between at[ri] and at[rj] not found */ - nCurTGroupNumber = inchi_min( nTGroupEqu[i], nTGroupEqu[j] ); - nMaxTGroupNumber = inchi_max( nTGroupEqu[i], nTGroupEqu[j] ); - for ( k = 0; k < nNumDiffTGroupNumbers; k ++ ) { - if ( nTGroupEqu[k]==nMaxTGroupNumber ) { - nTGroupEqu[k] = nCurTGroupNumber; - nNumFoundEqu ++; - } - } - for ( k = 0; k < *nNumEndPoints; k ++ ) { - if ( EndPoint[k].nEquNumber == nMaxTGroupNumber ) { - EndPoint[k].nEquNumber = nCurTGroupNumber; - } - } - } - } - if ( nNumFoundEqu ) { - /* leave in only non-equivalent representatives */ - for ( i = 1, k = 0; i < nNumDiffTGroupNumbers; i ++ ) { - for ( j = 0; j < i; j ++ ) { - if ( nTGroupEqu[j] == nTGroupEqu[i] ) { - nTGroupEqu[i] = 0; /* i > j; mark equivalent for removal*/ - break; - } - } - } - for ( i = j = 0; i < nNumDiffTGroupNumbers; i ++ ) { - if ( nTGroupEqu[i] ) { - if ( i != j ) { /* remove the marked */ - nTGroupEqu[j] = nTGroupEqu[i]; - nTGroupRepresenative[j] = nTGroupRepresenative[i]; - nTGEndPointNo[j] = nTGEndPointNo[i]; - } - j ++; - } - } - nNumDiffTGroupNumbers = j; /* number of known t-group representatives */ - } - /* collect endpoints that have not been assigned to t-groups */ - for ( i = 0, j = nNumDiffTGroupNumbers; i < *nNumEndPoints; i ++ ) { - if ( EndPoint[i].nEquNumber ) - continue; - nTGroupEqu[j] = 0; - nTGroupRepresenative[j] = EndPoint[i].nAtomNumber; - nTGEndPointNo[j] = i; - j ++; - - } - nNumTgroupNumbers = j; - nMaxEquNumber = num_atoms + 1; /* impossible atom or t-group number */ - - /* check whether each pair belongs to the same group and establish the equivalence(s) */ - for ( i = 0, nNumFoundEqu=0; i < nNumTgroupNumbers; i ++ ) { - for ( j = i+1; j < nNumTgroupNumbers; j ++ ) { - if ( nTGroupEqu[i] != nTGroupEqu[j] && (i>=nNumDiffTGroupNumbers || j>=nNumDiffTGroupNumbers) || - /* equivalence of a t-group and a non-t-group atom */ - !nTGroupEqu[i] && !nTGroupEqu[j] - /* equivalence of two non-t-group atoms */ - ) { - ri = nTGroupRepresenative[i]; - rj = nTGroupRepresenative[j]; - /*------------------------------!!!--------------------------------------------- - Explanation why GetNeutralRepsIfNeeded() may need to be changed 2004-02-27 - The change has been disabled due to undiscovered yet possibility of ambiguity - to search for neutral only among EndPoint[] in case taut-not_taut pairs - - Counterexample: O=C-NH(+)=C-NH2 - 1 2 3 - - Has already been found: 2-3 (+)-charge exchange - 1-2 tautomerism (charge removed to 3) - Now testing: 2-3 tautomerism. If not commented out, - GetNeutralRepsIfNeeded() would replace 2-3 test with 1-3 test because: - o Charge group has only one neutral and both 2 and 3 belong to it, - therefore we cannot neutralize both; search for neutral representative; - o Since 1 and 2 belong to the same t-group and 1 is neutral, - test 1-3 instead of 2-3. - This breaks our condition: - Test tautomeric H movement only between neutral atoms. - -----------------------------------------------------------------------------*/ - GetNeutralRepsIfNeeded( &ri, &rj, at, num_atoms, EndPoint, *nNumEndPoints, cgi ); - - nErr = bExistsAnyAltPath( pBNS, pBD, at, num_atoms, ri, rj, taut_mode ); - if ( IS_BNS_ERROR(nErr) ) - return nErr; - if ( nErr <= 0 ) - continue; - if ( nTGroupEqu[i] && nTGroupEqu[j] ) { - /* found equivalence of two t-groups; at least one of them must be a new one */ - nCurTGroupNumber = inchi_min( nTGroupEqu[i], nTGroupEqu[j] ); - nMaxTGroupNumber = inchi_max( nTGroupEqu[i], nTGroupEqu[j] ); - for ( k = 0; k < nNumTgroupNumbers; k ++ ) { - if ( nTGroupEqu[k]==nMaxTGroupNumber ) { - nTGroupEqu[k] = nCurTGroupNumber; - nNumFoundEqu ++; - } - } - for ( k = 0; k < *nNumEndPoints; k ++ ) { - if ( EndPoint[k].nEquNumber == nMaxTGroupNumber ) { - EndPoint[k].nEquNumber = nCurTGroupNumber; - } - } - } else - if ( nTGroupEqu[i] ) { /* extend existing t-group */ - nTGroupEqu[j] = nTGroupEqu[i]; - EndPoint[nTGEndPointNo[j]].nEquNumber = nTGroupEqu[i]; - - } else - if ( nTGroupEqu[j] ) { /* extend existing t-group */ - nTGroupEqu[i] = nTGroupEqu[j]; - EndPoint[nTGEndPointNo[i]].nEquNumber = nTGroupEqu[j]; - - } else { /* establis a new t-group */ - nTGroupEqu[i] = - nTGroupEqu[j] = nMaxEquNumber; /* assign a fict. ID to establish equivalence */ - EndPoint[nTGEndPointNo[i]].nEquNumber = - EndPoint[nTGEndPointNo[j]].nEquNumber = nMaxEquNumber; - nMaxEquNumber ++; - } - } - } - } - /* eliminate endpoints and bonds that do not belong to t-group(s) - (they have not been found connected by an alt path to any other endpoint) - */ - for ( i = 0, j = 0; i < *nNumEndPoints; i ++ ) { - if ( EndPoint[i].nEquNumber ) { -#if ( IGNORE_SINGLE_ENDPOINTS == 1 ) /* 1-28-2003 */ - for ( k = 0, nNumFoundEqu = 0; k < *nNumEndPoints; k ++ ) { - nNumFoundEqu += (EndPoint[i].nEquNumber == EndPoint[k].nEquNumber); - } - if ( nNumFoundEqu <= 1 ) { /* one time it is equal to itself when i == k above */ - /* if EndPoint[i] is not "equivalent" to any other EndPoint then ignore it */ - continue; - } -#endif - if ( i != j ) { /* save endpoints that are found to be connected to other endpoints by alt paths */ - EndPoint[j] = EndPoint[i]; - BondPos[j] = BondPos[i]; - } - j ++; - } - } - -#if ( IGNORE_SINGLE_ENDPOINTS != 1 ) /* 1-28-2003 */ - /* Do not allow a centerpoint to have only one tautomeric bond */ - /* Hack: we may have only one centerpoint */ - /* BondPos[*].nAtomNumber are centerpoints */ - if ( j == 1 ) { - /* check if there exist other centerpoint neighbors - * connected to it by another tautomeric-bond - */ - for ( i = 0, k = 0; i < at[BondPos[0].nAtomNumber].valence; i ++ ) { - k += ( i != BondPos[0].neighbor_index && - BOND_TAUTOM == (at[BondPos[0].nAtomNumber].bond_type[i] & ~BOND_MARK_ALL)); - } - if ( !k ) { - j = 0; - } - } -#endif - - *nNumEndPoints = *nNumBondPos = j; - return j; - -} - - - -/*****************************************************************************/ -/*#if ( MOVE_CHARGES == 1 ) */ /* { */ -/*****************************************************************************/ - -/**********************************************/ -/* */ -/* definitions for positive ion recognition */ -/* */ -/**********************************************/ - -/*****************************************************************************/ -typedef struct tagChargeType { /* meaning see in bCanBeACPoint() */ - char elname[3]; - S_CHAR charge; - S_CHAR neutral_valence; - S_CHAR neutral_bonds_valence; /* valence of a neutral atom */ - S_CHAR cChangeValence; /* charge increases valence by this value */ - S_CHAR cChargeType; /* different types are treated separately */ - S_CHAR num_bonds; /* added 02-06-2005 */ -} CHARGE_TYPE; - -CHARGE_TYPE CType[] = { - { "N\0", 1, 3, 3, 1, 0, 0 }, - { "P\0", 1, 3, 3, 1, 1, 0 }, -#if ( ADD_MOVEABLE_O_PLUS == 1 ) - { "O\0", 1, 2, 2, 1, 2, 2 }, /* added 02-06-2005 */ - { "S\0", 1, 2, 2, 1, 3, 2 }, /* added 03-18-2005 */ - { "Se", 1, 2, 2, 1, 4, 2 }, /* added 03-18-2005 */ - { "Te", 1, 2, 2, 1, 5, 2 }, /* added 03-18-2005 */ -#endif -}; - -/* bits */ - -#define C_SUBTYPE_CHARGED 0 -#define C_SUBTYPE_p_DONOR 1 /* new */ -#define C_SUBTYPE_p_ACCEPT 2 /* new */ -#define C_SUBTYPE_H_ACCEPT 4 -#define C_SUBTYPE_H_DONOR 8 -#define C_SUBTYPE_NEUTRAL 16 - -/* make sure any C_SUBTYPE_CHARGED_... < any C_SUBTYPE_NEUTRAL_... */ -/* charged */ -#define C_SUBTYPE_CHARGED_NON_TAUT (C_SUBTYPE_CHARGED) -#define C_SUBTYPE_CHARGED_p_DONOR (C_SUBTYPE_CHARGED|C_SUBTYPE_p_DONOR) -#define C_SUBTYPE_CHARGED_H_ACCEPT (C_SUBTYPE_CHARGED|C_SUBTYPE_H_ACCEPT) -#define C_SUBTYPE_CHARGED_H_ACCEPT_p_DONOR (C_SUBTYPE_CHARGED|C_SUBTYPE_H_ACCEPT|C_SUBTYPE_p_DONOR) -#define C_SUBTYPE_CHARGED_H_DONOR (C_SUBTYPE_CHARGED|C_SUBTYPE_H_DONOR |C_SUBTYPE_p_DONOR) -/* neutral */ -#define C_SUBTYPE_NEUTRAL_NON_TAUT (C_SUBTYPE_NEUTRAL) -#define C_SUBTYPE_NEUTRAL_H_ACCEPT (C_SUBTYPE_NEUTRAL|C_SUBTYPE_H_ACCEPT) -#define C_SUBTYPE_NEUTRAL_H_ACCEPT_p_ACCEPT (C_SUBTYPE_NEUTRAL|C_SUBTYPE_H_ACCEPT|C_SUBTYPE_p_ACCEPT) -#define C_SUBTYPE_NEUTRAL_H_DONOR (C_SUBTYPE_NEUTRAL|C_SUBTYPE_H_DONOR) - -#define NUM_C_TYPES (int)(sizeof( CType )/sizeof(CType[0])) -/*****************************************************************************/ - - - -/*****************************************************************************/ -int bCanBeACPoint( inp_ATOM *at, S_CHAR cCharge, S_CHAR cChangeValence, S_CHAR neutral_bonds_valence, - S_CHAR neutral_valence, S_CHAR nEndpointValence, S_CHAR *cChargeSubtype ) -{ - int nChangeValence; - int nNumBonds; - int nBondsValence; - int bNegCharge = (at->charge == -1); /* add fict. bonds to (-) 2004-02-24*/ - if ( at->charge == cCharge && at->valence == at->chem_bonds_valence && at->num_H ) { - /* proton donors candidates >NH(+)-, >NH2(+), -NH3(+), >OH(+), -OH2(+) */ - /* charged, added p-transfer -- 01-28-2004 */ - nChangeValence = at->charge * cChangeValence; /* +1 or -1; currently only +1 */ - nBondsValence = at->chem_bonds_valence + at->num_H; - if ( nBondsValence == neutral_bonds_valence + nChangeValence && nEndpointValence ) { - *cChargeSubtype = C_SUBTYPE_CHARGED_p_DONOR; /* ignore Phosphorus p-donors for now */ - } - return 0; - } else - if ( at->charge == cCharge && at->valence < at->chem_bonds_valence ) { - /* the requirement at->valence < at->chem_bonds_valence rejects - candidates >NH(+)-, >NH2(+), -NH3(+), >N(+)<, >OH(+), -OH2(+), >O(+)- - Moveable charge requires double bonds; these ions have no double bonds - */ - - /* charged */ - nChangeValence = at->charge * cChangeValence; /* +1 or -1; currently only +1 */ - nBondsValence = at->chem_bonds_valence + at->num_H; - nNumBonds = at->valence + at->num_H; - if ( nBondsValence == neutral_bonds_valence + nChangeValence ) { /* known valence */ - if ( nNumBonds == neutral_valence ) { - /* non-tautomeric: >N(+)=, =O(+)- - possibly tautomeric donor: =NH(+)-, =NH2(+), =OH(+) */ - if ( at->valence == neutral_valence || !nEndpointValence ) { - /* non-tautomeric: >N(+)=, =O(+)-; any suitable P+: >P(+)=, =PH(+)-, =PH2(+) */ - *cChargeSubtype = C_SUBTYPE_CHARGED_NON_TAUT; - } else { - /* possibly tautomeric donor: =NH(+)-, =NH2(+), =OH(+) */ - *cChargeSubtype = C_SUBTYPE_CHARGED_H_DONOR; - } - return 1; - } - if ( nNumBonds == neutral_valence - 1 ) { - /* possibly tutomeric acceptor: =N(+)=, #N(+)-, #NH(+), #O(+) */ - if ( nEndpointValence ) { - *cChargeSubtype = at->num_H? C_SUBTYPE_CHARGED_H_ACCEPT_p_DONOR : C_SUBTYPE_CHARGED_H_ACCEPT; - } else { - /* =P(+)=, #P(+)-, #PH(+) */ - *cChargeSubtype = C_SUBTYPE_CHARGED_NON_TAUT; - } - return 1; /* charge type, charged */ - } - } - - } else - if ( at->charge == 0 || bNegCharge ) { - /* neutral atom or anion, all bonds are single */ - nBondsValence = at->chem_bonds_valence + at->num_H + bNegCharge; /* add fict. bonds to (-) 2004-02-24*/ - nNumBonds = at->valence + at->num_H + bNegCharge; /* add fict. bonds to (-) 2004-02-24*/ - if ( nBondsValence == neutral_bonds_valence ) { - if ( nNumBonds == neutral_valence ) { - /* only single bonds: >N-, >NH, -NH2, -O-, -OH, >P- >PH -PH2 */ - /* >N(-), -NH(-), -O(-). >P(-) -PH(-) */ - if ( at->valence == neutral_valence || !nEndpointValence ) { - /* >N-, -O-, any P(3 single bonds): >P- >PH -PH2 */ - *cChargeSubtype = C_SUBTYPE_NEUTRAL_NON_TAUT; - } else - if ( at->valence < neutral_valence /*&& nEndpointValence */ ) { - /* num_H > 0: >NH -NH2 -OH */ - /* num_H = 0: none C_SUBTYPE_NEUTRAL_H_ACCEPT for now */ - *cChargeSubtype = at->num_H? C_SUBTYPE_NEUTRAL_H_DONOR: C_SUBTYPE_NEUTRAL_H_ACCEPT; - } else { - return 0; - } - return 1; /* charge type, neutral */ - } - if ( nNumBonds == neutral_valence - 1 ) { - /* possibly tautomeric acceptor =N-, =NH, =O or non-taut =P-, =PH */ - if ( nEndpointValence ) { - /* =N-, =NH, =O */ - *cChargeSubtype = C_SUBTYPE_NEUTRAL_H_ACCEPT_p_ACCEPT; - } else { - /* =P-, =PH */ - *cChargeSubtype = C_SUBTYPE_NEUTRAL_NON_TAUT; - } - return 1; /* charge type, (+) => neutral */ - } - } - } - return 0; -} - - - -/*****************************************************************************/ -int GetChargeType( inp_ATOM *atom, int iat, S_CHAR *cChargeSubtype ) -{ - int i, n; - S_CHAR nEndpointValence; - inp_ATOM *at = atom + iat; - - *cChargeSubtype = 0; - /* ignore ion pairs and charges != 1 */ - if ( abs(at->charge) == 1 ) { - for ( i = 0; i < at->valence; i ++ ) { - n = at->neighbor[i]; - /* allow negatively charged tautomeric neighbors 2004-02-26 */ - if ( abs(atom[n].charge + at->charge) < abs(atom[n].charge - at->charge) && !atom[n].endpoint ) { - return -1; /* charges have different signs */ - } - } - } else - if ( at->charge ) { - return -1; /* abs(charge) != 1 */ - } - /* find candidates */ - for ( i = 0; i < NUM_C_TYPES; i ++ ) { - if ( !strcmp( at->elname, CType[i].elname ) && - (!CType[i].num_bonds || CType[i].num_bonds==at->valence && at->nNumAtInRingSystem >= 5) ) { - nEndpointValence = (S_CHAR)get_endpoint_valence(at->el_number ); - if ( bCanBeACPoint( at, CType[i].charge, CType[i].cChangeValence, CType[i].neutral_bonds_valence, - CType[i].neutral_valence, nEndpointValence, cChargeSubtype ) ) { - return CType[i].cChargeType; - } - } - } - return -1; -} - - -/*****************************************************************************/ -int CmpCCandidates( const void *a1, const void *a2 ) -{ - const C_CANDIDATE *c1 = (const C_CANDIDATE *)a1; - const C_CANDIDATE *c2 = (const C_CANDIDATE *)a2; - int ret; - if ( ret = (int)c1->type - (int)c2->type ) - return ret; - if ( ret = (int)c1->subtype - (int)c2->subtype ) - return ret; - ret = (int)c1->atnumber - (int)c2->atnumber; - return ret; -} - - -/*****************************************************************************/ -int RegisterCPoints( C_GROUP *c_group, int *pnum_c, int max_num_c, T_GROUP_INFO *t_group_info, - int point1, int point2, int ctype, inp_ATOM *at, int num_atoms ) -{ - int num_c = *pnum_c, i, i1, i2; - AT_NUMB nGroupNumber = 0, nNewGroupNumber; - - - if ( at[point1].c_point == at[point2].c_point ) { - if ( at[point1].c_point ) - return 0; - memset( c_group+num_c, 0, sizeof(c_group[0]) ); - if ( num_c < max_num_c ) { - c_group[num_c].num[0] = CHARGED_CPOINT(at,point1) + CHARGED_CPOINT(at, point2); - c_group[num_c].num_CPoints += 2; - c_group[num_c].cGroupType = ctype; - /* get next available c-group number */ - for ( i = 0; i < num_c; i ++ ) { - if ( nGroupNumber < c_group[i].nGroupNumber ) - nGroupNumber = c_group[i].nGroupNumber; - } - nGroupNumber ++; - c_group[num_c].nGroupNumber = - at[point1].c_point = - at[point2].c_point = nGroupNumber; - *pnum_c = num_c+1; - /* count protons */ - if ( at[point1].num_H ) { - c_group[num_c].num[1] ++; - } else - if ( at[point2].num_H ) { - c_group[num_c].num[1] ++; - } else - if ( (at[point1].endpoint || at[point2].endpoint) && t_group_info && t_group_info->t_group && t_group_info->num_t_groups ) { - /* !!! add later !!! */ - } - - - return 1; - } - return BNS_CPOINT_ERR; /* overflow */ - } - if ( at[point1].c_point > at[point2].c_point ) { - /* make sure at[point1].c_point < at[point2].c_point */ - i = point1; - point1 = point2; - point2 = i; - } - if ( !at[point1].c_point ) { - /* add a new c-endpoint to an existing c-group */ - nGroupNumber = at[point2].c_point; - for ( i = 0; i < num_c; i ++ ) { - if ( nGroupNumber == c_group[i].nGroupNumber ) { - at[point1].c_point = at[point2].c_point; - c_group[i].num_CPoints ++; - c_group[i].num[0] += CHARGED_CPOINT(at,point1); - return 1; - } - } - return BNS_CPOINT_ERR; /* program error: c-group not found */ - } else { - /* merge two c-groups */ - nNewGroupNumber = at[point1].c_point; - nGroupNumber = at[point2].c_point; - for ( i = 0, i1=i2=-1; i < num_c && (i1 < 0 || i2 < 0); i ++ ) { - if ( nNewGroupNumber == c_group[i].nGroupNumber ) { - i1 = i; - continue; - } - if ( nGroupNumber == c_group[i].nGroupNumber ) { - i2 = i; - continue; - } - } - if ( i1 < 0 || i2 < 0 ) { - return BNS_CPOINT_ERR; /* at least one not found */ - } - - c_group[i1].num[0] += c_group[i2].num[0]; - c_group[i1].num_CPoints += c_group[i2].num_CPoints; - num_c --; - if ( num_c > i2 ) { - memmove( c_group+i2, c_group+i2+1, ( num_c - i2)*sizeof(c_group[0]) ); - } - *pnum_c = num_c; - /* renumber c-groups */ - for ( i = 0; i < num_c; i ++ ) { - if ( c_group[i].nGroupNumber > nGroupNumber ) { - c_group[i].nGroupNumber --; - } - } - /* renumber c-points */ - for ( i = 0; i < num_atoms; i ++ ) { - if ( at[i].c_point > nGroupNumber ) { - at[i].c_point --; - } else - if ( at[i].c_point == nGroupNumber ) { - at[i].c_point = nNewGroupNumber; - } - } - return 1; - } -} - - - -/*****************************************************************************/ -int MarkChargeGroups(inp_ATOM *at, int num_atoms, - C_GROUP_INFO *c_group_info, T_GROUP_INFO *t_group_info, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD) -{ - int nNumChanges = 0; - - if ( c_group_info && c_group_info->c_candidate && c_group_info->max_num_candidates > 0 ) { - int i, i1, i2, i3, j, num_tested; - C_CANDIDATE *c_candidate = c_group_info->c_candidate; - int nMaxNumCandidates = c_group_info->max_num_candidates; - int nNumCandidates = c_group_info->num_candidates; - S_CHAR c_type, c_subtype; - int iat1, iat2, ret, nDelta; - - if ( nNumCandidates == -1 ) - { - nNumCandidates = 0; /* 2004-02-26 they could appear after t-group discovery */ - /*return 0;*/ - } - if ( nNumCandidates == 0 ) - { - for ( i = 0, nNumCandidates = 0; i < num_atoms; i ++ ) - { - if ( 0 <= (c_type = GetChargeType( at, i, &c_subtype )) ) - { - if ( nNumCandidates >= nMaxNumCandidates ) - { - return BNS_VERT_EDGE_OVFL; - } - c_candidate[nNumCandidates].atnumber = i; - c_candidate[nNumCandidates].type = c_type; - c_candidate[nNumCandidates].subtype = c_subtype; - nNumCandidates ++; - } - } - if ( nNumCandidates <= 1 ) - { - c_group_info->num_candidates = -1; /* no candidate exists */ - return 0; - } - } - /* sorting keys: (1) atom type (N,P); (2) uncharged=16/charged=0; (3) other; - atom-charged-N .... i1 - ... - atom-charged-N - atom-neutral-N .... i2 - ... - atom-neutral-N - atom-charged-P .... i3 ... i1 - ... - atom-charged-P - atom-neutral-P ........... i2 - ... - atom-neutral-P - end. ........... i3 - */ - qsort(c_candidate, nNumCandidates, sizeof(c_candidate[0]), CmpCCandidates); - - i1 = 0; - num_tested = 0; - nDelta = 0; - - while ( i1 < nNumCandidates ) - { - - /* the the first charged candidate of a new atom type */ - for (; i1 < nNumCandidates && (c_candidate[i1].subtype & C_SUBTYPE_NEUTRAL); i1 ++) - ; - if ( i1 == nNumCandidates ) - break; /* not found */ - - /* bypass other charged candidates of the same atom type */ - for ( i2 = i1+1; i2 < nNumCandidates && - c_candidate[i2].type == c_candidate[i1].type && - !(c_candidate[i2].subtype & C_SUBTYPE_NEUTRAL); i2++ ) - ; - if ( i2 == nNumCandidates ) - break; /* no neutral candidates */ - - /* find next to the last neutral candidate of the same atom type */ - for ( i3 = i2; i3 < nNumCandidates && - c_candidate[i3].type == c_candidate[i1].type; i3 ++ ) - ; - - if ( i3 == i2 ) - { - /* no neutral candidates found */ - if ( i2 < nNumCandidates ) - { - i1 = i3; - continue; /* move to the next atom type */ - } - break; /* nothing more to do */ - } - - /* found charged candidates: i1...i2-1; neutral candidates: i2...i3-1 */ - for ( i = i1; i < i2; i ++ ) - { - iat1 = c_candidate[i].atnumber; - for ( j = i2; j < i3; j ++ ) - { - /* check alt path at[iat1]=-=-...-at[iat2]; at[iat1] is charged, at[iat2] is neutral */ - num_tested ++; - iat2 = c_candidate[j].atnumber; - if ( at[iat1].c_point && at[iat1].c_point == at[iat2].c_point ) - continue; - ret = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, iat1, iat2, ALT_PATH_MODE_CHARGE ); - if ( IS_BNS_ERROR( ret ) ) - { - return ret; - } - if ( ret & 1 ) - { - nDelta = (ret & ~3) >> 2; - nNumChanges += (ret & 2); - ret = RegisterCPoints( c_group_info->c_group, &c_group_info->num_c_groups, - c_group_info->max_num_c_groups, t_group_info, - iat1, iat2, c_candidate[i1].type, at, num_atoms ); - if ( IS_BNS_ERROR( ret ) ) - { - return ret; - } - if ( nDelta ) - { - goto quick_exit; - } - } - } - } - i1 = i3; - } -quick_exit: - if ( c_group_info->num_candidates == 0 ) - { - /* first time: initialize */ - c_group_info->num_candidates = num_tested? nNumCandidates : -1; /* no candidate exists */ - } - - } - return nNumChanges; -} - - - -/*****************************************************************************/ -int GetSaltChargeType(inp_ATOM *at, int at_no, T_GROUP_INFO *t_group_info, int *s_subtype ) -{ - static int el_number_C = 0; - static int el_number_O = 0; - static int el_number_S = 0; - static int el_number_Se = 0; - static int el_number_Te = 0; - -/* - type (returned value): - -1 => ignore - 0 => oxygen - subtype: - 1 = SALT_DONOR_H => has H - 2 = SALT_DONOR_Neg => has (-) charge - 4 = SALT_ACCEPTOR => may be an acceptor of H or (-), but not necessarily - - O-atom should be: - - a terminal atom - - connected to unsaturated, uncharged, non-radical atom C that has chemical valence 4: - H-donors: =CH-OH, =C(-X)-OH - possible H-acceptors: -CH=O, >C=O - H-acceptors are true if O is tautomeric -*/ - int iC, tg, i, type; - /* one-time initialization */ - if ( !el_number_O ) { - el_number_C = get_periodic_table_number( "C" ); - el_number_O = get_periodic_table_number( "O" ); - el_number_S = get_periodic_table_number( "S" ); - el_number_Se = get_periodic_table_number( "Se" ); - el_number_Te = get_periodic_table_number( "Te" ); - } - *s_subtype = 0; /* initialize the output */ - /* check whether it is a candidate */ - if ( at[at_no].valence != 1 || - at[at_no].radical && at[at_no].radical != RADICAL_SINGLET || - at[at_no].charge < -1 || - at[at_no].charge > 0 && !at[at_no].c_point ) { - return -1; - } - - if ( at[at_no].el_number == el_number_O || - at[at_no].el_number == el_number_S || - at[at_no].el_number == el_number_Se || - at[at_no].el_number == el_number_Te ) { - type = 0; /* terminal oxygen atom, needs more to be checked... */ - } else { - type = -1; /* ignore this atom */ - } - - if ( type < 0 || - at[at_no].chem_bonds_valence + at[at_no].num_H != - get_el_valence(at[at_no].el_number, at[at_no].charge, 0) ) { - return -1; /* non-standard valence or not an oxygen */ - } - - iC = at[at_no].neighbor[0]; - -#if ( SALT_WITH_PROTONS == 1 ) - if ( at[iC].el_number != el_number_C || - at[iC].chem_bonds_valence + at[iC].num_H != 4 || /* allow =C(H)-OH or -C(H)=O */ - at[iC].charge || - at[iC].radical && at[iC].radical != RADICAL_SINGLET || - at[iC].valence == at[iC].chem_bonds_valence ) { - return -1; /* oxigen is connected to a wrong atom */ - } -#else - if ( at[iC].el_number != el_number_C || - at[iC].num_H || - at[iC].chem_bonds_valence != 4 || /* allow only no H on C */ - at[iC].charge || - at[iC].radical && at[iC].radical != RADICAL_SINGLET || - at[iC].valence == at[iC].chem_bonds_valence ) { - return -1; /* oxigen is connected to a wrong atom */ - } -#endif - if ( (tg = at[at_no].endpoint) && t_group_info && t_group_info->t_group ) { - /* O-atom is in a tautomeric group */ - for ( i = 0; i < t_group_info->num_t_groups; i ++ ) { - if ( tg == t_group_info->t_group[i].nGroupNumber ) { - /* - t_group_info->t_group[i].num[0] = number of attached H-atoms and negative charges - t_group_info->t_group[i].num[1] = number of attached negative charges - */ - if ( t_group_info->t_group[i].num[0] > t_group_info->t_group[i].num[1] ) { - *s_subtype |= SALT_DONOR_H; /* has H */ - } - if ( t_group_info->t_group[i].num[1] ) { - *s_subtype |= SALT_DONOR_Neg; /* has (-) */ - } - *s_subtype |= SALT_ACCEPTOR; /* there is always an acceptor in a t-group */ - return type; - } - } - return -1; /* error: t-group not found */ - } - /* O is not not in a tautomeric group */ - /* assume valence(O-) < valence(O) < valence(O+) */ - if ( at[at_no].charge == -1 ) { - *s_subtype |= SALT_DONOR_Neg; /* has (-) */ - } - if ( at[at_no].charge <= 0 && at[at_no].num_H ) { - *s_subtype |= SALT_DONOR_H; /* has H */ - } - if ( at[at_no].charge == 0 && at[at_no].chem_bonds_valence == 2 ) { - *s_subtype |= SALT_ACCEPTOR; - } - /* since O cannot be a charge point, the following cannot happen: */ - if ( at[at_no].charge == 1 && at[at_no].c_point && at[at_no].chem_bonds_valence == 2 && at[at_no].num_H ) { - *s_subtype |= SALT_DONOR_H; /* has H */ - } - return type; -} - - - -/*****************************************************************************/ -int bDoNotMergeNonTautAtom(inp_ATOM *at, int at_no) -{ - static int el_number_N = 0; - - if ( !el_number_N ) { - el_number_N = get_periodic_table_number( "N" ); - } - if ( at[at_no].el_number == el_number_N ) - { - return 1; - } - return 0; -} - - - -/*****************************************************************************/ -int GetOtherSaltChargeType( inp_ATOM *at, int at_no, T_GROUP_INFO *t_group_info, int *s_subtype, int bAccept_O ) -{ - /* static int el_number_C = 0; */ - /* static int el_number_N = 0; */ - static int el_number_O = 0; - static int el_number_S = 0; - static int el_number_Se = 0; - static int el_number_Te = 0; - -/* - type (returned value): - -1 => ignore - 1 => not an oxygen - subtype: - 1 = SALT_DONOR_H => has H - 2 = SALT_DONOR_Neg => has (-) charge - 4 = SALT_ACCEPTOR => may be an acceptor of H or (-), but not necessarily - - the atom should be: - - a tautomeric endpoint atom - - connected to possible centerpoint atom - - another description of the atom searched here: - - any possibly tautomeric atom adjacent to a possibly centerpoint - that has at least one double bond (possibly if positively charged); - if eif.cAcceptor then the bond between the atom and the centerpoint must be possibly double - if eif.cAcceptor then the bond must be possibly single - Donors that belong to a t-group are also acceptors - - -*/ - int tg, i, j, type, endpoint_valence, num_centerpoints, bond_type, centerpoint; - ENDPOINT_INFO eif; - /* one-time initialization */ - if ( !el_number_O && !bAccept_O ) { - /* el_number_C = get_periodic_table_number( "C" ); */ - /* el_number_N = get_periodic_table_number( "N" ); */ - el_number_O = get_periodic_table_number( "O" ); - el_number_S = get_periodic_table_number( "S" ); - el_number_Se = get_periodic_table_number( "Se" ); - el_number_Te = get_periodic_table_number( "Te" ); - } - *s_subtype = 0; /* initialize the output */ - if ( !bAccept_O /* only N */ && - (at[at_no].el_number == el_number_O || - at[at_no].el_number == el_number_S || - at[at_no].el_number == el_number_Se || - at[at_no].el_number == el_number_Te ) ) { - return -1; /* we are not looking for oxygen here */ - } - - type = 1; - if ( !(endpoint_valence = nGetEndpointInfo( at, at_no, &eif )) ) { - return -1; /* not a possible endpoint */ - } else { - /* at[at_no] is not not in a tautomeric group; use eif previously filled out by nGetEndpointInfo */ - /* check whether there is adjacent atom-candidate for a centerpoint */ - num_centerpoints = 0; - for ( j = 0; j < at[at_no].valence; j ++ ) { - bond_type = (int)at[at_no].bond_type[j] & BOND_TYPE_MASK; - centerpoint = (int)at[at_no].neighbor[j]; /* a centerpoint candidate */ - if ( ( eif.cAcceptor && (bond_type == BOND_DOUBLE || - bond_type == BOND_ALTERN || /* possibly double */ - bond_type == BOND_ALT12NS || - bond_type == BOND_TAUTOM ) || - eif.cDonor && (bond_type == BOND_SINGLE || - bond_type == BOND_ALTERN || /* possibly single */ - bond_type == BOND_ALT12NS || - bond_type == BOND_TAUTOM ) ) && - (at[centerpoint].chem_bonds_valence > at[centerpoint].valence || - /* check for possible endpoint added 2004-02-24 */ - at[centerpoint].chem_bonds_valence == at[centerpoint].valence && - (at[centerpoint].endpoint || at[centerpoint].c_point) /* tautomerism or charge may increment at[centerpoint].chem_bonds_valence*/ ) && - is_centerpoint_elem( at[centerpoint].el_number ) ) { - num_centerpoints ++; - break; /* at least one possibly centerpoint neighbor has been found */ - } - } - if ( !num_centerpoints ) { - return -1; - } - /* moved here from just after "type = 1;" line 2004-02-26 */ - if ( (tg = at[at_no].endpoint) && t_group_info && t_group_info->t_group ) { - /* atom is in a tautomeric group */ - for ( i = 0; i < t_group_info->num_t_groups; i ++ ) { - if ( tg == t_group_info->t_group[i].nGroupNumber ) { - /* - t_group_info->t_group[i].num[0] = number of attached H-atoms and negative charges - t_group_info->t_group[i].num[1] = number of attached negative charges - */ - if ( t_group_info->t_group[i].num[0] > t_group_info->t_group[i].num[1] ) { - *s_subtype |= SALT_DONOR_H; /* has H */ - } - if ( t_group_info->t_group[i].num[1] ) { - *s_subtype |= SALT_DONOR_Neg; /* has (-) */ - } - *s_subtype |= SALT_ACCEPTOR; /* there is always an acceptor in a t-group */ - return type; - } - } - return -1; /* error: t-group not found */ - } - - if ( eif.cAcceptor ) { - *s_subtype |= SALT_ACCEPTOR; - } - if ( eif.cDonor ) { - if ( at[at_no].charge == -1 ) { - *s_subtype |= SALT_DONOR_Neg; /* has (-) */ - } - if ( at[at_no].num_H ) { - *s_subtype |= SALT_DONOR_H; /* has H */ - } - } - } - return type; -} - - - -/*****************************************************************************/ -int GetOtherSaltType( inp_ATOM *at, int at_no, int *s_subtype ) -{ - static int el_number_C = 0; - /* static int el_number_N = 0; */ - /* static int el_number_O = 0; */ - static int el_number_S = 0; - static int el_number_Se = 0; - static int el_number_Te = 0; - -/* - type (returned value): - -1 => ignore - 2 => found: SH - proton donor -CH2-SH, >CH-SH, >C< S(-) - proton acceptor -CH2-S(-), >CH-S(-), >C< - subtype: - 1 = SALT_DONOR_H => has H - 2 = SALT_DONOR_Neg => has (-) charge - 4 = SALT_ACCEPTOR => may be an acceptor of H or (-), but not necessarily - - non-O-atom should be: - - a tautomeric endpoint atom - - connected to possible middle point atom -*/ - int type, endpoint_valence, bond_type, centerpoint; - ENDPOINT_INFO eif; - - if ( at[at_no].valence != 1 || at[at_no].chem_bonds_valence != 1 || - 1 != (at[at_no].num_H==1) + (at[at_no].charge==-1) ) { - return -1; - } - /* one-time initialization */ - if ( !el_number_S ) { - el_number_C = get_periodic_table_number( "C" ); - /* el_number_N = get_periodic_table_number( "N" ); */ - /* el_number_O = get_periodic_table_number( "O" ); */ - el_number_S = get_periodic_table_number( "S" ); - el_number_Se = get_periodic_table_number( "Se" ); - el_number_Te = get_periodic_table_number( "Te" ); - } - *s_subtype = 0; /* initialize the output */ - if ( !(at[at_no].el_number == el_number_S || - at[at_no].el_number == el_number_Se || - at[at_no].el_number == el_number_Te ) ) { - return -1; /* we are not looking for oxygen here */ - } - - type = 2; /* non-tautomeric p-donor or acceptor: C-SH, C-S(-) */ - - if ( !(endpoint_valence = nGetEndpointInfo( at, at_no, &eif )) || - eif.cMoveableCharge && !at[at_no].c_point || !eif.cDonor || eif.cAcceptor ) { - return -1; /* not a possible -SH or -S(-) */ - } else { - /* at[at_no] is not not in a tautomeric group; use eif previously filled out by nGetEndpointInfo */ - /* check whether there is adjacent atom-candidate for a centerpoint */ - centerpoint = (int)at[at_no].neighbor[0]; - bond_type = (int)at[at_no].bond_type[0] & BOND_TYPE_MASK; - if ( at[centerpoint].el_number != el_number_C || - at[centerpoint].charge || - at[centerpoint].radical && at[centerpoint].radical != RADICAL_SINGLET || - at[centerpoint].valence != at[centerpoint].chem_bonds_valence ) { - return -1; /* not a carbon with all single bonds */ - } - if ( at[at_no].num_H == 1 ) { - *s_subtype |= SALT_p_DONOR; - } else - if ( at[at_no].charge == -1 ) { - *s_subtype |= SALT_p_ACCEPTOR; - } else { - return -1; - } - } - return type; -} - -/********************************************************************************************************/ -/* new version: merge all, check alt paths, then unmerge unreachable O-atoms if any */ -/* Check for oxygen negative charge-H tautomerism (Salts) - allowed long-range tautomerism; more than one H or (-) can be moved, for example: - HO-C=C-O(-) O=C-C=O - / \ / \ - R R R R - | | => | | - R' R' R' R' - \ / \ / - O=C-C=O HO-C=C-O(-) - - To check: - - | | - -add all possible HO-C=, O=C, (-)O-C= (including all containing O t-groups) into one t-group; - -temporarily disconnect one of previously not belonging to any t-group O-atoms from the one t-group; - -find whether there is an alt path allowing H or (-) to migrate - from the temp. disconnected O to any one left in the group. - If the alt path does not exist then the temp. disconnected atom does not - participate in the H/(-) migrartion and it will be unmarked/unmerged. - -*/ - - - -/*****************************************************************************/ -int comp_candidates( const void *a1, const void *a2 ) -{ - const S_CANDIDATE *s1 = (const S_CANDIDATE *)a1; - const S_CANDIDATE *s2 = (const S_CANDIDATE *)a2; - int ret; - if ( s1->type >= 0 /* enabled < */ && s2->type < 0 /* disabled */ ) - return -1; /* enabled goes first */ - if ( s1->type < 0 /* disabled > */ && s2->type >= 0 /* enabled */ ) - return 1; - if ( s1->endpoint && !s2->endpoint ) - return -1; /* tautomeric goes first; only tautomeric may be disabled */ - if ( !s1->endpoint && s2->endpoint ) - return 1; /* tautomeric goes first; only tautomeric may be disabled */ - if ( s1->endpoint && s2->endpoint && (ret = (int)s1->endpoint - (int)s2->endpoint) ) { - return ret; - } - return (int)s1->atnumber - (int)s2->atnumber; -} - - - -/*****************************************************************************/ -int MarkSaltChargeGroups2 ( inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, - T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, - struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD ) -{ -/* BNS_EDGE_FORBIDDEN_TEMP */ -#define ALT_PATH_FOUND (MAX_ATOMS+1) -#define NO_ENDPOINT (MAX_ATOMS+2) /* the two defines must be different */ -#define DISABLE_CANDIDATE 10 -#define cPAIR(a,b) cPair[a+b*nNumLeftCandidates] -#define ACCEPTOR_PAIR 1 -#define DONOR_PAIR 2 - - int nNumChanges = 0, nNumOtherChanges = 0, nNumAcidicChanges = 0, nTotNumChanges = 0; - S_CHAR *cPair = NULL; - T_ENDPOINT *EndPoint = NULL; - if ( s_group_info && s_group_info->s_candidate && s_group_info->max_num_candidates > 0 ) { - int i, j, i1, j1; - S_CANDIDATE *s_candidate = s_group_info->s_candidate; - int nMaxNumCandidates = s_group_info->max_num_candidates; - int nNumCandidates = s_group_info->num_candidates; - int nNumOtherCandidates = s_group_info->num_other_candidates; - int nNumPOnlyCandidates = s_group_info->num_p_only_candidates; - int nNumLeftCandidates = 0; - int nNumMarkedCandidates = 0; - int s_type, s_subtype; - int ret, nDelta; - int bHardAddedRemovedProtons = t_group_info && (t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT); - - int s_subtype_all = 0; - int nDonorPairs, nAcceptorPairs, nCurDonorPairs, nCurAcceptorPairs, bAlreadyTested; -/* - ENDPOINT_INFO eif; -*/ - -#if ( IGNORE_TGROUP_WITHOUT_H == 1 ) - int bTGroupHasNegativeChargesOnly = 1; -#endif - /*return 0;*/ /* debug only */ - - i1 = -1; - - if ( nNumCandidates <= -2 || !t_group_info || !t_group_info->t_group ) { - return 0; - } - /*************************************************************************/ - /* find all candidates including those with differen s_type (other type) */ - /*************************************************************************/ - for ( i = 0, nNumCandidates = nNumOtherCandidates = nNumPOnlyCandidates = 0; i < num_atoms; i ++ ) { - if ( 0 == (s_type = GetSaltChargeType( at, i, t_group_info, &s_subtype )) || - /* -C=O or =C-OH, O = S, Se, Te */ - 1 == (s_type = GetOtherSaltChargeType( at, i, t_group_info, &s_subtype, 1/* bAccept_O*/ )) || - /* =Z-MH or -Z=M, Z = centerpoint, M = endpoint, other than above */ - 2 == (s_type = GetOtherSaltType( at, i, &s_subtype ) ) || - ( bHardAddedRemovedProtons && 4 == (s_type = bIsHardRemHCandidate( at, i, &s_subtype ) ) ) - /* >C-SH, >C-S(-); S=S,Se,Te */ - ) { - - if ( nNumCandidates >= nMaxNumCandidates ) { - return BNS_VERT_EDGE_OVFL; - } - s_candidate[nNumCandidates].atnumber = i; - s_candidate[nNumCandidates].type = s_type; - s_candidate[nNumCandidates].subtype = s_subtype; - s_candidate[nNumCandidates].endpoint = at[i].endpoint; - nNumCandidates ++; - nNumOtherCandidates += (1 == s_type); - s_subtype_all |= s_subtype; - i1 = i; /* save a representative of a tautomeric group */ - } - } - - if ( nNumCandidates <= 1 || /* TG_FLAG_ALLOW_NO_NEGTV_O <=> CHARGED_SALTS_ONLY=0 */ - !(s_subtype_all & SALT_ACCEPTOR) || - (((t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O) || - (t_group_info->bTautFlagsDone & TG_FLAG_FOUND_SALT_CHARGES_DONE) || - (t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT)) ? - !(s_subtype_all & (SALT_DONOR)): - (!(s_subtype_all & SALT_DONOR_Neg) || nNumOtherCandidates == nNumCandidates )) - ) { - s_group_info->num_candidates = 0; /* no candidate exists */ - return 0; - } - if ( !(s_subtype_all & (SALT_DONOR_Neg) ) ) { - t_group_info->bTautFlagsDone |= TG_FLAG_ALLOW_NO_NEGTV_O_DONE; - } - - /************************************************************************************/ - /* Mark redundant candidates so that only one candidate from one t-group is left in */ - /************************************************************************************/ - for ( i = 0; i < nNumCandidates; i ++ ) { - if ( 2 == s_candidate[nNumCandidates].type ) { - s_candidate[i].type -= DISABLE_CANDIDATE; /* disable >C-SH candidates */ - nNumLeftCandidates ++; /* count rejected */ - continue; - } - if ( s_candidate[i].endpoint ) { - for ( j = i-1; 0 <= j; j -- ) { - if ( s_candidate[i].endpoint == s_candidate[j].endpoint ) { - s_candidate[i].type -= DISABLE_CANDIDATE; /* disable subsequent redundant */ - nNumLeftCandidates ++; /* count rejected */ - break; - } - } - } - } - nNumLeftCandidates = nNumCandidates - nNumLeftCandidates; /* subtract num. rejected from the total */ - s_group_info->num_candidates = 0; /* reinit next time */ - /*********************************************************************/ - /* reorder so that all disabled are at the end, tautomeric are first */ - /*********************************************************************/ - qsort ( s_candidate, nNumCandidates, sizeof(s_candidate[0]), comp_candidates ); - cPair = (S_CHAR *)inchi_calloc( nNumLeftCandidates*nNumLeftCandidates, sizeof(cPair[0]) ); - if ( !cPair ) { - /*printf("BNS_OUT_OF_RAM-6\n");*/ - nTotNumChanges = BNS_OUT_OF_RAM; - goto quick_exit; - } - nDonorPairs = nAcceptorPairs = 0; - /**********************************************************************/ - /* Find whether we have at least one donor pair and one acceptor pair */ - /**********************************************************************/ - for ( i = 0; i < nNumLeftCandidates; i ++ ) { - nCurDonorPairs = nCurAcceptorPairs = 0; - for ( j = 0; j <= i; j ++ ) { - if ( i == j && !s_candidate[i].endpoint ) { - continue; /* same non-taut atom. However, success for i==j means * - * that the whole tautomeric group may donate or accept 2H */ - } - /* check for acceptor pair */ - if ( (s_candidate[i].subtype & SALT_ACCEPTOR) && (s_candidate[j].subtype & SALT_ACCEPTOR) && - (ret = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, s_candidate[i].atnumber, - s_candidate[j].atnumber, ALT_PATH_MODE_ADD2H_TST ))) { - if ( IS_BNS_ERROR( ret ) ) { - nTotNumChanges = ret; - goto quick_exit; - } - if ( ret & 1 ) { - nDelta = (ret & ~3) >> 2; - /*nNumChanges += (ret & 2);*/ - if ( nDelta ) { - /* alt path unleashed previously localized radicals and they annihilated */ - nNumChanges = 0; - nTotNumChanges = BNS_RADICAL_ERR; - goto quick_exit; - } - cPAIR(i,j) |= ACCEPTOR_PAIR; /* the result: mark the pair */ - /*cPAIR(j,i) |= ACCEPTOR_PAIR;*/ - } - } - /* check for donor pair */ - if ( (s_candidate[i].subtype & SALT_DONOR) && (s_candidate[j].subtype & SALT_DONOR) && - (ret = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, s_candidate[i].atnumber, - s_candidate[j].atnumber, ALT_PATH_MODE_REM2H_TST ))) { - if ( IS_BNS_ERROR( ret ) ) { - nTotNumChanges = ret; - goto quick_exit; - } - if ( ret & 1 ) { - nDelta = (ret & ~3) >> 2; - /*nNumChanges += (ret & 2);*/ - if ( nDelta ) { - /* alt path unleashed previously localized radicals and they annihilated */ - nNumChanges = 0; - nTotNumChanges = BNS_RADICAL_ERR; - goto quick_exit; - } - cPAIR(i,j) |= DONOR_PAIR; /* the result: mark the pair */ - /*cPAIR(j,i) |= ACCEPTOR_PAIR;*/ - } - } - /* since the results will be used later to change bonds, check only now */ - /* when both results for (i,j) have been obtained. */ - if ( cPAIR(i,j) & ACCEPTOR_PAIR ) { - nCurAcceptorPairs ++; - if ( nDonorPairs ) { - /* find donor pair (i1,j1) such that i!=i1, i!=j1, j!=i1, j!=j1 */ - for ( i1 = 0; i1 < i; i1 ++ ) { - for ( j1 = 0; j1 <= i1; j1 ++ ) { - /* here always j1 < i && i1 < i therefore we do not compare i to i1 or j1 */ - if ( j1 != j && i1 != j && (cPAIR(i1,j1) & DONOR_PAIR) ) { - /* both the donor and the acceptor pairs have been found */ - goto bFound2Pairs; - } - } - } - } - } - if ( cPAIR(i,j) & DONOR_PAIR ) { - nCurDonorPairs ++; - if ( nAcceptorPairs ) { - /* find acceptor pair (i1,j1) such that i!=i1, i!=j1, j!=i1, j!=j1 */ - for ( i1 = 0; i1 < i; i1 ++ ) { - for ( j1 = 0; j1 <= i1; j1 ++ ) { - /* here always j1 < i && i1 < i therefore we do not compare i to i1 or j1 */ - if ( j1 != j && i1 != j && (cPAIR(i1,j1) & ACCEPTOR_PAIR) ) { - /* both the donor and the acceptor pairs have been found */ - goto bFound2Pairs; - } - } - } - } - } - } - nDonorPairs += nCurDonorPairs; - nAcceptorPairs += nCurAcceptorPairs; - } - /* nothing has been found */ - nNumChanges = 0; - inchi_free( cPair ); - cPair = NULL; - goto quick_exit; - - - /* both the donor and the acceptor pairs have been found */ -bFound2Pairs: - /* first, try already found pairs */ - i1 = i; - j1 = j; - - /* Find all possible donor and acceptor pairs */ - nNumMarkedCandidates = 0; - for ( i = 0; i < nNumLeftCandidates; i ++ ) { - nCurDonorPairs = nCurAcceptorPairs = 0; - for ( j = 0; j <= i; j ++ ) { - bAlreadyTested = (i < i1 || i == i1 && j <= j1); - if ( bAlreadyTested && (cPAIR(i,j) & ACCEPTOR_PAIR) || !bAlreadyTested ) { - /* checking for acceptor pair */ - if ( (s_candidate[i].subtype & SALT_ACCEPTOR) && (s_candidate[j].subtype & SALT_ACCEPTOR) && - (ret = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, s_candidate[i].atnumber, - s_candidate[j].atnumber, ALT_PATH_MODE_ADD2H_CHG ))) { - if ( IS_BNS_ERROR( ret ) ) { - nTotNumChanges = ret; - goto quick_exit; - } - if ( ret & 1 ) { - nDelta = (ret & ~3) >> 2; - nNumChanges += (ret & 2); - if ( nDelta ) { - /* alt path unleashed previously localized radicals and they annihilated */ - nNumChanges = 0; - nTotNumChanges = BNS_RADICAL_ERR; - goto quick_exit; - } - cPAIR(i,j) |= ACCEPTOR_PAIR; - /*cPAIR(j,i) |= ACCEPTOR_PAIR;*/ - nCurAcceptorPairs += !bAlreadyTested; - if ( !(s_candidate[i].subtype & SALT_SELECTED) ) { - s_candidate[i].subtype |= SALT_SELECTED; - nNumMarkedCandidates ++; - if ( !s_candidate[i].endpoint && s_candidate[i].type ) { - nNumOtherChanges ++; - } else { - nNumAcidicChanges ++; - } - } - if ( !(s_candidate[j].subtype & SALT_SELECTED) ) { - s_candidate[j].subtype |= SALT_SELECTED; - nNumMarkedCandidates ++; - if ( !s_candidate[j].endpoint && s_candidate[j].type ) { - nNumOtherChanges ++; - } else { - nNumAcidicChanges ++; - } - } - } - } - } - if ( bAlreadyTested && (cPAIR(i,j) & DONOR_PAIR) || !bAlreadyTested ) { - /* checking for donor pair */ - if ( (s_candidate[i].subtype & SALT_DONOR) && (s_candidate[j].subtype & SALT_DONOR) && - (ret = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, s_candidate[i].atnumber, - s_candidate[j].atnumber, ALT_PATH_MODE_REM2H_CHG ))) { - if ( IS_BNS_ERROR( ret ) ) { - nTotNumChanges = ret; - goto quick_exit; - } - if ( ret & 1 ) { - nDelta = (ret & ~3) >> 2; - nNumChanges += (ret & 2); - if ( nDelta ) { - /* alt path unleashed previously localized radicals and they annihilated */ - nNumChanges = 0; - nTotNumChanges = BNS_RADICAL_ERR; - goto quick_exit; - } - cPAIR(i,j) |= DONOR_PAIR; - /*cPAIR(j,i) |= ACCEPTOR_PAIR;*/ - nCurDonorPairs += !bAlreadyTested; - if ( !(s_candidate[i].subtype & SALT_SELECTED) ) { - s_candidate[i].subtype |= SALT_SELECTED; - nNumMarkedCandidates ++; - if ( !s_candidate[i].endpoint && s_candidate[i].type ) { - nNumOtherChanges ++; - } else { - nNumAcidicChanges ++; - } - } - if ( !(s_candidate[j].subtype & SALT_SELECTED) ) { - s_candidate[j].subtype |= SALT_SELECTED; - nNumMarkedCandidates ++; - if ( !s_candidate[j].endpoint && s_candidate[j].type ) { - nNumOtherChanges ++; - } else { - nNumAcidicChanges ++; - } - } - } - } - } - } - nDonorPairs += nCurDonorPairs; - nAcceptorPairs += nCurAcceptorPairs; - } - inchi_free( cPair ); - cPair = NULL; - - if ( nNumMarkedCandidates ) { - EndPoint = (T_ENDPOINT *)inchi_calloc( nNumMarkedCandidates, sizeof(EndPoint[0])); - if ( !EndPoint ) { - /*printf("BNS_OUT_OF_RAM-7\n");*/ - nTotNumChanges = BNS_OUT_OF_RAM; - goto quick_exit; - } - for ( i = 0, j = 0; i < nNumLeftCandidates; i ++ ) { - if ( s_candidate[i].subtype & SALT_SELECTED ) { - s_candidate[i].subtype ^= SALT_SELECTED; /* remove the flag */ - if ( j < nNumMarkedCandidates ) { - i1 = s_candidate[i].atnumber; /* save a representative of the t-group to be created */ - AddEndPoint( EndPoint+j, at, i1 ); - } - j ++; - } - } - if ( j != nNumMarkedCandidates ) { - nTotNumChanges = BNS_PROGRAM_ERR; - goto quick_exit; - } - /* merge all marked atoms and their t-groups into one t-group */ - ret = RegisterEndPoints( t_group_info, EndPoint, nNumMarkedCandidates, at, num_atoms, c_group_info, pBNS ); - if ( ret == -1 ) { - ret = BNS_PROGRAM_ERR; - } - if ( ret < 0 ) { - nTotNumChanges = ret; - goto quick_exit; - } - nTotNumChanges += (ret > 0); - inchi_free( EndPoint ); - EndPoint = NULL; - - if ( nNumMarkedCandidates ) { - for ( i = nNumLeftCandidates; i < nNumCandidates; i ++ ) { - s_candidate[i].type += DISABLE_CANDIDATE; - j1 = s_candidate[i].atnumber; - if ( at[j1].endpoint == at[i1].endpoint ) { - if ( !s_candidate[i].endpoint && s_candidate[i].type ) { - nNumOtherChanges ++; - } else { - nNumAcidicChanges ++; - } - } - } - } else { - for ( i = nNumLeftCandidates; i < nNumCandidates; i ++ ) { - s_candidate[i].type += DISABLE_CANDIDATE; - } - } - - /* find whether the new t-group have any movable H */ - for ( i = 0, bTGroupHasNegativeChargesOnly = 0; i < t_group_info->num_t_groups; i ++ ) { - if ( t_group_info->t_group[i].nGroupNumber == at[i1].endpoint && - t_group_info->t_group[i].num[0] == t_group_info->t_group[i].num[1] ) { - bTGroupHasNegativeChargesOnly = 1; - break; - } - } - } - nTotNumChanges = ( nTotNumChanges > 0); - -#if ( IGNORE_TGROUP_WITHOUT_H == 1 ) - if ( nTotNumChanges && bTGroupHasNegativeChargesOnly ) { - nTotNumChanges = 2; /* means no moveable H has been affected */ - } -#endif - } - -quick_exit: - if ( nNumOtherChanges && nTotNumChanges == 1 ) { - nTotNumChanges = 5; /* not only acidic atoms merged */ - } - if ( cPair ) { - inchi_free( cPair ); - /*cPair = NULL;*/ - } - if ( EndPoint ) { - inchi_free ( EndPoint ); - /*EndPoint = NULL;*/ - } - return nTotNumChanges; /* 0=>no changes, 1=>new salt tautomerism found, 2=>only new charge tautomerism found */ -#undef ALT_PATH_FOUND -#undef NO_ENDPOINT -} -/********************************************************************************************************/ -/* regular one-path version: find alt paths then merge */ -/* Check for oxygen negative charge-H tautomerism (Salts) - allowed long-range tautomerism; only one H or (-) can be moved, for example: - HO-C=X-Y=Z-...-C=O => O=C-X=Y-Z=...=C-OH -*/ - -#if ( SALT_WITH_PROTONS == 1 ) - -#define MAX_LOCAL_TGNUM 0 /* was 32; disable since it has not been used */ - -#if ( MAX_LOCAL_TGNUM > 0 ) -typedef struct tagTGroupData { - S_SHORT nGroupNumber; /* t-group number from t_group_info->t_group->nGroupNumber */ - S_SHORT nGroupIndex; /* TGroupData[nGroupNumber]nGroupIndex = index of t_group in t_group_info */ - S_SHORT nDonorM; /* number of endpoint-donors that have negative charge (Minus) */ - S_SHORT nDonorH; /* number of endpoint-donors that have only H */ - S_SHORT nAccepM; /* number of endpoint-acceptors that have negative charge (Minus) */ - S_SHORT nAccepH; /* number of endpoint-acceptors that have H and no negative charge */ - S_SHORT nAccep0; /* number of endpoint-acceptors that have no H and no negative charge */ - S_SHORT nDonorA; /* number of acidic endpoint-donors */ - S_SHORT nAccepS; /* number of acidic endpoint-acceptors */ -} TGroupData; -#endif - - - -/*****************************************************************************/ -int MarkSaltChargeGroups ( inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, - T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, - struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD ) -{ - - int nNumChanges = 0, nTotNumChanges = 0; - if ( s_group_info && s_group_info->s_candidate && s_group_info->max_num_candidates > 0 ) { - int i, i1, i2, j, j1, j2, jj, ii1, ii2, jj1, jj2, /*k,*/ num_tested; - S_CANDIDATE *s_candidate = s_group_info->s_candidate; - int nMaxNumCandidates = s_group_info->max_num_candidates; - int nNumCandidates = s_group_info->num_candidates; - int nNumOtherCandidates = s_group_info->num_other_candidates; - int nNumPOnlyCandidates = s_group_info->num_p_only_candidates; - int s_type, s_subtype; - int ret, nDelta, /*nMobile,*/ err = 0; - int s_subtype_all = 0; - int nGroupNumber; - T_ENDPOINT EndPoint[2]; -#if ( MAX_LOCAL_TGNUM > 0 ) - TGroupData tgData[MAX_LOCAL_TGNUM]; - TGroupData *ptgData = tgData; -#endif - int cond1=0,cond2a=0,cond2b=0,cond2c=0,cond2=0; - - if ( nNumCandidates <= -1 || !t_group_info || !t_group_info->t_group ) { - return 0; - } - - /* count t-groups */ - for ( i = 0, nGroupNumber = 0; i < t_group_info->num_t_groups; i ++ ) { - if ( nGroupNumber < t_group_info->t_group[i].nGroupNumber ) { - nGroupNumber = t_group_info->t_group[i].nGroupNumber; /* max. t-group number */ - } - } -#if ( MAX_LOCAL_TGNUM > 0 ) - /* prepare memory */ - if ( nGroupNumber >= MAX_LOCAL_TGNUM ) { - if ( !( ptgData = (TGroupData*)inchi_calloc( nGroupNumber+1, sizeof(TGroupData) ) ) ) { - err = BNS_OUT_OF_RAM; - goto quick_exit; - } - } else { - memset( ptgData, 0, sizeof(tgData) ); - } - ptgData[0].nGroupIndex = -1; /* data for non-tautomeric atoms */ - for ( i = 0, nGroupNumber = 0; i < t_group_info->num_t_groups; i ++ ) { - if ( nGroupNumber = t_group_info->t_group[i].nGroupNumber ) { - ptgData[nGroupNumber].nGroupIndex = i; - ptgData[i].nGroupNumber = nGroupNumber; - } - } -#endif - nNumCandidates = 0; /* always recalculate 2004-03-22 */ - num_tested = 0; - - if ( nNumCandidates == 0 ) - { - for ( i = 0, nNumCandidates = nNumOtherCandidates = nNumPOnlyCandidates = 0; i < num_atoms; i ++ ) - { - if ( 0 == (s_type = GetSaltChargeType( at, i, t_group_info, &s_subtype )) || - /* -C=O or =C-OH, O = S, Se, Te */ -#if ( INCL_NON_SALT_CANDIDATATES == 1 ) - 1 == (s_type = GetOtherSaltChargeType( at, i, t_group_info, &s_subtype, 1 )) || - /* =Z-MH or -Z=M, Z = centerpoint, M = endpoint, other than above */ -#endif - 2 == (s_type = GetOtherSaltType( at, i, &s_subtype ) ) - /* >C-SH, >C-S(-); S=S,Se,Te */ - ) - { - - if ( nNumCandidates >= nMaxNumCandidates ) - { - err = BNS_VERT_EDGE_OVFL; - goto quick_exit; - } - s_candidate[nNumCandidates].atnumber = i; - s_candidate[nNumCandidates].type = s_type; - s_candidate[nNumCandidates].subtype = s_subtype; - s_candidate[nNumCandidates].endpoint = at[i].endpoint; - nNumCandidates ++; - nNumOtherCandidates += (1 == s_type); - nNumPOnlyCandidates += (2 == s_type); - s_subtype_all |= s_subtype; - /*i1 = i;*/ /* save a representative of a tautomeric group */ - } - } /* for */ - - /* changes: TG_FLAG_ALLOW_NO_NEGTV_O replaced CHARGED_SALTS_ONLY==0 */ -#if 0 - if ( nNumCandidates <= 1 || - !(s_subtype_all & SALT_ACCEPTOR) || - (((t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O)|| - (t_group_info->bTautFlagsDone & TG_FLAG_FOUND_SALT_CHARGES_DONE) || - (t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT)) ? - !(s_subtype_all & (SALT_DONOR_Neg | SALT_DONOR_H)): - (!(s_subtype_all & SALT_DONOR_Neg) || nNumOtherCandidates==nNumCandidates)) - ) { -#endif - cond1 = s_subtype_all & SALT_ACCEPTOR; - cond2a = t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O; - cond2b = t_group_info->bTautFlagsDone & TG_FLAG_FOUND_SALT_CHARGES_DONE; - cond2c = t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT; - if ( cond2a || cond2b|| cond2c ) - cond2 = !(s_subtype_all & (SALT_DONOR_Neg | SALT_DONOR_H)); - else - cond2 = !(s_subtype_all & SALT_DONOR_Neg) || nNumOtherCandidates==nNumCandidates; - if ( nNumCandidates <= 1 || !cond1 || cond2 - /*( - ( cond2a || cond2b || cond2c ) - ? !(s_subtype_all & (SALT_DONOR_Neg | SALT_DONOR_H)) - : ( !(s_subtype_all & SALT_DONOR_Neg) || nNumOtherCandidates==nNumCandidates) ) */ - ) - { - s_group_info->num_candidates = -1; /* no candidate exists */ - goto quick_exit; - } - if ( !(s_subtype_all & (SALT_DONOR_Neg) ) ) { - t_group_info->bTautFlagsDone |= TG_FLAG_ALLOW_NO_NEGTV_O_DONE; - } - } else { - for ( i = 0; i < nNumCandidates; i ++ ) { - i1 = s_candidate[i].atnumber; - if ( 0 <= (s_type = GetSaltChargeType( at, i1, t_group_info, &s_subtype )) -#if ( INCL_NON_SALT_CANDIDATATES == 1 ) - || 0 < (s_type = GetOtherSaltChargeType( at, i1, t_group_info, &s_subtype, 1 /* bAccept_O*/ )) -#endif - ) { - s_candidate[nNumCandidates].type = s_type; - s_candidate[nNumCandidates].subtype = s_subtype; - s_candidate[nNumCandidates].endpoint = at[i1].endpoint; - } - } - } - /* Look for alt paths connecting: - SALT_DONOR_Neg to SALT_ACCEPTOR : long distance migration of negative charges - SALT_DONOR_H to SALT_ACCEPTOR : long distance migration of H-atoms - */ - do { - nNumChanges = 0; - for ( i1 = 0; i1 < nNumCandidates; i1 ++ ) { - j1 = s_candidate[i1].atnumber; - for ( i2 = i1+1; i2 < nNumCandidates; i2 ++ ) { - /* prev. approach: do not test if both candidates are not "salt-type". Disabled 2004-03-18 - if ( s_candidate[i1].type && s_candidate[i2].type ) - continue; - */ - j2 = s_candidate[i2].atnumber; - if ( at[j1].endpoint && at[j1].endpoint == at[j2].endpoint ) { - continue; - } - for ( j = 0; j < 2; j ++ ) { - if ( j ) { - ii1 = i2; /* candidate 1 (donor) ordering number */ - ii2 = i1; /* candidate 2 (acceptor) ordering number */ - jj1 = j2; /* candidate 1 (donor) atom number */ - jj2 = j1; /* candidate 2 (acceptor) atom number */ - } else { /* transposition */ - ii1 = i1; /* candidate 1 (donor) ordering number */ - ii2 = i2; /* candidate 2 (acceptor) ordering number */ - jj1 = j1; /* candidate 1 (donor) atom number */ - jj2 = j2; /* candidate 2 (acceptor) atom number */ - } - - if ( ( s_candidate[ii1].subtype & (SALT_DONOR_Neg | SALT_DONOR_H) ) && - ( s_candidate[ii2].subtype & SALT_ACCEPTOR ) ) - { - ret = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, jj2, jj1, ALT_PATH_MODE_4_SALT ); - num_tested ++; - if ( IS_BNS_ERROR( ret ) ) - { - err = ret; - goto quick_exit; - } - if ( ret & 1 ) - { - nDelta = (ret & ~3) >> 2; - nNumChanges += (ret & 2); - for ( i = 0; i < 2; i ++ ) - { - jj = i? jj2 : jj1; - AddEndPoint( EndPoint+i, at, jj ); - } - /* add/merge taut groups and reinit pBNS in the fly */ - ret = RegisterEndPoints( t_group_info, - EndPoint, 2, at, num_atoms, c_group_info, pBNS ); - if ( ret == -1 ) - { - ret = BNS_PROGRAM_ERR; - } - if ( ret < 0 ) - { - err = ret; - goto quick_exit; - } - if ( nDelta ) - { - err = BNS_RADICAL_ERR; - goto quick_exit; - } - nNumChanges += (ret > 0); - break; /* avoid redundant repetition */ - } - } - } - } - } - nTotNumChanges += nNumChanges; - } while ( num_tested && nNumChanges ); - -quick_exit: - if ( !err ) { - nTotNumChanges += nNumChanges; /* nNumChanges != 0 only in case of 'goto quick_exit' */ - if ( s_group_info->num_candidates == 0 ) { - /* first time: initialize */ - s_group_info->num_candidates = num_tested? nNumCandidates : -1; /* no candidate exists */ - } - } else { - nTotNumChanges = err; - } -#if ( MAX_LOCAL_TGNUM > 0 ) - if ( ptgData != tgData ) { - inchi_free( ptgData ); - } -#endif - } - return nTotNumChanges; -} -#else - -/*****************************************************************************/ -int MarkSaltChargeGroups ( inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, - T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, - struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD ) -{ - - int nNumChanges = 0, nTotNumChanges = 0; - if ( s_group_info && s_group_info->s_candidate && s_group_info->max_num_candidates > 0 ) { - int i, i1, i2, j, j1, j2, jj, ii1, ii2, jj1, jj2, k, num_tested; - S_CANDIDATE *s_candidate = s_group_info->s_candidate; - int nMaxNumCandidates = s_group_info->max_num_candidates; - int nNumCandidates = s_group_info->num_candidates; - int nNumOtherCandidates = s_group_info->num_other_candidates; - int s_type, s_subtype; - int ret, nDelta, nMobile; - int s_subtype_all = 0; - T_ENDPOINT EndPoint[2]; - - if ( nNumCandidates <= -1 || !t_group_info || !t_group_info->t_group ) { - return 0; - } else - if ( nNumCandidates == 0 ) { - for ( i = 0, nNumCandidates = nNumOtherCandidates = 0; i < num_atoms; i ++ ) { - if ( 0 <= (s_type = GetSaltChargeType( at, i, t_group_info, &s_subtype )) ) { - if ( nNumCandidates >= nMaxNumCandidates ) { - return BNS_VERT_EDGE_OVFL; - } - s_candidate[nNumCandidates].atnumber = i; - s_candidate[nNumCandidates].type = s_type; - s_candidate[nNumCandidates].subtype = s_subtype; - s_candidate[nNumCandidates].endpoint = at[i].endpoint; - nNumCandidates ++; - s_subtype_all |= s_subtype; - /*i1 = i;*/ /* save a representative of a tautomeric group */ - } -#if ( INCL_NON_SALT_CANDIDATATES == 1 ) - else /* new */ - if ( 0 < (s_type = GetOtherSaltChargeType( at, i, t_group_info, &s_subtype, 1 /* bAccept_O*/ )) ) { - if ( nNumCandidates >= nMaxNumCandidates ) { - return BNS_VERT_EDGE_OVFL; - } - s_candidate[nNumCandidates].atnumber = i; - s_candidate[nNumCandidates].type = s_type; - s_candidate[nNumCandidates].subtype = s_subtype; - s_candidate[nNumCandidates].endpoint = at[i].endpoint; - nNumCandidates ++; - nNumOtherCandidates ++; - s_subtype_all |= s_subtype; - } -#endif - } - - /* changes: TG_FLAG_ALLOW_NO_NEGTV_O replaced CHARGED_SALTS_ONLY==0 */ - if ( nNumCandidates <= 1 || nNumOtherCandidates == nNumCandidates || - ((t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O) ? - !(s_subtype_all & (SALT_DONOR_Neg | SALT_DONOR_H)): - !(s_subtype_all & SALT_DONOR_Neg)) || - !(s_subtype_all & SALT_ACCEPTOR)) { - s_group_info->num_candidates = -1; /* no candidate exists */ - return 0; - } - if ( !(s_subtype_all & (SALT_DONOR_Neg) ) ) { - t_group_info->bTautFlagsDone |= TG_FLAG_ALLOW_NO_NEGTV_O_DONE; - } - } else { - for ( i = 0; i < nNumCandidates; i ++ ) { - i1 = s_candidate[i].atnumber; - if ( 0 <= (s_type = GetSaltChargeType( at, i1, t_group_info, &s_subtype )) -#if ( INCL_NON_SALT_CANDIDATATES == 1 ) - || 0 < (s_type = GetOtherSaltChargeType( at, i1, t_group_info, &s_subtype, 1 /* bAccept_O*/ )) -#endif - ) { - s_candidate[nNumCandidates].type = s_type; - s_candidate[nNumCandidates].subtype = s_subtype; - s_candidate[nNumCandidates].endpoint = at[i1].endpoint; - } - } - } - /* Look for alt paths connecting: - SALT_DONOR_Neg to SALT_ACCEPTOR : long distance migration of negative charges - SALT_DONOR_H to SALT_ACCEPTOR : long distance migration of H-atoms - */ - num_tested = 0; - do { - nNumChanges = 0; - for ( i1 = 0; i1 < nNumCandidates; i1 ++ ) { - j1 = s_candidate[i1].atnumber; - for ( i2 = i1+1; i2 < nNumCandidates; i2 ++ ) { - if ( s_candidate[i1].type && s_candidate[i2].type ) - continue; /* both candidates are not "salt-type" */ - j2 = s_candidate[i2].atnumber; - if ( at[j1].endpoint && at[j1].endpoint == at[j2].endpoint ) { - continue; - } - for ( j = 0; j < 2; j ++ ) { - if ( j ) { - ii1 = i2; /* candidate 1 (donor) ordering number */ - ii2 = i1; /* candidate 2 (acceptor) ordering number */ - jj1 = j2; /* candidate 1 (donor) atom number */ - jj2 = j1; /* candidate 2 (acceptor) atom number */ - } else { /* transposition */ - ii1 = i1; /* candidate 1 (donor) ordering number */ - ii2 = i2; /* candidate 2 (acceptor) ordering number */ - jj1 = j1; /* candidate 1 (donor) atom number */ - jj2 = j2; /* candidate 2 (acceptor) atom number */ - } - - if ( ( s_candidate[ii1].subtype & (SALT_DONOR_Neg | SALT_DONOR_H) ) && - ( s_candidate[ii2].subtype & SALT_ACCEPTOR ) ) { - ret = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, jj2, jj1, ALT_PATH_MODE_4_SALT ); - num_tested ++; - if ( IS_BNS_ERROR( ret ) ) { - return ret; - } - if ( ret & 1 ) { - nDelta = (ret & ~3) >> 2; - nNumChanges += (ret & 2); - for ( i = 0; i < 2; i ++ ) { - jj = i? jj2 : jj1; - EndPoint[i].nAtomNumber = jj; - EndPoint[i].nEquNumber = 0; - EndPoint[i].nGroupNumber = at[jj].endpoint; - if ( at[jj].endpoint ) { - memset( EndPoint[i].num, 0, sizeof(EndPoint[i].num) ); - } else { - AddAtom2num( EndPoint[i].num, at, jj, 2 ); /* fill out */ - AddAtom2DA( EndPoint[i].num_DA, at, jj, 2 ); - /* - nMobile = EndPoint[i].num[1] = (at[jj].charge == -1); - nMobile = EndPoint[i].num[0] = at[jj].num_H + nMobile; - for ( k = 0; k < T_NUM_ISOTOPIC; k ++ ) { - EndPoint[i].num[T_NUM_NO_ISOTOPIC+k] = at[jj].num_iso_H[NUM_H_ISOTOPES-k-1]; - } - */ - } - } - /* add/merge taut groups and reinit pBNS */ - ret = RegisterEndPoints( t_group_info, - EndPoint, 2, at, num_atoms, c_group_info, pBNS ); - if ( ret < 0 ) { - return ret; - } - nNumChanges += (ret > 0); - if ( nDelta ) { - goto quick_exit; - } - break; /* avoid redundant repetition */ - } - } - } - } - } - nTotNumChanges += nNumChanges; - } while ( num_tested && nNumChanges ); - -quick_exit: - nTotNumChanges += nNumChanges; /* nNumChanges != 0 only in case of 'goto quick_exit' */ - if ( s_group_info->num_candidates == 0 ) { - /* first time: initialize */ - s_group_info->num_candidates = num_tested? nNumCandidates : -1; /* no candidate exists */ - } - - } - return nTotNumChanges; -} -#endif - - - -/*****************************************************************************/ -int MergeSaltTautGroups( inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, - T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, - struct BalancedNetworkStructure *pBNS ) -{ - /* count candidates to be connected: exclude pure donors that do not belong to any t-group */ - AT_NUMB nCurTGroupNumber; - int i, j, /*k,*/ ret, iat, /*nMobile,*/ nMinNumEndpoints; - int s_subtype_all, s_subtype_taut; - int nMaxNumCandidates, nNumCandidates, nNumCandidates2; - T_ENDPOINT EndPointStackArray[MAX_STACK_ARRAY_LEN]; /* will be reallocated if too short */ - T_ENDPOINT *EndPoint = EndPointStackArray; - - - if ( !s_group_info || !s_group_info->s_candidate || /*s_group_info->num_candidates <= 0 ||*/ - !t_group_info || !t_group_info->t_group || !c_group_info ) { - return 0; - } - nMinNumEndpoints = 0; - nMaxNumCandidates = s_group_info->max_num_candidates; - nCurTGroupNumber = MAX_ATOMS; /* impossible t-group number */ - s_subtype_all = s_subtype_taut = 0; - /* collect tautomeric acidic O and previously non-tautomeric C-OH, C-SH, C-O(-), C-S(-) */ - /* find whether previously found tautomeric atoms have both mobile H and (-) */ - if ( 1 || (s_group_info->num_candidates < 0) ) { - /* can be only -O(-) and -OH */ - int s_type, s_subtype; - S_CANDIDATE *s_candidate = s_group_info->s_candidate; - for ( i = 0, nNumCandidates = nNumCandidates2 = 0; i < num_atoms; i ++ ) { - s_subtype = 0; - if ( 0 == (s_type = GetSaltChargeType( at, i, t_group_info, &s_subtype )) || - /* -C=O or =C-OH, O = S, Se, Te */ - - /*(t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT) &&*/ - 1 == (s_type = GetOtherSaltChargeType( at, i, t_group_info, &s_subtype, 1/* bAccept_O*/ )) || - /* =Z-MH or -Z=M, Z = centerpoint, M = endpoint, other than above. M may be N */ - - 2 == (s_type = GetOtherSaltType( at, i, &s_subtype )) || - /* >C-SH, >C-S(-); S=S,Se,Te */ - - /* other proton donor or acceptor */ - bHasAcidicHydrogen( at, i) && ((s_type=3), (s_subtype = SALT_p_DONOR)) || - bHasAcidicMinus( at, i) && ((s_type=3), (s_subtype = SALT_p_ACCEPTOR)) - ) { - - if ( nNumCandidates >= nMaxNumCandidates ) { - return BNS_VERT_EDGE_OVFL; - } - if ( at[i].endpoint ) { - s_subtype_taut |= s_subtype; - } else - if ( bDoNotMergeNonTautAtom(at, i) ) { - continue; /* ignore non-tautomeric N */ - } - if ( !( s_subtype & SALT_DONOR_ALL ) || - (s_subtype & SALT_ACCEPTOR) && !at[i].endpoint ) { - continue; /* do not include non-taut acceptors like -C=O */ - } - s_candidate[nNumCandidates].atnumber = i; - s_candidate[nNumCandidates].type = s_type; - s_candidate[nNumCandidates].subtype = s_subtype; - s_candidate[nNumCandidates].endpoint = at[i].endpoint; - nNumCandidates ++; - s_subtype_all |= s_subtype; - } - } - /* - Forced merging occurs upon: - =========================== - (t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O) or - (t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT) - - - Allow forced merging in cases: - {t-groups} (H, (-)} {H, (-), t-groups} - - - Normal salt merging in cases: - (H, (-)} {H, (-), t-groups}, - - Cannot merge H into t-groups if no (-) is present - */ - - - if ( (t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O) || - (t_group_info->bTautFlagsDone & TG_FLAG_FOUND_SALT_CHARGES_DONE) || - (t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT) ) { - /* force merge even though no negative charges are present */ - if ( nNumCandidates <= 1 || - (!(s_subtype_all & SALT_DONOR_Neg2) || !(s_subtype_all & SALT_DONOR_H2)) && - !t_group_info->num_t_groups ) { - s_group_info->num_candidates = -1; /* no candidate exists */ - return 0; - } - } else { - /* normal salt mode: merge if both -XH and -X(-) are present */ - if ( nNumCandidates <= 1 || - (!(s_subtype_all & SALT_DONOR_Neg2) || !(s_subtype_all & SALT_DONOR_H2)) ) { - s_group_info->num_candidates = -1; /* no candidate exists */ - return 0; - } - } - /* -- old code -- - if ( nNumCandidates <= 1 || - (((t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O) || - (t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT)) ? - !(s_subtype_all & SALT_DONOR_ALL): - !(s_subtype_all & SALT_DONOR_Neg2) - ) - ) { - s_group_info->num_candidates = -1; - return 0; - } - */ - if ( !(s_subtype_all & (SALT_DONOR_Neg2) ) ) { - t_group_info->bTautFlagsDone |= TG_FLAG_ALLOW_NO_NEGTV_O_DONE; - } - s_group_info->num_candidates = nNumCandidates; - } - - for ( i = 0; i < s_group_info->num_candidates; i ++ ) { - iat = s_group_info->s_candidate[i].atnumber; - if ( (s_group_info->s_candidate[i].subtype & SALT_ACCEPTOR) && !at[iat].endpoint ) { - continue; /* should not happen */ - } - s_subtype_all |= s_group_info->s_candidate[i].subtype; - if ( at[iat].endpoint != nCurTGroupNumber || !at[iat].endpoint ) { - nMinNumEndpoints ++; - } - nCurTGroupNumber = (int)at[iat].endpoint; - } - if ( nMinNumEndpoints <= 1 ) { - return 0; /* too few endpoints */ - } - - /* make sure we have enough memory */ - if ( nMinNumEndpoints > MAX_STACK_ARRAY_LEN ) { - if ( !(EndPoint = (T_ENDPOINT *)inchi_calloc( nMinNumEndpoints, sizeof(EndPoint[0]) ) ) ) { - /*printf("BNS_OUT_OF_RAM-8\n");*/ - return BNS_OUT_OF_RAM; - } - } - - nCurTGroupNumber = MAX_ATOMS; /* impossible t-group number */ - for ( i = j = 0; i < s_group_info->num_candidates; i ++ ) { - iat = s_group_info->s_candidate[i].atnumber; - if ( s_group_info->s_candidate[i].subtype == SALT_ACCEPTOR && !at[iat].endpoint ) { - continue; - } - if ( at[iat].endpoint != nCurTGroupNumber || !at[iat].endpoint ) { - AddEndPoint( EndPoint+j, at, iat ); - j ++; - } - nCurTGroupNumber = (int)at[iat].endpoint; - } - - ret = RegisterEndPoints( t_group_info, - EndPoint, j, at, num_atoms, c_group_info, pBNS ); - if ( ret == -1 ) { - ret = BNS_PROGRAM_ERR; - } - - if ( EndPoint != EndPointStackArray ) { - inchi_free( EndPoint ); - } - - return ret; -} - - - -/*****************************************************************************/ -int MakeIsotopicHGroup(inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, - T_GROUP_INFO *t_group_info) -{ - /* all tautomeric atoms and all possible H+ donors and acceptors that have H */ - int i, j, k, n, bHasH, tg, nError=0; - int s_subtype_all, s_subtype_taut; - int nMaxNumCandidates, nNumCandidates, nNumNonTautCandidates; - - - if ( !s_group_info || !s_group_info->s_candidate || /*s_group_info->num_candidates <= 0 ||*/ - !t_group_info || !t_group_info->t_group ) { - return 0; - } - nMaxNumCandidates = s_group_info->max_num_candidates; - s_subtype_all = s_subtype_taut = 0; - memset( t_group_info->num_iso_H, 0, sizeof(t_group_info->num_iso_H) ); - if ( 1 || (s_group_info->num_candidates < 0) ) { - int s_type, s_subtype; - S_CANDIDATE *s_candidate = s_group_info->s_candidate; - for ( i = 0, nNumCandidates = nNumNonTautCandidates = 0; i < num_atoms; i ++ ) { - s_subtype = 0; - s_type = 0; - if ( at[i].endpoint ) { - if ( (tg = t_group_info->tGroupNumber[at[i].endpoint]) && - at[i].endpoint == t_group_info->t_group[tg-=1].nGroupNumber ) { - bHasH = (int)t_group_info->t_group[tg].num[0] - (int)t_group_info->t_group[tg].num[1]; - } else { - nError = BNS_PROGRAM_ERR; - break; - } - } else { - bHasH = (int)at[i].num_H; - } - if ( bHasH && at[i].endpoint || /* tautomeric atoms */ - - /* non-tautomeric heteroatoms that - (a) have H and - (b) may be donors of H - therefore may exchange isotopic-non-isotopic H */ - bHasH && - (0 == (s_type = GetSaltChargeType( at, i, t_group_info, &s_subtype )) || - /* -C=O or =C-OH, O = S, Se, Te */ - - /*(t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT) &&*/ - 1 == (s_type = GetOtherSaltChargeType( at, i, t_group_info, &s_subtype, 1/* bAccept_O*/ )) || - /* =Z-MH or -Z=M, Z = centerpoint, M = endpoint, other than above. M may be N */ - - 2 == (s_type = GetOtherSaltType( at, i, &s_subtype )) || - /* >C-SH, >C-S(-); S=S,Se,Te */ - - /* other proton donor or acceptor */ - bHasAcidicHydrogen( at, i) && ((s_type=3), (s_subtype = SALT_p_DONOR)) || - bHasAcidicMinus( at, i) && ((s_type=3), (s_subtype = SALT_p_ACCEPTOR)) || - bHasOtherExchangableH (at, i) && ((s_type=3), (s_subtype = SALT_DONOR_H)) ) - - ) { - - if ( nNumCandidates >= nMaxNumCandidates ) { - return BNS_VERT_EDGE_OVFL; - } - s_candidate[nNumCandidates].atnumber = i; - s_candidate[nNumCandidates].type = s_type; - s_candidate[nNumCandidates].subtype = s_subtype; - s_candidate[nNumCandidates].endpoint = at[i].endpoint; - nNumCandidates ++; - nNumNonTautCandidates += !at[i].endpoint; - s_subtype_all |= s_subtype; - } - } - if ( nError ) { - return nError; - } - if ( nNumCandidates > 0 ) { - t_group_info->nIsotopicEndpointAtomNumber = (AT_NUMB *)inchi_calloc( nNumNonTautCandidates+1, sizeof(t_group_info->nIsotopicEndpointAtomNumber[0])); - t_group_info->nIsotopicEndpointAtomNumber[0] = nNumNonTautCandidates; - for ( i = 0, n = 1; i < nNumCandidates; i ++ ) { - k = s_candidate[i].atnumber; - if ( !at[k].endpoint ) { - t_group_info->nIsotopicEndpointAtomNumber[n++] = k; - } - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { - t_group_info->num_iso_H[j] += at[k].num_iso_H[j]; - } - at[k].cFlags |= AT_FLAG_ISO_H_POINT; - } - t_group_info->nNumIsotopicEndpoints = nNumNonTautCandidates+1; - } - } - return nNumCandidates; -} - -/*#else*/ /* } DISCONNECT_SALTS == 0 */ - -/********************************************************************************** - Charges and tautomeric endpoints (N only) - ********************************************************************************** - - H = number of possibly moveable hydrogen atoms - C = possibly moveable positive charge - - - = single bond - = = double bond - # = triple bond - -+-----------------------------------------------------------------------------+ -|ca-| H | edges to t- | 1 bond | 2 bonds | 3 bonds *) | -|se | C | and c-groups | (valence) | (valence) | (valence) | -| # | | (edges flow) | | | | -+---|------+---------------+----------------+----------------+----------------| -| 1 | H=0 | -- (1) | =NH (3) | =N- (3) | >N- (3) | -| | C=0 | == | | | | -+---|------+---------------+----------------+----------------+----------------| -| 2 | H=1 | == (2) | -NH2 (3) | -NH- (3) | none | -| | C=0 | == | | | | -+---|------+---------------+----------------+----------------+----------------| -| 3 | H=0 | -- (0) | #NH(+) (4) | =N(+)= (4) +)| >N(+)= (4) | -| | C=1 | -- | (prohibited | | | -| | | | by edge cap) | | | -+---|------+---------------+----------------+----------------+----------------| -| 4 | H=1 | == (1) | =NH2(+) (4) +)| =NH(+)- (4) +)| >NH(+)- (4) | -| | C=1 | -- | | | | -+---+-------------------------------------------------------------------------+ - - *) Cannot be a tautomeric endpoint - - +) The three charged types of atoms [=N(+)=, =NH(+)-, =NH2(+)] should be - checked for possible H-tautomerism. Other types in the marked by *) - column should not be checked as long as H(+) exchange is not considered - tautomeric. - - Other possibilities: -NH3(+) >NH2(+) >N(+)< cannot be H-tautomeric endpoints. - - Case #1 (H=0, C=0) and #4 (H=1,C=0) is indistinguishable from the - viewpoint of edges flow and capacities except for flow from N to (+) vertex. - - Without taking precautions H(+) can be transferred - - from =NH2(+) to =NH, - from =NH(+)- to =N-, - from >NH(+)- to >N- - - or to any other appropriate atom that has a lone electron pair and bonds - will not change. In this case no bond must be marked as tautomeric. - - For this reason before attempting to transfer H from one endpoint to - another the charges on the two atoms should be set to zero by - forcing zero flow from each of atoms to the (+)-vertices if the - atoms belong to a c-group. - - **********************************************************************************/ - - -/***********************************************************************************/ -/* MarkTautomerGroups: do not identify positively charged N as endpoints for now */ -/***********************************************************************************/ -int MarkTautomerGroups( inp_ATOM *at, int num_atoms, T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info - , struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD ) -{ - int i, j, k, m, endpoint_valence, centerpoint, endpoint, bond_type, nMobile, num_changes=0, tot_changes=0; - T_ENDPOINT EndPoint[MAXVAL]; - T_BONDPOS BondPos[MAXVAL]; - AT_NUMB nGroupNumber; - int bDiffGroups; - int nNumEndPoints, nNumBondPos, nNumPossibleMobile; - int bTautBond, bNonTautBond, bAltBond; - int nNumDonor, nNumAcceptor, bPossiblyEndpoint; - T_GROUP *t_group; - int *pnum_t, max_num_t, bIgnoreIsotopic; - ENDPOINT_INFO eif1, eif2; - int nErr = 0; -#define ALLOWED_EDGE(PBNS, IAT,IBOND) ( !(PBNS) || !(PBNS)->edge || !(PBNS)->vert || !(PBNS)->edge[(PBNS)->vert[IAT].iedge[IBOND]].forbidden) -#define ACTUAL_ORDER(PBNS, IAT,IBOND, BTYPE) ( ((PBNS) && (PBNS)->edge && (PBNS)->vert &&\ - ((BTYPE)==BOND_ALT_123 || (BTYPE)==BOND_ALT_13 || (BTYPE)==BOND_ALT_23))? (PBNS)->edge[(PBNS)->vert[IAT].iedge[IBOND]].flow+BOND_TYPE_SINGLE:(BTYPE)) - - - if ( !t_group_info || !(t_group_info->bTautFlags & TG_FLAG_TEST_TAUT__ATOMS) ) - return 0; - /* initial t_group allocation */ - if ( !t_group_info->t_group && !t_group_info->max_num_t_groups ) { - INCHI_MODE bTautFlags = t_group_info->bTautFlags; /* save initial setting */ - INCHI_MODE bTautFlagsDone = t_group_info->bTautFlagsDone; /* save previous findings, if any */ - TNI tni = t_group_info->tni; - AT_NUMB *tGroupNumber = t_group_info->tGroupNumber; - bIgnoreIsotopic = t_group_info->bIgnoreIsotopic; - memset( t_group_info, 0, sizeof(*t_group_info) ); - t_group_info->bIgnoreIsotopic = bIgnoreIsotopic; /* restore initial setting */ - t_group_info->bTautFlags = bTautFlags; - t_group_info->bTautFlagsDone = bTautFlagsDone; - t_group_info->tni = tni; - t_group_info->tGroupNumber = tGroupNumber; - t_group_info->max_num_t_groups = num_atoms/2+1; /* upper limit */ - if (!(t_group_info->t_group = (T_GROUP*)inchi_calloc(t_group_info->max_num_t_groups, sizeof(t_group[0])))) { - return (t_group_info->max_num_t_groups = -1); /* failed, out of RAM */ - } - } - /* check if t_group_info exists */ - if ( !t_group_info->t_group || !t_group_info->max_num_t_groups ) - return 0; - - if ( 0 > t_group_info->max_num_t_groups ) - return t_group_info->max_num_t_groups; - - pnum_t = &t_group_info->num_t_groups; /* number of found tautomer endpoint groups */ - t_group = t_group_info->t_group; - max_num_t = t_group_info->max_num_t_groups; - bIgnoreIsotopic = t_group_info->bIgnoreIsotopic; - /* 1-3 tautomers */ - for ( i = 0; i < num_atoms; i ++ ) { - /* find possible endpoint Z = at[i] */ - if ( endpoint_valence = nGetEndpointInfo( at, i, &eif1 ) ) { - /* 1st endpoint candidate found. Find centerpoint candidate */ - for ( j = 0; j < at[i].valence; j ++ ) { - bond_type = (int)at[i].bond_type[j] & ~BOND_MARK_ALL; -#if ( FIX_BOND23_IN_TAUT == 1 ) - bond_type = ACTUAL_ORDER(pBNS,i,j,bond_type); -#endif - centerpoint = (int)at[i].neighbor[j]; /* a centerpoint candidate */ - if ( (bond_type == BOND_DOUBLE || - bond_type == BOND_ALTERN || - bond_type == BOND_ALT12NS || - bond_type == BOND_TAUTOM) && is_centerpoint_elem( at[centerpoint].el_number ) - && ALLOWED_EDGE(pBNS, i, j) - ) { - /* test a centerpoint candidate. */ - /* find all endpoints including at[i] and store them into EndPoint[] */ - nNumPossibleMobile = 0; - nGroupNumber = (AT_NUMB)num_atoms; /* greater than any tautomeric group number */ - bDiffGroups = -1; /* ignore the first difference */ - nNumDonor = nNumAcceptor = 0; - for ( k = 0, nNumEndPoints = 0, nNumBondPos = 0; k < at[centerpoint].valence; k ++ ) { - endpoint = at[centerpoint].neighbor[k]; /* endpoint candidate */ - bond_type = (int)at[centerpoint].bond_type[k] & ~BOND_MARK_ALL; -#if ( FIX_BOND23_IN_TAUT == 1 ) - bond_type = ACTUAL_ORDER(pBNS,centerpoint,k,bond_type); -#endif - bTautBond = - bNonTautBond = - bAltBond = - bPossiblyEndpoint = 0; - if ( !ALLOWED_EDGE(pBNS, centerpoint, k) ) { - continue; - } else - if ( bond_type == BOND_ALTERN || bond_type == BOND_ALT12NS || bond_type == BOND_TAUTOM ) { - bTautBond = 1; -#if ( REPLACE_ALT_WITH_TAUT == 1 ) - bAltBond = (bond_type == BOND_ALTERN || bond_type == BOND_ALT12NS); -#endif - } else - if ( bond_type == BOND_SINGLE || bond_type == BOND_DOUBLE ) - bNonTautBond = 1; - else - continue; - - if ( !(endpoint_valence = nGetEndpointInfo( at, endpoint, &eif1 )) ) - continue; /* not an endpoint element or can't have mobile groups */ - /* save information about the found possible tautomeric endpoint */ - /* 2 = T_NUM_NO_ISOTOPIC non-isotopic values */ - nMobile = - AddAtom2num( EndPoint[nNumEndPoints].num, at, endpoint, 2 ); /* fill out */ - AddAtom2DA( EndPoint[nNumEndPoints].num_DA, at, endpoint, 2 ); - /* --- why is isitopic info missing ? -- see below - nMobile = EndPoint[nNumEndPoints].num[1] = (at[endpoint].charge == -1); - nMobile = EndPoint[nNumEndPoints].num[0] = at[endpoint].num_H + nMobile; - */ - if ( bNonTautBond ) { - m = (bond_type == BOND_SINGLE && (nMobile || at[endpoint].endpoint)); - nNumDonor += m; - bPossiblyEndpoint += m; - m = (bond_type == BOND_DOUBLE ); - nNumAcceptor += m; - bPossiblyEndpoint += m; - } else { - /* tautomeric or alternating bond */ - m = (0 != at[endpoint].endpoint || eif1.cDonor ); - nNumDonor += m; - bPossiblyEndpoint += m; - m = ( at[endpoint].endpoint || - eif1.cNeutralBondsValence > at[endpoint].valence ); - nNumAcceptor += m; - bPossiblyEndpoint += m; - } - if ( !bPossiblyEndpoint ) - continue; - EndPoint[nNumEndPoints].nGroupNumber = at[endpoint].endpoint; /* =0 if it is an endpoint for the 1st time */ - EndPoint[nNumEndPoints].nEquNumber = 0; - EndPoint[nNumEndPoints].nAtomNumber = (AT_NUMB)endpoint; - if ( nGroupNumber != at[endpoint].endpoint ) { - bDiffGroups ++; - nGroupNumber = at[endpoint].endpoint; - } - - /* save positions of all, not only possibly tautomeric bonds */ -#if ( REPLACE_ALT_WITH_TAUT != 1 ) - if ( bNonTautBond || bAltBond ) { -#endif - BondPos[nNumBondPos].nAtomNumber = (AT_NUMB)centerpoint; - BondPos[nNumBondPos].neighbor_index = (AT_NUMB)k; /* bond ordering number; used to change bonds to tautomeric only */ - nNumBondPos ++; -#if ( REPLACE_ALT_WITH_TAUT != 1 ) - } -#endif - /* mobile group is possible if (a) the endpoint has a mobile group or */ - /* (b) the centerpoint is adjacent to another endpoint */ - nNumPossibleMobile += (nMobile>0 || at[endpoint].endpoint); - nNumEndPoints ++; - } - if ( nNumEndPoints > 1 && nNumPossibleMobile && nNumDonor && nNumAcceptor ) { - /* - * a tautomeric group has been found - * - * at this point: - * nGroupNumber = 0 if all endpoints belong to a newly discovered tautomeric group - * bDiffGroups > 0 if at least 2 tautomeric groups are to be merged (one of them can be new) - * case (nGroupNumber != 0 && bDiffGroups = 0 ) ignored because all endpoints belong to the same known t-group - * case (nGroupNumber != 0 && bDiffGroups < 0 ) cannot happen - */ - - nErr=FindAccessibleEndPoints( EndPoint, &nNumEndPoints, BondPos, &nNumBondPos, - pBNS, pBD, at, num_atoms, c_group_info, ALT_PATH_MODE_TAUTOM ); - if ( IS_BNS_ERROR(nErr) ) { - return nErr; - } - nErr = 0; - - if ( nNumEndPoints > 0 ) { - if ( !nGroupNumber || bDiffGroups > 0 ) { - num_changes = RegisterEndPoints( t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS ); - if ( num_changes == -1 ) { - nErr = CT_TAUCOUNT_ERR; - } - if ( num_changes < 0 ) { - nErr = num_changes; - } - if ( nErr ) - goto exit_function; - tot_changes += (num_changes>0); - } - if ( nNumBondPos > 0 ) { - /* some of the bonds have not been marked as tautomeric yet */ - num_changes = SetTautomericBonds( at, nNumBondPos, BondPos ); - tot_changes += (num_changes>0); - } - } - } - } - } - } - } -#if ( KETO_ENOL_TAUT == 1 ) /***** post v.1 feature *****/ - if ( t_group_info->bTautFlags & TG_FLAG_KETO_ENOL_TAUT ) { - /* 1,3 keto-enol tautomerism */ - for ( i = 0; i < num_atoms; i ++ ) { - /* find possible endpoint Z = at[i] */ - if ( endpoint_valence = nGetEndpointInfo_KET( at, i, &eif1 ) ) { - /* 1st endpoint candidate found. Find centerpoint candidate */ - for ( j = 0; j < at[i].valence; j ++ ) { - bond_type = (int)at[i].bond_type[j] & ~BOND_MARK_ALL; -#if ( FIX_BOND23_IN_TAUT == 1 ) - bond_type = ACTUAL_ORDER(pBNS,i,j,bond_type); -#endif - centerpoint = (int)at[i].neighbor[j]; /* a centerpoint candidate */ - if ( (bond_type == BOND_DOUBLE || - bond_type == BOND_ALTERN || - bond_type == BOND_ALT12NS || - bond_type == BOND_TAUTOM) && - is_centerpoint_elem_KET( at[centerpoint].el_number ) && - !at[centerpoint].charge && !at[centerpoint].radical && - /* only normal carbon is allowed */ - 4 == at[centerpoint].chem_bonds_valence + at[centerpoint].num_H - && ALLOWED_EDGE(pBNS, i, j) - ) { - int num_O = 0; - int num_C = 0; - /* test a centerpoint candidate. */ - /* find all endpoints including at[i] and store them into EndPoint[] */ - nNumPossibleMobile = 0; - nGroupNumber = (AT_NUMB)num_atoms; /* greater than any tautomeric group number */ - bDiffGroups = -1; /* ignore the first difference */ - nNumDonor = nNumAcceptor = 0; - for ( k = 0, nNumEndPoints = 0, nNumBondPos = 0; k < at[centerpoint].valence; k ++ ) { - endpoint = at[centerpoint].neighbor[k]; /* endpoint candidate */ - bond_type = (int)at[centerpoint].bond_type[k] & ~BOND_MARK_ALL; -#if ( FIX_BOND23_IN_TAUT == 1 ) - bond_type = ACTUAL_ORDER(pBNS,centerpoint,k,bond_type); -#endif - bTautBond = - bNonTautBond = - bAltBond = - bPossiblyEndpoint = 0; - if ( !ALLOWED_EDGE(pBNS, centerpoint, k) ) { - continue; - } else - if ( bond_type == BOND_ALTERN || bond_type == BOND_ALT12NS || bond_type == BOND_TAUTOM ) { - bTautBond = 1; -#if ( REPLACE_ALT_WITH_TAUT == 1 ) - bAltBond = (bond_type == BOND_ALTERN || bond_type == BOND_ALT12NS); -#endif - } else - if ( bond_type == BOND_SINGLE || bond_type == BOND_DOUBLE ) - bNonTautBond = 1; - else - continue; - - if ( !(endpoint_valence = nGetEndpointInfo_KET( at, endpoint, &eif2 )) ) { - continue; - } - /* - if ( 3 != eif1.cKetoEnolCode + eif2.cKetoEnolCode && endpoint != i ) - continue; - */ - /* save information about the found possible tautomeric endpoint */ - /* 2 = T_NUM_NO_ISOTOPIC non-isotopic values */ - nMobile = - AddAtom2num( EndPoint[nNumEndPoints].num, at, endpoint, 2 ); /* fill out */ - AddAtom2DA( EndPoint[nNumEndPoints].num_DA, at, endpoint, 2 ); - /* --- why is isitopic info missing ? -- see below - nMobile = EndPoint[nNumEndPoints].num[1] = (at[endpoint].charge == -1); - nMobile = EndPoint[nNumEndPoints].num[0] = at[endpoint].num_H + nMobile; - */ - if ( bNonTautBond ) { - m = (bond_type == BOND_SINGLE && (nMobile || at[endpoint].endpoint)); - nNumDonor += m; - bPossiblyEndpoint += m; - m = (bond_type == BOND_DOUBLE ); - nNumAcceptor += m; - bPossiblyEndpoint += m; - } else { - /* tautomeric or alternating bond */ - m = (0 != at[endpoint].endpoint || eif1.cDonor ); - nNumDonor += m; - bPossiblyEndpoint += m; - m = ( at[endpoint].endpoint || - eif1.cNeutralBondsValence > at[endpoint].valence ); - nNumAcceptor += m; - bPossiblyEndpoint += m; - } - if ( !bPossiblyEndpoint ) - continue; - - num_O += (endpoint_valence == 2); - num_C += (endpoint_valence == 4); - - EndPoint[nNumEndPoints].nGroupNumber = at[endpoint].endpoint; /* =0 if it is an endpoint for the 1st time */ - EndPoint[nNumEndPoints].nEquNumber = 0; - EndPoint[nNumEndPoints].nAtomNumber = (AT_NUMB)endpoint; - if ( nGroupNumber != at[endpoint].endpoint ) { - bDiffGroups ++; - nGroupNumber = at[endpoint].endpoint; - } - - /* save positions of all, not only possibly tautomeric bonds */ -#if ( REPLACE_ALT_WITH_TAUT != 1 ) - if ( bNonTautBond || bAltBond ) { -#endif - BondPos[nNumBondPos].nAtomNumber = (AT_NUMB)centerpoint; - BondPos[nNumBondPos].neighbor_index = (AT_NUMB)k; /* bond ordering number; used to change bonds to tautomeric only */ - nNumBondPos ++; -#if ( REPLACE_ALT_WITH_TAUT != 1 ) - } -#endif - /* mobile group is possible if (a) the endpoint has a mobile group or */ - /* (b) the centerpoint is adjacent to another endpoint */ - nNumPossibleMobile += (nMobile>0 || at[endpoint].endpoint); - nNumEndPoints ++; - } - if ( nNumEndPoints > 1 && nNumPossibleMobile && nNumDonor && nNumAcceptor && num_O==1 && num_C ) { - /* - * a tautomeric group has been found - * - * at this point: - * nGroupNumber = 0 if all endpoints belong to a newly discovered tautomeric group - * bDiffGroups > 0 if at least 2 tautomeric groups are to be merged (one of them can be new) - * case (nGroupNumber != 0 && bDiffGroups = 0 ) ignored because all endpoints belong to the same known t-group - * case (nGroupNumber != 0 && bDiffGroups < 0 ) cannot happen - */ - - nErr=FindAccessibleEndPoints( EndPoint, &nNumEndPoints, BondPos, &nNumBondPos, - pBNS, pBD, at, num_atoms, c_group_info, ALT_PATH_MODE_TAUTOM_KET ); - if ( IS_BNS_ERROR(nErr) ) { - return nErr; - } - nErr = 0; - - if ( nNumEndPoints > 0 ) { - if ( !nGroupNumber || bDiffGroups > 0 ) { - num_changes = RegisterEndPoints( t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS ); - if ( num_changes == -1 ) { - nErr = CT_TAUCOUNT_ERR; - } - if ( num_changes < 0 ) { - nErr = num_changes; - } - if ( nErr ) - goto exit_function; - tot_changes += (num_changes>0); - } - if ( nNumBondPos > 0 ) { - /* some of the bonds have not been marked as tautomeric yet */ - num_changes = SetTautomericBonds( at, nNumBondPos, BondPos ); - tot_changes += (num_changes>0); - } - } - } - } - } - } - } - } -#endif /* KETO_ENOL_TAUT */ - -#if ( TAUT_OTHER == 1 ) /* { */ - if ( !tot_changes ) { -#define MAX_ALT_PATH_LEN 8 - int nMaxLenDfsPath = MAX_ALT_PATH_LEN; - int i1, i2; - AT_RANK *nDfsPathPos = (AT_RANK *)inchi_calloc( num_atoms, sizeof(nDfsPathPos[0]) ); - DFS_PATH DfsPath[MAX_ALT_PATH_LEN]; - int ret; - if ( !nDfsPathPos || !DfsPath ) { - tot_changes = CT_OUT_OF_RAM; /* */ - goto free_memory; - } -#if ( TAUT_15_NON_RING == 1 ) /***** post v.1 feature *****/ - if ( t_group_info->bTautFlags & TG_FLAG_1_5_TAUT ) { - /* 1,5 tautomerism; one of the endpoints should no be on a ring */ - /* - O OH O - || | || - A--pos- A--pos- A--pos- - / sib- // sib- ? / sib- - C ly C ly CH ly - \\ a <--> \ a <--> \ a - B--ring B--ring B--ring - | || || - NH N N - - Note: few recent modifications now allow the terminal N be in a ring, too - */ - for ( i1 = 0; i1 < num_atoms; i1 ++ ) { - /* find possible endpoint Z = at[i1] */ - if ( !(endpoint_valence = nGetEndpointInfo( at, i1, &eif1 ) ) /*|| - at[i1].nNumAtInRingSystem > 1*/ ) { - continue; /* not a possibly endpoint */ - } - - if ( 1 ) { - nNumEndPoints = 0; - nNumBondPos = 0; - - ret = nGet15TautInAltPath( at, i1, nDfsPathPos, - DfsPath, nMaxLenDfsPath, - EndPoint, sizeof(EndPoint)/sizeof(EndPoint[0]), - BondPos, sizeof(BondPos)/sizeof(BondPos[0]), - &nNumEndPoints, &nNumBondPos, - pBNS, pBD, num_atoms); - if ( ret > 0 ) { - if ( nNumEndPoints ) { - num_changes = RegisterEndPoints( t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS); - if ( num_changes == -1 ) { - nErr = CT_TAUCOUNT_ERR; - } - if ( num_changes < 0 ) { - nErr = num_changes; - } - if ( nErr ) - goto free_memory; - tot_changes += (num_changes > 0); - } - if ( nNumBondPos ) { - tot_changes += ( 0 < SetTautomericBonds( at, nNumBondPos, BondPos ) ); - } - } else - if ( IS_BNS_ERROR( ret ) ) { - nErr = ret; - goto free_memory; - } - } - } - } -#endif -#if ( TAUT_4PYRIDINOL_RINGS == 1 ) - /* 6-member rings */ - /* - O OH OH - || | | - / \ // \ / \\ - || || <--> | || <--> || | - \ / \\ / \ // - NH N N - */ - for ( i1 = 0; i1 < num_atoms; i1 ++ ) { - /* find possible endpoint Z = at[i1] */ - if ( 3 != (endpoint_valence = nGetEndpointInfo( at, i1, &eif1 ) ) || - 2 != at[i1].valence ) { - continue; /* not a nitrogen atom or a wrong valence */ - } - - if ( at[i1].nNumAtInRingSystem >= 6 ) { - nNumEndPoints = 0; - nNumBondPos = 0; - - ret = nGet15TautIn6MembAltRing( at, i1, nDfsPathPos, - DfsPath, nMaxLenDfsPath, - EndPoint, sizeof(EndPoint)/sizeof(EndPoint[0]), - BondPos, sizeof(BondPos)/sizeof(BondPos[0]), - &nNumEndPoints, &nNumBondPos, - pBNS, pBD, num_atoms); - if ( ret > 0 ) { - if ( nNumEndPoints ) { - num_changes = RegisterEndPoints( t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS); - if ( num_changes == -1 ) { - nErr = CT_TAUCOUNT_ERR; - } - if ( num_changes < 0 ) { - nErr = num_changes; - } - if ( nErr ) - goto free_memory; - tot_changes += (num_changes > 0); - } - if ( nNumBondPos ) { - tot_changes += ( 0 < SetTautomericBonds( at, nNumBondPos, BondPos ) ); - } - } else - if ( IS_BNS_ERROR( ret ) ) { - nErr = ret; - goto free_memory; - } - } - } -#endif /* TAUT_4PYRIDINOL_RINGS */ -#if ( TAUT_PYRAZOLE_RINGS == 1 ) - /* 5-member rings: - - Z Z - / \\ // \ - X Y <--> X Y - \\ / \ // - N--NH HN--N - - ^ ^ - search for these NH - */ - /* 5-member rings (pyrazole derivatives): look for the neighboring N */ - for ( i1 = 0; i1 < num_atoms; i1 ++ ) { - if ( 2 == at[i1].valence && - at[i1].nNumAtInRingSystem >= 5 && - 3 == (endpoint_valence = nGetEndpointInfo( at, i1, &eif1 )) - ) { - nMobile = at[i1].num_H + (at[i1].charge == -1); - for ( j = 0; j < at[i1].valence; j ++ ) { - int nMobile2, endpoint_valence2; - i2 = at[i1].neighbor[j]; - - /* may be important */ - if ( i2 >= i1 ) - continue; /* do not try same pair 2 times */ - - if ( at[i2].nRingSystem != at[i1].nRingSystem ) - continue; - - bond_type = (at[i1].bond_type[j] & ~BOND_MARK_ALL); - if ( bond_type != BOND_SINGLE && - bond_type != BOND_TAUTOM && - bond_type != BOND_ALT12NS && - bond_type != BOND_ALTERN || /* added 1-15-2002 */ - 2 != at[i2].valence || - 3 != (endpoint_valence2 = nGetEndpointInfo( at, i2, &eif2 ) ) ) { - continue; /* not a nitrogen atom or a wrong valence or not a single bond */ - } - nMobile2 = at[i2].num_H + (at[i2].charge == -1); /* number of mobile groups */ -#if ( TAUT_IGNORE_EQL_ENDPOINTS == 1 ) - if ( at[i1].endpoint && at[i1].endpoint == at[i2].endpoint ) - continue; /* atoms already belong to the same t-group */ -#endif - if ( !at[i1].endpoint && !at[i2].endpoint && 1!=nMobile + nMobile2 ) - continue; - - ret = nGet12TautIn5MembAltRing( at, i1, j, nDfsPathPos, - DfsPath, nMaxLenDfsPath, - EndPoint, sizeof(EndPoint)/sizeof(EndPoint[0]), - BondPos, sizeof(BondPos)/sizeof(BondPos[0]), - &nNumEndPoints, &nNumBondPos - , pBNS, pBD, num_atoms); - if ( ret > 0 ) { - if ( nNumEndPoints ) { - num_changes = RegisterEndPoints( t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS); - if ( num_changes == -1 ) { - nErr = CT_TAUCOUNT_ERR; - } - if ( num_changes < 0 ) { - nErr = num_changes; - } - if ( nErr ) - goto free_memory; - tot_changes += (num_changes > 0); - } - if ( nNumBondPos ) { - tot_changes += ( 0 < SetTautomericBonds( at, nNumBondPos, BondPos ) ); - } - } else - if ( IS_BNS_ERROR( ret ) ) { - nErr = ret; - goto free_memory; - } - } - } - } -#endif /* TAUT_PYRAZOLE_RINGS */ -#if ( TAUT_TROPOLONE_7 == 1 || TAUT_TROPOLONE_5 == 1 ) /* { */ - /******************************************************** - * A B - * | || - * 7-member rings (tropolones): look for M=Q--R--ZH, - * ^ ^ ^ ^ - * endpoint1 i1 i2 endpoint2 - * where A-Q-R=B belong to a 7-member alt. (except Q-R bond) ring: ..=A-(Q-R)=B-.. - * Bond Q-R should be single or tautomeric or alternating - * M=Q and R-ZH should be chain (non-ring) bonds - * Same for 5-member rings - */ - for ( i1 = 0; i1 < num_atoms; i1 ++ ) { - if ( at[i1].nNumAtInRingSystem >= -#if ( TAUT_TROPOLONE_5 == 1 ) - 5 -#else - 7 -#endif - && - bIsCenterPointStrict( at, i1 ) && -#if ( TAUT_RINGS_ATTACH_CHAIN == 1 ) - at[i1].bCutVertex && -#endif - at[i1].valence == 3 && !at[i1].endpoint ) { - int nMobile1, endpoint1, endpoint1_valence, bond_type1; - int nMobile2, endpoint2, endpoint2_valence, bond_type2; - for ( j = 0; j < at[i1].valence; j ++ ) { - i2 = at[i1].neighbor[j]; - /* - // may be important - if ( i2 > i1 ) - continue; // do not try same pair 2 times - */ - if ( at[i2].nRingSystem != at[i1].nRingSystem || - !bIsCenterPointStrict( at, i2 ) || -#if ( TAUT_RINGS_ATTACH_CHAIN == 1 ) - !at[i2].bCutVertex || -#endif - at[i2].valence != 3 || at[i2].endpoint ) - continue; - bond_type = (at[i1].bond_type[j] & ~BOND_MARK_ALL); - if ( bond_type != BOND_SINGLE && - bond_type != BOND_TAUTOM && - bond_type != BOND_ALT12NS && - bond_type != BOND_ALTERN ) { - continue; /* not a single bond between Q-R */ - } - /* find endpoints */ - for ( k = 0; k < at[i1].valence; k ++ ) { - endpoint1 = at[i1].neighbor[k]; - if ( endpoint1 == i2 ) - continue; /* j == k */ - if ( !(endpoint1_valence = nGetEndpointInfo( at, endpoint1, &eif1 ) ) ) - continue; /* not an endpoint1 element or can't have mobile groups */ -#if ( TAUT_RINGS_ATTACH_CHAIN == 1 ) - if ( at[endpoint1].nRingSystem == at[i1].nRingSystem ) - continue; -#endif - nMobile1 = at[endpoint1].num_H + (at[endpoint1].charge == -1); /* number of mobile groups */ - if ( nMobile1 + at[endpoint1].chem_bonds_valence != endpoint1_valence ) - continue; /* abnormal endpoint1 valence; ignore. */ - bond_type1 = (at[i1].bond_type[k] & ~BOND_MARK_ALL); - - if ( bond_type1 != BOND_SINGLE && - bond_type1 != BOND_DOUBLE && - bond_type1 != BOND_TAUTOM && - bond_type1 != BOND_ALT12NS && - bond_type1 != BOND_ALTERN ) - continue; - - for ( m = 0; m < at[i2].valence; m ++ ) { - endpoint2 = at[i2].neighbor[m]; - if ( endpoint2 == i1 ) - continue; - if ( !(endpoint2_valence = nGetEndpointInfo( at, endpoint2, &eif2 )) ) - continue; /* not an endpoint2 element or can't have mobile groups */ -#if ( TAUT_RINGS_ATTACH_CHAIN == 1 ) - if ( at[endpoint2].nRingSystem == at[i2].nRingSystem ) - continue; -#endif - nMobile2 = at[endpoint2].num_H + (at[endpoint2].charge == -1); /* number of mobile groups */ - bond_type2 = (at[i2].bond_type[m] & ~BOND_MARK_ALL); - - if ( bond_type2 != BOND_SINGLE && - bond_type2 != BOND_DOUBLE && - bond_type2 != BOND_TAUTOM && - bond_type2 != BOND_ALT12NS && - bond_type2 != BOND_ALTERN ) - continue; - - /* final test for possible tautomerism */ - nMobile = 0; - - if ( ALLOWED_EDGE(pBNS, i1, k) && ALLOWED_EDGE(pBNS, i2, m) ) { - - /* can mobile group move from 1 to 2? */ - nMobile += (at[endpoint1].endpoint || nMobile1) && /* from endpoint1 */ - (bond_type1 != BOND_DOUBLE) && - - (at[endpoint2].endpoint || /* to endpoint2 */ - eif2.cNeutralBondsValence > at[endpoint2].valence ) && - (bond_type2 != BOND_SINGLE); - - - /* can mobile group move from 2 to 1? */ - nMobile += (at[endpoint2].endpoint || nMobile2) && /* from endpoint2 */ - (bond_type2 != BOND_DOUBLE) && /*changed from BOND_SINGLE 2004-02-26 */ - - (at[endpoint1].endpoint || /* to endpoint1 */ - eif1.cNeutralBondsValence > at[endpoint1].valence ) && - (bond_type1 != BOND_SINGLE); - } - if ( !nMobile ) - continue; - - if ( bond_type1 == bond_type2 && - (bond_type1 == BOND_SINGLE || bond_type1 == BOND_DOUBLE) ) - continue; - /* -- old -- - if ( !at[endpoint1].endpoint && !at[endpoint2].endpoint && 1 != nMobile1 + nMobile2 ) - continue; - */ - /* -- new -- - - if ( !at[endpoint1].endpoint && !at[endpoint2].endpoint ) { - if ( !(bond_type1 == BOND_SINGLE || bond_type1 == BOND_DOUBLE) || - !(bond_type2 == BOND_SINGLE || bond_type2 == BOND_DOUBLE) ) { - // at this point bond_type1 != bond_type2 - continue; - } - if ( bond_type1 == BOND_SINGLE && !nMobile1 || - bond_type2 == BOND_SINGLE && !nMobile2 || - 0 == nMobile1 + nMobile2 ) { - continue; - } - } - */ -#if ( TAUT_TROPOLONE_7 == 1 ) - if ( at[i1].nNumAtInRingSystem >= 7 ) { - ret = nGet14TautIn7MembAltRing( at, i1, j, k, m, nDfsPathPos, - DfsPath, nMaxLenDfsPath, - EndPoint, sizeof(EndPoint)/sizeof(EndPoint[0]), - BondPos, sizeof(BondPos)/sizeof(BondPos[0]), - &nNumEndPoints, &nNumBondPos, - pBNS, pBD, num_atoms); - if ( ret > 0 ) { - if ( nNumEndPoints ) { - num_changes = RegisterEndPoints( t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS); - if ( num_changes == -1 ) { - nErr = CT_TAUCOUNT_ERR; - } - if ( num_changes < 0 ) { - nErr = num_changes; - } - if ( nErr ) - goto free_memory; - tot_changes += (num_changes > 0); - } - if ( nNumBondPos ) { - tot_changes += ( 0 < SetTautomericBonds( at, nNumBondPos, BondPos ) ); - } - } else - if ( IS_BNS_ERROR( ret ) ) { - nErr = ret; - goto free_memory; - } - } -#endif - -#if ( TAUT_TROPOLONE_5 == 1 ) - if ( at[i1].nNumAtInRingSystem >= 5 ) { - ret = nGet14TautIn5MembAltRing( at, i1, j, k, m, nDfsPathPos, - DfsPath, nMaxLenDfsPath, - EndPoint, sizeof(EndPoint)/sizeof(EndPoint[0]), - BondPos, sizeof(BondPos)/sizeof(BondPos[0]), - &nNumEndPoints, &nNumBondPos, - pBNS, pBD, num_atoms); - if ( ret > 0 ) { - if ( nNumEndPoints ) { - num_changes = RegisterEndPoints( t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS); - if ( num_changes == -1 ) { - nErr = CT_TAUCOUNT_ERR; - } - if ( num_changes < 0 ) { - nErr = num_changes; - } - if ( nErr ) - goto free_memory; - tot_changes += (num_changes > 0); - } - if ( nNumBondPos ) { - tot_changes += ( 0 < SetTautomericBonds( at, nNumBondPos, BondPos ) ); - } - } else - if ( IS_BNS_ERROR( ret ) ) { - nErr = ret; - goto free_memory; - } - } -#endif - } - } - } - } - } -#endif /* } TAUT_TROPOLONE */ -free_memory: - if ( nDfsPathPos ) { - inchi_free( nDfsPathPos ); - } -#undef MAX_ALT_PATH_LEN - } -#endif /* } FIND_RING_SYSTEMS */ -exit_function: - return nErr < 0? nErr : tot_changes; -} - - - -/*****************************************************************************/ -int free_t_group_info( T_GROUP_INFO *t_group_info ) -{ - if ( t_group_info ) { - if ( t_group_info->t_group ) { - inchi_free( t_group_info->t_group ); - } - if ( t_group_info->nEndpointAtomNumber ) { - inchi_free( t_group_info->nEndpointAtomNumber ); - } - if ( t_group_info->tGroupNumber ) { - inchi_free( t_group_info->tGroupNumber ); - } - if ( t_group_info->nIsotopicEndpointAtomNumber ) { - inchi_free( t_group_info->nIsotopicEndpointAtomNumber ); - } - memset( t_group_info, 0, sizeof(*t_group_info)); - } - return 0; -} - - - -/*****************************************************************************/ -int make_a_copy_of_t_group_info( T_GROUP_INFO *t_group_info, T_GROUP_INFO *t_group_info_orig ) -{ - int err = 0, len; - free_t_group_info( t_group_info ); - if ( t_group_info_orig && t_group_info ) { - if ( (len=t_group_info_orig->max_num_t_groups) > 0 ) { - if (t_group_info->t_group = - (T_GROUP*) inchi_malloc( len * sizeof(t_group_info->t_group[0]))) { - memcpy(t_group_info->t_group, - t_group_info_orig->t_group, - len * sizeof(t_group_info->t_group[0])); - } else { - err ++; - } - } - if ( (len = t_group_info_orig->nNumEndpoints) > 0 ) { - if (t_group_info->nEndpointAtomNumber = - (AT_NUMB*) inchi_malloc( len * sizeof(t_group_info->nEndpointAtomNumber[0]))) { - memcpy(t_group_info->nEndpointAtomNumber, - t_group_info_orig->nEndpointAtomNumber, - len * sizeof(t_group_info->nEndpointAtomNumber[0])); - } else { - err ++; - } - } - if ( (len = t_group_info_orig->num_t_groups) > 0 ) { - if (t_group_info->tGroupNumber = - (AT_NUMB*) inchi_malloc( len * TGSO_TOTAL_LEN * sizeof(t_group_info->tGroupNumber[0]))) { - memcpy(t_group_info->tGroupNumber, - t_group_info_orig->tGroupNumber, - len * TGSO_TOTAL_LEN * sizeof(t_group_info->tGroupNumber[0])); - } else { - err ++; - } - } - if ( (len = t_group_info_orig->nNumIsotopicEndpoints) > 0 ) { - if (t_group_info->nIsotopicEndpointAtomNumber = - (AT_NUMB*) inchi_malloc( len * sizeof(t_group_info->nIsotopicEndpointAtomNumber[0]))) { - memcpy(t_group_info->nIsotopicEndpointAtomNumber, - t_group_info_orig->nIsotopicEndpointAtomNumber, - len * sizeof(t_group_info->nIsotopicEndpointAtomNumber[0])); - } else { - err ++; - } - } - if ( !err ) { - t_group_info->nNumEndpoints = t_group_info_orig->nNumEndpoints; - t_group_info->num_t_groups = t_group_info_orig->num_t_groups; - t_group_info->max_num_t_groups = t_group_info_orig->max_num_t_groups; - t_group_info->bIgnoreIsotopic = t_group_info_orig->bIgnoreIsotopic; - t_group_info->nNumIsotopicEndpoints = t_group_info_orig->nNumIsotopicEndpoints; - t_group_info->tni = t_group_info_orig->tni; - /* - t_group_info->nNumRemovedExplicitH = t_group_info_orig->nNumRemovedExplicitH; - t_group_info->nNumRemovedProtons = t_group_info_orig->nNumRemovedProtons; - t_group_info->bNormalizationFlags = t_group_info_orig->bNormalizationFlags; - */ - /* - t_group_info->bHardAddedRemovedProtons = t_group_info_orig->bHardAddedRemovedProtons; - t_group_info->bSimpleAddedRemovedProtons = t_group_info_orig->bSimpleAddedRemovedProtons; - t_group_info->nNumCanceledCharges = t_group_info_orig->nNumCanceledCharges; - */ - } - t_group_info->bTautFlags = t_group_info_orig->bTautFlags; - t_group_info->bTautFlagsDone = t_group_info_orig->bTautFlagsDone; - } - return err; -} - - - -/*****************************************************************************/ -/* Set tautomer group isotopic sort keys */ -/*****************************************************************************/ -int set_tautomer_iso_sort_keys( T_GROUP_INFO *t_group_info ) -{ - T_GROUP *t_group; - T_GROUP_ISOWT Mult = 1; - int i, j, num_t_groups, num_iso_t_groups = 0; - if ( !t_group_info || !(t_group = t_group_info->t_group) || - 0 >= (num_t_groups = t_group_info->num_t_groups) || t_group_info->nNumIsotopicEndpoints ) - return 0; - for ( i = 0; i < num_t_groups; i ++ ) { - t_group[i].iWeight = 0; - j = T_NUM_ISOTOPIC - 1; - Mult = 1; - do { - t_group[i].iWeight += Mult * (T_GROUP_ISOWT)t_group[i].num[T_NUM_NO_ISOTOPIC+j]; - } while ( --j >= 0 && (Mult *= T_GROUP_ISOWT_MULT) ); - num_iso_t_groups += (t_group[i].iWeight != 0); - } - return num_iso_t_groups; -} - -/****************************************************************************** - * - * Fill t_group_info with information necessary to fill out tautomer part - * of the linear connection table record. - * Note: on input, t_group_info should contain information created by MarkTautomerGroups() - * No previous t_group_info adjustment due to throwing out disconnected parts of - * the chemical structure is needed. - * - * Note2: throws out t_groups containing negative charges only (IGNORE_TGROUP_WITHOUT_H==1) - * (leave their tautomeric bonds unchanged) - * Note3: removes negative charges from other tautomeric groups - * and adjust counts of mobile atoms if permitted (REMOVE_TGROUP_CHARGE==1) - */ -int CountTautomerGroups( sp_ATOM *at, int num_atoms, T_GROUP_INFO *t_group_info ) -{ - int i, j, ret = 0, nNumEndpoints, max_t_group, num_groups_noH; - - AT_NUMB nGroupNumber, nNewGroupNumber, *nCurrEndpointAtNoPos = NULL; - - T_GROUP *t_group; - int num_t; - /* int bIgnoreIsotopic, max_num_t; */ - AT_NUMB *nTautomerGroupNumber = NULL; - AT_NUMB *nEndpointAtomNumber = NULL; - AT_NUMB *tGroupNumber = NULL; - - if ( !t_group_info || !t_group_info->t_group || 0 >= t_group_info->max_num_t_groups ) { - return 0; /* empty t-groups */ - } - num_t = t_group_info->num_t_groups; - t_group = t_group_info->t_group; - /* - max_num_t = t_group_info->max_num_t_groups; - bIgnoreIsotopic = t_group_info->bIgnoreIsotopic; - */ - num_groups_noH = 0; - - /* the following 2 arrays are to be rebuilt here */ - if ( t_group_info->nEndpointAtomNumber ) { - inchi_free ( t_group_info->nEndpointAtomNumber ); - t_group_info->nEndpointAtomNumber = NULL; - } - if ( t_group_info->tGroupNumber ) { - inchi_free ( t_group_info->tGroupNumber ); - t_group_info->tGroupNumber = NULL; - } - /* find max_t_group */ - for ( i = 0, max_t_group = 0; i < t_group_info->num_t_groups; i ++ ) { - if ( max_t_group < t_group[i].nGroupNumber ) - max_t_group = t_group[i].nGroupNumber; - } - /* allocate memory for temp storage of numbers of endpoints */ - if ( max_t_group && - !(nTautomerGroupNumber = (AT_NUMB*) inchi_calloc( max_t_group+1, sizeof(nTautomerGroupNumber[0]) ) /*temp*/ ) ) { - goto err_exit_function; /* program error: out of RAM */ /* */ - } - - /* count endpoints for each tautomer group */ - for ( i = 0, nNumEndpoints = 0; i < num_atoms; i ++ ) { - if ( (j = at[i].endpoint) == 0 ) - continue; - if ( j > max_t_group ) /* debug only */ - goto err_exit_function; /* program error */ /* */ - nTautomerGroupNumber[j] ++; - nNumEndpoints ++; - } - - if ( !nNumEndpoints ) { - goto exit_function; /* not a tautomer */ - } - - /* allocate temporary array */ - if ( !(nEndpointAtomNumber = (AT_NUMB*) inchi_calloc( nNumEndpoints, sizeof(nEndpointAtomNumber[0]) ) ) || - !(nCurrEndpointAtNoPos = (AT_NUMB*) inchi_calloc( num_t, sizeof(nCurrEndpointAtNoPos[0]) ) /*temp*/ ) ) { - goto err_exit_function; /* program error: out of RAM */ /* */ - } - /* - * Remove missing endpoints from t_group. Since only one - * disconnected part is processed, some endpoints groups may have disappeared. - * Mark t_groups containing charges only for subsequent removal - */ - for ( i = 0, nNewGroupNumber = 0; i < num_t; /*i ++*/ ) { - int bNoH = 0, nNumH; - nGroupNumber = t_group[i].nGroupNumber; - for ( j = 1, nNumH = t_group[i].num[0]; j < T_NUM_NO_ISOTOPIC; j ++ ) { - nNumH -= (int)t_group[i].num[j]; - } - if ( t_group[i].nNumEndpoints != nTautomerGroupNumber[(int)nGroupNumber] -#if ( IGNORE_TGROUP_WITHOUT_H == 1 ) - || (bNoH = (t_group[i].num[0]==t_group[i].num[1])) /* only for (H,-) t-groups; (+) t-groups are not removed */ -#endif - ) { - if ( !nTautomerGroupNumber[(int)nGroupNumber] || bNoH ) { - /* the group belongs to another disconnected part of the structure or has only charges */ - /* Remove the group */ - num_t --; - if ( i < num_t ) - memmove( t_group+i, t_group+i+1, (num_t-i)*sizeof(t_group[0]) ); - if ( bNoH ) { - /* group contains no mobile hydrogen atoms, only charges. Prepare to remove it. */ - nTautomerGroupNumber[(int)nGroupNumber] = 0; - num_groups_noH ++; - } - /*i --;*/ - } else { - /* different number of endpoints */ - goto err_exit_function; /* program error */ /* */ - } - } else { - /* renumber t_group and prepare to renumber at[i].endpoint */ - nTautomerGroupNumber[(int)nGroupNumber] = - t_group[i].nGroupNumber = ++nNewGroupNumber; /* = i+1 */ - /* get first group atom orig. number position in the nEndpointAtomNumber[] */ - /* and in the tautomer endpoint canon numbers part of the connection table */ - t_group[i].nFirstEndpointAtNoPos = nCurrEndpointAtNoPos[i] = - i? (t_group[i-1].nFirstEndpointAtNoPos+t_group[i-1].nNumEndpoints) : 0; - t_group[i].num[0] = nNumH; -#if ( REMOVE_TGROUP_CHARGE == 1 ) - t_group[i].num[1] = 0; /* remove only (-) charges */ -#endif - /* -- wrong condition. Disabled. - if ( t_group[i].nGroupNumber != i + 1 ) { // for debug only - goto err_exit_function; // program error - } - */ - i ++; - } - } - if ( num_t != nNewGroupNumber ) { /* for debug only */ - goto err_exit_function; /* program error */ /* */ - } - - /* check if any tautomer group was left */ - if ( !nNewGroupNumber ) { - if ( !num_groups_noH ) - goto err_exit_function; /* program error: not a tautomer */ /* */ - else - goto exit_function; - } - /* - * an array for tautomer group sorting later, at the time of storing Connection Table - * Later the sorting consists out of 2 steps: - * 1) Sort t_group[i].nNumEndpoints endpoint atom ranks within each endpoint group - * starting from t_group[i].nFirstEndpointAtNoPos; i = 0..t_group_info->num_t_groups-1 - * 2) Sort the groups indexes t_group_info->tGroupNumber[] - */ - if ( !(tGroupNumber= - (AT_NUMB*)inchi_calloc(nNewGroupNumber*TGSO_TOTAL_LEN, sizeof(tGroupNumber[0])))) { - goto err_exit_function; /* out of RAM */ - } - for ( i = 0; i < nNewGroupNumber; i ++ ) { - tGroupNumber[i] = (AT_NUMB)i; /* initialization: original t_group number = (at[i]->endpoint-1) */ - } - /* - * renumber endpoint atoms and save their orig. atom - * numbers for filling out the tautomer part of the LinearCT. - * nCurrEndpointAtNoPos[j] is an index of the atom number in the nEndpointAtomNumber[] - */ - for ( i = 0; i < num_atoms; i ++ ) { - if ( j = (int)at[i].endpoint ) { - j = (int)(at[i].endpoint = nTautomerGroupNumber[j])-1; /* new t_group number */ - if ( j >= 0 ) { /* j=-1 in case of no mobile hydrogen atoms (charges only), group being removed */ - if ( nCurrEndpointAtNoPos[j] >= /* debug only */ - t_group[j].nFirstEndpointAtNoPos+t_group[j].nNumEndpoints ) { - goto err_exit_function; /* program error */ /* */ - } - nEndpointAtomNumber[(int)nCurrEndpointAtNoPos[j] ++] = (AT_NUMB)i; - } else { - nNumEndpoints --; /* endpoint has been removed */ - } - } - } - t_group_info->num_t_groups = nNewGroupNumber; - t_group_info->nNumEndpoints = nNumEndpoints; - t_group_info->nEndpointAtomNumber = nEndpointAtomNumber; - t_group_info->tGroupNumber = tGroupNumber; /* only the 1st segment filled */ - inchi_free ( nTautomerGroupNumber ); - inchi_free ( nCurrEndpointAtNoPos ); - return nNumEndpoints + T_GROUP_HDR_LEN * nNewGroupNumber + 1; /* nLenLinearCTTautomer */ - -err_exit_function: - ret = CT_TAUCOUNT_ERR; -exit_function: - /* release allocated memory; set "no tautomeric group" */ - if ( nEndpointAtomNumber ) - inchi_free ( nEndpointAtomNumber ); - if ( nTautomerGroupNumber ) - inchi_free ( nTautomerGroupNumber ); - if ( tGroupNumber ) - inchi_free ( tGroupNumber ); - if ( nCurrEndpointAtNoPos ) - inchi_free ( nCurrEndpointAtNoPos ); - t_group_info->nNumEndpoints = 0; - t_group_info->num_t_groups = 0; - if ( !ret && ((t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) || - t_group_info->nNumIsotopicEndpoints>1 && (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE))) ) { - ret = 1; /* only protons have been (re)moved or neitralization happened */ - } - return ret; -} -#if ( READ_INCHI_STRING == 1 ) -#if ( INCLUDE_NORMALIZATION_ENTRY_POINT == 1 ) -/*****************************************************************************/ -int CountTautomerGroupsInpAt( inp_ATOM *at, int num_atoms, T_GROUP_INFO *t_group_info ) -{ - int i, j, ret = 0, nNumEndpoints, max_t_group, num_groups_noH; - - AT_NUMB nGroupNumber, nNewGroupNumber, *nCurrEndpointAtNoPos = NULL; - - T_GROUP *t_group; - int num_t; - /* int bIgnoreIsotopic, max_num_t; */ - AT_NUMB *nTautomerGroupNumber = NULL; - AT_NUMB *nEndpointAtomNumber = NULL; - AT_NUMB *tGroupNumber = NULL; - - if ( !t_group_info || !t_group_info->t_group || 0 >= t_group_info->max_num_t_groups ) { - return 0; /* empty t-groups */ - } - num_t = t_group_info->num_t_groups; - t_group = t_group_info->t_group; - /* - max_num_t = t_group_info->max_num_t_groups; - bIgnoreIsotopic = t_group_info->bIgnoreIsotopic; - */ - num_groups_noH = 0; - - /* the following 2 arrays are to be rebuilt here */ - if ( t_group_info->nEndpointAtomNumber ) { - inchi_free ( t_group_info->nEndpointAtomNumber ); - t_group_info->nEndpointAtomNumber = NULL; - } - if ( t_group_info->tGroupNumber ) { - inchi_free ( t_group_info->tGroupNumber ); - t_group_info->tGroupNumber = NULL; - } - /* find max_t_group */ - for ( i = 0, max_t_group = 0; i < t_group_info->num_t_groups; i ++ ) { - if ( max_t_group < t_group[i].nGroupNumber ) - max_t_group = t_group[i].nGroupNumber; - } - /* allocate memory for temp storage of numbers of endpoints */ - if ( max_t_group && - !(nTautomerGroupNumber = (AT_NUMB*) inchi_calloc( max_t_group+1, sizeof(nTautomerGroupNumber[0]) ) /*temp*/ ) ) { - goto err_exit_function; /* program error: out of RAM */ /* */ - } - - /* count endpoints for each tautomer group */ - for ( i = 0, nNumEndpoints = 0; i < num_atoms; i ++ ) { - if ( (j = at[i].endpoint) == 0 ) - continue; - if ( j > max_t_group ) /* debug only */ - goto err_exit_function; /* program error */ /* */ - nTautomerGroupNumber[j] ++; - nNumEndpoints ++; - } - - if ( !nNumEndpoints ) { - goto exit_function; /* not a tautomer */ - } - - /* allocate temporary array */ - if ( !(nEndpointAtomNumber = (AT_NUMB*) inchi_calloc( nNumEndpoints, sizeof(nEndpointAtomNumber[0]) ) ) || - !(nCurrEndpointAtNoPos = (AT_NUMB*) inchi_calloc( num_t, sizeof(nCurrEndpointAtNoPos[0]) ) /*temp*/ ) ) { - goto err_exit_function; /* program error: out of RAM */ /* */ - } - /* - * Remove missing endpoints from t_group. Since only one - * disconnected part is processed, some endpoints groups may have disappeared. - * Mark t_groups containing charges only for subsequent removal - */ - for ( i = 0, nNewGroupNumber = 0; i < num_t; /*i ++*/ ) { - int bNoH = 0, nNumH; - nGroupNumber = t_group[i].nGroupNumber; - for ( j = 1, nNumH = t_group[i].num[0]; j < T_NUM_NO_ISOTOPIC; j ++ ) { - nNumH -= (int)t_group[i].num[j]; - } - if ( t_group[i].nNumEndpoints != nTautomerGroupNumber[(int)nGroupNumber] -#if ( IGNORE_TGROUP_WITHOUT_H == 1 ) - || (bNoH = (t_group[i].num[0]==t_group[i].num[1])) /* only for (H,-) t-groups; (+) t-groups are not removed */ -#endif - ) { - if ( !nTautomerGroupNumber[(int)nGroupNumber] || bNoH ) { - /* the group belongs to another disconnected part of the structure or has only charges */ - /* Remove the group */ - num_t --; - if ( i < num_t ) - memmove( t_group+i, t_group+i+1, (num_t-i)*sizeof(t_group[0]) ); - if ( bNoH ) { - /* group contains no mobile hydrogen atoms, only charges. Prepare to remove it. */ - nTautomerGroupNumber[(int)nGroupNumber] = 0; - num_groups_noH ++; - } - /*i --;*/ - } else { - /* different number of endpoints */ - goto err_exit_function; /* program error */ /* */ - } - } else { - /* renumber t_group and prepare to renumber at[i].endpoint */ - nTautomerGroupNumber[(int)nGroupNumber] = - t_group[i].nGroupNumber = ++nNewGroupNumber; /* = i+1 */ - /* get first group atom orig. number position in the nEndpointAtomNumber[] */ - /* and in the tautomer endpoint canon numbers part of the connection table */ - t_group[i].nFirstEndpointAtNoPos = nCurrEndpointAtNoPos[i] = - i? (t_group[i-1].nFirstEndpointAtNoPos+t_group[i-1].nNumEndpoints) : 0; - t_group[i].num[0] = nNumH; -#if ( REMOVE_TGROUP_CHARGE == 1 ) - t_group[i].num[1] = 0; /* remove only (-) charges */ -#endif - /* -- wrong condition. Disabled. - if ( t_group[i].nGroupNumber != i + 1 ) { // for debug only - goto err_exit_function; // program error - } - */ - i ++; - } - } - if ( num_t != nNewGroupNumber ) { /* for debug only */ - goto err_exit_function; /* program error */ /* */ - } - - /* check if any tautomer group was left */ - if ( !nNewGroupNumber ) { - if ( !num_groups_noH ) - goto err_exit_function; /* program error: not a tautomer */ /* */ - else - goto exit_function; - } - /* - * an array for tautomer group sorting later, at the time of storing Connection Table - * Later the sorting consists out of 2 steps: - * 1) Sort t_group[i].nNumEndpoints endpoint atom ranks within each endpoint group - * starting from t_group[i].nFirstEndpointAtNoPos; i = 0..t_group_info->num_t_groups-1 - * 2) Sort the groups indexes t_group_info->tGroupNumber[] - */ - if ( !(tGroupNumber= - (AT_NUMB*)inchi_calloc(nNewGroupNumber*TGSO_TOTAL_LEN, sizeof(tGroupNumber[0])))) { - goto err_exit_function; /* out of RAM */ - } - for ( i = 0; i < nNewGroupNumber; i ++ ) { - tGroupNumber[i] = (AT_NUMB)i; /* initialization: original t_group number = (at[i]->endpoint-1) */ - } - /* - * renumber endpoint atoms and save their orig. atom - * numbers for filling out the tautomer part of the LinearCT. - * nCurrEndpointAtNoPos[j] is an index of the atom number in the nEndpointAtomNumber[] - */ - for ( i = 0; i < num_atoms; i ++ ) { - if ( j = (int)at[i].endpoint ) { - j = (int)(at[i].endpoint = nTautomerGroupNumber[j])-1; /* new t_group number */ - if ( j >= 0 ) { /* j=-1 in case of no mobile hydrogen atoms (charges only), group being removed */ - if ( nCurrEndpointAtNoPos[j] >= /* debug only */ - t_group[j].nFirstEndpointAtNoPos+t_group[j].nNumEndpoints ) { - goto err_exit_function; /* program error */ /* */ - } - nEndpointAtomNumber[(int)nCurrEndpointAtNoPos[j] ++] = (AT_NUMB)i; - } else { - nNumEndpoints --; /* endpoint has been removed */ - } - } - } - t_group_info->num_t_groups = nNewGroupNumber; - t_group_info->nNumEndpoints = nNumEndpoints; - t_group_info->nEndpointAtomNumber = nEndpointAtomNumber; - t_group_info->tGroupNumber = tGroupNumber; /* only the 1st segment filled */ - inchi_free ( nTautomerGroupNumber ); - inchi_free ( nCurrEndpointAtNoPos ); - return nNumEndpoints + T_GROUP_HDR_LEN * nNewGroupNumber + 1; /* nLenLinearCTTautomer */ - -err_exit_function: - ret = CT_TAUCOUNT_ERR; -exit_function: - /* release allocated memory; set "no tautomeric group" */ - if ( nEndpointAtomNumber ) - inchi_free ( nEndpointAtomNumber ); - if ( nTautomerGroupNumber ) - inchi_free ( nTautomerGroupNumber ); - if ( tGroupNumber ) - inchi_free ( tGroupNumber ); - if ( nCurrEndpointAtNoPos ) - inchi_free ( nCurrEndpointAtNoPos ); - t_group_info->nNumEndpoints = 0; - t_group_info->num_t_groups = 0; - if ( !ret && ((t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) || - t_group_info->nNumIsotopicEndpoints>1 && (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE))) ) { - ret = 1; /* only protons have been (re)moved or neitralization happened */ - } - return ret; -} -#endif -#endif - - - -/***************************************************************************** - * tautomers: Compare for sorting - *****************************************************************************/ -/* Compare for sorting Ranks only */ -/* Globals: pn_tRankForSort */ -int CompRankTautomer(const void* a1, const void* a2 ) -{ - int ret = (int)pn_tRankForSort[(int)(*(const AT_RANK*)a1)] - - (int)pn_tRankForSort[(int)(*(const AT_RANK*)a2)]; - return ret; -} - - - -/*****************************************************************************/ -int SortTautomerGroupsAndEndpoints( T_GROUP_INFO *t_group_info, int num_atoms, int num_at_tg, AT_RANK *nRank ) -{ - int i, nFirstEndpointAtNoPos, nNumEndpoints; - AT_NUMB *nEndpointAtomNumber; - int num_t_groups = num_at_tg - num_atoms; - T_GROUP *t_group = NULL; - /* check if sorting is required */ - - if ( num_t_groups <= 0 || t_group_info->nNumEndpoints < 2 ) { - return 0; /* no tautomer data */ - } - t_group = t_group_info->t_group; - /* sort endpoints within the groups */ - for ( i = 0; i < num_t_groups; i ++ ) { - if ( t_group[i].nNumEndpoints < 2 ) - continue; /* program error; should not happen */ /* */ - /* set globals for sorting */ - nFirstEndpointAtNoPos = t_group[i].nFirstEndpointAtNoPos; - nNumEndpoints = t_group[i].nNumEndpoints; - if ( nNumEndpoints + nFirstEndpointAtNoPos > t_group_info->nNumEndpoints ) { /* for debug only */ - return CT_TAUCOUNT_ERR; /* program error */ /* */ - } - nEndpointAtomNumber = t_group_info->nEndpointAtomNumber+(int)nFirstEndpointAtNoPos; - pn_tRankForSort = nRank; - insertions_sort( nEndpointAtomNumber, nNumEndpoints, sizeof(nEndpointAtomNumber[0]), CompRankTautomer); - } - /* sort the tautomeric groups according to their ranks only - (that is, ignoring the isotopic composition of the mobile groups and ranks of the endpoints) */ - if ( t_group_info->num_t_groups > 1 ) { - /* set globals for sorting */ - /* a hack: the ranks of all tautomeric groups are */ - /* located at nRank[num_atoms..num_at_tg-1] */ - pn_tRankForSort = nRank+num_atoms; - /* sort */ - /* ordering numbers to sort : t_group_info->tGroupNumber; */ - insertions_sort( t_group_info->tGroupNumber, num_t_groups, - sizeof(t_group_info->tGroupNumber[0]), CompRankTautomer); - } - return t_group_info->num_t_groups; -} diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichitaut.h b/INCHI-1-SRC/INCHI_API/inchi_dll/ichitaut.h deleted file mode 100644 index 3199022..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichitaut.h +++ /dev/null @@ -1,444 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHITAUT_H__ -#define __INCHITAUT_H__ - -#include "inpdef.h" -#include "ichi_bns.h" - - -/******************************************************* - --- Header of tautomers groups --- - --- Each entry is AT_TAUTOMER_HDR type --- - number of tautomer groups (nNumTautGroups) - index of the first tautomer group (#1) - ... - index of the last tautomer group (#nNumTautGroups) - --- end of the Header of tautomers groups description --- - - --- One endpoint group description --- - --- Each entry has AT_TAUTOMER type members --- - - number of endpoints (0=end of list) - number of mobile groups, including number of negative charges (=num(H)+num(-)) - number of negative charges - number of 1H atoms - number of 2H (deuterium) atoms - number of 3H (tritium) atoms - - atom rank #1 (ascending order) - ... - atom rank #endpoints - --- end of the endpoint group description ---- - - ---------------------------------------------- - Note: - In the following Linear CT Tautomer descriptions - we assume the tautomeric groups and the endpoints - within them have been properly sorted - - --------- Linear CT Tautomer description ----- - - -- fixed length part, non-isotopic -- - number of endpoints = t_group->nNumEndpoints - number of mobile atoms = t_group->num[0] - ... - number of negative charges = t_group->num[T_NUM_NO_ISOTOPIC-1] - -- fixed length part, isotopic -- - number of T (3H) = t_group->num[T_NUM_NO_ISOTOPIC] - ... - number of 1H = t_group->num[T_NUM_NO_ISOTOPIC+T_NUM_ISOTOPIC-1] - -- variable length part -- - rank of the first endpoint = nRank[t_group_info->nEndpointAtomNumber[t_group->nFirstEndpointAtNoPos]]; - ... - rank of the last endpoint = nRank[t_group_info->nEndpointAtomNumber[t_group->nFirstEndpointAtNoPos+t_group->nNumEndpoints-1]]; - - --------- Linear CT Isotopic Tautomer description ----- - - number of T (3H) = t_group->num[T_NUM_NO_ISOTOPIC] - ... - number of 1H = t_group->num[T_NUM_NO_ISOTOPIC+T_NUM_ISOTOPIC-1] - t-group ordering number in the Linear CT Tautomer, starts from 1 - -***************************************************************/ - - -#define T_NUM_NO_ISOTOPIC 2 -#define T_NUM_ISOTOPIC NUM_H_ISOTOPES /* was 2, now 3 */ - -#define T_GROUP_HDR_LEN (1+T_NUM_NO_ISOTOPIC /*+T_NUM_ISOTOPIC*/) /* LinearCTTautomer */ - -typedef AT_NUMB AT_TAUTOMER; /* LinearCTTautomer */ - -typedef AT_ISO_SORT_KEY T_GROUP_ISOWT; /* must hold value up to T_GROUP_ISOWT_MULT^3-1 */ - /* similar to AT_ISO_SORT_KEY */ -/* = num_1H + T_GROUP_ISOWT_MULT*(num_D + T_GROUP_ISOWT_MULT*num_T) */ - - -#define T_GROUP_ISOWT_MULT 1024 /* (max. number of identical isotopic hydrogens in a taut. group) + 1 */ - /* changed from 256U 9-12-2003 */ - /* (similar to AT_ISO_SORT_KEY_MULT ) */ -/* note: (long)T_GROUP_ISOWT should always be positive (have zero sign bit) */ - -typedef struct tagIsotopicTautomerGroup { - AT_NUMB tgroup_num; /* ordering number of a tautomer group with isotopes > 0 */ - /* - union { - struct { - AT_NUMB num_T; - AT_NUMB num_D; - AT_NUMB num_1H; - }; - AT_NUMB num[T_NUM_ISOTOPIC]; - }; - */ - AT_NUMB num[T_NUM_ISOTOPIC]; /* inverted order: num_T, num_D, num_1H */ -} AT_ISO_TGROUP; - -typedef enum tagTG_NumDA { /* 2004-02-26 */ - TG_Num_dH, /* number of H donors that have only H (all single bonds) */ - TG_Num_dM, /* number of H donors that have (-) (all single bonds) */ - TG_Num_aH, /* number of H acceptors that have H and no (-) (+a double bond) */ - TG_Num_aM, /* number of H acceptors that have (-) and possibly H (+ one double bond) */ - TG_Num_dO, /* number of H donors =C-OH or =C-O(-) */ - TG_Num_aO, /* number of H acceptors -C=O */ - TG_NUM_DA /* number of elements in an array */ -} TGNUMDA; - -typedef struct tagTautomerGroup { - /* - union { - struct { - // T_NUM_NO_ISOTOPIC = 2 elements: - AT_RANK num_Mobile; // Num_H+num_D+num_T+num_NegCharges - AT_RANK num_NegCharges; - // T_NUM_ISOTOPIC = 3 elements - AT_RANK num_T; // here the isotopic part (num+T_NUM_NO_ISOTOPIC) starts - AT_RANK num_D; - AT_RANK num_1H; - }; - AT_RANK num[T_NUM_NO_ISOTOPIC+T_NUM_ISOTOPIC]; // same size and meaning as num[] in T_ENDPOINT - }; - */ - AT_RANK num[T_NUM_NO_ISOTOPIC+T_NUM_ISOTOPIC]; /* same size and meaning as num[] in T_ENDPOINT */ - /* isotopic inv. order: num_T, num_D, num_1H */ - AT_RANK num_DA[TG_NUM_DA]; - T_GROUP_ISOWT iWeight; /* isotopic "weight" = T_GROUP_ISOWT_MULT*(T_GROUP_ISOWT_MULT*num_T + num_D)+num_1H; */ - AT_NUMB nGroupNumber; /* positive tautomer group ID = atom->endpoint */ - AT_NUMB nNumEndpoints; /* number of the atom numbers in T_GROUP_INFO::nEndpointAtomNumber[] */ - AT_NUMB nFirstEndpointAtNoPos; /* the first index of the atom number in T_GROUP_INFO::nEndpointAtomNumber[] */ -} T_GROUP; - -/* offsets/num_t_groups within T_GROUP_INFO::tGroupNumber */ -#define TGSO_CURR_ORDER 0 /* tGroupNumber: current sorting order */ -#define TGSO_SYMM_RANK 1 /* tSymmRank: symmetry ranks (no isotopes) = min. ordering number > 0. */ -#define TGSO_SYMM_IORDER 2 /* tiGroupNumber: isotopic symmetry rank sorting order */ -#define TGSO_SYMM_IRANK 3 /* tiSymmRank: isotopic symmetry ranks */ -#define TGSO_TOTAL_LEN 4 - -/***************************************************/ -/* flags for t_group_info->tni.bNormalizationFlags */ -/***************************************************/ -#define FLAG_PROTON_NPO_SIMPLE_REMOVED 0x0001 -#define FLAG_PROTON_NP_HARD_REMOVED 0x0002 -#define FLAG_PROTON_AC_SIMPLE_ADDED 0x0004 -#define FLAG_PROTON_AC_SIMPLE_REMOVED 0x0008 -#define FLAG_PROTON_AC_HARD_REMOVED 0x0010 -#define FLAG_PROTON_AC_HARD_ADDED 0x0020 -#define FLAG_PROTON_CHARGE_CANCEL 0x0040 -#define FLAG_PROTON_SINGLE_REMOVED 0x0080 - -/* signifies tautomeric structure even though no t-group discovered */ -#define FLAG_NORM_CONSIDER_TAUT ( FLAG_PROTON_NPO_SIMPLE_REMOVED | \ - FLAG_PROTON_NP_HARD_REMOVED | \ - FLAG_PROTON_AC_SIMPLE_ADDED | \ - FLAG_PROTON_AC_SIMPLE_REMOVED | \ - FLAG_PROTON_AC_HARD_REMOVED | \ - FLAG_PROTON_AC_HARD_ADDED | \ - FLAG_PROTON_SINGLE_REMOVED | \ - FLAG_PROTON_CHARGE_CANCEL ) - -#if ( FIX_N_MINUS_NORN_BUG == 1 ) -#define FLAG_FORCE_SALT_TAUT ( FLAG_PROTON_NP_HARD_REMOVED | \ - FLAG_PROTON_AC_HARD_REMOVED | \ - FLAG_PROTON_AC_HARD_ADDED | \ - FLAG_PROTON_CHARGE_CANCEL ) -#else -/* force salt tautomerism exploration */ -#define FLAG_FORCE_SALT_TAUT ( FLAG_PROTON_NP_HARD_REMOVED | \ - FLAG_PROTON_AC_HARD_REMOVED | \ - FLAG_PROTON_AC_HARD_ADDED ) -#endif - -typedef struct tagTautomerNormInfo { - NUM_H nNumRemovedExplicitH; /* keeps track of explicit H */ - NUM_H nNumRemovedProtons; - NUM_H nNumRemovedProtonsIsotopic[NUM_H_ISOTOPES]; - INCHI_MODE bNormalizationFlags; -} TNI; - -/***************************************************/ -/* t_group_info definition */ -/***************************************************/ -typedef struct tagTautomerGroupsInfo { - T_GROUP *t_group; /* max_num_t_groups elements */ - AT_NUMB *nEndpointAtomNumber; /* nNumEndpoints elements; also see comments to T_GROUP */ - AT_NUMB *tGroupNumber; - int nNumEndpoints; - int num_t_groups; - int max_num_t_groups; - int bIgnoreIsotopic; - - AT_NUMB *nIsotopicEndpointAtomNumber; /* [0]: number of the following atoms; [1...]: non-tautomeric atoms that may have isotopic H */ - int nNumIsotopicEndpoints; /* allocated length of nIsotopicEndpointAtomNumber */ - NUM_H num_iso_H[NUM_H_ISOTOPES]; /* isotopic H on tautomeric atoms and those in nIsotopicEndpointAtomNumber */ - - TNI tni; - - INCHI_MODE bTautFlags; - INCHI_MODE bTautFlagsDone; -} T_GROUP_INFO; - -#define CANON_FLAG_NO_H_RECANON 0x0001 /* iOther: second canonicalization of the no H structure */ -#define CANON_FLAG_NO_TAUT_H_DIFF 0x0002 /* iOther: NoTautH eq. partition differs from NoH */ -#define CANON_FLAG_ISO_ONLY_NON_TAUT_DIFF 0x0004 /* iOther: eq. partition in isotopic only non-taut differs from non-isotopic */ -#define CANON_FLAG_ISO_TAUT_DIFF 0x0008 /* iBase: isotopic eq. partition in isotopic taut differs from non-isotopic taut */ -#define CANON_FLAG_ISO_FIXED_H_DIFF 0x0010 /* iOther: isotopic eq. partition in fixed H non-taut differs from non-isotopic fixed H */ - -/* Note: rank of tautomer atom #i = Rank[nEndpointAtomNumber[i]] */ -/* for each tautomer atom group (t_group) t_group.nFirstEndpointAtNoPos */ -/* is the first index of the atom number in nEndpointAtomNumber[] */ - -typedef struct tagTautomerEndpoint { - /* - union { - struct { - AT_RANK num_Mobile; // Num_H+num_D+num_T+num_NegCharges - AT_RANK num_NegCharges; - AT_RANK num_T; - AT_RANK num_D; - }; - AT_RANK num[T_NUM_NO_ISOTOPIC+T_NUM_ISOTOPIC]; // same size and meaning as num[] in T_GROUP - }; - */ - AT_RANK num[T_NUM_NO_ISOTOPIC+T_NUM_ISOTOPIC]; /* same size and meaning as num[] in T_GROUP */ - AT_RANK num_DA[TG_NUM_DA]; - AT_NUMB nGroupNumber; - AT_NUMB nEquNumber; /* same for endpoints connected by alt paths */ - AT_NUMB nAtomNumber; - /*AT_NUMB neighbor_index; */ -} T_ENDPOINT; - -typedef struct tagTautomerBondLocation { - AT_NUMB nAtomNumber; - AT_NUMB neighbor_index; -} T_BONDPOS; - -typedef struct tagEndpointInfo { - S_CHAR cMoveableCharge; - S_CHAR cNeutralBondsValence; - S_CHAR cMobile; - S_CHAR cDonor; - S_CHAR cAcceptor; - S_CHAR cKetoEnolCode; /* 1 => carbon, 2 => oxygen */ /* post v.1 feature */ -} ENDPOINT_INFO; - - -/* positive charge group (extended onium) */ - -#define CHARGED_CPOINT(X,i) ((X)[i].charge==1) - -typedef struct tagChargeCandidate { - AT_NUMB atnumber; - S_CHAR type; - S_CHAR subtype; -} C_CANDIDATE; - -typedef struct tagChargeGroup { - AT_RANK num[2]; /* [0]: number of (+), [1]: number atoms that have H, including H accessible through tautomerism */ - AT_RANK num_CPoints; - AT_NUMB nGroupNumber; - U_CHAR cGroupType; -} C_GROUP; - -typedef struct tagChargeGroupsInfo { - C_GROUP *c_group; - int num_c_groups; - int max_num_c_groups; - - C_CANDIDATE *c_candidate; - int max_num_candidates; - int num_candidates; /* 0=>unimitialized, -1=>no candidates found */ -} C_GROUP_INFO; - -/* salts */ -typedef struct tagSaltChargeCandidate { - AT_NUMB atnumber; - S_CHAR type; - S_CHAR subtype; - AT_NUMB endpoint; /* MAX_ATOMS+1 => found alt path to the candidate */ -} S_CANDIDATE; - -typedef struct tagSaltGroupInfo { - S_CANDIDATE *s_candidate; - int max_num_candidates; - int num_candidates; /* 0=>unimitialized, -1=>no candidates found */ - int num_other_candidates; /* num. non-"acidic O" candidates */ - int num_p_only_candidates; /* num. non-tautomeric p-donor/acceptor candidates like -CH2-SH */ -} S_GROUP_INFO; - -/********************* ATOM_SIZES *******************************/ -/* sizes of a component */ -typedef struct tagAtomSizes { - /* for tautomeric and non-tautomeric structures */ - int nMaxNumStereoAtoms; /* max. number of stereo atoms in isotopic case */ - int nMaxNumStereoBonds; /* max. number of stereo bonds in isotopic case */ - int num_isotopic_atoms; /* includes atoms that have isotopic tautomeric H */ - int nLenCT; - int nLenBonds; - int nLenIsotopic; - int nLenCTAtOnly; - int nLenLinearCTStereoDble; /* max. number of stereo bonds in non-isotopic case */ - int nLenLinearCTStereoCarb; /* max. number of stereo atoms in non-isotopic case */ - /* int bHasIsotopicAtoms; */ - int bMayHaveStereo; - - int bIgnoreIsotopic; - - /* tautomeric structure only; zeroes in non-tautomeric */ - int nLenLinearCTTautomer; - int nLenLinearCTIsotopicTautomer; - int bHasIsotopicTautGroups; - int nLenIsotopicEndpoints; - -} ATOM_SIZES; - - -typedef struct tagDfsPath { - AT_RANK at_no; - /*AT_RANK nDfsLevel;*/ - U_CHAR bond_type; - S_CHAR bond_pos; -} DFS_PATH; - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -int is_centerpoint_elem( U_CHAR el_number ); -int is_centerpoint_elem_strict( U_CHAR el_number ); -#if ( KETO_ENOL_TAUT == 1 ) -int is_centerpoint_elem_KET( U_CHAR el_number ); -#endif -int bIsCenterPointStrict( inp_ATOM *atom, int iat ); - -int nGetEndpointInfo( inp_ATOM *atom, int iat, ENDPOINT_INFO *eif ); -#if ( KETO_ENOL_TAUT == 1 ) -int nGetEndpointInfo_KET( inp_ATOM *atom, int iat, ENDPOINT_INFO *eif ); -#endif -void AddAtom2DA( AT_RANK num_DA[], inp_ATOM *atom, int at_no, int bSubtract ); -int AddAtom2num( AT_RANK num[], inp_ATOM *atom, int at_no, int bSubtract ); -int AddEndPoint( T_ENDPOINT *pEndPoint, inp_ATOM *at, int iat ); -int bHasAcidicHydrogen( inp_ATOM *at, int i ); -int bHasOtherExchangableH ( inp_ATOM *at, int i ); -int bHasAcidicMinus( inp_ATOM *at, int i ); - - -int nGet15TautIn6MembAltRing( inp_ATOM *atom, int nStartAtom, AT_RANK *nBfsTreePos, - DFS_PATH *DfsPath, int nMaxLenBfsTree, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ); - -int nGet12TautIn5MembAltRing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, - AT_RANK *nBfsTreePos, DFS_PATH *DfsPath, int nMaxLenBfsTree, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ); -int nGet14TautIn7MembAltRing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, - int nStartAtomNeighborEndpoint, int nStartAtomNeighborNeighborEndpoint, - AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, int nMaxLenDfsPath, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ); -int nGet14TautIn5MembAltRing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, - int nStartAtomNeighborEndpoint, int nStartAtomNeighborNeighborEndpoint, - AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, int nMaxLenDfsPath, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ); - -int nGet15TautInAltPath( inp_ATOM *atom, int nStartAtom, AT_RANK *nDfsPathPos, - DFS_PATH *DfsPath, int nMaxLenDfsPath, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ); - - -#if ( RING2CHAIN == 1 ) -int Ring2Chain( ORIG_ATOM_DATA *orig_inp_data ); -#endif - -#if ( UNDERIVATIZE == 1 ) -int underivatize( ORIG_ATOM_DATA *orig_inp_data ); -#endif - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -#endif /* __INCHITAUT_H__ */ - diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichitime.h b/INCHI-1-SRC/INCHI_API/inchi_dll/ichitime.h deleted file mode 100644 index 92bad0f..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichitime.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __ICHITIME_H__ -#define __ICHITIME_H__ - - -#ifdef COMPILE_ANSI_ONLY - -#ifdef __FreeBSD__ -#include -#endif - -/* get times() */ -#ifdef INCHI_USETIMES -#include -#endif - -/*#include */ - -#include - -typedef struct tagInchiTime { - clock_t clockTime; -} inchiTime; - -#else - -/* Win32 _ftime(): */ -#include - -typedef struct tagInchiTime { - unsigned long clockTime; /* Time in seconds since midnight (00:00:00), January 1, 1970; - signed long overflow expected in 2038 */ - long millitime; /* milliseconds */ - -} inchiTime; - -#endif - - -#ifdef TARGET_EXE_USING_API - -#define InchiTimeGet e_InchiTimeGet -#define InchiTimeMsecDiff e_InchiTimeMsecDiff -#define InchiTimeAddMsec e_InchiTimeAddMsec -#define bInchiTimeIsOver e_bInchiTimeIsOver -#define InchiTimeElapsed e_InchiTimeElapsed - -#define FullMaxClock e_FullMaxClock -#define HalfMaxClock e_HalfMaxClock -#define MaxPositiveClock e_MaxPositiveClock -#define MinNegativeClock e_MinNegativeClock -#define HalfMaxPositiveClock e_HalfMaxPositiveClock -#define HalfMinNegativeClock e_HalfMinNegativeClock - - - -#endif - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - -void InchiTimeGet( inchiTime *TickEnd ); -long InchiTimeMsecDiff( inchiTime *TickEnd, inchiTime *TickStart ); -void InchiTimeAddMsec( inchiTime *TickEnd, unsigned long nNumMsec ); -int bInchiTimeIsOver( inchiTime *TickEnd ); -long InchiTimeElapsed( inchiTime *TickStart ); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /* __ICHITIME_H__ */ - diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ikey_base26.h b/INCHI-1-SRC/INCHI_API/inchi_dll/ikey_base26.h deleted file mode 100644 index 2b6b1eb..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ikey_base26.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __IKEY_BASE26_H__ -#define __IKEY_BASE26_H__ - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Base-26 encoding procedures. - - 'Base26' characters here are considered to be uppercase English - letters 'A..Z' - - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - -/* Uncomment the next line to fix base-26 encoding bug */ -/*#define FIX_BASE26_ENC_BUG 1*/ - - -typedef unsigned int UINT32; -typedef unsigned short int UINT16; - - - -#ifdef __cplusplus -extern "C" { -#endif - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get a character representing 1st 14-bit triplet (bits 0..13 of contiguous array of octets) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -const char* base26_triplet_1(const unsigned char *a); - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get a character representing 2nd 14-bit triplet (bits 14..27) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -const char* base26_triplet_2(const unsigned char *a); - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get a character representing 3rd 14-bit triplet (bits 28..41) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -const char* base26_triplet_3(const unsigned char *a); - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get a character representing 4th 14-bit triplet (bits 42..55) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -const char* base26_triplet_4(const unsigned char *a); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Tail dublets -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get dublet (bits 28..36) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -const char* base26_dublet_for_bits_28_to_36(unsigned char *a); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get dublet (bits 56..64) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -const char* base26_dublet_for_bits_56_to_64(unsigned char *a); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Calculate check character for the string. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -char base26_checksum(const char *str); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get hash extension in hexadecimal representation for the major block. -Len(extension) = 256 - 65 = 191 bit. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void get_xtra_hash_major_hex(const unsigned char *a, char* szXtra); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get hash extension in hexadecimal representation for the minor block. -Len(extension) = 256 - 37 = 219 bit. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void get_xtra_hash_minor_hex(const unsigned char *a, char* szXtra); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Used instead of isupper() to avoid locale interference. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -#define isbase26(_c) ( ((unsigned)(_c) >= 'A') && ((unsigned)(_c) <= 'Z') ) - - -#ifdef __cplusplus -} -#endif - - -#endif /*^^^ __IKEY_BASE26_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/inchi_api.h b/INCHI-1-SRC/INCHI_API/inchi_dll/inchi_api.h deleted file mode 100644 index e10a1e2..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/inchi_api.h +++ /dev/null @@ -1,1349 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INHCH_API_H__ -#define __INHCH_API_H__ - - - - -/*^^^ Post-1.02b fix - thanks to David Foss */ -#ifndef FIND_RING_SYSTEMS -#define FIND_RING_SYSTEMS 1 -#endif -#ifndef FIND_RINS_SYSTEMS_DISTANCES -#define FIND_RINS_SYSTEMS_DISTANCES 0 -#endif - - -/* radical definitions */ -typedef enum tagINCHIRadical { - INCHI_RADICAL_NONE = 0, - INCHI_RADICAL_SINGLET = 1, - INCHI_RADICAL_DOUBLET = 2, - INCHI_RADICAL_TRIPLET = 3 -} inchi_Radical; - -/* bond type definitions */ -typedef enum tagINCHIBondType { - INCHI_BOND_TYPE_NONE = 0, - INCHI_BOND_TYPE_SINGLE = 1, - INCHI_BOND_TYPE_DOUBLE = 2, - INCHI_BOND_TYPE_TRIPLE = 3, - INCHI_BOND_TYPE_ALTERN = 4 /* avoid by all means */ -} inchi_BondType; -/* 2D stereo definitions */ -typedef enum tagINCHIBondStereo2D { - /* stereocenter-related; positive: the sharp end points to this atom */ - INCHI_BOND_STEREO_NONE = 0, - INCHI_BOND_STEREO_SINGLE_1UP = 1, - INCHI_BOND_STEREO_SINGLE_1EITHER = 4, - INCHI_BOND_STEREO_SINGLE_1DOWN = 6, - /* stereocenter-related; negative: the sharp end points to the opposite atom */ - INCHI_BOND_STEREO_SINGLE_2UP = -1, - INCHI_BOND_STEREO_SINGLE_2EITHER = -4, - INCHI_BOND_STEREO_SINGLE_2DOWN = -6, - /* stereobond-related */ - INCHI_BOND_STEREO_DOUBLE_EITHER = 3 /* unknown stereobond geometry */ -} inchi_BondStereo2D; - -/************************************************************************* - * Notes on using INCHI_BOND_STEREO_SINGLE_* from inchi_BondStereo2D * - * * - * These stereo markings are used by InChI to characterize a stereogenic * - * atom if and only if all neighbors of this atom have same z-coordinate * - * as this atom (that is, in case of 2D fragment). * - * The only exception is INCHI_BOND_STEREO_SINGLE_?EITHER marking which * - * always assigns to the atom an "unknown" parity (u). * - * * - * Note the behavior which is default for InChI software v.1.04/03/02std * - * (at -NEWPSOFF option is not supplied) 2D stereo interpretation: * - * only bonds that have sharp end pointing to the stereogenic atom are * - * considered as being out of plane and only sharp ends of * - * INCHI_BOND_STEREO_SINGLE_?EITHER bonds are considered to determine * - * whether the stereochemistry is unknown. * - *************************************************************************/ - -/* sizes definitions */ -#define MAXVAL 20 /* max number of bonds per atom */ -#define ATOM_EL_LEN 6 /* length of ASCIIZ element symbol field */ -#define NUM_H_ISOTOPES 3 /* number of hydrogen isotopes: protium, D, T */ -#define ISOTOPIC_SHIFT_FLAG 10000 /* add to isotopic mass if isotopic_mass = */ - /* (isotopic mass - average atomic mass) */ -#define ISOTOPIC_SHIFT_MAX 100 /* max abs(isotopic mass - average atomic mass) */ - -#ifndef INCHI_US_CHAR_DEF -typedef signed char S_CHAR; -typedef unsigned char U_CHAR; -#define INCHI_US_CHAR_DEF -#endif - -#ifndef INCHI_US_SHORT_DEF -typedef signed short S_SHORT; -typedef unsigned short U_SHORT; -#define INCHI_US_SHORT_DEF -#endif - -typedef S_SHORT AT_NUM; /* atom number; starts from 0 */ - -/************************************************* - * - * - * A T O M S a n d C O N N E C T I V I T Y - * - * - *************************************************/ - -typedef struct tagInchiAtom { - /* atom coordinates */ - double x; - double y; - double z; - /* connectivity */ - AT_NUM neighbor[MAXVAL]; /* adjacency list: ordering numbers of */ - /* the adjacent atoms, >= 0 */ - S_CHAR bond_type[MAXVAL]; /* inchi_BondType */ - /* 2D stereo */ - S_CHAR bond_stereo[MAXVAL]; /* inchi_BondStereo2D; negative if the */ - /* sharp end points to opposite atom */ - /* other atom properties */ - char elname[ATOM_EL_LEN]; /* zero-terminated chemical element name:*/ - /* "H", "Si", etc. */ - AT_NUM num_bonds; /* number of neighbors, bond types and bond*/ - /* stereo in the adjacency list */ - S_CHAR num_iso_H[NUM_H_ISOTOPES+1]; /* implicit hydrogen atoms */ - /* [0]: number of implicit non-isotopic H - (exception: num_iso_H[0]=-1 means INCHI - adds implicit H automatically), - [1]: number of implicit isotopic 1H (protium), - [2]: number of implicit 2H (deuterium), - [3]: number of implicit 3H (tritium) */ - AT_NUM isotopic_mass; /* 0 => non-isotopic; isotopic mass or */ - /* ISOTOPIC_SHIFT_FLAG + mass - (average atomic mass) */ - S_CHAR radical; /* inchi_Radical */ - S_CHAR charge; /* positive or negative; 0 => no charge */ -}inchi_Atom; - -/******************************************************************* - * Notes: 1. Atom ordering numbers (i, k, and atom[i].neighbor[j] below) - * start from zero; max. ordering number is (num_atoms-1). - * 2. inchi_Atom atom[i] is connected to the atom[atom[i].neighbor[j]] - * by a bond that has type atom[i].bond_type[j] and 2D stereo type - * atom[i].bond_stereo[j] (in case of no stereo - * atom[i].bond_stereo[j] = INCHI_BOND_STEREO_NONE) - * Index j is in the range 0 <= j <= (atom[i].num_bonds-1) - * 3. Any connection (represented by atom[i].neighbor[j], - * atom[i].bond_type[j], and atom[i].bond_stereo[j]) - * should be present in one or both adjacency list: - * if k = atom[i].neighbor[j] then i may or may not be present in - * atom[k].neighbor[] list. For example, the adjacency lists may be - * populated with only such neighbors that atom[i].neighbor[j] < i - * All elements of an adjacency list must be different, that is, - * a bond must be specified in an adjacency list only once. - * 4. in Molfiles usually - * (number of implicit H) = Valence - SUM(bond_type[]) - * 5. Seemingly illogical order of the inchi_Atom members was - * chosen in an attempt to avoid alignment problems when - * accessing inchi_Atom from unrelated to C programming - * languages such as Visual Basic. - *******************************************************************/ - -/******************************************************************* - 0D Stereo Parity and Type definitions - ******************************************************************* - Note: - ===== - o Below #A is the ordering number of atom A, starting from 0 - o See parity values corresponding to 'o', 'e', and 'u' in - inchi_StereoParity0D definition below) - - ============================================= - stereogenic bond >A=B< or cumulene >A=C=C=B< - ============================================= - - neighbor[4] : {#X,#A,#B,#Y} in this order - X central_atom : NO_ATOM - \ X Y type : INCHI_StereoType_DoubleBond - A==B \ / - \ A==B - Y - - parity= 'e' parity= 'o' unknown parity = 'u' - - Limitations: - ============ - o Atoms A and B in cumulenes MUST be connected by a chain of double bonds; - atoms A and B in a stereogenic 'double bond' may be connected by a double, - single, or alternating bond. - o One atom may belong to up to 3 stereogenic bonds (i.g. in a fused - aromatic structure). - o Multiple stereogenic bonds incident to any given atom should - either all except possibly one have (possibly different) defined - parities ('o' or 'e') or should all have an unknown parity 'u'. - - Note on parities of alternating stereobonds - =========================================== - D--E - In large rings (see Fig. 1, all // \\ - atoms are C) all alternating bonds B--C F--G - are treated as stereogenic. // \\ - To avoid "undefined" bond parities A H - for bonds BC, DE, FG, HI, JK, LM, AN \ / - it is recommended to mark them with N==M J==I - parities. \ / - L==K Fig. 1 - Such a marking will make - the stereochemical layer unambiguous - and it will be different from the B--C F--G - stereochemical layer of the second // \\ // \\ - structure (Fig. 2). A D--E H - \ / - N==M J==I - By default, double and alternating \ / - bonds in 8-member and greater rings L==K Fig. 2 - are treated by InChI as stereogenic. - - - ============================================= - tetrahedral atom - ============================================= - - 4 neighbors - - X neighbor[4] : {#W, #X, #Y, #Z} - | central_atom: #A - W--A--Y type : INCHI_StereoType_Tetrahedral - | - Z - parity: if (X,Y,Z) are clockwize when seen from W then parity is 'e' otherwise 'o' - Example (see AXYZW above): if W is above the plane XYZ then parity = 'e' - - 3 neighbors - - Y Y neighbor[4] : {#A, #X, #Y, #Z} - / / central_atom: #A - X--A (e.g. O=S ) type : INCHI_StereoType_Tetrahedral - \ \ - Z Z - - parity: if (X,Y,Z) are clockwize when seen from A then parity is 'e', - otherwise 'o' - unknown parity = 'u' - Example (see AXYZ above): if A is above the plane XYZ then parity = 'e' - This approach may be used also in case of an implicit H attached to A. - - ============================================= - allene - ============================================= - - X Y neighbor[4] : {#X,#A,#B,#Y} - \ / central_atom : #C - A=C=B type : INCHI_StereoType_Allene - - Y X - | | - when seen from A along A=C=B: X-A Y-A - - parity: 'e' 'o' - - parity: if A, B, Y are clockwise when seen from X then parity is 'e', - otherwise 'o' - unknown parity = 'u' - Example (see XACBY above): if X on the diagram is above the plane ABY - then parity is 'o' - - Limitations - =========== - o Atoms A and B in allenes MUST be connected by a chain of double bonds; - - ============================================== - Note. Correspondence to CML 0D stereo parities - ============================================== - a list of 4 atoms corresponds to CML atomRefs4 - - tetrahedral atom - ================ - CML atomParity > 0 <=> INCHI_PARITY_EVEN - CML atomParity < 0 <=> INCHI_PARITY_ODD - - | 1 1 1 1 | where xW is x-coordinate of - | xW xX xY xZ | atom W, etc. (xyz is a - CML atomParity = determinant | yW yX yY yZ | 'right-handed' Cartesian - | zW zX xY zZ | coordinate system) - - allene (not yet defined in CML) - =============================== - the parity corresponds to the sign of the following determinant - in exactly same way as for tetrahedral atoms: - - | 1 1 1 1 | where bonds and neighbor[4] array are - | xX xA xB xY | same as defined above for allenes - | yX yA yB yY | Obviously, the parity is same for - | zX zA xB zY | {#X,#A,#B,#Y} and {#Y,#B,#A,#X} - because of the even number of column permutations. - - stereogenic double bond and (not yet defined in CML) cumulenes - ============================================================== - CML 'C' (cis) <=> INCHI_PARITY_ODD - CML 'T' (trans) <=> INCHI_PARITY_EVEN - - - How InChI uses 0D parities - ========================== - - 1. 0D parities are used if all atom coordinates are zeroes. - - In addition to that: - - 2. 0D parities are used for Stereobonds, Allenes, or Cumulenes if: - - 2a. A bond to the end-atom is shorter than MIN_BOND_LEN=0.000001 - 2b. A ratio of two bond lengths to the end-atom is smaller than MIN_SINE=0.03 - 2c. In case of a linear fragment X-A=B end-atom A is treated as satisfying 2a-b - - 0D parities are used if 2a or 2b or 2c applies to one or both end-atoms. - - 3. 0D parities are used for Tetrahedral Atoms if at least one of 3a-c is true: - - 3a. One of bonds to the central atom is shorter than MIN_BOND_LEN=0.000001 - 3b. A ratio of two bond lengths to the central atom is smaller than MIN_SINE=0.03 - 3c. The four neighbors are almost in one plane or the central atom and - its only 3 explicit neighbors are almost in one plane - - Notes on 0D parities and 'undefined' stereogenic elements - ========================================================= - - If 0D parity is to be used according to 1-3 but CH3 CH3 - has not been provided then the corresponding \ / - stereogenic element is considered 'undefined'. C=CH - / - For example, if in the structure (Fig. 3) H - the explicit H has been moved so that it Fig. 3 - has same coordinates as atom >C= (that is, - the length of the bond H-C became zero) - then the double bond is assigned 'undefined' CH3 CH3 - parity which by default is omitted from the \ / - Identifier. CH=CH - - However, the structure on Fig. 4 will have double Fig. 4 - bond parity 'o' and its parity in the Identifier is (-). - - Notes on 0D parities in structures containing metals - ==================================================== - Since InChI disconnects bonds to metals the 0D parities upon the - disconnection may change in several different ways: - - 1) previously non-stereogenic bond may become stereogenic: - - \ / \ / - CH==CH disconnection CH==CH - \ / ======> - M M - - before the disconnection: after the disconnection: - atoms C have valence=5 and the double bond may become - the double bond is not stereogenic - recognized as stereogenic - - 2) previously stereogenic bond may become non-stereogenic: - - M M(+) - \ / / - N==C disconnection (-)N==C - \ ======> \ - - 3) Oddball structures, usually resulting from projecting 3D - structures on the plane, may contain fragment like that - depicted on Fig. 5: - - M A M A - |\ / B / B - | X / disconnection / / - |/ \ / ======> / / - C===C C===C - Fig. 5 - (X stands for bond intersection) - - A-C=C-B parity is A-C=C-B parity is - trans (e) cis (o) or undefined - because the bond because C valence = 3, - orientation is same not 4. - as on Fig, 6 below: - - A M - \ / Removal of M from the structure - C===C on Fig. 5 changes the geometry from trans - / \ to cis. - M' B Removal of M and M' from the structure - Fig. 6 on Fig. 6 does not change the A-C=C-B - geometry: it is trans. - - To resolve the problem InChI API accepts the second parity - corresponding to the metal-disconnected structure. - To store both bond parities use left shift by 3 bits: - - inchi_Stereo0D::parity = ParityOfConnected | (ParityOfDisconnected<<3) - - In case when only disconnected structure parity exists set - ParityOfConnected = INCHI_PARITY_UNDEFINED. - This is the only case when INCHI_PARITY_UNDEFINED parity - may be fed to the InChI. - - In cases when the bond parity in a disconnected structure exists and - differs from the parity in the connected structure the atoms A and B - should be non-metals. - -****************************************************************************/ - -#define NO_ATOM (-1) /* non-existent (central) atom */ - -/* 0D parity types */ -typedef enum tagINCHIStereoType0D { - INCHI_StereoType_None = 0, - INCHI_StereoType_DoubleBond = 1, - INCHI_StereoType_Tetrahedral = 2, - INCHI_StereoType_Allene = 3 -} inchi_StereoType0D; - -/* 0D parities */ -typedef enum tagINCHIStereoParity0D { - INCHI_PARITY_NONE = 0, - INCHI_PARITY_ODD = 1, /* 'o' */ - INCHI_PARITY_EVEN = 2, /* 'e' */ - INCHI_PARITY_UNKNOWN = 3, /* 'u' */ /* (see also readinch.c) - used in: Extract0DParities, INChITo_Atom */ - INCHI_PARITY_UNDEFINED = 4 /* '?' -- should not be used; however, see Note above */ -} inchi_StereoParity0D; - - -/************************************************* - * - * - * 0D - S T E R E O (if no coordinates given) - * - * - *************************************************/ - - -typedef struct tagINCHIStereo0D { - AT_NUM neighbor[4]; /* 4 atoms always */ - AT_NUM central_atom; /* central tetrahedral atom or a central */ - /* atom of allene; otherwise NO_ATOM */ - S_CHAR type; /* inchi_StereoType0D */ - S_CHAR parity; /* inchi_StereoParity0D: may be a combination of two parities: */ - /* ParityOfConnected | (ParityOfDisconnected << 3), see Note above */ -}inchi_Stereo0D; - - - - -/************************************************* - * - * - * I N C h I D L L I n p u t - * - * - *************************************************/ - - -/* Structure -> InChI, GetINCHI() / GetStdINCHI() */ -typedef struct tagINCHI_Input { - /* the caller is responsible for the data allocation and deallocation */ - inchi_Atom *atom; /* array of num_atoms elements */ - inchi_Stereo0D *stereo0D; /* array of num_stereo0D 0D stereo elements or NULL */ - char *szOptions; /* InChI options: space-delimited; each is preceded by */ - /* '/' or '-' depending on OS and compiler */ - AT_NUM num_atoms; /* number of atoms in the structure < 1024 */ - AT_NUM num_stereo0D; /* number of 0D stereo elements */ -}inchi_Input; - -/* InChI -> Structure, GetStructFromINCHI()/GetStructFromStdINCHI() */ -typedef struct tagINCHI_InputINCHI { - /* the caller is responsible for the data allocation and deallocation */ - char *szInChI; /* InChI ASCIIZ string to be converted to a strucure */ - char *szOptions; /* InChI options: space-delimited; each is preceded by */ - /* '/' or '-' depending on OS and compiler */ -} inchi_InputINCHI; - - -/************************************************* - * - * - * I N C h I D L L O u t p u t - * - * - *************************************************/ - -/* Structure -> InChI */ -typedef struct tagINCHI_Output { - /* zero-terminated C-strings allocated by GetStdINCHI() */ - /* to deallocate all of them call FreeStdINCHI() (see below) */ - char *szInChI; /* InChI ASCIIZ string */ - char *szAuxInfo; /* Aux info ASCIIZ string */ - char *szMessage; /* Error/warning ASCIIZ message */ - char *szLog; /* log-file ASCIIZ string, contains a human-readable list */ - /* of recognized options and possibly an Error/warning message */ -} inchi_Output; - -/* InChI -> Structure */ -typedef struct tagINCHI_OutputStruct { - /* 4 pointers are allocated by GetStructFromINCHI()/GetStructFromStdINCHI() */ - /* to deallocate all of them call FreeStructFromStdINCHI()/FreeStructFromStdINCHI() */ - inchi_Atom *atom; /* array of num_atoms elements */ - inchi_Stereo0D *stereo0D; /* array of num_stereo0D 0D stereo elements or NULL */ - AT_NUM num_atoms; /* number of atoms in the structure < 1024 */ - AT_NUM num_stereo0D; /* number of 0D stereo elements */ - char *szMessage; /* Error/warning ASCIIZ message */ - char *szLog; /* log-file ASCIIZ string, contains a human-readable list */ - /* of recognized options and possibly an Error/warning message */ - unsigned long WarningFlags[2][2]; /* warnings, see INCHIDIFF in inchicmp.h */ - /* [x][y]: x=0 => Reconnected if present in InChI otherwise Disconnected/Normal - x=1 => Disconnected layer if Reconnected layer is present - y=1 => Main layer or Mobile-H - y=0 => Fixed-H layer - */ -}inchi_OutputStruct; - - - - -/************************************************* - * - * - * I N C h I D L L I n t e r f a c e - * - * - *************************************************/ - -#if (defined( _WIN32 ) && defined( _MSC_VER ) && defined(BUILD_LINK_AS_DLL) ) - /* Win32 & MS VC ++, compile and link as a DLL */ - #ifdef _USRDLL - /* InChI library dll */ - #define INCHI_API __declspec(dllexport) - #define EXPIMP_TEMPLATE - #define INCHI_DECL __stdcall - #else - /* calling the InChI dll program */ - #define INCHI_API __declspec(dllimport) - #define EXPIMP_TEMPLATE extern - #define INCHI_DECL __stdcall - #endif -#else - /* create a statically linked InChI library or link to an executable */ - #define INCHI_API - #define EXPIMP_TEMPLATE - #define INCHI_DECL -#endif - -/*^^^ Return codes for - GetINCHI - GetStdINCHI - Get_inchi_Input_FromAuxInfo - Get_std_inchi_Input_FromAuxInfo - GetStructFromINCHI - GetStructFromStdINCHI -*/ -typedef enum tagRetValGetINCHI { - inchi_Ret_SKIP = -2, /* not used in InChI library */ - inchi_Ret_EOF = -1, /* no structural data has been provided */ - inchi_Ret_OKAY = 0, /* Success; no errors or warnings */ - inchi_Ret_WARNING = 1, /* Success; warning(s) issued */ - inchi_Ret_ERROR = 2, /* Error: no InChI has been created */ - inchi_Ret_FATAL = 3, /* Severe error: no InChI has been created (typically, memory allocation failure) */ - inchi_Ret_UNKNOWN = 4, /* Unknown program error */ - inchi_Ret_BUSY = 5 /* Previuos call to InChI has not returned yet */ - -} RetValGetINCHI; - -/*^^^ Return codes for CheckINCHI */ -typedef enum tagRetValCheckINCHI -{ - INCHI_VALID_STANDARD = 0, - INCHI_VALID_NON_STANDARD = -1, - INCHI_INVALID_PREFIX = 1, - INCHI_INVALID_VERSION = 2, - INCHI_INVALID_LAYOUT = 3, - INCHI_FAIL_I2I = 4 - -} RetValCheckINCHI; - - - -/* to compile all InChI code as a C++ code #define COMPILE_ALL_CPP */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - -/*^^^ InChI PREFIX */ -#define INCHI_STRING_PREFIX "InChI=" -#define LEN_INCHI_STRING_PREFIX 6 - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Format: - - Standard InChI starts with: InChI=1S/ - Non-standard one with: InChI=1/ - Empty std InChI: InChI=1S// - Empty InChI: InChI=1// - AuxInfo=1// - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - - - -/* EXPORTED FUNCTIONS */ - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -GetINCHI / GetStdINCHI - - - inchi_Input is created by the user; strings in inchi_Output are allocated and deallocated by InChI - inchi_Output does not need to be initilized out to zeroes; see FreeNCHI()/FreeSTDINCHI() on how to deallocate it - - - Valid options for GetINCHI: - (use - instead of / for O.S. other than MS Windows) - - Structure perception (compatible with stdInChI) - /NEWPSOFF /DoNotAddH /SNon - Stereo interpretation (lead to generation of non-standard InChI) - /SRel /SRac /SUCF /ChiralFlagON /ChiralFlagOFF - InChI creation options (lead to generation of non-standard InChI) - /SUU /SLUUD /FixedH /RecMet /KET /15T - - - GetINCHI produces standard InChI if no InChI creation/stereo modification options - are specified. Inveresely, if any of SUU/SLUUD/RecMet/FixedH/Ket/15T/SRel/SRac/SUCF - options are specified, generated InChI will be non-standard one. - - - GetStdINCHI produces standard InChI only. - The valid structure perception options are: - /NEWPSOFF /DoNotAddH /SNon - - - Other options are: - /AuxNone Omit auxiliary information (default: Include) - /Wnumber Set time-out per structure in seconds; W0 means unlimited - In InChI library the default value is unlimited - /OutputSDF Output SDfile instead of InChI - /WarnOnEmptyStructure - Warn and produce empty InChI for empty structure - /SaveOpt Save custom InChI creation options (non-standard InChI) - - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetINCHI( inchi_Input *inp, inchi_Output *out ); -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStdINCHI( inchi_Input *inp, inchi_Output *out ); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -FreeINCHI / FreeStdINCHI - - should be called to deallocate char* pointers - obtained from each GetINCHI /GetStdINCHI call - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeINCHI ( inchi_Output *out ); -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeStdINCHI ( inchi_Output *out ); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -GetStringLength - - helper: get string length - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStringLength( char *p ); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -GetStructFromINCHI / GetStructFromStdINCHI - - inchi_Inputinchi_InputINCHI is created by the user; pointers in inchi_OutputStruct are allocated and deallocated by InChI - inchi_OutputStruct does not need to be initilized out to zeroes; see FreeStructFromStdINCHI() on how to deallocate it - Option /Inchi2Struct is not needed for GetStructFromINCHI()/GetStructFromStdINCHI() - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStructFromINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ); -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStructFromStdINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -FreeStructFromINCHI / FreeStructFromStdINCHI - - should be called to deallocate pointers obtained from each - GetStructFromStdINCHI / GetStructFromINCHI - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeStructFromINCHI( inchi_OutputStruct *out ); -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeStructFromStdINCHI( inchi_OutputStruct *out ); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -GetINCHIfromINCHI - - GetINCHIfromINCHI does same as -InChI2InChI option: converts InChI into InChI for validation purposes - It may also be used to filter out specific layers. For instance, /Snon would remove stereochemical layer - Omitting /FixedH and/or /RecMet would remove Fixed-H or Reconnected layers - To keep all InChI layers use options string "/FixedH /RecMet"; option /InChI2InChI is not needed - inchi_InputINCHI is created by the user; strings in inchi_Output are allocated and deallocated by InChI - inchi_Output does not need to be initilized out to zeroes; see FreeINCHI() on how to deallocate it - - Note: there is no explicit tool to conversion from/to standard InChI - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetINCHIfromINCHI( inchi_InputINCHI *inpInChI, inchi_Output *out ); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -/***************************************************************** - * - * - * C o n v e r s i o n: InChI AuxInfo string => inchi_Input - * - * - *****************************************************************/ - -#ifndef STR_ERR_LEN -#define STR_ERR_LEN 256 -#endif - -typedef struct tagInchiInpData { - inchi_Input *pInp; /* a pointer to pInp that has all items 0 or NULL */ - int bChiral; /* 1 => the structure was marked as chiral, 2=> not chiral, 0=> not marked */ - char szErrMsg[STR_ERR_LEN]; -} InchiInpData; - -/* to compile all InChI code as a C++ code #define COMPILE_ALL_CPP */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get_inchi_Input_FromAuxInfo / Get_std_inchi_Input_FromAuxInfo - -Input: - szInchiAuxInfo: contains ASCIIZ string of InChI output for a single - structure or only the AuxInfo line - bDoNotAddH: if 0 then InChI will be allowed to add implicit H - bDiffUnkUndfStereo - if not 0, use different labels for unknown and undefined stereo - pInchiInp: should have a valid pointer pInchiInp->pInp to an empty - (all members = 0) inchi_Input structure - -Output: - pInchiInp: The following members of pInp may be filled during the call: - atom, num_atoms, stereo0D, num_stereo0D - Return value: see RetValGetINCHI - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL Get_inchi_Input_FromAuxInfo( - char *szInchiAuxInfo, - int bDoNotAddH, - int bDiffUnkUndfStereo, - InchiInpData *pInchiInp ); -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL Get_std_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, - int bDoNotAddH, - InchiInpData *pInchiInp ); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Free_inchi_Input / Free_std_inchi_Input - - To deallocate and write zeroes into the changed members of pInchiInp->pInp call - Free_std_inchi_Input( inchi_Input *pInp ) - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL Free_inchi_Input( inchi_Input *pInp ); -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL Free_std_inchi_Input( inchi_Input *pInp ); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -CheckINCHI - -Check if the string represents valid InChI/standard InChI. -Input: - szINCHI source InChI - strict if 0, just briefly check for proper layout (prefix, version, etc.) - The result may not be strict. - If not 0, try to perform InChI2InChI conversion and - returns success if a resulting InChI string exactly match source. - The result may be 'false alarm' due to imperfectness of conversion. -Returns: - success/errors codes - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL CheckINCHI(const char *szINCHI, const int strict); - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - InChIKey API - - - InChIKey description - - - - The InChIKey is a character signature based on a hash code of the InChI string. - Standard InChIKey is produced out of standard InChI. - Non-standard InChIKey is produced out of non-standard InChI. - - AAAAAAAAAAAAAA-BBBBBBBBCD-P - - - InChIKey layout is as follows: - - AAAAAAAAAAAAAA - First block (14 letters) - Encodes molecular skeleton (connectivity) - - BBBBBBBB - Second block (8 letters) - Encodes tautomers, stereochemistry, isotopomers, reconnected layer - C - 'S' for standard - 'N' for non-standard - D - InChI version ('A' for 1) - P - (de)protonation flag - Protonization encoding: - N 0 - O +1 P +2 Q +3 R +4 S +5 T +6 U +7 V +8 W +9 X +10 Y +11 Z +12 - M -1 L-2 K -3 J -4 I -5 H -6 G -7 F -8 E -9 D -10 C -11 B -12 - A < -12 or > +12 - - - All symbols except delimiter (dash, that is, minus) are uppercase English - letters representing a "base 26" encoding. - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - -/*^^^ Return codes for key generation procedure */ -#define INCHIKEY_OK 0 -#define INCHIKEY_UNKNOWN_ERROR 1 -#define INCHIKEY_EMPTY_INPUT 2 -#define INCHIKEY_INVALID_INCHI_PREFIX 3 -#define INCHIKEY_NOT_ENOUGH_MEMORY 4 -#define INCHIKEY_INVALID_INCHI 20 -#define INCHIKEY_INVALID_STD_INCHI 21 - - -/*^^^ Return codes for CheckINCHIKey */ -typedef enum tagRetValGetINCHIKey -{ - INCHIKEY_VALID_STANDARD = 0, - INCHIKEY_VALID_NON_STANDARD = -1, - INCHIKEY_INVALID_LENGTH = 1, - INCHIKEY_INVALID_LAYOUT = 2, - INCHIKEY_INVALID_VERSION = 3 -} RetValCheckINCHIKeyv; - - - -/* EXPORTED FUNCTIONS */ - - - -/* To compile all InChI code as a C++ code #define COMPILE_ALL_CPP */ - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -GetINCHIKeyFromINCHI - -Calculate InChIKey by InChI string. - -Input: - szINCHISource - source InChI string - xtra1 - =1 calculate hash extension (up to 256 bits; 1st block) - xtra2 - =1 calculate hash extension (up to 256 bits; 2nd block) - -Output: - szINCHIKey - InChIKey string - The user-supplied buffer szINCHIKey should be at least 28 bytes long. - szXtra1 - hash extension (up to 256 bits; 1st block) string - Caller should allocate space for 64 characters + trailing NULL - szXtra2 - hash extension (up to 256 bits; 2nd block) string - Caller should allocate space for 64 characters + trailing NULL - -Returns: - success/errors codes - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetINCHIKeyFromINCHI(const char* szINCHISource, - const int xtra1, - const int xtra2, - char* szINCHIKey, - char* szXtra1, - char* szXtra2); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -GetStdINCHIKeyFromStdINCHI - - "Standard" counterpart - - For compatibility with v. 1.02std, no extra hash calculation is allowed. - To calculate extra hash(es), use GetINCHIKeyFromINCHI with stdInChI as input. - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStdINCHIKeyFromStdINCHI(const char* szINCHISource, - char* szINCHIKey); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -CheckINCHIKey - -Check if the string represents valid InChIKey. -Input: - szINCHIKey - source InChIKey string -Returns: - success/errors codes - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL CheckINCHIKey(const char *szINCHIKey); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Modularized InChI generation API - - - - Note. Functions with STDINCHIGEN prefix are - retained for compatibility with v. 1.02std - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - - - -/*^^^ Data structures holding intermediate (normalization) results */ - -#ifndef MAX_NUM_STEREO_ATOM_NEIGH -#define MAX_NUM_STEREO_ATOM_NEIGH 4 -#endif -#ifndef MAX_NUM_STEREO_BONDS -#define MAX_NUM_STEREO_BONDS 3 -#endif -#ifndef INCHI_NUM -#define INCHI_NUM 2 /* = array size; member indexes: */ -#endif - - -typedef unsigned short AT_NUMBR; -typedef signed short NUM_HS; -typedef unsigned long INCHI_MODES; - -typedef struct tagNormAtom -{ - char elname[ATOM_EL_LEN]; /* chem. element name */ - U_CHAR el_number; /* number of the element in the Periodic Table */ - AT_NUMBR neighbor[MAXVAL]; /* positions (from 0) of the neighbors in the NORM_ATOM array */ - AT_NUMBR orig_at_number; /* original atom number, starts from 1 */ - AT_NUMBR orig_compt_at_numb; /* atom number within a component before terminal H removal */ - S_CHAR bond_stereo[MAXVAL]; /* 1=Up,4=Either,6=Down (this atom is at the pointing wedge) - negative => on the opposite side of the wedge; 3=Either double bond */ - U_CHAR bond_type[MAXVAL]; /* 1=single, 2=double, 3=triple, 4=1/2 (bond order is 1 or 2) */ - /* 5=1/2/3, 6=1/3, 7=2/3, 8=tautomeric, 9=1/2 non-stereogenic */ - - S_CHAR valence; /* number of bonds = number of neighbors not greater than MAXVAL */ - S_CHAR chem_bonds_valence; /* sum of bond types (1,2,3); type 4 needs special treatment */ - S_CHAR num_H; /* number of adjacent implicit hydrogen atoms including D and T */ - S_CHAR num_iso_H[NUM_H_ISOTOPES];/* number of adjacent implicit 1H(protium), 2H(D), 3H(T) < 16 */ - S_CHAR iso_atw_diff; /* =0 => natural isotopic abundances */ - /* >0 => (isotopic mass) - (rounded average atomic mass) + 1 */ - /* <0 => (isotopic mass) - (rounded average atomic mass) */ - S_CHAR charge; /* charge */ - S_CHAR radical; /* RADICAL_SINGLET, RADICAL_DOUBLET, or RADICAL_TRIPLET */ - S_CHAR bAmbiguousStereo; /* flag of detected stereo ambiguity */ - S_CHAR cFlags; /* AT_FLAG_ISO_H_POINT: atom may have exchangeable isotopic H */ - AT_NUMBR at_type; /* ATT_NONE, ATT_ACIDIC, etc. See InChI normalization code */ - AT_NUMBR component; /* number of the structure component > 0 */ - AT_NUMBR endpoint; /* id of a tautomeric group */ - AT_NUMBR c_point; /* id of a positive charge group */ - double x; /* x coordinate */ - double y; /* y coordinate */ - double z; /* x coordinate */ - /*--------- 0D parities ----------*/ - S_CHAR bUsed0DParity; /* bit=1 => stereobond; bit=2 => stereocenter */ - /*----- tetrahedral stereo parity */ - S_CHAR p_parity; /* tetrahedral (sp3) cml parity */ - AT_NUMBR p_orig_at_num[MAX_NUM_STEREO_ATOM_NEIGH]; /* orig_at_number of each neighbor > 0; 0=> no neighbor */ - /*----- stereo bond (SB) parities */ - S_CHAR sb_ord[MAX_NUM_STEREO_BONDS]; /* neighbor[] index of another end of this SB, starts from 0 */ - S_CHAR sn_ord[MAX_NUM_STEREO_BONDS]; /* neighbor[] index of a bond that is not this SB; starts from 0; - -1 means the neighbor is a removed explicit H */ - /* atoms on both ends of a stereobond have same parity => trans/T/E/2, diff. parities => cis/C/Z/1 */ - S_CHAR sb_parity[MAX_NUM_STEREO_BONDS]; /* parities of stereobonds (sp2) incident to this atom */ - AT_NUMBR sn_orig_at_num[MAX_NUM_STEREO_BONDS]; /* orig_at_number of sn_ord[] neighbor > 0 */ - -#if ( FIND_RING_SYSTEMS == 1 ) - S_CHAR bCutVertex; /* is the atom a cut-vertex or not */ - AT_NUMBR nRingSystem; /* starts from 1; number of a ring system */ - AT_NUMBR nNumAtInRingSystem; /* number of atoms in a ring system to which this at belongs */ - AT_NUMBR nBlockSystem; /* ambiguous if the atom is a cut-vertex: better apply this to bonds */ - -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) - AT_NUMBR nDistanceFromTerminal; /* not used */ -#endif - -#endif -} NORM_ATOM; - - - -typedef struct tagNormAtomData -{ - NORM_ATOM *at; /* atom list */ - NORM_ATOM *at_fixed_bonds; /* atom list with added or removed protons only */ - int num_at; /* number of atoms except removed terminal H */ - int num_removed_H; /* number of removed H; at[] has (num_at+num_removed_H) elements */ - int num_bonds; - int num_isotopic; /* number of isotopic atoms */ - int bExists; /* for internal use */ - int bDeleted; /* for internal use */ - int bHasIsotopicLayer; - int bTautomeric; - int bTautPreprocessed; /* for internal use */ - int nNumRemovedProtons; - NUM_HS nNumRemovedProtonsIsotopic[NUM_H_ISOTOPES]; - /* isotopic composition of removed protons, not included in num_iso_H[] */ - NUM_HS num_iso_H[NUM_H_ISOTOPES]; - /* isotopic H on tautomeric atoms and those - in nIsotopicEndpointAtomNumber */ - INCHI_MODES bTautFlags; /* for internal use */ - INCHI_MODES bTautFlagsDone; /* for internal use */ - INCHI_MODES bNormalizationFlags;/* for internal use */ -} NORM_ATOMS; - - -typedef struct tagINCHIGEN_DATA -{ - - char pStrErrStruct[STR_ERR_LEN]; /* intermediate log (warning/error report) */ - int num_components[INCHI_NUM]; /* number of allocated INChI, INChI_Aux data structures */ - /* index=0 => disconnected, 1 => reconnected structure */ - - /*^^^ The results of normalization stage */ - /*^^^ for each member of pair disconnected/reconnected structures: */ - NORM_ATOMS *NormAtomsNontaut[INCHI_NUM]; - NORM_ATOMS *NormAtomsTaut[INCHI_NUM]; - -} INCHIGEN_DATA; - - -/*^^^ InChI Generator Handle */ - -typedef void* INCHIGEN_HANDLE; - - - - -/* EXPORTED FUNCTIONS */ - - - -/* to compile all InChI code as a C++ code #define COMPILE_ALL_CPP */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -INCHIGEN_Create / STDINCHIGEN_Create - - InChI Generator: create generator - Returns handle of generator object or NULL on failure - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API -INCHIGEN_HANDLE INCHI_DECL INCHIGEN_Create(void); -EXPIMP_TEMPLATE INCHI_API -INCHIGEN_HANDLE INCHI_DECL STDINCHIGEN_Create(void); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -INCHIGEN_Setup / STDINCHIGEN_Setup - - InChI Generator: setup - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL INCHIGEN_Setup(INCHIGEN_HANDLE HGen, - INCHIGEN_DATA * pGenData, - inchi_Input * pInp); -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL STDINCHIGEN_Setup(INCHIGEN_HANDLE HGen, - INCHIGEN_DATA * pGenData, - inchi_Input * pInp); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -INCHIGEN_DoNormalization / STDINCHIGEN_DoNormalization - - InChI Generator: structure normalization stage - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL INCHIGEN_DoNormalization(INCHIGEN_HANDLE HGen, - INCHIGEN_DATA * pGenData); -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL STDINCHIGEN_DoNormalization(INCHIGEN_HANDLE HGen, - INCHIGEN_DATA * pGenData); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -INCHIGEN_DoCanonicalization / STDINCHIGEN_DoCanonicalization - - InChI Generator: structure canonicalization stage - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL INCHIGEN_DoCanonicalization - (INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData); -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL STDINCHIGEN_DoCanonicalization - (INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -INCHIGEN_DoSerialization / STDINCHIGEN_DoSerialization - - InChI Generator: InChI serialization stage - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL INCHIGEN_DoSerialization(INCHIGEN_HANDLE HGen, - INCHIGEN_DATA * pGenData, - inchi_Output * pResults); -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL STDINCHIGEN_DoSerialization(INCHIGEN_HANDLE HGen, - INCHIGEN_DATA * pGenData, - inchi_Output * pResults); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -INCHIGEN_DoSerialization / STDINCHIGEN_DoSerialization - - InChI Generator: reset stage (use before get next structure) - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API -void INCHI_DECL INCHIGEN_Reset(INCHIGEN_HANDLE HGen, - INCHIGEN_DATA * pGenData, - inchi_Output * pResults); -EXPIMP_TEMPLATE INCHI_API -void INCHI_DECL STDINCHIGEN_Reset(INCHIGEN_HANDLE HGen, - INCHIGEN_DATA * pGenData, - inchi_Output * pResults); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -INCHIGEN_DoSerialization / STDINCHIGEN_DoSerialization - - InChI Generator: destroy generator - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL INCHIGEN_Destroy(INCHIGEN_HANDLE HGen); -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL STDINCHIGEN_Destroy(INCHIGEN_HANDLE HGen); - - - - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Prototypes for C calling conventions: - - int GetINCHI( inchi_Input *inp, inchi_Output *out ); - int GetStdINCHI( inchi_Input *inp, inchi_Output *out ); - void FreeINCHI( inchi_Output *out ); - void FreeStdINCHI( inchi_Output *out ); - int GetStringLength( char *p ); - int Get_inchi_Input_FromAuxInfo - ( char *szInchiAuxInfo, int bDoNotAddH, int bDiffUnkUndfStereo, InchiInpData *pInchiInp ); - int Get_std_inchi_Input_FromAuxInfo - ( char *szInchiAuxInfo, int bDoNotAddH, int bDiffUnkUndfStereo,InchiInpData *pInchiInp ); - void Free_inchi_Input( inchi_Input *pInp ); - void Free_std_inchi_Input( inchi_Input *pInp ); - int GetStructFromINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ); - int GetStructFromStdINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ); - void FreeStructFromStdINCHI( inchi_OutputStruct *out ); - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Win32 Dumpbin export information - - ordinal hint RVA name -cdecl - 1 0 000B7EAB CheckINCHI - 2 1 000B7221 CheckINCHIKey - 3 2 000B7C62 FreeINCHI - 4 3 000B7B04 FreeStdINCHI - 5 4 000B72B7 FreeStructFromINCHI - 6 5 000B7BC2 FreeStructFromStdINCHI - 7 6 000B7E33 Free_inchi_Input - 8 7 000B7C58 Free_std_inchi_Input - 9 8 000B727B GetINCHI - 10 9 000B75B4 GetINCHIKeyFromINCHI - 11 A 000B757D GetINCHIfromINCHI - 12 B 000B8211 GetStdINCHI - 13 C 000B7F0A GetStdINCHIKeyFromStdINCHI - 14 D 000B77CB GetStringLength - 15 E 000B7CA3 GetStructFromINCHI - 16 F 000B778A GetStructFromStdINCHI - 17 10 000B7DAC Get_inchi_Input_FromAuxInfo - 18 11 000B7D6B Get_std_inchi_Input_FromAuxInfo - 19 12 000B7D6B INCHIGEN_Create - 20 13 000B7F2D INCHIGEN_Destroy - 21 14 000B7F23 INCHIGEN_DoCanonicalization - 22 15 000B7F23 INCHIGEN_DoNormalization - 23 16 000B714A INCHIGEN_DoSerialization - 24 17 000B7FCD INCHIGEN_Reset - 25 18 000B7FCD INCHIGEN_Setup - 26 19 000B7EA6 STDINCHIGEN_Create - 27 1A 000B7EA6 STDINCHIGEN_Destroy - 28 1B 000B711D STDINCHIGEN_DoCanonicalization - 29 1C 000B7073 STDINCHIGEN_DoNormalization - 30 1D 000B7FC3 STDINCHIGEN_DoSerialization - 31 1E 000B7668 STDINCHIGEN_Reset - 32 1F 000B7438 STDINCHIGEN_Setup -__stdcall or PASCAL - 33 20 000B7DFC _CheckINCHI@8 - 34 21 000B7802 _CheckINCHIKey@4 - 35 22 000B7F73 _FreeINCHI@4 - 36 23 000B7F82 _FreeStdINCHI@4 - 37 24 000B75E1 _FreeStructFromINCHI@4 - 38 25 000B7B81 _FreeStructFromStdINCHI@4 - 39 26 000B7B86 _Free_inchi_Input@4 - 40 27 000B7A96 _Free_std_inchi_Input@4 - 41 28 000B7B5E _GetINCHI@8 - 42 29 000B7285 _GetINCHIKeyFromINCHI@24 - 43 2A 000B758C _GetINCHIfromINCHI@8 - 44 2B 000B7CDA _GetStdINCHI@8 - 45 2C 000B7979 _GetStdINCHIKeyFromStdINCHI@8 - 46 2D 000B7BA4 _GetStringLength@4 - 47 2E 000B70A5 _GetStructFromINCHI@8 - 48 2F 000B79B0 _GetStructFromStdINCHI@8 - 49 30 000B8022 _Get_inchi_Input_FromAuxInfo@16 - 50 31 000B76E0 _Get_std_inchi_Input_FromAuxInfo@12 - 51 32 000B7230 _INCHIGEN_Create@0 - 52 33 000B760E _INCHIGEN_Destroy@4 - 53 34 000B7087 _INCHIGEN_DoCanonicalization@8 - 54 35 000B70B4 _INCHIGEN_DoNormalization@8 - 55 36 000B72D5 _INCHIGEN_DoSerialization@12 - 56 37 000B7FE1 _INCHIGEN_Reset@12 - 57 38 000B7163 _INCHIGEN_Setup@12 - 58 39 000B7159 _STDINCHIGEN_Create@0 - 59 3A 000B78A7 _STDINCHIGEN_Destroy@4 - 60 3B 000B72F3 _STDINCHIGEN_DoCanonicalization@8 - 61 3C 000B737A _STDINCHIGEN_DoNormalization@8 - 62 3D 000B7B72 _STDINCHIGEN_DoSerialization@12 - 63 3E 000B7654 _STDINCHIGEN_Reset@12 - 64 3F 000B75FF _STDINCHIGEN_Setup@12 - - - Note. Currently there is no callback function for aborting, progress, etc. - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -#endif /* __INHCH_API_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/incomdef.h b/INCHI-1-SRC/INCHI_API/inchi_dll/incomdef.h deleted file mode 100644 index 72f1d08..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/incomdef.h +++ /dev/null @@ -1,181 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Former COMDEF.H -Renamed 06/12/07 to avoid occassional conflict with Microsoft's COMDEF.H -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -/* common definitions -- do not change */ -#ifndef __INCOMDEF_H__ -#define __INCOMDEF_H__ - -#include "ichisize.h" - - -/* SDF treatment */ -#define MAX_SDF_HEADER 64 /* max length of the SDFile data header */ -#define MAX_SDF_VALUE 255 /* max lenght of the SDFile data value */ - -/* size resrictions */ -#define ATOM_EL_LEN 6 /* length of atom name string including zero termination */ -#define ATOM_INFO_LEN 36 /* inf_ATOM output string ^123Al^+2H12..(+)/999/999/999/999: 32 chars */ -#define MAXVAL 20 /* max number of bonds per atom */ -#define MAX_STEREO_BONDS 3 /* max number of stereogenic bonds per atom */ -#define NUM_H_ISOTOPES 3 /* number of hydrogen isotopes: protium, deuterium, tritium */ -#define ATW_H 1 /* hydrogen atomic weight */ - -/* input bond type definition */ -#define MIN_INPUT_BOND_TYPE 1 -#define MAX_INPUT_BOND_TYPE 4 - -#define BOND_TYPE_SINGLE 1 -#define BOND_TYPE_DOUBLE 2 -#define BOND_TYPE_TRIPLE 3 -#define BOND_TYPE_ALTERN 4 - -#define STEREO_SNGL_UP 1 -#define STEREO_SNGL_EITHER 4 -#define STEREO_SNGL_DOWN 6 -#define STEREO_DBLE_EITHER 3 - - -/* MOlfile */ -#define INPUT_STEREO_SNGL_UP 1 -#define INPUT_STEREO_SNGL_EITHER 4 -#define INPUT_STEREO_SNGL_DOWN 6 -#define INPUT_STEREO_DBLE_EITHER 3 - -/* -#define BOND_MARK_ODD 0x10 -#define BOND_MARK_EVEN 0x20 -*/ -#define BOND_MARK_PARITY 0x30 -#define BOND_MARK_HIGHLIGHT 0x40 /* highlight equivalent components */ - -#define BOND_MARK_ODD '-' -#define BOND_MARK_EVEN '+' -#define BOND_MARK_UNDF '?' -#define BOND_MARK_UNKN 'u' -#define BOND_MARK_ERR '*' - -#define SALT_DONOR_H 1 -#define SALT_DONOR_Neg 2 -#define SALT_ACCEPTOR 4 -#define SALT_p_DONOR 8 /* >C-SH */ -#define SALT_p_ACCEPTOR 16 /* >C-S(-) */ -#define SALT_DONOR_ALL (SALT_DONOR_Neg | SALT_DONOR_H | SALT_p_ACCEPTOR | SALT_p_DONOR) -#define SALT_DONOR_Neg2 (SALT_DONOR_Neg | SALT_p_ACCEPTOR) -#define SALT_DONOR_H2 (SALT_DONOR_H | SALT_p_DONOR) -#define SALT_DONOR (SALT_DONOR_Neg | SALT_DONOR_H) - -#define SALT_SELECTED 32 - -/* radical definitions */ -#define RADICAL_SINGLET 1 -#define RADICAL_DOUBLET 2 -#define RADICAL_TRIPLET 3 - -/* metal definition */ -#define METAL 1 /* definition of an element: lowest valence */ -#define METAL2 3 /* definition of an element: lowest and next to it valence */ -#define IS_METAL 3 /* metal bitmap */ -/* isotopic shift */ -#define ZERO_ATW_DIFF 127 /* mark mass of the most abundant isotope */ - -/* other types */ - -#define UCINT (int)(unsigned char) - -#ifndef INCHI_US_CHAR_DEF -typedef signed char S_CHAR; -typedef unsigned char U_CHAR; -#define INCHI_US_CHAR_DEF -#endif - -#ifndef INCHI_US_SHORT_DEF -typedef signed short S_SHORT; -typedef unsigned short U_SHORT; -#define INCHI_US_SHORT_DEF -#endif - -/* BILLY 8/6/04 */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -#define STR_ERR_LEN 256 -int AddMOLfileError( char *pStrErr, const char *szMsg ); - -/* allocator */ -#ifndef inchi_malloc -void *inchi_malloc(size_t c); -#endif -#ifndef inchi_calloc -void *inchi_calloc(size_t c, size_t n); -#endif -#ifndef inchi_free -void inchi_free(void *p); -#endif - - - -/* sorting etc */ -void inchi_swap ( char *a, char *b, size_t width ); -int insertions_sort( void *base, size_t num, size_t width, int ( *compare )(const void *e1, const void *e2 ) ); -int insertions_sort_AT_NUMBERS( AT_NUMB *base, int num, int ( *compare )(const void *e1, const void *e2 ) ); - - -#define MOLFILE_ERR_FIN(err, new_err, err_fin, msg) \ - if ( !(err) && (new_err) ) { (err) = (new_err);} AddMOLfileError(pStrErr, (msg)); goto err_fin -#define MOLFILE_ERR_SET(err, new_err, msg) \ - if ( !(err) && (new_err) ) { (err) = (new_err);} AddMOLfileError(pStrErr, (msg)) - - -/* BILLY 8/6/04 */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -#endif /* __INCOMDEF_H__ */ - diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/inpdef.h b/INCHI-1-SRC/INCHI_API/inchi_dll/inpdef.h deleted file mode 100644 index a6b19ce..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/inpdef.h +++ /dev/null @@ -1,369 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -/* input/output format */ -#ifndef __INPDEF_H__ -#define __INPDEF_H__ - -/*^^^ */ -#include "mode.h" -#include "incomdef.h" -#include "ichidrp.h" -/*^^^ */ - -#define bDrawingLabelLeftShift endpoint /* for drawing only */ -typedef S_SHORT ST_CAP_FLOW; - -/* inp_ATOM::at_type */ -#define ATT_NONE 0x0000 -#define ATT_ACIDIC_CO 0x0001 -#define ATT_ACIDIC_S 0x0002 -#define ATT_OO 0x0004 -#define ATT_ZOO 0x0008 -#define ATT_NO 0x0010 -#define ATT_N_O 0x0020 -#define ATT_ATOM_N 0x0040 -#define ATT_ATOM_P 0x0080 -#define ATT_OTHER_NEG_O 0x0100 -#define ATT_OTHER_ZO 0x0200 /* -Z=O or =Z=O */ -#define ATT_OH_MINUS 0x0400 /* OH(-), O=O,S,Se,Te */ -#define ATT_O_PLUS 0x0800 /* -OH2(+), =OH(+), -OH(+)-, OH3(+), =O(+)-, etc; O=O,S,Se,Te */ -#define ATT_PROTON 0x1000 -#define ATT_HalAnion 0x2000 -#define ATT_HalAcid 0x4000 -#if ( FIX_NP_MINUS_BUG == 1 ) -#define ATT_NP_MINUS_V23 0x8000 /* =N(-) or =P(-) where = previously was triple */ -#endif - -#define AT_FLAG_ISO_H_POINT 0x01 /* may have isotopic H */ - -#define PERIODIC_NUMBER_H 1 - -#ifndef NUMH -#define NUM_ISO_H(AT,N) (AT[N].num_iso_H[0]+AT[N].num_iso_H[1]+AT[N].num_iso_H[2]) -#define NUMH(AT,N) (AT[N].num_H+NUM_ISO_H(AT,N)) -#endif - -#define FlagSC_0D 1 /* bUsed0DParity */ -#define FlagSB_0D 2 /* bUsed0DParity */ - -#define SB_PARITY_FLAG 0x38 /* mask for disconnected metal parity if it is different */ -#define SB_PARITY_SHFT 3 /* number of right shift bits to get disconnected metal parity */ -#define SB_PARITY_MASK 0x07 -#define SB_PARITY_1(X) (X & SB_PARITY_MASK) /* refers to connected structure */ -#define SB_PARITY_2(X) (((X) >> SB_PARITY_SHFT) & SB_PARITY_MASK) /* refers to connected structure */ - - - -typedef struct tagInputAtom { - char elname[ATOM_EL_LEN]; /* chem. element name */ - U_CHAR el_number; /* number of the element in the Periodic Table */ - AT_NUMB neighbor[MAXVAL]; /* positions (from 0) of the neighbors in the inp_ATOM array */ - AT_NUMB orig_at_number; /* original atom number */ - AT_NUMB orig_compt_at_numb; /* atom number within the component before terminal H removal */ - S_CHAR bond_stereo[MAXVAL]; /* 1=Up,4=Either,6=Down; this atom is at the pointing wedge, - negative => on the opposite side; 3=Either double bond */ - U_CHAR bond_type[MAXVAL]; /* 1..4; 4="aromatic", should be discouraged on input */ - - S_CHAR valence; /* number of bonds = number of neighbors */ - S_CHAR chem_bonds_valence; /* sum of bond types (type 4 needs special treatment) */ - S_CHAR num_H; /* number of implicit hydrogens including D and T */ - S_CHAR num_iso_H[NUM_H_ISOTOPES]; /* number of implicit 1H, 2H(D), 3H(T) < 16 */ - S_CHAR iso_atw_diff; /* =0 => natural isotopic abundances */ - /* >0 => (mass) - (mass of the most abundant isotope) + 1 */ - /* <0 => (mass) - (mass of the most abundant isotope) */ - S_CHAR charge; /* charge */ - S_CHAR radical; /* RADICAL_SINGLET, RADICAL_DOUBLET, or RADICAL_TRIPLET */ - S_CHAR bAmbiguousStereo; - S_CHAR cFlags; /* AT_FLAG_ISO_H_POINT */ - AT_NUMB at_type; /* ATT_NONE, ATT_ACIDIC */ - AT_NUMB component; /* number of the structure component > 0 */ - AT_NUMB endpoint; /* id of a tautomeric group */ - AT_NUMB c_point; /* id of a positive charge group */ - double x; - double y; - double z; - /* cml 0D parities */ - S_CHAR bUsed0DParity; /* bit=1 => stereobond; bit=2 => stereocenter */ - /* cml tetrahedral parity */ - S_CHAR p_parity; - AT_NUMB p_orig_at_num[MAX_NUM_STEREO_ATOM_NEIGH]; - /* cml bond parities */ - S_CHAR sb_ord[MAX_NUM_STEREO_BONDS]; /* stereo bond/neighbor ordering number, starts from 0 */ - /* neighbors on both sides of stereobond have same sign=> trans/T/E, diff. signs => cis/C/Z */ - S_CHAR sn_ord[MAX_NUM_STEREO_BONDS]; /* ord. num. of the neighbor adjacent to the SB; starts from 0; - -1 means removed explicit H */ - /* neighbors on both sides of stereobond have same parity => trans/T/E/2, diff. parities => cis/C/Z/1 */ - S_CHAR sb_parity[MAX_NUM_STEREO_BONDS]; - AT_NUMB sn_orig_at_num[MAX_NUM_STEREO_BONDS]; /* orig. at number of sn_ord[] neighbors */ - -#if ( FIND_RING_SYSTEMS == 1 ) - S_CHAR bCutVertex; - AT_NUMB nRingSystem; - AT_NUMB nNumAtInRingSystem; - AT_NUMB nBlockSystem; - -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) - AT_NUMB nDistanceFromTerminal; /* terminal atom or ring system has 1, next has 2, etc. */ -#endif - -#endif -} inp_ATOM; - -typedef struct tagOrigAtom { - /* initially filled out by MolfileToOrigAtom */ - /* may be changed by disconnecting salts and disconnecting metals */ - inp_ATOM *at; - int num_dimensions; - int num_inp_bonds; - int num_inp_atoms; - /* may be changed by disconnecting salts and disconnecting metals */ - int num_components; /* set by MarkDisconnectedComponents() and disconnecting metals */ - int bDisconnectSalts; /* whether salt disconnection is possible */ - int bDisconnectCoord; /* 0 if no disconnection needed else (Num Implicit H to disconnect)+1 */ -#if ( bRELEASE_VERSION == 0 ) - int bExtract; -#endif - AT_NUMB *nCurAtLen; /* has max_num_components elements */ - AT_NUMB *nOldCompNumber; /* 0 or component number in previous numbering */ - int nNumEquSets; /* number of found component equivalence sets */ - AT_NUMB *nEquLabels; /* num_inp_atoms elements, value>0 marks atoms in the set #value */ - AT_NUMB *nSortedOrder; /* num_components elements, values = 1..num_components; only if num_components > 1 */ - int bSavedInINCHI_LIB[INCHI_NUM]; - int bPreprocessed[INCHI_NUM]; - MOL_COORD *szCoord; -} ORIG_ATOM_DATA; - -typedef struct tagOriginalStruct { - int num_atoms; - char *szAtoms; - char *szBonds; - char *szCoord; -} ORIG_STRUCT; - -typedef struct tagAtomParmsForDrawing { - char at_string[ATOM_INFO_LEN]; - int DrawingLabelLeftShift; - int DrawingLabelLength; - AT_NUMB nCanonNbr; /* if zero then do not use all data for the atom */ - AT_NUMB nCanonEquNbr; - AT_NUMB nTautGroupCanonNbr; - AT_NUMB nTautGroupEquNbr; - S_CHAR cFlags; /* AT_FLAG_ISO_H_POINT */ -#ifdef DISPLAY_DEBUG_DATA - int nDebugData; -#endif - S_CHAR cHighlightTheAtom; - S_CHAR cStereoCenterParity; - S_CHAR cStereoBondParity[MAX_STEREO_BONDS]; - S_CHAR cStereoBondWarning[MAX_STEREO_BONDS]; - S_CHAR cStereoBondNumber[MAX_STEREO_BONDS]; -} inf_ATOM; - - -#define INF_STEREO_ABS 0x0001 -#define INF_STEREO_REL 0x0002 -#define INF_STEREO_RAC 0x0004 -#define INF_STEREO_NORM 0x0008 -#define INF_STEREO_INV 0x0010 -#define INF_STEREO 0x0020 -#define INF_STEREO_ABS_REL_RAC (INF_STEREO_ABS | INF_STEREO_REL | INF_STEREO_RAC) -#define INF_STEREO_NORM_INV (INF_STEREO_NORM | INF_STEREO_INV) - -#define MAX_LEN_REMOVED_PROTONS 128 - -typedef struct tagInfoAtomData { - inf_ATOM *at; - int num_at; - AT_NUMB StereoFlags; - AT_NUMB num_components; - AT_NUMB *pStereoFlags; - - int nNumRemovedProtons; - int num_removed_iso_H; /* number of exchangable isotopic H */ - NUM_H num_iso_H[NUM_H_ISOTOPES]; /* number of exchangable isotopic H */ - char szRemovedProtons[MAX_LEN_REMOVED_PROTONS]; -} INF_ATOM_DATA; - -typedef struct tagInputAtomData { - inp_ATOM *at; - inp_ATOM *at_fixed_bonds; /* tautomeric case, added or removed H */ - int num_at; - int num_removed_H; - int num_bonds; - int num_isotopic; - int bExists; - int bDeleted; - int bHasIsotopicLayer; - int bTautomeric; - int bTautPreprocessed; - int nNumRemovedProtons; - NUM_H nNumRemovedProtonsIsotopic[NUM_H_ISOTOPES]; /* isotopic composition of removed protons, not included in num_iso_H[] */ - NUM_H num_iso_H[NUM_H_ISOTOPES]; /* isotopic H on tautomeric atoms and those in nIsotopicEndpointAtomNumber */ - INCHI_MODE bTautFlags; - INCHI_MODE bTautFlagsDone; - INCHI_MODE bNormalizationFlags; -} INP_ATOM_DATA; -typedef INP_ATOM_DATA INP_ATOM_DATA2[TAUT_NUM]; - -typedef struct tagNormCanonFlags { - INCHI_MODE bTautFlags[INCHI_NUM][TAUT_NUM]; - INCHI_MODE bTautFlagsDone[INCHI_NUM][TAUT_NUM]; - INCHI_MODE bNormalizationFlags[INCHI_NUM][TAUT_NUM]; - int nCanonFlags[INCHI_NUM][TAUT_NUM]; -} NORM_CANON_FLAGS; - -typedef struct tagCompositeAtomData { - inp_ATOM *at; - int num_at; - int num_removed_H; - int num_bonds; - int num_isotopic; - int bExists; - int bDeleted; /* unused */ - int bHasIsotopicLayer; - int bTautomeric; - int nNumRemovedProtons; - NUM_H nNumRemovedProtonsIsotopic[NUM_H_ISOTOPES]; /* isotopic composition of removed protons, not included in num_iso_H[] */ - NUM_H num_iso_H[NUM_H_ISOTOPES]; /* isotopic H on tautomeric atoms and those in nIsotopicEndpointAtomNumber */ - - AT_NUMB *nOffsetAtAndH; - int num_components; -} COMP_ATOM_DATA; -/* -typedef COMP_ATOM_DATA COMP_ATOM_DATA3[TAUT_NUM+1]; -*/ -#define ADD_LEN_STRUCT_FPTRS 100 /* allocation increments */ -typedef long INCHI_FPTR; -typedef struct tagStructFptrs { - INCHI_FPTR *fptr; /* input: fptr[cur_fptr] = file pointer to the structure to read */ - /* output: fptr[cur_fptr+1] = file pointer to the next structure or EOF */ - int len_fptr; /* allocated length of fptr */ - int cur_fptr; /* input: k-1 to read the kth struct, k = 1, 2, 3,...; left unchanged; struct number := cur_fptr+1 */ - int max_fptr; /* length of the filled out portion of fptr */ -} STRUCT_FPTRS; - -#define FLAG_INP_AT_CHIRAL 1 -#define FLAG_INP_AT_NONCHIRAL 2 -#define FLAG_SET_INP_AT_CHIRAL 4 -#define FLAG_SET_INP_AT_NONCHIRAL 8 - -/* BILLY 8/6/04 */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -int MolfileToInpAtom( FILE *inp_molfile, int bDoNotAddH, inp_ATOM **at, MOL_COORD **szCoord, int max_num_at, - int *num_dimensions, int *num_bonds, const char *pSdfLabel, char *pSdfValue, - long *Id, long *lMolfileNumber, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ); -int MolfileToOrigAtom( FILE *inp_molfile, ORIG_ATOM_DATA *orig_at_data, int bMergeAllInputStructures, - int bGetOrigCoord, int bDoNotAddH, - const char *pSdfLabel, char *pSdfValue, long *lSdfId, long *lMolfileNumber, - INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ); -int INChIToOrigAtom( INCHI_IOSTREAM *inp_molfile, ORIG_ATOM_DATA *orig_at_data, int bMergeAllInputStructures, - int bGetOrigCoord, int bDoNotAddH, int vABParityUnknown, INPUT_TYPE nInputType, - char *pSdfLabel, char *pSdfValue, long *lSdfId, - INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ); - -int MarkDisconnectedComponents( ORIG_ATOM_DATA *orig_at_data, int bProcessOldCompNumbers ); -int DisconnectSalts( ORIG_ATOM_DATA *orig_inp_data, int bDisconnect ); -int DisconnectMetals( ORIG_ATOM_DATA *orig_inp_data, int bCheckMetalValence, INCHI_MODE *bTautFlagsDone ); -int bMayDisconnectMetals( ORIG_ATOM_DATA *orig_inp_data, int bCheckMetalValence, INCHI_MODE *bTautFlagsDone ); -int bHasMetalAtom( ORIG_ATOM_DATA *orig_inp_data ); -int FixAdjacentRadicals( int num_inp_atoms, inp_ATOM *at ); /* FIX_ADJ_RAD == 1 */ -int fix_odd_things( int num_atoms, inp_ATOM *at, int bFixBug, int bFixNonUniformDraw ); -int post_fix_odd_things( int num_atoms, inp_ATOM *at ); -int remove_ion_pairs( int num_atoms, inp_ATOM *at ); - -int bFoundFeature( inp_ATOM *at, int num_atoms ); -int CopyMOLfile(FILE *inp_file, long fPtrStart, long fPtrEnd, FILE *prb_file, long nNumb); - -void FreeInpAtom( inp_ATOM **at ); -void FreeInfAtom( inf_ATOM **at ); -void FreeOrigAtData( ORIG_ATOM_DATA *orig_at_data ); -void FreeInpAtomData( INP_ATOM_DATA *inp_at_data ); -void FreeCompAtomData( COMP_ATOM_DATA *inp_at_data ); -void FreeInfoAtomData( INF_ATOM_DATA *inf_at_data ); - -int FixUnkn0DStereoBonds(inp_ATOM *at, int num_at); - -inf_ATOM *CreateInfAtom( int num_atoms ); -inp_ATOM *CreateInpAtom( int num_atoms ); - -int CreateInfoAtomData( INF_ATOM_DATA *inf_at_data, int num_atoms, int num_components ); -int AllocateInfoAtomData( INF_ATOM_DATA *inf_at_data, int num_atoms, int num_components ); -int DuplicateInfoAtomData( INF_ATOM_DATA *inf_at_data_to, const INF_ATOM_DATA *inf_at_data_from); -int CreateInpAtomData( INP_ATOM_DATA *inp_at_data, int num_atoms, int create_at_fixed_bonds ); -int CreateCompAtomData( COMP_ATOM_DATA *inp_at_data, int num_atoms, int num_components, int bIntermediateTaut ); -#ifndef COMPILE_ANSI_ONLY -int DisplayInputStructure( char *szOutputString, inp_ATOM *at, INF_ATOM_DATA *inf_at_data, int num_at, DRAW_PARMS *dp ); -#endif -void PrintFileName( const char *fmt, FILE *output_file, const char *szFname ); -void MySleep( unsigned long ms ); - -#ifndef __ICHITIME_H__ -struct tagInchiTime; -int bInchiTimeIsOver( struct tagInchiTime *TickEnd ); -#endif - -int get_endpoint_valence( U_CHAR el_number ); -#if ( KETO_ENOL_TAUT == 1 ) -int get_endpoint_valence_KET( U_CHAR el_number ); -#endif - -#if ( TEST_RENUMB_ATOMS == 1 ) /* { */ -int CopyInpAtomData( INP_ATOM_DATA *dest_inp_at_data, INP_ATOM_DATA *src_inp_at_data ); -void RenumbInpAtomData( INP_ATOM_DATA *dest_inp_at_data, INP_ATOM_DATA *src_inp_at_data, AT_RANK *new_ord ); -void MakeNewOrd( int num_atoms, AT_RANK *new_ord ); -#endif - -int ReconcileAllCmlBondParities( inp_ATOM *at, int num_atoms, int bDisconnected ); - - -/* BILLY 8/6/04 */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -#endif /* __INPDEF_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/lreadmol.h b/INCHI-1-SRC/INCHI_API/inchi_dll/lreadmol.h deleted file mode 100644 index 667cf20..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/lreadmol.h +++ /dev/null @@ -1,1290 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -/* local prototypes */ -int bypass_sdf_data_items( FILE* inp, long *cas_reg_no, char* comment, int lcomment, char *name, int lname, int prev_err, - const char *pSdfLabel, char *pSdfValue, char *pStrErr ); -MOL_DATA* read_mol_file( FILE* inp, MOL_HEADER_BLOCK *OnlyHeaderBlock, MOL_CTAB *OnlyCtab, - int bGetOrigCoord, int *err, char *pStrErr ); - - -static int mol_read_hdr(MOL_HEADER_BLOCK *hdr, FILE* inp, char *pStrErr); -static int mol_read_counts_line( MOL_CTAB* ctab, FILE *inp, char *pStrErr ); -static int read_atom_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ); -static int read_bonds_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ); -static int read_stext_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ); -static int read_properties_block( MOL_CTAB* ctab, MOL_HEADER_BLOCK *pHdr, FILE *inp, int err, char *pStrErr ); - -static int identify_sdf_label( char* inp_line, const char *pSdfLabel ); -static long extract_cas_rn( char *line ); -static int mol_copy_check_empty( char* dest, char* source, int len, char **first_space ); -static int mol_read_datum(void* data, int field_len, int data_type, char** line_ptr); - -static int RemoveNonPrintable( char *line ); - - -/******/ -#ifndef MOLFILE_ERR_FIN -#define MOLFILE_ERR_FIN(err, new_err, err_fin, msg) \ - if ( !(err) && (new_err) ) { (err) = (new_err);} AddMOLfileError(pStrErr, (msg)); goto err_fin -#endif -#ifndef MOLFILE_ERR_SET -#define MOLFILE_ERR_SET(err, new_err, msg) \ - if ( !(err) && (new_err) ) { (err) = (new_err);} AddMOLfileError(pStrErr, (msg)) -#endif - -/*************************************************************************/ -int AddMOLfileError( char *pStrErr, const char *szMsg ) -{ - if ( pStrErr && szMsg && szMsg[0] ) { - int lenStrErr = strlen( pStrErr ); - int lenMsg = strlen( szMsg ); - char *p = strstr( pStrErr, szMsg ); - if ( p && (p==pStrErr || *(p-1) == ' ' && (*(p-2) == ';' || *(p-2) == ':' )) && - (p+lenMsg == pStrErr+lenStrErr || - p[lenMsg] == ';' && p[lenMsg+1] == ' ' || - p[lenMsg-1]==':' && p[lenMsg]==' ') ) { - return 1; /* reject duplicates */ - } - if ( lenStrErr + lenMsg + 2*(lenStrErr > 0) < STR_ERR_LEN ) { - /* enough room to add */ - if (lenStrErr > 0) { - if ( pStrErr[lenStrErr-1] != ':' ) { - strcat( pStrErr, ";" ); - } - strcat( pStrErr, " " ); - } - strcat( pStrErr, szMsg ); - return 1; - } - /* no room */ - if ( strstr( pStrErr, "..." ) ) { - return 0; /* no room mark has already been set */ - } - if ( lenStrErr + 3 < STR_ERR_LEN ) { - strcat( pStrErr, "..." ); - } - } - return 0; -} -/*************** static **********************************************************/ -int mol_copy_check_empty( char* dest, char* source, int len, char **first_space ) -{ - int i, c; /* required len >= 0; dest must have at least len+1 bytes */ - if ( len > 0 ) - strncpy( dest, source, len ); - dest[len]='\0'; - len = ( len > 0 )? (int)strlen( dest) : 0; - for ( i = (len-1); i >= 0 && 0 != (c = source[i]) && isspace(UCINT c); i-- ) - ; - *first_space = dest + (i+1); /* first blank or zero terminating byte in dest */ - return len; /* number of actually processed bytes; zero termination not included */ -} -/************* static ************************************************************/ -int mol_read_datum(void* data, int field_len, int data_type, char** line_ptr) -{ -/* 1. 'field_len' for MOL_STRING_DATA does not include trailing zero, - * that is actual length of the string pointed by 'data' - * should be at least field_len+1 bytes. - * For numerical data 'field_len' is length of input data field - * For numerical integral data field_len <= 0 means read up to first - * non-numeric character as strtod() does ("free format") - * 2. return value: for MOL_STRING_DATA: number of bytes excluding trailing zero - * for all others: 1=success; 0 = empty; -1= error - * 3. on exit *line_ptr points to the next byte after the last entered - */ - char *p = *line_ptr, *q, *p_end; - int i, ret=1, c, len; - long ldata; - double ddata; - - switch( data_type ) { - case MOL_STRING_DATA: - for ( i= 0; i < field_len && 0 != (c = p[i]) && isspace(UCINT c); i++ ) /* pass by all leading spaces */ - ; - len = mol_copy_check_empty( (char*)data, &p[i], field_len-i, &q ); - ret = ( q - (char*)data );/* actual data length */ - *q = '\0'; /* add zero termination to data if it is not there yet*/ - *line_ptr += (len+i); /* ptr to the 1st byte of the next input field or to zero termination */ - break; - - case MOL_CHAR_INT_DATA: - case MOL_SHORT_INT_DATA: - case MOL_LONG_INT_DATA: - { /* block start */ - char str[MOL_MAX_VALUE_LEN+1]; - ldata = 0L; - if ( field_len > MOL_MAX_VALUE_LEN ) { - ret = -1; - }else - if ( field_len > 0 ) { /* fixed length */ - *line_ptr += ( len = mol_copy_check_empty( str, p, field_len, &q ) ); - *q = '\0'; - if ( !len || !(q-str) ) { /* empty string */ - ret = 0; - }else - if ( (ldata=strtol(str,&p_end,10), p_end != q) ){ /* wrong data: incompletely interpreted */ - ret = -1; - } - }else{ /* free format: field_len <= 0 */ - ldata = strtol( p, &p_end, 10 ); - *line_ptr += ( len = p_end - p ); - if ( len == 0 ){ - ret = 0; - } - } - - switch( data_type ) { - case MOL_CHAR_INT_DATA: - if ( SCHAR_MIN <= ldata && ldata <= SCHAR_MAX ){ /* from || to &&: 11-19-96 */ - *(S_CHAR*)data = (S_CHAR)ldata; - }else{ - *(S_CHAR*)data = (S_CHAR)0; - ret = -1; - } - break; - case MOL_SHORT_INT_DATA: - if ( SHRT_MIN <= ldata && ldata <= SHRT_MAX ){ - *(S_SHORT*)data = (S_SHORT)ldata; - }else{ - *(S_SHORT*)data = (S_SHORT)0; - ret = -1; - } - break; - case MOL_LONG_INT_DATA: - if ( LONG_MIN < ldata && ldata < LONG_MAX ){ - *(long*)data = (long)ldata; - }else{ - *(long*)data = 0L; - ret = -1; - } - break; - default: - ret=-1; - } - - } /* block end */ - break; - case MOL_DOUBLE_DATA: - case MOL_FLOAT_DATA: - { /* block start */ - char str[MOL_MAX_VALUE_LEN+1]; - if ( field_len > MOL_MAX_VALUE_LEN ) { - ret = -1; - ddata = 0.0; - }else - if ( field_len > 0 ) { - *line_ptr += (len = mol_copy_check_empty( str, p, field_len, &q )); - *q = '\0'; - if ( !len || !(q-str) ) { /* empty string */ - ddata = 0.0; - ret = 0; - }else - if ( (ddata=strtod(str,&p_end), p_end != q) ){ /* wrong data */ - ret = -1; - } - }else{ /* free format */ - ddata = strtod( p, &p_end ); - *line_ptr += ( len = p_end - p ); - if ( len == 0 ){ - ret = 0; - } - } - switch(data_type){ - case MOL_DOUBLE_DATA: - if ( ddata != HUGE_VAL && /*ldata*/ ddata != -HUGE_VAL ){ /* replaced ldata with ddata 6-30-98 DCh */ - *(double*)data = ddata; - }else{ - *(double*)data = 0.0; - ret = -1; - } - break; - case MOL_FLOAT_DATA: - if ( fabs(ddata) <= (double)FLT_MIN ) { - *(float*)data = 0.0; - }else - if ( fabs(ddata) >= (double)FLT_MAX ) { - *(float*)data = 0.0; - ret = -1; - }else{ - *(float*)data = (float)ddata; - } - break; - } - } /* block end */ - break; - case MOL_JUMP_TO_RIGHT: - for ( i = 0; i < field_len && p[i]; i++ ) - ; - *line_ptr += i; - ret = i; - break; - default: - ret = -1; - } - return ret; -} -/************* static ************************************************************/ -int mol_read_hdr(MOL_HEADER_BLOCK *hdr, FILE* inp, char *pStrErr) -{ - /* All input lines can have are up 80 characters */ - /* Header Block */ - char line[MOLFILEINPLINELEN]; /* + cr +lf +zero termination + reserve */ - int err = 0, len; - const int line_len = sizeof(line); - char *p; - - /* memset( &hdr, 0, sizeof( MOL_HEADER_BLOCK ) ); */ - /*------------ header line #1: name ----------------*/ - if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ - err = 1; /* can't read the input file line */ - /* AddMOLfileError( pStrErr, "Can't read header block name line" ); */ - goto err_fin; - } - remove_one_lf( line ); - /* -- Disabled to relax strictness: allow > 80 chars names. - if ( line[MOLFILEMAXLINELEN] ){ - err = 2; // too long line - goto err_fin; - } - */ - len = mol_read_datum( hdr->szMoleculeName, sizeof(hdr->szMoleculeName)-1, MOL_STRING_DATA, &p ); - /*----------- header line #2 -----------------------*/ - if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ - err = 3; /* can't read the input file line */ - /* AddMOLfileError( pStrErr, "Can't read header block line 2" ); */ - goto err_fin; - } - remove_one_lf( line ); - /* -- Disabled to relax strictness: allow > 80 chars names. - if ( line[MOLFILEMAXLINELEN] ){ - err = 4; // too long input file line - goto err_fin; - } - */ - len = mol_read_datum( hdr->szUserInitials, sizeof(hdr->szUserInitials)-1, MOL_STRING_DATA, &p ); - len = mol_read_datum( hdr->szProgramName, sizeof(hdr->szProgramName)-1, MOL_STRING_DATA, &p ); - - /*------------ Relax strictness -----------------------*/ - len = mol_read_datum( &hdr->cMonth, 2, MOL_CHAR_INT_DATA, &p ); - len = mol_read_datum( &hdr->cDay, 2, MOL_CHAR_INT_DATA, &p ); - len = mol_read_datum( &hdr->cYear, 2, MOL_CHAR_INT_DATA, &p ); - len = mol_read_datum( &hdr->cHour, 2, MOL_CHAR_INT_DATA, &p ); - len = mol_read_datum( &hdr->cMinute, 2, MOL_CHAR_INT_DATA, &p ); - len = mol_read_datum( hdr->szDimCode, sizeof(hdr->szDimCode)-1, MOL_STRING_DATA, &p ); - len = mol_read_datum( &hdr->nScalingFactor1, 2, MOL_SHORT_INT_DATA, &p ); - len = mol_read_datum( &hdr->dScalingFactor2, 10, MOL_DOUBLE_DATA, &p ); - len = mol_read_datum( &hdr->dEnergy, 12, MOL_DOUBLE_DATA, &p ); - len = mol_read_datum( &hdr->lInternalRegistryNumber, 6, MOL_LONG_INT_DATA, &p ); - - /* save the whole line 2 */ - p = line; - len = mol_read_datum( hdr->szMoleculeLine2, sizeof(hdr->szMoleculeLine2)-1, MOL_STRING_DATA, &p ); - - - /*------------ header line #3: comment ----------------*/ - if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ - err = 7; /* can't read the line */ - /* AddMOLfileError( pStrErr, "Can't read header block comment line" ); */ - goto err_fin; - } - remove_one_lf( line ); - /* -- Disabled to relax strictness: allow > 80 chars comments. - if ( line[MOLFILEMAXLINELEN] ){ - err = 8; // too long line - goto err_fin; - } - */ - len = mol_read_datum( hdr->szComment, sizeof(hdr->szComment)-1, MOL_STRING_DATA, &p ); - -err_fin: - - return err; -} -/********** static *****************************************************/ -int RemoveNonPrintable( char *line ) -{ - int i, c, num = 0; - if ( line ) { - for ( i = 0; c = UCINT line[i]; i ++ ) { - /* assuming ASCII charset */ - if ( c < ' ' || c >= 0x7F ) { - line[i] = '.'; - num ++; - } - } - } - return num; -} -/************** static *************************************************/ -int mol_read_counts_line( MOL_CTAB* ctab, FILE *inp, char *pStrErr ) -{ - char *p; - char line[MOLFILEINPLINELEN]; - const int line_len = sizeof(line); - int err = 0, len; - - if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ - MOLFILE_ERR_FIN (err, 1, err_fin, "Cannot read counts line"); - /* can't read the input file line */ - } - remove_one_lf( line ); - if ( line[MOLFILEMAXLINELEN] ){ - MOLFILE_ERR_SET (err, 0, "Too long counts line"); /* too long input file line */ - } - if ( 0 > mol_read_datum( &ctab->nNumberOfAtoms, 3, MOL_SHORT_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->nNumberOfBonds, 3, MOL_SHORT_INT_DATA, &p ) -#if ( MOL_QUERY == MOL_PRESENT ) - || 0 > mol_read_datum( &ctab->nNumberOfAtomsLists, 3, MOL_SHORT_INT_DATA, &p ) -#else - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) -#endif - || 0 > mol_read_datum( NULL, /*obsolete*/ 3, MOL_JUMP_TO_RIGHT, &p ) - || 0 > mol_read_datum( &ctab->cChiralFlag, 3, MOL_CHAR_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->nNumberOfStextEntries, 3, MOL_SHORT_INT_DATA, &p ) -#if ( MOL_CPSS == MOL_PRESENT ) - || 0 > mol_read_datum( &ctab->nNumberOfReactionComponentsPlus1, 3, MOL_SHORT_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->nNumberOfReactants, 3, MOL_SHORT_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->nNumberOfProducts, 3, MOL_SHORT_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->nNumberOfIntermediates, 3, MOL_SHORT_INT_DATA, &p ) -#else - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) -#endif - || 0 > mol_read_datum( &ctab->nNumberOfPropertyLines, 3, MOL_SHORT_INT_DATA, &p ) ){ - err = 3; /* can't interpret counts line */ - MOLFILE_ERR_SET (err, 3, "Cannot interpret counts line:"); /* too long input file line */ - RemoveNonPrintable( line ); - AddMOLfileError(pStrErr, line); - goto err_fin; - } - len = mol_read_datum( ctab->csCurrentCtabVersion, sizeof(ctab->csCurrentCtabVersion)-1, MOL_STRING_DATA, &p ); -err_fin: - return err; -} - -/************ static *************************************************************/ -int read_atom_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ) -{ - char *p; - char line[MOLFILEINPLINELEN]; - const int line_len = sizeof(line); - S_SHORT i, chg; - static S_SHORT charge_val[] = {0, 3, 2, 1, 'R', -1, -2, -3}; - /* 0 1 2 3 4 5 6 7 */ - /* - if ( NULL == ctab->MolAtom ){ - err = 1; - goto err_fin; // internal error: MolAtom structure has not been allocated - } - */ - - for ( i = 0; i < ctab->nNumberOfAtoms; i++ ) { - - if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ - if ( !err ) { - MOLFILE_ERR_SET (err, 2, "Cannot read atom block line"); - } - break; - } - remove_one_lf( line ); - if ( line[MOLFILEMAXLINELEN] ){ - MOLFILE_ERR_SET (err, 0, "Too long atom block line"); - } - if ( err ) { - if ( !strcmp( line, SDF_END_OF_DATA ) ) { - err = -abs(err); - break; - } - continue; /* bypass the rest of the Atom block */ - } - if ( NULL != ctab->szCoord ) { - mystrncpy( ctab->szCoord[i], p, 31 ); /* original coordinates */ - } - - if ( NULL != ctab->MolAtom ) { - if ( 0 > mol_read_datum( &ctab->MolAtom[i].fX, 10, MOL_DOUBLE_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolAtom[i].fY, 10, MOL_DOUBLE_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolAtom[i].fZ, 10, MOL_DOUBLE_DATA, &p ) - || 0 > mol_read_datum( NULL, /* undescribed in article*/ 1, MOL_JUMP_TO_RIGHT, &p ) - || 0 == mol_read_datum( &ctab->MolAtom[i].szAtomSymbol, 3, MOL_STRING_DATA, &p ) /* was sizeof(ctab->MolAtom[0].szAtomSymbol)-1 */ -#ifdef TARGET_EXE_USING_API - || 0 > mol_read_datum( &ctab->MolAtom[i].cMassDifference, 2, MOL_SHORT_INT_DATA, &p ) -#else - || 0 > mol_read_datum( &ctab->MolAtom[i].cMassDifference, 2, MOL_CHAR_INT_DATA, &p ) -#endif - || 0 > mol_read_datum( &ctab->MolAtom[i].cCharge, 3, MOL_CHAR_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolAtom[i].cStereoParity, 3, MOL_CHAR_INT_DATA, &p ) -#if ( MOL_QUERY == MOL_PRESENT ) - || 0 > mol_read_datum( &ctab->MolAtom[i].cH_countPlus1, 3, MOL_CHAR_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolAtom[i].cStereoCare, 3, MOL_CHAR_INT_DATA, &p ) -#else - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) -#endif - || 0 > mol_read_datum( &ctab->MolAtom[i].cValence, 3, MOL_CHAR_INT_DATA, &p ) ) { - - err = 4; - MOLFILE_ERR_SET (err, 4, "Cannot interpret atom block line:"); - RemoveNonPrintable( line ); - AddMOLfileError(pStrErr, line); - if ( !strcmp( line, SDF_END_OF_DATA ) ) { - err = -abs(err); - break; - } - continue; /* can't interpret a first half of atom block line */ - } - if ( 2 == strlen(ctab->MolAtom[i].szAtomSymbol) && isupper(UCINT ctab->MolAtom[i].szAtomSymbol[1])) - ctab->MolAtom[i].szAtomSymbol[1] = (char)tolower(UCINT ctab->MolAtom[i].szAtomSymbol[1]); /* 5-4-99 DCh*/ - - if ( (chg = (S_SHORT) ctab->MolAtom[i].cCharge)< 0 || chg >= (int)(sizeof ( charge_val ) / sizeof( charge_val[0] )) ) { - /* ctab->MolAtom[i].cCharge = 0; */ /* error; ignore for now */ - ctab->MolAtom[i].cCharge = (S_CHAR)(4 - chg); /* allow greater charges to accommodate NCI structures. 8-20-2002 */ - ctab->MolAtom[i].cRadical = 0; - }else - if ( 'R' == (chg = charge_val[chg]) ){ - ctab->MolAtom[i].cCharge = 0; - ctab->MolAtom[i].cRadical = RADICAL_DOUBLET; - }else{ - ctab->MolAtom[i].cCharge = (S_CHAR)chg; /* actual charge value */ - ctab->MolAtom[i].cRadical = 0; - } -#ifdef TARGET_EXE_USING_API - if ( ctab->MolAtom[i].cMassDifference ) { /* e_ReadMOL.c specific */ - ctab->MolAtom[i].cMassDifference += ISOTOPIC_SHIFT_FLAG; - } -#endif - - if ( -#if ( MOL_CPSS == MOL_PRESENT ) - 0 > mol_read_datum( &ctab->MolAtom[i].cH0_designator, 3, MOL_CHAR_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolAtom[i].cReactionComponentType, 3, MOL_CHAR_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolAtom[i].cReactionComponentNumber, 3, MOL_CHAR_INT_DATA, &p ) -#else - 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) -#endif -#if ( MOL_REACT == MOL_PRESENT ) - || 0 > mol_read_datum( &ctab->MolAtom[i].nAtomAtomMappingNumber, 3, MOL_SHORT_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolAtom[i].cReactionComponentType, 3, MOL_CHAR_INT_DATA, &p ) -#else - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) -#endif -#if ( MOL_REACT == MOL_PRESENT || MOL_QUERY == MOL_PRESENT ) - || 0 > mol_read_datum( &ctab->MolAtom[i].cExactChargeFlag, 3, MOL_CHAR_INT_DATA, &p ) -#else - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) -#endif - ){ - err = 5; /* can't interpret a second half of atom block line */ - MOLFILE_ERR_SET (err, 5, "Cannot interpret atom block line:"); - RemoveNonPrintable( line ); - AddMOLfileError(pStrErr, line); - if ( !strcmp( line, SDF_END_OF_DATA ) ) { - err = -abs(err); - break; - } - continue; - } - } - } -/* err_fin: */ - return err; -} -/************ static *************************************************************/ -int read_bonds_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ) -{ - char *p; - char line[MOLFILEINPLINELEN]; - const int line_len = sizeof(line); - S_SHORT i; - /* - if ( NULL == ctab->MolBond ){ - err = 1; - goto err_fin; // internal error: memory has not been allocated for MolBond structure - } - */ - for ( i = 0; i < ctab->nNumberOfBonds; i++ ) { - - if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ - if ( !err ) { - MOLFILE_ERR_SET (err, 2, "Cannot read bond block line"); - } - break; - } - remove_one_lf( line ); - if ( line[MOLFILEMAXLINELEN] ){ - err = err? err : 3; /* too long input file line */ - } - if ( err ) { - if ( !strcmp( line, SDF_END_OF_DATA ) ) { - err = -abs(err); - break; - } - continue; - } - - if ( ctab->MolBond ) { - if ( 0 > mol_read_datum( &ctab->MolBond[i].nAtomNo1, 3, MOL_SHORT_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolBond[i].nAtomNo2, 3, MOL_SHORT_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolBond[i].cBondType, 3, MOL_CHAR_INT_DATA, &p ) - || 0 > mol_read_datum( &ctab->MolBond[i].cBondStereo, 3, MOL_CHAR_INT_DATA, &p ) -#if ( MOL_QUERY == MOL_PRESENT ) - || 0 > mol_read_datum( &ctab->MolBond[i].cBondTopology, 3, MOL_CHAR_INT_DATA, &p ) /* ring/chain */ -#else - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) -#endif -#if ( MOL_REACT == MOL_PRESENT ) - || 0 > mol_read_datum( &ctab->MolBond[i].cReactingCenterStatus, 3, MOL_CHAR_INT_DATA, &p ) -#else - || 0 > mol_read_datum( NULL, 3, MOL_JUMP_TO_RIGHT, &p ) -#endif - ){ - if ( !err ) { - /* can't interpret bonds block line */ - MOLFILE_ERR_SET (err, 4, "Cannot interpret bond block line:"); - RemoveNonPrintable( line ); - AddMOLfileError(pStrErr, line); - } - if ( !strcmp( line, SDF_END_OF_DATA ) ) { - err = -abs(err); - break; - } - } - } - } - /* err_fin: */ - return err; -} -/********** static ***************************************************************/ -int read_stext_block( MOL_CTAB* ctab, FILE *inp, int err, char *pStrErr ) -{ - /* just pass by all stext enties without attemp to interpret */ - char *p; - char line[MOLFILEINPLINELEN]; - const int line_len = sizeof(line); - S_SHORT i; - - for ( i = 0; i < 2*ctab->nNumberOfStextEntries; i++ ) { - - if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ - if ( !err ) { - MOLFILE_ERR_FIN (err, 2, err_fin, "Cannot read STEXT block line"); - } - break; - /* can't read the input file line */ - } - /* - remove_one_lf( line ); - if ( line[MOLFILEMAXLINELEN] ){ - MOLFILE_ERR_SET (err, 2, "Warning: Too long STEXT block line"); - // too long input file line - } - */ - } -err_fin: - return err; -} -/************ static *************************************************************/ -int read_properties_block( MOL_CTAB* ctab, MOL_HEADER_BLOCK *pHdr, FILE *inp, int err, char *pStrErr ) -{ - enum { MULTI_LINE_MODE_NO_MODE, MULTI_LINE_MODE_ISIS_ALIAS }; - char *p; - char line[MOLFILEINPLINELEN]; - const int line_len = sizeof(line); - int nMultiLineMode = MULTI_LINE_MODE_NO_MODE, nAtomNumber=0; - S_SHORT i, j; - char charM[2]; - char szBlank[3]; - char szType[4]; - S_SHORT skip_lines=0; - S_SHORT num_entries; - S_SHORT num_atoms = ctab->nNumberOfAtoms; - - int charge_encountered = 0; - int radical_encountered = 0; - int isotope_encountered = 0; - /* - if ( NULL == ctab->MolAtom ){ - err = 1; - goto err_fin; internal error: memory has not been allocated for MolAtom structure - } - */ - for ( i = 0; ctab->csCurrentCtabVersion[0]? 1 : (i < ctab->nNumberOfPropertyLines); i++ ) { /* the last line should be M END */ - /* ctab->csCurrentCtabVersion[0] == 0: - exactly ctab->nNumberOfPropertyLines lines including M END */ - /* ctab->csCurrentCtabVersion[0] != 0: - read until M END line was encountered */ - if ( NULL == ( p = inchi_fgetsLf( line, line_len, inp ) ) ){ - if ( !err ) { - MOLFILE_ERR_SET (err, 2, "Cannot read properties block line"); - } - goto err_fin; - } - remove_one_lf( line ); - if ( line[MOLFILEMAXLINELEN] ){ - MOLFILE_ERR_SET (err, 3, "Too long properties block line"); - continue; - } - if ( skip_lines > 0 ) { - skip_lines --; - continue; - } - /* alias. */ - if ( nMultiLineMode == MULTI_LINE_MODE_ISIS_ALIAS && nAtomNumber ) { - int len; - nMultiLineMode = MULTI_LINE_MODE_NO_MODE; - if ( 0 >= (len=normalize_name( p )) ) { - nAtomNumber = 0; - continue; - } - if( 0 < len && len < (int)(sizeof(ctab->MolAtom->szAtomSymbol)) ) { - int nCharge, nRad; - MOL_ATOM* MolAtom = ctab->MolAtom + nAtomNumber-1; - /* ctab->MolAtom[nAtomNumber-1].cAtomAliasedFlag = 1; */ - /* extract radicals & charges */ - extract_ChargeRadical( p, &nRad, &nCharge ); - /* Aliased atom cannot have charge, radical & mass difference */ - /* in the atom table or "M CHG", "M RAD", "M ISO" */ - /* if ( nCharge ) */ - MolAtom->cCharge = (S_CHAR)nCharge; - /* if ( nRad ) */ - MolAtom->cRadical = (char)nRad; - - if ( 1 == len && 'D' == p[0] ) { - /* H isotope */ - p[0] = 'H'; -#ifdef TARGET_EXE_USING_API - MolAtom->cMassDifference=(1 + ISOTOPIC_SHIFT_FLAG); -#else - MolAtom->cMassDifference=1; -#endif - } else - if ( 1 == len && 'T' == p[0] ) { - /* H isotope */ - p[0] = 'H'; -#ifdef TARGET_EXE_USING_API - MolAtom->cMassDifference=(2 + ISOTOPIC_SHIFT_FLAG); -#else - MolAtom->cMassDifference=2; -#endif - } else - MolAtom->cMassDifference=0; - if ( strlen(p) < sizeof(ctab->MolAtom[0].szAtomSymbol) ) { - strcpy(MolAtom->szAtomSymbol, p); - } else { - strcpy(MolAtom->szAtomSymbol, "???"); - } - MolAtom->cAtomAliasedFlag ++; - } - skip_lines = 0; - nAtomNumber = 0; - continue; - } - - if ( 1 != mol_read_datum( charM, sizeof(charM) - 1, MOL_STRING_DATA, &p ) - || 0 != mol_read_datum( szBlank, sizeof(szBlank) - 1, MOL_STRING_DATA, &p ) /* must contain 0 bytes */ - || 0 >= mol_read_datum( szType, sizeof(szType) - 1, MOL_STRING_DATA, &p ) /* must contain 3 bytes */ - ) { - if ( !strcmp( line, SDF_END_OF_DATA ) ) { - err = err? -abs(err): -4; - break; - } - continue; /* ignore because cannot recognize */ - } - if ( charM[0] == 'V' ){ - skip_lines = 0; /* ISIS/Desktop Atom Value: one-line property */ - continue; - } - if ( charM[0] == 'G' ){ - skip_lines = 1; /* ISIS/Desktop Group abbreviation: two-line property */ - continue; - } - if ( charM[0] == 'A' ) { - if ( NULL != ctab->MolAtom && - 0 < ( nAtomNumber = (int)strtol(szType, NULL, 10) ) && - nAtomNumber <= ctab->nNumberOfAtoms ){ - /* Atom Alias [ISIS/Desktop] two-line property */ - nMultiLineMode = MULTI_LINE_MODE_ISIS_ALIAS; - continue; - } else { - nAtomNumber = 0; - skip_lines = 1; - continue; - } - } - if ( charM[0] == 'S' && !strcmp( szType, "SKP" ) ){ /* skip lines */ - if ( 0 >= mol_read_datum( &skip_lines, 3, MOL_SHORT_INT_DATA, &p ) ) { - skip_lines = 0; - } - continue; - } - if ( charM[0] != 'M' ) {/* cannot recognize a line */ - continue; - } - if ( !strcmp( szType, "REG" ) ) { - int len; - p = p + strspn( p, " " ); - len = strcspn( p, " " ); - len = inchi_min( len, MOL_MAX_VALUE_LEN ); - mol_read_datum( &pHdr->lInternalRegistryNumber, len, MOL_LONG_INT_DATA, &p ); - continue; - } - - if ( !strcmp( szType, "END" ) ){ - if ( ctab->csCurrentCtabVersion[0] ) - break; /* end of property lines */ - continue; - } - - if ( NULL == ctab->MolAtom ) - continue; /* ignore because the user requested to bypass all this stuff */ - - /*----------------------------------- charge: Generic */ - if ( !strcmp( szType, "CHG" ) && - 0 < mol_read_datum( &num_entries, 3, MOL_SHORT_INT_DATA, &p ) && - 1 <= num_entries && num_entries <= 8 ) { - S_SHORT atoms[8]; - S_SHORT charges[8]; - if ( !charge_encountered && !radical_encountered ) { - /* first charge or radical record clears all Atom Block */ - /* entered charge and radical data to zeroes */ - charge_encountered = -1; - } - for ( j = 0; j < num_entries; j++ ) { - if ( 0 > mol_read_datum( &atoms[j], 0, MOL_SHORT_INT_DATA, &p ) || - 0 > mol_read_datum( &charges[j], 0, MOL_SHORT_INT_DATA, &p ) || - atoms[j] <= 0 || atoms[j] > num_atoms || - charges[j] < -15 || charges[j] > 15 ) { - goto charge_error; - } - } - if ( charge_encountered == -1 ) { - for ( j = 0; j < num_atoms; j++ ) { - if ( !ctab->MolAtom[j].cAtomAliasedFlag ) /* do not clear aliased atoms.*/ - ctab->MolAtom[j].cCharge = ctab->MolAtom[j].cRadical = '\0'; - } - charge_encountered = 1; - } - for ( j = 0; j < num_entries; j++ ) { - if ( !ctab->MolAtom[atoms[j]-1].cAtomAliasedFlag ) /* do not change aliased atoms.*/ - ctab->MolAtom[atoms[j]-1].cCharge = (S_CHAR)charges[j]; - } - continue; - charge_error: - MOLFILE_ERR_SET (err, 0, "Charge not recognized:"); - RemoveNonPrintable( line ); - AddMOLfileError(pStrErr, line); - continue; /* ignore for now */ - } - /*-------------------------------------- radical: Generic */ - if ( !strcmp( szType, "RAD" ) && - 0 < mol_read_datum( &num_entries, 3, MOL_SHORT_INT_DATA, &p ) && - 1 <= num_entries && num_entries <= 8 ) { - S_SHORT atoms[8]; - S_SHORT radicals[8]; - if ( !charge_encountered && !radical_encountered ) { - /* first charge or radical record clears all Atom Block */ - /* entered charge and radical data to zeroes */ - radical_encountered = -1; - } - for ( j = 0; j < num_entries; j++ ) { - if ( 0 > mol_read_datum( &atoms[j], 0, MOL_SHORT_INT_DATA, &p ) || - 0 > mol_read_datum( &radicals[j], 0, MOL_SHORT_INT_DATA, &p ) || - atoms[j] <= 0 || atoms[j] > num_atoms || - radicals[j] < 0 || radicals[j] > 3 ) { - goto radical_error; - } - } - if ( radical_encountered == -1 ) { - for ( j = 0; j < num_atoms; j++ ) { - if ( !ctab->MolAtom[j].cAtomAliasedFlag ) /* do not clear aliased atoms. 5-3-99 DCh */ - ctab->MolAtom[j].cCharge = ctab->MolAtom[j].cRadical = '\0'; - } - radical_encountered = 1; - } - for ( j = 0; j < num_entries; j++ ) { - if ( !ctab->MolAtom[atoms[j]-1].cAtomAliasedFlag ) { /* do not change aliased atoms. 5-3-99 DCh */ - ctab->MolAtom[atoms[j]-1].cRadical = (S_CHAR)radicals[j]; - } - } - continue; - radical_error: - MOLFILE_ERR_SET (err, 0, "Radical not recognized:"); - RemoveNonPrintable( line ); - AddMOLfileError(pStrErr, line); - continue; /* ignore error for now */ - } - /*-------------------------------------- isotope: Generic */ - if ( !strcmp( szType, "ISO" ) && - 0 < mol_read_datum( &num_entries, 3, MOL_SHORT_INT_DATA, &p ) && - 1 <= num_entries && num_entries <= 8 ) { - S_SHORT atoms[8]; - S_SHORT iso_mass[8]; /* contains istotope mass number, not difference. 7-14-00 DCh. */ - if ( !isotope_encountered ) { - /* first charge or radical record clears all Atom Block */ - /* entered charge and radical data to zeroes */ - isotope_encountered = -1; - } - for ( j = 0; j < num_entries; j++ ) { - if ( 0 > mol_read_datum( &atoms[j], 0, MOL_SHORT_INT_DATA, &p ) || - 0 > mol_read_datum( &iso_mass[j], 0, MOL_SHORT_INT_DATA, &p ) || - atoms[j] <= 0 || atoms[j] > num_atoms - /*|| iso_mass[j] < -18 || iso_mass[j] > 12*/ ) { - /* goto isotope_error; */ - atoms[j] = -1; /* flag error */ - MOLFILE_ERR_SET (err, 0, "Isotopic data not recognized:"); - RemoveNonPrintable( line ); - AddMOLfileError(pStrErr, line); - continue; /* ignore isotopic error for now */ - } - } - if ( isotope_encountered == -1 ) { - for ( j = 0; j < num_atoms; j++ ) { - /*if ( !ctab->MolAtom[j].cAtomAliasedFlag )*/ /* clear even aliased atoms */ - ctab->MolAtom[j].cMassDifference = 0; - } - isotope_encountered = 1; - } - for ( j = 0; j < num_entries; j++ ) { - if ( atoms[j] <= 0 ) - continue; /* ignore isotopic error for now */ - if ( 1 /* !ctab->MolAtom[atoms[j]-1].cAtomAliasedFlag */) { - char *at = ctab->MolAtom[atoms[j]-1].szAtomSymbol; - if ( at[1] || at[0] != 'D' && at[0] != 'T' ) { /* D & T cannot have ISO */ - /* need atomic weight to calculate isotope difference. 7-14-00 DCh. */ -#ifdef TARGET_EXE_USING_API - /*^^^ Check added 5-10-2008 - IPl */ - if (iso_mass[j] > 0) - /* According to MDL specification, p.12, only a positive - integer is allowed. And yes, there appeared some MOL/SD - files contaning here a negative value. This manifested - in mismatch in InChI_MAIN vs. cInChI-1/stdinchi-1 results. - */ - ctab->MolAtom[atoms[j]-1].cMassDifference = iso_mass[j]; /* mass, not difference */ - -#else - int atw, atw_diff; - /*^^^ - NB: According to MDL specification, difference should be in - [-18; +12] range, not in [-19; +19] as is checked below. */ - if ( (atw = get_atw( at )) && abs( atw_diff = (int)iso_mass[j] - atw ) < 20 ) { - ctab->MolAtom[atoms[j]-1].cMassDifference = (char)(atw_diff? atw_diff : ZERO_ATW_DIFF); - } -#endif - } - } - } - continue; - } - } -err_fin: - return err; -} -/************ global *************************************************************/ -MOL_DATA* delete_mol_data( MOL_DATA* mol_data ) -{ - if ( mol_data ) { - if ( mol_data->ctab.MolAtom ) - inchi_free( mol_data->ctab.MolAtom ); - if ( mol_data->ctab.MolBond ) - inchi_free( mol_data->ctab.MolBond ); - if ( mol_data->ctab.szCoord ) - inchi_free( mol_data->ctab.szCoord ); - inchi_free( mol_data ); - mol_data = NULL; - } - return mol_data; -} -/************* global ************************************************************/ -/* Comletely ingnore STEXT block, queries, and 3D features - */ -MOL_DATA* read_mol_file( FILE* inp, MOL_HEADER_BLOCK *OnlyHeaderBlock, MOL_CTAB *OnlyCtab, - int bGetOrigCoord, int *err, char *pStrErr ) -{ - MOL_DATA* mol_data = NULL; - int ret = 0, prev_ret, bEndOfData = 0; - int bReadAll = ( OnlyHeaderBlock == NULL ); - MOL_CTAB ctab, *pCtab = NULL; - MOL_HEADER_BLOCK *pHdr = NULL; - - *err = 0; - if ( bReadAll ) { - if ( NULL == ( mol_data = ( MOL_DATA* )inchi_calloc( 1, sizeof(MOL_DATA) ) ) ){ - ret = 1; /* can't allocate mol_data structure */ - AddMOLfileError( pStrErr, "Out of RAM" ); - goto err_fin; - } - pHdr = &mol_data->hdr; - pCtab = &mol_data->ctab; - } else { - pHdr = OnlyHeaderBlock; - pCtab = OnlyCtab? OnlyCtab : &ctab; - memset( pHdr, 0, sizeof( MOL_HEADER_BLOCK ) ); - memset( pCtab, 0, sizeof( MOL_CTAB ) ); - } - pCtab->MolBond = NULL; - pCtab->MolAtom = NULL; - pCtab->szCoord = NULL; - - if ( 0 != ( ret = mol_read_hdr(pHdr, inp, pStrErr) ) ){ - ret += 10; - goto err_fin; /* most probably end of file */ - } - if ( 0 != ( ret = mol_read_counts_line( pCtab , inp, pStrErr) ) ){ - ret += 20; - goto err_fin; - } - - if ( bReadAll ) { - if ( NULL == ( mol_data->ctab.MolAtom = (MOL_ATOM*)inchi_calloc(inchi_max(mol_data->ctab.nNumberOfAtoms,1), sizeof(MOL_ATOM)) ) ){ - ret = 2; /* can't allocate MolAtom structure */ - MOLFILE_ERR_FIN (ret, 2, err_fin, "Out of RAM"); - } - if ( bGetOrigCoord && - NULL == ( mol_data->ctab.szCoord = (MOL_COORD*)inchi_calloc(inchi_max(mol_data->ctab.nNumberOfAtoms,1), sizeof(MOL_COORD)) ) ){ - ret = 2; /* can't allocate MolAtom structure */ - MOLFILE_ERR_FIN (ret, 2, err_fin, "Out of RAM"); - } - } - if ( 0 != ( ret = read_atom_block(pCtab, inp, ret, pStrErr) ) ){ - if ( ret < 0 ) { - ret = -ret; - bEndOfData = 1; - } - ret += 30; - /* goto err_fin; */ - } - - if ( bReadAll && ret < 30 ) { - if ( !bEndOfData && NULL == ( mol_data->ctab.MolBond = (MOL_BONDS*)inchi_calloc(inchi_max(mol_data->ctab.nNumberOfBonds,1), sizeof(MOL_BONDS)) ) ){ - ret = 3; /* can't allocate MolBond structure */ - MOLFILE_ERR_FIN (ret, 3, err_fin, "Out of RAM"); - } - } - prev_ret = ret; - if ( !bEndOfData && 0 != ( ret = read_bonds_block(pCtab, inp, ret, pStrErr) ) ){ - if ( ret < 0 ) { - ret = -ret; - bEndOfData = 1; - } - ret = prev_ret? prev_ret : ret + 40; - } - prev_ret = ret; - if ( !bEndOfData && 0 != ( ret = read_stext_block(pCtab, inp, ret, pStrErr) ) ){ - ret = prev_ret? prev_ret : ret + 50; - } - prev_ret = ret; - if ( !bEndOfData && 0 != ( ret = read_properties_block(pCtab, pHdr, inp, ret, pStrErr) ) ){ - if ( ret < 0 ) { - ret = -ret; - bEndOfData = 1; - } - ret = prev_ret? prev_ret : ret + 60; - } - -err_fin: - *err = bEndOfData? -ret : ret; - if ( bReadAll ) { - if ( ret ) - mol_data = delete_mol_data( mol_data ); /* delete all results */ - return mol_data; - } else { - if ( ret ) - return NULL; - else - return (MOL_DATA*)OnlyHeaderBlock; - } -} - -/******************************************************************/ -static const char sdf_data_hdr_name[] = "NAME"; -static const char sdf_data_hdr_comm[] = "COMMENT"; -enum { SDF_START, SDF_DATA_HEADER, SDF_DATA_HEADER_NAME - , SDF_DATA_HEADER_COMMENT, SDF_DATA_HEADER_CAS - , SDF_DATA_HEADER_USER, SDF_DATA_LINE - , SDF_END_OF_DATA_ITEM, SDF_EMPTY_LINE, SDF_END_OF_DATA_BLOCK }; -/********** static ********************************************************/ -long extract_cas_rn( char *line ) -{ - int i, j; - i = line[0] == '-'? 1 : 0; - for ( j = i; line[i]; i ++ ) { - if ( isdigit( UCINT line[i] ) ) { - line[j++] = line[i]; - } else - if ( line[i] != '-' ) { - break; - } - } - line[j] = '\0'; - return strtol( line, NULL, 10 ); -} -/********** static ********************************************************/ -int identify_sdf_label( char* inp_line, const char *pSdfLabel ) -{ - char line[MOLFILEMAXLINELEN]; - char *p, *q; - int i, j, len; - if ( (p = strchr( inp_line, '<' )) && - (q = strchr( p, '>' )) && - (len = q-p-1) > 0 && len < (int)sizeof(line) ) { - memcpy( line, p+1, len ); - line[len] = '\0'; - for ( i = 0; isspace( UCINT line[i] ); i ++ ) - ; - for ( j = len-1; j >= i && isspace( UCINT line[i] ); j -- ) - ; - len = j-i+1; - p = line+i; - if ( pSdfLabel && pSdfLabel[0] && len == (int)strlen(pSdfLabel) && !memicmp( p, pSdfLabel, len ) ) - return SDF_DATA_HEADER_USER; - if ( len == sizeof(sdf_data_hdr_name)-1 && !memicmp( p, sdf_data_hdr_name, len ) ) - return SDF_DATA_HEADER_NAME; - if ( len == sizeof(sdf_data_hdr_comm)-1 && !memicmp( p, sdf_data_hdr_comm, len ) ) - return SDF_DATA_HEADER_COMMENT; - if ( !memicmp( p, "CAS", 3 ) ) - return SDF_DATA_HEADER_CAS; - } - return SDF_DATA_HEADER; -} -/************* global *****************************************************/ -int bypass_sdf_data_items( FILE* inp, long *cas_reg_no, char* comment, - int lcomment, char *name, int lname, int prev_err, - const char *pSdfLabel, char *pSdfValue, char *pStrErr ) -{ - char line[MOLFILEINPLINELEN]; - const int line_len = sizeof(line); - int err = 0; - int current_state = SDF_START; - int n_blank_lines = 0; - int n_lines = 0; - char* p = NULL; - int bNeedsName = name && lname > 0 && !name[0]; - int bNeedsComm = comment && lcomment > 0 && !comment[0]; - int bNeedsUser = pSdfLabel && pSdfLabel[0] && pSdfValue; - int bNeedsCASrn = 0; - int bCASrnIsUser = 0; - - if ( cas_reg_no != NULL ) { - bNeedsCASrn = 1; - *cas_reg_no = 0; - bCASrnIsUser = (bNeedsUser && !memicmp(pSdfLabel,"CAS", 3)); - } - - while ( err == 0 && - current_state !=SDF_END_OF_DATA_BLOCK && - NULL != ( p = inchi_fgetsLf( line, line_len, inp ) ) ) { - - if ( !n_lines && !memcmp(line, "M END", 6) ) { - continue; /* allow subtle errors */ - } - n_lines++; - - remove_trailing_spaces( line ); - if ( line[MOLFILEMAXLINELEN] ){ - if ( current_state != SDF_DATA_HEADER && - current_state != SDF_DATA_LINE && - current_state != SDF_DATA_HEADER_NAME && - current_state != SDF_DATA_HEADER_USER && - current_state != SDF_DATA_HEADER_COMMENT ) { - line[MOLFILEMAXLINELEN] = '\0'; - if ( !prev_err ) { - MOLFILE_ERR_SET (err, 0, "Too long SData line truncated"); - } - } else { - /* allow long lines in SDF data. 9-29-00 DCh */ - line[MOLFILEMAXLINELEN] = '\0'; - } - } - - n_blank_lines += ( *line == '\0' ); - - switch( current_state ) { - - case SDF_START: - case SDF_END_OF_DATA_ITEM: - case SDF_EMPTY_LINE: /* Added 9-25-97 DCh */ - - if ( 0 == strcmp( line, SDF_END_OF_DATA ) ) { - current_state = SDF_END_OF_DATA_BLOCK; - } - else - if ( '>' == *line ) { - current_state = ( bNeedsName || bNeedsComm || bNeedsCASrn || bNeedsUser )? identify_sdf_label(line, pSdfLabel) : SDF_DATA_HEADER; - }else - if ( *line == '\0' ) { /* Added 9-25-97 DCh */ - /* Relax the strictness: Allow more than 1 empty line. */ - current_state=SDF_EMPTY_LINE; - } else - if ( !prev_err ) { - MOLFILE_ERR_SET (err, 3, "Unexpected SData header line:"); - RemoveNonPrintable( line ); - AddMOLfileError(pStrErr, line); - /* unexpected contents of data header line */ - } else { - err = 3; - } - break; - - case SDF_DATA_HEADER_NAME: - if ( bNeedsName && 0 < normalize_name( line ) ) { - bNeedsName = 0; - mystrncpy( name, line, lname ); - } - goto got_data_line; - - case SDF_DATA_HEADER_COMMENT: - if ( bNeedsComm && 0 < normalize_name( line ) ) { - bNeedsComm = 0; - mystrncpy( comment, line, lcomment ); - } - goto got_data_line; - - case SDF_DATA_HEADER_USER: - if ( bNeedsUser && 0 < normalize_name( line ) ) { - bNeedsUser = 0; - mystrncpy( pSdfValue, line, MAX_SDF_VALUE+1 ); - if ( bCASrnIsUser && bNeedsCASrn ) { - *cas_reg_no = extract_cas_rn( line ); - bNeedsCASrn = (0 == *cas_reg_no); - } - } - goto got_data_line; - - case SDF_DATA_HEADER_CAS: - if ( bNeedsCASrn && 0 < normalize_name( line ) ) { - *cas_reg_no = extract_cas_rn( line ); - bNeedsCASrn = (0 == *cas_reg_no); - } - goto got_data_line; - - case SDF_DATA_HEADER: - case SDF_DATA_LINE: -got_data_line: - current_state = *line? SDF_DATA_LINE : SDF_END_OF_DATA_ITEM; - break; - - } - } - if ( 0 == err && SDF_END_OF_DATA_BLOCK != current_state && NULL == p ) - ; /* err = 4; */ /* unexpected end of file: missing $$$$ */ - else - if (err && ( n_blank_lines == n_lines && *line == '\0' ) ) - err = 5; /* empty lines -- do not know when this can happen */ - - if ( err && err != 5 && current_state != SDF_END_OF_DATA_BLOCK && p ) { - /* bypass up to $$$$ */ - while ( ( p = inchi_fgetsLf( line, line_len, inp ) ) && memcmp( line, SDF_END_OF_DATA, 4 ) ) - ; - if ( p ) { - err = 9; /* bypassed to $$$$; non-fatal */ - AddMOLfileError(pStrErr, "Bypassing to next structure"); - } - - } - - return err; -} -/**************** global **************************************************/ -MOL_DATA* read_sdfile_segment(FILE* inp, MOL_HEADER_BLOCK *OnlyHeaderBlock, MOL_CTAB *OnlyCtab, - int bGetOrigCoord, - char *pname, int lname, - long *Id, const char *pSdfLabel, char *pSdfValue, - int *err, char *pStrErr ) -{ - MOL_DATA* mol_data = read_mol_file( inp, OnlyHeaderBlock, OnlyCtab, bGetOrigCoord, err, pStrErr ); - int err_bypass_sdf = 0; - - if ( pname && lname ) { - pname[0] = '\0'; - } - if ( Id ) { - *Id = 0L; /* ignore for now */ - } - /* if ( mol_data && !*err ) { */ - if ( *err < 0 ) { - *err = -*err; /* end of data encountered */ - } else { - err_bypass_sdf = bypass_sdf_data_items( inp, Id, NULL, 0, pname, lname, *err, pSdfLabel, pSdfValue, pStrErr ); - if ( err_bypass_sdf ) { - *err = err_bypass_sdf; /* important to continue to the next good structure */ - } - } - /* } */ - return mol_data; -} -/******************* global *********************************************************/ -int CopyMOLfile(FILE *inp_file, long fPtrStart, long fPtrEnd, FILE *prb_file, long lNumb) -{ - char line[MOLFILEINPLINELEN], *p; - long fPtr; - int ret = 1; - char szNumber[32]; - - if ( inp_file && prb_file && fPtrStart >= 0L && - fPtrEnd > fPtrStart && - 0 == fseek( inp_file, fPtrStart, SEEK_SET ) ) { - - while ( fPtrEnd > (fPtr = ftell(inp_file)) && fPtr >= 0L && - inchi_fgetsLf( line, sizeof(line)-1, inp_file ) ) { - line[sizeof(line)-1] = '\0'; /* unnecessary extra precaution */ - if ( fPtr == fPtrStart && lNumb ) { - int len; - LtrimRtrim( line, &len ); - len = sprintf( szNumber, "#%ld%s", lNumb, len?"/":"" ); - mystrncpy( line+len, line, sizeof(line)-len-1 ); - memcpy( line, szNumber, len ); - } - if ( !strchr(line, '\n') ) { - p = line+strlen(line); - p[0] = '\n'; - p[1] = '\0'; - } - fputs( line, prb_file ); - } - ret = fseek( inp_file, fPtrEnd, SEEK_SET ); - } - return ret; -} diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/mode.h b/INCHI-1-SRC/INCHI_API/inchi_dll/mode.h deleted file mode 100644 index 826c8b1..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/mode.h +++ /dev/null @@ -1,1069 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __MODE_H__ -#define __MODE_H__ - -#include - - - - -/*******************/ -/* */ -/* BUILD TARGETS */ -/* */ -/*******************/ - -/* Valid targets are: - -TARGET_EXE_STANDALONE - Stand-alone executable inchi-1[.exe] -TARGET_API_LIB - Library (libinchi) for using InChI API described in inchi_api.h -TARGET_EXE_USING_API - Executable (INCHI_MAIN) which uses API library (e.g., libinchi.dll) -TARGET_LIB_FOR_WINCHI - library for wInChI - - Select and uncomment one from the list below. */ - - -/* #define TARGET_EXE_STANDALONE 1 */ -#define TARGET_API_LIB -/* #define TARGET_EXE_USING_API */ -/* #define TARGET_LIB_FOR_WINCHI 1 */ - - -/****************************/ -/* */ -/* BUILD OPTIONS/FEATURES */ -/* */ -/****************************/ - -/* Possible options are: - -BUILD_LINK_AS_DLL - Link library as a Win32 DLL or to eliminate stricmp duplication - (use with TARGET_API_LIB or TARGET_EXE_USING_API) - -BUILD_WITH_ENG_OPTIONS - Expose engineering options - -BUILD_WITH_AMI - Turns on AMI (Allow Multiple Inputs) mode for standalone executable - - Select and uncomment whichever are necessary from the list below. */ - - -/* #define BUILD_LINK_AS_DLL */ - -/* #define BUILD_WITH_ENG_OPTIONS 1 */ - -#ifndef BUILD_WITH_AMI -/* this allows BUILD_WITH_AMI be #defined in a makefile */ -/* #define BUILD_WITH_AMI 1 */ -#endif -/* NB: AMI mode is only for stand-alone executable */ -#ifndef TARGET_EXE_STANDALONE -#ifdef BUILD_WITH_AMI -#undef BUILD_WITH_AMI -#endif -#endif - - - -/* CML input is not supported started from v. 1.04 */ -/* set ADD_CMLPPP to zero to override possble makefile define */ -#define ADD_CMLPP 0 -#if 0 /* obsolete */ -#ifndef ADD_CMLPP -/* this allows ADD_CMLPP be #defined in a makefile */ -#define ADD_CMLPP 1 -#endif -#if ( ADD_CMLPP == 1 ) -#ifdef USE_CMLPPDLL -/* 1200 is VC++ 6.0 version, 1300 is VC++ .NET; USE_CMLPPDLL may be #defined in a makefile*/ -#if ( defined(_WIN32) && defined(_MSC_VER) && _MSC_VER >= 1200 ) -#define MSC_DELAY_LOAD_CMLPPDLL -#endif -#endif -#endif -#endif - - - - -/*****************************/ -/* */ -/* COMPILE OPTIONS/FEATURES */ -/* */ -/*****************************/ - -/* Possible options are: - -COMPILE_ANSI_ONLY - Unconditionally force ANSI-89 C, no Win32 specific code - -COMPILE_ADD_NON_ANSI_FUNCTIONS - Use with COMPILE_ANSI_ONLY to add stricmp(), etc., see util.c - -COMPILE_ALL_CPP - allow C++ compilation/linkage of functions prototyped in .h files - -MS VC compiler pragmas - - Select and uncomment whichever are necessary from the list below. */ - - -/* #define COMPILE_ANSI_ONLY */ -#if ( !defined(_MSC_VER) || defined(TARGET_API_LIB)) /* non-Microsoft GNU C, BCC, etc. compilers */ -#ifndef COMPILE_ANSI_ONLY -#define COMPILE_ANSI_ONLY -#endif -#endif -#ifdef COMPILE_ANSI_ONLY -/*#define COMPILE_ADD_NON_ANSI_FUNCTIONS */ -#endif - - -/* #define COMPILE_ALL_CPP */ - -#ifdef _MSC_VER -/* -========== disable MS VC++ 6.0 Level 4 compiler warnings: ============== - C4706: assignment within conditional expression - C4127: conditional expression is constant - C4244: '=' : conversion from 'int ' to '???', possible loss of data - C4267: '=' : conversion from 'size_t' to 'int', possible loss of data - C4701: local variable '???' may be used without having been initialized (removed) - C4514: unreferenced inline/local function has been removed (C++) - C4100: 'identifier' : unreferenced formal parameter - C4786: 'identifier' : identifier was truncated to 'number' characters in the debug information - C4996: 'identifier' was declared deprecated -======================================================================== -*/ - #pragma warning( disable : 4706 4127 4514 4100 4786 4996 4244 4267 ) -#endif - - - -/* TARGET_ID_STRING */ -#define TARGET_ID_STRING ", Software version 1.04 (API Library) Build of September 9, 2011" - - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - - - -/*********************/ -/* */ -/* INCHI ALGORITHM */ -/* */ -/*********************/ - -#define INCHI_VERSION "1" - -#if 0 /* obsolete */ -/*#define INCHI_VERSION "0.9Beta" */ -/*#define INCHI_VERSION "0.91Beta" */ /* 10-10-2002: sent to Jonathan Goodman */ -/*#define INCHI_VERSION "0.92Beta" */ /* 11-15-2002: added Hill notation; sent to S.Heller & S.Stein */ -/*#define INCHI_VERSION "0.93Beta" */ /* 12-09-2002: Fixed isotopic canon. bug & chiralanes; sent to S.Heller & A. McNaught */ -/*#define INCHI_VERSION "0.931Beta" */ /* Non-BNS without salts released to PMR 01-2003 */ -/*#define INCHI_VERSION "0.932Beta" */ /* Released to CAS 04-01-2003: - * - Improved taut. definitions as compared to 01-2003; - * - fixed bug: non-isotopic components' stereo missing from isotopic stereo - * - fixed bug: couldn't properly read Unix files (EOL = LF instead of CR/LF) - * (effective only for MS VC++, Borland and MinGW/GCC compiles that accept "rb" mode of fopen) - * DJGPP/GCC does not seem to need this fix. - */ -/*==== Release version ===*/ -/*#define INCHI_VERSION "0.94Beta" */ /* 02-27-2003: Balanced network search to find alt paths and non-stereo bonds; - Implemented salts disconnection; added (-) to taut groups */ -/*#define INCHI_VERSION "1.12Beta" */ /* 1.12: 07-06-2004: sort order: No H formula,..; Pointed end stereo ON, Aggressive (de)protonation OFF */ - /* 1.11: 05-19-2004: annotated plain text output, fixed bugs */ - /* 1.1: 04-08-2004: variable protonation version */ - /* 1.01: 12-23-2003 protected bonds, isotopic canonicalization in GetBaseCanonRanking() */ - /* 1.02: 01-26-2004 fixed new isotopic tgroup canon bug, molfile merge bug */ - -/*#define INCHI_VERSION "1.0RC"*/ /* 02-07-2005 v1.0 Release Candidate */ -#endif - -#define INCHI_NAME "InChI" -#if 0 /* obsolete */ -#define INCHI_REC_NAME "ReChI" -#endif - -#define INCHI_NAM_VER_DELIM "=" - -#ifdef _WIN32 -#define INCHI_OPTION_PREFX '/' -#define INCHI_PATH_DELIM '\\' -#else -#define INCHI_OPTION_PREFX '-' -#define INCHI_PATH_DELIM '/' -#endif - -#define INCHI_ALT_OPT_PREFIX '-' -#define INCHI_ACD_LABS_PREFIX '-' - -#define bRELEASE_VERSION 1 /* 1=> release version; comment out to disable */ -#ifndef bRELEASE_VERSION -#define bRELEASE_VERSION 0 /* 0=> debug version */ -#endif - - -/* display (non-canonical) c-groups, display orig at numbers */ -#if ( bRELEASE_VERSION == 1 ) -#define DISPLAY_DEBUG_DATA_C_POINT 0 /* disabled release version for now */ -#define DISPLAY_ORIG_AT_NUMBERS 1 /* 1 => in an uncanonicalized components display orig. atom numbers (default) */ -#else -#define DISPLAY_DEBUG_DATA_C_POINT 1 /* debug: 1=>display (non-canonically numbered) c-groups, 0=>do not display */ -#define DISPLAY_ORIG_AT_NUMBERS 1 /* 0 => in an uncanonicalized components display ordering atom numbers (debug) */ -#endif - -#if ( DISPLAY_DEBUG_DATA_C_POINT > 0 ) -#define DISPLAY_DEBUG_DATA DISPLAY_DEBUG_DATA_C_POINT -#endif - - - -/* BUG FIXES */ - -/**************************/ -/* bug fixes in v1.00 */ -/**************************/ -#define FIX_ChCh_STEREO_CANON_BUG 1 /* 1=> (NEEDED) */ -#define ADD_ChCh_STEREO_CANON_CHK 0 /* 1 is NOT needed; let it always be 0 */ -#define FIX_ChCh_CONSTIT_CANON_BUG 1 /* 1=> (NEEDED) */ -#define FIX_EITHER_STEREO_IN_AUX_INFO 1 /* 1=> fix bug: Either stereobond direction in Aux_Info; 0=> do not fix */ -#define FIX_NORM_BUG_ADD_ION_PAIR 1 /* 1=> (NEEDED) fix bug: Miscount number of charges when creating an ion pair */ -#define FIX_REM_PROTON_COUNT_BUG 1 /* 1=> (NEEDED) check for number of actually removed protons and issue an error if mismatch */ -#define FIX_READ_AUX_MEM_LEAK 1 -#define FIX_READ_LONG_LINE_BUG 1 /* 1=> (NEEDED) prevent failure when reading AuxInfo and InChI is too long */ -#define FIX_N_V_METAL_BONDS_GPF 1 /* 1=> (NEEDED) InChI v1 GPF bug fix */ -#define BNS_RAD_SEARCH 1 /* 1=> prevent normalization failures due to radical centers */ - -/*******************************/ -/* bug fixes in post-v1.00 */ -/*******************************/ -#define FIX_ODD_THINGS_REM_Plus_BUG 0 -#define FIX_N_MINUS_NORN_BUG 0 -#define FIX_CANCEL_CHARGE_COUNT_BUG 0 -#define FIX_2D_STEREO_BORDER_CASE 0 -#define FIX_REM_ION_PAIRS_Si_BUG 0 -#define FIX_STEREO_SCALING_BUG 0 -#define FIX_EMPTY_LAYER_BUG 0 -#define FIX_EITHER_DB_AS_NONSTEREO 0 -#define FIX_BOND23_IN_TAUT 0 -#define FIX_TACN_POSSIBLE_BUG 0 -#define FIX_KEEP_H_ON_NH_ANION 0 -#define FIX_AVOID_ADP 0 -/* may change InChI */ -#define FIX_NUM_TG 0 /* increase number of t-groups for isothiocyanate */ -/* changes InChI for isothiocyanate */ -#define FIX_CPOINT_BOND_CAP2 0 - -/*******************************/ -/* bug fixes in post-v1.02b */ -/*******************************/ - -#define FIX_ISO_FIXEDH_BUG 1 /* (2007-09-24) 1=> Fix bug: missing fixed-H iso segment in case of single removed D(+) */ -#define FIX_ISO_FIXEDH_BUG_READ 0 /* (2007-09-24) 1=> Accommodate this InChI bug in reading InChI */ -#define FIX_DALKE_BUGS 1 -#define FIX_TRANSPOSITION_CHARGE_BUG 1 /* (2008-01-02) fix bug that leads to missed charge in some cases when /o is present */ -#define FIX_I2I_STEREOCONVERSION_BUG 1 /* (2008-03-06) 1=> Fix bug of i2i conversion SAbs-->(SRel||Srac) */ -#define FIX_I2I_STEREOCONVERSION_BUG2 1 /* (2008-04-02) 1=> Fix bug of i2i conversion (missed empty /t) */ -#define FIX_I2I_STEREOCONVERSION_BUG3 1 /* (2008-04-10) 1=> Fix bug of i2i conversion */ - /* (missed repeating /s in FI after F for multi-component case) */ -#define FIX_TERM_H_CHRG_BUG 1 /* (2008-06-06) IPl) */ - /* fix bug: in some cases (dependent on ordering - numbers), moving a charge from terminal H to heavy - atom resulted in neutralizing H but not adjusting - charge of heavy atom */ - - -#define FIX_AROM_RADICAL 1 /* (2011-05-09) 1=> Fix bug which leads for different InChI */ - /* on atomic permitations for systems containing radical at */ - /* atom in aromatic ring */ - - -#if ( !defined(TARGET_API_LIB) && !defined(TARGET_EXE_USING_API) ) -#define I2S_MODIFY_OUTPUT 1 /* 1=> Allow various InChI2InChI output types from cInChI */ -#else -#define I2S_MODIFY_OUTPUT 0 /* 0=> Always */ -#endif - - -#define FIX_NP_MINUS_BUG 1 /* 2010-03-11 DCh */ - -/**************************/ -/* additions to v1.00 */ -/**************************/ -#define FIX_ADJ_RAD 0 - -#define SDF_OUTPUT_V2000 1 /* 1=>always output V2000 SDfile, 0=>only if needed */ -#define SDF_OUTPUT_DT 1 /* 1=> all option -SdfAtomsDT to output D and T into SDfile */ -#define CHECK_AROMBOND2ALT 1 /* 1=> check whether arom->alt bond conversion succeeded */ - -#ifdef TARGET_LIB_FOR_WINCHI -#define READ_INCHI_STRING 0 /* 1=> input InChI string and process it */ -#else -#define READ_INCHI_STRING 1 /* 1=> input InChI string and process it */ -#endif - -/****************************************************/ -/* disabled extra external calls to InChI algorithm */ -/****************************************************/ -#define INCLUDE_NORMALIZATION_ENTRY_POINT 0 - -/**************************/ -/* Normalization settings */ -/**************************/ - -/* post version 1 features */ -#define KETO_ENOL_TAUT 1 /* include keto-enol tautomerism */ -#define TAUT_15_NON_RING 1 /* 1,5 tautomerism with endpoints not in ring */ - -/* v.1.04 : still experimental but may be exposed (set to 1) */ -#define UNDERIVATIZE 0 /* split to possible underivatized fragments */ -#define RING2CHAIN 0 /* open rings R-C(-OH)-O-R => R-C(=O) OH-R */ - -/* post-2004-04-27 features */ -#define HAL_ACID_H_XCHG 1 /* allow iso H exchange to HX (X=halogen) and H2Y (Y=halcogen) */ -#define CANON_FIXH_TRANS 1 /* produce canonical fixed-H transposition */ -#define STEREO_WEDGE_ONLY 1 /* 1=> only pointed ends stereo bonds define stereo; 0=> both ends */ - -/* current new (with respect to v1.12 Beta) preprocessing */ -#define REMOVE_ION_PAIRS_EARLY 1 /* 1=> new preprocessing: step 1 before disconnecting metals in fix_odd_things() */ -#define REMOVE_ION_PAIRS_DISC_STRU 1 /* 1=> new post-preprocessing: remove charhes after metal disconnection */ -#define REMOVE_ION_PAIRS_FIX_BONDS 1 /* 1=> step2: set unchangeable bonds around removed ion pairs */ -#define S_VI_O_PLUS_METAL_FIX_BOND 1 /* 1=> count double bond M-O(+)=S as O=S in S(VI) ans S(VIII) fixing bonds */ -#define N_V_STEREOBONDS 1 /* 1=> detect stereobonds incident to N(V); 0 => don't */ -/* for testing */ -#define REMOVE_ION_PAIRS_ORIG_STRU 0 /* 0=> normal mode (default) - * 1=> testing mode only: remove ion pairs from the original structure - * to save the changes in the output Molfile (/OutputSDF) or AuxInfo - * NIP=No Ion Pairs - */ -/* salts treatment */ -#define DISCONNECT_SALTS 1 /* 1=>disconnect metal atoms from salts, 0=>dont */ -#define TEST_REMOVE_S_ATOMS 1 /* 1=>default: after merging into one group test & - * remove unreachable, - * 0=> old version: test only before merging into one t-group */ -#define CHARGED_SALTS_ONLY 1 /* 1=>(default)do not test far salts tautomerism if - * no negative charge(s) present */ -#define BNS_PROTECT_FROM_TAUT 1 /* 1=> do not allow testing of bonds to acetyl or nitro */ -#define BNS_MARK_EDGE_2_DISCONNECT 1 /* 1=> mark edge as temp forbidden instead of disconnection */ - -#define REPLACE_ALT_WITH_TAUT 1 /* 1 => replace alt bonds with tautomeric bonds in case of standard t-groups */ -#define MOVE_CHARGES 1 /* 1 => take moveable charges into account */ -#define NEUTRALIZE_ENDPOINTS 1 /* 1 => before checking whether an H is moveable make 2 endpoints neutral */ - /* implemented only if CHECK_TG_ALT_PATH = 0, defined in ichi_bns.c */ -#define FIX_H_CHECKING_TAUT 1 /* 1 => Fix moveable H or (-) before checking if taut. exchange is possible */ -#define ALWAYS_ADD_TG_ON_THE_FLY 1 /* 1 => disables radical calcellation by taut-charge movement */ -#define IGNORE_SINGLE_ENDPOINTS 1 /* 1 => see FindAccessibleEndPoints() in INChITaut.c */ - -/* recently added -- begin */ -#define INCL_NON_SALT_CANDIDATATES 1 /* 1=> allow H and (-) migrate between "acidic" O and - * other possible endpoints */ -#define SALT_WITH_PROTONS 1 /* 1=> (new new) include proton migrarion C-SH, =C-OH, NH+ */ -#define OPPOSITE_CHARGE_IN_CGROUP 1 /* 1=> allow N(-) in (+) c-group, 0=> disallow */ -#define MOVE_PPLUS_TO_REMOVE_PROTONS 0 /* 0=> default; 1=> (disabled) add P/P+ charge group during - * 'hard' proton removal */ -#define ADD_MOVEABLE_O_PLUS 1 /* 1=> allow charges on O(+) to move */ -/* recently added -- end */ - -#define DISCONNECT_METALS 1 /* make main layer disconnected */ -#define RECONNECT_METALS 0 /* 1=> by default add reconnected layer in case of coord. - * compound disconnection */ -#define CHECK_METAL_VALENCE 0 /* 1=> disconnect only metals that have abnormal valence */ -#define bREUSE_INCHI 1 /* 1=> do not recalulate INChI for components in reconnected - * structure that are same as in the connected one */ -#define OUTPUT_CONNECTED_METAL_ONLY 0 /* 0=> default; 1 => (debug) create only reconnected or - * initial struct. output */ -#define EMBED_REC_METALS_INCHI 1 /* 1=> (default) output Reconnected embedded in Disconnected INChI; - * 0=> separate output */ - -#define bOUTPUT_ONE_STRUCT_TIME 1 /* 1 => output each structure time (non-release only) */ - - - -/* constants and array sizes */ - -#define INCHI_NUM 2 /* = array size; member indexes: */ -#define INCHI_BAS 0 /* 0 => disconnected or normal */ -#define INCHI_REC 1 /* 1 => reconnected */ - -#define TAUT_NUM 2 /* = array size; member indexes: */ -#define TAUT_NON 0 /* 0 => normal structure */ -#define TAUT_YES 1 /* 1 => tautomeric */ -#define TAUT_INI 2 /* 2 => intermediate tautomeric structure */ -#define ALT_TAUT(X) ((X)>TAUT_YES? TAUT_YES : 1-(X)) /* was (1-(X)) */ - -/* INChI output modes */ -#define OUT_N1 0 /* non-tautomeric only */ -#define OUT_T1 1 /* tautomeric if present otherwise non-tautomeric */ -#define OUT_NT 2 /* only non-taut representations of tautomeric */ -#define OUT_TN 3 /* tautomeric if present otherwise non-tautomeric; - separately output non-taut representations of tautomeric if present */ -#define OUT_NN 4 /* only non-taut representations: non-taut else tautomeric */ - -/* OUT_TN = OUT_T1 + OUT_NT */ - -/* torture test */ - -#define TEST_RENUMB_ATOMS 0 /* 1 => heavy duty test by multiple renumbering of atoms */ -#define TEST_RENUMB_NEIGH 1 /* 1 => randomly permutate neighbors */ -#define TEST_RENUMB_SWITCH 0 /* 1 => display & output another (different) picture */ -#define TEST_RENUMB_ATOMS_SAVE_LONGEST 0 /* 1 => save the component with largest processing time into the problem file */ - - -/* stereo */ - -#define NEW_STEREOCENTER_CHECK 1 /* 1 => add new stereocenter categories (see bCanInpAtomBeAStereoCenter(...)) */ -#define MIN_SB_RING_SIZE 8 /* do not assume stereo bonds in rings containing 3..MIN_SB_RING_SIZE-1 atoms */ - -#define REMOVE_KNOWN_NONSTEREO 1 /* 1=> check in advance known stereo to remove parities from non-stereogenic elements */ -#define REMOVE_CALC_NONSTEREO 1 /* 1=> check new stereo numberings to remove parities from non-stereogenic elements */ -#define PROPAGATE_ILL_DEF_STEREO 1 /* 1=> if at least one of the pair of constitutionally identical (far) neighbors */ - /* (of the tested atom) has ill-defined stereo parity and another has any */ - /* stereo parity then set the parity of the tested atom to ill-defined value. */ - -#define ONLY_DOUBLE_BOND_STEREO 0 /* 1=> no alt bond stereo, no taut. bond attachment to stereo bond */ - /* 0=> allow other definitions (below) to be active */ -#define ONE_BAD_SB_NEIGHBOR 1 /* 1 => allow 1 "bad" bond type neighbor to a stereobond atom. 2004-06-02 */ - -/* more stereo settings */ -#define BREAK_ONE_MORE_SC_TIE 1 /* break one more tie when comparing possible stereocenter neighbors */ -#define BREAK_ALSO_NEIGH_TIE 0 /* post 1.12Beta 2004-08-20: if fixed neighbor has equ neighbors, fix the one with smaller canon. rank */ -#define BREAK_ALSO_NEIGH_TIE_ROTATE 1 /* post 1.12Beta 2004-09-02: break the second in 2nd psition; 1 works, 0 does not (example:MFCD01085607) */ - -#define STEREO_CENTER_BONDS_NORM 1 /* set length of the bonds around a stereocenter = 1 before getting the parity */ -#define STEREO_CENTER_BOND4_NORM 0 /* set length of the added bond around a stereocenter = 1 before getting the parity */ -#define NORMALIZE_INP_COORD 0 /* 0=>keep unchanged, 1 => make atom coordinates integer values, avg bond len=20 */ - -/* recent stereo */ -#define STEREO_WEDGE_ONLY 1 /* 1=> only pointed ends stereo bonds define stereo; 0=> both ends 1.12Beta */ -#define CHECK_C2v_S4_SYMM 0 /* post-1.12Beta 1=> check if a stereocenter has C2v or S4 symmetry; 0=>old mode */ - -#define EQL_H_NUM_TOGETHER 1 /* 1=> output 1-3,5H2 intead of 1-3H2,5H2 (CT_MODE_EQL_H_TOGETHER) */ -#define ABC_CT_NUM_CLOSURES 1 /* 1=> in coinnections compressed format output decimal number of closures instead of '-' */ - -/* temporary fix */ -#define SINGLET_IS_TRIPLET 1 /* 'singlet' means two electrons make a lone pair instead of 2 bonds - its effect on valence is same as the effect of a triplet */ - -/* defug: find structures where canonical partition is different from equitable */ -#define FIND_CANON_NE_EQUITABLE 0 /* 0=>normal mode */ - /* 1=> extract (set EXTR_FLAGS = (EXTR_CANON_NE_EQUITABLE)*/ - /* set cmd line options: /onlynonTAUT /: /UNCHARGEDACIDS:1 /DISCONSALT:0 /MOVEPOS:0 /DISCONMETAL:0 */ - -/* Debug: definitions for the extraction of the structures to the problem file */ - -/* definition of the flags for structure extraction to the - problem file (for debugging and non-standard searching) */ -#define EXTR_KNOWN_USED_TO_REMOVE_PARITY 0x000001 -#define EXTR_CALC_USED_TO_REMOVE_PARITY 0x000002 -#define EXTR_2EQL2CENTER_TO_REMOVE_PARITY 0x000004 -#define EXTR_HAS_ATOM_WITH_DEFINED_PARITY 0x000008 -#define EXTR_REMOVE_PARITY_WARNING 0x000010 -#define EXTR_SALT_WAS_DISCONNECTED 0x000020 -#define EXTR_SALT_PROTON_MOVED 0x000040 -#define EXTR_SALT_PROTON_MOVE_ERR_WARN 0x000080 -#define EXTR_METAL_WAS_DISCONNECTED 0x000100 -#define EXTR_METAL_WAS_NOT_DISCONNECTED 0x000200 -#define EXTR_NON_TRIVIAL_STEREO 0x000400 /* (Inv != Abs stereo) && (parities can't be obtained by inverting them) */ -#define EXTR_UNUSUAL_VALENCES 0x000800 -#define EXTR_HAS_METAL_ATOM 0x001000 -#define EXTR_TEST_TAUT3_SALTS_DONE 0x002000 /* non-oxygen t-points used to discover tautomerism of merged t-groups */ -#define EXTR_CANON_NE_EQUITABLE 0x004000 /* find structures where canonical partition is different from equitable */ -#define EXTR_HAS_PROTON_PN 0x008000 /* has movable H+ attached to N or P */ -#define EXTR_HAS_FEATURE 0x010000 /* found a feature */ -#define EXTR_TAUT_TREATMENT_CHARGES 0x020000 /* tautomeric treatment of charges */ -#define EXTR_TRANSPOSITION_EXAMPLES 0x040000 /* extract structures that have different mobile-H and fixed-H orders */ - -/* define conditions of structure extraction to the problem file */ -#define EXTR_MASK 0 /*EXTR_TAUT_TREATMENT_CHARGES*/ /*(EXTR_HAS_FEATURE)*/ /*(EXTR_UNUSUAL_VALENCES | EXTR_HAS_METAL_ATOM)*/ /* 0 to disable */ -#define EXTR_FLAGS 0 /*EXTR_TAUT_TREATMENT_CHARGES*/ /*(EXTR_HAS_FEATURE)*/ /*(EXTR_HAS_PROTON_PN)*/ /*(EXTR_UNUSUAL_VALENCES)*/ /*(EXTR_CANON_NE_EQUITABLE)*/ /*(EXTR_TEST_TAUT3_SALTS_DONE)*/ /*(EXTR_HAS_METAL_ATOM)*/ /* (EXTR_NON_TRIVIAL_STEREO)*/ /*(EXTR_METAL_WAS_DISCONNECTED)*/ /* (EXTR_REMOVE_PARITY_WARNING)*/ /*(EXTR_HAS_ATOM_WITH_DEFINED_PARITY) */ - - -#define ENTITY_REFS_IN_XML_MESSAGES 1 /* 1=> replace ' " < > & in error/warning messages with xml entity references */ - -/* added tautomeric structures */ - -#define TAUT_TROPOLONE_7 1 /* 1=> tautomeric 7-member rings ON */ -#define TAUT_TROPOLONE_5 1 /* 1=> taut. similar to tropolone, 5-member ring */ -#define TAUT_4PYRIDINOL_RINGS 1 /* 1=> OH-C5H4N rings tautomerism */ -#define TAUT_PYRAZOLE_RINGS 1 /* 1=> tautomerizm in pyrazole rings */ -/* limitation on tautomerism detection: */ -#define TAUT_IGNORE_EQL_ENDPOINTS 0 /* 0=> even though 2 endpoints belong to same t-group check - them to find more alt bonds (new) - 1=> ignore and do not check (old mode) */ -#define TAUT_RINGS_ATTACH_CHAIN 1 /* 1=> allow only chain attachments to tautomeric endpoints */ - /* (except pyrazole, where is no tautomeric attachment) */ - /* 0=> allow taut. attachments from same ring system. Default=1 */ - -#define FIND_RING_SYSTEMS 1 /* 1 => find and mark ring systems, blocks, cut-vertices */ - /* Needed for 5- and 6-member ring tautomers and in other places */ - -#define FIND_RINS_SYSTEMS_DISTANCES 0 /* 1 => find ring system and atom distance from terminal */ -#define USE_DISTANCES_FOR_RANKING 0 /* 1 => rank ring systems according to distances from terminal */ - -#define DISPLAY_RING_SYSTEMS 0 /* 1 => for debug only; displays: */ - /* "block no"/"ring system no"/"cut-vertex (num. intersecting blocks-1)" */ - /* instead of ranks */ -/* consistency */ - -#if ( bRELEASE_VERSION==1 && bOUTPUT_ONE_STRUCT_TIME==1) -#undef bOUTPUT_ONE_STRUCT_TIME -#define bOUTPUT_ONE_STRUCT_TIME 0 -#endif - -/* consistency: bRELEASE_VERSION==1 needs FIND_RING_SYSTEMS=1 */ -#if ( bRELEASE_VERSION==1 && FIND_RING_SYSTEMS!=1 ) -#ifdef FIND_RING_SYSTEMS -#undef FIND_RING_SYSTEMS -#endif -#define FIND_RING_SYSTEMS 1 -#endif - -/* consistency: FIND_RINS_SYSTEMS_DISTANCES needs FIND_RING_SYSTEMS */ -#if ( FIND_RING_SYSTEMS != 1 ) - -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) -#undef FIND_RINS_SYSTEMS_DISTANCES -#define FIND_RINS_SYSTEMS_DISTANCES 0 -#endif - -#endif - -/* consistency: USE_DISTANCES_FOR_RANKING and DISPLAY_RING_SYSTEMS need FIND_RINS_SYSTEMS_DISTANCES */ -#if ( FIND_RINS_SYSTEMS_DISTANCES != 1 ) - -#if ( USE_DISTANCES_FOR_RANKING == 1 ) -#undef USE_DISTANCES_FOR_RANKING -#define USE_DISTANCES_FOR_RANKING 0 -#endif - -#if ( DISPLAY_RING_SYSTEMS == 1 ) -#undef DISPLAY_RING_SYSTEMS -#define DISPLAY_RING_SYSTEMS 0 -#endif - -#endif - - -#if ( FIND_RING_SYSTEMS==1 && (TAUT_TROPOLONE_7==1 || TAUT_TROPOLONE_5==1 || TAUT_4PYRIDINOL_RINGS==1 || TAUT_PYRAZOLE_RINGS) ) -#define TAUT_OTHER 1 -#else -#define TAUT_OTHER 0 -#endif - -#define APPLY_IMPLICIT_H_DOWN_RULE 0 /* 1=> if 3 non-H atoms around stereocenter are in same plane */ - /* then add "down" hydrogen to obtain sterecenter oparity */ - /* 0=> Implicit H stereo is unknown if all bonds to 3 non-H atoms */ - /* are in XY plane */ -#define ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS 1 /* 1=> consider bond in an alternating circuit stereogenic */ - /* even though it has adjacent tautomeric atom(s) */ - -#define IGNORE_TGROUP_WITHOUT_H 1 /* ignore tautomeric groups containing charges only */ - -#if ( DISCONNECT_SALTS == 1 ) -#define REMOVE_TGROUP_CHARGE 0 /* 0: do not remove charge information from tautomeric groups */ -#else -#define REMOVE_TGROUP_CHARGE 1 /* 1: remove charge information from tautomeric groups */ -#endif - -#if ( REMOVE_TGROUP_CHARGE == 1 ) -#define INCHI_T_NUM_MOVABLE 1 -#else -#define INCHI_T_NUM_MOVABLE 2 -#endif - -/******************************************/ -/* define canonicalization modes here */ -/******************************************/ - -#define USE_AUX_RANKING 1 /* 1=> get auxiliary ranking to accelerate canonicalization of H layers */ -#define USE_AUX_RANKING_ALL 1 /* 1=> include all vertices in CellGetMinNode() selection 0=> only vertices with highest ranks */ - -#define USE_ISO_SORT_KEY_HFIXED 0 /* 0=> normal mode: merge isotopic taut H to isotopic atom sorting key in - taut H-fixed canonicalization; - 1=> add one more "string" iso_sort_Hfixed to the canonicalization */ - -/************************ - questionable behavior - ************************/ -#define REL_RAC_STEREO_IGN_1_SC 0 /* 1=> drop from InChI sp3 stereo in components that have a single stereocenter */ - /* 0=> old-old mode (all such sp3 stereo is in the Identifier) */ -/* internal definitions; see also REQ_MODE_BASIC etc in ichi.h */ -#define CMODE_CT 0x000001 -#define CMODE_ISO 0x000002 -#define CMODE_ISO_OUT 0x000004 /* obsolete ? */ -#define CMODE_STEREO 0x000008 -#define CMODE_ISO_STEREO 0x000010 -#define CMODE_TAUT 0x000020 -#define CMODE_NOEQ_STEREO 0x000040 /* 5-24-2002: do not use stereo equivalence to accelerate */ -#define CMODE_REDNDNT_STEREO 0x000080 /* 6-11-2002: do not check for redundant stereo elements */ -#define CMODE_NO_ALT_SBONDS 0x000100 /* 6-14-2002: do not assign stereo to alternating bonds */ -/* new 10-10-2003 */ -#define CMODE_RELATIVE_STEREO 0x000200 /* REL All Relative Stereo */ -#define CMODE_RACEMIC_STEREO 0x000400 /* RAC All Racemic Stereo */ -#define CMODE_SC_IGN_ALL_UU 0x000800 /* IAUSC Ignore stereocenters if All Undef/Unknown */ -#define CMODE_SB_IGN_ALL_UU 0x001000 /* IAUSC Ignore stereobonds if All Undef/Unknown */ -/* end of 10-10-2003 */ - -/* external definitions */ -#define CANON_MODE_CT (CMODE_CT) -#define CANON_MODE_TAUT (CMODE_CT|CMODE_TAUT) -#define CANON_MODE_ISO (CMODE_CT|CMODE_ISO|CMODE_ISO_OUT) -#define CANON_MODE_STEREO (CMODE_CT|CMODE_STEREO) -#define CANON_MODE_ISO_STEREO (CMODE_CT|CMODE_ISO|CMODE_ISO_OUT|CMODE_ISO_STEREO) - -#define CANON_MODE_MASK 0x00FF /* used to determine canonicalization mode */ - -/************************************************* - * from d_norm.c - */ - -/* implemented definitions for CT_ATOMID */ -#define CT_ATOMID_DONTINCLUDE 1 -#define CT_ATOMID_IS_INITRANK 2 -#define CT_ATOMID_IS_CURRANK 3 - -/*************************************** - * canonicalization settings I - ***************************************/ - -#define CANON_TAUTOMERS 1 /* 1=> process tautomers */ -#define HYDROGENS_IN_INIT_RANKS 1 /* 1=> include num_H in initial ranking */ - -#define DOUBLE_BOND_NEIGH_LIST 0 /* 1 => include double bond neighbor in NeighList 2 times */ -#define INCL_NON_6AROM 1 /* 1 => mark all arom. bonds; 0=>mark arom. bonds only in 6-member rings */ - -#define CT_SMALLEST /* minimal CT */ - -#define CT_NEIGH_SMALLER /* in CT, include neighbors with smaller ranks */ - -#define CT_ATOMID CT_ATOMID_IS_CURRANK /*CT_ATOMID_DONTINCLUDE */ - -#define CT_NEIGH_INCREASE /* in CT, neighbors ranks increase */ - -#define USE_SYMMETRY_TO_ACCELERATE 1 /*1 => for fast CT canonicalization, to avoid full enumeration */ - -/* dependent definitions due to settings */ - -#ifdef CT_SMALLEST -#define CT_GREATER_THAN > -#define CT_INITVALUE ~0 -#define BEST_PARITY 1 /* odd */ -#define WORSE_PARITY 2 -#else -#define CT_GREATER_THAN < -#define CT_INITVALUE 0 -#define BEST_PARITY 2 /* even */ -#define WORSE_PARITY 1 -#endif - -#ifdef CT_NEIGH_SMALLER -#define CT_NEIGH_SMALLER_THAN < -#else -#define CT_NEIGH_SMALLER_THAN > -#endif - -/* verify corectness of dependent settings */ -#if !defined( CT_ATOMID ) - #error You have to #define CT_ATOMID -#else -#if ( defined( CT_ATOMID ) && CT_ATOMID==CT_ATOMID_DONTINCLUDE ) - #error CT_DELIMITER should be #defined if CT_ATOMID is not included -#endif -#endif - -/*************************************** - * canonicalization settings II - ***************************************/ -/* from extr_ct.h */ -#define ALL_ALT_AS_AROMATIC 1 /* 1 => all altrnate bonds (even in cyclooctateraene) treat as aromatic */ - /* and set DOUBLE_BOND_NEIGH_LIST = 0 */ -#define ANY_ATOM_IN_ALT_CYCLE 1 /* 1=> accept any atom in alternating bond circuit, 0=>only some */ - -#define EXCL_ALL_AROM_BOND_PARITY 0 /* 1 => any arom atom cannot belong to stereo bond. */ - /* This has presedence over ADD_6MEMB_AROM_BOND_PARITY=1 */ - /* 0 => include arom bonds parities according to */ - /* ADD_6MEMB_AROM_BOND_PARITY definition */ - -#if ( EXCL_ALL_AROM_BOND_PARITY == 0 ) -#define ADD_6MEMB_AROM_BOND_PARITY 1 /* 1 => all arom bonds are stereo bonds */ - /* 0 => only those arom bonds which do not belong to */ - /* 6-member arom rings are stereo bonds */ -#else -#define ADD_6MEMB_AROM_BOND_PARITY 0 /* 0 => standard; 1 => meaningless: ignore parities of non-6-member ring alt. bonds */ -#endif - -#define CML_NUM_AT_IN_ATREF4 4 -#define MAX_NUM_STEREO_BONDS 3 -#define MAX_NUM_STEREO_BOND_NEIGH 3 -#define MIN_NUM_STEREO_BOND_NEIGH 2 - -#define MAX_NUM_STEREO_ATOM_NEIGH 4 -#define STEREO_AT_MARK 8 /* > MAX_NUM_STEREO_BONDS */ - -#if ( ONLY_DOUBLE_BOND_STEREO == 1 ) /* { */ - -#ifdef ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS -#undef ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS -#define ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS 0 -#endif - -#ifdef EXCL_ALL_AROM_BOND_PARITY -#undef EXCL_ALL_AROM_BOND_PARITY -#define EXCL_ALL_AROM_BOND_PARITY 1 -#endif - -#ifdef ADD_6MEMB_AROM_BOND_PARITY -#undef ADD_6MEMB_AROM_BOND_PARITY -#define ADD_6MEMB_AROM_BOND_PARITY 0 -#endif - -#endif /* } ONLY_DOUBLE_BOND_STEREO */ - -/* dependent definitions due to settings */ -#if ( ALL_ALT_AS_AROMATIC == 1 && DOUBLE_BOND_NEIGH_LIST != 0 ) -#undef DOUBLE_BOND_NEIGH_LIST -#define DOUBLE_BOND_NEIGH_LIST 0 -#endif - - -/************************************* - * Drawing - */ - -#define DRAW_AROM_TAUT 1 /* 1=> draw distinct aromatic & tautomer bonds, 0=> don't */ - -/******************************************************/ -/* C O M M O N D E F I N I T I O N S */ -/******************************************************/ - - -/* input bTautFlags flags */ -#define TG_FLAG_TEST_TAUT__ATOMS 0x00000001 /* find regular tautomerism */ -#define TG_FLAG_DISCONNECT_SALTS 0x00000002 /* DISCONNECT_SALTS disconnect */ -#define TG_FLAG_TEST_TAUT__SALTS 0x00000004 /* DISCONNECT_SALTS if possible find long-range H/(-) taut. on =C-OH, >C=O */ -#define TG_FLAG_MOVE_POS_CHARGES 0x00000008 /* MOVE_CHARGES allow long-range movement of N(+), P(+) charges */ -#define TG_FLAG_TEST_TAUT2_SALTS 0x00000010 /* TEST_REMOVE_S_ATOMS multi-attachement long-range H/(-) taut. on =C-OH, >C=O */ -#define TG_FLAG_ALLOW_NO_NEGTV_O 0x00000020 /* CHARGED_SALTS_ONLY=0 (debug) find long-range H-only tautomerism on =C-OH, >C=O */ -#define TG_FLAG_MERGE_TAUT_SALTS 0x00000040 /* DISCONNECT_SALTS merge all "salt"-t-groups and other =C-OH into one t-group */ - -#define TG_FLAG_ALL_TAUTOMERIC (TG_FLAG_TEST_TAUT__ATOMS| \ - TG_FLAG_TEST_TAUT__SALTS| \ - TG_FLAG_TEST_TAUT2_SALTS| \ - TG_FLAG_MERGE_TAUT_SALTS) - -#define TG_FLAG_DISCONNECT_COORD 0x00000080 /* find "coord. centers" and disconnect them */ -#define TG_FLAG_RECONNECT_COORD 0x00000100 /* reconnect disconnected "coord. centers" */ -#define TG_FLAG_CHECK_VALENCE_COORD 0x00000200 /* do not disconnect "coord. centers" with usual valence */ -#define TG_FLAG_MOVE_HPLUS2NEUTR 0x00000400 /* move protons to neutralize */ -#define TG_FLAG_VARIABLE_PROTONS 0x00000800 /* add/remove protons to neutralize */ -#define TG_FLAG_HARD_ADD_REM_PROTONS 0x00001000 /* add/remove protons to neutralize in hard way */ -#define TG_FLAG_POINTED_EDGE_STEREO 0x00002000 /* only pointed edge of stereo bond defines stereo */ -#if ( FIX_ADJ_RAD == 1 ) -#define TG_FLAG_FIX_ADJ_RADICALS 0x00004000 /* remove adjacent radical-doubletes, fix valence */ -#endif -#define TG_FLAG_PHOSPHINE_STEREO 0x00008000 /* add phosphine sp3 stereo */ -#define TG_FLAG_ARSINE_STEREO 0x00010000 /* add arsine sp3 stereo */ -#define TG_FLAG_H_ALREADY_REMOVED 0x00020000 /* processing structure restored from InChI */ -#define TG_FLAG_FIX_SP3_BUG 0x00040000 /* fix sp3 stereo bug: overlapping 2D stereo bond & coordinate scaling */ - -#define TG_FLAG_KETO_ENOL_TAUT 0x00080000 /* turn on keto-enol tautomerism detection */ -#define TG_FLAG_1_5_TAUT 0x00100000 /* turn on 1,5 tautomerism detection */ - -/*^^^ FB2 */ -#define TG_FLAG_FIX_ISO_FIXEDH_BUG 0x00200000 /* fix bug found after v.102b (isotopic H representation) */ -#define TG_FLAG_FIX_TERM_H_CHRG_BUG 0x00400000 /* fix bug found after v.102b (moving H charge in 'remove_terminal_HDT') */ - -/* output bTautFlags flags */ - -#define TG_FLAG_MOVE_HPLUS2NEUTR_DONE 0x00000001 /* protons have been moved to neutralize */ -#define TG_FLAG_TEST_TAUT__ATOMS_DONE 0x00000002 -#define TG_FLAG_DISCONNECT_SALTS_DONE 0x00000004 -#define TG_FLAG_TEST_TAUT__SALTS_DONE 0x00000008 /* multiple H tautomerism */ -#define TG_FLAG_MOVE_POS_CHARGES_DONE 0x00000010 -#define TG_FLAG_TEST_TAUT2_SALTS_DONE 0x00000020 /* merged t-groups */ -#define TG_FLAG_ALLOW_NO_NEGTV_O_DONE 0x00000040 -#define TG_FLAG_MERGE_TAUT_SALTS_DONE 0x00000080 /* added non-taut O to taut groups */ - -#define TG_FLAG_ALL_SALT_DONE (TG_FLAG_TEST_TAUT__SALTS_DONE | \ - TG_FLAG_TEST_TAUT2_SALTS_DONE | \ - TG_FLAG_MERGE_TAUT_SALTS_DONE ) - -#define TG_FLAG_DISCONNECT_COORD_DONE 0x00000100 /* found and disconnected "coord. centers" */ -#define TG_FLAG_CHECK_VALENCE_COORD_DONE 0x00000200 /* did not disconnect "coord. centers" with usual valence */ -#define TG_FLAG_MOVE_CHARGE_COORD_DONE 0x00000400 /* changed charge of a disconnected ligand to fit its valence */ -#define TG_FLAG_FIX_ODD_THINGS_DONE 0x00000800 /* fixed drawing ambiguities in fix_odd_things */ -#define TG_FLAG_TEST_TAUT3_SALTS_DONE 0x00001000 /* merged t-groups + non-O taut atoms */ -#define TG_FLAG_FOUND_SALT_CHARGES_DONE 0x00002000 /* not assigned: preprocessing detected possibility of salt-type tautomerism */ -#define TG_FLAG_FOUND_ISOTOPIC_H_DONE 0x00004000 /* preprocessing detected isotopic H on "good" heteroatoms or isotopic H(+) */ -#define TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE 0x00008000 /* preprocessing detected isotopic H on "good" heteroatoms or isotopic H(+) */ -#if ( FIX_ADJ_RAD == 1 ) -#define TG_FLAG_FIX_ADJ_RADICALS_DONE 0x00010000 -#endif - -#if ( READ_INCHI_STRING == 1 ) -#define READ_INCHI_OUTPUT_INCHI 0x00000001 -#define READ_INCHI_SPLIT_OUTPUT 0x00000002 -#define READ_INCHI_KEEP_BALANCE_P 0x00000004 -#define READ_INCHI_TO_STRUCTURE 0x00000008 -#endif - - - - -/*********/ -/* */ -/* I/O */ -/* */ -/*********/ - -typedef struct tagOutputString -{ - char *pStr; - int nAllocatedLength; - int nUsedLength; - int nPtr; -} INCHI_OUTPUT; - -typedef struct tagOutputStream -{ - /* output is directed either to resizable string buffer: */ - INCHI_OUTPUT s; - /* or to the plain file: */ - FILE* f; - int type; -} INCHI_IOSTREAM; -/* INCHI_IOSTREAM.type values */ -#define INCHI_IOSTREAM_NONE 0 -#define INCHI_IOSTREAM_STRING 1 -#define INCHI_IOSTREAM_FILE 2 - - - - -/***********/ -/* */ -/* DEBUG */ -/* */ -/***********/ - -#if ( defined(_WIN32) && defined(_DEBUG) && defined(_MSC_VER) /*&& !defined(COMPILE_ANSI_ONLY)*/ ) -/* debug: memory leaks tracking */ -#ifndef TARGET_LIB_FOR_WINCHI -#ifndef DO_NOT_TRACE_MEMORY_LEAKS -#define TRACE_MEMORY_LEAKS 1 /* 1=>trace, 0 => do not trace (Debug only) */ -#else -#define TRACE_MEMORY_LEAKS 0 -#endif -#else -#define TRACE_MEMORY_LEAKS 1 /* 1=>trace, **ALWAYS** =1 for TARGET_LIB_FOR_WINCHI */ -#endif -#else /* not MSC and not Debug */ -#define TRACE_MEMORY_LEAKS 0 /* 0: do not change */ -#endif - - -/* memory leaks tracking */ -#define INCHI_HEAPCHK /* default: no explicit heap checking during the execution */ - -#if ( TRACE_MEMORY_LEAKS == 1 ) -#ifdef _DEBUG - -#define inchi_malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) -#define inchi_calloc(c, s) _calloc_dbg(c, s, _NORMAL_BLOCK, __FILE__, __LINE__) -#define inchi_free(p) _free_dbg(p, _NORMAL_BLOCK) - -#ifdef TARGET_EXE_USING_API -/* INChI_MAIN specific */ -#define e_inchi_malloc(a) inchi_malloc(a) -#define e_inchi_calloc(a,b) inchi_calloc(a,b) -#define e_inchi_free(a) inchi_free(a) -#endif - -/*#define _CRTDBG_MAP_ALLOC*/ /* standard VC++ tool -- does not work with inchi_malloc(), etc */ - -#include - -/* to enable heap checking: #define CHECK_WIN32_VC_HEAP above #include "mode.h" in each source file or here */ -#ifdef CHECK_WIN32_VC_HEAP -/* -- Confirms the integrity of the memory blocks allocated in the debug heap -- */ -#undef INCHI_HEAPCHK -#define INCHI_HEAPCHK \ -do { \ - int tmp = _crtDbgFlag; \ - _crtDbgFlag |= _CRTDBG_ALLOC_MEM_DF; \ - _ASSERT( _CrtCheckMemory( ) ); \ - _crtDbgFlag = tmp; \ -} while(0); - -/* -- less thorough than _CrtCheckMemory() check: check minimal consistency of the heap -- */ -/* -#include -#define INCHI_HEAPCHK \ -do {\ - int heapstatus = _heapchk(); \ - _ASSERT( heapstatus != _HEAPBADBEGIN && heapstatus != _HEAPBADNODE && heapstatus != _HEAPBADPTR); \ -} while(0); -*/ -#endif - -#else -#undef TRACE_MEMORY_LEAKS -#define TRACE_MEMORY_LEAKS 0 -#endif /* _DEBUG */ -#endif /* TRACE_MEMORY_LEAKS */ - - - -/***********/ -/* */ -/* ALLOC */ -/* */ -/***********/ - -#ifdef TARGET_EXE_USING_API -/* INChI_MAIN specific */ -#ifndef inchi_malloc -#define inchi_malloc e_inchi_malloc -#endif -#ifndef inchi_calloc -#define inchi_calloc e_inchi_calloc -#endif -#ifndef inchi_free -#define inchi_free e_inchi_free -#endif - -#ifndef e_inchi_malloc -#define e_inchi_malloc malloc -#endif -#ifndef e_inchi_calloc -#define e_inchi_calloc calloc -#endif -#ifndef e_inchi_free -#define e_inchi_free(X) do{ if(X) free(X); }while(0) -#endif - -#else /* not TARGET_EXE_USING_API */ - -#ifndef inchi_malloc -#define inchi_malloc malloc -#endif -#ifndef inchi_calloc -#define inchi_calloc calloc -#endif -#ifndef inchi_free -#define inchi_free(X) do{ if(X) free(X); }while(0) -#endif - -#endif /* TARGET_EXE_USING_API */ - -/* allocation/deallocation */ -#define USE_ALLOCA 0 - -#if ( USE_ALLOCA == 1 ) -#define qmalloc(X) _alloca(X) -#define qfree(X) do{(X)=NULL;}while(0) -#else -#define qmalloc(X) inchi_malloc(X) -#define qfree(X) do{if(X){inchi_free(X);(X)=NULL;}}while(0) -#endif - -#if ( defined(_MSC_VER) && _MSC_VER >= 800 ) -#define fast_alloc(X) _alloca(X) -#define fast_free(X) -#else -#define fast_alloc(X) inchi_malloc(X) -#define fast_free(X) inchi_free(X) -#endif - -#define qzfree(X) do{if(X){inchi_free(X);(X)=NULL;}}while(0) - -/* rellocation */ - -#define MYREALLOC2(PTRTYPE1, PTRTYPE2, PTR1, PTR2, LEN1, LEN2, ERR) \ - do { \ - if( (LEN1) <= (LEN2) ) {\ - PTRTYPE1 * newPTR1 = (PTRTYPE1 *)inchi_calloc( (LEN2)+1, sizeof(PTRTYPE1) );\ - PTRTYPE2 * newPTR2 = (PTRTYPE2 *)inchi_calloc( (LEN2)+1, sizeof(PTRTYPE2) );\ - if ( newPTR1 && newPTR2 ) { \ - if ( (PTR1) && (LEN1) > 0 ) \ - (memcpy) ( newPTR1, (PTR1), (LEN1) * sizeof(PTRTYPE1) ); \ - if ( (PTR2) && (LEN1) > 0 ) \ - (memcpy) ( newPTR2, (PTR2), (LEN1) * sizeof(PTRTYPE2) ); \ - if ( PTR1 ) \ - inchi_free(PTR1); \ - if ( PTR2 ) \ - inchi_free(PTR2); \ - (PTR1) = newPTR1; \ - (PTR2) = newPTR2; \ - (LEN1) = (LEN2); \ - (ERR) = 0; \ - } else { \ - (ERR) = 1; \ - } \ - } else { (ERR) = 0; } \ - } while(0) - - - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /* __MODE_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/runichi.c b/INCHI-1-SRC/INCHI_API/inchi_dll/runichi.c deleted file mode 100644 index 4c49caf..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/runichi.c +++ /dev/null @@ -1,4008 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include -/* #include */ -#include -#include - - - -#include "mode.h" /* moved from below, suggestion by David Mosenkis */ - -#include "ichitime.h" - -#ifndef COMPILE_ANSI_ONLY -#include -#endif - -#include "inpdef.h" -#include "ichi.h" -#include "strutil.h" -#include "util.h" -#include "ichidrp.h" -#include "ichierr.h" -#include "ichimain.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichi_io.h" - -#ifdef TARGET_LIB_FOR_WINCHI -#include "ichi_lib.h" -#endif -#include "inchi_api.h" - -#include "ichicomp.h" - -#if ( ADD_CMLPP == 1 ) -#include "readcml.hpp" -#include "debug.h" -#endif - - -/* for DisplayTheWholeStructure() */ - -#define COMP_ORIG_0_MAIN 0x0001 -#define COMP_ORIG_0_RECN 0x0002 -#define COMP_PREP_0_MAIN 0x0004 -#define COMP_PREP_0_RECN 0x0008 -#define COMP_ORIG_1_MAIN 0x0010 -#define COMP_ORIG_1_RECN 0x0020 - - -/* local prototypes */ -int GetProcessingWarningsOneINChI(INChI *pINChI, INP_ATOM_DATA *inp_norm_data, char *pStrErrStruct); -int GetProcessingWarnings(INChI *cur_INChI[], INP_ATOM_DATA **inp_norm_data, STRUCT_DATA *sd); -int DisplayTheWholeStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, - INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, - ORIG_ATOM_DATA *orig_inp_data, long num_inp, int iINChI, int bShowStruct, int bINCHI_LIB_Flag ); -int DuplicateOrigAtom( ORIG_ATOM_DATA *new_orig_atom, ORIG_ATOM_DATA *orig_atom ); -int bCheckUnusualValences( ORIG_ATOM_DATA *orig_at_data, int bAddIsoH, char *pStrErrStruct ); -int CreateCompositeNormAtom( COMP_ATOM_DATA *composite_norm_data, INP_ATOM_DATA2 *all_inp_norm_data, - PINChI2 *pINChI, PINChI_Aux2 *pINChI_Aux, int num_components, INCHI_MODE nMode ); -int DetectInputINChIFileType( FILE **inp_file, INPUT_PARMS *ip, const char *fmode ); - - -/* callback */ -int (*ConsoleQuit)(void) = NULL; /* Console user issued CTRL+C etc. */ -int (*UserAction)(void) = NULL; /* callback */ - -#ifdef TARGET_LIB_FOR_WINCHI -void (*FWPRINT) (const char * format, va_list argptr )=NULL; -void (*DRAWDATA) ( struct DrawData * pDrawData) = NULL; -int (*DRAWDATA_EXISTS) ( int nComponent, int nType, int bReconnected ) = NULL; -struct DrawData * (*GET_DRAWDATA) ( int nComponent, int nType, int bReconnected ) = NULL; -#endif - -#if ( TEST_RENUMB_ATOMS == 1 ) /* { */ -/************************************************/ -/* atoms renumbering -- for testing only */ -/************************************************/ -typedef struct tagRenumbData { - PINChI2 ren_INChI2[1]; - PINChI_Aux2 ren_INChI_Aux[1]; - INP_ATOM_DATA orig_inp_cur_data; - INP_ATOM_DATA saved_inp_cur_data; -#if ( TEST_RENUMB_ATOMS_SAVE_LONGEST == 1 || TEST_RENUMB_SWITCH == 1 ) - INP_ATOM_DATA longest_inp_cur_data; -#endif - INP_ATOM_DATA ren_inp_norm_data1, ren_inp_norm_data2; - INP_ATOM_DATA *ren_inp_norm_data[2]; - int ren_counter; - int num_taut, num_non_taut, num_taut0, num_non_taut0; - AT_RANK *new_ord; - int nRet2, c1, c2, nComp, bRenumbErr; - unsigned long ulCurTimeNorm0, ulCurTimeCanon0, ulCurTimeNorm1, ulCurTimeCanon1; - unsigned long ulCurTimeNorm, ulCurTimeCanon, ulMaxTimeNorm, ulMaxTimeCanon; - unsigned long ulMaxTime, ulCurTime, ulCurTime0, ulCurTime1; -#if ( bRELEASE_VERSION == 0 ) - int bExtract; -#endif -} RENUMB_DATA; - -int RenumberingTestInit( RENUMB_DATA *pRenumbData, INP_ATOM_DATA *inp_cur_data ); -int RenumberingTestUninit( RENUMB_DATA *pRenumbData ); -int RenumberingTest( PINChI2 *pICh, PINChI_Aux2 *pINChI_Aux, ORIG_ATOM_DATA *orig_inp_data, int iINChI, - RENUMB_DATA *pRenumbData, INP_ATOM_DATA *inp_cur_data, INP_ATOM_DATA **inp_norm_data, - STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *prb_file, - int i, long num_inp, NORM_CANON_FLAGS *pncFlags); -/* -int RenumberingTest( INChI *pINChI[][TAUT_NUM], INChI_Aux *pINChI_Aux[][TAUT_NUM], int iINChI, - RENUMB_DATA *pRenumbData, INP_ATOM_DATA *inp_cur_data, INP_ATOM_DATA **inp_norm_data, - STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, INCHI_IOSTREAM *log_file, int i, long num_inp); -*/ -#endif /* } TEST_RENUMB_ATOMS */ - - - -#ifndef COMPILE_ANSI_ONLY -/********************************************************************/ -void FillTableParms( SET_DRAW_PARMS *sdp, INChI **cur_INChI, INChI_Aux **cur_INChI_Aux, - INCHI_MODE nMode, int bShowIsotopic, int indx ) -{ - TBL_DRAW_PARMS *tdp = sdp->tdp; - char (*ReqShownFound)[TDP_NUM_PAR] = tdp->ReqShownFound; - int i, j; - INChI_Stereo *Stereo; - int bShowTaut = (cur_INChI && cur_INChI[indx]->lenTautomer > 0)? 1 : 0; -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - int bRelRac = 0 != (nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO )); -#endif - if ( !cur_INChI || !cur_INChI_Aux ) { - sdp->tdp->bDrawTbl = 0; - sdp->bOrigAtom = 1; - return; - } - - /* Displayed */ - ReqShownFound[ilSHOWN][itBASIC] = bShowTaut? 'T':'\0'; - ReqShownFound[ilSHOWN][itISOTOPIC] = bShowIsotopic? 'I':'\0'; -/* - ReqShownFound[ilSHOWN][itBASIC] = bShowTaut? 'T':'B'; - ReqShownFound[ilSHOWN][itISOTOPIC] = bShowIsotopic? 'I':'N'; - */ - i = indx; - if ( cur_INChI[i] ) { - Stereo = bShowIsotopic? cur_INChI[i]->StereoIsotopic : cur_INChI[i]->Stereo; - } else { - Stereo = NULL; - } -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - if ( Stereo && ( 0 < Stereo->nNumberOfStereoBonds || - 0 < Stereo->nNumberOfStereoCenters-bRelRac ) ) { - ReqShownFound[ilSHOWN][itSTEREO] = 'S'; - if ( Stereo->nNumberOfStereoCenters && Stereo->nCompInv2Abs == -1 && - ( nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO ) ) ) { - if ( Stereo->nNumberOfStereoCenters < 2 && !Stereo->nNumberOfStereoBonds ) { - ReqShownFound[ilSHOWN][itSTEREO] = '\0'; - } else - if ( Stereo->nNumberOfStereoCenters >= 2 ) { - ReqShownFound[ilSHOWN][itSTEREO] = 's'; /* shown Inverted stereo */ - } - } -#else /* REL_RAC_STEREO_IGN_1_SC == 0 */ - if ( Stereo && ( Stereo->nNumberOfStereoBonds || Stereo->nNumberOfStereoCenters ) ) { - ReqShownFound[ilSHOWN][itSTEREO] = 'S'; - if ( Stereo->nNumberOfStereoCenters && Stereo->nCompInv2Abs == -1 && - ( nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO ) ) ) { - /* - if ( Stereo->nNumberOfStereoCenters < 2 && !Stereo->nNumberOfStereoBonds ) { - ReqShownFound[ilSHOWN][itSTEREO] = '\0'; - } else - if ( Stereo->nNumberOfStereoCenters >= 2 ) { - */ - ReqShownFound[ilSHOWN][itSTEREO] = 's'; /* shown Inverted stereo */ - /* - } - */ - } -#endif /* REL_RAC_STEREO_IGN_1_SC */ - } else { - ReqShownFound[ilSHOWN][itSTEREO] = '\0'; - } - /* - ReqShownFound[ilSHOWN][itSTEREO] = - (bShowIsotopic? (cur_INChI[i] && cur_INChI[i]->StereoIsotopic && - (cur_INChI[i]->StereoIsotopic->nNumberOfStereoBonds || - cur_INChI[i]->StereoIsotopic->nNumberOfStereoCenters) ) - : - (cur_INChI[i] && cur_INChI[i]->Stereo && - (cur_INChI[i]->Stereo->nNumberOfStereoBonds || - cur_INChI[i]->Stereo->nNumberOfStereoCenters) ) - ) ? 'S':'\0'; - */ - - /* remove zeroes between chars */ - for ( i = j = 0; i < TDP_NUM_PAR; i ++ ) { - if ( ReqShownFound[ilSHOWN][i] >= ' ' ) { - ReqShownFound[ilSHOWN][j++] = ReqShownFound[ilSHOWN][i]; - } - } - i = j; - for ( ; i < TDP_NUM_PAR; i ++ ) { - ReqShownFound[ilSHOWN][i] = '\0'; - } - - sdp->tdp->bDrawTbl = j? 1 : 0; - sdp->bOrigAtom = 0; -} -/********************************************************************/ -void FillCompositeTableParms( SET_DRAW_PARMS *sdp, AT_NUMB StereoFlags, - INCHI_MODE nMode, int bShowIsotopic, int bShowTaut ) -{ - TBL_DRAW_PARMS *tdp = sdp->tdp; - char (*ReqShownFound)[TDP_NUM_PAR] = tdp->ReqShownFound; - int i, j; - - /* Displayed */ - ReqShownFound[ilSHOWN][itBASIC] = bShowTaut? 'T':'\0'; - ReqShownFound[ilSHOWN][itISOTOPIC] = bShowIsotopic? 'I':'\0'; -/* - ReqShownFound[ilSHOWN][itBASIC] = bShowTaut? 'T':'B'; - ReqShownFound[ilSHOWN][itISOTOPIC] = bShowIsotopic? 'I':'N'; - */ - if ( StereoFlags & INF_STEREO ) { - ReqShownFound[ilSHOWN][itSTEREO] = 'S'; - if ( (StereoFlags & INF_STEREO_INV) && - ( nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO ) ) ) { - if (StereoFlags & (INF_STEREO_REL | INF_STEREO_RAC) ) { - ReqShownFound[ilSHOWN][itSTEREO] = 's'; - } else { - ReqShownFound[ilSHOWN][itSTEREO] = '\0'; /* shown Inverted stereo */ - } - } - } else { - ReqShownFound[ilSHOWN][itSTEREO] = '\0'; - } - /* - ReqShownFound[ilSHOWN][itSTEREO] = - (bShowIsotopic? (cur_INChI[i] && cur_INChI[i]->StereoIsotopic && - (cur_INChI[i]->StereoIsotopic->nNumberOfStereoBonds || - cur_INChI[i]->StereoIsotopic->nNumberOfStereoCenters) ) - : - (cur_INChI[i] && cur_INChI[i]->Stereo && - (cur_INChI[i]->Stereo->nNumberOfStereoBonds || - cur_INChI[i]->Stereo->nNumberOfStereoCenters) ) - ) ? 'S':'\0'; - */ - - /* remove zeroes between chars */ - for ( i = j = 0; i < TDP_NUM_PAR; i ++ ) { - if ( ReqShownFound[ilSHOWN][i] >= ' ' ) { - ReqShownFound[ilSHOWN][j++] = ReqShownFound[ilSHOWN][i]; - } - } - i = j; - for ( ; i < TDP_NUM_PAR; i ++ ) { - ReqShownFound[ilSHOWN][i] = '\0'; - } - - sdp->tdp->bDrawTbl = j? 1 : 0; - sdp->bOrigAtom = 0; -} -#endif -/* IchiParm.c was here */ -/*******************************************************************/ -#ifndef COMPILE_ANSI_ONLY -#ifndef TARGET_LIB_FOR_WINCHI -/*******************************************************************/ -int DisplayStructure( inp_ATOM *at, int num_at, int num_removed_H, int bAdd_DT_to_num_H, - int nNumRemovedProtons, NUM_H *nNumRemovedProtonsIsotopic, - int bIsotopic, int j /*bTautomeric*/, - INChI **cur_INChI, INChI_Aux **cur_INChI_Aux, - int bAbcNumbers, DRAW_PARMS *dp, INCHI_MODE nMode, char *szTitle ) -{ - INF_ATOM_DATA inf_data = {NULL,}; - int err = -1; - if ( CreateInfoAtomData( &inf_data, num_at, 1 ) ) { - err = 0; - FillOutInfAtom( at, &inf_data, num_at, num_removed_H, bAdd_DT_to_num_H, - nNumRemovedProtons, nNumRemovedProtonsIsotopic, bIsotopic, - cur_INChI?cur_INChI[j]:NULL, - cur_INChI_Aux?cur_INChI_Aux[j]:NULL, bAbcNumbers, nMode); - FillTableParms( &dp->sdp, cur_INChI, cur_INChI_Aux, nMode, bIsotopic, j ); - err = DisplayInputStructure( szTitle, at, &inf_data, num_at, dp ); - FreeInfoAtomData( &inf_data ); - } - return err; -} - -/*******************************************************************/ -int DisplayCompositeStructure( COMP_ATOM_DATA *composite_norm_data, int bIsotopic, int bTautomeric, - PINChI2 *pINChI2, PINChI_Aux2 *pINChI_Aux2, - int bAbcNumbers, DRAW_PARMS *dp, INCHI_MODE nMode, char *szTitle ) -{ - INF_ATOM_DATA inf_data; - int err = -1, ret; - memset( &inf_data, 0, sizeof(inf_data) ); - if ( CreateInfoAtomData( &inf_data, (composite_norm_data+bTautomeric)->num_at, - (composite_norm_data+bTautomeric)->num_components ) ) { - ret = FillOutCompositeCanonInfAtom(composite_norm_data, &inf_data, - bIsotopic, bTautomeric, - pINChI2, pINChI_Aux2, bAbcNumbers, nMode); - if ( !ret ) { - goto exit_function; /* error */ - } - if ( bTautomeric == TAUT_INI ) { - /* - FillOutInfAtom( (composite_norm_data+bTautomeric)->at, &inf_data, (composite_norm_data+bTautomeric)->num_at, - (composite_norm_data+bTautomeric)->num_removed_H, bAdd_DT_to_num_H, - (composite_norm_data+bTautomeric)->nNumRemovedProtons, - (composite_norm_data+bTautomeric)->nNumRemovedProtonsIsotopic, bIsotopic, - NULL, NULL, bAbcNumbers, nMode); - */ - ; - } else { - /* real check for tautomeric components 02-04-2005 */ - int m, nNumTautComponents = 0; - if ( 1 == bTautomeric ) { - for ( m = 0; m < composite_norm_data[TAUT_YES].num_components; m ++ ) { - if ( !pINChI2[m][TAUT_YES] ) - continue; - if ( pINChI2[m][TAUT_YES]->bDeleted || pINChI2[m][TAUT_YES]->lenTautomer > 0 ) - nNumTautComponents ++; - } - } - FillCompositeTableParms( &dp->sdp, inf_data.StereoFlags, nMode, bIsotopic, nNumTautComponents ); - } - err = DisplayInputStructure( szTitle, (composite_norm_data+bTautomeric)->at, &inf_data, (composite_norm_data+bTautomeric)->num_at, dp ); - FreeInfoAtomData( &inf_data ); - } -exit_function: - return err; -} -#endif -#endif -/************************************************/ -const char *ErrMsg( int nErrorCode ) -{ - const char *p; - static char szErrMsg[64]; - switch( nErrorCode ) { - case 0: p = ""; break; - case CT_OVERFLOW: p = "ARRAY OVERFLOW"; break; - case CT_LEN_MISMATCH: p = "LENGTH_MISMATCH"; break; - case CT_OUT_OF_RAM: p = "Out of RAM"; break; - case CT_RANKING_ERR: p = "RANKING_ERR"; break; - case CT_ISOCOUNT_ERR: p = "ISOCOUNT_ERR"; break; - case CT_TAUCOUNT_ERR: p = "TAUCOUNT_ERR"; break; - case CT_ISOTAUCOUNT_ERR: p = "ISOTAUCOUNT_ERR"; break; - case CT_MAPCOUNT_ERR: p = "MAPCOUNT_ERR"; break; - case CT_TIMEOUT_ERR: p = "Time limit exceeded"; break; - case CT_ISO_H_ERR: p = "ISO_H_ERR"; break; - case CT_STEREOCOUNT_ERR: p = "STEREOCOUNT_ERR"; break; - case CT_ATOMCOUNT_ERR: p = "ATOMCOUNT_ERR"; break; - case CT_STEREOBOND_ERROR: p = "STEREOBOND_ERR"; break; - case CT_USER_QUIT_ERR: p = "User requested termination"; break; - case CT_REMOVE_STEREO_ERR: p = "REMOVE_STEREO_ERR"; break; - case CT_CALC_STEREO_ERR: p = "CALC_STEREO_ERR"; break; - case CT_STEREO_CANON_ERR: p = "STEREO_CANON_ERR"; break; - case CT_CANON_ERR: p = "CANON_ERR"; break; - case CT_WRONG_FORMULA: p = "Wrong or missing chemical formula"; break; - /*case CT_CANON_ERR2: p = "CT_CANON_ERR2"; break;*/ - case CT_UNKNOWN_ERR: p = "UNKNOWN_ERR"; break; - case BNS_RADICAL_ERR: p = "Cannot process free radical center"; break; - case BNS_ALTBOND_ERR: p = "Cannot process aromatic bonds"; break; - - default: - if ( nErrorCode > CT_UNKNOWN_ERR ) { - sprintf( szErrMsg, "No description(%d)", nErrorCode ); - p = szErrMsg; - } else { - sprintf( szErrMsg, "UNKNOWN_ERR(%d)", CT_UNKNOWN_ERR - nErrorCode ); - p = szErrMsg; - } - break; - } - return p; -} -/***********************************************************************************/ -#ifndef COMPILE_ANSI_ONLY /* { */ -/***********************************************************************************/ -int SaveEquComponentsInfoAndSortOrder ( int iINChI, INCHI_SORT *pINChISort[TAUT_NUM], int *num_components, - ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data, - COMP_ATOM_DATA composite_norm_data[TAUT_NUM+1], - int bCompareComponents ) -{ - int nRet = 0, i, k, nNumDeleted; - /* equivalent components and sorting order */ - /* bCompareComponents: bit = 1 => compare */ - /* bit = 2 => compare non-isotopic */ - /* bit = 4 => compare non-tautomeric */ - int bCompareIsotopic, bCompareTaut, bCompareAlt; - ORIG_ATOM_DATA *inp_data = NULL; - - if ( num_components[iINChI] <= 1 ) - return 0; -#ifdef TARGET_LIB_FOR_WINCHI - if ( !DRAWDATA ) - return 0; -#endif - if ( !(bCompareComponents & CMP_COMPONENTS) ) - return 0; - bCompareIsotopic = !(bCompareComponents & CMP_COMPONENTS_NONISO); - bCompareTaut = (bCompareComponents & CMP_COMPONENTS_NONTAUT) ? TAUT_NON : TAUT_YES; - bCompareAlt = ALT_TAUT(bCompareTaut); - if ( num_components[iINChI] > 1 ) { - if ( prep_inp_data[iINChI].bSavedInINCHI_LIB[iINChI] && prep_inp_data[iINChI].bPreprocessed[iINChI] ) { - inp_data = prep_inp_data+iINChI; - } else - if ( orig_inp_data->bSavedInINCHI_LIB[iINChI] && !orig_inp_data->bPreprocessed[iINChI] ) { - inp_data = orig_inp_data; - } else { - inp_data = NULL; - } - if ( inp_data && !inp_data->nEquLabels && !prep_inp_data[iINChI].nSortedOrder ) { - int i1, i2, nSet; - AT_NUMB nAtNo; - AT_NUMB nNumAtoms = (AT_NUMB)inp_data->num_inp_atoms; - if ( (prep_inp_data[iINChI].nSortedOrder = - (AT_NUMB *)inchi_calloc(num_components[iINChI]+1, - sizeof(prep_inp_data[0].nSortedOrder[0])))) { - inp_data->nNumEquSets = 0; - for ( i1 = 0, nSet = 0; i1 < num_components[iINChI]; i1 = i2 ) { - nNumDeleted = (pINChISort[bCompareTaut][i1].pINChI[bCompareTaut] && pINChISort[bCompareTaut][i1].pINChI[bCompareTaut]->bDeleted); - for ( i2 = i1+1; i2 < num_components[iINChI]; i2 ++ ) { - /* isotopic/non-isotopic comparison does not separate equivalent components */ - if ( CompINChI2( pINChISort[bCompareTaut]+i1, pINChISort[bCompareTaut]+i2, bCompareTaut, bCompareIsotopic ) ) { - break; - } else { - nNumDeleted += (pINChISort[bCompareTaut][i2].pINChI[bCompareTaut] && pINChISort[bCompareTaut][i2].pINChI[bCompareTaut]->bDeleted); - } - } - if ( i2 - i1 - nNumDeleted > 1 ) { - if ( inp_data->nEquLabels || - (inp_data->nEquLabels = (AT_NUMB *)inchi_calloc(inp_data->num_inp_atoms+1, - sizeof(inp_data->nEquLabels[0]))) ) { - nSet ++; /* found i2-i1 equivalent components && memory has been allocated */ - for ( i = i1; i < i2; i ++ ) { - INChI_Aux *pINChI_Aux; - if (pINChISort[bCompareTaut][i].pINChI[bCompareTaut] && pINChISort[bCompareTaut][i].pINChI[bCompareTaut]->bDeleted) - continue; - pINChI_Aux = (pINChISort[bCompareTaut][i].pINChI_Aux[bCompareTaut] && - pINChISort[bCompareTaut][i].pINChI_Aux[bCompareTaut]->nNumberOfAtoms)? - pINChISort[bCompareTaut][i].pINChI_Aux[bCompareTaut]: - (pINChISort[bCompareTaut][i].pINChI_Aux[bCompareAlt] && - pINChISort[bCompareTaut][i].pINChI_Aux[bCompareAlt]->nNumberOfAtoms)? - pINChISort[bCompareTaut][i].pINChI_Aux[bCompareAlt]: - (INChI_Aux *)NULL; - if ( pINChI_Aux && pINChI_Aux->nOrigAtNosInCanonOrd ) { - for ( k = 0; k < pINChI_Aux->nNumberOfAtoms; k ++ ) { - if ( (nAtNo = pINChI_Aux->nOrigAtNosInCanonOrd[k]) && - nAtNo <= nNumAtoms ) { - inp_data->nEquLabels[nAtNo-1] = nSet; - } - } - } - } - } else { - return CT_OUT_OF_RAM; - } - } - } - nRet |= nSet? 1:0; - } else { - return CT_OUT_OF_RAM; - } - inp_data->nNumEquSets = nSet; - /* output order */ - prep_inp_data[iINChI].nSortedOrder[0] = 0; - for ( i1 = 0; i1 < num_components[iINChI]; i1 ++ ) { - prep_inp_data[iINChI].nSortedOrder[i1+1] = pINChISort[TAUT_YES][i1].ord_number+1; - } -#ifdef TARGET_LIB_FOR_WINCHI /* { */ - if ( DRAWDATA && GET_DRAWDATA && inp_data->nNumEquSets > 0 && inp_data->nEquLabels ) { - int nType = inp_data->bPreprocessed[iINChI]? - COMPONENT_ORIGINAL_PREPROCESSED : - COMPONENT_ORIGINAL; - struct DrawData *pDrawData = GET_DRAWDATA( 0, nType, iINChI); - if ( pDrawData && pDrawData->pWindowData && !pDrawData->pWindowData->nEquLabels ) { - /* copy equivalence data from inp_data to pDrawData->pWindowData */ - if ( inp_data->nEquLabels && - (pDrawData->pWindowData->nEquLabels = (AT_NUMB *)inchi_calloc(inp_data->num_inp_atoms, - sizeof(inp_data->nEquLabels[0])))) { - memcpy( pDrawData->pWindowData->nEquLabels, inp_data->nEquLabels, - inp_data->num_inp_atoms * sizeof(inp_data->nEquLabels[0])); - pDrawData->pWindowData->nNumEquSets = inp_data->nNumEquSets; - pDrawData->pWindowData->nCurEquLabel = 0; - } - } - } -#endif /* } TARGET_LIB_FOR_WINCHI */ - } - } - return nRet; -} - -/************************************************************************************************/ -int DisplayTheWholeCompositeStructure( INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, int iINChI, - PINChI2 *pINChI2, PINChI_Aux2 *pINChI_Aux2, - ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data, - COMP_ATOM_DATA composite_norm_data[TAUT_NUM+1] ) -{ - ORIG_ATOM_DATA *inp_data = NULL; - int jj, j, k, err = 0, nNumIntermediateTaut = 0, bDisplayTaut; - char szTitle[256]; - int nNumTautComponents, m; - - int bCompareIsotopic = !(ip->bCompareComponents & CMP_COMPONENTS_NONISO); - int bCompareTaut = (ip->bCompareComponents & CMP_COMPONENTS_NONTAUT) ? TAUT_NON : TAUT_YES; - - if ( ip->bCompareComponents & CMP_COMPONENTS ) { - if ( prep_inp_data[iINChI].bSavedInINCHI_LIB[iINChI] && prep_inp_data[iINChI].bPreprocessed[iINChI] ) { - inp_data = prep_inp_data+iINChI; - } else - if ( orig_inp_data->bSavedInINCHI_LIB[iINChI] && !orig_inp_data->bPreprocessed[iINChI] ) { - inp_data = orig_inp_data; - } - } - /************************************************************************** - * display from one up to 4 structure pictures-results for all components * - * Enable buttons: * - * BN (non-tautomeric non-isotopic): inp_norm_data[0]->bExists * - * TN (tautomeric non-isotopic): inp_norm_data[1]->bExists * - * BI (non-tautomeric isotopic): inp_norm_data[0]->bExists && * - * inp_norm_data[0]->bHasIsotopicLayer * - * TI (tautomeric isotopic): inp_norm_data[1]->bExists && * - * inp_norm_data[1]->bHasIsotopicLayer * - **************************************************************************/ - for ( jj = 0; ip->bDisplayCompositeResults && !sd->bUserQuitComponentDisplay && jj <= TAUT_INI; jj ++ ) { - /*for ( j = 0; ip->bDisplayCompositeResults && !sd->bUserQuitComponentDisplay && j <= TAUT_INI; j ++ )*/ - j = (jj==0)? TAUT_NON : (jj==1)? TAUT_INI : (jj==2)? TAUT_YES : -1; - if ( j < 0 ) - continue; - if ( composite_norm_data[j].bExists && composite_norm_data[j].num_components > 1 ) { - bDisplayTaut = (!(ip->nMode & REQ_MODE_BASIC) && !j)? -1 : j; - nNumTautComponents = 0; - if ( bDisplayTaut ) { - /* find whether the structure is actually tautomeric */ - for ( m = 0; m < composite_norm_data[TAUT_YES].num_components; m ++ ) { - if ( !pINChI2[m][TAUT_YES] ) - continue; - if ( pINChI2[m][TAUT_YES]->bDeleted || pINChI2[m][TAUT_YES]->lenTautomer > 0 ) - nNumTautComponents ++; - } - } - for ( k = 0; k <= composite_norm_data[j].bHasIsotopicLayer && !sd->bUserQuitComponentDisplay; k ++ ) { - /* added number of components, added another format for a single component case - DCh */ - int bMobileH = (bDisplayTaut>0 && nNumTautComponents); - sprintf( szTitle, "%s Structure #%ld%s%s.%s%s%s%s%s", - j == TAUT_INI? "Preprocessed":"Result for", num_inp, - bMobileH? ", mobile H": - bDisplayTaut==0?", fixed H":"", - /*j? ", mobile H":", fixed H",*/ - k? ", isotopic":"", - SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), iINChI? " (Reconnected)":""); -#ifndef TARGET_LIB_FOR_WINCHI - /****** Display composite Result structure **************/ - nNumIntermediateTaut += (j == TAUT_INI ); /* display TAUT_INI (preprocessed) only once */ - if ( j != TAUT_INI || nNumIntermediateTaut == 1 ) { - err = DisplayCompositeStructure( composite_norm_data, j==TAUT_INI? 1:k /* bIsotopic*/, - j/*tautomeric*/, - j==TAUT_INI? NULL:pINChI2, j==TAUT_INI? NULL:pINChI_Aux2, - ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); - } - if ( sd->bUserQuitComponentDisplay = (err==ESC_KEY) ) { - break; - } - - if ( inp_data && inp_data->nEquLabels && inp_data->nNumEquSets && !sd->bUserQuitComponentDisplay && - ((j == bCompareTaut || bCompareTaut && j == TAUT_INI) || - bCompareTaut && !composite_norm_data[bCompareTaut].bExists) && - (k == bCompareIsotopic || - bCompareIsotopic && !composite_norm_data[j].bHasIsotopicLayer) ) { - AT_NUMB nEquSet; - int bDisplaySaved = ip->bDisplay; - /****** Display Equ Sets of composite Result structure **************/ - for ( nEquSet = 1; nEquSet <= inp_data->nNumEquSets; nEquSet ++ ) { - sprintf( szTitle, "Equ set %d of %d, %s Structure #%ld%s%s.%s%s%s%s%s", - nEquSet, inp_data->nNumEquSets, - j == TAUT_INI? "Preprocessed":"Result for", - num_inp, - (bDisplayTaut>0 && nNumTautComponents)? ", mobile H": bDisplayTaut==0?", fixed H":"", - /*j? ", mobile H":", fixed H",*/ - k? ", isotopic":"", - SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), iINChI? " (Reconnected)":""); - ip->dp.nEquLabels = inp_data->nEquLabels; - ip->dp.nCurEquLabel = nEquSet; - ip->dp.nNumEquSets = inp_data->nNumEquSets; - ip->bDisplay = 1; /* force display if it was not requested */ - err = DisplayCompositeStructure( composite_norm_data, k, j, - pINChI2, pINChI_Aux2, - ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); - ip->dp.nEquLabels = NULL; - ip->dp.nCurEquLabel = 0; - ip->dp.nNumEquSets = 0; - ip->bDisplay = bDisplaySaved; /* restore display option */ - - if ( sd->bUserQuitComponentDisplay = (err==ESC_KEY) ) { - break; - } - } - } -#else - if(DRAWDATA && j <= TAUT_YES) - { - struct DrawData vDrawData; - vDrawData.pWindowData = CreateWinDataComposite_( composite_norm_data, k, j, - pINChI2, pINChI_Aux2, - ip->bAbcNumbers, &ip->dp, ip->nMode); - /* vDrawData.pWindowData = CreateWinData_( composite_norm_data[j].at, composite_norm_data[j].num_at, - k, j, pINChI[i], pINChI_Aux[i],ip->bAbcNumbers, &ip->dp, ip->nMode ); */ - if( vDrawData.pWindowData != NULL ) - { - int nType; - vDrawData.nComponent = 0; - if( j == 0 ) - nType = (k == 0) ? COMPONENT_BN: COMPONENT_BI; - else - nType = (k == 0) ? COMPONENT_TN: COMPONENT_TI; - vDrawData.nType = nType; - vDrawData.bReconnected = iINChI; /* 0=>main; 1=>reconnected */ - vDrawData.szTitle = _strdup(szTitle); - vDrawData.pWindowData->szTitle = _strdup(szTitle); - if ( inp_data && inp_data->nEquLabels && inp_data->nNumEquSets && - (j == bCompareTaut || bCompareTaut && !composite_norm_data[bCompareTaut].bExists) && - (k == bCompareIsotopic || bCompareIsotopic && !composite_norm_data[j].bHasIsotopicLayer) && - (vDrawData.pWindowData->nEquLabels = (AT_NUMB *)inchi_calloc(inp_data->num_inp_atoms, - sizeof(inp_data->nEquLabels[0])))) { - memcpy( vDrawData.pWindowData->nEquLabels, inp_data->nEquLabels, - inp_data->num_inp_atoms * sizeof(inp_data->nEquLabels[0])); - vDrawData.pWindowData->nNumEquSets = inp_data->nNumEquSets; - vDrawData.pWindowData->nCurEquLabel = 0; - } - DRAWDATA(&vDrawData); - } - } else - if(DRAWDATA && GET_DRAWDATA && j == TAUT_INI) - { - struct DrawData vDrawData; - struct DrawData *pDrawData; - - if ( !(ip->bCompareComponents & CMP_COMPONENTS) || - (ip->bCompareComponents & CMP_COMPONENTS_NONTAUT) || - !k != !composite_norm_data[j].bHasIsotopicLayer ) { - - continue; - } - /* - vDrawData.pWindowData = CreateWinDataComposite_( composite_norm_data, k, j, - pINChI2, pINChI_Aux2, - ip->bAbcNumbers, &ip->dp, ip->nMode); - */ - vDrawData.pWindowData = CreateWinDataComposite_( composite_norm_data, 1 /*k*/, j, - NULL, NULL, - ip->bAbcNumbers, &ip->dp, ip->nMode); - if( vDrawData.pWindowData != NULL ) - { - int nType = COMPONENT_ORIGINAL_PREPROCESSED; - pDrawData = GET_DRAWDATA( 0, nType, iINChI); - if ( pDrawData ) { - FreeDrawData( pDrawData ); - pDrawData->pWindowData = vDrawData.pWindowData; - vDrawData.pWindowData = NULL; - } else { - pDrawData = &vDrawData; - } - - /* vDrawData.pWindowData = CreateWinData_( composite_norm_data[j].at, composite_norm_data[j].num_at, - k, j, pINChI[i], pINChI_Aux[i],ip->bAbcNumbers, &ip->dp, ip->nMode ); */ - pDrawData->nComponent = 0; - pDrawData->nType = nType; - pDrawData->bReconnected = iINChI; /* 0=>main; 1=>reconnected */ - pDrawData->szTitle = _strdup(szTitle); - pDrawData->pWindowData->szTitle = _strdup(szTitle); - if ( inp_data && inp_data->nEquLabels && inp_data->nNumEquSets && - /*(j == bCompareTaut || bCompareTaut && !composite_norm_data[bCompareTaut].bExists) &&*/ - /*(k == bCompareIsotopic || bCompareIsotopic && !composite_norm_data[j].bHasIsotopicLayer) &&*/ - (pDrawData->pWindowData->nEquLabels = (AT_NUMB *)inchi_calloc(inp_data->num_inp_atoms, - sizeof(inp_data->nEquLabels[0])))) { - memcpy( pDrawData->pWindowData->nEquLabels, inp_data->nEquLabels, - inp_data->num_inp_atoms * sizeof(inp_data->nEquLabels[0])); - pDrawData->pWindowData->nNumEquSets = inp_data->nNumEquSets; - pDrawData->pWindowData->nCurEquLabel = 0; - } - if ( pDrawData == &vDrawData ) { - DRAWDATA(pDrawData); /* there was no prepocessed structure */ - } - } - } -#endif - } - } - } - return err; -} - -#endif /* }COMPILE_ANSI_ONLY */ - - - -/***********************************************************************************/ -/* pINChI[INCHI_BAS] refers to either disconnected or original structure; */ -/* num_components[INCHI_BAS] > 0 if there was input structure */ -/***********************************************************************************/ -/* pINChI[INCHI_REC] refers to the reconnected structure, */ -/* and only if the input structure has been disconnected, that is,*/ -/* num_components[INCHI_REC] > 0 */ -/***********************************************************************************/ -int SortAndPrintINChI(INCHI_IOSTREAM *output_file, - char *pStr, int nStrLen, - INCHI_IOSTREAM *log_file, - INPUT_PARMS *ip, - ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data, - COMP_ATOM_DATA composite_norm_data[INCHI_NUM][TAUT_NUM+1], - ORIG_STRUCT *pOrigStruct, int num_components[INCHI_NUM], - int num_non_taut[INCHI_NUM], int num_taut[INCHI_NUM], - INCHI_MODE bTautFlags[INCHI_NUM], INCHI_MODE bTautFlagsDone[INCHI_NUM], - NORM_CANON_FLAGS *pncFlags, long num_inp, - PINChI2 *pINChI[INCHI_NUM], - PINChI_Aux2 *pINChI_Aux[INCHI_NUM], - int *pSortPrintINChIFlags, unsigned char save_opt_bits) -{ - INCHI_SORT *pINChISort[INCHI_NUM][TAUT_NUM]; - int j, i, k, k1, ret, iINChI, max_num_components; - INCHI_MODE nMode; - int bDisconnectedCoord = (0 != (bTautFlagsDone[0] & TG_FLAG_DISCONNECT_COORD_DONE)); - int bINChIOutputOptions0, bCurOption, bINChIOutputOptionsCur, bEmbedReconnected, bAnnInXmlBrackets; - static const char szAnnHdr[] = "InChI ANNOTATED CONTENTS"; - int ikflag = 0; - - ret = 1; - for ( i = 0; i < INCHI_NUM; i ++ ) { - for ( k = 0; k < TAUT_NUM; k ++ ) { - bTautFlags[i] |= pncFlags->bTautFlags[i][k]; - bTautFlagsDone[i] |= pncFlags->bTautFlagsDone[i][k]; - } - } - nMode = ip->nMode; - if ( !(nMode & (REQ_MODE_BASIC|REQ_MODE_TAUT)) ) { - nMode |= (REQ_MODE_BASIC|REQ_MODE_TAUT); - } - - max_num_components = 0; - for ( j = 0; j < INCHI_NUM; j ++ ) { - if ( max_num_components < num_components[j] ) - max_num_components = num_components[j]; - } - if ( max_num_components <= 0 ) - max_num_components = 1; - - for ( j = 0, i = 0; j < INCHI_NUM; j ++ ) { - if ( num_components[j] ) { - for ( k1 = 0; k1 < TAUT_NUM; k1 ++ ) { - pINChISort[j][k1] = (INCHI_SORT *)inchi_calloc(max_num_components, sizeof(pINChISort[0][0][0]) ); - i += !pINChISort[j][k1]; /* number of failed allocatons */ - } - } else { - for ( k1 = 0; k1 < TAUT_NUM; k1 ++ ) { - pINChISort[j][k1] = NULL; /* keep BC happy */ - } - } - } - if ( i ) { - ret = CT_OUT_OF_RAM; - goto exit_function; - } - - - for ( j = 0; j < INCHI_NUM; j ++ ) { - - if ( !num_components[j] ) { - continue; - } - - iINChI = j; - -#if ( OUTPUT_CONNECTED_METAL_ONLY == 1 ) /* test: output connected as the only one INChI */ - if ( INCHI_BAS == j && num_components[INCHI_REC] ) { - j = INCHI_REC; - } -#endif - - /*j = INCHI_BAS; <- for debug only */ - /* for only normal or disconnected coord compounds */ - /* (j=0=INCHI_BAS => normal or disconnected, j=1=INCHI_REC => reconnected */ - for ( k1 = 0; k1 < TAUT_NUM; k1 ++ ) { - for ( i = 0; i < num_components[j]; i ++ ) { - for ( k = 0; k < TAUT_NUM; k ++ ) { - pINChISort[j][k1][i].pINChI[k] = pINChI[j][i][k]; - pINChISort[j][k1][i].pINChI_Aux[k] = pINChI_Aux[j][i][k]; - } - pINChISort[j][k1][i].ord_number = i; - } - } - /* sort component INChIs */ - for ( k1 = 0; k1 < TAUT_NUM; k1 ++ ) { - switch ( k1 ) { - case TAUT_NON: - qsort( pINChISort[j][k1], num_components[j], sizeof(pINChISort[0][0][0]), CompINChINonTaut2 ); - break; - case TAUT_YES: - qsort( pINChISort[j][k1], num_components[j], sizeof(pINChISort[0][0][0]), CompINChITaut2 ); - break; - } - } -#ifndef COMPILE_ANSI_ONLY -/* find equivalent and wINChI display order; use requested in ip->bCompareComponents comparison */ - ret = SaveEquComponentsInfoAndSortOrder ( iINChI, pINChISort[j], num_components, orig_inp_data, prep_inp_data, -#if ( FIX_DALKE_BUGS == 1 ) - composite_norm_data? composite_norm_data[j]:NULL, -#else - composite_norm_data[j], -#endif - ip->bCompareComponents ); - if ( RETURNED_ERROR( ret ) ) { - ret = 0; - goto exit_function; - } else { - ret = 1; - } -#endif - } - - if ( !( ip->bINChIOutputOptions & INCHI_OUT_PRINT_OPTIONS ) ) { - /* prepare InChI from the structures obtained by reversing InChI for returning to the caller */ - for ( j = 0; j < INCHI_NUM; j ++ ) { - if ( !num_components[j] ) { - continue; - } - /* pINChI[iINCHI][iComponent][bTaut] */ - /* j = disconnected/connected */ - /* k1 = sort order for Mobile or Fixed H */ - k1 = TAUT_YES; /* in Mobile H order */ - /* store components in Mobile H order */ - - for ( i = 0; i < num_components[j]; i ++ ) { - - if ( pINChISort[j][k1][i].pINChI[TAUT_NON] && - !pINChISort[j][k1][i].pINChI[TAUT_YES] ) { - /* make sure Mobile-H is always present */ - for ( k = 0; k < TAUT_NUM; k ++ ) { - pINChI[j][i][k] = pINChISort[j][k1][i].pINChI[ALT_TAUT(k)]; - pINChI_Aux[j][i][k] = pINChISort[j][k1][i].pINChI_Aux[ALT_TAUT(k)]; - } - } else { - - for ( k = 0; k < TAUT_NUM; k ++ ) { - pINChI[j][i][k] = pINChISort[j][k1][i].pINChI[k]; - pINChI_Aux[j][i][k] = pINChISort[j][k1][i].pINChI_Aux[k]; - } - } - } - } - - } else { - - /* print inchi string(s) */ - - - bINChIOutputOptions0 = ip->bINChIOutputOptions & ~INCHI_OUT_PRINT_OPTIONS; - - bEmbedReconnected = ip->bINChIOutputOptions & INCHI_OUT_EMBED_REC; - - for ( i = 0; i < 4; i ++ ) { - switch( i ) { - case 0: - bCurOption = INCHI_OUT_XML; - break; - case 1: - bCurOption = INCHI_OUT_PLAIN_TEXT; - break; - case 2: - bCurOption = INCHI_OUT_PLAIN_TEXT_COMMENTS; - break; - case 3: - bCurOption = INCHI_OUT_XML_TEXT_COMMENTS; - break; - default: - continue; - } - if ( ip->bINChIOutputOptions & bCurOption ) { - bAnnInXmlBrackets = 0; - if ( i == 1 ) { - ;/*bEmbedReconnected = 0;*/ - } - if ( i == 3 ) { - bCurOption = INCHI_OUT_XML; /* xml output as annotation */ - } - bINChIOutputOptionsCur = bINChIOutputOptions0 | bCurOption; - switch ( i ) { - case 0: - case 1: - /* output INChI */ - bINChIOutputOptionsCur |= bEmbedReconnected; - break; - case 2: - case 3: - /* output annotation */ - bAnnInXmlBrackets = (i == 2 && (ip->bINChIOutputOptions & INCHI_OUT_XML )); - if ( bAnnInXmlBrackets ) { - inchi_ios_print( output_file, "\n<%s>\n", szAnnHdr ); - } else { - inchi_ios_print( output_file, "\n==== %s ====\n", szAnnHdr ); - } - bINChIOutputOptionsCur |= bEmbedReconnected; - bINChIOutputOptionsCur &= ~INCHI_OUT_TABBED_OUTPUT; - break; - default: - continue; - } - -#ifdef TARGET_LIB_FOR_WINCHI - if ( ikflag==0 ) - output_file->type = INCHI_IOSTREAM_STRING; -#endif - - - ret &= OutputINChI2(pStr, nStrLen, - pINChISort, INCHI_BAS /*iINChI*/, - pOrigStruct, - bDisconnectedCoord, OUT_TN, - bINChIOutputOptionsCur, - 0 != (bINChIOutputOptionsCur & INCHI_OUT_XML), - ip->bAbcNumbers, ip->bCtPredecessors, - ip->bNoStructLabels, - num_components, num_non_taut, num_taut, - output_file, log_file, num_inp, - ip->pSdfLabel,ip->pSdfValue, ip->lSdfId, - pSortPrintINChIFlags, - save_opt_bits); - - if ( ret && !(bINChIOutputOptionsCur & INCHI_OUT_EMBED_REC) ) - { - ret &= OutputINChI2(pStr, nStrLen, pINChISort, INCHI_REC /*iINChI*/, - pOrigStruct, - bDisconnectedCoord, OUT_TN, - bINChIOutputOptionsCur, - 0 != (bINChIOutputOptionsCur & INCHI_OUT_XML), - ip->bAbcNumbers, ip->bCtPredecessors, - ip->bNoStructLabels, - num_components, num_non_taut, num_taut, - output_file, log_file, num_inp, - ip->pSdfLabel,ip->pSdfValue, ip->lSdfId, - pSortPrintINChIFlags, - save_opt_bits); - } - -#ifdef TARGET_LIB_FOR_WINCHI - /* always calculate InChIKey */ - ikflag++; - if (ikflag==1) - { - if (ret) - { - char ik_string[256]; /*^^^ Resulting InChIKey string */ - int ik_ret=0; /*^^^ InChIKey-calc result code */ - int xhash1, xhash2; - char szXtra1[256], szXtra2[256]; - size_t slen = output_file->s.nUsedLength; - char *buf = NULL; - extract_inchi_substring(&buf, output_file->s.pStr, slen); - inchi_ios_flush(output_file); - output_file->type = INCHI_IOSTREAM_FILE; - /* calculate and print InChIKey */ - if (NULL!=buf) - { - xhash1 = xhash2 = 0; - if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1 ) || - ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) - xhash1 = 1; - if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA2 ) || - ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) - xhash2 = 1; - ik_ret = GetINCHIKeyFromINCHI(buf, xhash1, xhash2, ik_string, szXtra1, szXtra2); - inchi_free(buf); - } - else - ik_ret = 3; - - if (ik_ret==INCHIKEY_OK) - { - /* NB: correctly treat tabbed output with InChIKey & hash extensions */ - char csep = '\n'; - if ( ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT ) - csep = '\t'; - inchi_ios_print(output_file, "InChIKey=%-s",ik_string); - if ( xhash1 ) - inchi_ios_print(output_file, "%cXHash1=%-s",csep,szXtra1); - if ( xhash2 ) - inchi_ios_print(output_file, "%cXHash2=%-s",csep,szXtra2); - inchi_ios_print(output_file, "\n"); - } - else - { - inchi_ios_print(log_file, "Warning (Could not compute InChIKey: ", num_inp); - } - - /*inchi_ios_flush(output_file); - inchi_ios_flush2(log_file, stderr);*/ - } - else - { - inchi_ios_flush(output_file); - output_file->type = INCHI_IOSTREAM_FILE; - } - - } - -#endif - - if ( bAnnInXmlBrackets ) { - inchi_ios_print( output_file, "\n\n", szAnnHdr ); - } - if ( !ret ) { - break; - } - } - } - } - -exit_function: - for ( j = 0; j < INCHI_NUM; j ++ ) { - for ( k1 = 0, i = 0; k1 < TAUT_NUM; k1 ++ ) { - if ( pINChISort[j][k1] ) { - inchi_free( pINChISort[j][k1] ); - } - } - } - ret = ret? 0 : _IS_FATAL; - - - - return ret; -} -/**********************************************************************************/ -void FreeAllINChIArrays( PINChI2 *pINChI[INCHI_NUM], PINChI_Aux2 *pINChI_Aux[INCHI_NUM], int num_components[INCHI_NUM] ) -{ - int k; - for ( k = 0; k < INCHI_NUM; k ++ ) { - FreeINChIArrays( pINChI[k], pINChI_Aux[k], num_components[k] ); - num_components[k] = 0; - if ( pINChI[k] ) { - inchi_free( pINChI[k] ); - pINChI[k] = NULL; - } - if ( pINChI_Aux[k] ) { - inchi_free( pINChI_Aux[k] ); - pINChI_Aux[k] = NULL; - } - - } -} -/**********************************************************************************/ -void FreeINChIArrays( PINChI2 *pINChI, PINChI_Aux2 *pINChI_Aux, int num_components ) -{ - int i, k; - /* release allocated memory */ - if ( pINChI ) { - for ( i = 0; i < num_components; i ++ ) { - for ( k = 0; k < TAUT_NUM; k ++ ) { - Free_INChI( &pINChI[i][k] ); - /* - inchi_free( pINChI[i][k] ); - pINChI[i][k] = NULL; - */ - } - } - } - if ( pINChI_Aux ) { - for ( i = 0; i < num_components; i ++ ) { - for ( k = 0; k < TAUT_NUM; k ++ ) { - Free_INChI_Aux( &pINChI_Aux[i][k] ); - /* - inchi_free( pINChI_Aux[i][k] ); - pINChI_Aux[i][k] = NULL; - */ - } - } - } -} - - -/********************************************** - * output " L=V" or " L missing" or "" - * The fprintf format string must contain %s%s%s%s - */ - -const char gsMissing[] = "is missing"; -const char gsEmpty[] = ""; -const char gsSpace[] = " "; -const char gsEqual[] = "="; - -#ifndef TARGET_API_LIB -/*********************************************************************************************************/ -void SplitTime( unsigned long ulTotalTime, int *hours, int *minutes, int *seconds, int *mseconds ) -{ - *mseconds = (int)(ulTotalTime % 1000); - ulTotalTime /= 1000; - *seconds = (int)(ulTotalTime % 60); - ulTotalTime /= 60; - *minutes = (int)(ulTotalTime % 60); - ulTotalTime /= 60; - *hours = (int)(ulTotalTime); -} -/*********************************************************************************************************/ -int ReadTheStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, INCHI_IOSTREAM *inp_file, ORIG_ATOM_DATA *orig_inp_data, - /* for CML:*/ int inp_index, int *out_index ) -{ - inchiTime ulTStart; - int nRet = 0, nRet2 = 0; - int bGetOrigCoord = !(ip->bINChIOutputOptions & (INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO)); - INCHI_MODE InpAtomFlags = 0; /* reading Molfile may set FLAG_INP_AT_CHIRAL bit */ - - /* vABParityUnknown holds actual value of an internal constant signifying */ - /* unknown parity: either the same as for undefined parity (default==standard) */ - /* or a specific one (non-std; requested by SLUUD switch). */ - int vABParityUnknown = AB_PARITY_UNDF; - if ( 0 != ( ip->nMode & REQ_MODE_DIFF_UU_STEREO) ) - { - /* Make labels for unknown and undefined stereo different */ - vABParityUnknown = AB_PARITY_UNKN; - } - - memset( sd, 0, sizeof(*sd) ); - switch ( ip->nInputType ) { - case INPUT_MOLFILE: - case INPUT_SDFILE: - if ( orig_inp_data ) { - if ( ip->pSdfValue && ip->pSdfValue[0] ) { - /* Added 07-29-2003 to avoid inheriting exact value from prev. structure - and to make reference to a (bad) structure with unknown ID Value */ - char *p, *q; /* q shadows prev declaration of const char *q */ - int n; - if ( (p = strrchr( ip->pSdfValue, '+' )) && - '[' == *(p-1) && 0 < (n=strtol(p+1,&q,10)) && q[0] && ']'==q[0] && !q[1] ) { - sprintf( p+1, "%d]", n+1 ); - } else { - strcat( ip->pSdfValue, " [+1]" ); - } - } - InchiTimeGet( &ulTStart ); - sd->fPtrStart = (inp_file->f == stdin)? -1 : ftell( inp_file->f ); - /* read the original structure */ - nRet2 = MolfileToOrigAtom( inp_file->f, orig_inp_data, ip->bMergeAllInputStructures, bGetOrigCoord, ip->bDoNotAddH, - ip->pSdfLabel, ip->pSdfValue, &ip->lSdfId, &ip->lMolfileNumber, - &InpAtomFlags, &sd->nStructReadError, sd->pStrErrStruct ); - - - if ( !ip->bGetSdfileId || ip->lSdfId == 999999) ip->lSdfId = 0; - if ( !ip->bGetMolfileNumber || ip->lMolfileNumber < 0 ) ip->lMolfileNumber = 0; - sd->fPtrEnd = (inp_file->f == stdin)? -1 : ftell( inp_file->f ); - sd->ulStructTime += InchiTimeElapsed( &ulTStart ); -#if ( bRELEASE_VERSION == 0 ) - sd->bExtract |= orig_inp_data->bExtract; -#endif - /* 2004-11-16: added Molfile Chiral Flag Mode */ - /* ***************************************************************************** - * Chiral flags are set in: - * - RunICHI.c #1610 -- ReadTheStructure() -- cInChI, wInChI (here) - * - e_IchiMain.c #273 -- main() -- C example of calling InChI dll - * - inchi_dll.c #1662 -- ExtractOneStructure -- InChI dll code - *******************************************************************************/ - /* 1. Highest precedence: Chiral Flag set by the user */ - if ( ip->bChiralFlag & FLAG_SET_INP_AT_CHIRAL ) { - InpAtomFlags = FLAG_INP_AT_CHIRAL; /* forced by the user */ - } else - if ( ip->bChiralFlag & FLAG_SET_INP_AT_NONCHIRAL ) { - InpAtomFlags = FLAG_INP_AT_NONCHIRAL; /* forced by the user */ - } else - if ( (InpAtomFlags & FLAG_INP_AT_CHIRAL) && (InpAtomFlags && FLAG_INP_AT_NONCHIRAL) ) { - InpAtomFlags &= ~FLAG_INP_AT_NONCHIRAL; - } - /* save requested flags in the AuxInfo */ - sd->bChiralFlag &= ~( FLAG_INP_AT_CHIRAL | FLAG_INP_AT_NONCHIRAL ); - sd->bChiralFlag |= InpAtomFlags & ( FLAG_INP_AT_CHIRAL | FLAG_INP_AT_NONCHIRAL ); - /* quick fix: modify ip->nMode on the fly */ - /* 2. The user requested both Stereo AND Chiral flag */ - if ( (ip->nMode & REQ_MODE_CHIR_FLG_STEREO) && (ip->nMode & REQ_MODE_STEREO) ) { - if ( InpAtomFlags & FLAG_INP_AT_CHIRAL ) { - /* structure has chiral flag or the user said it is chiral */ - ip->nMode &= ~(REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO); - sd->bChiralFlag |= FLAG_INP_AT_CHIRAL; /* write AuxInfo as chiral */ - } else { - ip->nMode &= ~REQ_MODE_RACEMIC_STEREO; - ip->nMode |= REQ_MODE_RELATIVE_STEREO; - sd->bChiralFlag |= FLAG_INP_AT_NONCHIRAL; /* write AuxInfo as explicitly not chiral */ - } - } - } else { - /* read the next original structure */ - int nStructReadError=0; - if ( !ip->bMergeAllInputStructures ) { - nRet2 = MolfileToOrigAtom( inp_file->f, NULL, 0, 0, 0, - NULL, NULL, NULL, NULL, - NULL, &nStructReadError, NULL ); - if ( nRet2 <= 0 && 10 < nStructReadError && nStructReadError < 20 ) { - return _IS_EOF; - } - } else { - return _IS_EOF; - } - } - break; - case INPUT_INCHI_XML: - case INPUT_INCHI_PLAIN: - if ( orig_inp_data ) { - if ( ip->pSdfValue && ip->pSdfValue[0] ) { - /* Added 07-29-2003 to avoid inheriting exact value from prev. structure - and to make reference to a (bad) structure with unknown ID Value */ - char *p, *q; - int n; - if ( (p = strrchr( ip->pSdfValue, '+' )) && - '[' == *(p-1) && 0 < (n=strtol(p+1,&q,10)) && q[0] && ']'==q[0] && !q[1] ) { - sprintf( p+1, "%d]", n+1 ); - } else { - strcat( ip->pSdfValue, " [+1]" ); - } - } - InchiTimeGet( &ulTStart ); - sd->fPtrStart = (inp_file->f == stdin)? -1 : ftell( inp_file->f ); - /* read the original structure */ - nRet2 = INChIToOrigAtom( inp_file, orig_inp_data, ip->bMergeAllInputStructures, - bGetOrigCoord, ip->bDoNotAddH, vABParityUnknown, - ip->nInputType, ip->pSdfLabel, ip->pSdfValue, &ip->lMolfileNumber, - &InpAtomFlags, &sd->nStructReadError, sd->pStrErrStruct ); - /*if ( !ip->bGetSdfileId || ip->lSdfId == 999999) ip->lSdfId = 0;*/ - sd->fPtrEnd = (inp_file->f == stdin)? -1 : ftell( inp_file->f ); - - sd->ulStructTime += InchiTimeElapsed( &ulTStart ); -#if ( bRELEASE_VERSION == 0 ) - sd->bExtract |= orig_inp_data->bExtract; -#endif - /* 2004-11-16: added Molfile Chiral Flag Mode */ - if ( ip->bChiralFlag & FLAG_SET_INP_AT_CHIRAL ) { - InpAtomFlags = FLAG_INP_AT_CHIRAL; /* forced by the user */ - } else - if ( ip->bChiralFlag & FLAG_SET_INP_AT_NONCHIRAL ) { - InpAtomFlags = FLAG_INP_AT_NONCHIRAL; /* forced by the user */ - } else - if ( (InpAtomFlags & FLAG_INP_AT_CHIRAL) && (InpAtomFlags && FLAG_INP_AT_NONCHIRAL) ) { - InpAtomFlags &= ~FLAG_INP_AT_NONCHIRAL; - } - sd->bChiralFlag |= InpAtomFlags; /* copy chiral flag to AuxInfo */ - /* quick fix: modify ip->nMode on the fly */ - if ( (ip->nMode & REQ_MODE_CHIR_FLG_STEREO) && (ip->nMode & REQ_MODE_STEREO) ) { - if ( InpAtomFlags & FLAG_INP_AT_CHIRAL ) { - ip->nMode &= ~(REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO); - } else { - ip->nMode &= ~REQ_MODE_RACEMIC_STEREO; - ip->nMode |= REQ_MODE_RELATIVE_STEREO; - } - } - } else { - /* read the next original structure */ - int nStructReadError=0; - if ( !ip->bMergeAllInputStructures ) { - nRet2 = INChIToOrigAtom( inp_file, NULL, 0, 0, 0, 0, - ip->nInputType, NULL, NULL, NULL, NULL, &nStructReadError, NULL ); - if ( nRet2 <= 0 && 10 < nStructReadError && nStructReadError < 20 ) { - return _IS_EOF; - } - } else { - return _IS_EOF; - } - } - break; - -#if ( ADD_CMLPP == 1 ) - /* BILLY 8/6/04 */ - case INPUT_CMLFILE: - if ( orig_inp_data ) { - - InchiTimeGet( &ulTStart ); - /* - if ( inp_index >= 0 ) { - sd->fPtrStart = inp_index; - } else { - sd->fPtrStart = GetCmlStructIndex(); - } - */ - sd->fPtrStart = -1; /* disable "CopyMOLfile() for CML input files */ - sd->fPtrEnd = -1; - /* read the original structure */ - nRet = CmlfileToOrigAtom( inp_file->f, orig_inp_data, ip->bMergeAllInputStructures, - bGetOrigCoord, ip->bDoNotAddH, inp_index, out_index, - ip->pSdfLabel, ip->pSdfValue, &ip->lSdfId, - &sd->nStructReadError, sd->pStrErrStruct ); - - - sd->ulStructTime += InchiTimeElapsed( &ulTStart ); -#if ( bRELEASE_VERSION == 0 ) - sd->bExtract |= orig_inp_data->bExtract; -#endif - } else { - /* read the next original structure */ - int nStructReadError=0; - if ( !ip->bMergeAllInputStructures ) { - nRet2 = CmlfileToOrigAtom( inp_file->f, NULL, 0, 0, 0, inp_index, out_index, - NULL, NULL, NULL, &nStructReadError, NULL ); - - if ( nRet2 <= 0 && 10 < nStructReadError && nStructReadError < 20 ) { - return _IS_EOF; - } - } else { - return _IS_EOF; - } - } - break; -#endif - - default: - nRet = _IS_FATAL; /* wrong file type */ - } - return nRet; -} -#endif -/*****************************************************************************************************/ -int TreatReadTheStructureErrors( STRUCT_DATA *sd, INPUT_PARMS *ip, int nLogMask, - INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, - INCHI_IOSTREAM *prb_file, /*^^^ was: INCHI_IOSTREAM */ - ORIG_ATOM_DATA *orig_inp_data, long *num_inp, char *pStr, int nStrLen ) -{ - int nRet = _IS_OKAY; - /* End of file */ - if ( 10 < sd->nStructReadError && sd->nStructReadError < 20 ) { - if ( sd->pStrErrStruct[0] ) { - inchi_ios_eprint( log_file, "%s inp structure #%ld: End of file.%s%s%s%s \n", sd->pStrErrStruct, *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - } - inchi_ios_eprint( log_file, "End of file detected after structure #%ld. \n", *num_inp-1 ); - nRet = _IS_EOF; - goto exit_function; /* end of file */ - } - - /*(*num_inp) ++;*/ - - /* Skipping the structures */ - if ( *num_inp < ip->first_struct_number ) { - -#if ( !defined(TARGET_API_LIB) && !defined(TARGET_EXE_STANDALONE) ) -/*^^^ #ifndef TARGET_API_LIB */ - if ( log_file->f != stderr ) { - inchi_fprintf( stderr, "\rSkipping structure #%ld.%s%s%s%s...", *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue)); - } -#endif - nRet = sd->nErrorType = _IS_SKIP; - goto exit_function; - } - - sd->nErrorType = GetInpStructErrorType( ip, sd->nStructReadError, sd->pStrErrStruct, orig_inp_data->num_inp_atoms ); - - /* init xml output */ - if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) && !ip->bXmlStarted ) { - OutputINChIXmlRootStartTag( output_file ); - ip->bXmlStarted ++; - } - /* init xml structure block */ - if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) && !sd->bXmlStructStarted ) { - if ( !OutputINChIXmlStructStartTag( output_file, pStr, 1, nStrLen, ip->bNoStructLabels, - *num_inp, ip->pSdfLabel, ip->pSdfValue ) ) { - inchi_ios_eprint( log_file, "Cannot create start xml tag for structure #%ld.%s%s%s%s Terminating.\n", *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - sd->bXmlStructStarted = -1; - nRet = _IS_FATAL; - goto exit_function; - } - sd->bXmlStructStarted ++; - } - - /* Fatal error */ - if ( sd->nErrorType == _IS_FATAL ) { - if ( nLogMask & LOG_MASK_FATAL ) - inchi_ios_eprint( log_file, "Fatal Error %d (aborted; %s) inp structure #%ld.%s%s%s%s\n", - sd->nStructReadError, sd->pStrErrStruct, *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); -#if ( bRELEASE_VERSION == 1 || EXTR_FLAGS == 0 ) - if ( prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd && !ip->bSaveAllGoodStructsAsProblem ) { - CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, *num_inp); - } -#endif - /* goto exit_function; */ - } - /* Non-fatal errors: do not produce INChI */ - if ( sd->nErrorType == _IS_ERROR ) { /* 70 => too many atoms */ - if ( nLogMask & LOG_MASK_ERR ) - inchi_ios_eprint( log_file, "Error %d (no %s; %s) inp structure #%ld.%s%s%s%s\n", - sd->nStructReadError, (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY)?"Molfile":INCHI_NAME, - sd->pStrErrStruct, *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); -#if ( bRELEASE_VERSION == 1 || EXTR_FLAGS == 0 ) - if ( prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd && !ip->bSaveAllGoodStructsAsProblem) { - CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, *num_inp); - } -#endif - } - - /* Warnings: try to produce INChI */ - if ( sd->nErrorType == _IS_WARNING ) { - if ( nLogMask & LOG_MASK_WARN ) - inchi_ios_eprint( log_file, "Warning: (%s) inp structure #%ld.%s%s%s%s\n", - sd->pStrErrStruct, *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - } - - /* xml error/warning processing; close xml struct block if error */ - if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) -#ifdef TARGET_LIB_FOR_WINCHI - || (ip->bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW) && (ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) -#endif - ) { - if ( sd->nErrorType != _IS_OKAY && sd->nErrorType != _IS_WARNING ) { - sd->nErrorType = - ProcessStructError( output_file, log_file, /*sd->nStructReadError,*/ - sd->pStrErrStruct, sd->nErrorType, &sd->bXmlStructStarted, *num_inp, ip, pStr, nStrLen ); - } - } -exit_function: - if ( nRet <= _IS_OKAY && sd->nErrorType > 0 ) { - nRet = sd->nErrorType; - } - return nRet; -} -/******************************************************************************************************/ -int GetOneComponent( STRUCT_DATA *sd, INPUT_PARMS *ip, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, - INP_ATOM_DATA *inp_cur_data, - ORIG_ATOM_DATA *orig_inp_data, int i, long num_inp, char *pStr, int nStrLen ) -{ - inchiTime ulTStart; - InchiTimeGet( &ulTStart ); - CreateInpAtomData( inp_cur_data, orig_inp_data->nCurAtLen[i], 0 ); - inp_cur_data->num_at = ExtractConnectedComponent( orig_inp_data->at, orig_inp_data->num_inp_atoms, i+1, inp_cur_data->at ); - sd->ulStructTime += InchiTimeElapsed( &ulTStart ); - - /* error processing */ - if ( inp_cur_data->num_at <= 0 || orig_inp_data->nCurAtLen[i] != inp_cur_data->num_at ) { - /* log error message */ - AddMOLfileError(sd->pStrErrStruct, "Cannot extract Component"); - inchi_ios_eprint( log_file, "%s #%d structure #%ld.%s%s%s%s\n", sd->pStrErrStruct, i+1, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue)); - sd->nErrorCode = inp_cur_data->num_at < 0? inp_cur_data->num_at : (orig_inp_data->nCurAtLen[i] != inp_cur_data->num_at)? CT_ATOMCOUNT_ERR : CT_UNKNOWN_ERR; - /* num_err ++; */ - sd->nErrorType = _IS_ERROR; - if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) -#ifdef TARGET_LIB_FOR_WINCHI - || (ip->bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW) && (ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) -#endif - ) { - /* xml error message */ - sd->nErrorType = ProcessStructError( output_file, log_file, /*sd->nErrorCode,*/ sd->pStrErrStruct, - sd->nErrorType, &sd->bXmlStructStarted, num_inp, ip, pStr, nStrLen ); - } - } - return sd->nErrorType; -} -/*******************************************************************************************/ -int GetProcessingWarningsOneINChI(INChI *pINChI, INP_ATOM_DATA *inp_norm_data, char *pStrErrStruct) -{ - int j; - int nAmbiguousStereoAtoms, nAmbiguousStereoBonds; - nAmbiguousStereoAtoms = 0; - nAmbiguousStereoBonds = 0; - - if ( inp_norm_data->at ) { - for ( j = 0; j < pINChI->nNumberOfAtoms; j ++ ) { - if ( inp_norm_data->at[j].bAmbiguousStereo & (AMBIGUOUS_STEREO_ATOM | AMBIGUOUS_STEREO_ATOM_ISO) ) { - nAmbiguousStereoAtoms ++; - } - if ( inp_norm_data->at[j].bAmbiguousStereo & (AMBIGUOUS_STEREO_BOND | AMBIGUOUS_STEREO_BOND_ISO) ) { - nAmbiguousStereoBonds ++; - } - } - if ( nAmbiguousStereoAtoms ) { - AddMOLfileError(pStrErrStruct, "Ambiguous stereo:"); - AddMOLfileError(pStrErrStruct, "center(s)"); - } - if ( nAmbiguousStereoBonds ) { - AddMOLfileError(pStrErrStruct, "Ambiguous stereo:"); - AddMOLfileError(pStrErrStruct, "bond(s)"); - } - } - return (nAmbiguousStereoAtoms || nAmbiguousStereoBonds); -} -/*******************************************************************************************/ -int GetProcessingWarnings(INChI *cur_INChI[], INP_ATOM_DATA **inp_norm_data, STRUCT_DATA *sd) -{ - int i, ret = 0; - for (i = 0; i < TAUT_NUM; i ++ ) { - if ( cur_INChI[i] && cur_INChI[i]->nNumberOfAtoms>0 ) { - ret |= GetProcessingWarningsOneINChI(cur_INChI[i], inp_norm_data[i], sd->pStrErrStruct); - } - } - return ret; -} - -/*******************************************************************************************/ -int CreateOneComponentINChI( STRUCT_DATA *sd, INPUT_PARMS *ip, INP_ATOM_DATA *inp_cur_data, ORIG_ATOM_DATA *orig_inp_data, - PINChI2 *pINChI, PINChI_Aux2 *pINChI_Aux, int iINChI, - int i, long num_inp, INP_ATOM_DATA **inp_norm_data, NORM_CANON_FLAGS *pncFlags, - INCHI_IOSTREAM *log_file ) -{ - inchiTime ulTStart, ulTEnd, *pulTEnd = NULL; - int k, num_at, ret = 0; - int bOrigCoord; - INCHI_MODE bTautFlags = ip->bTautFlags; - INCHI_MODE bTautFlagsDone = (ip->bTautFlagsDone | sd->bTautFlagsDone[INCHI_BAS]); - INChI *cur_INChI[TAUT_NUM]; - INChI_Aux *cur_INChI_Aux[TAUT_NUM]; - long lElapsedTime; - /* - PINChI2 *pINChI = pINChI2[iINChI]; - PINChI_Aux2 *pINChI_Aux = pINChI_Aux2[iINChI]; - */ - InchiTimeGet( &ulTStart ); - bOrigCoord = !(ip->bINChIOutputOptions & (INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO)); - - for ( k = 0; k < TAUT_NUM; k ++ ) { - cur_INChI[k] = NULL; - cur_INChI_Aux[k] = NULL; - } - /* allocate memory for non-tautimeric (k=0) and tautomeric (k=1) results */ - for ( k = 0; k < TAUT_NUM; k ++ ) { - int nAllocMode = (k==TAUT_YES? REQ_MODE_TAUT:0) | - (bTautFlagsDone & ( TG_FLAG_FOUND_ISOTOPIC_H_DONE | - TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE ))? - (ip->nMode & REQ_MODE_ISO):0; - - if ( k==TAUT_NON && (ip->nMode & REQ_MODE_BASIC ) || - k==TAUT_YES && (ip->nMode & REQ_MODE_TAUT ) ) { - /* alloc INChI and INChI_Aux */ - cur_INChI[k] = Alloc_INChI( inp_cur_data->at, inp_cur_data->num_at, &inp_cur_data->num_bonds, - &inp_cur_data->num_isotopic, nAllocMode ); - cur_INChI_Aux[k] = Alloc_INChI_Aux( inp_cur_data->num_at, - inp_cur_data->num_isotopic, nAllocMode, bOrigCoord ); - if ( cur_INChI_Aux[k] ) { - cur_INChI_Aux[k]->bIsIsotopic = inp_cur_data->num_isotopic; - } - /* alloc memory for the output structure: non-tautomeric and tautomeric (for displaying) */ - CreateInpAtomData( inp_norm_data[k], inp_cur_data->num_at, k ); - } else { - FreeInpAtomData( inp_norm_data[k] ); - } - } - lElapsedTime = InchiTimeElapsed( &ulTStart ); - if ( ip->msec_MaxTime ) { - ip->msec_LeftTime -= lElapsedTime; - } - sd->ulStructTime += lElapsedTime; - - -/*^^^#if ( !defined( TARGET_LIB_FOR_WINCHI ) && !defined( TARGET_API_LIB ) ) */ -#if ( !defined( TARGET_LIB_FOR_WINCHI ) && !defined( TARGET_API_LIB ) && !defined(TARGET_EXE_STANDALONE) ) -#if ( TEST_RENUMB_ATOMS != 1 ) - /* log file / console output */ - if ( log_file->f && log_file->f != stderr ) { /* NULL log_file now ignored. 11-23-2005 */ - if ( ip->bDisplay ) - inchi_ios_eprint( log_file, "Component #%d structure #%ld.%s%s%s%s...\n", i+1, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - else - inchi_fprintf( stderr, "Component #%d structure #%ld.%s%s%s%s...\r", i+1, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - } -#endif -#endif - /****************************************************** - * - * Get one component canonical numberings, etc. - * - ******************************************************/ - - /* - * Create_INChI() return value: - * num_at <= 0: error code - * num_at > 0: number of atoms (excluding terminal hydrogen atoms) - * inp_norm_data[0] => non-tautomeric, inp_norm_data[1] => tautomeric - */ - InchiTimeGet( &ulTStart ); - if ( ip->msec_MaxTime ) { - ulTEnd = ulTStart; - pulTEnd = &ulTEnd; - if ( ip->msec_LeftTime > 0 ) { - InchiTimeAddMsec( pulTEnd, ip->msec_LeftTime ); - } - } - num_at = Create_INChI( cur_INChI, cur_INChI_Aux, orig_inp_data/* not used */, inp_cur_data->at, - inp_norm_data, - inp_cur_data->num_at, - ip->nMode, &bTautFlags, &bTautFlagsDone, pulTEnd, NULL, sd->pStrErrStruct); - SetConnectedComponentNumber( inp_cur_data->at, inp_cur_data->num_at, i+1 ); /* normalization alters structure component number */ - for ( k = 0; k < TAUT_NUM; k ++ ) { - if ( cur_INChI_Aux[k] && cur_INChI_Aux[k]->nNumberOfAtoms > 0 ) { - pncFlags->bNormalizationFlags[iINChI][k] |= cur_INChI_Aux[k]->bNormalizationFlags; - pncFlags->bTautFlags[iINChI][k] |= cur_INChI_Aux[k]->bTautFlags; - pncFlags->bTautFlagsDone[iINChI][k] |= cur_INChI_Aux[k]->bTautFlagsDone; - pncFlags->nCanonFlags[iINChI][k] |= cur_INChI_Aux[k]->nCanonFlags; - } - } - - /* Detect errors */ - if ( num_at < 0 ) { - sd->nErrorCode = num_at; - } else - if ( num_at == 0 ) { - sd->nErrorCode = -1; - } else - if ( cur_INChI[TAUT_NON] && cur_INChI[TAUT_NON]->nErrorCode ) { - /* non-tautomeric error */ - sd->nErrorCode = cur_INChI[TAUT_NON]->nErrorCode; - } else - if ( cur_INChI[TAUT_YES] && cur_INChI[TAUT_YES]->nErrorCode ) { - /* tautomeric error */ - sd->nErrorCode = cur_INChI[TAUT_YES]->nErrorCode; - } -#if ( bRELEASE_VERSION == 0 ) - if ( cur_INChI[TAUT_NON] ) sd->bExtract |= cur_INChI[TAUT_NON]->bExtract; - if ( cur_INChI[TAUT_YES] ) sd->bExtract |= cur_INChI[TAUT_YES]->bExtract; - if ( (TG_FLAG_TEST_TAUT3_SALTS_DONE & bTautFlagsDone) ) { - sd->bExtract |= EXTR_TEST_TAUT3_SALTS_DONE; - } -#endif - /* detect and store stereo warnings */ - if ( !sd->nErrorCode ) { - GetProcessingWarnings(cur_INChI, inp_norm_data, sd); - } - - lElapsedTime = InchiTimeElapsed( &ulTStart ); - if ( ip->msec_MaxTime ) { - ip->msec_LeftTime -= lElapsedTime; - } - sd->ulStructTime += lElapsedTime; -#ifndef TARGET_API_LIB - /* Display the results */ - if ( ip->bDisplay ) - eat_keyboard_input(); -#endif - /* a) No matter what happened save the allocated INChI pointers */ - /* save the INChI of the current component */ - - InchiTimeGet( &ulTStart ); - for ( k = 0; k < TAUT_NUM; k ++ ) { - pINChI[i][k] = cur_INChI[k]; - pINChI_Aux[i][k] = cur_INChI_Aux[k]; - - cur_INChI[k] = NULL; - cur_INChI_Aux[k] = NULL; - } - - /* b) Count one component structure and/or INChI results only if there was no error */ - /* Set inp_norm_data[j]->num_removed_H = number of removed explicit H */ - - if ( !sd->nErrorCode ) { - - /* find where the current processed structure is located */ - int cur_is_in_non_taut = (pINChI[i][TAUT_NON] && pINChI[i][TAUT_NON]->nNumberOfAtoms>0); - int cur_is_in_taut = (pINChI[i][TAUT_YES] && pINChI[i][TAUT_YES]->nNumberOfAtoms>0); - int cur_is_non_taut = cur_is_in_non_taut && 0 == pINChI[i][TAUT_NON]->lenTautomer || - cur_is_in_taut && 0 == pINChI[i][TAUT_YES]->lenTautomer; - int cur_is_taut = cur_is_in_taut && 0 < pINChI[i][TAUT_YES]->lenTautomer; - /* - sd->bTautFlags[iINChI] |= bTautFlags; - sd->bTautFlagsDone[iINChI] |= bTautFlagsDone; - */ - if ( cur_is_non_taut + cur_is_taut ) { - /* count tautomeric and non-tautomeric components of the structures */ - int j1 = cur_is_in_non_taut? TAUT_NON:TAUT_YES; - int j2 = cur_is_in_taut? TAUT_YES:TAUT_NON; - int j; - sd->num_non_taut[iINChI] += cur_is_non_taut; - sd->num_taut[iINChI] += cur_is_taut; - for ( j = j1; j <= j2; j ++ ) { - int bIsotopic = (pINChI[i][j]->nNumberOfIsotopicAtoms || - pINChI[i][j]->nNumberOfIsotopicTGroups || - pINChI[i][j]->nPossibleLocationsOfIsotopicH && pINChI[i][j]->nPossibleLocationsOfIsotopicH[0]>1); - if ( j == TAUT_YES ) { - bIsotopic |= (0 < pINChI_Aux[i][j]->nNumRemovedIsotopicH[0] + - pINChI_Aux[i][j]->nNumRemovedIsotopicH[1] + - pINChI_Aux[i][j]->nNumRemovedIsotopicH[2]); - } - inp_norm_data[j]->bExists = 1; /* j=0: non-taut exists, j=1: taut exists */ - inp_norm_data[j]->bHasIsotopicLayer = bIsotopic; - /*inp_norm_data[j]->num_removed_H = inp_norm_data[j]->num_at - num_at;*/ - } - } - } -/* - return (sd->nErrorCode==CT_OUT_OF_RAM || sd->nErrorCode==CT_USER_QUIT_ERR)? _IS_FATAL : - sd->nErrorCode? _IS_ERROR : 0; -*/ - if ( sd->nErrorCode==CT_OUT_OF_RAM || sd->nErrorCode==CT_USER_QUIT_ERR ) { - ret = _IS_FATAL; - } else - if ( sd->nErrorCode ) { - ret = _IS_ERROR; - } - lElapsedTime = InchiTimeElapsed( &ulTStart ); - if ( ip->msec_MaxTime ) { - ip->msec_LeftTime -= lElapsedTime; - } - sd->ulStructTime += lElapsedTime; - return ret; -} -/****************************************************************************************************/ -int TreatCreateOneComponentINChIError(STRUCT_DATA *sd, INPUT_PARMS *ip, ORIG_ATOM_DATA *orig_inp_data, - int i, long num_inp, - INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, - INCHI_IOSTREAM *prb_file, /*^^^ was: INCHI_IOSTREAM */ - char *pStr, int nStrLen ) -{ - if ( sd->nErrorCode ) { - AddMOLfileError(sd->pStrErrStruct, ErrMsg(sd->nErrorCode) ); - inchi_ios_eprint( log_file, "Error %d (%s) structure #%ld component %d.%s%s%s%s\n", - sd->nErrorCode, sd->pStrErrStruct, num_inp, i+1, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - sd->nErrorType = (sd->nErrorCode==CT_OUT_OF_RAM || sd->nErrorCode==CT_USER_QUIT_ERR)? _IS_FATAL : _IS_ERROR; - if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) -#ifdef TARGET_LIB_FOR_WINCHI - || (ip->bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW) && (ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) -#endif - ) { - sd->nErrorType = ProcessStructError( output_file, log_file, /*sd->nErrorCode,*/ sd->pStrErrStruct, - sd->nErrorType, &sd->bXmlStructStarted, num_inp, ip, pStr, nStrLen ); - /* save the problem structure */ - if ( prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd && !ip->bSaveAllGoodStructsAsProblem ) { - CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, num_inp); - } - } else { - /* save the problem structure */ - if ( sd->nErrorCode && prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd && !ip->bSaveAllGoodStructsAsProblem ) { - CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, num_inp); - } - } - } -/*^^^ #ifndef TARGET_API_LIB */ -#if ( !defined( TARGET_API_LIB ) && !defined(TARGET_EXE_STANDALONE) ) - /* print the logfile record */ - if ( log_file->f && log_file->f != stderr && (sd->ulStructTime >= 1000 || sd->nErrorCode) ) { - fprintf( log_file->f, "%10lu msec structure #%ld.%s%s%s%s (%d component%s, %d atom%s, error=%d).\n", - sd->ulStructTime, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), - orig_inp_data->num_components, orig_inp_data->num_components==1?"":"s", - orig_inp_data->num_inp_atoms, orig_inp_data->num_inp_atoms==1?"":"s", sd->nErrorCode ); - } -#endif - return sd->nErrorType; -} -/****************************************************************************************************/ -int TreatCreateINChIWarning(STRUCT_DATA *sd, INPUT_PARMS *ip, ORIG_ATOM_DATA *orig_inp_data, long num_inp, - INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, - INCHI_IOSTREAM *prb_file, /*^^^ was: INCHI_IOSTREAM */ - char *pStr, int nStrLen ) -{ -#if ( bRELEASE_VERSION == 0 && (EXTR_FLAGS || EXTR_MASK) ) - if ( EXTR_MASK? ((sd->bExtract & EXTR_MASK) == EXTR_FLAGS) : (sd->bExtract & EXTR_FLAGS) ) { - char szMsg[64]; - sprintf( szMsg, "ExtractStruct.code=0x%X", sd->bExtract); - AddMOLfileError(sd->pStrErrStruct, szMsg); - } -#endif - if ( !sd->nErrorCode && sd->pStrErrStruct[0] ) { - inchi_ios_eprint( log_file, "Warning (%s) structure #%ld.%s%s%s%s\n", - sd->pStrErrStruct, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - sd->nErrorType = _IS_WARNING; - if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) -#ifdef TARGET_LIB_FOR_WINCHI - || (ip->bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW) && (ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) -#endif - ) { - sd->nErrorType = ProcessStructError( output_file, log_file, /*sd->nErrorCode,*/ sd->pStrErrStruct, - sd->nErrorType, &sd->bXmlStructStarted, num_inp, ip, pStr, nStrLen ); - } - /* save the structure as a problem structure if requested */ - if ( ip->bSaveWarningStructsAsProblem && !ip->bSaveAllGoodStructsAsProblem && - prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd ) { - CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, num_inp); - } -#if ( bRELEASE_VERSION == 0 ) - /* otherwise extract the structure as a problem structure if requested */ - else - if ( (EXTR_MASK? ((sd->bExtract & EXTR_MASK) == EXTR_FLAGS) : (sd->bExtract & EXTR_FLAGS)) && !ip->bSaveAllGoodStructsAsProblem && - prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd ) { - CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, num_inp); - } -#endif - } -#if ( bRELEASE_VERSION != 1 && bOUTPUT_ONE_STRUCT_TIME == 1 ) -#ifndef TARGET_API_LIB - if ( log_file && log_file != stderr ) { - fprintf( log_file, "%10lu msec structure %1dD #%ld.%s%s%s%s (%d component%s, %d atom%s, error=%d).\n", - sd->ulStructTime, orig_inp_data->num_dimensions, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), - orig_inp_data->num_components, orig_inp_data->num_components==1?"":"s", - orig_inp_data->num_inp_atoms, orig_inp_data->num_inp_atoms==1?"":"s", sd->nErrorCode ); - } -#else - if ( log_file ) { - inchi_ios_eprint( log_file, "%10lu msec structure %1dD #%ld.%s%s%s%s (%d component%s, %d atom%s, error=%d).\n", - sd->ulStructTime, orig_inp_data->num_dimensions, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), - orig_inp_data->num_components, orig_inp_data->num_components==1?"":"s", - orig_inp_data->num_inp_atoms, orig_inp_data->num_inp_atoms==1?"":"s", sd->nErrorCode ); - } -#endif -#endif - return sd->nErrorType; -} -/*******************************************************************************************/ -int DuplicateOrigAtom( ORIG_ATOM_DATA *new_orig_atom, ORIG_ATOM_DATA *orig_atom ) -{ - inp_ATOM *at = NULL; - AT_NUMB *nCurAtLen = NULL; - AT_NUMB *nOldCompNumber = NULL; - - if ( new_orig_atom->at && new_orig_atom->num_inp_atoms >= orig_atom->num_inp_atoms ) { - at = new_orig_atom->at; - } else { - at = (inp_ATOM *)inchi_calloc(orig_atom->num_inp_atoms+1, sizeof(at[0])); - } - if ( new_orig_atom->nOldCompNumber && new_orig_atom->num_components >= orig_atom->num_components ) { - nCurAtLen = new_orig_atom->nCurAtLen; - } else { - nCurAtLen = (AT_NUMB *)inchi_calloc(orig_atom->num_components+1, sizeof(nCurAtLen[0])); - } - if ( new_orig_atom->nCurAtLen && new_orig_atom->num_components >= orig_atom->num_components ) { - nOldCompNumber = new_orig_atom->nOldCompNumber; - } else { - nOldCompNumber = (AT_NUMB *)inchi_calloc(orig_atom->num_components+1, sizeof(nOldCompNumber[0])); - } - - if ( at && nCurAtLen && nOldCompNumber ) { - /* copy */ - if ( orig_atom->at ) - memcpy( at, orig_atom->at, orig_atom->num_inp_atoms * sizeof(new_orig_atom->at[0]) ); - if ( orig_atom->nCurAtLen ) - memcpy( nCurAtLen, orig_atom->nCurAtLen, orig_atom->num_components*sizeof(nCurAtLen[0]) ); - if ( orig_atom->nOldCompNumber ) - memcpy( nOldCompNumber, orig_atom->nOldCompNumber, orig_atom->num_components*sizeof(nOldCompNumber[0]) ); - /* deallocate */ - if ( new_orig_atom->at && new_orig_atom->at != at ) - inchi_free( new_orig_atom->at ); - if ( new_orig_atom->nCurAtLen && new_orig_atom->nCurAtLen != nCurAtLen ) - inchi_free( new_orig_atom->nCurAtLen ); - if ( new_orig_atom->nOldCompNumber && new_orig_atom->nOldCompNumber != nOldCompNumber ) - inchi_free( new_orig_atom->nOldCompNumber ); - - *new_orig_atom = *orig_atom; - new_orig_atom->at = at; - new_orig_atom->nCurAtLen = nCurAtLen; - new_orig_atom->nOldCompNumber = nOldCompNumber; - /* data that are not to be copied */ - new_orig_atom->nNumEquSets = 0; - memset(new_orig_atom->bSavedInINCHI_LIB, 0, sizeof(new_orig_atom->bSavedInINCHI_LIB)); - memset(new_orig_atom->bPreprocessed, 0, sizeof(new_orig_atom->bPreprocessed)); - /* arrays that are not to be copied */ - new_orig_atom->szCoord = NULL; - new_orig_atom->nEquLabels = NULL; - new_orig_atom->nSortedOrder = NULL; - return 0; - } - - /* deallocate */ - if ( at && new_orig_atom->at != at ) - inchi_free( at ); - if ( nCurAtLen && new_orig_atom->nCurAtLen != nCurAtLen ) - inchi_free( nCurAtLen ); - if ( nOldCompNumber && new_orig_atom->nOldCompNumber != nOldCompNumber ) - inchi_free( nOldCompNumber ); - - return -1; /* failed */ -} -#ifndef TARGET_API_LIB -/*******************************************************************************************/ -int GetOneStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, - INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, - INCHI_IOSTREAM *prb_file, /*^^^ was: INCHI_IOSTREAM */ - ORIG_ATOM_DATA *orig_inp_data, long *num_inp, char *pStr, int nStrLen, STRUCT_FPTRS *struct_fptrs ) -{ - int nRet, inp_index, out_index, bUseFptr = (NULL != struct_fptrs); - - FreeOrigAtData( orig_inp_data ); - /* - FreeOrigAtData( orig_inp_data + 1 ); - FreeOrigAtData( orig_inp_data + 2 ); - */ - - /* added for TARGET_LIB_FOR_WINCHI early EOF detection */ - inp_index = -1; - out_index = -1; - if ( struct_fptrs ) { - if ( inp_file->f == stdin ) { - return _IS_FATAL; - } - if ( ip->nInputType == INPUT_CMLFILE ) { - bUseFptr = 0; - } - /* initially allocate or increase length of struct_fptrs->fptr array */ - if ( !struct_fptrs->fptr || struct_fptrs->len_fptr <= struct_fptrs->cur_fptr+1 ) { - INCHI_FPTR *new_fptr = (INCHI_FPTR *)inchi_calloc( struct_fptrs->len_fptr + ADD_LEN_STRUCT_FPTRS, sizeof(new_fptr[0]) ); - if ( new_fptr ) { - if ( struct_fptrs->fptr ) { - if ( struct_fptrs->len_fptr ) { - memcpy( new_fptr, struct_fptrs->fptr, struct_fptrs->len_fptr*sizeof(new_fptr[0])); - } - inchi_free( struct_fptrs->fptr ); - } else { - struct_fptrs->len_fptr = 0; - struct_fptrs->cur_fptr = 0; - struct_fptrs->max_fptr = 0; - } - struct_fptrs->len_fptr += ADD_LEN_STRUCT_FPTRS; - struct_fptrs->fptr = new_fptr; - } else { - return _IS_FATAL; /* new_fptr allocation error */ - } - } - if ( struct_fptrs->fptr[struct_fptrs->cur_fptr] == EOF ) { - return _IS_EOF; - } else { - if ( bUseFptr ) { - if( fseek( inp_file->f, struct_fptrs->fptr[struct_fptrs->cur_fptr], SEEK_SET) ) { - return _IS_FATAL; - } - if ( struct_fptrs->cur_fptr && struct_fptrs->max_fptr <= struct_fptrs->cur_fptr ) { - return _IS_FATAL; - } - } else { - inp_index = struct_fptrs->fptr[struct_fptrs->cur_fptr]; - out_index = EOF; - } - } - *num_inp = struct_fptrs->cur_fptr; /* set structure count */ - } - - nRet = ReadTheStructure( sd, ip, inp_file, orig_inp_data, inp_index, &out_index ); - - if ( !nRet ) { - /***************************************************** - * In case of no error output structure xml start tag - * output read the structure errors and warnings - *****************************************************/ - if ( ip->nInputType == INPUT_INCHI_PLAIN || ip->nInputType == INPUT_INCHI_XML || - ip->nInputType == INPUT_MOLFILE || ip->nInputType == INPUT_SDFILE) { - if ( ip->lMolfileNumber ) { - *num_inp = ip->lMolfileNumber; - } else { - *num_inp += 1; - } - } else { - *num_inp += 1; - } - nRet = TreatReadTheStructureErrors( sd, ip, LOG_MASK_ALL, inp_file, log_file, output_file, prb_file, - orig_inp_data, num_inp, pStr, nStrLen ); - } - - /************************************************************/ - /* added for TARGET_LIB_FOR_WINCHI: look ahead for end of file detection */ - /************************************************************/ - if ( struct_fptrs && struct_fptrs->fptr && struct_fptrs->fptr[struct_fptrs->cur_fptr+1] <= 0 ) { - int nRet2 = 0; - INCHI_FPTR next_fptr; - STRUCT_DATA sd2; - - if ( nRet != _IS_EOF && nRet != _IS_FATAL ) { - if ( inp_file->f == stdin || struct_fptrs->len_fptr <= struct_fptrs->cur_fptr+1 ) { - return _IS_FATAL; - } - /* get next structure fptr */ - if ( bUseFptr ) { - next_fptr = ftell( inp_file->f ); - } else { - inp_index = out_index; - out_index = EOF; - } - /* read the next structure */ - nRet2 = ReadTheStructure( &sd2, ip, inp_file, NULL, inp_index, &out_index ); - /* restore fptr to the next structure */ - if ( bUseFptr ) { - if ( next_fptr != -1L ) { - fseek( inp_file->f, next_fptr, SEEK_SET); - } - } -#if ( ADD_CMLPP == 1 ) - else { - if ( inp_index >= 0 ) { - SetCmlStructIndex( inp_index ); /* so far nothing to do */ - } - } -#endif - } else { - /* treat current fatal error as end of file */ - struct_fptrs->fptr[struct_fptrs->cur_fptr] = EOF; - } - /* next is end of file or fatal */ - if ( nRet == _IS_EOF || nRet == _IS_FATAL || - nRet2 == _IS_EOF || nRet2 == _IS_FATAL ) { - struct_fptrs->fptr[struct_fptrs->cur_fptr+1] = EOF; - } else { - struct_fptrs->fptr[struct_fptrs->cur_fptr+1] = bUseFptr? sd->fPtrEnd : inp_index; - } - - /* update struct_fptrs->max_fptr */ - if ( struct_fptrs->max_fptr <= struct_fptrs->cur_fptr+1 ) { - struct_fptrs->max_fptr = struct_fptrs->cur_fptr+2; - } - } - - switch ( nRet ) { - case _IS_EOF: - *num_inp -= 1; - case _IS_FATAL: - case _IS_ERROR: - case _IS_SKIP: - goto exit_function; - } - - /* - if ( !orig_inp_data->num_dimensions ) { - AddMOLfileError(sd->pStrErrStruct, "0D"); */ /* 0D-structure: no coordinates - } - */ - - -exit_function: - return nRet; -} -#endif -#if ( TEST_RENUMB_ATOMS == 1 ) /* { */ -/************************************************************************************************/ -int RenumberingTestInit( RENUMB_DATA *pRenumbData, INP_ATOM_DATA *inp_cur_data ) -{ - int j; - pRenumbData->ren_inp_norm_data[0] = &pRenumbData->ren_inp_norm_data1; - pRenumbData->ren_inp_norm_data[1] = &pRenumbData->ren_inp_norm_data2; - memset( pRenumbData->ren_INChI2, 0, sizeof( pRenumbData->ren_INChI2 )); - memset( pRenumbData->ren_INChI_Aux, 0, sizeof( pRenumbData->ren_INChI_Aux )); - memset( &pRenumbData->orig_inp_cur_data, 0, sizeof( pRenumbData->orig_inp_cur_data )); - memset( &pRenumbData->saved_inp_cur_data, 0, sizeof( pRenumbData->saved_inp_cur_data )); - memset( pRenumbData->ren_inp_norm_data[0], 0, sizeof( *pRenumbData->ren_inp_norm_data[0] )); - memset( pRenumbData->ren_inp_norm_data[1], 0, sizeof( *pRenumbData->ren_inp_norm_data[1] )); -#if ( TEST_RENUMB_ATOMS_SAVE_LONGEST == 1 ) - memset( &pRenumbData->longest_inp_cur_data, 0, sizeof(pRenumbData->longest_inp_cur_data)); -#endif - CopyInpAtomData( &pRenumbData->orig_inp_cur_data, inp_cur_data ); - pRenumbData->ren_counter = pRenumbData->orig_inp_cur_data.num_at * pRenumbData->orig_inp_cur_data.num_at; - srand(1); /* for reproducibility */ - rand(); /* shift to avoid prev. sequences */ - pRenumbData->nComp = 0; - /*ren_counter = 29;*/ - pRenumbData->new_ord = (AT_RANK *)inchi_calloc( pRenumbData->orig_inp_cur_data.num_at, sizeof(pRenumbData->new_ord[0]) ); - if ( pRenumbData->new_ord ) { - for ( j = 0; j < pRenumbData->orig_inp_cur_data.num_at; j ++ ) { - pRenumbData->new_ord[j] = (AT_RANK)j; - } - return 0; - } - return -1; /* out of RAM */ -} -/************************************************************************************************/ -int RenumberingTestUninit( RENUMB_DATA *pRenumbData ) -{ - FreeInpAtomData( &pRenumbData->orig_inp_cur_data ); -#if ( TEST_RENUMB_ATOMS_SAVE_LONGEST == 1 ) - FreeInpAtomData( &pRenumbData->longest_inp_cur_data ); -#endif - inchi_free( pRenumbData->new_ord ); - return 0; -} -/************************************************************************************************/ -int RenumberingTest( PINChI2 *pINChI, PINChI_Aux2 *pINChI_Aux, ORIG_ATOM_DATA *orig_inp_data, int iINChI, - RENUMB_DATA *pRenumbData, INP_ATOM_DATA *inp_cur_data, INP_ATOM_DATA **inp_norm_data, - STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *prb_file, - int i, long num_inp, NORM_CANON_FLAGS *pncFlags) -{ - int k, bLongerTime; - CopyInpAtomData( &pRenumbData->saved_inp_cur_data, inp_cur_data ); - pRenumbData->nRet2 = 0; - pRenumbData->num_taut0 = sd->num_taut[iINChI]; - pRenumbData->num_non_taut0 = sd->num_non_taut[iINChI]; - pRenumbData->ulMaxTime = 0; - while ( -- pRenumbData->ren_counter >= 0 && !pRenumbData->nRet2 ) { - pRenumbData->nComp ++; - MakeNewOrd( pRenumbData->orig_inp_cur_data.num_at, pRenumbData->new_ord ); - RenumbInpAtomData( inp_cur_data /* output*/, &pRenumbData->orig_inp_cur_data/* input*/, pRenumbData->new_ord/* input*/ ); -#if ( TEST_RENUMB_ATOMS_SAVE_LONGEST == 1 ) - CopyInpAtomData( &pRenumbData->longest_inp_cur_data, inp_cur_data ); -#endif - if ( 470 == pRenumbData->nComp ) { - int stop = 1; /* debug only */ - } - - pRenumbData->nRet2 = CreateOneComponentINChI( sd, ip, inp_cur_data, NULL /*orig_inp_data*/, - pRenumbData->ren_INChI2, pRenumbData->ren_INChI_Aux, iINChI, - 0, num_inp, pRenumbData->ren_inp_norm_data, pncFlags, log_file ); - /* - CreateOneComponentINChI( sd, ip, inp_cur_data, orig_inp_data, pINChI2[iINChI], pINChI_Aux2[iINChI], iINChI, - i, num_inp, inp_norm_data, log_file ); - */ - if ( !pRenumbData->nRet2 ) { - pRenumbData->c1 = CompareINChI( pINChI[i][TAUT_NON], pRenumbData->ren_INChI2[0][TAUT_NON], - pINChI_Aux[i][TAUT_NON], pRenumbData->ren_INChI_Aux[0][TAUT_NON]); - pRenumbData->c2 = CompareINChI( pINChI[i][TAUT_YES], pRenumbData->ren_INChI2[0][TAUT_YES], - pINChI_Aux[i][TAUT_YES], pRenumbData->ren_INChI_Aux[0][TAUT_YES]); - if ( pRenumbData->c1 || pRenumbData->c2 || pRenumbData->nRet2 ) { - - /****** the renumbering result is different ******/ - - inchi_ios_eprint( log_file, "Compare (%d,%d) %d (err=%d) %s structure #%d component %d.%s%s%s%s\n", - pRenumbData->c1, pRenumbData->c2, - pRenumbData->nComp, pRenumbData->nRet2, INCHI_NAME, - num_inp, i+1, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - for ( k = 0; k < pRenumbData->orig_inp_cur_data.num_at; k ++ ) { - inchi_ios_eprint( log_file, " %d", (int)pRenumbData->new_ord[k] ); - } - inchi_ios_eprint( log_file, "\n" ); - pRenumbData->ren_counter = 0; /* force exit */ - pRenumbData->bRenumbErr = 1000*pRenumbData->c2 + pRenumbData->c1; -#if ( TEST_RENUMB_SWITCH == 1 ) - CopyInpAtomData( &pRenumbData->longest_inp_cur_data, inp_cur_data ); - if ( pRenumbData->longest_inp_cur_data.at ) { - for ( k = 0; k < pRenumbData->longest_inp_cur_data.num_at; k ++ ) { - pRenumbData->longest_inp_cur_data.at[k].orig_at_number = k+1; /* display new atom numbers */ - } - } -#endif - } -#if ( TEST_RENUMB_ATOMS_SAVE_LONGEST == 1 ) - /* output time per this component */ - inchi_ios_eprint( stderr, "\rComp#%d str#%ld/%d%s%s%s%s Ren %d/%d n(%lu:%lu)c(%lu:%lu)...\r", - i+1, num_inp, iINChI, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), pRenumbData->nComp, pRenumbData->ren_counter+pRenumbData->nComp, - pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulNormTime, pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulCanonTime, - pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulNormTime, pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulCanonTime); -#endif - /* make sure the max. time is not overwritten */ - pRenumbData->ulCurTime0 = pRenumbData->ren_INChI_Aux[0][TAUT_NON]? - (pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulNormTime - + pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulCanonTime) : 0; - pRenumbData->ulCurTime1 = pRenumbData->ren_INChI_Aux[0][TAUT_YES]? - (pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulNormTime - + pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulCanonTime) : 0; - pRenumbData->ulCurTime = inchi_max( pRenumbData->ulCurTime0, pRenumbData->ulCurTime1 ); - - pRenumbData->ulCurTimeCanon0 = pRenumbData->ren_INChI_Aux[0][TAUT_NON]? pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulCanonTime : 0; - pRenumbData->ulCurTimeCanon1 = pRenumbData->ren_INChI_Aux[0][TAUT_YES]? pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulCanonTime : 0; - pRenumbData->ulCurTimeCanon = inchi_max( pRenumbData->ulCurTimeCanon0, pRenumbData->ulCurTimeCanon1); - - pRenumbData->ulCurTimeNorm0 = pRenumbData->ren_INChI_Aux[0][TAUT_NON]? pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulNormTime:0; - pRenumbData->ulCurTimeNorm1 = pRenumbData->ren_INChI_Aux[0][TAUT_YES]? pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulNormTime:0; - pRenumbData->ulCurTimeNorm = inchi_max( pRenumbData->ulCurTimeNorm0, pRenumbData->ulCurTimeNorm1); - - - bLongerTime = 0; - if ( pRenumbData->ulCurTime > pRenumbData->ulMaxTime ) { - pRenumbData->ulMaxTime = pRenumbData->ulCurTime; - bLongerTime = 1; - } - if ( pRenumbData->ulMaxTimeCanon > pRenumbData->ulCurTimeCanon ) { - pRenumbData->ulMaxTimeCanon = pRenumbData->ulCurTimeCanon; - bLongerTime = 1; - } - if ( pRenumbData->ulMaxTimeNorm > pRenumbData->ulCurTimeCanon ) { - pRenumbData->ulMaxTimeCanon = pRenumbData->ulCurTimeCanon; - bLongerTime = 1; - } -#if ( TEST_RENUMB_ATOMS_SAVE_LONGEST == 1 || TEST_RENUMB_SWITCH == 1 ) - if ( bLongerTime || TEST_RENUMB_SWITCH == 1 && (pRenumbData->c1 || pRenumbData->c2 || pRenumbData->nRet2) ) { - char szLine[512]; - char szValue[512]; - inchi_ios_eprint( stderr, "\n" ); - sprintf( szLine, "Comp#%d str#%ld/%d%s%s%s%s Ren %d/%d n=%lu:%lu c=%lu:%lu", - i+1, num_inp, iINChI, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), pRenumbData->nComp, pRenumbData->ren_counter+pRenumbData->nComp, - pRenumbData->ren_INChI_Aux[0][TAUT_NON]? pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulNormTime:0, - pRenumbData->ren_INChI_Aux[0][TAUT_NON]? pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulCanonTime:0, - pRenumbData->ren_INChI_Aux[0][TAUT_YES]? pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulNormTime:0, - pRenumbData->ren_INChI_Aux[0][TAUT_YES]? pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulCanonTime:0); - sprintf( szValue, "%s (c%d/s%ld/i%d, r%d/%d n=%lu:%lu c=%lu:%lu)", - (ip->pSdfValue && ip->pSdfValue[0])? ip->pSdfValue:"unk", - i+1, num_inp, iINChI, pRenumbData->nComp, pRenumbData->ren_counter+pRenumbData->nComp, - pRenumbData->ren_INChI_Aux[0][TAUT_NON]? pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulNormTime:0, - pRenumbData->ren_INChI_Aux[0][TAUT_NON]? pRenumbData->ren_INChI_Aux[0][TAUT_NON]->ulCanonTime:0, - pRenumbData->ren_INChI_Aux[0][TAUT_YES]? pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulNormTime:0, - pRenumbData->ren_INChI_Aux[0][TAUT_YES]? pRenumbData->ren_INChI_Aux[0][TAUT_YES]->ulCanonTime:0); - - WriteToSDfile( &pRenumbData->longest_inp_cur_data, prb_file, szLine, NULL, ip->pSdfLabel, szValue ); - } -#endif - -#if ( TEST_RENUMB_SWITCH == 1 ) - if ( pRenumbData->c1 || pRenumbData->c2 || !pRenumbData->ren_counter ) { - inchi_swap( (char*)&pINChI[i][TAUT_NON], (char*)&pRenumbData->ren_INChI2[0][TAUT_NON], sizeof(&pRenumbData->ren_INChI2[0][0]) ); - inchi_swap( (char*)&pINChI[i][TAUT_YES], (char*)&pRenumbData->ren_INChI2[0][TAUT_YES], sizeof(&pRenumbData->ren_INChI2[0][0]) ); - inchi_swap( (char*)&pINChI_Aux[i][TAUT_NON], (char*)&pRenumbData->ren_INChI_Aux[0][TAUT_NON], sizeof(&pRenumbData->ren_INChI_Aux[0][0]) ); - inchi_swap( (char*)&pINChI_Aux[i][TAUT_YES], (char*)&pRenumbData->ren_INChI_Aux[0][TAUT_YES], sizeof(&pRenumbData->ren_INChI_Aux[0][0]) ); - } -#endif - } - - for ( k = 0; k < TAUT_NUM; k ++ ) { - if ( pRenumbData->ren_INChI2[0][k] ) { - Free_INChI(&pRenumbData->ren_INChI2[0][k]); - /* - inchi_free(pRenumbData->ren_INChI2[0][k]); - pRenumbData->ren_INChI2[0][k] = NULL; - */ - } - if ( pRenumbData->ren_INChI_Aux[0][k] ) { - Free_INChI_Aux(&pRenumbData->ren_INChI_Aux[0][k]); - /* - inchi_free(pRenumbData->ren_INChI_Aux[0][k]); - pRenumbData->ren_INChI_Aux[0][k] = NULL; - */ - } - } - } - /* eliminate overcounting due to multiple renumberings/recalculations */ - pRenumbData->num_taut = sd->num_taut[iINChI] - pRenumbData->num_taut0; - pRenumbData->num_non_taut = sd->num_non_taut[iINChI] - pRenumbData->num_non_taut0; - sd->num_taut[iINChI] = pRenumbData->num_taut0; - sd->num_non_taut[iINChI] = pRenumbData->num_non_taut0; - if ( pRenumbData->num_taut % pRenumbData->nComp || pRenumbData->num_non_taut % pRenumbData->nComp ) { - inchi_ios_eprint( log_file, "Compare (%d,%d) %d (err=%d) %s structure #%ld component %d.%s%s%s%s\n", - pRenumbData->num_non_taut % pRenumbData->nComp, pRenumbData->num_taut % pRenumbData->nComp, - pRenumbData->nComp, 333, INCHI_NAME, num_inp, i+1, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - } -#if ( TEST_RENUMB_SWITCH == 1 ) /* { */ - CopyInpAtomData( inp_norm_data[TAUT_NON], pRenumbData->ren_inp_norm_data[TAUT_NON] ); - CopyInpAtomData( inp_norm_data[TAUT_YES], pRenumbData->ren_inp_norm_data[TAUT_YES] ); - /* renumbered input structure */ -#ifndef COMPILE_ANSI_ONLY /* { */ - if ( /*ip->bDisplayEachComponentINChI &&*/ !pRenumbData->nRet2 ) { - int err, len; - /* - err = DisplayStructure( inp_cur_data->at, inp_cur_data->num_at, 0, 1, 0, NULL. 1, 0, NULL, NULL, - ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); - */ - err = DisplayStructure( inp_cur_data->at, inp_cur_data->num_at, 0, 1, 0, NULL, 1/*isotopic*/, 0/*taut*/, NULL, NULL, - ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); - if ( pRenumbData->c1 || pRenumbData->c2 ) { - len = strlen(szTitle); - strcat( szTitle, " (Renumbered)" ); - err = DisplayStructure( pRenumbData->longest_inp_cur_data.at, pRenumbData->longest_inp_cur_data.num_at, - 0, 1, 0, NULL, 1, 0, NULL, NULL, ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); - szTitle[len] = '\0'; - } - sd->bUserQuitComponentDisplay = (err==ESC_KEY); - if ( !err ) { - inchi_ios_eprint( stderr, "Cannot display the structure\n"); - } - } -#endif /* } COMPILE_ANSI_ONLY */ -#else /* } TEST_RENUMB_SWITCH { */ - CopyInpAtomData( inp_cur_data, &pRenumbData->saved_inp_cur_data ); -#endif /* } TEST_RENUMB_SWITCH */ - FreeInpAtomData( &pRenumbData->saved_inp_cur_data ); - FreeInpAtomData( pRenumbData->ren_inp_norm_data[TAUT_NON] ); - FreeInpAtomData( pRenumbData->ren_inp_norm_data[TAUT_YES] ); -#if ( TEST_RENUMB_ATOMS_SAVE_LONGEST == 1 || TEST_RENUMB_SWITCH == 1 ) - FreeInpAtomData( &pRenumbData->longest_inp_cur_data ); -#endif - return pRenumbData->nRet2; -} -#endif /* } TEST_RENUMB_ATOMS */ - -/****************************************************************************/ -int bCheckUnusualValences( ORIG_ATOM_DATA *orig_at_data, int bAddIsoH, char *pStrErrStruct ) -{ - int i, val, num_found = 0; - char msg[32]; - int len, num_H; - inp_ATOM *at = ( orig_at_data && orig_at_data->num_inp_atoms > 0 )? orig_at_data->at : NULL; - - if ( at ) { - for ( i = 0, num_found = 0; i < orig_at_data->num_inp_atoms; i ++ ) { - num_H = bAddIsoH? NUMH(at,i) : at[i].num_H; - val = detect_unusual_el_valence( at[i].el_number, at[i].charge, at[i].radical, - at[i].chem_bonds_valence, num_H, at[i].valence ); - if ( val ) { - num_found ++; - /* produce message */ - AddMOLfileError(pStrErrStruct, "Accepted unusual valence(s):"); - len = sprintf( msg, "%s", at[i].elname ); - if ( at[i].charge ) { - len += sprintf( msg+len, "%+d", at[i].charge ); - } - if ( at[i].radical ) { - len += sprintf( msg + len, ",%s", at[i].radical == RADICAL_SINGLET? "s" : - at[i].radical == RADICAL_DOUBLET? "d" : - at[i].radical == RADICAL_TRIPLET? "t" : "?" ); - } - len += sprintf( msg + len, "(%d)", val ); - AddMOLfileError(pStrErrStruct, msg); - } - } - } - return num_found; -} -/***************************************************************************/ -int PreprocessOneStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data ) -{ - int i; - INCHI_MODE bTautFlags = 0; - INCHI_MODE bTautFlagsDone = 0; - /*************************************************/ - /* 1. copy orig_inp_data --> prep_inp_data */ - /*************************************************/ - if ( 0 > DuplicateOrigAtom( prep_inp_data, orig_inp_data ) ) { - AddMOLfileError(sd->pStrErrStruct, "Out of RAM"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_FATAL; - goto exit_function; - } -#if ( bRELEASE_VERSION == 0 && (EXTR_HAS_METAL_ATOM & (EXTR_MASK | EXTR_FLAG) ) ) - if ( bHasMetalAtom( orig_inp_data ) ) { - sd->bExtract |= EXTR_HAS_METAL_ATOM; - } -#endif - - /*************************************************/ - /* 2. fix odd things in prep_inp_data */ - /*************************************************/ - - if ( 0 < fix_odd_things( prep_inp_data->num_inp_atoms, prep_inp_data->at, /*0*/ip->bTautFlags & TG_FLAG_FIX_SP3_BUG, ip->bFixNonUniformDraw ) ) { /* changed 2010-03-17 DT */ - AddMOLfileError(sd->pStrErrStruct, "Charges were rearranged"); - if ( sd->nErrorType < _IS_WARNING ) { - sd->nErrorType = _IS_WARNING; - } - sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ODD_THINGS_DONE; - } -#if ( FIX_ADJ_RAD == 1 ) - if ( ip->bTautFlags & TG_FLAG_FIX_ADJ_RADICALS ) { - if ( 0 < FixAdjacentRadicals( prep_inp_data->num_inp_atoms, prep_inp_data->at ) ) { - sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ADJ_RADICALS_DONE; - } - } -#endif -#if ( bRELEASE_VERSION == 0 && (EXTR_FLAGS & EXTR_HAS_FEATURE) ) - if ( bFoundFeature( prep_inp_data->at, prep_inp_data->num_inp_atoms ) ) { - sd->bExtract |= EXTR_HAS_FEATURE; - } -#endif - - /******************************************************************* - * Find whether the structure can be disconnected or is a salt - *******************************************************************/ - - - /* needs salt disconnection? */ - if ( ip->bTautFlags & TG_FLAG_DISCONNECT_SALTS ) { - prep_inp_data->bDisconnectSalts = (0 < DisconnectSalts( prep_inp_data, 0 )); - } else { - prep_inp_data->bDisconnectSalts = 0; - } - /* needs metal disconnection? */ - if ( ip->bTautFlags & TG_FLAG_DISCONNECT_COORD ) { - i = (0 != (ip->bTautFlags & TG_FLAG_CHECK_VALENCE_COORD)); - bMayDisconnectMetals( prep_inp_data, i, &bTautFlagsDone ); /* changes prep_inp_data->bDisconnectCoord */ - sd->bTautFlagsDone[INCHI_BAS] |= bTautFlagsDone; /* whether any disconnection has been rejected because of the metal proper valence */ -#if ( bRELEASE_VERSION == 0 ) - if ( i && (bTautFlagsDone & TG_FLAG_CHECK_VALENCE_COORD_DONE) ) { - sd->bExtract |= EXTR_METAL_WAS_NOT_DISCONNECTED; - } -#endif - } else { - prep_inp_data->bDisconnectCoord = 0; - } - orig_inp_data->bDisconnectSalts = prep_inp_data->bDisconnectSalts; - orig_inp_data->bDisconnectCoord = prep_inp_data->bDisconnectCoord; - - /*************************************************/ - /* 3. if( orig_inp_data->bDisconnectSalts ) then */ - /* -- disconnect salts in prep_inp_data */ - /*************************************************/ - - if ( ( ip->bTautFlags & TG_FLAG_DISCONNECT_SALTS ) && prep_inp_data->bDisconnectSalts && - 0 < (i=DisconnectSalts( prep_inp_data, 1 )) ) { - AddMOLfileError(sd->pStrErrStruct, "Salt was disconnected"); - sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_DISCONNECT_SALTS_DONE; - if ( sd->nErrorType < _IS_WARNING ) { - sd->nErrorType = _IS_WARNING; - } - if ( i = ReconcileAllCmlBondParities( prep_inp_data->at, prep_inp_data->num_inp_atoms, 0 ) ) { - char szErrCode[16]; - sprintf( szErrCode, "%d", i); - AddMOLfileError( sd->pStrErrStruct, "0D Parities Reconciliation failed:" ); - AddMOLfileError( sd->pStrErrStruct, szErrCode ); - } -#if ( bRELEASE_VERSION == 0 ) - sd->bExtract |= EXTR_SALT_WAS_DISCONNECTED; -#endif - } else { - prep_inp_data->bDisconnectSalts = 0; - } - - /***********************************************************/ - /* mark the (disconnected) components in prep_inp_data */ - /***********************************************************/ - - prep_inp_data->num_components = MarkDisconnectedComponents( prep_inp_data, 0 ); - - if ( prep_inp_data->num_components < 0 ) { - AddMOLfileError(sd->pStrErrStruct, "Out of RAM"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_FATAL; - goto exit_function; - } - - /***********************************************************/ - /* Detect isotopic H on heteroatoms -- necessary condition */ - /* for global isotopic tautomerism */ - /***********************************************************/ - - if ( i = bNumHeterAtomHasIsotopicH( prep_inp_data->at, prep_inp_data->num_inp_atoms ) ) { - if ( i & 1 ) { - sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FOUND_ISOTOPIC_H_DONE; - } - if ( i & 2 ) { - sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE; - } - } - - - /****************************************************************************/ - /* 4a. Detect unusual valences */ - /* should be called before metal disconnection */ - /****************************************************************************/ - - - if ( bCheckUnusualValences( prep_inp_data, 1, sd->pStrErrStruct ) ) { -#if ( bRELEASE_VERSION == 0 ) - sd->bExtract |= EXTR_UNUSUAL_VALENCES; -#else - ; -#endif - } - /***********************************************************/ - /* 5. if( orig_inp_data->bDisconnectCoord ) then */ - /* -- copy prep_inp_data --> prep_inp_data+1 */ - /* -- disconnect metals in prep_inp_data */ - /***********************************************************/ - - if ( prep_inp_data->bDisconnectCoord ) { - - prep_inp_data->num_components = MarkDisconnectedComponents( prep_inp_data, 0 ); - if ( prep_inp_data->num_components < 0 ) { - AddMOLfileError(sd->pStrErrStruct, "Out of RAM"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_FATAL; - goto exit_function; - } - /* save Reconnected structure in prep_inp_data+1 if requested */ - if ( 0 != ( ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) { - if ( 0 > DuplicateOrigAtom( prep_inp_data+1, prep_inp_data ) ) { - AddMOLfileError(sd->pStrErrStruct, "Out of RAM"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_FATAL; - goto exit_function; - } - sd->bTautFlags[INCHI_REC] = sd->bTautFlags[INCHI_BAS]; - sd->bTautFlagsDone[INCHI_REC] = sd->bTautFlagsDone[INCHI_BAS]; - { /* remove "parity undefined in disconnected structure" flag from reconnected structure */ - int k, m, p; - inp_ATOM *at = (prep_inp_data+1)->at; - int num_at = (prep_inp_data+1)->num_inp_atoms; - for ( k = 0; k < num_at; k ++ ) { - for ( m = 0; m < MAX_NUM_STEREO_BONDS && (p=at[k].sb_parity[m]); m ++ ) { - at[k].sb_parity[m] &= SB_PARITY_MASK; - } - } - } - } - - /* make Disconnected structure in prep_inp_data */ - i = (0 != ( ip->bTautFlags & TG_FLAG_CHECK_VALENCE_COORD )); - /* prep_inp_data->bDisconnectCoord > 1 means add prep_inp_data->bDisconnectCoord-1 explicit H atoms */ - if ( 0 < (i = DisconnectMetals( prep_inp_data, i, &bTautFlagsDone ) ) ) { - AddMOLfileError(sd->pStrErrStruct, "Metal was disconnected"); - sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_DISCONNECT_COORD_DONE; - if ( sd->nErrorType < _IS_WARNING ) { - sd->nErrorType = _IS_WARNING; - } -#if ( bRELEASE_VERSION == 0 ) - sd->bExtract |= EXTR_METAL_WAS_DISCONNECTED; -#endif - /* last parm=1 means find link between unchanged by Metal Disconnection components */ - prep_inp_data->num_components = MarkDisconnectedComponents( prep_inp_data, 1 ); - if ( prep_inp_data->num_components < 0 ) { - AddMOLfileError(sd->pStrErrStruct, "Out of RAM"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_FATAL; - goto exit_function; - } - - { /* set parities for the disconnected structure */ - int k, m, p; - inp_ATOM *at = (prep_inp_data)->at; - int num_at = (prep_inp_data)->num_inp_atoms; - for ( k = 0; k < num_at; k ++ ) { - for ( m = 0; m < MAX_NUM_STEREO_BONDS && (p=at[k].sb_parity[m]); m ++ ) { - if ( p & SB_PARITY_FLAG ) { - at[k].sb_parity[m] = (p >> SB_PARITY_SHFT) & SB_PARITY_MASK; - } - } - } - } - - if ( i = ReconcileAllCmlBondParities( prep_inp_data->at, prep_inp_data->num_inp_atoms, 1 ) ) { - char szErrCode[16]; - sprintf( szErrCode, "%d", i); - AddMOLfileError( sd->pStrErrStruct, "0D Parities Reconciliation failed:" ); - AddMOLfileError( sd->pStrErrStruct, szErrCode ); - } - -#if ( REMOVE_ION_PAIRS_DISC_STRU == 1 ) - if ( 0 < remove_ion_pairs( prep_inp_data->num_inp_atoms, prep_inp_data->at ) ) { - AddMOLfileError(sd->pStrErrStruct, "Charges were rearranged"); - if ( sd->nErrorType < _IS_WARNING ) { - sd->nErrorType = _IS_WARNING; - } - sd->bTautFlagsDone[INCHI_REC] |= TG_FLAG_FIX_ODD_THINGS_DONE; - sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ODD_THINGS_DONE; - } -#endif - - /* - if prep_inp_data->nOldCompNumber[i] = iINChI+1 > 0 then - component #(i+1) in prep_inp_data is identical to component #(iINChI+1) in prep_inp_data+1 - */ - } else - if ( i < 0 ) { - AddMOLfileError(sd->pStrErrStruct, "Cannot disconnect metal error"); - sd->nStructReadError = i; - sd->nErrorType = _IS_ERROR; - goto exit_function; - } - } else - { /* remove "disconnected structure parities" from the structure */ - int k, m, p; - inp_ATOM *at = (prep_inp_data)->at; - int num_at = (prep_inp_data)->num_inp_atoms; - for ( k = 0; k < num_at; k ++ ) { - for ( m = 0; m < MAX_NUM_STEREO_BONDS && (p=at[k].sb_parity[m]); m ++ ) { - at[k].sb_parity[m] &= SB_PARITY_MASK; - } - } - } - - -exit_function: - - if ( sd->nErrorType < _IS_ERROR && prep_inp_data ) { - - if ( 0 < post_fix_odd_things( prep_inp_data->num_inp_atoms, prep_inp_data->at ) ) { - AddMOLfileError(sd->pStrErrStruct, "Charges were rearranged"); - if ( sd->nErrorType < _IS_WARNING ) { - sd->nErrorType = _IS_WARNING; - } - sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ODD_THINGS_DONE; - } - if ( (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) && - (prep_inp_data+1)->at && (prep_inp_data+1)->num_inp_atoms > 0 ) { - if ( 0 < post_fix_odd_things( (prep_inp_data+1)->num_inp_atoms, (prep_inp_data+1)->at ) ) { - AddMOLfileError(sd->pStrErrStruct, "Charges were rearranged"); - if ( sd->nErrorType < _IS_WARNING ) { - sd->nErrorType = _IS_WARNING; - } - sd->bTautFlagsDone[INCHI_REC] |= TG_FLAG_FIX_ODD_THINGS_DONE; - sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ODD_THINGS_DONE; - } - } - } - - sd->bTautFlags[INCHI_BAS] |= bTautFlags; /* TG_FLAG_CHECK_VALENCE_COORD_DONE, TG_FLAG_MOVE_CHARGE_COORD_DONE */ - sd->bTautFlagsDone[INCHI_BAS] |= bTautFlagsDone; /* TG_FLAG_CHECK_VALENCE_COORD_DONE, TG_FLAG_MOVE_CHARGE_COORD_DONE */ - return sd->nErrorType; -} - -#ifndef COMPILE_ANSI_ONLY /* { */ -/************************************************************************************************/ -int DisplayTheWholeStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, - INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, - ORIG_ATOM_DATA *orig_inp_data, long num_inp, int iINChI, int bShowStruct, int bINCHI_LIB_Flag ) -{ - - int bDisplayEqu = 0; -#ifndef TARGET_LIB_FOR_WINCHI - /* Displaying equivalent input structures when disconnection has been done: */ - /* in case of TARGET_LIB_FOR_WINCHI equivalence info is always unknown here and bOriginalReconnected=0 */ - int bOriginalReconnected = iINChI < 0 && orig_inp_data && orig_inp_data->nEquLabels && - (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) && - (ip->bTautFlags & TG_FLAG_RECONNECT_COORD); - const char *lpszType = bOriginalReconnected? " (Reconnected)" : - (iINChI < 0 )? "" : - (iINChI == INCHI_BAS )? " (Preprocessed)" : - (iINChI == INCHI_REC )? " (Reconnected)" : ""; - int err = 0; - /* Display the original structure */ - bDisplayEqu = bShowStruct && ip->bDisplay && - ip->dp.nEquLabels && 0 < ip->dp.nCurEquLabel && ip->dp.nCurEquLabel <= ip->dp.nNumEquSets; -#else - if(!DRAWDATA || !DRAWDATA_EXISTS) - return 0; -#endif -#ifndef TARGET_API_LIB - /******************************************************************** - * Ask the user whether to process the input structure or quit - */ - if ( ip->bDisplay && inp_file->f != stdin ) { - if ( user_quit(bDisplayEqu?"Enter=Display identical components, Esc=Stop ?" : "Enter=Display, Esc=Stop ?", ip->ulDisplTime) ) { - sd->bUserQuit = 1; - goto exit_function; - } - } -#endif - /****************************************************** - * Display the whole input structure in console app - */ -/*^^^ #ifndef TARGET_LIB_FOR_WINCHI */ -#if ( !defined( TARGET_LIB_FOR_WINCHI ) && !defined(TARGET_EXE_STANDALONE) ) - if ( bShowStruct && ip->bDisplay ) { - if ( bDisplayEqu ) { - sprintf( szTitle, " Equ Set %d of %d, Input Structure #%ld.%s%s%s%s%s", - ip->dp.nCurEquLabel, ip->dp.nNumEquSets, - num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), lpszType); - } else { - sprintf( szTitle, "Input Structure #%ld.%s%s%s%s%s", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), lpszType); - } - err = DisplayStructure( orig_inp_data->at, orig_inp_data->num_inp_atoms, 0, 1, 0, NULL, 1/*isotopic*/, 0/*taut*/, NULL, NULL, - ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); - sd->bUserQuitComponent = (err==ESC_KEY); - if ( !err ) { - inchi_fprintf( stderr, "Cannot display the structure\n"); - } - } - if( !bDisplayEqu ) { - /* console output progress report */ - if ( ip->bDisplay && !sd->bUserQuitComponent ) { - if ( iINChI == 1 ) { - if ( ip->bDisplay ) - inchi_ios_eprint( log_file, "Processing (rec) structure #%ld.%s%s%s%s...\n", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - else - inchi_fprintf( stderr, "Processing (rec) structure #%ld.%s%s%s%s...\r", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - } else { - if ( ip->bDisplay ) - inchi_ios_eprint( log_file, "Processing structure #%ld.%s%s%s%s...\n", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - else - inchi_fprintf( stderr, "Processing structure #%ld.%s%s%s%s...\r", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - } - } - } -#endif - - - /****************************************************** - * Store the whole input structure in GUI application - */ -#ifdef TARGET_LIB_FOR_WINCHI - if ( ip->bDisplay && bINCHI_LIB_Flag ) -#else - if ( (ip->bDisplay || (ip->bCompareComponents & CMP_COMPONENTS)) && bINCHI_LIB_Flag ) -#endif - { - int bBit, k, bReconnected, nComponent, bPreprocessed; - for ( bBit = 1, k = 0; k < 8; k ++, bBit <<= 1 ) { - /****************************************************************************** - * bReconnected = k%2 (0 or 1) - * nComponent = k/4 (0 or 1) - * bPreprocessed = (k/2)%2 (0 or 1) - ******************************************************************************/ - if ( !(bINCHI_LIB_Flag & bBit) ) { - continue; - } - bReconnected = k%2; - nComponent = k/4; - bPreprocessed = ((k/2)%2); - - sprintf( szTitle, "%s Structure #%ld.%s%s%s%s", - bPreprocessed? "Preprocessed" : bReconnected? "Reconnected" : "Input", - num_inp, - SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue)); - -#ifdef TARGET_LIB_FOR_WINCHI - if(DRAWDATA && DRAWDATA_EXISTS) - { - struct DrawData vDrawData; - int nType = bPreprocessed? COMPONENT_ORIGINAL_PREPROCESSED : COMPONENT_ORIGINAL; - if ( DRAWDATA_EXISTS( nComponent, bPreprocessed, bReconnected ) ) { - sd->nErrorType = _IS_FATAL; - sd->nErrorCode = CT_UNKNOWN_ERR; - return -1; - } - vDrawData.pWindowData = CreateWinData_( orig_inp_data->at, orig_inp_data->num_inp_atoms, - 0, 1 /* bAdd_DT_to_num_H */, 0, NULL, 1, 0, NULL, NULL, - ip->bAbcNumbers, &ip->dp, ip->nMode ); - if( vDrawData.pWindowData != NULL ) - { - vDrawData.nComponent = nComponent; - vDrawData.nType = nType; /* COMPONENT_ORIGINAL or COMPONENT_ORIGINAL_PREPROCESSED */ - vDrawData.bReconnected = bReconnected; /* 0=>main; 1=>reconnected */ - vDrawData.pWindowData->szTitle = _strdup(szTitle); - vDrawData.szTitle = _strdup(szTitle); - DRAWDATA(&vDrawData); - if ( !nComponent ) { - /* keep track of saved INCHI_LIB data */ - orig_inp_data->bSavedInINCHI_LIB[bReconnected] ++; - orig_inp_data->bPreprocessed[bReconnected] = bPreprocessed; - } - } - } -#else - if ( !nComponent ) { - /* keep track of saved INCHI_LIB data */ - orig_inp_data->bSavedInINCHI_LIB[bReconnected] ++; - orig_inp_data->bPreprocessed[bReconnected] = bPreprocessed; - } -#endif - - } - } - -exit_function: - return sd->bUserQuit; -} -#endif /* } COMPILE_ANSI_ONLY */ -/************************************************************************************************/ -int ProcessOneStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, - PINChI2 *pINChI[INCHI_NUM], PINChI_Aux2 *pINChI_Aux[INCHI_NUM], - INCHI_IOSTREAM *inp_file, - INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *prb_file, /*^^^ was: INCHI_IOSTREAM */ - ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data, - long num_inp, char *pStr, int nStrLen, - unsigned char save_opt_bits) -{ - int nRet = 0, nRet1, i, k, maxINChI=0; - COMP_ATOM_DATA composite_norm_data[INCHI_NUM][TAUT_NUM+1]; /* [0]:non-taut, [1]:taut, [2]:intermediate taut struct */ - NORM_CANON_FLAGS ncFlags; - NORM_CANON_FLAGS *pncFlags = &ncFlags; - ORIG_STRUCT OrigStruct; - ORIG_STRUCT *pOrigStruct = NULL; - int bSortPrintINChIFlags=0; - - -#if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) - int ret1=0, ret2=0; -#endif - sd->bUserQuitComponent = 0; - sd->bUserQuitComponentDisplay = 0; - memset( composite_norm_data, 0, sizeof(composite_norm_data) ); - memset( pncFlags, 0, sizeof(*pncFlags) ); - /* ip->msec_LeftTime = ip->msec_MaxTime; */ /* start timeout countdown */ - - /* for testing only */ -#if ( REMOVE_ION_PAIRS_ORIG_STRU == 1 ) - fix_odd_things( orig_inp_data->num_inp_atoms, orig_inp_data->at, 0, ip->bFixNonUniformDraw ); -#endif - -#if ( UNDERIVATIZE == 1 ) /***** post v.1 feature *****/ - if ( ip->bUnderivatize && 0 > (ret2=underivatize( orig_inp_data )) ) { - long num_inp2 = num_inp; - AddMOLfileError(sd->pStrErrStruct, "Underivatization error"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_ERROR; - nRet = _IS_ERROR; - TreatReadTheStructureErrors( sd, ip, LOG_MASK_ALL, inp_file, log_file, output_file, prb_file, - prep_inp_data, &num_inp2, pStr, nStrLen ); - goto exit_function; /* output only if derivatives found */ - } -#endif /* UNDERIVATIZE == 1 */ -#if ( RING2CHAIN == 1 ) /***** post v.1 feature *****/ - if ( ip->bRing2Chain && 0 > (ret1 = Ring2Chain( orig_inp_data )) ) { - long num_inp2 = num_inp; - AddMOLfileError(sd->pStrErrStruct, "Ring to chain error"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_ERROR; - nRet = _IS_ERROR; - TreatReadTheStructureErrors( sd, ip, LOG_MASK_ALL, inp_file, log_file, output_file, prb_file, - prep_inp_data, &num_inp2, pStr, nStrLen ); - goto exit_function; /* output only if derivatives found */ - } -#endif /* RING2CHAIN == 1 */ -#if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) /***** post v.1 feature *****/ - if ( ip->bIngnoreUnchanged && !ret1 && !ret2 ) { - goto exit_function; /* output only if derivatives or ring/chain found */ - } -#endif /* RING2CHAIN == 1 || UNDERIVATIZE == 1 */ - - - /***** output MOLfile ***************/ - if ( ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY ) { - char szNumber[32]; - int ret1a=0, ret2a=0; /* for derivatives and ring-chain */ -/*^^^ #if ( !defined( TARGET_LIB_FOR_WINCHI ) && !defined( TARGET_API_LIB ) ) */ -#if ( !defined( TARGET_LIB_FOR_WINCHI ) && !defined( TARGET_API_LIB ) && !defined(TARGET_EXE_STANDALONE) ) -#if ( TEST_RENUMB_ATOMS != 1 ) - /* log file / console output */ - if ( log_file->f != stderr ) { - if ( ip->bDisplay ) - inchi_ios_eprint( log_file, "Writing structure #%ld.%s%s%s%s...\n", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - else - inchi_fprintf( stderr, "Writing structure #%ld.%s%s%s%s...\r", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - } -#endif -#endif - ret1a = sprintf( szNumber, "Structure #%ld", num_inp ); - ret2a = WriteOrigAtomDataToSDfile( orig_inp_data, output_file, szNumber, NULL, - (sd->bChiralFlag & FLAG_INP_AT_CHIRAL)? 1:0, - (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ATOMS_DT)? 1:0, ip->pSdfLabel, ip->pSdfValue ); - goto exit_function; - } - - /******* create full reversibility information **************/ - if ( !(ip->bINChIOutputOptions & (INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO)) ) { - pOrigStruct = &OrigStruct; - memset( pOrigStruct, 0, sizeof(*pOrigStruct)); - if ( FillOutOrigStruct( orig_inp_data, pOrigStruct, sd ) ) { - AddMOLfileError(sd->pStrErrStruct, "Cannot interpret reversibility information"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_ERROR; - nRet = _IS_ERROR; - } - } - /* create INChI for each connected component of the structure and optionally display them */ - /* create INChI for the whole disconnected or original structure */ - if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) { - nRet1 = CreateOneStructureINChI(sd, ip, szTitle, pINChI, pINChI_Aux, INCHI_BAS, - inp_file, log_file, output_file, prb_file, - orig_inp_data, prep_inp_data, - composite_norm_data, num_inp, - pStr, nStrLen, pncFlags ); - nRet = inchi_max(nRet, nRet1); - } - if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) { - maxINChI = 1; - } - - - if ( nRet != _IS_FATAL && nRet != _IS_ERROR && - (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) && - (ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) { - - /* create INChI for the whole reconnected structure */ - nRet1 = CreateOneStructureINChI(sd, ip, szTitle, pINChI, pINChI_Aux, INCHI_REC, - inp_file, log_file, output_file, prb_file, - orig_inp_data, prep_inp_data, - composite_norm_data,num_inp, - pStr, nStrLen, pncFlags); - nRet = inchi_max(nRet, nRet1); - if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) { - maxINChI = 2; - } - } - - - if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) { - - if ( (sd->bChiralFlag & FLAG_INP_AT_CHIRAL) && - (ip->nMode & REQ_MODE_STEREO) && - !(ip->nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO)) && - !bIsStructChiral( pINChI, sd->num_components ) ) { - - AddMOLfileError(sd->pStrErrStruct, "Not chiral"); - } - /*************************************/ - /* Output err/warn messages */ - /*************************************/ - if ( /*!sd->nErrorCode &&*/ !sd->bUserQuitComponent && !sd->bUserQuit ) { - /* if successful then returns 0, otherwise returns _IS_FATAL */ - /* exctract the structure if requested */ - nRet1 = TreatCreateINChIWarning(sd, ip, prep_inp_data, num_inp, - inp_file, log_file, output_file, prb_file,pStr, nStrLen ); - nRet = inchi_max(nRet, nRet1); - } - } - - - /************************************************/ - /* sort and print INChI for the whole structure */ - /************************************************/ - - if ( ip->nInputType != INPUT_INCHI ) - { - /* Prepare SaveOpt bits */ - save_opt_bits = 0; - if ( ip->bINChIOutputOptions & INCHI_OUT_SAVEOPT ) - { - if ( 0 != ( ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) - save_opt_bits |= SAVE_OPT_RECMET; - if ( 0 != ( ip->nMode & REQ_MODE_BASIC) ) - save_opt_bits |= SAVE_OPT_FIXEDH; - if ( 0 != ( ip->nMode & REQ_MODE_DIFF_UU_STEREO) ) - save_opt_bits |= SAVE_OPT_SLUUD; - if ( 0 == (ip->nMode & (REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU)) ) - save_opt_bits |= SAVE_OPT_SUU; - if ( 0 != (ip->bTautFlags & TG_FLAG_KETO_ENOL_TAUT) ) - save_opt_bits |= SAVE_OPT_KET; - if ( 0 != (ip->bTautFlags & TG_FLAG_1_5_TAUT) ) - save_opt_bits |= SAVE_OPT_15T; - /* Check if /SNon requested and turn OFF stereo bits if so */ - if ( ! (ip->nMode & REQ_MODE_STEREO) ) - { - save_opt_bits &= ~SAVE_OPT_SUU; - save_opt_bits &= ~SAVE_OPT_SLUUD; - } - } - } - - if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) { - - nRet1 = SortAndPrintINChI(output_file, pStr, nStrLen, log_file, - ip, orig_inp_data, prep_inp_data, - composite_norm_data, pOrigStruct, - sd->num_components, sd->num_non_taut, sd->num_taut, - sd->bTautFlags, sd->bTautFlagsDone, pncFlags, num_inp, - pINChI, pINChI_Aux, - &bSortPrintINChIFlags, save_opt_bits); - nRet = inchi_max(nRet, nRet1); - } -#ifndef COMPILE_ANSI_ONLY /* { */ - - /* display equivalent components on original or preprocessed structure(s) */ - -#ifndef TARGET_LIB_FOR_WINCHI - - if ( nRet != _IS_FATAL && nRet != _IS_ERROR && /*ip->bDisplay &&*/ - (ip->bCompareComponents & CMP_COMPONENTS) && !sd->bUserQuit && !sd->bUserQuitComponent ) - { - int j, ret, ord; - int bDisplaySaved = ip->bDisplay; - ORIG_ATOM_DATA *inp_data; - AT_NUMB nEquSet; - for ( ord = -1; ord < INCHI_NUM; ord ++ ) { - switch( ord ) { - case -1: - j = INCHI_BAS; /* preprocessed non-tautomeric */ - break; - case 0: - j = INCHI_REC; /* preprocessed tautomeric */ - break; - case 1: - j = -1; /* original input */ - break; - default: - continue; - } - inp_data = j < 0? orig_inp_data : prep_inp_data+j; - if ( inp_data && inp_data->num_inp_atoms && inp_data->at && - inp_data->nEquLabels && - inp_data->nNumEquSets ) { - for ( nEquSet = 1; nEquSet <= inp_data->nNumEquSets; nEquSet ++ ) { - ip->dp.nEquLabels = inp_data->nEquLabels; - ip->dp.nCurEquLabel = nEquSet; - ip->dp.nNumEquSets = inp_data->nNumEquSets; - ip->bDisplay = 1; /* force display if it was not requested */ - ret = DisplayTheWholeStructure( sd, ip, szTitle, inp_file, log_file, inp_data, num_inp, - j, 1 /*bShowStructure*/, 0 ); - ip->dp.nEquLabels = NULL; - ip->dp.nCurEquLabel = 0; - ip->dp.nNumEquSets = 0; - ip->bDisplay = bDisplaySaved; /* restore display option */ - if ( ret ) { - /* user pressed Esc */ - goto exit_loop; - } - } - } - } -exit_loop:; - } - -#endif - - - - /* display composite results and equivalent components on composite results */ - if ( nRet != _IS_FATAL && nRet != _IS_ERROR && /*ip->bDisplay &&*/ - ip->bDisplayCompositeResults ) { - int iINChI; - for ( iINChI = 0; iINChI < maxINChI && !sd->bUserQuitComponentDisplay; iINChI ++ ) { - DisplayTheWholeCompositeStructure( ip, sd, num_inp, iINChI, - pINChI[iINChI], pINChI_Aux[iINChI], - orig_inp_data, prep_inp_data, - composite_norm_data[iINChI] ); - } -#ifndef TARGET_LIB_FOR_WINCHI - if( !ip->bDisplay && sd->bUserQuitComponentDisplay ) { - sd->bUserQuit = 1; - } -#endif - } - -#endif /* } COMPILE_ANSI_ONLY */ - - - /* XML struct end tag */ - if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) && sd->bXmlStructStarted > 0 ) { - if ( !OutputINChIXmlStructEndTag( output_file, pStr, nStrLen, 1 ) ) { - inchi_ios_eprint( log_file, "Cannot create end xml tag for structure #%ld.%s%s%s%s Terminating.\n", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - sd->bXmlStructStarted = -1; /* do not repeat same message */ - nRet = _IS_FATAL; - } else { - sd->bXmlStructStarted = 0; /* do not continue xml output for this structure */ - } - } - if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) { - /* Special mode: extract all good MOLfiles into the problem file - * Do not extract any MOLfile that could not be processed (option /PGO) - */ - if ( prb_file && prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd && ip->bSaveAllGoodStructsAsProblem ) { - CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, 0); - } -#if ( /*bRELEASE_VERSION != 1 &&*/ EXTR_FLAGS == EXTR_TRANSPOSITION_EXAMPLES && EXTR_MASK == EXTR_FLAGS ) - else - if ( prb_file->f && (bSortPrintINChIFlags & - ( FLAG_SORT_PRINT_TRANSPOS_BAS | FLAG_SORT_PRINT_TRANSPOS_REC ) ) - ) { - CopyMOLfile(inp_file, sd->fPtrStart, sd->fPtrEnd, prb_file->f, 0); - } -#endif - } - for ( i = 0; i < INCHI_NUM; i ++ ) { - for ( k = 0; k < TAUT_NUM+1; k ++ ) { - FreeCompAtomData( &composite_norm_data[i][k] ); - } - } - FreeOrigStruct( pOrigStruct); - - -/* - FreeInpAtomData( inp_cur_data ); - FreeInpAtomData( inp_norm_data[0] ); - FreeInpAtomData( inp_norm_data[1] ); -*/ -exit_function: - - - return nRet; -} -/************************************************************************************************/ -int bIsStructChiral( PINChI2 *pINChI2[INCHI_NUM], int num_components[] ) -{ - int i, j, k; - INChI *pINChI; - INChI_Stereo *Stereo; - for ( j = 0; j < INCHI_NUM; j ++ ) { /* disconnected / reconnected */ - if ( !num_components[j] ) { - continue; - } - for ( i = 0; i < num_components[j]; i ++ ) { /* component */ - for ( k = 0; k < TAUT_NUM; k ++ ) { /* mobile/immobile H */ - if ( (pINChI = pINChI2[j][i][k]) && - !pINChI->bDeleted && - pINChI->nNumberOfAtoms > 0 ) { - - if ( (Stereo = pINChI->Stereo) && - Stereo->t_parity && - Stereo->nNumberOfStereoCenters > 0 && - Stereo->nCompInv2Abs ) { - return 1; /* inversion changed stereo */ - } - if ( (Stereo = pINChI->StereoIsotopic) && - Stereo->t_parity && - Stereo->nNumberOfStereoCenters > 0 && - Stereo->nCompInv2Abs ) { - return 1; /* inversion changed stereo */ - } - } - } - } - } - return 0; -} -/************************************************************************************************/ -int CreateOneStructureINChI( STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, - PINChI2 *pINChI2[INCHI_NUM], PINChI_Aux2 *pINChI_Aux2[INCHI_NUM], int iINChI, - INCHI_IOSTREAM *inp_file, - INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *prb_file, /*^^^ was: INCHI_IOSTREAM */ - ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data, - COMP_ATOM_DATA composite_norm_data2[][TAUT_NUM+1], - long num_inp, char *pStr, int nStrLen, NORM_CANON_FLAGS *pncFlags ) -{ - int i, j, k, /*m,*/ nRet = 0; -#ifndef TARGET_LIB_FOR_WINCHI - int n; -#ifndef COMPILE_ANSI_ONLY - int err; -#endif -#endif - - PINChI2 *pINChI = NULL; - PINChI_Aux2 *pINChI_Aux = NULL; - - INP_ATOM_DATA InpCurAtData; - INP_ATOM_DATA *inp_cur_data; - - INP_ATOM_DATA InpNormAtData, InpNormTautData; - INP_ATOM_DATA *inp_norm_data[TAUT_NUM]; /* = { &InpNormAtData, &InpNormTautData }; */ - ORIG_ATOM_DATA *cur_prep_inp_data = prep_inp_data + iINChI; - inchiTime ulTStart; -#ifndef COMPILE_ANSI_ONLY - int bShowStructure = 0; - int bStructurePreprocessed = 0; /* All changes except disconnection */ - int bStructureDisconnected = 0; - int bAlsoOutputReconnected = 0, bINCHI_LIB_Flag = 0; - COMP_ATOM_DATA *composite_norm_data = composite_norm_data2[iINChI]; - INP_ATOM_DATA2 *all_inp_norm_data = NULL; -#endif - - /* - if ( orig_inp_data is NOT empty AND - prep_inp_data[0] IS empty ) then: - - 1. copy orig_inp_data --> prep_inp_data[0] - 2. fix odd things in prep_inp_data[0] - 3. if( orig_inp_data->bDisconnectSalts ) then - -- disconnect salts in prep_inp_data[0] - 4. move protons to neutralize charges on heteroatoms - 5. if( orig_inp_data->bDisconnectCoord ) then - -- copy prep_inp_data[0] --> prep_inp_data[1] - -- disconnect metals in prep_inp_data[0] - - [ This all is done in PreprocessOneStructure() ] - - iINChI = 0 - ========= - (normal/disconnected layer) - - 1. normalize prep_inp_data[0] in inp_norm_data[0,1] - 2. create INChI[ iINChI ] out of inp_norm_data[0,1] - - - iINChI = 1 AND orig_inp_data->bDisconnectCoord > 0 - ================================================= - (reconnected layer) - - 1. normalize prep_inp_data[1] in inp_norm_data[0,1] - 2. create INChI[ iINChI ] out of inp_norm_data[0,1] - - */ - -#if ( TEST_RENUMB_ATOMS == 1 ) - RENUMB_DATA RenumbData; - RENUMB_DATA *pRenumbData = &RenumbData; -#endif - - - ip->msec_LeftTime = ip->msec_MaxTime; /* start timeout countdown for each component */ - -#if ( TEST_RENUMB_ATOMS == 1 ) - memset( pRenumbData, 0, sizeof(*pRenumbData) ); -#endif - - inp_cur_data = &InpCurAtData; - inp_norm_data[TAUT_NON] = &InpNormAtData; - inp_norm_data[TAUT_YES] = &InpNormTautData; - - memset( inp_cur_data , 0, sizeof( *inp_cur_data ) ); - memset( inp_norm_data[TAUT_NON], 0, sizeof( *inp_norm_data[0] ) ); - memset( inp_norm_data[TAUT_YES], 0, sizeof( *inp_norm_data[0] ) ); - -#ifndef COMPILE_ANSI_ONLY - memset( composite_norm_data+TAUT_NON, 0, sizeof( composite_norm_data[0] ) ); - memset( composite_norm_data+TAUT_YES, 0, sizeof( composite_norm_data[0] ) ); - memset( composite_norm_data+TAUT_INI, 0, sizeof( composite_norm_data[0] ) ); -#endif - if ( ip->bAllowEmptyStructure && !orig_inp_data->at && !orig_inp_data->num_inp_atoms ) { - ; - } else - if ( !orig_inp_data->at || !orig_inp_data->num_inp_atoms ) { - return 0; /* nothing to do */ - } - if ( iINChI == 1 && orig_inp_data->bDisconnectCoord <= 0 ) { - return 0; - } - - /* m = iINChI; */ /* orig_inp_data index */ - - if ( iINChI != INCHI_BAS && iINChI != INCHI_REC ) { - AddMOLfileError(sd->pStrErrStruct, "Fatal undetermined program error"); - sd->nStructReadError = 97; - nRet = sd->nErrorType = _IS_FATAL; - goto exit_function; - } - - /******************************************************************* - * * - * * - * Whole structure preprocessing: 1st step of the normalization * - * * - * Happen only on the first call to CreateOneStructureINChI() * - * * - * * - *******************************************************************/ - - if ( (!prep_inp_data->at || !prep_inp_data->num_inp_atoms) && orig_inp_data->num_inp_atoms > 0 ) { - /* the structure has not been preprocessed */ - if ( ip->msec_MaxTime ) { - InchiTimeGet( &ulTStart ); - } - PreprocessOneStructure( sd, ip, orig_inp_data, prep_inp_data ); - pncFlags->bTautFlags[iINChI][TAUT_YES] = - pncFlags->bTautFlags[iINChI][TAUT_NON] = sd->bTautFlags[INCHI_BAS] | ip->bTautFlags; - pncFlags->bTautFlagsDone[iINChI][TAUT_YES] = - pncFlags->bTautFlagsDone[iINChI][TAUT_NON] = sd->bTautFlagsDone[INCHI_BAS] | ip->bTautFlagsDone; - -#ifndef COMPILE_ANSI_ONLY - /* in this location the call happens once for each input structure, before preprocessing */ - bStructurePreprocessed = (0 != (sd->bTautFlagsDone[INCHI_BAS] & ( - TG_FLAG_MOVE_HPLUS2NEUTR_DONE | - TG_FLAG_DISCONNECT_SALTS_DONE | - TG_FLAG_MOVE_POS_CHARGES_DONE | - TG_FLAG_FIX_ODD_THINGS_DONE ))); - bStructureDisconnected = (0 != (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE)); - - bShowStructure = ( bStructurePreprocessed || - bStructureDisconnected || - prep_inp_data[0].num_components > 1); - - /* sd->bTautFlags[] contains output flags - ip->bTautFlags contains input flags - */ - bAlsoOutputReconnected = (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) && - (ip->bTautFlags & TG_FLAG_RECONNECT_COORD); - bINCHI_LIB_Flag = 0; - - /*************** output structures to TARGET_LIB_FOR_WINCHI conditions ********************* - * - * Send to TARGET_LIB_FOR_WINCHI: - * - * type component conditions - * - * COMPONENT_ORIGINAL #0: (num_components > 1) - * COMPONENT_ORIGINAL_PREPROCESSED #0: (num_components > 1) && (preprocessed) - * COMPONENT_ORIGINAL #1: (num_components = 1) && (preprocessed) - * - * Flags explanation: - * MAIN => iINChI=0, RECN => iINChI=1 (Reconnected) - * ORIG => Original, PREP => Preprocessed - * - * Possible flags: k - * - * COMP_ORIG_0_MAIN 0x0001 0 COMPONENT_ORIGINAL, bMain, component #0 - * COMP_ORIG_0_RECN 0x0002 1 COMPONENT_ORIGINAL, bRecn, component #0 - * - * COMP_PREP_0_MAIN 0x0004 2 COMPONENT_ORIGINAL_PREPROCESSED, bMain, component #0 - * COMP_PREP_0_RECN 0x0008 3 COMPONENT_ORIGINAL_PREPROCESSED, bRecn, component #0 - * - * COMP_ORIG_1_MAIN 0x0010 4 COMPONENT_ORIGINAL, bMain, component #1 - * COMP_ORIG_1_RECN 0x0020 5 COMPONENT_ORIGINAL, bRecn, component #1 - * - * bReconnected = k%2 (0 or 1) - * nComponent = k/4 (0 or 1) - * bPreprocessed = (k/2)%2 (0 or 1) - * - ******************************************************************************/ - /* Original -> Main, component #0, Original */ - if ( prep_inp_data[INCHI_BAS].num_components > 1 ) { - bINCHI_LIB_Flag |= COMP_ORIG_0_MAIN; - } else - /* Original -> Main, component #1, Original */ - if ( prep_inp_data[INCHI_BAS].num_components == 1 && bStructurePreprocessed ) { - bINCHI_LIB_Flag |= COMP_ORIG_1_MAIN; - /* preprocessed will be added when output canonicalization results */ - } - if ( bAlsoOutputReconnected ) { - /* Original -> Reconnected, component #0, Original */ - if ( prep_inp_data[INCHI_REC].num_components > 1 ) { - bINCHI_LIB_Flag |= COMP_ORIG_0_RECN; - } else - /* Original -> Reconnected, component #1, Original */ - if ( prep_inp_data[INCHI_BAS].num_components == 1 && bStructurePreprocessed ) { - bINCHI_LIB_Flag |= COMP_ORIG_1_RECN; - /* preprocessed will be added when output canonicalization results */ - } - } - if ( ip->msec_MaxTime ) { - ip->msec_LeftTime -= InchiTimeElapsed( &ulTStart ); - } - - /* display the ORIGINAL, UN-PREPROCESSED structure */ - if ( DisplayTheWholeStructure( sd, ip, szTitle, inp_file, log_file, orig_inp_data, num_inp, - -1, bShowStructure, bINCHI_LIB_Flag ) ) { - goto exit_function; - } -#endif - switch (sd->nErrorType) { - case _IS_ERROR: - case _IS_FATAL: - /* error message */ - nRet = TreatReadTheStructureErrors( sd, ip, LOG_MASK_ALL, inp_file, log_file, output_file, prb_file, - prep_inp_data, &num_inp, pStr, nStrLen ); - goto exit_cycle; - } - } - /* tranfer flags from INChI_Aux to sd */ - - - - - -#ifndef COMPILE_ANSI_ONLY /* { */ - - /******************************************/ - /* Displaying the structures */ - /* Only under WIN32 */ - /******************************************/ - if ( ip->bDisplayCompositeResults && - !sd->bUserQuitComponentDisplay && prep_inp_data[iINChI].num_components > 1) { - all_inp_norm_data = (INP_ATOM_DATA2 *)inchi_calloc( prep_inp_data[iINChI].num_components, sizeof(all_inp_norm_data[0])); - } - - - /* Display the input structure AFTER PREPROCESSING */ - switch ( iINChI ) { - - case INCHI_BAS: - /*------------ Possibly disconnected structure -------------------*/ - bStructurePreprocessed = 0 != (sd->bTautFlagsDone[iINChI] & ( - TG_FLAG_MOVE_HPLUS2NEUTR_DONE | - TG_FLAG_DISCONNECT_SALTS_DONE | - TG_FLAG_MOVE_POS_CHARGES_DONE | - TG_FLAG_MOVE_CHARGE_COORD_DONE | - TG_FLAG_DISCONNECT_COORD_DONE | - TG_FLAG_FIX_ODD_THINGS_DONE )); - bINCHI_LIB_Flag = 0; - - /* Preprocessed/Main -> Main, component #0, Preprocessed */ - if ( prep_inp_data[iINChI].num_components > 1 && bStructurePreprocessed ) { - bINCHI_LIB_Flag |= COMP_PREP_0_MAIN; - } - - bShowStructure = ( bStructurePreprocessed && prep_inp_data[iINChI].num_components > 1); - break; - - case INCHI_REC: - /*------------ Reconnected structure ------------------------------*/ - bAlsoOutputReconnected = (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) && - (ip->bTautFlags & TG_FLAG_RECONNECT_COORD); - if ( !bAlsoOutputReconnected ) { - break; - } - bStructurePreprocessed = 0 != (sd->bTautFlagsDone[iINChI] & ( - TG_FLAG_MOVE_HPLUS2NEUTR_DONE | - TG_FLAG_DISCONNECT_SALTS_DONE | - TG_FLAG_MOVE_POS_CHARGES_DONE | - TG_FLAG_FIX_ODD_THINGS_DONE )); - bINCHI_LIB_Flag = 0; - - /* Preprocessed/Reconnected -> Reconnected, component #0, Preprocessed */ - if ( prep_inp_data[iINChI].num_components > 1 && bStructurePreprocessed ) { - bINCHI_LIB_Flag |= COMP_PREP_0_RECN; - } - - bShowStructure = ( bStructurePreprocessed && prep_inp_data[iINChI].num_components > 1 ); - break; - default: - bShowStructure = 0; - } - if ( prep_inp_data[iINChI].num_inp_atoms > 0 ) { - if ( DisplayTheWholeStructure( sd, ip, szTitle, inp_file, log_file, prep_inp_data+iINChI, num_inp, - iINChI, bShowStructure, bINCHI_LIB_Flag ) ) { - goto exit_function; - } - } -#endif /* } ifndef COMPILE_ANSI_ONLY */ - - - - /* allocate pINChI[iINChI] and pINChI_Aux2[iINChI] -- arrays of pointers to INChI and INChI_Aux */ - /* assign values to sd->num_components[] */ - MYREALLOC2(PINChI2, PINChI_Aux2, pINChI2[iINChI], pINChI_Aux2[iINChI], sd->num_components[iINChI], cur_prep_inp_data->num_components, k); - if ( k ) { - AddMOLfileError(sd->pStrErrStruct, "Cannot allocate output data. Terminating"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_FATAL; - goto exit_function; - } - pINChI = pINChI2[iINChI]; - pINChI_Aux = pINChI_Aux2[iINChI]; - - /**************************************************************************/ - /* */ - /* */ - /* M A I N C Y C L E: P R O C E S S C O M P O N E N T S */ - /* */ - /* */ - /* O N E B Y O N E */ - /* */ - /* */ - /**************************************************************************/ - - for ( i = 0, nRet = 0; !sd->bUserQuitComponent && i < cur_prep_inp_data->num_components; i ++ ) { - if ( ip->msec_MaxTime ) { - InchiTimeGet( &ulTStart ); - } -#ifndef TARGET_LIB_FOR_WINCHI /* { */ -#if ( bREUSE_INCHI == 1 ) - if ( iINChI == INCHI_REC && (!ip->bDisplay && !ip->bDisplayCompositeResults && !(ip->bCompareComponents & CMP_COMPONENTS) || - sd->bUserQuitComponentDisplay) ) { - /* reconnected structure (06-20-2005: added "&& !ip->bDisplayCompositeResults" to display composite structure) */ - int m = iINChI-1; - /* find whether we have already calculated this INChI in basic (disconnected) layer */ - for ( j = n = 0; j < prep_inp_data[m].num_components; j ++ ) { - if ( i+1 == prep_inp_data[m].nOldCompNumber[j] && - (pINChI2[m][j][TAUT_NON] || pINChI2[m][j][TAUT_YES]) ) { - /* yes, we have already done this */ - if ( !n++ ) { - memcpy( pINChI +i, pINChI2 [m]+j, sizeof(pINChI[0])); - memcpy( pINChI_Aux+i, pINChI_Aux2[m]+j, sizeof(pINChI_Aux[0])); - for ( k = 0; k < TAUT_NUM; k ++ ) { - if ( pINChI[i][k] ) { - pINChI[i][k]->nRefCount ++; - if ( pINChI[i][k]->nNumberOfAtoms > 0 ) { - switch( k ) { - case TAUT_NON: - sd->num_non_taut[iINChI] ++; - break; - case TAUT_YES: - if ( pINChI[i][k]->lenTautomer > 0 ) { - sd->num_taut[iINChI] ++; - } else - if ( !pINChI[i][TAUT_NON] || !pINChI[i][TAUT_NON]->nNumberOfAtoms ) { - sd->num_non_taut[iINChI] ++; - } - break; - } - } - } - if ( pINChI_Aux[i][k] ) { - pINChI_Aux[i][k]->nRefCount ++; - } - } - } - } - } - if ( n == 1 ) { - continue; - } - if ( n > 1 ) { /* ith component is equivalent to more than one another component */ - AddMOLfileError(sd->pStrErrStruct, "Cannot distinguish components"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_ERROR; - goto exit_function; - } - } -#endif -#endif /* } TARGET_LIB_FOR_WINCHI */ - - /*****************************************************/ - /* a) allocate memory and extract current component */ - /*****************************************************/ - nRet = GetOneComponent( sd, ip, log_file, output_file, inp_cur_data, cur_prep_inp_data, i, num_inp, pStr, nStrLen ); - if ( ip->msec_MaxTime ) { - ip->msec_LeftTime -= InchiTimeElapsed( &ulTStart ); - } - switch ( nRet ) { - case _IS_ERROR: - case _IS_FATAL: - goto exit_cycle; - } -#ifndef TARGET_API_LIB - /* console request: Display the component? */ - if ( ip->bDisplay && inp_file->f != stdin ) { - if ( user_quit("Enter=Display Component, Esc=Stop ?", ip->ulDisplTime) ) { - sd->bUserQuitComponent = 1; - break; - } - } -#endif -#ifndef COMPILE_ANSI_ONLY /* { */ - /* b) Display the extracted original component structure */ - if ( inp_cur_data->at && ip->bDisplay && !sd->bUserQuitComponentDisplay ) { - if ( cur_prep_inp_data->num_components == 1 ) { - sprintf( szTitle, "%sInput Structure #%ld.%s%s%s%s%s", - bStructurePreprocessed? "Preprocessed ":"", - num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), iINChI? " (Reconnected)":""); - } else { - sprintf( szTitle, "Component #%d of %d, Input Structure #%ld.%s%s%s%s%s", - i+1, cur_prep_inp_data->num_components, - num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), iINChI? " (Reconnected)":""); - } -#ifndef TARGET_LIB_FOR_WINCHI - err = DisplayStructure( inp_cur_data->at, inp_cur_data->num_at, - 0, 1, 0, NULL, 1/*isotopic*/, 0/*taut*/, NULL, NULL, - ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); - sd->bUserQuitComponentDisplay = (err==ESC_KEY); - if ( !err ) - { - inchi_fprintf( stderr, "Cannot display the structure\n"); - } -#else - if(DRAWDATA && DRAWDATA_EXISTS) - { - struct DrawData vDrawData; - int nType = COMPONENT_ORIGINAL; - vDrawData.pWindowData = CreateWinData_( inp_cur_data->at, inp_cur_data->num_at, - 0, 1 /* bAdd_DT_to_num_H */, 0, NULL, - 1 /* display isotopic if present */, 0, NULL, NULL, - ip->bAbcNumbers, &ip->dp, ip->nMode ); - if( vDrawData.pWindowData != NULL ) - { - if ( DRAWDATA_EXISTS ( i+1, nType, iINChI ) ) { /* i = component number */ - nType = COMPONENT_ORIGINAL_PREPROCESSED; - } - vDrawData.nComponent = i+1; - vDrawData.nType = nType; - vDrawData.bReconnected = iINChI; /* 0=>main; 1=>reconnected */ - vDrawData.szTitle = _strdup(szTitle); - vDrawData.pWindowData->szTitle = _strdup(szTitle); - DRAWDATA(&vDrawData); - } - } -#endif - } -#endif /* } COMPILE_ANSI_ONLY */ - -#if ( TEST_RENUMB_ATOMS == 1 ) /* { */ - /****************************************************************************/ - /* R E N U M B E R I N G (testing only) Part I STARTS here */ - /****************************************************************************/ - RenumberingTestInit( pRenumbData, inp_cur_data ); - if ( log_file != stderr ) { - if ( ip->bDisplay ) - inchi_ios_eprint( log_file, "Component #%d structure #%ld.%s%s%s%s...\n", i+1, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - else - inchi_ios_eprint( stderr, "Component #%d structure #%ld.%s%s%s%s...\r", i+1, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - } - /****************************************************************************/ - /* R E N U M B E R I N G (testing only) Part I ENDS here */ - /****************************************************************************/ -#endif /* } TEST_RENUMB_ATOMS */ - - - /*******************************************************************************/ - /* */ - /* N O R M A L I Z A T I O N a n d C A N O N I C A L I Z A T I O N */ - /* */ - /* (both tautomeric and non-tautomeric if requested) */ - /* */ - /*******************************************************************************/ - /* c) Create the component's INChI ( copies ip->bTautFlags into sd->bTautFlags)*/ - /*******************************************************************************/ - nRet = CreateOneComponentINChI( sd, ip, inp_cur_data, orig_inp_data, pINChI/*2[iINChI]*/, pINChI_Aux/*2[iINChI]*/, iINChI, - i, num_inp, inp_norm_data, pncFlags, log_file ); - - -#if ( TEST_RENUMB_ATOMS == 1 ) /* { */ - /****************************************************************************/ - /* R E N U M B E R I N G (testing only) Part II STARTS here */ - /****************************************************************************/ - if ( !nRet ) { - nRet = RenumberingTest( pINChI/*2[iINChI]*/, pINChI_Aux/*2[iINChI]*/, orig_inp_data, iINChI, pRenumbData, inp_cur_data, inp_norm_data, sd, ip, szTitle, log_file, prb_file, i, num_inp, pncFlags); - } - RenumberingTestUninit( pRenumbData ); - /****************************************************************************/ - /* R E N U M B E R I N G (testing only) Part II ENDS here */ - /****************************************************************************/ -#endif /* } TEST_RENUMB_ATOMS */ - - - /* d) Display one component structure and/or INChI results only if there was no error */ -#ifndef COMPILE_ANSI_ONLY /* { */ - if ( !nRet ) { - /* output one component INChI to the stdout if requested */ - /* - if ( ip->bDisplayEachComponentINChI ) { - int cur_num_non_taut = (pINChI[i][TAUT_NON] && pINChI[i][TAUT_NON]->nNumberOfAtoms>0); - int cur_num_taut = (pINChI[i][TAUT_YES] && pINChI[i][TAUT_YES]->nNumberOfAtoms>0); - if ( ip->bDisplayEachComponentINChI && cur_num_non_taut + cur_num_taut ) { - SortAndPrintINChI(stdout, pStr, nStrLen, NULL, - ip, 1, cur_num_non_taut, cur_num_taut, - num_inp, pINChI+i, pINChI_Aux+i, - save_opt_bits); - } - } - */ - /************************************************************************** - * display from one up to 4 structure pictures-results for each component * - * Enable buttons: * - * BN (non-tautomeric non-isotopic): inp_norm_data[0]->bExists * - * TN (tautomeric non-isotopic): inp_norm_data[1]->bExists * - * BI (non-tautomeric isotopic): inp_norm_data[0]->bExists && * - * inp_norm_data[0]->bHasIsotopicLayer * - * TI (tautomeric isotopic): inp_norm_data[1]->bExists && * - * inp_norm_data[1]->bHasIsotopicLayer * - **************************************************************************/ - int bIsotopic, bTautomeric, bDisplayTaut, bHasIsotopicLayer, bFixedBondsTaut, m_max, m, nNumDisplayedFixedBondTaut=0; - for ( j = 0; ip->bDisplay && !sd->bUserQuitComponentDisplay && j < TAUT_NUM; j ++ ) { - if ( inp_norm_data[j]->bExists && !inp_norm_data[j]->bDeleted ) { - bTautomeric = (pINChI[i][j]->lenTautomer > 0); /* same as (inp_norm_data[j]->bTautomeric > 0) */ - /* if requested tautomeric and no tautmerism found then do not say mobile or fixed H. 2004-10-27 */ - bDisplayTaut = (!(ip->nMode & REQ_MODE_BASIC) && !bTautomeric)? -1 : bTautomeric; - bHasIsotopicLayer = (inp_norm_data[j]->bHasIsotopicLayer > 0); - for ( k = 0; k <= bHasIsotopicLayer; k ++ ) { - bIsotopic = (k > 0); - m_max = inp_norm_data[j]->at_fixed_bonds && inp_norm_data[j]->bTautPreprocessed? 1 : 0; - for ( m = m_max; 0 <= m; m -- ) { - bFixedBondsTaut = (m>0); - nNumDisplayedFixedBondTaut += bFixedBondsTaut; /* display only one time */ - /* added number of components, added another format for a single component case - DCh */ - if ( cur_prep_inp_data->num_components > 1 ) { - sprintf( szTitle, "%s Component #%d of %d, Structure #%ld%s%s.%s%s%s%s%s", - bFixedBondsTaut? "Preprocessed":"Result for", - i+1, cur_prep_inp_data->num_components, num_inp, - bDisplayTaut==1? ", mobile H": bDisplayTaut==0?", fixed H":"", - bIsotopic? ", isotopic":"", - SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), iINChI? " (Reconnected)":""); - } else { - sprintf( szTitle, "%s Structure #%ld%s%s.%s%s%s%s%s", - bFixedBondsTaut? "Preprocessed":"Result for", - num_inp, - bDisplayTaut==1? ", mobile H": bDisplayTaut==0?", fixed H":"", - bIsotopic? ", isotopic":"", - SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), iINChI? " (Reconnected)":""); - } -#ifndef TARGET_LIB_FOR_WINCHI - if ( bFixedBondsTaut && nNumDisplayedFixedBondTaut != 1 ) - continue; - if ( bFixedBondsTaut ) { - err = DisplayStructure( inp_norm_data[j]->at_fixed_bonds, inp_norm_data[j]->num_at, - inp_norm_data[j]->num_removed_H, 0 /*bAdd_DT_to_num_H*/, - inp_norm_data[j]->nNumRemovedProtons, - inp_norm_data[j]->nNumRemovedProtonsIsotopic, - bHasIsotopicLayer, j, NULL, NULL, - ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); - } else { - err = DisplayStructure( inp_norm_data[j]->at, inp_norm_data[j]->num_at, - 0, 0 /*bAdd_DT_to_num_H*/, 0, NULL, - k, j, pINChI[i], pINChI_Aux[i], - ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); - } - if ( sd->bUserQuitComponentDisplay = (err==ESC_KEY) ) { - break; - } -#else - if(DRAWDATA && !bFixedBondsTaut) - { - struct DrawData vDrawData; - vDrawData.pWindowData = CreateWinData_( inp_norm_data[j]->at, inp_norm_data[j]->num_at, - 0, 0 /* bAdd_DT_to_num_H */, 0, NULL, - k, j, pINChI[i], pINChI_Aux[i], - ip->bAbcNumbers, &ip->dp, ip->nMode ); - if( vDrawData.pWindowData != NULL ) - { - int nType; - vDrawData.nComponent = i+1; - if( bTautomeric == 0 ) - nType = (bIsotopic == 0) ? COMPONENT_BN: COMPONENT_BI; - else - nType = (bIsotopic == 0) ? COMPONENT_TN: COMPONENT_TI; - vDrawData.nType = nType; - vDrawData.bReconnected = iINChI; /* 0=>main; 1=>reconnected */ - vDrawData.szTitle = _strdup(szTitle); - vDrawData.pWindowData->szTitle = _strdup(szTitle); - DRAWDATA(&vDrawData); - } - } else - if(DRAWDATA && bFixedBondsTaut) - { - struct DrawData vDrawData; - if ( (ip->bCompareComponents & CMP_COMPONENTS) && - !(ip->bCompareComponents & CMP_COMPONENTS_NONTAUT) && - !bIsotopic == !inp_norm_data[j]->bHasIsotopicLayer ) { - - vDrawData.pWindowData = - CreateWinData_( inp_norm_data[j]->at_fixed_bonds, inp_norm_data[j]->num_at, - inp_norm_data[j]->num_removed_H, - 0 /* bAdd_DT_to_num_H */, - inp_norm_data[j]->nNumRemovedProtons, - inp_norm_data[j]->nNumRemovedProtonsIsotopic, - k, j, NULL, NULL, - ip->bAbcNumbers, &ip->dp, ip->nMode ); - } else { - continue; - } - if( vDrawData.pWindowData != NULL ) - { - vDrawData.nComponent = i+1; - vDrawData.nType = COMPONENT_ORIGINAL_PREPROCESSED; - vDrawData.bReconnected = iINChI; /* 0=>main; 1=>reconnected */ - vDrawData.szTitle = _strdup(szTitle); - vDrawData.pWindowData->szTitle = _strdup(szTitle); - DRAWDATA(&vDrawData); - } - } -#endif - } - } - } - } - - /* save normalized components for composite display */ - if ( ip->bDisplayCompositeResults && all_inp_norm_data ) { - for ( j = 0; j < TAUT_NUM; j ++ ) { - if ( inp_norm_data[j]->bExists ) { - all_inp_norm_data[i][j] = *inp_norm_data[j]; - memset( inp_norm_data[j], 0, sizeof(*inp_norm_data[0]) ); - } - } - } - - } -#endif /* } COMPILE_ANSI_ONLY */ - if ( nRet ) { - nRet = TreatCreateOneComponentINChIError(sd, ip, cur_prep_inp_data, i, num_inp, - inp_file, log_file, output_file, prb_file,pStr, nStrLen ); - - break; - } - } - /**************************************************************************/ - /* */ - /* */ - /* E N D O F T H E M A I N C Y C L E P R O C E S S I N G */ - /* */ - /* C O M P O N E N T S O N E B Y O N E */ - /* */ - /* */ - /**************************************************************************/ - - -exit_cycle: - -#if ( TEST_RENUMB_ATOMS == 1 ) /* { */ - if ( pRenumbData->bRenumbErr && (!nRet || nRet==_IS_WARNING) ) { - sd->nErrorCode = pRenumbData->bRenumbErr; - nRet = TreatCreateOneComponentINChIError(sd, ip, cur_prep_inp_data, -1, num_inp, - inp_file, log_file, output_file, prb_file,pStr, nStrLen ); - /* nRet = _IS_ERROR; */ - sd->nErrorCode = 0; - nRet = 0; - } -#endif /* } TEST_RENUMB_ATOMS */ - switch ( nRet ) { - - case _IS_FATAL: - case _IS_ERROR: - break; - - default: - -#ifndef COMPILE_ANSI_ONLY /* { */ - /* composite results picture(s) */ - if ( all_inp_norm_data ) { - int res = CreateCompositeNormAtom( composite_norm_data, all_inp_norm_data, pINChI, pINChI_Aux, - prep_inp_data[iINChI].num_components, ip->nMode ); - /* - for ( i = 0; i < prep_inp_data[iINChI].num_components; i ++ ) { - for ( k = 0; k < TAUT_NUM; k ++ ) { - FreeInpAtomData( &all_inp_norm_data[i][k] ); - } - } - inchi_free( all_inp_norm_data ); - all_inp_norm_data = NULL; - */ - } -#endif /* } COMPILE_ANSI_ONLY */ - - break; - } - - -#ifndef COMPILE_ANSI_ONLY /* { */ - /* avoid memory leaks in case of error */ - if ( all_inp_norm_data ) { - for ( i = 0; i < prep_inp_data[iINChI].num_components; i ++ ) { - for ( k = 0; k < TAUT_NUM; k ++ ) { - FreeInpAtomData( &all_inp_norm_data[i][k] ); - } - } - inchi_free( all_inp_norm_data ); - all_inp_norm_data = NULL; - } -#endif /* } COMPILE_ANSI_ONLY */ - - - FreeInpAtomData( inp_cur_data ); - for ( i = 0; i < TAUT_NUM; i ++ ) { - FreeInpAtomData( inp_norm_data[i] ); - } - - -exit_function: - - return nRet; -} - - - -#ifndef COMPILE_ANSI_ONLY /* { */ -/****************************************************************************/ -int CreateCompositeNormAtom( COMP_ATOM_DATA *composite_norm_data, INP_ATOM_DATA2 *all_inp_norm_data, - PINChI2 *pINChI, PINChI_Aux2 *pINChI_Aux, int num_components, INCHI_MODE nMode ) -{ - int i, j, jj, k, n, m, tot_num_at, tot_num_H, cur_num_at, cur_num_H, nNumRemovedProtons; - int num_comp[TAUT_NUM+1], num_taut[TAUT_NUM+1], num_del[TAUT_NUM+1], num_at[TAUT_NUM+1], num_inp_at[TAUT_NUM+1]; - int ret = 0, indicator = 1; - inp_ATOM *at, *at_from; - memset( num_comp, 0, sizeof(num_comp) ); - memset( num_taut, 0, sizeof(num_taut) ); - memset( num_del, 0, sizeof(num_taut) ); - /* count taut and non-taut components */ - for ( j = 0; j < TAUT_NUM; j ++ ) { - num_comp[j] = num_taut[j] = 0; - for ( i = 0; i < num_components; i ++ ) { - if ( all_inp_norm_data[i][j].bExists ) { - num_del[j] += (0 != all_inp_norm_data[i][j].bDeleted ); - num_comp[j] ++; - num_taut[j] += (0 != all_inp_norm_data[i][j].bTautomeric); - } - } - } - /* count intermediate taut structure components */ - if ( num_comp[TAUT_YES] > num_del[TAUT_YES] && num_taut[TAUT_YES] ) { - /* - num_comp[TAUT_INI] = num_comp[TAUT_YES] - num_del[TAUT_YES]; - */ - - for ( i = 0, j=TAUT_YES; i < num_components; i ++ ) { - if ( all_inp_norm_data[i][j].bExists && - (all_inp_norm_data[i][j].bDeleted || - all_inp_norm_data[i][j].bTautomeric && - all_inp_norm_data[i][j].at_fixed_bonds && - all_inp_norm_data[i][j].bTautPreprocessed) ) { - num_comp[TAUT_INI] ++; - } - } - - } - /* count atoms and allocate composite atom data */ - for ( jj = 0; jj <= TAUT_INI; jj ++ ) { - num_at[jj] = num_inp_at[jj] = 0; - j = inchi_min (jj, TAUT_YES); - if ( num_comp[jj] ) { - for ( i = 0; i < num_components; i ++ ) { - if ( all_inp_norm_data[i][j].bDeleted ) - continue; - /* find k = the normaized structure index */ - if ( jj == TAUT_INI ) { - if ( all_inp_norm_data[i][j].bExists && - all_inp_norm_data[i][j].at_fixed_bonds ) { - k = j; - } else - if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted && - !all_inp_norm_data[i][j].bDeleted ) { - k = ALT_TAUT(j); - } else - if ( all_inp_norm_data[i][j].bExists ) { - k = j; - } else { - continue; - } - } else { - if ( all_inp_norm_data[i][j].bExists ) { - k = j; - } else - if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted) { - k = ALT_TAUT(j); - } else { - continue; - } - } - num_inp_at[jj] += all_inp_norm_data[i][k].num_at; /* all atoms including terminal H */ - num_at[jj] += all_inp_norm_data[i][k].num_at - all_inp_norm_data[i][k].num_removed_H; - } - if ( num_inp_at[jj] ) { - if ( !CreateCompAtomData( composite_norm_data+jj, num_inp_at[jj], num_components, jj == TAUT_INI ) ) - goto exit_error; - composite_norm_data[jj].num_removed_H = num_inp_at[jj] - num_at[jj]; - } - } - } - /* fill out composite atom */ - for ( jj = 0; jj <= TAUT_INI; jj ++, indicator <<= 1 ) { - j = inchi_min (jj, TAUT_YES); - if ( num_comp[jj] ) { - tot_num_at = 0; - tot_num_H = 0; - for ( i = 0; i < num_components; i ++ ) { - if ( all_inp_norm_data[i][j].bDeleted ) { - composite_norm_data[jj].nNumRemovedProtons += all_inp_norm_data[i][j].nNumRemovedProtons; - for ( n = 0; n < NUM_H_ISOTOPES; n ++ ) { - composite_norm_data[jj].nNumRemovedProtonsIsotopic[n] += all_inp_norm_data[i][j].nNumRemovedProtonsIsotopic[n]; - } - continue; - } - nNumRemovedProtons = 0; - k = TAUT_NUM; - /* find k = the normaized structure index */ - if ( jj == TAUT_INI ) { - if ( all_inp_norm_data[i][j].bExists && all_inp_norm_data[i][j].at_fixed_bonds ) { - k = j; - } else - if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists ) { - k = ALT_TAUT(j); - } else - if ( all_inp_norm_data[i][j].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted ) { - k = j; - } else { - continue; - } - } else { - if ( all_inp_norm_data[i][j].bExists ) { - k = j; - } else - if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted ) { - k = ALT_TAUT(j); - } else { - continue; - } - } - /* copy main atoms */ - cur_num_H = all_inp_norm_data[i][k].num_removed_H; /* number of terminal H atoms */ - cur_num_at = all_inp_norm_data[i][k].num_at - cur_num_H; /* number of all but explicit terminal H atoms */ - - if ( (tot_num_at + cur_num_at) > num_at[jj] || - (num_at[jj] + tot_num_H + cur_num_H) > num_inp_at[jj] ) { - goto exit_error; /* miscount */ - } - at = composite_norm_data[jj].at+tot_num_at; /* points to the 1st destination atom */ - at_from = (jj == TAUT_INI && k == TAUT_YES && all_inp_norm_data[i][k].at_fixed_bonds)? - all_inp_norm_data[i][k].at_fixed_bonds : all_inp_norm_data[i][k].at; - memcpy( at, at_from, sizeof(composite_norm_data[0].at[0]) * cur_num_at ); /* copy atoms except terminal H */ - /* shift neighbors of main atoms */ - for ( n = 0; n < cur_num_at; n ++, at ++ ) { - for ( m = 0; m < at->valence; m ++ ) { - at->neighbor[m] += tot_num_at; - } - } - /* copy explicit H */ - if ( cur_num_H ) { - at = composite_norm_data[jj].at+num_at[jj]+tot_num_H; /* points to the 1st destination atom */ - memcpy( at, at_from+cur_num_at, - sizeof(composite_norm_data[0].at[0]) * cur_num_H ); - /* shift neighbors of explicit H atoms */ - for ( n = 0; n < cur_num_H; n ++, at ++ ) { - for ( m = 0; m < at->valence; m ++ ) { - at->neighbor[m] += tot_num_at; - } - } - } - /* composite counts */ - composite_norm_data[jj].bHasIsotopicLayer |= all_inp_norm_data[i][k].bHasIsotopicLayer; - composite_norm_data[jj].num_isotopic += all_inp_norm_data[i][k].num_isotopic; - composite_norm_data[jj].num_bonds += all_inp_norm_data[i][k].num_bonds; - composite_norm_data[jj].bTautomeric += (j == jj) && all_inp_norm_data[i][k].bTautomeric; - composite_norm_data[jj].nNumRemovedProtons += all_inp_norm_data[i][k].nNumRemovedProtons; - for ( n = 0; n < NUM_H_ISOTOPES; n ++ ) { - composite_norm_data[jj].nNumRemovedProtonsIsotopic[n] += all_inp_norm_data[i][k].nNumRemovedProtonsIsotopic[n]; - composite_norm_data[jj].num_iso_H[n] += all_inp_norm_data[i][k].num_iso_H[n]; - } - /* - composite_norm_data[j].num_at += cur_num_at + cur_num_H; - composite_norm_data[j].num_removed_H += cur_num_H; - */ - /* total count */ - tot_num_at += cur_num_at; - tot_num_H += cur_num_H; - /* offset for the next component */ - if ( composite_norm_data[jj].nOffsetAtAndH ) { - composite_norm_data[jj].nOffsetAtAndH[2*i] = tot_num_at; - composite_norm_data[jj].nOffsetAtAndH[2*i+1] = num_at[jj]+tot_num_H; - } - } - if ( tot_num_at != num_at[jj] || - num_at[jj] + tot_num_H != num_inp_at[jj] ) { - goto exit_error; /* miscount */ - } - composite_norm_data[jj].bExists = (tot_num_at>0); - ret |= indicator; - } - } - return ret; - - - - - -exit_error: - return ret; -} -#endif /* } COMPILE_ANSI_ONLY */ - - diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/strutil.c b/INCHI-1-SRC/INCHI_API/inchi_dll/strutil.c deleted file mode 100644 index 504a09a..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/strutil.c +++ /dev/null @@ -1,4621 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include - -#include "mode.h" - -#include "inpdef.h" -#include "util.h" -#include "ichi.h" -#include "strutil.h" -#include "ichierr.h" - -#include "ichicomp.h" -#include "extr_ct.h" -#include "ichister.h" - -#include "ichi_io.h" - - -#define FIX_P_IV_Plus_O_Minus /* added fix to remove_ion_pairs() -- 2010-03-17 DT */ - -/* local prototypes */ -int cmp_components( const void *a1, const void *a2 ); -/*int mark_one_struct_component( inp_ATOM* at, int j, AT_NUMB *mark, AT_NUMB num_disconnected_components );*/ -INChI_Stereo *Alloc_INChI_Stereo(int num_at, int num_bonds); -int RemoveInpAtBond( inp_ATOM *at, int iat, int k ); -int DisconnectInpAtBond( inp_ATOM *at, AT_NUMB *nOldCompNumber, int iat, int neigh_ord ); -int move_explicit_Hcation(inp_ATOM *at, int num_at, int iat, int iat_H, int bInAllComponents); -int DisconnectOneLigand( inp_ATOM *at, AT_NUMB *nOldCompNumber, S_CHAR *bMetal, char *elnumber_Heteroat, - int num_halogens, int num_atoms, int iMetal, int jLigand, INCHI_MODE *bTautFlagsDone ); -int bIsAmmoniumSalt( inp_ATOM *at, int i, int *piO, int *pk, S_CHAR *num_explicit_H ); -int DisconnectAmmoniumSalt ( inp_ATOM *at, int i, int iO, int k, S_CHAR *num_explicit_H ); -/*int bIsMetalSalt( inp_ATOM *at, int i ); - moved to strutil,h */ -int DisconnectMetalSalt( inp_ATOM *at, int i ); -int bIsMetalToDisconnect(inp_ATOM *at, int i, int bCheckMetalValence); - -int get_iat_number( int el_number, const int el_num[], int el_num_len ); -int tot_unsat( int unsat[] ); -int max_unsat( int unsat[] ); - -double dist3D( inp_ATOM *at1, inp_ATOM *at2 ); -double dist2D( inp_ATOM *at1, inp_ATOM *at2 ); -double dist_from_segm( double x, double y, double x1, double y1, double x2, double y2); -int segments_intersect( double x11, double y11, double x12, double y12, /* segment #1 */ - double x21, double y21, double x22, double y22 ); -double GetMinDistDistribution( inp_ATOM *at, int num_at, int iat, int iat_H, - int bInAllComponents, double min_dist[], int num_segm ); - -int nFindOneOM(inp_ATOM *at, int at_no, int ord_OM[], int num_OM); -int the_only_doublet_neigh(inp_ATOM *at, int i1, int *ineigh1, int *ineigh2); - - -#ifndef NUMH -#define NUM_ISO_H(AT,N) (AT[N].num_iso_H[0]+AT[N].num_iso_H[1]+AT[N].num_iso_H[2]) -#define NUMH(AT,N) (AT[N].num_H+NUM_ISO_H(AT,N)) -#endif - -/************************************************************************/ -int the_only_doublet_neigh(inp_ATOM *at, int i1, int *ineigh1, int *ineigh2) -{ - int i, neigh1, num_rad1=0, num_rad2=0; - inp_ATOM *a = at+i1, *b; - if ( RADICAL_DOUBLET != a->radical ) - return -1; - for ( i = 0; i < a->valence; i ++ ) { - b = at + (neigh1 = (int)a->neighbor[i]); - if ( RADICAL_DOUBLET == b->radical ) { - num_rad1 ++; - *ineigh1 = i; - } - } - if ( 1 == num_rad1 ) { - a = at + (neigh1 = (int)a->neighbor[*ineigh1]); - for ( i = 0; i < a->valence; i ++ ) { - b = at +(int)a->neighbor[i]; - if ( RADICAL_DOUBLET == b->radical ) { - num_rad2 ++; - *ineigh2 = i; - } - } - if ( 1 == num_rad2 ) { - return neigh1; - } - } - return -1; -} - -/************************************************************************/ -int fix_odd_things( int num_atoms, inp_ATOM *at, int bFixBug, int bFixNonUniformDraw ) -{ /* 0 1 2 3 4 5 6 7 8 9 */ - static const char el[] = "N;P;As;Sb;O;S;Se;Te;"; /* 8 elements + C, Si */ - static U_CHAR en[10]; /* same number: 8 elements */ - static int ne=0, ne2; /* will be 8 and 10 */ - static int el_number_P; - static int el_number_H; - static int el_number_C; - static int el_number_O; - static int el_number_Si; - -#define FIRST_NEIGHB2 4 -#define FIRST_CENTER2 5 -#define NUM_CENTERS_N 4 - - int i1, i2, k1, k2, c, num_changes = 0; - char elname[ATOM_EL_LEN]; - - - /* constants for element numbers */ - enum elems { dNone, dCl=17,dBr=35,dI=53,dAt=85,dO=8,dS=16,dSe=34,dTe=52, dP=15, dC=6, dN=7 } ; - - - - - if (bFixNonUniformDraw) - { - - - /* Correct non-uniformly drawn oxoanions and amidinium cations. */ - { - - /* For central halogen, apply the following correftion rules: - - O O(-) - || | - O=Hal(-)=O ===> O=Hal=O - || || - O O - - (perchlorate, etc.) - - - O O(-) - || | - Hal(-)=O ===> Hal=O - || || - O O - - (chlorate, etc.) - - O O(-) - || | - Hal(-)=O ===> Hal=O - - - (chlorite, etc.) - - Hal(-)=O ===> Hal-O(-) - - - (hypochlorite, etc.) - - - - For halcogenes (S, Se, Te) - Y Y(-) - || | - RnX(-) ===> RnX - - if: - 1) (X = S, Y = O) || (X = Se, Y = S, O) || (X = Te, Y = O, S, Se) - 2) valence of X exceeds 6, in initially drawn form - - - So the following is corrected: - - O O(-) - || | - O=S(-)-R ===> O=S-R - || || - O O - - or - - O O- - || | - F5Te(-) ===> F5Te - - but the following remains unchanged: - - - O - || - O=S(-)-R - - - The central atom (of IUPAC Group 16-17) is shown as negative but it contains double bond(s) - to terminal atom of greater electronegativity (of Group 16). - The central atom is halcogen (S,Se,Te) in highest oxidation state or halogen. - - Fix: - move negative charge to terminal atom and change double bond to a single one. - - Eligible central atom Eligible terminal atom at double bond's end - Cl O - Br O - I O - [At S,Se,Te] - S O - Se O,S - Te O, S, Se - - Comments: - 1. Central atoms of Groups 13-15 are not considered. - 2. Pauling electronegativities are: - F(3.98) > O(3.44) > Cl (3.16) > N (3.04) > Br(2.96) > I(2.66) > S(2.58) > Se(2.55) > At (2.2) > Te(2.1) - - - */ - - - static U_CHAR allowed_elnums_center_halogen[] = {dCl, dBr, dI, dAt} ; - static U_CHAR allowed_elnums_center_halcogen[] = {dS, dSe, dTe} ; - - int en_center; - int i, j, k; - - for (i=0; i 0? at[i].iso_atw_diff-1 : at[i].iso_atw_diff; - continue; - } - /* From same-element candidates, select one with less isotopic mass (arbitrary choice). */ - else if (en_term==min_en) - { - iso = at[j].iso_atw_diff > 0? at[i].iso_atw_diff-1 : at[i].iso_atw_diff; - if ( iso =0) - { - at[i].charge = 0; - at[jj].charge = -1; - at[i].bond_type[kk] = BOND_TYPE_SINGLE; - at[jj].bond_type[0] = BOND_TYPE_SINGLE; - at[i].bond_stereo[kk] = at[jj].bond_stereo[0] = 0; - at[i].chem_bonds_valence--; - at[jj].chem_bonds_valence--; - num_changes ++; - } - } - - } /* end of search for candidate centers. */ - - } /* end of correcting oxoanions */ - - - - - /* Correct non-uniformly drawn amidinium cations. */ - { - - /* Amidines include carboxamidines RC(=NR)NR2, - sulfinamidines RS(=NR)NR2 and phosphinamidines, R2P(=NR)NR2. - - - NR NR - | | - R"-Y-NHR' ===> R"-Y=N(+)HR' - (+) - - - Y = C, S, P - - - Fix: - move positive charge to nitrogen and change single bond to a double one. - - - Comment: - Fix is applied only if at least one of R's at N is hydrogen - (otherwise we have just a '+' delocalization which is already recognized). - - - */ - - static U_CHAR allowed_elnums_center[] = {dC, dS, dP} ; - int en_center; - int i, j, k, jj, kk; - int mismatch = 0, nuH=0, nuN = 0, nitrogens[MAXVAL]; - - for (i=0; i 3 ) || ( at[j].chem_bonds_valence > 3 ) ) - { - mismatch = 1; - break; - } - nuH+= NUMH(at,j); - nuN++; - if (jj<0) - { - jj = j; - kk = k; - } - } - } - - /* If OK, apply changes. */ - if (mismatch) continue; - if (nuN!=2) continue; - if (nuH<1) continue; - if (jj>=0) - { - at[i].charge = 0; - at[jj].charge = 1; - at[i].bond_type[kk] = BOND_TYPE_DOUBLE; - for ( k1 = 0; k1 < at[jj].valence && i != at[jj].neighbor[k1]; k1 ++ ) - ; - at[jj].bond_type[k1] = BOND_TYPE_DOUBLE; - at[i].chem_bonds_valence++; - at[jj].chem_bonds_valence++; - /* NB: do nothing with wedge stereo bonds (retain wedge) */ - num_changes ++; - } - - } /* end of search for candidate centers. */ - - } /* end of correcting amidiniums */ - - - - } /*( if (bFixNonUniformDraw) */ - - - - - if ( !ne ) - { - /* one time initialization */ - const char *b, *e; - int len; - for ( b = el; e = strchr( b, ';'); b = e+1 ) - { - len = e-b; - memcpy( elname, b, len ); - elname[len] = '\0'; - en[ne++] = get_periodic_table_number( elname ); - } - ne2 = ne; - el_number_P = get_periodic_table_number( "P" ); - el_number_H = get_periodic_table_number( "H" ); - el_number_O = get_periodic_table_number( "O" ); - en[ne2++] = el_number_C = get_periodic_table_number( "C" ); - en[ne2++] = el_number_Si = get_periodic_table_number( "Si" ); - } - - /* H(-)-X -> H-X(-); H(+)-X -> H-X(+) */ - for ( i1 = 0; i1 < num_atoms; i1 ++ ) - { - if ( 1 == at[i1].valence && - 1 == abs(at[i1].charge) && - (0 == at[i1].radical || RADICAL_SINGLET == at[i1].radical) && - BOND_TYPE_SINGLE == at[i1].bond_type[0] && - el_number_H == at[i1].el_number && - el_number_H != at[i2=(int)at[i1].neighbor[0]].el_number && - !NUMH(at,i1) && - !NUMH(at,i2) - ) - { - at[i2].charge += at[i1].charge; - at[i1].charge = 0; - } - } - - /* replace XHm(-)--Y==XHn(+) with XHm==Y--XHn, (n>=0 ,m>=0, X=N,P,As,Sb,O,S,Se,Te) */ - for ( i1 = 0; i1 < num_atoms; i1 ++ ) - { - if ( 1 != at[i1].charge || - at[i1].radical && RADICAL_SINGLET != at[i1].radical || - at[i1].chem_bonds_valence == at[i1].valence || - !memchr(en, at[i1].el_number, ne) || - get_el_valence( at[i1].el_number, at[i1].charge, 0 ) != at[i1].chem_bonds_valence+NUMH(at,i1) ) - { - continue; - } - - /* found a candidate at[i1] for X in XHn(+) */ - if ( 1 == at[i1].valence && - BOND_TYPE_DOUBLE == at[i1].bond_type[0] ) - { - c = (int)at[i1].neighbor[0]; - for ( k2 = 0; k2 < at[c].valence; k2 ++ ) - { - i2 = at[c].neighbor[k2]; - if ( 1 == at[i2].valence && - -1 == at[i2].charge && - at[i2].el_number == at[i1].el_number && /* exact match */ - (0 == at[i2].radical || RADICAL_SINGLET == at[i2].radical) && - BOND_TYPE_SINGLE == at[i2].bond_type[0] && - /*memchr(en, at[i2].el_number, ne) &&*/ - get_el_valence( at[i2].el_number, at[i2].charge, 0 ) == at[i2].chem_bonds_valence+NUMH(at,i2) ) { - /* found both X(-) and X(+); change bonds and remove charges */ - for ( k1 = 0; k1 < at[c].valence && i1 != at[c].neighbor[k1]; k1 ++ ) - ; - at[i1].charge = at[i2].charge = 0; - at[i1].bond_type[0] = at[c].bond_type[k1] = BOND_TYPE_SINGLE; - at[i1].chem_bonds_valence --; - at[i2].bond_type[0] = at[c].bond_type[k2] = BOND_TYPE_DOUBLE; - at[i2].chem_bonds_valence ++; - num_changes ++; - break; - } - } - } - else - { - /* explicit H case: detect H-neighbors and Y */ - int ineigh, neigh, i1_c, i2_c, num_H_i1, num_H_i2; - for ( ineigh = 0, num_H_i1 = 0, i1_c = -1; ineigh < at[i1].valence; ineigh ++ ) - { - neigh = at[i1].neighbor[ineigh]; - if ( at[neigh].el_number == el_number_H ) - { - if ( at[neigh].chem_bonds_valence == 1 && - (0 == at[neigh].radical || RADICAL_SINGLET == at[neigh].radical) ) - { - num_H_i1 ++; /* found H-neighbor */ - } - else - { - break; /* wrong neighbor */ - } - } - else if ( at[i1].bond_type[ineigh] == BOND_TYPE_DOUBLE ) - { - /* found a candidate for Y; bond must be double */ - i1_c = ineigh; - c = neigh; - } - } - if ( i1_c < 0 || num_H_i1 + 1 != at[i1].valence ) - { - continue; - } - for ( k2 = 0; k2 < at[c].valence; k2 ++ ) - { - i2 = at[c].neighbor[k2]; - if (-1 == at[i2].charge && - at[i2].el_number == at[i1].el_number && /* exact match */ - (0 == at[i2].radical || RADICAL_SINGLET == at[i2].radical) && - get_el_valence( at[i2].el_number, at[i2].charge, 0 ) == at[i2].chem_bonds_valence+NUMH(at,i2) ) - { - for ( ineigh = 0, num_H_i2 = 0, i2_c = -1; ineigh < at[i2].valence; ineigh ++ ) - { - neigh = at[i2].neighbor[ineigh]; - if ( at[neigh].el_number == el_number_H ) - { - if ( at[neigh].chem_bonds_valence == 1 && - (0 == at[neigh].radical || RADICAL_SINGLET == at[neigh].radical) ) - { - num_H_i2 ++; /* found H-neighbor */ - } - else - { - break; /* wrong neighbor */ - } - } - else - if ( c == neigh && at[i2].bond_type[ineigh] == BOND_TYPE_SINGLE ) - { - i2_c = ineigh; /* position of Y neighbor; bond must be single */ - } - else - { - break; - } - } - if ( num_H_i2 + (i2_c >= 0) != at[i2].valence ) - { - continue; - } - /* found both X(-) and X(+); change bonds and remove charges */ - for ( k1 = 0; k1 < at[c].valence && i1 != at[c].neighbor[k1]; k1 ++ ) - ; - at[i1].charge = at[i2].charge = 0; - at[i1].bond_type[i1_c] = at[c].bond_type[k1] = BOND_TYPE_SINGLE; - at[i1].chem_bonds_valence --; - at[i2].bond_type[i2_c] = at[c].bond_type[k2] = BOND_TYPE_DOUBLE; - at[i2].chem_bonds_valence ++; - num_changes ++; - break; - } - } - } - } - - /* Replace - - X- X X=O,S,Se,Te -- terminal atoms (NEIGHB2) - \ | \ || - >Y++ with >Y Y=S,Se,Te -- central cation (CENTER2) - / | / || - X- X Y valence=4, original Y bond valence = 4 - - - --- the following case of P is processed separately in remove_ion_pairs() - --- therefire, it has been disabled here, see #ifndef FIX_P_IV_Plus_O_Minus -- 2010-03-17 DT - - X- X X=O,S,Se,Te -- terminal atoms (NEIGHB2) - \ | \ || - >P+ with >P - / | / | - X- X- Y valence=4, original Y bond valence = 4 - - */ - - for ( i1 = 0; i1 < num_atoms; i1 ++ ) - { - if ( 1 == at[i1].valence && - -1 == at[i1].charge && - (0 == at[i1].radical || RADICAL_SINGLET == at[i1].radical) && - !NUMH(at,i1) && - BOND_TYPE_SINGLE == at[i1].bond_type[0] && - memchr( en+FIRST_NEIGHB2, at[i1].el_number, ne-FIRST_NEIGHB2 ) ) - { - int charge, i; - /* found a candidate for X */ - c = (int)at[i1].neighbor[0]; /* candidate for Y */ - if ( ((charge=2) == at[c].charge && memchr( en+FIRST_CENTER2, at[c].el_number, ne-FIRST_CENTER2) -#ifndef FIX_P_IV_Plus_O_Minus - || (charge=1) == at[c].charge && el_number_P==at[c].el_number -#endif - ) && - 4 == at[c].valence && - (0 == at[c].radical || RADICAL_SINGLET == at[c].radical ) && - at[c].valence == at[c].chem_bonds_valence && - !NUMH(at,c) ) - { - ; /* accept */ - } - else - { - continue; /* ignore at[i1] */ - } - for ( k2 = 0; k2 < at[c].valence; k2 ++ ) - { - i2 = at[c].neighbor[k2]; - if ( i2 == i1 ) - { - continue; - } - if ( 1 == at[i2].valence && - -1 == at[i2].charge && - memchr( en+FIRST_NEIGHB2, at[i2].el_number, ne-FIRST_NEIGHB2 ) && - /*at[i2].el_number == at[i1].el_number &&*/ /* exact match */ - (0 == at[i2].radical || RADICAL_SINGLET == at[i2].radical) && - !NUMH(at,i2) && - BOND_TYPE_SINGLE == at[i2].bond_type[0] ) - { - /* found both X(-) and X(-); change bonds and remove charges */ - for ( k1 = 0; k1 < at[c].valence && i1 != at[c].neighbor[k1]; k1 ++ ) - ; - for ( i = 0; i < charge; i ++ ) - { - /* in case of P it does not matter which X atom is neutralized - because of tautomerism. However, neutral central atom is important - for the neutralization of the components */ - switch ( i ) - { - case 0: - at[i1].charge ++; /* = 0; changed 2010-03-17 DT*/ - at[i1].bond_type[0] = at[c].bond_type[k1] = BOND_TYPE_DOUBLE; - at[i1].bond_stereo[0] = at[c].bond_stereo[k1] = 0; - at[i1].chem_bonds_valence ++; - at[c].chem_bonds_valence ++; - if ( bFixBug ) at[c].charge --; /* added 2010-03-17 DT*/ - num_changes ++; - break; - case 1: - at[i2].charge ++; /*= 0; changed 2010-03-17 DT*/ - at[i2].bond_type[0] = at[c].bond_type[k2] = BOND_TYPE_DOUBLE; - at[i2].bond_stereo[0] = at[c].bond_stereo[k2] = 0; - at[i2].chem_bonds_valence ++; - at[c].chem_bonds_valence ++; - if ( bFixBug ) at[c].charge --; /* added 2010-03-17 DT */ - num_changes ++; - break; - } - } -/* -- removed -- 2010-03-17 DT -#if ( FIX_ODD_THINGS_REM_Plus_BUG == 1 ) - at[c].charge -= charge; -#else - if ( bFixBug ) - { - at[c].charge -= charge; - } -#endif -*/ - break; - } - } - } - } - - - - - /* A(doublet)-B(doublet) -> A=B (A and B have no other doublet neighbors) */ - /* A(doublet)=B(doublet) -> A#B (A and B have no other doublet neighbors) */ - for( i1 = 0; i1 < num_atoms; i1 ++ ) - { - if ( RADICAL_DOUBLET == at[i1].radical && - 0 <= (i2=the_only_doublet_neigh(at, i1, &k1, &k2)) ) - { - if ( at[i1].bond_type[k1] <= BOND_TYPE_DOUBLE ) - { - at[i1].bond_type[k1] ++; - at[i1].chem_bonds_valence ++; - at[i2].bond_type[k2] ++; - at[i2].chem_bonds_valence ++; - at[i1].radical = 0; - at[i2].radical = 0; - } - } - } - -#if ( REMOVE_ION_PAIRS_EARLY == 1 ) - num_changes += remove_ion_pairs( num_atoms, at ); -#endif - - - - - return num_changes; -} - - - - - - - - -/************************************************************************/ -int post_fix_odd_things( int num_atoms, inp_ATOM *at ) -{ - int num_changes = 0; - /* currently does nothing */ - return num_changes; -} - - - -/************************************************************************/ -int nFindOneOM(inp_ATOM *at, int at_no, int ord_OM[], int num_OM) -{ - int i, n_OM, n_OM_best, best_value, cur_value, diff; - int num_best; - - if ( 1 == num_OM ) { - return ord_OM[0]; - } - if ( 1 > num_OM ) { - return -1; - } - - /* select neighbors with min. number of bonds */ - num_best = 1; - n_OM = (int)at[at_no].neighbor[ord_OM[0]]; - best_value = (int)at[n_OM].valence; - /* compare number of bonds; move indexes of the best neighbors to the first elements of ord_OM[] */ - for ( i = 1; i < num_OM; i ++ ) { - n_OM = at[at_no].neighbor[ord_OM[i]]; - cur_value = (int)at[n_OM].valence; - diff = cur_value - best_value; - if ( diff < 0 ) { - n_OM_best = n_OM; - best_value = cur_value; - ord_OM[0] = ord_OM[i]; - num_best = 1; - } else - if ( diff == 0 ) { /* was '=', pointed by WDI */ - ord_OM[num_best ++] = ord_OM[i]; - } - } - num_OM = num_best; - if ( 1 == num_OM ) { - return ord_OM[0]; - } - /* select neighbors with min. periodic numbers */ - num_best = 1; - n_OM = (int)at[at_no].neighbor[ord_OM[0]]; - best_value = (int)at[n_OM].el_number; - /* compare periodic numbers; move indexes of the best neighbors to the first elements of ord_OM[] */ - for ( i = 1; i < num_OM; i ++ ) { - n_OM = at[at_no].neighbor[ord_OM[i]]; - cur_value = (int)at[n_OM].el_number; - diff = cur_value - best_value; - if ( diff < 0 ) { - n_OM_best = n_OM; - best_value = cur_value; - ord_OM[0] = ord_OM[i]; - num_best = 1; - } else - if ( diff == 0 ) { /* was '=', pointed by WDI */ - ord_OM[num_best ++] = ord_OM[i]; - } - } - num_OM = num_best; - if ( 1 == num_OM ) { - return ord_OM[0]; - } - /* if neighbors are not terminal atoms then reject */ - if ( 1 < at[n_OM].valence ) { - return -1; - } - /* if neighbors are terminal atoms then the one without isotope or with lightest isotope */ - num_best = 1; - n_OM = (int)at[at_no].neighbor[ord_OM[0]]; - best_value = (int)at[n_OM].iso_atw_diff; - /* compare periodic numbers; move indexes of the best neighbors to the first elements of ord_OM[] */ - for ( i = 1; i < num_OM; i ++ ) { - n_OM = at[at_no].neighbor[ord_OM[i]]; - cur_value = (int)at[n_OM].el_number; - diff = cur_value - best_value; - if ( (!cur_value && best_value) || diff < 0 ) { - n_OM_best = n_OM; - best_value = cur_value; - ord_OM[0] = ord_OM[i]; - num_best = 1; - } else - if ( diff == 0 ) { /* was '=', pointed by WDI */ - ord_OM[num_best ++] = ord_OM[i]; - } - } - num_OM = num_best; - if ( 1 == num_OM ) { - return ord_OM[0]; - } - /* return any */ - return ord_OM[0]; -} - - - -/************************************************************************/ -/* the bonds are fixed in fix_special_bonds() */ -int remove_ion_pairs( int num_atoms, inp_ATOM *at ) -{ - int num_changes = 0; - - /* 0 1 2 3 4 5 6 7 8 9 8 9 */ -#if ( FIX_REM_ION_PAIRS_Si_BUG == 1 ) - static const char el[] = "N;P;As;Sb;O;S;Se;Te;C;Si;"; /* 8 elements + C, Si */ -#else - static const char el[] = "N;P;As;Sb;O;S;Se;Te;C;Si"; /* 8 elements + C, Si */ -#endif - static char en[12]; /* same number: 8 elements */ - static int ne=0; /* will be 8 and 10 */ - -#define ELEM_N_FST 0 -#define ELEM_N_LEN 4 -#define ELEM_O_FST 4 -#define ELEM_O_LEN 4 -#define ELEM_C_FST 8 -#define ELEM_C_LEN 2 - -#define MAX_NEIGH 6 - - int i, n, n2, i1, i2, i3, i4, type, chrg; - int num_C_II=0, num_C_plus=0, num_C_minus=0, num_N_plus=0, num_N_minus=0, num_O_plus=0, num_O_minus=0, num_All; -#ifdef FIX_P_IV_Plus_O_Minus - int num_P_IV_plus=0; /* added 2010-03-17 DT */ -#endif - inp_ATOM *a; - char elname[ATOM_EL_LEN], *p; - if ( !ne ) { /* one time initialization */ - const char *b, *e; - int len; - for ( b = el; e = strchr( b, ';'); b = e+1 ) { - len = e-b; - memcpy( elname, b, len ); - elname[len] = '\0'; - en[ne++] = get_periodic_table_number( elname ); - } - en[ne] = '\0'; - } - - /****** count candidates ********/ - for ( i = 0, a = at; i < num_atoms; i ++, a++ ) { - if ( 1 == (chrg=a->charge) || -1 == chrg ) { - if ( p = (char*)memchr( en, a->el_number, ne) ) { - n = p - en; - if ( n >= ELEM_C_FST ) { - if ( chrg > 0 ) - num_C_plus ++; - else - num_C_minus ++; - } else - if ( n >= ELEM_O_FST ) { - if ( chrg > 0 ) - num_O_plus ++; - else - num_O_minus ++; - } else { - if ( chrg > 0 ) - num_N_plus ++; - else - num_N_minus ++; -#ifdef FIX_P_IV_Plus_O_Minus - num_P_IV_plus += n > 0 && chrg == 1 && a->valence == 4 && a->chem_bonds_valence == 4; /* added 2010-03-17 DT */ -#endif - } - } - } else - if ( !chrg && a->chem_bonds_valence + NUMH(a, 0) == 2 && - get_el_valence( a->el_number, 0, 0 ) == 4 && - NULL != memchr( en+ELEM_C_FST, a->el_number, ELEM_C_LEN) ) { - num_C_II ++; - } - } - num_All = num_C_II + num_C_plus + num_C_minus + num_N_plus + num_N_minus + num_O_plus + num_O_minus; - /* do not add num_P_IV_plus ! -- 2010-03-17 DT */ - if ( !num_All ) { - return 0; - } - - /**************************************************************************/ - /*************************** Terminal ion pairs ***************************/ - /**************************************************************************/ - - /*------------------------------------------------------------------------- - Pair type 1 N=N,P,As,Sb; O=O,S,Se,Te - =========== - - X X if X is another -O(-) then neutralize O(-) - | | that has the smallest periodic table number - O=N(+)-O(-) => O=N=O - i n - --------------------------------------------------------------------------*/ - for ( type = 1; type <= 18; type ++ ) { - if ( (!type || 1 == type) ) { - for ( i = 0; i < num_atoms && 0 < num_N_plus && 0 < num_O_minus; i ++ ) { - if ( 1 == at[i].charge && 3 == nNoMetalNumBonds(at, i) && - 4 == nNoMetalBondsValence(at, i) && - NULL != memchr( en+ELEM_N_FST, at[i].el_number, ELEM_N_LEN) ) { - int num_OM = 0, ord_OM[3]; /* -O(-) */ - int num_O = 0; /* =O */ - int num_O_other = 0; - for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { - n = at[i].neighbor[i1]; - if ( 1 == nNoMetalNumBonds(at, n) && 0 == num_of_H( at, n ) && - NULL != (p = (char*)memchr( en+ELEM_O_FST, at[n].el_number, ELEM_O_LEN)) ) { - if ( BOND_TYPE_SINGLE == at[i].bond_type[i1] && - -1 == at[n].charge ) { - ord_OM[num_OM ++] = i1; - } else - if ( BOND_TYPE_DOUBLE == at[n].bond_type[0] && - 0 == at[n].charge ) { - num_O ++; - } else { - num_O_other ++; - } - } - } - if ( num_OM > 0 && num_O > 0 && !num_O_other && - 0 <= (i1=nFindOneOM(at, i, ord_OM, num_OM)) ) { - /* remove charges and increase bond order */ - n = at[i].neighbor[i1]; - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[i].charge --; - at[n].charge ++; - at[i].radical = 0; - at[n].radical = 0; - num_changes ++; - num_N_plus --; - num_O_minus --; - num_All -= 2; - } - } - } -#ifdef FIX_P_IV_Plus_O_Minus - /*------------------------------------------------------------------------- - Pair type 1a P=P,As,Sb; O=O,S,Se,Te -- added 2010-03-17 - ============= - - X X if X, Y, or Z is another -O(-) then neutralize O(-) - | | that has the smallest periodic table number - Y-P(+)-O(-) => Y-P=O - |i n | - Z Z - - --------------------------------------------------------------------------*/ - for ( i = 0; i < num_atoms && 0 < num_P_IV_plus /*&& 0 < num_N_plus*/ && 0 < num_O_minus; i ++ ) { - if ( 1 == at[i].charge && 4 == nNoMetalNumBonds(at, i) && - 4 == nNoMetalBondsValence(at, i) && - NULL != memchr( en+ELEM_N_FST+1, at[i].el_number, ELEM_N_LEN-1) ) { - int num_OM = 0, ord_OM[4]; /* -O(-) */ - /*int num_O = 0;*/ /* =O */ - int num_O_other = 0; - for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { - n = at[i].neighbor[i1]; - if ( 1 == nNoMetalNumBonds(at, n) && 0 == num_of_H( at, n ) && - NULL != (p = (char*)memchr( en+ELEM_O_FST, at[n].el_number, ELEM_O_LEN)) ) { - if ( BOND_TYPE_SINGLE == at[i].bond_type[i1] && - -1 == at[n].charge ) { - ord_OM[num_OM ++] = i1; - /* - } - if ( BOND_TYPE_DOUBLE == at[n].bond_type[0] && - 0 == at[n].charge ) { - num_O ++; - */ - } else { - num_O_other ++; - } - } - } - if ( num_OM > 0 /*&& num_O > 0 && !num_O_other*/ && - 0 <= (i1=nFindOneOM(at, i, ord_OM, num_OM)) ) { - /* remove charges and increase bond order */ - n = at[i].neighbor[i1]; - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[i].charge --; - at[n].charge ++; - at[i].radical = 0; - at[n].radical = 0; - num_changes ++; - num_N_plus --; - num_O_minus --; - num_P_IV_plus --; - num_All -= 2; - } - } - } -#endif /* FIX_P_IV_Plus_O_Minus */ - } - /*------------------------------------------------------------------------- - Terminal pair types: 2,3,4,5,6,7,8,9 N=N,P,As,Sb; O=O,S,Se,Te; C=C,Si - ==================================== - type # - 2 2: O=N-C(II)- => O=N#C- N=N,P,As,Sb; O=O,S,Se,Te; C=C,Si - 3 9: O=O(+)-C(-)(III) => O=O=C(IV) - 4 3: O(-)-N(+)(IV) => O=N(V) (input structure has at least 1 double bond) - 5 4: O(-)-O(+)(III) => O=O(IV) - 6 8: O(-)-O-C(+)(III) => O=O=C(IV) - 7 5: N(-)=N(+)(IV) => N#N(V) allow terminal H on N(-) - 8 6: N(-)=O(+)(III) => N#O- - 9 7: N(-)=C(+)(III) => N#C- - --------------------------------------------------------------------------*/ - if ( !type || 2 <= type && type <= 9 ) { - for ( i = 0; i < num_atoms && 0 < num_All; i ++ ) { - if ( 0 == at[i].charge && 1 == nNoMetalNumBonds(at, i) && 2 == nNoMetalBondsValence(at, i) && - 0 == num_of_H( at, i ) && - NULL != memchr( en+ELEM_O_FST, at[i].el_number, ELEM_O_LEN) && - 0 <= ( i1 = nNoMetalNeighIndex(at, i)) && - at[i].bond_type[i1] <= BOND_TYPE_TRIPLE ) { - /* terminal O= */ - n = at[i].neighbor[i1]; - if ( (!type || type == 2) && 0 < num_C_II ) { /* avoid alternating bonds */ - if ( 0 == at[n].charge && - 2 == nNoMetalNumBonds(at, n) && 3 == nNoMetalBondsValence(at, n) && - 0 == num_of_H( at, n ) && - NULL != memchr( en+ELEM_N_FST, at[n].el_number, ELEM_N_LEN) && - 0 <= (i2 = nNoMetalOtherNeighIndex( at, n, i ) ) && - at[n].bond_type[i2] <= BOND_TYPE_TRIPLE ) { - /* i2 = index of opposite to at[i] neighbor of at[n] */ - /*i2 = (at[n].neighbor[0] == i);*/ - n2 = at[n].neighbor[i2]; - if ( 0 == at[n2].charge && - 2 == at[n2].valence && 2 == at[n2].chem_bonds_valence && - 0 == num_of_H( at, n2 ) && - NULL != memchr( en+ELEM_C_FST, at[n2].el_number, ELEM_C_LEN) ) { - /* i n n2 */ - /* found O=N-C(II)- */ - /* convert O=N-C(II)- => O=N#C- */ - i3 = (at[n2].neighbor[0] != n); /* index of at[n] neighbor of n2 */ - at[ n].chem_bonds_valence = 5; /* N */ - at[n2].chem_bonds_valence = 4; /* C */ - at[ n].bond_type[i2] = BOND_TYPE_TRIPLE; - at[n2].bond_type[i3] = BOND_TYPE_TRIPLE; - at[n2].radical = 0; - num_changes ++; - num_C_II --; - num_All --; - continue; - } - } - } - if ( (!type || type == 3) && 0 < num_O_plus && 0 < num_C_minus ) { - if ( 1 == at[n].charge && 2 == nNoMetalNumBonds(at, n) && 3 == nNoMetalBondsValence(at, n) && - 0 == num_of_H( at, n ) && - NULL != memchr( en+ELEM_O_FST, at[n].el_number, ELEM_O_LEN) && - 0 <= (i2 = nNoMetalOtherNeighIndex( at, n, i ) ) && - at[n].bond_type[i2] <= BOND_TYPE_TRIPLE ) { - /* found O=O(+)- */ - /* i2 = index of opposite to at[i] neighbor of at[n] */ - /*i2 = (at[n].neighbor[0] == i);*/ - n2 = at[n].neighbor[i2]; - if ( -1 == at[n2].charge && 3 >= nNoMetalNumBonds(at, n2) && 3 == nNoMetalBondsValence(at, n2)+NUMH(at,n2) && - NULL != memchr( en+ELEM_C_FST, at[n2].el_number, ELEM_C_LEN) ) { - /* i n n2 */ - /* found found O=O(+)-C(-)(III) */ - /* convert O=O(+)-C(-)(III) => O=O=C(IV) */ - i3 = (at[n2].neighbor[0] != n); /* index of at[n] neighbor of n2 */ - at[ n].charge --; - at[n2].charge ++; - at[ n].chem_bonds_valence += 1; /* =O- => =O= */ - at[n2].chem_bonds_valence += 1; /* -C => =C */ - at[ n].bond_type[i2] = BOND_TYPE_DOUBLE; - at[n2].bond_type[i3] = BOND_TYPE_DOUBLE; - num_changes ++; - num_O_plus --; - num_C_minus --; - num_All -= 2; - continue; - } - } - } - } else - if ( -1 == at[i].charge && - 0 < num_O_minus + num_N_minus && - 0 < num_N_plus + num_O_plus + num_C_plus && - 1 == nNoMetalNumBonds(at, i) && 1 == nNoMetalBondsValence(at, i) && - 0 == num_of_H( at, i ) && - NULL != memchr( en+ELEM_O_FST, at[i].el_number, ELEM_O_LEN) && - 0 <= (i1 = nNoMetalNeighIndex( at, i )) && - at[i].bond_type[i1] <= BOND_TYPE_TRIPLE ) { - - /* terminal O(-)- */ - - n = at[i].neighbor[i1]; - - if ( (!type || type == 4) && 0 < num_O_minus && 0 < num_N_plus && /* O(-)-N(+)(IV) */ - 1 == at[n].charge && 3 >= nNoMetalNumBonds(at, n) && 4 == nNoMetalBondsValence(at, n) && - 0 == num_of_H( at, n ) && - NULL != memchr( en+ELEM_N_FST, at[n].el_number, ELEM_N_LEN) /* except >O(+)- */ - ) { - /* found O(-)-N(+)(IV) */ - /* convert O(-)-N(+)(IV) => O=N(V) */ - - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; /* index of at[i] neighbor of at[n] */ - at[i].charge ++; - at[n].charge --; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - num_changes ++; - num_O_minus --; - num_N_plus --; - num_All -= 2; - continue; - } - - if ( (!type || type == 5) && 0 < num_O_minus && 0 < num_O_plus &&/* O(-)-O(+)(III) */ - 1 == at[n].charge && 3 >= nNoMetalNumBonds(at, n) && 3 == nNoMetalBondsValence(at, n) && - 0 == num_of_H( at, n ) && - NULL != memchr( en+ELEM_O_FST, at[n].el_number, ELEM_O_LEN) /* except >O(+)- */ - ) { - /* found O(+)(III) */ - /* convert O(-)-O(+)(III) => O=O(IV) */ - - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; /* index of at[i] neighbor of at[n] */ - at[i].charge ++; - at[n].charge --; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - num_changes ++; - num_O_minus --; - num_O_plus --; - num_All -= 2; - continue; - } - /* i n n2 */ - if ( (!type || type == 6) && /* O(-)-O-C(+)(III) */ - 0 < num_O_minus && 0 < num_C_plus && - 0 == at[n].charge && 2 == nNoMetalNumBonds(at, n) && 2 == nNoMetalBondsValence(at, n) && - 0 == num_of_H( at, n ) && - NULL != memchr( en+ELEM_O_FST, at[n].el_number, ELEM_O_LEN) && - 0 <= (i2=nNoMetalOtherNeighIndex( at, n, i )) && - at[n].bond_type[i2] <= BOND_TYPE_TRIPLE ) { - /* found O(-)-O- */ - /* i2 = index of opposite to at[i] neighbor of at[n] */ - /*i2 = (at[n].neighbor[0] == i);*/ - n2 = at[n].neighbor[i2]; - if ( 1 == at[n2].charge && 3 >= nNoMetalNumBonds(at, n2) && - 3 == nNoMetalBondsValence(at, n2)+NUMH(at,n2) && - NULL != memchr( en+ELEM_C_FST, at[n2].el_number, ELEM_C_LEN) ) { - /* i n n2 */ - /* found O(-)-O-C(+)(III) */ - /* convert O(-)-O-C(+)(III) => O=O=C(IV) */ - /*i3 = (at[n2].neighbor[0] != n);*/ /* i3 = index of at[n] neighbor of at[n2] */ - i3 = is_in_the_list( at[n2].neighbor, (AT_NUMB)n, at[n2].valence ) - at[n2].neighbor; - /*i4 = index of at[i] in the adjacency list of at[n] */ - i4 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; - at[ i].charge ++; - at[n2].charge --; - at[ i].chem_bonds_valence += 1; /* O- => O= */ - at[ n].chem_bonds_valence += 2; /* -O- => =O= */ - at[n2].chem_bonds_valence += 1; /* -C => =C */ - at[ i].bond_type[i1] = BOND_TYPE_DOUBLE; - at[ n].bond_type[i4] = BOND_TYPE_DOUBLE; - at[ n].bond_type[i2] = BOND_TYPE_DOUBLE; - at[n2].bond_type[i3] = BOND_TYPE_DOUBLE; - num_changes ++; - num_O_minus --; - num_C_plus --; - num_All -= 2; - continue; - } - } - } else - if ( -1 == at[i].charge && 0 < num_N_minus && 0 < num_N_plus+num_O_plus+num_C_plus && - 1 == nNoMetalNumBonds(at, i) && 2 == nNoMetalBondsValence(at, i)+NUMH(at, i) && - /*0 == num_of_H( at, i ) &&*/ - NULL != memchr( en+ELEM_N_FST, at[i].el_number, ELEM_N_LEN) && - 0 <= (i1 = nNoMetalNeighIndex( at, i )) && - at[i].bond_type[i1] <= BOND_TYPE_TRIPLE ) { - /* terminal N(-)= */ - n = at[i].neighbor[i1 = 0]; - if ( (!type || type == 7) && 0 < num_N_plus && /* N(-)=N(+)(IV) */ - 1 == at[n].charge && 3 >= nNoMetalNumBonds(at, n) && 4 == nNoMetalBondsValence(at, n) && - 0 == num_of_H( at, n ) && - NULL != memchr( en+ELEM_N_FST, at[n].el_number, ELEM_N_LEN) - ) { - /* found N(-)-N(+)(IV) */ - /* convert N(-)=N(+)(IV) => N#N(V) */ - - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; /* index of at[i] neighbor of at[n] */ - at[i].charge ++; - at[n].charge --; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - num_changes ++; - num_N_minus --; - num_N_plus --; - num_All -= 2; - continue; - } - if ( (!type || type == 8) && 0 < num_O_plus && /* N(-)=O(+)(III) */ - 1 == at[n].charge && 2 == nNoMetalNumBonds(at, n) && 3 == nNoMetalBondsValence(at, n) && - 0 == num_of_H( at, n ) && - NULL != memchr( en+ELEM_O_FST, at[n].el_number, ELEM_O_LEN) - ) { - /* found N(-)-O(+)(III) */ - /* convert N(-)=O(+)(III) => N#O(IV)- */ - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; /* index of at[i] neighbor of at[n] */ - at[i].charge ++; - at[n].charge --; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - num_changes ++; - num_N_minus --; - num_O_plus --; - num_All -= 2; - continue; - } - if ( (!type || type == 9) && 0 < num_C_plus && /* N(-)=C(+)(III) */ - 1 == at[n].charge && 2 == at[n].valence && 3 == at[n].chem_bonds_valence && - 0 == num_of_H( at, n ) && - NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) - ) { - /* found N(-)=C(+)(III) */ - /* convert N(-)=C(+)(III) => N#C(IV)- */ - - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; /* index of at[i] neighbor of at[n] */ - at[i].charge ++; - at[n].charge --; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - num_changes ++; - num_N_minus --; - num_C_plus --; - num_All -= 2; - continue; - } - } - } - } - - /**************************************************************************/ - /*********************** NON-Terminal ion pairs ***************************/ - /**************************************************************************/ - /*------------------------------------------------------------------------- - Non-Terminal pair types: 10,11,12,13,14 N=N,P,As,Sb; O=O,S,Se,Te; C=C,Si - ======================================== - - 10: N(+)(IV)-C(-)(III) => N(V)=C(IV) (N has 3 or 2 bonds) - 11: N(+)(IV)=C(-)(III) => N(V)#C(IV) (N has 3 or 2 bonds) - 12: N(+)(IV)-N(-)(II) => N(V)=N(III) (allow terminal H on N(-)) - 13: -O(+)-C(-)(III) => -O=C- - 14: -O(+)=C(-)(III) => -O#C- - 15: O(+)(III)-N(-)(II) => O(IV)=N(III) (allow terminal H on N(-)) - --------------------------------------------------------------------------*/ - if ( !type || 10 <= type && type <= 15 ) { - for ( i = 0; i < num_atoms && 0 < num_All; i ++ ) { - if ( 1 == at[i].charge && - 0 < num_N_plus + num_O_plus && 0 < num_C_minus + num_N_minus && - 4 >= nNoMetalNumBonds(at, i) && 4 == nNoMetalBondsValence(at, i) && - 0 == num_of_H( at, i ) && - NULL != memchr( en+ELEM_N_FST, at[i].el_number, ELEM_N_LEN) ) { - /* found non-terminal N(+)(IV) */ - if ( (!type || 10 == type) && 0 < num_N_plus && 0 < num_C_minus ) { - int num_neigh = 0, pos_neigh = -1; - for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { - n = at[i].neighbor[i1]; - if ( -1 == at[n].charge && 3 >= at[n].valence && 3 == at[n].chem_bonds_valence+NUMH(at,n) && - /*0 == at[n].num_H &&*/ - at[i].bond_type[i1] == BOND_TYPE_SINGLE && - NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) { - /* found N(+)(IV)-C(-)(III); prepare conversion to N(V)=C(IV) */ - num_neigh ++; - pos_neigh = i1; - } - } - i1=pos_neigh; - if ( 1 == num_neigh && - at[i].bond_type[i1] <= BOND_TYPE_TRIPLE && - !has_other_ion_neigh( at, i, n=at[i].neighbor[i1], en, ne ) && - !has_other_ion_neigh( at, n, i, en, ne )) { - /*n = at[i].neighbor[i1=pos_neigh];*/ - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; - at[i].charge --; - at[n].charge ++; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - num_changes ++; - num_C_minus --; - num_N_plus --; - num_All -= 2; - continue; - } - } - if ( (!type || 11 == type) && 0 < num_N_plus && 0 < num_C_minus ) { - int num_neigh = 0, pos_neigh = -1; - for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { - n = at[i].neighbor[i1]; - if ( -1 == at[n].charge && 3 >= at[n].valence && 3 == at[n].chem_bonds_valence+NUMH(at,n) && - /*0 == at[n].num_H &&*/ - at[i].bond_type[i1] == BOND_TYPE_DOUBLE && - NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) { - /* found N(+)(IV)=C(-)(III); prepare conversion to N(V)#C(IV) */ - num_neigh ++; - pos_neigh = i1; - } - } - if ( 1 == num_neigh && - !has_other_ion_neigh( at, i, n=at[i].neighbor[i1=pos_neigh], en, ne ) && - !has_other_ion_neigh( at, n, i, en, ne )) { - /*n = at[i].neighbor[i1=pos_neigh];*/ - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; - at[i].charge --; - at[n].charge ++; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - num_changes ++; - num_C_minus --; - num_N_plus --; - num_All -= 2; - continue; - } - } - if ( !type || 12 == type && 0 < num_N_plus && 0 < num_N_minus ) { - int num_neigh = 0, pos_neigh = -1; - for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { - n = at[i].neighbor[i1]; - if ( -1 == at[n].charge && 2 >= nNoMetalNumBonds(at, n) && - 2 == nNoMetalBondsValence(at, n)+NUMH(at, n) && - /*0 == num_of_H( at, n ) &&*/ - at[i].bond_type[i1] == BOND_TYPE_SINGLE && - NULL != memchr( en+ELEM_N_FST, at[n].el_number, ELEM_N_LEN) ) { - /* found N(+)(IV)=N(-)(II); prepare conversion to N(V)#N(III) */ - num_neigh ++; - pos_neigh = i1; - } - } - if ( 1 == num_neigh && - !has_other_ion_neigh( at, i, n=at[i].neighbor[i1=pos_neigh], en, ne ) && - !has_other_ion_neigh( at, n, i, en, ne )) { - /*n = at[i].neighbor[i1=pos_neigh];*/ - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; - at[i].charge --; - at[n].charge ++; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - num_changes ++; - num_N_minus --; - num_N_plus --; - num_All -= 2; - continue; - } - } - } else - if ( 1 == at[i].charge && - 0 < num_O_plus && 0 < num_C_minus + num_N_minus && - 3 >= nNoMetalNumBonds(at, i) && 3 == nNoMetalBondsValence(at, i) && - 0 == num_of_H( at, i ) && - NULL != memchr( en+ELEM_O_FST, at[i].el_number, ELEM_O_LEN) ) { - /* found non-terminal O(+)(III) */ - if ( (!type || 13 == type) && 0 < num_C_minus ) { - int num_neigh = 0, pos_neigh = -1; - for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { - n = at[i].neighbor[i1]; - if ( -1 == at[n].charge && 3 >= at[n].valence && 3 == at[n].chem_bonds_valence+NUMH(at,n) && - /*0 == at[n].num_H &&*/ - at[i].bond_type[i1] == BOND_TYPE_SINGLE && - NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) { - /* found O(+)(III)-C(-)(II); prepare conversion to O(IV)=C(IV) */ - num_neigh ++; - pos_neigh = i1; - } - } - if ( 1 == num_neigh && - !has_other_ion_neigh( at, i, n=at[i].neighbor[i1=pos_neigh], en, ne ) && - !has_other_ion_neigh( at, n, i, en, ne )) { - /*n = at[i].neighbor[i1=pos_neigh];*/ - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; - at[i].charge --; - at[n].charge ++; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - num_changes ++; - num_C_minus --; - num_O_plus --; - num_All -= 2; - continue; - } - } - if ( (!type || 14 == type) && 0 < num_C_minus ) { - int num_neigh = 0, pos_neigh = -1; - for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { - n = at[i].neighbor[i1]; - if ( -1 == at[n].charge && 3 >= at[n].valence && 3 == at[n].chem_bonds_valence+NUMH(at,n) && - /*0 == at[n].num_H &&*/ - at[i].bond_type[i1] == BOND_TYPE_DOUBLE && - NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) { - /* found O(+)(III)=C(-)(III); prepare conversion to O(IV)#C(IV) */ - num_neigh ++; - pos_neigh = i1; - } - } - if ( 1 == num_neigh && - !has_other_ion_neigh( at, i, n=at[i].neighbor[i1=pos_neigh], en, ne ) && - !has_other_ion_neigh( at, n, i, en, ne )) { - /*n = at[i].neighbor[i1=pos_neigh];*/ - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; - at[i].charge --; - at[n].charge ++; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - num_changes ++; - num_C_minus --; - num_O_plus --; - num_All -= 2; - continue; - } - } - if ( (!type || 15 == type) && 0 < num_N_minus ) { - int num_neigh = 0, pos_neigh = -1; - for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { - n = at[i].neighbor[i1]; - if ( -1 == at[n].charge && 2 >= nNoMetalNumBonds(at, n) && - 2 == nNoMetalBondsValence(at, n)+NUMH(at, n) && - /*0 == num_of_H( at, n ) &&*/ - at[i].bond_type[i1] == BOND_TYPE_SINGLE && - NULL != memchr( en+ELEM_N_FST, at[n].el_number, ELEM_N_LEN) ) { - /* found O(+)(III)=N(-)(II); prepare conversion to O(IV)#N(III) */ - num_neigh ++; - pos_neigh = i1; - } - } - if ( 1 == num_neigh && - !has_other_ion_neigh( at, i, n=at[i].neighbor[i1=pos_neigh], en, ne ) && - !has_other_ion_neigh( at, n, i, en, ne )) { - /*n = at[i].neighbor[i1=pos_neigh];*/ - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; - at[i].charge --; - at[n].charge ++; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - num_changes ++; - num_N_minus --; - num_O_plus --; - num_All -= 2; - continue; - } - } - } - } - } - /**************************************************************************/ - /*********************** NON-Terminal ion triples *************************/ - /**************************************************************************/ - /*------------------------------------------------------------------------- - Non-Terminal triple types: 16, 17, 18 N=N,P,As,Sb; O=O,S,Se,Te; C=C,Si - ======================================== - 16: C(+)(III)-O-N(-)(II) => C(IV)=O=N(III) (allow terminal H on N(-)) - - | | - 17: C(+)(III)-N-C(-)(III) => C(IV)=N=C(IV) - - 18: C(-)(III)-N=C(+)(III) => C(IV)=N#C(IV) (may have two or no charges) - C(IV)=N-C(II) => C(IV)=N#C(IV) - - */ - if ( (!type || 16 == type) && 0 < num_C_plus && 0 < num_N_minus ) { - int m[2], j[2], k; - for ( i = 0; i < num_atoms; i ++ ) { - if ( 0 == at[i].charge && 2 == nNoMetalNumBonds(at, i) && 2 == nNoMetalBondsValence(at, i) && - 0 == num_of_H( at, i ) && - 0 <= (j[0] = nNoMetalNeighIndex( at, i )) && - at[m[0]=at[i].neighbor[j[0]]].charge && - 0 <= (j[1] = nNoMetalOtherNeighIndex( at, i, m[0] )) && - 0 == at[m[0]].charge + at[m[1]=at[i].neighbor[j[1]]].charge && - 5 >= nNoMetalBondsValence(at, m[0]) + nNoMetalBondsValence(at, m[1]) && - /*5 >= at[m[0]].chem_bonds_valence + at[m[1]].chem_bonds_valence &&*/ - NULL != memchr( en+ELEM_O_FST, at[i].el_number, ELEM_O_LEN) ) { - /* found non-terminal A(+)-O-B(-); chem_bond_val of A+B <= 5 */ - int n_N=-1, n_C=-1, i_C=-1; - for ( k = 0; k < 2; k ++ ) { - n = m[k]; - if ( -1 == at[n].charge && 2 == nNoMetalNumBonds(at, n)+NUMH(at, n) && - /*0 == num_of_H( at, n ) &&*/ - NULL != memchr( en+ELEM_N_FST, at[n].el_number, ELEM_N_LEN) ) { - n_N = n; - } else - if ( 1 == at[n].charge && 3 == at[n].chem_bonds_valence+NUMH(at,n) && - NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) { - n_C = n; - i_C = k; - } - } - if ( n_C < 0 || n_N < 0 || - has_other_ion_in_sphere_2(at, n_C, n_N, en, ne ) || - has_other_ion_in_sphere_2(at, n_N, n_C, en, ne ) ) { - continue; - } - /* C(+)(III)-O-N(-)(II) => C(IV)=O=N(III) */ - for ( k = 0; k < 2; k ++ ) { - n = k? n_C : n_N; - i1 = k? j[i_C] : j[1-i_C]; - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[n].charge += (k? -1:1); - } - num_changes ++; - num_N_minus --; - num_C_plus --; - num_All -= 2; - } - } - } - if ( (!type || 17 == type) && 0 < num_C_plus && 0 < num_C_minus ) { - int m[3], c[3], j[3], k; - for ( i = 0; i < num_atoms; i ++ ) { - if ( 0 == at[i].charge && 3 == nNoMetalNumBonds(at, i) && 3 == nNoMetalBondsValence(at, i) && - 0 == num_of_H( at, i ) && - 0 <= ( j[0] = nNoMetalNeighIndex(at, i) ) && - 0 <= ( j[1] = nNoMetalOtherNeighIndex( at, i, m[0] = at[i].neighbor[j[0]] ) ) && - 0 <= ( j[2] = nNoMetalOtherNeighIndex2( at, i, m[0], m[1] = at[i].neighbor[j[1]] ) ) && - 1 == !(c[0]=at[m[0]].charge) - + !(c[1]=at[m[1]].charge) - + !(c[2]=at[m[2]=at[i].neighbor[j[2]]].charge) && - 0 == c[0] + c[1] + c[2] && - 2 == (3== (c[0]? at[m[0]].chem_bonds_valence+NUMH(at,m[0]):0)) - + (3== (c[1]? at[m[1]].chem_bonds_valence+NUMH(at,m[1]):0)) - + (3== (c[2]? at[m[2]].chem_bonds_valence+NUMH(at,m[2]):0)) && - NULL != memchr( en+ELEM_N_FST, at[i].el_number, ELEM_N_LEN) ) { - /* found non-terminal A(+)-O-B(-) */ - int n_Cp=-1, n_Cm=-1, i_Cp=-1, i_Cm=-1; /* p = positive, m = negatice ion C */ - for ( k = 0; k < 3; k ++ ) { - if ( c[k] ) { - n = m[k]; - if ( -1 == at[n].charge && - NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) { - n_Cm = n; - i_Cm = k; - } else - if ( 1 == at[n].charge && - NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) { - n_Cp = n; - i_Cp = k; - } - } - } - if ( n_Cp < 0 || n_Cm < 0 || - has_other_ion_in_sphere_2(at, n_Cp, n_Cm, en, ne ) || - has_other_ion_in_sphere_2(at, n_Cm, n_Cp, en, ne )) { - continue; - } - /* | | */ - /* C(+)(III)-N-C(-)(III) => C(IV)=N=C(IV) */ - for ( k = 0; k < 2; k ++ ) { - n = k? n_Cp : n_Cm; - i1 = k? j[i_Cp] : j[i_Cm]; - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[n].charge += (k? -1:1); - } - num_changes ++; - num_C_minus --; - num_C_plus --; - num_All -= 2; - } - } - } - if ( (!type || 18 == type) && (0 < num_C_plus && 0 < num_C_minus || 0 < num_C_II) ) { - int m[2], v[2], j[2], k; - for ( i = 0; i < num_atoms; i ++ ) { - if ( 0 == at[i].charge && 2 == nNoMetalNumBonds(at, i) && 3 == nNoMetalBondsValence(at, i) && - 0 == num_of_H( at, i ) && - 0 <= (j[0] = nNoMetalNeighIndex( at, i )) && - 0 <= (j[1] = nNoMetalOtherNeighIndex( at, i, m[0] = at[i].neighbor[j[0]] )) && - 0 == at[m[0]].charge - +at[m[1]=at[i].neighbor[j[1]]].charge && - 6 == (v[0]=at[m[0]].chem_bonds_valence+NUMH(at,m[0])) - +(v[1]=at[m[1]].chem_bonds_valence+NUMH(at,m[1])) && - 2 >= abs(v[0]-v[1]) && - NULL != memchr( en+ELEM_N_FST, at[i].el_number, ELEM_N_LEN) && - NULL != memchr( en+ELEM_C_FST, at[m[0]].el_number, ELEM_C_LEN) && - NULL != memchr( en+ELEM_C_FST, at[m[1]].el_number, ELEM_C_LEN) - ) { - /* n_Cm i n_Cp */ - /* found non-terminal C(-)(III)-N=C(+)(III) or C(IV)=N-C(II): Cm-N-Cp */ - /* convert to C(IV)=N#C(IV) */ - int n_Cp=-1, n_Cm=-1, i_Cp=-1, i_Cm=-1; /* p = positive, m = negatice ion C */ - for ( k = 0; k < 2; k ++ ) { - n = m[k]; - if ( v[k] == 4 || v[k] == 3 && at[i].bond_type[j[k]] == BOND_TYPE_SINGLE ) { - n_Cm = n; - i_Cm = k; - } else - if ( v[k] == 2 || v[k] == 3 && at[i].bond_type[j[k]] == BOND_TYPE_DOUBLE ) { - n_Cp = n; - i_Cp = k; - } - } - if ( n_Cp < 0 || n_Cm < 0 || at[n_Cp].valence+NUMH(at,n_Cp) != 2 ) { - continue; /* guarantees at[n_Cp].valence <= 2 */ - } - if ( v[i_Cp] == 2 || !at[n_Cp].charge ) { - if ( at[n_Cp].valence == 2 ) { - /* neighbor of at[n_Cp] opposite to at[i] */ - k = at[n_Cp].neighbor[at[n_Cp].neighbor[0]==i]; - if ( NULL != memchr( en+ELEM_N_FST, at[k].el_number, ELEM_N_LEN) ) { - continue; - } - } - } else - if ( at[n_Cp].charge ) { - if ( has_other_ion_in_sphere_2(at, n_Cp, n_Cm, en, ne ) || - has_other_ion_in_sphere_2(at, n_Cm, n_Cp, en, ne )) { - continue; - } - } else { - continue; /* unknown case */ - } - /* */ - /* C(-)(III)-N=C(+)(III) => C(IV)=N#C(IV) */ - /* C(IV)=N-C(II) => C(IV)=N#C(IV) */ - if ( at[n_Cp].charge ) { - num_C_minus --; - num_C_plus --; - num_All -= 2; - } else { - num_C_II --; - num_All --; - } - - for ( k = 0; k < 2; k ++ ) { - n = k? n_Cp : n_Cm; - i3 = k? i_Cp : i_Cm; /* added to fix the bug */ - /*i1 = k? j[i_Cp] : j[i_Cm];*/ /* replaced with next line */ - i1 = j[i3]; - if ( v[i3 /*was i1*/] < 4 ) { /* WDI found a bug here: bounds violation */ - int delta = 4 - v[i3 /*was i1*/]; - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; - at[i].bond_type[i1] += delta; - at[n].bond_type[i2] += delta; - at[i].chem_bonds_valence += delta; - at[n].chem_bonds_valence += delta; - at[n].charge = 0; - at[n].radical = 0; - } - } - at[i].charge = 0; - at[i].radical = 0; - num_changes ++; - } - } - } - } - - return num_changes; -} - -/*#if ( DISCONNECT_SALTS == 1 )*/ /* { */ - - - -/*************************************************************************************************/ -int RemoveInpAtBond( inp_ATOM *atom, int iat, int k ) -{ - int i, j, m, m2, k2; - inp_ATOM *at = atom + iat; - inp_ATOM *at2 = NULL; - int val = at->valence - 1; - if ( val >= 0 ) { - int bond = at->bond_type[k]; - if ( bond > BOND_TYPE_TRIPLE ) - bond = BOND_TYPE_SINGLE; /* added 08-06-2003 */ - - /* update CML tetrahedral atom parity. */ - if ( at->p_parity ) { - for( m = 0; m < MAX_NUM_STEREO_ATOM_NEIGH; m ++ ) { - if ( at->p_orig_at_num[m] == at->orig_at_number ) { - at->p_parity = 0; - break; /* only 3 bonds are present; removing one bond removes stereo */ - } - } - if ( at->p_parity /* at->valence == MAX_NUM_STEREO_ATOM_NEIGH*/ ) { - for ( m = 0; m < at->valence; m ++ ) { - if ( atom[(int)at->neighbor[k]].orig_at_number == at->p_orig_at_num[m] ) { - break; - } - } - if ( m < at->valence ) { - at->p_orig_at_num[m] = at->orig_at_number; - } else { - at->p_parity = 0; /* wrong neighbors: at->neighbor[k] is not in the list of a stereo neighbors */ - } - } - } - - /* update CML stereogenic bond parities; at this point no removed explicit H exist yet */ - if ( at->sb_parity[0] ) { - for ( m = 0; m < MAX_NUM_STEREO_BONDS && at->sb_parity[m]; ) { - if ( k == at->sb_ord[m] || k == at->sn_ord[m] && val < 2 && ATOM_PARITY_WELL_DEF(at->sb_parity[m]) ) { - /* !!! FLAW: does take into account removed H !!! */ - /* stereogenic bond is being removed OR */ - /* remove stereogenic bond because its only neighbor is being removed */ - int pnxt_atom, pinxt2cur, pinxt_sb_parity_ord; - int len= get_opposite_sb_atom( atom, iat, at->sb_ord[m], &pnxt_atom, &pinxt2cur, &pinxt_sb_parity_ord ); - if ( len ) { - i = pinxt_sb_parity_ord; - at2 = atom + pnxt_atom; - k2 = pinxt2cur; - } else { - i = MAX_NUM_STEREO_BONDS; - } - /* - at2 = atom + at->neighbor[ (int)at->sb_ord[m] ]; - for ( i = 0; i < MAX_NUM_STEREO_BONDS && at2->sb_parity[i]; i ++ ) { - if ( iat == at2->neighbor[ (int)at2->sb_ord[i] ] ) - break; - } - */ - if ( i < MAX_NUM_STEREO_BONDS && at2->sb_parity[i] ) { - m2 = i; - /* remove bond parity from at */ - if ( m < MAX_NUM_STEREO_BONDS-1 ) { - memmove( at->sb_parity+m, at->sb_parity+m+1, (MAX_NUM_STEREO_BONDS-1 - m) * sizeof(at->sb_parity[0])); - memmove( at->sb_ord+m, at->sb_ord+m+1, (MAX_NUM_STEREO_BONDS-1 - m) * sizeof(at->sb_ord[0])); - memmove( at->sn_ord+m, at->sn_ord+m+1, (MAX_NUM_STEREO_BONDS-1 - m) * sizeof(at->sn_ord[0])); - memmove( at->sn_orig_at_num+m, at->sn_orig_at_num+m+1, (MAX_NUM_STEREO_BONDS-1 - m) * sizeof(at->sn_orig_at_num[0])); - } - at->sb_parity[MAX_NUM_STEREO_BONDS-1] = 0; - at->sb_ord[MAX_NUM_STEREO_BONDS-1] = 0; - at->sn_ord[MAX_NUM_STEREO_BONDS-1] = 0; - at->sn_orig_at_num[MAX_NUM_STEREO_BONDS-1] = 0; - /* remove bond parity from at2 */ - if ( m2 < MAX_NUM_STEREO_BONDS-1 ) { - memmove( at2->sb_parity+m2, at2->sb_parity+m2+1, (MAX_NUM_STEREO_BONDS-1 - m2) * sizeof(at2->sb_parity[0])); - memmove( at2->sb_ord+m2, at2->sb_ord+m2+1, (MAX_NUM_STEREO_BONDS-1 - m2) * sizeof(at2->sb_ord[0])); - memmove( at2->sn_ord+m2, at2->sn_ord+m2+1, (MAX_NUM_STEREO_BONDS-1 - m2) * sizeof(at2->sn_ord[0])); - memmove( at2->sn_orig_at_num+m2, at2->sn_orig_at_num+m2+1, (MAX_NUM_STEREO_BONDS-1 - m2) * sizeof(at2->sn_orig_at_num[0])); - } - at2->sb_parity[MAX_NUM_STEREO_BONDS-1] = 0; - at2->sb_ord[MAX_NUM_STEREO_BONDS-1] = 0; - at2->sn_ord[MAX_NUM_STEREO_BONDS-1] = 0; - at2->sn_orig_at_num[MAX_NUM_STEREO_BONDS-1] = 0; - /* do not increment m here because the array elements have been shifted */ - } else { - m ++; /* program error: inconsistent stereobond parity */ - } - } else - if ( k == at->sn_ord[m] ) { - /* stereogenic bond neighbor is being removed; another neighbor remains */ - /* !!! FLAW: does take into account removed H !!! */ - for ( j = 0, i = -1; j < at->valence; j ++ ) { - if ( j != k && j != at->sb_ord[m] ) { - i = j; - break; - } - } - /* i is the position of the neighbor that will become a new neighbor */ - /*************************************************************************** - * at->sb_parity[m] is the direction (EVEN=clockwise, ODD=counterclockwise) - * from stereobond to the neighbor. If the neighbor is removed then - * the parity should invert, otherwise it should be unchanged. - ***************************************************************************/ - if ( i < 0 ) { - /* no alternative neighbor is available */ - if ( ATOM_PARITY_WELL_DEF(at->sb_parity[m] ) ) { - /* parity cannot be not well-defined anymore */ - int pnxt_atom, pinxt2cur, pinxt_sb_parity_ord; - int len= get_opposite_sb_atom( atom, iat, at->sb_ord[m], &pnxt_atom, &pinxt2cur, &pinxt_sb_parity_ord ); - if ( len > 0 ) { - atom[pnxt_atom].sb_parity[pinxt_sb_parity_ord] = at->sb_parity[m] = AB_PARITY_UNDF; - } -#ifdef _DEBUG - else { - int stop = 1; /* sb parities error */ - } -#endif - } - at->sn_ord[m] = -99; /* sb neighbor has been disconnected */ - at->sb_ord[m] -= (at->sb_ord[m] > k); /* same as above */ - at->sn_orig_at_num[m] = 0; - } else - if ( i < at->valence ) { - /* choose another stereogenic bond neighbor, its ord. number is i before bond removal */ - if ( ATOM_PARITY_WELL_DEF(at->sb_parity[m]) ) { - /* ALL WRONG: 'move' previous stereo bond neighbor to the last position (pos. 2 out of 0,1,2) */ - /* the parity of the transpositions is (2 - at->sn_ord[m])%2 = at->sn_ord[m] % 2 */ - /* and replace the neighbor with another; the contribution to the parity is 1 */ - - /*at->sb_parity[m] = 2 - ( at->sb_parity[m] + at->sn_ord[m] + 1 ) % 2;*/ - - /*at->sb_parity[m] = 2 - ( at->sb_parity[m] + k + i + - (i > k) + (i > at->sb_ord[m]) ) % 2;*/ - /*=== parity should be INVERTED ===*/ - at->sb_parity[m] = 3 - at->sb_parity[m]; - } - at->sn_ord[m] = i - (i > k); /* ord. number shifted because preceding bond is removed */ - at->sb_ord[m] -= (at->sb_ord[m] > k); /* same as above */ - at->sn_orig_at_num[m] = atom[(int)at->neighbor[i]].orig_at_number; - /*at->sb_parity[m] = 2 - ( at->sb_parity[m] + 1 ) % 2;*/ - } else { - at->sb_parity[m] = 0; /* program error: inconsistent stereobond parity */ - } - m ++; - } else { - /* removing another neighbor, k: first move it to the last position (pos. 2 out of 0,1,2) */ - if ( k < 2 && ATOM_PARITY_WELL_DEF(at->sb_parity[m]) ) { - /*at->sb_parity[m] = 2 - ( at->sb_parity[m] + k ) % 2;*/ - /*at->sb_parity[m] = 2 - ( at->sb_parity[m] + (at->sn_ord[m] > k) + (at->sb_ord[m] > k) ) % 2;*/ - ;/*==== Parity should remain UNCHANGED ===*/ - } - if ( at->sb_ord[m] > k ) { - at->sb_ord[m] --; - } - if ( at->sn_ord[m] > k ) { - at->sn_ord[m] --; - } - m ++; - } - } - } - - if ( k < val ) { - memmove( at->neighbor+k, at->neighbor+k+1, sizeof(at->neighbor[0])*(val-k) ); - memmove( at->bond_stereo+k, at->bond_stereo+k+1, sizeof(at->bond_stereo[0])*(val-k) ); - memmove( at->bond_type+k, at->bond_type+k+1, sizeof(at->bond_type[0])*(val-k) ); - } - at->neighbor[val] = 0; - at->bond_stereo[val] = 0; - at->bond_type[val] = 0; - at->valence = val; - at->chem_bonds_valence -= bond; - return 1; - } - return 0; -} - - - -/*************************************************************************************************/ -int DisconnectInpAtBond( inp_ATOM *at, AT_NUMB *nOldCompNumber, int iat, int neigh_ord ) -{ - int neigh, i, ret = 0; - int component; - neigh = at[iat].neighbor[neigh_ord]; - for ( i = 0; i < at[neigh].valence; i ++ ) { - if ( iat == (int)at[neigh].neighbor[i] ) - break; - } - if ( i < at[neigh].valence ) { - ret += RemoveInpAtBond( at, iat, neigh_ord ); - ret += RemoveInpAtBond( at, neigh, i ); - if ( nOldCompNumber && ret ) { - if ( component = at[iat].component ) { - nOldCompNumber[component-1] = 0; - } - if ( component = at[neigh].component ) { - nOldCompNumber[component-1] = 0; - } - } - } - return (ret == 2); -} - - - -/*************************************************************************************************/ -int bIsAmmoniumSalt( inp_ATOM *at, int i, int *piO, int *pk, S_CHAR *num_explicit_H ) -{ - /* NH4(+charge)-O(-charge)-C -> NH3 + HO-C; any charge including 0, any C except charged or radical */ - /* F, Cl, Br, I */ - static U_CHAR el_number_C=0, el_number_O=0, el_number_H=0, el_number_N=0; - static U_CHAR el_number_F=0, el_number_Cl=0, el_number_Br=0, el_number_I=0; - int num_H, num_non_iso_H, num_impl_iso_H, bDisconnect = 1; - int j, val, neigh, iO=-1, iC, k=-1; - if ( 0 == el_number_C ) { - /* one time initialization */ - el_number_C = get_periodic_table_number( "C" ); - el_number_O = get_periodic_table_number( "O" ); - el_number_H = get_periodic_table_number( "H" ); - el_number_N = get_periodic_table_number( "N" ); - el_number_F = get_periodic_table_number( "F" ); - el_number_Cl= get_periodic_table_number( "Cl" ); - el_number_Br= get_periodic_table_number( "Br" ); - el_number_I = get_periodic_table_number( "I" ); - } - if ( at[i].el_number != el_number_N ) - return 0; - - /* check for NH4-O-C... -> NH3 + HO-C... */ - val = at[i].valence; - num_impl_iso_H = NUM_ISO_H(at,i); - num_non_iso_H = at[i].num_H; - num_H = num_non_iso_H + num_impl_iso_H; - if ( val + num_H == 5 ) { - int num_O = 0; - memset( num_explicit_H, 0, (NUM_H_ISOTOPES+1)*sizeof(num_explicit_H[0]) ); - for ( j = 0; j < val; j ++ ) { /* looking for O: H4N-O-C... */ - neigh = at[i].neighbor[j]; - if ( at[neigh].num_H || - at[neigh].charge && (at[neigh].el_number != el_number_O || at[neigh].charge + at[i].charge) || - at[neigh].radical && at[neigh].radical != RADICAL_SINGLET ) { - bDisconnect = 0; - break; /* reject */ - } - if ( at[neigh].el_number == el_number_H && at[neigh].valence == 1 && - !at[neigh].charge && !at[neigh].radical ) { - num_H ++; /* at this point at[].num_H does not include explicit H count */ - num_non_iso_H += (0==at[neigh].iso_atw_diff); - num_explicit_H[at[neigh].iso_atw_diff] ++; /* explicit H on N */ - } else - if ( at[neigh].el_number == el_number_O && at[neigh].valence == 2 && !num_O ) { - num_O ++; /* found O: N-O- */ - iO = neigh; - k = j; - iC = at[iO].neighbor[at[iO].neighbor[0] == i]; - if ( at[iC].el_number != el_number_C || /* - at[iC].num_H || - at[iC].chem_bonds_valence != 4 || */ - at[iC].charge || - at[iC].radical && at[iC].radical != RADICAL_SINGLET /*|| - at[iC].valence == at[iC].chem_bonds_valence*/ ) { - bDisconnect = 0; - break; /* reject */ - } - } else - if ( (at[neigh].el_number == el_number_F || - at[neigh].el_number == el_number_Cl || - at[neigh].el_number == el_number_Br || - at[neigh].el_number == el_number_I ) && - at[neigh].valence == 1 && at[neigh].chem_bonds_valence == 1 && - !at[neigh].charge && !NUMH(at,neigh) && !num_O ) { - num_O ++; /* found O: N-O- */ - iO = neigh; - k = j; - iC = -1; - } else { - bDisconnect = 0; - break; /* reject */ - } - } - if ( bDisconnect && (num_O != 1 || num_H != 4) ) { - bDisconnect = 0; /* reject */ - } - } else { - bDisconnect = 0; - } - if ( bDisconnect ) { - *piO = iO; - *pk = k; - } - return bDisconnect; -} - - - -/*************************************************************************************************/ -int DisconnectAmmoniumSalt ( inp_ATOM *at, int iN, int iO, int k, S_CHAR *num_explicit_H ) -{ - /* disconnect NH4-O from O */ - /* Note: iO = at[iN].neighbor[k], at[iN] is N, at[iO].neighbor[0] is either N=at[iN] or C=at[iC] */ - int nMove_H_iso_diff = -1; /* do not move explicit H */ - int j, neigh, iso_diff, neigh_pos; - static U_CHAR el_number_H = 0; - int val = at[iN].valence; - - if ( !el_number_H ) { - el_number_H = get_periodic_table_number( "H" ); - } - if ( at[iN].charge && !(at[iN].charge + at[iO].charge) ) { - at[iN].charge = at[iO].charge = 0; /* remove charges */ - } - neigh_pos = (at[iO].valence == 2)? (at[iO].neighbor[1] == iN) : 0; /* position of at[iN] in the neigh list of iO */ - /* disconnect bond O-N */ - RemoveInpAtBond( at, iO, neigh_pos ); - RemoveInpAtBond( at, iN, k ); - val --; - - /* move 1 H from NH4 to O- or Cl */ - - /* find non-isotopic or the lightest isotopic H to move from N to O */ - for ( iso_diff = 0; iso_diff <= NUM_H_ISOTOPES; iso_diff ++ ) { - if ( !iso_diff ) { - /* find non-isotopic H */ - if ( at[iN].num_H ) { - at[iN].num_H --; /* move non-isotopic implicit H */ - at[iO].num_H ++; - break; - } else - if ( num_explicit_H[0] ) { - nMove_H_iso_diff = 0; /* flag: move explicit non-isotopic H */ - break; - } - } else { - /* find isotopic H */ - if ( at[iN].num_iso_H[iso_diff] ) { - at[iN].num_iso_H[iso_diff] --; /* move implicit isotopic H, atw = 1 */ - at[iO].num_iso_H[iso_diff] ++; - break; - } else - if ( num_explicit_H[iso_diff] ) { - nMove_H_iso_diff = iso_diff; /* flag: move explicit isotopic H, atw = 1 */ - break; - } - } - } - if ( nMove_H_iso_diff >= 0 ) { - /* move explicit H, it is isotopic if nMove_H_iso_diff > 0 */ - double dist2_H_O, min_dist2_H_O = -1.0; - int jH = -1, iH = -1; - for ( j = 0; j < val; j ++ ) { /* looking H in N-H such that H-O is shortest */ - neigh = at[iN].neighbor[j]; - if ( at[neigh].el_number == el_number_H && - at[neigh].iso_atw_diff == nMove_H_iso_diff ) { - dist2_H_O = (at[neigh].x - at[iO].x) * (at[neigh].x - at[iO].x) + - (at[neigh].y - at[iO].y) * (at[neigh].y - at[iO].y) + - (at[neigh].z - at[iO].z) * (at[neigh].z - at[iO].z); - if ( min_dist2_H_O < 0.0 || min_dist2_H_O > dist2_H_O ) { - min_dist2_H_O = dist2_H_O; - iH = neigh; - jH = j; - } - } - } - /* reconnect; bonds do not need changes except stereo */ - neigh_pos = at[iO].valence; - at[iO].neighbor[neigh_pos] = iH; - at[iO].bond_stereo[neigh_pos] = 0; - at[iO].bond_type[neigh_pos] = at[iH].bond_type[0]; - at[iO].chem_bonds_valence += at[iH].bond_type[0]; - at[iO].valence ++; - at[iH].neighbor[0] = iO; - at[iH].bond_stereo[0] = 0; - /* disconnect H from N */ - RemoveInpAtBond( at, iN, jH ); - val --; - if ( k > jH ) { - k --; - } - } - return 1; -} - - - -/*************************************************************************************************/ -int bIsMetalSalt( inp_ATOM *at, int i ) -{ - int type, val, k, iO, iC, j, neigh; - int bDisconnect = 1; - static U_CHAR el_number_C=0, el_number_O=0, el_number_H=0; - static U_CHAR el_number_F=0, el_number_Cl=0, el_number_Br=0, el_number_I=0; - if ( 0 == el_number_C ) { - /* one time initialization */ - el_number_C = get_periodic_table_number( "C" ); - el_number_O = get_periodic_table_number( "O" ); - el_number_H = get_periodic_table_number( "H" ); - el_number_F = get_periodic_table_number( "F" ); - el_number_Cl= get_periodic_table_number( "Cl" ); - el_number_Br= get_periodic_table_number( "Br" ); - el_number_I = get_periodic_table_number( "I" ); - } - /* check for a metal atom: - metal atom should be connected and be a metal */ - if ( !(val = at[i].valence) || - !(type = get_el_type( at[i].el_number )) || - !(type & IS_METAL) ) { - bDisconnect = 0; /* reject */ - } else - /* metal atom should not have adjacent H or multiple bonds or radical */ - if ( at[i].num_H ) { - bDisconnect = 0; /* reject */ - } else - /* check valence */ - if ( at[i].charge == 0 && - ( (type & 1) && val == get_el_valence( at[i].el_number, 0, 0 ) || - (type & 2) && val == get_el_valence( at[i].el_number, 0, 1 ) ) || - at[i].charge > 0 && - (type & 1) && val == get_el_valence( at[i].el_number, at[i].charge, 0 ) ) { - ; /* accept */ - } else { - bDisconnect = 0; /* reject */ - } - if ( bDisconnect ) { - /************************************************************************* - * | * - * check M neighbors. Disconnect if all neighbors are M-O-C# or M-O-C= * - * | * - *************************************************************************/ - for ( k = 0; k < at[i].valence; k ++ ) { - iO = at[i].neighbor[k]; - /* halogenide 2004-07-08 */ - if ( (at[iO].el_number == el_number_F || - at[iO].el_number == el_number_Cl || - at[iO].el_number == el_number_Br || - at[iO].el_number == el_number_I ) && - at[iO].valence == 1 && at[iO].chem_bonds_valence == 1 && - !at[iO].charge && !(at[iO].radical && at[iO].radical != RADICAL_SINGLET) && !NUMH(at,iO) ) { - ; /* found */ - } else { - /* -O-C= */ - if ( at[iO].el_number != el_number_O || - NUMH(at, iO) || - at[iO].valence != 2 || - at[iO].charge || - at[iO].radical && at[iO].radical != RADICAL_SINGLET || - at[iO].valence != at[iO].chem_bonds_valence ) { - bDisconnect = 0; /* reject */ - break; - } - iC = at[iO].neighbor[at[iO].neighbor[0] == i]; - if ( at[iC].el_number != el_number_C || - at[iC].num_H || - at[iC].chem_bonds_valence != 4 || - at[iC].charge || - at[iC].radical && at[iC].radical != RADICAL_SINGLET || - at[iC].valence == at[iC].chem_bonds_valence ) { - bDisconnect = 0; /* reject */ - break; - } - for ( j = 0; j < at[iC].valence; j ++ ) { - neigh = at[iC].neighbor[j]; - if ( at[neigh].el_number == el_number_H ) { - break; - } - } - if ( j != at[iC].valence ) { - bDisconnect = 0; /* reject */ - break; - } - } - } - } - return bDisconnect; -} - - - -/*************************************************************************************************/ -int DisconnectMetalSalt( inp_ATOM *at, int i ) -{ - int k, iO; - /* disconnect metal atom or ion at[i] */ - for ( k = 0; k < at[i].valence; k ++ ) { - iO = at[i].neighbor[k]; - if ( at[iO].valence == 2 ) { - if ( at[iO].neighbor[0] == i ) { /* assuming atom O always has 2 bonds */ - /* copy the remaining neighbor to the 0 position */ - at[iO].neighbor[0] = at[iO].neighbor[1]; - at[iO].bond_stereo[0] = at[iO].bond_stereo[1]; - at[iO].bond_type[0] = at[iO].bond_type[1]; - } - /* clear neighbor at position 1 */ - at[iO].neighbor[1] = 0; - at[iO].bond_stereo[1] = 0; - at[iO].bond_type[1] = 0; - } else { - /* clear neighbor at position 1 */ - at[iO].neighbor[0] = 0; - at[iO].bond_stereo[0] = 0; - at[iO].bond_type[0] = 0; - } - /* make O negatively charged */ - at[iO].charge = -1; - /* reduce O valence to account for the removed single bond */ - at[iO].valence --; - at[iO].chem_bonds_valence --; - - /* clear metal neighbor (O) */ - at[i].neighbor[k] = 0; - at[i].bond_stereo[k] = 0; - at[i].bond_type[k] = 0; - /* add a positive charge to the metal */ - at[i].charge ++; - } - /* set metal valence to zero because it has been disconnected */ - at[i].valence = 0; - at[i].chem_bonds_valence = 0; - return k; -} - - - -/*************************************************************************************************/ -int DisconnectSalts( ORIG_ATOM_DATA *orig_inp_data, int bDisconnect ) -{ - int i, k, iO, num_changes, val; - S_CHAR num_explicit_H[NUM_H_ISOTOPES+1]; - inp_ATOM *at = orig_inp_data->at; - int num_at = orig_inp_data->num_inp_atoms; - - /* check each atom */ - for ( i = 0, num_changes = 0; i < num_at; i ++ ) { - - if ( !(val = at[i].valence) || /* disconnected atom */ - val != at[i].chem_bonds_valence || /* a bond has higher multiplicity than 1 */ - at[i].radical && at[i].radical != RADICAL_SINGLET /* radical */ ) { - continue; /* reject */ - } - if ( bIsAmmoniumSalt( at, i, &iO, &k, num_explicit_H ) ) { - if ( bDisconnect ) { - DisconnectAmmoniumSalt ( at, i, iO, k, num_explicit_H ); - orig_inp_data->num_inp_bonds --; - } - /* count disconnected atoms */ - num_changes ++; - } else - if ( bIsMetalSalt( at, i ) ) { - if ( bDisconnect ) { - k = DisconnectMetalSalt( at, i ); - orig_inp_data->num_inp_bonds -= k; - } - num_changes ++; - } - } - return num_changes; -} - - - -/*****************************************************************************/ -/* Important: Salt disconnection is independent from coord. disconnection: */ -/* because different atoms are disconnected. */ -/* However, sal disconnection may need to be rerun after metal disconnection */ -/* because metal disconnection may make certain atoms be eligible for salt */ -/* disconnection */ -/*****************************************************************************/ -int bIsMetalToDisconnect(inp_ATOM *at, int i, int bCheckMetalValence) -{ - int type, at_valence, num_H; -/* - if ( !at[i].valence ) -*/ - if ( !(type = get_el_type( at[i].el_number )) || - !(type & IS_METAL ) ) { - return 0; - } - num_H = NUMH(at,i); - at_valence = num_H + at[i].chem_bonds_valence; - if ( !at_valence ) { - return 0; /* nothing to disconnect */ - } - if ( bCheckMetalValence ) { - if ( abs(at[i].charge) > 1 ) { - return 1; /* multiple charges */ - } - for ( i = 0; i < 2 && (i & type); i ++ ) { - if ( at_valence == get_el_valence( at[i].el_number, at[i].charge, i ) ) { - return 2; /* atom has normal valence */ - } - } - } - return 1; - -} - - - -/*****************************************************************************/ -int bMayDisconnectMetals( ORIG_ATOM_DATA *orig_inp_data, int bCheckMetalValence, INCHI_MODE *bTautFlagsDone ) -{ - int i, j, k, iO, num_changes, val, bRadOrMultBonds, num_impl_H = 0; - S_CHAR num_explicit_H[NUM_H_ISOTOPES+1]; - inp_ATOM *at = orig_inp_data->at; - int num_at = orig_inp_data->num_inp_atoms; - int *nNumImplH = &orig_inp_data->bDisconnectCoord; - /* check each atom */ - for ( i = 0, num_changes = 0; i < num_at; i ++ ) { - - if ( !(val = at[i].valence) && !NUMH(at,i) ) { - continue; /* disconnected atom */ - } - bRadOrMultBonds = (val == 0) || - (val != at[i].chem_bonds_valence) || /* a bond has higher multiplicity than 1 */ - (at[i].radical && at[i].radical != RADICAL_SINGLET); /* radical */ - - if ( !bRadOrMultBonds && bIsAmmoniumSalt( at, i, &iO, &k, num_explicit_H ) ) { - ; - } else - if ( !bRadOrMultBonds && bIsMetalSalt( at, i ) ) { - ; - } else - if ( 1 == (j = bIsMetalToDisconnect(at, i, bCheckMetalValence)) ) { - num_impl_H += NUMH(at,i); - num_changes ++; - } else - if ( 2 == j && bTautFlagsDone ) { - *bTautFlagsDone |= TG_FLAG_CHECK_VALENCE_COORD_DONE; - } - } - if ( nNumImplH ) - *nNumImplH = num_changes? num_impl_H+1 : 0; - return num_changes; -} - - - -/*****************************************************************************/ -#if ( bRELEASE_VERSION == 0 && (EXTR_HAS_METAL_ATOM & (EXTR_MASK | EXTR_FLAG) ) ) -int bHasMetalAtom( ORIG_ATOM_DATA *orig_inp_data ) -{ - int i; - inp_ATOM *at; - if ( orig_inp_data && (at = orig_inp_data->at) ) { - int num_at = orig_inp_data->num_inp_atoms; - /* check each atom */ - for ( i = 0; i < num_at; i ++ ) { - if ( IS_METAL & get_el_type( at[i].el_number ) ) { - return 1; - } - } - } - return 0; -} -#endif -/***************************************************************************** -{ "F", 19, 19, 18.998403220, 0 , 0, {{0,}, {0,}, {1,}, {2,}, {3,5}, },}, -{ "Cl", 35, 35, 34.968852730, 0 , 0, {{0,}, {0,}, {1,3,5,7}, {2,4,6}, {3,5,}, },}, -{ "Br", 80, 79, 78.918336100, 0 , 0, {{0,}, {0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, },}, -{ "I", 127, 127, 126.904500000, 0 , 0, {{0,}, {0,}, {1,3,5,7,}, {2,4,6}, {3,5,}, },}, -{ "At", 210, 210, 209.987100000, 0 , 0, {{0,}, {0,}, {1,3,5,7,}, {2,4,6}, {3,5,}, },}, -{ "N", 14, 14, 14.003074000, 0 , 0, {{1,}, {2,}, {3,5}, {4,}, {3,}, },}, -{ "P", 31, 31, 30.973762000, 0 , 0, {{1,3,5,7,}, {2,4,6,}, {3,5,}, {4,}, {3,}, },}, -{ "As", 75, 75, 74.921594200, 0 , 0, {{0,}, {2,4,6,}, {3,5,}, {4,}, {3,}, },}, -{ "Sb", 122, 121, 120.903800000, 0 , 0, {{1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,}, {3,}, },}, -{ "O", 16, 16, 15.994914630, 0 , 0, {{0,}, {1,}, {2,}, {3,5,}, {4,}, },}, -{ "S", 32, 32, 31.972070700, 0 , 0, {{0,}, {1,3,5,7,}, {2,4,6}, {3,5,}, {4,}, },}, -{ "Se", 79, 80, 79.916519600, 0 , 0, {{0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, {4,}, },}, -{ "Te", 128, 130, 129.906200000, 0 , 0, {{0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,}, },}, -{ "Po", 209, 209, 208.982400000, 0 , 0, {{0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,}, },}, -{ "B", 11, 11, 11.009300000, 0 , 0, {{3,}, {4,}, {3,}, {2,}, {1,}, },}, -*****************************************************************************/ - - - -int DisconnectMetals( ORIG_ATOM_DATA *orig_inp_data, int bCheckMetalValence, INCHI_MODE *bTautFlagsDone ) - /*inp_ATOM *atom, int num_atoms, int nNumExplH, int *new_num_atoms */ -{ - int i, j, k, n, iO, num_changes, val, bRadOrMultBonds; - int num_impl_H, num_at, err, num_disconnected; - S_CHAR num_explicit_H[NUM_H_ISOTOPES+1]; - static char elnumber_Heteroat[16] = {'\0', }; - static int num_halogens; - inp_ATOM *at = NULL; - S_CHAR *bMetal = NULL; - inp_ATOM *atom = orig_inp_data->at; - int num_atoms = orig_inp_data->num_inp_atoms; - int nNumExplH = (orig_inp_data->bDisconnectCoord > 0)? orig_inp_data->bDisconnectCoord - 1 : 0; - AT_NUMB *nOldCompNumber = orig_inp_data->nOldCompNumber; - - err = 0; - num_impl_H = 0; - num_at = num_atoms; - num_disconnected = 0; - if ( !(at = (inp_ATOM *)inchi_calloc( num_at + nNumExplH, sizeof(at[0] ) )) || - !(bMetal = ( S_CHAR *)inchi_calloc( num_at + nNumExplH, sizeof(bMetal[0]) )) ) { - err = 1; - goto exit_function; - } - if (!elnumber_Heteroat[0] ) { - i = 0; - /* halogens */ - elnumber_Heteroat[i++] = (char)get_periodic_table_number( "F" ); /* 0 */ - elnumber_Heteroat[i++] = (char)get_periodic_table_number( "Cl" ); - elnumber_Heteroat[i++] = (char)get_periodic_table_number( "Br" ); - elnumber_Heteroat[i++] = (char)get_periodic_table_number( "I" ); - elnumber_Heteroat[i++] = (char)get_periodic_table_number( "At" ); /* 4 */ - num_halogens = i; - /* other non-metal */ - elnumber_Heteroat[i++] = (char)get_periodic_table_number( "N" ); - elnumber_Heteroat[i++] = (char)get_periodic_table_number( "P" ); - elnumber_Heteroat[i++] = (char)get_periodic_table_number( "As" ); - /*elnumber_Heteroat[i++] = get_periodic_table_number( "Sb" );*/ /* metal 10-28-2003 */ - elnumber_Heteroat[i++] = (char)get_periodic_table_number( "O" ); - elnumber_Heteroat[i++] = (char)get_periodic_table_number( "S" ); - elnumber_Heteroat[i++] = (char)get_periodic_table_number( "Se" ); - elnumber_Heteroat[i++] = (char)get_periodic_table_number( "Te" ); - /*elnumber_Heteroat[i++] = get_periodic_table_number( "Po" );*/ /* metal 10-28-2003 */ - elnumber_Heteroat[i++] = (char)get_periodic_table_number( "B" ); - elnumber_Heteroat[i++] = 0; - } - - memcpy( at, atom, num_atoms * sizeof(at[0]) ); - - /* check each atom, mark metals */ - for ( i = 0, k = 0, num_changes = 0; i < num_atoms; i ++ ) { - - if ( !(val = at[i].valence) && !NUMH(at,i) ) { - continue; /* disconnected atom */ - } - bRadOrMultBonds = (val == 0) || - (val != at[i].chem_bonds_valence) || /* a bond has higher multiplicity than 1 */ - (at[i].radical && at[i].radical != RADICAL_SINGLET); /* radical */ - - if ( !bRadOrMultBonds && bIsAmmoniumSalt( at, i, &iO, &k, num_explicit_H ) ) { - ; - } else - if ( !bRadOrMultBonds && bIsMetalSalt( at, i ) ) { - ; - } else - if ( 1 == (j = bIsMetalToDisconnect(at, i, bCheckMetalValence)) ) { - num_impl_H += (k = NUMH(at,i)); - bMetal[i] = 1+k; - num_changes ++; - } else - if ( 2 == j && bTautFlagsDone ) { - *bTautFlagsDone |= TG_FLAG_CHECK_VALENCE_COORD_DONE; - } - } - if ( num_impl_H != nNumExplH ) { - err = 2; - goto exit_function; - } - - - /* replace implicit H atoms with explicit H atoms */ - for ( i = 0; i < num_atoms && 0 < num_impl_H; i ++ ) { - if ( bMetal[i] <= 1 ) { - continue; - } - for ( k = 0; k < NUM_H_ISOTOPES+1; k ++ ) { - n = k? at[i].num_iso_H[k-1] : at[i].num_H; - for ( j = 0; j < n; j ++ ) { - if ( num_at >= num_atoms + nNumExplH ) { - err = 3; - goto exit_function; - } - at[num_at].elname[0] = 'H'; - at[num_at].el_number = get_periodic_table_number(at[num_at].elname); - at[num_at].iso_atw_diff = k; - at[num_at].component = at[i].component; - move_explicit_Hcation(at, num_at+1, i, num_at, 1); - at[num_at].orig_at_number = num_at+1; - num_at ++; - num_impl_H --; - bMetal[i] --; - if ( k ) { - at[i].num_iso_H[k-1] --; - } else { - at[i].num_H --; - } - } - } - if ( bMetal[i] != 1 ) { - err = 4; - goto exit_function; - } - } - if ( num_at != num_atoms + nNumExplH ) { - err = 5; - goto exit_function; - } - - /* disconnect metal - ligand bonds */ - for ( i = 0; i < num_atoms; i ++ ) { - if ( !bMetal[i] ) { - continue; - } - /* disconnect metal atom M - - Note: Defect in case of bridging ligands: - - M M M M M M(+) - \ / will be transformed to , not to - N(+) N(+) N(-) - / \ / \ / \ - R R R R R R - - Non-bridging are OK: - - M R M(+) R - \ / / - N(+) ---> N - / \ / \ - R R R R - - */ - for ( j = at[i].valence-1; 0 <= j; j -- ) { - if ( j < at[i].valence && !bMetal[ (int)at[i].neighbor[j] ] ) { - /* do not break metal-metal bond here */ - num_disconnected += DisconnectOneLigand( at, nOldCompNumber, bMetal, elnumber_Heteroat, - num_halogens, num_atoms, i, j, bTautFlagsDone ); - } - } - } - /* disconnect metal-metal bonds */ - for ( i = 0; i < num_atoms; i ++ ) { - if ( !bMetal[i] ) { - continue; - } - for ( j = at[i].valence-1; 0 <= j; j -- ) { - if ( j < at[i].valence && bMetal[ (int)at[i].neighbor[j] ] ) { - /* break metal-metal bond here */ - num_disconnected += DisconnectOneLigand( at, nOldCompNumber, bMetal, elnumber_Heteroat, - num_halogens, num_atoms, i, j, bTautFlagsDone ); - } - } - } - - -exit_function: - if ( !num_disconnected ) { - err = 6; - } - if ( at && err ) { - inchi_free( at ); - at = NULL; - } - if ( atom && at ) { /* changed if ( at ) to if ( atom && at ) 2004-04-03 */ - inchi_free( atom ); - atom = NULL; - } - if ( bMetal ) - inchi_free( bMetal ); - - if ( at ) { - orig_inp_data->at = at; - orig_inp_data->num_inp_atoms = num_at; - } - return err? -err : num_disconnected; -} - - - -/*****************************************************************************/ -int DisconnectOneLigand( inp_ATOM *at, AT_NUMB *nOldCompNumber, S_CHAR *bMetal, char *elnumber_Heteroat, - int num_halogens, int num_atoms, int iMetal, int jLigand, INCHI_MODE *bTautFlagsDone ) -{ - int i, j, iLigand, neigh, val; - int metal_neigh_ord[MAXVAL], num_neigh_arom_bonds[MAXVAL]; - int num_metal_neigh, num_disconnections; - int num_del_arom_bonds, num_tot_arom_bonds, new_charge; - char *p; - - iLigand = at[iMetal].neighbor[jLigand]; - num_metal_neigh = 0; - num_disconnections = 0; - num_del_arom_bonds = num_tot_arom_bonds = 0; - - /* find bonds to disconnect */ - for ( i = 0; i < at[iLigand].valence; i ++ ) { - num_neigh_arom_bonds[i] = 0; - neigh = (int)at[iLigand].neighbor[i]; - if ( neigh < num_atoms && bMetal[ neigh ] ) { - metal_neigh_ord[ num_metal_neigh ++ ] = i; - if ( at[iLigand].bond_type[i] > BOND_TYPE_TRIPLE ) { - /* aromatic bond */ - for ( j = 0; j < at[neigh].valence; j ++ ) { - num_neigh_arom_bonds[i] += ( at[neigh].bond_type[j] > BOND_TYPE_TRIPLE ); - } - num_del_arom_bonds ++; - } - } - num_tot_arom_bonds += (at[iLigand].bond_type[i] > BOND_TYPE_TRIPLE); - } - /* Disconnect */ - if ( num_del_arom_bonds ) { - /* fix chem_valence of the ligand and its neighbors in case of disconnecting arom. bonds */ - /* because in this case special care should be taken of updating at[].chem_bonds_valence */ - for ( i = 0; i < num_metal_neigh; i ++ ) { - j = metal_neigh_ord[i]; - if ( num_neigh_arom_bonds[j] ) { - neigh = at[iLigand].neighbor[j]; - at[neigh].chem_bonds_valence -= num_neigh_arom_bonds[j]/2 - (num_neigh_arom_bonds[j]-1)/2; - } - } - at[iLigand].chem_bonds_valence -= num_tot_arom_bonds/2 - (num_tot_arom_bonds-num_del_arom_bonds)/2; - } - /* disconnect in reverse order, otherwise the metal_neigh_ord[i] - becomes invalid after the first disconnection - */ - for ( i = num_metal_neigh-1; 0 <= i; i -- ) { - num_disconnections += DisconnectInpAtBond( at, nOldCompNumber, iLigand, metal_neigh_ord[i] ); - } - - /* attempt to change ligand charge to make its valence 'natural' */ - i = num_tot_arom_bonds - num_del_arom_bonds; - if ( i && i != 2 && i != 3 || - at[iLigand].radical && at[iLigand].radical != RADICAL_SINGLET || - !(p = strchr( elnumber_Heteroat, at[iLigand].el_number ) ) ) { - goto exit_function; /* non-standard atom */ - } - val = at[iLigand].chem_bonds_valence + NUMH(at, iLigand); - new_charge = MAX_ATOMS; /* impossible value */ - if ( !val ) { - if ( p - elnumber_Heteroat < num_halogens ) { - new_charge = -1; - } - } else { - for ( i = -1; i <= 1; i ++ ) { - if ( val == get_el_valence( at[iLigand].el_number, i, 0 ) ) { - new_charge = i; /* found charge that fits chem. valence */ - break; - } - } - } - if ( new_charge != MAX_ATOMS ) { - if ( (new_charge != at[iLigand].charge || - (at[iLigand].radical && at[iLigand].radical != RADICAL_SINGLET)) && - 1 == num_metal_neigh ) { - if ( 1 == new_charge && 4 == val && 2 == at[iLigand].valence && - 4 == at[iLigand].chem_bonds_valence && - at[iLigand].bond_type[0] == at[iLigand].bond_type[1] ) { - ; /* do not add +1 charge to disconnected =N=, etc. 2004-10-27 */ - } else { - if ( bTautFlagsDone && new_charge != at[iLigand].charge ) { - *bTautFlagsDone |= TG_FLAG_MOVE_CHARGE_COORD_DONE; - } - at[iMetal].charge -= new_charge - at[iLigand].charge; - at[iLigand].charge = new_charge; - /*at[iLigand].radical = 0;*/ - } - } - } -exit_function: - return num_disconnections; /* ret;*/ -} - - - - -/****************************************************************************************/ -double dist3D( inp_ATOM *at1, inp_ATOM *at2 ) -{ - double dx = at1->x - at2->x; - double dy = at1->y - at2->y; - double dz = at1->z - at2->z; - return sqrt( dx*dx+dy*dy+dz*dz ); -} -/****************************************************************************************/ -#define MIN_BOND_LENGTH (1.0e-6) -#define MIN_COS (1.0e-6) -#define MIN_BOND_LENGTH2 (MIN_BOND_LENGTH*MIN_BOND_LENGTH) -#define MAX_BOND_LENGTH (1.0e30) -/****************************************************************************************/ -double GetMinDistDistribution( inp_ATOM *at, int num_at, int iat, int iat_H, - int bInAllComponents, double min_dist[], int num_segm ) -{ -/* const double one_pi = 2.0*atan2(1.0 , 0.0 ); */ - const double one_pi = 3.14159265358979323846; /* M_PI */ - const double two_pi = 2.0*one_pi; - const double f_step = two_pi / num_segm; - const double h_step = f_step/2.0; - - int i, j, k, kk, ki, kn, n, num_bonds; - double xi, yi, xn, yn, cross_prod_in, dot_prod_in, xni, yni, rni, tni, rmin; - double fi, fk, fn, ft, rt, rk, ri, rn, c, ave_bond_len; - - for ( i = 0; i < num_segm; i ++ ) { - min_dist[i] = MAX_BOND_LENGTH; /* more than any distance */ - } - num_bonds = 0; - ave_bond_len = 0.0; - for ( i = 0; i < num_at; i ++ ) { - if ( i != iat && i != iat_H && (bInAllComponents || at[i].component == at[iat].component) ) { - for ( j = 0; j < at[i].valence; j ++ ) { - n = at[i].neighbor[j]; - if ( (n > i && n != iat) || n == iat_H ) - continue; -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - if ( n == iat ) { - int stop = 1; /* */ - } -#endif - xi = at[i].x - at[iat].x; /* ri; i != iat */ - yi = at[i].y - at[iat].y; - xn = at[n].x - at[iat].x; /* rn; possibly n == iat */ - yn = at[n].y - at[iat].y; - cross_prod_in = xi*yn - xn*yi; /* ((r(i)-r(iat)) x (r(n)-r(iat)) */ - if ( cross_prod_in < -0.01*MIN_BOND_LENGTH2 ) { - /* make sure the r(i)->r(n) vector is counterclockwise around at[iat] */ - inchi_swap( (char*)&xi, (char*)&xn, sizeof(xi) ); - inchi_swap( (char*)&yi, (char*)&yn, sizeof(yi) ); - cross_prod_in = -cross_prod_in; - } - xni = xn - xi; /* r(n)->r(i) */ - yni = yn - yi; - rni = xni*xni + yni*yni; - if ( rni > 0.01*MIN_BOND_LENGTH2 ) { - /* vector length |ri->rn| is not too small */ - /* arrowhead of the vector r(t) = ri + (rn-ri)*t; 0 <= t <= 1 points to the bond ri->rn */ - /* r(tni) is perpendicular to the bond ri->rn so that min|r(t)| = r(tni) = |tni|*rni */ - tni = -(xni*xi + yni*yi)/rni; - /* find min. distance from n-i bond to at[iat] */ - if ( tni < 0.0 ) { - rmin = sqrt( xi*xi + yi*yi ); - } else - if ( tni > 1.0 ) { - rmin = sqrt( xn*xn + yn*yn ); - } else { - rmin = sqrt(tni*tni*rni); - } - ave_bond_len += sqrt( rni ); - num_bonds ++; - } else { - /* zero length i-n bond */ - tni = 0.5; /* fake */ - rmin = sqrt( xi*xi + yi*yi ); /* arbitrarily choose one */ - } - if ( rmin >= 0.1*MIN_BOND_LENGTH ) { - /* at[iat] does not belong to at[i]-at[n] bond */ - int bCalc_rt = 1; - fi = atan2( yi, xi ); - fn = (n == iat)? fi : atan2( yn, xn ); - if ( fi > fn ) { - /* make sure fn - fi >= 0 */ - fn += two_pi; - } - if ( fi < 0.0 ) { - fi += two_pi; - fn += two_pi; - } - ki = (int)floor((fi+h_step)/f_step); /* cast does not match function type */ - kn = (int)floor((fn+h_step)/f_step); - /* the bond may affect several segments */ - for ( k = ki; k <= kn; k ++ ) { - kk = k % num_segm; - if ( min_dist[kk] < rmin ) - continue; - if ( bCalc_rt ) { - if ( n == iat ) { - ft = fi; - rt = rmin; - } else { - double xt, yt; - xt = xi + xni*tni; - yt = yi + yni*tni; - ft = atan2( yt, xt ); - rt = sqrt(xt*xt + yt*yt); - } - bCalc_rt = 0; - } - fk = f_step * kk; - c = fabs(cos( fk - ft )); - if ( c < MIN_COS ) - c = MIN_COS; - rk = rt / c; - if ( min_dist[kk] > rk ) { - min_dist[kk] = rk; - } - } - } else { - /* rmin < 0.1*MIN_BOND_LENGTH */ - ri = xi*xi + yi*yi; - rn = xn*xn + yn*yn; - if ( ri > MIN_BOND_LENGTH2 && rn > MIN_BOND_LENGTH2 ) { - dot_prod_in = xn*xi + yn*yi; - /* a very short bond */ - if ( dot_prod_in > 0.01*MIN_BOND_LENGTH2 ) { - /* bond does not cross at[iat] */ - double fyixi = atan2( yi, xi ); - if ( fyixi < 0.0 ) fyixi += two_pi; - kk = (int)floor((fyixi+h_step)/f_step) % num_segm; - if ( min_dist[kk] > rmin ) { - min_dist[kk] = rmin; - } - } else - if ( dot_prod_in < -0.01*MIN_BOND_LENGTH2 ) { - /* bond does cross at[iat] */ - double fyixi = atan2( yi, xi ); - if ( fyixi < 0.0 ) fyixi += two_pi; - kk = (int)floor((fyixi+h_step)/f_step) % num_segm; - if ( min_dist[kk] > rmin ) { - min_dist[kk] = rmin; - } - fyixi += one_pi; - kk = (int)floor((fyixi+h_step)/f_step) % num_segm; - if ( min_dist[kk] > rmin ) { - min_dist[kk] = rmin; - } - } else { - ; /* error, should not happen */ - } - } else - if ( ri <= MIN_BOND_LENGTH2 && rn <= MIN_BOND_LENGTH2 ) { - /* a very short bond coincides with at[iat]; ignore */ - ; - } else { - /* one end of the bond coincides with at[iat] */ - fi = ri>rn? atan2( yi, xi) : atan2( yn, xn ); - if ( fi < 0.0 ) fi += two_pi; - kk = (int)floor((fi+h_step)/f_step) % num_segm; - if ( min_dist[kk] > rmin ) { - min_dist[kk] = rmin; - } - } - } - } - } - } - if ( num_bonds ) { - return ave_bond_len / (double)num_bonds; - } else { - return 0.0; - } -} - - - -/****************************************************************************************/ -int move_explicit_Hcation(inp_ATOM *at, int num_at, int iat, int iat_H, int bInAllComponents) -{ -#define NUM_SEGM 20 - /* const double one_pi = 2.0*atan2(1.0 , 0.0 ); */ - const double one_pi = 3.14159265358979323846; /* M_PI */ - const double two_pi = 2.0*one_pi; - const double f_step = two_pi / NUM_SEGM; - const double h_step = f_step/2.0; - double min_dist[NUM_SEGM]; - int nB, i, k, kk, next, val; - double r, r0, xd, yd, zd, xr, yr, zr, ave_bond_len; - /*double step = 4.0*atan(1.0)/NUM_SEGM;*/ - /* find at[iat] neighbors coordinates */ - xd=yd=zd=0.0; - if ( at[iat].valence ) { - for ( i = 0, nB=0, r = 0.0; i < at[iat].valence; i ++ ) { - next = at[iat].neighbor[i]; - xd += at[next].x; - yd += at[next].y; - zd += at[next].z; - r += dist3D( at+iat, at+next ); - nB ++; - } - xd /= (double)nB; - yd /= (double)nB; - zd /= (double)nB; - r /= (double)nB; - r0 = sqrt((double)(xd-at[iat].x)*(xd-at[iat].x) - + (double)(yd-at[iat].y)*(yd-at[iat].y)); - } else { - if ( at[iat_H].valence ) { - r = dist3D( at+iat_H, at+ (int)at[iat_H].neighbor[0] ); - } else { - r = 0.0; - } - r0 = 0.0; - } - ave_bond_len = GetMinDistDistribution( at, num_at, iat, iat_H, bInAllComponents, min_dist, NUM_SEGM ); - if ( r < MIN_BOND_LENGTH && ave_bond_len > MIN_BOND_LENGTH ) { - r = ave_bond_len; /* ave_bond_len = 0.0 may mean that it is 0D structure */ - } - if ( r > MIN_BOND_LENGTH ) { - /* process non-zero bond lengths */ - double f; - if ( 10.0*r0 < r ) { - xr = -r; /* arbitrary */ - yr = 0.0; - zr = 0.0; - } else { - /* - if ( r0 < MIN_BOND_LENGTH ) { - r0 = 1.0; - } - */ - xr = r * ( at[iat].x - xd )/r0; - yr = r * ( at[iat].y - yd )/r0; /* length = r */ - zr = r * ( at[iat].z - zd )/r0; - -/* -- test: opposire direction -- - xr = -r * ( at[iat].x - xd )/r0; - yr = -r * ( at[iat].y - yd )/r0; - zr = -r * ( at[iat].z - zd )/r0; -*/ - if ( xr*xr + yr*yr < 0.04*r*r ) { - xr = -r; - yr = 0.0; - } - } - r = sqrt( xr*xr + yr*yr ); - f = atan2( yr, xr ); - - if ( f < 0.0 ) - f += two_pi; - - - - kk = (int)floor((f+h_step)/f_step) % NUM_SEGM; /* cast does not match function type by design */ - if ( min_dist[kk] < 1.5* r ) { - double dist = 1.5*r; - int start=-1, len=0, start_max=-1, len_max=0; -again: - /* look for longest kk interval with min_dist[kk] >= dist */ - for ( k = 0, start = 0, len = 0, len_max = 0; k < 2*NUM_SEGM; k ++ ) { - kk = k % NUM_SEGM; - if ( min_dist[kk] >= dist ) { - if ( !len ++) { - start = k; - } - } else { - if ( len > len_max ) { - len_max = len; - start_max = start; - } - len = 0; - } - } - if ( !len_max ) { - if ( dist > 0.1*r ) { - dist *= 0.75; - goto again; - } else { - goto done; /* do it anyway */ - } - } else { - /* found a good sector */ - f = f_step * (start_max + (double)(len_max - 1)/2.0); - r0 = dist / 1.5; - xr = r0 * cos(f); - yr = r0 * sin(f); - zr = zr/r*r0; - } - } - } else { - xr = yr = zr = 0; - } - -done: - - if ( at[iat_H].valence ) { - /* disconnect H */ - next = at[iat_H].neighbor[0]; - for ( i = 0; i < at[next].valence; i ++ ) { - if ( at[next].neighbor[i] == iat_H ) { - RemoveInpAtBond( at, next, i ); - i = 0; /* success */ - break; - } - } - } else { - /* isolated H+ cation */ - next = iat_H; - i = 0; - at[iat_H].valence = 1; - at[iat_H].chem_bonds_valence = 1; - at[iat_H].bond_type[0] = BOND_TYPE_SINGLE; - } - if ( 0 == i /*i < at[next].valence*/ ) { - /* move charge */ - if ( at[next].charge > 0 && at[iat].charge < 0 ) { - at[next].charge --; - at[iat].charge ++; - } - /* connect H to at[iat] */ - val = at[iat].valence; - at[iat].neighbor[val] = iat_H; - at[iat].bond_type[val] = at[iat_H].bond_type[0]; - at[iat].bond_stereo[val] = 0; - at[iat].chem_bonds_valence += at[iat_H].bond_type[0]; - at[iat].valence = val+1; - - at[iat_H].component = at[iat].component; - at[iat_H].neighbor[0] = iat; - at[iat_H].bond_stereo[0] = 0; /* possible loss of stereo info */ - at[iat_H].x = at[iat].x + xr; - at[iat_H].y = at[iat].y + yr; - at[iat_H].z = at[iat].z + zr; - return 1; /* success */ - } - return 0; /* failed */ -} -/****************************************************************************************/ -int get_iat_number( int el_number, const int el_num[], int el_num_len ) -{ - int i; - for ( i = 0; i < el_num_len; i ++ ) { - if ( el_num[i] == el_number ) - return i; - } - return -1; -} - - -/*#endif*/ /* } DISCONNECT_SALTS */ - typedef enum tagIonAtomType { - IAT_H=0, - IAT_C, - IAT_N, - IAT_P, - IAT_O, - IAT_S, - IAT_Se, - IAT_Te, - IAT_F, - IAT_Cl, - IAT_Br, - IAT_I, - IAT_MAX - } ION_ATOM_TYPE; - -#if ( READ_INCHI_STRING == 1 ) -/****************************************************************************************/ -int bHeteroAtomMayHaveXchgIsoH( inp_ATOM *atom, int iat ) -{ - inp_ATOM *at = atom + iat, *at2; - static int el_num[IAT_MAX]; - int j, val, is_O=0, is_Cl=0, is_N=0, is_H=0, num_H, iat_numb, bAccept, cur_num_iso_H; - - if ( !el_num[IAT_H]) { - el_num[IAT_H ] = get_periodic_table_number( "H" ); - el_num[IAT_C ] = get_periodic_table_number( "C" ); - el_num[IAT_N ] = get_periodic_table_number( "N" ); - el_num[IAT_P ] = get_periodic_table_number( "P" ); - el_num[IAT_O ] = get_periodic_table_number( "O" ); - el_num[IAT_S ] = get_periodic_table_number( "S" ); - el_num[IAT_Se] = get_periodic_table_number( "Se"); - el_num[IAT_Te] = get_periodic_table_number( "Te"); - el_num[IAT_F ] = get_periodic_table_number( "F" ); - el_num[IAT_Cl] = get_periodic_table_number( "Cl"); - el_num[IAT_Br] = get_periodic_table_number( "Br"); - el_num[IAT_I ] = get_periodic_table_number( "I" ); - } - if ( 0 > (iat_numb = get_iat_number( at->el_number, el_num, IAT_MAX )) ) { - return 0; - } - if ( abs(at->charge) > 1 || at->radical && RADICAL_SINGLET != at->radical ) { - return 0; - } - val = -1; - switch( iat_numb ) { - case IAT_N: - case IAT_P: - is_N = 1; - val = 3+at->charge; - break; - case IAT_O: - case IAT_S: - case IAT_Se: - case IAT_Te: - is_O = 1; - val = 2+at->charge; - break; - case IAT_F: - case IAT_Cl: - case IAT_Br: - case IAT_I: - if ( at->charge == 0 ) { - is_Cl = 1; /* isolated HCl */ - val = 1; - } - break; - case IAT_H: - if ( at->valence == 0 && - at->charge == 1 ) { - is_H = 1; /* isolated proton */ - val = 0; - } - } - if ( val < 0 ) { - return 0; - } - num_H = NUMH(at,0); - if ( val != at->chem_bonds_valence + num_H ) { - return 0; - } - if ( is_H ) { - return 2; /* H atom */ - } else { - cur_num_iso_H = 0; - for ( j = 0, bAccept = 1; j < at->valence && bAccept; j ++ ) { - at2 = atom + (int)at->neighbor[j]; - if ( at2->charge && at->charge || (at2->radical && RADICAL_SINGLET != at2->radical ) ) { - return 0; /* adjacent charged/radical atoms: do not neutralizate */ - } - } - } - return 1; -} -#endif -/****************************************************************************************/ -int bNumHeterAtomHasIsotopicH( inp_ATOM *atom, int num_atoms ) -{ - static int el_num[IAT_MAX]; - int i, j, val, is_O=0, is_Cl=0, is_N=0, is_H=0, num_H, iat_numb, bAccept, num_iso_H, cur_num_iso_H, num_iso_atoms; - inp_ATOM *at, *at2; - /* one time initialization */ - if ( !el_num[IAT_H]) { - el_num[IAT_H ] = get_periodic_table_number( "H" ); - el_num[IAT_C ] = get_periodic_table_number( "C" ); - el_num[IAT_N ] = get_periodic_table_number( "N" ); - el_num[IAT_P ] = get_periodic_table_number( "P" ); - el_num[IAT_O ] = get_periodic_table_number( "O" ); - el_num[IAT_S ] = get_periodic_table_number( "S" ); - el_num[IAT_Se] = get_periodic_table_number( "Se"); - el_num[IAT_Te] = get_periodic_table_number( "Te"); - el_num[IAT_F ] = get_periodic_table_number( "F" ); - el_num[IAT_Cl] = get_periodic_table_number( "Cl"); - el_num[IAT_Br] = get_periodic_table_number( "Br"); - el_num[IAT_I ] = get_periodic_table_number( "I" ); - } - num_iso_H = 0; - num_iso_atoms = 0; - for ( i = 0, at = atom; i < num_atoms; i ++, at ++ ) { - - num_iso_atoms += ( at->iso_atw_diff != 0 || NUM_ISO_H(at,0) ); /* isotopic atoms and implicit isotopic H */ - - if ( 0 > (iat_numb = get_iat_number( at->el_number, el_num, IAT_MAX )) ) { - continue; - } - - if ( abs(at->charge) > 1 || at->radical && RADICAL_SINGLET != at->radical ) { - continue; - } - - val = -1; - switch( iat_numb ) { - case IAT_N: - case IAT_P: - is_N = 1; - val = 3+at->charge; - break; - case IAT_O: - case IAT_S: - case IAT_Se: - case IAT_Te: - is_O = 1; - val = 2+at->charge; - break; - case IAT_F: - case IAT_Cl: - case IAT_Br: - case IAT_I: - if ( at->charge == 0 ) { - is_Cl = 1; /* isolated HCl */ - val = 1; - } - break; - case IAT_H: - if ( at->valence == 0 && - at->charge == 1 ) { - is_H = 1; /* isolated proton */ - val = 0; - } - } - if ( val < 0 ) { - continue; - } - num_H = NUMH(at,0); - if ( val != at->chem_bonds_valence + num_H ) { - continue; - } - if ( is_H ) { - bAccept = 1; - cur_num_iso_H = (at->iso_atw_diff != 0); - } else { - cur_num_iso_H = 0; - for ( j = 0, bAccept = 1; j < at->valence && bAccept; j ++ ) { - at2 = atom + (int)at->neighbor[j]; - if ( at2->charge && at->charge || (at2->radical && RADICAL_SINGLET != at2->radical ) ) { - bAccept = 0; /* adjacent charged/radical atoms: do not neutralizate */ - break; - } else - if ( at2->el_number == el_num[IAT_H ] && at2->valence == 1 && at2->iso_atw_diff ) { - cur_num_iso_H ++; /* isotopic explicit H */ - } - } - if ( bAccept ) { - num_iso_atoms -= cur_num_iso_H; /* avoid counting explicit H as isotopic atom */ - cur_num_iso_H += NUM_ISO_H(at,0); - } - - } - num_iso_H += (bAccept && cur_num_iso_H); /* number of acceptable heteroatoms that have isotopic H */ - } - return ((num_iso_H? 1:0) | (num_iso_atoms? 2:0)); -} - - -/****************************************************/ -/* Mark and count disconnected structure components */ -/* by Depth-first searching each component */ -/****************************************************/ -int cmp_components( const void *a1, const void *a2 ) -{ - int ret; - AT_NUMB n1; - AT_NUMB n2; - - n1 = ((const AT_NUMB *)a1)[0]; /* number of atoms in the component -- descending order */ - n2 = ((const AT_NUMB *)a2)[0]; - if ( ret = (int)n2 - (int)n1 ) { - return ret; - } - /* stable sort */ - n1 = ((const AT_NUMB *)a1)[1]; /* component ordering number -- ascending order */ - n2 = ((const AT_NUMB *)a2)[1]; - ret = (int)n1 - (int)n2; - - return ret; - -} -/*************************************************************************************************/ -int MarkDisconnectedComponents( ORIG_ATOM_DATA *orig_at_data, int bProcessOldCompNumbers ) -{ - typedef AT_NUMB AT_TRIPLE[3]; - - inp_ATOM *at = orig_at_data->at; - int num_at = orig_at_data->num_inp_atoms; - AT_NUMB *nCurAtLen = NULL; - - AT_NUMB *nNewCompNumber = NULL; - AT_NUMB *nPrevAtom = NULL; - S_CHAR *iNeigh = NULL; - - AT_NUMB *nOldCompNumber = NULL; - int i, j, num_components, ret; - int new_comp_no; - AT_NUMB old_comp_no, another_comp_no, no_component; - - /* component_nbr[i][0] = number of atoms in the component i-1 - * component_nbr[i][1] = original component number (id-1) = i - * after sorting: - * component_nbr[j][2] = new number of component #(component_nbr[i][1]+1) - */ - AT_TRIPLE *component_nbr = NULL; - - /* initialize */ - if ( bProcessOldCompNumbers && !orig_at_data->nOldCompNumber ) { - bProcessOldCompNumbers = 0; - } - num_components = 0; - /* - for ( j = 0; j < num_at; j ++ ) { - at[j].component = 0; - } - */ - ret = -1; - if ( !num_at ) { - return 0; - } - if ( !( nNewCompNumber = (AT_NUMB *) inchi_calloc( num_at, sizeof(nNewCompNumber[0]) ) ) || - /* for non-recursive DFS only: */ - !( nPrevAtom = (AT_NUMB *) inchi_calloc( num_at, sizeof(nPrevAtom[0]) ) ) || - !( iNeigh = (S_CHAR *) inchi_calloc( num_at, sizeof(iNeigh[0]) ) )) { - goto exit_function; - } - /* mark and count; avoid deep DFS recursion: it may make verifying software unhappy */ - /* nNewCompNumber[i] will contain new component number for atoms at[i], i=0..num_at-1 */ - for ( j = 0; j < num_at; j++ ) { - if ( !nNewCompNumber[j] ) { - /* mark starting with at[j] */ - int fst_at, nxt_at, cur_at = j; - num_components ++; - /* first time at at[j] */ - nNewCompNumber[fst_at = cur_at] = (AT_NUMB) num_components; - /* find next neighbor */ - while ( 1 ) { - if ( iNeigh[cur_at] < at[cur_at].valence ) { - nxt_at = at[cur_at].neighbor[(int)iNeigh[cur_at] ++]; - if ( !nNewCompNumber[nxt_at] ) { - /* forward edge: found new atom */ - nNewCompNumber[nxt_at] = (AT_NUMB) num_components; - nPrevAtom[nxt_at] = (AT_NUMB) cur_at; - cur_at = nxt_at; - } - } else - if ( cur_at == fst_at ) { - break; /* done */ - } else { - cur_at = nPrevAtom[cur_at]; /* retract */ - } - } - } - } - inchi_free( nPrevAtom ); nPrevAtom = NULL; - inchi_free( iNeigh ); iNeigh = NULL; - - /* Allocate more memory */ - i = inchi_max( num_components, orig_at_data->num_components ); - if ( !(nCurAtLen = (AT_NUMB *) inchi_calloc( num_components+1, sizeof(nCurAtLen[0]) ) ) || - !(nOldCompNumber = (AT_NUMB *) inchi_calloc( i +1, sizeof(nOldCompNumber[0]) ) ) || - !(component_nbr = (AT_TRIPLE *) inchi_calloc( num_components+1, sizeof(component_nbr[0]) ) ) ) { - goto exit_function; - } - - /* count atoms per component and renumber the components */ - for ( i = 0; i < num_components; i ++ ) { - component_nbr[i][0] = 0; /* number of atoms in the component */ - component_nbr[i][1] = i; /* component ordering number */ - } - for ( j = 0; j < num_at; j ++ ) { - component_nbr[(int)nNewCompNumber[j]-1][0] ++; /* count atoms in each component */ - } - /* sort key: number of atoms; order: descending */ - qsort( (void*)component_nbr[0], num_components, - sizeof(component_nbr[0]), cmp_components); - /* invert the transposition */ - for ( i = 0; i < num_components; i ++ ) { - nCurAtLen[i] = component_nbr[i][0]; - component_nbr[ component_nbr[i][1] ][2] = i+1; - } - /* renumber the components so that the component with the greatest number of atoms is the first */ - no_component = num_at+1; - for ( j = 0; j < num_at; j ++ ) { - /* new component number for at[j] */ - new_comp_no = component_nbr[(int)nNewCompNumber[j]-1][2]-1; /* starts from 0 */ - if ( bProcessOldCompNumbers ) { - /* old component number for at[j] */ - old_comp_no = at[j].component; - /* fill out nOldCompNumber[]; initially it contains zeroes */ - if ( !old_comp_no ) { - nOldCompNumber[new_comp_no] = no_component; /* atom did not have component number */ - } else - if ( nOldCompNumber[new_comp_no] != old_comp_no ) { - if ( !nOldCompNumber[new_comp_no] ) { - nOldCompNumber[new_comp_no] = old_comp_no; - } else { - /* at[j] moved from old comp #old_comp_no to old comp #nOldCompNumber[new_comp_no] - Both components cannot be equal to any current component */ - another_comp_no = nOldCompNumber[new_comp_no]; - for ( i = 0; i < num_components; i ++ ) { - if ( nOldCompNumber[i] == old_comp_no || - nOldCompNumber[i] == another_comp_no ) { - nOldCompNumber[i] = no_component; - } - } - /* nOldCompNumber[new_comp_no] = num_at+1; */ - } - } - } - /* orig_at_data->nOldCompNumber */ - at[j].component = new_comp_no+1; /* starts from 1 */ - } - if ( bProcessOldCompNumbers ) { - for ( j = 0; j < num_components; j ++ ) { - if ( nOldCompNumber[j] == no_component ) { - /* the component has atom from another component */ - nOldCompNumber[j] = 0; - } else - if ( nOldCompNumber[j] && - !orig_at_data->nOldCompNumber[nOldCompNumber[j]-1] ) { - /* the component has changed in the previous processing */ - nOldCompNumber[j] = 0; - } - } - } else { - for ( j = 0; j < num_components; j ++ ) { - nOldCompNumber[j] = j + 1; - } - } - ret = num_components; -exit_function: - if ( nNewCompNumber ) - inchi_free( nNewCompNumber ); - if ( component_nbr ) - inchi_free( component_nbr ); - - if ( ret < 0 ) { - if ( nPrevAtom ) { - inchi_free( nPrevAtom ); - nPrevAtom = NULL; - } - if ( iNeigh ) { - inchi_free( iNeigh ); - iNeigh = NULL; - } - if ( nCurAtLen ) { - inchi_free( nCurAtLen ); - nCurAtLen = NULL; - } - if ( nOldCompNumber ) { - inchi_free( nOldCompNumber ); - nOldCompNumber = NULL; - } - num_components = ret; - } - /* avoid memory leaks */ - if ( orig_at_data->nCurAtLen ) - inchi_free ( orig_at_data->nCurAtLen ); - if ( orig_at_data->nOldCompNumber ) - inchi_free ( orig_at_data->nOldCompNumber ); - - orig_at_data->nCurAtLen = nCurAtLen; - orig_at_data->nOldCompNumber = nOldCompNumber; - - orig_at_data->num_components = num_components; - - return ret; /* number of disconnected components; 1=>single connected structure*/ -} -/******************************************************************************/ -/* Extract one (connected) component */ -/******************************************************************************/ -int ExtractConnectedComponent( inp_ATOM *at, int num_at, int component_number, inp_ATOM *component_at ) -{ - int i, j, num_component_at; - AT_NUMB *number; - if ( NULL == (number = (AT_NUMB*)inchi_calloc(num_at, sizeof(AT_NUMB)))){ - return CT_OUT_OF_RAM; /* out of memory */ /* */ - } - /* copy atoms */ - for ( i = 0, num_component_at = 0; i < num_at; i ++ ) { - if ( at[i].component == component_number ) { - number[i] = num_component_at; - component_at[num_component_at ++] = at[i]; - } - } - /* renumber neighbors */ - for ( i = 0; i < num_component_at; i ++ ) { - component_at[i].orig_compt_at_numb = (AT_NUMB)(i + 1); - for ( j = 0; j < component_at[i].valence; j ++ ) { - component_at[i].neighbor[j] = number[(int)component_at[i].neighbor[j]]; - } - } - inchi_free( number ); - return num_component_at; -} -/****************************************************************/ -int SetConnectedComponentNumber( inp_ATOM *at, int num_at, int component_number ) -{ - int i; - for ( i = 0; i < num_at; i ++ ) { - at[i].component = (AT_NUMB)component_number; - } - return 0; -} -/****************************************************************/ - -int Free_INChI_Stereo( INChI_Stereo *pINChI_Stereo ) -{ - if ( pINChI_Stereo ) { - qzfree( pINChI_Stereo->nNumber ); - qzfree( pINChI_Stereo->t_parity ); - qzfree( pINChI_Stereo->nNumberInv ); - qzfree( pINChI_Stereo->t_parityInv ); - qzfree( pINChI_Stereo->nBondAtom1 ); - qzfree( pINChI_Stereo->nBondAtom2 ); - qzfree( pINChI_Stereo->b_parity ); - } - return 0; -} - -/****************************************************************/ -INChI_Stereo *Alloc_INChI_Stereo(int num_at, int num_bonds) -{ - INChI_Stereo *pINChI_Stereo = (INChI_Stereo *)inchi_calloc(1, sizeof(INChI_Stereo)); - if ( pINChI_Stereo ) { - if ( num_at && - (pINChI_Stereo->nNumber = (AT_NUMB *)inchi_calloc(num_at, sizeof(pINChI_Stereo->nNumber[0]))) && - (pINChI_Stereo->t_parity = (S_CHAR *)inchi_calloc(num_at, sizeof(pINChI_Stereo->t_parity[0]))) - && (pINChI_Stereo->nNumberInv = (AT_NUMB *)inchi_calloc(num_at, sizeof(pINChI_Stereo->nNumberInv[0]))) - && (pINChI_Stereo->t_parityInv = (S_CHAR *)inchi_calloc(num_at, sizeof(pINChI_Stereo->t_parityInv[0]))) - ) { - ; - } else - if ( num_at ) { - goto out_of_RAM; - } - if ( num_bonds && - (pINChI_Stereo->nBondAtom1 =(AT_NUMB *)inchi_calloc(num_bonds, sizeof(pINChI_Stereo->nBondAtom1[0]))) && - (pINChI_Stereo->nBondAtom2 =(AT_NUMB *)inchi_calloc(num_bonds, sizeof(pINChI_Stereo->nBondAtom2[0]))) && - (pINChI_Stereo->b_parity =(S_CHAR *)inchi_calloc(num_bonds, sizeof(pINChI_Stereo->b_parity[0]))) ) { - ; - } else - if ( num_bonds ) { - goto out_of_RAM; - } - return pINChI_Stereo; - -out_of_RAM: - Free_INChI_Stereo( pINChI_Stereo ); - qzfree( pINChI_Stereo ); - } - return NULL; -} -/****************************************************************/ -int Free_INChI(INChI **ppINChI) -{ - INChI *pINChI; - - if ( pINChI = *ppINChI ) { -#if ( bREUSE_INCHI == 1 ) - if ( pINChI->nRefCount -- > 0 ) - return 1; -#endif - Free_INChI_Members(pINChI); - - qzfree( pINChI ); - *ppINChI = NULL; - - } - return 0; -} -/****************************************************************/ -int Free_INChI_Members(INChI *pINChI) -{ - if ( pINChI ) { - - Free_INChI_Stereo(pINChI->Stereo ); - Free_INChI_Stereo(pINChI->StereoIsotopic ); - qzfree(pINChI->nAtom ); - qzfree(pINChI->nConnTable ); - qzfree(pINChI->nTautomer ); - qzfree(pINChI->nNum_H ); - qzfree(pINChI->nNum_H_fixed ); - qzfree(pINChI->IsotopicAtom ); - qzfree(pINChI->IsotopicTGroup ); - qzfree(pINChI->nPossibleLocationsOfIsotopicH); - qzfree(pINChI->Stereo ); - qzfree(pINChI->StereoIsotopic ); - qzfree(pINChI->szHillFormula ); - } - return 0; -} -/****************************************************************/ -INChI *Alloc_INChI( inp_ATOM *at, int num_at, int *found_num_bonds, int *found_num_isotopic, int nAllocMode ) -{ - int i, num_bonds, num_isotopic_atoms; - INChI *pINChI; - int bIsotopic = (nAllocMode & REQ_MODE_ISO); - /* int bTautomeric = (nAllocMode & REQ_MODE_TAUT); */ - - if ( num_at <= 0 || NULL == (pINChI = (INChI *)inchi_calloc( 1, sizeof(INChI)))) { - return NULL; - } - - for ( i = 0, num_bonds = 0, num_isotopic_atoms = 0; i < num_at; i ++ ) { - num_bonds += at[i].valence; - /* if ( bIsotopic ) { */ - num_isotopic_atoms += (0 != at[i].iso_atw_diff || - !strcmp(at[i].elname, "D") || - !strcmp(at[i].elname, "T") || - at[i].num_iso_H[0] || - at[i].num_iso_H[1] || - at[i].num_iso_H[2]); - /* } */ - - } - num_bonds /= 2; - - *found_num_bonds = num_bonds; - *found_num_isotopic = num_isotopic_atoms; - - - if ( (pINChI->nAtom = (U_CHAR*) inchi_calloc( num_at, sizeof(pINChI->nAtom[0]))) && - (pINChI->nConnTable = (AT_NUMB*)inchi_calloc( num_at+num_bonds, sizeof(pINChI->nConnTable[0]))) && - (pINChI->nTautomer = (AT_NUMB*)inchi_calloc( ((3+INCHI_T_NUM_MOVABLE)*num_at)/2+1, sizeof(pINChI->nTautomer[0]))) && - (pINChI->nNum_H = (S_CHAR*) inchi_calloc( num_at, sizeof(pINChI->nNum_H[0]))) && - (pINChI->nNum_H_fixed= (S_CHAR*) inchi_calloc( num_at, sizeof(pINChI->nNum_H_fixed[0]))) - ) { - ; /* nTautomer length: max. number of tautomeric groups is num_at/2 - - 1 word -> number of t-groups - - each group has: - - 1 word -> number of endpoints+INCHI_T_NUM_MOVABLE - INCHI_T_NUM_MOVABLE words -> number(s) of moveable attachments - numbers of endpoints words -> canon. numbers - - max. occurs if each t-group has 2 atoms (num_at/2 t-groups) and all atoms - belong to t-groups (num_at endpoints) - - Total: 1 + (number of t-groups)*(1+INCHI_T_NUM_MOVABLE) + (number of endpoints) <= - 1 + (num_at/2) * (1+INCHI_T_NUM_MOVABLE) + num_at <= - 1 + (3+INCHI_T_NUM_MOVABLE)*num_at/2 words. - */ - - } else { - goto out_of_RAM; - } - pINChI->szHillFormula = NULL; /* the length is unknown */ - if ( bIsotopic ) { - if ( num_isotopic_atoms && - (pINChI->IsotopicAtom = (INChI_IsotopicAtom *)inchi_calloc(num_isotopic_atoms, sizeof(INChI_IsotopicAtom) )) && - (pINChI->IsotopicTGroup = (INChI_IsotopicTGroup *)inchi_calloc(num_isotopic_atoms, sizeof(INChI_IsotopicTGroup) )) - ) { - ; - } else - if ( num_isotopic_atoms ) { - goto out_of_RAM; - } - if ( !(pINChI->nPossibleLocationsOfIsotopicH = (AT_NUMB *)inchi_calloc( num_at+1, sizeof(pINChI->nPossibleLocationsOfIsotopicH[0]) ) ) ) { - goto out_of_RAM; - } - } - - if ((pINChI->Stereo = Alloc_INChI_Stereo(num_at, num_bonds)) - ) { - ; - } else { - goto out_of_RAM; - } - if ( bIsotopic ) { - if ((pINChI->StereoIsotopic = Alloc_INChI_Stereo(num_at, num_bonds)) - ) { - ; - } else { - goto out_of_RAM; - } - } - return pINChI; - -out_of_RAM: - if ( pINChI ) { - Free_INChI(&pINChI); - /* - inchi_free(pINChI); - */ - } - return NULL; -} -/****************************************************************/ -int Free_INChI_Aux( INChI_Aux **ppINChI_Aux ) -{ - INChI_Aux *pINChI_Aux = *ppINChI_Aux; - if ( pINChI_Aux ) { -#if ( bREUSE_INCHI == 1 ) - if ( pINChI_Aux->nRefCount -- > 0 ) - return 1; -#endif - - qzfree( pINChI_Aux->nOrigAtNosInCanonOrd ); - qzfree( pINChI_Aux->nIsotopicOrigAtNosInCanonOrd ); - qzfree( pINChI_Aux->nOrigAtNosInCanonOrdInv ); - qzfree( pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv ); - qzfree( pINChI_Aux->szOrigCoord ); - qzfree( pINChI_Aux->OrigInfo ); -/* - qzfree( pINChI_Aux->nOriginalAtomNumber ); - qzfree( pINChI_Aux->nCanonicalTGroupNumbers ); - qzfree( pINChI_Aux->nIsotopicCanonicalTGroupNumbers); - qzfree( pINChI_Aux->nTautomer ); - qzfree( pINChI_Aux->nNontautomericCanonicalNumbers ); - qzfree( pINChI_Aux->nIsotopicCanonicalNumbers ); - qzfree( pINChI_Aux->nNontautomericIsotopicCanonicalNumbers ); - qzfree( pINChI_Aux->nNontautomericEquNumbers ); - qzfree( pINChI_Aux->nNontautomericIsotopicEquNumbers ); -*/ - qzfree( pINChI_Aux->nConstitEquNumbers ); - qzfree( pINChI_Aux->nConstitEquTGroupNumbers ); - qzfree( pINChI_Aux->nConstitEquIsotopicNumbers ); - qzfree( pINChI_Aux->nConstitEquIsotopicTGroupNumbers ); - qzfree( pINChI_Aux ); - *ppINChI_Aux = NULL; - } - return 0; -} -/****************************************************************/ -INChI_Aux *Alloc_INChI_Aux( int num_at, int num_isotopic_atoms, int nAllocMode, int bOrigCoord ) -{ - INChI_Aux *pINChI_Aux; - int bIsotopic = (nAllocMode & REQ_MODE_ISO); - int num_at_tg = num_at + num_at/2; - /* int bTautomeric = (nAllocMode & REQ_MODE_TAUT); */ - - if ( num_at <= 0 || NULL == (pINChI_Aux = (INChI_Aux *)inchi_calloc(sizeof(INChI_Aux), 1))) { - return NULL; - } - if ( (pINChI_Aux->nOrigAtNosInCanonOrd = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nOrigAtNosInCanonOrd[0]), num_at_tg)) && - (pINChI_Aux->nOrigAtNosInCanonOrdInv = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nOrigAtNosInCanonOrd[0]), num_at_tg)) && - (pINChI_Aux->nConstitEquNumbers = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nConstitEquNumbers[0]), num_at_tg)) ) { - ; - } else { - goto out_of_RAM; - } - - if ( num_at > 1 && - (pINChI_Aux->nConstitEquTGroupNumbers = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nConstitEquTGroupNumbers[0]), num_at/2+1)) ) { - ; - } else - if ( num_at > 1 ) { - goto out_of_RAM; - } - - if ( num_at > 0 ) { - pINChI_Aux->OrigInfo = (ORIG_INFO *)inchi_calloc(sizeof(pINChI_Aux->OrigInfo[0]), num_at); - if ( !pINChI_Aux->OrigInfo ) - goto out_of_RAM; - } - if ( bOrigCoord && num_at > 0 ) { - pINChI_Aux->szOrigCoord = (MOL_COORD *)inchi_calloc(sizeof(pINChI_Aux->szOrigCoord[0]), num_at); - if ( !pINChI_Aux->szOrigCoord ) - goto out_of_RAM; - } - if ( bIsotopic ) { - if ( /*num_isotopic_atoms &&*/ - (pINChI_Aux->nIsotopicOrigAtNosInCanonOrd = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nIsotopicOrigAtNosInCanonOrd[0]), num_at_tg)) && - (pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nIsotopicOrigAtNosInCanonOrd[0]), num_at_tg)) && - (pINChI_Aux->nConstitEquIsotopicNumbers = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nConstitEquIsotopicNumbers[0]), num_at_tg)) ) { - ; - } else - if ( num_isotopic_atoms ) { - goto out_of_RAM; - } - if ( /*num_isotopic_atoms && num_at > 1 &&*/ - (pINChI_Aux->nConstitEquIsotopicTGroupNumbers = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nConstitEquIsotopicTGroupNumbers[0]), num_at/2+1)) ) { - ; - } else - if ( num_isotopic_atoms && num_at > 1 ) { - goto out_of_RAM; - } - } - return pINChI_Aux; - - -out_of_RAM: - if ( pINChI_Aux ) { - Free_INChI_Aux(&pINChI_Aux); - /* - inchi_free(pINChI_Aux); - */ - } - return NULL; -} -/***********************************************************************************/ - -#define IS_DEUTERIUM(i) (!strcmp( at[i].elname, "D" ) || at[i].iso_atw_diff == 2 && !strcmp( at[i].elname, "H" )) -#define IS_TRITIUM(i) (!strcmp( at[i].elname, "T" ) || at[i].iso_atw_diff == 3 && !strcmp( at[i].elname, "H" )) - -#define ABNORMAL_ISO(i) (at[i].iso_atw_diff == 1 || at[i].iso_atw_diff < -3 || at[i].iso_atw_diff > 5 ) -#define ABNORMAL_CHG(i) (abs(at[i].charge) > 3) -#define ABNORMAL_RAD(i) (RADICAL_SINGLET <= at[i].radical && at[i].radical <= RADICAL_TRIPLET ) - -#define ANY_ISO(i, X) ((X)? (at[i].iso_atw_diff && !IS_DEUTERIUM(i) && !IS_TRITIUM(i)) :\ - (at[i].iso_atw_diff || IS_DEUTERIUM(i) || IS_TRITIUM(i))) -#define ANY_CHG(i) (0 != at[i].charge) -#define ANY_RAD(i) (RADICAL_SINGLET <= at[i].radical && at[i].radical <= RADICAL_TRIPLET ) - -#define NORMAL_ISO(i, X) (ANY_ISO(i, X) && !ABNORMAL_ISO(i)) - - -/* needs additional M CHG. M RAD, M ISO line */ -/* due to ISIS/Draw feature always include M RAD for any radical */ -#define ABNORMAL_AT(i) ( at[i].radical || abs(at[i].charge) > 3 || \ - ABNORMAL_ISO(i) ) - -/* always add M ISO, M RAD, M CHG; Except: (bAtomsDT && D or T) */ -#define ADD_LINE_AT(i) ( at[i].charge || \ - at[i].radical || \ - at[i].iso_atw_diff && (bAtomsDT? (at[i].iso_atw_diff != 1 || strcmp(at[i].elname, "H")) : 1) ) -#define ALIASED_AT(i) (0 < NUM_ISO_H(at, i)) -/***********************************************************************************/ -#if ( TEST_RENUMB_ATOMS_SAVE_LONGEST == 1 || TEST_RENUMB_SWITCH == 1 ) -int WriteToSDfile( const INP_ATOM_DATA *inp_at_data, INCHI_IOSTREAM* fcb, const char* name, const char* comment, - const char *szLabel, const char *szValue) -{ - int i, j, k, num_bonds=0, ret=0, bAtomsDT = 1 /* treat D, T as normal atoms */, bV2000 = 0 /*V2000 Molfile */; - int bAtomNeedsAlias; - int flag_bad_charge=0, flag_bad_iso=0, nNumAddLines=0, nNumIsoLines=0, nNumChargeLines=0, nNumRadicalLines=0, nNumAliasLines=0; - int nNumNecessaryIsoLines = 0, nNumNecessaryChgLines = 0, nNumNecessaryRadLines = 0; - /*sp_ATOM *at; */ - /*float fzero=0.0F;*/ - double x, y, z; - int bNext /*, s*/; - const inp_ATOM *at = inp_at_data->at_fixed_bonds? inp_at_data->at_fixed_bonds : inp_at_data->at; - int num_atoms = inp_at_data->num_at; - /*at = species->atom;*/ - - /*inchi_ios_eprint(fcb,"%ld.MOL\n",species->casno);*/ - { /* block start */ - char strLocName[82]; - memset(strLocName, 0, sizeof(strLocName) ); - if ( name && *name ) { - strncpy( strLocName, name, 80 ); - } - inchi_ios_print_nodisplay( fcb,"%s\n", strLocName ); - } /* block end */ - /**********************************************************************/ - /** **/ - /** Important: Atoms with alias cannot have charge, radical, or **/ - /** isotope differences. **/ - /** **/ - /** Atoms with alias cannot be abnormal. **/ - /** **/ - /** Abnormal atoms are atoms which need M CHG, M RAD, M ISO **/ - /** **/ - /**********************************************************************/ - - /* F10.5 F12.5 I6 - IIPPPPPPPPMMDDYYHHmmddSSssssssssssEEEEEEEEEEEERRRRRR - inchi_ios_eprint( fcb,"NISTTRANHP09089809272D 1 1.0 0.0 %6ld\n", lEpa);*/ - - /*^^^ - inchi_ios_print_nodisplay( fcb," -%s v%s SDfile Output \n", INCHI_NAME, INCHI_VERSION); - - Changed 01/10/2009 to conform CTFile specification (by Symyx request)*/ - inchi_ios_print_nodisplay( fcb, - /* IIPPPPPPPPMMDDYYHHmmddSSssssssssssEEEEEEEEEEEERRRRRR*/ - " InChIV10 \n"); - - - - /*y_fprintf(fcb, " -CPSS- 1213981200n\n");*/ - - { /*block start*/ - char strLocName[82]; - - memset(strLocName, 0, sizeof(strLocName) ); - if ( comment && *comment ) { - strncpy( strLocName, comment, 80 ); - } - inchi_ios_print_nodisplay( fcb,"%s\n", strLocName ); - } /*block end*/ - for (i=0; i< num_atoms; i++) - num_bonds += at[i].valence; - num_bonds /= 2; - - /*find if we need "M CHG", "M RAD", "M ISO" */ - for (i=0, nNumAddLines = 0; i < num_atoms; i++) { - if ( bAtomNeedsAlias = ALIASED_AT(i) ) { - nNumAliasLines += 2 * bAtomNeedsAlias; - } else { - nNumNecessaryIsoLines += ABNORMAL_ISO(i); - nNumNecessaryChgLines += ABNORMAL_CHG(i); - nNumNecessaryRadLines += ABNORMAL_RAD(i); - nNumIsoLines += ANY_ISO(i, bAtomsDT); - nNumChargeLines += ANY_CHG(i); - nNumRadicalLines += ANY_RAD(i); - } - } - if ( !bV2000 ) { - if ( !nNumNecessaryRadLines && !nNumNecessaryChgLines ) { - nNumRadicalLines = 0; - nNumChargeLines = 0; - } - if ( !nNumNecessaryIsoLines ) { - nNumIsoLines = 0; - } - } - - - /* count additional M lines*/ - nNumChargeLines = ( nNumChargeLines + 7 ) / 8; - nNumRadicalLines = ( nNumRadicalLines + 7 ) / 8; - nNumIsoLines = ( nNumIsoLines + 7 ) / 8; - - nNumAddLines = nNumChargeLines + nNumRadicalLines + nNumIsoLines + nNumAliasLines; /* 1 for M END*/ - - if ( nNumAddLines || bV2000 ) { - nNumAddLines += 1; /* add 1 for "M END" line*/ - } - - /* aaabbblllfffcccsssxxxrrrpppiiimmmvvvvvv*/ - inchi_ios_print_nodisplay(fcb,"%3d%3d 0 0 0 0 0 0 0 0%3d%s\n",num_atoms, num_bonds, nNumAddLines,nNumAddLines?" V2000":""); - /* atoms block*/ - for (i=0; i < num_atoms; i++) { - char elname[ATOM_EL_LEN]; - int iso = 0; - int charge = 0; - int valence = 0; - int nIsotopeH = IS_DEUTERIUM(i)? 1 : IS_TRITIUM(i)? 2 : 0; - bAtomNeedsAlias = ALIASED_AT(i); /* Has implicit D and/or T neighbors */ - memset( elname, 0, sizeof(elname) ); - - if ( bAtomNeedsAlias ) { - /* alias */ - strcpy ( elname, "C" ); - } else { - /* isotope*/ - if ( nIsotopeH ) { - strcpy( elname, bAtomsDT? ( nIsotopeH==1? "D" : "T" ) : "H" ); - } else { - strncpy ( elname, at[i].elname, sizeof(elname)-1 ); - } - if ( !ABNORMAL_CHG(i) && !ANY_RAD(i) ) { - /* charge*/ - /* Only atoms without alias can be here*/ - switch ( at[i].charge ) { - case 3: charge = 1; break; - case 2: charge = 2; break; - case 1: charge = 3; break; - case -1: charge = 5; break; - case -2: charge = 6; break; - case -3: charge = 7; break; - case 0: charge = 0; break; - default: flag_bad_charge = 1; break; - }; - } - /* radical*/ - if ( ANY_RAD(i) && !ANY_CHG(i) ) { - if ( at[i].radical == RADICAL_DOUBLET ) { - charge = 4; - } - } - } - /* allow isotopic shift for aliased atoms */ - if ( NORMAL_ISO(i, bAtomsDT) ) { - iso = at[i].iso_atw_diff > 0? at[i].iso_atw_diff-1: - at[i].iso_atw_diff < 0? at[i].iso_atw_diff : - nIsotopeH? nIsotopeH : (flag_bad_iso ++, 0); - } - - x = at[i].x; - y = at[i].y; - z = at[i].z; - - if( at[i].num_H > 0 ) { - for ( j = 0, valence = 0; j < at[i].valence; j++ ) { - switch( k = at[i].bond_type[j] ) { /* fixed valence calculation 12-23-99 DCh.*/ - case 2: - case 3: - valence += 2*k; - break; - case 4: - valence += 3; - break; - default: - valence += 2; - } - } - valence = valence/2 + at[i].num_H; - } else - /* Added 07-09-2003 DCh*/ - if ( at[i].chem_bonds_valence > 0 ) { - valence = at[i].chem_bonds_valence; - } else - /* Added 07-09-2003 DCh*/ - if ( !at[i].valence && !at[i].num_H && !at[i].chem_bonds_valence ) { - valence = 15; - } - /*inchi_ios_eprint(fcb,"%10.4f%10.4f%10.4f %-3.3s%2d%3d 0 0 0 0 0 0 0\n",*/ - /* (float)at[i].x, (float)(-at[i].y), fzero, at[i].elname, iso, charge);*/ - /* xxxxxxyyyyyyzzzzzz aaa____ddcccsssnnnbbbvvvrrriiimmmeee */ - inchi_ios_print_nodisplay(fcb,"%10.4f%10.4f%10.4f %-3.3s%2d%3d 0 0%3d 0 0 0 0\n", - x, y, z, elname, (int)iso, (int)charge, valence /* at[i].special*/); - /* reflect image against x-axis; - when transforming MOLfile back to STDATA in mol_to_stdata(...), - make one more reflection to restore original orientation. - Reason: in MS Search y-axis is directed from top to bottom, - while in MOLfile y-axis goes from bottom to top. - */ - } - bNext = 0; /* debug only*/ - - /* bonds*/ - for (i=0; i< num_atoms; i++) { - for (j=0; j 1 ) { - sprintf( str_m + strlen(str_m), "%d", num_H ); - } - } - if ( num_H = at[i].num_iso_H[1] ) { /* deuterium */ - strcat( str_m, "D" ); - if ( num_H > 1 ) { - sprintf( str_m + strlen(str_m), "%d", num_H ); - } - } - if ( num_H = at[i].num_iso_H[2] ) { /* Tritium */ - strcat( str_m, "T" ); - if ( num_H > 1 ) { - sprintf( str_m + strlen(str_m), "%d", num_H ); - } - } - /* Add charge to the Alias */ - if ( at[i].charge){ - strcat(str_m, at[i].charge>0? "+" : "-"); - if ( 1 < (j=abs(at[i].charge)) ) - sprintf( str_m+strlen(str_m), "%d", j ); - } - /* Add radical to the Alias */ - switch( at[i].radical ) { - case RADICAL_SINGLET: - strcat( str_m, ":" ); - break; - case RADICAL_DOUBLET: - strcat( str_m, "^" ); - break; - case RADICAL_TRIPLET: - strcat( str_m, "^^" ); - break; - } - inchi_ios_print_nodisplay( fcb, "%s\n", str_m ); - num_m ++; - } - } - if ( num_m != nNumAliasLines ) { - /* error in lines counting*/ - ret ++; - } - } - /* charges*/ - str_m[0] = 0; - num_m = 0; - if ( nNumChargeLines ) { - for (i=0; i < num_atoms; i++) { - if ( ANY_CHG(i) && !ALIASED_AT(i) ) { - sprintf( entry, " %3d %3d", i+1, (int)at[i].charge ); - strcat( str_m, entry ); - num_m ++; - } - if ( i == num_atoms-1 && num_m || num_m == 8 ) { - inchi_ios_print_nodisplay( fcb, "M CHG%3d%s\n", num_m, str_m ); - str_m[0] = 0; - num_m = 0; - } - } - } - /* radicals*/ - str_m[0] = 0; - num_m = 0; - if ( nNumRadicalLines ) { - for (i=0; i < num_atoms; i++) { - if ( ANY_RAD(i) && !ALIASED_AT(i) ) { - int radical = (at[i].radical==RADICAL_SINGLET || - at[i].radical==RADICAL_DOUBLET || - at[i].radical==RADICAL_TRIPLET)? at[i].radical : 0; - if ( radical ) { - sprintf( entry, " %3d %3d", i+1, radical ); - strcat( str_m, entry ); - num_m ++; - } - } - if ( i == num_atoms-1 && num_m || num_m == 8 ) { - inchi_ios_print_nodisplay( fcb, "M RAD%3d%s\n", num_m, str_m ); - str_m[0] = 0; - num_m = 0; - } - } - } - /* isotopes*/ - str_m[0] = 0; - num_m = 0; - if ( nNumIsoLines ) { - int el_num, iso; - for (i=0; i < num_atoms; i++) { - if ( ANY_ISO(i,bAtomsDT) && !ALIASED_AT(i) ) { - if ( IS_DEUTERIUM(i) ) { - iso = 1; - el_num = 1; - } else - if ( IS_TRITIUM(i) ) { - iso = 2; - el_num = 1; - } else { - iso = at[i].iso_atw_diff > 0? at[i].iso_atw_diff-1 : at[i].iso_atw_diff; - el_num = at[i].el_number; - } - iso += get_atw_from_elnum( el_num ); - - sprintf( entry, " %3d %3d", i+1, iso ); - strcat( str_m, entry ); - num_m ++; - } - - if ( i == num_atoms-1 && num_m || num_m == 8 ) { - inchi_ios_print_nodisplay( fcb, "M ISO%3d%s\n", num_m, str_m ); - str_m[0] = 0; - num_m = 0; - } - } - } - inchi_ios_print_nodisplay( fcb, "M END\n" ); - } - if ( szValue && szValue[0] ) { - if ( szLabel && szLabel[0] ) { - inchi_ios_print_nodisplay( fcb, "> <%s>\n", szLabel ); - } else { - inchi_ios_print_nodisplay( fcb, "> \n" ); - } - inchi_ios_print_nodisplay( fcb, "%s\n\n", szValue ); - } - inchi_ios_print_nodisplay(fcb, "$$$$\n"); - - - return ret; - -} -#endif -/***************************************************************************************************/ -int WriteOrigAtomDataToSDfile(const ORIG_ATOM_DATA *inp_at_data, INCHI_IOSTREAM * fcb, const char* name, - const char* comment, int bChiralFlag, int bAtomsDT, const char *szLabel, const char *szValue) -{ - int i, j, k, num_bonds=0, ret=0; - int bAtomNeedsAlias; - int flag_bad_charge=0, flag_bad_iso = 0; - int nNumAddLines=0, nNumIsoLines=0, nNumChargeLines=0, nNumRadicalLines=0, nNumAliasLines=0; - int nNumNecessaryIsoLines = 0, nNumNecessaryChgLines = 0, nNumNecessaryRadLines = 0; - int bV2000 = SDF_OUTPUT_V2000; - double x, y, z; - int bNext /*, s*/; - const inp_ATOM *at = inp_at_data->at; - int num_atoms = inp_at_data->num_inp_atoms; - /*at = species->atom;*/ - - /*inchi_ios_eprint(fcb,"%ld.MOL\n",species->casno);*/ - { /* block start */ - char strLocName[82]; - memset(strLocName, 0, sizeof(strLocName) ); - if ( name && *name ) { - strncpy( strLocName, name, 80 ); - /* --- debug only --- - if ( strstr( name, "#3959" ) ) { - int stop = 1; - } - */ - } - inchi_ios_print_nodisplay( fcb,"%s\n", strLocName ); - } /* block end */ - /**********************************************************************/ - /** **/ - /** Important: Atoms with alias cannot have charge, radical **/ - /** isotope differences are allowed **/ - /** **/ - /** Atoms with alias cannot be abnormal. **/ - /** **/ - /** Abnormal atoms are atoms which need M CHG, M RAD, M ISO **/ - /** **/ - /** Output aliased atoms if they have implicit D or T **/ - /** **/ - /**********************************************************************/ - - /* F10.5 F12.5 I6 - IIPPPPPPPPMMDDYYHHmmddSSssssssssssEEEEEEEEEEEERRRRRR - inchi_ios_eprint( fcb,"NISTTRANHP09089809272D 1 1.0 0.0 %6ld\n", lEpa);*/ - /*^^^ - inchi_ios_print_nodisplay( fcb," %s v%s SDfile Output \n", INCHI_NAME, INCHI_VERSION); - - Changed 01/10/2009 to conform CTFile specification (by Symyx request)*/ - inchi_ios_print_nodisplay( fcb, - /* IIPPPPPPPPMMDDYYHHmmddSSssssssssssEEEEEEEEEEEERRRRRR*/ - " InChIV10 \n"); - /*y_fprintf(fcb, " -CPSS- 1213981200n\n");*/ - - { /*block start*/ - char strLocName[82]; - - memset(strLocName, 0, sizeof(strLocName) ); - if ( comment && *comment ) { - strncpy( strLocName, comment, 80 ); - } - inchi_ios_print_nodisplay( fcb,"%s\n", strLocName ); - } /*block end*/ - for (i=0; i< num_atoms; i++) - num_bonds += at[i].valence; - num_bonds /= 2; - - /*find if we need "M CHG" and "M RAD"*/ - for (i=0; i < num_atoms; i++) { - if ( bAtomNeedsAlias = ALIASED_AT(i) ) { /* has isotopic implicit D or T; ignoring pure 1H */ - nNumAliasLines += 2 * bAtomNeedsAlias; - } else { - /* abnormal means atom needs CHG, RAD, or ISO entry */ - /* nNumAddLines += ABNORMAL_AT(i); */ - /* nNumIso += ( 0 == strcmp( at[i].elname, "D" ) || ( 0 == strcmp( at[i].elname, "T" ) || at[i].iso_atw_diff ) ); */ - /* nNumAddIso += at[i].iso_atw_diff && (at[i].iso_atw_diff == 1 || at[i].iso_atw_diff < -3 || at[i].iso_atw_diff > 5 ); */ - nNumNecessaryIsoLines += ABNORMAL_ISO(i); - nNumNecessaryChgLines += ABNORMAL_CHG(i); - nNumNecessaryRadLines += ABNORMAL_RAD(i); - nNumIsoLines += ANY_ISO(i, bAtomsDT); - nNumChargeLines += ANY_CHG(i); - nNumRadicalLines += ANY_RAD(i); - } - } - nNumChargeLines = ( nNumChargeLines + 7 ) / 8; - nNumRadicalLines = ( nNumRadicalLines + 7 ) / 8; - nNumIsoLines = ( nNumIsoLines + 7 ) / 8; - - if ( !bV2000 ) { - if ( !nNumNecessaryRadLines && !nNumNecessaryChgLines ) { - nNumRadicalLines = 0; - nNumChargeLines = 0; - } - if ( !nNumNecessaryIsoLines ) { - nNumIsoLines = 0; - } - } - - - /* recalculate number of added lines */ - nNumAddLines = nNumChargeLines + nNumRadicalLines + nNumIsoLines + nNumAliasLines; /* 1 for M END*/ - - if ( nNumAddLines || bV2000 ) { - nNumAddLines += 1; /* add 1 for "M END" line*/ - } - - /* aaabbblllfffcccsssxxxrrrpppiiimmmvvvvvv*/ - inchi_ios_print_nodisplay(fcb,"%3d%3d 0 0%3d 0 0 0 0 0%3d%s\n", - num_atoms, num_bonds, bChiralFlag?1:0, nNumAddLines,nNumAddLines?" V2000":""); - /* atoms block*/ - for (i=0; i < num_atoms; i++) { - char elname[ATOM_EL_LEN] = "\0\0\0\0\0"; - int iso = 0; - int charge = 0; - int valence = 0; - int nIsotopeH = IS_DEUTERIUM(i)? 1 : IS_TRITIUM(i)? 2 : 0; - int bonds_val; - bAtomNeedsAlias = ALIASED_AT(i); - memset( elname, 0, sizeof(elname) ); - - if ( bAtomNeedsAlias ) { - /* alias */ - strcpy ( elname, "C" ); - } else { - /* isotope*/ - if ( nIsotopeH ) { - strcpy( elname, bAtomsDT? ( nIsotopeH==1? "D" : "T" ) : "H" ); - } else { - strncpy ( elname, at[i].elname, sizeof(elname)-1 ); - } - if ( !ABNORMAL_CHG(i) && !ANY_RAD(i) ) { - /* charge*/ - /* Only atoms without alias can be here*/ - switch ( at[i].charge ) { - case 3: charge = 1; break; - case 2: charge = 2; break; - case 1: charge = 3; break; - case -1: charge = 5; break; - case -2: charge = 6; break; - case -3: charge = 7; break; - case 0: charge = 0; break; - default: flag_bad_charge = 1; break; - }; - } - /* radical*/ - if ( ANY_RAD(i) && !ANY_CHG(i) ) { - if ( at[i].radical == RADICAL_DOUBLET ) { - charge = 4; - } - } - } - /* allow isotopic shift for aliased atoms */ - if ( NORMAL_ISO(i, bAtomsDT) ) { - iso = at[i].iso_atw_diff > 0? at[i].iso_atw_diff-1: - at[i].iso_atw_diff < 0? at[i].iso_atw_diff : - nIsotopeH? nIsotopeH : (flag_bad_iso ++, 0); - } - - x = at[i].x; - y = at[i].y; - z = at[i].z; - - /* valence -- set only if needed */ - bonds_val = nBondsValenceInpAt( at+i, NULL, NULL ); - valence=needed_unusual_el_valence( at[i].el_number, at[i].charge, at[i].radical, - at[i].chem_bonds_valence, bonds_val, NUMH(at, i), at[i].valence ); - if ( valence < 0 ) { - valence = 15; /* means no bonds nor H */ - } - - /*inchi_ios_eprint(fcb,"%10.4f%10.4f%10.4f %-3.3s%2d%3d 0 0 0 0 0 0 0\n",*/ - /* (float)at[i].x, (float)(-at[i].y), fzero, at[i].elname, iso, charge);*/ - /* xxxxxxyyyyyyzzzzzz aaa____ddcccsssnnnbbbvvvrrriiimmmeee */ - inchi_ios_print_nodisplay(fcb,"%10.4f%10.4f%10.4f %-3.3s%2d%3d 0 0%3d 0 0 0 0\n", - x, y, z, elname, (int)iso, (int)charge, valence /* at[i].special*/); - /* reflect image against x-axis; - when transforming MOLfile back to STDATA in mol_to_stdata(...), - make one more reflection to restore original orientation. - Reason: in MS Search y-axis is directed from top to bottom, - while in MOLfile y-axis goes from bottom to top. - */ - } - bNext = 0; /* debug only*/ - - /* bonds*/ - for (i=0; i< num_atoms; i++) { - for (j=0; j0? "+" : "-"); - if ( 1 < (j=abs(at[i].charge)) ) { - len += sprintf( str_m+len, "%d", j ); - } - } - /* Add radical to the Alias */ - if ( at[i].radical == RADICAL_SINGLET ) { - len += sprintf( str_m+len, "%s", ":" ); - } else - if ( at[i].radical == RADICAL_DOUBLET ) { - len += sprintf( str_m+len, "%s", "^" ); - } else - if ( at[i].radical == RADICAL_TRIPLET ) { - len += sprintf( str_m+len, "%s", "^^" ); - } - inchi_ios_print_nodisplay( fcb, "%s\n", str_m ); - num_m ++; - } - } - if ( num_m != nNumAliasLines ) { - /* error in lines counting*/ - ret ++; - } - } - /* charges*/ - str_m[0] = 0; - num_m = 0; - if ( nNumChargeLines ) { - for (i=0; i < num_atoms; i++) { - if ( at[i].charge && !ALIASED_AT(i) ) { - sprintf( entry, " %3d %3d", i+1, (int)at[i].charge ); - strcat( str_m, entry ); - num_m ++; - } - if ( i == num_atoms-1 && num_m || num_m == 8 ) { - inchi_ios_print_nodisplay( fcb, "M CHG%3d%s\n", num_m, str_m ); - str_m[0] = 0; - num_m = 0; - } - } - } - /* radicals*/ - str_m[0] = 0; - num_m = 0; - if ( nNumRadicalLines ) { - for (i=0; i < num_atoms; i++) { - if ( at[i].radical && !ALIASED_AT(i) ) { - int radical = (at[i].radical==RADICAL_SINGLET || - at[i].radical==RADICAL_DOUBLET || - at[i].radical==RADICAL_TRIPLET)? at[i].radical : 0; - if ( radical ) { - sprintf( entry, " %3d %3d", i+1, radical ); - strcat( str_m, entry ); - num_m ++; - } - } - if ( i == num_atoms-1 && num_m || num_m == 8 ) { - inchi_ios_print_nodisplay( fcb, "M RAD%3d%s\n", num_m, str_m ); - str_m[0] = 0; - num_m = 0; - } - } - } - /* isotopes*/ - str_m[0] = 0; - num_m = 0; - if ( nNumIsoLines ) { - int el_num, iso; - for (i=0; i < num_atoms; i++) { - /* - if ( 0 == strcmp( at[i].elname, "D" ) ) { - sprintf( entry, " %3d %3d", i+1, 2 ); - strcat( str_m, entry ); - num_m ++; - } else - if ( 0 == strcmp( at[i].elname, "T" ) ) { - sprintf( entry, " %3d %3d", i+1, 3 ); - strcat( str_m, entry ); - num_m ++; - } else - if ( k = at[i].iso_atw_diff ) { - int mw = get_atw_from_elnum( at[i].el_number ); - mw += (k > 0)? k-1 : k; - sprintf( entry, " %3d %3d", i+1, mw ); - strcat( str_m, entry ); - num_m ++; - } - */ - if ( ANY_ISO(i, bAtomsDT) && !ALIASED_AT(i) ) { - if ( IS_DEUTERIUM(i) ) { - iso = 1; - el_num = 1; - } else - if ( IS_TRITIUM(i) ) { - iso = 2; - el_num = 1; - } else { - iso = at[i].iso_atw_diff > 0? at[i].iso_atw_diff-1 : at[i].iso_atw_diff; - el_num = at[i].el_number; - } - iso += get_atw_from_elnum( el_num ); - - sprintf( entry, " %3d %3d", i+1, iso ); - strcat( str_m, entry ); - num_m ++; - } - - - if ( i == num_atoms-1 && num_m || num_m == 8 ) { - inchi_ios_print_nodisplay( fcb, "M ISO%3d%s\n", num_m, str_m ); - str_m[0] = 0; - num_m = 0; - } - } - } - inchi_ios_print_nodisplay( fcb, "M END\n" ); - } - if ( szValue && szValue[0] ) { - if ( szLabel && szLabel[0] ) { - inchi_ios_print_nodisplay( fcb, "> <%s>\n", szLabel ); - } else { - inchi_ios_print_nodisplay( fcb, "> \n" ); - } - inchi_ios_print_nodisplay( fcb, " %s\n\n", szValue ); - } - inchi_ios_print_nodisplay(fcb, "$$$$\n"); - - - return ret; - -} -#if ( FIX_ADJ_RAD == 1 ) -/*************************************************************************/ -int FixNextRadicals( int cur_at, inp_ATOM *at ); -int FixNextRadicals( int cur_at, inp_ATOM *at ) -{ - int j, neigh, num_found = 0; - for ( j = 0; j < at[cur_at].valence; j ++ ) { - neigh = at[cur_at].neighbor[j]; - if ( at[neigh].radical == RADICAL_DOUBLET ) { - at[neigh].radical = 0; - num_found ++; - num_found += FixNextRadicals( neigh, at ); - } - } - return num_found; -} -/*************************************************************************/ -int FixAdjacentRadicals( int num_inp_atoms, inp_ATOM *at ) -{ - int i, j; - char *bVisited = NULL; - int nNumFound = 0, neigh, cur_found; - for ( i = 0; i < num_inp_atoms; i ++ ) { - if ( at[i].radical == RADICAL_DOUBLET ) { - cur_found = 1; - for ( j = 0; j < at[i].valence; j ++ ) { - neigh = at[i].neighbor[j]; - if ( at[neigh].radical == RADICAL_DOUBLET ) { - cur_found ++; - } - } - if ( cur_found >= 3 ) { - nNumFound ++; - at[i].radical = 0; - nNumFound += FixNextRadicals( i, at ); - } - } - } - return nNumFound; -} -#endif - -#ifdef COMPILE_ANSI_ONLY -#ifndef TARGET_API_LIB -/* -#include -#include "inpdef.h" -*/ -void PrintFileName( const char *fmt, - FILE *output_file, /* INCHI_IOSTREAM *output_file, */ - const char *szFname ) -{ - inchi_print_nodisplay( output_file, fmt, szFname ); -} -#endif -#endif - diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/util.h b/INCHI-1-SRC/INCHI_API/inchi_dll/util.h deleted file mode 100644 index ddcc2ae..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/util.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __UTIL_H__ -#define __UTIL_H__ - -#include "inpdef.h" - -/* BILLY 8/6/04 */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -int get_atw(const char *elname); -int get_atw_from_elnum( int nAtNum ); -int get_num_H (const char* elname, int inp_num_H, S_CHAR num_iso_H[], int charge, int radical, - int chem_bonds_valence, int atom_input_valence, int bAliased, int bDoNotAddH, int bHasMetalNeighbor ); -int extract_ChargeRadical( char *elname, int *pnRadical, int *pnCharge ); -int extract_H_atoms( char *elname, S_CHAR num_iso_H[] ); -int normalize_name( char* name ); - -int mystrncpy(char *target,const char *source,unsigned maxlen); -char* LtrimRtrim( char *p, int* nLen ); - -void remove_trailing_spaces( char* p ); -void remove_one_lf( char* p); -void mystrrev( char *p ); - - - - - - -#define ALPHA_BASE 27 -long inchi_strtol( const char *str, const char **p, int base); -double inchi_strtod( const char *str, const char **p ); - -AT_NUMB *is_in_the_list( AT_NUMB *pathAtom, AT_NUMB nNextAtom, int nPathLen ); -int get_periodic_table_number( const char* elname ); -int is_el_a_metal( int nPeriodicNum ); -int get_el_valence( int nPeriodicNum, int charge, int val_num ); -int get_unusual_el_valence( int nPeriodicNum, int charge, int radical, int bonds_valence, int num_H, int num_bonds ); -int detect_unusual_el_valence( int nPeriodicNum, int charge, int radical, int bonds_valence, int num_H, int num_bonds ); -int needed_unusual_el_valence( int nPeriodicNum, int charge, int radical, int bonds_valence, - int actual_bonds_val, int num_H, int num_bonds ); -int get_el_type( int nPeriodicNum ); -int get_el_number( const char* elname ); -int do_not_add_H( int nPeriodicNum ); -int GetElementFormulaFromAtNum(int nAtNum, char *szElement ); -int MakeRemovedProtonsString( int nNumRemovedProtons, NUM_H *nNumExchgIsotopicH, NUM_H *nNumRemovedProtonsIsotopic, - int bIsotopic, char *szRemovedProtons, int *num_removed_iso_H ); - -/* ion pairs and fixing bonds */ -int num_of_H( inp_ATOM *at, int iat ); -int has_other_ion_neigh( inp_ATOM *at, int iat, int iat_ion_neigh, const char *el, int el_len ); -int has_other_ion_in_sphere_2(inp_ATOM *at, int iat, int iat_ion_neigh, const char *el, int el_len ); -int nNoMetalNumBonds( inp_ATOM *at, int at_no ); -int nNoMetalBondsValence( inp_ATOM *at, int at_no ); -int nNoMetalNeighIndex( inp_ATOM *at, int at_no ); -int nNoMetalOtherNeighIndex( inp_ATOM *at, int at_no, int cur_neigh ); -int nNoMetalOtherNeighIndex2( inp_ATOM *at, int at_no, int cur_neigh, int cur_neigh2 ); - -void extract_inchi_substring(char ** buf, const char *str, size_t slen); - -/* mol2atom.c */ -int nBondsValToMetal( inp_ATOM* at, int iat ); - -/* ichi_bns.c */ -int nBondsValenceInpAt( const inp_ATOM *at, int *nNumAltBonds, int *nNumWrongBonds ); - -int bHeteroAtomMayHaveXchgIsoH( inp_ATOM *atom, int iat ); - -/* IChICan2.c */ -int SetBitFree( void ); - -void WriteCoord( char *str, double x ); - - -extern int ERR_ELEM; -extern int nElDataLen; - -/* BILLY 8/6/04 */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -#endif /* __UTIL_H__*/ - diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/e_ichi_io.c b/INCHI-1-SRC/INCHI_API/inchi_main/e_ichi_io.c deleted file mode 100644 index 3b5ad1c..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_main/e_ichi_io.c +++ /dev/null @@ -1,1339 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include -#include -#include - -#include "e_mode.h" -#include "e_ctl_data.h" - -#include "inchi_api.h" - -#include "ichitime.h" - -#include "e_comdef.h" -#include "e_ichicomp.h" -#include "e_util.h" -#include "e_ichi_io.h" -#include "e_ichi_parms.h" - - -#define LtrimRtrim e_LtrimRtrim - -/********************************************** - * output " L=V" or " L missing" or "" - * The fprintf format string must contain %s%s%s%s - */ -char e_gsMissing[] = "is missing"; -char e_gsEmpty[] = ""; -char e_gsSpace[] = " "; -char e_gsEqual[] = "="; - - - - - - -/*******************************************************************/ -void e_PrintFileName( const char *fmt, FILE *output_file, const char *szFname ) -{ - fprintf( output_file, fmt, szFname ); -} - - - - - -#ifdef COMPILE_ANSI_ONLY - -static clock_t InchiClock(void); - -#ifdef INCHI_USETIMES -static clock_t InchiClock(void) -{ - struct tms buf; - clock_t c = times( &buf ); - if ( c != (clock_t)-1 ) { - return buf.tms_utime; - } - return 0; -} -#else -static clock_t InchiClock(void) -{ - clock_t c = clock(); - if ( c != (clock_t)-1 ) { - return c; - } - return 0; -} -#endif - -#define INCHI_MSEC(X) (long)((1000.0/(double)CLOCKS_PER_SEC)*(X)) -#define INCHI_CLOCK_T(X) (clock_t)( (double)(X) / 1000.0 * (double)CLOCKS_PER_SEC ) -const clock_t FullMaxClock = (clock_t)(-1); -const clock_t HalfMaxClock = (clock_t)(-1) / 2; -clock_t MaxPositiveClock = 0; -clock_t MinNegativeClock = 0; -clock_t HalfMaxPositiveClock = 0; -clock_t HalfMinNegativeClock = 0; - -static void FillMaxMinClock(void); /* keep compiler happy */ - -static void FillMaxMinClock(void) -{ /* assume clock_t is a signed integral value */ - if ( !MaxPositiveClock ) { - clock_t valPos=0, val1 = 1; - while ( 0 < ((val1 <<= 1), (val1 |= 1)) ) { - valPos = val1; - } - MaxPositiveClock = valPos; - MinNegativeClock = -valPos; - HalfMaxPositiveClock = MaxPositiveClock / 2; - HalfMinNegativeClock = MinNegativeClock / 2; - } -} - - -/******** get current process time ****************************************/ -void InchiTimeGet( inchiTime *TickEnd ) -{ - TickEnd->clockTime = InchiClock(); -} -/******** returns difference TickEnd - TickStart in milliseconds **********/ -long InchiTimeMsecDiff( inchiTime *TickEnd, inchiTime *TickStart ) -{ - if ( FullMaxClock > 0 ) { - clock_t delta; - if ( !TickEnd || !TickStart ) - return 0; - /* clock_t is unsigned */ - if ( TickEnd->clockTime > TickStart->clockTime ) { - if ( TickEnd->clockTime > HalfMaxClock && - TickEnd->clockTime - TickStart->clockTime > HalfMaxClock ) { - /* overflow in TickStart->clockTime, actually TickStart->clockTime was later */ - delta = (FullMaxClock - TickEnd->clockTime) + TickStart->clockTime; - return -INCHI_MSEC(delta); - } - delta = TickEnd->clockTime - TickStart->clockTime; - return INCHI_MSEC(delta); - } else - if ( TickEnd->clockTime < TickStart->clockTime ) { - if ( TickStart->clockTime > HalfMaxClock && - TickStart->clockTime - TickEnd->clockTime > HalfMaxClock ) { - /* overflow in TickEnd->clockTime, actually TickEnd->clockTime was later */ - delta = (FullMaxClock - TickStart->clockTime) + TickEnd->clockTime; - return INCHI_MSEC(delta); - } - delta = TickStart->clockTime - TickEnd->clockTime; - return -INCHI_MSEC(delta); - } - return 0; /* TickEnd->clockTime == TickStart->clockTime */ - } else { - /* may happen under Win32 only where clock_t is SIGNED long */ - clock_t delta; - FillMaxMinClock( ); - if ( !TickEnd || !TickStart ) - return 0; - if ( TickEnd->clockTime >= 0 && TickStart->clockTime >= 0 || - TickEnd->clockTime <= 0 && TickStart->clockTime <= 0) { - delta = TickEnd->clockTime - TickStart->clockTime; - } else - if ( TickEnd->clockTime >= HalfMaxPositiveClock && - TickStart->clockTime <= HalfMinNegativeClock ) { - /* end is earlier than start */ - delta = (MaxPositiveClock - TickEnd->clockTime) + (TickStart->clockTime - MinNegativeClock); - delta = -delta; - } else - if ( TickEnd->clockTime <= HalfMinNegativeClock && - TickStart->clockTime >= HalfMaxPositiveClock ) { - /* start was earlier than end */ - delta = (MaxPositiveClock - TickStart->clockTime) + (TickEnd->clockTime - MinNegativeClock); - } else { - /* there was no overflow, clock passed zero */ - delta = TickEnd->clockTime - TickStart->clockTime; - } - return INCHI_MSEC(delta); - } -} -/******************* get elapsed time from TickStart ************************/ -long InchiTimeElapsed( inchiTime *TickStart ) -{ - inchiTime TickEnd; - if ( !TickStart ) - return 0; - InchiTimeGet( &TickEnd ); - return InchiTimeMsecDiff( &TickEnd, TickStart ); -} -/******************* add number of milliseconds to time *********************/ -void InchiTimeAddMsec( inchiTime *TickEnd, unsigned long nNumMsec ) -{ - clock_t delta; - if ( !TickEnd ) - return; - if ( FullMaxClock > 0 ) { - /* clock_t is unsigned */ - delta = INCHI_CLOCK_T(nNumMsec); - TickEnd->clockTime += delta; - } else { - /* may happen under Win32 only where clock_t is SIGNED long */ - /* clock_t is unsigned */ - FillMaxMinClock( ); - delta = INCHI_CLOCK_T(nNumMsec); - TickEnd->clockTime += delta; - } -} -/******************* check whether time has expired *********************/ -int bInchiTimeIsOver( inchiTime *TickStart ) -{ - if ( FullMaxClock > 0 ) { - clock_t clockCurrTime; - if ( !TickStart ) - return 0; - clockCurrTime = InchiClock(); - /* clock_t is unsigned */ - if ( TickStart->clockTime > clockCurrTime ) { - if ( TickStart->clockTime > HalfMaxClock && - TickStart->clockTime - clockCurrTime > HalfMaxClock ) { - /* overflow in clockCurrTime, actually clockCurrTime was later */ - return 1; - } - return 0; - } else - if ( TickStart->clockTime < clockCurrTime ) { - if ( clockCurrTime > HalfMaxClock && - clockCurrTime - TickStart->clockTime > HalfMaxClock ) { - /* overflow in TickStart->clockTime, actually TickStart->clockTime was later */ - return 0; - } - return 1; - } - return 0; /* TickStart->clockTime == clockCurrTime */ - } else { - /* may happen under Win32 only where clock_t is SIGNED long */ - clock_t clockCurrTime; - FillMaxMinClock( ); - if ( !TickStart ) - return 0; - clockCurrTime = InchiClock(); - if ( clockCurrTime >= 0 && TickStart->clockTime >= 0 || - clockCurrTime <= 0 && TickStart->clockTime <= 0) { - return (clockCurrTime > TickStart->clockTime); - } else - if ( clockCurrTime >= HalfMaxPositiveClock && - TickStart->clockTime <= HalfMinNegativeClock ) { - /* curr is earlier than start */ - return 0; - } else - if ( clockCurrTime <= HalfMinNegativeClock && - TickStart->clockTime >= HalfMaxPositiveClock ) { - /* start was earlier than curr */ - return 1; - } else { - /* there was no overflow, clock passed zero */ - return (clockCurrTime > TickStart->clockTime); - } - } -} - -#else - -/******** get current process time ****************************************/ -void InchiTimeGet( inchiTime *TickEnd ) -{ - if ( TickEnd ) { - struct _timeb timeb; - _ftime( &timeb ); - TickEnd->clockTime = (unsigned long)timeb.time; - TickEnd->millitime = (long)timeb.millitm; - } -} -/******** returns difference TickEnd - TickStart in milliseconds **********/ -long InchiTimeMsecDiff( inchiTime *TickEnd, inchiTime *TickStart ) -{ - long delta; - if ( !TickEnd || !TickStart ) { - return 0; - } - if ( TickEnd->clockTime >= TickStart->clockTime ) { - delta = (long)(TickEnd->clockTime - TickStart->clockTime); - delta *= 1000; - delta += TickEnd->millitime - TickStart->millitime; - } else { - delta = -(long)(TickStart->clockTime - TickEnd->clockTime); - delta *= 1000; - delta += TickEnd->millitime - TickStart->millitime; - } - return delta; -} -/******************* get elapsed time from TickStart ************************/ -long InchiTimeElapsed( inchiTime *TickStart ) -{ - inchiTime TickEnd; - if ( !TickStart ) - return 0; - InchiTimeGet( &TickEnd ); - return InchiTimeMsecDiff( &TickEnd, TickStart ); -} -/******************* add number of milliseconds to time *********************/ -void InchiTimeAddMsec( inchiTime *TickEnd, unsigned long nNumMsec ) -{ - long delta; - if ( !TickEnd ) - return; - TickEnd->clockTime += nNumMsec / 1000; - delta = nNumMsec % 1000 + TickEnd->millitime; - TickEnd->clockTime += delta / 1000; - TickEnd->millitime = delta % 1000; -} -/******************* check whether time has expired *********************/ -int bInchiTimeIsOver( inchiTime *TickEnd ) -{ - struct _timeb timeb; - if ( !TickEnd ) - return 0; - _ftime( &timeb ); - if ( TickEnd->clockTime > (unsigned long)timeb.time ) - return 0; - if ( TickEnd->clockTime < (unsigned long)timeb.time || - TickEnd->millitime < (long)timeb.millitm ) { - return 1; - } - return 0; -} -#endif - - - - - -#ifndef TARGET_API_LIB - - -#ifndef INCHI_ADD_STR_LEN -#define INCHI_ADD_STR_LEN 32768 -#endif - - -/*^^^ Internal functions */ - -int inchi_ios_str_getc( INCHI_IOSTREAM *ios ); -char *inchi_ios_str_gets( char *szLine, int len, INCHI_IOSTREAM *ios ); -char *inchi_ios_str_getsTab( char *szLine, int len, INCHI_IOSTREAM *ios ); -int GetMaxPrintfLength( const char *lpszFormat, va_list argList); -char *inchi_fgetsTab( char *szLine, int len, FILE *f ); -int inchi_vfprintf( FILE* f, const char* lpszFormat, va_list argList ); - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - - INCHI_IOSTREAM OPERATIONS - - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Init INCHI_IOSTREAM -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void inchi_ios_init(INCHI_IOSTREAM* ios, int io_type, FILE *f) -{ - memset( ios, 0, sizeof(*ios) ); - switch (io_type) - { - case INCHI_IOSTREAM_FILE: ios->type = INCHI_IOSTREAM_FILE; - break; - case INCHI_IOSTREAM_STRING: - default: ios->type = INCHI_IOSTREAM_STRING; - break; - } - ios->f = f; - return; -} - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - If INCHI_IOSTREAM type is INCHI_IOSTREAM_STRING, - flush INCHI_IOSTREAM string buffer to file (if non-NULL); then free buffer. - If INCHI_IOSTREAM type is INCHI_IOSTREAM_FILE, just flush the file. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void inchi_ios_flush(INCHI_IOSTREAM* ios) -{ - if (ios->type == INCHI_IOSTREAM_STRING) - { - if (ios->s.pStr) - { - if (ios->s.nUsedLength > 0) - { - if (ios->f) - { - fprintf(ios->f,"%-s", ios->s.pStr); - fflush(ios->f); - } - inchi_free(ios->s.pStr ); - ios->s.pStr = NULL; - ios->s.nUsedLength = ios->s.nAllocatedLength = ios->s.nPtr = 0; - } - } - } - else if (ios->type == INCHI_IOSTREAM_FILE) - { - /* output to plain file: just flush it. */ - fflush(ios->f); - } - return; -} - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - If INCHI_IOSTREAM type is INCHI_IOSTREAM_STRING, - flush INCHI_IOSTREAM string buffer to file (if non-NULL) and - another file f2 supplied as parameter (typically, it will be stderr); then free buffer. - If INCHI_IOSTREAM type is INCHI_IOSTREAM_FILE, just flush the both files. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void inchi_ios_flush2(INCHI_IOSTREAM* ios, FILE *f2) -{ - if (ios->type == INCHI_IOSTREAM_STRING) - { - if (ios->s.pStr) - { - if (ios->s.nUsedLength > 0) - { - if (ios->f) - { - fprintf(ios->f,"%-s", ios->s.pStr); - fflush(ios->f); - } - if (f2!=ios->f) - fprintf(f2,"%-s", ios->s.pStr); - - inchi_free(ios->s.pStr ); - ios->s.pStr = NULL; - ios->s.nUsedLength = ios->s.nAllocatedLength = ios->s.nPtr = 0; - } - } - } - else if (ios->type == INCHI_IOSTREAM_FILE) - { - /* output to plain file: just flush it. */ - if ( (ios->f) && (ios->f!=stderr) && (ios->f!=stdout) ) - fflush(ios->f); - if ( f2 && f2!=stderr && f2!=stdout) - fflush(f2); - - - } - - return; -} - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Close INCHI_IOSTREAM: free string buffer and close the file. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void inchi_ios_close(INCHI_IOSTREAM* ios) -{ - if (ios->s.pStr) - inchi_free(ios->s.pStr); - ios->s.pStr = NULL; - ios->s.nUsedLength = ios->s.nAllocatedLength = ios->s.nPtr = 0; - if ( (ios->f) && (ios->f!=stderr) && (ios->f!=stdout) && (ios->f!=stdin)) - fclose(ios->f); - return; -} - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Reset INCHI_IOSTREAM: set string buffer ptr to NULL (but do _not_ free memory)and close the file. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void inchi_ios_reset(INCHI_IOSTREAM* ios) -{ - ios->s.pStr = NULL; - ios->s.nUsedLength = ios->s.nAllocatedLength = ios->s.nPtr = 0; - if ( (ios->f) && (ios->f!=stderr) && (ios->f!=stdout) && (ios->f!=stdin)) - fclose(ios->f); - return; -} - - - - - -/*******************************************************************/ -int inchi_ios_str_getc(INCHI_IOSTREAM *ios) -{ - if (ios->type==INCHI_IOSTREAM_STRING) - { - if ( ios->s.nPtr < ios->s.nUsedLength ) - { - return (int)ios->s.pStr[ios->s.nPtr++]; - } - return EOF; - } - else if (ios->type==INCHI_IOSTREAM_FILE) - { - return fgetc( ios->f ); - } - - /* error */ - return EOF; -} - - - -/*******************************************************************/ -char *inchi_ios_str_gets(char *szLine, int len, INCHI_IOSTREAM *f) -{ - int length=0, c=0; - if ( -- len < 0 ) - { - return NULL; - } - while ( length < len && EOF != (c = inchi_ios_str_getc( f )) ) - { - szLine[length++] = (char)c; - if ( c == '\n' ) - break; - } - if ( !length && EOF == c ) - { - return NULL; - } - szLine[length] = '\0'; - return szLine; -} - - - -/********************************************************************************/ -/* read up to len or tab or LF; if empty read next until finds non-empty line */ -/* remove leading and trailing white spaces; keep zero termination */ -/********************************************************************************/ -char *inchi_ios_str_getsTab( char *szLine, int len, INCHI_IOSTREAM *f ) -{ - int length=0, c=0; - if ( --len < 0 ) - { - return NULL; - } - while ( length < len && EOF != (c = inchi_ios_str_getc(f)) ) - { - if ( c == '\t' ) - c = '\n'; - szLine[length++] = (char)c; - if ( c == '\n' ) - break; - } - if ( !length && EOF == c ) - { - return NULL; - } - szLine[length] = '\0'; - return szLine; -} - - -/*******************************************************************/ -int inchi_ios_gets( char *szLine, int len, INCHI_IOSTREAM *f, int *bTooLongLine ) -{ - int length; - char *p; - do - { - p = inchi_ios_str_gets( szLine, len-1, f ); - if ( !p ) - { - *bTooLongLine = 0; - return -1; /* end of file or cannot read */ - } - szLine[len-1] = '\0'; - /* - *bTooLongLine = !strchr( szLine, '\n' ); - */ - p = strchr( szLine, '\n' ); - *bTooLongLine = ( !p && ((int)strlen(szLine)) == len-2 ); - LtrimRtrim( szLine, &length ); - } while ( !length ); - - return length; -} - - -/*******************************************************************/ -/* read up to len or tab or LF; if empty read next until finds non-empty line */ -/* remove leading and trailing white spaces; keep zero termination */ -/*******************************************************************/ -int inchi_ios_getsTab( char *szLine, int len, INCHI_IOSTREAM *f, int *bTooLongLine ) -{ - int length; - char *p; - do - { - p = inchi_ios_str_getsTab( szLine, len-1, f ); - if ( !p ) - { - *bTooLongLine = 0; - return -1; /* end of file or cannot read */ - } - szLine[len-1] = '\0'; - /* - *bTooLongLine = !strchr( szLine, '\n' ); - */ - p = strchr( szLine, '\n' ); - *bTooLongLine = ( !p && ((int)strlen(szLine)) == len-2 ); - LtrimRtrim( szLine, &length ); - } while ( !length ); - return length; -} - -/*******************************************************************/ -int inchi_ios_getsTab1( char *szLine, int len, INCHI_IOSTREAM *f, int *bTooLongLine ) -{ - int length; - char *p; - /*do {*/ - p = inchi_ios_str_getsTab( szLine, len-1, f ); - if ( !p ) - { - *bTooLongLine = 0; - return -1; /* end of file or cannot read */ - } - szLine[len-1] = '\0'; - /* - *bTooLongLine = !strchr( szLine, '\n' ); - */ - p = strchr( szLine, '\n' ); - *bTooLongLine = ( !p && ((int)strlen(szLine)) == len-2 ); - LtrimRtrim( szLine, &length ); - /*} while ( !length );*/ - return length; -} - - - - - - -/*****************************************************************/ -int inchi_ios_print( INCHI_IOSTREAM * ios, const char* lpszFormat, ... ) -{ - if (!ios) return -1; - - if (ios->type == INCHI_IOSTREAM_STRING) - { - - /* output to string buffer */ - - int ret=0, max_len; - va_list argList; - my_va_start( argList, lpszFormat ); - max_len = GetMaxPrintfLength( lpszFormat, argList); - va_end( argList ); - - if ( max_len >= 0 ) - { - if ( ios->s.nAllocatedLength - ios->s.nUsedLength <= max_len ) - { - /* enlarge output string */ - int nAddLength = inchi_max( INCHI_ADD_STR_LEN, max_len ); - char *new_str = - (char *)inchi_calloc( ios->s.nAllocatedLength + nAddLength, sizeof(new_str[0]) ); - if ( new_str ) - { - if ( ios->s.pStr ) - { - if ( ios->s.nUsedLength > 0 ) - memcpy( new_str, ios->s.pStr, sizeof(new_str[0])* ios->s.nUsedLength ); - inchi_free( ios->s.pStr ); - } - ios->s.pStr = new_str; - ios->s.nAllocatedLength += nAddLength; - } - else return -1; /* failed */ - } - /* output */ - my_va_start( argList, lpszFormat ); - ret = vsprintf( ios->s.pStr + ios->s.nUsedLength, lpszFormat, argList ); - va_end(argList); - if ( ret >= 0 ) - ios->s.nUsedLength += ret; - return ret; - } - return -1; - } - - else if (ios->type == INCHI_IOSTREAM_FILE) - { - /* output to file */ - int ret=0, ret2=0; - va_list argList; - if (ios->f) - { - my_va_start( argList, lpszFormat ); - ret = vfprintf( ios->f, lpszFormat, argList ); - va_end( argList ); - } - else - { - my_va_start( argList, lpszFormat ); - ret2 = vfprintf( stdout, lpszFormat, argList ); - va_end( argList ); - } -#ifdef BUILD_LIB_FOR_WINCHI - if( FWPRINT ) - { - my_va_start( argList, lpszFormat ); - FWPRINT( lpszFormat, argList ); - va_end( argList ); - } -#endif - return ret? ret : ret2; - } - - - /* no output */ - return 0; -} - - - - -/**********************************************************************/ -/* This function's output should not be displayed in the output pane */ -/**********************************************************************/ -int inchi_ios_print_nodisplay( INCHI_IOSTREAM * ios, const char* lpszFormat, ... ) -{ - if (!ios) return -1; - - if (ios->type == INCHI_IOSTREAM_STRING) - { - /* output to string buffer */ - int ret=0, max_len; - va_list argList; - my_va_start( argList, lpszFormat ); - max_len = GetMaxPrintfLength( lpszFormat, argList); - va_end( argList ); - - if ( max_len >= 0 ) - { - if ( ios->s.nAllocatedLength - ios->s.nUsedLength <= max_len ) - { - /* enlarge output string */ - int nAddLength = inchi_max( INCHI_ADD_STR_LEN, max_len ); - char *new_str = (char *)inchi_calloc( ios->s.nAllocatedLength + nAddLength, sizeof(new_str[0]) ); - if ( new_str ) - { - if ( ios->s.pStr ) - { - if ( ios->s.nUsedLength > 0 ) - { - memcpy( new_str, ios->s.pStr, sizeof(new_str[0])*ios->s.nUsedLength ); - } - inchi_free( ios->s.pStr ); - } - ios->s.pStr = new_str; - ios->s.nAllocatedLength += nAddLength; - } else - { - return -1; /* failed */ - } - } - /* output */ - my_va_start( argList, lpszFormat ); - ret = vsprintf( ios->s.pStr + ios->s.nUsedLength, lpszFormat, argList ); - va_end(argList); - if ( ret >= 0 ) - { - ios->s.nUsedLength += ret; - } - return ret; - } - return -1; - } - - else if (ios->type == INCHI_IOSTREAM_FILE) - { - /* output to file */ - int ret=0, ret2=0; - va_list argList; - if (ios->f) - { - my_va_start( argList, lpszFormat ); - ret = vfprintf( ios->f, lpszFormat, argList ); - va_end( argList ); - } - else - { - my_va_start( argList, lpszFormat ); - ret2 = vfprintf( stdout, lpszFormat, argList ); - va_end( argList ); - } - return ret? ret : ret2; - } - - /* no output */ - return 0; -} - - - - -/*****************************************************************/ -/* Print to string buffer or to file+stderr */ -int inchi_ios_eprint( INCHI_IOSTREAM * ios, const char* lpszFormat, ... ) -{ -int ret=0, ret2=0; -va_list argList; - - if (!ios) - return -1; - - if (ios->type == INCHI_IOSTREAM_STRING) /* was #if ( defined(TARGET_API_LIB) || defined(BUILD_CINCHI_WITH_INCHIKEY) ) */ - { - /* output to string buffer */ - int max_len, nAddLength = 0; - char *new_str = NULL; - - my_va_start( argList, lpszFormat ); - max_len = GetMaxPrintfLength( lpszFormat, argList); - va_end( argList ); - - if ( max_len >= 0 ) - { - if ( ios->s.nAllocatedLength - ios->s.nUsedLength <= max_len ) - { - /* enlarge output string */ - nAddLength = inchi_max( INCHI_ADD_STR_LEN, max_len ); - new_str = (char *)inchi_calloc( ios->s.nAllocatedLength + nAddLength, sizeof(new_str[0]) ); - if ( new_str ) - { - if ( ios->s.pStr ) - { - if ( ios->s.nUsedLength > 0 ) - { - memcpy( new_str, ios->s.pStr, sizeof(new_str[0])* ios->s.nUsedLength ); - } - inchi_free( ios->s.pStr ); - } - ios->s.pStr = new_str; - ios->s.nAllocatedLength += nAddLength; - } - else - { - return -1; /* failed */ - } - } - - /* output */ - my_va_start( argList, lpszFormat ); - ret = vsprintf( ios->s.pStr + ios->s.nUsedLength, lpszFormat, argList ); - va_end(argList); - if ( ret >= 0 ) - { - ios->s.nUsedLength += ret; - } - return ret; - } - return -1; - } - - else if (ios->type == INCHI_IOSTREAM_FILE) - { - if ( ios->f) - { - /* output to plain file */ - my_va_start( argList, lpszFormat ); - ret = inchi_vfprintf( ios->f, lpszFormat, argList ); - va_end( argList ); -#if ( !defined(TARGET_API_LIB) && !defined(BUILD_LIB_FOR_WINCHI) ) - /*^^^ No output to stderr from within dll or GUI program */ - if ( ios->f != stderr ) - { - my_va_start( argList, lpszFormat ); - ret2 = vfprintf( stderr, lpszFormat, argList ); - va_end( argList ); - } -#endif - return ret? ret : ret2; - } - } - - /* no output */ - return 0; -} - - - - - - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - - PLAIN FILE OPERATIONS - - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -/* Print to file, echoing to stderr */ -int inchi_fprintf( FILE* f, const char* lpszFormat, ... ) -{ -int ret=0, ret2=0; -va_list argList; - if (f) - { - my_va_start( argList, lpszFormat ); - ret = inchi_vfprintf( f, lpszFormat, argList ); - va_end( argList ); -/*^^^ No output to stderr from within dll or GUI program */ -#if ( !defined(TARGET_API_LIB) && !defined(BUILD_LIB_FOR_WINCHI) ) - if ( f != stderr ) - { - my_va_start( argList, lpszFormat ); - ret2 = vfprintf( stderr, lpszFormat, argList ); - va_end( argList ); - } -#endif - return ret? ret : ret2; - } - return 0; -} - - - - -/* Print to file */ -int inchi_vfprintf( FILE* f, const char* lpszFormat, va_list argList ) -{ -int ret=0; - - if ( f == stderr && lpszFormat && lpszFormat[0] && '\r' == lpszFormat[strlen(lpszFormat)-1] ) - { -#define CONSOLE_LINE_LEN 80 -#ifndef COMPILE_ANSI_ONLY - char szLine[CONSOLE_LINE_LEN]; - ret = _vsnprintf( szLine, CONSOLE_LINE_LEN-1, lpszFormat, argList ); - if ( ret < 0 ) - { - /* output is longer than the console line */ - /*^^^ Fixed bug: (CONSOLE_LINE_LEN-4) --> (CONSOLE_LINE_LEN-4-1) 11-22-08 IPl */ - strcpy(szLine+CONSOLE_LINE_LEN-5, "...\r"); - - } - fputs( szLine, f ); -#else - ret = vfprintf( f, lpszFormat, argList ); -#endif -#undef CONSOLE_LINE_LEN - } - else - { - ret = vfprintf( f, lpszFormat, argList ); - } - return ret; -} - - -#if ( FIX_READ_LONG_LINE_BUG == 1 ) -/********************************************************************/ -int inchi_fgetsLfTab( char *szLine, int len, FILE *f ) -{ - int length; - char *p; - char szSkip[256]; - int bTooLongLine = 0; - do { - p = inchi_fgetsTab( szLine, len, f ); - if ( !p ) { - return -1; /* end of file or cannot read */ - } - bTooLongLine = ( (int)strlen(szLine) == len-1 && szLine[len-2] != '\n' ); - LtrimRtrim( szLine, &length ); - } while ( !length ); - if ( bTooLongLine ) { - while ( p = inchi_fgetsTab( szSkip, sizeof(szSkip)-1, f ) ) { - if ( strchr( szSkip, '\n' ) ) - break; - } - } - return length; -} -#else -/********************************************************************/ -int inchi_fgetsLfTab( char *szLine, int len, FILE *f ) -{ - int length; - char *p; - char szSkip[256]; - int bTooLongLine = 0; - do { - p = inchi_fgetsTab( szLine, len-1, f ); - if ( !p ) { - return -1; /* end of file or cannot read */ - } - szLine[len-1] = '\0'; - /* - bTooLongLine = !strchr( szLine, '\n' ); - */ - bTooLongLine = ( !p && ((int)strlen(szLine)) == len-2 ); - LtrimRtrim( szLine, &length ); - } while ( !length ); - if ( bTooLongLine ) { - while ( p = inchi_fgetsTab( szSkip, sizeof(szSkip)-1, f ) ) { - szSkip[sizeof(szSkip)-1] = '\0'; - if ( strchr( szSkip, '\n' ) ) - break; - } - } - return length; -} -#endif - - -/*******************************************************************/ -/* read up to len or tab or LF; if empty read next until finds non-empty line */ -/* remove leading and trailing white spaces; keep zero termination */ -/*******************************************************************/ -char *inchi_fgetsTab( char *szLine, int len, FILE *f ) -{ - int length=0, c=0; - len --; - while ( length < len && EOF != (c = fgetc( f )) ) { - if ( c == '\t' ) - c = '\n'; - szLine[length++] = (char)c; - if ( c == '\n' ) - break; - } - if ( !length && EOF == c ) { - return NULL; - } - szLine[length] = '\0'; - return szLine; -} - - - -/******************************************************************/ -/* read not more than line_len bytes from an lf-terminated line */ -/* if input line is too long quietly ignore the rest of the line */ -char* inchi_fgetsLf( char* line, int line_len, FILE* inp ) -{ - char *p, *q; - memset( line, 0, line_len ); - if ( NULL != (p = fgets( line, line_len, inp ) ) && NULL == strchr(p, '\n' ) ){ - char temp[64]; /* bypass up to '\n' or up to end of file whichever comes first*/ - while ( NULL != fgets( temp, sizeof(temp), inp ) && NULL == strchr(temp,'\n') ) - ; - } - if ( p && (q = strchr(line, '\r')) ) { /* fix CR CR LF line terminator. */ - q[0] = '\n'; - q[1] = '\0'; - } - return p; -} - - - - - - - - - - -/***************************************************************** - * - * Estimate printf string length - * - * The code is based on Microsoft Knowledge Base article Q127038: - * "FIX: CString::Format Gives Assertion Failed, Access Violation" - * (Related to Microsoft Visual C++, 32-bit Editions, versions 2.0, 2.1) - * - *****************************************************************/ - -#define FORCE_ANSI 0x10000 -#define FORCE_UNICODE 0x20000 - -/* formatting (using wsprintf style formatting)*/ -int GetMaxPrintfLength( const char *lpszFormat, va_list argList) -{ - /*ASSERT(AfxIsValidString(lpszFormat, FALSE));*/ - const char * lpsz; - int nMaxLen, nWidth, nPrecision, nModifier, nItemLen; - - nMaxLen = 0; - /* make a guess at the maximum length of the resulting string */ - for ( lpsz = lpszFormat; *lpsz; lpsz ++ ) - { - /* handle '%' character, but watch out for '%%' */ - if (*lpsz != '%' || *( ++ lpsz ) == '%') - { - nMaxLen += 1; - continue; - } - - nItemLen = 0; - - /* handle '%' character with format */ - nWidth = 0; - for (; *lpsz; lpsz ++ ) - { - /* check for valid flags */ - if (*lpsz == '#') - nMaxLen += 2; /* for '0x' */ - else if (*lpsz == '*') - nWidth = va_arg(argList, int); - else if (*lpsz == '-' || *lpsz == '+' || *lpsz == '0' - || *lpsz == ' ') - ; - else /* hit non-flag character */ - break; - } - /* get width and skip it */ - if (nWidth == 0) - { - /* width indicated by */ - nWidth = atoi(lpsz); - for (; *lpsz && isdigit(*lpsz); lpsz ++ ) - ; - } - /*ASSERT(nWidth >= 0);*/ - if ( nWidth < 0 ) - goto exit_error; /* instead of exception */ - - nPrecision = 0; - if (*lpsz == '.') - { - /* skip past '.' separator (width.precision)*/ - lpsz ++; - - /* get precision and skip it*/ - if (*lpsz == '*') - { - nPrecision = va_arg(argList, int); - lpsz ++; - } - else - { - nPrecision = atoi(lpsz); - for (; *lpsz && isdigit(*lpsz); lpsz ++) - ; - } - if ( nPrecision < 0 ) - goto exit_error; /* instead of exception */ - } - - /* should be on type modifier or specifier */ - nModifier = 0; - switch (*lpsz) - { - /* modifiers that affect size */ - case 'h': - switch ( lpsz[1] ) { - case 'd': - case 'i': - case 'o': - case 'x': - case 'X': - case 'u': - /* short unsigned, short double, etc. -- added to the original MS example */ - /* ignore the fact that these modifiers do affect size */ - lpsz ++; - break; - default: - nModifier = FORCE_ANSI; - lpsz ++; - break; - } - break; - case 'l': - switch ( lpsz[1] ) { - case 'd': - case 'i': - case 'o': - case 'x': - case 'X': - case 'u': - case 'f': /* long float -- post ANSI C */ - /* long unsigned, long double, etc. -- added to the original MS example */ - /* ignore the fact that these modifiers do affect size */ - lpsz ++; - break; - default: - /* - nModifier = FORCE_UNICODE; - lpsz ++; - break; - */ - goto exit_error; /* no UNICODE, please */ - } - break; - /* modifiers that do not affect size */ - case 'F': - case 'N': - case 'L': - lpsz ++; - break; - } - - /* now should be on specifier */ - switch (*lpsz | nModifier) - { - /* single characters*/ - case 'c': - case 'C': - nItemLen = 2; - va_arg(argList, int); - break; - case 'c'|FORCE_ANSI: - case 'C'|FORCE_ANSI: - nItemLen = 2; - va_arg(argList, int); - break; - case 'c'|FORCE_UNICODE: - case 'C'|FORCE_UNICODE: - goto exit_error; /* no UNICODE, please */ - /* - nItemLen = 2; - va_arg(argList, wchar_t); - break; - */ - - /* strings*/ - case 's': - case 'S': - nItemLen = strlen(va_arg(argList, char*)); - nItemLen = inchi_max(1, nItemLen); - break; - case 's'|FORCE_ANSI: - case 'S'|FORCE_ANSI: - nItemLen = strlen(va_arg(argList, char*)); - nItemLen = inchi_max(1, nItemLen); - break; - - case 's'|FORCE_UNICODE: - case 'S'|FORCE_UNICODE: - goto exit_error; /* no UNICODE, please */ - /* - nItemLen = wcslen(va_arg(argList, wchar_t*)); - nItemLen = inchi_max(1, nItemLen); - break; - */ - - } - - /* adjust nItemLen for strings */ - if (nItemLen != 0) - { - nItemLen = inchi_max(nItemLen, nWidth); - if (nPrecision != 0) - nItemLen = inchi_min(nItemLen, nPrecision); - } - else - { - switch (*lpsz) - { - /* integers */ - case 'd': - case 'i': - case 'u': - case 'x': - case 'X': - case 'o': - va_arg(argList, int); - nItemLen = 32; - nItemLen = inchi_max(nItemLen, nWidth+nPrecision); - break; - - case 'e': - case 'f': - case 'g': - case 'G': - va_arg(argList, double); - nItemLen = 32; - nItemLen = inchi_max(nItemLen, nWidth+nPrecision); - break; - - case 'p': - va_arg(argList, void*); - nItemLen = 32; - nItemLen = inchi_max(nItemLen, nWidth+nPrecision); - break; - - /* no output */ - case 'n': - va_arg(argList, int*); - break; - - default: - /*ASSERT(FALSE);*/ /* unknown formatting option*/ - goto exit_error; /* instead of exception */ - } - } - - /* adjust nMaxLen for output nItemLen */ - nMaxLen += nItemLen; - } - return nMaxLen; - -exit_error: - return -1; /* wrong format */ -} - - - -#endif diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/e_ichi_parms.h b/INCHI-1-SRC/INCHI_API/inchi_main/e_ichi_parms.h deleted file mode 100644 index e04fc58..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_main/e_ichi_parms.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __ICHI_PARMS_H__ -#define __ICHI_PARMS_H__ - - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - -int e_ReadCommandLineParms( int argc, const char *argv[], INPUT_PARMS *ip, char *szSdfDataValue, - unsigned long *ulDisplTime, int bReleaseVersion, INCHI_IOSTREAM *log_file ); - -int e_PrintInputParms( INCHI_IOSTREAM *log_file, INPUT_PARMS *ip ); -int e_OpenFiles( FILE **inp_file, FILE **output_file, FILE **log_file, FILE **prb_file, INPUT_PARMS *ip ); - -void e_HelpCommandLineParms(INCHI_IOSTREAM *f ); -#ifdef CREATE_INCHI_STEP_BY_STEP -void e_HelpCommandLineParmsReduced(INCHI_IOSTREAM *f ); -#endif - - - - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -#endif /* __ICHI_PARMS_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/e_readmol.c b/INCHI-1-SRC/INCHI_API/inchi_main/e_readmol.c deleted file mode 100644 index 5940387..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_main/e_readmol.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include -#include -#include - -#include "e_mode.h" - -#include "inchi_api.h" - -#include "e_ctl_data.h" - -#include "e_ichisize.h" -#include "e_comdef.h" -#include "e_util.h" -#include "e_readmol.h" -#include "e_ichicomp.h" -#include "e_ichi_io.h" -#include "e_readstru.h" -#include "e_inpdef.h" - -#define AddMOLfileError e_AddMOLfileError -#define remove_one_lf e_remove_one_lf -#define RemoveNonPrintable e_RemoveNonPrintable -#define mystrncpy e_mystrncpy -#define read_mol_file e_read_mol_file -#define bypass_sdf_data_items e_bypass_sdf_data_items -#define extract_ChargeRadical e_extract_ChargeRadical -#define delete_mol_data e_delete_mol_data -#define remove_trailing_spaces e_remove_trailing_spaces -#define normalize_name e_normalize_name -#define read_sdfile_segment e_read_sdfile_segment -#define CopyMOLfile e_CopyMOLfile -#define LtrimRtrim e_LtrimRtrim - -#ifndef inchi_calloc -#define inchi_calloc e_inchi_calloc -#endif - -#ifndef inchi_free -#define inchi_free e_inchi_free -#endif - -#include "lreadmol.h" - diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/e_util.c b/INCHI-1-SRC/INCHI_API/inchi_main/e_util.c deleted file mode 100644 index 319a1e1..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_main/e_util.c +++ /dev/null @@ -1,317 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include - -#include "e_mode.h" -#include "e_ichisize.h" -#include "inchi_api.h" - - -#include "e_comdef.h" -#include "e_util.h" -#include "e_ichicomp.h" - -#define extract_ChargeRadical e_extract_ChargeRadical -#define normalize_name e_normalize_name -/******************************************************************************************************/ -int extract_ChargeRadical( char *elname, int *pnRadical, int *pnCharge ) -{ - char *q, *r, *p; - int nCharge=0, nRad = 0, charge_len = 0, k, nVal, nSign, nLastSign=1, len; - - p = elname; - - /* extract radicals & charges */ - while ( q = strpbrk( p, "+-^" ) ) { - switch ( *q ) { - case '+': - case '-': - for ( k = 0, nVal=0; (nSign = ('+' == q[k])) || (nSign = -('-' == q[k])); k++ ) { - nVal += (nLastSign = nSign); - charge_len ++; - } - if ( nSign = (int)strtol( q+k, &r, 10 ) ) { /* fixed 12-5-2001 */ - nVal += nLastSign * (nSign-1); - } - charge_len = r - q; - nCharge += nVal; - break; - /* case '.': */ /* singlet '.' may be confused with '.' in formulas like CaO.H2O */ - case '^': - nRad = 1; /* doublet here is 1. See below */ - charge_len = 1; - for ( k = 1; q[0] == q[k]; k++ ) { - nRad ++; - charge_len ++; - } - break; - } - memmove( q, q+charge_len, strlen(q+charge_len)+1 ); - } - len = strlen(p); - /* radical */ - if ( (q = strrchr( p, ':' )) && !q[1]) { - nRad = RADICAL_SINGLET; - q[0] = '\0'; - len --; - } else { - while( (q = strrchr( p, '.' )) && !q[1] ) { - nRad ++; - q[0] = '\0'; - len --; - } - - nRad = nRad == 1? RADICAL_DOUBLET : - nRad == 2? RADICAL_TRIPLET : 0; - } - *pnRadical = nRad; - *pnCharge = nCharge; - return ( nRad || nCharge ); - -} -/*****************************************************************/ -int normalize_name( char* name ) -{ - /* remove leading & trailing spaces; replace consecutive spaces with a single space */ - /* Treat non-printable characters (Greeks) as spaces. 11-23-99 DCh. */ - int i, len, n; - len = (int)strlen(name); - for ( i = 0, n = 0; i < len; i++ ) { - if ( isspace( UCINT name[i] ) /*|| !isprint( UCINT name[i] )*/ ) { - name[i] = ' '; /* exterminate tabs !!! */ - n++; - } else { - if ( n > 0 ) { - memmove( (void*) &name[i-n], (void*) &name[i], len-i+1 ); - i -= n; - len -= n; - } - n = -1; - } - } - if ( n == len ) /* empty line */ - name[len=0] = '\0'; - else - if ( ++n && n <= len ) { - len -= n; - name[len] = '\0'; - } - return len; -} -/************************************************/ -#ifndef e_inchi_malloc -void *e_inchi_malloc(size_t c) -{ - return malloc(c); -} -#endif -#ifndef e_inchi_calloc -void *e_inchi_calloc(size_t c, size_t n) -{ - return calloc(c,n); -} -#endif -#ifndef e_inchi_free -void e_inchi_free(void *p) -{ - if(p) { - free(p); /*added check if zero*/ - } -} -#endif - - -/***************************************************************************/ -/* Copies up to maxlen characters INCLUDING end null from source to target */ -/* Fills out the rest of the target with null bytes */ -int e_mystrncpy(char *target,const char *source,unsigned maxlen) -{ /* protected from non-zero-terminated source and overlapped target/source. 7-9-99 DCh. */ - const char *p; - unsigned len; - - if (target==NULL || maxlen == 0 || source == NULL) - return 0; - if ( p = (const char*)memchr(source, 0, maxlen) ) { - len = p-source; /* maxlen does not include the found zero termination */ - } else { - len = maxlen-1; /* reduced length does not include one more byte for zero termination */ - } - if ( len ) - memmove( target, source, len ); - /* target[len] = '\0'; */ - memset( target+len, 0, maxlen-len); /* zero termination */ - return 1; -} -/************************************************************************/ -/* Remove leading and trailing white spaces */ -char* e_LtrimRtrim( char *p, int* nLen ) -{ - int i, len=0; - if ( p && (len = strlen( p )) ) { - for ( i = 0; i < len && __isascii( p[i] ) && isspace( p[i] ); i++ ) - ; - if ( i ) - (memmove)( p, p+i, (len -= i)+1 ); - for ( ; 0 < len && __isascii( p[len-1] ) && isspace( p[len-1] ); len-- ) - ; - p[len] = '\0'; - } - if ( nLen ) - *nLen = len; - return p; -} - -/*************************************************************************/ -void e_remove_trailing_spaces( char* p ) -{ - int len; - for( len = (int)strlen( p ) - 1; len >= 0 && isspace( UCINT p[len] ); len-- ) - ; - p[++len] = '\0'; -} -/*************************************************************************/ -void e_remove_one_lf( char* p) -{ - size_t len; - if ( p && 0 < (len = strlen(p)) && p[len-1] == '\n' ){ - p[len-1] = '\0'; - if ( len >= 2 && p[len-2] == '\r' ) - p[len-2] = '\0'; - } -} -/*************************************************************************/ -S_SHORT *e_is_in_the_slist( S_SHORT *pathAtom, S_SHORT nNextAtom, int nPathLen ) -{ - for ( ; nPathLen && *pathAtom != nNextAtom; nPathLen--, pathAtom++ ) - ; - return nPathLen? pathAtom : NULL; -} -/************************************************/ -int e_is_element_a_metal( char szEl[] ) -{ - static char szMetals[] = "K;V;Y;W;U;" - "Li;Be;Na;Mg;Al;Ca;Sc;Ti;Cr;Mn;Fe;Co;Ni;Cu;Zn;Ga;Rb;Sr;Zr;" - "Nb;Mo;Tc;Ru;Rh;Pd;Ag;Cd;In;Sn;Sb;Cs;Ba;La;Ce;Pr;Nd;Pm;Sm;" - "Eu;Gd;Tb;Dy;Ho;Er;Tm;Yb;Lu;Hf;Ta;Re;Os;Ir;Pt;Au;Hg;Tl;Pb;" - "Bi;Po;Fr;Ra;Ac;Th;Pa;Np;Pu;Am;Cm;Bk;Cf;Es;Fm;Md;No;Lr;Rf;"; - int len = strlen(szEl); - char *p; - - if ( 0 < len && len <= 2 && - isalpha( UCINT szEl[0] ) && isupper( szEl[0] ) && - (p = strstr(szMetals, szEl) ) && p[len] == ';' ) { - - return 1; /*return AtType_Metal;*/ - } - return 0; -} - -#ifdef COMPILE_ANSI_ONLY -/*************************************************************************/ -/************* non-ANSI functions ****************/ -/*************************************************************************/ -#define __MYTOLOWER(c) ( ((c) >= 'A') && ((c) <= 'Z') ? ((c) - 'A' + 'a') : (c) ) - -#if ( defined(COMPILE_ADD_NON_ANSI_FUNCTIONS) || defined(__STDC__) && __STDC__ == 1 ) -/* support (VC++ Language extensions) = OFF && defined(COMPILE_ANSI_ONLY) */ -#ifdef BUILD_LINK_AS_DLL -/* the following code is enabled if (ANSI C) */ -/* because the InChI library dll does not export it */ -int memicmp ( const void * p1, const void * p2, size_t length ) -{ - const U_CHAR *s1 = (const U_CHAR*)p1; - const U_CHAR *s2 = (const U_CHAR*)p2; - while ( length-- ) { - if ( *s1 == *s2 || - __MYTOLOWER( (int)*s1 ) == __MYTOLOWER( (int)*s2 )) { - s1++; - s2++; - } else { - return __MYTOLOWER( (int)*s1 ) - __MYTOLOWER( (int)*s2 ); - } - } - return 0; -} -/*************************************************************************/ -int stricmp( const char *s1, const char *s2 ) -{ - while ( *s1 ) { - if ( *s1 == *s2 || - __MYTOLOWER( (int)*s1 ) == __MYTOLOWER( (int)*s2 )) { - s1 ++; - s2 ++; - } else { - return __MYTOLOWER( (int)*s1 ) - __MYTOLOWER( (int)*s2 ); - } - } - if ( *s2 ) - return -1; - return 0; -} -/*************************************************************************/ -char *_strnset( char *s, int val, size_t length ) -{ - char *ps = s; - while (length-- && *ps) - *ps++ = (char)val; - return s; -} -/*************************************************************************/ -char *_strdup( const char *string ) -{ - char *p = NULL; - if ( string ) { - size_t length = strlen( string ); - p = (char *)e_inchi_malloc( length + 1 ); - if ( p ) { - strcpy( p, string ); - } - } - return p; -} -#endif /* BUILD_LINK_AS_DLL */ -#endif /* !defined(_MSC_VER) || defined(__STDC__) && __STDC__ == 1 */ -#endif /* COMPILE_ANSI_ONLY */ - diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/ichitime.h b/INCHI-1-SRC/INCHI_API/inchi_main/ichitime.h deleted file mode 100644 index 92bad0f..0000000 --- a/INCHI-1-SRC/INCHI_API/inchi_main/ichitime.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __ICHITIME_H__ -#define __ICHITIME_H__ - - -#ifdef COMPILE_ANSI_ONLY - -#ifdef __FreeBSD__ -#include -#endif - -/* get times() */ -#ifdef INCHI_USETIMES -#include -#endif - -/*#include */ - -#include - -typedef struct tagInchiTime { - clock_t clockTime; -} inchiTime; - -#else - -/* Win32 _ftime(): */ -#include - -typedef struct tagInchiTime { - unsigned long clockTime; /* Time in seconds since midnight (00:00:00), January 1, 1970; - signed long overflow expected in 2038 */ - long millitime; /* milliseconds */ - -} inchiTime; - -#endif - - -#ifdef TARGET_EXE_USING_API - -#define InchiTimeGet e_InchiTimeGet -#define InchiTimeMsecDiff e_InchiTimeMsecDiff -#define InchiTimeAddMsec e_InchiTimeAddMsec -#define bInchiTimeIsOver e_bInchiTimeIsOver -#define InchiTimeElapsed e_InchiTimeElapsed - -#define FullMaxClock e_FullMaxClock -#define HalfMaxClock e_HalfMaxClock -#define MaxPositiveClock e_MaxPositiveClock -#define MinNegativeClock e_MinNegativeClock -#define HalfMaxPositiveClock e_HalfMaxPositiveClock -#define HalfMinNegativeClock e_HalfMinNegativeClock - - - -#endif - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - -void InchiTimeGet( inchiTime *TickEnd ); -long InchiTimeMsecDiff( inchiTime *TickEnd, inchiTime *TickStart ); -void InchiTimeAddMsec( inchiTime *TickEnd, unsigned long nNumMsec ); -int bInchiTimeIsOver( inchiTime *TickEnd ); -long InchiTimeElapsed( inchiTime *TickStart ); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /* __ICHITIME_H__ */ - diff --git a/INCHI-1-SRC/INCHI_API/libinchi/gcc/libinchi.map b/INCHI-1-SRC/INCHI_API/libinchi/gcc/libinchi.map new file mode 100644 index 0000000..323dc85 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/libinchi/gcc/libinchi.map @@ -0,0 +1,4 @@ +{ +global: CheckINCHI; CheckINCHIKey; FreeINCHI; FreeStdINCHI; FreeStructFromINCHI; FreeStructFromStdINCHI; Free_inchi_Input; Free_std_inchi_Input; FreeStructFromINCHIEx; GetINCHI; GetINCHIKeyFromINCHI; GetINCHIfromINCHI; GetStdINCHI; GetStdINCHIKeyFromStdINCHI; GetStringLength; GetStructFromINCHI; GetStructFromStdINCHI; Get_inchi_Input_FromAuxInfo; Get_std_inchi_Input_FromAuxInfo; GetINCHIEx; GetStructFromINCHIEx; INCHIGEN_Create; INCHIGEN_Destroy; INCHIGEN_DoCanonicalization; INCHIGEN_DoNormalization; INCHIGEN_DoSerialization; INCHIGEN_Reset; INCHIGEN_Setup; STDINCHIGEN_Create; STDINCHIGEN_Destroy; STDINCHIGEN_DoCanonicalization; STDINCHIGEN_DoNormalization; STDINCHIGEN_DoSerialization; STDINCHIGEN_Reset; STDINCHIGEN_Setup; MakeINCHIFromMolfileText; IXA_STATUS_Create; IXA_STATUS_Clear; IXA_STATUS_Destroy; IXA_STATUS_HasError; IXA_STATUS_HasWarning; IXA_STATUS_GetCount; IXA_STATUS_GetSeverity; IXA_STATUS_GetMessage; IXA_MOL_Create; IXA_MOL_Clear; IXA_MOL_Destroy; IXA_MOL_ReadMolfile; IXA_MOL_ReadInChI; IXA_MOL_SetChiral; IXA_MOL_GetChiral; IXA_MOL_CreateAtom; IXA_MOL_SetAtomElement; IXA_MOL_SetAtomAtomicNumber; IXA_MOL_SetAtomMass; IXA_MOL_SetAtomCharge; IXA_MOL_SetAtomRadical; IXA_MOL_SetAtomHydrogens; IXA_MOL_SetAtomX; IXA_MOL_SetAtomY; IXA_MOL_SetAtomZ; IXA_MOL_CreateBond; IXA_MOL_SetBondType; IXA_MOL_SetBondWedge; IXA_MOL_SetDblBondConfig; IXA_MOL_CreateStereoTetrahedron; IXA_MOL_CreateStereoRectangle; IXA_MOL_CreateStereoAntiRectangle; IXA_MOL_SetStereoParity; IXA_MOL_GetNumAtoms; IXA_MOL_GetNumBonds; IXA_MOL_GetAtomId; IXA_MOL_GetBondId; IXA_MOL_GetAtomIndex; IXA_MOL_GetBondIndex; IXA_MOL_GetAtomNumBonds; IXA_MOL_GetAtomBond; IXA_MOL_GetCommonBond; IXA_MOL_GetBondAtom1; IXA_MOL_GetBondAtom2; IXA_MOL_GetAtomElement; IXA_MOL_GetAtomAtomicNumber; IXA_MOL_GetAtomMass; IXA_MOL_GetAtomCharge; IXA_MOL_GetAtomRadical; IXA_MOL_GetAtomHydrogens; IXA_MOL_GetAtomX; IXA_MOL_GetAtomY; IXA_MOL_GetAtomZ; IXA_MOL_GetBondType; IXA_MOL_GetBondWedge; IXA_MOL_GetDblBondConfig; IXA_MOL_GetNumStereos; IXA_MOL_GetStereoId; IXA_MOL_GetStereoIndex; IXA_MOL_GetStereoTopology; IXA_MOL_GetStereoCentralAtom; IXA_MOL_GetStereoCentralBond; IXA_MOL_GetStereoNumVertices; IXA_MOL_GetStereoVertex; IXA_MOL_GetStereoParity; IXA_INCHIBUILDER_Create; IXA_INCHIBUILDER_SetMolecule; IXA_INCHIBUILDER_GetInChI; IXA_INCHIBUILDER_GetInChIEx; IXA_INCHIBUILDER_GetAuxInfo; IXA_INCHIBUILDER_GetLog; IXA_INCHIBUILDER_Destroy; IXA_INCHIBUILDER_SetOption; IXA_INCHIBUILDER_SetOption_Stereo; IXA_INCHIBUILDER_SetOption_Timeout; IXA_INCHIKEYBUILDER_Create; IXA_INCHIKEYBUILDER_SetInChI; IXA_INCHIKEYBUILDER_GetInChIKey; IXA_INCHIKEYBUILDER_Destroy;local: *; +}; + diff --git a/INCHI-1-SRC/INCHI_API/libinchi/gcc/makefile b/INCHI-1-SRC/INCHI_API/libinchi/gcc/makefile new file mode 100644 index 0000000..53d741d --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/libinchi/gcc/makefile @@ -0,0 +1,196 @@ +# Comment out the next line to create so/dll only +#CREATE_MAIN = 1 +# Comment out the next line to create mol2inchi executable (otherwise, inchi_maiin is created) +#CALLER_IS_MOL2INCHI = 1 +# or define ISLINUX in command line: make ISLINUX=1 +ISLINUX = 1 +# Linux fpic option: replace -fPIC with -fpic if the latter works +# Comment out "LINUX_Z_RELRO =" if -z relro is not supported +# These options are needed to avoid the following SELinux message: +# "Error: cannot restore segment prot after reloc: Permission denied" +# In addition, inchi.map restricts set of expoorted from .so +# functions to those which belong to InChI API +LINUX_MAP = ,--version-script=libinchi.map +ifdef ISLINUX +LINUX_FPIC = -fPIC +LINUX_Z_RELRO = ,-z,relro +endif +# === version === +MAIN_VERSION = .1 +VERSION = $(MAIN_VERSION).05.00 +# === executable & library directory === +ifndef LIB_DIR + LIB_DIR = ../../bin/Linux +endif +# === InChI Library name === +ifndef INCHI_LIB_NAME + INCHI_LIB_NAME = libinchi +endif +INCHI_LIB_PATHNAME = $(LIB_DIR)/$(INCHI_LIB_NAME) +# === Main program name ==== +ifndef API_CALLER_NAME + ifndef CALLER_IS_MOL2INCHI + API_CALLER_NAME = inchi_main$ + else + API_CALLER_NAME = mol2inchi$ + endif +endif +API_CALLER_PATHNAME = $(LIB_DIR)/$(API_CALLER_NAME) +# === Linker to create (Shared) InChI library ==== +ifndef SHARED_LINK + SHARED_LINK = gcc -shared +endif +# === Linker to create Main program ===== +ifndef LINKER + ifdef ISLINUX + LINKER_CWD_PATH = -Wl,-R,"" + endif + LINKER = gcc -s $(LINKER_CWD_PATH) +endif +ifndef P_LIBR + P_LIBR = ../../libinchi/src/ +endif +ifndef P_LIBR_IXA + P_LIBR_IXA = ../../libinchi/src/ixa/ +endif +ifndef P_BASE + P_BASE = ../../../INCHI_BASE/src/ +endif +ifndef P_MAIN + ifndef CALLER_IS_MOL2INCHI + P_MAIN = ../src/ + else + P_MAIN = ../src/ + endif +endif +# === C Compiler =============== +ifndef C_COMPILER + C_COMPILER = gcc +endif +# === C Compiler Options ======= +ifndef C_OPTIONS + ifndef CALLER_IS_MOL2INCHI + C_OPTIONS = -ansi -O3 -c + else + C_OPTIONS = -O3 -c + endif + ifdef ISLINUX + ifndef C_SO_OPTIONS + C_SO_OPTIONS = $(LINUX_FPIC) -DTARGET_API_LIB -DCOMPILE_ANSI_ONLY + endif + endif + ifndef C_MAIN_OPTIONS + C_MAIN_OPTIONS = -DBUILD_LINK_AS_DLL -DTARGET_EXE_USING_API + endif +endif +ifdef CREATE_MAIN +ifndef CALLER_IS_MOL2INCHI +API_CALLER_SRCS = $(P_MAIN)e_0dstereo.c \ +$(P_MAIN)e_ichimain.c \ +$(P_MAIN)e_ichi_io.c \ +$(P_MAIN)e_ichi_parms.c \ +$(P_MAIN)e_inchi_atom.c \ +$(P_MAIN)e_mol2atom.c \ +$(P_MAIN)e_readinch.c \ +$(P_MAIN)e_readmol.c \ +$(P_MAIN)e_readstru.c \ +$(P_MAIN)e_util.c \ +$(P_MAIN)e_ichimain_a.c +API_CALLER_OBJS = e_0dstereo.o \ +e_ichimain.o \ +e_ichi_io.o \ +e_ichi_parms.o \ +e_inchi_atom.o \ +e_mol2atom.o \ +e_readinch.o \ +e_readmol.o \ +e_readstru.o \ +e_util.o \ +e_ichimain_a.o +else +API_CALLER_SRCS = $(P_MAIN)mol2inchi.c \ +$(P_MAIN)getcputime.c \ +$(P_MAIN)moreutil.c +API_CALLER_OBJS = mol2inchi.o \ +getcputime.o \ +moreutil.o +endif +# === InChI Main Link rule ================ +$(API_CALLER_PATHNAME) : $(API_CALLER_OBJS) $(INCHI_LIB_PATHNAME).so$(VERSION) + $(LINKER) -o $(API_CALLER_PATHNAME) $(API_CALLER_OBJS) \ + $(INCHI_LIB_PATHNAME).so$(VERSION) -lm +# === InChI Main compile rule ============ +%.o: $(P_MAIN)%.c + $(C_COMPILER) $(C_MAIN_OPTIONS) $(C_OPTIONS) $< +endif +# === InChI Library Object files ============ +INCHI_LIB_OBJS = ichican2.o \ +ichicano.o \ +ichi_io.o \ +ichierr.o \ +ichicans.o \ +ichiisot.o \ +ichilnct.o \ +ichimak2.o \ +ichimake.o \ +ichimap1.o \ +ichimap2.o \ +ichimap4.o \ +ichinorm.o \ +ichiparm.o \ +ichiprt1.o \ +ichiprt2.o \ +ichiprt3.o \ +ichiqueu.o \ +ichiring.o \ +ichisort.o \ +ichister.o \ +ichitaut.o \ +ichi_bns.o \ +inchi_dll.o \ +ichiread.o \ +ichirvr1.o \ +ichirvr2.o \ +ichirvr3.o \ +ichirvr4.o \ +ichirvr5.o \ +ichirvr6.o \ +ichirvr7.o \ +ikey_dll.o \ +ikey_base26.o \ +inchi_dll_main.o \ +inchi_dll_a.o \ +inchi_dll_a2.o \ +inchi_dll_b.o \ +ixa_inchikey_builder.o \ +ixa_read_mol.o \ +ixa_status.o \ +ixa_builder.o \ +ixa_mol.o \ +ixa_read_inchi.o \ +mol_fmt1.o \ +mol_fmt2.o \ +mol_fmt3.o \ +mol2atom.o \ +mol_fmt4.o \ +readinch.o \ +runichi.o \ +runichi2.o \ +runichi3.o \ +runichi4.o \ +sha2.o \ +strutil.o \ +util.o +# === InChI Library link rule ========= +$(INCHI_LIB_PATHNAME).so$(VERSION): $(INCHI_LIB_OBJS) + $(SHARED_LINK) $(SHARED_LINK_PARM) -o $(INCHI_LIB_PATHNAME).so$(VERSION) \ +$(INCHI_LIB_OBJS) -Wl$(LINUX_MAP)$(LINUX_Z_RELRO),-soname,$(INCHI_LIB_NAME).so$(MAIN_VERSION) -lm + ln -fs $(INCHI_LIB_NAME).so$(VERSION) \ +$(INCHI_LIB_PATHNAME).so$(MAIN_VERSION) +# === InChI Library compile rule ========= +%.o: $(P_LIBR)%.c + $(C_COMPILER) $(C_SO_OPTIONS) $(C_OPTIONS) $< +%.o: $(P_LIBR_IXA)%.c + $(C_COMPILER) $(C_SO_OPTIONS) $(C_OPTIONS) $< +%.o: $(P_BASE)%.c + $(C_COMPILER) $(C_SO_OPTIONS) $(C_OPTIONS) $< diff --git a/INCHI-1-SRC/INCHI_API/libinchi/gcc/makefile32 b/INCHI-1-SRC/INCHI_API/libinchi/gcc/makefile32 new file mode 100644 index 0000000..f0d5efe --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/libinchi/gcc/makefile32 @@ -0,0 +1,199 @@ +# This will create 32-bit executable under 64-bit Linux +# +# +# Comment out the next line to create so/dll only +#CREATE_MAIN = 1 +# Comment out the next line to create mol2inchi executable (otherwise, inchi_maiin is created) +#CALLER_IS_MOL2INCHI = 1 +# or define ISLINUX in command line: make ISLINUX=1 +ISLINUX = 1 +# Linux fpic option: replace -fPIC with -fpic if the latter works +# Comment out "LINUX_Z_RELRO =" if -z relro is not supported +# These options are needed to avoid the following SELinux message: +# "Error: cannot restore segment prot after reloc: Permission denied" +# In addition, inchi.map restricts set of expoorted from .so +# functions to those which belong to InChI API +LINUX_MAP = ,--version-script=libinchi.map +ifdef ISLINUX +LINUX_FPIC = -fPIC +LINUX_Z_RELRO = ,-z,relro +endif +# === version === +MAIN_VERSION = .1 +VERSION = $(MAIN_VERSION).05.00 +# === executable & library directory === +ifndef LIB_DIR + LIB_DIR = ../../bin/Linux/32bit +endif +# === InChI Library name === +ifndef INCHI_LIB_NAME + INCHI_LIB_NAME = libinchi +endif +INCHI_LIB_PATHNAME = $(LIB_DIR)/$(INCHI_LIB_NAME) +# === Main program name ==== +ifndef API_CALLER_NAME + ifndef CALLER_IS_MOL2INCHI + API_CALLER_NAME = inchi_main$ + else + API_CALLER_NAME = mol2inchi$ + endif +endif +API_CALLER_PATHNAME = $(LIB_DIR)/$(API_CALLER_NAME) +# === Linker to create (Shared) InChI library ==== +ifndef SHARED_LINK + SHARED_LINK = gcc -shared +endif +# === Linker to create Main program ===== +ifndef LINKER + ifdef ISLINUX + LINKER_CWD_PATH = -Wl,-R,"" + endif + LINKER = gcc -s $(LINKER_CWD_PATH) +endif +ifndef P_LIBR + P_LIBR = ../../libinchi/src/ +endif +ifndef P_LIBR_IXA + P_LIBR_IXA = ../../libinchi/src/ixa/ +endif +ifndef P_BASE + P_BASE = ../../../INCHI_BASE/src/ +endif +ifndef P_MAIN + ifndef CALLER_IS_MOL2INCHI + P_MAIN = ../src/ + else + P_MAIN = ../src/ + endif +endif +# === C Compiler =============== +ifndef C_COMPILER + C_COMPILER = gcc +endif +# === C Compiler Options ======= +ifndef C_OPTIONS + ifndef CALLER_IS_MOL2INCHI + C_OPTIONS = -m32 -ansi -O3 -c + else + C_OPTIONS = -m32 -O3 -c + endif + ifdef ISLINUX + ifndef C_SO_OPTIONS + C_SO_OPTIONS = $(LINUX_FPIC) -DTARGET_API_LIB -DCOMPILE_ANSI_ONLY + endif + endif + ifndef C_MAIN_OPTIONS + C_MAIN_OPTIONS = -DBUILD_LINK_AS_DLL -DTARGET_EXE_USING_API + endif +endif +ifdef CREATE_MAIN +ifndef CALLER_IS_MOL2INCHI +API_CALLER_SRCS = $(P_MAIN)e_0dstereo.c \ +$(P_MAIN)e_ichimain.c \ +$(P_MAIN)e_ichi_io.c \ +$(P_MAIN)e_ichi_parms.c \ +$(P_MAIN)e_inchi_atom.c \ +$(P_MAIN)e_mol2atom.c \ +$(P_MAIN)e_readinch.c \ +$(P_MAIN)e_readmol.c \ +$(P_MAIN)e_readstru.c \ +$(P_MAIN)e_util.c \ +$(P_MAIN)e_ichimain_a.c +API_CALLER_OBJS = e_0dstereo.o \ +e_ichimain.o \ +e_ichi_io.o \ +e_ichi_parms.o \ +e_inchi_atom.o \ +e_mol2atom.o \ +e_readinch.o \ +e_readmol.o \ +e_readstru.o \ +e_util.o \ +e_ichimain_a.o +else +API_CALLER_SRCS = $(P_MAIN)mol2inchi.c \ +$(P_MAIN)getcputime.c \ +$(P_MAIN)moreutil.c +API_CALLER_OBJS = mol2inchi.o \ +getcputime.o \ +moreutil.o +endif +# === InChI Main Link rule ================ +$(API_CALLER_PATHNAME) : $(API_CALLER_OBJS) $(INCHI_LIB_PATHNAME).so$(VERSION) + $(LINKER) -m32 -o $(API_CALLER_PATHNAME) $(API_CALLER_OBJS) \ + $(INCHI_LIB_PATHNAME).so$(VERSION) -lm +# === InChI Main compile rule ============ +%.o: $(P_MAIN)%.c + $(C_COMPILER) $(C_MAIN_OPTIONS) $(C_OPTIONS) $< +endif +# === InChI Library Object files ============ +INCHI_LIB_OBJS = ichican2.o \ +ichicano.o \ +ichi_io.o \ +ichierr.o \ +ichicans.o \ +ichiisot.o \ +ichilnct.o \ +ichimak2.o \ +ichimake.o \ +ichimap1.o \ +ichimap2.o \ +ichimap4.o \ +ichinorm.o \ +ichiparm.o \ +ichiprt1.o \ +ichiprt2.o \ +ichiprt3.o \ +ichiqueu.o \ +ichiring.o \ +ichisort.o \ +ichister.o \ +ichitaut.o \ +ichi_bns.o \ +inchi_dll.o \ +ichiread.o \ +ichirvr1.o \ +ichirvr2.o \ +ichirvr3.o \ +ichirvr4.o \ +ichirvr5.o \ +ichirvr6.o \ +ichirvr7.o \ +ikey_dll.o \ +ikey_base26.o \ +inchi_dll_main.o \ +inchi_dll_a.o \ +inchi_dll_a2.o \ +inchi_dll_b.o \ +ixa_inchikey_builder.o \ +ixa_read_mol.o \ +ixa_status.o \ +ixa_builder.o \ +ixa_mol.o \ +ixa_read_inchi.o \ +mol_fmt1.o \ +mol_fmt2.o \ +mol_fmt3.o \ +mol2atom.o \ +mol_fmt4.o \ +readinch.o \ +runichi.o \ +runichi2.o \ +runichi3.o \ +runichi4.o \ +sha2.o \ +strutil.o \ +util.o +# === InChI Library link rule ========= +$(INCHI_LIB_PATHNAME).so$(VERSION): $(INCHI_LIB_OBJS) + $(SHARED_LINK) $(SHARED_LINK_PARM) -m32 -o $(INCHI_LIB_PATHNAME).so$(VERSION) \ +$(INCHI_LIB_OBJS) -Wl$(LINUX_MAP)$(LINUX_Z_RELRO),-soname,$(INCHI_LIB_NAME).so$(MAIN_VERSION) -lm + ln -fs $(INCHI_LIB_NAME).so$(VERSION) \ +$(INCHI_LIB_PATHNAME).so$(MAIN_VERSION) +# === InChI Library compile rule ========= +%.o: $(P_LIBR)%.c + $(C_COMPILER) $(C_SO_OPTIONS) $(C_OPTIONS) $< +%.o: $(P_LIBR_IXA)%.c + $(C_COMPILER) $(C_SO_OPTIONS) $(C_OPTIONS) $< +%.o: $(P_BASE)%.c + $(C_COMPILER) $(C_SO_OPTIONS) $(C_OPTIONS) $< diff --git a/INCHI-1-SRC/INCHI_API/libinchi/gcc/run_make_on_linux.sh b/INCHI-1-SRC/INCHI_API/libinchi/gcc/run_make_on_linux.sh new file mode 100644 index 0000000..a6d09d8 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/libinchi/gcc/run_make_on_linux.sh @@ -0,0 +1,2 @@ +#!/bin/sh +make ISLINUX=1 diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichilnct.c b/INCHI-1-SRC/INCHI_API/libinchi/src/ichilnct.c similarity index 79% rename from INCHI-1-SRC/INCHI_API/inchi_dll/ichilnct.c rename to INCHI-1-SRC/INCHI_API/libinchi/src/ichilnct.c index 4055253..82324ab 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichilnct.c +++ b/INCHI-1-SRC/INCHI_API/libinchi/src/ichilnct.c @@ -1,348 +1,348 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include -#include -#include - -/* for use in the InChI library */ - -#include "mode.h" - -#include "util.h" -#include "ichierr.h" -#include "ichicomp.h" -#include "ichi_io.h" -#include "inchi_api.h" - - - - -#define INChIToInchi_Atom l_INChIToInchi_Atom - -int INChIToInchi_Input( INCHI_IOSTREAM *inp_molfile, inchi_Input *orig_at_data, int bMergeAllInputStructures, - int bDoNotAddH, int vABParityUnknown, INPUT_TYPE nInputType, - char *pSdfLabel, char *pSdfValue, long *lSdfId, INCHI_MODE *pInpAtomFlags, - int *err, char *pStrErr ); - -/* This contains executable code. Included in lReadAux.c, e_ReadINCH.c, ReadINCH.c, */ -#include "aux2atom.h" - -extern int bLibInchiSemaphore; - - - -/*****************************************************************************************************/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL Get_std_inchi_Input_FromAuxInfo - ( char *szInchiAuxInfo, - int bDoNotAddH, - InchiInpData *pInchiInp ) -{ - int bDiffUnkUndfStereo = 0; - return Get_inchi_Input_FromAuxInfo( szInchiAuxInfo, bDoNotAddH, bDiffUnkUndfStereo, - pInchiInp ); -} - -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL Get_inchi_Input_FromAuxInfo(char *szInchiAuxInfo, - int bDoNotAddH, int bDiffUnkUndfStereo, - InchiInpData *pInchiInp ) -{ - INCHI_IOSTREAM inp; - int num_at, nRet = inchi_Ret_OKAY, err = 0; - INCHI_MODE bChiral = 0; - /* the input string may contain the following line: "Structure NNN. HHH=VVV" */ - long lNumber; /* structure number NNN from the input */ - char szHeader[MAX_SDF_HEADER]; /* stucture label header HHH from the input */ - char szLabel[MAX_SDF_VALUE]; /* stucture label VVV from the input */ - - /* vABParityUnknown holds actual value of an internal constant signifying */ - /* unknown parity: either the same as for undefined parity (default==standard) */ - /* or a specific one (non-std; requested by SLUUD switch). */ - int vABParityUnknown = AB_PARITY_UNDF; - if ( 0 != bDiffUnkUndfStereo ) - { - /* Make labels for unknown and undefined stereo different */ - vABParityUnknown = AB_PARITY_UNKN; - } - - - if ( bLibInchiSemaphore ) { /* does not work properly under sufficient stress */ - return inchi_Ret_BUSY; - } - bLibInchiSemaphore = 1; - - if ( pInchiInp && pInchiInp->pInp ) { - /* clear output fields */ - inchi_Input *pInp = pInchiInp->pInp; - char *szOptions = pInp->szOptions; - memset( pInchiInp, 0, sizeof(*pInchiInp) ); - memset( pInp, 0, sizeof(*pInp) ); - pInp->szOptions = szOptions; - pInchiInp->pInp = pInp; - } else { - bLibInchiSemaphore = 0; - return inchi_Ret_ERROR; - } - szHeader[0] = '\0'; - szLabel[0] = '\0'; - lNumber = 0; - /* prepare input string pointers */ - inchi_ios_init(&inp, INCHI_IOSTREAM_STRING, NULL); /* fix bug discovered by Burt Leland 2008-12-23 */ - inp.s.pStr = szInchiAuxInfo; - inp.s.nUsedLength = strlen(szInchiAuxInfo); - inp.s.nAllocatedLength = inp.s.nUsedLength+1; - inp.s.nPtr = 0; - - num_at = INChIToInchi_Input( &inp, pInchiInp->pInp, 1, bDoNotAddH, vABParityUnknown, - INPUT_INCHI_PLAIN, szHeader, szLabel, - &lNumber, &bChiral, &err, pInchiInp->szErrMsg ); - pInchiInp->bChiral = bChiral; - if ( num_at <= 0 ) { - if ( 10 < err && err < 20 ) { - nRet = inchi_Ret_EOF; - } else - if ( err == 9 ) { - nRet = inchi_Ret_ERROR; /* sdfile bypassed to $$$$ */ - } else - if ( err && err < 30 ) { - nRet = inchi_Ret_FATAL; - } else - if ( 98 == err ) { - nRet = inchi_Ret_WARNING; /* empty AuxInfo */ - } else - if ( err ) { - nRet = inchi_Ret_ERROR; - } else - if ( pInchiInp->szErrMsg[0] ) { - nRet = inchi_Ret_WARNING; - } - } - if ( nRet != inchi_Ret_OKAY && nRet != inchi_Ret_WARNING ) { - Free_inchi_Input( pInchiInp->pInp ); - pInchiInp->bChiral = 0; - } - bLibInchiSemaphore = 0; - return nRet; -} -/*****************************************************************************************************/ -void INCHI_DECL Free_std_inchi_Input( inchi_Input *pInp ) -{ - Free_inchi_Input( pInp ); -} - -void INCHI_DECL Free_inchi_Input( inchi_Input *pInp ) -{ - FreeInchi_Atom( &pInp->atom ); - FreeInchi_Stereo0D ( &pInp->stereo0D ); - pInp->num_atoms = 0; - pInp->num_stereo0D = 0; -} - - -/*#endif*/ /* INCHI_MAIN */ - -#ifndef TARGET_API_LIB -#error "TARGET_API_LIB MUST be defined here" -#endif - - -/**********************************************************************************/ -int INChIToInchi_Input( INCHI_IOSTREAM *inp_molfile, inchi_Input *orig_at_data, int bMergeAllInputStructures, - int bDoNotAddH, int vABParityUnknown, INPUT_TYPE nInputType, - char *pSdfLabel, char *pSdfValue, long *lSdfId, INCHI_MODE *pInpAtomFlags, - int *err, char *pStrErr ) -{ - /* inp_ATOM *at = NULL; */ - int num_dimensions_new; - int num_inp_bonds_new; - int num_inp_atoms_new; - int num_inp_0D_new; - inchi_Atom *at_new = NULL; - inchi_Atom *at_old = NULL; - inchi_Stereo0D *stereo0D_new = NULL; - inchi_Stereo0D *stereo0D_old = NULL; - int nNumAtoms = 0, nNumStereo0D = 0; - MOL_COORD *szCoordNew = NULL; - MOL_COORD *szCoordOld = NULL; - int i, j; - - if ( pStrErr ) { - pStrErr[0] = '\0'; - } - - /*FreeOrigAtData( orig_at_data );*/ - if ( lSdfId ) - *lSdfId = 0; - do { - - at_old = orig_at_data? orig_at_data->atom : NULL; /* save pointer to the previous allocation */ - stereo0D_old = orig_at_data? orig_at_data->stereo0D : NULL; - szCoordOld = NULL; - num_inp_atoms_new = - INChIToInchi_Atom( inp_molfile, orig_at_data? &stereo0D_new:NULL, &num_inp_0D_new, - bDoNotAddH, vABParityUnknown, nInputType, - orig_at_data? &at_new:NULL, MAX_ATOMS, - &num_dimensions_new, &num_inp_bonds_new, - pSdfLabel, pSdfValue, lSdfId, pInpAtomFlags, err, pStrErr ); - if ( num_inp_atoms_new <= 0 && !*err ) { - MOLFILE_ERR_SET (*err, 0, "Empty structure"); - *err = 98; - } else - if ( orig_at_data && !num_inp_atoms_new && 10 < *err && *err < 20 && orig_at_data->num_atoms > 0 && bMergeAllInputStructures ) { - *err = 0; /* end of file */ - break; - } else - if ( num_inp_atoms_new > 0 && orig_at_data ) { - /* merge pOrigDataTmp + orig_at_data => pOrigDataTmp; */ - nNumAtoms = num_inp_atoms_new + orig_at_data->num_atoms; - nNumStereo0D = num_inp_0D_new + orig_at_data->num_stereo0D; - if ( nNumAtoms >= MAX_ATOMS ) { - MOLFILE_ERR_SET (*err, 0, "Too many atoms"); - *err = 70; - orig_at_data->num_atoms = -1; - } else - if ( !at_old ) { - /* the first structure */ - orig_at_data->atom = at_new; at_new = NULL; - orig_at_data->num_atoms = num_inp_atoms_new; num_inp_atoms_new = 0; - orig_at_data->stereo0D = stereo0D_new; stereo0D_new = NULL; - orig_at_data->num_stereo0D = num_inp_0D_new; num_inp_0D_new = 0; - } else - if ( orig_at_data->atom = CreateInchi_Atom( nNumAtoms ) ) { - /* switch at_new <--> orig_at_data->at; */ - if ( orig_at_data->num_atoms ) { - memcpy( orig_at_data->atom, at_old, orig_at_data->num_atoms * sizeof(orig_at_data->atom[0]) ); - /* adjust numbering in the newly read structure */ - for ( i = 0; i < num_inp_atoms_new; i ++ ) { - for ( j = 0; j < at_new[i].num_bonds; j ++ ) { - at_new[i].neighbor[j] += orig_at_data->num_atoms; - } - } - } - FreeInchi_Atom( &at_old ); - /* copy newly read structure */ - memcpy( orig_at_data->atom + orig_at_data->num_atoms, - at_new, - num_inp_atoms_new * sizeof(orig_at_data->atom[0]) ); - /* copy newly read 0D stereo */ - if ( num_inp_0D_new > 0 && stereo0D_new ) { - if ( orig_at_data->stereo0D = CreateInchi_Stereo0D( nNumStereo0D ) ) { - memcpy( orig_at_data->stereo0D, stereo0D_old, orig_at_data->num_stereo0D * sizeof(orig_at_data->stereo0D[0]) ); - /* adjust numbering in the newly read structure */ - for ( i = 0; i < num_inp_0D_new; i ++ ) { - if ( stereo0D_new[i].central_atom >= 0 ) { - stereo0D_new[i].central_atom += orig_at_data->num_atoms; - } - for ( j = 0; j < 4; j ++ ) { - stereo0D_new[i].neighbor[j] += orig_at_data->num_atoms; - } - } - FreeInchi_Stereo0D( &stereo0D_old ); - memcpy( orig_at_data->stereo0D+orig_at_data->num_stereo0D, - stereo0D_new, - num_inp_0D_new * sizeof(orig_at_data->stereo0D[0]) ); - } else { - num_inp_0D_new = 0; - MOLFILE_ERR_SET (*err, 0, "Out of RAM"); - *err = -1; - } - } else { - num_inp_0D_new = 0; - } - /* update lengths */ - orig_at_data->num_atoms += num_inp_atoms_new; - orig_at_data->num_stereo0D += num_inp_0D_new; - } else { - MOLFILE_ERR_SET (*err, 0, "Out of RAM"); - *err = -1; - } - } else - if ( num_inp_atoms_new > 0 ) { - nNumAtoms += num_inp_atoms_new; - } - FreeInchi_Atom( &at_new ); - num_inp_atoms_new = 0; - FreeInchi_Stereo0D( &stereo0D_new ); - num_inp_0D_new = 0; - - } while ( !*err && bMergeAllInputStructures ); - /* - if ( !*err ) { - orig_at_data->num_components = - MarkDisconnectedComponents( orig_at_data ); - if ( orig_at_data->num_components == 0 ) { - MOLFILE_ERR_SET (*err, 0, "No components found"); - *err = 99; - } - if ( orig_at_data->num_components < 0 ) { - MOLFILE_ERR_SET (*err, 0, "Too many components"); - *err = 99; - } - } - */ - if ( szCoordNew ) { - inchi_free( szCoordNew ); - } - if ( at_new ) { - inchi_free( at_new ); - } - /* - if ( !*err ) { - if ( ReconcileAllCmlBondParities( orig_at_data->atom, orig_at_data->num_atoms ) ) { - MOLFILE_ERR_SET (*err, 0, "Cannot reconcile stereobond parities"); - if (!orig_at_data->num_atoms) { - *err = 1; - } - } - } - */ - if ( *err ) { - FreeInchi_Input( orig_at_data ); - } - if ( *err && !(10 < *err && *err < 20) && pStrErr && !pStrErr[0] ) { - MOLFILE_ERR_SET (*err, 0, "Unknown error"); /* */ - } - return orig_at_data? orig_at_data->num_atoms : nNumAtoms; -} - +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include +#include +#include +#include +#include +#include + +/* for use in the InChI library */ + +#include "../../../INCHI_BASE/src/mode.h" + +#include "../../../INCHI_BASE/src/incomdef.h" +#include "../../../INCHI_BASE/src/ichidrp.h" +#include "../../../INCHI_BASE/src/inpdef.h" +#include "../../../INCHI_BASE/src/util.h" +#include "../../../INCHI_BASE/src/ichierr.h" +#include "../../../INCHI_BASE/src/ichicomp.h" +#include "../../../INCHI_BASE/src/ichi_io.h" +#include "../../../INCHI_BASE/src/inchi_api.h" + +#include "inchi_dll_b.h" + + + +int InchiToInchi_Input( INCHI_IOSTREAM *inp_molfile, inchi_Input *orig_at_data, int bMergeAllInputStructures, + int bDoNotAddH, int vABParityUnknown, INPUT_TYPE nInputType, + char *pSdfLabel, char *pSdfValue, long *lSdfId, INCHI_MODE *pInpAtomFlags, + int *err, char *pStrErr ); + +/* This contains executable code. Included in lReadAux.c, e_ReadINCH.c, ReadINCH.c, */ +#include "../../../INCHI_BASE/src/readinch.h" + + + +/*****************************************************************************************************/ +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL Get_std_inchi_Input_FromAuxInfo + ( char *szInchiAuxInfo, + int bDoNotAddH, + InchiInpData *pInchiInp ) +{ + int bDiffUnkUndfStereo = 0; + return Get_inchi_Input_FromAuxInfo( szInchiAuxInfo, bDoNotAddH, bDiffUnkUndfStereo, + pInchiInp ); +} + +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL Get_inchi_Input_FromAuxInfo(char *szInchiAuxInfo, + int bDoNotAddH, int bDiffUnkUndfStereo, + InchiInpData *pInchiInp ) +{ + INCHI_IOSTREAM inp; + int num_at, nRet = inchi_Ret_OKAY, err = 0; + INCHI_MODE bChiral = 0; + /* the input string may contain the following line: "Structure NNN. HHH=VVV" */ + long lNumber; /* structure number NNN from the input */ + char szHeader[MAX_SDF_HEADER]; /* stucture label header HHH from the input */ + char szLabel[MAX_SDF_VALUE]; /* stucture label VVV from the input */ + + /* vABParityUnknown holds actual value of an internal constant signifying */ + /* unknown parity: either the same as for undefined parity (default==standard) */ + /* or a specific one (non-std; requested by SLUUD switch). */ + int vABParityUnknown = AB_PARITY_UNDF; + if ( 0 != bDiffUnkUndfStereo ) + { + /* Make labels for unknown and undefined stereo different */ + vABParityUnknown = AB_PARITY_UNKN; + } + + + + if ( pInchiInp && pInchiInp->pInp ) { + /* clear output fields */ + inchi_Input *pInp = pInchiInp->pInp; + char *szOptions = pInp->szOptions; + memset( pInchiInp, 0, sizeof(*pInchiInp) ); + memset( pInp, 0, sizeof(*pInp) ); + pInp->szOptions = szOptions; + pInchiInp->pInp = pInp; + } else { + return inchi_Ret_ERROR; + } + szHeader[0] = '\0'; + szLabel[0] = '\0'; + lNumber = 0; + /* prepare input string pointers */ + inchi_ios_init(&inp, INCHI_IOSTREAM_TYPE_STRING, NULL); /* fix bug discovered by Burt Leland 2008-12-23 */ + inp.s.pStr = szInchiAuxInfo; + inp.s.nUsedLength = (int) strlen(szInchiAuxInfo); + inp.s.nAllocatedLength = inp.s.nUsedLength+1; + inp.s.nPtr = 0; + + num_at = InchiToInchi_Input( &inp, pInchiInp->pInp, 1, bDoNotAddH, vABParityUnknown, + INPUT_INCHI_PLAIN, szHeader, szLabel, + &lNumber, &bChiral, &err, pInchiInp->szErrMsg ); + pInchiInp->bChiral = bChiral; + if ( num_at <= 0 ) { + if ( 10 < err && err < 20 ) { + nRet = inchi_Ret_EOF; + } else + if ( err == 9 ) { + nRet = inchi_Ret_ERROR; /* sdfile bypassed to $$$$ */ + } else + if ( err && err < 30 ) { + nRet = inchi_Ret_FATAL; + } else + if ( 98 == err ) { + nRet = inchi_Ret_WARNING; /* empty AuxInfo */ + } else + if ( err ) { + nRet = inchi_Ret_ERROR; + } else + if ( pInchiInp->szErrMsg[0] ) { + nRet = inchi_Ret_WARNING; + } + } + if ( nRet != inchi_Ret_OKAY && nRet != inchi_Ret_WARNING ) { + Free_inchi_Input( pInchiInp->pInp ); + pInchiInp->bChiral = 0; + } + + return nRet; +} +/*****************************************************************************************************/ +void INCHI_DECL Free_std_inchi_Input( inchi_Input *pInp ) +{ + Free_inchi_Input( pInp ); +} + +void INCHI_DECL Free_inchi_Input( inchi_Input *pInp ) +{ + FreeInchi_Atom( &pInp->atom ); + FreeInchi_Stereo0D ( &pInp->stereo0D ); + pInp->num_atoms = 0; + pInp->num_stereo0D = 0; +} + + +/*#endif*/ /* INCHI_MAIN */ + +#ifndef TARGET_API_LIB +#error "TARGET_API_LIB MUST be defined here" +#endif + + +/**********************************************************************************/ +int InchiToInchi_Input( INCHI_IOSTREAM *inp_molfile, + inchi_Input *orig_at_data, + int bMergeAllInputStructures, + int bDoNotAddH, + int vABParityUnknown, + INPUT_TYPE nInputType, + char *pSdfLabel, + char *pSdfValue, + long *lSdfId, + INCHI_MODE *pInpAtomFlags, + int *err, + char *pStrErr ) +{ + /* inp_ATOM *at = NULL; */ + int num_dimensions_new; + int num_inp_bonds_new; + int num_inp_atoms_new; + int num_inp_0D_new; + inchi_Atom *at_new = NULL; + inchi_Atom *at_old = NULL; + inchi_Stereo0D *stereo0D_new = NULL; + inchi_Stereo0D *stereo0D_old = NULL; + int nNumAtoms = 0, nNumStereo0D = 0; + MOL_COORD *szCoordNew = NULL; + MOL_COORD *szCoordOld = NULL; + int i, j; + + if ( pStrErr ) { + pStrErr[0] = '\0'; + } + + /*FreeOrigAtData( orig_at_data );*/ + if ( lSdfId ) + *lSdfId = 0; + do { + + at_old = orig_at_data? orig_at_data->atom : NULL; /* save pointer to the previous allocation */ + stereo0D_old = orig_at_data? orig_at_data->stereo0D : NULL; + szCoordOld = NULL; + num_inp_atoms_new = + InchiToInchiAtom( inp_molfile, orig_at_data? &stereo0D_new:NULL, &num_inp_0D_new, + bDoNotAddH, vABParityUnknown, nInputType, + orig_at_data? &at_new:NULL, MAX_ATOMS, + &num_dimensions_new, &num_inp_bonds_new, + pSdfLabel, pSdfValue, lSdfId, pInpAtomFlags, err, pStrErr ); + if ( num_inp_atoms_new <= 0 && !*err ) { + TREAT_ERR (*err, 0, "Empty structure"); + *err = 98; + } else + if ( orig_at_data && !num_inp_atoms_new && 10 < *err && *err < 20 && orig_at_data->num_atoms > 0 && bMergeAllInputStructures ) { + *err = 0; /* end of file */ + break; + } else + if ( num_inp_atoms_new > 0 && orig_at_data ) { + /* merge pOrigDataTmp + orig_at_data => pOrigDataTmp; */ + nNumAtoms = num_inp_atoms_new + orig_at_data->num_atoms; + nNumStereo0D = num_inp_0D_new + orig_at_data->num_stereo0D; + if ( nNumAtoms >= MAX_ATOMS ) { + TREAT_ERR (*err, 0, "Too many atoms [did you forget 'LargeMolecules' switch?]"); + *err = 70; + orig_at_data->num_atoms = -1; + } else + if ( !at_old ) { + /* the first structure */ + orig_at_data->atom = at_new; at_new = NULL; + orig_at_data->num_atoms = num_inp_atoms_new; num_inp_atoms_new = 0; + orig_at_data->stereo0D = stereo0D_new; stereo0D_new = NULL; + orig_at_data->num_stereo0D = num_inp_0D_new; num_inp_0D_new = 0; + } else + if ( orig_at_data->atom = CreateInchiAtom( nNumAtoms ) ) { + /* switch at_new <--> orig_at_data->at; */ + if ( orig_at_data->num_atoms ) { + memcpy( orig_at_data->atom, at_old, orig_at_data->num_atoms * sizeof(orig_at_data->atom[0]) ); + /* adjust numbering in the newly read structure */ + for ( i = 0; i < num_inp_atoms_new; i ++ ) { + for ( j = 0; j < at_new[i].num_bonds; j ++ ) { + at_new[i].neighbor[j] += orig_at_data->num_atoms; + } + } + } + FreeInchi_Atom( &at_old ); + /* copy newly read structure */ + memcpy( orig_at_data->atom + orig_at_data->num_atoms, + at_new, + num_inp_atoms_new * sizeof(orig_at_data->atom[0]) ); + /* copy newly read 0D stereo */ + if ( num_inp_0D_new > 0 && stereo0D_new ) { + if ( orig_at_data->stereo0D = CreateInchi_Stereo0D( nNumStereo0D ) ) { + memcpy( orig_at_data->stereo0D, stereo0D_old, orig_at_data->num_stereo0D * sizeof(orig_at_data->stereo0D[0]) ); + /* adjust numbering in the newly read structure */ + for ( i = 0; i < num_inp_0D_new; i ++ ) { + if ( stereo0D_new[i].central_atom >= 0 ) { + stereo0D_new[i].central_atom += orig_at_data->num_atoms; + } + for ( j = 0; j < 4; j ++ ) { + stereo0D_new[i].neighbor[j] += orig_at_data->num_atoms; + } + } + FreeInchi_Stereo0D( &stereo0D_old ); + memcpy( orig_at_data->stereo0D+orig_at_data->num_stereo0D, + stereo0D_new, + num_inp_0D_new * sizeof(orig_at_data->stereo0D[0]) ); + } else { + num_inp_0D_new = 0; + TREAT_ERR (*err, 0, "Out of RAM"); + *err = -1; + } + } else { + num_inp_0D_new = 0; + } + /* update lengths */ + orig_at_data->num_atoms += num_inp_atoms_new; + orig_at_data->num_stereo0D += num_inp_0D_new; + } else { + TREAT_ERR (*err, 0, "Out of RAM"); + *err = -1; + } + } else + if ( num_inp_atoms_new > 0 ) { + nNumAtoms += num_inp_atoms_new; + } + FreeInchi_Atom( &at_new ); + num_inp_atoms_new = 0; + FreeInchi_Stereo0D( &stereo0D_new ); + num_inp_0D_new = 0; + } while ( !*err && bMergeAllInputStructures ); + /* + if ( !*err ) { + orig_at_data->num_components = + MarkDisconnectedComponents( orig_at_data ); + if ( orig_at_data->num_components == 0 ) { + TREAT_ERR (*err, 0, "No components found"); + *err = 99; + } + if ( orig_at_data->num_components < 0 ) { + TREAT_ERR (*err, 0, "Too many components"); + *err = 99; + } + } + */ + if ( szCoordNew ) { + inchi_free( szCoordNew ); + } + if ( at_new ) { + inchi_free( at_new ); + } + /* + if ( !*err ) { + if ( ReconcileAllCmlBondParities( orig_at_data->atom, orig_at_data->num_atoms ) ) { + TREAT_ERR (*err, 0, "Cannot reconcile stereobond parities"); + if (!orig_at_data->num_atoms) { + *err = 1; + } + } + } + */ + if ( *err ) { + FreeInchi_Input( orig_at_data ); + } + if ( *err && !(10 < *err && *err < 20) && pStrErr && !pStrErr[0] ) { + TREAT_ERR (*err, 0, "Unknown error"); /* */ + } + return orig_at_data? orig_at_data->num_atoms : nNumAtoms; +} diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/inchi_dll.c b/INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll.c similarity index 50% rename from INCHI-1-SRC/INCHI_API/inchi_dll/inchi_dll.c rename to INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll.c index 7b95979..a4c8729 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/inchi_dll.c +++ b/INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll.c @@ -1,2628 +1,3399 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include "mode.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "inpdef.h" -#include "ichi.h" -#include "strutil.h" -#include "util.h" -#include "ichierr.h" -#include "ichimain.h" -#include "extr_ct.h" -#include "ichi_io.h" - -#include "ichicomp.h" -#include "inchi_api.h" - - - - - - - - -/************************************************************************* - * - * Local prototypes - * - *************************************************************************/ - - -int SetAtomProperties( inp_ATOM *at, MOL_COORD *szCoord, inchi_Atom *ati, - int a1, int *nDim, char *pStrErr, int *err ); -int SetBondProperties( inp_ATOM *at, inchi_Atom *ati, int a1, int j, - int nNumAtoms, int *nNumBonds, char *pStrErr, int *err ); -int SetAtomAndBondProperties( inp_ATOM *at, inchi_Atom *ati, int a1, - int bDoNotAddH, char *pStrErr, int *err ); -void SetNumImplicitH(inp_ATOM* at, int num_atoms); -int Extract0DParities(inp_ATOM *at, int nNumAtoms, inchi_Stereo0D *stereo0D, - int num_stereo0D, char *pStrErr, int *err, int vABParityUnknown); -int parse_options_string ( char *cmd, const char *argv[], int maxargs ); - -int InpAtom0DToInchiAtom( inp_ATOM *at, int num_atoms, inchi_OutputStruct *outStruct ); - - -int ExtractOneStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, - inchi_Input *inp, - INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *prb_file, - ORIG_ATOM_DATA *orig_inp_data, long *num_inp, char *pStr, int nStrLen ); - - -static int GetINCHI1(inchi_Input *inp, inchi_Output *out, int bStdFormat); - -/*************************************************************************/ - -int bInterrupted = 0; - -/******************************************************************** - * - * INCHI API: DEALLOCATE INCHI OUTPUT - * - ********************************************************************/ - - -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeINCHI( inchi_Output *out ) -{ - if ( out->szInChI ) { - inchi_free( out->szInChI ); - } - if ( out->szLog ) { - inchi_free( out->szLog ); - } - if ( out->szMessage ) { - inchi_free( out->szMessage ); - } - memset( out, 0, sizeof(*out) ); -} - - -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeStdINCHI( inchi_Output *out ) -{ - FreeINCHI( out ); -} - -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeStructFromStdINCHI( inchi_OutputStruct *out ) -{ - FreeStructFromINCHI( out ); -} - - -/*******************************************************************/ -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeStructFromINCHI( inchi_OutputStruct *out ) -{ - if ( out->atom ) { - inchi_free( out->atom ); - } - if ( out->stereo0D ) { - inchi_free( out->stereo0D ); - } - if ( out->szLog ) { - inchi_free( out->szLog ); - } - if ( out->szMessage ) { - inchi_free( out->szMessage ); - } - memset( out, 0, sizeof(*out) ); -} -/********************************************************************/ -#define INCHI_MAX_NUM_ARG 32 -/******************************************************************** - * - * INCHI API: MAIN ENTRY POINT - * - ********************************************************************/ - -int bLibInchiSemaphore = 0; - - -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStdINCHI( inchi_Input *inp, inchi_Output *out ) -{ - return GetINCHI1( inp, out, 1 ); -} - - - - -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetINCHI( inchi_Input *inp, inchi_Output *out ) -{ - return GetINCHI1( inp, out, 0 ); -} - - -static int GetINCHI1(inchi_Input *inp, inchi_Output *out, int bStdFormat) -{ - STRUCT_DATA struct_data; - STRUCT_DATA *sd = &struct_data; - char szTitle[MAX_SDF_HEADER+MAX_SDF_VALUE+256]; - - int i; - long num_inp, num_err; - char szSdfDataValue[MAX_SDF_VALUE+1]; - PINChI2 *pINChI[INCHI_NUM]; - PINChI_Aux2 *pINChI_Aux[INCHI_NUM]; - - unsigned long ulDisplTime = 0; /* infinite, milliseconds */ - unsigned long ulTotalProcessingTime = 0; - - INPUT_PARMS inp_parms; - INPUT_PARMS *ip = &inp_parms; - - ORIG_ATOM_DATA OrigAtData; /* 0=> disconnected, 1=> original */ - ORIG_ATOM_DATA *orig_inp_data = &OrigAtData; - ORIG_ATOM_DATA PrepAtData[2]; /* 0=> disconnected, 1=> original */ - ORIG_ATOM_DATA *prep_inp_data = PrepAtData; - int bReleaseVersion = bRELEASE_VERSION; - const int nStrLen = 64000; - char *pStr = NULL; - int nRet = 0, nRet1; - - STRUCT_FPTRS *pStructPtrs = NULL; - -#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) - int num_repeat = REPEAT_ALL; -#endif - - const char *argv[INCHI_MAX_NUM_ARG+1]; - int argc; - char *szOptions = NULL; - - INCHI_IOSTREAM inchi_file[3], *output_file = inchi_file, *log_file = inchi_file+1; - INCHI_IOSTREAM prb_file0, *prb_file = &prb_file0; - - - if ( bLibInchiSemaphore ) { /* does not work properly under sufficient stress */ - return inchi_Ret_BUSY; - } - bLibInchiSemaphore = 1; - -#if( TRACE_MEMORY_LEAKS == 1 ) - _CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF); -/* for execution outside the VC++ debugger uncomment one of the following two */ -#ifdef MY_REPORT_FILE - _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_WARN, MY_REPORT_FILE ); - _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_ERROR, MY_REPORT_FILE ); - _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_ASSERT, MY_REPORT_FILE ); -#else - _CrtSetReportMode(_CRT_WARN | _CRT_ERROR, _CRTDBG_MODE_DEBUG); -#endif - -#if ( !defined(__STDC__) || __STDC__ != 1 ) - /* turn on floating point exceptions */ - { - /* Get the default control word. */ - int cw = _controlfp( 0,0 ); - - /* Set the exception masks OFF, turn exceptions on. */ - /*cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_INEXACT|EM_ZERODIVIDE|EM_DENORMAL);*/ - cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE|EM_DENORMAL); - - /* Set the control word. */ - _controlfp( cw, MCW_EM ); - - } -#endif -#endif - - - - - szTitle[0] = '\0'; - -#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) -repeat: - inchi_ios_close(output_file); - inchi_ios_close(log_file); - inchi_ios_close(prb_file); - pStr = NULL; -#endif - - /*^^^ Initialize internal for this function output streams as string buffers */ - inchi_ios_init(output_file, INCHI_IOSTREAM_STRING, NULL); - inchi_ios_init(log_file, INCHI_IOSTREAM_STRING, NULL); - inchi_ios_init(prb_file, INCHI_IOSTREAM_STRING, NULL); - - - num_inp = 0; - num_err = 0; - sd->bUserQuit = 0; - - /* clear original input structure */ - memset( pINChI, 0, sizeof(pINChI ) ); - memset( pINChI_Aux, 0, sizeof(pINChI_Aux) ); - memset( sd, 0, sizeof(*sd) ); - memset( ip, 0, sizeof(*ip) ); - memset( orig_inp_data , 0, sizeof( *orig_inp_data ) ); - memset( prep_inp_data , 0, 2*sizeof( *prep_inp_data ) ); - memset( szSdfDataValue , 0, sizeof( szSdfDataValue ) ); - - if ( !out ) { - nRet = _IS_ERROR; - goto exit_function; - } - memset( out, 0, sizeof(*out) ); - - /* options */ - if ( inp && inp->szOptions ) { - szOptions = (char*)inchi_malloc( strlen(inp->szOptions) + 1 ); - if ( szOptions ) { - strcpy( szOptions, inp->szOptions ); - argc = parse_options_string ( szOptions, argv, INCHI_MAX_NUM_ARG ); - } else { - nRet = _IS_FATAL; - goto translate_RetVal; /* emergency exit */ - } - } else { - argc = 1; - argv[0] = ""; - argv[1] = NULL; - } - - if ( argc == 1 -#ifdef TARGET_API_LIB - && (!inp || inp->num_atoms <= 0 || !inp->atom) -#endif - || argc==2 && ( argv[1][0]==INCHI_OPTION_PREFX ) && - (!strcmp(argv[1]+1, "?") || !stricmp(argv[1]+1, "help") ) ) { - HelpCommandLineParms(log_file); - out->szLog = log_file->s.pStr; - memset( log_file, 0, sizeof(*log_file) ); - nRet = _IS_EOF; - goto translate_RetVal; - } - - nRet1 = ReadCommandLineParms( argc, argv, ip, szSdfDataValue, &ulDisplTime, bReleaseVersion, log_file ); - if ( szOptions ) { - inchi_free( szOptions ); - szOptions = NULL; - } - /* INChI DLL specific */ - ip->bNoStructLabels = 1; - - if ( 0 > nRet1 ) { - nRet = _IS_FATAL; - goto exit_function; - } - if ( ip->bNoStructLabels ) { - ip->pSdfLabel = NULL; - ip->pSdfValue = NULL; - } else - if ( ip->nInputType == INPUT_INCHI_XML || ip->nInputType == INPUT_INCHI_PLAIN || ip->nInputType == INPUT_CMLFILE ) { - /* the input may contain both the header and the label of the structure */ - if ( !ip->pSdfLabel ) - ip->pSdfLabel = ip->szSdfDataHeader; - if ( !ip->pSdfValue ) - ip->pSdfValue = szSdfDataValue; - } - - /* Ensure standardness */ - if ( bStdFormat ) - { - if ( ip->bINChIOutputOptions & INCHI_OUT_SAVEOPT ) - { - ip->bINChIOutputOptions &= ~INCHI_OUT_SAVEOPT; - } - if ( 0 != ( ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) - { - ip->bTautFlags &= ~TG_FLAG_RECONNECT_COORD; - } - if ( 0 != (ip->nMode & REQ_MODE_BASIC) ) - { - ip->nMode &= ~REQ_MODE_BASIC; - } - if ( 0 != ( ip->nMode & REQ_MODE_RELATIVE_STEREO) ) - { - ip->nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); - } - if ( 0 != ( ip->nMode & REQ_MODE_RACEMIC_STEREO) ) - { - ip->nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); - } - if ( 0 != ( ip->nMode & REQ_MODE_CHIR_FLG_STEREO) ) - { - ip->nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); - } - if ( 0 != ( ip->nMode & REQ_MODE_DIFF_UU_STEREO) ) - { - ip->nMode &= ~REQ_MODE_DIFF_UU_STEREO; - } - if ( 0 == (ip->nMode & (REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU)) ) - { - ip->nMode |= REQ_MODE_SB_IGN_ALL_UU; - ip->nMode |= REQ_MODE_SC_IGN_ALL_UU; - } - if ( 0 != (ip->bTautFlags & TG_FLAG_KETO_ENOL_TAUT) ) - { - ip->bTautFlags &= ~TG_FLAG_KETO_ENOL_TAUT; - } - if ( 0 != (ip->bTautFlags & TG_FLAG_1_5_TAUT) ) - { - ip->bTautFlags &= ~TG_FLAG_1_5_TAUT; - } - /* And anyway... */ - ip->bINChIOutputOptions |= INCHI_OUT_STDINCHI; - ip->bINChIOutputOptions &= ~INCHI_OUT_SAVEOPT; - } - /* */ - - - PrintInputParms( log_file, ip ); - if ( !(pStr = (char*)inchi_malloc(nStrLen))) { - inchi_ios_eprint( log_file, "Cannot allocate output buffer. Terminating\n"); - goto exit_function; - } - pStr[0] = '\0'; - - - - /**********************************************************************************************/ - /* Main cycle */ - /* read input structures and create their INChI */ - ulTotalProcessingTime = 0; - - if ( pStructPtrs ) { - memset ( pStructPtrs, 0, sizeof(pStructPtrs[0]) ); - } - - /* === possible improvement: convert inp to orig_inp_data ==== */ - if ( !sd->bUserQuit && !bInterrupted ) - { - if ( ip->last_struct_number && num_inp >= ip->last_struct_number ) { - nRet = _IS_EOF; /* simulate end of file */ - goto exit_function; - } - - nRet = ExtractOneStructure( sd, ip, szTitle, inp, log_file, output_file, prb_file, - orig_inp_data, &num_inp, pStr, nStrLen ); - - if ( pStructPtrs ) { - pStructPtrs->cur_fptr ++; - } - -#ifndef TARGET_API_LIB - if ( sd->bUserQuit ) { - break; - } -#endif - switch ( nRet ) { - case _IS_FATAL: - num_err ++; - goto exit_function; - case _IS_EOF: - goto exit_function; - case _IS_ERROR: - num_err ++; - goto exit_function; -#ifndef TARGET_API_LIB - case _IS_SKIP: - continue; -#endif - } - - /* create INChI for each connected component of the structure and optionally display them */ - /* output INChI for the whole structure */ - nRet1 = ProcessOneStructure( sd, ip, szTitle, pINChI, pINChI_Aux, - NULL, /* inp_file is not necessary as all input is already saved in 'ip' */ - log_file, output_file, prb_file, - orig_inp_data, prep_inp_data, - num_inp, pStr, nStrLen, - 0 /* save_opt_bits */); - - /* free INChI memory */ - FreeAllINChIArrays( pINChI, pINChI_Aux, sd->num_components ); - - /* free structure data */ - FreeOrigAtData( orig_inp_data ); - FreeOrigAtData( prep_inp_data ); - FreeOrigAtData( prep_inp_data+1 ); - - ulTotalProcessingTime += sd->ulStructTime; - nRet = inchi_max(nRet, nRet1); - switch ( nRet ) { - case _IS_FATAL: - /* num_err ++; */ - goto exit_function; - case _IS_ERROR: - ; /* num_err ++; */ -#ifndef TARGET_API_LIB - continue; -#endif - } - } - -exit_function: - if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) && sd->bXmlStructStarted > 0 ) { - if ( !OutputINChIXmlStructEndTag( output_file, pStr, nStrLen, 1 ) ) { - inchi_ios_eprint( log_file, "Cannot create end xml tag for structure #%d.%s%s%s%s Terminating.\n", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - sd->bXmlStructStarted = -1; /* do not repeat same message */ - } - } - - - if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) && ip->bXmlStarted ) { - OutputINChIXmlRootEndTag( output_file ); - ip->bXmlStarted = 0; - } - - - /* avoid memory leaks in case of fatal error */ - if ( pStructPtrs && pStructPtrs->fptr ) { - inchi_free( pStructPtrs->fptr ); - } - - /* free INChI memory */ - FreeAllINChIArrays( pINChI, pINChI_Aux, sd->num_components ); - /* free structure data */ - FreeOrigAtData( orig_inp_data ); - FreeOrigAtData( prep_inp_data ); - FreeOrigAtData( prep_inp_data+1 ); - -#if( ADD_CMLPP == 1 ) - /* BILLY 8/6/04 */ - /* free CML memory */ - FreeCml (); - FreeCmlDoc( 1 ); -#endif - - if ( pStr ) { - inchi_free( pStr ); - } - - for ( i = 0; i < MAX_NUM_PATHS; i ++ ) { - if ( ip->path[i] ) { - inchi_free( (char*) ip->path[i] ); /* cast deliberately discards 'const' qualifier */ - ip->path[i] = NULL; - } - } - - SetBitFree( ); - - -#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) - if ( num_repeat-- > 0 ) { - goto repeat; - } -#endif - - - /* output */ - if ( sd->pStrErrStruct[0] ) { - if ( out && (out->szMessage = (char *)inchi_malloc( strlen(sd->pStrErrStruct) + 1 )) ) { - strcpy( out->szMessage, sd->pStrErrStruct ); - } - } - if ( output_file->s.pStr && output_file->s.nUsedLength > 0 && out ) { - char *p; - out->szInChI = output_file->s.pStr; - out->szAuxInfo = NULL; - if ( !(INCHI_OUT_SDFILE_ONLY & ip->bINChIOutputOptions ) ) /* do not remove last LF from SDF output - 2008-12-23 DT */ - for ( p = strchr(out->szInChI, '\n'); p; p = strchr(p+1, '\n') ) { - if ( !memcmp( p, "\nAuxInfo", 8 ) ) { - *p = '\0'; /* remove LF after INChI */ - out->szAuxInfo = p+1; /* save pointer to AuxInfo */ - } else - if ( out->szAuxInfo || !p[1]) { /* remove LF after aux info or from the last char */ - *p = '\0'; - break; - } - } - output_file->s.pStr = NULL; - } - - - if ( log_file->s.pStr && log_file->s.nUsedLength > 0 ) { - while ( log_file->s.nUsedLength && '\n' == log_file->s.pStr[log_file->s.nUsedLength-1] ) { - log_file->s.pStr[-- log_file->s.nUsedLength] = '\0'; /* remove last LF */ - } - if ( out ) { - out->szLog = log_file->s.pStr; - log_file->s.pStr = NULL; - } - } - - - -translate_RetVal: - - /* Close inernal I/O streams */ - inchi_ios_close(log_file); - inchi_ios_close(output_file); - inchi_ios_close(prb_file); - - switch (nRet) { - case _IS_SKIP : nRet = inchi_Ret_SKIP ; break; /* not used in INChI dll */ - case _IS_EOF : nRet = inchi_Ret_EOF ; break; /* no structural data has been provided */ - case _IS_OKAY : nRet = inchi_Ret_OKAY ; break; /* Success; break; no errors or warnings */ - case _IS_WARNING: nRet = inchi_Ret_WARNING; break; /* Success; break; warning(s) issued */ - case _IS_ERROR : nRet = inchi_Ret_ERROR ; break; /* Error: no INChI has been created */ - case _IS_FATAL : nRet = inchi_Ret_FATAL ; break; /* Severe error: no INChI has been created (typically; break; memory allocation failed) */ - case _IS_UNKNOWN: - default : nRet = inchi_Ret_UNKNOWN; break; /* Unlnown program error */ - } - bLibInchiSemaphore = 0; - - return nRet; -} - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Check if the string represents valid InChI/standard InChI. -Input: - szINCHI source InChI - strict if 0, just quickly check for proper layout (prefix, version, etc.) - The result may not be strict. - If not 0, try to perform InChI2InChI conversion and - returns success if a resulting InChI string exactly match source. - The result may be 'false alarm' due to imperfect algorithm of - conversion. -Returns: - success/errors codes - -*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL CheckINCHI(const char *szINCHI, const int strict) -{ -int ret=INCHI_VALID_NON_STANDARD; -int ret_i2i; -inchi_InputINCHI inchi_inp; -inchi_Output inchi_out; -size_t slen, pos_slash1=0; -char *str = NULL; -size_t i; -size_t slen0; -char pp; - - /* .. non-empty */ - if (szINCHI==NULL) - return INCHI_INVALID_PREFIX; - - slen = strlen(szINCHI); - - /* .. has valid prefix */ - if (slen standard InChIKey */ - ret = INCHI_VALID_STANDARD; - pos_slash1++; - } - - /* .. has trailing slash in the right place */ - if (szINCHI[pos_slash1]!='/') - return INCHI_INVALID_LAYOUT; - - /* .. the rest of source string contains valid literals */ -#if 0 - if (!isalnum(szINCHI[pos_slash1+1] ) && - ( szINCHI[pos_slash1+1]!='/' ) ) - return INCHI_INVALID_LAYOUT; -#endif - - /* Treat possible SaveOpt letters */ - slen0 = slen; - if ( (szINCHI[slen-3]=='\\') && - (szINCHI[slen-2] >= 'A') && (szINCHI[slen-2] <='Z') && - (szINCHI[slen-1] >= 'A') && (szINCHI[slen-1] <='Z') - ) - slen0 = slen -3; - - for (i=pos_slash1+1; i= 'A' && pp <='Z') continue; - if (pp >= 'a' && pp <='z') continue; - if (pp >= '0' && pp <='9') continue; - switch ( pp ) - { - case '(': case ')': - case '*': case '+': - case ',': case '-': - case '.': case '/': - case ';': case '=': - case '?': case '@': continue; - - default: break; - } - return INCHI_INVALID_LAYOUT; - } - - if ( strict ) - { - char opts[]="?FixedH ?RecMet ?SUU ?SLUUD"; - extract_inchi_substring(&str, szINCHI, slen); - if (NULL==str) - { - ret = INCHI_FAIL_I2I; - goto fin; - } - - inchi_inp.szInChI = str; - opts[0] = opts[8] = opts[16] = opts[21] = INCHI_OPTION_PREFX; - inchi_inp.szOptions = opts; - - ret_i2i = GetINCHIfromINCHI(&inchi_inp, &inchi_out); - - if ( ((ret_i2i!=inchi_Ret_OKAY) && (ret_i2i!=inchi_Ret_WARNING)) || !inchi_out.szInChI ) - { - ret = INCHI_FAIL_I2I; - } - else - { - if (strcmp(inchi_inp.szInChI, inchi_out.szInChI)) - { - ret = INCHI_FAIL_I2I; - } - } - } - -fin:if ( strict ) - { - if (NULL!=str) - inchi_free(str); - } - return ret; -} - - - -/*************************************************************************/ -/******************************** from readmol.c *************************/ -/*************************************************************************/ -int AddMOLfileError( char *pStrErr, const char *szMsg ) -{ - if ( pStrErr && szMsg && szMsg[0] ) { - int lenStrErr = strlen( pStrErr ); - int lenMsg = strlen( szMsg ); - char *p = strstr( pStrErr, szMsg ); - if ( p && (p==pStrErr || *(p-1) == ' ' && (*(p-2) == ';' || *(p-2) == ':' )) && - (p+lenMsg == pStrErr+lenStrErr || - p[lenMsg] == ';' && p[lenMsg+1] == ' ' || - p[lenMsg-1]==':' && p[lenMsg]==' ') ) { - return 1; /* reject duplicates */ - } - if ( lenStrErr + lenMsg + 2*(lenStrErr > 0) < STR_ERR_LEN ) { - /* enough room to add */ - if (lenStrErr > 0) { - if ( pStrErr[lenStrErr-1] != ':' ) { - strcat( pStrErr, ";" ); - } - strcat( pStrErr, " " ); - } - strcat( pStrErr, szMsg ); - return 1; - } - /* no room */ - if ( strstr( pStrErr, "..." ) ) { - return 0; /* no room mark has already been set */ - } - if ( lenStrErr + 3 < STR_ERR_LEN ) { - strcat( pStrErr, "..." ); - } - } - return 0; -} -/****************************************************************/ -int CopyMOLfile(FILE *inp_file, long fPtrStart, long fPtrEnd, - FILE *prb_file, - long lNumb) -{ - return 0; /* dummy */ -} -/****************************************************************/ -/************************** from mol2atom.c *********************/ -/****************************************************************/ -void SetNumImplicitH(inp_ATOM* at, int num_atoms) -{ - int bNonMetal; - int a1/*, n1*/; - - /* special valences */ - for ( bNonMetal = 0; bNonMetal < 2; bNonMetal ++ ) { - for ( a1 = 0; a1 < num_atoms; a1 ++ ) { - int bHasMetalNeighbor /*, j*/; - if ( bNonMetal != is_el_a_metal( at[a1].el_number ) ) { - continue; /* first process all metals, after that all non-metals */ - } - - bHasMetalNeighbor = 0; - /*********************************************************************** - * Set number of hydrogen atoms - */ - at[a1].num_H = get_num_H( at[a1].elname, at[a1].num_H, at[a1].num_iso_H, - at[a1].charge, at[a1].radical, - at[a1].chem_bonds_valence, - 0, /* instead of valence entered by the user: it does not exist here*/ - (at[a1].at_type & 1) /* bAliased */, - !(at[a1].at_type & 2) /* bDoNotAddH */, - bHasMetalNeighbor ); - at[a1].at_type = 0; - } - } -} - -/******************************************************************************************************/ -void FreeInpAtom( inp_ATOM **at ) -{ - if ( at && *at ) { - inchi_free( *at ); - *at = NULL; - } -} -/******************************************************************************************************/ -inp_ATOM *CreateInpAtom( int num_atoms ) -{ - /* - void *p = inchi_calloc(num_atoms, sizeof(inp_ATOM) ); - if ( p == (void*)0x009143A8 ) { - int stop = 1; - } - return (inp_ATOM* )p; - */ - return (inp_ATOM* ) inchi_calloc(num_atoms, sizeof(inp_ATOM) ); -} -/******************************************************************************************************/ -void FreeInpAtomData( INP_ATOM_DATA *inp_at_data ) -{ - if ( inp_at_data ) { - if ( inp_at_data->at ) { - FreeInpAtom( &inp_at_data->at ); - } - if ( inp_at_data->at_fixed_bonds ) { - FreeInpAtom( &inp_at_data->at_fixed_bonds ); - } - memset( inp_at_data, 0, sizeof(*inp_at_data) ); - } -} -/******************************************************************************************************/ -int CreateInpAtomData( INP_ATOM_DATA *inp_at_data, int num_atoms, int create_at_fixed_bonds ) -{ - FreeInpAtomData( inp_at_data ); - if ( (inp_at_data->at = CreateInpAtom( num_atoms )) && - (!create_at_fixed_bonds || (inp_at_data->at_fixed_bonds = CreateInpAtom( num_atoms) ) ) ) { - inp_at_data->num_at = num_atoms; - return 1; - } - FreeInpAtomData( inp_at_data ); - return 0; -} -/******************************************************************************************************/ -void FreeCompAtomData( COMP_ATOM_DATA *inp_at_data ) -{ - FreeInpAtom( &inp_at_data->at ); - if ( inp_at_data->nOffsetAtAndH ) - inchi_free( inp_at_data->nOffsetAtAndH ); - memset( inp_at_data, 0, sizeof(*inp_at_data) ); -} -/******************************************************************************************************/ -#if( TEST_RENUMB_ATOMS == 1 ) /* { */ -/******************************************************************************************************/ -int CopyInpAtomData( INP_ATOM_DATA *dest_inp_at_data, INP_ATOM_DATA *src_inp_at_data ) -{ - int ret = 1; - if ( !dest_inp_at_data->at || dest_inp_at_data->num_at != src_inp_at_data->num_at ) { - ret = CreateInpAtomData( dest_inp_at_data, src_inp_at_data->num_at, (NULL != src_inp_at_data->at_fixed_bonds) ); - } else { - inp_ATOM *at = dest_inp_at_data->at; /* save ptr to already allocated memory */ - inp_ATOM *at2 = dest_inp_at_data->at_fixed_bonds; - *dest_inp_at_data = *src_inp_at_data; /* copy all other (scalar) data */ - dest_inp_at_data->at = at; /* restore ptr to already allocated memory */ - dest_inp_at_data->at_fixed_bonds = at2; - } - if ( ret ) { - memcpy( dest_inp_at_data->at, src_inp_at_data->at, - src_inp_at_data->num_at*sizeof(dest_inp_at_data->at[0]) ); - if ( dest_inp_at_data->at_fixed_bonds && src_inp_at_data->at_fixed_bonds ) { - memcpy( dest_inp_at_data->at_fixed_bonds, src_inp_at_data->at_fixed_bonds, - src_inp_at_data->num_at*sizeof(dest_inp_at_data->at_fixed_bonds[0]) ); - } - } - return ret; -} -/******************************************************************************************************/ -void RenumbInpAtomData( INP_ATOM_DATA *dest_inp_at_data, INP_ATOM_DATA *src_inp_at_data, AT_RANK *new_ord ) -{ - int j, n, m, val; -#if( TEST_RENUMB_NEIGH == 1 ) - int i, k; -#endif - int num_atoms = src_inp_at_data->num_at; - inp_ATOM *dest_at = dest_inp_at_data->at; - for ( n = 0; n < num_atoms; n ++ ) { - m = new_ord[n]; - dest_at[m] = src_inp_at_data->at[n]; - dest_at[m].orig_compt_at_numb = (AT_NUMB)(m+1); /* new ordering number within the component */ - val = dest_at[m].valence; - for ( j = 0; j < val; j ++ ) { - dest_at[m].neighbor[j] = new_ord[dest_at[m].neighbor[j]]; - } -#if( TEST_RENUMB_NEIGH == 1 ) - for ( i = 0; i < 3*val; i ++ ) { - j = (rand() * val) / (RAND_MAX+1); - k = (rand() * val) / (RAND_MAX+1); - if ( j >= val || k >= val || j == k ) { - continue; - } - inchi_swap( (char*)&dest_at[m].neighbor[j], (char*)&dest_at[m].neighbor[k], sizeof(dest_at[0].neighbor[0]) ); - inchi_swap( (char*)&dest_at[m].bond_stereo[j], (char*)&dest_at[m].bond_stereo[k], sizeof(dest_at[0].bond_stereo[0]) ); - inchi_swap( (char*)&dest_at[m].bond_type[j], (char*)&dest_at[m].bond_type[k], sizeof(dest_at[0].bond_type[0]) ); - /* adjust stereo bond links */ - if ( dest_at[m].sb_parity[0] ) { - int a; - for ( a = 0; a < MAX_NUM_STEREO_BONDS && dest_at[m].sb_parity[a]; a ++ ) { - - if ( k == (int)dest_at[m].sb_ord[a] ) { - dest_at[m].sb_ord[a] = j; - } else - if ( j == (int)dest_at[m].sb_ord[a] ) { - dest_at[m].sb_ord[a] = k; - } - - if ( k == (int)dest_at[m].sn_ord[a] ) { - dest_at[m].sn_ord[a] = j; - } else - if ( j == (int)dest_at[m].sn_ord[a] ) { - dest_at[m].sn_ord[a] = k; - } - } - } - } -#endif - } - -} -/******************************************************************************************************/ -void MakeNewOrd( int num_atoms, AT_RANK *new_ord ) -{ - int i, j, k; - for ( i = 0; i < 3*num_atoms; i ++ ) { - j = (rand() * num_atoms) / (RAND_MAX+1); - k = (rand() * num_atoms) / (RAND_MAX+1); - if ( j >= num_atoms || k >= num_atoms || j == k ) { - continue; - } - inchi_swap( (char*)&new_ord[j], (char*)&new_ord[k], sizeof(new_ord[0]) ); - } -} -#endif /* } TEST_RENUMB_ATOMS == 1 */ -/**********************************************************************************/ -void FreeOrigAtData( ORIG_ATOM_DATA *orig_at_data ) -{ - if ( !orig_at_data ) - return; - FreeInpAtom( &orig_at_data->at ); - if ( NULL != orig_at_data->nCurAtLen ) { - inchi_free( orig_at_data->nCurAtLen ); - } - if ( NULL != orig_at_data->nOldCompNumber ) { - inchi_free( orig_at_data->nOldCompNumber ); - } - if ( NULL != orig_at_data->szCoord ) { - inchi_free( orig_at_data->szCoord ); - } - if ( NULL != orig_at_data->nEquLabels ) { - inchi_free( orig_at_data->nEquLabels ); - } - if ( NULL != orig_at_data->nSortedOrder ) { - inchi_free( orig_at_data->nSortedOrder ); - } - memset( orig_at_data, 0, sizeof(*orig_at_data) ); -} -/********************************************************************/ - -#define REPEAT_ALL 0 -/********************************************************************/ -int parse_options_string ( char *cmd, const char *argv[], int maxargs ) -{ - char *p; - char *pArgCurChar; - int bInsideQuotes; - int bCopyCharToArg; - int nNumBackSlashes; - int i; - - i = 0; - argv[i++] = ""; /* zeroth argument is not used */ - p = cmd; - bInsideQuotes = 0; - - /* arguments, one by one */ - while( i < maxargs-1 ) { - /* bypass spaces */ - while ( *p == ' ' || *p == '\t' ) - p ++; - if ( !*p ) - break; - /* scan an argument */ - argv[i++] = pArgCurChar = p; /* store preliminary ptr to arg */ - while ( 1 ) { - bCopyCharToArg = 1; - nNumBackSlashes = 0; - while (*p == '\\') { - ++p; - ++nNumBackSlashes; - } - /* each pair of backslashes => one backslash; one more backslash => literal quote */ - if ( *p == '\"' ) { - /* one " found */ - if ( nNumBackSlashes % 2 == 0 ) { - if (bInsideQuotes) { - if (*(p+1) == '\"') { - p++; - } else { - bCopyCharToArg = 0; - } - } else { - bCopyCharToArg = 0; - } - bInsideQuotes = !bInsideQuotes; - } - nNumBackSlashes /= 2; /* divide nNumBackSlashes by two */ - } - while (nNumBackSlashes--) { - *pArgCurChar++ = '\\'; - } - if (!*p) { - break; - } - if (!bInsideQuotes && (*p == ' ' || *p == '\t')) { - p ++; - /* move to the next char because this char may become - * zero due to *pArgCurChar++ = '\0'; line below */ - break; - } - if (bCopyCharToArg) { - *pArgCurChar++ = *p; - } - ++p; - } - *pArgCurChar++ = '\0'; /* argument zero termination */ - } - /* The last argument is NULL */ - argv[i] = NULL; - return i; -} -/*****************************************************************/ -#define MIN_BOND_LENGTH (1.0e-6) -int SetAtomProperties( inp_ATOM *at, MOL_COORD *szCoord, inchi_Atom *ati, int a1, int *nDim, char *pStrErr, int *err ) -{ - S_CHAR cRadical; - /* element, check later */ - - strcpy( at[a1].elname, ati[a1].elname ); - - /* charge */ - - at[a1].charge = ati[a1].charge; - - /* radical */ - - switch ( ati[a1].radical ) { - case INCHI_RADICAL_NONE: - cRadical = 0; - break; - case INCHI_RADICAL_SINGLET: -#if( SINGLET_IS_TRIPLET == 1) /* 'singlet' means two electrons make a lone pair instead of 2 bonds*/ - /* its effect on valence is same as the effect of a triplet */ - cRadical = RADICAL_TRIPLET; -#else - cRadical = RADICAL_SINGLET; -#endif - break; - case INCHI_RADICAL_DOUBLET: - cRadical = RADICAL_DOUBLET; - break; - case INCHI_RADICAL_TRIPLET: - cRadical = RADICAL_TRIPLET; - break; - default: - { - char szRadicalType[16]; - int nRad = ati[a1].radical; - while ( nRad > RADICAL_TRIPLET ) { - nRad -= 2; - } - sprintf( szRadicalType, "%d->%d", ati[a1].radical, nRad ); - MOLFILE_ERR_SET (*err, 0, "Radical center type replaced:"); - MOLFILE_ERR_SET (*err, 0, szRadicalType); - cRadical = nRad; - if ( nRad < 0 ) { - *err |= 8; /* Unrecognized Radical replaced with non-radical */ - } - } - break; - } - at[a1].radical = cRadical; - - /* coordinates */ - at[a1].x = ati[a1].x; - at[a1].y = ati[a1].y; - at[a1].z = ati[a1].z; - - if ( szCoord ) { - /* store text coordinates */ - char str[32]; - MOL_COORD * coord_p = szCoord + a1; - WriteCoord( str, ati[a1].x ); - memcpy( *coord_p, str, 10 ); - WriteCoord( str, ati[a1].y ); - memcpy( *coord_p+10, str, 10 ); - WriteCoord( str, ati[a1].z ); - memcpy( *coord_p+20, str, 10 ); - } - - if ( MIN_BOND_LENGTH < fabs(ati[a1].x) || MIN_BOND_LENGTH < fabs(ati[a1].y) || MIN_BOND_LENGTH < fabs(ati[a1].z) ) { - if ( MIN_BOND_LENGTH < fabs(ati[a1].z) ) { - *nDim |= 3; - } else { - *nDim |= 2; - } - } - - /* orig. at. number */ - at[a1].orig_at_number = a1+1; - return 0; -#undef MIN_BOND_LENGTH -} -/*********************************************************************/ -int SetBondProperties( inp_ATOM *at, inchi_Atom *ati, int a1, int j, - int nNumAtoms, int *nNumBonds, char *pStrErr, int *err ) -{ - int a2; - S_CHAR cBondType, cStereoType1, cStereoType2; - AT_NUMB *p1, *p2; - int n1, n2; - - /* bond type */ - switch( ati[a1].bond_type[j] ) { - case INCHI_BOND_TYPE_SINGLE: - cBondType = BOND_TYPE_SINGLE; - break; - case INCHI_BOND_TYPE_DOUBLE: - cBondType = BOND_TYPE_DOUBLE; - break; - case INCHI_BOND_TYPE_TRIPLE: - cBondType = BOND_TYPE_TRIPLE; - break; - case INCHI_BOND_TYPE_ALTERN: - cBondType = BOND_TYPE_ALTERN; - break; - default: - { - char szBondType[16]; - sprintf( szBondType, "%d", ati[a1].bond_type[j] ); - MOLFILE_ERR_SET (*err, 0, "Unrecognized bond type:"); - MOLFILE_ERR_SET (*err, 0, szBondType); - *err |= 8; /* Unrecognized Bond type replaced with single bond */ - cBondType = BOND_TYPE_SINGLE; - } - break; - } - - /* 2D stereo */ - - switch( ati[a1].bond_stereo[j] ) { - /* stereocenter-related; positive: the sharp end points to this atom */ - case INCHI_BOND_STEREO_NONE: - cStereoType1 = 0; - cStereoType2 = 0; - break; - case INCHI_BOND_STEREO_SINGLE_1UP: - cStereoType1 = STEREO_SNGL_UP; - cStereoType2 = -STEREO_SNGL_UP; - break; - case INCHI_BOND_STEREO_SINGLE_1EITHER: - cStereoType1 = STEREO_SNGL_EITHER; - cStereoType2 = -STEREO_SNGL_EITHER; - break; - case INCHI_BOND_STEREO_SINGLE_1DOWN: - cStereoType1 = STEREO_SNGL_DOWN; - cStereoType2 = -STEREO_SNGL_DOWN; - break; - /* stereocenter-related; negative: the sharp end points to the opposite atom */ - case INCHI_BOND_STEREO_SINGLE_2UP: - cStereoType1 = -STEREO_SNGL_UP; - cStereoType2 = STEREO_SNGL_UP; - break; - case INCHI_BOND_STEREO_SINGLE_2EITHER: - cStereoType1 = -STEREO_SNGL_EITHER; - cStereoType2 = STEREO_SNGL_EITHER; - break; - case INCHI_BOND_STEREO_SINGLE_2DOWN: - cStereoType1 = -STEREO_SNGL_DOWN; - cStereoType2 = STEREO_SNGL_DOWN; - break; - /* stereobond-related */ - case INCHI_BOND_STEREO_DOUBLE_EITHER: - case -INCHI_BOND_STEREO_DOUBLE_EITHER: - cStereoType1 = STEREO_DBLE_EITHER; - cStereoType2 = STEREO_DBLE_EITHER; - break; - default: - { - char szBondType[16]; - sprintf( szBondType, "%d", ati[a1].bond_stereo[j] ); - MOLFILE_ERR_SET (*err, 0, "Unrecognized bond stereo:"); - MOLFILE_ERR_SET (*err, 0, szBondType); - *err |= 8; /* Unrecognized Bond stereo replaced with non-stereo bond */ - cStereoType1 = 0; - cStereoType2 = 0; - } - break; - } - - /* neighbor */ - - if ( ati[a1].neighbor[j] < 0 || ati[a1].neighbor[j] >= nNumAtoms ) { - *err |= 1; /* bond for impossible atom number(s); ignored */ - MOLFILE_ERR_SET (*err, 0, "Bond to nonexistent atom"); - goto err_exit; - } - a2 = (AT_NUMB) ati[a1].neighbor[j]; - if ( a2 == a1 ) { - *err |= 1; /* bond for impossible atom number(s); ignored */ - MOLFILE_ERR_SET (*err, 0, "Atom has a bond to itself"); - goto err_exit; - } - - /* consistency check; locate the bond in the opposite atom */ - - p1 = is_in_the_list( at[a1].neighbor, (AT_NUMB)a2, at[a1].valence ); - p2 = is_in_the_list( at[a2].neighbor, (AT_NUMB)a1, at[a2].valence ); - if ( p1 && p2 ) { - n1 = (p1 - at[a1].neighbor); - n2 = (p2 - at[a2].neighbor); - if ( n1+1 < at[a1].valence && is_in_the_list( at[a1].neighbor+n1+1, (AT_NUMB)a2, at[a1].valence-n1-1 ) || - n2+1 < at[a2].valence && is_in_the_list( at[a2].neighbor+n2+1, (AT_NUMB)a1, at[a2].valence-n2-1 ) ) { - MOLFILE_ERR_SET (*err, 0, "Multiple bonds between two atoms"); - *err |= 2; /* multiple bonds between atoms */ - } else - if ( n1 < at[a1].valence && n2 < at[a2].valence && - cBondType == at[a2].bond_type[n2] && - cBondType == at[a1].bond_type[n1] && - cStereoType1 == at[a1].bond_stereo[n1] && - cStereoType2 == at[a2].bond_stereo[n2] ) { - /*MOLFILE_ERR_SET (*err, 0, "Duplicated bond(s) between two atoms");*/ - } else { - MOLFILE_ERR_SET (*err, 0, "Multiple bonds between two atoms"); - *err |= 2; /* multiple bonds between atoms */ - } - } else - if ( (p1 || p2) && (p1 || at[a1].valence < MAXVAL) && (p2 || at[a2].valence < MAXVAL) ) { - n1 = p1? (p1 - at[a1].neighbor) : at[a1].valence ++; - n2 = p2? (p2 - at[a2].neighbor) : at[a2].valence ++; - /* the bond is present in one atom only: possibly program error */ - if ( p1 && (cBondType != at[a1].bond_type[n1] || at[a1].bond_stereo[n1] != cStereoType1 )|| - p2 && (cBondType != at[a2].bond_type[n2] || at[a2].bond_stereo[n2] != cStereoType2 ) ) { - MOLFILE_ERR_SET (*err, 0, "Multiple bonds between two atoms"); - *err |= 2; /* multiple bonds between atoms */ - } else { - MOLFILE_ERR_SET (*err, 0, "Duplicated bond(s) between two atoms"); - /* warning */ - } - } else - if ( !p1 && !p2 && at[a1].valence < MAXVAL && at[a2].valence < MAXVAL ) { - n1 = at[a1].valence ++; - n2 = at[a2].valence ++; - (*nNumBonds) ++; - } else { - char szMsg[64]; - *err |= 4; /* too large number of bonds. Some bonds ignored. */ - sprintf( szMsg, "Atom '%s' has more than %d bonds", - at[a1].valence>= MAXVAL? at[a1].elname:at[a2].elname, MAXVAL ); - MOLFILE_ERR_SET (*err, 0, szMsg); - goto err_exit; - } - - /* store the connection */ - - /* bond type */ - at[a1].bond_type[n1] = - at[a2].bond_type[n2] = cBondType; - /* connection */ - at[a1].neighbor[n1] = (AT_NUMB)a2; - at[a2].neighbor[n2] = (AT_NUMB)a1; - /* stereo */ - at[a1].bond_stereo[n1] = cStereoType1; /* >0: the wedge (pointed) end is at this atom */ - at[a2].bond_stereo[n2] = cStereoType2; /* <0: the wedge (pointed) end is at the opposite atom */ - return 0; -err_exit: - return 1; -} -/******************************************************************/ -int SetAtomAndBondProperties( inp_ATOM *at, inchi_Atom *ati, int a1, - int bDoNotAddH, char *pStrErr, int *err ) -{ - int valence, chem_valence, num_alt_bonds, j, n1; - int nRadical, nCharge; - static int el_number_H = 0; - - if ( !el_number_H ) { - el_number_H = get_periodic_table_number( "H" ); - } - - nRadical = nCharge = 0; - valence = at[a1].valence; - chem_valence = num_alt_bonds = 0; - for ( j = 0; j < valence; j ++ ) { - if ( at[a1].bond_type[j] <= BOND_TYPE_TRIPLE ) { - chem_valence += at[a1].bond_type[j]; - } else { - num_alt_bonds ++; - } - } - switch( num_alt_bonds ) { - case 0: - break; - case 2: - chem_valence += 3; /* -C= */ - break; - case 3: - chem_valence += 4; /* >C= */ - break; - default: - { - char szMsg[64]; - *err |= 8; /* wrong number of alt. bonds */ - sprintf( szMsg, "Atom '%s' has %d alternating bonds", - at[a1].elname, num_alt_bonds ); - MOLFILE_ERR_SET (*err, 0, szMsg); - } - break; - } - at[a1].chem_bonds_valence = chem_valence; - - /* aliased hydrogen atoms */ - if ( ERR_ELEM == (n1 = get_periodic_table_number( at[a1].elname ) ) ) { - /* Case when elname contains more than 1 element: extract number of H if possible */ - if ( extract_ChargeRadical( at[a1].elname, &nRadical, &nCharge ) ) { - if ( nRadical && at[a1].radical && nRadical != at[a1].radical || - nCharge && at[a1].charge && nCharge != at[a1].charge ) { - MOLFILE_ERR_SET (*err, 0, "Ignored charge/radical redefinition:"); - MOLFILE_ERR_SET (*err, 0, ati[a1].elname); - } else { - if ( nRadical ) - at[a1].radical = nRadical; - if ( nCharge ) - at[a1].charge = nCharge; - } - } - at[a1].num_H = extract_H_atoms( at[a1].elname, at[a1].num_iso_H ); - if ( !at[a1].elname[0] && NUMH(at, a1) ) { - /* alias contains only H. Added 2004-07-21, fixed 2004-07-22 - * move the heaviest isotope to the "central atom" - * Note: this must be consistent with H-H treatment in remove_terminal_HDT() - */ - strcpy( at[a1].elname, "H" ); - if ( NUM_ISO_H(at,a1) ) { - for ( j = NUM_H_ISOTOPES-1; 0 <= j; j -- ) { - if ( at[a1].num_iso_H[j] ) { - at[a1].num_iso_H[j] --; - at[a1].iso_atw_diff = 1 + j; - break; - } - } - } else { - at[a1].num_H --; - } - } - if ( ERR_ELEM == (n1 = get_periodic_table_number( at[a1].elname ) ) ) { - n1 = 0; - } - if ( n1 ) { - at[a1].at_type |= 1; /* "Aliased" atom: data in the element name */ - MOLFILE_ERR_SET (*err, 0, "Parsed compound atom(s):"); - MOLFILE_ERR_SET (*err, 0, ati[a1].elname); - } - } - - at[a1].el_number = (U_CHAR) n1; - if ( !n1 ) { - *err |= 64; /* Unrecognized aromatic bond(s) replaced with single */ - MOLFILE_ERR_SET (*err, 0, "Unknown element(s):"); - MOLFILE_ERR_SET (*err, 0, at[a1].elname); - } else - /* replace explicit D or T with isotopic H (added 2003-06-02) */ - if ( el_number_H == n1 && !at[a1].iso_atw_diff ) { - switch( at[a1].elname[0] ) { - case 'D': - at[a1].iso_atw_diff = 2; - mystrncpy( at[a1].elname, "H", sizeof(at->elname) ); - break; - case 'T': - at[a1].iso_atw_diff = 3; - mystrncpy( at[a1].elname, "H", sizeof(at->elname) ); - break; - case 'H': - if ( 1 <= ati[a1].isotopic_mass ) { - AT_NUM iso_atw_diff; - if ( ISOTOPIC_SHIFT_FLAG - ISOTOPIC_SHIFT_MAX <= ati[a1].isotopic_mass && - ISOTOPIC_SHIFT_FLAG + ISOTOPIC_SHIFT_MAX >= ati[a1].isotopic_mass ) { - /* ati[a1].isotopic_mass is isotopic iso_atw_diff + ISOTOPIC_SHIFT_FLAG */ - iso_atw_diff = ati[a1].isotopic_mass - ISOTOPIC_SHIFT_FLAG; - } else { - /* ati[a1].isotopic_mass is isotopic mass */ - iso_atw_diff = get_atw_from_elnum( (int) at[a1].el_number ); - iso_atw_diff = ati[a1].isotopic_mass - iso_atw_diff; - } - if ( iso_atw_diff >= 0 ) - iso_atw_diff ++; - /* reproduce Bug04: allowed non-terminal H heavier than T */ - if ( 1 <= iso_atw_diff && - (at[a1].valence != 1 || iso_atw_diff <= NUM_H_ISOTOPES) ) { - at[a1].iso_atw_diff = (S_CHAR)iso_atw_diff; - } - } - } - } else - /* isotopic shift */ - if ( ati[a1].isotopic_mass ) { - AT_NUM iso_atw_diff; - if ( ISOTOPIC_SHIFT_FLAG - ISOTOPIC_SHIFT_MAX <= ati[a1].isotopic_mass && - ISOTOPIC_SHIFT_FLAG + ISOTOPIC_SHIFT_MAX >= ati[a1].isotopic_mass ) { - /* ati[a1].isotopic_mass is isotopic iso_atw_diff + ISOTOPIC_SHIFT_FLAG */ - iso_atw_diff = ati[a1].isotopic_mass - ISOTOPIC_SHIFT_FLAG; - } else { - /* ati[a1].isotopic_mass is isotopic mass */ - iso_atw_diff = get_atw_from_elnum( (int) at[a1].el_number ); - iso_atw_diff = ati[a1].isotopic_mass - iso_atw_diff; - } - if ( iso_atw_diff >= 0 ) - iso_atw_diff ++; - at[a1].iso_atw_diff = (S_CHAR)iso_atw_diff; - } - - /* add implicit hydrogen atoms flag */ - - if ( ati[a1].num_iso_H[0] == -1 ) { - if ( !bDoNotAddH ) { - at[a1].at_type |= 2; /* user requested to add H */ - } - } else { - at[a1].num_H = ati[a1].num_iso_H[0]; - } - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { - at[a1].num_iso_H[j] = ati[a1].num_iso_H[j+1]; - } - if ( num_alt_bonds ) { - /* atom has aromatic bonds AND the chemical valence is not known */ - int num_H = NUMH(at, a1); - int chem_valence_alt = at[a1].chem_bonds_valence + num_H; - int bUnusualValenceArom = - detect_unusual_el_valence( (int)at[a1].el_number, at[a1].charge, - at[a1].radical, chem_valence_alt, - num_H, at[a1].valence ); - int bUnusualValenceNoArom = - detect_unusual_el_valence( (int)at[a1].el_number, at[a1].charge, - at[a1].radical, chem_valence_alt-1, - num_H, at[a1].valence ); - if ( bUnusualValenceArom && !bUnusualValenceNoArom && 0 == nBondsValToMetal( at, a1) ) { - /* typically NH in 5-member aromatic ring */ - at[a1].chem_bonds_valence --; - } - } - - return 0; -} -/****************************************************************************************/ -int InpAtom0DToInchiAtom( inp_ATOM *at, int num_atoms, inchi_OutputStruct *outStruct ) -{ - int num_stereo_centers, num_stereo_bonds, num_stereo0D, i, m, m1, m2, n, ret=0; - /* count stereobonds, allenes. cumulenes. and stereoatoms */ - num_stereo_centers = num_stereo_bonds = ret = 0; - - outStruct->atom = NULL; - outStruct->num_atoms = 0; - outStruct->stereo0D = NULL; - outStruct->num_stereo0D = 0; - - for ( i = 0; i < num_atoms; i ++ ) { - if ( at[i].p_parity ) { - /* stereocenter */ - num_stereo_centers ++; - } else { - for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[i].sb_parity[m]; m ++ ) - ; - num_stereo_bonds += m; - } - } - num_stereo_bonds /= 2; - num_stereo0D = num_stereo_bonds + num_stereo_centers; - - if ( num_atoms > 0 ) { - outStruct->atom = (inchi_Atom *)inchi_calloc( num_atoms, sizeof( outStruct->atom[0] ) ); - } - outStruct->num_atoms = num_atoms; - if ( num_stereo0D > 0 ) { - outStruct->stereo0D = (inchi_Stereo0D *)inchi_calloc( num_stereo0D, sizeof(outStruct->stereo0D[0])); - } - if ( num_atoms && !outStruct->atom || num_stereo0D > 0 && !outStruct->stereo0D ) { - /* allocation failed */ - ret = -1; - goto exit_function; - } - - /* copy atom properties */ - for ( i = 0; i < num_atoms; i ++ ) { - outStruct->atom[i].num_bonds = at[i].valence; - for ( m = 0; m < at[i].valence; m ++ ) { - outStruct->atom[i].bond_type[m] = at[i].bond_type[m]; - outStruct->atom[i].neighbor[m] = at[i].neighbor[m]; - } - outStruct->atom[i].charge = at[i].charge; - memcpy( outStruct->atom[i].elname, at[i].elname, ATOM_EL_LEN ); - if ( at[i].iso_atw_diff ) { - outStruct->atom[i].isotopic_mass = ISOTOPIC_SHIFT_FLAG + (at[i].iso_atw_diff > 0? at[i].iso_atw_diff-1 : at[i].iso_atw_diff); - } - outStruct->atom[i].num_iso_H[0] = at[i].num_H; - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - outStruct->atom[i].num_iso_H[m+1] = at[i].num_iso_H[m]; - } - outStruct->atom[i].radical = at[i].radical; - } - /* stereo */ - for ( i = n = 0; i < num_atoms; i ++ ) { - if ( at[i].p_parity ) { - if ( n < num_stereo0D ) { - outStruct->stereo0D[n].central_atom = i; - outStruct->stereo0D[n].parity = at[i].p_parity; - outStruct->stereo0D[n].type = INCHI_StereoType_Tetrahedral; - for ( m = 0; m < MAX_NUM_STEREO_ATOM_NEIGH; m ++ ) { - outStruct->stereo0D[n].neighbor[m] = at[i].p_orig_at_num[m] - 1; - } - n ++; - } else { - ret |= 1; - break; - } - } else { - for ( m1 = 0; m1 < MAX_NUM_STEREO_BONDS && at[i].sb_parity[m1]; m1 ++ ) { - - /* find the opposite atom at the other end of double bond, allene, or cumulene */ - int chain[12], len = 0, nxt_neigh, nxt, cur; - cur = chain[len++] = i; - nxt_neigh = at[cur].sb_ord[m1]; - - do { - /* add next atom */ - chain[len ++] = nxt = at[cur].neighbor[nxt_neigh]; - nxt_neigh = (at[nxt].neighbor[0] == cur); - cur = nxt; - /* find nxt_neigh */ - } while ( !at[cur].sb_parity[0] && len < 12 && at[cur].valence == 2 ); - - if ( at[cur].sb_parity[0] && len <= 4 && i < cur /* count bonds only one time */ ) { - /* double bond, cumulene, or allene has been found */ - for ( m2 = 0; m2 < MAX_NUM_STEREO_BONDS && at[cur].sb_parity[m2]; m2 ++ ) { - if ( chain[len-2] == at[cur].neighbor[(int)at[cur].sb_ord[m2]] ) { - if ( n < num_stereo0D ) { - int parity1 = at[i].sb_parity[m1]; - int parity2 = at[cur].sb_parity[m2]; - int parity; - if ( (INCHI_PARITY_ODD == parity1 || INCHI_PARITY_EVEN == parity1) && - (INCHI_PARITY_ODD == parity2 || INCHI_PARITY_EVEN == parity2) ) { - /* well-defined parity */ - parity = (parity1==parity2)? INCHI_PARITY_EVEN : INCHI_PARITY_ODD; - } else { - parity = inchi_max(parity1, parity2); - } - outStruct->stereo0D[n].central_atom = (len==3)? chain[1] : NO_ATOM; - outStruct->stereo0D[n].parity = parity; - outStruct->stereo0D[n].type = len == 3? INCHI_StereoType_Allene : INCHI_StereoType_DoubleBond; - outStruct->stereo0D[n].neighbor[0] = at[i].sn_orig_at_num[m1]-1; - outStruct->stereo0D[n].neighbor[1] = i; - outStruct->stereo0D[n].neighbor[2] = cur; - outStruct->stereo0D[n].neighbor[3] = at[cur].sn_orig_at_num[m2] - 1; - n ++; - } else { - ret |= 1; - } - break; - } - } - } - } - } - } - outStruct->num_stereo0D = n; -exit_function: - if ( ret < 0 ) { - if ( outStruct->atom ) inchi_free( outStruct->atom ); - if ( outStruct->stereo0D ) inchi_free( outStruct->stereo0D ); - outStruct->atom = NULL; - outStruct->stereo0D = NULL; - outStruct->num_atoms = 0; - outStruct->num_stereo0D = 0; - } - return ret; -} -/****************************************************************************************/ -int ExtractOneStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, - inchi_Input *inp, - INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *prb_file, - ORIG_ATOM_DATA *orig_inp_data, long *num_inp, - char *pStr, int nStrLen ) -{ - int *err = &sd->nStructReadError; - char *pStrErr = sd->pStrErrStruct; - inp_ATOM *at = NULL; - MOL_COORD *szCoord = NULL; - inchi_Atom *ati = NULL; - int nNumAtoms = 0; - int a1, j, valence, nDim, nNumBonds, nRet = 0; - - /* vABParityUnknown holds actual value of an internal constant signifying */ - /* unknown parity: either the same as for undefined parity (default==standard) */ - /* or a specific one (non-std; requested by SLUUD switch). */ - int vABParityUnknown = AB_PARITY_UNDF; - if ( 0 != ( ip->nMode & REQ_MODE_DIFF_UU_STEREO) ) - { - /* Make labels for unknown and undefined stereo different */ - vABParityUnknown = AB_PARITY_UNKN; - } - - /******************************************************** - * - * Extract the structure - * - ********************************************************/ - - FreeOrigAtData( orig_inp_data ); - nDim = 0; - nNumBonds = 0; - - if ( !inp || (nNumAtoms = inp->num_atoms) <= 0 || !(ati = inp->atom) ) { - MOLFILE_ERR_SET (*err, 0, "Empty structure"); - *err = 98; - goto err_exit; - } - if ( nNumAtoms >= MAX_ATOMS ) { - MOLFILE_ERR_SET (*err, 0, "Too many atoms"); - *err = 70; - orig_inp_data->num_inp_atoms = -1; - goto err_exit; - } - - at = (inp_ATOM *) inchi_calloc( nNumAtoms, sizeof(at[0]) ); - szCoord = (MOL_COORD *) inchi_calloc (inchi_max(nNumAtoms, 1), sizeof (MOL_COORD)); - - if ( !at || !szCoord ) { - MOLFILE_ERR_SET (*err, 0, "Out of RAM"); - *err = -1; - goto err_exit; - } - - - /******************************************************** - * - * Extract typical for Molfile structural data - * - ********************************************************/ - /* extract atoms and bonds */ - for ( a1 = 0; a1 < nNumAtoms; a1 ++ ) { - /* extract atoms */ - SetAtomProperties( at, szCoord, ati, a1, &nDim, pStrErr, err ); - if ( *err ) { - goto err_exit; - } - /* extract connections */ - valence = ati[a1].num_bonds; - for ( j = 0; j < valence; j ++ ) { - SetBondProperties( at, ati, a1, j, nNumAtoms, &nNumBonds, pStrErr, err ); - } - if ( *err ) { - goto err_exit; - } - } - - orig_inp_data->num_inp_atoms = nNumAtoms; - orig_inp_data->num_inp_bonds = nNumBonds; - orig_inp_data->num_dimensions = nDim; - - /* extract elements, chemical valences, implicit H, isotopic shifts */ - for ( a1 = 0; a1 < nNumAtoms; a1 ++ ) { - /* set temp flags in at[a1].at_type (1: data in atom name; 2: request to add H) */ - SetAtomAndBondProperties( at, ati, a1, ip->bDoNotAddH, pStrErr, err ); - if ( *err ) { - goto err_exit; - } - } - /* clear temp flags in at[].at_type; add implicit H */ - SetNumImplicitH( at, nNumAtoms ); - if ( *err ) { - goto err_exit; - } - - /******************************************************** - * - * Extract the 0D parities (typical for CML) - * - ********************************************************/ - Extract0DParities(at, nNumAtoms, inp->stereo0D, inp->num_stereo0D, - pStrErr, err, vABParityUnknown); - - if ( *err ) { - goto err_exit; - } - orig_inp_data->at = at; at = NULL; - orig_inp_data->num_dimensions = nDim; - orig_inp_data->num_inp_atoms = nNumAtoms; - orig_inp_data->num_inp_bonds = nNumBonds; - orig_inp_data->szCoord = szCoord; szCoord = NULL; - - /* chiral flag */ - /* ***************************************************************************** - * Chiral flags are set in: - * - RunICHI.c #1610 -- ReadTheStructure() -- cInChI, wInChI - * - e_IchiMain.c #273 -- main() -- C example of calling InChI dll - * - inchi_dll.c #1662 -- ExtractOneStructure -- InChI dll code (here) - *******************************************************************************/ - if ( (ip->nMode & REQ_MODE_CHIR_FLG_STEREO) && (ip->nMode & REQ_MODE_STEREO) ) { - if ( ip->bChiralFlag & FLAG_SET_INP_AT_CHIRAL ) { - /* absolute stereo */ - ip->nMode &= ~(REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO); - sd->bChiralFlag &= ~FLAG_INP_AT_NONCHIRAL; - sd->bChiralFlag |= FLAG_INP_AT_CHIRAL; /* write AuxInfo as chiral */ - } else - /*if ( ip->bChiralFlag & FLAG_SET_INP_AT_NONCHIRAL )*/ { - /* relative stereo */ - ip->nMode &= ~(REQ_MODE_RACEMIC_STEREO); - ip->nMode |= REQ_MODE_RELATIVE_STEREO; - sd->bChiralFlag &= ~FLAG_INP_AT_CHIRAL; - sd->bChiralFlag |= FLAG_INP_AT_NONCHIRAL; /* write AuxInfo as non-chiral */ - } - } else - if ( ip->bChiralFlag & FLAG_SET_INP_AT_CHIRAL ) { - sd->bChiralFlag &= ~FLAG_INP_AT_NONCHIRAL; - sd->bChiralFlag |= FLAG_INP_AT_CHIRAL; /* write AuxInfo as chiral */ - } else - if ( ip->bChiralFlag & FLAG_SET_INP_AT_NONCHIRAL ) { - sd->bChiralFlag &= ~FLAG_INP_AT_CHIRAL; - sd->bChiralFlag |= FLAG_INP_AT_NONCHIRAL; /* write AuxInfo as non-chiral */ - } - - *num_inp += 1; - -err_exit: - - if ( at ) - inchi_free( at ); - if ( szCoord ) - inchi_free( szCoord ); - - nRet = TreatReadTheStructureErrors( sd, ip, LOG_MASK_NO_WARN, NULL, log_file, output_file, prb_file, - orig_inp_data, num_inp, pStr, nStrLen ); - - return nRet; - -} -/********************************************************/ -int INCHI_DECL GetStringLength( char *p ) -{ - if ( p ) { - return strlen(p); - } else { - return 0; - } -} -#define MAX_MSG_LEN 512 - - - -/* GetINCHIfromINCHI does same as -InChI2InChI option: converts InChI into InChI for validation purposes */ -/* It may also be used to filter out specific layers. For instance, /Snon would remove stereochemical layer */ -/* Omitting /FixedH and/or /RecMet would remove Fixed-H or Reconnected layers */ -/* To keep all InChI layers use options string "/FixedH /RecMet"; option /InChI2InChI is not needed */ -/* inchi_InputINCHI is created by the user; strings in inchi_Output are allocated and deallocated by InChI */ -/* inchi_Output does not need to be initilized out to zeroes; see FreeINCHI() on how to deallocate it */ -/*************************************************************/ -int INCHI_DECL GetINCHIfromINCHI( inchi_InputINCHI *inpInChI, inchi_Output *out ) -{ - STRUCT_DATA struct_data; - STRUCT_DATA *sd = &struct_data; - - static char szMainOption[] = " ?InChI2InChI"; - - int i; - char szSdfDataValue[MAX_SDF_VALUE+1]; - unsigned long ulDisplTime = 0; /* infinite, milliseconds */ - - INPUT_PARMS inp_parms; - INPUT_PARMS *ip = &inp_parms; - - int bReleaseVersion = bRELEASE_VERSION; - int nRet = 0, nRet1; - -#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) - int num_repeat = REPEAT_ALL; -#endif - - const char *argv[INCHI_MAX_NUM_ARG+1]; - int argc; - char *szOptions = NULL; - - INCHI_IOSTREAM inchi_file[3], *output_file = inchi_file, *log_file = inchi_file+1, *input_file = inchi_file+2; - - - - - if ( bLibInchiSemaphore ) { /* does not work properly under sufficient stress */ - return inchi_Ret_BUSY; - } - bLibInchiSemaphore = 1; - -#if( TRACE_MEMORY_LEAKS == 1 ) - _CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF); -/* for execution outside the VC++ debugger uncomment one of the following two */ -#ifdef MY_REPORT_FILE - _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_WARN, MY_REPORT_FILE ); - _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_ERROR, MY_REPORT_FILE ); - _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_ASSERT, MY_REPORT_FILE ); -#else - _CrtSetReportMode(_CRT_WARN | _CRT_ERROR, _CRTDBG_MODE_DEBUG); -#endif - - /* turn on floating point exceptions */ -#if ( !defined(__STDC__) || __STDC__ != 1 ) - { - /* Get the default control word. */ - int cw = _controlfp( 0,0 ); - - /* Set the exception masks OFF, turn exceptions on. */ - /*cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_INEXACT|EM_ZERODIVIDE|EM_DENORMAL);*/ - cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE|EM_DENORMAL); - - /* Set the control word. */ - _controlfp( cw, MCW_EM ); - - } -#endif -#endif - - - - memset( out, 0, sizeof(*out) ); -#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) -repeat: - FreeINCHI( out ); - inchi_ios_close(output_file); - inchi_ios_close(log_file); - inchi_ios_reset(input_file); /* do not close input_file - its string buffer may point to inpInChI->szInChI */ -#endif - - /*^^^ Initialize internal for this function I/O streams as string buffers */ - inchi_ios_init(input_file, INCHI_IOSTREAM_STRING, NULL); - inchi_ios_init(output_file, INCHI_IOSTREAM_STRING, NULL); - inchi_ios_init(log_file, INCHI_IOSTREAM_STRING, NULL); - - - sd->bUserQuit = 0; - - /* clear original input structure */ - /*^^^ memset( inchi_file, 0, sizeof(inchi_file) ); */ - memset( sd, 0, sizeof(*sd) ); - memset( ip, 0, sizeof(*ip) ); - memset( szSdfDataValue , 0, sizeof( szSdfDataValue ) ); - szMainOption[1] = INCHI_OPTION_PREFX; - - if ( !inpInChI ) { - nRet = _IS_ERROR; - goto exit_function; - } - - /* options */ - if ( inpInChI ) { - int opt_len = (inpInChI->szOptions? strlen(inpInChI->szOptions) : 0) + sizeof(szMainOption) + 1; - szOptions = (char*)inchi_calloc( opt_len+1, sizeof(szOptions[0]) ); - if ( szOptions ) { - if ( inpInChI->szOptions ) { - strcpy( szOptions, inpInChI->szOptions ); - } - strcat( szOptions, szMainOption ); - argc = parse_options_string ( szOptions, argv, INCHI_MAX_NUM_ARG ); - } else { - nRet = _IS_FATAL; - goto translate_RetVal; /* emergency exit */ - } - } else { - argc = 1; - argv[0] = ""; - argv[1] = NULL; - } - - if ( argc == 1 -#ifdef TARGET_API_LIB - && (!inpInChI || !inpInChI->szInChI) -#endif - || argc==2 && ( argv[1][0]==INCHI_OPTION_PREFX ) && - (!strcmp(argv[1]+1, "?") || !stricmp(argv[1]+1, "help") ) ) { - HelpCommandLineParms(log_file); - out->szLog = log_file->s.pStr; - memset( log_file, 0, sizeof(*log_file) ); - nRet = _IS_EOF; - goto translate_RetVal; - } - - nRet1 = ReadCommandLineParms( argc, argv, ip, szSdfDataValue, &ulDisplTime, bReleaseVersion, log_file ); - if ( szOptions ) { - /* argv pointed to strings in szOptions */ - inchi_free( szOptions ); - szOptions = NULL; - } - /* INChI DLL specific */ - ip->bNoStructLabels = 1; - - if ( 0 > nRet1 ) { - goto exit_function; - } - if ( ip->bNoStructLabels ) { - ip->pSdfLabel = NULL; - ip->pSdfValue = NULL; - } else - if ( ip->nInputType == INPUT_INCHI_XML || ip->nInputType == INPUT_INCHI_PLAIN || - ip->nInputType == INPUT_CMLFILE || ip->nInputType == INPUT_INCHI ) { - /* the input may contain both the header and the label of the structure */ - if ( !ip->pSdfLabel ) - ip->pSdfLabel = ip->szSdfDataHeader; - if ( !ip->pSdfValue ) - ip->pSdfValue = szSdfDataValue; - } - if ( ip->nInputType && ip->nInputType != INPUT_INCHI ) { - inchi_ios_eprint( log_file, "Input type set to INPUT_INCHI\n" ); - ip->nInputType = INPUT_INCHI; - } - - PrintInputParms( log_file, ip ); - /*********************************/ - /* InChI -> Structure conversion */ - /*********************************/ - - /* input_file simulation */ - input_file->s.pStr = inpInChI->szInChI; - input_file->s.nUsedLength = strlen(input_file->s.pStr)+1; - input_file->s.nAllocatedLength = input_file->s.nUsedLength; - input_file->s.nPtr = 0; - - /* buffer for the message */ - out->szMessage = (char *)inchi_calloc( MAX_MSG_LEN, sizeof(out->szMessage[0])); - if ( !out->szMessage ) { - inchi_ios_eprint( log_file, "Cannot allocate output message buffer.\n"); - nRet = -1; - } else { - nRet = ReadWriteInChI( input_file, output_file, log_file, - ip, sd, NULL, NULL, out->szMessage, MAX_MSG_LEN, NULL /*out->WarningFlags*/ ); - } - - if ( nRet >= 0 && output_file->s.pStr ) - { - /* success */ - char *p; - out->szInChI = output_file->s.pStr; - out->szAuxInfo = NULL; - for ( p = strchr(out->szInChI, '\n'); p; p = strchr(p+1, '\n') ) { - if ( !memcmp( p, "\nAuxInfo", 8 ) ) { - *p = '\0'; /* remove LF after INChI */ - out->szAuxInfo = p+1; /* save pointer to AuxInfo */ - } else - if ( out->szAuxInfo || !p[1]) { /* remove LF after aux info or from the last char */ - *p = '\0'; - break; - } - } - output_file->s.pStr = NULL; - } - /* - out->szLog = log_file->pStr; - log_file->pStr = NULL; - */ - -exit_function:; - -#if( ADD_CMLPP == 1 ) - /* BILLY 8/6/04 */ - /* free CML memory */ - FreeCml (); - FreeCmlDoc( 1 ); -#endif - - - - for ( i = 0; i < MAX_NUM_PATHS; i ++ ) { - if ( ip->path[i] ) { - inchi_free( (char*) ip->path[i] ); /* cast deliberately discards 'const' qualifier */ - ip->path[i] = NULL; - } - } - - SetBitFree( ); - - -#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) - if ( num_repeat-- > 0 ) { - goto repeat; - } -#endif - - -#ifdef TARGET_API_LIB - /* output */ - - if ( log_file->s.pStr && log_file->s.nUsedLength > 0 ) { - while ( log_file->s.nUsedLength && '\n' == log_file->s.pStr[log_file->s.nUsedLength-1] ) { - log_file->s.pStr[-- log_file->s.nUsedLength] = '\0'; /* remove last LF */ - } - if ( out ) { - out->szLog = log_file->s.pStr; - log_file->s.pStr = NULL; - } - } - - -#endif - - -translate_RetVal: - - /* Close internal output streams */ - inchi_ios_close(output_file); - inchi_ios_close(log_file); - inchi_ios_reset(input_file); /* do not close input_file - its string buffer may point to inpInChI->szInChI */ - - - switch (nRet) { - case -3 : nRet = inchi_Ret_ERROR ; break; /* Error: no Structure has been created */ - case -2 : nRet = inchi_Ret_ERROR ; break; /* Error: no Structure has been created */ - case -1 : nRet = inchi_Ret_FATAL ; break; /* Severe error: no Structure has been created (typically; break; memory allocation failed) */ - default : - /* - if ( !outStruct->atom || !outStruct->num_atoms ) { - nRet = inchi_Ret_EOF; - } else { - int m,n,t=0; - for ( m=0; m < 2; m ++ ) { - for ( n=0; n < 2; n ++ ) { - if ( outStruct->WarningFlags[m][n] ) { - t ++; - } - } - } - nRet = t? inchi_Ret_WARNING : inchi_Ret_OKAY; - } - */ - break; - } - - bLibInchiSemaphore = 0; - return nRet; -} - - -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStructFromStdINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ) -{ - if ( ( inpInChI ) && - ( inpInChI->szInChI ) && - ( strlen(inpInChI->szInChI) >= LEN_INCHI_STRING_PREFIX+3 ) && - ( inpInChI->szInChI[LEN_INCHI_STRING_PREFIX+1] == 'S' ) - ) - /* brief check indicated valid std input (more checks in GetStructFromINCHI) */ - return GetStructFromINCHI( inpInChI, outStruct ); - else - /* non-std or just invalid input */ - return inchi_Ret_ERROR; -} - - - -/*************************************************************/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStructFromINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ) -{ - STRUCT_DATA struct_data; - STRUCT_DATA *sd = &struct_data; - - - INCHI_IOSTREAM inchi_file[3]; - INCHI_IOSTREAM *output_file = inchi_file, *log_file = inchi_file+1, *input_file = inchi_file+2; - - - static char szMainOption[] = " ?InChI2Struct"; - - - int i; - char szSdfDataValue[MAX_SDF_VALUE+1]; - unsigned long ulDisplTime = 0; /* infinite, milliseconds */ - - INPUT_PARMS inp_parms; - INPUT_PARMS *ip = &inp_parms; - - int bReleaseVersion = bRELEASE_VERSION; - int nRet = 0, nRet1; - int bStdFormat = 0; - - /* conversion result */ - inp_ATOM *at=NULL; - int num_at = 0; - -#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) - int num_repeat = REPEAT_ALL; -#endif - - const char *argv[INCHI_MAX_NUM_ARG+1]; - int argc; - char *szOptions = NULL; - - if ( bLibInchiSemaphore ) { /* does not work properly under sufficient stress */ - return inchi_Ret_BUSY; - } -#if 0 - /* moved to after call to CheckINCHI - Marc 2010 */ - bLibInchiSemaphore = 1; -#endif - -#if( TRACE_MEMORY_LEAKS == 1 ) - _CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF); -/* for execution outside the VC++ debugger uncomment one of the following two */ -#ifdef MY_REPORT_FILE - _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_WARN, MY_REPORT_FILE ); - _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_ERROR, MY_REPORT_FILE ); - _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_ASSERT, MY_REPORT_FILE ); -#else - _CrtSetReportMode(_CRT_WARN | _CRT_ERROR, _CRTDBG_MODE_DEBUG); -#endif - - /* turn on floating point exceptions */ -#if ( !defined(__STDC__) || __STDC__ != 1 ) - { - /* Get the default control word. */ - int cw = _controlfp( 0,0 ); - - /* Set the exception masks OFF, turn exceptions on. */ - /*cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_INEXACT|EM_ZERODIVIDE|EM_DENORMAL);*/ - cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE|EM_DENORMAL); - - /* Set the control word. */ - _controlfp( cw, MCW_EM ); - - } -#endif -#endif - - memset( outStruct, 0, sizeof(*outStruct) ); - -#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) -repeat: - FreeStructFromINCHI( &outStruct ); - inchi_ios_reset(input_file); /* do not close input_file - its string buffer may point to inpInChI->szInChI */ - inchi_ios_close(output_file); - inchi_ios_close(log_file); -#endif - - sd->bUserQuit = 0; - - - - /*^^^ Initialize internal for this function I/O streams as string buffers */ - inchi_ios_init(input_file, INCHI_IOSTREAM_STRING, NULL); - inchi_ios_init(output_file, INCHI_IOSTREAM_STRING, NULL); - inchi_ios_init(log_file, INCHI_IOSTREAM_STRING, NULL); - - - /* clear original input structure */ - - memset( sd, 0, sizeof(*sd) ); - memset( ip, 0, sizeof(*ip) ); - memset( szSdfDataValue , 0, sizeof( szSdfDataValue ) ); - szMainOption[1] = INCHI_OPTION_PREFX; - - if ( !inpInChI ) - { - nRet = _IS_ERROR; - goto exit_function; - } - - /* options */ - if ( inpInChI /*&& inpInChI->szOptions*/ ) { /* fix bug discovered by Burt Leland 2008-12-23 */ - int opt_len = (inpInChI->szOptions? strlen(inpInChI->szOptions) : 0) + sizeof(szMainOption) + 1; - szOptions = (char*)inchi_calloc( opt_len+1, sizeof(szOptions[0]) ); - if ( szOptions ) { - if ( inpInChI->szOptions ) /* fix bug discovered by Burt Leland 2008-12-23 */ - strcpy( szOptions, inpInChI->szOptions ); - strcat( szOptions, szMainOption ); - argc = parse_options_string ( szOptions, argv, INCHI_MAX_NUM_ARG ); - } else { - nRet = _IS_FATAL; - goto translate_RetVal; /* emergency exit */ - } - } else { - argc = 1; - argv[0] = ""; - argv[1] = NULL; - } - - if ( argc == 1 -#ifdef TARGET_API_LIB - && (!inpInChI || !inpInChI->szInChI) -#endif - || argc==2 && ( argv[1][0]==INCHI_OPTION_PREFX ) && - (!strcmp(argv[1]+1, "?") || !stricmp(argv[1]+1, "help") ) ) { - HelpCommandLineParms(log_file); - outStruct->szLog = log_file->s.pStr; - nRet = _IS_EOF; - goto translate_RetVal; - } - - nRet1 = ReadCommandLineParms( argc, argv, ip, szSdfDataValue, &ulDisplTime, bReleaseVersion, log_file ); - if ( szOptions ) { - /* argv pointed to strings in szOptions */ - inchi_free( szOptions ); - szOptions = NULL; - } - /* INChI DLL specific */ - ip->bNoStructLabels = 1; - - if ( 0 > nRet1 ) { - goto exit_function; - } - if ( ip->bNoStructLabels ) { - ip->pSdfLabel = NULL; - ip->pSdfValue = NULL; - } else - if ( ip->nInputType == INPUT_INCHI_XML || ip->nInputType == INPUT_INCHI_PLAIN || - ip->nInputType == INPUT_CMLFILE || ip->nInputType == INPUT_INCHI ) { - /* the input may contain both the header and the label of the structure */ - if ( !ip->pSdfLabel ) - ip->pSdfLabel = ip->szSdfDataHeader; - if ( !ip->pSdfValue ) - ip->pSdfValue = szSdfDataValue; - } - if ( ip->nInputType && ip->nInputType != INPUT_INCHI ) { - inchi_ios_eprint( log_file, "Input type set to INPUT_INCHI\n" ); - ip->nInputType = INPUT_INCHI; - } - - if ( !inpInChI->szInChI ) - { - nRet = _IS_ERROR; - goto exit_function; - } - else - { - const int strict=0; /* do not use strict mode, it may be too alarmous */ - nRet = CheckINCHI(inpInChI->szInChI, strict); - if (nRet == INCHI_VALID_STANDARD) - { - bStdFormat = 1; - } - else if (nRet == INCHI_VALID_NON_STANDARD) - { - ; - } - else - { - nRet = _IS_ERROR; - goto exit_function; - } - } - - - if ( bLibInchiSemaphore ) { /* does not work properly under sufficient stress */ - return inchi_Ret_BUSY; - } - bLibInchiSemaphore = 1; - - - PrintInputParms( log_file, ip ); - /*********************************/ - /* InChI -> Structure conversion */ - /*********************************/ - - /* input_file simulation */ - input_file->s.pStr = inpInChI->szInChI; - input_file->s.nUsedLength = strlen(input_file->s.pStr)+1; - input_file->s.nAllocatedLength = input_file->s.nUsedLength; - input_file->s.nPtr = 0; - /* buffer for the message */ - outStruct->szMessage = (char *)inchi_calloc( MAX_MSG_LEN, sizeof(outStruct->szMessage[0])); - if ( !outStruct->szMessage ) { - inchi_ios_eprint( log_file, "Cannot allocate output message buffer.\n"); - nRet = -1; - } else { - nRet = ReadWriteInChI( input_file, output_file, log_file, - ip, sd, &at, &num_at, outStruct->szMessage, MAX_MSG_LEN, outStruct->WarningFlags ); - } - if ( nRet >= 0 && at && num_at ) { - /* success */ - nRet = InpAtom0DToInchiAtom( at, num_at, outStruct ); - if ( at ) { - inchi_free( at ); - at = NULL; - } - if ( nRet < 0 ) { - inchi_ios_eprint( log_file, "Final structure conversion failed\n" ); - } - } - outStruct->szLog = log_file->s.pStr; - - -exit_function:; - -#if( ADD_CMLPP == 1 ) - /* BILLY 8/6/04 */ - /* free CML memory */ - FreeCml (); - FreeCmlDoc( 1 ); -#endif - - - - for ( i = 0; i < MAX_NUM_PATHS; i ++ ) { - if ( ip->path[i] ) { - inchi_free( (char*) ip->path[i] ); /* cast deliberately discards 'const' qualifier */ - ip->path[i] = NULL; - } - } - - SetBitFree( ); - - -#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) - if ( num_repeat-- > 0 ) { - goto repeat; - } -#endif - - -#ifdef TARGET_API_LIB - /* output */ - - if ( log_file->s.pStr && log_file->s.nUsedLength > 0 ) { - while ( log_file->s.nUsedLength && '\n' == log_file->s.pStr[log_file->s.nUsedLength-1] ) { - log_file->s.pStr[-- log_file->s.nUsedLength] = '\0'; /* remove last LF */ - } - if ( outStruct ) { - outStruct->szLog = log_file->s.pStr; - log_file->s.pStr = NULL; - } - } -#endif - -translate_RetVal: - - /* Close internal I/O streams */ - inchi_ios_reset(input_file); /* do not close input_file - its string buffer may point to inpInChI->szInChI */ - inchi_ios_close(output_file); - inchi_ios_close(log_file); - - switch (nRet) { - case -3 : nRet = inchi_Ret_ERROR ; break; /* Error: no Structure has been created */ - case -2 : nRet = inchi_Ret_ERROR ; break; /* Error: no Structure has been created */ - case -1 : nRet = inchi_Ret_FATAL ; break; /* Severe error: no Structure has been created (typically; break; memory allocation failed) */ - default : - if ( !outStruct->atom || !outStruct->num_atoms ) { - nRet = inchi_Ret_EOF; - } else { - int m,n,t=0; - for ( m=0; m < 2; m ++ ) { - for ( n=0; n < 2; n ++ ) { - if ( outStruct->WarningFlags[m][n] ) { - t ++; - } - } - } - nRet = t? inchi_Ret_WARNING : inchi_Ret_OKAY; - } - break; - } - - bLibInchiSemaphore = 0; - return nRet; -} - -/********************************************************************/ - -#if( defined( _WIN32 ) && defined( _MSC_VER ) && _MSC_VER >= 800 && defined(_USRDLL) && defined(BUILD_LINK_AS_DLL) ) - /* Win32 & MS VC ++, compile and link as a DLL */ -/*********************************************************/ -/* C calling conventions export from Win32 dll */ -/*********************************************************/ -/* prototypes */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -int cdecl_GetINCHI( inchi_Input *inp, inchi_Output *out ); -int cdecl_GetStdINCHI( inchi_Input *inp, inchi_Output *out ); -void cdecl_FreeINCHI( inchi_Output *out ); -void cdecl_FreeStdINCHI( inchi_Output *out ); -int cdecl_GetStringLength( char *p ); -int cdecl_Get_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, - int bDoNotAddH, int bDiffUnkUndfStereo, - InchiInpData *pInchiInp ); -int cdecl_Get_std_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, - int bDoNotAddH, - InchiInpData *pInchiInp ); -void cdecl_Free_inchi_Input( inchi_Input *pInp ); -void cdecl_Free_std_inchi_Input( inchi_Input *pInp ); -int cdecl_GetStructFromINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ); -int cdecl_GetStructFromStdINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ); -int cdecl_GetINCHIfromINCHI( inchi_InputINCHI *inpInChI, inchi_Output *out ); -void cdecl_FreeStructFromINCHI( inchi_OutputStruct *outStruct ); -void cdecl_FreeStructFromStdINCHI( inchi_OutputStruct *outStruct ); -int cdecl_CheckINCHI(const char *szINCHI, const int strict); -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -/* implementation */ -/* libinchi.def provides export without cdecl_ prefixes */ - -/********************************************************/ -int cdecl_GetINCHI( inchi_Input *inp, inchi_Output *out ) -{ - return GetINCHI( inp, out ); -} -/********************************************************/ -int cdecl_GetStdINCHI( inchi_Input *inp, inchi_Output *out ) -{ - return GetStdINCHI( inp, out ); -} -/********************************************************/ -void cdecl_FreeINCHI( inchi_Output *out ) -{ - FreeINCHI( out ); -} -/********************************************************/ -void cdecl_FreeStdINCHI( inchi_Output *out ) -{ - FreeStdINCHI( out ); -} -/********************************************************/ -int cdecl_GetStringLength( char *p ) -{ - return GetStringLength( p ); -} -/********************************************************/ -int cdecl_Get_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, - int bDoNotAddH, int bDiffUnkUndfStereo, - InchiInpData *pInchiInp ) -{ - return Get_inchi_Input_FromAuxInfo( szInchiAuxInfo, bDoNotAddH, bDiffUnkUndfStereo, - pInchiInp ); -} -/********************************************************/ -/********************************************************/ -int cdecl_Get_std_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, - int bDoNotAddH, - InchiInpData *pInchiInp ) -{ - return Get_std_inchi_Input_FromAuxInfo( szInchiAuxInfo, bDoNotAddH, pInchiInp ); -} -/********************************************************/ -void cdecl_Free_std_inchi_Input( inchi_Input *pInp ) -{ - Free_std_inchi_Input( pInp ); -} -/********************************************************/ -void cdecl_Free_inchi_Input( inchi_Input *pInp ) -{ - Free_inchi_Input( pInp ); -} -/********************************************************/ -int cdecl_GetStructFromINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ) -{ - return GetStructFromINCHI( inpInChI, outStruct ); -} -/********************************************************//********************************************************/ -int cdecl_GetStructFromStdINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ) -{ - return GetStructFromStdINCHI( inpInChI, outStruct ); -} -/********************************************************/ -void cdecl_FreeStructFromINCHI( inchi_OutputStruct *outStruct ) -{ - FreeStructFromINCHI( outStruct ); -} -/********************************************************/ -int cdecl_GetINCHIfromINCHI( inchi_InputINCHI *inpInChI, inchi_Output *out ) -{ - return GetINCHIfromINCHI( inpInChI, out ); -} -/********************************************************/ -void cdecl_FreeStructFromStdINCHI( inchi_OutputStruct *outStruct ) -{ - FreeStructFromStdINCHI( outStruct ); -} -/********************************************************/ -int cdecl_CheckINCHI(const char *szINCHI, const int strict) -{ - return CheckINCHI( szINCHI, strict ); -} -#endif - -#if( defined(__GNUC__) && __GNUC__ >= 3 && defined(__MINGW32__) && defined(_WIN32) ) -#include -/*********************************************************/ -/* Pacal calling conventions export from Win32 dll */ -/*********************************************************/ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif -/* prototypes */ - -int PASCAL pasc_GetINCHI( inchi_Input *inp, inchi_Output *out ); -int PASCAL pasc_GetStdINCHI( inchi_Input *inp, inchi_Output *out ); -void PASCAL pasc_FreeINCHI( inchi_Output *out ); -void PASCAL pasc_FreeStdINCHI( inchi_Output *out ); -int PASCAL pasc_GetStringLength( char *p ); -int PASCAL pasc_Get_std_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, - int bDoNotAddH, - InchiInpData *pInchiInp ); -int PASCAL pasc_Get_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, - int bDoNotAddH, - int bDiffUnkUndfStereo, - InchiInpData *pInchiInp ); -void PASCAL pasc_Free_inchi_Input( inchi_Input *pInp ); -void PASCAL pasc_Free_std_inchi_Input( inchi_Input *pInp ); -void PASCAL pasc_FreeStructFromINCHI( inchi_OutputStruct *out ); -void PASCAL pasc_FreeStructFromStdINCHI( inchi_OutputStruct *out ); -int PASCAL pasc_GetStructFromINCHI( inchi_InputINCHI *inp, inchi_OutputStruct *out ); -int PASCAL pasc_GetStructFromStdINCHI( inchi_InputINCHI *inp, inchi_OutputStruct *out ); -int PASCAL pasc_CheckINCHI(const char *szINCHI, const int strict); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -/* implementation */ -/* libinchi.def provides export without PASCAL pasc_ prefixes */ -/********************************************************/ -int PASCAL pasc_GetINCHI( inchi_Input *inp, inchi_Output *out ) -{ - return GetINCHI( inp, out ); -} -/********************************************************/ -int PASCAL pasc_GetStdINCHI( inchi_Input *inp, inchi_Output *out ) -{ - return GetStdINCHI( inp, out ); -} -/********************************************************/ -void PASCAL pasc_FreeINCHI( inchi_Output *out ) -{ - FreeINCHI( out ); -} -/********************************************************/ -void PASCAL pasc_FreeStdINCHI( inchi_Output *out ) -{ - FreeStdINCHI( out ); -} -/********************************************************/ -int PASCAL pasc_GetStringLength( char *p ) -{ - return GetStringLength( p ); -} -/********************************************************/ -int PASCAL pasc_Get_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, - int bDoNotAddH, - int bDiffUnkUndfStereo, - InchiInpData *pInchiInp ) -{ - return Get_inchi_Input_FromAuxInfo( szInchiAuxInfo, bDoNotAddH, - bDiffUnkUndfStereo, pInchiInp ); -} -/********************************************************/ -int PASCAL pasc_Get_std_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, - int bDoNotAddH, - InchiInpData *pInchiInp ) -{ - return Get_std_inchi_Input_FromAuxInfo( szInchiAuxInfo, bDoNotAddH, pInchiInp ); -} -/********************************************************/ -void PASCAL pasc_Free_inchi_Input( inchi_Input *pInp ) -{ - Free_inchi_Input( pInp ); -} -/********************************************************/ -void PASCAL pasc_Free_std_inchi_Input( inchi_Input *pInp ) -{ - Free_std_inchi_Input( pInp ); -} -/********************************************************/ -void PASCAL pasc_FreeStructFromINCHI( inchi_OutputStruct *out ) -{ - FreeStructFromINCHI( out ); -} -/********************************************************/ -void PASCAL pasc_FreeStructFromStdINCHI( inchi_OutputStruct *out ) -{ - FreeStructFromStdINCHI( out ); -} -/********************************************************//********************************************************/ -int PASCAL pasc_GetStructFromINCHI( inchi_InputINCHI *inp, inchi_OutputStruct *out ) -{ - return GetStructFromINCHI( inp, out ); -} -/********************************************************//********************************************************/ -int PASCAL pasc_GetStructFromStdINCHI( inchi_InputINCHI *inp, inchi_OutputStruct *out ) -{ - return GetStructFromStdINCHI( inp, out ); -} -/********************************************************/ -int PASCAL pasc_CheckINCHI(const char *szINCHI, const int strict) -{ - return CheckINCHI( szINCHI, strict ); -} - -#endif - - +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../../INCHI_BASE/src/mode.h" + +#include "../../../INCHI_BASE/src/incomdef.h" +#include "../../../INCHI_BASE/src/ichidrp.h" +#include "../../../INCHI_BASE/src/inpdef.h" +#include "../../../INCHI_BASE/src/ichi.h" +#include "../../../INCHI_BASE/src/strutil.h" +#include "../../../INCHI_BASE/src/util.h" +#include "../../../INCHI_BASE/src/ichierr.h" +#include "../../../INCHI_BASE/src/ichimain.h" +#include "../../../INCHI_BASE/src/extr_ct.h" +#include "../../../INCHI_BASE/src/ichi_io.h" +#include "../../../INCHI_BASE/src/ichicomp.h" +#include "../../../INCHI_BASE/src/inchi_api.h" +#include "../../../INCHI_BASE/src/readinch.h" + +#include "../../../INCHI_BASE/src/ichitaut.h" +#include "../../../INCHI_BASE/src/ichicant.h" +#include "../../../INCHI_BASE/src/ichitime.h" + +#include "inchi_dll.h" + +/************************************************************************* + * + * Local prototypes + * + *************************************************************************/ + +int SetAtomProperties( inp_ATOM *at, + MOL_COORD *szCoord, + inchi_Atom *ati, + int a1, + int *nDim, + char *pStrErr, + int *err ); +void SetNumImplicitH( inp_ATOM* at, int num_atoms ); +int SetBondProperties( inp_ATOM *at, + inchi_Atom *ati, + int a1, + int j, + int nNumAtoms, + int *nNumBonds, + char *pStrErr, + int *err ); +int SetAtomAndBondProperties( inp_ATOM *at, + inchi_Atom *ati, + int a1, + int bDoNotAddH, + char *pStrErr, + int *err ); +int InpAtom0DToInchiAtom( inp_ATOM *at, + int num_inp_atoms, + AT_NUM *num_atoms, + inchi_Atom **atom, + AT_NUM *num_stereo0D, + inchi_Stereo0D **stereo0D ); +int ExtractOneStructure( STRUCT_DATA *sd, + INPUT_PARMS *ip, + char *szTitle, + inchi_InputEx *inp, + INCHI_IOSTREAM *log_file, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM *prb_file, + ORIG_ATOM_DATA *orig_inp_data, + long *num_inp ); + +static int GetINCHI1( inchi_InputEx *inp, inchi_Output *out, int bStdFormat); + +int SetExtOrigAtDataByInChIExtInput( OrigAtDataPolymer **ppPolymer, + OrigAtDataV3000 **ppV3000, + inchi_Input_Polymer *polymer, + inchi_Input_V3000 *v3000, + int nat); +int SetInChIExtInputByExtOrigAtData( OrigAtDataPolymer *pPolymer, + OrigAtDataV3000 *pV3000, + inchi_Input_Polymer **ipolymer, + inchi_Input_V3000 **iv3000, + int nat); + +/****************************************************************************/ + +int bInterrupted = 0; + +/******************************************************************** + * + * INCHI API + * + ********************************************************************/ + +/* + FreeINCHI +*/ +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeINCHI( inchi_Output *out ) +{ + if ( !out ) + return; + + if ( out->szInChI ) + inchi_free( out->szInChI ); + if ( out->szLog ) + inchi_free( out->szLog ); + if ( out->szMessage ) + inchi_free( out->szMessage ); + + memset( out, 0, sizeof(*out) ); +} + +/* + FreeStdINCHI +*/ +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeStdINCHI( inchi_Output *out ) +{ + FreeINCHI( out ); +} + +/* + FreeStructFromStdINCHI +*/ +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeStructFromStdINCHI( inchi_OutputStruct *out ) +{ + FreeStructFromINCHI( out ); +} + +/* + FreeStructFromINCHI +*/ +EXPIMP_TEMPLATE INCHI_API + void INCHI_DECL FreeStructFromINCHI( inchi_OutputStruct *out ) +{ + if ( !out ) + return; + + if ( out->atom ) + inchi_free( out->atom ); + if ( out->stereo0D ) + inchi_free( out->stereo0D ); + if ( out->szLog ) + inchi_free( out->szLog ); + if ( out->szMessage ) + inchi_free( out->szMessage ); + + memset( out, 0, sizeof(*out) ); +} + +/* + GetStdINCHI +*/ +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStdINCHI( inchi_Input *inp, + inchi_Output *out ) +{ + inchi_InputEx extended_input; + extended_input.atom = inp->atom; + extended_input.num_atoms = inp->num_atoms; + extended_input.num_stereo0D = inp->num_stereo0D; + extended_input.stereo0D = inp->stereo0D; + extended_input.szOptions = inp->szOptions; + extended_input.polymer = NULL; + extended_input.v3000 = NULL; + return GetINCHI1( &extended_input, out, 1 ); +} + +/* + GetINCHI + +*/ +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetINCHI( inchi_Input *inp, + inchi_Output *out ) +{ + inchi_InputEx extended_input; + char *str_noz = "Unknown element \'*\'"; + int i; + + /* For back compatibility: no '*' or 'Zz' elements are allowed in the input to GetINCHI() ! */ + for (i=0; inum_atoms; i++) + { + if ( !strcmp(inp->atom->elname,"Zz") || !strcmp(inp->atom->elname,"*") ) + { + if ( out ) + { + memset( out, 0, sizeof(*out) ); + if (out->szMessage = (char *) inchi_malloc( strlen(str_noz) + 1 ) ) + { + strcpy( out->szMessage, str_noz ); + } + } + return _IS_ERROR; + } + } + + extended_input.atom = inp->atom; + extended_input.num_atoms = inp->num_atoms; + extended_input.stereo0D = inp->stereo0D; + extended_input.num_stereo0D = inp->num_stereo0D; + extended_input.szOptions = inp->szOptions; + extended_input.polymer = NULL; + extended_input.v3000 = NULL; + + return GetINCHI1( &extended_input, out, 0 ); +} + +/* + GetINCHIEx +*/ +EXPIMP_TEMPLATE INCHI_API +int INCHI_DECL GetINCHIEx( inchi_InputEx *inp, inchi_Output *out ) +{ +int i; + /* Check for and then replace star atoms if Polymer extension is supplied */ + if ( inp->polymer && inp->polymer->n && inp->polymer->units && inp->polymer->units[0] ) + { + for (i=0; inum_atoms; i++) + { + if ( !strcmp( inp->atom[i].elname,"*") ) + { + strcpy( inp->atom[i].elname, "Zz" ); + } + } + } + + return GetINCHI1( inp, out, 0 ); +} + +/* + GetINCHI1 (real worker) +*/ +static int GetINCHI1( inchi_InputEx *extended_input, + inchi_Output *out, + int bStdFormat) +{ + STRUCT_DATA struct_data; + STRUCT_DATA *sd = &struct_data; + char szTitle[MAX_SDF_HEADER+MAX_SDF_VALUE+256]; + + int i; + long num_inp, num_err; + char szSdfDataValue[MAX_SDF_VALUE+1]; + PINChI2 *pINChI[INCHI_NUM]; + PINChI_Aux2 *pINChI_Aux[INCHI_NUM]; + + unsigned long ulDisplTime = 0; /* infinite, milliseconds */ + unsigned long ulTotalProcessingTime = 0; + + INPUT_PARMS inp_parms; + INPUT_PARMS *ip = &inp_parms; + + ORIG_ATOM_DATA OrigAtData; /* 0=> disconnected, 1=> original */ + ORIG_ATOM_DATA *orig_inp_data = &OrigAtData; + ORIG_ATOM_DATA PrepAtData[2]; /* 0=> disconnected, 1=> original */ + ORIG_ATOM_DATA *prep_inp_data = PrepAtData; + int bReleaseVersion = bRELEASE_VERSION; + int nRet = 0, nRet1; + + CANON_GLOBALS CG; + INCHI_CLOCK ic; + + STRUCT_FPTRS *pStructPtrs = NULL; + +#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) + int num_repeat = REPEAT_ALL; +#endif + + const char *argv[INCHI_MAX_NUM_ARG+1]; + int argc; + char *szOptions = NULL; + + INCHI_IOSTREAM inchi_file[3], *out_file = inchi_file, *log_file = inchi_file+1; + INCHI_IOSTREAM prb_file0, *prb_file = &prb_file0; + + INCHI_IOSTREAM_STRING temp_string_container; + INCHI_IOSTREAM_STRING *strbuf = &temp_string_container; + + inchi_Input prev_versions_input; + inchi_Input *pvinp = &prev_versions_input; + pvinp->atom = extended_input->atom; + pvinp->num_atoms = extended_input->num_atoms; + pvinp->num_stereo0D = extended_input->num_stereo0D; + pvinp->stereo0D = extended_input->stereo0D; + pvinp->szOptions = extended_input->szOptions; + +#if( TRACE_MEMORY_LEAKS == 1 ) + _CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF); +/* for execution outside the VC++ debugger uncomment one of the following two */ +#ifdef MY_REPORT_FILE + _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_WARN, MY_REPORT_FILE ); + _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ERROR, MY_REPORT_FILE ); + _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ASSERT, MY_REPORT_FILE ); +#else + _CrtSetReportMode(_CRT_WARN | _CRT_ERROR, _CRTDBG_MODE_DEBUG); +#endif + +#if ( !defined(__STDC__) || __STDC__ != 1 ) + /* turn on floating point exceptions */ + { + /* Get the default control word. */ + int cw = _controlfp( 0,0 ); + + /* Set the exception masks OFF, turn exceptions on. */ + /*cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_INEXACT|EM_ZERODIVIDE|EM_DENORMAL);*/ + cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE|EM_DENORMAL); + + /* Set the control word. */ + _controlfp( cw, MCW_EM ); + } +#endif +#endif + + szTitle[0] = '\0'; + +#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) +repeat: + inchi_ios_close(out_file); + inchi_ios_close(log_file); + inchi_ios_close(prb_file); + pStr = NULL; +#endif + + /* Initialize internal for this function output streams as string buffers */ + inchi_ios_init(out_file, INCHI_IOSTREAM_TYPE_STRING, NULL); + inchi_ios_init(log_file, INCHI_IOSTREAM_TYPE_STRING, NULL); + inchi_ios_init(prb_file, INCHI_IOSTREAM_TYPE_STRING, NULL); + + num_inp = 0; + num_err = 0; + sd->bUserQuit = 0; + + /* clear original input structure */ + memset( pINChI, 0, sizeof(pINChI ) ); + memset( pINChI_Aux, 0, sizeof(pINChI_Aux) ); + memset( sd, 0, sizeof(*sd) ); + memset( ip, 0, sizeof(*ip) ); + memset( orig_inp_data , 0, sizeof( *orig_inp_data ) ); + memset( prep_inp_data , 0, 2*sizeof( *prep_inp_data ) ); + memset( szSdfDataValue , 0, sizeof( szSdfDataValue ) ); + + memset( &CG, 0, sizeof(CG)); + memset( &ic, 0, sizeof(ic)); + + if ( !out ) { + nRet = _IS_ERROR; + goto exit_function; + } + memset( out, 0, sizeof(*out) ); + + /* options */ + if ( pvinp && pvinp->szOptions ) { + szOptions = (char*)inchi_malloc( strlen(pvinp->szOptions) + 1 ); + if ( szOptions ) { + strcpy( szOptions, pvinp->szOptions ); + argc = parse_options_string ( szOptions, argv, INCHI_MAX_NUM_ARG ); + } else { + nRet = _IS_FATAL; + goto translate_RetVal; /* emergency exit */ + } + } else { + argc = 1; + argv[0] = ""; + argv[1] = NULL; + } + + if ( argc == 1 +#ifdef TARGET_API_LIB + && (!pvinp || pvinp->num_atoms <= 0 || !pvinp->atom) +#endif + || argc==2 && ( argv[1][0]==INCHI_OPTION_PREFX ) && + (!strcmp(argv[1]+1, "?") || !inchi_stricmp(argv[1]+1, "help") ) ) { + HelpCommandLineParms(log_file); + out->szLog = log_file->s.pStr; + memset( log_file, 0, sizeof(*log_file) ); + nRet = _IS_EOF; + goto translate_RetVal; + } + + nRet1 = ReadCommandLineParms( argc, argv, ip, szSdfDataValue, &ulDisplTime, bReleaseVersion, log_file ); + if ( szOptions ) { + inchi_free( szOptions ); + szOptions = NULL; + } + /* INChI DLL specific */ + ip->bNoStructLabels = 1; + + if ( 0 > nRet1 ) { + nRet = _IS_FATAL; + goto exit_function; + } + if ( ip->bNoStructLabels ) { + ip->pSdfLabel = NULL; + ip->pSdfValue = NULL; + } else + if ( ip->nInputType == INPUT_INCHI_XML || ip->nInputType == INPUT_INCHI_PLAIN || ip->nInputType == INPUT_CMLFILE ) { + /* the input may contain both the header and the label of the structure */ + if ( !ip->pSdfLabel ) + ip->pSdfLabel = ip->szSdfDataHeader; + if ( !ip->pSdfValue ) + ip->pSdfValue = szSdfDataValue; + } + + /* Ensure standardness */ + if ( bStdFormat ) + { + if ( ip->bINChIOutputOptions & INCHI_OUT_SAVEOPT ) + { + ip->bINChIOutputOptions &= ~INCHI_OUT_SAVEOPT; + } + if ( 0 != ( ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) + { + ip->bTautFlags &= ~TG_FLAG_RECONNECT_COORD; + } + if ( 0 != (ip->nMode & REQ_MODE_BASIC) ) + { + ip->nMode &= ~REQ_MODE_BASIC; + } + if ( 0 != ( ip->nMode & REQ_MODE_RELATIVE_STEREO) ) + { + ip->nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); + } + if ( 0 != ( ip->nMode & REQ_MODE_RACEMIC_STEREO) ) + { + ip->nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); + } + if ( 0 != ( ip->nMode & REQ_MODE_CHIR_FLG_STEREO) ) + { + ip->nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); + } + if ( 0 != ( ip->nMode & REQ_MODE_DIFF_UU_STEREO) ) + { + ip->nMode &= ~REQ_MODE_DIFF_UU_STEREO; + } + if ( 0 == (ip->nMode & (REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU)) ) + { + ip->nMode |= REQ_MODE_SB_IGN_ALL_UU; + ip->nMode |= REQ_MODE_SC_IGN_ALL_UU; + } + if ( 0 != (ip->bTautFlags & TG_FLAG_KETO_ENOL_TAUT) ) + { + ip->bTautFlags &= ~TG_FLAG_KETO_ENOL_TAUT; + } + if ( 0 != (ip->bTautFlags & TG_FLAG_1_5_TAUT) ) + { + ip->bTautFlags &= ~TG_FLAG_1_5_TAUT; + } + /* And anyway... */ + ip->bINChIOutputOptions |= INCHI_OUT_STDINCHI; + ip->bINChIOutputOptions &= ~INCHI_OUT_SAVEOPT; + } + /* */ + + PrintInputParms( log_file, ip ); + + if ( 0>=inchi_strbuf_init( strbuf, INCHI_STRBUF_INITIAL_SIZE, INCHI_STRBUF_SIZE_INCREMENT ) ) + { + inchi_ios_eprint( log_file, "Cannot allocate internal string buffer. Terminating\n"); + nRet = _IS_FATAL; + goto exit_function; + } + + /*************************************************** + /* Main cycle */ + /* read input structures and create their INChI's */ + ulTotalProcessingTime = 0; + + if ( pStructPtrs ) + { + memset ( pStructPtrs, 0, sizeof(pStructPtrs[0]) ); + } + + /* === possible improvement: convert inp to orig_inp_data ==== */ + if ( !sd->bUserQuit && !bInterrupted ) + { + if ( ip->last_struct_number && num_inp >= ip->last_struct_number ) + { + nRet = _IS_EOF; /* simulate end of file */ + goto exit_function; + } + + nRet = ExtractOneStructure( sd, + ip, + szTitle, + extended_input, + log_file, + out_file, + prb_file, + orig_inp_data, + &num_inp ); + + if ( pStructPtrs ) + { + pStructPtrs->cur_fptr ++; + } + +#ifndef TARGET_API_LIB + if ( sd->bUserQuit ) + { + break; + } +#endif + switch ( nRet ) + { + case _IS_FATAL: + num_err ++; + goto exit_function; + case _IS_EOF: + goto exit_function; + case _IS_ERROR: + num_err ++; + goto exit_function; +#ifndef TARGET_API_LIB + case _IS_SKIP: + continue; +#endif + } + + /* Create INChI for each connected component of the structure and optionally display them */ + /* output INChI for the whole structure */ + nRet1 = ProcessOneStructureEx( &ic, &CG, sd, ip, szTitle, + pINChI, pINChI_Aux, + NULL, /* inp_file is not necessary as all input is already saved in 'ip' */ + log_file, out_file, prb_file, + orig_inp_data, prep_inp_data, + num_inp, strbuf, + 0 /* save_opt_bits */); + + /* Free INChI memory */ + FreeAllINChIArrays( pINChI, pINChI_Aux, sd->num_components ); + + /* Free structure data */ + FreeOrigAtData( orig_inp_data ); + FreeOrigAtData( prep_inp_data ); + FreeOrigAtData( prep_inp_data+1 ); + + ulTotalProcessingTime += sd->ulStructTime; + nRet = inchi_max(nRet, nRet1); + switch ( nRet ) { + case _IS_FATAL: + /* num_err ++; */ + goto exit_function; + case _IS_ERROR: + ; /* num_err ++; */ +#ifndef TARGET_API_LIB + continue; +#endif + } + } + +exit_function: + + /* Avoid memory leaks in case of fatal error */ + if ( pStructPtrs && pStructPtrs->fptr ) + { + inchi_free( pStructPtrs->fptr ); + } + + /* Free INChI memory */ + FreeAllINChIArrays( pINChI, pINChI_Aux, sd->num_components ); + /* Free structure data */ + FreeOrigAtData( orig_inp_data ); + FreeOrigAtData( prep_inp_data ); + FreeOrigAtData( prep_inp_data+1 ); + + inchi_strbuf_close( strbuf ); + + for ( i = 0; i < MAX_NUM_PATHS; i ++ ) + { + if ( ip->path[i] ) + { + inchi_free( (char*) ip->path[i] ); /* cast deliberately discards 'const' qualifier */ + ip->path[i] = NULL; + } + } + + SetBitFree( &CG ); + +#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) + if ( num_repeat-- > 0 ) { + goto repeat; + } +#endif + + /* output */ + produce_generation_output( out, sd, ip, log_file, out_file ); + +translate_RetVal: + + /* Close inernal I/O streams */ + inchi_ios_close(log_file); + inchi_ios_close(out_file); + inchi_ios_close(prb_file); + + switch (nRet) + { + case _IS_SKIP : nRet = inchi_Ret_SKIP ; break; /* not used in INChI dll */ + case _IS_EOF : nRet = inchi_Ret_EOF ; break; /* no structural data has been provided */ + case _IS_OKAY : nRet = inchi_Ret_OKAY ; break; /* Success; break; no errors or warnings */ + case _IS_WARNING: nRet = inchi_Ret_WARNING; break; /* Success; break; warning(s) issued */ + case _IS_ERROR : nRet = inchi_Ret_ERROR ; break; /* Error: no INChI has been created */ + case _IS_FATAL : nRet = inchi_Ret_FATAL ; break; /* Severe error: no INChI has been created (typically; break; memory allocation failed) */ + case _IS_UNKNOWN: + default : nRet = inchi_Ret_UNKNOWN; break; /* Unlnown program error */ + } + + return nRet; +} + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +void produce_generation_output( inchi_Output *out, STRUCT_DATA *sd, INPUT_PARMS *ip, + INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *out_file ) + +{ + if ( sd->pStrErrStruct[0] ) + { + if ( out && (out->szMessage = (char *)inchi_malloc( strlen(sd->pStrErrStruct) + 1 )) ) + { + strcpy( out->szMessage, sd->pStrErrStruct ); + } + } + + /* Make separate strings with InChI and AuxInfo */ + if ( out_file->s.pStr && out_file->s.nUsedLength > 0 && out ) + { + char *p; + out->szInChI = out_file->s.pStr; + out->szAuxInfo = NULL; + if ( !(INCHI_OUT_SDFILE_ONLY & ip->bINChIOutputOptions ) ) /* do not remove last LF from SDF output - 2008-12-23 DT */ + for ( p = strchr(out->szInChI, '\n'); p; p = strchr(p+1, '\n') ) + { + if ( !memcmp( p, "\nAuxInfo", 8 ) ) + { + *p = '\0'; /* remove LF after INChI */ + out->szAuxInfo = p+1; /* save pointer to AuxInfo */ + } + else if ( out->szAuxInfo || !p[1]) + { + /* remove LF after aux info or from the last char */ + *p = '\0'; + break; + } + } + out_file->s.pStr = NULL; + } + + copy_corrected_log_tail( out, log_file ); +} + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +void copy_corrected_log_tail( inchi_Output *out, INCHI_IOSTREAM *log_file ) +{ + if ( log_file->s.pStr && log_file->s.nUsedLength > 0 ) + { + while ( log_file->s.nUsedLength && + '\n' == log_file->s.pStr[log_file->s.nUsedLength-1] ) + { + log_file->s.pStr[-- log_file->s.nUsedLength] = '\0'; + /* remove last LF */ + } + if ( out ) + { + char *p; + out->szLog = log_file->s.pStr; + log_file->s.pStr = NULL; + for ( p = strchr(out->szLog, ' '); p; p = strchr(p+1, ' ') ) + { + if ( !memcmp( p, " structure #", 12 ) ) + { + *p = '\0'; + } + } + } + } +} + +/* + CheckINCHI + + Check if the string represents valid InChI/standard InChI. + Input: + szINCHI source InChI + strict if 0, just quickly check for proper layout (prefix, version, etc.) + The result may not be strict. + If not 0, try to perform InChI2InChI conversion and + returns success if a resulting InChI string exactly match source. + The result may be 'false alarm' due to imperfect algorithm of + conversion. + Returns: + success/errors codes + +*/ + +EXPIMP_TEMPLATE INCHI_API + int INCHI_DECL CheckINCHI(const char *szINCHI, const int strict) +{ +int ret=INCHI_VALID_NON_STANDARD; +int ret_i2i; +inchi_InputINCHI inchi_inp; +inchi_Output inchi_out; +size_t slen, pos_slash1=0; +char *str = NULL; +size_t i; +size_t slen0; +char pp; + + /* .. non-empty */ + if (szINCHI==NULL) + return INCHI_INVALID_PREFIX; + + slen = strlen(szINCHI); + + /* .. has valid prefix */ + if (slen standard InChIKey */ + ret = INCHI_VALID_STANDARD; + pos_slash1++; + } + else if (szINCHI[pos_slash1]=='B') + { + /* Beta version InChI ==> non-standard */ + ret = INCHI_VALID_BETA; + pos_slash1++; + } + + /* .. has trailing slash in the right place */ + if (szINCHI[pos_slash1]!='/') + return INCHI_INVALID_LAYOUT; + + /* .. the rest of source string contains valid literals */ + + /* Treat possible SaveOpt letters */ + slen0 = slen; + if ( (szINCHI[slen-3]=='\\') && + (szINCHI[slen-2] >= 'A') && (szINCHI[slen-2] <='Z') && + (szINCHI[slen-1] >= 'A') && (szINCHI[slen-1] <='Z') + ) + slen0 = slen -3; + + for (i=pos_slash1+1; i= 'A' && pp <='Z') continue; + if (pp >= 'a' && pp <='z') continue; + if (pp >= '0' && pp <='9') continue; + switch ( pp ) + { + case '(': case ')': + case '*': case '+': + case ',': case '-': + case '.': case '/': + case ';': case '=': + case '?': case '@': continue; + + default: break; + } + return INCHI_INVALID_LAYOUT; + } + + if ( strict ) + { + char opts[]="?FixedH ?RecMet ?SUU ?SLUUD"; + extract_inchi_substring(&str, szINCHI, slen); + if (NULL==str) + { + ret = INCHI_FAIL_I2I; + goto fin; + } + + inchi_inp.szInChI = str; + opts[0] = opts[8] = opts[16] = opts[21] = INCHI_OPTION_PREFX; + inchi_inp.szOptions = opts; + + ret_i2i = GetINCHIfromINCHI(&inchi_inp, &inchi_out); + + if ( ((ret_i2i!=inchi_Ret_OKAY) && (ret_i2i!=inchi_Ret_WARNING)) || !inchi_out.szInChI ) + { + ret = INCHI_FAIL_I2I; + } + else + { + if (strcmp(inchi_inp.szInChI, inchi_out.szInChI)) + { + ret = INCHI_FAIL_I2I; + } + } + } + +fin:if ( strict ) + { + if (NULL!=str) + inchi_free(str); + } + return ret; +} + +/****************************************************************************/ +void SetNumImplicitH(inp_ATOM* at, int num_atoms) +{ + int bNonMetal; + int a1/*, n1*/; + + /* special valences */ + for ( bNonMetal = 0; bNonMetal < 2; bNonMetal ++ ) + { + for ( a1 = 0; a1 < num_atoms; a1 ++ ) + { + int bHasMetalNeighbor /*, j*/; + if ( bNonMetal != is_el_a_metal( at[a1].el_number ) ) + { + continue; /* first process all metals, after that all non-metals */ + } + + bHasMetalNeighbor = 0; + /*********************************************************************** + * Set number of hydrogen atoms + */ + at[a1].num_H = get_num_H( at[a1].elname, + at[a1].num_H, + at[a1].num_iso_H, + at[a1].charge, + at[a1].radical, + at[a1].chem_bonds_valence, + 0, /* instead of valence entered by the user: it does not exist here*/ + (at[a1].at_type & 1) /* bAliased */, + !(at[a1].at_type & 2) /* bDoNotAddH */, + bHasMetalNeighbor ); + at[a1].at_type = 0; + } + } +} + +/********************************************************************/ + +#define REPEAT_ALL 0 + +/********************************************************************/ +int parse_options_string ( char *cmd, const char *argv[], int maxargs ) +{ + char *p; + char *pArgCurChar; + int bInsideQuotes; + int bCopyCharToArg; + int nNumBackSlashes; + int i; + + i = 0; + argv[i++] = ""; /* zeroth argument is not used */ + p = cmd; + bInsideQuotes = 0; + + /* arguments, one by one */ + while( i < maxargs-1 ) + { + /* bypass spaces */ + while ( *p == ' ' || *p == '\t' ) + p ++; + if ( !*p ) + break; + /* scan an argument */ + argv[i++] = pArgCurChar = p; /* store preliminary ptr to arg */ + + while ( 1 ) + { + bCopyCharToArg = 1; + nNumBackSlashes = 0; + while (*p == '\\') + { + ++p; + ++nNumBackSlashes; + } + + /* each pair of backslashes => one backslash; one more backslash => literal quote */ + if ( *p == '\"' ) + { + /* one " found */ + if ( nNumBackSlashes % 2 == 0 ) + { + if (bInsideQuotes) { + if (*(p+1) == '\"') + { + p++; + } else { + bCopyCharToArg = 0; + } + } + else + { + bCopyCharToArg = 0; + } + bInsideQuotes = !bInsideQuotes; + } + nNumBackSlashes /= 2; /* divide nNumBackSlashes by two */ + } + while (nNumBackSlashes--) + { + *pArgCurChar++ = '\\'; + } + if (!*p) + { + break; + } + if (!bInsideQuotes && (*p == ' ' || *p == '\t')) + { + p ++; + /* move to the next char because this char may become + * zero due to *pArgCurChar++ = '\0'; line below */ + break; + } + if (bCopyCharToArg) + { + *pArgCurChar++ = *p; + } + ++p; + } + *pArgCurChar++ = '\0'; /* argument zero termination */ + } + + /* The last argument is NULL */ + argv[i] = NULL; + + return i; +} + +/*****************************************************************/ + +#define MIN_BOND_LENGTH (1.0e-6) + +/****************************************************************************/ +int SetAtomProperties( inp_ATOM *at, + MOL_COORD *szCoord, + inchi_Atom *ati, + int a1, + int *nDim, + char *pStrErr, + int *err ) +{ + S_CHAR cRadical; + /* element, check later */ + + strcpy( at[a1].elname, ati[a1].elname ); + + /* charge */ + + at[a1].charge = ati[a1].charge; + + /* radical */ + + switch ( ati[a1].radical ) + { + case INCHI_RADICAL_NONE: + cRadical = 0; + break; + case INCHI_RADICAL_SINGLET: +#if( SINGLET_IS_TRIPLET == 1) /* 'singlet' means two electrons make a lone pair instead of 2 bonds*/ + /* its effect on valence is same as the effect of a triplet */ + cRadical = RADICAL_TRIPLET; +#else + cRadical = RADICAL_SINGLET; +#endif + break; + case INCHI_RADICAL_DOUBLET: + cRadical = RADICAL_DOUBLET; + break; + case INCHI_RADICAL_TRIPLET: + cRadical = RADICAL_TRIPLET; + break; + default: + { + char szRadicalType[16]; + int nRad = ati[a1].radical; + while ( nRad > RADICAL_TRIPLET ) + { + nRad -= 2; + } + sprintf( szRadicalType, "%d->%d", ati[a1].radical, nRad ); + TREAT_ERR (*err, 0, "Radical center type replaced:"); + TREAT_ERR (*err, 0, szRadicalType); + cRadical = nRad; + if ( nRad < 0 ) + { + *err |= 8; /* Unrecognized Radical replaced with non-radical */ + } + } + break; + } + at[a1].radical = cRadical; + + /* coordinates */ + at[a1].x = ati[a1].x; + at[a1].y = ati[a1].y; + at[a1].z = ati[a1].z; + + if ( szCoord ) + { + /* store text coordinates */ + char str[32]; + MOL_COORD * coord_p = szCoord + a1; + WriteCoord( str, ati[a1].x ); + memcpy( *coord_p, str, 10 ); + WriteCoord( str, ati[a1].y ); + memcpy( *coord_p+10, str, 10 ); + WriteCoord( str, ati[a1].z ); + memcpy( *coord_p+20, str, 10 ); + } + + if ( MIN_BOND_LENGTH < fabs(ati[a1].x) || MIN_BOND_LENGTH < fabs(ati[a1].y) || MIN_BOND_LENGTH < fabs(ati[a1].z) ) + { + if ( MIN_BOND_LENGTH < fabs(ati[a1].z) ) + { + *nDim |= 3; + } + else + { + *nDim |= 2; + } + } + + /* orig. at. number */ + at[a1].orig_at_number = a1+1; + return 0; + +#undef MIN_BOND_LENGTH +} + +/****************************************************************************/ +int SetBondProperties( inp_ATOM *at, + inchi_Atom *ati, + int a1, + int j, + int nNumAtoms, + int *nNumBonds, + char *pStrErr, + int *err ) +{ + int a2; + S_CHAR cBondType, cStereoType1, cStereoType2; + AT_NUMB *p1, *p2; + int n1, n2; + + /* bond type */ + switch( ati[a1].bond_type[j] ) + { + case INCHI_BOND_TYPE_SINGLE: + cBondType = BOND_TYPE_SINGLE; + break; + case INCHI_BOND_TYPE_DOUBLE: + cBondType = BOND_TYPE_DOUBLE; + break; + case INCHI_BOND_TYPE_TRIPLE: + cBondType = BOND_TYPE_TRIPLE; + break; + case INCHI_BOND_TYPE_ALTERN: + cBondType = BOND_TYPE_ALTERN; + break; + default: + { + char szBondType[16]; + sprintf( szBondType, "%d", ati[a1].bond_type[j] ); + TREAT_ERR (*err, 0, "Unrecognized bond type:"); + TREAT_ERR (*err, 0, szBondType); + *err |= 8; /* Unrecognized Bond type replaced with single bond */ + cBondType = BOND_TYPE_SINGLE; + } + break; + } + + /* 2D stereo */ + + switch( ati[a1].bond_stereo[j] ) + { + /* stereocenter-related; positive: the sharp end points to this atom */ + case INCHI_BOND_STEREO_NONE: + cStereoType1 = 0; + cStereoType2 = 0; + break; + case INCHI_BOND_STEREO_SINGLE_1UP: + cStereoType1 = STEREO_SNGL_UP; + cStereoType2 = -STEREO_SNGL_UP; + break; + case INCHI_BOND_STEREO_SINGLE_1EITHER: + cStereoType1 = STEREO_SNGL_EITHER; + cStereoType2 = -STEREO_SNGL_EITHER; + break; + case INCHI_BOND_STEREO_SINGLE_1DOWN: + cStereoType1 = STEREO_SNGL_DOWN; + cStereoType2 = -STEREO_SNGL_DOWN; + break; + /* stereocenter-related; negative: the sharp end points to the opposite atom */ + case INCHI_BOND_STEREO_SINGLE_2UP: + cStereoType1 = -STEREO_SNGL_UP; + cStereoType2 = STEREO_SNGL_UP; + break; + case INCHI_BOND_STEREO_SINGLE_2EITHER: + cStereoType1 = -STEREO_SNGL_EITHER; + cStereoType2 = STEREO_SNGL_EITHER; + break; + case INCHI_BOND_STEREO_SINGLE_2DOWN: + cStereoType1 = -STEREO_SNGL_DOWN; + cStereoType2 = STEREO_SNGL_DOWN; + break; + /* stereobond-related */ + case INCHI_BOND_STEREO_DOUBLE_EITHER: + case -INCHI_BOND_STEREO_DOUBLE_EITHER: + cStereoType1 = STEREO_DBLE_EITHER; + cStereoType2 = STEREO_DBLE_EITHER; + break; + default: + { + char szBondType[16]; + sprintf( szBondType, "%d", ati[a1].bond_stereo[j] ); + TREAT_ERR (*err, 0, "Unrecognized bond stereo:"); + TREAT_ERR (*err, 0, szBondType); + *err |= 8; /* Unrecognized Bond stereo replaced with non-stereo bond */ + cStereoType1 = 0; + cStereoType2 = 0; + } + break; + } + + /* neighbor */ + + if ( ati[a1].neighbor[j] < 0 || ati[a1].neighbor[j] >= nNumAtoms ) + { + *err |= 1; /* bond for impossible atom number(s); ignored */ + TREAT_ERR (*err, 0, "Bond to nonexistent atom"); + goto err_exit; + } + + a2 = (AT_NUMB) ati[a1].neighbor[j]; + if ( a2 == a1 ) + { + *err |= 1; /* bond for impossible atom number(s); ignored */ + TREAT_ERR (*err, 0, "Atom has a bond to itself"); + goto err_exit; + } + + /* consistency check; locate the bond in the opposite atom */ + p1 = is_in_the_list( at[a1].neighbor, (AT_NUMB)a2, at[a1].valence ); + p2 = is_in_the_list( at[a2].neighbor, (AT_NUMB)a1, at[a2].valence ); + + if ( p1 && p2 ) + { + n1 = (int) (p1 - at[a1].neighbor); + n2 = (int) (p2 - at[a2].neighbor); + if ( n1+1 < at[a1].valence && + is_in_the_list( at[a1].neighbor+n1+1, (AT_NUMB)a2, at[a1].valence-n1-1 ) + || + n2+1 < at[a2].valence && + is_in_the_list( at[a2].neighbor+n2+1, (AT_NUMB)a1, at[a2].valence-n2-1 ) ) + { + TREAT_ERR (*err, 0, "Multiple bonds between two atoms"); + *err |= 2; /* multiple bonds between atoms */ + } + else if ( n1 < at[a1].valence && n2 < at[a2].valence && + cBondType == at[a2].bond_type[n2] && + cBondType == at[a1].bond_type[n1] && + cStereoType1 == at[a1].bond_stereo[n1] && + cStereoType2 == at[a2].bond_stereo[n2] ) + { + /*TREAT_ERR (*err, 0, "Duplicated bond(s) between two atoms");*/ + } + else + { + TREAT_ERR (*err, 0, "Multiple bonds between two atoms"); + *err |= 2; /* multiple bonds between atoms */ + } + } + else if ( (p1 || p2) && + (p1 || at[a1].valence < MAXVAL) && + (p2 || at[a2].valence < MAXVAL) ) + { + n1 = p1? (int) (p1 - at[a1].neighbor) : at[a1].valence ++; + n2 = p2? (int) (p2 - at[a2].neighbor) : at[a2].valence ++; + /* the bond is present in one atom only: possibly program error */ + if ( p1 && (cBondType != at[a1].bond_type[n1] || at[a1].bond_stereo[n1] != cStereoType1 )|| + p2 && (cBondType != at[a2].bond_type[n2] || at[a2].bond_stereo[n2] != cStereoType2 ) ) + { + TREAT_ERR (*err, 0, "Multiple bonds between two atoms"); + *err |= 2; /* multiple bonds between atoms */ + } + else + { + TREAT_ERR (*err, 0, "Duplicated bond(s) between two atoms"); + /* warning */ + } + } + else if ( !p1 && !p2 && at[a1].valence < MAXVAL && at[a2].valence < MAXVAL ) + { + n1 = at[a1].valence ++; + n2 = at[a2].valence ++; + (*nNumBonds) ++; + } + else + { + char szMsg[64]; + *err |= 4; /* too large number of bonds. Some bonds ignored. */ + sprintf( szMsg, "Atom '%s' has more than %d bonds", + at[a1].valence>= MAXVAL? at[a1].elname:at[a2].elname, MAXVAL ); + TREAT_ERR (*err, 0, szMsg); + goto err_exit; + } + + /* store the connection */ + + /* bond type */ + at[a1].bond_type[n1] = + at[a2].bond_type[n2] = cBondType; + /* connection */ + at[a1].neighbor[n1] = (AT_NUMB)a2; + at[a2].neighbor[n2] = (AT_NUMB)a1; + /* stereo */ + at[a1].bond_stereo[n1] = cStereoType1; /* >0: the wedge (pointed) end is at this atom */ + at[a2].bond_stereo[n2] = cStereoType2; /* <0: the wedge (pointed) end is at the opposite atom */ + + return 0; + +err_exit: + return 1; +} + +/******************************************************************/ +int SetAtomAndBondProperties( inp_ATOM *at, + inchi_Atom *ati, + int a1, + int bDoNotAddH, + char *pStrErr, + int *err ) +{ + int valence, chem_valence, num_alt_bonds, j, n1; + int nRadical, nCharge; + static int el_number_H = 0; + + if ( !el_number_H ) + { + el_number_H = get_periodic_table_number( "H" ); + } + + nRadical = nCharge = 0; + valence = at[a1].valence; + chem_valence = num_alt_bonds = 0; + for ( j = 0; j < valence; j ++ ) + { + if ( at[a1].bond_type[j] <= BOND_TYPE_TRIPLE ) + { + chem_valence += at[a1].bond_type[j]; + } + else + { + num_alt_bonds ++; + } + } + switch( num_alt_bonds ) { + case 0: + break; + case 2: + chem_valence += 3; /* -C= */ + break; + case 3: + chem_valence += 4; /* >C= */ + break; + default: + { + char szMsg[64]; + *err |= 8; /* wrong number of alt. bonds */ + sprintf( szMsg, "Atom '%s' has %d alternating bonds", + at[a1].elname, num_alt_bonds ); + TREAT_ERR (*err, 0, szMsg); + } + break; + } + at[a1].chem_bonds_valence = chem_valence; + + /* aliased hydrogen atoms */ + if ( ERR_ELEM == (n1 = get_periodic_table_number( at[a1].elname ) ) ) + { + /* Case when elname contains more than 1 element: extract number of H if possible */ + if ( extract_charges_and_radicals( at[a1].elname, &nRadical, &nCharge ) ) + { + if ( nRadical && at[a1].radical && nRadical != at[a1].radical || + nCharge && at[a1].charge && nCharge != at[a1].charge ) + { + TREAT_ERR (*err, 0, "Ignored charge/radical redefinition:"); + TREAT_ERR (*err, 0, ati[a1].elname); + } + else + { + if ( nRadical ) + at[a1].radical = nRadical; + if ( nCharge ) + at[a1].charge = nCharge; + } + } + + at[a1].num_H = extract_H_atoms( at[a1].elname, at[a1].num_iso_H ); + if ( !at[a1].elname[0] && NUMH(at, a1) ) + { + /* alias contains only H. Added 2004-07-21, fixed 2004-07-22 + * move the heaviest isotope to the "central atom" + * Note: this must be consistent with H-H treatment in remove_terminal_HDT() + */ + strcpy( at[a1].elname, "H" ); + if ( NUM_ISO_H(at,a1) ) { + for ( j = NUM_H_ISOTOPES-1; 0 <= j; j -- ) + { + if ( at[a1].num_iso_H[j] ) + { + at[a1].num_iso_H[j] --; + at[a1].iso_atw_diff = 1 + j; + break; + } + } + } + else + { + at[a1].num_H --; + } + } + + if ( ERR_ELEM == (n1 = get_periodic_table_number( at[a1].elname ) ) ) + { + n1 = 0; + } + if ( n1 ) + { + at[a1].at_type |= 1; /* "Aliased" atom: data in the element name */ + TREAT_ERR (*err, 0, "Parsed compound atom(s):"); + TREAT_ERR (*err, 0, ati[a1].elname); + } + } + + at[a1].el_number = (U_CHAR) n1; + if ( !n1 ) + { + *err |= 64; /* Unrecognized aromatic bond(s) replaced with single */ + TREAT_ERR (*err, 0, "Unknown element(s):"); + TREAT_ERR (*err, 0, at[a1].elname); + } + else + /* replace explicit D or T with isotopic H (added 2003-06-02) */ + if ( el_number_H == n1 && !at[a1].iso_atw_diff ) + { + switch( at[a1].elname[0] ) + { + case 'D': + at[a1].iso_atw_diff = 2; + mystrncpy( at[a1].elname, "H", sizeof(at->elname) ); + break; + case 'T': + at[a1].iso_atw_diff = 3; + mystrncpy( at[a1].elname, "H", sizeof(at->elname) ); + break; + case 'H': + if ( 1 <= ati[a1].isotopic_mass ) + { + AT_NUM iso_atw_diff; + if ( ISOTOPIC_SHIFT_FLAG - ISOTOPIC_SHIFT_MAX <= ati[a1].isotopic_mass && + ISOTOPIC_SHIFT_FLAG + ISOTOPIC_SHIFT_MAX >= ati[a1].isotopic_mass ) + { + /* ati[a1].isotopic_mass is isotopic iso_atw_diff + ISOTOPIC_SHIFT_FLAG */ + iso_atw_diff = ati[a1].isotopic_mass - ISOTOPIC_SHIFT_FLAG; + } + else + { + /* ati[a1].isotopic_mass is isotopic mass */ + int iso_atw = get_atomic_mass_from_elnum( (int) at[a1].el_number ); + iso_atw_diff = ati[a1].isotopic_mass - iso_atw; + } + if ( iso_atw_diff >= 0 ) + iso_atw_diff ++; + /* reproduce Bug04: allowed non-terminal H heavier than T */ + if ( 1 <= iso_atw_diff && + (at[a1].valence != 1 || iso_atw_diff <= NUM_H_ISOTOPES) ) + { + at[a1].iso_atw_diff = (S_CHAR)iso_atw_diff; + } + } + } + } + else + /* isotopic shift */ + if ( ati[a1].isotopic_mass ) + { + AT_NUM iso_atw_diff; + if ( ISOTOPIC_SHIFT_FLAG - ISOTOPIC_SHIFT_MAX <= ati[a1].isotopic_mass && + ISOTOPIC_SHIFT_FLAG + ISOTOPIC_SHIFT_MAX >= ati[a1].isotopic_mass ) + { + /* ati[a1].isotopic_mass is isotopic iso_atw_diff + ISOTOPIC_SHIFT_FLAG */ + iso_atw_diff = ati[a1].isotopic_mass - ISOTOPIC_SHIFT_FLAG; + } + else + { + /* ati[a1].isotopic_mass is isotopic mass */ + iso_atw_diff = get_atomic_mass_from_elnum( (int) at[a1].el_number ); + iso_atw_diff = ati[a1].isotopic_mass - iso_atw_diff; + } + if ( iso_atw_diff >= 0 ) + iso_atw_diff ++; + at[a1].iso_atw_diff = (S_CHAR)iso_atw_diff; + } + + /* add implicit hydrogen atoms flag */ + if ( ati[a1].num_iso_H[0] == -1 ) + { + if ( !bDoNotAddH ) + { + at[a1].at_type |= 2; /* user requested to add H */ + } + } + else + { + at[a1].num_H = ati[a1].num_iso_H[0]; + } + + for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) + { + at[a1].num_iso_H[j] = ati[a1].num_iso_H[j+1]; + } + + if ( num_alt_bonds ) + { + /* atom has aromatic bonds AND the chemical valence is not known */ + int num_H = NUMH(at, a1); + int chem_valence_alt = at[a1].chem_bonds_valence + num_H; + int bUnusualValenceArom = + detect_unusual_el_valence( (int)at[a1].el_number, at[a1].charge, + at[a1].radical, chem_valence_alt, + num_H, at[a1].valence ); + int bUnusualValenceNoArom = + detect_unusual_el_valence( (int)at[a1].el_number, at[a1].charge, + at[a1].radical, chem_valence_alt-1, + num_H, at[a1].valence ); + if ( bUnusualValenceArom && !bUnusualValenceNoArom && 0 == nBondsValToMetal( at, a1) ) + { + /* typically NH in 5-member aromatic ring */ + at[a1].chem_bonds_valence --; + } + } + + return 0; +} + +/****************************************************************************************/ +int InpAtom0DToInchiAtom( inp_ATOM *at, + int num_inp_atoms, + AT_NUM *num_atoms, + inchi_Atom **atom, + AT_NUM *num_stereo0D, + inchi_Stereo0D **stereo0D ) +{ + int num_stereo_centers, num_stereo_bonds, num_inp_stereo0D, i, m, m1, m2, n, ret=0; + /* count stereobonds, allenes. cumulenes. and stereoatoms */ + num_stereo_centers = num_stereo_bonds = ret = 0; + + *atom = NULL; + *num_atoms = 0; + *stereo0D = NULL; + *num_stereo0D = 0; + + for ( i = 0; i < num_inp_atoms; i ++ ) + { + if ( at[i].p_parity ) + { + /* stereocenter */ + num_stereo_centers ++; + } + else + { + for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[i].sb_parity[m]; m ++ ) + ; + num_stereo_bonds += m; + } + } + + num_stereo_bonds /= 2; + num_inp_stereo0D = num_stereo_bonds + num_stereo_centers; + + if ( num_inp_atoms > 0 ) + { + *atom = (inchi_Atom *) inchi_calloc( num_inp_atoms, sizeof( (*atom)[0] ) ); + } + + *num_atoms = num_inp_atoms; + + if ( num_inp_stereo0D > 0 ) + { + *stereo0D = (inchi_Stereo0D *)inchi_calloc( num_inp_stereo0D, sizeof((*stereo0D)[0])); + } + + if ( num_inp_atoms && !(*atom) || num_inp_stereo0D > 0 && !(*stereo0D) ) + { + /* allocation failed */ + ret = -1; + goto exit_function; + } + + /* copy atom properties */ + for ( i = 0; i < num_inp_atoms; i ++ ) + { + (*atom)[i].num_bonds = at[i].valence; + for ( m = 0; m < at[i].valence; m ++ ) + { + (*atom)[i].bond_type[m] = at[i].bond_type[m]; + (*atom)[i].neighbor[m] = at[i].neighbor[m]; + } + (*atom)[i].charge = at[i].charge; + memcpy( (*atom)[i].elname, at[i].elname, ATOM_EL_LEN ); + if ( at[i].iso_atw_diff ) + { + (*atom)[i].isotopic_mass = ISOTOPIC_SHIFT_FLAG + (at[i].iso_atw_diff > 0? at[i].iso_atw_diff-1 : at[i].iso_atw_diff); + } + (*atom)[i].num_iso_H[0] = at[i].num_H; + for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) + { + (*atom)[i].num_iso_H[m+1] = at[i].num_iso_H[m]; + } + (*atom)[i].radical = at[i].radical; + } + + /* stereo */ + for ( i = n = 0; i < num_inp_atoms; i ++ ) + { + if ( at[i].p_parity ) + { + if ( n < num_inp_stereo0D ) + { + (*stereo0D)[n].central_atom = i; + (*stereo0D)[n].parity = at[i].p_parity; + (*stereo0D)[n].type = INCHI_StereoType_Tetrahedral; + for ( m = 0; m < MAX_NUM_STEREO_ATOM_NEIGH; m ++ ) + { + (*stereo0D)[n].neighbor[m] = at[i].p_orig_at_num[m] - 1; + } + n ++; + } + else + { + ret |= 1; + break; + } + } + else + { + for ( m1 = 0; m1 < MAX_NUM_STEREO_BONDS && at[i].sb_parity[m1]; m1 ++ ) + { + /* find the opposite atom at the other end of double bond, allene, or cumulene */ + int chain[12], len = 0, nxt_neigh, nxt, cur; + cur = chain[len++] = i; + nxt_neigh = at[cur].sb_ord[m1]; + + do + { + /* add next atom */ + chain[len ++] = nxt = at[cur].neighbor[nxt_neigh]; + nxt_neigh = (at[nxt].neighbor[0] == cur); + cur = nxt; + /* find nxt_neigh */ + } while ( !at[cur].sb_parity[0] && + len < 12 && + at[cur].valence == 2 ); + + if ( at[cur].sb_parity[0] && len <= 4 && i < cur /* count bonds only one time */ ) + { + /* double bond, cumulene, or allene has been found */ + for ( m2 = 0; m2 < MAX_NUM_STEREO_BONDS && at[cur].sb_parity[m2]; m2 ++ ) + { + if ( chain[len-2] == at[cur].neighbor[(int)at[cur].sb_ord[m2]] ) + { + if ( n < num_inp_stereo0D ) + { + int parity1 = at[i].sb_parity[m1]; + int parity2 = at[cur].sb_parity[m2]; + int parity; + if ( (INCHI_PARITY_ODD == parity1 || INCHI_PARITY_EVEN == parity1) && + (INCHI_PARITY_ODD == parity2 || INCHI_PARITY_EVEN == parity2) ) { + /* well-defined parity */ + parity = (parity1==parity2)? INCHI_PARITY_EVEN : INCHI_PARITY_ODD; + } + else + { + parity = inchi_max(parity1, parity2); + } + (*stereo0D)[n].central_atom = (len==3)? chain[1] : NO_ATOM; + (*stereo0D)[n].parity = parity; + (*stereo0D)[n].type = len == 3? INCHI_StereoType_Allene : INCHI_StereoType_DoubleBond; + (*stereo0D)[n].neighbor[0] = at[i].sn_orig_at_num[m1]-1; + (*stereo0D)[n].neighbor[1] = i; + (*stereo0D)[n].neighbor[2] = cur; + (*stereo0D)[n].neighbor[3] = at[cur].sn_orig_at_num[m2] - 1; + n ++; + } + else + { + ret |= 1; + } + break; + } + } + } + } + } + } + + *num_stereo0D = n; + +exit_function: + if ( ret < 0 ) + { + if ( *atom ) inchi_free( *atom ); + if ( *stereo0D ) inchi_free( *stereo0D ); + *atom = NULL; + *stereo0D = NULL; + *num_atoms = 0; + *num_stereo0D = 0; + } + + return ret; +} + +/****************************************************************************************/ +int ExtractOneStructure( STRUCT_DATA *sd, + INPUT_PARMS *ip, + char *szTitle, + inchi_InputEx *inp, + INCHI_IOSTREAM *log_file, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM *prb_file, + ORIG_ATOM_DATA *orig_inp_data, + long *num_inp ) +{ + int *err = &sd->nStructReadError; + char *pStrErr = sd->pStrErrStruct; + inp_ATOM *at = NULL; + MOL_COORD *szCoord = NULL; + inchi_Atom *ati = NULL; + int nNumAtoms = 0; + int a1, j, valence, nDim, nNumBonds, nRet=0, max_num_at; + + /* vABParityUnknown holds actual value of an internal constant signifying */ + /* unknown parity: either the same as for undefined parity (default==standard) */ + /* or a specific one (non-std; requested by SLUUD switch). */ + int vABParityUnknown = AB_PARITY_UNDF; + if ( 0 != ( ip->nMode & REQ_MODE_DIFF_UU_STEREO) ) + { + /* Make labels for unknown and undefined stereo different */ + vABParityUnknown = AB_PARITY_UNKN; + } + + /******************************************************** + * + * Extract the structure + * + ********************************************************/ + + FreeOrigAtData( orig_inp_data ); + nDim = 0; + nNumBonds = 0; + + if ( !inp || (nNumAtoms = inp->num_atoms) <= 0 || !(ati = inp->atom) ) + { + TREAT_ERR (*err, 0, "Empty structure"); + *err = 98; + goto err_exit; + } + + max_num_at = ip->bLargeMolecules ? MAX_ATOMS : NORMALLY_ALLOWED_INP_MAX_ATOMS; + if ( nNumAtoms>=max_num_at ) + { + TREAT_ERR (*err, 0, "Too many atoms [did you forget 'LargeMolecules' switch?]"); + *err = 70; + orig_inp_data->num_inp_atoms = -1; + goto err_exit; + } + + at = (inp_ATOM *) inchi_calloc( nNumAtoms, sizeof(at[0]) ); + szCoord = (MOL_COORD *) inchi_calloc (inchi_max(nNumAtoms, 1), sizeof (MOL_COORD)); + + if ( !at || !szCoord ) + { + TREAT_ERR (*err, 0, "Out of RAM"); + *err = -1; + goto err_exit; + } + + /******************************************************** + * + * Extract typical for Molfile structural data + * + ********************************************************/ + /* extract atoms and bonds */ + for ( a1 = 0; a1 < nNumAtoms; a1 ++ ) + { + /* extract atoms */ + SetAtomProperties( at, szCoord, ati, a1, &nDim, pStrErr, err ); + + if ( *err ) + { + goto err_exit; + } + + /* extract connections */ + valence = ati[a1].num_bonds; + for ( j = 0; j < valence; j ++ ) + { + SetBondProperties( at, ati, a1, j, nNumAtoms, &nNumBonds, pStrErr, err ); + } + + if ( *err ) + { + goto err_exit; + } + } + + orig_inp_data->num_inp_atoms = nNumAtoms; + orig_inp_data->num_inp_bonds = nNumBonds; + orig_inp_data->num_dimensions = nDim; + + /* extract elements, chemical valences, implicit H, isotopic shifts */ + for ( a1 = 0; a1 < nNumAtoms; a1 ++ ) + { + /* set temp flags in at[a1].at_type */ + /* (1: data in atom name; 2: request to add H) */ + SetAtomAndBondProperties( at, + ati, + a1, + ip->bDoNotAddH, + pStrErr, + err ); + if ( *err ) + { + goto err_exit; + } + } + + /* clear temp flags in at[].at_type; add implicit H */ + SetNumImplicitH( at, nNumAtoms ); + + if ( *err ) + { + goto err_exit; + } + + /******************************************************** + * + * Extract the 0D parities (typical for CML) + * + ********************************************************/ + Extract0DParities( at, + nNumAtoms, + inp->stereo0D, + inp->num_stereo0D, + pStrErr, + err, + vABParityUnknown); + + if ( *err ) + { + goto err_exit; + } + + orig_inp_data->at = at; + at = NULL; + orig_inp_data->num_dimensions = nDim; + orig_inp_data->num_inp_atoms = nNumAtoms; + orig_inp_data->num_inp_bonds = nNumBonds; + orig_inp_data->szCoord = szCoord; + szCoord = NULL; + + /* chiral flag */ + + /* ***************************************************************************** + * Chiral flags are set in: + * - ReadTheStructure() inchi-1, wInChI + * - e_IchiMain.c -- main() -- C example of calling InChI dll + * - inchi_dll.c ExtractOneStructure -- InChI dll code (here) + *******************************************************************************/ + + if ( (ip->nMode & REQ_MODE_CHIR_FLG_STEREO) && (ip->nMode & REQ_MODE_STEREO) ) + { + if ( ip->bChiralFlag & FLAG_SET_INP_AT_CHIRAL ) + { + /* absolute stereo */ + ip->nMode &= ~(REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO); + sd->bChiralFlag &= ~FLAG_INP_AT_NONCHIRAL; + sd->bChiralFlag |= FLAG_INP_AT_CHIRAL; /* write AuxInfo as chiral */ + } + else + /*if ( ip->bChiralFlag & FLAG_SET_INP_AT_NONCHIRAL )*/ + { + /* relative stereo */ + ip->nMode &= ~(REQ_MODE_RACEMIC_STEREO); + ip->nMode |= REQ_MODE_RELATIVE_STEREO; + sd->bChiralFlag &= ~FLAG_INP_AT_CHIRAL; + sd->bChiralFlag |= FLAG_INP_AT_NONCHIRAL; /* write AuxInfo as non-chiral */ + } + } + else if ( ip->bChiralFlag & FLAG_SET_INP_AT_CHIRAL ) + { + sd->bChiralFlag &= ~FLAG_INP_AT_NONCHIRAL; + sd->bChiralFlag |= FLAG_INP_AT_CHIRAL; /* write AuxInfo as chiral */ + } + else if ( ip->bChiralFlag & FLAG_SET_INP_AT_NONCHIRAL ) + { + sd->bChiralFlag &= ~FLAG_INP_AT_CHIRAL; + sd->bChiralFlag |= FLAG_INP_AT_NONCHIRAL; /* write AuxInfo as non-chiral */ + } + + /* v. 1.05 extensions */ + { + int res = SetExtOrigAtDataByInChIExtInput( &orig_inp_data->polymer, + &orig_inp_data->v3000, + inp->polymer, + inp->v3000, + orig_inp_data->num_inp_atoms ); + if ( res ) + { + TREAT_ERR ( res, 0, "General error on treating polymers"); + *err = -1; + goto err_exit; + } + } + *num_inp += 1; + +err_exit: + + if ( at ) + /* if not moved to orig_inp_data/then nullified */ + inchi_free( at ); + if ( szCoord ) + inchi_free( szCoord ); + + nRet = TreatErrorsInReadTheStructure( sd, ip, LOG_MASK_NO_WARN, NULL, + log_file, out_file, prb_file, + orig_inp_data, num_inp ); + + return nRet; +} + +/********************************************************/ +int INCHI_DECL GetStringLength( char *p ) +{ + if ( p ) + return + (int) strlen(p); + else + return 0; +} + +#define MAX_MSG_LEN 512 + +/* GetINCHIfromINCHI does same as -InChI2InChI option: converts InChI into InChI for validation purposes */ +/* It may also be used to filter out specific layers. For instance, /Snon would remove stereochemical layer */ +/* Omitting /FixedH and/or /RecMet would remove Fixed-H or Reconnected layers */ +/* To keep all InChI layers use options string "/FixedH /RecMet"; option /InChI2InChI is not needed */ +/* inchi_InputINCHI is created by the user; strings in inchi_Output are allocated and deallocated by InChI */ +/* inchi_Output does not need to be initilized out to zeroes; see FreeINCHI() on how to deallocate it */ + +/*************************************************************/ +int INCHI_DECL GetINCHIfromINCHI( inchi_InputINCHI *inpInChI, + inchi_Output *out ) +{ + STRUCT_DATA struct_data; + STRUCT_DATA *sd = &struct_data; + + static char szMainOption[] = " ?InChI2InChI"; + + INCHI_CLOCK ic; + CANON_GLOBALS CG; + + int i; + char szSdfDataValue[MAX_SDF_VALUE+1]; + unsigned long ulDisplTime = 0; /* infinite, milliseconds */ + + INPUT_PARMS inp_parms; + INPUT_PARMS *ip = &inp_parms; + + int bReleaseVersion = bRELEASE_VERSION; + int nRet = 0, nRet1; + +#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) + int num_repeat = REPEAT_ALL; +#endif + + const char *argv[INCHI_MAX_NUM_ARG+1]; + int argc; + char *szOptions = NULL; + + INCHI_IOSTREAM inchi_file[3], *out_file = inchi_file, *log_file = inchi_file+1, *input_file = inchi_file+2; + +#if( TRACE_MEMORY_LEAKS == 1 ) + _CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF); +/* for execution outside the VC++ debugger uncomment one of the following two */ + +#ifdef MY_REPORT_FILE + _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_WARN, MY_REPORT_FILE ); + _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ERROR, MY_REPORT_FILE ); + _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ASSERT, MY_REPORT_FILE ); +#else + _CrtSetReportMode(_CRT_WARN | _CRT_ERROR, _CRTDBG_MODE_DEBUG); +#endif + + /* turn on floating point exceptions */ +#if ( !defined(__STDC__) || __STDC__ != 1 ) + { + /* Get the default control word. */ + int cw = _controlfp( 0,0 ); + + /* Set the exception masks OFF, turn exceptions on. */ + /*cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_INEXACT|EM_ZERODIVIDE|EM_DENORMAL);*/ + cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE|EM_DENORMAL); + + /* Set the control word. */ + _controlfp( cw, MCW_EM ); + } +#endif +#endif + + memset( out, 0, sizeof(*out) ); +#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) +repeat: + FreeINCHI( out ); + inchi_ios_close(out_file); + inchi_ios_close(log_file); + inchi_ios_reset(input_file); /* do not close input_file - its string buffer may point to inpInChI->szInChI */ +#endif + + /* Initialize internal for this function I/O streams as string buffers */ + inchi_ios_init(input_file, INCHI_IOSTREAM_TYPE_STRING, NULL); + inchi_ios_init(out_file, INCHI_IOSTREAM_TYPE_STRING, NULL); + inchi_ios_init(log_file, INCHI_IOSTREAM_TYPE_STRING, NULL); + + sd->bUserQuit = 0; + + /* clear original input structure */ + /* memset( inchi_file, 0, sizeof(inchi_file) ); */ + memset( sd, 0, sizeof(*sd) ); + memset( ip, 0, sizeof(*ip) ); + memset( szSdfDataValue , 0, sizeof( szSdfDataValue ) ); + + memset(&ic, 0, sizeof(ic)); + memset(&CG, 0, sizeof(CG)); + + szMainOption[1] = INCHI_OPTION_PREFX; + + if ( !inpInChI ) + { + nRet = _IS_ERROR; + goto exit_function; + } + + /* options */ + if ( inpInChI ) + { + int opt_len = (int) ( (inpInChI->szOptions? strlen(inpInChI->szOptions) : 0) + sizeof(szMainOption) + 1 ); + szOptions = (char*)inchi_calloc( opt_len+1, sizeof(szOptions[0]) ); + if ( szOptions ) + { + if ( inpInChI->szOptions ) + { + strcpy( szOptions, inpInChI->szOptions ); + } + strcat( szOptions, szMainOption ); + argc = parse_options_string ( szOptions, argv, INCHI_MAX_NUM_ARG ); + } + else + { + nRet = _IS_FATAL; + goto translate_RetVal; /* emergency exit */ + } + } + else + { + argc = 1; + argv[0] = ""; + argv[1] = NULL; + } + + if ( argc == 1 + +#ifdef TARGET_API_LIB + && (!inpInChI || !inpInChI->szInChI) +#endif + + || argc==2 && ( argv[1][0]==INCHI_OPTION_PREFX ) && + (!strcmp(argv[1]+1, "?") || !inchi_stricmp(argv[1]+1, "help") ) ) + { + HelpCommandLineParms(log_file); + out->szLog = log_file->s.pStr; + memset( log_file, 0, sizeof(*log_file) ); + nRet = _IS_EOF; + goto translate_RetVal; + } + + nRet1 = ReadCommandLineParms( argc, + argv, + ip, + szSdfDataValue, + &ulDisplTime, + bReleaseVersion, + log_file ); + + if ( szOptions ) + { + /* argv pointed to strings in szOptions */ + inchi_free( szOptions ); + szOptions = NULL; + } + /* INChI DLL specific */ + ip->bNoStructLabels = 1; + + if ( 0 > nRet1 ) + { + goto exit_function; + } + + if ( ip->bNoStructLabels ) + { + ip->pSdfLabel = NULL; + ip->pSdfValue = NULL; + } else if ( ip->nInputType == INPUT_INCHI_XML || + ip->nInputType == INPUT_INCHI_PLAIN || + ip->nInputType == INPUT_CMLFILE || + ip->nInputType == INPUT_INCHI ) + { + /* the input may contain both the header and the label of the structure */ + if ( !ip->pSdfLabel ) + ip->pSdfLabel = ip->szSdfDataHeader; + if ( !ip->pSdfValue ) + ip->pSdfValue = szSdfDataValue; + } + + if ( ip->nInputType && ip->nInputType != INPUT_INCHI ) + { + inchi_ios_eprint( log_file, "Input type set to INPUT_INCHI\n" ); + ip->nInputType = INPUT_INCHI; + } + + PrintInputParms( log_file, ip ); + + /*********************************/ + /* InChI -> Structure conversion */ + /*********************************/ + + /* input_file simulation */ + input_file->s.pStr = inpInChI->szInChI; + input_file->s.nUsedLength = (int) strlen(input_file->s.pStr)+1; + input_file->s.nAllocatedLength = input_file->s.nUsedLength; + input_file->s.nPtr = 0; + + /* buffer for the message */ + out->szMessage = (char *)inchi_calloc( MAX_MSG_LEN, sizeof(out->szMessage[0])); + if ( !out->szMessage ) + { + inchi_ios_eprint( log_file, "Cannot allocate output message buffer.\n"); + nRet = -1; + } + else + { + nRet = ReadWriteInChI( input_file, out_file, log_file, + ip, sd, + NULL, 0, NULL, + NULL, NULL, + out->szMessage, MAX_MSG_LEN, + NULL /*out->WarningFlags*/ , + &ic, &CG); + } + + if ( nRet >= 0 && out_file->s.pStr ) + { + /* success */ + char *p; + out->szInChI = out_file->s.pStr; + out->szAuxInfo = NULL; + + for ( p = strchr(out->szInChI, '\n'); p; p = strchr(p+1, '\n') ) + { + if ( !memcmp( p, "\nAuxInfo", 8 ) ) + { + *p = '\0'; /* remove LF after INChI */ + out->szAuxInfo = p+1; /* save pointer to AuxInfo */ + } + else if ( out->szAuxInfo || !p[1]) + { + /* remove LF after aux info or from the last char */ + *p = '\0'; + break; + } + } + out_file->s.pStr = NULL; + } + + /* + out->szLog = log_file->pStr; + log_file->pStr = NULL; + */ + +exit_function:; + + for ( i = 0; i < MAX_NUM_PATHS; i ++ ) + { + if ( ip->path[i] ) + { + inchi_free( (char*) ip->path[i] ); /* cast deliberately discards 'const' qualifier */ + ip->path[i] = NULL; + } + } + + SetBitFree( &CG ); + +#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) + if ( num_repeat-- > 0 ) + { + goto repeat; + } +#endif + +#ifdef TARGET_API_LIB + /* output */ + + if ( log_file->s.pStr && log_file->s.nUsedLength > 0 ) + { + while ( log_file->s.nUsedLength && '\n' == log_file->s.pStr[log_file->s.nUsedLength-1] ) + { + log_file->s.pStr[-- log_file->s.nUsedLength] = '\0'; /* remove last LF */ + } + if ( out ) + { + out->szLog = log_file->s.pStr; + log_file->s.pStr = NULL; + } + } + +#endif + +translate_RetVal: + /* close internal output streams */ + inchi_ios_close(out_file); + inchi_ios_close(log_file); + inchi_ios_reset(input_file); /* do not close input_file - its string buffer may point to inpInChI->szInChI */ + + switch (nRet) + { + case -3 : nRet = inchi_Ret_ERROR ; break; /* Error: no Structure has been created */ + case -2 : nRet = inchi_Ret_ERROR ; break; /* Error: no Structure has been created */ + case -1 : nRet = inchi_Ret_FATAL ; break; /* Severe error: no Structure has been created (typically; break; memory allocation failed) */ + default : + /* + if ( !outStruct->atom || !outStruct->num_atoms ) + { + nRet = inchi_Ret_EOF; + } + else + { + int m,n,t=0; + for ( m=0; m < 2; m ++ ) + { + for ( n=0; n < 2; n ++ ) + { + if ( outStruct->WarningFlags[m][n] ) { + t ++; + } + } + } + nRet = t? inchi_Ret_WARNING : inchi_Ret_OKAY; + } + */ + break; + } + + return nRet; +} + +/* + GetStructFromStdINCHI +*/ +EXPIMP_TEMPLATE INCHI_API + int INCHI_DECL GetStructFromStdINCHI( inchi_InputINCHI *inpInChI, + inchi_OutputStruct *outStruct ) +{ + if ( ( inpInChI ) && + ( inpInChI->szInChI ) && + ( strlen(inpInChI->szInChI) >= LEN_INCHI_STRING_PREFIX+3 ) && + ( inpInChI->szInChI[LEN_INCHI_STRING_PREFIX+1] == 'S' ) ) + { + /* brief check indicated valid std input (more checks in GetStructFromINCHI) */ + return GetStructFromINCHI( inpInChI, outStruct ); + } + else + { + /* non-std or just invalid input */ + return inchi_Ret_ERROR; + } +} + +/* + GetStructFromINCHIEx +*/ +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStructFromINCHIEx( inchi_InputINCHI *inpInChI, + inchi_OutputStructEx *outStruct ) +{ + INCHI_CLOCK ic; + CANON_GLOBALS CG; + STRUCT_DATA struct_data; + STRUCT_DATA *sd = &struct_data; + INPUT_PARMS inp_parms; + INPUT_PARMS *ip = &inp_parms; + INCHI_IOSTREAM inchi_file[3]; + INCHI_IOSTREAM *out_file = inchi_file, *log_file = inchi_file+1, *input_file = inchi_file+2; + int i, nRet=0, nRet1; + int bStdFormat=0; + int bReleaseVersion=bRELEASE_VERSION; + unsigned long ulDisplTime=0; /* infinite, milliseconds */ +#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) + int num_repeat = REPEAT_ALL; +#endif + static char szMainOption[] = " ?InChI2Struct"; + char szSdfDataValue[MAX_SDF_VALUE+1]; + const char *argv[INCHI_MAX_NUM_ARG+1]; + int argc; + char *szOptions = NULL; + /* conversion result */ + inp_ATOM *at = NULL; + int num_at = 0; + OrigAtDataPolymer *polymer = NULL; + OrigAtDataV3000 *v3000 = NULL; + +#if( TRACE_MEMORY_LEAKS == 1 ) + _CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF); + +/* for execution outside the VC++ debugger uncomment one of the following two */ +#ifdef MY_REPORT_FILE + _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_WARN, MY_REPORT_FILE ); + _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ERROR, MY_REPORT_FILE ); + _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ASSERT, MY_REPORT_FILE ); +#else + _CrtSetReportMode(_CRT_WARN | _CRT_ERROR, _CRTDBG_MODE_DEBUG); +#endif + + /* turn on floating point exceptions */ +#if ( !defined(__STDC__) || __STDC__ != 1 ) + { + /* Get the default control word. */ + int cw = _controlfp( 0,0 ); + + /* Set the exception masks OFF, turn exceptions on. */ + /*cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_INEXACT|EM_ZERODIVIDE|EM_DENORMAL);*/ + cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE|EM_DENORMAL); + + /* Set the control word. */ + _controlfp( cw, MCW_EM ); + } +#endif +#endif + + memset( outStruct, 0, sizeof(*outStruct) ); + +#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) +repeat: + FreeStructFromINCHI( &outStruct ); + inchi_ios_reset(input_file); /* do not close input_file - its string buffer may point to inpInChI->szInChI */ + inchi_ios_close(out_file); + inchi_ios_close(log_file); +#endif + + sd->bUserQuit = 0; + + /* Initialize internal for this function I/O streams as string buffers */ + inchi_ios_init(input_file, INCHI_IOSTREAM_TYPE_STRING, NULL); + inchi_ios_init(out_file, INCHI_IOSTREAM_TYPE_STRING, NULL); + inchi_ios_init(log_file, INCHI_IOSTREAM_TYPE_STRING, NULL); + + /* clear original input structure */ + memset( sd, 0, sizeof(*sd) ); + memset( ip, 0, sizeof(*ip) ); + memset( szSdfDataValue , 0, sizeof( szSdfDataValue ) ); + + memset(&ic, 0, sizeof(ic)); + memset(&CG, 0, sizeof(CG)); + + szMainOption[1] = INCHI_OPTION_PREFX; + + if ( !inpInChI ) + { + nRet = _IS_ERROR; + goto exit_function; + } + + /* options */ + if ( inpInChI /*&& inpInChI->szOptions*/ ) + { + /* fix bug discovered by Burt Leland 2008-12-23 */ + int opt_len = (inpInChI->szOptions? strlen(inpInChI->szOptions) : 0) + sizeof(szMainOption) + 1; + szOptions = (char*)inchi_calloc( opt_len+1, sizeof(szOptions[0]) ); + if ( szOptions ) + { + if ( inpInChI->szOptions ) + /* fix bug discovered by Burt Leland 2008-12-23 */ + strcpy( szOptions, inpInChI->szOptions ); + strcat( szOptions, szMainOption ); + argc = parse_options_string ( szOptions, argv, INCHI_MAX_NUM_ARG ); + } + else + { + nRet = _IS_FATAL; + goto translate_RetVal; /* emergency exit */ + } + } + else + { + argc = 1; + argv[0] = ""; + argv[1] = NULL; + } + + if ( argc == 1 +#ifdef TARGET_API_LIB + && (!inpInChI || !inpInChI->szInChI) +#endif + || argc==2 && ( argv[1][0]==INCHI_OPTION_PREFX ) && + (!strcmp(argv[1]+1, "?") || !inchi_stricmp(argv[1]+1, "help") ) ) { + HelpCommandLineParms(log_file); + outStruct->szLog = log_file->s.pStr; + nRet = _IS_EOF; + goto translate_RetVal; + } + + nRet1 = ReadCommandLineParms( argc, argv, ip, szSdfDataValue, + &ulDisplTime, bReleaseVersion, log_file ); + + if ( szOptions ) + { + /* argv pointed to strings in szOptions */ + inchi_free( szOptions ); + szOptions = NULL; + } + + /* INChI DLL specific */ + ip->bNoStructLabels = 1; + + if ( 0 > nRet1 ) + { + goto exit_function; + } + + if ( ip->bNoStructLabels ) + { + ip->pSdfLabel = NULL; + ip->pSdfValue = NULL; + } + else if ( ip->nInputType == INPUT_INCHI_XML || + ip->nInputType == INPUT_INCHI_PLAIN || + ip->nInputType == INPUT_CMLFILE || + ip->nInputType == INPUT_INCHI ) + { + /* the input may contain both the header and the label of the structure */ + if ( !ip->pSdfLabel ) + ip->pSdfLabel = ip->szSdfDataHeader; + if ( !ip->pSdfValue ) + ip->pSdfValue = szSdfDataValue; + } + + if ( ip->nInputType && ip->nInputType != INPUT_INCHI ) + { + inchi_ios_eprint( log_file, "Input type set to INPUT_INCHI\n" ); + ip->nInputType = INPUT_INCHI; + } + + if ( !inpInChI->szInChI ) + { + nRet = _IS_ERROR; + goto exit_function; + } + else + { + const int strict=0; /* do not use strict mode, it may be too alarmous */ + nRet = CheckINCHI(inpInChI->szInChI, strict); + if (nRet == INCHI_VALID_STANDARD) + { + bStdFormat = 1; + } + else if (nRet == INCHI_VALID_NON_STANDARD || nRet == INCHI_VALID_BETA ) + { + ; + } + else + { + nRet = _IS_ERROR; + goto exit_function; + } + } + + PrintInputParms( log_file, ip ); + /*********************************/ + /* InChI -> Structure conversion */ + /*********************************/ + + /* input_file simulation */ + + /* + that was incorrect simulation, and correct one is much simpler, see below + input_file->s.pStr = inpInChI->szInChI; + input_file->s.nUsedLength = (int) strlen(input_file->s.pStr)+1; + input_file->s.nAllocatedLength = input_file->s.nUsedLength; + input_file->s.nPtr = 0; + */ + inchi_ios_print_nodisplay( input_file, inpInChI->szInChI ); + + /* buffer for the message */ + outStruct->szMessage = (char *)inchi_calloc( MAX_MSG_LEN, sizeof(outStruct->szMessage[0])); + if ( !outStruct->szMessage ) + { + inchi_ios_eprint( log_file, "Cannot allocate output message buffer.\n"); + nRet = -1; + } + else + { + int num_bonds; + nRet = ReadWriteInChI( input_file, out_file, log_file, + ip, sd, + &at, &num_at, &num_bonds, + &polymer, &v3000, + outStruct->szMessage, + MAX_MSG_LEN, outStruct->WarningFlags, + &ic, &CG); + + if ( nRet >= 0 &&polymer ) + OrigAtData_CheckAndMakePolymerPhaseShifts( polymer, at, num_at, &num_bonds ); + } + + if ( nRet >= 0 && at && num_at ) + { + /* success */ + nRet = InpAtom0DToInchiAtom( at, num_at, + &outStruct->num_atoms, + &outStruct->atom, + &outStruct->num_stereo0D, + &outStruct->stereo0D ); + + if ( at ) + { + inchi_free( at ); + at = NULL; + } + + if ( nRet >= 0 && polymer ) + { + /* Check for and then replace ZZ for star atoms if Polymer extension is supplied */ + for (i=0; inum_atoms; i++) + { + if ( !strcmp( outStruct->atom[i].elname,"Zz") ) + { + strcpy( outStruct->atom[i].elname, "*" ); + } + } + } + + if ( nRet >=0 ) + { + if ( polymer || v3000 ) + { + nRet = SetInChIExtInputByExtOrigAtData( polymer, v3000, + &outStruct->polymer, + &outStruct->v3000, + outStruct->num_atoms ); /* pair to SetExtOrigAtDataByInChIExtInput */ + FreeExtOrigAtData( polymer, v3000 ); + polymer = NULL; + v3000 = NULL; + } + } + if ( nRet < 0 ) + { + inchi_ios_eprint( log_file, "Final structure conversion failed\n" ); + } + } + outStruct->szLog = log_file->s.pStr; + +exit_function:; + + for ( i = 0; i < MAX_NUM_PATHS; i ++ ) + { + if ( ip->path[i] ) + { + inchi_free( (char*) ip->path[i] ); /* cast deliberately discards 'const' qualifier */ + ip->path[i] = NULL; + } + } + + SetBitFree( &CG ); + +#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) + if ( num_repeat-- > 0 ) { + goto repeat; + } +#endif + +#ifdef TARGET_API_LIB + /* output */ + if ( log_file->s.pStr && log_file->s.nUsedLength > 0 ) + { + while ( log_file->s.nUsedLength && + '\n' == log_file->s.pStr[log_file->s.nUsedLength-1] ) + { + log_file->s.pStr[-- log_file->s.nUsedLength] = '\0'; /* remove last LF */ + } + if ( outStruct ) + { + outStruct->szLog = log_file->s.pStr; + log_file->s.pStr = NULL; + } + } +#endif + +translate_RetVal: + + /* Close internal I/O streams */ + /* that was incorrect also + inchi_ios_reset(input_file); */ /* do not close input_file - its string buffer may point to inpInChI->szInChI */ + inchi_ios_close( input_file ); + inchi_ios_close(out_file); + inchi_ios_close(log_file); + + switch (nRet) + { + case -3 : nRet = inchi_Ret_ERROR ; break; /* Error: no Structure has been created */ + case -2 : nRet = inchi_Ret_ERROR ; break; /* Error: no Structure has been created */ + case -1 : nRet = inchi_Ret_FATAL ; break; /* Severe error: no Structure has been created (typically; break; memory allocation failed) */ + default : + if ( !outStruct->atom || !outStruct->num_atoms ) + { + nRet = inchi_Ret_EOF; + } + else + { + int m,n,t=0; + for ( m=0; m < 2; m ++ ) + { + for ( n=0; n < 2; n ++ ) + { + if ( outStruct->WarningFlags[m][n] ) + { + t++; + } + } + } + nRet = t? inchi_Ret_WARNING : inchi_Ret_OKAY; + } + break; + } + + return nRet; +} + +/* + GetStructFromINCHI +*/ +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStructFromINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *out ) +{ +int ret = 0; + + inchi_OutputStructEx outex; + memset( out, 0, sizeof(*out) ); + + ret = GetStructFromINCHIEx( inpInChI, &outex ); + + out->szLog = outex.szLog; + out->szMessage = outex.szMessage; + out->WarningFlags[0][0] = outex.WarningFlags[0][0]; + out->WarningFlags[0][1] = outex.WarningFlags[0][1]; + out->WarningFlags[1][0] = outex.WarningFlags[1][0]; + out->WarningFlags[1][1] = outex.WarningFlags[1][1]; + + if ( ret == inchi_Ret_OKAY || ret == inchi_Ret_WARNING ) + { + out->num_atoms = outex.num_atoms; + out->atom = outex.atom; + out->num_stereo0D = outex.num_stereo0D; + out->stereo0D = outex.stereo0D; + } + + return ret; +} + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeStructFromINCHIEx( inchi_OutputStructEx *out ) +{ + if ( !out ) + return; + + if ( out->atom ) + inchi_free( out->atom ); + if ( out->stereo0D ) + inchi_free( out->stereo0D ); + if ( out->szLog ) + inchi_free( out->szLog ); + if ( out->szMessage ) + inchi_free( out->szMessage ); + if ( out->polymer || out->v3000 ) + FreeInChIExtInput( out->polymer, out->v3000 ); + + memset( out, 0, sizeof(*out) ); +} + +/* + +*/ +void FreeInChIExtInput( inchi_Input_Polymer *polymer, inchi_Input_V3000 *v3000 ) +{ + int k; + if ( polymer ) + { + if ( polymer->n && polymer->units ) + { + int k; + for (k=0; kn; k++) + { + if ( polymer->units[k] ) + { + if ( polymer->units[k]->alist ) + { inchi_free( polymer->units[k]->alist ); polymer->units[k]->alist = NULL; } + if ( polymer->units[k]->blist ) + { inchi_free( polymer->units[k]->blist ); polymer->units[k]->blist = NULL; } + } + inchi_free( polymer->units[k] ); + } + inchi_free( polymer->units ); + polymer->units = NULL; + inchi_free( polymer ); + } + } + if ( v3000 ) + { + if ( v3000->atom_index_orig ) + { + inchi_free( v3000->atom_index_orig ); + v3000->atom_index_orig = NULL; + } + if ( v3000->atom_index_fin ) + { + inchi_free( v3000->atom_index_fin ); + v3000->atom_index_fin = NULL; + } + if ( v3000->n_haptic_bonds && v3000->lists_haptic_bonds ) + { + for (k=0; kn_haptic_bonds; k++) + if ( v3000->lists_haptic_bonds[k] ) + { + inchi_free( v3000->lists_haptic_bonds[k] ); + v3000->lists_haptic_bonds[k] = NULL; + } + inchi_free( v3000->lists_haptic_bonds ); + v3000->lists_haptic_bonds = NULL; + } + if ( v3000->n_steabs && v3000->lists_steabs ) + { + for (k=0; kn_steabs; k++) + if ( v3000->lists_steabs[k] ) + { + inchi_free( v3000->lists_steabs[k] ); + v3000->lists_steabs[k] = NULL; + } + inchi_free( v3000->lists_steabs ); + v3000->lists_steabs = NULL; + } + if ( v3000->n_sterel && v3000->lists_sterel ) + { + for (k=0; kn_sterel; k++) + if ( v3000->lists_sterel[k] ) + { + inchi_free( v3000->lists_sterel[k] ); + v3000->lists_sterel[k] = NULL; + } + inchi_free( v3000->lists_sterel ); + v3000->lists_sterel = NULL; + } + if ( v3000->n_sterac && v3000->lists_sterac ) + { + for (k=0; kn_sterac; k++) + if ( v3000->lists_sterac[k] ) + { + inchi_free( v3000->lists_sterac[k] ); + v3000->lists_sterac[k] = NULL; + } + inchi_free( v3000->lists_sterac ); + v3000->lists_sterac = NULL; + } + memset( v3000, 0, sizeof( *v3000 ) ); + } +} + +/* + SetExtOrigAtDataByInChIExtInput +*/ +int SetExtOrigAtDataByInChIExtInput( OrigAtDataPolymer **ppPolymer, + OrigAtDataV3000 **ppV3000, + inchi_Input_Polymer *iep, + inchi_Input_V3000 *iev, + int nat) +{ + int k, m, err = 0; + OrigAtDataV3000 *pv=NULL; + + /* Polymers */ + if ( iep && iep->n ) + { + /* Prepare OrigAtDataPolymer container */ + *ppPolymer = (OrigAtDataPolymer *) inchi_calloc( 1, sizeof(OrigAtDataPolymer) ); + if ( !*ppPolymer ) + { + err = 9001; + goto exitf; + } + + /* Convert Molfile's Sgroup's to OrigAtDataPolymerUnit's */ + (*ppPolymer)->units = (OrigAtDataPolymerUnit**) inchi_calloc( iep->n, sizeof((*ppPolymer)->units[0]) ); + if ( !(*ppPolymer)->units ) + { + err = 9001; + goto exitf; + } + memset( (*ppPolymer)->units, 0, sizeof( *(*ppPolymer)->units ) ); + + (*ppPolymer)->n = iep->n; + (*ppPolymer)->valid = -1; + (*ppPolymer)->really_do_phase_shift = 0; + + for (k=0; kn; k++ ) + { + int q=0; + OrigAtDataPolymerUnit *unitk; + + inchi_Input_PolymerUnit *groupk = iep->units[k]; + (*ppPolymer)->units[k] = (OrigAtDataPolymerUnit*) inchi_calloc( 1, sizeof(OrigAtDataPolymerUnit) ); + unitk = (*ppPolymer)->units[k]; + if (!unitk ) + { + err = 9001; + goto exitf; + } + + memset( unitk, 0, sizeof( *unitk ) ); + unitk->id = groupk->id; + unitk->type = groupk->type; + unitk->subtype = groupk->subtype; + unitk->conn = groupk->conn; + unitk->label = groupk->label; + unitk->real_kind = POLYMER_UNIT_KIND_UNKNOWN; + + for (q=0; q<4; q++) + { + unitk->xbr1[q] = groupk->xbr1[q]; + unitk->xbr2[q] = groupk->xbr2[q]; + } + strcpy( unitk->smt, groupk->smt ); + unitk->na = groupk->na; + unitk->alist = (int *) inchi_calloc( unitk->na, sizeof(int) ); + if (!unitk->alist ) + { + err = 9001; + goto exitf; + } + for (m=0; mna; m++) + unitk->alist[m] = groupk->alist[m]; + unitk->nb = groupk->nb; + if ( unitk->nb > 0 ) + { + unitk->blist = (int *) inchi_calloc( 2*unitk->nb, sizeof(int) ); + if (!unitk->blist ) + { + err = 9001; + goto exitf; + } + for (m=0; m < 2*groupk->nb; m++) + unitk->blist[m] = groupk->blist[m]; + } + else + unitk->blist = NULL; + } + } + + /* V3000 Extensions */ + if ( iev ) + { + int m, k, nn; + *ppV3000 = (OrigAtDataV3000 *) inchi_calloc( 1, sizeof(OrigAtDataV3000) ); + pv = *ppV3000; + if ( !pv ) + { + err = 9001; + goto exitf; + } + memset( pv, 0, sizeof(*pv) ); + + pv->n_collections = iev->n_collections; + pv->n_haptic_bonds = iev->n_haptic_bonds; + pv->n_non_haptic_bonds = iev->n_non_haptic_bonds; + pv->n_sgroups = iev->n_sgroups; + pv->n_non_star_atoms = iev->n_non_star_atoms; + pv->n_star_atoms = iev->n_star_atoms; + pv->n_steabs = iev->n_steabs; + pv->n_sterac = iev->n_sterac; + pv->n_sterel = iev->n_sterel; + pv->n_3d_constraints = iev->n_3d_constraints; + + if ( iev->atom_index_orig ) + { + pv->atom_index_orig = (int *) inchi_calloc( nat, sizeof(int) ); + if ( NULL==pv->atom_index_orig ) + { + err = 9001; + goto exitf; + } + memcpy( pv->atom_index_orig, iev->atom_index_orig, nat); + } + if ( iev->atom_index_fin ) + { + pv->atom_index_fin = (int *) inchi_calloc( nat, sizeof(int) ); + if ( NULL==pv->atom_index_fin ) + { + err = 9001; + goto exitf; + } + memcpy( pv->atom_index_fin, iev->atom_index_fin, nat); + } + if ( iev->n_haptic_bonds && iev->lists_haptic_bonds ) + { + pv->lists_haptic_bonds = (int **) calloc( iev->n_haptic_bonds, sizeof (int*) ); + if ( NULL==pv->lists_haptic_bonds ) + { + err = 9001; + goto exitf; + } + for (m=0; mn_haptic_bonds; m++) + { + int *lst=NULL; + int *mol_lst = iev->lists_haptic_bonds[m]; + nn = mol_lst[2] + 3; + lst = pv->lists_haptic_bonds[m] = (int *) calloc( nn, sizeof (int) ); + if ( NULL==lst ) + { + err = 9001; + goto exitf; + } + for (k=0; kn_steabs && iev->lists_steabs ) + { + pv->lists_steabs = (int **) calloc( iev->n_steabs, sizeof (int*) ); + if ( NULL==pv->lists_steabs ) { err = 9001; goto exitf; } + for (m=0; mn_steabs; m++) + { + int *lst=NULL; + int *mol_lst = iev->lists_steabs[m]; + nn = mol_lst[1] + 2; + lst = pv->lists_steabs[m] = (int *) calloc( nn, sizeof (int) ); + if ( NULL==lst ) { err = 9001; goto exitf; } + for (k=0; kn_sterac && iev->lists_sterac ) + { + pv->lists_sterac = (int **) calloc( iev->n_sterac, sizeof (int*) ); + if ( NULL==pv->lists_sterac ) { err = 9001; goto exitf; } + for (m=0; mn_sterac; m++) + { + int *lst=NULL; + int *mol_lst = iev->lists_sterac[m]; + nn = mol_lst[1] + 2; + lst = pv->lists_sterac[m] = (int *) calloc( nn, sizeof (int) ); + if ( NULL==lst ) { err = 9001; goto exitf; } + for (k=0; kn_sterel && iev->lists_sterel ) + { + pv->lists_sterel = (int **) calloc( iev->n_sterel, sizeof (int*) ); + if ( NULL==pv->lists_sterel ) { err = 9001; goto exitf; } + for (m=0; mn_sterel; m++) + { + int *lst=NULL; + int *mol_lst = iev->lists_sterel[m]; + nn = mol_lst[1] + 2; + lst = pv->lists_sterel[m] = (int *) calloc( nn, sizeof (int) ); + if ( NULL==lst ) { err = 9001; goto exitf; } + for (k=0; kn > 0 ) + { + *iip = (inchi_Input_Polymer *) inchi_calloc( 1, sizeof(inchi_Input_Polymer) ); + if ( !*iip ) + { + err = 9001; + goto exitf; + } + (*iip)->n = orp->n; + (*iip)->units = (inchi_Input_PolymerUnit**) inchi_calloc( orp->n, sizeof((*iip)->units[0]) ); + if ( !(*iip)->units ) + { err = 9001; goto exitf; } + memset( (*iip)->units, 0, sizeof( * (*iip)->units ) ); + for (k=0; kn; k++ ) + { + int q=0; + inchi_Input_PolymerUnit *unitk; + OrigAtDataPolymerUnit *groupk = orp->units[k]; + (*iip)->units[k] = (inchi_Input_PolymerUnit*) inchi_calloc( 1, sizeof(inchi_Input_PolymerUnit) ); + unitk = (*iip)->units[k]; + if (!unitk ) + { err = 9001; goto exitf; } + memset( unitk, 0, sizeof(*unitk) ); + unitk->id = groupk->id; + unitk->type = groupk->type; + unitk->subtype = groupk->subtype; + unitk->conn = groupk->conn; + unitk->label = groupk->label; + for (q=0; q<4; q++) + { + unitk->xbr1[q] = groupk->xbr1[q]; + unitk->xbr2[q] = groupk->xbr2[q]; + } + strcpy( unitk->smt, groupk->smt ); + unitk->na = groupk->na; + unitk->alist = (int *) inchi_calloc( unitk->na, sizeof(int) ); + if (!unitk->alist ) + { err = 9001; goto exitf; } + for (m=0; mna; m++) + unitk->alist[m] = groupk->alist[m]; + unitk->nb = groupk->nb; + if ( unitk->nb > 0 ) + { + unitk->blist = (int *) inchi_calloc( 2*unitk->nb, sizeof(int) ); + if (!unitk->blist ) + { err = 9001; goto exitf; } + for (m=0; m < 2*groupk->nb; m++) + unitk->blist[m] = groupk->blist[m]; + } + else + unitk->blist = NULL; + } + } + + if ( orv ) + { + int m, k, nn; + *iiv = (inchi_Input_V3000 *) inchi_calloc( 1, sizeof(OrigAtDataV3000) ); + if ( !*iiv ) + { err = 9001; goto exitf; } + memset( *iiv, 0, sizeof( **iiv ) ); + + (*iiv)->n_collections = orv->n_collections; + (*iiv)->n_haptic_bonds = orv->n_haptic_bonds; + (*iiv)->n_non_haptic_bonds = orv->n_non_haptic_bonds; + (*iiv)->n_sgroups = orv->n_sgroups; + (*iiv)->n_non_star_atoms = orv->n_non_star_atoms; + (*iiv)->n_star_atoms = orv->n_star_atoms; + (*iiv)->n_steabs = orv->n_steabs; + (*iiv)->n_sterac = orv->n_sterac; + (*iiv)->n_sterel = orv->n_sterel; + (*iiv)->n_3d_constraints = orv->n_3d_constraints; + + if ( orv->atom_index_orig ) + { + (*iiv)->atom_index_orig = (int *) inchi_calloc( nat, sizeof(int) ); + if ( NULL==(*iiv)->atom_index_orig ) + { + err = 9001; + goto exitf; + } + memcpy( (*iiv)->atom_index_orig, orv->atom_index_orig, nat); + } + if ( orv->atom_index_fin ) + { + (*iiv)->atom_index_fin = (int *) inchi_calloc( nat, sizeof(int) ); + if ( NULL==(*iiv)->atom_index_fin ) + { + err = 9001; + goto exitf; + } + memcpy( (*iiv)->atom_index_fin, orv->atom_index_fin, nat); + } + if ( orv->n_haptic_bonds && orv->lists_haptic_bonds ) + { + (*iiv)->lists_haptic_bonds = (int **) calloc( orv->n_haptic_bonds, sizeof (int*) ); + if ( NULL==(*iiv)->lists_haptic_bonds ) + { + err = 9001; + goto exitf; + } + for (m=0; mn_haptic_bonds; m++) + { + int *lst=NULL; + int *mol_lst = orv->lists_haptic_bonds[m]; + nn = mol_lst[2] + 3; + lst = (*iiv)->lists_haptic_bonds[m] = (int *) calloc( nn, sizeof (int) ); + if ( NULL==lst ) + { + err = 9001; + goto exitf; + } + for (k=0; kn_steabs && orv->lists_steabs ) + { + (*iiv)->lists_steabs = (int **) calloc( orv->n_steabs, sizeof (int*) ); + if ( NULL==(*iiv)->lists_steabs ) { err = 9001; goto exitf; } + for (m=0; mn_steabs; m++) + { + int *lst=NULL; + int *mol_lst = orv->lists_steabs[m]; + nn = mol_lst[1] + 2; + lst = (*iiv)->lists_steabs[m] = (int *) calloc( nn, sizeof (int) ); + if ( NULL==lst ) { err = 9001; goto exitf; } + for (k=0; kn_sterac && orv->lists_sterac ) + { + (*iiv)->lists_sterac = (int **) calloc( orv->n_sterac, sizeof (int*) ); + if ( NULL==(*iiv)->lists_sterac ) { err = 9001; goto exitf; } + for (m=0; mn_sterac; m++) + { + int *lst=NULL; + int *mol_lst = orv->lists_sterac[m]; + nn = mol_lst[1] + 2; + lst = (*iiv)->lists_sterac[m] = (int *) calloc( nn, sizeof (int) ); + if ( NULL==lst ) { err = 9001; goto exitf; } + for (k=0; kn_sterel && orv->lists_sterel ) + { + (*iiv)->lists_sterel = (int **) calloc( orv->n_sterel, sizeof (int*) ); + if ( NULL==(*iiv)->lists_sterel ) { err = 9001; goto exitf; } + for (m=0; mn_sterel; m++) + { + int *lst=NULL; + int *mol_lst = orv->lists_sterel[m]; + nn = mol_lst[1] + 2; + lst = (*iiv)->lists_sterel[m] = (int *) calloc( nn, sizeof (int) ); + if ( NULL==lst ) { err = 9001; goto exitf; } + for (k=0; k= 800 && defined(_USRDLL) && defined(BUILD_LINK_AS_DLL) ) +/* Win32 & MS VC ++, compile and link as a DLL */ +/*********************************************************/ +/* C calling conventions export from Win32 dll */ +/*********************************************************/ +/* prototypes */ +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + +int cdecl_GetINCHI( inchi_Input *inp, inchi_Output *out ); +int cdecl_GetStdINCHI( inchi_Input *inp, inchi_Output *out ); +void cdecl_FreeINCHI( inchi_Output *out ); +void cdecl_FreeStdINCHI( inchi_Output *out ); +int cdecl_GetStringLength( char *p ); +int cdecl_Get_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, + int bDoNotAddH, + int bDiffUnkUndfStereo, + InchiInpData *pInchiInp ); +int cdecl_Get_std_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, + int bDoNotAddH, + InchiInpData *pInchiInp ); +/*void cdecl_Free_inchi_Input( inchi_Input *pInp );*/ +void cdecl_Free_std_inchi_Input( inchi_Input *pInp ); +int cdecl_GetStructFromINCHI( inchi_InputINCHI *inpInChI, + inchi_OutputStruct *outStruct ); +int cdecl_GetStructFromStdINCHI( inchi_InputINCHI *inpInChI, + inchi_OutputStruct *outStruct ); +int cdecl_GetINCHIfromINCHI( inchi_InputINCHI *inpInChI, + inchi_Output *out ); +void cdecl_FreeStructFromINCHI( inchi_OutputStruct *outStruct ); +void cdecl_FreeStructFromStdINCHI( inchi_OutputStruct *outStruct ); +int cdecl_CheckINCHI( const char *szINCHI, const int strict); + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + +/* implementation */ +/* libinchi.def provides export without cdecl_ prefixes */ + +/********************************************************/ +int cdecl_GetINCHI( inchi_Input *inp, inchi_Output *out ) +{ + return GetINCHI( inp, out ); +} +/********************************************************/ +int cdecl_GetStdINCHI( inchi_Input *inp, inchi_Output *out ) +{ + return GetStdINCHI( inp, out ); +} +/********************************************************/ +void cdecl_FreeINCHI( inchi_Output *out ) +{ + FreeINCHI( out ); +} +/********************************************************/ +void cdecl_FreeStdINCHI( inchi_Output *out ) +{ + FreeStdINCHI( out ); +} +/********************************************************/ +int cdecl_GetStringLength( char *p ) +{ + return GetStringLength( p ); +} +/********************************************************/ +int cdecl_Get_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, + int bDoNotAddH, + int bDiffUnkUndfStereo, + InchiInpData *pInchiInp ) +{ + return Get_inchi_Input_FromAuxInfo( szInchiAuxInfo, + bDoNotAddH, + bDiffUnkUndfStereo, + pInchiInp ); +} + +/********************************************************/ +int cdecl_Get_std_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, + int bDoNotAddH, + InchiInpData *pInchiInp ) +{ + return Get_std_inchi_Input_FromAuxInfo( szInchiAuxInfo, + bDoNotAddH, + pInchiInp ); +} + +/********************************************************/ +void cdecl_Free_std_inchi_Input( inchi_Input *pInp ) +{ + Free_std_inchi_Input( pInp ); +} + +/********************************************************/ +void cdecl_Free_inchi_Input( inchi_Input *pInp ) +{ + Free_inchi_Input( pInp ); +} + +/********************************************************/ +int cdecl_GetStructFromINCHI( inchi_InputINCHI *inpInChI, + inchi_OutputStruct *outStruct ) +{ + return GetStructFromINCHI( inpInChI, outStruct ); +} + +/********************************************************//********************************************************/ +int cdecl_GetStructFromStdINCHI( inchi_InputINCHI *inpInChI, + inchi_OutputStruct *outStruct ) +{ + return GetStructFromStdINCHI( inpInChI, outStruct ); +} + +/********************************************************/ +void cdecl_FreeStructFromINCHI( inchi_OutputStruct *outStruct ) +{ + FreeStructFromINCHI( outStruct ); +} + +/********************************************************/ +int cdecl_GetINCHIfromINCHI( inchi_InputINCHI *inpInChI, + inchi_Output *out ) +{ + return GetINCHIfromINCHI( inpInChI, out ); +} + +/********************************************************/ +void cdecl_FreeStructFromStdINCHI( inchi_OutputStruct *outStruct ) +{ + FreeStructFromStdINCHI( outStruct ); +} + +/********************************************************/ +int cdecl_CheckINCHI(const char *szINCHI, const int strict) +{ + return CheckINCHI( szINCHI, strict ); +} +#endif + +#if( defined(__GNUC__) && __GNUC__ >= 3 && defined(__MINGW32__) && defined(_WIN32) ) +#include +/*********************************************************/ +/* Pacal calling conventions export from Win32 dll */ +/*********************************************************/ +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif +/* prototypes */ + +int PASCAL pasc_GetINCHI( inchi_Input *inp, inchi_Output *out ); +int PASCAL pasc_GetStdINCHI( inchi_Input *inp, inchi_Output *out ); +void PASCAL pasc_FreeINCHI( inchi_Output *out ); +void PASCAL pasc_FreeStdINCHI( inchi_Output *out ); +int PASCAL pasc_GetStringLength( char *p ); +int PASCAL pasc_Get_std_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, + int bDoNotAddH, + InchiInpData *pInchiInp ); +int PASCAL pasc_Get_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, + int bDoNotAddH, + int bDiffUnkUndfStereo, + InchiInpData *pInchiInp ); +void PASCAL pasc_Free_inchi_Input( inchi_Input *pInp ); +void PASCAL pasc_Free_std_inchi_Input( inchi_Input *pInp ); +void PASCAL pasc_FreeStructFromINCHI( inchi_OutputStruct *out ); +void PASCAL pasc_FreeStructFromStdINCHI( inchi_OutputStruct *out ); +int PASCAL pasc_GetStructFromINCHI( inchi_InputINCHI *inp, inchi_OutputStruct *out ); +int PASCAL pasc_GetStructFromStdINCHI( inchi_InputINCHI *inp, inchi_OutputStruct *out ); +int PASCAL pasc_CheckINCHI(const char *szINCHI, const int strict); + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + +/* implementation */ +/* libinchi.def provides export without PASCAL pasc_ prefixes */ +/********************************************************/ +int PASCAL pasc_GetINCHI( inchi_Input *inp, inchi_Output *out ) +{ + return GetINCHI( inp, out ); +} +/********************************************************/ +int PASCAL pasc_GetStdINCHI( inchi_Input *inp, inchi_Output *out ) +{ + return GetStdINCHI( inp, out ); +} +/********************************************************/ +void PASCAL pasc_FreeINCHI( inchi_Output *out ) +{ + FreeINCHI( out ); +} +/********************************************************/ +void PASCAL pasc_FreeStdINCHI( inchi_Output *out ) +{ + FreeStdINCHI( out ); +} +/********************************************************/ +int PASCAL pasc_GetStringLength( char *p ) +{ + return GetStringLength( p ); +} +/********************************************************/ +int PASCAL pasc_Get_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, + int bDoNotAddH, + int bDiffUnkUndfStereo, + InchiInpData *pInchiInp ) +{ + return Get_inchi_Input_FromAuxInfo( szInchiAuxInfo, bDoNotAddH, + bDiffUnkUndfStereo, pInchiInp ); +} +/********************************************************/ +int PASCAL pasc_Get_std_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, + int bDoNotAddH, + InchiInpData *pInchiInp ) +{ + return Get_std_inchi_Input_FromAuxInfo( szInchiAuxInfo, bDoNotAddH, pInchiInp ); +} +/********************************************************/ +void PASCAL pasc_Free_inchi_Input( inchi_Input *pInp ) +{ + Free_inchi_Input( pInp ); +} +/********************************************************/ +void PASCAL pasc_Free_std_inchi_Input( inchi_Input *pInp ) +{ + Free_std_inchi_Input( pInp ); +} +/********************************************************/ +void PASCAL pasc_FreeStructFromINCHI( inchi_OutputStruct *out ) +{ + FreeStructFromINCHI( out ); +} +/********************************************************/ +void PASCAL pasc_FreeStructFromStdINCHI( inchi_OutputStruct *out ) +{ + FreeStructFromStdINCHI( out ); +} +/********************************************************//********************************************************/ +int PASCAL pasc_GetStructFromINCHI( inchi_InputINCHI *inp, inchi_OutputStruct *out ) +{ + return GetStructFromINCHI( inp, out ); +} +/********************************************************//********************************************************/ +int PASCAL pasc_GetStructFromStdINCHI( inchi_InputINCHI *inp, inchi_OutputStruct *out ) +{ + return GetStructFromStdINCHI( inp, out ); +} +/********************************************************/ +int PASCAL pasc_CheckINCHI(const char *szINCHI, const int strict) +{ + return CheckINCHI( szINCHI, strict ); +} + +#endif diff --git a/INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll.h b/INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll.h new file mode 100644 index 0000000..5d2b2a5 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll.h @@ -0,0 +1,58 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef __INCHI_DLL_H__ +#define __INCHI_DLL_H__ + + +#define INCHI_MAX_NUM_ARG 32 + +int parse_options_string ( char *cmd, + const char *argv[], + int maxargs ); +void produce_generation_output( inchi_Output *out, + STRUCT_DATA *sd, + INPUT_PARMS *ip, + INCHI_IOSTREAM *log_file, + INCHI_IOSTREAM *out_file ); +void copy_corrected_log_tail( inchi_Output *out, + INCHI_IOSTREAM *log_file ); + + +#endif /* __INCHI_DLL_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/inchi_dll_a.c b/INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll_a.c similarity index 57% rename from INCHI-1-SRC/INCHI_API/inchi_dll/inchi_dll_a.c rename to INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll_a.c index bfe8d05..e92fb4c 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/inchi_dll_a.c +++ b/INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll_a.c @@ -1,1586 +1,1415 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - InChI - API 1.02 - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "inpdef.h" -#include "ichi.h" -#include "strutil.h" -#include "util.h" -#include "ichierr.h" -#include "ichimain.h" -#include "extr_ct.h" -#include "ichi_io.h" - -#include "ichicomp.h" -#include "ichitaut.h" -#include "ichinorm.h" - - -#include "ichisize.h" -#include "mode.h" -#include "inchi_api.h" - -#include "inchi_dll_a.h" /* not inchi_api.h as it hides internal data types */ - - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Local prototypes. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -int parse_options_string ( char *cmd, const char *argv[], int maxargs ); - -int ExtractOneStructure( STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, - inchi_Input *pInp, INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *prb_file, - ORIG_ATOM_DATA *orig_inp_data, long *num_inp, char *pStr, int nStrLen ); - - - -int NormOneStructureINChI(INCHIGEN_DATA *pGenData, INCHIGEN_CONTROL * HGen, - int iINChI, INCHI_IOSTREAM *inp_file); -int CanonOneStructureINChI(INCHIGEN_CONTROL *HGen, int iINChI, INCHI_IOSTREAM *inp_file); - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -InChI Generator: create generator -Returns handle of generator object or NULL on failure -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -INCHIGEN_HANDLE INCHI_DECL STDINCHIGEN_Create(void) -{ - return INCHIGEN_Create(); -} - - -INCHIGEN_HANDLE INCHI_DECL INCHIGEN_Create(void) -{ -INCHIGEN_CONTROL * HGen = NULL; - - - HGen = (INCHIGEN_CONTROL *)inchi_malloc( sizeof(INCHIGEN_CONTROL) ); - - if (!HGen) - return (INCHIGEN_HANDLE) NULL; - - memset(HGen, 0, sizeof(INCHIGEN_CONTROL)); - - /*^^^ Set/init aliases */ - memset(&(HGen->InpParms), 0, sizeof(INPUT_PARMS)); - memset(&(HGen->StructData), 0, sizeof(STRUCT_DATA) ); - - HGen->ulTotalProcessingTime = 0; - HGen->num_err = 0; - HGen->num_inp = 0; - HGen->szTitle[0] = '\0'; - - HGen->pStr = (char*) inchi_malloc(PSTR_BUFFER_SIZE); - if (!HGen->pStr) - { - inchi_free(HGen); - return (INCHIGEN_HANDLE) NULL; - } - HGen->pStr[0] = '\0'; - - - /*^^^ Initialize output streams as string buffers */ - inchi_ios_init(&(HGen->inchi_file[0]), INCHI_IOSTREAM_STRING, NULL); - inchi_ios_init(&(HGen->inchi_file[1]), INCHI_IOSTREAM_STRING, NULL); - inchi_ios_init(&(HGen->inchi_file[2]), INCHI_IOSTREAM_STRING, NULL); - - - memset(&(HGen->OrigInpData), 0, sizeof( HGen->OrigInpData ) ); - memset(&(HGen->PrepInpData[0]), 0, 2*sizeof( HGen->PrepInpData[0] ) ); - - memset(HGen->pINChI, 0, sizeof(HGen->pINChI) ); - memset(HGen->pINChI_Aux, 0, sizeof(HGen->pINChI_Aux) ); - - - return (INCHIGEN_HANDLE) HGen; -} - - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -InChI Generator: initialization stage (accepts a specific structure) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - -int INCHI_DECL STDINCHIGEN_Setup(INCHIGEN_HANDLE _HGen, - INCHIGEN_DATA * pGenData, - inchi_Input * pInp) -{ -INCHIGEN_CONTROL *HGen = (INCHIGEN_CONTROL *)_HGen; -INPUT_PARMS *ip = &(HGen->InpParms); -STRUCT_DATA *sd = &(HGen->StructData); -int retcode = inchi_Ret_OKAY; -int force_std=0; - - retcode = INCHIGEN_Setup(_HGen, pGenData, pInp); - - /* Ensure standardness */ - if ( ip->bINChIOutputOptions & INCHI_OUT_SAVEOPT ) - { - ip->bINChIOutputOptions &= ~INCHI_OUT_SAVEOPT; - /* - if ( !force_std ) - { - AddMOLfileError(sd->pStrErrStruct, "Options adjusted to STDINCHI mode"); - force_std = 1; - } - AddMOLfileError(sd->pStrErrStruct, "SaveOpt ignored"); - sd->nErrorType = _IS_WARNING; - */ - retcode = _IS_WARNING; - } - if ( 0 != ( ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) - { - ip->bTautFlags &= ~TG_FLAG_RECONNECT_COORD; - /* - if ( !force_std ) - { - AddMOLfileError(sd->pStrErrStruct, "Options adjusted to STDINCHI mode"); - force_std = 1; - } - AddMOLfileError(sd->pStrErrStruct, "RecMet ignored"); - sd->nErrorType = _IS_WARNING; - */ - retcode = _IS_WARNING; - } - if ( 0 != (ip->nMode & REQ_MODE_BASIC) ) - { - ip->nMode &= ~REQ_MODE_BASIC; - /* - if ( !force_std ) - { - AddMOLfileError(sd->pStrErrStruct, "Options adjusted to STDINCHI mode"); - force_std = 1; - } - AddMOLfileError(sd->pStrErrStruct, "FixedH ignored"); - sd->nErrorType = _IS_WARNING; - */ - retcode = _IS_WARNING; - } - if ( 0 != ( ip->nMode & REQ_MODE_RELATIVE_STEREO) ) - { - ip->nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); - - /* - if ( !force_std ) - { - AddMOLfileError(sd->pStrErrStruct, "Options adjusted to STDINCHI mode"); - force_std = 1; - } - AddMOLfileError(sd->pStrErrStruct, "SREL ignored"); - sd->nErrorType = _IS_WARNING; - */ - retcode = _IS_WARNING; - } - if ( 0 != ( ip->nMode & REQ_MODE_RACEMIC_STEREO) ) - { - ip->nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); - /* - if ( !force_std ) - { - AddMOLfileError(sd->pStrErrStruct, "Options adjusted to STDINCHI mode"); - force_std = 1; - } - AddMOLfileError(sd->pStrErrStruct, "SRAC ignored"); - sd->nErrorType = _IS_WARNING; - */ - retcode = _IS_WARNING; - } - if ( 0 != ( ip->nMode & REQ_MODE_CHIR_FLG_STEREO) ) - { - ip->nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); - /* - if ( !force_std ) - { - AddMOLfileError(sd->pStrErrStruct, "Options adjusted to STDINCHI mode"); - force_std = 1; - } - AddMOLfileError(sd->pStrErrStruct, "SUCF ignored"); - sd->nErrorType = _IS_WARNING; - */ - retcode = _IS_WARNING; - } - if ( 0 != ( ip->nMode & REQ_MODE_DIFF_UU_STEREO) ) - { - ip->nMode &= ~REQ_MODE_DIFF_UU_STEREO; - /* - if ( !force_std ) - { - AddMOLfileError(sd->pStrErrStruct, "Options adjusted to STDINCHI mode"); - force_std = 1; - } - AddMOLfileError(sd->pStrErrStruct, "SLUUD ignored"); - sd->nErrorType = _IS_WARNING; - */ - retcode = _IS_WARNING; - } - if ( 0 == (ip->nMode & (REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU)) ) - { - ip->nMode |= REQ_MODE_SB_IGN_ALL_UU; - ip->nMode |= REQ_MODE_SC_IGN_ALL_UU; - /* - if ( !force_std ) - { - AddMOLfileError(sd->pStrErrStruct, "Options adjusted to STDINCHI mode"); - force_std = 1; - } - AddMOLfileError(sd->pStrErrStruct, "SUU ignored"); - sd->nErrorType = _IS_WARNING; - */ - retcode = _IS_WARNING; - } - if ( 0 != (ip->bTautFlags & TG_FLAG_KETO_ENOL_TAUT) ) - { - ip->bTautFlags &= ~TG_FLAG_KETO_ENOL_TAUT; - /* - if ( !force_std ) - { - AddMOLfileError(sd->pStrErrStruct, "Options adjusted to STDINCHI mode"); - force_std = 1; - } - AddMOLfileError(sd->pStrErrStruct, "KET ignored"); - sd->nErrorType = _IS_WARNING; - */ - retcode = _IS_WARNING; - } - if ( 0 != (ip->bTautFlags & TG_FLAG_1_5_TAUT) ) - { - ip->bTautFlags &= ~TG_FLAG_1_5_TAUT; - /* - if ( !force_std ) - { - AddMOLfileError(sd->pStrErrStruct, "Options adjusted to STDINCHI mode"); - force_std = 1; - } - AddMOLfileError(sd->pStrErrStruct, "15T ignored"); - sd->nErrorType = _IS_WARNING; - */ - retcode = _IS_WARNING; - } - - /* And anyway... */ - ip->bINChIOutputOptions |= INCHI_OUT_STDINCHI; - ip->bINChIOutputOptions &= ~INCHI_OUT_SAVEOPT; - - strcpy(pGenData->pStrErrStruct, sd->pStrErrStruct); - return retcode; -} - - - -int INCHI_DECL INCHIGEN_Setup(INCHIGEN_HANDLE _HGen, - INCHIGEN_DATA * pGenData, - inchi_Input * pInp) - - -{ -int retcode = inchi_Ret_OKAY; - -INCHIGEN_CONTROL *HGen = (INCHIGEN_CONTROL *)_HGen; - -ORIG_ATOM_DATA *orig_inp_data = &(HGen->OrigInpData); -STRUCT_DATA *sd = &(HGen->StructData); -INPUT_PARMS *ip = &(HGen->InpParms); -INCHI_IOSTREAM *log_file = HGen->inchi_file+1; -INCHI_IOSTREAM prbstr, *prb_file=&prbstr; - -const char *argv[INCHI_MAX_NUM_ARG+1]; -int argc; -char *szOptions = NULL; -char szSdfDataValue[MAX_SDF_VALUE+1]; -int bReleaseVersion = bRELEASE_VERSION; -unsigned long ulDisplTime = 0; /* infinite, milliseconds */ -int p; - - - - - - /*^^^ Make allocs/inits */ - - if (!pGenData) - { - retcode = _IS_ERROR; - goto ret; - } - memset(pGenData, 0, sizeof(*pGenData)); - - - - - - /*^^^ Parse 'command-line' options and fill internal INPUT_PARMS structure */ - - if ( pInp && pInp->szOptions ) - { - szOptions = (char*)inchi_malloc( strlen(pInp->szOptions) + 1 ); - if (!szOptions) - return _IS_FATAL; /*^^^ Not enough memory.... */ - else - { - /*^^^ Parse. */ - strcpy( szOptions, pInp->szOptions ); - argc = parse_options_string ( szOptions, argv, INCHI_MAX_NUM_ARG ); - } - } - else - { - /*^^^ Got NULL options string or NULL 'pInp', will use defaults. */ - argc = 1; - argv[0] = ""; - argv[1] = NULL; - } - - - if ( argc == 1 -#ifdef TARGET_API_LIB - && (!pInp || pInp->num_atoms <= 0 || !pInp->atom) -#endif - || argc==2 && ( argv[1][0]==INCHI_OPTION_PREFX ) && - (!strcmp(argv[1]+1, "?") || !stricmp(argv[1]+1, "help") ) ) - { - - HelpCommandLineParms(log_file); - memset( log_file, 0, sizeof(*log_file) ); - return _IS_EOF; - } - - - memset( szSdfDataValue , 0, sizeof( szSdfDataValue ) ); - - - /*^^^ Decrypt. */ - /*^^^ NB: ReadCommandLineParms resides in _header_ file, ichiparm.h */ - - retcode = ReadCommandLineParms( argc, argv, ip, szSdfDataValue, &ulDisplTime, bReleaseVersion, log_file ); - - if (szOptions) - inchi_free( szOptions ); - - /* INChI DLL specific */ - ip->bNoStructLabels = 1; - - if ( 0 > retcode) goto ret; - - if ( ip->bNoStructLabels ) - { - ip->pSdfLabel = NULL; - ip->pSdfValue = NULL; - } - else - if ( ip->nInputType == INPUT_INCHI_XML || ip->nInputType == INPUT_INCHI_PLAIN || ip->nInputType == INPUT_CMLFILE ) - { - /* the input may contain both the header and the label of the structure */ - if ( !ip->pSdfLabel ) - ip->pSdfLabel = ip->szSdfDataHeader; - if ( !ip->pSdfValue ) - ip->pSdfValue = szSdfDataValue; - } - - - if (retcode!=inchi_Ret_OKAY) goto ret; - - PrintInputParms( log_file, ip); - - - - /*^^^ Extract the structure */ - - retcode = ExtractOneStructure( sd, ip, - HGen->szTitle, pInp, - log_file, - HGen->inchi_file, /* output_file */ - prb_file, - orig_inp_data, - &(HGen->num_inp), - HGen->pStr, - PSTR_BUFFER_SIZE ); - - -ret:switch (retcode) - { - case _IS_OKAY : retcode = inchi_Ret_OKAY ; HGen->init_passed = 1; break; /* Success; break; no errors or warnings */ - - case _IS_ERROR : (HGen->num_err)++; retcode = inchi_Ret_ERROR ; break; - /* Error: no INChI has been created */ - case _IS_FATAL : (HGen->num_err)++; retcode = inchi_Ret_FATAL ; break; - /* Severe error: no INChI has been created - (typically; break; memory allocation failed) */ - case _IS_SKIP : retcode = inchi_Ret_SKIP ; break; /* not used in INChI dll */ - case _IS_EOF : retcode = inchi_Ret_EOF ; break; /* no structural data has been provided */ - case _IS_WARNING: retcode = inchi_Ret_WARNING; HGen->init_passed = 1; break; /* Success; break; warning(s) issued */ - case _IS_UNKNOWN: - default : retcode = inchi_Ret_UNKNOWN; break; /* Unlnown program error */ - } - - if (!pGenData) - { - strcpy(pGenData->pStrErrStruct, sd->pStrErrStruct); - for (p=0; p < INCHI_NUM; p++) - pGenData->num_components[p] = sd->num_components[p]; - } - - return retcode; -} - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Get normalized form of the structure. - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -int INCHI_DECL STDINCHIGEN_DoNormalization(INCHIGEN_HANDLE HGen,INCHIGEN_DATA * pGenData) -{ - return INCHIGEN_DoNormalization( HGen, pGenData); -} - -int INCHI_DECL INCHIGEN_DoNormalization(INCHIGEN_HANDLE _HGen, INCHIGEN_DATA *pGenData) -{ -int nRet=0, nRet1=0; -/* int maxINChI=0; */ - - -INCHIGEN_CONTROL * HGen = (INCHIGEN_CONTROL *)_HGen; -INPUT_PARMS *ip = &(HGen->InpParms); -STRUCT_DATA *sd = &(HGen->StructData); -NORM_CANON_FLAGS *pncFlags = &(HGen->ncFlags); -INCHI_IOSTREAM *output_file = HGen->inchi_file; -INCHI_IOSTREAM inpstr, *inp_file = &inpstr; -ORIG_ATOM_DATA *orig_inp_data = &(HGen->OrigInpData); -ORIG_STRUCT *pOrigStruct = NULL; - -int k; - -#if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) -int ret1=0, ret2=0; -#endif - - - - -/*^^^ Set debug output */ -#if (TRACE_MEMORY_LEAKS == 1) - - _CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF); -/* for execution outside the VC++ debugger uncomment one of the following two */ -#ifdef MY_REPORT_FILE - _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_WARN, MY_REPORT_FILE ); - _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_ERROR, MY_REPORT_FILE ); - _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_ASSERT, MY_REPORT_FILE ); -#else - _CrtSetReportMode(_CRT_WARN | _CRT_ERROR, _CRTDBG_MODE_DEBUG); -#endif -#if ( !defined(__STDC__) || __STDC__ != 1 ) - /* turn on floating point exceptions */ - { - /* Get the default control word. */ - int cw = _controlfp( 0,0 ); - /* Set the exception masks OFF, turn exceptions on. */ - /*cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_INEXACT|EM_ZERODIVIDE|EM_DENORMAL);*/ - cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE|EM_DENORMAL); - /* Set the control word. */ - _controlfp( cw, MCW_EM ); - } -#endif /*^^^ ( !defined(__STDC__) || __STDC__ != 1 ) */ - -#endif /*^^^ (TRACE_MEMORY_LEAKS == 1) */ - - - - if (HGen->init_passed==0) - { - AddMOLfileError(sd->pStrErrStruct, "InChI generator not initialized"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_ERROR; - nRet = _IS_ERROR; - goto exit_function; - } - - inchi_ios_init(inp_file, INCHI_IOSTREAM_FILE, NULL); - - sd->bUserQuitComponent = 0; - sd->bUserQuitComponentDisplay = 0; - memset( HGen->composite_norm_data, 0, sizeof(HGen->composite_norm_data) ); - memset( pncFlags, 0, sizeof(*pncFlags) ); - - /* for testing only */ -#if( REMOVE_ION_PAIRS_ORIG_STRU == 1 ) - fix_odd_things( orig_inp_data->num_inp_atoms, orig_inp_data->at, 0 ); -#endif -#if( UNDERIVATIZE == 1 ) /***** post v.1 feature *****/ - if ( ip->bUnderivatize && 0 > (ret2=underivatize( orig_inp_data )) ) - { - long num_inp2 = HGen->num_inp; - AddMOLfileError(sd->pStrErrStruct, "Underivatization error"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_ERROR; - nRet = _IS_ERROR; - TreatReadTheStructureErrors( sd, ip, LOG_MASK_ALL, inp_file, log_file, output_file, prb_file, - prep_inp_data, &num_inp2, HGen->pStr, PSTR_BUFFER_SIZE); - goto exit_function; /* output only if derivatives found */ - } -#endif /* UNDERIVATIZE == 1 */ -#if( RING2CHAIN == 1 ) /***** post v.1 feature *****/ - if ( ip->bRing2Chain && 0 > (ret1 = Ring2Chain( orig_inp_data )) ) - { - long num_inp2 = HGen->num_inp; - AddMOLfileError(sd->pStrErrStruct, "Ring to chain error"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_ERROR; - nRet = _IS_ERROR; - TreatReadTheStructureErrors( sd, ip, LOG_MASK_ALL, inp_file, log_file, output_file, prb_file, - prep_inp_data, &num_inp2, HGen->pStr, PSTR_BUFFER_SIZE); - goto exit_function; /* output only if derivatives found */ - } -#endif /* RING2CHAIN == 1 */ -#if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) /***** post v.1 feature *****/ - if ( ip->bIngnoreUnchanged && !ret1 && !ret2 ) - { - goto exit_function; /* output only if derivatives or ring/chain found */ - } -#endif /* RING2CHAIN == 1 || UNDERIVATIZE == 1 */ - - - /***** output MOLfile ***************/ - if ( ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY ) - { - char szNumber[32]; - int ret1a=0, ret2a=0; /* for derivatives and ring-chain */ - ret1a = sprintf(szNumber, "Structure #%ld", HGen->num_inp); - ret2a = WriteOrigAtomDataToSDfile( orig_inp_data, output_file, szNumber, NULL, - (sd->bChiralFlag & FLAG_INP_AT_CHIRAL)? 1:0, - (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ATOMS_DT)? 1:0, ip->pSdfLabel, ip->pSdfValue ); - goto exit_function; - } - - /******* create full reversibility information **************/ - if ( !(ip->bINChIOutputOptions & (INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO)) ) - { - pOrigStruct = &(HGen->OrigStruct); - memset( pOrigStruct, 0, sizeof(*pOrigStruct)); - if ( FillOutOrigStruct( orig_inp_data, pOrigStruct, sd ) ) - { - AddMOLfileError(sd->pStrErrStruct, "Cannot interpret reversibility information"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_ERROR; - nRet = _IS_ERROR; - } - } - - - sd->bUserQuit = 0; - if (sd->bUserQuit) goto exit_function; - - - - /*^^^ Normalize the whole disconnected or original structure */ - if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) { - nRet1 = NormOneStructureINChI( pGenData, HGen, INCHI_BAS, inp_file); - nRet = inchi_max(nRet, nRet1); - } - -/* - if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) - maxINChI = 1; -*/ - - if ( nRet != _IS_FATAL && nRet != _IS_ERROR && - (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) && - (ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) - { - /* Normalize the whole reconnected structure */ - nRet1 = NormOneStructureINChI( pGenData, HGen, INCHI_REC, inp_file); - nRet = inchi_max(nRet, nRet1); -/* - if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) - maxINChI = 2; -*/ - } - - - - -exit_function: - - - if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) - HGen->norm_passed = 1; - - - for (k=0; k < INCHI_NUM; k++) - pGenData->num_components[k] = sd->num_components[k]; - - - /*^^^ issue normalization warnings */ - if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) - { - int ic, istruct, itaut, nc[2]; - int warn_prot=0, warn_neutr=0; - INP_ATOM_DATA *inp_norm_data[TAUT_NUM]; /* = { &InpNormAtData, &InpNormTautData }; */ - nc[0] = pGenData->num_components[0]; - nc[1] = pGenData->num_components[1]; - for (istruct=0; istruct<2; istruct++) - { - if (nc[istruct]>0) - { - for (ic=0; ic < nc[istruct]; ic++) - { - inp_norm_data[0] = &(HGen->InpNormAtData[istruct][ic]); - inp_norm_data[1] = &(HGen->InpNormTautData[istruct][ic]); - for (itaut=0;itaut<2;itaut++) - { - if (NULL!=inp_norm_data[itaut]) - { - if ( inp_norm_data[itaut]->bTautomeric ) - { - if (inp_norm_data[itaut]->bNormalizationFlags & (FLAG_NORM_CONSIDER_TAUT &~FLAG_PROTON_CHARGE_CANCEL) ) - if (warn_prot==0) - { - warn_prot++; - AddMOLfileError(sd->pStrErrStruct, "Proton(s) added/removed"); - } - if (inp_norm_data[itaut]->bNormalizationFlags & FLAG_PROTON_CHARGE_CANCEL ) - if (warn_neutr==0) - { - warn_neutr++; - AddMOLfileError(sd->pStrErrStruct, "Charges neutralized"); - } - - } - } - } /* itaut */ - - } - } - } - - } - strcpy(pGenData->pStrErrStruct, sd->pStrErrStruct); - make_norm_atoms_from_inp_atoms(pGenData, HGen); - - return nRet; -} - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Get canonicalized form of the structure. - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -int INCHI_DECL STDINCHIGEN_DoCanonicalization - (INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData) -{ - return INCHIGEN_DoCanonicalization(HGen, pGenData ) ; -} - -int INCHI_DECL INCHIGEN_DoCanonicalization - (INCHIGEN_HANDLE _HGen, INCHIGEN_DATA *pGenData ) -{ -int nRet = 0, nRet1 /*, maxINChI=0*/; -INCHIGEN_CONTROL * HGen = (INCHIGEN_CONTROL *)_HGen; - - -STRUCT_DATA *sd = &(HGen->StructData); -INPUT_PARMS *ip = &(HGen->InpParms); -INCHI_IOSTREAM *output_file = HGen->inchi_file, *log_file = HGen->inchi_file+1; -INCHI_IOSTREAM prbstr, *prb_file=&prbstr; -INCHI_IOSTREAM inpstr, *inp_file = &inpstr; - -ORIG_ATOM_DATA *prep_inp_data = &(HGen->PrepInpData[0]); -int k; - - -/*^^^ Set debug output */ -#if (TRACE_MEMORY_LEAKS == 1) - - _CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF); - /* for execution outside the VC++ debugger uncomment one of the following two */ -#ifdef MY_REPORT_FILE - _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_WARN, MY_REPORT_FILE ); - _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_ERROR, MY_REPORT_FILE ); - _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_ASSERT, MY_REPORT_FILE ); -#else - _CrtSetReportMode(_CRT_WARN | _CRT_ERROR, _CRTDBG_MODE_DEBUG); -#endif -#if ( !defined(__STDC__) || __STDC__ != 1 ) - /* turn on floating point exceptions */ - { - /* Get the default control word. */ - int cw = _controlfp( 0,0 ); - /* Set the exception masks OFF, turn exceptions on. */ - /*cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_INEXACT|EM_ZERODIVIDE|EM_DENORMAL);*/ - cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE|EM_DENORMAL); - /* Set the control word. */ - _controlfp( cw, MCW_EM ); - } -#endif /*^^^ ( !defined(__STDC__) || __STDC__ != 1 ) */ - -#endif /*^^^ (TRACE_MEMORY_LEAKS == 1) */ - - - - if (HGen->norm_passed==0) - { - AddMOLfileError(sd->pStrErrStruct, "Got non-normalized structure"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_ERROR; - nRet = _IS_ERROR; - goto exit_function; - } - - - inchi_ios_init(inp_file, INCHI_IOSTREAM_FILE, NULL); - inchi_ios_init(prb_file, INCHI_IOSTREAM_FILE, NULL); - - sd->bUserQuit = 0; - if (sd->bUserQuit) goto exit_function; - - /* create INChI for each connected component of the structure and optionally display them */ - /* output INChI for the whole structure */ - - - - /* create INChI for each connected component of the structure and optionally display them */ - /* create INChI for the whole disconnected or original structure */ - if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) { - nRet1 = CanonOneStructureINChI(HGen, INCHI_BAS, inp_file); - nRet = inchi_max(nRet, nRet1); - } - -/* - if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) - maxINChI = 1; -*/ - - if ( nRet != _IS_FATAL && nRet != _IS_ERROR && - (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) && - (ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) - { - /* create INChI for the whole reconnected structure */ - nRet1 = CanonOneStructureINChI(HGen, INCHI_REC, inp_file); - nRet = inchi_max(nRet, nRet1); -/* - if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) - maxINChI = 2; -*/ - } - - if (nRet != _IS_FATAL && nRet != _IS_ERROR) - { - if ( (sd->bChiralFlag & FLAG_INP_AT_CHIRAL) && - (ip->nMode & REQ_MODE_STEREO) && - !(ip->nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO)) && - !bIsStructChiral( HGen->pINChI, sd->num_components ) ) - { - AddMOLfileError(sd->pStrErrStruct, "Not chiral"); - } - - /*************************************/ - /* Output err/warn messages */ - /*************************************/ - if ( /*!sd->nErrorCode &&*/ !sd->bUserQuitComponent && !sd->bUserQuit ) - { - /* if successful then returns 0, otherwise returns _IS_FATAL */ - /* extract the structure if requested */ - nRet1 = TreatCreateINChIWarning(sd, ip, prep_inp_data, HGen->num_inp, - inp_file, log_file, output_file, prb_file, HGen->pStr, PSTR_BUFFER_SIZE); - nRet = inchi_max(nRet, nRet1); - } - } - - - - - switch (nRet) - { - case _IS_SKIP : nRet = inchi_Ret_SKIP ; break; /* not used in INChI dll */ - case _IS_EOF : nRet = inchi_Ret_EOF ; break; /* no structural data has been provided */ - case _IS_OKAY : nRet = inchi_Ret_OKAY ; HGen->canon_passed = 1; break; - /* Success; break; no errors or warnings */ - case _IS_WARNING: nRet = inchi_Ret_WARNING; HGen->canon_passed = 1; break; - /* Success; break; warning(s) issued */ - case _IS_ERROR : nRet = inchi_Ret_ERROR ; break; /* Error: no INChI has been created */ - case _IS_FATAL : nRet = inchi_Ret_FATAL ; break; /* Severe error: no INChI has been created (typically; break; memory allocation failed) */ - case _IS_UNKNOWN: - default : nRet = inchi_Ret_UNKNOWN; break; /* Unknown program error */ - } -exit_function: - - strcpy(pGenData->pStrErrStruct, sd->pStrErrStruct); - for (k=0; k < INCHI_NUM; k++) - pGenData->num_components[k] = sd->num_components[k]; - - - return nRet; - -} /*^^^ INCHIGEN_DoCanonicalization */ - - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get serialized form (InChI string). -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -int INCHI_DECL STDINCHIGEN_DoSerialization(INCHIGEN_HANDLE HGen, - INCHIGEN_DATA * pGenData, - inchi_Output * pResults) -{ - return INCHIGEN_DoSerialization(HGen, pGenData, pResults); -} - - -int INCHI_DECL INCHIGEN_DoSerialization(INCHIGEN_HANDLE _HGen, - INCHIGEN_DATA * pGenData, - inchi_Output * pResults) -{ -int nRet=0, nRet1=0, i, k; - - - -INCHIGEN_CONTROL * HGen = (INCHIGEN_CONTROL *)_HGen; - -INPUT_PARMS *ip = &(HGen->InpParms); -INCHI_IOSTREAM *output_file = HGen->inchi_file, *log_file = HGen->inchi_file+1; -INCHI_IOSTREAM inpstr, *inp_file = &inpstr; -INCHI_IOSTREAM prbstr, *prb_file=&prbstr; - -STRUCT_DATA *sd = &(HGen->StructData); -NORM_CANON_FLAGS *pncFlags = &(HGen->ncFlags); -ORIG_ATOM_DATA *orig_inp_data = &(HGen->OrigInpData); -ORIG_ATOM_DATA *prep_inp_data = &(HGen->PrepInpData[0]); -ORIG_STRUCT *pOrigStruct = &(HGen->OrigStruct); -int bSortPrintINChIFlags=0; -unsigned char save_opt_bits=0; -int retcode = 0; - - - - /*^^^ Post-1.02b - added initialization of pResults to 0; thanks to David Foss */ - memset(pResults, 0, sizeof(*pResults)); - pResults->szLog = log_file->s.pStr; - inchi_ios_init(inp_file, INCHI_IOSTREAM_FILE, NULL); - inchi_ios_init(prb_file, INCHI_IOSTREAM_FILE, NULL); - - -/*^^^ Set debug output */ - -#if (TRACE_MEMORY_LEAKS == 1) - - _CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF); - -/* for execution outside the VC++ debugger uncomment one of the following two */ -#ifdef MY_REPORT_FILE - _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_WARN, MY_REPORT_FILE ); - _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_ERROR, MY_REPORT_FILE ); - _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_ASSERT, MY_REPORT_FILE ); -#else - _CrtSetReportMode(_CRT_WARN | _CRT_ERROR, _CRTDBG_MODE_DEBUG); -#endif -#if ( !defined(__STDC__) || __STDC__ != 1 ) - /* turn on floating point exceptions */ - { - /* Get the default control word. */ - int cw = _controlfp( 0,0 ); - /* Set the exception masks OFF, turn exceptions on. */ - /*cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_INEXACT|EM_ZERODIVIDE|EM_DENORMAL);*/ - cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE|EM_DENORMAL); - /* Set the control word. */ - _controlfp( cw, MCW_EM ); - } -#endif /*^^^ ( !defined(__STDC__) || __STDC__ != 1 ) */ - -#endif /*^^^ (TRACE_MEMORY_LEAKS == 1) */ - - -/*****************************/ - - - if (HGen->canon_passed==0) - { - AddMOLfileError(sd->pStrErrStruct, "Got non-canonicalized structure"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_ERROR; - retcode = _IS_ERROR; - goto frees; - } - - - /************************************************/ - /* sort and print INChI for the whole structure */ - /************************************************/ - - /* Prepare SaveOpt bits */ - if ( ip->bINChIOutputOptions & INCHI_OUT_SAVEOPT ) - { - if ( 0 != ( ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) - save_opt_bits |= SAVE_OPT_RECMET; - if ( 0 != ( ip->nMode & REQ_MODE_BASIC) ) - save_opt_bits |= SAVE_OPT_FIXEDH; - if ( 0 != ( ip->nMode & REQ_MODE_DIFF_UU_STEREO) ) - save_opt_bits |= SAVE_OPT_SLUUD; - if ( 0 == (ip->nMode & (REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU)) ) - save_opt_bits |= SAVE_OPT_SUU; - if ( 0 != (ip->bTautFlags & TG_FLAG_KETO_ENOL_TAUT) ) - save_opt_bits |= SAVE_OPT_KET; - if ( 0 != (ip->bTautFlags & TG_FLAG_1_5_TAUT) ) - save_opt_bits |= SAVE_OPT_15T; - } - - - nRet = SortAndPrintINChI(output_file, HGen->pStr, PSTR_BUFFER_SIZE, log_file, - ip, orig_inp_data, prep_inp_data, - HGen->composite_norm_data, pOrigStruct, - sd->num_components, sd->num_non_taut, sd->num_taut, - sd->bTautFlags, sd->bTautFlagsDone, pncFlags, HGen->num_inp, - HGen->pINChI, HGen->pINChI_Aux, - &bSortPrintINChIFlags, save_opt_bits); - - - - /* XML struct end tag */ - if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) && sd->bXmlStructStarted > 0 ) - { - if ( !OutputINChIXmlStructEndTag( output_file, HGen->pStr, PSTR_BUFFER_SIZE, 1 ) ) - { - inchi_ios_eprint( log_file, "Cannot create end xml tag for structure #%ld.%s%s%s%s Terminating.\n", - HGen->num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - sd->bXmlStructStarted = -1; /* do not repeat same message */ - nRet = _IS_FATAL; - } - else - { - sd->bXmlStructStarted = 0; /* do not continue xml output for this structure */ - } - } - if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) - { - /* Special mode: extract all good MOLfiles into the problem file - * Do not extract any MOLfile that could not be processed (option /PGO) - */ - if ( prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd && ip->bSaveAllGoodStructsAsProblem ) { - CopyMOLfile(inp_file->f, sd->fPtrStart, sd->fPtrEnd, prb_file->f, 0); - } -#if( /*bRELEASE_VERSION != 1 &&*/ EXTR_FLAGS == EXTR_TRANSPOSITION_EXAMPLES && EXTR_MASK == EXTR_FLAGS ) - else - if ( prb_file->f && (bSortPrintINChIFlags & - ( FLAG_SORT_PRINT_TRANSPOS_BAS | FLAG_SORT_PRINT_TRANSPOS_REC ) ) - ) - { - CopyMOLfile(inp_file, sd->fPtrStart, sd->fPtrEnd, prb_file->f, 0); - } -#endif - } - - for ( i = 0; i < INCHI_NUM; i ++ ) - { - for ( k = 0; k < TAUT_NUM+1; k ++ ) - { - FreeCompAtomData( &(HGen->composite_norm_data[i][k]) ); - } - } -/*****************************/ - - /*^^^ Prepare output message(s). */ - - - /*^^^ Error/warning. */ - if ( sd->pStrErrStruct[0] ) - if ( pGenData && (pResults->szMessage = (char *)inchi_malloc( strlen(sd->pStrErrStruct) + 1 )) ) - strcpy( pResults->szMessage, sd->pStrErrStruct ); - - /*^^^ InChI, AuxInfo (go to pResults->szInChI, pResults->szAuxInfo) */ - if ( output_file->s.pStr && output_file->s.nUsedLength > 0 && pGenData ) - { - char *p; - pResults->szInChI = output_file->s.pStr; - pResults->szAuxInfo = NULL; - if ( !(INCHI_OUT_SDFILE_ONLY & ip->bINChIOutputOptions ) ) /* do not remove last LF from SDF output - 2008-12-23 DT */ - for ( p = strchr(pResults->szInChI, '\n'); p; p = strchr(p+1, '\n') ) - { - if ( !memcmp( p, "\nAuxInfo", 8 ) ) - { - *p = '\0'; /* remove LF after INChI */ - pResults->szAuxInfo = p+1; /* save pointer to AuxInfo */ - } - else - if ( pResults->szAuxInfo || !p[1]) - { - /* remove LF after aux info or from the last char */ - *p = '\0'; - break; - } - } - output_file->s.pStr = NULL; - } - - - /*^^^ Log message. */ - if ( log_file->s.pStr && log_file->s.nUsedLength > 0 ) - { - while ( log_file->s.nUsedLength && '\n' == log_file->s.pStr[log_file->s.nUsedLength-1] ) - log_file->s.pStr[-- log_file->s.nUsedLength] = '\0'; /* remove last LF */ - if ( pGenData ) - { - pResults->szLog = log_file->s.pStr; - log_file->s.pStr = NULL; - } - } - - if ( output_file->s.pStr ) {inchi_free( output_file->s.pStr ); output_file->s.pStr = NULL;} - if ( log_file->s.pStr ) {inchi_free( log_file->s.pStr ); log_file->s.pStr = NULL;} - - - - - - HGen->ulTotalProcessingTime += sd->ulStructTime; - nRet = inchi_max(nRet, nRet1); - - switch ( nRet ) - { - case _IS_FATAL: - case _IS_ERROR: HGen->num_err ++; - } - - - - /*^^^ XML-related. */ - if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) && sd->bXmlStructStarted > 0 ) - { - if ( !OutputINChIXmlStructEndTag( output_file, HGen->pStr, PSTR_BUFFER_SIZE, 1 ) ) - { - inchi_ios_eprint( log_file, "Cannot create end xml tag for structure #%d.%s%s%s%s Terminating.\n", - HGen->num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - sd->bXmlStructStarted = -1; /* do not repeat same message */ - } - } - if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) && ip->bXmlStarted ) - { - OutputINChIXmlRootEndTag( output_file ); - ip->bXmlStarted = 0; - } - - -frees: - /*^^^ Free all. */ - - /* free INChI memory */ -/* FreeAllINChIArrays(HGen->pINChI, HGen->pINChI_Aux, sd->num_components ); -*/ -#if( ADD_CMLPP == 1 ) - /* BILLY 8/6/04 */ - /* free CML memory */ - FreeCml (); - FreeCmlDoc( 1 ); -#endif - for ( i = 0; i < MAX_NUM_PATHS; i ++ ) - { - if ( ip->path[i] ) - { - inchi_free( (char*) ip->path[i] ); /* cast deliberately discards 'const' qualifier */ - ip->path[i] = NULL; - } - } - SetBitFree( ); - - - strcpy(pGenData->pStrErrStruct, sd->pStrErrStruct); - for (k=0; k < INCHI_NUM; k++) - pGenData->num_components[k] = sd->num_components[k]; - - - return retcode; -} - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -InChI Generator: reset stage (use before get next structure) - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void INCHI_DECL STDINCHIGEN_Reset(INCHIGEN_HANDLE HGen, - INCHIGEN_DATA * pGenData, - inchi_Output * pResults) -{ - INCHIGEN_Reset(HGen, pGenData, pResults); -} - - -void INCHI_DECL INCHIGEN_Reset(INCHIGEN_HANDLE _HGen, - INCHIGEN_DATA * pGenData, - inchi_Output * pResults) -{ -int i, k, nc; -INCHIGEN_CONTROL * HGen = (INCHIGEN_CONTROL *)_HGen; - - - if ( pResults->szInChI ) inchi_free( pResults->szInChI ); - if ( pResults->szLog ) inchi_free( pResults->szLog ); - if ( pResults->szMessage ) inchi_free( pResults->szMessage ); - - - /* Free all data associated with components of disconn/conn structures */ - - if (NULL!=HGen) - { - - /*^^^ Re-initialize output streams/string buffers */ - inchi_ios_close(&(HGen->inchi_file[0])); - inchi_ios_close(&(HGen->inchi_file[1])); - inchi_ios_close(&(HGen->inchi_file[2])); - inchi_ios_init(&(HGen->inchi_file[0]), INCHI_IOSTREAM_STRING, NULL); - inchi_ios_init(&(HGen->inchi_file[1]), INCHI_IOSTREAM_STRING, NULL); - inchi_ios_init(&(HGen->inchi_file[2]), INCHI_IOSTREAM_STRING, NULL); - - - if (HGen->pStr ) - memset(HGen->pStr, 0, sizeof( (*HGen->pStr) )); - - - for ( i = 0; i < MAX_NUM_PATHS; i ++ ) - { - if ( HGen->InpParms.path[i] ) - { - inchi_free( (char*) HGen->InpParms.path[i] ); /* cast deliberately discards 'const' qualifier */ - HGen->InpParms.path[i] = NULL; - } - } - memset(&(HGen->InpParms), 0, sizeof(INPUT_PARMS)); - - FreeOrigAtData( &(HGen->OrigInpData) ); - memset(&(HGen->OrigInpData), 0, sizeof( HGen->OrigInpData ) ); - - - FreeOrigAtData( &(HGen->PrepInpData[0])); - FreeOrigAtData( &(HGen->PrepInpData[1])); - memset(&(HGen->PrepInpData[0]), 0, 2*sizeof( HGen->PrepInpData[0] ) ); - - FreeOrigStruct( &(HGen->OrigStruct)); - memset(&(HGen->OrigStruct), 0, sizeof( HGen->OrigStruct ) ); - - - for ( i = 0; i < INCHI_NUM; i ++ ) - for ( k = 0; k < TAUT_NUM+1; k ++ ) - FreeCompAtomData( &(HGen->composite_norm_data[i][k]) ); - - for ( k = 0; k < INCHI_NUM; k++) - { - nc = HGen->StructData.num_components[k]; - - if ( HGen->InpCurAtData[k] ) - { - for ( i = 0; i < nc; i ++ ) - FreeInpAtomData( &(HGen->InpCurAtData[k][i]) ); - inchi_free(HGen->InpCurAtData[k]); - HGen->InpCurAtData[k] = NULL; - } - - - if ( HGen->cti[k] ) - { - if ( (HGen->cti[k])->at[TAUT_YES] ) - { - inchi_free( (HGen->cti[k])->at[TAUT_YES] ); - (HGen->cti[k])->at[TAUT_YES] = NULL; - } - - if ( (HGen->cti[k])->at[TAUT_NON] ) - { - inchi_free( (HGen->cti[k])->at[TAUT_NON] ); - (HGen->cti[k])->at[TAUT_NON] = NULL; - } - - if (&((HGen->cti[k])->vt_group_info)) - free_t_group_info(&((HGen->cti[k])->vt_group_info)); - - if (&((HGen->cti[k])->vt_group_info_orig)) - free_t_group_info(&((HGen->cti[k])->vt_group_info_orig)); - - inchi_free(HGen->cti[k]); - HGen->cti[k] = NULL; - } - - } - - - for ( k = 0; k < INCHI_NUM; k++) - { - nc = HGen->StructData.num_components[k]; - if ( HGen->InpNormAtData[k] ) - { - for ( i = 0; i < nc; i ++ ) - FreeInpAtomData( &(HGen->InpNormAtData[k][i]) ); - inchi_free(HGen->InpNormAtData[k]); - HGen->InpNormAtData[k] = NULL; - } - - if ( HGen->InpNormTautData[k] ) - { - for ( i = 0; i < nc; i ++ ) - FreeInpAtomData( &(HGen->InpNormTautData[k][i]) ); - inchi_free(HGen->InpNormTautData[k]); - HGen->InpNormTautData[k] = NULL; - } - - - if ( pGenData->NormAtomsTaut[k] ) - { - /* - for ( i = 0; i < nc; i ++ ) - FreeInpAtomData( &(pGenData->NormAtomsTaut[k][i]) ); - */ - inchi_free(pGenData->NormAtomsTaut[k]); - pGenData->NormAtomsTaut[k] = NULL; - } - if ( pGenData->NormAtomsNontaut[k] ) - { - /* for ( i = 0; i < nc; i ++ ) - FreeInpAtomData( &(pGenData->NormAtomsNontaut[k][i]) ); - */ - inchi_free(pGenData->NormAtomsNontaut[k]); - pGenData->NormAtomsNontaut[k] = NULL; - } - - - } - - /* free INChI memory */ - FreeAllINChIArrays( HGen->pINChI, HGen->pINChI_Aux, HGen->StructData.num_components ); - memset(HGen->pINChI, 0, sizeof(HGen->pINChI) ); - memset(HGen->pINChI_Aux, 0, sizeof(HGen->pINChI_Aux) ); - - HGen->szTitle[0] = '\0'; - } - - memset(&(HGen->StructData), 0, sizeof(STRUCT_DATA) ); - - memset( pResults, 0, sizeof(*pResults) ); - memset( pGenData , 0, sizeof(*pGenData) ); - - return; -} - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -InChI Generator: destroy generator -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void INCHI_DECL STDINCHIGEN_Destroy(INCHIGEN_HANDLE HGen) -{ - INCHIGEN_Destroy(HGen) ; -} - - -void INCHI_DECL INCHIGEN_Destroy(INCHIGEN_HANDLE _HGen) -{ -INCHIGEN_CONTROL * HGen = (INCHIGEN_CONTROL *)_HGen; - - if (NULL!=HGen) - { - if ( HGen->pStr ) - inchi_free(HGen->pStr); - - inchi_ios_close(&(HGen->inchi_file[0])); - inchi_ios_close(&(HGen->inchi_file[1])); - inchi_ios_close(&(HGen->inchi_file[2])); - - inchi_free(HGen); - } -} - - - - - -/********************************************************************/ - -#if( defined( _WIN32 ) && defined( _MSC_VER ) && _MSC_VER >= 800 && defined(_USRDLL) && defined(BUILD_LINK_AS_DLL) ) - /* Win32 & MS VC ++, compile and link as a DLL */ -/*********************************************************/ -/* C calling conventions export from Win32 dll */ -/*********************************************************/ -/* prototypes */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif -INCHIGEN_HANDLE cdecl_INCHIGEN_Create(void); -INCHIGEN_HANDLE cdecl_STDINCHIGEN_Create(void); -int cdecl_INCHIGEN_Setup( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp); -int cdecl_STDINCHIGEN_Setup( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp); -int cdecl_INCHIGEN_DoNormalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ); -int cdecl_STDINCHIGEN_DoNormalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ); -int cdecl_INCHIGEN_DoCanonicalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ); -int cdecl_STDINCHIGEN_DoCanonicalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ); -int cdecl_INCHIGEN_DoSerialization(INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults ); -int cdecl_STDINCHIGEN_DoSerialization(INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults ); -void cdecl_INCHIGEN_Reset( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults); -void cdecl_STDINCHIGEN_Reset( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults); -void cdecl_INCHIGEN_Destroy( INCHIGEN_HANDLE HGen ); -void cdecl_STDINCHIGEN_Destroy( INCHIGEN_HANDLE HGen ); -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -/* implementation */ -/* libinchi.def provides export withou cdecl_ prefixes */ - -/********************************************************/ -INCHIGEN_HANDLE cdecl_INCHIGEN_Create(void) -{ - return INCHIGEN_Create( ); -} -/********************************************************/ -INCHIGEN_HANDLE cdecl_STDINCHIGEN_Create(void) -{ - return STDINCHIGEN_Create( ); -} -/********************************************************/ -int cdecl_INCHIGEN_Setup( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp) -{ - return INCHIGEN_Setup( HGen, pGenData, pInp ); -} -/********************************************************/ -int cdecl_STDINCHIGEN_Setup( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp) -{ - return STDINCHIGEN_Setup( HGen, pGenData, pInp ); -} -/********************************************************/ -int cdecl_INCHIGEN_DoNormalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ) -{ - return INCHIGEN_DoNormalization( HGen, pGenData ); -} -/********************************************************/ -int cdecl_STDINCHIGEN_DoNormalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ) -{ - return STDINCHIGEN_DoNormalization( HGen, pGenData ); -} -/********************************************************/ -int cdecl_INCHIGEN_DoCanonicalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ) -{ - return INCHIGEN_DoCanonicalization( HGen, pGenData ); -}/********************************************************/ -int cdecl_STDINCHIGEN_DoCanonicalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ) -{ - return STDINCHIGEN_DoCanonicalization( HGen, pGenData ); -} -/********************************************************/ -int cdecl_INCHIGEN_DoSerialization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults ) -{ - return INCHIGEN_DoSerialization( HGen, pGenData, pResults ); -} -/********************************************************/ -int cdecl_STDINCHIGEN_DoSerialization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults ) -{ - return STDINCHIGEN_DoSerialization( HGen, pGenData, pResults ); -} -/********************************************************/ -void cdecl_INCHIGEN_Reset( INCHIGEN_HANDLE HGen , INCHIGEN_DATA *pGenData, inchi_Output *pResults) -{ - INCHIGEN_Reset( HGen, pGenData, pResults ); -} -/********************************************************/ -void cdecl_STDINCHIGEN_Reset( INCHIGEN_HANDLE HGen , INCHIGEN_DATA *pGenData, inchi_Output *pResults) -{ - STDINCHIGEN_Reset( HGen, pGenData, pResults ); -} -/********************************************************/ -void cdecl_INCHIGEN_Destroy( INCHIGEN_HANDLE HGen ) -{ - INCHIGEN_Destroy( HGen ); -} -/********************************************************/ -void cdecl_STDINCHIGEN_Destroy( INCHIGEN_HANDLE HGen ) -{ - STDINCHIGEN_Destroy( HGen ); -} -#endif - -#if( defined(__GNUC__) && __GNUC__ >= 3 && defined(__MINGW32__) && defined(_WIN32) ) -#include -/*********************************************************/ -/* Pacal calling conventions export from Win32 dll */ -/*********************************************************/ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif -/* prototypes */ -/********************************************************/ -INCHIGEN_HANDLE PASCAL pasc_INCHIGEN_Create(void); -INCHIGEN_HANDLE PASCAL pasc_STDINCHIGEN_Create(void); -int PASCAL pasc_INCHIGEN_Setup( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp ); -int PASCAL pasc_STDINCHIGEN_Setup( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp ); -int PASCAL pasc_INCHIGEN_DoNormalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ); -int PASCAL pasc_STDINCHIGEN_DoNormalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ); -int PASCAL pasc_INCHIGEN_DoCanonicalization( INCHIGEN_HANDLE _HGen, INCHIGEN_DATA *pGenData ); -int PASCAL pasc_STDINCHIGEN_DoCanonicalization( INCHIGEN_HANDLE _HGen, INCHIGEN_DATA *pGenData ); -int PASCAL pasc_INCHIGEN_DoSerialization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults ); -int PASCAL pasc_STDINCHIGEN_DoSerialization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults ); -void PASCAL pasc_INCHIGEN_Reset( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData, inchi_Output *pResults ); -void PASCAL pasc_STDINCHIGEN_Reset( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData, inchi_Output *pResults ); -void PASCAL pasc_INCHIGEN_Destroy(INCHIGEN_HANDLE HGen); -void PASCAL pasc_STDINCHIGEN_Destroy(INCHIGEN_HANDLE HGen); -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -/* implementation */ -/* libinchi.def provides export without PASCAL pasc_ prefixes */ -/********************************************************/ -INCHIGEN_HANDLE PASCAL pasc_INCHIGEN_Create(void) -{ - return INCHIGEN_Create( ); -} -/********************************************************/ -INCHIGEN_HANDLE PASCAL pasc_STDINCHIGEN_Create(void) -{ - return STDINCHIGEN_Create( ); -} -/********************************************************/ -int PASCAL pasc_INCHIGEN_Setup( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp) -{ - return INCHIGEN_Setup( HGen, pGenData, pInp); -} -/********************************************************/ -int PASCAL pasc_STDINCHIGEN_Setup( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp) -{ - return STDINCHIGEN_Setup( HGen, pGenData, pInp); -} -/********************************************************/ -int PASCAL pasc_INCHIGEN_DoNormalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ) -{ - return INCHIGEN_DoNormalization( HGen, pGenData ); -}/********************************************************/ -int PASCAL pasc_STDINCHIGEN_DoNormalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ) -{ - return STDINCHIGEN_DoNormalization( HGen, pGenData ); -} -/********************************************************/ -int PASCAL pasc_INCHIGEN_DoCanonicalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ) -{ - return INCHIGEN_DoCanonicalization( HGen, pGenData ); -}/********************************************************/ -int PASCAL pasc_STDINCHIGEN_DoCanonicalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ) -{ - return STDINCHIGEN_DoCanonicalization( HGen, pGenData ); -} -/********************************************************/ -int PASCAL pasc_INCHIGEN_DoSerialization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults ) -{ - return INCHIGEN_DoSerialization( HGen, pGenData, pResults ); -}/********************************************************/ -int PASCAL pasc_STDINCHIGEN_DoSerialization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults ) -{ - return STDINCHIGEN_DoSerialization( HGen, pGenData, pResults ); -} -/********************************************************/ -void PASCAL pasc_INCHIGEN_Reset( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData, inchi_Output *pResults) -{ - INCHIGEN_Reset( HGen, pGenData, pResults ); -} -/********************************************************/ -void PASCAL pasc_STDINCHIGEN_Reset( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData, inchi_Output *pResults) -{ - STDINCHIGEN_Reset( HGen, pGenData, pResults ); -} -/********************************************************/ -void PASCAL pasc_INCHIGEN_Destroy(INCHIGEN_HANDLE HGen) -{ - INCHIGEN_Destroy( HGen); -} -/********************************************************/ -void PASCAL pasc_STDINCHIGEN_Destroy(INCHIGEN_HANDLE HGen) -{ - STDINCHIGEN_Destroy( HGen); -} -#endif - - - - - - - - - +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +/* + InChI - 'modularized' API ( since v. 1.02 ) +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../../INCHI_BASE/src/incomdef.h" +#include "../../../INCHI_BASE/src/ichidrp.h" +#include "../../../INCHI_BASE/src/inpdef.h" +#include "../../../INCHI_BASE/src/ichi.h" +#include "../../../INCHI_BASE/src/strutil.h" +#include "../../../INCHI_BASE/src/util.h" +#include "../../../INCHI_BASE/src/ichierr.h" +#include "../../../INCHI_BASE/src/ichimain.h" +#include "../../../INCHI_BASE/src/extr_ct.h" +#include "../../../INCHI_BASE/src/ichi_io.h" +#include "../../../INCHI_BASE/src/mol_fmt.h" +#include "../../../INCHI_BASE/src/ichicomp.h" +#include "../../../INCHI_BASE/src/ichitaut.h" +#include "../../../INCHI_BASE/src/ichinorm.h" + + +#include "../../../INCHI_BASE/src/ichisize.h" +#include "../../../INCHI_BASE/src/ichitime.h" +#include "../../../INCHI_BASE/src/mode.h" +#include "../../../INCHI_BASE/src/inchi_api.h" + +#include "inchi_dll_a.h" /* not inchi_api.h as it hides internal data types */ +#include "inchi_dll.h" + + + +/* Forward declaration */ +struct tagINCHI_CLOCK; + +/* + Local prototypes. +*/ + +int parse_options_string ( char *cmd, const char *argv[], int maxargs ); +int ExtractOneStructure( STRUCT_DATA *sd, + INPUT_PARMS *ip, + char *szTitle, + inchi_InputEx *inp, + INCHI_IOSTREAM *log_file, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM *prb_file, + ORIG_ATOM_DATA *orig_inp_data, + long *num_inp ); +int NormOneStructureINChI( CANON_GLOBALS *pCG, + INCHI_CLOCK *ic, + INCHIGEN_DATA *pGenData, + INCHIGEN_CONTROL * HGen, + int iINChI, + INCHI_IOSTREAM *inp_file); +int CanonOneStructureINChI( CANON_GLOBALS *pCG, + struct tagINCHI_CLOCK *ic, + INCHIGEN_CONTROL *HGen, + int iINChI, + INCHI_IOSTREAM *inp_file); + + +/* InChI Generator: create generator + Returns handle of generator object or NULL on failure +*/ +INCHIGEN_HANDLE INCHI_DECL STDINCHIGEN_Create(void) +{ + return INCHIGEN_Create(); +} + + +INCHIGEN_HANDLE INCHI_DECL INCHIGEN_Create(void) +{ +INCHIGEN_CONTROL * HGen = NULL; + + + HGen = (INCHIGEN_CONTROL *) inchi_malloc( sizeof(INCHIGEN_CONTROL) ); + + if (!HGen) + return (INCHIGEN_HANDLE) NULL; + + memset(HGen, 0, sizeof(INCHIGEN_CONTROL)); + + /* Set/init aliases */ + memset(&(HGen->InpParms), 0, sizeof(INPUT_PARMS)); + memset(&(HGen->StructData), 0, sizeof(STRUCT_DATA) ); + + HGen->ulTotalProcessingTime = 0; + HGen->num_err = 0; + HGen->num_inp = 0; + HGen->szTitle[0] = '\0'; + + + /* Initialize output streams as string buffers */ + inchi_ios_init(&(HGen->inchi_file[0]), INCHI_IOSTREAM_TYPE_STRING, NULL); + inchi_ios_init(&(HGen->inchi_file[1]), INCHI_IOSTREAM_TYPE_STRING, NULL); + inchi_ios_init(&(HGen->inchi_file[2]), INCHI_IOSTREAM_TYPE_STRING, NULL); + + + memset(&(HGen->OrigInpData), 0, sizeof( HGen->OrigInpData ) ); + memset(&(HGen->PrepInpData[0]), 0, 2*sizeof( HGen->PrepInpData[0] ) ); + + memset(HGen->pINChI, 0, sizeof(HGen->pINChI) ); + memset(HGen->pINChI_Aux, 0, sizeof(HGen->pINChI_Aux) ); + + /* Supply expandable string buffer */ + if ( 0>=inchi_strbuf_init( &(HGen->strbuf_container), INCHI_STRBUF_INITIAL_SIZE, INCHI_STRBUF_SIZE_INCREMENT ) ) + { + inchi_free(HGen); + return (INCHIGEN_HANDLE) NULL; + } + + + return (INCHIGEN_HANDLE) HGen; +} + + +/* InChI Generator: initialization stage (accepts a specific structure) */ +int INCHI_DECL STDINCHIGEN_Setup(INCHIGEN_HANDLE _HGen, + INCHIGEN_DATA * pGenData, + inchi_Input * pInp) +{ +INCHIGEN_CONTROL *HGen = (INCHIGEN_CONTROL *)_HGen; +INPUT_PARMS *ip = &(HGen->InpParms); +STRUCT_DATA *sd = &(HGen->StructData); +int retcode = inchi_Ret_OKAY; + + retcode = INCHIGEN_Setup(_HGen, pGenData, pInp); + + /* Ensure standardness */ + if ( ip->bINChIOutputOptions & INCHI_OUT_SAVEOPT ) + { + ip->bINChIOutputOptions &= ~INCHI_OUT_SAVEOPT; + retcode = _IS_WARNING; + } + if ( 0 != ( ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) + { + ip->bTautFlags &= ~TG_FLAG_RECONNECT_COORD; + retcode = _IS_WARNING; + } + if ( 0 != (ip->nMode & REQ_MODE_BASIC) ) + { + ip->nMode &= ~REQ_MODE_BASIC; + retcode = _IS_WARNING; + } + if ( 0 != ( ip->nMode & REQ_MODE_RELATIVE_STEREO) ) + { + ip->nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); + retcode = _IS_WARNING; + } + if ( 0 != ( ip->nMode & REQ_MODE_RACEMIC_STEREO) ) + { + ip->nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); + retcode = _IS_WARNING; + } + if ( 0 != ( ip->nMode & REQ_MODE_CHIR_FLG_STEREO) ) + { + ip->nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); + retcode = _IS_WARNING; + } + if ( 0 != ( ip->nMode & REQ_MODE_DIFF_UU_STEREO) ) + { + ip->nMode &= ~REQ_MODE_DIFF_UU_STEREO; + retcode = _IS_WARNING; + } + if ( 0 == (ip->nMode & (REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU)) ) + { + ip->nMode |= REQ_MODE_SB_IGN_ALL_UU; + ip->nMode |= REQ_MODE_SC_IGN_ALL_UU; + retcode = _IS_WARNING; + } + if ( 0 != (ip->bTautFlags & TG_FLAG_KETO_ENOL_TAUT) ) + { + ip->bTautFlags &= ~TG_FLAG_KETO_ENOL_TAUT; + retcode = _IS_WARNING; + } + if ( 0 != (ip->bTautFlags & TG_FLAG_1_5_TAUT) ) + { + ip->bTautFlags &= ~TG_FLAG_1_5_TAUT; + retcode = _IS_WARNING; + } + + /* And anyway... */ + ip->bINChIOutputOptions |= INCHI_OUT_STDINCHI; + ip->bINChIOutputOptions &= ~INCHI_OUT_SAVEOPT; + + strcpy(pGenData->pStrErrStruct, sd->pStrErrStruct); + return retcode; +} + + +int INCHI_DECL INCHIGEN_Setup(INCHIGEN_HANDLE _HGen, + INCHIGEN_DATA * pGenData, + inchi_Input * pInp) +{ +int retcode = inchi_Ret_OKAY; + +INCHIGEN_CONTROL *HGen = (INCHIGEN_CONTROL *)_HGen; + +ORIG_ATOM_DATA *orig_inp_data = &(HGen->OrigInpData); +STRUCT_DATA *sd = &(HGen->StructData); +INPUT_PARMS *ip = &(HGen->InpParms); +INCHI_IOSTREAM *log_file = HGen->inchi_file+1; +INCHI_IOSTREAM prbstr, *prb_file=&prbstr; + +const char *argv[INCHI_MAX_NUM_ARG+1]; +int argc; +char *szOptions = NULL; +char szSdfDataValue[MAX_SDF_VALUE+1]; +int bReleaseVersion = bRELEASE_VERSION; +unsigned long ulDisplTime = 0; /* infinite, milliseconds */ +int p; +inchi_InputEx inpEx; + + inchi_InputEx *pInpEx = &inpEx; + pInpEx->atom = pInp->atom; + pInpEx->num_atoms = pInp->num_atoms; + pInpEx->num_stereo0D = pInp->num_stereo0D; + pInpEx->stereo0D = pInp->stereo0D; + pInpEx->szOptions = pInp->szOptions; + pInpEx->polymer = NULL; /* v. 1.05 ext not supported in modulariozed API */ + pInpEx->v3000 = NULL; /* v. 1.05 ext not supported in modulariozed API */ + + + /* Allocate/init */ + if (!pGenData) + { + retcode = _IS_ERROR; + goto ret; + } + memset(pGenData, 0, sizeof(*pGenData)); + + /* Parse 'command-line' options and fill internal INPUT_PARMS structure */ + if ( pInp && pInp->szOptions ) + { + szOptions = (char*)inchi_malloc( strlen(pInp->szOptions) + 1 ); + if (!szOptions) + return _IS_FATAL; /* Not enough memory.... */ + else + { + /* Parse. */ + strcpy( szOptions, pInp->szOptions ); + argc = parse_options_string ( szOptions, argv, INCHI_MAX_NUM_ARG ); + } + } + else + { + /* Got NULL options string or NULL 'pInp', will use defaults. */ + argc = 1; + argv[0] = ""; + argv[1] = NULL; + } + + + if ( argc == 1 +#ifdef TARGET_API_LIB + && (!pInp || pInp->num_atoms <= 0 || !pInp->atom) +#endif + || argc==2 && ( argv[1][0]==INCHI_OPTION_PREFX ) && + (!strcmp(argv[1]+1, "?") || !inchi_stricmp(argv[1]+1, "help") ) ) + { + + HelpCommandLineParms(log_file); + memset( log_file, 0, sizeof(*log_file) ); + return _IS_EOF; + } + + + memset( szSdfDataValue , 0, sizeof( szSdfDataValue ) ); + + + /* Decrypt command line */ + retcode = ReadCommandLineParms( argc, argv, ip, szSdfDataValue, &ulDisplTime, bReleaseVersion, log_file ); + + if (szOptions) + inchi_free( szOptions ); + + /* INChI DLL specific */ + ip->bNoStructLabels = 1; + + if ( 0 > retcode) goto ret; + + if ( ip->bNoStructLabels ) + { + ip->pSdfLabel = NULL; + ip->pSdfValue = NULL; + } + else + if ( ip->nInputType == INPUT_INCHI_XML || + ip->nInputType == INPUT_INCHI_PLAIN || + ip->nInputType == INPUT_CMLFILE ) + { + /* the input may contain both the header and the label of the structure */ + if ( !ip->pSdfLabel ) + ip->pSdfLabel = ip->szSdfDataHeader; + if ( !ip->pSdfValue ) + ip->pSdfValue = szSdfDataValue; + } + + if (retcode!=inchi_Ret_OKAY) goto ret; + + PrintInputParms( log_file, ip); + + /* Extract the structure */ + retcode = ExtractOneStructure( sd, ip, + HGen->szTitle, + pInpEx, + log_file, + HGen->inchi_file, /* out_file */ + prb_file, + orig_inp_data, + &(HGen->num_inp) ); + + +ret:switch (retcode) + { + case _IS_OKAY : retcode = inchi_Ret_OKAY ; HGen->init_passed = 1; break; /* Success; break; no errors or warnings */ + + case _IS_ERROR : (HGen->num_err)++; retcode = inchi_Ret_ERROR ; break; + /* Error: no INChI has been created */ + case _IS_FATAL : (HGen->num_err)++; retcode = inchi_Ret_FATAL ; break; + /* Severe error: no INChI has been created + (typically; break; memory allocation failed) */ + case _IS_SKIP : retcode = inchi_Ret_SKIP ; break; /* not used in INChI dll */ + case _IS_EOF : retcode = inchi_Ret_EOF ; break; /* no structural data has been provided */ + case _IS_WARNING: retcode = inchi_Ret_WARNING; HGen->init_passed = 1; break; /* Success; break; warning(s) issued */ + case _IS_UNKNOWN: + default : retcode = inchi_Ret_UNKNOWN; break; /* Unlnown program error */ + } + + if (!pGenData) + { + strcpy(pGenData->pStrErrStruct, sd->pStrErrStruct); + for (p=0; p < INCHI_NUM; p++) + pGenData->num_components[p] = sd->num_components[p]; + } + + return retcode; +} + + + + +/* + Get normalized form of the structure +*/ + + +int INCHI_DECL STDINCHIGEN_DoNormalization(INCHIGEN_HANDLE HGen,INCHIGEN_DATA * pGenData) +{ + return INCHIGEN_DoNormalization( HGen, pGenData); +} + + +int INCHI_DECL INCHIGEN_DoNormalization(INCHIGEN_HANDLE _HGen, INCHIGEN_DATA *pGenData) +{ +int nRet=0, nRet1=0; +/* int maxINChI=0; */ + + +INCHIGEN_CONTROL * HGen = (INCHIGEN_CONTROL *)_HGen; +INPUT_PARMS *ip = &(HGen->InpParms); +STRUCT_DATA *sd = &(HGen->StructData); +NORM_CANON_FLAGS *pncFlags = &(HGen->ncFlags); +INCHI_IOSTREAM *out_file = HGen->inchi_file; +INCHI_IOSTREAM inpstr, *inp_file = &inpstr; +ORIG_ATOM_DATA *orig_inp_data = &(HGen->OrigInpData); +ORIG_STRUCT *pOrigStruct = NULL; +INCHI_CLOCK ic; +CANON_GLOBALS CG; +int k; + +memset(&CG, 0, sizeof(CG)); +memset(&ic, 0, sizeof(ic)); + +#if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) +int ret1=0, ret2=0; +#endif + +/* Set debug output */ +#if (TRACE_MEMORY_LEAKS == 1) + + _CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF); +/* for execution outside the VC++ debugger uncomment one of the following two */ +#ifdef MY_REPORT_FILE + _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_WARN, MY_REPORT_FILE ); + _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ERROR, MY_REPORT_FILE ); + _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ASSERT, MY_REPORT_FILE ); +#else + _CrtSetReportMode(_CRT_WARN | _CRT_ERROR, _CRTDBG_MODE_DEBUG); +#endif +#if ( !defined(__STDC__) || __STDC__ != 1 ) + /* turn on floating point exceptions */ + { + /* Get the default control word. */ + int cw = _controlfp( 0,0 ); + /* Set the exception masks OFF, turn exceptions on. */ + /*cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_INEXACT|EM_ZERODIVIDE|EM_DENORMAL);*/ + cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE|EM_DENORMAL); + /* Set the control word. */ + _controlfp( cw, MCW_EM ); + } +#endif /* ( !defined(__STDC__) || __STDC__ != 1 ) */ + +#endif /* (TRACE_MEMORY_LEAKS == 1) */ + + if (HGen->init_passed==0) + { + AddErrorMessage(sd->pStrErrStruct, "InChI generator not initialized"); + sd->nStructReadError = 99; + sd->nErrorType = _IS_ERROR; + nRet = _IS_ERROR; + goto exit_function; + } + + inchi_ios_init(inp_file, INCHI_IOSTREAM_TYPE_FILE, NULL); + + sd->bUserQuitComponent = 0; + sd->bUserQuitComponentDisplay = 0; + memset( HGen->composite_norm_data, 0, sizeof(HGen->composite_norm_data) ); + memset( pncFlags, 0, sizeof(*pncFlags) ); + + /* for testing only */ +#if( REMOVE_ION_PAIRS_ORIG_STRU == 1 ) + fix_odd_things( orig_inp_data->num_inp_atoms, orig_inp_data->at, 0 ); +#endif +#if( UNDERIVATIZE == 1 ) /***** post v.1 feature *****/ + if ( ip->bUnderivatize && 0 > (ret2=underivatize( orig_inp_data )) ) + { + long num_inp2 = HGen->num_inp; + AddErrorMessage(sd->pStrErrStruct, "Underivatization error"); + sd->nStructReadError = 99; + sd->nErrorType = _IS_ERROR; + nRet = _IS_ERROR; + TreatReadTheStructureErrors( sd, ip, LOG_MASK_ALL, inp_file, log_file, out_file, prb_file, + prep_inp_data, &num_inp2, HGen->pStr, PSTR_BUFFER_SIZE); + goto exit_function; /* output only if derivatives found */ + } +#endif /* UNDERIVATIZE == 1 */ +#if( RING2CHAIN == 1 ) /***** post v.1 feature *****/ + if ( ip->bRing2Chain && 0 > (ret1 = Ring2Chain( orig_inp_data )) ) + { + long num_inp2 = HGen->num_inp; + AddErrorMessage(sd->pStrErrStruct, "Ring to chain error"); + sd->nStructReadError = 99; + sd->nErrorType = _IS_ERROR; + nRet = _IS_ERROR; + TreatReadTheStructureErrors( sd, ip, LOG_MASK_ALL, inp_file, log_file, out_file, prb_file, + prep_inp_data, &num_inp2, HGen->pStr, PSTR_BUFFER_SIZE); + goto exit_function; /* output only if derivatives found */ + } +#endif /* RING2CHAIN == 1 */ +#if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) /***** post v.1 feature *****/ + if ( ip->bIngnoreUnchanged && !ret1 && !ret2 ) + { + goto exit_function; /* output only if derivatives or ring/chain found */ + } +#endif /* RING2CHAIN == 1 || UNDERIVATIZE == 1 */ + + + /***** output MOLfile ***************/ + if ( ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY ) + { + char szNumber[32]; + int ret1a=0, ret2a=0; /* for derivatives and ring-chain */ + ret1a = sprintf(szNumber, "Structure #%ld", HGen->num_inp); + ret2a = OrigAtData_WriteToSDfile( orig_inp_data, out_file, szNumber, NULL, + (sd->bChiralFlag & FLAG_INP_AT_CHIRAL)? 1:0, + (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ATOMS_DT)? 1:0, ip->pSdfLabel, ip->pSdfValue ); + goto exit_function; + } + + /******* create full reversibility information **************/ + if ( !(ip->bINChIOutputOptions & (INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO)) ) + { + pOrigStruct = &(HGen->OrigStruct); + memset( pOrigStruct, 0, sizeof(*pOrigStruct)); + if ( OrigStruct_FillOut( &CG, orig_inp_data, pOrigStruct, sd ) ) + { + AddErrorMessage(sd->pStrErrStruct, "Cannot interpret reversibility information"); + sd->nStructReadError = 99; + sd->nErrorType = _IS_ERROR; + nRet = _IS_ERROR; + } + } + + + sd->bUserQuit = 0; + if (sd->bUserQuit) goto exit_function; + + + + /* Normalize the whole disconnected or original structure */ + if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) + { + nRet1 = NormOneStructureINChI( &CG, &ic, pGenData, HGen, INCHI_BAS, inp_file); + nRet = inchi_max(nRet, nRet1); + } + +/* + if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) + maxINChI = 1; +*/ + + if ( nRet != _IS_FATAL && nRet != _IS_ERROR && + (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) && + (ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) + { + /* Normalize the whole reconnected structure */ + nRet1 = NormOneStructureINChI( &CG, &ic, pGenData, HGen, INCHI_REC, inp_file); + nRet = inchi_max(nRet, nRet1); +/* + if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) + maxINChI = 2; +*/ + } + +exit_function: + + if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) + HGen->norm_passed = 1; + + for (k=0; k < INCHI_NUM; k++) + pGenData->num_components[k] = sd->num_components[k]; + + /* Emit normalization warnings */ + if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) + { + int ic, istruct, itaut, nc[2]; + int warn_prot=0, warn_neutr=0; + INP_ATOM_DATA *inp_norm_data[TAUT_NUM]; /* = { &InpNormAtData, &InpNormTautData }; */ + nc[0] = pGenData->num_components[0]; + nc[1] = pGenData->num_components[1]; + for (istruct=0; istruct<2; istruct++) + { + if (nc[istruct]>0) + { + for (ic=0; ic < nc[istruct]; ic++) + { + inp_norm_data[0] = &(HGen->InpNormAtData[istruct][ic]); + inp_norm_data[1] = &(HGen->InpNormTautData[istruct][ic]); + for (itaut=0;itaut<2;itaut++) + { + if (NULL!=inp_norm_data[itaut]) + { + if ( inp_norm_data[itaut]->bTautomeric ) + { + if (inp_norm_data[itaut]->bNormalizationFlags & (FLAG_NORM_CONSIDER_TAUT &~FLAG_PROTON_CHARGE_CANCEL) ) + if (warn_prot==0) + { + warn_prot++; + WarningMessage(sd->pStrErrStruct, "Proton(s) added/removed"); + } + if (inp_norm_data[itaut]->bNormalizationFlags & FLAG_PROTON_CHARGE_CANCEL ) + if (warn_neutr==0) + { + warn_neutr++; + WarningMessage(sd->pStrErrStruct, "Charges neutralized"); + } + } + } + } /* itaut */ + } + } + } + } + + strcpy(pGenData->pStrErrStruct, sd->pStrErrStruct); + make_norm_atoms_from_inp_atoms(pGenData, HGen); + + return nRet; +} + + + + +/* + Get canonicalized form of the structure +*/ + + +int INCHI_DECL STDINCHIGEN_DoCanonicalization + (INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData) +{ + return INCHIGEN_DoCanonicalization(HGen, pGenData ) ; +} + +int INCHI_DECL INCHIGEN_DoCanonicalization + (INCHIGEN_HANDLE _HGen, INCHIGEN_DATA *pGenData ) +{ +int nRet = 0, nRet1 /*, maxINChI=0*/; +INCHIGEN_CONTROL * HGen = (INCHIGEN_CONTROL *)_HGen; + + +STRUCT_DATA *sd = &(HGen->StructData); +INPUT_PARMS *ip = &(HGen->InpParms); +INCHI_IOSTREAM *out_file = HGen->inchi_file, *log_file = HGen->inchi_file+1; +INCHI_IOSTREAM prbstr, *prb_file=&prbstr; +INCHI_IOSTREAM inpstr, *inp_file = &inpstr; + +ORIG_ATOM_DATA *prep_inp_data = &(HGen->PrepInpData[0]); + +INCHI_CLOCK ic; +CANON_GLOBALS CG; + +int k; + +memset(&ic, 0, sizeof(ic)); +memset(&CG, 0, sizeof(CG)); + +/* Set debug output */ +#if (TRACE_MEMORY_LEAKS == 1) + + _CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF); + /* for execution outside the VC++ debugger uncomment one of the following two */ +#ifdef MY_REPORT_FILE + _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_WARN, MY_REPORT_FILE ); + _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ERROR, MY_REPORT_FILE ); + _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ASSERT, MY_REPORT_FILE ); +#else + _CrtSetReportMode(_CRT_WARN | _CRT_ERROR, _CRTDBG_MODE_DEBUG); +#endif +#if ( !defined(__STDC__) || __STDC__ != 1 ) + /* turn on floating point exceptions */ + { + /* Get the default control word. */ + int cw = _controlfp( 0,0 ); + /* Set the exception masks OFF, turn exceptions on. */ + /*cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_INEXACT|EM_ZERODIVIDE|EM_DENORMAL);*/ + cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE|EM_DENORMAL); + /* Set the control word. */ + _controlfp( cw, MCW_EM ); + } +#endif /* ( !defined(__STDC__) || __STDC__ != 1 ) */ + +#endif /* (TRACE_MEMORY_LEAKS == 1) */ + + + + if (HGen->norm_passed==0) + { + AddErrorMessage(sd->pStrErrStruct, "Got non-normalized structure"); + sd->nStructReadError = 99; + sd->nErrorType = _IS_ERROR; + nRet = _IS_ERROR; + goto exit_function; + } + + + inchi_ios_init(inp_file, INCHI_IOSTREAM_TYPE_FILE, NULL); + inchi_ios_init(prb_file, INCHI_IOSTREAM_TYPE_FILE, NULL); + + sd->bUserQuit = 0; + if (sd->bUserQuit) goto exit_function; + + /* create INChI for each connected component of the structure and optionally display them */ + /* output INChI for the whole structure */ + + + + /* create INChI for each connected component of the structure and optionally display them */ + /* create INChI for the whole disconnected or original structure */ + if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) { + nRet1 = CanonOneStructureINChI( &CG, &ic, HGen, INCHI_BAS, inp_file); + nRet = inchi_max(nRet, nRet1); + } + +/* + if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) + maxINChI = 1; +*/ + + if ( nRet != _IS_FATAL && nRet != _IS_ERROR && + (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) && + (ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) + { + /* create INChI for the whole reconnected structure */ + nRet1 = CanonOneStructureINChI( &CG, &ic, HGen, INCHI_REC, inp_file); + nRet = inchi_max(nRet, nRet1); +/* + if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) + maxINChI = 2; +*/ + } + + if (nRet != _IS_FATAL && nRet != _IS_ERROR) + { + if ( (sd->bChiralFlag & FLAG_INP_AT_CHIRAL) && + (ip->nMode & REQ_MODE_STEREO) && + !(ip->nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO)) && + !bIsStructChiral( HGen->pINChI, sd->num_components ) ) + { + WarningMessage(sd->pStrErrStruct, "Not chiral"); + } + + /*************************************/ + /* Output err/warn messages */ + /*************************************/ + if ( /*!sd->nErrorCode &&*/ !sd->bUserQuitComponent && !sd->bUserQuit ) + { + /* if successful then returns 0, otherwise returns _IS_FATAL */ + /* extract the structure if requested */ + nRet1 = TreatCreateINChIWarning(sd, ip, prep_inp_data, HGen->num_inp, + inp_file, log_file, out_file, prb_file ); + nRet = inchi_max(nRet, nRet1); + } + } + + + + + switch (nRet) + { + case _IS_SKIP : nRet = inchi_Ret_SKIP ; break; /* not used in INChI dll */ + case _IS_EOF : nRet = inchi_Ret_EOF ; break; /* no structural data has been provided */ + case _IS_OKAY : nRet = inchi_Ret_OKAY ; HGen->canon_passed = 1; break; + /* Success; break; no errors or warnings */ + case _IS_WARNING: nRet = inchi_Ret_WARNING; HGen->canon_passed = 1; break; + /* Success; break; warning(s) issued */ + case _IS_ERROR : nRet = inchi_Ret_ERROR ; break; /* Error: no INChI has been created */ + case _IS_FATAL : nRet = inchi_Ret_FATAL ; break; /* Severe error: no INChI has been created (typically; break; memory allocation failed) */ + case _IS_UNKNOWN: + default : nRet = inchi_Ret_UNKNOWN; break; /* Unknown program error */ + } +exit_function: + + strcpy(pGenData->pStrErrStruct, sd->pStrErrStruct); + for (k=0; k < INCHI_NUM; k++) + pGenData->num_components[k] = sd->num_components[k]; + + + return nRet; +} /* INCHIGEN_DoCanonicalization */ + + + + + +/* + Get serialized form (InChI string). +*/ + + +int INCHI_DECL STDINCHIGEN_DoSerialization(INCHIGEN_HANDLE HGen, + INCHIGEN_DATA * pGenData, + inchi_Output * pResults) +{ + return INCHIGEN_DoSerialization(HGen, pGenData, pResults); +} + + +int INCHI_DECL INCHIGEN_DoSerialization(INCHIGEN_HANDLE _HGen, + INCHIGEN_DATA * pGenData, + inchi_Output * pResults) +{ +int nRet=0, nRet1=0, i, k; + + + +INCHIGEN_CONTROL * HGen = (INCHIGEN_CONTROL *)_HGen; + +INPUT_PARMS *ip = &(HGen->InpParms); +INCHI_IOSTREAM *out_file = HGen->inchi_file, *log_file = HGen->inchi_file+1; +INCHI_IOSTREAM inpstr, *inp_file = &inpstr; +INCHI_IOSTREAM prbstr, *prb_file=&prbstr; + +STRUCT_DATA *sd = &(HGen->StructData); +NORM_CANON_FLAGS *pncFlags = &(HGen->ncFlags); +ORIG_ATOM_DATA *orig_inp_data = &(HGen->OrigInpData); +ORIG_ATOM_DATA *prep_inp_data = &(HGen->PrepInpData[0]); +ORIG_STRUCT *pOrigStruct = &(HGen->OrigStruct); +int bSortPrintINChIFlags=0; +unsigned char save_opt_bits=0; +int retcode = 0; + +CANON_GLOBALS CG; +memset(&CG, 0, sizeof(CG)); + + + /* Post-1.02b - added initialization of pResults to 0; thanks to David Foss */ + memset(pResults, 0, sizeof(*pResults)); + pResults->szLog = log_file->s.pStr; + inchi_ios_init(inp_file, INCHI_IOSTREAM_TYPE_FILE, NULL); + inchi_ios_init(prb_file, INCHI_IOSTREAM_TYPE_FILE, NULL); + + +/* Set debug output */ + +#if (TRACE_MEMORY_LEAKS == 1) + + _CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF); + +/* for execution outside the VC++ debugger uncomment one of the following two */ +#ifdef MY_REPORT_FILE + _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_WARN, MY_REPORT_FILE ); + _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ERROR, MY_REPORT_FILE ); + _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ASSERT, MY_REPORT_FILE ); +#else + _CrtSetReportMode(_CRT_WARN | _CRT_ERROR, _CRTDBG_MODE_DEBUG); +#endif +#if ( !defined(__STDC__) || __STDC__ != 1 ) + /* turn on floating point exceptions */ + { + /* Get the default control word. */ + int cw = _controlfp( 0,0 ); + /* Set the exception masks OFF, turn exceptions on. */ + /*cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_INEXACT|EM_ZERODIVIDE|EM_DENORMAL);*/ + cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE|EM_DENORMAL); + /* Set the control word. */ + _controlfp( cw, MCW_EM ); + } +#endif /* ( !defined(__STDC__) || __STDC__ != 1 ) */ + +#endif /* (TRACE_MEMORY_LEAKS == 1) */ + + +/*****************************/ + + + if (HGen->canon_passed==0) + { + AddErrorMessage(sd->pStrErrStruct, "Got non-canonicalized structure"); + sd->nStructReadError = 99; + sd->nErrorType = _IS_ERROR; + retcode = _IS_ERROR; + goto frees; + } + + + /************************************************/ + /* sort and print INChI for the whole structure */ + /************************************************/ + + /* Prepare SaveOpt bits */ + if ( ip->bINChIOutputOptions & INCHI_OUT_SAVEOPT ) + { + if ( 0 != ( ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) + save_opt_bits |= SAVE_OPT_RECMET; + if ( 0 != ( ip->nMode & REQ_MODE_BASIC) ) + save_opt_bits |= SAVE_OPT_FIXEDH; + if ( 0 != ( ip->nMode & REQ_MODE_DIFF_UU_STEREO) ) + save_opt_bits |= SAVE_OPT_SLUUD; + if ( 0 == (ip->nMode & (REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU)) ) + save_opt_bits |= SAVE_OPT_SUU; + if ( 0 != (ip->bTautFlags & TG_FLAG_KETO_ENOL_TAUT) ) + save_opt_bits |= SAVE_OPT_KET; + if ( 0 != (ip->bTautFlags & TG_FLAG_1_5_TAUT) ) + save_opt_bits |= SAVE_OPT_15T; + } + + + nRet = SortAndPrintINChI(&CG, + out_file, + &(HGen->strbuf_container), + log_file, + ip, orig_inp_data, prep_inp_data, + HGen->composite_norm_data, pOrigStruct, + sd->num_components, sd->num_non_taut, sd->num_taut, + sd->bTautFlags, sd->bTautFlagsDone, pncFlags, HGen->num_inp, + HGen->pINChI, HGen->pINChI_Aux, + &bSortPrintINChIFlags, save_opt_bits); + + + if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) + { + /* Special mode: extract all good MOLfiles into the problem file + * Do not extract any MOLfile that could not be processed (option /PGO) + */ + if ( prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd && ip->bSaveAllGoodStructsAsProblem ) { + MolfileSaveCopy(inp_file, sd->fPtrStart, sd->fPtrEnd, prb_file->f, 0); + } +#if( /*bRELEASE_VERSION != 1 &&*/ EXTR_FLAGS == EXTR_TRANSPOSITION_EXAMPLES && EXTR_MASK == EXTR_FLAGS ) + else + if ( prb_file->f && (bSortPrintINChIFlags & + ( FLAG_SORT_PRINT_TRANSPOS_BAS | FLAG_SORT_PRINT_TRANSPOS_REC ) ) + ) + { + MolfileSaveCopy(inp_file, sd->fPtrStart, sd->fPtrEnd, prb_file->f, 0); + } +#endif + } + + for ( i = 0; i < INCHI_NUM; i ++ ) + { + for ( k = 0; k < TAUT_NUM+1; k ++ ) + { + FreeCompAtomData( &(HGen->composite_norm_data[i][k]) ); + } + } +/*****************************/ + + /* Prepare output message(s). */ + + + /* Error/warning. */ + if ( sd->pStrErrStruct[0] ) + if ( pGenData && (pResults->szMessage = (char *)inchi_malloc( strlen(sd->pStrErrStruct) + 1 )) ) + strcpy( pResults->szMessage, sd->pStrErrStruct ); + + /* InChI, AuxInfo (go to pResults->szInChI, pResults->szAuxInfo) */ + if ( out_file->s.pStr && out_file->s.nUsedLength > 0 && pGenData ) + { + char *p; + pResults->szInChI = out_file->s.pStr; + pResults->szAuxInfo = NULL; + if ( !(INCHI_OUT_SDFILE_ONLY & ip->bINChIOutputOptions ) ) /* do not remove last LF from SDF output - 2008-12-23 DT */ + for ( p = strchr(pResults->szInChI, '\n'); p; p = strchr(p+1, '\n') ) + { + if ( !memcmp( p, "\nAuxInfo", 8 ) ) + { + *p = '\0'; /* remove LF after INChI */ + pResults->szAuxInfo = p+1; /* save pointer to AuxInfo */ + } + else + if ( pResults->szAuxInfo || !p[1]) + { + /* remove LF after aux info or from the last char */ + *p = '\0'; + break; + } + } + out_file->s.pStr = NULL; + } + + + /* Log message. */ + if ( log_file->s.pStr && log_file->s.nUsedLength > 0 ) + { + while ( log_file->s.nUsedLength && '\n' == log_file->s.pStr[log_file->s.nUsedLength-1] ) + log_file->s.pStr[-- log_file->s.nUsedLength] = '\0'; /* remove last LF */ + if ( pGenData ) + { + pResults->szLog = log_file->s.pStr; + log_file->s.pStr = NULL; + } + } + + if ( out_file->s.pStr ) {inchi_free( out_file->s.pStr ); out_file->s.pStr = NULL;} + if ( log_file->s.pStr ) {inchi_free( log_file->s.pStr ); log_file->s.pStr = NULL;} + + + + + + HGen->ulTotalProcessingTime += sd->ulStructTime; + nRet = inchi_max(nRet, nRet1); + + switch ( nRet ) + { + case _IS_FATAL: + case _IS_ERROR: HGen->num_err ++; + } + +frees: + /* Free all. */ + for ( i = 0; i < MAX_NUM_PATHS; i ++ ) + { + if ( ip->path[i] ) + { + inchi_free( (char*) ip->path[i] ); /* cast deliberately discards 'const' qualifier */ + ip->path[i] = NULL; + } + } + SetBitFree( &CG ); + + + strcpy(pGenData->pStrErrStruct, sd->pStrErrStruct); + for (k=0; k < INCHI_NUM; k++) + pGenData->num_components[k] = sd->num_components[k]; + + + return retcode; +} + + + + +/* + InChI Generator: reset stage (use before get next structure) +*/ + + +void INCHI_DECL STDINCHIGEN_Reset(INCHIGEN_HANDLE HGen, + INCHIGEN_DATA * pGenData, + inchi_Output * pResults) +{ + INCHIGEN_Reset(HGen, pGenData, pResults); +} + + +void INCHI_DECL INCHIGEN_Reset(INCHIGEN_HANDLE _HGen, + INCHIGEN_DATA * pGenData, + inchi_Output * pResults) +{ +int i, k, nc; +INCHIGEN_CONTROL * HGen = (INCHIGEN_CONTROL *)_HGen; + + + if ( pResults->szInChI ) inchi_free( pResults->szInChI ); + if ( pResults->szLog ) inchi_free( pResults->szLog ); + if ( pResults->szMessage ) inchi_free( pResults->szMessage ); + + + /* Free all data associated with components of disconn/conn structures */ + + if (NULL!=HGen) + { + + /* Re-initialize output streams/string buffers */ + inchi_ios_close(&(HGen->inchi_file[0])); + inchi_ios_close(&(HGen->inchi_file[1])); + inchi_ios_close(&(HGen->inchi_file[2])); + inchi_ios_init(&(HGen->inchi_file[0]), INCHI_IOSTREAM_TYPE_STRING, NULL); + inchi_ios_init(&(HGen->inchi_file[1]), INCHI_IOSTREAM_TYPE_STRING, NULL); + inchi_ios_init(&(HGen->inchi_file[2]), INCHI_IOSTREAM_TYPE_STRING, NULL); + + + inchi_strbuf_reset( &(HGen->strbuf_container) ); + + + for ( i = 0; i < MAX_NUM_PATHS; i ++ ) + { + if ( HGen->InpParms.path[i] ) + { + inchi_free( (char*) HGen->InpParms.path[i] ); /* cast deliberately discards 'const' qualifier */ + HGen->InpParms.path[i] = NULL; + } + } + memset(&(HGen->InpParms), 0, sizeof(INPUT_PARMS)); + + FreeOrigAtData( &(HGen->OrigInpData) ); + memset(&(HGen->OrigInpData), 0, sizeof( HGen->OrigInpData ) ); + + + FreeOrigAtData( &(HGen->PrepInpData[0])); + FreeOrigAtData( &(HGen->PrepInpData[1])); + memset(&(HGen->PrepInpData[0]), 0, 2*sizeof( HGen->PrepInpData[0] ) ); + + FreeOrigStruct( &(HGen->OrigStruct)); + memset(&(HGen->OrigStruct), 0, sizeof( HGen->OrigStruct ) ); + + + for ( i = 0; i < INCHI_NUM; i ++ ) + for ( k = 0; k < TAUT_NUM+1; k ++ ) + FreeCompAtomData( &(HGen->composite_norm_data[i][k]) ); + + for ( k = 0; k < INCHI_NUM; k++) + { + nc = HGen->StructData.num_components[k]; + + if ( HGen->InpCurAtData[k] ) + { + for ( i = 0; i < nc; i ++ ) + FreeInpAtomData( &(HGen->InpCurAtData[k][i]) ); + inchi_free(HGen->InpCurAtData[k]); + HGen->InpCurAtData[k] = NULL; + } + + + if ( HGen->cti[k] ) + { + if ( (HGen->cti[k])->at[TAUT_YES] ) + { + inchi_free( (HGen->cti[k])->at[TAUT_YES] ); + (HGen->cti[k])->at[TAUT_YES] = NULL; + } + + if ( (HGen->cti[k])->at[TAUT_NON] ) + { + inchi_free( (HGen->cti[k])->at[TAUT_NON] ); + (HGen->cti[k])->at[TAUT_NON] = NULL; + } + + if (&((HGen->cti[k])->vt_group_info)) + free_t_group_info(&((HGen->cti[k])->vt_group_info)); + + if (&((HGen->cti[k])->vt_group_info_orig)) + free_t_group_info(&((HGen->cti[k])->vt_group_info_orig)); + + inchi_free(HGen->cti[k]); + HGen->cti[k] = NULL; + } + } + + + for ( k = 0; k < INCHI_NUM; k++) + { + nc = HGen->StructData.num_components[k]; + if ( HGen->InpNormAtData[k] ) + { + for ( i = 0; i < nc; i ++ ) + FreeInpAtomData( &(HGen->InpNormAtData[k][i]) ); + inchi_free(HGen->InpNormAtData[k]); + HGen->InpNormAtData[k] = NULL; + } + + if ( HGen->InpNormTautData[k] ) + { + for ( i = 0; i < nc; i ++ ) + FreeInpAtomData( &(HGen->InpNormTautData[k][i]) ); + inchi_free(HGen->InpNormTautData[k]); + HGen->InpNormTautData[k] = NULL; + } + + + if ( pGenData->NormAtomsTaut[k] ) + { + /* + for ( i = 0; i < nc; i ++ ) + FreeInpAtomData( &(pGenData->NormAtomsTaut[k][i]) ); + */ + inchi_free(pGenData->NormAtomsTaut[k]); + pGenData->NormAtomsTaut[k] = NULL; + } + if ( pGenData->NormAtomsNontaut[k] ) + { + /* for ( i = 0; i < nc; i ++ ) + FreeInpAtomData( &(pGenData->NormAtomsNontaut[k][i]) ); + */ + inchi_free(pGenData->NormAtomsNontaut[k]); + pGenData->NormAtomsNontaut[k] = NULL; + } + } + + /* free INChI memory */ + FreeAllINChIArrays( HGen->pINChI, HGen->pINChI_Aux, HGen->StructData.num_components ); + memset(HGen->pINChI, 0, sizeof(HGen->pINChI) ); + memset(HGen->pINChI_Aux, 0, sizeof(HGen->pINChI_Aux) ); + + HGen->szTitle[0] = '\0'; + } + + memset(&(HGen->StructData), 0, sizeof(STRUCT_DATA) ); + + memset( pResults, 0, sizeof(*pResults) ); + memset( pGenData , 0, sizeof(*pGenData) ); + + return; +} + + + + +/* + InChI Generator: destroy generator +*/ + + +void INCHI_DECL STDINCHIGEN_Destroy(INCHIGEN_HANDLE HGen) +{ + INCHIGEN_Destroy(HGen) ; +} + + +void INCHI_DECL INCHIGEN_Destroy(INCHIGEN_HANDLE _HGen) +{ +INCHIGEN_CONTROL * HGen = (INCHIGEN_CONTROL *)_HGen; + + if (NULL!=HGen) + { + inchi_strbuf_close( &(HGen->strbuf_container) ); + + inchi_ios_close(&(HGen->inchi_file[0])); + inchi_ios_close(&(HGen->inchi_file[1])); + inchi_ios_close(&(HGen->inchi_file[2])); + + inchi_free(HGen); + } +} + + +#if( defined( _WIN32 ) && defined( _MSC_VER ) && _MSC_VER >= 800 && defined(_USRDLL) && defined(BUILD_LINK_AS_DLL) ) + /* Win32 & MS VC ++, compile and link as a DLL */ +/*********************************************************/ +/* C calling conventions export from Win32 dll */ +/*********************************************************/ +/* prototypes */ +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif +INCHIGEN_HANDLE cdecl_INCHIGEN_Create(void); +INCHIGEN_HANDLE cdecl_STDINCHIGEN_Create(void); +int cdecl_INCHIGEN_Setup( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp); +int cdecl_STDINCHIGEN_Setup( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp); +int cdecl_INCHIGEN_DoNormalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ); +int cdecl_STDINCHIGEN_DoNormalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ); +int cdecl_INCHIGEN_DoCanonicalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ); +int cdecl_STDINCHIGEN_DoCanonicalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ); +int cdecl_INCHIGEN_DoSerialization(INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults ); +int cdecl_STDINCHIGEN_DoSerialization(INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults ); +void cdecl_INCHIGEN_Reset( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults); +void cdecl_STDINCHIGEN_Reset( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults); +void cdecl_INCHIGEN_Destroy( INCHIGEN_HANDLE HGen ); +void cdecl_STDINCHIGEN_Destroy( INCHIGEN_HANDLE HGen ); +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + + +/* Implementation */ +/* libinchi.def provides export without cdecl_ prefixes */ + +INCHIGEN_HANDLE cdecl_INCHIGEN_Create(void) +{ + return INCHIGEN_Create( ); +} +INCHIGEN_HANDLE cdecl_STDINCHIGEN_Create(void) +{ + return STDINCHIGEN_Create( ); +} +int cdecl_INCHIGEN_Setup( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp) +{ + return INCHIGEN_Setup( HGen, pGenData, pInp ); +} +int cdecl_STDINCHIGEN_Setup( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp) +{ + return STDINCHIGEN_Setup( HGen, pGenData, pInp ); +} +int cdecl_INCHIGEN_DoNormalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ) +{ + return INCHIGEN_DoNormalization( HGen, pGenData ); +} +int cdecl_STDINCHIGEN_DoNormalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ) +{ + return STDINCHIGEN_DoNormalization( HGen, pGenData ); +} +int cdecl_INCHIGEN_DoCanonicalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ) +{ + return INCHIGEN_DoCanonicalization( HGen, pGenData ); +} +int cdecl_STDINCHIGEN_DoCanonicalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ) +{ + return STDINCHIGEN_DoCanonicalization( HGen, pGenData ); +} +int cdecl_INCHIGEN_DoSerialization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults ) +{ + return INCHIGEN_DoSerialization( HGen, pGenData, pResults ); +} +int cdecl_STDINCHIGEN_DoSerialization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults ) +{ + return STDINCHIGEN_DoSerialization( HGen, pGenData, pResults ); +} +void cdecl_INCHIGEN_Reset( INCHIGEN_HANDLE HGen , INCHIGEN_DATA *pGenData, inchi_Output *pResults) +{ + INCHIGEN_Reset( HGen, pGenData, pResults ); +} +void cdecl_STDINCHIGEN_Reset( INCHIGEN_HANDLE HGen , INCHIGEN_DATA *pGenData, inchi_Output *pResults) +{ + STDINCHIGEN_Reset( HGen, pGenData, pResults ); +} +void cdecl_INCHIGEN_Destroy( INCHIGEN_HANDLE HGen ) +{ + INCHIGEN_Destroy( HGen ); +} +void cdecl_STDINCHIGEN_Destroy( INCHIGEN_HANDLE HGen ) +{ + STDINCHIGEN_Destroy( HGen ); +} +#endif + + +#if( defined(__GNUC__) && __GNUC__ >= 3 && defined(__MINGW32__) && defined(_WIN32) ) +#include +/* + Pascal calling conventions export from Win32 dll +*/ + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + +/* Prototypes */ +INCHIGEN_HANDLE PASCAL pasc_INCHIGEN_Create(void); +INCHIGEN_HANDLE PASCAL pasc_STDINCHIGEN_Create(void); +int PASCAL pasc_INCHIGEN_Setup( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp ); +int PASCAL pasc_STDINCHIGEN_Setup( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp ); +int PASCAL pasc_INCHIGEN_DoNormalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ); +int PASCAL pasc_STDINCHIGEN_DoNormalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ); +int PASCAL pasc_INCHIGEN_DoCanonicalization( INCHIGEN_HANDLE _HGen, INCHIGEN_DATA *pGenData ); +int PASCAL pasc_STDINCHIGEN_DoCanonicalization( INCHIGEN_HANDLE _HGen, INCHIGEN_DATA *pGenData ); +int PASCAL pasc_INCHIGEN_DoSerialization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults ); +int PASCAL pasc_STDINCHIGEN_DoSerialization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults ); +void PASCAL pasc_INCHIGEN_Reset( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData, inchi_Output *pResults ); +void PASCAL pasc_STDINCHIGEN_Reset( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData, inchi_Output *pResults ); +void PASCAL pasc_INCHIGEN_Destroy(INCHIGEN_HANDLE HGen); +void PASCAL pasc_STDINCHIGEN_Destroy(INCHIGEN_HANDLE HGen); +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + +/* Implementation */ +/* libinchi.def provides export without PASCAL pasc_ prefixes */ + +INCHIGEN_HANDLE PASCAL pasc_INCHIGEN_Create(void) +{ + return INCHIGEN_Create( ); +} +INCHIGEN_HANDLE PASCAL pasc_STDINCHIGEN_Create(void) +{ + return STDINCHIGEN_Create( ); +} +int PASCAL pasc_INCHIGEN_Setup( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp) +{ + return INCHIGEN_Setup( HGen, pGenData, pInp); +} +int PASCAL pasc_STDINCHIGEN_Setup( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Input * pInp) +{ + return STDINCHIGEN_Setup( HGen, pGenData, pInp); +} +int PASCAL pasc_INCHIGEN_DoNormalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ) +{ + return INCHIGEN_DoNormalization( HGen, pGenData ); +} +int PASCAL pasc_STDINCHIGEN_DoNormalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ) +{ + return STDINCHIGEN_DoNormalization( HGen, pGenData ); +} +int PASCAL pasc_INCHIGEN_DoCanonicalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ) +{ + return INCHIGEN_DoCanonicalization( HGen, pGenData ); +} +int PASCAL pasc_STDINCHIGEN_DoCanonicalization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData ) +{ + return STDINCHIGEN_DoCanonicalization( HGen, pGenData ); +} +int PASCAL pasc_INCHIGEN_DoSerialization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults ) +{ + return INCHIGEN_DoSerialization( HGen, pGenData, pResults ); +} +int PASCAL pasc_STDINCHIGEN_DoSerialization( INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData, inchi_Output * pResults ) +{ + return STDINCHIGEN_DoSerialization( HGen, pGenData, pResults ); +} +void PASCAL pasc_INCHIGEN_Reset( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData, inchi_Output *pResults) +{ + INCHIGEN_Reset( HGen, pGenData, pResults ); +} +void PASCAL pasc_STDINCHIGEN_Reset( INCHIGEN_HANDLE HGen, INCHIGEN_DATA *pGenData, inchi_Output *pResults) +{ + STDINCHIGEN_Reset( HGen, pGenData, pResults ); +} +void PASCAL pasc_INCHIGEN_Destroy(INCHIGEN_HANDLE HGen) +{ + INCHIGEN_Destroy( HGen); +} +void PASCAL pasc_STDINCHIGEN_Destroy(INCHIGEN_HANDLE HGen) +{ + STDINCHIGEN_Destroy( HGen); +} + +#endif diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/inchi_dll_a.h b/INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll_a.h similarity index 63% rename from INCHI-1-SRC/INCHI_API/inchi_dll/inchi_dll_a.h rename to INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll_a.h index 861ef8f..99a52ac 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/inchi_dll_a.h +++ b/INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll_a.h @@ -1,188 +1,178 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHI_DLL_A_H__ -#define __INCHI_DLL_A_H__ - - - - -/*^^^ */ - - - - - -#include "ichicant.h" - -typedef struct tagCOMPONENT_TREAT_INFO -{ - int n1; - int n2; - int num_atoms; - int num_at_tg; - int num_deleted_H; - int num_deleted_H_taut; - INCHI_MODE nMode; - T_GROUP_INFO vt_group_info; - T_GROUP_INFO vt_group_info_orig; - ATOM_SIZES s[TAUT_NUM]; - BCN Bcn; - int bHasIsotopicAtoms; - int bMayHaveStereo; - int num_taut_at; - - int bPointedEdgeStereo; - int vABParityUnknown; /* actual value of constant for unknown parity (2009-12-10 ) */ - - INCHI_MODE bTautFlags; - INCHI_MODE bTautFlagsDone; - INCHI_MODE nUserMode; - - sp_ATOM *at[TAUT_NUM]; - inp_ATOM *out_at; - int fix_isofixedh; /*^^^ 04-12-2008 */ - int fix_termhchrg; /*^^^ 07-06-2008 */ - -} COMPONENT_TREAT_INFO; - -typedef struct tagINCHIGEN_CONTROL -{ - - int init_passed; - int norm_passed; - int canon_passed; - - INPUT_PARMS InpParms; - - unsigned long ulTotalProcessingTime; - char szTitle[MAX_SDF_HEADER+MAX_SDF_VALUE+256]; - char *pStr; - long num_err; - long num_inp; - - - ORIG_STRUCT OrigStruct; - ORIG_ATOM_DATA OrigInpData; - - /*^^^ For the whole structure: */ - STRUCT_DATA StructData; - - /*^^^ For each member of pair disconnected/reconnected structures: */ - ORIG_ATOM_DATA PrepInpData[INCHI_NUM]; /*^^^ INCHI_NUM=2; 0 disconnected/original - 1 reconnected */ - INP_ATOM_DATA *InpCurAtData[INCHI_NUM]; - - INP_ATOM_DATA *InpNormAtData[INCHI_NUM]; - INP_ATOM_DATA *InpNormTautData[INCHI_NUM]; - - COMP_ATOM_DATA composite_norm_data[INCHI_NUM][TAUT_NUM+1]; - /*^^^ TAUT_NUM=2; 0 non-tautomeric - 1 tautomeric - 2 intermediate tautomeric */ - - NORM_CANON_FLAGS - ncFlags; - - /*^^^ For each connected component of structures: */ - PINChI2 *pINChI[INCHI_NUM]; - PINChI_Aux2 *pINChI_Aux[INCHI_NUM]; - - /*^^^ For each connected component of structures: */ - COMPONENT_TREAT_INFO - *cti[INCHI_NUM]; - - /*^^^ Placed at the end intentionally */ - INCHI_IOSTREAM inchi_file[3]; - -} INCHIGEN_CONTROL; - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Exported functions -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - -#if (defined( _WIN32 ) && defined( _MSC_VER ) && defined(BUILD_LINK_AS_DLL) ) - /* Win32 & MS VC ++, compile and link as a DLL */ - #ifdef _USRDLL - /* InChI library dll */ - #define INCHI_API __declspec(dllexport) - #define EXPIMP_TEMPLATE - #define INCHI_DECL __stdcall - #else - /* calling the InChI dll program */ - #define INCHI_API __declspec(dllimport) - #define EXPIMP_TEMPLATE extern - #define INCHI_DECL __stdcall - #endif -#else - /* create a statically linked InChI library or link to an executable */ - #define INCHI_API - #define EXPIMP_TEMPLATE - #define INCHI_DECL -#endif - - - -/* to compile all InChI code as a C++ code #define COMPILE_ALL_CPP */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - - - -/*^^^ Local functions */ -void make_norm_atoms_from_inp_atoms(INCHIGEN_DATA *gendata, INCHIGEN_CONTROL *genctl); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -#define PSTR_BUFFER_SIZE 64000 -#define INCHI_MAX_NUM_ARG 32 - -#endif /* __INCHI_DLL_A_H__ */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef __INCHI_DLL_A_H__ +#define __INCHI_DLL_A_H__ + +#include "../../../INCHI_BASE/src/ichicant.h" + +typedef struct tagCOMPONENT_TREAT_INFO +{ + int n1; + int n2; + int num_atoms; + int num_at_tg; + int num_deleted_H; + int num_deleted_H_taut; + INCHI_MODE nMode; + T_GROUP_INFO vt_group_info; + T_GROUP_INFO vt_group_info_orig; + ATOM_SIZES s[TAUT_NUM]; + BCN Bcn; + int bHasIsotopicAtoms; + int bMayHaveStereo; + int num_taut_at; + + int bPointedEdgeStereo; + int vABParityUnknown; /* actual value of constant for unknown parity (2009-12-10 ) */ + + INCHI_MODE bTautFlags; + INCHI_MODE bTautFlagsDone; + INCHI_MODE nUserMode; + + sp_ATOM *at[TAUT_NUM]; + inp_ATOM *out_at; + int fix_isofixedh; /* 04-12-2008 */ + int fix_termhchrg; /* 07-06-2008 */ +} COMPONENT_TREAT_INFO; + +typedef struct tagINCHIGEN_CONTROL +{ + + int init_passed; + int norm_passed; + int canon_passed; + + INPUT_PARMS InpParms; + + unsigned long ulTotalProcessingTime; + char szTitle[MAX_SDF_HEADER+MAX_SDF_VALUE+256]; + /* Expandable string buffer */ + INCHI_IOSTREAM_STRING strbuf_container; + /*char *pStr;*/ + long num_err; + long num_inp; + + + ORIG_STRUCT OrigStruct; + ORIG_ATOM_DATA OrigInpData; + + /* For the whole structure: */ + STRUCT_DATA StructData; + + /* For each member of pair disconnected/reconnected structures: */ + ORIG_ATOM_DATA PrepInpData[INCHI_NUM]; /* INCHI_NUM=2; 0 disconnected/original + 1 reconnected */ + INP_ATOM_DATA *InpCurAtData[INCHI_NUM]; + + INP_ATOM_DATA *InpNormAtData[INCHI_NUM]; + INP_ATOM_DATA *InpNormTautData[INCHI_NUM]; + + COMP_ATOM_DATA composite_norm_data[INCHI_NUM][TAUT_NUM+1]; + /* TAUT_NUM=2; 0 non-tautomeric + 1 tautomeric + 2 intermediate tautomeric */ + + NORM_CANON_FLAGS + ncFlags; + + /* For each connected component of structures: */ + PINChI2 *pINChI[INCHI_NUM]; + PINChI_Aux2 *pINChI_Aux[INCHI_NUM]; + + /* For each connected component of structures: */ + COMPONENT_TREAT_INFO + *cti[INCHI_NUM]; + + /* Placed at the end intentionally */ + INCHI_IOSTREAM inchi_file[3]; +} INCHIGEN_CONTROL; + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Exported functions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ + + +#if (defined( _WIN32 ) && defined( _MSC_VER ) && defined(BUILD_LINK_AS_DLL) ) + /* Win32 & MS VC ++, compile and link as a DLL */ + #ifdef _USRDLL + /* InChI library dll */ + #define INCHI_API __declspec(dllexport) + #define EXPIMP_TEMPLATE + #define INCHI_DECL + #else + /* calling the InChI dll program */ + #define INCHI_API __declspec(dllimport) + #define EXPIMP_TEMPLATE extern + #define INCHI_DECL + #endif +#else + /* create a statically linked InChI library or link to an executable */ + #define INCHI_API + #define EXPIMP_TEMPLATE + #define INCHI_DECL +#endif + + + +/* to compile all InChI code as a C++ code #define COMPILE_ALL_CPP */ +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + + + + +/* Local functions */ +void make_norm_atoms_from_inp_atoms(INCHIGEN_DATA *gendata, INCHIGEN_CONTROL *genctl); + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + +#define PSTR_BUFFER_SIZE 511999 + + +#endif /* __INCHI_DLL_A_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/inchi_dll_a2.c b/INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll_a2.c similarity index 78% rename from INCHI-1-SRC/INCHI_API/inchi_dll/inchi_dll_a2.c rename to INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll_a2.c index 2f9ca42..100d4fe 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/inchi_dll_a2.c +++ b/INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll_a2.c @@ -1,2586 +1,2665 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include -/* #include */ -#include -#include - - -/*^^^ */ - -#include "mode.h" - -#include "ichitime.h" - - -#include "inpdef.h" -#include "ichi.h" -#include "strutil.h" -#include "util.h" -#include "ichidrp.h" -#include "ichierr.h" -#include "ichimain.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichi_io.h" - -#include "ichinorm.h" -#include "ichicant.h" -#include "ichicano.h" -#include "ichicomn.h" -#include "ichimake.h" -#include "ichister.h" -/*^^^ */ - - -#ifdef INCHI_LIB -#include "ichi_lib.h" -#endif - -#include "ichicomp.h" - -#if( ADD_CMLPP == 1 ) -#include "readcml.hpp" -#include "debug.h" -#endif -/*^^^ */ - -/* for DisplayTheWholeStructure() */ - -#define COMP_ORIG_0_MAIN 0x0001 -#define COMP_ORIG_0_RECN 0x0002 -#define COMP_PREP_0_MAIN 0x0004 -#define COMP_PREP_0_RECN 0x0008 -#define COMP_ORIG_1_MAIN 0x0010 -#define COMP_ORIG_1_RECN 0x0020 - -#include "ichisize.h" -#include "mode.h" -#include "inchi_api.h" -#include "inchi_dll_a.h" /* not inchi_api.h as it hides internal data types */ - - -int GetProcessingWarnings(INChI *cur_INChI[], INP_ATOM_DATA **inp_norm_data, STRUCT_DATA *sd); - -/*^^^ */ - - -int inp2spATOM( inp_ATOM *inp_at, int num_inp_at, sp_ATOM *at ); -int CheckCanonNumberingCorrectness( - int num_atoms, int num_at_tg, - sp_ATOM *at, CANON_STAT *pCS, int bTautomeric, - char *pStrErrStruct ); - -int CreateCompositeNormAtom( COMP_ATOM_DATA *composite_norm_data, - INP_ATOM_DATA2 *all_inp_norm_data, - int num_components); - -int CopyLinearCTStereoToINChIStereo( INChI_Stereo *Stereo, - AT_STEREO_CARB *LinearCTStereoCarb, int nLenLinearCTStereoCarb, - AT_STEREO_DBLE *LinearCTStereoDble, int nLenLinearCTStereoDble - , AT_NUMB *pCanonOrd, AT_RANK *pCanonRank, sp_ATOM *at, int bIsotopic - , AT_STEREO_CARB *LinearCTStereoCarbInv - , AT_STEREO_DBLE *LinearCTStereoDbleInv - , AT_NUMB *pCanonOrdInv, AT_RANK *pCanonRankInv ); -int MarkAmbiguousStereo( sp_ATOM *at, inp_ATOM *norm_at, int bIsotopic, AT_NUMB *pCanonOrd, - AT_STEREO_CARB *LinearCTStereoCarb, int nLenLinearCTStereoCarb, - AT_STEREO_DBLE *LinearCTStereoDble, int nLenLinearCTStereoDble ); - -INCHI_MODE UnmarkAllUndefinedUnknownStereo( INChI_Stereo *Stereo, INCHI_MODE nUserMode ); -int FillOutINChIReducedWarn( INChI *pINChI, INChI_Aux *pINChI_Aux, - int num_atoms, int num_at_tg, int num_removed_H, - sp_ATOM *at, inp_ATOM *norm_at, CANON_STAT *pCS, int bTautomeric, - INCHI_MODE nUserMode, char *pStrErrStruct ); - -int NormOneStructureINChI(INCHIGEN_DATA *gendata, INCHIGEN_CONTROL *genctl, - int iINChI, INCHI_IOSTREAM *inp_file); -int CanonOneStructureINChI(INCHIGEN_CONTROL *genctl, int iINChI, INCHI_IOSTREAM *inp_file); - -int NormOneComponentINChI(INCHIGEN_CONTROL *genctl, int iINChI, int i); -int CanonOneComponentINChI(INCHIGEN_CONTROL *genctl, int iINChI, int i); -int Normalization_step( INChI **ppINChI, INChI_Aux **ppINChI_Aux, - inp_ATOM *inp_at, INP_ATOM_DATA *out_norm_data[2], - int num_inp_at, - INCHI_MODE *pbTautFlags, - INCHI_MODE *pbTautFlagsDone, - COMPONENT_TREAT_INFO *cti); -int Canonicalization_step( INChI **ppINChI, INChI_Aux **ppINChI_Aux, - INP_ATOM_DATA *out_norm_data[2], - struct tagInchiTime *ulMaxTime, - T_GROUP_INFO *ti_out, - char *pStrErrStruct, - COMPONENT_TREAT_INFO *cti); - - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -int NormOneStructureINChI(INCHIGEN_DATA *gendata, - INCHIGEN_CONTROL *genctl, - int iINChI, - INCHI_IOSTREAM *inp_file) -{ -int k, i, j, nRet = 0; -int nStrLen = PSTR_BUFFER_SIZE; - - -STRUCT_DATA *sd = &(genctl->StructData); - -INPUT_PARMS *ip = &(genctl->InpParms); -ORIG_ATOM_DATA *prep_inp_data = &(genctl->PrepInpData[0]); -ORIG_ATOM_DATA *orig_inp_data = &(genctl->OrigInpData); -INCHI_IOSTREAM *output_file = genctl->inchi_file, *log_file = genctl->inchi_file+1; -INCHI_IOSTREAM prbstr, *prb_file=&prbstr; - -PINChI2 **pINChI2 = genctl->pINChI; -PINChI_Aux2 **pINChI_Aux2 = genctl->pINChI_Aux; -NORM_CANON_FLAGS *pncFlags = &(genctl->ncFlags); - - -INP_ATOM_DATA *inp_cur_data = NULL; - -long num_inp = genctl->num_inp; -char *pStr = genctl->pStr; - -INP_ATOM_DATA *inp_norm_data[TAUT_NUM]; /* = { &InpNormAtData, &InpNormTautData }; */ -ORIG_ATOM_DATA *cur_prep_inp_data = prep_inp_data + iINChI; - -inchiTime ulTStart; - - /*^^^ To save intermediate data... */ - - COMP_ATOM_DATA *composite_norm_data = genctl->composite_norm_data[iINChI]; - INP_ATOM_DATA2 *all_inp_norm_data = NULL; - memset( composite_norm_data+TAUT_NON, 0, sizeof( composite_norm_data[0] ) ); - memset( composite_norm_data+TAUT_YES, 0, sizeof( composite_norm_data[0] ) ); - memset( composite_norm_data+TAUT_INI, 0, sizeof( composite_norm_data[0] ) ); - - inchi_ios_init(prb_file, INCHI_IOSTREAM_FILE, NULL); - - /* - if ( orig_inp_data is NOT empty AND - prep_inp_data[0] IS empty ) then: - - 1. copy orig_inp_data --> prep_inp_data[0] - 2. fix odd things in prep_inp_data[0] - 3. if( orig_inp_data->bDisconnectSalts ) then - -- disconnect salts in prep_inp_data[0] - 4. move protons to neutralize charges on heteroatoms - 5. if( orig_inp_data->bDisconnectCoord ) then - -- copy prep_inp_data[0] --> prep_inp_data[1] - -- disconnect metals in prep_inp_data[0] - - [ This all is done in PreprocessOneStructure() ] - - iINChI = 0 - ========= - (normal/disconnected layer) - - 1. normalize prep_inp_data[0] in inp_norm_data[0,1] - 2. create INChI[ iINChI ] out of inp_norm_data[0,1] - - - iINChI = 1 AND orig_inp_data->bDisconnectCoord > 0 - ================================================= - (reconnected layer) - - 1. normalize prep_inp_data[1] in inp_norm_data[0,1] - 2. create INChI[ iINChI ] out of inp_norm_data[0,1] - - */ - - - - ip->msec_LeftTime = ip->msec_MaxTime; /* start timeout countdown for each component */ - - - - - if ( ip->bAllowEmptyStructure && !orig_inp_data->at && !orig_inp_data->num_inp_atoms ) { - ; - } else - if ( !orig_inp_data->at || !orig_inp_data->num_inp_atoms ) { - return 0; /* nothing to do */ - } - if ( iINChI == 1 && orig_inp_data->bDisconnectCoord <= 0 ) { - return 0; - } - - /* m = iINChI; */ /* orig_inp_data index */ - - if ( iINChI != INCHI_BAS && iINChI != INCHI_REC ) { - AddMOLfileError(sd->pStrErrStruct, "Fatal undetermined program error"); - sd->nStructReadError = 97; - nRet = sd->nErrorType = _IS_FATAL; - goto exit_function; - } - - /******************************************************************* - * * - * * - * Whole structure preprocessing: 1st step of the normalization * - * * - * Happen only on the first call to CreateOneStructureINChI() * - * * - * * - *******************************************************************/ - - if ( (!prep_inp_data->at || !prep_inp_data->num_inp_atoms) && orig_inp_data->num_inp_atoms > 0 ) - { - /* the structure has not been preprocessed */ - if ( ip->msec_MaxTime ) - { - InchiTimeGet( &ulTStart ); - } - - PreprocessOneStructure( sd, ip, orig_inp_data, prep_inp_data ); - pncFlags->bTautFlags[iINChI][TAUT_YES] = - pncFlags->bTautFlags[iINChI][TAUT_NON] = sd->bTautFlags[INCHI_BAS] | ip->bTautFlags; - pncFlags->bTautFlagsDone[iINChI][TAUT_YES] = - pncFlags->bTautFlagsDone[iINChI][TAUT_NON] = sd->bTautFlagsDone[INCHI_BAS] | ip->bTautFlagsDone; - - switch (sd->nErrorType) - { - case _IS_ERROR: - case _IS_FATAL: - /* error message */ - nRet = TreatReadTheStructureErrors( sd, ip, LOG_MASK_ALL, inp_file, log_file, output_file, prb_file, - prep_inp_data, &num_inp, pStr, nStrLen ); - goto exit_function; - } - } - - /*^^^ To save intermediate data... */ - if ( prep_inp_data[iINChI].num_components > 1) - { - all_inp_norm_data = (INP_ATOM_DATA2 *)inchi_calloc( prep_inp_data[iINChI].num_components, sizeof(all_inp_norm_data[0])); - } - - - - /* allocate pINChI[iINChI] and pINChI_Aux2[iINChI] -- arrays of pointers to INChI and INChI_Aux */ - /* assign values to sd->num_components[] */ - - MYREALLOC2(PINChI2, PINChI_Aux2, pINChI2[iINChI], pINChI_Aux2[iINChI], sd->num_components[iINChI], cur_prep_inp_data->num_components, k); - if ( k ) - { - AddMOLfileError(sd->pStrErrStruct, "Cannot allocate output data. Terminating"); - sd->nStructReadError = 99; - sd->nErrorType = _IS_FATAL; - goto exit_function; - } - - - /*^^^ Allocate */ - - /*^^^ visible */ - gendata->NormAtomsNontaut[iINChI] = (NORM_ATOMS *)inchi_calloc( sd->num_components[iINChI], sizeof(NORM_ATOMS)); - gendata->NormAtomsTaut[iINChI] = (NORM_ATOMS *)inchi_calloc( sd->num_components[iINChI], sizeof(NORM_ATOMS)); - /*^^^ invisible */ - genctl->InpNormAtData[iINChI] = (INP_ATOM_DATA *)inchi_calloc( sd->num_components[iINChI], sizeof(INP_ATOM_DATA)); - genctl->InpNormTautData[iINChI] = (INP_ATOM_DATA *)inchi_calloc( sd->num_components[iINChI], sizeof(INP_ATOM_DATA)); - genctl->InpCurAtData[iINChI] = (INP_ATOM_DATA *)inchi_calloc( sd->num_components[iINChI], sizeof(INP_ATOM_DATA)); - genctl->cti[iINChI] = (COMPONENT_TREAT_INFO *)inchi_calloc( sd->num_components[iINChI], sizeof(COMPONENT_TREAT_INFO)); - memset (genctl->cti[iINChI], 0, sd->num_components[iINChI]*sizeof(COMPONENT_TREAT_INFO)); - - - - - /*^^^ Second normalization step - component by component */ - - - for ( i = 0, nRet = 0; !sd->bUserQuitComponent && i < cur_prep_inp_data->num_components; i ++ ) - { - - if (ip->msec_MaxTime) InchiTimeGet( &ulTStart ); - - inp_cur_data = &(genctl->InpCurAtData[iINChI][i]); - - /* a) allocate memory and extract current component */ - nRet = GetOneComponent( sd, ip, log_file, output_file, inp_cur_data, - cur_prep_inp_data, i, num_inp, pStr, nStrLen ); - - if (ip->msec_MaxTime) ip->msec_LeftTime -= InchiTimeElapsed( &ulTStart ); - - switch (nRet) - { - case _IS_ERROR: - case _IS_FATAL: - goto exit_cycle; - } - - - - /* c) Create the component's INChI ( copies ip->bTautFlags into sd->bTautFlags)*/ - - inp_norm_data[TAUT_NON] = &(genctl->InpNormAtData[iINChI][i]); - memset( inp_norm_data[TAUT_NON], 0, sizeof( *inp_norm_data[0] ) ); - inp_norm_data[TAUT_YES] = &(genctl->InpNormTautData[iINChI][i]); - memset( inp_norm_data[TAUT_YES], 0, sizeof( *inp_norm_data[0] ) ); - - - nRet = NormOneComponentINChI(genctl, iINChI, i); - - /*^^^ To save intermediate data... */ - if ( all_inp_norm_data ) - { - for ( j = 0; j < TAUT_NUM; j ++ ) - { - if ( inp_norm_data[j]->bExists ) - { - all_inp_norm_data[i][j] = *inp_norm_data[j]; - memset( inp_norm_data[j], 0, sizeof(*inp_norm_data[0]) ); - } - } - } - - - if (nRet) - { - nRet = TreatCreateOneComponentINChIError(sd, ip, cur_prep_inp_data, i, num_inp, - inp_file, log_file, output_file, prb_file,pStr, nStrLen ); - break; - } - } - - -exit_cycle: - - switch ( nRet ) - { - case _IS_FATAL: - case _IS_ERROR: break; - default: - - /*^^^ To save intermediate data... */ - if ( all_inp_norm_data ) - { - int res = CreateCompositeNormAtom( composite_norm_data, - all_inp_norm_data, - prep_inp_data[iINChI].num_components); - } - break; - } - - - /*^^^ When saving intermediate data - avoid memory leaks in case of error */ - if ( all_inp_norm_data ) - { - for ( i = 0; i < prep_inp_data[iINChI].num_components; i ++ ) - { - for ( k = 0; k < TAUT_NUM; k ++ ) - { - FreeInpAtomData( &all_inp_norm_data[i][k] ); - } - } - inchi_free( all_inp_norm_data ); - all_inp_norm_data = NULL; - } - - - - - - - -exit_function: - - return nRet; -} - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -int CanonOneStructureINChI(INCHIGEN_CONTROL *genctl, int iINChI, INCHI_IOSTREAM *inp_file) -{ -int i, /*m,*/ nRet = 0; - -STRUCT_DATA *sd = &(genctl->StructData); - -INPUT_PARMS *ip = &(genctl->InpParms); -INCHI_IOSTREAM *output_file = genctl->inchi_file, *log_file = genctl->inchi_file+1; -INCHI_IOSTREAM prbstr, *prb_file=&prbstr; - -ORIG_ATOM_DATA *prep_inp_data = &(genctl->PrepInpData[0]); -PINChI2 **pINChI2 = genctl->pINChI; -PINChI_Aux2 **pINChI_Aux2 = genctl->pINChI_Aux; - -long num_inp = genctl->num_inp; -char *pStr = genctl->pStr; -int nStrLen = PSTR_BUFFER_SIZE; - - - - INP_ATOM_DATA *inp_cur_data = NULL; - - INP_ATOM_DATA *inp_norm_data[TAUT_NUM]; /* = { &InpNormAtData, &InpNormTautData }; */ - - ORIG_ATOM_DATA *cur_prep_inp_data = prep_inp_data + iINChI; - - inchiTime ulTStart; - - inchi_ios_init(prb_file, INCHI_IOSTREAM_FILE, NULL); - - for (i = 0; i < TAUT_NUM; i ++) /* initialize in case no InChI to generate 2008-12-23 DT */ - inp_norm_data[i]=NULL; - - /**************************************************************************/ - /* */ - /* */ - /* M A I N C Y C L E: P R O C E S S C O M P O N E N T S */ - /* */ - /* */ - /* O N E B Y O N E */ - /* */ - /* */ - /**************************************************************************/ - - for ( i = 0, nRet = 0; !sd->bUserQuitComponent && i < cur_prep_inp_data->num_components; i ++ ) - { - - if ( ip->msec_MaxTime ) - InchiTimeGet( &ulTStart ); - - - - - /*****************************************************/ - /* a) allocate memory and extract current component */ - /*****************************************************/ - - inp_cur_data = &(genctl->InpCurAtData[iINChI][i]); - - - nRet = GetOneComponent( sd, ip, log_file, output_file, inp_cur_data, cur_prep_inp_data, - i, num_inp, pStr, nStrLen ); - - if ( ip->msec_MaxTime ) - ip->msec_LeftTime -= InchiTimeElapsed( &ulTStart ); - - switch ( nRet ) { case _IS_ERROR: case _IS_FATAL: goto exit_cycle; } - -#ifndef TARGET_API_LIB - /* console request: Display the component? */ - if ( ip->bDisplay && inp_file != stdin ) - { - if ( user_quit("Enter=Display Component, Esc=Stop ?", ip->ulDisplTime) ) - { - sd->bUserQuitComponent = 1; - break; - } - } -#endif - - - - - /*******************************************************************************/ - /* */ - /* C A N O N I C A L I Z A T I O N */ - /* */ - /* (both tautomeric and non-tautomeric if requested) */ - /* */ - /*******************************************************************************/ - /* c) Create the component's INChI ( copies ip->bTautFlags into sd->bTautFlags)*/ - /*******************************************************************************/ - - inp_norm_data[TAUT_NON] = &(genctl->InpNormAtData[iINChI][i]); - inp_norm_data[TAUT_YES] = &(genctl->InpNormTautData[iINChI][i]); - - nRet = CanonOneComponentINChI(genctl, iINChI, i); - - - - - if ( nRet ) - { - nRet = TreatCreateOneComponentINChIError(sd, ip, cur_prep_inp_data, i, num_inp, - inp_file, log_file, output_file, prb_file,pStr, nStrLen ); - break; - } - } - /**************************************************************************/ - /* */ - /* */ - /* E N D O F T H E M A I N C Y C L E P R O C E S S I N G */ - /* */ - /* C O M P O N E N T S O N E B Y O N E */ - /* */ - /* */ - /**************************************************************************/ - - -exit_cycle: - - switch ( nRet ) - { - case _IS_FATAL: - case _IS_ERROR: break; - default: - - break; - } - - - - - for (i = 0; i < TAUT_NUM; i ++) - FreeInpAtomData( inp_norm_data[i] ); - - - - return nRet; -} - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -int NormOneComponentINChI(INCHIGEN_CONTROL * genctl, int iINChI, int i) -{ -STRUCT_DATA *sd = &(genctl->StructData); -INPUT_PARMS *ip = &(genctl->InpParms); -PINChI2 **pINChI2 = genctl->pINChI; -PINChI_Aux2 **pINChI_Aux2 = genctl->pINChI_Aux; -NORM_CANON_FLAGS *pncFlags = &(genctl->ncFlags); - - inchiTime ulTStart, ulTEnd, *pulTEnd = NULL; - int k, num_at, ret = 0; - int bOrigCoord; - INCHI_MODE bTautFlags = ip->bTautFlags; - INCHI_MODE bTautFlagsDone = (ip->bTautFlagsDone | sd->bTautFlagsDone[INCHI_BAS]); - long lElapsedTime; - /* - PINChI2 *pINChI = pINChI2[iINChI]; - PINChI_Aux2 *pINChI_Aux = pINChI_Aux2[iINChI]; - */ - -PINChI2 *pINChI = NULL; -PINChI_Aux2 *pINChI_Aux = NULL; - -INChI *cur_INChI[TAUT_NUM]; -INChI_Aux *cur_INChI_Aux[TAUT_NUM]; - - -/* pINChI2[m=iINChI-1][j< prep_inp_data[m].num_components][TAUT_NON] */ - -INP_ATOM_DATA *inp_norm_data[TAUT_NUM]; /* = { &InpNormAtData, &InpNormTautData }; */ -INP_ATOM_DATA *inp_cur_data = NULL; - -COMPONENT_TREAT_INFO *cti = NULL; - - - - inp_cur_data = &(genctl->InpCurAtData[iINChI][i]); - - cti = &(genctl->cti[iINChI][i]); - - inp_norm_data[TAUT_NON] = &(genctl->InpNormAtData[iINChI][i]); - inp_norm_data[TAUT_YES] = &(genctl->InpNormTautData[iINChI][i]); - - - pINChI = pINChI2[iINChI]; - pINChI_Aux = pINChI_Aux2[iINChI]; - - - InchiTimeGet( &ulTStart ); - bOrigCoord = !(ip->bINChIOutputOptions & (INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO)); - - - for ( k = 0; k < TAUT_NUM; k ++ ) - { - cur_INChI[k] = pINChI[i][k]; - cur_INChI_Aux[k] = pINChI_Aux[i][k]; - } - - - /* allocate memory for non-tautimeric (k=0) and tautomeric (k=1) results */ - for ( k = 0; k < TAUT_NUM; k ++ ) - { - int nAllocMode = (k==TAUT_YES? REQ_MODE_TAUT:0) | - (bTautFlagsDone & ( TG_FLAG_FOUND_ISOTOPIC_H_DONE | - TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE ))? - (ip->nMode & REQ_MODE_ISO):0; - - if ( k==TAUT_NON && (ip->nMode & REQ_MODE_BASIC ) || - k==TAUT_YES && (ip->nMode & REQ_MODE_TAUT ) ) - { - /* alloc INChI and INChI_Aux */ - cur_INChI[k] = Alloc_INChI( inp_cur_data->at, inp_cur_data->num_at, &inp_cur_data->num_bonds, - &inp_cur_data->num_isotopic, nAllocMode ); - cur_INChI_Aux[k] = Alloc_INChI_Aux( inp_cur_data->num_at, - inp_cur_data->num_isotopic, nAllocMode, bOrigCoord ); - if ( cur_INChI_Aux[k] ) - cur_INChI_Aux[k]->bIsIsotopic = inp_cur_data->num_isotopic; - /* alloc memory for the output structure: non-tautomeric and tautomeric (for displaying) */ - CreateInpAtomData( inp_norm_data[k], inp_cur_data->num_at, k ); - } - else - FreeInpAtomData( inp_norm_data[k] ); - - } - - lElapsedTime = InchiTimeElapsed( &ulTStart ); - if ( ip->msec_MaxTime ) - ip->msec_LeftTime -= lElapsedTime; - sd->ulStructTime += lElapsedTime; - - - /****************************************************** - * - * Get one component canonical numberings, etc. - * - ******************************************************/ - - /* - * Create_INChI() return value: - * num_at <= 0: error code - * num_at > 0: number of atoms (excluding terminal hydrogen atoms) - * inp_norm_data[0] => non-tautomeric, inp_norm_data[1] => tautomeric - */ - InchiTimeGet( &ulTStart ); - if ( ip->msec_MaxTime ) - { - ulTEnd = ulTStart; - pulTEnd = &ulTEnd; - if ( ip->msec_LeftTime > 0 ) - InchiTimeAddMsec( pulTEnd, ip->msec_LeftTime ); - } - - - cti->nUserMode = ip->nMode; - - /* vABParityUnknown holds actual value of an internal constant signifying */ - /* unknown parity: either the same as for undefined parity (default==standard) */ - /* or a specific one (non-std; requested by SLUUD switch). */ - cti->vABParityUnknown = AB_PARITY_UNDF; - if ( 0 != ( ip->nMode & REQ_MODE_DIFF_UU_STEREO) ) - { - /* Make labels for unknown and undefined stereo different */ - cti->vABParityUnknown = AB_PARITY_UNKN; - } - - num_at = Normalization_step( cur_INChI, cur_INChI_Aux, inp_cur_data->at, - inp_norm_data, inp_cur_data->num_at, - &bTautFlags, &bTautFlagsDone, cti); - - SetConnectedComponentNumber( inp_cur_data->at, inp_cur_data->num_at, i+1 ); /* normalization alters structure component number */ - - for ( k = 0; k < TAUT_NUM; k ++ ) - { - if ( cur_INChI_Aux[k] && cur_INChI_Aux[k]->nNumberOfAtoms > 0 ) - { - pncFlags->bNormalizationFlags[iINChI][k] |= cur_INChI_Aux[k]->bNormalizationFlags; - pncFlags->bTautFlags[iINChI][k] |= cur_INChI_Aux[k]->bTautFlags; - pncFlags->bTautFlagsDone[iINChI][k] |= cur_INChI_Aux[k]->bTautFlagsDone; - pncFlags->nCanonFlags[iINChI][k] |= cur_INChI_Aux[k]->nCanonFlags; - } - } - - /* Detect errors */ - if ( num_at < 0 ) - sd->nErrorCode = num_at; - else if ( num_at == 0 ) - sd->nErrorCode = -1; - else if ( cur_INChI[TAUT_NON] && cur_INChI[TAUT_NON]->nErrorCode ) - /* non-tautomeric error */ - sd->nErrorCode = cur_INChI[TAUT_NON]->nErrorCode; - else if ( cur_INChI[TAUT_YES] && cur_INChI[TAUT_YES]->nErrorCode ) - /* tautomeric error */ - sd->nErrorCode = cur_INChI[TAUT_YES]->nErrorCode; - - /* detect and store stereo warnings */ - if ( !sd->nErrorCode ) - GetProcessingWarnings(cur_INChI, inp_norm_data, sd); - - - lElapsedTime = InchiTimeElapsed( &ulTStart ); - if ( ip->msec_MaxTime ) - ip->msec_LeftTime -= lElapsedTime; - - sd->ulStructTime += lElapsedTime; -#ifndef TARGET_API_LIB - /* Display the results */ - if ( ip->bDisplay ) - eat_keyboard_input(); -#endif - /* a) No matter what happened save the allocated INChI pointers */ - /* save the INChI of the current component */ - - InchiTimeGet( &ulTStart ); - for ( k = 0; k < TAUT_NUM; k ++ ) - { - pINChI[i][k] = cur_INChI[k]; - pINChI_Aux[i][k] = cur_INChI_Aux[k]; - - cur_INChI[k] = NULL; - cur_INChI_Aux[k] = NULL; - } - - /* b) Count one component structure and/or INChI results only if there was no error */ - /* Set inp_norm_data[j]->num_removed_H = number of removed explicit H */ - - if ( !sd->nErrorCode ) - { - - /* find where the current processed structure is located */ - int cur_is_in_non_taut = (pINChI[i][TAUT_NON] && pINChI[i][TAUT_NON]->nNumberOfAtoms>0); - int cur_is_in_taut = (pINChI[i][TAUT_YES] && pINChI[i][TAUT_YES]->nNumberOfAtoms>0); - int cur_is_non_taut = cur_is_in_non_taut && 0 == pINChI[i][TAUT_NON]->lenTautomer || - cur_is_in_taut && 0 == pINChI[i][TAUT_YES]->lenTautomer; - int cur_is_taut = cur_is_in_taut && 0 < pINChI[i][TAUT_YES]->lenTautomer; - - if ( cur_is_non_taut + cur_is_taut ) - { - /* count tautomeric and non-tautomeric components of the structures */ - int j1 = cur_is_in_non_taut? TAUT_NON:TAUT_YES; - int j2 = cur_is_in_taut? TAUT_YES:TAUT_NON; - int j; - sd->num_non_taut[iINChI] += cur_is_non_taut; - sd->num_taut[iINChI] += cur_is_taut; - for ( j = j1; j <= j2; j ++ ) - { - int bIsotopic = (pINChI[i][j]->nNumberOfIsotopicAtoms || - pINChI[i][j]->nNumberOfIsotopicTGroups || - pINChI[i][j]->nPossibleLocationsOfIsotopicH && pINChI[i][j]->nPossibleLocationsOfIsotopicH[0]>1); - if ( j == TAUT_YES ) { - bIsotopic |= (0 < pINChI_Aux[i][j]->nNumRemovedIsotopicH[0] + - pINChI_Aux[i][j]->nNumRemovedIsotopicH[1] + - pINChI_Aux[i][j]->nNumRemovedIsotopicH[2]); - } - inp_norm_data[j]->bExists = 1; /* j=0: non-taut exists, j=1: taut exists */ - inp_norm_data[j]->bHasIsotopicLayer = bIsotopic; - } - } - } - - if ( sd->nErrorCode==CT_OUT_OF_RAM || sd->nErrorCode==CT_USER_QUIT_ERR ) - ret = _IS_FATAL; - else if ( sd->nErrorCode ) - ret = _IS_ERROR; - - lElapsedTime = InchiTimeElapsed( &ulTStart ); - if ( ip->msec_MaxTime ) - ip->msec_LeftTime -= lElapsedTime; - - sd->ulStructTime += lElapsedTime; - return ret; -} - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -int CanonOneComponentINChI(INCHIGEN_CONTROL *genctl, int iINChI, int i) -{ -STRUCT_DATA *sd = &(genctl->StructData); -INPUT_PARMS *ip = &(genctl->InpParms); -PINChI2 **pINChI2 = genctl->pINChI; -PINChI_Aux2 **pINChI_Aux2 = genctl->pINChI_Aux; -NORM_CANON_FLAGS *pncFlags = &(genctl->ncFlags); - - inchiTime ulTStart, ulTEnd, *pulTEnd = NULL; - int k, num_at, ret = 0; - INChI *cur_INChI[TAUT_NUM]; - INChI_Aux *cur_INChI_Aux[TAUT_NUM]; - long lElapsedTime; - /* - PINChI2 *pINChI = pINChI2[iINChI]; - PINChI_Aux2 *pINChI_Aux = pINChI_Aux2[iINChI]; - */ - -PINChI2 *pINChI = NULL; -PINChI_Aux2 *pINChI_Aux = NULL; - -INP_ATOM_DATA *inp_norm_data[TAUT_NUM]; /* = { &InpNormAtData, &InpNormTautData }; */ -INP_ATOM_DATA *inp_cur_data = NULL; - -COMPONENT_TREAT_INFO *cti = NULL; - - - inp_cur_data = &(genctl->InpCurAtData[iINChI][i]); - - cti = &(genctl->cti[iINChI][i]); - - inp_norm_data[TAUT_NON] = &(genctl->InpNormAtData[iINChI][i]); - inp_norm_data[TAUT_YES] = &(genctl->InpNormTautData[iINChI][i]); - - - - pINChI = pINChI2[iINChI]; - pINChI_Aux = pINChI_Aux2[iINChI]; - - InchiTimeGet( &ulTStart ); - - for ( k = 0; k < TAUT_NUM; k ++ ) - { - cur_INChI[k] = pINChI[i][k]; - cur_INChI_Aux[k] = pINChI_Aux[i][k]; - } - - - - lElapsedTime = InchiTimeElapsed( &ulTStart ); - if ( ip->msec_MaxTime ) { - ip->msec_LeftTime -= lElapsedTime; - } - sd->ulStructTime += lElapsedTime; - - - /****************************************************** - * - * Get one component canonical numberings, etc. - * - ******************************************************/ - - /* - * Create_INChI() return value: - * num_at <= 0: error code - * num_at > 0: number of atoms (excluding terminal hydrogen atoms) - * inp_norm_data[0] => non-tautomeric, inp_norm_data[1] => tautomeric - */ - InchiTimeGet( &ulTStart ); - if ( ip->msec_MaxTime ) { - ulTEnd = ulTStart; - pulTEnd = &ulTEnd; - if ( ip->msec_LeftTime > 0 ) { - InchiTimeAddMsec( pulTEnd, ip->msec_LeftTime ); - } - } - num_at = Canonicalization_step(cur_INChI, cur_INChI_Aux, inp_norm_data, - pulTEnd, NULL, sd->pStrErrStruct, cti); - - num_at = cti->num_atoms; - - SetConnectedComponentNumber( inp_cur_data->at, inp_cur_data->num_at, i+1 ); /* normalization alters structure component number */ - for ( k = 0; k < TAUT_NUM; k ++ ) - { - if ( cur_INChI_Aux[k] && cur_INChI_Aux[k]->nNumberOfAtoms > 0 ) - { - pncFlags->bNormalizationFlags[iINChI][k] |= cur_INChI_Aux[k]->bNormalizationFlags; - pncFlags->bTautFlags[iINChI][k] |= cur_INChI_Aux[k]->bTautFlags; - pncFlags->bTautFlagsDone[iINChI][k] |= cur_INChI_Aux[k]->bTautFlagsDone; - pncFlags->nCanonFlags[iINChI][k] |= cur_INChI_Aux[k]->nCanonFlags; - } - } - - /* Detect errors */ - if ( num_at < 0 ) - sd->nErrorCode = num_at; - else if ( num_at == 0 ) - sd->nErrorCode = -1; - else if ( cur_INChI[TAUT_NON] && cur_INChI[TAUT_NON]->nErrorCode ) - /* non-tautomeric error */ - sd->nErrorCode = cur_INChI[TAUT_NON]->nErrorCode; - else if ( cur_INChI[TAUT_YES] && cur_INChI[TAUT_YES]->nErrorCode ) - /* tautomeric error */ - sd->nErrorCode = cur_INChI[TAUT_YES]->nErrorCode; - - /* detect and store stereo warnings */ - if ( !sd->nErrorCode ) - GetProcessingWarnings(cur_INChI, inp_norm_data, sd); - - - lElapsedTime = InchiTimeElapsed( &ulTStart ); - if ( ip->msec_MaxTime ) - ip->msec_LeftTime -= lElapsedTime; - - sd->ulStructTime += lElapsedTime; -#ifndef TARGET_API_LIB - /* Display the results */ - if ( ip->bDisplay ) - eat_keyboard_input(); -#endif - /* a) No matter what happened save the allocated INChI pointers */ - /* save the INChI of the current component */ - - InchiTimeGet( &ulTStart ); - for ( k = 0; k < TAUT_NUM; k ++ ) - { - pINChI[i][k] = cur_INChI[k]; - pINChI_Aux[i][k] = cur_INChI_Aux[k]; - - cur_INChI[k] = NULL; - cur_INChI_Aux[k] = NULL; - } - - /* b) Count one component structure and/or INChI results only if there was no error */ - /* Set inp_norm_data[j]->num_removed_H = number of removed explicit H */ - - if ( !sd->nErrorCode ) - { - - /* find where the current processed structure is located */ - int cur_is_in_non_taut = (pINChI[i][TAUT_NON] && pINChI[i][TAUT_NON]->nNumberOfAtoms>0); - int cur_is_in_taut = (pINChI[i][TAUT_YES] && pINChI[i][TAUT_YES]->nNumberOfAtoms>0); - int cur_is_non_taut = cur_is_in_non_taut && 0 == pINChI[i][TAUT_NON]->lenTautomer || - cur_is_in_taut && 0 == pINChI[i][TAUT_YES]->lenTautomer; - int cur_is_taut = cur_is_in_taut && 0 < pINChI[i][TAUT_YES]->lenTautomer; - - if ( cur_is_non_taut + cur_is_taut ) - { - /* count tautomeric and non-tautomeric components of the structures */ - int j1 = cur_is_in_non_taut? TAUT_NON:TAUT_YES; - int j2 = cur_is_in_taut? TAUT_YES:TAUT_NON; - int j; - sd->num_non_taut[iINChI] += cur_is_non_taut; - sd->num_taut[iINChI] += cur_is_taut; - for ( j = j1; j <= j2; j ++ ) - { - int bIsotopic = (pINChI[i][j]->nNumberOfIsotopicAtoms || - pINChI[i][j]->nNumberOfIsotopicTGroups || - pINChI[i][j]->nPossibleLocationsOfIsotopicH && pINChI[i][j]->nPossibleLocationsOfIsotopicH[0]>1); - if ( j == TAUT_YES ) { - bIsotopic |= (0 < pINChI_Aux[i][j]->nNumRemovedIsotopicH[0] + - pINChI_Aux[i][j]->nNumRemovedIsotopicH[1] + - pINChI_Aux[i][j]->nNumRemovedIsotopicH[2]); - } - inp_norm_data[j]->bExists = 1; /* j=0: non-taut exists, j=1: taut exists */ - inp_norm_data[j]->bHasIsotopicLayer = bIsotopic; - } - } - } - - if ( sd->nErrorCode==CT_OUT_OF_RAM || sd->nErrorCode==CT_USER_QUIT_ERR ) - ret = _IS_FATAL; - else if ( sd->nErrorCode ) - ret = _IS_ERROR; - - lElapsedTime = InchiTimeElapsed( &ulTStart ); - if ( ip->msec_MaxTime ) - ip->msec_LeftTime -= lElapsedTime; - - sd->ulStructTime += lElapsedTime; - return ret; -} - - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -int Normalization_step( INChI **ppINChI, - INChI_Aux **ppINChI_Aux, - inp_ATOM *inp_at, - INP_ATOM_DATA *out_norm_data[2], - int num_inp_at, - INCHI_MODE *pbTautFlags, - INCHI_MODE *pbTautFlagsDone, - COMPONENT_TREAT_INFO *z) -{ - -int i,ret=0; - - - -T_GROUP_INFO * /*const*/ t_group_info = &(z->vt_group_info); -T_GROUP_INFO * /*const*/ t_group_info_orig = &(z->vt_group_info_orig); - - - BCN *pBCN = &(z->Bcn); - - /*^^^ */ - z->fix_isofixedh = 0; - z->fix_termhchrg = 0; - /*^^^ */ - #if( FIX_ISO_FIXEDH_BUG == 1 ) - if (TG_FLAG_FIX_ISO_FIXEDH_BUG & *pbTautFlags) - z->fix_isofixedh = 1; - #endif -#if( FIX_TERM_H_CHRG_BUG == 1 ) - if (TG_FLAG_FIX_TERM_H_CHRG_BUG & *pbTautFlags) - z->fix_termhchrg = 1; - #endif - - - z->bPointedEdgeStereo = ((TG_FLAG_POINTED_EDGE_STEREO & *pbTautFlags)? PES_BIT_POINT_EDGE_STEREO:0) - | ((TG_FLAG_PHOSPHINE_STEREO & *pbTautFlags)? PES_BIT_PHOSPHINE_STEREO :0) - | ((TG_FLAG_ARSINE_STEREO & *pbTautFlags)? PES_BIT_ARSINE_STEREO :0) - | ((TG_FLAG_FIX_SP3_BUG & *pbTautFlags)? PES_BIT_FIX_SP3_BUG :0); - z->bTautFlags = (*pbTautFlags & (~(INCHI_MODE)TG_FLAG_ALL_TAUTOMERIC) ); - z->bTautFlagsDone = (*pbTautFlagsDone /*& (~(INCHI_MODE)TG_FLAG_ALL_TAUTOMERIC) */); - - z->out_at = NULL; /*, *norm_at_fixed_bonds[TAUT_NUM]; */ /* = {out_norm_nontaut_at, out_norm_taut_at} ; */ - - /* Init: internal structs */ - - memset( z->s, 0, sizeof(z->s) ); - - if ( pBCN ) memset( pBCN, 0, sizeof( pBCN[0] ) ); - - memset( t_group_info, 0, sizeof(*t_group_info) ); - memset( t_group_info_orig, 0, sizeof(*t_group_info_orig) ); - - - /* Allocate: at[] */ - - for ( i = 0; i < TAUT_NUM; i ++ ) - { - if ( out_norm_data[i]->at ) - { - z->at[i] = - (sp_ATOM *) inchi_malloc( num_inp_at * sizeof(*(z->at[0])) ); - - if ( !z->at[i] ) ret = -1; - } - else - z->at[i] = NULL; - - } - - if ( !out_norm_data[TAUT_NON]->at && !out_norm_data[TAUT_YES]->at || !inp_at || ret ) - { - ret = -1; - goto exit_function; - } - - /* the first struct to process: tautomeric if exists else non-tautomeric */ - - z->out_at = out_norm_data[TAUT_YES]->at? out_norm_data[TAUT_YES]->at : out_norm_data[TAUT_NON]->at; - - /* copy the input structure to be normalized to the buffer for the normalization data */ - - memcpy( z->out_at, inp_at, num_inp_at*sizeof(z->out_at[0]) ); - - - /* tautomeric groups setting */ - - t_group_info->bIgnoreIsotopic = 0; /* include tautomeric group isotopic info in MarkTautomerGroups() */ - t_group_info->bTautFlags = *pbTautFlags; - t_group_info->bTautFlagsDone = *pbTautFlagsDone; - - - /* Preprocess the structure; here THE NUMBER OF ATOMS MAY BE REDUCED */ - /* ??? Ambiguity: H-D may become HD or DH (that is, H+implicit D or D+implicit H) */ - - if ( TG_FLAG_H_ALREADY_REMOVED & z->bTautFlags ) - { - INP_ATOM_DATA *out_norm_data1 = out_norm_data[TAUT_YES]->at? out_norm_data[TAUT_YES] : - out_norm_data[TAUT_NON]->at? out_norm_data[TAUT_NON] : NULL; - if ( out_norm_data1 ) - { - z->num_at_tg = - z->num_atoms = out_norm_data1->num_at - out_norm_data1->num_removed_H; - z->num_deleted_H = out_norm_data1->num_removed_H; - t_group_info->tni.nNumRemovedExplicitH = z->num_deleted_H; - } - else - { - ret = -1; - goto exit_function; - } - } - else - { - z->num_at_tg = z->num_atoms = remove_terminal_HDT( num_inp_at, z->out_at, z->fix_termhchrg); - - z->num_deleted_H = num_inp_at - z->num_atoms; - t_group_info->tni.nNumRemovedExplicitH = z->num_deleted_H; - - add_DT_to_num_H( z->num_atoms, z->out_at ); - } - - - - /* fix_odd_things( z->num_atoms, z->out_at );*/ - - -#if( FIND_RING_SYSTEMS == 1 ) - MarkRingSystemsInp( z->out_at, z->num_atoms, 0 ); -#endif - - /* duplicate the preprocessed structure so that all supplied out_norm_data[]->at buffers are filled */ - if ( z->out_at != out_norm_data[TAUT_YES]->at && out_norm_data[TAUT_YES]->at ) - memcpy( out_norm_data[TAUT_YES]->at, z->out_at, num_inp_at*sizeof(z->out_at[0]) ); - - if ( out_norm_data[TAUT_YES]->at_fixed_bonds && out_norm_data[TAUT_YES]->at ) - memcpy( out_norm_data[TAUT_YES]->at_fixed_bonds, z->out_at, num_inp_at*sizeof(z->out_at[0]) ); - - if ( z->out_at != out_norm_data[TAUT_NON]->at && out_norm_data[TAUT_NON]->at ) - memcpy( out_norm_data[TAUT_NON]->at, z->out_at, num_inp_at*sizeof(z->out_at[0]) ); - - - /******************************************************************************* - * ??? not true ??? duplicate inp_at and keep inp_at[] unchanged after terminal hydrogens removal - * set stereo parities in taut_at[], non_taut_at[] - * obtain max. lenghts of the name stereo parts - * Ignore absence/presence of isotopic stereo for now - * mark isotopic atoms - *******************************************************************************/ - - if ( out_norm_data[TAUT_YES]->at && z->at[TAUT_YES] ) - { - - /* final normalization of possibly tautomeric structure */ - - ret = - mark_alt_bonds_and_taut_groups ( out_norm_data[TAUT_YES]->at, - out_norm_data[TAUT_YES]->at_fixed_bonds, - z->num_atoms, - t_group_info, - NULL, NULL ); - - if ( ret < 0 ) - goto exit_function;/* out of RAM or other normalization problem */ - - z->num_taut_at = ret; /* number of atoms without removed H? */ - z->num_deleted_H_taut = t_group_info->tni.nNumRemovedExplicitH; - out_norm_data[TAUT_YES]->num_at = z->num_atoms + z->num_deleted_H_taut; /* protons might have been removed */ - out_norm_data[TAUT_YES]->num_removed_H = z->num_deleted_H_taut; - out_norm_data[TAUT_YES]->nNumRemovedProtons += t_group_info->tni.nNumRemovedProtons; - - for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) - { - out_norm_data[TAUT_YES]->nNumRemovedProtonsIsotopic[i] += t_group_info->tni.nNumRemovedProtonsIsotopic[i] /*+ t_group_info->num_iso_H[i]*/; - out_norm_data[TAUT_YES]->num_iso_H[i] += t_group_info->num_iso_H[i]; - } - - /* mark deleted isolated tautomeric H(+) */ - - if ( z->num_taut_at == 1 && out_norm_data[TAUT_YES]->at[0].at_type == ATT_PROTON && - t_group_info && t_group_info->tni.nNumRemovedProtons == 1 ) - { - out_norm_data[TAUT_YES]->bDeleted = 1; - - FreeInpAtom( &out_norm_data[TAUT_YES]->at_fixed_bonds ); - } - else if ( (t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) && - out_norm_data[TAUT_YES]->at_fixed_bonds) - { - out_norm_data[TAUT_YES]->bTautPreprocessed = 1; - } - - out_norm_data[TAUT_YES]->bTautFlags = *pbTautFlags = t_group_info->bTautFlags; - out_norm_data[TAUT_YES]->bTautFlagsDone = *pbTautFlagsDone = t_group_info->bTautFlagsDone; - out_norm_data[TAUT_YES]->bNormalizationFlags = t_group_info->tni.bNormalizationFlags; - - /* create internal sp_ATOM at[] out of out_norm_data[]->at */ - - inp2spATOM( out_norm_data[TAUT_YES]->at, num_inp_at, z->at[TAUT_YES] ); - - - - /* set stereo parities to at[]; nUserMode: accept alt. stereo bonds, min ring size */ - - ret = - set_stereo_parity( out_norm_data[TAUT_YES]->at, z->at[TAUT_YES], z->num_taut_at, z->num_deleted_H_taut, - &(z->s[TAUT_YES].nMaxNumStereoAtoms), - &(z->s[TAUT_YES].nMaxNumStereoBonds), z->nUserMode, - z->bPointedEdgeStereo, z->vABParityUnknown ); - - if ( RETURNED_ERROR(ret) ) goto exit_function; /* stereo bond error */ - - - z->s[TAUT_YES].bMayHaveStereo = (z->s[TAUT_YES].nMaxNumStereoAtoms || z->s[TAUT_YES].nMaxNumStereoBonds); - - /* - * mark isotopic atoms and atoms that have non-tautomeric - * isotopic terminal hydrogen atoms 1H, 2H(D), 3H(T) - */ - - z->s[TAUT_YES].num_isotopic_atoms = - - set_atom_iso_sort_keys( z->num_taut_at, z->at[TAUT_YES], t_group_info, - &(z->s[TAUT_YES].bHasIsotopicTautGroups) ); - - - /************************************************************************** - * prepare tautomeric (if no tautomerism found then prepare non-tautomeric) - * structure for canonicalizaton: - ************************************************************************** - * remove t-groups that have no H, - * remove charges from t-groups if requested - * renumber t-groups and find final t_group_info->num_t_groups - * add to t-groups lists of endpoints tgroup->nEndpointAtomNumber[] - * calculate length of the t-group part of the connection table - **************************************************************************/ - - z->s[TAUT_YES].nLenLinearCTTautomer = - - CountTautomerGroups( z->at[TAUT_YES], z->num_taut_at, t_group_info ); - - - if ( RETURNED_ERROR(z->s[TAUT_YES].nLenLinearCTTautomer) ) - { - /* added error treatment 9-11-2003 */ - ret = z->s[TAUT_YES].nLenLinearCTTautomer; - goto exit_function; - /* error has happened; no breakpoint here - z->s[TAUT_YES].nLenLinearCTTautomer = 0; - */ - } - else if ( z->s[TAUT_YES].nLenLinearCTTautomer > 0 ) - { - z->num_at_tg = z->num_taut_at+t_group_info->num_t_groups; - - /* ??? -not true- create t_group_info_orig for multiple calls with atom renumbering */ - - make_a_copy_of_t_group_info( t_group_info_orig /* dest*/, t_group_info /* source*/ ); - - /* mark isotopic tautomer groups: calculate t_group->iWeight */ - z->s[TAUT_YES].nLenLinearCTIsotopicTautomer=set_tautomer_iso_sort_keys( t_group_info ); - if ( z->s[TAUT_YES].nLenLinearCTIsotopicTautomer < 0 ) - { - /* ??? -error cannot happen- error has happened; no breakpoint here */ - z->s[TAUT_YES].nLenLinearCTIsotopicTautomer = 0; - } - out_norm_data[TAUT_YES]->bTautomeric = z->s[TAUT_YES].nLenLinearCTTautomer; - } - - /* new variable: z->s[TAUT_YES].nLenCT introduced 7-22-2002 */ - - GetCanonLengths( z->num_taut_at, z->at[TAUT_YES], &(z->s[TAUT_YES]), t_group_info ); - - - } /* end of: final normalization of possibly tautomeric structure */ - - - - if ( out_norm_data[TAUT_NON]->at && out_norm_data[TAUT_YES]->at && z->at[TAUT_NON] && !z->s[TAUT_YES].nLenLinearCTTautomer ) - { - /* the structure is non-tautomeric: use tautomeric treatment results only for it */ - - inchi_free( z->at[TAUT_NON] ); - - z->at[TAUT_NON] = NULL; - } - - else if ( !out_norm_data[TAUT_NON]->at && out_norm_data[TAUT_YES]->at && - !z->at[TAUT_NON] && z->at[TAUT_YES] && !z->s[TAUT_YES].nLenLinearCTTautomer ) - { - /* requested tautomeric; found non-tautomeric; it is located in out_norm_data[TAUT_YES]->at */ - - out_norm_data[TAUT_YES]->bTautomeric = 0; - } - - else if ( out_norm_data[TAUT_NON]->at && z->at[TAUT_NON] ) - { - /* the structure needs non-tautomeric treatment: final normalization of non-tautomeric structure */ - - ret = - mark_alt_bonds_and_taut_groups (out_norm_data[TAUT_NON]->at, - NULL, - z->num_atoms, - NULL, - &(z->bTautFlags), &(z->bTautFlagsDone) ); - - if ( ret < 0 ) goto exit_function; /* out of RAM or other normalization problem */ - - out_norm_data[TAUT_NON]->num_at = z->num_atoms + z->num_deleted_H; - out_norm_data[TAUT_NON]->num_removed_H = z->num_deleted_H; - out_norm_data[TAUT_NON]->bTautFlags = *pbTautFlags; - out_norm_data[TAUT_NON]->bTautFlagsDone = *pbTautFlagsDone; - out_norm_data[TAUT_NON]->bNormalizationFlags = 0; - - /* create internal sp_ATOM at[] out of out_norm_data[]->at */ - - inp2spATOM( out_norm_data[TAUT_NON]->at, num_inp_at, z->at[TAUT_NON] ); - - /* set stereo parities to at[]; nUserMode: accept alt. stereo bonds, min ring size */ - - ret = - set_stereo_parity( out_norm_data[TAUT_NON]->at, - z->at[TAUT_NON], - z->num_atoms, z->num_deleted_H, - &(z->s[TAUT_NON].nMaxNumStereoAtoms), - &(z->s[TAUT_NON].nMaxNumStereoBonds), - z->nUserMode, - z->bPointedEdgeStereo, z->vABParityUnknown ); - - if ( RETURNED_ERROR( ret ) ) goto exit_function; /* stereo bond error */ - - - z->s[TAUT_NON].bMayHaveStereo = (z->s[TAUT_NON].nMaxNumStereoAtoms || z->s[TAUT_NON].nMaxNumStereoBonds); - - /* - * mark isotopic atoms and atoms that have non-tautomeric - * isotopic terminal hydrogen atoms 1H, 2H(D), 3H(T) - */ - - z->s[TAUT_NON].num_isotopic_atoms = - - set_atom_iso_sort_keys( z->num_atoms, z->at[TAUT_NON], NULL, NULL ); - - - GetCanonLengths( z->num_atoms, z->at[TAUT_NON], &(z->s[TAUT_NON]), NULL); - - - out_norm_data[TAUT_NON]->bTautomeric = 0; - - } /* the structure needs non-tautomeric treatment: final normalization of non-tautomeric structure */ - - - - - /**********************************************************/ - - - - - /* common */ - z->bMayHaveStereo = z->s[TAUT_YES].bMayHaveStereo || z->s[TAUT_NON].bMayHaveStereo; - z->bHasIsotopicAtoms = z->s[TAUT_NON].num_isotopic_atoms > 0 || z->s[TAUT_NON].bHasIsotopicTautGroups > 0 || - z->s[TAUT_YES].num_isotopic_atoms > 0 || z->s[TAUT_YES].bHasIsotopicTautGroups > 0 ; -/*^^^ */ - if (z->fix_isofixedh) /* 2008-03-21 DT */ - z->bHasIsotopicAtoms = z->bHasIsotopicAtoms || - z->s[TAUT_YES].nLenLinearCTTautomer > 0 && t_group_info && - (0 < NUM_H_ISOTOPES && t_group_info->tni.nNumRemovedProtonsIsotopic[0] || - 1 < NUM_H_ISOTOPES && t_group_info->tni.nNumRemovedProtonsIsotopic[1] || - 2 < NUM_H_ISOTOPES && t_group_info->tni.nNumRemovedProtonsIsotopic[2]) ; -/*^^^ */ - z->bHasIsotopicAtoms = z->bHasIsotopicAtoms || - z->s[TAUT_YES].nLenIsotopicEndpoints > 1 && t_group_info && - (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE|TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE)); - - - - - - - /* Set mode */ - - /* default mode */ - - if ( !(z->nUserMode & REQ_MODE_DEFAULT) ) - { - z->nUserMode |= REQ_MODE_DEFAULT; - } - - - /* adjust the mode to the reality */ - - if ( ( z->nUserMode & REQ_MODE_ISO ) && !z->bHasIsotopicAtoms ) - { - z->nUserMode ^= REQ_MODE_ISO; - z->nUserMode |= REQ_MODE_NON_ISO; /* at least one is needed */ - } - - if ( (z->nUserMode & REQ_MODE_STEREO) && ( z->nUserMode & REQ_MODE_ISO ) ) - { - z->nUserMode |= REQ_MODE_ISO_STEREO; - } - - if ( (z->nUserMode & REQ_MODE_STEREO) && !( z->nUserMode & REQ_MODE_NON_ISO ) ) - { - z->nUserMode ^= REQ_MODE_STEREO; - } - - if ( !z->bMayHaveStereo ) - { - if ( z->nUserMode & REQ_MODE_STEREO ) - z->nUserMode ^= REQ_MODE_STEREO; - if ( z->nUserMode & REQ_MODE_ISO_STEREO ) - z->nUserMode ^= REQ_MODE_ISO_STEREO; - } - - if ( (z->nUserMode & REQ_MODE_BASIC) && (!out_norm_data[TAUT_NON]->at || !ppINChI[TAUT_NON] || !ppINChI_Aux[TAUT_NON] || !z->at[TAUT_NON]) ) - { - z->nUserMode ^= REQ_MODE_BASIC; - } - if ( (z->nUserMode & REQ_MODE_TAUT) && (!out_norm_data[TAUT_YES]->at || !ppINChI[TAUT_YES] || !ppINChI_Aux[TAUT_YES] || !z->at[TAUT_YES]) ) - { - z->nUserMode ^= REQ_MODE_TAUT; - } - - - /* Set n1, n2 according to the mode */ - - switch ((int)z->nUserMode & (REQ_MODE_BASIC | REQ_MODE_TAUT)) - { - case REQ_MODE_BASIC: - z->n1 = TAUT_NON; - z->n2 = TAUT_NON; - break; - case REQ_MODE_TAUT: - z->n1 = TAUT_YES; - z->n2 = TAUT_YES; - break; - case (REQ_MODE_BASIC | REQ_MODE_TAUT): - z->n1 = TAUT_NON; - z->n2 = TAUT_YES; - break; - default: - /* program error: inconsistent nUserMode or missing taut/non-taut allocation */ /* */ - ret = -3; - goto exit_function; - } - - - if ( ret == 0 ) - ret = z->num_atoms; - - /* treat the results later */ - -exit_function: - - return ret; - -} - - - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -int Canonicalization_step( INChI **ppINChI, - INChI_Aux **ppINChI_Aux, - INP_ATOM_DATA *out_norm_data[2], - struct tagInchiTime *ulMaxTime, - T_GROUP_INFO *ti_out, - char *pStrErrStruct, - COMPONENT_TREAT_INFO *z) -{ -int i,ret=0, ret2=0; - - - -T_GROUP_INFO * /*const*/ t_group_info = &(z->vt_group_info); -T_GROUP_INFO * /*const*/ t_group_info_orig = &(z->vt_group_info_orig); - -CANON_STAT CS, CS2; -CANON_STAT *pCS = &CS; -CANON_STAT *pCS2 = &CS2; /* save all allocations to avoid memory leaks in case Canon_INChI() removes the pointer */ - - BCN *pBCN = &(z->Bcn); - - - INChI *pINChI=NULL; /* added initialization 2006-03 */ - INChI_Aux *pINChI_Aux=NULL; /* added initialization 2006-03 */ - - - - /************************************************************ - * * - * Obtain all non-stereo canonical numberings * - * * - ************************************************************/ - - if ( (z->nUserMode & REQ_MODE_NON_ISO) && !(z->nUserMode & REQ_MODE_ISO) ) - { - - /* added for special non-isotopic test mode 2004-10-04 */ - if ( t_group_info ) - { - t_group_info->bIgnoreIsotopic = 1; - if ( t_group_info->nIsotopicEndpointAtomNumber ) - { - t_group_info->nIsotopicEndpointAtomNumber[0] = inchi_min(1, t_group_info->nIsotopicEndpointAtomNumber[0]); - } - memset( t_group_info->num_iso_H, 0, sizeof(t_group_info->num_iso_H) ); - memset ( t_group_info->tni.nNumRemovedProtonsIsotopic, 0, sizeof(t_group_info->tni.nNumRemovedProtonsIsotopic)); - t_group_info->bTautFlagsDone &= ~(TG_FLAG_FOUND_ISOTOPIC_H_DONE|TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE); - } - - for ( i = 0; i < TAUT_NUM; i ++ ) - { - z->s[i].bHasIsotopicTautGroups = 0; - z->s[i].bIgnoreIsotopic = 1; - z->s[i].nLenIsotopic = 0; - z->s[i].nLenIsotopicEndpoints = 0; - z->s[i].nLenLinearCTIsotopicTautomer = 0; - z->s[i].num_isotopic_atoms = 0; - } - z->bHasIsotopicAtoms = 0; - } - - ret = GetBaseCanonRanking( z->num_atoms, z->num_at_tg, z->at, t_group_info, z->s, pBCN, ulMaxTime, z->fix_isofixedh ); - - if ( ret < 0 ) - goto exit_function; /* program error */ - - - /* added for special non-isotopic test mode 2004-10-04 */ - if ( !pBCN->ftcn[z->n1].PartitionCt.Rank ) - z->n1 = ALT_TAUT(z->n1); - - if ( !pBCN->ftcn[z->n2].PartitionCt.Rank ) - z->n2 = ALT_TAUT(z->n2); - - if ( z->n1 > z->n2 ) - { - ret = CT_TAUCOUNT_ERR; - goto exit_function; /* program error */ - } - - - /************************************************************ - * * - * Obtain stereo canonical numberings * - * * - ************************************************************/ - - for ( i = z->n2; i >= z->n1 && !RETURNED_ERROR( ret ); i -- ) - { - - - memset( pCS, 0, sizeof(*pCS) ); - - switch (i) - { - - case TAUT_NON: /* non-tautomeric */ - - z->nMode = 0; - z->nMode = (z->s[i].nLenLinearCTTautomer == 0)? CANON_MODE_CT:CANON_MODE_TAUT; - z->nMode |= (z->bHasIsotopicAtoms && (z->nUserMode & REQ_MODE_ISO))? CANON_MODE_ISO:0; - z->nMode |= (z->s[TAUT_NON].bMayHaveStereo && (z->nUserMode & REQ_MODE_STEREO) )? CANON_MODE_STEREO:0; - z->nMode |= (z->bHasIsotopicAtoms && z->s[TAUT_NON].bMayHaveStereo && (z->nUserMode & REQ_MODE_ISO_STEREO))? CANON_MODE_ISO_STEREO:0; - z->nMode |= (z->nUserMode & REQ_MODE_NOEQ_STEREO )? CMODE_NOEQ_STEREO : 0; - z->nMode |= (z->nUserMode & REQ_MODE_REDNDNT_STEREO)? CMODE_REDNDNT_STEREO : 0; - z->nMode |= (z->nUserMode & REQ_MODE_NO_ALT_SBONDS )? CMODE_NO_ALT_SBONDS : 0; - - /* 2010-01-12 */ - z->nMode |= (z->vABParityUnknown==AB_PARITY_UNDF)? 0 : REQ_MODE_DIFF_UU_STEREO; - - if ( (z->nMode & CANON_MODE_STEREO) == CANON_MODE_STEREO || - (z->nMode & CANON_MODE_ISO_STEREO) == CANON_MODE_ISO_STEREO ) - { - z->nMode |= (z->nUserMode & REQ_MODE_RELATIVE_STEREO)? CMODE_RELATIVE_STEREO: 0; - z->nMode |= (z->nUserMode & REQ_MODE_RACEMIC_STEREO )? CMODE_RACEMIC_STEREO : 0; - z->nMode |= (z->nUserMode & REQ_MODE_SC_IGN_ALL_UU )? CMODE_SC_IGN_ALL_UU : 0; - z->nMode |= (z->nUserMode & REQ_MODE_SB_IGN_ALL_UU )? CMODE_SB_IGN_ALL_UU : 0; - } - - if ( ret= AllocateCS( pCS, z->num_atoms, z->num_atoms, z->s[TAUT_NON].nLenCT, z->s[TAUT_NON].nLenCTAtOnly, - z->s[TAUT_NON].nLenLinearCTStereoDble, z->s[TAUT_NON].nMaxNumStereoBonds, - z->s[TAUT_NON].nLenLinearCTStereoCarb, z->s[TAUT_NON].nMaxNumStereoAtoms, - 0, 0, z->s[TAUT_NON].nLenIsotopic, z->nMode, pBCN ) ) - { - goto exit_function; - } - - *pCS2 = *pCS; - break; - - - case TAUT_YES: /* tautomeric */ - - z->nMode = 0; - z->nMode = (z->s[i].nLenLinearCTTautomer == 0)? CANON_MODE_CT:CANON_MODE_TAUT; - z->nMode |= (z->bHasIsotopicAtoms && (z->nUserMode & REQ_MODE_ISO) )? CANON_MODE_ISO:0; - z->nMode |= (z->s[TAUT_YES].bMayHaveStereo && (z->nUserMode & REQ_MODE_STEREO) )? CANON_MODE_STEREO:0; - z->nMode |= (z->bHasIsotopicAtoms && z->s[TAUT_YES].bMayHaveStereo && (z->nUserMode & REQ_MODE_ISO_STEREO))? CANON_MODE_ISO_STEREO:0; - z->nMode |= (z->nUserMode & REQ_MODE_NOEQ_STEREO )? CMODE_NOEQ_STEREO : 0; - z->nMode |= (z->nUserMode & REQ_MODE_REDNDNT_STEREO)? CMODE_REDNDNT_STEREO : 0; - z->nMode |= (z->nUserMode & REQ_MODE_NO_ALT_SBONDS )? CMODE_NO_ALT_SBONDS : 0; - - /* 2010-01-12 */ - z->nMode |= (z->vABParityUnknown==AB_PARITY_UNDF)? 0 : REQ_MODE_DIFF_UU_STEREO; - - if ( (z->nMode & CANON_MODE_STEREO) == CANON_MODE_STEREO || - (z->nMode & CANON_MODE_ISO_STEREO) == CANON_MODE_ISO_STEREO ) - { - z->nMode |= (z->nUserMode & REQ_MODE_RELATIVE_STEREO)? CMODE_RELATIVE_STEREO: 0; - z->nMode |= (z->nUserMode & REQ_MODE_RACEMIC_STEREO )? CMODE_RACEMIC_STEREO : 0; - z->nMode |= (z->nUserMode & REQ_MODE_SC_IGN_ALL_UU )? CMODE_SC_IGN_ALL_UU : 0; - z->nMode |= (z->nUserMode & REQ_MODE_SB_IGN_ALL_UU )? CMODE_SB_IGN_ALL_UU : 0; - } - - if ( ret= AllocateCS( pCS, z->num_atoms, z->num_at_tg, z->s[TAUT_YES].nLenCT, z->s[TAUT_YES].nLenCTAtOnly, - z->s[TAUT_YES].nLenLinearCTStereoDble, z->s[TAUT_YES].nMaxNumStereoBonds, - z->s[TAUT_YES].nLenLinearCTStereoCarb, z->s[TAUT_YES].nMaxNumStereoAtoms, - z->s[TAUT_YES].nLenLinearCTTautomer, z->s[TAUT_YES].nLenLinearCTIsotopicTautomer, - z->s[TAUT_YES].nLenIsotopic, z->nMode, pBCN ) ) - { - goto exit_function; - } - - *pCS2 = *pCS; - break; - - - } /* switch () */ - - - /* settings */ - pCS->lNumDecreasedCT = -1; - pCS->bDoubleBondSquare = DOUBLE_BOND_NEIGH_LIST? 2:0; /* 2 => special mode */ - pCS->bIgnoreIsotopic = !((z->s[TAUT_NON].num_isotopic_atoms || - z->s[TAUT_YES].num_isotopic_atoms || - z->s[TAUT_YES].bHasIsotopicTautGroups) || - (z->nUserMode & REQ_MODE_NON_ISO) || - !(z->nUserMode & REQ_MODE_ISO)); - - if ( (z->nUserMode & REQ_MODE_NON_ISO) && !(z->nUserMode & REQ_MODE_ISO) ) - pCS->bIgnoreIsotopic = 1; /* 10-04-2004 */ - - - if ( i == TAUT_YES ) - { - /* tautomeric */ - pCS->t_group_info = t_group_info; /* ??? make a copy or reuse ??? */ - pCS->t_group_info->bIgnoreIsotopic = !(z->s[TAUT_YES].bHasIsotopicTautGroups || - (z->nUserMode & REQ_MODE_NON_ISO) || - !(z->nUserMode & REQ_MODE_ISO)); - if ( (z->nUserMode & REQ_MODE_NON_ISO) && !(z->nUserMode & REQ_MODE_ISO) ) - pCS->t_group_info->bIgnoreIsotopic = 1; /* 10-04-2004 */ - - } - pCS->ulTimeOutTime = pBCN->ulTimeOutTime; - /*=========== Obsolete Mode Bits (bit 0 is Least Significant Bit) =========== - * - * Mode Bits Description - * '0' c 0 Only one connection table canonicalization - * '1' C 1 Recalculate CT using fixed nSymmRank - * '2' i 1|2 Isotopic canonicalization (internal) - * '3' I 1|2|4 Isotopic canonicalization (output) - * '4' s 1|8 Stereo canonicalization - * '5' S 1|2|4|16 Stereo isotopic canonicalization - * '6' A 1|2|4|8|16 Output All - */ - - /*************************************** - The last canonicalization step - ***************************************/ - if ( pBCN ) - { - /* USE_CANON2 == 1 */ - pCS->NeighList = NULL; - pCS->pBCN = pBCN; - ret = Canon_INChI( z->num_atoms, i?z->num_at_tg:z->num_atoms, - z->at[i], pCS, z->nMode, i); - } - else - { - /* old way */ - pCS->NeighList = CreateNeighList( z->num_atoms, i?z->num_at_tg:z->num_atoms, z->at[i], pCS->bDoubleBondSquare, pCS->t_group_info ); - pCS->pBCN = NULL; - ret = Canon_INChI( z->num_atoms, i?z->num_at_tg:z->num_atoms, - z->at[i], pCS, z->nMode, i); - } - - pINChI = ppINChI[i]; /* pointers to already allocated still empty InChI */ - pINChI_Aux = ppINChI_Aux[i]; - - if ( ret <= 0 ) - { - /***************************************/ - /* failure in Canon_INChI() */ - /***************************************/ - pINChI->nErrorCode = ret; - pINChI_Aux->nErrorCode = ret; - } - else - { - /***************************************/ - /* success Canon_INChI() */ - /* save canonicalization results in */ - /* pINChI and pINChI_Aux */ - /***************************************/ - pINChI->nErrorCode = 0; - pINChI_Aux->nErrorCode = 0; - pINChI->bDeleted = pINChI_Aux->bDeleted = out_norm_data[i]->bDeleted; - pINChI_Aux->nCanonFlags = pCS->nCanonFlags; - pINChI_Aux->bTautFlags = out_norm_data[i]->bTautFlags; - pINChI_Aux->bTautFlagsDone = out_norm_data[i]->bTautFlagsDone; - pINChI_Aux->bNormalizationFlags = out_norm_data[i]->bNormalizationFlags; - /* may return an error or a warning */ - ret = FillOutINChIReducedWarn( pINChI, pINChI_Aux, - z->num_atoms, i?z->num_at_tg:z->num_atoms, - i?z->num_deleted_H_taut:z->num_deleted_H, z->at[i], - out_norm_data[i]->at, pCS, i, z->nUserMode, pStrErrStruct ); - if ( RETURNED_ERROR( ret ) ) - { - /* failure in FillOutINChI() */ - pINChI->nErrorCode = ret; - pINChI_Aux->nErrorCode = ret; - } - else - { - /****************************/ - /* success in FillOutINChI() */ - /****************************/ - - /* mark non-tautomeric representation as having another, tautomeric representation */ - if ( pINChI_Aux && z->s[TAUT_YES].nLenLinearCTTautomer ) - pINChI_Aux->bIsTautomeric = z->s[TAUT_YES].nLenLinearCTTautomer; - - - ret2 = CheckCanonNumberingCorrectness(z->num_atoms, - i?z->num_at_tg:z->num_atoms, - z->at[i], pCS, i, pStrErrStruct ); - if (ret2) - { - pINChI->nErrorCode = ret2; - pINChI_Aux->nErrorCode = ret2; - ret = ret2; - } - } /* success in FillOutINChI */ - } /* success Canon_INChI */ - - FreeNeighList( pCS->NeighList ); - DeAllocateCS( pCS2 ); - - pINChI = NULL; /* avoid dangling pointers */ - pINChI_Aux = NULL; /* avoid dangling pointers */ - - } /* for ( i = z->n2; i >= z->n1 && !RETURNED_ERROR( ret ); i -- ) */ - - - - if ( ret == 0 ) - ret = z->num_atoms; - - -exit_function: - - DeAllocBCN( pBCN ); - - if ( z->at[TAUT_YES] ) - { - inchi_free( z->at[TAUT_YES] ); - z->at[TAUT_YES] = NULL; - } - - if ( z->at[TAUT_NON] ) - { - inchi_free( z->at[TAUT_NON] ); - z->at[TAUT_NON] = NULL; - } - - if ( ti_out ) - *ti_out = *t_group_info; - else - { - free_t_group_info( t_group_info ); - t_group_info = NULL; - } - - free_t_group_info( t_group_info_orig ); - - return ret; -} - - - - -/****************************************************************************/ -int CreateCompositeNormAtom(COMP_ATOM_DATA *composite_norm_data, INP_ATOM_DATA2 *all_inp_norm_data, int num_components) -{ - int i, j, jj, k, n, m, tot_num_at, tot_num_H, cur_num_at, cur_num_H, nNumRemovedProtons; - int num_comp[TAUT_NUM+1], num_taut[TAUT_NUM+1], num_del[TAUT_NUM+1], num_at[TAUT_NUM+1], num_inp_at[TAUT_NUM+1]; - int ret = 0, indicator = 1; - inp_ATOM *at, *at_from; - memset( num_comp, 0, sizeof(num_comp) ); - memset( num_taut, 0, sizeof(num_taut) ); - memset( num_del, 0, sizeof(num_taut) ); - /* count taut and non-taut components */ - for ( j = 0; j < TAUT_NUM; j ++ ) { - num_comp[j] = num_taut[j] = 0; - for ( i = 0; i < num_components; i ++ ) { - if ( all_inp_norm_data[i][j].bExists ) { - num_del[j] += (0 != all_inp_norm_data[i][j].bDeleted ); - num_comp[j] ++; - num_taut[j] += (0 != all_inp_norm_data[i][j].bTautomeric); - } - } - } - /* count intermediate taut structure components */ - if ( num_comp[TAUT_YES] > num_del[TAUT_YES] && num_taut[TAUT_YES] ) { - /* - num_comp[TAUT_INI] = num_comp[TAUT_YES] - num_del[TAUT_YES]; - */ - - for ( i = 0, j=TAUT_YES; i < num_components; i ++ ) { - if ( all_inp_norm_data[i][j].bExists && - (all_inp_norm_data[i][j].bDeleted || - all_inp_norm_data[i][j].bTautomeric && - all_inp_norm_data[i][j].at_fixed_bonds && - all_inp_norm_data[i][j].bTautPreprocessed) ) { - num_comp[TAUT_INI] ++; - } - } - - } - /* count atoms and allocate composite atom data */ - for ( jj = 0; jj <= TAUT_INI; jj ++ ) { - num_at[jj] = num_inp_at[jj] = 0; - j = inchi_min (jj, TAUT_YES); - if ( num_comp[jj] ) { - for ( i = 0; i < num_components; i ++ ) { - if ( all_inp_norm_data[i][j].bDeleted ) - continue; - /* find k = the normaized structure index */ - if ( jj == TAUT_INI ) { - if ( all_inp_norm_data[i][j].bExists && - all_inp_norm_data[i][j].at_fixed_bonds ) { - k = j; - } else - if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted && - !all_inp_norm_data[i][j].bDeleted ) { - k = ALT_TAUT(j); - } else - if ( all_inp_norm_data[i][j].bExists ) { - k = j; - } else { - continue; - } - } else { - if ( all_inp_norm_data[i][j].bExists ) { - k = j; - } else - if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted) { - k = ALT_TAUT(j); - } else { - continue; - } - } - num_inp_at[jj] += all_inp_norm_data[i][k].num_at; /* all atoms including terminal H */ - num_at[jj] += all_inp_norm_data[i][k].num_at - all_inp_norm_data[i][k].num_removed_H; - } - if ( num_inp_at[jj] ) { - if ( !CreateCompAtomData( composite_norm_data+jj, num_inp_at[jj], num_components, jj == TAUT_INI ) ) - goto exit_error; - composite_norm_data[jj].num_removed_H = num_inp_at[jj] - num_at[jj]; - } - } - } - /* fill out composite atom */ - for ( jj = 0; jj <= TAUT_INI; jj ++, indicator <<= 1 ) { - j = inchi_min (jj, TAUT_YES); - if ( num_comp[jj] ) { - tot_num_at = 0; - tot_num_H = 0; - for ( i = 0; i < num_components; i ++ ) { - if ( all_inp_norm_data[i][j].bDeleted ) { - composite_norm_data[jj].nNumRemovedProtons += all_inp_norm_data[i][j].nNumRemovedProtons; - for ( n = 0; n < NUM_H_ISOTOPES; n ++ ) { - composite_norm_data[jj].nNumRemovedProtonsIsotopic[n] += all_inp_norm_data[i][j].nNumRemovedProtonsIsotopic[n]; - } - continue; - } - nNumRemovedProtons = 0; - k = TAUT_NUM; - /* find k = the normaized structure index */ - if ( jj == TAUT_INI ) { - if ( all_inp_norm_data[i][j].bExists && all_inp_norm_data[i][j].at_fixed_bonds ) { - k = j; - } else - if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists ) { - k = ALT_TAUT(j); - } else - if ( all_inp_norm_data[i][j].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted ) { - k = j; - } else { - continue; - } - } else { - if ( all_inp_norm_data[i][j].bExists ) { - k = j; - } else - if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted ) { - k = ALT_TAUT(j); - } else { - continue; - } - } - /* copy main atoms */ - cur_num_H = all_inp_norm_data[i][k].num_removed_H; /* number of terminal H atoms */ - cur_num_at = all_inp_norm_data[i][k].num_at - cur_num_H; /* number of all but explicit terminal H atoms */ - - if ( (tot_num_at + cur_num_at) > num_at[jj] || - (num_at[jj] + tot_num_H + cur_num_H) > num_inp_at[jj] ) { - goto exit_error; /* miscount */ - } - at = composite_norm_data[jj].at+tot_num_at; /* points to the 1st destination atom */ - at_from = (jj == TAUT_INI && k == TAUT_YES && all_inp_norm_data[i][k].at_fixed_bonds)? - all_inp_norm_data[i][k].at_fixed_bonds : all_inp_norm_data[i][k].at; - memcpy( at, at_from, sizeof(composite_norm_data[0].at[0]) * cur_num_at ); /* copy atoms except terminal H */ - /* shift neighbors of main atoms */ - for ( n = 0; n < cur_num_at; n ++, at ++ ) { - for ( m = 0; m < at->valence; m ++ ) { - at->neighbor[m] += tot_num_at; - } - } - /* copy explicit H */ - if ( cur_num_H ) { - at = composite_norm_data[jj].at+num_at[jj]+tot_num_H; /* points to the 1st destination atom */ - memcpy( at, at_from+cur_num_at, - sizeof(composite_norm_data[0].at[0]) * cur_num_H ); - /* shift neighbors of explicit H atoms */ - for ( n = 0; n < cur_num_H; n ++, at ++ ) { - for ( m = 0; m < at->valence; m ++ ) { - at->neighbor[m] += tot_num_at; - } - } - } - /* composite counts */ - composite_norm_data[jj].bHasIsotopicLayer |= all_inp_norm_data[i][k].bHasIsotopicLayer; - composite_norm_data[jj].num_isotopic += all_inp_norm_data[i][k].num_isotopic; - composite_norm_data[jj].num_bonds += all_inp_norm_data[i][k].num_bonds; - composite_norm_data[jj].bTautomeric += (j == jj) && all_inp_norm_data[i][k].bTautomeric; - composite_norm_data[jj].nNumRemovedProtons += all_inp_norm_data[i][k].nNumRemovedProtons; - for ( n = 0; n < NUM_H_ISOTOPES; n ++ ) { - composite_norm_data[jj].nNumRemovedProtonsIsotopic[n] += all_inp_norm_data[i][k].nNumRemovedProtonsIsotopic[n]; - composite_norm_data[jj].num_iso_H[n] += all_inp_norm_data[i][k].num_iso_H[n]; - } - /* - composite_norm_data[j].num_at += cur_num_at + cur_num_H; - composite_norm_data[j].num_removed_H += cur_num_H; - */ - /* total count */ - tot_num_at += cur_num_at; - tot_num_H += cur_num_H; - /* offset for the next component */ - if ( composite_norm_data[jj].nOffsetAtAndH ) { - composite_norm_data[jj].nOffsetAtAndH[2*i] = tot_num_at; - composite_norm_data[jj].nOffsetAtAndH[2*i+1] = num_at[jj]+tot_num_H; - } - } - if ( tot_num_at != num_at[jj] || - num_at[jj] + tot_num_H != num_inp_at[jj] ) { - goto exit_error; /* miscount */ - } - composite_norm_data[jj].bExists = (tot_num_at>0); - ret |= indicator; - } - } - return ret; - - - - - -exit_error: - return ret; -} - -int CreateCompAtomData( COMP_ATOM_DATA *inp_at_data, int num_atoms, int num_components, int bIntermediateTaut ) -{ - FreeCompAtomData( inp_at_data ); - if ( (inp_at_data->at = CreateInpAtom( num_atoms )) && - (num_components <= 1 || bIntermediateTaut || - (inp_at_data->nOffsetAtAndH = (AT_NUMB*)inchi_calloc(sizeof(inp_at_data->nOffsetAtAndH[0]), 2*(num_components+1))))) { - - inp_at_data->num_at = num_atoms; - inp_at_data->num_components = (num_components>1)? num_components : 0; - return 1; - } - FreeCompAtomData( inp_at_data ); - return 0; -} - - -/**********************************************************************************************/ -int FillOutINChIReducedWarn( INChI *pINChI, INChI_Aux *pINChI_Aux, - int num_atoms, int num_at_tg, int num_removed_H, - sp_ATOM *at, inp_ATOM *norm_at, CANON_STAT *pCS, int bTautomeric, - INCHI_MODE nUserMode, char *pStrErrStruct ) -{ - int i, j, m, n, g, len, ii, ret=0; - - AT_NUMB *pSymmRank, *pOrigNosInCanonOrd, *pConstitEquNumb, *pCanonOrd=NULL, *pCanonOrdInv=NULL, *pCanonOrdTaut; - T_GROUP_INFO *t_group_info = pCS->t_group_info; - T_GROUP *t_group; - int nErrorCode = 0; - AT_NUMB *pCanonRank, *pCanonRankInv; /* canonical ranks of the atoms or tautomeric groups */ - AT_NUMB *pCanonRankAtoms=NULL, *pSortOrd = NULL; - AT_RANK nMinOrd; - INChI_Stereo *Stereo; - int bUseNumberingInv = 0, bUseIsotopicNumberingInv = 0; - INCHI_MODE nStereoUnmarkMode; - - /*AT_NUMB *pCanonOrdNonIso = NULL, *pCanonOrdIso = NULL;*/ - /*AT_NUMB *nOrigAtNosInCanonOrdNonIso = NULL, *nOrigAtNosInCanonOrdIso = NULL;*/ - - /* Check for warnings */ - if ( pCS->nLenLinearCTStereoCarb < 0 || pCS->nLenLinearCTStereoDble < 0 || - pCS->nLenCanonOrdStereo < 0 || pCS->nLenCanonOrdStereoTaut < 0) { - nErrorCode |= WARN_FAILED_STEREO; - } - if ( pCS->nLenLinearCTIsotopic < 0 || pCS->nLenLinearCTIsotopicTautomer < 0 || - pCS->nLenCanonOrdIsotopic < 0 || pCS->nLenCanonOrdIsotopicTaut < 0 ) { - nErrorCode |= WARN_FAILED_ISOTOPIC; - } - if ( pCS->nLenLinearCTIsotopicStereoCarb < 0 || pCS->nLenLinearCTIsotopicStereoDble < 0 || - pCS->nLenCanonOrdIsotopicStereo < 0 || pCS->nLenCanonOrdIsotopicStereoTaut < 0) { - nErrorCode |= WARN_FAILED_ISOTOPIC_STEREO; - } - pCanonRankAtoms = (AT_NUMB *)inchi_calloc( num_at_tg+1, sizeof(pCanonRankAtoms[0]) ); - pSortOrd = (AT_NUMB *)inchi_calloc( num_at_tg+1, sizeof(pSortOrd[0]) ); /* must have more than num_atoms */ - - if ( !pCanonRankAtoms || !pSortOrd ) { - nErrorCode = 0; - ret = CT_OUT_OF_RAM; /* */ - pINChI->nErrorCode = pINChI_Aux->nErrorCode = CT_OUT_OF_RAM; - goto exit_function; - } - - /* total charge */ - for ( i = 0, n = 0; i < num_atoms+num_removed_H; i ++ ) { - n += at[i].charge; - } - pINChI->nTotalCharge = n; - - /* number of atoms */ - pINChI->nNumberOfAtoms = num_atoms; - pINChI_Aux->nNumberOfAtoms = num_atoms; - - /* removed protons and detachable isotopic H */ - if ( bTautomeric && t_group_info ) { - pINChI_Aux->nNumRemovedProtons = t_group_info->tni.nNumRemovedProtons; - for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { - pINChI_Aux->nNumRemovedIsotopicH[i] = t_group_info->num_iso_H[i] - + t_group_info->tni.nNumRemovedProtonsIsotopic[i]; - } - if ( pINChI_Aux->bNormalizationFlags & FLAG_FORCE_SALT_TAUT ) { - pINChI->nFlags |= INCHI_FLAG_HARD_ADD_REM_PROTON; - } -/*^^^ - if ( pINChI_Aux->bNormalizationFlags & (FLAG_NORM_CONSIDER_TAUT &~FLAG_PROTON_CHARGE_CANCEL) ) { - AddMOLfileError(pStrErrStruct, "Proton(s) added/removed"); - } - - if ( pINChI_Aux->bNormalizationFlags & FLAG_PROTON_CHARGE_CANCEL ) { - AddMOLfileError(pStrErrStruct, "Charges neutralized"); - } -^^^*/ - } - - /* abs or rel stereo may establish one of two canonical numberings */ - if ( (pCS->nLenLinearCTStereoCarb > 0 || pCS->nLenLinearCTStereoDble > 0) && - pCS->nLenCanonOrdStereo > 0 && - (pCS->LinearCTStereoCarb && pCS->LinearCTStereoCarbInv || - pCS->LinearCTStereoDble && pCS->LinearCTStereoDbleInv) && - pCS->nCanonOrdStereo && pCS->nCanonOrdStereoInv - ) { - - pCanonRank = pCanonRankAtoms; - pCanonOrd = pCS->nCanonOrdStereo; - pCanonRankInv = pSortOrd; - pCanonOrdInv = pCS->nCanonOrdStereoInv; - Stereo = pINChI->Stereo; - for ( i = 0; i < num_at_tg; i ++ ) { - pCanonRankInv[pCanonOrdInv[i]] = - pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); - } - /********************************************************************/ - /* copy stereo bonds and stereo centers; compare Inv and Abs stereo */ - /********************************************************************/ - nErrorCode = CopyLinearCTStereoToINChIStereo( Stereo, - pCS->LinearCTStereoCarb, pCS->nLenLinearCTStereoCarb, - pCS->LinearCTStereoDble, pCS->nLenLinearCTStereoDble - , pCanonOrd, pCanonRank, at, 0 /* non-isotopic */ - , pCS->LinearCTStereoCarbInv - , pCS->LinearCTStereoDbleInv - , pCanonOrdInv, pCanonRankInv ); - - if ( Stereo->t_parityInv && Stereo->nNumberInv ) { - if ( nUserMode & REQ_MODE_RELATIVE_STEREO ) { - pINChI->nFlags |= INCHI_FLAG_REL_STEREO; - } - if ( nUserMode & REQ_MODE_RACEMIC_STEREO ) { - pINChI->nFlags |= INCHI_FLAG_RAC_STEREO; - } - if ( Stereo->nCompInv2Abs ) { - if ( Stereo->nCompInv2Abs == -1 ) { - /* switch pointers in Stereo so that the stereo becomes the smallest (relative) */ - /* flag Stereo->nCompInv2Abs == -1 will keep track of this exchange */ - AT_NUMB *nNumberInv = Stereo->nNumberInv; - S_CHAR *t_parityInv = Stereo->t_parityInv; - Stereo->nNumberInv = Stereo->nNumber; - Stereo->t_parityInv = Stereo->t_parity; - Stereo->nNumber = nNumberInv; - Stereo->t_parity = t_parityInv; - /* switch pointers to set rel. stereo to pINChI_Aux->nOrigAtNosInCanonOrd - and inv. stereo to pINChI_Aux->nOrigAtNosInCanonOrdInv */ - switch_ptrs( &pCanonRank, &pCanonRankInv ); - switch_ptrs( &pCanonOrd, &pCanonOrdInv ); - bUseNumberingInv = 1; /* use inverted stereo numbering instead of normal */ - } - } - } - - for ( i = 0; i < num_atoms; i ++ ) { - pINChI_Aux->nOrigAtNosInCanonOrdInv[i] = at[pCanonOrdInv[i]].orig_at_number; - pINChI_Aux->nOrigAtNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; - } - if ( bUseNumberingInv ) { - /* switch ptrs back to avoid confusion */ - switch_ptrs( &pCanonRank, &pCanonRankInv ); - switch_ptrs( &pCanonOrd, &pCanonOrdInv ); - /* save inverted stereo ranks & order because it represents the smallest (relative) */ - memcpy( pCanonRank, pCanonRankInv, num_at_tg * sizeof(pCanonRank[0]) ); - /* change pCS->nCanonOrdStereo[] to inverted: */ - memcpy( pCanonOrd, pCanonOrdInv, num_at_tg * sizeof(pCanonOrd[0]) ); - } - pCanonRankInv = NULL; - pCanonOrdInv = NULL; - pOrigNosInCanonOrd = NULL; - - } else { /*------------------------------ no stereo */ - - pCanonOrd = pCS->nLenCanonOrdStereo > 0? pCS->nCanonOrdStereo : - pCS->nLenCanonOrd > 0? pCS->nCanonOrd : NULL; - pCanonRank = pCanonRankAtoms; - pOrigNosInCanonOrd = pINChI_Aux->nOrigAtNosInCanonOrd; - if ( pCanonOrd && pCanonRank ) { - for ( i = 0; i < num_atoms; i ++ ) { - pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); - pOrigNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; - } - for ( ; i < num_at_tg; i ++ ) { - pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); - } - } - } - /*pCanonOrdNonIso = pCanonOrd;*/ /* save for aux info */ - - - if ( pINChI_Aux->OrigInfo ) { - /* charges, radicals, valences */ - for ( i = 0; i < num_atoms; i ++ ) { - ii = pCanonOrd[i]; - if ( norm_at[ii].valence || norm_at[ii].num_H ) { - pINChI_Aux->OrigInfo[i].cCharge = norm_at[ii].charge; - pINChI_Aux->OrigInfo[i].cRadical = (norm_at[ii].radical==RADICAL_SINGLET)? 0 : - (norm_at[ii].radical==RADICAL_DOUBLET)? 1 : - (norm_at[ii].radical==RADICAL_TRIPLET)? 2 : - norm_at[ii].radical? 3 : 0 ; - pINChI_Aux->OrigInfo[i].cUnusualValence = - get_unusual_el_valence( norm_at[ii].el_number, norm_at[ii].charge, norm_at[ii].radical, - norm_at[ii].chem_bonds_valence, norm_at[ii].num_H, norm_at[ii].valence ); - } else { - /* charge of a single atom component is in the INChI; valence = 0 is standard */ - pINChI_Aux->OrigInfo[i].cRadical = (norm_at[ii].radical==RADICAL_SINGLET)? 0 : - (norm_at[ii].radical==RADICAL_DOUBLET)? 1 : - (norm_at[ii].radical==RADICAL_TRIPLET)? 2 : - norm_at[ii].radical? 3 : 0 ; - } - - } - } - - /* non-isotopic canonical numbers and equivalence of atoms (Aux) */ - pConstitEquNumb = pINChI_Aux->nConstitEquNumbers; /* contitutional equivalence */ - pSymmRank = pCS->nSymmRank; - if ( pCanonOrd && pCanonRank && pSymmRank && pConstitEquNumb ) { - for ( i = 0; i < num_atoms; i ++ ) { - pConstitEquNumb[i] = pSymmRank[pCanonOrd[i]]; /* constit. equ. ranks in order of canonical numbers */ - pSortOrd[i] = i; - } - for ( ; i < num_at_tg; i ++ ) { - pSortOrd[i] = MAX_ATOMS; /* for debugging only */ - } - pn_RankForSort = pConstitEquNumb; - qsort( pSortOrd, num_atoms, sizeof(pSortOrd[0]), CompRanksOrd ); - for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= num_atoms; j ++ ) { - if ( j == num_atoms || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) { - nMinOrd ++; - if ( j - i > 1 ) { - /* found a sequence of equivalent atoms: i..j-1 */ - while ( i < j ) { - pConstitEquNumb[pSortOrd[i++]] = nMinOrd; /* = min. canon. rank in the group of equ. atoms */ - } - /* at this point j == i */ - } else { - pConstitEquNumb[pSortOrd[i++]] = 0; /* means the atom is not equivalent to any other */ - } - nMinOrd = pSortOrd[j]; /* at the end j = num_atoms */ - } - } - } else { - nErrorCode |= ERR_NO_CANON_RESULTS; - ret = -1; /* program error; no breakpoint here */ - goto exit_function; - } - /* atomic numbers from the Periodic Table */ - for ( i = 0; i < num_atoms; i ++ ) { - pINChI->nAtom[i] = (int)at[pCanonOrd[i]].el_number; - } - /* connection table: atoms only (before 7-29-2003 pCS->LinearCT2 contained non-isotopic CT) */ - if ( pCS->nLenLinearCTAtOnly <= 0 || !pCS->LinearCT || !pINChI->nConnTable ) { - nErrorCode |= ERR_NO_CANON_RESULTS; - ret = -2; - goto exit_function; - } - memcpy( pINChI->nConnTable, pCS->LinearCT, sizeof(pINChI->nConnTable[0])*pCS->nLenLinearCTAtOnly); - pINChI->lenConnTable = pCS->nLenLinearCTAtOnly; - - /* tautomeric group(s) canonical representation */ - len = 0; - if ( bTautomeric && 0 < (n = SortTautomerGroupsAndEndpoints( t_group_info, num_atoms, num_at_tg, pCanonRank )) ) { - /* SortTautomerGroupsAndEndpoints() produces canonically ordered t-groups */ - pINChI->nFlags |= (t_group_info->bTautFlagsDone & TG_FLAG_ALL_SALT_DONE)? INCHI_FLAG_ACID_TAUT : 0; - /* number of tautomeric groups */ - pINChI->nTautomer[len ++] = (AT_NUMB)n; - /* store each tautomeric group, one by one */ - for ( i = 0; i < n; i ++ ) { - g = (int)t_group_info->tGroupNumber[i]; /* original group numbers in sorted order */ - t_group = t_group_info->t_group + g; /* pointer to the tautomeric group */ - /* NumAt+INCHI_T_NUM_MOVABLE (group length excluding this number) */ - pINChI->nTautomer[len ++] = t_group->nNumEndpoints+INCHI_T_NUM_MOVABLE; - /* Num(H), Num(-) */ - for ( j = 0; j < INCHI_T_NUM_MOVABLE && j < T_NUM_NO_ISOTOPIC; j ++ ) - pINChI->nTautomer[len ++] = t_group->num[j]; - for ( j = T_NUM_NO_ISOTOPIC; j < INCHI_T_NUM_MOVABLE; j ++ ) - pINChI->nTautomer[len ++] = 0; /* should not happen */ - /* tautomeric group endpoint canonical numbers, pre-sorted in ascending order */ - for ( j = (int)t_group->nFirstEndpointAtNoPos, - m = j + (int)t_group->nNumEndpoints; j < m; j ++ ) { - pINChI->nTautomer[len ++] = pCanonRank[(int)t_group_info->nEndpointAtomNumber[j]]; /* At[j] */ - } - } - pINChI->lenTautomer = len; - pINChI_Aux->nNumberOfTGroups = n; - } else { - pINChI->lenTautomer = 0; - pINChI_Aux->nNumberOfTGroups = 0; - if ( t_group_info && ((t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) || - t_group_info->nNumIsotopicEndpoints>1 && - (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE))) - ) { - /* only protons (re)moved or added */ - pINChI->lenTautomer = 1; - pINChI->nTautomer[0] = 0; - } - } - - /* number of H (excluding tautomeric) */ - if ( pCS->nNum_H ) { - for ( i = 0; i < num_atoms; i ++ ) { - pINChI->nNum_H[i] = pCS->nNum_H[i]; - } - } - /* number of fixed H (tautomeric H in non-tautomeric representation) */ - if ( pCS->nNum_H_fixed && !pINChI->lenTautomer ) { - for ( i = 0; i < num_atoms; i ++ ) { - pINChI->nNum_H_fixed[i] = pCS->nNum_H_fixed[i]; - pINChI->nNum_H[i] += pCS->nNum_H_fixed[i]; - } - } - - /*********************************************************** - * tautomeric group(s) numbering and symmetry; - * should not depend on switching to rel. stereo numbering - */ - if ( pINChI->lenTautomer && (n=pINChI_Aux->nNumberOfTGroups) ) { - pCanonOrdTaut = pCS->nLenCanonOrdStereoTaut > 0? pCS->nCanonOrdStereoTaut : - pCS->nLenCanonOrdTaut > 0? pCS->nCanonOrdTaut : NULL; - pConstitEquNumb = pINChI_Aux->nConstitEquTGroupNumbers; - pSymmRank = pCS->nSymmRankTaut; - if ( pCanonOrdTaut && pSymmRank && pConstitEquNumb ) { - for ( i = 0; i < n; i ++ ) { - pConstitEquNumb[i] = pSymmRank[pCanonOrdTaut[i]]; - pSortOrd[i] = i; - } - pn_RankForSort = pConstitEquNumb; - qsort( pSortOrd, n, sizeof(pSortOrd[0]), CompRanksOrd ); - for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= n; j ++ ) { - if ( j == n || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) { - nMinOrd ++; /* make is start from 1, not from zero */ - if ( j - i > 1 ) { - /* found a sequence of more than one equivalent t-groups: i..j-1 */ - while ( i < j ) { - pConstitEquNumb[pSortOrd[i++]] = nMinOrd; - } - } else { - pConstitEquNumb[pSortOrd[i++]] = 0; - } - nMinOrd = pSortOrd[j]; /* at the end j == n */ - } - } - } - } - - /* Allocate and fill Hill formula */ - if ( !(pINChI->szHillFormula = AllocateAndFillHillFormula( pINChI ) ) ) { - nErrorCode = 0; - ret = CT_WRONG_FORMULA; /* CT_OUT_OF_RAM;*/ /* */ - pINChI->nErrorCode = pINChI_Aux->nErrorCode = ret; - goto exit_function; - } - - if ( nStereoUnmarkMode = UnmarkAllUndefinedUnknownStereo( pINChI->Stereo, nUserMode ) ) { - pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU)? INCHI_FLAG_SC_IGN_ALL_UU : 0; - pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU)? INCHI_FLAG_SB_IGN_ALL_UU : 0; - if ( (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU) || - (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU) ) { - AddMOLfileError(pStrErrStruct, "Omitted undefined stereo"); - } - } - - /*************************/ - /* mark ambiguous stereo */ - /*************************/ - MarkAmbiguousStereo( at, norm_at, 0 /* non-isotopic */, pCanonOrd, - pCS->LinearCTStereoCarb, pCS->nLenLinearCTStereoCarb, - pCS->LinearCTStereoDble, pCS->nLenLinearCTStereoDble ); - - - /************************************************************************ - * - * isotopic part - */ - /* abs or rel stereo may establish one of two canonical numberings */ - if ( (pCS->nLenLinearCTIsotopicStereoCarb > 0 || pCS->nLenLinearCTIsotopicStereoDble > 0) && - pCS->nLenCanonOrdIsotopicStereo > 0 && - (pCS->LinearCTIsotopicStereoCarb && pCS->LinearCTIsotopicStereoCarbInv || - pCS->LinearCTIsotopicStereoDble && pCS->LinearCTIsotopicStereoDbleInv) && - pCS->nCanonOrdIsotopicStereo && pCS->nCanonOrdIsotopicStereoInv - ) { - /* found isotopic stereo */ - pCanonRank = pCanonRankAtoms; - pCanonOrd = pCS->nCanonOrdIsotopicStereo; - pCanonRankInv = pSortOrd; - pCanonOrdInv = pCS->nCanonOrdIsotopicStereoInv; - Stereo = pINChI->StereoIsotopic; - for ( i = 0; i < num_at_tg; i ++ ) { - pCanonRankInv[pCanonOrdInv[i]] = - pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); - } - /********************************************************************/ - /* copy stereo bonds and stereo centers; compare Inv and Abs stereo */ - /********************************************************************/ - nErrorCode = CopyLinearCTStereoToINChIStereo( Stereo, - pCS->LinearCTIsotopicStereoCarb, pCS->nLenLinearCTIsotopicStereoCarb, - pCS->LinearCTIsotopicStereoDble, pCS->nLenLinearCTIsotopicStereoDble - , pCanonOrd, pCanonRank, at, 1 /* isotopic */ - , pCS->LinearCTIsotopicStereoCarbInv - , pCS->LinearCTIsotopicStereoDbleInv - , pCanonOrdInv, pCanonRankInv ); - - if ( Stereo->t_parityInv && Stereo->nNumberInv ) { - if ( nUserMode & REQ_MODE_RELATIVE_STEREO ) { - pINChI->nFlags |= INCHI_FLAG_REL_STEREO; - } - if ( nUserMode & REQ_MODE_RACEMIC_STEREO ) { - pINChI->nFlags |= INCHI_FLAG_RAC_STEREO; - } - if ( Stereo->nCompInv2Abs ) { - if ( Stereo->nCompInv2Abs == -1 ) { - /* switch pointers so that the stereo becomes the smallest (relative) */ - /* flag Stereo->nCompInv2Abs == -1 will keep track of this exchange */ - AT_NUMB *nNumberInv = Stereo->nNumberInv; - S_CHAR *t_parityInv = Stereo->t_parityInv; - Stereo->nNumberInv = Stereo->nNumber; - Stereo->t_parityInv = Stereo->t_parity; - Stereo->nNumber = nNumberInv; - Stereo->t_parity = t_parityInv; - switch_ptrs( &pCanonRank, &pCanonRankInv ); - switch_ptrs( &pCanonOrd, &pCanonOrdInv ); - bUseIsotopicNumberingInv = 1; - } - } - } - - for ( i = 0; i < num_atoms; i ++ ) { - pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv[i] = at[pCanonOrdInv[i]].orig_at_number; - pINChI_Aux->nIsotopicOrigAtNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; - } - if ( bUseIsotopicNumberingInv ) { - switch_ptrs( &pCanonRank, &pCanonRankInv ); - switch_ptrs( &pCanonOrd, &pCanonOrdInv ); - memcpy( pCanonRank, pCanonRankInv, num_at_tg * sizeof(pCanonRank[0]) ); - memcpy( pCanonOrd, pCanonOrdInv, num_at_tg * sizeof(pCanonOrd[0]) ); - } - pCanonRankInv = NULL; - pCanonOrdInv = NULL; - pOrigNosInCanonOrd = NULL; - - } else { - /* no isotopic stereo */ - pCanonOrd = pCS->nLenCanonOrdIsotopicStereo > 0? pCS->nCanonOrdIsotopicStereo : - pCS->nLenCanonOrdIsotopic > 0? pCS->nCanonOrdIsotopic : NULL; - pCanonRank = pCanonRankAtoms; - pOrigNosInCanonOrd = pINChI_Aux->nIsotopicOrigAtNosInCanonOrd; - if ( pCanonOrd && pCanonRank ) { - for ( i = 0; i < num_atoms; i ++ ) { /* Fix13 -- out of bounds */ - pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); - pOrigNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; - } - for ( ; i < num_at_tg; i ++ ) { /* Fix13 -- out of bounds */ - pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); - } - } - } - /*pCanonOrdIso = pCanonOrd;*/ - - pConstitEquNumb = pINChI_Aux->nConstitEquIsotopicNumbers; - pSymmRank = pCS->nSymmRankIsotopic; - if ( pCanonOrd && pCanonRank && pConstitEquNumb && pSymmRank ) { - for ( i = 0; i < num_atoms; i ++ ) { - pConstitEquNumb[i] = pSymmRank[pCanonOrd[i]]; - pSortOrd[i] = i; - } - for ( ; i < num_at_tg; i ++ ) { - pSortOrd[i] = i; - } - pn_RankForSort = pConstitEquNumb; - qsort( pSortOrd, num_atoms, sizeof(pSortOrd[0]), CompRanksOrd ); - for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= num_atoms; j ++ ) { - if ( j == num_atoms || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) { - nMinOrd ++; - if ( j - i > 1 ) { - /* found a sequence of equivalent atoms: i..j-1 */ - while ( i < j ) { - pConstitEquNumb[pSortOrd[i++]] = nMinOrd; - } - } else { - pConstitEquNumb[pSortOrd[i++]] = 0; /* nMinOrd; */ - } - nMinOrd = pSortOrd[j]; - } - } - } else { - goto exit_function; /* no isotopic info available */ - } - /* isotopic atoms */ - n = pINChI->nNumberOfIsotopicAtoms = pCS->nLenLinearCTIsotopic; - for ( i = 0; i < n; i ++ ) { - pINChI->IsotopicAtom[i].nAtomNumber = pCS->LinearCTIsotopic[i].at_num; - pINChI->IsotopicAtom[i].nIsoDifference = pCS->LinearCTIsotopic[i].iso_atw_diff; - pINChI->IsotopicAtom[i].nNum_H = pCS->LinearCTIsotopic[i].num_1H; - pINChI->IsotopicAtom[i].nNum_D = pCS->LinearCTIsotopic[i].num_D; - pINChI->IsotopicAtom[i].nNum_T = pCS->LinearCTIsotopic[i].num_T; - } - /* isotopic tautomeric groups */ - n = pINChI->nNumberOfIsotopicTGroups = pCS->nLenLinearCTIsotopicTautomer; - for ( i = 0; i < n; i ++ ) { - pINChI->IsotopicTGroup[i].nTGroupNumber = pCS->LinearCTIsotopicTautomer[i].tgroup_num; - pINChI->IsotopicTGroup[i].nNum_H = pCS->LinearCTIsotopicTautomer[i].num[2]; - pINChI->IsotopicTGroup[i].nNum_D = pCS->LinearCTIsotopicTautomer[i].num[1]; - pINChI->IsotopicTGroup[i].nNum_T = pCS->LinearCTIsotopicTautomer[i].num[0]; - } - /* atoms that may exchange isotopic H-atoms */ - if ( pCS->nExchgIsoH && pINChI->nPossibleLocationsOfIsotopicH ) { - for ( i = 0, j = 1; i < num_atoms; i ++ ) { - if ( pCS->nExchgIsoH[i] ) { - pINChI->nPossibleLocationsOfIsotopicH[j++] = (AT_NUMB)(i+1); /* canonical number */ - } - } - pINChI->nPossibleLocationsOfIsotopicH[0] = (AT_NUMB)j; /* length including the 0th element */ - } - - if ( nStereoUnmarkMode = UnmarkAllUndefinedUnknownStereo( pINChI->StereoIsotopic, nUserMode ) ) { - pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU)? INCHI_FLAG_SC_IGN_ALL_ISO_UU : 0; - pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU)? INCHI_FLAG_SC_IGN_ALL_ISO_UU : 0; - if ( (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU) || - (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU) ) { - AddMOLfileError(pStrErrStruct, "Omitted undefined stereo"); - } - } - /* mark ambiguous stereo */ - MarkAmbiguousStereo( at, norm_at, 1 /* isotopic */, pCanonOrd, - pCS->LinearCTIsotopicStereoCarb, pCS->nLenLinearCTIsotopicStereoCarb, - pCS->LinearCTIsotopicStereoDble, pCS->nLenLinearCTIsotopicStereoDble ); - - /*********************************************************** - * isotopic tautomeric group(s) numbering and symmetry; - * should not depend on switching to rel. stereo numbering - */ - if ( pINChI->lenTautomer && pINChI_Aux->nConstitEquIsotopicTGroupNumbers && pCS->nSymmRankIsotopicTaut && - (pCS->nLenLinearCTIsotopic || pCS->nLenLinearCTIsotopicTautomer) && - t_group_info && t_group_info->num_t_groups > 0 ) { - n = t_group_info->num_t_groups; - pCanonOrdTaut = pCS->nLenCanonOrdIsotopicStereoTaut > 0? - (n=pCS->nLenCanonOrdIsotopicStereoTaut, pCS->nCanonOrdIsotopicStereoTaut) : - pCS->nLenCanonOrdIsotopicTaut > 0? - (n=pCS->nLenCanonOrdIsotopicTaut,pCS->nCanonOrdIsotopicTaut) : (n=0,(AT_RANK*)NULL); - pConstitEquNumb = pINChI_Aux->nConstitEquIsotopicTGroupNumbers; - pSymmRank = pCS->nSymmRankIsotopicTaut; - if ( pCanonOrdTaut && pSymmRank && pConstitEquNumb && n > 0 ) { - for ( i = 0; i < n; i ++ ) { - pConstitEquNumb[i] = pSymmRank[pCanonOrdTaut[i]]; - pSortOrd[i] = i; - } - pn_RankForSort = pConstitEquNumb; - qsort( pSortOrd, n, sizeof(pSortOrd[0]), CompRanksOrd ); - for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= n; j ++ ) { - if ( j == n || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) { - nMinOrd ++; - if ( j - i > 1 ) { - /* found a sequence of equivalent t-groups: i..j-1 */ - while ( i < j ) { - pConstitEquNumb[pSortOrd[i++]] = nMinOrd; - } - } else { - pConstitEquNumb[pSortOrd[i++]] = 0; /* nMinOrd; */ - } - nMinOrd = pSortOrd[j]; /* at the end j = n */ - } - } - } - } - - -exit_function: - if ( pCanonRankAtoms ) - inchi_free( pCanonRankAtoms ); - if ( pSortOrd ) - inchi_free( pSortOrd ); - - pINChI->nErrorCode |= nErrorCode; - pINChI_Aux->nErrorCode |= nErrorCode; - - return ret; -} - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void make_norm_atoms_from_inp_atoms(INCHIGEN_DATA *gendata, INCHIGEN_CONTROL *genctl) -{ -/*^^^ TODO: make a full copy (with allocs) of atom arrays */ -size_t t1; -int k; - - for ( k = 0; k < INCHI_NUM; k++) - { - if (NULL!=genctl->InpNormAtData[k]) - { - t1 = genctl->StructData.num_components[k] * sizeof(NORM_ATOMS); - memcpy(gendata->NormAtomsNontaut[k], genctl->InpNormAtData[k], t1); - } - - if (NULL!=genctl->InpNormTautData[k]) - { - t1 = genctl->StructData.num_components[k] * sizeof(NORM_ATOMS); - memcpy(gendata->NormAtomsTaut[k], genctl->InpNormTautData[k], t1); - } - } - -} +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include +#include +#include +#include +/* #include */ +#include +#include + +/* */ + +#include "../../../INCHI_BASE/src/mode.h" +#include "../../../INCHI_BASE/src/ichitime.h" +#include "../../../INCHI_BASE/src/incomdef.h" +#include "../../../INCHI_BASE/src/ichidrp.h" +#include "../../../INCHI_BASE/src/inpdef.h" +#include "../../../INCHI_BASE/src/ichi.h" +#include "../../../INCHI_BASE/src/strutil.h" +#include "../../../INCHI_BASE/src/util.h" +#include "../../../INCHI_BASE/src/ichidrp.h" +#include "../../../INCHI_BASE/src/ichierr.h" +#include "../../../INCHI_BASE/src/ichimain.h" +#include "../../../INCHI_BASE/src/extr_ct.h" +#include "../../../INCHI_BASE/src/ichitaut.h" +#include "../../../INCHI_BASE/src/ichi_io.h" +#include "../../../INCHI_BASE/src/ichinorm.h" +#include "../../../INCHI_BASE/src/ichicant.h" +#include "../../../INCHI_BASE/src/ichicano.h" +#include "../../../INCHI_BASE/src/ichicomn.h" +#include "../../../INCHI_BASE/src/ichimake.h" +#include "../../../INCHI_BASE/src/ichister.h" +/* */ +#ifdef INCHI_LIB +#include "ichi_lib.h" +#endif +#include "../../../INCHI_BASE/src/ichicomp.h" + +/* for DisplayTheWholeStructure() */ +#define COMP_ORIG_0_MAIN 0x0001 +#define COMP_ORIG_0_RECN 0x0002 +#define COMP_PREP_0_MAIN 0x0004 +#define COMP_PREP_0_RECN 0x0008 +#define COMP_ORIG_1_MAIN 0x0010 +#define COMP_ORIG_1_RECN 0x0020 + +#include "../../../INCHI_BASE/src/ichisize.h" +#include "../../../INCHI_BASE/src/mode.h" +#include "../../../INCHI_BASE/src/inchi_api.h" +#include "inchi_dll_a.h" /* not inchi_api.h as it hides internal data types */ + + +int inp2spATOM( inp_ATOM *inp_at, + int num_inp_at, + sp_ATOM *at ); +int CheckCanonNumberingCorrectness( int num_atoms, + int num_at_tg, + sp_ATOM *at, + CANON_STAT *pCS, + CANON_GLOBALS *pCG, + int bTautomeric, + char *pStrErrStruct ); +int CreateCompositeNormAtom( COMP_ATOM_DATA *composite_norm_data, + INP_ATOM_DATA2 *all_inp_norm_data, + int num_components); +int CopyLinearCTStereoToINChIStereo( INChI_Stereo *Stereo, + AT_STEREO_CARB *LinearCTStereoCarb, + int nLenLinearCTStereoCarb, + AT_STEREO_DBLE *LinearCTStereoDble, + int nLenLinearCTStereoDble, + AT_NUMB *pCanonOrd, + AT_RANK *pCanonRank, + sp_ATOM *at, + int bIsotopic, + AT_STEREO_CARB *LinearCTStereoCarbInv, + AT_STEREO_DBLE *LinearCTStereoDbleInv, + AT_NUMB *pCanonOrdInv, + AT_RANK *pCanonRankInv ); +int MarkAmbiguousStereo( sp_ATOM *at, + inp_ATOM *norm_at, + int bIsotopic, + AT_NUMB *pCanonOrd, + AT_STEREO_CARB *LinearCTStereoCarb, + int nLenLinearCTStereoCarb, + AT_STEREO_DBLE *LinearCTStereoDble, + int nLenLinearCTStereoDble ); +INCHI_MODE UnmarkAllUndefinedUnknownStereo( INChI_Stereo *Stereo, + INCHI_MODE nUserMode ); +int FillOutINChIReducedWarn( INChI *pINChI, + INChI_Aux *pINChI_Aux, + int num_atoms, + int num_at_tg, + int num_removed_H, + sp_ATOM *at, + inp_ATOM *norm_at, + CANON_STAT *pCS, + CANON_GLOBALS *pCG, + int bTautomeric, + INCHI_MODE nUserMode, + char *pStrErrStruct ); +int NormOneStructureINChI( CANON_GLOBALS *pCG, + INCHI_CLOCK *ic, + INCHIGEN_DATA *gendata, + INCHIGEN_CONTROL *genctl, + int iINChI, + INCHI_IOSTREAM *inp_file); +int CanonOneStructureINChI( CANON_GLOBALS *pCG, + INCHI_CLOCK *ic, + INCHIGEN_CONTROL *genctl, + int iINChI, + INCHI_IOSTREAM *inp_file); +int NormOneComponentINChI( CANON_GLOBALS *pCG, + INCHI_CLOCK *ic, + INCHIGEN_CONTROL *genctl, + int iINChI, + int i); +int CanonOneComponentINChI( CANON_GLOBALS *pCG, + INCHI_CLOCK *ic, + INCHIGEN_CONTROL *genctl, + int iINChI, + int i); +int Normalization_step( CANON_GLOBALS *pCG, + INCHI_CLOCK *ic, + INChI **ppINChI, + INChI_Aux **ppINChI_Aux, + inp_ATOM *inp_at, + INP_ATOM_DATA *out_norm_data[2], + int num_inp_at, + struct tagInchiTime *ulMaxTime, + INCHI_MODE *pbTautFlags, + INCHI_MODE *pbTautFlagsDone, + COMPONENT_TREAT_INFO *cti); +int Canonicalization_step( CANON_GLOBALS *pCG, + INCHI_CLOCK *ic, + INChI **ppINChI, + INChI_Aux **ppINChI_Aux, + INP_ATOM_DATA *out_norm_data[2], + struct tagInchiTime *ulMaxTime, + T_GROUP_INFO *ti_out, + char *pStrErrStruct, + COMPONENT_TREAT_INFO *cti, + int LargeMolecules); + +int NormOneStructureINChI( CANON_GLOBALS *pCG, + INCHI_CLOCK *ic, + INCHIGEN_DATA *gendata, + INCHIGEN_CONTROL *genctl, + int iINChI, + INCHI_IOSTREAM *inp_file) +{ +int k, i, j, nRet = 0; + + +STRUCT_DATA *sd = &(genctl->StructData); + +INPUT_PARMS *ip = &(genctl->InpParms); +ORIG_ATOM_DATA *prep_inp_data = &(genctl->PrepInpData[0]); +ORIG_ATOM_DATA *orig_inp_data = &(genctl->OrigInpData); +INCHI_IOSTREAM *out_file = genctl->inchi_file, *log_file = genctl->inchi_file+1; +INCHI_IOSTREAM prbstr, *prb_file=&prbstr; + +PINChI2 **pINChI2 = genctl->pINChI; +PINChI_Aux2 **pINChI_Aux2 = genctl->pINChI_Aux; +NORM_CANON_FLAGS *pncFlags = &(genctl->ncFlags); + + +INP_ATOM_DATA *inp_cur_data = NULL; + +long num_inp = genctl->num_inp; + +INP_ATOM_DATA *inp_norm_data[TAUT_NUM]; /* = { &InpNormAtData, &InpNormTautData }; */ +ORIG_ATOM_DATA *cur_prep_inp_data = prep_inp_data + iINChI; + +inchiTime ulTStart; + + /* To save intermediate data... */ + + COMP_ATOM_DATA *composite_norm_data = genctl->composite_norm_data[iINChI]; + INP_ATOM_DATA2 *all_inp_norm_data = NULL; + memset( composite_norm_data+TAUT_NON, 0, sizeof( composite_norm_data[0] ) ); + memset( composite_norm_data+TAUT_YES, 0, sizeof( composite_norm_data[0] ) ); + memset( composite_norm_data+TAUT_INI, 0, sizeof( composite_norm_data[0] ) ); + + inchi_ios_init(prb_file, INCHI_IOSTREAM_TYPE_FILE, NULL); + + /* + if ( orig_inp_data is NOT empty AND + prep_inp_data[0] IS empty ) then: + + 1. copy orig_inp_data --> prep_inp_data[0] + 2. fix odd things in prep_inp_data[0] + 3. if( orig_inp_data->bDisconnectSalts ) then + -- disconnect salts in prep_inp_data[0] + 4. move protons to neutralize charges on heteroatoms + 5. if( orig_inp_data->bDisconnectCoord ) then + -- copy prep_inp_data[0] --> prep_inp_data[1] + -- disconnect metals in prep_inp_data[0] + + [ This all is done in PreprocessOneStructure() ] + + iINChI = 0 + ========= + (normal/disconnected layer) + + 1. normalize prep_inp_data[0] in inp_norm_data[0,1] + 2. create INChI[ iINChI ] out of inp_norm_data[0,1] + + + iINChI = 1 AND orig_inp_data->bDisconnectCoord > 0 + ================================================= + (reconnected layer) + + 1. normalize prep_inp_data[1] in inp_norm_data[0,1] + 2. create INChI[ iINChI ] out of inp_norm_data[0,1] + + */ + + + + ip->msec_LeftTime = ip->msec_MaxTime; /* start timeout countdown for each component */ + + + + + if ( ip->bAllowEmptyStructure && !orig_inp_data->at && !orig_inp_data->num_inp_atoms ) { + ; + } else + if ( !orig_inp_data->at || !orig_inp_data->num_inp_atoms ) { + return 0; /* nothing to do */ + } + if ( iINChI == 1 && orig_inp_data->bDisconnectCoord <= 0 ) { + return 0; + } + + /* m = iINChI; */ /* orig_inp_data index */ + + if ( iINChI != INCHI_BAS && iINChI != INCHI_REC ) { + AddErrorMessage(sd->pStrErrStruct, "Fatal undetermined program error"); + sd->nStructReadError = 97; + nRet = sd->nErrorType = _IS_FATAL; + goto exit_function; + } + + /******************************************************************* + * * + * * + * Whole structure preprocessing: 1st step of the normalization * + * * + * * + * * + *******************************************************************/ + + if ( (!prep_inp_data->at || !prep_inp_data->num_inp_atoms) && orig_inp_data->num_inp_atoms > 0 ) + { + /* the structure has not been preprocessed */ + if ( ip->msec_MaxTime ) + { + InchiTimeGet( &ulTStart ); + } + + PreprocessOneStructure( ic, sd, ip, orig_inp_data, prep_inp_data ); + pncFlags->bTautFlags[iINChI][TAUT_YES] = + pncFlags->bTautFlags[iINChI][TAUT_NON] = sd->bTautFlags[INCHI_BAS] | ip->bTautFlags; + pncFlags->bTautFlagsDone[iINChI][TAUT_YES] = + pncFlags->bTautFlagsDone[iINChI][TAUT_NON] = sd->bTautFlagsDone[INCHI_BAS] | ip->bTautFlagsDone; + + switch (sd->nErrorType) + { + case _IS_ERROR: + case _IS_FATAL: + /* error message */ + nRet = TreatErrorsInReadTheStructure( sd, + ip, + LOG_MASK_ALL, + inp_file, + log_file, + out_file, + prb_file, + prep_inp_data, + &num_inp ); + goto exit_function; + } + } + + /* To save intermediate data... */ + if ( prep_inp_data[iINChI].num_components > 1) + { + all_inp_norm_data = (INP_ATOM_DATA2 *)inchi_calloc( prep_inp_data[iINChI].num_components, sizeof(all_inp_norm_data[0])); + } + + + + /* allocate pINChI[iINChI] and pINChI_Aux2[iINChI] -- arrays of pointers to INChI and INChI_Aux */ + /* assign values to sd->num_components[] */ + + MYREALLOC2(PINChI2, PINChI_Aux2, pINChI2[iINChI], pINChI_Aux2[iINChI], sd->num_components[iINChI], cur_prep_inp_data->num_components, k); + if ( k ) + { + AddErrorMessage(sd->pStrErrStruct, "Cannot allocate output data. Terminating"); + sd->nStructReadError = 99; + sd->nErrorType = _IS_FATAL; + goto exit_function; + } + + + /* Allocate */ + + /* visible */ + gendata->NormAtomsNontaut[iINChI] = (NORM_ATOMS *)inchi_calloc( sd->num_components[iINChI], sizeof(NORM_ATOMS)); + gendata->NormAtomsTaut[iINChI] = (NORM_ATOMS *)inchi_calloc( sd->num_components[iINChI], sizeof(NORM_ATOMS)); + /* invisible */ + genctl->InpNormAtData[iINChI] = (INP_ATOM_DATA *)inchi_calloc( sd->num_components[iINChI], sizeof(INP_ATOM_DATA)); + genctl->InpNormTautData[iINChI] = (INP_ATOM_DATA *)inchi_calloc( sd->num_components[iINChI], sizeof(INP_ATOM_DATA)); + genctl->InpCurAtData[iINChI] = (INP_ATOM_DATA *)inchi_calloc( sd->num_components[iINChI], sizeof(INP_ATOM_DATA)); + genctl->cti[iINChI] = (COMPONENT_TREAT_INFO *)inchi_calloc( sd->num_components[iINChI], sizeof(COMPONENT_TREAT_INFO)); + memset (genctl->cti[iINChI], 0, sd->num_components[iINChI]*sizeof(COMPONENT_TREAT_INFO)); + + + + + /* Second normalization step - component by component */ + + + for ( i = 0, nRet = 0; !sd->bUserQuitComponent && i < cur_prep_inp_data->num_components; i ++ ) + { + + if (ip->msec_MaxTime) InchiTimeGet( &ulTStart ); + + inp_cur_data = &(genctl->InpCurAtData[iINChI][i]); + + /* a) allocate memory and extract current component */ + nRet = GetOneComponent( ic, sd, ip, log_file, out_file, inp_cur_data, + cur_prep_inp_data, i, num_inp ); + + if (ip->msec_MaxTime) + ip->msec_LeftTime -= InchiTimeElapsed( ic, &ulTStart ); + + switch (nRet) + { + case _IS_ERROR: + case _IS_FATAL: + goto exit_cycle; + } + + + + /* c) Create the component's INChI ( copies ip->bTautFlags into sd->bTautFlags)*/ + + inp_norm_data[TAUT_NON] = &(genctl->InpNormAtData[iINChI][i]); + memset( inp_norm_data[TAUT_NON], 0, sizeof( *inp_norm_data[0] ) ); + inp_norm_data[TAUT_YES] = &(genctl->InpNormTautData[iINChI][i]); + memset( inp_norm_data[TAUT_YES], 0, sizeof( *inp_norm_data[0] ) ); + + + nRet = NormOneComponentINChI( pCG, ic, genctl, iINChI, i); + + /* To save intermediate data... */ + if ( all_inp_norm_data ) + { + for ( j = 0; j < TAUT_NUM; j ++ ) + { + if ( inp_norm_data[j]->bExists ) + { + all_inp_norm_data[i][j] = *inp_norm_data[j]; + memset( inp_norm_data[j], 0, sizeof(*inp_norm_data[0]) ); + } + } + } + + + if (nRet) + { + nRet = TreatErrorsInCreateOneComponentINChI( sd, + ip, + cur_prep_inp_data, + i, + num_inp, + inp_file, + log_file, + out_file, + prb_file ); + break; + } + } + + +exit_cycle: + + switch ( nRet ) + { + case _IS_FATAL: + case _IS_ERROR: break; + default: + + /* To save intermediate data... */ + if ( all_inp_norm_data ) + { + CreateCompositeNormAtom( composite_norm_data, + all_inp_norm_data, + prep_inp_data[iINChI].num_components); + } + break; + } + + + /* When saving intermediate data - avoid memory leaks in case of error */ + if ( all_inp_norm_data ) + { + for ( i = 0; i < prep_inp_data[iINChI].num_components; i ++ ) + { + for ( k = 0; k < TAUT_NUM; k ++ ) + { + FreeInpAtomData( &all_inp_norm_data[i][k] ); + } + } + inchi_free( all_inp_norm_data ); + all_inp_norm_data = NULL; + } + + + + + + + +exit_function: + + return nRet; +} + + +int CanonOneStructureINChI( CANON_GLOBALS *pCG, + INCHI_CLOCK *ic, + INCHIGEN_CONTROL *genctl, + int iINChI, + INCHI_IOSTREAM *inp_file) +{ +int i, /*m,*/ nRet = 0; + +STRUCT_DATA *sd = &(genctl->StructData); + +INPUT_PARMS *ip = &(genctl->InpParms); +INCHI_IOSTREAM *out_file = genctl->inchi_file, *log_file = genctl->inchi_file+1; +INCHI_IOSTREAM prbstr, *prb_file=&prbstr; + +ORIG_ATOM_DATA *prep_inp_data = &(genctl->PrepInpData[0]); + +long num_inp = genctl->num_inp; + + + + INP_ATOM_DATA *inp_cur_data = NULL; + + INP_ATOM_DATA *inp_norm_data[TAUT_NUM]; /* = { &InpNormAtData, &InpNormTautData }; */ + + ORIG_ATOM_DATA *cur_prep_inp_data = prep_inp_data + iINChI; + + inchiTime ulTStart; + + inchi_ios_init(prb_file, INCHI_IOSTREAM_TYPE_FILE, NULL); + + for (i = 0; i < TAUT_NUM; i ++) /* initialize in case no InChI to generate 2008-12-23 DT */ + inp_norm_data[i]=NULL; + + /**************************************************************************/ + /* */ + /* */ + /* M A I N C Y C L E: P R O C E S S C O M P O N E N T S */ + /* */ + /* */ + /* O N E B Y O N E */ + /* */ + /* */ + /**************************************************************************/ + + for ( i = 0, nRet = 0; !sd->bUserQuitComponent && i < cur_prep_inp_data->num_components; i ++ ) + { + + if ( ip->msec_MaxTime ) + InchiTimeGet( &ulTStart ); + + + + + /*****************************************************/ + /* a) allocate memory and extract current component */ + /*****************************************************/ + + inp_cur_data = &(genctl->InpCurAtData[iINChI][i]); + + + nRet = GetOneComponent( ic, sd, ip, log_file, out_file, + inp_cur_data, cur_prep_inp_data, + i, num_inp ); + + if ( ip->msec_MaxTime ) + ip->msec_LeftTime -= InchiTimeElapsed( ic, &ulTStart ); + + switch ( nRet ) { case _IS_ERROR: case _IS_FATAL: goto exit_cycle; } + +#ifndef TARGET_API_LIB + /* console request: Display the component? */ + if ( ip->bDisplay && inp_file != stdin ) + { + if ( user_quit("Enter=Display Component, Esc=Stop ?", ip->ulDisplTime) ) + { + sd->bUserQuitComponent = 1; + break; + } + } +#endif + + + + + /*******************************************************************************/ + /* */ + /* C A N O N I C A L I Z A T I O N */ + /* */ + /* (both tautomeric and non-tautomeric if requested) */ + /* */ + /*******************************************************************************/ + /* c) Create the component's INChI ( copies ip->bTautFlags into sd->bTautFlags)*/ + /*******************************************************************************/ + + inp_norm_data[TAUT_NON] = &(genctl->InpNormAtData[iINChI][i]); + inp_norm_data[TAUT_YES] = &(genctl->InpNormTautData[iINChI][i]); + + nRet = CanonOneComponentINChI( pCG, ic, genctl, iINChI, i); + + + + + if ( nRet ) + { + nRet = TreatErrorsInCreateOneComponentINChI( sd, + ip, + cur_prep_inp_data, + i, + num_inp, + inp_file, + log_file, + out_file, + prb_file ); + break; + } + } + /**************************************************************************/ + /* */ + /* */ + /* E N D O F T H E M A I N C Y C L E P R O C E S S I N G */ + /* */ + /* C O M P O N E N T S O N E B Y O N E */ + /* */ + /* */ + /**************************************************************************/ + + +exit_cycle: + + switch ( nRet ) + { + case _IS_FATAL: + case _IS_ERROR: break; + default: + + break; + } + + + + + for (i = 0; i < TAUT_NUM; i ++) + FreeInpAtomData( inp_norm_data[i] ); + + + + return nRet; +} + + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +int NormOneComponentINChI( CANON_GLOBALS *pCG, + INCHI_CLOCK *ic, + INCHIGEN_CONTROL * genctl, + int iINChI, + int i) +{ +STRUCT_DATA *sd = &(genctl->StructData); +INPUT_PARMS *ip = &(genctl->InpParms); +PINChI2 **pINChI2 = genctl->pINChI; +PINChI_Aux2 **pINChI_Aux2 = genctl->pINChI_Aux; +NORM_CANON_FLAGS *pncFlags = &(genctl->ncFlags); + + inchiTime ulTStart, ulTEnd, *pulTEnd = NULL; + int k, num_at, ret = 0; + int bOrigCoord; + INCHI_MODE bTautFlags = ip->bTautFlags; + INCHI_MODE bTautFlagsDone = (ip->bTautFlagsDone | sd->bTautFlagsDone[INCHI_BAS]); + long lElapsedTime; + /* + PINChI2 *pINChI = pINChI2[iINChI]; + PINChI_Aux2 *pINChI_Aux = pINChI_Aux2[iINChI]; + */ + +PINChI2 *pINChI = NULL; +PINChI_Aux2 *pINChI_Aux = NULL; + +INChI *cur_INChI[TAUT_NUM]; +INChI_Aux *cur_INChI_Aux[TAUT_NUM]; + + +/* pINChI2[m=iINChI-1][j< prep_inp_data[m].num_components][TAUT_NON] */ + +INP_ATOM_DATA *inp_norm_data[TAUT_NUM]; /* = { &InpNormAtData, &InpNormTautData }; */ +INP_ATOM_DATA *inp_cur_data = NULL; + +COMPONENT_TREAT_INFO *cti = NULL; + + + + inp_cur_data = &(genctl->InpCurAtData[iINChI][i]); + + cti = &(genctl->cti[iINChI][i]); + + inp_norm_data[TAUT_NON] = &(genctl->InpNormAtData[iINChI][i]); + inp_norm_data[TAUT_YES] = &(genctl->InpNormTautData[iINChI][i]); + + + pINChI = pINChI2[iINChI]; + pINChI_Aux = pINChI_Aux2[iINChI]; + + + InchiTimeGet( &ulTStart ); + bOrigCoord = !(ip->bINChIOutputOptions & (INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO)); + + + for ( k = 0; k < TAUT_NUM; k ++ ) + { + cur_INChI[k] = pINChI[i][k]; + cur_INChI_Aux[k] = pINChI_Aux[i][k]; + } + + + /* allocate memory for non-tautimeric (k=0) and tautomeric (k=1) results */ + for ( k = 0; k < TAUT_NUM; k ++ ) + { + int nAllocMode = (k==TAUT_YES? REQ_MODE_TAUT:0) | + (bTautFlagsDone & ( TG_FLAG_FOUND_ISOTOPIC_H_DONE | + TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE ))? + (ip->nMode & REQ_MODE_ISO):0; + + if ( k==TAUT_NON && (ip->nMode & REQ_MODE_BASIC ) || + k==TAUT_YES && (ip->nMode & REQ_MODE_TAUT ) ) + { + /* alloc INChI and INChI_Aux */ + cur_INChI[k] = Alloc_INChI( inp_cur_data->at, inp_cur_data->num_at, &inp_cur_data->num_bonds, + &inp_cur_data->num_isotopic, nAllocMode ); + cur_INChI_Aux[k] = Alloc_INChI_Aux( inp_cur_data->num_at, + inp_cur_data->num_isotopic, nAllocMode, bOrigCoord ); + if ( cur_INChI_Aux[k] ) + cur_INChI_Aux[k]->bIsIsotopic = inp_cur_data->num_isotopic; + /* alloc memory for the output structure: non-tautomeric and tautomeric (for displaying) */ + CreateInpAtomData( inp_norm_data[k], inp_cur_data->num_at, k ); + } + else + FreeInpAtomData( inp_norm_data[k] ); + } + + lElapsedTime = InchiTimeElapsed( ic, &ulTStart ); + if ( ip->msec_MaxTime ) + ip->msec_LeftTime -= lElapsedTime; + sd->ulStructTime += lElapsedTime; + + + /****************************************************** + * + * Get one component canonical numberings, etc. + * + ******************************************************/ + + /* + * Create_INChI() return value: + * num_at <= 0: error code + * num_at > 0: number of atoms (excluding terminal hydrogen atoms) + * inp_norm_data[0] => non-tautomeric, inp_norm_data[1] => tautomeric + */ + InchiTimeGet( &ulTStart ); + if ( ip->msec_MaxTime ) + { + ulTEnd = ulTStart; + pulTEnd = &ulTEnd; + if ( ip->msec_LeftTime > 0 ) + InchiTimeAddMsec( ic, pulTEnd, ip->msec_LeftTime ); + } + + + cti->nUserMode = ip->nMode; + + /* vABParityUnknown holds actual value of an internal constant signifying */ + /* unknown parity: either the same as for undefined parity (default==standard) */ + /* or a specific one (non-std; requested by SLUUD switch). */ + cti->vABParityUnknown = AB_PARITY_UNDF; + if ( 0 != ( ip->nMode & REQ_MODE_DIFF_UU_STEREO) ) + { + /* Make labels for unknown and undefined stereo different */ + cti->vABParityUnknown = AB_PARITY_UNKN; + } + + num_at = Normalization_step( pCG, ic, + cur_INChI, cur_INChI_Aux, + inp_cur_data->at, inp_norm_data, inp_cur_data->num_at, + pulTEnd, &bTautFlags, &bTautFlagsDone, cti); + + SetConnectedComponentNumber( inp_cur_data->at, inp_cur_data->num_at, i+1 ); /* normalization alters structure component number */ + + for ( k = 0; k < TAUT_NUM; k ++ ) + { + if ( cur_INChI_Aux[k] && cur_INChI_Aux[k]->nNumberOfAtoms > 0 ) + { + pncFlags->bNormalizationFlags[iINChI][k] |= cur_INChI_Aux[k]->bNormalizationFlags; + pncFlags->bTautFlags[iINChI][k] |= cur_INChI_Aux[k]->bTautFlags; + pncFlags->bTautFlagsDone[iINChI][k] |= cur_INChI_Aux[k]->bTautFlagsDone; + pncFlags->nCanonFlags[iINChI][k] |= cur_INChI_Aux[k]->nCanonFlags; + } + } + + /* Detect errors */ + if ( num_at < 0 ) + sd->nErrorCode = num_at; + else if ( num_at == 0 ) + sd->nErrorCode = -1; + else if ( cur_INChI[TAUT_NON] && cur_INChI[TAUT_NON]->nErrorCode ) + /* non-tautomeric error */ + sd->nErrorCode = cur_INChI[TAUT_NON]->nErrorCode; + else if ( cur_INChI[TAUT_YES] && cur_INChI[TAUT_YES]->nErrorCode ) + /* tautomeric error */ + sd->nErrorCode = cur_INChI[TAUT_YES]->nErrorCode; + + /* detect and store stereo warnings */ + if ( !sd->nErrorCode ) + GetProcessingWarningsOneComponentInChI(cur_INChI, inp_norm_data, sd); + + + lElapsedTime = InchiTimeElapsed( ic, &ulTStart ); + if ( ip->msec_MaxTime ) + ip->msec_LeftTime -= lElapsedTime; + + sd->ulStructTime += lElapsedTime; +#ifndef TARGET_API_LIB + /* Display the results */ + if ( ip->bDisplay ) + eat_keyboard_input(); +#endif + /* a) No matter what happened save the allocated INChI pointers */ + /* save the INChI of the current component */ + + InchiTimeGet( &ulTStart ); + for ( k = 0; k < TAUT_NUM; k ++ ) + { + pINChI[i][k] = cur_INChI[k]; + pINChI_Aux[i][k] = cur_INChI_Aux[k]; + + cur_INChI[k] = NULL; + cur_INChI_Aux[k] = NULL; + } + + /* b) Count one component structure and/or INChI results only if there was no error */ + /* Set inp_norm_data[j]->num_removed_H = number of removed explicit H */ + + if ( !sd->nErrorCode ) + { + + /* find where the current processed structure is located */ + int cur_is_in_non_taut = (pINChI[i][TAUT_NON] && pINChI[i][TAUT_NON]->nNumberOfAtoms>0); + int cur_is_in_taut = (pINChI[i][TAUT_YES] && pINChI[i][TAUT_YES]->nNumberOfAtoms>0); + int cur_is_non_taut = cur_is_in_non_taut && 0 == pINChI[i][TAUT_NON]->lenTautomer || + cur_is_in_taut && 0 == pINChI[i][TAUT_YES]->lenTautomer; + int cur_is_taut = cur_is_in_taut && 0 < pINChI[i][TAUT_YES]->lenTautomer; + + if ( cur_is_non_taut + cur_is_taut ) + { + /* count tautomeric and non-tautomeric components of the structures */ + int j1 = cur_is_in_non_taut? TAUT_NON:TAUT_YES; + int j2 = cur_is_in_taut? TAUT_YES:TAUT_NON; + int j; + sd->num_non_taut[iINChI] += cur_is_non_taut; + sd->num_taut[iINChI] += cur_is_taut; + for ( j = j1; j <= j2; j ++ ) + { + int bIsotopic = (pINChI[i][j]->nNumberOfIsotopicAtoms || + pINChI[i][j]->nNumberOfIsotopicTGroups || + pINChI[i][j]->nPossibleLocationsOfIsotopicH && pINChI[i][j]->nPossibleLocationsOfIsotopicH[0]>1); + if ( j == TAUT_YES ) { + bIsotopic |= (0 < pINChI_Aux[i][j]->nNumRemovedIsotopicH[0] + + pINChI_Aux[i][j]->nNumRemovedIsotopicH[1] + + pINChI_Aux[i][j]->nNumRemovedIsotopicH[2]); + } + inp_norm_data[j]->bExists = 1; /* j=0: non-taut exists, j=1: taut exists */ + inp_norm_data[j]->bHasIsotopicLayer = bIsotopic; + } + } + } + + if ( sd->nErrorCode==CT_OUT_OF_RAM || sd->nErrorCode==CT_USER_QUIT_ERR ) + ret = _IS_FATAL; + else if ( sd->nErrorCode ) + ret = _IS_ERROR; + + lElapsedTime = InchiTimeElapsed( ic, &ulTStart ); + if ( ip->msec_MaxTime ) + ip->msec_LeftTime -= lElapsedTime; + + sd->ulStructTime += lElapsedTime; + return ret; +} + + +int CanonOneComponentINChI( CANON_GLOBALS *pCG, + INCHI_CLOCK *ic, + INCHIGEN_CONTROL *genctl, + int iINChI, + int i) +{ +STRUCT_DATA *sd = &(genctl->StructData); +INPUT_PARMS *ip = &(genctl->InpParms); +PINChI2 **pINChI2 = genctl->pINChI; +PINChI_Aux2 **pINChI_Aux2 = genctl->pINChI_Aux; +NORM_CANON_FLAGS *pncFlags = &(genctl->ncFlags); + + inchiTime ulTStart, ulTEnd, *pulTEnd = NULL; + int k, num_at, ret = 0; + INChI *cur_INChI[TAUT_NUM]; + INChI_Aux *cur_INChI_Aux[TAUT_NUM]; + long lElapsedTime; + /* + PINChI2 *pINChI = pINChI2[iINChI]; + PINChI_Aux2 *pINChI_Aux = pINChI_Aux2[iINChI]; + */ + +PINChI2 *pINChI = NULL; +PINChI_Aux2 *pINChI_Aux = NULL; + +INP_ATOM_DATA *inp_norm_data[TAUT_NUM]; /* = { &InpNormAtData, &InpNormTautData }; */ +INP_ATOM_DATA *inp_cur_data = NULL; + +COMPONENT_TREAT_INFO *cti = NULL; + + + inp_cur_data = &(genctl->InpCurAtData[iINChI][i]); + + cti = &(genctl->cti[iINChI][i]); + + inp_norm_data[TAUT_NON] = &(genctl->InpNormAtData[iINChI][i]); + inp_norm_data[TAUT_YES] = &(genctl->InpNormTautData[iINChI][i]); + + + + pINChI = pINChI2[iINChI]; + pINChI_Aux = pINChI_Aux2[iINChI]; + + InchiTimeGet( &ulTStart ); + + for ( k = 0; k < TAUT_NUM; k ++ ) + { + cur_INChI[k] = pINChI[i][k]; + cur_INChI_Aux[k] = pINChI_Aux[i][k]; + } + + + + lElapsedTime = InchiTimeElapsed( ic, &ulTStart ); + if ( ip->msec_MaxTime ) { + ip->msec_LeftTime -= lElapsedTime; + } + sd->ulStructTime += lElapsedTime; + + + /****************************************************** + * + * Get one component canonical numberings, etc. + * + ******************************************************/ + + /* + * Create_INChI() return value: + * num_at <= 0: error code + * num_at > 0: number of atoms (excluding terminal hydrogen atoms) + * inp_norm_data[0] => non-tautomeric, inp_norm_data[1] => tautomeric + */ + InchiTimeGet( &ulTStart ); + if ( ip->msec_MaxTime ) { + ulTEnd = ulTStart; + pulTEnd = &ulTEnd; + if ( ip->msec_LeftTime > 0 ) { + InchiTimeAddMsec( ic, pulTEnd, ip->msec_LeftTime ); + } + } + num_at = Canonicalization_step( pCG, ic, cur_INChI, cur_INChI_Aux, + inp_norm_data, pulTEnd, NULL, sd->pStrErrStruct, + cti, ip->bLargeMolecules ); + +#ifndef FIX_DOCANON_RETCODE_RESET_BUG +/* The next line erroneously resets num_at which just became the return value */ +/* of canon procedure. Thanks to David Foss of Accelrys for finding this. 2013-01-24 */ + num_at = cti->num_atoms; +#endif + + SetConnectedComponentNumber( inp_cur_data->at, inp_cur_data->num_at, i+1 ); /* normalization alters structure component number */ + for ( k = 0; k < TAUT_NUM; k ++ ) + { + if ( cur_INChI_Aux[k] && cur_INChI_Aux[k]->nNumberOfAtoms > 0 ) + { + pncFlags->bNormalizationFlags[iINChI][k] |= cur_INChI_Aux[k]->bNormalizationFlags; + pncFlags->bTautFlags[iINChI][k] |= cur_INChI_Aux[k]->bTautFlags; + pncFlags->bTautFlagsDone[iINChI][k] |= cur_INChI_Aux[k]->bTautFlagsDone; + pncFlags->nCanonFlags[iINChI][k] |= cur_INChI_Aux[k]->nCanonFlags; + } + } + + /* Detect errors */ + if ( num_at < 0 ) + sd->nErrorCode = num_at; + else if ( num_at == 0 ) + sd->nErrorCode = -1; + else if ( cur_INChI[TAUT_NON] && cur_INChI[TAUT_NON]->nErrorCode ) + /* non-tautomeric error */ + sd->nErrorCode = cur_INChI[TAUT_NON]->nErrorCode; + else if ( cur_INChI[TAUT_YES] && cur_INChI[TAUT_YES]->nErrorCode ) + /* tautomeric error */ + sd->nErrorCode = cur_INChI[TAUT_YES]->nErrorCode; + + /* detect and store stereo warnings */ + if ( !sd->nErrorCode ) + GetProcessingWarningsOneComponentInChI(cur_INChI, inp_norm_data, sd); + + + lElapsedTime = InchiTimeElapsed( ic, &ulTStart ); + if ( ip->msec_MaxTime ) + ip->msec_LeftTime -= lElapsedTime; + + sd->ulStructTime += lElapsedTime; +#ifndef TARGET_API_LIB + /* Display the results */ + if ( ip->bDisplay ) + eat_keyboard_input(); +#endif + /* a) No matter what happened save the allocated INChI pointers */ + /* save the INChI of the current component */ + + InchiTimeGet( &ulTStart ); + for ( k = 0; k < TAUT_NUM; k ++ ) + { + pINChI[i][k] = cur_INChI[k]; + pINChI_Aux[i][k] = cur_INChI_Aux[k]; + + cur_INChI[k] = NULL; + cur_INChI_Aux[k] = NULL; + } + + /* b) Count one component structure and/or INChI results only if there was no error */ + /* Set inp_norm_data[j]->num_removed_H = number of removed explicit H */ + + if ( !sd->nErrorCode ) + { + + /* find where the current processed structure is located */ + int cur_is_in_non_taut = (pINChI[i][TAUT_NON] && pINChI[i][TAUT_NON]->nNumberOfAtoms>0); + int cur_is_in_taut = (pINChI[i][TAUT_YES] && pINChI[i][TAUT_YES]->nNumberOfAtoms>0); + int cur_is_non_taut = cur_is_in_non_taut && 0 == pINChI[i][TAUT_NON]->lenTautomer || + cur_is_in_taut && 0 == pINChI[i][TAUT_YES]->lenTautomer; + int cur_is_taut = cur_is_in_taut && 0 < pINChI[i][TAUT_YES]->lenTautomer; + + if ( cur_is_non_taut + cur_is_taut ) + { + /* count tautomeric and non-tautomeric components of the structures */ + int j1 = cur_is_in_non_taut? TAUT_NON:TAUT_YES; + int j2 = cur_is_in_taut? TAUT_YES:TAUT_NON; + int j; + sd->num_non_taut[iINChI] += cur_is_non_taut; + sd->num_taut[iINChI] += cur_is_taut; + for ( j = j1; j <= j2; j ++ ) + { + int bIsotopic = (pINChI[i][j]->nNumberOfIsotopicAtoms || + pINChI[i][j]->nNumberOfIsotopicTGroups || + pINChI[i][j]->nPossibleLocationsOfIsotopicH && pINChI[i][j]->nPossibleLocationsOfIsotopicH[0]>1); + if ( j == TAUT_YES ) { + bIsotopic |= (0 < pINChI_Aux[i][j]->nNumRemovedIsotopicH[0] + + pINChI_Aux[i][j]->nNumRemovedIsotopicH[1] + + pINChI_Aux[i][j]->nNumRemovedIsotopicH[2]); + } + inp_norm_data[j]->bExists = 1; /* j=0: non-taut exists, j=1: taut exists */ + inp_norm_data[j]->bHasIsotopicLayer = bIsotopic; + } + } + } + + if ( sd->nErrorCode==CT_OUT_OF_RAM || sd->nErrorCode==CT_USER_QUIT_ERR ) + ret = _IS_FATAL; + else if ( sd->nErrorCode ) + ret = _IS_ERROR; + + lElapsedTime = InchiTimeElapsed( ic, &ulTStart ); + if ( ip->msec_MaxTime ) + ip->msec_LeftTime -= lElapsedTime; + + sd->ulStructTime += lElapsedTime; + return ret; +} + + + +int Normalization_step( CANON_GLOBALS *pCG, + INCHI_CLOCK *ic, + INChI **ppINChI, + INChI_Aux **ppINChI_Aux, + inp_ATOM *inp_at, + INP_ATOM_DATA *out_norm_data[2], + int num_inp_at, + struct tagInchiTime *ulMaxTime, + INCHI_MODE *pbTautFlags, + INCHI_MODE *pbTautFlagsDone, + COMPONENT_TREAT_INFO *z) +{ + +int i,ret=0; + + + +T_GROUP_INFO * /*const*/ t_group_info = &(z->vt_group_info); +T_GROUP_INFO * /*const*/ t_group_info_orig = &(z->vt_group_info_orig); + + + BCN *pBCN = &(z->Bcn); + + /* */ + z->fix_isofixedh = 0; + z->fix_termhchrg = 0; + /* */ + #if( FIX_ISO_FIXEDH_BUG == 1 ) + if (TG_FLAG_FIX_ISO_FIXEDH_BUG & *pbTautFlags) + z->fix_isofixedh = 1; + #endif +#if( FIX_TERM_H_CHRG_BUG == 1 ) + if (TG_FLAG_FIX_TERM_H_CHRG_BUG & *pbTautFlags) + z->fix_termhchrg = 1; + #endif + + + z->bPointedEdgeStereo = ((TG_FLAG_POINTED_EDGE_STEREO & *pbTautFlags)? PES_BIT_POINT_EDGE_STEREO:0) + | ((TG_FLAG_PHOSPHINE_STEREO & *pbTautFlags)? PES_BIT_PHOSPHINE_STEREO :0) + | ((TG_FLAG_ARSINE_STEREO & *pbTautFlags)? PES_BIT_ARSINE_STEREO :0) + | ((TG_FLAG_FIX_SP3_BUG & *pbTautFlags)? PES_BIT_FIX_SP3_BUG :0); + z->bTautFlags = (*pbTautFlags & (~(INCHI_MODE)TG_FLAG_ALL_TAUTOMERIC) ); + z->bTautFlagsDone = (*pbTautFlagsDone /*& (~(INCHI_MODE)TG_FLAG_ALL_TAUTOMERIC) */); + + z->out_at = NULL; /*, *norm_at_fixed_bonds[TAUT_NUM]; */ /* = {out_norm_nontaut_at, out_norm_taut_at} ; */ + + /* Init: internal structs */ + + memset( z->s, 0, sizeof(z->s) ); + + if ( pBCN ) memset( pBCN, 0, sizeof( pBCN[0] ) ); + + memset( t_group_info, 0, sizeof(*t_group_info) ); + memset( t_group_info_orig, 0, sizeof(*t_group_info_orig) ); + + + /* Allocate: at[] */ + + for ( i = 0; i < TAUT_NUM; i ++ ) + { + if ( out_norm_data[i]->at ) + { + z->at[i] = + (sp_ATOM *) inchi_malloc( num_inp_at * sizeof(*(z->at[0])) ); + + if ( !z->at[i] ) ret = -1; + } + else + z->at[i] = NULL; + } + + if ( !out_norm_data[TAUT_NON]->at && !out_norm_data[TAUT_YES]->at || !inp_at || ret ) + { + ret = -1; + goto exit_function; + } + + /* the first struct to process: tautomeric if exists else non-tautomeric */ + + z->out_at = out_norm_data[TAUT_YES]->at? out_norm_data[TAUT_YES]->at : out_norm_data[TAUT_NON]->at; + + /* copy the input structure to be normalized to the buffer for the normalization data */ + + memcpy( z->out_at, inp_at, num_inp_at*sizeof(z->out_at[0]) ); + + + /* tautomeric groups setting */ + + t_group_info->bIgnoreIsotopic = 0; /* include tautomeric group isotopic info in MarkTautomerGroups() */ + t_group_info->bTautFlags = *pbTautFlags; + t_group_info->bTautFlagsDone = *pbTautFlagsDone; + + + /* Preprocess the structure; here THE NUMBER OF ATOMS MAY BE REDUCED */ + /* ??? Ambiguity: H-D may become HD or DH (that is, H+implicit D or D+implicit H) */ + + if ( TG_FLAG_H_ALREADY_REMOVED & z->bTautFlags ) + { + INP_ATOM_DATA *out_norm_data1 = out_norm_data[TAUT_YES]->at? out_norm_data[TAUT_YES] : + out_norm_data[TAUT_NON]->at? out_norm_data[TAUT_NON] : NULL; + if ( out_norm_data1 ) + { + z->num_at_tg = + z->num_atoms = out_norm_data1->num_at - out_norm_data1->num_removed_H; + z->num_deleted_H = out_norm_data1->num_removed_H; + t_group_info->tni.nNumRemovedExplicitH = z->num_deleted_H; + } + else + { + ret = -1; + goto exit_function; + } + } + else + { + z->num_at_tg = z->num_atoms = remove_terminal_HDT( num_inp_at, z->out_at, z->fix_termhchrg); + + z->num_deleted_H = num_inp_at - z->num_atoms; + t_group_info->tni.nNumRemovedExplicitH = z->num_deleted_H; + + add_DT_to_num_H( z->num_atoms, z->out_at ); + } + + + + /* fix_odd_things( z->num_atoms, z->out_at );*/ + + +#if( FIND_RING_SYSTEMS == 1 ) + MarkRingSystemsInp( z->out_at, z->num_atoms, 0 ); +#endif + + /* duplicate the preprocessed structure so that all supplied out_norm_data[]->at buffers are filled */ + if ( z->out_at != out_norm_data[TAUT_YES]->at && out_norm_data[TAUT_YES]->at ) + memcpy( out_norm_data[TAUT_YES]->at, z->out_at, num_inp_at*sizeof(z->out_at[0]) ); + + if ( out_norm_data[TAUT_YES]->at_fixed_bonds && out_norm_data[TAUT_YES]->at ) + memcpy( out_norm_data[TAUT_YES]->at_fixed_bonds, z->out_at, num_inp_at*sizeof(z->out_at[0]) ); + + if ( z->out_at != out_norm_data[TAUT_NON]->at && out_norm_data[TAUT_NON]->at ) + memcpy( out_norm_data[TAUT_NON]->at, z->out_at, num_inp_at*sizeof(z->out_at[0]) ); + + + /******************************************************************************* + * ??? not true ??? duplicate inp_at and keep inp_at[] unchanged after terminal hydrogens removal + * set stereo parities in taut_at[], non_taut_at[] + * obtain max. lenghts of the name stereo parts + * Ignore absence/presence of isotopic stereo for now + * mark isotopic atoms + *******************************************************************************/ + + if ( out_norm_data[TAUT_YES]->at && z->at[TAUT_YES] ) + { + + /* final normalization of possibly tautomeric structure */ + + ret = mark_alt_bonds_and_taut_groups ( ic, pCG, + out_norm_data[TAUT_YES]->at, + out_norm_data[TAUT_YES]->at_fixed_bonds, + z->num_atoms, ulMaxTime, t_group_info, + NULL, NULL, 0, NULL ); + + if ( ret < 0 ) + goto exit_function;/* out of RAM or other normalization problem */ + + z->num_taut_at = ret; /* number of atoms without removed H? */ + z->num_deleted_H_taut = t_group_info->tni.nNumRemovedExplicitH; + out_norm_data[TAUT_YES]->num_at = z->num_atoms + z->num_deleted_H_taut; /* protons might have been removed */ + out_norm_data[TAUT_YES]->num_removed_H = z->num_deleted_H_taut; + out_norm_data[TAUT_YES]->nNumRemovedProtons += t_group_info->tni.nNumRemovedProtons; + + for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) + { + out_norm_data[TAUT_YES]->nNumRemovedProtonsIsotopic[i] += t_group_info->tni.nNumRemovedProtonsIsotopic[i] /*+ t_group_info->num_iso_H[i]*/; + out_norm_data[TAUT_YES]->num_iso_H[i] += t_group_info->num_iso_H[i]; + } + + /* mark deleted isolated tautomeric H(+) */ + + if ( z->num_taut_at == 1 && out_norm_data[TAUT_YES]->at[0].at_type == ATT_PROTON && + t_group_info && t_group_info->tni.nNumRemovedProtons == 1 ) + { + out_norm_data[TAUT_YES]->bDeleted = 1; + + FreeInpAtom( &out_norm_data[TAUT_YES]->at_fixed_bonds ); + } + else if ( (t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) && + out_norm_data[TAUT_YES]->at_fixed_bonds) + { + out_norm_data[TAUT_YES]->bTautPreprocessed = 1; + } + + out_norm_data[TAUT_YES]->bTautFlags = *pbTautFlags = t_group_info->bTautFlags; + out_norm_data[TAUT_YES]->bTautFlagsDone = *pbTautFlagsDone = t_group_info->bTautFlagsDone; + out_norm_data[TAUT_YES]->bNormalizationFlags = t_group_info->tni.bNormalizationFlags; + + /* create internal sp_ATOM at[] out of out_norm_data[]->at */ + + inp2spATOM( out_norm_data[TAUT_YES]->at, num_inp_at, z->at[TAUT_YES] ); + + + + /* set stereo parities to at[]; nUserMode: accept alt. stereo bonds, min ring size */ + + ret = set_stereo_parity( pCG, + out_norm_data[TAUT_YES]->at, + z->at[TAUT_YES], + z->num_taut_at, + z->num_deleted_H_taut, + &(z->s[TAUT_YES].nMaxNumStereoAtoms), + &(z->s[TAUT_YES].nMaxNumStereoBonds), + z->nUserMode, + z->bPointedEdgeStereo, + z->vABParityUnknown ); + + if ( RETURNED_ERROR(ret) ) goto exit_function; /* stereo bond error */ + + + z->s[TAUT_YES].bMayHaveStereo = (z->s[TAUT_YES].nMaxNumStereoAtoms || z->s[TAUT_YES].nMaxNumStereoBonds); + + /* + * mark isotopic atoms and atoms that have non-tautomeric + * isotopic terminal hydrogen atoms 1H, 2H(D), 3H(T) + */ + + z->s[TAUT_YES].num_isotopic_atoms = + + set_atom_iso_sort_keys( z->num_taut_at, z->at[TAUT_YES], t_group_info, + &(z->s[TAUT_YES].bHasIsotopicTautGroups) ); + + + /************************************************************************** + * prepare tautomeric (if no tautomerism found then prepare non-tautomeric) + * structure for canonicalizaton: + ************************************************************************** + * remove t-groups that have no H, + * remove charges from t-groups if requested + * renumber t-groups and find final t_group_info->num_t_groups + * add to t-groups lists of endpoints tgroup->nEndpointAtomNumber[] + * calculate length of the t-group part of the connection table + **************************************************************************/ + + z->s[TAUT_YES].nLenLinearCTTautomer = + + CountTautomerGroups( z->at[TAUT_YES], z->num_taut_at, t_group_info ); + + + if ( RETURNED_ERROR(z->s[TAUT_YES].nLenLinearCTTautomer) ) + { + /* added error treatment 9-11-2003 */ + ret = z->s[TAUT_YES].nLenLinearCTTautomer; + goto exit_function; + /* error has happened; no breakpoint here + z->s[TAUT_YES].nLenLinearCTTautomer = 0; + */ + } + else if ( z->s[TAUT_YES].nLenLinearCTTautomer > 0 ) + { + z->num_at_tg = z->num_taut_at+t_group_info->num_t_groups; + + /* ??? -not true- create t_group_info_orig for multiple calls with atom renumbering */ + + make_a_copy_of_t_group_info( t_group_info_orig /* dest*/, t_group_info /* source*/ ); + + /* mark isotopic tautomer groups: calculate t_group->iWeight */ + z->s[TAUT_YES].nLenLinearCTIsotopicTautomer=set_tautomer_iso_sort_keys( t_group_info ); + if ( z->s[TAUT_YES].nLenLinearCTIsotopicTautomer < 0 ) + { + /* ??? -error cannot happen- error has happened; no breakpoint here */ + z->s[TAUT_YES].nLenLinearCTIsotopicTautomer = 0; + } + out_norm_data[TAUT_YES]->bTautomeric = z->s[TAUT_YES].nLenLinearCTTautomer; + } + + /* new variable: z->s[TAUT_YES].nLenCT introduced 7-22-2002 */ + + GetCanonLengths( z->num_taut_at, z->at[TAUT_YES], &(z->s[TAUT_YES]), t_group_info ); + } /* end of: final normalization of possibly tautomeric structure */ + + + + if ( out_norm_data[TAUT_NON]->at && out_norm_data[TAUT_YES]->at && z->at[TAUT_NON] && !z->s[TAUT_YES].nLenLinearCTTautomer ) + { + /* the structure is non-tautomeric: use tautomeric treatment results only for it */ + + inchi_free( z->at[TAUT_NON] ); + + z->at[TAUT_NON] = NULL; + } + + else if ( !out_norm_data[TAUT_NON]->at && out_norm_data[TAUT_YES]->at && + !z->at[TAUT_NON] && z->at[TAUT_YES] && !z->s[TAUT_YES].nLenLinearCTTautomer ) + { + /* requested tautomeric; found non-tautomeric; it is located in out_norm_data[TAUT_YES]->at */ + + out_norm_data[TAUT_YES]->bTautomeric = 0; + } + + else if ( out_norm_data[TAUT_NON]->at && z->at[TAUT_NON] ) + { + /* the structure needs non-tautomeric treatment: final normalization of non-tautomeric structure */ + + ret = mark_alt_bonds_and_taut_groups( ic, pCG, + out_norm_data[TAUT_NON]->at, + NULL, z->num_atoms, ulMaxTime, NULL, + &(z->bTautFlags), &(z->bTautFlagsDone), + 0, NULL); + + if ( ret < 0 ) goto exit_function; /* out of RAM or other normalization problem */ + + out_norm_data[TAUT_NON]->num_at = z->num_atoms + z->num_deleted_H; + out_norm_data[TAUT_NON]->num_removed_H = z->num_deleted_H; + out_norm_data[TAUT_NON]->bTautFlags = *pbTautFlags; + out_norm_data[TAUT_NON]->bTautFlagsDone = *pbTautFlagsDone; + out_norm_data[TAUT_NON]->bNormalizationFlags = 0; + + /* create internal sp_ATOM at[] out of out_norm_data[]->at */ + + inp2spATOM( out_norm_data[TAUT_NON]->at, num_inp_at, z->at[TAUT_NON] ); + + /* set stereo parities to at[]; nUserMode: accept alt. stereo bonds, min ring size */ + + ret = set_stereo_parity( pCG, + out_norm_data[TAUT_NON]->at, + z->at[TAUT_NON], + z->num_atoms, z->num_deleted_H, + &(z->s[TAUT_NON].nMaxNumStereoAtoms), + &(z->s[TAUT_NON].nMaxNumStereoBonds), + z->nUserMode, + z->bPointedEdgeStereo, z->vABParityUnknown ); + + if ( RETURNED_ERROR( ret ) ) goto exit_function; /* stereo bond error */ + + + z->s[TAUT_NON].bMayHaveStereo = (z->s[TAUT_NON].nMaxNumStereoAtoms || z->s[TAUT_NON].nMaxNumStereoBonds); + + /* + * mark isotopic atoms and atoms that have non-tautomeric + * isotopic terminal hydrogen atoms 1H, 2H(D), 3H(T) + */ + + z->s[TAUT_NON].num_isotopic_atoms = + + set_atom_iso_sort_keys( z->num_atoms, z->at[TAUT_NON], NULL, NULL ); + + + GetCanonLengths( z->num_atoms, z->at[TAUT_NON], &(z->s[TAUT_NON]), NULL); + + + out_norm_data[TAUT_NON]->bTautomeric = 0; + } /* the structure needs non-tautomeric treatment: final normalization of non-tautomeric structure */ + + + + + /**********************************************************/ + + + + + /* common */ + z->bMayHaveStereo = z->s[TAUT_YES].bMayHaveStereo || z->s[TAUT_NON].bMayHaveStereo; + z->bHasIsotopicAtoms = z->s[TAUT_NON].num_isotopic_atoms > 0 || z->s[TAUT_NON].bHasIsotopicTautGroups > 0 || + z->s[TAUT_YES].num_isotopic_atoms > 0 || z->s[TAUT_YES].bHasIsotopicTautGroups > 0 ; +/* */ + if (z->fix_isofixedh) /* 2008-03-21 DT */ + z->bHasIsotopicAtoms = z->bHasIsotopicAtoms || + z->s[TAUT_YES].nLenLinearCTTautomer > 0 && t_group_info && + (0 < NUM_H_ISOTOPES && t_group_info->tni.nNumRemovedProtonsIsotopic[0] || + 1 < NUM_H_ISOTOPES && t_group_info->tni.nNumRemovedProtonsIsotopic[1] || + 2 < NUM_H_ISOTOPES && t_group_info->tni.nNumRemovedProtonsIsotopic[2]) ; +/* */ + z->bHasIsotopicAtoms = z->bHasIsotopicAtoms || + z->s[TAUT_YES].nLenIsotopicEndpoints > 1 && t_group_info && + (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE|TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE)); + + + + + + + /* Set mode */ + + /* default mode */ + + if ( !(z->nUserMode & REQ_MODE_DEFAULT) ) + { + z->nUserMode |= REQ_MODE_DEFAULT; + } + + + /* adjust the mode to the reality */ + + if ( ( z->nUserMode & REQ_MODE_ISO ) && !z->bHasIsotopicAtoms ) + { + z->nUserMode ^= REQ_MODE_ISO; + z->nUserMode |= REQ_MODE_NON_ISO; /* at least one is needed */ + } + + if ( (z->nUserMode & REQ_MODE_STEREO) && ( z->nUserMode & REQ_MODE_ISO ) ) + { + z->nUserMode |= REQ_MODE_ISO_STEREO; + } + + if ( (z->nUserMode & REQ_MODE_STEREO) && !( z->nUserMode & REQ_MODE_NON_ISO ) ) + { + z->nUserMode ^= REQ_MODE_STEREO; + } + + if ( !z->bMayHaveStereo ) + { + if ( z->nUserMode & REQ_MODE_STEREO ) + z->nUserMode ^= REQ_MODE_STEREO; + if ( z->nUserMode & REQ_MODE_ISO_STEREO ) + z->nUserMode ^= REQ_MODE_ISO_STEREO; + } + + if ( (z->nUserMode & REQ_MODE_BASIC) && (!out_norm_data[TAUT_NON]->at || !ppINChI[TAUT_NON] || !ppINChI_Aux[TAUT_NON] || !z->at[TAUT_NON]) ) + { + z->nUserMode ^= REQ_MODE_BASIC; + } + if ( (z->nUserMode & REQ_MODE_TAUT) && (!out_norm_data[TAUT_YES]->at || !ppINChI[TAUT_YES] || !ppINChI_Aux[TAUT_YES] || !z->at[TAUT_YES]) ) + { + z->nUserMode ^= REQ_MODE_TAUT; + } + + + /* Set n1, n2 according to the mode */ + + switch ((int)z->nUserMode & (REQ_MODE_BASIC | REQ_MODE_TAUT)) + { + case REQ_MODE_BASIC: + z->n1 = TAUT_NON; + z->n2 = TAUT_NON; + break; + case REQ_MODE_TAUT: + z->n1 = TAUT_YES; + z->n2 = TAUT_YES; + break; + case (REQ_MODE_BASIC | REQ_MODE_TAUT): + z->n1 = TAUT_NON; + z->n2 = TAUT_YES; + break; + default: + /* program error: inconsistent nUserMode or missing taut/non-taut allocation */ /* */ + ret = -3; + goto exit_function; + } + + + if ( ret == 0 ) + ret = z->num_atoms; + + /* treat the results later */ + +exit_function: + + return ret; +} + + +int Canonicalization_step( CANON_GLOBALS *pCG, + INCHI_CLOCK *ic, + INChI **ppINChI, + INChI_Aux **ppINChI_Aux, + INP_ATOM_DATA *out_norm_data[2], + struct tagInchiTime *ulMaxTime, + T_GROUP_INFO *ti_out, + char *pStrErrStruct, + COMPONENT_TREAT_INFO *z, + int LargeMolecules ) +{ +int i,ret=0, ret2=0; + + + +T_GROUP_INFO * /*const*/ t_group_info = &(z->vt_group_info); +T_GROUP_INFO * /*const*/ t_group_info_orig = &(z->vt_group_info_orig); + +CANON_STAT CS, CS2; +CANON_STAT *pCS = &CS; +CANON_STAT *pCS2 = &CS2; /* save all allocations to avoid memory leaks in case Canon_INChI() removes the pointer */ + + BCN *pBCN = &(z->Bcn); + + + INChI *pINChI=NULL; /* added initialization 2006-03 */ + INChI_Aux *pINChI_Aux=NULL; /* added initialization 2006-03 */ + + + + /************************************************************ + * * + * Obtain all non-stereo canonical numberings * + * * + ************************************************************/ + + if ( (z->nUserMode & REQ_MODE_NON_ISO) && !(z->nUserMode & REQ_MODE_ISO) ) + { + + /* added for special non-isotopic test mode 2004-10-04 */ + if ( t_group_info ) + { + t_group_info->bIgnoreIsotopic = 1; + if ( t_group_info->nIsotopicEndpointAtomNumber ) + { + t_group_info->nIsotopicEndpointAtomNumber[0] = inchi_min(1, t_group_info->nIsotopicEndpointAtomNumber[0]); + } + memset( t_group_info->num_iso_H, 0, sizeof(t_group_info->num_iso_H) ); + memset ( t_group_info->tni.nNumRemovedProtonsIsotopic, 0, sizeof(t_group_info->tni.nNumRemovedProtonsIsotopic)); + t_group_info->bTautFlagsDone &= ~(TG_FLAG_FOUND_ISOTOPIC_H_DONE|TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE); + } + + for ( i = 0; i < TAUT_NUM; i ++ ) + { + z->s[i].bHasIsotopicTautGroups = 0; + z->s[i].bIgnoreIsotopic = 1; + z->s[i].nLenIsotopic = 0; + z->s[i].nLenIsotopicEndpoints = 0; + z->s[i].nLenLinearCTIsotopicTautomer = 0; + z->s[i].num_isotopic_atoms = 0; + } + z->bHasIsotopicAtoms = 0; + } + + ret = GetBaseCanonRanking( ic, + z->num_atoms, z->num_at_tg, z->at, + t_group_info, z->s, + pBCN, + ulMaxTime, + pCG, + z->fix_isofixedh, LargeMolecules ); + + if ( ret < 0 ) + goto exit_function; /* program error */ + + + /* added for special non-isotopic test mode 2004-10-04 */ + if ( !pBCN->ftcn[z->n1].PartitionCt.Rank ) + z->n1 = ALT_TAUT(z->n1); + + if ( !pBCN->ftcn[z->n2].PartitionCt.Rank ) + z->n2 = ALT_TAUT(z->n2); + + if ( z->n1 > z->n2 ) + { + ret = CT_TAUCOUNT_ERR; + goto exit_function; /* program error */ + } + + + /************************************************************ + * * + * Obtain stereo canonical numberings * + * * + ************************************************************/ + + for ( i = z->n2; i >= z->n1 && !RETURNED_ERROR( ret ); i -- ) + { + + + memset( pCS, 0, sizeof(*pCS) ); + + switch (i) + { + + case TAUT_NON: /* non-tautomeric */ + + z->nMode = 0; + z->nMode = (z->s[i].nLenLinearCTTautomer == 0)? CANON_MODE_CT:CANON_MODE_TAUT; + z->nMode |= (z->bHasIsotopicAtoms && (z->nUserMode & REQ_MODE_ISO))? CANON_MODE_ISO:0; + z->nMode |= (z->s[TAUT_NON].bMayHaveStereo && (z->nUserMode & REQ_MODE_STEREO) )? CANON_MODE_STEREO:0; + z->nMode |= (z->bHasIsotopicAtoms && z->s[TAUT_NON].bMayHaveStereo && (z->nUserMode & REQ_MODE_ISO_STEREO))? CANON_MODE_ISO_STEREO:0; + z->nMode |= (z->nUserMode & REQ_MODE_NOEQ_STEREO )? CMODE_NOEQ_STEREO : 0; + z->nMode |= (z->nUserMode & REQ_MODE_REDNDNT_STEREO)? CMODE_REDNDNT_STEREO : 0; + z->nMode |= (z->nUserMode & REQ_MODE_NO_ALT_SBONDS )? CMODE_NO_ALT_SBONDS : 0; + + /* 2010-01-12 */ + z->nMode |= (z->vABParityUnknown==AB_PARITY_UNDF)? 0 : REQ_MODE_DIFF_UU_STEREO; + + if ( (z->nMode & CANON_MODE_STEREO) == CANON_MODE_STEREO || + (z->nMode & CANON_MODE_ISO_STEREO) == CANON_MODE_ISO_STEREO ) + { + z->nMode |= (z->nUserMode & REQ_MODE_RELATIVE_STEREO)? CMODE_RELATIVE_STEREO: 0; + z->nMode |= (z->nUserMode & REQ_MODE_RACEMIC_STEREO )? CMODE_RACEMIC_STEREO : 0; + z->nMode |= (z->nUserMode & REQ_MODE_SC_IGN_ALL_UU )? CMODE_SC_IGN_ALL_UU : 0; + z->nMode |= (z->nUserMode & REQ_MODE_SB_IGN_ALL_UU )? CMODE_SB_IGN_ALL_UU : 0; + } + + if ( ret= AllocateCS( pCS, z->num_atoms, z->num_atoms, z->s[TAUT_NON].nLenCT, z->s[TAUT_NON].nLenCTAtOnly, + z->s[TAUT_NON].nLenLinearCTStereoDble, z->s[TAUT_NON].nMaxNumStereoBonds, + z->s[TAUT_NON].nLenLinearCTStereoCarb, z->s[TAUT_NON].nMaxNumStereoAtoms, + 0, 0, z->s[TAUT_NON].nLenIsotopic, z->nMode, pBCN ) ) + { + goto exit_function; + } + + *pCS2 = *pCS; + break; + + + case TAUT_YES: /* tautomeric */ + + z->nMode = 0; + z->nMode = (z->s[i].nLenLinearCTTautomer == 0)? CANON_MODE_CT:CANON_MODE_TAUT; + z->nMode |= (z->bHasIsotopicAtoms && (z->nUserMode & REQ_MODE_ISO) )? CANON_MODE_ISO:0; + z->nMode |= (z->s[TAUT_YES].bMayHaveStereo && (z->nUserMode & REQ_MODE_STEREO) )? CANON_MODE_STEREO:0; + z->nMode |= (z->bHasIsotopicAtoms && z->s[TAUT_YES].bMayHaveStereo && (z->nUserMode & REQ_MODE_ISO_STEREO))? CANON_MODE_ISO_STEREO:0; + z->nMode |= (z->nUserMode & REQ_MODE_NOEQ_STEREO )? CMODE_NOEQ_STEREO : 0; + z->nMode |= (z->nUserMode & REQ_MODE_REDNDNT_STEREO)? CMODE_REDNDNT_STEREO : 0; + z->nMode |= (z->nUserMode & REQ_MODE_NO_ALT_SBONDS )? CMODE_NO_ALT_SBONDS : 0; + + /* 2010-01-12 */ + z->nMode |= (z->vABParityUnknown==AB_PARITY_UNDF)? 0 : REQ_MODE_DIFF_UU_STEREO; + + if ( (z->nMode & CANON_MODE_STEREO) == CANON_MODE_STEREO || + (z->nMode & CANON_MODE_ISO_STEREO) == CANON_MODE_ISO_STEREO ) + { + z->nMode |= (z->nUserMode & REQ_MODE_RELATIVE_STEREO)? CMODE_RELATIVE_STEREO: 0; + z->nMode |= (z->nUserMode & REQ_MODE_RACEMIC_STEREO )? CMODE_RACEMIC_STEREO : 0; + z->nMode |= (z->nUserMode & REQ_MODE_SC_IGN_ALL_UU )? CMODE_SC_IGN_ALL_UU : 0; + z->nMode |= (z->nUserMode & REQ_MODE_SB_IGN_ALL_UU )? CMODE_SB_IGN_ALL_UU : 0; + } + + if ( ret= AllocateCS( pCS, z->num_atoms, z->num_at_tg, z->s[TAUT_YES].nLenCT, z->s[TAUT_YES].nLenCTAtOnly, + z->s[TAUT_YES].nLenLinearCTStereoDble, z->s[TAUT_YES].nMaxNumStereoBonds, + z->s[TAUT_YES].nLenLinearCTStereoCarb, z->s[TAUT_YES].nMaxNumStereoAtoms, + z->s[TAUT_YES].nLenLinearCTTautomer, z->s[TAUT_YES].nLenLinearCTIsotopicTautomer, + z->s[TAUT_YES].nLenIsotopic, z->nMode, pBCN ) ) + { + goto exit_function; + } + + *pCS2 = *pCS; + break; + } /* switch () */ + + + /* settings */ + pCS->lNumDecreasedCT = -1; + pCS->bDoubleBondSquare = DOUBLE_BOND_NEIGH_LIST? 2:0; /* 2 => special mode */ + pCS->bIgnoreIsotopic = !((z->s[TAUT_NON].num_isotopic_atoms || + z->s[TAUT_YES].num_isotopic_atoms || + z->s[TAUT_YES].bHasIsotopicTautGroups) || + (z->nUserMode & REQ_MODE_NON_ISO) || + !(z->nUserMode & REQ_MODE_ISO)); + + if ( (z->nUserMode & REQ_MODE_NON_ISO) && !(z->nUserMode & REQ_MODE_ISO) ) + pCS->bIgnoreIsotopic = 1; /* 10-04-2004 */ + + + if ( i == TAUT_YES ) + { + /* tautomeric */ + pCS->t_group_info = t_group_info; /* ??? make a copy or reuse ??? */ + pCS->t_group_info->bIgnoreIsotopic = !(z->s[TAUT_YES].bHasIsotopicTautGroups || + (z->nUserMode & REQ_MODE_NON_ISO) || + !(z->nUserMode & REQ_MODE_ISO)); + if ( (z->nUserMode & REQ_MODE_NON_ISO) && !(z->nUserMode & REQ_MODE_ISO) ) + pCS->t_group_info->bIgnoreIsotopic = 1; /* 10-04-2004 */ + } + pCS->ulTimeOutTime = pBCN->ulTimeOutTime; + /*=========== Obsolete Mode Bits (bit 0 is Least Significant Bit) =========== + * + * Mode Bits Description + * '0' c 0 Only one connection table canonicalization + * '1' C 1 Recalculate CT using fixed nSymmRank + * '2' i 1|2 Isotopic canonicalization (internal) + * '3' I 1|2|4 Isotopic canonicalization (output) + * '4' s 1|8 Stereo canonicalization + * '5' S 1|2|4|16 Stereo isotopic canonicalization + * '6' A 1|2|4|8|16 Output All + */ + + /*************************************** + The last canonicalization step + ***************************************/ + if ( pBCN ) + { + /* USE_CANON2 == 1 */ + pCS->NeighList = NULL; + pCS->pBCN = pBCN; + ret = Canon_INChI( ic, + z->num_atoms, + i ? z->num_at_tg : z->num_atoms, + z->at[i], pCS, pCG, z->nMode, i); + } + else + { + /* old way */ + pCS->NeighList = CreateNeighList( z->num_atoms, i?z->num_at_tg:z->num_atoms, z->at[i], pCS->bDoubleBondSquare, pCS->t_group_info ); + pCS->pBCN = NULL; + ret = Canon_INChI( ic, + z->num_atoms, + i ? z->num_at_tg : z->num_atoms, + z->at[i], pCS, pCG, z->nMode, i); + } + + pINChI = ppINChI[i]; /* pointers to already allocated still empty InChI */ + pINChI_Aux = ppINChI_Aux[i]; + + if ( ret <= 0 ) + { + /***************************************/ + /* failure in Canon_INChI() */ + /***************************************/ + pINChI->nErrorCode = ret; + pINChI_Aux->nErrorCode = ret; + } + else + { + /***************************************/ + /* success Canon_INChI() */ + /* save canonicalization results in */ + /* pINChI and pINChI_Aux */ + /***************************************/ + pINChI->nErrorCode = 0; + pINChI_Aux->nErrorCode = 0; + pINChI->bDeleted = pINChI_Aux->bDeleted = out_norm_data[i]->bDeleted; + pINChI_Aux->nCanonFlags = pCS->nCanonFlags; + pINChI_Aux->bTautFlags = out_norm_data[i]->bTautFlags; + pINChI_Aux->bTautFlagsDone = out_norm_data[i]->bTautFlagsDone; + pINChI_Aux->bNormalizationFlags = out_norm_data[i]->bNormalizationFlags; + + /* may return an error or a warning */ + ret = FillOutINChIReducedWarn( pINChI, pINChI_Aux, + z->num_atoms, + i ? z->num_at_tg : z->num_atoms, + i ? z->num_deleted_H_taut : z->num_deleted_H, + z->at[i], + out_norm_data[i]->at, + pCS, + pCG, + i, + z->nUserMode, + pStrErrStruct ); + + if ( RETURNED_ERROR( ret ) ) + { + /* failure in FillOutINChI() */ + pINChI->nErrorCode = ret; + pINChI_Aux->nErrorCode = ret; + } + else + { + /****************************/ + /* success in FillOutINChI() */ + /****************************/ + + /* mark non-tautomeric representation as having another, tautomeric representation */ + if ( pINChI_Aux && z->s[TAUT_YES].nLenLinearCTTautomer ) + pINChI_Aux->bIsTautomeric = z->s[TAUT_YES].nLenLinearCTTautomer; + + + ret2 = CheckCanonNumberingCorrectness( z->num_atoms, + i ? z->num_at_tg : z->num_atoms, + z->at[i], + pCS, + pCG, + i, + pStrErrStruct ); + + if (ret2) + { + pINChI->nErrorCode = ret2; + pINChI_Aux->nErrorCode = ret2; + ret = ret2; + } + } /* success in FillOutINChI */ + } /* success Canon_INChI */ + + FreeNeighList( pCS->NeighList ); + DeAllocateCS( pCS2 ); + + pINChI = NULL; /* avoid dangling pointers */ + pINChI_Aux = NULL; /* avoid dangling pointers */ + } /* for ( i = z->n2; i >= z->n1 && !RETURNED_ERROR( ret ); i -- ) */ + + + + if ( ret == 0 ) + ret = z->num_atoms; + + +exit_function: + + DeAllocBCN( pBCN ); + + if ( z->at[TAUT_YES] ) + { + inchi_free( z->at[TAUT_YES] ); + z->at[TAUT_YES] = NULL; + } + + if ( z->at[TAUT_NON] ) + { + inchi_free( z->at[TAUT_NON] ); + z->at[TAUT_NON] = NULL; + } + + if ( ti_out ) + *ti_out = *t_group_info; + else + { + free_t_group_info( t_group_info ); + t_group_info = NULL; + } + + free_t_group_info( t_group_info_orig ); + + return ret; +} + + +int CreateCompositeNormAtom( COMP_ATOM_DATA *composite_norm_data, + INP_ATOM_DATA2 *all_inp_norm_data, + int num_components) +{ + int i, j, jj, k, n, m, tot_num_at, tot_num_H, cur_num_at, cur_num_H, nNumRemovedProtons; + int num_comp[TAUT_NUM+1], num_taut[TAUT_NUM+1], num_del[TAUT_NUM+1], num_at[TAUT_NUM+1], num_inp_at[TAUT_NUM+1]; + int ret = 0, indicator = 1; + inp_ATOM *at, *at_from; + memset( num_comp, 0, sizeof(num_comp) ); + memset( num_taut, 0, sizeof(num_taut) ); + memset( num_del, 0, sizeof(num_taut) ); + /* count taut and non-taut components */ + for ( j = 0; j < TAUT_NUM; j ++ ) { + num_comp[j] = num_taut[j] = 0; + for ( i = 0; i < num_components; i ++ ) { + if ( all_inp_norm_data[i][j].bExists ) { + num_del[j] += (0 != all_inp_norm_data[i][j].bDeleted ); + num_comp[j] ++; + num_taut[j] += (0 != all_inp_norm_data[i][j].bTautomeric); + } + } + } + /* count intermediate taut structure components */ + if ( num_comp[TAUT_YES] > num_del[TAUT_YES] && num_taut[TAUT_YES] ) { + /* + num_comp[TAUT_INI] = num_comp[TAUT_YES] - num_del[TAUT_YES]; + */ + + for ( i = 0, j=TAUT_YES; i < num_components; i ++ ) { + if ( all_inp_norm_data[i][j].bExists && + (all_inp_norm_data[i][j].bDeleted || + all_inp_norm_data[i][j].bTautomeric && + all_inp_norm_data[i][j].at_fixed_bonds && + all_inp_norm_data[i][j].bTautPreprocessed) ) { + num_comp[TAUT_INI] ++; + } + } + } + /* count atoms and allocate composite atom data */ + for ( jj = 0; jj <= TAUT_INI; jj ++ ) { + num_at[jj] = num_inp_at[jj] = 0; + j = inchi_min (jj, TAUT_YES); + if ( num_comp[jj] ) { + for ( i = 0; i < num_components; i ++ ) { + if ( all_inp_norm_data[i][j].bDeleted ) + continue; + /* find k = the normaized structure index */ + if ( jj == TAUT_INI ) { + if ( all_inp_norm_data[i][j].bExists && + all_inp_norm_data[i][j].at_fixed_bonds ) { + k = j; + } else + if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted && + !all_inp_norm_data[i][j].bDeleted ) { + k = ALT_TAUT(j); + } else + if ( all_inp_norm_data[i][j].bExists ) { + k = j; + } else { + continue; + } + } else { + if ( all_inp_norm_data[i][j].bExists ) { + k = j; + } else + if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted) { + k = ALT_TAUT(j); + } else { + continue; + } + } + num_inp_at[jj] += all_inp_norm_data[i][k].num_at; /* all atoms including terminal H */ + num_at[jj] += all_inp_norm_data[i][k].num_at - all_inp_norm_data[i][k].num_removed_H; + } + if ( num_inp_at[jj] ) { + if ( !CreateCompAtomData( composite_norm_data+jj, num_inp_at[jj], num_components, jj == TAUT_INI ) ) + goto exit_error; + composite_norm_data[jj].num_removed_H = num_inp_at[jj] - num_at[jj]; + } + } + } + /* fill out composite atom */ + for ( jj = 0; jj <= TAUT_INI; jj ++, indicator <<= 1 ) { + j = inchi_min (jj, TAUT_YES); + if ( num_comp[jj] ) { + tot_num_at = 0; + tot_num_H = 0; + for ( i = 0; i < num_components; i ++ ) { + if ( all_inp_norm_data[i][j].bDeleted ) { + composite_norm_data[jj].nNumRemovedProtons += all_inp_norm_data[i][j].nNumRemovedProtons; + for ( n = 0; n < NUM_H_ISOTOPES; n ++ ) { + composite_norm_data[jj].nNumRemovedProtonsIsotopic[n] += all_inp_norm_data[i][j].nNumRemovedProtonsIsotopic[n]; + } + continue; + } + nNumRemovedProtons = 0; + k = TAUT_NUM; + /* find k = the normaized structure index */ + if ( jj == TAUT_INI ) { + if ( all_inp_norm_data[i][j].bExists && all_inp_norm_data[i][j].at_fixed_bonds ) { + k = j; + } else + if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists ) { + k = ALT_TAUT(j); + } else + if ( all_inp_norm_data[i][j].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted ) { + k = j; + } else { + continue; + } + } else { + if ( all_inp_norm_data[i][j].bExists ) { + k = j; + } else + if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted ) { + k = ALT_TAUT(j); + } else { + continue; + } + } + /* copy main atoms */ + cur_num_H = all_inp_norm_data[i][k].num_removed_H; /* number of terminal H atoms */ + cur_num_at = all_inp_norm_data[i][k].num_at - cur_num_H; /* number of all but explicit terminal H atoms */ + + if ( (tot_num_at + cur_num_at) > num_at[jj] || + (num_at[jj] + tot_num_H + cur_num_H) > num_inp_at[jj] ) { + goto exit_error; /* miscount */ + } + at = composite_norm_data[jj].at+tot_num_at; /* points to the 1st destination atom */ + at_from = (jj == TAUT_INI && k == TAUT_YES && all_inp_norm_data[i][k].at_fixed_bonds)? + all_inp_norm_data[i][k].at_fixed_bonds : all_inp_norm_data[i][k].at; + memcpy( at, at_from, sizeof(composite_norm_data[0].at[0]) * cur_num_at ); /* copy atoms except terminal H */ + /* shift neighbors of main atoms */ + for ( n = 0; n < cur_num_at; n ++, at ++ ) { + for ( m = 0; m < at->valence; m ++ ) { + at->neighbor[m] += tot_num_at; + } + } + /* copy explicit H */ + if ( cur_num_H ) { + at = composite_norm_data[jj].at+num_at[jj]+tot_num_H; /* points to the 1st destination atom */ + memcpy( at, at_from+cur_num_at, + sizeof(composite_norm_data[0].at[0]) * cur_num_H ); + /* shift neighbors of explicit H atoms */ + for ( n = 0; n < cur_num_H; n ++, at ++ ) { + for ( m = 0; m < at->valence; m ++ ) { + at->neighbor[m] += tot_num_at; + } + } + } + /* composite counts */ + composite_norm_data[jj].bHasIsotopicLayer |= all_inp_norm_data[i][k].bHasIsotopicLayer; + composite_norm_data[jj].num_isotopic += all_inp_norm_data[i][k].num_isotopic; + composite_norm_data[jj].num_bonds += all_inp_norm_data[i][k].num_bonds; + composite_norm_data[jj].bTautomeric += (j == jj) && all_inp_norm_data[i][k].bTautomeric; + composite_norm_data[jj].nNumRemovedProtons += all_inp_norm_data[i][k].nNumRemovedProtons; + for ( n = 0; n < NUM_H_ISOTOPES; n ++ ) { + composite_norm_data[jj].nNumRemovedProtonsIsotopic[n] += all_inp_norm_data[i][k].nNumRemovedProtonsIsotopic[n]; + composite_norm_data[jj].num_iso_H[n] += all_inp_norm_data[i][k].num_iso_H[n]; + } + /* + composite_norm_data[j].num_at += cur_num_at + cur_num_H; + composite_norm_data[j].num_removed_H += cur_num_H; + */ + /* total count */ + tot_num_at += cur_num_at; + tot_num_H += cur_num_H; + /* offset for the next component */ + if ( composite_norm_data[jj].nOffsetAtAndH ) { + composite_norm_data[jj].nOffsetAtAndH[2*i] = tot_num_at; + composite_norm_data[jj].nOffsetAtAndH[2*i+1] = num_at[jj]+tot_num_H; + } + } + if ( tot_num_at != num_at[jj] || + num_at[jj] + tot_num_H != num_inp_at[jj] ) { + goto exit_error; /* miscount */ + } + composite_norm_data[jj].bExists = (tot_num_at>0); + ret |= indicator; + } + } + return ret; + +exit_error: + return ret; +} + + +int CreateCompAtomData( COMP_ATOM_DATA *inp_at_data, + int num_atoms, + int num_components, + int bIntermediateTaut ) +{ + FreeCompAtomData( inp_at_data ); + if ( (inp_at_data->at = CreateInpAtom( num_atoms )) && + (num_components <= 1 || bIntermediateTaut || + (inp_at_data->nOffsetAtAndH = (AT_NUMB*)inchi_calloc(sizeof(inp_at_data->nOffsetAtAndH[0]), 2*(num_components+1))))) { + + inp_at_data->num_at = num_atoms; + inp_at_data->num_components = (num_components>1)? num_components : 0; + return 1; + } + FreeCompAtomData( inp_at_data ); + return 0; +} + + +int FillOutINChIReducedWarn( INChI *pINChI, + INChI_Aux *pINChI_Aux, + int num_atoms, + int num_at_tg, + int num_removed_H, + sp_ATOM *at, + inp_ATOM *norm_at, + CANON_STAT *pCS, + CANON_GLOBALS *pCG, + int bTautomeric, + INCHI_MODE nUserMode, + char *pStrErrStruct ) +{ + int i, j, m, n, g, len, ii, ret=0; + + AT_NUMB *pSymmRank, *pOrigNosInCanonOrd, *pConstitEquNumb, *pCanonOrd=NULL, *pCanonOrdInv=NULL, *pCanonOrdTaut; + T_GROUP_INFO *t_group_info = pCS->t_group_info; + T_GROUP *t_group; + int nErrorCode = 0; + AT_NUMB *pCanonRank, *pCanonRankInv; /* canonical ranks of the atoms or tautomeric groups */ + AT_NUMB *pCanonRankAtoms=NULL, *pSortOrd = NULL; + AT_RANK nMinOrd; + INChI_Stereo *Stereo; + int bUseNumberingInv = 0, bUseIsotopicNumberingInv = 0; + INCHI_MODE nStereoUnmarkMode; + + /*AT_NUMB *pCanonOrdNonIso = NULL, *pCanonOrdIso = NULL;*/ + /*AT_NUMB *nOrigAtNosInCanonOrdNonIso = NULL, *nOrigAtNosInCanonOrdIso = NULL;*/ + + /* Check for warnings */ + if ( pCS->nLenLinearCTStereoCarb < 0 || pCS->nLenLinearCTStereoDble < 0 || + pCS->nLenCanonOrdStereo < 0 || pCS->nLenCanonOrdStereoTaut < 0) { + nErrorCode |= WARN_FAILED_STEREO; + } + if ( pCS->nLenLinearCTIsotopic < 0 || pCS->nLenLinearCTIsotopicTautomer < 0 || + pCS->nLenCanonOrdIsotopic < 0 || pCS->nLenCanonOrdIsotopicTaut < 0 ) { + nErrorCode |= WARN_FAILED_ISOTOPIC; + } + if ( pCS->nLenLinearCTIsotopicStereoCarb < 0 || pCS->nLenLinearCTIsotopicStereoDble < 0 || + pCS->nLenCanonOrdIsotopicStereo < 0 || pCS->nLenCanonOrdIsotopicStereoTaut < 0) { + nErrorCode |= WARN_FAILED_ISOTOPIC_STEREO; + } + pCanonRankAtoms = (AT_NUMB *)inchi_calloc( num_at_tg+1, sizeof(pCanonRankAtoms[0]) ); + pSortOrd = (AT_NUMB *)inchi_calloc( num_at_tg+1, sizeof(pSortOrd[0]) ); /* must have more than num_atoms */ + + if ( !pCanonRankAtoms || !pSortOrd ) { + nErrorCode = 0; + ret = CT_OUT_OF_RAM; /* */ + pINChI->nErrorCode = pINChI_Aux->nErrorCode = CT_OUT_OF_RAM; + goto exit_function; + } + + /* total charge */ + for ( i = 0, n = 0; i < num_atoms+num_removed_H; i ++ ) { + n += at[i].charge; + } + pINChI->nTotalCharge = n; + + /* number of atoms */ + pINChI->nNumberOfAtoms = num_atoms; + pINChI_Aux->nNumberOfAtoms = num_atoms; + + /* removed protons and detachable isotopic H */ + if ( bTautomeric && t_group_info ) { + pINChI_Aux->nNumRemovedProtons = t_group_info->tni.nNumRemovedProtons; + for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { + pINChI_Aux->nNumRemovedIsotopicH[i] = t_group_info->num_iso_H[i] + + t_group_info->tni.nNumRemovedProtonsIsotopic[i]; + } + if ( pINChI_Aux->bNormalizationFlags & FLAG_FORCE_SALT_TAUT ) { + pINChI->nFlags |= INCHI_FLAG_HARD_ADD_REM_PROTON; + } +/*^^^ + if ( pINChI_Aux->bNormalizationFlags & (FLAG_NORM_CONSIDER_TAUT &~FLAG_PROTON_CHARGE_CANCEL) ) { + WarningMessage(pStrErrStruct, "Proton(s) added/removed"); + } + + if ( pINChI_Aux->bNormalizationFlags & FLAG_PROTON_CHARGE_CANCEL ) { + WarningMessage(pStrErrStruct, "Charges neutralized"); + } +^^^*/ + } + + /* abs or rel stereo may establish one of two canonical numberings */ + if ( (pCS->nLenLinearCTStereoCarb > 0 || pCS->nLenLinearCTStereoDble > 0) && + pCS->nLenCanonOrdStereo > 0 && + (pCS->LinearCTStereoCarb && pCS->LinearCTStereoCarbInv || + pCS->LinearCTStereoDble && pCS->LinearCTStereoDbleInv) && + pCS->nCanonOrdStereo && pCS->nCanonOrdStereoInv + ) { + + pCanonRank = pCanonRankAtoms; + pCanonOrd = pCS->nCanonOrdStereo; + pCanonRankInv = pSortOrd; + pCanonOrdInv = pCS->nCanonOrdStereoInv; + Stereo = pINChI->Stereo; + for ( i = 0; i < num_at_tg; i ++ ) { + pCanonRankInv[pCanonOrdInv[i]] = + pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); + } + /********************************************************************/ + /* copy stereo bonds and stereo centers; compare Inv and Abs stereo */ + /********************************************************************/ + nErrorCode = CopyLinearCTStereoToINChIStereo( Stereo, + pCS->LinearCTStereoCarb, pCS->nLenLinearCTStereoCarb, + pCS->LinearCTStereoDble, pCS->nLenLinearCTStereoDble + , pCanonOrd, pCanonRank, at, 0 /* non-isotopic */ + , pCS->LinearCTStereoCarbInv + , pCS->LinearCTStereoDbleInv + , pCanonOrdInv, pCanonRankInv ); + + if ( Stereo->t_parityInv && Stereo->nNumberInv ) { + if ( nUserMode & REQ_MODE_RELATIVE_STEREO ) { + pINChI->nFlags |= INCHI_FLAG_REL_STEREO; + } + if ( nUserMode & REQ_MODE_RACEMIC_STEREO ) { + pINChI->nFlags |= INCHI_FLAG_RAC_STEREO; + } + if ( Stereo->nCompInv2Abs ) { + if ( Stereo->nCompInv2Abs == -1 ) { + /* switch pointers in Stereo so that the stereo becomes the smallest (relative) */ + /* flag Stereo->nCompInv2Abs == -1 will keep track of this exchange */ + AT_NUMB *nNumberInv = Stereo->nNumberInv; + S_CHAR *t_parityInv = Stereo->t_parityInv; + Stereo->nNumberInv = Stereo->nNumber; + Stereo->t_parityInv = Stereo->t_parity; + Stereo->nNumber = nNumberInv; + Stereo->t_parity = t_parityInv; + /* switch pointers to set rel. stereo to pINChI_Aux->nOrigAtNosInCanonOrd + and inv. stereo to pINChI_Aux->nOrigAtNosInCanonOrdInv */ + switch_ptrs( &pCanonRank, &pCanonRankInv ); + switch_ptrs( &pCanonOrd, &pCanonOrdInv ); + bUseNumberingInv = 1; /* use inverted stereo numbering instead of normal */ + } + } + } + + for ( i = 0; i < num_atoms; i ++ ) { + pINChI_Aux->nOrigAtNosInCanonOrdInv[i] = at[pCanonOrdInv[i]].orig_at_number; + pINChI_Aux->nOrigAtNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; + } + if ( bUseNumberingInv ) { + /* switch ptrs back to avoid confusion */ + switch_ptrs( &pCanonRank, &pCanonRankInv ); + switch_ptrs( &pCanonOrd, &pCanonOrdInv ); + /* save inverted stereo ranks & order because it represents the smallest (relative) */ + memcpy( pCanonRank, pCanonRankInv, num_at_tg * sizeof(pCanonRank[0]) ); + /* change pCS->nCanonOrdStereo[] to inverted: */ + memcpy( pCanonOrd, pCanonOrdInv, num_at_tg * sizeof(pCanonOrd[0]) ); + } + pCanonRankInv = NULL; + pCanonOrdInv = NULL; + pOrigNosInCanonOrd = NULL; + } else { /*------------------------------ no stereo */ + + pCanonOrd = pCS->nLenCanonOrdStereo > 0? pCS->nCanonOrdStereo : + pCS->nLenCanonOrd > 0? pCS->nCanonOrd : NULL; + pCanonRank = pCanonRankAtoms; + pOrigNosInCanonOrd = pINChI_Aux->nOrigAtNosInCanonOrd; + if ( pCanonOrd && pCanonRank ) { + for ( i = 0; i < num_atoms; i ++ ) { + pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); + pOrigNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; + } + for ( ; i < num_at_tg; i ++ ) { + pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); + } + } + } + /*pCanonOrdNonIso = pCanonOrd;*/ /* save for aux info */ + + + if ( pINChI_Aux->OrigInfo ) { + /* charges, radicals, valences */ + for ( i = 0; i < num_atoms; i ++ ) { + ii = pCanonOrd[i]; + if ( norm_at[ii].valence || norm_at[ii].num_H ) { + pINChI_Aux->OrigInfo[i].cCharge = norm_at[ii].charge; + pINChI_Aux->OrigInfo[i].cRadical = (norm_at[ii].radical==RADICAL_SINGLET)? 0 : + (norm_at[ii].radical==RADICAL_DOUBLET)? 1 : + (norm_at[ii].radical==RADICAL_TRIPLET)? 2 : + norm_at[ii].radical? 3 : 0 ; + pINChI_Aux->OrigInfo[i].cUnusualValence = + get_unusual_el_valence( norm_at[ii].el_number, norm_at[ii].charge, norm_at[ii].radical, + norm_at[ii].chem_bonds_valence, norm_at[ii].num_H, norm_at[ii].valence ); + } else { + /* charge of a single atom component is in the INChI; valence = 0 is standard */ + pINChI_Aux->OrigInfo[i].cRadical = (norm_at[ii].radical==RADICAL_SINGLET)? 0 : + (norm_at[ii].radical==RADICAL_DOUBLET)? 1 : + (norm_at[ii].radical==RADICAL_TRIPLET)? 2 : + norm_at[ii].radical? 3 : 0 ; + } + } + } + + /* non-isotopic canonical numbers and equivalence of atoms (Aux) */ + pConstitEquNumb = pINChI_Aux->nConstitEquNumbers; /* contitutional equivalence */ + pSymmRank = pCS->nSymmRank; + if ( pCanonOrd && pCanonRank && pSymmRank && pConstitEquNumb ) { + for ( i = 0; i < num_atoms; i ++ ) { + pConstitEquNumb[i] = pSymmRank[pCanonOrd[i]]; /* constit. equ. ranks in order of canonical numbers */ + pSortOrd[i] = i; + } + for ( ; i < num_at_tg; i ++ ) { + pSortOrd[i] = MAX_ATOMS; /* for debugging only */ + } + + pCG->m_pn_RankForSort = pConstitEquNumb; + inchi_qsort( pCG, pSortOrd, num_atoms, sizeof(pSortOrd[0]), CompRanksOrd ); + + for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= num_atoms; j ++ ) { + if ( j == num_atoms || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) { + nMinOrd ++; + if ( j - i > 1 ) { + /* found a sequence of equivalent atoms: i..j-1 */ + while ( i < j ) { + pConstitEquNumb[pSortOrd[i++]] = nMinOrd; /* = min. canon. rank in the group of equ. atoms */ + } + /* at this point j == i */ + } else { + pConstitEquNumb[pSortOrd[i++]] = 0; /* means the atom is not equivalent to any other */ + } + nMinOrd = pSortOrd[j]; /* at the end j = num_atoms */ + } + } + } else { + nErrorCode |= ERR_NO_CANON_RESULTS; + ret = -1; /* program error; no breakpoint here */ + goto exit_function; + } + /* atomic numbers from the Periodic Table */ + for ( i = 0; i < num_atoms; i ++ ) { + pINChI->nAtom[i] = (int)at[pCanonOrd[i]].el_number; + } + /* connection table: atoms only (before 7-29-2003 pCS->LinearCT2 contained non-isotopic CT) */ + if ( pCS->nLenLinearCTAtOnly <= 0 || !pCS->LinearCT || !pINChI->nConnTable ) { + nErrorCode |= ERR_NO_CANON_RESULTS; + ret = -2; + goto exit_function; + } + memcpy( pINChI->nConnTable, pCS->LinearCT, sizeof(pINChI->nConnTable[0])*pCS->nLenLinearCTAtOnly); + pINChI->lenConnTable = pCS->nLenLinearCTAtOnly; + + /* tautomeric group(s) canonical representation */ + len = 0; + if ( bTautomeric && + 0 < (n=SortTautomerGroupsAndEndpoints( pCG, t_group_info, + num_atoms, num_at_tg, pCanonRank )) ) + { + /* SortTautomerGroupsAndEndpoints() produces canonically ordered t-groups */ + pINChI->nFlags |= (t_group_info->bTautFlagsDone & TG_FLAG_ALL_SALT_DONE)? INCHI_FLAG_ACID_TAUT : 0; + /* number of tautomeric groups */ + pINChI->nTautomer[len ++] = (AT_NUMB)n; + /* store each tautomeric group, one by one */ + for ( i = 0; i < n; i ++ ) { + g = (int)t_group_info->tGroupNumber[i]; /* original group numbers in sorted order */ + t_group = t_group_info->t_group + g; /* pointer to the tautomeric group */ + /* NumAt+INCHI_T_NUM_MOVABLE (group length excluding this number) */ + pINChI->nTautomer[len ++] = t_group->nNumEndpoints+INCHI_T_NUM_MOVABLE; + /* Num(H), Num(-) */ + for ( j = 0; j < INCHI_T_NUM_MOVABLE && j < T_NUM_NO_ISOTOPIC; j ++ ) + pINChI->nTautomer[len ++] = t_group->num[j]; + for ( j = T_NUM_NO_ISOTOPIC; j < INCHI_T_NUM_MOVABLE; j ++ ) + pINChI->nTautomer[len ++] = 0; /* should not happen */ + /* tautomeric group endpoint canonical numbers, pre-sorted in ascending order */ + for ( j = (int)t_group->nFirstEndpointAtNoPos, + m = j + (int)t_group->nNumEndpoints; j < m; j ++ ) { + pINChI->nTautomer[len ++] = pCanonRank[(int)t_group_info->nEndpointAtomNumber[j]]; /* At[j] */ + } + } + pINChI->lenTautomer = len; + pINChI_Aux->nNumberOfTGroups = n; + } else { + pINChI->lenTautomer = 0; + pINChI_Aux->nNumberOfTGroups = 0; + if ( t_group_info && ((t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) || + t_group_info->nNumIsotopicEndpoints>1 && + (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE))) + ) { + /* only protons (re)moved or added */ + pINChI->lenTautomer = 1; + pINChI->nTautomer[0] = 0; + } + } + + /* number of H (excluding tautomeric) */ + if ( pCS->nNum_H ) { + for ( i = 0; i < num_atoms; i ++ ) { + pINChI->nNum_H[i] = pCS->nNum_H[i]; + } + } + /* number of fixed H (tautomeric H in non-tautomeric representation) */ + if ( pCS->nNum_H_fixed && !pINChI->lenTautomer ) { + for ( i = 0; i < num_atoms; i ++ ) { + pINChI->nNum_H_fixed[i] = pCS->nNum_H_fixed[i]; + pINChI->nNum_H[i] += pCS->nNum_H_fixed[i]; + } + } + + /*********************************************************** + * tautomeric group(s) numbering and symmetry; + * should not depend on switching to rel. stereo numbering + */ + if ( pINChI->lenTautomer && (n=pINChI_Aux->nNumberOfTGroups) ) { + pCanonOrdTaut = pCS->nLenCanonOrdStereoTaut > 0? pCS->nCanonOrdStereoTaut : + pCS->nLenCanonOrdTaut > 0? pCS->nCanonOrdTaut : NULL; + pConstitEquNumb = pINChI_Aux->nConstitEquTGroupNumbers; + pSymmRank = pCS->nSymmRankTaut; + if ( pCanonOrdTaut && pSymmRank && pConstitEquNumb ) { + for ( i = 0; i < n; i ++ ) { + pConstitEquNumb[i] = pSymmRank[pCanonOrdTaut[i]]; + pSortOrd[i] = i; + } + + pCG->m_pn_RankForSort = pConstitEquNumb; + inchi_qsort( pCG, pSortOrd, n, sizeof(pSortOrd[0]), CompRanksOrd ); + + for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= n; j ++ ) { + if ( j == n || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) { + nMinOrd ++; /* make is start from 1, not from zero */ + if ( j - i > 1 ) { + /* found a sequence of more than one equivalent t-groups: i..j-1 */ + while ( i < j ) { + pConstitEquNumb[pSortOrd[i++]] = nMinOrd; + } + } else { + pConstitEquNumb[pSortOrd[i++]] = 0; + } + nMinOrd = pSortOrd[j]; /* at the end j == n */ + } + } + } + } + + /* Allocate and fill Hill formula */ + if ( !(pINChI->szHillFormula = AllocateAndFillHillFormula( pINChI ) ) ) { + nErrorCode = 0; + ret = CT_WRONG_FORMULA; /* CT_OUT_OF_RAM;*/ /* */ + pINChI->nErrorCode = pINChI_Aux->nErrorCode = ret; + goto exit_function; + } + + if ( nStereoUnmarkMode = UnmarkAllUndefinedUnknownStereo( pINChI->Stereo, nUserMode ) ) { + pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU)? INCHI_FLAG_SC_IGN_ALL_UU : 0; + pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU)? INCHI_FLAG_SB_IGN_ALL_UU : 0; + if ( (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU) || + (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU) ) { + WarningMessage(pStrErrStruct, "Omitted undefined stereo"); + } + } + + /*************************/ + /* mark ambiguous stereo */ + /*************************/ + MarkAmbiguousStereo( at, norm_at, 0 /* non-isotopic */, pCanonOrd, + pCS->LinearCTStereoCarb, pCS->nLenLinearCTStereoCarb, + pCS->LinearCTStereoDble, pCS->nLenLinearCTStereoDble ); + + + /************************************************************************ + * + * isotopic part + */ + /* abs or rel stereo may establish one of two canonical numberings */ + if ( (pCS->nLenLinearCTIsotopicStereoCarb > 0 || pCS->nLenLinearCTIsotopicStereoDble > 0) && + pCS->nLenCanonOrdIsotopicStereo > 0 && + (pCS->LinearCTIsotopicStereoCarb && pCS->LinearCTIsotopicStereoCarbInv || + pCS->LinearCTIsotopicStereoDble && pCS->LinearCTIsotopicStereoDbleInv) && + pCS->nCanonOrdIsotopicStereo && pCS->nCanonOrdIsotopicStereoInv + ) { + /* found isotopic stereo */ + pCanonRank = pCanonRankAtoms; + pCanonOrd = pCS->nCanonOrdIsotopicStereo; + pCanonRankInv = pSortOrd; + pCanonOrdInv = pCS->nCanonOrdIsotopicStereoInv; + Stereo = pINChI->StereoIsotopic; + for ( i = 0; i < num_at_tg; i ++ ) { + pCanonRankInv[pCanonOrdInv[i]] = + pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); + } + /********************************************************************/ + /* copy stereo bonds and stereo centers; compare Inv and Abs stereo */ + /********************************************************************/ + nErrorCode = CopyLinearCTStereoToINChIStereo( Stereo, + pCS->LinearCTIsotopicStereoCarb, pCS->nLenLinearCTIsotopicStereoCarb, + pCS->LinearCTIsotopicStereoDble, pCS->nLenLinearCTIsotopicStereoDble + , pCanonOrd, pCanonRank, at, 1 /* isotopic */ + , pCS->LinearCTIsotopicStereoCarbInv + , pCS->LinearCTIsotopicStereoDbleInv + , pCanonOrdInv, pCanonRankInv ); + + if ( Stereo->t_parityInv && Stereo->nNumberInv ) { + if ( nUserMode & REQ_MODE_RELATIVE_STEREO ) { + pINChI->nFlags |= INCHI_FLAG_REL_STEREO; + } + if ( nUserMode & REQ_MODE_RACEMIC_STEREO ) { + pINChI->nFlags |= INCHI_FLAG_RAC_STEREO; + } + if ( Stereo->nCompInv2Abs ) { + if ( Stereo->nCompInv2Abs == -1 ) { + /* switch pointers so that the stereo becomes the smallest (relative) */ + /* flag Stereo->nCompInv2Abs == -1 will keep track of this exchange */ + AT_NUMB *nNumberInv = Stereo->nNumberInv; + S_CHAR *t_parityInv = Stereo->t_parityInv; + Stereo->nNumberInv = Stereo->nNumber; + Stereo->t_parityInv = Stereo->t_parity; + Stereo->nNumber = nNumberInv; + Stereo->t_parity = t_parityInv; + switch_ptrs( &pCanonRank, &pCanonRankInv ); + switch_ptrs( &pCanonOrd, &pCanonOrdInv ); + bUseIsotopicNumberingInv = 1; + } + } + } + + for ( i = 0; i < num_atoms; i ++ ) { + pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv[i] = at[pCanonOrdInv[i]].orig_at_number; + pINChI_Aux->nIsotopicOrigAtNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; + } + if ( bUseIsotopicNumberingInv ) { + switch_ptrs( &pCanonRank, &pCanonRankInv ); + switch_ptrs( &pCanonOrd, &pCanonOrdInv ); + memcpy( pCanonRank, pCanonRankInv, num_at_tg * sizeof(pCanonRank[0]) ); + memcpy( pCanonOrd, pCanonOrdInv, num_at_tg * sizeof(pCanonOrd[0]) ); + } + pCanonRankInv = NULL; + pCanonOrdInv = NULL; + pOrigNosInCanonOrd = NULL; + } else { + /* no isotopic stereo */ + pCanonOrd = pCS->nLenCanonOrdIsotopicStereo > 0? pCS->nCanonOrdIsotopicStereo : + pCS->nLenCanonOrdIsotopic > 0? pCS->nCanonOrdIsotopic : NULL; + pCanonRank = pCanonRankAtoms; + pOrigNosInCanonOrd = pINChI_Aux->nIsotopicOrigAtNosInCanonOrd; + if ( pCanonOrd && pCanonRank ) { + for ( i = 0; i < num_atoms; i ++ ) { /* Fix13 -- out of bounds */ + pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); + pOrigNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; + } + for ( ; i < num_at_tg; i ++ ) { /* Fix13 -- out of bounds */ + pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); + } + } + } + /*pCanonOrdIso = pCanonOrd;*/ + + pConstitEquNumb = pINChI_Aux->nConstitEquIsotopicNumbers; + pSymmRank = pCS->nSymmRankIsotopic; + if ( pCanonOrd && pCanonRank && pConstitEquNumb && pSymmRank ) { + for ( i = 0; i < num_atoms; i ++ ) { + pConstitEquNumb[i] = pSymmRank[pCanonOrd[i]]; + pSortOrd[i] = i; + } + for ( ; i < num_at_tg; i ++ ) { + pSortOrd[i] = i; + } + + pCG->m_pn_RankForSort = pConstitEquNumb; + inchi_qsort( pCG, pSortOrd, num_atoms, sizeof(pSortOrd[0]), CompRanksOrd ); + + for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= num_atoms; j ++ ) { + if ( j == num_atoms || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) { + nMinOrd ++; + if ( j - i > 1 ) { + /* found a sequence of equivalent atoms: i..j-1 */ + while ( i < j ) { + pConstitEquNumb[pSortOrd[i++]] = nMinOrd; + } + } else { + pConstitEquNumb[pSortOrd[i++]] = 0; /* nMinOrd; */ + } + nMinOrd = pSortOrd[j]; + } + } + } else { + goto exit_function; /* no isotopic info available */ + } + /* isotopic atoms */ + n = pINChI->nNumberOfIsotopicAtoms = pCS->nLenLinearCTIsotopic; + for ( i = 0; i < n; i ++ ) { + pINChI->IsotopicAtom[i].nAtomNumber = pCS->LinearCTIsotopic[i].at_num; + pINChI->IsotopicAtom[i].nIsoDifference = pCS->LinearCTIsotopic[i].iso_atw_diff; + pINChI->IsotopicAtom[i].nNum_H = pCS->LinearCTIsotopic[i].num_1H; + pINChI->IsotopicAtom[i].nNum_D = pCS->LinearCTIsotopic[i].num_D; + pINChI->IsotopicAtom[i].nNum_T = pCS->LinearCTIsotopic[i].num_T; + } + /* isotopic tautomeric groups */ + n = pINChI->nNumberOfIsotopicTGroups = pCS->nLenLinearCTIsotopicTautomer; + for ( i = 0; i < n; i ++ ) { + pINChI->IsotopicTGroup[i].nTGroupNumber = pCS->LinearCTIsotopicTautomer[i].tgroup_num; + pINChI->IsotopicTGroup[i].nNum_H = pCS->LinearCTIsotopicTautomer[i].num[2]; + pINChI->IsotopicTGroup[i].nNum_D = pCS->LinearCTIsotopicTautomer[i].num[1]; + pINChI->IsotopicTGroup[i].nNum_T = pCS->LinearCTIsotopicTautomer[i].num[0]; + } + /* atoms that may exchange isotopic H-atoms */ + if ( pCS->nExchgIsoH && pINChI->nPossibleLocationsOfIsotopicH ) { + for ( i = 0, j = 1; i < num_atoms; i ++ ) { + if ( pCS->nExchgIsoH[i] ) { + pINChI->nPossibleLocationsOfIsotopicH[j++] = (AT_NUMB)(i+1); /* canonical number */ + } + } + pINChI->nPossibleLocationsOfIsotopicH[0] = (AT_NUMB)j; /* length including the 0th element */ + } + + if ( nStereoUnmarkMode = UnmarkAllUndefinedUnknownStereo( pINChI->StereoIsotopic, nUserMode ) ) { + pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU)? INCHI_FLAG_SC_IGN_ALL_ISO_UU : 0; + pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU)? INCHI_FLAG_SC_IGN_ALL_ISO_UU : 0; + if ( (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU) || + (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU) ) { + WarningMessage(pStrErrStruct, "Omitted undefined stereo"); + } + } + /* mark ambiguous stereo */ + MarkAmbiguousStereo( at, norm_at, 1 /* isotopic */, pCanonOrd, + pCS->LinearCTIsotopicStereoCarb, pCS->nLenLinearCTIsotopicStereoCarb, + pCS->LinearCTIsotopicStereoDble, pCS->nLenLinearCTIsotopicStereoDble ); + + /*********************************************************** + * isotopic tautomeric group(s) numbering and symmetry; + * should not depend on switching to rel. stereo numbering + */ + if ( pINChI->lenTautomer && pINChI_Aux->nConstitEquIsotopicTGroupNumbers && pCS->nSymmRankIsotopicTaut && + (pCS->nLenLinearCTIsotopic || pCS->nLenLinearCTIsotopicTautomer) && + t_group_info && t_group_info->num_t_groups > 0 ) { + n = t_group_info->num_t_groups; + pCanonOrdTaut = pCS->nLenCanonOrdIsotopicStereoTaut > 0? + (n=pCS->nLenCanonOrdIsotopicStereoTaut, pCS->nCanonOrdIsotopicStereoTaut) : + pCS->nLenCanonOrdIsotopicTaut > 0? + (n=pCS->nLenCanonOrdIsotopicTaut,pCS->nCanonOrdIsotopicTaut) : (n=0,(AT_RANK*)NULL); + pConstitEquNumb = pINChI_Aux->nConstitEquIsotopicTGroupNumbers; + pSymmRank = pCS->nSymmRankIsotopicTaut; + if ( pCanonOrdTaut && pSymmRank && pConstitEquNumb && n > 0 ) { + for ( i = 0; i < n; i ++ ) { + pConstitEquNumb[i] = pSymmRank[pCanonOrdTaut[i]]; + pSortOrd[i] = i; + } + + pCG->m_pn_RankForSort = pConstitEquNumb; + inchi_qsort( pCG, pSortOrd, n, sizeof(pSortOrd[0]), CompRanksOrd ); + + for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= n; j ++ ) { + if ( j == n || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) { + nMinOrd ++; + if ( j - i > 1 ) { + /* found a sequence of equivalent t-groups: i..j-1 */ + while ( i < j ) { + pConstitEquNumb[pSortOrd[i++]] = nMinOrd; + } + } else { + pConstitEquNumb[pSortOrd[i++]] = 0; /* nMinOrd; */ + } + nMinOrd = pSortOrd[j]; /* at the end j = n */ + } + } + } + } + + +exit_function: + if ( pCanonRankAtoms ) + inchi_free( pCanonRankAtoms ); + if ( pSortOrd ) + inchi_free( pSortOrd ); + + pINChI->nErrorCode |= nErrorCode; + pINChI_Aux->nErrorCode |= nErrorCode; + + return ret; +} + + +void make_norm_atoms_from_inp_atoms(INCHIGEN_DATA *gendata, INCHIGEN_CONTROL *genctl) +{ +/* TODO: make a full copy (with allocs) of atom arrays */ +size_t t1; +int k; + + for ( k = 0; k < INCHI_NUM; k++) + { + if (NULL!=genctl->InpNormAtData[k]) + { + t1 = genctl->StructData.num_components[k] * sizeof(NORM_ATOMS); + memcpy(gendata->NormAtomsNontaut[k], genctl->InpNormAtData[k], t1); + } + + if (NULL!=genctl->InpNormTautData[k]) + { + t1 = genctl->StructData.num_components[k] * sizeof(NORM_ATOMS); + memcpy(gendata->NormAtomsTaut[k], genctl->InpNormTautData[k], t1); + } + } +} diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/aux2atom.h b/INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll_b.c similarity index 59% rename from INCHI-1-SRC/INCHI_API/inchi_dll/aux2atom.h rename to INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll_b.c index f6b2679..f6ff1f7 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/aux2atom.h +++ b/INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll_b.c @@ -1,2792 +1,2949 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -/* - The code in this #include file reads InChI AuxInfo -*/ - -/****************************************************************************/ -#define MIN_BOND_LENGTH (1.0e-6) -#define INCHI_LINE_LEN 512 /*1024*/ /*256*/ -#define INCHI_LINE_ADD 384 /*128*/ /*64*/ -/* Note: (INCHI_LINE_LEN - INCHI_LINE_ADD) > (length of the longest item: szCoord) = 33 */ -/*****************************************************************************/ - -#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) - -#define AB_MAX_WELL_DEFINED_PARITY inchi_max(INCHI_PARITY_ODD, INCHI_PARITY_EVEN) /* 1, 2 => well defined parities, uncluding 'unknown' */ -#define AB_MIN_WELL_DEFINED_PARITY inchi_min(INCHI_PARITY_ODD, INCHI_PARITY_EVEN) /* min(INCHI_PARITY_ODD, INCHI_PARITY_EVEN) */ -#define ATOM_PARITY_WELL_DEF(X) (AB_MIN_WELL_DEFINED_PARITY <= (X) && (X) <= AB_MAX_WELL_DEFINED_PARITY) - -#define inchi_NUMH2(AT,CUR_AT) ((AT[CUR_AT].num_iso_H[0]>0?AT[CUR_AT].num_iso_H[0]:0) +AT[CUR_AT].num_iso_H[1]+AT[CUR_AT].num_iso_H[2]+AT[CUR_AT].num_iso_H[3]) - -#define SB_PARITY_FLAG 0x38 /* disconnected structure has undef. parity */ -#define SB_PARITY_SHFT 3 -#define SB_PARITY_MASK 0x07 -#define SB_PARITY_1(X) (X & SB_PARITY_MASK) /* refers to connected structure */ -#define SB_PARITY_2(X) (((X) >> SB_PARITY_SHFT) & SB_PARITY_MASK) /* refers to connected structure */ - - - - - - -#endif - - - -#ifdef TARGET_API_LIB - -void FreeInchi_Atom( inchi_Atom **at ); -inchi_Atom *CreateInchi_Atom( int num_atoms ); -void FreeInchi_Input( inchi_Input *inp_at_data ); -S_SHORT *is_in_the_slist( S_SHORT *pathAtom, S_SHORT nNextAtom, int nPathLen ); -int is_element_a_metal( char szEl[] ); - - - - -#endif - -#ifndef TARGET_EXE_USING_API - -void FreeInchi_Stereo0D( inchi_Stereo0D **stereo0D ); -inchi_Stereo0D *CreateInchi_Stereo0D( int num_stereo0D ); -int Extract0DParities( inp_ATOM *at, int nNumAtoms, inchi_Stereo0D *stereo0D, - int num_stereo0D, char *pStrErr, int *err, - int vABParityUnknown); - -#endif - - -#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) - -/* inchi_fgets */ - -#endif - - -#ifdef TARGET_API_LIB -/******************************************************************************************************/ -void FreeInchi_Atom( inchi_Atom **at ) -{ - if ( at && *at ) { - inchi_free( *at ); - *at = NULL; - } -} -/******************************************************************************************************/ -inchi_Atom *CreateInchi_Atom( int num_atoms ) -{ - inchi_Atom *p = (inchi_Atom* ) inchi_calloc(num_atoms, sizeof(inchi_Atom) ); - return p; -} -/******************************************************************************************************/ -void FreeInchi_Input( inchi_Input *inp_at_data ) -{ - FreeInchi_Atom( &inp_at_data->atom ); - FreeInchi_Stereo0D( &inp_at_data->stereo0D ); - memset( inp_at_data, 0, sizeof(*inp_at_data) ); -} -/*************************************************************************/ -S_SHORT *is_in_the_slist( S_SHORT *pathAtom, S_SHORT nNextAtom, int nPathLen ) -{ - for ( ; nPathLen && *pathAtom != nNextAtom; nPathLen--, pathAtom++ ) - ; - return nPathLen? pathAtom : NULL; -} -/************************************************/ -int is_element_a_metal( char szEl[] ) -{ - static const char szMetals[] = "K;V;Y;W;U;" - "Li;Be;Na;Mg;Al;Ca;Sc;Ti;Cr;Mn;Fe;Co;Ni;Cu;Zn;Ga;Rb;Sr;Zr;" - "Nb;Mo;Tc;Ru;Rh;Pd;Ag;Cd;In;Sn;Sb;Cs;Ba;La;Ce;Pr;Nd;Pm;Sm;" - "Eu;Gd;Tb;Dy;Ho;Er;Tm;Yb;Lu;Hf;Ta;Re;Os;Ir;Pt;Au;Hg;Tl;Pb;" - "Bi;Po;Fr;Ra;Ac;Th;Pa;Np;Pu;Am;Cm;Bk;Cf;Es;Fm;Md;No;Lr;Rf;"; - const int len = strlen(szEl); - const char *p; - - if ( 0 < len && len <= 2 && - isalpha( UCINT szEl[0] ) && isupper( szEl[0] ) && - (p = strstr(szMetals, szEl) ) && p[len] == ';' ) { - - return 1; /*return AtType_Metal;*/ - } - return 0; -} - -#endif - - -#ifndef TARGET_EXE_USING_API -/******************************************************************************************************/ -inchi_Stereo0D *CreateInchi_Stereo0D( int num_stereo0D ) -{ - return (inchi_Stereo0D* ) inchi_calloc(num_stereo0D, sizeof(inchi_Stereo0D) ); -} -/******************************************************************************************************/ -void FreeInchi_Stereo0D( inchi_Stereo0D **stereo0D ) -{ - if ( stereo0D && *stereo0D ) { - inchi_free( *stereo0D ); - *stereo0D = NULL; - } -} -#endif - -#define INPUT_FILE INCHI_IOSTREAM - -#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) - -#ifdef TARGET_API_LIB -#define INChITo_Atom ll_INChIToInchi_Atom -#else -#define INChITo_Atom ee_INChIToIbChI_Atom -#define FindToken e_FindToken -#define LoadLine e_LoadLine -#endif - -#define AT_NUM_BONDS(AT) (AT).num_bonds -#define ATOM_NUMBER AT_NUM -#define IN_NEIGH_LIST is_in_the_slist -/*#define INPUT_FILE INCHI_IOSTREAM*/ -#define Create_Atom CreateInchi_Atom -#define AT_BONDS_VAL(AT,I) AT[I].num_iso_H[0] -#define ISOLATED_ATOM (-15) -#define NUM_ISO_Hk(AT,I,K) AT[I].num_iso_H[K+1] -#define IS_METAL_ATOM(AT,I) is_element_a_metal( AT[I].elname ) - -#else - -#define inchi_Atom inp_ATOM -#define AT_NUM_BONDS(AT) (AT).valence -#define ATOM_NUMBER AT_NUMB -#define IN_NEIGH_LIST is_in_the_list -#define inchi_NUMH2(AT,N) NUMH(AT,N) -#define INChITo_Atom cc_INChIToInpAtom -/*#define INPUT_FILE FILE*/ -#define Create_Atom CreateInpAtom -#define AT_BONDS_VAL(AT,I) AT[I].chem_bonds_valence -#define ISOLATED_ATOM 15 -#define NUM_ISO_Hk(AT,I,K) AT[I].num_iso_H[K] -#define IS_METAL_ATOM(AT,I) is_el_a_metal( AT[I].el_number ) - -#endif - -/*****************************************************************************/ -/* local prototypes */ -char *FindToken( INCHI_IOSTREAM *inp_molfile, int *bTooLongLine, const char *sToken, int lToken, - char *szLine, int nLenLine, char *p, int *res ); -char *LoadLine( INPUT_FILE *inp_molfile, int *bTooLongLine, int *bItemIsOver, char **s, - char *szLine, int nLenLine, int nMinLen2Load, char *p, int *res ); - - -int INChITo_Atom(INPUT_FILE *inp_molfile, MOL_COORD **szCoord, - inchi_Stereo0D **stereo0D, int *num_stereo0D, - int bDoNotAddH, int vABParityUnknown, INPUT_TYPE nInputType, inchi_Atom **at, - int max_num_at, - int *num_dimensions, int *num_bonds, char *pSdfLabel, char *pSdfValue, - long *Id, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ); - - -#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) -/*****************************************************************************/ -int INChIToInchi_Atom ( INCHI_IOSTREAM *inp_molfile, inchi_Stereo0D **stereo0D, int *num_stereo0D, - int bDoNotAddH, int vABParityUnknown, INPUT_TYPE nInputType, - inchi_Atom **at, int max_num_at, - int *num_dimensions, int *num_bonds, char *pSdfLabel, char *pSdfValue, - long *Id, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ); - -int INChIToInchi_Atom ( INCHI_IOSTREAM *inp_molfile, inchi_Stereo0D **stereo0D, int *num_stereo0D, - int bDoNotAddH, int vABParityUnknown, INPUT_TYPE nInputType, - inchi_Atom **at, int max_num_at, - int *num_dimensions, int *num_bonds, char *pSdfLabel, char *pSdfValue, - long *Id, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ) -{ - return INChITo_Atom ( inp_molfile, NULL, stereo0D, num_stereo0D, - bDoNotAddH, vABParityUnknown, nInputType, at, max_num_at, - num_dimensions, num_bonds, pSdfLabel, pSdfValue, - Id, pInpAtomFlags, err, pStrErr ); -} -#else -int INChIToInpAtom ( INCHI_IOSTREAM *inp_molfile, MOL_COORD **szCoord, - int bDoNotAddH, int vABParityUnknown, INPUT_TYPE nInputType, inp_ATOM **at, - int max_num_at, - int *num_dimensions, int *num_bonds, char *pSdfLabel, char *pSdfValue, - long *Id, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ); -int INChIToInpAtom ( INCHI_IOSTREAM *inp_molfile, MOL_COORD **szCoord, - int bDoNotAddH, int vABParityUnknown, INPUT_TYPE nInputType, inp_ATOM **at, - int max_num_at, - int *num_dimensions, int *num_bonds, char *pSdfLabel, char *pSdfValue, - long *Id, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ) -{ - return INChITo_Atom ( inp_molfile, szCoord, NULL, NULL, - bDoNotAddH, vABParityUnknown, nInputType, at, max_num_at, - num_dimensions, num_bonds, pSdfLabel, pSdfValue, - Id, pInpAtomFlags, err, pStrErr ); -} -#endif -/*****************************************************************************/ -char *FindToken( INCHI_IOSTREAM *inp_molfile, int *bTooLongLine, const char *sToken, int lToken, - char *szLine, int nLenLine, char *p, int *res ) -{ - char *q; - int res2; - - while ( !(q = strstr( p, sToken ) ) ) { - if ( (q = strrchr( p, '/' )) && (q + lToken > szLine + *res) ) { - *res -= q - szLine; /* res = the length of the szLine to be left in */ - memmove( szLine, q, *res + 1); - } else { - *res = 0; - } - if ( !*bTooLongLine || - 0 > (res2 = inchi_ios_getsTab1( szLine + *res, nLenLine - *res - 1, - inp_molfile, bTooLongLine ) ) ) { - /* the line is over or end of file */ - return NULL; - } else { - *res += res2; - p = szLine; - } - } - - return q + lToken; -} -/*****************************************************************************/ -char *LoadLine( INCHI_IOSTREAM *inp_molfile, int *bTooLongLine, int *bItemIsOver, char **s, - char *szLine, int nLenLine, int nMinLen2Load, char *p, int *res ) -{ - int pos = p - szLine, res2; - if ( !*bItemIsOver && nLenLine - (*res - pos) > nMinLen2Load ) { - /* load the next portion if possible */ - if ( pos ) { - *res -= pos; - memmove( szLine, p, *res+1 ); - p = szLine; - if ( *s ) { - *s -= pos; - } - pos = 0; - } - res2 = inchi_ios_getsTab1( szLine + *res, nLenLine - *res - 1, inp_molfile, bTooLongLine ); - if ( res2 > 0 ) { - *bItemIsOver = ( (*s = strchr( p + *res, '/') ) || !*bTooLongLine ); - *res += res2; - } else { - *bItemIsOver = 1; - } - } - return p; -} -/*****************************************************************************/ -int INChITo_Atom(INCHI_IOSTREAM *inp_molfile, MOL_COORD **szCoord, - inchi_Stereo0D **stereo0D, int *num_stereo0D, - int bDoNotAddH, int vABParityUnknown, INPUT_TYPE nInputType, inchi_Atom **at, - int max_num_at, - int *num_dimensions, int *num_bonds, char *pSdfLabel, char *pSdfValue, - long *Id, INCHI_MODE *pInpAtomFlags, int *err, char *pStrErr ) -{ - int num_atoms = 0, bFindNext = 0, len, bHeaderRead, bItemIsOver, bErrorMsg, bRestoreInfo; - int bFatal = 0, num_struct = 0; - int i, k, k2, res, bond_type, bond_stereo1, bond_stereo2, bond_char, neigh, bond_parity, bond_parityNM; - int bTooLongLine, res2, bTooLongLine2, pos, hlen, hk; - long longID; - char szLine[INCHI_LINE_LEN], szNextLine[INCHI_LINE_ADD], *p, *q, *s, parity; - int b2D=0, b3D=0, b23D, nNumBonds = 0, bNonZeroXYZ, bNonMetal; - int len_stereo0D = 0, max_len_stereo0D = 0; - inchi_Stereo0D *atom_stereo0D = NULL; - inchi_Atom *atom = NULL; - MOL_COORD *pszCoord = NULL; - INCHI_MODE InpAtomFlags = 0; /* 0 or FLAG_INP_AT_NONCHIRAL or FLAG_INP_AT_CHIRAL */ - static const char szIsoH[] = "hdt"; - /* plain tags */ - static const char sStructHdrPln[] = "Structure:"; - static const char sStructHdrPlnNoLblVal[] = " is missing"; - static char sStructHdrPlnAuxStart[64] =""; /*"$1.1Beta/";*/ - static int lenStructHdrPlnAuxStart = 0; - static const char sStructHdrPlnRevAt[] = "/rA:"; - static const char sStructHdrPlnRevBn[] = "/rB:"; - static const char sStructHdrPlnRevXYZ[] = "/rC:"; - const char *sToken; - int lToken; - if ( !lenStructHdrPlnAuxStart ) { - lenStructHdrPlnAuxStart = sprintf( sStructHdrPlnAuxStart, "AuxInfo=" ); - } - - if ( at ) { - if ( *at && max_num_at ) { - memset( *at, 0, max_num_at * sizeof(**at) ); - } -#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) - if ( stereo0D && num_stereo0D ) { - if ( *stereo0D && *num_stereo0D ) { - max_len_stereo0D = *num_stereo0D; - memset( *stereo0D, 0, max_len_stereo0D * sizeof( **stereo0D )); - } else { - max_len_stereo0D = 0; - } - } -#else - if ( szCoord && *szCoord ) { - inchi_free( *szCoord ); - *szCoord = NULL; - } -#endif - } else { - bFindNext = 1; - } - bHeaderRead = bErrorMsg = bRestoreInfo = 0; - *num_dimensions = *num_bonds = 0; - - /*************************************************************/ - /* extract reversibility info from plain text INChI format */ - /*************************************************************/ - if ( nInputType == INPUT_INCHI_PLAIN ) { - bHeaderRead = hk = 0; - while ( 0 < (res = inchi_ios_getsTab( szLine, sizeof(szLine)-1, inp_molfile, &bTooLongLine ) ) ) { - - /********************* find and interpret structure header ************/ - if ( !bTooLongLine && - (hlen=sizeof(sStructHdrPln)-1, !memcmp(szLine, sStructHdrPln, hlen)) ) { - p = szLine + hlen; - longID = 0; - num_atoms = 0; - /* structure number */ - longID = strtol( p, &q, 10 ); - if ( q && q[0] == '.' && q[1] == ' ' ) { - p = q+2; - } - p = p + strspn( p, " \n\r" ); - - if ( pSdfLabel ) { - pSdfLabel[0] = '\0'; - } - if ( pSdfValue ) { - pSdfValue[0] = '\0'; - } - - if ( *p ) { - /* has label name */ - /*p ++;*/ - if ( q = strchr( p, '=' ) ) { - /* '=' separates label name from the value */ - len = inchi_min( q-p+1, MAX_SDF_HEADER-1); - if ( pSdfLabel ) { - mystrncpy( pSdfLabel, p, len ); - LtrimRtrim( pSdfLabel, &len ); - } - p = q+1; - q = p + (int)strlen( p ); - if ( q-p > 0 ) { - len = inchi_min( q-p+1, MAX_SDF_VALUE-1); - if ( pSdfValue ) { - mystrncpy( pSdfValue, p, len ); - } - p = q; - } - - } else - if ( q = strstr( p, sStructHdrPlnNoLblVal ) ) { - len = inchi_min( q-p+1, MAX_SDF_HEADER-1); - if ( pSdfLabel ) { - mystrncpy( pSdfLabel, p, len ); - } - p = q+1; - } - } - if ( Id ) - *Id = longID; - - bHeaderRead = 1; - bErrorMsg = bRestoreInfo = 0; - } else - if ( !memcmp( szLine, sStructHdrPlnAuxStart, lenStructHdrPlnAuxStart) ) { - /* found the header of the AuxInfo, read AuxInfo head of the line */ - if ( !bHeaderRead ) { - longID = 0; - if ( Id ) - *Id = longID; - if ( pSdfLabel ) { - pSdfLabel[0] = '\0'; - } - if ( pSdfValue ) { - pSdfValue[0] = '\0'; - } - } - bHeaderRead = 0; - /* check for empty "AuxInfo=ver//" */ - p = strchr( szLine + lenStructHdrPlnAuxStart, '/' ); - if ( p && p[1] == '/' && (!p[2] || '\n' == p[2]) ) { - goto bypass_end_of_INChI_plain; - } - /***************** search for atoms block (plain) **********************/ - p = szLine; - sToken = sStructHdrPlnRevAt; - lToken = sizeof(sStructHdrPlnRevAt)-1; - /* search for sToken in the line; load next segments of the line if sToken has not found */ - p = FindToken( inp_molfile, &bTooLongLine, sToken, lToken, - szLine, sizeof(szLine), p, &res ); - if ( !p ) { - *err = INCHI_INP_ERROR_ERR; - num_atoms = INCHI_INP_ERROR_RET; - MOLFILE_ERR_SET (*err, 0, "Missing atom data"); - goto bypass_end_of_INChI_plain; - } else { - /* atoms block started */ - i = 0; - res2 = bTooLongLine2 = -1; - bItemIsOver = (s = strchr( p, '/') ) || !bTooLongLine; - while ( 1 ) { - p = LoadLine( inp_molfile, &bTooLongLine, &bItemIsOver, &s, - szLine, sizeof(szLine), INCHI_LINE_ADD, p, &res ); - if ( !i ) { - /* allocate atom */ - num_atoms = strtol( p, &q, 10 ); - if ( !num_atoms || !q || !*q ) { - num_atoms = 0; /* no atom data */ - goto bypass_end_of_INChI_plain; - } - p = q; - /* Molfile chirality flag */ - switch( *p ) { - case 'c': - InpAtomFlags |= FLAG_INP_AT_CHIRAL; - p ++; - break; - case 'n': - InpAtomFlags |= FLAG_INP_AT_NONCHIRAL; - p ++; - break; - } - if ( at && *at ) { - if ( num_atoms > max_num_at ) { - inchi_free( *at ); - *at = NULL; - } else { - memset( *at, 0, max_num_at * sizeof( **at ) ); - atom = *at; - } - } - if ( !at || !*at ) { - atom = Create_Atom( num_atoms+1 ); - if ( !atom ) { - num_atoms = INCHI_INP_FATAL_RET; /* was -1; error */ - *err = INCHI_INP_FATAL_ERR; - MOLFILE_ERR_SET (*err, 0, "Out of RAM"); - goto bypass_end_of_INChI_plain; - } - } - if ( stereo0D && *stereo0D ) { - if ( num_atoms > max_len_stereo0D ) { - FreeInchi_Stereo0D( stereo0D ); - } else { - memset( *stereo0D, 0, max_len_stereo0D * sizeof( **stereo0D ) ); - atom_stereo0D = *stereo0D; - } - } - if ( !stereo0D || !*stereo0D ) { - max_len_stereo0D = num_atoms+1; - atom_stereo0D = CreateInchi_Stereo0D( max_len_stereo0D ); - if ( !atom_stereo0D ) { - num_atoms = INCHI_INP_FATAL_RET; /* fatal error: cannot allocate */ - *err = INCHI_INP_FATAL_ERR; - MOLFILE_ERR_SET (*err, 0, "Out of RAM"); - goto bypass_end_of_INChI_plain; - } - } - } - /* element, first char */ - if ( !isalpha( UCINT *p ) || !isupper( UCINT *p ) || i >= num_atoms ) { - break; /* end of atoms block */ - } - atom[i].elname[0] = *p ++; - /* element, second char */ - if ( isalpha( UCINT *p ) && islower( UCINT *p ) ) { - atom[i].elname[1] = *p ++; - } - #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) - #else - atom[i].el_number = get_periodic_table_number( atom[i].elname ); - #endif - /* bonds' valence + number of non-isotopic H */ - if ( isdigit( UCINT *p ) ) { - AT_BONDS_VAL(atom,i) = (char)strtol( p, &q, 10 ); - if ( !AT_BONDS_VAL(atom,i) ) - AT_BONDS_VAL(atom,i) = ISOLATED_ATOM; /* same convention as in MOLfile, found zero bonds valence */ - p = q; - } - /* charge */ - atom[i].charge = (*p == '+')? 1 : (*p == '-')? -1 : 0; - if ( atom[i].charge ) { - p ++; - if ( isdigit( UCINT *p ) ) { - atom[i].charge *= (S_CHAR)(strtol( p, &q, 10 ) & CHAR_MASK); - p = q; - } - } - /* radical */ - if ( *p == '.' ) { - p ++; - if ( isdigit( UCINT *p ) ) { - atom[i].radical = (S_CHAR)strtol( p, &q, 10 ); - p = q; - } - } - /* isotopic mass */ - if ( *p == 'i' ) { - p ++; - if ( isdigit( UCINT *p ) ) { - int mw = strtol( p, &q, 10 ); - p = q; - #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) - atom[i].isotopic_mass = mw; - #else - mw -= get_atw_from_elnum( atom[i].el_number ); - if ( mw >= 0 ) - mw ++; - atom[i].iso_atw_diff = mw; - #endif - } - } - /* parity */ - switch( *p ) { - case 'o': - parity = INCHI_PARITY_ODD; - p ++; - break; - case 'e': - parity = INCHI_PARITY_EVEN; - p ++; - break; - case 'u': - parity = INCHI_PARITY_UNKNOWN; - p ++; - break; - case '?': - parity = INCHI_PARITY_UNDEFINED; - p ++; - break; - default: - parity = 0; - break; - } - if ( parity ) { - atom_stereo0D[len_stereo0D].central_atom = i; - atom_stereo0D[len_stereo0D].parity = parity; - atom_stereo0D[len_stereo0D].type = INCHI_StereoType_Tetrahedral; - len_stereo0D ++; - } - /* isotopic h, d, t */ - for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) { - if ( *p == szIsoH[k] ) { - NUM_ISO_Hk(atom,i,k) = 1; - p ++; - if ( isdigit( UCINT *p ) ) { - NUM_ISO_Hk(atom,i,k) = (char)strtol( p, &q, 10 ); - p = q; - } - } - } - i ++; - } - if ( !bItemIsOver || i != num_atoms || s && p != s ) { - num_atoms = INCHI_INP_ERROR_RET; /* error */ - *err = INCHI_INP_ERROR_ERR; - MOLFILE_ERR_SET (*err, 0, "Wrong number of atoms"); - goto bypass_end_of_INChI_plain; - } - } - /***************** search for bonds block (plain) and read it *****************/ - /*p = szLine;*/ - sToken = sStructHdrPlnRevBn; - lToken = sizeof(sStructHdrPlnRevBn)-1; - /* search for sToken in the line; load next segments of the line if sToken has not found */ - p = FindToken( inp_molfile, &bTooLongLine, sToken, lToken, - szLine, sizeof(szLine), p, &res ); - if ( !p ) { - num_atoms = INCHI_INP_ERROR_RET; /* error */ - *err = INCHI_INP_ERROR_ERR; - MOLFILE_ERR_SET (*err, 0, "Missing bonds data"); - goto bypass_end_of_INChI_plain; - } else { - /* bonds block started */ - i = 1; - res2 = bTooLongLine2 = -1; - bItemIsOver = (s = strchr( p, '/') ) || !bTooLongLine; - if ( 1 == num_atoms ) { - /* needed because the next '/' may be still out of szLine */ - p = LoadLine( inp_molfile, &bTooLongLine, &bItemIsOver, &s, - szLine, sizeof(szLine), INCHI_LINE_ADD, p, &res ); - } - while ( i < num_atoms ) { - p = LoadLine( inp_molfile, &bTooLongLine, &bItemIsOver, &s, - szLine, sizeof(szLine), INCHI_LINE_ADD, p, &res ); - if ( i >= num_atoms || s && p >= s ) { - break; /* end of bonds (plain) */ - } - /* bond, first char */ - if ( *p == ';' ) { - p ++; - i ++; - continue; - } - if ( !isalpha( UCINT *p ) ) { - num_atoms = INCHI_INP_ERROR_RET; /* error */ - *err = INCHI_INP_ERROR_ERR; - MOLFILE_ERR_SET (*err, 0, "Wrong bonds data"); - goto bypass_end_of_INChI_plain; - } - bond_char = *p ++; - /* bond parity */ - switch( *p ) { - case '-': - bond_parity = INCHI_PARITY_ODD; - p ++; - break; - case '+': - bond_parity = INCHI_PARITY_EVEN; - p ++; - break; - case 'u': - bond_parity = INCHI_PARITY_UNKNOWN; - p ++; - break; - case '?': - bond_parity = INCHI_PARITY_UNDEFINED; - p ++; - break; - default: - bond_parity = 0; - break; - } - if ( bond_parity ) { - switch( *p ) { - case '-': - bond_parityNM = INCHI_PARITY_ODD; - p ++; - break; - case '+': - bond_parityNM = INCHI_PARITY_EVEN; - p ++; - break; - case 'u': - bond_parityNM = INCHI_PARITY_UNKNOWN; - p ++; - break; - case '?': - bond_parityNM = INCHI_PARITY_UNDEFINED; - p ++; - break; - default: - bond_parityNM = 0; - break; - } - } else { - bond_parityNM = 0; - } - - /* neighbor of the current atom */ - if ( !isdigit( UCINT *p ) ) { - num_atoms = INCHI_INP_ERROR_RET; /* error */ - *err = INCHI_INP_ERROR_ERR; - MOLFILE_ERR_SET (*err, 0, "Wrong bonds data"); - goto bypass_end_of_INChI_plain; - } - neigh = (int)strtol( p, &q, 10 )-1; - - if ( i >= num_atoms || neigh >= num_atoms ) { - num_atoms = INCHI_INP_ERROR_RET; /* error */ - *err = INCHI_INP_ERROR_ERR; - MOLFILE_ERR_SET (*err, 0, "Bond to nonexistent atom"); - goto bypass_end_of_INChI_plain; - } - p = q; - bond_stereo1 = bond_stereo2 = 0; - - /* bond type & 2D stereo */ - switch( bond_char ) { - case 'v': - bond_type = INCHI_BOND_TYPE_SINGLE; - bond_stereo1 = INCHI_BOND_STEREO_SINGLE_1EITHER; - bond_stereo2 = INCHI_BOND_STEREO_SINGLE_2EITHER; - break; - case 'V': - bond_type = INCHI_BOND_TYPE_SINGLE; - bond_stereo1 = INCHI_BOND_STEREO_SINGLE_2EITHER; - bond_stereo2 = INCHI_BOND_STEREO_SINGLE_1EITHER; - break; - case 'w': - bond_type = INCHI_BOND_TYPE_DOUBLE; - bond_stereo1 = - bond_stereo2 = INCHI_BOND_STEREO_DOUBLE_EITHER; - break; - case 's': - bond_type = INCHI_BOND_TYPE_SINGLE; - break; - case 'd': - bond_type = INCHI_BOND_TYPE_DOUBLE; - break; - case 't': - bond_type = INCHI_BOND_TYPE_TRIPLE; - break; - case 'a': - bond_type = INCHI_BOND_TYPE_ALTERN; - break; - case 'p': - bond_type = INCHI_BOND_TYPE_SINGLE; - bond_stereo1 = INCHI_BOND_STEREO_SINGLE_1UP; - bond_stereo2 = INCHI_BOND_STEREO_SINGLE_2UP; - break; - case 'P': - bond_type = INCHI_BOND_TYPE_SINGLE; - bond_stereo1 = INCHI_BOND_STEREO_SINGLE_2UP; - bond_stereo2 = INCHI_BOND_STEREO_SINGLE_1UP; - break; - case 'n': - bond_type = INCHI_BOND_TYPE_SINGLE; - bond_stereo1 = INCHI_BOND_STEREO_SINGLE_1DOWN; - bond_stereo2 = INCHI_BOND_STEREO_SINGLE_2DOWN; - break; - case 'N': - bond_type = INCHI_BOND_TYPE_SINGLE; - bond_stereo1 = INCHI_BOND_STEREO_SINGLE_2DOWN; - bond_stereo2 = INCHI_BOND_STEREO_SINGLE_1DOWN; - break; - default: - num_atoms = INCHI_INP_ERROR_RET; /* error */ - *err = INCHI_INP_ERROR_ERR; - MOLFILE_ERR_SET (*err, 0, "Wrong bond type"); - goto bypass_end_of_INChI_plain; - } - k = AT_NUM_BONDS(atom[i]) ++; - atom[i].bond_type[k] = bond_type; - atom[i].bond_stereo[k] = bond_stereo1; - atom[i].neighbor[k] = (ATOM_NUMBER)neigh; - k2 = AT_NUM_BONDS(atom[neigh]) ++; - atom[neigh].bond_type[k2] = bond_type; - atom[neigh].bond_stereo[k2] = bond_stereo2; - atom[neigh].neighbor[k2] = (ATOM_NUMBER)i; - bond_parity |= (bond_parityNM << SB_PARITY_SHFT); - - if ( bond_parity ) { - if ( max_len_stereo0D <= len_stereo0D ) { - /* realloc atom_Stereo0D */ - inchi_Stereo0D *new_atom_stereo0D = CreateInchi_Stereo0D( max_len_stereo0D+num_atoms ); - if ( !new_atom_stereo0D ) { - num_atoms = INCHI_INP_FATAL_RET; /* fatal error: cannot allocate */ - *err = INCHI_INP_FATAL_ERR; - MOLFILE_ERR_SET (*err, 0, "Out of RAM"); - goto bypass_end_of_INChI_plain; - } - memcpy( new_atom_stereo0D, atom_stereo0D, len_stereo0D * sizeof(*atom_stereo0D) ); - FreeInchi_Stereo0D( &atom_stereo0D ); - atom_stereo0D = new_atom_stereo0D; - max_len_stereo0D += num_atoms; - } - /* (a) i may be allene endpoint and neigh = allene middle point or - (b) i may be allene middle point and neigh = allene endpoint - !!!!! CURRENTLY ONLY (b) IS ALLOWED !!!!! - */ - atom_stereo0D[len_stereo0D].neighbor[1] = neigh; /* neigh < i */ - atom_stereo0D[len_stereo0D].neighbor[2] = i; - atom_stereo0D[len_stereo0D].parity = bond_parity; - atom_stereo0D[len_stereo0D].type = INCHI_StereoType_DoubleBond; /* incl allenes & cumulenes */ - len_stereo0D ++; - } - } - if ( !bItemIsOver || i != num_atoms || s && p != s ) { - num_atoms = INCHI_INP_ERROR_RET; /* error */ - *err = INCHI_INP_ERROR_ERR; - MOLFILE_ERR_SET (*err, 0, "Wrong number of bonds"); - goto bypass_end_of_INChI_plain; - } - } - /***************** search for coordinates block (plain) **********************/ - /*p = szLine;*/ - sToken = sStructHdrPlnRevXYZ; - lToken = sizeof(sStructHdrPlnRevXYZ)-1; - /* search for sToken in the line; load next segments of the line if sToken has not found */ - p = FindToken( inp_molfile, &bTooLongLine, sToken, lToken, - szLine, sizeof(szLine), p, &res ); - if ( !p ) { - num_atoms = INCHI_INP_ERROR_RET; /* error */ - *err = INCHI_INP_ERROR_ERR; - MOLFILE_ERR_SET (*err, 0, "Missing atom coordinates data"); - goto bypass_end_of_INChI_plain; - } else { - /* coordinates block started */ - if ( pszCoord = (MOL_COORD*) inchi_malloc(inchi_max(num_atoms,1) * sizeof(MOL_COORD)) ) { - memset( pszCoord, ' ', inchi_max(num_atoms,1) * sizeof(MOL_COORD)); - } else { - num_atoms = INCHI_INP_FATAL_RET; /* allocation error */ - *err = INCHI_INP_FATAL_ERR; - MOLFILE_ERR_SET (*err, 0, "Out of RAM"); - goto bypass_end_of_INChI_plain; - } - i = 0; - res2 = bTooLongLine2 = -1; - bItemIsOver = (s = strchr( p, '/') ) || !bTooLongLine; - while ( i < num_atoms ) { - p = LoadLine( inp_molfile, &bTooLongLine, &bItemIsOver, &s, - szLine, sizeof(szLine), INCHI_LINE_ADD, p, &res ); - if ( i >= num_atoms || s && p >= s ) { - break; /* end of bonds (plain) */ - } - - /* coord, first char */ - if ( *p == ';' ) { - for ( k = 0; k < NUM_COORD; k ++ ) { - pszCoord[i][LEN_COORD*k + 4] = '0'; - } - p ++; - i ++; - continue; - } - for ( k = 0; k < 3; k ++ ) { - double xyz; - bNonZeroXYZ = 0; - if ( *p == ';' ) { - pszCoord[i][LEN_COORD*k + 4] = '0'; - xyz = 0.0; - } else - if ( *p == ',' ) { - /* empty */ - pszCoord[i][LEN_COORD*k + 4] = '0'; - xyz = 0.0; - p ++; - } else { - xyz = strtod( p, &q ); - bNonZeroXYZ = fabs(xyz) > MIN_BOND_LENGTH; - if ( q != NULL ) { - memcpy( pszCoord[i]+LEN_COORD*k, p, q-p ); - if ( *q == ',' ) - q ++; - p = q; - } else { - pszCoord[i][LEN_COORD*k + 4] = '0'; - } - } - switch( k ) { - case 0: - atom[i].x = xyz; - b2D |= bNonZeroXYZ; - break; - case 1: - atom[i].y = xyz; - b2D |= bNonZeroXYZ; - break; - case 2: - b3D |= bNonZeroXYZ; - atom[i].z = xyz; - break; - } - } - if ( *p == ';' ) { - p ++; /* end of this triple of coordinates */ - i ++; - } else { - num_atoms = INCHI_INP_ERROR_RET; /* error in input data: atoms, bonds & coord must be present together */ - *err = INCHI_INP_ERROR_ERR; - MOLFILE_ERR_SET (*err, 0, "Wrong atom coordinates data"); - goto bypass_end_of_INChI_plain; - } - } - if ( !bItemIsOver || s && p != s || i != num_atoms ) { - num_atoms = INCHI_INP_ERROR_RET; /* error */ - *err = INCHI_INP_ERROR_ERR; - MOLFILE_ERR_SET (*err, 0, "Wrong number of coordinates"); - goto bypass_end_of_INChI_plain; - } - } /* end of coordinates */ - /* set special valences and implicit H (xml) */ - b23D = b2D | b3D; - b2D = b3D = 0; - if ( at ) { - if ( !*at ) { - int a1, a2, n1, n2, valence; - int chem_bonds_valence; - int nX=0, nY=0, nZ=0, nXYZ; - *at = atom; - /* special valences */ - for ( bNonMetal = 0; bNonMetal < 1; bNonMetal ++ ) { - for ( a1 = 0; a1 < num_atoms; a1 ++ ) { - int num_bond_type[MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE + 1]; -#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) -#else - int bHasMetalNeighbor=0; -#endif - memset( num_bond_type, 0, sizeof(num_bond_type) ); - - valence = AT_BONDS_VAL(atom, a1); /* save atom valence if available */ - AT_BONDS_VAL(atom, a1) = 0; -#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) -#else - atom[a1].orig_at_number = a1+1; -#endif - nX = nY = nZ = 0; - for ( n1 = 0; n1 < AT_NUM_BONDS(atom[a1]); n1 ++ ) { - bond_type = atom[a1].bond_type[n1] - MIN_INPUT_BOND_TYPE; - if ( bond_type < 0 || bond_type > MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE ) { - bond_type = 0; - MOLFILE_ERR_SET (*err, 0, "Unknown bond type in InChI aux assigned as a single bond"); - } - - num_bond_type[ bond_type ] ++; - nNumBonds ++; - if ( b23D ) { - neigh = atom[a1].neighbor[n1]; - nX |= (fabs(atom[a1].x - atom[neigh].x) > MIN_BOND_LENGTH); - nY |= (fabs(atom[a1].y - atom[neigh].y) > MIN_BOND_LENGTH); - nZ |= (fabs(atom[a1].z - atom[neigh].z) > MIN_BOND_LENGTH); - } - } - chem_bonds_valence = 0; - for ( n1 = 0; MIN_INPUT_BOND_TYPE + n1 <= 3 && MIN_INPUT_BOND_TYPE + n1 <= MAX_INPUT_BOND_TYPE; n1 ++ ) { - chem_bonds_valence += (MIN_INPUT_BOND_TYPE + n1) * num_bond_type[n1]; - } - if ( MIN_INPUT_BOND_TYPE <= INCHI_BOND_TYPE_ALTERN && INCHI_BOND_TYPE_ALTERN <= MAX_INPUT_BOND_TYPE && - ( n2 = num_bond_type[INCHI_BOND_TYPE_ALTERN-MIN_INPUT_BOND_TYPE] ) ) { - /* accept input aromatic bonds for now */ - switch ( n2 ) { - case 2: - chem_bonds_valence += 3; /* =A- */ - break; - case 3: - chem_bonds_valence += 4; /* =A< */ - break; - default: - /* if 1 or >= 4 aromatic bonds then replace such bonds with single bonds */ - for ( n1 = 0; n1 < AT_NUM_BONDS(atom[a1]); n1 ++ ) { - if ( atom[a1].bond_type[n1] == INCHI_BOND_TYPE_ALTERN ) { - ATOM_NUMBER *p1; - a2 = atom[a1].neighbor[n1]; - p1 = IN_NEIGH_LIST( atom[a2].neighbor, (ATOM_NUMBER)a1, AT_NUM_BONDS(atom[a2]) ); - if ( p1 ) { - atom[a1].bond_type[n1] = - atom[a2].bond_type[p1-atom[a2].neighbor] = INCHI_BOND_TYPE_SINGLE; - } else { - *err = -2; /* Program error */ - MOLFILE_ERR_SET (*err, 0, "Program error interpreting InChI aux"); - num_atoms = 0; - goto bypass_end_of_INChI_plain; /* no structure */ - } - } - } - chem_bonds_valence += n2; - *err |= 32; /* Unrecognized aromatic bond(s) replaced with single */ - MOLFILE_ERR_SET (*err, 0, "Atom has 1 or more than 3 aromatic bonds"); - break; - } - } -#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) - /************************************************************************************* - * - * Set number of hydrogen atoms - */ - { - int num_iso_H; - num_iso_H = atom[a1].num_iso_H[1] + atom[a1].num_iso_H[2] + atom[a1].num_iso_H[3]; - if ( valence == ISOLATED_ATOM ) { - atom[a1].num_iso_H[0] = 0; - } else - if ( valence && valence >= chem_bonds_valence ) { - atom[a1].num_iso_H[0] = valence - chem_bonds_valence; - } else - if ( valence || bDoNotAddH ) { - atom[a1].num_iso_H[0] = 0; - } else - if ( !bDoNotAddH ) { - atom[a1].num_iso_H[0] = -1; /* auto add H */ - } - } -#else - /* added 2006-07-19 to process aromatic bonds same way as from molfile */ - if ( n2 && !valence ) { - int num_H = NUMH(atom, a1); /* only isotopic */ - int chem_valence = chem_bonds_valence; - int bUnusualValenceArom = - detect_unusual_el_valence( (int)atom[a1].el_number, atom[a1].charge, - atom[a1].radical, chem_valence, - num_H, atom[a1].valence ); - int bUnusualValenceNoArom = - detect_unusual_el_valence( (int)atom[a1].el_number, atom[a1].charge, - atom[a1].radical, chem_valence-1, - num_H, atom[a1].valence ); -#if ( CHECK_AROMBOND2ALT == 1 ) - if ( bUnusualValenceArom && !bUnusualValenceNoArom && 0 == nBondsValToMetal( atom, a1) ) -#else - if ( bUnusualValenceArom && !bUnusualValenceNoArom ) -#endif - { - /* typically NH in 5-member aromatic ring */ - chem_bonds_valence --; - } - } else - if ( n2 && valence ) { - /* atom has aromatic bonds AND the chemical valence is known */ - int num_H = NUMH(atom, a1); - int chem_valence = chem_bonds_valence + num_H; - if ( valence == chem_valence-1 ) { - /* typically NH in 5-member aromatic ring */ - chem_bonds_valence --; - } - } - - atom[a1].chem_bonds_valence = chem_bonds_valence; - atom[a1].num_H = get_num_H( atom[a1].elname, atom[a1].num_H, atom[a1].num_iso_H, atom[a1].charge, atom[a1].radical, - atom[a1].chem_bonds_valence, - valence, - 0, bDoNotAddH, bHasMetalNeighbor ); -#endif - } - } - nNumBonds /= 2; - if ( b23D && nNumBonds ) { - nXYZ = nX+nY+nZ; - b2D = (nXYZ > 0); - b3D = (nXYZ == 3); - *num_dimensions = b3D? 3 : b2D? 2 : 0; - *num_bonds = nNumBonds; - } - /*======= 0D parities =================================*/ -#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) - if ( len_stereo0D > 0 && atom_stereo0D && stereo0D ) { - *stereo0D = atom_stereo0D; - *num_stereo0D = len_stereo0D; - } else { - FreeInchi_Stereo0D( &atom_stereo0D ); - *num_stereo0D = len_stereo0D = 0; - } -#endif - for ( i = 0; i < len_stereo0D; i ++ ) { - ATOM_NUMBER *p1, *p2; - int sb_ord_from_a1 = -1, sb_ord_from_a2 = -1, bEnd1 = 0, bEnd2 = 0; - switch( atom_stereo0D[i].type ) { - - case INCHI_StereoType_Tetrahedral: - a1 = atom_stereo0D[i].central_atom; - if ( atom_stereo0D[i].parity && (AT_NUM_BONDS(atom[a1]) == 3 || AT_NUM_BONDS(atom[a1]) == 4) ) { - int ii, kk = 0; - if ( AT_NUM_BONDS(atom[a1]) == 3 ) { - atom_stereo0D[i].neighbor[kk++] = a1; - } - for ( ii = 0; ii < AT_NUM_BONDS(atom[a1]); ii ++ ) { - atom_stereo0D[i].neighbor[kk++] = atom[a1].neighbor[ii]; - } - } - - break; - - case INCHI_StereoType_DoubleBond: -#define MAX_CHAIN_LEN 20 - a1 = atom_stereo0D[i].neighbor[1]; - a2 = atom_stereo0D[i].neighbor[2]; - p1 = IN_NEIGH_LIST( atom[a1].neighbor, (ATOM_NUMBER)a2, AT_NUM_BONDS(atom[a1]) ); - p2 = IN_NEIGH_LIST( atom[a2].neighbor, (ATOM_NUMBER)a1, AT_NUM_BONDS(atom[a2]) ); - if ( !p1 || !p2 ) { - atom_stereo0D[i].type = INCHI_StereoType_None; - atom_stereo0D[i].central_atom = NO_ATOM; - atom_stereo0D[i].neighbor[0] = - atom_stereo0D[i].neighbor[3] = -1; - *err |= 64; /* Error in cumulene stereo */ - MOLFILE_ERR_SET (*err, 0, "0D stereobond not recognized"); - break; - } - /* streobond, allene, or cumulene */ - - sb_ord_from_a1 = p1 - atom[a1].neighbor; - sb_ord_from_a2 = p2 - atom[a2].neighbor; - - if ( AT_NUM_BONDS(atom[a1]) == 2 && - atom[a1].bond_type[0] + atom[a1].bond_type[1] == 2*INCHI_BOND_TYPE_DOUBLE && - 0 == inchi_NUMH2(atom, a1) && - (AT_NUM_BONDS(atom[a2]) != 2 || - atom[a2].bond_type[0] + atom[a2].bond_type[1] != 2*INCHI_BOND_TYPE_DOUBLE ) ) { - bEnd2 = 1; /* a2 is the end-atom, a1 is middle atom */ - } - if ( AT_NUM_BONDS(atom[a2]) == 2 && - atom[a2].bond_type[0] + atom[a2].bond_type[1] == 2*INCHI_BOND_TYPE_DOUBLE && - 0 == inchi_NUMH2(atom, a2) && - (AT_NUM_BONDS(atom[a1]) != 2 || - atom[a1].bond_type[0] + atom[a1].bond_type[1] != 2*INCHI_BOND_TYPE_DOUBLE ) ) { - bEnd1 = 1; /* a1 is the end-atom, a2 is middle atom */ - } - - if ( bEnd2 + bEnd1 == 1 ) { - /* allene or cumulene */ - ATOM_NUMBER chain[MAX_CHAIN_LEN+1], prev, cur, next; - if ( bEnd2 && !bEnd1 ) { - cur = a1; - a1 = a2; - a2 = cur; - sb_ord_from_a1 = sb_ord_from_a2; - } - sb_ord_from_a2 = -1; - cur = a1; - next = a2; - len = 0; - chain[len++] = cur; - chain[len++] = next; - while ( len < MAX_CHAIN_LEN ) { /* arbitrary very high upper limit to prevent infinite loop */ - prev = cur; - cur = next; - /* follow double bond path && avoid going back */ - if ( AT_NUM_BONDS(atom[cur]) == 2 && - atom[cur].bond_type[0]+atom[cur].bond_type[1] == 2*INCHI_BOND_TYPE_DOUBLE && - 0 == inchi_NUMH2(atom, cur) ) { - next = atom[cur].neighbor[atom[cur].neighbor[0] == prev]; - chain[len++] = next; - } else { - break; - } - } - if ( len > 2 && - (p2 = IN_NEIGH_LIST( atom[cur].neighbor, (ATOM_NUMBER)prev, AT_NUM_BONDS(atom[cur]))) ) { - sb_ord_from_a2 = p2 - atom[cur].neighbor; - a2 = cur; - /* by design we need to pick up the first non-stereo-bond-neighbor as "sn"-atom */ - atom_stereo0D[i].neighbor[0] = atom[a1].neighbor[sb_ord_from_a1 == 0]; - atom_stereo0D[i].neighbor[1] = a1; - atom_stereo0D[i].neighbor[2] = a2; - atom_stereo0D[i].neighbor[3] = atom[a2].neighbor[sb_ord_from_a2 == 0]; - if ( len % 2 ) { - atom_stereo0D[i].central_atom = chain[len/2]; - atom_stereo0D[i].type = INCHI_StereoType_Allene; - } else { - atom_stereo0D[i].central_atom = NO_ATOM; - } - } else { - /* error */ - atom_stereo0D[i].type = INCHI_StereoType_None; - atom_stereo0D[i].central_atom = NO_ATOM; - atom_stereo0D[i].neighbor[0] = - atom_stereo0D[i].neighbor[3] = -1; - *err |= 64; /* Error in cumulene stereo */ - MOLFILE_ERR_SET (*err, 0, "Cumulene stereo not recognized (0D)"); - - } -#undef MAX_CHAIN_LEN - } else { - /****** a normal possibly stereogenic bond -- not an allene or cumulene *******/ - /* by design we need to pick up the first non-stereo-bond-neighbor as "sn"-atom */ - sb_ord_from_a1 = p1 - atom[a1].neighbor; - sb_ord_from_a2 = p2 - atom[a2].neighbor; - atom_stereo0D[i].neighbor[0] = atom[a1].neighbor[p1 == atom[a1].neighbor]; - atom_stereo0D[i].neighbor[3] = atom[a2].neighbor[p2 == atom[a2].neighbor]; - atom_stereo0D[i].central_atom = NO_ATOM; - } - if ( atom_stereo0D[i].type != INCHI_StereoType_None && - sb_ord_from_a1 >= 0 && sb_ord_from_a2 >= 0 && - ATOM_PARITY_WELL_DEF( SB_PARITY_2(atom_stereo0D[i].parity) ) ) { - /* Detected well-defined disconnected stereo - * locate first non-metal neighbors */ - int a, n, j, /* k,*/ sb_ord, cur_neigh, min_neigh; - for ( k = 0; k < 2; k ++ ) { - a = k? atom_stereo0D[i].neighbor[2] : atom_stereo0D[i].neighbor[1]; - sb_ord = k? sb_ord_from_a2 : sb_ord_from_a1; - min_neigh = num_atoms; - for ( n = j = 0; j < AT_NUM_BONDS(atom[a]); j ++ ) { - cur_neigh = atom[a].neighbor[j]; - if ( j != sb_ord && !IS_METAL_ATOM(atom, cur_neigh) ) { - min_neigh = inchi_min( cur_neigh, min_neigh ); - } - } - if ( min_neigh < num_atoms ) { - atom_stereo0D[i].neighbor[k?3:0] = min_neigh; - } else { - MOLFILE_ERR_SET (*err, 0, "Cannot find non-metal stereobond neighor (0D)"); - } - } - } - - break; - } - } - /* end of 0D parities extraction */ -/*exit_cycle:;*/ - } -#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) -#else - /* transfer atom_stereo0D[] to atom[] */ - if ( len_stereo0D ) { - Extract0DParities( atom, num_atoms, atom_stereo0D, len_stereo0D, - pStrErr, err, vABParityUnknown ); - } -#endif - if ( pInpAtomFlags ) { - /* save chirality flag */ - *pInpAtomFlags |= InpAtomFlags; - } - } else - if ( atom ) { - inchi_free( atom ); - atom = NULL; - } -#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) -#else -#if ( FIX_READ_AUX_MEM_LEAK == 1 ) - /* 2005-08-04 avoid memory leak */ - if ( atom_stereo0D && !(stereo0D && *stereo0D == atom_stereo0D) ) { - FreeInchi_Stereo0D( &atom_stereo0D ); - } -#endif - if ( szCoord ) { - *szCoord = pszCoord; - pszCoord = NULL; - } else -#endif - if ( pszCoord ) { - inchi_free( pszCoord ); - pszCoord = NULL; - } - goto bypass_end_of_INChI_plain; - /*return num_atoms;*/ - } - } - if ( atom_stereo0D ) { - FreeInchi_Stereo0D( &atom_stereo0D ); - } - /* end of struct. reading cycle */ - if ( res <= 0 ) { - if ( *err == INCHI_INP_ERROR_ERR ) { - return num_atoms; - } - *err = INCHI_INP_EOF_ERR; - return INCHI_INP_EOF_RET; /* no more data */ - } -bypass_end_of_INChI_plain: - /* cleanup */ - if ( num_atoms == INCHI_INP_ERROR_RET && atom_stereo0D ) { - if ( stereo0D && *stereo0D == atom_stereo0D ) { - *stereo0D = NULL; - *num_stereo0D = 0; - } - FreeInchi_Stereo0D( &atom_stereo0D ); - } - while ( bTooLongLine && - 0 < inchi_ios_getsTab1( szLine, sizeof(szLine)-1, inp_molfile, &bTooLongLine ) ) { - ; - } -#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) - /* cleanup */ - if ( !*at ) { - if ( atom ) { - inchi_free( atom ); - atom = NULL; - } - if ( pszCoord ) { - inchi_free( pszCoord ); - pszCoord = NULL; - } - } -#endif - - return num_atoms; - } - - /***********************************************************/ - /* extract reversibility info from xml text INChI format */ - /* */ - /* OBSOLETE CODE because InChI output in XML */ - /* does not exist anymore. Unsupported. */ - /* */ - /***********************************************************/ - if ( nInputType == INPUT_INCHI_XML ) { - /* xml tags */ - static const char sStructHdrXml[] = " max_num_at ) { - inchi_free( *at ); - *at = NULL; - } else { - memset( *at, 0, max_num_at * sizeof( **at ) ); - atom = *at; - } - } - if ( !at || !*at ) { - atom = Create_Atom( num_atoms+1 ); - if ( !atom ) { - num_atoms = INCHI_INP_FATAL_RET; /* fatal error: cannot allocate */ - *err = INCHI_INP_FATAL_ERR; - MOLFILE_ERR_SET (*err, 0, "Out of RAM"); - goto bypass_end_of_INChI; - } - } - if ( stereo0D && *stereo0D ) { - if ( num_atoms > max_len_stereo0D ) { - FreeInchi_Stereo0D( stereo0D ); - } else { - memset( *stereo0D, 0, max_len_stereo0D * sizeof( **stereo0D ) ); - atom_stereo0D = *stereo0D; - } - } - if ( !stereo0D || !*stereo0D ) { - max_len_stereo0D = num_atoms+1; - atom_stereo0D = CreateInchi_Stereo0D( max_len_stereo0D ); - if ( !atom_stereo0D ) { - num_atoms = INCHI_INP_FATAL_RET; /* fatal error: cannot allocate */ - *err = INCHI_INP_FATAL_ERR; - MOLFILE_ERR_SET (*err, 0, "Out of RAM"); - goto bypass_end_of_INChI; - } - } - - i = 0; - bItemIsOver = 0; - res2 = bTooLongLine2 = -1; - - /* read all atoms xml */ - while ( i < num_atoms ) { - pos = p - szLine; - if ( !bItemIsOver && (int)sizeof(szLine)-res + pos > (int)sizeof(szNextLine) ) { - /* load next line if possible */ - res2 = inchi_ios_gets( szNextLine, sizeof(szNextLine)-1, inp_molfile, &bTooLongLine2 ); - if ( res2 > 0 && memcmp(szNextLine, sStructRevXmlRevAtEnd, sizeof(sStructRevXmlRevAtEnd)-1) ) { - if ( pos ) { - res -= pos; /* number of chars left to process in szLine */ - memmove( szLine, p, res*sizeof(szLine[0]) ); /* move them to the start of the line */ - } - memcpy( szLine+res, szNextLine, (res2+1)*sizeof(szNextLine[0]) ); - res += res2; - szLine[res] = '\0'; - bTooLongLine = bTooLongLine2; - p = szLine; - } else { - bItemIsOver = 1; - } - } - /* element, first char */ - if ( !isalpha( UCINT *p ) || !isupper( UCINT *p ) || i >= num_atoms ) { - bHeaderRead = 0; /* wrong atom data */ - num_atoms = INCHI_INP_ERROR_RET; /* was 0, error */ - *err = INCHI_INP_ERROR_ERR; /* 40 */ - MOLFILE_ERR_SET (*err, 0, "Wrong atoms data"); - goto bypass_end_of_INChI; - } - atom[i].elname[0] = *p ++; - /* element, second char */ - if ( isalpha( UCINT *p ) && islower( UCINT *p ) ) { - atom[i].elname[1] = *p ++; - } -#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) -#else - atom[i].el_number = get_periodic_table_number( atom[i].elname ); -#endif - /* bonds' valence */ - if ( isdigit( UCINT *p ) ) { - AT_BONDS_VAL(atom,i) = (char)strtol( p, &q, 10 ); - if ( !AT_BONDS_VAL(atom,i) ) - AT_BONDS_VAL(atom,i) = ISOLATED_ATOM; /* same convention as in MOLfile, found zero bonds valence */ - p = q; - } - /* charge */ - atom[i].charge = (*p == '+')? 1 : (*p == '-')? -1 : 0; - if ( atom[i].charge ) { - p ++; - if ( isdigit( UCINT *p ) ) { - atom[i].charge *= (S_CHAR)(strtol( p, &q, 10 ) & CHAR_MASK); - p = q; - } - } - /* radical */ - if ( *p == '.' ) { - p ++; - if ( isdigit( UCINT *p ) ) { - atom[i].radical = (S_CHAR)strtol( p, &q, 10 ); - p = q; - } - } - /* isotopic mass */ - if ( *p == 'i' ) { - p ++; - if ( isdigit( UCINT *p ) ) { - int mw = strtol( p, &q, 10 ); - p = q; -#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) - atom[i].isotopic_mass = mw; -#else - mw -= get_atw_from_elnum( atom[i].el_number ); - if ( mw >= 0 ) - mw ++; - atom[i].iso_atw_diff = mw; -#endif - } - } - /* parity */ - switch( *p ) { - case 'o': - parity = INCHI_PARITY_ODD; - p ++; - break; - case 'e': - parity = INCHI_PARITY_EVEN; - p ++; - break; - case 'u': - parity = INCHI_PARITY_UNKNOWN; - p ++; - break; - case '?': - parity = INCHI_PARITY_UNDEFINED; - p ++; - break; - default: - parity = 0; - break; - } - if ( parity ) { - atom_stereo0D[len_stereo0D].central_atom = i; - atom_stereo0D[len_stereo0D].parity = parity; - atom_stereo0D[len_stereo0D].type = INCHI_StereoType_Tetrahedral; - len_stereo0D ++; - } - /* isotopic h, d, t */ - for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) { - if ( *p == szIsoH[k] ) { - NUM_ISO_Hk(atom,i,k) = 1; - p ++; - if ( isdigit( UCINT *p ) ) { - NUM_ISO_Hk(atom,i,k) = (char)strtol( p, &q, 10 ); - p = q; - } - } - } - i ++; - } - if ( !bItemIsOver || p - szLine != res || i != num_atoms ) { - num_atoms = INCHI_INP_ERROR_RET; /* error */ - *err = INCHI_INP_ERROR_ERR; - MOLFILE_ERR_SET (*err, 0, "Wrong number of atoms"); - goto bypass_end_of_INChI; - } - /********************** bonds xml ****************************/ - res = inchi_ios_gets( szLine, sizeof(szLine)-1, inp_molfile, &bTooLongLine ); - if ( res <= 0 ) { - num_atoms = 0; /* no data */ - goto bypass_end_of_INChI; - } - if ( memcmp(szLine, sStructRevXmlRevBn, sizeof(sStructRevXmlRevBn)-1) ) { - bHeaderRead = 0; /* invalid reversibility info; look for another header */ - continue; - } - /* read (the head of) the xml bonds line */ - res = inchi_ios_gets( szLine, sizeof(szLine)-1, inp_molfile, &bTooLongLine ); - if ( res <= 0 ) { - num_atoms = INCHI_INP_ERROR_RET; /* was 0; error: no data -- eof? */ - *err = INCHI_INP_ERROR_ERR; - goto bypass_end_of_INChI; - } - i = 1; - bItemIsOver = 0; - res2 = bTooLongLine2 = -1; - p = szLine; - if ( !memcmp(szLine, sStructRevXmlRevBnEnd, sizeof(sStructRevXmlRevBnEnd)-1) ) { - /* empty bonds section */ - res = 0; - bItemIsOver = 1; - } - /* read all bonds (xml), starting from atom 1 (not 0) */ - while ( i < num_atoms ) { - pos = p - szLine; - if ( !bItemIsOver && (int)sizeof(szLine)-res + pos > (int)sizeof(szNextLine) ) { - /* load next line if possible */ - res2 = inchi_ios_gets( szNextLine, sizeof(szNextLine)-1, inp_molfile, &bTooLongLine2 ); - if ( res2 > 0 && memcmp(szNextLine, sStructRevXmlRevBnEnd, sizeof(sStructRevXmlRevBnEnd)-1) ) { - if ( pos ) { - res -= pos; /* number of chars left to process in szLine */ - memmove( szLine, p, res*sizeof(szLine[0]) ); /* move them to the start of the line */ - } - memcpy( szLine+res, szNextLine, (res2+1)*sizeof(szNextLine[0]) ); - res += res2; - szLine[res] = '\0'; - bTooLongLine = bTooLongLine2; - p = szLine; - } else { - bItemIsOver = 1; - } - } - if ( i >= num_atoms ) { - break; - } - /* bond, first char */ - if ( *p == ';' ) { - p ++; - i ++; - continue; - } - if ( !isalpha( UCINT *p ) ) { - num_atoms = INCHI_INP_ERROR_RET; /* error in input data */ - *err = INCHI_INP_ERROR_ERR; - MOLFILE_ERR_SET (*err, 0, "Wrong bonds data"); - goto bypass_end_of_INChI; - } - bond_char = *p ++; - /* bond parity */ - switch( *p ) { - case '-': - bond_parity = INCHI_PARITY_ODD; - p ++; - break; - case '+': - bond_parity = INCHI_PARITY_EVEN; - p ++; - break; - case 'u': - bond_parity = INCHI_PARITY_UNKNOWN; - p ++; - break; - case '?': - bond_parity = INCHI_PARITY_UNDEFINED; - p ++; - break; - default: - bond_parity = 0; - break; - } - if ( bond_parity ) { - switch( *p ) { - case '-': - bond_parityNM = INCHI_PARITY_ODD; - p ++; - break; - case '+': - bond_parityNM = INCHI_PARITY_EVEN; - p ++; - break; - case 'u': - bond_parityNM = INCHI_PARITY_UNKNOWN; - p ++; - break; - case '?': - bond_parityNM = INCHI_PARITY_UNDEFINED; - p ++; - break; - default: - bond_parityNM = 0; - break; - } - } else { - bond_parityNM = 0; - } - - /* neighbor of the current atom */ - if ( !isdigit( UCINT *p ) ) { - num_atoms = INCHI_INP_ERROR_RET; /* error in input data */ - *err = INCHI_INP_ERROR_ERR; - MOLFILE_ERR_SET (*err, 0, "Wrong bonds data"); - goto bypass_end_of_INChI; - } - neigh = (int)strtol( p, &q, 10 )-1; - - if ( i >= num_atoms || neigh >= num_atoms ) { - num_atoms = INCHI_INP_ERROR_RET; /* error in input data */ - *err = INCHI_INP_ERROR_ERR; - MOLFILE_ERR_SET (*err, 0, "Bond to nonexistent atom"); - goto bypass_end_of_INChI; - } - p = q; - bond_stereo1 = bond_stereo2 = 0; - - /* bond type & 2D stereo */ - switch( bond_char ) { - case 'v': - bond_type = INCHI_BOND_TYPE_SINGLE; - bond_stereo1 = INCHI_BOND_STEREO_SINGLE_1EITHER; - bond_stereo2 = INCHI_BOND_STEREO_SINGLE_2EITHER; - break; - case 'V': - bond_type = INCHI_BOND_TYPE_SINGLE; - bond_stereo1 = INCHI_BOND_STEREO_SINGLE_2EITHER; - bond_stereo2 = INCHI_BOND_STEREO_SINGLE_1EITHER; - break; - case 'w': - bond_type = INCHI_BOND_TYPE_DOUBLE; - bond_stereo1 = - bond_stereo2 = INCHI_BOND_STEREO_DOUBLE_EITHER; - break; - case 's': - bond_type = INCHI_BOND_TYPE_SINGLE; - break; - case 'd': - bond_type = INCHI_BOND_TYPE_DOUBLE; - break; - case 't': - bond_type = INCHI_BOND_TYPE_TRIPLE; - break; - case 'a': - bond_type = INCHI_BOND_TYPE_ALTERN; - break; - case 'p': - bond_type = INCHI_BOND_TYPE_SINGLE; - bond_stereo1 = INCHI_BOND_STEREO_SINGLE_1UP; - bond_stereo2 = INCHI_BOND_STEREO_SINGLE_2UP; - break; - case 'P': - bond_type = INCHI_BOND_TYPE_SINGLE; - bond_stereo1 = INCHI_BOND_STEREO_SINGLE_2UP; - bond_stereo2 = INCHI_BOND_STEREO_SINGLE_1UP; - break; - case 'n': - bond_type = INCHI_BOND_TYPE_SINGLE; - bond_stereo1 = INCHI_BOND_STEREO_SINGLE_1DOWN; - bond_stereo2 = INCHI_BOND_STEREO_SINGLE_2DOWN; - break; - case 'N': - bond_type = INCHI_BOND_TYPE_SINGLE; - bond_stereo1 = INCHI_BOND_STEREO_SINGLE_2DOWN; - bond_stereo2 = INCHI_BOND_STEREO_SINGLE_1DOWN; - break; - default: - num_atoms = INCHI_INP_ERROR_RET; /* error */ - *err = INCHI_INP_ERROR_ERR; - MOLFILE_ERR_SET (*err, 0, "Wrong bond type"); - goto bypass_end_of_INChI; - } - k = AT_NUM_BONDS(atom[i]) ++; - atom[i].bond_type[k] = bond_type; - atom[i].bond_stereo[k] = bond_stereo1; - atom[i].neighbor[k] = (ATOM_NUMBER)neigh; - k2 = AT_NUM_BONDS(atom[neigh]) ++; - atom[neigh].bond_type[k2] = bond_type; - atom[neigh].bond_stereo[k2] = bond_stereo2; - atom[neigh].neighbor[k2] = (ATOM_NUMBER)i; - bond_parity |= (bond_parityNM << SB_PARITY_SHFT); - - if ( bond_parity ) { - if ( max_len_stereo0D <= len_stereo0D ) { - /* realloc atom_Stereo0D */ - inchi_Stereo0D *new_atom_stereo0D = CreateInchi_Stereo0D( max_len_stereo0D+num_atoms ); - if ( !new_atom_stereo0D ) { - num_atoms = INCHI_INP_FATAL_RET; /* fatal error: cannot allocate */ - *err = INCHI_INP_FATAL_ERR; - MOLFILE_ERR_SET (*err, 0, "Out of RAM"); - goto bypass_end_of_INChI; - } - memcpy( new_atom_stereo0D, atom_stereo0D, len_stereo0D * sizeof(*atom_stereo0D) ); - FreeInchi_Stereo0D( &atom_stereo0D ); - atom_stereo0D = new_atom_stereo0D; - max_len_stereo0D += num_atoms; - } - /* (a) i may be allene endpoint and neigh = allene middle point or - (b) i may be allene middle point and neigh = allene endpoint - !!!!! CURRENTLY ONLY (b) IS ALLOWED !!!!! - */ - atom_stereo0D[len_stereo0D].neighbor[1] = neigh; /* neigh < i */ - atom_stereo0D[len_stereo0D].neighbor[2] = i; - atom_stereo0D[len_stereo0D].parity = bond_parity; - atom_stereo0D[len_stereo0D].type = INCHI_StereoType_DoubleBond; /* incl allenes & cumulenes */ - len_stereo0D ++; - } - } - if ( !bItemIsOver || p - szLine != res || i != num_atoms ) { - num_atoms = INCHI_INP_ERROR_RET; /* error in input data */ - *err = INCHI_INP_ERROR_ERR; - MOLFILE_ERR_SET (*err, 0, "Wrong number of bonds"); - goto bypass_end_of_INChI; - } - /********************** coordinates xml ****************************/ - if ( pszCoord = (MOL_COORD*) inchi_malloc(inchi_max(num_atoms,1) * sizeof(MOL_COORD)) ) { - memset( pszCoord, ' ', inchi_max(num_atoms,1) * sizeof(MOL_COORD)); - res = inchi_ios_gets( szLine, sizeof(szLine)-1, inp_molfile, &bTooLongLine ); - if ( res <= 0 || - /* compare the header */ - memcmp(szLine, sStructRevXmlRevXYZ, sizeof(sStructRevXmlRevXYZ)-1) || - /* read (the head of) the coordinates (xml) line */ - 0 >= (res = inchi_ios_gets( szLine, sizeof(szLine)-1, inp_molfile, &bTooLongLine ))) { - num_atoms = INCHI_INP_ERROR_RET; /* error in input data: atoms, bonds & coord must be present together */ - *err = INCHI_INP_ERROR_ERR; - MOLFILE_ERR_SET (*err, 0, "Missing atom coordinates data"); - goto bypass_end_of_INChI; - } - i = 0; - bItemIsOver = 0; - res2 = bTooLongLine2 = -1; - p = szLine; - if ( !memcmp(szLine, sStructRevXmlRevXYZEnd, sizeof(sStructRevXmlRevXYZEnd)-1) ) { - /* empty bonds section */ - res = 0; - bItemIsOver = 1; - } - /* read all coordinates (xml), starting from atom 1 (not 0) */ - while ( i < num_atoms ) { - pos = p - szLine; - if ( !bItemIsOver && (int)sizeof(szLine)-res + pos > (int)sizeof(szNextLine) ) { - /* load next line if possible */ - res2 = inchi_ios_gets( szNextLine, sizeof(szNextLine)-1, inp_molfile, &bTooLongLine2 ); - if ( res2 > 0 && memcmp(szNextLine, sStructRevXmlRevXYZEnd, sizeof(sStructRevXmlRevXYZEnd)-1) ) { - if ( pos ) { - res -= pos; /* number of chars left to process in szLine */ - memmove( szLine, p, res*sizeof(szLine[0]) ); /* move them to the start of the line */ - } - memcpy( szLine+res, szNextLine, (res2+1)*sizeof(szNextLine[0]) ); - res += res2; - szLine[res] = '\0'; - bTooLongLine = bTooLongLine2; - p = szLine; - } else { - bItemIsOver = 1; - } - } - /* coord, first char */ - if ( *p == ';' ) { - for ( k = 0; k < NUM_COORD; k ++ ) { - pszCoord[i][LEN_COORD*k + 4] = '0'; - } - p ++; - i ++; - continue; - } - for ( k = 0; k < 3; k ++ ) { - double xyz; - bNonZeroXYZ = 0; - if ( *p == ';' ) { - pszCoord[i][LEN_COORD*k + 4] = '0'; - xyz = 0.0; - } else - if ( *p == ',' ) { - /* empty */ - pszCoord[i][LEN_COORD*k + 4] = '0'; - xyz = 0.0; - p ++; - } else { - xyz = strtod( p, &q ); - bNonZeroXYZ = fabs(xyz) > MIN_BOND_LENGTH; - if ( q != NULL ) { - memcpy( pszCoord[i]+LEN_COORD*k, p, q-p ); - if ( *q == ',' ) - q ++; - p = q; - } else { - pszCoord[i][LEN_COORD*k + 4] = '0'; - } - } - switch( k ) { - case 0: - atom[i].x = xyz; - b2D |= bNonZeroXYZ; - break; - case 1: - atom[i].y = xyz; - b2D |= bNonZeroXYZ; - break; - case 2: - b3D |= bNonZeroXYZ; - atom[i].z = xyz; - break; - } - } - if ( *p == ';' ) { - p ++; /* end of this triple of coordinates */ - i ++; - } else { - num_atoms = INCHI_INP_ERROR_RET; /* error in input data: atoms, bonds & coord must be present together */ - *err = INCHI_INP_ERROR_ERR; - MOLFILE_ERR_SET (*err, 0, "Wrong atom coordinates data"); - goto bypass_end_of_INChI; - } - } - if ( !bItemIsOver || p - szLine != res || i != num_atoms ) { - num_atoms = INCHI_INP_ERROR_RET; /* error in input data: atoms, bonds & coord must be present together */ - *err = INCHI_INP_ERROR_ERR; - MOLFILE_ERR_SET (*err, 0, "Wrong number of coordinates"); - goto bypass_end_of_INChI; - } - } else { /* allocation failed */ - num_atoms = INCHI_INP_FATAL_RET; - *err = INCHI_INP_FATAL_ERR; - MOLFILE_ERR_SET (*err, 0, "Out of RAM"); - goto bypass_end_of_INChI; - } - /* set special valences and implicit H (xml) */ - b23D = b2D | b3D; - b2D = b3D = 0; - if ( at ) { - if ( !*at ) { - int a1, a2, n1, n2, valence; - int chem_bonds_valence; - int nX=0, nY=0, nZ=0, nXYZ; - *at = atom; - /* special valences */ - for ( bNonMetal = 0; bNonMetal < 1 /*2*/; bNonMetal ++ ) { - for ( a1 = 0; a1 < num_atoms; a1 ++ ) { - int num_bond_type[MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE + 1]; -#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) -#else - int bHasMetalNeighbor=0; -#endif - memset( num_bond_type, 0, sizeof(num_bond_type) ); - - valence = AT_BONDS_VAL(atom, a1); /* save atom valence if available */ - AT_BONDS_VAL(atom, a1) = 0; -#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) -#else - atom[a1].orig_at_number = a1+1; -#endif - nX = nY = nZ = 0; - for ( n1 = 0; n1 < AT_NUM_BONDS(atom[a1]); n1 ++ ) { - bond_type = atom[a1].bond_type[n1] - MIN_INPUT_BOND_TYPE; - if ( bond_type < 0 || bond_type > MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE ) { - bond_type = 0; /* cannot happen */ - MOLFILE_ERR_SET (*err, 0, "Unknown bond type in InChI aux assigned as a single bond"); - } - - num_bond_type[ bond_type ] ++; - nNumBonds ++; - if ( b23D ) { - neigh = atom[a1].neighbor[n1]; - nX |= (fabs(atom[a1].x - atom[neigh].x) > MIN_BOND_LENGTH); - nY |= (fabs(atom[a1].y - atom[neigh].y) > MIN_BOND_LENGTH); - nZ |= (fabs(atom[a1].z - atom[neigh].z) > MIN_BOND_LENGTH); - } - } - chem_bonds_valence = 0; - for ( n1 = 0; MIN_INPUT_BOND_TYPE + n1 <= 3 && MIN_INPUT_BOND_TYPE + n1 <= MAX_INPUT_BOND_TYPE; n1 ++ ) { - chem_bonds_valence += (MIN_INPUT_BOND_TYPE + n1) * num_bond_type[n1]; - } - if ( MIN_INPUT_BOND_TYPE <= INCHI_BOND_TYPE_ALTERN && INCHI_BOND_TYPE_ALTERN <= MAX_INPUT_BOND_TYPE && - ( n2 = num_bond_type[INCHI_BOND_TYPE_ALTERN-MIN_INPUT_BOND_TYPE] ) ) { - /* accept input aromatic bonds for now */ - switch ( n2 ) { - case 2: - chem_bonds_valence += 3; /* =A- */ - break; - case 3: - chem_bonds_valence += 4; /* =A< */ - break; - default: - /* if 1 or >= 4 aromatic bonds then replace such bonds with single bonds */ - for ( n1 = 0; n1 < AT_NUM_BONDS(atom[a1]); n1 ++ ) { - if ( atom[a1].bond_type[n1] == INCHI_BOND_TYPE_ALTERN ) { - ATOM_NUMBER *p1; - a2 = atom[a1].neighbor[n1]; - p1 = IN_NEIGH_LIST( atom[a2].neighbor, (ATOM_NUMBER)a1, AT_NUM_BONDS(atom[a2]) ); - if ( p1 ) { - atom[a1].bond_type[n1] = - atom[a2].bond_type[p1-atom[a2].neighbor] = INCHI_BOND_TYPE_SINGLE; - } else { - *err = -2; /* Program error */ - MOLFILE_ERR_SET (*err, 0, "Program error interpreting InChI aux"); - num_atoms = 0; - goto bypass_end_of_INChI; /* no structure */ - } - } - } - chem_bonds_valence += n2; - *err |= 32; /* Unrecognized aromatic bond(s) replaced with single */ - MOLFILE_ERR_SET (*err, 0, "Atom has 1 or more than 3 aromatic bonds"); - break; - } - } - -#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) - /************************************************************************************* - * - * Set number of hydrogen atoms - */ - { - int num_iso_H; - num_iso_H = atom[a1].num_iso_H[1] + atom[a1].num_iso_H[2] + atom[a1].num_iso_H[3]; - if ( valence == ISOLATED_ATOM ) { - atom[a1].num_iso_H[0] = 0; - } else - if ( valence && valence >= chem_bonds_valence ) { - atom[a1].num_iso_H[0] = valence - chem_bonds_valence; - } else - if ( valence || bDoNotAddH ) { - atom[a1].num_iso_H[0] = 0; - } else - if ( !bDoNotAddH ) { - atom[a1].num_iso_H[0] = -1; /* auto add H */ - } - } -#else - /* added 2006-07-19 to process aromatic bonds same way as from molfile */ - if ( n2 && !valence ) { - int num_H = NUMH(atom, a1); /* only isotopic */ - int chem_valence = chem_bonds_valence; - int bUnusualValenceArom = - detect_unusual_el_valence( (int)atom[a1].el_number, atom[a1].charge, - atom[a1].radical, chem_valence, - num_H, atom[a1].valence ); - int bUnusualValenceNoArom = - detect_unusual_el_valence( (int)atom[a1].el_number, atom[a1].charge, - atom[a1].radical, chem_valence-1, - num_H, atom[a1].valence ); -#if ( CHECK_AROMBOND2ALT == 1 ) - if ( bUnusualValenceArom && !bUnusualValenceNoArom && 0 == nBondsValToMetal( atom, a1) ) -#else - if ( bUnusualValenceArom && !bUnusualValenceNoArom ) -#endif - { - /* typically NH in 5-member aromatic ring */ - chem_bonds_valence --; - } - } else - if ( n2 && valence ) { - /* atom has aromatic bonds AND the chemical valence is known */ - int num_H = NUMH(atom, a1); - int chem_valence = chem_bonds_valence + num_H; - if ( valence == chem_valence-1 ) { - /* typically NH in 5-member aromatic ring */ - chem_bonds_valence --; - } - } - - atom[a1].chem_bonds_valence = chem_bonds_valence; - atom[a1].num_H = get_num_H( atom[a1].elname, atom[a1].num_H, atom[a1].num_iso_H, atom[a1].charge, atom[a1].radical, - atom[a1].chem_bonds_valence, - valence, - 0, bDoNotAddH, bHasMetalNeighbor ); -#endif - } - } - nNumBonds /= 2; - if ( b23D && nNumBonds ) { - nXYZ = nX+nY+nZ; - b2D = (nXYZ > 0); - b3D = (nXYZ == 3); - *num_dimensions = b3D? 3 : b2D? 2 : 0; - *num_bonds = nNumBonds; - } - /*======= 0D parities =================================*/ -#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) - if ( len_stereo0D > 0 && atom_stereo0D && stereo0D ) { - *stereo0D = atom_stereo0D; - *num_stereo0D = len_stereo0D; - } else { - FreeInchi_Stereo0D( &atom_stereo0D ); - *num_stereo0D = len_stereo0D = 0; - } -#endif - for ( i = 0; i < len_stereo0D; i ++ ) { - ATOM_NUMBER *p1, *p2; - int sb_ord_from_a1 = -1, sb_ord_from_a2 = -1, bEnd1 = 0, bEnd2 = 0; - switch( atom_stereo0D[i].type ) { - - case INCHI_StereoType_Tetrahedral: - a1 = atom_stereo0D[i].central_atom; - if ( atom_stereo0D[i].parity && (AT_NUM_BONDS(atom[a1]) == 3 || AT_NUM_BONDS(atom[a1]) == 4) ) { - int ii, kk = 0; - if ( AT_NUM_BONDS(atom[a1]) == 3 ) { - atom_stereo0D[i].neighbor[kk++] = a1; - } - for ( ii = 0; ii < AT_NUM_BONDS(atom[a1]); ii ++ ) { - atom_stereo0D[i].neighbor[kk++] = atom[a1].neighbor[ii]; - } - } - - break; - - case INCHI_StereoType_DoubleBond: -#define MAX_CHAIN_LEN 20 - a1 = atom_stereo0D[i].neighbor[1]; - a2 = atom_stereo0D[i].neighbor[2]; - p1 = IN_NEIGH_LIST( atom[a1].neighbor, (ATOM_NUMBER)a2, AT_NUM_BONDS(atom[a1]) ); - p2 = IN_NEIGH_LIST( atom[a2].neighbor, (ATOM_NUMBER)a1, AT_NUM_BONDS(atom[a2]) ); - if ( !p1 || !p2 ) { - atom_stereo0D[i].type = INCHI_StereoType_None; - atom_stereo0D[i].central_atom = NO_ATOM; - atom_stereo0D[i].neighbor[0] = - atom_stereo0D[i].neighbor[3] = -1; - *err |= 64; /* Error in cumulene stereo */ - MOLFILE_ERR_SET (*err, 0, "0D stereobond not recognized"); - break; - } - /* streobond, allene, or cumulene */ - - sb_ord_from_a1 = p1 - atom[a1].neighbor; - sb_ord_from_a2 = p2 - atom[a2].neighbor; - - if ( AT_NUM_BONDS(atom[a1]) == 2 && - atom[a1].bond_type[0] + atom[a1].bond_type[1] == 2*INCHI_BOND_TYPE_DOUBLE && - 0 == inchi_NUMH2(atom, a1) && - (AT_NUM_BONDS(atom[a2]) != 2 || - atom[a2].bond_type[0] + atom[a2].bond_type[1] != 2*INCHI_BOND_TYPE_DOUBLE ) ) { - bEnd2 = 1; /* a2 is the end-atom, a1 is middle atom */ - } - if ( AT_NUM_BONDS(atom[a2]) == 2 && - atom[a2].bond_type[0] + atom[a2].bond_type[1] == 2*INCHI_BOND_TYPE_DOUBLE && - 0 == inchi_NUMH2(atom, a2) && - (AT_NUM_BONDS(atom[a1]) != 2 || - atom[a1].bond_type[0] + atom[a1].bond_type[1] != 2*INCHI_BOND_TYPE_DOUBLE ) ) { - bEnd1 = 1; /* a1 is the end-atom, a2 is middle atom */ - } - - if ( bEnd2 + bEnd1 == 1 ) { - /* allene or cumulene */ - ATOM_NUMBER chain[MAX_CHAIN_LEN+1], prev, cur, next; - if ( bEnd2 && !bEnd1 ) { - cur = a1; - a1 = a2; - a2 = cur; - sb_ord_from_a1 = sb_ord_from_a2; - } - sb_ord_from_a2 = -1; - cur = a1; - next = a2; - len = 0; - chain[len++] = cur; - chain[len++] = next; - while ( len < MAX_CHAIN_LEN ) { /* arbitrary very high upper limit to prevent infinite loop */ - prev = cur; - cur = next; - /* follow double bond path && avoid going back */ - if ( AT_NUM_BONDS(atom[cur]) == 2 && - atom[cur].bond_type[0]+atom[cur].bond_type[1] == 2*INCHI_BOND_TYPE_DOUBLE && - 0 == inchi_NUMH2(atom, cur) ) { - next = atom[cur].neighbor[atom[cur].neighbor[0] == prev]; - chain[len++] = next; - } else { - break; - } - } - if ( len > 2 && - (p2 = IN_NEIGH_LIST( atom[cur].neighbor, (ATOM_NUMBER)prev, AT_NUM_BONDS(atom[cur]))) ) { - sb_ord_from_a2 = p2 - atom[cur].neighbor; - a2 = cur; - /* by design we need to pick up the first non-stereo-bond-neighbor as "sn"-atom */ - atom_stereo0D[i].neighbor[0] = atom[a1].neighbor[sb_ord_from_a1 == 0]; - atom_stereo0D[i].neighbor[1] = a1; - atom_stereo0D[i].neighbor[2] = a2; - atom_stereo0D[i].neighbor[3] = atom[a2].neighbor[sb_ord_from_a2 == 0]; - if ( len % 2 ) { - atom_stereo0D[i].central_atom = chain[len/2]; - atom_stereo0D[i].type = INCHI_StereoType_Allene; - } else { - atom_stereo0D[i].central_atom = NO_ATOM; - } - } else { - /* error */ - atom_stereo0D[i].type = INCHI_StereoType_None; - atom_stereo0D[i].central_atom = NO_ATOM; - atom_stereo0D[i].neighbor[0] = - atom_stereo0D[i].neighbor[3] = -1; - *err |= 64; /* Error in cumulene stereo */ - MOLFILE_ERR_SET (*err, 0, "Cumulene stereo not recognized (0D)"); - - } -#undef MAX_CHAIN_LEN - } else { - /****** a normal possibly stereogenic bond -- not an allene or cumulene *******/ - /* by design we need to pick up the first non-stereo-bond-neighbor as "sn"-atom */ - sb_ord_from_a1 = p1 - atom[a1].neighbor; - sb_ord_from_a2 = p2 - atom[a2].neighbor; - atom_stereo0D[i].neighbor[0] = atom[a1].neighbor[p1 == atom[a1].neighbor]; - atom_stereo0D[i].neighbor[3] = atom[a2].neighbor[p2 == atom[a2].neighbor]; - atom_stereo0D[i].central_atom = NO_ATOM; - } - if ( atom_stereo0D[i].type != INCHI_StereoType_None && - sb_ord_from_a1 >= 0 && sb_ord_from_a2 >= 0 && - ATOM_PARITY_WELL_DEF( SB_PARITY_2(atom_stereo0D[i].parity) ) ) { - /* Detected well-defined disconnected stereo - * locate first non-metal neighbors */ - int a, n, j, /* k,*/ sb_ord, cur_neigh, min_neigh; - for ( k = 0; k < 2; k ++ ) { - a = k? atom_stereo0D[i].neighbor[2] : atom_stereo0D[i].neighbor[1]; - sb_ord = k? sb_ord_from_a2 : sb_ord_from_a1; - min_neigh = num_atoms; - for ( n = j = 0; j < AT_NUM_BONDS(atom[a]); j ++ ) { - cur_neigh = atom[a].neighbor[j]; - if ( j != sb_ord && !IS_METAL_ATOM(atom, cur_neigh) ) { - min_neigh = inchi_min( cur_neigh, min_neigh ); - } - } - if ( min_neigh < num_atoms ) { - atom_stereo0D[i].neighbor[k?3:0] = min_neigh; - } else { - MOLFILE_ERR_SET (*err, 0, "Cannot find non-metal stereobond neighor (0D)"); - } - } - } - - break; - } - } - /* end of 0D parities extraction */ -/*exit_cycle:;*/ - } -#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) -#else - /* transfer atom_stereo0D[] to atom[] */ - if ( len_stereo0D ) { - Extract0DParities( atom, num_atoms, atom_stereo0D, len_stereo0D, - pStrErr, err, vABParityUnknown ); - } -#endif - if ( pInpAtomFlags ) { - /* save chirality flag */ - *pInpAtomFlags |= InpAtomFlags; - } - } else - if ( atom ) { - inchi_free( atom ); - atom = NULL; - } -#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) -#else - if ( szCoord ) { - *szCoord = pszCoord; - pszCoord = NULL; - } else -#endif - if ( pszCoord ) { - inchi_free( pszCoord ); - } - goto bypass_end_of_INChI; - /*return num_atoms;*/ - } - } - if ( atom_stereo0D ) { - FreeInchi_Stereo0D( &atom_stereo0D ); - } - /* end of struct. reading cycle, code never used? */ - if ( res <= 0 ) { - if ( *err == INCHI_INP_ERROR_ERR ) { - return num_atoms; - } - *err = INCHI_INP_EOF_ERR; - return INCHI_INP_EOF_RET; /* no more data */ - } -bypass_end_of_INChI: - /* cleanup */ - if ( num_atoms == INCHI_INP_ERROR_RET && atom_stereo0D ) { - if ( stereo0D && *stereo0D == atom_stereo0D ) { - *stereo0D = NULL; - *num_stereo0D = 0; - } - FreeInchi_Stereo0D( &atom_stereo0D ); - } - if ( !memcmp(szLine, sStructHdrXmlEnd, sizeof(sStructHdrXmlEnd)-1) ) - num_struct --; - if ( !memcmp(szLine, sStructHdrXml, sizeof(sStructHdrXml)-1) ) - num_struct ++; - - while ( num_struct > 0 && 0 < inchi_ios_gets( szLine, sizeof(szLine)-1, inp_molfile, &bTooLongLine ) ) { - if ( !memcmp(szLine, sStructHdrXmlEnd, sizeof(sStructHdrXmlEnd)-1) ) - num_struct --; - else - if ( !memcmp(szLine, sStructHdrXml, sizeof(sStructHdrXml)-1) ) - num_struct ++; - } - return num_atoms; - - } - - return num_atoms; - -#undef AT_NUM_BONDS -#undef ATOM_NUMBER -#undef IN_NEIGH_LIST -#undef inchi_NUMH2 - -#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) -#else -#undef inchi_Atom -#endif - -#undef AT_NUM_BONDS -#undef ATOM_NUMBER -#undef IN_NEIGH_LIST -#undef inchi_NUMH2 -#undef INChITo_Atom -#undef MoreParms -#undef INPUT_FILE -#undef Create_Atom -#undef AT_BONDS_VAL -#undef ISOLATED_ATOM -#undef NUM_ISO_Hk -#undef IS_METAL_ATOM - - -} -#ifdef TARGET_EXE_USING_API - -/**********************************************************************************/ -int INChIToInchi_Input( INCHI_IOSTREAM *inp_molfile, inchi_Input *orig_at_data, int bMergeAllInputStructures, - int bDoNotAddH, int vABParityUnknown, INPUT_TYPE nInputType, - char *pSdfLabel, char *pSdfValue, long *lSdfId, INCHI_MODE *pInpAtomFlags, - int *err, char *pStrErr ) -{ - /* inp_ATOM *at = NULL; */ - int num_dimensions_new; - int num_inp_bonds_new; - int num_inp_atoms_new; - int num_inp_0D_new; - inchi_Atom *at_new = NULL; - inchi_Atom *at_old = NULL; - inchi_Stereo0D *stereo0D_new = NULL; - inchi_Stereo0D *stereo0D_old = NULL; - int nNumAtoms = 0, nNumStereo0D = 0; - MOL_COORD *szCoordNew = NULL; - MOL_COORD *szCoordOld = NULL; - int i, j; - - if ( pStrErr ) { - pStrErr[0] = '\0'; - } - - /*FreeOrigAtData( orig_at_data );*/ - if ( lSdfId ) - *lSdfId = 0; - do { - - at_old = orig_at_data? orig_at_data->atom : NULL; /* save pointer to the previous allocation */ - stereo0D_old = orig_at_data? orig_at_data->stereo0D : NULL; - szCoordOld = NULL; - num_inp_atoms_new = - INChIToInchi_Atom( inp_molfile, orig_at_data? &stereo0D_new:NULL, &num_inp_0D_new, - bDoNotAddH, vABParityUnknown, nInputType, - orig_at_data? &at_new:NULL, MAX_ATOMS, - &num_dimensions_new, &num_inp_bonds_new, - pSdfLabel, pSdfValue, lSdfId, pInpAtomFlags, err, pStrErr ); - if ( num_inp_atoms_new <= 0 && !*err ) { - MOLFILE_ERR_SET (*err, 0, "Empty structure"); - *err = 98; - } else - if ( orig_at_data && !num_inp_atoms_new && 10 < *err && *err < 20 && orig_at_data->num_atoms > 0 && bMergeAllInputStructures ) { - *err = 0; /* end of file */ - break; - } else - if ( num_inp_atoms_new > 0 && orig_at_data ) { - /* merge pOrigDataTmp + orig_at_data => pOrigDataTmp; */ - nNumAtoms = num_inp_atoms_new + orig_at_data->num_atoms; - nNumStereo0D = num_inp_0D_new + orig_at_data->num_stereo0D; - if ( nNumAtoms >= MAX_ATOMS ) { - MOLFILE_ERR_SET (*err, 0, "Too many atoms"); - *err = 70; - orig_at_data->num_atoms = -1; - } else - if ( !at_old ) { - /* the first structure */ - orig_at_data->atom = at_new; at_new = NULL; - orig_at_data->num_atoms = num_inp_atoms_new; num_inp_atoms_new = 0; - orig_at_data->stereo0D = stereo0D_new; stereo0D_new = NULL; - orig_at_data->num_stereo0D = num_inp_0D_new; num_inp_0D_new = 0; - } else - if ( orig_at_data->atom = CreateInchi_Atom( nNumAtoms ) ) { - /* switch at_new <--> orig_at_data->at; */ - if ( orig_at_data->num_atoms ) { - memcpy( orig_at_data->atom, at_old, orig_at_data->num_atoms * sizeof(orig_at_data->atom[0]) ); - /* adjust numbering in the newly read structure */ - for ( i = 0; i < num_inp_atoms_new; i ++ ) { - for ( j = 0; j < at_new[i].num_bonds; j ++ ) { - at_new[i].neighbor[j] += orig_at_data->num_atoms; - } - } - } - FreeInchi_Atom( &at_old ); - /* copy newly read structure */ - memcpy( orig_at_data->atom + orig_at_data->num_atoms, - at_new, - num_inp_atoms_new * sizeof(orig_at_data->atom[0]) ); - /* cpy newly read 0D stereo */ - if ( num_inp_0D_new > 0 && stereo0D_new ) { - if ( orig_at_data->stereo0D = CreateInchi_Stereo0D( nNumStereo0D ) ) { - memcpy( orig_at_data->stereo0D, stereo0D_old, orig_at_data->num_stereo0D * sizeof(orig_at_data->stereo0D[0]) ); - /* adjust numbering in the newly read structure */ - for ( i = 0; i < num_inp_0D_new; i ++ ) { - if ( stereo0D_new[i].central_atom >= 0 ) { - stereo0D_new[i].central_atom += orig_at_data->num_atoms; - } - for ( j = 0; j < 4; j ++ ) { - stereo0D_new[i].neighbor[j] += orig_at_data->num_atoms; - } - } - FreeInchi_Stereo0D( &stereo0D_old ); - memcpy( orig_at_data->stereo0D+orig_at_data->num_stereo0D, - stereo0D_new, - num_inp_0D_new * sizeof(orig_at_data->stereo0D[0]) ); - } else { - num_inp_0D_new = 0; - MOLFILE_ERR_SET (*err, 0, "Out of RAM"); - *err = -1; - } - } else { - num_inp_0D_new = 0; - } - /* update lengths */ - orig_at_data->num_atoms += num_inp_atoms_new; - orig_at_data->num_stereo0D += num_inp_0D_new; - } else { - MOLFILE_ERR_SET (*err, 0, "Out of RAM"); - *err = -1; - } - } else - if ( num_inp_atoms_new > 0 ) { - nNumAtoms += num_inp_atoms_new; - } - FreeInchi_Atom( &at_new ); - num_inp_atoms_new = 0; - FreeInchi_Stereo0D( &stereo0D_new ); - num_inp_0D_new = 0; - - } while ( !*err && bMergeAllInputStructures ); - /* - if ( !*err ) { - orig_at_data->num_components = - MarkDisconnectedComponents( orig_at_data ); - if ( orig_at_data->num_components == 0 ) { - MOLFILE_ERR_SET (*err, 0, "No components found"); - *err = 99; - } - if ( orig_at_data->num_components < 0 ) { - MOLFILE_ERR_SET (*err, 0, "Too many components"); - *err = 99; - } - } - */ - if ( szCoordNew ) { - inchi_free( szCoordNew ); - } - if ( at_new ) { - inchi_free( at_new ); - } - /* - if ( !*err ) { - if ( ReconcileAllCmlBondParities( orig_at_data->atom, orig_at_data->num_atoms ) ) { - MOLFILE_ERR_SET (*err, 0, "Cannot reconcile stereobond parities"); - if (!orig_at_data->num_atoms) { - *err = 1; - } - } - } - */ - if ( *err ) { - FreeInchi_Input( orig_at_data ); - } - if ( *err && !(10 < *err && *err < 20) && pStrErr && !pStrErr[0] ) { - MOLFILE_ERR_SET (*err, 0, "Unknown error"); /* */ - } - return orig_at_data? orig_at_data->num_atoms : nNumAtoms; -} - -#endif - -#ifndef TARGET_EXE_USING_API -#undef AB_MAX_WELL_DEFINED_PARITY -#undef AB_MIN_WELL_DEFINED_PARITY -#include "extr_ct.h" -/****************************************************************************************/ -int Extract0DParities( inp_ATOM *at, int nNumAtoms, inchi_Stereo0D *stereo0D, - int num_stereo0D, char *pStrErr, int *err, - int vABParityUnknown) -{ - /* vABParityUnknown holds actual value of an internal constant signifying */ - /* unknown parity: either the same as for undefined parity (default==standard) */ - /* or a specific one (non-std; requested by SLUUD switch). */ - - if ( stereo0D && num_stereo0D > 0 ) { - int i0D, a2, k, k_prev, type, j, j1, j2, len, parity, parityNM; - int sb_ord_from_i1, sb_ord_from_i2, sn_ord_from_i1, sn_ord_from_i2; - AT_NUMB i1n, i2n, i1, i2; - for ( i0D = 0; i0D < num_stereo0D; i0D ++ ) { - parity = (stereo0D[i0D].parity & SB_PARITY_MASK); - parityNM = (stereo0D[i0D].parity & SB_PARITY_FLAG) >> SB_PARITY_SHFT; - if ( parity == INCHI_PARITY_NONE || - parity != INCHI_PARITY_ODD && parity != INCHI_PARITY_EVEN && - parity != INCHI_PARITY_UNKNOWN && parity != INCHI_PARITY_UNDEFINED ) { - char szTemp[16]; - sprintf( szTemp, "#%d", i0D+1 ); - MOLFILE_ERR_SET (*err, 0, "Wrong 0D stereo descriptor(s):"); - MOLFILE_ERR_SET (*err, 0, szTemp); - continue; /* warning */ - } - type = stereo0D[i0D].type; - a2 = stereo0D[i0D].central_atom; /* central atom or -1 */ - j = -1; - len = 0; - sb_ord_from_i1 = sb_ord_from_i2 = sn_ord_from_i1 = sn_ord_from_i2 = -1; - i1n = i2n = i1 = i2 = MAX_ATOMS+1; - - if ( (type == INCHI_StereoType_Tetrahedral || - type == INCHI_StereoType_Allene ) && - 0 <= a2 && a2 < nNumAtoms || - type == INCHI_StereoType_DoubleBond && - a2 == NO_ATOM) { - /* test the quadruplet */ - for ( j = 0, k_prev = -1; j < 4; j ++, k_prev = k ) { - k = stereo0D[i0D].neighbor[j]; - if ( k < 0 || k >= nNumAtoms || k_prev == k ) - break; - /* tetrahedral atom connectivity test */ - if ( type == INCHI_StereoType_Tetrahedral && - k != a2 && - !is_in_the_list( at[a2].neighbor, (AT_NUMB)k, at[a2].valence) ) { - break; - } - /* Double bond, Cumulene and allene are tested in the next if() */ - } - } - /* find in the adjacency lists the double bond neighbor that leads to the opposite atom */ - if ( j == 4 && (type == INCHI_StereoType_Allene || - type == INCHI_StereoType_DoubleBond) ) { - AT_NUMB *p1 = NULL, *p2 = NULL, *q1 = NULL, *q2 = NULL; - i1n = (AT_NUMB)stereo0D[i0D].neighbor[0]; - i1 = (AT_NUMB)stereo0D[i0D].neighbor[1]; - i2 = (AT_NUMB)stereo0D[i0D].neighbor[2]; - i2n = (AT_NUMB)stereo0D[i0D].neighbor[3]; - /* find q1 and q2 */ - if ( !(q1 = is_in_the_list( at[i1].neighbor, i1n, at[i1].valence)) || - !(q2 = is_in_the_list( at[i2].neighbor, i2n, at[i2].valence)) ) { - j = -2; /* error flag */ - } else - /* allene or cumulene; follow double bonds from i1 to i2 */ - if ( !(p1 = is_in_the_list( at[i1].neighbor, i2, at[i1].valence)) ) { - /* at[i1] and at[i2] are not connected: can be only allene or cumulene */ - AT_NUMB prev, cur, next; - int num_dbond, i, next_ord, half_len; - - cur = next = i1; - len = half_len = 0; - while ( len < 20 ) { /* arbitrary very high upper limit to prevent infinite loop */ - prev = cur; - cur = next; - for ( i = 0, num_dbond = 0; i < at[cur].valence; i ++ ) { - /* follow double bond path && avoid going back */ - if ( at[cur].bond_type[i] == BOND_TYPE_DOUBLE && - prev != at[cur].neighbor[i] ) { - next = at[cur].neighbor[i]; - next_ord = i; - num_dbond ++; - } - } - if ( num_dbond == 1 && next != i1 ) { - len ++; - if ( len == 1 ) { - sb_ord_from_i1 = next_ord; - } - if ( type == INCHI_StereoType_Allene && next == (AT_NUMB)a2 ) { - half_len = len; - } - } else { - break; - } - } - if ( cur == i2 && prev != cur && 0 == num_dbond && len > 1 && - (p2 = is_in_the_list( at[i2].neighbor, prev, at[i2].valence)) && - (type != INCHI_StereoType_Allene || len == 2*half_len )) { - sb_ord_from_i2 = p2 - at[i2].neighbor; - sn_ord_from_i1 = q1 - at[i1].neighbor; - sn_ord_from_i2 = q2 - at[i2].neighbor; - } else { - j = -5; /* error flag */ - } - } else - /* allene must have been already processed, otherwise error */ - if ( type == INCHI_StereoType_Allene ) { - /* error: atoms #1 and #2 of allene are connected */ - j = -3; /* error flag */ - } else - /* double bond only; the bond type is not checked because at the end - of the normalization it may happen to be alternating */ - if ( type == INCHI_StereoType_DoubleBond && - (p2 = is_in_the_list( at[i2].neighbor, i1, at[i2].valence) ) ) { - sb_ord_from_i1 = p1 - at[i1].neighbor; - sb_ord_from_i2 = p2 - at[i2].neighbor; - sn_ord_from_i1 = q1 - at[i1].neighbor; - sn_ord_from_i2 = q2 - at[i2].neighbor; - } else { - j = -4; /* error flag */ - } - } - if ( j != 4 ) { - char szTemp[16]; - sprintf( szTemp, "#%d", i0D+1 ); - MOLFILE_ERR_SET (*err, 0, "Wrong 0D stereo descriptor(s):"); - MOLFILE_ERR_SET (*err, 0, szTemp); - continue; /* error */ - } - - switch ( type ) { - case INCHI_StereoType_None: - continue; - case INCHI_StereoType_DoubleBond: - case INCHI_StereoType_Allene: - for ( j1 = 0; j1 < MAX_NUM_STEREO_BONDS && at[i1].sb_parity[j1]; j1 ++ ) - ; - for ( j2 = 0; j2 < MAX_NUM_STEREO_BONDS && at[i2].sb_parity[j2]; j2 ++ ) - ; - if ( j1 < MAX_NUM_STEREO_BONDS && j2 < MAX_NUM_STEREO_BONDS && - sb_ord_from_i1 >= 0 && sb_ord_from_i2 >= 0 && - sn_ord_from_i1 >= 0 && sn_ord_from_i2 >= 0) { - - switch( parity ) { - case INCHI_PARITY_ODD: - at[i1].sb_parity[j1] = AB_PARITY_ODD; - at[i2].sb_parity[j2] = AB_PARITY_EVEN; - break; - case INCHI_PARITY_EVEN: - at[i1].sb_parity[j1] = AB_PARITY_ODD; - at[i2].sb_parity[j2] = AB_PARITY_ODD; - break; - case INCHI_PARITY_UNDEFINED: - at[i1].sb_parity[j1] = AB_PARITY_UNDF; - at[i2].sb_parity[j2] = AB_PARITY_UNDF; - break; - default: - if ( parity == INCHI_PARITY_UNKNOWN ) - { - at[i1].sb_parity[j1] = vABParityUnknown; - at[i2].sb_parity[j2] = vABParityUnknown; - } - else - { - at[i1].sb_parity[j1] = AB_PARITY_NONE; - at[i2].sb_parity[j2] = AB_PARITY_NONE; - } - break; - } - - switch( parityNM ) { - case INCHI_PARITY_ODD: - at[i1].sb_parity[j1] |= AB_PARITY_ODD << SB_PARITY_SHFT; - at[i2].sb_parity[j2] |= AB_PARITY_EVEN << SB_PARITY_SHFT; - break; - case INCHI_PARITY_EVEN: - at[i1].sb_parity[j1] |= AB_PARITY_ODD << SB_PARITY_SHFT; - at[i2].sb_parity[j2] |= AB_PARITY_ODD << SB_PARITY_SHFT; - break; - case INCHI_PARITY_UNDEFINED: - at[i1].sb_parity[j1] |= AB_PARITY_UNDF << SB_PARITY_SHFT; - at[i2].sb_parity[j2] |= AB_PARITY_UNDF << SB_PARITY_SHFT; - break; - default: - if ( parityNM == INCHI_PARITY_UNKNOWN ) - { - at[i1].sb_parity[j1] |= vABParityUnknown << SB_PARITY_SHFT; - at[i2].sb_parity[j2] |= vABParityUnknown << SB_PARITY_SHFT; - } - break; - } - - at[i1].sb_ord[j1] = sb_ord_from_i1; - at[i1].sn_ord[j1] = sn_ord_from_i1; - at[i1].sn_orig_at_num[j1] = at[i1n].orig_at_number; - - at[i2].sb_ord[j2] = sb_ord_from_i2; - at[i2].sn_ord[j2] = sn_ord_from_i2; - at[i2].sn_orig_at_num[j2] = at[i2n].orig_at_number; - } - break; - case INCHI_StereoType_Tetrahedral: - switch( parity ) { - case INCHI_PARITY_ODD: - at[a2].p_parity = AB_PARITY_ODD; - break; - case INCHI_PARITY_EVEN: - at[a2].p_parity = AB_PARITY_EVEN; - break; - case INCHI_PARITY_UNDEFINED: - at[a2].p_parity = AB_PARITY_UNDF; - break; - default: - if (parity == INCHI_PARITY_UNKNOWN ) - { - at[a2].p_parity = vABParityUnknown; - break; - } - else - continue; - } - for ( j = 0; j < 4; j ++ ) { - k = stereo0D[i0D].neighbor[j]; - at[a2].p_orig_at_num[j] = at[k].orig_at_number; - } - break; - default: - break; - } - } - /* take care of Unknown stereobonds: */ - /* copy their Unknown stereo descriptors to at->bond_stereo (2005-03-01) */ - /* Note: to this stage, unk/undef set to what was requested */ - /*( through vABParityUnknown ) (2009-12-12) */ - FixUnkn0DStereoBonds(at, nNumAtoms); - -#ifdef TARGET_API_LIB - - if ( k = ReconcileAllCmlBondParities( at, nNumAtoms, 0 ) ) { - char szErrCode[16]; - sprintf( szErrCode, "%d", k); - AddMOLfileError( pStrErr, "0D Parities Reconciliation failed:" ); - AddMOLfileError( pStrErr, szErrCode ); - } - -#endif - - } - return 0; -} - -#endif - +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../../INCHI_BASE/src/mode.h" +#include "../../../INCHI_BASE/src/inchi_api.h" +#include "../../../INCHI_BASE/src/incomdef.h" +#include "../../../INCHI_BASE/src/ichidrp.h" +#include "../../../INCHI_BASE/src/inpdef.h" +#include "../../../INCHI_BASE/src/ichi.h" +#include "../../../INCHI_BASE/src/strutil.h" +#include "../../../INCHI_BASE/src/util.h" +#include "../../../INCHI_BASE/src/ichierr.h" +#include "../../../INCHI_BASE/src/ichimain.h" +#include "../../../INCHI_BASE/src/extr_ct.h" +#include "../../../INCHI_BASE/src/ichi_io.h" +#include "../../../INCHI_BASE/src/ichicomp.h" +#include "../../../INCHI_BASE/src/ichitime.h" +#include "../../../INCHI_BASE/src/ichicant.h" +#include "../../../INCHI_BASE/src/readinch.h" + +#include "inchi_dll.h" +#include "inchi_dll_b.h" + + +static +int PrepareToMakeINCHI( STRUCT_DATA *sd, + INPUT_PARMS *ip, + ORIG_ATOM_DATA *orig_inp_data, + ORIG_ATOM_DATA *prep_inp_data, + PINChI2 *pINChI[INCHI_NUM], + PINChI_Aux2 *pINChI_Aux[INCHI_NUM], + INCHI_IOSTREAM *pout, + INCHI_IOSTREAM *plog, + INCHI_IOSTREAM *pprb, + INCHI_IOSTREAM *inp_file, + const char *moltext, + char *options, + INCHI_IOSTREAM_STRING *strbuf ); +static +int PostMakeINCHICleanup( struct tagCANON_GLOBALS *pCG, + STRUCT_DATA *sd, + INPUT_PARMS *ip, + ORIG_ATOM_DATA *orig_inp_data, + ORIG_ATOM_DATA *prep_inp_data, + PINChI2 *pINChI[INCHI_NUM], + PINChI_Aux2 *pINChI_Aux[INCHI_NUM], + INCHI_IOSTREAM *pout, + INCHI_IOSTREAM *plog, + INCHI_IOSTREAM *pprb, + INCHI_IOSTREAM *inp_file, + const char *moltext, + INCHI_IOSTREAM_STRING *strbuf ); + + + +/****************************************************************************/ +void FreeInchi_Atom( inchi_Atom **at ) +{ + if ( at && *at ) + { + inchi_free( *at ); + *at = NULL; + } +} + + +/****************************************************************************/ +inchi_Atom *CreateInchiAtom( int num_atoms ) +{ + inchi_Atom *p = (inchi_Atom* ) inchi_calloc(num_atoms, sizeof(inchi_Atom) ); + return p; +} + + + + + +/*****************************************************************************/ +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL +MakeINCHIFromMolfileText( const char *moltext, + char *szOptions, + inchi_Output *result ) +{ +int retcode=0, retcode2=0; +long num_inp=0, num_err=0; +char szTitle[MAX_SDF_HEADER+MAX_SDF_VALUE+256]; + +STRUCT_FPTRS *pStructPtrs = NULL; /* dummy in this context */ +INPUT_PARMS inp_parms; +INPUT_PARMS *ip = &inp_parms; +STRUCT_DATA struct_data; +STRUCT_DATA *sd = &struct_data; +ORIG_ATOM_DATA OrigAtData; +ORIG_ATOM_DATA *orig_inp_data = &OrigAtData; +ORIG_ATOM_DATA PrepAtData[2]; +ORIG_ATOM_DATA *prep_inp_data = PrepAtData; +PINChI2 *pINChI[INCHI_NUM]; +PINChI_Aux2 *pINChI_Aux[INCHI_NUM]; +INCHI_IOSTREAM outputstr, logstr, prbstr, instr; +INCHI_IOSTREAM *pout=&outputstr, *plog = &logstr, *pprb = &prbstr, *inp_file = &instr; +int output_error_inchi = 0; +int have_err_in_GetOneStructure = 0; + +INCHI_IOSTREAM_STRING temp_string_container; +INCHI_IOSTREAM_STRING *strbuf = &temp_string_container; + + CANON_GLOBALS CG; + INCHI_CLOCK ic; + memset( &CG, 0, sizeof(CG)); + memset( &ic, 0, sizeof(ic)); + + retcode = PrepareToMakeINCHI( sd, ip, + orig_inp_data, prep_inp_data, + pINChI, pINChI_Aux, + pout, plog, pprb, + inp_file, moltext, + szOptions,strbuf); + + output_error_inchi = + ip->bINChIOutputOptions2 & INCHI_OUT_INCHI_GEN_ERROR; + + if ( retcode ) + { + if ( plog && plog->s.pStr ) + AddErrorMessage( plog->s.pStr, "Error while preparing to make InChI" ); + retcode = mol2inchi_Ret_ERROR; + num_err++; + goto ret; + } + + + have_err_in_GetOneStructure = 0; + + retcode = GetOneStructure( &ic, sd,ip, szTitle, inp_file, plog, pout, pprb, + orig_inp_data, &num_inp, pStructPtrs ); + + if ( retcode==_IS_FATAL || retcode==_IS_ERROR) + { + retcode = mol2inchi_Ret_ERROR; + num_err++; + have_err_in_GetOneStructure = 1; + /* + if ( plog && plog->s.pStr ) + AddErrorMessage( plog->s.pStr, "Error while reading/parsing structure" ); + */ + if ( !output_error_inchi ) + goto ret; + } + else if ( retcode==_IS_EOF ) + goto ret; + else if ( retcode==_IS_SKIP ) + goto ret; + + /* Always enable polymer extensions */ + if ( orig_inp_data->polymer ) + orig_inp_data->polymer->valid = 1; + + retcode = ProcessOneStructureEx( &ic, &CG, sd, ip, szTitle, + pINChI, pINChI_Aux, + inp_file, plog, pout, pprb, + orig_inp_data, prep_inp_data, + num_inp, strbuf, + 0 /* save_opt_bits */); + + + if ( retcode!=_IS_FATAL && retcode!=_IS_ERROR ) + { + /* output */ + produce_generation_output( result, sd, ip, plog, pout ); + if ( retcode==_IS_WARNING ) + retcode = mol2inchi_Ret_WARNING; + } + else + { + retcode = mol2inchi_Ret_ERROR; + num_err++; + if ( output_error_inchi ) + { + produce_generation_output( result, sd, ip, plog, pout ); + if ( !result->szInChI ) + { + /* As OutErrInchi was requested, we must fill an + InChI string anyway, here is the last chance */ + result->szInChI = (char *) inchi_malloc( 12*sizeof(char)); + if ( ip->bINChIOutputOptions & INCHI_OUT_STDINCHI ) + strcpy( result->szInChI, "InChI=1S//"); + else + strcpy( result->szInChI, "InChI=1//"); + } + } + } + + +ret: + retcode2 = PostMakeINCHICleanup( &CG, + sd, ip, + orig_inp_data, prep_inp_data, + pINChI, pINChI_Aux, + pout, plog, pprb, inp_file, + moltext, + strbuf ); + + if ( retcode2 ) + { + if ( plog && plog->s.pStr ) + AddErrorMessage( plog->s.pStr, "Failed while cleaning things after InChI produced" ); + retcode2 = mol2inchi_Ret_WARNING; + num_err++; + } + + copy_corrected_log_tail( result, plog ); + +#if 0 + /*if ( result->szLog && plog->s.pStr && plog->s.pStr[0] )&*/ + if ( plog->s.pStr && plog->s.pStr[0] ) + result->szLog = plog->s.pStr; + plog->s.pStr = NULL; + /* inchi_ios_close( plog ); */ +#endif + + if ( retcode < retcode2 ) + retcode = retcode2; + + return retcode; +} + + + + +/*****************************************************************************/ +int PrepareToMakeINCHI( STRUCT_DATA *sd, + INPUT_PARMS *ip, + ORIG_ATOM_DATA *orig_inp_data, + ORIG_ATOM_DATA *prep_inp_data, + PINChI2 *pINChI[INCHI_NUM], + PINChI_Aux2 *pINChI_Aux[INCHI_NUM], + INCHI_IOSTREAM *pout, + INCHI_IOSTREAM *plog, + INCHI_IOSTREAM *pprb, + INCHI_IOSTREAM *inp_file, + const char *moltext, + char *options, + INCHI_IOSTREAM_STRING *strbuf ) +{ +int retcode = 0; +unsigned long ulDisplTime = 0; +int bReleaseVersion = bRELEASE_VERSION; +char szSdfDataValue[MAX_SDF_VALUE+1]; + + + const char *quasi_argv[INCHI_MAX_NUM_ARG+1]; + int quasi_argc; + char *quasi_options = NULL; + + if ( options ) + quasi_options = (char*) malloc( strlen(options) + 1 ); + if ( quasi_options ) + { + strcpy( quasi_options, options ); + quasi_argc = parse_options_string ( quasi_options, quasi_argv, INCHI_MAX_NUM_ARG ); + } + else + { + quasi_argc = 1; + quasi_argv[0] = ""; + quasi_argv[1] = NULL; + } + + + /* I/O streams */ + + inchi_ios_init(pout, INCHI_IOSTREAM_TYPE_STRING, NULL); + inchi_ios_init(plog, INCHI_IOSTREAM_TYPE_STRING, NULL); + inchi_ios_init(pprb, INCHI_IOSTREAM_TYPE_STRING, NULL); + + + /* input ( string of Molfile )*/ + + inchi_ios_init(inp_file, INCHI_IOSTREAM_TYPE_STRING, NULL); + inp_file->s.pStr = (char * ) moltext; + inp_file->s.nPtr = 0; + inp_file->s.nUsedLength = strlen( moltext ) + 1; + inp_file->f = NULL; + + memset( szSdfDataValue, 0, sizeof( szSdfDataValue ) ); + + + /* data structs */ + + memset( sd, 0, sizeof(*sd) ); + memset( ip, 0, sizeof(*ip) ); + + memset( orig_inp_data , 0, sizeof( *orig_inp_data ) ); + memset( prep_inp_data , 0, 2*sizeof( *prep_inp_data ) ); + + pINChI[0] = pINChI[1] = NULL; + pINChI_Aux[0] = pINChI_Aux[1] = NULL; + + /* Parse command line */ + if ( 0 > ReadCommandLineParms( quasi_argc, + quasi_argv, + ip, + szSdfDataValue, + &ulDisplTime, + bReleaseVersion, + plog) ) + { + return MOL2INCHI_BAD_COMMAND_LINE; + } + + ip->nInputType = INPUT_MOLFILE; + ip->bNoStructLabels = 1; + ip->pSdfLabel = NULL; + ip->pSdfValue = NULL; + /* ip->bINChIOutputOptions |= INCHI_OUT_NO_AUX_INFO; */ + + + /* Supply expandable string buffer */ + if ( 0>=inchi_strbuf_init( strbuf, INCHI_STRBUF_INITIAL_SIZE, INCHI_STRBUF_SIZE_INCREMENT ) ) + { + if ( plog && plog->s.pStr ) + inchi_ios_eprint( plog, "Cannot allocate output string buffer. Terminating\n"); + retcode = MOL2INCHI_NO_RAM; + goto ret; + } + + +ret:if ( quasi_options ) + free( quasi_options ); + return retcode; +} + + +/*****************************************************************************/ +int PostMakeINCHICleanup( struct tagCANON_GLOBALS *pCG, + STRUCT_DATA *sd, + INPUT_PARMS *ip, + ORIG_ATOM_DATA *orig_inp_data, + ORIG_ATOM_DATA *prep_inp_data, + PINChI2 *pINChI[INCHI_NUM], + PINChI_Aux2 *pINChI_Aux[INCHI_NUM], + INCHI_IOSTREAM *pout, + INCHI_IOSTREAM *plog, + INCHI_IOSTREAM *pprb, + INCHI_IOSTREAM *inp_file, + const char *moltext, + INCHI_IOSTREAM_STRING *strbuf ) +{ +int retcode = 0; +int i; + + + /* Free structure data */ + + /* Free INChI memory */ + + FreeAllINChIArrays( pINChI, pINChI_Aux, sd->num_components ); + + FreeOrigAtData( orig_inp_data ); + FreeOrigAtData( prep_inp_data ); + FreeOrigAtData( prep_inp_data+1 ); + + inchi_ios_close(pout); + inchi_ios_close(pprb); + + inchi_strbuf_close( strbuf ); + + for ( i = 0; i < MAX_NUM_PATHS; i ++ ) + { + if ( ip->path[i] ) + { + free( (void*) ip->path[i] ); + /* cast deliberately discards 'const' qualifier */ + ip->path[i] = NULL; + } + } + + SetBitFree( pCG ); + + + return retcode; +} + + +/*****************************************************************************/ +void FreeInchi_Input( inchi_Input *inp_at_data ) +{ + FreeInchi_Atom( &inp_at_data->atom ); + FreeInchi_Stereo0D( &inp_at_data->stereo0D ); + memset( inp_at_data, 0, sizeof(*inp_at_data) ); +} + + +/*****************************************************************************/ + +#if( defined( _WIN32 ) && defined( _MSC_VER ) && _MSC_VER >= 800 && defined(_USRDLL) && defined(BUILD_LINK_AS_DLL) ) + /* Win32 & MS VC ++, compile and link as a DLL */ + +/*********************************************************/ +/* C calling conventions export from Win32 dll */ +/*********************************************************/ +/* prototypes */ +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + +int cdecl_MakeINCHIFromMolfileText( const char *moltext, + char *szOptions, + inchi_Output *out ); +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + +/* implementation */ +/* libinchi.def provides export without cdecl_ prefixes */ + +/*****************************************************************************/ +int cdecl_MakeINCHIFromMolfileText( const char *moltext, + char *options, + inchi_Output *out ) +{ + return + MakeINCHIFromMolfileText( moltext, + options, + out ); +} + +#endif + + + +#if( defined(__GNUC__) && __GNUC__ >= 3 && defined(__MINGW32__) && defined(_WIN32) ) + +#include +/*********************************************************/ +/* Pacal calling conventions export from Win32 dll */ +/*********************************************************/ + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + +/* prototypes */ +int PASCAL pasc_MakeINCHIFromMolfileText( const char *moltext, + char *options, + inchi_Output *out ); +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + +/* implementation */ +/* libinchi.def provides export without PASCAL pasc_ prefixes */ +/********************************************************/ +int PASCAL pasc_MakeINCHIFromMolfileText( const char *moltext, + char *options, + inchi_Output *out ) +{ + return + MakeINCHIFromMolfileText( moltext, + options, + out ); +} + +#endif + + + +/*****************************************************************************/ +S_SHORT *is_in_the_slist( S_SHORT *pathAtom, S_SHORT nNextAtom, int nPathLen ) +{ + for ( ; nPathLen && *pathAtom != nNextAtom; nPathLen--, pathAtom++ ) + ; + return nPathLen? pathAtom : NULL; +} + + +/*****************************************************************************/ +int is_element_a_metal( char szEl[] ) +{ + static const char szMetals[] = "K;V;Y;W;U;" + "Li;Be;Na;Mg;Al;Ca;Sc;Ti;Cr;Mn;Fe;Co;Ni;Cu;Zn;Ga;Rb;Sr;Zr;" + "Nb;Mo;Tc;Ru;Rh;Pd;Ag;Cd;In;Sn;Sb;Cs;Ba;La;Ce;Pr;Nd;Pm;Sm;" + "Eu;Gd;Tb;Dy;Ho;Er;Tm;Yb;Lu;Hf;Ta;Re;Os;Ir;Pt;Au;Hg;Tl;Pb;" + "Bi;Po;Fr;Ra;Ac;Th;Pa;Np;Pu;Am;Cm;Bk;Cf;Es;Fm;Md;No;Lr;Rf;"; + const int len = (int) strlen(szEl); + const char *p; + + if ( 0 < len && len <= 2 && + isalpha( UCINT szEl[0] ) && isupper( szEl[0] ) && + (p = strstr(szMetals, szEl) ) && p[len] == ';' ) { + + return 1; /*return AtType_Metal;*/ + } + return 0; +} + + +/*****************************************************************************/ + +#define inchi_NUMH2(AT,CUR_AT) ((AT[CUR_AT].num_iso_H[0]>0?AT[CUR_AT].num_iso_H[0]:0) +AT[CUR_AT].num_iso_H[1]+AT[CUR_AT].num_iso_H[2]+AT[CUR_AT].num_iso_H[3]) + +#define AT_NUM_BONDS(AT) (AT).num_bonds +#define ATOM_NUMBER AT_NUM +#define IN_NEIGH_LIST is_in_the_slist +#define Create_Atom CreateInchi_Atom +#define AT_BONDS_VAL(AT,I) AT[I].num_iso_H[0] +#define ISOLATED_ATOM (-15) +#define NUM_ISO_Hk(AT,I,K) AT[I].num_iso_H[K+1] +#define IS_METAL_ATOM(AT,I) is_element_a_metal( AT[I].elname ) + +/*****************************************************************************/ + + + +/*****************************************************************************/ +int InchiToInchiAtom( INCHI_IOSTREAM *inp_file, + inchi_Stereo0D **stereo0D, + int *num_stereo0D, + int bDoNotAddH, + int vABParityUnknown, + INPUT_TYPE nInputType, + inchi_Atom **at, + int max_num_at, + int *num_dimensions, + int *num_bonds, + char *pSdfLabel, + char *pSdfValue, + long *Id, + INCHI_MODE *pInpAtomFlags, + int *err, + char *pStrErr ) +{ +int num_atoms = 0, bFindNext = 0, len, bHeaderRead, bItemIsOver, bErrorMsg, bRestoreInfo; +int bFatal = 0, num_struct = 0; +int i, k, k2, res, bond_type, bond_stereo1, bond_stereo2, bond_char, neigh, bond_parity, bond_parityNM; +int bTooLongLine, res2, bTooLongLine2, pos, hlen, hk; +long longID; +char szLine[INCHI_LINE_LEN], szNextLine[INCHI_LINE_ADD], *p, *q, *s, parity; +int b2D=0, b3D=0, b23D, nNumBonds = 0, bNonZeroXYZ, bNonMetal; +int len_stereo0D = 0, max_len_stereo0D = 0; +inchi_Stereo0D *atom_stereo0D = NULL; +inchi_Atom *atom = NULL; +MOL_COORD *pszCoord = NULL; +INCHI_MODE InpAtomFlags = 0; /* 0 or FLAG_INP_AT_NONCHIRAL or FLAG_INP_AT_CHIRAL */ +static const char szIsoH[] = "hdt"; +/* plain tags */ +static const char sStructHdrPln[] = "Structure:"; +static const char sStructHdrPlnNoLblVal[] = " is missing"; +static char sStructHdrPlnAuxStart[64] =""; /*"$1.1Beta/";*/ +static int lenStructHdrPlnAuxStart = 0; +static const char sStructHdrPlnRevAt[] = "/rA:"; +static const char sStructHdrPlnRevBn[] = "/rB:"; +static const char sStructHdrPlnRevXYZ[] = "/rC:"; +const char *sToken; +int lToken; + + if ( !lenStructHdrPlnAuxStart ) + lenStructHdrPlnAuxStart = sprintf( sStructHdrPlnAuxStart, "AuxInfo=" ); + + + if ( at ) + { + + if ( *at && max_num_at ) + memset( *at, 0, max_num_at * sizeof(**at) ); + + if ( stereo0D && num_stereo0D ) + { + if ( *stereo0D && *num_stereo0D ) + { + max_len_stereo0D = *num_stereo0D; + memset( *stereo0D, 0, max_len_stereo0D * sizeof( **stereo0D )); + } + else + max_len_stereo0D = 0; + } + } + else /* if ( at ) */ + bFindNext = 1; + + + bHeaderRead = bErrorMsg = bRestoreInfo = 0; + *num_dimensions = *num_bonds = 0; + + + /*************************************************************/ + /* extract reversibility info from plain text INChI format */ + /*************************************************************/ + + if ( nInputType == INPUT_INCHI_PLAIN ) + { + + bHeaderRead = hk = 0; + + while ( 0 < (res = inchi_ios_getsTab( szLine, sizeof(szLine)-1, inp_file, &bTooLongLine ) ) ) + { + + /********************* find and interpret structure header ************/ + if ( !bTooLongLine && + (hlen=sizeof(sStructHdrPln)-1, !memcmp(szLine, sStructHdrPln, hlen)) ) + { + p = szLine + hlen; + longID = 0; + num_atoms = 0; + + /* structure number */ + longID = strtol( p, &q, 10 ); + if ( q && q[0] == '.' && q[1] == ' ' ) + p = q+2; + p = p + strspn( p, " \n\r" ); + + if ( pSdfLabel ) + pSdfLabel[0] = '\0'; + + if ( pSdfValue ) + pSdfValue[0] = '\0'; + + + if ( *p ) + { + /* has label name */ + + /*p ++;*/ + if ( q = strchr( p, '=' ) ) + { + + /* '=' separates label name from the value */ + len = inchi_min( q-p+1, MAX_SDF_HEADER-1); + + if ( pSdfLabel ) + { + mystrncpy( pSdfLabel, p, len ); + lrtrim( pSdfLabel, &len ); + } + + p = q+1; + q = p + (int)strlen( p ); + + if ( q-p > 0 ) + { + len = inchi_min( q-p+1, MAX_SDF_VALUE-1); + if ( pSdfValue ) + { + mystrncpy( pSdfValue, p, len ); + } + p = q; + } + } + else if ( q = strstr( p, sStructHdrPlnNoLblVal ) ) + { + len = inchi_min( q-p+1, MAX_SDF_HEADER-1); + if ( pSdfLabel ) { + mystrncpy( pSdfLabel, p, len ); + } + p = q+1; + } + } + + if ( Id ) + *Id = longID; + + bHeaderRead = 1; + bErrorMsg = bRestoreInfo = 0; + } + else if ( !memcmp( szLine, sStructHdrPlnAuxStart, lenStructHdrPlnAuxStart) ) + { + /* found the header of the AuxInfo, read AuxInfo head of the line */ + + if ( !bHeaderRead ) + { + longID = 0; + if ( Id ) + *Id = longID; + if ( pSdfLabel ) + pSdfLabel[0] = '\0'; + if ( pSdfValue ) + pSdfValue[0] = '\0'; + } + + bHeaderRead = 0; + + /* check for empty "AuxInfo=ver//" */ + + p = strchr( szLine + lenStructHdrPlnAuxStart, '/' ); + + if ( p && p[1] == '/' && (!p[2] || '\n' == p[2]) ) + { + goto bypass_end_of_INChI_plain; + } + + /***************** search for atoms block (plain) **********************/ + + p = szLine; + sToken = sStructHdrPlnRevAt; + lToken = sizeof(sStructHdrPlnRevAt)-1; + + /* search for sToken in the line; load next segments of the line if sToken has not found */ + + p = FindToken( inp_file, &bTooLongLine, sToken, lToken, + szLine, sizeof(szLine), p, &res ); + + if ( !p ) + { + *err = INCHI_INP_ERROR_ERR; + num_atoms = INCHI_INP_ERROR_RET; + TREAT_ERR (*err, 0, "Missing atom data"); + goto bypass_end_of_INChI_plain; + } + else + { + /* atoms block started */ + + i = 0; + res2 = bTooLongLine2 = -1; + bItemIsOver = (s = strchr( p, '/') ) || !bTooLongLine; + + while ( 1 ) + { + + p = LoadLine( inp_file, &bTooLongLine, &bItemIsOver, &s, + szLine, sizeof(szLine), INCHI_LINE_ADD, p, &res ); + + if ( !i ) + { + /* allocate atom */ + num_atoms = strtol( p, &q, 10 ); + + if ( !num_atoms || !q || !*q ) + { + num_atoms = 0; /* no atom data */ + goto bypass_end_of_INChI_plain; + } + p = q; + + /* Molfile chirality flag */ + switch( *p ) + { + case 'c': + InpAtomFlags |= FLAG_INP_AT_CHIRAL; + p ++; + break; + case 'n': + InpAtomFlags |= FLAG_INP_AT_NONCHIRAL; + p ++; + break; + } + + if ( at && *at ) + { + if ( num_atoms > max_num_at ) + { + inchi_free( *at ); + *at = NULL; + } + else + { + memset( *at, 0, max_num_at * sizeof( **at ) ); + atom = *at; + } + } + + if ( !at || !*at ) + { + + atom = CreateInchiAtom( num_atoms+1 ); + + if ( !atom ) + { + num_atoms = INCHI_INP_FATAL_RET; /* was -1; error */ + *err = INCHI_INP_FATAL_ERR; + TREAT_ERR (*err, 0, "Out of RAM"); + goto bypass_end_of_INChI_plain; + } + } + + if ( stereo0D && *stereo0D ) + { + if ( num_atoms > max_len_stereo0D ) + FreeInchi_Stereo0D( stereo0D ); + else + { + memset( *stereo0D, 0, max_len_stereo0D * sizeof( **stereo0D ) ); + atom_stereo0D = *stereo0D; + } + } + + if ( !stereo0D || !*stereo0D ) + { + max_len_stereo0D = num_atoms+1; + + atom_stereo0D = CreateInchi_Stereo0D( max_len_stereo0D ); + + if ( !atom_stereo0D ) + { + num_atoms = INCHI_INP_FATAL_RET; /* fatal error: cannot allocate */ + *err = INCHI_INP_FATAL_ERR; + TREAT_ERR (*err, 0, "Out of RAM"); + goto bypass_end_of_INChI_plain; + } + } + } + + /* element, first char */ + if ( !isalpha( UCINT *p ) || !isupper( UCINT *p ) || i >= num_atoms ) + { + break; /* end of atoms block */ + } + + atom[i].elname[0] = *p ++; + + /* element, second char */ + if ( isalpha( UCINT *p ) && islower( UCINT *p ) ) + { + atom[i].elname[1] = *p ++; + } + + /* bonds' valence + number of non-isotopic H */ + if ( isdigit( UCINT *p ) ) + { + AT_BONDS_VAL(atom,i) = (char)strtol( p, &q, 10 ); + if ( !AT_BONDS_VAL(atom,i) ) + AT_BONDS_VAL(atom,i) = ISOLATED_ATOM; /* same convention as in MOLfile, found zero bonds valence */ + p = q; + } + + /* charge */ + atom[i].charge = (*p == '+')? 1 : (*p == '-') ? -1 + : 0; + if ( atom[i].charge ) + { + p ++; + if ( isdigit( UCINT *p ) ) + { + atom[i].charge *= (S_CHAR)(strtol( p, &q, 10 ) & CHAR_MASK); + p = q; + } + } + + /* radical */ + if ( *p == '.' ) + { + p ++; + if ( isdigit( UCINT *p ) ) + { + atom[i].radical = (S_CHAR)strtol( p, &q, 10 ); + p = q; + } + } + + /* isotopic mass */ + if ( *p == 'i' ) + { + p ++; + if ( isdigit( UCINT *p ) ) + { + int mw = strtol( p, &q, 10 ); + p = q; + + atom[i].isotopic_mass = mw; + } + } + + /* parity */ + switch( *p ) + { + case 'o': + parity = INCHI_PARITY_ODD; + p ++; + break; + case 'e': + parity = INCHI_PARITY_EVEN; + p ++; + break; + case 'u': + parity = INCHI_PARITY_UNKNOWN; + p ++; + break; + case '?': + parity = INCHI_PARITY_UNDEFINED; + p ++; + break; + default: + parity = 0; + break; + } + + if ( parity ) + { + atom_stereo0D[len_stereo0D].central_atom = i; + atom_stereo0D[len_stereo0D].parity = parity; + atom_stereo0D[len_stereo0D].type = INCHI_StereoType_Tetrahedral; + len_stereo0D ++; + } + + /* isotopic h, d, t */ + for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) + { + if ( *p == szIsoH[k] ) { + NUM_ISO_Hk(atom,i,k) = 1; + p ++; + if ( isdigit( UCINT *p ) ) { + NUM_ISO_Hk(atom,i,k) = (char)strtol( p, &q, 10 ); + p = q; + } + } + } + + i ++; + } + + + if ( !bItemIsOver || i != num_atoms || s && p != s ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong number of atoms"); + goto bypass_end_of_INChI_plain; + } + } + + /***************** search for bonds block (plain) and read it *****************/ + + /*p = szLine;*/ + sToken = sStructHdrPlnRevBn; + lToken = sizeof(sStructHdrPlnRevBn)-1; + + /* search for sToken in the line; load next segments of the line if sToken has not found */ + + p = FindToken( inp_file, &bTooLongLine, sToken, lToken, + szLine, sizeof(szLine), p, &res ); + + if ( !p ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Missing bonds data"); + goto bypass_end_of_INChI_plain; + } + else + { + /* bonds block started */ + + i = 1; + + res2 = bTooLongLine2 = -1; + + bItemIsOver = (s = strchr( p, '/') ) || !bTooLongLine; + + if ( 1 == num_atoms ) + { + /* needed because the next '/' may be still out of szLine */ + + p = LoadLine( inp_file, &bTooLongLine, &bItemIsOver, &s, + szLine, sizeof(szLine), INCHI_LINE_ADD, p, &res ); + } + + while ( i < num_atoms ) + { + + p = LoadLine( inp_file, &bTooLongLine, &bItemIsOver, &s, + szLine, sizeof(szLine), INCHI_LINE_ADD, p, &res ); + + if ( i >= num_atoms || s && p >= s ) + { + break; /* end of bonds (plain) */ + } + + /* bond, first char */ + if ( *p == ';' ) + { + p ++; + i ++; + continue; + } + + if ( !isalpha( UCINT *p ) ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong bonds data"); + goto bypass_end_of_INChI_plain; + } + + bond_char = *p ++; + + /* bond parity */ + switch( *p ) + { + case '-': + bond_parity = INCHI_PARITY_ODD; + p ++; + break; + case '+': + bond_parity = INCHI_PARITY_EVEN; + p ++; + break; + case 'u': + bond_parity = INCHI_PARITY_UNKNOWN; + p ++; + break; + case '?': + bond_parity = INCHI_PARITY_UNDEFINED; + p ++; + break; + default: + bond_parity = 0; + break; + } + + if ( bond_parity ) + { + switch( *p ) + { + case '-': + bond_parityNM = INCHI_PARITY_ODD; + p ++; + break; + case '+': + bond_parityNM = INCHI_PARITY_EVEN; + p ++; + break; + case 'u': + bond_parityNM = INCHI_PARITY_UNKNOWN; + p ++; + break; + case '?': + bond_parityNM = INCHI_PARITY_UNDEFINED; + p ++; + break; + default: + bond_parityNM = 0; + break; + } + } + else + { + bond_parityNM = 0; + } + + /* neighbor of the current atom */ + if ( !isdigit( UCINT *p ) ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong bonds data"); + goto bypass_end_of_INChI_plain; + } + + neigh = (int)strtol( p, &q, 10 )-1; + + if ( i >= num_atoms || neigh >= num_atoms ) { + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Bond to nonexistent atom"); + goto bypass_end_of_INChI_plain; + } + + p = q; + bond_stereo1 = bond_stereo2 = 0; + + /* bond type & 2D stereo */ + switch( bond_char ) + { + case 'v': + bond_type = INCHI_BOND_TYPE_SINGLE; + bond_stereo1 = INCHI_BOND_STEREO_SINGLE_1EITHER; + bond_stereo2 = INCHI_BOND_STEREO_SINGLE_2EITHER; + break; + case 'V': + bond_type = INCHI_BOND_TYPE_SINGLE; + bond_stereo1 = INCHI_BOND_STEREO_SINGLE_2EITHER; + bond_stereo2 = INCHI_BOND_STEREO_SINGLE_1EITHER; + break; + case 'w': + bond_type = INCHI_BOND_TYPE_DOUBLE; + bond_stereo1 = + bond_stereo2 = INCHI_BOND_STEREO_DOUBLE_EITHER; + break; + case 's': + bond_type = INCHI_BOND_TYPE_SINGLE; + break; + case 'd': + bond_type = INCHI_BOND_TYPE_DOUBLE; + break; + case 't': + bond_type = INCHI_BOND_TYPE_TRIPLE; + break; + case 'a': + bond_type = INCHI_BOND_TYPE_ALTERN; + break; + case 'p': + bond_type = INCHI_BOND_TYPE_SINGLE; + bond_stereo1 = INCHI_BOND_STEREO_SINGLE_1UP; + bond_stereo2 = INCHI_BOND_STEREO_SINGLE_2UP; + break; + case 'P': + bond_type = INCHI_BOND_TYPE_SINGLE; + bond_stereo1 = INCHI_BOND_STEREO_SINGLE_2UP; + bond_stereo2 = INCHI_BOND_STEREO_SINGLE_1UP; + break; + case 'n': + bond_type = INCHI_BOND_TYPE_SINGLE; + bond_stereo1 = INCHI_BOND_STEREO_SINGLE_1DOWN; + bond_stereo2 = INCHI_BOND_STEREO_SINGLE_2DOWN; + break; + case 'N': + bond_type = INCHI_BOND_TYPE_SINGLE; + bond_stereo1 = INCHI_BOND_STEREO_SINGLE_2DOWN; + bond_stereo2 = INCHI_BOND_STEREO_SINGLE_1DOWN; + break; + default: + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong bond type"); + goto bypass_end_of_INChI_plain; + } + + k = AT_NUM_BONDS(atom[i]) ++; + + atom[i].bond_type[k] = bond_type; + atom[i].bond_stereo[k] = bond_stereo1; + atom[i].neighbor[k] = (ATOM_NUMBER)neigh; + + k2 = AT_NUM_BONDS(atom[neigh]) ++; + atom[neigh].bond_type[k2] = bond_type; + atom[neigh].bond_stereo[k2] = bond_stereo2; + atom[neigh].neighbor[k2] = (ATOM_NUMBER)i; + + bond_parity |= (bond_parityNM << SB_PARITY_SHFT); + + if ( bond_parity ) + { + if ( max_len_stereo0D <= len_stereo0D ) + { + /* realloc atom_Stereo0D */ + + inchi_Stereo0D *new_atom_stereo0D = CreateInchi_Stereo0D( max_len_stereo0D+num_atoms ); + + if ( !new_atom_stereo0D ) + { + num_atoms = INCHI_INP_FATAL_RET; /* fatal error: cannot allocate */ + *err = INCHI_INP_FATAL_ERR; + TREAT_ERR (*err, 0, "Out of RAM"); + goto bypass_end_of_INChI_plain; + } + + memcpy( new_atom_stereo0D, atom_stereo0D, len_stereo0D * sizeof(*atom_stereo0D) ); + FreeInchi_Stereo0D( &atom_stereo0D ); + atom_stereo0D = new_atom_stereo0D; + max_len_stereo0D += num_atoms; + } + + /* (a) i may be allene endpoint and neigh = allene middle point or + (b) i may be allene middle point and neigh = allene endpoint + !!!!! CURRENTLY ONLY (b) IS ALLOWED !!!!! + */ + + atom_stereo0D[len_stereo0D].neighbor[1] = neigh; /* neigh < i */ + atom_stereo0D[len_stereo0D].neighbor[2] = i; + atom_stereo0D[len_stereo0D].parity = bond_parity; + atom_stereo0D[len_stereo0D].type = INCHI_StereoType_DoubleBond; /* incl allenes & cumulenes */ + len_stereo0D ++; + } + } + + if ( !bItemIsOver || i != num_atoms || s && p != s ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong number of bonds"); + goto bypass_end_of_INChI_plain; + } + } + + /***************** search for coordinates block (plain) **********************/ + /*p = szLine;*/ + + sToken = sStructHdrPlnRevXYZ; + lToken = sizeof(sStructHdrPlnRevXYZ)-1; + + /* search for sToken in the line; load next segments of the line if sToken has not found */ + + p = FindToken( inp_file, &bTooLongLine, sToken, lToken, + szLine, sizeof(szLine), p, &res ); + + if ( !p ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Missing atom coordinates data"); + goto bypass_end_of_INChI_plain; + } + else + { + /* coordinates block started */ + if ( pszCoord = (MOL_COORD*) inchi_malloc(inchi_max(num_atoms,1) * sizeof(MOL_COORD)) ) + { + memset( pszCoord, ' ', inchi_max(num_atoms,1) * sizeof(MOL_COORD)); + } + else + { + num_atoms = INCHI_INP_FATAL_RET; /* allocation error */ + *err = INCHI_INP_FATAL_ERR; + TREAT_ERR (*err, 0, "Out of RAM"); + goto bypass_end_of_INChI_plain; + } + + i = 0; + res2 = bTooLongLine2 = -1; + bItemIsOver = (s = strchr( p, '/') ) || !bTooLongLine; + + while ( i < num_atoms ) + { + + p = LoadLine( inp_file, &bTooLongLine, &bItemIsOver, &s, + szLine, sizeof(szLine), INCHI_LINE_ADD, p, &res ); + + if ( i >= num_atoms || s && p >= s ) { + break; /* end of bonds (plain) */ + } + + /* coord, first char */ + if ( *p == ';' ) + { + for ( k = 0; k < NUM_COORD; k ++ ) + { + pszCoord[i][LEN_COORD*k + 4] = '0'; + } + p ++; + i ++; + continue; + } + + for ( k = 0; k < 3; k ++ ) + { + double xyz; + bNonZeroXYZ = 0; + if ( *p == ';' ) + { + pszCoord[i][LEN_COORD*k + 4] = '0'; + xyz = 0.0; + } else + if ( *p == ',' ) + { + /* empty */ + pszCoord[i][LEN_COORD*k + 4] = '0'; + xyz = 0.0; + p ++; + } + else + { + xyz = strtod( p, &q ); + bNonZeroXYZ = fabs(xyz) > MIN_BOND_LENGTH; + if ( q != NULL ) { + memcpy( pszCoord[i]+LEN_COORD*k, p, q-p ); + if ( *q == ',' ) + q ++; + p = q; + } + else + pszCoord[i][LEN_COORD*k + 4] = '0'; + } + + switch( k ) + { + case 0: + atom[i].x = xyz; + b2D |= bNonZeroXYZ; + break; + case 1: + atom[i].y = xyz; + b2D |= bNonZeroXYZ; + break; + case 2: + b3D |= bNonZeroXYZ; + atom[i].z = xyz; + break; + } + } + + if ( *p == ';' ) + { + p ++; /* end of this triple of coordinates */ + i ++; + } + else + { + num_atoms = INCHI_INP_ERROR_RET; /* error in input data: atoms, bonds & coord must be present together */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong atom coordinates data"); + goto bypass_end_of_INChI_plain; + } + } + + if ( !bItemIsOver || s && p != s || i != num_atoms ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong number of coordinates"); + goto bypass_end_of_INChI_plain; + } + } /* end of coordinates */ + + + /* set special valences and implicit H (xml) */ + + b23D = b2D | b3D; + b2D = b3D = 0; + if ( at ) + { + if ( !*at ) + { + int a1, a2, n1, n2, valence; + int chem_bonds_valence; + int nX=0, nY=0, nZ=0, nXYZ; + *at = atom; + + /* special valences */ + + for ( bNonMetal = 0; bNonMetal < 1; bNonMetal ++ ) + { + + for ( a1 = 0; a1 < num_atoms; a1 ++ ) + { + + int num_bond_type[MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE + 1]; + + memset( num_bond_type, 0, sizeof(num_bond_type) ); + + valence = AT_BONDS_VAL(atom, a1); /* save atom valence if available */ + AT_BONDS_VAL(atom, a1) = 0; + + + nX = nY = nZ = 0; + + for ( n1 = 0; n1 < AT_NUM_BONDS(atom[a1]); n1 ++ ) + { + bond_type = atom[a1].bond_type[n1] - MIN_INPUT_BOND_TYPE; + if ( bond_type < 0 || bond_type > MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE ) + { + bond_type = 0; + TREAT_ERR (*err, 0, "Unknown bond type in InChI aux assigned as a single bond"); + } + + num_bond_type[ bond_type ] ++; + nNumBonds ++; + if ( b23D ) + { + neigh = atom[a1].neighbor[n1]; + nX |= (fabs(atom[a1].x - atom[neigh].x) > MIN_BOND_LENGTH); + nY |= (fabs(atom[a1].y - atom[neigh].y) > MIN_BOND_LENGTH); + nZ |= (fabs(atom[a1].z - atom[neigh].z) > MIN_BOND_LENGTH); + } + } + + chem_bonds_valence = 0; + for ( n1 = 0; MIN_INPUT_BOND_TYPE + n1 <= 3 && MIN_INPUT_BOND_TYPE + n1 <= MAX_INPUT_BOND_TYPE; n1 ++ ) + { + chem_bonds_valence += (MIN_INPUT_BOND_TYPE + n1) * num_bond_type[n1]; + } + + if ( MIN_INPUT_BOND_TYPE <= INCHI_BOND_TYPE_ALTERN && INCHI_BOND_TYPE_ALTERN <= MAX_INPUT_BOND_TYPE && + ( n2 = num_bond_type[INCHI_BOND_TYPE_ALTERN-MIN_INPUT_BOND_TYPE] ) ) + { + + /* accept input aromatic bonds for now */ + + switch ( n2 ) + { + case 2: + chem_bonds_valence += 3; /* =A- */ + break; + + case 3: + chem_bonds_valence += 4; /* =A< */ + break; + + default: + /* if 1 or >= 4 aromatic bonds then replace such bonds with single bonds */ + for ( n1 = 0; n1 < AT_NUM_BONDS(atom[a1]); n1 ++ ) + { + if ( atom[a1].bond_type[n1] == INCHI_BOND_TYPE_ALTERN ) + { + ATOM_NUMBER *p1; + a2 = atom[a1].neighbor[n1]; + p1 = IN_NEIGH_LIST( atom[a2].neighbor, (ATOM_NUMBER)a1, AT_NUM_BONDS(atom[a2]) ); + if ( p1 ) + { + atom[a1].bond_type[n1] = + atom[a2].bond_type[p1-atom[a2].neighbor] = INCHI_BOND_TYPE_SINGLE; + } + else + { + *err = -2; /* Program error */ + TREAT_ERR (*err, 0, "Program error interpreting InChI aux"); + num_atoms = 0; + goto bypass_end_of_INChI_plain; /* no structure */ + } + } + } + + chem_bonds_valence += n2; + *err |= 32; /* Unrecognized aromatic bond(s) replaced with single */ + TREAT_ERR (*err, 0, "Atom has 1 or more than 3 aromatic bonds"); + break; + } + } + /******************************** + * + * Set number of hydrogen atoms + */ + { + int num_iso_H; + num_iso_H = atom[a1].num_iso_H[1] + atom[a1].num_iso_H[2] + atom[a1].num_iso_H[3]; + if ( valence == ISOLATED_ATOM ) { + atom[a1].num_iso_H[0] = 0; + } else + if ( valence && valence >= chem_bonds_valence ) { + atom[a1].num_iso_H[0] = valence - chem_bonds_valence; + } else + if ( valence || bDoNotAddH ) { + atom[a1].num_iso_H[0] = 0; + } else + if ( !bDoNotAddH ) { + atom[a1].num_iso_H[0] = -1; /* auto add H */ + } + } + } + } + + nNumBonds /= 2; + + if ( b23D && nNumBonds ) + { + nXYZ = nX+nY+nZ; + b2D = (nXYZ > 0); + b3D = (nXYZ == 3); + *num_dimensions = b3D? 3 : b2D? 2 : 0; + *num_bonds = nNumBonds; + } + + /*======= 0D parities =================================*/ + if ( len_stereo0D > 0 && atom_stereo0D && stereo0D ) + { + *stereo0D = atom_stereo0D; + *num_stereo0D = len_stereo0D; + } + else + { + FreeInchi_Stereo0D( &atom_stereo0D ); + *num_stereo0D = len_stereo0D = 0; + } + + for ( i = 0; i < len_stereo0D; i ++ ) + { + ATOM_NUMBER *p1, *p2; + int sb_ord_from_a1 = -1, sb_ord_from_a2 = -1, bEnd1 = 0, bEnd2 = 0; + + switch( atom_stereo0D[i].type ) + { + + case INCHI_StereoType_Tetrahedral: + a1 = atom_stereo0D[i].central_atom; + if ( atom_stereo0D[i].parity && (AT_NUM_BONDS(atom[a1]) == 3 || AT_NUM_BONDS(atom[a1]) == 4) ) + { + int ii, kk = 0; + if ( AT_NUM_BONDS(atom[a1]) == 3 ) + atom_stereo0D[i].neighbor[kk++] = a1; + for ( ii = 0; ii < AT_NUM_BONDS(atom[a1]); ii ++ ) + atom_stereo0D[i].neighbor[kk++] = atom[a1].neighbor[ii]; + } + + break; + + case INCHI_StereoType_DoubleBond: +#define MAX_CHAIN_LEN 20 + a1 = atom_stereo0D[i].neighbor[1]; + a2 = atom_stereo0D[i].neighbor[2]; + p1 = IN_NEIGH_LIST( atom[a1].neighbor, (ATOM_NUMBER)a2, AT_NUM_BONDS(atom[a1]) ); + p2 = IN_NEIGH_LIST( atom[a2].neighbor, (ATOM_NUMBER)a1, AT_NUM_BONDS(atom[a2]) ); + if ( !p1 || !p2 ) + { + atom_stereo0D[i].type = INCHI_StereoType_None; + atom_stereo0D[i].central_atom = NO_ATOM; + atom_stereo0D[i].neighbor[0] = + atom_stereo0D[i].neighbor[3] = -1; + *err |= 64; /* Error in cumulene stereo */ + TREAT_ERR (*err, 0, "0D stereobond not recognized"); + break; + } + + /* streobond, allene, or cumulene */ + + sb_ord_from_a1 = p1 - atom[a1].neighbor; + sb_ord_from_a2 = p2 - atom[a2].neighbor; + + if ( AT_NUM_BONDS(atom[a1]) == 2 && + atom[a1].bond_type[0] + atom[a1].bond_type[1] == 2*INCHI_BOND_TYPE_DOUBLE && + 0 == inchi_NUMH2(atom, a1) && + (AT_NUM_BONDS(atom[a2]) != 2 || + atom[a2].bond_type[0] + atom[a2].bond_type[1] != 2*INCHI_BOND_TYPE_DOUBLE ) ) + { + bEnd2 = 1; /* a2 is the end-atom, a1 is middle atom */ + } + + if ( AT_NUM_BONDS(atom[a2]) == 2 && + atom[a2].bond_type[0] + atom[a2].bond_type[1] == 2*INCHI_BOND_TYPE_DOUBLE && + 0 == inchi_NUMH2(atom, a2) && + (AT_NUM_BONDS(atom[a1]) != 2 || + atom[a1].bond_type[0] + atom[a1].bond_type[1] != 2*INCHI_BOND_TYPE_DOUBLE ) ) + { + bEnd1 = 1; /* a1 is the end-atom, a2 is middle atom */ + } + + if ( bEnd2 + bEnd1 == 1 ) + { + /* allene or cumulene */ + + ATOM_NUMBER chain[MAX_CHAIN_LEN+1], prev, cur, next; + + if ( bEnd2 && !bEnd1 ) + { + cur = a1; + a1 = a2; + a2 = cur; + sb_ord_from_a1 = sb_ord_from_a2; + } + + sb_ord_from_a2 = -1; + cur = a1; + next = a2; + len = 0; + chain[len++] = cur; + chain[len++] = next; + + while ( len < MAX_CHAIN_LEN ) + { + /* arbitrary very high upper limit to prevent infinite loop */ + + prev = cur; + cur = next; + /* follow double bond path && avoid going back */ + if ( AT_NUM_BONDS(atom[cur]) == 2 && + atom[cur].bond_type[0]+atom[cur].bond_type[1] == 2*INCHI_BOND_TYPE_DOUBLE && + 0 == inchi_NUMH2(atom, cur) ) + { + next = atom[cur].neighbor[atom[cur].neighbor[0] == prev]; + chain[len++] = next; + } + else + { + break; + } + } + if ( len > 2 && + (p2 = IN_NEIGH_LIST( atom[cur].neighbor, (ATOM_NUMBER)prev, AT_NUM_BONDS(atom[cur]))) ) + { + sb_ord_from_a2 = p2 - atom[cur].neighbor; + a2 = cur; + /* by design we need to pick up the first non-stereo-bond-neighbor as "sn"-atom */ + atom_stereo0D[i].neighbor[0] = atom[a1].neighbor[sb_ord_from_a1 == 0]; + atom_stereo0D[i].neighbor[1] = a1; + atom_stereo0D[i].neighbor[2] = a2; + atom_stereo0D[i].neighbor[3] = atom[a2].neighbor[sb_ord_from_a2 == 0]; + + if ( len % 2 ) + { + atom_stereo0D[i].central_atom = chain[len/2]; + atom_stereo0D[i].type = INCHI_StereoType_Allene; + } + else + { + atom_stereo0D[i].central_atom = NO_ATOM; + } + } + else + { + /* error */ + atom_stereo0D[i].type = INCHI_StereoType_None; + atom_stereo0D[i].central_atom = NO_ATOM; + atom_stereo0D[i].neighbor[0] = + atom_stereo0D[i].neighbor[3] = -1; + *err |= 64; /* Error in cumulene stereo */ + TREAT_ERR (*err, 0, "Cumulene stereo not recognized (0D)"); + } +#undef MAX_CHAIN_LEN + } + else + { + /****** a normal possibly stereogenic bond -- not an allene or cumulene *******/ + /* by design we need to pick up the first non-stereo-bond-neighbor as "sn"-atom */ + sb_ord_from_a1 = p1 - atom[a1].neighbor; + sb_ord_from_a2 = p2 - atom[a2].neighbor; + atom_stereo0D[i].neighbor[0] = atom[a1].neighbor[p1 == atom[a1].neighbor]; + atom_stereo0D[i].neighbor[3] = atom[a2].neighbor[p2 == atom[a2].neighbor]; + atom_stereo0D[i].central_atom = NO_ATOM; + } + + if ( atom_stereo0D[i].type != INCHI_StereoType_None && + sb_ord_from_a1 >= 0 && sb_ord_from_a2 >= 0 && + ATOM_PARITY_WELL_DEF( SB_PARITY_2(atom_stereo0D[i].parity) ) ) + { + /* Detected well-defined disconnected stereo + * locate first non-metal neighbors */ + + int a, n, j, /* k,*/ sb_ord, cur_neigh, min_neigh; + + for ( k = 0; k < 2; k ++ ) + { + a = k? atom_stereo0D[i].neighbor[2] : atom_stereo0D[i].neighbor[1]; + sb_ord = k? sb_ord_from_a2 : sb_ord_from_a1; + min_neigh = num_atoms; + for ( n = j = 0; j < AT_NUM_BONDS(atom[a]); j ++ ) + { + cur_neigh = atom[a].neighbor[j]; + if ( j != sb_ord && !IS_METAL_ATOM(atom, cur_neigh) ) + { + min_neigh = inchi_min( cur_neigh, min_neigh ); + } + } + if ( min_neigh < num_atoms ) { + atom_stereo0D[i].neighbor[k?3:0] = min_neigh; + } else { + TREAT_ERR (*err, 0, "Cannot find non-metal stereobond neighor (0D)"); + } + } + } + + break; + } + } + /* end of 0D parities extraction */ +/*exit_cycle:;*/ + } + + if ( pInpAtomFlags ) + { + /* save chirality flag */ + *pInpAtomFlags |= InpAtomFlags; + } + } + else if ( atom ) + { + inchi_free( atom ); + atom = NULL; + } + + if ( pszCoord ) + { + inchi_free( pszCoord ); + pszCoord = NULL; + } + + goto bypass_end_of_INChI_plain; + /*return num_atoms;*/ + } + } + + if ( atom_stereo0D ) + { + FreeInchi_Stereo0D( &atom_stereo0D ); + } + + + /* end of structure reading cycle */ + + if ( res <= 0 ) + { + if ( *err == INCHI_INP_ERROR_ERR ) + return num_atoms; + + *err = INCHI_INP_EOF_ERR; + return INCHI_INP_EOF_RET; /* no more data */ + } + + +bypass_end_of_INChI_plain: + + /* cleanup */ + if ( num_atoms == INCHI_INP_ERROR_RET && atom_stereo0D ) + { + if ( stereo0D && *stereo0D == atom_stereo0D ) + { + *stereo0D = NULL; + *num_stereo0D = 0; + } + FreeInchi_Stereo0D( &atom_stereo0D ); + } + + while ( bTooLongLine && + 0 < inchi_ios_getsTab1( szLine, sizeof(szLine)-1, inp_file, &bTooLongLine ) ) + { + ; + } + + + /* cleanup */ + if ( !*at ) { + if ( atom ) { + inchi_free( atom ); + atom = NULL; + } + if ( pszCoord ) { + inchi_free( pszCoord ); + pszCoord = NULL; + } + } + + + return num_atoms; + } + + /***********************************************************/ + /* extract reversibility info from xml text INChI format */ + /* */ + /* OBSOLETE CODE because InChI output in XML */ + /* does not exist anymore. Unsupported. */ + /* */ + /***********************************************************/ + + if ( nInputType == INPUT_INCHI_XML ) + { + + /* xml tags */ + + static const char sStructHdrXml[] = " max_num_at ) + { + inchi_free( *at ); + *at = NULL; + } + else + { + memset( *at, 0, max_num_at * sizeof( **at ) ); + atom = *at; + } + } + if ( !at || !*at ) + { + atom = CreateInchiAtom( num_atoms+1 ); + if ( !atom ) { + num_atoms = INCHI_INP_FATAL_RET; /* fatal error: cannot allocate */ + *err = INCHI_INP_FATAL_ERR; + TREAT_ERR (*err, 0, "Out of RAM"); + goto bypass_end_of_INChI; + } + } + if ( stereo0D && *stereo0D ) + { + if ( num_atoms > max_len_stereo0D ) + { + FreeInchi_Stereo0D( stereo0D ); + } + else + { + memset( *stereo0D, 0, max_len_stereo0D * sizeof( **stereo0D ) ); + atom_stereo0D = *stereo0D; + } + } + if ( !stereo0D || !*stereo0D ) + { + max_len_stereo0D = num_atoms+1; + atom_stereo0D = CreateInchi_Stereo0D( max_len_stereo0D ); + if ( !atom_stereo0D ) + { + num_atoms = INCHI_INP_FATAL_RET; /* fatal error: cannot allocate */ + *err = INCHI_INP_FATAL_ERR; + TREAT_ERR (*err, 0, "Out of RAM"); + goto bypass_end_of_INChI; + } + } + + i = 0; + bItemIsOver = 0; + res2 = bTooLongLine2 = -1; + + /* read all atoms xml */ + while ( i < num_atoms ) + { + pos = p - szLine; + if ( !bItemIsOver && (int)sizeof(szLine)-res + pos > (int)sizeof(szNextLine) ) + { + /* load next line if possible */ + res2 = inchi_ios_gets( szNextLine, sizeof(szNextLine)-1, inp_file, &bTooLongLine2 ); + if ( res2 > 0 && memcmp(szNextLine, sStructRevXmlRevAtEnd, sizeof(sStructRevXmlRevAtEnd)-1) ) + { + if ( pos ) + { + res -= pos; /* number of chars left to process in szLine */ + memmove( szLine, p, res*sizeof(szLine[0]) ); /* move them to the start of the line */ + } + memcpy( szLine+res, szNextLine, (res2+1)*sizeof(szNextLine[0]) ); + res += res2; + szLine[res] = '\0'; + bTooLongLine = bTooLongLine2; + p = szLine; + } + else + bItemIsOver = 1; + } + + /* element, first char */ + if ( !isalpha( UCINT *p ) || !isupper( UCINT *p ) || i >= num_atoms ) + { + bHeaderRead = 0; /* wrong atom data */ + num_atoms = INCHI_INP_ERROR_RET; /* was 0, error */ + *err = INCHI_INP_ERROR_ERR; /* 40 */ + TREAT_ERR (*err, 0, "Wrong atoms data"); + goto bypass_end_of_INChI; + } + atom[i].elname[0] = *p ++; + /* element, second char */ + if ( isalpha( UCINT *p ) && islower( UCINT *p ) ) + { + atom[i].elname[1] = *p ++; + } + + /* bonds' valence */ + if ( isdigit( UCINT *p ) ) + { + AT_BONDS_VAL(atom,i) = (char)strtol( p, &q, 10 ); + if ( !AT_BONDS_VAL(atom,i) ) + AT_BONDS_VAL(atom,i) = ISOLATED_ATOM; /* same convention as in MOLfile, found zero bonds valence */ + p = q; + } + /* charge */ + atom[i].charge = (*p == '+')? 1 : (*p == '-')? -1 : 0; + if ( atom[i].charge ) + { + p ++; + if ( isdigit( UCINT *p ) ) + { + atom[i].charge *= (S_CHAR)(strtol( p, &q, 10 ) & CHAR_MASK); + p = q; + } + } + + /* radical */ + if ( *p == '.' ) + { + p ++; + if ( isdigit( UCINT *p ) ) + { + atom[i].radical = (S_CHAR)strtol( p, &q, 10 ); + p = q; + } + } + + /* isotopic mass */ + if ( *p == 'i' ) + { + p ++; + if ( isdigit( UCINT *p ) ) + { + int mw = strtol( p, &q, 10 ); + p = q; + + atom[i].isotopic_mass = mw; + } + } + + /* parity */ + switch( *p ) + { + case 'o': + parity = INCHI_PARITY_ODD; + p ++; + break; + case 'e': + parity = INCHI_PARITY_EVEN; + p ++; + break; + case 'u': + parity = INCHI_PARITY_UNKNOWN; + p ++; + break; + case '?': + parity = INCHI_PARITY_UNDEFINED; + p ++; + break; + default: + parity = 0; + break; + } + if ( parity ) + { + atom_stereo0D[len_stereo0D].central_atom = i; + atom_stereo0D[len_stereo0D].parity = parity; + atom_stereo0D[len_stereo0D].type = INCHI_StereoType_Tetrahedral; + len_stereo0D ++; + } + + /* isotopic h, d, t */ + for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) + { + if ( *p == szIsoH[k] ) + { + NUM_ISO_Hk(atom,i,k) = 1; + p ++; + if ( isdigit( UCINT *p ) ) + { + NUM_ISO_Hk(atom,i,k) = (char)strtol( p, &q, 10 ); + p = q; + } + } + } + + i ++; + } + + if ( !bItemIsOver || p - szLine != res || i != num_atoms ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong number of atoms"); + goto bypass_end_of_INChI; + } + + /********************** bonds xml ****************************/ + + res = inchi_ios_gets( szLine, sizeof(szLine)-1, inp_file, &bTooLongLine ); + if ( res <= 0 ) + { + num_atoms = 0; /* no data */ + goto bypass_end_of_INChI; + } + if ( memcmp(szLine, sStructRevXmlRevBn, sizeof(sStructRevXmlRevBn)-1) ) + { + bHeaderRead = 0; /* invalid reversibility info; look for another header */ + continue; + } + + /* read (the head of) the xml bonds line */ + + res = inchi_ios_gets( szLine, sizeof(szLine)-1, inp_file, &bTooLongLine ); + + if ( res <= 0 ) + { + num_atoms = INCHI_INP_ERROR_RET; /* was 0; error: no data -- eof? */ + *err = INCHI_INP_ERROR_ERR; + goto bypass_end_of_INChI; + } + + i = 1; + bItemIsOver = 0; + res2 = bTooLongLine2 = -1; + p = szLine; + + if ( !memcmp(szLine, sStructRevXmlRevBnEnd, sizeof(sStructRevXmlRevBnEnd)-1) ) + { + /* empty bonds section */ + res = 0; + bItemIsOver = 1; + } + + /* read all bonds (xml), starting from atom 1 (not 0) */ + + while ( i < num_atoms ) + { + pos = p - szLine; + if ( !bItemIsOver && + (int)sizeof(szLine)-res + pos > (int)sizeof(szNextLine) ) + { + + /* load next line if possible */ + + res2 = inchi_ios_gets( szNextLine, sizeof(szNextLine)-1, inp_file, &bTooLongLine2 ); + + if ( res2 > 0 && memcmp(szNextLine, sStructRevXmlRevBnEnd, sizeof(sStructRevXmlRevBnEnd)-1) ) + { + if ( pos ) + { + res -= pos; /* number of chars left to process in szLine */ + memmove( szLine, p, res*sizeof(szLine[0]) ); /* move them to the start of the line */ + } + memcpy( szLine+res, szNextLine, (res2+1)*sizeof(szNextLine[0]) ); + res += res2; + szLine[res] = '\0'; + bTooLongLine = bTooLongLine2; + p = szLine; + } + else + bItemIsOver = 1; + } + + if ( i >= num_atoms ) + break; + + /* bond, first char */ + + if ( *p == ';' ) + { + p ++; + i ++; + continue; + } + + if ( !isalpha( UCINT *p ) ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error in input data */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong bonds data"); + goto bypass_end_of_INChI; + } + + bond_char = *p ++; + + /* bond parity */ + + switch( *p ) + { + case '-': + bond_parity = INCHI_PARITY_ODD; + p ++; + break; + case '+': + bond_parity = INCHI_PARITY_EVEN; + p ++; + break; + case 'u': + bond_parity = INCHI_PARITY_UNKNOWN; + p ++; + break; + case '?': + bond_parity = INCHI_PARITY_UNDEFINED; + p ++; + break; + default: + bond_parity = 0; + break; + } + + if ( bond_parity ) + { + switch( *p ) + { + case '-': + bond_parityNM = INCHI_PARITY_ODD; + p ++; + break; + case '+': + bond_parityNM = INCHI_PARITY_EVEN; + p ++; + break; + case 'u': + bond_parityNM = INCHI_PARITY_UNKNOWN; + p ++; + break; + case '?': + bond_parityNM = INCHI_PARITY_UNDEFINED; + p ++; + break; + default: + bond_parityNM = 0; + break; + } + } + else + bond_parityNM = 0; + + + /* neighbor of the current atom */ + + if ( !isdigit( UCINT *p ) ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error in input data */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong bonds data"); + goto bypass_end_of_INChI; + } + + neigh = (int)strtol( p, &q, 10 )-1; + + if ( i >= num_atoms || neigh >= num_atoms ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error in input data */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Bond to nonexistent atom"); + goto bypass_end_of_INChI; + } + p = q; + bond_stereo1 = bond_stereo2 = 0; + + /* bond type & 2D stereo */ + switch( bond_char ) + { + case 'v': + bond_type = INCHI_BOND_TYPE_SINGLE; + bond_stereo1 = INCHI_BOND_STEREO_SINGLE_1EITHER; + bond_stereo2 = INCHI_BOND_STEREO_SINGLE_2EITHER; + break; + case 'V': + bond_type = INCHI_BOND_TYPE_SINGLE; + bond_stereo1 = INCHI_BOND_STEREO_SINGLE_2EITHER; + bond_stereo2 = INCHI_BOND_STEREO_SINGLE_1EITHER; + break; + case 'w': + bond_type = INCHI_BOND_TYPE_DOUBLE; + bond_stereo1 = + bond_stereo2 = INCHI_BOND_STEREO_DOUBLE_EITHER; + break; + case 's': + bond_type = INCHI_BOND_TYPE_SINGLE; + break; + case 'd': + bond_type = INCHI_BOND_TYPE_DOUBLE; + break; + case 't': + bond_type = INCHI_BOND_TYPE_TRIPLE; + break; + case 'a': + bond_type = INCHI_BOND_TYPE_ALTERN; + break; + case 'p': + bond_type = INCHI_BOND_TYPE_SINGLE; + bond_stereo1 = INCHI_BOND_STEREO_SINGLE_1UP; + bond_stereo2 = INCHI_BOND_STEREO_SINGLE_2UP; + break; + case 'P': + bond_type = INCHI_BOND_TYPE_SINGLE; + bond_stereo1 = INCHI_BOND_STEREO_SINGLE_2UP; + bond_stereo2 = INCHI_BOND_STEREO_SINGLE_1UP; + break; + case 'n': + bond_type = INCHI_BOND_TYPE_SINGLE; + bond_stereo1 = INCHI_BOND_STEREO_SINGLE_1DOWN; + bond_stereo2 = INCHI_BOND_STEREO_SINGLE_2DOWN; + break; + case 'N': + bond_type = INCHI_BOND_TYPE_SINGLE; + bond_stereo1 = INCHI_BOND_STEREO_SINGLE_2DOWN; + bond_stereo2 = INCHI_BOND_STEREO_SINGLE_1DOWN; + break; + default: + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong bond type"); + goto bypass_end_of_INChI; + } + + k = AT_NUM_BONDS(atom[i]) ++; + atom[i].bond_type[k] = bond_type; + atom[i].bond_stereo[k] = bond_stereo1; + atom[i].neighbor[k] = (ATOM_NUMBER)neigh; + + k2 = AT_NUM_BONDS(atom[neigh]) ++; + atom[neigh].bond_type[k2] = bond_type; + atom[neigh].bond_stereo[k2] = bond_stereo2; + atom[neigh].neighbor[k2] = (ATOM_NUMBER)i; + + bond_parity |= (bond_parityNM << SB_PARITY_SHFT); + + if ( bond_parity ) + { + if ( max_len_stereo0D <= len_stereo0D ) + { + /* realloc atom_Stereo0D */ + + inchi_Stereo0D *new_atom_stereo0D = CreateInchi_Stereo0D( max_len_stereo0D+num_atoms ); + + if ( !new_atom_stereo0D ) + { + num_atoms = INCHI_INP_FATAL_RET; /* fatal error: cannot allocate */ + *err = INCHI_INP_FATAL_ERR; + TREAT_ERR (*err, 0, "Out of RAM"); + goto bypass_end_of_INChI; + } + memcpy( new_atom_stereo0D, atom_stereo0D, len_stereo0D * sizeof(*atom_stereo0D) ); + FreeInchi_Stereo0D( &atom_stereo0D ); + atom_stereo0D = new_atom_stereo0D; + max_len_stereo0D += num_atoms; + } + /* (a) i may be allene endpoint and neigh = allene middle point or + (b) i may be allene middle point and neigh = allene endpoint + !!!!! CURRENTLY ONLY (b) IS ALLOWED !!!!! + */ + atom_stereo0D[len_stereo0D].neighbor[1] = neigh; /* neigh < i */ + atom_stereo0D[len_stereo0D].neighbor[2] = i; + atom_stereo0D[len_stereo0D].parity = bond_parity; + atom_stereo0D[len_stereo0D].type = INCHI_StereoType_DoubleBond; /* incl allenes & cumulenes */ + len_stereo0D ++; + } + } + + if ( !bItemIsOver || p - szLine != res || i != num_atoms ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error in input data */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong number of bonds"); + goto bypass_end_of_INChI; + } + + /********************** coordinates xml ****************************/ + + pszCoord = (MOL_COORD*) inchi_malloc(inchi_max(num_atoms,1) * sizeof(MOL_COORD)); + + if ( pszCoord ) + { + memset( pszCoord, ' ', inchi_max(num_atoms,1) * sizeof(MOL_COORD)); + res = inchi_ios_gets( szLine, sizeof(szLine)-1, inp_file, &bTooLongLine ); + + if ( res <= 0 || + /* compare the header */ + memcmp(szLine, sStructRevXmlRevXYZ, sizeof(sStructRevXmlRevXYZ)-1) || + /* read (the head of) the coordinates (xml) line */ + 0 >= (res = inchi_ios_gets( szLine, sizeof(szLine)-1, inp_file, &bTooLongLine ))) + { + num_atoms = INCHI_INP_ERROR_RET; /* error in input data: atoms, bonds & coord must be present together */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Missing atom coordinates data"); + goto bypass_end_of_INChI; + } + + i = 0; + bItemIsOver = 0; + res2 = bTooLongLine2 = -1; + p = szLine; + if ( !memcmp(szLine, sStructRevXmlRevXYZEnd, sizeof(sStructRevXmlRevXYZEnd)-1) ) + { + /* empty bonds section */ + res = 0; + bItemIsOver = 1; + } + + /* read all coordinates (xml), starting from atom 1 (not 0) */ + + while ( i < num_atoms ) + { + pos = p - szLine; + + if ( !bItemIsOver && + (int)sizeof(szLine)-res + pos > (int)sizeof(szNextLine) ) + { + + /* load next line if possible */ + + res2 = inchi_ios_gets( szNextLine, sizeof(szNextLine)-1, inp_file, &bTooLongLine2 ); + + if ( res2 > 0 && memcmp(szNextLine, sStructRevXmlRevXYZEnd, sizeof(sStructRevXmlRevXYZEnd)-1) ) + { + if ( pos ) + { + res -= pos; /* number of chars left to process in szLine */ + memmove( szLine, p, res*sizeof(szLine[0]) ); /* move them to the start of the line */ + } + memcpy( szLine+res, szNextLine, (res2+1)*sizeof(szNextLine[0]) ); + res += res2; + szLine[res] = '\0'; + bTooLongLine = bTooLongLine2; + p = szLine; + } + else + bItemIsOver = 1; + } + + /* coord, first char */ + + if ( *p == ';' ) + { + for ( k = 0; k < NUM_COORD; k ++ ) + { + pszCoord[i][LEN_COORD*k + 4] = '0'; + } + p ++; + i ++; + continue; + } + + for ( k = 0; k < 3; k ++ ) + { + double xyz; + bNonZeroXYZ = 0; + if ( *p == ';' ) + { + pszCoord[i][LEN_COORD*k + 4] = '0'; + xyz = 0.0; + } + else if ( *p == ',' ) + { + /* empty */ + pszCoord[i][LEN_COORD*k + 4] = '0'; + xyz = 0.0; + p ++; + } + else + { + xyz = strtod( p, &q ); + bNonZeroXYZ = fabs(xyz) > MIN_BOND_LENGTH; + if ( q != NULL ) + { + memcpy( pszCoord[i]+LEN_COORD*k, p, q-p ); + if ( *q == ',' ) + q ++; + p = q; + } + else + pszCoord[i][LEN_COORD*k + 4] = '0'; + } + + switch( k ) + { + case 0: + atom[i].x = xyz; + b2D |= bNonZeroXYZ; + break; + case 1: + atom[i].y = xyz; + b2D |= bNonZeroXYZ; + break; + case 2: + b3D |= bNonZeroXYZ; + atom[i].z = xyz; + break; + } + } + + if ( *p == ';' ) + { + p ++; /* end of this triple of coordinates */ + i ++; + } + else + { + num_atoms = INCHI_INP_ERROR_RET; /* error in input data: atoms, bonds & coord must be present together */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong atom coordinates data"); + goto bypass_end_of_INChI; + } + } + + if ( !bItemIsOver || p - szLine != res || i != num_atoms ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error in input data: atoms, bonds & coord must be present together */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong number of coordinates"); + goto bypass_end_of_INChI; + } + } + else + { + /* allocation failed */ + num_atoms = INCHI_INP_FATAL_RET; + *err = INCHI_INP_FATAL_ERR; + TREAT_ERR (*err, 0, "Out of RAM"); + goto bypass_end_of_INChI; + } + + /* set special valences and implicit H (xml) */ + + b23D = b2D | b3D; + b2D = b3D = 0; + + if ( at ) + { + if ( !*at ) + { + int a1, a2, n1, n2, valence; + int chem_bonds_valence; + int nX=0, nY=0, nZ=0, nXYZ; + *at = atom; + + /* special valences */ + + for ( bNonMetal = 0; bNonMetal < 1 /*2*/; bNonMetal ++ ) + { + for ( a1 = 0; a1 < num_atoms; a1 ++ ) + { + int num_bond_type[MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE + 1]; + + memset( num_bond_type, 0, sizeof(num_bond_type) ); + + valence = AT_BONDS_VAL(atom, a1); /* save atom valence if available */ + AT_BONDS_VAL(atom, a1) = 0; + + nX = nY = nZ = 0; + + for ( n1 = 0; n1 < AT_NUM_BONDS(atom[a1]); n1 ++ ) + { + bond_type = atom[a1].bond_type[n1] - MIN_INPUT_BOND_TYPE; + if ( bond_type < 0 || bond_type > MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE ) + { + bond_type = 0; /* cannot happen */ + TREAT_ERR (*err, 0, "Unknown bond type in InChI aux assigned as a single bond"); + } + + num_bond_type[ bond_type ] ++; + nNumBonds ++; + + if ( b23D ) + { + neigh = atom[a1].neighbor[n1]; + nX |= (fabs(atom[a1].x - atom[neigh].x) > MIN_BOND_LENGTH); + nY |= (fabs(atom[a1].y - atom[neigh].y) > MIN_BOND_LENGTH); + nZ |= (fabs(atom[a1].z - atom[neigh].z) > MIN_BOND_LENGTH); + } + } + + chem_bonds_valence = 0; + for ( n1 = 0; MIN_INPUT_BOND_TYPE + n1 <= 3 && MIN_INPUT_BOND_TYPE + n1 <= MAX_INPUT_BOND_TYPE; n1 ++ ) + { + chem_bonds_valence += (MIN_INPUT_BOND_TYPE + n1) * num_bond_type[n1]; + } + + if ( MIN_INPUT_BOND_TYPE <= INCHI_BOND_TYPE_ALTERN && + INCHI_BOND_TYPE_ALTERN <= MAX_INPUT_BOND_TYPE && + ( n2 = num_bond_type[INCHI_BOND_TYPE_ALTERN-MIN_INPUT_BOND_TYPE] ) ) + { + + /* accept input aromatic bonds for now */ + + switch ( n2 ) + { + case 2: + chem_bonds_valence += 3; /* =A- */ + break; + case 3: + chem_bonds_valence += 4; /* =A< */ + break; + default: + /* if 1 or >= 4 aromatic bonds then replace such bonds with single bonds */ + for ( n1 = 0; n1 < AT_NUM_BONDS(atom[a1]); n1 ++ ) + { + if ( atom[a1].bond_type[n1] == INCHI_BOND_TYPE_ALTERN ) + { + ATOM_NUMBER *p1; + a2 = atom[a1].neighbor[n1]; + p1 = IN_NEIGH_LIST( atom[a2].neighbor, (ATOM_NUMBER)a1, AT_NUM_BONDS(atom[a2]) ); + if ( p1 ) + { + atom[a1].bond_type[n1] = + atom[a2].bond_type[p1-atom[a2].neighbor] = INCHI_BOND_TYPE_SINGLE; + } + else + { + *err = -2; /* Program error */ + TREAT_ERR (*err, 0, "Program error interpreting InChI aux"); + num_atoms = 0; + goto bypass_end_of_INChI; /* no structure */ + } + } + } + chem_bonds_valence += n2; + *err |= 32; /* Unrecognized aromatic bond(s) replaced with single */ + TREAT_ERR (*err, 0, "Atom has 1 or more than 3 aromatic bonds"); + break; + } + } + + /************************************************************************************* + * + * Set number of hydrogen atoms + */ + { + int num_iso_H; + num_iso_H = atom[a1].num_iso_H[1] + atom[a1].num_iso_H[2] + atom[a1].num_iso_H[3]; + if ( valence == ISOLATED_ATOM ) { + atom[a1].num_iso_H[0] = 0; + } else + if ( valence && valence >= chem_bonds_valence ) { + atom[a1].num_iso_H[0] = valence - chem_bonds_valence; + } else + if ( valence || bDoNotAddH ) { + atom[a1].num_iso_H[0] = 0; + } else + if ( !bDoNotAddH ) { + atom[a1].num_iso_H[0] = -1; /* auto add H */ + } + } + } + } + + nNumBonds /= 2; + + if ( b23D && nNumBonds ) + { + nXYZ = nX+nY+nZ; + b2D = (nXYZ > 0); + b3D = (nXYZ == 3); + *num_dimensions = b3D? 3 : b2D? 2 : 0; + *num_bonds = nNumBonds; + } + + /*======= 0D parities =================================*/ + if ( len_stereo0D > 0 && atom_stereo0D && stereo0D ) + { + *stereo0D = atom_stereo0D; + *num_stereo0D = len_stereo0D; + } else { + FreeInchi_Stereo0D( &atom_stereo0D ); + *num_stereo0D = len_stereo0D = 0; + } + + for ( i = 0; i < len_stereo0D; i ++ ) + { + ATOM_NUMBER *p1, *p2; + int sb_ord_from_a1 = -1, sb_ord_from_a2 = -1, bEnd1 = 0, bEnd2 = 0; + switch( atom_stereo0D[i].type ) + { + + case INCHI_StereoType_Tetrahedral: + a1 = atom_stereo0D[i].central_atom; + if ( atom_stereo0D[i].parity && + (AT_NUM_BONDS(atom[a1]) == 3 || AT_NUM_BONDS(atom[a1]) == 4) ) + { + int ii, kk = 0; + if ( AT_NUM_BONDS(atom[a1]) == 3 ) + { + atom_stereo0D[i].neighbor[kk++] = a1; + } + for ( ii = 0; ii < AT_NUM_BONDS(atom[a1]); ii ++ ) + { + atom_stereo0D[i].neighbor[kk++] = atom[a1].neighbor[ii]; + } + } + + break; + + case INCHI_StereoType_DoubleBond: + +#define MAX_CHAIN_LEN 20 + + a1 = atom_stereo0D[i].neighbor[1]; + a2 = atom_stereo0D[i].neighbor[2]; + p1 = IN_NEIGH_LIST( atom[a1].neighbor, (ATOM_NUMBER)a2, AT_NUM_BONDS(atom[a1]) ); + p2 = IN_NEIGH_LIST( atom[a2].neighbor, (ATOM_NUMBER)a1, AT_NUM_BONDS(atom[a2]) ); + + if ( !p1 || !p2 ) + { + atom_stereo0D[i].type = INCHI_StereoType_None; + atom_stereo0D[i].central_atom = NO_ATOM; + atom_stereo0D[i].neighbor[0] = + atom_stereo0D[i].neighbor[3] = -1; + *err |= 64; /* Error in cumulene stereo */ + TREAT_ERR (*err, 0, "0D stereobond not recognized"); + break; + } + + /* streobond, allene, or cumulene */ + + sb_ord_from_a1 = p1 - atom[a1].neighbor; + sb_ord_from_a2 = p2 - atom[a2].neighbor; + + if ( AT_NUM_BONDS(atom[a1]) == 2 && + atom[a1].bond_type[0] + atom[a1].bond_type[1] == 2*INCHI_BOND_TYPE_DOUBLE && + 0 == inchi_NUMH2(atom, a1) && + (AT_NUM_BONDS(atom[a2]) != 2 || + atom[a2].bond_type[0] + atom[a2].bond_type[1] != 2*INCHI_BOND_TYPE_DOUBLE ) ) + { + bEnd2 = 1; /* a2 is the end-atom, a1 is middle atom */ + } + if ( AT_NUM_BONDS(atom[a2]) == 2 && + atom[a2].bond_type[0] + atom[a2].bond_type[1] == 2*INCHI_BOND_TYPE_DOUBLE && + 0 == inchi_NUMH2(atom, a2) && + (AT_NUM_BONDS(atom[a1]) != 2 || + atom[a1].bond_type[0] + atom[a1].bond_type[1] != 2*INCHI_BOND_TYPE_DOUBLE ) ) + { + bEnd1 = 1; /* a1 is the end-atom, a2 is middle atom */ + } + + if ( bEnd2 + bEnd1 == 1 ) + { + /* allene or cumulene */ + ATOM_NUMBER chain[MAX_CHAIN_LEN+1], prev, cur, next; + if ( bEnd2 && !bEnd1 ) + { + cur = a1; + a1 = a2; + a2 = cur; + sb_ord_from_a1 = sb_ord_from_a2; + } + + sb_ord_from_a2 = -1; + cur = a1; + next = a2; + len = 0; + chain[len++] = cur; + chain[len++] = next; + + while ( len < MAX_CHAIN_LEN ) + { + /* arbitrary very high upper limit to prevent infinite loop */ + + prev = cur; + cur = next; + /* follow double bond path && avoid going back */ + if ( AT_NUM_BONDS(atom[cur]) == 2 && + atom[cur].bond_type[0]+atom[cur].bond_type[1] == 2*INCHI_BOND_TYPE_DOUBLE && + 0 == inchi_NUMH2(atom, cur) ) + { + next = atom[cur].neighbor[atom[cur].neighbor[0] == prev]; + chain[len++] = next; + } else + break; + } + + if ( len > 2 && + (p2 = IN_NEIGH_LIST( atom[cur].neighbor, (ATOM_NUMBER)prev, AT_NUM_BONDS(atom[cur]))) ) + { + sb_ord_from_a2 = p2 - atom[cur].neighbor; + a2 = cur; + + /* by design we need to pick up the first non-stereo-bond-neighbor as "sn"-atom */ + + atom_stereo0D[i].neighbor[0] = atom[a1].neighbor[sb_ord_from_a1 == 0]; + atom_stereo0D[i].neighbor[1] = a1; + atom_stereo0D[i].neighbor[2] = a2; + atom_stereo0D[i].neighbor[3] = atom[a2].neighbor[sb_ord_from_a2 == 0]; + + if ( len % 2 ) + { + atom_stereo0D[i].central_atom = chain[len/2]; + atom_stereo0D[i].type = INCHI_StereoType_Allene; + } + else + atom_stereo0D[i].central_atom = NO_ATOM; + } else + { + /* error */ + atom_stereo0D[i].type = INCHI_StereoType_None; + atom_stereo0D[i].central_atom = NO_ATOM; + atom_stereo0D[i].neighbor[0] = + atom_stereo0D[i].neighbor[3] = -1; + *err |= 64; /* Error in cumulene stereo */ + TREAT_ERR (*err, 0, "Cumulene stereo not recognized (0D)"); + } + +#undef MAX_CHAIN_LEN + } + else + { + + /****** a normal possibly stereogenic bond -- not an allene or cumulene *******/ + /* by design we need to pick up the first non-stereo-bond-neighbor as "sn"-atom */ + + sb_ord_from_a1 = p1 - atom[a1].neighbor; + sb_ord_from_a2 = p2 - atom[a2].neighbor; + atom_stereo0D[i].neighbor[0] = atom[a1].neighbor[p1 == atom[a1].neighbor]; + atom_stereo0D[i].neighbor[3] = atom[a2].neighbor[p2 == atom[a2].neighbor]; + atom_stereo0D[i].central_atom = NO_ATOM; + } + + if ( atom_stereo0D[i].type != INCHI_StereoType_None && + sb_ord_from_a1 >= 0 && sb_ord_from_a2 >= 0 && + ATOM_PARITY_WELL_DEF( SB_PARITY_2(atom_stereo0D[i].parity) ) ) + { + + /* Detected well-defined disconnected stereo + * locate first non-metal neighbors */ + + int a, n, j, /* k,*/ sb_ord, cur_neigh, min_neigh; + for ( k = 0; k < 2; k ++ ) + { + a = k? atom_stereo0D[i].neighbor[2] : atom_stereo0D[i].neighbor[1]; + sb_ord = k? sb_ord_from_a2 : sb_ord_from_a1; + min_neigh = num_atoms; + for ( n = j = 0; j < AT_NUM_BONDS(atom[a]); j ++ ) + { + cur_neigh = atom[a].neighbor[j]; + if ( j != sb_ord && !IS_METAL_ATOM(atom, cur_neigh) ) + { + min_neigh = inchi_min( cur_neigh, min_neigh ); + } + } + if ( min_neigh < num_atoms ) + atom_stereo0D[i].neighbor[k?3:0] = min_neigh; + else + TREAT_ERR (*err, 0, "Cannot find non-metal stereobond neighor (0D)"); + } + } + + break; + } + } + + /* end of 0D parities extraction */ +/*exit_cycle:;*/ + } + + if ( pInpAtomFlags ) + { + /* save chirality flag */ + *pInpAtomFlags |= InpAtomFlags; + } + } + else if ( atom ) + { + inchi_free( atom ); + atom = NULL; + } + + if ( pszCoord ) + { + inchi_free( pszCoord ); + } + goto bypass_end_of_INChI; + /*return num_atoms;*/ + } + } + + if ( atom_stereo0D ) + FreeInchi_Stereo0D( &atom_stereo0D ); + + /* end of struct. reading cycle, code never used? */ + + if ( res <= 0 ) + { + if ( *err == INCHI_INP_ERROR_ERR ) + return num_atoms; + + *err = INCHI_INP_EOF_ERR; + return INCHI_INP_EOF_RET; /* no more data */ + } + +bypass_end_of_INChI: + + /* cleanup */ + + if ( num_atoms == INCHI_INP_ERROR_RET && atom_stereo0D ) + { + if ( stereo0D && *stereo0D == atom_stereo0D ) + { + *stereo0D = NULL; + *num_stereo0D = 0; + } + FreeInchi_Stereo0D( &atom_stereo0D ); + } + + if ( !memcmp(szLine, sStructHdrXmlEnd, sizeof(sStructHdrXmlEnd)-1) ) + num_struct --; + + if ( !memcmp(szLine, sStructHdrXml, sizeof(sStructHdrXml)-1) ) + num_struct ++; + + while ( num_struct > 0 && 0 < inchi_ios_gets( szLine, sizeof(szLine)-1, inp_file, &bTooLongLine ) ) { + if ( !memcmp(szLine, sStructHdrXmlEnd, sizeof(sStructHdrXmlEnd)-1) ) + num_struct --; + else + if ( !memcmp(szLine, sStructHdrXml, sizeof(sStructHdrXml)-1) ) + num_struct ++; + } + return num_atoms; + } + + return num_atoms; +} diff --git a/INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll_b.h b/INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll_b.h new file mode 100644 index 0000000..84d4216 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll_b.h @@ -0,0 +1,81 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef __INCHI_DLL_B_H__ +#define __INCHI_DLL_B_H__ + +#ifndef AB_PARITY_UNKN +#define AB_PARITY_UNKN 3 /* 3 => user marked as unknown parity */ +#endif +#ifndef AB_PARITY_UNDF +#define AB_PARITY_UNDF 4 /* 4 => parity cannot be defined because of symmetry or not well defined geometry */ +#endif + + +#define MOL2INCHI_NO_RAM 1001 +#define MOL2INCHI_BAD_COMMAND_LINE 1002 + + +void FreeInchi_Stereo0D( inchi_Stereo0D **stereo0D ); +void FreeInchi_Atom( inchi_Atom **at ); +inchi_Atom *CreateInchiAtom( int num_atoms ); +inchi_Stereo0D *CreateInchi_Stereo0D( int num_stereo0D ); +void FreeInchi_Input( inchi_Input *inp_at_data ); +S_SHORT *is_in_the_slist( S_SHORT *pathAtom, S_SHORT nNextAtom, int nPathLen ); +int is_element_a_metal( char szEl[] ); + +int InchiToInchiAtom( INCHI_IOSTREAM *inp_molfile, + inchi_Stereo0D **stereo0D, + int *num_stereo0D, + int bDoNotAddH, + int vABParityUnknown, + INPUT_TYPE nInputType, + inchi_Atom **at, + int max_num_at, + int *num_dimensions, + int *num_bonds, + char *pSdfLabel, + char *pSdfValue, + long *Id, + INCHI_MODE *pInpAtomFlags, + int *err, + char *pStrErr ); + + +#endif /* __INCHI_DLL_B_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/inchi_dll_main.c b/INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll_main.c similarity index 52% rename from INCHI-1-SRC/INCHI_API/inchi_dll/inchi_dll_main.c rename to INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll_main.c index 4e03061..44bb0ce 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/inchi_dll_main.c +++ b/INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll_main.c @@ -1,55 +1,50 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -/* inchi_dll_main.c : Defines the entry point for the DLL application. */ - -#if defined(_WIN32) && defined(_USRDLL) && defined(_DEBUG) && !(defined(__STDC__) && __STDC__ == 1) -#include "inchi_dll_main.h" -int INCHI_DLLMAIN_TYPE DllMain( HANDLE hModule, - DWORD ul_reason_for_call, - LPVOID lpReserved - ) -{ - return TRUE; -} -#else -int dummy_inchi_dll_main=0; /* avoid empty module to keep C compiler happy */ -#endif - +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +/* inchi_dll_main.c : Defines the entry point for the DLL application. */ + +#if defined(_WIN32) && defined(_USRDLL) && defined(_DEBUG) && !(defined(__STDC__) && __STDC__ == 1) +#include "inchi_dll_main.h" +int INCHI_DLLMAIN_TYPE DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) +{ + return TRUE; +} +#else +int dummy_inchi_dll_main=0; /* avoid empty module to keep C compiler happy */ +#endif diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/inchi_dll_main.h b/INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll_main.h similarity index 58% rename from INCHI-1-SRC/INCHI_API/inchi_dll/inchi_dll_main.h rename to INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll_main.h index 15125b3..72c168a 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/inchi_dll_main.h +++ b/INCHI-1-SRC/INCHI_API/libinchi/src/inchi_dll_main.h @@ -1,62 +1,61 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHI_DLL_MAIN_H__ -#define __INCHI_DLL_MAIN_H__ - -#if _MSC_VER > 1000 -#pragma once -#endif /* _MSC_VER > 1000 */ - -#if defined(_WIN32) && defined(_MSC_VER) && defined(_USRDLL) - -/*#define WIN32_LEAN_AND_MEAN */ /* Exclude rarely-used stuff from Windows headers */ -#include - -#define INCHI_DLLMAIN_TYPE APIENTRY - -#else /* not a Win32 DLL under MS VC++ */ - -#define INCHI_DLLMAIN_TYPE - -#endif - - -#endif /* __INCHI_DLL_MAIN_H__ */ \ No newline at end of file +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef __INCHI_DLL_MAIN_H__ +#define __INCHI_DLL_MAIN_H__ + +#if _MSC_VER > 1000 +#pragma once +#endif /* _MSC_VER > 1000 */ + +#if defined(_WIN32) && defined(_MSC_VER) && defined(_USRDLL) + +/*#define WIN32_LEAN_AND_MEAN */ /* Exclude rarely-used stuff from Windows headers */ +#include + +#define INCHI_DLLMAIN_TYPE APIENTRY + +#else /* not a Win32 DLL under MS VC++ */ + +#define INCHI_DLLMAIN_TYPE + +#endif + + +#endif /* __INCHI_DLL_MAIN_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/libinchi/src/ixa/ixa_builder.c b/INCHI-1-SRC/INCHI_API/libinchi/src/ixa/ixa_builder.c new file mode 100644 index 0000000..668d8ff --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/libinchi/src/ixa/ixa_builder.c @@ -0,0 +1,1219 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include "../../../../INCHI_BASE/src/mode.h" +#include "../../../../INCHI_BASE/src/inchi_api.h" +#include "../../../../INCHI_BASE/src/inpdef.h" +#include "ixa_status.h" +#include "ixa_mol.h" +#include +#include +#include + + +#ifdef _WIN32 +#define OPTION_PREFIX "/" +#else +#define OPTION_PREFIX "-" +#endif + + +typedef struct +{ + AT_NUM num_atoms; /* number of atoms in the structure */ + inchi_Atom* atom; /* array of num_atoms elements */ + + AT_NUM num_stereo0D; /* number of 0D stereo elements */ + inchi_Stereo0D* stereo0D; /* array of num_stereo0D 0D stereo elements or NULL */ + + int chiral; /* 1 if the molecule is chiral, 0 if it is not. */ + + inchi_Input_Polymer *polymer; /* later extension */ + inchi_Input_V3000 *v3000; /* later extension */ +} BuilderMolecule; + + +typedef struct +{ + BuilderMolecule molecule; + + int option_Wnumber; + IXA_INCHIBUILDER_STEREOOPTION option_stereo; + IXA_BOOL option_NEWPSOFF; + IXA_BOOL option_DoNotAddH; + IXA_BOOL option_SUU; + IXA_BOOL option_SLUUD; + IXA_BOOL option_FixedH; + IXA_BOOL option_RecMet; + IXA_BOOL option_KET; + IXA_BOOL option_15T; + IXA_BOOL option_AuxNone; + IXA_BOOL option_WarnOnEmptyStructure; + IXA_BOOL option_LargeMolecules; + IXA_BOOL option_SaveOpt; + + char* inchi; + char* auxinfo; + char* log; + + IXA_BOOL dirty; +} INCHIBUILDER; + + + +static int GetSingleStereoCode(IXA_STATUS_HANDLE hStatus, + IXA_BOND_WEDGE direction, + IXA_BOND_WEDGE reverse_direction) +{ + switch (direction) + { + case IXA_BOND_WEDGE_NONE: + switch (reverse_direction) + { + case IXA_BOND_WEDGE_NONE: + return INCHI_BOND_STEREO_NONE; + case IXA_BOND_WEDGE_UP: + return INCHI_BOND_STEREO_SINGLE_2UP; + case IXA_BOND_WEDGE_DOWN: + return INCHI_BOND_STEREO_SINGLE_2DOWN; + case IXA_BOND_WEDGE_EITHER: + return INCHI_BOND_STEREO_SINGLE_2EITHER; + default: + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Illegal bond detected"); + return INCHI_BOND_STEREO_NONE; + } + + case IXA_BOND_WEDGE_UP: + return INCHI_BOND_STEREO_SINGLE_1UP; + case IXA_BOND_WEDGE_DOWN: + return INCHI_BOND_STEREO_SINGLE_1DOWN; + case IXA_BOND_WEDGE_EITHER: + return INCHI_BOND_STEREO_SINGLE_1EITHER; + default: + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Illegal bond detected"); + return INCHI_BOND_STEREO_NONE; + } +} + + +static int GetDoubleStereoCode(IXA_STATUS_HANDLE hStatus, + IXA_DBLBOND_CONFIG vConfig) +{ + switch (vConfig) + { + case IXA_DBLBOND_CONFIG_PERCEIVE: + return INCHI_BOND_STEREO_NONE; + case IXA_DBLBOND_CONFIG_EITHER: + return INCHI_BOND_STEREO_DOUBLE_EITHER; + default: + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Illegal bond detected"); + return INCHI_BOND_STEREO_NONE; + } +} + + +static INCHIBUILDER* BUILDER_Unpack(IXA_STATUS_HANDLE hStatus, + IXA_INCHIBUILDER_HANDLE hBuilder) +{ + if (!hBuilder) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Illegal builder handle detected"); + return NULL; + } + + return (INCHIBUILDER*)hBuilder; +} + + +static IXA_INCHIBUILDER_HANDLE BUILDER_Pack(INCHIBUILDER* pBuilder) +{ + return (IXA_INCHIBUILDER_HANDLE)pBuilder; +} + + +static void TranslateTetrahedralVertex(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_STEREOID vSourceStereo, + inchi_Stereo0D* pTargetStereo, + int vVertexIndex) +{ + IXA_ATOMID vertex = IXA_MOL_GetStereoVertex(hStatus, hMolecule, vSourceStereo, vVertexIndex); + if (IXA_STATUS_HasError(hStatus)) return; + if (vertex == IXA_ATOMID_IMPLICIT_H) + { + pTargetStereo->neighbor[vVertexIndex] = pTargetStereo->central_atom; + } + else + { + pTargetStereo->neighbor[vVertexIndex] = IXA_MOL_GetAtomIndex(hStatus, hMolecule, vertex); + if (IXA_STATUS_HasError(hStatus)) return; + } +} + + +static void ExtendAllene(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vCentralAtom, + IXA_ATOMID* pAtom1, + IXA_ATOMID* pAtom2) +{ + int bond_count; + IXA_BONDID bond; + IXA_BOND_TYPE bond_type; + + bond_count = IXA_MOL_GetAtomNumBonds(hStatus, hMolecule, vCentralAtom); + if (IXA_STATUS_HasError(hStatus)) return; + if (bond_count != 2) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Stereo centre is not allene"); + return; + } + + bond = IXA_MOL_GetAtomBond(hStatus, hMolecule, vCentralAtom, 0); + if (IXA_STATUS_HasError(hStatus)) return; + bond_type = IXA_MOL_GetBondType(hStatus, hMolecule, bond); + if (IXA_STATUS_HasError(hStatus)) return; + if (bond_type != IXA_BOND_TYPE_DOUBLE) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, + "Internal bonds of allenes must be double"); + return; + } + *pAtom1 = MOL_GetBondOtherAtom(hStatus, hMolecule, bond, vCentralAtom); + if (IXA_STATUS_HasError(hStatus)) return; + + bond = IXA_MOL_GetAtomBond(hStatus, hMolecule, vCentralAtom, 1); + if (IXA_STATUS_HasError(hStatus)) return; + bond_type = IXA_MOL_GetBondType(hStatus, hMolecule, bond); + if (IXA_STATUS_HasError(hStatus)) return; + if (bond_type != IXA_BOND_TYPE_DOUBLE) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, + "Internal bonds of allenes must be double"); + return; + } + *pAtom2 = MOL_GetBondOtherAtom(hStatus, hMolecule, bond, vCentralAtom); + if (IXA_STATUS_HasError(hStatus)) return; +} + + +static IXA_ATOMID ExtendCumulene(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_BONDID vBond, + IXA_ATOMID vAtom) +{ + int bond_count; + IXA_BONDID bond; + IXA_BOND_TYPE bond_type; + IXA_ATOMID atom; + + bond_count = IXA_MOL_GetAtomNumBonds(hStatus, hMolecule, vAtom); + if (IXA_STATUS_HasError(hStatus)) return IXA_ATOMID_INVALID; + if (bond_count != 2) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Stereo centre is neither olefin nor cumulene"); + return IXA_ATOMID_INVALID; + } + bond = IXA_MOL_GetAtomBond(hStatus, hMolecule, vAtom, 0); + if (IXA_STATUS_HasError(hStatus)) return IXA_ATOMID_INVALID; + if (bond == vBond) + { + bond = IXA_MOL_GetAtomBond(hStatus, hMolecule, vAtom, 1); + if (IXA_STATUS_HasError(hStatus)) return IXA_ATOMID_INVALID; + } + bond_type = IXA_MOL_GetBondType(hStatus, hMolecule, bond); + if (IXA_STATUS_HasError(hStatus)) return IXA_ATOMID_INVALID; + if (bond_type != IXA_BOND_TYPE_DOUBLE) + { + STATUS_PushMessage( hStatus, IXA_STATUS_ERROR, + "Internal bonds of cumulenes must be double"); + return IXA_ATOMID_INVALID; + } + atom = MOL_GetBondOtherAtom(hStatus, hMolecule, bond, vAtom); + if (IXA_STATUS_HasError(hStatus)) return IXA_ATOMID_INVALID; + return atom; +} + + +static IXA_BOOL IsRectangularVertex(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vVertex, + IXA_ATOMID vInternal) +{ + IXA_BONDID bond; + + if (vVertex != IXA_ATOMID_IMPLICIT_H) + { + bond = IXA_MOL_GetCommonBond(hStatus, hMolecule, vVertex, vInternal); + if (IXA_STATUS_HasError(hStatus)) return IXA_FALSE; + if (bond == IXA_BONDID_INVALID) + { + return IXA_FALSE; + } + else + { + return IXA_TRUE; + } + } + else + { + return IXA_TRUE; + } +} + + +static IXA_BOOL IsRectOrAntiRectCentre(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vVertex1a, + IXA_ATOMID vVertex1b, + IXA_ATOMID vInternal1, + IXA_ATOMID vVertex2a, + IXA_ATOMID vVertex2b, + IXA_ATOMID vInternal2) +{ + if (!IsRectangularVertex(hStatus, hMolecule, vVertex1a, vInternal1)) return IXA_FALSE; + if (!IsRectangularVertex(hStatus, hMolecule, vVertex1b, vInternal1)) return IXA_FALSE; + if (!IsRectangularVertex(hStatus, hMolecule, vVertex2a, vInternal2)) return IXA_FALSE; + if (!IsRectangularVertex(hStatus, hMolecule, vVertex2b, vInternal2)) return IXA_FALSE; + return IXA_TRUE; +} + + +static void ClearMolecule(BuilderMolecule* pMolecule) +{ + inchi_free(pMolecule->atom); + pMolecule->atom = NULL; + pMolecule->num_atoms = 0; + + inchi_free(pMolecule->stereo0D); + pMolecule->stereo0D = NULL; + pMolecule->num_stereo0D = 0; + + pMolecule->chiral = 0; + + FreeInChIExtInput( pMolecule->polymer, pMolecule->v3000 ); + pMolecule->polymer = NULL; + pMolecule->v3000 = NULL; +} + + +static void AppendOption(char* pString, + const char* pOption) +{ + if (pString[0] != '\0') + { + strcat(pString, " "); + } + strcat(pString, pOption); +} + + +static void BUILDER_ClearOptions(INCHIBUILDER* pBuilder) +{ + pBuilder->option_NEWPSOFF = IXA_FALSE; + pBuilder->option_DoNotAddH = IXA_FALSE; + pBuilder->option_stereo = IXA_INCHIBUILDER_STEREOOPTION_SAbs; + pBuilder->option_SUU = IXA_FALSE; + pBuilder->option_SLUUD = IXA_FALSE; + pBuilder->option_FixedH = IXA_FALSE; + pBuilder->option_RecMet = IXA_FALSE; + pBuilder->option_KET = IXA_FALSE; + pBuilder->option_15T = IXA_FALSE; + pBuilder->option_AuxNone = IXA_FALSE; + pBuilder->option_Wnumber = 0; /* i.e. unlimited time */ + pBuilder->option_WarnOnEmptyStructure = IXA_FALSE; + pBuilder->option_LargeMolecules = IXA_FALSE; + pBuilder->option_SaveOpt = IXA_FALSE; +} + + +static void BUILDER_Update(IXA_STATUS_HANDLE hStatus, + INCHIBUILDER* pBuilder) +{ + char buffer[256]; +#ifdef IXA_USES_NON_EX_CORE_API + inchi_Input input; +#else + inchi_InputEx input; +#endif + inchi_Output output; + char options[256]; + + if (!pBuilder->dirty) return; + + options[0] = '\0'; + if (pBuilder->option_NEWPSOFF) + { + AppendOption(options, OPTION_PREFIX "NEWPSOFF"); + } + if (pBuilder->option_DoNotAddH) + { + AppendOption(options, OPTION_PREFIX "DoNotAddH"); + } + switch (pBuilder->option_stereo) + { + case IXA_INCHIBUILDER_STEREOOPTION_SAbs: + break; + case IXA_INCHIBUILDER_STEREOOPTION_SNon: + AppendOption(options, OPTION_PREFIX "SNon"); + break; + case IXA_INCHIBUILDER_STEREOOPTION_SRel: + AppendOption(options, OPTION_PREFIX "SRel"); + break; + case IXA_INCHIBUILDER_STEREOOPTION_SRac: + AppendOption(options, OPTION_PREFIX "SRac"); + break; + case IXA_INCHIBUILDER_STEREOOPTION_SUCF: + AppendOption(options, OPTION_PREFIX "SUCF"); + break; + default: + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, + "Stereo option %d is not recognised", pBuilder->option_stereo); + return; + } + if (pBuilder->option_SUU) + { + AppendOption(options, OPTION_PREFIX "SUU"); + } + if (pBuilder->option_SLUUD) + { + AppendOption(options, OPTION_PREFIX "SLUUD"); + } + if (pBuilder->option_FixedH) + { + AppendOption(options, OPTION_PREFIX "FixedH"); + } + if (pBuilder->option_RecMet) + { + AppendOption(options, OPTION_PREFIX "RecMet"); + } + if (pBuilder->option_KET) + { + AppendOption(options, OPTION_PREFIX "KET"); + } + if (pBuilder->option_15T) + { + AppendOption(options, OPTION_PREFIX "15T"); + } + if (pBuilder->option_AuxNone) + { + AppendOption(options, OPTION_PREFIX "AuxNone"); + } + if (pBuilder->option_Wnumber > 0) + { + sprintf(buffer, OPTION_PREFIX "W%d", pBuilder->option_Wnumber); + AppendOption(options, buffer); + } + if (pBuilder->option_WarnOnEmptyStructure) + { + AppendOption(options, OPTION_PREFIX "WarnOnEmptyStructure"); + } + if (pBuilder->option_LargeMolecules) + { + AppendOption(options, OPTION_PREFIX "LargeMolecules"); + } + if (pBuilder->option_SaveOpt) + { + AppendOption(options, OPTION_PREFIX "SaveOpt"); + } + if (pBuilder->molecule.chiral) + { + AppendOption(options, OPTION_PREFIX "ChiralFlagOn"); + } + else + { + AppendOption(options, OPTION_PREFIX "ChiralFlagOff"); + } + + input.szOptions = options; + input.num_atoms = pBuilder->molecule.num_atoms; + input.atom = pBuilder->molecule.atom; + input.num_stereo0D = pBuilder->molecule.num_stereo0D; + input.stereo0D = pBuilder->molecule.stereo0D; + +#ifdef IXA_USES_NON_EX_CORE_API + switch (GetINCHI(&input, &output)) +#else + input.polymer = pBuilder->molecule.polymer; + input.v3000 = pBuilder->molecule.v3000; + switch (GetINCHIEx(&input, &output)) +#endif + { + case inchi_Ret_WARNING: + STATUS_PushMessage(hStatus, IXA_STATUS_WARNING, output.szMessage); + /* Deliberate fall-through. */ + + case inchi_Ret_OKAY: + inchi_free(pBuilder->inchi); + pBuilder->inchi = NULL; + if (output.szInChI) + { + pBuilder->inchi = (char *) inchi_malloc(strlen(output.szInChI) + 1); + strcpy(pBuilder->inchi, output.szInChI); + } + + inchi_free(pBuilder->auxinfo); + pBuilder->auxinfo = NULL; + if (output.szAuxInfo) + { + pBuilder->auxinfo = (char *) inchi_malloc(strlen(output.szAuxInfo) + 1); + strcpy(pBuilder->auxinfo, output.szAuxInfo); + } + + inchi_free(pBuilder->log); + pBuilder->log = NULL; + if (output.szLog) + { + pBuilder->log = (char *) inchi_malloc(strlen(output.szLog) + 1); + strcpy(pBuilder->log, output.szLog); + } + break; + + default: + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, output.szMessage); + break; + } + + FreeINCHI(&output); + + pBuilder->dirty = IXA_FALSE; +} + + +IXA_INCHIBUILDER_HANDLE INCHI_DECL IXA_INCHIBUILDER_Create(IXA_STATUS_HANDLE hStatus) +{ + INCHIBUILDER* builder = (INCHIBUILDER*)inchi_malloc(sizeof(INCHIBUILDER)); + if (!builder) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Out of memory"); + return NULL; + } + + memset(&builder->molecule, 0, sizeof(builder->molecule)); + BUILDER_ClearOptions(builder); + builder->dirty = IXA_FALSE; + builder->inchi = NULL; + builder->auxinfo = NULL; + builder->log = NULL; + + return BUILDER_Pack(builder); +} + + +void INCHI_DECL IXA_INCHIBUILDER_Destroy(IXA_STATUS_HANDLE hStatus, + IXA_INCHIBUILDER_HANDLE hBuilder) +{ + INCHIBUILDER* builder = BUILDER_Unpack(hStatus, hBuilder); + if (!builder) return; + + ClearMolecule(&builder->molecule); + inchi_free(builder->inchi); + inchi_free(builder->auxinfo); + inchi_free(builder->log); + inchi_free(builder); +} + + +void INCHI_DECL IXA_INCHIBUILDER_SetMolecule(IXA_STATUS_HANDLE hStatus, + IXA_INCHIBUILDER_HANDLE hBuilder, + IXA_MOL_HANDLE hMolecule) +{ + int atom_index; + IXA_ATOMID atom; + IXA_ATOMID atom1; + IXA_ATOMID atom2; + int bond_index; + IXA_BONDID bond; + IXA_BOND_TYPE bond_type; + int stereo_index; + IXA_STEREOID stereo; + IXA_STEREO_TOPOLOGY topology; + int stereo_code; + const char* element; + + INCHIBUILDER* builder = BUILDER_Unpack(hStatus, hBuilder); + if (!builder) return; + + ClearMolecule(&builder->molecule); + + builder->molecule.num_atoms = IXA_MOL_GetNumAtoms(hStatus, hMolecule); + if (IXA_STATUS_HasError(hStatus)) return; + + if (builder->molecule.num_atoms > 0) + { + builder->molecule.atom = (inchi_Atom *) inchi_calloc(builder->molecule.num_atoms, sizeof(inchi_Atom)); + if (!builder->molecule.atom) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Out of memory"); + return; + } + + for (atom_index = 0; atom_index < builder->molecule.num_atoms; atom_index++) + { + atom = IXA_MOL_GetAtomId(hStatus, hMolecule, atom_index); + if (IXA_STATUS_HasError(hStatus)) return; + element = IXA_MOL_GetAtomElement(hStatus, hMolecule, atom); + if (IXA_STATUS_HasError(hStatus)) return; + strcpy(builder->molecule.atom[atom_index].elname, element); + builder->molecule.atom[atom_index].x = IXA_MOL_GetAtomX(hStatus, hMolecule, atom); + if (IXA_STATUS_HasError(hStatus)) return; + builder->molecule.atom[atom_index].y = IXA_MOL_GetAtomY(hStatus, hMolecule, atom); + if (IXA_STATUS_HasError(hStatus)) return; + builder->molecule.atom[atom_index].z = IXA_MOL_GetAtomZ(hStatus, hMolecule, atom); + if (IXA_STATUS_HasError(hStatus)) return; + builder->molecule.atom[atom_index].isotopic_mass = IXA_MOL_GetAtomMass(hStatus, hMolecule, atom); + if (IXA_STATUS_HasError(hStatus)) return; + builder->molecule.atom[atom_index].charge = IXA_MOL_GetAtomCharge(hStatus, hMolecule, atom); + if (IXA_STATUS_HasError(hStatus)) return; + builder->molecule.atom[atom_index].radical = IXA_MOL_GetAtomRadical(hStatus, hMolecule, atom); + if (IXA_STATUS_HasError(hStatus)) return; + builder->molecule.atom[atom_index].num_iso_H[0] = IXA_MOL_GetAtomHydrogens(hStatus, hMolecule, atom, 0); + if (IXA_STATUS_HasError(hStatus)) return; + builder->molecule.atom[atom_index].num_iso_H[1] = IXA_MOL_GetAtomHydrogens(hStatus, hMolecule, atom, 1); + if (IXA_STATUS_HasError(hStatus)) return; + builder->molecule.atom[atom_index].num_iso_H[2] = IXA_MOL_GetAtomHydrogens(hStatus, hMolecule, atom, 2); + if (IXA_STATUS_HasError(hStatus)) return; + builder->molecule.atom[atom_index].num_iso_H[3] = IXA_MOL_GetAtomHydrogens(hStatus, hMolecule, atom, 3); + if (IXA_STATUS_HasError(hStatus)) return; + builder->molecule.atom[atom_index].num_bonds = IXA_MOL_GetAtomNumBonds(hStatus, hMolecule, atom); + if (IXA_STATUS_HasError(hStatus)) return; + for (bond_index = 0; bond_index < builder->molecule.atom[atom_index].num_bonds; bond_index++) + { + bond = IXA_MOL_GetAtomBond(hStatus, hMolecule, atom, bond_index); + if (IXA_STATUS_HasError(hStatus)) return; + bond_type = IXA_MOL_GetBondType(hStatus, hMolecule, bond); + if (IXA_STATUS_HasError(hStatus)) return; + atom1 = IXA_MOL_GetBondAtom1(hStatus, hMolecule, bond); + if (IXA_STATUS_HasError(hStatus)) return; + atom2 = IXA_MOL_GetBondAtom2(hStatus, hMolecule, bond); + if (IXA_STATUS_HasError(hStatus)) return; + + if (atom == atom1) + { + builder->molecule.atom[atom_index].neighbor[bond_index] = IXA_MOL_GetAtomIndex(hStatus, hMolecule, atom2); + if (IXA_STATUS_HasError(hStatus)) return; + } + else + { + if (atom == atom2) + { + builder->molecule.atom[atom_index].neighbor[bond_index] = IXA_MOL_GetAtomIndex(hStatus, hMolecule, atom1); + if (IXA_STATUS_HasError(hStatus)) return; + } + else + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Illegal bond detected"); + return; + } + } + + switch (bond_type) + { + case IXA_BOND_TYPE_SINGLE: + { + IXA_BOND_WEDGE direction1; + IXA_BOND_WEDGE direction2; + direction1 = IXA_MOL_GetBondWedge(hStatus, hMolecule, bond, atom1); + if (IXA_STATUS_HasError(hStatus)) return; + direction2 = IXA_MOL_GetBondWedge(hStatus, hMolecule, bond, atom2); + if (IXA_STATUS_HasError(hStatus)) return; + + if (atom == atom1) + { + stereo_code = GetSingleStereoCode(hStatus, direction1, direction2); + if (IXA_STATUS_HasError(hStatus)) return; + } + else + { + if (atom == atom2) + { + stereo_code = GetSingleStereoCode(hStatus, direction2, direction1); + if (IXA_STATUS_HasError(hStatus)) return; + } + else + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Illegal bond detected"); + return; + } + } + } break; + + case IXA_BOND_TYPE_DOUBLE: + { + IXA_DBLBOND_CONFIG config; + config = IXA_MOL_GetDblBondConfig(hStatus, hMolecule, bond); + if (IXA_STATUS_HasError(hStatus)) return; + stereo_code = GetDoubleStereoCode(hStatus, config); + if (IXA_STATUS_HasError(hStatus)) return; + } break; + + default: + stereo_code = INCHI_BOND_STEREO_NONE; + break; + } + + builder->molecule.atom[atom_index].bond_stereo[bond_index] = stereo_code; + builder->molecule.atom[atom_index].bond_type[bond_index] = bond_type; + } + } + } + + builder->molecule.num_stereo0D = IXA_MOL_GetNumStereos(hStatus, hMolecule); + if (IXA_STATUS_HasError(hStatus)) return; + if (builder->molecule.num_stereo0D > 0) + { + builder->molecule.stereo0D = (inchi_Stereo0D *) inchi_calloc(builder->molecule.num_stereo0D, sizeof(inchi_Stereo0D)); + if (!builder->molecule.stereo0D) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Out of memory"); + return; + } + + for (stereo_index = 0; stereo_index < builder->molecule.num_stereo0D; stereo_index++) + { + stereo = IXA_MOL_GetStereoId(hStatus, hMolecule, stereo_index); + if (IXA_STATUS_HasError(hStatus)) return; + + topology = IXA_MOL_GetStereoTopology(hStatus, hMolecule, stereo); + if (IXA_STATUS_HasError(hStatus)) return; + switch (topology) + { + case IXA_STEREO_TOPOLOGY_TETRAHEDRON: + builder->molecule.stereo0D[stereo_index].type = INCHI_StereoType_Tetrahedral; + atom = IXA_MOL_GetStereoCentralAtom(hStatus, hMolecule, stereo); + if (IXA_STATUS_HasError(hStatus)) return; + builder->molecule.stereo0D[stereo_index].central_atom = IXA_MOL_GetAtomIndex(hStatus, hMolecule, atom); + if (IXA_STATUS_HasError(hStatus)) return; + TranslateTetrahedralVertex(hStatus, hMolecule, stereo, &builder->molecule.stereo0D[stereo_index], 0); + TranslateTetrahedralVertex(hStatus, hMolecule, stereo, &builder->molecule.stereo0D[stereo_index], 1); + TranslateTetrahedralVertex(hStatus, hMolecule, stereo, &builder->molecule.stereo0D[stereo_index], 2); + TranslateTetrahedralVertex(hStatus, hMolecule, stereo, &builder->molecule.stereo0D[stereo_index], 3); + builder->molecule.stereo0D[stereo_index].parity = IXA_MOL_GetStereoParity(hStatus, hMolecule, stereo); + if (IXA_STATUS_HasError(hStatus)) return; + break; + + case IXA_STEREO_TOPOLOGY_RECTANGLE: + { + IXA_BONDID central_bond; + IXA_ATOMID internal1; + IXA_ATOMID vertex1a; + IXA_ATOMID vertex1b; + IXA_ATOMID internal2; + IXA_ATOMID vertex2a; + IXA_ATOMID vertex2b; + IXA_BOOL flag; + + central_bond = IXA_MOL_GetStereoCentralBond(hStatus, hMolecule, stereo); + if (IXA_STATUS_HasError(hStatus)) return; + internal1 = IXA_MOL_GetBondAtom1(hStatus, hMolecule, central_bond); + if (IXA_STATUS_HasError(hStatus)) return; + internal2 = IXA_MOL_GetBondAtom2(hStatus, hMolecule, central_bond); + if (IXA_STATUS_HasError(hStatus)) return; + + vertex1a = IXA_MOL_GetStereoVertex(hStatus, hMolecule, stereo, 0); + if (IXA_STATUS_HasError(hStatus)) return; + vertex1b = IXA_MOL_GetStereoVertex(hStatus, hMolecule, stereo, 1); + if (IXA_STATUS_HasError(hStatus)) return; + vertex2a = IXA_MOL_GetStereoVertex(hStatus, hMolecule, stereo, 2); + if (IXA_STATUS_HasError(hStatus)) return; + vertex2b = IXA_MOL_GetStereoVertex(hStatus, hMolecule, stereo, 3); + if (IXA_STATUS_HasError(hStatus)) return; + + flag = IsRectOrAntiRectCentre(hStatus, hMolecule, + vertex1a, vertex1b, internal1, vertex2a, vertex2b, internal2); + if (IXA_STATUS_HasError(hStatus)) return; + if (!flag) + { + /* Internal and external vertices as currently assigned + do not constitute a valid olefin. Try + swapping the internal vertices. */ + atom = internal1; + internal1 = internal2; + internal2 = atom; + + flag = IsRectOrAntiRectCentre(hStatus, hMolecule, + vertex1a, vertex1b, internal1, vertex2a, vertex2b, internal2); + if (IXA_STATUS_HasError(hStatus)) return; + if (!flag) + { + /* The stereo centre cannot be an olefin. + Perhaps it is a cumulene? */ + internal1 = ExtendCumulene(hStatus, hMolecule, central_bond, internal1); + if (IXA_STATUS_HasError(hStatus)) return; + internal2 = ExtendCumulene(hStatus, hMolecule, central_bond, internal2); + if (IXA_STATUS_HasError(hStatus)) return; + + flag = IsRectOrAntiRectCentre(hStatus, hMolecule, + vertex1a, vertex1b, internal1, vertex2a, vertex2b, internal2); + if (IXA_STATUS_HasError(hStatus)) return; + if (!flag) + { + /* Internal and external vertices as currently assigned + do not constitute a valid cumulene. Try + swapping the internal vertices. */ + atom = internal1; + internal1 = internal2; + internal2 = atom; + + flag = IsRectOrAntiRectCentre(hStatus, hMolecule, + vertex1a, vertex1b, internal1, vertex2a, vertex2b, internal2); + if (IXA_STATUS_HasError(hStatus)) return; + if (!flag) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Stereo centre cannot be either an olefin or a cumulenee"); + return; + } + } + } + } + builder->molecule.stereo0D[stereo_index].type = INCHI_StereoType_DoubleBond; + builder->molecule.stereo0D[stereo_index].central_atom = NO_ATOM; + builder->molecule.stereo0D[stereo_index].neighbor[1] = IXA_MOL_GetAtomIndex(hStatus, hMolecule, internal1); + if (IXA_STATUS_HasError(hStatus)) return; + builder->molecule.stereo0D[stereo_index].neighbor[2] = IXA_MOL_GetAtomIndex(hStatus, hMolecule, internal2); + if (IXA_STATUS_HasError(hStatus)) return; + + if ((vertex1a != IXA_ATOMID_IMPLICIT_H) && (vertex2b != IXA_ATOMID_IMPLICIT_H)) + { + builder->molecule.stereo0D[stereo_index].neighbor[0] = IXA_MOL_GetAtomIndex(hStatus, hMolecule, vertex1a); + if (IXA_STATUS_HasError(hStatus)) return; + builder->molecule.stereo0D[stereo_index].neighbor[3] = IXA_MOL_GetAtomIndex(hStatus, hMolecule, vertex2b); + if (IXA_STATUS_HasError(hStatus)) return; + } + else if ((vertex1b != IXA_ATOMID_IMPLICIT_H) && (vertex2a != IXA_ATOMID_IMPLICIT_H)) + { + builder->molecule.stereo0D[stereo_index].neighbor[0] = IXA_MOL_GetAtomIndex(hStatus, hMolecule, vertex1b); + if (IXA_STATUS_HasError(hStatus)) return; + builder->molecule.stereo0D[stereo_index].neighbor[3] = IXA_MOL_GetAtomIndex(hStatus, hMolecule, vertex2a); + if (IXA_STATUS_HasError(hStatus)) return; + } + else + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Cannot create internal representation of stereo centre"); + return; + } + + builder->molecule.stereo0D[stereo_index].parity = IXA_MOL_GetStereoParity(hStatus, hMolecule, stereo); + if (IXA_STATUS_HasError(hStatus)) return; + break; + } + + case IXA_STEREO_TOPOLOGY_ANTIRECTANGLE: + { + IXA_ATOMID central_atom; + IXA_ATOMID internal1; + IXA_ATOMID vertex1a; + IXA_ATOMID vertex1b; + IXA_ATOMID internal2; + IXA_ATOMID vertex2a; + IXA_ATOMID vertex2b; + IXA_BOOL flag; + + central_atom = IXA_MOL_GetStereoCentralAtom(hStatus, hMolecule, stereo); + if (IXA_STATUS_HasError(hStatus)) return; + ExtendAllene(hStatus, hMolecule, central_atom, &internal1, &internal2); + if (IXA_STATUS_HasError(hStatus)) return; + + vertex1a = IXA_MOL_GetStereoVertex(hStatus, hMolecule, stereo, 0); + if (IXA_STATUS_HasError(hStatus)) return; + vertex1b = IXA_MOL_GetStereoVertex(hStatus, hMolecule, stereo, 1); + if (IXA_STATUS_HasError(hStatus)) return; + vertex2a = IXA_MOL_GetStereoVertex(hStatus, hMolecule, stereo, 2); + if (IXA_STATUS_HasError(hStatus)) return; + vertex2b = IXA_MOL_GetStereoVertex(hStatus, hMolecule, stereo, 3); + if (IXA_STATUS_HasError(hStatus)) return; + + flag = IsRectOrAntiRectCentre(hStatus, hMolecule, + vertex1a, vertex1b, internal1, vertex2a, vertex2b, internal2); + if (IXA_STATUS_HasError(hStatus)) return; + if (!flag) + { + /* Internal and external vertices as currently assigned + do not constitute a valid cumulene. Try + swapping the internal vertices. */ + atom = internal1; + internal1 = internal2; + internal2 = atom; + + flag = IsRectOrAntiRectCentre(hStatus, hMolecule, + vertex1a, vertex1b, internal1, vertex2a, vertex2b, internal2); + if (IXA_STATUS_HasError(hStatus)) return; + if (!flag) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Stereo centre cannot be either an olefin or a cumulenee"); + return; + } + } + + builder->molecule.stereo0D[stereo_index].type = INCHI_StereoType_Allene; + builder->molecule.stereo0D[stereo_index].central_atom = IXA_MOL_GetAtomIndex(hStatus, hMolecule, central_atom); + if (IXA_STATUS_HasError(hStatus)) return; + builder->molecule.stereo0D[stereo_index].neighbor[1] = IXA_MOL_GetAtomIndex(hStatus, hMolecule, internal1); + if (IXA_STATUS_HasError(hStatus)) return; + builder->molecule.stereo0D[stereo_index].neighbor[2] = IXA_MOL_GetAtomIndex(hStatus, hMolecule, internal2); + if (IXA_STATUS_HasError(hStatus)) return; + if ((vertex1a != IXA_ATOMID_IMPLICIT_H) && (vertex2b != IXA_ATOMID_IMPLICIT_H)) + { + builder->molecule.stereo0D[stereo_index].neighbor[0] = IXA_MOL_GetAtomIndex(hStatus, hMolecule, vertex1a); + builder->molecule.stereo0D[stereo_index].neighbor[3] = IXA_MOL_GetAtomIndex(hStatus, hMolecule, vertex2b); + } + else if ((vertex1b != IXA_ATOMID_IMPLICIT_H) && (vertex2a != IXA_ATOMID_IMPLICIT_H)) + { + builder->molecule.stereo0D[stereo_index].neighbor[0] = IXA_MOL_GetAtomIndex(hStatus, hMolecule, vertex1b); + builder->molecule.stereo0D[stereo_index].neighbor[3] = IXA_MOL_GetAtomIndex(hStatus, hMolecule, vertex2a); + } + else + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Cannot create internal representation of stereo centre"); + return; + } + builder->molecule.stereo0D[stereo_index].parity = IXA_MOL_GetStereoParity(hStatus, hMolecule, stereo); + if (IXA_STATUS_HasError(hStatus)) return; + break; + } + + default: + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Unexpected stereo topology"); + return; + } + } + } + + builder->molecule.chiral = IXA_MOL_GetChiral(hStatus, hMolecule); + + /* Extended input features (TODO: replace and isolate) */ + { + int k, m; + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if ( molecule ) + { + int nat = IXA_MOL_GetNumAtoms( hStatus, hMolecule); + + /* Polymer */ + if ( molecule->polymer ) + { + builder->molecule.polymer = (inchi_Input_Polymer *) inchi_calloc( 1, sizeof(inchi_Input_Polymer) ); + if ( !builder->molecule.polymer ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Out of memory"); + return; + } + builder->molecule.polymer->n = molecule->polymer->n; + if ( builder->molecule.polymer->n ) + { + builder->molecule.polymer->units = (inchi_Input_PolymerUnit **) inchi_calloc( builder->molecule.polymer->n, sizeof(builder->molecule.polymer->units[0]) ); + if ( !builder->molecule.polymer->units ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Out of memory"); + return; + } + memset( builder->molecule.polymer->units, 0, sizeof( *(builder->molecule.polymer->units) ) ); + } + for (k=0; kmolecule.polymer->n; k++ ) + { + int q=0; + INCHIMOL_SGROUP * groupk = molecule->polymer->units[k]; + inchi_Input_PolymerUnit * unitk = builder->molecule.polymer->units[k] = (inchi_Input_PolymerUnit *) inchi_calloc( 1, sizeof(inchi_Input_PolymerUnit) ); + if (!unitk ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Out of memory"); + return; + } + memset( unitk, 0, sizeof( *unitk ) ); + + unitk->id = groupk->id; + unitk->type = groupk->type; + unitk->subtype = groupk->subtype; + unitk->conn = groupk->conn; + unitk->label = groupk->label; + + for (q=0; q<4; q++) + { + unitk->xbr1[q] = groupk->xbr1[q]; + unitk->xbr2[q] = groupk->xbr2[q]; + } + strcpy( unitk->smt, groupk->smt ); + unitk->na = groupk->na; + unitk->alist = (int *) inchi_calloc( unitk->na, sizeof(int) ); + if (!unitk->alist ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Out of memory"); + return; + } + for (m=0; mna; m++) + { + unitk->alist[m] = groupk->alist[m]; + } + unitk->nb = groupk->nb; + if ( unitk->nb > 0 ) + { + unitk->blist = (int *) inchi_calloc( 2*unitk->nb, sizeof(int) ); + if (!unitk->blist ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Out of memory"); + return; + } + for (m=0; m < 2*unitk->nb; m++) + { + unitk->blist[m] = groupk->blist[m]; + } + } + else + unitk->blist = NULL; + } + } + + /* V3000 */ + if ( molecule->v3000 ) + { + int m, k, nn; + + builder->molecule.v3000 = (inchi_Input_V3000 *) inchi_calloc( 1, sizeof(inchi_Input_V3000) ); + if ( !builder->molecule.v3000 ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Out of memory"); + return; + } + memset( builder->molecule.v3000, 0, sizeof( inchi_Input_V3000 ) ); + + builder->molecule.v3000->n_collections = molecule->v3000->n_collections; + builder->molecule.v3000->n_haptic_bonds = molecule->v3000->n_haptic_bonds; + builder->molecule.v3000->n_non_haptic_bonds = molecule->v3000->n_non_haptic_bonds; + builder->molecule.v3000->n_sgroups = molecule->v3000->n_sgroups; + builder->molecule.v3000->n_non_star_atoms = molecule->v3000->n_non_star_atoms; + builder->molecule.v3000->n_star_atoms = molecule->v3000->n_star_atoms; + builder->molecule.v3000->n_steabs = molecule->v3000->n_steabs; + builder->molecule.v3000->n_sterac = molecule->v3000->n_sterac; + builder->molecule.v3000->n_sterel = molecule->v3000->n_sterel; + builder->molecule.v3000->n_3d_constraints = molecule->v3000->n_3d_constraints; + + if ( molecule->v3000->atom_index_orig ) + { + builder->molecule.v3000->atom_index_orig = (int *) inchi_calloc( nat, sizeof(int) ); + if ( NULL==builder->molecule.v3000->atom_index_orig ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Out of memory"); + return; + } + memcpy( builder->molecule.v3000->atom_index_orig, molecule->v3000->atom_index_orig, nat ); + } + if ( molecule->v3000->atom_index_fin ) + { + builder->molecule.v3000->atom_index_fin = (int *) inchi_calloc( nat, sizeof(int) ); + if ( NULL==builder->molecule.v3000->atom_index_fin ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Out of memory"); + return; + } + memcpy( builder->molecule.v3000->atom_index_fin, molecule->v3000->atom_index_fin, nat); + } + if ( molecule->v3000->n_haptic_bonds && molecule->v3000->lists_haptic_bonds ) + { + builder->molecule.v3000->lists_haptic_bonds = (int **) calloc( molecule->v3000->n_haptic_bonds, sizeof (int*) ); + if ( NULL==builder->molecule.v3000->lists_haptic_bonds ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Out of memory"); + return; + } + for (m=0; mv3000->n_haptic_bonds; m++) + { + int *lst=NULL; + int *mol_lst = molecule->v3000->lists_haptic_bonds[m]; + int n_endpts = mol_lst[2]; + nn = n_endpts + 3; + lst = builder->molecule.v3000->lists_haptic_bonds[m] = (int *) calloc( nn, sizeof (int) ); + if ( NULL==lst ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Out of memory"); + return; + } + for (k=0; kv3000->n_steabs && molecule->v3000->lists_steabs ) + { + builder->molecule.v3000->lists_steabs = (int **) calloc( molecule->v3000->n_steabs, sizeof (int*) ); + if ( NULL==builder->molecule.v3000->lists_steabs ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Out of memory"); + return; + } + for (m=0; mv3000->n_steabs; m++) + { + int *lst=NULL; + int *mol_lst = molecule->v3000->lists_steabs[m]; + nn = mol_lst[1] + 2; + lst = builder->molecule.v3000->lists_steabs[m] = (int *) calloc( nn, sizeof (int) ); + if ( NULL==lst ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Out of memory"); + return; + } + for (k=0; kdirty = IXA_TRUE; +} + + +void INCHI_DECL IXA_INCHIBUILDER_SetOption_Stereo(IXA_STATUS_HANDLE hStatus, + IXA_INCHIBUILDER_HANDLE hBuilder, + IXA_INCHIBUILDER_STEREOOPTION vValue) +{ + INCHIBUILDER* builder = BUILDER_Unpack(hStatus, hBuilder); + if (!builder) return; + + builder->option_stereo = vValue; +} + + +void INCHI_DECL IXA_INCHIBUILDER_SetOption_Timeout(IXA_STATUS_HANDLE hStatus, + IXA_INCHIBUILDER_HANDLE hBuilder, + int vValue) +{ + INCHIBUILDER* builder = BUILDER_Unpack(hStatus, hBuilder); + if (!builder) return; + + builder->option_Wnumber = vValue; +} + + +void INCHI_DECL IXA_INCHIBUILDER_SetOption(IXA_STATUS_HANDLE hStatus, + IXA_INCHIBUILDER_HANDLE hBuilder, + IXA_INCHIBUILDER_OPTION vOption, + IXA_BOOL vValue) +{ + INCHIBUILDER* builder = BUILDER_Unpack(hStatus, hBuilder); + if (!builder) return; + + switch (vOption) + { + case IXA_INCHIBUILDER_OPTION_NewPsOff: + builder->option_NEWPSOFF = vValue; + return; + case IXA_INCHIBUILDER_OPTION_DoNotAddH: + builder->option_DoNotAddH = vValue; + return; + case IXA_INCHIBUILDER_OPTION_SUU: + builder->option_SUU = vValue; + return; + case IXA_INCHIBUILDER_OPTION_SLUUD: + builder->option_SLUUD = vValue; + return; + case IXA_INCHIBUILDER_OPTION_FixedH: + builder->option_FixedH = vValue; + return; + case IXA_INCHIBUILDER_OPTION_RecMet: + builder->option_RecMet = vValue; + return; + case IXA_INCHIBUILDER_OPTION_KET: + builder->option_KET = vValue; + return; + case IXA_INCHIBUILDER_OPTION_15T: + builder->option_15T = vValue; + return; + case IXA_INCHIBUILDER_OPTION_SaveOpt: + builder->option_SaveOpt = vValue; + return; + case IXA_INCHIBUILDER_OPTION_AuxNone: + builder->option_AuxNone = vValue; + return; + case IXA_INCHIBUILDER_OPTION_WarnOnEmptyStructure: + builder->option_WarnOnEmptyStructure = vValue; + return; + case IXA_INCHIBUILDER_OPTION_LargeMolecules: + builder->option_LargeMolecules = vValue; + return; + default: + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Option is not recognised"); + return; + } +} + + +const char* INCHI_DECL IXA_INCHIBUILDER_GetInChI(IXA_STATUS_HANDLE hStatus, + IXA_INCHIBUILDER_HANDLE hBuilder) +{ + INCHIBUILDER* builder = BUILDER_Unpack(hStatus, hBuilder); + if (!builder) return NULL; + + BUILDER_Update(hStatus, builder); + return builder->inchi; +} + + +const char* INCHI_DECL IXA_INCHIBUILDER_GetInChIEx(IXA_STATUS_HANDLE hStatus, + IXA_INCHIBUILDER_HANDLE hBuilder) +{ + INCHIBUILDER* builder = BUILDER_Unpack(hStatus, hBuilder); + if (!builder) return NULL; + + BUILDER_Update(hStatus, builder); + return builder->inchi; +} + + +const char* INCHI_DECL IXA_INCHIBUILDER_GetAuxInfo(IXA_STATUS_HANDLE hStatus, + IXA_INCHIBUILDER_HANDLE hBuilder) +{ + INCHIBUILDER* builder = BUILDER_Unpack(hStatus, hBuilder); + if (!builder) return NULL; + + BUILDER_Update(hStatus, builder); + return builder->auxinfo; +} + + +const char* INCHI_DECL IXA_INCHIBUILDER_GetLog(IXA_STATUS_HANDLE hStatus, + IXA_INCHIBUILDER_HANDLE hBuilder) +{ + INCHIBUILDER* builder = BUILDER_Unpack(hStatus, hBuilder); + if (!builder) return NULL; + + BUILDER_Update(hStatus, builder); + return builder->log; +} diff --git a/INCHI-1-SRC/INCHI_API/libinchi/src/ixa/ixa_inchikey_builder.c b/INCHI-1-SRC/INCHI_API/libinchi/src/ixa/ixa_inchikey_builder.c new file mode 100644 index 0000000..58eddf5 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/libinchi/src/ixa/ixa_inchikey_builder.c @@ -0,0 +1,138 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include "../../../../INCHI_BASE/src/mode.h" +#include "../../../../INCHI_BASE/src/inchi_api.h" +#include "ixa_status.h" +#include +#include + + +typedef struct +{ + char* inchi; + char inchi_key[256]; + int dirty; +} INCHIKEYBUILDER; + + +static INCHIKEYBUILDER* KEYBUILDER_Unpack(IXA_STATUS_HANDLE hStatus, + IXA_INCHIKEYBUILDER_HANDLE hKeyBuilder) +{ + if (!hKeyBuilder) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Illegal keybuilder handle detected"); + return NULL; + } + + return (INCHIKEYBUILDER*)hKeyBuilder; +} + + +static IXA_INCHIKEYBUILDER_HANDLE KEYBUILDER_Pack(INCHIKEYBUILDER* pKeyBuilder) +{ + return (IXA_INCHIKEYBUILDER_HANDLE)pKeyBuilder; +} + + +IXA_INCHIKEYBUILDER_HANDLE INCHI_DECL IXA_INCHIKEYBUILDER_Create(IXA_STATUS_HANDLE hStatus) +{ + INCHIKEYBUILDER* key_builder = (INCHIKEYBUILDER*)inchi_malloc(sizeof(INCHIKEYBUILDER)); + if (!key_builder) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Out of memory"); + return NULL; + } + + memset(key_builder, 0, sizeof(INCHIKEYBUILDER)); + return KEYBUILDER_Pack(key_builder); +} + + +void INCHI_DECL IXA_INCHIKEYBUILDER_Destroy(IXA_STATUS_HANDLE hStatus, + IXA_INCHIKEYBUILDER_HANDLE hKeyBuilder) +{ + INCHIKEYBUILDER* key_builder = KEYBUILDER_Unpack(hStatus, hKeyBuilder); + if (!key_builder) return; + + inchi_free(key_builder->inchi); + inchi_free(key_builder); +} + + +void INCHI_DECL IXA_INCHIKEYBUILDER_SetInChI(IXA_STATUS_HANDLE hStatus, + IXA_INCHIKEYBUILDER_HANDLE hKeyBuilder, + const char* pInChI) +{ + INCHIKEYBUILDER* key_builder = KEYBUILDER_Unpack(hStatus, hKeyBuilder); + if (!key_builder) return; + + inchi_free(key_builder->inchi); + key_builder->inchi = (char *) inchi_malloc(strlen(pInChI) + 1); + if (!key_builder->inchi) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Out of memory"); + return; + } + + strcpy(key_builder->inchi, pInChI); + key_builder->dirty = 1; +} + + +const char* INCHI_DECL IXA_INCHIKEYBUILDER_GetInChIKey(IXA_STATUS_HANDLE hStatus, + IXA_INCHIKEYBUILDER_HANDLE hKeyBuilder) +{ + INCHIKEYBUILDER* key_builder = KEYBUILDER_Unpack(hStatus, hKeyBuilder); + if (!key_builder) return NULL; + + if (key_builder->dirty) + { + char extra1[256]; + char extra2[256]; + if (GetINCHIKeyFromINCHI(key_builder->inchi, 0, 0, key_builder->inchi_key, extra1, extra2) != INCHIKEY_OK) + { + printf("Failed to create InChIKey\n"); + return NULL; + } + key_builder->dirty = 0; + } + + return key_builder->inchi_key; +} diff --git a/INCHI-1-SRC/INCHI_API/libinchi/src/ixa/ixa_mol.c b/INCHI-1-SRC/INCHI_API/libinchi/src/ixa/ixa_mol.c new file mode 100644 index 0000000..50806b0 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/libinchi/src/ixa/ixa_mol.c @@ -0,0 +1,2023 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include + +#include "../../../../INCHI_BASE/src/mode.h" +#include "../../../../INCHI_BASE/src/ichierr.h" +#include "../../../../INCHI_BASE/src/inchi_api.h" +#include "ixa_mol.h" +#include "ixa_status.h" + +static const char* Elements[] = + {"", + "H", "He", + "Li","Be", "B", "C", "N", "O", "F", "Ne", + "Na","Mg", "Al","Si","P", "S", "Cl","Ar", + "K", "Ca","Sc","Ti","V", "Cr","Mn","Fe","Co","Ni","Cu","Zn","Ga","Ge","As","Se","Br","Kr", + "Rb","Sr","Y", "Zr","Nb","Mo","Tc","Ru","Rh","Pd","Ag","Cd","In","Sn","Sb","Te","I", "Xe", + "Cs","Ba","La", + "Ce","Pr","Nd","Pm","Sm","Eu","Gd","Tb","Dy","Ho","Er","Tm","Yb","Lu", + "Hf","Ta","W", "Re","Os","Ir","Pt","Au","Hg","Tl","Pb","Bi","Po","At","Rn", + "Fr","Ra","Ac", + "Th","Pa","U", "Np","Pu","Am","Cm","Bk","Cf","Es","Fm","Md","No", + "Lr","Rf","Db","Sg","Bh","Hs","Mt","Ds","Rg","Cn","Nh","Fl","Mc","Lv","Ts","Og", + "Zz" + }; + +static int GetStereo(IXA_BOND_TYPE type, + IXA_BOND_WEDGE direction, + IXA_BOND_WEDGE reverse_direction) +{ + if (type == IXA_BOND_TYPE_SINGLE /*INCHI_BOND_TYPE_SINGLE*/ ) + { + switch (direction) + { + case IXA_BOND_WEDGE_NONE: + switch (reverse_direction) + { + case IXA_BOND_WEDGE_NONE: + return INCHI_BOND_STEREO_NONE; + case IXA_BOND_WEDGE_UP: + return INCHI_BOND_STEREO_SINGLE_2UP; + case IXA_BOND_WEDGE_DOWN: + return INCHI_BOND_STEREO_SINGLE_2DOWN; + case IXA_BOND_WEDGE_EITHER: + return INCHI_BOND_STEREO_SINGLE_2EITHER; + } + + case IXA_BOND_WEDGE_UP: + return INCHI_BOND_STEREO_SINGLE_1UP; + case IXA_BOND_WEDGE_DOWN: + return INCHI_BOND_STEREO_SINGLE_1DOWN; + case IXA_BOND_WEDGE_EITHER: + return INCHI_BOND_STEREO_SINGLE_1EITHER; + } + } + + if (direction == IXA_BOND_WEDGE_NONE) + { + return INCHI_BOND_STEREO_NONE; + } + else + { + return INCHI_BOND_STEREO_DOUBLE_EITHER; + } +} + + +static int GetVertexCount(IXA_STATUS_HANDLE hStatus, + IXA_STEREO_TOPOLOGY vTopology) +{ + switch (vTopology) + { + case IXA_STEREO_TOPOLOGY_TETRAHEDRON: + case IXA_STEREO_TOPOLOGY_RECTANGLE: + case IXA_STEREO_TOPOLOGY_ANTIRECTANGLE: + return 4; + default: + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Unexpected stereo topology"); + return 0; + } +} + + +static IXA_ATOMID MOL_PackAtom(int vAtomIndex) +{ + return (IXA_ATOMID)((size_t)(vAtomIndex + 1)); +} + + +static IXA_BONDID MOL_PackBond(int vBondIndex) +{ + return (IXA_BONDID)((size_t)(vBondIndex + 1)); +} + + +static IXA_STEREOID MOL_PackStereo(int vStereoIndex) +{ + return (IXA_STEREOID)((size_t)(vStereoIndex + 1)); +} + + +static IXA_BOOL MOL_UnpackAtom(IXA_STATUS_HANDLE hStatus, + INCHIMOL* pMolecule, + IXA_ATOMID vAtom, + int* pAtomIndex) +{ + *pAtomIndex = (size_t)(vAtom) - 1; + if ((*pAtomIndex < 0) || (*pAtomIndex >= pMolecule->atom_count)) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Atom ID is invalid"); + return IXA_FALSE; + } + + return IXA_TRUE; +} + + +static IXA_BOOL MOL_UnpackBond(IXA_STATUS_HANDLE hStatus, + INCHIMOL* pMolecule, + IXA_BONDID vBond, + int* pBondIndex) +{ + *pBondIndex = (size_t)(vBond) - 1; + if ((*pBondIndex < 0) || (*pBondIndex >= pMolecule->bond_count)) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Bond ID is invalid"); + return IXA_FALSE; + } + + return IXA_TRUE; +} + + +static IXA_BOOL MOL_UnpackStereo(IXA_STATUS_HANDLE hStatus, + INCHIMOL* pMolecule, + IXA_STEREOID vStereo, + int* pStereoIndex) +{ + *pStereoIndex = (size_t)(vStereo) - 1; + if ((*pStereoIndex < 0) || (*pStereoIndex >= pMolecule->stereo_count)) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Stereo ID is invalid"); + return IXA_FALSE; + } + + return IXA_TRUE; +} + + +static INCHIMOL_ATOM* MOL_GetAtom(IXA_STATUS_HANDLE hStatus, + INCHIMOL* pMolecule, + IXA_ATOMID vAtom) +{ + int atom_index; + if (!MOL_UnpackAtom(hStatus, pMolecule, vAtom, &atom_index)) return NULL; + return &pMolecule->atoms[atom_index]; +} + + +static INCHIMOL_BOND* MOL_GetBond(IXA_STATUS_HANDLE hStatus, + INCHIMOL* pMolecule, + IXA_BONDID vBond) +{ + int bond_index; + if (!MOL_UnpackBond(hStatus, pMolecule, vBond, &bond_index)) return NULL; + return &pMolecule->bonds[bond_index]; +} + + +static INCHIMOL_STEREO* MOL_GetStereo(IXA_STATUS_HANDLE hStatus, + INCHIMOL* pMolecule, + IXA_STEREOID vStereo) +{ + int stereo_index; + if (!MOL_UnpackStereo(hStatus, pMolecule, vStereo, &stereo_index)) return NULL; + return &pMolecule->stereos[stereo_index]; +} + + +void MOL_Clear(INCHIMOL* pMolecule) +{ + inchi_free(pMolecule->atoms); + inchi_free(pMolecule->bonds); + inchi_free(pMolecule->stereos); + + IXA_MOL_ClearExtMolData( pMolecule->polymer, pMolecule->v3000 ); + + memset(pMolecule, 0, sizeof(INCHIMOL)); +} + + +static IXA_ATOMID MOL_CreateAtom(IXA_STATUS_HANDLE hStatus, + INCHIMOL* pMolecule) +{ + /* + Reallocate the atoms array to make space for one more atom. Doing things this way means + that a reallocation occurs every time MOL_CreateAtom is called, which is inefficient. + Alternatives include: + 1. Allocating several atoms at a time so that a reallocations occur only when the pool + of pre-allocated atoms has been exhausted. This is more efficient but also more + complicated and wastes memory. + 2. Giving INCHIMOL a function equivalent to std::vector::reserve so that the user can + pre-allocate exactly the number of atoms that they know they will need. This avoids + wasting memory but is even more complicated. + */ + INCHIMOL_ATOM* temp = (INCHIMOL_ATOM *) inchi_calloc(pMolecule->atom_count + 1, sizeof(INCHIMOL_ATOM)); + if (!temp) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Out of memory"); + return IXA_ATOMID_INVALID; + } + + memcpy(temp, pMolecule->atoms, pMolecule->atom_count * sizeof(INCHIMOL_ATOM)); + inchi_free(pMolecule->atoms); + pMolecule->atoms = temp; + pMolecule->atoms[pMolecule->atom_count].x = 0; + pMolecule->atoms[pMolecule->atom_count].y = 0; + pMolecule->atoms[pMolecule->atom_count].z = 0; + pMolecule->atoms[pMolecule->atom_count].atomic_number = 6; /* Carbon */ + pMolecule->atoms[pMolecule->atom_count].hydrogens[0] = 0; + pMolecule->atoms[pMolecule->atom_count].hydrogens[1] = 0; + pMolecule->atoms[pMolecule->atom_count].hydrogens[2] = 0; + pMolecule->atoms[pMolecule->atom_count].hydrogens[3] = 0; + pMolecule->atoms[pMolecule->atom_count].mass = 0; + pMolecule->atoms[pMolecule->atom_count].radical = IXA_ATOM_RADICAL_NONE; + pMolecule->atoms[pMolecule->atom_count].charge = 0; + pMolecule->atoms[pMolecule->atom_count].bond_count = 0; + return MOL_PackAtom(pMolecule->atom_count++); +} + + +static int MOL_CreateStereo(IXA_STATUS_HANDLE hStatus, + INCHIMOL* pMolecule) +{ + /* + Reallocate the atoms array to make space for one more atom. Doing things this way means + that a reallocation occurs every time MOL_CreateAtom is called, which is inefficient. + Alternatives include: + 1. Allocating several atoms at a time so that a reallocations occur only when the pool + of pre-allocated atoms has been exhausted. This is more efficient but also more + complicated and wastes memory. + 2. Giving INCHIMOL a function equivalent to std::vector::reserve so that the user can + pre-allocate exactly the number of atoms that they know they will need. This avoids + wasting memory but is even more complicated. + */ + INCHIMOL_STEREO* temp = (INCHIMOL_STEREO *) inchi_calloc(pMolecule->stereo_count + 1, sizeof(INCHIMOL_STEREO)); + if (!temp) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Out of memory"); + return -1; + } + + memcpy(temp, pMolecule->stereos, pMolecule->stereo_count * sizeof(INCHIMOL_STEREO)); + memset(&temp[pMolecule->stereo_count], 0, sizeof(INCHIMOL_STEREO)); + inchi_free(pMolecule->stereos); + pMolecule->stereos = temp; + return pMolecule->stereo_count++; +} + + +static IXA_MOL_HANDLE MOL_Pack(INCHIMOL* pMolecule) +{ + return (IXA_MOL_HANDLE)pMolecule; +} + + +INCHIMOL* MOL_Unpack(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule) +{ + if (!hMolecule) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Illegal molecule handle detected"); + return NULL; + } + + return (INCHIMOL*)hMolecule; +} + + +IXA_ATOMID MOL_GetBondOtherAtom(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_BONDID vBond, + IXA_ATOMID vAtom) +{ + IXA_ATOMID atom1; + IXA_ATOMID atom2; + + atom1 = IXA_MOL_GetBondAtom1(hStatus, hMolecule, vBond); + if (IXA_STATUS_HasError(hStatus)) return IXA_ATOMID_INVALID; + + atom2 = IXA_MOL_GetBondAtom2(hStatus, hMolecule, vBond); + if (IXA_STATUS_HasError(hStatus)) return IXA_ATOMID_INVALID; + + if (atom1 == vAtom) + { + return atom2; + } + else if (atom2 == vAtom) + { + return atom1; + } + else + { + return IXA_ATOMID_INVALID; + } +} + + +IXA_MOL_HANDLE INCHI_DECL IXA_MOL_Create(IXA_STATUS_HANDLE hStatus) +{ + INCHIMOL* molecule = (INCHIMOL*)inchi_malloc(sizeof(INCHIMOL)); + if (!molecule) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Out of memory"); + return NULL; + } + + memset(molecule, 0, sizeof(INCHIMOL)); + return MOL_Pack(molecule); +} + + +void INCHI_DECL IXA_MOL_Destroy(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule) +{ + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return; + + MOL_Clear(molecule); + inchi_free(molecule); +} + + +void INCHI_DECL IXA_MOL_Clear(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule) +{ + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return; + + MOL_Clear(molecule); +} + + +void INCHI_DECL IXA_MOL_SetChiral(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_BOOL vChiral) +{ + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return; + + molecule->chiral = vChiral; +} + + +IXA_BOOL INCHI_DECL IXA_MOL_GetChiral(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule) +{ + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return IXA_FALSE; + + return molecule->chiral; +} + + +IXA_ATOMID INCHI_DECL IXA_MOL_CreateAtom(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule) +{ + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return IXA_ATOMID_INVALID; + + return MOL_CreateAtom(hStatus, molecule); +} + + +int INCHI_DECL IXA_MOL_GetNumAtoms(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule) +{ + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return 0; + + return molecule->atom_count; +} + + +IXA_ATOMID INCHI_DECL IXA_MOL_GetAtomId(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + int vAtomIndex) +{ + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return IXA_ATOMID_INVALID; + + if (vAtomIndex < 0) return IXA_ATOMID_INVALID; + if (vAtomIndex >= molecule->atom_count) return IXA_ATOMID_INVALID; + return MOL_PackAtom(vAtomIndex); +} + + +int INCHI_DECL IXA_MOL_GetAtomIndex(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom) +{ + int atom_index; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return -1; + + if (!MOL_UnpackAtom(hStatus, molecule, vAtom, &atom_index)) return -1; + + return atom_index; +} + + +int INCHI_DECL IXA_MOL_GetAtomNumBonds(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom) +{ + INCHIMOL_ATOM* atomptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return 0; + + atomptr = MOL_GetAtom(hStatus, molecule, vAtom); + if (!atomptr) return 0; + + return atomptr->bond_count; +} + + +IXA_BONDID INCHI_DECL IXA_MOL_GetAtomBond(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom, + int vBondIndex) +{ + INCHIMOL_ATOM* atomptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return 0; + + atomptr = MOL_GetAtom(hStatus, molecule, vAtom); + if (!atomptr) return 0; + + if ((vBondIndex < 0) || (vBondIndex >= atomptr->bond_count)) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Bond index %d is out of range", vBondIndex); + return IXA_BONDID_INVALID; + } + + return atomptr->bonds[vBondIndex]; +} + + +void INCHI_DECL IXA_MOL_SetAtomX(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom, + double vX) +{ + INCHIMOL_ATOM* atomptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return; + + atomptr = MOL_GetAtom(hStatus, molecule, vAtom); + if (!atomptr) return; + + atomptr->x = vX; +} + + +double INCHI_DECL IXA_MOL_GetAtomX(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom) +{ + INCHIMOL_ATOM* atomptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return 0; + + atomptr = MOL_GetAtom(hStatus, molecule, vAtom); + if (!atomptr) return 0; + + return atomptr->x; +} + + +void INCHI_DECL IXA_MOL_SetAtomY(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom, + double vY) +{ + INCHIMOL_ATOM* atomptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return; + + atomptr = MOL_GetAtom(hStatus, molecule, vAtom); + if (!atomptr) return; + + atomptr->y = vY; +} + + +double INCHI_DECL IXA_MOL_GetAtomY(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom) +{ + INCHIMOL_ATOM* atomptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return 0; + + atomptr = MOL_GetAtom(hStatus, molecule, vAtom); + if (!atomptr) return 0; + + return atomptr->y; +} + + +void INCHI_DECL IXA_MOL_SetAtomZ(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom, + double vZ) +{ + INCHIMOL_ATOM* atomptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return; + + atomptr = MOL_GetAtom(hStatus, molecule, vAtom); + if (!atomptr) return; + + atomptr->z = vZ; +} + + +double INCHI_DECL IXA_MOL_GetAtomZ(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom) +{ + INCHIMOL_ATOM* atomptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return 0; + + atomptr = MOL_GetAtom(hStatus, molecule, vAtom); + if (!atomptr) return 0; + + return atomptr->z; +} + + +void INCHI_DECL IXA_MOL_SetAtomElement(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom, + const char* pElement) +{ + int atomic_number; + INCHIMOL_ATOM* atomptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return; + + atomptr = MOL_GetAtom(hStatus, molecule, vAtom); + if (!atomptr) return; + + for (atomic_number = 1; atomic_number < (sizeof(Elements) / sizeof(Elements[0])); atomic_number++) + { + if (strcmp(pElement, Elements[atomic_number]) == 0) + { + atomptr->atomic_number = atomic_number; + return; + } + } + /*^^^ Fixed original issue with D, T. -IPl */ + if ( !strcmp(pElement, "D") || !strcmp(pElement, "T") ) + { + atomptr->atomic_number = 1; + return; + } + + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Element name %s is not recognised", pElement); +} + + +const char* INCHI_DECL IXA_MOL_GetAtomElement(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom) +{ + INCHIMOL_ATOM* atomptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return NULL; + + atomptr = MOL_GetAtom(hStatus, molecule, vAtom); + if (!atomptr) return NULL; + + return Elements[atomptr->atomic_number]; +} + + +void INCHI_DECL IXA_MOL_SetAtomAtomicNumber(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom, + int vAtomicNumber) +{ + INCHIMOL_ATOM* atomptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return; + + atomptr = MOL_GetAtom(hStatus, molecule, vAtom); + if (!atomptr) return; + + if ((vAtomicNumber < 1) || (vAtomicNumber >= sizeof(Elements) / sizeof(Elements[0]))) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Atomic number %d is out of range", vAtomicNumber); + return; + } + + atomptr->atomic_number = vAtomicNumber; +} + + +int INCHI_DECL IXA_MOL_GetAtomAtomicNumber(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom) +{ + INCHIMOL_ATOM* atomptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return 0; + + atomptr = MOL_GetAtom(hStatus, molecule, vAtom); + if (!atomptr) return 0; + + return atomptr->atomic_number; +} + + +void INCHI_DECL IXA_MOL_SetAtomHydrogens(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom, + int vHydrogenIsotope, + int vHydrogenCount) +{ + INCHIMOL_ATOM* atomptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return; + + atomptr = MOL_GetAtom(hStatus, molecule, vAtom); + if (!atomptr) return; + + switch (vHydrogenIsotope) + { + case 0: + if (vHydrogenCount < -1) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, + "Implicit hydrogen count may not be negative unless it is -1, meaning deduce from valency"); + return; + } + break; + case 1: + case 2: + case 3: + if (vHydrogenCount < 0) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, + "Implicit protium, dueterium and tritium counts may not be negative"); + return; + } + break; + default: + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Hydrogen isotope %d is not recognised", vHydrogenIsotope); + return; + } + + + atomptr->hydrogens[vHydrogenIsotope] = vHydrogenCount; +} + + +int INCHI_DECL IXA_MOL_GetAtomHydrogens(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom, + int vHydrogenIsotope) +{ + INCHIMOL_ATOM* atomptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return 0; + + atomptr = MOL_GetAtom(hStatus, molecule, vAtom); + if (!atomptr) return 0; + + switch (vHydrogenIsotope) + { + case 0: + case 1: + case 2: + case 3: + break; + default: + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Hydrogen isotope %d is not recognised", vHydrogenIsotope); + return 0; + } + + return atomptr->hydrogens[vHydrogenIsotope]; +} + + +void INCHI_DECL IXA_MOL_SetAtomMass(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom, + int vMassNumber) +{ + INCHIMOL_ATOM* atomptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return; + + atomptr = MOL_GetAtom(hStatus, molecule, vAtom); + if (!atomptr) return; + + if (vMassNumber < 0) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Atom mass may not be negative"); + return; + } +/* @@@ Fix 2016-03-01: account for old convention and do not check uncheckable + TODO: return back a check, also move that Molfile-origin play with + ISOTOPIC_SHIFT_FLAG to a point just before calling GetINCHI() */ +#if 0 + if ( vMassNumber < ISOTOPIC_SHIFT_FLAG ) + { + if (vMassNumber > 400) /* The heaviest known element is 294Uuo */ + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Atom mass is too large"); + return; + } + } +#endif +/* Endfix */ + + atomptr->mass = vMassNumber; +} + + +int INCHI_DECL IXA_MOL_GetAtomMass(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom) +{ + INCHIMOL_ATOM* atomptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return 0; + + atomptr = MOL_GetAtom(hStatus, molecule, vAtom); + if (!atomptr) return 0; + + return atomptr->mass; +} + + +void INCHI_DECL IXA_MOL_SetAtomRadical(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom, + IXA_ATOM_RADICAL vRadical) +{ + INCHIMOL_ATOM* atomptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return; + + atomptr = MOL_GetAtom(hStatus, molecule, vAtom); + if (!atomptr) return; + + atomptr->radical = vRadical; +} + + +IXA_ATOM_RADICAL INCHI_DECL IXA_MOL_GetAtomRadical(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom) +{ + INCHIMOL_ATOM* atomptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return IXA_ATOM_RADICAL_NONE; + + atomptr = MOL_GetAtom(hStatus, molecule, vAtom); + if (!atomptr) return IXA_ATOM_RADICAL_NONE; + + return atomptr->radical; +} + + +void INCHI_DECL IXA_MOL_SetAtomCharge(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom, + int vCharge) +{ + INCHIMOL_ATOM* atomptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return; + + atomptr = MOL_GetAtom(hStatus, molecule, vAtom); + if (!atomptr) return; + + atomptr->charge = vCharge; +} + + +int INCHI_DECL IXA_MOL_GetAtomCharge(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom) +{ + INCHIMOL_ATOM* atomptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return 0; + + atomptr = MOL_GetAtom(hStatus, molecule, vAtom); + if (!atomptr) return 0; + + return atomptr->charge; +} + + +IXA_BONDID INCHI_DECL IXA_MOL_CreateBond(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom1, + IXA_ATOMID vAtom2) +{ + IXA_BONDID bond; + INCHIMOL_BOND* temp; + INCHIMOL_ATOM* atom1ptr; + INCHIMOL_ATOM* atom2ptr; + int bond_index; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return IXA_BONDID_INVALID; + + atom1ptr = MOL_GetAtom(hStatus, molecule, vAtom1); + if (!atom1ptr) return IXA_BONDID_INVALID; + + atom2ptr = MOL_GetAtom(hStatus, molecule, vAtom2); + if (!atom2ptr) return IXA_BONDID_INVALID; + + if (atom1ptr->bond_count >= sizeof(atom1ptr->bonds) / sizeof(atom1ptr->bonds[0])) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Atom has too many bonds"); + return IXA_BONDID_INVALID; + } + + if (atom2ptr->bond_count >= sizeof(atom2ptr->bonds) / sizeof(atom2ptr->bonds[0])) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Atom has too many bonds"); + return IXA_BONDID_INVALID; + } + + if (vAtom1 == vAtom2) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "A bond cannot join an atom to itself"); + return IXA_BONDID_INVALID; + } + + for (bond_index = 0; bond_index < atom1ptr->bond_count; bond_index++) + { + temp = MOL_GetBond(hStatus, molecule, atom1ptr->bonds[bond_index]); + if (vAtom2 == temp->atom2) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "A bond already exists between the atoms"); + return IXA_BONDID_INVALID; + } + } + + /* + Reallocate the bonds array to make space for one more bond. Doing things this way means + that a reallocation occurs every time MOL_CreateBond is called, which is inefficient. + Alternatives include: + 1. Allocating several bonds at a time so that a reallocations occur only when the pool + of pre-allocated bonds has been exhausted. This is more efficient but also more + complicated and wastes memory. + 2. Giving INCHIMOL a function equivalent to std::vector::reserve so that the user can + pre-allocate exactly the number of bonds that they know they will need. This avoids + wasting memory but is even more complicated. + */ + temp = (INCHIMOL_BOND *) inchi_calloc(molecule->bond_count + 1, sizeof(INCHIMOL_BOND)); + if (!temp) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Out of memory"); + return IXA_BONDID_INVALID; + } + + memcpy(temp, molecule->bonds, molecule->bond_count * sizeof(INCHIMOL_BOND)); + inchi_free(molecule->bonds); + molecule->bonds = temp; + + molecule->bonds[molecule->bond_count].atom1 = vAtom1; + molecule->bonds[molecule->bond_count].atom2 = vAtom2; + molecule->bonds[molecule->bond_count].type = IXA_BOND_TYPE_SINGLE; + molecule->bonds[molecule->bond_count].config = IXA_DBLBOND_CONFIG_PERCEIVE; + molecule->bonds[molecule->bond_count].wedge_from_atom1 = IXA_BOND_WEDGE_NONE; + molecule->bonds[molecule->bond_count].wedge_from_atom2 = IXA_BOND_WEDGE_NONE; + + bond = MOL_PackBond(molecule->bond_count++); + atom1ptr->bonds[atom1ptr->bond_count] = bond; + atom1ptr->bond_count++; + atom2ptr->bonds[atom2ptr->bond_count] = bond; + atom2ptr->bond_count++; + return bond; +} + + +int INCHI_DECL IXA_MOL_GetNumBonds(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule) +{ + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return 0; + + return molecule->bond_count; +} + + +IXA_BONDID INCHI_DECL IXA_MOL_GetBondId(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + int vBondIndex) +{ + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return IXA_BONDID_INVALID; + + if (vBondIndex < 0) return IXA_BONDID_INVALID; + if (vBondIndex >= molecule->bond_count) return IXA_BONDID_INVALID; + return MOL_PackBond(vBondIndex); +} + + +int INCHI_DECL IXA_MOL_GetBondIndex(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_BONDID vBond) +{ + int bond_index; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return -1; + + if (!MOL_UnpackBond(hStatus, molecule, vBond, &bond_index)) return -1; + + return bond_index; +} + + +EXPIMP_TEMPLATE INCHI_API IXA_ATOMID INCHI_DECL IXA_MOL_GetBondAtom1(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_BONDID vBond) +{ + INCHIMOL_BOND* bondptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return IXA_ATOMID_INVALID; + + bondptr = MOL_GetBond(hStatus, molecule, vBond); + if (!bondptr) return IXA_ATOMID_INVALID; + + return bondptr->atom1; +} + + +EXPIMP_TEMPLATE INCHI_API IXA_ATOMID INCHI_DECL IXA_MOL_GetBondAtom2(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_BONDID vBond) +{ + INCHIMOL_BOND* bondptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return IXA_ATOMID_INVALID; + + bondptr = MOL_GetBond(hStatus, molecule, vBond); + if (!bondptr) return IXA_ATOMID_INVALID; + + return bondptr->atom2; +} + + +void INCHI_DECL IXA_MOL_SetBondType(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_BONDID vBond, + IXA_BOND_TYPE vType) +{ + INCHIMOL_BOND* bondptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return; + + bondptr = MOL_GetBond(hStatus, molecule, vBond); + if (!bondptr) return; + + bondptr->type = vType; +} + + +EXPIMP_TEMPLATE INCHI_API IXA_BOND_TYPE INCHI_DECL IXA_MOL_GetBondType(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_BONDID vBond) +{ + INCHIMOL_BOND* bondptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return IXA_BOND_TYPE_SINGLE; + + bondptr = MOL_GetBond(hStatus, molecule, vBond); + if (!bondptr) return IXA_BOND_TYPE_SINGLE; + + return bondptr->type; +} + + +void INCHI_DECL IXA_MOL_SetBondWedge(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_BONDID vBond, + IXA_ATOMID vRefAtom, + IXA_BOND_WEDGE vDirection) +{ + INCHIMOL_BOND* bondptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return; + + bondptr = MOL_GetBond(hStatus, molecule, vBond); + if (!bondptr) return; + + if (vRefAtom == bondptr->atom1) + { + bondptr->wedge_from_atom1 = vDirection; + } + else if (vRefAtom == bondptr->atom2) + { + bondptr->wedge_from_atom2 = vDirection; + } + else + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Reference atom is illegal"); + return; + } +} + + +IXA_BOND_WEDGE INCHI_DECL IXA_MOL_GetBondWedge(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_BONDID vBond, + IXA_ATOMID vRefAtom) +{ + INCHIMOL_BOND* bondptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return IXA_BOND_WEDGE_NONE; + + bondptr = MOL_GetBond(hStatus, molecule, vBond); + if (!bondptr) return IXA_BOND_WEDGE_NONE; + + if (vRefAtom == bondptr->atom1) + { + return bondptr->wedge_from_atom1; + } + else if (vRefAtom == bondptr->atom2) + { + return bondptr->wedge_from_atom2; + } + else + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Reference atom is illegal"); + return IXA_BOND_WEDGE_NONE; + } +} + + +void INCHI_DECL IXA_MOL_SetDblBondConfig(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_BONDID vBond, + IXA_DBLBOND_CONFIG vConfig) +{ + INCHIMOL_BOND* bondptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return; + + bondptr = MOL_GetBond(hStatus, molecule, vBond); + if (!bondptr) return; + + bondptr->config = vConfig; +} + + +IXA_DBLBOND_CONFIG INCHI_DECL IXA_MOL_GetDblBondConfig(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_BONDID vBond) +{ + INCHIMOL_BOND* bondptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return IXA_DBLBOND_CONFIG_PERCEIVE; + + bondptr = MOL_GetBond(hStatus, molecule, vBond); + if (!bondptr) return IXA_DBLBOND_CONFIG_PERCEIVE; + + return bondptr->config; +} + + +IXA_BONDID INCHI_DECL IXA_MOL_GetCommonBond(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom1, + IXA_ATOMID vAtom2) +{ + INCHIMOL_ATOM* atom1ptr; + INCHIMOL_ATOM* atom2ptr; + IXA_BONDID bond1; + int bond1_index; + int bond2_index; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return IXA_BONDID_INVALID; + + atom1ptr = MOL_GetAtom(hStatus, molecule, vAtom1); + if (!atom1ptr) return IXA_BONDID_INVALID; + + atom2ptr = MOL_GetAtom(hStatus, molecule, vAtom2); + if (!atom2ptr) return IXA_BONDID_INVALID; + + for (bond1_index = 0; bond1_index < atom1ptr->bond_count; bond1_index++) + { + bond1 = atom1ptr->bonds[bond1_index]; + for (bond2_index = 0; bond2_index < atom2ptr->bond_count; bond2_index++) + { + if (atom2ptr->bonds[bond2_index] == bond1) + { + return bond1; + } + } + } + return IXA_BONDID_INVALID; +} + + +IXA_STEREOID INCHI_DECL IXA_MOL_CreateStereoTetrahedron(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vCentralAtom, + IXA_ATOMID vVertex1, + IXA_ATOMID vVertex2, + IXA_ATOMID vVertex3, + IXA_ATOMID vVertex4) +{ + int stereo_index; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return IXA_STEREOID_INVALID; + + stereo_index = MOL_CreateStereo(hStatus, molecule); + if (stereo_index == -1) return IXA_STEREOID_INVALID; + + molecule->stereos[stereo_index].topology = IXA_STEREO_TOPOLOGY_TETRAHEDRON; + molecule->stereos[stereo_index].central_entity = vCentralAtom; + molecule->stereos[stereo_index].vertices[0] = vVertex1; + molecule->stereos[stereo_index].vertices[1] = vVertex2; + molecule->stereos[stereo_index].vertices[2] = vVertex3; + molecule->stereos[stereo_index].vertices[3] = vVertex4; + molecule->stereos[stereo_index].parity = IXA_STEREO_PARITY_NONE; + return MOL_PackStereo(stereo_index); +} + + +IXA_STEREOID INCHI_DECL IXA_MOL_CreateStereoRectangle(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_BONDID vCentralBond, + IXA_ATOMID vVertex1, + IXA_ATOMID vVertex2, + IXA_ATOMID vVertex3, + IXA_ATOMID vVertex4) +{ + int stereo_index; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return IXA_STEREOID_INVALID; + + stereo_index = MOL_CreateStereo(hStatus, molecule); + if (stereo_index == -1) return IXA_STEREOID_INVALID; + + molecule->stereos[stereo_index].topology = IXA_STEREO_TOPOLOGY_RECTANGLE; + molecule->stereos[stereo_index].central_entity = vCentralBond; + molecule->stereos[stereo_index].vertices[0] = vVertex1; + molecule->stereos[stereo_index].vertices[1] = vVertex2; + molecule->stereos[stereo_index].vertices[2] = vVertex3; + molecule->stereos[stereo_index].vertices[3] = vVertex4; + molecule->stereos[stereo_index].parity = IXA_STEREO_PARITY_NONE; + return MOL_PackStereo(stereo_index); +} + + +IXA_STEREOID INCHI_DECL IXA_MOL_CreateStereoAntiRectangle(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vCentralAtom, + IXA_ATOMID vVertex1, + IXA_ATOMID vVertex2, + IXA_ATOMID vVertex3, + IXA_ATOMID vVertex4) +{ + int stereo_index; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return IXA_STEREOID_INVALID; + + stereo_index = MOL_CreateStereo(hStatus, molecule); + if (stereo_index == -1) return IXA_STEREOID_INVALID; + + molecule->stereos[stereo_index].topology = IXA_STEREO_TOPOLOGY_ANTIRECTANGLE; + molecule->stereos[stereo_index].central_entity = vCentralAtom; + molecule->stereos[stereo_index].vertices[0] = vVertex1; + molecule->stereos[stereo_index].vertices[1] = vVertex2; + molecule->stereos[stereo_index].vertices[2] = vVertex3; + molecule->stereos[stereo_index].vertices[3] = vVertex4; + molecule->stereos[stereo_index].parity = IXA_STEREO_PARITY_NONE; + return MOL_PackStereo(stereo_index); +} + + +int INCHI_DECL IXA_MOL_GetNumStereos(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule) +{ + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return 0; + + return molecule->stereo_count; +} + + +IXA_STEREOID INCHI_DECL IXA_MOL_GetStereoId(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + int vStereoIndex) +{ + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return IXA_STEREOID_INVALID; + + if (vStereoIndex < 0) return IXA_STEREOID_INVALID; + if (vStereoIndex >= molecule->stereo_count) return IXA_STEREOID_INVALID; + return MOL_PackStereo(vStereoIndex); +} + + +int INCHI_DECL IXA_MOL_GetStereoIndex(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_STEREOID vStereo) +{ + int stereo_index; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return -1; + + if (!MOL_UnpackStereo(hStatus, molecule, vStereo, &stereo_index)) return -1; + + return stereo_index; +} + + +IXA_STEREO_TOPOLOGY INCHI_DECL IXA_MOL_GetStereoTopology(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_STEREOID vStereo) +{ + INCHIMOL_STEREO* stereoptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return IXA_STEREO_TOPOLOGY_INVALID; + + stereoptr = MOL_GetStereo(hStatus, molecule, vStereo); + if (!stereoptr) return IXA_STEREO_TOPOLOGY_INVALID; + + return stereoptr->topology; +} + + +IXA_ATOMID INCHI_DECL IXA_MOL_GetStereoCentralAtom(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_STEREOID vStereo) +{ + INCHIMOL_STEREO* stereoptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return IXA_ATOMID_INVALID; + + stereoptr = MOL_GetStereo(hStatus, molecule, vStereo); + if (!stereoptr) return IXA_ATOMID_INVALID; + + switch (stereoptr->topology) + { + case IXA_STEREO_TOPOLOGY_TETRAHEDRON: + case IXA_STEREO_TOPOLOGY_ANTIRECTANGLE: + return ((IXA_ATOMID) stereoptr->central_entity); + case IXA_STEREO_TOPOLOGY_RECTANGLE: + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Stereo centre does not have a central atom"); + return IXA_ATOMID_INVALID; + default: + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Unexpected stereo topology"); + return IXA_ATOMID_INVALID; + } +} + + +IXA_BONDID INCHI_DECL IXA_MOL_GetStereoCentralBond(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_STEREOID vStereo) +{ + INCHIMOL_STEREO* stereoptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return IXA_BONDID_INVALID; + + stereoptr = MOL_GetStereo(hStatus, molecule, vStereo); + if (!stereoptr) return IXA_BONDID_INVALID; + + switch (stereoptr->topology) + { + case IXA_STEREO_TOPOLOGY_TETRAHEDRON: + case IXA_STEREO_TOPOLOGY_ANTIRECTANGLE: + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Stereo centre does not have a central bond"); + return IXA_BONDID_INVALID; + case IXA_STEREO_TOPOLOGY_RECTANGLE: + return ((IXA_BONDID) stereoptr->central_entity); + default: + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Unexpected stereo topology"); + return IXA_BONDID_INVALID; + } +} + + +int INCHI_DECL IXA_MOL_GetStereoNumVertices(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_STEREOID vStereo) +{ + INCHIMOL_STEREO* stereoptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return 0; + + stereoptr = MOL_GetStereo(hStatus, molecule, vStereo); + if (!stereoptr) return 0; + + return GetVertexCount(hStatus, stereoptr->topology); +} + + +IXA_ATOMID INCHI_DECL IXA_MOL_GetStereoVertex(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_STEREOID vStereo, + int vVertexIndex) +{ + int vertex_count; + INCHIMOL_STEREO* stereoptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return IXA_ATOMID_INVALID; + + stereoptr = MOL_GetStereo(hStatus, molecule, vStereo); + if (!stereoptr) return IXA_ATOMID_INVALID; + + vertex_count = GetVertexCount(hStatus, stereoptr->topology); + if (IXA_STATUS_HasError(hStatus)) return IXA_ATOMID_INVALID; + + if ((vVertexIndex < 0) || (vVertexIndex >= vertex_count)) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Vertex index is out of range"); + return IXA_ATOMID_INVALID; + } + + return stereoptr->vertices[vVertexIndex]; +} + + +INCHI_API void INCHI_DECL IXA_MOL_SetStereoParity(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_STEREOID vStereo, + IXA_STEREO_PARITY vParity) +{ + INCHIMOL_STEREO* stereoptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return; + + stereoptr = MOL_GetStereo(hStatus, molecule, vStereo); + if (!stereoptr) return; + + stereoptr->parity = vParity; +} + + +IXA_STEREO_PARITY INCHI_DECL IXA_MOL_GetStereoParity(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_STEREOID vStereo) +{ + INCHIMOL_STEREO* stereoptr; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return IXA_STEREO_PARITY_NONE; + + stereoptr = MOL_GetStereo(hStatus, molecule, vStereo); + if (!stereoptr) return IXA_STEREO_PARITY_NONE; + + return stereoptr->parity; +} + + +/* + Extended input supporting v. 1.05 extensions: V3000; polymers + This is a later addition to native IXA intended at this time + just to be a carrier which transports extension data to GetINCHI() +*/ + +#include "../../../../INCHI_BASE/src/mol_fmt.h" + +int IXA_MOL_SetExtMoldataByMolfileExtInput( IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + MOL_FMT_DATA* mfdata) +{ + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) return 0; + + { + int k, m, err = 0; + int nsgroups = mfdata->ctab.sgroups.used; + + /* Polymers */ + if ( nsgroups > 0 ) + { + /* Prepare OrigAtDataPolymer container */ + molecule->polymer = (INCHIMOL_POLYMER *) inchi_calloc( 1, sizeof(INCHIMOL_POLYMER) ); + if ( !molecule->polymer ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Polymer data error, no memory "); + return IXA_EXT_POLYMER_INVALID; + } + + molecule->polymer->units = (INCHIMOL_SGROUP**) inchi_calloc( nsgroups, sizeof(molecule->polymer->units[0]) ); + if ( !molecule->polymer->units ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Polymer data error, no memory "); + return IXA_EXT_POLYMER_INVALID; + } + memset( molecule->polymer->units, 0, sizeof( *(molecule->polymer->units) ) ); + + molecule->polymer->n = nsgroups; + + for (k=0; kctab.sgroups.group[k]; + + INCHIMOL_SGROUP* unitk = molecule->polymer->units[k] = (INCHIMOL_SGROUP*) inchi_calloc( 1, sizeof(INCHIMOL_SGROUP) ); + if (!unitk ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Polymer data error, no memory "); + return IXA_EXT_POLYMER_INVALID; + } + memset( unitk, 0, sizeof( *unitk ) ); + + unitk->id = groupk->id; + unitk->type = groupk->type; + unitk->subtype = groupk->subtype; + unitk->conn = groupk->conn; + unitk->label = groupk->label; + + for (q=0; q<4; q++) + { + unitk->xbr1[q] = groupk->xbr1[q]; + unitk->xbr2[q] = groupk->xbr2[q]; + } + strcpy( unitk->smt, groupk->smt ); + unitk->na = groupk->alist.used; + unitk->alist = (int *) inchi_calloc( unitk->na, sizeof(int) ); + if (!unitk->alist ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Polymer data error, no memory"); + return IXA_EXT_POLYMER_INVALID; + } + for (m=0; mna; m++) + { + unitk->alist[m] = groupk->alist.item[m]; + } + unitk->nb = groupk->blist.used; + if ( unitk->nb > 0 ) + { + unitk->blist = (int *) inchi_calloc( 2*unitk->nb, sizeof(int) ); + if (!unitk->blist ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Polymer data error, no memory"); + return IXA_EXT_POLYMER_INVALID; + } + for (m=0; m < groupk->blist.used; m++) + { + int ib, ia1, ia2; + ib = groupk->blist.item[m]; + if ( ib<1 || ib>mfdata->ctab.n_bonds ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Polymer unit refers to invalid bond"); + return IXA_EXT_POLYMER_INVALID; + } + ia1 = mfdata->ctab.bonds[ib-1].atnum1; + ia2 = mfdata->ctab.bonds[ib-1].atnum2; + unitk->blist[2*m] = ia1; + unitk->blist[2*m+1] = ia2; + if ( !strcmp(mfdata->ctab.atoms[ia1-1].symbol,"H") || + !strcmp(mfdata->ctab.atoms[ia2-1].symbol,"H") ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Hydrogen as polymer end group is not supported"); + return IXA_EXT_POLYMER_INVALID; + } + } + } + else + unitk->blist = NULL; + } + } + + /* V3000 Extensions */ + if ( mfdata->ctab.v3000 ) + { + int m, k, nn; + MOL_FMT_v3000 *mpv = mfdata->ctab.v3000; + + molecule->v3000 = (INCHIMOL_V3000 *) inchi_calloc( 1, sizeof(INCHIMOL_V3000) ); + if ( !molecule->v3000 ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "V3000 data error, no memory"); + return IXA_EXT_V3000_INVALID; + } + memset( molecule->v3000, 0, sizeof( *molecule->v3000 ) ); + + + molecule->v3000->n_collections = mpv->n_collections; + molecule->v3000->n_haptic_bonds = mpv->n_haptic_bonds; + molecule->v3000->n_non_haptic_bonds = mpv->n_non_haptic_bonds; + molecule->v3000->n_sgroups = mpv->n_sgroups; + molecule->v3000->n_non_star_atoms = mpv->n_non_star_atoms; + molecule->v3000->n_star_atoms = mpv->n_star_atoms; + molecule->v3000->n_steabs = mpv->n_steabs; + molecule->v3000->n_sterac = mpv->n_sterac; + molecule->v3000->n_sterel = mpv->n_sterel; + molecule->v3000->n_3d_constraints = mpv->n_3d_constraints; + + if ( mpv->atom_index_orig ) + { + molecule->v3000->atom_index_orig = (int *) inchi_calloc( mfdata->ctab.n_atoms, sizeof(int) ); + if ( NULL==molecule->v3000->atom_index_orig ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "V3000 data error, no memory"); + return IXA_EXT_V3000_INVALID; + } + memcpy( molecule->v3000->atom_index_orig, mpv->atom_index_orig, mfdata->ctab.n_atoms); + } + if ( mpv->atom_index_fin ) + { + molecule->v3000->atom_index_fin = (int *) inchi_calloc( mfdata->ctab.n_atoms, sizeof(int) ); + if ( NULL==molecule->v3000->atom_index_fin ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "V3000 data error, no memory"); + return IXA_EXT_V3000_INVALID; + } + memcpy( molecule->v3000->atom_index_fin, mpv->atom_index_fin, mfdata->ctab.n_atoms); + } + if ( mpv->n_haptic_bonds && mpv->haptic_bonds ) + { + molecule->v3000->lists_haptic_bonds = (int **) calloc( mpv->n_haptic_bonds, sizeof (int*) ); + if ( NULL==molecule->v3000->lists_haptic_bonds ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "V3000 data error, no memory"); + return IXA_EXT_V3000_INVALID; + } + for (m=0; mn_haptic_bonds; m++) + { + int *lst=NULL; + int *mol_lst = mpv->haptic_bonds->lists[m]; + nn = mol_lst[2] + 3; + lst = molecule->v3000->lists_haptic_bonds[m] = (int *) calloc( nn, sizeof (int) ); + if ( NULL==lst ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "V3000 data error, no memory"); + return IXA_EXT_V3000_INVALID; + } + for (k=0; kn_steabs && mpv->steabs ) + { + molecule->v3000->lists_steabs = (int **) calloc( mpv->n_steabs, sizeof (int*) ); + if ( NULL==molecule->v3000->lists_steabs ) + { err = 16; goto exitf; } + for (m=0; mn_steabs; m++) + { + int *lst=NULL; + int *mol_lst = mpv->steabs->lists[m]; + nn = mol_lst[1] + 2; + lst = molecule->v3000->lists_steabs[m] = (int *) calloc( nn, sizeof (int) ); + if ( NULL==lst ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "V3000 data error, no memory"); + return IXA_EXT_V3000_INVALID; + } + for (k=0; kn_sterac && mpv->sterac ) + { + molecule->v3000->lists_sterac = (int **) calloc( mpv->n_sterac, sizeof (int*) ); + if ( NULL==molecule->v3000->lists_sterac ) + { err = 16; goto exitf; } + for (m=0; mn_sterac; m++) + { + int *lst=NULL; + int *mol_lst = mpv->sterac->lists[m]; + nn = mol_lst[1] + 2; + lst = molecule->v3000->lists_sterac[m] = (int *) calloc( nn, sizeof (int) ); + if ( NULL==lst ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "V3000 data error, no memory"); + return IXA_EXT_V3000_INVALID; + } + for (k=0; kn_sterel && mpv->sterel ) + { + molecule->v3000->lists_sterel = (int **) calloc( mpv->n_sterel, sizeof (int*) ); + if ( NULL==molecule->v3000->lists_sterel ) + { err = 16; goto exitf; } + for (m=0; mn_sterel; m++) + { + int *lst=NULL; + int *mol_lst = mpv->sterel->lists[m]; + nn = mol_lst[1] + 2; + lst = molecule->v3000->lists_sterel[m] = (int *) calloc( nn, sizeof (int) ); + if ( NULL==lst ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "V3000 data error, no memory"); + return IXA_EXT_V3000_INVALID; + } + for (k=0; kpolymer, molecule->v3000 ); + + return err; + } +} + + +void IXA_MOL_ClearExtMolData( INCHIMOL_POLYMER *pd, INCHIMOL_V3000 *v3k ) +{ + int k; + + + /* Polymers */ + if ( pd ) + { + if ( pd->n && pd->units ) + { + int k; + for (k=0; kn; k++) + { + if ( pd->units[k] ) + { + if ( pd->units[k]->alist ) + { inchi_free( pd->units[k]->alist ); pd->units[k]->alist = NULL; } + if ( pd->units[k]->blist ) + { inchi_free( pd->units[k]->blist ); pd->units[k]->blist = NULL; } + } + inchi_free( pd->units[k] ); + } + inchi_free( pd->units ); + pd->units = NULL; + pd->n = 0; + } + free( pd ); + pd = NULL; + } + + /* V3000 Extensions */ + if ( v3k ) + { + if ( v3k->atom_index_orig ) + { + inchi_free( v3k->atom_index_orig ); + v3k->atom_index_orig = NULL; + } + if ( v3k->atom_index_fin ) + { + inchi_free( v3k->atom_index_fin ); + v3k->atom_index_fin = NULL; + } + if ( v3k->n_haptic_bonds && v3k->lists_haptic_bonds ) + { + for (k=0; kn_haptic_bonds; k++) + if ( v3k->lists_haptic_bonds[k] ) + { + inchi_free( v3k->lists_haptic_bonds[k] ); + v3k->lists_haptic_bonds[k] = NULL; + } + inchi_free( v3k->lists_haptic_bonds ); + v3k->lists_haptic_bonds = NULL; + } + if ( v3k->n_steabs && v3k->lists_steabs ) + { + for (k=0; kn_steabs; k++) + if ( v3k->lists_steabs[k] ) + { + inchi_free( v3k->lists_steabs[k] ); + v3k->lists_steabs[k] = NULL; + } + inchi_free( v3k->lists_steabs ); + v3k->lists_steabs = NULL; + } + if ( v3k->n_sterel && v3k->lists_sterel ) + { + for (k=0; kn_sterel; k++) + if ( v3k->lists_sterel[k] ) + { + inchi_free( v3k->lists_sterel[k] ); + v3k->lists_sterel[k] = NULL; + } + inchi_free( v3k->lists_sterel ); + v3k->lists_sterel = NULL; + } + if ( v3k->n_sterac && v3k->lists_sterac ) + { + for (k=0; kn_sterac; k++) + if ( v3k->lists_sterac[k] ) + { + inchi_free( v3k->lists_sterac[k] ); + v3k->lists_sterac[k] = NULL; + } + inchi_free( v3k->lists_sterac ); + v3k->lists_sterac = NULL; + } + /*memset( v3k, 0, sizeof( v3k ) );*/ + free( v3k ); + v3k = NULL; + } + + return; +} + + +int IXA_MOL_SetExtMolDataByInChIExtInput( IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + inchi_Output_Polymer *polymer, + inchi_Output_V3000 *v3000, + int nat ) +{ +int n, k, m, err = 0; + + INCHIMOL* molecule = MOL_Unpack(hStatus, hMolecule); + if (!molecule) + return 0; + + if ( polymer && polymer->n ) + { + n = polymer->n; + nat = molecule->atom_count; + + /* Polymers */ + if ( n > 0 ) + { + /* Prepare OrigAtDataPolymer container */ + molecule->polymer = (INCHIMOL_POLYMER *) inchi_calloc( 1, sizeof(INCHIMOL_POLYMER) ); + if ( !molecule->polymer ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Polymer data error, no memory "); + return IXA_EXT_POLYMER_INVALID; + } + + molecule->polymer->units = (INCHIMOL_SGROUP**) inchi_calloc( n, sizeof(molecule->polymer->units[0]) ); + if ( !molecule->polymer->units ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Polymer data error, no memory "); + return IXA_EXT_POLYMER_INVALID; + } + memset( molecule->polymer->units, 0, sizeof( *(molecule->polymer->units) ) ); + + molecule->polymer->n = n; + + for (k=0; kunits[k]; + INCHIMOL_SGROUP* unitk = molecule->polymer->units[k] = (INCHIMOL_SGROUP*) inchi_calloc( 1, sizeof(INCHIMOL_SGROUP) ); + if (!unitk ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Polymer data error, no memory "); + return IXA_EXT_POLYMER_INVALID; + } + memset( unitk, 0, sizeof( *unitk ) ); + + unitk->id = groupk->id; + unitk->type = groupk->type; + unitk->subtype = groupk->subtype; + unitk->conn = groupk->conn; + unitk->label = groupk->label; + + for (q=0; q<4; q++) + { + unitk->xbr1[q] = groupk->xbr1[q]; + unitk->xbr2[q] = groupk->xbr2[q]; + } + strcpy( unitk->smt, groupk->smt ); + unitk->na = groupk->na; + unitk->alist = (int *) inchi_calloc( unitk->na, sizeof(int) ); + if (!unitk->alist ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Polymer data error, no memory"); + return IXA_EXT_POLYMER_INVALID; + } + for (m=0; mna; m++) + unitk->alist[m] = groupk->alist[m]; + unitk->nb = groupk->nb; + if ( unitk->nb > 0 ) + { + unitk->blist = (int *) inchi_calloc( 2*unitk->nb, sizeof(int) ); + if (!unitk->blist ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Polymer data error, no memory"); + return IXA_EXT_POLYMER_INVALID; + } + for (m=0; m < 2*groupk->nb; m++) + unitk->blist[m] = groupk->blist[m]; + } + else + unitk->blist = NULL; + } + } + } + + /* V3000 Extensions */ + if ( v3000 ) + { + int m, k, nn; + + molecule->v3000 = (INCHIMOL_V3000 *) inchi_calloc( 1, sizeof(INCHIMOL_V3000) ); + + if ( !molecule->v3000 ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "V3000 data error, no memory"); + return IXA_EXT_V3000_INVALID; + } + memset( molecule->v3000, 0, sizeof( *molecule->v3000 ) ); + + molecule->v3000->n_collections = v3000->n_collections; + molecule->v3000->n_haptic_bonds = v3000->n_haptic_bonds; + molecule->v3000->n_non_haptic_bonds = v3000->n_non_haptic_bonds; + molecule->v3000->n_sgroups = v3000->n_sgroups; + molecule->v3000->n_non_star_atoms = v3000->n_non_star_atoms; + molecule->v3000->n_star_atoms = v3000->n_star_atoms; + molecule->v3000->n_steabs = v3000->n_steabs; + molecule->v3000->n_sterac = v3000->n_sterac; + molecule->v3000->n_sterel = v3000->n_sterel; + molecule->v3000->n_3d_constraints = v3000->n_3d_constraints; + + if ( v3000->atom_index_orig ) + { + molecule->v3000->atom_index_orig = (int *) inchi_calloc( nat, sizeof(int) ); + if ( NULL==molecule->v3000->atom_index_orig ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "V3000 data error, no memory"); + return IXA_EXT_V3000_INVALID; + } + memcpy( molecule->v3000->atom_index_orig, v3000->atom_index_orig, nat); + } + if ( v3000->atom_index_fin ) + { + molecule->v3000->atom_index_fin = (int *) inchi_calloc( nat, sizeof(int) ); + if ( NULL==molecule->v3000->atom_index_fin ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "V3000 data error, no memory"); + return IXA_EXT_V3000_INVALID; + } + memcpy( molecule->v3000->atom_index_fin, v3000->atom_index_fin, nat); + } + if ( v3000->n_haptic_bonds && v3000->lists_haptic_bonds ) + { + molecule->v3000->lists_haptic_bonds = (int **) calloc( v3000->n_haptic_bonds, sizeof (int*) ); + if ( NULL==molecule->v3000->lists_haptic_bonds ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "V3000 data error, no memory"); + return IXA_EXT_V3000_INVALID; + } + for (m=0; mn_haptic_bonds; m++) + { + int *lst=NULL; + int *mol_lst = v3000->lists_haptic_bonds[m]; + nn = mol_lst[2] + 3; + lst = molecule->v3000->lists_haptic_bonds[m] = (int *) calloc( nn, sizeof (int) ); + if ( NULL==lst ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "V3000 data error, no memory"); + return IXA_EXT_V3000_INVALID; + } + for (k=0; kn_steabs && v3000->lists_steabs ) + { + molecule->v3000->lists_steabs = (int **) calloc( v3000->n_steabs, sizeof (int*) ); + if ( NULL==molecule->v3000->lists_steabs ) + { err = 16; goto exitf; } + for (m=0; mn_steabs; m++) + { + int *lst=NULL; + int *mol_lst = v3000->lists_steabs[m]; + nn = mol_lst[1] + 2; + lst = molecule->v3000->lists_steabs[m] = (int *) calloc( nn, sizeof (int) ); + if ( NULL==lst ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "V3000 data error, no memory"); + return IXA_EXT_V3000_INVALID; + } + for (k=0; kn_sterac && v3000->lists_sterac ) + { + molecule->v3000->lists_sterac = (int **) calloc( v3000->n_sterac, sizeof (int*) ); + if ( NULL==molecule->v3000->lists_sterac ) + { err = 16; goto exitf; } + for (m=0; mn_sterac; m++) + { + int *lst=NULL; + int *mol_lst = v3000->lists_sterac[m]; + nn = mol_lst[1] + 2; + lst = molecule->v3000->lists_sterac[m] = (int *) calloc( nn, sizeof (int) ); + if ( NULL==lst ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "V3000 data error, no memory"); + return IXA_EXT_V3000_INVALID; + } + for (k=0; kn_sterel && v3000->lists_sterel ) + { + molecule->v3000->lists_sterel = (int **) calloc( v3000->n_sterel, sizeof (int*) ); + if ( NULL==molecule->v3000->lists_sterel ) + { err = 16; goto exitf; } + for (m=0; mn_sterel; m++) + { + int *lst=NULL; + int *mol_lst = v3000->lists_sterel[m]; + nn = mol_lst[1] + 2; + lst = molecule->v3000->lists_sterel[m] = (int *) calloc( nn, sizeof (int) ); + if ( NULL==lst ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "V3000 data error, no memory"); + return IXA_EXT_V3000_INVALID; + } + for (k=0; kpolymer, molecule->v3000 ); + + return err; +} diff --git a/INCHI-1-SRC/INCHI_API/libinchi/src/ixa/ixa_mol.h b/INCHI-1-SRC/INCHI_API/libinchi/src/ixa/ixa_mol.h new file mode 100644 index 0000000..9df7c85 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/libinchi/src/ixa/ixa_mol.h @@ -0,0 +1,171 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef __IXA_MOL_H__ +#define __IXA_MOL_H__ + +#include "../../../../INCHI_BASE/src/ixa.h" + +typedef struct +{ + double x; + double y; + double z; + int atomic_number; + int hydrogens[4]; + int mass; + IXA_ATOM_RADICAL radical; + int charge; + int bond_count; + IXA_BONDID bonds[20]; +} INCHIMOL_ATOM; + + +typedef struct +{ + IXA_ATOMID atom1; + IXA_ATOMID atom2; + IXA_BOND_TYPE type; + IXA_DBLBOND_CONFIG config; + IXA_BOND_WEDGE wedge_from_atom1; + IXA_BOND_WEDGE wedge_from_atom2; +} INCHIMOL_BOND; + + +typedef struct +{ + IXA_STEREO_TOPOLOGY topology; + IXA_ATOMID vertices[4]; /* All currently supported topologies have 4 vertices. */ + void* central_entity; /* Could be an atom or a bond, depending on topology. */ + IXA_STEREO_PARITY parity; +} INCHIMOL_STEREO; + +/************************************************************************/ +/* + Extended input supporting v. 1.05 extensions: V3000; polymers + This is a later addition to native IXA intended at this time + just to be a carrier which transports extension data to GetINCHI() +*/ +typedef struct INCHIMOL_SGROUP +{ + int id; /* it is what is called 'Sgroup number' in CTFILE */ + int type; /* type as by MDL format (STY) */ + int subtype; /* subtype as by MDL format (SST) */ + int conn; /* connection scheme as by MDL format (SCN) */ + int label; /* it is what is called 'unique Sgroup identifier' in CTFILE */ + int na; /* number of atoms in the unit */ + int nb; /* number of bonds in the unit */ + double xbr1[4]; /* bracket ends coordinates (SDI) */ + double xbr2[4]; /* bracket ends coordinates (SDI) */ + char smt[80]; /* Sgroup Subscript (SMT) */ + int *alist; /* atoms in the unit (SAL) */ + int *blist; /* bonds in the unit: as [atom1, atom2, atom1, atom2,.. ] for all bonds (as made from SBL) */ +} INCHIMOL_SGROUP; +typedef struct INCHIMOL_POLYMER +{ + INCHIMOL_SGROUP **units; /* array of pointers */ + int n; +} INCHIMOL_POLYMER; +/* Extended input supporting v. 1.05 extensions: V3000; polymers */ +typedef struct INCHIMOL_V3000 +{ + int n_non_star_atoms; + int n_star_atoms; + int *atom_index_orig; /* index as supplied for atoms */ + int *atom_index_fin; /* = index or -1 for star atom */ + int n_sgroups; /* currently, we do not use this. */ + int n_3d_constraints; /* currently, we do not use this. */ + int n_collections; + int n_non_haptic_bonds; + int n_haptic_bonds; + int **lists_haptic_bonds;/* haptic_bonds[i] is pointer to int* which contains: */ + /* bond_type, non-star atom number, nendpts, then endpts themselves */ + /* Enhanced stereo */ + int n_steabs; + int **lists_steabs; /* steabs[k][0] - not used */ + /* steabs[k][1] - number of members in collection */ + /* steabs[k][2..] - member atom numbers */ + int n_sterel; + int **lists_sterel; /* sterel[k][0] - n from "STERELn" tag */ + /* sterel[k][1] - number of members in collection */ + /* sterel[k][2..] - member atom numbers */ + int n_sterac; + int **lists_sterac; /* sterac[k][0] - n from "STERACn" tag */ + /* sterac[k][1] - number of members in collection */ + /* sterac[k][0] - number from "STERACn" tag */ +} INCHIMOL_V3000; +/************************************************************************/ + +typedef struct +{ + int atom_count; + INCHIMOL_ATOM *atoms; + + int bond_count; + INCHIMOL_BOND* bonds; + + int stereo_count; + INCHIMOL_STEREO *stereos; + + INCHIMOL_POLYMER *polymer; + INCHIMOL_V3000 *v3000; + + IXA_BOOL chiral; +} INCHIMOL; + + +INCHIMOL* MOL_Unpack(IXA_STATUS_HANDLE hStatus, IXA_MOL_HANDLE HMol); + +IXA_ATOMID MOL_GetBondOtherAtom(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_BONDID vBond, + IXA_ATOMID vAtom); + +/****************************************************************************/ +/* Functions for treating extended input/output */ +int IXA_MOL_SetExtMolDataByInChIExtInput( IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + inchi_Output_Polymer *polymer, + inchi_Output_V3000 *v3000, + int nat ); + +void IXA_MOL_ClearExtMolData( INCHIMOL_POLYMER *pd, INCHIMOL_V3000 *v3k ); + + +#endif diff --git a/INCHI-1-SRC/INCHI_API/libinchi/src/ixa/ixa_read_inchi.c b/INCHI-1-SRC/INCHI_API/libinchi/src/ixa/ixa_read_inchi.c new file mode 100644 index 0000000..040884c --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/libinchi/src/ixa/ixa_read_inchi.c @@ -0,0 +1,480 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include "string.h" + +#include "../../../../INCHI_BASE/src/mode.h" +#include "../../../../INCHI_BASE/src/inchi_api.h" +#include "../../../../INCHI_BASE/src/util.h" +#include "ixa_mol.h" +#include "ixa_status.h" + + + +static IXA_ATOMID AnalyseInternalVertex(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vVertex, + IXA_ATOMID vInternal, + IXA_ATOMID vIgnore1, + IXA_ATOMID vIgnore2) +{ + int bond_count; + int bond_index; + IXA_BONDID bond; + IXA_ATOMID nbr; + + bond_count = IXA_MOL_GetAtomNumBonds(hStatus, hMolecule, vInternal); + if (IXA_STATUS_HasError(hStatus)) return IXA_ATOMID_INVALID; + switch (bond_count) + { + case 2: + /* One bond joins vInternal to vVertex. The other joins vInternal to the + distal part of the stereo centre. No other bonds are available so we + presume that the vertex we are looking for is an implicit hydrogen. */ + return IXA_ATOMID_IMPLICIT_H; + + case 3: + /* Look for a neighbour of vInternal which is not any of the ones whose role + has already been accounted for. Assume that any such neighbour is a previously + unidentified vertex of the stereo centre. */ + for (bond_index = 0; bond_index < bond_count; bond_index++) + { + bond = IXA_MOL_GetAtomBond(hStatus, hMolecule, vInternal, bond_index); + if (IXA_STATUS_HasError(hStatus)) return IXA_ATOMID_INVALID; + nbr = MOL_GetBondOtherAtom(hStatus, hMolecule, bond, vInternal); + if (IXA_STATUS_HasError(hStatus)) return IXA_ATOMID_INVALID; + if ((nbr != vVertex) && (nbr != vIgnore1) && (nbr != vIgnore2)) + { + return nbr; + } + } + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Illegal rectangular centre detected"); + return IXA_ATOMID_INVALID; + + default: + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Illegal rectangular centre detected"); + return IXA_ATOMID_INVALID; + } +} + + +static IXA_BONDID FindCumuleneCentre(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vInternal1, + IXA_ATOMID vInternal2) +{ + int bond_count1; + int bond_index1; + IXA_BONDID bond1; + IXA_ATOMID atom1; + int bond_count2; + int bond_index2; + IXA_BONDID bond2; + IXA_ATOMID atom2; + IXA_BONDID central_bond; + IXA_BOND_TYPE type; + + bond_count1 = IXA_MOL_GetAtomNumBonds(hStatus, hMolecule, vInternal1); + if (IXA_STATUS_HasError(hStatus)) return IXA_BONDID_INVALID; + bond_count2 = IXA_MOL_GetAtomNumBonds(hStatus, hMolecule, vInternal2); + if (IXA_STATUS_HasError(hStatus)) return IXA_BONDID_INVALID; + + for (bond_index1 = 0; bond_index1 < bond_count1; bond_index1++) + { + bond1 = IXA_MOL_GetAtomBond(hStatus, hMolecule, vInternal1, bond_index1); + if (IXA_STATUS_HasError(hStatus)) return IXA_BONDID_INVALID; + atom1 = MOL_GetBondOtherAtom(hStatus, hMolecule, bond1, vInternal1); + if (IXA_STATUS_HasError(hStatus)) return IXA_BONDID_INVALID; + for (bond_index2 = 0; bond_index2 < bond_count2; bond_index2++) + { + bond2 = IXA_MOL_GetAtomBond(hStatus, hMolecule, vInternal2, bond_index2); + if (IXA_STATUS_HasError(hStatus)) return IXA_BONDID_INVALID; + atom2 = MOL_GetBondOtherAtom(hStatus, hMolecule, bond2, vInternal2); + if (IXA_STATUS_HasError(hStatus)) return IXA_BONDID_INVALID; + central_bond = IXA_MOL_GetCommonBond(hStatus, hMolecule, atom1, atom2); + if (IXA_STATUS_HasError(hStatus)) return IXA_BONDID_INVALID; + if (central_bond != IXA_BONDID_INVALID) + { + type = IXA_MOL_GetBondType(hStatus, hMolecule, central_bond); + if (IXA_STATUS_HasError(hStatus)) return IXA_BONDID_INVALID; + if (type != IXA_BOND_TYPE_DOUBLE) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Central bond of cumulene must be double"); + return IXA_BONDID_INVALID; + } + return central_bond; + } + } + } + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Cannot find central bond for cumulene"); + return IXA_BONDID_INVALID; +} + + + +void INCHI_DECL IXA_MOL_ReadInChI(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + const char* pInChI) +{ + inchi_InputINCHI input; +#ifdef IXA_USES_NON_EX_CORE_API + inchi_OutputStruct output; +#else + inchi_OutputStructEx output; +#endif + int atom_index; + IXA_ATOMID atom; + int bond_count; + int bond_index; + IXA_BONDID bond; + int nbr_index; + IXA_ATOMID nbr; + int direction; + int stereo_index; + IXA_STEREOID stereo; + int res; + + IXA_MOL_Clear(hStatus, hMolecule); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + + input.szInChI = (char*) pInChI; + input.szOptions = ""; + +#ifdef IXA_USES_NON_EX_CORE_API + res = GetStructFromINCHI(&input, &output); +#else + res = GetStructFromINCHIEx(&input, &output); +#endif + if ( res == inchi_Ret_ERROR || res == inchi_Ret_FATAL ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, output.szMessage); + goto cleanup; + } + else if ( res == inchi_Ret_WARNING ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_WARNING, output.szMessage); + } + + +#ifndef IXA_USES_NON_EX_CORE_API + /* Check for and then replace star atoms if Polymer extension is supplied */ + if ( output.polymer && output.polymer->n && output.polymer->units && output.polymer->units[0] ) + { + for (atom_index = 0; atom_index < output.num_atoms; atom_index++) + { + if ( !strcmp( output.atom[atom_index].elname,"*") ) + { + strcpy( output.atom[atom_index].elname, "Zz" ); + } + } + } +#endif + + + for (atom_index = 0; atom_index < output.num_atoms; atom_index++) + { + int mass; + if ((ISOTOPIC_SHIFT_FLAG - ISOTOPIC_SHIFT_MAX <= output.atom[atom_index].isotopic_mass) && + (output.atom[atom_index].isotopic_mass <= ISOTOPIC_SHIFT_FLAG + ISOTOPIC_SHIFT_MAX)) + { + int base_mass = get_atomic_mass(output.atom[atom_index].elname); + mass = base_mass + output.atom[atom_index].isotopic_mass - ISOTOPIC_SHIFT_FLAG; + } + else + { + mass = output.atom[atom_index].isotopic_mass; + } + atom = IXA_MOL_CreateAtom(hStatus, hMolecule); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + IXA_MOL_SetAtomElement(hStatus, hMolecule, atom, output.atom[atom_index].elname); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + IXA_MOL_SetAtomCharge(hStatus, hMolecule, atom, output.atom[atom_index].charge); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + IXA_MOL_SetAtomRadical(hStatus, hMolecule, atom, (IXA_ATOM_RADICAL) output.atom[atom_index].radical); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + IXA_MOL_SetAtomMass(hStatus, hMolecule, atom, mass); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + IXA_MOL_SetAtomHydrogens(hStatus, hMolecule, atom, 0, output.atom[atom_index].num_iso_H[0]); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + IXA_MOL_SetAtomHydrogens(hStatus, hMolecule, atom, 1, output.atom[atom_index].num_iso_H[1]); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + IXA_MOL_SetAtomHydrogens(hStatus, hMolecule, atom, 2, output.atom[atom_index].num_iso_H[2]); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + IXA_MOL_SetAtomHydrogens(hStatus, hMolecule, atom, 3, output.atom[atom_index].num_iso_H[3]); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + IXA_MOL_SetAtomX(hStatus, hMolecule, atom, output.atom[atom_index].x); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + IXA_MOL_SetAtomY(hStatus, hMolecule, atom, output.atom[atom_index].y); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + IXA_MOL_SetAtomZ(hStatus, hMolecule, atom, output.atom[atom_index].z); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + } + + for (atom_index = 0; atom_index < output.num_atoms; atom_index++) + { + atom = IXA_MOL_GetAtomId(hStatus, hMolecule, atom_index); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + for (bond_index = 0; bond_index < output.atom[atom_index].num_bonds; bond_index++) + { + nbr_index = output.atom[atom_index].neighbor[bond_index]; + if (nbr_index > atom_index) + { + nbr = IXA_MOL_GetAtomId(hStatus, hMolecule, nbr_index); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + bond = IXA_MOL_CreateBond(hStatus, hMolecule, atom, nbr); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + IXA_MOL_SetBondType(hStatus, hMolecule, bond, (IXA_BOND_TYPE) output.atom[atom_index].bond_type[bond_index]); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + + direction = output.atom[atom_index].bond_stereo[bond_index] & 0x08; + switch (direction) + { + case INCHI_BOND_STEREO_NONE: + break; + case INCHI_BOND_STEREO_SINGLE_1UP: + IXA_MOL_SetBondWedge(hStatus, hMolecule, bond, atom, IXA_BOND_WEDGE_UP); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + break; + case INCHI_BOND_STEREO_SINGLE_1EITHER: + IXA_MOL_SetBondWedge(hStatus, hMolecule, bond, atom, IXA_BOND_WEDGE_EITHER); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + break; + case INCHI_BOND_STEREO_SINGLE_1DOWN: + IXA_MOL_SetBondWedge(hStatus, hMolecule, bond, atom, IXA_BOND_WEDGE_DOWN); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + break; + case INCHI_BOND_STEREO_SINGLE_2UP: + IXA_MOL_SetBondWedge(hStatus, hMolecule, bond, nbr, IXA_BOND_WEDGE_UP); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + break; + case INCHI_BOND_STEREO_SINGLE_2EITHER: + IXA_MOL_SetBondWedge(hStatus, hMolecule, bond, nbr, IXA_BOND_WEDGE_EITHER); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + break; + case INCHI_BOND_STEREO_SINGLE_2DOWN: + IXA_MOL_SetBondWedge(hStatus, hMolecule, bond, nbr, IXA_BOND_WEDGE_DOWN); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + break; + case INCHI_BOND_STEREO_DOUBLE_EITHER: + IXA_MOL_SetDblBondConfig(hStatus, hMolecule, bond, IXA_DBLBOND_CONFIG_EITHER); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + break; + default: + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Stereo code %d is not recognised", direction); + goto cleanup; + } + } + } + } + + for (stereo_index = 0; stereo_index < output.num_stereo0D; stereo_index++) + { + switch (output.stereo0D[stereo_index].type) + { + case INCHI_StereoType_Tetrahedral: + { + atom = IXA_MOL_GetAtomId(hStatus, hMolecule, output.stereo0D[stereo_index].central_atom); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + bond_count = IXA_MOL_GetAtomNumBonds(hStatus, hMolecule, atom); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + switch (bond_count) + { + case 3: + { + IXA_ATOMID nbr1; + IXA_ATOMID nbr2; + IXA_ATOMID nbr3; + + nbr1 = IXA_MOL_GetAtomId(hStatus, hMolecule, output.stereo0D[stereo_index].neighbor[1]); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + nbr2 = IXA_MOL_GetAtomId(hStatus, hMolecule, output.stereo0D[stereo_index].neighbor[2]); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + nbr3 = IXA_MOL_GetAtomId(hStatus, hMolecule, output.stereo0D[stereo_index].neighbor[3]); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + + stereo = IXA_MOL_CreateStereoTetrahedron(hStatus, hMolecule, + atom, IXA_ATOMID_IMPLICIT_H, nbr1, nbr2, nbr3); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + } break; + + case 4: + { + IXA_ATOMID nbr0; + IXA_ATOMID nbr1; + IXA_ATOMID nbr2; + IXA_ATOMID nbr3; + + nbr0 = IXA_MOL_GetAtomId(hStatus, hMolecule, output.stereo0D[stereo_index].neighbor[0]); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + nbr1 = IXA_MOL_GetAtomId(hStatus, hMolecule, output.stereo0D[stereo_index].neighbor[1]); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + nbr2 = IXA_MOL_GetAtomId(hStatus, hMolecule, output.stereo0D[stereo_index].neighbor[2]); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + nbr3 = IXA_MOL_GetAtomId(hStatus, hMolecule, output.stereo0D[stereo_index].neighbor[3]); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + + stereo = IXA_MOL_CreateStereoTetrahedron(hStatus, hMolecule, + atom, nbr0, nbr1, nbr2, nbr3); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + } break; + + default: + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, + "Central atom of tetrahedral centre must have 3 or 4 neighbours"); + goto cleanup; + } + } break; + + case INCHI_StereoType_DoubleBond: + { + IXA_ATOMID nbr1a; + IXA_ATOMID nbr1b; + IXA_ATOMID internal1; + IXA_ATOMID nbr2a; + IXA_ATOMID nbr2b; + IXA_ATOMID internal2; + IXA_BONDID central_bond; + + nbr1a = IXA_MOL_GetAtomId(hStatus, hMolecule, output.stereo0D[stereo_index].neighbor[0]); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + nbr2b = IXA_MOL_GetAtomId(hStatus, hMolecule, output.stereo0D[stereo_index].neighbor[3]); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + + internal1 = IXA_MOL_GetAtomId(hStatus, hMolecule, output.stereo0D[stereo_index].neighbor[1]); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + internal2 = IXA_MOL_GetAtomId(hStatus, hMolecule, output.stereo0D[stereo_index].neighbor[2]); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + central_bond = IXA_MOL_GetCommonBond(hStatus, hMolecule, internal1, internal2); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + if (central_bond != IXA_BONDID_INVALID) + { + /* The stereo centre is an olefin. */ + nbr1b = AnalyseInternalVertex(hStatus, hMolecule, nbr1a, internal1, internal2, IXA_ATOMID_INVALID); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + + nbr2a = AnalyseInternalVertex(hStatus, hMolecule, nbr2b, internal2, internal1, IXA_ATOMID_INVALID); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + } + else + { + IXA_ATOMID central1; + IXA_ATOMID central2; + + /* The stereo centre is not an olefin. Assume it is a cumulene. */ + central_bond = FindCumuleneCentre(hStatus, hMolecule, internal1, internal2); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + central1 = IXA_MOL_GetBondAtom1(hStatus, hMolecule, central_bond); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + central2 = IXA_MOL_GetBondAtom2(hStatus, hMolecule, central_bond); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + nbr1b = AnalyseInternalVertex(hStatus, hMolecule, nbr1a, internal1, central1, central2); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + nbr2a = AnalyseInternalVertex(hStatus, hMolecule, nbr2b, internal2, central1, central2); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + } + + stereo = IXA_MOL_CreateStereoRectangle(hStatus, hMolecule, + central_bond, nbr1a, nbr1b, nbr2a, nbr2b); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + } break; + + case INCHI_StereoType_Allene: + { + IXA_ATOMID nbr1a; + IXA_ATOMID nbr1b; + IXA_ATOMID internal1; + IXA_ATOMID nbr2a; + IXA_ATOMID nbr2b; + IXA_ATOMID internal2; + IXA_ATOMID central_atom; + + nbr1a = IXA_MOL_GetAtomId(hStatus, hMolecule, output.stereo0D[stereo_index].neighbor[0]); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + nbr2b = IXA_MOL_GetAtomId(hStatus, hMolecule, output.stereo0D[stereo_index].neighbor[3]); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + + internal1 = IXA_MOL_GetAtomId(hStatus, hMolecule, output.stereo0D[stereo_index].neighbor[1]); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + internal2 = IXA_MOL_GetAtomId(hStatus, hMolecule, output.stereo0D[stereo_index].neighbor[2]); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + central_atom = IXA_MOL_GetAtomId(hStatus, hMolecule, output.stereo0D[stereo_index].central_atom); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + + nbr1b = AnalyseInternalVertex(hStatus, hMolecule, nbr1a, internal1, central_atom, IXA_ATOMID_INVALID); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + nbr2a = AnalyseInternalVertex(hStatus, hMolecule, nbr2b, internal2, central_atom, IXA_ATOMID_INVALID); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + + stereo = IXA_MOL_CreateStereoAntiRectangle(hStatus, hMolecule, + central_atom, nbr1a, nbr1b, nbr2a, nbr2b); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + } break; + + default: + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, + "Stereo type %d is not recognised", output.stereo0D[stereo_index].type); + goto cleanup; + } + + IXA_MOL_SetStereoParity(hStatus, hMolecule, stereo, (IXA_STEREO_PARITY) output.stereo0D[stereo_index].parity); + if (IXA_STATUS_HasError(hStatus)) goto cleanup; + } + +#ifdef IXA_USES_NON_EX_CORE_API +#else + /* v. 1.5 extensions */ + { + int res = IXA_MOL_SetExtMolDataByInChIExtInput( hStatus, hMolecule, output.polymer, output.v3000, output.num_atoms ); + if ( res ) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Error creating IXA molecule by read InChI string" ); + goto cleanup; + } + } +#endif + + + +cleanup: +#ifdef IXA_USES_NON_EX_CORE_API + FreeStructFromINCHI(&output); +#else + FreeStructFromINCHIEx(&output); +#endif +} diff --git a/INCHI-1-SRC/INCHI_API/libinchi/src/ixa/ixa_read_mol.c b/INCHI-1-SRC/INCHI_API/libinchi/src/ixa/ixa_read_mol.c new file mode 100644 index 0000000..d8272cf --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/libinchi/src/ixa/ixa_read_mol.c @@ -0,0 +1,355 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include +#include +#include +#include + +#include "../../../../INCHI_BASE/src/mode.h" +#include "../../../../INCHI_BASE/src/inchi_api.h" +#include "../../../../INCHI_BASE/src/ichicomp.h" +#include "../../../../INCHI_BASE/src/util.h" +#include "../../../../INCHI_BASE/src/mol_fmt.h" +#include "../../../../INCHI_BASE/src/ichi_io.h" + +#include "ixa_status.h" + + +/* #define memicmp strncasecmp */ + +int IXA_MOL_SetExtMoldataByMolfileExtInput( IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + MOL_FMT_DATA* mfdata); + + + +/* + Read Molfile (throuh call to InChI native ReadMolfile() ) and pack the data into INCHIMOL +*/ +void INCHI_DECL IXA_MOL_ReadMolfile( IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + const char* pBytes) +{ + int err; + char error_string[STR_ERR_LEN]; + MOL_FMT_DATA* mol_data = NULL; + MOL_FMT_CTAB OnlyCtab; + int i, n1; + IXA_ATOMID atom1; + IXA_ATOMID atom2; + IXA_BONDID bond; + int bond_count; + IXA_BOND_TYPE bond_type; + const char* p_cursor; + INCHI_IOSTREAM instr; + INCHI_IOSTREAM *inp_file = &instr; + S_CHAR orig_mass_diff; + int mass_or_mass_diff; + int inlen=0; + + + /* Throw out any existing content in the IXA molecule object. */ + IXA_MOL_Clear(hStatus, hMolecule); + + /* Wrap input text into an INCHI_IOSTREAM_STRING to use a */ + /* general-purpose (Molfile->InChI internal data) reader */ + inchi_ios_init(inp_file, INCHI_IOSTREAM_TYPE_STRING, NULL); + inp_file->s.pStr = (char * ) pBytes; + inlen = strlen( inp_file->s.pStr ) + 1; + inp_file->s.nUsedLength = inlen; + inp_file->s.nPtr = 0; + inp_file->f = NULL; + + error_string[0] = '\0'; + + /* Parse the MolFile and store its content in an internal data structure. It might be possible + to eliminate the internal struct and read directly into the IXA molecule object but that + would mean rewriting a lot of code and also mask the relationship between this MolFile + reading function and those used in previous InChI code. */ + p_cursor = pBytes; + mol_data = ReadMolfile( inp_file, + NULL, + &OnlyCtab, + 0, /*NULL != szCoord,*/ + 1, + NULL, 0, NULL, NULL, NULL, + &err, error_string ); + + /* uses "err" and "error_string" to report errors. Pick up any errors + and write them to the status object. */ + if (!mol_data) + { + if (error_string[0]) + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, error_string); + } + else + { + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "No molfile data or failed to read"); + } + goto exit_function; + } + else + { + if (error_string[0]) + { + if ( !err) + STATUS_PushMessage(hStatus, IXA_STATUS_WARNING, error_string); + } + } + + /* No need to do anything if the MolFile does not contain any atoms. */ + if (mol_data->ctab.n_atoms > 0) + { + /* Create the appropriate number of IXA atoms. Some atom properties can only be set + once bonds have been created and bonds can only be created once all atoms are in + place. Hence this rather lightweight loop that creates atoms but does not set + their properties. */ + for (i = 0; i < mol_data->ctab.n_atoms; i++) + { + IXA_MOL_CreateAtom(hStatus, hMolecule); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + } + + /* All atoms now exist so it is now possible to create bonds and set their properties. */ + for ( i = 0; i < mol_data->ctab.n_bonds; i ++ ) + { + /* Create the bond. */ + atom1 = IXA_MOL_GetAtomId(hStatus, hMolecule, mol_data->ctab.bonds[i].atnum1-1); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + atom2 = IXA_MOL_GetAtomId(hStatus, hMolecule, mol_data->ctab.bonds[i].atnum2-1); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + bond = IXA_MOL_CreateBond(hStatus, hMolecule, atom1, atom2); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + + /* Set bond order. */ + switch (mol_data->ctab.bonds[i].bond_type) + { + case 1: + IXA_MOL_SetBondType(hStatus, hMolecule, bond, IXA_BOND_TYPE_SINGLE); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + break; + case 2: + IXA_MOL_SetBondType(hStatus, hMolecule, bond, IXA_BOND_TYPE_DOUBLE); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + break; + case 3: + IXA_MOL_SetBondType(hStatus, hMolecule, bond, IXA_BOND_TYPE_TRIPLE); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + break; + case 4: + IXA_MOL_SetBondType(hStatus, hMolecule, bond, IXA_BOND_TYPE_AROMATIC); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + break; + default: + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Bond type %d is not recognised", mol_data->ctab.bonds[i].bond_type); + goto exit_function; + } + + /* Set bond wedge and double bond configuration. */ + switch (mol_data->ctab.bonds[i].bond_stereo) + { + case INPUT_STEREO_DBLE_EITHER: + IXA_MOL_SetDblBondConfig(hStatus, hMolecule, bond, IXA_DBLBOND_CONFIG_EITHER); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + break; + case INPUT_STEREO_SNGL_UP: + IXA_MOL_SetBondWedge(hStatus, hMolecule, bond, atom1, IXA_BOND_WEDGE_UP); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + break; + case INPUT_STEREO_SNGL_EITHER: + IXA_MOL_SetBondWedge(hStatus, hMolecule, bond, atom1, IXA_BOND_WEDGE_EITHER); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + break; + case INPUT_STEREO_SNGL_DOWN: + IXA_MOL_SetBondWedge(hStatus, hMolecule, bond, atom1, IXA_BOND_WEDGE_DOWN); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + break; + case 0: + IXA_MOL_SetBondWedge(hStatus, hMolecule, bond, atom1, IXA_BOND_WEDGE_NONE); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + break; + default: + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Stereo type %d is not recognised", mol_data->ctab.bonds[i].bond_stereo); + goto exit_function; + } + } + + /* Now that all bonds have been dealt with, set atom properties. */ + for (i = 0; i < mol_data->ctab.n_atoms; i++) + { + /* Most atom properties are easily dealt with. They might equally have been set + when the atom was first created. */ + atom1 = IXA_MOL_GetAtomId(hStatus, hMolecule, i); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + IXA_MOL_SetAtomElement(hStatus, hMolecule, atom1, mol_data->ctab.atoms[i].symbol); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + + /* Fix 2016-02-18/03-04: conform GetINCHI() conventions, account for the most abundant isotope */ + orig_mass_diff = mol_data->ctab.atoms[i].mass_difference; + if ( orig_mass_diff == 0 ) + { + /* not isotopic */ + mass_or_mass_diff = 0; + } + else if ( orig_mass_diff == ZERO_ATW_DIFF ) + { + /* isotopic enrichment, special case: + isotope's integer mass is equal to the + rounded average mass from Periodic Table + */ + mass_or_mass_diff = 0 + ISOTOPIC_SHIFT_FLAG; + } + else + { + /* isotopic mass, general case */ + mass_or_mass_diff = orig_mass_diff + ISOTOPIC_SHIFT_FLAG; + } + IXA_MOL_SetAtomMass( hStatus, hMolecule, atom1, mass_or_mass_diff ); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + IXA_MOL_SetAtomCharge(hStatus, hMolecule, atom1, mol_data->ctab.atoms[i].charge); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + IXA_MOL_SetAtomX(hStatus, hMolecule, atom1, mol_data->ctab.atoms[i].fx); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + IXA_MOL_SetAtomY(hStatus, hMolecule, atom1, mol_data->ctab.atoms[i].fy); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + IXA_MOL_SetAtomZ(hStatus, hMolecule, atom1, mol_data->ctab.atoms[i].fz); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; +#if( SINGLET_IS_TRIPLET == 1 ) + if (mol_data->ctab.atoms[i].radical == 1) + { + IXA_MOL_SetAtomRadical(hStatus, hMolecule, atom1, IXA_ATOM_RADICAL_TRIPLET); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + } + else +#endif + IXA_MOL_SetAtomRadical(hStatus, hMolecule, atom1, (IXA_ATOM_RADICAL) mol_data->ctab.atoms[i].radical); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + + /* Implicit hydrogen count depends on an analysis of the bonds connected to the + atom. This is the reason why bonds had to be created before atom properties + could be set. */ + bond_count = IXA_MOL_GetAtomNumBonds(hStatus, hMolecule, atom1); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + if (mol_data->ctab.atoms[i].valence && + (mol_data->ctab.atoms[i].valence != 15 || (bond_count != 0))) + { + /* Molfile contains special valence => calculate number of H */ + int valence; + int chem_bonds_valence = 0; + int aromatic_count = 0; + valence = mol_data->ctab.atoms[i].valence; /* save atom valence if available */ + for ( n1 = 0; n1 < bond_count; n1 ++ ) + { + bond = IXA_MOL_GetAtomBond(hStatus, hMolecule, atom1, n1); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + bond_type = IXA_MOL_GetBondType(hStatus, hMolecule, bond); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + switch (bond_type) + { + case INCHI_BOND_TYPE_SINGLE: + chem_bonds_valence += 1; + break; + case INCHI_BOND_TYPE_DOUBLE: + chem_bonds_valence += 2; + break; + case INCHI_BOND_TYPE_TRIPLE: + chem_bonds_valence += 3; + break; + case INCHI_BOND_TYPE_ALTERN: + chem_bonds_valence += 1; + aromatic_count++; + break; + } + } + switch (aromatic_count) + { + case 0: + break; + case 2: + case 3: + chem_bonds_valence++; + break; + default: + STATUS_PushMessage(hStatus, IXA_STATUS_ERROR, "Atom cannot have %d aromatic bonds", aromatic_count); + goto exit_function; + } + if ( valence >= chem_bonds_valence ) + { + IXA_MOL_SetAtomHydrogens(hStatus, hMolecule, atom1, 0, valence - chem_bonds_valence); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + } + } + else if ( mol_data->ctab.atoms[i].atom_aliased_flag ) + { + IXA_MOL_SetAtomHydrogens(hStatus, hMolecule, atom1, 0, 0); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + } + else if ( mol_data->ctab.atoms[i].valence == 15 && (bond_count == 0)) + { + IXA_MOL_SetAtomHydrogens(hStatus, hMolecule, atom1, 0, 0); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + } + else + { + IXA_MOL_SetAtomHydrogens(hStatus, hMolecule, atom1, 0, -1); + } + } + + /* Chiral flag is a whole-molecule property not tied to any particular atom, bond + or stereo centre. */ + if (mol_data->ctab.chiral_flag) + { + IXA_MOL_SetChiral(hStatus, hMolecule, IXA_TRUE); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + } + + if ( mol_data->ctab.sgroups.used || mol_data->ctab.v3000 ) + { + /* Treat Molfile extended data (polymers; V300 features ) */ + IXA_MOL_SetExtMoldataByMolfileExtInput( hStatus, hMolecule, mol_data ); + if (IXA_STATUS_HasError(hStatus)) goto exit_function; + } + } + +exit_function: + FreeMolfileData( mol_data ); +} diff --git a/INCHI-1-SRC/INCHI_API/libinchi/src/ixa/ixa_status.c b/INCHI-1-SRC/INCHI_API/libinchi/src/ixa/ixa_status.c new file mode 100644 index 0000000..f6b9750 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/libinchi/src/ixa/ixa_status.c @@ -0,0 +1,283 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include "../../../../INCHI_BASE/src/mode.h" +#include "../../../../INCHI_BASE/src/inchi_api.h" +#include "ixa_status.h" +#include +#include +#include + + +#define MAXERR 50 +#define MAX_MSGLENGTH 1024 + + +typedef struct +{ + IXA_STATUS severity; + char* message; +} StatusItem; + + +typedef struct StatusBlockTag +{ + char data[MAX_MSGLENGTH]; + struct StatusBlockTag* next; +} StatusBlock; + + +typedef struct +{ + int item_count; + StatusItem items[MAXERR]; + StatusBlock first_block; + StatusBlock* current_block; + char* current_position; +} INCHISTATUS; + + +static void BLOCK_clear(StatusBlock* pBlock) +{ + /* If the block points to yet another block, recursively clear the entire list.*/ + if (pBlock->next) + { + BLOCK_clear(pBlock->next); + inchi_free(pBlock->next); + pBlock->next = NULL; + } +} + + +static void STATUS_init(INCHISTATUS* pStatus) +{ + pStatus->first_block.next = NULL; + pStatus->current_block = &pStatus->first_block; + pStatus->current_position = pStatus->current_block->data; + pStatus->item_count = 0; +} + + +static void STATUS_Clear(INCHISTATUS* pStatus) +{ + BLOCK_clear(&pStatus->first_block); + /*@@@ Fixed memory leak in original code */ + if ( pStatus->item_count ) + { + int i; + for (i=0; iitem_count; i++) + { + if ( pStatus->items[i].message ) + { + inchi_free( pStatus->items[i].message ); + pStatus->items[i].message = NULL; + } + } + } + STATUS_init(pStatus); +} + + +IXA_STATUS_HANDLE STATUS_Pack(INCHISTATUS* pStatus) +{ + return (IXA_STATUS_HANDLE)pStatus; +} + + +static INCHISTATUS* STATUS_Unpack(IXA_STATUS_HANDLE hStatus) +{ + return (INCHISTATUS*)hStatus; +} + + +void STATUS_PushMessage(IXA_STATUS_HANDLE hStatus, + IXA_STATUS vSeverity, + char* pFormat, + ...) +{ + va_list arguments; + char buffer[MAX_MSGLENGTH]; + StatusItem* item; + int item_index; + int size; + + INCHISTATUS* status = STATUS_Unpack(hStatus); + if (!status) return; + + if ( !pFormat ) + /* We shouldn't get here */ + return; + + if (status->item_count < MAXERR) + { +/* The stack is not yet full. Push another item onto it. */ + item = &status->items[status->item_count]; + status->item_count++; + } + else + { +/* The stack is already full. Shuffle the existing items down to make room + for the new one. The bottom-most item falls off the end and is lost. */ + for (item_index = 1; item_index < status->item_count; item_index++) + { + status->items[item_index - 1].severity = status->items[item_index].severity; + status->items[item_index - 1].message = status->items[item_index].message; + } + item = &status->items[status->item_count - 1]; + } + + item->severity = vSeverity; + + +/* Build the message string and write it to the new stack entry. */ + + va_start(arguments, pFormat); + size = vsnprintf(buffer, sizeof(buffer), pFormat, arguments) + 1; + va_end(arguments); + +/* vsnprintf returns -1 if it had to truncate the string, which shows + up here as a size of zero. */ + if (size == 0) + { + buffer[sizeof(buffer) - 4] = '.'; + buffer[sizeof(buffer) - 3] = '.'; + buffer[sizeof(buffer) - 2] = '.'; + buffer[sizeof(buffer) - 1] = '\0'; + size = sizeof(buffer); + } + + item->message = (char *) inchi_malloc(size); + if (item->message) + { + strcpy(item->message, buffer); + } +} + + +static IXA_BOOL INCHISTATUS_TestSeverity(IXA_STATUS_HANDLE hStatus, + IXA_STATUS vSeverity) +{ + int item_index; + + INCHISTATUS* status = STATUS_Unpack(hStatus); + if (!status) return IXA_FALSE; + + for (item_index = 0; item_index < status->item_count; item_index++) + { + if (status->items[item_index].severity == vSeverity) + { + return IXA_TRUE; + } + } + return IXA_FALSE; +} + + +IXA_STATUS_HANDLE INCHI_DECL IXA_STATUS_Create(void) +{ + INCHISTATUS* status = (INCHISTATUS*)inchi_malloc(sizeof(INCHISTATUS)); + if (!status) return NULL; + + STATUS_init(status); + return STATUS_Pack(status); +} + + +void INCHI_DECL IXA_STATUS_Destroy(IXA_STATUS_HANDLE hStatus) +{ + INCHISTATUS* status = STATUS_Unpack(hStatus); + if (!status) return; + + STATUS_Clear(status); + inchi_free(status); +} + + +void INCHI_DECL IXA_STATUS_Clear(IXA_STATUS_HANDLE hStatus) +{ + INCHISTATUS* status = STATUS_Unpack(hStatus); + if (!status) return; + + STATUS_Clear(status); +} + + +IXA_BOOL INCHI_DECL IXA_STATUS_HasError(IXA_STATUS_HANDLE hStatus) +{ + return INCHISTATUS_TestSeverity(hStatus, IXA_STATUS_ERROR); +} + + +IXA_BOOL INCHI_DECL IXA_STATUS_HasWarning(IXA_STATUS_HANDLE hStatus) +{ + return INCHISTATUS_TestSeverity(hStatus, IXA_STATUS_WARNING); +} + + +int INCHI_DECL IXA_STATUS_GetCount(IXA_STATUS_HANDLE hStatus) +{ + INCHISTATUS* status = STATUS_Unpack(hStatus); + if (!status) return 0; + + return status->item_count; +} + + +const char* INCHI_DECL IXA_STATUS_GetMessage(IXA_STATUS_HANDLE hStatus, + int vIndex) +{ + INCHISTATUS* status = STATUS_Unpack(hStatus); + if (!status) return "Invalid status handle"; + + if ((vIndex < 0) || (vIndex >= status->item_count)) return "Message index is out of range"; + + return status->items[vIndex].message; +} + + +IXA_STATUS INCHI_DECL IXA_STATUS_GetSeverity(IXA_STATUS_HANDLE hStatus, + int vIndex) +{ + INCHISTATUS* status = STATUS_Unpack(hStatus); + if (!status) return IXA_STATUS_ERROR; + + if ((vIndex < 0) || (vIndex >= status->item_count)) return IXA_STATUS_ERROR; + + return status->items[vIndex].severity; +} diff --git a/INCHI-1-SRC/INCHI_API/libinchi/src/ixa/ixa_status.h b/INCHI-1-SRC/INCHI_API/libinchi/src/ixa/ixa_status.h new file mode 100644 index 0000000..ffb6d5d --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/libinchi/src/ixa/ixa_status.h @@ -0,0 +1,50 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef __IXA_STATUS_H__ +#define __IXA_STATUS_H__ + +#include "../../../../INCHI_BASE/src/ixa.h" + +void STATUS_PushMessage(IXA_STATUS_HANDLE hStatus, + IXA_STATUS vSeverity, + char* pFormat, + ...); + +#endif diff --git a/INCHI-1-SRC/INCHI_API/libinchi/vc9/libinchi.aps b/INCHI-1-SRC/INCHI_API/libinchi/vc9/libinchi.aps new file mode 100644 index 0000000..e6b8a3a Binary files /dev/null and b/INCHI-1-SRC/INCHI_API/libinchi/vc9/libinchi.aps differ diff --git a/INCHI-1-SRC/INCHI_API/vc9/inchi_dll/INCHI_DLL.rc b/INCHI-1-SRC/INCHI_API/libinchi/vc9/libinchi.rc similarity index 77% rename from INCHI-1-SRC/INCHI_API/vc9/inchi_dll/INCHI_DLL.rc rename to INCHI-1-SRC/INCHI_API/libinchi/vc9/libinchi.rc index 3160b3c..e4430b0 100644 --- a/INCHI-1-SRC/INCHI_API/vc9/inchi_dll/INCHI_DLL.rc +++ b/INCHI-1-SRC/INCHI_API/libinchi/vc9/libinchi.rc @@ -1,101 +1,102 @@ -// Microsoft Visual C++ generated resource script. -// -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "afxres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (U.S.) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -#ifdef _WIN32 -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) -#endif //_WIN32 - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,0,4,1 - PRODUCTVERSION 1,0,4,1 - FILEFLAGSMASK 0x1fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x4L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "Comments", "IUPAC International Chemical Identifier (InChI) version 1, Software version 1.04. Library. Build of September 9, 2011." - VALUE "FileDescription", "InChI version 1 (1.04) Build of September 9, 2011" - VALUE "FileVersion", "1.04" - VALUE "InternalName", "libinchi.dll" - VALUE "OriginalFilename", "libinchi.dll" - VALUE "ProductName", "IUPAC International Chemical Identifier (InChI) version 1, Software version 1.04. Library. Build of September 9, 2011." - VALUE "ProductVersion", "1, 0, 4, 1" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END - - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""afxres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - -#endif // English (U.S.) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,5,0 + PRODUCTVERSION 1,0,5,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x21L +#else + FILEFLAGS 0x20L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "IUPAC International Chemical Identifier (InChI) version 1, Software version 1.05, Winter 2017. InChI Software Library (API functions)." + VALUE "FileDescription", "InChI Software Library (API functions)." + VALUE "FileVersion", "1.05" + VALUE "InternalName", "libinchi.dll" + VALUE "OriginalFilename", "libinchi.dll" + VALUE "ProductName", "IUPAC International Chemical Identifier (InChI) version 1, Software version 1.05, Winter 2017. InChI Software Library (API procedures)." + VALUE "ProductVersion", "1, 0, 5, 0" + VALUE "SpecialBuild", "RELEASE" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/INCHI-1-SRC/INCHI_API/vc9/inchi_dll/inchi_dll.sln b/INCHI-1-SRC/INCHI_API/libinchi/vc9/libinchi.sln similarity index 83% rename from INCHI-1-SRC/INCHI_API/vc9/inchi_dll/inchi_dll.sln rename to INCHI-1-SRC/INCHI_API/libinchi/vc9/libinchi.sln index c5be4fb..71707ce 100644 --- a/INCHI-1-SRC/INCHI_API/vc9/inchi_dll/inchi_dll.sln +++ b/INCHI-1-SRC/INCHI_API/libinchi/vc9/libinchi.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 10.00 # Visual Studio 2008 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "inchi_dll", "inchi_dll.vcproj", "{5DAB1696-1795-49AA-BDE6-1771FBE25445}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libinchi", "libinchi.vcproj", "{5DAB1696-1795-49AA-BDE6-1771FBE25445}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/INCHI-1-SRC/INCHI_API/libinchi/vc9/libinchi.vcproj b/INCHI-1-SRC/INCHI_API/libinchi/vc9/libinchi.vcproj new file mode 100644 index 0000000..2c97181 --- /dev/null +++ b/INCHI-1-SRC/INCHI_API/libinchi/vc9/libinchi.vcproj @@ -0,0 +1,753 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/INCHI-1-SRC/INCHI_API/vc9/inchi_dll/resource.h b/INCHI-1-SRC/INCHI_API/libinchi/vc9/resource.h similarity index 77% rename from INCHI-1-SRC/INCHI_API/vc9/inchi_dll/resource.h rename to INCHI-1-SRC/INCHI_API/libinchi/vc9/resource.h index c28deba..aa2d557 100644 --- a/INCHI-1-SRC/INCHI_API/vc9/inchi_dll/resource.h +++ b/INCHI-1-SRC/INCHI_API/libinchi/vc9/resource.h @@ -1,6 +1,6 @@ //{{NO_DEPENDENCIES}} -// Microsoft Developer Studio generated include file. -// Used by INCHI_DLL.rc +// Microsoft Visual C++ generated include file. +// Used by libinchi.rc // // Next default values for new objects diff --git a/INCHI-1-SRC/INCHI_API/vc9/inchi_dll/vc9_libinchi.def b/INCHI-1-SRC/INCHI_API/libinchi/vc9/vc9_libinchi.def similarity index 97% rename from INCHI-1-SRC/INCHI_API/vc9/inchi_dll/vc9_libinchi.def rename to INCHI-1-SRC/INCHI_API/libinchi/vc9/vc9_libinchi.def index cfaca95..77d49d2 100644 --- a/INCHI-1-SRC/INCHI_API/vc9/inchi_dll/vc9_libinchi.def +++ b/INCHI-1-SRC/INCHI_API/libinchi/vc9/vc9_libinchi.def @@ -33,6 +33,7 @@ EXPORTS STDINCHIGEN_DoSerialization = cdecl_STDINCHIGEN_DoSerialization @30 STDINCHIGEN_Reset = cdecl_STDINCHIGEN_Reset @31 STDINCHIGEN_Setup = cdecl_STDINCHIGEN_Setup @32 + MakeINCHIFromMolfileText = cdecl_MakeINCHIFromMolfileText @33 ; dll_exported names(stdcall) ; _CheckINCHI@8 = _CheckINCHI@8 @33 ; _CheckINCHIKey@4 = _CheckINCHIKey@4 @34 diff --git a/INCHI-1-SRC/INCHI_API/python_sample/PyINCHI.py b/INCHI-1-SRC/INCHI_API/python_sample/PyINCHI.py deleted file mode 100644 index f0aa0d5..0000000 --- a/INCHI-1-SRC/INCHI_API/python_sample/PyINCHI.py +++ /dev/null @@ -1,248 +0,0 @@ -#!/usr/bin/python -# -# International Chemical Identifier (InChI) -# Version 1 -# Software version 1.04 -# September 9, 2011 -# -# The InChI library and programs are free software developed under the -# auspices of the International Union of Pure and Applied Chemistry (IUPAC). -# Originally developed at NIST. Modifications and additions by IUPAC -# and the InChI Trust. -# -# IUPAC/InChI-Trust Licence No.1.0 for the -# International Chemical Identifier (InChI) Software version 1.04 -# Copyright (C) IUPAC and InChI Trust Limited -# -# This library is free software; you can redistribute it and/or modify it -# under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, -# or any later version. -# -# Please note that this library is distributed WITHOUT ANY WARRANTIES -# whatsoever, whether expressed or implied. See the IUPAC/InChI Trust -# Licence for the International Chemical Identifier (InChI) Software -# version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") -# for more details. -# -# You should have received a copy of the IUPAC/InChI Trust InChI -# Licence No. 1.0 with this library; if not, please write to: -# -# The InChI Trust -# c/o FIZ CHEMIE Berlin -# -# Franklinstrasse 11 -# 10587 Berlin -# GERMANY -# -# or email to: ulrich@inchi-trust.org. -# - - - - - -""" - Interface to INCHI library (used by InChI generation example) - - The implementation is very 'light' and is provided for - illustrative purposes only. - -""" - - -import os -import sys -import string - -from ctypes import * - - -PYINCHI_MAXVAL = 20 -PYINCHI_ATOM_EL_LEN = 6 -PYINCHI_NUM_H_ISOTOPES = 3 -#this flag means isotopic shift relative to avg. atw, not abs. isotopic mass -PYINCHI_ISOTOPIC_SHIFT_FLAG = 10000 - - -########################################################## -# 0D - S T E R E O (if no coordinates given) -########################################################## - -class inchi_Stereo0D(Structure): - _fields_ = [("neighbor", c_short * 4), # 4 atoms always - ("central_atom", c_short), # central tetrahedral atom or a central */ - # atom of allene; otherwise NO_ATOM */ - ("type", c_byte), # inchi_StereoType0D - ("parity", c_byte)] # inchi_StereoParity0D: may be a combination of two parities: */ - # ParityOfConnected | (ParityOfDisconnected << 3), see Note above */ - def dump(self): - print "\tDump of inchi_Stereo0D structure" - print '\t\t neighbor: ', - for nbr in self.neighbor: - print nbr, - print - print '\t\t central_atom: ', self.central_atom - print '\t\t type: ', self.type - print '\t\t parity: ', self.parity - - - - - - -########################################## -# inchi_Atom -########################################## - -class inchi_Atom(Structure): - _fields_ = [("x", c_double), # atom coordinates - ("y", c_double), - ("z", c_double), - # connectivity - ("neighbor", c_short * PYINCHI_MAXVAL), # adjacency list: ordering numbers of the adjacent atoms, >= 0 - ("bond_type", c_byte * PYINCHI_MAXVAL), # inchi_BondType - # 2D stereo - ("bond_stereo", c_byte * PYINCHI_MAXVAL), # inchi_BondStereo2D; negative if the sharp end points to another atom - # other atom properties - ("elname", c_byte * PYINCHI_ATOM_EL_LEN), # zero-terminated chemical element name: "H", "Si", etc. - ("num_bonds", c_short ), # number of neighbors, bond types and bond stereo in the adjacency list - ("num_iso_H", c_byte * (PYINCHI_NUM_H_ISOTOPES+1)), # implicit hydrogen atoms - # [0]: number of implicit non-isotopic H - # (exception: num_iso_H[0]=-1 means INCHI adds implicit H automatically), - # [1]: number of implicit isotopic 1H (protium), - # [2]: number of implicit 2H (deuterium), - # [3]: number of implicit 3H (tritium) - ("isotopic_mass", c_short ), # 0 => non-isotopic; isotopic mass or 10000 + mass - (average atomic mass) - ("radical", c_byte ), # inchi_Radical - ("charge", c_byte )] # positive or negative; 0 => no charge - - def fdump(self, fw): - fw.write('\t{\t --- Dump of inchi_Atom structure ---\n') - s = "" - for sy in self.elname: - s = s + chr(sy) - fw.write('\t\t element: %-s \n' % s ) - fw.write('\t\t charge: %-d radical: %-d isotopic_mass: %-d\n' % - (self.charge, self.radical, self.isotopic_mass) ) - fw.write('\t\t num_bonds: %-d\n' % self.num_bonds) - fw.write('\t\t neighbor: ') - for nbr in self.neighbor: - fw.write(' %-d' % nbr) - fw.write('\n') - fw.write('\t\t bond_types: ') - for bt in self.bond_type: - fw.write(' %-d ' % bt) - fw.write('\n') - fw.write('\t\t bond_stereos: ') - for bs in self.bond_stereo: - fw.write(' %-d' % bs) - fw.write('\n') - fw.write('\t\t num_iso_H: ') - for ni in self.num_iso_H: - fw.write(' %-d' % ni) - fw.write('\n\t} \n') - - def dump(self): - self.fdump(sys.stdout) - - -########################################## -# Structure -> InChI, GetINCHI() -########################################## - -class inchi_Input(Structure): - # the caller is responsible for the data allocation and deallocation - _fields_ = [("atom", POINTER(inchi_Atom)), # actually, pointer to array of inchi_Atom pointers - ("stereo0D", POINTER(inchi_Stereo0D)), # actually, pointer to array of inchi_Stereo0D - ("szOptions", c_char_p), # InChI options: space-delimited; each is preceded by '/' or '-' - ("num_atoms", c_int), #c_short), # number of atoms in the compound < 1024 - ("num_stereo0D", c_short)] # number of 0D stereo elements - - - - - -# /* InChI -> Structure, GetStructFromINCHI() */ -# typedef struct tagINCHI_InputINCHI { -# /* the caller is responsible for the data allocation and deallocation */ -# char *szInChI; /* InChI ASCIIZ string to be converted to a strucure */ -# char *szOptions; /* InChI options: space-delimited; each is preceded by */ -# /* '/' or '-' depending on OS and compiler */ -# } inchi_InputINCHI; - - - - -########################################################## -# InChI -> Structure, GetStructFromINCHI() -########################################################## - -class inchi_InputINCHI(Structure): - _fields_ = [("szInChI", c_char_p), # InChI ASCIIZ string to be converted to a strucure - ("szOptions", c_char_p)] # InChI options: space-delimited; each is preceded by - # '/' or '-' depending on OS and compiler */ - -# the caller is responsible for the data allocation and deallocation - - - -########################################################################## -# inchi_Output -########################################################################## - -class inchi_Output(Structure): - # zero-terminated C-strings allocated by GetINCHI() - # to deallocate all of them call FreeINCHI() (see below) - - _fields_ = [("szInChI", POINTER(c_char) ), # c_char_p - ("szAuxInfo", POINTER(c_char) ), - ("szMessage", POINTER(c_char) ), - ("szLog", POINTER(c_char) ) ] # c_char_p)] - - def dump(self): - print "\tDump of inchi_Output structure" - print '\t\t',self.szInChI - print '\t\t',self.szAuxInfo - print '\t\t',self.szMessage - print '\t\t',self.szLog - - - -########################################################################## -# InChI -> Structure -########################################################################## - -#class inchi_OutputStruct(Structure): -# # 4 pointers are allocated by GetStructFromINCHI() -# # to deallocate all of them call FreeStructFromINCHI() -# _fields_ = [("atom", c_long), # actually, pointer to array of inchi_Atom -# ("stereo0D", c_long), # actually, pointer to array of inchi_Stereo0D -# -# _fields_ = [("atom", POINTER(inchi_Atom)), # actually, pointer to array of inchi_Atom -# ("stereo0D", POINTER(inchi_Stereo0D)), # actually, pointer to array of inchi_Stereo0D -# -# -# ("szMessage", c_char_p), # Error/warning ASCIIZ message -# ("szLog", c_char_p), # log-file ASCIIZ string, contains a human-readable list -# # of recognized options and possibly an Error/warning message -# -# -# ("num_stereo0D", c_short)] # number of 0D stereo elements -# -# -# -# typedef struct tagINCHI_OutputStruct { -# inchi_Atom *atom; /* array of num_atoms elements */ -# inchi_Stereo0D *stereo0D; /* array of num_stereo0D 0D stereo elements or NULL */ -# AT_NUM num_atoms; /* number of atoms in the structure < 1024 */ -# AT_NUM num_stereo0D; /* number of 0D stereo elements */ -# char *szMessage; /* Error/warning ASCIIZ message */ -# char *szLog; /* log-file ASCIIZ string, contains a human-readable list */ -# /* of recognized options and possibly an Error/warning message */ -# unsigned long WarningFlags[2][2]; /* warnings, see INCHIDIFF in inchicmp.h */ -# /* [x][y]: x=0 => Reconnected if present in InChI otherwise Disconnected/Normal -# x=1 => Disconnected layer if Reconnected layer is present -# y=1 => Main layer or Mobile-H -# y=0 => Fixed-H layer -# */ -# }inchi_OutputStruct; diff --git a/INCHI-1-SRC/INCHI_API/python_sample/lightsdf.py b/INCHI-1-SRC/INCHI_API/python_sample/lightsdf.py deleted file mode 100644 index a7667e9..0000000 --- a/INCHI-1-SRC/INCHI_API/python_sample/lightsdf.py +++ /dev/null @@ -1,441 +0,0 @@ -#!/usr/bin/python -# -# International Chemical Identifier (InChI) -# Version 1 -# Software version 1.04 -# September 9, 2011 -# -# The InChI library and programs are free software developed under the -# auspices of the International Union of Pure and Applied Chemistry (IUPAC). -# Originally developed at NIST. Modifications and additions by IUPAC -# and the InChI Trust. -# -# IUPAC/InChI-Trust Licence No.1.0 for the -# International Chemical Identifier (InChI) Software version 1.04 -# Copyright (C) IUPAC and InChI Trust Limited -# -# This library is free software; you can redistribute it and/or modify it -# under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, -# or any later version. -# -# Please note that this library is distributed WITHOUT ANY WARRANTIES -# whatsoever, whether expressed or implied. See the IUPAC/InChI Trust -# Licence for the International Chemical Identifier (InChI) Software -# version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") -# for more details. -# -# You should have received a copy of the IUPAC/InChI Trust InChI -# Licence No. 1.0 with this library; if not, please write to: -# -# The InChI Trust -# c/o FIZ CHEMIE Berlin -# -# Franklinstrasse 11 -# 10587 Berlin -# GERMANY -# -# or email to: ulrich@inchi-trust.org. -# - - -#=============================================================== -# -# Light SDF reader object (used by InChI generation example) -# -# Parsing MDL SD file records and collecting information -# (atomic, bonding, etc.) for further use -# The implementation is very 'light' and is provided for -# illustrative purposes only. -# -# -#=============================================================== - - -#---------------------------------------------------------------- -def sint(s): - """ smart convert to int """ - ss = string.strip(s) - if ss == '': - return 0 - return int(ss) -#---------------------------------------------------------------- -def sfloat(s): - """ smart convert to float """ - ss = string.strip(s) - if ss == '': - return 0.0 - return float(ss) -#---------------------------------------------------------------- -def stripw(s): - """ strip common-use whitespaces - - leading, trailing, and within-string """ - s1 = string.strip(s) - s1 = string.replace(s1,'\n','') - s1 = string.replace(s1,'\r','') - s1 = string.replace(s1,'\t','') - return s1 -#---------------------------------------------------------------- -def isstruct(record): - """ check if structural data present in SDF record - return - 1 if structural data present or - 0 - otherwise """ - - if string.count(record['Structure_as_text'],'\n') < 5: - return 0 - else: - return 1 -#---------------------------------------------------------------- -def convert_MDL_structure_as_text_to_preobject(text,strname=''): - """ convert structure in MDL's SDF record - from textual representation to object """ - - record = {} - - lines = string.split(text,'\n') - - record['Name'] = strname - if (strname==''): - name_line = stripw(lines[0]) - if (name_line!=''): - record['Name'] = name_line - - counts_line = lines[3] - - try: - natoms = sint(counts_line[:3]) - record['Natoms'] = natoms - except: - raise 'MDL_CT_ERROR_COUNTS',natoms - try: - nbonds = sint(counts_line[3:6]) - record['Nbonds'] = nbonds - except: - raise 'MDL_CT_ERROR_COUNTS',nbonds - try: - chiral_flag = sint(counts_line[12:15]) - except: - raise 'MDL_CT_ERROR_COUNTS', chiral_flag - try: - nproplines = sint(counts_line[30:33]) - #print 'nproplines',nproplines - except: - raise 'MDL_CT_ERROR_COUNTS', nproplines - - - # atom block - - atom_block = lines[4:4+natoms] - record['Atoms'] = [] - for line in atom_block: - try: - x = sfloat(line[:10]) - y = sfloat(line[10:20]) - z = sfloat(line[20:30]) - atname = string.strip(line[31:34]) - dd = sint(line[34:36]) - charge = sint(line[36:39]) - stereo_pairity = sint(line[39:42]) - hydrogen_count = sint(line[42:45]) - stereo_care = sint(line[45:48]) - valence = sint(line[48:51]) - h0_designator = sint(line[51:54]) - reac_component_type = sint(line[54:57]) - reac_component_num = sint(line[57:60]) - atom_mapping_num = sint(line[60:63]) - inv_ret_flag = sint(line[63:66]) - exact_change_flag = sint(line[66:69]) - record['Atoms'].append( (atname, charge, (x,y,z) , - stereo_pairity, - hydrogen_count, - stereo_care, - valence, - h0_designator, - reac_component_type, - reac_component_num, - atom_mapping_num, - inv_ret_flag, - exact_change_flag, - dd ) ) - except: - raise 'MDL_CT_ERROR_ATOM' - - - # bond block - bond_block = lines[4+natoms:4+natoms+nbonds] - record['Bonds'] = [] - for line in bond_block: - try: - atom1 = sint(line[:3]) - atom2 = sint(line[3:6]) - type = sint(line[6:9]) - stereo = sint(line[9:12]) - topology = sint(line[15:18]) - reac_center = sint(line[18:21]) - record['Bonds'].append( (atom1, atom2, type, stereo, topology, reac_center) ) - except: - raise 'MDL_CT_ERROR_BOND' - - # prop block - # NB: we do not refer to nproplines in counts line - # instead, check possible property lines directly - record['Prop'] = [] - for line in lines[4+natoms+nbonds:]: - try: - if line[:1]=="M": - record['Prop'].append(line) - except: - raise 'MDL_PROP_LINE_ERROR' - - return record - - - - - -#=============================================================== - -""" - Base class for light representation/processing of MDL SD file - Light means no conversion to internal molobject representation, - just parsing the text and collecting fields -""" - -import sys, os, time, string - - -class SDF_file: - """ - Base class for representation/processing of MDL's SD file - - SD file is represented as a collection (Python's list) - of records with associated file name. - - Each record is a Python's dictionary, i.e. a series of - pairs {key:value}. - - One key is pre-defined: 'Structure_as_text' - Corresponding value, record['Structure_as_text'], - contains structure representation as the text just as - it stands in SDF file. - - All other keys (and values) are established as described - in SDF file (key is that name which appeared in "> ..." - strings). - - - """ -#---------------------------------------------------------------- - def __init__(self): - """ SDF_file - object constructor """ - - self.records = [] - self.nrecords = 0 - self.fname = '' - self.fp = None - self.keys = [] - - -#---------------------------------------------------------------- - def open(self,fname): - """ open SD file for reading """ - - self.fname = fname - - try: - self.fp = open(fname,'rb') - self.nrecords = 0 - self.records = [] - except: - return 0 - return 1 - - -#---------------------------------------------------------------- - def read(self): - """ read SDF file content """ - - self.nrecords = 0 - self.records = [] - while 1: - rec = self.readnext() - if rec == None: - break - #else: - self.nrecords = self.nrecords + 1 - #print '[%-ld records]\r'%self.nrecords, - self.records.append(rec) - #pass - self.getkeys() - #print - - -#---------------------------------------------------------------- - def readnext(self): - """ read next record in SDF_file """ - - rec = {} - key = 'Structure_as_text' - rec[key] = '' - - # read line by line while no $$$$ sign occur - - while 1: - - line = self.fp.readline() - - if not line: - # EOF reached - # NB: last line in SDF should be empty - return None - - if line[:4] == '$$$$' : - # have end-of-record line - rec[key] = string.rstrip(rec[key]) #remove trailing linefeed - break - - if line[:1] == '>' : - # have field-name line - # remove trailing linefeed - rec[key] = string.rstrip(rec[key]) - # extract field name - pos0 = string.find(line[1:],'<') - pos = string.find(line[pos0+1:],'>') - if pos < 1: - key = 'empty' - else: - key = line[pos0+2:pos0+pos+1] - rec[key] = '' - - else: - # have field-content line - rec[key] = rec[key] + string.rstrip(line)+'\n' - - -# if rec != None: -# self.nrecords = self.nrecords + 1 -# self.records.append(rec) - - return rec - - -#---------------------------------------------------------------- - def getkeys(self,sort=0): - """ collect all the field names (keys) - optionally, sort them lexicographically """ - - self.keys = [] - for r in self.records: - rks = r.keys() - for rk in rks: - if rk not in self.keys: - self.keys.append(rk) - if sort>0: - self.keys.sort() - return self.keys - - -#---------------------------------------------------------------- - def exportcsv(self,fwname, numbers=[], fields=[]): - """ write content of specified fields (non-structural data) - into comma-separated-values (.csv) format file """ - - try: - fw = open(fwname,'wt') - except: - return 0 - if numbers == []: - numbers = range(self.nrecords) - - if fields == []: - fields = self.keys - - - # write field names in 1st line - - s = '' - for k in fields: - - if k!='Structure_as_text': # skip structure field - sk = k - if ' ' in sk: - if '"' in sk: - sk = string.replace(sk,'"','\'\'') - s = s + '"' + sk + '",' - else: - s = s + sk + ',' - fw.write('%-s\n'%s[:-1]) - - # for each record write the fields - - for i in numbers: - r = self.records[i] - s = '' - for k in fields: - if k!='Structure_as_text': - try: - val = r[k] - val = stripw(val) - except: - val = '' - if ' ' in val or ',' in val: - if '"' in val: - val = string.replace(val,'"','\'\'') - s = s + '"' + val + '",' - else: - s = s + val + ',' - - fw.write('%-s\n'%s[:-1]) - - return 1 - - -#---------------------------------------------------------------- - def write(self,fwname,numbers=[]): - """ write SDF file containing specified - records in specified order """ - - try: - fw = open(fwname,'wt') - except: - return 0 - if numbers == []: - numbers = range(self.nrecords) - for i in numbers: - r = self.records[i] - #place structure at first - fw.write('%-s\n'%r['Structure_as_text']) - for k in r.keys(): - if k!='Structure_as_text': - fw.write('> <%-s>\n'%k) - fw.write('%-s\n\n'%r[k]) - fw.write('$$$$\n') - return 1 - - -#---------------------------------------------------------------- - def getfieldduplicates(self,field): - """ get duplicates by field idfield""" - - dup = {} - ilist = range(self.nrecords) #all internal nos - for i in ilist: - m = self.records[i] - try: - f = m[field] - except: - # no such field in this record - continue - try: - #if this id still here, add item - dup[f] = dup[f] + [i] - except: - #if no this id presents, create entry - dup[f] = [i] - for dk in dup.keys(): - if len(dup[dk])<2: - del dup[dk] - return dup - - diff --git a/INCHI-1-SRC/INCHI_API/python_sample/make_inchi.py b/INCHI-1-SRC/INCHI_API/python_sample/make_inchi.py deleted file mode 100644 index 2a7048c..0000000 --- a/INCHI-1-SRC/INCHI_API/python_sample/make_inchi.py +++ /dev/null @@ -1,546 +0,0 @@ -#!/usr/bin/python -# -# International Chemical Identifier (InChI) -# Version 1 -# Software version 1.04 -# September 9, 2011 -# -# The InChI library and programs are free software developed under the -# auspices of the International Union of Pure and Applied Chemistry (IUPAC). -# Originally developed at NIST. Modifications and additions by IUPAC -# and the InChI Trust. -# -# IUPAC/InChI-Trust Licence No.1.0 for the -# International Chemical Identifier (InChI) Software version 1.04 -# Copyright (C) IUPAC and InChI Trust Limited -# -# This library is free software; you can redistribute it and/or modify it -# under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, -# or any later version. -# -# Please note that this library is distributed WITHOUT ANY WARRANTIES -# whatsoever, whether expressed or implied. See the IUPAC/InChI Trust -# Licence for the International Chemical Identifier (InChI) Software -# version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") -# for more details. -# -# You should have received a copy of the IUPAC/InChI Trust InChI -# Licence No. 1.0 with this library; if not, please write to: -# -# The InChI Trust -# c/o FIZ CHEMIE Berlin -# -# Franklinstrasse 11 -# 10587 Berlin -# GERMANY -# -# or email to: ulrich@inchi-trust.org. -# - - -""" - Test the INCHI API - -""" - -import os -import sys -import string - -from ctypes import * -from optparse import OptionParser - -from lightsdf import * -from PyINCHI import * - - -def make_pystring(p): - s = "" - try: - for c in p: - if c=='\0': - break - s = s + c - except: - pass - return s - - - -def process_sdf_record(insdf,irec,r,fw,fl,iopts,baux,getinchi,freeinchi,getikey,ikey,ixtra,inchilogmess, nerr): - - - # Skip if no structure field present - - if isstruct(r)==0: - return - - guessname='' - - - # Parse SD/MOL fields - - info = convert_MDL_structure_as_text_to_preobject(r['Structure_as_text'],guessname) - name = info['Name'] - if (name==''): - j = irec + 1 - name = insdf.fname+'#'+`j` - - - - nat = info['Natoms'] - nbonds = info['Nbonds'] -# fw.write('***************************************************************************\n') -# fw.write('RECORD %-d Name = %-s\n' % (irec+1,name) ) -# if (fl!=sys.stdout): -# fl.write('RECORD %-d Name = %-s\n' % (i+1,name) ) - - - - # Make array of inchi_Atom (storage space) anf resp. pointers - - iatoms = (inchi_Atom * nat) () - piatoms = (POINTER(inchi_Atom) * nat) () - - - # Fill inchi_Atom's by SDF record atom block content - - atoms = info['Atoms'] - ia = -1 - valences = [] - for a in atoms: - - ia = ia + 1 - atname, charge, (x,y,z) , stereo_pairity, hydrogen_count, stereo_care, valence, h0_designator, reac_component_type, reac_component_num, atom_mapping_num, inv_ret_flag, exact_change_flag, dd = a - x,y,z = (x,y,z) - valences.append(valence) - - # Make inchi_Atom - - elname = (c_byte * PYINCHI_ATOM_EL_LEN) (PYINCHI_ATOM_EL_LEN*0) - num_iso_H = (c_byte * (PYINCHI_NUM_H_ISOTOPES+1)) ((PYINCHI_NUM_H_ISOTOPES+1)*0) - neighbor = (c_short * PYINCHI_MAXVAL) (PYINCHI_MAXVAL*0) - bond_type = (c_byte * PYINCHI_MAXVAL) (PYINCHI_MAXVAL*0) - bond_stereo = (c_byte * PYINCHI_MAXVAL) (PYINCHI_MAXVAL*0) - - i = 0 - for c in atname: - elname[i] = ord(c) - i = i + 1 - isotopic_mass = 0 - if dd != 0: - isotopic_mass = dd + PYINCHI_ISOTOPIC_SHIFT_FLAG - radical = 0 - if charge == 4: - radical = 2 #doublet - else: - if charge != 0: - charge = 4 - charge - - iatom = inchi_Atom( - x, y, z, - neighbor, # to be filled later - bond_type, # to be filled later - bond_stereo, # to be filled later - elname, - 0, # num_bonds, to be filled later - num_iso_H, # to be filled later - isotopic_mass, - radical, - charge - ) - - - iatoms[ia] = iatom - pia = pointer(iatoms[ia]) - piatoms[ia] = pia - - - - # Get bond block - # push all bonding info to inchi_Atom structures - - bonds = info['Bonds'] - for b in bonds: - atom1, atom2, type, stereo, topology,reac_center = b - # fw.write('(%-d,%-d)\ttype=%-d\tster=%-d\ttopo=%-d\treac=%-d\n' % b ) - j = atom1-1 - k = atom2-1 - # both atnums must be valid - if ( (j>=0) and (j=0) and (knat): - continue - iatoms[ia].isotopic_mass = imass - - elif code=="CHG": - if (FirstChargeOrRadical > 0): - for j in range(nat): - iatoms[j].charge = 0 - iatoms[j].radical = 0 - FirstChargeOrRadical = 0 - for j in range(1,2*nv+1,2): - ia = int(vals[j]) - 1 - ichg = int(vals[j+1]) - if (ia<0) or (ia>nat): - continue - iatoms[ia].charge = ichg - - elif code=="RAD": - if (FirstChargeOrRadical > 0): - for j in range(nat): - iatoms[j].charge = 0 - iatoms[j].radical = 0 - FirstChargeOrRadical = 0 - for j in range(1,2*nv+1,2): - ia = int(vals[j]) - 1 - irad = int(vals[j+1]) #1=singlet, 2=doublet, 3=triplet - if irad==1: - #InChI should treat singlet (additional lone pair) as a triplet which reduces numH by 2 - irad = 3 - if (ia<0) or (ia>nat): - continue - iatoms[ia].radical = irad - - except: - pass - - for ia in range(0,nat): - - v = valences[ia] - - if v>0: - # need to use valences if any to find out number of H - - - nBondsValence = 0 - nNumAltBonds = 0 - for a1 in range (0, iatoms[ia].num_bonds): - if (iatoms[ia].bond_type[a1] < 4): - nBondsValence = nBondsValence + iatoms[ia].bond_type[a1] - else: - nNumAltBonds = nNumAltBonds + 1 - - if (nNumAltBonds == 2): - nBondsValence = nBondsValence + 3 - elif (nNumAltBonds == 3): - nBondsValence = nBondsValence + 4 - elif (nNumAltBonds > 0): - #Error: wrong number of aromatic bonds - #In this simplified code we do not provide any diagnostics - nBondsValence = nBondsValence + nNumAltBonds - - #number of H - if (nBondsValence == 0) and (valences[ia] == 15): - numH = 0 - else: - numH = valences[ia] - nBondsValence - if (numH < 0): - numH = 0 - - iatoms[ia].num_iso_H[0] = numH - - else: - # let inchi hydrogenize automatically - iatoms[ia].num_iso_H[0] = -1 - - # inchi dislike bond repetitions, clean the adjacncy lists... - for ia in range(0,nat): - nbr = iatoms[ia].neighbor[:] - bty = iatoms[ia].bond_type[:] - bst = iatoms[ia].bond_stereo[:] - nbs = iatoms[ia].num_bonds - iatoms[ia].num_bonds = 0 - for nb in range(0,nbs): - if (nbr[nb]) > ia: - iatoms[ia].neighbor[iatoms[ia].num_bonds] = nbr[nb] - iatoms[ia].bond_type[iatoms[ia].num_bonds] = bty[nb] - iatoms[ia].bond_stereo[iatoms[ia].num_bonds] = bst[nb] - iatoms[ia].num_bonds = iatoms[ia].num_bonds + 1 - - - # Compose inchi_Input - - istereo0D = (inchi_Stereo0D * 1) () - pistereo0D = (POINTER(inchi_Stereo0D) * 1) () - - iinput = inchi_Input( - iatoms, # inchi-style atoms - istereo0D, # stereo0D (empty) - iopts, # command line switches - nat, # natoms - 0 # num_stereo0D - ) - - - # Prepare inchi_Output - - szInChI = create_string_buffer(1) # NB: will be reallocd by inchi dll - szIKey = create_string_buffer(256) # - szXtra1 = create_string_buffer(256) # - szXtra2 = create_string_buffer(256) # - szAuxInfo = create_string_buffer(1) - szMessage = create_string_buffer(1) - szLog = create_string_buffer(1) - ioutput = inchi_Output( cast(pointer(szInChI), POINTER(c_char)), - cast(szAuxInfo, POINTER(c_char)), - cast(szMessage, POINTER(c_char)), - cast(szLog, POINTER(c_char)) ) - - - - # Call DLL function(s) - - result = getinchi(byref(iinput), byref(ioutput)) # 0 = okay, - # 1 => warning, - # 2=>error, - # 3=>fatal - - - # Process results - - si = make_pystring(ioutput.szInChI) - fw.write('%-s\n' % si) - - if (result!=0): - # problem! - if (fl!=sys.stdout): - fl.write('RECORD %-d Name = %-s\n\t' % (irec+1,name) ) - if (result==1): - fl.write('Warning : ') - else: - fl.write('InChI creation error (%-d) : ' % result) - nerr = nerr + 1 - fl.write('%-s\n' % make_pystring(ioutput.szMessage)) - - if (result<=1): - # no error or just warnings - if (ikey==1): - result1 = getikey(ioutput.szInChI, ixtra, ixtra, szIKey, szXtra1, szXtra2) - if (result1!=0): - if (fl!=sys.stdout): - fl.write('RECORD %-d Name = %-s\t' % (irec+1,name) ) - # special case: reversal of InChI failed - # InChIKey is computed, but the warning issued - if (result1==11): - fl.write('Warning: InChI reversal gets mismatching InChI string\n') - fw.write('InChIKey=%-s\n' % make_pystring(szIKey)) - else: - fl.write('Key calculation error (%-d)\n' % result1) - - - else: - fw.write('InChIKey=%-s\n' % make_pystring(szIKey)) - if (ixtra==1): - fw.write('XHash1=%-s\n' % make_pystring(szXtra1)) - fw.write('XHash2=%-s\n' % make_pystring(szXtra2)) - - - if baux: - fw.write('%-s\n' % make_pystring(ioutput.szAuxInfo)) - if inchilogmess=="": - inchilogmess = make_pystring(ioutput.szLog) - - - # Deallocate memory reallocated by inchi dll - - freeinchi(byref(ioutput)) - - - return (inchilogmess, nerr) - - - - - - - -if __name__ == "__main__": - - - - print "This Python demo program reads SD file and calls InChI library" - print "(libinchi.dll/libinchi.so.1) functions to generate " - print "InChI strings and InChIKey codes." - print "Note: the example is provided for illustrative purposes only." - print - - parser = OptionParser(usage="""\ -Usage: %prog [options] -""") - parser.add_option('-i', '--input', - type='string', action='store', - help='name of input SD file (required)') - parser.add_option('-o', '--output', - type='string', action='store', - help='name of output file (default=inchi_out.txt)') - parser.add_option('-l', '--log', - type='string', action='store', - help='name of log file (errors/warnings; default=stdout)') - parser.add_option('-s', '--start_record', - type='int', action='store', - help='starting number of record to be converted') - parser.add_option('-e', '--end_record', - type='int', action='store', - help='number of the last record to be converted') - parser.add_option('-x', '--aux', dest='aux', - help='print auxilary info', default=False, action='store_true') - parser.add_option('-k', '--key', dest='key', - help='calculate InChIKey', default=False, action='store_true') - parser.add_option('-z', '--xtra', dest='xtra', - help='calculate extra hash complementing InChIKey', default=False, action='store_true') - parser.add_option('-p', '--inchi_options', - type='string', action='store', - help='string with InChI options') - - opts, args = parser.parse_args() - - if not opts.input: - parser.print_help() - sys.exit(1) - fname = opts.input - - if opts.output: - fwrite = opts.output - else: - fwrite = "inchi_out.txt" - try: - fw = open(fwrite,'wt') - except: - print "COUD NOT OPEN OUTPUT FILE" - exit(2) - - if opts.inchi_options: - iopts = opts.inchi_options - else: - iopts = "" - - if opts.log: - try: - fl = open(opts.log,'wt') - except: - print "COUD NOT OPEN LOG FILE" - exit(3) - else: - fl = sys.stdout - if not opts.aux: - baux = 0 - else: - baux = 1 - - if not opts.key: - ikey = 0 - else: - ikey = 1 - - if not opts.xtra: - ixtra = 0 - else: - ixtra = 1 - - startr = 0 - if opts.start_record: - startr = opts.start_record - else: - startr = -1 - if opts.end_record: - endr = opts.end_record - else: - endr = -1 - - - insdf = SDF_file() - if not insdf.open(fname): - print '\n* IO fatal error *' - sys.exit(4) - - - libname = '' - opsys = sys.platform - if (opsys[:3]=='win'): - libname = 'libinchi.dll' - else: - if (opsys[:5]=='linux'): - libname = '/usr/lib/libinchi.so.1' - try: - print "QQ!" - libinchi = cdll.LoadLibrary(libname) - print "QQ2!" - getinchi = libinchi.GetINCHI - freeinchi = libinchi.FreeINCHI - getikey = libinchi.GetINCHIKeyFromINCHI - except: - print '** ERROR ** Could not access InChI library', libname - sys.exit() - inchilogmess = "" - timing = time.clock() - i = -1 - nerr = 0 - while 1: - try: - rec = insdf.readnext() - if rec == None: - break - else: - i = i + 1 - if (i+10): - if (i+1>endr): - break - print 'RECORD #',i+1 - inchilogmess, nerr = process_sdf_record(insdf,i,rec, - fw,fl, - iopts,baux, - getinchi,freeinchi,getikey, - ikey, ixtra, - inchilogmess, nerr) - except: - print "***ERROR IN RECORD",i+1,", SKIPPED" - fw.write('***ERROR IN RECORD %-ld", SKIPPED\n'% i) - - - fl.write('\n[Used InChI options:\n%-s]\n' % inchilogmess ) - - - fl.write( '\nProcessed records: %-ld \nTotal errors: %-ld' % (i+1,nerr) ) - - timing = time.clock() - timing - fl.write( '\nELAPSED TIME: %-.2f SEC' % timing ) diff --git a/INCHI-1-SRC/INCHI_API/python_sample/readme.txt b/INCHI-1-SRC/INCHI_API/python_sample/readme.txt deleted file mode 100644 index 5e78b55..0000000 --- a/INCHI-1-SRC/INCHI_API/python_sample/readme.txt +++ /dev/null @@ -1,106 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -This directory contains Python demo application illustrating how the InChI -library functions may be called from within Python. It has a simple Mol/SDfile -reader and produces InChI strings for a SD file and, optionally, generates -InChIKey. - -Tested with Python 2.7.2 64 bit under Windows 7 64bit and -Python 2.5.2 under Ubuntu 10.04.3 LTS. - -It is assumed that under MS Windows the InChI library name is 'libinchi.dll' -while under Linux it is '/usr/lib/libinchi.so.1' (see readme.txt file in -INCHI_API/gcc_so_makefile sub-directory for the description of InChI software -library creation). - -Note that this Python program should not be used as an InChI generator. The -code in this example is provided for illustrative purposes only. It does not -extensively check the input data and does not provide any diagnostic concerning -input structure(s). Due to its simplicity, this SDfile reader cannot interpret -correctly some of molfile features properly interpreted by inchi-1. - - -Usage ------ -make_inchi.py [options] - - -Options: - -h, --help show this help message and exit - -i INPUT, --input=INPUT - name of input SD file (required) - -o OUTPUT, --output=OUTPUT - name of output file (default=inchi_out.txt) - -l LOG, --log=LOG name of log file (errors/warnings; default=stdout) - -s START_RECORD, --start_record=START_RECORD - starting number of record to be converted - -e END_RECORD, --end_record=END_RECORD - number of the last record to be converted - -x, --aux print auxilary info - -k, --key calculate InChIKey - -z, --xtra calculate extra hash complementing InChIKey - -p INCHI_OPTIONS, --inchi_options=INCHI_OPTIONS - string with InChI options - - - -========= - FILES -========= - -readme.txt This file - -make_inchi.py Python demo app, main program - -PyINCHI.py Python interface to INCHI library - -lightsdf.py Light SDF reader: parses SD file records and collects - information - - - -========= - LINKS -========= - -IUPAC http://www.iupac.org/inchi -InChI Trust http://www.inchi-trust.org -InChI discussion group https://lists.sourceforge.net/lists/listinfo/inchi-discuss diff --git a/INCHI-1-SRC/INCHI_API/readme.txt b/INCHI-1-SRC/INCHI_API/readme.txt deleted file mode 100644 index d47a989..0000000 --- a/INCHI-1-SRC/INCHI_API/readme.txt +++ /dev/null @@ -1,133 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - - -This directory contains code, VC++ projects and gcc makefiles of -InChI generation library with API (Win32 - libinchi.dll; Linux - libinchi.so) -and demo applications which call libinchi. - -The library produces both standard and non-standard InChI/InChIKey. - -The library does not provide any support for graphic user interface (GUI). -It is not designed to work in a multithreaded environment and should be called -from only one thread at a time. - -Besides the InChI library itself, this directory contains examples of using -previously available and newly added InChI software library functionality -(inchi_main and make_inchi.py). Note that the demo programs are samples -which are not supposed to be used for the production. - - -========= - FILES -========= - -readme.txt This file - - -inchi_dll SUB-DIRECTORY - Contains InChI Library source code - - -inchi_main SUB-DIRECTORY - Contains ANSI-C demo application source to call InChI Library - libinchi.dll under Microsoft Windows or libinchi.so - under Linux or Unix. - - -vc9 SUB-DIRECTORY - - inchi_dll SUB-DIRECTORY - Contains MS Visual C++ 2008 project to build dynamically - linked library libinchi.dll under Windows. - inchi_main SUB-DIRECTORY - Contains MS Visual C++ 2008 project to build both - dynamically linked library libinchi.dll and the testing - application inchi_main.exe under MS Windows (both - library and executable are placed into sub-directory - vc9/inchi_dll/Release). - - -gcc_so_makefile SUB-DIRECTORY - Contains a gcc makefile for INCHI_MAIN + INCHI_DLL code - to create a InChI library as a shared object (Linux) - or dll (Windows) dynamically linked to the main program - - result SUB-DIRECTORY - Contains shared object libinchi.so.1.04.00.gz - and demo application inchi_main.gz (for Linux) - - -python_sample SUB-DIRECTORY - Contains Python demo application calling - InChI Library functions. - - - -Notes on inchi_main demo application ------------------------------------- - -Defining CREATE_INCHI_STEP_BY_STEP in e_mode.h makes program use the -modularized interface to InChI generation process. Modularized interface -is used by default; commenting out the line containing the #define makes -the program use software version 1.01 ("classic") interface to InChI -generation process. - -If the demo application is compiled with CREATE_INCHI_STEP_BY_STEP option, -an additional defining of OUTPUT_NORMALIZATION_DATA in e_mode.h makes the -program output the intermediate (normalization) data into the log file. -The related data structures are described in header files inchi_api.h; -their use is exemplified in e_ichimain_a.c file. -Note that including the output of the intermediate (normalization) data may -produce a very long log file. - -Please notice that /D "BUILD_LINK_AS_DLL" Visual C ++ compiler option is -necessary to create and link the dll and the testing executable with -Microsoft Visual C++ under Win32. - - - -========= - LINKS -========= - -IUPAC http://www.iupac.org/inchi -InChI Trust http://www.inchi-trust.org -InChI discussion group https://lists.sourceforge.net/lists/listinfo/inchi-discuss diff --git a/INCHI-1-SRC/INCHI_API/vc9/inchi_dll/INCHI_DLL.APS b/INCHI-1-SRC/INCHI_API/vc9/inchi_dll/INCHI_DLL.APS deleted file mode 100644 index b7bb7f8..0000000 Binary files a/INCHI-1-SRC/INCHI_API/vc9/inchi_dll/INCHI_DLL.APS and /dev/null differ diff --git a/INCHI-1-SRC/INCHI_API/vc9/inchi_dll/inchi_dll.vcproj b/INCHI-1-SRC/INCHI_API/vc9/inchi_dll/inchi_dll.vcproj deleted file mode 100644 index e376def..0000000 --- a/INCHI-1-SRC/INCHI_API/vc9/inchi_dll/inchi_dll.vcproj +++ /dev/null @@ -1,659 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/INCHI-1-SRC/INCHI_API/vc9/inchi_main/inchi_main.aps b/INCHI-1-SRC/INCHI_API/vc9/inchi_main/inchi_main.aps deleted file mode 100644 index f445734..0000000 Binary files a/INCHI-1-SRC/INCHI_API/vc9/inchi_main/inchi_main.aps and /dev/null differ diff --git a/INCHI-1-SRC/INCHI_API/vc9/inchi_main/inchi_main1.aps b/INCHI-1-SRC/INCHI_API/vc9/inchi_main/inchi_main1.aps deleted file mode 100644 index 3071779..0000000 Binary files a/INCHI-1-SRC/INCHI_API/vc9/inchi_main/inchi_main1.aps and /dev/null differ diff --git a/INCHI-1-SRC/INCHI_BASE/readme.txt b/INCHI-1-SRC/INCHI_BASE/readme.txt new file mode 100644 index 0000000..3d1e51e --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/readme.txt @@ -0,0 +1,41 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +The sub-directory 'src' contains C source files of common codebase +which is used by both InChI Library and inchi-1 executable. diff --git a/INCHI-1-SRC/INCHI_BASE/src/aux2atom.h b/INCHI-1-SRC/INCHI_BASE/src/aux2atom.h new file mode 100644 index 0000000..08e5f98 --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/aux2atom.h @@ -0,0 +1,1512 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _AUX2ATOM_H_ +#define _AUX2ATOM_H_ + + +#ifdef TARGET_API_LIB + + +/*************************************************************************/ +S_SHORT *is_in_the_slist( S_SHORT *pathAtom, S_SHORT nNextAtom, int nPathLen ) +{ + for ( ; nPathLen && *pathAtom != nNextAtom; nPathLen--, pathAtom++ ) + ; + return nPathLen? pathAtom : NULL; +} +/************************************************/ +int is_element_a_metal( char szEl[] ) +{ + static const char szMetals[] = "K;V;Y;W;U;" + "Li;Be;Na;Mg;Al;Ca;Sc;Ti;Cr;Mn;Fe;Co;Ni;Cu;Zn;Ga;Rb;Sr;Zr;" + "Nb;Mo;Tc;Ru;Rh;Pd;Ag;Cd;In;Sn;Sb;Cs;Ba;La;Ce;Pr;Nd;Pm;Sm;" + "Eu;Gd;Tb;Dy;Ho;Er;Tm;Yb;Lu;Hf;Ta;Re;Os;Ir;Pt;Au;Hg;Tl;Pb;" + "Bi;Po;Fr;Ra;Ac;Th;Pa;Np;Pu;Am;Cm;Bk;Cf;Es;Fm;Md;No;Lr;Rf;"; + const int len = strlen(szEl); + const char *p; + + if ( 0 < len && len <= 2 && + isalpha( UCINT szEl[0] ) && isupper( szEl[0] ) && + (p = strstr(szMetals, szEl) ) && p[len] == ';' ) + { + return 1; /*return AtType_Metal;*/ + } + return 0; +} + +#endif + + + + + + +#define INPUT_FILE INCHI_IOSTREAM + + + +#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) + +#ifdef TARGET_API_LIB +/* #define InchiToAtom ll_InchiToInchi_Atom */ +#else +/*#define InchiToAtom ee_InchiToIbChI_Atom*/ +#define FindToken e_FindToken +#define LoadLine e_LoadLine +#endif + +#define AT_NUM_BONDS(AT) (AT).num_bonds +#define ATOM_NUMBER AT_NUM +#define IN_NEIGH_LIST is_in_the_slist +/*#define INPUT_FILE INCHI_IOSTREAM*/ +#define Create_Atom CreateInchi_Atom +#define AT_BONDS_VAL(AT,I) AT[I].num_iso_H[0] +#define ISOLATED_ATOM (-15) +#define NUM_ISO_Hk(AT,I,K) AT[I].num_iso_H[K+1] +#define IS_METAL_ATOM(AT,I) is_element_a_metal( AT[I].elname ) + +#else + +#define inchi_Atom inp_ATOM +#define AT_NUM_BONDS(AT) (AT).valence +#define ATOM_NUMBER AT_NUMB +#define IN_NEIGH_LIST is_in_the_list +#define inchi_NUMH2(AT,N) NUMH(AT,N) +/*#define InchiToAtom cc_InchiToInpAtom*/ +/*#define INPUT_FILE FILE*/ +#define Create_Atom CreateInpAtom +#define AT_BONDS_VAL(AT,I) AT[I].chem_bonds_valence +#define ISOLATED_ATOM 15 +#define NUM_ISO_Hk(AT,I,K) AT[I].num_iso_H[K] +#define IS_METAL_ATOM(AT,I) is_el_a_metal( AT[I].el_number ) + +#endif + +/*****************************************************************************/ + +#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) + +/*****************************************************************************/ +int InchiToInchi_Atom ( INCHI_IOSTREAM *inp_file, + inchi_Stereo0D **stereo0D, + int *num_stereo0D, + int bDoNotAddH, + int vABParityUnknown, + INPUT_TYPE nInputType, + inchi_Atom **at, + int max_num_at, + int *num_dimensions, + int *num_bonds, + char *pSdfLabel, + char *pSdfValue, + long *Id, + INCHI_MODE *pInpAtomFlags, + int *err, + char *pStrErr ); + +int InchiToInchi_Atom ( INCHI_IOSTREAM *inp_file, + inchi_Stereo0D **stereo0D, + int *num_stereo0D, + int bDoNotAddH, + int vABParityUnknown, + INPUT_TYPE nInputType, + inchi_Atom **at, + int max_num_at, + int *num_dimensions, + int *num_bonds, + char *pSdfLabel, + char *pSdfValue, + long *Id, + INCHI_MODE *pInpAtomFlags, + int *err, + char *pStrErr ) +{ + return InchiToAtom ( inp_file, + NULL, stereo0D, num_stereo0D, + bDoNotAddH, vABParityUnknown, + nInputType, + at, max_num_at, + num_dimensions, num_bonds, + pSdfLabel, pSdfValue, + Id, + pInpAtomFlags, + err, pStrErr ); +} + +#endif + + +#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) +#else + +/****************************************************************************/ +int InchiToAtom( INCHI_IOSTREAM *inp_file, + MOL_COORD **szCoord, + inchi_Stereo0D **stereo0D, + int *num_stereo0D, + int bDoNotAddH, + int vABParityUnknown, + INPUT_TYPE nInputType, + inchi_Atom **at, + int max_num_at, + int *num_dimensions, + int *num_bonds, + char *pSdfLabel, + char *pSdfValue, + long *Id, + INCHI_MODE *pInpAtomFlags, + int *err, + char *pStrErr ) +{ +int num_atoms = 0, bFindNext = 0, len, bHeaderRead, bItemIsOver, bErrorMsg, bRestoreInfo; +int bFatal = 0, num_struct = 0; +int i, k, k2, res, bond_type, bond_stereo1, bond_stereo2, bond_char, neigh, bond_parity, bond_parityNM; +int bTooLongLine, res2, bTooLongLine2, pos, hlen, hk; +long longID; +char szLine[INCHI_LINE_LEN], szNextLine[INCHI_LINE_ADD], *p, *q, *s, parity; +int b2D=0, b3D=0, b23D, nNumBonds = 0, bNonZeroXYZ, bNonMetal; +int len_stereo0D = 0, max_len_stereo0D = 0; +inchi_Stereo0D *atom_stereo0D = NULL; +inchi_Atom *atom = NULL; +MOL_COORD *pszCoord = NULL; +INCHI_MODE InpAtomFlags = 0; /* 0 or FLAG_INP_AT_NONCHIRAL or FLAG_INP_AT_CHIRAL */ +static const char szIsoH[] = "hdt"; +/* plain tags */ +static const char sStructHdrPln[] = "Structure:"; +static const char sStructHdrPlnNoLblVal[] = " is missing"; +static char sStructHdrPlnAuxStart[64] =""; /*"$1.1Beta/";*/ +static int lenStructHdrPlnAuxStart = 0; +static const char sStructHdrPlnRevAt[] = "/rA:"; +static const char sStructHdrPlnRevBn[] = "/rB:"; +static const char sStructHdrPlnRevXYZ[] = "/rC:"; +const char *sToken; +int lToken; + + if ( !lenStructHdrPlnAuxStart ) + lenStructHdrPlnAuxStart = sprintf( sStructHdrPlnAuxStart, "AuxInfo=" ); + + + if ( at ) + { + + if ( *at && max_num_at ) + memset( *at, 0, max_num_at * sizeof(**at) ); + +#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) + if ( stereo0D && num_stereo0D ) + { + if ( *stereo0D && *num_stereo0D ) + { + max_len_stereo0D = *num_stereo0D; + memset( *stereo0D, 0, max_len_stereo0D * sizeof( **stereo0D )); + } + else + max_len_stereo0D = 0; + } +#else + if ( szCoord && *szCoord ) + { + inchi_free( *szCoord ); + *szCoord = NULL; + } +#endif + } + else /* if ( at ) */ + bFindNext = 1; + + + bHeaderRead = bErrorMsg = bRestoreInfo = 0; + *num_dimensions = *num_bonds = 0; + + + /*************************************************************/ + /* extract reversibility info from plain text INChI format */ + /*************************************************************/ + + if ( nInputType == INPUT_INCHI_PLAIN ) + { + + bHeaderRead = hk = 0; + + while ( 0 < (res = inchi_ios_getsTab( szLine, sizeof(szLine)-1, inp_file, &bTooLongLine ) ) ) + { + + /********************* find and interpret structure header ************/ + if ( !bTooLongLine && + (hlen=sizeof(sStructHdrPln)-1, !memcmp(szLine, sStructHdrPln, hlen)) ) + { + p = szLine + hlen; + longID = 0; + num_atoms = 0; + + /* structure number */ + longID = strtol( p, &q, 10 ); + if ( q && q[0] == '.' && q[1] == ' ' ) + p = q+2; + p = p + strspn( p, " \n\r" ); + + if ( pSdfLabel ) + pSdfLabel[0] = '\0'; + + if ( pSdfValue ) + pSdfValue[0] = '\0'; + + + if ( *p ) + { + /* has label name */ + + /*p ++;*/ + if ( q = strchr( p, '=' ) ) + { + + /* '=' separates label name from the value */ + len = inchi_min( q-p+1, MAX_SDF_HEADER-1); + + if ( pSdfLabel ) + { + mystrncpy( pSdfLabel, p, len ); + lrtrim( pSdfLabel, &len ); + } + + p = q+1; + q = p + (int)strlen( p ); + + if ( q-p > 0 ) + { + len = inchi_min( q-p+1, MAX_SDF_VALUE-1); + if ( pSdfValue ) + { + mystrncpy( pSdfValue, p, len ); + } + p = q; + } + } + else if ( q = strstr( p, sStructHdrPlnNoLblVal ) ) + { + len = inchi_min( q-p+1, MAX_SDF_HEADER-1); + if ( pSdfLabel ) { + mystrncpy( pSdfLabel, p, len ); + } + p = q+1; + } + } + + if ( Id ) + *Id = longID; + + bHeaderRead = 1; + bErrorMsg = bRestoreInfo = 0; + } + else if ( !memcmp( szLine, sStructHdrPlnAuxStart, lenStructHdrPlnAuxStart) ) + { + /* found the header of the AuxInfo, read AuxInfo head of the line */ + + if ( !bHeaderRead ) + { + longID = 0; + if ( Id ) + *Id = longID; + if ( pSdfLabel ) + pSdfLabel[0] = '\0'; + if ( pSdfValue ) + pSdfValue[0] = '\0'; + } + + bHeaderRead = 0; + + /* check for empty "AuxInfo=ver//" */ + + p = strchr( szLine + lenStructHdrPlnAuxStart, '/' ); + + if ( p && p[1] == '/' && (!p[2] || '\n' == p[2]) ) + { + goto bypass_end_of_INChI_plain; + } + + /***************** search for atoms block (plain) **********************/ + + p = szLine; + sToken = sStructHdrPlnRevAt; + lToken = sizeof(sStructHdrPlnRevAt)-1; + + /* search for sToken in the line; load next segments of the line if sToken has not found */ + + p = FindToken( inp_file, &bTooLongLine, sToken, lToken, + szLine, sizeof(szLine), p, &res ); + + if ( !p ) + { + *err = INCHI_INP_ERROR_ERR; + num_atoms = INCHI_INP_ERROR_RET; + TREAT_ERR (*err, 0, "Missing atom data"); + goto bypass_end_of_INChI_plain; + } + else + { + /* atoms block started */ + + i = 0; + res2 = bTooLongLine2 = -1; + bItemIsOver = (s = strchr( p, '/') ) || !bTooLongLine; + + while ( 1 ) + { + + p = LoadLine( inp_file, &bTooLongLine, &bItemIsOver, &s, + szLine, sizeof(szLine), INCHI_LINE_ADD, p, &res ); + + if ( !i ) + { + /* allocate atom */ + num_atoms = strtol( p, &q, 10 ); + + if ( !num_atoms || !q || !*q ) + { + num_atoms = 0; /* no atom data */ + goto bypass_end_of_INChI_plain; + } + p = q; + + /* Molfile chirality flag */ + switch( *p ) + { + case 'c': + InpAtomFlags |= FLAG_INP_AT_CHIRAL; + p ++; + break; + case 'n': + InpAtomFlags |= FLAG_INP_AT_NONCHIRAL; + p ++; + break; + } + + if ( at && *at ) + { + if ( num_atoms > max_num_at ) + { + inchi_free( *at ); + *at = NULL; + } + else + { + memset( *at, 0, max_num_at * sizeof( **at ) ); + atom = *at; + } + } + + if ( !at || !*at ) + { + + atom = Create_Atom( num_atoms+1 ); + + if ( !atom ) + { + num_atoms = INCHI_INP_FATAL_RET; /* was -1; error */ + *err = INCHI_INP_FATAL_ERR; + TREAT_ERR (*err, 0, "Out of RAM"); + goto bypass_end_of_INChI_plain; + } + } + + if ( stereo0D && *stereo0D ) + { + if ( num_atoms > max_len_stereo0D ) + FreeInchi_Stereo0D( stereo0D ); + else + { + memset( *stereo0D, 0, max_len_stereo0D * sizeof( **stereo0D ) ); + atom_stereo0D = *stereo0D; + } + } + + if ( !stereo0D || !*stereo0D ) + { + max_len_stereo0D = num_atoms+1; + + atom_stereo0D = CreateInchi_Stereo0D( max_len_stereo0D ); + + if ( !atom_stereo0D ) + { + num_atoms = INCHI_INP_FATAL_RET; /* fatal error: cannot allocate */ + *err = INCHI_INP_FATAL_ERR; + TREAT_ERR (*err, 0, "Out of RAM"); + goto bypass_end_of_INChI_plain; + } + } + } + + /* element, first char */ + if ( !isalpha( UCINT *p ) || !isupper( UCINT *p ) || i >= num_atoms ) + { + break; /* end of atoms block */ + } + + atom[i].elname[0] = *p ++; + + /* element, second char */ + if ( isalpha( UCINT *p ) && islower( UCINT *p ) ) + { + atom[i].elname[1] = *p ++; + } + + #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) + #else + atom[i].el_number = get_periodic_table_number( atom[i].elname ); + #endif + + /* bonds' valence + number of non-isotopic H */ + if ( isdigit( UCINT *p ) ) + { + AT_BONDS_VAL(atom,i) = (char)strtol( p, &q, 10 ); + if ( !AT_BONDS_VAL(atom,i) ) + AT_BONDS_VAL(atom,i) = ISOLATED_ATOM; /* same convention as in MOLfile, found zero bonds valence */ + p = q; + } + + /* charge */ + atom[i].charge = (*p == '+')? 1 : (*p == '-') ? -1 + : 0; + if ( atom[i].charge ) + { + p ++; + if ( isdigit( UCINT *p ) ) + { + atom[i].charge *= (S_CHAR)(strtol( p, &q, 10 ) & CHAR_MASK); + p = q; + } + } + + /* radical */ + if ( *p == '.' ) + { + p ++; + if ( isdigit( UCINT *p ) ) + { + atom[i].radical = (S_CHAR)strtol( p, &q, 10 ); + p = q; + } + } + + /* isotopic mass */ + if ( *p == 'i' ) + { + p ++; + if ( isdigit( UCINT *p ) ) + { + int mw = strtol( p, &q, 10 ); + p = q; + + #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) + atom[i].isotopic_mass = mw; + #else + mw -= get_atomic_mass_from_elnum( atom[i].el_number ); + if ( mw >= 0 ) + mw ++; + atom[i].iso_atw_diff = mw; + #endif + } + } + + /* parity */ + switch( *p ) + { + case 'o': + parity = INCHI_PARITY_ODD; + p ++; + break; + case 'e': + parity = INCHI_PARITY_EVEN; + p ++; + break; + case 'u': + parity = INCHI_PARITY_UNKNOWN; + p ++; + break; + case '?': + parity = INCHI_PARITY_UNDEFINED; + p ++; + break; + default: + parity = 0; + break; + } + + if ( parity ) + { + atom_stereo0D[len_stereo0D].central_atom = i; + atom_stereo0D[len_stereo0D].parity = parity; + atom_stereo0D[len_stereo0D].type = INCHI_StereoType_Tetrahedral; + len_stereo0D ++; + } + + /* isotopic h, d, t */ + for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) + { + if ( *p == szIsoH[k] ) { + NUM_ISO_Hk(atom,i,k) = 1; + p ++; + if ( isdigit( UCINT *p ) ) { + NUM_ISO_Hk(atom,i,k) = (char)strtol( p, &q, 10 ); + p = q; + } + } + } + + i ++; + } + + + if ( !bItemIsOver || i != num_atoms || s && p != s ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong number of atoms"); + goto bypass_end_of_INChI_plain; + } + } + + /***************** search for bonds block (plain) and read it *****************/ + + /*p = szLine;*/ + sToken = sStructHdrPlnRevBn; + lToken = sizeof(sStructHdrPlnRevBn)-1; + + /* search for sToken in the line; load next segments of the line if sToken has not found */ + + p = FindToken( inp_file, &bTooLongLine, sToken, lToken, + szLine, sizeof(szLine), p, &res ); + + if ( !p ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Missing bonds data"); + goto bypass_end_of_INChI_plain; + } + else + { + /* bonds block started */ + + i = 1; + + res2 = bTooLongLine2 = -1; + + bItemIsOver = (s = strchr( p, '/') ) || !bTooLongLine; + + if ( 1 == num_atoms ) + { + /* needed because the next '/' may be still out of szLine */ + + p = LoadLine( inp_file, &bTooLongLine, &bItemIsOver, &s, + szLine, sizeof(szLine), INCHI_LINE_ADD, p, &res ); + } + + while ( i < num_atoms ) + { + + p = LoadLine( inp_file, &bTooLongLine, &bItemIsOver, &s, + szLine, sizeof(szLine), INCHI_LINE_ADD, p, &res ); + + if ( i >= num_atoms || s && p >= s ) + { + break; /* end of bonds (plain) */ + } + + /* bond, first char */ + if ( *p == ';' ) + { + p ++; + i ++; + continue; + } + + if ( !isalpha( UCINT *p ) ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong bonds data"); + goto bypass_end_of_INChI_plain; + } + + bond_char = *p ++; + + /* bond parity */ + switch( *p ) + { + case '-': + bond_parity = INCHI_PARITY_ODD; + p ++; + break; + case '+': + bond_parity = INCHI_PARITY_EVEN; + p ++; + break; + case 'u': + bond_parity = INCHI_PARITY_UNKNOWN; + p ++; + break; + case '?': + bond_parity = INCHI_PARITY_UNDEFINED; + p ++; + break; + default: + bond_parity = 0; + break; + } + + if ( bond_parity ) + { + switch( *p ) + { + case '-': + bond_parityNM = INCHI_PARITY_ODD; + p ++; + break; + case '+': + bond_parityNM = INCHI_PARITY_EVEN; + p ++; + break; + case 'u': + bond_parityNM = INCHI_PARITY_UNKNOWN; + p ++; + break; + case '?': + bond_parityNM = INCHI_PARITY_UNDEFINED; + p ++; + break; + default: + bond_parityNM = 0; + break; + } + } + else + { + bond_parityNM = 0; + } + + /* neighbor of the current atom */ + if ( !isdigit( UCINT *p ) ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong bonds data"); + goto bypass_end_of_INChI_plain; + } + + neigh = (int)strtol( p, &q, 10 )-1; + + if ( i >= num_atoms || neigh >= num_atoms ) { + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Bond to nonexistent atom"); + goto bypass_end_of_INChI_plain; + } + + p = q; + bond_stereo1 = bond_stereo2 = 0; + + /* bond type & 2D stereo */ + switch( bond_char ) + { + case 'v': + bond_type = INCHI_BOND_TYPE_SINGLE; + bond_stereo1 = INCHI_BOND_STEREO_SINGLE_1EITHER; + bond_stereo2 = INCHI_BOND_STEREO_SINGLE_2EITHER; + break; + case 'V': + bond_type = INCHI_BOND_TYPE_SINGLE; + bond_stereo1 = INCHI_BOND_STEREO_SINGLE_2EITHER; + bond_stereo2 = INCHI_BOND_STEREO_SINGLE_1EITHER; + break; + case 'w': + bond_type = INCHI_BOND_TYPE_DOUBLE; + bond_stereo1 = + bond_stereo2 = INCHI_BOND_STEREO_DOUBLE_EITHER; + break; + case 's': + bond_type = INCHI_BOND_TYPE_SINGLE; + break; + case 'd': + bond_type = INCHI_BOND_TYPE_DOUBLE; + break; + case 't': + bond_type = INCHI_BOND_TYPE_TRIPLE; + break; + case 'a': + bond_type = INCHI_BOND_TYPE_ALTERN; + break; + case 'p': + bond_type = INCHI_BOND_TYPE_SINGLE; + bond_stereo1 = INCHI_BOND_STEREO_SINGLE_1UP; + bond_stereo2 = INCHI_BOND_STEREO_SINGLE_2UP; + break; + case 'P': + bond_type = INCHI_BOND_TYPE_SINGLE; + bond_stereo1 = INCHI_BOND_STEREO_SINGLE_2UP; + bond_stereo2 = INCHI_BOND_STEREO_SINGLE_1UP; + break; + case 'n': + bond_type = INCHI_BOND_TYPE_SINGLE; + bond_stereo1 = INCHI_BOND_STEREO_SINGLE_1DOWN; + bond_stereo2 = INCHI_BOND_STEREO_SINGLE_2DOWN; + break; + case 'N': + bond_type = INCHI_BOND_TYPE_SINGLE; + bond_stereo1 = INCHI_BOND_STEREO_SINGLE_2DOWN; + bond_stereo2 = INCHI_BOND_STEREO_SINGLE_1DOWN; + break; + default: + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong bond type"); + goto bypass_end_of_INChI_plain; + } + + k = AT_NUM_BONDS(atom[i]) ++; + + atom[i].bond_type[k] = bond_type; + atom[i].bond_stereo[k] = bond_stereo1; + atom[i].neighbor[k] = (ATOM_NUMBER)neigh; + + k2 = AT_NUM_BONDS(atom[neigh]) ++; + atom[neigh].bond_type[k2] = bond_type; + atom[neigh].bond_stereo[k2] = bond_stereo2; + atom[neigh].neighbor[k2] = (ATOM_NUMBER)i; + + bond_parity |= (bond_parityNM << SB_PARITY_SHFT); + + if ( bond_parity ) + { + if ( max_len_stereo0D <= len_stereo0D ) + { + /* realloc atom_Stereo0D */ + + inchi_Stereo0D *new_atom_stereo0D = CreateInchi_Stereo0D( max_len_stereo0D+num_atoms ); + + if ( !new_atom_stereo0D ) + { + num_atoms = INCHI_INP_FATAL_RET; /* fatal error: cannot allocate */ + *err = INCHI_INP_FATAL_ERR; + TREAT_ERR (*err, 0, "Out of RAM"); + goto bypass_end_of_INChI_plain; + } + + memcpy( new_atom_stereo0D, atom_stereo0D, len_stereo0D * sizeof(*atom_stereo0D) ); + FreeInchi_Stereo0D( &atom_stereo0D ); + atom_stereo0D = new_atom_stereo0D; + max_len_stereo0D += num_atoms; + } + + /* (a) i may be allene endpoint and neigh = allene middle point or + (b) i may be allene middle point and neigh = allene endpoint + !!!!! CURRENTLY ONLY (b) IS ALLOWED !!!!! + */ + + atom_stereo0D[len_stereo0D].neighbor[1] = neigh; /* neigh < i */ + atom_stereo0D[len_stereo0D].neighbor[2] = i; + atom_stereo0D[len_stereo0D].parity = bond_parity; + atom_stereo0D[len_stereo0D].type = INCHI_StereoType_DoubleBond; /* incl allenes & cumulenes */ + len_stereo0D ++; + } + } + + if ( !bItemIsOver || i != num_atoms || s && p != s ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong number of bonds"); + goto bypass_end_of_INChI_plain; + } + } + + /***************** search for coordinates block (plain) **********************/ + /*p = szLine;*/ + + sToken = sStructHdrPlnRevXYZ; + lToken = sizeof(sStructHdrPlnRevXYZ)-1; + + /* search for sToken in the line; load next segments of the line if sToken has not found */ + + p = FindToken( inp_file, &bTooLongLine, sToken, lToken, + szLine, sizeof(szLine), p, &res ); + + if ( !p ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Missing atom coordinates data"); + goto bypass_end_of_INChI_plain; + } + else + { + /* coordinates block started */ + if ( pszCoord = (MOL_COORD*) inchi_malloc(inchi_max(num_atoms,1) * sizeof(MOL_COORD)) ) + { + memset( pszCoord, ' ', inchi_max(num_atoms,1) * sizeof(MOL_COORD)); + } + else + { + num_atoms = INCHI_INP_FATAL_RET; /* allocation error */ + *err = INCHI_INP_FATAL_ERR; + TREAT_ERR (*err, 0, "Out of RAM"); + goto bypass_end_of_INChI_plain; + } + + i = 0; + res2 = bTooLongLine2 = -1; + bItemIsOver = (s = strchr( p, '/') ) || !bTooLongLine; + + while ( i < num_atoms ) + { + + p = LoadLine( inp_file, &bTooLongLine, &bItemIsOver, &s, + szLine, sizeof(szLine), INCHI_LINE_ADD, p, &res ); + + if ( i >= num_atoms || s && p >= s ) { + break; /* end of bonds (plain) */ + } + + /* coord, first char */ + if ( *p == ';' ) + { + for ( k = 0; k < NUM_COORD; k ++ ) + { + pszCoord[i][LEN_COORD*k + 4] = '0'; + } + p ++; + i ++; + continue; + } + + for ( k = 0; k < 3; k ++ ) + { + double xyz; + bNonZeroXYZ = 0; + if ( *p == ';' ) + { + pszCoord[i][LEN_COORD*k + 4] = '0'; + xyz = 0.0; + } else + if ( *p == ',' ) + { + /* empty */ + pszCoord[i][LEN_COORD*k + 4] = '0'; + xyz = 0.0; + p ++; + } + else + { + xyz = strtod( p, &q ); + bNonZeroXYZ = fabs(xyz) > MIN_BOND_LENGTH; + if ( q != NULL ) { + memcpy( pszCoord[i]+LEN_COORD*k, p, q-p ); + if ( *q == ',' ) + q ++; + p = q; + } + else + pszCoord[i][LEN_COORD*k + 4] = '0'; + } + + switch( k ) + { + case 0: + atom[i].x = xyz; + b2D |= bNonZeroXYZ; + break; + case 1: + atom[i].y = xyz; + b2D |= bNonZeroXYZ; + break; + case 2: + b3D |= bNonZeroXYZ; + atom[i].z = xyz; + break; + } + } + + if ( *p == ';' ) + { + p ++; /* end of this triple of coordinates */ + i ++; + } + else + { + num_atoms = INCHI_INP_ERROR_RET; /* error in input data: atoms, bonds & coord must be present together */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong atom coordinates data"); + goto bypass_end_of_INChI_plain; + } + } + + if ( !bItemIsOver || s && p != s || i != num_atoms ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong number of coordinates"); + goto bypass_end_of_INChI_plain; + } + } /* end of coordinates */ + + + /* set special valences and implicit H (xml) */ + + b23D = b2D | b3D; + b2D = b3D = 0; + if ( at ) + { + if ( !*at ) + { + int a1, a2, n1, n2, valence; + int chem_bonds_valence; + int nX=0, nY=0, nZ=0, nXYZ; + *at = atom; + + /* special valences */ + + for ( bNonMetal = 0; bNonMetal < 1; bNonMetal ++ ) + { + + for ( a1 = 0; a1 < num_atoms; a1 ++ ) + { + + int num_bond_type[MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE + 1]; + +#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) +#else + int bHasMetalNeighbor=0; +#endif + + memset( num_bond_type, 0, sizeof(num_bond_type) ); + + valence = AT_BONDS_VAL(atom, a1); /* save atom valence if available */ + AT_BONDS_VAL(atom, a1) = 0; + +#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) +#else + atom[a1].orig_at_number = a1+1; +#endif + + nX = nY = nZ = 0; + + for ( n1 = 0; n1 < AT_NUM_BONDS(atom[a1]); n1 ++ ) + { + bond_type = atom[a1].bond_type[n1] - MIN_INPUT_BOND_TYPE; + if ( bond_type < 0 || bond_type > MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE ) + { + bond_type = 0; + TREAT_ERR (*err, 0, "Unknown bond type in InChI aux assigned as a single bond"); + } + + num_bond_type[ bond_type ] ++; + nNumBonds ++; + if ( b23D ) + { + neigh = atom[a1].neighbor[n1]; + nX |= (fabs(atom[a1].x - atom[neigh].x) > MIN_BOND_LENGTH); + nY |= (fabs(atom[a1].y - atom[neigh].y) > MIN_BOND_LENGTH); + nZ |= (fabs(atom[a1].z - atom[neigh].z) > MIN_BOND_LENGTH); + } + } + + chem_bonds_valence = 0; + for ( n1 = 0; MIN_INPUT_BOND_TYPE + n1 <= 3 && MIN_INPUT_BOND_TYPE + n1 <= MAX_INPUT_BOND_TYPE; n1 ++ ) + { + chem_bonds_valence += (MIN_INPUT_BOND_TYPE + n1) * num_bond_type[n1]; + } + + if ( MIN_INPUT_BOND_TYPE <= INCHI_BOND_TYPE_ALTERN && INCHI_BOND_TYPE_ALTERN <= MAX_INPUT_BOND_TYPE && + ( n2 = num_bond_type[INCHI_BOND_TYPE_ALTERN-MIN_INPUT_BOND_TYPE] ) ) + { + + /* accept input aromatic bonds for now */ + + switch ( n2 ) + { + case 2: + chem_bonds_valence += 3; /* =A- */ + break; + + case 3: + chem_bonds_valence += 4; /* =A< */ + break; + + default: + /* if 1 or >= 4 aromatic bonds then replace such bonds with single bonds */ + for ( n1 = 0; n1 < AT_NUM_BONDS(atom[a1]); n1 ++ ) + { + if ( atom[a1].bond_type[n1] == INCHI_BOND_TYPE_ALTERN ) + { + ATOM_NUMBER *p1; + a2 = atom[a1].neighbor[n1]; + p1 = IN_NEIGH_LIST( atom[a2].neighbor, (ATOM_NUMBER)a1, AT_NUM_BONDS(atom[a2]) ); + if ( p1 ) + { + atom[a1].bond_type[n1] = + atom[a2].bond_type[p1-atom[a2].neighbor] = INCHI_BOND_TYPE_SINGLE; + } + else + { + *err = -2; /* Program error */ + TREAT_ERR (*err, 0, "Program error interpreting InChI aux"); + num_atoms = 0; + goto bypass_end_of_INChI_plain; /* no structure */ + } + } + } + + chem_bonds_valence += n2; + *err |= 32; /* Unrecognized aromatic bond(s) replaced with single */ + TREAT_ERR (*err, 0, "Atom has 1 or more than 3 aromatic bonds"); + break; + } + } + +#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) + /************************************************************************************* + * + * Set number of hydrogen atoms + */ + { + int num_iso_H; + num_iso_H = atom[a1].num_iso_H[1] + atom[a1].num_iso_H[2] + atom[a1].num_iso_H[3]; + if ( valence == ISOLATED_ATOM ) { + atom[a1].num_iso_H[0] = 0; + } else + if ( valence && valence >= chem_bonds_valence ) { + atom[a1].num_iso_H[0] = valence - chem_bonds_valence; + } else + if ( valence || bDoNotAddH ) { + atom[a1].num_iso_H[0] = 0; + } else + if ( !bDoNotAddH ) { + atom[a1].num_iso_H[0] = -1; /* auto add H */ + } + } +#else + /* added 2006-07-19 to process aromatic bonds same way as from molfile */ + if ( n2 && !valence ) + { + int num_H = NUMH(atom, a1); /* only isotopic */ + int chem_valence = chem_bonds_valence; + int bUnusualValenceArom = + detect_unusual_el_valence( (int)atom[a1].el_number, atom[a1].charge, + atom[a1].radical, chem_valence, + num_H, atom[a1].valence ); + int bUnusualValenceNoArom = + detect_unusual_el_valence( (int)atom[a1].el_number, atom[a1].charge, + atom[a1].radical, chem_valence-1, + num_H, atom[a1].valence ); + +#if ( CHECK_AROMBOND2ALT == 1 ) + if ( bUnusualValenceArom && !bUnusualValenceNoArom && 0 == nBondsValToMetal( atom, a1) ) +#else + if ( bUnusualValenceArom && !bUnusualValenceNoArom ) +#endif + + { + /* typically NH in 5-member aromatic ring */ + chem_bonds_valence --; + } + } + else if ( n2 && valence ) + { + /* atom has aromatic bonds AND the chemical valence is known */ + int num_H = NUMH(atom, a1); + int chem_valence = chem_bonds_valence + num_H; + if ( valence == chem_valence-1 ) { + /* typically NH in 5-member aromatic ring */ + chem_bonds_valence --; + } + } + + atom[a1].chem_bonds_valence = chem_bonds_valence; + + atom[a1].num_H = get_num_H( atom[a1].elname, atom[a1].num_H, atom[a1].num_iso_H, atom[a1].charge, atom[a1].radical, + atom[a1].chem_bonds_valence, + valence, + 0, bDoNotAddH, bHasMetalNeighbor ); +#endif + } + } + + nNumBonds /= 2; + + if ( b23D && nNumBonds ) + { + nXYZ = nX+nY+nZ; + b2D = (nXYZ > 0); + b3D = (nXYZ == 3); + *num_dimensions = b3D? 3 : b2D? 2 : 0; + *num_bonds = nNumBonds; + } + + /*======= 0D parities =================================*/ + +#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) + if ( len_stereo0D > 0 && atom_stereo0D && stereo0D ) { + *stereo0D = atom_stereo0D; + *num_stereo0D = len_stereo0D; + } else { + FreeInchi_Stereo0D( &atom_stereo0D ); + *num_stereo0D = len_stereo0D = 0; + } +#endif + + for ( i = 0; i < len_stereo0D; i ++ ) + { + ATOM_NUMBER *p1, *p2; + int sb_ord_from_a1 = -1, sb_ord_from_a2 = -1, bEnd1 = 0, bEnd2 = 0; + + switch( atom_stereo0D[i].type ) + { + + case INCHI_StereoType_Tetrahedral: + a1 = atom_stereo0D[i].central_atom; + if ( atom_stereo0D[i].parity && (AT_NUM_BONDS(atom[a1]) == 3 || AT_NUM_BONDS(atom[a1]) == 4) ) + { + int ii, kk = 0; + if ( AT_NUM_BONDS(atom[a1]) == 3 ) + atom_stereo0D[i].neighbor[kk++] = a1; + for ( ii = 0; ii < AT_NUM_BONDS(atom[a1]); ii ++ ) + atom_stereo0D[i].neighbor[kk++] = atom[a1].neighbor[ii]; + } + + break; + + case INCHI_StereoType_DoubleBond: +#define MAX_CHAIN_LEN 20 + a1 = atom_stereo0D[i].neighbor[1]; + a2 = atom_stereo0D[i].neighbor[2]; + p1 = IN_NEIGH_LIST( atom[a1].neighbor, (ATOM_NUMBER)a2, AT_NUM_BONDS(atom[a1]) ); + p2 = IN_NEIGH_LIST( atom[a2].neighbor, (ATOM_NUMBER)a1, AT_NUM_BONDS(atom[a2]) ); + if ( !p1 || !p2 ) + { + atom_stereo0D[i].type = INCHI_StereoType_None; + atom_stereo0D[i].central_atom = NO_ATOM; + atom_stereo0D[i].neighbor[0] = + atom_stereo0D[i].neighbor[3] = -1; + *err |= 64; /* Error in cumulene stereo */ + TREAT_ERR (*err, 0, "0D stereobond not recognized"); + break; + } + + /* streobond, allene, or cumulene */ + + sb_ord_from_a1 = p1 - atom[a1].neighbor; + sb_ord_from_a2 = p2 - atom[a2].neighbor; + + if ( AT_NUM_BONDS(atom[a1]) == 2 && + atom[a1].bond_type[0] + atom[a1].bond_type[1] == 2*INCHI_BOND_TYPE_DOUBLE && + 0 == inchi_NUMH2(atom, a1) && + (AT_NUM_BONDS(atom[a2]) != 2 || + atom[a2].bond_type[0] + atom[a2].bond_type[1] != 2*INCHI_BOND_TYPE_DOUBLE ) ) + { + bEnd2 = 1; /* a2 is the end-atom, a1 is middle atom */ + } + + if ( AT_NUM_BONDS(atom[a2]) == 2 && + atom[a2].bond_type[0] + atom[a2].bond_type[1] == 2*INCHI_BOND_TYPE_DOUBLE && + 0 == inchi_NUMH2(atom, a2) && + (AT_NUM_BONDS(atom[a1]) != 2 || + atom[a1].bond_type[0] + atom[a1].bond_type[1] != 2*INCHI_BOND_TYPE_DOUBLE ) ) + { + bEnd1 = 1; /* a1 is the end-atom, a2 is middle atom */ + } + + if ( bEnd2 + bEnd1 == 1 ) + { + /* allene or cumulene */ + + ATOM_NUMBER chain[MAX_CHAIN_LEN+1], prev, cur, next; + + if ( bEnd2 && !bEnd1 ) + { + cur = a1; + a1 = a2; + a2 = cur; + sb_ord_from_a1 = sb_ord_from_a2; + } + + sb_ord_from_a2 = -1; + cur = a1; + next = a2; + len = 0; + chain[len++] = cur; + chain[len++] = next; + + while ( len < MAX_CHAIN_LEN ) + { + /* arbitrary very high upper limit to prevent infinite loop */ + + prev = cur; + cur = next; + /* follow double bond path && avoid going back */ + if ( AT_NUM_BONDS(atom[cur]) == 2 && + atom[cur].bond_type[0]+atom[cur].bond_type[1] == 2*INCHI_BOND_TYPE_DOUBLE && + 0 == inchi_NUMH2(atom, cur) ) + { + next = atom[cur].neighbor[atom[cur].neighbor[0] == prev]; + chain[len++] = next; + } + else + { + break; + } + } + if ( len > 2 && + (p2 = IN_NEIGH_LIST( atom[cur].neighbor, (ATOM_NUMBER)prev, AT_NUM_BONDS(atom[cur]))) ) + { + sb_ord_from_a2 = p2 - atom[cur].neighbor; + a2 = cur; + /* by design we need to pick up the first non-stereo-bond-neighbor as "sn"-atom */ + atom_stereo0D[i].neighbor[0] = atom[a1].neighbor[sb_ord_from_a1 == 0]; + atom_stereo0D[i].neighbor[1] = a1; + atom_stereo0D[i].neighbor[2] = a2; + atom_stereo0D[i].neighbor[3] = atom[a2].neighbor[sb_ord_from_a2 == 0]; + + if ( len % 2 ) + { + atom_stereo0D[i].central_atom = chain[len/2]; + atom_stereo0D[i].type = INCHI_StereoType_Allene; + } + else + { + atom_stereo0D[i].central_atom = NO_ATOM; + } + } + else + { + /* error */ + atom_stereo0D[i].type = INCHI_StereoType_None; + atom_stereo0D[i].central_atom = NO_ATOM; + atom_stereo0D[i].neighbor[0] = + atom_stereo0D[i].neighbor[3] = -1; + *err |= 64; /* Error in cumulene stereo */ + TREAT_ERR (*err, 0, "Cumulene stereo not recognized (0D)"); + } +#undef MAX_CHAIN_LEN + } + else + { + /****** a normal possibly stereogenic bond -- not an allene or cumulene *******/ + /* by design we need to pick up the first non-stereo-bond-neighbor as "sn"-atom */ + sb_ord_from_a1 = p1 - atom[a1].neighbor; + sb_ord_from_a2 = p2 - atom[a2].neighbor; + atom_stereo0D[i].neighbor[0] = atom[a1].neighbor[p1 == atom[a1].neighbor]; + atom_stereo0D[i].neighbor[3] = atom[a2].neighbor[p2 == atom[a2].neighbor]; + atom_stereo0D[i].central_atom = NO_ATOM; + } + + if ( atom_stereo0D[i].type != INCHI_StereoType_None && + sb_ord_from_a1 >= 0 && sb_ord_from_a2 >= 0 && + ATOM_PARITY_WELL_DEF( SB_PARITY_2(atom_stereo0D[i].parity) ) ) + { + /* Detected well-defined disconnected stereo + * locate first non-metal neighbors */ + + int a, n, j, /* k,*/ sb_ord, cur_neigh, min_neigh; + + for ( k = 0; k < 2; k ++ ) + { + a = k? atom_stereo0D[i].neighbor[2] : atom_stereo0D[i].neighbor[1]; + sb_ord = k? sb_ord_from_a2 : sb_ord_from_a1; + min_neigh = num_atoms; + for ( n = j = 0; j < AT_NUM_BONDS(atom[a]); j ++ ) + { + cur_neigh = atom[a].neighbor[j]; + if ( j != sb_ord && !IS_METAL_ATOM(atom, cur_neigh) ) + { + min_neigh = inchi_min( cur_neigh, min_neigh ); + } + } + if ( min_neigh < num_atoms ) { + atom_stereo0D[i].neighbor[k?3:0] = min_neigh; + } else { + TREAT_ERR (*err, 0, "Cannot find non-metal stereobond neighor (0D)"); + } + } + } + + break; + } + } + /* end of 0D parities extraction */ +/*exit_cycle:;*/ + } + +#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) +#else + /* transfer atom_stereo0D[] to atom[] */ + if ( len_stereo0D ) + { + Extract0DParities( atom, num_atoms, atom_stereo0D, len_stereo0D, + pStrErr, err, vABParityUnknown ); + } +#endif + + if ( pInpAtomFlags ) + { + /* save chirality flag */ + *pInpAtomFlags |= InpAtomFlags; + } + } + else if ( atom ) + { + inchi_free( atom ); + atom = NULL; + } + +#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) +#else +#if ( FIX_READ_AUX_MEM_LEAK == 1 ) + + /* 2005-08-04 avoid memory leak */ + if ( atom_stereo0D && !(stereo0D && *stereo0D == atom_stereo0D) ) + { + FreeInchi_Stereo0D( &atom_stereo0D ); + } +#endif + + if ( szCoord ) + { + *szCoord = pszCoord; + pszCoord = NULL; + } + else +#endif + if ( pszCoord ) + { + inchi_free( pszCoord ); + pszCoord = NULL; + } + + goto bypass_end_of_INChI_plain; + /*return num_atoms;*/ + } + } + + if ( atom_stereo0D ) + { + FreeInchi_Stereo0D( &atom_stereo0D ); + } + + + /* end of structure reading cycle */ + + if ( res <= 0 ) + { + if ( *err == INCHI_INP_ERROR_ERR ) + return num_atoms; + + *err = INCHI_INP_EOF_ERR; + return INCHI_INP_EOF_RET; /* no more data */ + } + + +bypass_end_of_INChI_plain: + + /* cleanup */ + if ( num_atoms == INCHI_INP_ERROR_RET && atom_stereo0D ) + { + if ( stereo0D && *stereo0D == atom_stereo0D ) + { + *stereo0D = NULL; + *num_stereo0D = 0; + } + FreeInchi_Stereo0D( &atom_stereo0D ); + } + + while ( bTooLongLine && + 0 < inchi_ios_getsTab1( szLine, sizeof(szLine)-1, inp_file, &bTooLongLine ) ) + { + ; + } + + +#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) + /* cleanup */ + if ( !*at ) { + if ( atom ) { + inchi_free( atom ); + atom = NULL; + } + if ( pszCoord ) { + inchi_free( pszCoord ); + pszCoord = NULL; + } + } +#endif + + + return num_atoms; + } + + return num_atoms; + +#undef AT_NUM_BONDS +#undef ATOM_NUMBER +#undef IN_NEIGH_LIST +#undef inchi_NUMH2 + +#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) +#else +#undef inchi_Atom +#endif + +#undef AT_NUM_BONDS +#undef ATOM_NUMBER +#undef IN_NEIGH_LIST +#undef inchi_NUMH2 +#undef InchiToAtom +#undef MoreParms +#undef INPUT_FILE +#undef Create_Atom +#undef AT_BONDS_VAL +#undef ISOLATED_ATOM +#undef NUM_ISO_Hk +#undef IS_METAL_ATOM +} +#endif + + + +#endif /* _AUX2ATOM_H_ */ diff --git a/INCHI-1-SRC/INCHI/common/extr_ct.h b/INCHI-1-SRC/INCHI_BASE/src/extr_ct.h similarity index 91% rename from INCHI-1-SRC/INCHI/common/extr_ct.h rename to INCHI-1-SRC/INCHI_BASE/src/extr_ct.h index f728025..de93a7d 100644 --- a/INCHI-1-SRC/INCHI/common/extr_ct.h +++ b/INCHI-1-SRC/INCHI_BASE/src/extr_ct.h @@ -1,285 +1,305 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __EXTR_CT_H__ -#define __EXTR_CT_H__ - -#include "mode.h" -#include "ichisize.h" - -struct AtData { - char element[3]; - int maxvalence; -}; - - - -#define NUM_CHEM_ELEMENTS 127 /* well above number of known chem. elements */ - - -#define AT_ISO_SORT_KEY_MULT 32 /* up to 32 identical hydrogen isotopes */ - /* (similar to T_GROUP_ISOWT_MULT) */ - /* changed from 16 9-12-2003 */ -typedef long AT_ISO_SORT_KEY; /* signed, should hold up to 4096*max_iso_diff */ - /* (similar to T_GROUP_ISOWT) */ -/* - = num_1H + AT_ISO_SORT_KEY_MULT*(num_D + AT_ISO_SORT_KEY_MULT*(num_T+AT_ISO_SORT_KEY_MULT*iso_atw_diff)) -*/ - -/* typedef signed char AT_ISOTOPIC; */ /* + or - */ -typedef struct tagStereoCarb { - AT_NUMB at_num; - U_CHAR parity; -} AT_STEREO_CARB; -typedef struct tagStereoDble { - AT_NUMB at_num1; - AT_NUMB at_num2; - U_CHAR parity; -} AT_STEREO_DBLE; - -typedef struct tagIsotopicAtom { - AT_NUMB at_num; - NUM_H num_1H; - NUM_H num_D; - NUM_H num_T; - NUM_H iso_atw_diff; -} AT_ISOTOPIC; - -typedef AT_NUMB AT_STEREO; - -#define BYTE_BITS 8 /* number of bits in one byte */ - -#define BOND_MASK 0xf /* 4 bits */ -#define BOND_BITS 4 /* 3 or 4 does not matter; 2 is too small for BOND_TAUTOM */ -#define BOND_ADD (BOND_BITS==2?-1:0) /* subtract 1 from bonds stored in CT */ - - -typedef struct tagAtom { - char elname[ATOM_EL_LEN]; - AT_NUMB neighbor[MAXVAL]; /* changed to unsigned 2-2-95. D.Ch. */ - AT_NUMB init_rank; /* also used in remove_terminal_HDT() to save orig. at. number */ - AT_NUMB orig_at_number; - AT_NUMB orig_compt_at_numb; - /* low 3 bits=bond type; - high 5 bits (in case of cut-vertex atom) = an attached part number - */ - U_CHAR bond_type[MAXVAL]; - U_CHAR el_number; /* periodic table number = charge of the nucleus = number of the protons */ - /* U_CHAR hill_type; */ /* number in psudo hill order */ - S_CHAR valence; - S_CHAR chem_bonds_valence; /* 8-24-00 to treat tautomer centerpoints, etc. */ - S_CHAR num_H; /* first not including D, T; add_DT_to_num_H() includes. */ - S_CHAR num_iso_H[NUM_H_ISOTOPES]; /* num 1H, 2H(D), 3H(T) */ - S_CHAR cFlags; - S_CHAR iso_atw_diff; /* abs(iso_atw_diff) < 127 or 31 - ??? */ - AT_ISO_SORT_KEY iso_sort_key; /* = num_1H + AT_ISO_SORT_KEY_MULT^1*num_D - + AT_ISO_SORT_KEY_MULT^2*num_T - + AT_ISO_SORT_KEY_MULT^3*iso_atw_diff - */ - S_CHAR charge; - S_CHAR radical; /* 1=>doublet(.), 2=> triplet as singlet (:) ???? why are they same ???? */ - S_CHAR marked; - - AT_NUMB endpoint; /* tautomer analysis. If != 0 then the hydrogens & (-)charge are in the tautomer group. */ - - /* - Pairs stereo_bond_neighbor[] and stereo_bond_neighbor2[], etc - initially refer to non-isotopic and isotopic cases, respectively. - To use same stereo processing code these arrays are swapped when - switching from non-isotopic to isotopic processing and back. - */ - AT_NUMB stereo_bond_neighbor[MAX_NUM_STEREO_BONDS]; /* Original number of an opposite atom */ - AT_NUMB stereo_bond_neighbor2[MAX_NUM_STEREO_BONDS]; /* (stereo bond neighbor) +1; */ - S_CHAR stereo_bond_ord[MAX_NUM_STEREO_BONDS]; /* Ordering number of a bond/neighbor in the direction to the */ - S_CHAR stereo_bond_ord2[MAX_NUM_STEREO_BONDS]; /* stereo bond opposite atom (important for cumulenes); */ - S_CHAR stereo_bond_z_prod[MAX_NUM_STEREO_BONDS]; /* Relative atom-neighbors */ - S_CHAR stereo_bond_z_prod2[MAX_NUM_STEREO_BONDS]; /* double bond planes orientation; */ - S_CHAR stereo_bond_parity[MAX_NUM_STEREO_BONDS]; /* parity + MULT_STEREOBOND*chain_length, */ - S_CHAR stereo_bond_parity2[MAX_NUM_STEREO_BONDS]; /* where: */ - /* - parity (Mask 0x07=BITS_PARITY): - - 0 = AB_PARITY_NONE = not a stereo bond - 1/2 = AB_PARITY_ODD/EVEN = bond parity defined from initial ranks - 3 = AB_PARITY_UNKN = geometry is unknown to the user - 4 = AB_PARITY_UNDF = not enough geometry info to find the parity - 6 = AB_PARITY_CALC = calculate later from the neighbor ranks; some ot them can be - replaced with AB_PARITY_ODD/EVEN after equivalence ranks have been determined - - length (Mask 0x38=MASK_CUMULENE_LEN, length=stereo_bond_parity[i]/MULT_STEREOBOND): - - 0 => double or alternating stereogenic bond - 1 => cumulene with 2 double bonds (stereogenic center) - 2 => cumulene with 3 double bonds (stereogenic bond) - length <= (MAX_CUMULENE_LEN=2) - bit KNOWN_PARITIES_EQL = 0x40: all pairs of const. equ. atoms are connected by stereo bonds - and these bonds have identical parities - */ - - S_CHAR parity; /* -- Mask 0x07=BITS_PARITY: -- - 0 = AB_PARITY_NONE => no parity; also parity&0x38 = 0 - 1 = AB_PARITY_ODD => odd parity - 2 = AB_PARITY_EVEN => even parity - 3 = AB_PARITY_UNKN => user marked as unknown parity - 4 = AB_PARITY_UNDF => parity cannot be defined because of symmetry or not well defined geometry - */ - S_CHAR parity2; /* parity including parity due to isotopic terminal H */ - /* bit msks: 0x07 => known parity (1,2,3,4) or AB_PARITY_CALC=6, AB_PARITY_IISO = 6 */ - /* 0x40 => KNOWN_PARITIES_EQL */ - S_CHAR stereo_atom_parity; /* similar to stereo_bond_parity[]: known in advance AB_PARITY_* value + KNOWN_PARITIES_EQL bit */ - S_CHAR stereo_atom_parity2; - S_CHAR final_parity; /* defined by equivalence ranks */ - S_CHAR final_parity2; /* defined by equivalence ranks, incl. due to terminal isotopic H */ - S_CHAR bAmbiguousStereo; - S_CHAR bHasStereoOrEquToStereo; - S_CHAR bHasStereoOrEquToStereo2; -#if ( FIND_RING_SYSTEMS == 1 ) - S_CHAR bCutVertex; - AT_NUMB nRingSystem; - AT_NUMB nNumAtInRingSystem; - AT_NUMB nBlockSystem; -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) - AT_NUMB nDistanceFromTerminal; -#endif -#endif - S_CHAR z_dir[3]; - -} sp_ATOM ; - -#define BOND_SINGLE BOND_TYPE_SINGLE /* 1 */ -#define BOND_DOUBLE BOND_TYPE_DOUBLE /* 2 */ -#define BOND_TRIPLE BOND_TYPE_TRIPLE /* 3 */ -#define BOND_ALTERN BOND_TYPE_ALTERN /* 4 single/double */ - -#define BOND_ALT_123 5 /* single/double/triple */ -#define BOND_ALT_13 6 /* single/triple */ -#define BOND_ALT_23 7 /* double/triple */ -#define BOND_TAUTOM 8 -#define BOND_ALT12NS 9 -#define BOND_NUMDIF 9 /* number of different kinds of bonds */ - -#define BOND_TYPE_MASK 0x0f - -#define BOND_MARK_ALL 0xf0 /* complement to BOND_TYPE_MASK */ - -#define BOND_MARK_ALT12 0x10 -#define BOND_MARK_ALT123 0x20 -#define BOND_MARK_ALT13 0x30 -#define BOND_MARK_ALT23 0x40 -#define BOND_MARK_ALT12NS 0x50 /* 1 or 2, non-stereo */ -#define BOND_MARK_MASK 0x70 - -#define ACTUAL_ORDER(PBNS, IAT,IBOND, BTYPE) ( ((PBNS) && (PBNS)->edge && (PBNS)->vert &&\ - ((BTYPE)==BOND_ALT_123 || (BTYPE)==BOND_ALT_13 || (BTYPE)==BOND_ALT_23))? (PBNS)->edge[(PBNS)->vert[IAT].iedge[IBOND]].flow+BOND_TYPE_SINGLE:(BTYPE)) - - -#define BITS_PARITY 0x07 /* mask to retrieve half-bond parity */ -#define MASK_CUMULENE_LEN 0x38 /* mask to retrieve (cumulene chain length - 1)*MULT_STEREOBOND */ -#define KNOWN_PARITIES_EQL 0x40 /* parity is same for all pairs of constit. equivalent atoms */ -#define MAX_CUMULENE_LEN 2 /* max number of bonds in a cumulene chain - 1 */ - -#define MULT_STEREOBOND 0x08 /* multiplier for cumulene chain length - odd length => chiral, even length => stereogenic bond */ - -#define MAKE_BITS_CUMULENE_LEN(X) ((X)*MULT_STEREOBOND) -#define GET_BITS_CUMULENE_LEN(X) ((X)&MASK_CUMULENE_LEN) -#define BOND_CHAIN_LEN(X) (GET_BITS_CUMULENE_LEN(X)/MULT_STEREOBOND) /* 0 => double bond, 1 => allene, 2 => cumulene,..*/ -#define IS_ALLENE_CHAIN(X) ((GET_BITS_CUMULENE_LEN(X)/MULT_STEREOBOND)%2) - -/* atom or bond parity value definitions */ -#define AB_PARITY_NONE 0 /* 0 => no parity; also parity&0x38 = 0 */ -#define AB_PARITY_ODD 1 /* 1 => odd parity */ -#define AB_PARITY_EVEN 2 /* 2 => even parity */ -#define AB_PARITY_UNKN 3 /* 3 => user marked as unknown parity */ -#define AB_PARITY_UNDF 4 /* 4 => parity cannot be defined because of symmetry or not well defined geometry */ -#define AB_PARITY_IISO 5 /* 5 => no parity because of identical atoms */ -#define AB_PARITY_CALC 6 /* 6 => calculate parity later */ -#define AB_PARITY_0D 8 /* 8 => bit signifies 0D case -- not used */ - -#define AB_INV_PARITY_BITS (AB_PARITY_ODD ^ AB_PARITY_EVEN) - - -#define AB_MAX_KNOWN_PARITY 4 /* precalculated from const. equivalence parities */ -#define AB_MIN_KNOWN_PARITY 1 - -#define AB_MAX_PART_DEFINED_PARITY 3 /* 1, 2, 3 => defined parities, uncluding 'unknown' */ -#define AB_MIN_PART_DEFINED_PARITY 1 /* min(AB_PARITY_ODD, AB_PARITY_EVEN, AB_PARITY_UNKN) */ - -#define AB_MAX_WELL_DEFINED_PARITY 2 /* 1, 2 => well defined parities, uncluding 'unknown' */ -#define AB_MIN_WELL_DEFINED_PARITY 1 /* min(AB_PARITY_ODD, AB_PARITY_EVEN) */ - -#define AB_MIN_ILL_DEFINED_PARITY 3 -#define AB_MAX_ILL_DEFINED_PARITY 4 - -#define AB_MAX_ANY_PARITY 4 -#define AB_MIN_ANY_PARITY 1 - -#define AMBIGUOUS_STEREO 1 -#define AMBIGUOUS_STEREO_ATOM 2 -#define AMBIGUOUS_STEREO_BOND 4 -#define AMBIGUOUS_STEREO_ATOM_ISO 8 -#define AMBIGUOUS_STEREO_BOND_ISO 16 -#define AMBIGUOUS_STEREO_ERROR 32 - - -#define MIN_DOT_PROD 50 /* min value of at->stereo_bond_z_prod[i] to define parity */ - -#define ATOM_PARITY_VAL(X) (X) -#define ATOM_PARITY_PART_DEF(X) (AB_MIN_PART_DEFINED_PARITY <= (X) && (X) <= AB_MAX_PART_DEFINED_PARITY) -#define ATOM_PARITY_ILL_DEF(X) (AB_MIN_ILL_DEFINED_PARITY <= (X) && (X) <= AB_MAX_ILL_DEFINED_PARITY) -#define ATOM_PARITY_KNOWN(X) (AB_MIN_KNOWN_PARITY <= (X) && (X) <= AB_MAX_KNOWN_PARITY) -#define ATOM_PARITY_WELL_DEF(X) (AB_MIN_WELL_DEFINED_PARITY <= (X) && (X) <= AB_MAX_WELL_DEFINED_PARITY) -#define ATOM_PARITY_NOT_UNKN(X) (ATOM_PARITY_KNOWN(X) && (X) != AB_PARITY_UNKN) - -#define PARITY_VAL(X) ((X) & BITS_PARITY) -#define PARITY_PART_DEF(X) (AB_MIN_PART_DEFINED_PARITY <= PARITY_VAL(X) && PARITY_VAL(X) <= AB_MAX_PART_DEFINED_PARITY) -#define PARITY_ILL_DEF(X) (AB_MIN_ILL_DEFINED_PARITY <= PARITY_VAL(X) && PARITY_VAL(X) <= AB_MAX_ILL_DEFINED_PARITY) -#define PARITY_KNOWN(X) (AB_MIN_KNOWN_PARITY <= PARITY_VAL(X) && PARITY_VAL(X) <= AB_MAX_KNOWN_PARITY) -#define PARITY_WELL_DEF(X) (AB_MIN_WELL_DEFINED_PARITY <= PARITY_VAL(X) && PARITY_VAL(X) <= AB_MAX_WELL_DEFINED_PARITY) -#define PARITY_CALCULATE(X) (AB_PARITY_CALC == PARITY_VAL(X)) -#define BOND_PARITY_PART_DEFINED(X) (PARITY_PART_DEF(X) || PARITY_CALCULATE(X)) -#define BOND_PARITY_PART_KNOWN(X) (PARITY_KNOWN(X) || PARITY_CALCULATE(X)) -#define ALL_BUT_PARITY(X) ((X)&~BITS_PARITY) - -#define ALWAYS_SET_STEREO_PARITY 0 -#define NO_ISOLATED_NON_6RING_AROM_BOND 0 /* for Yuri */ -#define SAVE_6_AROM_CENTERS 0 /* for Yuri */ - -#endif /* __EXTR_CT_H__ */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _EXTR_CT_H_ +#define _EXTR_CT_H_ + + + +#include "mode.h" +#include "ichisize.h" +#include "util.h" + + + +struct AtData +{ + char element[3]; + int maxvalence; +}; + + + +#define NUM_CHEM_ELEMENTS 127 /* well above number of known chem. elements */ + + + +#define AT_ISO_SORT_KEY_MULT 32 /* up to 32 identical hydrogen isotopes */ + /* (similar to T_GROUP_ISOWT_MULT) */ + /* changed from 16 9-12-2003 */ + +typedef long AT_ISO_SORT_KEY; /* signed, should hold up to 4096*max_iso_diff */ + /* (similar to T_GROUP_ISOWT) */ +/* + = num_1H + AT_ISO_SORT_KEY_MULT*(num_D + AT_ISO_SORT_KEY_MULT*(num_T+AT_ISO_SORT_KEY_MULT*iso_atw_diff)) +*/ + +/* typedef signed char AT_ISOTOPIC; */ /* + or - */ +typedef struct tagStereoCarb +{ + AT_NUMB at_num; + U_CHAR parity; +} AT_STEREO_CARB; + +typedef struct tagStereoDble +{ + AT_NUMB at_num1; + AT_NUMB at_num2; + U_CHAR parity; +} AT_STEREO_DBLE; + +typedef struct tagIsotopicAtom +{ + AT_NUMB at_num; + NUM_H num_1H; + NUM_H num_D; + NUM_H num_T; + NUM_H iso_atw_diff; +} AT_ISOTOPIC; + +typedef AT_NUMB AT_STEREO; + + + +#define BYTE_BITS 8 /* number of bits in one byte */ + +#define BOND_MASK 0xf /* 4 bits */ +#define BOND_BITS 4 /* 3 or 4 does not matter; 2 is too small for BOND_TAUTOM */ +#define BOND_ADD (BOND_BITS==2?-1:0) /* subtract 1 from bonds stored in CT */ + + + +typedef struct tagAtom +{ + char elname[ATOM_EL_LEN]; + AT_NUMB neighbor[MAXVAL]; /* changed to unsigned 2-2-95. D.Ch. */ + AT_NUMB init_rank; /* also used in remove_terminal_HDT() to save orig. at. number */ + AT_NUMB orig_at_number; + AT_NUMB orig_compt_at_numb; + /* low 3 bits=bond type; + high 5 bits (in case of cut-vertex atom) = an attached part number + */ + U_CHAR bond_type[MAXVAL]; + U_CHAR el_number; /* periodic table number = charge of the nucleus = number of the protons */ + /* U_CHAR hill_type; */ /* number in psudo hill order */ + S_CHAR valence; + S_CHAR chem_bonds_valence; /* 8-24-00 to treat tautomer centerpoints, etc. */ + S_CHAR num_H; /* first not including D, T; add_DT_to_num_H() includes. */ + S_CHAR num_iso_H[NUM_H_ISOTOPES]; /* num 1H, 2H(D), 3H(T) */ + S_CHAR cFlags; + S_CHAR iso_atw_diff; /* abs(iso_atw_diff) < 127 or 31 - ??? */ + AT_ISO_SORT_KEY iso_sort_key; /* = num_1H + AT_ISO_SORT_KEY_MULT^1*num_D + + AT_ISO_SORT_KEY_MULT^2*num_T + + AT_ISO_SORT_KEY_MULT^3*iso_atw_diff + */ + S_CHAR charge; + S_CHAR radical; /* 1=>doublet(.), 2=> triplet as singlet (:) ???? why are they same ???? */ + S_CHAR marked; + + AT_NUMB endpoint; /* tautomer analysis. If != 0 then the hydrogens & (-)charge are in the tautomer group. */ + + /* + Pairs stereo_bond_neighbor[] and stereo_bond_neighbor2[], etc + initially refer to non-isotopic and isotopic cases, respectively. + To use same stereo processing code these arrays are swapped when + switching from non-isotopic to isotopic processing and back. + */ + AT_NUMB stereo_bond_neighbor[MAX_NUM_STEREO_BONDS]; /* Original number of an opposite atom */ + AT_NUMB stereo_bond_neighbor2[MAX_NUM_STEREO_BONDS]; /* (stereo bond neighbor) +1; */ + S_CHAR stereo_bond_ord[MAX_NUM_STEREO_BONDS]; /* Ordering number of a bond/neighbor in the direction to the */ + S_CHAR stereo_bond_ord2[MAX_NUM_STEREO_BONDS]; /* stereo bond opposite atom (important for cumulenes); */ + S_CHAR stereo_bond_z_prod[MAX_NUM_STEREO_BONDS]; /* Relative atom-neighbors */ + S_CHAR stereo_bond_z_prod2[MAX_NUM_STEREO_BONDS]; /* double bond planes orientation; */ + S_CHAR stereo_bond_parity[MAX_NUM_STEREO_BONDS]; /* parity + MULT_STEREOBOND*chain_length, */ + S_CHAR stereo_bond_parity2[MAX_NUM_STEREO_BONDS]; /* where: */ + /* + parity (Mask 0x07=BITS_PARITY): + + 0 = AB_PARITY_NONE = not a stereo bond + 1/2 = AB_PARITY_ODD/EVEN = bond parity defined from initial ranks + 3 = AB_PARITY_UNKN = geometry is unknown to the user + 4 = AB_PARITY_UNDF = not enough geometry info to find the parity + 6 = AB_PARITY_CALC = calculate later from the neighbor ranks; some ot them can be + replaced with AB_PARITY_ODD/EVEN after equivalence ranks have been determined + + length (Mask 0x38=MASK_CUMULENE_LEN, length=stereo_bond_parity[i]/MULT_STEREOBOND): + + 0 => double or alternating stereogenic bond + 1 => cumulene with 2 double bonds (stereogenic center) + 2 => cumulene with 3 double bonds (stereogenic bond) + length <= (MAX_CUMULENE_LEN=2) + bit KNOWN_PARITIES_EQL = 0x40: all pairs of const. equ. atoms are connected by stereo bonds + and these bonds have identical parities + */ + + S_CHAR parity; /* -- Mask 0x07=BITS_PARITY: -- + 0 = AB_PARITY_NONE => no parity; also parity&0x38 = 0 + 1 = AB_PARITY_ODD => odd parity + 2 = AB_PARITY_EVEN => even parity + 3 = AB_PARITY_UNKN => user marked as unknown parity + 4 = AB_PARITY_UNDF => parity cannot be defined because of symmetry or not well defined geometry + */ + S_CHAR parity2; /* parity including parity due to isotopic terminal H */ + /* bit msks: 0x07 => known parity (1,2,3,4) or AB_PARITY_CALC=6, AB_PARITY_IISO = 6 */ + /* 0x40 => KNOWN_PARITIES_EQL */ + S_CHAR stereo_atom_parity; /* similar to stereo_bond_parity[]: known in advance AB_PARITY_* value + KNOWN_PARITIES_EQL bit */ + S_CHAR stereo_atom_parity2; + S_CHAR final_parity; /* defined by equivalence ranks */ + S_CHAR final_parity2; /* defined by equivalence ranks, incl. due to terminal isotopic H */ + S_CHAR bAmbiguousStereo; + S_CHAR bHasStereoOrEquToStereo; + S_CHAR bHasStereoOrEquToStereo2; + +#if ( FIND_RING_SYSTEMS == 1 ) + S_CHAR bCutVertex; + AT_NUMB nRingSystem; + AT_NUMB nNumAtInRingSystem; + AT_NUMB nBlockSystem; + +#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) + AT_NUMB nDistanceFromTerminal; +#endif + +#endif + + S_CHAR z_dir[3]; +} sp_ATOM ; + +#define BOND_SINGLE BOND_TYPE_SINGLE /* 1 */ +#define BOND_DOUBLE BOND_TYPE_DOUBLE /* 2 */ +#define BOND_TRIPLE BOND_TYPE_TRIPLE /* 3 */ +#define BOND_ALTERN BOND_TYPE_ALTERN /* 4 single/double */ + +#define BOND_ALT_123 5 /* single/double/triple */ +#define BOND_ALT_13 6 /* single/triple */ +#define BOND_ALT_23 7 /* double/triple */ +#define BOND_TAUTOM 8 +#define BOND_ALT12NS 9 +#define BOND_NUMDIF 9 /* number of different kinds of bonds */ + +#define BOND_TYPE_MASK 0x0f + +#define BOND_MARK_ALL 0xf0 /* complement to BOND_TYPE_MASK */ + +#define BOND_MARK_ALT12 0x10 +#define BOND_MARK_ALT123 0x20 +#define BOND_MARK_ALT13 0x30 +#define BOND_MARK_ALT23 0x40 +#define BOND_MARK_ALT12NS 0x50 /* 1 or 2, non-stereo */ +#define BOND_MARK_MASK 0x70 + +#define ACTUAL_ORDER(PBNS, IAT,IBOND, BTYPE) ( ((PBNS) && (PBNS)->edge && (PBNS)->vert &&\ + ((BTYPE)==BOND_ALT_123 || (BTYPE)==BOND_ALT_13 || (BTYPE)==BOND_ALT_23))? (PBNS)->edge[(PBNS)->vert[IAT].iedge[IBOND]].flow+BOND_TYPE_SINGLE:(BTYPE)) + + +#define BITS_PARITY 0x07 /* mask to retrieve half-bond parity */ +#define MASK_CUMULENE_LEN 0x38 /* mask to retrieve (cumulene chain length - 1)*MULT_STEREOBOND */ +#define KNOWN_PARITIES_EQL 0x40 /* parity is same for all pairs of constit. equivalent atoms */ +#define MAX_CUMULENE_LEN 2 /* max number of bonds in a cumulene chain - 1 */ + +#define MULT_STEREOBOND 0x08 /* multiplier for cumulene chain length + odd length => chiral, even length => stereogenic bond */ + +#define MAKE_BITS_CUMULENE_LEN(X) ((X)*MULT_STEREOBOND) +#define GET_BITS_CUMULENE_LEN(X) ((X)&MASK_CUMULENE_LEN) +#define BOND_CHAIN_LEN(X) (GET_BITS_CUMULENE_LEN(X)/MULT_STEREOBOND) /* 0 => double bond, 1 => allene, 2 => cumulene,..*/ +#define IS_ALLENE_CHAIN(X) ((GET_BITS_CUMULENE_LEN(X)/MULT_STEREOBOND)%2) + +/* atom or bond parity value definitions */ +#define AB_PARITY_NONE 0 /* 0 => no parity; also parity&0x38 = 0 */ +#define AB_PARITY_ODD 1 /* 1 => odd parity */ +#define AB_PARITY_EVEN 2 /* 2 => even parity */ +#define AB_PARITY_UNKN 3 /* 3 => user marked as unknown parity */ +#define AB_PARITY_UNDF 4 /* 4 => parity cannot be defined because of symmetry or not well defined geometry */ +#define AB_PARITY_IISO 5 /* 5 => no parity because of identical atoms */ +#define AB_PARITY_CALC 6 /* 6 => calculate parity later */ +#define AB_PARITY_0D 8 /* 8 => bit signifies 0D case -- not used */ + +#define AB_INV_PARITY_BITS (AB_PARITY_ODD ^ AB_PARITY_EVEN) + + +#define AB_MAX_KNOWN_PARITY 4 /* precalculated from const. equivalence parities */ +#define AB_MIN_KNOWN_PARITY 1 + +#define AB_MAX_PART_DEFINED_PARITY 3 /* 1, 2, 3 => defined parities, uncluding 'unknown' */ +#define AB_MIN_PART_DEFINED_PARITY 1 /* min(AB_PARITY_ODD, AB_PARITY_EVEN, AB_PARITY_UNKN) */ + +#define AB_MAX_WELL_DEFINED_PARITY 2 /* 1, 2 => well defined parities, uncluding 'unknown' */ +#define AB_MIN_WELL_DEFINED_PARITY 1 /* min(AB_PARITY_ODD, AB_PARITY_EVEN) */ + +#define AB_MIN_ILL_DEFINED_PARITY 3 +#define AB_MAX_ILL_DEFINED_PARITY 4 + +#define AB_MAX_ANY_PARITY 4 +#define AB_MIN_ANY_PARITY 1 + +#define AMBIGUOUS_STEREO 1 +#define AMBIGUOUS_STEREO_ATOM 2 +#define AMBIGUOUS_STEREO_BOND 4 +#define AMBIGUOUS_STEREO_ATOM_ISO 8 +#define AMBIGUOUS_STEREO_BOND_ISO 16 +#define AMBIGUOUS_STEREO_ERROR 32 + + +#define MIN_DOT_PROD 50 /* min value of at->stereo_bond_z_prod[i] to define parity */ + +#define ATOM_PARITY_VAL(X) (X) +#define ATOM_PARITY_PART_DEF(X) (AB_MIN_PART_DEFINED_PARITY <= (X) && (X) <= AB_MAX_PART_DEFINED_PARITY) +#define ATOM_PARITY_ILL_DEF(X) (AB_MIN_ILL_DEFINED_PARITY <= (X) && (X) <= AB_MAX_ILL_DEFINED_PARITY) +#define ATOM_PARITY_KNOWN(X) (AB_MIN_KNOWN_PARITY <= (X) && (X) <= AB_MAX_KNOWN_PARITY) +#define ATOM_PARITY_WELL_DEF(X) (AB_MIN_WELL_DEFINED_PARITY <= (X) && (X) <= AB_MAX_WELL_DEFINED_PARITY) +#define ATOM_PARITY_NOT_UNKN(X) (ATOM_PARITY_KNOWN(X) && (X) != AB_PARITY_UNKN) + +#define PARITY_VAL(X) ((X) & BITS_PARITY) +#define PARITY_PART_DEF(X) (AB_MIN_PART_DEFINED_PARITY <= PARITY_VAL(X) && PARITY_VAL(X) <= AB_MAX_PART_DEFINED_PARITY) +#define PARITY_ILL_DEF(X) (AB_MIN_ILL_DEFINED_PARITY <= PARITY_VAL(X) && PARITY_VAL(X) <= AB_MAX_ILL_DEFINED_PARITY) +#define PARITY_KNOWN(X) (AB_MIN_KNOWN_PARITY <= PARITY_VAL(X) && PARITY_VAL(X) <= AB_MAX_KNOWN_PARITY) +#define PARITY_WELL_DEF(X) (AB_MIN_WELL_DEFINED_PARITY <= PARITY_VAL(X) && PARITY_VAL(X) <= AB_MAX_WELL_DEFINED_PARITY) +#define PARITY_CALCULATE(X) (AB_PARITY_CALC == PARITY_VAL(X)) +#define BOND_PARITY_PART_DEFINED(X) (PARITY_PART_DEF(X) || PARITY_CALCULATE(X)) +#define BOND_PARITY_PART_KNOWN(X) (PARITY_KNOWN(X) || PARITY_CALCULATE(X)) +#define ALL_BUT_PARITY(X) ((X)&~BITS_PARITY) + +#define ALWAYS_SET_STEREO_PARITY 0 +#define NO_ISOLATED_NON_6RING_AROM_BOND 0 /* for Yuri */ +#define SAVE_6_AROM_CENTERS 0 /* for Yuri */ + + + +#endif /* _EXTR_CT_H_ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichi.h b/INCHI-1-SRC/INCHI_BASE/src/ichi.h similarity index 87% rename from INCHI-1-SRC/INCHI_API/inchi_dll/ichi.h rename to INCHI-1-SRC/INCHI_BASE/src/ichi.h index 2191aac..c189a58 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichi.h +++ b/INCHI-1-SRC/INCHI_BASE/src/ichi.h @@ -1,272 +1,308 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHI_H__ -#define __INCHI_H__ - -#include "incomdef.h" - -#define REQ_MODE_BASIC 0x000001 /* B include Fixed-H layer */ -#define REQ_MODE_TAUT 0x000002 /* T include Mobile-H layer */ -#define REQ_MODE_ISO 0x000004 /* I */ -#define REQ_MODE_NON_ISO 0x000008 /* NI */ -#define REQ_MODE_STEREO 0x000010 /* S */ -#define REQ_MODE_ISO_STEREO 0x000020 /* IS */ -#define REQ_MODE_NOEQ_STEREO 0x000040 /* SS */ -#define REQ_MODE_REDNDNT_STEREO 0x000080 /* RS */ -#define REQ_MODE_NO_ALT_SBONDS 0x000100 /* NASB */ -/* new 10-10-2003 */ -#define REQ_MODE_RELATIVE_STEREO 0x000200 /* REL All Relative Stereo */ -#define REQ_MODE_RACEMIC_STEREO 0x000400 /* RAC All Racemic Stereo */ -#define REQ_MODE_SC_IGN_ALL_UU 0x000800 /* IAUSC Ignore stereocenters if All Undef/Unknown */ -#define REQ_MODE_SB_IGN_ALL_UU 0x001000 /* IAUSC Ignore stereobonds if All Undef/Unknown */ -#define REQ_MODE_CHIR_FLG_STEREO 0x002000 /* SUCF If Chiral flag then Abs otherwise Rel stereo */ -/* end of 10-10-2003 */ - -/*^^^ 2009-12-05 */ -#define REQ_MODE_DIFF_UU_STEREO 0x004000 /* SLUUD Make labels for unknown and undefined stereo different */ -/*^^^ 2009-12-05 */ -#define REQ_MODE_MIN_SB_RING_MASK 0x0F0000 /* RSB */ -#define REQ_MODE_MIN_SB_RING_SHFT 16 - -#define REQ_MODE_DEFAULT (REQ_MODE_BASIC | REQ_MODE_TAUT | REQ_MODE_ISO | REQ_MODE_NON_ISO | REQ_MODE_STEREO) - -#define WARN_FAILED_STEREO 0x0001 -#define WARN_FAILED_ISOTOPIC 0x0002 -#define WARN_FAILED_ISOTOPIC_STEREO 0x0004 -#define ERR_NO_CANON_RESULTS 0x0008 - -/*********** compare components flags **********************************/ -#define CMP_COMPONENTS 0x0001 /* perform compare components */ -#define CMP_COMPONENTS_NONISO 0x0002 /* ignore isotopic */ -#define CMP_COMPONENTS_NONTAUT 0x0004 /* compare non-tautomeric */ -/****************** chemical identifier member definitions *************/ -typedef struct tagINChI_IsotopicAtom { - AT_NUMB nAtomNumber; /* Canonical atom number */ - NUM_H nIsoDifference; /* 0=non-isotopic; 1=rounded avg. atomic mass */ - NUM_H nNum_H; /* number of 1H isotopic atoms attached */ - NUM_H nNum_D; /* number of 2H isotopic atoms attached */ - NUM_H nNum_T; /* number of 3H isotopic atoms attached */ -} INChI_IsotopicAtom; -typedef struct tagINChI_IsotopicTGroup { - AT_NUMB nTGroupNumber; /* Tautomeric group number */ - AT_NUMB nNum_H; /* number of 1H isotopic atoms */ - AT_NUMB nNum_D; /* number of 2H isotopic atoms */ - AT_NUMB nNum_T; /* number of 3H isotopic atoms */ -} INChI_IsotopicTGroup; -typedef struct tagINChI_Stereo { /* [N] = allocated length */ - /* ---- possibly tetrahedral stereogenic atoms */ - int nNumberOfStereoCenters; - AT_NUMB *nNumber; /* Canonical number of a possibly tetrahedral - * stereogenic atom or allenes [nNumberOfAtoms] */ - S_CHAR *t_parity; /* tetrahedral (relative, see nCompInv2Abs) atom parities [nNumberOfAtoms] */ - /* ---- possibly tetrahedral stereogenic atoms of the iverted structure */ - AT_NUMB *nNumberInv; /* Canonical number of a possibly tetrahedral - * stereogenic atom or allene [nNumberOfAtoms] */ - S_CHAR *t_parityInv; /* tetrahedral inverted atom parities [nNumberOfAtoms] */ - /* bFlagAbsStereoIsInverted = nCompInv2Abs==-1: Abs stereo = Inverted */ - int nCompInv2Abs; /* 0=>Inv = Abs stereo; -1=> Inv < Abs stereo, +1=> Inv > Abs stereo; +2=> in reading InChI: no /m was found and in /sN N>0 */ - int bTrivialInv; /* 1=> nCompInv2Abs!= 0 && Inverted = Abs stereo with inverted parities 1<-->2 */ - /* ---- possibly stereogenic bonds and tetrahedral cumuleles */ - int nNumberOfStereoBonds; - AT_NUMB *nBondAtom1; /* Canonical number of a first atom - * [number of bonds] */ - AT_NUMB *nBondAtom2; /* Canonical number of a second atom - * [number of bonds] */ - S_CHAR *b_parity; /* possibly stereogenic bond parities - * [number of bonds] */ -} INChI_Stereo; -#define INCHI_FLAG_ACID_TAUT 0x0001 /* tautomerism of dissociated acid invoked */ -#define INCHI_FLAG_REL_STEREO 0x0002 /* requested relative stereo */ -#define INCHI_FLAG_RAC_STEREO 0x0004 /* requested racemic stereo */ -#define INCHI_FLAG_SC_IGN_ALL_UU 0x0008 /* ignored all undefined/unknown stereocenters, non-isotopic */ -#define INCHI_FLAG_SB_IGN_ALL_UU 0x0010 /* ignored all undefined/unknown stereocenters, non-isotopic */ -#define INCHI_FLAG_SC_IGN_ALL_ISO_UU 0x0020 /* ignored all undefined/unknown stereocenters, isotopic */ -#define INCHI_FLAG_SB_IGN_ALL_ISO_UU 0x0040 /* ignored all undefined/unknown stereocenters, isotopic */ -#define INCHI_FLAG_HARD_ADD_REM_PROTON 0x0080 /* in normalization a proton has been added or removed along alt path */ - -#define INCHI_OUT_NO_AUX_INFO 0x0001 /* do not output Aux Info */ -#define INCHI_OUT_SHORT_AUX_INFO 0x0002 /* output short version of Aux Info */ -#define INCHI_OUT_ONLY_AUX_INFO 0x0004 /* output only Aux Info */ -#define INCHI_OUT_EMBED_REC 0x0008 /* embed reconnected INChI into disconnected INChI */ -#define INCHI_OUT_SDFILE_ONLY 0x0010 /* save input data in a Molfile instead of creating INChI */ -#define INCHI_OUT_XML 0x0020 /* output xml INChI */ -#define INCHI_OUT_PLAIN_TEXT 0x0040 /* output plain text INChI */ -#define INCHI_OUT_PLAIN_TEXT_COMMENTS 0x0080 /* output plain text annotation */ -#define INCHI_OUT_XML_TEXT_COMMENTS 0x0100 /* output xml text annotation */ -#define INCHI_OUT_WINCHI_WINDOW 0x0200 /* output into wINChI text window */ -#define INCHI_OUT_TABBED_OUTPUT 0x0400 /* tab-delimited (only for plain text) */ -#define INCHI_OUT_SDFILE_ATOMS_DT 0x0800 /* SDfile output H isotopes as D and T */ -#define INCHI_OUT_SDFILE_SPLIT 0x1000 /* Split SDfile into components */ - -#define INCHI_OUT_FIX_TRANSPOSITION_CHARGE_BUG 0x2000 - /* used to accomodate FIX_TRANSPOSITION_CHARGE_BUG */ - -#define INCHI_OUT_STDINCHI 0x4000 -#define INCHI_OUT_SAVEOPT 0x8000 - - -/* Bits encoding InChI creation options to be saved */ -#define SAVE_OPT_SLUUD 0x0001 -#define SAVE_OPT_SUU 0x0002 -#define SAVE_OPT_FIXEDH 0x0004 -#define SAVE_OPT_RECMET 0x0008 -#define SAVE_OPT_KET 0x0010 -#define SAVE_OPT_15T 0x0020 - - -#define INCHI_OUT_PRINT_OPTIONS (INCHI_OUT_EMBED_REC | \ - INCHI_OUT_XML | \ - INCHI_OUT_PLAIN_TEXT | \ - INCHI_OUT_PLAIN_TEXT_COMMENTS | \ - INCHI_OUT_XML_TEXT_COMMENTS) - - -/*******REQ_MODE_SB_IGN_ALL_UU*************** chemical identifier definition *****************/ -typedef struct tagINChI { /* [N] = allocated length */ - - int nErrorCode; /* 0 = success */ - INCHI_MODE nFlags; /* INCHI_FLAG_ACID_TAUT tautomerism of dissociated acid invoked - INCHI_FLAG_REL_STEREO requested relative stereo - INCHI_FLAG_RAC_STEREO requested racemic stereo - INCHI_FLAG_SC_IGN_ALL_UU ignored all undefined/unknown stereocenters, non-isotopic - INCHI_FLAG_SB_IGN_ALL_UU ignored all undefined/unknown stereocenters, non-isotopic - INCHI_FLAG_SC_IGN_ALL_ISO_UU ignored all undefined/unknown stereocenters, isotopic - INCHI_FLAG_SB_IGN_ALL_ISO_UU ignored all undefined/unknown stereocenters, isotopic - INCHI_FLAG_HARD_ADD_REM_PROTON in normalization a proton has been added or removed along alt path - */ - /* ---- basic & tautomer layer */ - int nTotalCharge; - int nNumberOfAtoms; - char *szHillFormula; - U_CHAR *nAtom; /* atomic numbers [nNumberOfAtoms] from the Periodic Table */ - int lenConnTable; - AT_NUMB *nConnTable; /* Connection table [nNumberOfAtoms+NumberOfBonds] */ - int lenTautomer; - AT_NUMB *nTautomer; /* NumGroups; ((NumAt+1, NumH, At1..AtNumAt),...); {INCHI_T_NUM_MOVABLE = 1} - old - * NumGroups; ((NumAt+2, NumH, Num(-), At1..AtNumAt),...); {INCHI_T_NUM_MOVABLE = 2} - new - * Allocated length: [5*nNumberOfAtoms/2+1], see Alloc_INChI(...) */ - S_CHAR *nNum_H; /* number of terminal hydrogen atoms on each atom; in tautomeric - * representation these H on tautomeric atoms are not included [nNumberOfAtoms] */ - S_CHAR *nNum_H_fixed;/* number of terminal hydrogen atoms on tautomeric atoms, - * in non-atautomeric representation only [nNumberOfAtoms] */ - /* ---- isotopic & isotopic tautomeric layer */ - int nNumberOfIsotopicAtoms; - INChI_IsotopicAtom *IsotopicAtom; /* [nNumberOfIsotopicAtoms] */ - int nNumberOfIsotopicTGroups; - /* in reversing InChI keeps a pointer to stolen from AuxInfo coordinates */ - INChI_IsotopicTGroup *IsotopicTGroup; /* [nNumberOfIsotopicAtoms] */ - /* ---- stereo layer */ - INChI_Stereo *Stereo; - INChI_Stereo *StereoIsotopic; - /* not including mobile H groups */ - AT_NUMB *nPossibleLocationsOfIsotopicH; /* [0]=> length including 0th element, location1,...*/ - int bDeleted; -#if ( bREUSE_INCHI == 1 ) - int nRefCount; -#endif -#if ( bRELEASE_VERSION == 0 ) - int bExtract; -#endif -#if ( READ_INCHI_STRING == 1 ) - int nLink; /* negative: ignore InChI; positive: index of (Reconnected component) + 1 linked to it */ -#endif -} INChI; - -typedef INChI *PINChI2[TAUT_NUM]; - -typedef struct tagOrigInfo { - S_CHAR cCharge; - S_CHAR cRadical; /* 0=none, 1=doublet, 2=triplet, 3=unknown */ - S_CHAR cUnusualValence; /* see get_unusual_el_valence() */ -} ORIG_INFO; -/******************** auxiliary chemical identifier info **************/ -typedef struct tagINChI_Aux { /* [N] = allocated length */ - - int nErrorCode; /* 0 = success */ - int nNumberOfAtoms; - int nNumberOfTGroups; /* non-zero only in tautomeric representation */ - int bIsIsotopic; /* filled out even though isotopic has not been requested */ - int bIsTautomeric; /* filled out even though tautomeric has not been requested; non-zero if taut exists */ - /* canonical numbers of the atoms: nOrigAtNosInCanonOrd[i-1]+1 = */ - /* input atom number for the canonical number i */ - AT_NUMB *nOrigAtNosInCanonOrd; /* [nNumberOfInputAtoms*1.5]; */ - AT_NUMB *nIsotopicOrigAtNosInCanonOrd; /* [nNumberOfInputAtoms*1.5]; */ - /* same for the inverted structure */ - AT_NUMB *nOrigAtNosInCanonOrdInv; /* inveterted stereo [nNumberOfInputAtoms*1.5]; */ - AT_NUMB *nIsotopicOrigAtNosInCanonOrdInv; /* [nNumberOfInputAtoms*1.5]; */ - AT_NUMB *nConstitEquNumbers; /* [nNumberOfAtoms*1.5] */ - AT_NUMB *nConstitEquTGroupNumbers; /* [nNumberOfAtoms/2] */ - AT_NUMB *nConstitEquIsotopicNumbers; /* [nNumberOfAtoms*1.5] */ - AT_NUMB *nConstitEquIsotopicTGroupNumbers; /* [nNumberOfAtoms/2] */ -#if ( bREUSE_INCHI == 1 ) - int nRefCount; -#endif -#if ( TEST_RENUMB_ATOMS == 1 ) - unsigned long ulNormTime; - unsigned long ulCanonTime; -#endif - - ORIG_INFO *OrigInfo; - MOL_COORD *szOrigCoord; - NUM_H nNumRemovedProtons; - NUM_H nNumRemovedIsotopicH[NUM_H_ISOTOPES]; /* isotopic H that may be exchanged and considered - randomly distributed, including removed protons; - order: 0=>1H, 1=>D, 2=>T */ - int bDeleted; - INCHI_MODE bTautFlags; /* t_group_info->bTautFlags */ - INCHI_MODE bTautFlagsDone; /* t_group_info->bTautFlagsDone */ - INCHI_MODE bNormalizationFlags; /* t_group_info->tni.bNormalizationFlags */ - int nCanonFlags; -} INChI_Aux; - -typedef INChI_Aux *PINChI_Aux2[TAUT_NUM]; - -/********************* array of pointers for sorting components and INChI output *********/ -typedef struct tagINChIforSort { - INChI *pINChI[TAUT_NUM]; - INChI_Aux *pINChI_Aux[TAUT_NUM]; - short ord_number; /* for stable sort */ - short n1; /* points to the original; used in structure reconstruction only */ - short n2; /* points to the original; used in structure reconstruction only */ - short n3; /* points to the original; used in structure reconstruction only */ -}INCHI_SORT; - -#endif /* __INCHI_H__ */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _ICHI_H_ +#define _ICHI_H_ + + +#include "incomdef.h" +#include "mode.h" + + +#define REQ_MODE_BASIC 0x000001 /* B include Fixed-H layer */ +#define REQ_MODE_TAUT 0x000002 /* T include Mobile-H layer */ +#define REQ_MODE_ISO 0x000004 /* I */ +#define REQ_MODE_NON_ISO 0x000008 /* NI */ +#define REQ_MODE_STEREO 0x000010 /* S */ +#define REQ_MODE_ISO_STEREO 0x000020 /* IS */ +#define REQ_MODE_NOEQ_STEREO 0x000040 /* SS */ +#define REQ_MODE_REDNDNT_STEREO 0x000080 /* RS */ +#define REQ_MODE_NO_ALT_SBONDS 0x000100 /* NASB */ + +/* new 10-10-2003 */ +#define REQ_MODE_RELATIVE_STEREO 0x000200 /* REL All Relative Stereo */ + +#define REQ_MODE_RACEMIC_STEREO 0x000400 /* RAC All Racemic Stereo */ + +#define REQ_MODE_SC_IGN_ALL_UU 0x000800 /* IAUSC Ignore stereocenters if All Undef/Unknown */ + +#define REQ_MODE_SB_IGN_ALL_UU 0x001000 /* IAUSC Ignore stereobonds if All Undef/Unknown */ + +#define REQ_MODE_CHIR_FLG_STEREO 0x002000 /* SUCF If Chiral flag then Abs otherwise Rel stereo */ +/* end of 10-10-2003 */ + + + +/* 2009-12-05 */ +#define REQ_MODE_DIFF_UU_STEREO 0x004000 /* SLUUD Make labels for unknown and undefined stereo different */ +/* 2009-12-05 */ + +#define REQ_MODE_MIN_SB_RING_MASK 0x0F0000 /* RSB */ +#define REQ_MODE_MIN_SB_RING_SHFT 16 + +#define REQ_MODE_DEFAULT (REQ_MODE_BASIC | REQ_MODE_TAUT | REQ_MODE_ISO | REQ_MODE_NON_ISO | REQ_MODE_STEREO) + +#define WARN_FAILED_STEREO 0x0001 +#define WARN_FAILED_ISOTOPIC 0x0002 +#define WARN_FAILED_ISOTOPIC_STEREO 0x0004 +#define ERR_NO_CANON_RESULTS 0x0008 + +/*********** compare components flags **********************************/ +#define CMP_COMPONENTS 0x0001 /* perform compare components */ +#define CMP_COMPONENTS_NONISO 0x0002 /* ignore isotopic */ +#define CMP_COMPONENTS_NONTAUT 0x0004 /* compare non-tautomeric */ + +/****************** chemical identifier member definitions *************/ +typedef struct tagINChI_IsotopicAtom +{ + + AT_NUMB nAtomNumber; /* Canonical atom number */ + + NUM_H nIsoDifference; /* 0=non-isotopic; 1=rounded avg. atomic mass */ + + NUM_H nNum_H; /* number of 1H isotopic atoms attached */ + + NUM_H nNum_D; /* number of 2H isotopic atoms attached */ + + NUM_H nNum_T; /* number of 3H isotopic atoms attached */ +} INChI_IsotopicAtom; + + + +typedef struct tagINChI_IsotopicTGroup +{ + + AT_NUMB nTGroupNumber; /* Tautomeric group number */ + + AT_NUMB nNum_H; /* number of 1H isotopic atoms */ + + AT_NUMB nNum_D; /* number of 2H isotopic atoms */ + + AT_NUMB nNum_T; /* number of 3H isotopic atoms */ +} INChI_IsotopicTGroup; + + + + +typedef struct tagINChI_Stereo +{ + /* [N] = allocated length */ + /* ---- possibly tetrahedral stereogenic atoms */ + + int nNumberOfStereoCenters; + + AT_NUMB *nNumber; /* Canonical number of a possibly tetrahedral + * stereogenic atom or allenes [nNumberOfAtoms] */ + + S_CHAR *t_parity; /* tetrahedral (relative, see nCompInv2Abs) atom parities [nNumberOfAtoms] */ + /* ---- possibly tetrahedral stereogenic atoms of the iverted structure */ + + AT_NUMB *nNumberInv; /* Canonical number of a possibly tetrahedral + * stereogenic atom or allene [nNumberOfAtoms] */ + + S_CHAR *t_parityInv; /* tetrahedral inverted atom parities [nNumberOfAtoms] */ + + /* bFlagAbsStereoIsInverted = nCompInv2Abs==-1: Abs stereo = Inverted */ + int nCompInv2Abs; /* 0=>Inv = Abs stereo; -1=> Inv < Abs stereo, +1=> Inv > Abs stereo; +2=> in reading InChI: no /m was found and in /sN N>0 */ + + int bTrivialInv; /* 1=> nCompInv2Abs!= 0 && Inverted = Abs stereo with inverted parities 1<-->2 */ + + /* ---- possibly stereogenic bonds and tetrahedral cumuleles */ + int nNumberOfStereoBonds; + AT_NUMB *nBondAtom1; /* Canonical number of a first atom + * [number of bonds] */ + AT_NUMB *nBondAtom2; /* Canonical number of a second atom + * [number of bonds] */ + S_CHAR *b_parity; /* possibly stereogenic bond parities + * [number of bonds] */ +} INChI_Stereo; +#define INCHI_FLAG_ACID_TAUT 0x0001 /* tautomerism of dissociated acid invoked */ +#define INCHI_FLAG_REL_STEREO 0x0002 /* requested relative stereo */ +#define INCHI_FLAG_RAC_STEREO 0x0004 /* requested racemic stereo */ +#define INCHI_FLAG_SC_IGN_ALL_UU 0x0008 /* ignored all undefined/unknown stereocenters, non-isotopic */ +#define INCHI_FLAG_SB_IGN_ALL_UU 0x0010 /* ignored all undefined/unknown stereocenters, non-isotopic */ +#define INCHI_FLAG_SC_IGN_ALL_ISO_UU 0x0020 /* ignored all undefined/unknown stereocenters, isotopic */ +#define INCHI_FLAG_SB_IGN_ALL_ISO_UU 0x0040 /* ignored all undefined/unknown stereocenters, isotopic */ +#define INCHI_FLAG_HARD_ADD_REM_PROTON 0x0080 /* in normalization a proton has been added or removed along alt path */ + +#define INCHI_OUT_NO_AUX_INFO 0x0001 /* do not output Aux Info */ +#define INCHI_OUT_SHORT_AUX_INFO 0x0002 /* output short version of Aux Info */ +#define INCHI_OUT_ONLY_AUX_INFO 0x0004 /* output only Aux Info */ +#define INCHI_OUT_EMBED_REC 0x0008 /* embed reconnected INChI into disconnected INChI */ +#define INCHI_OUT_SDFILE_ONLY 0x0010 /* save input data in a Molfile instead of creating INChI */ +#define INCHI_OUT_XML 0x0020 /* *obsolete* output xml INChI */ +#define INCHI_OUT_PLAIN_TEXT 0x0040 /* output plain text INChI */ +#define INCHI_OUT_PLAIN_TEXT_COMMENTS 0x0080 /* output plain text annotation */ +#define INCHI_OUT_XML_TEXT_COMMENTS 0x0100 /* *obsolete* output xml text annotation */ +#define INCHI_OUT_WINCHI_WINDOW 0x0200 /* output into wINChI text window */ +#define INCHI_OUT_TABBED_OUTPUT 0x0400 /* tab-delimited (only for plain text) */ +#define INCHI_OUT_SDFILE_ATOMS_DT 0x0800 /* SDfile output H isotopes as D and T */ +#define INCHI_OUT_SDFILE_SPLIT 0x1000 /* Split SDfile into components */ + +#define INCHI_OUT_FIX_TRANSPOSITION_CHARGE_BUG 0x2000 + /* used to accomodate FIX_TRANSPOSITION_CHARGE_BUG */ +#define INCHI_OUT_STDINCHI 0x4000 +#define INCHI_OUT_SAVEOPT 0x8000 + +#define INCHI_OUT_INCHI_GEN_ERROR 0x0001 /* v. 1.05 */ +#define INCHI_OUT_MISMATCH_AS_ERROR 0x0002 /* v. 1.05 */ + +/* Bits encoding InChI creation options to be saved */ +#define SAVE_OPT_SLUUD 0x0001 +#define SAVE_OPT_SUU 0x0002 +#define SAVE_OPT_FIXEDH 0x0004 +#define SAVE_OPT_RECMET 0x0008 +#define SAVE_OPT_KET 0x0010 +#define SAVE_OPT_15T 0x0020 + + +#define INCHI_OUT_PRINT_OPTIONS (INCHI_OUT_EMBED_REC | \ + INCHI_OUT_PLAIN_TEXT | \ + INCHI_OUT_PLAIN_TEXT_COMMENTS) + + +/*******REQ_MODE_SB_IGN_ALL_UU*************** chemical identifier definition *****************/ +typedef struct tagINChI { /* [N] = allocated length */ + + int nErrorCode; /* 0 = success */ + INCHI_MODE nFlags; /* INCHI_FLAG_ACID_TAUT tautomerism of dissociated acid invoked + INCHI_FLAG_REL_STEREO requested relative stereo + INCHI_FLAG_RAC_STEREO requested racemic stereo + INCHI_FLAG_SC_IGN_ALL_UU ignored all undefined/unknown stereocenters, non-isotopic + INCHI_FLAG_SB_IGN_ALL_UU ignored all undefined/unknown stereocenters, non-isotopic + INCHI_FLAG_SC_IGN_ALL_ISO_UU ignored all undefined/unknown stereocenters, isotopic + INCHI_FLAG_SB_IGN_ALL_ISO_UU ignored all undefined/unknown stereocenters, isotopic + INCHI_FLAG_HARD_ADD_REM_PROTON in normalization a proton has been added or removed along alt path + */ + /* ---- basic & tautomer layer */ + int nTotalCharge; + int nNumberOfAtoms; + char *szHillFormula; + U_CHAR *nAtom; /* atomic numbers [nNumberOfAtoms] from the Periodic Table */ + int lenConnTable; + AT_NUMB *nConnTable; /* Connection table [nNumberOfAtoms+NumberOfBonds] */ + int lenTautomer; + AT_NUMB *nTautomer; /* NumGroups; ((NumAt+1, NumH, At1..AtNumAt),...); {INCHI_T_NUM_MOVABLE = 1} - old + * NumGroups; ((NumAt+2, NumH, Num(-), At1..AtNumAt),...); {INCHI_T_NUM_MOVABLE = 2} - new + * Allocated length: [5*nNumberOfAtoms/2+1], see Alloc_INChI(...) */ + S_CHAR *nNum_H; /* number of terminal hydrogen atoms on each atom; in tautomeric + * representation these H on tautomeric atoms are not included [nNumberOfAtoms] */ + S_CHAR *nNum_H_fixed;/* number of terminal hydrogen atoms on tautomeric atoms, + * in non-atautomeric representation only [nNumberOfAtoms] */ + /* ---- isotopic & isotopic tautomeric layer */ + int nNumberOfIsotopicAtoms; + INChI_IsotopicAtom *IsotopicAtom; /* [nNumberOfIsotopicAtoms] */ + int nNumberOfIsotopicTGroups; + /* in reversing InChI keeps a pointer to stolen from AuxInfo coordinates */ + INChI_IsotopicTGroup *IsotopicTGroup; /* [nNumberOfIsotopicAtoms] */ + /* ---- stereo layer */ + INChI_Stereo *Stereo; + INChI_Stereo *StereoIsotopic; + /* not including mobile H groups */ + AT_NUMB *nPossibleLocationsOfIsotopicH; /* [0]=> length including 0th element, location1,...*/ + int bDeleted; +#if ( bREUSE_INCHI == 1 ) + int nRefCount; +#endif +#if ( bRELEASE_VERSION == 0 ) + int bExtract; +#endif +#if ( READ_INCHI_STRING == 1 ) + int nLink; /* negative: ignore InChI; positive: index of (Reconnected component) + 1 linked to it */ +#endif +} INChI; + +typedef INChI *PINChI2[TAUT_NUM]; + +typedef struct tagOrigInfo { + S_CHAR cCharge; + S_CHAR cRadical; /* 0=none, 1=doublet, 2=triplet, 3=unknown */ + S_CHAR cUnusualValence; /* see get_unusual_el_valence() */ +} ORIG_INFO; +/******************** auxiliary chemical identifier info **************/ +typedef struct tagINChI_Aux { /* [N] = allocated length */ + + int nErrorCode; /* 0 = success */ + int nNumberOfAtoms; + int nNumberOfTGroups; /* non-zero only in tautomeric representation */ + int bIsIsotopic; /* filled out even though isotopic has not been requested */ + int bIsTautomeric; /* filled out even though tautomeric has not been requested; non-zero if taut exists */ + /* canonical numbers of the atoms: nOrigAtNosInCanonOrd[i-1]+1 = */ + /* input atom number for the canonical number i */ + AT_NUMB *nOrigAtNosInCanonOrd; /* [nNumberOfInputAtoms*1.5]; */ + AT_NUMB *nIsotopicOrigAtNosInCanonOrd; /* [nNumberOfInputAtoms*1.5]; */ + /* same for the inverted structure */ + AT_NUMB *nOrigAtNosInCanonOrdInv; /* inverted stereo [nNumberOfInputAtoms*1.5]; */ + AT_NUMB *nIsotopicOrigAtNosInCanonOrdInv; /* [nNumberOfInputAtoms*1.5]; */ + AT_NUMB *nConstitEquNumbers; /* [nNumberOfAtoms*1.5] */ + AT_NUMB *nConstitEquTGroupNumbers; /* [nNumberOfAtoms/2] */ + AT_NUMB *nConstitEquIsotopicNumbers; /* [nNumberOfAtoms*1.5] */ + AT_NUMB *nConstitEquIsotopicTGroupNumbers; /* [nNumberOfAtoms/2] */ +#if ( bREUSE_INCHI == 1 ) + int nRefCount; +#endif + + ORIG_INFO *OrigInfo; + MOL_COORD *szOrigCoord; + NUM_H nNumRemovedProtons; + NUM_H nNumRemovedIsotopicH[NUM_H_ISOTOPES]; /* isotopic H that may be exchanged and considered + randomly distributed, including removed protons; + order: 0=>1H, 1=>D, 2=>T */ + int bDeleted; + INCHI_MODE bTautFlags; /* t_group_info->bTautFlags */ + INCHI_MODE bTautFlagsDone; /* t_group_info->bTautFlagsDone */ + INCHI_MODE bNormalizationFlags; /* t_group_info->tni.bNormalizationFlags */ + int nCanonFlags; +} INChI_Aux; + +typedef INChI_Aux *PINChI_Aux2[TAUT_NUM]; + +/********************* array of pointers for sorting components and INChI output *********/ +typedef struct tagINChIforSort { + INChI *pINChI[TAUT_NUM]; + INChI_Aux *pINChI_Aux[TAUT_NUM]; + short ord_number; /* for stable sort */ + short n1; /* points to the original; used in structure reconstruction only */ + short n2; /* points to the original; used in structure reconstruction only */ + short n3; /* points to the original; used in structure reconstruction only */ +}INCHI_SORT; + + + +#endif /* _ICHI_H_ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichi_bns.c b/INCHI-1-SRC/INCHI_BASE/src/ichi_bns.c similarity index 86% rename from INCHI-1-SRC/INCHI_API/inchi_dll/ichi_bns.c rename to INCHI-1-SRC/INCHI_BASE/src/ichi_bns.c index 98bb2f9..91e6a3e 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichi_bns.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichi_bns.c @@ -1,10095 +1,10445 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - - -#include "mode.h" - -#include "ichierr.h" -#include "incomdef.h" -#include "inpdef.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "util.h" -#include "ichicomp.h" -#include "ichister.h" -#include "ichi_bns.h" - - -#define BNS_MARK_ONLY_BLOCKS 1 /* 1 => find only blocks, do not search for ring systems */ -#define ALLOW_ONLY_SIMPLE_ALT_PATH 0 /* 0 => allow alt. path to contain same bond 2 times (in opposite directions) */ -#define CHECK_TG_ALT_PATH 0 /* 1=> when chacking alt path of a tautomeric atom modify - t-group, not the atom */ - /* 0=> old mode */ - -#define FIX_CPOINT_BOND_CAP 1 /* 1=> fix bug in case of double bond from neutral cpoint */ -#define RESET_EDGE_FORBIDDEN_MASK 1 /* 1: previous; 0: do not apply "edge->forbidden &= pBNS->edge_forbidden_mask" */ -#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) -#define IS_FORBIDDEN(EDGE_FORBIDDEN, PBNS) (EDGE_FORBIDDEN) -#else -#define IS_FORBIDDEN(EDGE_FORBIDDEN, PBNS) (EDGE_FORBIDDEN & PBNS->edge_forbidden_mask) -#endif - - -typedef enum tagAtTypeTotals -{ - /* counts do not include: - charged atom adjacent to another charged atom - atom in an unusual valence state or adjacent to an atom in an unusual valence state - radicals different from singlet - */ - /*ATTOT_NUM_Plus. */ /* number of positive charges, +1, is (ATTOT_NUM_CHARGES + ATTOT_TOT_CHARGE)/2 */ - /*ATTOT_NUM_Minus.*/ /* number of negative charges, -1, is (ATTOT_NUM_CHARGES - ATTOT_TOT_CHARGE)/2 */ - ATTOT_NUM_NP_Plus, /* 0 no H: =N(+)=, #N(+)-, =N(+)<, does not include onium cations >P(+)<, >N(+)< */ - ATTOT_NUM_NP_Proton, /* 1 H(+): -NH3(+), =NH2(+), >NH2(+), =NH(+)-, >NH(+)-, #NH(+), N=N,P */ - ATTOT_NUM_NP_H, /* 2 H: -NH2, =NH, >NH -NH(-) */ - ATTOT_NUM_N_Minus, /* 3 (-): -NH(-), >N(-), =N(-) */ - ATTOT_NUM_NP, /* 4 no H: >N- =N-, #N */ - ATTOT_NUM_ON, /* 5 -N=O: do not allow -N=O => -NH-OH during H(+) add/removal */ - ATTOT_NUM_COH, /* 6 =C-OH, #C-OH; O=O,S,Se,Te */ - ATTOT_NUM_CSH, /* 7 -C-SH, -C-SeH -C-TeH */ - ATTOT_NUM_ZOH, /* 8 =Z-OH, #Z-OH; O=O,S,Se,Te; Z may have charge, Z != C */ - ATTOT_NUM_OOH, /* 9 -O-OH, O=O,S,Se,Te */ - ATTOT_NUM_ZOOH, /* 10 O=Z-OH, O=O,S,Se,Te */ - ATTOT_NUM_NOH, /* 11 =N-OH, -N(-)-OH */ - ATTOT_NUM_N_OH, /* 12 >N-OH, -NH-OH, >NH(+)-OH, -N(-)-OH */ - ATTOT_NUM_CO, /* 13 -C=O, =C=O; O=O,S,Se,Te */ - ATTOT_NUM_ZO, /* 14 -Z=O, =Z=O; O=O,S,Se,Te; Z may have charge */ - ATTOT_NUM_NO, /* 15 -N=O, =N(+)=O */ - ATTOT_NUM_N_O, /* 16 >N(+)=O, =N(+)=O */ - ATTOT_NUM_CO_Minus, /* 17 =C-O(-), #C-O(-); O=O,S,Se,Te */ - ATTOT_NUM_CS_Minus, /* 18 -C-S(-); S = S, Se, Te */ - ATTOT_NUM_ZO_Minus, /* 19 =Z-O(-), #Z-O(-); O = O, S, Se, Te */ - ATTOT_NUM_OO_Minus, /* 20 -O-O(-), O=O,S,Se,Te */ - ATTOT_NUM_ZOO_Minus, /* 21 O=Z-O(-), O=O,S,Se,Te */ - ATTOT_NUM_NO_Minus, /* 22 >N-O(-), -NH-O(-) */ - ATTOT_NUM_N_O_Minus, /* 23 -NH-O(-), >N-O(-); O = O, S, Se, Te */ - ATTOT_NUM_O_Minus, /* 24 -Z-O(-); O=O,S,Se,Te */ - ATTOT_NUM_OH_Plus, /* 25 any OH(+) */ - ATTOT_NUM_O_Plus, /* 26 any O(+) without H */ - ATTOT_NUM_Proton, /* 27 proton */ - ATTOT_NUM_HalAnion, /* 28 Halogen anion */ - ATTOT_NUM_HalAcid, /* 29 Halogen acid */ - ATTOT_NUM_Errors, /* 30 for debugging */ - ATTOT_TOT_CHARGE, /* 31 total of positive and negative single charges, +1 and -1 */ - ATTOT_NUM_CHARGES, /* 32 number of positive and negative single charges, +1 and -1 */ - ATTOT_ARRAY_LEN /* 33 array length */ -} AT_TYPE_TOTALS; - -#define ATBIT_NP_Plus (1 << ATTOT_NUM_NP_Plus) -#define ATBIT_NP_Proton (1 << ATTOT_NUM_NP_Proton) -#define ATBIT_NP_H (1 << ATTOT_NUM_NP_H) -#define ATBIT_N_Minus (1 << ATTOT_NUM_N_Minus) -#define ATBIT_NP (1 << ATTOT_NUM_NP) -#define ATBIT_ON (1 << ATTOT_NUM_ON) -#define ATBIT_COH (1 << ATTOT_NUM_COH) -#define ATBIT_CSH (1 << ATTOT_NUM_CSH) -#define ATBIT_ZOH (1 << ATTOT_NUM_ZOH) -#define ATBIT_OOH (1 << ATTOT_NUM_OOH) -#define ATBIT_ZOOH (1 << ATTOT_NUM_ZOOH) -#define ATBIT_NOH (1 << ATTOT_NUM_NOH) -#define ATBIT_N_OH (1 << ATTOT_NUM_N_OH) -#define ATBIT_CO (1 << ATTOT_NUM_CO) -#define ATBIT_ZO (1 << ATTOT_NUM_ZO) -#define ATBIT_NO (1 << ATTOT_NUM_NO) -#define ATBIT_N_O (1 << ATTOT_NUM_N_O) -#define ATBIT_CO_Minus (1 << ATTOT_NUM_CO_Minus) -#define ATBIT_CS_Minus (1 << ATTOT_NUM_CS_Minus) -#define ATBIT_ZO_Minus (1 << ATTOT_NUM_ZO_Minus) -#define ATBIT_OO_Minus (1 << ATTOT_NUM_OO_Minus) -#define ATBIT_ZOO_Minus (1 << ATTOT_NUM_ZOO_Minus) -#define ATBIT_NO_Minus (1 << ATTOT_NUM_NO_Minus) -#define ATBIT_N_O_Minus (1 << ATTOT_NUM_N_O_Minus) -#define ATBIT_O_Minus (1 << ATTOT_NUM_O_Minus) -#define ATBIT_OH_Plus (1 << ATTOT_NUM_OH_Plus) -#define ATBIT_O_Plus (1 << ATTOT_NUM_O_Plus) -#define ATBIT_Proton (1 << ATTOT_NUM_Proton) -#define ATBIT_HalAnion (1 << ATTOT_NUM_HalAnion) -#define ATBIT_HalAcid (1 << ATTOT_NUM_HalAcid) - - -#define ATBIT_Errors (1 << ATTOT_NUM_Errors) - -typedef struct tagProtonRemovalMaskAndType -{ - int typePos; /* atoms accessible to positive charges */ - int maskPos; - int typeNeg; /* atoms accessible to negative charges */ - int maskNeg; - int typeH; /* atoms accessible to hydrogen atoms */ - int maskH; -} PRMAT; - -#define PR_SIMPLE_MSK (ATBIT_NP_Proton | ATBIT_OH_Plus) -#define PR_SIMPLE_TYP (ATT_ATOM_N | ATT_ATOM_P | ATT_O_PLUS) - -#define ATBIT_MSK_NP (ATBIT_NP_Plus | ATBIT_NP_Proton | ATBIT_NP_H | ATBIT_N_Minus | ATBIT_NP) -#define KNOWN_ACIDIC_TYPE (ATT_ACIDIC_CO | ATT_ACIDIC_S | ATT_OO | ATT_ZOO | ATT_NO) -#define ATBIT_MSK_OS (ATBIT_COH | ATBIT_CSH | ATBIT_ZOH | ATBIT_OOH | ATBIT_ZOOH | ATBIT_NOH | ATBIT_N_OH |\ - ATBIT_CO | ATBIT_ZO | ATBIT_NO | ATBIT_N_O |\ - ATBIT_CO_Minus | ATBIT_CS_Minus | ATBIT_ZO_Minus | ATBIT_OO_Minus |\ - ATBIT_ZOO_Minus | ATBIT_NO_Minus | ATBIT_N_O_Minus /*| ATBIT_O_Minus*/ ) -#define ATBIT_MSK_H (ATBIT_NP_Proton | ATBIT_NP_H | ATBIT_COH | ATBIT_CSH | ATBIT_ZOH | ATBIT_OOH |\ - ATBIT_ZOOH | ATBIT_NOH | ATBIT_N_OH) - -#define ATTYP_OS (ATT_ACIDIC_CO | ATT_ACIDIC_S | ATT_OO | ATT_ZOO | ATT_NO /*| ATT_OTHER_NEG_O*/ | ATT_OTHER_ZO) -#define ATTYP_NP (ATT_ATOM_N | ATT_ATOM_P) -#define ATTYP_N (ATT_ATOM_N) -#define ATTYP_P (ATT_ATOM_P) - -/************* simple proton removal from acids **************************/ -#define AR_ANY_OH 0 /* 1 => create unknown to be acidic anions */ -#define AR_SIMPLE_STEPS 3 -/* acidic groups for proton removal, step 1 */ -#define AR_SIMPLE_MSK1 (ATBIT_COH | ATBIT_CSH | ATBIT_OOH | ATBIT_ZOOH | ATBIT_NOH | ATBIT_HalAcid) -#define AR_SIMPLE_TYP1 (ATT_ACIDIC_CO | ATT_ACIDIC_S | ATT_OO | ATT_ZOO | ATT_NO | ATT_HalAcid) -/* acidic groups for proton removal, step 2 */ -#define AR_SIMPLE_MSK2 (AR_ANY_OH? (ATBIT_N_OH) :0) -#define AR_SIMPLE_TYP2 (AR_ANY_OH? (ATT_N_O) :0) -/* acidic groups for proton removal, step 3 */ -#define AR_SIMPLE_MSK3 (AR_ANY_OH? (ATBIT_ZOH) :0) -#define AR_SIMPLE_TYP3 (AR_ANY_OH? (ATT_OTHER_ZO):0) - -/************* simple proton addition to acids **************************/ -#define AA_ANY_O_Minus 0 /* 1 => neutralize unknown to be acidic anions */ -#define AA_SIMPLE_STEPS 3 -/* acidic groups for proton addition, step 1 */ -#define AA_SIMPLE_MSK1 (ATBIT_CO_Minus | ATBIT_CS_Minus | ATBIT_OO_Minus | ATBIT_ZOO_Minus | ATBIT_NO_Minus | ATBIT_O_Minus | ATBIT_HalAnion) -#define AA_SIMPLE_TYP1 (ATT_ACIDIC_CO | ATT_ACIDIC_S | ATT_OO | ATT_ZOO | ATT_NO | ATT_OH_MINUS | ATT_HalAnion ) -/* acidic groups for proton addition, step 2 */ -#define AA_SIMPLE_MSK2 (AA_ANY_O_Minus? (ATBIT_N_O_Minus) :0) -#define AA_SIMPLE_TYP2 (AA_ANY_O_Minus? (ATT_N_O) :0) -/* acidic groups for proton addition, step 3 */ -#define AA_SIMPLE_MSK3 (AA_ANY_O_Minus? (ATBIT_ZO_Minus | ATBIT_O_Minus):0) -#define AA_SIMPLE_TYP3 (AA_ANY_O_Minus? (ATT_OTHER_ZO) :0) - -#if ( FIX_NP_MINUS_BUG == 1 ) -/* allow to add H(+) to =N(-) which previously was #N */ -#undef AA_SIMPLE_STEPS -#define AA_SIMPLE_STEPS 4 -#define AA_SIMPLE_MSK4 ATBIT_N_Minus -#define AA_SIMPLE_TYP4 ATT_NP_MINUS_V23 -#endif - -/************* hard proton removal from NP **************************/ -/* (+) charge group for proton removal: mask & type */ -#define PR_HARD_MSK_POS ATBIT_MSK_NP -#define PR_HARD_TYP_POS ATTYP_N -#define PR_HARD_TYP_POSP ATTYP_P -/* (-) charge group for proton removal */ -#define PR_HARD_MSK_NEG (ATBIT_MSK_NP | ATBIT_MSK_OS) -#define PR_HARD_TYP_NEG (ATTYP_N | ATTYP_OS) -/* H-group for proton removal */ -#define PR_HARD_MSK_H (ATBIT_MSK_NP | ATBIT_MSK_OS) -#define PR_HARD_TYP_H (ATTYP_N | ATTYP_OS) - -/************* hard proton removal from acids **************************/ -/* (+) charge group for proton removal: mask & type */ -#define AR_HARD_MSK_POS ATBIT_MSK_NP -#define AR_HARD_TYP_POS ATTYP_N -/* (-) charge group for proton removal */ -#define AR_HARD_MSK_NEG (ATBIT_MSK_NP | ATBIT_MSK_OS) -#define AR_HARD_TYP_NEG (ATTYP_N | ATTYP_OS) -/* H-group acid for proton removal */ -#define AR_HARD_MSK_HA (ATBIT_CO | ATBIT_NO ) -#define AR_HARD_TYP_HA (ATT_ACIDIC_CO | ATT_NO) -/* H-group non-acid for proton removal */ -#define AR_HARD_MSK_HN ((ATBIT_MSK_NP | ATBIT_MSK_OS) & ~AR_HARD_MSK_HA) -#define AR_HARD_TYP_HN ((ATTYP_N | ATTYP_OS) /*& ~AR_HARD_TYP_HA*/) - -/************* hard proton addition to acids **************************/ -/* (+) charge group for proton removal: mask & type */ -#define AA_HARD_MSK_POS ATBIT_MSK_NP -#define AA_HARD_TYP_POS ATTYP_N -/* (-) charge group for negative charge removal */ -#define AA_HARD_MSK_NEG ((ATBIT_MSK_NP | ATBIT_MSK_OS) & ~(ATBIT_CO | ATBIT_NO )) -#define AA_HARD_TYP_NEG (ATTYP_N | ATTYP_OS) -/* (-) charge group to accept negative charges */ -#define AA_HARD_MSK_CO (ATBIT_CO | ATBIT_NO ) -#define AA_HARD_TYP_CO (ATT_ACIDIC_CO | ATT_NO) -/* H-group non-acid for proton removal */ -#define AA_HARD_MSK_H (ATBIT_MSK_NP | ATBIT_MSK_OS) -#define AA_HARD_TYP_H (ATTYP_N | ATTYP_OS) - - -/*****************************************************************************/ -#define BNS_MAX_NUM_FLOW_CHANGES (1+2*MAX_BOND_EDGE_CAP) - -/* -- opiginal Pascal values -- -#define NO_VERTEX 0 -#define BLOSSOM_BASE -1 -#define FIRST_INDX 1 -*/ - -#define TREE_NOT_IN_M 0 /* not in T or T' */ -#define TREE_IN_2 1 /* in T' and not s-reachable */ -#define TREE_IN_2BLOSS 2 /* in T' and in a blossom, is s-reachable */ -#define TREE_IN_1 3 /* in T and is s-reachable */ - -#define TREE_IS_S_REACHABLE(X) (Tree[X] >= TREE_IN_2BLOSS) -#define TREE_IS_ON_SCANQ TREE_IS_S_REACHABLE -/* #define TREE_IS_ON_SCANQ(X) (Tree[X] != TREE_NOT_IN_M) */ -#define TREE_MARK(X, MARK) do{ if( Tree[X] < MARK ) Tree[X]=MARK; }while(0) - - -/***************************************************************************** - * store changes done to check whether an alternating path exists - * (see bSetBnsToCheckAltPath, bRestoreBnsAfterCheckAltPath) -******************************************************************************/ -typedef struct tagAltPathChanges -{ - /* caps changed in up to 2 vertices */ - VertexFlow nOldCapsVert[2][MAXVAL+1]; - Vertex vOldVert[2]; - S_CHAR bSetOldCapsVert[2]; /* number of caps to restore, including st-cap */ - /* save ids of the newly created temporary vertices */ - Vertex vNewVertex[2]; - S_CHAR bSetNew[2]; /* indicators whether to remove vertices */ - -} ALT_PATH_CHANGES; - -/*****************************************************************************/ - -/* Local functions */ - -int RestoreRadicalsOnly( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at ); -int bRadChangesAtomType( BN_STRUCT *pBNS, BN_DATA *pBD, Vertex v, Vertex v_1, Vertex v_2 ); -int BnsAdjustFlowBondsRad( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at, int num_atoms ); -int SetAtomRadAndChemValFromVertexCapFlow( BN_STRUCT *pBNS, inp_ATOM *atom, int v1 ); -int bNeedToTestTheFlow( int bond_type, int nTestFlow, int bTestForNonStereoBond ); -int RestoreEdgeFlow( BNS_EDGE *edge, int delta, int bChangeFlow ); -int SetAtomBondType( BNS_EDGE *edge, U_CHAR *bond_type12, U_CHAR *bond_type21, int delta, int bChangeFlow ); -int RestoreBnStructFlow( BN_STRUCT *pBNS, int bChangeFlow ); -int CompTGroupNumber( const void *tg1, const void *tg2 ); -int CompCGroupNumber( const void *cg1, const void *cg2 ); - -/* Rings, Blocks, Non-stereo bonds */ -int ReInitBnStructForAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bUnknAltAsNoStereo ); -int MarkRingSystemsAltBns( BN_STRUCT* pBNS, int bUnknAltAsNoStereo ); -int MarkNonStereoAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bUnknAltAsNoStereo ); - -/* Called from BalancedNetworkSearch */ -int GetVertexDegree( BN_STRUCT* pBNS, Vertex v ); -/* Vertex Get2ndNeighbor1( BN_STRUCT* pBNS, Vertex u, EdgeIndex iedge ); not used */ -Vertex Get2ndEdgeVertex( BN_STRUCT* pBNS, Edge uv ); -Vertex GetVertexNeighbor( BN_STRUCT* pBNS, Vertex v, int neigh, EdgeIndex *iedge ); -int GetEdgePointer( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv, BNS_EDGE **uv, S_CHAR *s_or_t ); -int AugmentEdge( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv, int delta, S_CHAR bReverse, int bChangeFlow ); -int rescap_mark( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv ); -int rescap( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv ); -Vertex FindBase( Vertex u, Vertex *BasePtr ); -int FindPathToVertex_s( Vertex x, Edge *SwitchEdge, Vertex *BasePtr, Vertex *Path, int MaxPathLen ); -Vertex MakeBlossom( BN_STRUCT* pBNS, Vertex *ScanQ, int *pQSize, - Vertex *Pu, Vertex *Pv, int max_len_Pu_Pv, - Edge *SwitchEdge, Vertex *BasePtr, - Vertex u, Vertex v, EdgeIndex iuv, Vertex b_u, Vertex b_v, S_CHAR *Tree ); -int PullFlow( BN_STRUCT *pBNS, Edge *SwitchEdge, Vertex x, Vertex y, int delta, S_CHAR bReverse, int bChangeFlow ); -int FindPathCap( BN_STRUCT* pBNS, Edge *SwitchEdge, Vertex x, Vertex y, int delta ); - -/* -int SetBondType( BNS_EDGE *edge, U_CHAR *bond_type12, U_CHAR *bond_type21, int delta, int bChangeFlow ); -int SetBondsRestoreBnStructFlow( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bChangeFlow ); -*/ -int SetBondsFromBnStructFlow( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bChangeFlow0 ); -int MarkAtomsAtTautGroups( BN_STRUCT *pBNS, int num_atoms, BN_AATG *pAATG, int nEnd1, int nEnd2 ); - -int nMinFlow2Check( BN_STRUCT *pBNS, int iedge ); -int nMaxFlow2Check( BN_STRUCT *pBNS, int iedge ); -int nCurFlow2Check( BN_STRUCT *pBNS, int iedge ); - -/* Bonds testing */ -/* -int bRestoreFlowToCheckOneBond( BN_STRUCT *pBNS, BNS_FLOW_CHANGES *fcd, int nTestFlow, inp_ATOM *at, int num_atoms, int bChangeFlow ); -*/ -int bSetFlowToCheckOneBond( BN_STRUCT *pBNS, int iedge, int flow, BNS_FLOW_CHANGES *fcd ); -int bRestoreFlowAfterCheckOneBond( BN_STRUCT *pBNS, BNS_FLOW_CHANGES *fcd ); -int bSetBondsAfterCheckOneBond( BN_STRUCT *pBNS, BNS_FLOW_CHANGES *fcd, int nTestFlow, inp_ATOM *at, int num_atoms, int bChangeFlow ); -int BnsTestAndMarkAltBonds( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at, int num_atoms, BNS_FLOW_CHANGES *fcd, int bChangeFlow, int nBondTypeToTest ); -int bIsAltBond( int bond_type ); - -/* Fix bonds */ -int fix_special_bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int edge_forbidden_mask ); -int TempFix_NH_NH_Bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms ); -int CorrectFixing_NH_NH_Bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms ); - -/* Alt path testing */ -int bSetBnsToCheckAltPath( BN_STRUCT *pBNS, int nVertDoubleBond, int nVertSingleBond, AT_NUMB type, - int path_type, ALT_PATH_CHANGES *apc, BNS_FLOW_CHANGES *fcd, int *nDots ); -int bRestoreBnsAfterCheckAltPath( BN_STRUCT *pBNS, ALT_PATH_CHANGES *apc, int bChangeFlow ); -Vertex GetGroupVertex(BN_STRUCT *pBNS, Vertex v1, AT_NUMB type); -BNS_IEDGE GetEdgeToGroupVertex( BN_STRUCT *pBNS, Vertex v1, AT_NUMB type); -int bAddNewVertex( BN_STRUCT *pBNS, int nVertDoubleBond, int nCap, int nFlow, int nMaxAdjEdges, int *nDots ); -int AddNewEdge( BNS_VERTEX *p1, BNS_VERTEX *p2, BN_STRUCT *pBNS, int nEdgeCap, int nEdgeFlow ); -int bAddStCapToAVertex( BN_STRUCT *pBNS, Vertex v1, Vertex v2, VertexFlow *nOldCapVertSingleBond, int *nDots, int bAdjacentDonors ); - -static void remove_alt_bond_marks(inp_ATOM *at, int num_atoms); -int bIsBnsEndpoint( BN_STRUCT *pBNS, int v ); - -/* Protons removal, charge neutralization */ -int is_acidic_CO( inp_ATOM *atom, int at_no ); -int mark_at_type( inp_ATOM *atom, int num_atoms, int nAtTypeTotals[] ); -int GetAtomChargeType( inp_ATOM *atom, int at_no, int nAtTypeTotals[], int *pMask, int bSubtract ); -int AddChangedAtHChargeBNS( inp_ATOM *at, int num_atoms, int nAtTypeTotals[], S_CHAR *mark ); -int EliminatePlusMinusChargeAmbiguity( BN_STRUCT *pBNS, int num_atoms ); -int AddOrRemoveExplOrImplH( int nDelta, inp_ATOM *at, int num_atoms, AT_NUMB at_no, T_GROUP_INFO *t_group_info ); -int SubtractOrChangeAtHChargeBNS( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, - int nAtTypeTotals[], S_CHAR *mark, T_GROUP_INFO *t_group_info, int bSubtract ); -int is_Z_atom( U_CHAR el_number ); -int IsZOX( inp_ATOM *atom, int at_x, int ord ); -int SimpleRemoveHplusNPO( inp_ATOM *at, int num_atoms, int nAtTypeTotals[], T_GROUP_INFO *t_group_info ); -int CreateCGroupInBnStruct( inp_ATOM *at, int num_atoms, - BN_STRUCT *pBNS, int nType, int nMask, int nCharge ); -int CreateTGroupInBnStruct( inp_ATOM *at, int num_atoms, - BN_STRUCT *pBNS, int nType, int nMask ); -int RemoveLastGroupFromBnStruct( inp_ATOM *at, int num_atoms, int tg, BN_STRUCT *pBNS ); -int SetInitCapFlowToCurrent( BN_STRUCT *pBNS ); -int SimpleRemoveAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2remove ); -int SimpleAddAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2add ); -int HardRemoveAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2remove, - int *nNumCanceledCharges, BN_STRUCT *pBNS, BN_DATA *pBD ); -int HardAddAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2add, - int *nNumCanceledCharges, BN_STRUCT *pBNS, BN_DATA *pBD ); -int HardRemoveHplusNP( inp_ATOM *at, int num_atoms, int bCancelChargesAlways, int *nNumCanceledCharges, - BN_AATG *pAATG, BN_STRUCT *pBNS, BN_DATA *pBD ); -int RemoveNPProtonsAndAcidCharges( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, BN_STRUCT *pBNS, BN_DATA *pBD ); -Vertex GetPrevVertex( BN_STRUCT* pBNS, Vertex y, Edge *SwitchEdge, EdgeIndex *iuv ); -int bIgnoreVertexNonTACN_atom( BN_STRUCT* pBNS, Vertex u, Vertex v ); -int bIgnoreVertexNonTACN_group( BN_STRUCT* pBNS, Vertex v, Vertex w, Edge *SwitchEdge ); -int bIsRemovedHfromNHaion( BN_STRUCT* pBNS, Vertex u, Vertex v ); -int bIsAggressiveDeprotonation( BN_STRUCT* pBNS, Vertex v, Vertex w, Edge *SwitchEdge ); - -int bIsAtomTypeHard( inp_ATOM *at, int endpoint, int nType, int nMask, int nCharge ); -int bIsHDonorAccAtomType( inp_ATOM *at, int endpoint, int *cSubType ); -int bIsNegAtomType( inp_ATOM *at, int i, int *cSubType ); - -#if ( BNS_RAD_SEARCH == 1 ) -int RegisterRadEndpoint( BN_STRUCT *pBNS, BN_DATA *pBD, Vertex u); -int cmp_rad_endpoints( const void *a1, const void *a2 ); -int cmp_endpoints_rad( const void *a1, const void *a2 ); -#endif - -int bHasChargedNeighbor( inp_ATOM *at, int iat ); -/*****************************************************************************/ -/**** prim(v) is v' *****/ -#define prim(v) (Vertex)((v)^1) - -/*****************************************************************************/ -#define SwitchEdge_Vert1(u) SwitchEdge[u][0] -#define SwitchEdge_Vert2(u) Get2ndEdgeVertex( pBNS, SwitchEdge[u] ) -#define SwitchEdge_IEdge(u) SwitchEdge[u][1] -/*****************************************************************************/ - - - - - -/*****************************************************************************/ -/* Returns value > 0 if a bond has been changed */ -/*****************************************************************************/ -int RestoreEdgeFlow( BNS_EDGE *edge, int delta, int bChangeFlow ) -{ - /*flow1 = edge->flow;*/ /* output from BNS */ - switch ( bChangeFlow & BNS_EF_CHNG_RSTR ) { - case 0: /* the flow has not been permitted to change inside the BNS */ - /* nothing to do */ - /*flow1 = edge->flow;*/ /* output from BNS, the original flow value */ - /*flow2 = flow1 + delta;*/ /* the flow would be changed to this value by the BNS if permitted */ - break; - case BNS_EF_CHNG_FLOW: /* the flow has been changed by the BNS; update flow0 */ - /*flow2 = edge->flow;*/ /* output from BNS, the changed value */ - /*flow1 = flow2 - delta;*/ /* the original flow value before the BNS */ - edge->flow0 = edge->flow; /* SAVE NEW EDGE FLOW AS THE INITIAL FLOW FROM CHEM. BONDS */ - break; - case BNS_EF_CHNG_RSTR: /* the flow has been changed by the BNS; requested to change it back */ - /*flow2 = edge->flow;*/ /* output from BNS, the changed value */ - /*flow1 = flow2 - delta;*/ /* the original flow value before the BNS */ - edge->flow = edge->flow-delta; /* CHANGE EDGE FLOW BACK (RESTORE) */ - break; - case BNS_EF_RSTR_FLOW: /* the flow has not been permitted to change inside the BNS */ - /* nothing to do */ - /*flow1 = edge->flow;*/ /* output from BNS, the original flow value */ - /*flow2 = flow1 + delta;*/ /* the flow would be changed to this value by the BNS if permitted */ - break; - } - return 0; -} - - - -/*****************************************************************************/ -/* Returns value > 0 if a bond has been changed; do not change flow */ -/*****************************************************************************/ -int SetAtomBondType( BNS_EDGE *edge, U_CHAR *bond_type12, U_CHAR *bond_type21, int delta, int bChangeFlow ) -{ - int flow1, flow2, tmp, ret = 0; - int bond_mark, bond_type, new_bond_type; - - if ( !edge->pass || !bond_type21 ) - return 0; - - switch ( bChangeFlow & BNS_EF_CHNG_RSTR ) { - case 0: /* the flow has not been permitted to change inside the BNS: simulated in case of check one bond */ - case BNS_EF_RSTR_FLOW: /* the flow has not been permitted to change inside the BNS: obsolete mode, unexpected bChangeFlow */ - flow1 = edge->flow0; /* output from BNS, the original (old) flow value */ - flow2 = flow1 + delta; /* the flow would be changed to this value by the BNS if permitted */ - break; - case BNS_EF_CHNG_FLOW: /* the flow has been changed by the BNS */ - case BNS_EF_CHNG_RSTR: /* the flow has been changed by the BNS; requested to change it back */ - flow2 = edge->flow; /* output from BNS, the changed (new) value */ - flow1 = edge->flow0; /* the original flow (old) value before the BNS */ - break; - default: - return 0; /* added 2006-03-21 */ - } - - if ( (bChangeFlow & BNS_EF_CHNG_BONDS) && (bChangeFlow & BNS_EF_ALTR_NS) !=BNS_EF_ALTR_NS ) { - /* set new bond types according to the new flow values */ - new_bond_type = flow2+BOND_SINGLE; - if ( *bond_type12 != new_bond_type ) { - *bond_type12 = *bond_type21 = new_bond_type; - ret ++; - } - } else - if ( bChangeFlow & BNS_EF_ALTR_BONDS ) { - if ( flow1 == flow2 ) { - goto exit_function; - } - /* update alternating bond information */ - if ( flow1 > flow2 ) { - /* make sure flow2 > flow1 */ - tmp = flow1; - flow1 = flow2; - flow2 = tmp; - } - bond_mark = 0; - switch( bond_type = (*bond_type12 & BOND_TYPE_MASK) ) { - - case BOND_SINGLE: - case BOND_DOUBLE: - case BOND_TRIPLE: - /* assume that the input bond type fits either flow1 or flow2 */ - if ( flow1 == 0 && flow2 == 1 ) { - if ( bChangeFlow & BNS_EF_SET_NOSTEREO ) { - bond_mark = BOND_MARK_ALT12NS; - bond_type = BOND_ALT12NS; - } else { - bond_mark = BOND_MARK_ALT12; - bond_type = BOND_ALTERN; - } - } else - if ( flow1 == 0 && flow2 == 2 ) { - bond_mark = BOND_MARK_ALT13; - bond_type = BOND_ALT_13; - } else - if ( flow1 == 1 && flow2 == 2 ) { - bond_mark = BOND_MARK_ALT23; - bond_type = BOND_ALT_23; - } else { - return BNS_BOND_ERR; /* error */ - } - break; - case BOND_TAUTOM: - if ( flow1 == 0 && flow2 == 1 ) { - bond_mark = BOND_MARK_ALT12NS; - } else { - return BNS_BOND_ERR; /* error */ - } - break; - - default: - new_bond_type = bond_type; - bond_mark = (*bond_type12 & BOND_MARK_MASK); - switch( bond_mark ) { - case BOND_MARK_ALT12: - if ( (bChangeFlow & BNS_EF_SET_NOSTEREO) && flow1 == 0 && flow2 == 1 ) { - bond_mark = BOND_MARK_ALT12NS; - new_bond_type = BOND_ALT12NS; - break; - } - case BOND_MARK_ALT12NS: - if ( flow1 == 2 || flow2 == 2 ) { - bond_mark = BOND_MARK_ALT123; - new_bond_type = BOND_ALT_123; - } - break; - case BOND_MARK_ALT13: - if ( flow1 == 1 || flow2 == 1 ) { - bond_mark = BOND_MARK_ALT123; - new_bond_type = BOND_ALT_123; - } - break; - case BOND_MARK_ALT23: - if ( flow1 == 0 || flow2 == 0 ) { - bond_mark = BOND_MARK_ALT123; - new_bond_type = BOND_ALT_123; - } - break; - case BOND_MARK_ALT123: - break; - - case 0: /* special case: second alt bond testing */ - if ( flow1 == 0 && flow2 == 1 ) { - bond_mark = BOND_MARK_ALT12; - } else - if ( flow1 == 0 && flow2 == 2 ) { - bond_mark = BOND_MARK_ALT13; - } else - if ( flow1 == 1 && flow2 == 2 ) { - bond_mark = BOND_MARK_ALT23; - } else { - return BNS_BOND_ERR; /* error */ - } - break; - - - default: - return BNS_BOND_ERR; /* error */ - } - switch( bond_type ) { - case BOND_TAUTOM: - break; - case BOND_ALTERN: - case BOND_ALT12NS: - case BOND_ALT_123: - case BOND_ALT_13: - case BOND_ALT_23: - bond_type = new_bond_type; - break; - default: - return BNS_BOND_ERR; /* error */ - } - } - new_bond_type = bond_type | bond_mark; - if ( new_bond_type != *bond_type12 ) { - *bond_type12 = *bond_type21 = new_bond_type; - ret ++; - } - } -exit_function: - return ret; -} - - - -/*****************************************************************************/ -int RunBalancedNetworkSearch( BN_STRUCT *pBNS, BN_DATA *pBD, int bChangeFlow ) -{ - /* Run BNS until no aug pass is found */ - int pass, delta=0, nSumDelta; - nSumDelta = 0; - for ( pass = 0; pass < pBNS->max_altp; pass ++ ) { - pBNS->alt_path = pBNS->altp[pass]; - pBNS->bChangeFlow = 0; - delta=BalancedNetworkSearch ( pBNS, pBD, bChangeFlow ); - ReInitBnData( pBD ); - if ( 0 < delta ) { - pBNS->num_altp ++; - nSumDelta += abs( delta ); - } else { - break; - } - } - if ( IS_BNS_ERROR(delta) ) - return delta; - return nSumDelta; /* number of eliminated pairs of "dots" */ -} - - - -/*****************************************************************************/ -int SetAtomRadAndChemValFromVertexCapFlow( BN_STRUCT *pBNS, inp_ATOM *atom, int v1 ) -{ - BNS_VERTEX *vert = pBNS->vert + v1; - inp_ATOM *at = atom + v1; - S_CHAR cValue; - int nChanges = 0; - /* set only on the 1st pass */ - if ( !vert->st_edge.pass ) { - return 0; - } - /* adjust chem_bonds_valence */ - cValue = at->chem_bonds_valence - at->valence; - if ( cValue >= 0 && cValue != vert->st_edge.flow ) { - at->chem_bonds_valence = at->valence + vert->st_edge.flow; - nChanges ++; - } - /* adjast radical */ - switch ( vert->st_edge.cap - vert->st_edge.flow ) { - case 0: - cValue = 0; - break; - case 1: - cValue = RADICAL_DOUBLET; - break; - case 2: - cValue = RADICAL_TRIPLET; - break; - default: - return BNS_BOND_ERR; - } - if ( cValue != at->radical ) { - at->radical = cValue; - nChanges ++; - } - - return nChanges; -} - - - -/*****************************************************************************/ -int AddChangedAtHChargeBNS( inp_ATOM *at, int num_atoms, int nAtTypeTotals[], S_CHAR *mark ) -{ - int i, mask, num; - for ( i = 0, num = 0; i < num_atoms; i ++ ) { - if ( mark[i] ) { - mark[i] = 0; -#if ( FIX_NORM_BUG_ADD_ION_PAIR == 1 ) - /* add ignoring adjacent charges */ - at[i].at_type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, -2 ); -#else - at[i].at_type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); -#endif - num ++; - } - } - return num; -} - - - -/************************************************************************************/ -/* Eliminate neutral representation ambiguity: replace (+)--N==(-) with (+)==N--(-) */ -/* here (+) is positive charge group, (-) is negative charge group, N is N or P */ -/* this reduces possibility of ion pair -OH => -O(+) + H(+) creation instead of */ -/* removing H(+) from N or P */ -/* */ -/*==== Call this function after alt path was found and new flows have been set. ====*/ -/* */ -/************************************************************************************/ -int EliminatePlusMinusChargeAmbiguity( BN_STRUCT *pBNS, int num_atoms ) -{ - int pass, i, v0, v1, v2, ineigh1, /*ineigh0,*/ /*ineigh2,*/ vLast, n, delta, ret, err = 0; - BNS_EDGE *edge; - int nFound, k; - - for ( pass = pBNS->num_altp-1, ret = 0; 0 <= pass; pass -- ) { - - pBNS->alt_path = pBNS->altp[pass]; - v1 = ALTP_START_ATOM(pBNS->alt_path); - n = ALTP_PATH_LEN(pBNS->alt_path); - delta = ALTP_DELTA(pBNS->alt_path); - vLast = ALTP_END_ATOM(pBNS->alt_path); - v0 = v2 = NO_VERTEX; /* negative number */ - - for ( i = 0; i < n; i ++, delta = -delta, v0 = v1, v1 = v2 /*, ineigh0 = ineigh1*/ ) { - ineigh1 = ALTP_THIS_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v1->v2 neighbor */ - /*ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i);*/ /* v2->v1 neighbor */ - edge = pBNS->edge + pBNS->vert[v1].iedge[ineigh1]; - /* follow the BN Structure, not the inp_ATOM, to take care of swithching to - t-groups, c-groups or other fictitious edges/vertices - */ - v2 = edge->neighbor12 ^ v1; - if ( v1 < num_atoms && - ( v0 >= num_atoms && ( pBNS->vert[v0].type & BNS_VERT_TYPE_C_GROUP ) || - v2 >= num_atoms && ( pBNS->vert[v2].type & BNS_VERT_TYPE_C_GROUP ) ) ) { - int cgPos, cgNeg; - int neighPos = -1, neighNeg = -1; - BNS_EDGE *edgePos, *edgeNeg; - nFound = 0; - for ( k = pBNS->vert[v1].num_adj_edges-1; k >= 0 && (neighPos < 0 || neighNeg < 0); k -- ) { - BNS_EDGE *next_edge = pBNS->edge + pBNS->vert[v1].iedge[k]; - int v = next_edge->neighbor12 ^ v1; - if ( pBNS->vert[v].type & BNS_VERT_TYPE_C_GROUP ) { - if ( pBNS->vert[v].type & BNS_VERT_TYPE_C_NEGATIVE ) { - cgNeg = v; - neighNeg = k; - nFound ++; - } else { - cgPos = v; - neighPos = k; - nFound ++; - } - } - } - if ( 2 == nFound && neighPos >= 0 && neighNeg >= 0 ) { - /* both c-groups have been found */ - edgePos = pBNS->edge + pBNS->vert[v1].iedge[neighPos]; - edgeNeg = pBNS->edge + pBNS->vert[v1].iedge[neighNeg]; - if ( edgePos->flow < edgeNeg->flow ) { - /* ambiguity found; replace (+cg)--N==(-cg) with (+cg)==N--(-cg) */ - int dflow = edgeNeg->flow - edgePos->flow; - - edgePos->flow += dflow; - pBNS->vert[cgPos].st_edge.cap += dflow; - pBNS->vert[cgPos].st_edge.flow += dflow; - - edgeNeg->flow -= dflow; - pBNS->vert[cgNeg].st_edge.cap -= dflow; - pBNS->vert[cgNeg].st_edge.flow -= dflow; - ret ++; - } - } - } - } - - if ( v2 != vLast ) { - err = BNS_PROGRAM_ERR; - } - } - return err? err : ret; -} - - - -/*********************************************************************************/ -int AddOrRemoveExplOrImplH( int nDelta, inp_ATOM *at, int num_atoms, AT_NUMB at_no, T_GROUP_INFO *t_group_info ) -{ - int i, iso, tot_num_iso_H, - num_H, /* number of H before the removal, including explicit H */ - nNum2Remove, /*number of H to remove */ - nNumRemovedExplicitH, - nNumExplicit2Implicit; - S_CHAR num_iso_H[NUM_H_ISOTOPES]; - inp_ATOM *at_H; - - if ( !nDelta ) { - return 0; - } - /* add */ - if ( nDelta > 0 ) { - at[at_no].num_H += nDelta; - t_group_info->tni.nNumRemovedProtons --; - return nDelta; - } - /* remove */ - nNum2Remove = -nDelta; - nNumRemovedExplicitH = t_group_info->tni.nNumRemovedExplicitH; /* number of explicit H saved separately in - at[num_atoms+i], i=0..nNumRemovedExplicitH-1 */ - tot_num_iso_H = NUM_ISO_H(at,at_no); - num_H = at[at_no].num_H; - /* - tot_num_iso_H = NUM_ISO_H(at,at_no); - num_H = at[at_no].num_H; - nNumAtomExplicitH = 0; - nNumRemovedExplicitH = t_group_info->tni.nNumRemovedExplicitH; - tot_num_explicit_iso_H = 0; - */ - at_H = at + num_atoms; - memcpy( num_iso_H, at[at_no].num_iso_H, sizeof(num_iso_H)); - /* Remove all explicit H, otherwise a false stereo can occur. - Example: remove H(+) from the following substructure: - - H H - A / A / - >X==N(+) produces false stereogenic bond: >X==N - B \ B - H - - To avoid this effect all explicit H atoms must be removed - */ - nNumExplicit2Implicit = 0; - for ( i = 0; i < nNumRemovedExplicitH; ) { - if ( at_H[i].neighbor[0] == at_no ) { - int m, k, orig_no = at_H[i].orig_at_number; - nNumRemovedExplicitH --; - nNumExplicit2Implicit ++; - if ( nNumRemovedExplicitH > i ) { - inp_ATOM at_i = at_H[i]; - memmove( at_H+i, at_H+i+1, sizeof(at_H[0])*(nNumRemovedExplicitH-i) ); - at_H[nNumRemovedExplicitH] = at_i; /* save removed H (for debugging purposes?) */ - } - /* adjust 0D parities */ - if ( at[at_no].sb_parity[0] ) { - for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[at_no].sb_parity[m]; m ++ ) { - if ( at[at_no].sn_orig_at_num[m] == orig_no ) { -#ifdef _DEBUG - if ( at[at_no].sn_ord[m] >= 0 ) { - int stop = 1; /* sb maintenance error */ - } -#endif - if ( at[at_no].valence >= MIN_NUM_STEREO_BOND_NEIGH ) { - at[at_no].sn_ord[m] = k = (at[at_no].sb_ord[m]==0); - at[at_no].sn_orig_at_num[m] = at[(int)at[at_no].neighbor[k]].orig_at_number; - if ( ATOM_PARITY_WELL_DEF( at[at_no].sb_parity[m] ) ) { - at[at_no].sb_parity[m] = 3 - at[at_no].sb_parity[m]; - } - } else { - at[at_no].sn_ord[m] = -99; /* no sb neighbor exists anymore */ - at[at_no].sn_orig_at_num[m] = 0; - if ( ATOM_PARITY_WELL_DEF( at[at_no].sb_parity[m] ) ) { - int pnxt_atom, pinxt2cur, pinxt_sb_parity_ord; - if ( 0 < get_opposite_sb_atom( at, at_no, at[at_no].sb_ord[m], - &pnxt_atom, &pinxt2cur, &pinxt_sb_parity_ord ) ) { - at[at_no].sb_parity[m] = - at[pnxt_atom].sb_parity[pinxt_sb_parity_ord] = AB_PARITY_UNDF; - } -#ifdef _DEBUG - else { - int stop = 1; /* sb maintenance error */ - } -#endif - } - } - } - } - } - /* do not increment i here: we have shifted next at_H[] element - to the ith position and decremented nNumRemovedExplicitH */ - } else { - i ++; - } - } - - for ( iso = -1; iso < NUM_H_ISOTOPES && 0 < nNum2Remove; iso ++ ) { - /* each pass removes up to one H */ - if ( iso < 0 ) { - /* try to remove non-isotopic */ - while ( tot_num_iso_H < num_H && 0 < nNum2Remove ) { - /* non-isotopic H exists */ - num_H --; - t_group_info->tni.nNumRemovedProtons ++; - nNum2Remove --; - } - } else { - /* remove isotopic */ - while ( num_iso_H[iso] && num_H && 0 < nNum2Remove ) { - /* isotopic H exists */ - num_H --; - num_iso_H[iso] --; - t_group_info->tni.nNumRemovedProtonsIsotopic[iso] ++; - t_group_info->tni.nNumRemovedProtons ++; - nNum2Remove --; - } - } - } -#if ( bRELEASE_VERSION != 1 ) - if ( nNum2Remove ) { - int stop = 1; /* Program error */ - } -#endif - if ( nDelta + nNum2Remove < 0 ) { - at[at_no].num_H = num_H; - memcpy( at[at_no].num_iso_H, num_iso_H, sizeof(at[0].num_iso_H)); - t_group_info->tni.nNumRemovedExplicitH = nNumRemovedExplicitH; - } - return nDelta + nNum2Remove; -} - - - -/*********************************************************************************/ -int SubtractOrChangeAtHChargeBNS( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, - int nAtTypeTotals[], S_CHAR *mark, T_GROUP_INFO *t_group_info, int bSubtract ) -{ - int pass, i, v0, v1, v2, ineigh1, /*ineigh2,*/ vLast, n, delta, ret, err = 0; - BNS_EDGE *edge; - int nDeltaH, nDeltaCharge; - int mask, type; - - for ( pass = pBNS->num_altp-1, ret = 0; 0 <= pass; pass -- ) { - - pBNS->alt_path = pBNS->altp[pass]; - v1 = ALTP_START_ATOM(pBNS->alt_path); - n = ALTP_PATH_LEN(pBNS->alt_path); - delta = ALTP_DELTA(pBNS->alt_path); - vLast = ALTP_END_ATOM(pBNS->alt_path); - v0 = v2 = NO_VERTEX; - - for ( i = 0; i < n; i ++, delta = -delta, v0 = v1, v1 = v2 ) { - ineigh1 = ALTP_THIS_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v1->v2 neighbor */ - /*ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i);*/ /* v2->v1 neighbor */ - edge = pBNS->edge + pBNS->vert[v1].iedge[ineigh1]; - /* follow the BN Structure, not the inp_ATOM, to take care of swithching to - t-groups, c-groups or other fictitious edges/vertices - */ - v2 = edge->neighbor12 ^ v1; - if ( v1 < num_atoms && (v0 >= num_atoms || v2 >= num_atoms) ) { - nDeltaH = nDeltaCharge = 0; - if ( v0 >= num_atoms ) { - /* delta(v0-v1) = -delta(v1-v2) along the alternating path */ - if ( pBNS->vert[v0].type & BNS_VERT_TYPE_TGROUP ) { - nDeltaH -= delta; - } else - if ( pBNS->vert[v0].type & BNS_VERT_TYPE_C_GROUP ) { - nDeltaCharge += delta; - } - } - if ( v2 >= num_atoms ) { - if ( pBNS->vert[v2].type & BNS_VERT_TYPE_TGROUP ) { - nDeltaH += delta; - } else - if ( pBNS->vert[v2].type & BNS_VERT_TYPE_C_GROUP ) { - nDeltaCharge -= delta; - } - } - if ( nDeltaH || nDeltaCharge ) { - if ( bSubtract ) { - if ( !mark[v1] ) { - /* first time the atom has been encountered: subtract */ -#if ( FIX_NORM_BUG_ADD_ION_PAIR == 1 ) - type = GetAtomChargeType( at, v1, nAtTypeTotals, &mask, 2 ); -#else - type = GetAtomChargeType( at, v1, nAtTypeTotals, &mask, 1 ); -#endif - ret ++; /* number of changed atoms */ - mark[v1] ++; - } - } else { /* Change */ - at[v1].charge += nDeltaCharge; - if ( nDeltaH ) { - AddOrRemoveExplOrImplH( nDeltaH, at, num_atoms, (AT_NUMB)v1, t_group_info ); - } - ret ++; /* number of changed atoms */ - } - } - } - } - - if ( v2 != vLast ) { - err = BNS_PROGRAM_ERR; - } - } - return err? err : ret; -} - - - -/*********************************************************************************/ -int SetBondsFromBnStructFlow( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bChangeFlow0 ) -{ - int pass, i, v0, v1, v2, ineigh1, ineigh2, vLast, n, delta, ret, ret_val, err = 0; - BNS_EDGE *edge; - int bMovingRad = 0, bChangeFlowAdd; - int bChangeFlow = (bChangeFlow0 & ~BNS_EF_SET_NOSTEREO); - /* - bCheckMovingRad = (bChangeFlow & BNS_EF_ALTR_NS) == BNS_EF_ALTR_NS && - pBNS->tot_st_cap > pBNS->tot_st_flow; - */ - for ( pass = pBNS->num_altp-1, ret = 0; 0 <= pass; pass -- ) { - pBNS->alt_path = pBNS->altp[pass]; - v1 = ALTP_START_ATOM(pBNS->alt_path); - n = ALTP_PATH_LEN(pBNS->alt_path); - delta = ALTP_DELTA(pBNS->alt_path); - vLast = ALTP_END_ATOM(pBNS->alt_path); - if ( (bChangeFlow0 & BNS_EF_SET_NOSTEREO) && - (pBNS->vert[v1].st_edge.cap0 > pBNS->vert[v1].st_edge.flow0 || - pBNS->vert[vLast].st_edge.cap0 > pBNS->vert[vLast].st_edge.flow0 ) ) { - bMovingRad ++; - bChangeFlowAdd = BNS_EF_SET_NOSTEREO; - ret |= 2; - } else { - bChangeFlowAdd = 0; - } - /* start vertex */ - if ( (bChangeFlow & BNS_EF_CHNG_RSTR) == BNS_EF_CHNG_RSTR) { - /* restore s-v1 edge flow to the BNS this pass input value */ - ; /*pBNS->vert[v1].st_edge.flow -= delta;*/ - } else - if ( (bChangeFlow & BNS_EF_SAVE_ALL) == BNS_EF_SAVE_ALL ) { - if ( v1 < num_atoms ) { - /* will produce wrong result if called for v1 next time? */ - ret_val = SetAtomRadAndChemValFromVertexCapFlow( pBNS, at, v1 ); - if ( ret_val < 0 ) { - err = BNS_PROGRAM_ERR; - } else { - ret |= (ret_val > 0); - } - } - /*pBNS->vert[v1].st_edge.flow0 = pBNS->vert[v1].st_edge.flow;*/ - } - pBNS->vert[v1].st_edge.pass = 0; - - v0 = v2 = NO_VERTEX; - for ( i = 0; i < n; i ++, delta = -delta, v0 = v1, v1 = v2 ) { - ineigh1 = ALTP_THIS_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v1->v2 neighbor */ - ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v2->v1 neighbor */ - edge = pBNS->edge + pBNS->vert[v1].iedge[ineigh1]; - /* follow the BN Structure, not the inp_ATOM, to take care of swithching to - t-groups, c-groups or other fictitious edges/vertices - */ - - v2 = edge->neighbor12 ^ v1; - - /* change at->chem_bonds_valence 2004-03-08 */ - if ( (bChangeFlow & BNS_EF_CHNG_BONDS) && v1 < num_atoms ) { - if ( v0 >= num_atoms && v2 < num_atoms ) { - at[v1].chem_bonds_valence += delta; /* change in v1-v2 bond order */ - } else - if ( v0 < num_atoms && v2 >= num_atoms && v0 != NO_VERTEX ) { - at[v1].chem_bonds_valence -= delta; /* change in v0-v1 bond order */ - } - } - - if ( !edge->pass ) - continue; - - if ( v1 < num_atoms && ineigh1 < at[v1].valence && - v2 < num_atoms && ineigh2 < at[v2].valence ) { - if ( (bChangeFlow0 & BNS_EF_ALTR_NS )==BNS_EF_ALTR_NS && - (bChangeFlow0 & BNS_EF_SAVE_ALL)==BNS_EF_SAVE_ALL ) { - /* 2004-07-02 special mode: save new ring bonds and mark as non-stereo non-ring bonds */ - if ( at[v1].nRingSystem != at[v2].nRingSystem ) { - /* non-ring bond (bridge) */ - bChangeFlowAdd = BNS_EF_ALTR_NS; - } else { - /* ring bond */ - bChangeFlowAdd = 0; - } - } - /* change bonds on the first pass only: in this case all flow correspond to the BNS output */ - ret_val = SetAtomBondType( edge, &at[v1].bond_type[ineigh1], &at[v2].bond_type[ineigh2], delta, bChangeFlow | bChangeFlowAdd ); - if ( ret_val < 0 ) { - err = BNS_PROGRAM_ERR; - } else { - ret |= (ret_val > 0); - } - } - edge->pass = 0; - } - - if ( v2 != vLast ) { - err = BNS_PROGRAM_ERR; - } else - if ( (bChangeFlow & BNS_EF_CHNG_RSTR) == BNS_EF_CHNG_RSTR) { - /* restore v2-t edge flow to the BNS this pass input value */ - /* "+=" instead of "-=" explanation: delta must have same sign as at the last edge */ - ; /*pBNS->vert[v2].st_edge.flow += delta; */ - } else - if ( (bChangeFlow & BNS_EF_SAVE_ALL) == BNS_EF_SAVE_ALL ) { - if ( v2 < num_atoms ) { - ret_val = SetAtomRadAndChemValFromVertexCapFlow( pBNS, at, v2 ); - if ( ret_val < 0 ) { - err = BNS_PROGRAM_ERR; - } else { - ret |= (ret_val > 0); - } - } - /*pBNS->vert[v2].st_edge.flow0 = pBNS->vert[v2].st_edge.flow;*/ - } - pBNS->vert[v2].st_edge.pass = 0; - - } - return err? err : ret; -} - - - -/*********************************************************************************/ -int MarkAtomsAtTautGroups( BN_STRUCT *pBNS, int num_atoms, BN_AATG *pAATG, int nEnd1, int nEnd2 ) -{ - int pass, i, j, v1, v2, ineigh1, ineigh2, vLast, vFirst, n, delta, err = 0; - BNS_EDGE *edge; - S_CHAR cDelta[MAX_ALT_AATG_ARRAY_LEN]; - AT_NUMB nVertex[MAX_ALT_AATG_ARRAY_LEN]; - int nLenDelta = 0, last_i, nNumFound; - - for ( pass = pBNS->num_altp-1; 0 <= pass; pass -- ) { - pBNS->alt_path = pBNS->altp[pass]; - vFirst = - v1 = ALTP_START_ATOM(pBNS->alt_path); - n = ALTP_PATH_LEN(pBNS->alt_path); - delta = ALTP_DELTA(pBNS->alt_path); - vLast = ALTP_END_ATOM(pBNS->alt_path); - v2 = NO_VERTEX; - pAATG->nNumFound = 0; /* initialize */ - - if ( nEnd1 != vFirst && nEnd1 != vLast ) { - nEnd1 = -1; /* really not the end */ - } - if ( nEnd2 != vFirst && nEnd2 != vLast ) { - nEnd2 = -1; /* really not the end */ - } - - for ( i = 0; i < n; i ++, delta = -delta, v1 = v2 ) { - ineigh1 = ALTP_THIS_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v1->v2 neighbor */ - ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v2->v1 neighbor */ - edge = pBNS->edge + pBNS->vert[v1].iedge[ineigh1]; - /* follow the BN Structure, not the inp_ATOM, to take care of swithching to - t-groups, c-groups or other fictitious edges/vertices - */ - v2 = edge->neighbor12 ^ v1; - /* - if ( v1 < num_atoms && v2 < num_atoms ) { - continue; - } - */ - /* BNS increased edge flow by delta */ - if ( v1 >= num_atoms && - ((pBNS->vert[v1].type & BNS_VERT_TYPE_TGROUP)||(pBNS->vert[v1].type & BNS_VERT_TYPE_TEMP)) && - 0 <= v2 && v2 < num_atoms && (pBNS->vert[v2].type & BNS_VERT_TYPE_ATOM ) ) { - /* - if ( !(pAATG->nMarkedAtom[v2] & AATG_MARK_IN_PATH) ) { - pAATG->nMarkedAtom[v2] |= AATG_MARK_IN_PATH; - pAATG->nNumFound ++; - } - */ - /* BNS increased bond order in v1(t-group)-v2(atom) by delta: added delta attachments */ - if ( nLenDelta < MAX_ALT_AATG_ARRAY_LEN ) { - cDelta[nLenDelta] = delta; - nVertex[nLenDelta] = v2; - nLenDelta ++; - } - } else - if ( v2 >= num_atoms && - ((pBNS->vert[v2].type & BNS_VERT_TYPE_TGROUP)||(pBNS->vert[v2].type & BNS_VERT_TYPE_TEMP)) && - 0 <= v1 && v1 < num_atoms && (pBNS->vert[v1].type & BNS_VERT_TYPE_ATOM ) ) { - /* - if ( !(pAATG->nMarkedAtom[v1] & AATG_MARK_IN_PATH) ) { - pAATG->nMarkedAtom[v1] |= AATG_MARK_IN_PATH; - pAATG->nNumFound ++; - } - */ - /* BNS increased bond order in v1(atom)-v2(t-group) by delta: added delta attachments */ - if ( nLenDelta < MAX_ALT_AATG_ARRAY_LEN ) { - cDelta[nLenDelta] = delta; - nVertex[nLenDelta] = v1; - nLenDelta ++; - } - } else - /* special case when the testing 'dot' was placed on an atom (should be nEnd1 only) */ - if ( 0 <= v1 && v1 == nEnd1 || v1 == nEnd2 && 0 <= v2 && v2 < num_atoms ) { - if ( nLenDelta < MAX_ALT_AATG_ARRAY_LEN ) { - cDelta[nLenDelta] = -delta; - nVertex[nLenDelta] = v1; - nLenDelta ++; - } - } else - if ( 0 <= v2 && v2 == nEnd1 || v2 == nEnd2 && 0 <= v1 && v1 < num_atoms ) { - if ( nLenDelta < MAX_ALT_AATG_ARRAY_LEN ) { - cDelta[nLenDelta] = -delta; - nVertex[nLenDelta] = v2; - nLenDelta ++; - } - } - } - - if ( v2 != vLast ) { - err = BNS_PROGRAM_ERR; - } else { - last_i = -1; - nNumFound = 0; - /* first run */ - for ( i = 1, j = 0; i < nLenDelta; j = i ++ ) { - /* ignore sequences (-1,+1) and (+1,-1) in cDelta[] because they */ - /* describe ordinary aug. paths of moving a single attachment */ - /* we are looking for aug. paths describing movement of 2 or more */ - if ( cDelta[j] > 0 && cDelta[i] > 0 || - cDelta[j] < 0 && cDelta[i] < 0 ) { - if ( j == last_i ) { - /* three attachments moved */ - return 0; - } - v1 = nVertex[j]; - if ( !(pAATG->nMarkedAtom[v1] & AATG_MARK_IN_PATH) ) { - nNumFound ++; - } - v2 = nVertex[i]; - if ( !(pAATG->nMarkedAtom[v2] & AATG_MARK_IN_PATH) ) { - nNumFound ++; - } - last_i = i; - } - } - if ( !nNumFound ) { - return 0; - } - if ( nNumFound > 4 ) { - return 0; - } - if ( nNumFound < 4 ) { - return 0; - } - /* second run */ - for ( i = 1, j = 0; i < nLenDelta; j = i ++ ) { - /* ignore sequences (-1,+1) and (+1,-1) in cDelta[] because they */ - /* describe ordinary aug. paths of moving a single attachment */ - /* we are looking for aug. paths describing movement of 2 or more */ - if ( cDelta[j] > 0 && cDelta[i] > 0 || - cDelta[j] < 0 && cDelta[i] < 0 ) { - v1 = nVertex[i-1]; - if ( !(pAATG->nMarkedAtom[v1] & AATG_MARK_IN_PATH) ) { - pAATG->nMarkedAtom[v1] |= AATG_MARK_IN_PATH; - pAATG->nNumFound ++; - } - v2 = nVertex[i]; - if ( !(pAATG->nMarkedAtom[v2] & AATG_MARK_IN_PATH) ) { - pAATG->nMarkedAtom[v2] |= AATG_MARK_IN_PATH; - pAATG->nNumFound ++; - } - } - } - } - } - return err? err : pAATG->nNumFound; -} - - - -/*********************************************************************************/ -int RestoreBnStructFlow( BN_STRUCT *pBNS, int bChangeFlow ) -{ - int pass, i, v1, v2, ineigh1, ineigh2, vLast, n, delta, ret, err = 0; - BNS_EDGE *edge; - - for ( pass = pBNS->num_altp - 1, ret = 0; 0 <= pass; pass -- ) { - pBNS->alt_path = pBNS->altp[pass]; - v1 = ALTP_START_ATOM(pBNS->alt_path); - n = ALTP_PATH_LEN(pBNS->alt_path); - delta = ALTP_DELTA(pBNS->alt_path); - vLast = ALTP_END_ATOM(pBNS->alt_path); - v2 = NO_VERTEX; - /* starting vertex */ - if ( (bChangeFlow & BNS_EF_CHNG_RSTR) == BNS_EF_CHNG_RSTR) { - pBNS->vert[v1].st_edge.flow -= delta; /* restore s-v1 edge flow to the BNS input value */ - } else - if ( (bChangeFlow & BNS_EF_SAVE_ALL) == BNS_EF_SAVE_ALL ) { - pBNS->vert[v1].st_edge.flow0 = pBNS->vert[v1].st_edge.flow; - } - /* augmenting path edges */ - for ( i = 0; i < n; i ++, delta = -delta, v1 = v2 ) { - ineigh1 = ALTP_THIS_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v1->v2 neighbor */ - ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v2->v1 neighbor */ - edge = pBNS->edge + pBNS->vert[v1].iedge[ineigh1]; - v2 = edge->neighbor12 ^ v1; - RestoreEdgeFlow( edge, delta, bChangeFlow ); - edge->pass = 0; - } - /* ending vertex */ - if ( v2 != vLast ) { - err = BNS_PROGRAM_ERR; - } else - if ( (bChangeFlow & BNS_EF_CHNG_RSTR) == BNS_EF_CHNG_RSTR) { - /* restore v2-t edge flow to the original value */ - /* "+=" instead of "-=" explanation: delta must have same sign as at the last edge */ - pBNS->vert[v2].st_edge.flow += delta; - } else - if ( (bChangeFlow & BNS_EF_SAVE_ALL) == BNS_EF_SAVE_ALL ) { - pBNS->vert[v2].st_edge.flow0 = pBNS->vert[v2].st_edge.flow; - } - - } - return err? err : ret; -} - - - -/***************************************************************************************/ -int bNeedToTestTheFlow( int bond_type, int nTestFlow, int bTestForNonStereoBond ) -{ - int nBondType = ( BOND_TYPE_MASK & bond_type ); - int nBondAttrib = ( BOND_MARK_MASK & bond_type ); - - if ( bTestForNonStereoBond ) { - if ( nBondAttrib || nBondType == BOND_ALTERN || nBondType == BOND_ALT12NS ) { - switch( nTestFlow ) { - case 0: /* single: can be 1 (single)? */ - if ( nBondAttrib == BOND_MARK_ALT12NS|| - nBondAttrib == BOND_MARK_ALT123 || - nBondAttrib == BOND_MARK_ALT13 ) { - return 0; /* yes, already checked */ - } - break; - - case 1: /* double: can be 2 (double)? */ - if ( nBondAttrib == BOND_MARK_ALT12NS|| - nBondAttrib == BOND_MARK_ALT123 || - nBondAttrib == BOND_MARK_ALT23 ) { - return 0; /* yes, already checked */ - } - break; - case 2: /* triple: can be 3 (triple)? */ - if ( nBondAttrib == BOND_MARK_ALT13 || - nBondAttrib == BOND_MARK_ALT123 || - nBondAttrib == BOND_MARK_ALT23 ) { - return 0; /* yes, already checked */ - } - break; - } - } - } else { - if ( nBondAttrib || nBondType == BOND_ALTERN || nBondType == BOND_ALT12NS ) { - switch( nTestFlow ) { - case 0: /* single: can be 1 (single)? */ - if ( nBondAttrib == BOND_MARK_ALT12 || - nBondAttrib == BOND_MARK_ALT12NS|| - nBondAttrib == BOND_MARK_ALT123 || - nBondAttrib == BOND_MARK_ALT13 ) { - return 0; - } - break; - - case 1: /* double: can be 2 (double)? */ - if ( nBondAttrib == BOND_MARK_ALT12 || - nBondAttrib == BOND_MARK_ALT12NS|| - nBondAttrib == BOND_MARK_ALT123 || - nBondAttrib == BOND_MARK_ALT23 ) { - return 0; /* yes */ - } - break; - case 2: /* triple: can be 3 (triple)? */ - if ( nBondAttrib == BOND_MARK_ALT13 || - nBondAttrib == BOND_MARK_ALT123 || - nBondAttrib == BOND_MARK_ALT23 ) { - return 0; - } - break; - } - } - } - return 1; -} - - - -/***********************************************************************************/ -int nBondsValenceInpAt( const inp_ATOM *at, int *nNumAltBonds, int *nNumWrongBonds ) -{ - int j, bond_type, nBondsValence = 0, nAltBonds = 0, nNumWrong = 0; - for ( j = 0; j < at->valence; j ++ ) { - bond_type = at->bond_type[j] & BOND_TYPE_MASK; - switch( bond_type ) { - case 0: /* for structure from InChI reconstruction */ - case BOND_SINGLE: - case BOND_DOUBLE: - case BOND_TRIPLE: - nBondsValence += bond_type; - break; - case BOND_ALTERN: - nAltBonds ++; - break; - default: - nNumWrong ++; - } - } - switch ( nAltBonds ) { - case 0: - break; - case 1: - nBondsValence += 1; /* 1 or greater than 3 is wrong */ - nNumWrong ++; - break; - default: - nBondsValence += nAltBonds+1; - break; - } - if ( nNumAltBonds ) *nNumAltBonds = nAltBonds; - if ( nNumWrongBonds ) *nNumWrongBonds = nNumWrong; - return nBondsValence; -} - - - -/******************************************************************************/ -/* if radical or has aromatic bonds then augment to the lowest "multiplicity" */ -/******************************************************************************/ -int BnsAdjustFlowBondsRad( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at, int num_atoms ) -{ - int bError=0, nOrigDelta=0, ret, num_removed; - -#if( CHECK_AROMBOND2ALT == 1 ) - int *pcValMinusBondsVal = NULL; - int i, nValMinusBondsVal, nAltBonds, bIgnore, valen, is_rad, excess; - - /* find valence excess (it may only be due to aromatic bonds) */ - for ( i = 0; i < num_atoms; i ++ ) - { - valen = nBondsValenceInpAt( at+i, &nAltBonds, &bIgnore ); - nValMinusBondsVal = (int)at[i].chem_bonds_valence - valen; - bIgnore += (nAltBonds > 3); - if ( !bIgnore && nValMinusBondsVal > 0 ) - { - if ( !pcValMinusBondsVal && - !(pcValMinusBondsVal = (int *)inchi_calloc(num_atoms, sizeof(pcValMinusBondsVal[0])))) - { - bError = BNS_OUT_OF_RAM; - goto exit_function; - } - /* mark atoms that have extra unsatisfied valence due to aromatic bonds */ - is_rad = (at[i].radical == RADICAL_DOUBLET); - excess = nValMinusBondsVal+ is_rad; - pcValMinusBondsVal[i] = excess; - } - } -#endif /* CHECK_AROMBOND2ALT */ - - /* match bonds to valences */ - do - { - num_removed = 0; - ret = RunBalancedNetworkSearch( pBNS, pBD, BNS_EF_CHNG_FLOW ); - if ( IS_BNS_ERROR( ret ) ) - { - bError = ret; - } - else - { - nOrigDelta += ret; - num_removed = pBNS->num_altp; /* number of augmenting paths */ - if ( ret > 0 ) - { - /* save new bonds in at[] and flows in pBNS and at[] */ - ret = SetBondsFromBnStructFlow( pBNS, at, num_atoms, BNS_EF_SAVE_ALL ); /* must include 1: 5=(4|1) */ - if ( IS_BNS_ERROR( ret ) ) - { - bError = ret; - } - ret = RestoreBnStructFlow( pBNS, BNS_EF_SAVE_ALL ); /* must include 1: 5=(4|1) */ - if ( IS_BNS_ERROR( ret ) ) - { - bError = ret; - } - } - ReInitBnStructAltPaths( pBNS ); - } - } while ( num_removed && num_removed == pBNS->max_altp && !bError ); - -#if( CHECK_AROMBOND2ALT == 1 ) - /* check whether aromatic bonds have been replaces with alternating bonds */ - if ( !bError && pcValMinusBondsVal ) - { - for ( i = 0; i < num_atoms; i ++ ) - { - if ( !pcValMinusBondsVal[i] ) - continue; - valen = nBondsValenceInpAt( at+i, &nAltBonds, &bIgnore ); - nValMinusBondsVal = (int)at[i].chem_bonds_valence - valen; - is_rad = (at[i].radical == RADICAL_DOUBLET); - excess = nValMinusBondsVal + is_rad; - if ( bIgnore || - ( pcValMinusBondsVal[i] - excess ) != 1 ) - { - /* radical excess has not been reduced */ - bError = BNS_ALTBOND_ERR; - break; - } - } - } - -exit_function: - if ( pcValMinusBondsVal ) - inchi_free( pcValMinusBondsVal ); -#endif /* CHECK_AROMBOND2ALT */ - - return bError? bError : nOrigDelta; -} - - - -/***************************************************************************************/ -int BnsTestAndMarkAltBonds( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at, int num_atoms, BNS_FLOW_CHANGES *fcd, int bChangeFlow, int nBondTypeToTest ) -{ - int ret, iat, ineigh, neigh; - int nMinFlow, nMaxFlow, nTestFlow, nCurFlow; - int iedge, bSuccess, bError, nDots, nChanges, bTestForNonStereoBond; - /* Normalize bonds and find tautomeric groups */ - bError = 0; - nChanges = 0; - bTestForNonStereoBond = pBNS->tot_st_cap > pBNS->tot_st_flow; - for ( iat = 0; iat < num_atoms && !bError; iat ++ ) { - for ( ineigh = 0; ineigh < at[iat].valence && !bError; ineigh ++ ) { - neigh = at[iat].neighbor[ineigh]; - if ( neigh < iat ) - continue; /* we have already tested the bond */ - iedge = pBNS->vert[iat].iedge[ineigh]; - if ( IS_FORBIDDEN(pBNS->edge[iedge].forbidden, pBNS) ) - continue; - if ( nBondTypeToTest && (at[iat].bond_type[ineigh] & BOND_TYPE_MASK) != nBondTypeToTest ) - continue; - nMinFlow = nMinFlow2Check( pBNS, iedge ); - nMaxFlow = nMaxFlow2Check( pBNS, iedge ); - nCurFlow = nCurFlow2Check( pBNS, iedge ); - if ( nMinFlow == nMaxFlow ) { - if ( nMaxFlow && bTestForNonStereoBond ) { - nTestFlow = nMaxFlow - (int)(pBNS->tot_st_cap - pBNS->tot_st_flow); /* temporary use of nTestFlow */ - nMinFlow = inchi_max( 0, nTestFlow ); - } else { - continue; - } - } - for ( nTestFlow = nMinFlow; nTestFlow <= nMaxFlow && !bError; nTestFlow ++ ) { - if ( nTestFlow == nCurFlow ) - continue; - if ( !bNeedToTestTheFlow( at[iat].bond_type[ineigh], nTestFlow, bTestForNonStereoBond ) ) - continue; - bSuccess = 0; - nDots = bSetFlowToCheckOneBond( pBNS, iedge, nTestFlow, fcd ); - if ( IS_BNS_ERROR(nDots) ) { - if ( nDots == BNS_CANT_SET_BOND ) { - ret = bRestoreFlowAfterCheckOneBond( pBNS, fcd ); - if ( !IS_BNS_ERROR( ret ) ) { - continue; - } - } - bError = nDots; - } else - if ( nDots > 0 ) { - ret = RunBalancedNetworkSearch( pBNS, pBD, bChangeFlow ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - } else - if ( ret > 0 ) { - if ( 2*ret == nDots ) { - ret = bSetBondsAfterCheckOneBond( pBNS, fcd, nTestFlow, at, num_atoms, bChangeFlow ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - } else { - nChanges += (ret & 1); - ret = SetBondsFromBnStructFlow( pBNS, at, num_atoms, bChangeFlow ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - } else - if ( ret >= 0 ) { - nChanges += (ret & 1); - bSuccess = 1; - } else { - bError = ret; - } - } - } - /* typically 2*ret < nDots; 2*ret > nDots should not happen. Check later */ - ret = RestoreBnStructFlow( pBNS, bChangeFlow & BNS_EF_CHNG_RSTR); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - } - } - /* --- reinitialize to repeat the calculations --- */ - ReInitBnStructAltPaths( pBNS ); - } else - if ( nDots == 0 ) { - ret = bSetBondsAfterCheckOneBond( pBNS, fcd, nTestFlow, at, num_atoms, bChangeFlow ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - } else { - nChanges += (ret & 1); - } - } - ret = bRestoreFlowAfterCheckOneBond( pBNS, fcd ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - } - } - } - } - return bError? bError : nChanges; -} - - - -/************************************************************************/ -static void remove_alt_bond_marks(inp_ATOM *at, int num_atoms) -{ - int i, j, val; - for ( i = 0; i < num_atoms; i++ ) { - for ( val = at[i].valence, j = 0; j < val; j ++ ) { - at[i].bond_type[j] &= BOND_TYPE_MASK; - } - } -} - - - -/***************************************************************************************/ -int SetForbiddenEdges( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int forbidden_mask ) -{ - static U_CHAR el_number_O; - static U_CHAR el_number_C; - static U_CHAR el_number_N; - - int i, j, neigh, num_found; - BNS_IEDGE iedge; - /*S_CHAR edge_forbidden_mask = BNS_EDGE_FORBIDDEN_MASK;*/ - S_CHAR edge_forbidden_mask = forbidden_mask; - - pBNS->edge_forbidden_mask |= forbidden_mask; - - if ( !el_number_C ) { - el_number_O = (U_CHAR)get_periodic_table_number( "O" ); - el_number_C = (U_CHAR)get_periodic_table_number( "C" ); - el_number_N = (U_CHAR)get_periodic_table_number( "N" ); - } - - num_found = 0; - - for ( i = 0; i < num_atoms; i ++ ) { - /* acetyl */ - if ( at[i].el_number == el_number_C && 3 == at[i].valence && - 4 == at[i].chem_bonds_valence ) { - int num_O = 0; - int bond_to_O_val = 0; - int forbidden_bond_pos = -1; - int forbidden_bond_val = -1; - for ( j = 0; j < at[i].valence; j ++ ) { - neigh = at[i].neighbor[j]; - if ( at[neigh].el_number == el_number_O && - at[neigh].valence == 1 ) { - num_O ++; - bond_to_O_val += (at[i].bond_type[j] & BOND_TYPE_MASK); - } else { - forbidden_bond_pos = j; - forbidden_bond_val = (at[i].bond_type[j] & BOND_TYPE_MASK); - } - } - if ( 2 == num_O && 3 == bond_to_O_val && 1 == forbidden_bond_val ) { - iedge = pBNS->vert[i].iedge[forbidden_bond_pos]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_found ++; - } - } else - /* nitro */ - if ( at[i].el_number == el_number_N && 3 == at[i].valence && - (4 == at[i].chem_bonds_valence || 5 == at[i].chem_bonds_valence) ) { - int num_O = 0; - int bond_to_O_val = 0; - int forbidden_bond_pos = -1; - int forbidden_bond_val = -1; - for ( j = 0; j < at[i].valence; j ++ ) { - neigh = at[i].neighbor[j]; - if ( at[neigh].el_number == el_number_O && - at[neigh].valence == 1 ) { - num_O ++; - bond_to_O_val += (at[i].bond_type[j] & BOND_TYPE_MASK); - } else { - forbidden_bond_pos = j; - forbidden_bond_val = (at[i].bond_type[j] & BOND_TYPE_MASK); - - } - } - if ( 2 == num_O && (3 == bond_to_O_val || 4 == bond_to_O_val) && 1 == forbidden_bond_val ) { - iedge = pBNS->vert[i].iedge[forbidden_bond_pos]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_found ++; - } - } - } -#if ( REMOVE_ION_PAIRS_FIX_BONDS == 1 ) - num_found += fix_special_bonds( pBNS, at, num_atoms, edge_forbidden_mask ); -#endif -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) - num_found += TempFix_NH_NH_Bonds( pBNS, at, num_atoms ); -#endif - return num_found; -} - - - -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) -/************************************************************************/ -int TempFix_NH_NH_Bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms ) -{ - static U_CHAR el_number_N; - int i, j, neigh, num_found; - BNS_IEDGE iedge; - S_CHAR edge_forbidden_mask = BNS_EDGE_FORBIDDEN_TEMP; - if ( !el_number_N ) { - el_number_N = (U_CHAR)get_periodic_table_number( "N" ); - } - for ( i = 0, num_found = 0; i < num_atoms; i ++ ) { - /* -NH-NH- or -NH-NH3 */ - if ( at[i].el_number == el_number_N && at[i].valence < 3 && at[i].num_H && - 3 == at[i].chem_bonds_valence + at[i].num_H && - at[i].chem_bonds_valence == at[i].valence && - !at[i].charge && !at[i].radical ) { - for ( j = 0; j < at[i].valence; j ++ ) { - neigh = at[i].neighbor[j]; - if ( neigh < i && - at[neigh].el_number == el_number_N && at[neigh].valence < 3 && at[neigh].num_H && - 3 == at[neigh].chem_bonds_valence + at[neigh].num_H && - at[neigh].chem_bonds_valence == at[neigh].valence && - !at[neigh].charge && !at[neigh].radical) { - iedge = pBNS->vert[i].iedge[j]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_found ++; - } - } - } - } - return num_found; -} - - - -/************************************************************************/ -int CorrectFixing_NH_NH_Bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms ) -{ - static U_CHAR el_number_N; - int i, j, neigh, num_found; - BNS_IEDGE iedge; - S_CHAR edge_forbidden_mask = BNS_EDGE_FORBIDDEN_TEMP; - if ( !el_number_N ) { - el_number_N = (U_CHAR)get_periodic_table_number( "N" ); - } - for ( i = 0, num_found = 0; i < num_atoms; i ++ ) { - /* -NH-NH- or -NH-NH3 */ - if ( at[i].el_number == el_number_N && at[i].valence < 3 ) { - for ( j = 0; j < at[i].valence; j ++ ) { - neigh = at[i].neighbor[j]; - if ( neigh < i && - at[neigh].el_number == el_number_N && at[neigh].valence < 3 ) { - if ( BOND_TYPE_SINGLE != (at[i].bond_type[j] & BOND_TYPE_MASK) ) { - iedge = pBNS->vert[i].iedge[j]; - if ( pBNS->edge[iedge].forbidden & edge_forbidden_mask ) { - pBNS->edge[iedge].forbidden &= ~edge_forbidden_mask; - num_found ++; - } - } - } - } - } - } - return num_found; -} -#endif - - - - -/******************************************************/ -/* fixes bonds set by remove_ion_pairs() in strutil.c */ -/******************************************************/ -int fix_special_bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int forbidden_mask ) -{ - int num_changes = 0; - - /* 0 1 2 3 4 5 6 7 8 9 8 9 */ -#if ( FIX_REM_ION_PAIRS_Si_BUG == 1 ) - static const char el[] = "N;P;As;Sb;O;S;Se;Te;C;Si;"; /* 8 elements + C, Si */ -#else - static const char el[] = "N;P;As;Sb;O;S;Se;Te;C;Si"; /* 8 elements + C, Si */ -#endif - static char en[12]; /* same number: 8 elements */ - static int ne=0; /* will be 8 and 10 */ - -#define ELEM_N_FST 0 -#define ELEM_N_LEN 4 -#define ELEM_O_FST 4 -#define ELEM_O_LEN 4 -#define ELEM_S_FST (ELEM_O_FST+1) -#define ELEM_S_LEN (ELEM_O_LEN-1) -#define ELEM_C_FST 8 -#define ELEM_C_LEN 2 - -#define MAX_NEIGH 6 - - int i, k, n1, n2, n3, n4, i1, i2, i3, i4, bond_type; - inp_ATOM *a; - char elname[ATOM_EL_LEN]; - int j[3], m[3], num_O, k_O, num_N, num_OH, num_OM, num_X, num_other, k_N; - - BNS_IEDGE iedge; - /*S_CHAR edge_forbidden_mask = BNS_EDGE_FORBIDDEN_MASK;*/ - S_CHAR edge_forbidden_mask = forbidden_mask; - - pBNS->edge_forbidden_mask |= edge_forbidden_mask; - - if ( !ne ) { /* one time initialization */ - const char *b, *e; - int len; - for ( b = el; e = strchr( b, ';'); b = e+1 ) { - len = e-b; - memcpy( elname, b, len ); - elname[len] = '\0'; - en[ne++] = get_periodic_table_number( elname ); - } - en[ne] = '\0'; - en[ne+1] = '\0'; - } - for ( i = 0, a = at; i < num_atoms; i ++, a++ ) { - if ( !a->charge && !a->radical && - 2 <= a->chem_bonds_valence + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && - 0 == num_of_H( at, i ) && - 2 == nNoMetalBondsValence(at, i) + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && - NULL != memchr( en+ELEM_N_FST, at[i].el_number, ELEM_N_LEN) ) { - /* found N(V), no H */ - if ( 2 == nNoMetalNumBonds(at, i) ) { - /* #N= */ - /* fix bonds: double and triple: =N# so that bonds cannot be changed by the normalization */ -#if ( FIX_N_V_METAL_BONDS_GPF == 1 ) - if ( 0 > (i1 = nNoMetalNeighIndex( at, i )) || - 0 > (i2 = nNoMetalOtherNeighIndex( at, i, - n1 = a->neighbor[i1]/* non-metal neighbor #1 */ ) ) ) { - /*num_err ++; */ /* do not count would-be original InChI v.1 buffer overflow GPF */ - continue; /* v1 bug: 2 bonds to metal yield i1 < 0 and/or i2 < 0 => bounds violation */ - } -#else - i1 = nNoMetalNeighIndex( at, i ); - n1 = a->neighbor[i1]; /* non-metal neighbor #1 */ - i2 = nNoMetalOtherNeighIndex( at, i, n1 ); -#endif - n2 = a->neighbor[i2]; /* non-metal neighbor #2 */ - /* forbid all edges to non-metals */ - iedge = pBNS->vert[i].iedge[i1]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; /* fix bond to neighbor #1 */ - iedge = pBNS->vert[i].iedge[i2]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; /* fix bond to neighbor #1 */ - num_changes ++; /* added 11-15-2005 */ - /* i n3 */ - /* forbid single bond edge beyond the neighboring =N- as in #N=N- */ - if ( (at[i].bond_type[i1] & BOND_TYPE_MASK) == BOND_TYPE_DOUBLE ) { - i3 = i1; - n3 = n1; - } else { - i3 = i2; - n3 = n2; - } - if ( 0 == NUMH(at, n3) && 2 == nNoMetalNumBonds( at, n3 ) && - 3 == nNoMetalBondsValence( at, n3 ) && - NULL != memchr( en+ELEM_N_FST, at[n3].el_number, ELEM_N_LEN) && - 0 <= (k = nNoMetalOtherNeighIndex( at, n3, i )) ) { - /* found =N- ; forbid the edge*/ - iedge = pBNS->vert[n3].iedge[k]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_changes ++; - } - - } else - if ( 3 == nNoMetalNumBonds(at, i) && - /* | */ - /* found =N= */ - /* locate all non-metal neighbors */ - 0 <= (j[0] = nNoMetalNeighIndex( at, i )) && - 0 <= (j[1] = nNoMetalOtherNeighIndex( at, i, m[0] = a->neighbor[j[0]] )) && - 0 <= (j[2] = nNoMetalOtherNeighIndex2( at, i, m[0], m[1] = a->neighbor[j[1]] )) ) { - /* count specific neighbors: N(V)=N, N(V)-N N(V)=O, and N(V)-N */ - /* if there is a single neighbor connected by a double bond, namely - N(V)=N and/or N(V)=O, then fix the bond(s). - If N(V)=O was fixed then do not fix another bond */ - m[2] = a->neighbor[j[2]]; - num_O = num_N = 0; - for ( k= 0; k < 3; k ++ ) { - n1 = m[k]; - i1 = j[k]; - if ( NULL != memchr( en+ELEM_N_FST, at[n1].el_number, ELEM_N_LEN) ) { - k_N = k; - num_N ++; - } else - if ( NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) && - 1 == nNoMetalNumBonds( at, n1 ) ) { - k_O = k; - num_O ++; - } - } - num_other = 0; - if ( 1 == num_O && 0 == at[n1=m[k_O]].charge && 0 == at[n1].radical && - BOND_TYPE_DOUBLE == (at[i].bond_type[i1=j[k_O]] & BOND_TYPE_MASK) ) { - /* fix bond to neighbor =O */ - iedge = pBNS->vert[i].iedge[i1]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_changes ++; - num_other ++; /* indicator: double to a terminal O has been fixed */ - } - if ( !num_other && num_O <= 1 && - 1 == num_N && 0 == at[n1=m[k_N]].charge && 0 == at[n1].radical && - BOND_TYPE_DOUBLE == (at[i].bond_type[i1=j[k_N]] & BOND_TYPE_MASK ) ) { - /* fix bond to neighbor =N */ - iedge = pBNS->vert[i].iedge[i1]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_changes ++; - } - - } else - if ( 4 == nNoMetalNumBonds(at, i) ) { - /* | */ - /* found -N=N- */ - /* | */ - /* locate non-metal neighbor connected by a double bond; - * if it is =N- then fix the double bond and the single bond beyond the neighbor - */ - num_N = 0; - num_other = 0; - for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { - if ( BOND_TYPE_DOUBLE == (at[i].bond_type[i1] & BOND_TYPE_MASK) && - !is_el_a_metal(at[n1=(int)at[i].neighbor[i1]].el_number) && - NULL != memchr( en+ELEM_N_FST, at[n1].el_number, ELEM_N_LEN) ) { - num_N ++; - n2 = n1; - i2 = i1; - } - } - if ( 1 == num_N && 0 == NUMH(at, n2) && - 2 == nNoMetalNumBonds( at, n2 ) && - 3 == nNoMetalBondsValence(at, n2) && - 0 <= ( i3 = nNoMetalOtherNeighIndex( at, n2, i ) ) && - BOND_TYPE_SINGLE == (at[n2].bond_type[i3] & BOND_TYPE_MASK) ) { - /* fix the single bond beyond the N(V) neighbor N(V)=N- */ - iedge = pBNS->vert[n2].iedge[i3]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_changes ++; - /* fix the double bond */ - iedge = pBNS->vert[i].iedge[i2]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_changes ++; - } - } - } else - if ( !a->charge && !a->radical && - 2 <= a->chem_bonds_valence + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && - 0 == num_of_H( at, i ) && - 2 == nNoMetalBondsValence(at, i) + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && - NULL != memchr( en+ELEM_S_FST, a->el_number, ELEM_S_LEN) && - 3 == nNoMetalNumBonds( at, i ) ) { - /* found S(IV), no H, one double bond, total 3 bonds */ - /* OH - / - in O=S (X != O) fix single bond O-X (type 1) - \ - X - - X - / - in Z=S (X, Y != OH) fix double bond Z=S (type 2) - \ - Y - */ - num_N = 0; /* number of non-metal neighbors connected by a double bond */ - num_OH = 0; /* number of neighbors OH connected by a single bond */ - num_OM = 0; /* number of charged neighbors O connected by a single bond */ - num_O = 0; /* number of neighbors =O connected by a double bond */ - num_other = 0; - for ( i1 = 0; i1 < a->valence; i1 ++ ) { - n1=(int)a->neighbor[i1]; - if ( is_el_a_metal(at[n1].el_number) ) { - continue; - } - bond_type = (a->bond_type[i1] & BOND_TYPE_MASK); - if ( BOND_TYPE_DOUBLE == bond_type ) { - num_N ++; - n2 = n1; - i2 = i1; - if ( NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) ) { - num_O ++; - } - } else - if ( BOND_TYPE_SINGLE == bond_type && - 1 == nNoMetalNumBonds( at, n1 ) && - 1 == nNoMetalBondsValence(at, n1 ) && - NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) ) { - if ( 0 == at[n1].charge ) { - num_OH ++; - n3 = n1; - i3 = i1; - } else { - num_OM ++; - } - } else { - num_other ++; - n4 = n1; - i4 = i1; - } - } - if ( 1 == num_N && 1 == num_O && 1 == num_OH + num_OM ) { - if ( 1 == num_other ) { - /* type 1: fix the single bond S-X */ - iedge = pBNS->vert[i].iedge[i4]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_changes ++; - } - } else - if ( 1 == num_N && !num_OH && !num_OM ) { - int bFound = 0; /* flag */ - int bDoNotFixAnyBond = 0; /* flag */ - /* avoid case N=S-NH or N=S-N(-); N = N, P, As, Sb */ - if ( NULL != memchr( en+ELEM_N_FST, at[n2].el_number, ELEM_N_LEN) ) { - U_CHAR el_number = at[n2].el_number; - for ( i1 = 0; i1 < a->valence; i1 ++ ) { - n1=(int)a->neighbor[i1]; - bond_type = (a->bond_type[i1] & BOND_TYPE_MASK); - if ( BOND_TYPE_SINGLE == bond_type && - (NUMH(at, n1) || -1 == at[n1].charge) && - el_number == at[n1].el_number ) { - i3 = i1; - n3 = n1; - bFound ++; - } - } - } - /* exception: check if Z==X and they belong to the same ring system */ - for ( i1 = 0; i1 < a->valence; i1 ++ ) { - if ( i1 != i2 ) { - n1=(int)a->neighbor[i1]; - if ( at[n2].el_number == at[n1].el_number && - at[n2].nRingSystem == at[n1].nRingSystem ) { - bDoNotFixAnyBond ++; - } - } - } - - if ( bDoNotFixAnyBond ) { - ; /* do nothing */ - } else - if ( bFound ) { - if ( 1 == bFound && - 0 <= ( i4 = nNoMetalOtherNeighIndex2( at, i, n2, n3) ) ) { - /* fix bond i4 */ - iedge = pBNS->vert[i].iedge[i4]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_changes ++; - } - } else { - /* fix the double bond >S=X */ - iedge = pBNS->vert[i].iedge[i2]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_changes ++; - /* -- test later -- - if ( 2 == nNoMetalNumBonds( at, n2 ) && - 0 <= ( i3 = nNoMetalOtherNeighIndex( at, n2, i ) ) ) { - iedge = pBNS->vert[n2].iedge[i3]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_changes ++; - } - -------------------*/ - } - } - } else - if ( !a->charge && !a->radical && - 4 <= a->chem_bonds_valence + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && - 0 == num_of_H( at, i ) && - 4 == nNoMetalBondsValence(at, i) + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && - NULL != memchr( en+ELEM_S_FST, a->el_number, ELEM_S_LEN) && - 4 == nNoMetalNumBonds( at, i ) ) { - /* found S(VI), no H, two double bonds or one triple bond */ - /* O - || - in O=S--Y- (X, Y -- non-terminal) fix single bonds S-X, S-Y (type 1) - \ - X-- - - O - || - in O=S--O(-) (X -- non-terminal) fix single bond S-X (type 2) - \ - X-- - - O - || - in O=S--OH (X -- non-terminal) fix single bond S-X (type 3) - \ - X-- - - */ - int iN[4]; /* indexes of non-terminal neighbors connected by a single bond */ - num_N = 0; /* number of non-metal neighbors connected by a double bond */ - num_OH = 0; /* number of neighbors OH connected by a single bond */ - num_OM = 0; /* number of non-terminal neighbors connected by a single bond */ - num_O = 0; /* number of neighbors =O connected by a double bond */ - num_X = 0; /* number of terminal atom X != O connected by a single bond */ - num_other = 0; - for ( i1 = 0; i1 < a->valence; i1 ++ ) { - n1=(int)a->neighbor[i1]; - if ( is_el_a_metal(at[n1].el_number) ) { - continue; - } - bond_type = (a->bond_type[i1] & BOND_TYPE_MASK); - if ( BOND_TYPE_DOUBLE == bond_type ) { - num_N ++; - if ( (0 == at[n1].charge -#if ( S_VI_O_PLUS_METAL_FIX_BOND == 1 ) - || 1 == at[n1].charge && 2 == at[n1].valence -#endif - ) && 0 == at[n1].radical && - 0 == num_of_H( at, n1 ) && - NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) && - 1 == nNoMetalNumBonds( at, n1 ) ) { - - num_O ++; - } - } else - if ( BOND_TYPE_SINGLE == bond_type && - 1 == nNoMetalNumBonds( at, n1 ) && - NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) && - 1 >= num_of_H( at, n1 ) && - 1 == (( 0 == at[n1].charge) && 1==num_of_H( at, n1 )) - +((-1 == at[n1].charge) && 0==num_of_H( at, n1 )) ) { - - num_OH ++; /* -OH or -O(-) */ - - } else - if ( BOND_TYPE_SINGLE == bond_type && - 1 < nNoMetalNumBonds( at, n1 ) ) { - - iN[num_OM ++] = i1; /* non-terminal neighbor connected by a single bond */ - - } else - if ( BOND_TYPE_SINGLE == bond_type && - 1 == nNoMetalNumBonds( at, n1 ) ) { - - num_X ++; /* other terminal neighbor connected by a single bond */ - - } else { - num_other ++; - } - } - if ( num_N == num_O && 2 == num_O && 2 == num_OH + num_OM + num_X && 0 == num_other ) { - for ( i2 = 0; i2 < num_OM; i2 ++ ) { - i1 = iN[i2]; - /* fix bond i1 */ - iedge = pBNS->vert[i].iedge[i1]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_changes ++; - } - } - } else - if ( !a->charge && !a->radical && - 6 <= a->chem_bonds_valence + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && - 0 == num_of_H( at, i ) && - 6 == nNoMetalBondsValence(at, i) + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && - NULL != memchr( en+ELEM_S_FST, a->el_number, ELEM_S_LEN) && - 5 == nNoMetalNumBonds( at, i ) ) { - /* found S(VIII), no H, three double bonds or two triple bond */ - /* - - O - || - in O=S--Y-- (X, Y -- non-terminal) fix single bond S-X, S-Y (type 4) - //\ - O X-- - - note: this structure is a mistakenly drawn structure - - O O - || || - O=S--O--Y-- or O=S--Y-- - \ \ - X-- O--X-- - - - */ - int iN[5]; /* indexes of non-terminal neighbors connected by a single bond */ - num_N = 0; /* number of non-metal neighbors connected by a double bond */ - num_OH = 0; /* number of neighbors OH connected by a single bond */ - num_OM = 0; /* number of non-terminal neighbors connected by a single bond */ - num_O = 0; /* number of neighbors =O connected by a double bond */ - num_X = 0; /* number of terminal atom X != O connected by a single bond */ - num_other = 0; - for ( i1 = 0; i1 < a->valence; i1 ++ ) { - n1=(int)a->neighbor[i1]; - if ( is_el_a_metal(at[n1].el_number) ) { - continue; - } - bond_type = (a->bond_type[i1] & BOND_TYPE_MASK); - if ( BOND_TYPE_DOUBLE == bond_type ) { - num_N ++; - if ( (0 == at[n1].charge -#if ( S_VI_O_PLUS_METAL_FIX_BOND == 1 ) - || 1 == at[n1].charge && 2 == at[n1].valence -#endif - ) - && 0 == at[n1].radical && - 0 == num_of_H( at, n1 ) && - NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) && - 1 == nNoMetalNumBonds( at, n1 ) ) { - - num_O ++; - } - } else - if ( BOND_TYPE_SINGLE == bond_type && - 1 == nNoMetalNumBonds( at, n1 ) && - NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) && - 1 >= num_of_H( at, n1 ) && - 1 == (( 0 == at[n1].charge) && 1==num_of_H( at, n1 )) - +((-1 == at[n1].charge) && 0==num_of_H( at, n1 )) ) { - - num_OH ++; /* -OH or -O(-) */ - - } else - if ( BOND_TYPE_SINGLE == bond_type && - 1 < nNoMetalNumBonds( at, n1 ) ) { - - iN[num_OM ++] = i1; /* non-terminal neighbor connected by a single bond */ - - } else - if ( BOND_TYPE_SINGLE == bond_type && - 1 == nNoMetalNumBonds( at, n1 ) ) { - - num_X ++; /* other terminal neighbor connected by a single bond */ - - } else { - num_other ++; - } - } - if ( num_N == num_O && 3 == num_O && 2 == num_OH + num_OM + num_X && 0 == num_other ) { - for ( i2 = 0; i2 < num_OM; i2 ++ ) { - i1 = iN[i2]; - /* fix bond i1 */ - iedge = pBNS->vert[i].iedge[i1]; - pBNS->edge[iedge].forbidden |= edge_forbidden_mask; - num_changes ++; - } - } - } - } - return num_changes; -} - - - -#define ALL_NONMETAL_Z 0 - - - - -/***************************************************************************************/ -int is_Z_atom( U_CHAR el_number ) -{ - typedef enum tag_Z_elnumber { - el_C , - el_N , - el_P , - el_As, - el_Sb, - el_S , - el_Se, - el_Te, - el_Cl, - el_Br, - el_I , -#if ( ALL_NONMETAL_Z == 1 ) - el_B , - el_O , - el_Si, - el_Ge, - el_F , - el_At, -#endif - el_len - } Z_ELNUMBER; - static U_CHAR el_numb[el_len]; -/* - return is_el_a_metal( (int)el_number ); -*/ - if ( !el_numb[el_C] ) { - el_numb[el_C ] = (U_CHAR)get_periodic_table_number( "C" ); - el_numb[el_N ] = (U_CHAR)get_periodic_table_number( "N" ); - el_numb[el_P ] = (U_CHAR)get_periodic_table_number( "P" ); - el_numb[el_As] = (U_CHAR)get_periodic_table_number( "As" ); - el_numb[el_Sb] = (U_CHAR)get_periodic_table_number( "Sb" ); - el_numb[el_S ] = (U_CHAR)get_periodic_table_number( "S" ); - el_numb[el_Se] = (U_CHAR)get_periodic_table_number( "Se" ); - el_numb[el_Te] = (U_CHAR)get_periodic_table_number( "Te" ); - el_numb[el_Cl] = (U_CHAR)get_periodic_table_number( "Cl" ); - el_numb[el_Br] = (U_CHAR)get_periodic_table_number( "Br" ); - el_numb[el_I ] = (U_CHAR)get_periodic_table_number( "I" ); -#if ( ALL_NONMETAL_Z == 1 ) - el_numb[el_B ] = (U_CHAR)get_periodic_table_number( "B" ); - el_numb[el_O ] = (U_CHAR)get_periodic_table_number( "O" ); - el_numb[el_Si] = (U_CHAR)get_periodic_table_number( "Si" ); - el_numb[el_Ge] = (U_CHAR)get_periodic_table_number( "Ge" ); - el_numb[el_F ] = (U_CHAR)get_periodic_table_number( "F" ); - el_numb[el_At] = (U_CHAR)get_periodic_table_number( "At" ); -#endif - } - if ( memchr( el_numb, el_number, el_len ) ) { - return 1; - } - return 0; - -} - - - -/***************************************************************************************/ -int IsZOX( inp_ATOM *atom, int at_x, int ord ) -{ /* detect O==Z--X, O=O,S,Se,Te */ - static U_CHAR el_number_O = 0; - static U_CHAR el_number_S = 0; - static U_CHAR el_number_Se = 0; - static U_CHAR el_number_Te = 0; - inp_ATOM *at_Z = atom + atom[at_x].neighbor[ord]; - - int i, neigh, num_O; - - if ( !el_number_O ) { - el_number_O = (U_CHAR)get_periodic_table_number( "O" ); - el_number_S = (U_CHAR)get_periodic_table_number( "S" ); - el_number_Se = (U_CHAR)get_periodic_table_number( "Se" ); - el_number_Te = (U_CHAR)get_periodic_table_number( "Te" ); - } - for ( i = 0, num_O = 0; i < at_Z->valence; i ++ ) { - neigh = at_Z->neighbor[i]; - if ( neigh == at_x ) { - continue; - } - if ( atom[neigh].valence == 1 && - atom[neigh].chem_bonds_valence == 2 && - atom[neigh].charge == 0 && - atom[neigh].radical == 0 && - (atom[neigh].el_number == el_number_O || - atom[neigh].el_number == el_number_S || - atom[neigh].el_number == el_number_Se || - atom[neigh].el_number == el_number_Te ) ) { - num_O ++; - } - } - return num_O; -} - - - -/***************************************************************************************/ -int GetAtomChargeType( inp_ATOM *atom, int at_no, int nAtTypeTotals[], int *pMask, int bSubtract ) -{ - static U_CHAR el_number_C = 0; - static U_CHAR el_number_O = 0; - static U_CHAR el_number_S = 0; - static U_CHAR el_number_Se = 0; - static U_CHAR el_number_Te = 0; - static U_CHAR el_number_P = 0; - static U_CHAR el_number_N = 0; - static U_CHAR el_number_H = 0; - - static U_CHAR el_number_F = 0; - static U_CHAR el_number_Cl = 0; - static U_CHAR el_number_Br = 0; - static U_CHAR el_number_I = 0; - - inp_ATOM *at = atom + at_no; -#if ( FIX_NORM_BUG_ADD_ION_PAIR == 1 ) - int i, neigh, mask, bit, type, num_z, num_m, num_o, delta = bSubtract > 0 ? -1 : 1; /* 0 or -2 => add, 1 or 2 => subtract */ - int bNoAdjIon = (bSubtract==0 || bSubtract==1); -#else - int i, neigh, mask, bit, type, num_z, num_m, num_o, delta = bSubtract? -1 : 1; -#endif - int bUnsatNHasTerminalO = 0; - if ( !el_number_C ) { - el_number_C = (U_CHAR)get_periodic_table_number( "C" ); - el_number_O = (U_CHAR)get_periodic_table_number( "O" ); - el_number_S = (U_CHAR)get_periodic_table_number( "S" ); - el_number_Se = (U_CHAR)get_periodic_table_number( "Se" ); - el_number_Te = (U_CHAR)get_periodic_table_number( "Te" ); - el_number_P = (U_CHAR)get_periodic_table_number( "P" ); - el_number_N = (U_CHAR)get_periodic_table_number( "N" ); - el_number_H = (U_CHAR)get_periodic_table_number( "H" ); - el_number_F = (U_CHAR)get_periodic_table_number( "F" ); - el_number_Cl = (U_CHAR)get_periodic_table_number( "Cl" ); - el_number_Br = (U_CHAR)get_periodic_table_number( "Br" ); - el_number_I = (U_CHAR)get_periodic_table_number( "I" ); - } - - type = ATT_NONE; - mask = 0; - if ( at->radical && at->radical != RADICAL_SINGLET ) { - goto exit_function; - } - if ( is_el_a_metal( at->el_number ) ) { - goto exit_function; /* metal */ - } - if ( at->charge < -1 || at->charge > 1 ) { - goto exit_function; - } - if ( !at->valence && at->charge == 1 && !at->num_H && !at->radical && at->el_number == el_number_H ) { - /* a proton (#1) */ - type = ATT_PROTON; - mask = ATBIT_Proton; - goto count_mask_bits; - } - if ( !at->valence && at->charge == -1 && !at->num_H && !at->radical && - ( at->el_number == el_number_F || - at->el_number == el_number_Cl || - at->el_number == el_number_Br || - at->el_number == el_number_I ) - ) { - /* a halogen anion (#2) */ - type = ATT_HalAnion; - mask = ATBIT_HalAnion; - goto count_mask_bits; - } -#if ( HAL_ACID_H_XCHG == 1 ) - /* halogen/chalcogen acid */ - if ( !at->valence && at->charge == 0 && 1 == at->num_H && !at->radical && - ( at->el_number == el_number_F || - at->el_number == el_number_Cl || - at->el_number == el_number_Br || - at->el_number == el_number_I ) || - !at->valence && at->charge == 0 && 2 == at->num_H && !at->radical && - ( at->el_number == el_number_O || - at->el_number == el_number_S || - at->el_number == el_number_Se || - at->el_number == el_number_Te ) - ) { - /* a halogen/chalcogen acid (#3) */ - type = ATT_HalAcid; - mask = ATBIT_HalAcid; - goto count_mask_bits; - } -#endif - if ( detect_unusual_el_valence( at->el_number, at->charge, at->radical, - at->chem_bonds_valence, at->num_H, - at->valence ) ) { - goto exit_function; /* unusual valence state */ - } - /* check neighbors */ - for ( i = 0, num_z = 0, num_m = 0, num_o = 0; i < at->valence; i ++ ) { - neigh = at->neighbor[i]; -#if ( FIX_NORM_BUG_ADD_ION_PAIR == 1 ) - if ( atom[neigh].charge < -1 || atom[neigh].charge > 1 ) { - goto exit_function; /* neighboring charge */ - } - if ( atom[neigh].charge && at->charge ) { - if ( bNoAdjIon ) { - goto exit_function; /* neighboring charge */ - } - type = ATT_NONE; - mask = 0; - goto count_mask_bits; - } -#else - if ( atom[neigh].charge < -1 || atom[neigh].charge > 1 || atom[neigh].charge && at->charge ) { - goto exit_function; /* neighboring charge */ - } -#endif - if ( detect_unusual_el_valence( atom[neigh].el_number, atom[neigh].charge, atom[neigh].radical, - atom[neigh].chem_bonds_valence, atom[neigh].num_H, - atom[neigh].valence ) ) { - goto exit_function; /* neighbor in unusual valence state */ - } - if ( is_Z_atom( atom[neigh].el_number ) ) { - num_z ++; - } - if ( is_el_a_metal( atom[neigh].el_number ) ) { - num_m ++; - } - num_o += (atom[neigh].el_number == el_number_O); - if ( at->el_number == el_number_N && at->valence == 2 && !at->charge && - /*at->valence < at->chem_bonds_valence &&*/ - atom[neigh].valence == 1 && atom[neigh].chem_bonds_valence == 2 && - (atom[neigh].el_number == el_number_O || - atom[neigh].el_number == el_number_S || - atom[neigh].el_number == el_number_Se || - atom[neigh].el_number == el_number_Te )) { - bUnsatNHasTerminalO ++; - } - } - /* O, S, Se, Te */ - if ( at->el_number == el_number_O || - at->el_number == el_number_S || - at->el_number == el_number_Se || - at->el_number == el_number_Te ) { - if ( at->charge == 1 ) { - if ( at->num_H ) { /* #4 */ - type = ATT_O_PLUS; - mask |= ATBIT_OH_Plus; - } else { /* #5 */ - type = ATT_O_PLUS; - mask |= ATBIT_O_Plus; - } - } else - if ( at->valence > 1 ) { - goto exit_function; /* not a terminal atom #C1 */ - } else - if ( at->valence && !(num_z || num_o) ) { - if ( num_m == at->valence ) { - goto exit_function; /* #C2 */ - } - goto count_mask_bits; /* #C3 count charges, no donor or acceptor found */ - } else - /* here at->neigh[0] is one of: O, or Z={C, N, P, As, Sb, S, Se, Te, Cl, Br, I} */ - if ( at->valence ) { - neigh = at->neighbor[0]; /* Z or O only */ - if ( !atom[neigh].charge && atom[neigh].el_number == el_number_C && - atom[neigh].chem_bonds_valence > atom[neigh].valence ) { - /* =C-OH, #C-OH, =C-O(-), #C-O(-), -C=O, =C=O; O = O, S, Se, Te */ - type = ATT_ACIDIC_CO; - if ( at->num_H == 1 ) { - mask |= (ATBIT_COH); /* #6: =C-OH, #C-OH; O=O,S,Se,Te */ - /*nAtTypeTotals[ATTOT_NUM_COH] ++;*/ - } else - if ( at->charge == -1 ) { - mask |= (ATBIT_CO_Minus); /* #7: =C-O(-), #C-O(-); O=O,S,Se,Te */ - /*nAtTypeTotals[ATTOT_NUM_CO_Minus] ++;*/ - } else - if ( !at->num_H && !at->charge ) { - mask |= (ATBIT_CO); /* #8 -C=O, =C=O; O=O,S,Se,Te */ - /*nAtTypeTotals[ATTOT_NUM_CO] ++;*/ - } else { - mask |= (ATBIT_Errors); - /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ - } - } else - if ( !atom[neigh].charge && - (atom[neigh].el_number == el_number_O || - atom[neigh].el_number == el_number_S || - atom[neigh].el_number == el_number_Se || - atom[neigh].el_number == el_number_Te ) && - atom[neigh].chem_bonds_valence == atom[neigh].valence ) { - /* -O-OH, -O-O(-); O = O, S, Se, Te */ - type = ATT_OO; - if ( at->num_H == 1 ) { - mask |= (ATBIT_OOH); /* #9 -O-OH */ - /*nAtTypeTotals[ATTOT_NUM_OOH] ++;*/ - } else - if ( at->charge == -1 ) { - mask |= (ATBIT_OO_Minus); /* #10 -O-O(-) */ - /*nAtTypeTotals[ATTOT_NUM_OO_Minus] ++;*/ - } else { - mask |= (ATBIT_Errors); - /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ - } - } else - if ( !atom[neigh].charge && - atom[neigh].chem_bonds_valence == atom[neigh].valence && - atom[neigh].el_number == el_number_C && - at->el_number != el_number_O ) { - /* >C-S(-), >C-SH; S = S, Se, Te */ - type = ATT_ACIDIC_S; - if ( at->num_H == 1 ) { - mask |= (ATBIT_CSH); /* #11: >C-SH, >CH-SH, -CH2-SH; S = S, Se, Te */ - /*nAtTypeTotals[ATTOT_NUM_CSH] ++;*/ - } else - if ( at->charge == -1 ) { - mask |= (ATBIT_CS_Minus); /* #12: >C-S(-), >CH-S(-), -CH2-S(-); S = S, Se, Te */ - /*nAtTypeTotals[ATTOT_NUM_CS_Minus] ++;*/ - } else { - mask |= (ATBIT_Errors); - /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ - } - } else - if ( atom[neigh].el_number == el_number_N && - atom[neigh].valence == 2 && (!atom[neigh].num_H || atom[neigh].num_H == 1 && atom[neigh].charge == 1) ) { - /* N or N(-) or NH(+) neighbor */ - type = ATT_NO; /* single bond only */ - if ( at->num_H == 1 ) { - mask |= (ATBIT_NOH); /* #13: =N-OH, =NH(+)-OH, #N(+)-OH, -N(-)-OH; O = O, S, Se, Te */ - /*nAtTypeTotals[ATTOT_NUM_NOH] ++;*/ - } else - if ( at->charge == -1 ) { - mask |= (ATBIT_NO_Minus); /* #14: =N-O(-); O = O, S, Se, Te */ - /*nAtTypeTotals[ATTOT_NUM_NO_Minus] ++;*/ - } else - if ( atom[neigh].charge == 1 || atom[neigh].charge == 0 ) { - mask |= (ATBIT_NO); /* #15: =N(+)=O, -NH(+)=O -N=O */ - /*nAtTypeTotals[ATTOT_NUM_NO] ++;*/ - } else { - mask |= (ATBIT_Errors); - /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ - } - } else - if ( atom[neigh].el_number == el_number_N ) { - type = ATT_N_O; /* #16: single bond only */ - if ( at->num_H == 1 ) { - mask |= (ATBIT_N_OH); /* #16: -NH-OH, >N-OH or >N(+)charge == -1 ) { - mask |= (ATBIT_N_O_Minus); /* #17: -NH-O(-), >N-O(-); O = O, S, Se, Te */ - /*nAtTypeTotals[ATTOT_NUM_NO_Minus] ++;*/ - } else - if ( atom[neigh].charge == 1 ) { - mask |= (ATBIT_N_O); /* #18: >N(+)=O */ - /*nAtTypeTotals[ATTOT_NUM_NO] ++;*/ - } else { - mask |= (ATBIT_Errors); - /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ - } - } else - if ( atom[neigh].el_number != el_number_C && atom[neigh].el_number != el_number_O && - !is_el_a_metal( atom[neigh].el_number ) && - atom[neigh].chem_bonds_valence > atom[neigh].valence ) { - /* =Z-OH, #Z-OH, =Z-O(-), #Z-O(-), -Z=O, =Z=O; - =Z(+)-OH, #Z(+)-OH, =Z-O(-), #Z-O(-), -Z(+)=O, =Z(+)=O; O = O, S, Se, Te */ - /* neigh = Z\{N,C} = P, As, Sb, S, Se, Te, Cl, Br, I */ - if ( at->chem_bonds_valence == 1 && IsZOX( atom, at_no, 0 ) ) { - type = ATT_ZOO; - if ( at->num_H == 1 ) { - mask |= (ATBIT_ZOOH); /* 18: O=Z-OH; O=O,S,Se,Te; Z may have charge */ - /*nAtTypeTotals[ATTOT_NUM_ZOOH] ++;*/ - } else - if ( at->charge == -1 ) { - mask |= (ATBIT_ZOO_Minus); /* 19: O=Z-O(-); O = O, S, Se, Te */ - /*nAtTypeTotals[ATTOT_NUM_ZOO_Minus] ++;*/ - } else { - mask |= (ATBIT_Errors); - /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ - } - } else { - type = ATT_OTHER_ZO; - if ( at->num_H == 1 ) { - mask |= (ATBIT_ZOH); /* 20: =Z-OH, #Z-OH; O=O,S,Se,Te; Z may have charge */ - /*nAtTypeTotals[ATTOT_NUM_ZOH] ++;*/ - } else - if ( at->charge == -1 ) { - mask |= (ATBIT_ZO_Minus); /* 21: =Z-O(-), #Z-O(-); O = O, S, Se, Te */ - /*nAtTypeTotals[ATTOT_NUM_ZO_Minus] ++;*/ - } else - if ( at->num_H == 0 ) { - mask |= (ATBIT_ZO); /* 22: -Z=O, =Z=O; O=O,S,Se,Te; Z may have charge */ - /*nAtTypeTotals[ATTOT_NUM_ZO] ++;*/ - } else { - mask |= (ATBIT_Errors); - /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ - } - } - } else - if ( at->charge == -1 && !is_el_a_metal( atom[neigh].el_number ) ) { - /* >Z-O(-); O=O,S,Se,Te */ - type = ATT_OTHER_NEG_O; - mask |= (ATBIT_O_Minus); /* 23: -Z-O(-); O=O,S,Se,Te */ - /*nAtTypeTotals[ATTOT_NUM_ZO_Minus] ++;*/ - } - } else - if ( at->charge == -1 && at->num_H == 1 ) { - type = ATT_OH_MINUS; - mask |= (ATBIT_O_Minus); /* 25: HO(-); O=O,S,Se,Te */ - } - } else - /* P, N, neutral valence = 3 (not 5) */ - if ( (at->el_number == el_number_N || - at->el_number == el_number_P) && - 0 <= at->valence && at->valence <= 3 && - at->chem_bonds_valence + at->num_H == 3 + at->charge ) { - if ( at->valence && !(num_z /*|| num_o == at->valence*/) ) { - if ( num_m == at->valence ) { - goto exit_function; - } - goto count_mask_bits; /* N(III), N(-)(II), N(+)(IV) and same P that have only oxygen neighbors are ignored here */ - } - type = (at->el_number == el_number_N)? ATT_ATOM_N : ATT_ATOM_P; - switch ( at->charge ) { - case -1: - if (at->el_number == el_number_N) { - mask |= (ATBIT_N_Minus); /* 26: -NH(-), =N(-), >N(-) */ - - if ( at->num_H ) - mask |= (ATBIT_NP_H); /* 27: -NH(-) */ -#if ( FIX_NP_MINUS_BUG == 1 ) - else - if ( at->valence == 1 && at->chem_bonds_valence >= 2 && (at->bond_type[0] & BOND_MARK_ALL) ) - type |= ATT_NP_MINUS_V23; /* =N(-) created by normalization 2010-03-11 DT */ -#endif - - } - /*nAtTypeTotals[ATTOT_NUM_N_Minus] += (at->el_number == el_number_N);*/ - break; - case 0: - if ( at->num_H ) { - mask |= (ATBIT_NP_H); /* 28: -NH2, =NH, >NH */ - /*nAtTypeTotals[ATTOT_NUM_NP_H] ++;*/ - } else { - if ( bUnsatNHasTerminalO == 1 ) { - mask |= (ATBIT_ON); /* 29: -N=O,-N=OH(+) only, not =N-OH */ - } else { - mask |= (ATBIT_NP); /* 30: -P=O,-P=OH(+), >N- =N- (incl. =N-OH) , #N */ - /*nAtTypeTotals[ATTOT_NUM_NP] ++;*/ - } - } - break; /* ignore neutral N or P */ - case 1: - if ( at->num_H ) { - mask |= (ATBIT_NP_Proton); /* 31: NH4(+), -NH3(+), =NH2(+), >NH2(+), =NH(+)-, >NH(+)-, #NH(+) */ - /*nAtTypeTotals[ATTOT_NUM_NP_Proton] ++;*/ - } else - if ( at->chem_bonds_valence > at->valence ) { - mask |= (ATBIT_NP_Plus); /* =N(+)=, #N(+)-, =N(+)< */ - /*nAtTypeTotals[ATTOT_NUM_NP_Plus] ++;*/ - } else { - type = 0; /* 32: ignore onium cations >N(+)< */ - } - break; - default: - mask |= (1 << ATTOT_NUM_Errors); - /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ - break; - } - } -count_mask_bits: - if ( nAtTypeTotals ) { - if ( mask && !(mask & (ATBIT_Errors)) ) { - for ( i = 0, bit = 1; i < ATTOT_ARRAY_LEN; i ++, bit <<= 1 ) { - if ( bit & mask ) { - nAtTypeTotals[i] += delta; - } - } - } - /* count charges */ - if ( at->charge ) { - nAtTypeTotals[ATTOT_TOT_CHARGE] += delta * at->charge; - nAtTypeTotals[ATTOT_NUM_CHARGES] += delta; - } - } - if ( pMask ) { - *pMask = mask; - } -exit_function: - if ( mask & (ATBIT_Errors) ) { - type = 0; - if ( nAtTypeTotals ) { - nAtTypeTotals[ATTOT_NUM_Errors] += 1; - } - } - return type; -} - - - -/***************************************************************************************/ -int SimpleRemoveHplusNPO( inp_ATOM *at, int num_atoms, int nAtTypeTotals[], T_GROUP_INFO *t_group_info ) -{ - int i, mask, type, num_removed; - for ( i = 0, num_removed = 0; i < num_atoms; i ++ ) { - if ( (PR_SIMPLE_TYP & (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) && - (PR_SIMPLE_MSK & mask ) ) { -#if ( bRELEASE_VERSION == 0 ) - if ( at[i].charge != 1 || at[i].num_H == 0 ) { - return -1; /* program error */ - } -#endif - type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ - at[i].charge = 0; - AddOrRemoveExplOrImplH( -1, at, num_atoms, (AT_NUMB)i, t_group_info ); - /*at[i].num_H --;*/ - num_removed ++; -#if ( FIX_NORM_BUG_ADD_ION_PAIR == 1 ) - type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ -#else - type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* bug: subtract instead of add */ -#endif - /* - if ( nAtTypeTotals ) { - nAtTypeTotals[ATTOT_NUM_NP_Proton] --; - if ( at[i].num_H ) { - nAtTypeTotals[ATTOT_NUM_NP_H] ++; - } else { - nAtTypeTotals[ATTOT_NUM_NP] ++; - } - nAtTypeTotals[ATTOT_TOT_CHARGE] --; - nAtTypeTotals[ATTOT_NUM_CHARGES] --; - } - */ - } - } - return num_removed; -} - - - -/***************************************************************************************/ -int bIsAtomTypeHard( inp_ATOM *at, int endpoint, int nType, int nMask, int nCharge ) -{ - int mask; - if ( (nType & GetAtomChargeType( at, endpoint, NULL, &mask, 0 )) && (mask & nMask) -#if ( OPPOSITE_CHARGE_IN_CGROUP == 0 ) - && ( at[endpoint].charge == nCharge || !at[endpoint].charge ) -#endif - ) { - return 1; - } - return 0; - -} - - - -/***************************************************************************************/ -int bIsHDonorAccAtomType( inp_ATOM *at, int endpoint, int *cSubType ) -{ - if ( bIsAtomTypeHard( at, endpoint, PR_HARD_TYP_H, PR_HARD_MSK_H, 0 ) ) { - /* obtain donor/acceptor info */ - int neutral_valence = at[endpoint].chem_bonds_valence + at[endpoint].num_H - at[endpoint].charge; - if ( neutral_valence != 2 /* O, S, Se, Te */ && - neutral_valence != 3 /* N, P */ ) { - return -1; /* wrong endpoint neutral valence */ - } else { - int edge_flow = at[endpoint].num_H; - int num_bonds = at[endpoint].valence; - int edge_cap = neutral_valence - num_bonds; /* does not allow to reduce -NH3(+) to #N or -OH(+)- to -O- */ - edge_flow = inchi_min( edge_flow, edge_cap); - /* what this means: */ - if ( edge_cap ) { - if ( edge_cap > edge_flow ) - *cSubType |= SALT_ACCEPTOR; - if ( edge_flow ) - *cSubType |= SALT_DONOR_H; - return 4; - } - } - } - return -1; -} - - - -/***************************************************************************************/ -int bIsNegAtomType( inp_ATOM *at, int endpoint, int *cSubType ) -{ - int sub_type = 0; - if ( bIsAtomTypeHard( at, endpoint, PR_HARD_TYP_NEG, PR_HARD_MSK_NEG, -1 ) ) { - /* obtain donor/acceptor info */ - int neutral_valence = at[endpoint].chem_bonds_valence + at[endpoint].num_H - at[endpoint].charge; - if ( neutral_valence != 2 /* O, S, Se, Te */ && - neutral_valence != 3 /* N, P */ ) { - return -1; /* wrong endpoint neutral valence */ - } else { - int edge_flow = (at[endpoint].charge == -1); - int num_bonds = at[endpoint].valence; - int edge_cap = neutral_valence - num_bonds - at[endpoint].num_H; /* does not allow to reduce -NH3(+) to #N or -OH(+)- to -O- */ - edge_flow = inchi_min( edge_flow, edge_cap); - /* what this means: */ - if ( edge_cap ) { - if ( edge_cap > edge_flow ) - sub_type |= SALT_ACCEPTOR; - if ( edge_flow ) { - sub_type |= SALT_DONOR_Neg; - } - if ( sub_type ) { - *cSubType |= sub_type; - return 4; - } - } - } - } - return -1; -} - - - -/*****************************************************************************/ -int bIsHardRemHCandidate( inp_ATOM *at, int i, int *cSubType ) -{ - int ret1, ret2, ret; - int sub_type = 0; - ret1 = bIsHDonorAccAtomType( at, i, &sub_type ); - ret2 = bIsNegAtomType( at, i, &sub_type ); - ret = inchi_max(ret1, ret2); - if ( ret > 0 && sub_type ) { - *cSubType |= sub_type; - return ret; - } - return -1; -} - - - -/***************************************************************************************/ -int CreateCGroupInBnStruct( inp_ATOM *at, int num_atoms, - BN_STRUCT *pBNS, int nType, int nMask, int nCharge ) -{ - int k, c_point, cg, centerpoint, fictpoint, type, ret = 0; - int num_cg = 1; - int num_edges = pBNS->num_edges; - int num_vertices = pBNS->num_vertices; /* new c-group bns-ID */ - BNS_VERTEX *vert_ficpoint, *ver_ficpont_prev; /* fictitious vertex describing charge c-group */ - BNS_VERTEX *vertex_cpoint; - BNS_EDGE *edge; /* edge between that vertex and the tautomeric c_point */ - int mask, num_CPoints; - - /* Debug: check overflow */ - if ( num_vertices + num_cg >= pBNS->max_vertices ) { - return BNS_VERT_EDGE_OVFL; - } - /* count new c-group edges */ - for ( c_point = 0, num_CPoints = 0; c_point < num_atoms; c_point ++ ) { - if ( (nType & GetAtomChargeType( at, c_point, NULL, &mask, 0 )) && (mask & nMask) -#if ( OPPOSITE_CHARGE_IN_CGROUP == 0 ) - && ( at[c_point].charge == nCharge || !at[c_point].charge ) -#endif - ) { - num_CPoints ++; - } - } - if ( !num_CPoints ) { - return 0; - } - - - - /* clear the new vertex */ - memset( pBNS->vert+num_vertices, 0, 1*sizeof(pBNS->vert[0]) ); - /* *old* Make sure the last t-group has the largest t-group ID: - this is necessary to correctly add new edges and vertices for testing augmenting paths - */ - /**************************************/ - /* initialize new fictitious vertex */ - /* representing c-point group */ - /**************************************/ - ver_ficpont_prev = pBNS->vert+num_vertices - 1; - - for ( cg = 0; cg < num_cg; cg ++, ver_ficpont_prev = vert_ficpoint ) { - /* - vert_ficpoint-1 is the last vertex; - vert_ficpoint is the being added vertex - Note: nGroupNumber are not contiguous - */ - vert_ficpoint = pBNS->vert+num_vertices + cg; - vert_ficpoint->iedge = ver_ficpont_prev->iedge + ver_ficpont_prev->max_adj_edges; - vert_ficpoint->max_adj_edges = num_CPoints+BNS_ADD_EDGES; - vert_ficpoint->num_adj_edges = 0; - vert_ficpoint->st_edge.flow = vert_ficpoint->st_edge.flow0 = 0; - vert_ficpoint->st_edge.cap = vert_ficpoint->st_edge.cap0 = 0; - vert_ficpoint->type = BNS_VERT_TYPE_C_GROUP | ((nCharge<0)?BNS_VERT_TYPE_C_NEGATIVE:0); - } - - /************************************************/ - /* connect c-points to the fictitious vertices */ - /* representing c-point groups; set caps, flows */ - /************************************************/ - cg = 1; - for ( c_point = 0; c_point < num_atoms; c_point ++ ) { - if ( (nType & (type=GetAtomChargeType( at, c_point, NULL, &mask, 0 ))) && (mask & nMask) -#if ( OPPOSITE_CHARGE_IN_CGROUP == 0 ) - && ( at[c_point].charge == nCharge || !at[c_point].charge) -#endif - ); - else - continue; - fictpoint = cg + num_vertices - 1; /* c-group vertex index */ - vert_ficpoint = pBNS->vert + fictpoint; /* c-group vertex */ - vertex_cpoint = pBNS->vert + c_point; /* c_point vertex */ - /* Debug: check overflow */ - if ( fictpoint >= pBNS->max_vertices || - num_edges >= pBNS->max_edges || - vert_ficpoint->num_adj_edges >= vert_ficpoint->max_adj_edges || - vertex_cpoint->num_adj_edges >= vertex_cpoint->max_adj_edges ) { - ret = BNS_VERT_EDGE_OVFL; - break; - } - vertex_cpoint->type |= BNS_VERT_TYPE_C_POINT; - if ( (KNOWN_ACIDIC_TYPE & type) && nCharge < 0 ) { - vertex_cpoint->type |= pBNS->type_TACN; - } -#if ( FIX_CPOINT_BOND_CAP != 1 ) /* { */ - /* set capacity = 1 to the edges from the c_point to the centerpoint(s) */ - /* if their current capacity is zero */ - /* the centerpoint is any adjacent atom that is adjacent to a multiple bond */ - for ( k = 0; k < vertex_cpoint->num_adj_edges; k ++ ) { - int iedge = vertex_cpoint->iedge[k]; - if ( !pBNS->edge[iedge].cap ) { - /* single bond, possibly between c_point and centerpoint */ - centerpoint = (pBNS->edge[iedge].neighbor12 ^ c_point); - if ( centerpoint < pBNS->num_atoms && - pBNS->vert[centerpoint].st_edge.cap >= 1 ) { - int bond_type = (at[c_point].bond_type[k] & BOND_TYPE_MASK); - if ( bond_type == BOND_TAUTOM || - bond_type == BOND_ALTERN || - bond_type == BOND_SINGLE ) { - pBNS->edge[iedge].cap = 1; - } - } - } - } -#endif /* } FIX_CPOINT_BOND_CAP */ - /* create a new edge connecting c_point to the new fictitious c-group vertex vert_ficpoint */ - edge = pBNS->edge + num_edges; - edge->cap = 1; - edge->flow = 0; - edge->pass = 0; -#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) - edge->forbidden &= pBNS->edge_forbidden_mask; /* remove previous temporary ban */ -#endif - /* nCharge = +1: mark edge to c-point having no (+)-moveable charge with flow=1 */ - /* nCharge = -1: mark edge to c-point having -1 moveable charge with flow=1 */ - if ( nCharge==1 && at[c_point].charge != 1 || nCharge==-1 && at[c_point].charge == -1 ) - /*if ( !CHARGED_CPOINT(at,c_point) )*/ - { - /* increment new edge flow, update st_edges of the adjacent vertices */ - edge->flow ++; - /* increment c-group vertex st-flow & cap */ - vert_ficpoint->st_edge.flow ++; - vert_ficpoint->st_edge.cap ++; - /* increment c-point vertex st-flow & cap */ - vertex_cpoint->st_edge.flow ++; - vertex_cpoint->st_edge.cap ++; - } -#if ( FIX_CPOINT_BOND_CAP == 1 ) /* { */ - /* set capacity = 1 to the edges from the c_point to the centerpoint(s) */ - /* if their current capacity is zero */ - /* the centerpoint is any adjacent atom that is adjacent to a multiple bond */ - for ( k = 0; k < vertex_cpoint->num_adj_edges; k ++ ) { - int iedge = vertex_cpoint->iedge[k]; - VertexFlow nNewCap = vertex_cpoint->st_edge.cap; - centerpoint = (pBNS->edge[iedge].neighbor12 ^ c_point); - if ( !pBNS->edge[iedge].cap ) { - /* single bond, possibly between c_point and centerpoint */ - if ( centerpoint < pBNS->num_atoms && - pBNS->vert[centerpoint].st_edge.cap >= 1 ) { - nNewCap = inchi_min( pBNS->vert[centerpoint].st_edge.cap, nNewCap ); - nNewCap = inchi_min( nNewCap, MAX_BOND_EDGE_CAP ); - pBNS->edge[iedge].cap = nNewCap; - } - } -#if ( FIX_CPOINT_BOND_CAP2 == 1 ) /* multiple bond */ - else - if ( centerpoint < pBNS->num_atoms && - edge->flow && pBNS->edge[iedge].cap < MAX_BOND_EDGE_CAP ) { - pBNS->edge[iedge].cap ++; - } -#endif - } -#endif /* } FIX_CPOINT_BOND_CAP */ - /* connect edge to c_point and fictpoint and increment the counters of neighbors and edges */ - edge->neighbor1 = c_point; /* the smallest out of v1=endopoint and v2=num_vertices */ - edge->neighbor12 = c_point ^ fictpoint; /* v1 ^ v2 */ - vertex_cpoint->iedge[vertex_cpoint->num_adj_edges] = num_edges; - vert_ficpoint->iedge[vert_ficpoint->num_adj_edges] = num_edges ++; - edge->neigh_ord[0] = vertex_cpoint->num_adj_edges ++; - edge->neigh_ord[1] = vert_ficpoint->num_adj_edges ++; - edge->cap0 = edge->cap; - edge->flow0 = edge->flow; - - } - ret = pBNS->num_vertices; /* new c-group atom number */ - pBNS->num_edges = num_edges; - pBNS->num_vertices += num_cg; - pBNS->num_c_groups += num_cg; - - return ret; -} - -/*********************************************************************************/ -int CreateTGroupInBnStruct( inp_ATOM *at, int num_atoms, - BN_STRUCT *pBNS, int nType, int nMask ) -{ - int ret = 0; - /* ret = ReInitBnStruct( pBNS ); */ - int k, endpoint, tg, centerpoint, fictpoint; - int num_tg = 1; - int num_edges = pBNS->num_edges; - int num_vertices = pBNS->num_vertices; - BNS_VERTEX *vert_ficpoint, *ver_ficpont_prev; /* fictitious vertex describing t-group */ - BNS_VERTEX *vert_endpoint; - BNS_EDGE *edge; /* edge between that vertex and the tautomeric endpoint */ - int mask, num_endpoints, neutral_valence, edge_flow, edge_cap, num_bonds; - - /* Debug: check overflow */ - if ( num_vertices + num_tg >= pBNS->max_vertices ) { - return BNS_VERT_EDGE_OVFL; - } - /* count new t-group edges */ - for ( endpoint = 0, num_endpoints = 0; endpoint < num_atoms; endpoint ++ ) { - if ( (nType & GetAtomChargeType( at, endpoint, NULL, &mask, 0 )) && (mask & nMask) - ) { - num_endpoints ++; - } - } - if ( !num_endpoints ) { - return 0; - } - - - /* since t-group IDs may be not contiguous, clear all vertices that will be added. - all-zeroes-vertex will be ignored by the BNS - */ - memset( pBNS->vert+num_vertices, 0, num_tg*sizeof(pBNS->vert[0]) ); - /* *old* Make sure the last t-group has the largest t-group ID: - this is necessary to correctly add new edges and vertices for testing augmenting paths - */ - /**************************************/ - /* initialize new fictitious vertex */ - /* representing t-point group */ - /**************************************/ - ver_ficpont_prev = pBNS->vert+num_vertices - 1; - - for ( tg = 0; tg < num_tg; tg ++, ver_ficpont_prev = vert_ficpoint ) { - /* - vert_ficpoint-1 is the last vertex; - vert_ficpoint is the vertex that is being added - Note: nGroupNumber are not contiguous - */ - vert_ficpoint = pBNS->vert+num_vertices + tg; - vert_ficpoint->iedge = ver_ficpont_prev->iedge + ver_ficpont_prev->max_adj_edges; - vert_ficpoint->max_adj_edges = num_endpoints+BNS_ADD_EDGES+BNS_ADD_SUPER_TGROUP; - vert_ficpoint->num_adj_edges = 0; - vert_ficpoint->st_edge.flow = vert_ficpoint->st_edge.flow0 = 0; - vert_ficpoint->st_edge.cap = vert_ficpoint->st_edge.cap0 = 0; - vert_ficpoint->type |= BNS_VERT_TYPE_TGROUP; - } - tg = 1; - for ( endpoint = 0; endpoint < num_atoms; endpoint ++ ) { - if ( (nType & GetAtomChargeType( at, endpoint, NULL, &mask, 0 )) && (mask & nMask)); - else - continue; - fictpoint = tg + num_vertices - 1; - vert_ficpoint = pBNS->vert + fictpoint; - vert_endpoint = pBNS->vert + endpoint; - /* Debug: check overflow */ - if ( fictpoint >= pBNS->max_vertices || - num_edges >= pBNS->max_edges || - vert_ficpoint->num_adj_edges >= vert_ficpoint->max_adj_edges || - vert_endpoint->num_adj_edges >= vert_endpoint->max_adj_edges ) { - ret = BNS_VERT_EDGE_OVFL; - break; - } - /* obtain donor/acceptor info */ - neutral_valence = at[endpoint].chem_bonds_valence + at[endpoint].num_H - at[endpoint].charge; - if ( neutral_valence != 2 /* O, S, Se, Te */ && - neutral_valence != 3 /* N, P */ ) { - ret = BNS_PROGRAM_ERR; /* wrong endpoint neutral valence */ - break; - } - edge_flow = at[endpoint].num_H; - num_bonds = at[endpoint].valence; - edge_cap = neutral_valence - num_bonds; /* does not allow to reduce -NH3(+) to #N or -OH(+)- to -O- */ - if ( 3 == neutral_valence /* N or P */ && 1 < num_bonds ) { - edge_cap ++; /* allow -NH2(+)- => -N=, >NH(+)- => >N- */ - } - edge_flow = inchi_min( edge_flow, edge_cap); - /* - if ( !nGetEndpointInfo( at, endpoint, &eif ) ) { - ret = BNS_BOND_ERR; - break; - } - */ - vert_endpoint->type |= BNS_VERT_TYPE_ENDPOINT; - /* create a new edge connecting endpoint to the new fictitious t-group vertex vert_ficpoint */ - edge = pBNS->edge + num_edges; - edge->cap = edge_cap; - edge->flow = edge_flow; - edge->pass = 0; -#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) - edge->forbidden &= pBNS->edge_forbidden_mask; -#endif - /* adjust st_flow and st_cap of the adjacent vertices */ - /* adjust t-group vertex st-flow & cap */ - vert_ficpoint->st_edge.flow += edge->flow; - vert_ficpoint->st_edge.cap += edge->flow; - /* adjust endpoint vertex st-flow & cap */ - vert_endpoint->st_edge.flow += edge->flow; - vert_endpoint->st_edge.cap += edge->flow; - - /* adjust edge cap & flow according to the number of H and number of bonds */ - for ( k = 0; k < vert_endpoint->num_adj_edges; k ++ ) { - int iedge = vert_endpoint->iedge[k]; - VertexFlow nNewCap = vert_endpoint->st_edge.cap; - if ( !pBNS->edge[iedge].cap ) { - /* single bond, possibly between endpoint and centerpoint */ - centerpoint = (pBNS->edge[iedge].neighbor12 ^ endpoint); - if ( centerpoint < pBNS->num_atoms && - pBNS->vert[centerpoint].st_edge.cap >= 1 ) { - nNewCap = inchi_min( pBNS->vert[centerpoint].st_edge.cap, nNewCap ); - nNewCap = inchi_min( nNewCap, MAX_BOND_EDGE_CAP ); - pBNS->edge[iedge].cap = nNewCap; - } - } - } - - /* connect edge to endpoint and fictpoint and increment the counters of neighbors and edges */ - edge->neighbor1 = endpoint; /* the smallest out of v1=endopoint and v2=num_vertices */ - edge->neighbor12 = endpoint ^ fictpoint; /* v1 ^ v2 */ - vert_endpoint->iedge[vert_endpoint->num_adj_edges] = num_edges; - vert_ficpoint->iedge[vert_ficpoint->num_adj_edges] = num_edges ++; - edge->neigh_ord[0] = vert_endpoint->num_adj_edges ++; - edge->neigh_ord[1] = vert_ficpoint->num_adj_edges ++; - edge->cap0 = edge->cap; - edge->flow0 = edge->flow; - } - - ret = pBNS->num_vertices; /* new t-group atom number */ - pBNS->num_edges = num_edges; - pBNS->num_vertices += num_tg; - pBNS->num_t_groups += num_tg; - - return ret; -} - - - -/*********************************************************************************/ -int RemoveLastGroupFromBnStruct( inp_ATOM *at, int num_atoms, int tg, BN_STRUCT *pBNS ) -{ - int ret = 0; - /* ret = ReInitBnStruct( pBNS ); */ - int k, endpoint, /*centerpoint, fictpoint,*/ iedge; - int num_edges = pBNS->num_edges; - int num_vertices = pBNS->num_vertices; - BNS_VERTEX *vert_ficpoint /*, *ver_ficpont_prev*/; /* fictitious vertex describing t-group */ - BNS_VERTEX *vert_endpoint; - BNS_EDGE *edge; /* edge between that vertex and the tautomeric endpoint */ - /*int mask, num_endpoints, neutral_valence, edge_flow, edge_cap, num_bonds;*/ - int is_t_group = 0, is_c_group = 0; - - /* Debug: check overflow */ - if ( pBNS->num_added_atoms + pBNS->num_c_groups + pBNS->num_t_groups + num_atoms >= pBNS->max_vertices ) { - return BNS_VERT_EDGE_OVFL; - } - if ( tg + 1 != num_vertices ) { - return BNS_VERT_EDGE_OVFL; - } - vert_ficpoint = pBNS->vert + tg; - if ( vert_ficpoint->type & BNS_VERT_TYPE_TGROUP ) { - is_t_group = 1; - } - if ( vert_ficpoint->type & BNS_VERT_TYPE_C_GROUP ) { - is_c_group = 1; - if ( vert_ficpoint->type & BNS_VERT_TYPE_C_NEGATIVE ) - is_c_group = 2; - } - for ( k = vert_ficpoint->num_adj_edges-1; 0 <= k; k -- ) { - iedge = vert_ficpoint->iedge[k]; - if ( iedge + 1 != num_edges ) { - return BNS_VERT_EDGE_OVFL; - } - edge = pBNS->edge + iedge; - endpoint = edge->neighbor12 ^ tg; - vert_endpoint = pBNS->vert + endpoint; - /* adjust st_flow, st_cap */ - vert_endpoint->st_edge.cap0 = - vert_endpoint->st_edge.cap -= edge->flow; - vert_endpoint->st_edge.flow0 = - vert_endpoint->st_edge.flow -= edge->flow; - if ( pBNS->type_TACN && (vert_endpoint->type & pBNS->type_TACN) == pBNS->type_TACN ) { - vert_endpoint->type ^= pBNS->type_TACN; - } - if ( is_t_group ) { - vert_endpoint->type ^= (vert_ficpoint->type & BNS_VERT_TYPE_ENDPOINT); - } - if ( is_c_group ) { - vert_endpoint->type ^= (vert_ficpoint->type & BNS_VERT_TYPE_C_POINT); - } - /* remove edge */ - if ( edge->neigh_ord[0]+1 != vert_endpoint->num_adj_edges ) { - return BNS_VERT_EDGE_OVFL; - } - vert_endpoint->num_adj_edges --; - memset( edge, 0, sizeof(*edge) ); - num_edges --; - if ( 1 == is_t_group && endpoint < num_atoms ) { - at->endpoint = 0; - } - if ( 1 == is_c_group && endpoint < num_atoms ) { - at->c_point = 0; - } - } - memset( vert_ficpoint, 0, sizeof(*vert_ficpoint) ); - num_vertices --; - - pBNS->num_edges = num_edges; - pBNS->num_vertices = num_vertices; - if ( is_t_group ) - pBNS->num_t_groups --; - if ( is_c_group ) - pBNS->num_c_groups --; - - return ret; -} - - - -/******************************************************************************************/ -int SetInitCapFlowToCurrent( BN_STRUCT *pBNS ) -{ - int i, j; - BNS_EDGE *pEdge=NULL; - for ( i = 0; i < pBNS->num_vertices; i ++ ) { - pBNS->vert[i].st_edge.flow0 = pBNS->vert[i].st_edge.flow; - pBNS->vert[i].st_edge.cap0 = pBNS->vert[i].st_edge.cap; - for ( j = 0; j < pBNS->vert[i].num_adj_edges; j ++ ) { - pEdge = pBNS->edge + pBNS->vert[i].iedge[j]; - pEdge->cap0 = pEdge->cap; - pEdge->flow0 = pEdge->flow; - } - } - return 0; -} - - - -/******************************************************************************************/ -int ArTypMask[] = { - AR_SIMPLE_TYP1, AR_SIMPLE_MSK1, - AR_SIMPLE_TYP2, AR_SIMPLE_MSK2, - AR_SIMPLE_TYP3, AR_SIMPLE_MSK3, - 0, 0 }; - - - -/******************************************************************************************/ -int SimpleRemoveAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2remove ) -{ - int i, j, max_j=-1, mask, type, num_removed; - int num[AR_SIMPLE_STEPS+1], num_tot; - - for ( j = 0; ArTypMask[2*j]; j ++ ) { - num[max_j = j] = 0; - } - - for ( i = 0; i < num_atoms; i ++ ) { - if ( !at[i].charge && at[i].num_H && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { - for ( j = 0; j <= max_j; j ++ ) { - if ( (type & ArTypMask[2*j]) && (mask && ArTypMask[2*j+1]) ) { - num[j] ++; - break; - } - } - } - } - for ( j = 0, num_tot = 0; j <= max_j; j ++ ) { - if ( (num_tot += num[j]) >= num2remove ) { - max_j = j; - break; - } - } - if ( !num_tot ) { - return 0; - } - for ( i = 0, num_removed = 0; i < num_atoms && num_removed < num2remove; i ++ ) { - if ( !at[i].charge && at[i].num_H && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { - for ( j = 0; j <= max_j; j ++ ) { - if ( num[j] && (type & ArTypMask[2*j]) && (mask && ArTypMask[2*j+1]) ) { - type = GetAtomChargeType( at, i, pAATG->nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ - num[j] --; - at[i].charge --; - AddOrRemoveExplOrImplH( -1, at, num_atoms, (AT_NUMB)i, pAATG->t_group_info ); - /*at[i].num_H --;*/ - num_removed ++; - type = GetAtomChargeType( at, i, pAATG->nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ - break; - } - } - } - } - /* - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] -= num_removed; - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] += num_removed; - */ - return num_removed; -} - - - -/******************************************************************************************/ -int bHasAcidicHydrogen( inp_ATOM *at, int i ) -{ - int bFound = 0, j, type, mask; - if ( !at[i].charge && at[i].num_H && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { - for ( j = 0; ArTypMask[2*j]; j ++ ) { - if ( (type & ArTypMask[2*j]) && (mask & ArTypMask[2*j+1]) ) { - bFound ++; - break; - } - } - } - return bFound; -} - - - -/******************************************************************************************/ -int bHasOtherExchangableH ( inp_ATOM *at, int i ) -{ - int bFound = 0, type, mask; - if ( at[i].num_H && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { - if ( (type & ATT_ATOM_N) && (mask & ATBIT_NP_H) ) { - bFound ++; - } - } - return bFound; -} - - - -/******************************************************************************************/ -int AaTypMask[] = { - AA_SIMPLE_TYP1, AA_SIMPLE_MSK1, -#if ( FIX_NP_MINUS_BUG == 1 ) - AA_SIMPLE_TYP4, AA_SIMPLE_MSK4, /* should not follow 0,0 pair */ -#endif - AA_SIMPLE_TYP2, AA_SIMPLE_MSK2, - AA_SIMPLE_TYP3, AA_SIMPLE_MSK3, - 0, 0 }; - - - -/******************************************************************************************/ -int SimpleAddAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2add ) -{ - int i, j, max_j=-1, mask, type, num_added; - int num[AR_SIMPLE_STEPS+1], num_tot; - - for ( j = 0; AaTypMask[2*j]; j ++ ) { - num[max_j = j] = 0; - } - - for ( i = 0; i < num_atoms; i ++ ) { - if ( at[i].charge==-1 && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { - for ( j = 0; j <= max_j; j ++ ) { - if ( (type & AaTypMask[2*j]) && (mask && AaTypMask[2*j+1]) ) { - num[j] ++; - break; - } - } - } - } - for ( j = 0, num_tot = 0; j <= max_j; j ++ ) { - if ( (num_tot += num[j]) >= num2add ) { - max_j = j; - break; - } - } - if ( !num_tot ) { - return 0; - } - for ( i = 0, num_added = 0; i < num_atoms && num_added < num2add; i ++ ) { - if ( at[i].charge==-1 && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { - for ( j = 0; j <= max_j; j ++ ) { - if ( num[j] && (type & AaTypMask[2*j]) && (mask && AaTypMask[2*j+1]) ) { - type = GetAtomChargeType( at, i, pAATG->nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ - num[j] --; - at[i].charge ++; - AddOrRemoveExplOrImplH( 1, at, num_atoms, (AT_NUMB)i, pAATG->t_group_info ); - /*at[i].num_H ++;*/ - num_added ++; - type = GetAtomChargeType( at, i, pAATG->nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ - break; - } - } - } - } - /* - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] += num_added; - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] -= num_added; - */ - return num_added; -} - - - -/******************************************************************************************/ -int bHasAcidicMinus( inp_ATOM *at, int i ) -{ - int bFound = 0, j, type, mask; - if ( at[i].charge==-1 && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { - for ( j = 0; AaTypMask[2*j]; j ++ ) { - if ( (type & AaTypMask[2*j]) && (mask & AaTypMask[2*j+1]) ) { - bFound ++; - break; - } - } - } - return bFound; -} - - - -/****************************************************************************************** -Create 2 tautomeric groups: (1) for O on -C=O, (2) for the rest of the atoms. -Pull H from (2) to (1); remove later -*******************************************************************************************/ -int HardRemoveAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2remove, int *nNumCanceledCharges, BN_STRUCT *pBNS, BN_DATA *pBD ) -{ - int cg_Plus = 0; - int cg_Minus = 0; - int tg_H_Other = 0; - int tg_H_Acid = 0; - - int ret = 0, ret2; - int nDelta, nNumChanges = 0, nNumMoved2AcidH = 0, nNumNeutralized = 0, nPrevNumCharges; - - int nPosCharges, nPosCharges2; - int nNegCharges, nNegCharges2; - /* - int nNumNP_H, nNumNP_H2; - int nNumOS_H, nNumOS_H2; - */ - - nPosCharges = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; - nNegCharges = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; - /* - nNumNP_H = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] + - pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton]; - nNumOS_H = pAATG->nAtTypeTotals[ATTOT_NUM_COH] + - pAATG->nAtTypeTotals[ATTOT_NUM_CSH] + - pAATG->nAtTypeTotals[ATTOT_NUM_ZOH]; - */ - - /* prevent free exchange H <-> (-) */ - pBNS->type_CN = (BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE); - pBNS->type_T = BNS_VERT_TYPE_TGROUP; - pBNS->type_TACN = BNS_VERT_TYPE_ACID; - /* create (+) charge group */ - cg_Plus = CreateCGroupInBnStruct( at, num_atoms, pBNS, AR_HARD_TYP_POS, AR_HARD_MSK_POS, 1 ); - /* create (-) charge group */ - /* - if ( nAtTypeTotals[ATTOT_NUM_CO_Minus] + - nAtTypeTotals[ATTOT_NUM_CS_Minus] + - nAtTypeTotals[ATTOT_NUM_ZO_Minus] + - nAtTypeTotals[ATTOT_NUM_N_Minus] ) - */ - cg_Minus = CreateCGroupInBnStruct( at, num_atoms, pBNS, AR_HARD_TYP_NEG, AR_HARD_MSK_NEG, -1 ); - - pBNS->type_CN = (BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE); - pBNS->type_T = BNS_VERT_TYPE_TGROUP; - pBNS->type_TACN = BNS_VERT_TYPE_ACID; - - /* create tautomeric group for non-acidic or negatively charged acidic O */ - tg_H_Other = CreateTGroupInBnStruct( at, num_atoms, pBNS, AR_HARD_TYP_HN, AR_HARD_MSK_HN ); - - /* create tautomeric group for possibly acidic O */ - tg_H_Acid = CreateTGroupInBnStruct( at, num_atoms, pBNS, AR_HARD_TYP_HA, AR_HARD_MSK_HA ); - if ( tg_H_Other >= num_atoms && tg_H_Acid >= num_atoms ) { - /* find alt path to remove one proton */ - do { - /* remove a proton */ - nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; - ret = bExistsAltPath( pBNS, pBD, pAATG, at, num_atoms, - tg_H_Other /*nVertDoubleBond*/, tg_H_Acid /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON ); - if ( IS_BNS_ERROR( ret ) ) { - return ret; - } - if ( ret & 1 ) { - nDelta = (ret & ~3) >> 2; - nNumChanges += (0 != (ret & 2)); - if ( nDelta ) { - /* radical pair has disappeared */ - ; /* goto quick_exit;*/ - } - nNumMoved2AcidH ++; - if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + 1 ) { - nNumNeutralized += (nPrevNumCharges - (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - 1))/2; - } - } - - } while ( (ret & 1) && nNumMoved2AcidH < num2remove ); - - /* neutralize: remove ion pairs like >N(+)=-O(-) => >N-=O */ - if ( (nNumMoved2AcidH /*|| bCancelChargesAlways*/) && cg_Minus >= num_atoms && cg_Plus >= num_atoms && - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] > abs(pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE]) ) { - do { - nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; - ret = bExistsAltPath( pBNS, pBD, pAATG, at, num_atoms, - cg_Minus /*nVertDoubleBond*/, cg_Plus /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON ); - if ( IS_BNS_ERROR( ret ) ) { - return ret; - } - if ( ret & 1 ) { - nDelta = (ret & ~3) >> 2; - nNumChanges += (0 != (ret & 2)); - if ( nDelta ) { - /* radical pair has disappeared */ - ; /* goto quick_exit;*/ - } - if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] ) { - nNumNeutralized += (nPrevNumCharges - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES])/2; - } - } - } while ( ret & 1 ); - } - } - - ret = 0; - if ( tg_H_Acid >= num_atoms ) { - ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, tg_H_Acid, pBNS ); - if ( !ret && ret2 ) - ret = ret2; - } - if ( tg_H_Other >= num_atoms ) { - ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, tg_H_Other, pBNS ); - if ( !ret && ret2 ) - ret = ret2; - } - if ( cg_Minus >= num_atoms ) { - ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Minus, pBNS ); - if ( !ret && ret2 ) - ret = ret2; - } - if ( cg_Plus >= num_atoms ) { - ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Plus, pBNS ); - if ( !ret && ret2 ) - ret = ret2; - } - - pBNS->type_CN = 0; - pBNS->type_T = 0; - pBNS->type_TACN = 0; - - if ( ret ) { - return ret; - } - if ( pAATG->nAtTypeTotals[ATTOT_NUM_CO_Minus] + pAATG->nAtTypeTotals[ATTOT_NUM_ZO_Minus] && - pAATG->nAtTypeTotals[ATTOT_NUM_N_Minus] ) { - } - - nPosCharges2 = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; - nNegCharges2 = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; - /* - nNumNP_H2 = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] + - pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton]; - nNumOS_H2 = pAATG->nAtTypeTotals[ATTOT_NUM_COH] + - pAATG->nAtTypeTotals[ATTOT_NUM_CSH] + - pAATG->nAtTypeTotals[ATTOT_NUM_ZOH]; - */ - if ( (nPosCharges - nNegCharges) - (nPosCharges2 - nNegCharges2) != 0 ) { - return BNS_PROGRAM_ERR; - } - - if ( nNumCanceledCharges ) { -#if ( FIX_CANCEL_CHARGE_COUNT_BUG == 1 ) - *nNumCanceledCharges += 2*nNumNeutralized; -#else - *nNumCanceledCharges = 2*nNumNeutralized; -#endif - } - - return nNumMoved2AcidH; -} - - - -/******************************************************************************************/ -int HardAddAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2add, int *nNumCanceledCharges, BN_STRUCT *pBNS, BN_DATA *pBD ) -{ - int cg_Plus = 0; - int cg_Minus_CO = 0; - int cg_Minus_Other = 0; - int tg_H = 0; - - int ret = 0, ret2; - int nDelta, nNumChanges = 0, nNumMoved2AcidMinus = 0, nNumNeutralized = 0, nPrevNumCharges; - - int nPosCharges, nPosCharges2; - int nNegCharges, nNegCharges2; - /* - int nNumNP_H, nNumNP_H2; - int nNumOS_H, nNumOS_H2; - */ - nPosCharges = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; - nNegCharges = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; - /* - nNumNP_H = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] + - pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton]; - nNumOS_H = pAATG->nAtTypeTotals[ATTOT_NUM_COH] + - pAATG->nAtTypeTotals[ATTOT_NUM_CSH] + - pAATG->nAtTypeTotals[ATTOT_NUM_ZOH]; - */ - /* prevent free exchange H <-> (-) */ - pBNS->type_CN = (BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE); - pBNS->type_T = BNS_VERT_TYPE_TGROUP; - pBNS->type_TACN = BNS_VERT_TYPE_ACID; - /* create (+) charge group */ - cg_Plus = CreateCGroupInBnStruct( at, num_atoms, pBNS, AA_HARD_TYP_POS, AA_HARD_MSK_POS, 1 ); - /* create (-) charge group */ - /* - if ( nAtTypeTotals[ATTOT_NUM_CO_Minus] + - nAtTypeTotals[ATTOT_NUM_CS_Minus] + - nAtTypeTotals[ATTOT_NUM_ZO_Minus] + - nAtTypeTotals[ATTOT_NUM_N_Minus] ) - */ - cg_Minus_CO = CreateCGroupInBnStruct( at, num_atoms, pBNS, AA_HARD_TYP_CO, AA_HARD_MSK_CO, -1 ); - - cg_Minus_Other = CreateCGroupInBnStruct( at, num_atoms, pBNS, AA_HARD_TYP_NEG, AA_HARD_MSK_NEG, -1 ); - - pBNS->type_CN = (BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE); - pBNS->type_T = BNS_VERT_TYPE_TGROUP; - pBNS->type_TACN = BNS_VERT_TYPE_ACID; - - /* create tautomeric group for all H */ - tg_H = CreateTGroupInBnStruct( at, num_atoms, pBNS, AA_HARD_TYP_H, AA_HARD_MSK_H ); - - - if ( cg_Minus_Other >= num_atoms && cg_Minus_CO >= num_atoms ) { - /* find alt path to remove one proton */ - do { - /* add a proton */ - nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; - ret = bExistsAltPath( pBNS, pBD, pAATG, at, num_atoms, - cg_Minus_Other /*nVertDoubleBond*/, cg_Minus_CO /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON ); - if ( IS_BNS_ERROR( ret ) ) { - return ret; - } - if ( ret & 1 ) { - nDelta = (ret & ~3) >> 2; - nNumChanges += (0 != (ret & 2)); - if ( nDelta ) { - /* radical pair has disappeared */ - ; /* goto quick_exit;*/ - } - nNumMoved2AcidMinus ++; - if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + 1 ) { - nNumNeutralized += (nPrevNumCharges - (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - 1))/2; - } - } - - } while ( (ret & 1) && nNumMoved2AcidMinus < num2add ); - - /* neutralize: remove ion pairs like >N(+)=-O(-) => >N-=O */ - if ( (nNumMoved2AcidMinus /*|| bCancelChargesAlways*/) && cg_Minus_Other >= num_atoms && cg_Plus >= num_atoms && - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] > abs(pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE]) ) { - do { - nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; - ret = bExistsAltPath( pBNS, pBD, pAATG, at, num_atoms, - cg_Minus_Other /*nVertDoubleBond*/, cg_Plus /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON ); - if ( IS_BNS_ERROR( ret ) ) { - return ret; - } - if ( ret & 1 ) { - nDelta = (ret & ~3) >> 2; - nNumChanges += (0 != (ret & 2)); - if ( nDelta ) { - /* radical pair has disappeared */ - ; /* goto quick_exit;*/ - } - if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] ) { - nNumNeutralized += (nPrevNumCharges - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES])/2; - } - } - } while ( ret & 1 ); - } - } - - ret = 0; - if ( tg_H >= num_atoms ) { - ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, tg_H, pBNS ); - if ( !ret && ret2 ) - ret = ret2; - } - if ( cg_Minus_Other >= num_atoms ) { - ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Minus_Other, pBNS ); - if ( !ret && ret2 ) - ret = ret2; - } - if ( cg_Minus_CO >= num_atoms ) { - ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Minus_CO, pBNS ); - if ( !ret && ret2 ) - ret = ret2; - } - if ( cg_Plus >= num_atoms ) { - ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Plus, pBNS ); - if ( !ret && ret2 ) - ret = ret2; - } - - pBNS->type_CN = 0; - pBNS->type_T = 0; - pBNS->type_TACN = 0; - - if ( ret ) { - return ret; - } - if ( pAATG->nAtTypeTotals[ATTOT_NUM_CO_Minus] + pAATG->nAtTypeTotals[ATTOT_NUM_ZO_Minus] && - pAATG->nAtTypeTotals[ATTOT_NUM_N_Minus] ) { - } - - nPosCharges2 = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; - nNegCharges2 = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; - /* - nNumNP_H2 = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] + - pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton]; - nNumOS_H2 = pAATG->nAtTypeTotals[ATTOT_NUM_COH] + - pAATG->nAtTypeTotals[ATTOT_NUM_CSH] + - pAATG->nAtTypeTotals[ATTOT_NUM_ZOH]; - */ - if ( (nPosCharges - nNegCharges) - (nPosCharges2 - nNegCharges2) != 0 ) { - return BNS_PROGRAM_ERR; - } - - if ( nNumCanceledCharges ) { -#if ( FIX_CANCEL_CHARGE_COUNT_BUG == 1 ) - *nNumCanceledCharges += 2*nNumNeutralized; -#else - *nNumCanceledCharges = 2*nNumNeutralized; -#endif - } - - return nNumMoved2AcidMinus; -} - - - -/******************************************************************************************/ -/* examples include removal of H from tautomeric O that belongs to the same t-group as N: */ -/* >N(+)=-N=-OH =(taut.)=> >N(+)=-NH-=O =(+charge move)=> >N-=NH(+)-=O => >N-=N-=O + H(+) */ -/******************************************************************************************/ -int HardRemoveHplusNP( inp_ATOM *at, int num_atoms, int bCancelChargesAlways, int *nNumCanceledCharges, - BN_AATG *pAATG, BN_STRUCT *pBNS, BN_DATA *pBD ) -{ - - int cg_Plus = 0; - int cg_Minus = 0; - int tg_H = 0; -#if ( MOVE_PPLUS_TO_REMOVE_PROTONS == 1 ) - int cg_PlusP = 0; -#endif -#if ( FIX_REM_PROTON_COUNT_BUG == 1 ) - int nPrevRemovedProtons, nCurrRemovedProtons; -#endif - int ret = 0, ret2; - int nDelta, nNumChanges = 0, nNumRemovedProtons = 0, nNumNeutralized = 0, nPrevNumCharges; - - int nPosCharges, nPosCharges2; - int nNegCharges, nNegCharges2; - /* - int nNumNP_H, nNumNP_H2; - int nNumOS_H, nNumOS_H2; - */ - - nPosCharges = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; - nNegCharges = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; - /* - nNumNP_H = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] + - pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton]; - nNumOS_H = pAATG->nAtTypeTotals[ATTOT_NUM_COH] + - pAATG->nAtTypeTotals[ATTOT_NUM_CSH] + - pAATG->nAtTypeTotals[ATTOT_NUM_ZOH]; - */ - /* prevent free exchange H <-> (-) */ - pBNS->type_CN = (BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE); - pBNS->type_T = BNS_VERT_TYPE_TGROUP; - pBNS->type_TACN = BNS_VERT_TYPE_ACID; - /* create (+) charge group */ - cg_Plus = CreateCGroupInBnStruct( at, num_atoms, pBNS, PR_HARD_TYP_POS, PR_HARD_MSK_POS, 1 ); - /* create (-) charge group */ - /* - if ( nAtTypeTotals[ATTOT_NUM_CO_Minus] + - nAtTypeTotals[ATTOT_NUM_CS_Minus] + - nAtTypeTotals[ATTOT_NUM_ZO_Minus] + - nAtTypeTotals[ATTOT_NUM_N_Minus] ) - */ -#if ( MOVE_PPLUS_TO_REMOVE_PROTONS == 1 ) - cg_PlusP = CreateCGroupInBnStruct( at, num_atoms, pBNS, PR_HARD_TYP_POSP, PR_HARD_MSK_POS, 1 ); -#endif - cg_Minus = CreateCGroupInBnStruct( at, num_atoms, pBNS, PR_HARD_TYP_NEG, PR_HARD_MSK_NEG, -1 ); - - /* create single tautomeric group */ - tg_H = CreateTGroupInBnStruct( at, num_atoms, pBNS, PR_HARD_TYP_H, PR_HARD_MSK_H ); - - if ( tg_H >= num_atoms && cg_Plus >= num_atoms ) { - -#if ( FIX_N_MINUS_NORN_BUG == 1 ) - /* neutralize: remove ion pairs like >N(+)=-O(-) => >N-=O; >N(+)=-NH(-) => >N-=NH */ - if ( (nNumRemovedProtons || bCancelChargesAlways) && cg_Minus >= num_atoms && cg_Plus >= num_atoms && - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] > abs(pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE]) ) { - do { - nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; -#if ( FIX_REM_PROTON_COUNT_BUG == 1 ) - nPrevRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons; -#endif - ret = bExistsAltPath( pBNS, pBD, pAATG, at, num_atoms, - cg_Minus /*nVertDoubleBond*/, cg_Plus /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON ); - if ( IS_BNS_ERROR( ret ) ) { - return ret; - } -#if ( FIX_REM_PROTON_COUNT_BUG == 1 ) - nCurrRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons; - if ( nCurrRemovedProtons != nPrevRemovedProtons ) { - return BNS_RADICAL_ERR; - } -#endif - if ( ret & 1 ) { - nDelta = (ret & ~3) >> 2; - nNumChanges += (0 != (ret & 2)); - if ( nDelta ) { - /* radical pair has disappeared */ - ; /* goto quick_exit;*/ - } - if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] ) { - nNumNeutralized += (nPrevNumCharges - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES])/2; - } - } - } while ( ret & 1 ); - } -#endif - /* find alt path to remove one proton */ - do { - /* remove a proton */ - nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; -#if ( FIX_REM_PROTON_COUNT_BUG == 1 ) - nPrevRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons; -#endif - ret = bExistsAltPath( pBNS, pBD, pAATG, at, num_atoms, - tg_H /*nVertDoubleBond*/, cg_Plus /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON ); - if ( IS_BNS_ERROR( ret ) ) { - return ret; - } -#if ( FIX_REM_PROTON_COUNT_BUG == 1 ) - nCurrRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons; - if ( nCurrRemovedProtons != nPrevRemovedProtons + (ret & 1) ) { - return BNS_RADICAL_ERR; - } -#endif - if ( ret & 1 ) { - nDelta = (ret & ~3) >> 2; - nNumChanges += (0 != (ret & 2)); - if ( nDelta ) { - /* radical pair has disappeared */ - ; /* goto quick_exit;*/ - } - nNumRemovedProtons ++; - if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + 1 ) { - nNumNeutralized += (nPrevNumCharges - (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - 1))/2; - } - } - - } while ( ret & 1 ); - - /* neutralize: remove ion pairs like >N(+)=-O(-) => >N-=O */ - if ( (nNumRemovedProtons || bCancelChargesAlways) && cg_Minus >= num_atoms && cg_Plus >= num_atoms && - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] > abs(pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE]) ) { - do { - nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; -#if ( FIX_REM_PROTON_COUNT_BUG == 1 ) - nPrevRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons; -#endif - ret = bExistsAltPath( pBNS, pBD, pAATG, at, num_atoms, - cg_Minus /*nVertDoubleBond*/, cg_Plus /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON ); - if ( IS_BNS_ERROR( ret ) ) { - return ret; - } -#if ( FIX_REM_PROTON_COUNT_BUG == 1 ) - nCurrRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons; - if ( nCurrRemovedProtons != nPrevRemovedProtons ) { - return BNS_RADICAL_ERR; - } -#endif - if ( ret & 1 ) { - nDelta = (ret & ~3) >> 2; - nNumChanges += (0 != (ret & 2)); - if ( nDelta ) { - /* radical pair has disappeared */ - ; /* goto quick_exit;*/ - } - if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] ) { - nNumNeutralized += (nPrevNumCharges - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES])/2; - } - } - } while ( ret & 1 ); - } - } - ret = 0; - if ( tg_H >= num_atoms ) { - ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, tg_H, pBNS ); - if ( !ret && ret2 ) - ret = ret2; - } - if ( cg_Minus >= num_atoms ) { - ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Minus, pBNS ); - if ( !ret && ret2 ) - ret = ret2; - } -#if ( MOVE_PPLUS_TO_REMOVE_PROTONS == 1 ) - if ( cg_PlusP >= num_atoms ) { - ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_PlusP, pBNS ); - if ( !ret && ret2 ) - ret = ret2; - } -#endif - if ( cg_Plus >= num_atoms ) { - ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Plus, pBNS ); - if ( !ret && ret2 ) - ret = ret2; - } - - pBNS->type_CN = 0; - pBNS->type_T = 0; - pBNS->type_TACN = 0; - - if ( ret ) { - return ret; - } - if ( pAATG->nAtTypeTotals[ATTOT_NUM_CO_Minus] + pAATG->nAtTypeTotals[ATTOT_NUM_ZO_Minus] && - pAATG->nAtTypeTotals[ATTOT_NUM_N_Minus] ) { - } - - nPosCharges2 = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; - nNegCharges2 = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; - /* - nNumNP_H2 = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] + - pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton]; - nNumOS_H2 = pAATG->nAtTypeTotals[ATTOT_NUM_COH] + - pAATG->nAtTypeTotals[ATTOT_NUM_CSH] + - pAATG->nAtTypeTotals[ATTOT_NUM_ZOH]; - */ - if ( (nPosCharges - nNegCharges) - (nPosCharges2 - nNegCharges2) != nNumRemovedProtons ) { - return BNS_PROGRAM_ERR; - } - - if ( nNumCanceledCharges ) { -#if ( FIX_CANCEL_CHARGE_COUNT_BUG == 1 ) - *nNumCanceledCharges += 2*nNumNeutralized; -#else - *nNumCanceledCharges = 2*nNumNeutralized; -#endif - } - - return nNumRemovedProtons; -} - - - -/***************************************************************************************/ -int mark_at_type( inp_ATOM *atom, int num_atoms, int nAtTypeTotals[] ) -{ - int i, max_num_ions, mask, type; - /*int max_protons, max_O_Minus, num_H = 0, num_CO=0;*/ - if ( nAtTypeTotals ) { - memset( nAtTypeTotals, 0, ATTOT_ARRAY_LEN * sizeof(nAtTypeTotals[0]) ); - } - for ( i = 0; i < num_atoms; i++ ) { - type = GetAtomChargeType( atom, i, nAtTypeTotals, &mask, 0 ); - atom[i].at_type = type; - /* - num_H += ((type & PR_HARD_TYP_H) && (mask & ATBIT_MSK_H)); - num_CO += ((type & AR_HARD_TYP_HA) && (mask & AR_HARD_MSK_HA)); - */ - } - if ( nAtTypeTotals ) { - /* - max_protons = nAtTypeTotals[ATTOT_NUM_NP_Proton] + - inchi_min(num_H, nAtTypeTotals[ATTOT_NUM_NP_Plus]); - max_O_Minus = nAtTypeTotals[ATTOT_NUM_CO_Minus] + nAtTypeTotals[ATTOT_NUM_CS_Minus] + - nAtTypeTotals[ATTOT_NUM_ZO_Minus] + nAtTypeTotals[ATTOT_NUM_OO_Minus] + - nAtTypeTotals[ATTOT_NUM_ZOO_Minus] + nAtTypeTotals[ATTOT_NUM_NO_Minus] + - nAtTypeTotals[ATTOT_NUM_O_Minus] +nAtTypeTotals[ATTOT_NUM_N_Minus]; - ; - max_num_ions = max_protons + max_O_Minus + nAtTypeTotals[ATTOT_NUM_CHARGES]; - */ - max_num_ions = nAtTypeTotals[ATTOT_NUM_CHARGES]; - } else { - max_num_ions = 0; - } - return max_num_ions; -} - - - -/***************************************************************************************/ -int RemoveNPProtonsAndAcidCharges( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, BN_STRUCT *pBNS, BN_DATA *pBD ) -{ - - /* prepare data structure */ - int num; - int nNumCanceledCharges = 0; - int nNumHardRemovedProtons = 0; - int nNumHardRemovedAcidicProtons = 0; - T_GROUP_INFO *t_group_info = pAATG->t_group_info; - int ret=0, bError = 0; - int bAllowHardRemove = (t_group_info->bTautFlags & TG_FLAG_TEST_TAUT__SALTS) && - (t_group_info->bTautFlags & TG_FLAG_TEST_TAUT2_SALTS) && - (t_group_info->bTautFlags & TG_FLAG_MOVE_POS_CHARGES ) && - (t_group_info->bTautFlags & TG_FLAG_HARD_ADD_REM_PROTONS); - if ( pAATG->nMarkedAtom && num_atoms < pAATG->nAllocLen ) - { - inchi_free( pAATG->nMarkedAtom ); - qzfree( pAATG->nEndPoint ); - memset( pAATG, 0, sizeof(*pAATG) ); - } - if ( !pAATG->nMarkedAtom && (pAATG->nMarkedAtom = (S_CHAR *) inchi_malloc( num_atoms * sizeof(pAATG->nMarkedAtom[0]))) ) - { - pAATG->nAllocLen = num_atoms; - pAATG->nNumFound = 0; - } - - /* o TECHMAN-5.1. Remove protons from charged heteroatoms */ - - /* (TECHMAN-5.1a) Simple remove of protons from N, P, and O,S,Se,Te */ - if ( num = pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton] + pAATG->nAtTypeTotals[ATTOT_NUM_OH_Plus] ) - { - ret = SimpleRemoveHplusNPO(at, num_atoms, pAATG->nAtTypeTotals, t_group_info); - if ( ret != num ) - { - bError = BNS_PROGRAM_ERR; - goto exit_function; - } - /*t_group_info->nNumRemovedProtons += ret;*/ - t_group_info->tni.bNormalizationFlags |= (ret > 0)? FLAG_PROTON_NPO_SIMPLE_REMOVED : 0; - } - - if ( (num = pAATG->nAtTypeTotals[ATTOT_NUM_NP_Plus]) && bAllowHardRemove ) - { - /* [TECHMAN-5.1b] Hard removing more protons from cationic N; charges may be canceled */ - ret = HardRemoveHplusNP(at, num_atoms, 1, &nNumCanceledCharges, pAATG, pBNS, pBD); - if ( IS_BNS_ERROR( ret ) ) - { - bError = ret; - goto exit_function; - } - nNumHardRemovedProtons += ret; - /*t_group_info->nNumRemovedProtons += ret;*/ - t_group_info->tni.bNormalizationFlags |= (ret > 0)? FLAG_PROTON_NP_HARD_REMOVED : 0; - } - - - if ( pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] > 0 ) - { - /* o TECHMAN-5.2. Remove protons from neutral heteroatoms */ - - /* (TECHMAN-5.2a) Simple removal */ - ret = SimpleRemoveAcidicProtons( at, num_atoms, pAATG, pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] ); - if ( IS_BNS_ERROR( ret ) ) - { - bError = ret; - goto exit_function; - } - /*t_group_info->nNumRemovedProtons += ret;*/ - t_group_info->tni.bNormalizationFlags |= (ret > 0)? FLAG_PROTON_AC_SIMPLE_REMOVED : 0; - if ( pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] > 0 && bAllowHardRemove ) - { - /* (TECHMAN-5.2b) Hard removal */ - ret = HardRemoveAcidicProtons( at, num_atoms, pAATG, pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE], &nNumCanceledCharges, pBNS, pBD ); - if ( IS_BNS_ERROR( ret ) ) - { - bError = ret; - goto exit_function; - } - if ( ret > 0 ) - { - int ret2 = SimpleRemoveAcidicProtons( at, num_atoms, pAATG, ret ); - if ( ret2 != ret ) - { - bError = BNS_PROGRAM_ERR; - goto exit_function; - } - /*t_group_info->nNumRemovedProtons += ret;*/ - t_group_info->tni.bNormalizationFlags |= (ret > 0)? FLAG_PROTON_AC_HARD_REMOVED : 0; - nNumHardRemovedAcidicProtons += ret; - } - } - } - else - if ( pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] < 0 ) - { - ret = SimpleAddAcidicProtons( at, num_atoms, pAATG, -pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] ); - if ( IS_BNS_ERROR( ret ) ) - { - bError = ret; - goto exit_function; - } - /*t_group_info->nNumRemovedProtons -= ret;*/ - /* - CHECK_TACN == 1 prohibits replacing (-) on N with H unless H can be moved to N - along an alternating path from another heteroatom (t-group will be detected). - */ - t_group_info->tni.bNormalizationFlags |= (ret > 0)? FLAG_PROTON_AC_SIMPLE_ADDED : 0; - if ( pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] < 0 && bAllowHardRemove ) { - ret = HardAddAcidicProtons( at, num_atoms, pAATG, -pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE], &nNumCanceledCharges, pBNS, pBD ); - if ( IS_BNS_ERROR( ret ) ) - { - bError = ret; - goto exit_function; - } - if ( ret > 0 ) - { - int ret2 = SimpleAddAcidicProtons( at, num_atoms, pAATG, ret ); - if ( ret2 != ret ) - { - bError = BNS_PROGRAM_ERR; - goto exit_function; - } - /*t_group_info->nNumRemovedProtons -= ret;*/ - t_group_info->tni.bNormalizationFlags |= (ret > 0)? FLAG_PROTON_AC_HARD_ADDED : 0; - nNumHardRemovedAcidicProtons -= ret; - } - } - } - t_group_info->tni.bNormalizationFlags |= nNumCanceledCharges? FLAG_PROTON_CHARGE_CANCEL : 0; - -exit_function: - if ( bError ) - { - ret = IS_BNS_ERROR(bError)? bError : BNS_PROGRAM_ERR; - } - return ret; -} - - - -/********************************/ -/* */ -/* Main normalization procedure */ -/* */ -/********************************/ -int mark_alt_bonds_and_taut_groups ( inp_ATOM *at, inp_ATOM *at_fixed_bonds_out, int num_atoms, - T_GROUP_INFO *t_group_info, INCHI_MODE *inpbTautFlags, INCHI_MODE *inpbTautFlagsDone ) - - -{ - BN_STRUCT *pBNS = NULL; - BN_DATA *pBD = NULL; - int bError, nChanges, nTotChanges, taut_found, salt_found, taut_pass, salt_pass, salt_step, ret, ret2, num; - int nOrigDelta, num_changed_bonds; - int max_altp = BN_MAX_ALTP; - int bChangeFlow = (BNS_EF_CHNG_RSTR | BNS_EF_ALTR_BONDS); - BNS_FLOW_CHANGES fcd[BNS_MAX_NUM_FLOW_CHANGES+1]; - C_GROUP_INFO CGroupInfo; - C_GROUP_INFO *c_group_info = &CGroupInfo; - S_GROUP_INFO SGroupInfo; - S_GROUP_INFO *s_group_info = &SGroupInfo; - INCHI_MODE *pbTautFlags = t_group_info? &t_group_info->bTautFlags : inpbTautFlags; - INCHI_MODE *pbTautFlagsDone = t_group_info? &t_group_info->bTautFlagsDone : inpbTautFlagsDone; - - int nAtTypeTotals[ATTOT_ARRAY_LEN]; - int nNumOrigTotAtoms; - - BN_AATG aatg; - BN_AATG *pAATG = &aatg; - -#ifdef FIX_AROM_RADICAL /* Added 2011-05-09 IPl */ - int i, n_arom_radicals=0, *stored_radicals=NULL; -#endif - - nChanges = 0; - bError = 0; - - memset( c_group_info, 0, sizeof(*c_group_info) ); - memset( s_group_info, 0, sizeof(*s_group_info) ); - memset( pAATG, 0, sizeof(*pAATG) ); - - -#ifdef FIX_AROM_RADICAL /* Added 2011-05-09 IPl */ - for ( i = 0; i < num_atoms; i ++ ) - { - if ( (at[i].radical==RADICAL_DOUBLET) && (at[i].valence==2) && - (at[i].bond_type[0]==BOND_ALTERN) && (at[i].bond_type[1]==BOND_ALTERN) ) - { - n_arom_radicals++; - if ( !stored_radicals ) - { - stored_radicals = (int *) inchi_calloc(num_atoms, sizeof(int)); - /* 2011-08-05 explicit cast added due to Evan Bolton */ - if ( !stored_radicals ) - { - bError = BNS_OUT_OF_RAM; - goto exit_function; - } - stored_radicals[i] = RADICAL_DOUBLET; - at[i].radical = 0; - at[i].num_H++; - } - } - } - -#endif - - - if ( (*pbTautFlags & TG_FLAG_MOVE_POS_CHARGES) && num_atoms > 1 ) { - /* charge groups memory allocation */ - c_group_info->c_group = (C_GROUP *)inchi_calloc(num_atoms/2, sizeof(c_group_info->c_group[0])); - c_group_info->c_candidate = (C_CANDIDATE*)inchi_calloc(num_atoms, sizeof(c_group_info->c_candidate[0])); - if (c_group_info->c_group && c_group_info->c_candidate) { - c_group_info->max_num_c_groups = num_atoms/2; - c_group_info->max_num_candidates = num_atoms; - } else { - bError = BNS_OUT_OF_RAM; /* error: out of RAM */ - /*printf("BNS_OUT_OF_RAM-1: num_at=%d, c_gr=%lx c_can=%lx\n", num_atoms, c_group_info->c_group, c_group_info->c_candidate);*/ - goto exit_function; - } - } - - if ( *pbTautFlags & TG_FLAG_TEST_TAUT__SALTS ) { - if ( t_group_info ) { - /* salt groups memory allocation */ - s_group_info->s_candidate = (S_CANDIDATE*)inchi_calloc(num_atoms, sizeof(s_group_info->s_candidate[0])); - if (s_group_info->s_candidate) { - s_group_info->max_num_candidates = num_atoms; - } else { - bError = BNS_OUT_OF_RAM; /* error: out of RAM */ - /*printf("BNS_OUT_OF_RAM-2\n");*/ - goto exit_function; - } - } - } - if ( t_group_info ) { - if ( t_group_info->tGroupNumber ) - inchi_free( t_group_info->tGroupNumber ); - t_group_info->tGroupNumber = (AT_NUMB *)inchi_calloc( 2*num_atoms+1, sizeof(t_group_info->tGroupNumber[0]) ); - if ( !t_group_info->tGroupNumber ) { - /*printf("BNS_OUT_OF_RAM-9\n");*/ - bError = BNS_OUT_OF_RAM; /* error: out of RAM */ - goto exit_function; - } - num = t_group_info->tni.nNumRemovedExplicitH; - memset ( &t_group_info->tni, 0, sizeof(t_group_info->tni) ); - t_group_info->tni.nNumRemovedExplicitH = num; - } - - - -/* -again: -*/ - /* allocate Balanced Network Data Strucures; replace Alternating bonds with Single */ - if ( (pBNS = AllocateAndInitBnStruct( at, num_atoms, BNS_ADD_ATOMS, BNS_ADD_EDGES, max_altp, &num_changed_bonds )) && - (pBD = AllocateAndInitBnData( pBNS->max_vertices )) ) - { - - - pBNS->pbTautFlags = pbTautFlags; /* carry through all functions */ - pBNS->pbTautFlagsDone = pbTautFlagsDone; /* carry through all functions */ - -#if ( BNS_PROTECT_FROM_TAUT == 1 ) - /* protect bonds to acetyl and nitro */ - SetForbiddenEdges( pBNS, at, num_atoms, BNS_EDGE_FORBIDDEN_MASK ); -#endif - - - /* set bonds in case of input "aromatic" bonds or multiple radicals */ - -#ifdef FIX_AROM_RADICAL /* Added 2011-05-09 IPl */ - if ( n_arom_radicals ) - { - ret = BnsAdjustFlowBondsRad( pBNS, pBD, at, num_atoms ); - if ( stored_radicals ) - { - for ( i = 0; i < num_atoms; i ++ ) - { - if ( stored_radicals[i] ) - { - at[i].radical = stored_radicals[i]; - at[i].num_H--; - } - } - } - if ( IS_BNS_ERROR( ret ) ) - { - bError = ret; - goto exit_function; - } - } -#endif - ret = BnsAdjustFlowBondsRad( pBNS, pBD, at, num_atoms ); - - - /* (here pair(s) of radicals could have disappeared from the atoms) */ - if ( IS_BNS_ERROR( ret ) ) - { - bError = ret; - goto exit_function; - } - pBNS->tot_st_flow += 2*ret; - - - - /*return 0;*/ /* debug */ - nOrigDelta = ret; - if ( pBNS->tot_st_cap > pBNS->tot_st_flow ) { - /* has radical */ - bChangeFlow |= BNS_EF_SET_NOSTEREO; - } - /******************************************************************** - * Remove protons from NH(+), but not PH(+) - * Add protons to COO(-) etc. - * or remove protons from COOH etc to make the organic part neutral - * Note: for now (-) from N(-) can be only canceled or moved to -C=O - ********************************************************************/ - if ( ( *pbTautFlags & TG_FLAG_VARIABLE_PROTONS ) && t_group_info && - mark_at_type( at, num_atoms, nAtTypeTotals ) && - nAtTypeTotals[ATTOT_NUM_CHARGES] ) { - /* - the structure is simple to neutralize if it yields exactly - num[H(+)] = num[N,P H(+)] - num[N,S,O(-)] = num[=C-O(-)] + num[C-S(-)] + num[N(-)] + num[other O(-), S(-)] - - and n(p) = num[H(+)] - num[N,S,O(-)] (no protons, no negative N,O,S condition) - - Additional check is needed if: - min{num[N,PH], num[N,P(+), not onium]} > 0 - => possibility to yield more H(+) - - min_charge = orig_charge(P,N,O,S) - n(p) - n(OH,SH) - max_charge = orig_charge(P,N,O,S) - n(p) + n(O,S,N(-)) - */ - - - nNumOrigTotAtoms = t_group_info->tni.nNumRemovedExplicitH + num_atoms; - pAATG->nAtTypeTotals = nAtTypeTotals; - pAATG->t_group_info = t_group_info; -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) - pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP; -#endif - /***********************************************************/ - /* */ - /* ( D E ) P R O T O N A T I O N */ - /* */ - /***********************************************************/ - ret = RemoveNPProtonsAndAcidCharges( at, num_atoms, pAATG, pBNS, pBD ); -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) - pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP; -#endif - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - goto exit_function; - } - if ( t_group_info->tni.bNormalizationFlags ) { - SetInitCapFlowToCurrent( pBNS ); - if ( at_fixed_bonds_out ) { - /* copy modified initial tautomeric structure for displaying - Warning: implicit H counts in at_fixed_bonds_out include explicit Hs */ - memcpy( at_fixed_bonds_out, at, nNumOrigTotAtoms*sizeof(at_fixed_bonds_out[0]) ); - /* -- will be done in FillOutInputInfAtom() -- - RemoveExcessiveImplicitH( num_atoms, t_group_info->tni.nNumRemovedExplicitH, at_fixed_bonds_out ); - */ - } - } - } - /****************** initial bonds normalization ***************/ - if ( *pbTautFlags & TG_FLAG_MOVE_POS_CHARGES ) { - /******************* find moveable positive charges **********************/ - do { /* cycling while ret>0 added 2004-06-04 */ -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) - pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP; - CorrectFixing_NH_NH_Bonds( pBNS, at, num_atoms ); -#endif - ret = MarkChargeGroups ( at, num_atoms, c_group_info, t_group_info, pBNS, pBD ); -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) - pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP; -#endif - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - goto exit_function; - } - if ( ret ) { - nChanges += ret; - ret2 = AddCGroups2BnStruct( pBNS, at, num_atoms, c_group_info ); - if ( IS_BNS_ERROR( ret2 ) ) { - bError = ret2; - goto exit_function; - } - *pbTautFlagsDone |= TG_FLAG_MOVE_POS_CHARGES_DONE; - } - } while ( ret > 0 ); -#if ( BNS_RAD_SEARCH == 1 ) -#else - /* moveable charges may allow to cancel radicals -- check it */ - if ( pBNS->tot_st_cap > pBNS->tot_st_flow ) { - ret = BnsAdjustFlowBondsRad( pBNS, pBD, at, num_atoms ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - goto exit_function; - } - if ( ret > 0 ) { - /* - pBNS->tot_st_flow += 2*ret; - ret = ReInitBnStruct( pBNS, at, num_atoms, 1 ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - goto exit_function; - } - */ - bError = BNS_RADICAL_ERR; - goto exit_function; - } - } -#endif - } - /************************************************************************/ - /******** test bonds for bond tautomerism **************/ - /******** replace moveable bonds with "alternating" bonds **************/ - /************************************************************************/ - ret = BnsTestAndMarkAltBonds( pBNS, pBD, at, num_atoms, fcd, bChangeFlow, 0 ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - goto exit_function; - } - nChanges += ret; - /*********************** end of initial bonds normalization *************/ - nTotChanges = 0; - /* check for tautomerism */ - /* find new tautomer groups */ - salt_pass = 0; - salt_step = 0; - salt_found = 0; - - /*************************************************************/ - /* */ - /* M A I N C Y C L E B E G I N */ - /* */ - /*************************************************************/ - - do { - nTotChanges += nChanges; - nChanges = 0; - taut_pass = 0; - - /**************** regular bond/H/(-)/positive charges tautomerism cycle begin **************/ - do { - taut_pass ++; - for ( taut_found = 0; 0 < (ret=MarkTautomerGroups( at, num_atoms, t_group_info, c_group_info, pBNS, pBD )); taut_found ++ ) - ; - if ( ret < 0 ) { - bError = ret; - } - if ( taut_found && !salt_pass ) { - *pbTautFlagsDone |= TG_FLAG_TEST_TAUT__ATOMS_DONE; - } - if ( taut_found || salt_found ) { - /****************** repeat bonds normalization ***************/ - ret = ReInitBnStructAddGroups( pBNS, at, num_atoms, t_group_info, c_group_info ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - goto exit_function; - } -#if ( BNS_RAD_SEARCH == 1 ) -#else - /* discovered moveable charges and H-atoms may allow to cancel radicals */ - if ( pBNS->tot_st_cap > pBNS->tot_st_flow ) { - ret = BnsAdjustFlowBondsRad( pBNS, pBD, at, num_atoms ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - goto exit_function; - } - if ( ret > 0 ) { - /* - pBNS->tot_st_flow += 2*ret; - ret = ReInitBnStruct( pBNS, at, num_atoms, 1 ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - goto exit_function; - } - */ - bError = BNS_RADICAL_ERR; - goto exit_function; - } - } -#endif - /****************** update bonds normalization ***************/ - if ( *pbTautFlags & TG_FLAG_MOVE_POS_CHARGES ) { - /******************* find moveable charges ***************/ - do { /* cycling while ret>0 added 2004-06-04 */ -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) - pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP; - CorrectFixing_NH_NH_Bonds( pBNS, at, num_atoms ); -#endif - ret = MarkChargeGroups ( at, num_atoms, c_group_info, t_group_info, pBNS, pBD ); -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) - pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP; -#endif - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - goto exit_function; - } - nChanges+= ret; - if ( ret > 0 ) { - ret2 = ReInitBnStructAddGroups( pBNS, at, num_atoms, t_group_info, c_group_info ); - if ( IS_BNS_ERROR( ret2 ) ) { - bError = ret2; - goto exit_function; - } - *pbTautFlagsDone |= TG_FLAG_MOVE_POS_CHARGES_DONE; - } - } while ( ret > 0 ); - } - - /************************************************************************/ - /******** find moveable bonds: **************/ - /******** test bonds for bond tautomerism **************/ - /******** replace moveable bonds with "alternating" bonds **************/ - /************************************************************************/ - ret = BnsTestAndMarkAltBonds( pBNS, pBD, at, num_atoms, fcd, bChangeFlow, 0 ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - goto exit_function; - } - nChanges+= ret; - /****************** end of update bonds normalization ***************/ - } - salt_found = 0; - - } while( taut_found && !bError ); - /**************** regular bond/H/(-)/positive charges tautomerism cycle end **************/ - - if ( bError ) { - break; - } - - /******************* 'salt' tautomerism permitted *************************/ - if ( *pbTautFlags & TG_FLAG_TEST_TAUT__SALTS ) { - - if ( *pbTautFlags & TG_FLAG_TEST_TAUT2_SALTS ) { - /*********** requested one or more "salt" attachement migrartion test ********/ - if ( !nChanges && salt_pass && salt_step ) { - break; /* done */ - } - if ( !salt_step ) { /* salt step 0: process one attachment migrartion */ -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) - pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP; - CorrectFixing_NH_NH_Bonds( pBNS, at, num_atoms ); -#endif - salt_found = MarkSaltChargeGroups ( at, num_atoms, s_group_info, - t_group_info, c_group_info, pBNS, pBD ); -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) - pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP; -#endif - if ( salt_found < 0 ) { - bError = salt_found; - break; - } else - if ( salt_found > 0 ) { - *pbTautFlagsDone |= TG_FLAG_TEST_TAUT__SALTS_DONE; - } - salt_step = !salt_found; - /* if new 'salt' atoms have been found then repeat regular taut. search - * MarkTautomerGroups() and do not perform salt step 1 - * if new 'salt' atoms have NOT been found then switch to salt step 1 - * and never repeat salt step 0 for the current structure - */ - } - if ( salt_step /*|| - (t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT)*/ ) { - /* salt step 1: process more than one attachment migration */ -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) - pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP; - CorrectFixing_NH_NH_Bonds( pBNS, at, num_atoms ); -#endif - salt_found = MarkSaltChargeGroups2 ( at, num_atoms, s_group_info, - t_group_info, c_group_info, pBNS, pBD ); -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) - pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP; -#endif - if ( salt_found < 0 ) { - bError = salt_found; - break; - } else - if ( salt_found == 1 || salt_found == 5 ) { - *pbTautFlagsDone |= TG_FLAG_TEST_TAUT2_SALTS_DONE; - if ( salt_found == 5 ) { - *pbTautFlagsDone |= TG_FLAG_TEST_TAUT3_SALTS_DONE; - } - /* salt_found == 2 => only negative charges involved */ - } - } - - salt_pass ++; - - } else { /* !( *pbTautFlags & TG_FLAG_TEST_TAUT2_SALTS ) */ - /*************** requested only one attachement migration test **********/ - if ( !nChanges && salt_pass ) { /* one attachment migration */ - break; - } /* salt step 0: process one attachment migration */ -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) - pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP; - CorrectFixing_NH_NH_Bonds( pBNS, at, num_atoms ); -#endif - salt_found = MarkSaltChargeGroups ( at, num_atoms, s_group_info, - t_group_info, c_group_info, pBNS, pBD ); -#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) - pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP; -#endif - if ( salt_found < 0 ) { - bError = salt_found; - break; - } else - if ( salt_found > 0 ) { - *pbTautFlagsDone |= TG_FLAG_TEST_TAUT__SALTS_DONE; - } - salt_pass ++; - } /* ( *pbTautFlags & TG_FLAG_TEST_TAUT2_SALTS ) */ - } /* ( *pbTautFlags & TG_FLAG_TEST_TAUT__SALTS ) */ - - } while ( salt_found && !bError ); - /*************************************************************/ - /* */ - /* M A I N C Y C L E E N D */ - /* */ - /*************************************************************/ - - if ( *pbTautFlags & TG_FLAG_MERGE_TAUT_SALTS ) { - if ( !bError && s_group_info /*&& s_group_info->num_candidates > 0*/ ) { - ret = MergeSaltTautGroups( at, num_atoms, s_group_info, - t_group_info, c_group_info, pBNS ); - if ( ret < 0 ) { - bError = ret; - } else - if ( ret > 0 ) { - *pbTautFlagsDone |= TG_FLAG_MERGE_TAUT_SALTS_DONE; - } - } - } - if ( !bError && t_group_info && - (t_group_info->bTautFlags & TG_FLAG_VARIABLE_PROTONS) && - (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE|TG_FLAG_FOUND_ISOTOPIC_H_DONE)) ) { - ret = MakeIsotopicHGroup( at, num_atoms, s_group_info, t_group_info ); - if ( ret < 0 ) { - bError = ret; - } - } - /* success */ - remove_alt_bond_marks( at, num_atoms); - - /************************************************ - * Temporarily ignore all non-alternating bonds - * and mark non-ring alt bonds non-stereogenic - ************************************************/ - - ReInitBnStructForAltBns( pBNS, at, num_atoms, 0 ); - MarkRingSystemsAltBns( pBNS, 0 ); - MarkNonStereoAltBns( pBNS, at, num_atoms, 0 ); -#if ( FIX_EITHER_DB_AS_NONSTEREO == 1 ) - /* second time unknown ("Either") alternating bonds are treated as non-stereogenic */ - /* stereobonds bonds that lost stereo get "Either" stereo_type */ - ReInitBnStructForAltBns( pBNS, at, num_atoms, 1 ); - MarkRingSystemsAltBns( pBNS, 1 ); - MarkNonStereoAltBns( pBNS, at, num_atoms, 1 ); -#endif - } else { - bError = BNS_OUT_OF_RAM; - /*printf("BNS_OUT_OF_RAM-3\n");*/ - } - -exit_function: - - pBNS = DeAllocateBnStruct( pBNS ); - pBD = DeAllocateBnData( pBD ); -/*#if ( MOVE_CHARGES == 1 )*/ - if ( c_group_info ) { - if ( c_group_info->c_group ) { - inchi_free( c_group_info->c_group ); - } - if ( c_group_info->c_candidate ) { - inchi_free( c_group_info->c_candidate ); - } - } -/*#endif*/ - if ( s_group_info && s_group_info->s_candidate ) { - inchi_free( s_group_info->s_candidate ); - } - if ( pAATG && pAATG->nMarkedAtom ) { - inchi_free( pAATG->nMarkedAtom ); - qzfree( pAATG->nEndPoint ); - /*qzfree( pAATG->nAtTypeTotals );*/ /* nAtTypeTotals is a stack array */ - } - if ( t_group_info && t_group_info->tGroupNumber ) { - inchi_free( t_group_info->tGroupNumber ); - t_group_info->tGroupNumber = NULL; - } - - if ( !bError && num_atoms == 1 && at[0].at_type == ATT_PROTON && t_group_info && !t_group_info->tni.nNumRemovedExplicitH ) { - /* remove single isolated proton */ - t_group_info->tni.nNumRemovedProtons = 1; - t_group_info->tni.bNormalizationFlags |= FLAG_PROTON_SINGLE_REMOVED; - if ( at[0].iso_atw_diff ) { - t_group_info->tni.nNumRemovedProtonsIsotopic[at[0].iso_atw_diff-1] ++; - } - if ( at_fixed_bonds_out ) { - memcpy( at_fixed_bonds_out, at, num_atoms*sizeof(at_fixed_bonds_out[0]) ); - } - /*num_atoms --;*/ - } - - - /* - Additional currently unused info: - - nOrigDelta > 0: original structure has been changed - due to fiund augmenting path(s) - nChanges > 0: either alt. bonds or taut. groups have been found - */ - -#ifdef FIX_AROM_RADICAL /* Added 2011-05-09 IPl */ - if ( stored_radicals ) - inchi_free( stored_radicals ); -#endif - - - return bError? bError : num_atoms; /* ret = 0 => success, any other => error */ - -} - - - -/*********************************************************************************/ -int nMaxFlow2Check( BN_STRUCT *pBNS, int iedge ) -{ - BNS_EDGE *pEdge = pBNS->edge + iedge; - int nMaxFlow = (pEdge->cap & EDGE_FLOW_MASK); /* edge cap */ - - if ( nMaxFlow > MAX_BOND_EDGE_CAP ) { - nMaxFlow = MAX_BOND_EDGE_CAP; - } - return nMaxFlow; -} - - - -/*********************************************************************************/ -int nCurFlow2Check( BN_STRUCT *pBNS, int iedge ) -{ - BNS_EDGE *pEdge = pBNS->edge + iedge; - int nCurFlow = (pEdge->flow & EDGE_FLOW_MASK); /* edge flow */ - return nCurFlow; -} - - - -/*********************************************************************************/ -int nMinFlow2Check( BN_STRUCT *pBNS, int iedge ) -{ - BNS_EDGE *pEdge = pBNS->edge + iedge; - Vertex v1 = pEdge->neighbor1; - Vertex v2 = v1 ^ pEdge->neighbor12; - int f12 = (pEdge->flow & EDGE_FLOW_MASK); - int rescap1, rescap2, rescap12, i, iedge_i; - - if ( f12 > 0 ) { - for ( i = 0, rescap1 = 0; i < pBNS->vert[v1].num_adj_edges; i ++ ) { - iedge_i = pBNS->vert[v1].iedge[i]; - if ( iedge_i == iedge ) - continue; - rescap1 += (pBNS->edge[iedge_i].cap & EDGE_FLOW_MASK) - (pBNS->edge[iedge_i].flow & EDGE_FLOW_MASK); - } - for ( i = 0, rescap2 = 0; i < pBNS->vert[v2].num_adj_edges; i ++ ) { - iedge_i = pBNS->vert[v2].iedge[i]; - if ( iedge_i == iedge ) - continue; - rescap2 += (pBNS->edge[iedge_i].cap & EDGE_FLOW_MASK) - (pBNS->edge[iedge_i].flow & EDGE_FLOW_MASK); - } - rescap12 = inchi_min( rescap1, rescap2 ); - rescap12 = inchi_min( rescap12, f12 ); - return f12-rescap12; - } - return 0; -} - - - -/**********************************************************************************/ -int bSetBondsAfterCheckOneBond( BN_STRUCT *pBNS, BNS_FLOW_CHANGES *fcd, int nTestFlow, inp_ATOM *at, int num_atoms, int bChangeFlow0 ) -{ - int ifcd, iedge, new_flow, ret_val, nChanges = 0, bError=0; - int bChangeFlow; - Vertex v1, v2; - int ineigh1, ineigh2; - BNS_EDGE *pEdge; - - bChangeFlow0 &= ~BNS_EF_CHNG_RSTR; /* do not change pEdge flow in SetBondType */ - if ( !bChangeFlow0 ) - return 0; - - bChangeFlow = (bChangeFlow0 & ~BNS_EF_SET_NOSTEREO); - /* find the next to the last changed */ - if ( bChangeFlow0 & BNS_EF_SET_NOSTEREO ) { - - for ( ifcd = 0; NO_VERTEX != (iedge = fcd[ifcd].iedge); ifcd ++ ) { - iedge = fcd[ifcd].iedge; - pEdge = pBNS->edge + iedge; - if ( !pEdge->pass ) { - continue; - } - - if ( !ifcd && nTestFlow>=0 ) { - new_flow = nTestFlow; - } else { - new_flow = (int)pEdge->flow; - } - - v1 = pEdge->neighbor1; - v2 = pEdge->neighbor12 ^ v1; - if ( v1 < num_atoms && v2 < num_atoms && new_flow != pEdge->flow0 ) { - if ( (pBNS->vert[v1].st_edge.cap0 == pBNS->vert[v1].st_edge.flow0) != - (pBNS->vert[v1].st_edge.cap == pBNS->vert[v1].st_edge.flow ) || - (pBNS->vert[v2].st_edge.cap0 == pBNS->vert[v2].st_edge.flow0) != - (pBNS->vert[v2].st_edge.cap == pBNS->vert[v2].st_edge.flow )) { - bChangeFlow |= BNS_EF_SET_NOSTEREO; - nChanges |= BNS_EF_SET_NOSTEREO; - } - } - } - - } else { - for ( ifcd = 0; NO_VERTEX != (iedge = fcd[ifcd].iedge); ifcd ++ ) - ; - } - - /* restore in reversed order to correctly handle vertex changed more than once */ - for ( ifcd -= 1; 0 <= ifcd; ifcd -- ) { - - iedge = fcd[ifcd].iedge; - pEdge = pBNS->edge + iedge; - if ( !pEdge->pass ) { - continue; - } - - if ( !ifcd && nTestFlow>=0 ) { - new_flow = nTestFlow; - } else { - new_flow = (int)pEdge->flow; - } - - v1 = pEdge->neighbor1; - v2 = pEdge->neighbor12 ^ v1; - if ( v1 < num_atoms && v2 < num_atoms && bChangeFlow && new_flow != pEdge->flow0 ) { - ineigh1 = pEdge->neigh_ord[0]; - ineigh2 = pEdge->neigh_ord[1]; - ret_val = SetAtomBondType( pEdge, &at[v1].bond_type[ineigh1], &at[v2].bond_type[ineigh2], new_flow-pEdge->flow0, bChangeFlow ); - if ( !IS_BNS_ERROR( ret_val ) ) { - nChanges |= (ret_val > 0); - } else { - bError = ret_val; - } - } - pEdge->pass = 0; - } - return bError? bError : nChanges; -} - - - -/**********************************************************************************/ -int bRestoreFlowAfterCheckOneBond( BN_STRUCT *pBNS, BNS_FLOW_CHANGES *fcd ) -{ - int ifcd, iedge; - Vertex v1, v2; - BNS_EDGE *pEdge; - - /* find the next to the last changed */ - for ( ifcd = 0; NO_VERTEX != (iedge = fcd[ifcd].iedge); ifcd ++ ) - ; - - /* restore in reversed order to correctly handle vertex changed more than once */ - for ( ifcd -= 1; 0 <= ifcd; ifcd -- ) { - - /* restore edge flow & cap */ - iedge = fcd[ifcd].iedge; - pEdge = pBNS->edge + iedge; - pEdge->flow = fcd[ifcd].flow; - pEdge->cap = fcd[ifcd].cap; - pEdge->pass = 0; - - /* restore st-flow, cap */ - if ( NO_VERTEX != (v1 = fcd[ifcd].v1) ) { - pBNS->vert[v1].st_edge.flow = fcd[ifcd].flow_st1; - pBNS->vert[v1].st_edge.cap = fcd[ifcd].cap_st1; - pBNS->vert[v1].st_edge.pass = 0; - } - if ( NO_VERTEX != (v2 = fcd[ifcd].v2) ) { - pBNS->vert[v2].st_edge.flow = fcd[ifcd].flow_st2; - pBNS->vert[v2].st_edge.cap = fcd[ifcd].cap_st2; - pBNS->vert[v2].st_edge.pass = 0; - } - } - return 0; -} - - - -/**********************************************************************************/ -int bSetFlowToCheckOneBond( BN_STRUCT *pBNS, int iedge, int flow, BNS_FLOW_CHANGES *fcd ) -{ - BNS_EDGE *pEdge = pBNS->edge + iedge; - int f12 = (pEdge->flow & EDGE_FLOW_MASK); /* the original flow */ - int ifcd = 0; - int nDots = 0; - int i, iedge_i; - - fcd[ifcd].iedge = NO_VERTEX; - - if ( f12 < flow ) { - /* Increase edge flow: Grab flow from the neighbors and delete it: set flow12=cap12 = 0 */ - /************************************************************************************/ - /* For example, simulate a new fixed double bond in place of a single bond and */ - /* creates ONE or NONE (in case of a radical on adjacent atom) augmenting paths and */ - /* makes it impossible for the BNS to set same flow as it originally was */ - /************************************************************************************/ - Vertex v1 = pEdge->neighbor1; - Vertex v2 = v1 ^ pEdge->neighbor12; - Vertex v_i; /* neighbor of v1 or v2 */ - BNS_EDGE *pEdge_i; - int delta1, delta2, f, st_edge_rescap; - - if ( (pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK) < flow || - (pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK) < flow ) { - return BNS_CANT_SET_BOND; - } - if ( (pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK) < f12 || - (pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK) < f12 ) { - return BNS_CAP_FLOW_ERR; - } - - - fcd[ifcd].iedge = iedge; - fcd[ifcd].flow = pEdge->flow; - fcd[ifcd].cap = pEdge->cap; - - fcd[ifcd].v1 = v1; - fcd[ifcd].flow_st1 = pBNS->vert[v1].st_edge.flow; - fcd[ifcd].cap_st1 = pBNS->vert[v1].st_edge.cap; - - fcd[ifcd].v2 = v2; - fcd[ifcd].flow_st2 = pBNS->vert[v2].st_edge.flow; - fcd[ifcd].cap_st2 = pBNS->vert[v2].st_edge.cap; - - fcd[++ifcd].iedge = NO_VERTEX; /* mark the end of the fcd[] data */ - pEdge->pass |= 64; - - delta1 = delta2 = flow - f12; - - if ( f12 > 0 ) { - /* remove old edge flow from the flow and cap of the adjacent vertices' st-edges */ - pBNS->vert[v1].st_edge.cap = ((pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK)-f12) | (pBNS->vert[v1].st_edge.cap & ~EDGE_FLOW_ST_MASK); - pBNS->vert[v2].st_edge.cap = ((pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK)-f12) | (pBNS->vert[v2].st_edge.cap & ~EDGE_FLOW_ST_MASK); - pBNS->vert[v1].st_edge.flow = ((pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK)-f12) | (pBNS->vert[v1].st_edge.flow & ~EDGE_FLOW_ST_MASK); - pBNS->vert[v2].st_edge.flow = ((pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK)-f12) | (pBNS->vert[v2].st_edge.flow & ~EDGE_FLOW_ST_MASK); - /* delete current edge flow and capacity */ - pEdge->flow = (pEdge->flow & ~EDGE_FLOW_MASK); - } - pEdge->cap = (pEdge->cap & ~EDGE_FLOW_MASK); - - /* grab the adjacent vertex1 radical (st_edge_rescap) if it exists */ - st_edge_rescap = (pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK) - (pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK); - while ( st_edge_rescap && delta1 ) { - st_edge_rescap --; /* grab the radical */ - delta1 --; - pBNS->vert[v1].st_edge.cap = ((pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v1].st_edge.cap & ~EDGE_FLOW_ST_MASK); - nDots --; - } - /* grab the adjacent vertex2 radical (st_edge_rescap) if it exists */ - st_edge_rescap = (pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK) - (pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK); - while ( st_edge_rescap && delta2 ) { - st_edge_rescap --; /* grab the radical */ - delta2 --; - pBNS->vert[v2].st_edge.cap = ((pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v2].st_edge.cap & ~EDGE_FLOW_ST_MASK); - nDots --; - } - /* grab flows from v1 neighbors */ - for ( i = 0; delta1 && i < pBNS->vert[v1].num_adj_edges; i ++ ) { - iedge_i = pBNS->vert[v1].iedge[i]; - if ( iedge_i == iedge ) - continue; - pEdge_i = pBNS->edge + iedge_i; - if ( IS_FORBIDDEN(pEdge_i->forbidden, pBNS) ) - continue; - f = (pEdge_i->flow & EDGE_FLOW_MASK); - if ( f ) { - v_i = pEdge_i->neighbor12 ^ v1; - - fcd[ifcd].iedge = iedge_i; - fcd[ifcd].flow = pEdge_i->flow; - fcd[ifcd].cap = pEdge_i->cap; - - fcd[ifcd].v1 = v_i; - fcd[ifcd].flow_st1 = pBNS->vert[v_i].st_edge.flow; - fcd[ifcd].cap_st1 = pBNS->vert[v_i].st_edge.cap; - - fcd[ifcd].v2 = NO_VERTEX; - fcd[ifcd].flow_st2 = 0; - fcd[ifcd].cap_st2 = 0; - - fcd[++ifcd].iedge = NO_VERTEX; /* mark the end of the fcd[] data */ - pEdge_i->pass |= 64; - - while ( f && delta1 ) { - f --; - delta1 --; - pEdge_i->flow = ((pEdge_i->flow & EDGE_FLOW_MASK) - 1) | (pEdge_i->flow & ~EDGE_FLOW_MASK); - pBNS->vert[v_i].st_edge.flow = ((pBNS->vert[v_i].st_edge.flow & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v_i].st_edge.flow & ~EDGE_FLOW_ST_MASK); - /* next 2 lines added 01-22-2002 */ - pBNS->vert[v1].st_edge.cap = ((pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v1].st_edge.cap & ~EDGE_FLOW_ST_MASK); - pBNS->vert[v1].st_edge.flow = ((pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v1].st_edge.flow & ~EDGE_FLOW_ST_MASK); - - nDots ++; - } - - } - } - /* grab flows from v2 neighbors */ - for ( i = 0; delta2 && i < pBNS->vert[v2].num_adj_edges; i ++ ) { - iedge_i = pBNS->vert[v2].iedge[i]; - if ( iedge_i == iedge ) - continue; - pEdge_i = pBNS->edge + iedge_i; - if ( IS_FORBIDDEN(pEdge_i->forbidden, pBNS) ) - continue; - f = (pEdge_i->flow & EDGE_FLOW_MASK); - if ( f ) { - v_i = pEdge_i->neighbor12 ^ v2; - - fcd[ifcd].iedge = iedge_i; - fcd[ifcd].flow = pEdge_i->flow; - fcd[ifcd].cap = pEdge_i->cap; - - fcd[ifcd].v1 = v_i; - fcd[ifcd].flow_st1 = pBNS->vert[v_i].st_edge.flow; - fcd[ifcd].cap_st1 = pBNS->vert[v_i].st_edge.cap; - - fcd[ifcd].v2 = NO_VERTEX; - fcd[ifcd].flow_st2 = 0; - fcd[ifcd].cap_st2 = 0; - - fcd[++ifcd].iedge = NO_VERTEX; /* mark the end of the fcd[] data */ - pEdge_i->pass |= 64; - - while ( f && delta2 ) { - f --; - delta2 --; - pEdge_i->flow = ((pEdge_i->flow & EDGE_FLOW_MASK) - 1) | (pEdge_i->flow & ~EDGE_FLOW_MASK); - pBNS->vert[v_i].st_edge.flow = ((pBNS->vert[v_i].st_edge.flow & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v_i].st_edge.flow & ~EDGE_FLOW_ST_MASK); - /* next 2 lines added 01-22-2002 */ - pBNS->vert[v2].st_edge.cap = ((pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v2].st_edge.cap & ~EDGE_FLOW_ST_MASK); - pBNS->vert[v2].st_edge.flow = ((pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v2].st_edge.flow & ~EDGE_FLOW_ST_MASK); - - nDots ++; - } - - } - } - if ( delta1 || delta2 ) { - return BNS_CANT_SET_BOND; - } - } - - if ( f12 >= flow ) { - /* Decrease edge flow: Redirect flow to the neighbors and delete it on the edge: set flow12=cap12 = 0 */ - /* f12==flow fixes flow through the edge so that BNS cannot change it */ - /**********************************************************************************************/ - /* For example, simulate a removal of a double bond and create ONE or NONE augmenting path */ - /* Make it impossible for BNS to set same flow as it originally was */ - /**********************************************************************************************/ - Vertex v1 = pEdge->neighbor1; - Vertex v2 = (v1 ^ pEdge->neighbor12); - int delta; - /* if NOT (st-cap >= st-flow >= f12 >= flow) then error in the BN structure */ - if ( (pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK) < f12 || - (pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK) < f12 || - (pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK) < flow || - (pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK) < flow ) { - return BNS_CAP_FLOW_ERR; - } - fcd[ifcd].iedge = iedge; - fcd[ifcd].flow = pEdge->flow; - fcd[ifcd].cap = pEdge->cap; - - fcd[ifcd].v1 = v1; - fcd[ifcd].flow_st1 = pBNS->vert[v1].st_edge.flow; - fcd[ifcd].cap_st1 = pBNS->vert[v1].st_edge.cap; - - fcd[ifcd].v2 = v2; - fcd[ifcd].flow_st2 = pBNS->vert[v2].st_edge.flow; - fcd[ifcd].cap_st2 = pBNS->vert[v2].st_edge.cap; - - fcd[++ifcd].iedge = NO_VERTEX; /* mark the end of the fcd[] data */ - pEdge->pass |= 64; - - delta = f12 - flow; - /* remove current edge flow from st-edges */ - /* -- seem to be a bug -- - pBNS->vert[v1].st_edge.flow = ((pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK)-delta) | (pBNS->vert[v1].st_edge.flow & ~EDGE_FLOW_ST_MASK); - pBNS->vert[v2].st_edge.flow = ((pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK)-delta) | (pBNS->vert[v2].st_edge.flow & ~EDGE_FLOW_ST_MASK); - */ - - /* replacement to the above 2 lines 01-16-2002 */ - /* remove old edge flow from the flow of the adjacent vertices' st-edges */ - - pBNS->vert[v1].st_edge.flow = ((pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK)-f12) | (pBNS->vert[v1].st_edge.flow & ~EDGE_FLOW_ST_MASK); - pBNS->vert[v2].st_edge.flow = ((pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK)-f12) | (pBNS->vert[v2].st_edge.flow & ~EDGE_FLOW_ST_MASK); - - /* added 01-16-2002: reduce st-cap if new flow > 0 */ - /* remove new edge flow from the cap of the adjacent vertices' st-edges */ - pBNS->vert[v1].st_edge.cap = ((pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK)-flow) | (pBNS->vert[v1].st_edge.cap & ~EDGE_FLOW_ST_MASK); - pBNS->vert[v2].st_edge.cap = ((pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK)-flow) | (pBNS->vert[v2].st_edge.cap & ~EDGE_FLOW_ST_MASK); - - /* delete current edge flow and capacity */ - pEdge->flow = (pEdge->flow & ~EDGE_FLOW_MASK); - pEdge->cap = (pEdge->cap & ~EDGE_FLOW_MASK); - nDots = 2*delta; - } - return nDots; -} - - - -/**********************************************************************************/ -/* Connect new (fictitious, temporary) vertex to to nVertDoubleBond by a new edge */ -/* Add radical (set st-cap=1) to the new vertex, set cap=1 to the new edge */ -/* Add radical (set st-cap=1) to nVertSingleBond */ -/* Find augmenting path connecting new vertex to nVertSingleBond */ -/* This corresponds to moving H-atom from nVertSingleBond to nVertDoubleBond */ -/**********************************************************************************/ -int bAddNewVertex( BN_STRUCT *pBNS, int nVertDoubleBond, int nCap, int nFlow, int nMaxAdjEdges, int *nDots ) -{ - Vertex vlast = pBNS->num_vertices - 1; - Vertex vnew = pBNS->num_vertices; - Vertex v2 = nVertDoubleBond; - BNS_VERTEX *pVert2 = pBNS->vert + v2; /* pointer to an old vertex */ - BNS_VERTEX *pNewVert = pBNS->vert + vnew; /* pointer to a new vertex */ - - EdgeIndex iedge = pBNS->num_edges; - BNS_EDGE *pEdge = pBNS->edge + iedge; /* pointer to a new edge */ - - if ( iedge >= pBNS->max_edges || vnew >= pBNS->max_vertices ) { - return BNS_VERT_EDGE_OVFL; /* edges or vertices overflow */ - } - if ( (pBNS->vert[vlast].iedge - pBNS->iedge) + pBNS->vert[vlast].max_adj_edges + nMaxAdjEdges >= pBNS->max_iedges ) { - return BNS_VERT_EDGE_OVFL; /* iedges overflow */ - } - if ( pVert2->num_adj_edges >= pVert2->max_adj_edges || nMaxAdjEdges <= 0 ) { - return BNS_VERT_EDGE_OVFL; /* neighbors overflow */ - } - /* fill out the new edge, set its cap and flow, connect */ - /* memset( pEdge, 0, sizeof(*pEdge) ); */ - pEdge->cap = pEdge->cap0 = nCap; - pEdge->flow = pEdge->flow0 = nFlow; - pEdge->pass = 0; - pEdge->neighbor1 = v2; - pEdge->neighbor12 = v2 ^ vnew; - pEdge->forbidden = 0; - /* fill out the new vertex */ - /* memset( pNewVert, 0, sizeof(*pNewVert) ); */ - pNewVert->max_adj_edges = nMaxAdjEdges; - pNewVert->num_adj_edges = 0; - pNewVert->st_edge.cap0 = pNewVert->st_edge.cap = nCap; - pNewVert->st_edge.flow0 = pNewVert->st_edge.flow = nFlow; - pNewVert->st_edge.pass = 0; /* add initialization; added 2006-03-25 */ - pNewVert->iedge = pBNS->vert[vlast].iedge + pBNS->vert[vlast].max_adj_edges; - pNewVert->type = BNS_VERT_TYPE_TEMP; - *nDots += nCap - nFlow; - - pEdge->neigh_ord[v2>vnew] = pVert2->num_adj_edges; - pEdge->neigh_ord[v2num_adj_edges; - - /* connect new edge to v2 */ - pVert2->iedge[pVert2->num_adj_edges ++] = iedge; - /* connect new edge to vnew */ - pNewVert->iedge[pNewVert->num_adj_edges ++] = iedge; - - /* fix v2 flow and cap */ - *nDots -= (int)pVert2->st_edge.cap - (int)pVert2->st_edge.flow; - pVert2->st_edge.flow += nFlow; - if ( pVert2->st_edge.cap < pVert2->st_edge.flow ) { - pVert2->st_edge.cap = pVert2->st_edge.flow; - } - *nDots += (int)pVert2->st_edge.cap - (int)pVert2->st_edge.flow; - - pBNS->num_edges ++; - pBNS->num_vertices ++; - - return vnew; -} - - - -/*****************************************************************************************************/ -int AddNewEdge( BNS_VERTEX *p1, BNS_VERTEX *p2, BN_STRUCT *pBNS, int nEdgeCap, int nEdgeFlow ) -{ - int ip1 = p1 - pBNS->vert; - int ip2 = p2 - pBNS->vert; - int ie = pBNS->num_edges; - BNS_EDGE *e = pBNS->edge + ie; - /* debug: check bounds */ - if ( ip1 >= pBNS->max_vertices || ip1 < 0 || - ip2 >= pBNS->max_vertices || ip2 < 0 || - ie >= pBNS->max_edges || ie < 0 || - (p1->iedge - pBNS->iedge) < 0 || - (p1->iedge - pBNS->iedge) + p1->max_adj_edges > pBNS->max_iedges || - (p2->iedge - pBNS->iedge) < 0 || - (p2->iedge - pBNS->iedge) + p2->max_adj_edges > pBNS->max_iedges || - p1->num_adj_edges >= p1->max_adj_edges || - p2->num_adj_edges >= p2->max_adj_edges ) { - return BNS_VERT_EDGE_OVFL; - } - /* clear the edge */ - memset( e, 0, sizeof(*e) ); - /* connect */ - e->neighbor1 = inchi_min( ip1, ip2 ); - e->neighbor12 = ip1 ^ ip2; - p1->iedge[p1->num_adj_edges] = ie; - p2->iedge[p2->num_adj_edges] = ie; - e->neigh_ord[ip1 > ip2] = p1->num_adj_edges ++; - e->neigh_ord[ip1 < ip2] = p2->num_adj_edges ++; - e->cap = e->cap0 = nEdgeCap; - e->flow = e->flow0 = nEdgeFlow; - p1->st_edge.flow += nEdgeFlow; - p2->st_edge.flow += nEdgeFlow; - if ( p1->st_edge.cap < p1->st_edge.flow ) { - p1->st_edge.cap = p1->st_edge.flow; - } - if ( p2->st_edge.cap < p2->st_edge.flow ) { - p2->st_edge.cap = p2->st_edge.flow; - } - pBNS->num_edges ++; - return ie; -} - - - -/**********************************************************************************/ -BNS_IEDGE GetEdgeToGroupVertex( BN_STRUCT *pBNS, Vertex v1, AT_NUMB type) -{ - if ( v1 < pBNS->num_atoms ) { - Vertex v2; - BNS_EDGE *pEdge1; - BNS_VERTEX *pVert1 = pBNS->vert+v1; - int i = pVert1->num_adj_edges-1; - - while( 0 <= i ) { - pEdge1 = pBNS->edge + pVert1->iedge[i]; - v2 = pEdge1->neighbor12 ^ v1; - if ( pBNS->vert[v2].type == type ) { - return IS_FORBIDDEN(pEdge1->forbidden, pBNS)? NO_VERTEX : pVert1->iedge[i]; - } - i --; - } - return NO_VERTEX; /* not found t-group */ - } else - if ( v1 < pBNS->num_vertices ) { - return NO_VERTEX; - } - return BNS_VERT_EDGE_OVFL; -} - - - -/**********************************************************************************/ -Vertex GetGroupVertex(BN_STRUCT *pBNS, Vertex v1, AT_NUMB type) -{ - if ( v1 < pBNS->num_atoms ) { - Vertex v2; - BNS_EDGE *pEdge1; - BNS_VERTEX *pVert1 = pBNS->vert+v1; - int i = pVert1->num_adj_edges-1; - - AT_NUMB type2; - - if ( type == BNS_VERT_TYPE_ENDPOINT ) - type2 = BNS_VERT_TYPE_TGROUP; - else - if ( type == BNS_VERT_TYPE_C_POINT ) - type2 = BNS_VERT_TYPE_C_GROUP; - else - type2 = 0; - - if ( (pVert1->type & type) == type ) { - while( 0 <= i ) { - pEdge1 = pBNS->edge + pVert1->iedge[i]; - v2 = pEdge1->neighbor12 ^ v1; - if ( pBNS->vert[v2].type == type2 ) { - if ( IS_FORBIDDEN(pEdge1->forbidden, pBNS) ) { - return NO_VERTEX; - } - return v2; - } - i --; - } - } - return BNS_BOND_ERR; /* not found t-group */ - - } else - if ( v1 < pBNS->num_vertices ) { - return NO_VERTEX; - } - return BNS_VERT_EDGE_OVFL; -} - - - -/**********************************************************************************/ -int bAddStCapToAVertex( BN_STRUCT *pBNS, Vertex v1, Vertex v2, VertexFlow *nOldCapVertSingleBond, int *nDots, int bAdjacentDonors ) -{ - BNS_VERTEX *pVert1 = pBNS->vert + v1; - BNS_VERTEX *pVert; - BNS_EDGE *pEdge; - Vertex v; - int i, n; - VertexFlow nNewCap; - /* Change v1: increment its st-cap */ - n = 0; - nOldCapVertSingleBond[n++] = pVert1->st_edge.cap; - /*if ( pVert1->st_edge.cap == pVert1->st_edge.flow ) {*/ - pVert1->st_edge.cap ++; - *nDots += 1; - /*}*/ - /* increment caps of adjacent edges if - (1) the neighbor has st-cap != 0 and - (2) (edge cap==0) OR (nSumEdgeCap < pVert1->st_edge.cap && pVert->st_edge.flow > pVert1->st_edge.cap) - */ - if ( !(pVert1->type & BNS_VERT_TYPE_ANY_GROUP) ) { - /* - AT_NUMB nSumEdgeCap = 0; - for ( i = 0; i < pVert1->num_adj_edges; i ++ ) { - pEdge = pBNS->edge + pVert1->iedge[i]; - nSumEdgeCap += pEdge->cap; - } - */ - /* do not increment caps of t-group or c-group edges */ - for ( i = 0; i < pVert1->num_adj_edges; i ++ ) { - pEdge = pBNS->edge + pVert1->iedge[i]; - nOldCapVertSingleBond[n++] = pEdge->cap; /* save edge cap */ - v = pEdge->neighbor12 ^ v1; - if ( v == v2 && !bAdjacentDonors ) { - continue; - } - pVert = pBNS->vert + v; - if ( pVert->type & BNS_VERT_TYPE_ANY_GROUP ) - continue; - nNewCap = inchi_min(pVert->st_edge.cap, pVert1->st_edge.cap); - nNewCap = inchi_min(nNewCap, MAX_BOND_EDGE_CAP); - pEdge->cap = nNewCap; /* change edge cap */ - /* - if ( pVert->st_edge.cap > 0 && !pEdge->cap ) { - pEdge->cap ++; - } else - if ( pVert->st_edge.flow > pVert1->st_edge.cap && - pEdge->cap < MAX_BOND_EDGE_CAP && - nSumEdgeCap < pVert1->st_edge.cap ) { - pEdge->cap ++; - } - */ - } - } - - return n; /* number of elements in nOldCapVertSingleBond[*] */ -} - - - -/**********************************************************************************/ - -#define BNS_CHK_ALTP_NO_ALTPATH 0 -#define BNS_CHK_ALTP_SAME_TGROUP 1 -#define BNS_CHK_ALTP_SAME_VERTEX 2 -#define BNS_CHK_ALTP_SET_SUCCESS 4 - - - -/**********************************************************************************/ -int bSetBnsToCheckAltPath( BN_STRUCT *pBNS, int nVertDoubleBond, int nVertSingleBond, AT_NUMB type, - int path_type, ALT_PATH_CHANGES *apc, BNS_FLOW_CHANGES *fcd, int *nDots ) -{ - - if ( !pBNS->vert[nVertDoubleBond].st_edge.flow && - - !( path_type == ALT_PATH_MODE_REM2H_CHG || - path_type == ALT_PATH_MODE_ADD2H_CHG || - path_type == ALT_PATH_MODE_REM2H_TST || - path_type == ALT_PATH_MODE_ADD2H_TST ) - ) { - return BNS_CHK_ALTP_NO_ALTPATH; - } else { - - - Vertex vNew; - Vertex v1 = nVertSingleBond; - Vertex v2 = nVertDoubleBond; - - BNS_VERTEX *pVert1 = pBNS->vert + v1; - BNS_VERTEX *pVert2 = pBNS->vert + v2; - int n, bAdjacentDonors = 0; - int ifcd = 0; - - Vertex t1=NO_VERTEX; - Vertex t2=NO_VERTEX; - int iapc; - -/*#if ( TEST_REMOVE_S_ATOMS == 1 )*/ /* && ALT_PATH_MODE_4_SALT == path_type */ - if ( ( *pBNS->pbTautFlags & TG_FLAG_TEST_TAUT2_SALTS ) && - - ALT_PATH_MODE_4_SALT2 == path_type && - (BNS_VERT_TYPE_ENDPOINT & type) ) { - -/* ---------------------------------------------------------- - \ action | DB action (v2) | SB action (v1) | -vertex \ | accept H @ vertex | donate H @ vertex | -type \ | nVertDoubleBond | nVertSingleBond | -----------------+-------------------+-------------------+ - -ZH (v1) | error | -ZH(.) | -(cap>0 on edge | | increment | - except v1-v2) | | st-cap on Z | -----------------+-------------------+-------------------+ - =Z (v2) | =Z-(.) | error | - (st-flow>0) | add fict vertex | | - | with st-cap=1 | | -----------------+-------------------+-------------------+ - endpoint | T(.) | T-(.) | - of t-group | increment | add fict vertex | - represented | st-cap on T | with st-cap=1 | - by fictitious | | | - vertex T | | | ---------------------------------------------------------- -*/ - - int bSet_v1; /* indicator: v1 has been set */ - int bSet_v2; /* indicator: v2 has been set */ - int i; - - Vertex v1t = NO_VERTEX; - Vertex v2t = NO_VERTEX; - Vertex v1Act, v2Act; - Vertex v; - - memset( apc, 0, sizeof(*apc) ); - fcd[ifcd].iedge = NO_VERTEX; - *nDots = 0; - - if ( v1 == v2 ) { - return BNS_CHK_ALTP_SAME_VERTEX; - } - - /* check whether v1 has neighbors adjacent to - multiple bonds - */ - for ( i = 0, n = 0; i < pVert1->num_adj_edges; i ++ ) { - v = (pBNS->edge + pVert1->iedge[i])->neighbor12 ^ v1; /* v is adjacent to v1 */ - if ( v == v2 ) - continue; /* ignore connection to v2 */ - n += (pBNS->vert[v].st_edge.cap > 0); - } - if ( !n ) { - return BNS_CHK_ALTP_NO_ALTPATH; /* the vertex cannot have flow */ - } - - v1Act = v1; - v2Act = v2; - - /* find t-group that contains v1 */ - if ( (pVert1->type & type) == type ) { - v1t = GetGroupVertex(pBNS, v1, type); - if ( IS_BNS_ERROR( v1t ) ) { - return v1t; - } - if ( v1t != NO_VERTEX ) { - v1Act = v1t; - } - } - /* find t-group that contains v2 */ - if ( (pVert2->type & type) == type ) { - v2t = GetGroupVertex(pBNS, v2, type); - if ( IS_BNS_ERROR( v2t ) ) { - return v2t; - } - if ( v2t != NO_VERTEX ) { - v2Act = v2t; - } - } - if ( v1t != NO_VERTEX && v1t == v2t ) { - return BNS_CHK_ALTP_SAME_TGROUP; - } - - bSet_v1 = bSet_v2 = 0; - /* create new edges adjacent to v1t or v2 */ - iapc = 0; - if ( v1t != NO_VERTEX ) { - /* create new edge and vertex, connect to v1t */ - vNew = bAddNewVertex( pBNS, v1t, 1, 0, 1, nDots ); - if ( IS_BNS_ERROR(vNew) ) { - return vNew; - } - apc->vNewVertex[iapc] = vNew; - apc->bSetNew[iapc] = 1; - bSet_v1 = 1; - iapc ++; - } - if ( v2t == NO_VERTEX ) { - /* create new edge and vertex, connect to v2 */ - vNew = bAddNewVertex( pBNS, v2, 1, 0, 1, nDots ); - if ( IS_BNS_ERROR(vNew) ) { - return vNew; - } - apc->vNewVertex[iapc] = vNew; - apc->bSetNew[iapc] = 1; - bSet_v2 = 1; - iapc ++; - } - - /* add st-cap to v1 and/or v2t */ - iapc = 0; - if ( !bSet_v1 ) { - /* add st-cap to v1 */ - if ( v1t != NO_VERTEX ) { - return BNS_BOND_ERR; - } - n = bAddStCapToAVertex( pBNS, v1, v2Act, apc->nOldCapsVert[iapc], nDots, 0 ); - apc->bSetOldCapsVert[iapc] = n; - apc->vOldVert[iapc] = v1; - iapc ++; - } - if ( !bSet_v2 ) { - /* add st-cap to v2t */ - if ( v2t == NO_VERTEX ) { - return BNS_BOND_ERR; - } - n = bAddStCapToAVertex( pBNS, v2t, v1Act, apc->nOldCapsVert[iapc], nDots, 0 ); - apc->bSetOldCapsVert[iapc] = n; - apc->vOldVert[iapc] = v2t; - iapc ++; - } - if ( *nDots < 0 || *nDots %2 ) { - return BNS_SET_ALTP_ERR; - } - return BNS_CHK_ALTP_SET_SUCCESS; - - } - /* ( *pBNS->pbTautFlags & TG_FLAG_TEST_TAUT2_SALTS ) */ -/*#endif*/ /* ( TEST_REMOVE_S_ATOMS == 1 && ALT_PATH_MODE_4_SALT == path_type ) */ - /*****************************************************************/ - if ( path_type == ALT_PATH_MODE_REM2H_CHG || - path_type == ALT_PATH_MODE_ADD2H_CHG || - path_type == ALT_PATH_MODE_REM2H_TST || - path_type == ALT_PATH_MODE_ADD2H_TST ) { /* added 2004-03-18 */ - - int bDonors = (path_type == ALT_PATH_MODE_REM2H_CHG) || (path_type == ALT_PATH_MODE_REM2H_TST); - - int bSet_v1; /* indicator: v1 has been set */ - int bSet_v2; /* indicator: v2 has been set */ - int i, cap = 1; - - Vertex v1t = NO_VERTEX; - Vertex v2t = NO_VERTEX; - Vertex v1Act, v2Act; - Vertex v; - - memset( apc, 0, sizeof(*apc) ); - fcd[ifcd].iedge = NO_VERTEX; - *nDots = 0; - /* - if ( v1 == v2 ) { - return BNS_CHK_ALTP_SAME_VERTEX; - } - */ - /* check whether v1 and v2 have proper neighbors */ - for ( i = 0, n = bAdjacentDonors = 0; i < pVert1->num_adj_edges; i ++ ) { - v = (pBNS->edge + pVert1->iedge[i])->neighbor12 ^ v1; /* v is adjacent to v1 */ - /* do not ignore connection to v2 - if ( v == v2 ) - continue; - */ - n += bDonors ? (pBNS->vert[v].st_edge.cap > 0) : ((pBNS->edge + pVert1->iedge[i])->flow > 0); - bAdjacentDonors += bDonors ? (v == v2) && ((pBNS->edge + pVert1->iedge[i])->flow < MAX_BOND_EDGE_CAP) : 0; - /* two donors connected by a single or double bond */ - } - if ( !n && !bAdjacentDonors ) { - return BNS_CHK_ALTP_NO_ALTPATH; /* the vertex cannot have flow */ - } - for ( i = 0, n = bAdjacentDonors = 0; i < pVert2->num_adj_edges; i ++ ) { - v = (pBNS->edge + pVert2->iedge[i])->neighbor12 ^ v2; /* v is adjacent to v2 */ - /* do not ignore connection to v1 - if ( v == v1 ) - continue; - */ - n += bDonors ? (pBNS->vert[v].st_edge.cap > 0) : ((pBNS->edge + pVert2->iedge[i])->flow > 0); - bAdjacentDonors += bDonors ? (v == v1) && ((pBNS->edge + pVert2->iedge[i])->flow < MAX_BOND_EDGE_CAP ) : 0; - /* two donors connected by a single or double bond */ - } - if ( !n && !bAdjacentDonors ) { - return BNS_CHK_ALTP_NO_ALTPATH; /* the vertex cannot have flow */ - } - - v1Act = v1; - v2Act = v2; - - /* find t-group that contains v1 */ - if ( (pVert1->type & type) == type ) { - v1t = GetGroupVertex(pBNS, v1, type); - if ( BNS_BOND_ERR == v1t ) { - v1t = NO_VERTEX; - } else - if ( IS_BNS_ERROR( v1t ) ) { - return v1t; - } else - if ( v1t != NO_VERTEX ) { - v1Act = v1t; - } - } - /* find t-group that contains v2 */ - if ( (pVert2->type & type) == type ) { - v2t = GetGroupVertex(pBNS, v2, type); - if ( BNS_BOND_ERR == v2t ) { - v2t = NO_VERTEX; - } else - if ( IS_BNS_ERROR( v2t ) ) { - return v2t; - } else - if ( v2t != NO_VERTEX ) { - v2Act = v2t; - } - } - - if ( v1t != NO_VERTEX && v1t == v2t ) { - cap = 2; /* same t-group */ - } - - /* bAddNewVertex: (bDonors != 0) == (vit != NO_VERTEX), i=1,2 */ - bSet_v1 = bSet_v2 = 0; - /* create new edges adjacent to v1t or v2 */ - iapc = 0; - if ( (bDonors != 0) == (v1t != NO_VERTEX) ) { - /* create new edge and vertex, connect to v1Act */ - vNew = bAddNewVertex( pBNS, v1Act, cap, 0, 1, nDots ); - if ( IS_BNS_ERROR(vNew) ) { - return vNew; - } - apc->vNewVertex[iapc] = vNew; - apc->bSetNew[iapc] = 1; - bSet_v1 = 1; - iapc ++; - } - if ( (bDonors != 0) == (v2t != NO_VERTEX) && cap == 1 ) { - /* create new edge and vertex, connect to v2Act; do not do it if cap==2 */ - vNew = bAddNewVertex( pBNS, v2Act, cap, 0, 1, nDots ); - if ( IS_BNS_ERROR(vNew) ) { - return vNew; - } - apc->vNewVertex[iapc] = vNew; - apc->bSetNew[iapc] = 1; - bSet_v2 = 1; - iapc ++; - } else - if ( (bDonors != 0) == (v2t != NO_VERTEX) ) { - bSet_v2 = 1; - } - - /* add st-cap to v1 and/or v2t */ - iapc = 0; - /* if cap=2 then just increment st_cap 2 times */ - if ( !bSet_v1 ) { - /* add st-cap to v1 */ - if ( (bDonors != 0) == (v1t != NO_VERTEX) ) { - return BNS_BOND_ERR; - } - n = bAddStCapToAVertex( pBNS, v1Act, v2Act, apc->nOldCapsVert[iapc], nDots, bAdjacentDonors ); - apc->bSetOldCapsVert[iapc] = n; - apc->vOldVert[iapc] = v1Act; - iapc ++; - } - if ( !bSet_v2 ) { - /* add st-cap to v2t */ - if ( (bDonors != 0) == (v2t != NO_VERTEX) ) { - return BNS_BOND_ERR; - } - n = bAddStCapToAVertex( pBNS, v2Act, v1Act, apc->nOldCapsVert[iapc], nDots, bAdjacentDonors ); - apc->bSetOldCapsVert[iapc] = n; - apc->vOldVert[iapc] = v2Act; - iapc ++; - } - if ( *nDots < 0 || *nDots %2 ) { - return BNS_SET_ALTP_ERR; - } - return BNS_CHK_ALTP_SET_SUCCESS; - - } - /**************************************************************************/ - if ( path_type == ALT_PATH_MODE_REM_PROTON ) { /* added 2004-03-05 */ - if ( v1 >= 0 && v2 >= 0 && - (pVert1->type & BNS_VERT_TYPE_ANY_GROUP) && - (pVert2->type & BNS_VERT_TYPE_ANY_GROUP) ) { - /* create new edge and vertex, connect to v2 */ - if ( (pBNS->vert[v1].type & BNS_VERT_TYPE_C_GROUP) && - (pBNS->vert[v1].st_edge.flow == 2*pBNS->vert[v1].num_adj_edges ) ) { - /* so far in a charge group max edge flow = 1 2004-03-08 */ - return BNS_CHK_ALTP_NO_ALTPATH; - } - memset( apc, 0, sizeof(*apc) ); - fcd[ifcd].iedge = NO_VERTEX; - *nDots = 0; - iapc = 0; - - vNew = bAddNewVertex( pBNS, v2, 1, 0, 1, nDots ); - if ( IS_BNS_ERROR(vNew) ) { - return vNew; - } - apc->vNewVertex[iapc] = vNew; - apc->bSetNew[iapc] = 1; - /*iapc ++;*/ - /* add st-cap (dot) to v1 */ - n = bAddStCapToAVertex( pBNS, v1, v2, apc->nOldCapsVert[iapc], nDots, 0 ); - apc->bSetOldCapsVert[iapc] = n; - apc->vOldVert[iapc] = v1; - iapc ++; - return BNS_CHK_ALTP_SET_SUCCESS; - } - } - -#if ( NEUTRALIZE_ENDPOINTS == 1 ) /* { */ - - - *nDots = 0; - memset( apc, 0, sizeof(*apc) ); - fcd[ifcd].iedge = NO_VERTEX; - - if ( type & BNS_VERT_TYPE_ENDPOINT ) { - BNS_IEDGE iedge; - AT_NUMB type2; - int ret2; - /* prohibit charge movement */ - type2 = BNS_VERT_TYPE_C_GROUP; - iedge = GetEdgeToGroupVertex( pBNS, v1, type2 ); - if (iedge != NO_VERTEX ) { - /* set flow=1 on an edge to a c-group vertex to make sure there is no positive charge - * when moving tautomeric H-atoms - */ - ret2 = bSetFlowToCheckOneBond( pBNS, iedge, 1, fcd+ifcd ); - if ( IS_BNS_ERROR(ret2) ) { - return ret2; - } - *nDots += ret2; - while ( fcd[ifcd].iedge != NO_VERTEX ) { - ifcd ++; - } - } - iedge = GetEdgeToGroupVertex( pBNS, v2, type2 ); - if (iedge != NO_VERTEX ) { - /* set flow=1 on an edge to a c-group vertex to make sure there is no positive charge - * when moving tautomeric H-atoms - */ - ret2 = bSetFlowToCheckOneBond( pBNS, iedge, 1, fcd+ifcd ); - if ( IS_BNS_ERROR(ret2) ) { - return ret2; - } - *nDots += ret2; - while ( fcd[ifcd].iedge != NO_VERTEX ) { - ifcd ++; - } - } - /* set hydrogen counts */ - type2 = BNS_VERT_TYPE_TGROUP; - iedge = GetEdgeToGroupVertex( pBNS, v1, type2 ); - if (iedge != NO_VERTEX ) { - /* set flow=1 on an edge to a t-group vertex to make sure there is - * a moveable hydrogen atom or (-) on v1 when moving tautomeric H-atoms - */ -#if ( FIX_H_CHECKING_TAUT == 1 ) - ret2 = bSetFlowToCheckOneBond( pBNS, iedge, 1, fcd+ifcd ); - if ( IS_BNS_ERROR(ret2) ) { - return ret2; - } - *nDots += ret2; - while ( fcd[ifcd].iedge != NO_VERTEX ) { - ifcd ++; - } -#else - t1 = pBNS->edge[iedge].neighbor12 ^ v1; -#endif - } - iedge = GetEdgeToGroupVertex( pBNS, v2, type2 ); - if (iedge != NO_VERTEX ) { - /* set flow=0 on an edge to a t-group vertex to make sure there is - * no moveable hydrogen atom or (-) on v2 when moving tautomeric H-atoms - */ -#if ( FIX_H_CHECKING_TAUT == 1 ) - ret2 = bSetFlowToCheckOneBond( pBNS, iedge, 0, fcd+ifcd ); - if ( IS_BNS_ERROR(ret2) ) { - return ret2; - } - *nDots += ret2; - while ( fcd[ifcd].iedge != NO_VERTEX ) { - ifcd ++; - } -#else - t2 = pBNS->edge[iedge].neighbor12 ^ v2; -#endif - } - -#if ( FIX_H_CHECKING_TAUT == 1 ) -#else - if ( t1 == t2 && t1 != NO_VERTEX ) { - return BNS_CHK_ALTP_SAME_TGROUP; - } -#endif - iapc = 0; - /* create new edge and vertex with cap=1 at v2 and/or t1 */ - if ( t1 != NO_VERTEX ) { - /* create new edge and vertex, connect to t1 */ - vNew = bAddNewVertex( pBNS, t1, 1/*cap*/, 0/*flow*/, 1/*max_adj_edges*/, nDots ); - if ( IS_BNS_ERROR(vNew) ) { - return vNew; - } - apc->vNewVertex[iapc] = vNew; - apc->bSetNew[iapc] = 1; - iapc ++; - } - if ( t2 == NO_VERTEX ) { - /* create new edge and vertex, connect to v2 */ - vNew = bAddNewVertex( pBNS, v2, 1/*cap*/, 0/*flow*/, 1/*max_adj_edges*/, nDots ); - if ( IS_BNS_ERROR(vNew) ) { - return vNew; - } - apc->vNewVertex[iapc] = vNew; - apc->bSetNew[iapc] = 1; - iapc ++; - } - - /* add st-cap to v1 and/or v2t */ - iapc = 0; - if ( t1 == NO_VERTEX ) { - /* add st-cap to v1 */ - n = bAddStCapToAVertex( pBNS, v1, (Vertex)(t2 == NO_VERTEX? v2:t2), apc->nOldCapsVert[iapc], nDots, 0 ); - apc->bSetOldCapsVert[iapc] = n; - apc->vOldVert[iapc] = v1; - iapc ++; - } - if ( t2 != NO_VERTEX ) { - /* add st-cap to t2 */ - n = bAddStCapToAVertex( pBNS, t2, (Vertex)(t1 == NO_VERTEX? v1:t1), apc->nOldCapsVert[iapc], nDots, 0 ); - apc->bSetOldCapsVert[iapc] = n; - apc->vOldVert[iapc] = t2; - iapc ++; - } - } else { - /* create new edge and vertex, connect to v2 */ - vNew = bAddNewVertex( pBNS, v2, 1 /* cap*/, 0 /* flow */, 1 /* max_adj_edges */, nDots ); - if ( IS_BNS_ERROR(vNew) ) { - return vNew; - } - apc->vNewVertex[0] = vNew; - apc->bSetNew[0] = 1; - - /* add st-cap to v1 */ - n = bAddStCapToAVertex( pBNS, v1, v2, apc->nOldCapsVert[0], nDots, 0 ); - apc->bSetOldCapsVert[0] = n; - apc->vOldVert[0] = v1; - } -#else /* } NEUTRALIZE_ENDPOINTS == 0 {*/ - - *nDots = 0; - memset( apc, 0, sizeof(*apc) ); - fcd[ifcd].iedge = NO_VERTEX; - - /* create new edge and vertex, connect to v2 */ - vNew = bAddNewVertex( pBNS, v2, 1 /* cap*/, 0 /* flow */, 1 /* max_adj_edges */, nDots, 0 ); - if ( IS_BNS_ERROR(vNew) ) { - return vNew; - } - apc->vNewVertex[0] = vNew; - apc->bSetNew[0] = 1; - - /* add st-cap to v1 */ - n = bAddStCapToAVertex( pBNS, v1, v2, apc->nOldCapsVert[0], nDots ); - apc->bSetOldCapsVert[0] = n; - apc->vOldVert[0] = v1; -#endif /* } NEUTRALIZE_ENDPOINTS */ - - if ( *nDots < 0 || *nDots %2 ) { - return BNS_SET_ALTP_ERR; - } - return BNS_CHK_ALTP_SET_SUCCESS; - } - /*return BNS_CHK_ALTP_NO_ALTPATH;*/ -} - - - -/**********************************************************************************/ -int bRestoreBnsAfterCheckAltPath( BN_STRUCT *pBNS, ALT_PATH_CHANGES *apc, int bChangeFlow ) -/* int nVertDoubleBond, int nVertSingleBond, int nNewVertex, AT_NUMB *nOldCapVertSingleBond */ -{ - BNS_EDGE *pEdge; - Vertex vNew; - Vertex vOld; - BNS_VERTEX *pOldVert; - BNS_VERTEX *pNewVert; - int i, j, n, ret; - - ret = 0; - if ( bChangeFlow & BNS_EF_UPD_H_CHARGE ) { - /* remove new temp. vertices and edges connectong them to the structure */ - for ( i = sizeof(apc->bSetNew)/sizeof(apc->bSetNew[0])-1; 0 <= i; i -- ) { - if ( apc->bSetNew[i] ) { - vNew = apc->vNewVertex[i]; - pNewVert = pBNS->vert + vNew; - for ( j = 0; j < pNewVert->num_adj_edges; j ++ ) { - pEdge = pBNS->edge+pNewVert->iedge[j]; - vOld = pEdge->neighbor12 ^ vNew; - pOldVert = pBNS->vert + vOld; - pOldVert->st_edge.flow -= pEdge->flow; - pOldVert->st_edge.cap -= pEdge->flow; - /* disconnect new edge from pOldVert */ - pOldVert->iedge[--pOldVert->num_adj_edges] = 0; - /* clear the new edge */ - memset( pEdge, 0, sizeof(*pEdge) ); - /* and decrement the total number of edges */ - pBNS->num_edges --; - } - /* clear the new vertex */ - memset( pNewVert, 0, sizeof( pNewVert ) ); - /* and decrement the total number of vertices (new vertice ids are contiguous */ - pBNS->num_vertices --; - ret ++; - } - } - /* Restore changed caps of old vertices */ - for ( i = sizeof(apc->bSetOldCapsVert)/sizeof(apc->bSetOldCapsVert[0])-1; 0 <= i; i -- ) { - if ( n = apc->bSetOldCapsVert[i] ) { - pOldVert = pBNS->vert + apc->vOldVert[i]; - if ( pOldVert->st_edge.flow <= apc->nOldCapsVert[i][0] ) { - pOldVert->st_edge.cap = apc->nOldCapsVert[i][0]; - n --; - ret ++; - for ( j = 0; j < n && j < pOldVert->num_adj_edges; j ++ ) { - pEdge = pBNS->edge + pOldVert->iedge[j]; - pEdge->cap = apc->nOldCapsVert[i][j+1]; - } - } - } - } - } else { - /* Restore changed caps of old vertices */ - for ( i = sizeof(apc->bSetOldCapsVert)/sizeof(apc->bSetOldCapsVert[0])-1; 0 <= i; i -- ) { - if ( n = apc->bSetOldCapsVert[i] ) { - pOldVert = pBNS->vert + apc->vOldVert[i]; - pOldVert->st_edge.cap = apc->nOldCapsVert[i][0]; - n --; - ret ++; - for ( j = 0; j < n && j < pOldVert->num_adj_edges; j ++ ) { - pEdge = pBNS->edge + pOldVert->iedge[j]; - pEdge->cap = apc->nOldCapsVert[i][j+1]; - } - } - } - - /* remove new temp. vertices and edges connectong them to the structure */ - for ( i = sizeof(apc->bSetNew)/sizeof(apc->bSetNew[0])-1; 0 <= i; i -- ) { - if ( apc->bSetNew[i] ) { - vNew = apc->vNewVertex[i]; - pNewVert = pBNS->vert + vNew; - for ( j = 0; j < pNewVert->num_adj_edges; j ++ ) { - pEdge = pBNS->edge+pNewVert->iedge[j]; - vOld = pEdge->neighbor12 ^ vNew; - pOldVert = pBNS->vert + vOld; - /* disconnect new edge from pOldVert */ - pOldVert->iedge[--pOldVert->num_adj_edges] = 0; - /* clear the new edge */ - memset( pEdge, 0, sizeof(*pEdge) ); - /* and decrement the total number of edges */ - pBNS->num_edges --; - } - /* clear the new vertex */ - memset( pNewVert, 0, sizeof( pNewVert ) ); - /* and decrement the total number of vertices (new vertice ids are contiguous */ - pBNS->num_vertices --; - ret ++; - } - } - } - return 0; -} - - - -/**********************************************************************************/ -int bExistsAnyAltPath( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at, int num_atoms, - int nVert2, int nVert1, int path_type ) -{ - int nRet1, nRet2; - nRet1 = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, nVert2, nVert1, path_type ); - if ( nRet1 > 0 ) - return nRet1; - nRet2 = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, nVert1, nVert2, path_type ); - if ( nRet2 > 0 ) - return nRet2; - if ( IS_BNS_ERROR( nRet1 ) ) - return nRet1; - if ( IS_BNS_ERROR( nRet2 ) ) - return nRet2; - return 0; -} - -#define ALT_PATH_TAUTOM 1 -#define ALT_PATH_CHARGE 2 -#define ALT_PATH_4_SALT 3 - - - -/**********************************************************************************/ -int bIsBnsEndpoint( BN_STRUCT *pBNS, int v ) -{ - int i, vt; - BNS_VERTEX *pVert; /* vertices */ - BNS_EDGE *pEdge; /* edges */ - - if ( 0 <= v && v < pBNS->num_atoms && (pVert = pBNS->vert+v) && (pVert->type & BNS_VERT_TYPE_ENDPOINT) ) { - for ( i = pVert->num_adj_edges - 1; 0 <= i; i -- ) { - pEdge = pBNS->edge + pVert->iedge[i]; - vt = pEdge->neighbor12 ^ v; - if ( pBNS->vert[vt].type & BNS_VERT_TYPE_TGROUP ) { - return !IS_FORBIDDEN(pEdge->forbidden, pBNS); - } - } - } - return 0; -} - - - -/**********************************************************************************/ -#if ( BNS_RAD_SEARCH == 1 ) -/**********************************************************************************/ -int bRadChangesAtomType( BN_STRUCT *pBNS, BN_DATA *pBD, Vertex v, Vertex v_1, Vertex v_2 ) -{ - - EdgeIndex iuv; - Vertex v_O, v_ChgOrH; - /* the previous atom along the path: should be a terminal atom */ - if ( v_1 == NO_VERTEX ) { - v_1 = GetPrevVertex( pBNS, v, pBD->SwitchEdge, &iuv ); - } - v_O = v_1 / 2 - 1; - if ( v_O < 0 || v_O >= pBNS->num_atoms ) { - return 0; - } - /* make sure v_O is a terminal atom: its second neighbor is not an atom */ - if ( pBNS->vert[pBNS->edge[pBNS->vert[v_O].iedge[1]].neighbor12 ^ v_O].type & BNS_VERT_TYPE_ATOM ) { - return 0; - } - /* the next to previous vertex vertex along the path: should be a Charge or Taut group vertex */ - if ( v_2 == NO_VERTEX ) { - v_2 = GetPrevVertex( pBNS, v_1, pBD->SwitchEdge, &iuv ); - } - v_ChgOrH = v_2 / 2 - 1; - if ( v_ChgOrH < pBNS->num_atoms ) { - return 0; - } - /* make sure v_ChgOrH is a charge or taut_group */ - if ( pBNS->vert[v_ChgOrH].type & (BNS_VERT_TYPE_TGROUP | BNS_VERT_TYPE_C_GROUP) ) - return 1; - return 0; -} - - - -/**********************************************************************************/ -int RegisterRadEndpoint( BN_STRUCT *pBNS, BN_DATA *pBD, Vertex u) -{ - EdgeIndex iuv; - int i, num_found; - Vertex v, w; - Vertex u_last, v2; - switch( pBD->bRadSrchMode ) { - case RAD_SRCH_NORM: - /* go backwards along alt path and stop at the 1st found atom (not a fictitious vertex) */ - /* we need only vertices where a radical may be moved, therefore exclude u%2=1 (odd) vertices */ - /* atom number = u/2-1; u = 0 or 1 is 's' or 't' vertices, respectively, they are not atoms */ - num_found = 0; - while ( u > Vertex_t && (u % 2 || u/2 > pBNS->num_atoms ) ) { - u = GetPrevVertex( pBNS, u, pBD->SwitchEdge, &iuv ); - } - w = u/2 - 1; /* Check whether u is a radical endpoint */ - if ( Vertex_t < u && w < pBNS->num_atoms && - pBNS->vert[w].st_edge.cap == (pBNS->vert[w].st_edge.flow & EDGE_FLOW_ST_MASK) ) { - /* u is an atom; it is not a radical atom */ - /* now search for the starting radical atom by following the path back from u */ - v = u_last = u; - while( v > Vertex_t ) { - u = v; - v = GetPrevVertex( pBNS, u, pBD->SwitchEdge, &iuv ); /* Radical endpoint */ - } - /* check whether u is a radical atom */ - if ( !(u%2) && Vertex_t < u && - (u = u/2 - 1) < pBNS->num_atoms && - pBNS->vert[u].st_edge.cap > (pBNS->vert[u].st_edge.flow & EDGE_FLOW_ST_MASK) ) { - /* at pBNS->vert[u] we have found the radical that originated the path */ - /* pBD->RadEndpoints[2k] is the radical, pBD->RadEndpoints[2k+1] is the farthest atom */ - /* to which the radical may be moved (farthest reachable atom) */ - - /* add *all* atoms that may receive radical from u_rad */ - /* exception: at2 in: ==(+/-/H)---at1==at2(possible rad endpoint) if pBNS->type_TACN */ - - for ( v = u_last; v > Vertex_t; v = GetPrevVertex( pBNS, v, pBD->SwitchEdge, &iuv ) ) { - if ( !(v%2) && (v2 = v/2 - 1) < pBNS->num_atoms && - pBNS->vert[v2].st_edge.cap == (pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK) ) { - /* check exception */ - if ( pBNS->type_TACN && - bRadChangesAtomType( pBNS, pBD, v, NO_VERTEX, NO_VERTEX ) ) { - continue; - } - /* add */ - for ( i = 0; i < pBD->nNumRadEndpoints; i += 2 ) { - /* check whether this pair, (u,w), has already been saved */ - if ( u == pBD->RadEndpoints[i] && - v2 == pBD->RadEndpoints[i+1] ) { - break; - } - } - if ( i >= pBD->nNumRadEndpoints ) { - /* add new (u,w) pair */ - if ( pBD->nNumRadEndpoints+2 <= pBD->max_num_vertices ) { - /* add */ - pBD->RadEndpoints[pBD->nNumRadEndpoints ++] = u; /* radical */ - pBD->RadEndpoints[pBD->nNumRadEndpoints ++] = v2; /* endpoint */ - num_found ++; - /*return 1;*/ /* registered */ - } else { - return BNS_VERT_EDGE_OVFL; - } - } - } - } - if ( num_found ) { - return 1; - } - } - } - break; - - case RAD_SRCH_FROM_FICT: - /* find nearest atom accessible from a fictitious vertex */ - /* go backwards along alt path and stop at the 1st found atom (not a fictitious vertex) */ - v = u; - w = NO_VERTEX; /* the nearest atom -- radical-endpoint */ - u = NO_VERTEX; /* fictitious vertex carrying a radical */ - while ( v > Vertex_t ) { - u = v; - if ( !(v % 2) && v/2 <= pBNS->num_atoms && - pBNS->vert[v/2-1].st_edge.cap - pBNS->vert[v/2-1].st_edge.flow < 2 ) { - w = v; /* vertex w is atom that may be singlet or doublet but not triplet */ - } - v = GetPrevVertex( pBNS, u, pBD->SwitchEdge, &iuv ); - } - v = u/2 - 1; /* vertex u may be the radical from which the path originated; w is the nearest atom */ - if ( w == NO_VERTEX || u == NO_VERTEX || w % 2 || u == w || v < pBNS->num_atoms || - pBNS->vert[v].st_edge.cap == pBNS->vert[v].st_edge.flow || - (w = w/2 - 1) >= pBNS->num_atoms ) { - break; /* reject */ - } - u = v; - /* at pBNS->vert[u] we have found the radical that originated the path, w is the nearest atom */ - for ( i = 0; i < pBD->nNumRadEndpoints; i += 2 ) { - if ( u == pBD->RadEndpoints[i] && - w == pBD->RadEndpoints[i+1] ) { - break; /* this pair has already been stored */ - } - } - if ( i >= pBD->nNumRadEndpoints ) { - /* a new pair has been found */ - if ( pBD->nNumRadEndpoints+2 <= pBD->max_num_vertices ) { - /* add */ - pBD->RadEndpoints[pBD->nNumRadEndpoints ++] = u; /* radical */ - pBD->RadEndpoints[pBD->nNumRadEndpoints ++] = w; /* endpoint */ - return 1; /* registered */ - } else { - return BNS_VERT_EDGE_OVFL; - } - } - break; - } - - return 0; /* rejected */ -} - - - -/**********************************************************************************/ -int cmp_rad_endpoints( const void *a1, const void *a2 ) -{ - /* Vertex radical_vertex, radical_endpoint */ - const Vertex *p1 = (const Vertex *)a1; - const Vertex *p2 = (const Vertex *)a2; - if ( p1[0] < p2[0] ) - return -1; - if ( p1[0] > p2[0] ) - return 1; - if ( p1[1] < p2[1] ) - return -1; - if ( p1[1] > p2[1] ) - return 1; - return 0; -} - - - -/**********************************************************************************/ -int RemoveRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at ) -{ - BNS_EDGE *e; - EdgeIndex ie; - BNS_VERTEX *p1, *p2; - Vertex v1, v2; - int i, delta, rad; - for ( i = pBD->nNumRadEdges-1; 0 <= i; i -- ) { - ie = pBD->RadEdges[i]; - if ( ie < 0 || ie >= pBNS->num_edges ) { - goto error_exit; - } - e = pBNS->edge + ie; - v1 = e->neighbor1; - v2 = e->neighbor12 ^ v1; /* v2 > v1 <=> v2 was added later */ - if ( ie + 1 != pBNS->num_edges || - v1 < 0 || v1 >= pBNS->num_vertices || - v2 < 0 || v2 >= pBNS->num_vertices ) { - goto error_exit; - } - p1 = pBNS->vert + v1; - p2 = pBNS->vert + v2; - - if ( p2->iedge[p2->num_adj_edges-1] != ie || - p1->iedge[p1->num_adj_edges-1] != ie ) { - goto error_exit; - } - p2->num_adj_edges --; - p1->num_adj_edges --; - p2->iedge[p2->num_adj_edges] = 0; - p1->iedge[p1->num_adj_edges] = 0; - p2->st_edge.flow -= e->flow; - p1->st_edge.flow -= e->flow; - - if ( !p2->num_adj_edges && v2 >= pBNS->num_atoms ) { - if ( v2+1 != pBNS->num_vertices ) { - goto error_exit; - } - memset( p2, 0, sizeof(*p2) ); - pBNS->num_vertices --; - } - if ( !p1->num_adj_edges && v1 >= pBNS->num_atoms ) { - if ( v1+1 != pBNS->num_vertices ) { - goto error_exit; - } - memset( p1, 0, sizeof(*p1) ); - pBNS->num_vertices --; - } - if ( at && v1 < pBNS->num_atoms ) { - delta = p1->st_edge.cap - p1->st_edge.flow; - rad = at[v1].radical; - switch( delta ) { - case 0: - if ( rad == RADICAL_DOUBLET ) - rad = 0; - break; - case 1: - if ( rad != RADICAL_DOUBLET ) - rad = RADICAL_DOUBLET; - } - at[v1].radical = rad; - } - memset( e, 0, sizeof(*e) ); - pBNS->num_edges --; - } - pBD->nNumRadEdges = 0; - pBD->nNumRadicals = 0; - pBD->bRadSrchMode = RAD_SRCH_NORM; - return 0; -error_exit: - return BNS_PROGRAM_ERR; -} - - - -/**********************************************************************************/ -int RestoreRadicalsOnly( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at ) -{ - BNS_EDGE *e; - EdgeIndex ie; - BNS_VERTEX *p1, *p2; - Vertex v1, v2; - int i, delta, rad; - int p1_num_adj_edges, p2_num_adj_edges; - - for ( i = pBD->nNumRadEdges-1; 0 <= i; i -- ) { - ie = pBD->RadEdges[i]; - if ( ie < 0 || ie >= pBNS->num_edges ) { - goto error_exit; - } - e = pBNS->edge + ie; - v1 = e->neighbor1; /* atom */ - v2 = e->neighbor12 ^ v1; /* v2 > v1 <=> v2 was added later */ - if ( v1 < 0 || v1 >= pBNS->num_atoms || - v2 < pBNS->num_atoms || v2 >= pBNS->num_vertices ) { - goto error_exit; - } - p1 = pBNS->vert + v1; - p2 = pBNS->vert + v2; - - p1_num_adj_edges = e->neigh_ord[0]; - p2_num_adj_edges = e->neigh_ord[1]; - - if ( p2->iedge[p2_num_adj_edges] != ie || - p1->iedge[p1_num_adj_edges] != ie ) { - goto error_exit; - } - - if ( at && v1 < pBNS->num_atoms ) { - delta = p1->st_edge.cap - p1->st_edge.flow + e->flow; - rad = at[v1].radical; - switch( delta ) { - case 0: - if ( rad == RADICAL_DOUBLET ) - rad = 0; - break; - case 1: - if ( rad != RADICAL_DOUBLET ) - rad = RADICAL_DOUBLET; - } - at[v1].radical = rad; - } - } - return 0; -error_exit: - return BNS_PROGRAM_ERR; -} - - - -/**********************************************************************************/ -int SetRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode ) -{ - int ret, i, j, k, num_new_edges, delta; - BNS_VERTEX *pRad, *pEndp; - Vertex wRad, vRad, vEndp, nNumRadicals; - int nDots=0 /* added initialization, 2006-03 */, nNumEdges; - if ( pBNS->tot_st_cap <= pBNS->tot_st_flow ) { - return 0; - } - pBD->nNumRadEndpoints = 0; - pBD->nNumRadEdges = 0; - pBD->bRadSrchMode = bRadSrchMode; - pBNS->alt_path = pBNS->altp[0]; - pBNS->bChangeFlow = 0; - ret = BalancedNetworkSearch( pBNS, pBD, BNS_EF_RAD_SRCH ); - ReInitBnData( pBD ); - ReInitBnStructAltPaths( pBNS ); - if ( !ret && pBD->nNumRadEndpoints >= 2 ) { - /* sort by radical locations */ - qsort( pBD->RadEndpoints, pBD->nNumRadEndpoints/2, 2*sizeof(pBD->RadEndpoints[0]), cmp_rad_endpoints ); - num_new_edges = 0; - nNumRadicals = 0; - /* create new vertices (type=BNS_VERT_TYPE_TEMP) and edges with flow=cap=1 */ - /* connecting the new vertices radical vertices */ - for ( i = 0; i < pBD->nNumRadEndpoints; i = j ) { - wRad = pBD->RadEndpoints[i]; - pRad = pBNS->vert + wRad; - delta = pRad->st_edge.cap - (pRad->st_edge.flow & EDGE_FLOW_ST_MASK); - if ( delta <= 0 ) { - delta = 1; - } - nNumEdges = 0; - for ( j = i; j < pBD->nNumRadEndpoints && wRad == pBD->RadEndpoints[j] ; j += 2 ) { - nNumEdges ++; - } - /* add new aux vertex to the radical atom/vertex */ - vRad = bAddNewVertex( pBNS, wRad, delta, delta, nNumEdges+1, &nDots ); - if ( IS_BNS_ERROR( vRad ) ) { - ret = vRad; - goto error_exit; - } - pRad = pBNS->vert + vRad; - pBD->RadEdges[pBD->nNumRadEdges ++] = pRad->iedge[pRad->num_adj_edges-1]; - /* replace references to vertex wRad with vRad */ - for ( k = i, nNumEdges = 0; k < j; k += 2 ) { - pBD->RadEndpoints[k] = vRad; - } - nNumRadicals ++; - } - /* all vRad vertex indices should be in the range vFirstNewVertex...vFirstNewVertex+nNumRadicals-1 */ - /* connect new vertices to the radical endpoints thus replacing radicals with even-length alternating cycles */ - for ( i = 0; i < pBD->nNumRadEndpoints; i = j ) { - vRad = pBD->RadEndpoints[i]; - pRad = pBNS->vert + vRad; - for ( j = i; j < pBD->nNumRadEndpoints && vRad == pBD->RadEndpoints[j] ; j += 2 ) { - /* connect vew vertex pRad to radical endpoints */ - vEndp = pBD->RadEndpoints[j+1]; - pEndp = pBNS->vert + vEndp; - ret = AddNewEdge( pRad, pEndp, pBNS, 1, 0 ); - if ( IS_BNS_ERROR( ret ) ) { - goto error_exit; - } - pBD->RadEdges[pBD->nNumRadEdges ++] = ret; - } - } - pBD->nNumRadicals = nNumRadicals; - return nNumRadicals; /* done */ - } - return 0; /* nothing to do */ - -error_exit: - RemoveRadEndpoints( pBNS, pBD, NULL ); - return ret; - -} - - - -/**********************************************************************************/ -#define MAX_NUM_RAD 256 - - - -/***************************************************************************/ -int SetRadEndpoints2( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode ) -{ - int ret = 0, i, j, k, n, num_new_edges, delta = 1; - BNS_VERTEX *pRad, *pEndp; - Vertex wRad, vRad, vEndp, nNumRadicals; - Vertex vRadList[MAX_NUM_RAD], vRadEqul[MAX_NUM_RAD]; - int nNumRad = 0; - int edge_flow; - int nDots=0 /* added initialization, 2006-03 */, nNumEdges; - NodeSet VertSet; - if ( pBNS->tot_st_cap <= pBNS->tot_st_flow ) { - return 0; - } - /* find all radicals: their vertices have st_cap-st_flow=delta */ - /* save radical atom numbers in vRadList[] and remove radical by making st_cap=st_flow */ - for ( i = 0; i < pBNS->num_atoms; i ++ ) { - if ( pBNS->vert[i].st_edge.cap - delta == (pBNS->vert[i].st_edge.flow & EDGE_FLOW_ST_MASK) ) { - if ( nNumRad < MAX_NUM_RAD ) { - pBNS->vert[i].st_edge.cap -= delta; - pBNS->tot_st_cap -= delta; - vRadList[nNumRad] = i; /* radical position; i > j <=> vRadList[i] > vRadList[j] */ - vRadEqul[nNumRad] = nNumRad; /* the smallest radical atom that has reachable - * atoms in common with this radical atom - * always keep vRadEqul[nNumRad] <= nNumRad */ - nNumRad ++; - } - } - } - if ( pBNS->tot_st_cap - pBNS->tot_st_flow > nNumRad ) { - return BNS_CAP_FLOW_ERR; /* extra st_cap on non-atoms or program error */ - } - memset( &VertSet, 0, sizeof(VertSet) ); - /* find reachable atoms by enabling each radical separately */ - for ( j = 0; j < nNumRad; j ++ ) { - i = vRadList[j]; - pBD->nNumRadEndpoints = 0; - pBD->nNumRadEdges = 0; - pBD->bRadSrchMode = bRadSrchMode; - pBNS->alt_path = pBNS->altp[0]; - pBNS->bChangeFlow = 0; - pBNS->vert[i].st_edge.cap += delta; /* enable single radical */ - pBNS->tot_st_cap += delta; - ret = BalancedNetworkSearch( pBNS, pBD, BNS_EF_RAD_SRCH ); /* find reachable atoms */ - ReInitBnData( pBD ); - ReInitBnStructAltPaths( pBNS ); - pBD->bRadSrchMode = RAD_SRCH_NORM; - pBNS->vert[i].st_edge.cap -= delta; /* disable single radical */ - pBNS->tot_st_cap -= delta; - if ( IS_BNS_ERROR( ret ) ) { - goto error_exit; - } else - if ( ret ) { - ret = BNS_RADICAL_ERR; /* found augmenting path: should not happen since only one radical was enabled */ - goto error_exit; - } - if ( !ret && pBD->nNumRadEndpoints >= 2 ) { - /* sort by: primary_key=radical locations, secondary_key=radical endoint */ - qsort( pBD->RadEndpoints, pBD->nNumRadEndpoints/2, 2*sizeof(pBD->RadEndpoints[0]), cmp_rad_endpoints ); - if ( pBD->RadEndpoints[0] != i || pBD->RadEndpoints[pBD->nNumRadEndpoints-2] != i ) { - ret = BNS_RADICAL_ERR; /* more than one radical vertex */ - goto error_exit; - } - if ( nNumRad > 1 ) { - /* if more than one radical then save reachable atoms in bitmaps to allow */ - /* faster finding whether same atoms are reachable by two or more radicals */ - /* Later merge such sets */ - if ( NULL == VertSet.bitword ) { - SetBitCreate( ); - if ( !NodeSetCreate( &VertSet, pBNS->num_atoms, nNumRad ) ) { - ret = BNS_OUT_OF_RAM; /* out of RAM */ - goto error_exit; - } - } - NodeSetFromRadEndpoints( &VertSet, j, pBD->RadEndpoints, pBD->nNumRadEndpoints); - /* do not allow any radical center be treated as a reachable atom: */ - RemoveFromNodeSet( &VertSet, j, vRadList, nNumRad ); - } - } - } - /* restore radical st_cap so that st_cap-st_flow=delta */ - for ( j = 0; j < nNumRad; j ++ ) { - i = vRadList[j]; - pBNS->vert[i].st_edge.cap += delta; - pBNS->tot_st_cap += delta; - } - /* merge lists that have common radical endpoints */ - /* defect: if vertex sets i and j do not intersect they will be compared 2 times */ - /* total up to nNumRad*(nNumRad-1)/2 calls to DoNodeSetsIntersect() */ - if ( nNumRad > 1 ) { - for ( i = 0; i < nNumRad; i ++ ) { - if ( vRadEqul[i] != i ) - continue; - do { - n = 0; - for ( j = i+1; j < nNumRad; j ++ ) { - if ( vRadEqul[j] != j ) - continue; - if ( DoNodeSetsIntersect( &VertSet, i, j) ) { - AddNodeSet2ToNodeSet1( &VertSet, i, j); - vRadEqul[j] = i; /* Set j was copied to set i; i < j */ - n ++; - } - } - } while( n ); - } - /* fill out pBD->RadEndpoints[] */ - for ( i = 0, n = 0; i < nNumRad; i ++ ) { - if ( i == vRadEqul[i] ) { - if ( !IsNodeSetEmpty( &VertSet, i) ) { - /* store equivalent radicals */ - for ( j = i+1; j < nNumRad; j ++ ) { - if (i == vRadEqul[j] ) { - pBD->RadEndpoints[n++] = vRadList[i]; - pBD->RadEndpoints[n++] = -vRadList[j]-2; /* equivalent radical, alvays not zero */ - } - } - /* store endpoints */ - n = AddNodesToRadEndpoints( &VertSet, i, pBD->RadEndpoints, vRadList[i], n, pBD->max_len_Pu_Pv ); - if ( n < 0 ) { - ret = BNS_RADICAL_ERR; /* pBD->RadEndpoints overflow */ - goto error_exit; - } - } else { - pBD->RadEndpoints[n++] = vRadList[i]; - pBD->RadEndpoints[n++] = -1; /* immobile radical, only one edge to add */ - } - } - } - pBD->nNumRadEndpoints = n; - NodeSetFree( &VertSet ); - } else - if ( nNumRad == 1 && !pBD->nNumRadEndpoints ) { - /* 2006-07-30: a single radical; no possible endpoint found */ - for ( i = 0, n = 0; i < nNumRad; i ++ ) { - pBD->RadEndpoints[n++] = vRadList[i]; - pBD->RadEndpoints[n++] = -1; /* immobile radical, only one edge to add */ - } - pBD->nNumRadEndpoints = n; - } - - if ( !ret && pBD->nNumRadEndpoints >= 2 ) { - /* already sorted by radical locations */ - num_new_edges = 0; - nNumRadicals = 0; - /************************************************************************** - * create new vertices (type=BNS_VERT_TYPE_TEMP) and edges with flow=cap=1 - * connecting the new vertices radical vertices - * - * - * Original structure: atom A is a radical center A==B--C*--D==E - * A*--B==C--D==E atoms C and E are reachable: A==B--C===D--E* - * - * Resultant temporary structure: - * A---B==C--D==E - * || / / - * || / / The additional new vertex (*) and its - * || / / 3 edges replace the radical with alternating - * || / / circuits that allow same bond changes - * || / / as moving the radical to atoms C or E. - * ||// "Double bonds" here have edge cap=1, flow=1 - * (*) "Single bonds" have edge cap=1, flow=0 - * - * The "equivalent radical centers" (which have at least one reachable atom - * in common) are connected to (*) with "double bonds" (edge cap=1, flow=1). - * Reachable non-radical atoms are connected by edges with cap=1, flow=0 - * After running BNS to find alt.path a "double bond" from (*) may move - * to another atom thus muving the radical. - * - * Number of additional (*) vertices = number of sets of - * "equivalent radical centers". - * Each such a set may include one or more radical centers. - * - * The radicals will be re-created in RemoveRadEndpoints() - ***************************************************************************/ - for ( i = 0; i < pBD->nNumRadEndpoints; i = j ) { - wRad = pBD->RadEndpoints[i]; - pRad = pBNS->vert + wRad; - delta = pRad->st_edge.cap - (pRad->st_edge.flow & EDGE_FLOW_ST_MASK); - if ( delta <= 0 ) { - delta = 1; - } - nNumEdges = 0; - for ( j = i; j < pBD->nNumRadEndpoints && wRad == pBD->RadEndpoints[j] ; j += 2 ) { - nNumEdges += (pBD->RadEndpoints[j+1] != -1); /* immobile radicals have one edge only */ - } - /* add new aux vertex to the radical atom/vertex making st_cap-st_flow=0 */ - /* in case of immobile radical there will be no additional eddges since nNumEdges=0 */ - vRad = bAddNewVertex( pBNS, wRad, delta, delta, nNumEdges+1, &nDots ); - if ( IS_BNS_ERROR( vRad ) ) { - ret = vRad; - goto error_exit; - } - pRad = pBNS->vert + vRad; - pBD->RadEdges[pBD->nNumRadEdges ++] = pRad->iedge[pRad->num_adj_edges-1]; - /* replace references to vertex wRad with vRad */ - for ( k = i, nNumEdges = 0; k < j; k += 2 ) { - pBD->RadEndpoints[k] = vRad; - } - nNumRadicals ++; - } - /* all vRad vertex indices should be in the range vFirstNewVertex...vFirstNewVertex+nNumRadicals-1 */ - /* connect new vertices to the radical endpoints thus replacing radicals with even-length alternating cycles */ - for ( i = 0; i < pBD->nNumRadEndpoints; i = j ) { - vRad = pBD->RadEndpoints[i]; - pRad = pBNS->vert + vRad; - for ( j = i; j < pBD->nNumRadEndpoints && vRad == pBD->RadEndpoints[j] ; j += 2 ) { - /* connect vew vertex pRad to radical endpoints */ - vEndp = pBD->RadEndpoints[j+1]; - if ( vEndp == -1 ) - continue; - if ( vEndp < 0 ) { - edge_flow = 1; - vEndp = -vEndp - 2; /* equivalent radical centers */ - } else { - edge_flow = 0; - } - pEndp = pBNS->vert + vEndp; - ret = AddNewEdge( pRad, pEndp, pBNS, 1, edge_flow ); - if ( IS_BNS_ERROR( ret ) ) { - goto error_exit; - } - pBD->RadEdges[pBD->nNumRadEdges ++] = ret; - } - } - pBD->nNumRadicals = nNumRadicals; - return nNumRadicals; /* done */ - } - return 0; /* nothing to do */ - -error_exit: - RemoveRadEndpoints( pBNS, pBD, NULL ); - NodeSetFree( &VertSet ); - return ret; - -} - - -#else - - - -/**********************************************************************************/ -int SetRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode ) -{ - return 0; -} -int RemoveRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at ) -{ - return 0; -} -int SetRadEndpoints2( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode ) -{ - return 0; -} -#endif - - - -/**********************************************************************************/ -/* Return value ret bits if not IS_BNS_ERROR(ret): - - ret & 1 => Success - ret & 2 => Bonds changed to Alt - (ret & ~3) >> 2 => nDelta: number of removed dots -*/ -int bExistsAltPath( BN_STRUCT *pBNS, BN_DATA *pBD, BN_AATG *pAATG, inp_ATOM *at, int num_atoms, - int nVertDoubleBond, int nVertSingleBond, int path_type ) -{ - ALT_PATH_CHANGES apc; - int ret, ret_val, bError, bSuccess, bChangeFlow=0, nDots, nDelta, bDoMarkChangedBonds = 1; - int bAdjustRadicals = 0; - AT_NUMB type; - BNS_FLOW_CHANGES fcd[4*BNS_MAX_NUM_FLOW_CHANGES+1]; - ENDPOINT_INFO eif; -#if ( KETO_ENOL_TAUT == 1 ) - ENDPOINT_INFO eif2; -#endif - - /* initialize */ - switch( path_type ) { - case ALT_PATH_MODE_TAUTOM: - /* Check for alt path allowing to move H and (-). Purpose: confirm possible tautomerism */ - type = BNS_VERT_TYPE_ENDPOINT; - bChangeFlow = BNS_EF_CHNG_RSTR; - if ( !at[nVertSingleBond].endpoint && - (!nGetEndpointInfo( at, nVertSingleBond, &eif ) || !eif.cDonor ) ) - return 0; - if ( !at[nVertDoubleBond].endpoint && - (!nGetEndpointInfo( at, nVertDoubleBond, &eif ) || !eif.cAcceptor ) ) - return 0; - break; - -#if ( KETO_ENOL_TAUT == 1 ) - case ALT_PATH_MODE_TAUTOM_KET: - /* Check for alt path allowing to move H and (-). Purpose: confirm possible tautomerism */ - type = BNS_VERT_TYPE_ENDPOINT; - bChangeFlow = BNS_EF_CHNG_RSTR; - - if ( !at[nVertSingleBond].endpoint && - (!nGetEndpointInfo_KET( at, nVertSingleBond, &eif ) || !eif.cDonor ) ) - return 0; - if ( !at[nVertDoubleBond].endpoint && - (!nGetEndpointInfo_KET( at, nVertDoubleBond, &eif2 ) || !eif2.cAcceptor ) ) - return 0; - /* - if ( eif.cKetoEnolCode + eif2.cKetoEnolCode != 3 ) - return 0; - */ - break; - -#endif - case ALT_PATH_MODE_CHARGE: - /* Find alt path allowing to move (+). Purpose: establish "charge groups", - mark alt. bonds due to (+) charge movement */ - type = BNS_VERT_TYPE_C_POINT; - bChangeFlow = (BNS_EF_CHNG_RSTR | BNS_EF_ALTR_BONDS); - break; - - case ALT_PATH_MODE_4_SALT: - case ALT_PATH_MODE_4_SALT2: - /* Find alt paths allowing to move (-) and H between "acidic oxygen atoms". - Purpose: mark alt bonds due to this "long range" tautomerism. */ - type = BNS_VERT_TYPE_ENDPOINT; - bChangeFlow = (BNS_EF_CHNG_RSTR | BNS_EF_ALTR_BONDS); - if ( !bIsBnsEndpoint( pBNS, nVertSingleBond ) /* !at[nVertSingleBond].endpoint*/ && - (!nGetEndpointInfo( at, nVertSingleBond, &eif ) || !eif.cDonor ) ) - return 0; - if ( !bIsBnsEndpoint( pBNS, nVertDoubleBond ) /* !at[nVertDoubleBond].endpoint*/ && - (!nGetEndpointInfo( at, nVertDoubleBond, &eif ) || !eif.cAcceptor ) ) - return 0; - memset( &apc, 0, sizeof(apc) ); - break; - - case ALT_PATH_MODE_REM2H_CHG: - bChangeFlow |= BNS_EF_ALTR_BONDS; /* fall through */ - case ALT_PATH_MODE_REM2H_TST: - bChangeFlow |= BNS_EF_CHNG_RSTR; - type = BNS_VERT_TYPE_ENDPOINT; - /* allow non-tautomeric donors or any tautomeric atom */ - if ( !bIsBnsEndpoint( pBNS, nVertSingleBond ) /* not linked to a t-group or the edge forbidden */&& - (!nGetEndpointInfo( at, nVertSingleBond, &eif ) || !eif.cDonor ) ) /* not a donor */ - return 0; - if ( !bIsBnsEndpoint( pBNS, nVertDoubleBond ) /* not connected to a t-group */ && - (!nGetEndpointInfo( at, nVertDoubleBond, &eif ) || !eif.cDonor ) ) - return 0; - memset( &apc, 0, sizeof(apc) ); - break; - - case ALT_PATH_MODE_ADD2H_CHG: - bChangeFlow |= BNS_EF_ALTR_BONDS; /* fall through */ - case ALT_PATH_MODE_ADD2H_TST: - bChangeFlow |= BNS_EF_CHNG_RSTR; - type = BNS_VERT_TYPE_ENDPOINT; - /* allow non-tautomeric acceptors or any tautomeric atom */ - if ( !bIsBnsEndpoint( pBNS, nVertSingleBond ) /* !at[nVertSingleBond].endpoint*/ && - (!nGetEndpointInfo( at, nVertSingleBond, &eif ) || !eif.cAcceptor ) ) - return 0; - if ( !bIsBnsEndpoint( pBNS, nVertDoubleBond ) /* !at[nVertSingleBond].endpoint*/ && - (!nGetEndpointInfo( at, nVertDoubleBond, &eif ) || !eif.cAcceptor ) ) - return 0; - break; - - case ALT_PATH_MODE_REM_PROTON: - /* alt path is between the t-group (nVertDoubleBond) and - the (+)-charge group (nVertSingleBond) */ - type = 0; - /*bDoMarkChangedBonds = 0;*/ - bChangeFlow = (BNS_EF_SAVE_ALL | BNS_EF_UPD_H_CHARGE) | BNS_EF_ALTR_NS; /* added BNS_EF_ALTR_NS: set non-stereo altern non-ring bonds 2004-07-02*/ - break; - default: - type = 0; - bChangeFlow = BNS_EF_CHNG_RSTR; - break; - } - - bError = 0; - bSuccess = 0; - nDelta = 0; - - ret = SetRadEndpoints2( pBNS, pBD, RAD_SRCH_NORM ); - if ( IS_BNS_ERROR( ret ) ) { - return ret; - } - - /* set BNS to check alt path */ - ret = bSetBnsToCheckAltPath( pBNS, nVertDoubleBond, nVertSingleBond, type, path_type, &apc, fcd, &nDots ); - switch( ret ) { - case BNS_CHK_ALTP_NO_ALTPATH: - ret = RemoveRadEndpoints( pBNS, pBD, NULL ); - return ret; - case BNS_CHK_ALTP_SAME_TGROUP: - bSuccess = 1; - goto reinit_BNS; - case BNS_CHK_ALTP_SAME_VERTEX: - ret = RemoveRadEndpoints( pBNS, pBD, NULL ); - return ret? ret : 1; /* very strange ... set a breakpoint here */ - case BNS_CHK_ALTP_SET_SUCCESS: - break; /* actually check the existence of the altpath */ - case BNS_CANT_SET_BOND: - goto reinit_BNS; - default: - ret_val = RemoveRadEndpoints( pBNS, pBD, NULL ); - if ( IS_BNS_ERROR( ret ) ) { - return ret; - } - return BNS_PROGRAM_ERR; - } - - bAdjustRadicals = ( (bChangeFlow & BNS_EF_UPD_RAD_ORI) && !(bChangeFlow & BNS_EF_RSTR_FLOW) ); - - /***************************************************************** - * nDots = 2 for ALT_PATH_CHARGE (checking moveable positive charges) - * Now nDots for ALT_PATH_TAUTOM or ALT_PATH_4_SALT can be greater - * because some of the bonds are effectively removed and dots - * (vertex st-caps) may be added - * -- to make sure there is no (+) charge on a tautomeric endpoint - * -- to fix positions of moveable tautomeric attachements - * (H and (-)-charges) at the ends of an alt path - */ - - /* run BNS */ - - ret = RunBalancedNetworkSearch( pBNS, pBD, bChangeFlow ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - } else - if ( ret > 0 ) { - if ( 2*ret >= nDots ) { - nDelta = 2*ret - nDots; /* non-zero means augmentation created another alt. path -- between radicals */ - if ( pAATG && pAATG->nMarkedAtom ) { - if ( pAATG->nAtTypeTotals && (bChangeFlow & BNS_EF_UPD_H_CHARGE) ) { - memset( pAATG->nMarkedAtom, 0, num_atoms*sizeof(pAATG->nMarkedAtom[0]) ); - /* mark atoms that have charge or H changed, check their input types (that is, before changes), - and subtract their input charge/H from nAtTypeTotals */ - SubtractOrChangeAtHChargeBNS( pBNS, at, num_atoms, pAATG->nAtTypeTotals, pAATG->nMarkedAtom, NULL, 1 ); - /* ZChange charges and/or H, update t_group_info, do not check types or change nAtTypeTotals */ - /* Atom types will be checked and nAtTypeTotals will be changed in - AddChangedAtHChargeBNS() later */ - SubtractOrChangeAtHChargeBNS( pBNS, at, num_atoms, NULL, NULL, pAATG->t_group_info, 0 ); - } else - if ( !pAATG->nAtTypeTotals ){ - bDoMarkChangedBonds = MarkAtomsAtTautGroups( pBNS, num_atoms, pAATG, nVertSingleBond, nVertDoubleBond ); - if ( bDoMarkChangedBonds < 0 ) { - bError = bDoMarkChangedBonds; - bDoMarkChangedBonds = 0; - } - } - } - if ( bDoMarkChangedBonds ) { - /* mark bonds that were changed to configure bond testing */ - ret_val = bSetBondsAfterCheckOneBond( pBNS, fcd, -1, at, num_atoms, bChangeFlow ); - if ( IS_BNS_ERROR( ret_val ) ) { - bError = ret_val; - } - /*ret = SetBondsRestoreBnStructFlow( pBNS, at, num_atoms, bChangeFlow );*/ - /* mark all other changed bonds */ - ret = SetBondsFromBnStructFlow( pBNS, at, num_atoms, bChangeFlow ); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - } else - if ( !(ret & 1) && !(ret_val & 1) ) { - bSuccess = 1; - } else - if ( ((ret & 1) || (ret_val & 1)) && - (bChangeFlow & BNS_EF_ALTR_BONDS) || (bChangeFlow & BNS_EF_UPD_H_CHARGE) ) { - /* some bonds have been changed to alternating */ - bSuccess = 3; - } else { - bError = BNS_BOND_ERR; - } - if ( !bError && pAATG && pAATG->nMarkedAtom && (bChangeFlow & BNS_EF_UPD_H_CHARGE) ) { - /* Update radicals to avoid errors in atom type check in AddChangedAtHChargeBNS() */ - if ( bAdjustRadicals ) { - ret_val = RestoreRadicalsOnly( pBNS, pBD, at ); - if ( IS_BNS_ERROR( ret_val ) ) { - bError = ret_val; - } - } - /* Check atom types of marked atoms and add charge/H changes to nAtTypeTotals */ - /* Changing atoms were marked in the 1st call to SubtractOrChangeAtHChargeBNS(..., 1) above */ - AddChangedAtHChargeBNS( at, num_atoms, pAATG->nAtTypeTotals, pAATG->nMarkedAtom ); - if ( bChangeFlow & BNS_EF_CHNG_FLOW ) { - /* eliminate ambiguities in already changed flow: - replace (+)--N==(-) with (+)==N--(-) (both represent neutral N) */ - EliminatePlusMinusChargeAmbiguity( pBNS, num_atoms ); - } - } - } - } - ret = RestoreBnStructFlow( pBNS, bChangeFlow & BNS_EF_CHNG_RSTR); - if ( IS_BNS_ERROR( ret ) ) { - bError = ret; - } - } -reinit_BNS: - /* --- reinitialize to repeat the calculations --- */ - bRestoreBnsAfterCheckAltPath( pBNS, &apc, bChangeFlow & BNS_EF_UPD_H_CHARGE ); - bRestoreFlowAfterCheckOneBond( pBNS, fcd ); - ret_val = RemoveRadEndpoints( pBNS, pBD, bAdjustRadicals? at : NULL ); - ReInitBnStructAltPaths( pBNS ); - return bError? bError : ret_val? ret_val : (bSuccess + 4*nDelta); -} - - - -/*****************************************************************************/ -BN_STRUCT* AllocateAndInitBnStruct( inp_ATOM *at, int num_atoms, int nMaxAddAtoms, int nMaxAddEdges, int max_altp, int *pNum_changed_bonds ) -{ - BN_STRUCT *pBNS = NULL; - BNS_VERTEX *vert; - - int neigh, num_changed_bonds=0; - U_CHAR bond_type, bond_mark; - - int i, j, k, n_edges, num_bonds, num_edges, f1, f2, edge_cap, edge_flow, st_cap, st_flow, flag_alt_bond; - int tot_st_cap, tot_st_flow; - int max_tg, max_edges, max_vertices, len_alt_path, max_iedges, num_altp; -#if ( BNS_RAD_SEARCH == 1 ) - int num_rad = 0; - - nMaxAddEdges += 1; -#endif -#if ( FIX_NUM_TG == 1 ) - max_tg = inchi_max( num_atoms / 2, 5); -#else - max_tg = num_atoms; -#endif - num_changed_bonds = 0; - - for ( i = 0, num_bonds = 0; i < num_atoms; i ++ ) { - num_bonds += at[i].valence; -#if ( BNS_RAD_SEARCH == 1 ) - num_rad += (at[i].radical == RADICAL_DOUBLET); -#endif - } - /* each atom has enough edges to belong to a tautomeric group + nMaxAddEdges */ - /* number of atoms is large enough to accommodate max. possible number of t-groups + nMaxAddAtoms */ - /* max_altp cannot be larger than BN_MAX_ALTP = 16 */ - num_edges = (num_bonds /= 2); - /* +1 for a super-tautomeric group */ - max_vertices = num_atoms + nMaxAddAtoms + max_tg + 1; - /* +max_tg for edges between t-groups and super-tautomeric group */ - max_edges = num_edges + (nMaxAddEdges + NUM_KINDS_OF_GROUPS)*max_vertices + max_tg; -#if ( BNS_RAD_SEARCH == 1 ) - if ( num_rad ) { - max_vertices *= 2; - max_edges *= 2; - } -#endif - max_iedges = 2*max_edges; - len_alt_path = max_vertices+iALTP_HDR_LEN+1; /* may overflow if an edge is traversed in 2 directions */ - - if ( !( pBNS = (BN_STRUCT *)inchi_calloc( 1, sizeof(BN_STRUCT)) ) || - !( pBNS->edge = (BNS_EDGE *)inchi_calloc( max_edges, sizeof(BNS_EDGE)) ) || - !( pBNS->vert = (BNS_VERTEX *)inchi_calloc( max_vertices,sizeof(BNS_VERTEX)) ) || - !( pBNS->iedge = (BNS_IEDGE *)inchi_calloc( max_iedges, sizeof(BNS_IEDGE)) ) ) { - return DeAllocateBnStruct( pBNS ); - } - /* alt path init */ - for ( num_altp = 0; num_altp < max_altp && num_altp < BN_MAX_ALTP; num_altp ++ ) { - if ( !( pBNS->altp[num_altp] = (BNS_ALT_PATH*)inchi_calloc( len_alt_path,sizeof(BNS_ALT_PATH))) ) { - return DeAllocateBnStruct( pBNS ); - } - ALTP_ALLOCATED_LEN(pBNS->altp[num_altp]) = len_alt_path; - pBNS->len_alt_path = len_alt_path; /* ??? duplication ??? */ - /* re-init */ - ALTP_DELTA(pBNS->altp[num_altp]) = 0; - ALTP_START_ATOM(pBNS->altp[num_altp]) = NO_VERTEX; - ALTP_END_ATOM(pBNS->altp[num_altp]) = NO_VERTEX; - ALTP_PATH_LEN(pBNS->altp[num_altp]) = 0; - } - pBNS->alt_path = NULL; - pBNS->num_altp = 0; - pBNS->max_altp = num_altp; - - - /* fill vertices (no connectivity) */ - pBNS->vert[0].iedge = pBNS->iedge; - for ( i = 0; i < num_atoms; i ++ ) { - k = pBNS->vert[i].max_adj_edges = at[i].valence + (nMaxAddEdges + NUM_KINDS_OF_GROUPS); - pBNS->vert[i+1].iedge = pBNS->vert[i].iedge + k; - } - pBNS->num_atoms = num_atoms; /* number of real atoms */ - pBNS->num_added_atoms = 0; - pBNS->num_t_groups = 0; /* number of added t-groups */ - pBNS->num_c_groups = 0; - pBNS->nMaxAddAtoms = nMaxAddAtoms; - pBNS->nMaxAddEdges = nMaxAddEdges; - - pBNS->num_vertices = num_atoms; /* current number of vertices, a sum of - pBNS->num_atoms - pBNS->num_t_groups - pBNS->num_added_atoms - */ - pBNS->max_vertices = max_vertices; - - - pBNS->num_bonds = num_bonds; /* number of real edges (bonds) */ - pBNS->max_edges = max_edges; - pBNS->max_iedges = max_iedges; - - /* - To remove t-groups and added atoms: - In atoms i = 0..pBNS->num_atoms-1 - pBNS->vert[i].num_adj_edges = pBNS->vert[i].max_adj_edges - pBNS->nMaxAddEdges - NUM_KINDS_OF_GROUPS; - pBNS->num_vertices = pBNS->num_atoms; - pBNS->num_edges = pBNS->num_bonds; - pBNS->num_added_atoms = 0; - pBNS->num_t_groups = 0; - pBNS->num_added_edges = 0; - - ALTP_DELTA(pBNS->alt_path) = 0; - ALTP_START_ATOM(pBNS->alt_path) = NO_VERTEX; - ALTP_END_ATOM(pBNS->alt_path) = NO_VERTEX; - ALTP_PATH_LEN(pBNS->alt_path) = 0; - - */ - - - /* fill edges and connectivity */ - tot_st_cap = tot_st_flow = 0; - for ( i = 0, n_edges = 0; i < num_atoms; i ++ ) { - vert = &pBNS->vert[i]; - st_cap = 0; - st_flow = 0; - flag_alt_bond = 0; - for ( j = 0; j < at[i].valence; j ++ ) { - neigh = at[i].neighbor[j]; - /* find this bond at the neighbor */ - for ( k = 0; k < at[neigh].valence; k ++ ) { - if ( at[neigh].neighbor[k] == i ) { - break; - } - } - bond_type = (at[i].bond_type[j] & BOND_TYPE_MASK); - bond_mark = (at[i].bond_type[j] & ~BOND_TYPE_MASK); - if ( bond_type != BOND_SINGLE && bond_type != BOND_DOUBLE && - bond_type != BOND_TRIPLE /*&& bond_type != BOND_ALTERN*/ ) { - /* make Unknown or Alternating bonds single */ - bond_type = 1; - at[i].bond_type[j] = bond_mark | bond_type; - num_changed_bonds ++; - } - if ( neigh > i ) { - /* this is the first time we encounter this bond */ - f1 = MAX_AT_FLOW(at[i]); - f2 = MAX_AT_FLOW(at[neigh]); - edge_flow = bond_type-1; - if ( edge_flow > MAX_BOND_EDGE_CAP ) { - flag_alt_bond ++; - edge_flow = 0; /* BNS will determine flows (that is, bonds) */ - edge_cap = AROM_BOND_EDGE_CAP; - } else { -#if ( 0 && KETO_ENOL_TAUT == 1 ) /* ????? */ - edge_cap = inchi_max(f1, f2); -#else - edge_cap = inchi_min(f1, f2); -#endif - edge_cap = inchi_min(edge_cap, MAX_BOND_EDGE_CAP); /* max capacity = 2 means up to triple bond */ - } - pBNS->edge[n_edges].neighbor1 = (AT_NUMB)i; - pBNS->edge[n_edges].neighbor12 = (AT_NUMB)(i ^ neigh); - pBNS->edge[n_edges].flow = - pBNS->edge[n_edges].flow0 = edge_flow; - pBNS->edge[n_edges].cap = - pBNS->edge[n_edges].cap0 = edge_cap; - pBNS->edge[n_edges].neigh_ord[0] = j; - pBNS->edge[n_edges].neigh_ord[1] = k; - pBNS->edge[n_edges].pass = 0; - pBNS->edge[n_edges].forbidden = 0; - - vert->iedge[j] = pBNS->vert[neigh].iedge[k] = n_edges ++; - } else { - /* this is the second time we encounter this bond. It was stored at */ - int iedge = pBNS->vert[neigh].iedge[k]; - edge_cap = pBNS->edge[iedge].cap; - edge_flow = pBNS->edge[iedge].flow; - } - st_flow += edge_flow; - st_cap += edge_cap; - } - vert->num_adj_edges = j; - vert->st_edge.cap = - vert->st_edge.cap0 = MAX_AT_FLOW(at[i]); - vert->st_edge.flow = - vert->st_edge.flow0 = st_flow; - vert->type = BNS_VERT_TYPE_ATOM; - tot_st_cap += vert->st_edge.cap; - tot_st_flow += vert->st_edge.flow; - } - *pNum_changed_bonds = num_changed_bonds/2; - - pBNS->num_edges = n_edges; /* number of edges */ - pBNS->num_added_edges = 0; - - pBNS->tot_st_cap = tot_st_cap; - pBNS->tot_st_flow = tot_st_flow; - - return pBNS; -} - - - -/*****************************************************************************/ -BN_STRUCT* DeAllocateBnStruct( BN_STRUCT *pBNS ) -{ - int i; - if ( pBNS ) { - if ( pBNS->edge ) { - inchi_free( pBNS->edge ); - } - for ( i = 0; i < pBNS->max_altp && i < BN_MAX_ALTP ; i ++ ) { - if ( pBNS->altp[i] ) { - inchi_free( pBNS->altp[i] ); - } - } - if ( pBNS->vert ) { - if ( pBNS->vert[0].iedge ) { - inchi_free( pBNS->vert[0].iedge ); - } - inchi_free( pBNS->vert ); - } - inchi_free( pBNS ); - } - return NULL; -} - - - -/*****************************************************************************/ -int ReInitBnStructAltPaths( BN_STRUCT *pBNS ) -{ - int i; - for ( i = 0; i < pBNS->max_altp && i < BN_MAX_ALTP; i ++ ) { - if ( pBNS->altp[i] ) { - ALTP_DELTA(pBNS->altp[i]) = 0; - ALTP_PATH_LEN(pBNS->altp[i]) = 0; - ALTP_START_ATOM(pBNS->altp[i]) = NO_VERTEX; - ALTP_END_ATOM(pBNS->altp[i]) = NO_VERTEX; - } - } - pBNS->alt_path = NULL; - pBNS->num_altp = 0; - return i; -} - - - -/*****************************************************************************/ -int ReInitBnStructAddGroups( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, T_GROUP_INFO *tgi, C_GROUP_INFO *cgi ) -{ - int ret; - /* strip all t-groups and c-groups */ - ret = ReInitBnStruct( pBNS, at, num_atoms, 0 ); - if ( ret ) { - ret = BNS_REINIT_ERR; - goto exit_function; - } -/*#if ( MOVE_CHARGES == 1 )*/ - if ( *pBNS->pbTautFlags & TG_FLAG_MOVE_POS_CHARGES ) { - /* add c-groups */ - ret = AddCGroups2BnStruct( pBNS, at, num_atoms, cgi ); - if ( IS_BNS_ERROR( ret ) ) { - goto exit_function; - } - } -/*#endif*/ - /* add t-groups */ - ret = AddTGroups2BnStruct( pBNS, at, num_atoms, tgi ); - if ( IS_BNS_ERROR( ret ) ) { - goto exit_function; - } -exit_function: - return ret; -} - - - -/*****************************************************************************/ -int ReInitBnStruct( BN_STRUCT *pBNS, inp_ATOM *at, int num_at, int bRemoveGroupsFromAtoms ) -{ - int i, vfict, kfict, iedgefict, endpoint, centerpoint, iedge, k; - int ret = 0; - if ( pBNS ) { - if ( pBNS->vert && pBNS->edge ) { - /* debug */ - for ( k = 0, i = 0; k < pBNS->num_edges; k ++ ) { - if ( pBNS->edge[k].pass ) { - i ++; - } - } - ret += i * 100; - /* restore flow and cap on edges to vertices connected to fictitious atoms */ - for ( vfict = pBNS->num_atoms; vfict < pBNS->num_vertices; vfict ++ ) { - for ( kfict = 0; kfict < pBNS->vert[vfict].num_adj_edges; kfict ++ ) { - iedgefict = pBNS->vert[vfict].iedge[kfict]; /* fictitious edge to the endpoint */ - endpoint = pBNS->edge[iedgefict].neighbor12 ^ vfict; /* the endpoint */ - /* to simlify restore cap and flow in ALL edges to the endpoint */ - if ( bRemoveGroupsFromAtoms && endpoint < num_at ) { - at[endpoint].c_point = 0; - at[endpoint].endpoint = 0; - } - for ( k = 0; k < pBNS->vert[endpoint].num_adj_edges; k ++ ) { - iedge = pBNS->vert[endpoint].iedge[k]; /* edge to endpoint */ - centerpoint = pBNS->edge[iedge].neighbor12 ^ endpoint; - pBNS->edge[iedge].cap = pBNS->edge[iedge].cap0; - pBNS->edge[iedge].flow = pBNS->edge[iedge].flow0; - pBNS->edge[iedge].pass = 0; -#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) - pBNS->edge[iedge].forbidden &= pBNS->edge_forbidden_mask; -#endif - pBNS->vert[centerpoint].st_edge.cap = pBNS->vert[centerpoint].st_edge.cap0; - pBNS->vert[centerpoint].st_edge.flow = pBNS->vert[centerpoint].st_edge.flow0; - } - pBNS->vert[endpoint].st_edge.cap = pBNS->vert[endpoint].st_edge.cap0; - pBNS->vert[endpoint].st_edge.flow = pBNS->vert[endpoint].st_edge.flow0; - pBNS->vert[endpoint].type &= BNS_VERT_TYPE_ATOM; - } - } - /* reset number of neighbors */ - if ( pBNS->num_edges > pBNS->num_bonds ) { - for ( i = 0; i < pBNS->num_atoms; i ++ ) { - pBNS->vert[i].num_adj_edges = - pBNS->vert[i].max_adj_edges - pBNS->nMaxAddEdges - NUM_KINDS_OF_GROUPS; - } - } - } else { - ret += 2; - } - if ( !pBNS->edge ) { - ret += 4; - } - if ( !pBNS->iedge ) { - ret += 8; - } - - ReInitBnStructAltPaths( pBNS ); - - pBNS->num_vertices = pBNS->num_atoms; - pBNS->num_edges = pBNS->num_bonds; - pBNS->num_added_atoms = 0; - pBNS->num_t_groups = 0; - pBNS->num_c_groups = 0; - pBNS->num_added_edges = 0; - - } else { - ret += 1; - } - - return ret; -} - - - -/*****************************************************************************/ -int CompTGroupNumber( const void *tg1, const void *tg2 ) -{ - return (int)((const T_GROUP *)tg1)->nGroupNumber - (int)((const T_GROUP *)tg2)->nGroupNumber; -} - - - -/*****************************************************************************/ -int CompCGroupNumber( const void *cg1, const void *cg2 ) -{ - return (int)((const C_GROUP *)cg1)->nGroupNumber - (int)((const C_GROUP *)cg2)->nGroupNumber; -} - - - -/*****************************************************************************/ -int AddTGroups2BnStruct( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, T_GROUP_INFO *tgi ) -{ - int ret = 0; - /* ret = ReInitBnStruct( pBNS ); */ - if ( tgi && tgi->num_t_groups && tgi->t_group ) { - int i, k, endpoint, centerpoint, fictpoint; - int num_tg = tgi->num_t_groups; - int num_edges = pBNS->num_edges; - int num_vertices = pBNS->num_vertices; - BNS_VERTEX *vert_ficpoint, *ver_ficpont_prev; /* fictitious vertex describing t-group */ - BNS_VERTEX *vert_endpoint; - BNS_EDGE *edge; /* edge between that vertex and the tautomeric endpoint */ - int nMaxTGroupNumber = 0; - ENDPOINT_INFO eif; - - /* Debug: check overflow */ - if ( num_vertices + num_tg >= pBNS->max_vertices ) { - return BNS_VERT_EDGE_OVFL; - } - /* find the largest t-group ID */ - for ( i = 0; i < num_tg; i ++ ) { - if ( tgi->t_group[i].nGroupNumber > nMaxTGroupNumber ) { - nMaxTGroupNumber = tgi->t_group[i].nGroupNumber; - } - } - /* since t-group IDs may be not contiguous, clear all vertices that will be added. - all-zeroes-vertex will be ignored by the BNS - */ - memset( pBNS->vert+num_vertices, 0, nMaxTGroupNumber*sizeof(pBNS->vert[0]) ); - /* Make sure the last t-group has the largest t-group ID: - this is necessary to correctly add new edges and vertices for testing augmenting paths - */ -#if ( bRELEASE_VERSION != 1 ) - insertions_sort( tgi->t_group, num_tg, sizeof(tgi->t_group[0]), CompTGroupNumber ); - for ( i = 1; i < num_tg; i ++ ) { - if ( 1 != tgi->t_group[i].nGroupNumber - tgi->t_group[i-1].nGroupNumber ) { - return BNS_BOND_ERR; - } - } -#else - if ( nMaxTGroupNumber != tgi->t_group[num_tg-1].nGroupNumber ) { - insertions_sort( tgi->t_group, num_tg, sizeof(tgi->t_group[0]), CompTGroupNumber ); - } -#endif - /* initialize new fictitious vertices */ - ver_ficpont_prev = pBNS->vert+num_vertices - 1; - - for ( i = 0; i < num_tg; i ++, ver_ficpont_prev = vert_ficpoint ) { - /* - vert_ficpoint-1 is the last vertex; - vert_ficpoint is the vertex that is being added - Note: nGroupNumber are not contiguous - */ - vert_ficpoint = pBNS->vert+num_vertices + tgi->t_group[i].nGroupNumber - 1; - vert_ficpoint->iedge = ver_ficpont_prev->iedge + ver_ficpont_prev->max_adj_edges; - vert_ficpoint->max_adj_edges = tgi->t_group[i].nNumEndpoints+BNS_ADD_EDGES+BNS_ADD_SUPER_TGROUP; - vert_ficpoint->num_adj_edges = 0; - vert_ficpoint->st_edge.flow = vert_ficpoint->st_edge.flow0 = 0; - vert_ficpoint->st_edge.cap = vert_ficpoint->st_edge.cap0 = 0; - vert_ficpoint->type = BNS_VERT_TYPE_TGROUP; - } - - for ( endpoint = 0; endpoint < num_atoms; endpoint ++ ) { - if ( !at[endpoint].endpoint ) - continue; - fictpoint = at[endpoint].endpoint + num_vertices - 1; - vert_ficpoint = pBNS->vert + fictpoint; - vert_endpoint = pBNS->vert + endpoint; - /* Debug: check overflow */ - if ( fictpoint >= pBNS->max_vertices || - num_edges >= pBNS->max_edges || - vert_ficpoint->num_adj_edges >= vert_ficpoint->max_adj_edges || - vert_endpoint->num_adj_edges >= vert_endpoint->max_adj_edges ) { - ret = BNS_VERT_EDGE_OVFL; - break; - } - /* obtain donor/acceptor info */ - if ( !nGetEndpointInfo( at, endpoint, &eif ) ) { -#if ( KETO_ENOL_TAUT == 1 ) - if ( !((tgi->bTautFlags & TG_FLAG_KETO_ENOL_TAUT ) && - nGetEndpointInfo_KET( at, endpoint, &eif )) ) -#endif - { - ret = BNS_BOND_ERR; - break; - } - } - - vert_endpoint->type |= BNS_VERT_TYPE_ENDPOINT; - - /* set capacity = 1 to the edges from the endpoint to the centerpoint(s) */ - for ( k = 0; k < vert_endpoint->num_adj_edges; k ++ ) { - int iedge = vert_endpoint->iedge[k]; - if ( !pBNS->edge[iedge].cap ) { - /* single bond, possibly between endpoint and centerpoint */ - centerpoint = (pBNS->edge[iedge].neighbor12 ^ endpoint); - if ( centerpoint < pBNS->num_atoms && - pBNS->vert[centerpoint].st_edge.cap >= 1 ) { - int bond_type = (at[endpoint].bond_type[k] & BOND_TYPE_MASK); - if (bond_type == BOND_TAUTOM || - bond_type == BOND_ALTERN || - bond_type == BOND_ALT12NS || - bond_type == BOND_SINGLE ) { - pBNS->edge[iedge].cap = 1; - } - } - } - } - /* create a new edge connecting endpoint to the new fictitious t-group vertex vert_ficpoint */ - edge = pBNS->edge + num_edges; - edge->cap = 1; - edge->flow = 0; - edge->pass = 0; -#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) - edge->forbidden &= pBNS->edge_forbidden_mask; -#endif - /* later include case when the charge change allows the endpoint to become tautomeric */ - /* mark endoint having moveable H atom with flow=1 */ - - /* -- old "no charges" version -- */ - /* if (at[endpoint].chem_bonds_valence == at[endpoint].valence) */ - /* -- the following line takes charges into account -- */ - if ( eif.cDonor ) /* means the endpoint has an H-atom to donate */ - { - /* increment edge flow */ - edge->flow ++; - /* increment one vertex st-flow & cap */ - vert_ficpoint->st_edge.flow ++; - vert_ficpoint->st_edge.cap ++; - /* increment another vertex st-flow & cap */ - vert_endpoint->st_edge.flow ++; - vert_endpoint->st_edge.cap ++; - } - /* connect edge to endpoint and fictpoint and increment the counters of neighbors and edges */ - edge->neighbor1 = endpoint; /* the smallest out of v1=endopoint and v2=num_vertices */ - edge->neighbor12 = endpoint ^ fictpoint; /* v1 ^ v2 */ - vert_endpoint->iedge[vert_endpoint->num_adj_edges] = num_edges; - vert_ficpoint->iedge[vert_ficpoint->num_adj_edges] = num_edges ++; - edge->neigh_ord[0] = vert_endpoint->num_adj_edges ++; - edge->neigh_ord[1] = vert_ficpoint->num_adj_edges ++; - edge->cap0 = edge->cap; - edge->flow0 = edge->flow; - } - - pBNS->num_edges = num_edges; - pBNS->num_vertices += nMaxTGroupNumber; - pBNS->num_t_groups = num_tg; - - } - return ret; -} - - - -/*****************************************************************************/ -/*#if ( MOVE_CHARGES == 1 )*/ /* { */ - - - -/*****************************************************************************/ -int AddCGroups2BnStruct( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, C_GROUP_INFO *cgi ) -{ - int ret = 0; - /* ret = ReInitBnStruct( pBNS ); */ - if ( cgi && cgi->num_c_groups && cgi->c_group ) { - int i, k, c_point, centerpoint, fictpoint; - int num_cg = cgi->num_c_groups; - int num_edges = pBNS->num_edges; - int num_vertices = pBNS->num_vertices; - BNS_VERTEX *vert_ficpoint, *ver_ficpont_prev; /* fictitious vertex describing charge c-group */ - BNS_VERTEX *vertex_cpoint; - BNS_EDGE *edge; /* edge between that vertex and the tautomeric c_point */ - int nMaxCGroupNumber = 0; - - /* Debug: check overflow */ - if ( num_vertices + num_cg >= pBNS->max_vertices ) { - return BNS_VERT_EDGE_OVFL; - } - /* find the largest t-group ID */ - for ( i = 0; i < num_cg; i ++ ) { - if ( cgi->c_group[i].nGroupNumber > nMaxCGroupNumber ) { - nMaxCGroupNumber = cgi->c_group[i].nGroupNumber; - } - } - /* since t-group IDs may be not contiguous, clear all vertices that will be added. - all-zeroes-vertex will be ignored by the BNS - */ - memset( pBNS->vert+num_vertices, 0, nMaxCGroupNumber*sizeof(pBNS->vert[0]) ); - /* Make sure the last t-group has the largest t-group ID: - this is necessary to correctly add new edges and vertices for testing augmenting paths - */ -#if ( bRELEASE_VERSION != 1 ) - insertions_sort( cgi->c_group, num_cg, sizeof(cgi->c_group[0]), CompCGroupNumber ); - for ( i = 1; i < num_cg; i ++ ) { - if ( 1 != cgi->c_group[i].nGroupNumber - cgi->c_group[i-1].nGroupNumber ) { - return BNS_BOND_ERR; - } - } -#else - if ( nMaxCGroupNumber != cgi->c_group[num_cg-1].nGroupNumber ) { - insertions_sort( cgi->c_group, num_cg, sizeof(cgi->c_group[0]), CompCGroupNumber ); - } -#endif - /**************************************/ - /* initialize new fictitious vertices */ - /* representing c-point groups */ - /**************************************/ - ver_ficpont_prev = pBNS->vert+num_vertices - 1; - - for ( i = 0; i < num_cg; i ++, ver_ficpont_prev = vert_ficpoint ) { - /* - vert_ficpoint-1 is the last vertex; - vert_ficpoint is the being added vertex - Note: nGroupNumber are not contiguous - */ - vert_ficpoint = pBNS->vert+num_vertices + cgi->c_group[i].nGroupNumber - 1; - vert_ficpoint->iedge = ver_ficpont_prev->iedge + ver_ficpont_prev->max_adj_edges; - vert_ficpoint->max_adj_edges = cgi->c_group[i].num_CPoints+BNS_ADD_EDGES; - vert_ficpoint->num_adj_edges = 0; - vert_ficpoint->st_edge.flow = vert_ficpoint->st_edge.flow0 = 0; - vert_ficpoint->st_edge.cap = vert_ficpoint->st_edge.cap0 = 0; - vert_ficpoint->type = BNS_VERT_TYPE_C_GROUP; - } - - /************************************************/ - /* connect c-points to the fictitious vertices */ - /* representing c-point groups; set caps, flows */ - /************************************************/ - for ( c_point = 0; c_point < num_atoms; c_point ++ ) { - if ( !at[c_point].c_point ) - continue; - fictpoint = at[c_point].c_point + num_vertices - 1; /* c-group vertex index */ - vert_ficpoint = pBNS->vert + fictpoint; /* c-group vertex */ - vertex_cpoint = pBNS->vert + c_point; /* c_point vertex */ - /* Debug: check overflow */ - if ( fictpoint >= pBNS->max_vertices || - num_edges >= pBNS->max_edges || - vert_ficpoint->num_adj_edges >= vert_ficpoint->max_adj_edges || - vertex_cpoint->num_adj_edges >= vertex_cpoint->max_adj_edges ) { - ret = BNS_VERT_EDGE_OVFL; - break; - } - vertex_cpoint->type |= BNS_VERT_TYPE_C_POINT; -#if ( FIX_CPOINT_BOND_CAP != 1 ) /* { */ - /* set capacity = 1 to the edges from the c_point to the centerpoint(s) */ - /* if their current capacity is zero */ - /* the centerpoint is any adjacent atom that is adjacent to a multiple bond */ - for ( k = 0; k < vertex_cpoint->num_adj_edges; k ++ ) { - int iedge = vertex_cpoint->iedge[k]; - if ( !pBNS->edge[iedge].cap ) { - /* single bond, possibly between c_point and centerpoint */ - centerpoint = (pBNS->edge[iedge].neighbor12 ^ c_point); - if ( centerpoint < pBNS->num_atoms && - pBNS->vert[centerpoint].st_edge.cap >= 1 ) { - int bond_type = (at[c_point].bond_type[k] & BOND_TYPE_MASK); - if ( bond_type == BOND_TAUTOM || - bond_type == BOND_ALTERN || - bond_type == BOND_SINGLE ) { - pBNS->edge[iedge].cap = 1; - } - } - } - } -#endif /* } FIX_CPOINT_BOND_CAP */ - /* create a new edge connecting c_point to the new fictitious c-group vertex vert_ficpoint */ - edge = pBNS->edge + num_edges; - edge->cap = 1; - edge->flow = 0; - edge->pass = 0; -#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) - edge->forbidden &= pBNS->edge_forbidden_mask; -#endif - /* mark edge to c-point having NO moveable charge with flow=1 */ - if ( !CHARGED_CPOINT(at,c_point) ) { - /* increment edge flow */ - edge->flow ++; - /* increment c-group vertex st-flow & cap */ - vert_ficpoint->st_edge.flow ++; - vert_ficpoint->st_edge.cap ++; - /* increment c-point vertex st-flow & cap */ - vertex_cpoint->st_edge.flow ++; - vertex_cpoint->st_edge.cap ++; - } -#if ( FIX_CPOINT_BOND_CAP == 1 ) /* { */ - /* set capacity = 1 to the edges from the c_point to the centerpoint(s) */ - /* if their current capacity is zero */ - /* the centerpoint is any adjacent atom that is adjacent to a multiple bond */ - for ( k = 0; k < vertex_cpoint->num_adj_edges; k ++ ) { - int iedge = vertex_cpoint->iedge[k]; - VertexFlow nNewCap = vertex_cpoint->st_edge.cap; - centerpoint = (pBNS->edge[iedge].neighbor12 ^ c_point); - if ( !pBNS->edge[iedge].cap ) { - /* single bond, possibly between c_point and centerpoint */ - if ( centerpoint < pBNS->num_atoms && - pBNS->vert[centerpoint].st_edge.cap >= 1 ) { - nNewCap = inchi_min( pBNS->vert[centerpoint].st_edge.cap, nNewCap ); - nNewCap = inchi_min( nNewCap, MAX_BOND_EDGE_CAP ); - pBNS->edge[iedge].cap = nNewCap; - } -#if ( FIX_CPOINT_BOND_CAP2 == 1 ) /* multiple bond */ - else - if ( centerpoint < pBNS->num_atoms && - edge->flow && pBNS->edge[iedge].cap < MAX_BOND_EDGE_CAP ) { - pBNS->edge[iedge].cap ++; - } -#endif - } - } -#endif /* } FIX_CPOINT_BOND_CAP */ - /* connect edge to c_point and fictpoint and increment the counters of neighbors and edges */ - edge->neighbor1 = c_point; /* the smallest out of v1=endopoint and v2=num_vertices */ - edge->neighbor12 = c_point ^ fictpoint; /* v1 ^ v2 */ - vertex_cpoint->iedge[vertex_cpoint->num_adj_edges] = num_edges; - vert_ficpoint->iedge[vert_ficpoint->num_adj_edges] = num_edges ++; - edge->neigh_ord[0] = vertex_cpoint->num_adj_edges ++; - edge->neigh_ord[1] = vert_ficpoint->num_adj_edges ++; - edge->cap0 = edge->cap; - edge->flow0 = edge->flow; - - } - - pBNS->num_edges = num_edges; - pBNS->num_vertices += nMaxCGroupNumber; - pBNS->num_c_groups = num_cg; - - } - return ret; -} -/*#endif*/ /* } MOVE_CHARGES == 1 */ - - - - -/*****************************************************************************/ -void ClearAllBnDataVertices( Vertex *v, Vertex value, int size ) -{ - int i; - for ( i = 0; i < size; i ++ ) { - v[i] = value; - } -} - - - -/*****************************************************************************/ -void ClearAllBnDataEdges( Edge *e, Vertex value, int size ) -{ - int i; - for ( i = 0; i < size; i ++ ) { - e[i][0] = value; - } -} - - -/*****************************************************************************/ -BN_DATA *DeAllocateBnData( BN_DATA *pBD ) -{ - if ( pBD ) { - if ( pBD->BasePtr ) - inchi_free( pBD->BasePtr ); - if ( pBD->SwitchEdge ) - inchi_free( pBD->SwitchEdge ); - if ( pBD->Tree ) - inchi_free( pBD->Tree ); - if ( pBD->ScanQ ) - inchi_free( pBD->ScanQ ); - if ( pBD->Pu ) - inchi_free( pBD->Pu ); - if ( pBD->Pv ) - inchi_free( pBD->Pv ); -#if ( BNS_RAD_SEARCH == 1 ) - if ( pBD->RadEndpoints ) { - inchi_free( pBD->RadEndpoints ); - } - if ( pBD->RadEdges ) { - inchi_free( pBD->RadEdges ); - } -#endif - inchi_free( pBD ); - } - return NULL; -} - - -/*****************************************************************************/ -BN_DATA *AllocateAndInitBnData( int max_num_vertices ) -{ - BN_DATA *pBD = NULL; - int max_len_Pu_Pv; - max_num_vertices = 2*max_num_vertices+2; - max_len_Pu_Pv = max_num_vertices/2+1; - max_len_Pu_Pv += max_len_Pu_Pv % 2; /* even length */ - if ( !(pBD = (BN_DATA *) inchi_calloc( 1, sizeof(BN_DATA) ) ) || - !(pBD->BasePtr = (Vertex *) inchi_calloc( max_num_vertices, sizeof(Vertex) ) ) || - !(pBD->SwitchEdge = (Edge *) inchi_calloc( max_num_vertices, sizeof(Edge ) ) ) || - !(pBD->Tree = (S_CHAR *) inchi_calloc( max_num_vertices, sizeof(S_CHAR) ) ) || - !(pBD->ScanQ = (Vertex *) inchi_calloc( max_num_vertices, sizeof(Vertex) ) ) || - !(pBD->Pu = (Vertex *) inchi_calloc( max_len_Pu_Pv, sizeof(Vertex) ) ) || -#if ( BNS_RAD_SEARCH == 1 ) - !(pBD->RadEndpoints = (Vertex *) inchi_calloc( max_len_Pu_Pv, sizeof(Vertex) ) ) || - !(pBD->RadEdges = (EdgeIndex*) inchi_calloc( max_len_Pu_Pv, sizeof(EdgeIndex) ) ) || -#endif - !(pBD->Pv = (Vertex *) inchi_calloc( max_len_Pu_Pv, sizeof(Vertex) ) ) - ) { - pBD = DeAllocateBnData( pBD ); - } else { - /* Initialize data */ - ClearAllBnDataEdges(pBD->SwitchEdge, NO_VERTEX, max_num_vertices); - ClearAllBnDataVertices(pBD->BasePtr, NO_VERTEX, max_num_vertices); - memset(pBD->Tree, TREE_NOT_IN_M, max_num_vertices); - pBD->QSize = -1; - pBD->max_len_Pu_Pv = max_len_Pu_Pv; - pBD->max_num_vertices = max_num_vertices; -#if ( BNS_RAD_SEARCH == 1 ) - pBD->nNumRadEndpoints = 0; -#endif - } - return pBD; -} - - - -/*****************************************************************************/ -int ReInitBnData( BN_DATA *pBD ) -{ - int i, ret = 0; - Vertex u, v; - if ( pBD ) { - if ( !pBD->ScanQ ) { - ret += 2; - } - if ( !pBD->BasePtr ) { - ret += 4; - } - if ( !pBD->SwitchEdge ) { - ret += 8; - } - if ( !pBD->Tree ) { - ret += 16; - } - if ( !ret ) { - for ( i = 0; i <= pBD->QSize; i ++ ) { - u = pBD->ScanQ[i]; - v = prim(u); - pBD->BasePtr[u] = - pBD->BasePtr[v] = - pBD->SwitchEdge_Vert1(u) = - pBD->SwitchEdge_Vert1(v) = NO_VERTEX; - pBD->Tree[u] = - pBD->Tree[v] = TREE_NOT_IN_M; - } - } - pBD->QSize = -1; - if ( !pBD->Pu ) { - ret += 32; - } - if ( !pBD->Pv ) { - ret += 64; - } - } else { - ret += 1; - } - - return ret; -} - - - -/*****************************************************************************/ -int GetVertexDegree( BN_STRUCT* pBNS, Vertex v ) -{ - int i = v / 2 - 1; - if ( i >= 0 ) { - if ( pBNS->vert[i].st_edge.cap > 0 ) { - return pBNS->vert[i].num_adj_edges+1; /* add 1 neighbor for s or t */ - } else { - return 0; /* since the edge s-v has zero capacity, we ignore vertex v */ - } - } else { - return pBNS->num_vertices; - } -} - - - -/*****************************************************************************/ -Vertex Get2ndEdgeVertex( BN_STRUCT* pBNS, Edge uv ) -{ - /* - Vertex ret; - */ - if ( uv[1] >= 0 ) { - /* -- debug -- - if ( uv[1] > pBNS->num_edges || uv[0] > 2*pBNS->num_vertices+3 ) { - int stop = 1; - } - ret = ((uv[0]-2) ^ (2*pBNS->edge[uv[1]].neighbor12+1)) + 2; - if ( ret > 2*pBNS->num_vertices+3 ) { - int stop = 1; - } - return ret; - -- end debug -- */ - return ((uv[0]-2) ^ (2*pBNS->edge[uv[1]].neighbor12+1)) + 2; - /*short u = uv[0]-FIRST_INDX; */ - /*short t = 2*(((u / 2 - 1) ^ pBNS->edge[uv[1]].neighbor12) + 1) + ((u+1) & 1) + FIRST_INDX; */ - /*return t; */ - } - if ( uv[0] <= 1 ) - return -(1 + uv[1]); /* vertex1 is s or t, return x or y */ - else - return uv[0] % 2; /* vertex1 is x or y, return s or t; never happens? -- NSC 3737, 7634,... */ -} - - - -/*****************************************************************************/ -Vertex GetVertexNeighbor( BN_STRUCT* pBNS, Vertex v, int neigh, EdgeIndex *iedge ) -{ - /* neigh = 0 => the neighbor is s or t except case when v is s or t. */ - /* v= FIRST_INDX or FIRST_INDX+1: v is s or t respectively */ - int i, neighbor; - if ( (i = v - 2) >= 0 ) { - /* neighbor of x or y */ - if ( neigh ) { - neigh --; - /* x or y */ - *iedge = pBNS->vert[i/2].iedge[neigh]; - if ( !(pBNS->edge[*iedge].cap & EDGE_FLOW_MASK) || IS_FORBIDDEN(pBNS->edge[*iedge].forbidden, pBNS) ) { - return NO_VERTEX; - } - neighbor = (i ^ (2 * pBNS->edge[*iedge].neighbor12 + 1)) + 2; /* parity opposite to v parity */ - } else { - /* neighbor of x or y is s or t */ - neighbor = (v & 1); /* s or t, same parity as v */ - *iedge = -( neighbor + 1 ); - } - } else { - /* neighbor of s or t: x or y, same parity as v */ - if ( !(pBNS->vert[neigh].st_edge.cap & EDGE_FLOW_ST_MASK) ) { - return NO_VERTEX; - } - neighbor = 2*neigh + 2 + (v & 1); /* parity same as the parity of v */ - *iedge = -( neighbor + 1 ); - } - return neighbor; -} - - - -/*****************************************************************************/ -int GetEdgePointer( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv, BNS_EDGE **uv, S_CHAR *s_or_t ) -{ - int i = u / 2 - 1; - int j = v / 2 - 1; - int bBackward = BNS_WRONG_PARMS; - *uv = NULL; - if ( i >= 0 ) { - /* u is an atom */ - if ( j >= 0 ) { - /* v is an atom */ - if ( (u+v)%2 ) { - *uv = pBNS->edge+iuv; - bBackward = ( u & 1 ); - *s_or_t = 0; - } - } else - /* v is s or t */ - if ( v >= 0 && !((u+v)%2) ) { - *uv = (BNS_EDGE*)&pBNS->vert[i].st_edge; - bBackward = !(v & 1); - *s_or_t = v+3; /* 3=> v=s, 4=> v=t */ - } - } else - if ( j >= 0 ) { - /* u is s or t */ - if ( u >= 0 && !((u+v)%2) ) { - /* v is an atom */ - *uv = (BNS_EDGE*)&pBNS->vert[j].st_edge; - bBackward = (u & 1 ); - *s_or_t = u+1; /* 1=> u=s, 2=> u=t */ - } - } - return bBackward; -} - - - -/*****************************************************************************/ -int AugmentEdge( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv, int delta, S_CHAR bReverse, int bChangeFlow ) -{ - int f, flow, ret=0; - - BNS_ST_EDGE *pst_edge; - BNS_EDGE *pedge; - S_CHAR s_or_t; - int bBackward = GetEdgePointer( pBNS, u, v, iuv, &pedge, &s_or_t ); - if ( !IS_BNS_ERROR(bBackward) ) { - if ( bBackward ) { - delta = -delta; - } - - if ( s_or_t ) { - pst_edge = ( BNS_ST_EDGE *) pedge; - flow = pst_edge->flow; - f = (flow & EDGE_FLOW_ST_MASK) + delta; /* new flow */ - if ( !delta ) { - /*((BNS_ST_EDGE *)pedge)->flow = pst_edge->flow & ~EDGE_FLOW_ST_PATH;*/ - pst_edge->flow = pst_edge->flow & ~EDGE_FLOW_ST_PATH; - } else { - int cap = pst_edge->cap; - if ( f < 0 || f > cap ) { - ret = BNS_WRONG_PARMS; - } else { - if ( !(bChangeFlow & BNS_EF_CHNG_FLOW) ) { - f -= delta; /* do not actually change the flow, only find the augmenting path */ - } else - if ( delta ) { - /*((BNS_ST_EDGE *)pedge)->pass ++;*/ - pst_edge->pass ++; - } - flow = (flow & ~(EDGE_FLOW_ST_PATH | EDGE_FLOW_ST_MASK)) + f; - /*((BNS_ST_EDGE *)pedge)->flow = flow;*/ - pst_edge->flow = flow; - /*((BNS_ST_EDGE *)pedge)->delta += delta; */ - if ( bReverse ) { - /* u <- v; Note: in case of bReverse s_or_t has actually been determined - for the u' <- v' pair; therefore s and t should be switched - in order to correctly determine the 1st or the last atom - on the augmenting path. - */ - switch( s_or_t ) { - case 1: /* u = t: t<-v, v is the last vertex */ - ALTP_END_ATOM(pBNS->alt_path) = v / 2 - 1; - break; - case 2: /* u = s: s<-v, error */ - ret = BNS_WRONG_PARMS; - break; - case 3: /* v = t: u<-t, error */ - ret = BNS_WRONG_PARMS; - break; - case 4: /* v = s: u<-s, u is the first vertex */ - ALTP_START_ATOM(pBNS->alt_path) = u / 2 - 1; - ALTP_DELTA(pBNS->alt_path) = delta; - break; - default: - ret = BNS_WRONG_PARMS; - break; - } - } else { - /* u -> v */ - switch( s_or_t ) { - case 1: /* u = s: s->v, v is the first vertex */ - ALTP_START_ATOM(pBNS->alt_path) = v / 2 - 1; - ALTP_DELTA(pBNS->alt_path) = delta; - break; - case 2: /* u = t: t->v, error */ - ret = BNS_WRONG_PARMS; - break; - case 3: /* v = s: u->s, error */ - ret = BNS_WRONG_PARMS; - break; - case 4: /* v = t: u->t, u is the last vertex */ - ALTP_END_ATOM(pBNS->alt_path) = u / 2 - 1; - break; - default: - ret = BNS_WRONG_PARMS; - break; - } - } - } - } - } else { - f = (pedge->flow & EDGE_FLOW_MASK) + delta; - if ( !delta ) { - pedge->flow &= ~EDGE_FLOW_PATH; - } else { - if ( f < 0 || f > pedge->cap ) { - ret = BNS_WRONG_PARMS; - } else { - AT_NUMB iu = u / 2 - 1; - AT_NUMB iv = v / 2 - 1; - int indx; - if ( !(bChangeFlow & BNS_EF_CHNG_FLOW) ) { - f -= delta; /* do not actually change the flow, only find the augmenting path */ - } else - if ( delta ) { - pedge->pass ++; - } - pedge->flow = (pedge->flow & ~(EDGE_FLOW_PATH | EDGE_FLOW_MASK)) | f; - if ( ALTP_MAY_ADD(pBNS->alt_path) ) { - /* bReverse? u <- v : u -> v */ - indx = bReverse? (pedge->neighbor1 == iv) : (pedge->neighbor1 == iu); - ALTP_CUR_THIS_ATOM_NEIGHBOR(pBNS->alt_path) = pedge->neigh_ord[1-indx]; - ALTP_CUR_NEXT_ATOM_NEIGHBOR(pBNS->alt_path) = pedge->neigh_ord[indx]; - ALTP_NEXT(pBNS->alt_path); - } else { - ALTP_OVERFLOW(pBNS->alt_path) = 1; - ret = BNS_ALTPATH_OVFL; - } - } - } - } - return ret? ret : f; - - } - return bBackward; -} - - - -/*********************************************************************************/ -/* find residual capacity and mark the edge as belonging to the augmenting path */ -/*********************************************************************************/ -int rescap_mark( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv ) -{ - BNS_ST_EDGE *pst_edge; - BNS_EDGE *pedge; - - int f, flow; - S_CHAR s_or_t; - int bBackward = GetEdgePointer( pBNS, u, v, iuv, &pedge, &s_or_t ); - - if ( !IS_BNS_ERROR( bBackward ) ) { - - if ( s_or_t ) { - pst_edge = (BNS_ST_EDGE *)pedge; - flow = pst_edge->flow; - f = (flow & EDGE_FLOW_ST_MASK); - if ( !bBackward ) { - f = (int)pst_edge->cap - f; - } - if ( flow & EDGE_FLOW_ST_PATH ) { - pBNS->bNotASimplePath ++; - f /= 2; /* this is the second time we pass the same edge: reduce flow by a factor of 2 */ - } else { - pst_edge->flow |= EDGE_FLOW_ST_PATH; /* mark the edge */ - } - } else { - flow = pedge->flow; - f = flow & EDGE_FLOW_MASK; - if ( !bBackward ) { - f = (int)pedge->cap - f; - } - if ( flow & EDGE_FLOW_PATH ) { - f /= 2; /* this is the second time we pass the same edge: reduce flow by a factor of 2 */ - pBNS->bNotASimplePath ++; - } else { - pedge->flow |= EDGE_FLOW_PATH; /* mark the edge */ - } - } - return f; - - } - return bBackward; -} - - - -/***************************************************************************** -* Get previous vertex in the searched path * -* z is SwitchEdge_Vert2(y) != y. Go backward from z to y * -******************************************************************************/ -Vertex GetPrevVertex( BN_STRUCT* pBNS, Vertex y, Edge *SwitchEdge, EdgeIndex *iuv ) -{ - Vertex w, z, x2, y2, n; - EdgeIndex iwy; - - w = SwitchEdge_Vert1(y); - z = SwitchEdge_Vert2(y); - iwy = SwitchEdge_IEdge(y); - if ( z == y ) { - *iuv = iwy; - return w; - } - x2 = prim(y); - y2 = prim(z); - n = 0; - while ( y2 != NO_VERTEX ) { - w = SwitchEdge_Vert1(y2); - z = SwitchEdge_Vert2(y2); - iwy = SwitchEdge_IEdge(y2); - if ( w == x2 ) { - *iuv = iwy; - /*return z; */ - return (y + z)%2? z : prim(z); - } - n ++; -#ifdef _DEBUG - if ( n ) { - int stop = 1; - } -#endif - if ( w == y2 ) - return NO_VERTEX; - y2 = w; - } - return y2; -} - - - -#define CHECK_TACN 1 - - - - -/***************************************************************************** - The purpose is to avoid paths - - (H-group)[u]---atom[v]---((-)-cgroup)[w], - - where atom[v] is not acidic and (-) and H are not interchangeable without - explicit bond tautomerism. - - It is important that acidic atoms are only O,S,Se,Te and should have - only one chemical bond. Only because of this an early rejection of - the vertex v (before it gets on SCANQ) is possible. - - CHECK_TACN == 1 prohibits replacing (-) on N with H unless H can be moved to N - along an alternating path from another heteroatom (t-group will be detected). -*****************************************************************************/ - - - -#if ( FIX_TACN_POSSIBLE_BUG == 1 ) /* { */ - - - -/*****************************************************************************/ -int bIgnoreVertexNonTACN_atom( BN_STRUCT* pBNS, Vertex u, Vertex v ) -{ -#define TYPE_T 1 /* t-group [also called H-group] */ -#define TYPE_CN 2 /* (-)c-group */ - int i, degree, ret, u_is_taut=0, w_is_taut, num_allowed=0, num_found_groups=0; - Vertex w; - EdgeIndex ivw; - if ( !pBNS->type_TACN || u <= 1 || v <= 1 || - (pBNS->vert[v/2-1].type & pBNS->type_TACN) ) { - return 0; /* add/remove H(+) is allowed for acidic atoms */ - } - if ( !pBNS->type_T || !pBNS->type_CN ) - return 0; /* should not happen */ - u_is_taut = ((pBNS->vert[u/2-1].type & pBNS->type_T) == pBNS->type_T )? TYPE_T : - ((pBNS->vert[u/2-1].type & pBNS->type_CN) == pBNS->type_CN)? TYPE_CN : 0; - if ( u_is_taut ) { - /* u is either t-group vertex or (-) c-group */ - degree = GetVertexDegree( pBNS, v ); - for ( i = 0; i < degree; i ++ ) { - /* v = vert[u].neighbor[i]; */ - w = GetVertexNeighbor( pBNS, v, i, &ivw ); - if ( w == NO_VERTEX || w <= 1 ) { - continue; /* the atom has only single bonds or it is s or t, ignore it */ - } - if ( w != u && (ret = rescap(pBNS, v, w, ivw)) > 0 ) { - num_allowed ++; - w_is_taut = ((pBNS->vert[w/2-1].type & pBNS->type_CN) == pBNS->type_CN)? TYPE_CN : - ((pBNS->vert[w/2-1].type & pBNS->type_T) == pBNS->type_T )? TYPE_T : 0; - if ( (u_is_taut | w_is_taut) == (TYPE_T | TYPE_CN) ) { - num_found_groups ++; - } - } - } - if ( num_found_groups && num_allowed == 1 ) { - return 1; /* reject */ - } - } - return 0; -#undef TYPE_T -#undef TYPE_CN -} - - - -#else /* } FIX_TACN_POSSIBLE_BUG { */ - - - -/*****************************************************************************/ -int bIgnoreVertexNonTACN_atom( BN_STRUCT* pBNS, Vertex u, Vertex v ) -{ - int i, degree, ret, u_is_taut=0, num_allowed=0, num_found_groups=0; - Vertex w; - EdgeIndex ivw; - if ( !pBNS->type_TACN || u <= 1 || v <= 1 || - (pBNS->vert[v/2-1].type & pBNS->type_TACN) ) { - return 0; /* add/remove H(+) is allowed for acidic atoms */ - } - if ( !pBNS->type_T || !pBNS->type_CN ) - return 0; /* should not happen */ - if ( (u_is_taut = (pBNS->vert[u/2-1].type & pBNS->type_T) == pBNS->type_T) || - ( (pBNS->vert[u/2-1].type & pBNS->type_CN) == pBNS->type_CN) ) { - /* u is either t-group vertex or (-) c-group */ - degree = GetVertexDegree( pBNS, v ); - for ( i = 0; i < degree; i ++ ) { - /* v = vert[u].neighbor[i]; */ - w = GetVertexNeighbor( pBNS, v, i, &ivw ); - if ( w == NO_VERTEX || w <= 1 ) { - continue; /* the atom has only single bonds or it is s or t, ignore it */ - } - if ( w != u && (ret = rescap(pBNS, v, w, ivw)) > 0 ) { - num_allowed ++; - if ( (u_is_taut? ((pBNS->vert[w/2-1].type & pBNS->type_CN) == pBNS->type_CN) : - ((pBNS->vert[w/2-1].type & pBNS->type_T) == pBNS->type_T ) ) ) { - num_found_groups ++; - } - } - } - if ( num_found_groups && num_allowed == 1 ) { - return 1; /* reject */ - } - - } - return 0; -} - - - -#endif /* } FIX_TACN_POSSIBLE_BUG */ - - - -/***************************************************************************** - The purpose is to avoid paths - (H-group)[u]---atom[v]---((-)-cgroup)[w], - - where atom[v] is not acidic and (-) and H are not interchangeable without - explicit bond tautomerism. - - It is important that acidic atoms are only O,S,Se,Te and should have - only one chemical bond. Only because of this an early rejection of - the vertex v (before it gets on SCANQ) is possible. -*****************************************************************************/ - - - -#if ( FIX_TACN_POSSIBLE_BUG == 1 ) /* { */ - - - -/*****************************************************************************/ -int bIgnoreVertexNonTACN_group( BN_STRUCT* pBNS, Vertex v, Vertex w, Edge *SwitchEdge ) -{ -#define TYPE_T 1 /* t-group [also called H-group] */ -#define TYPE_CN 2 /* (-)c-group */ - int u_is_taut=0, w_is_taut=0; - Vertex u; - EdgeIndex iuv; - if ( v <= 1 || w <= 1 ) - return 0; -#if ( CHECK_TACN == 1 ) - if ( !pBNS->type_TACN || - (pBNS->vert[v/2-1].type & pBNS->type_TACN) ) { - return 0; - } - if ( !pBNS->type_T || !pBNS->type_CN ) - return 0; /* should not happen */ -#endif - u = GetPrevVertex( pBNS, v, SwitchEdge, &iuv ); - /* - u = SwitchEdge_Vert1(v); - iuv = SwitchEdge_IEdge(v); - */ - if ( u == NO_VERTEX || iuv < 0 ) - return 0; /* should not happen */ - /* check edge adjacency */ - if ( pBNS->edge[iuv].neighbor1 != (u/2-1) && pBNS->edge[iuv].neighbor1 != v/2-1 || - (pBNS->edge[iuv].neighbor12 ^ (u/2-1)) != (v/2-1) ) { - return 0; /* !!! should not happen !!! */ - } - -#if ( CHECK_TACN == 1 ) - u_is_taut = ((pBNS->vert[u/2-1].type & pBNS->type_T) == pBNS->type_T )? TYPE_T : - ((pBNS->vert[u/2-1].type & pBNS->type_CN) == pBNS->type_CN)? TYPE_CN : 0; - w_is_taut = ((pBNS->vert[w/2-1].type & pBNS->type_T) == pBNS->type_T )? TYPE_T : - ((pBNS->vert[w/2-1].type & pBNS->type_CN) == pBNS->type_CN)? TYPE_CN : 0; - if ( (u_is_taut | w_is_taut) == (TYPE_T | TYPE_CN ) ) { - /* rescap must have already been checked */ - return 1; - } -#endif - - return 0; -#undef TYPE_T -#undef TYPE_CN -} - - - -#else /* } FIX_TACN_POSSIBLE_BUG { */ - - - -/*****************************************************************************/ -int bIgnoreVertexNonTACN_group( BN_STRUCT* pBNS, Vertex v, Vertex w, Edge *SwitchEdge ) -{ - int u_is_taut=0, w_is_taut=0; - Vertex u; - EdgeIndex iuv; - if ( v <= 1 || w <= 1 ) - return 0; -#if ( CHECK_TACN == 1 ) - if ( !pBNS->type_TACN || - (pBNS->vert[v/2-1].type & pBNS->type_TACN) ) { - return 0; - } - if ( !pBNS->type_T || !pBNS->type_CN ) - return 0; /* should not happen */ -#endif - u = GetPrevVertex( pBNS, v, SwitchEdge, &iuv ); - /* - u = SwitchEdge_Vert1(v); - iuv = SwitchEdge_IEdge(v); - */ - if ( u == NO_VERTEX || iuv < 0 ) - return 0; /* should not happen */ - /* check edge adjacency */ - if ( pBNS->edge[iuv].neighbor1 != (u/2-1) && pBNS->edge[iuv].neighbor1 != v/2-1 || - (pBNS->edge[iuv].neighbor12 ^ (u/2-1)) != (v/2-1) ) { - return 0; /* !!! should not happen !!! */ - } - -#if ( CHECK_TACN == 1 ) - if ( ((u_is_taut = (pBNS->vert[u/2-1].type & pBNS->type_T) == pBNS->type_T) || - ( (pBNS->vert[u/2-1].type & pBNS->type_CN) == pBNS->type_CN)) && - ((w_is_taut = (pBNS->vert[w/2-1].type & pBNS->type_T) == pBNS->type_T) || - ( (pBNS->vert[w/2-1].type & pBNS->type_CN) == pBNS->type_CN)) && - u_is_taut + w_is_taut == 1 ) { - /* rescap must have already been checked */ - return 1; - } -#endif - - return 0; -} -#endif /* } FIX_TACN_POSSIBLE_BUG { */ - - - -#if ( FIX_KEEP_H_ON_NH_ANION == 1 ) - - - -/*********************************************************************************/ -/* Detect an attempt to remove H from -NH(-) to make =N(-); */ -/* all taut atoma except N are 'acidic' */ -/*********************************************************************************/ -int bIsRemovedHfromNHaion( BN_STRUCT* pBNS, Vertex u, Vertex v ) -{ - int i, u2, v2, vat2; - Vertex vtg, vat; - BNS_VERTEX *pvAT, *pvCN; - BNS_EDGE *pEdge; - if ( !pBNS->type_TACN || u <= 1 || v <= 1 || - u%2 || !(v%2) /* the edge flow may only increase */ ) { - return 0; - } - if ((pBNS->vert[u2 = u/2-1].type & pBNS->type_TACN) || - (pBNS->vert[v2 = v/2-1].type & pBNS->type_TACN) ) { - return 0; /* add/remove H is allowed for acidic atoms */ - } - if ( !pBNS->type_T || !pBNS->type_CN ) - return 0; /* should not happen */ - /* find which of u, v vertices is N and which is t-group */ - if ( ((pBNS->vert[u2].type & pBNS->type_T) == pBNS->type_T ) && v2 < pBNS->num_atoms ) { - vtg = u; - vat = v; - } else - if ( ((pBNS->vert[v2].type & pBNS->type_T) == pBNS->type_T ) && u2 < pBNS->num_atoms ) { - vtg = v; - vat = u; - } else { - return 0; - } - vat2 = vat/2-1; - pvAT = pBNS->vert + vat2; /* atom */ - for ( i = pvAT->num_adj_edges-1; 0 <= i; i -- ) { - pEdge = pBNS->edge + pvAT->iedge[i]; - pvCN = pBNS->vert + (pEdge->neighbor12 ^ vat2); - if ( ((pvCN->type & pBNS->type_CN) == pBNS->type_CN) && pEdge->flow > 0 ) { - return 1; /* detected */ - } - } - return 0; -} -#endif - - - -#if ( FIX_AVOID_ADP == 1 ) -/************************************************************************ - Detect (tg)-N=A-A=A-A=N-(tg) - u v w - k = 5 4 3 2 1 0 1 2 - ^ - odd number means ADP -*************************************************************************/ -int bIsAggressiveDeprotonation( BN_STRUCT* pBNS, Vertex v, Vertex w, Edge *SwitchEdge ) -{ -#define TYPE_T 1 /* t-group [also called H-group] */ -#define TYPE_CN 2 /* (-)c-group */ -#define TYPE_AT 4 - int k, v2, u2, w2, u2_next, type0, type1, type2, type; - Vertex u, u_next; - EdgeIndex iuv; - if ( v <= 1 || w <= 1 ) - return 0; - - if ( !pBNS->type_TACN || !pBNS->type_T || !pBNS->type_CN ) - return 0; /* should not happen */ - v2 = v/2 - 1; - w2 = w/2 - 1; - if ( v2 >= pBNS->num_atoms || w2 < pBNS->num_atoms ) - goto cross_edge; - - if ( !((pBNS->vert[w2].type & pBNS->type_T) == pBNS->type_T ) && - !((pBNS->vert[w2].type & pBNS->type_CN) == pBNS->type_CN) ) - goto cross_edge; - /* v ia an atom, w is a t-group, v != w' */ - for ( k = 0, u = v; 1 < (u_next = u, u = GetPrevVertex( pBNS, u, SwitchEdge, &iuv )); k ++ ) { - u2 = u/2 - 1; - if ( u2 >= pBNS->num_atoms ) { - /* moving backward along the alt path we have found a vertex - that is not an atom. Possibly it is a t- or (-)c-group */ - if ( !( k % 2 ) ) { - return 0; /* even vertex -- always okay */ - } - if ( !((pBNS->vert[u2].type & pBNS->type_T) == pBNS->type_T ) && - !((pBNS->vert[u2].type & pBNS->type_CN) == pBNS->type_CN) ) { - /* not a t- or (-)c-group */ - return 0; - } - u2_next = u_next/2 - 1; - if ( !(pBNS->vert[v2 ].type & pBNS->type_TACN) && - !(pBNS->vert[u2_next].type & pBNS->type_TACN) ) { - /* none of the atoms at the ends are N */ - return 0; - } - return 1; - } - } - return 0; -cross_edge: - /***************************************************************************** - * v and w (v=w') are same vertex reached with opposite "phases". - * w cannot be (t) because this would have been detected earlier -- ??? - * (t)-A=A-A=A-A=A-(t) - * v - * 3 2 1 0 1 2 3 4 - * kv kw - * (kv + kw)%2 == 1 <==> aggressive deprotonation - *****************************************************************************/ - if ( v == prim(w) ) { - type0 = 0; - if ( v2 >= pBNS->num_atoms ) { - type0 = ((pBNS->vert[v2].type & pBNS->type_T) == pBNS->type_T )? TYPE_T : - ((pBNS->vert[v2].type & pBNS->type_CN) == pBNS->type_CN)? TYPE_CN : 0; - } - - - - } - - - return 0; -} -#endif - - - -/*****************************************************************************/ -int rescap( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv ) -{ - BNS_ST_EDGE *pst_edge; - BNS_EDGE *pedge; - - int f; - S_CHAR s_or_t; - int bBackward = GetEdgePointer( pBNS, u, v, iuv, &pedge, &s_or_t ); - if ( !IS_BNS_ERROR(bBackward) ) { - - if ( s_or_t ) { - pst_edge = (BNS_ST_EDGE *)pedge; - f = (pst_edge->flow & EDGE_FLOW_ST_MASK); - if ( !bBackward ) { - f = (int)pst_edge->cap-f; - } - } else { - f = (pedge->flow & EDGE_FLOW_MASK); - if ( !bBackward ) { - f = (int)pedge->cap-f; - } - } - return f; - - } - return bBackward; /* error */ -} - - - -/********************************************************************************* - W.Kocay, D.Stone, - "An Algorithm for Balanced Flows", - The Journal of Combinatorial Mathematics and Combinatorial Computing, - vol. 19 (1995) pp. 3-31 - - W.Kocay, D.Stone, - "Balanced network flows", - Bulletin of the Institute of Combinatorics and its Applications, - vol. 7 (1993), pp. 17--32 - - N = a balanced network (bipartite directed graph) of: - n=2*V+2 vertices (incl. s (source) and t (target); - each other vertex i is included 2 times: - set X (x[i]) of V vertices (v) and a set Y (y[j]) of - V complementary vertices (v') so that x[i]' = y[i], x[i]''=x[i], and - m=2*E+2*V edges (each original undirected edge i-j is represented as - 2 directed edges: x[i]->y[j] and x[j]->y[i]; plus - V edges s->x[i] and V edges y[j]->t) - v' = a complement vertex to v - v'u' = (uv)' = a complement edge to uv - rescap(uv) = cap(uv)-f(uv) if uv is a forward edge - = f(uv) if uv is a backward edge - (i) 0 <= f(uv) <= cap(uv) - (ii) f+(u) = f-(u) for all u in X, Y where f+(u) is a total flow out of u, - and f-(u) is a total flow into u - (iii) f(uv) = f((uv)') (balanced flow condition) - - S = a set of all s-searchable vertices - S- = all other vertices - if t in S, then N contains a valid augmenting path P, so that flow can be - augmented on both P and P' - if t not in S, then let K=[S,S-], the set of all edges directed from S to S-. - K is an edge-cut that has a special structure. - Let - A = {x[i], y[i] |x[i] not in S, y[i] in S} - B = {x[i], y[i] |x[i] in S, y[i] not in S} - C = {x[i], y[i] |x[i] in S, y[i] in S} - D = {x[i], y[i] |x[i] not in S, y[i] not in S} - N[C] = subgraph of N induced by C; - it consists of of a number of connected components C[i] - K[i] = those edges of K with one endpoint in C[i]. - - If t is in S- then - - i) Each C[i] = C[i]' - ii) There are no edges between C and D - iii) Each K[i] has odd capacity, it is called a balanced edge-cut. - - balcap(K) = cap(K) - odd(K), where odd(K) is the number of connected components in N[C]. - Name "odd(K)" is because each cap(K[i]) is odd. - - Max-Balanced-Flow-Min-Balanced-Cut Theorem: - - Let f be a balanced flow in N, and let K be any balanced edge-cut. - The value of a maximum balanced flow equals the capacity of a minimum - edge-cut, that is, val(f) = balcap(K) when f is maximum and K is minimum. - -*********************************************************************************/ - -/*******************************************************/ -/* */ -/* VERTEX NUMBERING */ -/* */ -/* Total number of atoms = n */ -/* Total number of vertices = 2*n+2 */ -/*******************************************************/ -/* */ -/* atom numbering starts from 0: */ -/* */ -/* atoms s t x0 y0 x1 y1 ... xi yi ...xn yn */ -/* vertices 0 1 2 3 4 5 ... 2i-2 2i-1 ...2n-2 2n-1 */ -/* */ -/* atom = vertex/2-1; if negative then s or t */ -/* */ -/* vertex = (atom + 1) * 2 + i; i=0 for x, i=1 for y */ -/* */ -/*******************************************************/ - - - -/*********************************************************************************/ -/* v' variable is called prim(v) for now */ -/*********************************************************************************/ -int BalancedNetworkSearch ( BN_STRUCT* pBNS, BN_DATA *pBD, int bChangeFlow ) -{ /* N has source s, target t, the mirror network M is constructed */ - /* the tree T contains a valid sv-path for each v in T. Simultaneously the complementary - tree T' is built as indicated in comments. The trees T and T' must have no edges or - vertices in common. Initially T will be built as in breadth-first-search, and T' will - be the complementary tree, it will contain the complementary valid v't-path. - */ - - Vertex *BasePtr = pBD->BasePtr; - Edge *SwitchEdge = pBD->SwitchEdge; - S_CHAR *Tree = pBD->Tree; - Vertex *ScanQ = pBD->ScanQ; - int QSize = pBD->QSize; - Vertex *Pu = pBD->Pu; - Vertex *Pv = pBD->Pv; - int max_len_Pu_Pv= pBD->max_len_Pu_Pv; - - /* added to translate into C */ - int i, k, degree, delta, ret = 0; - Vertex u, b_u, v, b_v, w; - EdgeIndex iuv; -#if ( BNS_RAD_SEARCH == 1 ) - int n, bRadSearch = (BNS_EF_RAD_SRCH & bChangeFlow) && pBD->RadEndpoints; - BRS_MODE bRadSrchMode = RAD_SRCH_NORM; - int bRadSearchPrelim = 0; - if ( bRadSearch ) { - pBD->nNumRadEndpoints = 0; - bRadSrchMode = pBD->bRadSrchMode; - bRadSearchPrelim = pBNS->type_TACN && bRadSrchMode == RAD_SRCH_NORM; - } -#endif - -/* -- Always -- - Vertex_s = FIRST_INDX; - Vertex_t = Vertex_s+1; -*/ - QSize = k = 0; /* put s on ScanQ = set S */ - ScanQ[QSize] = Vertex_s; - BasePtr[Vertex_t] = Vertex_s; - BasePtr[Vertex_s] = BLOSSOM_BASE; /* create initial blossom C(Vertex_s) with base s */ - Tree[Vertex_s] = TREE_IN_1; - - do { - u = ScanQ[k]; /* select u from the head of ScanQ */ - /* since u is on the queue, it has a blossom C(U) with base b_u */ - b_u = FindBase( u, BasePtr ); - degree = GetVertexDegree( pBNS, u ); -#if ( BNS_RAD_SEARCH == 1 ) - n = 0; -#endif - for ( i = 0; i < degree; i ++ ) { - /* v = vert[u].neighbor[i]; */ - v = GetVertexNeighbor( pBNS, u, i, &iuv ); - if ( v == NO_VERTEX ) { - continue; /* the atom has only single bonds, ignore it */ - } -#if ( BNS_RAD_SEARCH == 1 ) - if ( !k && bRadSrchMode == RAD_SRCH_FROM_FICT && v/2 <= pBNS->num_atoms ) { - continue; /* start from fict. vertices only */ - } - if ( bRadSearchPrelim && v/2 > pBNS->num_atoms ) { - continue; /* during initial add/remove H allow radical movement only through real atoms */ - } -#endif - if ( /* PrevPt[u] != v ** avoid edges of T */ - ( SwitchEdge_Vert1(u) != v || SwitchEdge_Vert2(u) != u ) /* avoid edges of T */ - && (ret = rescap(pBNS, u, v, iuv)) > 0 ) { - /* special treatment to prevent H<->(-) replacement on non-acidic atoms */ - /*----------------------------------------------------------------------*/ - if ( pBNS->type_TACN ) { - if ( bIgnoreVertexNonTACN_atom( pBNS, u, v ) ) { - continue; - } - if ( bIgnoreVertexNonTACN_group( pBNS, u, v, SwitchEdge ) ) { - continue; - } -#if ( FIX_KEEP_H_ON_NH_ANION == 1 ) - if ( bIsRemovedHfromNHaion( pBNS, u, v ) ) { - continue; - } -#endif -#if ( FIX_AVOID_ADP == 1 ) - if ( bIsAggressiveDeprotonation( pBNS, u, v, SwitchEdge ) ) { - continue; - } -#endif - - } - /*----------------------------------------------------------------------*/ - b_v = FindBase(v, BasePtr); /* Notation: b_x is a base of x */ - - if ( b_v == NO_VERTEX ) { /* originally 0 instead of NO_VERTEX */ - /* Important note: following "A. Note of Implementing the Algorithm" from the - article by Kocay and Stone, all references to PrevPt[a] have been - replaced with SwitchEdge[a][0]=SwitchEdge_Vert1(a); to be on a safe side - the check whether (SwitchEdge_Vert2(a)==a) has been added. - */ - - /* v is not yet in T or T' -- add it to T and M */ - /*PrevPt[v] = u; ** this effectively adds v to T and v' to T' */ - QSize ++; - ScanQ[QSize] = v; - TREE_MARK(v, TREE_IN_1); /* mark v s-reachable (in T) */ - TREE_MARK(prim(v), TREE_IN_2); /* mark v' in T' */ - - /* Switch Edges: If u in T then Pu (a valid su-path) can be constructed - by successfully executing u=PrevPt[u] until u=s. - For vertices in T' the situation is different. - Suppose uv and v'u' are added to a mirror network M creating a blossom: - - s T (Note: in the code v' is prim(v), - T / \ u' is prim(u), etc.) - / ... - w=x1 - / \ u in T, v in T' - u=y2 v'=y3 - \ / <--- two added edges uv and (uv)'=v'u' - -------- X ------------intersection of the edges ------------------------ - / \ <--- (the edges intersection in the picture is shown as X) - u'=x2 v=x3 u' it T', v' in T - \ / - T' w'=y1 - \ ... - \ / - t T' - - Vertices v and u' now become s-reachable; - The valid paths to v and u' must use the edges uv and v'u' respectively. - - For each vertex z in S we define a switch-edge that allows a valid sz-path - to be constructed, SwitchEdge[v]=uv and SwitchEdge[u']=v'u'. (We don't - know the direction of the edge uv, it may be (u,v) or (v,u). In either case, - the complementary edge is indicated by v'u'). - - Vertex w' also becomes s-reachable when uv is added to M, and a valid sw'-path - must use one of uv and v'u'. Therefore we choose one of them, say uv (see below - the rule of choosing the switch-edge), and define SwitchEdge[w'] = uv. - - When the addition of an edge uv to M causes a vertex z to become s-reachable - (where z was previously non-reachable), z is placed on the ScanQ, that is, into S. - The edge uv is said to be a switch-edge for z. - - Rule: We choose the the order of the vertices uv to be such that the valid sz-path - consists of a valid su-path, followed by edge uv, followed by a valid vz-path. - - For vertices z in T we can take SwitchEdge[z]=yz where y=PrevPt[z] since - it is the edge yz that allows z to be s-reachable. - For vertices z not in S we take SwitchEdge[z]=NONE. - - */ - - /* v is not yet in T or T' -- add it to T and M */ - SwitchEdge_Vert1(v) = u; /* this effectively adds uv and v'u' to M */ - SwitchEdge_IEdge(v) = iuv; - - BasePtr[prim(v)] = v; - BasePtr[v] = BLOSSOM_BASE; /* create a trivial blossom C(v) with base v */ -#if ( BNS_RAD_SEARCH == 1 ) - n ++; -#endif - } else - if ( TREE_IS_S_REACHABLE(prim(v)) /*Is_s_Reachable(prim(v)*/ - /* if v' is reachable, an st-path is given by P(u)-uv-P'(v') */ - /*&& PrevPt[prim(u)] != prim(v) ** avoid edges of T' */ - && (SwitchEdge_Vert1(prim(u)) != prim(v) || SwitchEdge_Vert2(prim(u)) != prim(u)) /* avoid edges of T' */ - && b_u != b_v - && !(pBNS->type_TACN && bIgnoreVertexNonTACN_group( pBNS, prim(v), u, SwitchEdge )) -#if ( FIX_KEEP_H_ON_NH_ANION == 1 ) - && !(pBNS->type_TACN && bIsRemovedHfromNHaion( pBNS, prim(v), u )) -#endif - ) { -#if ( BNS_RAD_SEARCH == 1 ) - n ++; -#endif - /* there is now a valid sv-path via u avoiding b_v (unless v==b_v) - => u, v, u', and v' now all become part of the same connected component of M[C] */ - w = MakeBlossom( pBNS, ScanQ, &QSize, Pu, Pv, max_len_Pu_Pv, SwitchEdge, BasePtr, u, v, iuv, b_u, b_v, Tree ); - /* this constructed the new blossom and returned its base */ - if ( IS_BNS_ERROR( w ) ) { - pBD->QSize = QSize; - return w; /* error */ - } - b_u = w; /* the new base of C(u) */ - if ( prim(w) == Vertex_t ) { - /* t is now s-reachable, a valid augmenting path P exists in M */ - delta = FindPathCap( pBNS, SwitchEdge, Vertex_s, Vertex_t, 10000 ); /* compute the residual capacity of P + P' */ - if ( IS_BNS_ERROR( delta ) ) { - pBD->QSize = QSize; - return delta; /* error */ - } -#if ( ALLOW_ONLY_SIMPLE_ALT_PATH == 1 ) - if ( pBNS->bNotASimplePath || abs(delta) > 1 ) { - delta = 0; - } -#endif - if ( delta ) { - pBNS->bChangeFlow |= (bChangeFlow & BNS_EF_CHNG_FLOW); - } - ret = PullFlow( pBNS, SwitchEdge, Vertex_s, Vertex_t, delta, 0, bChangeFlow ); /* augment on a pair of valid st-paths */ - pBD->QSize = QSize; - return ( IS_BNS_ERROR(ret)? ret : delta ); - } - } - } else - if ( IS_BNS_ERROR( ret ) ) { - pBD->QSize = QSize; - return ret; /* error */ - } - } -#if ( BNS_RAD_SEARCH == 1 ) - if ( bRadSearch && !n ) { - /* the BNS stopped at u */ - n = RegisterRadEndpoint( pBNS, pBD, u); - if ( IS_BNS_ERROR( n ) ) { - pBD->QSize = QSize; - return n; - } - } -#endif - k ++; /* advance ScanQ */ - } while( k <= QSize ); - /* if this point is reached, no valid augmenting path exists, ScanQ contains - the set S of all s-reachable vertices and K=[S,S-] is a minimum balanced edge-cut */ - /* ClearFlowMarks( vert, num_vert); */ - pBD->QSize = QSize; - return 0; -} -/******************************************************************** -Blossoms. - - The vertices of a mirror network M consist T U T'. Intersection T ^ T' is empty. - - The edges of M consist of switch-edges and their complements because edges - are added in complementary pairs, one of which is always a switch-edge. - - The base of every blossom is in T. - Let C(i) be a blossom with base b_i. Since C(i)=C(i)', C(i) contains vertices of T and T'. - Since every valid sv-path to v in C(i) contains b_i, b_i is the first s-reachable vertex of C(i). - - Suppose the mirror network M contains a valid sz-path P(z) to all vertices z in ScanQ. - Every vertex of P(z) is s-reachable therefore its vertices are all in blossoms or - trivial blossoms. - - Let z be an s-reachable vertex and P(z) be a valid path in M. - Then every valid sz-path in M contains exactly the same sequence of blossom bases as P(z). - -*********************************************************************/ - - - -/*********************************************************************** - BasePtr[u] = -2 NO_VERTEX u is not a blossom - -1 BLOSSOM_BASE u is the base of its blossom - v a vertex closer to the base -************************************************************************/ -Vertex FindBase( Vertex u, Vertex *BasePtr ) -{ - if ( BasePtr[u] == NO_VERTEX ) { - return NO_VERTEX; - } else - if ( BasePtr[u] == BLOSSOM_BASE ) { - return u; - } else { - Vertex b; - b = FindBase(BasePtr[u], BasePtr ); - BasePtr[u] = b; /* path compression */ - return b; - } -} - - - -/*********************************************************************************/ -/* Returns index of the last path element and the path */ -/*********************************************************************************/ -int FindPathToVertex_s( Vertex x, Edge *SwitchEdge, Vertex *BasePtr, Vertex *Path, int MaxPathLen ) -{ - /* x is the base of a blossom, construct a valid Path of blossom bases to s */ - int i = 0; - Path[i] = x; - while ( x != Vertex_s ) { - x = FindBase(SwitchEdge_Vert1(x), BasePtr); - if ( ++i < MaxPathLen ) { - Path[i] = x; - } else { - return BNS_WRONG_PARMS; - } - } - return i; -} - - - -/*********************************************************************************/ -/* Make a blossom */ -/*********************************************************************************/ -Vertex MakeBlossom( BN_STRUCT* pBNS, Vertex *ScanQ, int *pQSize, - Vertex *Pu, Vertex *Pv, int max_len_Pu_Pv, - Edge *SwitchEdge, Vertex *BasePtr, - Vertex u, Vertex v, EdgeIndex iuv, Vertex b_u, Vertex b_v, S_CHAR *Tree ) -{ - /* In order to find the base of the new blossom, the paths - P(u) and P(v') are constructed and compared in order to - find the last blossom base they have in common which - is reachable on a valid path. - - Edge uv connects two blossoms, their bases are b_u and b_v. - */ - Vertex w, z; - int len_Pu, len_Pv; - int i, j; - EdgeIndex izw; - - len_Pu = FindPathToVertex_s( b_u, SwitchEdge, BasePtr, Pu, max_len_Pu_Pv ); - if ( IS_BNS_ERROR( len_Pu ) ) { - return len_Pu; - } - len_Pv = FindPathToVertex_s( b_v, SwitchEdge, BasePtr, Pv, max_len_Pu_Pv ); - if ( IS_BNS_ERROR( len_Pv ) ) { - return len_Pv; - } - i = len_Pu; - j = len_Pv; - /* initially Pu[i] and Pv[j] both equal to s, but their first elements are different */ - /* find the last blossom base common to Pu and Pv */ - while ( i >= 0 && j >= 0 && Pu[i] == Pv[j] ) { - /* was (Pu[i]==Pv[j] && i>=0 && j>=0) => tested Pu[-1], Pv[-1] <- pointed by W.Ihlenfeldt 08-26-2004*/ - i --; - j --; - } - i ++; - w = Pu[i]; /* w is the last common vertex */ - z = SwitchEdge_Vert1(w); - izw = SwitchEdge_IEdge(w); - /* now extend the blossom if rescap(zw) >= 2 */ - while ( w != Vertex_s && rescap(pBNS, z, w, izw) >= 2 ) - { - i++; - w = Pu[i]; - z = SwitchEdge_Vert1(w); - izw = SwitchEdge_IEdge(w); - } - /* w is the base of the new blossom */ - /* first follow the path Pu from w to b_u */ - for ( i = i-1; i >= 0; i -- ) { - z = Pu[i]; /* z is the base of the blossom */ - BasePtr[z] = w; - BasePtr[prim(z)] = w; /* w is the new base of the blossom */ - /* z and z' may already be part of a blossom that is being - swallowed into a larger blossom. - We don't want to change the switch edge in that case. - */ - - if ( !TREE_IS_ON_SCANQ(prim(z)) /*!IsInScanQ(prim(z)) */) - { - SwitchEdge_Vert1(prim(z)) = prim(v); /* set the switch edge of z' */ - /* SwitchEdge[prim(z)][1] = prim(u); */ - SwitchEdge_IEdge(prim(z)) = iuv; - (*pQSize) ++; - ScanQ[*pQSize] = prim(z); /* add z' to ScanQ */ - TREE_MARK(prim(z), TREE_IN_2BLOSS); /* mark z' s-reachable */ - } - } - /* now follow the path Pv */ - for ( j = j; j >= 0; j -- ) { - z = Pv[j]; /* z is the base of the blossom */ - BasePtr[z] = w; - BasePtr[prim(z)] = w; /* w is the new base of the blossom */ - /* z and z' may already be part of a blossom that is being - swallowed into a larger blossom. - We don't want to change the switch edge in that case. - */ - - if ( !TREE_IS_ON_SCANQ(prim(z)) /*!IsInScanQ(prim(z)) */ ) - { - SwitchEdge_Vert1(prim(z)) = u; /* set the switch edge of z' */ - /* SwitchEdge[prim(z)][1] = v; */ - SwitchEdge_IEdge(prim(z)) = iuv; - (*pQSize) ++; - ScanQ[*pQSize] = prim(z); /* add z' to ScanQ */ - TREE_MARK(prim(z), TREE_IN_2BLOSS); /* mark z' s-reachable */ - } - } - - if ( !TREE_IS_ON_SCANQ(prim(w)) /* !IsInScanQ(prim(w))*/ ) - { /* add w' to the blossom */ - SwitchEdge_Vert1(prim(w)) = u; /* set the switch edge of w' */ - /* SwitchEdge[prim(w)][1] = v; */ - SwitchEdge_IEdge(prim(w)) = iuv; - (*pQSize) ++; - ScanQ[*pQSize] = prim(w); /* add w' to ScanQ */ - TREE_MARK(prim(w), TREE_IN_2BLOSS); /* mark w' s-reachable */ - } - return w; -} - - - -/***************************************************************************** - When t is found to be s-reachable, a valid st-path P is known to exist. - Its complementary path P' is also valid. Once the residual capacity - delta(P) is known, the flow is augmented by calling PullFlow(s,t,delta). - It constructs the path P by using the switch-edges. - Let uv=SwitchEdge[t]. - Then P is given by a valid su-path, followed by the edge uv, followed by - a valid vt-path. - PullFlow is a recursive procedure that constructs the path and its complement. - - Let wz=SwitchEdge[y]. PullFlow(x, y, delta) uses the xw- and zy-portions of P - (see below). Since it must also augment on P' simultaneously, the zy-portion - is replaced by the y'z'-portion. - - x y' - | | P: x--w--z--y - P | | P' P': y'-z'-w'-x' - | o - o \ - / w' z \ - / o----\ /----o / - \ / \ / \ / - \/ X \/ - /\ / \ /\ - / \ w / \ z' / \ - \ o---- ----o / Using a switch-edge wz and w'z' - \ / to construct P and P' - o o - | | - | | - x' y - -**********************************************************************************/ - - - - -/*********************************************************************************/ -int PullFlow( BN_STRUCT *pBNS, Edge *SwitchEdge, Vertex x, Vertex y, int delta, S_CHAR bReverse, int bChangeFlow ) -{ /* - Augment the flow by delta on all edges on a path P - between x and y in the order of the path; - AugmentEdge( pBNS, w, z, iwz, delta, 0 ) means the path is in w->z direction - AugmentEdge( pBNS, w, z, iwz, delta, 1 ) means the path is in w<-z direction - - Unlike PullFlow in the paper by Kocay & Stone, here the augmentation - always starts at "s", proceeds sequentially through the path end terminates at "t". - Since we do not really need the complement path, PullFlow ignores it. - - */ - - Vertex w, z; - EdgeIndex iwz; - int ret = 0; - - w = SwitchEdge_Vert1(y); - z = SwitchEdge_Vert2(y); - iwz = SwitchEdge_IEdge(y); - if ( bReverse ) { - /* P consists of a path from x to w, then wz, then a path from z to y. */ - /* z may equal y, in which case z is just PrevPt[y] */ - if ( z != y ) { - ret = PullFlow( pBNS, SwitchEdge, prim(y), prim(z), delta, (S_CHAR)(1-bReverse), bChangeFlow ); /* augment between z and y */ - } - if ( !IS_BNS_ERROR(ret) ) { - ret = AugmentEdge( pBNS, w, z, iwz, delta, bReverse, bChangeFlow); - } - /* Do not augment the complementary path: AugmentEdge( prim(z), prim(w), vert, delta); */ - /* w may equal x, in which case there is no need to call PullFlow(x, w) */ - if ( w != x && !IS_BNS_ERROR(ret) ) { - ret = PullFlow( pBNS, SwitchEdge, x, w, delta, bReverse, bChangeFlow ); /* augment between x and w */ - } - } else { - /* P consists of a path from x to w, then wz, then a path from z to y. */ - /* w may equal x, in which case there is no need to call PullFlow(x, w) */ - if ( w != x && !IS_BNS_ERROR(ret) ) { - ret = PullFlow( pBNS, SwitchEdge, x, w, delta, bReverse, bChangeFlow ); /* augment between x and w */ - } - if ( !IS_BNS_ERROR(ret) ) { - ret = AugmentEdge( pBNS, w, z, iwz, delta, bReverse, bChangeFlow); - } - /* z may equal y, in which case z is just PrevPt[y] */ - if ( z != y && !IS_BNS_ERROR(ret) ) { - ret = PullFlow( pBNS, SwitchEdge, prim(y), prim(z), delta, (S_CHAR)(1-bReverse), bChangeFlow ); /* augment between z and y */ - } - } - return ret; -} - - - -/******************************************************************************** -Before augmenting on the two paths, it is necessary to find delta(P). -This can be done by following the paths and computing the minimum -residual capacity of all edges on P. An edge on both P and P' counts -for only half of its actual residual capacity, since augmentng on P by -delta will simutaneously reduce its capacity on P' by delta. -The path P can only be followed by using the switch-edges, as in PullFlow(...). -FindPathCap( x, y, delta ) is a recursive procedure that finds the residual -capacity on the portion of P between x and y. delta is the minimum capacity -found so far along the path. -********************************************************************************/ -int FindPathCap( BN_STRUCT* pBNS, Edge *SwitchEdge, Vertex x, Vertex y, int delta ) -{ /* find the minimum residual capacity of all edges - between x and y in a valid st-path P. - delta is the minimum found so far - the vertices occur in order s,...,x,...,y,...,t along P - the vertices occur in order s,...,y',...,x',...,t along P' - */ - Vertex w, z, iwz; - int cap, delta2; - static int level; - - if ( level ++ > 50 ) { -#ifdef _DEBUG - int stop = 1; -#else - ; -#endif - } - - - w = SwitchEdge_Vert1(y); - z = SwitchEdge_Vert2(y); /* wz is on the path P */ - iwz = SwitchEdge_IEdge(y); /* edge index */ - - /* rescap_mark() detects edges passed 2 times and reduces rescap */ - cap = rescap_mark( pBNS, w, z, iwz ); - - if ( IS_BNS_ERROR( cap ) ) { - level --; - return cap; - } - if ( cap < delta ) { - delta = cap; - } - /* P consists of a path from x to w, then wz, then a path from z to y */ - if ( w != x ) { - delta2 = FindPathCap( pBNS, SwitchEdge, x, w, delta ); - delta = inchi_min( delta2, delta ); - } - if ( z != y ) { - delta2 = FindPathCap( pBNS, SwitchEdge, prim(y), prim(z), delta ); - delta = inchi_min( delta2, delta ); - } - level --; - return delta; -} - - - -/*********************************************************************************/ -/* BT = bond types */ -#define BT_ALTERN_BOND 1 /* 1-2, possibly stereo */ -#define BT_OTHER_ALTERN_BOND 2 /* 1-3, 2-3, 1-2-3 alternating non-stereo non-taut bonds */ - -#define BT_ALTERN_NS_BOND 4 - -#define BT_TAUTOM_BOND 8 - -#define BT_ALTERN_UNKN_BOND 16 - -#define BT_IGNORE_BOND 0 - -#define BT_NONSTEREO_MASK (BT_TAUTOM_BOND|BT_ALTERN_NS_BOND) - -#define BT_ALT_BOND_MASK (BT_ALTERN_BOND|BT_OTHER_ALTERN_BOND) - -#define BT_NONTAUT_BOND_MASK (BT_ALTERN_BOND|BT_OTHER_ALTERN_BOND|BT_ALTERN_NS_BOND) - -/* BNS members redefinitions for finding non-stereo bonds */ -/* BNS_EDGE */ -#define nBlockNumberAltBns flow /* internal variable of the DFS traversal: mark traversed bonds */ -#define nNumAtInBlockAltBns cap -#define nBondTypeInpAltBns pass /* 0=>cannot be stereo at all, 1=>alt or taut non-stereo, 2=>can be stereo */ -#define nBondNonStereoAltBns cap /* 1=>found to be non-stereogenic although BondTypeInp=2; 0=>as in BondTypeInp */ - -#if ( BNS_MARK_ONLY_BLOCKS == 1 ) /* { */ -/* BNS_VERTEX */ -#define bCutVertexAltBns st_edge.cap0 /* cut-vertex flag */ -#define nRingSystemAltBns st_edge.cap /* ordering number of a ring system */ -#define nNumAtInRingSystemAltBns st_edge.flow0 /* number of vertices in a ring system */ -#define nBlockSystemAltBns st_edge.flow /* result of the DFS traversal: even cirquit must be within one block */ - -#endif /* } */ - -#define valenceAltBns num_adj_edges -/*********************************************************************************/ - - -/********************************************************************************/ -int MarkRingSystemsAltBns( BN_STRUCT* pBNS, int bUnknAltAsNoStereo ) -{ - AT_NUMB *nStackAtom = NULL; - int nTopStackAtom; - AT_NUMB *nRingStack = NULL; - int nTopRingStack; /* was AT_NUMB */ - AT_NUMB *nBondStack = NULL; - int nTopBondStack; - AT_NUMB *nDfsNumber = NULL; - AT_NUMB *nLowNumber = NULL; - S_CHAR *cNeighNumb = NULL; - AT_NUMB nDfs; - AT_NUMB nNumAtInRingSystem; - int i, j, u, w, start, nNumRingSystems, nNumStartChildren; - BNS_VERTEX *at = pBNS->vert; - BNS_EDGE *bond = pBNS->edge; - int num_atoms = pBNS->num_atoms; - int num_edges = pBNS->num_bonds; - - /* allocate arrays */ - nStackAtom = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nStackAtom[0])); - nRingStack = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nRingStack[0])); - nDfsNumber = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nDfsNumber[0])); - nLowNumber = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nLowNumber[0])); - nBondStack = (AT_NUMB *) (num_edges? inchi_malloc(num_edges*sizeof(nBondStack[0])):NULL); /* special case: no bonds 2006-03 */ - cNeighNumb = (S_CHAR *) inchi_malloc(num_atoms*sizeof(cNeighNumb[0])); - /* check allocation */ - if ( !nStackAtom || !nRingStack || !nDfsNumber || !nLowNumber || !nBondStack && num_edges || !cNeighNumb - ) { - nNumRingSystems = CT_OUT_OF_RAM; /* program error */ /* */ - goto exit_function; - } - - /******************************************** - * - * Find Cut-vertices & Blocks - * - * 1\ /5 has 3 blocks (maximal subgraphs that - * Example: | >3--4< | are nonseparable by deleting a single vertex): - * 2/ \6 (1,2,3, has 3 bonds), (3,4, has 1 bond), and (4,5,6, has 3 bonds) - * - * Cut-vertices or articulation points are - * intersections of the blocks: points 3 and 4. - ********************************************/ - - /******************************************************** - - RingSystemAlt are atoms connected by alternating bonds - (as must be indicated in bIsAltBond()): - - BOND_ALTERN - BOND_ALT_123 - BOND_ALT_13 - BOND_ALT_23 - - Since other bonds may be present, we possibly need - to restart to move to another component - *********************************************************/ - - nNumRingSystems = 0; - memset( nDfsNumber, 0, num_atoms*sizeof(nDfsNumber[0])); - - for ( start = 0; start < num_atoms; start ++ ) { - if ( nDfsNumber[start] ) - continue; - for ( i = 0; i < at[start].valenceAltBns; i ++ ) { - if ( bond[at[start].iedge[i]].nBondTypeInpAltBns & BT_ALTERN_BOND ) - goto found_alt; - } - continue; - -found_alt: - - - /* initiation */ - u = start; /* start atom */ - nDfs = 0; - nTopStackAtom =-1; - nTopRingStack =-1; - nTopBondStack =-1; - memset( cNeighNumb, 0, num_atoms*sizeof(cNeighNumb[0])); - /* push the start atom on the stack */ - nLowNumber[u] = nDfsNumber[u] = ++nDfs; - nStackAtom[++nTopStackAtom] = (AT_NUMB)u; - nRingStack[++nTopRingStack] = (AT_NUMB)u; - - nNumStartChildren = 0; - - do { - /* advance */ - /*while ( (int)at[i=nStackAtom[nTopStackAtom]].valenceAltBns > (j = (int)cNeighNumb[i]) )*/ - /* replaced due to missing sequence point */ - while ( i=(int)nStackAtom[nTopStackAtom], j = (int)cNeighNumb[i], (int)at[i].valenceAltBns > j ) - { - cNeighNumb[i] ++; - if ( !(bond[w=at[i].iedge[j]].nBondTypeInpAltBns & BT_ALT_BOND_MASK) ) { - continue; - } - u = (int)(bond[at[i].iedge[j]].neighbor12 ^ i); - if ( !nDfsNumber[u] ) { - /* tree edge, 1st visit -- advance */ - nStackAtom[++nTopStackAtom] = (AT_NUMB)u; - nRingStack[++nTopRingStack] = (AT_NUMB)u; - nBondStack[++nTopBondStack] = (AT_NUMB)w; - nLowNumber[u] = nDfsNumber[u] = ++nDfs; - nNumStartChildren += (i == start); - } else - if ( !nTopStackAtom || u != (int)nStackAtom[nTopStackAtom-1] ) { /* may comment out ? */ - /* back edge: u is not a predecessor of i */ - if ( nDfsNumber[u] < nDfsNumber[i] ) { - /* Back edge, 1st visit: u is ancestor of i. Save and compare */ - nBondStack[++nTopBondStack] = (AT_NUMB)w; - if ( nLowNumber[i] > nDfsNumber[u] ) { - nLowNumber[i] = nDfsNumber[u]; - } - } - } - } - cNeighNumb[i] = 0; - - /* back up */ - if ( i != start ) { - u = (int)nStackAtom[nTopStackAtom-1]; /* predecessor of i */ - if ( nLowNumber[i] >= nDfsNumber[u] ) { - /* output the block; the block was entered through its first bond u->i */ - nNumRingSystems ++; - /*at[u].nBlockSystemAltBns = nNumRingSystems;*/ /* mark the atom */ - nNumAtInRingSystem = 1; - /* - if ( u != start || nNumStartChildren > 1 ) { - at[u].bCutVertexAltBns += 1; // mark cut-vertex (articulation point) - } - */ - while ( nTopRingStack >= 0 ) { - j = nRingStack[nTopRingStack--]; - /*at[j].nBlockSystemAltBns = nNumRingSystems;*/ /* mark the atom */ - nNumAtInRingSystem ++; - if ( i == j ) { - break; - } - } - while ( nTopBondStack >= 0 ) { - w = nBondStack[nTopBondStack--]; - bond[w].nBlockNumberAltBns = nNumRingSystems; /* mark the bond */ - bond[w].nNumAtInBlockAltBns = nNumAtInRingSystem; - if ( i == bond[w].neighbor1 && u == (i ^ bond[w].neighbor12) || - u == bond[w].neighbor1 && i == (u ^ bond[w].neighbor12)) { - break; - } - } - } else - if ( nLowNumber[u] > nLowNumber[i] ) { - /* inherit */ - nLowNumber[u] = nLowNumber[i]; - } - } - } while ( --nTopStackAtom >= 0 ); - } - -#if ( BNS_MARK_ONLY_BLOCKS != 1 ) /* { */ - - /******************************************** - * - * Find Ring Systems - * Including chain atoms X: A-X-B, where the bonds (of any kind) are bridges. - * - ********************************************/ - - /* initiation */ - - nNumRingSystems = 0; - - for ( start = 0; start < num_atoms; start ++ ) { - if ( at[start].nRingSystemAltBns ) - continue; - for ( i = 0; i < at[start].valenceAltBns; i ++ ) { - if ( bond[at[start].iedge[i]].nBondTypeInpAltBns & BT_ALT_BOND_MASK ) - goto found_alt2; - } - continue; - -found_alt2: - - u = start; /* start atom */ - nDfs = 0; - nTopStackAtom =-1; - nTopRingStack =-1; - memset( nDfsNumber, 0, num_atoms*sizeof(nDfsNumber[0])); - memset( cNeighNumb, 0, num_atoms*sizeof(cNeighNumb[0])); - /* push the start atom on the stack */ - nLowNumber[u] = nDfsNumber[u] = ++nDfs; - nStackAtom[++nTopStackAtom] = (AT_NUMB)u; - nRingStack[++nTopRingStack] = (AT_NUMB)u; - - do { - /* advance */ -advance_ring: - /*if ( (int)at[i=nStackAtom[nTopStackAtom]].valenceAltBns > (j = (int)cNeighNumb[i]) )*/ - /* replaced due to missing sequence point */ - if ( i=(int)nStackAtom[nTopStackAtom], j = (int)cNeighNumb[i], (int)at[i].valenceAltBns > j ) - { - cNeighNumb[i] ++; - if ( !(bond[at[i].iedge[j]].nBondTypeInpAltBns & BT_ALTERN_BOND) ) { - goto advance_ring; - } - u = (int)(bond[at[i].iedge[j]].neighbor12 ^ i); - if ( !nDfsNumber[u] ) { - /* tree edge, 1st visit -- advance */ - nStackAtom[++nTopStackAtom] = (AT_NUMB)u; - nRingStack[++nTopRingStack] = (AT_NUMB)u; - nLowNumber[u] = nDfsNumber[u] = ++nDfs; - } else - if ( !nTopStackAtom || u != (int)nStackAtom[nTopStackAtom-1] ) { - /* back edge: u is not a predecessor of i */ - if ( nDfsNumber[u] < nDfsNumber[i] ) { - /* Back edge, 1st visit: u is ancestor of i. Compare */ - if ( nLowNumber[i] > nDfsNumber[u] ) { - nLowNumber[i] = nDfsNumber[u]; - } - } - } - goto advance_ring; - } else { - cNeighNumb[i] = 0; - } - - /* back up */ - if ( nDfsNumber[i] == nLowNumber[i] ) { - /* found a ring system */ - nNumRingSystems ++; - - /* unwind nRingStack[] down to i */ - - /* count atoms in a ring system */ - for ( nNumAtInRingSystem = 0, j = nTopRingStack; 0 <= j; j -- ) { - nNumAtInRingSystem ++; - if ( i == (int)nRingStack[j] ) { - break; - } - } - while ( nTopRingStack >= 0 ) { - j = (int)nRingStack[nTopRingStack--]; - at[j].nRingSystemAltBns = (AT_NUMB)nNumRingSystems; /* ring system id */ - at[j].nNumAtInRingSystemAltBns = nNumAtInRingSystem; - if ( i == j ) { - /* reached atom on the top of nStackAtom[] stack */ - break; - } - } - } else - if ( nTopStackAtom > 0 ) { - j = (int)nStackAtom[nTopStackAtom-1]; - /* inherit nLowNumber */ - if ( nLowNumber[j] > nLowNumber[i] ) { - nLowNumber[j] = nLowNumber[i]; - } - } - } while ( --nTopStackAtom >= 0 ); - } - -#endif /* } BNS_MARK_ONLY_BLOCKS != 1 */ - -exit_function: - if ( nStackAtom ) - inchi_free( nStackAtom ); - if ( nRingStack ) - inchi_free( nRingStack ); - if ( nDfsNumber ) - inchi_free( nDfsNumber ); - if ( nLowNumber ) - inchi_free( nLowNumber ); - if ( nBondStack ) - inchi_free( nBondStack ); - if ( cNeighNumb ) - inchi_free( cNeighNumb ); - return nNumRingSystems; -} - - - -/*****************************************************************************/ -int ReInitBnStructForAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bUnknAltAsNoStereo ) -{ - Vertex v, v2; - int ret, bond_type, num_to_test, j; - BNS_EDGE *pBond; - BNS_VERTEX *pAtom; - /* strip all t-groups and c-groups */ - num_to_test = 0; - if ( bUnknAltAsNoStereo ) { - for ( j = 0; j < pBNS->num_edges; j ++ ) { - pBNS->edge[j].pass = 0; - } - } - ret = ReInitBnStruct( pBNS, at, num_atoms, 0 ); - if ( ret || pBNS->num_atoms != num_atoms || pBNS->num_vertices != num_atoms || pBNS->num_bonds != pBNS->num_edges ) { - ret = BNS_REINIT_ERR; - goto exit_function; - } - /* eliminate bonds and fix st-caps */ - for ( v = 0; v < num_atoms; v ++ ) { - pAtom = pBNS->vert + v; - for ( j = 0; j < pAtom->valenceAltBns; j ++ ) { - pBond = pBNS->edge + pAtom->iedge[j]; - if ( pBond->neighbor1 == v ) { - bond_type = (at[v].bond_type[j] & BOND_TYPE_MASK); - v2 = pBond->neighbor12 ^ v; - if ( at[v].endpoint || at[v2].endpoint ) { - bond_type = 0; /* any bond to an endpoint considered non-stereogenic */ - } -#if ( FIX_EITHER_DB_AS_NONSTEREO == 1 ) - if ( bUnknAltAsNoStereo ) { - if ( bond_type == BOND_ALTERN && at[v].bond_stereo[j] == STEREO_DBLE_EITHER ) { - bond_type = 0; /* treat unknown (Either) ALT bond as non-stereo */ - } - } -#endif - switch ( bond_type ) { - - case BOND_ALTERN : - pBond->nBondTypeInpAltBns = BT_ALTERN_BOND; - num_to_test ++; - break; - - case BOND_ALT_123: - case BOND_ALT_13 : - case BOND_ALT_23 : - pBond->nBondTypeInpAltBns = BT_OTHER_ALTERN_BOND; - break; - - case BOND_TAUTOM : - pBond->nBondTypeInpAltBns = BT_TAUTOM_BOND; - break; - - case BOND_ALT12NS: - pBond->nBondTypeInpAltBns = BT_ALTERN_NS_BOND; - break; - - case 0: - case BOND_SINGLE : - case BOND_DOUBLE : - case BOND_TRIPLE : - pBond->nBondTypeInpAltBns = BT_IGNORE_BOND; - break; - - default: - pBond->nBondTypeInpAltBns = BT_IGNORE_BOND; - break; - - } - pBond->nBondNonStereoAltBns = - pBond->nBlockNumberAltBns = - pBond->nNumAtInBlockAltBns = 0; - -#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) - pBond->forbidden &= pBNS->edge_forbidden_mask; -#endif - } - } - pAtom->bCutVertexAltBns = - pAtom->nRingSystemAltBns = - pAtom->nNumAtInRingSystemAltBns = - pAtom->nBlockSystemAltBns = 0; - } - - return num_to_test; -exit_function: - return ret; -} - - - -/*****************************************************************************/ -int MarkNonStereoAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bUnknAltAsNoStereo ) -{ - int num_bonds = pBNS->num_bonds; - int ret; - int ibond, ib1, ib2; - BNS_EDGE *pBond; - Vertex iat1, iat2; - - ret = 0; - - if ( pBNS->num_atoms != num_atoms || pBNS->num_vertices != num_atoms || pBNS->num_bonds != pBNS->num_edges ) { - ret = BNS_REINIT_ERR; - goto exit_function; - } - if ( bUnknAltAsNoStereo ) { - for ( ibond=0; ibond < num_bonds; ibond ++ ) { - pBond = pBNS->edge + ibond; - if ( pBond->nBondTypeInpAltBns != BT_ALTERN_BOND && pBond->nBondTypeInpAltBns != BT_IGNORE_BOND ) { - continue; - } - iat1 = pBond->neighbor1; - iat2 = pBond->neighbor12 ^ iat1; - ib1 = pBond->neigh_ord[0]; - ib2 = pBond->neigh_ord[1]; - if ( /* alt bond non-adjacent to a taut. endpoint: */ - (pBond->nBondTypeInpAltBns == BT_ALTERN_BOND && - pBond->nNumAtInBlockAltBns <= 3 ) /* non-ring bond */ || - /* alt bond adjacent to a taut. endpoint: */ - (pBond->nBondTypeInpAltBns == BT_IGNORE_BOND && - (at[iat1].bond_type[ib1] & BOND_TYPE_MASK) == BOND_ALTERN ) - ) { - if ( (at[iat1].bond_type[ib1] & BOND_TYPE_MASK) == BOND_ALTERN ) { - /* bond_type = BOND_ALT12NS; */ - at[iat1].bond_stereo[ib1] = - at[iat2].bond_stereo[ib2] =STEREO_DBLE_EITHER; - ret ++; - } - } - } - } else { - for ( ibond=0; ibond < num_bonds; ibond ++ ) { - pBond = pBNS->edge + ibond; - if ( pBond->nBondTypeInpAltBns != BT_ALTERN_BOND && pBond->nBondTypeInpAltBns != BT_IGNORE_BOND ) { - continue; - } - iat1 = pBond->neighbor1; - iat2 = pBond->neighbor12 ^ iat1; - ib1 = pBond->neigh_ord[0]; - ib2 = pBond->neigh_ord[1]; - if ( /* alt bond non-adjacent to a taut. endpoint: */ - (pBond->nBondTypeInpAltBns == BT_ALTERN_BOND && - pBond->nNumAtInBlockAltBns <= 3 ) /* non-ring bond */ || - /* alt bond adjacent to a taut. endpoint: */ - (pBond->nBondTypeInpAltBns == BT_IGNORE_BOND && - (at[iat1].bond_type[ib1] & BOND_TYPE_MASK) == BOND_ALTERN ) - ) - { - at[iat1].bond_type[ib1] = - at[iat2].bond_type[ib2] =BOND_ALT12NS; - ret ++; - } - } - } - -exit_function: - - return ret; -} - -#if ( READ_INCHI_STRING == 1 ) -/*****************************************************************************/ -#ifndef RI_ERR_ALLOC -/* from ichirvrs.h */ -#define RI_ERR_ALLOC (-1) -#define RI_ERR_SYNTAX (-2) -#define RI_ERR_PROGR (-3) -#endif - - - -/*****************************************************************************/ -int bHasChargedNeighbor( inp_ATOM *at, int iat ) -{ - int i; - for( i = 0; i < at[iat].valence; i ++ ) { - if ( at[(int)at[iat].neighbor[i]].charge ) - return 1; - } - return 0; -} - - - -/********************************************************************************* - *num_protons_to_add = nToBeRemovedByNormFromRevrs - - nToBeRemovedByNormFromRevrs > 0: less protons should be allowed to be - added by the Normalization of the Reconstructed Structure - nToBeRemovedByNormFromRevrs < 0: prepare more H(+) to be removed by - the InChI Normalization of the Reconstructed Structure - - OrigStruct -> NormOrig + n(orig)*H(+) - RevrStruct -> NormRevr + n(revr)*H(+) - nToBeRemovedByNormFromRevrs = n(orig) - n(revr) [each may be negative] - - n(orig) > n(revr) or nToBeRemovedByNormFromRevrs > 0 means: - ----------------------------------------------------------- - - Too many protons were added by the Normalization to the Reconstructed Structure - (a) n(revr) < 0 => protons were added while they should not have been added; - Solution: "neutralize" (-) charged proton acceptors by moving charges to other atoms - on the condition ADP cannot add in another way; - (b) n(orig) > n(revr) => 0 => too few protons were removed - Solution: (the easiest) attach H(+) to =O or -N< or -N= - Solution: move (+) from N or OH to an atom adjacent to (-) charge or to - an atom that is not N. - - n(orig) < n(revr) or nToBeRemovedByNormFromRevrs < 0 means: - ----------------------------------------------------------- - - Too few protons were added by the Normalization to the Reconstructed Stucture - (a) n(orig) < 0 => protons were not added while they should have been added; - Solution: move (-) to O by replacing =O with -O(-) - (b) 0 <= n(orig) < n(revr) => too many protons were removed - - Note: it is critically important to takr into account cumbersome Normalization - Total Charge: if it is >= 0 then no H(+) may be removed from -OH or by ADP - However, if N(+) is present then ADP will always try to remove a proton -*********************************************************************************/ -int AddRemoveProtonsRestr( inp_ATOM *at, int num_atoms, int *num_protons_to_add, - int nNumProtAddedByRestr, INCHI_MODE bNormalizationFlags, - int num_tg, int nChargeRevrs, int nChargeInChI ) -{ - int i, j, ret = 0; - int nAtTypeTotals[ATTOT_ARRAY_LEN]; - int num_prot = *num_protons_to_add; - int type, mask, bSuccess, nTotCharge, nNumSuccess = 0; - int max_j_Aa=-1, max_j_Ar=-1; - -/* for the reference: - -#define FLAG_NORM_CONSIDER_TAUT ( FLAG_PROTON_NPO_SIMPLE_REMOVED | \ - FLAG_PROTON_NP_HARD_REMOVED | \ - FLAG_PROTON_AC_SIMPLE_ADDED | \ - FLAG_PROTON_AC_SIMPLE_REMOVED | \ - FLAG_PROTON_AC_HARD_REMOVED | \ - FLAG_PROTON_AC_HARD_ADDED | \ - FLAG_PROTON_SINGLE_REMOVED | \ - FLAG_PROTON_CHARGE_CANCEL ) - -#define FLAG_FORCE_SALT_TAUT ( FLAG_PROTON_NP_HARD_REMOVED | \ - FLAG_PROTON_AC_HARD_REMOVED | \ - FLAG_PROTON_AC_HARD_ADDED ) - -*/ - /* if ChargeRevrs > nChargeInChI then we should prevent proton addition or facilitate proton removal - a typical case is (=) on N or O instead of C(-) - - if ChargeRevrs < nChargeInChI then we should prevent proton removal or facilitate proton addition - */ - - mark_at_type( at, num_atoms, nAtTypeTotals ); - for ( i = nTotCharge = 0; i < num_atoms; i ++ ) { - nTotCharge += at[i].charge; - } - /* size for SimpleAddAcidicProtons() */ - for ( max_j_Aa = 0; AaTypMask[2*max_j_Aa]; max_j_Aa ++ ) - ; - /* size for SimpleRemoveAcidicProtons */ - for ( max_j_Ar = 0; ArTypMask[2*max_j_Ar]; max_j_Ar ++ ) - ; - if ( num_prot < 0 && nAtTypeTotals[ATTOT_TOT_CHARGE]-nNumProtAddedByRestr <= 0 ) { - /* remove proton(s) */ - /* use test from SimpleAddAcidicProtons() to test whether removal of H(+) from =C-OH, etc. is correct */ - for ( i = 0; i < num_atoms && num_prot; i ++ ) { - /* choose an atom */ - if ( at[i].sb_parity[0] || at[i].p_parity || at[i].charge || - !at[i].num_H || at[i].radical || bHasChargedNeighbor( at, i ) ) { - continue; - } - /* try to remove a proton and check whether InChI would add it back */ - at[i].charge --; - at[i].num_H --; - type = GetAtomChargeType( at, i, NULL, &mask, 0 ); - at[i].charge ++; - at[i].num_H ++; - - if ( type ) { - for ( bSuccess = 0, j = 0; j < max_j_Aa; j ++ ) { - if ( bSuccess = (type & AaTypMask[2*j]) && (mask && AaTypMask[2*j+1]) ) { - break; /* the proton may be added to this atom */ - } - } - if ( bSuccess ) { - type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ - at[i].charge --; - at[i].num_H --; - type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ - num_prot ++; /* success */ - nNumSuccess ++; - } - } - } - } - if ( num_prot < 0 && num_tg && nAtTypeTotals[ATTOT_TOT_CHARGE]-nNumProtAddedByRestr <= 0 ) { - /* alternative proton removal: O=C-NH => (-)O-C=N, O and N are taut. endpoints */ - int endp2, centp, k, i0, k0; - for ( i = 0; i < num_atoms; i ++ ) { - /* choose an atom */ - if ( !at[i].endpoint || at[i].sb_parity[0] || at[i].p_parity || - at[i].radical || at[i].charge || bHasChargedNeighbor( at, i ) ) { - continue; - } - /* looking for tautomeric =O */ - if ( 1 != at[i].valence || BOND_TYPE_DOUBLE != at[i].bond_type[0] || at[i].num_H || - 2 != get_endpoint_valence( at[i].el_number ) ) { - continue; - } - centp = at[i].neighbor[0]; - if ( at[centp].sb_parity[0] || at[centp].p_parity || !is_centerpoint_elem( at[centp].el_number ) ) { - continue; - } - /* found a possible centerpoint, looking for -NH endpoint */ - for ( k = 0; k < at[centp].valence; k ++ ) { - if ( at[centp].bond_type[k] != BOND_TYPE_SINGLE ) { - continue; - } - endp2 = at[centp].neighbor[k]; - if ( at[endp2].endpoint != at[i].endpoint || - !at[endp2].num_H || at[endp2].charge || - at[endp2].sb_parity[0] || at[endp2].p_parity || - at[endp2].valence != at[endp2].chem_bonds_valence || - 3 != at[endp2].chem_bonds_valence + at[endp2].num_H || - 3 != get_endpoint_valence( at[endp2].el_number ) ) { - continue; - } - /* find bonds in reciprocal ajacency lists */ - for ( i0 = 0; i0 < at[centp].valence && i != at[centp].neighbor[i0]; i0 ++ ) - ; - for ( k0 = 0; k0 < at[endp2].valence && centp != at[endp2].neighbor[k0]; k0 ++ ) - ; - if ( i0 == at[centp].valence || k0 == at[endp2].valence ) { - return RI_ERR_PROGR; - } - /* -NH has been found */ - type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ - type = GetAtomChargeType( at, endp2, nAtTypeTotals, &mask, 1 ); /* subtract at[endp2] */ - - at[i].bond_type[0] --; - at[centp].bond_type[i0] --; - at[i].chem_bonds_valence --; - at[i].charge --; - - at[endp2].bond_type[k0] ++; - at[centp].bond_type[k] ++; - at[endp2].chem_bonds_valence ++; - at[endp2].num_H --; - - num_prot ++; - nNumSuccess ++; - - type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add at[i] */ - type = GetAtomChargeType( at, endp2, nAtTypeTotals, &mask, 0 ); /* add at[endp2] */ - } - } - } - if ( num_prot > 0 ) { - /* add protons */ - /* 1. Use test from SimpleRemoveAcidicProtons() to test whether addition of H(+) to =C-O(-), etc. is correct */ - for ( i = 0; i < num_atoms && num_prot && nAtTypeTotals[ATTOT_TOT_CHARGE]-nNumProtAddedByRestr >= 0; i ++ ) { - /* choose an atom */ - if ( at[i].sb_parity[0] || at[i].p_parity || at[i].num_H || - at[i].charge != -1 || at[i].radical || bHasChargedNeighbor( at, i ) ) { - continue; - } - /* try to add a proton and check whether InChI would remove it back */ - at[i].charge ++; - at[i].num_H ++; - type = GetAtomChargeType( at, i, NULL, &mask, 0 ); - at[i].charge --; - at[i].num_H --; - - if ( type ) { - for ( bSuccess = 0, j = 0; j < max_j_Ar; j ++ ) { - if ( bSuccess = (type & ArTypMask[2*j]) && (mask && ArTypMask[2*j+1]) ) { - break; - } - } - if ( bSuccess ) { - type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ - at[i].charge ++; - at[i].num_H ++; - type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ - num_prot --; /* success */ - nNumSuccess ++; - } - } - } - /* 2. Use test from SimpleRemoveHplusNPO() */ - for ( i = 0; i < num_atoms && num_prot; i ++ ) { - /* choose an atom */ - if ( at[i].sb_parity[0] || at[i].p_parity || - at[i].charge || at[i].radical || bHasChargedNeighbor( at, i ) ) { - continue; - } - /* try to add a proton and check whether InChI would remove it back */ - at[i].num_H ++; - at[i].charge ++; - bSuccess = (PR_SIMPLE_TYP & (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) && - (PR_SIMPLE_MSK & mask ); - at[i].num_H --; /* failed */ - at[i].charge --; - if ( bSuccess ) { - type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ - at[i].num_H ++; - at[i].charge ++; - type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ - num_prot --; /* succeeded */ - nNumSuccess ++; - } - } - } - - if ( num_prot < 0 && (bNormalizationFlags & FLAG_PROTON_AC_HARD_ADDED) && 1 == num_tg && - nAtTypeTotals[ATTOT_TOT_CHARGE]-nNumProtAddedByRestr <= 0 ) { - /* try to remove protons from tautomeric N (specific ADP must be present) */ - int nNumAcceptors_DB_O=0, nNumDonors_SB_NH=0, num_max, num_success; - for ( i = 0; i < num_atoms; i ++ ) { - /* choose an atom */ - if ( !at[i].endpoint || at[i].radical || - at[i].sb_parity[0] || at[i].p_parity || bHasChargedNeighbor( at, i ) ) { - continue; - } - type = GetAtomChargeType( at, i, NULL, &mask, 0 ); - if ( (type & AA_HARD_TYP_CO) && (mask & AA_HARD_MSK_CO) ) { - nNumAcceptors_DB_O ++; - } else - if ( (type == ATT_ATOM_N ) && (mask == ATBIT_NP_H) && !at[i].charge && - at[i].valence == at[i].chem_bonds_valence ) { - nNumDonors_SB_NH ++; - } - } - num_max = inchi_min( nNumAcceptors_DB_O, nNumDonors_SB_NH ); - for ( i = 0, num_success = 0; i < num_atoms && num_success < num_max && num_prot < 0; i ++ ) { - /* choose an atom */ - if ( !at[i].endpoint|| at[i].radical || at[i].sb_parity[0] || - at[i].p_parity || bHasChargedNeighbor( at, i ) ) { - continue; - } - type = GetAtomChargeType( at, i, NULL, &mask, 0 ); - if ( (type == ATT_ATOM_N ) && (mask == ATBIT_NP_H) && !at[i].charge && - at[i].valence == at[i].chem_bonds_valence ) { - type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ - at[i].num_H --; - at[i].charge --; - type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ - num_prot ++; - num_success ++; - nNumSuccess ++; - } - } - } -/*exit_function:*/ - *num_protons_to_add = num_prot; - return ret<0? ret : nNumSuccess; -} - - - -/*****************************************************************************/ -int AddRemoveIsoProtonsRestr( inp_ATOM *at, int num_atoms, NUM_H num_protons_to_add[], int num_tg ) -{ - int i, j, k, n, ret = 0; - int nNumSuccess = 0, min_at, max_at, num_H, num_iso_H, num_expl_H, num_expl_iso_H; - int iCurIso; /* 0=> 1H, 1=> D, 2=> T */ - int iCurMode, iCurMode1, iCurMode2; /* 0=> Not Endpoints, 1=> Endpoints */ - static U_CHAR el_number_H = 0; - - /* distribute isotopes from heaviest to lightest; pick up atoms in order 1. Not endpoints; 2. Endpoints */ - iCurMode1 = 0; - iCurMode2 = num_tg ? 1 : 0; - if ( !el_number_H ) { - el_number_H = (U_CHAR) get_periodic_table_number( "H" ); - } - for ( iCurMode = iCurMode1; iCurMode <= iCurMode2; iCurMode ++ ) { - for ( iCurIso = 2; 0 <= iCurIso; iCurIso -- ) { - /* check for isotopic H to add */ - if ( !num_protons_to_add[iCurIso] ) { - continue; - } - if ( 0 > num_protons_to_add[iCurIso] ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - /* limits for atom scanning */ - min_at = 0; - max_at = num_atoms; - /* cycle withio the limits */ - for ( i = min_at; i < max_at && 0 < num_protons_to_add[iCurIso]; i ++ ) { - /* pick an atom */ - if ( iCurMode ) { - if ( at[i].endpoint ) - j = i; /* atom number */ - else - continue; - } else - if ( !at[i].endpoint && - 1 == bHeteroAtomMayHaveXchgIsoH( at, i ) ) { /* atom number */ - j = i; - } else - if ( at[i].el_number == el_number_H && at[i].charge == 1 && - !at[i].valence && !at[i].radical && !at[i].iso_atw_diff ) { - /* proton, not isotopic; make it isotopic */ - at[i].iso_atw_diff = 1 + iCurIso; - num_protons_to_add[iCurIso] --; - nNumSuccess ++; - continue; - } else { - continue; - } - /* j is the atom number */ - /* count implicit H */ - num_H = at[j].num_H; - num_iso_H = NUM_ISO_H(at,j); - while ( num_H > 0 && num_protons_to_add[iCurIso] > 0 ) { - /* substitute one implicit H with an isotopic atom H */ - at[j].num_iso_H[iCurIso] ++; - at[j].num_H --; - num_protons_to_add[iCurIso] --; - num_H --; - num_iso_H ++; - nNumSuccess ++; - } - /* count explicit H */ - num_expl_H = num_expl_iso_H = 0; - for ( k = 0; k < at[j].valence && num_atoms <= (n=at[j].neighbor[k]); k ++ ) { - num_expl_H += (0 == at[n].iso_atw_diff); - num_expl_iso_H += (0 != at[n].iso_atw_diff); - } - while ( num_expl_H > 0 && num_protons_to_add[iCurIso] > 0 ) { - /* substitute one explicit H with an isotopic atom H */ - n = at[j].neighbor[num_expl_H]; - if ( at[n].iso_atw_diff ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - at[n].iso_atw_diff = 1 + iCurIso; - num_expl_H --; - num_expl_iso_H ++; - num_protons_to_add[iCurIso] --; - nNumSuccess ++; - } - } - } - } -exit_function: - return ret<0? ret : nNumSuccess; -} - -#endif - +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +/* + Balanced Network Search + + Normalization related procedures +*/ + + +#include +#include + +#include "mode.h" +#include "ichitime.h" +#include "ichicant.h" +#include "ichierr.h" +#include "ichitaut.h" +#include "ichinorm.h" +#include "util.h" +#include "ichister.h" +#include "ichi_bns.h" + + +#define BNS_MARK_ONLY_BLOCKS 1 /* 1 => find only blocks, do not search for ring systems */ +#define ALLOW_ONLY_SIMPLE_ALT_PATH 0 /* 0 => allow alt. path to contain same bond 2 times (in opposite directions) */ +#define CHECK_TG_ALT_PATH 0 /* 1=> when chacking alt path of a tautomeric atom modify + t-group, not the atom */ + /* 0=> old mode */ + +#define FIX_CPOINT_BOND_CAP 1 /* 1=> fix bug in case of double bond from neutral cpoint */ +#define RESET_EDGE_FORBIDDEN_MASK 1 /* 1: previous; 0: do not apply "edge->forbidden &= pBNS->edge_forbidden_mask" */ +#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) +#define IS_FORBIDDEN(EDGE_FORBIDDEN, PBNS) (EDGE_FORBIDDEN) +#else +#define IS_FORBIDDEN(EDGE_FORBIDDEN, PBNS) (EDGE_FORBIDDEN & PBNS->edge_forbidden_mask) +#endif + + +typedef enum tagAtTypeTotals +{ + /* counts do not include: + charged atom adjacent to another charged atom + atom in an unusual valence state or adjacent to an atom in an unusual valence state + radicals different from singlet + */ + /*ATTOT_NUM_Plus. */ /* number of positive charges, +1, is (ATTOT_NUM_CHARGES + ATTOT_TOT_CHARGE)/2 */ + /*ATTOT_NUM_Minus.*/ /* number of negative charges, -1, is (ATTOT_NUM_CHARGES - ATTOT_TOT_CHARGE)/2 */ + ATTOT_NUM_NP_Plus, /* 0 no H: =N(+)=, #N(+)-, =N(+)<, does not include onium cations >P(+)<, >N(+)< */ + ATTOT_NUM_NP_Proton, /* 1 H(+): -NH3(+), =NH2(+), >NH2(+), =NH(+)-, >NH(+)-, #NH(+), N=N,P */ + ATTOT_NUM_NP_H, /* 2 H: -NH2, =NH, >NH -NH(-) */ + ATTOT_NUM_N_Minus, /* 3 (-): -NH(-), >N(-), =N(-) */ + ATTOT_NUM_NP, /* 4 no H: >N- =N-, #N */ + ATTOT_NUM_ON, /* 5 -N=O: do not allow -N=O => -NH-OH during H(+) add/removal */ + ATTOT_NUM_COH, /* 6 =C-OH, #C-OH; O=O,S,Se,Te */ + ATTOT_NUM_CSH, /* 7 -C-SH, -C-SeH -C-TeH */ + ATTOT_NUM_ZOH, /* 8 =Z-OH, #Z-OH; O=O,S,Se,Te; Z may have charge, Z != C */ + ATTOT_NUM_OOH, /* 9 -O-OH, O=O,S,Se,Te */ + ATTOT_NUM_ZOOH, /* 10 O=Z-OH, O=O,S,Se,Te */ + ATTOT_NUM_NOH, /* 11 =N-OH, -N(-)-OH */ + ATTOT_NUM_N_OH, /* 12 >N-OH, -NH-OH, >NH(+)-OH, -N(-)-OH */ + ATTOT_NUM_CO, /* 13 -C=O, =C=O; O=O,S,Se,Te */ + ATTOT_NUM_ZO, /* 14 -Z=O, =Z=O; O=O,S,Se,Te; Z may have charge */ + ATTOT_NUM_NO, /* 15 -N=O, =N(+)=O */ + ATTOT_NUM_N_O, /* 16 >N(+)=O, =N(+)=O */ + ATTOT_NUM_CO_Minus, /* 17 =C-O(-), #C-O(-); O=O,S,Se,Te */ + ATTOT_NUM_CS_Minus, /* 18 -C-S(-); S = S, Se, Te */ + ATTOT_NUM_ZO_Minus, /* 19 =Z-O(-), #Z-O(-); O = O, S, Se, Te */ + ATTOT_NUM_OO_Minus, /* 20 -O-O(-), O=O,S,Se,Te */ + ATTOT_NUM_ZOO_Minus, /* 21 O=Z-O(-), O=O,S,Se,Te */ + ATTOT_NUM_NO_Minus, /* 22 >N-O(-), -NH-O(-) */ + ATTOT_NUM_N_O_Minus, /* 23 -NH-O(-), >N-O(-); O = O, S, Se, Te */ + ATTOT_NUM_O_Minus, /* 24 -Z-O(-); O=O,S,Se,Te */ + ATTOT_NUM_OH_Plus, /* 25 any OH(+) */ + ATTOT_NUM_O_Plus, /* 26 any O(+) without H */ + ATTOT_NUM_Proton, /* 27 proton */ + ATTOT_NUM_HalAnion, /* 28 Halogen anion */ + ATTOT_NUM_HalAcid, /* 29 Halogen acid */ + ATTOT_NUM_Errors, /* 30 for debugging */ + ATTOT_TOT_CHARGE, /* 31 total of positive and negative single charges, +1 and -1 */ + ATTOT_NUM_CHARGES, /* 32 number of positive and negative single charges, +1 and -1 */ + ATTOT_ARRAY_LEN /* 33 array length */ +} AT_TYPE_TOTALS; + +#define ATBIT_NP_Plus (1 << ATTOT_NUM_NP_Plus) +#define ATBIT_NP_Proton (1 << ATTOT_NUM_NP_Proton) +#define ATBIT_NP_H (1 << ATTOT_NUM_NP_H) +#define ATBIT_N_Minus (1 << ATTOT_NUM_N_Minus) +#define ATBIT_NP (1 << ATTOT_NUM_NP) +#define ATBIT_ON (1 << ATTOT_NUM_ON) +#define ATBIT_COH (1 << ATTOT_NUM_COH) +#define ATBIT_CSH (1 << ATTOT_NUM_CSH) +#define ATBIT_ZOH (1 << ATTOT_NUM_ZOH) +#define ATBIT_OOH (1 << ATTOT_NUM_OOH) +#define ATBIT_ZOOH (1 << ATTOT_NUM_ZOOH) +#define ATBIT_NOH (1 << ATTOT_NUM_NOH) +#define ATBIT_N_OH (1 << ATTOT_NUM_N_OH) +#define ATBIT_CO (1 << ATTOT_NUM_CO) +#define ATBIT_ZO (1 << ATTOT_NUM_ZO) +#define ATBIT_NO (1 << ATTOT_NUM_NO) +#define ATBIT_N_O (1 << ATTOT_NUM_N_O) +#define ATBIT_CO_Minus (1 << ATTOT_NUM_CO_Minus) +#define ATBIT_CS_Minus (1 << ATTOT_NUM_CS_Minus) +#define ATBIT_ZO_Minus (1 << ATTOT_NUM_ZO_Minus) +#define ATBIT_OO_Minus (1 << ATTOT_NUM_OO_Minus) +#define ATBIT_ZOO_Minus (1 << ATTOT_NUM_ZOO_Minus) +#define ATBIT_NO_Minus (1 << ATTOT_NUM_NO_Minus) +#define ATBIT_N_O_Minus (1 << ATTOT_NUM_N_O_Minus) +#define ATBIT_O_Minus (1 << ATTOT_NUM_O_Minus) +#define ATBIT_OH_Plus (1 << ATTOT_NUM_OH_Plus) +#define ATBIT_O_Plus (1 << ATTOT_NUM_O_Plus) +#define ATBIT_Proton (1 << ATTOT_NUM_Proton) +#define ATBIT_HalAnion (1 << ATTOT_NUM_HalAnion) +#define ATBIT_HalAcid (1 << ATTOT_NUM_HalAcid) + + +#define ATBIT_Errors (1 << ATTOT_NUM_Errors) + +typedef struct tagProtonRemovalMaskAndType +{ + int typePos; /* atoms accessible to positive charges */ + int maskPos; + int typeNeg; /* atoms accessible to negative charges */ + int maskNeg; + int typeH; /* atoms accessible to hydrogen atoms */ + int maskH; +} PRMAT; + +#define PR_SIMPLE_MSK (ATBIT_NP_Proton | ATBIT_OH_Plus) +#define PR_SIMPLE_TYP (ATT_ATOM_N | ATT_ATOM_P | ATT_O_PLUS) + +#define ATBIT_MSK_NP (ATBIT_NP_Plus | ATBIT_NP_Proton | ATBIT_NP_H | ATBIT_N_Minus | ATBIT_NP) +#define KNOWN_ACIDIC_TYPE (ATT_ACIDIC_CO | ATT_ACIDIC_S | ATT_OO | ATT_ZOO | ATT_NO) +#define ATBIT_MSK_OS (ATBIT_COH | ATBIT_CSH | ATBIT_ZOH | ATBIT_OOH | ATBIT_ZOOH | ATBIT_NOH | ATBIT_N_OH |\ + ATBIT_CO | ATBIT_ZO | ATBIT_NO | ATBIT_N_O |\ + ATBIT_CO_Minus | ATBIT_CS_Minus | ATBIT_ZO_Minus | ATBIT_OO_Minus |\ + ATBIT_ZOO_Minus | ATBIT_NO_Minus | ATBIT_N_O_Minus /*| ATBIT_O_Minus*/ ) +#define ATBIT_MSK_H (ATBIT_NP_Proton | ATBIT_NP_H | ATBIT_COH | ATBIT_CSH | ATBIT_ZOH | ATBIT_OOH |\ + ATBIT_ZOOH | ATBIT_NOH | ATBIT_N_OH) + +#define ATTYP_OS (ATT_ACIDIC_CO | ATT_ACIDIC_S | ATT_OO | ATT_ZOO | ATT_NO /*| ATT_OTHER_NEG_O*/ | ATT_OTHER_ZO) +#define ATTYP_NP (ATT_ATOM_N | ATT_ATOM_P) +#define ATTYP_N (ATT_ATOM_N) +#define ATTYP_P (ATT_ATOM_P) + +/************* simple proton removal from acids **************************/ +#define AR_ANY_OH 0 /* 1 => create unknown to be acidic anions */ +#define AR_SIMPLE_STEPS 3 +/* acidic groups for proton removal, step 1 */ +#define AR_SIMPLE_MSK1 (ATBIT_COH | ATBIT_CSH | ATBIT_OOH | ATBIT_ZOOH | ATBIT_NOH | ATBIT_HalAcid) +#define AR_SIMPLE_TYP1 (ATT_ACIDIC_CO | ATT_ACIDIC_S | ATT_OO | ATT_ZOO | ATT_NO | ATT_HalAcid) +/* acidic groups for proton removal, step 2 */ +#define AR_SIMPLE_MSK2 (AR_ANY_OH? (ATBIT_N_OH) :0) +#define AR_SIMPLE_TYP2 (AR_ANY_OH? (ATT_N_O) :0) +/* acidic groups for proton removal, step 3 */ +#define AR_SIMPLE_MSK3 (AR_ANY_OH? (ATBIT_ZOH) :0) +#define AR_SIMPLE_TYP3 (AR_ANY_OH? (ATT_OTHER_ZO):0) + +/************* simple proton addition to acids **************************/ +#define AA_ANY_O_Minus 0 /* 1 => neutralize unknown to be acidic anions */ +#define AA_SIMPLE_STEPS 3 +/* acidic groups for proton addition, step 1 */ +#define AA_SIMPLE_MSK1 (ATBIT_CO_Minus | ATBIT_CS_Minus | ATBIT_OO_Minus | ATBIT_ZOO_Minus | ATBIT_NO_Minus | ATBIT_O_Minus | ATBIT_HalAnion) +#define AA_SIMPLE_TYP1 (ATT_ACIDIC_CO | ATT_ACIDIC_S | ATT_OO | ATT_ZOO | ATT_NO | ATT_OH_MINUS | ATT_HalAnion ) +/* acidic groups for proton addition, step 2 */ +#define AA_SIMPLE_MSK2 (AA_ANY_O_Minus? (ATBIT_N_O_Minus) :0) +#define AA_SIMPLE_TYP2 (AA_ANY_O_Minus? (ATT_N_O) :0) +/* acidic groups for proton addition, step 3 */ +#define AA_SIMPLE_MSK3 (AA_ANY_O_Minus? (ATBIT_ZO_Minus | ATBIT_O_Minus):0) +#define AA_SIMPLE_TYP3 (AA_ANY_O_Minus? (ATT_OTHER_ZO) :0) + +#if ( FIX_NP_MINUS_BUG == 1 ) +/* allow to add H(+) to =N(-) which previously was #N */ +#undef AA_SIMPLE_STEPS +#define AA_SIMPLE_STEPS 4 +#define AA_SIMPLE_MSK4 ATBIT_N_Minus +#define AA_SIMPLE_TYP4 ATT_NP_MINUS_V23 +#endif + +/************* hard proton removal from NP **************************/ +/* (+) charge group for proton removal: mask & type */ +#define PR_HARD_MSK_POS ATBIT_MSK_NP +#define PR_HARD_TYP_POS ATTYP_N +#define PR_HARD_TYP_POSP ATTYP_P +/* (-) charge group for proton removal */ +#define PR_HARD_MSK_NEG (ATBIT_MSK_NP | ATBIT_MSK_OS) +#define PR_HARD_TYP_NEG (ATTYP_N | ATTYP_OS) +/* H-group for proton removal */ +#define PR_HARD_MSK_H (ATBIT_MSK_NP | ATBIT_MSK_OS) +#define PR_HARD_TYP_H (ATTYP_N | ATTYP_OS) + +/************* hard proton removal from acids **************************/ +/* (+) charge group for proton removal: mask & type */ +#define AR_HARD_MSK_POS ATBIT_MSK_NP +#define AR_HARD_TYP_POS ATTYP_N +/* (-) charge group for proton removal */ +#define AR_HARD_MSK_NEG (ATBIT_MSK_NP | ATBIT_MSK_OS) +#define AR_HARD_TYP_NEG (ATTYP_N | ATTYP_OS) +/* H-group acid for proton removal */ +#define AR_HARD_MSK_HA (ATBIT_CO | ATBIT_NO ) +#define AR_HARD_TYP_HA (ATT_ACIDIC_CO | ATT_NO) +/* H-group non-acid for proton removal */ +#define AR_HARD_MSK_HN ((ATBIT_MSK_NP | ATBIT_MSK_OS) & ~AR_HARD_MSK_HA) +#define AR_HARD_TYP_HN ((ATTYP_N | ATTYP_OS) /*& ~AR_HARD_TYP_HA*/) + +/************* hard proton addition to acids **************************/ +/* (+) charge group for proton removal: mask & type */ +#define AA_HARD_MSK_POS ATBIT_MSK_NP +#define AA_HARD_TYP_POS ATTYP_N +/* (-) charge group for negative charge removal */ +#define AA_HARD_MSK_NEG ((ATBIT_MSK_NP | ATBIT_MSK_OS) & ~(ATBIT_CO | ATBIT_NO )) +#define AA_HARD_TYP_NEG (ATTYP_N | ATTYP_OS) +/* (-) charge group to accept negative charges */ +#define AA_HARD_MSK_CO (ATBIT_CO | ATBIT_NO ) +#define AA_HARD_TYP_CO (ATT_ACIDIC_CO | ATT_NO) +/* H-group non-acid for proton removal */ +#define AA_HARD_MSK_H (ATBIT_MSK_NP | ATBIT_MSK_OS) +#define AA_HARD_TYP_H (ATTYP_N | ATTYP_OS) + + +/*****************************************************************************/ +#define BNS_MAX_NUM_FLOW_CHANGES (1+2*MAX_BOND_EDGE_CAP) + +/* -- opiginal Pascal values -- +#define NO_VERTEX 0 +#define BLOSSOM_BASE -1 +#define FIRST_INDX 1 +*/ + +#define TREE_NOT_IN_M 0 /* not in T or T' */ +#define TREE_IN_2 1 /* in T' and not s-reachable */ +#define TREE_IN_2BLOSS 2 /* in T' and in a blossom, is s-reachable */ +#define TREE_IN_1 3 /* in T and is s-reachable */ + +#define TREE_IS_S_REACHABLE(X) (Tree[X] >= TREE_IN_2BLOSS) +#define TREE_IS_ON_SCANQ TREE_IS_S_REACHABLE +/* #define TREE_IS_ON_SCANQ(X) (Tree[X] != TREE_NOT_IN_M) */ +#define TREE_MARK(X, MARK) do{ if( Tree[X] < MARK ) Tree[X]=MARK; }while(0) + + +/***************************************************************************** + * store changes done to check whether an alternating path exists + * (see bSetBnsToCheckAltPath, bRestoreBnsAfterCheckAltPath) +******************************************************************************/ +typedef struct tagAltPathChanges +{ + /* caps changed in up to 2 vertices */ + VertexFlow nOldCapsVert[2][MAXVAL+1]; + Vertex vOldVert[2]; + S_CHAR bSetOldCapsVert[2]; /* number of caps to restore, including st-cap */ + /* save ids of the newly created temporary vertices */ + Vertex vNewVertex[2]; + S_CHAR bSetNew[2]; /* indicators whether to remove vertices */ +} ALT_PATH_CHANGES; + +/*****************************************************************************/ + +/* Local functions */ + +int RestoreRadicalsOnly( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at ); +int bRadChangesAtomType( BN_STRUCT *pBNS, BN_DATA *pBD, Vertex v, Vertex v_1, Vertex v_2 ); +int BnsAdjustFlowBondsRad( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at, int num_atoms ); +int SetAtomRadAndChemValFromVertexCapFlow( BN_STRUCT *pBNS, inp_ATOM *atom, int v1 ); +int bNeedToTestTheFlow( int bond_type, int nTestFlow, int bTestForNonStereoBond ); +int RestoreEdgeFlow( BNS_EDGE *edge, int delta, int bChangeFlow ); +int SetAtomBondType( BNS_EDGE *edge, U_CHAR *bond_type12, U_CHAR *bond_type21, int delta, int bChangeFlow ); +int RestoreBnStructFlow( BN_STRUCT *pBNS, int bChangeFlow ); +int CompTGroupNumber( const void *tg1, const void *tg2, void *p ); +int CompCGroupNumber( const void *cg1, const void *cg2, void *p ); + +/* Rings, Blocks, Non-stereo bonds */ +int ReInitBnStructForAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bUnknAltAsNoStereo ); +int MarkRingSystemsAltBns( BN_STRUCT* pBNS, int bUnknAltAsNoStereo ); +int MarkNonStereoAltBns( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bUnknAltAsNoStereo ); + +/* Called from BalancedNetworkSearch */ +int GetVertexDegree( BN_STRUCT* pBNS, Vertex v ); +/* Vertex Get2ndNeighbor1( BN_STRUCT* pBNS, Vertex u, EdgeIndex iedge ); not used */ +Vertex Get2ndEdgeVertex( BN_STRUCT* pBNS, Edge uv ); +Vertex GetVertexNeighbor( BN_STRUCT* pBNS, Vertex v, int neigh, EdgeIndex *iedge ); +int GetEdgePointer( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv, BNS_EDGE **uv, S_CHAR *s_or_t ); +int AugmentEdge( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv, int delta, S_CHAR bReverse, int bChangeFlow ); +int rescap_mark( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv ); +int rescap( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv ); +Vertex FindBase( Vertex u, Vertex *BasePtr ); +int FindPathToVertex_s( Vertex x, Edge *SwitchEdge, Vertex *BasePtr, Vertex *Path, int MaxPathLen ); +Vertex MakeBlossom( BN_STRUCT* pBNS, Vertex *ScanQ, int *pQSize, + Vertex *Pu, Vertex *Pv, int max_len_Pu_Pv, + Edge *SwitchEdge, Vertex *BasePtr, + Vertex u, Vertex v, EdgeIndex iuv, Vertex b_u, Vertex b_v, S_CHAR *Tree ); +int PullFlow( BN_STRUCT *pBNS, Edge *SwitchEdge, Vertex x, Vertex y, int delta, S_CHAR bReverse, int bChangeFlow ); +int FindPathCap( BN_STRUCT* pBNS, Edge *SwitchEdge, Vertex x, Vertex y, int delta ); + +/* +int SetBondType( BNS_EDGE *edge, U_CHAR *bond_type12, U_CHAR *bond_type21, int delta, int bChangeFlow ); +int SetBondsRestoreBnStructFlow( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bChangeFlow ); +*/ +int SetBondsFromBnStructFlow( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int bChangeFlow0 ); +int MarkAtomsAtTautGroups( BN_STRUCT *pBNS, int num_atoms, BN_AATG *pAATG, int nEnd1, int nEnd2 ); + +int nMinFlow2Check( BN_STRUCT *pBNS, int iedge ); +int nMaxFlow2Check( BN_STRUCT *pBNS, int iedge ); +int nCurFlow2Check( BN_STRUCT *pBNS, int iedge ); + +/* Bonds testing */ +/* +int bRestoreFlowToCheckOneBond( BN_STRUCT *pBNS, BNS_FLOW_CHANGES *fcd, int nTestFlow, inp_ATOM *at, int num_atoms, int bChangeFlow ); +*/ +int bSetFlowToCheckOneBond( BN_STRUCT *pBNS, int iedge, int flow, BNS_FLOW_CHANGES *fcd ); +int bRestoreFlowAfterCheckOneBond( BN_STRUCT *pBNS, BNS_FLOW_CHANGES *fcd ); +int bSetBondsAfterCheckOneBond( BN_STRUCT *pBNS, BNS_FLOW_CHANGES *fcd, int nTestFlow, inp_ATOM *at, int num_atoms, int bChangeFlow ); +int BnsTestAndMarkAltBonds( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at, int num_atoms, BNS_FLOW_CHANGES *fcd, int bChangeFlow, int nBondTypeToTest ); +int bIsAltBond( int bond_type ); + +/* Fix bonds */ +int fix_special_bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int edge_forbidden_mask ); +int TempFix_NH_NH_Bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms ); +int CorrectFixing_NH_NH_Bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms ); +int fix_explicitly_indicated_bonds( int nebend, int *ebend, BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms ); + +/* Alt path testing */ +int bSetBnsToCheckAltPath( BN_STRUCT *pBNS, int nVertDoubleBond, int nVertSingleBond, AT_NUMB type, + int path_type, ALT_PATH_CHANGES *apc, BNS_FLOW_CHANGES *fcd, int *nDots ); +int bRestoreBnsAfterCheckAltPath( BN_STRUCT *pBNS, ALT_PATH_CHANGES *apc, int bChangeFlow ); +Vertex GetGroupVertex(BN_STRUCT *pBNS, Vertex v1, AT_NUMB type); +BNS_IEDGE GetEdgeToGroupVertex( BN_STRUCT *pBNS, Vertex v1, AT_NUMB type); +int bAddNewVertex( BN_STRUCT *pBNS, int nVertDoubleBond, int nCap, int nFlow, int nMaxAdjEdges, int *nDots ); +int AddNewEdge( BNS_VERTEX *p1, BNS_VERTEX *p2, BN_STRUCT *pBNS, int nEdgeCap, int nEdgeFlow ); +int bAddStCapToAVertex( BN_STRUCT *pBNS, Vertex v1, Vertex v2, VertexFlow *nOldCapVertSingleBond, int *nDots, int bAdjacentDonors ); + +static void remove_alt_bond_marks(inp_ATOM *at, int num_atoms); +int bIsBnsEndpoint( BN_STRUCT *pBNS, int v ); + +/* Protons removal, charge neutralization */ +int is_acidic_CO( inp_ATOM *atom, int at_no ); +int mark_at_type( inp_ATOM *atom, int num_atoms, int nAtTypeTotals[] ); +int GetAtomChargeType( inp_ATOM *atom, int at_no, int nAtTypeTotals[], int *pMask, int bSubtract ); +int AddChangedAtHChargeBNS( inp_ATOM *at, int num_atoms, int nAtTypeTotals[], S_CHAR *mark ); +int EliminatePlusMinusChargeAmbiguity( BN_STRUCT *pBNS, int num_atoms ); +int AddOrRemoveExplOrImplH( int nDelta, inp_ATOM *at, int num_atoms, AT_NUMB at_no, T_GROUP_INFO *t_group_info ); +int SubtractOrChangeAtHChargeBNS( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, + int nAtTypeTotals[], S_CHAR *mark, T_GROUP_INFO *t_group_info, int bSubtract ); +int is_Z_atom( U_CHAR el_number ); +int IsZOX( inp_ATOM *atom, int at_x, int ord ); +int SimpleRemoveHplusNPO( inp_ATOM *at, int num_atoms, int nAtTypeTotals[], T_GROUP_INFO *t_group_info ); +int CreateCGroupInBnStruct( inp_ATOM *at, int num_atoms, + BN_STRUCT *pBNS, int nType, int nMask, int nCharge ); +int CreateTGroupInBnStruct( inp_ATOM *at, int num_atoms, + BN_STRUCT *pBNS, int nType, int nMask ); +int RemoveLastGroupFromBnStruct( inp_ATOM *at, int num_atoms, int tg, BN_STRUCT *pBNS ); +int SetInitCapFlowToCurrent( BN_STRUCT *pBNS ); +int SimpleRemoveAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2remove ); +int SimpleAddAcidicProtons( inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2add ); +int HardRemoveAcidicProtons( struct tagCANON_GLOBALS *pCG, inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2remove, + int *nNumCanceledCharges, BN_STRUCT *pBNS, BN_DATA *pBD ); +int HardAddAcidicProtons( struct tagCANON_GLOBALS *pCG, inp_ATOM *at, int num_atoms, BN_AATG *pAATG, int num2add, + int *nNumCanceledCharges, BN_STRUCT *pBNS, BN_DATA *pBD ); +int HardRemoveHplusNP( struct tagCANON_GLOBALS *pCG, inp_ATOM *at, int num_atoms, int bCancelChargesAlways, int *nNumCanceledCharges, + BN_AATG *pAATG, BN_STRUCT *pBNS, BN_DATA *pBD ); +int RemoveNPProtonsAndAcidCharges( struct tagCANON_GLOBALS *pCG, inp_ATOM *at, int num_atoms, BN_AATG *pAATG, BN_STRUCT *pBNS, BN_DATA *pBD ); +Vertex GetPrevVertex( BN_STRUCT* pBNS, Vertex y, Edge *SwitchEdge, EdgeIndex *iuv ); +int bIgnoreVertexNonTACN_atom( BN_STRUCT* pBNS, Vertex u, Vertex v ); +int bIgnoreVertexNonTACN_group( BN_STRUCT* pBNS, Vertex v, Vertex w, Edge *SwitchEdge ); +int bIsRemovedHfromNHaion( BN_STRUCT* pBNS, Vertex u, Vertex v ); +int bIsAggressiveDeprotonation( BN_STRUCT* pBNS, Vertex v, Vertex w, Edge *SwitchEdge ); + +int bIsAtomTypeHard( inp_ATOM *at, int endpoint, int nType, int nMask, int nCharge ); +int bIsHDonorAccAtomType( inp_ATOM *at, int endpoint, int *cSubType ); +int bIsNegAtomType( inp_ATOM *at, int i, int *cSubType ); + +#if ( BNS_RAD_SEARCH == 1 ) +int RegisterRadEndpoint( BN_STRUCT *pBNS, BN_DATA *pBD, Vertex u); +int cmp_rad_endpoints( const void *a1, const void *a2 ); +int cmp_endpoints_rad( const void *a1, const void *a2 ); +#endif + +int bHasChargedNeighbor( inp_ATOM *at, int iat ); +/*****************************************************************************/ +/**** prim(v) is v' *****/ +#define prim(v) (Vertex)((v)^1) + +/*****************************************************************************/ +#define SwitchEdge_Vert1(u) SwitchEdge[u][0] +#define SwitchEdge_Vert2(u) Get2ndEdgeVertex( pBNS, SwitchEdge[u] ) +#define SwitchEdge_IEdge(u) SwitchEdge[u][1] +/*****************************************************************************/ + + + + + +/* Returns value > 0 if a bond has been changed */ +int RestoreEdgeFlow( BNS_EDGE *edge, int delta, int bChangeFlow ) +{ + /*flow1 = edge->flow;*/ /* output from BNS */ + switch ( bChangeFlow & BNS_EF_CHNG_RSTR ) { + case 0: /* the flow has not been permitted to change inside the BNS */ + /* nothing to do */ + /*flow1 = edge->flow;*/ /* output from BNS, the original flow value */ + /*flow2 = flow1 + delta;*/ /* the flow would be changed to this value by the BNS if permitted */ + break; + case BNS_EF_CHNG_FLOW: /* the flow has been changed by the BNS; update flow0 */ + /*flow2 = edge->flow;*/ /* output from BNS, the changed value */ + /*flow1 = flow2 - delta;*/ /* the original flow value before the BNS */ + edge->flow0 = edge->flow; /* SAVE NEW EDGE FLOW AS THE INITIAL FLOW FROM CHEM. BONDS */ + break; + case BNS_EF_CHNG_RSTR: /* the flow has been changed by the BNS; requested to change it back */ + /*flow2 = edge->flow;*/ /* output from BNS, the changed value */ + /*flow1 = flow2 - delta;*/ /* the original flow value before the BNS */ + edge->flow = edge->flow-delta; /* CHANGE EDGE FLOW BACK (RESTORE) */ + break; + case BNS_EF_RSTR_FLOW: /* the flow has not been permitted to change inside the BNS */ + /* nothing to do */ + /*flow1 = edge->flow;*/ /* output from BNS, the original flow value */ + /*flow2 = flow1 + delta;*/ /* the flow would be changed to this value by the BNS if permitted */ + break; + } + + return 0; +} + + +/* Returns value > 0 if a bond has been changed; do not change flow */ +int SetAtomBondType( BNS_EDGE *edge, + U_CHAR *bond_type12, + U_CHAR *bond_type21, + int delta, int bChangeFlow ) +{ + int flow1, flow2, tmp, ret = 0; + int bond_mark, bond_type, new_bond_type; + + if ( !edge->pass || !bond_type21 ) + return 0; + + switch ( bChangeFlow & BNS_EF_CHNG_RSTR ) { + case 0: /* the flow has not been permitted to change inside the BNS: simulated in case of check one bond */ + case BNS_EF_RSTR_FLOW: /* the flow has not been permitted to change inside the BNS: obsolete mode, unexpected bChangeFlow */ + flow1 = edge->flow0; /* output from BNS, the original (old) flow value */ + flow2 = flow1 + delta; /* the flow would be changed to this value by the BNS if permitted */ + break; + case BNS_EF_CHNG_FLOW: /* the flow has been changed by the BNS */ + case BNS_EF_CHNG_RSTR: /* the flow has been changed by the BNS; requested to change it back */ + flow2 = edge->flow; /* output from BNS, the changed (new) value */ + flow1 = edge->flow0; /* the original flow (old) value before the BNS */ + break; + default: + return 0; /* added 2006-03-21 */ + } + + if ( (bChangeFlow & BNS_EF_CHNG_BONDS) && (bChangeFlow & BNS_EF_ALTR_NS) !=BNS_EF_ALTR_NS ) { + /* set new bond types according to the new flow values */ + new_bond_type = flow2+BOND_SINGLE; + if ( *bond_type12 != new_bond_type ) { + *bond_type12 = *bond_type21 = new_bond_type; + ret ++; + } + } else + if ( bChangeFlow & BNS_EF_ALTR_BONDS ) { + if ( flow1 == flow2 ) { + goto exit_function; + } + /* update alternating bond information */ + if ( flow1 > flow2 ) { + /* make sure flow2 > flow1 */ + tmp = flow1; + flow1 = flow2; + flow2 = tmp; + } + bond_mark = 0; + switch( bond_type = (*bond_type12 & BOND_TYPE_MASK) ) { + + case BOND_SINGLE: + case BOND_DOUBLE: + case BOND_TRIPLE: + /* assume that the input bond type fits either flow1 or flow2 */ + if ( flow1 == 0 && flow2 == 1 ) { + if ( bChangeFlow & BNS_EF_SET_NOSTEREO ) { + bond_mark = BOND_MARK_ALT12NS; + bond_type = BOND_ALT12NS; + } else { + bond_mark = BOND_MARK_ALT12; + bond_type = BOND_ALTERN; + } + } else + if ( flow1 == 0 && flow2 == 2 ) { + bond_mark = BOND_MARK_ALT13; + bond_type = BOND_ALT_13; + } else + if ( flow1 == 1 && flow2 == 2 ) { + bond_mark = BOND_MARK_ALT23; + bond_type = BOND_ALT_23; + } else { + return BNS_BOND_ERR; /* error */ + } + break; + case BOND_TAUTOM: + if ( flow1 == 0 && flow2 == 1 ) { + bond_mark = BOND_MARK_ALT12NS; + } else { + return BNS_BOND_ERR; /* error */ + } + break; + + default: + new_bond_type = bond_type; + bond_mark = (*bond_type12 & BOND_MARK_MASK); + switch( bond_mark ) { + case BOND_MARK_ALT12: + if ( (bChangeFlow & BNS_EF_SET_NOSTEREO) && flow1 == 0 && flow2 == 1 ) { + bond_mark = BOND_MARK_ALT12NS; + new_bond_type = BOND_ALT12NS; + break; + } + case BOND_MARK_ALT12NS: + if ( flow1 == 2 || flow2 == 2 ) { + bond_mark = BOND_MARK_ALT123; + new_bond_type = BOND_ALT_123; + } + break; + case BOND_MARK_ALT13: + if ( flow1 == 1 || flow2 == 1 ) { + bond_mark = BOND_MARK_ALT123; + new_bond_type = BOND_ALT_123; + } + break; + case BOND_MARK_ALT23: + if ( flow1 == 0 || flow2 == 0 ) { + bond_mark = BOND_MARK_ALT123; + new_bond_type = BOND_ALT_123; + } + break; + case BOND_MARK_ALT123: + break; + + case 0: /* special case: second alt bond testing */ + if ( flow1 == 0 && flow2 == 1 ) { + bond_mark = BOND_MARK_ALT12; + } else + if ( flow1 == 0 && flow2 == 2 ) { + bond_mark = BOND_MARK_ALT13; + } else + if ( flow1 == 1 && flow2 == 2 ) { + bond_mark = BOND_MARK_ALT23; + } else { + return BNS_BOND_ERR; /* error */ + } + break; + + + default: + return BNS_BOND_ERR; /* error */ + } + switch( bond_type ) { + case BOND_TAUTOM: + break; + case BOND_ALTERN: + case BOND_ALT12NS: + case BOND_ALT_123: + case BOND_ALT_13: + case BOND_ALT_23: + bond_type = new_bond_type; + break; + default: + return BNS_BOND_ERR; /* error */ + } + } + new_bond_type = bond_type | bond_mark; + if ( new_bond_type != *bond_type12 ) { + *bond_type12 = *bond_type21 = new_bond_type; + ret ++; + } + } +exit_function: + return ret; +} + + +/* + Run BalancedNetworkSearch( ... ) until no aug pass is found +*/ +int RunBalancedNetworkSearch( BN_STRUCT *pBNS, BN_DATA *pBD, int bChangeFlow ) +{ + int pass, delta=0, nSumDelta; + + nSumDelta = 0; + for ( pass=0; passmax_altp; pass++ ) + { + pBNS->alt_path = pBNS->altp[pass]; + pBNS->bChangeFlow = 0; + delta = BalancedNetworkSearch( pBNS, pBD, bChangeFlow ); + ReInitBnData( pBD ); + if ( 0 < delta ) + { + pBNS->num_altp ++; + nSumDelta += abs( delta ); + } + else + { + break; + } + } + + if ( IS_BNS_ERROR(delta) ) + return delta; + + if ( bInchiTimeIsOver( pBNS->ic, pBNS->ulTimeOutTime) ) + { + return + BNS_TIMEOUT; + } + + return nSumDelta; /* number of eliminated pairs of "dots" */ +} + + +int SetAtomRadAndChemValFromVertexCapFlow( BN_STRUCT *pBNS, inp_ATOM *atom, int v1 ) +{ + BNS_VERTEX *vert = pBNS->vert + v1; + inp_ATOM *at = atom + v1; + S_CHAR cValue; + int nChanges = 0; + /* set only on the 1st pass */ + if ( !vert->st_edge.pass ) { + return 0; + } + /* adjust chem_bonds_valence */ + cValue = at->chem_bonds_valence - at->valence; + if ( cValue >= 0 && cValue != vert->st_edge.flow ) { + at->chem_bonds_valence = at->valence + vert->st_edge.flow; + nChanges ++; + } + /* adjast radical */ + switch ( vert->st_edge.cap - vert->st_edge.flow ) { + case 0: + cValue = 0; + break; + case 1: + cValue = RADICAL_DOUBLET; + break; + case 2: + cValue = RADICAL_TRIPLET; + break; + default: + return BNS_BOND_ERR; + } + if ( cValue != at->radical ) { + at->radical = cValue; + nChanges ++; + } + + return nChanges; +} + + +int AddChangedAtHChargeBNS( inp_ATOM *at, + int num_atoms, + int nAtTypeTotals[], + S_CHAR *mark ) +{ + int i, mask, num; + for ( i = 0, num = 0; i < num_atoms; i ++ ) { + if ( mark[i] ) { + mark[i] = 0; +#if ( FIX_NORM_BUG_ADD_ION_PAIR == 1 ) + /* add ignoring adjacent charges */ + at[i].at_type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, -2 ); +#else + at[i].at_type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); +#endif + num ++; + } + } + return num; +} + + +/* Eliminate neutral representation ambiguity: + + replace (+)--N==(-) with (+)==N--(-) + + here (+) is positive charge group, (-) is negative charge group, N is N or P + This reduces possibility of creating ion pair -OH => -O(+) + H(+) + instead of removing H(+) from N or P + ! Call this function after alt path was found and new flows have been set. + +*/ +int EliminatePlusMinusChargeAmbiguity( BN_STRUCT *pBNS, int num_atoms ) +{ + int pass, i, v0, v1, v2, ineigh1, /*ineigh0,*/ /*ineigh2,*/ + vLast, n, delta, ret, err = 0; + int nFound, k; + BNS_EDGE *edge; + + for ( pass = pBNS->num_altp-1, ret = 0; 0 <= pass; pass -- ) { + + pBNS->alt_path = pBNS->altp[pass]; + v1 = ALTP_START_ATOM(pBNS->alt_path); + n = ALTP_PATH_LEN(pBNS->alt_path); + delta = ALTP_DELTA(pBNS->alt_path); + vLast = ALTP_END_ATOM(pBNS->alt_path); + v0 = v2 = NO_VERTEX; /* negative number */ + + for ( i = 0; i < n; i ++, delta = -delta, v0 = v1, v1 = v2 /*, ineigh0 = ineigh1*/ ) { + ineigh1 = ALTP_THIS_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v1->v2 neighbor */ + /*ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i);*/ /* v2->v1 neighbor */ + edge = pBNS->edge + pBNS->vert[v1].iedge[ineigh1]; + /* follow the BN Structure, not the inp_ATOM, to take care of swithching to + t-groups, c-groups or other fictitious edges/vertices + */ + v2 = edge->neighbor12 ^ v1; + if ( v1 < num_atoms && + ( v0 >= num_atoms && ( pBNS->vert[v0].type & BNS_VERT_TYPE_C_GROUP ) || + v2 >= num_atoms && ( pBNS->vert[v2].type & BNS_VERT_TYPE_C_GROUP ) ) ) + { + int cgPos=0, cgNeg=0; + int neighPos = -1, neighNeg = -1; + BNS_EDGE *edgePos, *edgeNeg; + nFound = 0; + for ( k = pBNS->vert[v1].num_adj_edges-1; k >= 0 && (neighPos < 0 || neighNeg < 0); k -- ) + { + BNS_EDGE *next_edge = pBNS->edge + pBNS->vert[v1].iedge[k]; + int v = next_edge->neighbor12 ^ v1; + if ( pBNS->vert[v].type & BNS_VERT_TYPE_C_GROUP ) + { + if ( pBNS->vert[v].type & BNS_VERT_TYPE_C_NEGATIVE ) + { + cgNeg = v; + neighNeg = k; + nFound ++; + } + else + { + cgPos = v; + neighPos = k; + nFound ++; + } + } + } + if ( 2 == nFound && neighPos >= 0 && neighNeg >= 0 ) { + /* both c-groups have been found */ + edgePos = pBNS->edge + pBNS->vert[v1].iedge[neighPos]; + edgeNeg = pBNS->edge + pBNS->vert[v1].iedge[neighNeg]; + if ( edgePos->flow < edgeNeg->flow ) { + /* ambiguity found; replace (+cg)--N==(-cg) with (+cg)==N--(-cg) */ + int dflow = edgeNeg->flow - edgePos->flow; + + edgePos->flow += dflow; + pBNS->vert[cgPos].st_edge.cap += dflow; + pBNS->vert[cgPos].st_edge.flow += dflow; + + edgeNeg->flow -= dflow; + pBNS->vert[cgNeg].st_edge.cap -= dflow; + pBNS->vert[cgNeg].st_edge.flow -= dflow; + ret ++; + } + } + } + } + + if ( v2 != vLast ) { + err = BNS_PROGRAM_ERR; + } + } + return err? err : ret; +} + + +/* + Add or remove ixplicit or implicit hydrogens +*/ +int AddOrRemoveExplOrImplH( int nDelta, + inp_ATOM *at, + int num_atoms, + AT_NUMB at_no, + T_GROUP_INFO *t_group_info ) +{ + int i, iso, tot_num_iso_H, + num_H, /* number of H before the removal, including explicit H */ + nNum2Remove, /*number of H to remove */ + nNumRemovedExplicitH, + nNumExplicit2Implicit; + S_CHAR num_iso_H[NUM_H_ISOTOPES]; + inp_ATOM *at_H; + + if ( !nDelta ) { + return 0; + } + /* add */ + if ( nDelta > 0 ) { + at[at_no].num_H += nDelta; + t_group_info->tni.nNumRemovedProtons --; + return nDelta; + } + /* remove */ + nNum2Remove = -nDelta; + nNumRemovedExplicitH = t_group_info->tni.nNumRemovedExplicitH; /* number of explicit H saved separately in + at[num_atoms+i], i=0..nNumRemovedExplicitH-1 */ + tot_num_iso_H = NUM_ISO_H(at,at_no); + num_H = at[at_no].num_H; + /* + tot_num_iso_H = NUM_ISO_H(at,at_no); + num_H = at[at_no].num_H; + nNumAtomExplicitH = 0; + nNumRemovedExplicitH = t_group_info->tni.nNumRemovedExplicitH; + tot_num_explicit_iso_H = 0; + */ + at_H = at + num_atoms; + memcpy( num_iso_H, at[at_no].num_iso_H, sizeof(num_iso_H)); + /* Remove all explicit H, otherwise a false stereo can occur. + Example: remove H(+) from the following substructure: + + H H + A / A / + >X==N(+) produces false stereogenic bond: >X==N + B \ B + H + + To avoid this effect all explicit H atoms must be removed + */ + nNumExplicit2Implicit = 0; + for ( i = 0; i < nNumRemovedExplicitH; ) { + if ( at_H[i].neighbor[0] == at_no ) { + int m, k, orig_no = at_H[i].orig_at_number; + nNumRemovedExplicitH --; + nNumExplicit2Implicit ++; + if ( nNumRemovedExplicitH > i ) { + inp_ATOM at_i = at_H[i]; + memmove( at_H+i, at_H+i+1, sizeof(at_H[0])*(nNumRemovedExplicitH-i) ); + at_H[nNumRemovedExplicitH] = at_i; /* save removed H (for debugging purposes?) */ + } + /* adjust 0D parities */ + if ( at[at_no].sb_parity[0] ) { + for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[at_no].sb_parity[m]; m ++ ) { + if ( at[at_no].sn_orig_at_num[m] == orig_no ) { + if ( at[at_no].valence >= MIN_NUM_STEREO_BOND_NEIGH ) { + at[at_no].sn_ord[m] = k = (at[at_no].sb_ord[m]==0); + at[at_no].sn_orig_at_num[m] = at[(int)at[at_no].neighbor[k]].orig_at_number; + if ( ATOM_PARITY_WELL_DEF( at[at_no].sb_parity[m] ) ) { + at[at_no].sb_parity[m] = 3 - at[at_no].sb_parity[m]; + } + } else { + at[at_no].sn_ord[m] = -99; /* no sb neighbor exists anymore */ + at[at_no].sn_orig_at_num[m] = 0; + if ( ATOM_PARITY_WELL_DEF( at[at_no].sb_parity[m] ) ) { + int pnxt_atom, pinxt2cur, pinxt_sb_parity_ord; + if ( 0 < get_opposite_sb_atom( at, at_no, at[at_no].sb_ord[m], + &pnxt_atom, &pinxt2cur, &pinxt_sb_parity_ord ) ) { + at[at_no].sb_parity[m] = + at[pnxt_atom].sb_parity[pinxt_sb_parity_ord] = AB_PARITY_UNDF; + } + } + } + } + } + } + /* do not increment i here: we have shifted next at_H[] element + to the ith position and decremented nNumRemovedExplicitH */ + } else { + i ++; + } + } + + for ( iso = -1; iso < NUM_H_ISOTOPES && 0 < nNum2Remove; iso ++ ) { + /* each pass removes up to one H */ + if ( iso < 0 ) { + /* try to remove non-isotopic */ + while ( tot_num_iso_H < num_H && 0 < nNum2Remove ) { + /* non-isotopic H exists */ + num_H --; + t_group_info->tni.nNumRemovedProtons ++; + nNum2Remove --; + } + } else { + /* remove isotopic */ + while ( num_iso_H[iso] && num_H && 0 < nNum2Remove ) { + /* isotopic H exists */ + num_H --; + num_iso_H[iso] --; + t_group_info->tni.nNumRemovedProtonsIsotopic[iso] ++; + t_group_info->tni.nNumRemovedProtons ++; + nNum2Remove --; + } + } + } +#if ( bRELEASE_VERSION != 1 ) + if ( nNum2Remove ) { + int stop = 1; /* Program error */ + } +#endif + if ( nDelta + nNum2Remove < 0 ) { + at[at_no].num_H = num_H; + memcpy( at[at_no].num_iso_H, num_iso_H, sizeof(at[0].num_iso_H)); + t_group_info->tni.nNumRemovedExplicitH = nNumRemovedExplicitH; + } + return nDelta + nNum2Remove; +} + + +int SubtractOrChangeAtHChargeBNS( BN_STRUCT *pBNS, + inp_ATOM *at, + int num_atoms, + int nAtTypeTotals[], + S_CHAR *mark, + T_GROUP_INFO *t_group_info, + int bSubtract ) +{ + int pass, i, v0, v1, v2, ineigh1, /*ineigh2,*/ vLast, n, delta, ret, err = 0; + BNS_EDGE *edge; + int nDeltaH, nDeltaCharge; + int mask, type; + + for ( pass = pBNS->num_altp-1, ret = 0; 0 <= pass; pass -- ) { + + pBNS->alt_path = pBNS->altp[pass]; + v1 = ALTP_START_ATOM(pBNS->alt_path); + n = ALTP_PATH_LEN(pBNS->alt_path); + delta = ALTP_DELTA(pBNS->alt_path); + vLast = ALTP_END_ATOM(pBNS->alt_path); + v0 = v2 = NO_VERTEX; + + for ( i = 0; i < n; i ++, delta = -delta, v0 = v1, v1 = v2 ) { + ineigh1 = ALTP_THIS_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v1->v2 neighbor */ + /*ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i);*/ /* v2->v1 neighbor */ + edge = pBNS->edge + pBNS->vert[v1].iedge[ineigh1]; + /* follow the BN Structure, not the inp_ATOM, to take care of swithching to + t-groups, c-groups or other fictitious edges/vertices + */ + v2 = edge->neighbor12 ^ v1; + if ( v1 < num_atoms && (v0 >= num_atoms || v2 >= num_atoms) ) { + nDeltaH = nDeltaCharge = 0; + if ( v0 >= num_atoms ) { + /* delta(v0-v1) = -delta(v1-v2) along the alternating path */ + if ( pBNS->vert[v0].type & BNS_VERT_TYPE_TGROUP ) { + nDeltaH -= delta; + } else + if ( pBNS->vert[v0].type & BNS_VERT_TYPE_C_GROUP ) { + nDeltaCharge += delta; + } + } + if ( v2 >= num_atoms ) { + if ( pBNS->vert[v2].type & BNS_VERT_TYPE_TGROUP ) { + nDeltaH += delta; + } else + if ( pBNS->vert[v2].type & BNS_VERT_TYPE_C_GROUP ) { + nDeltaCharge -= delta; + } + } + if ( nDeltaH || nDeltaCharge ) { + if ( bSubtract ) { + if ( !mark[v1] ) { + /* first time the atom has been encountered: subtract */ +#if ( FIX_NORM_BUG_ADD_ION_PAIR == 1 ) + type = GetAtomChargeType( at, v1, nAtTypeTotals, &mask, 2 ); +#else + type = GetAtomChargeType( at, v1, nAtTypeTotals, &mask, 1 ); +#endif + ret ++; /* number of changed atoms */ + mark[v1] ++; + } + } else { /* Change */ + at[v1].charge += nDeltaCharge; + if ( nDeltaH ) { + AddOrRemoveExplOrImplH( nDeltaH, at, num_atoms, (AT_NUMB)v1, t_group_info ); + } + ret ++; /* number of changed atoms */ + } + } + } + } + + if ( v2 != vLast ) { + err = BNS_PROGRAM_ERR; + } + } + return err? err : ret; +} + + +/* + Set Bonds From BnS Struct Flow +*/ +int SetBondsFromBnStructFlow( BN_STRUCT *pBNS, + inp_ATOM *at, + int num_atoms, + int bChangeFlow0 ) +{ + int pass, i, v0, v1, v2, ineigh1, ineigh2, vLast, n, delta, ret, ret_val, err = 0; + BNS_EDGE *edge; + int bMovingRad = 0, bChangeFlowAdd; + int bChangeFlow = (bChangeFlow0 & ~BNS_EF_SET_NOSTEREO); + /* + bCheckMovingRad = (bChangeFlow & BNS_EF_ALTR_NS) == BNS_EF_ALTR_NS && + pBNS->tot_st_cap > pBNS->tot_st_flow; + */ + for ( pass = pBNS->num_altp-1, ret = 0; 0 <= pass; pass -- ) + { + pBNS->alt_path = pBNS->altp[pass]; + v1 = ALTP_START_ATOM(pBNS->alt_path); + n = ALTP_PATH_LEN(pBNS->alt_path); + delta = ALTP_DELTA(pBNS->alt_path); + vLast = ALTP_END_ATOM(pBNS->alt_path); + if ( (bChangeFlow0 & BNS_EF_SET_NOSTEREO) && + (pBNS->vert[v1].st_edge.cap0 > pBNS->vert[v1].st_edge.flow0 || + pBNS->vert[vLast].st_edge.cap0 > pBNS->vert[vLast].st_edge.flow0 ) ) { + bMovingRad ++; + bChangeFlowAdd = BNS_EF_SET_NOSTEREO; + ret |= 2; + } else { + bChangeFlowAdd = 0; + } + /* start vertex */ + if ( (bChangeFlow & BNS_EF_CHNG_RSTR) == BNS_EF_CHNG_RSTR) { + /* restore s-v1 edge flow to the BNS this pass input value */ + ; /*pBNS->vert[v1].st_edge.flow -= delta;*/ + } else + if ( (bChangeFlow & BNS_EF_SAVE_ALL) == BNS_EF_SAVE_ALL ) { + if ( v1 < num_atoms ) { + /* will produce wrong result if called for v1 next time? */ + ret_val = SetAtomRadAndChemValFromVertexCapFlow( pBNS, at, v1 ); + if ( ret_val < 0 ) { + err = BNS_PROGRAM_ERR; + } else { + ret |= (ret_val > 0); + } + } + /*pBNS->vert[v1].st_edge.flow0 = pBNS->vert[v1].st_edge.flow;*/ + } + pBNS->vert[v1].st_edge.pass = 0; + + v0 = v2 = NO_VERTEX; + for ( i = 0; i < n; i ++, delta = -delta, v0 = v1, v1 = v2 ) { + ineigh1 = ALTP_THIS_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v1->v2 neighbor */ + ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v2->v1 neighbor */ + edge = pBNS->edge + pBNS->vert[v1].iedge[ineigh1]; + /* follow the BN Structure, not the inp_ATOM, to take care of swithching to + t-groups, c-groups or other fictitious edges/vertices + */ + + v2 = edge->neighbor12 ^ v1; + + /* change at->chem_bonds_valence 2004-03-08 */ + if ( (bChangeFlow & BNS_EF_CHNG_BONDS) && v1 < num_atoms ) { + if ( v0 >= num_atoms && v2 < num_atoms ) { + at[v1].chem_bonds_valence += delta; /* change in v1-v2 bond order */ + } else + if ( v0 < num_atoms && v2 >= num_atoms && v0 != NO_VERTEX ) { + at[v1].chem_bonds_valence -= delta; /* change in v0-v1 bond order */ + } + } + + if ( !edge->pass ) + continue; + + if ( v1 < num_atoms && ineigh1 < at[v1].valence && + v2 < num_atoms && ineigh2 < at[v2].valence ) { + if ( (bChangeFlow0 & BNS_EF_ALTR_NS )==BNS_EF_ALTR_NS && + (bChangeFlow0 & BNS_EF_SAVE_ALL)==BNS_EF_SAVE_ALL ) { + /* 2004-07-02 special mode: save new ring bonds and mark as non-stereo non-ring bonds */ + if ( at[v1].nRingSystem != at[v2].nRingSystem ) { + /* non-ring bond (bridge) */ + bChangeFlowAdd = BNS_EF_ALTR_NS; + } else { + /* ring bond */ + bChangeFlowAdd = 0; + } + } + /* change bonds on the first pass only: in this case all flow correspond to the BNS output */ + ret_val = SetAtomBondType( edge, &at[v1].bond_type[ineigh1], &at[v2].bond_type[ineigh2], delta, bChangeFlow | bChangeFlowAdd ); + if ( ret_val < 0 ) { + err = BNS_PROGRAM_ERR; + } else { + ret |= (ret_val > 0); + } + } + edge->pass = 0; + } + + if ( v2 != vLast ) { + err = BNS_PROGRAM_ERR; + } else + if ( (bChangeFlow & BNS_EF_CHNG_RSTR) == BNS_EF_CHNG_RSTR) { + /* restore v2-t edge flow to the BNS this pass input value */ + /* "+=" instead of "-=" explanation: delta must have same sign as at the last edge */ + ; /*pBNS->vert[v2].st_edge.flow += delta; */ + } else + if ( (bChangeFlow & BNS_EF_SAVE_ALL) == BNS_EF_SAVE_ALL ) { + if ( v2 < num_atoms ) { + ret_val = SetAtomRadAndChemValFromVertexCapFlow( pBNS, at, v2 ); + if ( ret_val < 0 ) { + err = BNS_PROGRAM_ERR; + } else { + ret |= (ret_val > 0); + } + } + /*pBNS->vert[v2].st_edge.flow0 = pBNS->vert[v2].st_edge.flow;*/ + } + pBNS->vert[v2].st_edge.pass = 0; + } + return err? err : ret; +} + + +int MarkAtomsAtTautGroups( BN_STRUCT *pBNS, + int num_atoms, + BN_AATG *pAATG, + int nEnd1, + int nEnd2 ) +{ + int pass, i, j, v1, v2, ineigh1, ineigh2, vLast, vFirst, n, delta, err = 0; + BNS_EDGE *edge; + S_CHAR cDelta[MAX_ALT_AATG_ARRAY_LEN]; + AT_NUMB nVertex[MAX_ALT_AATG_ARRAY_LEN]; + int nLenDelta = 0, last_i, nNumFound; + + for ( pass = pBNS->num_altp-1; 0 <= pass; pass -- ) { + pBNS->alt_path = pBNS->altp[pass]; + vFirst = + v1 = ALTP_START_ATOM(pBNS->alt_path); + n = ALTP_PATH_LEN(pBNS->alt_path); + delta = ALTP_DELTA(pBNS->alt_path); + vLast = ALTP_END_ATOM(pBNS->alt_path); + v2 = NO_VERTEX; + pAATG->nNumFound = 0; /* initialize */ + + if ( nEnd1 != vFirst && nEnd1 != vLast ) { + nEnd1 = -1; /* really not the end */ + } + if ( nEnd2 != vFirst && nEnd2 != vLast ) { + nEnd2 = -1; /* really not the end */ + } + + for ( i = 0; i < n; i ++, delta = -delta, v1 = v2 ) { + ineigh1 = ALTP_THIS_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v1->v2 neighbor */ + ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v2->v1 neighbor */ + edge = pBNS->edge + pBNS->vert[v1].iedge[ineigh1]; + /* follow the BN Structure, not the inp_ATOM, to take care of swithching to + t-groups, c-groups or other fictitious edges/vertices + */ + v2 = edge->neighbor12 ^ v1; + /* + if ( v1 < num_atoms && v2 < num_atoms ) { + continue; + } + */ + /* BNS increased edge flow by delta */ + if ( v1 >= num_atoms && + ((pBNS->vert[v1].type & BNS_VERT_TYPE_TGROUP)||(pBNS->vert[v1].type & BNS_VERT_TYPE_TEMP)) && + 0 <= v2 && v2 < num_atoms && (pBNS->vert[v2].type & BNS_VERT_TYPE_ATOM ) ) { + /* + if ( !(pAATG->nMarkedAtom[v2] & AATG_MARK_IN_PATH) ) { + pAATG->nMarkedAtom[v2] |= AATG_MARK_IN_PATH; + pAATG->nNumFound ++; + } + */ + /* BNS increased bond order in v1(t-group)-v2(atom) by delta: added delta attachments */ + if ( nLenDelta < MAX_ALT_AATG_ARRAY_LEN ) { + cDelta[nLenDelta] = delta; + nVertex[nLenDelta] = v2; + nLenDelta ++; + } + } else + if ( v2 >= num_atoms && + ((pBNS->vert[v2].type & BNS_VERT_TYPE_TGROUP)||(pBNS->vert[v2].type & BNS_VERT_TYPE_TEMP)) && + 0 <= v1 && v1 < num_atoms && (pBNS->vert[v1].type & BNS_VERT_TYPE_ATOM ) ) { + /* + if ( !(pAATG->nMarkedAtom[v1] & AATG_MARK_IN_PATH) ) { + pAATG->nMarkedAtom[v1] |= AATG_MARK_IN_PATH; + pAATG->nNumFound ++; + } + */ + /* BNS increased bond order in v1(atom)-v2(t-group) by delta: added delta attachments */ + if ( nLenDelta < MAX_ALT_AATG_ARRAY_LEN ) { + cDelta[nLenDelta] = delta; + nVertex[nLenDelta] = v1; + nLenDelta ++; + } + } else + /* special case when the testing 'dot' was placed on an atom (should be nEnd1 only) */ + if ( 0 <= v1 && v1 == nEnd1 || v1 == nEnd2 && 0 <= v2 && v2 < num_atoms ) { + if ( nLenDelta < MAX_ALT_AATG_ARRAY_LEN ) { + cDelta[nLenDelta] = -delta; + nVertex[nLenDelta] = v1; + nLenDelta ++; + } + } else + if ( 0 <= v2 && v2 == nEnd1 || v2 == nEnd2 && 0 <= v1 && v1 < num_atoms ) { + if ( nLenDelta < MAX_ALT_AATG_ARRAY_LEN ) { + cDelta[nLenDelta] = -delta; + nVertex[nLenDelta] = v2; + nLenDelta ++; + } + } + } + + if ( v2 != vLast ) { + err = BNS_PROGRAM_ERR; + } else { + last_i = -1; + nNumFound = 0; + /* first run */ + for ( i = 1, j = 0; i < nLenDelta; j = i ++ ) { + /* ignore sequences (-1,+1) and (+1,-1) in cDelta[] because they */ + /* describe ordinary aug. paths of moving a single attachment */ + /* we are looking for aug. paths describing movement of 2 or more */ + if ( cDelta[j] > 0 && cDelta[i] > 0 || + cDelta[j] < 0 && cDelta[i] < 0 ) { + if ( j == last_i ) { + /* three attachments moved */ + return 0; + } + v1 = nVertex[j]; + if ( !(pAATG->nMarkedAtom[v1] & AATG_MARK_IN_PATH) ) { + nNumFound ++; + } + v2 = nVertex[i]; + if ( !(pAATG->nMarkedAtom[v2] & AATG_MARK_IN_PATH) ) { + nNumFound ++; + } + last_i = i; + } + } + if ( !nNumFound ) { + return 0; + } + if ( nNumFound > 4 ) { + return 0; + } + if ( nNumFound < 4 ) { + return 0; + } + /* second run */ + for ( i = 1, j = 0; i < nLenDelta; j = i ++ ) { + /* ignore sequences (-1,+1) and (+1,-1) in cDelta[] because they */ + /* describe ordinary aug. paths of moving a single attachment */ + /* we are looking for aug. paths describing movement of 2 or more */ + if ( cDelta[j] > 0 && cDelta[i] > 0 || + cDelta[j] < 0 && cDelta[i] < 0 ) { + v1 = nVertex[i-1]; + if ( !(pAATG->nMarkedAtom[v1] & AATG_MARK_IN_PATH) ) { + pAATG->nMarkedAtom[v1] |= AATG_MARK_IN_PATH; + pAATG->nNumFound ++; + } + v2 = nVertex[i]; + if ( !(pAATG->nMarkedAtom[v2] & AATG_MARK_IN_PATH) ) { + pAATG->nMarkedAtom[v2] |= AATG_MARK_IN_PATH; + pAATG->nNumFound ++; + } + } + } + } + } + return err? err : pAATG->nNumFound; +} + + +int RestoreBnStructFlow( BN_STRUCT *pBNS, int bChangeFlow ) +{ + int pass, i, v1, v2, ineigh1, ineigh2, vLast, n, delta, ret, err = 0; + BNS_EDGE *edge; + + for ( pass = pBNS->num_altp - 1, ret = 0; 0 <= pass; pass -- ) { + pBNS->alt_path = pBNS->altp[pass]; + v1 = ALTP_START_ATOM(pBNS->alt_path); + n = ALTP_PATH_LEN(pBNS->alt_path); + delta = ALTP_DELTA(pBNS->alt_path); + vLast = ALTP_END_ATOM(pBNS->alt_path); + v2 = NO_VERTEX; + /* starting vertex */ + if ( (bChangeFlow & BNS_EF_CHNG_RSTR) == BNS_EF_CHNG_RSTR) { + pBNS->vert[v1].st_edge.flow -= delta; /* restore s-v1 edge flow to the BNS input value */ + } else + if ( (bChangeFlow & BNS_EF_SAVE_ALL) == BNS_EF_SAVE_ALL ) { + pBNS->vert[v1].st_edge.flow0 = pBNS->vert[v1].st_edge.flow; + } + /* augmenting path edges */ + for ( i = 0; i < n; i ++, delta = -delta, v1 = v2 ) { + ineigh1 = ALTP_THIS_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v1->v2 neighbor */ + ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v2->v1 neighbor */ + edge = pBNS->edge + pBNS->vert[v1].iedge[ineigh1]; + v2 = edge->neighbor12 ^ v1; + RestoreEdgeFlow( edge, delta, bChangeFlow ); + edge->pass = 0; + } + /* ending vertex */ + if ( v2 != vLast ) { + err = BNS_PROGRAM_ERR; + } else + if ( (bChangeFlow & BNS_EF_CHNG_RSTR) == BNS_EF_CHNG_RSTR) { + /* restore v2-t edge flow to the original value */ + /* "+=" instead of "-=" explanation: delta must have same sign as at the last edge */ + pBNS->vert[v2].st_edge.flow += delta; + } else + if ( (bChangeFlow & BNS_EF_SAVE_ALL) == BNS_EF_SAVE_ALL ) { + pBNS->vert[v2].st_edge.flow0 = pBNS->vert[v2].st_edge.flow; + } + } + return err? err : ret; +} + + +int bNeedToTestTheFlow( int bond_type, int nTestFlow, int bTestForNonStereoBond ) +{ + int nBondType = ( BOND_TYPE_MASK & bond_type ); + int nBondAttrib = ( BOND_MARK_MASK & bond_type ); + + if ( bTestForNonStereoBond ) { + if ( nBondAttrib || nBondType == BOND_ALTERN || nBondType == BOND_ALT12NS ) { + switch( nTestFlow ) { + case 0: /* single: can be 1 (single)? */ + if ( nBondAttrib == BOND_MARK_ALT12NS|| + nBondAttrib == BOND_MARK_ALT123 || + nBondAttrib == BOND_MARK_ALT13 ) { + return 0; /* yes, already checked */ + } + break; + + case 1: /* double: can be 2 (double)? */ + if ( nBondAttrib == BOND_MARK_ALT12NS|| + nBondAttrib == BOND_MARK_ALT123 || + nBondAttrib == BOND_MARK_ALT23 ) { + return 0; /* yes, already checked */ + } + break; + case 2: /* triple: can be 3 (triple)? */ + if ( nBondAttrib == BOND_MARK_ALT13 || + nBondAttrib == BOND_MARK_ALT123 || + nBondAttrib == BOND_MARK_ALT23 ) { + return 0; /* yes, already checked */ + } + break; + } + } + } else { + if ( nBondAttrib || nBondType == BOND_ALTERN || nBondType == BOND_ALT12NS ) { + switch( nTestFlow ) { + case 0: /* single: can be 1 (single)? */ + if ( nBondAttrib == BOND_MARK_ALT12 || + nBondAttrib == BOND_MARK_ALT12NS|| + nBondAttrib == BOND_MARK_ALT123 || + nBondAttrib == BOND_MARK_ALT13 ) { + return 0; + } + break; + + case 1: /* double: can be 2 (double)? */ + if ( nBondAttrib == BOND_MARK_ALT12 || + nBondAttrib == BOND_MARK_ALT12NS|| + nBondAttrib == BOND_MARK_ALT123 || + nBondAttrib == BOND_MARK_ALT23 ) { + return 0; /* yes */ + } + break; + case 2: /* triple: can be 3 (triple)? */ + if ( nBondAttrib == BOND_MARK_ALT13 || + nBondAttrib == BOND_MARK_ALT123 || + nBondAttrib == BOND_MARK_ALT23 ) { + return 0; + } + break; + } + } + } + return 1; +} + + +int nBondsValenceInpAt( const inp_ATOM *at, int *nNumAltBonds, int *nNumWrongBonds ) +{ + int j, bond_type, nBondsValence = 0, nAltBonds = 0, nNumWrong = 0; + for ( j = 0; j < at->valence; j ++ ) { + bond_type = at->bond_type[j] & BOND_TYPE_MASK; + switch( bond_type ) { + case 0: /* for structure from InChI reconstruction */ + case BOND_SINGLE: + case BOND_DOUBLE: + case BOND_TRIPLE: + nBondsValence += bond_type; + break; + case BOND_ALTERN: + nAltBonds ++; + break; + default: + nNumWrong ++; + } + } + switch ( nAltBonds ) { + case 0: + break; + case 1: + nBondsValence += 1; /* 1 or greater than 3 is wrong */ + nNumWrong ++; + break; + default: + nBondsValence += nAltBonds+1; + break; + } + if ( nNumAltBonds ) *nNumAltBonds = nAltBonds; + if ( nNumWrongBonds ) *nNumWrongBonds = nNumWrong; + return nBondsValence; +} + + +/* If radical or has aromatic bonds, + then augment to the lowest "multiplicity" */ +int BnsAdjustFlowBondsRad( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at, int num_atoms ) +{ + int bError=0, nOrigDelta=0, ret, num_removed; + +#if( CHECK_AROMBOND2ALT == 1 ) + int *pcValMinusBondsVal = NULL; + int i, nValMinusBondsVal, nAltBonds, bIgnore, valen, is_rad, excess; + + /* find valence excess (it may only be due to aromatic bonds) */ + for ( i = 0; i < num_atoms; i ++ ) + { + valen = nBondsValenceInpAt( at+i, &nAltBonds, &bIgnore ); + nValMinusBondsVal = (int)at[i].chem_bonds_valence - valen; + bIgnore += (nAltBonds > 3); + if ( !bIgnore && nValMinusBondsVal > 0 ) + { + if ( !pcValMinusBondsVal && + !(pcValMinusBondsVal = (int *)inchi_calloc( num_atoms, sizeof(pcValMinusBondsVal[0])))) + { + bError = BNS_OUT_OF_RAM; + goto exit_function; + } + /* mark atoms that have extra unsatisfied valence due to aromatic bonds */ + is_rad = (at[i].radical == RADICAL_DOUBLET); + excess = nValMinusBondsVal+ is_rad; + pcValMinusBondsVal[i] = excess; + } + } +#endif /* CHECK_AROMBOND2ALT */ + + /* match bonds to valences */ + do + { + num_removed = 0; + ret = RunBalancedNetworkSearch( pBNS, pBD, BNS_EF_CHNG_FLOW ); + if ( IS_BNS_ERROR( ret ) ) + { + bError = ret; + } + else + { + nOrigDelta += ret; + num_removed = pBNS->num_altp; /* number of augmenting paths */ + if ( ret > 0 ) + { + /* save new bonds in at[] and flows in pBNS and at[] */ + ret = SetBondsFromBnStructFlow( pBNS, at, num_atoms, BNS_EF_SAVE_ALL ); /* must include 1: 5=(4|1) */ + if ( IS_BNS_ERROR( ret ) ) + { + bError = ret; + } + ret = RestoreBnStructFlow( pBNS, BNS_EF_SAVE_ALL ); /* must include 1: 5=(4|1) */ + if ( IS_BNS_ERROR( ret ) ) + { + bError = ret; + } + } + ReInitBnStructAltPaths( pBNS ); + } + } while ( num_removed && num_removed == pBNS->max_altp && !bError ); + +#if( CHECK_AROMBOND2ALT == 1 ) + /* check whether aromatic bonds have been replaces with alternating bonds */ + if ( !bError && pcValMinusBondsVal ) + { + for ( i = 0; i < num_atoms; i ++ ) + { + if ( !pcValMinusBondsVal[i] ) + continue; + valen = nBondsValenceInpAt( at+i, &nAltBonds, &bIgnore ); + nValMinusBondsVal = (int)at[i].chem_bonds_valence - valen; + is_rad = (at[i].radical == RADICAL_DOUBLET); + excess = nValMinusBondsVal + is_rad; + if ( bIgnore || + ( pcValMinusBondsVal[i] - excess ) != 1 ) + { + /* radical excess has not been reduced */ + bError = BNS_ALTBOND_ERR; + break; + } + } + } + +exit_function: + if ( pcValMinusBondsVal ) + inchi_free( pcValMinusBondsVal ); +#endif /* CHECK_AROMBOND2ALT */ + + return bError? bError : nOrigDelta; +} + + +int BnsTestAndMarkAltBonds( BN_STRUCT *pBNS, + BN_DATA *pBD, + inp_ATOM *at, + int num_atoms, + BNS_FLOW_CHANGES *fcd, + int bChangeFlow, + int nBondTypeToTest ) +{ + int ret, iat, ineigh, neigh; + int nMinFlow, nMaxFlow, nTestFlow, nCurFlow; + int iedge, bSuccess, bError, nDots, nChanges, bTestForNonStereoBond; + /* Normalize bonds and find tautomeric groups */ + bError = 0; + nChanges = 0; + bTestForNonStereoBond = pBNS->tot_st_cap > pBNS->tot_st_flow; + for ( iat = 0; iat < num_atoms && !bError; iat ++ ) { + for ( ineigh = 0; ineigh < at[iat].valence && !bError; ineigh ++ ) { + neigh = at[iat].neighbor[ineigh]; + if ( neigh < iat ) + continue; /* we have already tested the bond */ + iedge = pBNS->vert[iat].iedge[ineigh]; + if ( IS_FORBIDDEN(pBNS->edge[iedge].forbidden, pBNS) ) + continue; + if ( nBondTypeToTest && (at[iat].bond_type[ineigh] & BOND_TYPE_MASK) != nBondTypeToTest ) + continue; + nMinFlow = nMinFlow2Check( pBNS, iedge ); + nMaxFlow = nMaxFlow2Check( pBNS, iedge ); + nCurFlow = nCurFlow2Check( pBNS, iedge ); + if ( nMinFlow == nMaxFlow ) { + if ( nMaxFlow && bTestForNonStereoBond ) { + nTestFlow = nMaxFlow - (int)(pBNS->tot_st_cap - pBNS->tot_st_flow); /* temporary use of nTestFlow */ + nMinFlow = inchi_max( 0, nTestFlow ); + } else { + continue; + } + } + for ( nTestFlow = nMinFlow; nTestFlow <= nMaxFlow && !bError; nTestFlow ++ ) { + if ( nTestFlow == nCurFlow ) + continue; + if ( !bNeedToTestTheFlow( at[iat].bond_type[ineigh], nTestFlow, bTestForNonStereoBond ) ) + continue; + bSuccess = 0; + nDots = bSetFlowToCheckOneBond( pBNS, iedge, nTestFlow, fcd ); + if ( IS_BNS_ERROR(nDots) ) { + if ( nDots == BNS_CANT_SET_BOND ) { + ret = bRestoreFlowAfterCheckOneBond( pBNS, fcd ); + if ( !IS_BNS_ERROR( ret ) ) { + continue; + } + } + bError = nDots; + } else + if ( nDots > 0 ) { + ret = RunBalancedNetworkSearch( pBNS, pBD, bChangeFlow ); + if ( IS_BNS_ERROR( ret ) ) { + bError = ret; + } else + if ( ret > 0 ) { + if ( 2*ret == nDots ) { + ret = bSetBondsAfterCheckOneBond( pBNS, fcd, nTestFlow, at, num_atoms, bChangeFlow ); + if ( IS_BNS_ERROR( ret ) ) { + bError = ret; + } else { + nChanges += (ret & 1); + ret = SetBondsFromBnStructFlow( pBNS, at, num_atoms, bChangeFlow ); + if ( IS_BNS_ERROR( ret ) ) { + bError = ret; + } else + if ( ret >= 0 ) { + nChanges += (ret & 1); + bSuccess = 1; + } else { + bError = ret; + } + } + } + /* typically 2*ret < nDots; 2*ret > nDots should not happen. Check later */ + ret = RestoreBnStructFlow( pBNS, bChangeFlow & BNS_EF_CHNG_RSTR); + if ( IS_BNS_ERROR( ret ) ) { + bError = ret; + } + } + /* --- reinitialize to repeat the calculations --- */ + ReInitBnStructAltPaths( pBNS ); + } else + if ( nDots == 0 ) { + ret = bSetBondsAfterCheckOneBond( pBNS, fcd, nTestFlow, at, num_atoms, bChangeFlow ); + if ( IS_BNS_ERROR( ret ) ) { + bError = ret; + } else { + nChanges += (ret & 1); + } + } + ret = bRestoreFlowAfterCheckOneBond( pBNS, fcd ); + if ( IS_BNS_ERROR( ret ) ) { + bError = ret; + } + } + } + } + return bError? bError : nChanges; +} + + +static void remove_alt_bond_marks(inp_ATOM *at, int num_atoms) +{ + int i, j, val; + for ( i = 0; i < num_atoms; i++ ) { + for ( val = at[i].valence, j = 0; j < val; j ++ ) { + at[i].bond_type[j] &= BOND_TYPE_MASK; + } + } +} + + +int SetForbiddenEdges( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, + int forbidden_mask, int nebend, int *ebend ) +{ + static U_CHAR el_number_O; + static U_CHAR el_number_C; + static U_CHAR el_number_N; + + int i, j, neigh, num_found; + BNS_IEDGE iedge; + /*S_CHAR edge_forbidden_mask = BNS_EDGE_FORBIDDEN_MASK;*/ + S_CHAR edge_forbidden_mask = forbidden_mask; + + pBNS->edge_forbidden_mask |= forbidden_mask; + + if ( !el_number_C ) { + el_number_O = (U_CHAR)get_periodic_table_number( "O" ); + el_number_C = (U_CHAR)get_periodic_table_number( "C" ); + el_number_N = (U_CHAR)get_periodic_table_number( "N" ); + } + + num_found = 0; + + for ( i = 0; i < num_atoms; i ++ ) { + /* acetyl */ + if ( at[i].el_number == el_number_C && 3 == at[i].valence && + 4 == at[i].chem_bonds_valence ) { + int num_O = 0; + int bond_to_O_val = 0; + int forbidden_bond_pos = -1; + int forbidden_bond_val = -1; + for ( j = 0; j < at[i].valence; j ++ ) { + neigh = at[i].neighbor[j]; + if ( at[neigh].el_number == el_number_O && + at[neigh].valence == 1 ) { + num_O ++; + bond_to_O_val += (at[i].bond_type[j] & BOND_TYPE_MASK); + } else { + forbidden_bond_pos = j; + forbidden_bond_val = (at[i].bond_type[j] & BOND_TYPE_MASK); + } + } + if ( 2 == num_O && 3 == bond_to_O_val && 1 == forbidden_bond_val ) { + iedge = pBNS->vert[i].iedge[forbidden_bond_pos]; + pBNS->edge[iedge].forbidden |= edge_forbidden_mask; + num_found ++; + } + } else + /* nitro */ + if ( at[i].el_number == el_number_N && 3 == at[i].valence && + (4 == at[i].chem_bonds_valence || 5 == at[i].chem_bonds_valence) ) { + int num_O = 0; + int bond_to_O_val = 0; + int forbidden_bond_pos = -1; + int forbidden_bond_val = -1; + for ( j = 0; j < at[i].valence; j ++ ) { + neigh = at[i].neighbor[j]; + if ( at[neigh].el_number == el_number_O && + at[neigh].valence == 1 ) { + num_O ++; + bond_to_O_val += (at[i].bond_type[j] & BOND_TYPE_MASK); + } else { + forbidden_bond_pos = j; + forbidden_bond_val = (at[i].bond_type[j] & BOND_TYPE_MASK); + } + } + if ( 2 == num_O && (3 == bond_to_O_val || 4 == bond_to_O_val) && 1 == forbidden_bond_val ) { + iedge = pBNS->vert[i].iedge[forbidden_bond_pos]; + pBNS->edge[iedge].forbidden |= edge_forbidden_mask; + num_found ++; + } + } + } +#ifdef FIX_SRU_CYCLIZING_PS_BONDS_IN_BNS + if ( nebend > 1 ) + num_found += fix_explicitly_indicated_bonds( nebend, ebend, pBNS, at, num_atoms ); +#endif + +#if ( REMOVE_ION_PAIRS_FIX_BONDS == 1 ) + num_found += fix_special_bonds( pBNS, at, num_atoms, edge_forbidden_mask ); +#endif +#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) + num_found += TempFix_NH_NH_Bonds( pBNS, at, num_atoms ); +#endif + + + return num_found; +} + + +#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) +/************************************************************************/ +int TempFix_NH_NH_Bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms ) +{ + static U_CHAR el_number_N; + int i, j, neigh, num_found; + BNS_IEDGE iedge; + S_CHAR edge_forbidden_mask = BNS_EDGE_FORBIDDEN_TEMP; + if ( !el_number_N ) { + el_number_N = (U_CHAR)get_periodic_table_number( "N" ); + } + for ( i = 0, num_found = 0; i < num_atoms; i ++ ) { + /* -NH-NH- or -NH-NH3 */ + if ( at[i].el_number == el_number_N && at[i].valence < 3 && at[i].num_H && + 3 == at[i].chem_bonds_valence + at[i].num_H && + at[i].chem_bonds_valence == at[i].valence && + !at[i].charge && !at[i].radical ) { + for ( j = 0; j < at[i].valence; j ++ ) { + neigh = at[i].neighbor[j]; + if ( neigh < i && + at[neigh].el_number == el_number_N && at[neigh].valence < 3 && at[neigh].num_H && + 3 == at[neigh].chem_bonds_valence + at[neigh].num_H && + at[neigh].chem_bonds_valence == at[neigh].valence && + !at[neigh].charge && !at[neigh].radical) { + iedge = pBNS->vert[i].iedge[j]; + pBNS->edge[iedge].forbidden |= edge_forbidden_mask; + num_found ++; + } + } + } + } + return num_found; +} + + +int CorrectFixing_NH_NH_Bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms ) +{ + static U_CHAR el_number_N; + int i, j, neigh, num_found; + BNS_IEDGE iedge; + S_CHAR edge_forbidden_mask = BNS_EDGE_FORBIDDEN_TEMP; + if ( !el_number_N ) { + el_number_N = (U_CHAR)get_periodic_table_number( "N" ); + } + for ( i = 0, num_found = 0; i < num_atoms; i ++ ) { + /* -NH-NH- or -NH-NH3 */ + if ( at[i].el_number == el_number_N && at[i].valence < 3 ) { + for ( j = 0; j < at[i].valence; j ++ ) { + neigh = at[i].neighbor[j]; + if ( neigh < i && + at[neigh].el_number == el_number_N && at[neigh].valence < 3 ) { + if ( BOND_TYPE_SINGLE != (at[i].bond_type[j] & BOND_TYPE_MASK) ) { + iedge = pBNS->vert[i].iedge[j]; + if ( pBNS->edge[iedge].forbidden & edge_forbidden_mask ) { + pBNS->edge[iedge].forbidden &= ~edge_forbidden_mask; + num_found ++; + } + } + } + } + } + } + return num_found; +} +#endif + + + + +/* + Fixes bonds which were set by remove_ion_pairs( ... ) +*/ +int fix_special_bonds( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int forbidden_mask ) +{ + int num_changes = 0; + + /* 0 1 2 3 4 5 6 7 8 9 8 9 */ +#if ( FIX_REM_ION_PAIRS_Si_BUG == 1 ) + static const char el[] = "N;P;As;Sb;O;S;Se;Te;C;Si;"; /* 8 elements + C, Si */ +#else + static const char el[] = "N;P;As;Sb;O;S;Se;Te;C;Si"; /* 8 elements + C, Si */ +#endif + static char en[12]; /* same number: 8 elements */ + static int ne=0; /* will be 8 and 10 */ + int ne2; + +#define ELEM_N_FST 0 +#define ELEM_N_LEN 4 +#define ELEM_O_FST 4 +#define ELEM_O_LEN 4 +#define ELEM_S_FST (ELEM_O_FST+1) +#define ELEM_S_LEN (ELEM_O_LEN-1) +#define ELEM_C_FST 8 +#define ELEM_C_LEN 2 + +#define MAX_NEIGH 6 + + int i, k, n1, n2, n3, n4, i1, i2, i3, i4, bond_type; + inp_ATOM *a; + char elname[ATOM_EL_LEN]; + int j[3], m[3], num_O, k_O, num_N, num_OH, num_OM, num_X, num_other, k_N; + + BNS_IEDGE iedge; + /*S_CHAR edge_forbidden_mask = BNS_EDGE_FORBIDDEN_MASK;*/ + S_CHAR edge_forbidden_mask = forbidden_mask; + + pBNS->edge_forbidden_mask |= edge_forbidden_mask; + + if ( !ne ) + { + /* one time initialization */ + const char *b, *e; + int len; + ne2=0; + for ( b = el; e = strchr( b, ';'); b = e+1 ) + { + len = (int) (e-b); + memcpy( elname, b, len ); + elname[len] = '\0'; + en[ne2++] = get_periodic_table_number( elname ); + } + en[ne2] = '\0'; + en[ne2+1] = '\0'; + ne=ne2; + } + + for ( i = 0, a = at; i < num_atoms; i ++, a++ ) + { + if ( !a->charge && !a->radical && + 2 <= a->chem_bonds_valence + NUMH(a,0) - get_el_valence( a->el_number,0, 0 ) && + 0 == num_of_H( at, i ) && + 2 == nNoMetalBondsValence(at, i) + NUMH(a,0) - get_el_valence( a->el_number,0, 0 ) && + NULL != memchr( en+ELEM_N_FST, at[i].el_number, ELEM_N_LEN) ) + { + /* found N(V), no H */ + if ( 2 == nNoMetalNumBonds(at, i) ) + { + /* #N= */ + /* fix bonds: double and triple: =N# so that bonds cannot be changed by the normalization */ +#if ( FIX_N_V_METAL_BONDS_GPF == 1 ) + if ( 0 > ( i1 = nNoMetalNeighIndex( at, i ) ) + || + 0 > ( i2 = nNoMetalOtherNeighIndex( at, i, n1 = a->neighbor[i1]/* non-metal neighbor #1 */ ) ) ) + { + /*num_err ++; */ /* do not count would-be original InChI v.1 buffer overflow GPF */ + continue; /* v1 bug: 2 bonds to metal yield i1 < 0 and/or i2 < 0 => bounds violation */ + } +#else + i1 = nNoMetalNeighIndex( at, i ); + n1 = a->neighbor[i1]; /* non-metal neighbor #1 */ + i2 = nNoMetalOtherNeighIndex( at, i, n1 ); +#endif + n2 = a->neighbor[i2]; /* non-metal neighbor #2 */ + /* forbid all edges to non-metals */ + iedge = pBNS->vert[i].iedge[i1]; + pBNS->edge[iedge].forbidden |= edge_forbidden_mask; /* fix bond to neighbor #1 */ + iedge = pBNS->vert[i].iedge[i2]; + pBNS->edge[iedge].forbidden |= edge_forbidden_mask; /* fix bond to neighbor #1 */ + num_changes ++; /* added 11-15-2005 */ + /* i n3 */ + /* forbid single bond edge beyond the neighboring =N- as in #N=N- */ + if ( (at[i].bond_type[i1] & BOND_TYPE_MASK) == BOND_TYPE_DOUBLE ) + { + i3 = i1; + n3 = n1; + } + else + { + i3 = i2; + n3 = n2; + } + if ( 0 == NUMH(at, n3) && 2 == nNoMetalNumBonds( at, n3 ) && + 3 == nNoMetalBondsValence( at, n3 ) && + NULL != memchr( en+ELEM_N_FST, at[n3].el_number, ELEM_N_LEN) && + 0 <= (k = nNoMetalOtherNeighIndex( at, n3, i )) ) + { + /* found =N- ; forbid the edge*/ + iedge = pBNS->vert[n3].iedge[k]; + pBNS->edge[iedge].forbidden |= edge_forbidden_mask; + num_changes ++; + } + } + else if ( 3 == nNoMetalNumBonds(at, i) && + /* | */ + /* found =N= */ + /* locate all non-metal neighbors */ + 0 <= (j[0] = nNoMetalNeighIndex( at, i )) && + 0 <= (j[1] = nNoMetalOtherNeighIndex( at, i, m[0] = a->neighbor[j[0]] )) && + 0 <= (j[2] = nNoMetalOtherNeighIndex2( at, i, m[0], m[1] = a->neighbor[j[1]] )) ) + { + /* count specific neighbors: N(V)=N, N(V)-N N(V)=O, and N(V)-N */ + + /* if there is a single neighbor connected by a double bond, namely + N(V)=N and/or N(V)=O, then fix the bond(s). + If N(V)=O was fixed then do not fix another bond */ + m[2] = a->neighbor[j[2]]; + num_O = num_N = 0; + for ( k= 0; k < 3; k ++ ) + { + n1 = m[k]; + i1 = j[k]; + if ( NULL != memchr( en+ELEM_N_FST, at[n1].el_number, ELEM_N_LEN) ) + { + k_N = k; + num_N ++; + } + else if ( NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) && + 1 == nNoMetalNumBonds( at, n1 ) ) + { + k_O = k; + num_O ++; + } + } + num_other = 0; + if ( 1 == num_O && + 0 == at[n1=m[k_O]].charge && + 0 == at[n1].radical && + BOND_TYPE_DOUBLE == (at[i].bond_type[i1=j[k_O]] & BOND_TYPE_MASK) + ) + { + /* fix bond to neighbor =O */ + iedge = pBNS->vert[i].iedge[i1]; + pBNS->edge[iedge].forbidden |= edge_forbidden_mask; + num_changes ++; + num_other ++; /* indicator: double to a terminal O has been fixed */ + } + if ( !num_other && + num_O <= 1 && + 1 == num_N && + 0 == at[n1=m[k_N]].charge && + 0 == at[n1].radical && + BOND_TYPE_DOUBLE == (at[i].bond_type[i1=j[k_N]] & BOND_TYPE_MASK ) ) + { + /* fix bond to neighbor =N */ + iedge = pBNS->vert[i].iedge[i1]; + pBNS->edge[iedge].forbidden |= edge_forbidden_mask; + num_changes ++; + } + } + else if ( 4 == nNoMetalNumBonds(at, i) ) + { + /* | */ + /* found -N=N- */ + /* | */ + /* locate non-metal neighbor connected by a double bond; + * if it is =N- then fix the double bond and the single bond beyond the neighbor + */ + num_N = 0; + num_other = 0; + for ( i1 = 0; i1 < at[i].valence; i1 ++ ) + { + if ( BOND_TYPE_DOUBLE == (at[i].bond_type[i1] & BOND_TYPE_MASK) && + !is_el_a_metal(at[n1=(int)at[i].neighbor[i1]].el_number) && + NULL != memchr( en+ELEM_N_FST, at[n1].el_number, ELEM_N_LEN) ) + { + num_N ++; + n2 = n1; + i2 = i1; + } + } + if ( 1 == num_N && 0 == NUMH(at, n2) && + 2 == nNoMetalNumBonds( at, n2 ) && + 3 == nNoMetalBondsValence(at, n2) && + 0 <= ( i3 = nNoMetalOtherNeighIndex( at, n2, i ) ) && + BOND_TYPE_SINGLE == (at[n2].bond_type[i3] & BOND_TYPE_MASK) ) + { + /* fix the single bond beyond the N(V) neighbor N(V)=N- */ + iedge = pBNS->vert[n2].iedge[i3]; + pBNS->edge[iedge].forbidden |= edge_forbidden_mask; + num_changes ++; + /* fix the double bond */ + iedge = pBNS->vert[i].iedge[i2]; + pBNS->edge[iedge].forbidden |= edge_forbidden_mask; + num_changes ++; + } + } + } + else if ( !a->charge && !a->radical && + 2 <= a->chem_bonds_valence + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && + 0 == num_of_H( at, i ) && + 2 == nNoMetalBondsValence(at, i) + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && + NULL != memchr( en+ELEM_S_FST, a->el_number, ELEM_S_LEN) && + 3 == nNoMetalNumBonds( at, i ) ) + { + /* found S(IV), no H, one double bond, total 3 bonds */ + /* OH + / + in O=S (X != O) fix single bond O-X (type 1) + \ + X + + X + / + in Z=S (X, Y != OH) fix double bond Z=S (type 2) + \ + Y + */ + num_N = 0; /* number of non-metal neighbors connected by a double bond */ + num_OH = 0; /* number of neighbors OH connected by a single bond */ + num_OM = 0; /* number of charged neighbors O connected by a single bond */ + num_O = 0; /* number of neighbors =O connected by a double bond */ + num_other = 0; + for ( i1 = 0; i1 < a->valence; i1 ++ ) + { + n1=(int)a->neighbor[i1]; + if ( is_el_a_metal(at[n1].el_number) ) + { + continue; + } + bond_type = (a->bond_type[i1] & BOND_TYPE_MASK); + if ( BOND_TYPE_DOUBLE == bond_type ) + { + num_N ++; + n2 = n1; + i2 = i1; + if ( NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) ) + { + num_O ++; + } + } + else if ( BOND_TYPE_SINGLE == bond_type && + 1 == nNoMetalNumBonds( at, n1 ) && + 1 == nNoMetalBondsValence(at, n1 ) && + NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) ) + { + if ( 0 == at[n1].charge ) + { + num_OH ++; + n3 = n1; + i3 = i1; + } + else + { + num_OM ++; + } + } + else + { + num_other ++; + n4 = n1; + i4 = i1; + } + } + if ( 1 == num_N && 1 == num_O && 1 == num_OH + num_OM ) + { + if ( 1 == num_other ) + { + /* type 1: fix the single bond S-X */ + iedge = pBNS->vert[i].iedge[i4]; + pBNS->edge[iedge].forbidden |= edge_forbidden_mask; + num_changes ++; + } + } + else if ( 1 == num_N && !num_OH && !num_OM ) + { + int bFound = 0; /* flag */ + int bDoNotFixAnyBond = 0; /* flag */ + /* avoid case N=S-NH or N=S-N(-); N = N, P, As, Sb */ + if ( NULL != memchr( en+ELEM_N_FST, at[n2].el_number, ELEM_N_LEN) ) + { + U_CHAR el_number = at[n2].el_number; + for ( i1 = 0; i1 < a->valence; i1 ++ ) + { + n1=(int)a->neighbor[i1]; + bond_type = (a->bond_type[i1] & BOND_TYPE_MASK); + if ( BOND_TYPE_SINGLE == bond_type && + (NUMH(at, n1) || -1 == at[n1].charge) && + el_number == at[n1].el_number ) + { + i3 = i1; + n3 = n1; + bFound ++; + } + } + } + /* exception: check if Z==X and they belong to the same ring system */ + for ( i1 = 0; i1 < a->valence; i1 ++ ) + { + if ( i1 != i2 ) + { + n1=(int)a->neighbor[i1]; + if ( at[n2].el_number == at[n1].el_number && + at[n2].nRingSystem == at[n1].nRingSystem ) + { + bDoNotFixAnyBond ++; + } + } + } + + if ( bDoNotFixAnyBond ) + { + ; /* do nothing */ + } + else if ( bFound ) + { + if ( 1 == bFound && + 0 <= ( i4 = nNoMetalOtherNeighIndex2( at, i, n2, n3) ) ) + { + /* fix bond i4 */ + iedge = pBNS->vert[i].iedge[i4]; + pBNS->edge[iedge].forbidden |= edge_forbidden_mask; + num_changes ++; + } + } + else + { + /* fix the double bond >S=X */ + iedge = pBNS->vert[i].iedge[i2]; + pBNS->edge[iedge].forbidden |= edge_forbidden_mask; + num_changes ++; + /* -- test later -- + if ( 2 == nNoMetalNumBonds( at, n2 ) && + 0 <= ( i3 = nNoMetalOtherNeighIndex( at, n2, i ) ) ) { + iedge = pBNS->vert[n2].iedge[i3]; + pBNS->edge[iedge].forbidden |= edge_forbidden_mask; + num_changes ++; + } + -------------------*/ + } + } + } + else if ( !a->charge && !a->radical && + 4 <= a->chem_bonds_valence + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && + 0 == num_of_H( at, i ) && + 4 == nNoMetalBondsValence(at, i) + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && + NULL != memchr( en+ELEM_S_FST, a->el_number, ELEM_S_LEN) && + 4 == nNoMetalNumBonds( at, i ) ) + { + /* found S(VI), no H, two double bonds or one triple bond */ + /* O + || + in O=S--Y- (X, Y -- non-terminal) fix single bonds S-X, S-Y (type 1) + \ + X-- + + O + || + in O=S--O(-) (X -- non-terminal) fix single bond S-X (type 2) + \ + X-- + + O + || + in O=S--OH (X -- non-terminal) fix single bond S-X (type 3) + \ + X-- + + */ + int iN[4]; /* indexes of non-terminal neighbors connected by a single bond */ + num_N = 0; /* number of non-metal neighbors connected by a double bond */ + num_OH = 0; /* number of neighbors OH connected by a single bond */ + num_OM = 0; /* number of non-terminal neighbors connected by a single bond */ + num_O = 0; /* number of neighbors =O connected by a double bond */ + num_X = 0; /* number of terminal atom X != O connected by a single bond */ + num_other = 0; + for ( i1 = 0; i1 < a->valence; i1 ++ ) + { + n1=(int)a->neighbor[i1]; + if ( is_el_a_metal(at[n1].el_number) ) + { + continue; + } + bond_type = (a->bond_type[i1] & BOND_TYPE_MASK); + if ( BOND_TYPE_DOUBLE == bond_type ) + { + num_N ++; + if ( (0 == at[n1].charge +#if ( S_VI_O_PLUS_METAL_FIX_BOND == 1 ) + || 1 == at[n1].charge && 2 == at[n1].valence +#endif + ) && + 0 == at[n1].radical && + 0 == num_of_H( at, n1 ) && + NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) && + 1 == nNoMetalNumBonds( at, n1 ) ) + { + num_O ++; + } + } + else if ( BOND_TYPE_SINGLE == bond_type && + 1 == nNoMetalNumBonds( at, n1 ) && + NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) && + 1 >= num_of_H( at, n1 ) && + 1 == (( 0 == at[n1].charge) && 1==num_of_H( at, n1 )) + +((-1 == at[n1].charge) && 0==num_of_H( at, n1 )) ) + { + num_OH ++; /* -OH or -O(-) */ + } + else if ( BOND_TYPE_SINGLE == bond_type && + 1 < nNoMetalNumBonds( at, n1 ) ) + { + iN[num_OM ++] = i1; /* non-terminal neighbor connected by a single bond */ + } + else if ( BOND_TYPE_SINGLE == bond_type && + 1 == nNoMetalNumBonds( at, n1 ) ) + { + num_X ++; /* other terminal neighbor connected by a single bond */ + } + else + { + num_other ++; + } + } + if ( num_N == num_O && 2 == num_O && 2 == num_OH + num_OM + num_X && 0 == num_other ) + { + for ( i2 = 0; i2 < num_OM; i2 ++ ) + { + i1 = iN[i2]; + /* fix bond i1 */ + iedge = pBNS->vert[i].iedge[i1]; + pBNS->edge[iedge].forbidden |= edge_forbidden_mask; + num_changes ++; + } + } + } + else if ( !a->charge && !a->radical && + 6 <= a->chem_bonds_valence + NUMH(a,0) - get_el_valence( a->el_number, 0, 0 ) && + 0 == num_of_H( at, i ) && + 6 == nNoMetalBondsValence(at, i) + NUMH(a,0) - get_el_valence( a->el_number,0,0 ) && + NULL != memchr( en+ELEM_S_FST, a->el_number, ELEM_S_LEN) && + 5 == nNoMetalNumBonds( at, i ) ) + { + /* found S(VIII), no H, three double bonds or two triple bond */ + /* + + O + || + in O=S--Y-- (X, Y -- non-terminal) fix single bond S-X, S-Y (type 4) + //\ + O X-- + + note: this structure is a mistakenly drawn structure + + O O + || || + O=S--O--Y-- or O=S--Y-- + \ \ + X-- O--X-- + + + */ + int iN[5]; /* indexes of non-terminal neighbors connected by a single bond */ + num_N = 0; /* number of non-metal neighbors connected by a double bond */ + num_OH = 0; /* number of neighbors OH connected by a single bond */ + num_OM = 0; /* number of non-terminal neighbors connected by a single bond */ + num_O = 0; /* number of neighbors =O connected by a double bond */ + num_X = 0; /* number of terminal atom X != O connected by a single bond */ + num_other = 0; + for ( i1 = 0; i1 < a->valence; i1 ++ ) + { + n1=(int)a->neighbor[i1]; + if ( is_el_a_metal(at[n1].el_number) ) + { + continue; + } + bond_type = (a->bond_type[i1] & BOND_TYPE_MASK); + if ( BOND_TYPE_DOUBLE == bond_type ) + { + num_N ++; + if ( (0 == at[n1].charge +#if ( S_VI_O_PLUS_METAL_FIX_BOND == 1 ) + || 1 == at[n1].charge && 2 == at[n1].valence +#endif + ) + && 0 == at[n1].radical && + 0 == num_of_H( at, n1 ) && + NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) && + 1 == nNoMetalNumBonds( at, n1 ) ) + { + num_O ++; + } + } + else if ( BOND_TYPE_SINGLE == bond_type && + 1 == nNoMetalNumBonds( at, n1 ) && + NULL != memchr( en+ELEM_O_FST, at[n1].el_number, ELEM_O_LEN) && + 1 >= num_of_H( at, n1 ) && + 1 == (( 0 == at[n1].charge) && 1==num_of_H( at, n1 )) + +((-1 == at[n1].charge) && 0==num_of_H( at, n1 )) ) + { + num_OH ++; /* -OH or -O(-) */ + } + else + if ( BOND_TYPE_SINGLE == bond_type && + 1 < nNoMetalNumBonds( at, n1 ) ) { + + iN[num_OM ++] = i1; /* non-terminal neighbor connected by a single bond */ + } + else + if ( BOND_TYPE_SINGLE == bond_type && + 1 == nNoMetalNumBonds( at, n1 ) ) + { + + num_X ++; /* other terminal neighbor connected by a single bond */ + } + else + { + num_other ++; + } + } + if ( num_N == num_O && 3 == num_O && 2 == num_OH + num_OM + num_X && 0 == num_other ) + { + for ( i2 = 0; i2 < num_OM; i2 ++ ) + { + i1 = iN[i2]; + /* fix bond i1 */ + iedge = pBNS->vert[i].iedge[i1]; + pBNS->edge[iedge].forbidden |= edge_forbidden_mask; + num_changes ++; + } + } + } + } + + return num_changes; +} + + +/* + +*/ +int fix_explicitly_indicated_bonds( int nebend, int *ebend, BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms ) +{ + int i, num_changes = 0; + inp_ATOM *a; + int j, ia1, ia2, i1=-1, i2=-1, index=-1, neigh; + BNS_IEDGE iedge; + S_CHAR edge_forbidden_mask = BNS_EDGE_FORBIDDEN_MASK; + pBNS->edge_forbidden_mask |= edge_forbidden_mask; + + if ( nebend < 1 || !ebend ) + return 0; + + for ( j=0; j 0 && i2 > 0 ) break; + } + if ( i1<0 || i2<0 ) return 0; + + if ( i2 < i1 ) + { int tmp = i1; i1 = i2; i2 = tmp; } + + a = at + i1; + for ( i = 0; i < a->valence; i ++ ) + { + neigh = (int)a->neighbor[i]; + if ( neigh == i2 ) + { index = i; break; } + } + if ( index > -1 ) + { + iedge = pBNS->vert[i].iedge[index]; + pBNS->edge[iedge].forbidden |= edge_forbidden_mask; + num_changes ++; + } + } + + return num_changes; +} + + + + +#define ALL_NONMETAL_Z 0 + + +/* + +*/ +int is_Z_atom( U_CHAR el_number ) +{ + typedef enum tag_Z_elnumber { + el_C , + el_N , + el_P , + el_As, + el_Sb, + el_S , + el_Se, + el_Te, + el_Cl, + el_Br, + el_I , +#if ( ALL_NONMETAL_Z == 1 ) + el_B , + el_O , + el_Si, + el_Ge, + el_F , + el_At, +#endif + el_len + } Z_ELNUMBER; + static U_CHAR el_numb[el_len]; +/* + return is_el_a_metal( (int)el_number ); +*/ + if ( !el_numb[el_C] ) { + el_numb[el_C ] = (U_CHAR)get_periodic_table_number( "C" ); + el_numb[el_N ] = (U_CHAR)get_periodic_table_number( "N" ); + el_numb[el_P ] = (U_CHAR)get_periodic_table_number( "P" ); + el_numb[el_As] = (U_CHAR)get_periodic_table_number( "As" ); + el_numb[el_Sb] = (U_CHAR)get_periodic_table_number( "Sb" ); + el_numb[el_S ] = (U_CHAR)get_periodic_table_number( "S" ); + el_numb[el_Se] = (U_CHAR)get_periodic_table_number( "Se" ); + el_numb[el_Te] = (U_CHAR)get_periodic_table_number( "Te" ); + el_numb[el_Cl] = (U_CHAR)get_periodic_table_number( "Cl" ); + el_numb[el_Br] = (U_CHAR)get_periodic_table_number( "Br" ); + el_numb[el_I ] = (U_CHAR)get_periodic_table_number( "I" ); +#if ( ALL_NONMETAL_Z == 1 ) + el_numb[el_B ] = (U_CHAR)get_periodic_table_number( "B" ); + el_numb[el_O ] = (U_CHAR)get_periodic_table_number( "O" ); + el_numb[el_Si] = (U_CHAR)get_periodic_table_number( "Si" ); + el_numb[el_Ge] = (U_CHAR)get_periodic_table_number( "Ge" ); + el_numb[el_F ] = (U_CHAR)get_periodic_table_number( "F" ); + el_numb[el_At] = (U_CHAR)get_periodic_table_number( "At" ); +#endif + } + if ( memchr( el_numb, el_number, el_len ) ) { + return 1; + } + return 0; +} + + +/* Detect O==Z--X, O=O,S,Se,Te */ +int IsZOX( inp_ATOM *atom, int at_x, int ord ) +{ + static U_CHAR el_number_O = 0; + static U_CHAR el_number_S = 0; + static U_CHAR el_number_Se = 0; + static U_CHAR el_number_Te = 0; + inp_ATOM *at_Z = atom + atom[at_x].neighbor[ord]; + + int i, neigh, num_O; + + if ( !el_number_O ) { + el_number_O = (U_CHAR)get_periodic_table_number( "O" ); + el_number_S = (U_CHAR)get_periodic_table_number( "S" ); + el_number_Se = (U_CHAR)get_periodic_table_number( "Se" ); + el_number_Te = (U_CHAR)get_periodic_table_number( "Te" ); + } + for ( i = 0, num_O = 0; i < at_Z->valence; i ++ ) { + neigh = at_Z->neighbor[i]; + if ( neigh == at_x ) { + continue; + } + if ( atom[neigh].valence == 1 && + atom[neigh].chem_bonds_valence == 2 && + atom[neigh].charge == 0 && + atom[neigh].radical == 0 && + (atom[neigh].el_number == el_number_O || + atom[neigh].el_number == el_number_S || + atom[neigh].el_number == el_number_Se || + atom[neigh].el_number == el_number_Te ) ) { + num_O ++; + } + } + return num_O; +} + + +int GetAtomChargeType( inp_ATOM *atom, int at_no, int nAtTypeTotals[], int *pMask, int bSubtract ) +{ + static U_CHAR el_number_C = 0; + static U_CHAR el_number_O = 0; + static U_CHAR el_number_S = 0; + static U_CHAR el_number_Se = 0; + static U_CHAR el_number_Te = 0; + static U_CHAR el_number_P = 0; + static U_CHAR el_number_N = 0; + static U_CHAR el_number_H = 0; + + static U_CHAR el_number_F = 0; + static U_CHAR el_number_Cl = 0; + static U_CHAR el_number_Br = 0; + static U_CHAR el_number_I = 0; + + inp_ATOM *at = atom + at_no; +#if ( FIX_NORM_BUG_ADD_ION_PAIR == 1 ) + int i, neigh, mask, bit, type, num_z, num_m, num_o, delta = bSubtract > 0 ? -1 : 1; /* 0 or -2 => add, 1 or 2 => subtract */ + int bNoAdjIon = (bSubtract==0 || bSubtract==1); +#else + int i, neigh, mask, bit, type, num_z, num_m, num_o, delta = bSubtract? -1 : 1; +#endif + int bUnsatNHasTerminalO = 0; + if ( !el_number_C ) { + el_number_C = (U_CHAR)get_periodic_table_number( "C" ); + el_number_O = (U_CHAR)get_periodic_table_number( "O" ); + el_number_S = (U_CHAR)get_periodic_table_number( "S" ); + el_number_Se = (U_CHAR)get_periodic_table_number( "Se" ); + el_number_Te = (U_CHAR)get_periodic_table_number( "Te" ); + el_number_P = (U_CHAR)get_periodic_table_number( "P" ); + el_number_N = (U_CHAR)get_periodic_table_number( "N" ); + el_number_H = (U_CHAR)get_periodic_table_number( "H" ); + el_number_F = (U_CHAR)get_periodic_table_number( "F" ); + el_number_Cl = (U_CHAR)get_periodic_table_number( "Cl" ); + el_number_Br = (U_CHAR)get_periodic_table_number( "Br" ); + el_number_I = (U_CHAR)get_periodic_table_number( "I" ); + } + + type = ATT_NONE; + mask = 0; + if ( at->radical && at->radical != RADICAL_SINGLET ) { + goto exit_function; + } + if ( is_el_a_metal( at->el_number ) ) { + goto exit_function; /* metal */ + } + if ( at->charge < -1 || at->charge > 1 ) { + goto exit_function; + } + if ( !at->valence && at->charge == 1 && !at->num_H && !at->radical && at->el_number == el_number_H ) { + /* a proton (#1) */ + type = ATT_PROTON; + mask = ATBIT_Proton; + goto count_mask_bits; + } + if ( !at->valence && at->charge == -1 && !at->num_H && !at->radical && + ( at->el_number == el_number_F || + at->el_number == el_number_Cl || + at->el_number == el_number_Br || + at->el_number == el_number_I ) + ) { + /* a halogen anion (#2) */ + type = ATT_HalAnion; + mask = ATBIT_HalAnion; + goto count_mask_bits; + } +#if ( HAL_ACID_H_XCHG == 1 ) + /* halogen/chalcogen acid */ + if ( !at->valence && at->charge == 0 && 1 == at->num_H && !at->radical && + ( at->el_number == el_number_F || + at->el_number == el_number_Cl || + at->el_number == el_number_Br || + at->el_number == el_number_I ) || + !at->valence && at->charge == 0 && 2 == at->num_H && !at->radical && + ( at->el_number == el_number_O || + at->el_number == el_number_S || + at->el_number == el_number_Se || + at->el_number == el_number_Te ) + ) { + /* a halogen/chalcogen acid (#3) */ + type = ATT_HalAcid; + mask = ATBIT_HalAcid; + goto count_mask_bits; + } +#endif + if ( detect_unusual_el_valence( at->el_number, at->charge, at->radical, + at->chem_bonds_valence, at->num_H, + at->valence ) ) { + goto exit_function; /* unusual valence state */ + } + /* check neighbors */ + for ( i = 0, num_z = 0, num_m = 0, num_o = 0; i < at->valence; i ++ ) { + neigh = at->neighbor[i]; +#if ( FIX_NORM_BUG_ADD_ION_PAIR == 1 ) + if ( atom[neigh].charge < -1 || atom[neigh].charge > 1 ) { + goto exit_function; /* neighboring charge */ + } + if ( atom[neigh].charge && at->charge ) { + if ( bNoAdjIon ) { + goto exit_function; /* neighboring charge */ + } + type = ATT_NONE; + mask = 0; + goto count_mask_bits; + } +#else + if ( atom[neigh].charge < -1 || atom[neigh].charge > 1 || atom[neigh].charge && at->charge ) { + goto exit_function; /* neighboring charge */ + } +#endif + if ( detect_unusual_el_valence( atom[neigh].el_number, atom[neigh].charge, atom[neigh].radical, + atom[neigh].chem_bonds_valence, atom[neigh].num_H, + atom[neigh].valence ) ) { + goto exit_function; /* neighbor in unusual valence state */ + } + if ( is_Z_atom( atom[neigh].el_number ) ) { + num_z ++; + } + if ( is_el_a_metal( atom[neigh].el_number ) ) { + num_m ++; + } + num_o += (atom[neigh].el_number == el_number_O); + if ( at->el_number == el_number_N && at->valence == 2 && !at->charge && + /*at->valence < at->chem_bonds_valence &&*/ + atom[neigh].valence == 1 && atom[neigh].chem_bonds_valence == 2 && + (atom[neigh].el_number == el_number_O || + atom[neigh].el_number == el_number_S || + atom[neigh].el_number == el_number_Se || + atom[neigh].el_number == el_number_Te )) { + bUnsatNHasTerminalO ++; + } + } + /* O, S, Se, Te */ + if ( at->el_number == el_number_O || + at->el_number == el_number_S || + at->el_number == el_number_Se || + at->el_number == el_number_Te ) { + if ( at->charge == 1 ) { + if ( at->num_H ) { /* #4 */ + type = ATT_O_PLUS; + mask |= ATBIT_OH_Plus; + } else { /* #5 */ + type = ATT_O_PLUS; + mask |= ATBIT_O_Plus; + } + } else + if ( at->valence > 1 ) { + goto exit_function; /* not a terminal atom #C1 */ + } else + if ( at->valence && !(num_z || num_o) ) { + if ( num_m == at->valence ) { + goto exit_function; /* #C2 */ + } + goto count_mask_bits; /* #C3 count charges, no donor or acceptor found */ + } else + /* here at->neigh[0] is one of: O, or Z={C, N, P, As, Sb, S, Se, Te, Cl, Br, I} */ + if ( at->valence ) { + neigh = at->neighbor[0]; /* Z or O only */ + if ( !atom[neigh].charge && atom[neigh].el_number == el_number_C && + atom[neigh].chem_bonds_valence > atom[neigh].valence ) { + /* =C-OH, #C-OH, =C-O(-), #C-O(-), -C=O, =C=O; O = O, S, Se, Te */ + type = ATT_ACIDIC_CO; + if ( at->num_H == 1 ) { + mask |= (ATBIT_COH); /* #6: =C-OH, #C-OH; O=O,S,Se,Te */ + /*nAtTypeTotals[ATTOT_NUM_COH] ++;*/ + } else + if ( at->charge == -1 ) { + mask |= (ATBIT_CO_Minus); /* #7: =C-O(-), #C-O(-); O=O,S,Se,Te */ + /*nAtTypeTotals[ATTOT_NUM_CO_Minus] ++;*/ + } else + if ( !at->num_H && !at->charge ) { + mask |= (ATBIT_CO); /* #8 -C=O, =C=O; O=O,S,Se,Te */ + /*nAtTypeTotals[ATTOT_NUM_CO] ++;*/ + } else { + mask |= (ATBIT_Errors); + /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ + } + } else + if ( !atom[neigh].charge && + (atom[neigh].el_number == el_number_O || + atom[neigh].el_number == el_number_S || + atom[neigh].el_number == el_number_Se || + atom[neigh].el_number == el_number_Te ) && + atom[neigh].chem_bonds_valence == atom[neigh].valence ) { + /* -O-OH, -O-O(-); O = O, S, Se, Te */ + type = ATT_OO; + if ( at->num_H == 1 ) { + mask |= (ATBIT_OOH); /* #9 -O-OH */ + /*nAtTypeTotals[ATTOT_NUM_OOH] ++;*/ + } else + if ( at->charge == -1 ) { + mask |= (ATBIT_OO_Minus); /* #10 -O-O(-) */ + /*nAtTypeTotals[ATTOT_NUM_OO_Minus] ++;*/ + } else { + mask |= (ATBIT_Errors); + /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ + } + } else + if ( !atom[neigh].charge && + atom[neigh].chem_bonds_valence == atom[neigh].valence && + atom[neigh].el_number == el_number_C && + at->el_number != el_number_O ) { + /* >C-S(-), >C-SH; S = S, Se, Te */ + type = ATT_ACIDIC_S; + if ( at->num_H == 1 ) { + mask |= (ATBIT_CSH); /* #11: >C-SH, >CH-SH, -CH2-SH; S = S, Se, Te */ + /*nAtTypeTotals[ATTOT_NUM_CSH] ++;*/ + } else + if ( at->charge == -1 ) { + mask |= (ATBIT_CS_Minus); /* #12: >C-S(-), >CH-S(-), -CH2-S(-); S = S, Se, Te */ + /*nAtTypeTotals[ATTOT_NUM_CS_Minus] ++;*/ + } else { + mask |= (ATBIT_Errors); + /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ + } + } else + if ( atom[neigh].el_number == el_number_N && + atom[neigh].valence == 2 && (!atom[neigh].num_H || atom[neigh].num_H == 1 && atom[neigh].charge == 1) ) { + /* N or N(-) or NH(+) neighbor */ + type = ATT_NO; /* single bond only */ + if ( at->num_H == 1 ) { + mask |= (ATBIT_NOH); /* #13: =N-OH, =NH(+)-OH, #N(+)-OH, -N(-)-OH; O = O, S, Se, Te */ + /*nAtTypeTotals[ATTOT_NUM_NOH] ++;*/ + } else + if ( at->charge == -1 ) { + mask |= (ATBIT_NO_Minus); /* #14: =N-O(-); O = O, S, Se, Te */ + /*nAtTypeTotals[ATTOT_NUM_NO_Minus] ++;*/ + } else + if ( atom[neigh].charge == 1 || atom[neigh].charge == 0 ) { + mask |= (ATBIT_NO); /* #15: =N(+)=O, -NH(+)=O -N=O */ + /*nAtTypeTotals[ATTOT_NUM_NO] ++;*/ + } else { + mask |= (ATBIT_Errors); + /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ + } + } else + if ( atom[neigh].el_number == el_number_N ) { + type = ATT_N_O; /* #16: single bond only */ + if ( at->num_H == 1 ) { + mask |= (ATBIT_N_OH); /* #16: -NH-OH, >N-OH or >N(+)charge == -1 ) { + mask |= (ATBIT_N_O_Minus); /* #17: -NH-O(-), >N-O(-); O = O, S, Se, Te */ + /*nAtTypeTotals[ATTOT_NUM_NO_Minus] ++;*/ + } else + if ( atom[neigh].charge == 1 ) { + mask |= (ATBIT_N_O); /* #18: >N(+)=O */ + /*nAtTypeTotals[ATTOT_NUM_NO] ++;*/ + } else { + mask |= (ATBIT_Errors); + /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ + } + } else + if ( atom[neigh].el_number != el_number_C && atom[neigh].el_number != el_number_O && + !is_el_a_metal( atom[neigh].el_number ) && + atom[neigh].chem_bonds_valence > atom[neigh].valence ) { + /* =Z-OH, #Z-OH, =Z-O(-), #Z-O(-), -Z=O, =Z=O; + =Z(+)-OH, #Z(+)-OH, =Z-O(-), #Z-O(-), -Z(+)=O, =Z(+)=O; O = O, S, Se, Te */ + /* neigh = Z\{N,C} = P, As, Sb, S, Se, Te, Cl, Br, I */ + if ( at->chem_bonds_valence == 1 && IsZOX( atom, at_no, 0 ) ) { + type = ATT_ZOO; + if ( at->num_H == 1 ) { + mask |= (ATBIT_ZOOH); /* 18: O=Z-OH; O=O,S,Se,Te; Z may have charge */ + /*nAtTypeTotals[ATTOT_NUM_ZOOH] ++;*/ + } else + if ( at->charge == -1 ) { + mask |= (ATBIT_ZOO_Minus); /* 19: O=Z-O(-); O = O, S, Se, Te */ + /*nAtTypeTotals[ATTOT_NUM_ZOO_Minus] ++;*/ + } else { + mask |= (ATBIT_Errors); + /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ + } + } else { + type = ATT_OTHER_ZO; + if ( at->num_H == 1 ) { + mask |= (ATBIT_ZOH); /* 20: =Z-OH, #Z-OH; O=O,S,Se,Te; Z may have charge */ + /*nAtTypeTotals[ATTOT_NUM_ZOH] ++;*/ + } else + if ( at->charge == -1 ) { + mask |= (ATBIT_ZO_Minus); /* 21: =Z-O(-), #Z-O(-); O = O, S, Se, Te */ + /*nAtTypeTotals[ATTOT_NUM_ZO_Minus] ++;*/ + } else + if ( at->num_H == 0 ) { + mask |= (ATBIT_ZO); /* 22: -Z=O, =Z=O; O=O,S,Se,Te; Z may have charge */ + /*nAtTypeTotals[ATTOT_NUM_ZO] ++;*/ + } else { + mask |= (ATBIT_Errors); + /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ + } + } + } else + if ( at->charge == -1 && !is_el_a_metal( atom[neigh].el_number ) ) { + /* >Z-O(-); O=O,S,Se,Te */ + type = ATT_OTHER_NEG_O; + mask |= (ATBIT_O_Minus); /* 23: -Z-O(-); O=O,S,Se,Te */ + /*nAtTypeTotals[ATTOT_NUM_ZO_Minus] ++;*/ + } + } else + if ( at->charge == -1 && at->num_H == 1 ) { + type = ATT_OH_MINUS; + mask |= (ATBIT_O_Minus); /* 25: HO(-); O=O,S,Se,Te */ + } + } else + /* P, N, neutral valence = 3 (not 5) */ + if ( (at->el_number == el_number_N || + at->el_number == el_number_P) && + 0 <= at->valence && at->valence <= 3 && + at->chem_bonds_valence + at->num_H == 3 + at->charge ) { + if ( at->valence && !(num_z /*|| num_o == at->valence*/) ) { + if ( num_m == at->valence ) { + goto exit_function; + } + goto count_mask_bits; /* N(III), N(-)(II), N(+)(IV) and same P that have only oxygen neighbors are ignored here */ + } + type = (at->el_number == el_number_N)? ATT_ATOM_N : ATT_ATOM_P; + switch ( at->charge ) { + case -1: + if (at->el_number == el_number_N) { + mask |= (ATBIT_N_Minus); /* 26: -NH(-), =N(-), >N(-) */ + + if ( at->num_H ) + mask |= (ATBIT_NP_H); /* 27: -NH(-) */ +#if ( FIX_NP_MINUS_BUG == 1 ) + else + if ( at->valence == 1 && at->chem_bonds_valence >= 2 && (at->bond_type[0] & BOND_MARK_ALL) ) + type |= ATT_NP_MINUS_V23; /* =N(-) created by normalization 2010-03-11 DT */ +#endif + } + /*nAtTypeTotals[ATTOT_NUM_N_Minus] += (at->el_number == el_number_N);*/ + break; + case 0: + if ( at->num_H ) { + mask |= (ATBIT_NP_H); /* 28: -NH2, =NH, >NH */ + /*nAtTypeTotals[ATTOT_NUM_NP_H] ++;*/ + } else { + if ( bUnsatNHasTerminalO == 1 ) { + mask |= (ATBIT_ON); /* 29: -N=O,-N=OH(+) only, not =N-OH */ + } else { + mask |= (ATBIT_NP); /* 30: -P=O,-P=OH(+), >N- =N- (incl. =N-OH) , #N */ + /*nAtTypeTotals[ATTOT_NUM_NP] ++;*/ + } + } + break; /* ignore neutral N or P */ + case 1: + if ( at->num_H ) { + mask |= (ATBIT_NP_Proton); /* 31: NH4(+), -NH3(+), =NH2(+), >NH2(+), =NH(+)-, >NH(+)-, #NH(+) */ + /*nAtTypeTotals[ATTOT_NUM_NP_Proton] ++;*/ + } else + if ( at->chem_bonds_valence > at->valence ) { + mask |= (ATBIT_NP_Plus); /* =N(+)=, #N(+)-, =N(+)< */ + /*nAtTypeTotals[ATTOT_NUM_NP_Plus] ++;*/ + } else { + type = 0; /* 32: ignore onium cations >N(+)< */ + } + break; + default: + mask |= (1 << ATTOT_NUM_Errors); + /*nAtTypeTotals[ATTOT_NUM_Errors] ++;*/ + break; + } + } +count_mask_bits: + if ( nAtTypeTotals ) { + if ( mask && !(mask & (ATBIT_Errors)) ) { + for ( i = 0, bit = 1; i < ATTOT_ARRAY_LEN; i ++, bit <<= 1 ) { + if ( bit & mask ) { + nAtTypeTotals[i] += delta; + } + } + } + /* count charges */ + if ( at->charge ) { + nAtTypeTotals[ATTOT_TOT_CHARGE] += delta * at->charge; + nAtTypeTotals[ATTOT_NUM_CHARGES] += delta; + } + } + if ( pMask ) { + *pMask = mask; + } +exit_function: + if ( mask & (ATBIT_Errors) ) { + type = 0; + if ( nAtTypeTotals ) { + nAtTypeTotals[ATTOT_NUM_Errors] += 1; + } + } + return type; +} + + +int SimpleRemoveHplusNPO( inp_ATOM *at, + int num_atoms, + int nAtTypeTotals[], + T_GROUP_INFO *t_group_info ) +{ + int i, mask, type, num_removed; + for ( i = 0, num_removed = 0; i < num_atoms; i ++ ) { + if ( (PR_SIMPLE_TYP & (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) && + (PR_SIMPLE_MSK & mask ) ) { +#if ( bRELEASE_VERSION == 0 ) + if ( at[i].charge != 1 || at[i].num_H == 0 ) { + return -1; /* program error */ + } +#endif + type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ + at[i].charge = 0; + AddOrRemoveExplOrImplH( -1, at, num_atoms, (AT_NUMB)i, t_group_info ); + /*at[i].num_H --;*/ + num_removed ++; +#if ( FIX_NORM_BUG_ADD_ION_PAIR == 1 ) + type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ +#else + type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* bug: subtract instead of add */ +#endif + /* + if ( nAtTypeTotals ) { + nAtTypeTotals[ATTOT_NUM_NP_Proton] --; + if ( at[i].num_H ) { + nAtTypeTotals[ATTOT_NUM_NP_H] ++; + } else { + nAtTypeTotals[ATTOT_NUM_NP] ++; + } + nAtTypeTotals[ATTOT_TOT_CHARGE] --; + nAtTypeTotals[ATTOT_NUM_CHARGES] --; + } + */ + } + } + return num_removed; +} + + +int bIsAtomTypeHard( inp_ATOM *at, int endpoint, int nType, int nMask, int nCharge ) +{ + int mask; + if ( (nType & GetAtomChargeType( at, endpoint, NULL, &mask, 0 )) && (mask & nMask) +#if ( OPPOSITE_CHARGE_IN_CGROUP == 0 ) + && ( at[endpoint].charge == nCharge || !at[endpoint].charge ) +#endif + ) { + return 1; + } + return 0; +} + + +int bIsHDonorAccAtomType( inp_ATOM *at, int endpoint, int *cSubType ) +{ + if ( bIsAtomTypeHard( at, endpoint, PR_HARD_TYP_H, PR_HARD_MSK_H, 0 ) ) { + /* obtain donor/acceptor info */ + int neutral_valence = at[endpoint].chem_bonds_valence + at[endpoint].num_H - at[endpoint].charge; + if ( neutral_valence != 2 /* O, S, Se, Te */ && + neutral_valence != 3 /* N, P */ ) { + return -1; /* wrong endpoint neutral valence */ + } else { + int edge_flow = at[endpoint].num_H; + int num_bonds = at[endpoint].valence; + int edge_cap = neutral_valence - num_bonds; /* does not allow to reduce -NH3(+) to #N or -OH(+)- to -O- */ + edge_flow = inchi_min( edge_flow, edge_cap); + /* what this means: */ + if ( edge_cap ) { + if ( edge_cap > edge_flow ) + *cSubType |= SALT_ACCEPTOR; + if ( edge_flow ) + *cSubType |= SALT_DONOR_H; + return 4; + } + } + } + return -1; +} + + +int bIsNegAtomType( inp_ATOM *at, int endpoint, int *cSubType ) +{ + int sub_type = 0; + if ( bIsAtomTypeHard( at, endpoint, PR_HARD_TYP_NEG, PR_HARD_MSK_NEG, -1 ) ) { + /* obtain donor/acceptor info */ + int neutral_valence = at[endpoint].chem_bonds_valence + at[endpoint].num_H - at[endpoint].charge; + if ( neutral_valence != 2 /* O, S, Se, Te */ && + neutral_valence != 3 /* N, P */ ) { + return -1; /* wrong endpoint neutral valence */ + } else { + int edge_flow = (at[endpoint].charge == -1); + int num_bonds = at[endpoint].valence; + int edge_cap = neutral_valence - num_bonds - at[endpoint].num_H; /* does not allow to reduce -NH3(+) to #N or -OH(+)- to -O- */ + edge_flow = inchi_min( edge_flow, edge_cap); + /* what this means: */ + if ( edge_cap ) { + if ( edge_cap > edge_flow ) + sub_type |= SALT_ACCEPTOR; + if ( edge_flow ) { + sub_type |= SALT_DONOR_Neg; + } + if ( sub_type ) { + *cSubType |= sub_type; + return 4; + } + } + } + } + return -1; +} + + +int bIsHardRemHCandidate( inp_ATOM *at, int i, int *cSubType ) +{ + int ret1, ret2, ret; + int sub_type = 0; + ret1 = bIsHDonorAccAtomType( at, i, &sub_type ); + ret2 = bIsNegAtomType( at, i, &sub_type ); + ret = inchi_max(ret1, ret2); + if ( ret > 0 && sub_type ) { + *cSubType |= sub_type; + return ret; + } + return -1; +} + + +int CreateCGroupInBnStruct( inp_ATOM *at, + int num_atoms, + BN_STRUCT *pBNS, + int nType, + int nMask, + int nCharge ) +{ + int k, c_point, cg, centerpoint, fictpoint, type, ret = 0; + int num_cg = 1; + int num_edges = pBNS->num_edges; + int num_vertices = pBNS->num_vertices; /* new c-group bns-ID */ + BNS_VERTEX *vert_ficpoint, *ver_ficpont_prev; /* fictitious vertex describing charge c-group */ + BNS_VERTEX *vertex_cpoint; + BNS_EDGE *edge; /* edge between that vertex and the tautomeric c_point */ + int mask, num_CPoints; + + /* Debug: check overflow */ + if ( num_vertices + num_cg >= pBNS->max_vertices ) { + return BNS_VERT_EDGE_OVFL; + } + /* count new c-group edges */ + for ( c_point = 0, num_CPoints = 0; c_point < num_atoms; c_point ++ ) { + if ( (nType & GetAtomChargeType( at, c_point, NULL, &mask, 0 )) && (mask & nMask) +#if ( OPPOSITE_CHARGE_IN_CGROUP == 0 ) + && ( at[c_point].charge == nCharge || !at[c_point].charge ) +#endif + ) { + num_CPoints ++; + } + } + if ( !num_CPoints ) { + return 0; + } + + + + /* clear the new vertex */ + memset( pBNS->vert+num_vertices, 0, 1*sizeof(pBNS->vert[0]) ); + /* *old* Make sure the last t-group has the largest t-group ID: + this is necessary to correctly add new edges and vertices for testing augmenting paths + */ + /**************************************/ + /* initialize new fictitious vertex */ + /* representing c-point group */ + /**************************************/ + ver_ficpont_prev = pBNS->vert+num_vertices - 1; + + for ( cg = 0; cg < num_cg; cg ++, ver_ficpont_prev = vert_ficpoint ) { + /* + vert_ficpoint-1 is the last vertex; + vert_ficpoint is the being added vertex + Note: nGroupNumber are not contiguous + */ + vert_ficpoint = pBNS->vert+num_vertices + cg; + vert_ficpoint->iedge = ver_ficpont_prev->iedge + ver_ficpont_prev->max_adj_edges; + vert_ficpoint->max_adj_edges = num_CPoints+BNS_ADD_EDGES; + vert_ficpoint->num_adj_edges = 0; + vert_ficpoint->st_edge.flow = vert_ficpoint->st_edge.flow0 = 0; + vert_ficpoint->st_edge.cap = vert_ficpoint->st_edge.cap0 = 0; + vert_ficpoint->type = BNS_VERT_TYPE_C_GROUP | ((nCharge<0)?BNS_VERT_TYPE_C_NEGATIVE:0); + } + + /************************************************/ + /* connect c-points to the fictitious vertices */ + /* representing c-point groups; set caps, flows */ + /************************************************/ + cg = 1; + for ( c_point = 0; c_point < num_atoms; c_point ++ ) { + if ( (nType & (type=GetAtomChargeType( at, c_point, NULL, &mask, 0 ))) && (mask & nMask) +#if ( OPPOSITE_CHARGE_IN_CGROUP == 0 ) + && ( at[c_point].charge == nCharge || !at[c_point].charge) +#endif + ); + else + continue; + fictpoint = cg + num_vertices - 1; /* c-group vertex index */ + vert_ficpoint = pBNS->vert + fictpoint; /* c-group vertex */ + vertex_cpoint = pBNS->vert + c_point; /* c_point vertex */ + /* Debug: check overflow */ + if ( fictpoint >= pBNS->max_vertices || + num_edges >= pBNS->max_edges || + vert_ficpoint->num_adj_edges >= vert_ficpoint->max_adj_edges || + vertex_cpoint->num_adj_edges >= vertex_cpoint->max_adj_edges ) { + ret = BNS_VERT_EDGE_OVFL; + break; + } + vertex_cpoint->type |= BNS_VERT_TYPE_C_POINT; + if ( (KNOWN_ACIDIC_TYPE & type) && nCharge < 0 ) { + vertex_cpoint->type |= pBNS->type_TACN; + } +#if ( FIX_CPOINT_BOND_CAP != 1 ) /* { */ + /* set capacity = 1 to the edges from the c_point to the centerpoint(s) */ + /* if their current capacity is zero */ + /* the centerpoint is any adjacent atom that is adjacent to a multiple bond */ + for ( k = 0; k < vertex_cpoint->num_adj_edges; k ++ ) { + int iedge = vertex_cpoint->iedge[k]; + if ( !pBNS->edge[iedge].cap ) { + /* single bond, possibly between c_point and centerpoint */ + centerpoint = (pBNS->edge[iedge].neighbor12 ^ c_point); + if ( centerpoint < pBNS->num_atoms && + pBNS->vert[centerpoint].st_edge.cap >= 1 ) { + int bond_type = (at[c_point].bond_type[k] & BOND_TYPE_MASK); + if ( bond_type == BOND_TAUTOM || + bond_type == BOND_ALTERN || + bond_type == BOND_SINGLE ) { + pBNS->edge[iedge].cap = 1; + } + } + } + } +#endif /* } FIX_CPOINT_BOND_CAP */ + /* create a new edge connecting c_point to the new fictitious c-group vertex vert_ficpoint */ + edge = pBNS->edge + num_edges; + edge->cap = 1; + edge->flow = 0; + edge->pass = 0; +#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) + edge->forbidden &= pBNS->edge_forbidden_mask; /* remove previous temporary ban */ +#endif + /* nCharge = +1: mark edge to c-point having no (+)-moveable charge with flow=1 */ + /* nCharge = -1: mark edge to c-point having -1 moveable charge with flow=1 */ + if ( nCharge==1 && at[c_point].charge != 1 || nCharge==-1 && at[c_point].charge == -1 ) + /*if ( !CHARGED_CPOINT(at,c_point) )*/ + { + /* increment new edge flow, update st_edges of the adjacent vertices */ + edge->flow ++; + /* increment c-group vertex st-flow & cap */ + vert_ficpoint->st_edge.flow ++; + vert_ficpoint->st_edge.cap ++; + /* increment c-point vertex st-flow & cap */ + vertex_cpoint->st_edge.flow ++; + vertex_cpoint->st_edge.cap ++; + } +#if ( FIX_CPOINT_BOND_CAP == 1 ) /* { */ + /* set capacity = 1 to the edges from the c_point to the centerpoint(s) */ + /* if their current capacity is zero */ + /* the centerpoint is any adjacent atom that is adjacent to a multiple bond */ + for ( k = 0; k < vertex_cpoint->num_adj_edges; k ++ ) { + int iedge = vertex_cpoint->iedge[k]; + VertexFlow nNewCap = vertex_cpoint->st_edge.cap; + centerpoint = (pBNS->edge[iedge].neighbor12 ^ c_point); + if ( !pBNS->edge[iedge].cap ) { + /* single bond, possibly between c_point and centerpoint */ + if ( centerpoint < pBNS->num_atoms && + pBNS->vert[centerpoint].st_edge.cap >= 1 ) { + nNewCap = inchi_min( pBNS->vert[centerpoint].st_edge.cap, nNewCap ); + nNewCap = inchi_min( nNewCap, MAX_BOND_EDGE_CAP ); + pBNS->edge[iedge].cap = nNewCap; + } + } +#if ( FIX_CPOINT_BOND_CAP2 == 1 ) /* multiple bond */ + else + if ( centerpoint < pBNS->num_atoms && + edge->flow && pBNS->edge[iedge].cap < MAX_BOND_EDGE_CAP ) { + pBNS->edge[iedge].cap ++; + } +#endif + } +#endif /* } FIX_CPOINT_BOND_CAP */ + /* connect edge to c_point and fictpoint and increment the counters of neighbors and edges */ + edge->neighbor1 = c_point; /* the smallest out of v1=endopoint and v2=num_vertices */ + edge->neighbor12 = c_point ^ fictpoint; /* v1 ^ v2 */ + vertex_cpoint->iedge[vertex_cpoint->num_adj_edges] = num_edges; + vert_ficpoint->iedge[vert_ficpoint->num_adj_edges] = num_edges ++; + edge->neigh_ord[0] = vertex_cpoint->num_adj_edges ++; + edge->neigh_ord[1] = vert_ficpoint->num_adj_edges ++; + edge->cap0 = edge->cap; + edge->flow0 = edge->flow; + } + ret = pBNS->num_vertices; /* new c-group atom number */ + pBNS->num_edges = num_edges; + pBNS->num_vertices += num_cg; + pBNS->num_c_groups += num_cg; + + return ret; +} + + +int CreateTGroupInBnStruct( inp_ATOM *at, + int num_atoms, + BN_STRUCT *pBNS, + int nType, + int nMask ) +{ + int ret = 0; + /* ret = ReInitBnStruct( pBNS ); */ + int k, endpoint, tg, centerpoint, fictpoint; + int num_tg = 1; + int num_edges = pBNS->num_edges; + int num_vertices = pBNS->num_vertices; + BNS_VERTEX *vert_ficpoint, *ver_ficpont_prev; /* fictitious vertex describing t-group */ + BNS_VERTEX *vert_endpoint; + BNS_EDGE *edge; /* edge between that vertex and the tautomeric endpoint */ + int mask, num_endpoints, neutral_valence, edge_flow, edge_cap, num_bonds; + + /* Debug: check overflow */ + if ( num_vertices + num_tg >= pBNS->max_vertices ) { + return BNS_VERT_EDGE_OVFL; + } + /* count new t-group edges */ + for ( endpoint = 0, num_endpoints = 0; endpoint < num_atoms; endpoint ++ ) { + if ( (nType & GetAtomChargeType( at, endpoint, NULL, &mask, 0 )) && (mask & nMask) + ) { + num_endpoints ++; + } + } + if ( !num_endpoints ) { + return 0; + } + + + /* since t-group IDs may be not contiguous, clear all vertices that will be added. + all-zeroes-vertex will be ignored by the BNS + */ + memset( pBNS->vert+num_vertices, 0, num_tg*sizeof(pBNS->vert[0]) ); + /* *old* Make sure the last t-group has the largest t-group ID: + this is necessary to correctly add new edges and vertices for testing augmenting paths + */ + /**************************************/ + /* initialize new fictitious vertex */ + /* representing t-point group */ + /**************************************/ + ver_ficpont_prev = pBNS->vert+num_vertices - 1; + + for ( tg = 0; tg < num_tg; tg ++, ver_ficpont_prev = vert_ficpoint ) { + /* + vert_ficpoint-1 is the last vertex; + vert_ficpoint is the vertex that is being added + Note: nGroupNumber are not contiguous + */ + vert_ficpoint = pBNS->vert+num_vertices + tg; + vert_ficpoint->iedge = ver_ficpont_prev->iedge + ver_ficpont_prev->max_adj_edges; + vert_ficpoint->max_adj_edges = num_endpoints+BNS_ADD_EDGES+BNS_ADD_SUPER_TGROUP; + vert_ficpoint->num_adj_edges = 0; + vert_ficpoint->st_edge.flow = vert_ficpoint->st_edge.flow0 = 0; + vert_ficpoint->st_edge.cap = vert_ficpoint->st_edge.cap0 = 0; + vert_ficpoint->type |= BNS_VERT_TYPE_TGROUP; + } + tg = 1; + for ( endpoint = 0; endpoint < num_atoms; endpoint ++ ) { + if ( (nType & GetAtomChargeType( at, endpoint, NULL, &mask, 0 )) && (mask & nMask)); + else + continue; + fictpoint = tg + num_vertices - 1; + vert_ficpoint = pBNS->vert + fictpoint; + vert_endpoint = pBNS->vert + endpoint; + /* Debug: check overflow */ + if ( fictpoint >= pBNS->max_vertices || + num_edges >= pBNS->max_edges || + vert_ficpoint->num_adj_edges >= vert_ficpoint->max_adj_edges || + vert_endpoint->num_adj_edges >= vert_endpoint->max_adj_edges ) { + ret = BNS_VERT_EDGE_OVFL; + break; + } + /* obtain donor/acceptor info */ + neutral_valence = at[endpoint].chem_bonds_valence + at[endpoint].num_H - at[endpoint].charge; + if ( neutral_valence != 2 /* O, S, Se, Te */ && + neutral_valence != 3 /* N, P */ ) { + ret = BNS_PROGRAM_ERR; /* wrong endpoint neutral valence */ + break; + } + edge_flow = at[endpoint].num_H; + num_bonds = at[endpoint].valence; + edge_cap = neutral_valence - num_bonds; /* does not allow to reduce -NH3(+) to #N or -OH(+)- to -O- */ + if ( 3 == neutral_valence /* N or P */ && 1 < num_bonds ) { + edge_cap ++; /* allow -NH2(+)- => -N=, >NH(+)- => >N- */ + } + edge_flow = inchi_min( edge_flow, edge_cap); + /* + if ( !nGetEndpointInfo( at, endpoint, &eif ) ) { + ret = BNS_BOND_ERR; + break; + } + */ + vert_endpoint->type |= BNS_VERT_TYPE_ENDPOINT; + /* create a new edge connecting endpoint to the new fictitious t-group vertex vert_ficpoint */ + edge = pBNS->edge + num_edges; + edge->cap = edge_cap; + edge->flow = edge_flow; + edge->pass = 0; +#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) + edge->forbidden &= pBNS->edge_forbidden_mask; +#endif + /* adjust st_flow and st_cap of the adjacent vertices */ + /* adjust t-group vertex st-flow & cap */ + vert_ficpoint->st_edge.flow += edge->flow; + vert_ficpoint->st_edge.cap += edge->flow; + /* adjust endpoint vertex st-flow & cap */ + vert_endpoint->st_edge.flow += edge->flow; + vert_endpoint->st_edge.cap += edge->flow; + + /* adjust edge cap & flow according to the number of H and number of bonds */ + for ( k = 0; k < vert_endpoint->num_adj_edges; k ++ ) { + int iedge = vert_endpoint->iedge[k]; + VertexFlow nNewCap = vert_endpoint->st_edge.cap; + if ( !pBNS->edge[iedge].cap ) { + /* single bond, possibly between endpoint and centerpoint */ + centerpoint = (pBNS->edge[iedge].neighbor12 ^ endpoint); + if ( centerpoint < pBNS->num_atoms && + pBNS->vert[centerpoint].st_edge.cap >= 1 ) { + nNewCap = inchi_min( pBNS->vert[centerpoint].st_edge.cap, nNewCap ); + nNewCap = inchi_min( nNewCap, MAX_BOND_EDGE_CAP ); + pBNS->edge[iedge].cap = nNewCap; + } + } + } + + /* connect edge to endpoint and fictpoint and increment the counters of neighbors and edges */ + edge->neighbor1 = endpoint; /* the smallest out of v1=endopoint and v2=num_vertices */ + edge->neighbor12 = endpoint ^ fictpoint; /* v1 ^ v2 */ + vert_endpoint->iedge[vert_endpoint->num_adj_edges] = num_edges; + vert_ficpoint->iedge[vert_ficpoint->num_adj_edges] = num_edges ++; + edge->neigh_ord[0] = vert_endpoint->num_adj_edges ++; + edge->neigh_ord[1] = vert_ficpoint->num_adj_edges ++; + edge->cap0 = edge->cap; + edge->flow0 = edge->flow; + } + + ret = pBNS->num_vertices; /* new t-group atom number */ + pBNS->num_edges = num_edges; + pBNS->num_vertices += num_tg; + pBNS->num_t_groups += num_tg; + + return ret; +} + + +int RemoveLastGroupFromBnStruct( inp_ATOM *at, + int num_atoms, + int tg, + BN_STRUCT *pBNS ) +{ + int ret = 0; + /* ret = ReInitBnStruct( pBNS ); */ + int k, endpoint, /*centerpoint, fictpoint,*/ iedge; + int num_edges = pBNS->num_edges; + int num_vertices = pBNS->num_vertices; + BNS_VERTEX *vert_ficpoint /*, *ver_ficpont_prev*/; /* fictitious vertex describing t-group */ + BNS_VERTEX *vert_endpoint; + BNS_EDGE *edge; /* edge between that vertex and the tautomeric endpoint */ + /*int mask, num_endpoints, neutral_valence, edge_flow, edge_cap, num_bonds;*/ + int is_t_group = 0, is_c_group = 0; + + /* Debug: check overflow */ + if ( pBNS->num_added_atoms + pBNS->num_c_groups + pBNS->num_t_groups + num_atoms >= pBNS->max_vertices ) { + return BNS_VERT_EDGE_OVFL; + } + if ( tg + 1 != num_vertices ) { + return BNS_VERT_EDGE_OVFL; + } + vert_ficpoint = pBNS->vert + tg; + if ( vert_ficpoint->type & BNS_VERT_TYPE_TGROUP ) { + is_t_group = 1; + } + if ( vert_ficpoint->type & BNS_VERT_TYPE_C_GROUP ) { + is_c_group = 1; + if ( vert_ficpoint->type & BNS_VERT_TYPE_C_NEGATIVE ) + is_c_group = 2; + } + for ( k = vert_ficpoint->num_adj_edges-1; 0 <= k; k -- ) { + iedge = vert_ficpoint->iedge[k]; + if ( iedge + 1 != num_edges ) { + return BNS_VERT_EDGE_OVFL; + } + edge = pBNS->edge + iedge; + endpoint = edge->neighbor12 ^ tg; + vert_endpoint = pBNS->vert + endpoint; + /* adjust st_flow, st_cap */ + vert_endpoint->st_edge.cap0 = + vert_endpoint->st_edge.cap -= edge->flow; + vert_endpoint->st_edge.flow0 = + vert_endpoint->st_edge.flow -= edge->flow; + if ( pBNS->type_TACN && (vert_endpoint->type & pBNS->type_TACN) == pBNS->type_TACN ) { + vert_endpoint->type ^= pBNS->type_TACN; + } + if ( is_t_group ) { + vert_endpoint->type ^= (vert_ficpoint->type & BNS_VERT_TYPE_ENDPOINT); + } + if ( is_c_group ) { + vert_endpoint->type ^= (vert_ficpoint->type & BNS_VERT_TYPE_C_POINT); + } + /* remove edge */ + if ( edge->neigh_ord[0]+1 != vert_endpoint->num_adj_edges ) { + return BNS_VERT_EDGE_OVFL; + } + vert_endpoint->num_adj_edges --; + memset( edge, 0, sizeof(*edge) ); + num_edges --; + if ( 1 == is_t_group && endpoint < num_atoms ) { + at->endpoint = 0; + } + if ( 1 == is_c_group && endpoint < num_atoms ) { + at->c_point = 0; + } + } + memset( vert_ficpoint, 0, sizeof(*vert_ficpoint) ); + num_vertices --; + + pBNS->num_edges = num_edges; + pBNS->num_vertices = num_vertices; + if ( is_t_group ) + pBNS->num_t_groups --; + if ( is_c_group ) + pBNS->num_c_groups --; + + return ret; +} + + +int SetInitCapFlowToCurrent( BN_STRUCT *pBNS ) +{ + int i, j; + BNS_EDGE *pEdge=NULL; + for ( i = 0; i < pBNS->num_vertices; i ++ ) { + pBNS->vert[i].st_edge.flow0 = pBNS->vert[i].st_edge.flow; + pBNS->vert[i].st_edge.cap0 = pBNS->vert[i].st_edge.cap; + for ( j = 0; j < pBNS->vert[i].num_adj_edges; j ++ ) { + pEdge = pBNS->edge + pBNS->vert[i].iedge[j]; + pEdge->cap0 = pEdge->cap; + pEdge->flow0 = pEdge->flow; + } + } + return 0; +} + + +int ArTypMask[] = { + AR_SIMPLE_TYP1, AR_SIMPLE_MSK1, + AR_SIMPLE_TYP2, AR_SIMPLE_MSK2, + AR_SIMPLE_TYP3, AR_SIMPLE_MSK3, + 0, 0 }; + + + +int SimpleRemoveAcidicProtons( inp_ATOM *at, + int num_atoms, + BN_AATG *pAATG, + int num2remove ) +{ + int i, j, max_j=-1, mask, type, num_removed; + int num[AR_SIMPLE_STEPS+1], num_tot; + + for ( j = 0; ArTypMask[2*j]; j ++ ) { + num[max_j = j] = 0; + } + + for ( i = 0; i < num_atoms; i ++ ) { + if ( !at[i].charge && at[i].num_H && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { + for ( j = 0; j <= max_j; j ++ ) { + if ( (type & ArTypMask[2*j]) && (mask && ArTypMask[2*j+1]) ) { + num[j] ++; + break; + } + } + } + } + for ( j = 0, num_tot = 0; j <= max_j; j ++ ) { + if ( (num_tot += num[j]) >= num2remove ) { + max_j = j; + break; + } + } + if ( !num_tot ) { + return 0; + } + for ( i = 0, num_removed = 0; i < num_atoms && num_removed < num2remove; i ++ ) { + if ( !at[i].charge && at[i].num_H && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { + for ( j = 0; j <= max_j; j ++ ) { + if ( num[j] && (type & ArTypMask[2*j]) && (mask && ArTypMask[2*j+1]) ) { + type = GetAtomChargeType( at, i, pAATG->nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ + num[j] --; + at[i].charge --; + AddOrRemoveExplOrImplH( -1, at, num_atoms, (AT_NUMB)i, pAATG->t_group_info ); + /*at[i].num_H --;*/ + num_removed ++; + type = GetAtomChargeType( at, i, pAATG->nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ + break; + } + } + } + } + /* + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] -= num_removed; + pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] += num_removed; + */ + return num_removed; +} + + +int bHasAcidicHydrogen( inp_ATOM *at, int i ) +{ + int bFound = 0, j, type, mask; + if ( !at[i].charge && at[i].num_H && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { + for ( j = 0; ArTypMask[2*j]; j ++ ) { + if ( (type & ArTypMask[2*j]) && (mask & ArTypMask[2*j+1]) ) { + bFound ++; + break; + } + } + } + return bFound; +} + + +int bHasOtherExchangableH ( inp_ATOM *at, int i ) +{ + int bFound = 0, type, mask; + if ( at[i].num_H && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { + if ( (type & ATT_ATOM_N) && (mask & ATBIT_NP_H) ) { + bFound ++; + } + } + return bFound; +} + + +int AaTypMask[] = { + AA_SIMPLE_TYP1, AA_SIMPLE_MSK1, +#if ( FIX_NP_MINUS_BUG == 1 ) + AA_SIMPLE_TYP4, AA_SIMPLE_MSK4, /* should not follow 0,0 pair */ +#endif + AA_SIMPLE_TYP2, AA_SIMPLE_MSK2, + AA_SIMPLE_TYP3, AA_SIMPLE_MSK3, + 0, 0 }; + + +int SimpleAddAcidicProtons( inp_ATOM *at, + int num_atoms, + BN_AATG *pAATG, int num2add ) +{ + int i, j, max_j=-1, mask, type, num_added; + int num[AR_SIMPLE_STEPS+1], num_tot; + + for ( j = 0; AaTypMask[2*j]; j ++ ) { + num[max_j = j] = 0; + } + + for ( i = 0; i < num_atoms; i ++ ) { + if ( at[i].charge==-1 && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { + for ( j = 0; j <= max_j; j ++ ) { + if ( (type & AaTypMask[2*j]) && (mask && AaTypMask[2*j+1]) ) { + num[j] ++; + break; + } + } + } + } + for ( j = 0, num_tot = 0; j <= max_j; j ++ ) { + if ( (num_tot += num[j]) >= num2add ) { + max_j = j; + break; + } + } + if ( !num_tot ) { + return 0; + } + for ( i = 0, num_added = 0; i < num_atoms && num_added < num2add; i ++ ) { + if ( at[i].charge==-1 && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) { + for ( j = 0; j <= max_j; j ++ ) { + if ( num[j] && (type & AaTypMask[2*j]) && (mask && AaTypMask[2*j+1]) ) { + type = GetAtomChargeType( at, i, pAATG->nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ + num[j] --; + at[i].charge ++; + AddOrRemoveExplOrImplH( 1, at, num_atoms, (AT_NUMB)i, pAATG->t_group_info ); + /*at[i].num_H ++;*/ + num_added ++; + type = GetAtomChargeType( at, i, pAATG->nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ + break; + } + } + } + } + /* + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] += num_added; + pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] -= num_added; + */ + return num_added; +} + + +int bHasAcidicMinus( inp_ATOM *at, int i ) +{ + int bFound = 0, j, type, mask; + if ( at[i].charge==-1 && (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) + { + for ( j = 0; AaTypMask[2*j]; j ++ ) + { + if ( (type & AaTypMask[2*j]) && (mask & AaTypMask[2*j+1]) ) + { + bFound ++; + break; + } + } + } + return bFound; +} + + +/* + HardRemoveAcidicProtons( ... ) + + Create 2 tautomeric groups: + (1) for O on -C=O, + (2) for the rest of the atoms. + Pull H from (2) to (1); remove later +*/ +int HardRemoveAcidicProtons( CANON_GLOBALS *pCG, + inp_ATOM *at, + int num_atoms, + BN_AATG *pAATG, + int num2remove, + int *nNumCanceledCharges, + BN_STRUCT *pBNS, + BN_DATA *pBD ) +{ + int cg_Plus = 0; + int cg_Minus = 0; + int tg_H_Other = 0; + int tg_H_Acid = 0; + + int ret = 0, ret2; + int nDelta, nNumChanges = 0, nNumMoved2AcidH = 0, nNumNeutralized = 0, nPrevNumCharges; + + int nPosCharges, nPosCharges2; + int nNegCharges, nNegCharges2; + /* + int nNumNP_H, nNumNP_H2; + int nNumOS_H, nNumOS_H2; + */ + + nPosCharges = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; + nNegCharges = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; + /* + nNumNP_H = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] + + pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton]; + nNumOS_H = pAATG->nAtTypeTotals[ATTOT_NUM_COH] + + pAATG->nAtTypeTotals[ATTOT_NUM_CSH] + + pAATG->nAtTypeTotals[ATTOT_NUM_ZOH]; + */ + + /* prevent free exchange H <-> (-) */ + pBNS->type_CN = (BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE); + pBNS->type_T = BNS_VERT_TYPE_TGROUP; + pBNS->type_TACN = BNS_VERT_TYPE_ACID; + /* create (+) charge group */ + cg_Plus = CreateCGroupInBnStruct( at, num_atoms, pBNS, AR_HARD_TYP_POS, AR_HARD_MSK_POS, 1 ); + /* create (-) charge group */ + /* + if ( nAtTypeTotals[ATTOT_NUM_CO_Minus] + + nAtTypeTotals[ATTOT_NUM_CS_Minus] + + nAtTypeTotals[ATTOT_NUM_ZO_Minus] + + nAtTypeTotals[ATTOT_NUM_N_Minus] ) + */ + cg_Minus = CreateCGroupInBnStruct( at, num_atoms, pBNS, AR_HARD_TYP_NEG, AR_HARD_MSK_NEG, -1 ); + + pBNS->type_CN = (BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE); + pBNS->type_T = BNS_VERT_TYPE_TGROUP; + pBNS->type_TACN = BNS_VERT_TYPE_ACID; + + /* create tautomeric group for non-acidic or negatively charged acidic O */ + tg_H_Other = CreateTGroupInBnStruct( at, num_atoms, pBNS, AR_HARD_TYP_HN, AR_HARD_MSK_HN ); + + /* create tautomeric group for possibly acidic O */ + tg_H_Acid = CreateTGroupInBnStruct( at, num_atoms, pBNS, AR_HARD_TYP_HA, AR_HARD_MSK_HA ); + if ( tg_H_Other >= num_atoms && tg_H_Acid >= num_atoms ) + { + /* find alt path to remove one proton */ + do + { + /* remove a proton */ + nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; + ret = bExistsAltPath( pCG, pBNS, pBD, pAATG, + at, num_atoms, + tg_H_Other /*nVertDoubleBond*/, + tg_H_Acid /*nVertSingleBond*/, + ALT_PATH_MODE_REM_PROTON ); + if ( IS_BNS_ERROR( ret ) ) + { + return ret; + } + if ( ret & 1 ) { + nDelta = (ret & ~3) >> 2; + nNumChanges += (0 != (ret & 2)); + if ( nDelta ) { + /* radical pair has disappeared */ + ; /* goto quick_exit;*/ + } + nNumMoved2AcidH ++; + if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + 1 ) { + nNumNeutralized += (nPrevNumCharges - (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - 1))/2; + } + } + } while ( (ret & 1) && nNumMoved2AcidH < num2remove ); + + /* neutralize: remove ion pairs like >N(+)=-O(-) => >N-=O */ + if ( (nNumMoved2AcidH /*|| bCancelChargesAlways*/) && cg_Minus >= num_atoms && cg_Plus >= num_atoms && + pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] > abs(pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE]) ) { + do { + nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; + ret = bExistsAltPath( pCG, pBNS, pBD, pAATG, + at, num_atoms, + cg_Minus /*nVertDoubleBond*/, + cg_Plus /*nVertSingleBond*/, + ALT_PATH_MODE_REM_PROTON ); + if ( IS_BNS_ERROR( ret ) ) { + return ret; + } + if ( ret & 1 ) { + nDelta = (ret & ~3) >> 2; + nNumChanges += (0 != (ret & 2)); + if ( nDelta ) { + /* radical pair has disappeared */ + ; /* goto quick_exit;*/ + } + if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] ) { + nNumNeutralized += (nPrevNumCharges - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES])/2; + } + } + } while ( ret & 1 ); + } + } + + ret = 0; + if ( tg_H_Acid >= num_atoms ) { + ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, tg_H_Acid, pBNS ); + if ( !ret && ret2 ) + ret = ret2; + } + if ( tg_H_Other >= num_atoms ) { + ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, tg_H_Other, pBNS ); + if ( !ret && ret2 ) + ret = ret2; + } + if ( cg_Minus >= num_atoms ) { + ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Minus, pBNS ); + if ( !ret && ret2 ) + ret = ret2; + } + if ( cg_Plus >= num_atoms ) { + ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Plus, pBNS ); + if ( !ret && ret2 ) + ret = ret2; + } + + pBNS->type_CN = 0; + pBNS->type_T = 0; + pBNS->type_TACN = 0; + + if ( ret ) { + return ret; + } + if ( pAATG->nAtTypeTotals[ATTOT_NUM_CO_Minus] + pAATG->nAtTypeTotals[ATTOT_NUM_ZO_Minus] && + pAATG->nAtTypeTotals[ATTOT_NUM_N_Minus] ) { + } + + nPosCharges2 = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; + nNegCharges2 = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; + /* + nNumNP_H2 = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] + + pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton]; + nNumOS_H2 = pAATG->nAtTypeTotals[ATTOT_NUM_COH] + + pAATG->nAtTypeTotals[ATTOT_NUM_CSH] + + pAATG->nAtTypeTotals[ATTOT_NUM_ZOH]; + */ + if ( (nPosCharges - nNegCharges) - (nPosCharges2 - nNegCharges2) != 0 ) { + return BNS_PROGRAM_ERR; + } + + if ( nNumCanceledCharges ) { +#if ( FIX_CANCEL_CHARGE_COUNT_BUG == 1 ) + *nNumCanceledCharges += 2*nNumNeutralized; +#else + *nNumCanceledCharges = 2*nNumNeutralized; +#endif + } + + return nNumMoved2AcidH; +} + + +/* + HardAddAcidicProtons( ... ) +*/ +int HardAddAcidicProtons( CANON_GLOBALS *pCG, + inp_ATOM *at, + int num_atoms, + BN_AATG *pAATG, + int num2add, + int *nNumCanceledCharges, + BN_STRUCT *pBNS, + BN_DATA *pBD ) +{ + int cg_Plus = 0; + int cg_Minus_CO = 0; + int cg_Minus_Other = 0; + int tg_H = 0; + + int ret = 0, ret2; + int nDelta, nNumChanges = 0, nNumMoved2AcidMinus = 0, nNumNeutralized = 0, nPrevNumCharges; + + int nPosCharges, nPosCharges2; + int nNegCharges, nNegCharges2; + /* + int nNumNP_H, nNumNP_H2; + int nNumOS_H, nNumOS_H2; + */ + nPosCharges = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; + nNegCharges = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; + /* + nNumNP_H = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] + + pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton]; + nNumOS_H = pAATG->nAtTypeTotals[ATTOT_NUM_COH] + + pAATG->nAtTypeTotals[ATTOT_NUM_CSH] + + pAATG->nAtTypeTotals[ATTOT_NUM_ZOH]; + */ + /* prevent free exchange H <-> (-) */ + pBNS->type_CN = (BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE); + pBNS->type_T = BNS_VERT_TYPE_TGROUP; + pBNS->type_TACN = BNS_VERT_TYPE_ACID; + /* create (+) charge group */ + cg_Plus = CreateCGroupInBnStruct( at, num_atoms, pBNS, AA_HARD_TYP_POS, AA_HARD_MSK_POS, 1 ); + /* create (-) charge group */ + /* + if ( nAtTypeTotals[ATTOT_NUM_CO_Minus] + + nAtTypeTotals[ATTOT_NUM_CS_Minus] + + nAtTypeTotals[ATTOT_NUM_ZO_Minus] + + nAtTypeTotals[ATTOT_NUM_N_Minus] ) + */ + cg_Minus_CO = CreateCGroupInBnStruct( at, num_atoms, pBNS, AA_HARD_TYP_CO, AA_HARD_MSK_CO, -1 ); + + cg_Minus_Other = CreateCGroupInBnStruct( at, num_atoms, pBNS, AA_HARD_TYP_NEG, AA_HARD_MSK_NEG, -1 ); + + pBNS->type_CN = (BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE); + pBNS->type_T = BNS_VERT_TYPE_TGROUP; + pBNS->type_TACN = BNS_VERT_TYPE_ACID; + + /* create tautomeric group for all H */ + tg_H = CreateTGroupInBnStruct( at, num_atoms, pBNS, AA_HARD_TYP_H, AA_HARD_MSK_H ); + + + if ( cg_Minus_Other >= num_atoms && cg_Minus_CO >= num_atoms ) + { + /* find alt path to remove one proton */ + do + { + /* add a proton */ + nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; + ret = bExistsAltPath( pCG, pBNS, pBD, pAATG, + at, num_atoms, + cg_Minus_Other /*nVertDoubleBond*/, + cg_Minus_CO /*nVertSingleBond*/, + ALT_PATH_MODE_REM_PROTON ); + if ( IS_BNS_ERROR( ret ) ) + { + return ret; + } + if ( ret & 1 ) + { + nDelta = (ret & ~3) >> 2; + nNumChanges += (0 != (ret & 2)); + if ( nDelta ) + { + /* radical pair has disappeared */ + ; /* goto quick_exit;*/ + } + nNumMoved2AcidMinus ++; + if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + 1 ) + { + nNumNeutralized += (nPrevNumCharges - (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - 1))/2; + } + } + } while ( (ret & 1) && nNumMoved2AcidMinus < num2add ); + + /* neutralize: remove ion pairs like >N(+)=-O(-) => >N-=O */ + if ( (nNumMoved2AcidMinus /*|| bCancelChargesAlways*/) && + cg_Minus_Other >= num_atoms && cg_Plus >= num_atoms && + pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] > abs(pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE]) ) + { + do + { + nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; + ret = bExistsAltPath( pCG, pBNS, pBD, pAATG, + at, num_atoms, + cg_Minus_Other /*nVertDoubleBond*/, + cg_Plus /*nVertSingleBond*/, + ALT_PATH_MODE_REM_PROTON ); + if ( IS_BNS_ERROR( ret ) ) + { + return ret; + } + if ( ret & 1 ) + { + nDelta = (ret & ~3) >> 2; + nNumChanges += (0 != (ret & 2)); + if ( nDelta ) { + /* radical pair has disappeared */ + ; /* goto quick_exit;*/ + } + if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] ) { + nNumNeutralized += (nPrevNumCharges - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES])/2; + } + } + } while ( ret & 1 ); + } + } + + ret = 0; + if ( tg_H >= num_atoms ) { + ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, tg_H, pBNS ); + if ( !ret && ret2 ) + ret = ret2; + } + if ( cg_Minus_Other >= num_atoms ) { + ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Minus_Other, pBNS ); + if ( !ret && ret2 ) + ret = ret2; + } + if ( cg_Minus_CO >= num_atoms ) { + ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Minus_CO, pBNS ); + if ( !ret && ret2 ) + ret = ret2; + } + if ( cg_Plus >= num_atoms ) { + ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Plus, pBNS ); + if ( !ret && ret2 ) + ret = ret2; + } + + pBNS->type_CN = 0; + pBNS->type_T = 0; + pBNS->type_TACN = 0; + + if ( ret ) { + return ret; + } + if ( pAATG->nAtTypeTotals[ATTOT_NUM_CO_Minus] + pAATG->nAtTypeTotals[ATTOT_NUM_ZO_Minus] && + pAATG->nAtTypeTotals[ATTOT_NUM_N_Minus] ) { + } + + nPosCharges2 = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; + nNegCharges2 = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; + /* + nNumNP_H2 = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] + + pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton]; + nNumOS_H2 = pAATG->nAtTypeTotals[ATTOT_NUM_COH] + + pAATG->nAtTypeTotals[ATTOT_NUM_CSH] + + pAATG->nAtTypeTotals[ATTOT_NUM_ZOH]; + */ + if ( (nPosCharges - nNegCharges) - (nPosCharges2 - nNegCharges2) != 0 ) { + return BNS_PROGRAM_ERR; + } + + if ( nNumCanceledCharges ) { +#if ( FIX_CANCEL_CHARGE_COUNT_BUG == 1 ) + *nNumCanceledCharges += 2*nNumNeutralized; +#else + *nNumCanceledCharges = 2*nNumNeutralized; +#endif + } + + return nNumMoved2AcidMinus; +} + + +/* + HardRemoveHplusNP( ... ) + + Examples include removal of H from tautomeric O + that belongs to the same t-group as N: + + >N(+)=-N=-OH =(taut.) => + >N(+)=-NH-=O =(+charge move) => + >N-=NH(+)-=O => >N-=N-=O + H(+) + +*/ +int HardRemoveHplusNP( CANON_GLOBALS *pCG, + inp_ATOM *at, int num_atoms, + int bCancelChargesAlways, + int *nNumCanceledCharges, + BN_AATG *pAATG, BN_STRUCT *pBNS, BN_DATA *pBD ) +{ + + int cg_Plus = 0; + int cg_Minus = 0; + int tg_H = 0; +#if ( MOVE_PPLUS_TO_REMOVE_PROTONS == 1 ) + int cg_PlusP = 0; +#endif +#if ( FIX_REM_PROTON_COUNT_BUG == 1 ) + int nPrevRemovedProtons, nCurrRemovedProtons; +#endif + int ret = 0, ret2; + int nDelta, nNumChanges = 0, nNumRemovedProtons = 0, nNumNeutralized = 0, nPrevNumCharges; + + int nPosCharges, nPosCharges2; + int nNegCharges, nNegCharges2; + /* + int nNumNP_H, nNumNP_H2; + int nNumOS_H, nNumOS_H2; + */ + + nPosCharges = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; + nNegCharges = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; + /* + nNumNP_H = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] + + pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton]; + nNumOS_H = pAATG->nAtTypeTotals[ATTOT_NUM_COH] + + pAATG->nAtTypeTotals[ATTOT_NUM_CSH] + + pAATG->nAtTypeTotals[ATTOT_NUM_ZOH]; + */ + /* prevent free exchange H <-> (-) */ + pBNS->type_CN = (BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE); + pBNS->type_T = BNS_VERT_TYPE_TGROUP; + pBNS->type_TACN = BNS_VERT_TYPE_ACID; + /* create (+) charge group */ + cg_Plus = CreateCGroupInBnStruct( at, num_atoms, pBNS, PR_HARD_TYP_POS, PR_HARD_MSK_POS, 1 ); + /* create (-) charge group */ + /* + if ( nAtTypeTotals[ATTOT_NUM_CO_Minus] + + nAtTypeTotals[ATTOT_NUM_CS_Minus] + + nAtTypeTotals[ATTOT_NUM_ZO_Minus] + + nAtTypeTotals[ATTOT_NUM_N_Minus] ) + */ +#if ( MOVE_PPLUS_TO_REMOVE_PROTONS == 1 ) + cg_PlusP = CreateCGroupInBnStruct( at, num_atoms, pBNS, PR_HARD_TYP_POSP, PR_HARD_MSK_POS, 1 ); +#endif + cg_Minus = CreateCGroupInBnStruct( at, num_atoms, pBNS, PR_HARD_TYP_NEG, PR_HARD_MSK_NEG, -1 ); + + /* create single tautomeric group */ + tg_H = CreateTGroupInBnStruct( at, num_atoms, pBNS, PR_HARD_TYP_H, PR_HARD_MSK_H ); + + if ( tg_H >= num_atoms && cg_Plus >= num_atoms ) { + +#if ( FIX_N_MINUS_NORN_BUG == 1 ) + /* neutralize: remove ion pairs like >N(+)=-O(-) => >N-=O; >N(+)=-NH(-) => >N-=NH */ + if ( (nNumRemovedProtons || bCancelChargesAlways) && cg_Minus >= num_atoms && cg_Plus >= num_atoms && + pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] > abs(pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE]) ) { + do { + nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; +#if ( FIX_REM_PROTON_COUNT_BUG == 1 ) + nPrevRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons; +#endif + ret = bExistsAltPath( pCG, pBNS, pBD, pAATG, at, num_atoms, + cg_Minus /*nVertDoubleBond*/, cg_Plus /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON ); + if ( IS_BNS_ERROR( ret ) ) { + return ret; + } +#if ( FIX_REM_PROTON_COUNT_BUG == 1 ) + nCurrRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons; + if ( nCurrRemovedProtons != nPrevRemovedProtons ) { + return BNS_RADICAL_ERR; + } +#endif + if ( ret & 1 ) { + nDelta = (ret & ~3) >> 2; + nNumChanges += (0 != (ret & 2)); + if ( nDelta ) { + /* radical pair has disappeared */ + ; /* goto quick_exit;*/ + } + if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] ) { + nNumNeutralized += (nPrevNumCharges - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES])/2; + } + } + } while ( ret & 1 ); + } +#endif + /* find alt path to remove one proton */ + do { + /* remove a proton */ + nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; +#if ( FIX_REM_PROTON_COUNT_BUG == 1 ) + nPrevRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons; +#endif + ret = bExistsAltPath( pCG, pBNS, pBD, pAATG, at, num_atoms, + tg_H /*nVertDoubleBond*/, cg_Plus /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON ); + if ( IS_BNS_ERROR( ret ) ) { + return ret; + } +#if ( FIX_REM_PROTON_COUNT_BUG == 1 ) + nCurrRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons; + if ( nCurrRemovedProtons != nPrevRemovedProtons + (ret & 1) ) { + return BNS_RADICAL_ERR; + } +#endif + if ( ret & 1 ) { + nDelta = (ret & ~3) >> 2; + nNumChanges += (0 != (ret & 2)); + if ( nDelta ) { + /* radical pair has disappeared */ + ; /* goto quick_exit;*/ + } + nNumRemovedProtons ++; + if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + 1 ) { + nNumNeutralized += (nPrevNumCharges - (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - 1))/2; + } + } + } while ( ret & 1 ); + + /* neutralize: remove ion pairs like >N(+)=-O(-) => >N-=O */ + if ( (nNumRemovedProtons || bCancelChargesAlways) && cg_Minus >= num_atoms && cg_Plus >= num_atoms && + pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] > abs(pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE]) ) { + do { + nPrevNumCharges = pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES]; +#if ( FIX_REM_PROTON_COUNT_BUG == 1 ) + nPrevRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons; +#endif + ret = bExistsAltPath( pCG, pBNS, pBD, pAATG, at, num_atoms, + cg_Minus /*nVertDoubleBond*/, cg_Plus /*nVertSingleBond*/, ALT_PATH_MODE_REM_PROTON ); + if ( IS_BNS_ERROR( ret ) ) { + return ret; + } +#if ( FIX_REM_PROTON_COUNT_BUG == 1 ) + nCurrRemovedProtons = pAATG->t_group_info->tni.nNumRemovedProtons; + if ( nCurrRemovedProtons != nPrevRemovedProtons ) { + return BNS_RADICAL_ERR; + } +#endif + if ( ret & 1 ) { + nDelta = (ret & ~3) >> 2; + nNumChanges += (0 != (ret & 2)); + if ( nDelta ) { + /* radical pair has disappeared */ + ; /* goto quick_exit;*/ + } + if ( nPrevNumCharges > pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] ) { + nNumNeutralized += (nPrevNumCharges - pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES])/2; + } + } + } while ( ret & 1 ); + } + } + ret = 0; + if ( tg_H >= num_atoms ) { + ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, tg_H, pBNS ); + if ( !ret && ret2 ) + ret = ret2; + } + if ( cg_Minus >= num_atoms ) { + ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Minus, pBNS ); + if ( !ret && ret2 ) + ret = ret2; + } +#if ( MOVE_PPLUS_TO_REMOVE_PROTONS == 1 ) + if ( cg_PlusP >= num_atoms ) { + ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_PlusP, pBNS ); + if ( !ret && ret2 ) + ret = ret2; + } +#endif + if ( cg_Plus >= num_atoms ) { + ret2 = RemoveLastGroupFromBnStruct( at, num_atoms, cg_Plus, pBNS ); + if ( !ret && ret2 ) + ret = ret2; + } + + pBNS->type_CN = 0; + pBNS->type_T = 0; + pBNS->type_TACN = 0; + + if ( ret ) { + return ret; + } + if ( pAATG->nAtTypeTotals[ATTOT_NUM_CO_Minus] + pAATG->nAtTypeTotals[ATTOT_NUM_ZO_Minus] && + pAATG->nAtTypeTotals[ATTOT_NUM_N_Minus] ) { + } + + nPosCharges2 = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] + pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; + nNegCharges2 = (pAATG->nAtTypeTotals[ATTOT_NUM_CHARGES] - pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE])/2; + /* + nNumNP_H2 = pAATG->nAtTypeTotals[ATTOT_NUM_NP_H] + + pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton]; + nNumOS_H2 = pAATG->nAtTypeTotals[ATTOT_NUM_COH] + + pAATG->nAtTypeTotals[ATTOT_NUM_CSH] + + pAATG->nAtTypeTotals[ATTOT_NUM_ZOH]; + */ + if ( (nPosCharges - nNegCharges) - (nPosCharges2 - nNegCharges2) != nNumRemovedProtons ) { + return BNS_PROGRAM_ERR; + } + + if ( nNumCanceledCharges ) { +#if ( FIX_CANCEL_CHARGE_COUNT_BUG == 1 ) + *nNumCanceledCharges += 2*nNumNeutralized; +#else + *nNumCanceledCharges = 2*nNumNeutralized; +#endif + } + + return nNumRemovedProtons; +} + + +int mark_at_type( inp_ATOM *atom, int num_atoms, int nAtTypeTotals[] ) +{ + int i, max_num_ions, mask, type; + /*int max_protons, max_O_Minus, num_H = 0, num_CO=0;*/ + if ( nAtTypeTotals ) { + memset( nAtTypeTotals, 0, ATTOT_ARRAY_LEN * sizeof(nAtTypeTotals[0]) ); + } + for ( i = 0; i < num_atoms; i++ ) { + type = GetAtomChargeType( atom, i, nAtTypeTotals, &mask, 0 ); + atom[i].at_type = type; + /* + num_H += ((type & PR_HARD_TYP_H) && (mask & ATBIT_MSK_H)); + num_CO += ((type & AR_HARD_TYP_HA) && (mask & AR_HARD_MSK_HA)); + */ + } + if ( nAtTypeTotals ) { + /* + max_protons = nAtTypeTotals[ATTOT_NUM_NP_Proton] + + inchi_min(num_H, nAtTypeTotals[ATTOT_NUM_NP_Plus]); + max_O_Minus = nAtTypeTotals[ATTOT_NUM_CO_Minus] + nAtTypeTotals[ATTOT_NUM_CS_Minus] + + nAtTypeTotals[ATTOT_NUM_ZO_Minus] + nAtTypeTotals[ATTOT_NUM_OO_Minus] + + nAtTypeTotals[ATTOT_NUM_ZOO_Minus] + nAtTypeTotals[ATTOT_NUM_NO_Minus] + + nAtTypeTotals[ATTOT_NUM_O_Minus] +nAtTypeTotals[ATTOT_NUM_N_Minus]; + ; + max_num_ions = max_protons + max_O_Minus + nAtTypeTotals[ATTOT_NUM_CHARGES]; + */ + max_num_ions = nAtTypeTotals[ATTOT_NUM_CHARGES]; + } else { + max_num_ions = 0; + } + return max_num_ions; +} + + +int RemoveNPProtonsAndAcidCharges( CANON_GLOBALS *pCG, + inp_ATOM *at, + int num_atoms, + BN_AATG *pAATG, + BN_STRUCT *pBNS, + BN_DATA *pBD ) +{ + + /* prepare data structure */ + int num; + int nNumCanceledCharges = 0; + int nNumHardRemovedProtons = 0; + int nNumHardRemovedAcidicProtons = 0; + T_GROUP_INFO *t_group_info = pAATG->t_group_info; + int ret=0, bError = 0; + int bAllowHardRemove = (t_group_info->bTautFlags & TG_FLAG_TEST_TAUT__SALTS) && + (t_group_info->bTautFlags & TG_FLAG_TEST_TAUT2_SALTS) && + (t_group_info->bTautFlags & TG_FLAG_MOVE_POS_CHARGES ) && + (t_group_info->bTautFlags & TG_FLAG_HARD_ADD_REM_PROTONS); + if ( pAATG->nMarkedAtom && num_atoms < pAATG->nAllocLen ) + { + inchi_free( pAATG->nMarkedAtom ); + qzfree( pAATG->nEndPoint ); + memset( pAATG, 0, sizeof(*pAATG) ); + } + if ( !pAATG->nMarkedAtom && (pAATG->nMarkedAtom = (S_CHAR *) inchi_malloc( num_atoms * sizeof(pAATG->nMarkedAtom[0]))) ) + { + pAATG->nAllocLen = num_atoms; + pAATG->nNumFound = 0; + } + + /* o TECHMAN-5.1. Remove protons from charged heteroatoms */ + + /* (TECHMAN-5.1a) Simple remove of protons from N, P, and O,S,Se,Te */ + if ( num = pAATG->nAtTypeTotals[ATTOT_NUM_NP_Proton] + pAATG->nAtTypeTotals[ATTOT_NUM_OH_Plus] ) + { + ret = SimpleRemoveHplusNPO(at, num_atoms, pAATG->nAtTypeTotals, t_group_info); + if ( ret != num ) + { + bError = BNS_PROGRAM_ERR; + goto exit_function; + } + /*t_group_info->nNumRemovedProtons += ret;*/ + t_group_info->tni.bNormalizationFlags |= (ret > 0)? FLAG_PROTON_NPO_SIMPLE_REMOVED : 0; + } + + if ( (num = pAATG->nAtTypeTotals[ATTOT_NUM_NP_Plus]) && bAllowHardRemove ) + { + /* [TECHMAN-5.1b] Hard removing more protons from cationic N; charges may be canceled */ + ret = HardRemoveHplusNP(pCG, at, num_atoms, 1, &nNumCanceledCharges, pAATG, pBNS, pBD); + if ( IS_BNS_ERROR( ret ) ) + { + bError = ret; + goto exit_function; + } + nNumHardRemovedProtons += ret; + /*t_group_info->nNumRemovedProtons += ret;*/ + t_group_info->tni.bNormalizationFlags |= (ret > 0)? FLAG_PROTON_NP_HARD_REMOVED : 0; + } + + + if ( pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] > 0 ) + { + /* o TECHMAN-5.2. Remove protons from neutral heteroatoms */ + + /* (TECHMAN-5.2a) Simple removal */ + ret = SimpleRemoveAcidicProtons( at, num_atoms, pAATG, pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] ); + if ( IS_BNS_ERROR( ret ) ) + { + bError = ret; + goto exit_function; + } + /*t_group_info->nNumRemovedProtons += ret;*/ + t_group_info->tni.bNormalizationFlags |= (ret > 0)? FLAG_PROTON_AC_SIMPLE_REMOVED : 0; + if ( pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] > 0 && bAllowHardRemove ) + { + /* (TECHMAN-5.2b) Hard removal */ + ret = HardRemoveAcidicProtons( pCG, at, num_atoms, pAATG, pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE], &nNumCanceledCharges, pBNS, pBD ); + if ( IS_BNS_ERROR( ret ) ) + { + bError = ret; + goto exit_function; + } + if ( ret > 0 ) + { + int ret2 = SimpleRemoveAcidicProtons( at, num_atoms, pAATG, ret ); + if ( ret2 != ret ) + { + bError = BNS_PROGRAM_ERR; + goto exit_function; + } + /*t_group_info->nNumRemovedProtons += ret;*/ + t_group_info->tni.bNormalizationFlags |= (ret > 0)? FLAG_PROTON_AC_HARD_REMOVED : 0; + nNumHardRemovedAcidicProtons += ret; + } + } + } + else + if ( pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] < 0 ) + { + ret = SimpleAddAcidicProtons( at, num_atoms, pAATG, -pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] ); + if ( IS_BNS_ERROR( ret ) ) + { + bError = ret; + goto exit_function; + } + /*t_group_info->nNumRemovedProtons -= ret;*/ + /* + CHECK_TACN == 1 prohibits replacing (-) on N with H unless H can be moved to N + along an alternating path from another heteroatom (t-group will be detected). + */ + t_group_info->tni.bNormalizationFlags |= (ret > 0)? FLAG_PROTON_AC_SIMPLE_ADDED : 0; + if ( pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE] < 0 && bAllowHardRemove ) + { + ret = HardAddAcidicProtons( pCG, at, num_atoms, pAATG, -pAATG->nAtTypeTotals[ATTOT_TOT_CHARGE], &nNumCanceledCharges, pBNS, pBD ); + if ( IS_BNS_ERROR( ret ) ) + { + bError = ret; + goto exit_function; + } + if ( ret > 0 ) + { + int ret2 = SimpleAddAcidicProtons( at, num_atoms, pAATG, ret ); + if ( ret2 != ret ) + { + bError = BNS_PROGRAM_ERR; + goto exit_function; + } + /*t_group_info->nNumRemovedProtons -= ret;*/ + t_group_info->tni.bNormalizationFlags |= (ret > 0)? FLAG_PROTON_AC_HARD_ADDED : 0; + nNumHardRemovedAcidicProtons -= ret; + } + } + } + t_group_info->tni.bNormalizationFlags |= nNumCanceledCharges? FLAG_PROTON_CHARGE_CANCEL : 0; + +exit_function: + if ( bError ) + { + ret = IS_BNS_ERROR(bError)? bError : BNS_PROGRAM_ERR; + } + return ret; +} + + +/* + + Main normalization procedure + +*/ +int mark_alt_bonds_and_taut_groups( struct tagINCHI_CLOCK *ic, + struct tagCANON_GLOBALS *pCG, + inp_ATOM *at, + inp_ATOM *at_fixed_bonds_out, + int num_atoms, + struct tagInchiTime *ulTimeOutTime, + T_GROUP_INFO *t_group_info, + INCHI_MODE *inpbTautFlags, + INCHI_MODE *inpbTautFlagsDone, + int nebend, int *ebend) + + +{ + BN_STRUCT *pBNS = NULL; + BN_DATA *pBD = NULL; + int bError, nChanges, nTotChanges, taut_found, salt_found, taut_pass, salt_pass, salt_step, ret, ret2, num; + int nOrigDelta, num_changed_bonds; + int max_altp = BN_MAX_ALTP; + int bChangeFlow = (BNS_EF_CHNG_RSTR | BNS_EF_ALTR_BONDS); + BNS_FLOW_CHANGES fcd[BNS_MAX_NUM_FLOW_CHANGES+1]; + C_GROUP_INFO CGroupInfo; + C_GROUP_INFO *c_group_info = &CGroupInfo; + S_GROUP_INFO SGroupInfo; + S_GROUP_INFO *s_group_info = &SGroupInfo; + INCHI_MODE *pbTautFlags = t_group_info? &t_group_info->bTautFlags : inpbTautFlags; + INCHI_MODE *pbTautFlagsDone = t_group_info? &t_group_info->bTautFlagsDone : inpbTautFlagsDone; + + int nAtTypeTotals[ATTOT_ARRAY_LEN]; + int nNumOrigTotAtoms; + + BN_AATG aatg; + BN_AATG *pAATG = &aatg; + +#ifdef FIX_AROM_RADICAL /* Added 2011-05-09 IPl */ + int i, n_arom_radicals=0, *stored_radicals=NULL; +#endif + + nChanges = 0; + bError = 0; + + memset( c_group_info, 0, sizeof(*c_group_info) ); + memset( s_group_info, 0, sizeof(*s_group_info) ); + memset( pAATG, 0, sizeof(*pAATG) ); + + +#ifdef FIX_AROM_RADICAL /* Added 2011-05-09 IPl */ + for ( i = 0; i < num_atoms; i ++ ) + { + if ( (at[i].radical==RADICAL_DOUBLET) && (at[i].valence==2) && + (at[i].bond_type[0]==BOND_ALTERN) && (at[i].bond_type[1]==BOND_ALTERN) ) + { + n_arom_radicals++; + if ( !stored_radicals ) + { + stored_radicals = (int *) inchi_calloc( num_atoms, sizeof(int)); + /* 2011-08-05 explicit cast added due to Evan Bolton */ + if ( !stored_radicals ) + { + bError = BNS_OUT_OF_RAM; + goto exit_function; + } + stored_radicals[i] = RADICAL_DOUBLET; + at[i].radical = 0; + at[i].num_H++; + } + } + } + +#endif + + + if ( (*pbTautFlags & TG_FLAG_MOVE_POS_CHARGES) && num_atoms > 1 ) + { + /* charge groups memory allocation */ + c_group_info->c_group = (C_GROUP *)inchi_calloc( num_atoms/2, sizeof(c_group_info->c_group[0])); + c_group_info->c_candidate = (C_CANDIDATE*)inchi_calloc( num_atoms, sizeof(c_group_info->c_candidate[0])); + if (c_group_info->c_group && c_group_info->c_candidate) + { + c_group_info->max_num_c_groups = num_atoms/2; + c_group_info->max_num_candidates = num_atoms; + } + else + { + bError = BNS_OUT_OF_RAM; /* error: out of RAM */ + /*printf("BNS_OUT_OF_RAM-1: num_at=%d, c_gr=%lx c_can=%lx\n", num_atoms, c_group_info->c_group, c_group_info->c_candidate);*/ + goto exit_function; + } + } + + if ( *pbTautFlags & TG_FLAG_TEST_TAUT__SALTS ) + { + if ( t_group_info ) + { + /* salt groups memory allocation */ + s_group_info->s_candidate = + (S_CANDIDATE*)inchi_calloc( num_atoms, + sizeof(s_group_info->s_candidate[0])); + if (s_group_info->s_candidate) + { + s_group_info->max_num_candidates = num_atoms; + } + else + { + bError = BNS_OUT_OF_RAM; /* error: out of RAM */ + /*printf("BNS_OUT_OF_RAM-2\n");*/ + goto exit_function; + } + } + } + + if ( t_group_info ) + { + if ( t_group_info->tGroupNumber ) + inchi_free( t_group_info->tGroupNumber ); + t_group_info->tGroupNumber = (AT_NUMB *)inchi_calloc( 2*num_atoms+1, sizeof(t_group_info->tGroupNumber[0]) ); + if ( !t_group_info->tGroupNumber ) + { + /*printf("BNS_OUT_OF_RAM-9\n");*/ + bError = BNS_OUT_OF_RAM; /* error: out of RAM */ + goto exit_function; + } + num = t_group_info->tni.nNumRemovedExplicitH; + memset ( &t_group_info->tni, 0, sizeof(t_group_info->tni) ); + t_group_info->tni.nNumRemovedExplicitH = num; + } + + + +/* +again: +*/ + /* allocate Balanced Network Data Strucures; replace Alternating bonds with Single */ + if ( (pBNS = AllocateAndInitBnStruct( at, num_atoms, + BNS_ADD_ATOMS, BNS_ADD_EDGES, + max_altp, &num_changed_bonds )) + && + (pBD = AllocateAndInitBnData( pBNS->max_vertices )) + ) + { + + + pBNS->pbTautFlags = pbTautFlags; /* carry through all functions */ + pBNS->pbTautFlagsDone = pbTautFlagsDone; /* carry through all functions */ + + pBNS->ulTimeOutTime = ulTimeOutTime; /* v. 1.05 */ + pBNS->ic = ic; /* v. 1.05 */ + +#if ( BNS_PROTECT_FROM_TAUT == 1 ) + /* protect bonds to acetyl and nitro */ + SetForbiddenEdges( pBNS, at, num_atoms, BNS_EDGE_FORBIDDEN_MASK, nebend, ebend ); +#endif + + + /* set bonds in case of input "aromatic" bonds or multiple radicals */ + +#ifdef FIX_AROM_RADICAL /* Added 2011-05-09 IPl */ + if ( n_arom_radicals ) + { + ret = BnsAdjustFlowBondsRad( pBNS, pBD, at, num_atoms ); + if ( stored_radicals ) + { + for ( i = 0; i < num_atoms; i ++ ) + { + if ( stored_radicals[i] ) + { + at[i].radical = stored_radicals[i]; + at[i].num_H--; + } + } + } + if ( IS_BNS_ERROR( ret ) ) + { + bError = ret; + goto exit_function; + } + } +#endif + + + ret = BnsAdjustFlowBondsRad( pBNS, pBD, at, num_atoms ); + + + /* (here pair(s) of radicals could have disappeared from the atoms) */ + if ( IS_BNS_ERROR( ret ) ) + { + bError = ret; + goto exit_function; + } + pBNS->tot_st_flow += 2*ret; + + + + /*return 0;*/ /* debug */ + nOrigDelta = ret; + if ( pBNS->tot_st_cap > pBNS->tot_st_flow ) + { + /* has radical */ + bChangeFlow |= BNS_EF_SET_NOSTEREO; + } + + /******************************************************************** + * Remove protons from NH(+), but not PH(+) + * Add protons to COO(-) etc. + * or remove protons from COOH etc to make the organic part neutral + * Note: for now (-) from N(-) can be only canceled or moved to -C=O + ********************************************************************/ + if ( ( *pbTautFlags & TG_FLAG_VARIABLE_PROTONS ) && + t_group_info && + mark_at_type( at, num_atoms, nAtTypeTotals ) && + nAtTypeTotals[ATTOT_NUM_CHARGES] ) + { + /* + the structure is simple to neutralize if it yields exactly + num[H(+)] = num[N,P H(+)] + num[N,S,O(-)] = num[=C-O(-)] + num[C-S(-)] + num[N(-)] + num[other O(-), S(-)] + + and n(p) = num[H(+)] - num[N,S,O(-)] (no protons, no negative N,O,S condition) + + Additional check is needed if: + min{num[N,PH], num[N,P(+), not onium]} > 0 + => possibility to yield more H(+) + + min_charge = orig_charge(P,N,O,S) - n(p) - n(OH,SH) + max_charge = orig_charge(P,N,O,S) - n(p) + n(O,S,N(-)) + */ + + + nNumOrigTotAtoms = t_group_info->tni.nNumRemovedExplicitH + num_atoms; + pAATG->nAtTypeTotals = nAtTypeTotals; + pAATG->t_group_info = t_group_info; +#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) + pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP; +#endif + + /***********************************************************/ + /* */ + /* ( D E ) P R O T O N A T I O N */ + /* */ + /***********************************************************/ + + ret = RemoveNPProtonsAndAcidCharges( pCG, at, num_atoms, pAATG, pBNS, pBD ); + +#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) + pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP; +#endif + if ( IS_BNS_ERROR( ret ) ) + { + bError = ret; + goto exit_function; + } + + if ( t_group_info->tni.bNormalizationFlags ) + { + SetInitCapFlowToCurrent( pBNS ); + if ( at_fixed_bonds_out ) + { + /* copy modified initial tautomeric structure for displaying + Warning: implicit H counts in at_fixed_bonds_out include explicit Hs */ + memcpy( at_fixed_bonds_out, at, nNumOrigTotAtoms*sizeof(at_fixed_bonds_out[0]) ); + /* -- will be done in FillOutInputInfAtom() -- + RemoveExcessiveImplicitH( num_atoms, t_group_info->tni.nNumRemovedExplicitH, at_fixed_bonds_out ); + */ + } + } + } + + + /****************** initial bonds normalization ***************/ + + if ( *pbTautFlags & TG_FLAG_MOVE_POS_CHARGES ) + { + + /******************* find moveable positive charges **********************/ + + do + { + /* cycling while ret>0 added 2004-06-04 */ +#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) + pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP; + CorrectFixing_NH_NH_Bonds( pBNS, at, num_atoms ); +#endif + ret = MarkChargeGroups( pCG, + at, num_atoms, + c_group_info, t_group_info, + pBNS, pBD ); + +#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) + pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP; +#endif + if ( IS_BNS_ERROR( ret ) ) + { + bError = ret; + goto exit_function; + } + if ( ret ) + { + nChanges += ret; + ret2 = AddCGroups2BnStruct( pCG, pBNS, at, num_atoms, c_group_info ); + if ( IS_BNS_ERROR( ret2 ) ) + { + bError = ret2; + goto exit_function; + } + *pbTautFlagsDone |= TG_FLAG_MOVE_POS_CHARGES_DONE; + } + } + while ( ret > 0 ); + + +#if ( BNS_RAD_SEARCH == 1 ) +#else + /* moveable charges may allow to cancel radicals -- check it */ + if ( pBNS->tot_st_cap > pBNS->tot_st_flow ) + { + ret = BnsAdjustFlowBondsRad( pBNS, pBD, at, num_atoms ); + if ( IS_BNS_ERROR( ret ) ) + { + bError = ret; + goto exit_function; + } + if ( ret > 0 ) + { + /* + pBNS->tot_st_flow += 2*ret; + ret = ReInitBnStruct( pBNS, at, num_atoms, 1 ); + if ( IS_BNS_ERROR( ret ) ) { + bError = ret; + goto exit_function; + } + */ + bError = BNS_RADICAL_ERR; + goto exit_function; + } + } +#endif + } + + /************************************************************************/ + /******** test bonds for bond tautomerism **************/ + /******** replace moveable bonds with "alternating" bonds **************/ + /************************************************************************/ + ret = BnsTestAndMarkAltBonds( pBNS, pBD, at, num_atoms, fcd, bChangeFlow, 0 ); + + if ( IS_BNS_ERROR( ret ) ) + { + bError = ret; + goto exit_function; + } + nChanges += ret; + + /*********************** end of initial bonds normalization *************/ + nTotChanges = 0; + /* check for tautomerism */ + /* find new tautomer groups */ + salt_pass = 0; + salt_step = 0; + salt_found = 0; + + + + /*************************************************************/ + /* */ + /* M A I N C Y C L E B E G I N */ + /* */ + /*************************************************************/ + + do { + nTotChanges += nChanges; + nChanges = 0; + taut_pass = 0; + + /**************** regular bond/H/(-)/positive charges tautomerism cycle begin **************/ + do + { + taut_pass ++; + for ( taut_found = 0; + 0 < (ret=MarkTautomerGroups( pCG, at, num_atoms, + t_group_info, c_group_info, + pBNS, pBD )); + taut_found ++ ) + { + ; + } + if ( ret < 0 ) + { + bError = ret; + } + if ( taut_found && !salt_pass ) + { + *pbTautFlagsDone |= TG_FLAG_TEST_TAUT__ATOMS_DONE; + } + + if ( taut_found || salt_found ) + { + /****************** repeat bonds normalization ***************/ + ret = ReInitBnStructAddGroups( pCG, pBNS, at, num_atoms, + t_group_info, c_group_info ); + if ( IS_BNS_ERROR( ret ) ) + { + bError = ret; + goto exit_function; + } +#if ( BNS_RAD_SEARCH == 1 ) +#else + /* discovered moveable charges and H-atoms may allow to cancel radicals */ + if ( pBNS->tot_st_cap > pBNS->tot_st_flow ) { + ret = BnsAdjustFlowBondsRad( pBNS, pBD, at, num_atoms ); + if ( IS_BNS_ERROR( ret ) ) { + bError = ret; + goto exit_function; + } + if ( ret > 0 ) { + /* + pBNS->tot_st_flow += 2*ret; + ret = ReInitBnStruct( pBNS, at, num_atoms, 1 ); + if ( IS_BNS_ERROR( ret ) ) { + bError = ret; + goto exit_function; + } + */ + bError = BNS_RADICAL_ERR; + goto exit_function; + } + } +#endif + /****************** update bonds normalization ***************/ + if ( *pbTautFlags & TG_FLAG_MOVE_POS_CHARGES ) + { + /******************* find moveable charges ***************/ + do + { + /* cycling while ret>0 added 2004-06-04 */ +#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) + pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP; + CorrectFixing_NH_NH_Bonds( pBNS, at, num_atoms ); +#endif + + ret = MarkChargeGroups( pCG, + at, num_atoms, + c_group_info, t_group_info, + pBNS, pBD ); +#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) + pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP; +#endif + if ( IS_BNS_ERROR( ret ) ) + { + bError = ret; + goto exit_function; + } + nChanges+= ret; + if ( ret > 0 ) + { + ret2 = ReInitBnStructAddGroups( pCG, pBNS, + at, num_atoms, + t_group_info, + c_group_info ); + if ( IS_BNS_ERROR( ret2 ) ) + { + bError = ret2; + goto exit_function; + } + *pbTautFlagsDone |= TG_FLAG_MOVE_POS_CHARGES_DONE; + } + } while ( ret > 0 ); + } + + + /************************************************************************/ + /******** find moveable bonds: **************/ + /******** test bonds for bond tautomerism **************/ + /******** replace moveable bonds with "alternating" bonds **************/ + /************************************************************************/ + ret = BnsTestAndMarkAltBonds( pBNS, pBD, at, num_atoms, fcd, bChangeFlow, 0 ); + if ( IS_BNS_ERROR( ret ) ) + { + bError = ret; + goto exit_function; + } + nChanges+= ret; + /****************** end of update bonds normalization ***************/ + } + salt_found = 0; + } + while( taut_found && !bError ); + + /**************** regular bond/H/(-)/positive charges tautomerism cycle end **************/ + + if ( bError ) + { + break; + } + + + /******************* 'salt' tautomerism permitted *************************/ + if ( *pbTautFlags & TG_FLAG_TEST_TAUT__SALTS ) + { + + if ( *pbTautFlags & TG_FLAG_TEST_TAUT2_SALTS ) + { + /*********** requested one or more "salt" attachement migrartion test ********/ + if ( !nChanges && salt_pass && salt_step ) + { + break; /* done */ + } + if ( !salt_step ) + { + /* salt step 0: process one attachment migrartion */ +#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) + pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP; + CorrectFixing_NH_NH_Bonds( pBNS, at, num_atoms ); +#endif + salt_found = MarkSaltChargeGroups( pCG, + at, + num_atoms, s_group_info, + t_group_info, + c_group_info, pBNS, pBD ); +#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) + pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP; +#endif + if ( salt_found < 0 ) { + bError = salt_found; + break; + } else + if ( salt_found > 0 ) { + *pbTautFlagsDone |= TG_FLAG_TEST_TAUT__SALTS_DONE; + } + salt_step = !salt_found; + /* if new 'salt' atoms have been found then repeat regular taut. search + * MarkTautomerGroups() and do not perform salt step 1 + * if new 'salt' atoms have NOT been found then switch to salt step 1 + * and never repeat salt step 0 for the current structure + */ + } + if ( salt_step /*|| + (t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT)*/ ) { + /* salt step 1: process more than one attachment migration */ +#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) + pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP; + CorrectFixing_NH_NH_Bonds( pBNS, at, num_atoms ); +#endif + salt_found = MarkSaltChargeGroups2( pCG, + at, num_atoms, + s_group_info, + t_group_info, + c_group_info, + pBNS, pBD ); +#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) + pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP; +#endif + if ( salt_found < 0 ) { + bError = salt_found; + break; + } else + if ( salt_found == 1 || salt_found == 5 ) { + *pbTautFlagsDone |= TG_FLAG_TEST_TAUT2_SALTS_DONE; + if ( salt_found == 5 ) { + *pbTautFlagsDone |= TG_FLAG_TEST_TAUT3_SALTS_DONE; + } + /* salt_found == 2 => only negative charges involved */ + } + } + + salt_pass ++; + } else { /* !( *pbTautFlags & TG_FLAG_TEST_TAUT2_SALTS ) */ + /*************** requested only one attachement migration test **********/ + if ( !nChanges && salt_pass ) { /* one attachment migration */ + break; + } /* salt step 0: process one attachment migration */ +#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) + pBNS->edge_forbidden_mask |= BNS_EDGE_FORBIDDEN_TEMP; + CorrectFixing_NH_NH_Bonds( pBNS, at, num_atoms ); +#endif + salt_found = MarkSaltChargeGroups( pCG, + at, + num_atoms, + s_group_info, + t_group_info, + c_group_info, + pBNS, pBD ); +#if ( RESET_EDGE_FORBIDDEN_MASK == 0 ) + pBNS->edge_forbidden_mask &= ~BNS_EDGE_FORBIDDEN_TEMP; +#endif + if ( salt_found < 0 ) { + bError = salt_found; + break; + } else + if ( salt_found > 0 ) { + *pbTautFlagsDone |= TG_FLAG_TEST_TAUT__SALTS_DONE; + } + salt_pass ++; + } /* ( *pbTautFlags & TG_FLAG_TEST_TAUT2_SALTS ) */ + } /* ( *pbTautFlags & TG_FLAG_TEST_TAUT__SALTS ) */ + } while ( salt_found && !bError ); + /*************************************************************/ + /* */ + /* M A I N C Y C L E E N D */ + /* */ + /*************************************************************/ + + if ( *pbTautFlags & TG_FLAG_MERGE_TAUT_SALTS ) { + if ( !bError && s_group_info /*&& s_group_info->num_candidates > 0*/ ) { + ret = MergeSaltTautGroups( pCG, at, num_atoms, s_group_info, + t_group_info, c_group_info, pBNS ); + if ( ret < 0 ) { + bError = ret; + } else + if ( ret > 0 ) { + *pbTautFlagsDone |= TG_FLAG_MERGE_TAUT_SALTS_DONE; + } + } + } + if ( !bError && t_group_info && + (t_group_info->bTautFlags & TG_FLAG_VARIABLE_PROTONS) && + (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE|TG_FLAG_FOUND_ISOTOPIC_H_DONE)) ) { + ret = MakeIsotopicHGroup( at, num_atoms, s_group_info, t_group_info ); + if ( ret < 0 ) { + bError = ret; + } + } + /* success */ + remove_alt_bond_marks( at, num_atoms); + + /************************************************ + * Temporarily ignore all non-alternating bonds + * and mark non-ring alt bonds non-stereogenic + ************************************************/ + + ReInitBnStructForAltBns( pBNS, at, num_atoms, 0 ); + MarkRingSystemsAltBns( pBNS, 0 ); + MarkNonStereoAltBns( pBNS, at, num_atoms, 0 ); +#if ( FIX_EITHER_DB_AS_NONSTEREO == 1 ) + /* second time unknown ("Either") alternating bonds are treated as non-stereogenic */ + /* stereobonds bonds that lost stereo get "Either" stereo_type */ + ReInitBnStructForAltBns( pBNS, at, num_atoms, 1 ); + MarkRingSystemsAltBns( pBNS, 1 ); + MarkNonStereoAltBns( pBNS, at, num_atoms, 1 ); +#endif + } else { + bError = BNS_OUT_OF_RAM; + /*printf("BNS_OUT_OF_RAM-3\n");*/ + } + +exit_function: + + pBNS = DeAllocateBnStruct( pBNS ); + pBD = DeAllocateBnData( pBD ); +/*#if ( MOVE_CHARGES == 1 )*/ + if ( c_group_info ) { + if ( c_group_info->c_group ) { + inchi_free( c_group_info->c_group ); + } + if ( c_group_info->c_candidate ) { + inchi_free( c_group_info->c_candidate ); + } + } +/*#endif*/ + if ( s_group_info && s_group_info->s_candidate ) { + inchi_free( s_group_info->s_candidate ); + } + if ( pAATG && pAATG->nMarkedAtom ) { + inchi_free( pAATG->nMarkedAtom ); + qzfree( pAATG->nEndPoint ); + /*qzfree( pAATG->nAtTypeTotals );*/ /* nAtTypeTotals is a stack array */ + } + if ( t_group_info && t_group_info->tGroupNumber ) { + inchi_free( t_group_info->tGroupNumber ); + t_group_info->tGroupNumber = NULL; + } + + if ( !bError && num_atoms == 1 && at[0].at_type == ATT_PROTON && t_group_info && !t_group_info->tni.nNumRemovedExplicitH ) { + /* remove single isolated proton */ + t_group_info->tni.nNumRemovedProtons = 1; + t_group_info->tni.bNormalizationFlags |= FLAG_PROTON_SINGLE_REMOVED; + if ( at[0].iso_atw_diff ) { + t_group_info->tni.nNumRemovedProtonsIsotopic[at[0].iso_atw_diff-1] ++; + } + if ( at_fixed_bonds_out ) { + memcpy( at_fixed_bonds_out, at, num_atoms*sizeof(at_fixed_bonds_out[0]) ); + } + /*num_atoms --;*/ + } + + + /* + Additional currently unused info: + + nOrigDelta > 0: original structure has been changed + due to fiund augmenting path(s) + nChanges > 0: either alt. bonds or taut. groups have been found + */ + +#ifdef FIX_AROM_RADICAL /* Added 2011-05-09 IPl */ + if ( stored_radicals ) + inchi_free( stored_radicals ); +#endif + + + return bError? bError : num_atoms; /* ret = 0 => success, any other => error */ +} + + +int nMaxFlow2Check( BN_STRUCT *pBNS, int iedge ) +{ + BNS_EDGE *pEdge = pBNS->edge + iedge; + int nMaxFlow = (pEdge->cap & EDGE_FLOW_MASK); /* edge cap */ + + if ( nMaxFlow > MAX_BOND_EDGE_CAP ) { + nMaxFlow = MAX_BOND_EDGE_CAP; + } + return nMaxFlow; +} + + +int nCurFlow2Check( BN_STRUCT *pBNS, int iedge ) +{ + BNS_EDGE *pEdge = pBNS->edge + iedge; + int nCurFlow = (pEdge->flow & EDGE_FLOW_MASK); /* edge flow */ + return nCurFlow; +} + + +int nMinFlow2Check( BN_STRUCT *pBNS, int iedge ) +{ + BNS_EDGE *pEdge = pBNS->edge + iedge; + Vertex v1 = pEdge->neighbor1; + Vertex v2 = v1 ^ pEdge->neighbor12; + int f12 = (pEdge->flow & EDGE_FLOW_MASK); + int rescap1, rescap2, rescap12, i, iedge_i; + + if ( f12 > 0 ) { + for ( i = 0, rescap1 = 0; i < pBNS->vert[v1].num_adj_edges; i ++ ) { + iedge_i = pBNS->vert[v1].iedge[i]; + if ( iedge_i == iedge ) + continue; + rescap1 += (pBNS->edge[iedge_i].cap & EDGE_FLOW_MASK) - (pBNS->edge[iedge_i].flow & EDGE_FLOW_MASK); + } + for ( i = 0, rescap2 = 0; i < pBNS->vert[v2].num_adj_edges; i ++ ) { + iedge_i = pBNS->vert[v2].iedge[i]; + if ( iedge_i == iedge ) + continue; + rescap2 += (pBNS->edge[iedge_i].cap & EDGE_FLOW_MASK) - (pBNS->edge[iedge_i].flow & EDGE_FLOW_MASK); + } + rescap12 = inchi_min( rescap1, rescap2 ); + rescap12 = inchi_min( rescap12, f12 ); + return f12-rescap12; + } + return 0; +} + + +int bSetBondsAfterCheckOneBond( BN_STRUCT *pBNS, BNS_FLOW_CHANGES *fcd, int nTestFlow, inp_ATOM *at, int num_atoms, int bChangeFlow0 ) +{ + int ifcd, iedge, new_flow, ret_val, nChanges = 0, bError=0; + int bChangeFlow; + Vertex v1, v2; + int ineigh1, ineigh2; + BNS_EDGE *pEdge; + + bChangeFlow0 &= ~BNS_EF_CHNG_RSTR; /* do not change pEdge flow in SetBondType */ + if ( !bChangeFlow0 ) + return 0; + + bChangeFlow = (bChangeFlow0 & ~BNS_EF_SET_NOSTEREO); + /* find the next to the last changed */ + if ( bChangeFlow0 & BNS_EF_SET_NOSTEREO ) { + + for ( ifcd = 0; NO_VERTEX != (iedge = fcd[ifcd].iedge); ifcd ++ ) { + iedge = fcd[ifcd].iedge; + pEdge = pBNS->edge + iedge; + if ( !pEdge->pass ) { + continue; + } + + if ( !ifcd && nTestFlow>=0 ) { + new_flow = nTestFlow; + } else { + new_flow = (int)pEdge->flow; + } + + v1 = pEdge->neighbor1; + v2 = pEdge->neighbor12 ^ v1; + if ( v1 < num_atoms && v2 < num_atoms && new_flow != pEdge->flow0 ) { + if ( (pBNS->vert[v1].st_edge.cap0 == pBNS->vert[v1].st_edge.flow0) != + (pBNS->vert[v1].st_edge.cap == pBNS->vert[v1].st_edge.flow ) || + (pBNS->vert[v2].st_edge.cap0 == pBNS->vert[v2].st_edge.flow0) != + (pBNS->vert[v2].st_edge.cap == pBNS->vert[v2].st_edge.flow )) { + bChangeFlow |= BNS_EF_SET_NOSTEREO; + nChanges |= BNS_EF_SET_NOSTEREO; + } + } + } + } else { + for ( ifcd = 0; NO_VERTEX != (iedge = fcd[ifcd].iedge); ifcd ++ ) + ; + } + + /* restore in reversed order to correctly handle vertex changed more than once */ + for ( ifcd -= 1; 0 <= ifcd; ifcd -- ) { + + iedge = fcd[ifcd].iedge; + pEdge = pBNS->edge + iedge; + if ( !pEdge->pass ) { + continue; + } + + if ( !ifcd && nTestFlow>=0 ) { + new_flow = nTestFlow; + } else { + new_flow = (int)pEdge->flow; + } + + v1 = pEdge->neighbor1; + v2 = pEdge->neighbor12 ^ v1; + if ( v1 < num_atoms && v2 < num_atoms && bChangeFlow && new_flow != pEdge->flow0 ) { + ineigh1 = pEdge->neigh_ord[0]; + ineigh2 = pEdge->neigh_ord[1]; + ret_val = SetAtomBondType( pEdge, &at[v1].bond_type[ineigh1], &at[v2].bond_type[ineigh2], new_flow-pEdge->flow0, bChangeFlow ); + if ( !IS_BNS_ERROR( ret_val ) ) { + nChanges |= (ret_val > 0); + } else { + bError = ret_val; + } + } + pEdge->pass = 0; + } + return bError? bError : nChanges; +} + + +int bRestoreFlowAfterCheckOneBond( BN_STRUCT *pBNS, BNS_FLOW_CHANGES *fcd ) +{ + int ifcd, iedge; + Vertex v1, v2; + BNS_EDGE *pEdge; + + /* find the next to the last changed */ + for ( ifcd = 0; NO_VERTEX != (iedge = fcd[ifcd].iedge); ifcd ++ ) + ; + + /* restore in reversed order to correctly handle vertex changed more than once */ + for ( ifcd -= 1; 0 <= ifcd; ifcd -- ) { + + /* restore edge flow & cap */ + iedge = fcd[ifcd].iedge; + pEdge = pBNS->edge + iedge; + pEdge->flow = fcd[ifcd].flow; + pEdge->cap = fcd[ifcd].cap; + pEdge->pass = 0; + + /* restore st-flow, cap */ + if ( NO_VERTEX != (v1 = fcd[ifcd].v1) ) { + pBNS->vert[v1].st_edge.flow = fcd[ifcd].flow_st1; + pBNS->vert[v1].st_edge.cap = fcd[ifcd].cap_st1; + pBNS->vert[v1].st_edge.pass = 0; + } + if ( NO_VERTEX != (v2 = fcd[ifcd].v2) ) { + pBNS->vert[v2].st_edge.flow = fcd[ifcd].flow_st2; + pBNS->vert[v2].st_edge.cap = fcd[ifcd].cap_st2; + pBNS->vert[v2].st_edge.pass = 0; + } + } + return 0; +} + + +int bSetFlowToCheckOneBond( BN_STRUCT *pBNS, int iedge, int flow, BNS_FLOW_CHANGES *fcd ) +{ + BNS_EDGE *pEdge = pBNS->edge + iedge; + int f12 = (pEdge->flow & EDGE_FLOW_MASK); /* the original flow */ + int ifcd = 0; + int nDots = 0; + int i, iedge_i; + + fcd[ifcd].iedge = NO_VERTEX; + + if ( f12 < flow ) { + /* Increase edge flow: Grab flow from the neighbors and delete it: set flow12=cap12 = 0 */ + /************************************************************************************/ + /* For example, simulate a new fixed double bond in place of a single bond and */ + /* creates ONE or NONE (in case of a radical on adjacent atom) augmenting paths and */ + /* makes it impossible for the BNS to set same flow as it originally was */ + /************************************************************************************/ + Vertex v1 = pEdge->neighbor1; + Vertex v2 = v1 ^ pEdge->neighbor12; + Vertex v_i; /* neighbor of v1 or v2 */ + BNS_EDGE *pEdge_i; + int delta1, delta2, f, st_edge_rescap; + + if ( (pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK) < flow || + (pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK) < flow ) { + return BNS_CANT_SET_BOND; + } + if ( (pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK) < f12 || + (pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK) < f12 ) { + return BNS_CAP_FLOW_ERR; + } + + + fcd[ifcd].iedge = iedge; + fcd[ifcd].flow = pEdge->flow; + fcd[ifcd].cap = pEdge->cap; + + fcd[ifcd].v1 = v1; + fcd[ifcd].flow_st1 = pBNS->vert[v1].st_edge.flow; + fcd[ifcd].cap_st1 = pBNS->vert[v1].st_edge.cap; + + fcd[ifcd].v2 = v2; + fcd[ifcd].flow_st2 = pBNS->vert[v2].st_edge.flow; + fcd[ifcd].cap_st2 = pBNS->vert[v2].st_edge.cap; + + fcd[++ifcd].iedge = NO_VERTEX; /* mark the end of the fcd[] data */ + pEdge->pass |= 64; + + delta1 = delta2 = flow - f12; + + if ( f12 > 0 ) { + /* remove old edge flow from the flow and cap of the adjacent vertices' st-edges */ + pBNS->vert[v1].st_edge.cap = ((pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK)-f12) | (pBNS->vert[v1].st_edge.cap & ~EDGE_FLOW_ST_MASK); + pBNS->vert[v2].st_edge.cap = ((pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK)-f12) | (pBNS->vert[v2].st_edge.cap & ~EDGE_FLOW_ST_MASK); + pBNS->vert[v1].st_edge.flow = ((pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK)-f12) | (pBNS->vert[v1].st_edge.flow & ~EDGE_FLOW_ST_MASK); + pBNS->vert[v2].st_edge.flow = ((pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK)-f12) | (pBNS->vert[v2].st_edge.flow & ~EDGE_FLOW_ST_MASK); + /* delete current edge flow and capacity */ + pEdge->flow = (pEdge->flow & ~EDGE_FLOW_MASK); + } + pEdge->cap = (pEdge->cap & ~EDGE_FLOW_MASK); + + /* grab the adjacent vertex1 radical (st_edge_rescap) if it exists */ + st_edge_rescap = (pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK) - (pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK); + while ( st_edge_rescap && delta1 ) { + st_edge_rescap --; /* grab the radical */ + delta1 --; + pBNS->vert[v1].st_edge.cap = ((pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v1].st_edge.cap & ~EDGE_FLOW_ST_MASK); + nDots --; + } + /* grab the adjacent vertex2 radical (st_edge_rescap) if it exists */ + st_edge_rescap = (pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK) - (pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK); + while ( st_edge_rescap && delta2 ) { + st_edge_rescap --; /* grab the radical */ + delta2 --; + pBNS->vert[v2].st_edge.cap = ((pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v2].st_edge.cap & ~EDGE_FLOW_ST_MASK); + nDots --; + } + /* grab flows from v1 neighbors */ + for ( i = 0; delta1 && i < pBNS->vert[v1].num_adj_edges; i ++ ) { + iedge_i = pBNS->vert[v1].iedge[i]; + if ( iedge_i == iedge ) + continue; + pEdge_i = pBNS->edge + iedge_i; + if ( IS_FORBIDDEN(pEdge_i->forbidden, pBNS) ) + continue; + f = (pEdge_i->flow & EDGE_FLOW_MASK); + if ( f ) { + v_i = pEdge_i->neighbor12 ^ v1; + + fcd[ifcd].iedge = iedge_i; + fcd[ifcd].flow = pEdge_i->flow; + fcd[ifcd].cap = pEdge_i->cap; + + fcd[ifcd].v1 = v_i; + fcd[ifcd].flow_st1 = pBNS->vert[v_i].st_edge.flow; + fcd[ifcd].cap_st1 = pBNS->vert[v_i].st_edge.cap; + + fcd[ifcd].v2 = NO_VERTEX; + fcd[ifcd].flow_st2 = 0; + fcd[ifcd].cap_st2 = 0; + + fcd[++ifcd].iedge = NO_VERTEX; /* mark the end of the fcd[] data */ + pEdge_i->pass |= 64; + + while ( f && delta1 ) { + f --; + delta1 --; + pEdge_i->flow = ((pEdge_i->flow & EDGE_FLOW_MASK) - 1) | (pEdge_i->flow & ~EDGE_FLOW_MASK); + pBNS->vert[v_i].st_edge.flow = ((pBNS->vert[v_i].st_edge.flow & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v_i].st_edge.flow & ~EDGE_FLOW_ST_MASK); + /* next 2 lines added 01-22-2002 */ + pBNS->vert[v1].st_edge.cap = ((pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v1].st_edge.cap & ~EDGE_FLOW_ST_MASK); + pBNS->vert[v1].st_edge.flow = ((pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v1].st_edge.flow & ~EDGE_FLOW_ST_MASK); + + nDots ++; + } + } + } + /* grab flows from v2 neighbors */ + for ( i = 0; delta2 && i < pBNS->vert[v2].num_adj_edges; i ++ ) { + iedge_i = pBNS->vert[v2].iedge[i]; + if ( iedge_i == iedge ) + continue; + pEdge_i = pBNS->edge + iedge_i; + if ( IS_FORBIDDEN(pEdge_i->forbidden, pBNS) ) + continue; + f = (pEdge_i->flow & EDGE_FLOW_MASK); + if ( f ) { + v_i = pEdge_i->neighbor12 ^ v2; + + fcd[ifcd].iedge = iedge_i; + fcd[ifcd].flow = pEdge_i->flow; + fcd[ifcd].cap = pEdge_i->cap; + + fcd[ifcd].v1 = v_i; + fcd[ifcd].flow_st1 = pBNS->vert[v_i].st_edge.flow; + fcd[ifcd].cap_st1 = pBNS->vert[v_i].st_edge.cap; + + fcd[ifcd].v2 = NO_VERTEX; + fcd[ifcd].flow_st2 = 0; + fcd[ifcd].cap_st2 = 0; + + fcd[++ifcd].iedge = NO_VERTEX; /* mark the end of the fcd[] data */ + pEdge_i->pass |= 64; + + while ( f && delta2 ) { + f --; + delta2 --; + pEdge_i->flow = ((pEdge_i->flow & EDGE_FLOW_MASK) - 1) | (pEdge_i->flow & ~EDGE_FLOW_MASK); + pBNS->vert[v_i].st_edge.flow = ((pBNS->vert[v_i].st_edge.flow & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v_i].st_edge.flow & ~EDGE_FLOW_ST_MASK); + /* next 2 lines added 01-22-2002 */ + pBNS->vert[v2].st_edge.cap = ((pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v2].st_edge.cap & ~EDGE_FLOW_ST_MASK); + pBNS->vert[v2].st_edge.flow = ((pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK)-1) | (pBNS->vert[v2].st_edge.flow & ~EDGE_FLOW_ST_MASK); + + nDots ++; + } + } + } + if ( delta1 || delta2 ) { + return BNS_CANT_SET_BOND; + } + } + + if ( f12 >= flow ) { + /* Decrease edge flow: Redirect flow to the neighbors and delete it on the edge: set flow12=cap12 = 0 */ + /* f12==flow fixes flow through the edge so that BNS cannot change it */ + /**********************************************************************************************/ + /* For example, simulate a removal of a double bond and create ONE or NONE augmenting path */ + /* Make it impossible for BNS to set same flow as it originally was */ + /**********************************************************************************************/ + Vertex v1 = pEdge->neighbor1; + Vertex v2 = (v1 ^ pEdge->neighbor12); + int delta; + /* if NOT (st-cap >= st-flow >= f12 >= flow) then error in the BN structure */ + if ( (pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK) < f12 || + (pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK) < f12 || + (pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK) < flow || + (pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK) < flow ) { + return BNS_CAP_FLOW_ERR; + } + fcd[ifcd].iedge = iedge; + fcd[ifcd].flow = pEdge->flow; + fcd[ifcd].cap = pEdge->cap; + + fcd[ifcd].v1 = v1; + fcd[ifcd].flow_st1 = pBNS->vert[v1].st_edge.flow; + fcd[ifcd].cap_st1 = pBNS->vert[v1].st_edge.cap; + + fcd[ifcd].v2 = v2; + fcd[ifcd].flow_st2 = pBNS->vert[v2].st_edge.flow; + fcd[ifcd].cap_st2 = pBNS->vert[v2].st_edge.cap; + + fcd[++ifcd].iedge = NO_VERTEX; /* mark the end of the fcd[] data */ + pEdge->pass |= 64; + + delta = f12 - flow; + /* remove current edge flow from st-edges */ + /* -- seem to be a bug -- + pBNS->vert[v1].st_edge.flow = ((pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK)-delta) | (pBNS->vert[v1].st_edge.flow & ~EDGE_FLOW_ST_MASK); + pBNS->vert[v2].st_edge.flow = ((pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK)-delta) | (pBNS->vert[v2].st_edge.flow & ~EDGE_FLOW_ST_MASK); + */ + + /* replacement to the above 2 lines 01-16-2002 */ + /* remove old edge flow from the flow of the adjacent vertices' st-edges */ + + pBNS->vert[v1].st_edge.flow = ((pBNS->vert[v1].st_edge.flow & EDGE_FLOW_ST_MASK)-f12) | (pBNS->vert[v1].st_edge.flow & ~EDGE_FLOW_ST_MASK); + pBNS->vert[v2].st_edge.flow = ((pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK)-f12) | (pBNS->vert[v2].st_edge.flow & ~EDGE_FLOW_ST_MASK); + + /* added 01-16-2002: reduce st-cap if new flow > 0 */ + /* remove new edge flow from the cap of the adjacent vertices' st-edges */ + pBNS->vert[v1].st_edge.cap = ((pBNS->vert[v1].st_edge.cap & EDGE_FLOW_ST_MASK)-flow) | (pBNS->vert[v1].st_edge.cap & ~EDGE_FLOW_ST_MASK); + pBNS->vert[v2].st_edge.cap = ((pBNS->vert[v2].st_edge.cap & EDGE_FLOW_ST_MASK)-flow) | (pBNS->vert[v2].st_edge.cap & ~EDGE_FLOW_ST_MASK); + + /* delete current edge flow and capacity */ + pEdge->flow = (pEdge->flow & ~EDGE_FLOW_MASK); + pEdge->cap = (pEdge->cap & ~EDGE_FLOW_MASK); + nDots = 2*delta; + } + return nDots; +} + + +/* + bAddNewVertex( ... ) + + Connect new (fictitious, temporary) vertex to nVertDoubleBond by a new edge + Add radical (set st-cap=1) to the new vertex, set cap=1 to the new edge + Add radical (set st-cap=1) to nVertSingleBond + Find augmenting path connecting new vertex to nVertSingleBond + This corresponds to moving H-atom from nVertSingleBond to nVertDoubleBond +*/ +int bAddNewVertex( BN_STRUCT *pBNS, + int nVertDoubleBond, + int nCap, + int nFlow, + int nMaxAdjEdges, + int *nDots ) +{ + Vertex vlast = pBNS->num_vertices - 1; + Vertex vnew = pBNS->num_vertices; + Vertex v2 = nVertDoubleBond; + BNS_VERTEX *pVert2 = pBNS->vert + v2; /* pointer to an old vertex */ + BNS_VERTEX *pNewVert = pBNS->vert + vnew; /* pointer to a new vertex */ + + EdgeIndex iedge = pBNS->num_edges; + BNS_EDGE *pEdge = pBNS->edge + iedge; /* pointer to a new edge */ + + if ( iedge >= pBNS->max_edges || vnew >= pBNS->max_vertices ) { + return BNS_VERT_EDGE_OVFL; /* edges or vertices overflow */ + } + if ( (pBNS->vert[vlast].iedge - pBNS->iedge) + pBNS->vert[vlast].max_adj_edges + nMaxAdjEdges >= pBNS->max_iedges ) { + return BNS_VERT_EDGE_OVFL; /* iedges overflow */ + } + if ( pVert2->num_adj_edges >= pVert2->max_adj_edges || nMaxAdjEdges <= 0 ) { + return BNS_VERT_EDGE_OVFL; /* neighbors overflow */ + } + /* fill out the new edge, set its cap and flow, connect */ + /* memset( pEdge, 0, sizeof(*pEdge) ); */ + pEdge->cap = pEdge->cap0 = nCap; + pEdge->flow = pEdge->flow0 = nFlow; + pEdge->pass = 0; + pEdge->neighbor1 = v2; + pEdge->neighbor12 = v2 ^ vnew; + pEdge->forbidden = 0; + /* fill out the new vertex */ + /* memset( pNewVert, 0, sizeof(*pNewVert) ); */ + pNewVert->max_adj_edges = nMaxAdjEdges; + pNewVert->num_adj_edges = 0; + pNewVert->st_edge.cap0 = pNewVert->st_edge.cap = nCap; + pNewVert->st_edge.flow0 = pNewVert->st_edge.flow = nFlow; + pNewVert->st_edge.pass = 0; /* add initialization; added 2006-03-25 */ + pNewVert->iedge = pBNS->vert[vlast].iedge + pBNS->vert[vlast].max_adj_edges; + pNewVert->type = BNS_VERT_TYPE_TEMP; + *nDots += nCap - nFlow; + + pEdge->neigh_ord[v2>vnew] = pVert2->num_adj_edges; + pEdge->neigh_ord[v2num_adj_edges; + + /* connect new edge to v2 */ + pVert2->iedge[pVert2->num_adj_edges ++] = iedge; + /* connect new edge to vnew */ + pNewVert->iedge[pNewVert->num_adj_edges ++] = iedge; + + /* fix v2 flow and cap */ + *nDots -= (int)pVert2->st_edge.cap - (int)pVert2->st_edge.flow; + pVert2->st_edge.flow += nFlow; + if ( pVert2->st_edge.cap < pVert2->st_edge.flow ) { + pVert2->st_edge.cap = pVert2->st_edge.flow; + } + *nDots += (int)pVert2->st_edge.cap - (int)pVert2->st_edge.flow; + + pBNS->num_edges ++; + pBNS->num_vertices ++; + + return vnew; +} + + +int AddNewEdge( BNS_VERTEX *p1, + BNS_VERTEX *p2, + BN_STRUCT *pBNS, + int nEdgeCap, + int nEdgeFlow ) +{ + int ip1 = (int) (p1 - pBNS->vert); + int ip2 = (int) (p2 - pBNS->vert); + int ie = pBNS->num_edges; + BNS_EDGE *e = pBNS->edge + ie; + /* debug: check bounds */ + if ( ip1 >= pBNS->max_vertices || ip1 < 0 || + ip2 >= pBNS->max_vertices || ip2 < 0 || + ie >= pBNS->max_edges || ie < 0 || + (p1->iedge - pBNS->iedge) < 0 || + (p1->iedge - pBNS->iedge) + p1->max_adj_edges > pBNS->max_iedges || + (p2->iedge - pBNS->iedge) < 0 || + (p2->iedge - pBNS->iedge) + p2->max_adj_edges > pBNS->max_iedges || + p1->num_adj_edges >= p1->max_adj_edges || + p2->num_adj_edges >= p2->max_adj_edges ) { + return BNS_VERT_EDGE_OVFL; + } + /* clear the edge */ + memset( e, 0, sizeof(*e) ); + /* connect */ + e->neighbor1 = inchi_min( ip1, ip2 ); + e->neighbor12 = ip1 ^ ip2; + p1->iedge[p1->num_adj_edges] = ie; + p2->iedge[p2->num_adj_edges] = ie; + e->neigh_ord[ip1 > ip2] = p1->num_adj_edges ++; + e->neigh_ord[ip1 < ip2] = p2->num_adj_edges ++; + e->cap = e->cap0 = nEdgeCap; + e->flow = e->flow0 = nEdgeFlow; + p1->st_edge.flow += nEdgeFlow; + p2->st_edge.flow += nEdgeFlow; + if ( p1->st_edge.cap < p1->st_edge.flow ) { + p1->st_edge.cap = p1->st_edge.flow; + } + if ( p2->st_edge.cap < p2->st_edge.flow ) { + p2->st_edge.cap = p2->st_edge.flow; + } + pBNS->num_edges ++; + return ie; +} + + +BNS_IEDGE GetEdgeToGroupVertex( BN_STRUCT *pBNS, Vertex v1, AT_NUMB type) +{ + if ( v1 < pBNS->num_atoms ) { + Vertex v2; + BNS_EDGE *pEdge1; + BNS_VERTEX *pVert1 = pBNS->vert+v1; + int i = pVert1->num_adj_edges-1; + + while( 0 <= i ) { + pEdge1 = pBNS->edge + pVert1->iedge[i]; + v2 = pEdge1->neighbor12 ^ v1; + if ( pBNS->vert[v2].type == type ) { + return IS_FORBIDDEN(pEdge1->forbidden, pBNS)? NO_VERTEX : pVert1->iedge[i]; + } + i --; + } + return NO_VERTEX; /* not found t-group */ + } else + if ( v1 < pBNS->num_vertices ) { + return NO_VERTEX; + } + return BNS_VERT_EDGE_OVFL; +} + + +Vertex GetGroupVertex(BN_STRUCT *pBNS, Vertex v1, AT_NUMB type) +{ + if ( v1 < pBNS->num_atoms ) { + Vertex v2; + BNS_EDGE *pEdge1; + BNS_VERTEX *pVert1 = pBNS->vert+v1; + int i = pVert1->num_adj_edges-1; + + AT_NUMB type2; + + if ( type == BNS_VERT_TYPE_ENDPOINT ) + type2 = BNS_VERT_TYPE_TGROUP; + else + if ( type == BNS_VERT_TYPE_C_POINT ) + type2 = BNS_VERT_TYPE_C_GROUP; + else + type2 = 0; + + if ( (pVert1->type & type) == type ) { + while( 0 <= i ) { + pEdge1 = pBNS->edge + pVert1->iedge[i]; + v2 = pEdge1->neighbor12 ^ v1; + if ( pBNS->vert[v2].type == type2 ) { + if ( IS_FORBIDDEN(pEdge1->forbidden, pBNS) ) { + return NO_VERTEX; + } + return v2; + } + i --; + } + } + return BNS_BOND_ERR; /* not found t-group */ + } else + if ( v1 < pBNS->num_vertices ) { + return NO_VERTEX; + } + return BNS_VERT_EDGE_OVFL; +} + + +int bAddStCapToAVertex( BN_STRUCT *pBNS, + Vertex v1, + Vertex v2, + VertexFlow *nOldCapVertSingleBond, + int *nDots, + int bAdjacentDonors ) +{ + BNS_VERTEX *pVert1 = pBNS->vert + v1; + BNS_VERTEX *pVert; + BNS_EDGE *pEdge; + Vertex v; + int i, n; + VertexFlow nNewCap; + /* Change v1: increment its st-cap */ + n = 0; + nOldCapVertSingleBond[n++] = pVert1->st_edge.cap; + /*if ( pVert1->st_edge.cap == pVert1->st_edge.flow ) {*/ + pVert1->st_edge.cap ++; + *nDots += 1; + /*}*/ + /* increment caps of adjacent edges if + (1) the neighbor has st-cap != 0 and + (2) (edge cap==0) OR (nSumEdgeCap < pVert1->st_edge.cap && pVert->st_edge.flow > pVert1->st_edge.cap) + */ + if ( !(pVert1->type & BNS_VERT_TYPE_ANY_GROUP) ) { + /* + AT_NUMB nSumEdgeCap = 0; + for ( i = 0; i < pVert1->num_adj_edges; i ++ ) { + pEdge = pBNS->edge + pVert1->iedge[i]; + nSumEdgeCap += pEdge->cap; + } + */ + /* do not increment caps of t-group or c-group edges */ + for ( i = 0; i < pVert1->num_adj_edges; i ++ ) { + pEdge = pBNS->edge + pVert1->iedge[i]; + nOldCapVertSingleBond[n++] = pEdge->cap; /* save edge cap */ + v = pEdge->neighbor12 ^ v1; + if ( v == v2 && !bAdjacentDonors ) { + continue; + } + pVert = pBNS->vert + v; + if ( pVert->type & BNS_VERT_TYPE_ANY_GROUP ) + continue; + nNewCap = inchi_min(pVert->st_edge.cap, pVert1->st_edge.cap); + nNewCap = inchi_min(nNewCap, MAX_BOND_EDGE_CAP); + pEdge->cap = nNewCap; /* change edge cap */ + /* + if ( pVert->st_edge.cap > 0 && !pEdge->cap ) { + pEdge->cap ++; + } else + if ( pVert->st_edge.flow > pVert1->st_edge.cap && + pEdge->cap < MAX_BOND_EDGE_CAP && + nSumEdgeCap < pVert1->st_edge.cap ) { + pEdge->cap ++; + } + */ + } + } + + return n; /* number of elements in nOldCapVertSingleBond[*] */ +} + + +#define BNS_CHK_ALTP_NO_ALTPATH 0 +#define BNS_CHK_ALTP_SAME_TGROUP 1 +#define BNS_CHK_ALTP_SAME_VERTEX 2 +#define BNS_CHK_ALTP_SET_SUCCESS 4 + + +int bSetBnsToCheckAltPath( BN_STRUCT *pBNS, + int nVertDoubleBond, + int nVertSingleBond, + AT_NUMB type, + int path_type, + ALT_PATH_CHANGES *apc, + BNS_FLOW_CHANGES *fcd, + int *nDots ) +{ + + if ( !pBNS->vert[nVertDoubleBond].st_edge.flow && + + !( path_type == ALT_PATH_MODE_REM2H_CHG || + path_type == ALT_PATH_MODE_ADD2H_CHG || + path_type == ALT_PATH_MODE_REM2H_TST || + path_type == ALT_PATH_MODE_ADD2H_TST ) + ) { + return BNS_CHK_ALTP_NO_ALTPATH; + } else { + + + Vertex vNew; + Vertex v1 = nVertSingleBond; + Vertex v2 = nVertDoubleBond; + + BNS_VERTEX *pVert1 = pBNS->vert + v1; + BNS_VERTEX *pVert2 = pBNS->vert + v2; + int n, bAdjacentDonors = 0; + int ifcd = 0; + + Vertex t1=NO_VERTEX; + Vertex t2=NO_VERTEX; + int iapc; + +/*#if ( TEST_REMOVE_S_ATOMS == 1 )*/ /* && ALT_PATH_MODE_4_SALT == path_type */ + if ( ( *pBNS->pbTautFlags & TG_FLAG_TEST_TAUT2_SALTS ) && + + ALT_PATH_MODE_4_SALT2 == path_type && + (BNS_VERT_TYPE_ENDPOINT & type) ) { + +/* +--------------------------------------------------------- + \ action | DB action (v2) | SB action (v1) | +vertex \ | accept H @ vertex | donate H @ vertex | +type \ | nVertDoubleBond | nVertSingleBond | +----------------+-------------------+-------------------+ + -ZH (v1) | error | -ZH(.) | +(cap>0 on edge | | increment | + except v1-v2) | | st-cap on Z | +----------------+-------------------+-------------------+ + =Z (v2) | =Z-(.) | error | + (st-flow>0) | add fict vertex | | + | with st-cap=1 | | +----------------+-------------------+-------------------+ + endpoint | T(.) | T-(.) | + of t-group | increment | add fict vertex | + represented | st-cap on T | with st-cap=1 | + by fictitious | | | + vertex T | | | +--------------------------------------------------------- +*/ + + int bSet_v1; /* indicator: v1 has been set */ + int bSet_v2; /* indicator: v2 has been set */ + int i; + + Vertex v1t = NO_VERTEX; + Vertex v2t = NO_VERTEX; + Vertex v1Act, v2Act; + Vertex v; + + memset( apc, 0, sizeof(*apc) ); + fcd[ifcd].iedge = NO_VERTEX; + *nDots = 0; + + if ( v1 == v2 ) { + return BNS_CHK_ALTP_SAME_VERTEX; + } + + /* check whether v1 has neighbors adjacent to + multiple bonds + */ + for ( i = 0, n = 0; i < pVert1->num_adj_edges; i ++ ) { + v = (pBNS->edge + pVert1->iedge[i])->neighbor12 ^ v1; /* v is adjacent to v1 */ + if ( v == v2 ) + continue; /* ignore connection to v2 */ + n += (pBNS->vert[v].st_edge.cap > 0); + } + if ( !n ) { + return BNS_CHK_ALTP_NO_ALTPATH; /* the vertex cannot have flow */ + } + + v1Act = v1; + v2Act = v2; + + /* find t-group that contains v1 */ + if ( (pVert1->type & type) == type ) { + v1t = GetGroupVertex(pBNS, v1, type); + if ( IS_BNS_ERROR( v1t ) ) { + return v1t; + } + if ( v1t != NO_VERTEX ) { + v1Act = v1t; + } + } + /* find t-group that contains v2 */ + if ( (pVert2->type & type) == type ) { + v2t = GetGroupVertex(pBNS, v2, type); + if ( IS_BNS_ERROR( v2t ) ) { + return v2t; + } + if ( v2t != NO_VERTEX ) { + v2Act = v2t; + } + } + if ( v1t != NO_VERTEX && v1t == v2t ) { + return BNS_CHK_ALTP_SAME_TGROUP; + } + + bSet_v1 = bSet_v2 = 0; + /* create new edges adjacent to v1t or v2 */ + iapc = 0; + if ( v1t != NO_VERTEX ) { + /* create new edge and vertex, connect to v1t */ + vNew = bAddNewVertex( pBNS, v1t, 1, 0, 1, nDots ); + if ( IS_BNS_ERROR(vNew) ) { + return vNew; + } + apc->vNewVertex[iapc] = vNew; + apc->bSetNew[iapc] = 1; + bSet_v1 = 1; + iapc ++; + } + if ( v2t == NO_VERTEX ) { + /* create new edge and vertex, connect to v2 */ + vNew = bAddNewVertex( pBNS, v2, 1, 0, 1, nDots ); + if ( IS_BNS_ERROR(vNew) ) { + return vNew; + } + apc->vNewVertex[iapc] = vNew; + apc->bSetNew[iapc] = 1; + bSet_v2 = 1; + iapc ++; + } + + /* add st-cap to v1 and/or v2t */ + iapc = 0; + if ( !bSet_v1 ) { + /* add st-cap to v1 */ + if ( v1t != NO_VERTEX ) { + return BNS_BOND_ERR; + } + n = bAddStCapToAVertex( pBNS, v1, v2Act, apc->nOldCapsVert[iapc], nDots, 0 ); + apc->bSetOldCapsVert[iapc] = n; + apc->vOldVert[iapc] = v1; + iapc ++; + } + if ( !bSet_v2 ) { + /* add st-cap to v2t */ + if ( v2t == NO_VERTEX ) { + return BNS_BOND_ERR; + } + n = bAddStCapToAVertex( pBNS, v2t, v1Act, apc->nOldCapsVert[iapc], nDots, 0 ); + apc->bSetOldCapsVert[iapc] = n; + apc->vOldVert[iapc] = v2t; + iapc ++; + } + if ( *nDots < 0 || *nDots %2 ) { + return BNS_SET_ALTP_ERR; + } + return BNS_CHK_ALTP_SET_SUCCESS; + } + + /* ( *pBNS->pbTautFlags & TG_FLAG_TEST_TAUT2_SALTS ) */ + +/*#endif*/ + + /* ( TEST_REMOVE_S_ATOMS == 1 && ALT_PATH_MODE_4_SALT == path_type ) */ + + if ( path_type == ALT_PATH_MODE_REM2H_CHG || + path_type == ALT_PATH_MODE_ADD2H_CHG || + path_type == ALT_PATH_MODE_REM2H_TST || + path_type == ALT_PATH_MODE_ADD2H_TST ) { /* added 2004-03-18 */ + + int bDonors = (path_type == ALT_PATH_MODE_REM2H_CHG) || (path_type == ALT_PATH_MODE_REM2H_TST); + + int bSet_v1; /* indicator: v1 has been set */ + int bSet_v2; /* indicator: v2 has been set */ + int i, cap = 1; + + Vertex v1t = NO_VERTEX; + Vertex v2t = NO_VERTEX; + Vertex v1Act, v2Act; + Vertex v; + + memset( apc, 0, sizeof(*apc) ); + fcd[ifcd].iedge = NO_VERTEX; + *nDots = 0; + /* + if ( v1 == v2 ) { + return BNS_CHK_ALTP_SAME_VERTEX; + } + */ + /* check whether v1 and v2 have proper neighbors */ + for ( i = 0, n = bAdjacentDonors = 0; i < pVert1->num_adj_edges; i ++ ) { + v = (pBNS->edge + pVert1->iedge[i])->neighbor12 ^ v1; /* v is adjacent to v1 */ + /* do not ignore connection to v2 + if ( v == v2 ) + continue; + */ + n += bDonors ? (pBNS->vert[v].st_edge.cap > 0) : ((pBNS->edge + pVert1->iedge[i])->flow > 0); + bAdjacentDonors += bDonors ? (v == v2) && ((pBNS->edge + pVert1->iedge[i])->flow < MAX_BOND_EDGE_CAP) : 0; + /* two donors connected by a single or double bond */ + } + if ( !n && !bAdjacentDonors ) { + return BNS_CHK_ALTP_NO_ALTPATH; /* the vertex cannot have flow */ + } + for ( i = 0, n = bAdjacentDonors = 0; i < pVert2->num_adj_edges; i ++ ) { + v = (pBNS->edge + pVert2->iedge[i])->neighbor12 ^ v2; /* v is adjacent to v2 */ + /* do not ignore connection to v1 + if ( v == v1 ) + continue; + */ + n += bDonors ? (pBNS->vert[v].st_edge.cap > 0) : ((pBNS->edge + pVert2->iedge[i])->flow > 0); + bAdjacentDonors += bDonors ? (v == v1) && ((pBNS->edge + pVert2->iedge[i])->flow < MAX_BOND_EDGE_CAP ) : 0; + /* two donors connected by a single or double bond */ + } + if ( !n && !bAdjacentDonors ) { + return BNS_CHK_ALTP_NO_ALTPATH; /* the vertex cannot have flow */ + } + + v1Act = v1; + v2Act = v2; + + /* find t-group that contains v1 */ + if ( (pVert1->type & type) == type ) { + v1t = GetGroupVertex(pBNS, v1, type); + if ( BNS_BOND_ERR == v1t ) { + v1t = NO_VERTEX; + } else + if ( IS_BNS_ERROR( v1t ) ) { + return v1t; + } else + if ( v1t != NO_VERTEX ) { + v1Act = v1t; + } + } + /* find t-group that contains v2 */ + if ( (pVert2->type & type) == type ) { + v2t = GetGroupVertex(pBNS, v2, type); + if ( BNS_BOND_ERR == v2t ) { + v2t = NO_VERTEX; + } else + if ( IS_BNS_ERROR( v2t ) ) { + return v2t; + } else + if ( v2t != NO_VERTEX ) { + v2Act = v2t; + } + } + + if ( v1t != NO_VERTEX && v1t == v2t ) { + cap = 2; /* same t-group */ + } + + /* bAddNewVertex: (bDonors != 0) == (vit != NO_VERTEX), i=1,2 */ + bSet_v1 = bSet_v2 = 0; + /* create new edges adjacent to v1t or v2 */ + iapc = 0; + if ( (bDonors != 0) == (v1t != NO_VERTEX) ) { + /* create new edge and vertex, connect to v1Act */ + vNew = bAddNewVertex( pBNS, v1Act, cap, 0, 1, nDots ); + if ( IS_BNS_ERROR(vNew) ) { + return vNew; + } + apc->vNewVertex[iapc] = vNew; + apc->bSetNew[iapc] = 1; + bSet_v1 = 1; + iapc ++; + } + if ( (bDonors != 0) == (v2t != NO_VERTEX) && cap == 1 ) { + /* create new edge and vertex, connect to v2Act; do not do it if cap==2 */ + vNew = bAddNewVertex( pBNS, v2Act, cap, 0, 1, nDots ); + if ( IS_BNS_ERROR(vNew) ) { + return vNew; + } + apc->vNewVertex[iapc] = vNew; + apc->bSetNew[iapc] = 1; + bSet_v2 = 1; + iapc ++; + } else + if ( (bDonors != 0) == (v2t != NO_VERTEX) ) { + bSet_v2 = 1; + } + + /* add st-cap to v1 and/or v2t */ + iapc = 0; + /* if cap=2 then just increment st_cap 2 times */ + if ( !bSet_v1 ) { + /* add st-cap to v1 */ + if ( (bDonors != 0) == (v1t != NO_VERTEX) ) { + return BNS_BOND_ERR; + } + n = bAddStCapToAVertex( pBNS, v1Act, v2Act, apc->nOldCapsVert[iapc], nDots, bAdjacentDonors ); + apc->bSetOldCapsVert[iapc] = n; + apc->vOldVert[iapc] = v1Act; + iapc ++; + } + if ( !bSet_v2 ) { + /* add st-cap to v2t */ + if ( (bDonors != 0) == (v2t != NO_VERTEX) ) { + return BNS_BOND_ERR; + } + n = bAddStCapToAVertex( pBNS, v2Act, v1Act, apc->nOldCapsVert[iapc], nDots, bAdjacentDonors ); + apc->bSetOldCapsVert[iapc] = n; + apc->vOldVert[iapc] = v2Act; + iapc ++; + } + if ( *nDots < 0 || *nDots %2 ) { + return BNS_SET_ALTP_ERR; + } + return BNS_CHK_ALTP_SET_SUCCESS; + } + + if ( path_type == ALT_PATH_MODE_REM_PROTON ) { /* added 2004-03-05 */ + if ( v1 >= 0 && v2 >= 0 && + (pVert1->type & BNS_VERT_TYPE_ANY_GROUP) && + (pVert2->type & BNS_VERT_TYPE_ANY_GROUP) ) { + /* create new edge and vertex, connect to v2 */ + if ( (pBNS->vert[v1].type & BNS_VERT_TYPE_C_GROUP) && + (pBNS->vert[v1].st_edge.flow == 2*pBNS->vert[v1].num_adj_edges ) ) { + /* so far in a charge group max edge flow = 1 2004-03-08 */ + return BNS_CHK_ALTP_NO_ALTPATH; + } + memset( apc, 0, sizeof(*apc) ); + fcd[ifcd].iedge = NO_VERTEX; + *nDots = 0; + iapc = 0; + + vNew = bAddNewVertex( pBNS, v2, 1, 0, 1, nDots ); + if ( IS_BNS_ERROR(vNew) ) { + return vNew; + } + apc->vNewVertex[iapc] = vNew; + apc->bSetNew[iapc] = 1; + /*iapc ++;*/ + /* add st-cap (dot) to v1 */ + n = bAddStCapToAVertex( pBNS, v1, v2, apc->nOldCapsVert[iapc], nDots, 0 ); + apc->bSetOldCapsVert[iapc] = n; + apc->vOldVert[iapc] = v1; + iapc ++; + return BNS_CHK_ALTP_SET_SUCCESS; + } + } + +#if ( NEUTRALIZE_ENDPOINTS == 1 ) /* { */ + + + *nDots = 0; + memset( apc, 0, sizeof(*apc) ); + fcd[ifcd].iedge = NO_VERTEX; + + if ( type & BNS_VERT_TYPE_ENDPOINT ) { + BNS_IEDGE iedge; + AT_NUMB type2; + int ret2; + /* prohibit charge movement */ + type2 = BNS_VERT_TYPE_C_GROUP; + iedge = GetEdgeToGroupVertex( pBNS, v1, type2 ); + if (iedge != NO_VERTEX ) { + /* set flow=1 on an edge to a c-group vertex to make sure there is no positive charge + * when moving tautomeric H-atoms + */ + ret2 = bSetFlowToCheckOneBond( pBNS, iedge, 1, fcd+ifcd ); + if ( IS_BNS_ERROR(ret2) ) { + return ret2; + } + *nDots += ret2; + while ( fcd[ifcd].iedge != NO_VERTEX ) { + ifcd ++; + } + } + iedge = GetEdgeToGroupVertex( pBNS, v2, type2 ); + if (iedge != NO_VERTEX ) { + /* set flow=1 on an edge to a c-group vertex to make sure there is no positive charge + * when moving tautomeric H-atoms + */ + ret2 = bSetFlowToCheckOneBond( pBNS, iedge, 1, fcd+ifcd ); + if ( IS_BNS_ERROR(ret2) ) { + return ret2; + } + *nDots += ret2; + while ( fcd[ifcd].iedge != NO_VERTEX ) { + ifcd ++; + } + } + /* set hydrogen counts */ + type2 = BNS_VERT_TYPE_TGROUP; + iedge = GetEdgeToGroupVertex( pBNS, v1, type2 ); + if (iedge != NO_VERTEX ) { + /* set flow=1 on an edge to a t-group vertex to make sure there is + * a moveable hydrogen atom or (-) on v1 when moving tautomeric H-atoms + */ +#if ( FIX_H_CHECKING_TAUT == 1 ) + ret2 = bSetFlowToCheckOneBond( pBNS, iedge, 1, fcd+ifcd ); + if ( IS_BNS_ERROR(ret2) ) { + return ret2; + } + *nDots += ret2; + while ( fcd[ifcd].iedge != NO_VERTEX ) { + ifcd ++; + } +#else + t1 = pBNS->edge[iedge].neighbor12 ^ v1; +#endif + } + iedge = GetEdgeToGroupVertex( pBNS, v2, type2 ); + if (iedge != NO_VERTEX ) { + /* set flow=0 on an edge to a t-group vertex to make sure there is + * no moveable hydrogen atom or (-) on v2 when moving tautomeric H-atoms + */ +#if ( FIX_H_CHECKING_TAUT == 1 ) + ret2 = bSetFlowToCheckOneBond( pBNS, iedge, 0, fcd+ifcd ); + if ( IS_BNS_ERROR(ret2) ) { + return ret2; + } + *nDots += ret2; + while ( fcd[ifcd].iedge != NO_VERTEX ) { + ifcd ++; + } +#else + t2 = pBNS->edge[iedge].neighbor12 ^ v2; +#endif + } + +#if ( FIX_H_CHECKING_TAUT == 1 ) +#else + if ( t1 == t2 && t1 != NO_VERTEX ) { + return BNS_CHK_ALTP_SAME_TGROUP; + } +#endif + iapc = 0; + /* create new edge and vertex with cap=1 at v2 and/or t1 */ + if ( t1 != NO_VERTEX ) { + /* create new edge and vertex, connect to t1 */ + vNew = bAddNewVertex( pBNS, t1, 1/*cap*/, 0/*flow*/, 1/*max_adj_edges*/, nDots ); + if ( IS_BNS_ERROR(vNew) ) { + return vNew; + } + apc->vNewVertex[iapc] = vNew; + apc->bSetNew[iapc] = 1; + iapc ++; + } + if ( t2 == NO_VERTEX ) { + /* create new edge and vertex, connect to v2 */ + vNew = bAddNewVertex( pBNS, v2, 1/*cap*/, 0/*flow*/, 1/*max_adj_edges*/, nDots ); + if ( IS_BNS_ERROR(vNew) ) { + return vNew; + } + apc->vNewVertex[iapc] = vNew; + apc->bSetNew[iapc] = 1; + iapc ++; + } + + /* add st-cap to v1 and/or v2t */ + iapc = 0; + if ( t1 == NO_VERTEX ) { + /* add st-cap to v1 */ + n = bAddStCapToAVertex( pBNS, v1, (Vertex)(t2 == NO_VERTEX? v2:t2), apc->nOldCapsVert[iapc], nDots, 0 ); + apc->bSetOldCapsVert[iapc] = n; + apc->vOldVert[iapc] = v1; + iapc ++; + } + if ( t2 != NO_VERTEX ) { + /* add st-cap to t2 */ + n = bAddStCapToAVertex( pBNS, t2, (Vertex)(t1 == NO_VERTEX? v1:t1), apc->nOldCapsVert[iapc], nDots, 0 ); + apc->bSetOldCapsVert[iapc] = n; + apc->vOldVert[iapc] = t2; + iapc ++; + } + } else { + /* create new edge and vertex, connect to v2 */ + vNew = bAddNewVertex( pBNS, v2, 1 /* cap*/, 0 /* flow */, 1 /* max_adj_edges */, nDots ); + if ( IS_BNS_ERROR(vNew) ) { + return vNew; + } + apc->vNewVertex[0] = vNew; + apc->bSetNew[0] = 1; + + /* add st-cap to v1 */ + n = bAddStCapToAVertex( pBNS, v1, v2, apc->nOldCapsVert[0], nDots, 0 ); + apc->bSetOldCapsVert[0] = n; + apc->vOldVert[0] = v1; + } +#else /* } NEUTRALIZE_ENDPOINTS == 0 {*/ + + *nDots = 0; + memset( apc, 0, sizeof(*apc) ); + fcd[ifcd].iedge = NO_VERTEX; + + /* create new edge and vertex, connect to v2 */ + vNew = bAddNewVertex( pBNS, v2, 1 /* cap*/, 0 /* flow */, 1 /* max_adj_edges */, nDots, 0 ); + if ( IS_BNS_ERROR(vNew) ) { + return vNew; + } + apc->vNewVertex[0] = vNew; + apc->bSetNew[0] = 1; + + /* add st-cap to v1 */ + n = bAddStCapToAVertex( pBNS, v1, v2, apc->nOldCapsVert[0], nDots ); + apc->bSetOldCapsVert[0] = n; + apc->vOldVert[0] = v1; +#endif /* } NEUTRALIZE_ENDPOINTS */ + + if ( *nDots < 0 || *nDots %2 ) { + return BNS_SET_ALTP_ERR; + } + return BNS_CHK_ALTP_SET_SUCCESS; + } + /*return BNS_CHK_ALTP_NO_ALTPATH;*/ +} + + +int bRestoreBnsAfterCheckAltPath( BN_STRUCT *pBNS, ALT_PATH_CHANGES *apc, int bChangeFlow ) +/* int nVertDoubleBond, int nVertSingleBond, int nNewVertex, AT_NUMB *nOldCapVertSingleBond */ +{ + BNS_EDGE *pEdge; + Vertex vNew; + Vertex vOld; + BNS_VERTEX *pOldVert; + BNS_VERTEX *pNewVert; + int i, j, n, ret; + + ret = 0; + if ( bChangeFlow & BNS_EF_UPD_H_CHARGE ) { + /* remove new temp. vertices and edges connectong them to the structure */ + for ( i = sizeof(apc->bSetNew)/sizeof(apc->bSetNew[0])-1; 0 <= i; i -- ) { + if ( apc->bSetNew[i] ) { + vNew = apc->vNewVertex[i]; + pNewVert = pBNS->vert + vNew; + for ( j = 0; j < pNewVert->num_adj_edges; j ++ ) { + pEdge = pBNS->edge+pNewVert->iedge[j]; + vOld = pEdge->neighbor12 ^ vNew; + pOldVert = pBNS->vert + vOld; + pOldVert->st_edge.flow -= pEdge->flow; + pOldVert->st_edge.cap -= pEdge->flow; + /* disconnect new edge from pOldVert */ + pOldVert->iedge[--pOldVert->num_adj_edges] = 0; + /* clear the new edge */ + memset( pEdge, 0, sizeof(*pEdge) ); + /* and decrement the total number of edges */ + pBNS->num_edges --; + } + /* clear the new vertex */ + memset( pNewVert, 0, sizeof( *pNewVert ) ); + /* and decrement the total number of vertices (new vertice ids are contiguous */ + pBNS->num_vertices --; + ret ++; + } + } + /* Restore changed caps of old vertices */ + for ( i = sizeof(apc->bSetOldCapsVert)/sizeof(apc->bSetOldCapsVert[0])-1; 0 <= i; i -- ) { + if ( n = apc->bSetOldCapsVert[i] ) { + pOldVert = pBNS->vert + apc->vOldVert[i]; + if ( pOldVert->st_edge.flow <= apc->nOldCapsVert[i][0] ) { + pOldVert->st_edge.cap = apc->nOldCapsVert[i][0]; + n --; + ret ++; + for ( j = 0; j < n && j < pOldVert->num_adj_edges; j ++ ) { + pEdge = pBNS->edge + pOldVert->iedge[j]; + pEdge->cap = apc->nOldCapsVert[i][j+1]; + } + } + } + } + } else { + /* Restore changed caps of old vertices */ + for ( i = sizeof(apc->bSetOldCapsVert)/sizeof(apc->bSetOldCapsVert[0])-1; 0 <= i; i -- ) { + if ( n = apc->bSetOldCapsVert[i] ) { + pOldVert = pBNS->vert + apc->vOldVert[i]; + pOldVert->st_edge.cap = apc->nOldCapsVert[i][0]; + n --; + ret ++; + for ( j = 0; j < n && j < pOldVert->num_adj_edges; j ++ ) { + pEdge = pBNS->edge + pOldVert->iedge[j]; + pEdge->cap = apc->nOldCapsVert[i][j+1]; + } + } + } + + /* remove new temp. vertices and edges connectong them to the structure */ + for ( i = sizeof(apc->bSetNew)/sizeof(apc->bSetNew[0])-1; 0 <= i; i -- ) { + if ( apc->bSetNew[i] ) { + vNew = apc->vNewVertex[i]; + pNewVert = pBNS->vert + vNew; + for ( j = 0; j < pNewVert->num_adj_edges; j ++ ) { + pEdge = pBNS->edge+pNewVert->iedge[j]; + vOld = pEdge->neighbor12 ^ vNew; + pOldVert = pBNS->vert + vOld; + /* disconnect new edge from pOldVert */ + pOldVert->iedge[--pOldVert->num_adj_edges] = 0; + /* clear the new edge */ + memset( pEdge, 0, sizeof(*pEdge) ); + /* and decrement the total number of edges */ + pBNS->num_edges --; + } + /* clear the new vertex */ + memset( pNewVert, 0, sizeof( *pNewVert ) ); + /* and decrement the total number of vertices (new vertice ids are contiguous */ + pBNS->num_vertices --; + ret ++; + } + } + } + return 0; +} + + +int bExistsAnyAltPath( CANON_GLOBALS *pCG, BN_STRUCT *pBNS, BN_DATA *pBD, + inp_ATOM *at, int num_atoms, + int nVert2, int nVert1, int path_type ) +{ + int nRet1, nRet2; + nRet1 = bExistsAltPath( pCG, pBNS, pBD, NULL, at, num_atoms, nVert2, nVert1, path_type ); + if ( nRet1 > 0 ) + return nRet1; + nRet2 = bExistsAltPath( pCG, pBNS, pBD, NULL, at, num_atoms, nVert1, nVert2, path_type ); + if ( nRet2 > 0 ) + return nRet2; + if ( IS_BNS_ERROR( nRet1 ) ) + return nRet1; + if ( IS_BNS_ERROR( nRet2 ) ) + return nRet2; + return 0; +} + + +#define ALT_PATH_TAUTOM 1 +#define ALT_PATH_CHARGE 2 +#define ALT_PATH_4_SALT 3 + + +int bIsBnsEndpoint( BN_STRUCT *pBNS, int v ) +{ + int i, vt; + BNS_VERTEX *pVert; /* vertices */ + BNS_EDGE *pEdge; /* edges */ + + if ( 0 <= v && v < pBNS->num_atoms && (pVert = pBNS->vert+v) && (pVert->type & BNS_VERT_TYPE_ENDPOINT) ) { + for ( i = pVert->num_adj_edges - 1; 0 <= i; i -- ) { + pEdge = pBNS->edge + pVert->iedge[i]; + vt = pEdge->neighbor12 ^ v; + if ( pBNS->vert[vt].type & BNS_VERT_TYPE_TGROUP ) { + return !IS_FORBIDDEN(pEdge->forbidden, pBNS); + } + } + } + return 0; +} + + + +#if ( BNS_RAD_SEARCH == 1 ) + +int bRadChangesAtomType( BN_STRUCT *pBNS, + BN_DATA *pBD, + Vertex v, + Vertex v_1, + Vertex v_2 ) +{ + + EdgeIndex iuv; + Vertex v_O, v_ChgOrH; + /* the previous atom along the path: should be a terminal atom */ + if ( v_1 == NO_VERTEX ) { + v_1 = GetPrevVertex( pBNS, v, pBD->SwitchEdge, &iuv ); + } + v_O = v_1 / 2 - 1; + if ( v_O < 0 || v_O >= pBNS->num_atoms ) { + return 0; + } + /* make sure v_O is a terminal atom: its second neighbor is not an atom */ + if ( pBNS->vert[pBNS->edge[pBNS->vert[v_O].iedge[1]].neighbor12 ^ v_O].type & BNS_VERT_TYPE_ATOM ) { + return 0; + } + /* the next to previous vertex vertex along the path: should be a Charge or Taut group vertex */ + if ( v_2 == NO_VERTEX ) { + v_2 = GetPrevVertex( pBNS, v_1, pBD->SwitchEdge, &iuv ); + } + v_ChgOrH = v_2 / 2 - 1; + if ( v_ChgOrH < pBNS->num_atoms ) { + return 0; + } + /* make sure v_ChgOrH is a charge or taut_group */ + if ( pBNS->vert[v_ChgOrH].type & (BNS_VERT_TYPE_TGROUP | BNS_VERT_TYPE_C_GROUP) ) + return 1; + return 0; +} + + +int RegisterRadEndpoint( BN_STRUCT *pBNS, BN_DATA *pBD, Vertex u) +{ + EdgeIndex iuv; + int i, num_found; + Vertex v, w; + Vertex u_last, v2; + switch( pBD->bRadSrchMode ) { + case RAD_SRCH_NORM: + /* go backwards along alt path and stop at the 1st found atom (not a fictitious vertex) */ + /* we need only vertices where a radical may be moved, therefore exclude u%2=1 (odd) vertices */ + /* atom number = u/2-1; u = 0 or 1 is 's' or 't' vertices, respectively, they are not atoms */ + num_found = 0; + while ( u > Vertex_t && (u % 2 || u/2 > pBNS->num_atoms ) ) { + u = GetPrevVertex( pBNS, u, pBD->SwitchEdge, &iuv ); + } + w = u/2 - 1; /* Check whether u is a radical endpoint */ + if ( Vertex_t < u && w < pBNS->num_atoms && + pBNS->vert[w].st_edge.cap == (pBNS->vert[w].st_edge.flow & EDGE_FLOW_ST_MASK) ) { + /* u is an atom; it is not a radical atom */ + /* now search for the starting radical atom by following the path back from u */ + v = u_last = u; + while( v > Vertex_t ) { + u = v; + v = GetPrevVertex( pBNS, u, pBD->SwitchEdge, &iuv ); /* Radical endpoint */ + } + /* check whether u is a radical atom */ + if ( !(u%2) && Vertex_t < u && + (u = u/2 - 1) < pBNS->num_atoms && + pBNS->vert[u].st_edge.cap > (pBNS->vert[u].st_edge.flow & EDGE_FLOW_ST_MASK) ) { + /* at pBNS->vert[u] we have found the radical that originated the path */ + /* pBD->RadEndpoints[2k] is the radical, pBD->RadEndpoints[2k+1] is the farthest atom */ + /* to which the radical may be moved (farthest reachable atom) */ + + /* add *all* atoms that may receive radical from u_rad */ + /* exception: at2 in: ==(+/-/H)---at1==at2(possible rad endpoint) if pBNS->type_TACN */ + + for ( v = u_last; v > Vertex_t; v = GetPrevVertex( pBNS, v, pBD->SwitchEdge, &iuv ) ) { + if ( !(v%2) && (v2 = v/2 - 1) < pBNS->num_atoms && + pBNS->vert[v2].st_edge.cap == (pBNS->vert[v2].st_edge.flow & EDGE_FLOW_ST_MASK) ) { + /* check exception */ + if ( pBNS->type_TACN && + bRadChangesAtomType( pBNS, pBD, v, NO_VERTEX, NO_VERTEX ) ) { + continue; + } + /* add */ + for ( i = 0; i < pBD->nNumRadEndpoints; i += 2 ) { + /* check whether this pair, (u,w), has already been saved */ + if ( u == pBD->RadEndpoints[i] && + v2 == pBD->RadEndpoints[i+1] ) { + break; + } + } + if ( i >= pBD->nNumRadEndpoints ) { + /* add new (u,w) pair */ + if ( pBD->nNumRadEndpoints+2 <= pBD->max_num_vertices ) { + /* add */ + pBD->RadEndpoints[pBD->nNumRadEndpoints ++] = u; /* radical */ + pBD->RadEndpoints[pBD->nNumRadEndpoints ++] = v2; /* endpoint */ + num_found ++; + /*return 1;*/ /* registered */ + } else { + return BNS_VERT_EDGE_OVFL; + } + } + } + } + if ( num_found ) { + return 1; + } + } + } + break; + + case RAD_SRCH_FROM_FICT: + /* find nearest atom accessible from a fictitious vertex */ + /* go backwards along alt path and stop at the 1st found atom (not a fictitious vertex) */ + v = u; + w = NO_VERTEX; /* the nearest atom -- radical-endpoint */ + u = NO_VERTEX; /* fictitious vertex carrying a radical */ + while ( v > Vertex_t ) { + u = v; + if ( !(v % 2) && v/2 <= pBNS->num_atoms && + pBNS->vert[v/2-1].st_edge.cap - pBNS->vert[v/2-1].st_edge.flow < 2 ) { + w = v; /* vertex w is atom that may be singlet or doublet but not triplet */ + } + v = GetPrevVertex( pBNS, u, pBD->SwitchEdge, &iuv ); + } + v = u/2 - 1; /* vertex u may be the radical from which the path originated; w is the nearest atom */ + if ( w == NO_VERTEX || u == NO_VERTEX || w % 2 || u == w || v < pBNS->num_atoms || + pBNS->vert[v].st_edge.cap == pBNS->vert[v].st_edge.flow || + (w = w/2 - 1) >= pBNS->num_atoms ) { + break; /* reject */ + } + u = v; + /* at pBNS->vert[u] we have found the radical that originated the path, w is the nearest atom */ + for ( i = 0; i < pBD->nNumRadEndpoints; i += 2 ) { + if ( u == pBD->RadEndpoints[i] && + w == pBD->RadEndpoints[i+1] ) { + break; /* this pair has already been stored */ + } + } + if ( i >= pBD->nNumRadEndpoints ) { + /* a new pair has been found */ + if ( pBD->nNumRadEndpoints+2 <= pBD->max_num_vertices ) { + /* add */ + pBD->RadEndpoints[pBD->nNumRadEndpoints ++] = u; /* radical */ + pBD->RadEndpoints[pBD->nNumRadEndpoints ++] = w; /* endpoint */ + return 1; /* registered */ + } else { + return BNS_VERT_EDGE_OVFL; + } + } + break; + } + + return 0; /* rejected */ +} + + +int cmp_rad_endpoints( const void *a1, const void *a2 ) +{ + /* Vertex radical_vertex, radical_endpoint */ + const Vertex *p1 = (const Vertex *)a1; + const Vertex *p2 = (const Vertex *)a2; + if ( p1[0] < p2[0] ) + return -1; + if ( p1[0] > p2[0] ) + return 1; + if ( p1[1] < p2[1] ) + return -1; + if ( p1[1] > p2[1] ) + return 1; + return 0; +} + + +int RemoveRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at ) +{ + BNS_EDGE *e; + EdgeIndex ie; + BNS_VERTEX *p1, *p2; + Vertex v1, v2; + int i, delta, rad; + for ( i = pBD->nNumRadEdges-1; 0 <= i; i -- ) { + ie = pBD->RadEdges[i]; + if ( ie < 0 || ie >= pBNS->num_edges ) { + goto error_exit; + } + e = pBNS->edge + ie; + v1 = e->neighbor1; + v2 = e->neighbor12 ^ v1; /* v2 > v1 <=> v2 was added later */ + if ( ie + 1 != pBNS->num_edges || + v1 < 0 || v1 >= pBNS->num_vertices || + v2 < 0 || v2 >= pBNS->num_vertices ) { + goto error_exit; + } + p1 = pBNS->vert + v1; + p2 = pBNS->vert + v2; + + if ( p2->iedge[p2->num_adj_edges-1] != ie || + p1->iedge[p1->num_adj_edges-1] != ie ) { + goto error_exit; + } + p2->num_adj_edges --; + p1->num_adj_edges --; + p2->iedge[p2->num_adj_edges] = 0; + p1->iedge[p1->num_adj_edges] = 0; + p2->st_edge.flow -= e->flow; + p1->st_edge.flow -= e->flow; + + if ( !p2->num_adj_edges && v2 >= pBNS->num_atoms ) { + if ( v2+1 != pBNS->num_vertices ) { + goto error_exit; + } + memset( p2, 0, sizeof(*p2) ); + pBNS->num_vertices --; + } + if ( !p1->num_adj_edges && v1 >= pBNS->num_atoms ) { + if ( v1+1 != pBNS->num_vertices ) { + goto error_exit; + } + memset( p1, 0, sizeof(*p1) ); + pBNS->num_vertices --; + } + if ( at && v1 < pBNS->num_atoms ) { + delta = p1->st_edge.cap - p1->st_edge.flow; + rad = at[v1].radical; + switch( delta ) { + case 0: + if ( rad == RADICAL_DOUBLET ) + rad = 0; + break; + case 1: + if ( rad != RADICAL_DOUBLET ) + rad = RADICAL_DOUBLET; + } + at[v1].radical = rad; + } + memset( e, 0, sizeof(*e) ); + pBNS->num_edges --; + } + pBD->nNumRadEdges = 0; + pBD->nNumRadicals = 0; + pBD->bRadSrchMode = RAD_SRCH_NORM; + return 0; +error_exit: + return BNS_PROGRAM_ERR; +} + + +int RestoreRadicalsOnly( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at ) +{ + BNS_EDGE *e; + EdgeIndex ie; + BNS_VERTEX *p1, *p2; + Vertex v1, v2; + int i, delta, rad; + int p1_num_adj_edges, p2_num_adj_edges; + + for ( i = pBD->nNumRadEdges-1; 0 <= i; i -- ) { + ie = pBD->RadEdges[i]; + if ( ie < 0 || ie >= pBNS->num_edges ) { + goto error_exit; + } + e = pBNS->edge + ie; + v1 = e->neighbor1; /* atom */ + v2 = e->neighbor12 ^ v1; /* v2 > v1 <=> v2 was added later */ + if ( v1 < 0 || v1 >= pBNS->num_atoms || + v2 < pBNS->num_atoms || v2 >= pBNS->num_vertices ) { + goto error_exit; + } + p1 = pBNS->vert + v1; + p2 = pBNS->vert + v2; + + p1_num_adj_edges = e->neigh_ord[0]; + p2_num_adj_edges = e->neigh_ord[1]; + + if ( p2->iedge[p2_num_adj_edges] != ie || + p1->iedge[p1_num_adj_edges] != ie ) { + goto error_exit; + } + + if ( at && v1 < pBNS->num_atoms ) { + delta = p1->st_edge.cap - p1->st_edge.flow + e->flow; + rad = at[v1].radical; + switch( delta ) { + case 0: + if ( rad == RADICAL_DOUBLET ) + rad = 0; + break; + case 1: + if ( rad != RADICAL_DOUBLET ) + rad = RADICAL_DOUBLET; + } + at[v1].radical = rad; + } + } + return 0; +error_exit: + return BNS_PROGRAM_ERR; +} + + +int SetRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode ) +{ + int ret, i, j, k, num_new_edges, delta; + BNS_VERTEX *pRad, *pEndp; + Vertex wRad, vRad, vEndp, nNumRadicals; + int nDots=0 /* added initialization, 2006-03 */, nNumEdges; + if ( pBNS->tot_st_cap <= pBNS->tot_st_flow ) { + return 0; + } + pBD->nNumRadEndpoints = 0; + pBD->nNumRadEdges = 0; + pBD->bRadSrchMode = bRadSrchMode; + pBNS->alt_path = pBNS->altp[0]; + pBNS->bChangeFlow = 0; + ret = BalancedNetworkSearch( pBNS, pBD, BNS_EF_RAD_SRCH ); + ReInitBnData( pBD ); + ReInitBnStructAltPaths( pBNS ); + if ( !ret && pBD->nNumRadEndpoints >= 2 ) { + /* sort by radical locations */ + qsort( pBD->RadEndpoints, pBD->nNumRadEndpoints/2, 2*sizeof(pBD->RadEndpoints[0]), cmp_rad_endpoints ); + num_new_edges = 0; + nNumRadicals = 0; + /* create new vertices (type=BNS_VERT_TYPE_TEMP) and edges with flow=cap=1 */ + /* connecting the new vertices radical vertices */ + for ( i = 0; i < pBD->nNumRadEndpoints; i = j ) { + wRad = pBD->RadEndpoints[i]; + pRad = pBNS->vert + wRad; + delta = pRad->st_edge.cap - (pRad->st_edge.flow & EDGE_FLOW_ST_MASK); + if ( delta <= 0 ) { + delta = 1; + } + nNumEdges = 0; + for ( j = i; j < pBD->nNumRadEndpoints && wRad == pBD->RadEndpoints[j] ; j += 2 ) { + nNumEdges ++; + } + /* add new aux vertex to the radical atom/vertex */ + vRad = bAddNewVertex( pBNS, wRad, delta, delta, nNumEdges+1, &nDots ); + if ( IS_BNS_ERROR( vRad ) ) { + ret = vRad; + goto error_exit; + } + pRad = pBNS->vert + vRad; + pBD->RadEdges[pBD->nNumRadEdges ++] = pRad->iedge[pRad->num_adj_edges-1]; + /* replace references to vertex wRad with vRad */ + for ( k = i, nNumEdges = 0; k < j; k += 2 ) { + pBD->RadEndpoints[k] = vRad; + } + nNumRadicals ++; + } + /* all vRad vertex indices should be in the range vFirstNewVertex...vFirstNewVertex+nNumRadicals-1 */ + /* connect new vertices to the radical endpoints thus replacing radicals with even-length alternating cycles */ + for ( i = 0; i < pBD->nNumRadEndpoints; i = j ) { + vRad = pBD->RadEndpoints[i]; + pRad = pBNS->vert + vRad; + for ( j = i; j < pBD->nNumRadEndpoints && vRad == pBD->RadEndpoints[j] ; j += 2 ) { + /* connect vew vertex pRad to radical endpoints */ + vEndp = pBD->RadEndpoints[j+1]; + pEndp = pBNS->vert + vEndp; + ret = AddNewEdge( pRad, pEndp, pBNS, 1, 0 ); + if ( IS_BNS_ERROR( ret ) ) { + goto error_exit; + } + pBD->RadEdges[pBD->nNumRadEdges ++] = ret; + } + } + pBD->nNumRadicals = nNumRadicals; + return nNumRadicals; /* done */ + } + return 0; /* nothing to do */ + +error_exit: + RemoveRadEndpoints( pBNS, pBD, NULL ); + return ret; +} + + +#define MAX_NUM_RAD 256 + + +int SetRadEndpoints2( CANON_GLOBALS *pCG, + BN_STRUCT *pBNS, BN_DATA *pBD, + BRS_MODE bRadSrchMode ) +{ + int ret = 0, i, j, k, n, num_new_edges, delta = 1; + BNS_VERTEX *pRad, *pEndp; + Vertex wRad, vRad, vEndp, nNumRadicals; + Vertex vRadList[MAX_NUM_RAD], vRadEqul[MAX_NUM_RAD]; + int nNumRad = 0; + int edge_flow; + int nDots=0 /* added initialization, 2006-03 */, nNumEdges; + NodeSet VertSet; + if ( pBNS->tot_st_cap <= pBNS->tot_st_flow ) { + return 0; + } + /* find all radicals: their vertices have st_cap-st_flow=delta */ + /* save radical atom numbers in vRadList[] and remove radical by making st_cap=st_flow */ + for ( i = 0; i < pBNS->num_atoms; i ++ ) { + if ( pBNS->vert[i].st_edge.cap - delta == (pBNS->vert[i].st_edge.flow & EDGE_FLOW_ST_MASK) ) { + if ( nNumRad < MAX_NUM_RAD ) { + pBNS->vert[i].st_edge.cap -= delta; + pBNS->tot_st_cap -= delta; + vRadList[nNumRad] = i; /* radical position; i > j <=> vRadList[i] > vRadList[j] */ + vRadEqul[nNumRad] = nNumRad; /* the smallest radical atom that has reachable + * atoms in common with this radical atom + * always keep vRadEqul[nNumRad] <= nNumRad */ + nNumRad ++; + } + } + } + if ( pBNS->tot_st_cap - pBNS->tot_st_flow > nNumRad ) { + return BNS_CAP_FLOW_ERR; /* extra st_cap on non-atoms or program error */ + } + memset( &VertSet, 0, sizeof(VertSet) ); + /* find reachable atoms by enabling each radical separately */ + for ( j = 0; j < nNumRad; j ++ ) { + i = vRadList[j]; + pBD->nNumRadEndpoints = 0; + pBD->nNumRadEdges = 0; + pBD->bRadSrchMode = bRadSrchMode; + pBNS->alt_path = pBNS->altp[0]; + pBNS->bChangeFlow = 0; + pBNS->vert[i].st_edge.cap += delta; /* enable single radical */ + pBNS->tot_st_cap += delta; + ret = BalancedNetworkSearch( pBNS, pBD, BNS_EF_RAD_SRCH ); /* find reachable atoms */ + ReInitBnData( pBD ); + ReInitBnStructAltPaths( pBNS ); + pBD->bRadSrchMode = RAD_SRCH_NORM; + pBNS->vert[i].st_edge.cap -= delta; /* disable single radical */ + pBNS->tot_st_cap -= delta; + if ( IS_BNS_ERROR( ret ) ) { + goto error_exit; + } else + if ( ret ) { + ret = BNS_RADICAL_ERR; /* found augmenting path: should not happen since only one radical was enabled */ + goto error_exit; + } + if ( !ret && pBD->nNumRadEndpoints >= 2 ) + { + /* sort by: primary_key=radical locations, secondary_key=radical endoint */ + qsort( pBD->RadEndpoints, pBD->nNumRadEndpoints/2, 2*sizeof(pBD->RadEndpoints[0]), cmp_rad_endpoints ); + if ( pBD->RadEndpoints[0] != i || pBD->RadEndpoints[pBD->nNumRadEndpoints-2] != i ) + { + ret = BNS_RADICAL_ERR; /* more than one radical vertex */ + goto error_exit; + } + if ( nNumRad > 1 ) + { + /* if more than one radical then save reachable atoms in bitmaps to allow */ + /* faster finding whether same atoms are reachable by two or more radicals */ + /* Later merge such sets */ + if ( NULL == VertSet.bitword ) + { + SetBitCreate( pCG ); + if ( !NodeSetCreate( pCG, &VertSet, pBNS->num_atoms, nNumRad ) ) + { + ret = BNS_OUT_OF_RAM; /* out of RAM */ + goto error_exit; + } + } + NodeSetFromRadEndpoints( pCG, &VertSet, j, pBD->RadEndpoints, pBD->nNumRadEndpoints); + /* do not allow any radical center be treated as a reachable atom: */ + RemoveFromNodeSet( pCG, &VertSet, j, vRadList, nNumRad ); + } + } + } + /* restore radical st_cap so that st_cap-st_flow=delta */ + for ( j = 0; j < nNumRad; j ++ ) { + i = vRadList[j]; + pBNS->vert[i].st_edge.cap += delta; + pBNS->tot_st_cap += delta; + } + /* merge lists that have common radical endpoints */ + /* defect: if vertex sets i and j do not intersect they will be compared 2 times */ + /* total up to nNumRad*(nNumRad-1)/2 calls to DoNodeSetsIntersect() */ + if ( nNumRad > 1 ) { + for ( i = 0; i < nNumRad; i ++ ) { + if ( vRadEqul[i] != i ) + continue; + do { + n = 0; + for ( j = i+1; j < nNumRad; j ++ ) { + if ( vRadEqul[j] != j ) + continue; + if ( DoNodeSetsIntersect( &VertSet, i, j) ) { + AddNodeSet2ToNodeSet1( &VertSet, i, j); + vRadEqul[j] = i; /* Set j was copied to set i; i < j */ + n ++; + } + } + } while( n ); + } + /* fill out pBD->RadEndpoints[] */ + for ( i = 0, n = 0; i < nNumRad; i ++ ) { + if ( i == vRadEqul[i] ) { + if ( !IsNodeSetEmpty( &VertSet, i) ) { + /* store equivalent radicals */ + for ( j = i+1; j < nNumRad; j ++ ) { + if (i == vRadEqul[j] ) { + pBD->RadEndpoints[n++] = vRadList[i]; + pBD->RadEndpoints[n++] = -vRadList[j]-2; /* equivalent radical, alvays not zero */ + } + } + /* store endpoints */ + n = AddNodesToRadEndpoints( pCG, &VertSet, i, pBD->RadEndpoints, vRadList[i], n, pBD->max_len_Pu_Pv ); + if ( n < 0 ) { + ret = BNS_RADICAL_ERR; /* pBD->RadEndpoints overflow */ + goto error_exit; + } + } else { + pBD->RadEndpoints[n++] = vRadList[i]; + pBD->RadEndpoints[n++] = -1; /* immobile radical, only one edge to add */ + } + } + } + pBD->nNumRadEndpoints = n; + NodeSetFree( pCG, &VertSet ); + } + else + if ( nNumRad == 1 && !pBD->nNumRadEndpoints ) + { + /* 2006-07-30: a single radical; no possible endpoint found */ + for ( i = 0, n = 0; i < nNumRad; i ++ ) { + pBD->RadEndpoints[n++] = vRadList[i]; + pBD->RadEndpoints[n++] = -1; /* immobile radical, only one edge to add */ + } + pBD->nNumRadEndpoints = n; + } + + if ( !ret && pBD->nNumRadEndpoints >= 2 ) { + /* already sorted by radical locations */ + num_new_edges = 0; + nNumRadicals = 0; + /************************************************************************** + * create new vertices (type=BNS_VERT_TYPE_TEMP) and edges with flow=cap=1 + * connecting the new vertices radical vertices + * + * + * Original structure: atom A is a radical center A==B--C*--D==E + * A*--B==C--D==E atoms C and E are reachable: A==B--C===D--E* + * + * Resultant temporary structure: + * A---B==C--D==E + * || / / + * || / / The additional new vertex (*) and its + * || / / 3 edges replace the radical with alternating + * || / / circuits that allow same bond changes + * || / / as moving the radical to atoms C or E. + * ||// "Double bonds" here have edge cap=1, flow=1 + * (*) "Single bonds" have edge cap=1, flow=0 + * + * The "equivalent radical centers" (which have at least one reachable atom + * in common) are connected to (*) with "double bonds" (edge cap=1, flow=1). + * Reachable non-radical atoms are connected by edges with cap=1, flow=0 + * After running BNS to find alt.path a "double bond" from (*) may move + * to another atom thus muving the radical. + * + * Number of additional (*) vertices = number of sets of + * "equivalent radical centers". + * Each such a set may include one or more radical centers. + * + * The radicals will be re-created in RemoveRadEndpoints() + ***************************************************************************/ + for ( i = 0; i < pBD->nNumRadEndpoints; i = j ) { + wRad = pBD->RadEndpoints[i]; + pRad = pBNS->vert + wRad; + delta = pRad->st_edge.cap - (pRad->st_edge.flow & EDGE_FLOW_ST_MASK); + if ( delta <= 0 ) { + delta = 1; + } + nNumEdges = 0; + for ( j = i; j < pBD->nNumRadEndpoints && wRad == pBD->RadEndpoints[j] ; j += 2 ) { + nNumEdges += (pBD->RadEndpoints[j+1] != -1); /* immobile radicals have one edge only */ + } + /* add new aux vertex to the radical atom/vertex making st_cap-st_flow=0 */ + /* in case of immobile radical there will be no additional eddges since nNumEdges=0 */ + vRad = bAddNewVertex( pBNS, wRad, delta, delta, nNumEdges+1, &nDots ); + if ( IS_BNS_ERROR( vRad ) ) { + ret = vRad; + goto error_exit; + } + pRad = pBNS->vert + vRad; + pBD->RadEdges[pBD->nNumRadEdges ++] = pRad->iedge[pRad->num_adj_edges-1]; + /* replace references to vertex wRad with vRad */ + for ( k = i, nNumEdges = 0; k < j; k += 2 ) { + pBD->RadEndpoints[k] = vRad; + } + nNumRadicals ++; + } + /* all vRad vertex indices should be in the range vFirstNewVertex...vFirstNewVertex+nNumRadicals-1 */ + /* connect new vertices to the radical endpoints thus replacing radicals with even-length alternating cycles */ + for ( i = 0; i < pBD->nNumRadEndpoints; i = j ) { + vRad = pBD->RadEndpoints[i]; + pRad = pBNS->vert + vRad; + for ( j = i; j < pBD->nNumRadEndpoints && vRad == pBD->RadEndpoints[j] ; j += 2 ) { + /* connect vew vertex pRad to radical endpoints */ + vEndp = pBD->RadEndpoints[j+1]; + if ( vEndp == -1 ) + continue; + if ( vEndp < 0 ) { + edge_flow = 1; + vEndp = -vEndp - 2; /* equivalent radical centers */ + } else { + edge_flow = 0; + } + pEndp = pBNS->vert + vEndp; + ret = AddNewEdge( pRad, pEndp, pBNS, 1, edge_flow ); + if ( IS_BNS_ERROR( ret ) ) { + goto error_exit; + } + pBD->RadEdges[pBD->nNumRadEdges ++] = ret; + } + } + pBD->nNumRadicals = nNumRadicals; + return nNumRadicals; /* done */ + } + return 0; /* nothing to do */ + +error_exit: + RemoveRadEndpoints( pBNS, pBD, NULL ); + NodeSetFree( pCG, &VertSet ); + return ret; +} + + +#else +int SetRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode ) +{ + return 0; +} +int RemoveRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at ) +{ + return 0; +} +int SetRadEndpoints2( CANON_GLOBALS *pCG, BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode ) +{ + return 0; +} +#endif + + +/* + bExistsAltPath( ... ) + + Return value ret bits if not IS_BNS_ERROR(ret): + + ret & 1 => Success + ret & 2 => Bonds changed to Alt + (ret & ~3) >> 2 => nDelta: number of removed dots +*/ +int bExistsAltPath( CANON_GLOBALS *pCG, BN_STRUCT *pBNS, BN_DATA *pBD, BN_AATG *pAATG, + inp_ATOM *at, int num_atoms, + int nVertDoubleBond, int nVertSingleBond, int path_type ) +{ + ALT_PATH_CHANGES apc; + int ret, ret_val, bError, bSuccess, bChangeFlow=0, nDots, nDelta, bDoMarkChangedBonds = 1; + int bAdjustRadicals = 0; + AT_NUMB type; + BNS_FLOW_CHANGES fcd[4*BNS_MAX_NUM_FLOW_CHANGES+1]; + ENDPOINT_INFO eif; +#if ( KETO_ENOL_TAUT == 1 ) + ENDPOINT_INFO eif2; +#endif + + /* initialize */ + switch( path_type ) { + case ALT_PATH_MODE_TAUTOM: + /* Check for alt path allowing to move H and (-). Purpose: confirm possible tautomerism */ + type = BNS_VERT_TYPE_ENDPOINT; + bChangeFlow = BNS_EF_CHNG_RSTR; + if ( !at[nVertSingleBond].endpoint && + (!nGetEndpointInfo( at, nVertSingleBond, &eif ) || !eif.cDonor ) ) + return 0; + if ( !at[nVertDoubleBond].endpoint && + (!nGetEndpointInfo( at, nVertDoubleBond, &eif ) || !eif.cAcceptor ) ) + return 0; + break; + +#if ( KETO_ENOL_TAUT == 1 ) + case ALT_PATH_MODE_TAUTOM_KET: + /* Check for alt path allowing to move H and (-). Purpose: confirm possible tautomerism */ + type = BNS_VERT_TYPE_ENDPOINT; + bChangeFlow = BNS_EF_CHNG_RSTR; + + if ( !at[nVertSingleBond].endpoint && + (!nGetEndpointInfo_KET( at, nVertSingleBond, &eif ) || !eif.cDonor ) ) + return 0; + if ( !at[nVertDoubleBond].endpoint && + (!nGetEndpointInfo_KET( at, nVertDoubleBond, &eif2 ) || !eif2.cAcceptor ) ) + return 0; + /* + if ( eif.cKetoEnolCode + eif2.cKetoEnolCode != 3 ) + return 0; + */ + break; + +#endif + case ALT_PATH_MODE_CHARGE: + /* Find alt path allowing to move (+). Purpose: establish "charge groups", + mark alt. bonds due to (+) charge movement */ + type = BNS_VERT_TYPE_C_POINT; + bChangeFlow = (BNS_EF_CHNG_RSTR | BNS_EF_ALTR_BONDS); + break; + + case ALT_PATH_MODE_4_SALT: + case ALT_PATH_MODE_4_SALT2: + /* Find alt paths allowing to move (-) and H between "acidic oxygen atoms". + Purpose: mark alt bonds due to this "long range" tautomerism. */ + type = BNS_VERT_TYPE_ENDPOINT; + bChangeFlow = (BNS_EF_CHNG_RSTR | BNS_EF_ALTR_BONDS); + if ( !bIsBnsEndpoint( pBNS, nVertSingleBond ) /* !at[nVertSingleBond].endpoint*/ && + (!nGetEndpointInfo( at, nVertSingleBond, &eif ) || !eif.cDonor ) ) + return 0; + if ( !bIsBnsEndpoint( pBNS, nVertDoubleBond ) /* !at[nVertDoubleBond].endpoint*/ && + (!nGetEndpointInfo( at, nVertDoubleBond, &eif ) || !eif.cAcceptor ) ) + return 0; + memset( &apc, 0, sizeof(apc) ); + break; + + case ALT_PATH_MODE_REM2H_CHG: + bChangeFlow |= BNS_EF_ALTR_BONDS; /* fall through */ + case ALT_PATH_MODE_REM2H_TST: + bChangeFlow |= BNS_EF_CHNG_RSTR; + type = BNS_VERT_TYPE_ENDPOINT; + /* allow non-tautomeric donors or any tautomeric atom */ + if ( !bIsBnsEndpoint( pBNS, nVertSingleBond ) /* not linked to a t-group or the edge forbidden */&& + (!nGetEndpointInfo( at, nVertSingleBond, &eif ) || !eif.cDonor ) ) /* not a donor */ + return 0; + if ( !bIsBnsEndpoint( pBNS, nVertDoubleBond ) /* not connected to a t-group */ && + (!nGetEndpointInfo( at, nVertDoubleBond, &eif ) || !eif.cDonor ) ) + return 0; + memset( &apc, 0, sizeof(apc) ); + break; + + case ALT_PATH_MODE_ADD2H_CHG: + bChangeFlow |= BNS_EF_ALTR_BONDS; /* fall through */ + case ALT_PATH_MODE_ADD2H_TST: + bChangeFlow |= BNS_EF_CHNG_RSTR; + type = BNS_VERT_TYPE_ENDPOINT; + /* allow non-tautomeric acceptors or any tautomeric atom */ + if ( !bIsBnsEndpoint( pBNS, nVertSingleBond ) /* !at[nVertSingleBond].endpoint*/ && + (!nGetEndpointInfo( at, nVertSingleBond, &eif ) || !eif.cAcceptor ) ) + return 0; + if ( !bIsBnsEndpoint( pBNS, nVertDoubleBond ) /* !at[nVertSingleBond].endpoint*/ && + (!nGetEndpointInfo( at, nVertDoubleBond, &eif ) || !eif.cAcceptor ) ) + return 0; + break; + + case ALT_PATH_MODE_REM_PROTON: + /* alt path is between the t-group (nVertDoubleBond) and + the (+)-charge group (nVertSingleBond) */ + type = 0; + /*bDoMarkChangedBonds = 0;*/ + bChangeFlow = (BNS_EF_SAVE_ALL | BNS_EF_UPD_H_CHARGE) | BNS_EF_ALTR_NS; /* added BNS_EF_ALTR_NS: set non-stereo altern non-ring bonds 2004-07-02*/ + break; + default: + type = 0; + bChangeFlow = BNS_EF_CHNG_RSTR; + break; + } + + bError = 0; + bSuccess = 0; + nDelta = 0; + + ret = SetRadEndpoints2( pCG, pBNS, pBD, RAD_SRCH_NORM ); + if ( IS_BNS_ERROR( ret ) ) { + return ret; + } + + /* set BNS to check alt path */ + ret = bSetBnsToCheckAltPath( pBNS, nVertDoubleBond, nVertSingleBond, type, path_type, &apc, fcd, &nDots ); + switch( ret ) { + case BNS_CHK_ALTP_NO_ALTPATH: + ret = RemoveRadEndpoints( pBNS, pBD, NULL ); + return ret; + case BNS_CHK_ALTP_SAME_TGROUP: + bSuccess = 1; + goto reinit_BNS; + case BNS_CHK_ALTP_SAME_VERTEX: + ret = RemoveRadEndpoints( pBNS, pBD, NULL ); + return ret? ret : 1; /* very strange ... set a breakpoint here */ + case BNS_CHK_ALTP_SET_SUCCESS: + break; /* actually check the existence of the altpath */ + case BNS_CANT_SET_BOND: + goto reinit_BNS; + default: + ret_val = RemoveRadEndpoints( pBNS, pBD, NULL ); + if ( IS_BNS_ERROR( ret ) ) { + return ret; + } + return BNS_PROGRAM_ERR; + } + + bAdjustRadicals = ( (bChangeFlow & BNS_EF_UPD_RAD_ORI) && !(bChangeFlow & BNS_EF_RSTR_FLOW) ); + + /***************************************************************** + * nDots = 2 for ALT_PATH_CHARGE (checking moveable positive charges) + * Now nDots for ALT_PATH_TAUTOM or ALT_PATH_4_SALT can be greater + * because some of the bonds are effectively removed and dots + * (vertex st-caps) may be added + * -- to make sure there is no (+) charge on a tautomeric endpoint + * -- to fix positions of moveable tautomeric attachements + * (H and (-)-charges) at the ends of an alt path + */ + + /* run BNS */ + + ret = RunBalancedNetworkSearch( pBNS, pBD, bChangeFlow ); + + if ( IS_BNS_ERROR( ret ) ) + { + bError = ret; + } + else if ( ret > 0 ) + { + if ( 2*ret >= nDots ) { + nDelta = 2*ret - nDots; /* non-zero means augmentation created another alt. path -- between radicals */ + if ( pAATG && pAATG->nMarkedAtom ) { + if ( pAATG->nAtTypeTotals && (bChangeFlow & BNS_EF_UPD_H_CHARGE) ) { + memset( pAATG->nMarkedAtom, 0, num_atoms*sizeof(pAATG->nMarkedAtom[0]) ); + /* mark atoms that have charge or H changed, check their input types (that is, before changes), + and subtract their input charge/H from nAtTypeTotals */ + SubtractOrChangeAtHChargeBNS( pBNS, at, num_atoms, pAATG->nAtTypeTotals, pAATG->nMarkedAtom, NULL, 1 ); + /* ZChange charges and/or H, update t_group_info, do not check types or change nAtTypeTotals */ + /* Atom types will be checked and nAtTypeTotals will be changed in + AddChangedAtHChargeBNS() later */ + SubtractOrChangeAtHChargeBNS( pBNS, at, num_atoms, NULL, NULL, pAATG->t_group_info, 0 ); + } else + if ( !pAATG->nAtTypeTotals ){ + bDoMarkChangedBonds = MarkAtomsAtTautGroups( pBNS, num_atoms, pAATG, nVertSingleBond, nVertDoubleBond ); + if ( bDoMarkChangedBonds < 0 ) { + bError = bDoMarkChangedBonds; + bDoMarkChangedBonds = 0; + } + } + } + if ( bDoMarkChangedBonds ) { + /* mark bonds that were changed to configure bond testing */ + ret_val = bSetBondsAfterCheckOneBond( pBNS, fcd, -1, at, num_atoms, bChangeFlow ); + if ( IS_BNS_ERROR( ret_val ) ) { + bError = ret_val; + } + /*ret = SetBondsRestoreBnStructFlow( pBNS, at, num_atoms, bChangeFlow );*/ + /* mark all other changed bonds */ + ret = SetBondsFromBnStructFlow( pBNS, at, num_atoms, bChangeFlow ); + if ( IS_BNS_ERROR( ret ) ) { + bError = ret; + } else + if ( !(ret & 1) && !(ret_val & 1) ) { + bSuccess = 1; + } else + if ( ((ret & 1) || (ret_val & 1)) && + (bChangeFlow & BNS_EF_ALTR_BONDS) || (bChangeFlow & BNS_EF_UPD_H_CHARGE) ) { + /* some bonds have been changed to alternating */ + bSuccess = 3; + } else { + bError = BNS_BOND_ERR; + } + if ( !bError && pAATG && pAATG->nMarkedAtom && (bChangeFlow & BNS_EF_UPD_H_CHARGE) ) { + /* Update radicals to avoid errors in atom type check in AddChangedAtHChargeBNS() */ + if ( bAdjustRadicals ) { + ret_val = RestoreRadicalsOnly( pBNS, pBD, at ); + if ( IS_BNS_ERROR( ret_val ) ) { + bError = ret_val; + } + } + /* Check atom types of marked atoms and add charge/H changes to nAtTypeTotals */ + /* Changing atoms were marked in the 1st call to SubtractOrChangeAtHChargeBNS(..., 1) above */ + AddChangedAtHChargeBNS( at, num_atoms, pAATG->nAtTypeTotals, pAATG->nMarkedAtom ); + if ( bChangeFlow & BNS_EF_CHNG_FLOW ) { + /* eliminate ambiguities in already changed flow: + replace (+)--N==(-) with (+)==N--(-) (both represent neutral N) */ + EliminatePlusMinusChargeAmbiguity( pBNS, num_atoms ); + } + } + } + } + ret = RestoreBnStructFlow( pBNS, bChangeFlow & BNS_EF_CHNG_RSTR); + if ( IS_BNS_ERROR( ret ) ) { + bError = ret; + } + } + +reinit_BNS: + + /* --- reinitialize to repeat the calculations --- */ + bRestoreBnsAfterCheckAltPath( pBNS, &apc, bChangeFlow & BNS_EF_UPD_H_CHARGE ); + bRestoreFlowAfterCheckOneBond( pBNS, fcd ); + ret_val = RemoveRadEndpoints( pBNS, pBD, bAdjustRadicals? at : NULL ); + ReInitBnStructAltPaths( pBNS ); + + return bError? bError : ret_val? ret_val : (bSuccess + 4*nDelta); +} + + +/* + AllocateAndInitBnStruct( ... ) + +*/ +BN_STRUCT* AllocateAndInitBnStruct( inp_ATOM *at, + int num_atoms, + int nMaxAddAtoms, + int nMaxAddEdges, + int max_altp, + int *pNum_changed_bonds ) +{ + BN_STRUCT *pBNS = NULL; + BNS_VERTEX *vert; + + int neigh, num_changed_bonds=0; + U_CHAR bond_type, bond_mark; + + int i, j, k, n_edges, num_bonds, num_edges, f1, f2, edge_cap, edge_flow, st_cap, st_flow, flag_alt_bond; + int tot_st_cap, tot_st_flow; + int max_tg, max_edges, max_vertices, len_alt_path, max_iedges, num_altp; +#if ( BNS_RAD_SEARCH == 1 ) + int num_rad = 0; + + nMaxAddEdges += 1; +#endif +#if ( FIX_NUM_TG == 1 ) + max_tg = inchi_max( num_atoms / 2, 5); +#else + max_tg = num_atoms; +#endif + num_changed_bonds = 0; + + for ( i = 0, num_bonds = 0; i < num_atoms; i ++ ) { + num_bonds += at[i].valence; +#if ( BNS_RAD_SEARCH == 1 ) + num_rad += (at[i].radical == RADICAL_DOUBLET); +#endif + } + /* each atom has enough edges to belong to a tautomeric group + nMaxAddEdges */ + /* number of atoms is large enough to accommodate max. possible number of t-groups + nMaxAddAtoms */ + /* max_altp cannot be larger than BN_MAX_ALTP = 16 */ + num_edges = (num_bonds /= 2); + /* +1 for a super-tautomeric group */ + max_vertices = num_atoms + nMaxAddAtoms + max_tg + 1; + /* +max_tg for edges between t-groups and super-tautomeric group */ + max_edges = num_edges + (nMaxAddEdges + NUM_KINDS_OF_GROUPS)*max_vertices + max_tg; +#if ( BNS_RAD_SEARCH == 1 ) + if ( num_rad ) { + max_vertices *= 2; + max_edges *= 2; + } +#endif + max_iedges = 2*max_edges; + len_alt_path = max_vertices+iALTP_HDR_LEN+1; /* may overflow if an edge is traversed in 2 directions */ + + if ( !( pBNS = (BN_STRUCT *)inchi_calloc( 1, sizeof(BN_STRUCT)) ) || + !( pBNS->edge = (BNS_EDGE *)inchi_calloc( max_edges, sizeof(BNS_EDGE)) ) || + !( pBNS->vert = (BNS_VERTEX *)inchi_calloc( max_vertices,sizeof(BNS_VERTEX)) ) || + !( pBNS->iedge = (BNS_IEDGE *)inchi_calloc( max_iedges, sizeof(BNS_IEDGE)) ) ) { + return DeAllocateBnStruct( pBNS ); + } + /* alt path init */ + for ( num_altp = 0; num_altp < max_altp && num_altp < BN_MAX_ALTP; num_altp ++ ) { + if ( !( pBNS->altp[num_altp] = (BNS_ALT_PATH*)inchi_calloc( len_alt_path,sizeof(BNS_ALT_PATH))) ) { + return DeAllocateBnStruct( pBNS ); + } + ALTP_ALLOCATED_LEN(pBNS->altp[num_altp]) = len_alt_path; + pBNS->len_alt_path = len_alt_path; /* ??? duplication ??? */ + /* re-init */ + ALTP_DELTA(pBNS->altp[num_altp]) = 0; + ALTP_START_ATOM(pBNS->altp[num_altp]) = NO_VERTEX; + ALTP_END_ATOM(pBNS->altp[num_altp]) = NO_VERTEX; + ALTP_PATH_LEN(pBNS->altp[num_altp]) = 0; + } + pBNS->alt_path = NULL; + pBNS->num_altp = 0; + pBNS->max_altp = num_altp; + + + /* fill vertices (no connectivity) */ + pBNS->vert[0].iedge = pBNS->iedge; + for ( i = 0; i < num_atoms; i ++ ) { + k = pBNS->vert[i].max_adj_edges = at[i].valence + (nMaxAddEdges + NUM_KINDS_OF_GROUPS); + pBNS->vert[i+1].iedge = pBNS->vert[i].iedge + k; + } + pBNS->num_atoms = num_atoms; /* number of real atoms */ + pBNS->num_added_atoms = 0; + pBNS->num_t_groups = 0; /* number of added t-groups */ + pBNS->num_c_groups = 0; + pBNS->nMaxAddAtoms = nMaxAddAtoms; + pBNS->nMaxAddEdges = nMaxAddEdges; + + pBNS->num_vertices = num_atoms; /* current number of vertices, a sum of + pBNS->num_atoms + pBNS->num_t_groups + pBNS->num_added_atoms + */ + pBNS->max_vertices = max_vertices; + + + pBNS->num_bonds = num_bonds; /* number of real edges (bonds) */ + pBNS->max_edges = max_edges; + pBNS->max_iedges = max_iedges; + + /* + To remove t-groups and added atoms: + In atoms i = 0..pBNS->num_atoms-1 + pBNS->vert[i].num_adj_edges = pBNS->vert[i].max_adj_edges - pBNS->nMaxAddEdges - NUM_KINDS_OF_GROUPS; + pBNS->num_vertices = pBNS->num_atoms; + pBNS->num_edges = pBNS->num_bonds; + pBNS->num_added_atoms = 0; + pBNS->num_t_groups = 0; + pBNS->num_added_edges = 0; + + ALTP_DELTA(pBNS->alt_path) = 0; + ALTP_START_ATOM(pBNS->alt_path) = NO_VERTEX; + ALTP_END_ATOM(pBNS->alt_path) = NO_VERTEX; + ALTP_PATH_LEN(pBNS->alt_path) = 0; + + */ + + + /* fill edges and connectivity */ + tot_st_cap = tot_st_flow = 0; + for ( i = 0, n_edges = 0; i < num_atoms; i ++ ) { + vert = &pBNS->vert[i]; + st_cap = 0; + st_flow = 0; + flag_alt_bond = 0; + for ( j = 0; j < at[i].valence; j ++ ) { + neigh = at[i].neighbor[j]; + /* find this bond at the neighbor */ + for ( k = 0; k < at[neigh].valence; k ++ ) { + if ( at[neigh].neighbor[k] == i ) { + break; + } + } + bond_type = (at[i].bond_type[j] & BOND_TYPE_MASK); + bond_mark = (at[i].bond_type[j] & ~BOND_TYPE_MASK); + if ( bond_type != BOND_SINGLE && bond_type != BOND_DOUBLE && + bond_type != BOND_TRIPLE /*&& bond_type != BOND_ALTERN*/ ) { + /* make Unknown or Alternating bonds single */ + bond_type = 1; + at[i].bond_type[j] = bond_mark | bond_type; + num_changed_bonds ++; + } + if ( neigh > i ) { + /* this is the first time we encounter this bond */ + f1 = MAX_AT_FLOW(at[i]); + f2 = MAX_AT_FLOW(at[neigh]); + edge_flow = bond_type-1; + if ( edge_flow > MAX_BOND_EDGE_CAP ) { + flag_alt_bond ++; + edge_flow = 0; /* BNS will determine flows (that is, bonds) */ + edge_cap = AROM_BOND_EDGE_CAP; + } else { +#if ( 0 && KETO_ENOL_TAUT == 1 ) /* ????? */ + edge_cap = inchi_max(f1, f2); +#else + edge_cap = inchi_min(f1, f2); +#endif + edge_cap = inchi_min(edge_cap, MAX_BOND_EDGE_CAP); /* max capacity = 2 means up to triple bond */ + } + pBNS->edge[n_edges].neighbor1 = (AT_NUMB)i; + pBNS->edge[n_edges].neighbor12 = (AT_NUMB)(i ^ neigh); + pBNS->edge[n_edges].flow = + pBNS->edge[n_edges].flow0 = edge_flow; + pBNS->edge[n_edges].cap = + pBNS->edge[n_edges].cap0 = edge_cap; + pBNS->edge[n_edges].neigh_ord[0] = j; + pBNS->edge[n_edges].neigh_ord[1] = k; + pBNS->edge[n_edges].pass = 0; + pBNS->edge[n_edges].forbidden = 0; + + vert->iedge[j] = pBNS->vert[neigh].iedge[k] = n_edges ++; + } else { + /* this is the second time we encounter this bond. It was stored at */ + int iedge = pBNS->vert[neigh].iedge[k]; + edge_cap = pBNS->edge[iedge].cap; + edge_flow = pBNS->edge[iedge].flow; + } + st_flow += edge_flow; + st_cap += edge_cap; + } + vert->num_adj_edges = j; + vert->st_edge.cap = + vert->st_edge.cap0 = MAX_AT_FLOW(at[i]); + vert->st_edge.flow = + vert->st_edge.flow0 = st_flow; + vert->type = BNS_VERT_TYPE_ATOM; + tot_st_cap += vert->st_edge.cap; + tot_st_flow += vert->st_edge.flow; + } + *pNum_changed_bonds = num_changed_bonds/2; + + pBNS->num_edges = n_edges; /* number of edges */ + pBNS->num_added_edges = 0; + + pBNS->tot_st_cap = tot_st_cap; + pBNS->tot_st_flow = tot_st_flow; + + return pBNS; +} + + +BN_STRUCT* DeAllocateBnStruct( BN_STRUCT *pBNS ) +{ + int i; + if ( pBNS ) { + if ( pBNS->edge ) { + inchi_free( pBNS->edge ); + } + for ( i = 0; i < pBNS->max_altp && i < BN_MAX_ALTP ; i ++ ) { + if ( pBNS->altp[i] ) { + inchi_free( pBNS->altp[i] ); + } + } + if ( pBNS->vert ) { + if ( pBNS->vert[0].iedge ) { + inchi_free( pBNS->vert[0].iedge ); + } + inchi_free( pBNS->vert ); + } + inchi_free( pBNS ); + } + return NULL; +} + + +int ReInitBnStructAltPaths( BN_STRUCT *pBNS ) +{ + int i; + for ( i = 0; i < pBNS->max_altp && i < BN_MAX_ALTP; i ++ ) { + if ( pBNS->altp[i] ) { + ALTP_DELTA(pBNS->altp[i]) = 0; + ALTP_PATH_LEN(pBNS->altp[i]) = 0; + ALTP_START_ATOM(pBNS->altp[i]) = NO_VERTEX; + ALTP_END_ATOM(pBNS->altp[i]) = NO_VERTEX; + } + } + pBNS->alt_path = NULL; + pBNS->num_altp = 0; + return i; +} + + +int ReInitBnStructAddGroups( CANON_GLOBALS *pCG, + BN_STRUCT *pBNS, + inp_ATOM *at, + int num_atoms, + T_GROUP_INFO *tgi, + C_GROUP_INFO *cgi ) +{ + int ret; + /* strip all t-groups and c-groups */ + ret = ReInitBnStruct( pBNS, at, num_atoms, 0 ); + if ( ret ) + { + ret = BNS_REINIT_ERR; + goto exit_function; + } +/*#if ( MOVE_CHARGES == 1 )*/ + if ( *pBNS->pbTautFlags & TG_FLAG_MOVE_POS_CHARGES ) + { + /* add c-groups */ + ret = AddCGroups2BnStruct( pCG, pBNS, at, num_atoms, cgi ); + if ( IS_BNS_ERROR( ret ) ) + { + goto exit_function; + } + } +/*#endif*/ + /* add t-groups */ + ret = AddTGroups2BnStruct( pCG, pBNS, at, num_atoms, tgi ); + if ( IS_BNS_ERROR( ret ) ) + { + goto exit_function; + } +exit_function: + return ret; +} + + +int ReInitBnStruct( BN_STRUCT *pBNS, + inp_ATOM *at, + int num_at, + int bRemoveGroupsFromAtoms ) +{ + int i, vfict, kfict, iedgefict, endpoint, centerpoint, iedge, k; + int ret = 0; + if ( pBNS ) { + if ( pBNS->vert && pBNS->edge ) { + /* debug */ + for ( k = 0, i = 0; k < pBNS->num_edges; k ++ ) { + if ( pBNS->edge[k].pass ) { + i ++; + } + } + ret += i * 100; + /* restore flow and cap on edges to vertices connected to fictitious atoms */ + for ( vfict = pBNS->num_atoms; vfict < pBNS->num_vertices; vfict ++ ) { + for ( kfict = 0; kfict < pBNS->vert[vfict].num_adj_edges; kfict ++ ) { + iedgefict = pBNS->vert[vfict].iedge[kfict]; /* fictitious edge to the endpoint */ + endpoint = pBNS->edge[iedgefict].neighbor12 ^ vfict; /* the endpoint */ + /* to simlify restore cap and flow in ALL edges to the endpoint */ + if ( bRemoveGroupsFromAtoms && endpoint < num_at ) { + at[endpoint].c_point = 0; + at[endpoint].endpoint = 0; + } + for ( k = 0; k < pBNS->vert[endpoint].num_adj_edges; k ++ ) { + iedge = pBNS->vert[endpoint].iedge[k]; /* edge to endpoint */ + centerpoint = pBNS->edge[iedge].neighbor12 ^ endpoint; + pBNS->edge[iedge].cap = pBNS->edge[iedge].cap0; + pBNS->edge[iedge].flow = pBNS->edge[iedge].flow0; + pBNS->edge[iedge].pass = 0; +#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) + pBNS->edge[iedge].forbidden &= pBNS->edge_forbidden_mask; +#endif + pBNS->vert[centerpoint].st_edge.cap = pBNS->vert[centerpoint].st_edge.cap0; + pBNS->vert[centerpoint].st_edge.flow = pBNS->vert[centerpoint].st_edge.flow0; + } + pBNS->vert[endpoint].st_edge.cap = pBNS->vert[endpoint].st_edge.cap0; + pBNS->vert[endpoint].st_edge.flow = pBNS->vert[endpoint].st_edge.flow0; + pBNS->vert[endpoint].type &= BNS_VERT_TYPE_ATOM; + } + } + /* reset number of neighbors */ + if ( pBNS->num_edges > pBNS->num_bonds ) { + for ( i = 0; i < pBNS->num_atoms; i ++ ) { + pBNS->vert[i].num_adj_edges = + pBNS->vert[i].max_adj_edges - pBNS->nMaxAddEdges - NUM_KINDS_OF_GROUPS; + } + } + } else { + ret += 2; + } + if ( !pBNS->edge ) { + ret += 4; + } + if ( !pBNS->iedge ) { + ret += 8; + } + + ReInitBnStructAltPaths( pBNS ); + + pBNS->num_vertices = pBNS->num_atoms; + pBNS->num_edges = pBNS->num_bonds; + pBNS->num_added_atoms = 0; + pBNS->num_t_groups = 0; + pBNS->num_c_groups = 0; + pBNS->num_added_edges = 0; + } else { + ret += 1; + } + + return ret; +} + + +int CompTGroupNumber( const void *tg1, const void *tg2, void *p ) +{ + return (int)((const T_GROUP *)tg1)->nGroupNumber - (int)((const T_GROUP *)tg2)->nGroupNumber; +} + + +int CompCGroupNumber( const void *cg1, const void *cg2, void *p ) +{ + return (int)((const C_GROUP *)cg1)->nGroupNumber - (int)((const C_GROUP *)cg2)->nGroupNumber; +} + + +int AddTGroups2BnStruct( CANON_GLOBALS *pCG, + BN_STRUCT *pBNS, + inp_ATOM *at, + int num_atoms, + T_GROUP_INFO *tgi ) +{ + int ret = 0; + /* ret = ReInitBnStruct( pBNS ); */ + if ( tgi && tgi->num_t_groups && tgi->t_group ) { + int i, k, endpoint, centerpoint, fictpoint; + int num_tg = tgi->num_t_groups; + int num_edges = pBNS->num_edges; + int num_vertices = pBNS->num_vertices; + BNS_VERTEX *vert_ficpoint, *ver_ficpont_prev; /* fictitious vertex describing t-group */ + BNS_VERTEX *vert_endpoint; + BNS_EDGE *edge; /* edge between that vertex and the tautomeric endpoint */ + int nMaxTGroupNumber = 0; + ENDPOINT_INFO eif; + + /* Debug: check overflow */ + if ( num_vertices + num_tg >= pBNS->max_vertices ) { + return BNS_VERT_EDGE_OVFL; + } + /* find the largest t-group ID */ + for ( i = 0; i < num_tg; i ++ ) { + if ( tgi->t_group[i].nGroupNumber > nMaxTGroupNumber ) { + nMaxTGroupNumber = tgi->t_group[i].nGroupNumber; + } + } + /* since t-group IDs may be not contiguous, clear all vertices that will be added. + all-zeroes-vertex will be ignored by the BNS + */ + memset( pBNS->vert+num_vertices, 0, nMaxTGroupNumber*sizeof(pBNS->vert[0]) ); + /* Make sure the last t-group has the largest t-group ID: + this is necessary to correctly add new edges and vertices for testing augmenting paths + */ +#if ( bRELEASE_VERSION != 1 ) + insertions_sort(pCG, tgi->t_group, num_tg, sizeof(tgi->t_group[0]), CompTGroupNumber ); + for ( i = 1; i < num_tg; i ++ ) { + if ( 1 != tgi->t_group[i].nGroupNumber - tgi->t_group[i-1].nGroupNumber ) + { + return BNS_BOND_ERR; + } + } +#else + if ( nMaxTGroupNumber != tgi->t_group[num_tg-1].nGroupNumber ) + { + insertions_sort( pCG, tgi->t_group, num_tg, sizeof(tgi->t_group[0]), CompTGroupNumber ); + } +#endif + /* initialize new fictitious vertices */ + ver_ficpont_prev = pBNS->vert+num_vertices - 1; + + for ( i = 0; i < num_tg; i ++, ver_ficpont_prev = vert_ficpoint ) { + /* + vert_ficpoint-1 is the last vertex; + vert_ficpoint is the vertex that is being added + Note: nGroupNumber are not contiguous + */ + vert_ficpoint = pBNS->vert+num_vertices + tgi->t_group[i].nGroupNumber - 1; + vert_ficpoint->iedge = ver_ficpont_prev->iedge + ver_ficpont_prev->max_adj_edges; + vert_ficpoint->max_adj_edges = tgi->t_group[i].nNumEndpoints+BNS_ADD_EDGES+BNS_ADD_SUPER_TGROUP; + vert_ficpoint->num_adj_edges = 0; + vert_ficpoint->st_edge.flow = vert_ficpoint->st_edge.flow0 = 0; + vert_ficpoint->st_edge.cap = vert_ficpoint->st_edge.cap0 = 0; + vert_ficpoint->type = BNS_VERT_TYPE_TGROUP; + } + + for ( endpoint = 0; endpoint < num_atoms; endpoint ++ ) { + if ( !at[endpoint].endpoint ) + continue; + fictpoint = at[endpoint].endpoint + num_vertices - 1; + vert_ficpoint = pBNS->vert + fictpoint; + vert_endpoint = pBNS->vert + endpoint; + /* Debug: check overflow */ + if ( fictpoint >= pBNS->max_vertices || + num_edges >= pBNS->max_edges || + vert_ficpoint->num_adj_edges >= vert_ficpoint->max_adj_edges || + vert_endpoint->num_adj_edges >= vert_endpoint->max_adj_edges ) { + ret = BNS_VERT_EDGE_OVFL; + break; + } + /* obtain donor/acceptor info */ + if ( !nGetEndpointInfo( at, endpoint, &eif ) ) { +#if ( KETO_ENOL_TAUT == 1 ) + if ( !((tgi->bTautFlags & TG_FLAG_KETO_ENOL_TAUT ) && + nGetEndpointInfo_KET( at, endpoint, &eif )) ) +#endif + { + ret = BNS_BOND_ERR; + break; + } + } + + vert_endpoint->type |= BNS_VERT_TYPE_ENDPOINT; + + /* set capacity = 1 to the edges from the endpoint to the centerpoint(s) */ + for ( k = 0; k < vert_endpoint->num_adj_edges; k ++ ) { + int iedge = vert_endpoint->iedge[k]; + if ( !pBNS->edge[iedge].cap ) { + /* single bond, possibly between endpoint and centerpoint */ + centerpoint = (pBNS->edge[iedge].neighbor12 ^ endpoint); + if ( centerpoint < pBNS->num_atoms && + pBNS->vert[centerpoint].st_edge.cap >= 1 ) { + int bond_type = (at[endpoint].bond_type[k] & BOND_TYPE_MASK); + if (bond_type == BOND_TAUTOM || + bond_type == BOND_ALTERN || + bond_type == BOND_ALT12NS || + bond_type == BOND_SINGLE ) { + pBNS->edge[iedge].cap = 1; + } + } + } + } + /* create a new edge connecting endpoint to the new fictitious t-group vertex vert_ficpoint */ + edge = pBNS->edge + num_edges; + edge->cap = 1; + edge->flow = 0; + edge->pass = 0; +#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) + edge->forbidden &= pBNS->edge_forbidden_mask; +#endif + /* later include case when the charge change allows the endpoint to become tautomeric */ + /* mark endoint having moveable H atom with flow=1 */ + + /* -- old "no charges" version -- */ + /* if (at[endpoint].chem_bonds_valence == at[endpoint].valence) */ + /* -- the following line takes charges into account -- */ + if ( eif.cDonor ) /* means the endpoint has an H-atom to donate */ + { + /* increment edge flow */ + edge->flow ++; + /* increment one vertex st-flow & cap */ + vert_ficpoint->st_edge.flow ++; + vert_ficpoint->st_edge.cap ++; + /* increment another vertex st-flow & cap */ + vert_endpoint->st_edge.flow ++; + vert_endpoint->st_edge.cap ++; + } + /* connect edge to endpoint and fictpoint and increment the counters of neighbors and edges */ + edge->neighbor1 = endpoint; /* the smallest out of v1=endopoint and v2=num_vertices */ + edge->neighbor12 = endpoint ^ fictpoint; /* v1 ^ v2 */ + vert_endpoint->iedge[vert_endpoint->num_adj_edges] = num_edges; + vert_ficpoint->iedge[vert_ficpoint->num_adj_edges] = num_edges ++; + edge->neigh_ord[0] = vert_endpoint->num_adj_edges ++; + edge->neigh_ord[1] = vert_ficpoint->num_adj_edges ++; + edge->cap0 = edge->cap; + edge->flow0 = edge->flow; + } + + pBNS->num_edges = num_edges; + pBNS->num_vertices += nMaxTGroupNumber; + pBNS->num_t_groups = num_tg; + } + return ret; +} + + +/*#if ( MOVE_CHARGES == 1 )*/ /* { */ + + +int AddCGroups2BnStruct( CANON_GLOBALS *pCG, + BN_STRUCT *pBNS, + inp_ATOM *at, + int num_atoms, + C_GROUP_INFO *cgi ) +{ + int ret = 0; + /* ret = ReInitBnStruct( pBNS ); */ + if ( cgi && cgi->num_c_groups && cgi->c_group ) { + int i, k, c_point, centerpoint, fictpoint; + int num_cg = cgi->num_c_groups; + int num_edges = pBNS->num_edges; + int num_vertices = pBNS->num_vertices; + BNS_VERTEX *vert_ficpoint, *ver_ficpont_prev; /* fictitious vertex describing charge c-group */ + BNS_VERTEX *vertex_cpoint; + BNS_EDGE *edge; /* edge between that vertex and the tautomeric c_point */ + int nMaxCGroupNumber = 0; + + /* Debug: check overflow */ + if ( num_vertices + num_cg >= pBNS->max_vertices ) { + return BNS_VERT_EDGE_OVFL; + } + /* find the largest t-group ID */ + for ( i = 0; i < num_cg; i ++ ) { + if ( cgi->c_group[i].nGroupNumber > nMaxCGroupNumber ) { + nMaxCGroupNumber = cgi->c_group[i].nGroupNumber; + } + } + /* since t-group IDs may be not contiguous, clear all vertices that will be added. + all-zeroes-vertex will be ignored by the BNS + */ + memset( pBNS->vert+num_vertices, 0, nMaxCGroupNumber*sizeof(pBNS->vert[0]) ); + /* Make sure the last t-group has the largest t-group ID: + this is necessary to correctly add new edges and vertices for testing augmenting paths + */ +#if ( bRELEASE_VERSION != 1 ) + insertions_sort( cgi->c_group, num_cg, sizeof(cgi->c_group[0]), CompCGroupNumber ); + for ( i = 1; i < num_cg; i ++ ) { + if ( 1 != cgi->c_group[i].nGroupNumber - cgi->c_group[i-1].nGroupNumber ) { + return BNS_BOND_ERR; + } + } +#else + if ( nMaxCGroupNumber != cgi->c_group[num_cg-1].nGroupNumber ) + { + insertions_sort( pCG, + cgi->c_group, num_cg, + sizeof(cgi->c_group[0]), + CompCGroupNumber ); + } +#endif + /**************************************/ + /* initialize new fictitious vertices */ + /* representing c-point groups */ + /**************************************/ + ver_ficpont_prev = pBNS->vert+num_vertices - 1; + + for ( i = 0; i < num_cg; i ++, ver_ficpont_prev = vert_ficpoint ) { + /* + vert_ficpoint-1 is the last vertex; + vert_ficpoint is the being added vertex + Note: nGroupNumber are not contiguous + */ + vert_ficpoint = pBNS->vert+num_vertices + cgi->c_group[i].nGroupNumber - 1; + vert_ficpoint->iedge = ver_ficpont_prev->iedge + ver_ficpont_prev->max_adj_edges; + vert_ficpoint->max_adj_edges = cgi->c_group[i].num_CPoints+BNS_ADD_EDGES; + vert_ficpoint->num_adj_edges = 0; + vert_ficpoint->st_edge.flow = vert_ficpoint->st_edge.flow0 = 0; + vert_ficpoint->st_edge.cap = vert_ficpoint->st_edge.cap0 = 0; + vert_ficpoint->type = BNS_VERT_TYPE_C_GROUP; + } + + /************************************************/ + /* connect c-points to the fictitious vertices */ + /* representing c-point groups; set caps, flows */ + /************************************************/ + for ( c_point = 0; c_point < num_atoms; c_point ++ ) { + if ( !at[c_point].c_point ) + continue; + fictpoint = at[c_point].c_point + num_vertices - 1; /* c-group vertex index */ + vert_ficpoint = pBNS->vert + fictpoint; /* c-group vertex */ + vertex_cpoint = pBNS->vert + c_point; /* c_point vertex */ + /* Debug: check overflow */ + if ( fictpoint >= pBNS->max_vertices || + num_edges >= pBNS->max_edges || + vert_ficpoint->num_adj_edges >= vert_ficpoint->max_adj_edges || + vertex_cpoint->num_adj_edges >= vertex_cpoint->max_adj_edges ) { + ret = BNS_VERT_EDGE_OVFL; + break; + } + vertex_cpoint->type |= BNS_VERT_TYPE_C_POINT; +#if ( FIX_CPOINT_BOND_CAP != 1 ) /* { */ + /* set capacity = 1 to the edges from the c_point to the centerpoint(s) */ + /* if their current capacity is zero */ + /* the centerpoint is any adjacent atom that is adjacent to a multiple bond */ + for ( k = 0; k < vertex_cpoint->num_adj_edges; k ++ ) { + int iedge = vertex_cpoint->iedge[k]; + if ( !pBNS->edge[iedge].cap ) { + /* single bond, possibly between c_point and centerpoint */ + centerpoint = (pBNS->edge[iedge].neighbor12 ^ c_point); + if ( centerpoint < pBNS->num_atoms && + pBNS->vert[centerpoint].st_edge.cap >= 1 ) { + int bond_type = (at[c_point].bond_type[k] & BOND_TYPE_MASK); + if ( bond_type == BOND_TAUTOM || + bond_type == BOND_ALTERN || + bond_type == BOND_SINGLE ) { + pBNS->edge[iedge].cap = 1; + } + } + } + } +#endif /* } FIX_CPOINT_BOND_CAP */ + /* create a new edge connecting c_point to the new fictitious c-group vertex vert_ficpoint */ + edge = pBNS->edge + num_edges; + edge->cap = 1; + edge->flow = 0; + edge->pass = 0; +#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) + edge->forbidden &= pBNS->edge_forbidden_mask; +#endif + /* mark edge to c-point having NO moveable charge with flow=1 */ + if ( !CHARGED_CPOINT(at,c_point) ) { + /* increment edge flow */ + edge->flow ++; + /* increment c-group vertex st-flow & cap */ + vert_ficpoint->st_edge.flow ++; + vert_ficpoint->st_edge.cap ++; + /* increment c-point vertex st-flow & cap */ + vertex_cpoint->st_edge.flow ++; + vertex_cpoint->st_edge.cap ++; + } +#if ( FIX_CPOINT_BOND_CAP == 1 ) /* { */ + /* set capacity = 1 to the edges from the c_point to the centerpoint(s) */ + /* if their current capacity is zero */ + /* the centerpoint is any adjacent atom that is adjacent to a multiple bond */ + for ( k = 0; k < vertex_cpoint->num_adj_edges; k ++ ) { + int iedge = vertex_cpoint->iedge[k]; + VertexFlow nNewCap = vertex_cpoint->st_edge.cap; + centerpoint = (pBNS->edge[iedge].neighbor12 ^ c_point); + if ( !pBNS->edge[iedge].cap ) { + /* single bond, possibly between c_point and centerpoint */ + if ( centerpoint < pBNS->num_atoms && + pBNS->vert[centerpoint].st_edge.cap >= 1 ) { + nNewCap = inchi_min( pBNS->vert[centerpoint].st_edge.cap, nNewCap ); + nNewCap = inchi_min( nNewCap, MAX_BOND_EDGE_CAP ); + pBNS->edge[iedge].cap = nNewCap; + } +#if ( FIX_CPOINT_BOND_CAP2 == 1 ) /* multiple bond */ + else + if ( centerpoint < pBNS->num_atoms && + edge->flow && pBNS->edge[iedge].cap < MAX_BOND_EDGE_CAP ) { + pBNS->edge[iedge].cap ++; + } +#endif + } + } +#endif /* } FIX_CPOINT_BOND_CAP */ + /* connect edge to c_point and fictpoint and increment the counters of neighbors and edges */ + edge->neighbor1 = c_point; /* the smallest out of v1=endopoint and v2=num_vertices */ + edge->neighbor12 = c_point ^ fictpoint; /* v1 ^ v2 */ + vertex_cpoint->iedge[vertex_cpoint->num_adj_edges] = num_edges; + vert_ficpoint->iedge[vert_ficpoint->num_adj_edges] = num_edges ++; + edge->neigh_ord[0] = vertex_cpoint->num_adj_edges ++; + edge->neigh_ord[1] = vert_ficpoint->num_adj_edges ++; + edge->cap0 = edge->cap; + edge->flow0 = edge->flow; + } + + pBNS->num_edges = num_edges; + pBNS->num_vertices += nMaxCGroupNumber; + pBNS->num_c_groups = num_cg; + } + return ret; +} +/*#endif*/ /* } MOVE_CHARGES == 1 */ + + + +void ClearAllBnDataVertices( Vertex *v, Vertex value, int size ) +{ + int i; + for ( i = 0; i < size; i ++ ) { + v[i] = value; + } +} + + +void ClearAllBnDataEdges( Edge *e, Vertex value, int size ) +{ + int i; + for ( i = 0; i < size; i ++ ) { + e[i][0] = value; + } +} + + +BN_DATA *DeAllocateBnData( BN_DATA *pBD ) +{ + if ( pBD ) { + if ( pBD->BasePtr ) + inchi_free( pBD->BasePtr ); + if ( pBD->SwitchEdge ) + inchi_free( pBD->SwitchEdge ); + if ( pBD->Tree ) + inchi_free( pBD->Tree ); + if ( pBD->ScanQ ) + inchi_free( pBD->ScanQ ); + if ( pBD->Pu ) + inchi_free( pBD->Pu ); + if ( pBD->Pv ) + inchi_free( pBD->Pv ); +#if ( BNS_RAD_SEARCH == 1 ) + if ( pBD->RadEndpoints ) { + inchi_free( pBD->RadEndpoints ); + } + if ( pBD->RadEdges ) { + inchi_free( pBD->RadEdges ); + } +#endif + inchi_free( pBD ); + } + return NULL; +} + + +BN_DATA *AllocateAndInitBnData( int max_num_vertices ) +{ + BN_DATA *pBD = NULL; + int max_len_Pu_Pv; + max_num_vertices = 2*max_num_vertices+2; + max_len_Pu_Pv = max_num_vertices/2+1; + max_len_Pu_Pv += max_len_Pu_Pv % 2; /* even length */ + if ( !(pBD = (BN_DATA *) inchi_calloc( 1, sizeof(BN_DATA) ) ) || + !(pBD->BasePtr = (Vertex *) inchi_calloc( max_num_vertices, sizeof(Vertex) ) ) || + !(pBD->SwitchEdge = (Edge *) inchi_calloc( max_num_vertices, sizeof(Edge ) ) ) || + !(pBD->Tree = (S_CHAR *) inchi_calloc( max_num_vertices, sizeof(S_CHAR) ) ) || + !(pBD->ScanQ = (Vertex *) inchi_calloc( max_num_vertices, sizeof(Vertex) ) ) || + !(pBD->Pu = (Vertex *) inchi_calloc( max_len_Pu_Pv, sizeof(Vertex) ) ) || +#if ( BNS_RAD_SEARCH == 1 ) + !(pBD->RadEndpoints = (Vertex *) inchi_calloc( max_len_Pu_Pv, sizeof(Vertex) ) ) || + !(pBD->RadEdges = (EdgeIndex*) inchi_calloc( max_len_Pu_Pv, sizeof(EdgeIndex) ) ) || +#endif + !(pBD->Pv = (Vertex *) inchi_calloc( max_len_Pu_Pv, sizeof(Vertex) ) ) + ) { + pBD = DeAllocateBnData( pBD ); + } else { + /* Initialize data */ + ClearAllBnDataEdges(pBD->SwitchEdge, NO_VERTEX, max_num_vertices); + ClearAllBnDataVertices(pBD->BasePtr, NO_VERTEX, max_num_vertices); + memset(pBD->Tree, TREE_NOT_IN_M, max_num_vertices); + pBD->QSize = -1; + pBD->max_len_Pu_Pv = max_len_Pu_Pv; + pBD->max_num_vertices = max_num_vertices; +#if ( BNS_RAD_SEARCH == 1 ) + pBD->nNumRadEndpoints = 0; +#endif + } + return pBD; +} + + +int ReInitBnData( BN_DATA *pBD ) +{ + int i, ret = 0; + Vertex u, v; + if ( pBD ) { + if ( !pBD->ScanQ ) { + ret += 2; + } + if ( !pBD->BasePtr ) { + ret += 4; + } + if ( !pBD->SwitchEdge ) { + ret += 8; + } + if ( !pBD->Tree ) { + ret += 16; + } + if ( !ret ) { + for ( i = 0; i <= pBD->QSize; i ++ ) { + u = pBD->ScanQ[i]; + v = prim(u); + pBD->BasePtr[u] = + pBD->BasePtr[v] = + pBD->SwitchEdge_Vert1(u) = + pBD->SwitchEdge_Vert1(v) = NO_VERTEX; + pBD->Tree[u] = + pBD->Tree[v] = TREE_NOT_IN_M; + } + } + pBD->QSize = -1; + if ( !pBD->Pu ) { + ret += 32; + } + if ( !pBD->Pv ) { + ret += 64; + } + } else { + ret += 1; + } + + return ret; +} + + +int GetVertexDegree( BN_STRUCT* pBNS, Vertex v ) +{ + int i = v / 2 - 1; + if ( i >= 0 ) { + if ( pBNS->vert[i].st_edge.cap > 0 ) { + return pBNS->vert[i].num_adj_edges+1; /* add 1 neighbor for s or t */ + } else { + return 0; /* since the edge s-v has zero capacity, we ignore vertex v */ + } + } else { + return pBNS->num_vertices; + } +} + + +Vertex Get2ndEdgeVertex( BN_STRUCT* pBNS, Edge uv ) +{ + /* + Vertex ret; + */ + if ( uv[1] >= 0 ) { + /* -- debug -- + if ( uv[1] > pBNS->num_edges || uv[0] > 2*pBNS->num_vertices+3 ) { + int stop = 1; + } + ret = ((uv[0]-2) ^ (2*pBNS->edge[uv[1]].neighbor12+1)) + 2; + if ( ret > 2*pBNS->num_vertices+3 ) { + int stop = 1; + } + return ret; + -- end debug -- */ + return ((uv[0]-2) ^ (2*pBNS->edge[uv[1]].neighbor12+1)) + 2; + /*short u = uv[0]-FIRST_INDX; */ + /*short t = 2*(((u / 2 - 1) ^ pBNS->edge[uv[1]].neighbor12) + 1) + ((u+1) & 1) + FIRST_INDX; */ + /*return t; */ + } + if ( uv[0] <= 1 ) + return -(1 + uv[1]); /* vertex1 is s or t, return x or y */ + else + return uv[0] % 2; /* vertex1 is x or y, return s or t; never happens? -- NSC 3737, 7634,... */ +} + + +Vertex GetVertexNeighbor( BN_STRUCT* pBNS, + Vertex v, + int neigh, + EdgeIndex *iedge ) +{ + /* neigh = 0 => the neighbor is s or t except case when v is s or t. */ + /* v= FIRST_INDX or FIRST_INDX+1: v is s or t respectively */ + int i, neighbor; + if ( (i = v - 2) >= 0 ) { + /* neighbor of x or y */ + if ( neigh ) { + neigh --; + /* x or y */ + *iedge = pBNS->vert[i/2].iedge[neigh]; + if ( !(pBNS->edge[*iedge].cap & EDGE_FLOW_MASK) || IS_FORBIDDEN(pBNS->edge[*iedge].forbidden, pBNS) ) { + return NO_VERTEX; + } + neighbor = (i ^ (2 * pBNS->edge[*iedge].neighbor12 + 1)) + 2; /* parity opposite to v parity */ + } else { + /* neighbor of x or y is s or t */ + neighbor = (v & 1); /* s or t, same parity as v */ + *iedge = -( neighbor + 1 ); + } + } else { + /* neighbor of s or t: x or y, same parity as v */ + if ( !(pBNS->vert[neigh].st_edge.cap & EDGE_FLOW_ST_MASK) ) { + return NO_VERTEX; + } + neighbor = 2*neigh + 2 + (v & 1); /* parity same as the parity of v */ + *iedge = -( neighbor + 1 ); + } + return neighbor; +} + + +int GetEdgePointer( BN_STRUCT* pBNS, + Vertex u, + Vertex v, + EdgeIndex iuv, + BNS_EDGE **uv, + S_CHAR *s_or_t ) +{ + int i = u / 2 - 1; + int j = v / 2 - 1; + int bBackward = BNS_WRONG_PARMS; + *uv = NULL; + if ( i >= 0 ) { + /* u is an atom */ + if ( j >= 0 ) { + /* v is an atom */ + if ( (u+v)%2 ) { + *uv = pBNS->edge+iuv; + bBackward = ( u & 1 ); + *s_or_t = 0; + } + } else + /* v is s or t */ + if ( v >= 0 && !((u+v)%2) ) { + *uv = (BNS_EDGE*)&pBNS->vert[i].st_edge; + bBackward = !(v & 1); + *s_or_t = v+3; /* 3=> v=s, 4=> v=t */ + } + } else + if ( j >= 0 ) { + /* u is s or t */ + if ( u >= 0 && !((u+v)%2) ) { + /* v is an atom */ + *uv = (BNS_EDGE*)&pBNS->vert[j].st_edge; + bBackward = (u & 1 ); + *s_or_t = u+1; /* 1=> u=s, 2=> u=t */ + } + } + return bBackward; +} + + +int AugmentEdge( BN_STRUCT* pBNS, + Vertex u, + Vertex v, + EdgeIndex iuv, + int delta, + S_CHAR bReverse, + int bChangeFlow ) +{ + int f, flow, ret=0; + + BNS_ST_EDGE *pst_edge; + BNS_EDGE *pedge; + S_CHAR s_or_t; + int bBackward = GetEdgePointer( pBNS, u, v, iuv, &pedge, &s_or_t ); + if ( !IS_BNS_ERROR(bBackward) ) { + if ( bBackward ) { + delta = -delta; + } + + if ( s_or_t ) { + pst_edge = ( BNS_ST_EDGE *) pedge; + flow = pst_edge->flow; + f = (flow & EDGE_FLOW_ST_MASK) + delta; /* new flow */ + if ( !delta ) { + /*((BNS_ST_EDGE *)pedge)->flow = pst_edge->flow & ~EDGE_FLOW_ST_PATH;*/ + pst_edge->flow = pst_edge->flow & ~EDGE_FLOW_ST_PATH; + } else { + int cap = pst_edge->cap; + if ( f < 0 || f > cap ) { + ret = BNS_WRONG_PARMS; + } else { + if ( !(bChangeFlow & BNS_EF_CHNG_FLOW) ) { + f -= delta; /* do not actually change the flow, only find the augmenting path */ + } else + if ( delta ) { + /*((BNS_ST_EDGE *)pedge)->pass ++;*/ + pst_edge->pass ++; + } + flow = (flow & ~(EDGE_FLOW_ST_PATH | EDGE_FLOW_ST_MASK)) + f; + /*((BNS_ST_EDGE *)pedge)->flow = flow;*/ + pst_edge->flow = flow; + /*((BNS_ST_EDGE *)pedge)->delta += delta; */ + if ( bReverse ) { + /* u <- v; Note: in case of bReverse s_or_t has actually been determined + for the u' <- v' pair; therefore s and t should be switched + in order to correctly determine the 1st or the last atom + on the augmenting path. + */ + switch( s_or_t ) { + case 1: /* u = t: t<-v, v is the last vertex */ + ALTP_END_ATOM(pBNS->alt_path) = v / 2 - 1; + break; + case 2: /* u = s: s<-v, error */ + ret = BNS_WRONG_PARMS; + break; + case 3: /* v = t: u<-t, error */ + ret = BNS_WRONG_PARMS; + break; + case 4: /* v = s: u<-s, u is the first vertex */ + ALTP_START_ATOM(pBNS->alt_path) = u / 2 - 1; + ALTP_DELTA(pBNS->alt_path) = delta; + break; + default: + ret = BNS_WRONG_PARMS; + break; + } + } else { + /* u -> v */ + switch( s_or_t ) { + case 1: /* u = s: s->v, v is the first vertex */ + ALTP_START_ATOM(pBNS->alt_path) = v / 2 - 1; + ALTP_DELTA(pBNS->alt_path) = delta; + break; + case 2: /* u = t: t->v, error */ + ret = BNS_WRONG_PARMS; + break; + case 3: /* v = s: u->s, error */ + ret = BNS_WRONG_PARMS; + break; + case 4: /* v = t: u->t, u is the last vertex */ + ALTP_END_ATOM(pBNS->alt_path) = u / 2 - 1; + break; + default: + ret = BNS_WRONG_PARMS; + break; + } + } + } + } + } else { + f = (pedge->flow & EDGE_FLOW_MASK) + delta; + if ( !delta ) { + pedge->flow &= ~EDGE_FLOW_PATH; + } else { + if ( f < 0 || f > pedge->cap ) { + ret = BNS_WRONG_PARMS; + } else { + AT_NUMB iu = u / 2 - 1; + AT_NUMB iv = v / 2 - 1; + int indx; + if ( !(bChangeFlow & BNS_EF_CHNG_FLOW) ) { + f -= delta; /* do not actually change the flow, only find the augmenting path */ + } else + if ( delta ) { + pedge->pass ++; + } + pedge->flow = (pedge->flow & ~(EDGE_FLOW_PATH | EDGE_FLOW_MASK)) | f; + if ( ALTP_MAY_ADD(pBNS->alt_path) ) { + /* bReverse? u <- v : u -> v */ + indx = bReverse? (pedge->neighbor1 == iv) : (pedge->neighbor1 == iu); + ALTP_CUR_THIS_ATOM_NEIGHBOR(pBNS->alt_path) = pedge->neigh_ord[1-indx]; + ALTP_CUR_NEXT_ATOM_NEIGHBOR(pBNS->alt_path) = pedge->neigh_ord[indx]; + ALTP_NEXT(pBNS->alt_path); + } else { + ALTP_OVERFLOW(pBNS->alt_path) = 1; + ret = BNS_ALTPATH_OVFL; + } + } + } + } + return ret? ret : f; + } + return bBackward; +} + + +/* rescap_mark( ... ) + + Find residual capacity and mark the edge + as belonging to the augmenting path +*/ +int rescap_mark( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv ) +{ + BNS_ST_EDGE *pst_edge; + BNS_EDGE *pedge; + + int f, flow; + S_CHAR s_or_t; + int bBackward = GetEdgePointer( pBNS, u, v, iuv, &pedge, &s_or_t ); + + if ( !IS_BNS_ERROR( bBackward ) ) { + + if ( s_or_t ) { + pst_edge = (BNS_ST_EDGE *)pedge; + flow = pst_edge->flow; + f = (flow & EDGE_FLOW_ST_MASK); + if ( !bBackward ) { + f = (int) pst_edge->cap - f; + } + if ( flow & EDGE_FLOW_ST_PATH ) { + pBNS->bNotASimplePath ++; + f /= 2; /* this is the second time we pass the same edge: reduce flow by a factor of 2 */ + } else { + pst_edge->flow |= EDGE_FLOW_ST_PATH; /* mark the edge */ + } + } else { + flow = pedge->flow; + f = flow & EDGE_FLOW_MASK; + if ( !bBackward ) { + f = (int) pedge->cap - f; + } + if ( flow & EDGE_FLOW_PATH ) { + f /= 2; /* this is the second time we pass the same edge: reduce flow by a factor of 2 */ + pBNS->bNotASimplePath ++; + } else { + pedge->flow |= EDGE_FLOW_PATH; /* mark the edge */ + } + } + return f; + } + return bBackward; +} + + +/* GetPrevVertex( ... ) + + Get previous vertex in the searched path + z is SwitchEdge_Vert2(y) != y. Go backward from z to y +*/ +Vertex GetPrevVertex( BN_STRUCT* pBNS, Vertex y, Edge *SwitchEdge, EdgeIndex *iuv ) +{ + Vertex w, z, x2, y2, n; + EdgeIndex iwy; + + w = SwitchEdge_Vert1(y); + z = SwitchEdge_Vert2(y); + iwy = SwitchEdge_IEdge(y); + if ( z == y ) { + *iuv = iwy; + return w; + } + x2 = prim(y); + y2 = prim(z); + n = 0; + while ( y2 != NO_VERTEX ) { + w = SwitchEdge_Vert1(y2); + z = SwitchEdge_Vert2(y2); + iwy = SwitchEdge_IEdge(y2); + if ( w == x2 ) { + *iuv = iwy; + /*return z; */ + return (y + z)%2? z : prim(z); + } + n ++; +/* +#ifdef _DEBUG + if ( n ) { + int stop = 1; + } +#endif +*/ + if ( w == y2 ) + return NO_VERTEX; + y2 = w; + } + return y2; +} + + + +#define CHECK_TACN 1 + + +/* + The purpose is to avoid paths + + (H-group)[u]---atom[v]---((-)-cgroup)[w], + + where atom[v] is not acidic and (-) and H are not interchangeable without + explicit bond tautomerism. + + It is important that acidic atoms are only O,S,Se,Te and should have + only one chemical bond. Only because of this an early rejection of + the vertex v (before it gets on SCANQ) is possible. + + CHECK_TACN == 1 prohibits replacing (-) on N with H unless H can be moved to N + along an alternating path from another heteroatom (t-group will be detected). +*/ + + +#if ( FIX_TACN_POSSIBLE_BUG == 1 ) /* { */ + + +int bIgnoreVertexNonTACN_atom( BN_STRUCT* pBNS, Vertex u, Vertex v ) +{ +#define TYPE_T 1 /* t-group [also called H-group] */ +#define TYPE_CN 2 /* (-)c-group */ + int i, degree, ret, u_is_taut=0, w_is_taut, num_allowed=0, num_found_groups=0; + Vertex w; + EdgeIndex ivw; + if ( !pBNS->type_TACN || u <= 1 || v <= 1 || + (pBNS->vert[v/2-1].type & pBNS->type_TACN) ) { + return 0; /* add/remove H(+) is allowed for acidic atoms */ + } + if ( !pBNS->type_T || !pBNS->type_CN ) + return 0; /* should not happen */ + u_is_taut = ((pBNS->vert[u/2-1].type & pBNS->type_T) == pBNS->type_T )? TYPE_T : + ((pBNS->vert[u/2-1].type & pBNS->type_CN) == pBNS->type_CN)? TYPE_CN : 0; + if ( u_is_taut ) { + /* u is either t-group vertex or (-) c-group */ + degree = GetVertexDegree( pBNS, v ); + for ( i = 0; i < degree; i ++ ) { + /* v = vert[u].neighbor[i]; */ + w = GetVertexNeighbor( pBNS, v, i, &ivw ); + if ( w == NO_VERTEX || w <= 1 ) { + continue; /* the atom has only single bonds or it is s or t, ignore it */ + } + if ( w != u && (ret = rescap(pBNS, v, w, ivw)) > 0 ) { + num_allowed ++; + w_is_taut = ((pBNS->vert[w/2-1].type & pBNS->type_CN) == pBNS->type_CN)? TYPE_CN : + ((pBNS->vert[w/2-1].type & pBNS->type_T) == pBNS->type_T )? TYPE_T : 0; + if ( (u_is_taut | w_is_taut) == (TYPE_T | TYPE_CN) ) { + num_found_groups ++; + } + } + } + if ( num_found_groups && num_allowed == 1 ) { + return 1; /* reject */ + } + } + return 0; +#undef TYPE_T +#undef TYPE_CN +} + + +#else /* } FIX_TACN_POSSIBLE_BUG { */ + + +int bIgnoreVertexNonTACN_atom( BN_STRUCT* pBNS, Vertex u, Vertex v ) +{ + int i, degree, ret, u_is_taut=0, num_allowed=0, num_found_groups=0; + Vertex w; + EdgeIndex ivw; + if ( !pBNS->type_TACN || u <= 1 || v <= 1 || + (pBNS->vert[v/2-1].type & pBNS->type_TACN) ) { + return 0; /* add/remove H(+) is allowed for acidic atoms */ + } + if ( !pBNS->type_T || !pBNS->type_CN ) + return 0; /* should not happen */ + if ( (u_is_taut = (pBNS->vert[u/2-1].type & pBNS->type_T) == pBNS->type_T) || + ( (pBNS->vert[u/2-1].type & pBNS->type_CN) == pBNS->type_CN) ) { + /* u is either t-group vertex or (-) c-group */ + degree = GetVertexDegree( pBNS, v ); + for ( i = 0; i < degree; i ++ ) { + /* v = vert[u].neighbor[i]; */ + w = GetVertexNeighbor( pBNS, v, i, &ivw ); + if ( w == NO_VERTEX || w <= 1 ) { + continue; /* the atom has only single bonds or it is s or t, ignore it */ + } + if ( w != u && (ret = rescap(pBNS, v, w, ivw)) > 0 ) { + num_allowed ++; + if ( (u_is_taut? ((pBNS->vert[w/2-1].type & pBNS->type_CN) == pBNS->type_CN) : + ((pBNS->vert[w/2-1].type & pBNS->type_T) == pBNS->type_T ) ) ) { + num_found_groups ++; + } + } + } + if ( num_found_groups && num_allowed == 1 ) { + return 1; /* reject */ + } + } + return 0; +} + + + +#endif /* } FIX_TACN_POSSIBLE_BUG */ + + + +/* + The purpose is to avoid paths + (H-group)[u]---atom[v]---((-)-cgroup)[w], + + where atom[v] is not acidic and (-) and H are not interchangeable without + explicit bond tautomerism. + + It is important that acidic atoms are only O,S,Se,Te and should have + only one chemical bond. Only because of this an early rejection of + the vertex v (before it gets on SCANQ) is possible. +*/ + + +#if ( FIX_TACN_POSSIBLE_BUG == 1 ) /* { */ + + +int bIgnoreVertexNonTACN_group( BN_STRUCT* pBNS, + Vertex v, + Vertex w, + Edge *SwitchEdge ) +{ +#define TYPE_T 1 /* t-group [also called H-group] */ +#define TYPE_CN 2 /* (-)c-group */ + int u_is_taut=0, w_is_taut=0; + Vertex u; + EdgeIndex iuv; + if ( v <= 1 || w <= 1 ) + return 0; +#if ( CHECK_TACN == 1 ) + if ( !pBNS->type_TACN || + (pBNS->vert[v/2-1].type & pBNS->type_TACN) ) { + return 0; + } + if ( !pBNS->type_T || !pBNS->type_CN ) + return 0; /* should not happen */ +#endif + u = GetPrevVertex( pBNS, v, SwitchEdge, &iuv ); + /* + u = SwitchEdge_Vert1(v); + iuv = SwitchEdge_IEdge(v); + */ + if ( u == NO_VERTEX || iuv < 0 ) + return 0; /* should not happen */ + /* check edge adjacency */ + if ( pBNS->edge[iuv].neighbor1 != (u/2-1) && pBNS->edge[iuv].neighbor1 != v/2-1 || + (pBNS->edge[iuv].neighbor12 ^ (u/2-1)) != (v/2-1) ) { + return 0; /* !!! should not happen !!! */ + } + +#if ( CHECK_TACN == 1 ) + u_is_taut = ((pBNS->vert[u/2-1].type & pBNS->type_T) == pBNS->type_T )? TYPE_T : + ((pBNS->vert[u/2-1].type & pBNS->type_CN) == pBNS->type_CN)? TYPE_CN : 0; + w_is_taut = ((pBNS->vert[w/2-1].type & pBNS->type_T) == pBNS->type_T )? TYPE_T : + ((pBNS->vert[w/2-1].type & pBNS->type_CN) == pBNS->type_CN)? TYPE_CN : 0; + if ( (u_is_taut | w_is_taut) == (TYPE_T | TYPE_CN ) ) { + /* rescap must have already been checked */ + return 1; + } +#endif + + return 0; +#undef TYPE_T +#undef TYPE_CN +} + + +#else /* } FIX_TACN_POSSIBLE_BUG { */ + + +int bIgnoreVertexNonTACN_group( BN_STRUCT* pBNS, + Vertex v, + Vertex w, + Edge *SwitchEdge ) +{ + int u_is_taut=0, w_is_taut=0; + Vertex u; + EdgeIndex iuv; + if ( v <= 1 || w <= 1 ) + return 0; +#if ( CHECK_TACN == 1 ) + if ( !pBNS->type_TACN || + (pBNS->vert[v/2-1].type & pBNS->type_TACN) ) { + return 0; + } + if ( !pBNS->type_T || !pBNS->type_CN ) + return 0; /* should not happen */ +#endif + u = GetPrevVertex( pBNS, v, SwitchEdge, &iuv ); + /* + u = SwitchEdge_Vert1(v); + iuv = SwitchEdge_IEdge(v); + */ + if ( u == NO_VERTEX || iuv < 0 ) + return 0; /* should not happen */ + /* check edge adjacency */ + if ( pBNS->edge[iuv].neighbor1 != (u/2-1) && pBNS->edge[iuv].neighbor1 != v/2-1 || + (pBNS->edge[iuv].neighbor12 ^ (u/2-1)) != (v/2-1) ) { + return 0; /* !!! should not happen !!! */ + } + +#if ( CHECK_TACN == 1 ) + if ( ((u_is_taut = (pBNS->vert[u/2-1].type & pBNS->type_T) == pBNS->type_T) || + ( (pBNS->vert[u/2-1].type & pBNS->type_CN) == pBNS->type_CN)) && + ((w_is_taut = (pBNS->vert[w/2-1].type & pBNS->type_T) == pBNS->type_T) || + ( (pBNS->vert[w/2-1].type & pBNS->type_CN) == pBNS->type_CN)) && + u_is_taut + w_is_taut == 1 ) { + /* rescap must have already been checked */ + return 1; + } +#endif + + return 0; +} +#endif /* } FIX_TACN_POSSIBLE_BUG { */ + + + +#if ( FIX_KEEP_H_ON_NH_ANION == 1 ) + + +/* bIsRemovedHfromNHaion( ... ) + + Detect an attempt to remove H from -NH(-) to make =N(-); + all taut atoma except N are 'acidic' +*/ +int bIsRemovedHfromNHaion( BN_STRUCT* pBNS, Vertex u, Vertex v ) +{ + int i, u2, v2, vat2; + Vertex vtg, vat; + BNS_VERTEX *pvAT, *pvCN; + BNS_EDGE *pEdge; + if ( !pBNS->type_TACN || u <= 1 || v <= 1 || + u%2 || !(v%2) /* the edge flow may only increase */ ) { + return 0; + } + if ((pBNS->vert[u2 = u/2-1].type & pBNS->type_TACN) || + (pBNS->vert[v2 = v/2-1].type & pBNS->type_TACN) ) { + return 0; /* add/remove H is allowed for acidic atoms */ + } + if ( !pBNS->type_T || !pBNS->type_CN ) + return 0; /* should not happen */ + /* find which of u, v vertices is N and which is t-group */ + if ( ((pBNS->vert[u2].type & pBNS->type_T) == pBNS->type_T ) && v2 < pBNS->num_atoms ) { + vtg = u; + vat = v; + } else + if ( ((pBNS->vert[v2].type & pBNS->type_T) == pBNS->type_T ) && u2 < pBNS->num_atoms ) { + vtg = v; + vat = u; + } else { + return 0; + } + vat2 = vat/2-1; + pvAT = pBNS->vert + vat2; /* atom */ + for ( i = pvAT->num_adj_edges-1; 0 <= i; i -- ) { + pEdge = pBNS->edge + pvAT->iedge[i]; + pvCN = pBNS->vert + (pEdge->neighbor12 ^ vat2); + if ( ((pvCN->type & pBNS->type_CN) == pBNS->type_CN) && pEdge->flow > 0 ) { + return 1; /* detected */ + } + } + return 0; +} +#endif + + +#if ( FIX_AVOID_ADP == 1 ) + +/* bIsAggressiveDeprotonation( ... ) + + Detect (tg)-N=A-A=A-A=N-(tg) + u v w + k = 5 4 3 2 1 0 1 2 + ^ + odd number means ADP +*/ +int bIsAggressiveDeprotonation( BN_STRUCT* pBNS, + Vertex v, + Vertex w, + Edge *SwitchEdge ) +{ +#define TYPE_T 1 /* t-group [also called H-group] */ +#define TYPE_CN 2 /* (-)c-group */ +#define TYPE_AT 4 + int k, v2, u2, w2, u2_next, type0, type1, type2, type; + Vertex u, u_next; + EdgeIndex iuv; + if ( v <= 1 || w <= 1 ) + return 0; + + if ( !pBNS->type_TACN || !pBNS->type_T || !pBNS->type_CN ) + return 0; /* should not happen */ + v2 = v/2 - 1; + w2 = w/2 - 1; + if ( v2 >= pBNS->num_atoms || w2 < pBNS->num_atoms ) + goto cross_edge; + + if ( !((pBNS->vert[w2].type & pBNS->type_T) == pBNS->type_T ) && + !((pBNS->vert[w2].type & pBNS->type_CN) == pBNS->type_CN) ) + goto cross_edge; + /* v ia an atom, w is a t-group, v != w' */ + for ( k = 0, u = v; 1 < (u_next = u, u = GetPrevVertex( pBNS, u, SwitchEdge, &iuv )); k ++ ) { + u2 = u/2 - 1; + if ( u2 >= pBNS->num_atoms ) { + /* moving backward along the alt path we have found a vertex + that is not an atom. Possibly it is a t- or (-)c-group */ + if ( !( k % 2 ) ) { + return 0; /* even vertex -- always okay */ + } + if ( !((pBNS->vert[u2].type & pBNS->type_T) == pBNS->type_T ) && + !((pBNS->vert[u2].type & pBNS->type_CN) == pBNS->type_CN) ) { + /* not a t- or (-)c-group */ + return 0; + } + u2_next = u_next/2 - 1; + if ( !(pBNS->vert[v2 ].type & pBNS->type_TACN) && + !(pBNS->vert[u2_next].type & pBNS->type_TACN) ) { + /* none of the atoms at the ends are N */ + return 0; + } + return 1; + } + } + return 0; +cross_edge: + /***************************************************************************** + * v and w (v=w') are same vertex reached with opposite "phases". + * w cannot be (t) because this would have been detected earlier -- ??? + * (t)-A=A-A=A-A=A-(t) + * v + * 3 2 1 0 1 2 3 4 + * kv kw + * (kv + kw)%2 == 1 <==> aggressive deprotonation + *****************************************************************************/ + if ( v == prim(w) ) { + type0 = 0; + if ( v2 >= pBNS->num_atoms ) { + type0 = ((pBNS->vert[v2].type & pBNS->type_T) == pBNS->type_T )? TYPE_T : + ((pBNS->vert[v2].type & pBNS->type_CN) == pBNS->type_CN)? TYPE_CN : 0; + } + } + + + return 0; +} +#endif + + +int rescap( BN_STRUCT* pBNS, Vertex u, Vertex v, EdgeIndex iuv ) +{ + BNS_ST_EDGE *pst_edge; + BNS_EDGE *pedge; + + int f; + S_CHAR s_or_t; + int bBackward = GetEdgePointer( pBNS, u, v, iuv, &pedge, &s_or_t ); + + if ( !IS_BNS_ERROR(bBackward) ) + { + + if ( s_or_t ) + { + pst_edge = (BNS_ST_EDGE *)pedge; + f = (pst_edge->flow & EDGE_FLOW_ST_MASK); + if ( !bBackward ) + { + f = (int) pst_edge->cap-f; + } + } + else + { + f = (pedge->flow & EDGE_FLOW_MASK); + if ( !bBackward ) + { + f = (int) pedge->cap-f; + } + } + return f; + } + return bBackward; /* error */ +} + + +/* + W.Kocay, D.Stone, + "An Algorithm for Balanced Flows", + The Journal of Combinatorial Mathematics and Combinatorial Computing, + vol. 19 (1995) pp. 3-31 + + W.Kocay, D.Stone, + "Balanced network flows", + Bulletin of the Institute of Combinatorics and its Applications, + vol. 7 (1993), pp. 17--32 + + N = a balanced network (bipartite directed graph) of: + n=2*V+2 vertices (incl. s (source) and t (target); + each other vertex i is included 2 times: + set X (x[i]) of V vertices (v) and a set Y (y[j]) of + V complementary vertices (v') so that x[i]' = y[i], x[i]''=x[i], and + m=2*E+2*V edges (each original undirected edge i-j is represented as + 2 directed edges: x[i]->y[j] and x[j]->y[i]; plus + V edges s->x[i] and V edges y[j]->t) + v' = a complement vertex to v + v'u' = (uv)' = a complement edge to uv + rescap(uv) = cap(uv)-f(uv) if uv is a forward edge + = f(uv) if uv is a backward edge + (i) 0 <= f(uv) <= cap(uv) + (ii) f+(u) = f-(u) for all u in X, Y where f+(u) is a total flow out of u, + and f-(u) is a total flow into u + (iii) f(uv) = f((uv)') (balanced flow condition) + + S = a set of all s-searchable vertices + S- = all other vertices + if t in S, then N contains a valid augmenting path P, so that flow can be + augmented on both P and P' + if t not in S, then let K=[S,S-], the set of all edges directed from S to S-. + K is an edge-cut that has a special structure. + Let + A = {x[i], y[i] |x[i] not in S, y[i] in S} + B = {x[i], y[i] |x[i] in S, y[i] not in S} + C = {x[i], y[i] |x[i] in S, y[i] in S} + D = {x[i], y[i] |x[i] not in S, y[i] not in S} + N[C] = subgraph of N induced by C; + it consists of of a number of connected components C[i] + K[i] = those edges of K with one endpoint in C[i]. + + If t is in S- then + + i) Each C[i] = C[i]' + ii) There are no edges between C and D + iii) Each K[i] has odd capacity, it is called a balanced edge-cut. + + balcap(K) = cap(K) - odd(K), where odd(K) is the number of connected components in N[C]. + Name "odd(K)" is because each cap(K[i]) is odd. + + Max-Balanced-Flow-Min-Balanced-Cut Theorem: + + Let f be a balanced flow in N, and let K be any balanced edge-cut. + The value of a maximum balanced flow equals the capacity of a minimum + edge-cut, that is, val(f) = balcap(K) when f is maximum and K is minimum. + +*/ + +/*******************************************************/ +/* */ +/* VERTEX NUMBERING */ +/* */ +/* Total number of atoms = n */ +/* Total number of vertices = 2*n+2 */ +/*******************************************************/ +/* */ +/* atom numbering starts from 0: */ +/* */ +/* atoms s t x0 y0 x1 y1 ... xi yi ...xn yn */ +/* vertices 0 1 2 3 4 5 ... 2i-2 2i-1 ...2n-2 2n-1 */ +/* */ +/* atom = vertex/2-1; if negative then s or t */ +/* */ +/* vertex = (atom + 1) * 2 + i; i=0 for x, i=1 for y */ +/* */ +/*******************************************************/ + + + +/*********************************************************************************/ +/* v' variable is called prim(v) for now */ +/*********************************************************************************/ + + +/* + BalancedNetworkSearch( ... ) + + N has source s, target t, the mirror network M is constructed. + The tree T contains a valid sv-path for each v in T. Simultaneously, the complementary + tree T' is built as indicated in comments. The trees T and T' must have no edges or + vertices in common. Initially T will be built as in breadth-first-search, and T' will + be the complementary tree, it will contain the complementary valid v't-path. + + @@@ NB: this procedure is _not_ recursive + +*/ +int BalancedNetworkSearch( BN_STRUCT* pBNS, BN_DATA *pBD, int bChangeFlow ) + +{ + Vertex *BasePtr = pBD->BasePtr; + Edge *SwitchEdge = pBD->SwitchEdge; + S_CHAR *Tree = pBD->Tree; + Vertex *ScanQ = pBD->ScanQ; + int QSize = pBD->QSize; + Vertex *Pu = pBD->Pu; + Vertex *Pv = pBD->Pv; + int max_len_Pu_Pv= pBD->max_len_Pu_Pv; + + /* added to translate into C */ + int i, k, degree, delta, ret = 0; + Vertex u, b_u, v, b_v, w; + EdgeIndex iuv; +#if ( BNS_RAD_SEARCH == 1 ) + int n, bRadSearch = (BNS_EF_RAD_SRCH & bChangeFlow) && pBD->RadEndpoints; + BRS_MODE bRadSrchMode = RAD_SRCH_NORM; + int bRadSearchPrelim = 0; + if ( bRadSearch ) + { + pBD->nNumRadEndpoints = 0; + bRadSrchMode = pBD->bRadSrchMode; + bRadSearchPrelim = pBNS->type_TACN && bRadSrchMode == RAD_SRCH_NORM; + } +#endif + +/* -- Always -- + Vertex_s = FIRST_INDX; + Vertex_t = Vertex_s+1; +*/ + QSize = k = 0; /* put s on ScanQ = set S */ + ScanQ[QSize] = Vertex_s; + BasePtr[Vertex_t] = Vertex_s; + BasePtr[Vertex_s] = BLOSSOM_BASE; /* create initial blossom C(Vertex_s) with base s */ + Tree[Vertex_s] = TREE_IN_1; + + do { + u = ScanQ[k]; /* select u from the head of ScanQ */ + /* since u is on the queue, it has a blossom C(U) with base b_u */ + b_u = FindBase( u, BasePtr ); + degree = GetVertexDegree( pBNS, u ); +#if ( BNS_RAD_SEARCH == 1 ) + n = 0; +#endif + for ( i = 0; i < degree; i ++ ) + { + /* v = vert[u].neighbor[i]; */ + v = GetVertexNeighbor( pBNS, u, i, &iuv ); + if ( v == NO_VERTEX ) + { + continue; /* the atom has only single bonds, ignore it */ + } +#if ( BNS_RAD_SEARCH == 1 ) + if ( !k && bRadSrchMode == RAD_SRCH_FROM_FICT && v/2 <= pBNS->num_atoms ) + { + continue; /* start from fict. vertices only */ + } + if ( bRadSearchPrelim && v/2 > pBNS->num_atoms ) + { + continue; /* during initial add/remove H allow radical movement only through real atoms */ + } +#endif + if ( /* PrevPt[u] != v ** avoid edges of T */ + ( SwitchEdge_Vert1(u) != v || SwitchEdge_Vert2(u) != u ) /* avoid edges of T */ + && (ret = rescap(pBNS, u, v, iuv)) > 0 ) + { + /* special treatment to prevent H<->(-) replacement on non-acidic atoms */ + /*----------------------------------------------------------------------*/ + if ( pBNS->type_TACN ) { + if ( bIgnoreVertexNonTACN_atom( pBNS, u, v ) ) + { + continue; + } + if ( bIgnoreVertexNonTACN_group( pBNS, u, v, SwitchEdge ) ) + { + continue; + } +#if ( FIX_KEEP_H_ON_NH_ANION == 1 ) + if ( bIsRemovedHfromNHaion( pBNS, u, v ) ) + { + continue; + } +#endif +#if ( FIX_AVOID_ADP == 1 ) + if ( bIsAggressiveDeprotonation( pBNS, u, v, SwitchEdge ) ) + { + continue; + } +#endif + } + /*----------------------------------------------------------------------*/ + b_v = FindBase(v, BasePtr); /* Notation: b_x is a base of x */ + + if ( b_v == NO_VERTEX ) + { + /* originally 0 instead of NO_VERTEX */ + /* Important note: following "A. Note of Implementing the Algorithm" from the + article by Kocay and Stone, all references to PrevPt[a] have been + replaced with SwitchEdge[a][0]=SwitchEdge_Vert1(a); to be on a safe side + the check whether (SwitchEdge_Vert2(a)==a) has been added. + */ + + /* v is not yet in T or T' -- add it to T and M */ + /*PrevPt[v] = u; ** this effectively adds v to T and v' to T' */ + QSize ++; + ScanQ[QSize] = v; + TREE_MARK(v, TREE_IN_1); /* mark v s-reachable (in T) */ + TREE_MARK(prim(v), TREE_IN_2); /* mark v' in T' */ + + /* Switch Edges: If u in T then Pu (a valid su-path) can be constructed + by successfully executing u=PrevPt[u] until u=s. + For vertices in T' the situation is different. + Suppose uv and v'u' are added to a mirror network M creating a blossom: + + s T (Note: in the code v' is prim(v), + T / \ u' is prim(u), etc.) + / ... + w=x1 + / \ u in T, v in T' + u=y2 v'=y3 + \ / <--- two added edges uv and (uv)'=v'u' + -------- X ------------intersection of the edges ------------------------ + / \ <--- (the edges intersection in the picture is shown as X) + u'=x2 v=x3 u' it T', v' in T + \ / + T' w'=y1 + \ ... + \ / + t T' + + Vertices v and u' now become s-reachable; + The valid paths to v and u' must use the edges uv and v'u' respectively. + + For each vertex z in S we define a switch-edge that allows a valid sz-path + to be constructed, SwitchEdge[v]=uv and SwitchEdge[u']=v'u'. (We don't + know the direction of the edge uv, it may be (u,v) or (v,u). In either case, + the complementary edge is indicated by v'u'). + + Vertex w' also becomes s-reachable when uv is added to M, and a valid sw'-path + must use one of uv and v'u'. Therefore we choose one of them, say uv (see below + the rule of choosing the switch-edge), and define SwitchEdge[w'] = uv. + + When the addition of an edge uv to M causes a vertex z to become s-reachable + (where z was previously non-reachable), z is placed on the ScanQ, that is, into S. + The edge uv is said to be a switch-edge for z. + + Rule: We choose the the order of the vertices uv to be such that the valid sz-path + consists of a valid su-path, followed by edge uv, followed by a valid vz-path. + + For vertices z in T we can take SwitchEdge[z]=yz where y=PrevPt[z] since + it is the edge yz that allows z to be s-reachable. + For vertices z not in S we take SwitchEdge[z]=NONE. + + */ + + /* v is not yet in T or T' -- add it to T and M */ + SwitchEdge_Vert1(v) = u; /* this effectively adds uv and v'u' to M */ + SwitchEdge_IEdge(v) = iuv; + + BasePtr[prim(v)] = v; + BasePtr[v] = BLOSSOM_BASE; /* create a trivial blossom C(v) with base v */ +#if ( BNS_RAD_SEARCH == 1 ) + n ++; +#endif + } + else if ( TREE_IS_S_REACHABLE(prim(v)) /*Is_s_Reachable(prim(v)*/ + /* if v' is reachable, an st-path is given by P(u)-uv-P'(v') */ + /*&& PrevPt[prim(u)] != prim(v) ** avoid edges of T' */ + && (SwitchEdge_Vert1(prim(u)) != prim(v) || SwitchEdge_Vert2(prim(u)) != prim(u)) /* avoid edges of T' */ + && b_u != b_v + && !(pBNS->type_TACN && bIgnoreVertexNonTACN_group( pBNS, prim(v), u, SwitchEdge )) +#if ( FIX_KEEP_H_ON_NH_ANION == 1 ) + && !(pBNS->type_TACN && bIsRemovedHfromNHaion( pBNS, prim(v), u )) +#endif + ) + { + +#if ( BNS_RAD_SEARCH == 1 ) + n ++; +#endif + + /* there is now a valid sv-path via u avoiding b_v (unless v==b_v) + => u, v, u', and v' now all become part of the same connected component of M[C] */ + w = MakeBlossom( pBNS, ScanQ, &QSize, Pu, Pv, max_len_Pu_Pv, SwitchEdge, BasePtr, u, v, iuv, b_u, b_v, Tree ); + /* this constructed the new blossom and returned its base */ + if ( IS_BNS_ERROR( w ) ) + { + pBD->QSize = QSize; + return w; /* error */ + } + b_u = w; /* the new base of C(u) */ + if ( prim(w) == Vertex_t ) + { + /* t is now s-reachable, a valid augmenting path P exists in M */ + delta = FindPathCap( pBNS, SwitchEdge, Vertex_s, Vertex_t, 10000 ); /* compute the residual capacity of P + P' */ + if ( IS_BNS_ERROR( delta ) ) + { + pBD->QSize = QSize; + return delta; /* error */ + } +#if ( ALLOW_ONLY_SIMPLE_ALT_PATH == 1 ) + if ( pBNS->bNotASimplePath || abs(delta) > 1 ) + { + delta = 0; + } +#endif + if ( delta ) + { + pBNS->bChangeFlow |= (bChangeFlow & BNS_EF_CHNG_FLOW); + } + ret = PullFlow( pBNS, SwitchEdge, Vertex_s, Vertex_t, delta, 0, bChangeFlow ); /* augment on a pair of valid st-paths */ + pBD->QSize = QSize; + return + ( IS_BNS_ERROR(ret) ? ret : delta ); + } + } + } + + else if ( IS_BNS_ERROR( ret ) ) + { + pBD->QSize = QSize; + return ret; /* error */ + } + } + +#if ( BNS_RAD_SEARCH == 1 ) + if ( bRadSearch && !n ) + { + /* the BNS stopped at u */ + n = RegisterRadEndpoint( pBNS, pBD, u); + if ( IS_BNS_ERROR( n ) ) + { + pBD->QSize = QSize; + return n; + } + } +#endif + + k ++; /* advance ScanQ */ + } while( k <= QSize ); + + + /* if this point is reached, no valid augmenting path exists, ScanQ contains + the set S of all s-reachable vertices and K=[S,S-] is a minimum balanced edge-cut */ + /* ClearFlowMarks( vert, num_vert); */ + pBD->QSize = QSize; + + return 0; +} + + +/* +Blossoms. + + The vertices of a mirror network M consist T U T'. Intersection T ^ T' is empty. + + The edges of M consist of switch-edges and their complements because edges + are added in complementary pairs, one of which is always a switch-edge. + + The base of every blossom is in T. + Let C(i) be a blossom with base b_i. Since C(i)=C(i)', C(i) contains vertices of T and T'. + Since every valid sv-path to v in C(i) contains b_i, b_i is the first s-reachable vertex of C(i). + + Suppose the mirror network M contains a valid sz-path P(z) to all vertices z in ScanQ. + Every vertex of P(z) is s-reachable therefore its vertices are all in blossoms or + trivial blossoms. + + Let z be an s-reachable vertex and P(z) be a valid path in M. + Then every valid sz-path in M contains exactly the same sequence of blossom bases as P(z). + +*/ + + + +/*********************************************************************** + BasePtr[u] = -2 NO_VERTEX u is not a blossom + -1 BLOSSOM_BASE u is the base of its blossom + v a vertex closer to the base +************************************************************************/ +Vertex FindBase( Vertex u, Vertex *BasePtr ) +{ + if ( BasePtr[u] == NO_VERTEX ) + { + return NO_VERTEX; + } + else if ( BasePtr[u] == BLOSSOM_BASE ) + { + return u; + } + else + { + Vertex b; + b = FindBase(BasePtr[u], BasePtr ); + BasePtr[u] = b; /* path compression */ + return b; + } +} + + +/* Returns index of the last path element and the path */ +int FindPathToVertex_s( Vertex x, Edge *SwitchEdge, Vertex *BasePtr, Vertex *Path, int MaxPathLen ) +{ + /* x is the base of a blossom, construct a valid Path of blossom bases to s */ + int i = 0; + Path[i] = x; + while ( x != Vertex_s ) { + x = FindBase(SwitchEdge_Vert1(x), BasePtr); + if ( ++i < MaxPathLen ) { + Path[i] = x; + } else { + return BNS_WRONG_PARMS; + } + } + return i; +} + + +/* Make a blossom */ +Vertex MakeBlossom( BN_STRUCT* pBNS, Vertex *ScanQ, int *pQSize, + Vertex *Pu, Vertex *Pv, int max_len_Pu_Pv, + Edge *SwitchEdge, Vertex *BasePtr, + Vertex u, Vertex v, EdgeIndex iuv, Vertex b_u, Vertex b_v, S_CHAR *Tree ) +{ + /* In order to find the base of the new blossom, the paths + P(u) and P(v') are constructed and compared in order to + find the last blossom base they have in common which + is reachable on a valid path. + + Edge uv connects two blossoms, their bases are b_u and b_v. + */ + Vertex w, z; + int len_Pu, len_Pv; + int i, j; + EdgeIndex izw; + + len_Pu = FindPathToVertex_s( b_u, SwitchEdge, BasePtr, Pu, max_len_Pu_Pv ); + if ( IS_BNS_ERROR( len_Pu ) ) { + return len_Pu; + } + len_Pv = FindPathToVertex_s( b_v, SwitchEdge, BasePtr, Pv, max_len_Pu_Pv ); + if ( IS_BNS_ERROR( len_Pv ) ) { + return len_Pv; + } + i = len_Pu; + j = len_Pv; + /* initially Pu[i] and Pv[j] both equal to s, but their first elements are different */ + /* find the last blossom base common to Pu and Pv */ + while ( i >= 0 && j >= 0 && Pu[i] == Pv[j] ) { + /* was (Pu[i]==Pv[j] && i>=0 && j>=0) => tested Pu[-1], Pv[-1] <- pointed by W.Ihlenfeldt 08-26-2004*/ + i --; + j --; + } + i ++; + w = Pu[i]; /* w is the last common vertex */ + z = SwitchEdge_Vert1(w); + izw = SwitchEdge_IEdge(w); + /* now extend the blossom if rescap(zw) >= 2 */ + while ( w != Vertex_s && rescap(pBNS, z, w, izw) >= 2 ) + { + i++; + w = Pu[i]; + z = SwitchEdge_Vert1(w); + izw = SwitchEdge_IEdge(w); + } + /* w is the base of the new blossom */ + /* first follow the path Pu from w to b_u */ + for ( i = i-1; i >= 0; i -- ) { + z = Pu[i]; /* z is the base of the blossom */ + BasePtr[z] = w; + BasePtr[prim(z)] = w; /* w is the new base of the blossom */ + /* z and z' may already be part of a blossom that is being + swallowed into a larger blossom. + We don't want to change the switch edge in that case. + */ + + if ( !TREE_IS_ON_SCANQ(prim(z)) /*!IsInScanQ(prim(z)) */) + { + SwitchEdge_Vert1(prim(z)) = prim(v); /* set the switch edge of z' */ + /* SwitchEdge[prim(z)][1] = prim(u); */ + SwitchEdge_IEdge(prim(z)) = iuv; + (*pQSize) ++; + ScanQ[*pQSize] = prim(z); /* add z' to ScanQ */ + TREE_MARK(prim(z), TREE_IN_2BLOSS); /* mark z' s-reachable */ + } + } + /* now follow the path Pv */ + for ( j = j; j >= 0; j -- ) { + z = Pv[j]; /* z is the base of the blossom */ + BasePtr[z] = w; + BasePtr[prim(z)] = w; /* w is the new base of the blossom */ + /* z and z' may already be part of a blossom that is being + swallowed into a larger blossom. + We don't want to change the switch edge in that case. + */ + + if ( !TREE_IS_ON_SCANQ(prim(z)) /*!IsInScanQ(prim(z)) */ ) + { + SwitchEdge_Vert1(prim(z)) = u; /* set the switch edge of z' */ + /* SwitchEdge[prim(z)][1] = v; */ + SwitchEdge_IEdge(prim(z)) = iuv; + (*pQSize) ++; + ScanQ[*pQSize] = prim(z); /* add z' to ScanQ */ + TREE_MARK(prim(z), TREE_IN_2BLOSS); /* mark z' s-reachable */ + } + } + + if ( !TREE_IS_ON_SCANQ(prim(w)) /* !IsInScanQ(prim(w))*/ ) + { /* add w' to the blossom */ + SwitchEdge_Vert1(prim(w)) = u; /* set the switch edge of w' */ + /* SwitchEdge[prim(w)][1] = v; */ + SwitchEdge_IEdge(prim(w)) = iuv; + (*pQSize) ++; + ScanQ[*pQSize] = prim(w); /* add w' to ScanQ */ + TREE_MARK(prim(w), TREE_IN_2BLOSS); /* mark w' s-reachable */ + } + return w; +} + + +/* + When t is found to be s-reachable, a valid st-path P is known to exist. + Its complementary path P' is also valid. Once the residual capacity + delta(P) is known, the flow is augmented by calling PullFlow(s,t,delta). + It constructs the path P by using the switch-edges. + Let uv=SwitchEdge[t]. + Then P is given by a valid su-path, followed by the edge uv, followed by + a valid vt-path. + PullFlow is a recursive procedure that constructs the path and its complement. + + Let wz=SwitchEdge[y]. PullFlow(x, y, delta) uses the xw- and zy-portions of P + (see below). Since it must also augment on P' simultaneously, the zy-portion + is replaced by the y'z'-portion. + + x y' + | | P: x--w--z--y + P | | P' P': y'-z'-w'-x' + | o + o \ + / w' z \ + / o----\ /----o / + \ / \ / \ / + \/ X \/ + /\ / \ /\ + / \ w / \ z' / \ + \ o---- ----o / Using a switch-edge wz and w'z' + \ / to construct P and P' + o o + | | + | | + x' y + + +*/ + + + +/* PullFlow( ... ) + + Augment the flow by delta on all edges on a path P + between x and y in the order of the path; + AugmentEdge( pBNS, w, z, iwz, delta, 0 ) means the path is in w->z direction + AugmentEdge( pBNS, w, z, iwz, delta, 1 ) means the path is in w<-z direction + + Unlike PullFlow in the paper by Kocay & Stone, here the augmentation + starts at "s", proceeds sequentially through the path end terminates at "t". + Since we do not really need the complement path, PullFlow ignores it. +*/ +int PullFlow( BN_STRUCT *pBNS, + Edge *SwitchEdge, + Vertex x, + Vertex y, + int delta, + S_CHAR bReverse, + int bChangeFlow ) +{ Vertex w, z; + EdgeIndex iwz; + int ret = 0; + + w = SwitchEdge_Vert1(y); + z = SwitchEdge_Vert2(y); + iwz = SwitchEdge_IEdge(y); + if ( bReverse ) { + /* P consists of a path from x to w, then wz, then a path from z to y. */ + /* z may equal y, in which case z is just PrevPt[y] */ + if ( z != y ) { + ret = PullFlow( pBNS, SwitchEdge, prim(y), prim(z), delta, (S_CHAR)(1-bReverse), bChangeFlow ); /* augment between z and y */ + } + if ( !IS_BNS_ERROR(ret) ) { + ret = AugmentEdge( pBNS, w, z, iwz, delta, bReverse, bChangeFlow); + } + /* Do not augment the complementary path: AugmentEdge( prim(z), prim(w), vert, delta); */ + /* w may equal x, in which case there is no need to call PullFlow(x, w) */ + if ( w != x && !IS_BNS_ERROR(ret) ) { + ret = PullFlow( pBNS, SwitchEdge, x, w, delta, bReverse, bChangeFlow ); /* augment between x and w */ + } + } else { + /* P consists of a path from x to w, then wz, then a path from z to y. */ + /* w may equal x, in which case there is no need to call PullFlow(x, w) */ + if ( w != x && !IS_BNS_ERROR(ret) ) { + ret = PullFlow( pBNS, SwitchEdge, x, w, delta, bReverse, bChangeFlow ); /* augment between x and w */ + } + if ( !IS_BNS_ERROR(ret) ) { + ret = AugmentEdge( pBNS, w, z, iwz, delta, bReverse, bChangeFlow); + } + /* z may equal y, in which case z is just PrevPt[y] */ + if ( z != y && !IS_BNS_ERROR(ret) ) { + ret = PullFlow( pBNS, SwitchEdge, prim(y), prim(z), delta, (S_CHAR)(1-bReverse), bChangeFlow ); /* augment between z and y */ + } + } + return ret; +} + + +/* + Before augmenting on the two paths, it is necessary to find delta(P). + This can be done by following the paths and computing the minimum + residual capacity of all edges on P. An edge on both P and P' counts + for only half of its actual residual capacity, since augmentng on P by + delta will simutaneously reduce its capacity on P' by delta. + The path P can only be followed by using the switch-edges, as in PullFlow(...). + FindPathCap( x, y, delta ) is a recursive procedure that finds the residual + capacity on the portion of P between x and y. delta is the minimum capacity + found so far along the path. +*/ + + +/* + Find the minimum residual capacity of all edges + between x and y in a valid st-path P. + delta is the minimum found so far + the vertices occur in order s,...,x,...,y,...,t along P + the vertices occur in order s,...,y',...,x',...,t along P' +*/ +int FindPathCap( BN_STRUCT* pBNS, Edge *SwitchEdge, Vertex x, Vertex y, int delta ) +{ + + Vertex w, z, iwz; + int cap, delta2; + +/* static int level; + + if ( level ++ > 50 ) { +#ifdef _DEBUG + int stop = 1; +#else + ; +#endif + }*/ + + + w = SwitchEdge_Vert1(y); + z = SwitchEdge_Vert2(y); /* wz is on the path P */ + iwz = SwitchEdge_IEdge(y); /* edge index */ + + /* rescap_mark() detects edges passed 2 times and reduces rescap */ + cap = rescap_mark( pBNS, w, z, iwz ); + + if ( IS_BNS_ERROR( cap ) ) { + /* level --; */ + return cap; + } + if ( cap < delta ) { + delta = cap; + } + /* P consists of a path from x to w, then wz, then a path from z to y */ + if ( w != x ) { + delta2 = FindPathCap( pBNS, SwitchEdge, x, w, delta ); + delta = inchi_min( delta2, delta ); + } + if ( z != y ) { + delta2 = FindPathCap( pBNS, SwitchEdge, prim(y), prim(z), delta ); + delta = inchi_min( delta2, delta ); + } + /* level --; */ + return delta; +} + + +/* + BT = bond types +*/ + + +#define BT_ALTERN_BOND 1 /* 1-2, possibly stereo */ +#define BT_OTHER_ALTERN_BOND 2 /* 1-3, 2-3, 1-2-3 alternating non-stereo non-taut bonds */ + +#define BT_ALTERN_NS_BOND 4 + +#define BT_TAUTOM_BOND 8 + +#define BT_ALTERN_UNKN_BOND 16 + +#define BT_IGNORE_BOND 0 + +#define BT_NONSTEREO_MASK (BT_TAUTOM_BOND|BT_ALTERN_NS_BOND) + +#define BT_ALT_BOND_MASK (BT_ALTERN_BOND|BT_OTHER_ALTERN_BOND) + +#define BT_NONTAUT_BOND_MASK (BT_ALTERN_BOND|BT_OTHER_ALTERN_BOND|BT_ALTERN_NS_BOND) + +/* BNS members redefinitions for finding non-stereo bonds */ +/* BNS_EDGE */ +#define nBlockNumberAltBns flow /* internal variable of the DFS traversal: mark traversed bonds */ +#define nNumAtInBlockAltBns cap +#define nBondTypeInpAltBns pass /* 0=>cannot be stereo at all, 1=>alt or taut non-stereo, 2=>can be stereo */ +#define nBondNonStereoAltBns cap /* 1=>found to be non-stereogenic although BondTypeInp=2; 0=>as in BondTypeInp */ + +#if ( BNS_MARK_ONLY_BLOCKS == 1 ) /* { */ +/* BNS_VERTEX */ +#define bCutVertexAltBns st_edge.cap0 /* cut-vertex flag */ +#define nRingSystemAltBns st_edge.cap /* ordering number of a ring system */ +#define nNumAtInRingSystemAltBns st_edge.flow0 /* number of vertices in a ring system */ +#define nBlockSystemAltBns st_edge.flow /* result of the DFS traversal: even cirquit must be within one block */ + +#endif /* } */ + +#define valenceAltBns num_adj_edges + + +/* + MarkRingSystemsAltBns +*/ +int MarkRingSystemsAltBns( BN_STRUCT* pBNS, int bUnknAltAsNoStereo ) +{ + AT_NUMB *nStackAtom = NULL; + int nTopStackAtom; + AT_NUMB *nRingStack = NULL; + int nTopRingStack; /* was AT_NUMB */ + AT_NUMB *nBondStack = NULL; + int nTopBondStack; + AT_NUMB *nDfsNumber = NULL; + AT_NUMB *nLowNumber = NULL; + S_CHAR *cNeighNumb = NULL; + AT_NUMB nDfs; + AT_NUMB nNumAtInRingSystem; + int i, j, u, w, start, nNumRingSystems, nNumStartChildren; + BNS_VERTEX *at = pBNS->vert; + BNS_EDGE *bond = pBNS->edge; + int num_atoms = pBNS->num_atoms; + int num_edges = pBNS->num_bonds; + + /* allocate arrays */ + nStackAtom = (AT_NUMB *)inchi_malloc( num_atoms*sizeof(nStackAtom[0])); + nRingStack = (AT_NUMB *)inchi_malloc( num_atoms*sizeof(nRingStack[0])); + nDfsNumber = (AT_NUMB *)inchi_malloc( num_atoms*sizeof(nDfsNumber[0])); + nLowNumber = (AT_NUMB *)inchi_malloc( num_atoms*sizeof(nLowNumber[0])); + nBondStack = num_edges? ((AT_NUMB *) inchi_malloc( num_edges*sizeof(nBondStack[0]))) : (AT_NUMB *)(NULL); /* special case: no bonds 2006-03 */ + cNeighNumb = (S_CHAR *)inchi_malloc( num_atoms*sizeof(cNeighNumb[0])); + /* check allocation */ + if ( !nStackAtom || !nRingStack || !nDfsNumber || !nLowNumber || !nBondStack && num_edges || !cNeighNumb + ) { + nNumRingSystems = CT_OUT_OF_RAM; /* program error */ /* */ + goto exit_function; + } + + /******************************************** + * + * Find Cut-vertices & Blocks + * + * 1\ /5 has 3 blocks (maximal subgraphs that + * Example: | >3--4< | are nonseparable by deleting a single vertex): + * 2/ \6 (1,2,3, has 3 bonds), (3,4, has 1 bond), and (4,5,6, has 3 bonds) + * + * Cut-vertices or articulation points are + * intersections of the blocks: points 3 and 4. + ********************************************/ + + /******************************************************** + + RingSystemAlt are atoms connected by alternating bonds + (as must be indicated in bIsAltBond()): + + BOND_ALTERN + BOND_ALT_123 + BOND_ALT_13 + BOND_ALT_23 + + Since other bonds may be present, we possibly need + to restart to move to another component + *********************************************************/ + + nNumRingSystems = 0; + memset( nDfsNumber, 0, num_atoms*sizeof(nDfsNumber[0])); + + for ( start = 0; start < num_atoms; start ++ ) { + if ( nDfsNumber[start] ) + continue; + for ( i = 0; i < at[start].valenceAltBns; i ++ ) { + if ( bond[at[start].iedge[i]].nBondTypeInpAltBns & BT_ALTERN_BOND ) + goto found_alt; + } + continue; + +found_alt: + + + /* initiation */ + u = start; /* start atom */ + nDfs = 0; + nTopStackAtom =-1; + nTopRingStack =-1; + nTopBondStack =-1; + memset( cNeighNumb, 0, num_atoms*sizeof(cNeighNumb[0])); + /* push the start atom on the stack */ + nLowNumber[u] = nDfsNumber[u] = ++nDfs; + nStackAtom[++nTopStackAtom] = (AT_NUMB)u; + nRingStack[++nTopRingStack] = (AT_NUMB)u; + + nNumStartChildren = 0; + + do { + /* advance */ + /*while ( (int)at[i=nStackAtom[nTopStackAtom]].valenceAltBns > (j = (int)cNeighNumb[i]) )*/ + /* replaced due to missing sequence point */ + while ( i=(int)nStackAtom[nTopStackAtom], j = (int)cNeighNumb[i], (int)at[i].valenceAltBns > j ) + { + cNeighNumb[i] ++; + if ( !(bond[w=at[i].iedge[j]].nBondTypeInpAltBns & BT_ALT_BOND_MASK) ) { + continue; + } + u = (int)(bond[at[i].iedge[j]].neighbor12 ^ i); + if ( !nDfsNumber[u] ) { + /* tree edge, 1st visit -- advance */ + nStackAtom[++nTopStackAtom] = (AT_NUMB)u; + nRingStack[++nTopRingStack] = (AT_NUMB)u; + nBondStack[++nTopBondStack] = (AT_NUMB)w; + nLowNumber[u] = nDfsNumber[u] = ++nDfs; + nNumStartChildren += (i == start); + } else + if ( !nTopStackAtom || u != (int)nStackAtom[nTopStackAtom-1] ) { /* may comment out ? */ + /* back edge: u is not a predecessor of i */ + if ( nDfsNumber[u] < nDfsNumber[i] ) { + /* Back edge, 1st visit: u is ancestor of i. Save and compare */ + nBondStack[++nTopBondStack] = (AT_NUMB)w; + if ( nLowNumber[i] > nDfsNumber[u] ) { + nLowNumber[i] = nDfsNumber[u]; + } + } + } + } + cNeighNumb[i] = 0; + + /* back up */ + if ( i != start ) { + u = (int)nStackAtom[nTopStackAtom-1]; /* predecessor of i */ + if ( nLowNumber[i] >= nDfsNumber[u] ) { + /* output the block; the block was entered through its first bond u->i */ + nNumRingSystems ++; + /*at[u].nBlockSystemAltBns = nNumRingSystems;*/ /* mark the atom */ + nNumAtInRingSystem = 1; + /* + if ( u != start || nNumStartChildren > 1 ) { + at[u].bCutVertexAltBns += 1; // mark cut-vertex (articulation point) + } + */ + while ( nTopRingStack >= 0 ) { + j = nRingStack[nTopRingStack--]; + /*at[j].nBlockSystemAltBns = nNumRingSystems;*/ /* mark the atom */ + nNumAtInRingSystem ++; + if ( i == j ) { + break; + } + } + while ( nTopBondStack >= 0 ) { + w = nBondStack[nTopBondStack--]; + bond[w].nBlockNumberAltBns = nNumRingSystems; /* mark the bond */ + bond[w].nNumAtInBlockAltBns = nNumAtInRingSystem; + if ( i == bond[w].neighbor1 && u == (i ^ bond[w].neighbor12) || + u == bond[w].neighbor1 && i == (u ^ bond[w].neighbor12)) { + break; + } + } + } else + if ( nLowNumber[u] > nLowNumber[i] ) { + /* inherit */ + nLowNumber[u] = nLowNumber[i]; + } + } + } while ( --nTopStackAtom >= 0 ); + } + +#if ( BNS_MARK_ONLY_BLOCKS != 1 ) /* { */ + + /******************************************** + * + * Find Ring Systems + * Including chain atoms X: A-X-B, where the bonds (of any kind) are bridges. + * + ********************************************/ + + /* initiation */ + + nNumRingSystems = 0; + + for ( start = 0; start < num_atoms; start ++ ) { + if ( at[start].nRingSystemAltBns ) + continue; + for ( i = 0; i < at[start].valenceAltBns; i ++ ) { + if ( bond[at[start].iedge[i]].nBondTypeInpAltBns & BT_ALT_BOND_MASK ) + goto found_alt2; + } + continue; + +found_alt2: + + u = start; /* start atom */ + nDfs = 0; + nTopStackAtom =-1; + nTopRingStack =-1; + memset( nDfsNumber, 0, num_atoms*sizeof(nDfsNumber[0])); + memset( cNeighNumb, 0, num_atoms*sizeof(cNeighNumb[0])); + /* push the start atom on the stack */ + nLowNumber[u] = nDfsNumber[u] = ++nDfs; + nStackAtom[++nTopStackAtom] = (AT_NUMB)u; + nRingStack[++nTopRingStack] = (AT_NUMB)u; + + do { + /* advance */ +advance_ring: + /*if ( (int)at[i=nStackAtom[nTopStackAtom]].valenceAltBns > (j = (int)cNeighNumb[i]) )*/ + /* replaced due to missing sequence point */ + if ( i=(int)nStackAtom[nTopStackAtom], j = (int)cNeighNumb[i], (int)at[i].valenceAltBns > j ) + { + cNeighNumb[i] ++; + if ( !(bond[at[i].iedge[j]].nBondTypeInpAltBns & BT_ALTERN_BOND) ) { + goto advance_ring; + } + u = (int)(bond[at[i].iedge[j]].neighbor12 ^ i); + if ( !nDfsNumber[u] ) { + /* tree edge, 1st visit -- advance */ + nStackAtom[++nTopStackAtom] = (AT_NUMB)u; + nRingStack[++nTopRingStack] = (AT_NUMB)u; + nLowNumber[u] = nDfsNumber[u] = ++nDfs; + } else + if ( !nTopStackAtom || u != (int)nStackAtom[nTopStackAtom-1] ) { + /* back edge: u is not a predecessor of i */ + if ( nDfsNumber[u] < nDfsNumber[i] ) { + /* Back edge, 1st visit: u is ancestor of i. Compare */ + if ( nLowNumber[i] > nDfsNumber[u] ) { + nLowNumber[i] = nDfsNumber[u]; + } + } + } + goto advance_ring; + } else { + cNeighNumb[i] = 0; + } + + /* back up */ + if ( nDfsNumber[i] == nLowNumber[i] ) { + /* found a ring system */ + nNumRingSystems ++; + + /* unwind nRingStack[] down to i */ + + /* count atoms in a ring system */ + for ( nNumAtInRingSystem = 0, j = nTopRingStack; 0 <= j; j -- ) { + nNumAtInRingSystem ++; + if ( i == (int)nRingStack[j] ) { + break; + } + } + while ( nTopRingStack >= 0 ) { + j = (int)nRingStack[nTopRingStack--]; + at[j].nRingSystemAltBns = (AT_NUMB)nNumRingSystems; /* ring system id */ + at[j].nNumAtInRingSystemAltBns = nNumAtInRingSystem; + if ( i == j ) { + /* reached atom on the top of nStackAtom[] stack */ + break; + } + } + } else + if ( nTopStackAtom > 0 ) { + j = (int)nStackAtom[nTopStackAtom-1]; + /* inherit nLowNumber */ + if ( nLowNumber[j] > nLowNumber[i] ) { + nLowNumber[j] = nLowNumber[i]; + } + } + } while ( --nTopStackAtom >= 0 ); + } + +#endif /* } BNS_MARK_ONLY_BLOCKS != 1 */ + +exit_function: + if ( nStackAtom ) + inchi_free( nStackAtom ); + if ( nRingStack ) + inchi_free( nRingStack ); + if ( nDfsNumber ) + inchi_free( nDfsNumber ); + if ( nLowNumber ) + inchi_free( nLowNumber ); + if ( nBondStack ) + inchi_free( nBondStack ); + if ( cNeighNumb ) + inchi_free( cNeighNumb ); + return nNumRingSystems; +} + + +/* + ReInitBnStructForAltBns +*/ +int ReInitBnStructForAltBns( BN_STRUCT *pBNS, + inp_ATOM *at, + int num_atoms, + int bUnknAltAsNoStereo ) +{ + Vertex v, v2; + int ret, bond_type, num_to_test, j; + BNS_EDGE *pBond; + BNS_VERTEX *pAtom; + /* strip all t-groups and c-groups */ + num_to_test = 0; + if ( bUnknAltAsNoStereo ) { + for ( j = 0; j < pBNS->num_edges; j ++ ) { + pBNS->edge[j].pass = 0; + } + } + ret = ReInitBnStruct( pBNS, at, num_atoms, 0 ); + if ( ret || pBNS->num_atoms != num_atoms || pBNS->num_vertices != num_atoms || pBNS->num_bonds != pBNS->num_edges ) { + ret = BNS_REINIT_ERR; + goto exit_function; + } + /* eliminate bonds and fix st-caps */ + for ( v = 0; v < num_atoms; v ++ ) { + pAtom = pBNS->vert + v; + for ( j = 0; j < pAtom->valenceAltBns; j ++ ) { + pBond = pBNS->edge + pAtom->iedge[j]; + if ( pBond->neighbor1 == v ) { + bond_type = (at[v].bond_type[j] & BOND_TYPE_MASK); + v2 = pBond->neighbor12 ^ v; + if ( at[v].endpoint || at[v2].endpoint ) { + bond_type = 0; /* any bond to an endpoint considered non-stereogenic */ + } +#if ( FIX_EITHER_DB_AS_NONSTEREO == 1 ) + if ( bUnknAltAsNoStereo ) { + if ( bond_type == BOND_ALTERN && at[v].bond_stereo[j] == STEREO_DBLE_EITHER ) { + bond_type = 0; /* treat unknown (Either) ALT bond as non-stereo */ + } + } +#endif + switch ( bond_type ) { + + case BOND_ALTERN : + pBond->nBondTypeInpAltBns = BT_ALTERN_BOND; + num_to_test ++; + break; + + case BOND_ALT_123: + case BOND_ALT_13 : + case BOND_ALT_23 : + pBond->nBondTypeInpAltBns = BT_OTHER_ALTERN_BOND; + break; + + case BOND_TAUTOM : + pBond->nBondTypeInpAltBns = BT_TAUTOM_BOND; + break; + + case BOND_ALT12NS: + pBond->nBondTypeInpAltBns = BT_ALTERN_NS_BOND; + break; + + case 0: + case BOND_SINGLE : + case BOND_DOUBLE : + case BOND_TRIPLE : + pBond->nBondTypeInpAltBns = BT_IGNORE_BOND; + break; + + default: + pBond->nBondTypeInpAltBns = BT_IGNORE_BOND; + break; + } + pBond->nBondNonStereoAltBns = + pBond->nBlockNumberAltBns = + pBond->nNumAtInBlockAltBns = 0; + +#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) + pBond->forbidden &= pBNS->edge_forbidden_mask; +#endif + } + } + pAtom->bCutVertexAltBns = + pAtom->nRingSystemAltBns = + pAtom->nNumAtInRingSystemAltBns = + pAtom->nBlockSystemAltBns = 0; + } + + return num_to_test; +exit_function: + return ret; +} + + +/* + MarkNonStereoAltBns +*/ +int MarkNonStereoAltBns( BN_STRUCT *pBNS, + inp_ATOM *at, + int num_atoms, + int bUnknAltAsNoStereo ) +{ + int num_bonds = pBNS->num_bonds; + int ret; + int ibond, ib1, ib2; + BNS_EDGE *pBond; + Vertex iat1, iat2; + + ret = 0; + + if ( pBNS->num_atoms != num_atoms || pBNS->num_vertices != num_atoms || pBNS->num_bonds != pBNS->num_edges ) { + ret = BNS_REINIT_ERR; + goto exit_function; + } + if ( bUnknAltAsNoStereo ) { + for ( ibond=0; ibond < num_bonds; ibond ++ ) { + pBond = pBNS->edge + ibond; + if ( pBond->nBondTypeInpAltBns != BT_ALTERN_BOND && pBond->nBondTypeInpAltBns != BT_IGNORE_BOND ) { + continue; + } + iat1 = pBond->neighbor1; + iat2 = pBond->neighbor12 ^ iat1; + ib1 = pBond->neigh_ord[0]; + ib2 = pBond->neigh_ord[1]; + if ( /* alt bond non-adjacent to a taut. endpoint: */ + (pBond->nBondTypeInpAltBns == BT_ALTERN_BOND && + pBond->nNumAtInBlockAltBns <= 3 ) /* non-ring bond */ || + /* alt bond adjacent to a taut. endpoint: */ + (pBond->nBondTypeInpAltBns == BT_IGNORE_BOND && + (at[iat1].bond_type[ib1] & BOND_TYPE_MASK) == BOND_ALTERN ) + ) { + if ( (at[iat1].bond_type[ib1] & BOND_TYPE_MASK) == BOND_ALTERN ) { + /* bond_type = BOND_ALT12NS; */ + at[iat1].bond_stereo[ib1] = + at[iat2].bond_stereo[ib2] =STEREO_DBLE_EITHER; + ret ++; + } + } + } + } else { + for ( ibond=0; ibond < num_bonds; ibond ++ ) { + pBond = pBNS->edge + ibond; + if ( pBond->nBondTypeInpAltBns != BT_ALTERN_BOND && pBond->nBondTypeInpAltBns != BT_IGNORE_BOND ) { + continue; + } + iat1 = pBond->neighbor1; + iat2 = pBond->neighbor12 ^ iat1; + ib1 = pBond->neigh_ord[0]; + ib2 = pBond->neigh_ord[1]; + if ( /* alt bond non-adjacent to a taut. endpoint: */ + (pBond->nBondTypeInpAltBns == BT_ALTERN_BOND && + pBond->nNumAtInBlockAltBns <= 3 ) /* non-ring bond */ || + /* alt bond adjacent to a taut. endpoint: */ + (pBond->nBondTypeInpAltBns == BT_IGNORE_BOND && + (at[iat1].bond_type[ib1] & BOND_TYPE_MASK) == BOND_ALTERN ) + ) + { + at[iat1].bond_type[ib1] = + at[iat2].bond_type[ib2] =BOND_ALT12NS; + ret ++; + } + } + } + +exit_function: + + return ret; +} + +#if ( READ_INCHI_STRING == 1 ) +/*****************************************************************************/ +#ifndef RI_ERR_ALLOC +/* from ichirvrs.h */ +#define RI_ERR_ALLOC (-1) +#define RI_ERR_SYNTAX (-2) +#define RI_ERR_PROGR (-3) +#endif + + +/* Check if atom bonded to charged atom */ +int bHasChargedNeighbor( inp_ATOM *at, int iat ) +{ + int i; + for( i = 0; i < at[iat].valence; i ++ ) { + if ( at[(int)at[iat].neighbor[i]].charge ) + return 1; + } + return 0; +} + + +/* + Add or remove protons + + *num_protons_to_add = nToBeRemovedByNormFromRevrs + + nToBeRemovedByNormFromRevrs > 0: less protons should be allowed to be + added by the Normalization of the Reconstructed Structure + nToBeRemovedByNormFromRevrs < 0: prepare more H(+) to be removed by + the InChI Normalization of the Reconstructed Structure + + OrigStruct -> NormOrig + n(orig)*H(+) + RevrStruct -> NormRevr + n(revr)*H(+) + nToBeRemovedByNormFromRevrs = n(orig) - n(revr) [each may be negative] + + n(orig) > n(revr) or nToBeRemovedByNormFromRevrs > 0 means: + ----------------------------------------------------------- + - Too many protons were added by the Normalization to the Reconstructed Structure + (a) n(revr) < 0 => protons were added while they should not have been added; + Solution: "neutralize" (-) charged proton acceptors by moving charges to other atoms + on the condition ADP cannot add in another way; + (b) n(orig) > n(revr) => 0 => too few protons were removed + Solution: (the easiest) attach H(+) to =O or -N< or -N= + Solution: move (+) from N or OH to an atom adjacent to (-) charge or to + an atom that is not N. + + n(orig) < n(revr) or nToBeRemovedByNormFromRevrs < 0 means: + ----------------------------------------------------------- + - Too few protons were added by the Normalization to the Reconstructed Stucture + (a) n(orig) < 0 => protons were not added while they should have been added; + Solution: move (-) to O by replacing =O with -O(-) + (b) 0 <= n(orig) < n(revr) => too many protons were removed + + Note: it is critically important to takr into account cumbersome Normalization + Total Charge: if it is >= 0 then no H(+) may be removed from -OH or by ADP + However, if N(+) is present then ADP will always try to remove a proton +*/ +int AddRemoveProtonsRestr( inp_ATOM *at, + int num_atoms, + int *num_protons_to_add, + int nNumProtAddedByRestr, + INCHI_MODE bNormalizationFlags, + int num_tg, + int nChargeRevrs, + int nChargeInChI ) +{ + int i, j, ret = 0; + int nAtTypeTotals[ATTOT_ARRAY_LEN]; + int num_prot = *num_protons_to_add; + int type, mask, bSuccess, nTotCharge, nNumSuccess = 0; + int max_j_Aa=-1, max_j_Ar=-1; + +/* for the reference: + +#define FLAG_NORM_CONSIDER_TAUT ( FLAG_PROTON_NPO_SIMPLE_REMOVED | \ + FLAG_PROTON_NP_HARD_REMOVED | \ + FLAG_PROTON_AC_SIMPLE_ADDED | \ + FLAG_PROTON_AC_SIMPLE_REMOVED | \ + FLAG_PROTON_AC_HARD_REMOVED | \ + FLAG_PROTON_AC_HARD_ADDED | \ + FLAG_PROTON_SINGLE_REMOVED | \ + FLAG_PROTON_CHARGE_CANCEL ) + +#define FLAG_FORCE_SALT_TAUT ( FLAG_PROTON_NP_HARD_REMOVED | \ + FLAG_PROTON_AC_HARD_REMOVED | \ + FLAG_PROTON_AC_HARD_ADDED ) + +*/ + /* if ChargeRevrs > nChargeInChI then we should prevent proton addition or facilitate proton removal + a typical case is (=) on N or O instead of C(-) + + if ChargeRevrs < nChargeInChI then we should prevent proton removal or facilitate proton addition + */ + + mark_at_type( at, num_atoms, nAtTypeTotals ); + for ( i = nTotCharge = 0; i < num_atoms; i ++ ) { + nTotCharge += at[i].charge; + } + /* size for SimpleAddAcidicProtons() */ + for ( max_j_Aa = 0; AaTypMask[2*max_j_Aa]; max_j_Aa ++ ) + ; + /* size for SimpleRemoveAcidicProtons */ + for ( max_j_Ar = 0; ArTypMask[2*max_j_Ar]; max_j_Ar ++ ) + ; + if ( num_prot < 0 && nAtTypeTotals[ATTOT_TOT_CHARGE]-nNumProtAddedByRestr <= 0 ) { + /* remove proton(s) */ + /* use test from SimpleAddAcidicProtons() to test whether removal of H(+) from =C-OH, etc. is correct */ + for ( i = 0; i < num_atoms && num_prot; i ++ ) { + /* choose an atom */ + if ( at[i].sb_parity[0] || at[i].p_parity || at[i].charge || + !at[i].num_H || at[i].radical || bHasChargedNeighbor( at, i ) ) { + continue; + } + /* try to remove a proton and check whether InChI would add it back */ + at[i].charge --; + at[i].num_H --; + type = GetAtomChargeType( at, i, NULL, &mask, 0 ); + at[i].charge ++; + at[i].num_H ++; + + if ( type ) { + for ( bSuccess = 0, j = 0; j < max_j_Aa; j ++ ) { + if ( bSuccess = (type & AaTypMask[2*j]) && (mask && AaTypMask[2*j+1]) ) { + break; /* the proton may be added to this atom */ + } + } + if ( bSuccess ) { + type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ + at[i].charge --; + at[i].num_H --; + type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ + num_prot ++; /* success */ + nNumSuccess ++; + } + } + } + } + if ( num_prot < 0 && num_tg && nAtTypeTotals[ATTOT_TOT_CHARGE]-nNumProtAddedByRestr <= 0 ) { + /* alternative proton removal: O=C-NH => (-)O-C=N, O and N are taut. endpoints */ + int endp2, centp, k, i0, k0; + for ( i = 0; i < num_atoms; i ++ ) { + /* choose an atom */ + if ( !at[i].endpoint || at[i].sb_parity[0] || at[i].p_parity || + at[i].radical || at[i].charge || bHasChargedNeighbor( at, i ) ) { + continue; + } + /* looking for tautomeric =O */ + if ( 1 != at[i].valence || BOND_TYPE_DOUBLE != at[i].bond_type[0] || at[i].num_H || + 2 != get_endpoint_valence( at[i].el_number ) ) { + continue; + } + centp = at[i].neighbor[0]; + if ( at[centp].sb_parity[0] || at[centp].p_parity || !is_centerpoint_elem( at[centp].el_number ) ) { + continue; + } + /* found a possible centerpoint, looking for -NH endpoint */ + for ( k = 0; k < at[centp].valence; k ++ ) { + if ( at[centp].bond_type[k] != BOND_TYPE_SINGLE ) { + continue; + } + endp2 = at[centp].neighbor[k]; + if ( at[endp2].endpoint != at[i].endpoint || + !at[endp2].num_H || at[endp2].charge || + at[endp2].sb_parity[0] || at[endp2].p_parity || + at[endp2].valence != at[endp2].chem_bonds_valence || + 3 != at[endp2].chem_bonds_valence + at[endp2].num_H || + 3 != get_endpoint_valence( at[endp2].el_number ) ) { + continue; + } + /* find bonds in reciprocal ajacency lists */ + for ( i0 = 0; i0 < at[centp].valence && i != at[centp].neighbor[i0]; i0 ++ ) + ; + for ( k0 = 0; k0 < at[endp2].valence && centp != at[endp2].neighbor[k0]; k0 ++ ) + ; + if ( i0 == at[centp].valence || k0 == at[endp2].valence ) { + return RI_ERR_PROGR; + } + /* -NH has been found */ + type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ + type = GetAtomChargeType( at, endp2, nAtTypeTotals, &mask, 1 ); /* subtract at[endp2] */ + + at[i].bond_type[0] --; + at[centp].bond_type[i0] --; + at[i].chem_bonds_valence --; + at[i].charge --; + + at[endp2].bond_type[k0] ++; + at[centp].bond_type[k] ++; + at[endp2].chem_bonds_valence ++; + at[endp2].num_H --; + + num_prot ++; + nNumSuccess ++; + + type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add at[i] */ + type = GetAtomChargeType( at, endp2, nAtTypeTotals, &mask, 0 ); /* add at[endp2] */ + } + } + } + if ( num_prot > 0 ) { + /* add protons */ + /* 1. Use test from SimpleRemoveAcidicProtons() to test whether addition of H(+) to =C-O(-), etc. is correct */ + for ( i = 0; i < num_atoms && num_prot && nAtTypeTotals[ATTOT_TOT_CHARGE]-nNumProtAddedByRestr >= 0; i ++ ) { + /* choose an atom */ + if ( at[i].sb_parity[0] || at[i].p_parity || at[i].num_H || + at[i].charge != -1 || at[i].radical || bHasChargedNeighbor( at, i ) ) { + continue; + } + /* try to add a proton and check whether InChI would remove it back */ + at[i].charge ++; + at[i].num_H ++; + type = GetAtomChargeType( at, i, NULL, &mask, 0 ); + at[i].charge --; + at[i].num_H --; + + if ( type ) { + for ( bSuccess = 0, j = 0; j < max_j_Ar; j ++ ) { + if ( bSuccess = (type & ArTypMask[2*j]) && (mask && ArTypMask[2*j+1]) ) { + break; + } + } + if ( bSuccess ) { + type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ + at[i].charge ++; + at[i].num_H ++; + type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ + num_prot --; /* success */ + nNumSuccess ++; + } + } + } + /* 2. Use test from SimpleRemoveHplusNPO() */ + for ( i = 0; i < num_atoms && num_prot; i ++ ) { + /* choose an atom */ + if ( at[i].sb_parity[0] || at[i].p_parity || + at[i].charge || at[i].radical || bHasChargedNeighbor( at, i ) ) { + continue; + } + /* try to add a proton and check whether InChI would remove it back */ + at[i].num_H ++; + at[i].charge ++; + bSuccess = (PR_SIMPLE_TYP & (type = GetAtomChargeType( at, i, NULL, &mask, 0 )) ) && + (PR_SIMPLE_MSK & mask ); + at[i].num_H --; /* failed */ + at[i].charge --; + if ( bSuccess ) { + type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ + at[i].num_H ++; + at[i].charge ++; + type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ + num_prot --; /* succeeded */ + nNumSuccess ++; + } + } + } + + if ( num_prot < 0 && (bNormalizationFlags & FLAG_PROTON_AC_HARD_ADDED) && 1 == num_tg && + nAtTypeTotals[ATTOT_TOT_CHARGE]-nNumProtAddedByRestr <= 0 ) { + /* try to remove protons from tautomeric N (specific ADP must be present) */ + int nNumAcceptors_DB_O=0, nNumDonors_SB_NH=0, num_max, num_success; + for ( i = 0; i < num_atoms; i ++ ) { + /* choose an atom */ + if ( !at[i].endpoint || at[i].radical || + at[i].sb_parity[0] || at[i].p_parity || bHasChargedNeighbor( at, i ) ) { + continue; + } + type = GetAtomChargeType( at, i, NULL, &mask, 0 ); + if ( (type & AA_HARD_TYP_CO) && (mask & AA_HARD_MSK_CO) ) { + nNumAcceptors_DB_O ++; + } else + if ( (type == ATT_ATOM_N ) && (mask == ATBIT_NP_H) && !at[i].charge && + at[i].valence == at[i].chem_bonds_valence ) { + nNumDonors_SB_NH ++; + } + } + num_max = inchi_min( nNumAcceptors_DB_O, nNumDonors_SB_NH ); + for ( i = 0, num_success = 0; i < num_atoms && num_success < num_max && num_prot < 0; i ++ ) { + /* choose an atom */ + if ( !at[i].endpoint|| at[i].radical || at[i].sb_parity[0] || + at[i].p_parity || bHasChargedNeighbor( at, i ) ) { + continue; + } + type = GetAtomChargeType( at, i, NULL, &mask, 0 ); + if ( (type == ATT_ATOM_N ) && (mask == ATBIT_NP_H) && !at[i].charge && + at[i].valence == at[i].chem_bonds_valence ) { + type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 1 ); /* subtract at[i] */ + at[i].num_H --; + at[i].charge --; + type = GetAtomChargeType( at, i, nAtTypeTotals, &mask, 0 ); /* add changed at[i] */ + num_prot ++; + num_success ++; + nNumSuccess ++; + } + } + } +/*exit_function:*/ + *num_protons_to_add = num_prot; + return ret<0? ret : nNumSuccess; +} + + +/* + Add or remove isotopic protons +*/ +int AddRemoveIsoProtonsRestr( inp_ATOM *at, + int num_atoms, + NUM_H num_protons_to_add[], + int num_tg ) +{ + int i, j, k, n, ret = 0; + int nNumSuccess = 0, min_at, max_at, num_H, num_iso_H, num_expl_H, num_expl_iso_H; + int iCurIso; /* 0=> 1H, 1=> D, 2=> T */ + int iCurMode, iCurMode1, iCurMode2; /* 0=> Not Endpoints, 1=> Endpoints */ + static U_CHAR el_number_H = 0; + + /* distribute isotopes from heaviest to lightest; pick up atoms in order 1. Not endpoints; 2. Endpoints */ + iCurMode1 = 0; + iCurMode2 = num_tg ? 1 : 0; + if ( !el_number_H ) { + el_number_H = (U_CHAR) get_periodic_table_number( "H" ); + } + for ( iCurMode = iCurMode1; iCurMode <= iCurMode2; iCurMode ++ ) { + for ( iCurIso = 2; 0 <= iCurIso; iCurIso -- ) { + /* check for isotopic H to add */ + if ( !num_protons_to_add[iCurIso] ) { + continue; + } + if ( 0 > num_protons_to_add[iCurIso] ) { + ret = RI_ERR_PROGR; + goto exit_function; + } + /* limits for atom scanning */ + min_at = 0; + max_at = num_atoms; + /* cycle withio the limits */ + for ( i = min_at; i < max_at && 0 < num_protons_to_add[iCurIso]; i ++ ) { + /* pick an atom */ + if ( iCurMode ) { + if ( at[i].endpoint ) + j = i; /* atom number */ + else + continue; + } else + if ( !at[i].endpoint && + 1 == bHeteroAtomMayHaveXchgIsoH( at, i ) ) { /* atom number */ + j = i; + } else + if ( at[i].el_number == el_number_H && at[i].charge == 1 && + !at[i].valence && !at[i].radical && !at[i].iso_atw_diff ) { + /* proton, not isotopic; make it isotopic */ + at[i].iso_atw_diff = 1 + iCurIso; + num_protons_to_add[iCurIso] --; + nNumSuccess ++; + continue; + } else { + continue; + } + /* j is the atom number */ + /* count implicit H */ + num_H = at[j].num_H; + num_iso_H = NUM_ISO_H(at,j); + while ( num_H > 0 && num_protons_to_add[iCurIso] > 0 ) { + /* substitute one implicit H with an isotopic atom H */ + at[j].num_iso_H[iCurIso] ++; + at[j].num_H --; + num_protons_to_add[iCurIso] --; + num_H --; + num_iso_H ++; + nNumSuccess ++; + } + /* count explicit H */ + num_expl_H = num_expl_iso_H = 0; + for ( k = 0; k < at[j].valence && num_atoms <= (n=at[j].neighbor[k]); k ++ ) { + num_expl_H += (0 == at[n].iso_atw_diff); + num_expl_iso_H += (0 != at[n].iso_atw_diff); + } + while ( num_expl_H > 0 && num_protons_to_add[iCurIso] > 0 ) { + /* substitute one explicit H with an isotopic atom H */ + n = at[j].neighbor[num_expl_H]; + if ( at[n].iso_atw_diff ) { + ret = RI_ERR_PROGR; + goto exit_function; + } + at[n].iso_atw_diff = 1 + iCurIso; + num_expl_H --; + num_expl_iso_H ++; + num_protons_to_add[iCurIso] --; + nNumSuccess ++; + } + } + } + } + +exit_function: + return + ret < 0 ? ret : nNumSuccess; +} + + +#endif diff --git a/INCHI-1-SRC/INCHI/common/ichi_bns.h b/INCHI-1-SRC/INCHI_BASE/src/ichi_bns.h similarity index 87% rename from INCHI-1-SRC/INCHI/common/ichi_bns.h rename to INCHI-1-SRC/INCHI_BASE/src/ichi_bns.h index aa44e90..d47c1f6 100644 --- a/INCHI-1-SRC/INCHI/common/ichi_bns.h +++ b/INCHI-1-SRC/INCHI_BASE/src/ichi_bns.h @@ -1,466 +1,483 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHI_BNS_H___ -#define __INCHI_BNS_H___ - -#define BN_MAX_ALTP 16 -/*#define MAX_VERTEX 1024*/ /* including s; if vert[] has num_vert then MAX_VERTEX has (2*num_vert+2+FIRST_INDX) elements */ - -/* forward declarations */ - -struct BalancedNetworkStructure; -struct BalancedNetworkData; -struct tagTautomerGroupsInfo; -struct tagChargeGroupsInfo; -struct BN_AtomsAtTautGroup; -struct tagSaltChargeCandidate; - -/* define BNS types */ - -typedef S_SHORT Vertex; -typedef S_SHORT EdgeIndex; -typedef S_SHORT Edge[2]; /* Edge[0] = vertex1, Edge[1] = iedge or -(1+vertex1) if vertex2 = s or t */ -typedef S_SHORT BNS_IEDGE; -typedef S_SHORT EdgeFlow; -typedef S_SHORT VertexFlow; - - -#define BNS_EDGE_FORBIDDEN_MASK 1 -#define BNS_EDGE_FORBIDDEN_TEMP 2 -#define BNS_EDGE_FORBIDDEN_TEST 4 - -/* BNS vertex types */ - -#define BNS_VERT_TYPE_ATOM 0x0001 -#define BNS_VERT_TYPE_ENDPOINT 0x0002 /* attribute */ -#define BNS_VERT_TYPE_TGROUP 0x0004 -#define BNS_VERT_TYPE_C_POINT 0x0008 -#define BNS_VERT_TYPE_C_GROUP 0x0010 -#define BNS_VERT_TYPE_SUPER_TGROUP 0x0020 -#define BNS_VERT_TYPE_TEMP 0x0040 - -#define BNS_VERT_TYPE__AUX 0x0080 /* vertex added to build charge substructures */ -#define BNS_VERT_TYPE_C_NEGATIVE 0x0100 /* negative charge group; attribute, should be used with BNS_VERT_TYPE_C_GROUP */ -#define BNS_VERT_TYPE_ACID 0x0200 /* only for this type are allowed paths: t_group-atom-c_group_neg (path_TACN) */ -#define BNS_VERT_TYPE_CARBON_GR 0x0400 /* charge of carbon atom; should be used with BNS_VT_C_POS, BNS_VT_C_NEG */ -#define BNS_VERT_TYPE_METAL_GR 0x0800 /* metal atom group; may be used alone or with BNS_VT_M_POS, BNS_VT_M_NEG */ - -#define BNS_VERT_TYPE_ANY_GROUP (BNS_VERT_TYPE_TGROUP | BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_SUPER_TGROUP) - -/* InChI->Structure */ - -#define BNS_VT_C_POS BNS_VERT_TYPE_C_GROUP /* positive charge group, heteroat */ -#define BNS_VT_C_NEG (BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE) /* negative charge group, heteroat */ -#define BNS_VT_C_POS_C (BNS_VT_C_POS | BNS_VERT_TYPE_CARBON_GR) /* positive charge group, C, Si, Ge, Sn */ -#define BNS_VT_C_NEG_C (BNS_VT_C_NEG | BNS_VERT_TYPE_CARBON_GR) /* negative charge group, C, Si, Ge, Sn */ -#define BNS_VT_C_POS_M (BNS_VT_C_POS | BNS_VERT_TYPE_METAL_GR) /* positive charge group, metal */ -#define BNS_VT_C_NEG_M (BNS_VT_C_NEG | BNS_VERT_TYPE_METAL_GR) /* negative charge group, metal */ -#define BNS_VT_M_GROUP BNS_VERT_TYPE_METAL_GR /* metal-group, flower vertex */ - -#define BNS_VT_C_POS_ALL (BNS_VERT_TYPE_SUPER_TGROUP | BNS_VERT_TYPE_C_GROUP) /* supergroup (+) */ -#define BNS_VT_C_NEG_ALL (BNS_VT_C_POS_ALL | BNS_VERT_TYPE_C_NEGATIVE) /* supergroup (-) */ - -#define BNS_VT_CHRG_STRUCT (BNS_VERT_TYPE__AUX | BNS_VERT_TYPE_TEMP) /* ChargeStruct vertex */ -#define BNS_VT_YVCONNECTOR BNS_VERT_TYPE__AUX /* group connection */ - -#define IS_BNS_VT_C_OR_CSUPER_GR(X) ((X) & BNS_VT_C_POS) -#define IS_BNS_VT_C_GR(X) (((X) & BNS_VT_C_POS_ALL) == BNS_VERT_TYPE_C_GROUP) -#define IS_BNS_VT_CM_GR(X) (((X) & BNS_VT_C_POS_M) == BNS_VT_C_POS_M) /* metal charge group */ -#define IS_BNS_VT_M_GR(X) ((X) == BNS_VERT_TYPE_METAL_GR ) /* metal flower base or vertices */ -#define IS_BNS_VT_YVCONNECTOR(X) (((X) & BNS_VERT_TYPE__AUX) && !((X) & BNS_VERT_TYPE_TEMP)) -#define IS_BNS_VT_CHRG_STRUCT(X) (((X) & BNS_VERT_TYPE__AUX) && ((X) & BNS_VERT_TYPE_TEMP)) -#define IS_BNS_VT_ATOM(X) ((X) & BNS_VERT_TYPE_ATOM) - -#define BNS_ADD_SUPER_TGROUP 1 /* reserve one more edge for a t-group to connect to a single super-t-group */ -#define NUM_KINDS_OF_GROUPS 2 /* 1 accounts for t-group kind, one more 1 accounts for c-group kind */ - -#define BNS_ADD_ATOMS 2 /* max. number of fictitious atoms to add (except t-gtoups) */ -#define BNS_ADD_EDGES 1 /* max. number of edges to add to each atom (except edges to a t-group or c-group) */ - -typedef enum tagAltPathConst { - iALTP_MAX_LEN, /* 0 */ - iALTP_FLOW, /* 1 */ - iALTP_PATH_LEN, /* 2 */ - iALTP_START_ATOM, /* 3 */ - iALTP_END_ATOM, /* 4 */ - iALTP_NEIGHBOR, /* 5 */ - iALTP_HDR_LEN = iALTP_NEIGHBOR -} ALT_CONST; - -#define ALTP_PATH_LEN(altp) (altp)[iALTP_PATH_LEN].number /* number of bonds = number of atoms-1*/ -#define ALTP_END_ATOM(altp) (altp)[iALTP_END_ATOM].number -#define ALTP_START_ATOM(altp) (altp)[iALTP_START_ATOM].number -#define ALTP_THIS_ATOM_NEIGHBOR(altp,X) (altp)[iALTP_NEIGHBOR+(X)].ineigh[0] /* 0 <= X < path_len */ -#define ALTP_NEXT_ATOM_NEIGHBOR(altp,X) (altp)[iALTP_NEIGHBOR+(X)].ineigh[1] -#define ALTP_CUR_THIS_ATOM_NEIGHBOR(altp) (altp)[iALTP_NEIGHBOR+ALTP_PATH_LEN(altp)].ineigh[0] /* 0 <= X < path_len */ -#define ALTP_CUR_NEXT_ATOM_NEIGHBOR(altp) (altp)[iALTP_NEIGHBOR+ALTP_PATH_LEN(altp)].ineigh[1] -#define ALTP_NEXT(altp) (++ALTP_PATH_LEN(altp)) -#define ALTP_PREV(altp) (--ALTP_PATH_LEN(altp)) -#define ALTP_MAY_ADD(altp) (iALTP_NEIGHBOR + (altp)[iALTP_PATH_LEN].number < (altp)[iALTP_MAX_LEN].number) -#define ALTP_ALLOCATED_LEN(altp) (altp)[iALTP_MAX_LEN].number -#define ALTP_DELTA(altp) (altp)[iALTP_FLOW].flow[0] -#define ALTP_OVERFLOW(altp) (altp)[iALTP_FLOW].flow[1] - -#define Vertex_s 0 -#define Vertex_t 1 - -#define NO_VERTEX -2 -#define BLOSSOM_BASE -1 - -#define ADD_CAPACITY_RADICAL 1 /* add capacity to radical */ - -#define MAX_BOND_EDGE_CAP 2 /* triple bond */ -#define AROM_BOND_EDGE_CAP 1 -#define MAX_TGROUP_EDGE_CAP 2 /* -NH2 provides max. capacity */ - -/* edge to s or t */ -#define EDGE_FLOW_ST_MASK 0x3fff /* mask for flow */ -#define EDGE_FLOW_ST_PATH 0x4000 /* mark: the edge belongs to the augmenting path */ - -/* edges between other vertices */ -/* EdgeFlow defined as S_SHORT; change from S_CHAR made 9-23-2005 */ -#define EDGE_FLOW_MASK 0x3fff /* mask for flow */ -#define EDGE_FLOW_PATH 0x4000 /* mark: the edge belongs to the augmenting path */ - -/*********************************************************************************/ -#if ( ADD_CAPACITY_RADICAL == 1 ) /* { */ -/* -- do not treat triplets as moving dots -- 2004-02-18 -- -#define MAX_AT_FLOW(X) (((X).chem_bonds_valence - (X).valence)+\ - ((is_centerpoint_elem((X).el_number)||get_endpoint_valence((X).el_number))?\ - (((X).radical==RADICAL_DOUBLET)+2*((X).radical==RADICAL_TRIPLET)):0)) -*/ -#define MAX_AT_FLOW(X) (((X).chem_bonds_valence - (X).valence)+\ - ((is_centerpoint_elem((X).el_number)||get_endpoint_valence((X).el_number))?\ - (((X).radical==RADICAL_DOUBLET)/*+2*((X).radical==RADICAL_TRIPLET)*/):0)) - - -#else /* } ADD_CAPACITY_RADICAL { */ - -#define MAX_AT_FLOW(X) (((X).chem_bonds_valence - (X).valence) - -#endif /* } ADD_CAPACITY_RADICAL */ - -/**************************** BNS_EDGE ************************************/ -typedef struct BnsEdge { - AT_NUMB neighbor1; /* the smaller neighbor */ - AT_NUMB neighbor12; /* neighbor1 ^ neighbor2 */ - AT_NUMB neigh_ord[2]; /* ordering number of the neighbor: [0]: atneighbor */ - EdgeFlow cap; /* Edge capacity */ - EdgeFlow cap0; /* Initial edge capacity */ - EdgeFlow flow; /* Edge flow */ - EdgeFlow flow0; /* Initial flow */ - /*S_CHAR delta; */ - S_CHAR pass; /* number of times changed in AugmentEdge() */ - S_CHAR forbidden; -} BNS_EDGE; - -/**************************** BNS_ST_EDGE ************************************/ -typedef struct BnsStEdge { - VertexFlow cap; /* Edge capacity */ - VertexFlow cap0; /* Initial edge capacity */ - VertexFlow flow; /* Edge flow */ - VertexFlow flow0; /* Initial edge flow */ - S_CHAR pass; /* number of times changed in AugmentEdge() */ - /*S_CHAR delta; */ -} BNS_ST_EDGE; - -/**************************** BNS_VERTEX ************************************/ -typedef struct BnsVertex { - - BNS_ST_EDGE st_edge; /* 0,1 capacity and flow of the edge to s or t */ - AT_NUMB type; /* 2, atom, t-group, or added atom: BNS_VERT_TYPE_TGROUP, etc. */ - AT_NUMB num_adj_edges; /* 3, actual number of neighbors incl. t-groups, excl. s or t */ - AT_NUMB max_adj_edges; /* 4, including reserved */ - /*S_CHAR path_neigh[2];*/ /* 5 found path information */ - /* indexes of Edges */ - BNS_IEDGE *iedge; /* 6 a pointer to the array of edge indexes adjacent to this vertex */ -}BNS_VERTEX; - -/**************************** BNS_ALT_PATH ************************************/ -typedef union BnsAltPath { - VertexFlow flow[2]; - Vertex number; - AT_NUMB ineigh[2]; -} BNS_ALT_PATH; - -/**************************** BN_STRUCT ************************************/ -typedef struct BalancedNetworkStructure { - - int num_atoms; /* number of real atoms */ - /*int len_atoms; */ /* size of filled with real atoms portion of the BNS_VERTEX data */ - int num_added_atoms; /* number of added fictitious atoms */ - int nMaxAddAtoms; /* max. number of atoms to add (not including t-groups) */ - int num_c_groups; /* number of added c-groups */ - int num_t_groups; /* number of added t-groups */ - int num_vertices; /* total number currently in effect; includes t-groups and added atoms */ - /*int len_vertices; */ /* allocation size for BNS_VERTEX data */ - int num_bonds; /* number of real bonds/2 = number of edges between real atoms */ - int num_edges; /* number of currently in effect */ - int num_iedges; /* added 9-16-2005; used only in InChI Reversing */ - int num_added_edges; /* number of added edges (not including edges to t-groups) */ - int nMaxAddEdges; /* max. number edges of add to each atom (not including edges to t-groups) */ - - int max_vertices; /* allocation size for BNS_VERTEX structures */ - int max_edges; /* allocation size for edge[]; iedge has length 2*max_edges */ - int max_iedges; /* allocation size for iedge */ - - int tot_st_cap; /* not used, only calculated */ - int tot_st_flow; /* not used, only calculated */ - - int len_alt_path; /* length of alt_path[] */ - - int bNotASimplePath; /* alternating path traversed same bond 2 times in opposite directions */ - int bChangeFlow; /* actually change flow */ - - BNS_VERTEX *vert; /* vertices */ - BNS_EDGE *edge; /* edges */ - BNS_IEDGE *iedge; - BNS_ALT_PATH *alt_path; /* current altp[] element */ - BNS_ALT_PATH *altp[BN_MAX_ALTP]; /* keep alt. paths */ - - int max_altp; - int num_altp; - - INCHI_MODE *pbTautFlags; /* carry it through all functions; never NULL */ - INCHI_MODE *pbTautFlagsDone; /* carry it through all functions; never NULL */ - AT_NUMB type_TACN; /* BNS_VERT_TYPE_ACID: if non-zero than only for it path type_T-type_TACN-type_CN allowed */ - AT_NUMB type_T; /* BNS_VERT_TYPE_TGROUP */ - AT_NUMB type_CN; /* BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE */ - S_CHAR edge_forbidden_mask; - -} BN_STRUCT; - -/********************* BN_DATA *******************************************/ -typedef enum tagBnsRadSrchMode { - RAD_SRCH_NORM = 0, /* normal search for normalization */ - RAD_SRCH_FROM_FICT = 1 /* search from fict. vertices to atoms */ -} BRS_MODE; -typedef struct BalancedNetworkData { - Vertex *BasePtr; /*[MAX_VERTEX]; pointer toward the base of C(v) */ - Edge *SwitchEdge; /*[MAX_VERTEX]; a pair of vertices and an edge, implemented here as [*][2] array */ - S_CHAR *Tree; /*[MAX_VERTEX]; indicates presence in ScanQ, T, T', s-reachability */ - Vertex *ScanQ; /*[MAX_VERTEX]; contains the set S of s-reachable vertices */ - int QSize; /* index of the last element added to ScanQ */ - Vertex *Pu; /*[MAX_VERTEX/2+1] */ - Vertex *Pv; /*[MAX_VERTEX/2+1] */ - int max_num_vertices; /* allocation size of all except Pu, Pv */ - int max_len_Pu_Pv; /* allocation size of Pu and Pv */ -#if ( BNS_RAD_SEARCH == 1 ) - Vertex *RadEndpoints; /*[MAX_VERTEX*/ - int nNumRadEndpoints; - EdgeIndex *RadEdges; - int nNumRadEdges; - int nNumRadicals; - BRS_MODE bRadSrchMode; /* 1 => connect fict. vertices-radicals to the accessible atoms */ -#endif -} BN_DATA; - -/* internal array size */ -#define MAX_ALT_AATG_ARRAY_LEN 127 -/* detected endpoint markings */ -#define AATG_MARK_IN_PATH 1 /* atom in path detected by the BNS */ -#define AATG_MARK_WAS_IN_PATH 2 /* found to be in path before next level */ -/* output */ -#define AATG_MARK_MAIN_TYPE 4 /* atom O-"salt" */ -#define AATG_MARK_OTHER_TYPE 8 /* other atom to be tested */ - -struct tagTautomerGroupsInfo; /* forward declaration */ - -/******************** atoms in alt path through taut group ****************/ -typedef struct BN_AtomsAtTautGroup { - int nAllocLen; - int nNumFound; - int nNumMainAdj2Tgroup; - int nNumOthersAdj2Tgroup; - AT_NUMB *nEndPoint; /* original t-group number */ - S_CHAR *nMarkedAtom; /* atom mark, see AATG_MARK_* */ - int *nAtTypeTotals; - struct tagTautomerGroupsInfo *t_group_info; -} BN_AATG; - - -/************ store changes in flow and capacity to test a bond ****************/ - -typedef struct tagBNS_FLOW_CHANGES { - BNS_IEDGE iedge; - EdgeFlow flow; - EdgeFlow cap; - Vertex v1; - VertexFlow cap_st1; - VertexFlow flow_st1; - Vertex v2; - VertexFlow cap_st2; - VertexFlow flow_st2; -} BNS_FLOW_CHANGES; - - -#define ALT_PATH_MODE_TAUTOM 1 -#define ALT_PATH_MODE_CHARGE 2 -#define ALT_PATH_MODE_4_SALT 3 /* mark alt bonds along the path */ -#define ALT_PATH_MODE_4_SALT2 4 /* mark alt bonds along the path, path to taut. group fict. vertex if exists */ -#define ALT_PATH_MODE_REM2H_CHG 5 /* remove 2 H along alt. path AH-=-BH => A=-=B and change bonds to alternating */ -#define ALT_PATH_MODE_ADD2H_CHG 6 /* add 2 H along alt. path A=-=B => AH-=-BH and change bonds to alternating */ -#define ALT_PATH_MODE_REM2H_TST 7 /* test-remove 2 H along alt. path AH-=-BH => A=-=B; restore changed bonds */ -#define ALT_PATH_MODE_ADD2H_TST 8 /* test-add 2 H along alt. path A=-=B => AH-=-BH; restore changed bonds */ -#define ALT_PATH_MODE_REM_PROTON 9 /* remove proton, adjust bonds, charges, H-counts 2004-03-05 */ -#if ( KETO_ENOL_TAUT == 1 ) -#define ALT_PATH_MODE_TAUTOM_KET 10 /* same as ALT_PATH_MODE_TAUTOM, applies to C=-OH or CH-=O; H may be (-) */ -#endif - -typedef U_SHORT bitWord; -#define BIT_WORD_MASK ((bitWord)~0) - -typedef struct tagNodeSet { - bitWord **bitword; - int num_set; /* number of sets */ - int len_set; /* number of bitWords in each set */ -} NodeSet; - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - -/********************************************************************************* - bChangeFlow: - 1 => change flow inside the BNS search - 3 => change flow inside the BNS search and undo the flow change in the BNS structure here - 4 => change bonds in the structure according to the flow - 8 => make altern. bonds in the structure - - Note: (bChangeFlow & 1) == 1 is needed for multiple runs -**********************************************************************************/ - -/* "EF" = "Edge Flow" */ -#define BNS_EF_CHNG_FLOW 1 /* change Balanced Network (BN) flow inside the BNS search */ -#define BNS_EF_RSTR_FLOW 2 /* undo BN flow changes after BNS */ -#define BNS_EF_CHNG_RSTR (BNS_EF_CHNG_FLOW | BNS_EF_RSTR_FLOW) -#define BNS_EF_CHNG_BONDS 4 /* change bonds in the structure according to the BN flow */ -#define BNS_EF_ALTR_BONDS 8 /* make altern. bonds in the structure if the flow has changed */ -#define BNS_EF_UPD_RAD_ORI 16 /* update BN flow0 & Atom radical values: - flow0 := flow, radical:=st_cap - st_flow */ -#define BNS_EF_SET_NOSTEREO 32 /* in combination with BNS_EF_ALTR_BONDS only: - ALT12 bond cannot be stereogenic */ -#define BNS_EF_UPD_H_CHARGE 64 /* update charges and H-counts according to change flow to c- and t-group vertices */ - -#define BNS_EF_SAVE_ALL (BNS_EF_CHNG_FLOW | BNS_EF_CHNG_BONDS | BNS_EF_UPD_RAD_ORI) -#define BNS_EF_ALTR_NS (BNS_EF_ALTR_BONDS | BNS_EF_SET_NOSTEREO) - -#define BNS_EF_RAD_SRCH 128 /* search for rafical paths closures */ - - - -int SetBitCreate( void ); -int NodeSetCreate( NodeSet *pSet, int n, int L ); -void NodeSetFree( NodeSet *pSet ); - -int IsNodeSetEmpty( NodeSet *cur_nodes, int k); -int DoNodeSetsIntersect( NodeSet *cur_nodes, int k1, int k2); -void AddNodeSet2ToNodeSet1( NodeSet *cur_nodes, int k1, int k2); -void NodeSetFromRadEndpoints( NodeSet *cur_nodes, int k, /*Node *v*/ Vertex RadEndpoints[], int num_v); -void RemoveFromNodeSet( NodeSet *cur_nodes, int k, Vertex v[], int num_v); -int AddNodesToRadEndpoints( NodeSet *cur_nodes, int k, Vertex RadEndpoints[], Vertex vRad, int nStart, int nLen ); - - -int nExists2AtMoveAltPath( struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, - struct BN_AtomsAtTautGroup *pAATG, inp_ATOM *at, int num_atoms, - int jj2, int jj1, struct tagSaltChargeCandidate *s_candidate, int nNumCandidates, - AT_NUMB *nForbiddenAtom, int nNumForbiddenAtoms); -int bExistsAltPath( struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, - struct BN_AtomsAtTautGroup *pAATG, inp_ATOM *at, int num_atoms, int nVertDoubleBond, int nVertSingleBond, int path_type ); -int bExistsAnyAltPath( struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, - inp_ATOM *at, int num_atoms, int nVertDoubleBond, int nVertSingleBond, int path_type ); -int AddTGroups2BnStruct( struct BalancedNetworkStructure *pBNS, inp_ATOM *at, int num_atoms, - struct tagTautomerGroupsInfo *tgi ); -int AddSuperTGroup2BnStruct( struct BalancedNetworkStructure *pBNS, inp_ATOM *at, int num_atoms, - struct tagTautomerGroupsInfo *tgi ); -int AddCGroups2BnStruct( struct BalancedNetworkStructure *pBNS, inp_ATOM *at, int num_atoms, - struct tagChargeGroupsInfo *cgi ); - -int ReInitBnStruct( struct BalancedNetworkStructure *pBNS, inp_ATOM *at, int num_at, int bRemoveGroupsFromAtoms ); -int ReInitBnStructAddGroups( struct BalancedNetworkStructure *pBNS, inp_ATOM *at, int num_atoms, - struct tagTautomerGroupsInfo *tgi, struct tagChargeGroupsInfo *cgi ); - - -int DisconnectTestAtomFromTGroup( struct BalancedNetworkStructure *pBNS, int v1, int *pv2, BNS_FLOW_CHANGES *fcd ); -int DisconnectTGroupFromSuperTGroup( struct BalancedNetworkStructure *pBNS, int v1, int *pv1, int *pv2, - BNS_FLOW_CHANGES *fcd ); -int ReconnectTestAtomToTGroup( struct BalancedNetworkStructure *pBNS, int v1, int v2, int ie, BNS_FLOW_CHANGES *fcd ); - -int bIsHardRemHCandidate( inp_ATOM *at, int i, int *cSubType ); - -/* moved from ichi_bns.c 2005-08-23 */ -int RunBalancedNetworkSearch( BN_STRUCT *pBNS, BN_DATA *pBD, int bChangeFlow ); -BN_STRUCT* AllocateAndInitBnStruct( inp_ATOM *at, int num_atoms, int nMaxAddAtoms, int nMaxAddEdges, int max_altp, int *num_changed_bonds ); -BN_STRUCT* DeAllocateBnStruct( BN_STRUCT *pBNS ); -int ReInitBnStructAltPaths( BN_STRUCT *pBNS ); -int ReInitBnStructForMoveableAltBondTest( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms ); -void ClearAllBnDataVertices( Vertex *v, Vertex value, int size ); -void ClearAllBnDataEdges( Edge *e, Vertex value, int size ); -BN_DATA *DeAllocateBnData( BN_DATA *pBD ); -BN_DATA *AllocateAndInitBnData( int max_num_vertices ); -int ReInitBnData( BN_DATA *pBD ); -int SetForbiddenEdges( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int edge_forbidden_mask ); -/* main function: find augmenting path */ -int BalancedNetworkSearch ( BN_STRUCT* pBNS, BN_DATA *pBD, int bChangeFlow ); - -int SetRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode ); -int SetRadEndpoints2( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode ); - -int RemoveRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at ); - -int AddRemoveProtonsRestr( inp_ATOM *at, int num_atoms, int *num_protons_to_add, - int nNumProtAddedByRestr, INCHI_MODE bNormalizationFlags, - int num_tg, int nChargeRevrs, int nChargeInChI ); -int AddRemoveIsoProtonsRestr( inp_ATOM *at, int num_atoms, NUM_H num_protons_to_add[], int num_tg ); - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /* __INCHI_BNS_H___ */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _INCHI_BNS_H_ +#define _INCHI_BNS_H_ + + +#include "incomdef.h" +#include "inpdef.h" + + +/*#define FIX_SRU_CYCLIZING_PS_BONDS_IN_BNS 1*/ + +#define BN_MAX_ALTP 16 +/*#define MAX_VERTEX 1024*/ /* including s; if vert[] has num_vert then MAX_VERTEX has (2*num_vert+2+FIRST_INDX) elements */ + +/* forward declarations */ + +struct BalancedNetworkStructure; +struct BalancedNetworkData; +struct tagTautomerGroupsInfo; +struct tagChargeGroupsInfo; +struct BN_AtomsAtTautGroup; +struct tagSaltChargeCandidate; + +/* define BNS types */ + +typedef int Vertex; +typedef int EdgeIndex; +typedef int Edge[2]; /* Edge[0] = vertex1, Edge[1] = iedge or -(1+vertex1) if vertex2 = s or t */ +typedef int BNS_IEDGE; +typedef int EdgeFlow; +typedef int VertexFlow; + + +#define BNS_EDGE_FORBIDDEN_MASK 1 +#define BNS_EDGE_FORBIDDEN_TEMP 2 +#define BNS_EDGE_FORBIDDEN_TEST 4 + +/* BNS vertex types */ + +#define BNS_VERT_TYPE_ATOM 0x0001 +#define BNS_VERT_TYPE_ENDPOINT 0x0002 /* attribute */ +#define BNS_VERT_TYPE_TGROUP 0x0004 +#define BNS_VERT_TYPE_C_POINT 0x0008 +#define BNS_VERT_TYPE_C_GROUP 0x0010 +#define BNS_VERT_TYPE_SUPER_TGROUP 0x0020 +#define BNS_VERT_TYPE_TEMP 0x0040 + +#define BNS_VERT_TYPE__AUX 0x0080 /* vertex added to build charge substructures */ +#define BNS_VERT_TYPE_C_NEGATIVE 0x0100 /* negative charge group; attribute, should be used with BNS_VERT_TYPE_C_GROUP */ +#define BNS_VERT_TYPE_ACID 0x0200 /* only for this type are allowed paths: t_group-atom-c_group_neg (path_TACN) */ +#define BNS_VERT_TYPE_CARBON_GR 0x0400 /* charge of carbon atom; should be used with BNS_VT_C_POS, BNS_VT_C_NEG */ +#define BNS_VERT_TYPE_METAL_GR 0x0800 /* metal atom group; may be used alone or with BNS_VT_M_POS, BNS_VT_M_NEG */ + +#define BNS_VERT_TYPE_ANY_GROUP (BNS_VERT_TYPE_TGROUP | BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_SUPER_TGROUP) + +/* InChI->Structure */ + +#define BNS_VT_C_POS BNS_VERT_TYPE_C_GROUP /* positive charge group, heteroat */ +#define BNS_VT_C_NEG (BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE) /* negative charge group, heteroat */ +#define BNS_VT_C_POS_C (BNS_VT_C_POS | BNS_VERT_TYPE_CARBON_GR) /* positive charge group, C, Si, Ge, Sn */ +#define BNS_VT_C_NEG_C (BNS_VT_C_NEG | BNS_VERT_TYPE_CARBON_GR) /* negative charge group, C, Si, Ge, Sn */ +#define BNS_VT_C_POS_M (BNS_VT_C_POS | BNS_VERT_TYPE_METAL_GR) /* positive charge group, metal */ +#define BNS_VT_C_NEG_M (BNS_VT_C_NEG | BNS_VERT_TYPE_METAL_GR) /* negative charge group, metal */ +#define BNS_VT_M_GROUP BNS_VERT_TYPE_METAL_GR /* metal-group, flower vertex */ + +#define BNS_VT_C_POS_ALL (BNS_VERT_TYPE_SUPER_TGROUP | BNS_VERT_TYPE_C_GROUP) /* supergroup (+) */ +#define BNS_VT_C_NEG_ALL (BNS_VT_C_POS_ALL | BNS_VERT_TYPE_C_NEGATIVE) /* supergroup (-) */ + +#define BNS_VT_CHRG_STRUCT (BNS_VERT_TYPE__AUX | BNS_VERT_TYPE_TEMP) /* ChargeStruct vertex */ +#define BNS_VT_YVCONNECTOR BNS_VERT_TYPE__AUX /* group connection */ + +#define IS_BNS_VT_C_OR_CSUPER_GR(X) ((X) & BNS_VT_C_POS) +#define IS_BNS_VT_C_GR(X) (((X) & BNS_VT_C_POS_ALL) == BNS_VERT_TYPE_C_GROUP) +#define IS_BNS_VT_CM_GR(X) (((X) & BNS_VT_C_POS_M) == BNS_VT_C_POS_M) /* metal charge group */ +#define IS_BNS_VT_M_GR(X) ((X) == BNS_VERT_TYPE_METAL_GR ) /* metal flower base or vertices */ +#define IS_BNS_VT_YVCONNECTOR(X) (((X) & BNS_VERT_TYPE__AUX) && !((X) & BNS_VERT_TYPE_TEMP)) +#define IS_BNS_VT_CHRG_STRUCT(X) (((X) & BNS_VERT_TYPE__AUX) && ((X) & BNS_VERT_TYPE_TEMP)) +#define IS_BNS_VT_ATOM(X) ((X) & BNS_VERT_TYPE_ATOM) + +#define BNS_ADD_SUPER_TGROUP 1 /* reserve one more edge for a t-group to connect to a single super-t-group */ +#define NUM_KINDS_OF_GROUPS 2 /* 1 accounts for t-group kind, one more 1 accounts for c-group kind */ + +#define BNS_ADD_ATOMS 2 /* max. number of fictitious atoms to add (except t-gtoups) */ +#define BNS_ADD_EDGES 1 /* max. number of edges to add to each atom (except edges to a t-group or c-group) */ + +typedef enum tagAltPathConst { + iALTP_MAX_LEN, /* 0 */ + iALTP_FLOW, /* 1 */ + iALTP_PATH_LEN, /* 2 */ + iALTP_START_ATOM, /* 3 */ + iALTP_END_ATOM, /* 4 */ + iALTP_NEIGHBOR, /* 5 */ + iALTP_HDR_LEN = iALTP_NEIGHBOR +} ALT_CONST; + +#define ALTP_PATH_LEN(altp) (altp)[iALTP_PATH_LEN].number /* number of bonds = number of atoms-1*/ +#define ALTP_END_ATOM(altp) (altp)[iALTP_END_ATOM].number +#define ALTP_START_ATOM(altp) (altp)[iALTP_START_ATOM].number +#define ALTP_THIS_ATOM_NEIGHBOR(altp,X) (altp)[iALTP_NEIGHBOR+(X)].ineigh[0] /* 0 <= X < path_len */ +#define ALTP_NEXT_ATOM_NEIGHBOR(altp,X) (altp)[iALTP_NEIGHBOR+(X)].ineigh[1] +#define ALTP_CUR_THIS_ATOM_NEIGHBOR(altp) (altp)[iALTP_NEIGHBOR+ALTP_PATH_LEN(altp)].ineigh[0] /* 0 <= X < path_len */ +#define ALTP_CUR_NEXT_ATOM_NEIGHBOR(altp) (altp)[iALTP_NEIGHBOR+ALTP_PATH_LEN(altp)].ineigh[1] +#define ALTP_NEXT(altp) (++ALTP_PATH_LEN(altp)) +#define ALTP_PREV(altp) (--ALTP_PATH_LEN(altp)) +#define ALTP_MAY_ADD(altp) (iALTP_NEIGHBOR + (altp)[iALTP_PATH_LEN].number < (altp)[iALTP_MAX_LEN].number) +#define ALTP_ALLOCATED_LEN(altp) (altp)[iALTP_MAX_LEN].number +#define ALTP_DELTA(altp) (altp)[iALTP_FLOW].flow[0] +#define ALTP_OVERFLOW(altp) (altp)[iALTP_FLOW].flow[1] + +#define Vertex_s 0 +#define Vertex_t 1 + +#define NO_VERTEX -2 +#define BLOSSOM_BASE -1 + +#define ADD_CAPACITY_RADICAL 1 /* add capacity to radical */ + +#define MAX_BOND_EDGE_CAP 2 /* triple bond */ +#define AROM_BOND_EDGE_CAP 1 +#define MAX_TGROUP_EDGE_CAP 2 /* -NH2 provides max. capacity */ + +/* edge to s or t */ +#define EDGE_FLOW_ST_MASK 0x3fff /* mask for flow */ +#define EDGE_FLOW_ST_PATH 0x4000 /* mark: the edge belongs to the augmenting path */ + +/* edges between other vertices */ +/* EdgeFlow WAS defined as S_SHORT; change from S_CHAR made 9-23-2005 */ +#define EDGE_FLOW_MASK 0x3fff /* mask for flow */ +#define EDGE_FLOW_PATH 0x4000 /* mark: the edge belongs to the augmenting path */ + +/*********************************************************************************/ +#if ( ADD_CAPACITY_RADICAL == 1 ) /* { */ +/* -- do not treat triplets as moving dots -- 2004-02-18 -- +#define MAX_AT_FLOW(X) (((X).chem_bonds_valence - (X).valence)+\ + ((is_centerpoint_elem((X).el_number)||get_endpoint_valence((X).el_number))?\ + (((X).radical==RADICAL_DOUBLET)+2*((X).radical==RADICAL_TRIPLET)):0)) +*/ +#define MAX_AT_FLOW(X) (((X).chem_bonds_valence - (X).valence)+\ + ((is_centerpoint_elem((X).el_number)||get_endpoint_valence((X).el_number))?\ + (((X).radical==RADICAL_DOUBLET)/*+2*((X).radical==RADICAL_TRIPLET)*/):0)) + + +#else /* } ADD_CAPACITY_RADICAL { */ + +#define MAX_AT_FLOW(X) (((X).chem_bonds_valence - (X).valence) + +#endif /* } ADD_CAPACITY_RADICAL */ + +/**************************** BNS_EDGE ************************************/ +typedef struct BnsEdge { + AT_NUMB neighbor1; /* the smaller neighbor */ + AT_NUMB neighbor12; /* neighbor1 ^ neighbor2 */ + AT_NUMB neigh_ord[2]; /* ordering number of the neighbor: [0]: atneighbor */ + EdgeFlow cap; /* Edge capacity */ + EdgeFlow cap0; /* Initial edge capacity */ + EdgeFlow flow; /* Edge flow */ + EdgeFlow flow0; /* Initial flow */ + /*S_CHAR delta; */ + S_CHAR pass; /* number of times changed in AugmentEdge() */ + S_CHAR forbidden; +} BNS_EDGE; + +/**************************** BNS_ST_EDGE ************************************/ +typedef struct BnsStEdge { + VertexFlow cap; /* Edge capacity */ + VertexFlow cap0; /* Initial edge capacity */ + VertexFlow flow; /* Edge flow */ + VertexFlow flow0; /* Initial edge flow */ + S_CHAR pass; /* number of times changed in AugmentEdge() */ + /*S_CHAR delta; */ +} BNS_ST_EDGE; + +/**************************** BNS_VERTEX ************************************/ +typedef struct BnsVertex { + + BNS_ST_EDGE st_edge; /* 0,1 capacity and flow of the edge to s or t */ + AT_NUMB type; /* 2, atom, t-group, or added atom: BNS_VERT_TYPE_TGROUP, etc. */ + AT_NUMB num_adj_edges; /* 3, actual number of neighbors incl. t-groups, excl. s or t */ + AT_NUMB max_adj_edges; /* 4, including reserved */ + /*S_CHAR path_neigh[2];*/ /* 5 found path information */ + /* indexes of Edges */ + BNS_IEDGE *iedge; /* 6 a pointer to the array of edge indexes adjacent to this vertex */ +}BNS_VERTEX; + +/**************************** BNS_ALT_PATH ************************************/ +typedef union BnsAltPath { + VertexFlow flow[2]; + Vertex number; + AT_NUMB ineigh[2]; +} BNS_ALT_PATH; + +/**************************** BN_STRUCT ************************************/ +typedef struct BalancedNetworkStructure { + + int num_atoms; /* number of real atoms */ + /*int len_atoms; */ /* size of filled with real atoms portion of the BNS_VERTEX data */ + int num_added_atoms; /* number of added fictitious atoms */ + int nMaxAddAtoms; /* max. number of atoms to add (not including t-groups) */ + int num_c_groups; /* number of added c-groups */ + int num_t_groups; /* number of added t-groups */ + int num_vertices; /* total number currently in effect; includes t-groups and added atoms */ + /*int len_vertices; */ /* allocation size for BNS_VERTEX data */ + int num_bonds; /* number of real bonds/2 = number of edges between real atoms */ + int num_edges; /* number of currently in effect */ + int num_iedges; /* added 9-16-2005; used only in InChI Reversing */ + int num_added_edges; /* number of added edges (not including edges to t-groups) */ + int nMaxAddEdges; /* max. number edges of add to each atom (not including edges to t-groups) */ + + int max_vertices; /* allocation size for BNS_VERTEX structures */ + int max_edges; /* allocation size for edge[]; iedge has length 2*max_edges */ + int max_iedges; /* allocation size for iedge */ + + int tot_st_cap; /* not used, only calculated */ + int tot_st_flow; /* not used, only calculated */ + + int len_alt_path; /* length of alt_path[] */ + + int bNotASimplePath; /* alternating path traversed same bond 2 times in opposite directions */ + int bChangeFlow; /* actually change flow */ + + BNS_VERTEX *vert; /* vertices */ + BNS_EDGE *edge; /* edges */ + BNS_IEDGE *iedge; + BNS_ALT_PATH *alt_path; /* current altp[] element */ + BNS_ALT_PATH *altp[BN_MAX_ALTP]; /* keep alt. paths */ + + int max_altp; + int num_altp; + + INCHI_MODE *pbTautFlags; /* carry it through all functions; never NULL */ + INCHI_MODE *pbTautFlagsDone; /* carry it through all functions; never NULL */ + AT_NUMB type_TACN; /* BNS_VERT_TYPE_ACID: if non-zero than only for it path type_T-type_TACN-type_CN allowed */ + AT_NUMB type_T; /* BNS_VERT_TYPE_TGROUP */ + AT_NUMB type_CN; /* BNS_VERT_TYPE_C_GROUP | BNS_VERT_TYPE_C_NEGATIVE */ + S_CHAR edge_forbidden_mask; + /* v. 1.05 */ + struct tagINCHI_CLOCK *ic; + struct tagInchiTime *ulTimeOutTime; +} BN_STRUCT; + +/********************* BN_DATA *******************************************/ +typedef enum tagBnsRadSrchMode { + RAD_SRCH_NORM = 0, /* normal search for normalization */ + RAD_SRCH_FROM_FICT = 1 /* search from fict. vertices to atoms */ +} BRS_MODE; +typedef struct BalancedNetworkData { + Vertex *BasePtr; /*[MAX_VERTEX]; pointer toward the base of C(v) */ + Edge *SwitchEdge; /*[MAX_VERTEX]; a pair of vertices and an edge, implemented here as [*][2] array */ + S_CHAR *Tree; /*[MAX_VERTEX]; indicates presence in ScanQ, T, T', s-reachability */ + Vertex *ScanQ; /*[MAX_VERTEX]; contains the set S of s-reachable vertices */ + int QSize; /* index of the last element added to ScanQ */ + Vertex *Pu; /*[MAX_VERTEX/2+1] */ + Vertex *Pv; /*[MAX_VERTEX/2+1] */ + int max_num_vertices; /* allocation size of all except Pu, Pv */ + int max_len_Pu_Pv; /* allocation size of Pu and Pv */ +#if ( BNS_RAD_SEARCH == 1 ) + Vertex *RadEndpoints; /*[MAX_VERTEX*/ + int nNumRadEndpoints; + EdgeIndex *RadEdges; + int nNumRadEdges; + int nNumRadicals; + BRS_MODE bRadSrchMode; /* 1 => connect fict. vertices-radicals to the accessible atoms */ +#endif +} BN_DATA; + +/* internal array size */ +#define MAX_ALT_AATG_ARRAY_LEN 127 +/* detected endpoint markings */ +#define AATG_MARK_IN_PATH 1 /* atom in path detected by the BNS */ +#define AATG_MARK_WAS_IN_PATH 2 /* found to be in path before next level */ +/* output */ +#define AATG_MARK_MAIN_TYPE 4 /* atom O-"salt" */ +#define AATG_MARK_OTHER_TYPE 8 /* other atom to be tested */ + +struct tagTautomerGroupsInfo; /* forward declaration */ + +/******************** atoms in alt path through taut group ****************/ +typedef struct BN_AtomsAtTautGroup { + int nAllocLen; + int nNumFound; + int nNumMainAdj2Tgroup; + int nNumOthersAdj2Tgroup; + AT_NUMB *nEndPoint; /* original t-group number */ + S_CHAR *nMarkedAtom; /* atom mark, see AATG_MARK_* */ + int *nAtTypeTotals; + struct tagTautomerGroupsInfo *t_group_info; +} BN_AATG; + + +/************ store changes in flow and capacity to test a bond ****************/ + +typedef struct tagBNS_FLOW_CHANGES { + BNS_IEDGE iedge; + EdgeFlow flow; + EdgeFlow cap; + Vertex v1; + VertexFlow cap_st1; + VertexFlow flow_st1; + Vertex v2; + VertexFlow cap_st2; + VertexFlow flow_st2; +} BNS_FLOW_CHANGES; + + +#define ALT_PATH_MODE_TAUTOM 1 +#define ALT_PATH_MODE_CHARGE 2 +#define ALT_PATH_MODE_4_SALT 3 /* mark alt bonds along the path */ +#define ALT_PATH_MODE_4_SALT2 4 /* mark alt bonds along the path, path to taut. group fict. vertex if exists */ +#define ALT_PATH_MODE_REM2H_CHG 5 /* remove 2 H along alt. path AH-=-BH => A=-=B and change bonds to alternating */ +#define ALT_PATH_MODE_ADD2H_CHG 6 /* add 2 H along alt. path A=-=B => AH-=-BH and change bonds to alternating */ +#define ALT_PATH_MODE_REM2H_TST 7 /* test-remove 2 H along alt. path AH-=-BH => A=-=B; restore changed bonds */ +#define ALT_PATH_MODE_ADD2H_TST 8 /* test-add 2 H along alt. path A=-=B => AH-=-BH; restore changed bonds */ +#define ALT_PATH_MODE_REM_PROTON 9 /* remove proton, adjust bonds, charges, H-counts 2004-03-05 */ +#if ( KETO_ENOL_TAUT == 1 ) +#define ALT_PATH_MODE_TAUTOM_KET 10 /* same as ALT_PATH_MODE_TAUTOM, applies to C=-OH or CH-=O; H may be (-) */ +#endif + +typedef U_SHORT bitWord; +#define BIT_WORD_MASK ((bitWord)~0) + +typedef struct tagNodeSet { + bitWord **bitword; + int num_set; /* number of sets */ + int len_set; /* number of bitWords in each set */ +} NodeSet; + + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + + +/********************************************************************************* + bChangeFlow: + 1 => change flow inside the BNS search + 3 => change flow inside the BNS search and undo the flow change in the BNS structure here + 4 => change bonds in the structure according to the flow + 8 => make altern. bonds in the structure + + Note: (bChangeFlow & 1) == 1 is needed for multiple runs +**********************************************************************************/ + +/* "EF" = "Edge Flow" */ +#define BNS_EF_CHNG_FLOW 1 /* change Balanced Network (BN) flow inside the BNS search */ +#define BNS_EF_RSTR_FLOW 2 /* undo BN flow changes after BNS */ +#define BNS_EF_CHNG_RSTR (BNS_EF_CHNG_FLOW | BNS_EF_RSTR_FLOW) +#define BNS_EF_CHNG_BONDS 4 /* change bonds in the structure according to the BN flow */ +#define BNS_EF_ALTR_BONDS 8 /* make altern. bonds in the structure if the flow has changed */ +#define BNS_EF_UPD_RAD_ORI 16 /* update BN flow0 & Atom radical values: + flow0 := flow, radical:=st_cap - st_flow */ +#define BNS_EF_SET_NOSTEREO 32 /* in combination with BNS_EF_ALTR_BONDS only: + ALT12 bond cannot be stereogenic */ +#define BNS_EF_UPD_H_CHARGE 64 /* update charges and H-counts according to change flow to c- and t-group vertices */ + +#define BNS_EF_SAVE_ALL (BNS_EF_CHNG_FLOW | BNS_EF_CHNG_BONDS | BNS_EF_UPD_RAD_ORI) +#define BNS_EF_ALTR_NS (BNS_EF_ALTR_BONDS | BNS_EF_SET_NOSTEREO) + +#define BNS_EF_RAD_SRCH 128 /* search for rafical paths closures */ + + +struct tagCANON_GLOBALS; + +int NodeSetCreate( struct tagCANON_GLOBALS *pCG, NodeSet *pSet, int n, int L ); +void NodeSetFree( struct tagCANON_GLOBALS *pCG, NodeSet *pSet ); + +int IsNodeSetEmpty( NodeSet *cur_nodes, int k); +int DoNodeSetsIntersect( NodeSet *cur_nodes, int k1, int k2); +void AddNodeSet2ToNodeSet1( NodeSet *cur_nodes, int k1, int k2); +void NodeSetFromRadEndpoints( struct tagCANON_GLOBALS *pCG, NodeSet *cur_nodes, int k, /*Node *v*/ Vertex RadEndpoints[], int num_v); +void RemoveFromNodeSet( struct tagCANON_GLOBALS *pCG, NodeSet *cur_nodes, int k, Vertex v[], int num_v); +int AddNodesToRadEndpoints( struct tagCANON_GLOBALS *pCG, NodeSet *cur_nodes, int k, Vertex RadEndpoints[], Vertex vRad, int nStart, int nLen ); + + +int nExists2AtMoveAltPath( struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, + struct BN_AtomsAtTautGroup *pAATG, inp_ATOM *at, int num_atoms, + int jj2, int jj1, struct tagSaltChargeCandidate *s_candidate, int nNumCandidates, + AT_NUMB *nForbiddenAtom, int nNumForbiddenAtoms); + +int bExistsAltPath( struct tagCANON_GLOBALS *pCG, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD, + struct BN_AtomsAtTautGroup *pAATG, + inp_ATOM *at, + int num_atoms, + int nVertDoubleBond, + int nVertSingleBond, + int path_type ); +int bExistsAnyAltPath( struct tagCANON_GLOBALS *pCG, struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, + inp_ATOM *at, int num_atoms, int nVertDoubleBond, int nVertSingleBond, int path_type ); +int AddTGroups2BnStruct( struct tagCANON_GLOBALS *pCG, struct BalancedNetworkStructure *pBNS, inp_ATOM *at, int num_atoms, + struct tagTautomerGroupsInfo *tgi ); +int AddSuperTGroup2BnStruct( struct BalancedNetworkStructure *pBNS, inp_ATOM *at, int num_atoms, + struct tagTautomerGroupsInfo *tgi ); +int AddCGroups2BnStruct( struct tagCANON_GLOBALS *pCG, struct BalancedNetworkStructure *pBNS, inp_ATOM *at, int num_atoms, + struct tagChargeGroupsInfo *cgi ); + +int ReInitBnStruct( struct BalancedNetworkStructure *pBNS, inp_ATOM *at, int num_at, int bRemoveGroupsFromAtoms ); +int ReInitBnStructAddGroups( struct tagCANON_GLOBALS *pCG, struct BalancedNetworkStructure *pBNS, inp_ATOM *at, int num_atoms, + struct tagTautomerGroupsInfo *tgi, struct tagChargeGroupsInfo *cgi ); + + +int DisconnectTestAtomFromTGroup( struct BalancedNetworkStructure *pBNS, int v1, int *pv2, BNS_FLOW_CHANGES *fcd ); +int DisconnectTGroupFromSuperTGroup( struct BalancedNetworkStructure *pBNS, int v1, int *pv1, int *pv2, + BNS_FLOW_CHANGES *fcd ); +int ReconnectTestAtomToTGroup( struct BalancedNetworkStructure *pBNS, int v1, int v2, int ie, BNS_FLOW_CHANGES *fcd ); + +int bIsHardRemHCandidate( inp_ATOM *at, int i, int *cSubType ); + +/* moved from ichi_bns.c 2005-08-23 */ +int RunBalancedNetworkSearch( BN_STRUCT *pBNS, BN_DATA *pBD, int bChangeFlow ); +BN_STRUCT* AllocateAndInitBnStruct( inp_ATOM *at, int num_atoms, int nMaxAddAtoms, int nMaxAddEdges, int max_altp, int *num_changed_bonds ); +BN_STRUCT* DeAllocateBnStruct( BN_STRUCT *pBNS ); +int ReInitBnStructAltPaths( BN_STRUCT *pBNS ); +int ReInitBnStructForMoveableAltBondTest( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms ); +void ClearAllBnDataVertices( Vertex *v, Vertex value, int size ); +void ClearAllBnDataEdges( Edge *e, Vertex value, int size ); +BN_DATA *DeAllocateBnData( BN_DATA *pBD ); +BN_DATA *AllocateAndInitBnData( int max_num_vertices ); +int ReInitBnData( BN_DATA *pBD ); +int SetForbiddenEdges( BN_STRUCT *pBNS, inp_ATOM *at, int num_atoms, int edge_forbidden_mask, + int nebend, int *ebend ); +/* main function: find augmenting path */ +int BalancedNetworkSearch ( BN_STRUCT* pBNS, BN_DATA *pBD, int bChangeFlow ); + +int SetRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode ); +int SetRadEndpoints2( struct tagCANON_GLOBALS *pCG, BN_STRUCT *pBNS, BN_DATA *pBD, BRS_MODE bRadSrchMode ); +int RemoveRadEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, inp_ATOM *at ); + +int AddRemoveProtonsRestr( inp_ATOM *at, int num_atoms, int *num_protons_to_add, + int nNumProtAddedByRestr, INCHI_MODE bNormalizationFlags, + int num_tg, int nChargeRevrs, int nChargeInChI ); +int AddRemoveIsoProtonsRestr( inp_ATOM *at, int num_atoms, NUM_H num_protons_to_add[], int num_tg ); + + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + + + +#endif /* _INCHI_BNS_H_ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichi_io.c b/INCHI-1-SRC/INCHI_BASE/src/ichi_io.c similarity index 55% rename from INCHI-1-SRC/INCHI_API/inchi_dll/ichi_io.c rename to INCHI-1-SRC/INCHI_BASE/src/ichi_io.c index 1403f86..4dc7057 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichi_io.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichi_io.c @@ -1,1052 +1,1374 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include -#include "mode.h" -#include "ichi_io.h" -#include "ichicomp.h" -#include "util.h" - -#ifndef INCHI_ADD_STR_LEN -#define INCHI_ADD_STR_LEN 32768 -#endif - - -#ifdef TARGET_LIB_FOR_WINCHI -extern void (*FWPRINT) (const char * format, va_list argptr ); -#endif - - -/*^^^ Internal functions */ - -int inchi_ios_str_getc( INCHI_IOSTREAM *ios ); -char *inchi_ios_str_gets( char *szLine, int len, INCHI_IOSTREAM *ios ); -char *inchi_ios_str_getsTab( char *szLine, int len, INCHI_IOSTREAM *ios ); -int GetMaxPrintfLength( const char *lpszFormat, va_list argList); -char *inchi_fgetsTab( char *szLine, int len, FILE *f ); -int inchi_vfprintf( FILE* f, const char* lpszFormat, va_list argList ); - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - - INCHI_IOSTREAM OPERATIONS - - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Init INCHI_IOSTREAM -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void inchi_ios_init(INCHI_IOSTREAM* ios, int io_type, FILE *f) -{ - memset( ios, 0, sizeof(*ios) ); - switch (io_type) - { - case INCHI_IOSTREAM_FILE: ios->type = INCHI_IOSTREAM_FILE; - break; - case INCHI_IOSTREAM_STRING: - default: ios->type = INCHI_IOSTREAM_STRING; - break; - } - ios->f = f; - return; -} - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - If INCHI_IOSTREAM type is INCHI_IOSTREAM_STRING, - flush INCHI_IOSTREAM string buffer to file (if non-NULL); then free buffer. - If INCHI_IOSTREAM type is INCHI_IOSTREAM_FILE, just flush the file. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void inchi_ios_flush(INCHI_IOSTREAM* ios) -{ - - if (ios->type == INCHI_IOSTREAM_STRING) - { - if (ios->s.pStr) - { - if (ios->s.nUsedLength > 0) - { - if (ios->f) - { - fprintf(ios->f,"%-s", ios->s.pStr); - fflush(ios->f); - } - inchi_free(ios->s.pStr ); - ios->s.pStr = NULL; - ios->s.nUsedLength = ios->s.nAllocatedLength = ios->s.nPtr = 0; - } - } - } - - else if (ios->type == INCHI_IOSTREAM_FILE) - { - /* output to plain file: just flush it. */ - fflush(ios->f); - } - - return; -} - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - If INCHI_IOSTREAM type is INCHI_IOSTREAM_STRING, - flush INCHI_IOSTREAM string buffer to file (if non-NULL) and - another file f2 supplied as parameter (typically, it will be stderr); then free buffer. - If INCHI_IOSTREAM type is INCHI_IOSTREAM_FILE, just flush the both files. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void inchi_ios_flush2(INCHI_IOSTREAM* ios, FILE *f2) -{ - - if (ios->type == INCHI_IOSTREAM_STRING) - { - if (ios->s.pStr) - { - if (ios->s.nUsedLength > 0) - { - if (ios->f) - { - fprintf(ios->f,"%-s", ios->s.pStr); - fflush(ios->f); - } - if (f2!=ios->f) - fprintf(f2,"%-s", ios->s.pStr); - - inchi_free(ios->s.pStr ); - ios->s.pStr = NULL; - ios->s.nUsedLength = ios->s.nAllocatedLength = ios->s.nPtr = 0; - } - } - } - - else if (ios->type == INCHI_IOSTREAM_FILE) - { - /* output to plain file: just flush it. */ - if ( (ios->f) && (ios->f!=stderr) && (ios->f!=stdout) ) - fflush(ios->f); - if ( f2 && f2!=stderr && f2!=stdout) - fflush(f2); - - - } - - return; -} - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Close INCHI_IOSTREAM: free string buffer and close the file. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void inchi_ios_close(INCHI_IOSTREAM* ios) -{ - if (ios->s.pStr) - inchi_free(ios->s.pStr); - ios->s.pStr = NULL; - ios->s.nUsedLength = ios->s.nAllocatedLength = ios->s.nPtr = 0; - if ( (ios->f) && (ios->f!=stderr) && (ios->f!=stdout) && (ios->f!=stdin)) - fclose(ios->f); - return; -} - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Reset INCHI_IOSTREAM: set string buffer ptr to NULL (but do _not_ free memory)and close the file. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void inchi_ios_reset(INCHI_IOSTREAM* ios) -{ - ios->s.pStr = NULL; - ios->s.nUsedLength = ios->s.nAllocatedLength = ios->s.nPtr = 0; - if ( (ios->f) && (ios->f!=stderr) && (ios->f!=stdout) && (ios->f!=stdin)) - fclose(ios->f); - return; -} - - - - - -/*******************************************************************/ -int inchi_ios_str_getc(INCHI_IOSTREAM *ios) -{ - - if (ios->type==INCHI_IOSTREAM_STRING) - { - if ( ios->s.nPtr < ios->s.nUsedLength ) - { - return (int)ios->s.pStr[ios->s.nPtr++]; - } - return EOF; - } - - else if (ios->type==INCHI_IOSTREAM_FILE) - { - return fgetc( ios->f ); - } - - /* error */ - return EOF; -} - - - -/*******************************************************************/ -char *inchi_ios_str_gets(char *szLine, int len, INCHI_IOSTREAM *f) -{ -int length=0, c=0; - if ( -- len < 0 ) - { - return NULL; - } - while ( length < len && EOF != (c = inchi_ios_str_getc( f )) ) - { - szLine[length++] = (char)c; - if ( c == '\n' ) - break; - } - if ( !length && EOF == c ) - { - return NULL; - } - szLine[length] = '\0'; - return szLine; -} - - - -/********************************************************************************/ -/* read up to len or tab or LF; if empty read next until finds non-empty line */ -/* remove leading and trailing white spaces; keep zero termination */ -/********************************************************************************/ -char *inchi_ios_str_getsTab( char *szLine, int len, INCHI_IOSTREAM *f ) -{ -int length=0, c=0; - if ( --len < 0 ) - { - return NULL; - } - while ( length < len && EOF != (c = inchi_ios_str_getc(f)) ) - { - if ( c == '\t' ) - c = '\n'; - szLine[length++] = (char)c; - if ( c == '\n' ) - break; - } - if ( !length && EOF == c ) - { - return NULL; - } - szLine[length] = '\0'; - return szLine; -} - - -/*******************************************************************/ -int inchi_ios_gets( char *szLine, int len, INCHI_IOSTREAM *f, int *bTooLongLine ) -{ -int length; -char *p; - do - { - p = inchi_ios_str_gets( szLine, len-1, f ); - if ( !p ) - { - *bTooLongLine = 0; - return -1; /* end of file or cannot read */ - } - szLine[len-1] = '\0'; - /* - *bTooLongLine = !strchr( szLine, '\n' ); - */ - p = strchr( szLine, '\n' ); - *bTooLongLine = ( !p && ((int)strlen(szLine)) == len-2 ); - LtrimRtrim( szLine, &length ); - } while ( !length ); - - return length; -} - - -/*******************************************************************/ -/* read up to len or tab or LF; if empty read next until finds non-empty line */ -/* remove leading and trailing white spaces; keep zero termination */ -/*******************************************************************/ -int inchi_ios_getsTab( char *szLine, int len, INCHI_IOSTREAM *f, int *bTooLongLine ) -{ -int length; -char *p; - do - { - p = inchi_ios_str_getsTab( szLine, len-1, f ); - if ( !p ) - { - *bTooLongLine = 0; - return -1; /* end of file or cannot read */ - } - szLine[len-1] = '\0'; - /* - *bTooLongLine = !strchr( szLine, '\n' ); - */ - p = strchr( szLine, '\n' ); - *bTooLongLine = ( !p && ((int)strlen(szLine)) == len-2 ); - LtrimRtrim( szLine, &length ); - } while ( !length ); - return length; -} - -/*******************************************************************/ -int inchi_ios_getsTab1( char *szLine, int len, INCHI_IOSTREAM *f, int *bTooLongLine ) -{ -int length; -char *p; - /*do {*/ - p = inchi_ios_str_getsTab( szLine, len-1, f ); - if ( !p ) - { - *bTooLongLine = 0; - return -1; /* end of file or cannot read */ - } - szLine[len-1] = '\0'; - /* - *bTooLongLine = !strchr( szLine, '\n' ); - */ - p = strchr( szLine, '\n' ); - *bTooLongLine = ( !p && ((int)strlen(szLine)) == len-2 ); - LtrimRtrim( szLine, &length ); - /*} while ( !length );*/ - return length; -} - - - - - - -/*****************************************************************/ -int inchi_ios_print( INCHI_IOSTREAM * ios, const char* lpszFormat, ... ) -{ -int ret=0, ret2=0; -va_list argList; - - if (!ios) - return -1; - - if (ios->type == INCHI_IOSTREAM_STRING) - { - /* output to string buffer */ - int max_len; - my_va_start( argList, lpszFormat ); - max_len = GetMaxPrintfLength( lpszFormat, argList); - va_end( argList ); - if ( max_len >= 0 ) - { - if ( ios->s.nAllocatedLength - ios->s.nUsedLength <= max_len ) - { - /* enlarge output string */ - int nAddLength = inchi_max( INCHI_ADD_STR_LEN, max_len ); - char *new_str = - (char *)inchi_calloc( ios->s.nAllocatedLength + nAddLength, sizeof(new_str[0]) ); - if ( new_str ) - { - if ( ios->s.pStr ) - { - if ( ios->s.nUsedLength > 0 ) - memcpy( new_str, ios->s.pStr, sizeof(new_str[0])* ios->s.nUsedLength ); - inchi_free( ios->s.pStr ); - } - ios->s.pStr = new_str; - ios->s.nAllocatedLength += nAddLength; - } - else return -1; /* failed */ - } - /* output */ - my_va_start( argList, lpszFormat ); - ret = vsprintf( ios->s.pStr + ios->s.nUsedLength, lpszFormat, argList ); - va_end(argList); - if ( ret >= 0 ) - ios->s.nUsedLength += ret; -#ifdef TARGET_LIB_FOR_WINCHI - if( FWPRINT ) - { - my_va_start( argList, lpszFormat ); - FWPRINT( lpszFormat, argList ); - va_end( argList ); - } -#endif - return ret; - } - return -1; - } - - else if (ios->type == INCHI_IOSTREAM_FILE) - { - /* output to file */ - if (ios->f) - { - my_va_start( argList, lpszFormat ); - ret = vfprintf( ios->f, lpszFormat, argList ); - va_end( argList ); - } - else - { - my_va_start( argList, lpszFormat ); - ret2 = vfprintf( stdout, lpszFormat, argList ); - va_end( argList ); - } -#ifdef TARGET_LIB_FOR_WINCHI - if( FWPRINT ) - { - my_va_start( argList, lpszFormat ); - FWPRINT( lpszFormat, argList ); - va_end( argList ); - } -#endif - return ret? ret : ret2; - } - - - /* no output */ - return 0; -} - - - - -/**********************************************************************/ -/* This function's output should not be displayed in the output pane */ -/**********************************************************************/ -int inchi_ios_print_nodisplay( INCHI_IOSTREAM * ios, const char* lpszFormat, ... ) -{ -va_list argList; - - if (!ios) - return -1; - - if (ios->type == INCHI_IOSTREAM_STRING) - { - /* output to string buffer */ - int ret=0, max_len; - my_va_start( argList, lpszFormat ); - max_len = GetMaxPrintfLength( lpszFormat, argList); - va_end( argList ); - if ( max_len >= 0 ) - { - if ( ios->s.nAllocatedLength - ios->s.nUsedLength <= max_len ) - { - /* enlarge output string */ - int nAddLength = inchi_max( INCHI_ADD_STR_LEN, max_len ); - char *new_str = (char *)inchi_calloc( ios->s.nAllocatedLength + nAddLength, sizeof(new_str[0]) ); - if ( new_str ) - { - if ( ios->s.pStr ) - { - if ( ios->s.nUsedLength > 0 ) - { - memcpy( new_str, ios->s.pStr, sizeof(new_str[0])*ios->s.nUsedLength ); - } - inchi_free( ios->s.pStr ); - } - ios->s.pStr = new_str; - ios->s.nAllocatedLength += nAddLength; - } else - { - return -1; /* failed */ - } - } - /* output */ - my_va_start( argList, lpszFormat ); - ret = vsprintf( ios->s.pStr + ios->s.nUsedLength, lpszFormat, argList ); - va_end(argList); - if ( ret >= 0 ) - { - ios->s.nUsedLength += ret; - } - return ret; - } - return -1; - } - - else if (ios->type == INCHI_IOSTREAM_FILE) - { - my_va_start( argList, lpszFormat ); - inchi_print_nodisplay( ios->f, lpszFormat, argList); - va_end(argList); - } - - /* no output */ - return 0; -} - - - - -/*****************************************************************/ -/* Print to string buffer or to file+stderr */ -int inchi_ios_eprint( INCHI_IOSTREAM * ios, const char* lpszFormat, ... ) -{ -int ret=0, ret2=0; -va_list argList; - - if (!ios) - return -1; - - if (ios->type == INCHI_IOSTREAM_STRING) /* was #if ( defined(TARGET_API_LIB) || defined(INCHI_STANDALONE_EXE) ) */ - { - /* output to string buffer */ - int max_len, nAddLength = 0; - char *new_str = NULL; - - my_va_start( argList, lpszFormat ); - max_len = GetMaxPrintfLength( lpszFormat, argList); - va_end( argList ); - - if ( max_len >= 0 ) - { - if ( ios->s.nAllocatedLength - ios->s.nUsedLength <= max_len ) - { - /* enlarge output string */ - nAddLength = inchi_max( INCHI_ADD_STR_LEN, max_len ); - new_str = (char *)inchi_calloc( ios->s.nAllocatedLength + nAddLength, sizeof(new_str[0]) ); - if ( new_str ) - { - if ( ios->s.pStr ) - { - if ( ios->s.nUsedLength > 0 ) - { - memcpy( new_str, ios->s.pStr, sizeof(new_str[0])* ios->s.nUsedLength ); - } - inchi_free( ios->s.pStr ); - } - ios->s.pStr = new_str; - ios->s.nAllocatedLength += nAddLength; - } - else - { - return -1; /* failed */ - } - } - - /* output */ - my_va_start( argList, lpszFormat ); - ret = vsprintf( ios->s.pStr + ios->s.nUsedLength, lpszFormat, argList ); - va_end(argList); - if ( ret >= 0 ) - { - ios->s.nUsedLength += ret; - } - return ret; - } - return -1; - } - - else if (ios->type == INCHI_IOSTREAM_FILE) - { - if ( ios->f) - { - /* output to plain file */ - my_va_start( argList, lpszFormat ); - ret = inchi_vfprintf( ios->f, lpszFormat, argList ); - va_end( argList ); -/*^^^ No output to stderr from within dll or GUI program */ -#if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) ) - if ( ios->f != stderr ) - { - my_va_start( argList, lpszFormat ); - ret2 = vfprintf( stderr, lpszFormat, argList ); - va_end( argList ); - } -#endif - return ret? ret : ret2; - } - } - - /* no output */ - return 0; -} - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - - PLAIN FILE OPERATIONS - - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -/* Print to file, echoing to stderr */ -int inchi_fprintf( FILE* f, const char* lpszFormat, ... ) -{ -int ret=0, ret2=0; -va_list argList; - if (f) - { - my_va_start( argList, lpszFormat ); - ret = inchi_vfprintf( f, lpszFormat, argList ); - va_end( argList ); -/*^^^ No output to stderr from within dll or GUI program */ -#if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) ) - if ( f != stderr ) - { - my_va_start( argList, lpszFormat ); - ret2 = vfprintf( stderr, lpszFormat, argList ); - va_end( argList ); - } -#endif - return ret? ret : ret2; - } - return 0; -} - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -/* Print to file */ -int inchi_vfprintf( FILE* f, const char* lpszFormat, va_list argList ) -{ -int ret=0; - if ( f == stderr && lpszFormat && lpszFormat[0] && '\r' == lpszFormat[strlen(lpszFormat)-1] ) - { -#define CONSOLE_LINE_LEN 80 -#ifndef COMPILE_ANSI_ONLY - char szLine[CONSOLE_LINE_LEN]; - ret = _vsnprintf( szLine, CONSOLE_LINE_LEN-1, lpszFormat, argList ); - if ( ret < 0 ) - { - /* output is longer than the console line */ - /*^^^ Fixed bug: (CONSOLE_LINE_LEN-4) --> (CONSOLE_LINE_LEN-4-1) 11-22-08 IPl */ - strcpy(szLine+CONSOLE_LINE_LEN-5, "...\r"); - } - fputs( szLine, f ); -#else - ret = vfprintf( f, lpszFormat, argList ); -#endif -#undef CONSOLE_LINE_LEN - } - else - { - ret = vfprintf( f, lpszFormat, argList ); - } - return ret; -} - - - -/**********************************************************************/ -/* This function's output should not be displayed in the output pane */ -/**********************************************************************/ -int inchi_print_nodisplay( FILE* f, const char* lpszFormat, ... ) -{ -int ret=0; -va_list argList; -FILE* fi; - if (f) - fi = f; - else - fi = stdout; - my_va_start( argList, lpszFormat ); - ret = vfprintf( fi, lpszFormat, argList ); - return ret; -} - - - -#if ( FIX_READ_LONG_LINE_BUG == 1 ) -/********************************************************************/ -int inchi_fgetsLfTab( char *szLine, int len, FILE *f ) -{ - int length; - char *p; - char szSkip[256]; - int bTooLongLine = 0; - do { - p = inchi_fgetsTab( szLine, len, f ); - if ( !p ) { - return -1; /* end of file or cannot read */ - } - bTooLongLine = ( (int)strlen(szLine) == len-1 && szLine[len-2] != '\n' ); - LtrimRtrim( szLine, &length ); - } while ( !length ); - if ( bTooLongLine ) { - while ( p = inchi_fgetsTab( szSkip, sizeof(szSkip)-1, f ) ) { - if ( strchr( szSkip, '\n' ) ) - break; - } - } - return length; -} -#else -/********************************************************************/ -int inchi_fgetsLfTab( char *szLine, int len, FILE *f ) -{ - int length; - char *p; - char szSkip[256]; - int bTooLongLine = 0; - do { - p = inchi_fgetsTab( szLine, len-1, f ); - if ( !p ) { - return -1; /* end of file or cannot read */ - } - szLine[len-1] = '\0'; - /* - bTooLongLine = !strchr( szLine, '\n' ); - */ - bTooLongLine = ( !p && ((int)strlen(szLine)) == len-2 ); - LtrimRtrim( szLine, &length ); - } while ( !length ); - if ( bTooLongLine ) { - while ( p = inchi_fgetsTab( szSkip, sizeof(szSkip)-1, f ) ) { - szSkip[sizeof(szSkip)-1] = '\0'; - if ( strchr( szSkip, '\n' ) ) - break; - } - } - return length; -} -#endif - - -/*******************************************************************/ -/* read up to len or tab or LF; if empty read next until finds non-empty line */ -/* remove leading and trailing white spaces; keep zero termination */ -/*******************************************************************/ -char *inchi_fgetsTab( char *szLine, int len, FILE *f ) -{ - int length=0, c=0; - len --; - while ( length < len && EOF != (c = fgetc( f )) ) { - if ( c == '\t' ) - c = '\n'; - szLine[length++] = (char)c; - if ( c == '\n' ) - break; - } - if ( !length && EOF == c ) { - return NULL; - } - szLine[length] = '\0'; - return szLine; -} - - - -/******************************************************************/ -/* read not more than line_len bytes from an lf-terminated line */ -/* if input line is too long quietly ignore the rest of the line */ -char* inchi_fgetsLf( char* line, int line_len, FILE* inp ) -{ - char *p, *q; - memset( line, 0, line_len ); - if ( NULL != (p = fgets( line, line_len, inp ) ) && NULL == strchr(p, '\n' ) ){ - char temp[64]; /* bypass up to '\n' or up to end of file whichever comes first*/ - while ( NULL != fgets( temp, sizeof(temp), inp ) && NULL == strchr(temp,'\n') ) - ; - } - if ( p && (q = strchr(line, '\r')) ) { /* fix CR CR LF line terminator. */ - q[0] = '\n'; - q[1] = '\0'; - } - return p; -} - - - - - - - - - - -/***************************************************************** - * - * Estimate printf string length - * - * The code is based on Microsoft Knowledge Base article Q127038: - * "FIX: CString::Format Gives Assertion Failed, Access Violation" - * (Related to Microsoft Visual C++, 32-bit Editions, versions 2.0, 2.1) - * - *****************************************************************/ - -#define FORCE_ANSI 0x10000 -#define FORCE_UNICODE 0x20000 - -/* formatting (using wsprintf style formatting)*/ -int GetMaxPrintfLength( const char *lpszFormat, va_list argList) -{ - /*ASSERT(AfxIsValidString(lpszFormat, FALSE));*/ - const char * lpsz; - int nMaxLen, nWidth, nPrecision, nModifier, nItemLen; - - nMaxLen = 0; - /* make a guess at the maximum length of the resulting string */ - for ( lpsz = lpszFormat; *lpsz; lpsz ++ ) - { - /* handle '%' character, but watch out for '%%' */ - if (*lpsz != '%' || *( ++ lpsz ) == '%') - { - nMaxLen += 1; - continue; - } - - nItemLen = 0; - - /* handle '%' character with format */ - nWidth = 0; - for (; *lpsz; lpsz ++ ) - { - /* check for valid flags */ - if (*lpsz == '#') - nMaxLen += 2; /* for '0x' */ - else if (*lpsz == '*') - nWidth = va_arg(argList, int); - else if (*lpsz == '-' || *lpsz == '+' || *lpsz == '0' - || *lpsz == ' ') - ; - else /* hit non-flag character */ - break; - } - /* get width and skip it */ - if (nWidth == 0) - { - /* width indicated by */ - nWidth = atoi(lpsz); - for (; *lpsz && isdigit(*lpsz); lpsz ++ ) - ; - } - /*ASSERT(nWidth >= 0);*/ - if ( nWidth < 0 ) - goto exit_error; /* instead of exception */ - - nPrecision = 0; - if (*lpsz == '.') - { - /* skip past '.' separator (width.precision)*/ - lpsz ++; - - /* get precision and skip it*/ - if (*lpsz == '*') - { - nPrecision = va_arg(argList, int); - lpsz ++; - } - else - { - nPrecision = atoi(lpsz); - for (; *lpsz && isdigit(*lpsz); lpsz ++) - ; - } - if ( nPrecision < 0 ) - goto exit_error; /* instead of exception */ - } - - /* should be on type modifier or specifier */ - nModifier = 0; - switch (*lpsz) - { - /* modifiers that affect size */ - case 'h': - switch ( lpsz[1] ) { - case 'd': - case 'i': - case 'o': - case 'x': - case 'X': - case 'u': - /* short unsigned, short double, etc. -- added to the original MS example */ - /* ignore the fact that these modifiers do affect size */ - lpsz ++; - break; - default: - nModifier = FORCE_ANSI; - lpsz ++; - break; - } - break; - case 'l': - switch ( lpsz[1] ) { - case 'd': - case 'i': - case 'o': - case 'x': - case 'X': - case 'u': - case 'f': /* long float -- post ANSI C */ - /* long unsigned, long double, etc. -- added to the original MS example */ - /* ignore the fact that these modifiers do affect size */ - lpsz ++; - break; - default: - /* - nModifier = FORCE_UNICODE; - lpsz ++; - break; - */ - goto exit_error; /* no UNICODE, please */ - } - break; - /* modifiers that do not affect size */ - case 'F': - case 'N': - case 'L': - lpsz ++; - break; - } - - /* now should be on specifier */ - switch (*lpsz | nModifier) - { - /* single characters*/ - case 'c': - case 'C': - nItemLen = 2; - va_arg(argList, int); - break; - case 'c'|FORCE_ANSI: - case 'C'|FORCE_ANSI: - nItemLen = 2; - va_arg(argList, int); - break; - case 'c'|FORCE_UNICODE: - case 'C'|FORCE_UNICODE: - goto exit_error; /* no UNICODE, please */ - /* - nItemLen = 2; - va_arg(argList, wchar_t); - break; - */ - - /* strings*/ - case 's': - case 'S': - nItemLen = strlen(va_arg(argList, char*)); - nItemLen = inchi_max(1, nItemLen); - break; - case 's'|FORCE_ANSI: - case 'S'|FORCE_ANSI: - nItemLen = strlen(va_arg(argList, char*)); - nItemLen = inchi_max(1, nItemLen); - break; - - case 's'|FORCE_UNICODE: - case 'S'|FORCE_UNICODE: - goto exit_error; /* no UNICODE, please */ - /* - nItemLen = wcslen(va_arg(argList, wchar_t*)); - nItemLen = inchi_max(1, nItemLen); - break; - */ - - } - - /* adjust nItemLen for strings */ - if (nItemLen != 0) - { - nItemLen = inchi_max(nItemLen, nWidth); - if (nPrecision != 0) - nItemLen = inchi_min(nItemLen, nPrecision); - } - else - { - switch (*lpsz) - { - /* integers */ - case 'd': - case 'i': - case 'u': - case 'x': - case 'X': - case 'o': - va_arg(argList, int); - nItemLen = 32; - nItemLen = inchi_max(nItemLen, nWidth+nPrecision); - break; - - case 'e': - case 'f': - case 'g': - case 'G': - va_arg(argList, double); - nItemLen = 32; - nItemLen = inchi_max(nItemLen, nWidth+nPrecision); - break; - - case 'p': - va_arg(argList, void*); - nItemLen = 32; - nItemLen = inchi_max(nItemLen, nWidth+nPrecision); - break; - - /* no output */ - case 'n': - va_arg(argList, int*); - break; - - default: - /*ASSERT(FALSE);*/ /* unknown formatting option*/ - goto exit_error; /* instead of exception */ - } - } - - /* adjust nMaxLen for output nItemLen */ - nMaxLen += nItemLen; - } - return nMaxLen; - -exit_error: - return -1; /* wrong format */ -} - +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include +#include +#include + +#include "mode.h" +#include "ichi_io.h" +#include "ichicomp.h" +#include "ichidrp.h" +#include "util.h" + +#ifndef INCHI_ADD_STR_LEN +#define INCHI_ADD_STR_LEN 32768 +#endif + +#ifdef TARGET_LIB_FOR_WINCHI +extern void (*FWPRINT) (const char * format, va_list argptr ); +#endif + + +/* Internal functions */ + +int inchi_ios_str_getc( INCHI_IOSTREAM *ios ); +char *inchi_ios_str_gets( char *szLine, int len, INCHI_IOSTREAM *ios ); +char *inchi_ios_str_getsTab( char *szLine, int len, INCHI_IOSTREAM *ios ); +int GetMaxPrintfLength( const char *lpszFormat, va_list argList); +char *inchi_fgetsTab( char *szLine, int len, FILE *f ); +int inchi_vfprintf( FILE* f, const char* lpszFormat, va_list argList ); + + +/* + INCHI_IOSTREAM OPERATIONS +*/ + + +/* Init INCHI_IOSTREAM */ +void inchi_ios_init(INCHI_IOSTREAM* ios, int io_type, FILE *f) +{ + memset( ios, 0, sizeof(*ios) ); + switch (io_type) + { + case INCHI_IOSTREAM_TYPE_FILE: + ios->type = INCHI_IOSTREAM_TYPE_FILE; + break; + case INCHI_IOSTREAM_TYPE_STRING: + default: + ios->type = INCHI_IOSTREAM_TYPE_STRING; + break; + } + ios->f = f; + return; +} + + +/* Make a copy of INCHI_IOSTREAM */ +int inchi_ios_create_copy(INCHI_IOSTREAM* ios, INCHI_IOSTREAM* ios0) +{ + if ( ios ) + memset( ios, 0, sizeof(*ios) ); + ios->type = ios0->type; + if ( ios->type == INCHI_IOSTREAM_TYPE_STRING ) + { + if ( ios->s.pStr ) + inchi_free( ios->s.pStr ); + ios->s.pStr = (char *) inchi_calloc( ios0->s.nAllocatedLength, sizeof(char) ); + if ( ios->s.pStr ) + { + ios->s.nUsedLength = ios0->s.nUsedLength; + ios->s.nPtr = ios0->s.nPtr; + } + else + return -1; /* no memory */ + } + ios->f = ios0->f; + return 0; +} + + +/* If INCHI_IOSTREAM type is INCHI_IOSTREAM_TYPE_STRING + and associated file exists, flush the string buffer + to that file, then free the buffer. + If INCHI_IOSTREAM type is INCHI_IOSTREAM_TYPE_FILE, + just flush the file. */ +void inchi_ios_flush(INCHI_IOSTREAM* ios) +{ + + if (ios->type == INCHI_IOSTREAM_TYPE_STRING) + { + if (ios->s.pStr) + { + if (ios->s.nUsedLength > 0) + { + if (ios->f) + { + fprintf(ios->f,"%-s", ios->s.pStr); + fflush(ios->f); + } + inchi_free(ios->s.pStr ); + ios->s.pStr = NULL; + ios->s.nUsedLength = ios->s.nAllocatedLength = ios->s.nPtr = 0; + } + } + } + + else if (ios->type == INCHI_IOSTREAM_TYPE_FILE) + { + /* output to plain file: just flush it. */ + fflush(ios->f); + } + + return; +} + + +/* If INCHI_IOSTREAM type is INCHI_IOSTREAM_TYPE_STRING, + flush INCHI_IOSTREAM string buffer to associated file + (if non-NULL) echoing to another file (supplied as + parameter; typically, stderr); then free buffer. + If INCHI_IOSTREAM type is INCHI_IOSTREAM_TYPE_FILE, + just flush the both files. */ +void inchi_ios_flush2(INCHI_IOSTREAM* ios, FILE *f2) +{ + + if (ios->type == INCHI_IOSTREAM_TYPE_STRING) + { + if (ios->s.pStr) + { + if (ios->s.nUsedLength > 0) + { + if (ios->f) + { + fprintf(ios->f,"%-s", ios->s.pStr); + fflush(ios->f); + } + if (f2!=ios->f) + fprintf(f2,"%-s", ios->s.pStr); + + inchi_free(ios->s.pStr ); + ios->s.pStr = NULL; + ios->s.nUsedLength = ios->s.nAllocatedLength = ios->s.nPtr = 0; + } + } + } + + else if (ios->type == INCHI_IOSTREAM_TYPE_FILE) + { + /* output to plain file: just flush it. */ + if ( (ios->f) && (ios->f!=stderr) && (ios->f!=stdout) ) + fflush(ios->f); + if ( f2 && f2!=stderr && f2!=stdout) + fflush(f2); + } + + return; +} + + +/* Close INCHI_IOSTREAM: free string buffer and close associated file. */ +void inchi_ios_close(INCHI_IOSTREAM* ios) +{ + if ( NULL==ios ) + return; + if (ios->s.pStr) + { + inchi_free(ios->s.pStr); + } + ios->s.pStr = NULL; + ios->s.nUsedLength = ios->s.nAllocatedLength = ios->s.nPtr = 0; + + if ( NULL!=ios->f && stdout!=ios->f && stderr!=ios->f && stdin!=ios->f ) + fclose(ios->f); + + return; +} + + +/* Reset INCHI_IOSTREAM: set string buffer ptr to NULL + (but do _not_ free memory)and close associated file. */ +void inchi_ios_reset(INCHI_IOSTREAM* ios) +{ + ios->s.pStr = NULL; + ios->s.nUsedLength = ios->s.nAllocatedLength = ios->s.nPtr = 0; + if ( NULL!=ios->f && stdout!=ios->f && stderr!=ios->f && stdin!=ios->f ) + fclose(ios->f); + + return; +} + + +/* Reset INCHI_IOSTREAM: set string buffer ptr to NULL + (after freeing memory) but do not close associated file. */ +void inchi_ios_free_str(INCHI_IOSTREAM *ios) +{ + if ( NULL==ios ) + return; + if ( ios->s.pStr && ios->s.nAllocatedLength) + inchi_free(ios->s.pStr); + ios->s.pStr = NULL; + ios->s.nUsedLength = ios->s.nAllocatedLength = ios->s.nPtr = 0; + + return; +} + + + +/* [str] getc() */ +int inchi_ios_str_getc(INCHI_IOSTREAM *ios) +{ +int c; + if (ios->type==INCHI_IOSTREAM_TYPE_STRING) + { + if ( ios->s.nPtr < ios->s.nUsedLength ) + { + return (int)ios->s.pStr[ios->s.nPtr++]; + } + return EOF; + } + + else if (ios->type==INCHI_IOSTREAM_TYPE_FILE) + { + c = fgetc( ios->f ); + if ( ferror( ios->f ) ) + c = EOF; + return c; + } + + /* error */ + return EOF; +} + + +/* [str] gets() */ +char *inchi_ios_str_gets(char *szLine, int len, INCHI_IOSTREAM *f) +{ +int length=0, c=0; + if ( -- len < 0 ) + { + return NULL; + } + while ( length < len && EOF != (c = inchi_ios_str_getc( f )) ) + { + szLine[length++] = (char)c; + if ( c == '\n' ) + break; + } + if ( !length && EOF == c ) + { + return NULL; + } + szLine[length] = '\0'; + return szLine; +} + + +/* Read up to tab or LF but not more than len chars; + if empty line, read further until a non-empty line; + remove leading and trailing white spaces; + keep zero termination */ +char *inchi_ios_str_getsTab( char *szLine, int len, INCHI_IOSTREAM *f ) +{ +int length=0, c=0; + if ( --len < 0 ) + { + return NULL; + } + while ( length < len && EOF != (c = inchi_ios_str_getc(f)) ) + { + if ( c == '\t' ) + c = '\n'; + szLine[length++] = (char) c; + if ( c == '\n' ) + break; + } + if ( !length && EOF == c ) + { + return NULL; + } + szLine[length] = '\0'; + return szLine; +} + + +/* gets() */ +int inchi_ios_gets( char *szLine, int len, INCHI_IOSTREAM *f, int *bTooLongLine ) +{ +int length; +char *p; + do + { + p = inchi_ios_str_gets( szLine, len-1, f ); + if ( !p ) + { + *bTooLongLine = 0; + return -1; /* end of file or cannot read */ + } + szLine[len-1] = '\0'; + /* + *bTooLongLine = !strchr( szLine, '\n' ); + */ + p = strchr( szLine, '\n' ); + *bTooLongLine = ( !p && ((int)strlen(szLine)) == len-2 ); + lrtrim( szLine, &length ); + } while ( !length ); + + return length; +} + + +/* Read up to tab or LF but not more than len chars; + if empty line, read further until a non-empty line; + remove leading and trailing white spaces; + keep zero termination */ +int inchi_ios_getsTab( char *szLine, int len, INCHI_IOSTREAM *f, int *bTooLongLine ) +{ +int length; +char *p; + do + { + p = inchi_ios_str_getsTab( szLine, len-1, f ); + if ( !p ) + { + *bTooLongLine = 0; + return -1; /* end of file or cannot read */ + } + szLine[len-1] = '\0'; + /* + *bTooLongLine = !strchr( szLine, '\n' ); + */ + p = strchr( szLine, '\n' ); + *bTooLongLine = ( !p && ((int)strlen(szLine)) == len-2 ); + lrtrim( szLine, &length ); + } while ( !length ); + return length; +} + + +int inchi_ios_getsTab1( char *szLine, int len, INCHI_IOSTREAM *f, int *bTooLongLine ) +{ +int length; +char *p; + p = inchi_ios_str_getsTab( szLine, len-1, f ); + if ( !p ) + { + *bTooLongLine = 0; + return -1; /* end of file or cannot read */ + } + szLine[len-1] = '\0'; + p = strchr( szLine, '\n' ); + *bTooLongLine = ( !p && ((int)strlen(szLine)) == len-2 ); + lrtrim( szLine, &length ); + return length; +} + + +/* General procedure for printing to INCHI_IOSTREAM */ +int inchi_ios_print( INCHI_IOSTREAM * ios, const char* lpszFormat, ... ) +{ +int ret=0, ret2=0; +va_list argList; + + if (!ios) + return -1; + + if (ios->type == INCHI_IOSTREAM_TYPE_STRING) + { + /* output to string buffer */ + int max_len; + my_va_start( argList, lpszFormat ); + max_len = GetMaxPrintfLength( lpszFormat, argList); + va_end( argList ); + if ( max_len >= 0 ) + { + if ( ios->s.nAllocatedLength - ios->s.nUsedLength <= max_len ) + { + /* enlarge output string */ + int nAddLength = inchi_max( INCHI_ADD_STR_LEN, max_len ); + char *new_str = + (char *) inchi_calloc( ios->s.nAllocatedLength + nAddLength, sizeof(char) ); + if ( new_str ) + { + if ( ios->s.pStr ) + { + if ( ios->s.nUsedLength > 0 ) + memcpy( new_str, ios->s.pStr, sizeof(new_str[0])* ios->s.nUsedLength ); + inchi_free( ios->s.pStr ); + } + ios->s.pStr = new_str; + ios->s.nAllocatedLength += nAddLength; + } + else return -1; /* failed */ + } + /* output */ + my_va_start( argList, lpszFormat ); + ret = vsprintf( ios->s.pStr + ios->s.nUsedLength, lpszFormat, argList ); + va_end(argList); + if ( ret >= 0 ) + ios->s.nUsedLength += ret; +#ifdef TARGET_LIB_FOR_WINCHI + if( FWPRINT ) + { + my_va_start( argList, lpszFormat ); + FWPRINT( lpszFormat, argList ); + va_end( argList ); + } +#endif + return ret; + } + return -1; + } + + else if (ios->type == INCHI_IOSTREAM_TYPE_FILE) + { + /* output to file */ + if (ios->f) + { + my_va_start( argList, lpszFormat ); + ret = vfprintf( ios->f, lpszFormat, argList ); + va_end( argList ); + } + else + { + my_va_start( argList, lpszFormat ); + ret2 = vfprintf( stdout, lpszFormat, argList ); + va_end( argList ); + } +#ifdef TARGET_LIB_FOR_WINCHI + if( FWPRINT ) + { + my_va_start( argList, lpszFormat ); + FWPRINT( lpszFormat, argList ); + va_end( argList ); + } +#endif + return ret? ret : ret2; + } + + + /* no output */ + return 0; +} + + +/* This function's output should not be displayed in the output pane */ +int inchi_ios_print_nodisplay( INCHI_IOSTREAM * ios, const char* lpszFormat, ... ) +{ +va_list argList; + + if (!ios) + return -1; + + if (ios->type == INCHI_IOSTREAM_TYPE_STRING) + { + /* output to string buffer */ + int ret=0, max_len; + my_va_start( argList, lpszFormat ); + max_len = GetMaxPrintfLength( lpszFormat, argList); + va_end( argList ); + if ( max_len >= 0 ) + { + if ( ios->s.nAllocatedLength - ios->s.nUsedLength <= max_len ) + { + /* enlarge output string */ + int nAddLength = inchi_max( INCHI_ADD_STR_LEN, max_len ); + char *new_str = (char *)inchi_calloc( ios->s.nAllocatedLength + nAddLength, sizeof(new_str[0]) ); + if ( new_str ) + { + if ( ios->s.pStr ) + { + if ( ios->s.nUsedLength > 0 ) + { + memcpy( new_str, ios->s.pStr, sizeof(new_str[0])*ios->s.nUsedLength ); + } + inchi_free( ios->s.pStr ); + } + ios->s.pStr = new_str; + ios->s.nAllocatedLength += nAddLength; + } else + { + return -1; /* failed */ + } + } + /* output */ + my_va_start( argList, lpszFormat ); + ret = vsprintf( ios->s.pStr + ios->s.nUsedLength, lpszFormat, argList ); + va_end(argList); + if ( ret >= 0 ) + { + ios->s.nUsedLength += ret; + } + return ret; + } + return -1; + } + + else if (ios->type == INCHI_IOSTREAM_TYPE_FILE) + { + my_va_start( argList, lpszFormat ); + inchi_print_nodisplay( ios->f, lpszFormat, argList); + va_end(argList); + } + + /* no output */ + return 0; +} + + +/* This function's flushes previously hidden output and resets string stream + returns n chars on success, otherwise -1 +*/ +int inchi_ios_flush_not_displayed( INCHI_IOSTREAM * ios ) +{ +char *obuf = NULL; +int ret; + + if ( !ios ) return -1; + + obuf = (char *) inchi_calloc( ios->s.nUsedLength + 1, sizeof(char) ); + + if ( !obuf ) return -1; + + strcpy( obuf, ios->s.pStr); + ios->s.nUsedLength = 0; + ret = inchi_ios_print( ios, "%s", obuf ); + inchi_free( obuf ); + + return ret; +} + + +/* Print to string buffer or to file+stderr */ +int inchi_ios_eprint( INCHI_IOSTREAM * ios, const char* lpszFormat, ... ) +{ +int ret=0, ret2=0; +va_list argList; + + if ( !ios ) + return -1; + + if (ios->type == INCHI_IOSTREAM_TYPE_STRING) + /* was #if ( defined(TARGET_API_LIB) || defined(INCHI_STANDALONE_EXE) ) */ + { + /* output to string buffer */ + int max_len, nAddLength = 0; + char *new_str = NULL; + + my_va_start( argList, lpszFormat ); + max_len = GetMaxPrintfLength( lpszFormat, argList); + va_end( argList ); + + if ( max_len >= 0 ) + { + if ( ios->s.nAllocatedLength - ios->s.nUsedLength <= max_len ) + { + /* enlarge output string */ + nAddLength = inchi_max( INCHI_ADD_STR_LEN, max_len ); + new_str = (char *)inchi_calloc( ios->s.nAllocatedLength + nAddLength, sizeof(new_str[0]) ); + if ( new_str ) + { + if ( ios->s.pStr ) + { + if ( ios->s.nUsedLength > 0 ) + { + memcpy( new_str, ios->s.pStr, sizeof(new_str[0])* ios->s.nUsedLength ); + } + inchi_free( ios->s.pStr ); + } + ios->s.pStr = new_str; + ios->s.nAllocatedLength += nAddLength; + } + else + { + return -1; /* failed */ + } + } + + /* output */ + my_va_start( argList, lpszFormat ); + ret = vsprintf( ios->s.pStr + ios->s.nUsedLength, lpszFormat, argList ); + va_end(argList); + if ( ret >= 0 ) + { + ios->s.nUsedLength += ret; + } + return ret; + } + return -1; + } + + else if (ios->type == INCHI_IOSTREAM_TYPE_FILE) + { + if ( ios->f) + { + /* output to plain file */ + my_va_start( argList, lpszFormat ); + ret = inchi_vfprintf( ios->f, lpszFormat, argList ); + va_end( argList ); + /* No output to stderr from within dll or GUI program */ +#if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) ) + if ( ios->f != stderr ) + { + my_va_start( argList, lpszFormat ); + ret2 = vfprintf( stderr, lpszFormat, argList ); + va_end( argList ); + } +#endif + return ret? ret : ret2; + } + } + + /* no output */ + return 0; +} + + +/* PLAIN FILE OPERATIONS */ + + +/* Print to file, echoing to stderr */ +int inchi_fprintf( FILE* f, const char* lpszFormat, ... ) +{ +int ret=0, ret2=0; +va_list argList; + if (f) + { + my_va_start( argList, lpszFormat ); + ret = inchi_vfprintf( f, lpszFormat, argList ); + va_end( argList ); + /* No output to stderr from within dll or GUI program */ +#if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) ) + if ( f != stderr ) + { + my_va_start( argList, lpszFormat ); + ret2 = vfprintf( stderr, lpszFormat, argList ); + va_end( argList ); + } +#endif + return ret? ret : ret2; + } + return 0; +} + + +/* Print to file */ +int inchi_vfprintf( FILE* f, const char* lpszFormat, va_list argList ) +{ +int ret=0; + if ( f == stderr && lpszFormat && lpszFormat[0] && '\r' == lpszFormat[strlen(lpszFormat)-1] ) + { +#define CONSOLE_LINE_LEN 80 +#ifndef COMPILE_ANSI_ONLY + char szLine[CONSOLE_LINE_LEN]; + ret = _vsnprintf( szLine, CONSOLE_LINE_LEN-1, lpszFormat, argList ); + if ( ret < 0 ) + { + /* output is longer than the console line */ + /* Fixed bug: (CONSOLE_LINE_LEN-4) --> (CONSOLE_LINE_LEN-4-1) 11-22-08 IPl */ + strcpy(szLine+CONSOLE_LINE_LEN-5, "...\r"); + } + fputs( szLine, f ); +#else + ret = vfprintf( f, lpszFormat, argList ); +#endif +#undef CONSOLE_LINE_LEN + } + else + { + ret = vfprintf( f, lpszFormat, argList ); + } + return ret; +} + + +/* This function's output should not be displayed in the output pane */ +int inchi_print_nodisplay( FILE* f, const char* lpszFormat, ... ) +{ +int ret=0; +va_list argList; +FILE* fi; + if (f) + fi = f; + else + fi = stdout; + my_va_start( argList, lpszFormat ); + ret = vfprintf( fi, lpszFormat, argList ); + return ret; +} + + +#if ( FIX_READ_LONG_LINE_BUG == 1 ) +/********************************************************************/ +int inchi_fgetsLfTab( char *szLine, int len, FILE *f ) +{ + int length; + char *p; + char szSkip[256]; + int bTooLongLine = 0; + do { + p = inchi_fgetsTab( szLine, len, f ); + if ( !p ) { + return -1; /* end of file or cannot read */ + } + bTooLongLine = ( (int)strlen(szLine) == len-1 && szLine[len-2] != '\n' ); + lrtrim( szLine, &length ); + } while ( !length ); + if ( bTooLongLine ) { + while ( p = inchi_fgetsTab( szSkip, sizeof(szSkip)-1, f ) ) { + if ( strchr( szSkip, '\n' ) ) + break; + } + } + return length; +} +#else +/********************************************************************/ +int inchi_fgetsLfTab( char *szLine, int len, FILE *f ) +{ + int length; + char *p; + char szSkip[256]; + int bTooLongLine = 0; + do { + p = inchi_fgetsTab( szLine, len-1, f ); + if ( !p ) { + return -1; /* end of file or cannot read */ + } + szLine[len-1] = '\0'; + /* + bTooLongLine = !strchr( szLine, '\n' ); + */ + bTooLongLine = ( !p && ((int)strlen(szLine)) == len-2 ); + lrtrim( szLine, &length ); + } while ( !length ); + if ( bTooLongLine ) { + while ( p = inchi_fgetsTab( szSkip, sizeof(szSkip)-1, f ) ) { + szSkip[sizeof(szSkip)-1] = '\0'; + if ( strchr( szSkip, '\n' ) ) + break; + } + } + return length; +} +#endif + + +/* Read up to tab or LF but not more than len chars; + if empty line, read further until a non-empty line; + remove leading and trailing white spaces; + keep zero termination */ +char *inchi_fgetsTab( char *szLine, int len, FILE *f ) +{ + int length=0, c=0; + len --; + while ( length < len && EOF != (c = fgetc( f )) ) { + if ( c == '\t' ) + c = '\n'; + szLine[length++] = (char)c; + if ( c == '\n' ) + break; + } + if ( !length && EOF == c ) { + return NULL; + } + szLine[length] = '\0'; + return szLine; +} + + +/* Read up to LF but not more than line_len bytes; + if input line is too long, quietly ignore the rest of the line */ +char* inchi_fgetsLf( char* line, int line_len, INCHI_IOSTREAM* inp_stream ) +{ +char *p=NULL, *q; +FILE* finp=NULL; + + if ( inp_stream->type == INCHI_IOSTREAM_TYPE_FILE ) + { + /* Read from file */ + finp = inp_stream->f; + memset( line, 0, line_len ); + if ( NULL != (p = fgets( line, line_len, finp ) ) && + NULL == strchr(p, '\n' ) ) + { + char temp[64]; + /* bypass up to '\n' or up to end of file whichever comes first */ + while ( NULL != fgets( temp, sizeof(temp), finp ) && NULL == strchr(temp,'\n') ) + ; + } + } + else if ( inp_stream->type == INCHI_IOSTREAM_TYPE_STRING ) + { + /* Read from supplied string representing Molfile */ + memset( line, 0, line_len ); + if ( NULL != (p = inchi_sgets( line, line_len, inp_stream ) ) && + NULL == strchr(p, '\n' ) ) + { + char temp[64]; + /* bypass up to '\n' or up to end of file whichever comes first */ + while ( NULL != inchi_sgets( temp, sizeof(temp), inp_stream ) && NULL == strchr(temp,'\n') ) + ; + } + } + else + ; + + if ( p ) + { + if (q = strchr(line, '\r') ) + { + /* fix CR CR LF line terminator. */ + q[0] = '\n'; + q[1] = '\0'; + } + } + + return p; +} + + +/* Estimate printf string length. + + The code is based on Microsoft Knowledge Base article Q127038: + "FIX: CString::Format Gives Assertion Failed, Access Violation" + (Related to Microsoft Visual C++, 32-bit Editions, versions 2.0, 2.1) */ + +#define FORCE_ANSI 0x10000 +#define FORCE_UNICODE 0x20000 + +/* Formatting (using wsprintf style formatting) */ +int GetMaxPrintfLength( const char *lpszFormat, va_list argList) +{ + /*ASSERT(AfxIsValidString(lpszFormat, FALSE));*/ + const char * lpsz; + int nMaxLen, nWidth, nPrecision, nModifier, nItemLen; + + nMaxLen = 0; + /* make a guess at the maximum length of the resulting string */ + for ( lpsz = lpszFormat; *lpsz; lpsz ++ ) + { + /* handle '%' character, but watch out for '%%' */ + if (*lpsz != '%' || *( ++ lpsz ) == '%') + { + nMaxLen += 1; + continue; + } + + nItemLen = 0; + + /* handle '%' character with format */ + nWidth = 0; + for (; *lpsz; lpsz ++ ) + { + /* check for valid flags */ + if (*lpsz == '#') + nMaxLen += 2; /* for '0x' */ + else if (*lpsz == '*') + nWidth = va_arg(argList, int); + else if (*lpsz == '-' || *lpsz == '+' || *lpsz == '0' + || *lpsz == ' ') + ; + else /* hit non-flag character */ + break; + } + /* get width and skip it */ + if (nWidth == 0) + { + /* width indicated by */ + nWidth = atoi(lpsz); + for (; *lpsz && isdigit(*lpsz); lpsz ++ ) + ; + } + /*ASSERT(nWidth >= 0);*/ + if ( nWidth < 0 ) + goto exit_error; /* instead of exception */ + + nPrecision = 0; + if (*lpsz == '.') + { + /* skip past '.' separator (width.precision)*/ + lpsz ++; + + /* get precision and skip it*/ + if (*lpsz == '*') + { + nPrecision = va_arg(argList, int); + lpsz ++; + } + else + { + nPrecision = atoi(lpsz); + for (; *lpsz && isdigit(*lpsz); lpsz ++) + ; + } + if ( nPrecision < 0 ) + goto exit_error; /* instead of exception */ + } + + /* should be on type modifier or specifier */ + nModifier = 0; + switch (*lpsz) + { + /* modifiers that affect size */ + case 'h': + switch ( lpsz[1] ) { + case 'd': + case 'i': + case 'o': + case 'x': + case 'X': + case 'u': + /* short unsigned, short double, etc. -- added to the original MS example */ + /* ignore the fact that these modifiers do affect size */ + lpsz ++; + break; + default: + nModifier = FORCE_ANSI; + lpsz ++; + break; + } + break; + case 'l': + switch ( lpsz[1] ) { + case 'd': + case 'i': + case 'o': + case 'x': + case 'X': + case 'u': + case 'f': /* long float -- post ANSI C */ + /* long unsigned, long double, etc. -- added to the original MS example */ + /* ignore the fact that these modifiers do affect size */ + lpsz ++; + break; + default: + /* + nModifier = FORCE_UNICODE; + lpsz ++; + break; + */ + goto exit_error; /* no UNICODE, please */ + } + break; + /* modifiers that do not affect size */ + case 'F': + case 'N': + case 'L': + lpsz ++; + break; + } + + /* now should be on specifier */ + switch (*lpsz | nModifier) + { + /* single characters*/ + case 'c': + case 'C': + nItemLen = 2; + va_arg(argList, int); + break; + case 'c'|FORCE_ANSI: + case 'C'|FORCE_ANSI: + nItemLen = 2; + va_arg(argList, int); + break; + case 'c'|FORCE_UNICODE: + case 'C'|FORCE_UNICODE: + goto exit_error; /* no UNICODE, please */ + /* + nItemLen = 2; + va_arg(argList, wchar_t); + break; + */ + + /* strings*/ + case 's': + case 'S': + nItemLen = (int) strlen(va_arg(argList, char*)); + nItemLen = inchi_max(1, nItemLen); + break; + case 's'|FORCE_ANSI: + case 'S'|FORCE_ANSI: + nItemLen = (int) strlen(va_arg(argList, char*)); + nItemLen = inchi_max(1, nItemLen); + break; + + case 's'|FORCE_UNICODE: + case 'S'|FORCE_UNICODE: + goto exit_error; /* no UNICODE, please */ + /* + nItemLen = wcslen(va_arg(argList, wchar_t*)); + nItemLen = inchi_max(1, nItemLen); + break; + */ + } + + /* adjust nItemLen for strings */ + if (nItemLen != 0) + { + nItemLen = inchi_max(nItemLen, nWidth); + if (nPrecision != 0) + nItemLen = inchi_min(nItemLen, nPrecision); + } + else + { + switch (*lpsz) + { + /* integers */ + case 'd': + case 'i': + case 'u': + case 'x': + case 'X': + case 'o': + va_arg(argList, int); + nItemLen = 32; + nItemLen = inchi_max(nItemLen, nWidth+nPrecision); + break; + + case 'e': + case 'f': + case 'g': + case 'G': + va_arg(argList, double); + nItemLen = 32; + nItemLen = inchi_max(nItemLen, nWidth+nPrecision); + break; + + case 'p': + va_arg(argList, void*); + nItemLen = 32; + nItemLen = inchi_max(nItemLen, nWidth+nPrecision); + break; + + /* no output */ + case 'n': + va_arg(argList, int*); + break; + + default: + /*ASSERT(FALSE);*/ /* unknown formatting option*/ + goto exit_error; /* instead of exception */ + } + } + + /* adjust nMaxLen for output nItemLen */ + nMaxLen += nItemLen; + } + return nMaxLen; + +exit_error: + return -1; /* wrong format */ +} + + +/* Get at most n-1 chars, plus a null, then advance input's start. + Return emulates fgets() +*/ +char *inchi_sgets(char *s, int n, INCHI_IOSTREAM* ios) +{ +int c=0; +char *p; +char *inp; + + inp = ios->s.pStr + ios->s.nPtr; + + if ( n<=0 ) + return NULL; + + if ( NULL==inp ) + /* like EOF */ + return NULL; + + p = s; + /* + if ( *inp == '\0' ) + s = '\0'; + else + */ + while ( --n>0 && ( c = *inp++ ) ) + { + ios->s.nPtr++; + if ( (*p++ = c) == '\n') + break; + } + *p = '\0'; + + /* printf("\n*** {%-s}",s); */ + + return ( c=='\0' && p==s) ? NULL /* like EOF reached */ + : s; +} + + +/* Init expandable buffer of type INCHI_IOSTREAM_STRING */ +int inchi_strbuf_init( INCHI_IOSTREAM_STRING *buf, + int start_size, + int incr_size ) +{ +char *new_str=NULL; + memset( buf, 0, sizeof(*buf) ); + + if ( start_size <= 0 ) start_size = INCHI_STRBUF_INITIAL_SIZE; + if ( incr_size <= 0 ) incr_size = INCHI_STRBUF_SIZE_INCREMENT; + + new_str = (char *) inchi_calloc( start_size, sizeof(new_str[0]) ); + + if ( !new_str ) return -1; + + buf->pStr = new_str; + buf->nAllocatedLength = start_size; + buf->nPtr = incr_size; + + return start_size; +} + + +/* Reset INCHI_IOSTREAM_STRING object holding an expandable buffer string + (place '\0' at the start and do _not_ free memory). */ +void inchi_strbuf_reset( INCHI_IOSTREAM_STRING *buf ) +{ + if ( !buf ) return; + if ( buf->pStr) buf->pStr[0] = '\0'; + buf->nUsedLength = buf->nPtr = 0; +} + + +/* Close INCHI_IOSTREAM_STRING object holding an expandable buffer string, + free previously allocated sring memory */ +void inchi_strbuf_close( INCHI_IOSTREAM_STRING *buf ) +{ + if ( !buf ) return; + if ( buf->pStr ) inchi_free( buf->pStr ); + memset( buf, 0, sizeof(*buf) ); +} + +int inchi_strbuf_create_copy( INCHI_IOSTREAM_STRING *buf2, INCHI_IOSTREAM_STRING *buf ) +{ +char *new_str=NULL; + + new_str = (char *) inchi_calloc( buf->nAllocatedLength, sizeof(new_str[0]) ); + + buf2->pStr = new_str; + if ( !new_str ) return -1; + + buf2->nAllocatedLength = buf->nAllocatedLength; + buf2->nUsedLength = buf->nUsedLength; + buf2->nPtr = buf->nPtr; + + return 0; +} + +/* Check size and if necessary expand string buffer in INCHI_IOSTREAM_STRING*/ +int inchi_strbuf_update( INCHI_IOSTREAM_STRING *buf, int new_addition_size ) +{ + int requsted_len; + + if ( !buf ) return -1; + + if ( new_addition_size <= 0 ) return buf->nAllocatedLength; + + requsted_len = buf->nUsedLength + new_addition_size; + + if ( requsted_len >= buf->nAllocatedLength ) + { + /* Expand */ + int nAddLength = inchi_max( buf->nPtr, new_addition_size ); + /* buf->nPtr stores size increment for this buffer */ + char *new_str = + (char *) inchi_calloc( buf->nAllocatedLength + nAddLength, + sizeof(new_str[0]) ); + if ( !new_str ) + return -1; /* failed */ + if ( buf->pStr ) + { + if ( buf->nUsedLength > 0 ) + memcpy( new_str, buf->pStr, sizeof(new_str[0])* buf->nUsedLength ); + inchi_free( buf->pStr ); + } + buf->pStr = new_str; + buf->nAllocatedLength += nAddLength; + } + + return buf->nAllocatedLength; +} + + +/* Add to the end of string in INCHI_IOSTREAM_STRING object, + expanding buffer if necessary */ +int inchi_strbuf_printf( INCHI_IOSTREAM_STRING *buf, const char* lpszFormat, ... ) +{ +int ret=0, max_len; +va_list argList; + + if ( !buf ) return -1; + + my_va_start( argList, lpszFormat ); + max_len = GetMaxPrintfLength( lpszFormat, argList); + va_end( argList ); + if ( max_len < 0 ) return 0; + + inchi_strbuf_update( buf, max_len ); + + my_va_start( argList, lpszFormat ); + ret = vsprintf( buf->pStr + buf->nUsedLength, lpszFormat, argList ); + va_end(argList); + if ( ret >= 0 ) + buf->nUsedLength += ret; + + return ret; +} + + +/* Print to string in INCHI_IOSTREAM_STRING object + from specified position 'npos', expanding buffer if necessary. + NB: be careful, intentionally no checks on where is 'npos'! */ +int inchi_strbuf_printf_from( INCHI_IOSTREAM_STRING *buf, + int npos, + const char* lpszFormat, ... ) +{ +int ret=0, max_len; +va_list argList; + + if ( !buf ) return -1; + + my_va_start( argList, lpszFormat ); + max_len = GetMaxPrintfLength( lpszFormat, argList); + va_end( argList ); + if ( max_len < 0 ) return 0; + + max_len += npos; + + inchi_strbuf_update( buf, max_len ); + + my_va_start( argList, lpszFormat ); + ret = vsprintf( buf->pStr + npos, lpszFormat, argList ); + va_end(argList); + if ( ret >= 0 ) + buf->nUsedLength = npos + ret; + + return ret; +} + + +/* + Reads the next line to growing str buf. + Returns n of read chars, -1 at end of file or at error. + */ +int inchi_strbuf_getline(INCHI_IOSTREAM_STRING *buf, FILE *f, int crlf2lf, int preserve_lf ) +{ +int c; + inchi_strbuf_reset( buf ); + + while( 1 ) + { + c= fgetc( f ); + if ( ferror( f ) ) return -1; + if ( c == EOF ) return -1; + inchi_strbuf_printf( buf, "%c", c ); + if ( c == '\n' ) break; + } + + if ( crlf2lf ) + { + if ( buf->nUsedLength > 2 ) + { + if ( buf->pStr[ buf->nUsedLength - 2] == '\r' ) + { + buf->pStr[ buf->nUsedLength - 2] = '\n'; + buf->pStr[ --buf->nUsedLength ] = '\0'; + } + } + } + if ( !preserve_lf ) + { + buf->pStr[ --buf->nUsedLength ] = '\0'; + } + + return buf->nUsedLength; +} + + + +/* + Adds the next line to growing str buf (does not reset buf before adding). + Returns n of read chars, -1 at end of file or at error. + */ +int inchi_strbuf_addline(INCHI_IOSTREAM_STRING *buf, INCHI_IOSTREAM *inp_stream, int crlf2lf, int preserve_lf ) +{ +int c; + + while( 1 ) + { + c = inchi_ios_str_getc(inp_stream); + if ( c == EOF ) + return -1; + inchi_strbuf_printf( buf, "%c", c ); + if ( c == '\n' ) + break; + } + if ( crlf2lf ) + { + if ( buf->nUsedLength > 2 ) + { + if ( buf->pStr[ buf->nUsedLength - 2] == '\r' ) + { + buf->pStr[ buf->nUsedLength - 2] = '\n'; + buf->pStr[ --buf->nUsedLength ] = '\0'; + } + } + } + if ( !preserve_lf ) + { + buf->pStr[ --buf->nUsedLength ] = '\0'; + } + + return buf->nUsedLength; +} + + +#if ( defined(_WIN32) && defined(_DEBUG) && defined(_MSC_VER) ) +#include +int _inchi_trace(char *format, ...) +{ + /*TCHAR buffer[32767];*/ + char buffer[32767]; + va_list argptr; + va_start(argptr, format); + /*wvsprintf(buffer, format, argptr);*/ + vsprintf(buffer, format, argptr); + va_end(argptr); + OutputDebugString(buffer); + return 1; +} +#else +int _inchi_trace(char *format, ...) { return 1; } +#endif diff --git a/INCHI-1-SRC/INCHI_BASE/src/ichi_io.h b/INCHI-1-SRC/INCHI_BASE/src/ichi_io.h new file mode 100644 index 0000000..0c31b33 --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/ichi_io.h @@ -0,0 +1,134 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _ICHI_IO_H_ +#define _ICHI_IO_H_ + + +/* + INCHI I/O (IOSTREAM MAINLY) OPERATIONS +*/ + + +#include "mode.h" +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + + +void inchi_ios_init(INCHI_IOSTREAM *ios, int io_type, FILE *f); +void inchi_ios_flush(INCHI_IOSTREAM *ios); +void inchi_ios_flush2(INCHI_IOSTREAM *ios, FILE *f2); +void inchi_ios_close(INCHI_IOSTREAM *ios); +void inchi_ios_reset(INCHI_IOSTREAM *ios); +void inchi_ios_free_str(INCHI_IOSTREAM *ios); +int inchi_ios_create_copy(INCHI_IOSTREAM* ios, INCHI_IOSTREAM* ios0); + +int inchi_ios_gets( char *szLine, int len, INCHI_IOSTREAM *ios, int *bTooLongLine ); +int inchi_ios_getsTab( char *szLine, int len, INCHI_IOSTREAM *ios, int *bTooLongLine ); +int inchi_ios_getsTab1( char *szLine, int len, INCHI_IOSTREAM *ios, int *bTooLongLine ); + +int inchi_ios_print( INCHI_IOSTREAM *ios, const char* lpszFormat, ... ); +int inchi_ios_print_nodisplay( INCHI_IOSTREAM *ios, const char* lpszFormat, ... ); +int inchi_ios_flush_not_displayed( INCHI_IOSTREAM * ios ); + +/* Print to string buffer or to file+stderr */ +int inchi_ios_eprint( INCHI_IOSTREAM *ios, const char* lpszFormat, ... ); + + +/* + PLAIN FILE OPERATIONS +*/ + +/* Print to file, echoing to stderr */ +int inchi_fprintf( FILE* f, const char* lpszFormat, ... ); +int inchi_print_nodisplay( FILE* f, const char* lpszFormat, ... ); + +char* inchi_fgetsLf( char* line, int line_len, INCHI_IOSTREAM* inp_stream ); +int inchi_fgetsLfTab( char *szLine, int len, FILE *f ); + +char *inchi_sgets(char *s, int n, INCHI_IOSTREAM* ios); + + + + +/* + Support of simplistic print buffer (growing string) +*/ + +#define INCHI_STRBUF_INITIAL_SIZE 262144 +#define INCHI_STRBUF_SIZE_INCREMENT 262144 +#define INCHI_STRBUF_SMALLER_INITIAL_SIZE 1024 +#define INCHI_STRBUF_SMALLER_SIZE_INCREMENT 4096 + +int inchi_strbuf_init( INCHI_IOSTREAM_STRING *buf, + int start_size, + int incr_size ); +void inchi_strbuf_reset( INCHI_IOSTREAM_STRING *buf ); +void inchi_strbuf_close( INCHI_IOSTREAM_STRING *buf ); +int inchi_strbuf_printf( INCHI_IOSTREAM_STRING *buf, + const char* lpszFormat, ... ); +int inchi_strbuf_printf_from( INCHI_IOSTREAM_STRING *buf, + int npos, + const char* lpszFormat, ... ); +int inchi_strbuf_create_copy( INCHI_IOSTREAM_STRING *buf2, INCHI_IOSTREAM_STRING *buf ); +int inchi_strbuf_update( INCHI_IOSTREAM_STRING *buf, int new_addition_size ); +int inchi_strbuf_getline(INCHI_IOSTREAM_STRING *buf, FILE *f, int crlf2lf, int preserve_lf ); + +int inchi_strbuf_addline(INCHI_IOSTREAM_STRING *buf, INCHI_IOSTREAM *inp_stream, int crlf2lf, int preserve_lf ); + +void print_sequence_of_nums_compressing_ranges( int n, int *num, INCHI_IOSTREAM_STRING *strbuf ); + +int _inchi_trace(char *format, ...); +#if ( defined(_WIN32) && defined(_DEBUG) && defined(_MSC_VER) ) +#define ITRACE_ _inchi_trace +#else +#define ITRACE_ 0 && _inchi_trace +#endif + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + + +#endif /* _ICHI_IO_H_ */ diff --git a/INCHI-1-SRC/INCHI/common/ichican2.c b/INCHI-1-SRC/INCHI_BASE/src/ichican2.c similarity index 77% rename from INCHI-1-SRC/INCHI/common/ichican2.c rename to INCHI-1-SRC/INCHI_BASE/src/ichican2.c index 020bb73..131e069 100644 --- a/INCHI-1-SRC/INCHI/common/ichican2.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichican2.c @@ -1,5168 +1,6483 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include - -/* #define CHECK_WIN32_VC_HEAP */ -#include "mode.h" - -#include "ichi.h" -#include "util.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "inpdef.h" -#include "ichinorm.h" -#include "ichicant.h" -#include "ichicano.h" -#include "ichicomn.h" - -#include "ichicomp.h" - -#define MAX_CELLS 1024 -#define MAX_NODES 1024 -#define MAX_SET_SIZE 2048 /*16384*/ -#define MAX_LAYERS 7 - -#define INFINITY 0x3FFF -#define EMPTY_CT 0 -#define EMPTY_H_NUMBER (INFINITY-1) -#define BASE_H_NUMBER ((INFINITY-1)/2) -#define EMPTY_ISO_SORT_KEY LONG_MAX - -#define SEPARATE_CANON_CALLS 0 - -/* #define INCHI_CANON_USE_HASH */ -#define INCHI_CANON_MIN - -/****************************************************************/ -#ifdef INCHI_CANON_USE_HASH -typedef unsigned long U_INT_32; -typedef unsigned char U_INT_08; -typedef U_INT_32 CtHash; -CtHash hash_mark_bit; -#endif - -/* -- moved to ichi_bns.h -- -typedef U_SHORT bitWord; -#define BIT_WORD_MASK ((bitWord)~0) -*/ - -static bitWord *bBit = NULL; -static int num_bit = 0; -/*bitWord mark_bit; */ /* highest bit in AT_NUMB */ -/*bitWord mask_bit; */ /* ~mark_bit */ - -AT_NUMB rank_mark_bit; -AT_NUMB rank_mask_bit; - - -typedef AT_NUMB Node; -typedef NEIGH_LIST Graph; -/* -typedef struct tagGraph { - int dummy; -} Graph; -*/ -typedef struct tagUnorderedPartition { - /* AT_NUMB *next; */ /* links */ - AT_NUMB *equ2; /* mcr */ -} UnorderedPartition; - -typedef struct tagCell { - int first; /* index of the first cell element in Partition::AtNumber[] */ - int next; /* next after the last index */ - int prev; /* position of the previously returned cell element */ -} Cell; - -#ifdef NEVER /* moved to ichi_bns.h */ -typedef struct tagNodeSet { - bitWord **bitword; - int num_set; /* number of sets */ - int len_set; /* number of bitWords in each set */ -} NodeSet; -#endif - -typedef struct tagTransposition { - AT_NUMB *nAtNumb; -} Transposition; - - -typedef struct tagCTable { - AT_RANK *Ctbl; /* connection table */ - /* Format-atoms: atom_rank[k] neigh_rank[k][1]...neigh_rank[k][n] - 1) atom_rank[k1] < atom_rank[k2] <=> k1 < k2 - where 2) atom_rank[k] > neigh_rank[k][i], i=1..n - 3) neigh_rank[k][i] < neigh_rank[k][j] <=> i < j - - Format-tgroup: tgroup_rank[k] endpoint_rank[k][1]...endpoint_rank[k][n] - where 1) tgroup_rank[k1] < tgroup_rank[k2] <=> k1 < k2 - 2) endpoint_rank[k][i] < endpoint_rank[k][j] <=> i < j - - Note: tgroup_rank[k] > endpoint_rank[k][j] for all j by construction - */ - - int lenCt; /* used length */ - int nLenCTAtOnly; /* to split Ctnl comparison in case of bDigraph != 0 */ - int maxlenCt; /* allocated length of Ctbl */ - int maxPos; /* allocated length of nextCtblPos */ - int maxVert; /* max number of vertices to separate atoms from taut groups */ - int lenPos; /* first unused element of nextCtblPos */ - AT_RANK *nextAtRank; /* rank (k value) after the last node of the Ctbl portion*/ - AT_NUMB *nextCtblPos; /* first unused element of Ctbl */ - - /* hydrogen atoms fixed in tautomeric representation: - compare before diff sign inversion: (+) <=> Ct1->() > Ct2->() */ - NUM_H *NumH; - int lenNumH; /* used length */ - int maxlenNumH; /* n + T_NUM_NO_ISOTOPIC*(n_tg-n) + 1 */ - - /* hydrogen atoms fixed in non-tautomeric representation only: - compare before diff sign inversion: (+) <=> Ct1->() > Ct2->() */ - NUM_H *NumHfixed; - /*int lenNumHfixed; */ /* used length */ - /*int maxlenNumHfixed; */ /* max length = n+1 */ - - /* isotopic atoms (without tautomeric H) and isotopic tautomeric groups */ - /* note: AT_ISO_SORT_KEY and T_GROUP_ISOWT are identical types: long */ - AT_ISO_SORT_KEY *iso_sort_key; - int len_iso_sort_key; /* used length */ - int maxlen_iso_sort_key; /* max length = n_tg+1 */ - - S_CHAR *iso_exchg_atnos; - int len_iso_exchg_atnos; - int maxlen_iso_exchg_atnos; - - /* isotopic hydrogen atoms fixed in non-tautomeric representation only */ -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - AT_ISO_SORT_KEY *iso_sort_key_Hfixed; - int len_iso_sort_key_Hfixed; /* used length */ - int maxlen_iso_sort_key_Hfixed; /* max length = n+1 */ -#endif - -#ifdef INCHI_CANON_USE_HASH - CtHash *hash; -#endif -} ConTable; -/**************************************************************/ -typedef struct tagkLeast { - int k; - int i; -} kLeast; -/*************local prototypes **********************************/ -int CanonGraph( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ); - -void CtPartFill( Graph *G, CANON_DATA *pCD, Partition *p, - ConTable *Ct, int k, int n, int n_tg ); -void CtPartClear( ConTable *Ct, int k ); -void CtPartInfinity( ConTable *Ct, S_CHAR *cmp, int k ); -int CtPartCompare( ConTable *Ct1, ConTable *Ct2, S_CHAR *cmp, - kLeast *kLeastForLayer, int k, int bOnlyCommon, int bSplitTautCompare ); -int CtFullCompare( ConTable *Ct1, ConTable *Ct2, int bOnlyCommon, int bSplitTautCompare ); -void CtPartCopy( ConTable *Ct1 /* to */, ConTable *Ct2 /* from */, int k ); -void CtFullCopy( ConTable *Ct1, ConTable *Ct2 ); - -int CtFullCompareLayers( kLeast *kLeastForLayer ); -int CtCompareLayersGetFirstDiff( kLeast *kLeast_rho, int nOneAdditionalLayer, - int *L_rho, int *I_rho, int *k_rho ); -int CtPartCompareLayers( kLeast *kLeast_rho, int L_rho_fix_prev, int nOneAdditionalLayer ); -void UpdateCompareLayers( kLeast kLeastForLayer[], int hzz ); -int GetOneAdditionalLayer( CANON_DATA *pCD, ConTable *pzb_rho_fix ); - -void CleanNumH( NUM_H *NumH, int len ); -int CleanCt( AT_RANK *Ct, int len ); -void CleanIsoSortKeys( AT_ISO_SORT_KEY * isk, int len ); -void MergeCleanIsoSortKeys( AT_ISO_SORT_KEY * isk1, AT_ISO_SORT_KEY * isk2, int len ); - -int UnorderedPartitionJoin( UnorderedPartition *p1, UnorderedPartition *p2, int n ); -Node GetUnorderedPartitionMcrNode( UnorderedPartition *p1, Node v ); -int nJoin2Mcrs2( AT_RANK *nEqArray, AT_RANK n1, AT_RANK n2 ); -AT_RANK nGetMcr2( AT_RANK *nEqArray, AT_RANK n ); -int AllNodesAreInSet( NodeSet *cur_nodes, int lcur_nodes, NodeSet *set, int lset ); -void NodeSetFromVertices( NodeSet *cur_nodes, int l, Node *v, int num_v); -void CellMakeEmpty( Cell *baseW, int k ); -Node CellGetMinNode( Partition *p, Cell *W, Node v, CANON_DATA *pCD ); -int CellGetNumberOfNodes( Partition *p, Cell *W ); -int CellIntersectWithSet( Partition *p, Cell *W, NodeSet *Mcr, int l ); - -int PartitionColorVertex( Graph *G, Partition *p, Node v, int n, int n_tg, int n_max, int bDigraph, int nNumPrevRanks ); -void PartitionCopy( Partition *To, Partition *From, int n ); -int PartitionSatisfiesLemma_2_25( Partition *p, int n ); -void PartitionGetTransposition( Partition *pFrom, Partition *pTo, int n, Transposition *gamma ); -void PartitionGetMcrAndFixSet( Partition *p, NodeSet *Mcr, NodeSet *Fix, int n, int l ); -int PartitionGetFirstCell( Partition *p, Cell *baseW, int k, int n ); -int PartitionIsDiscrete( Partition *p, int n); -void PartitionFree( Partition *p ); -int PartitionCreate( Partition *p, int n); -void UnorderedPartitionMakeDiscrete( UnorderedPartition *p, int n); -void UnorderedPartitionFree( UnorderedPartition *p ); -int UnorderedPartitionCreate( UnorderedPartition *p, int n ); -void CTableFree( ConTable *Ct ); -int CTableCreate( ConTable *Ct, int n, CANON_DATA *pCD ); -void TranspositionFree( Transposition *p ); -int TranspositionCreate( Transposition *p, int n ); -void TranspositionGetMcrAndFixSetAndUnorderedPartition( Transposition *gamma, NodeSet *McrSet, NodeSet *FixSet, int n, int l, UnorderedPartition *p ); - -void insertions_sort_NeighList_AT_NUMBERS2( NEIGH_LIST base, AT_RANK *nRank, AT_RANK max_rj ); -int WriteGraph( Graph *G, int n, int gnum, char *fname, char *fmode ); - -int SetInitialRanks2( int num_atoms, ATOM_INVARIANT2* pAtomInvariant2, AT_RANK *nNewRank, AT_RANK *nAtomNumber ); -void FillOutAtomInvariant2( sp_ATOM* at, int num_atoms, int num_at_tg, ATOM_INVARIANT2* pAtomInvariant, - int bIgnoreIsotopic, int bHydrogensInRanks, int bHydrogensFixedInRanks, - int bDigraph, int bTautGroupsOnly, T_GROUP_INFO *t_group_info ); - -int GetCanonRanking2( int num_atoms, int num_at_tg, int num_max, int bDigraph, sp_ATOM* at, - AT_RANK **pRankStack, int nNumPrevRanks, - AT_RANK *nSymmRank, AT_RANK *nCanonRank, - NEIGH_LIST *NeighList, AT_RANK *nTempRank, - CANON_STAT* pCS ); - - -#if ( SEPARATE_CANON_CALLS == 1 ) -/* for profiling purposes */ - -int CanonGraph01( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) -{ - return - CanonGraph( n, n_tg, n_max, bDigraph, G, pi , - nSymmRank, nCanonRank, nAtomNumberCanon, - pCD, pCC, - pp_zb_rho_inp, pp_zb_rho_out ); -} -int CanonGraph02( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) -{ - return - CanonGraph( n, n_tg, n_max, bDigraph, G, pi , - nSymmRank, nCanonRank, nAtomNumberCanon, - pCD, pCC, - pp_zb_rho_inp, pp_zb_rho_out ); -} -int CanonGraph03( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) -{ - return - CanonGraph( n, n_tg, n_max, bDigraph, G, pi , - nSymmRank, nCanonRank, nAtomNumberCanon, - pCD, pCC, - pp_zb_rho_inp, pp_zb_rho_out ); -} -int CanonGraph04( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) -{ - return - CanonGraph( n, n_tg, n_max, bDigraph, G, pi , - nSymmRank, nCanonRank, nAtomNumberCanon, - pCD, pCC, - pp_zb_rho_inp, pp_zb_rho_out ); -} -int CanonGraph05( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) -{ - return - CanonGraph( n, n_tg, n_max, bDigraph, G, pi , - nSymmRank, nCanonRank, nAtomNumberCanon, - pCD, pCC, - pp_zb_rho_inp, pp_zb_rho_out ); -} -int CanonGraph06( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) -{ - return - CanonGraph( n, n_tg, n_max, bDigraph, G, pi , - nSymmRank, nCanonRank, nAtomNumberCanon, - pCD, pCC, - pp_zb_rho_inp, pp_zb_rho_out ); -} -int CanonGraph07( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) -{ - return - CanonGraph( n, n_tg, n_max, bDigraph, G, pi , - nSymmRank, nCanonRank, nAtomNumberCanon, - pCD, pCC, - pp_zb_rho_inp, pp_zb_rho_out ); -} -int CanonGraph08( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) -{ - return - CanonGraph( n, n_tg, n_max, bDigraph, G, pi , - nSymmRank, nCanonRank, nAtomNumberCanon, - pCD, pCC, - pp_zb_rho_inp, pp_zb_rho_out ); -} -int CanonGraph09( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) -{ - return - CanonGraph( n, n_tg, n_max, bDigraph, G, pi , - nSymmRank, nCanonRank, nAtomNumberCanon, - pCD, pCC, - pp_zb_rho_inp, pp_zb_rho_out ); -} -int CanonGraph10( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) -{ - return - CanonGraph( n, n_tg, n_max, bDigraph, G, pi , - nSymmRank, nCanonRank, nAtomNumberCanon, - pCD, pCC, - pp_zb_rho_inp, pp_zb_rho_out ); -} -int CanonGraph11( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) -{ - return - CanonGraph( n, n_tg, n_max, bDigraph, G, pi , - nSymmRank, nCanonRank, nAtomNumberCanon, - pCD, pCC, - pp_zb_rho_inp, pp_zb_rho_out ); -} -int CanonGraph12( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) -{ - return - CanonGraph( n, n_tg, n_max, bDigraph, G, pi , - nSymmRank, nCanonRank, nAtomNumberCanon, - pCD, pCC, - pp_zb_rho_inp, pp_zb_rho_out ); -} -#else - -#define CanonGraph01 CanonGraph -#define CanonGraph02 CanonGraph -#define CanonGraph03 CanonGraph -#define CanonGraph04 CanonGraph -#define CanonGraph05 CanonGraph -#define CanonGraph06 CanonGraph -#define CanonGraph07 CanonGraph -#define CanonGraph08 CanonGraph -#define CanonGraph09 CanonGraph -#define CanonGraph10 CanonGraph -#define CanonGraph11 CanonGraph -#define CanonGraph12 CanonGraph - -#endif - -#ifdef INCHI_CANON_USE_HASH -/****************************************************************/ -static call_fill_crc32_data = 1; -static U_INT_32 crc32_data[256]; - -void fill_crc32_data() -{ - U_INT_32 c; - int n, k; - - for (n = 0; n < 256; n++) - { - c = (U_INT_32)n; - for (k = 0; k < 8; k++) - c = c & 1 ? 0xEDB88320L ^ (c >> 1) : c >> 1; - crc32_data[n] = c; - } - call_fill_crc32_data = 0; -} -/****************************************************************/ -unsigned long add2crc32( unsigned long crc32, AT_NUMB n ) -{ - U_INT_08 chr; - if (call_fill_crc32_data) { - fill_crc32_data(); - } - chr = n % 128; - crc32 = crc32_data[((int)crc32 ^ (int)chr) & 0xff] ^ (crc32 >> 8); - chr = n / 128; - crc32 = crc32_data[((int)crc32 ^ (int)chr) & 0xff] ^ (crc32 >> 8); - return crc32; -} -#endif -/****************************************************************/ -int TranspositionCreate( Transposition *p, int n ) -{ - p->nAtNumb = (AT_NUMB*)inchi_calloc( n, sizeof(p->nAtNumb[0]) ); - if ( p->nAtNumb ) { - return 1; - } - return 0; -} -/****************************************************************/ -void TranspositionFree( Transposition *p ) -{ - if ( p && p->nAtNumb ) { - inchi_free( p->nAtNumb ); - p->nAtNumb = NULL; - } -} -/****************************************************************/ -int NodeSetCreate( NodeSet *pSet, int n, int L ) -{ - int i, len; - - len = (n+ num_bit - 1)/num_bit; - - pSet->bitword = (bitWord**)inchi_calloc(L, sizeof(pSet->bitword[0])); - - if ( !pSet->bitword ) { - return 0; - } - pSet->bitword[0] = (bitWord*)inchi_calloc(len*L, sizeof(pSet->bitword[0][0])); - if ( !pSet->bitword[0] ) { - /* cleanup */ - inchi_free( pSet->bitword ); - pSet->bitword = NULL; - return 0; /* failed */ - } - for ( i = 1; i < L; i ++ ) { - pSet->bitword[i] = pSet->bitword[i-1]+len; - } - pSet->len_set = len; - pSet->num_set = L; - return 1; -} -/****************************************************************/ -void NodeSetFree( NodeSet *pSet ) -{ - if ( pSet && pSet->bitword ) { - if ( pSet->bitword[0] ) { - inchi_free( pSet->bitword[0] ); - } - inchi_free( pSet->bitword ); - pSet->bitword = NULL; - } -} -/****************************************************************/ -int CTableCreate( ConTable *Ct, int n, CANON_DATA *pCD ) -{ - int maxlenCt = pCD->nMaxLenLinearCT + 1; /* add one element for CtPartInfinity() */ - int maxlenNumH = pCD->NumH? (pCD->maxlenNumH + 1) : 0; - int maxlenNumHfixed = pCD->NumHfixed? (pCD->maxlenNumHfixed + 1) : 0; - int maxlenIso = pCD->maxlen_iso_sort_key? (pCD->maxlen_iso_sort_key+1) : 0; - int maxlenIsoExchg = pCD->iso_exchg_atnos? (pCD->maxlen_iso_exchg_atnos+1) : 0; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - int maxlenIsoHfixed = pCD->maxlen_iso_sort_key_Hfixed? (pCD->maxlen_iso_sort_key_Hfixed+1):0; -#endif - - memset( Ct, 0, sizeof(Ct[0]) ); - - Ct->maxVert = n; - - n ++; - - Ct->Ctbl = (AT_RANK*) inchi_calloc(maxlenCt, sizeof(Ct->Ctbl[0]) ); - Ct->nextCtblPos = (AT_NUMB*) inchi_calloc(n, sizeof(Ct->nextCtblPos[0]) ); - Ct->nextAtRank = (AT_RANK*) inchi_calloc(n, sizeof(Ct->nextAtRank[0]) ); - if ( maxlenNumH ) { - Ct->NumH = (NUM_H *) inchi_calloc(maxlenNumH, sizeof(Ct->NumH[0])); - } - if ( maxlenNumHfixed ) { - Ct->NumHfixed = (NUM_H *) inchi_calloc(maxlenNumHfixed, sizeof(Ct->NumH[0])); - } - if ( maxlenIso ) { - Ct->iso_sort_key = (AT_ISO_SORT_KEY *)inchi_calloc(maxlenIso, sizeof(Ct->iso_sort_key[0])); - } - if ( maxlenIsoExchg ) { - Ct->iso_exchg_atnos = (S_CHAR *)inchi_calloc( maxlenIsoExchg, sizeof(Ct->iso_exchg_atnos[0])); - } -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - if ( maxlenIsoHfixed ) { - Ct->iso_sort_key_Hfixed = (AT_ISO_SORT_KEY *)inchi_calloc(maxlenIsoHfixed, sizeof(Ct->iso_sort_key_Hfixed[0])); - } -#endif -#ifdef INCHI_CANON_USE_HASH - Ct->hash = (CtHash*) inchi_calloc(n, sizeof(Ct->hash[0]) ); -#endif - Ct->lenCt = 0; - Ct->nLenCTAtOnly = pCD->nLenCTAtOnly; - Ct->maxlenCt = maxlenCt; - Ct->lenNumH = 0; - Ct->maxlenNumH = maxlenNumH; - Ct->len_iso_sort_key = 0; - Ct->maxlen_iso_sort_key = maxlenIso; - Ct->len_iso_exchg_atnos = 0; - Ct->maxlen_iso_exchg_atnos = maxlenIso; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - Ct->len_iso_sort_key_Hfixed = 0; - Ct->maxlen_iso_sort_key_Hfixed = maxlenIsoHfixed; -#endif - Ct->maxPos = n; - Ct->lenPos = 0; - Ct->nextAtRank[0] = 0; - Ct->nextCtblPos[0] = 0; - if ( Ct->Ctbl && Ct->nextCtblPos && - (!maxlenNumH || Ct->NumH) && - (!maxlenNumHfixed || Ct->NumHfixed ) ) { - return 1; - } - return 0; -} -/****************************************************************/ -void CTableFree( ConTable *Ct ) -{ - if ( Ct ) { - if ( Ct->Ctbl ) - inchi_free( Ct->Ctbl ); - if ( Ct->nextCtblPos ) - inchi_free( Ct->nextCtblPos ); - if ( Ct->nextAtRank ) - inchi_free( Ct->nextAtRank ); - if ( Ct->NumH ) - inchi_free( Ct->NumH ); - if ( Ct->NumHfixed ) - inchi_free( Ct->NumHfixed ); - if ( Ct->iso_sort_key ) - inchi_free( Ct->iso_sort_key ); - if ( Ct->iso_exchg_atnos ) - inchi_free( Ct->iso_exchg_atnos ); -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - if ( Ct->iso_sort_key_Hfixed ) - inchi_free( Ct->iso_sort_key_Hfixed ); -#endif -#ifdef INCHI_CANON_USE_HASH - if ( Ct->hash ) - inchi_free( Ct->hash ); -#endif - memset( Ct, 0, sizeof( Ct[0] ) ); - } -} -/****************************************************************/ -int UnorderedPartitionCreate( UnorderedPartition *p, int n ) -{ - p->equ2 = (AT_NUMB*)inchi_calloc( n, sizeof(p->equ2[0])); - /* p->next = (AT_NUMB*)inchi_calloc( n, sizeof(p->next[0])); */ - if ( p->equ2 /*&& p->next*/ ) - return 1; - return 0; -} -/****************************************************************/ -void UnorderedPartitionFree( UnorderedPartition *p ) -{ - if (p->equ2) inchi_free(p->equ2); - /* if (p->next) inchi_free(p->next); */ - p->equ2 = NULL; - /* p->next = NULL; */ -} -/****************************************************************/ -void UnorderedPartitionMakeDiscrete( UnorderedPartition *p, int n) -{ - int i; - for ( i = 0; i < n; i ++ ) { - p->equ2[i] = (AT_NUMB)i; - /* p->next[i] = INFINITY; */ - } - INCHI_HEAPCHK -} -/****************************************************************/ -int PartitionCreate( Partition *p, int n) -{ - p->AtNumber = (AT_NUMB*)inchi_calloc( n, sizeof(p->AtNumber[0])); - p->Rank = (AT_RANK*)inchi_calloc( n, sizeof(p->Rank[0])); - if ( p->AtNumber && p->Rank ) { - return 1; - } - return 0; -} -/****************************************************************/ -void PartitionFree( Partition *p ) -{ - if ( p ) { - if ( p->AtNumber ) { - inchi_free( p->AtNumber ); - p->AtNumber = NULL; - } - if ( p->Rank ) { - inchi_free( p->Rank ); - p->Rank = NULL; - } - } -} -/****************************************************************/ -int PartitionIsDiscrete( Partition *p, int n) -{ - int i; - AT_RANK r; - for ( i = 0, r = 1; i < n; i ++, r ++ ) { - if ( r != (rank_mask_bit & p->Rank[p->AtNumber[i]]) ) { - INCHI_HEAPCHK - return 0; - } - } - INCHI_HEAPCHK - return 1; -} -/****************************************************************/ -int PartitionGetFirstCell( Partition *p, Cell *baseW, int k, int n ) -{ - int i; - AT_RANK r; - Cell *W = baseW+k-1; - - i = (k > 1)? baseW[k-2].first+1 : 0; - if ( i < n ) { - /* bypass single vertex cells */ - for ( r = (AT_RANK)(i+1); i < n && r == (rank_mask_bit & p->Rank[(int)p->AtNumber[i]]); i ++, r++ ) - ; - } - if ( i < n ) { - W->first = i; - for ( r = (rank_mask_bit & p->Rank[(int)p->AtNumber[i]]), i++ ; - i < n && r == (rank_mask_bit & p->Rank[(int)p->AtNumber[i]]); - i ++ ) - ; - W->next = i; - INCHI_HEAPCHK - return (W->next - W->first); - } - W->first = INFINITY; - W->next = 0; - INCHI_HEAPCHK - return 0; -} -/****************************************************************/ -void CellMakeEmpty( Cell *baseW, int k ) -{ - k --; - baseW[k].first = INFINITY; - baseW[k].next = 0; - baseW[k].prev = -1; - INCHI_HEAPCHK -} -/****************************************************************/ -void NodeSetFromVertices( NodeSet *cur_nodes, int l, Node *v, int num_v) -{ - bitWord *Bits = cur_nodes->bitword[l-1]; - int len = cur_nodes->len_set*sizeof(bitWord); - int i, j; - - memset( Bits, 0, len ); - - for ( i = 0; i < num_v; i ++ ) { - j = (int)v[i]-1; - Bits[ j / num_bit ] |= bBit[ j % num_bit ]; - } - INCHI_HEAPCHK -} -/****************************************************************/ -int AllNodesAreInSet( NodeSet *cur_nodes, int lcur_nodes, NodeSet *set, int lset ) -{ - int i; - int n = cur_nodes->len_set; - bitWord *BitsNode = cur_nodes->bitword[lcur_nodes-1]; - bitWord *BitsSet = set->bitword[lset-1]; - /* find any BitsNode[i] bit not in BitsSet[i] */ - for ( i = 0; i < n; i ++ ) { - if ( BitsNode[i] & ~BitsSet[i] ) { - INCHI_HEAPCHK - return 0; - } - } - INCHI_HEAPCHK - return 1; -} -/****************************************************************/ -void PartitionGetMcrAndFixSet( Partition *p, NodeSet *Mcr, NodeSet *Fix, int n, int l ) -{ - int i, j1, j2; - AT_RANK r, r1; - bitWord *McrBits = Mcr->bitword[l-1]; - bitWord *FixBits = Fix->bitword[l-1]; - int len = Mcr->len_set*sizeof(bitWord); - - memset( McrBits, 0, len ); - memset( FixBits, 0, len ); - for ( i = 0, r = 1; i < n; i ++, r ++ ) { - if ( r == (r1=(rank_mask_bit&p->Rank[j1=(int)p->AtNumber[i]])) ) { - FixBits[j1 / num_bit] |= bBit[j1 % num_bit]; - McrBits[j1 / num_bit] |= bBit[j1 % num_bit]; - } else { - for ( r = r1; i+1 < n && r == (rank_mask_bit&p->Rank[j2=(int)p->AtNumber[i+1]]); i ++ ) { - if ( j1 > j2 ) { - j1 = j2; - } - } - McrBits[j1 / num_bit] |= bBit[j1 % num_bit]; - } - } - INCHI_HEAPCHK -} -/************* used in ichi_bns.c ********************************/ -void NodeSetFromRadEndpoints( NodeSet *cur_nodes, int k, /*Node *v*/ Vertex RadEndpoints[], int num_v) -{ - bitWord *Bits = cur_nodes->bitword[k]; - int len = cur_nodes->len_set*sizeof(bitWord); - int i, j; - - memset( Bits, 0, len ); - - for ( i = 1; i < num_v; i += 2 ) { - j = (int)RadEndpoints[i]; - Bits[ j / num_bit ] |= bBit[ j % num_bit ]; - } -} -/************* used in ichi_bns.c ********************************/ -void RemoveFromNodeSet( NodeSet *cur_nodes, int k, Vertex v[], int num_v) -{ - if ( cur_nodes->bitword ) { - bitWord *Bits = cur_nodes->bitword[k]; - /*int len = cur_nodes->len_set*sizeof(bitWord);*/ - int i, j; - - for ( i = 0; i < num_v; i ++ ) { - j = (int) v[i]; - Bits[ j / num_bit ] &= ~bBit[ j % num_bit ]; - } - } -} -/************* used in ichi_bns.c ********************************/ -int DoNodeSetsIntersect( NodeSet *cur_nodes, int k1, int k2) -{ - if ( cur_nodes->bitword ) { - bitWord *Bits1 = cur_nodes->bitword[k1]; - bitWord *Bits2 = cur_nodes->bitword[k2]; - int len = cur_nodes->len_set; - int i; - - for ( i = 0; i < len; i ++ ) { - if ( Bits1[i] & Bits2[i] ) - return 1; - } - } - return 0; -} -/************* used in ichi_bns.c ********************************/ -int IsNodeSetEmpty( NodeSet *cur_nodes, int k) -{ - if ( cur_nodes->bitword ) { - bitWord *Bits = cur_nodes->bitword[k]; - int len = cur_nodes->len_set; - int i; - - for ( i = 0; i < len; i ++ ) { - if ( Bits[i] ) - return 0; - } - } - return 1; -} -/************* used in ichi_bns.c ********************************/ -void AddNodeSet2ToNodeSet1( NodeSet *cur_nodes, int k1, int k2) -{ - if ( cur_nodes->bitword ) { - bitWord *Bits1 = cur_nodes->bitword[k1]; - bitWord *Bits2 = cur_nodes->bitword[k2]; - int len = cur_nodes->len_set; - int i; - - for ( i = 0; i < len; i ++ ) { - Bits1[i] |= Bits2[i]; - } - } -} -/************* used in ichi_bns.c ********************************/ -int AddNodesToRadEndpoints( NodeSet *cur_nodes, int k, Vertex RadEndpoints[], Vertex vRad, int nStart, int nLen ) -{ - int n = nStart; - if ( cur_nodes->bitword ) { - bitWord *Bits = cur_nodes->bitword[k]; - int len = cur_nodes->len_set; - int i, j; - Vertex v; - - for ( i = 0, v = 0; i < len; i ++ ) { - if ( Bits[i] ) { - for ( j = 0; j < num_bit; j ++, v ++ ) { - if ( Bits[i] & bBit[j] ) { - if ( n >= nLen ) { - return -1; /* overflow */ - } - RadEndpoints[n ++] = vRad; - RadEndpoints[n ++] = v; - } - } - } else { - v += num_bit; - } - } - } - return n; -} -/****************************************************************/ -void PartitionGetTransposition( Partition *pFrom, Partition *pTo, int n, Transposition *gamma ) -{ - int i; - for ( i = 0; i < n; i ++ ) { - gamma->nAtNumb[(int)pFrom->AtNumber[i]] =pTo->AtNumber[i]; - } - INCHI_HEAPCHK -} -/**************************************************************************************/ -/* Get minimal set (class) representative and partially compress the partitioning */ -/* mcr = minimal class representative. */ -AT_RANK nGetMcr2( AT_RANK *nEqArray, AT_RANK n ) -{ - AT_RANK n1, n2, mcr; /* recursive version is much shorter. */ - INCHI_HEAPCHK - n1=nEqArray[(int)n]; - if ( n == n1 ) { - return n; - } - /* 1st pass: find mcr */ - while ( n1 != (n2=nEqArray[(int)n1])) { - n1 = n2; - } - /* 2nd pass: copy mcr to each element of the set starting from nEqArray[n] */ - mcr = n1; - n1 = n; - while ( /*n1*/ mcr != (n2=nEqArray[(int)n1]) ) { - nEqArray[(int)n1]=mcr; - n1 = n2; - } - INCHI_HEAPCHK - return ( mcr ); -} -/**************************************************************************************/ -/* Join 2 sets (classes) that have members n1 and n2 */ -int nJoin2Mcrs2( AT_RANK *nEqArray, AT_RANK n1, AT_RANK n2 ) -{ - n1 = nGetMcr2( nEqArray, n1 ); - n2 = nGetMcr2( nEqArray, n2 ); - if ( n1 < n2 ) { - nEqArray[n2] = n1; - INCHI_HEAPCHK - return 1; /* a change has been made */ - } - if ( n2 < n1 ) { - nEqArray[n1] = n2; - INCHI_HEAPCHK - return 1; /* a change has been made */ - } - INCHI_HEAPCHK - return 0; /* no changes */ -} -/****************************************************************/ -Node GetUnorderedPartitionMcrNode( UnorderedPartition *p1, Node v ) -{ - Node ret = (Node)(1+ nGetMcr2( p1->equ2, (AT_RANK)(v-1) )); - INCHI_HEAPCHK - return ret; -} -/****************************************************************/ -/* change p2 to (p2 v p1) */ -int UnorderedPartitionJoin( UnorderedPartition *p1, UnorderedPartition *p2, int n ) -{ - int i, j; - int nNumChanges = 0; - for ( i = 0; i < n; i ++ ) { - if ( (j=(int)p1->equ2[i]) == i || p2->equ2[(int)i] == p2->equ2[(int)j] ) { - continue; - } - nNumChanges += nJoin2Mcrs2(p2->equ2, (AT_NUMB)i, (AT_NUMB)j ); - } - INCHI_HEAPCHK - return nNumChanges; -} -/****************************************************************/ -int PartitionSatisfiesLemma_2_25( Partition *p, int n ) -{ - int nPartitionSize = 0; - int nNumNonTrivialCells = 0; - AT_RANK r; - int i, num; - for ( i = num = 0, r=1; i < n; i ++, r++ ) { - if ( (rank_mask_bit & p->Rank[(int)p->AtNumber[i]]) == r ) { - nPartitionSize ++; - if ( num ) { - /* num+1 = cell size > 1 */ - nNumNonTrivialCells ++; - num = 0; - } - } else { - num ++; - } - } - /* check Lemma_2_25 conditions */ - if ( n <= nPartitionSize+4 || - n == nPartitionSize + nNumNonTrivialCells || - n == nPartitionSize + nNumNonTrivialCells + 1 ) { - return 1; - } - return 0; -} -/****************************************************************/ -void PartitionCopy( Partition *To, Partition *From, int n ) -{ - int i; - memcpy( To->AtNumber, From->AtNumber, n*sizeof(To->AtNumber[0])); - memcpy( To->Rank, From->Rank, n*sizeof(To->AtNumber[0])); - for ( i = 0; i < n; i ++ ) { - To->Rank[i] &= rank_mask_bit; - } - INCHI_HEAPCHK -} -/****************************************************************/ -/* makes new equitable partition (p+1) out of p; first reduce the rank of vertex v */ -int PartitionColorVertex( Graph *G, Partition *p, Node v, int n, int n_tg, int n_max, int bDigraph, int nNumPrevRanks ) -{ - int nNumNewRanks, i, j; - long lNumNeighListIter = 0; - AT_RANK rv, r; - AT_NUMB s, sv; - for ( i = 1; i <= 2; i ++ ) { - if ( !p[i].AtNumber ) { - p[i].AtNumber = (AT_NUMB *) inchi_malloc(n_max*sizeof(p[0].AtNumber[0])); - } - if ( !p[i].Rank ) { - p[i].Rank = (AT_RANK *) inchi_malloc(n_max*sizeof(p[0].Rank[0])); - } - if ( !p[i].AtNumber || !p[i].Rank ) { - INCHI_HEAPCHK - return CT_OUT_OF_RAM; - } - } - PartitionCopy( p+1, p, n_tg ); - sv = v-1; /* atom number we are looking for */ - if ( sv >= (AT_NUMB) n_tg ) { - INCHI_HEAPCHK - return CT_CANON_ERR; /* !!! severe program error: sv not found !!! */ - } - rv = p[1].Rank[(int)sv]; /* rank of this atom */ - /* second, locate sv among all vertices that have same rank as v */ - s = n_max + 1; /* always greater than sv; this initialization is needed only to keep the compiler happy */ - for ( j = (int)rv-1; 0 <= j && rv == (r = p[1].Rank[(int)(s=p[1].AtNumber[j])]) && s != sv; j -- ) - ; - if ( s != sv ) { - INCHI_HEAPCHK - return CT_CANON_ERR; /* !!! severe program error: sv not found !!! */ - } - /* shift preceding atom numbers to the right to fill the gap after removing sv */ - r = rv-1; /* initialization only to keep compiler happy */ - for ( i = j--; 0 <= j && rv == (r = p[1].Rank[(int)(s=p[1].AtNumber[j])]); i = j, j -- ) { - p[1].AtNumber[i] = s; - } - r = (i > 0)? (r+1):1; /* new reduced rank = (next lower rank)+1 or 1 */ - /* insert sv and adjust its rank */ - p[1].AtNumber[i] = sv; - p[1].Rank[(int)sv] = r; - - - /* make equitable partition */ - if ( bDigraph ) { - - /* - nNumNewRanks = DifferentiateRanks2( n_tg, G, - nNumPrevRanks+1, p[1].Rank, p[2].Rank, - p[1].AtNumber, &lNumNeighListIter, 1 ); - */ - nNumNewRanks = DifferentiateRanks4( n_tg, G, - nNumPrevRanks+1, p[1].Rank, p[2].Rank /* temp array */, - p[1].AtNumber, (AT_RANK)n, &lNumNeighListIter ); - - - } else { - /* - nNumNewRanks = DifferentiateRanks2( n_tg, G, - nNumPrevRanks+1, p[1].Rank, p[2].Rank, - p[1].AtNumber, &lNumNeighListIter, 1 ); - */ - nNumNewRanks = DifferentiateRanks3( n_tg, G, - nNumPrevRanks+1, p[1].Rank, p[2].Rank /* temp array */, - p[1].AtNumber, &lNumNeighListIter ); - } - INCHI_HEAPCHK - - return nNumNewRanks; -} -typedef struct tagNodeValues { - NUM_H NumH; - AT_ISO_SORT_KEY iso_sort_key; - NUM_H NumHfixed; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - AT_ISO_SORT_KEY iso_sort_key_Hfixed; -#endif - AT_NUMB nAtNumber; -} NV; - -/****************************************************************/ -/* return min node > vPrev or INFINITY if not found */ -/* Input: v = previous atom number + 1 or 0 on first call*/ -Node CellGetMinNode( Partition *p, Cell *W, Node v, CANON_DATA *pCD ) -{ - AT_NUMB i; - AT_NUMB uCurAtNumb, uMinAtNumb = INFINITY; - /* in case of emty cell: (W->first=INFINITY) > (W->next=0); returns INFINITY */ - if ( W->first > W->next ) { - return INFINITY; - } -#if ( USE_AUX_RANKING == 1 ) - if ( pCD && pCD->nAuxRank ) - { - AT_RANK uMinAuxRank, uCurAuxRank; - int nCurAtNumb; -#if ( USE_AUX_RANKING_ALL == 1 ) - AT_RANK uInpAuxRank; - int nInpAtNumb, nMinAtNumb; -#endif - for ( i = W->first; i < W->next; i ++ ) { - uCurAtNumb = p->AtNumber[(int)i]; - if ( !(p->Rank[(int)uCurAtNumb] & rank_mark_bit) ) { - break; /* found the first unmarked yet node */ - } - } - if ( i == W->next ) { - return INFINITY; - } -#if ( USE_AUX_RANKING_ALL == 1 ) - /*==== vertex ordering definition === - * vertex v1 < v2 <=> (AuxRank(v1)==AuxRank(v2) && AtNumb(v1) < AtNumb(v2)) || (AuxRank(v1) < AuxRank(v2)) - * vertex v1 > v2 <=> (AuxRank(v1)==AuxRank(v2) && AtNumb(v1) > AtNumb(v2)) || (AuxRank(v1) > AuxRank(v2)) - * vertex v1 = v2 <=> (AuxRank(v1)==AuxRank(v2) && AtNumb(v1) == AtNumb(v2)) - */ - - /* set initial vMin so that vMin > any vertex */ - uMinAuxRank = INFINITY; - nMinAtNumb = INFINITY; - /* set vInp */ - if ( v ) { - nInpAtNumb = (int)v - 1; /* previous vertex */ - uInpAuxRank = pCD->nAuxRank[nInpAtNumb]; - } else { - nInpAtNumb = -1; /* less than any vertex */ - uInpAuxRank = 0; - } - /* find vMin = min { vCur : (vCur > vInp) && (vCur in W) } */ - for ( ; i < W->next; i ++ ) { - nCurAtNumb = (int)p->AtNumber[(int)i]; - if ( !(p->Rank[nCurAtNumb] & rank_mark_bit) ) { - /* vertex nCurAtNumb is not marked, find whether it fits the conditions */ - uCurAuxRank = pCD->nAuxRank[nCurAtNumb]; - if ( uCurAuxRank == uInpAuxRank && nCurAtNumb > nInpAtNumb || uCurAuxRank > uInpAuxRank ) { - /* here vCur > vInp */ - if ( uCurAuxRank == uMinAuxRank && nCurAtNumb < nMinAtNumb ) { - /* vCur < vMin (1) */ - nMinAtNumb = nCurAtNumb; - } else - if ( uCurAuxRank < uMinAuxRank ) { - /* vCur < vMin (2) */ - uMinAuxRank = uCurAuxRank; - nMinAtNumb = nCurAtNumb; - } - } - } - } - uMinAtNumb = (nMinAtNumb==INFINITY)? INFINITY : (AT_NUMB)nMinAtNumb; -#else - if ( v ) { - nCurAtNumb = (int)v-1; - /* any valid found node must have nAuxRank == uMinAuxRank */ - uMinAuxRank = pCD->nAuxRank[nCurAtNumb]; - } else { - /* any valid found node must have minimal uMinAuxRank from pCD->nAuxRank[] */ - uMinAuxRank = INFINITY; /* undefined */ - } - - for ( ; i < W->next; i ++ ) { - uCurAtNumb = p->AtNumber[(int)i]; - nCurAtNumb = (int)uCurAtNumb; - if ( uCurAtNumb >= v && !(p->Rank[nCurAtNumb] & rank_mark_bit) ) { - uCurAuxRank = pCD->nAuxRank[nCurAtNumb]; - if ( v ) { - /* get next node */ - /* find node with smallest uCurAtNumb among nodes with aux. ranks equal to uMinAuxRank */ - if ( uCurAuxRank == uMinAuxRank && uCurAtNumb < uMinAtNumb ) { - uMinAtNumb = uCurAtNumb; - } - } else { - /* get first node */ - /* find node with smallest smallest uCurAtNumb among nodes with smallest aux. ranks */ - if ( uMinAuxRank > uCurAuxRank ) { - uMinAuxRank = uCurAuxRank; - uMinAtNumb = uCurAtNumb; - } else - if ( uMinAuxRank == uCurAuxRank && uCurAtNumb < uMinAtNumb ) { - uMinAtNumb = uCurAtNumb; - } - } - } - } -#endif - } else -#endif /* } USE_AUX_RANKING */ - { - for ( i = W->first; i < W->next; i ++ ) { - uCurAtNumb = p->AtNumber[(int)i]; - if ( uCurAtNumb >= v && !(p->Rank[(int)uCurAtNumb] & rank_mark_bit) && uCurAtNumb < uMinAtNumb ) { - uMinAtNumb = uCurAtNumb; - } - } - } - if ( uMinAtNumb != INFINITY ) uMinAtNumb ++; - INCHI_HEAPCHK - return uMinAtNumb; -} -/****************************************************************/ -int CellGetNumberOfNodes( Partition *p, Cell *W ) -{ - int first = W->first; - int next = W->next; - int i, num; - for ( i = first, num = 0; i < next; i ++ ) { - if ( !( rank_mark_bit & p->Rank[(int)p->AtNumber[i]] ) ) { - num++; - } - } - INCHI_HEAPCHK - return num; -} -/****************************************************************/ -int CellIntersectWithSet( Partition *p, Cell *W, NodeSet *Mcr, int l ) -{ - bitWord *McrBits = Mcr->bitword[l-1]; - int first = W->first; - int next = W->next; - int i, j, k; - if ( first >= next ) { /* for testing only */ - return 0; - } - for ( i = first, k = 0; i < next; i ++ ) { - j = (int)p->AtNumber[i]; - if ( !(McrBits[ j / num_bit ] & bBit[ j % num_bit ]) ) { /* BC: reading uninit memory ???-not examined yet */ - k += !(p->Rank[j] & rank_mark_bit); /* for testing only */ - p->Rank[j] |= rank_mark_bit; - } - } - INCHI_HEAPCHK - return k; -} -/****************************************************************/ -void CtPartClear( ConTable *Ct, int k ) -{ - int start; - int len; - /* connection table */ - start = k>1? Ct->nextCtblPos[k-1] : 0; - len = Ct->lenCt - start; - if ( len > 0 ) { - memset( Ct->Ctbl + start, 0, (Ct->lenCt - start)*sizeof(Ct->Ctbl[0]) ); - } - Ct->lenCt = start; - Ct->lenPos = k; - - INCHI_HEAPCHK -} -/**********************************************************************************/ -/* Sort neighbors according to ranks in ascending order */ -void insertions_sort_NeighList_AT_NUMBERS2( NEIGH_LIST base, AT_RANK *nRank, AT_RANK max_rj ) -{ - AT_NUMB *i, *j, *pk, tmp, rj; - int k, num = (int)*base++; - for( k=1, pk = base; k < num; k++, pk ++ ) { - i = pk; - j = i + 1; - rj = (rank_mask_bit & nRank[(int)*j]); - if ( rj < max_rj ) { - while ( j > base && rj < (rank_mask_bit & nRank[(int)*i])) { - tmp = *i; - *i = *j; - *j = tmp; - j = i --; - } - } - } - INCHI_HEAPCHK -} -/****************************************************************/ -/* may need previous Lambda */ -void CtPartFill( Graph *G, CANON_DATA *pCD, Partition *p, - ConTable *Ct, int k, int n, int n_tg ) - /* k = (new index in Ct->nextAtRank[] and Ct->nextCtblPos[]) + 1 */ -{ - int startCtbl; - int startAtOrd; - AT_RANK r, rj, nn, j, rj_prev; - int i, m; -#ifdef INCHI_CANON_USE_HASH - CtHash hash = 0; -#endif - static int count; /* for debug only */ - count ++; - - - INCHI_HEAPCHK - - k --; - if ( k ) { - startCtbl = Ct->nextCtblPos[k-1]; - startAtOrd = Ct->nextAtRank[k-1]-1; /* here p->Rank[p->AtNumber[r-1]] = r */ - } else { - startCtbl = 0; - startAtOrd = 0; - } - /******* well-defined (by fixed ranks) part of the connection table ************/ - r = (rank_mask_bit & p->Rank[(int)p->AtNumber[startAtOrd]]); - for ( i = startAtOrd; i < n_tg && r == (rank_mask_bit&p->Rank[m=(int)p->AtNumber[i]]); i++, r ++ ) { - Ct->Ctbl[startCtbl++] = r; - insertions_sort_NeighList_AT_NUMBERS2( G[m], p->Rank, r ); - nn = G[m][0]; /* number of neighbors */ - rj_prev = 0; /* debug only */ -#ifdef INCHI_CANON_USE_HASH - hash = add2crc32( hash, (AT_NUMB)(r + n) ); -#endif - for ( j = 1; j <= nn && (rj=(rank_mask_bit&p->Rank[(int)G[m][j]])) < r; j ++ ) { - Ct->Ctbl[startCtbl++] = rj; -#ifdef INCHI_CANON_USE_HASH - hash = add2crc32( hash, rj ); -#endif -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - /* debug only */ - if ( rj < rj_prev ) { - int stop = 1; /* */ - } -#endif - rj_prev = rj; - } - } - - INCHI_HEAPCHK - - /****************** well-defined part of base hydrogen atoms *******************/ - if ( pCD->NumH && Ct->NumH ) { - nn = inchi_min(n, i); - for ( j = startAtOrd; j < nn; j ++ ) { /* atoms */ - Ct->NumH[j] = pCD->NumH[p->AtNumber[j]]; - } - for ( ; j < i; j ++ ) { /* t-groups */ - int data_pos = n + T_NUM_NO_ISOTOPIC * ((int)p->AtNumber[j] - n); - for ( m = 0; m < T_NUM_NO_ISOTOPIC; m ++ ) { - Ct->NumH[nn ++] = pCD->NumH[data_pos ++]; - } - } - Ct->lenNumH = nn; - } else { - Ct->lenNumH = 0; - } - - INCHI_HEAPCHK - - /****************** well-defined part of fixed hydrogen atoms *******************/ - if ( pCD->NumHfixed && Ct->NumHfixed ) { - nn = inchi_min(n, i); - for ( j = startAtOrd; j < nn; j ++ ) { - Ct->NumHfixed[j] = pCD->NumHfixed[p->AtNumber[j]]; - - INCHI_HEAPCHK - - } - /* Ct->lenNumHfixed = nn; */ - } else { - ;/* Ct->lenNumHfixed = 0; */ - } - - INCHI_HEAPCHK - - /****************** well-defined part of isotopic keys ***************************/ - if ( pCD->iso_sort_key && Ct->iso_sort_key ) { - for ( j = startAtOrd; j < i; j ++ ) { - Ct->iso_sort_key[j] = pCD->iso_sort_key[p->AtNumber[j]]; - } - Ct->len_iso_sort_key = i; - } else { - Ct->len_iso_sort_key = 0; - } - - INCHI_HEAPCHK - - /****************** well-defined part of isotopic iso_exchg_atnos ***************************/ - if ( pCD->iso_exchg_atnos && Ct->iso_exchg_atnos ) { - for ( j = startAtOrd; j < i; j ++ ) { - Ct->iso_exchg_atnos[j] = pCD->iso_exchg_atnos[p->AtNumber[j]]; - } - Ct->len_iso_exchg_atnos = i; - } else { - Ct->len_iso_exchg_atnos = 0; - } - - INCHI_HEAPCHK - /******** well-defined part of isotopic keys for fixed hydrogen atoms ************/ -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - if ( pCD->iso_sort_key_Hfixed && Ct->iso_sort_key_Hfixed ) { - nn = inchi_min(n, i); - for ( j = startAtOrd; j < nn; j ++ ) { - Ct->iso_sort_key_Hfixed[j] = pCD->iso_sort_key_Hfixed[p->AtNumber[j]]; - } - Ct->len_iso_sort_key_Hfixed = nn; - } else { - Ct->len_iso_sort_key_Hfixed = 0; - } -#endif - - INCHI_HEAPCHK - - Ct->lenCt = startCtbl; /* not aways increases */ - Ct->nextCtblPos[k] = startCtbl; - Ct->nextAtRank[k] = r; - Ct->lenPos = k+1; - /* the rest of the CTable */ -#ifdef INCHI_CANON_USE_HASH - while ( i < n ) { - r = (rank_mask_bit&p->Rank[m=(int)p->AtNumber[i]]); - hash = add2crc32( hash, (AT_NUMB)(r + n) ); - r++; - insertions_sort_NeighList_AT_NUMBERS2( G[m], p->Rank, r ); - nn = G[m][0]; - rj_prev = 0; /* debug only */ - for ( j = 1; j <= nn && (rj=(rank_mask_bit&p->Rank[(int)G[m][j]])) < r; j ++ ) { - hash = add2crc32( hash, rj ); - } - i ++; - } - Ct->hash[k] = hash; -#endif - - INCHI_HEAPCHK -} -/****************************************************************/ -void CtPartInfinity( ConTable *Ct, S_CHAR *cmp, int k ) -{ - int startCtbl; - /*int startAtOrd;*/ - k --; - if ( k ) { - startCtbl = Ct->nextCtblPos[k-1]; - /*startAtOrd = Ct->nextAtRank[k-1]-1;*/ /* here p->Rank[p->AtNumber[r-1]] = r */ - if ( cmp ) { - memset( cmp, 0, k*sizeof(cmp[0]) ); - } - } else { - startCtbl = 0; - /*startAtOrd = 0;*/ - } - if ( !startCtbl || Ct->Ctbl[startCtbl-1] != EMPTY_CT ) { - Ct->Ctbl[startCtbl] = EMPTY_CT; - } - INCHI_HEAPCHK -} -/****************************************************************/ -/* Return value: - -1 <=> *Lambda1 < *Lambda2 - 0 <=> *Lambda1 = *Lambda2 - +1 <=> *Lambda1 > *Lambda2 - - Input: k+1 = value of level at which the comparison is executed - (that is, in the calling program k(caller) = k+1) - - Stars (*) below mark the differences: - - bSplitTautCompare != 0 => directed graph; compare: - non-tautomeric part of CT in layer 0; (*) - non-tautomeric H in layer 1; (*) - tautomeric part of CT & H in layer 2; (*) - fixed H in layer 3; - isotopic atoms, non-taut - H & t-groups in layer 4; - fixed isotopic H in layer 5; <- move to layer 4 - - bSplitTautCompare == 0 => undirected graph; compare: - full CT in Layer 0; (*) - taut and non-taut H in Layer 1; (*) - * nothing * in layer 2; (*) - fixed H in layer 3; - isotopic atoms, non-taut - H & t-groups in layer 4; - fixed isotopic H in layer 5; <- move to layer 4 - -*/ -int CtPartCompare( ConTable *Ct1, ConTable *Ct2, S_CHAR *cmp, - kLeast *kLeastForLayer, int k, int bOnlyCommon, int bSplitTautCompare ) -{ - int startCt1, endCt1, startCt2, endCt2; /*endCt,*/ - int startAt1, endAt1, startAt2, endAt2; /*endCt,*/ - int midCt /* end of atoms only Ct */, midNumH=0 /* end of atoms only NumH */, maxVert; - int diff, i, k1, k2, lenNumH, len_iso_sort_key, /*mid_iso_sort_key,*/ midAt; - int nLayer = 0; - - k --; - i = -1; - /* set kLeastForLayer[nLayer].k = (k+1) or -(k+1) - kLeastForLayer[nLayer].i = iDiff - if all the conditions are met: - 1) kLeastForLayer[nLayer].k = 0 - 2) diff==0 for all layers < nLayer - - sign: - if the final diff < 0 then kLeastForLayer[nLayer].k = -(k+1) else - if the final diff > 0 then kLeastForLayer[nLayer].k = +(k+1) - - k+1 instead of k takes into account k--; statememt above) - - meaning: - ======== - abs(kLeastForLayer[nLayer].k) is the greatest level k at which - difference at layer nLayer are zeroes of hidden by differences in smaller nLayer. - - "Hidden by difference in smaller level" means that nLayer of comparison - has not been reached because the difference was discovered at a previous layer. - - - Lambda vs zf_zeta comparison - ============================================= - accept only diff == 0 - - Lambda vs pzb_rho and pzb_rho_fix comparison - ============================================= - Maintain kLeastForLayer[] and kLeastForLayerFix[] - - The algorithm provides that pzb_rho(m-1) < pzb_rho(m) <= pzb_rho_fix - - Definition: pzb_rho(m-1) < pzb_rho(m) means that - ----------------------------------------------- - pzb_rho(m-1)[nLayerCurr] == pzb_rho(m)[nLayerCurr] for nLayerCurr = 0..nLayerDiff-1 - pzb_rho(m-1)[nLayerDiff] < pzb_rho(m)[nLayerDiff] - - Definition: pzb_rho(m-1)[nLayerDiff] < pzb_rho(m)[nLayerDiff] means that - ------------------------------------------------------------------------- - pzb_rho(m-1)[nLayerDiff][i] == pzb_rho(m)[nLayerDiff][i] for i=0..iDdiff-1 - pzb_rho(m-1)[nLayerDiff][iDdiff] < pzb_rho(m)[nLayerDiff][iDdiff] - - This defines nLayerDiff(pzb1, pzb2) where pszb1 = pzb_rho(a), pzb2=pzb_rho(b) (a= 0 *) && - ((L_fix < L_rho) || (L_fix == L_rho && I_fix < I_rho)) - => - qzb_rho_fix = kLeastForLayerFix[L_fix].k if prevoiusly qzb_rho_fix == 0 - - b) otherwise do not change qzb_rho_fix, except the following: - - c) Special case L_rho == L_fix && I_rho == I_fix. Let L=L_rho, I = I_rho. - - Compare 3 valirs: Lambda[L][I], pzb_rho(m)[L][I], pzb_rho_fix[L][I] - The algorithm provides pzb_rho(m)[L][I] < pzb_rho_fix[L][I] - (pzb_rho(m)[L][I]==pzb_rho_fix[L][I] <=> pzb_rho(m)[L][I]==pzb_rho_fix[L][I] - is impossible by construction) - There are 3 possibilities: - c1) Lambda[L][I] < pzb_rho(m)[L][I] < pzb_rho_fix[L][I] <=> - kLeastForLayer[L].k < 0 && kLeastForLayerFix[L].k < 0 - => qzb_rho := kLeastForLayer[L].k, reject too small Lambda - c2) pzb_rho(m)[L][I] < Lambda[L][I] < pzb_rho_fix[L][I] - kLeastForLayer[L].k > 0 && kLeastForLayerFix[L].k < 0 - => qzb_rho := kLeastForLayer[L].k, accept Lambda, rho:=nu - c3) pzb_rho(m)[L][I] < pzb_rho_fix[L][I] < Lambda[L][I] - kLeastForLayer[L].k > 0 && kLeastForLayerFix[L].k > 0 - => qzb_rho_fix := kLeastForLayerFix[L].k, reject too big Lambda - - Case - kLeastForLayer[L].k < 0 && kLeastForLayerFix[L].k > 0 is impossible - because it means - pzb_rho_fix < Lambda < pzb_rho(m) <=> pzb_rho_fix < pzb_rho(m) - - - Case (c3) occurs in case of (a) - Case (c1) - - 2. Comparison Lambda vs pzb_rho before reaching discrete partition - ---------------------------------------------------------------------- - a) (L_rho < L_fix) || (L_rho == L_fix && I_rho < I_fix) => - - Lambda differs from pzb_rho(m) in the part of pzb_rho(m) that will never change - qzb_rho = kLeastForLayer[L_rho].k; reject Labmda or accept pzb_rho(m+1):=Labmda - - b) (L_rho == L_fix && I_rho > I_fix) && kLeastForLayer[L_rho].k < 0 - Lambda < pzb_rho(m), therefore - qzb_rho = kLeastForLayer[L_rho].k; reject Labmda - - c) (L_rho > L_fix) => - qzb_rho := 0 because more significant difference may be discovered - in layer < L_rho later. The final comparison may be needed at the - level of discrete partition. - - - */ - - if ( cmp ) { - for ( i = 0; i <= k && !cmp[i]; i++ ) - ; - if ( i < k ) { - cmp[k] = cmp[i]; - return (int)cmp[i]; - } - } - k1 = Ct1->lenPos-1; - k2 = Ct2->lenPos-1; - -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - if ( k > k1 || k > k2 ) { - int stop = 1; - } -#endif - diff = 0; - - if ( k ) { - startCt1 = Ct1->nextCtblPos[k-1]; - startCt2 = Ct2->nextCtblPos[k-1]; - startAt1 = Ct1->nextAtRank[k-1]-1; - startAt2 = Ct2->nextAtRank[k-1]-1; - } else { - startCt1 = startCt2 = 0; - startAt1 = startAt2 = 0; - } - - endCt1 = Ct1->nextCtblPos[k]; - endCt2 = Ct2->nextCtblPos[k]; - endAt1 = (int)Ct1->nextAtRank[k]-1; - endAt2 = (int)Ct2->nextAtRank[k]-1; - - maxVert = inchi_min(Ct1->maxVert, Ct2->maxVert); - -#ifdef INCHI_CANON_USE_HASH - if ( !diff ) { - if ( Ct1->hash[k] > Ct2->hash[k] ) - diff = 1; - else - if ( Ct1->hash[k] < Ct2->hash[k] ) - diff = -1; - } - if ( diff ) { - goto done; - } -#endif - - /************************** lengths **************************************************/ - if ( diff = -(startCt1 - startCt2) ) { - /* comparing two INFINITY terminations */ - if ( bOnlyCommon && - startCt1 >= Ct1->nLenCTAtOnly && startCt2 >= Ct2->nLenCTAtOnly && - Ct1->Ctbl[startCt1] == EMPTY_CT && Ct2->Ctbl[startCt2] == EMPTY_CT ) { - return 0; - } - if ( bOnlyCommon ) { - startCt1 = startCt2 = inchi_min(startCt1, startCt2); - startAt1 = startAt2 = inchi_min(startAt1, startAt2); - if ( Ct1->lenCt == Ct2->lenCt ) { - endCt1 = endCt2 = inchi_max(endCt1, endCt2); - endAt1 = endAt2 = inchi_max(endAt1, endAt2); - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; - } -#endif - } else - /* comparing (taut tail) vs INFINITY termination -- ??? */ - if ( startCt1 > startCt2 && - Ct1->maxVert > Ct2->maxVert && - startAt2 == Ct2->maxVert ) { - return 0; - } else { - goto done; - } - } - - lenNumH = Ct1->lenNumH; - len_iso_sort_key = Ct1->len_iso_sort_key; - - if ( diff = -(endCt1 - endCt2) ) { /* negative sign reproduces results for NSC=28393 */ - if ( bOnlyCommon ) { - endCt1 = endCt2 = inchi_min(endCt1, endCt2); - endAt1 = endAt2 = inchi_min(endAt1, endAt2); - lenNumH = inchi_min(Ct1->lenNumH, Ct2->lenNumH); - len_iso_sort_key = inchi_min(Ct1->len_iso_sort_key, Ct1->len_iso_sort_key); - } else - /* take care of case when comparing tautomeric vs non-tautomeric: - since (taut)->maxVert > (non-taut)->maxVert, --??? - (taut)->maxlenCt > (non-taut)->maxlenCt --!!! - compare up to min out of the two, ignoring INFINITY in the last position */ - if ( endCt1 > endCt2 && Ct1->maxlenCt > Ct2->maxlenCt ) { - if ( endAt2 == Ct2->maxVert + 1 ) { - /* remove INFINITY termination of the shorter CT */ - /* should never happen */ - endAt2 --; - len_iso_sort_key = lenNumH = endAt1 = endAt2; - endCt2 --; - endCt1 = endCt2; - diff = 0; - } else - if ( endAt2 == Ct2->maxVert ) { - /* remove INFINITY termination of CT */ - len_iso_sort_key = lenNumH = endAt1 = endAt2; - endCt1 = endCt2; - diff = 0; - } else { - goto done; - } - } else { - goto done; - } - } - - if ( bSplitTautCompare ) { - midCt = inchi_min(Ct1->nLenCTAtOnly, Ct2->nLenCTAtOnly); - if ( midCt > endCt1 ) { - midCt = endCt1; - } - midAt = inchi_min(maxVert, endAt1); - } else { - midCt = endCt1; - midAt = endAt1; - } - - /*endCt = min(endCt1, endCt2);*/ - /*************************************************************************/ - /************ layer 0: connection table without tautomeric groups ********/ - /*************************************************************************/ - for ( i = startCt1; i < midCt && Ct1->Ctbl[i] == Ct2->Ctbl[i]; i ++ ) - /*for ( i = startCt1; i < endCt && !(diff = (int)Ct1->Ctbl[i] - (int)Ct2->Ctbl[i]); i ++ )*/ - ; - if ( i < midCt ) { - diff = (int)Ct1->Ctbl[i] - (int)Ct2->Ctbl[i]; - goto done; - } - /*************************************************************************/ - /******** layer 1 NumH: H atoms without tautomeric H *********************/ - /*************************************************************************/ - nLayer ++; - /*============= check limits for consistency ==========*/ - if ( diff = -(startAt1 - startAt2) ) { - goto done; /* should not happen */ - } - if ( diff = -(endAt1 - endAt2) ) { - goto done; /* should not happen */ - } - /*============= comparison =============================*/ - if ( Ct1->NumH && Ct2->NumH ) { - if ( endAt1 < maxVert ) { - midNumH = lenNumH = endAt1; - } else - if ( bSplitTautCompare ) { - midNumH = maxVert; - } else { - midNumH = lenNumH; - } - /* lenNumH = (endAt2 >= maxVert)? lenNumH : endAt2; */ - /* endAt1 = (endAt2 == n)? lenNumH : endAt2; */ - - for ( i = startAt1; i < midNumH && Ct1->NumH[i] == Ct2->NumH[i]; i ++ ) - ; - if ( i < midNumH ) { - diff = (int)Ct1->NumH[i] - (int)Ct2->NumH[i]; - goto done; - } - } - /*************************************************************************/ - /************** layer 2: tautomeric part of CT and tautomeric H **********/ - /*************************************************************************/ - nLayer ++; - for ( i = midCt; i < endCt1 && Ct1->Ctbl[i] == Ct2->Ctbl[i]; i ++ ) - ; /* compare tautomeric groups part of CT */ - if ( i < endCt1 ) { - diff = (int)Ct1->Ctbl[i] - (int)Ct2->Ctbl[i]; - goto done; - } - if ( Ct1->NumH && Ct2->NumH ) { - for ( i = midNumH; i < lenNumH && Ct1->NumH[i] == Ct2->NumH[i]; i ++ ) - ; /* compare tautomeric H */ - if ( i < lenNumH ) { - diff = (int)Ct1->NumH[i] - (int)Ct2->NumH[i]; - i += endCt1 - midCt; - goto done; - } - } - /*************************************************************************/ - /************** layer 3: Fixed H atoms ***********************************/ - /*************************************************************************/ - nLayer ++; - if ( Ct1->NumHfixed && Ct2->NumHfixed ) { - for ( i = startAt1; i < midAt && Ct1->NumHfixed[i] == Ct2->NumHfixed[i]; i ++ ) - ; - if ( i < midAt ) { - diff = (int)Ct1->NumHfixed[i] - (int)Ct2->NumHfixed[i]; - goto done; - } - } - /*************************************************************************/ - /************** layer 4: isotopic atoms H, incl. tautomeric **************/ - /*************************************************************************/ - nLayer ++; - if ( Ct1->iso_sort_key && Ct2->iso_sort_key ) { - for ( i = startAt1; i < endAt1 && Ct1->iso_sort_key[i] == Ct2->iso_sort_key[i]; i ++ ) - ; - if ( i < endAt1 ) { - diff = Ct1->iso_sort_key[i] > Ct2->iso_sort_key[i]? 1:-1; - goto done; - } - } - if ( Ct1->iso_exchg_atnos && Ct2->len_iso_exchg_atnos ) { - for ( i = startAt1; i < endAt1 && Ct1->iso_exchg_atnos[i] == Ct2->iso_exchg_atnos[i]; i ++ ) - ; - if ( i < endAt1 ) { - diff = Ct1->iso_exchg_atnos[i] > Ct2->iso_exchg_atnos[i]? 1:-1; - goto done; - } - } -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - /*************************************************************************/ - /************** layer 6: Fixed isotopic H atoms **************************/ - /*************************************************************************/ - nLayer ++; - if ( Ct1->iso_sort_key_Hfixed && Ct2->iso_sort_key_Hfixed ) { - for ( i = startAt1; i < midAt && Ct1->iso_sort_key_Hfixed[i] == Ct2->iso_sort_key_Hfixed[i]; i ++ ) - ; - if ( i < midAt ) { - diff = Ct1->iso_sort_key_Hfixed[i] > Ct2->iso_sort_key_Hfixed[i]? 1:-1; - goto done; - } - } -#endif - -done: -#ifdef INCHI_CANON_MIN - diff = -diff; -#endif - - if ( diff ) { - diff = (diff > 0)? (nLayer+1) : -(nLayer+1); /* return the discovered difference layer number >= 1 */ - if ( kLeastForLayer ) { -#if ( bRELEASE_VERSION != 1 ) - if ( abs(kLeastForLayer[nLayer].k) > k+1 ) { /* for debug only */ - int stop = 1; /* */ - } -#endif - if ( !kLeastForLayer[nLayer].k ) { - kLeastForLayer[nLayer].k = (diff > 0)? (k+1) : -(k+1); - kLeastForLayer[nLayer].i = i; - } - if ( nLayer /* && !bOnlyCommon */) { - diff = 0; - } - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; /* for debug only */ - } -#endif - if ( cmp ) { - cmp[k] = (diff > 0)? 1 : (diff < 0)? -1 : 0; - } - return diff; -} -/**************************************************************************************************************/ -int CtFullCompare( ConTable *Ct1, ConTable *Ct2, int bOnlyCommon, int bSplitTautCompare ) -{ - int startCt1, endCt1, startCt2, endCt2; /*endCt,*/ - int startAt1, endAt1, startAt2, endAt2; /*endCt,*/ - int midCt /* end of atoms only in Ctbl */, - midNumH = 0 /* end of atoms only NumH */, - midAt /* end of atoms only */; - int diff, i, k1, k2, lenNumH1, lenNumH2, lenNumH, maxVert /* min num atoms */; - int len_iso_sort_key1, len_iso_sort_key2, len_iso_sort_key /*, mid_iso_sort_key*/; - int nLayer = 0; - - k1 = Ct1->lenPos-1; - k2 = Ct2->lenPos-1; - - diff = 0; - - startCt1 = startCt2 = 0; - startAt1 = startAt2 = 0; - - endCt1 = Ct1->nextCtblPos[k1]; - endCt2 = Ct2->nextCtblPos[k2]; - endAt1 = (int)Ct1->nextAtRank[k1]-1; - endAt2 = (int)Ct2->nextAtRank[k2]-1; - - maxVert = inchi_min(Ct1->maxVert, Ct2->maxVert); - - if ( bOnlyCommon ) { - endCt1 = inchi_min(endCt1, endCt2); - endCt1 = endCt2 = inchi_min(endCt1, Ct1->lenCt); - endAt1 = endAt2 = inchi_min(endAt1, endAt2); - if ( Ct1->Ctbl[endCt1] == EMPTY_CT || Ct1->Ctbl[endCt1] == 0 || - Ct2->Ctbl[endCt1] == EMPTY_CT || Ct2->Ctbl[endCt1] == 0 ) { - endCt1 = endCt2 = endCt1-1; - } - lenNumH = - lenNumH1 = - lenNumH2 = inchi_min(Ct1->lenNumH, Ct2->lenNumH); - len_iso_sort_key = - len_iso_sort_key1 = - len_iso_sort_key2 = inchi_min(Ct1->len_iso_sort_key, Ct1->len_iso_sort_key); - } else { - if ( Ct1->Ctbl[endCt1-1] == EMPTY_CT ) { - endCt1 --; - } - if ( Ct2->Ctbl[endCt2-1] == EMPTY_CT ) { - endCt2 --; - } - lenNumH1 = Ct1->lenNumH; - lenNumH2 = Ct2->lenNumH; - lenNumH = inchi_min(lenNumH1, lenNumH2); - len_iso_sort_key1 = Ct1->len_iso_sort_key; - len_iso_sort_key2 = Ct2->len_iso_sort_key; - len_iso_sort_key = inchi_min(len_iso_sort_key1, len_iso_sort_key2); - } - - if ( diff = -(endCt1 - endCt2) ) { /* negative sign reproduces results for NSC=28393 */ - goto done; - } - - if ( bSplitTautCompare ) { - midCt = inchi_min(Ct1->nLenCTAtOnly, Ct2->nLenCTAtOnly); - if ( midCt > endCt1 ) { - midCt = endCt1; - } - midAt = inchi_min(maxVert, endAt1); - } else { - midCt = endCt1; - midAt = endAt1; - } - - /*************************************************************************/ - /************ layer 0: connection table without tautomeric groups ********/ - /*************************************************************************/ - for ( i = startCt1; i < midCt && Ct1->Ctbl[i] == Ct2->Ctbl[i]; i ++ ) - ; - if ( i < midCt ) { - diff = (int)Ct1->Ctbl[i] - (int)Ct2->Ctbl[i]; - goto done; - } - /*************************************************************************/ - /************* layer 1: H atoms without tautomeric H *********************/ - /*************************************************************************/ - nLayer ++; - if ( Ct1->NumH && Ct2->NumH ) { - if ( diff = -(lenNumH1 - lenNumH2) ) { /* negative sign reproduces results for NSC=28393 */ - goto done; - } - if ( endAt1 < maxVert ) { - midNumH = lenNumH1 = endAt1; - } else - if ( bSplitTautCompare ) { - midNumH = maxVert; - } else { - midNumH = lenNumH1; - } - for ( i = startAt1; i < midNumH && Ct1->NumH[i] == Ct2->NumH[i]; i ++ ) - ; - if ( i < midNumH ) { - diff = (int)Ct1->NumH[i] - (int)Ct2->NumH[i]; - goto done; - } - } - /*************************************************************************/ - /************** layer 2: tautomeric part of CT and tautomeric H **********/ - /*************************************************************************/ - nLayer ++; - for ( i = midCt; i < endCt1 && Ct1->Ctbl[i] == Ct2->Ctbl[i]; i ++ ) - ; /* compare tautomeric groups part of CT */ - if ( i < endCt1 ) { - diff = (int)Ct1->Ctbl[i] - (int)Ct2->Ctbl[i]; - goto done; - } - if ( Ct1->NumH && Ct2->NumH ) { - for ( i = midNumH; i < lenNumH1 && Ct1->NumH[i] == Ct2->NumH[i]; i ++ ) - ; /* compare tautomeric H */ - if ( i < lenNumH1 ) { - diff = (int)Ct1->NumH[i] - (int)Ct2->NumH[i]; - goto done; - } - } - /*************************************************************************/ - /************** layer 3: Fixed H atoms ***********************************/ - /*************************************************************************/ - nLayer ++; - if ( Ct1->NumHfixed && Ct2->NumHfixed ) { - for ( i = startAt1; i < endAt1 && Ct1->NumHfixed[i] == Ct2->NumHfixed[i]; i ++ ) - ; - if ( i < endAt1 ) { - diff = (int)Ct1->NumHfixed[i] - (int)Ct2->NumHfixed[i]; - goto done; - } - } - /*************************************************************************/ - /************** layer 4: isotopic atoms, H and isotopic taut H ***********/ - /*************************************************************************/ - nLayer ++; - if ( Ct1->iso_sort_key && Ct2->iso_sort_key ) { - if ( diff = -(len_iso_sort_key1 - len_iso_sort_key2) ) { /* negative sign reproduces results for NSC=28393 */ - goto done; - } - for ( i = startAt1; i < endAt1 && Ct1->iso_sort_key[i] == Ct2->iso_sort_key[i]; i ++ ) - ; - if ( i < endAt1 ) { - diff = Ct1->iso_sort_key[i] > Ct2->iso_sort_key[i]? 1:-1; - goto done; - } - } -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - /*************************************************************************/ - /************** layer 6: Fixed isotopic H atoms **************************/ - /*************************************************************************/ - nLayer ++; - if ( Ct1->iso_sort_key_Hfixed && Ct2->iso_sort_key_Hfixed ) { - for ( i = startAt1; i < midAt && Ct1->iso_sort_key_Hfixed[i] == Ct2->iso_sort_key_Hfixed[i]; i ++ ) - ; - if ( i < midAt ) { - diff = Ct1->iso_sort_key_Hfixed[i] > Ct2->iso_sort_key_Hfixed[i]? 1:-1; - goto done; - } - } -#endif - -done: -#ifdef INCHI_CANON_MIN - diff = -diff; -#endif - - if ( diff ) { - diff = (diff > 0)? (nLayer+1) : -(nLayer+1); /* return the discovered difference layer number >= 1 */ - } - return diff; -} -/****************************************************************/ -int CtFullCompareLayers( kLeast *kLeastForLayer ) -{ - int iLayer; - /* check for the rejection condition: Lambda > zb_rho_fix */ - for ( iLayer = 0; iLayer < MAX_LAYERS; iLayer ++ ) { - if ( kLeastForLayer[iLayer].k ) { - return (kLeastForLayer[iLayer].k > 0)? (iLayer+1) : -(iLayer+1); - } - } - return 0; -} -/**************************************************************/ -int CtCompareLayersGetFirstDiff( kLeast *kLeast_rho, int nOneAdditionalLayer, - int *L_rho, int *I_rho, int *k_rho ) -{ - int iLayer; - if ( kLeast_rho ) { - for ( iLayer = 0; iLayer < MAX_LAYERS; iLayer ++ ) { - if ( kLeast_rho[iLayer].k ) { - *L_rho = iLayer; - *I_rho = kLeast_rho[iLayer].i; - *k_rho = kLeast_rho[iLayer].k; - break; - } - } - if ( iLayer == MAX_LAYERS ) { - if ( nOneAdditionalLayer ) { - *L_rho = nOneAdditionalLayer; /* ??? subtract 1 ??? */ - *I_rho = -1; - *k_rho = 0; - return 0; /* difference may be in the first additional layer */ - } else { - *L_rho = INFINITY; - *I_rho = -1; - *k_rho = 0; - return 0; /* no difference found */ - } - } else { - return 1; /* difference in a real layer */ - } - } else { - return -1; /* no input, should not happen */ - } -} -/**************************************************************/ -int CtPartCompareLayers( kLeast *kLeast_rho, int L_rho_fix_prev, int nOneAdditionalLayer ) -{ - int L_rho, I_rho, k_rho; - if ( 0 < CtCompareLayersGetFirstDiff( kLeast_rho, nOneAdditionalLayer, &L_rho, &I_rho, &k_rho ) && - /* differences has been found in a real layer or all real layers are identical */ - L_rho <= L_rho_fix_prev ) { - /* in this layer pzb_rho == pzb_rho_fix or in the previous real layer */ - return k_rho > 0? (L_rho+1) : -(L_rho+1); - } - return 0; -} -/****************************************************************/ -void UpdateCompareLayers( kLeast kLeastForLayer[], int hzz ) -{ - int i; - if ( kLeastForLayer ) { - for ( i = 0; i < MAX_LAYERS; i ++ ) { - if ( abs(kLeastForLayer[i].k) >= hzz ) { - kLeastForLayer[i].k = 0; - kLeastForLayer[i].i = 0; - } - } - } -} - -/****************************************************************/ -void CtPartCopy( ConTable *Ct1 /* to */, ConTable *Ct2 /* from */, int k ) -{ - int startCt1, startCt2, endCt1, endCt2; - int len2, len2H, len2Hfixed, len2iso_sort_key, len2iso_exchg_atnos, i; - int startAt1, endAt1, startAt2, endAt2; /*endCt,*/ -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - int len2iso_sort_key_Hfixed; -#endif - k --; - if ( k ) { - startCt1 = Ct1->nextCtblPos[k-1]; - startCt2 = Ct2->nextCtblPos[k-1]; - startAt1 = Ct1->nextAtRank[k-1]-1; - startAt2 = Ct2->nextAtRank[k-1]-1; - } else { - startCt1 = startCt2 = 0; - startAt1 = startAt2 = 0; - } - - endCt1 = Ct1->nextCtblPos[k]; - endCt2 = Ct2->nextCtblPos[k]; - endAt1 = (int)Ct1->nextAtRank[k]-1; - endAt2 = (int)Ct2->nextAtRank[k]-1; - - len2 = endCt2-startCt2; - /* len = min(len1, len2); */ -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - if ( startCt1 != startCt2 || startAt1 != startAt2 ) { - int stop = 1; - } -#endif - - /* copy connection table: Ctbl */ - for ( i = 0; i < len2; i ++ ) { - Ct1->Ctbl[startCt1+i] = Ct2->Ctbl[startCt2+i]; - } - /* copy number of H: NumH */ - len2H = 0; - if ( Ct1->NumH && Ct2->NumH ) { - len2H = endAt2-startAt2; - if ( endAt2 > Ct2->maxVert ) { - len2H = Ct2->lenNumH - startAt2; - } - for ( i = 0; i < len2H; i ++ ) { - Ct1->NumH[startAt1+i] = Ct2->NumH[startAt2+i]; - } - } - /* copy number of fixed H */ - len2Hfixed = 0; - if ( Ct1->NumHfixed && Ct2->NumHfixed ) { - len2Hfixed = endAt2-startAt2; - for ( i = 0; i < len2Hfixed; i ++ ) { - Ct1->NumHfixed[startAt1+i] = Ct2->NumHfixed[startAt2+i]; - } - } - /* copy isotopic keys */ - len2iso_sort_key = 0; - if ( Ct1->iso_sort_key && Ct2->iso_sort_key ) { - len2iso_sort_key = endAt2-startAt2; - for ( i = 0; i < len2iso_sort_key; i ++ ) { - Ct1->iso_sort_key[startAt1+i] = Ct2->iso_sort_key[startAt2+i]; - } - } - len2iso_exchg_atnos = 0; - if ( Ct1->iso_exchg_atnos && Ct2->iso_exchg_atnos ) { - len2iso_exchg_atnos = endAt2-startAt2; - for ( i = 0; i < len2iso_exchg_atnos; i ++ ) { - Ct1->iso_exchg_atnos[startAt1+i] = Ct2->iso_exchg_atnos[startAt2+i]; - } - } -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - len2iso_sort_key_Hfixed = 0; - if ( Ct1->iso_sort_key_Hfixed && Ct2->iso_sort_key_Hfixed ) { - len2iso_sort_key_Hfixed = endAt2-startAt2; - for ( i = 0; i < len2iso_sort_key; i ++ ) { - Ct1->iso_sort_key_Hfixed[startAt1+i] = Ct2->iso_sort_key_Hfixed[startAt2+i]; - } - } -#endif - Ct1->lenCt = startCt1 + len2; - Ct1->nextCtblPos[k] = startCt1 + len2; - Ct1->nextAtRank[k] = Ct2->nextAtRank[k]; - if ( len2H ) { - Ct1->lenNumH = startAt1 + len2H; - } - /* - if ( len2Hfixed ) { - Ct1->lenNumHfixed = startAt1 + len2Hfixed; - } - */ - if ( len2iso_sort_key ) { - Ct1->len_iso_sort_key = startAt1 + len2iso_sort_key; - } - if ( len2iso_exchg_atnos ) { - Ct1->len_iso_exchg_atnos = startAt1 + len2iso_exchg_atnos; - } - -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - if ( len2iso_sort_key_Hfixed ) { - Ct1->len_iso_sort_key_Hfixed = startAt1 + len2iso_sort_key_Hfixed; - } -#endif -#ifdef INCHI_CANON_USE_HASH - Ct1->hash[k] = Ct2->hash[k]; -#endif - Ct1->lenPos = k+1; - INCHI_HEAPCHK -} -/****************************************************************/ -void CtFullCopy( ConTable *Ct1, ConTable *Ct2 ) -{ - /* Ct1 does not have INFINITY termination */ - int k; - for ( k = 0; k < Ct2->lenPos; k ++ ) { - CtPartCopy( Ct1 /* to */, Ct2 /* from */, k+1 ); - } -} -/****************************************************************/ -void TranspositionGetMcrAndFixSetAndUnorderedPartition( Transposition *gamma, NodeSet *McrSet, NodeSet *FixSet, int n, int l, UnorderedPartition *p ) -{ - int i, j, k, mcr, num; - AT_RANK next; - bitWord *McrBits = McrSet->bitword[l-1]; - bitWord *FixBits = FixSet->bitword[l-1]; - int len = McrSet->len_set*sizeof(bitWord); - - memset( McrBits, 0, len ); - memset( FixBits, 0, len ); - for ( i = 0; i < n; i ++ ) { - p->equ2[i] = INFINITY; /* for debug only */ - } - - for ( i = 0; i < n; i ++ ) { - j = (int)(next = gamma->nAtNumb[i]); - if ( j == i ) { - FixBits[ i / num_bit ] |= bBit[ i % num_bit ]; - McrBits[ i / num_bit ] |= bBit[ i % num_bit ]; - /* p->next[i] = INFINITY; */ /* no link to same orbit points */ - p->equ2[i] = next; /* fixed point */ - } else - if ( !(rank_mark_bit & next) ) { - gamma->nAtNumb[i] |= rank_mark_bit; - mcr = inchi_min(j, i); - num = 0; - /* mark all nodes in the cycle to ignore later; find mcr */ - while( !(rank_mark_bit & (next = gamma->nAtNumb[j])) ) { - gamma->nAtNumb[j] |= rank_mark_bit; - j = (int)next; - if ( mcr > j ) { - mcr = j; - } - num ++; - } - McrBits[ mcr / num_bit ] |= bBit[mcr % num_bit]; /* save mcr */ - /* fill out the unordered partition, the mcr first, other in the cycle after that */ - p->equ2[mcr] = mcr; - for ( k = mcr; mcr != (j = (int)(rank_mask_bit & gamma->nAtNumb[k])); k = j ) { - p->equ2[j] = mcr; - } - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - /* for debug only */ - for ( i = 0; i < n; i ++ ) { - if ( p->equ2[i] >= n ) { - int stop = 1; - } - } -#endif - /* remove the marks */ - for ( i = 0; i < n; i ++ ) { - gamma->nAtNumb[i] &= rank_mask_bit; - } - INCHI_HEAPCHK -} -/****************************************************************/ -int SetBitCreate( void ) -{ - bitWord b1, b2; - AT_NUMB n1, n2; -#ifdef INCHI_CANON_USE_HASH - CtHash h1, h2; -#endif - int i; - - if ( bBit ) { - INCHI_HEAPCHK - return 0; /* already created */ - } - - b1 = 1; - num_bit = 1; - for ( b1=1, num_bit=1; b1 < (b2 = (bitWord)((b1 << 1)& BIT_WORD_MASK)); b1 = b2, num_bit ++ ) - ; - bBit = (bitWord*)inchi_calloc( num_bit, sizeof(bitWord)); - if ( !bBit ) { - INCHI_HEAPCHK - return -1; /* failed */ - } - for ( i = 0, b1=1; i < num_bit; i++, b1 <<= 1 ) { - bBit[i] = b1; - } - - for ( n1 = 1; n1 < (n2 = (AT_RANK)((n1 << 1)& AT_RANK_MASK) ); n1 = n2 ) - ; - rank_mark_bit = n1; - rank_mask_bit = ~n1; - -#ifdef INCHI_CANON_USE_HASH - for ( h1 = 1; h1 < (h2 = (h1 << 1)); h1 = h2 ) - ; - hash_mark_bit = h1; -#endif - INCHI_HEAPCHK - return 1; -} -/****************************************************************/ -int SetBitFree( void ) -{ - if ( bBit ) { - inchi_free( bBit ); - bBit = NULL; - INCHI_HEAPCHK - return 1; /* success */ - } - INCHI_HEAPCHK - return 0; /* already destroyed */ -} -#ifdef NEVER /* { how to renumber a graph */ -/*********************************************************************/ -void RenumberTheGraph( int n, NEIGH_LIST *NeighList, AT_NUMB *old2new, - AT_NUMB *new2old, S_CHAR *mark, int bDone ) -{ - int i, k, j; - NEIGH_LIST nl; - - /* renumber neighbors */ - for ( i = 0; i < n; i ++ ) { - for ( j = 1; j <= NeighList[i][0]; j ++ ) { - NeighList[i][j] = old2new[NeighList[i][j]]; - } - } - /* rearrange NeighList in situ using new2old[] */ - for ( k = 0; k < n; k ++ ) { - if ( mark[k] & bDone ) - continue; - if ( k == ( j = new2old[k] ) ) { - mark[k] |= bDone; - continue; - } - /* transposition cycle */ - i = k; - nl = NeighList[k]; - do { - NeighList[i] = NeighList[j]; - mark[i] |= bDone; - i = j; - } while ( k != ( j = new2old[i] ) ); - NeighList[i] = nl; - mark[i] |= bDone; - } -#ifdef NEVER - /* rearrange NeighList in situ using old2new[] */ - s = 0; - for ( k = 0; k < n; k ++ ) { - if ( mark[k] & bDone ) - continue; - if ( k == ( j = old2new[k] ) ) { - mark[k] |= bDone; - continue; - } - /* transposition cycle */ - i = k; - /* NeighList[j] goes to ith position */ - nl2[s] = NeighList[j]; - s ^= 1; - do { - nl2[s] = NeighList[i]; - NeighList[i] = nl2[s ^= 1]; - mark[i] |= bDone; - i = j; - j = old2new[i]; - } while ( k != ( j = old2new[i] ) ); - NeighList[j] = nl2[s ^= 1]; - mark[j] |= bDone; - } -#endif -} -/***************************************************************************************/ -void RearrangeAtRankArray ( int n, AT_RANK *nRank, AT_NUMB *new2old, S_CHAR *mark, int bDone ) -{ - int i, k, j; - AT_RANK r; - /* rearrange the array in situ using new2old[] */ - for ( k = 0; k < n; k ++ ) { - if ( mark[k] & bDone ) - continue; - if ( k == ( j = new2old[k] ) ) { - mark[k] |= bDone; - continue; - } - /* transposition cycle */ - i = k; - r = nRank[k]; - do { - nRank[i] = nRank[j]; - mark[i] |= bDone; - i = j; - } while ( k != ( j = new2old[i] ) ); - nRank[i] = r; - mark[i] |= bDone; - } - -} -/***************************************************************************************/ -void RenumberAtNumbArray( int n, AT_NUMB *nAtNumb, AT_NUMB *old2new ) -{ - int i; - for ( i = 0; i < n; i ++ ) { - nAtNumb[i] = old2new[nAtNumb[i]]; - } -} -/****************************************************************/ -int GetCanonRanking2( int num_atoms, int num_at_tg, int num_max, int bDigraph, sp_ATOM* at, - AT_RANK **pRankStack, int nNumPrevRanks, - AT_RANK *nSymmRank, AT_RANK *nCanonRank, - NEIGH_LIST *NeighList, AT_RANK *nTempRank, - CANON_STAT* pCS ) -{ - void *pzb_rho=NULL; - int ret, cmp1=0, cmp2=0; - - int i, j, k, n; - AT_NUMB *old2new = NULL, *new2old = NULL, m, r1, r2; - S_CHAR *mark = NULL; - - int nMaxLenCt = pCS->nMaxLenLinearCT; - AT_RANK *pCt = pCS->LinearCT; - int nLenCt = pCS->nLenLinearCT; - AT_RANK *pCt0 = pCS->LinearCT; - int nLenCt0 = pCS->nLenLinearCT; - - - CANON_DATA CanonData; - CANON_DATA *pCD = &CanonData; - CANON_COUNTS CanonCounts; - CANON_COUNTS *pCC = &CanonCounts; - memset (pCD, 0, sizeof(pCD[0])); - memset (pCC, 0, sizeof(pCC[0])); - /* pointers */ - pCD->LinearCT = pCS->LinearCT; - /* variables - unchanged */ - pCD->ulTimeOutTime = pCS->ulTimeOutTime; - pCD->nMaxLenLinearCT = pCS->nMaxLenLinearCT; - /* return values & input/output */ - pCD->nLenLinearCT = pCS->nLenLinearCT; - - pCC->lNumBreakTies = pCS->lNumBreakTies; - pCC->lNumDecreasedCT = pCS->lNumDecreasedCT; - pCC->lNumRejectedCT = pCS->lNumRejectedCT; - pCC->lNumEqualCT = pCS->lNumEqualCT; - pCC->lNumTotCT = pCS->lNumTotCT; - - ret = CanonGraph( num_atoms, num_at_tg, num_max, bDigraph, NeighList, (Partition *)pRankStack, - nSymmRank, nCanonRank, pCS->nPrevAtomNumber, pCD, pCC, NULL, &pzb_rho ); - - pCS->nLenLinearCT = pCD->nLenLinearCT; - pCS->lNumBreakTies = pCC->lNumBreakTies; - pCS->lNumDecreasedCT = pCC->lNumDecreasedCT; - pCS->lNumRejectedCT = pCC->lNumRejectedCT; - pCS->lNumEqualCT = pCC->lNumEqualCT; - pCS->lNumTotCT = pCC->lNumTotCT; - - - /* save the connection table for comparison with the 2nd one */ - pCt0 = (AT_RANK*)inchi_calloc(nMaxLenCt, sizeof(pCt0[0])); - memcpy(pCt0, pCS->LinearCT, nMaxLenCt*sizeof(pCt0[0])); - nLenCt0 = pCS->nLenLinearCT; - - /**********************************************************/ - /* rearrange numbering to make canon. numbering the first */ - /**********************************************************/ - n = num_at_tg; - /* 1. get transpositions */ - old2new = (AT_NUMB*) inchi_calloc( n, sizeof(old2new[0]) ); - new2old = (AT_NUMB*) inchi_calloc( n, sizeof(new2old[0]) ); - mark = (S_CHAR *) inchi_calloc( n, sizeof(mark[0]) ); - for ( i = 0; i < n; i ++ ) { - /* forward transposition: at[i] -> at[old2new[i]] position */ - old2new[i] = m = nCanonRank[i]-1; - /* forward transposition: at[new2old[i]] -> at[i] position */ - new2old[m] = i; - } - /* rearrange input data according to the new numbering */ - RenumberTheGraph( n, NeighList, old2new, new2old, mark, 1 ); - RearrangeAtRankArray ( n, pRankStack[0], new2old, mark, 2 ); - RenumberAtNumbArray ( n, pRankStack[1], old2new ); - /* make sure the atom numbers are sorted */ - for ( i = k = 0, r1 = pRankStack[0][pRankStack[1][i]]; i < n; r1 = r2) { - for ( j = i++; i < n && r1 == (r2 = pRankStack[0][pRankStack[1][i]]); i ++ ) - ; - if ( i - j > 1 ) { - k += insertions_sort_AT_RANK( pRankStack[1]+j, i-j ); - } - } - - ret = CanonGraph( num_atoms, num_at_tg, num_max, bDigraph, NeighList, (Partition *)pRankStack, - nSymmRank, nCanonRank, pCS->nPrevAtomNumber, pCD, pCC, &pzb_rho, NULL ); - - pCS->nLenLinearCT = pCD->nLenLinearCT; - pCS->lNumBreakTies = pCC->lNumBreakTies; - pCS->lNumDecreasedCT = pCC->lNumDecreasedCT; - pCS->lNumRejectedCT = pCC->lNumRejectedCT; - pCS->lNumEqualCT = pCC->lNumEqualCT; - pCS->lNumTotCT = pCC->lNumTotCT; - - - /* compare the connection tables */ - cmp1 = nLenCt0 - pCS->nLenLinearCT; - cmp2 = memcmp( pCt0, pCS->LinearCT, pCS->nLenLinearCT*sizeof(pCt0[0])); -#ifdef _DEBUG - if ( cmp1 || cmp2 ) { - int stop = 1; - } -#endif - /**********************************************************/ - /* rearrange numbering back to the original numbering */ - /**********************************************************/ - /* restore the input data to its original numbering */ - RenumberTheGraph( n, NeighList, new2old, old2new, mark, 4 ); - RearrangeAtRankArray ( n, pRankStack[0], old2new, mark, 8 ); - RenumberAtNumbArray ( n, pRankStack[1], new2old ); - /* rearrange the output data to the original numbering */ - RearrangeAtRankArray ( n, nCanonRank, old2new, mark, 16 ); - RenumberAtNumbArray ( n, pCS->nPrevAtomNumber, new2old ); - RearrangeAtRankArray ( n, nSymmRank, old2new, mark, 32 ); - - /* free memory */ - CTableFree( pzb_rho ); - if ( pzb_rho ) { - inchi_free( pzb_rho ); - } - inchi_free( old2new ); - inchi_free( new2old ); - inchi_free( mark ); - inchi_free( pCt0 ); - - return ret; -} -#endif /* } */ - -#define QZFIX_OK(X) ((X)<=0) - -int GetOneAdditionalLayer( CANON_DATA *pCD, ConTable *pzb_rho_fix ) -{ - int nLastLayer = -1, nNumLast = 0, nLayer = 0; - - if ( !pCD || !pzb_rho_fix ) { - return 0; - } - - nLayer ++; /* 1 */ - if ( pCD->NumH && !pzb_rho_fix->NumH ) { - nLastLayer = nLayer; - nNumLast ++; - } - nLayer ++; /* 2 */ - if ( pCD->nLenCTAtOnly < pCD->nLenLinearCT && pzb_rho_fix->nLenCTAtOnly == pzb_rho_fix->lenCt ) { - nLastLayer = nLayer; - nNumLast ++; - } - nLayer ++; /* 3 */ - if ( pCD->NumHfixed && !pzb_rho_fix->NumHfixed ) { - nLastLayer = nLayer; - nNumLast ++; - } - nLayer ++; /* 4 */ - if ( pCD->iso_sort_key && !pzb_rho_fix->iso_sort_key ) { - nLastLayer = nLayer; - nNumLast ++; - } - /* - nLayer ++; // 5 - if ( pCD->nLenCTAtOnly < pCD->nLenLinearCT && pCD->iso_sort_key && - (pzb_rho_fix->nLenCTAtOnly == pzb_rho_fix->lenCt || !pzb_rho_fix->iso_sort_key ) ) { - nLastLayer = nLayer; - nNumLast ++; - } - */ -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - nLayer ++; /* 6 */ - if ( pCD->iso_sort_key_Hfixed && !pzb_rho_fix->iso_sort_key_Hfixed ) { - nLastLayer = nLayer; - nNumLast ++; - } -#endif - if ( 1 == nNumLast ) { - return nLastLayer; - } - return 0; -} - - -/*#define QZFIX_OK(X) (!(X))*/ - -/********************** CanonGraph ************************************* - * A naive implementation of graph canonical numbering algorithm * - * from "Practical Graph Isomorphism" by Brendan D. McKay, * - * Congressus Numerantium, Vol. 30 (1981), pp. 45 - 87. * - * Note: Several typos fixed, added chem. struct. specifics * - ***********************************************************************/ - -/* on entry: pi[0] is equitable */ -/*******************************************************10/21/2003****** - * Later add optimization: if Aut(G) <= Aut(G0) due to some additional * - * layer of coloring applied to G and the following is known about G0: * * - * * - * 0) canonical numbering of G should be same as that of G0 * - * * - * 1) canonical numbering as v= v0[n] {vertex number v from * - * G0 canonical number n) * - * 2) orbits of Aut(G0) as UnorderedPartition theta0 * - * * - * then when choosing next v[i] for refining the partition consider * - * only vertices from the Aut(G0) orbit of V(i), that is, only such * - * v[i] that: * - * * - * GetUnorderedPartitionMcrNode( &theta0, v[i] ) == * - * GetUnorderedPartitionMcrNode( &theta0, v0[i] ) * - ***********************************************************************/ -int CanonGraph( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], - AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, - CANON_DATA *pCD, CANON_COUNTS *pCC, - ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out ) -{ - /* bDigraph != 0 - means consider edges from atoms to t-groups - as directed, that is, do not include - t-group ranks in comparing neighbors - when refining partition - */ - - /* Always set - lab = true - dig = true - */ - - /* in the comments: - m = |zeta| - r = |rho| - - m < n or r < n in case pi[k] in P (i.e. satisfies Lemma 2.25) - - Just after passing point B: - =========================== - K = k-1 - wi = v[i], i = 1..K - Gamma(0) = Gamma = Aut(G)pi - Gamma(i) = Gamma(w1,w2,...,wi) pointwise stabilizer for i=1..K - zeta is a terminal node => - the coarsest equitable partition that fixes w1,...,wK is discrete => - Gamma(K)=1 - At point A only: - index = |Gamma(k-1)|/|Gamma(k)| - At points A and B: - size = |Gamma(k-1)| - theta = theta(Gamma(k-1)); - Gamma(k-1) = , where Y is the set of all automprhisms output up - to the present stage (in Step 10 = L10 ) - |Y| <= n - |theta| - */ - - AT_RANK *pCt = pCD->LinearCT; - /*int nMaxLenCt = pCD->nMaxLenLinearCT;*/ - int *nLenCt = &pCD->nLenLinearCT; - CANON_DATA *pCD1 = pCD; - - int i, k, k2, index, l, ok, ret=0, res; - int t_Lemma; /* hh: if pi[k-1] satisfies Lemma 2-25 then - t_Lemma = min{i| i=1..k && pi[i-1] satisfies Lemma 2-25}*/ - /* otherwise t_Lemma = k --> here this is always the case */ - int t_eq_zeta; /* ht: min{i|i=1..m && all terminal modes descended from or equal - to zeta(i) have been shown to be equivalent}. */ - int h_zeta; /* h: the longest common ancestor of zeta and nu is nu(h_zeta) */ - int h_rho; /* hb: the longest common ancestor of rho and nu is nu(h_rho) */ - int hz_rho; /* hzb: max{i|i=1..min(k,r) && Lambda(G,pi,nu(i)) == Lambda(G,pi,rho(i))} */ - int hz_zeta; /* hzf: max{i|i=1..min(k,m) && Lambda(G,pi,nu(i)) == Lambda(G,pi,zeta(i))} */ - int qzb_rho; /* Ct(Lambda[k]) - Ct(rho[k]) */ - double size; /* |Aut(G)| */ - int nNumLayers = (NULL != pCD->NumH) + (NULL != pCD->NumHfixed) + - /* (bDigraph && pCD->nLenLinearCT > pCD->nLenCTAtOnly)*/ /* ??? tautomeric */ - (NULL != pCD->iso_sort_key) -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - + (NULL != pCD->iso_sort_key_Hfixed) -#endif - ; - int dig = (bDigraph || nNumLayers); - int bSplitTautCompare = (bDigraph || nNumLayers); /* compare taut. H and tgroups connections after H */ - /* digraph: 1=>do not use Lemma 2.25, 0 => use */ - int lab = 1; /* label: 1=>find canonical numbering; - 0=>do not find canonical numbering, do not use rho */ - int r; /* |rho| */ - int bZetaEqRho = lab; - int bZetaIsomorph; - - long lNumEqlZeta; - - - const int L = MAX_SET_SIZE; - UnorderedPartition theta, theta_from_gamma; - Cell *W; /* W[i] is the first non-trivial cell of pi[i+1] */ - Node *v; /* v[i] is in W[i] to create T(G,pi,nu[i+1]) */ - Node tvc, tvh; - S_CHAR *e, *qzb=NULL; /* qzb = NULL always */ - /* current node CT */ - ConTable Lambda; - /* first leaf CT */ - ConTable zf_zeta; /* Ct for zeta, the first discovered terminal node */ - /* best leaf/node CT: find the greatest pzb_rho possibly subject to pzb_rho[k] <= pzb_rho_fix[k] condition */ - ConTable *pzb_rho = NULL; /* Ct for rho, the best discovered terminal node */ - /* fixed input CT: for all k pzb_rho[k] <= pzb_rho_fix[k]; at the end pzb_rho == pzb_rho_fix */ - ConTable *pzb_rho_fix = (pp_zb_rho_inp && *pp_zb_rho_inp)? *pp_zb_rho_inp:NULL; - - NodeSet Omega; /* MAX_SET_SIZE */ - NodeSet Phi; /* MAX_SET_SIZE */ - NodeSet cur_nodes; /* 1 each */ - Transposition gamma; - Partition zeta; /* the first discovered terminal node */ - Partition rho; /* the best discovered terminal node */ - int nNumFoundGenerators=0; - int qzb_rho_fix = 0; - int hzb_rho_fix = 0; - int bRhoIsDiscrete = 1; - kLeast kLeast_rho[MAX_LAYERS]; - kLeast kLeast_rho_fix[MAX_LAYERS]; - int nOneAdditionalLayer; - int pzb_rho_fix_reached = 0; - int L_rho_fix_prev = 0, I_rho_fix_prev=-1, k_rho_fix_prev=0; - - /* Note: Layered comparison should be consistent, especially in layer numbers. - Layered comparison is implemented in: - CtFullCompare() - CtPartCompare() - GetOneAdditionalLayer() - - The partial comparison results in kLeast[] are used in - CtFullCompareLayers() - CtPartCompareLayers() - CtCompareLayersGetFirstDiff() - UpdateCompareLayers() - */ - nOneAdditionalLayer = GetOneAdditionalLayer( pCD1, pzb_rho_fix ); - - - /* next 2 lines for debug only */ - /* num_g++; */ - /* WriteGraph( G, n_tg, num_g, "V:\\IChI_v10\\Gordon-Graphs\\hard\\k06g08v312-alt.dre", "a+" ); */ - - /* memory allocation */ - - if ( 0 > SetBitCreate() ) { - return -1; - } - if ( pzb_rho_fix && pzb_rho_fix->nLenCTAtOnly != pCD->nLenCTAtOnly ) { - /* consistency check */ - return -2; - } - ok = 1; - - - ok &= UnorderedPartitionCreate( &theta, n_tg ); - ok &= UnorderedPartitionCreate( &theta_from_gamma, n_tg ); - - ok &= (NULL != (W = (Cell*)inchi_calloc(n_tg, sizeof(W[0])))); - ok &= (NULL != (v = (Node*)inchi_calloc(n_tg, sizeof(v[0])))); - ok &= (NULL != (e = (S_CHAR*)inchi_calloc(n_tg, sizeof(e[0])))); - -/* - ok &= (NULL != (v = (Node*)inchi_calloc(n_tg, sizeof(W[0])))); - ok &= (NULL != (e = (S_CHAR*)inchi_calloc(n_tg, sizeof(W[0])))); -*/ - - /* ok &= (NULL != (qzb = (S_CHAR*)calloc(n_tg, sizeof(W[0])))); */ - ok &= CTableCreate( &Lambda, n, pCD ); - ok &= CTableCreate( &zf_zeta, n, pCD ); - ok &= ( (pzb_rho = (ConTable *)inchi_calloc( 1, sizeof( *pzb_rho ) ) ) && - CTableCreate( pzb_rho, n, pCD ) ); - - ok &= NodeSetCreate( &Omega, n_tg, MAX_SET_SIZE ); - ok &= NodeSetCreate( &Phi, n_tg, MAX_SET_SIZE ); - ok &= NodeSetCreate( &cur_nodes, n_tg, 1 ); - - ok &= PartitionCreate( &zeta, n_tg); - ok &= PartitionCreate( &rho, n_tg); - ok &= TranspositionCreate( &gamma, n_tg ); - - INCHI_HEAPCHK - - -/*L1:*/ - k = 1; - size = 1.0; - h_zeta = hz_rho = index = l = 0; - - if ( !ok ) { - goto exit_function; /* initialization failed */ - } - - UnorderedPartitionMakeDiscrete(&theta, n_tg); - t_Lemma = 2; - - pCC->lNumBreakTies = 0; - pCC->lNumDecreasedCT = 0; - pCC->lNumRejectedCT = 0; - pCC->lNumEqualCT = 1; - pCC->lNumTotCT = 0; - lNumEqlZeta = 1; - - hzb_rho_fix = 1; - - memset( kLeast_rho, 0, sizeof(kLeast_rho) ); - memset( kLeast_rho_fix, 0, sizeof(kLeast_rho_fix) ); - - if ( PartitionIsDiscrete( &pi[k-1], n_tg ) ) { - /* added the following 3 lines to the original to create Ct */ - PartitionCopy( &rho, &pi[k-1], n_tg ); - CtPartFill( G, pCD, &pi[k-1], pzb_rho, 1, n, n_tg ); - CtPartInfinity( pzb_rho, qzb, 2 ); - pCC->lNumTotCT ++; - r = k; - /* goto L18; */ - goto exit_function; - } - if ( !dig && PartitionSatisfiesLemma_2_25( &pi[0], n ) ) - t_Lemma = 1; - /* - PartitionGetFirstCell( &pi[k-1], &W[k-1], k, n ); - v[k-1] = CellGetMinNode( &pi[k-1], &W[k-1], 0, pCD1 ); - CtPartClear( &Lambda, 1 ); - e[k-1] = 0; - */ - CtPartClear( &Lambda, 1 ); - INCHI_HEAPCHK - -/* L2: reach the first leaf and save it in zeta and rho */ - while( k ) { - /* the two next lines intentionally switched */ - /* Create equitable partition in pi[k] */ - PartitionGetFirstCell( &pi[k-1], W, k, n ); - v[k-1] = CellGetMinNode( &pi[k-1], &W[k-1], 0, pCD1 ); - e[k-1] = 0; - if ( dig || !PartitionSatisfiesLemma_2_25(&pi[k-1], n) ) - t_Lemma = k+1; - /* e[k-1] = 0; */ - { Node vv = v[k-1]; - if ( 0 > (ret=PartitionColorVertex( G, &pi[k-1], vv /*v[k-1]*/, n, n_tg, n_max, bDigraph, 0 )) ) { - goto exit_error; - }} - pCC->lNumBreakTies ++; - k ++; - CtPartFill( G, pCD, &pi[k-1], &Lambda, k-1, n, n_tg ); - /* return -1; *//* debug only */ - /* if(h_zeta==0)goto L5; L5: */ - /* the first terminal node has not been reached yet */ - /* search for the predefined numbering */ - if ( pzb_rho_fix && QZFIX_OK(qzb_rho_fix) ) { - qzb_rho_fix = CtPartCompare( &Lambda, pzb_rho_fix, qzb, kLeast_rho_fix, k-1, 1, bSplitTautCompare ); - if ( QZFIX_OK(qzb_rho_fix) ) { - hzb_rho_fix = k; - } - } - - if ( lab && QZFIX_OK(qzb_rho_fix) ) /* DCh */ - CtPartCopy( pzb_rho, &Lambda, k-1 ); - CtPartCopy( &zf_zeta, &Lambda, k-1 ); - /*goto L4; L4:*/ - if ( PartitionIsDiscrete( &pi[k-1], n ) ) { - break; /* goto L7; */ - } - /* goto L2; */ - } - pCC->lNumTotCT ++; - /* L7; L7: */ - /* if ( h_zeta == 0 ) goto L18; L18:*/ - h_zeta = t_eq_zeta = hz_zeta = k; - CtPartInfinity( &zf_zeta, NULL, k ); - /******************** <<<===== B **************************/ - PartitionCopy( &zeta, &pi[k-1], n_tg ); - if ( lab ) { - if ( pzb_rho_fix ) { - if ( 0 == qzb_rho_fix ) { - qzb_rho_fix = CtFullCompare( &Lambda, pzb_rho_fix, 1, bSplitTautCompare ); - if ( qzb_rho_fix > 0 ) { - hzb_rho_fix = 1; - } - } - if ( hzb_rho_fix > 1 ) { - PartitionCopy( &rho, &pi[hzb_rho_fix-1], n_tg ); - /*CtPartInfinity( pzb_rho, qzb, k );*/ - } - hz_rho = h_rho = hzb_rho_fix; - bRhoIsDiscrete = (hzb_rho_fix == k); - if ( bRhoIsDiscrete ) { - CtPartInfinity( pzb_rho, qzb, k ); - pzb_rho_fix_reached = !qzb_rho_fix; - CtCompareLayersGetFirstDiff( kLeast_rho_fix, nOneAdditionalLayer, - &L_rho_fix_prev, &I_rho_fix_prev, &k_rho_fix_prev ); - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; - } -#endif - } else { - PartitionCopy( &rho, &pi[k-1], n_tg ); - hz_rho = h_rho = k; - CtPartInfinity( pzb_rho, qzb, k ); - } - qzb_rho = 0; - } - r = k; - v[k-1] = INFINITY; /* DCh */ - CellMakeEmpty( W, k ); /* DCh */ - k --; - goto L13; - - -L2: - /* the two next lines intentionally switched */ - /* Create equitable partition in pi[k] */ - if ( 0 > (ret=PartitionColorVertex( G, &pi[k-1], v[k-1], n, n_tg, n_max, bDigraph, 0 )) ) { - goto exit_error; - } - pCC->lNumBreakTies ++; - k ++; - CtPartFill( G, pCD, &pi[k-1], &Lambda, k-1, n, n_tg ); - e[k-1] = 0; /* moved */ - v[k-1] = INFINITY; /* added by DCh. */ - CellMakeEmpty( W, k ); /* DCh */ - - if ( hz_zeta == k-1 && 0 == CtPartCompare( &Lambda, &zf_zeta, NULL, NULL, k-1, 0, bSplitTautCompare ) ) { - hz_zeta = k; /* max{k|Lambda(G,pi,nu(k))==Lambda(G,pi,zeta) } */ - } /* added */ - - /* -- old code --- - if ( pzb_rho_fix && QZFIX_OK(qzb_rho_fix) ) { - qzb_rho_fix = CtPartCompare( &Lambda, pzb_rho_fix, qzb, kLeast_rho_fix, k-1, 1, bSplitTautCompare ); - if ( QZFIX_OK(qzb_rho_fix) ) { - hzb_rho_fix = k; - } else { - pCC->lNumRejectedCT ++; - } - } - */ - - /* --- new code ---*/ - if ( pzb_rho_fix && !qzb_rho_fix ) { - qzb_rho_fix = CtPartCompare( &Lambda, pzb_rho_fix, qzb, kLeast_rho_fix, k-1, 1, bSplitTautCompare ); - if ( !qzb_rho_fix && bRhoIsDiscrete ) { - qzb_rho_fix = CtPartCompareLayers( kLeast_rho_fix, L_rho_fix_prev, nOneAdditionalLayer ); - -#if ( FIX_ChCh_CONSTIT_CANON_BUG == 1 ) - if ( qzb_rho_fix ) { - int L_rho_fix_diff = abs(qzb_rho_fix)-1; - if ( L_rho_fix_diff < L_rho_fix_prev || - L_rho_fix_diff == L_rho_fix_prev && kLeast_rho_fix[L_rho_fix_diff].i < I_rho_fix_prev ) { - qzb_rho_fix = L_rho_fix_diff+1; /* positive difference will be rejected */ - } - } -#endif - -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - if ( qzb_rho_fix ) { - int stop = 1; /* debug only */ - } -#endif - } - if ( !QZFIX_OK(qzb_rho_fix) ) { - pCC->lNumRejectedCT ++; - } - } - if ( pzb_rho_fix && QZFIX_OK(qzb_rho_fix) ) { - hzb_rho_fix = k; - } - /* if (!lab) goto L3; */ - if ( lab && QZFIX_OK(qzb_rho_fix) ) { - /* once the difference has been found it is meaningful as long as k increments */ - /* cur_qzb2 = CtPartCompare( &Lambda, pzb_rho, qzb, k-1 ); */ /* rho compare */ - if ( hz_rho == k-1 && !qzb_rho && bRhoIsDiscrete ) { - int qzb_rho_temp = 0; - qzb_rho = CtPartCompare( &Lambda, pzb_rho, qzb, kLeast_rho, k-1, 0, bSplitTautCompare ); - /* old code */ - if ( !qzb_rho && pzb_rho_fix_reached && - nOneAdditionalLayer && 0 > kLeast_rho[nOneAdditionalLayer].k ) { - qzb_rho_temp = -(nOneAdditionalLayer+1); - /* qzb_rho = -(nOneAdditionalLayer+1); *//* early rejection */ - } - /* new code */ - if ( !qzb_rho && bRhoIsDiscrete ) { - qzb_rho = CtPartCompareLayers( kLeast_rho, L_rho_fix_prev, 0 ); -#if ( FIX_ChCh_CONSTIT_CANON_BUG == 1 ) - if ( qzb_rho ) { - int L_rho_diff = abs(qzb_rho)-1; - if ( L_rho_diff < L_rho_fix_prev || - L_rho_diff == L_rho_fix_prev && kLeast_rho[L_rho_diff].i < I_rho_fix_prev ) { - qzb_rho = -(L_rho_diff+1); /* negative difference will be rejected */ - } - } -#endif - } - /* compare old results to new */ -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - if ( qzb_rho_temp && qzb_rho_temp != qzb_rho ) { - int stop = 1; /* */ - } -#endif - if ( !qzb_rho ) { - hz_rho = k; - } else - if ( qzb_rho < 0 ) { - pCC->lNumRejectedCT ++; - } - } - if ( qzb_rho > 0 || !qzb_rho && !bRhoIsDiscrete ) { - /* found better rho */ - if ( !nNumLayers ) { - CtPartCopy( pzb_rho, &Lambda, k-1 ); - } - } - } -/*L3:*/ - /*if ( hz_rho == k || (lab && qzb_rho >= 0 ) )*/ - /*if ( hz_zeta == k || hz_rho == k || (lab && qzb_rho >= 0 ) ) goto L4; else goto L6;*/ - if ( hz_zeta == k || hz_rho == k || (lab && qzb_rho >= 0 && QZFIX_OK(qzb_rho_fix) ) ) { - /*L4: check for possible isomorphism or found a better rho */ - if ( PartitionIsDiscrete( &pi[k-1], n ) ) { - pCC->lNumTotCT ++; - goto L7; - } - PartitionGetFirstCell( &pi[k-1], W, k, n ); - v[k-1] = CellGetMinNode( &pi[k-1], &W[k-1], 0, pCD1 ); - if ( !dig && PartitionSatisfiesLemma_2_25(&pi[k-1], n) ) { - ; /* found additional isomprphism */ - } else { - t_Lemma = k+1; - } - e[k-1] = 0; /* created new cell W[k-1] */ - goto L2; - } -L6: - /* a better rho or no good node was found at this level; return to smaller k */ - k2 = k; - k = inchi_min(t_Lemma-1, inchi_max(t_eq_zeta-1, hz_rho)); - if ( k2 == t_Lemma ) - goto L13; - /* store isomorphism found from Lemma 2.25. should be dig=0 !!! */ - if ( dig ) { -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - int stop = 1; -#endif - goto L13; - } - l = inchi_min(l+1, L); - PartitionGetMcrAndFixSet( &pi[t_Lemma-1], &Omega, &Phi, n_tg, l ); - goto L12; -L7: - /* from L4: pi[k-1] is discrete */ - if ( h_zeta == 0 ) { - /*goto L18;*/ /* error. the first T(nu) leaf was found */ - ret = CT_CANON_ERR; - goto exit_error; - } - if ( k != hz_zeta ) - goto L8; - /* here zeta^gamma == nu */ - /* if ( G^gamma == G ) goto L10; */ - if ( 0 == (res=CtFullCompare( &Lambda, &zf_zeta, 0, bSplitTautCompare )) ) { - PartitionGetTransposition( &zeta, &pi[k-1], n_tg, &gamma ); - bZetaIsomorph = 1; /* for testing only */ - lNumEqlZeta ++; - goto L10; - } else -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - { - int stop = 1; - } -#endif - /* !!! we should never come here !!! */ - if ( !nNumLayers ) { - ret = -2; - goto exit_error; - } - -L8: /* here nu is discrete: check rho for being a bettere leaf or isomorphism */ - /*if ( !lab || qzb_rho < 0 || !QZFIX_OK(qzb_rho_fix) )*/ - if ( !lab || qzb_rho < 0 && ( !pzb_rho_fix || qzb_rho_fix > 0 ) ) - goto L6; - if ( pzb_rho_fix && kLeast_rho_fix && 0 == qzb_rho_fix ) { - /* check for the rejection condition: Lambda > zb_rho_fix */ - if ( kLeast_rho_fix ) { - int qzb_rho_fix_alt; - qzb_rho_fix = CtFullCompareLayers( kLeast_rho_fix ); - /* for debug only */ - qzb_rho_fix_alt = CtFullCompare( &Lambda, pzb_rho_fix, 1, bSplitTautCompare ); - if ( qzb_rho_fix != qzb_rho_fix_alt ) { -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - int stop = 1; -#endif - qzb_rho_fix = qzb_rho_fix_alt; - } - /* end debug */ - } else { - qzb_rho_fix = CtFullCompare( &Lambda, pzb_rho_fix, 1, bSplitTautCompare ); - } - if ( !pzb_rho_fix_reached ) { - pzb_rho_fix_reached = !qzb_rho_fix; - } - if ( 0 < qzb_rho_fix ) { - /* Lambda > pzb_rho_fix, ignore this node */ - /* hzb_rho_fix = min( hzb_rho_fix, hz_rho ); */ /* ??? */ - qzb_rho_fix = 0; - goto L6; - } - qzb_rho_fix = 0; - } - - if ( qzb_rho < 0 ) - goto L6; - if ( qzb_rho > 0 || !bRhoIsDiscrete ) - goto L9; /* note: p67 says k > PartitionSize( &rho, n ) */ - if ( k < r ) { - goto L9; /* cannot understand it... */ - } - - /* !!! we should never come here if G(nu) != G(rho): CtPartCompare must be enough !!! */ - - /* if ( G(nu) > G(rho) ) goto L9; */ - if ( kLeast_rho ) { - int cur_qzb_alt; - qzb_rho = CtFullCompareLayers( kLeast_rho ); - /* for debug only */ - cur_qzb_alt = CtFullCompare( &Lambda, pzb_rho, 0, bSplitTautCompare ); - if ( qzb_rho != cur_qzb_alt ) { -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - int stop = 1; -#endif - qzb_rho = cur_qzb_alt; - } - /* end debug */ - } else { - qzb_rho = CtFullCompare( &Lambda, pzb_rho, 0, bSplitTautCompare ); - } - /* qzb_rho difference can be due to layers 1..MAX_LAYERS-1 only */ - if ( 0 < qzb_rho ) { - /* CtFullCompare( &Lambda, pzb_rho, 0, bSplitTautCompare ); */ - qzb_rho = 0; - goto L9; - } - /* if ( G(nu) < G(rho) ) goto L6; */ - if ( 0 > qzb_rho ) { - qzb_rho = 0; - goto L6; - } - /* nu^gamma == rho */ - if ( r != k ) { /* if() is for debug only */ - r = k; - } - PartitionGetTransposition( &pi[k-1], &rho, n_tg, &gamma ); - bZetaIsomorph = 0; /* DCh */ - pCC->lNumEqualCT ++; - goto L10; -L9: - /* rho := nu; */ - PartitionCopy( &rho, &pi[k-1], n_tg ); - if ( nNumLayers ) { - CtFullCopy( pzb_rho, &Lambda ); - } - bZetaEqRho = 0; - qzb_rho = 0; - CtCompareLayersGetFirstDiff( kLeast_rho_fix, nOneAdditionalLayer, - &L_rho_fix_prev, &I_rho_fix_prev, &k_rho_fix_prev ); - memset( kLeast_rho, 0, sizeof(kLeast_rho) ); - h_rho = hz_rho = k; - CtPartInfinity( pzb_rho, qzb, k ); - pCC->lNumDecreasedCT ++; - pCC->lNumEqualCT = 1; - bRhoIsDiscrete = 1; - goto L6; - -L10: /* discrete pi[k-1] && G^gamma == G */ - - pCC->lNumEqualCT += bZetaEqRho || !(bZetaIsomorph || qzb_rho); - l = inchi_min(l+1, L); - /* Omega[l] := mcr(gamma); - Phi[l] := fix(gamma); - */ - TranspositionGetMcrAndFixSetAndUnorderedPartition( &gamma, &Omega, &Phi, n_tg, l, &theta_from_gamma ); - - /* - if ( theta(gamma) <= theta ) goto L11; - theta := theta v theta(gamma); - UnorderedPartitionJoin() returns 0 if theta_from_gamma is finer than theta, - which means no changes in theta: theta_from_gamma ^ theta == theta. - */ - if ( !UnorderedPartitionJoin( &theta_from_gamma, &theta, n_tg ) ) - goto L11; /* no new isomorphism found */ - /* Output gamma (it is the Aut(G) generator) -- omitted -- */ - nNumFoundGenerators ++; - /* if ( tvc in mcr(theta) ) goto L11; */ - if ( tvc == GetUnorderedPartitionMcrNode( &theta, tvc ) ) - goto L11; - k = h_zeta; - goto L13; -L11: - k = lab? h_rho : h_zeta; /***Changed*** originally was k = h_rho; */ -L12: - /* if ( e[k-1] == 1 ) */ -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - if ( e[k-1] == 1 && v[k-1] == INFINITY ) { - int stop = 1; /* testing only */ - } -#endif - if ( e[k-1] == 1 && v[k-1] != INFINITY ) { /* INFINITY for testing only */ - CellIntersectWithSet( &pi[k-1], &W[k-1], &Omega, l ); - } -L13: - - if ( UserAction && USER_ACTION_QUIT == (*UserAction)() || - ConsoleQuit && (*ConsoleQuit)() ) { - ret = CT_USER_QUIT_ERR; - goto exit_error; - } - if ( bInchiTimeIsOver(pCD->ulTimeOutTime) ) { - ret = CT_TIMEOUT_ERR; - goto exit_error; - } - - if ( k == 0 ) - goto exit_function; /* stop */ - - if ( lab && k < h_rho ) { /***Added***/ - h_rho = k; - } - if ( k > h_zeta ) { - if ( v[k-1] == INFINITY ) {/*** Added by DCh for testing only ****/ - k --; - goto L13; - } - goto L17; - } - if ( k == h_zeta ) - goto L14; - h_zeta = k; - tvc = tvh = CellGetMinNode( &pi[k-1], &W[k-1], 0, pCD1 ); -L14: - /* if v[k] and tvh are in the same cell of theta then index ++ */ - if ( GetUnorderedPartitionMcrNode( &theta, v[k-1] ) == - GetUnorderedPartitionMcrNode( &theta, tvh ) ) { - index ++; - } - v[k-1] = CellGetMinNode( &pi[k-1], &W[k-1], v[k-1], pCD1 ); - - if ( v[k-1] == INFINITY ) - goto L16; - if ( v[k-1] != GetUnorderedPartitionMcrNode( &theta, v[k-1] ) ) - goto L14; -L15: - t_Lemma = inchi_min(t_Lemma, k+1); - hz_zeta = inchi_min(hz_zeta, k); -/* - if ( lab && hz_rho >= k ) { - hz_rho = k; - qzb_rho = 0; - } -*/ - if ( lab ) { - if ( hz_rho >= k /*-1*/ ) - qzb_rho = 0; - if ( hz_rho > k ) - hz_rho = k; - UpdateCompareLayers( kLeast_rho, hz_rho ); - } - if ( pzb_rho_fix ) { - if ( hzb_rho_fix >= k /*-1*/ ) - qzb_rho_fix = 0; - if ( hzb_rho_fix > k ) - hzb_rho_fix = k; - UpdateCompareLayers( kLeast_rho_fix, hzb_rho_fix ); - } - - goto L2; -L16: - if ( t_eq_zeta == k+1 && index == CellGetNumberOfNodes( &pi[k-1], &W[k-1] ) ) - t_eq_zeta = k; - size *= (double)index; - /******************** <<<===== A **************************/ - /* passed K times after passing point A. At these passes - k = K, K-1, ..., 1 in this order - */ - index = 0; - k --; - goto L13; -L17: - /* if ( e[k-1] == 0 ) */ -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - if ( e[k-1] == 0 && v[k-1] == INFINITY ) { /* testing only */ - int stop = 1; /* */ - } -#endif - /* - if ( e[k] == 0 set W[k] = Intersection(W[k], Omega[i]) for each i = 1..l, - such that {v[1]..v[k-1]} in Phi[i] - */ - if ( e[k-1] == 0 && v[k-1] != INFINITY ) /* Added v[k-1]!=... DCh */ - { - NodeSetFromVertices( &cur_nodes, 1, v, k-1 ); - for ( i = 1; i <= l; i ++ ) { - if ( AllNodesAreInSet( &cur_nodes, 1, &Phi, i ) ) { - CellIntersectWithSet( &pi[k-1], &W[k-1], &Omega, i ); - } - } - } - e[k-1] = 1; - v[k-1] = CellGetMinNode( &pi[k-1], &W[k-1], v[k-1], pCD1 ); - if ( v[k-1] != INFINITY ) - goto L15; - k --; - goto L13; -/* L18: see above */ - -exit_function: - /* CtPartFill( G, pCD, &rho, pzb_rho, 1, n, n_tg ); */ - if ( !bRhoIsDiscrete ) { - ret = CT_CANON_ERR; - goto exit_error; - } - if ( pzb_rho_fix ) { - qzb_rho_fix = CtFullCompare( pzb_rho_fix, pzb_rho, 1, bSplitTautCompare ); - if ( qzb_rho_fix ) { - ret = CT_CANON_ERR; - goto exit_error; - } - } - /* SymmRank */ - memset( nSymmRank, 0, n_tg * sizeof(nSymmRank[0]) ); - for ( i = 0; i < n_tg; i ++ ) { - k = rho.AtNumber[i]; - k2 = (int)GetUnorderedPartitionMcrNode( &theta, (AT_NUMB)(k+1) ) - 1; - if ( !nSymmRank[k2] || nSymmRank[k2] > rho.Rank[k] ) { - nSymmRank[k2] = rho.Rank[k]; - } - } - for ( i = 0; i < n_tg; i ++ ) { - k = rho.AtNumber[i]; - k2 = (int)GetUnorderedPartitionMcrNode( &theta, (AT_NUMB)(k+1) ) - 1; - nSymmRank[k] = nSymmRank[k2]; - } - /* CanonRank, nAtomNumberCanon */ - memcpy( nCanonRank, rho.Rank, n_tg * sizeof(nCanonRank[0]) ); - memcpy( nAtomNumberCanon, rho.AtNumber, n_tg * sizeof(nAtomNumberCanon[0]) ); - /* LinearCT */ - *nLenCt = pzb_rho->lenCt-1; - if ( pCt ) { - memcpy( pCt, pzb_rho->Ctbl, *nLenCt*sizeof(pCt[0]) ); - } - pCC->lNumTotCT = pCC->lNumDecreasedCT + pCC->lNumRejectedCT + pCC->lNumEqualCT; - pCC->dGroupSize = size; - pCC->lNumGenerators = nNumFoundGenerators; - pCC->lNumStoredIsomorphisms = l; - /* Note: check nNumFoundGenerators */ - - if ( pp_zb_rho_out && !*pp_zb_rho_out ) { - *pp_zb_rho_out = pzb_rho; - pzb_rho = NULL; - } - -exit_error: - INCHI_HEAPCHK - - UnorderedPartitionFree( &theta ); - UnorderedPartitionFree( &theta_from_gamma ); - if ( W ) inchi_free( W ); - if ( v ) inchi_free( v ); - if ( e ) inchi_free( e ); - if ( qzb ) inchi_free( qzb ); - CTableFree( &Lambda ); - CTableFree( &zf_zeta ); - if ( pzb_rho ) { - CTableFree( pzb_rho ); - inchi_free( pzb_rho ); - pzb_rho = NULL; - } - -/* CTableFree( &zf_zeta2 ); */ - - - NodeSetFree( &Omega ); - NodeSetFree( &Phi ); - /* NodeSetFree( &mcr_theta, n, 1 ); */ - NodeSetFree( &cur_nodes ); - - PartitionFree( &zeta ); -/* PartitionFree( &zeta2 ); */ - PartitionFree( &rho ); - TranspositionFree( &gamma ); - - - return ret; -} -/********************************************************************************************** - * SetInitialRanks2: Set initial ranks in nRank according to pAtomInvariant[] values - * Make sure enough prines have been generated. - **********************************************************************************************/ -/* Upon exit: */ -/* nAtomNumber[i]: number (from 0) of an atom in the ith (from 0) position of the sorted order */ -/* nNewRank[i]: initial rank of the atom[i] based on atom invariants; from 1 to num_atoms */ -/* Return value: Number of different ranks */ -int SetInitialRanks2( int num_atoms, ATOM_INVARIANT2* pAtomInvariant2, AT_RANK *nNewRank, AT_RANK *nAtomNumber ) -{ - int i, nNumDiffRanks; - AT_RANK nCurrentRank; - - for ( i = 0; i < num_atoms; i++ ) - nAtomNumber[i] = (AT_RANK)i; - - /* global for qsort */ - pAtomInvariant2ForSort = pAtomInvariant2; - - qsort( nAtomNumber, num_atoms, sizeof(nAtomNumber[0]), CompAtomInvariants2 ); - - /* nNewRank[i]: non-decreading order; do not increment nCurrentRank */ - /* if consecutive sorted atom invariants are identical */ - - for ( i=num_atoms-1, nCurrentRank=nNewRank[nAtomNumber[i]] = (AT_RANK)num_atoms, nNumDiffRanks = 1; 0 < i ; i -- ) { - /* Note: CompAtomInvariants2Only() in following line implicitly reads pAtomInvariant2 pointed by pAtomInvariant2ForSort */ - if ( CompAtomInvariants2Only( &nAtomNumber[i-1], &nAtomNumber[i] ) ) { - nNumDiffRanks ++; - nCurrentRank = (AT_RANK)i; - } - nNewRank[nAtomNumber[i - 1]] = nCurrentRank; - } - - - return nNumDiffRanks; -} - -/****************************************************************************/ -void FillOutAtomInvariant2( sp_ATOM* at, int num_atoms, int num_at_tg, ATOM_INVARIANT2* pAtomInvariant, - int bIgnoreIsotopic, int bHydrogensInRanks, int bHydrogensFixedInRanks, - int bDigraph, int bTautGroupsOnly, T_GROUP_INFO *t_group_info ) -{ - int i, k, j, i_t_group; - /* tautomers */ - T_GROUP *t_group=NULL; - int num_t_groups = 0; - int num_tautomer_iso = 0; -#define ELEM_NAME_LEN 2 - char ChemElements[ELEM_NAME_LEN*NUM_CHEM_ELEMENTS+ELEM_NAME_LEN]; - char CurElement[ELEM_NAME_LEN + ELEM_NAME_LEN], *pCurElem; - int nNumChemElements = 0; - int nNumHydrogenAtoms = 0; - int nNumCarbonAtoms = 0; - memset( ChemElements, 0, sizeof(ChemElements) ); - memset( CurElement, 0, sizeof(CurElement) ); - nNumChemElements = 0; - - if ( num_at_tg > num_atoms && t_group_info ) { - t_group = t_group_info->t_group; - num_t_groups = t_group_info->num_t_groups; - num_tautomer_iso = t_group_info->bIgnoreIsotopic? 0 : T_NUM_ISOTOPIC; - } - - if ( !bTautGroupsOnly ) { - - for ( i = 0; i < num_atoms; i ++ ) { - if ( !strcmp( at[i].elname, "C" ) ) { - nNumCarbonAtoms ++; - } else - if ( !strcmp( at[i].elname, "H" ) || - !strcmp( at[i].elname, "D" ) || - !strcmp( at[i].elname, "T" ) ) { - nNumHydrogenAtoms ++; - } else { - CurElement[0] = at[i].elname[0]; - CurElement[1] = at[i].elname[1]? at[i].elname[1] : ' '; - if ( ! (pCurElem = strstr( ChemElements, CurElement ) ) ) { - strcat( ChemElements, CurElement ); - nNumChemElements ++; - } - } - } - if ( nNumChemElements > 1 ) { - qsort( ChemElements, nNumChemElements, ELEM_NAME_LEN, CompChemElemLex ); - } - if ( nNumCarbonAtoms ) { - if ( nNumChemElements ) { - memmove( ChemElements + ELEM_NAME_LEN, ChemElements, ELEM_NAME_LEN*nNumChemElements ); - } - ChemElements[0] = 'C'; - ChemElements[1] = ' '; - nNumChemElements ++; - } - if ( nNumHydrogenAtoms ) { - ChemElements[ ELEM_NAME_LEN*nNumChemElements ] = 'H'; - ChemElements[ ELEM_NAME_LEN*nNumChemElements+1 ] = ' '; - nNumChemElements ++; - } - - - /* general */ - for ( i = 0; i < num_atoms; i ++ ) { - memset( &pAtomInvariant[i], 0, sizeof(pAtomInvariant[0]) ); - CurElement[0] = at[i].elname[0]; - CurElement[1] = at[i].elname[1]? at[i].elname[1] : ' '; - pCurElem = strstr( ChemElements, CurElement ); - if ( pCurElem ) { - j = (pCurElem - ChemElements)/ELEM_NAME_LEN + 1; - } else { - j = nNumChemElements; /* must be D or T */ - } - /* at[i].hill_type = (U_CHAR) j; */ - pAtomInvariant[i].val[AT_INV_HILL_ORDER] = j; - - pAtomInvariant[i].val[AT_INV_NUM_CONNECTIONS] = at[i].valence; - if ( bHydrogensInRanks ) { - pAtomInvariant[i].val[AT_INV_NUM_H] = ((t_group && at[i].endpoint>0)? 0 : at[i].num_H); - } - if ( bHydrogensFixedInRanks ) { - pAtomInvariant[i].val[AT_INV_NUM_H_FIX] = ((t_group && at[i].endpoint>0)? at[i].num_H : 0); - } - if ( !bDigraph && t_group && (i_t_group = (int)at[i].endpoint-1) >= 0 && i_t_group < num_t_groups ) { - pAtomInvariant[i].val[AT_INV_NUM_TG_ENDPOINTS] = t_group[i_t_group].nNumEndpoints; - for ( j = 0; j < T_NUM_NO_ISOTOPIC; j ++ ) { - pAtomInvariant[i].val[AT_INV_TG_NUMBERS+j] = t_group[i_t_group].num[j]; - } - for ( j = 0; j < num_tautomer_iso; j ++ ) { - pAtomInvariant[i].val[AT_INV_TAUT_ISO+j] = t_group[i_t_group].num[j + T_NUM_NO_ISOTOPIC]; - } - } - pAtomInvariant[i].iso_sort_key = bIgnoreIsotopic? 0 : at[i].iso_sort_key; - } - } else { - /* fill tautomeric groups only */ - memset ( pAtomInvariant, 0, num_at_tg*sizeof(pAtomInvariant[0]) ); - } - /**************************************/ - /* tautomeric groups */ - /**************************************/ - for ( i = num_atoms; i < num_at_tg; i ++ ) { - - k = i - num_atoms; - memset( &pAtomInvariant[i], 0, sizeof(pAtomInvariant[0]) ); - if ( !t_group ) - continue; - /* make sure ranks of t-groups are larger than that of any atom */ - /* greater than for any real atom */ - pAtomInvariant[i].val[AT_INV_HILL_ORDER] = bTautGroupsOnly? num_at_tg : nNumChemElements+1; - /* greater than for any real atom */ - pAtomInvariant[i].val[AT_INV_NUM_CONNECTIONS] = MAXVAL+1; - if ( k < num_t_groups ) { - pAtomInvariant[i].val[AT_INV_NUM_TG_ENDPOINTS] = t_group[k].nNumEndpoints; - for ( j = 0; j < T_NUM_NO_ISOTOPIC; j ++ ) { - pAtomInvariant[i].val[AT_INV_TAUT_ISO+j] = t_group[k].num[j]; - } - for ( j = 0; j < num_tautomer_iso; j ++ ) { - pAtomInvariant[i].val[AT_INV_TAUT_ISO+j] = t_group[k].num[j + T_NUM_NO_ISOTOPIC]; - } - } - } -} -/*****************************************************************************/ -void CleanNumH( NUM_H *NumH, int len ) -{ - int i; - if ( NumH ) { - for ( i = 0; i < len; i ++ ) { - if ( NumH[i] == EMPTY_H_NUMBER ) { - NumH[i] = 0; - } else { - NumH[i] -= BASE_H_NUMBER; - } - } - } -} -/*****************************************************************************/ -int CleanCt( AT_RANK *Ct, int len ) -{ - if ( Ct && Ct[len] == EMPTY_CT ) { - Ct[len] = 0; - return 1; - } - return 0; -} -/*****************************************************************************/ -void CleanIsoSortKeys( AT_ISO_SORT_KEY * isk, int len ) -{ - int i; - if ( isk ) { - for ( i = 0; i < len; i ++ ) { - if ( isk[i] == EMPTY_ISO_SORT_KEY ) { - isk[i] = 0; - } - } - } -} -/*****************************************************************************/ -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) -void MergeCleanIsoSortKeys( AT_ISO_SORT_KEY * isk1, AT_ISO_SORT_KEY * isk2, int len ) -{ - int i; - AT_ISO_SORT_KEY k1, k2; - if ( isk1 && isk2 ) { - for ( i = 0; i < len; i ++ ) { - k1 = (isk1[i] == EMPTY_ISO_SORT_KEY)? 0 : isk1[i]; - k2 = (isk2[i] == EMPTY_ISO_SORT_KEY)? 0 : isk2[i]; - isk1[i] = k1 | k2; - } - } else - if ( isk1 ) { - CleanIsoSortKeys( isk1, len ); - } -} -#endif -#define FREE_CONTABLE(X) if (X) {CTableFree(X);inchi_free(X);} -#define FREE_ARRAY(X) if (X) inchi_free(X); -/*****************************************************************************/ -void DeAllocBCN( BCN *pBCN ) -{ - int i, k; - FTCN *ftcn; - if ( !pBCN ) - return; - if ( pBCN->pRankStack ) { - for ( i = 0; i < pBCN->nMaxLenRankStack; i ++ ) { - FREE_ARRAY( pBCN->pRankStack[i] ) - } - FREE_ARRAY( pBCN->pRankStack ) - } - for ( k = 0; k < TAUT_NUM; k ++ ) { - ftcn = pBCN->ftcn + k; - FreeNeighList( ftcn->NeighList ); - FREE_ARRAY( ftcn->LinearCt ) - PartitionFree( &ftcn->PartitionCt ); - FREE_ARRAY( ftcn->nSymmRankCt ) - FREE_ARRAY( ftcn->nNumHOrig ) - FREE_ARRAY( ftcn->nNumH ) - FREE_ARRAY( ftcn->nNumHOrigFixH ) - FREE_ARRAY( ftcn->nNumHFixH ) - PartitionFree( &ftcn->PartitionCtIso ); - FREE_ARRAY( ftcn->nSymmRankCtIso ) - FREE_ARRAY( ftcn->iso_sort_keys ) - FREE_ARRAY( ftcn->iso_sort_keysOrig ) - FREE_ARRAY( ftcn->iso_exchg_atnos ) - FREE_ARRAY( ftcn->iso_exchg_atnosOrig ) - } -} -#undef FREE_CONTABLE -#undef FREE_ARRAY - -/*****************************************************************************/ -#if ( bRELEASE_VERSION == 0 && FIND_CANON_NE_EQUITABLE == 1 ) -/* debug: find whether canonical equivalence is different from equitable partition */ -int bCanonIsFinerThanEquitablePartition( int num_atoms, sp_ATOM* at, AT_RANK *nSymmRank ) -{ - AT_RANK *nRank = NULL; - AT_RANK *nAtomNumber = NULL; - AT_RANK *nTempRank = NULL; - AT_RANK nCurSymm, nCurRank; - ATOM_INVARIANT2 *pAtomInvariant = NULL; - NEIGH_LIST *NeighList = NULL; - int nNumCurrRanks, i, is, ir, j; - long lCount; - int bIsNotSame = 0; - if ( at && nSymmRank ) { - if ( !(nRank = (AT_RANK*)inchi_calloc( num_atoms, sizeof(nRank[0]))) || - !(nAtomNumber = (AT_RANK*)inchi_calloc( num_atoms, sizeof(nAtomNumber[0]))) || - !(nTempRank = (AT_RANK*)inchi_calloc( num_atoms, sizeof(nTempRank[0]))) || - !(pAtomInvariant = (ATOM_INVARIANT2 *)inchi_calloc( num_atoms, sizeof(pAtomInvariant[0]))) - ) { - goto exit_err; - } - if ( !(NeighList = CreateNeighList( num_atoms, num_atoms, at, 0, NULL )) ) { - goto exit_err; - } - - FillOutAtomInvariant2( at, num_atoms, num_atoms, pAtomInvariant, 1 /*bIgnoreIsotopic*/, - 1 /*bHydrogensInRanks*/, 1 /*bHydrogensFixedInRanks*/, 0 /*bTaut=bDigraph*/, - 0 /* bTautGroupsOnly */, NULL /*t_group_info*/ ); - /* initial partitioning of a hydrogenless skeleton: create equitable partition (assign initial ranks) */ - nNumCurrRanks = SetInitialRanks2( num_atoms, pAtomInvariant, nRank, nAtomNumber ); - - lCount = 0; - /* make equitable partition in pBCN->pRankStack[0,1] */ - nNumCurrRanks = DifferentiateRanks2( num_atoms, NeighList, - nNumCurrRanks, nRank, - nTempRank, nAtomNumber, &lCount, 0 /* 0 means use qsort */ ); - /* at this point the equitable partition is in nRank; the order of atoms is in nAtomNumber*/ - /* compare */ - nCurSymm = nCurRank = 0; - for ( i = 0; i < num_atoms; i ++ ) { - j = (int)nAtomNumber[i]; - if ( nCurSymm != nSymmRank[j] ) { - nCurSymm = nSymmRank[j]; - is = i; - } - if ( nCurRank != nRank[j] ) { - nCurRank = nRank[j]; - ir = i; - } - if ( is != ir ) { - bIsNotSame = 1; - break; - } - } - } -exit_err: - if ( nRank ) - inchi_free( nRank ); - if ( nAtomNumber ) - inchi_free( nAtomNumber ); - if ( nTempRank ) - inchi_free( nTempRank ); - if ( pAtomInvariant ) - inchi_free( pAtomInvariant ); - if ( NeighList ) - FreeNeighList( NeighList ); - return bIsNotSame; -} -#endif -/*****************************************************************************/ -int GetBaseCanonRanking( int num_atoms, int num_at_tg, sp_ATOM* at[], - T_GROUP_INFO *t_group_info, ATOM_SIZES s[], BCN *pBCN, - struct tagInchiTime *ulTimeOutTime, int bFixIsoFixedH ) -{ - int ret = 0; - int iBase; /* base structure index, always valid; = TAUT_YES except special fully non-taut mode */ - int iOther; /* other than basic structure index, usually non-taut; may be = iBase */ - int bReqNonTaut; /* 1 => requested non-tautomeric results */ - int bReqTaut; /* 1 => requested tautomeric results and the base structure is tautomeric */ - int bChanged; - sp_ATOM *at_base = NULL; - sp_ATOM *at_other = NULL; - int bTautIgnoreIsotopic = 0; - /*int bIgnoreIsotopic = 0;*/ - int nNumCurrRanks = 0; - int nMaxLenRankStack = 0; - int num_max = num_at_tg; - long lCount; - /* local allocations */ - ATOM_INVARIANT2 *pAtomInvariant = NULL; - NEIGH_LIST *NeighList[TAUT_NUM]; - ConTable *Ct_Temp = NULL; - - /* initial partition for canonicalization */ - AT_RANK *nRank = NULL; - AT_NUMB *nAtomNumber = NULL; - - /* canonicalization output */ - - ConTable *Ct_NoH = NULL; - AT_RANK *nCanonRankNoH = NULL; - AT_NUMB *nAtomNumberCanonNoH = NULL; - AT_RANK *nSymmRankNoH = NULL; - - ConTable *Ct_NoTautH = NULL; - AT_RANK *nSymmRankNoTautH = NULL; - AT_RANK *nCanonRankNoTautH = NULL; - AT_NUMB *nAtomNumberCanonNoTautH = NULL; - NUM_H *numHNoTautH = NULL; - int lenNumHNoTautH; - int maxlenNumHNoTautH; - - ConTable *Ct_Base = NULL; - AT_RANK *nSymmRankBase = NULL; - AT_RANK *nCanonRankBase = NULL; - AT_NUMB *nAtomNumberCanonBase = NULL; - NUM_H *numH = NULL; - int lenNumH; - int maxlenNumH = 0; - -#if ( USE_AUX_RANKING == 1 ) - AT_RANK *nRankAux = NULL; - AT_NUMB *nAtomNumberAux = NULL; - ATOM_INVARIANT2 *pAtomInvariantAux= NULL; -#endif - - - ConTable *Ct_FixH = NULL; - AT_RANK *nSymmRankFixH = NULL; - AT_RANK *nCanonRankFixH = NULL; - AT_NUMB *nAtomNumberCanonFixH = NULL; - NUM_H *NumHfixed = NULL; - int maxlenNumHfixed; - - /* isotopic canonicalization */ - - ConTable *Ct_NoTautHIso = NULL; - AT_RANK *nSymmRankNoTautHIso = NULL; - AT_RANK *nCanonRankNoTautHIso = NULL; - AT_NUMB *nAtomNumberCanonNoTautHIso = NULL; - AT_ISO_SORT_KEY *iso_sort_key_NoTautH = NULL; - int maxlen_iso_sort_key_NoTautH; - int len_iso_sort_key_NoTautH; - int num_iso_NoTautH, num_iso_NoAuxBase; - - ConTable *Ct_BaseIso = NULL; - AT_RANK *nSymmRankBaseIso = NULL; - AT_RANK *nCanonRankBaseIso = NULL; - AT_NUMB *nAtomNumberCanonBaseIso = NULL; - - AT_ISO_SORT_KEY *iso_sort_keyBase = NULL; - int maxlen_iso_sort_keyBase; - int len_iso_sort_keyBase; - - int bUseIsoAuxBase[TAUT_NUM]; - S_CHAR *iso_exchg_atnos = NULL; - int len_iso_exchg_atnos; - int maxlen_iso_exchg_atnos; - int num_iso_Base; - - AT_ISO_SORT_KEY iso_sort_key; - - ConTable *Ct_FixHIso = NULL; - AT_RANK *nSymmRankFixHIso = NULL; - AT_RANK *nCanonRankFixHIso = NULL; - AT_NUMB *nAtomNumberCanonFixHIso = NULL; - -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - AT_ISO_SORT_KEY iso_sort_key2; - AT_ISO_SORT_KEY *iso_sort_key_Hfixed = NULL; - int maxlen_iso_sort_key_Hfixed; - int len_iso_sort_key_Hfixed; - int num_iso_Hfixed; -#endif - - AT_RANK *nTempRank = NULL; - - CANON_DATA pCD[3]; /* = &CanonData; */ - CANON_COUNTS CanonCounts; - CANON_COUNTS *pCC = &CanonCounts; - - int i, j, k, m; - int nCanonFlags[2]; - - /*^^^ */ - int iflag; - - memset (pCD, 0, sizeof(pCD)); - memset (pCC, 0, sizeof(pCC[0])); - memset ( bUseIsoAuxBase, 0, sizeof(bUseIsoAuxBase) ); - memset ( nCanonFlags, 0, sizeof(nCanonFlags) ); - NeighList[TAUT_NON] = NULL; - NeighList[TAUT_YES] = NULL; - - /* select base structure, find whether it is tautomeric or not */ - if ( at[TAUT_YES] && s[TAUT_YES].nLenCT && - t_group_info && (s[TAUT_YES].nLenLinearCTTautomer > 0 && /* ordinary tautomerism */ - t_group_info->t_group && t_group_info->num_t_groups > 0 || - /* protons have been moved */ - (t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) || - /* tautomerism due to possible isotopic proton exchange */ - t_group_info->nNumIsotopicEndpoints > 1 && - (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE|TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE)) ) ) { - /* tautomeric: (1) has tautomeric atoms OR - (2) H-atoms have been rearranged due to proton addition/removal OR - (3) Found isotopic H-atoms on tautomeric or hetero atoms - */ - iBase = TAUT_YES; - bReqTaut = 1; - bUseIsoAuxBase[iBase] = (s[iBase].nLenIsotopicEndpoints > 1) && - (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE|TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE)); - if ( at[TAUT_NON] && s[TAUT_NON].nLenCT ) { - iOther = TAUT_NON; /* tautomeric and non-tautomeric */ - bReqNonTaut = 1; - } else { - iOther = iBase; /* tautomeric only */ - bReqNonTaut = 0; - } - } else - if ( at[TAUT_NON] && s[TAUT_NON].nLenCT ) { - /* force pure non-tautomeric processing; happens for testing only */ - iBase = TAUT_NON; - bReqTaut = 0; - iOther = iBase; - bReqNonTaut = 1; - num_at_tg = num_atoms; - } else - if ( at[TAUT_YES] && s[TAUT_YES].nLenCT ) { - /* although the user requested tautomeric processing, tautomerism has not been found */ - /* however, the results should be saved in the TAUT_YES elements of the arrays */ - iBase = TAUT_YES; - bReqTaut = 0; - bUseIsoAuxBase[iBase] = (s[iBase].nLenIsotopicEndpoints > 1); - iOther = iBase; - bReqNonTaut = 1; - num_at_tg = num_atoms; - } else { - ret = CT_UNKNOWN_ERR; - goto exit_error; - } - if ( bReqTaut ) { - /* save "process isotopic" mark; temporarily set it to NO */ - bTautIgnoreIsotopic = t_group_info->bIgnoreIsotopic; - t_group_info->bIgnoreIsotopic = 1; - } - lenNumH = num_atoms; - - /* isotopic canonicalization */ - num_iso_NoTautH = 0; - len_iso_sort_key_NoTautH = 0; - maxlen_iso_sort_key_NoTautH = 0; - num_iso_Base = 0; - len_iso_sort_keyBase = 0; - maxlen_iso_sort_keyBase = 0; - len_iso_exchg_atnos = 0; - maxlen_iso_exchg_atnos = 0; - len_iso_exchg_atnos = 0; - maxlen_iso_exchg_atnos = 0; - -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - num_iso_Hfixed = - len_iso_sort_key_Hfixed = - maxlen_iso_sort_key_Hfixed = 0; -#endif - - /* prepare initial data */ - at_base = at[iBase]; - at_other = at[iOther]; - pAtomInvariant = (ATOM_INVARIANT2 *)inchi_calloc( num_max, sizeof(pAtomInvariant[0]) ); - nSymmRankNoH = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankNoH[0] ) ); - nCanonRankNoH = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankNoH[0] ) ); - nAtomNumberCanonNoH = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonNoH[0]) ); - nRank = (AT_RANK *) inchi_calloc( num_max, sizeof(nRank[0] ) ); - nAtomNumber = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumber[0]) ); - nTempRank = (AT_RANK *) inchi_calloc( num_max, sizeof(nTempRank[0] ) ); - - if ( !pAtomInvariant || - !nSymmRankNoH || !nCanonRankNoH || !nAtomNumberCanonNoH || - !nRank || !nAtomNumber || !nTempRank ) { - goto exit_error_alloc; - } -#if ( USE_AUX_RANKING == 1 ) - nRankAux = (AT_RANK *) inchi_calloc( num_max, sizeof(nRankAux[0] ) ); - nAtomNumberAux = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberAux[0] ) ); - pAtomInvariantAux = (ATOM_INVARIANT2 *) inchi_malloc( num_max * sizeof(pAtomInvariantAux[0]) ); - if ( !nRankAux || !nAtomNumberAux || !pAtomInvariantAux ) { - goto exit_error_alloc; - } -#endif - - if ( bReqTaut ) { - if ( !(NeighList[TAUT_YES] = CreateNeighList( num_atoms, num_at_tg, at_base, 0, t_group_info )) ) - goto exit_error_alloc; - /* needed for the hydrogenless structure */ - if ( !(NeighList[TAUT_NON] = CreateNeighList( num_atoms, num_atoms, at_base, 0, NULL )) ) - goto exit_error_alloc; - } else { - if ( !(NeighList[TAUT_NON] = CreateNeighList( num_atoms, num_atoms, at_base, 0, NULL )) ) - goto exit_error_alloc; - NeighList[TAUT_YES] = NULL; - INCHI_HEAPCHK - } - - /* avoid memory leaks in case of error */ - /* - pBCN->ftcn[TAUT_NON].NeighList = NeighList[TAUT_NON]; - pBCN->ftcn[TAUT_YES].NeighList = NeighList[TAUT_YES]; - */ - pBCN->nMaxLenRankStack = 0; - pBCN->num_max = num_max; /* allocated nRank[] arrays lengths in pRankStack */ - pBCN->num_at_tg = num_at_tg; /* all of the following arrays have this length */ - pBCN->num_atoms = num_atoms; - pBCN->ulTimeOutTime = ulTimeOutTime; - - /* initial partitioning of a hydrogenless skeleton: fill out the inveriant */ - FillOutAtomInvariant2( at_base, num_atoms, num_atoms, pAtomInvariant, 1 /*bIgnoreIsotopic*/, - 0 /*bHydrogensInRanks*/, 0 /*bHydrogensFixedInRanks*/, 0 /*bTaut=bDigraph*/, - 0 /* bTautGroupsOnly */, NULL /*t_group_info*/ ); - /* initial partitioning of a hydrogenless skeleton: create equitable partition (assign initial ranks) */ - nNumCurrRanks = SetInitialRanks2( num_atoms, pAtomInvariant, nRank, nAtomNumber ); - - lCount = 0; - /* make equitable partition in pBCN->pRankStack[0,1] */ - nNumCurrRanks = DifferentiateRanks2( num_atoms, NeighList[TAUT_NON], - nNumCurrRanks, nRank, - nTempRank, nAtomNumber, &lCount, 0 /* 0 means use qsort */ ); - - /* allocate partition stack */ - nMaxLenRankStack = 2*(num_at_tg-nNumCurrRanks) + 8; /* was 2*(...) + 6 */ - pBCN->pRankStack = (AT_RANK **) inchi_calloc( nMaxLenRankStack, sizeof(pBCN->pRankStack[0]) ); - if ( !pBCN->pRankStack ) { - pBCN->nMaxLenRankStack = 0; /* avoid memory leaks in case of error */ - goto exit_error_alloc; - } - pBCN->nMaxLenRankStack = nMaxLenRankStack; /* avoid memory leaks in case of error */ - /* init partition stack */ - pBCN->pRankStack[0] = nRank; - pBCN->pRankStack[1] = nAtomNumber; - - /********************************************************************************************/ - /* get NoH/no taut groups canonical numbering, connection table, and equivalence partition */ - /********************************************************************************************/ - - /* pointers */ - pCD[iOther].LinearCT = NULL; - pCD[iOther].NumH = NULL; - pCD[iOther].NumHfixed = NULL; - pCD[iOther].iso_sort_key = NULL; - pCD[iOther].iso_exchg_atnos = NULL; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iOther].iso_sort_key_Hfixed = NULL; -#endif - /* variables - unchanged */ - pCD[iOther].ulTimeOutTime = pBCN->ulTimeOutTime; - pCD[iOther].nMaxLenLinearCT = s[iOther].nLenCTAtOnly + 1; - /* return values & input/output */ - pCD[iOther].nLenLinearCT = s[iOther].nLenCTAtOnly; - pCD[iOther].nLenCTAtOnly = s[iOther].nLenCTAtOnly; - pCD[iOther].lenNumH = 0; - pCD[iOther].lenNumHfixed = 0; - pCD[iOther].len_iso_sort_key = 0; - pCD[iOther].maxlen_iso_sort_key = 0; - pCD[iOther].len_iso_exchg_atnos = 0; - pCD[iOther].maxlen_iso_exchg_atnos = 0; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iOther].len_iso_sort_key_Hfixed = 0; - pCD[iOther].maxlen_iso_sort_key_Hfixed = 0; -#endif - ret = CanonGraph01( num_atoms, num_atoms, num_max, 0, NeighList[TAUT_NON], (Partition *)pBCN->pRankStack, - nSymmRankNoH, nCanonRankNoH, nAtomNumberCanonNoH, pCD+iOther, pCC, NULL, &Ct_NoH ); - if ( ret < 0 ) { - goto exit_error; - } - /* update initial partitioning */ - nNumCurrRanks = FixCanonEquivalenceInfo( num_atoms, nSymmRankNoH, nRank, nTempRank, nAtomNumber, &bChanged ); - /* repartition if necessary */ - if ( bChanged & 3 ) { - if ( Ct_NoH ) { - CTableFree( Ct_NoH ); - inchi_free( Ct_NoH ); - Ct_NoH = NULL; - } - pCD[iOther].nCanonFlags |= CANON_FLAG_NO_H_RECANON; - - ret = CanonGraph02( num_atoms, num_atoms, num_max, 0, NeighList[TAUT_NON], (Partition *)pBCN->pRankStack, - nSymmRankNoH, nCanonRankNoH, nAtomNumberCanonNoH, pCD+iOther, pCC, NULL, &Ct_NoH ); - if ( ret < 0 ) { - goto exit_error; - } - } - /********************************************************************************/ - /* get NoTautH canonical numbering, connection table, and equivalence partition */ - /********************************************************************************/ - maxlenNumHNoTautH = num_atoms + 1; - nSymmRankNoTautH = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankNoTautH[0] ) ); - nCanonRankNoTautH = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankNoTautH[0] ) ); - nAtomNumberCanonNoTautH = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonNoTautH[0]) ); - numHNoTautH = (NUM_H *) inchi_calloc( maxlenNumHNoTautH, sizeof(numHNoTautH[0]) ); - if ( !numHNoTautH || !nSymmRankNoTautH || !nCanonRankNoTautH || !nAtomNumberCanonNoTautH ) { - goto exit_error_alloc; - } - /* find number of H atoms attached to not-a-tautomeric-endpoint atoms */ - for ( i = 0; i < num_atoms; i ++ ) { - numHNoTautH[i] = (!at_base[i].endpoint && at_base[i].num_H)? at_base[i].num_H+BASE_H_NUMBER : EMPTY_H_NUMBER; - } - /* pointers */ - pCD[iOther].LinearCT = NULL; - pCD[iOther].NumH = numHNoTautH; - pCD[iOther].NumHfixed = NULL; - pCD[iOther].iso_sort_key = NULL; - pCD[iOther].iso_exchg_atnos = NULL; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iOther].iso_sort_key_Hfixed = NULL; -#endif - /* variables - unchanged */ - pCD[iOther].ulTimeOutTime = pBCN->ulTimeOutTime; - pCD[iOther].nMaxLenLinearCT = s[iOther].nLenCTAtOnly + 1; - pCD[iOther].maxlenNumH = maxlenNumHNoTautH; - /* return values & input/output */ - pCD[iOther].nLenLinearCT = s[iOther].nLenCTAtOnly; - pCD[iOther].nLenCTAtOnly = s[iOther].nLenCTAtOnly; - pCD[iOther].lenNumH = lenNumHNoTautH = num_atoms; - pCD[iOther].lenNumHfixed = 0; - pCD[iOther].len_iso_sort_key = 0; - pCD[iOther].maxlen_iso_sort_key = 0; - pCD[iOther].len_iso_exchg_atnos = 0; - pCD[iOther].maxlen_iso_exchg_atnos = 0; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iOther].len_iso_sort_key_Hfixed = 0; - pCD[iOther].maxlen_iso_sort_key_Hfixed = 0; -#endif - pCD[iOther].nAuxRank = NULL; - - /* check whether we need NoTautH cononicalization */ - memset( nTempRank, 0, num_max * sizeof(nTempRank[0]) ); - for ( i = 0; i < num_atoms; i ++ ) { - if ( nTempRank[nSymmRankNoH[i]-1] < i ) { - nTempRank[nSymmRankNoH[i]-1] = i; /* greatest class representative */ - } - } - for ( i = 0; i < num_atoms; i ++ ) { - if ( numHNoTautH[i] != numHNoTautH[nTempRank[nSymmRankNoH[i]-1]] ) { - pCD[iOther].nCanonFlags |= CANON_FLAG_NO_TAUT_H_DIFF; - break; /* atoms so far found to be equivalent have different number of H; the canonicalization is needed */ - } - } - /* i = 0; *//* debug: force to call the canonicalization */ - if ( i < num_atoms ) { - /* needs canonicalization */ - /* get aux canonical ranking of the structure with attached H */ -#if ( USE_AUX_RANKING == 1 ) - /* refine no-H partition according to not-a-taut-H distribution */ - memset( pAtomInvariantAux, 0, num_max * sizeof(pAtomInvariantAux[0]) ); - for ( i = 0; i < num_atoms; i ++ ) { - pAtomInvariantAux[i].val[0] = nSymmRankNoH[i]; - pAtomInvariantAux[i].val[1] = numHNoTautH[i]; /* additional differentiation: not-a-taut-H distribution */ - } - /* initial partitioning */ - nNumCurrRanks = SetInitialRanks2( num_atoms, pAtomInvariantAux, nRankAux, nAtomNumberAux ); - /* make equitable partition */ - nNumCurrRanks = DifferentiateRanks2( num_atoms, NeighList[TAUT_NON], - nNumCurrRanks, nRankAux, - nTempRank, nAtomNumberAux, &lCount, 0 /* 0 means use qsort */ ); - /* to accelerate do not call CanonGraph() to find really equivalent atoms */ - pCD[iOther].nAuxRank = nRankAux; -#endif - - ret = CanonGraph03( num_atoms, num_atoms, num_max, 1 /* digraph?? was 0 */, NeighList[TAUT_NON], (Partition *)pBCN->pRankStack, - nSymmRankNoTautH, nCanonRankNoTautH, nAtomNumberCanonNoTautH, pCD+iOther, pCC, &Ct_NoH, &Ct_NoTautH ); - if ( ret < 0 ) { - goto exit_error; - } - /* in case of non-tautomeric structure the final results are in: - - nSymmRankNoTautH - nCanonRankNoTautH - nAtomNumberCanonNoTautH - Ct_NoTautH - numHNoTautH (original H positions) - */ - } else { - /* copy the results of the previous (no H) canonicalization */ - /* in this case numHNoTautH[] is not needed for the next canonicalization(s) */ - if ( (Ct_Temp = (ConTable *)inchi_calloc( 1, sizeof( *Ct_Temp ) ) ) && - CTableCreate( Ct_Temp, num_atoms, pCD+iOther) ) { - CtFullCopy( Ct_Temp, Ct_NoH ); - /* since Ct_NoH does not have Ct_NoH->NumH we have to fill out Ct_Temp->NumH separately */ - for ( i = 0; i < num_atoms; i ++ ) { - Ct_Temp->NumH[nCanonRankNoH[i]-1] = numHNoTautH[i]; - /*Ct_Temp->NumH[i] = numHNoTautH[nAtomNumberCanonNoH[i]]; -- alternative */ - } - Ct_Temp->lenNumH = num_atoms; - } else { - goto exit_error_alloc; - } - Ct_NoTautH = Ct_Temp; - Ct_Temp = NULL; - memcpy( nSymmRankNoTautH, nSymmRankNoH, num_atoms*sizeof(nSymmRankNoTautH[0]) ); - memcpy( nCanonRankNoTautH, nCanonRankNoH, num_atoms*sizeof(nCanonRankNoTautH[0]) ); - memcpy( nAtomNumberCanonNoTautH, nAtomNumberCanonNoH, num_atoms*sizeof(nAtomNumberCanonNoTautH[0]) ); - } - /* in case of non-tautomeric component this is the final result */ - /* i = CtFullCompare( Ct_NoTautH, Ct_Temp, num_atoms, 0, 0 );*/ - - /*******************************************************************************************/ - /* If only Isotopic atoms and isotopic H, tautomerism has not been found: */ - /* get isotopic canonical numbering, connection table, and equivalence partition */ - /*******************************************************************************************/ - - if ( s[iOther].num_isotopic_atoms && !s[iOther].bIgnoreIsotopic && !bReqTaut && bReqNonTaut ) { - - maxlen_iso_sort_key_NoTautH = num_atoms+1; - nSymmRankNoTautHIso = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankNoTautHIso[0] ) ); - nCanonRankNoTautHIso = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankNoTautHIso[0] ) ); - nAtomNumberCanonNoTautHIso = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonNoTautHIso[0]) ); - iso_sort_key_NoTautH = (AT_ISO_SORT_KEY *) inchi_calloc( maxlen_iso_sort_key_NoTautH, sizeof(iso_sort_key_NoTautH[0]) ); - - if ( !nSymmRankNoTautHIso || !nCanonRankNoTautHIso || !nAtomNumberCanonNoTautHIso || !iso_sort_key_NoTautH ) { - goto exit_error_alloc; - } - - /* fill out isotopic non-tautomeric keys */ - num_iso_NoTautH = 0; - for ( i = 0; i < num_atoms; i ++ ) { - if ( at_base[i].endpoint ) { - /* should not happen */ - iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, 0, 0, 0); - } else { - iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, at_base[i].num_iso_H[0], at_base[i].num_iso_H[1], at_base[i].num_iso_H[2]); - } - if ( iso_sort_key ) { - iso_sort_key_NoTautH[i] = iso_sort_key; - num_iso_NoTautH ++; - } else { - iso_sort_key_NoTautH[i] = EMPTY_ISO_SORT_KEY; - } - } - /* pointers */ - pCD[iOther].LinearCT = NULL; /* LinearCT; */ - pCD[iOther].NumH = numHNoTautH; - pCD[iOther].NumHfixed = NULL; - pCD[iOther].iso_sort_key = iso_sort_key_NoTautH; - pCD[iOther].iso_exchg_atnos = NULL; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iOther].iso_sort_key_Hfixed = NULL; -#endif - /* variables - unchanged */ - pCD[iOther].ulTimeOutTime = pBCN->ulTimeOutTime; - pCD[iOther].nMaxLenLinearCT = s[iOther].nLenCTAtOnly + 1; - pCD[iOther].maxlenNumH = maxlenNumHNoTautH; - /* return values & input/output */ - pCD[iOther].nLenLinearCT = s[iOther].nLenCTAtOnly; - pCD[iOther].nLenCTAtOnly = s[iOther].nLenCTAtOnly; - pCD[iOther].lenNumH = lenNumHNoTautH /*= num_atoms*/; - pCD[iOther].lenNumHfixed = 0; - pCD[iOther].len_iso_sort_key = len_iso_sort_key_NoTautH = num_atoms; - pCD[iOther].maxlen_iso_sort_key = maxlen_iso_sort_key_NoTautH; - pCD[iOther].len_iso_exchg_atnos = 0; - pCD[iOther].maxlen_iso_exchg_atnos = 0; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iOther].len_iso_sort_key_Hfixed = 0; - pCD[iOther].maxlen_iso_sort_key_Hfixed = 0; -#endif - pCD[iOther].nAuxRank = NULL; - - if ( num_iso_NoTautH ) { - /* check whether we need NoTautH cononicalization */ - memset( nTempRank, 0, num_max * sizeof(nTempRank[0]) ); - for ( i = 0; i < num_atoms; i ++ ) { - if ( nTempRank[nSymmRankNoTautH[i]-1] < i ) { - nTempRank[nSymmRankNoTautH[i]-1] = i; /* greatest class representative */ - } - } - for ( i = 0; i < num_atoms; i ++ ) { - if ( iso_sort_key_NoTautH[i] != iso_sort_key_NoTautH[nTempRank[nSymmRankNoTautH[i]-1]] ) { - pCD[iOther].nCanonFlags |= CANON_FLAG_ISO_ONLY_NON_TAUT_DIFF; - break; /* atoms so far found to be equivalent differ in isotopes; the canonicalization is needed */ - } - } - } else { - i = num_atoms; - } - /* i = 0; *//* debug: force to call the canonicalization */ - if ( i < num_atoms ) { - /* we need canonicalization */ - /* get aux canonical ranking of the structure with isotopic non-tautomeric H */ - -#if ( USE_AUX_RANKING == 1 ) - /* refine no-taut-H partition according to non-taut H isotopic distribution */ - memset( pAtomInvariantAux, 0, num_max * sizeof(pAtomInvariantAux[0]) ); - for ( i = 0; i < num_atoms; i ++ ) { - pAtomInvariantAux[i].val[0] = nSymmRankNoTautH[i]; - pAtomInvariantAux[i].iso_sort_key = iso_sort_key_NoTautH[i]; /* additional differentiation */ - } - /* initial ranks for non-taut H isotopic distribution */ - nNumCurrRanks = SetInitialRanks2( num_atoms, pAtomInvariantAux, nRankAux, nAtomNumberAux ); - /* make equitable */ - nNumCurrRanks = DifferentiateRanks2( num_atoms, NeighList[TAUT_NON], - nNumCurrRanks, nRankAux, - nTempRank, nAtomNumberAux, &lCount, 0 /* 0 means use qsort */ ); - /* to accelerate do not call CanonGraph() to find really equivalent atoms */ - pCD[iOther].nAuxRank = nRankAux; -#endif - - ret = CanonGraph04( num_atoms, num_atoms, num_max, 1 /* digraph?? was 0 */, NeighList[TAUT_NON], (Partition *)pBCN->pRankStack, - nSymmRankNoTautHIso, nCanonRankNoTautHIso, nAtomNumberCanonNoTautHIso, pCD+iOther, pCC, &Ct_NoTautH, &Ct_NoTautHIso ); - if ( ret < 0 ) { - goto exit_error; - } - /* in case of non-tautomeric structure the final results are in: - - nSymmRankNoTautHIso - nCanonRankNoTautHIso - nAtomNumberCanonNoTautHIso - Ct_NoTautHIso - iso_sort_key_NoTautH (original isotopic atom positions) - */ - } else { - /* copy the results of the previous (no taut H) canonicalization */ - /* in this case numHNoTautH[] is not needed for the next canonicalization(s) */ - if ( (Ct_Temp = (ConTable *)inchi_calloc( 1, sizeof( *Ct_Temp ) ) ) && - CTableCreate( Ct_Temp, num_atoms, pCD+iOther) ) { - CtFullCopy( Ct_Temp, Ct_NoTautH ); - /* since Ct_NoTautH does not have Ct_NoTautH->iso_sort_key we have to fill out Ct_Temp->iso_sort_key separately */ - for ( i = 0; i < num_atoms; i ++ ) { - Ct_Temp->iso_sort_key[nCanonRankNoTautH[i]-1] = iso_sort_key_NoTautH[i]; - } - Ct_Temp->len_iso_sort_key = num_atoms; - } else { - goto exit_error_alloc; - } - Ct_NoTautHIso = Ct_Temp; - Ct_Temp = NULL; - memcpy( nSymmRankNoTautHIso, nSymmRankNoTautH, num_atoms*sizeof(nSymmRankNoTautHIso[0]) ); - memcpy( nCanonRankNoTautHIso, nCanonRankNoTautH, num_atoms*sizeof(nCanonRankNoTautHIso[0]) ); - memcpy( nAtomNumberCanonNoTautHIso, nAtomNumberCanonNoTautH, num_atoms*sizeof(nAtomNumberCanonNoTautHIso[0]) ); - } - /* in case of non-tautomeric component this is the final result */ - /* i = CtFullCompare( Ct_NoTautHIso, Ct_Temp, num_atoms, 0, 0 );*/ - } - - - if ( bReqTaut ) { - /*****************************************************************************/ - /* Tautomeric Structure Canonicalizaton: */ - /* get base canonical numbering, connection table, and equivalence partition */ - /*****************************************************************************/ - /* find H atoms attached to non-tautomeric-endpoints and to tautomeric endpoints */ - maxlenNumH = num_atoms + T_NUM_NO_ISOTOPIC*(num_at_tg-num_atoms) + 1; /* including negative charges */ - nSymmRankBase = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankBase[0] ) ); - nCanonRankBase = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankBase[0] ) ); - nAtomNumberCanonBase = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonBase[0]) ); - numH = (NUM_H *) inchi_calloc( maxlenNumH, sizeof(numH[0]) ); - if ( !numH || !nSymmRankBase || !nCanonRankBase || !nAtomNumberCanonBase ) { - goto exit_error_alloc; - } - /* non-tautomeric H counts */ - for ( i = 0; i < num_atoms; i ++ ) { - numH[i] = (!at_base[i].endpoint && at_base[i].num_H)? at_base[i].num_H+BASE_H_NUMBER : EMPTY_H_NUMBER; - } - /* tautomeric H and negative charge counts */ - for ( i = k = num_atoms; i < num_at_tg; i ++ ) { - m = i-num_atoms; - for ( j = 0; j < T_NUM_NO_ISOTOPIC; j ++ ) { - /* non-zeroes for j=1 are negative charge counts; T_NUM_NO_ISOTOPIC=2 entry per t-group */ - numH[k ++] = t_group_info->t_group[m].num[j]? t_group_info->t_group[m].num[j]+BASE_H_NUMBER : EMPTY_H_NUMBER; - } - } - /* pointers */ - pCD[iBase].LinearCT = NULL; - pCD[iBase].NumH = numH; /* num_atoms non-tautomeric H; num_tg pairs of H and (-) in t-groups */ - pCD[iBase].NumHfixed = NULL; - pCD[iBase].iso_sort_key = NULL; - pCD[iBase].iso_exchg_atnos = NULL; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iBase].iso_sort_key_Hfixed = NULL; -#endif - /* variables - unchanged */ - pCD[iBase].ulTimeOutTime = pBCN->ulTimeOutTime; - pCD[iBase].nMaxLenLinearCT = s[iBase].nLenCT + 1; - pCD[iBase].maxlenNumH = maxlenNumH; - /* return values & input/output */ - pCD[iBase].nLenLinearCT = s[iBase].nLenCT; - pCD[iBase].nLenCTAtOnly = s[iBase].nLenCTAtOnly; - pCD[iBase].lenNumH = lenNumH = k; - pCD[iBase].lenNumHfixed = 0; - pCD[iBase].len_iso_sort_key = 0; - pCD[iBase].maxlen_iso_sort_key = 0; - pCD[iBase].len_iso_exchg_atnos = 0; - pCD[iBase].maxlen_iso_exchg_atnos = 0; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iBase].len_iso_sort_key_Hfixed = 0; - pCD[iBase].maxlen_iso_sort_key_Hfixed = 0; -#endif - pCD[iBase].nAuxRank = NULL; - - /* make sure the initial partition is equitable (at this point t-groups do not have ranks yet) */ - FillOutAtomInvariant2( at_base, num_atoms, num_at_tg, pAtomInvariant, 1 /*bIgnoreIsotopic*/, - 0 /*bHydrogensInRanks*/, 0 /*bHydrogensFixedInRanks*/, 1 /*bTaut=bDigraph*/, - 1 /* bTautGroupsOnly */, t_group_info ); - for ( i = 0; i < num_atoms; i ++ ) { - pAtomInvariant[i].val[0] = pBCN->pRankStack[0][i]; - } - /* initial ranks for t-group(s) only */ - nNumCurrRanks = SetInitialRanks2( num_at_tg, pAtomInvariant, nRank, nAtomNumber ); - /* make equitable, call digraph procedure; - pBCN->pRankStack[0] is nRank, pBCN->pRankStack[1] is nAtomNumber - This should only split ranks of tautomeric groups */ - nNumCurrRanks = DifferentiateRanks4( num_at_tg, NeighList[TAUT_YES], - nNumCurrRanks, pBCN->pRankStack[0], nTempRank /* temp array */, - pBCN->pRankStack[1], (AT_RANK)num_atoms, &lCount ); -#if ( USE_AUX_RANKING == 1 ) - /* refine no-H partition according to non-taut H distribution */ - memset( pAtomInvariantAux, 0, num_max * sizeof(pAtomInvariantAux[0]) ); - for ( i = 0; i < num_atoms; i ++ ) { - pAtomInvariantAux[i].val[0] = nSymmRankNoTautH[i]; - pAtomInvariantAux[i].val[1] = numH[i]; /* additional differentiation */ - } - for ( j = i; i < num_at_tg; i ++ ) { - pAtomInvariantAux[i].val[0] = nRank[i]; - } - - /* initial ranks for t-group(s) */ - nNumCurrRanks = SetInitialRanks2( num_at_tg, pAtomInvariantAux, nRankAux, nAtomNumberAux ); - /* make equitable, call digraph procedure */ - nNumCurrRanks = DifferentiateRanks4( num_at_tg, NeighList[TAUT_YES], - nNumCurrRanks, nRankAux, nTempRank /* temp array */, - nAtomNumberAux, (AT_RANK)num_atoms, &lCount ); - /* to accelerate do not call CanonGraph() to find really equivalent atoms */ - pCD[iBase].nAuxRank = nRankAux; -#endif - - - ret = CanonGraph05( num_atoms, num_at_tg, num_max, 1 /* digraph*/, NeighList[TAUT_YES], (Partition *)pBCN->pRankStack, - nSymmRankBase, nCanonRankBase, nAtomNumberCanonBase, pCD+iBase, pCC, &Ct_NoTautH, &Ct_Base ); - if ( ret < 0 ) { - goto exit_error; - } - - /* tautomeric isotopic structure */ - /**************************************************************************************/ - /* Isotopic atoms and isotopic H atoms and isotopic tautomeric groups */ - /* get isotopic canonical numbering, connection table, and equivalence partition */ - /**************************************************************************************/ - if ( s[iBase].num_isotopic_atoms && !s[iBase].bIgnoreIsotopic || - s[iBase].bHasIsotopicTautGroups && !bTautIgnoreIsotopic || - bUseIsoAuxBase[iBase] && !bTautIgnoreIsotopic ) { - - t_group_info->bIgnoreIsotopic = bTautIgnoreIsotopic; - - nSymmRankBaseIso = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankBaseIso[0] ) ); - nCanonRankBaseIso = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankBaseIso[0] ) ); - nAtomNumberCanonBaseIso = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonBaseIso[0]) ); - if ( bUseIsoAuxBase[iBase] ) { - maxlen_iso_exchg_atnos = num_max+1; - iso_exchg_atnos = (S_CHAR *) inchi_calloc( maxlen_iso_exchg_atnos, sizeof(iso_exchg_atnos[0]) ); - } - maxlen_iso_sort_keyBase = num_max+1; /* num_at_tg+1;*/ - iso_sort_keyBase = (AT_ISO_SORT_KEY *) inchi_calloc( maxlen_iso_sort_keyBase, sizeof(iso_sort_keyBase[0]) ); - if ( !nSymmRankBaseIso || !nCanonRankBaseIso || !nAtomNumberCanonBaseIso || - !iso_sort_keyBase || - maxlen_iso_exchg_atnos && !iso_exchg_atnos ) { - goto exit_error_alloc; - } - /* atoms */ - num_iso_NoTautH = 0; - num_iso_NoAuxBase = 0; - if ( iso_exchg_atnos ) { - len_iso_exchg_atnos = num_at_tg; - } - for ( i = 0; i < num_atoms; i ++ ) { - if ( at_base[i].endpoint || iso_exchg_atnos && (at_base[i].cFlags & AT_FLAG_ISO_H_POINT) ) { - /* tautomeric or may have exchangeable isotopic H */ - iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, 0, 0, 0); - if ( iso_exchg_atnos ) { - num_iso_NoAuxBase += !at_base[i].endpoint; /* these non-taut atom may exchange isotopic H as tautomeric atoms do */ - } - } else { - /* non-mobile H */ - iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, at_base[i].num_iso_H[0], at_base[i].num_iso_H[1], at_base[i].num_iso_H[2]); - if ( iso_exchg_atnos ) { - iso_exchg_atnos[i] = 1; /* atom cannot have exchangable isotopic H atom(s) */ - } - } - if ( iso_sort_key ) { - num_iso_NoTautH ++; - iso_sort_keyBase[i] = iso_sort_key; - } else { - iso_sort_keyBase[i] = EMPTY_ISO_SORT_KEY; - } - } - /* check marking and count of non-taut atoms that may exchange isotopic H -- debug only */ - if ( iso_exchg_atnos ) { - if ( num_iso_NoAuxBase != t_group_info->nIsotopicEndpointAtomNumber[0] ) { - ret = CT_ISOCOUNT_ERR; - goto exit_error; - } - for ( i = 1; i <= num_iso_NoAuxBase; i ++ ) { - j = t_group_info->nIsotopicEndpointAtomNumber[i]; - if ( at_base[j].endpoint || !(at_base[j].cFlags & AT_FLAG_ISO_H_POINT) ) { - ret = CT_ISOCOUNT_ERR; - goto exit_error; - } - } - } - /* t-groups */ - num_iso_Base = 0; - if ( iso_exchg_atnos ) { - for ( i = num_atoms; i < num_at_tg; i ++ ) { - iso_sort_keyBase[i] = EMPTY_ISO_SORT_KEY; /* new mode: do not provide info about isotopic tautomeric H */ - } - } else { - for ( i = num_atoms; i < num_at_tg; i ++ ) { /* should not happen anymore */ - m = i-num_atoms; - if ( iso_sort_key = t_group_info->t_group[m].iWeight ) { - /* old approach: each t-group has its own isotopic "weight" */ - num_iso_Base ++; - iso_sort_keyBase[i] = iso_sort_key; - } else { - iso_sort_keyBase[i] = EMPTY_ISO_SORT_KEY; - } - } - } - if ( !num_iso_NoAuxBase && iso_exchg_atnos ) { - /* all atoms that may exchange isotopic H are either tautomeric or not present */ - inchi_free( iso_exchg_atnos ); - iso_exchg_atnos = NULL; - len_iso_exchg_atnos = 0; - maxlen_iso_exchg_atnos = 0; - } - if ( !num_iso_NoTautH && !num_iso_Base && iso_sort_keyBase ) { - /* no isotopic atoms present */ - inchi_free( iso_sort_keyBase ); - iso_sort_keyBase = NULL; - maxlen_iso_sort_keyBase = 0; - } else { - len_iso_sort_keyBase = num_at_tg; - } - if ( !iso_exchg_atnos && !iso_sort_keyBase ) { - /* no isotopic part at all or only tautomeric groups */ - inchi_free( nSymmRankBaseIso ); nSymmRankBaseIso = NULL; - inchi_free( nCanonRankBaseIso ); nCanonRankBaseIso = NULL; - inchi_free( nAtomNumberCanonBaseIso ); nAtomNumberCanonBaseIso = NULL; - } else { - /* proceed with tautomeric isotopic canonicalization */ - /* pointers */ - pCD[iBase].LinearCT = NULL; - pCD[iBase].NumH = numH; /* num_atoms non-tautomeric H; num_tg pairs of H and (-) in t-groups */ - pCD[iBase].NumHfixed = NULL; - pCD[iBase].iso_sort_key = iso_sort_keyBase; - pCD[iBase].iso_exchg_atnos = iso_exchg_atnos; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iBase].iso_sort_key_Hfixed = NULL; -#endif - /* variables - unchanged */ - pCD[iBase].ulTimeOutTime = pBCN->ulTimeOutTime; - pCD[iBase].nMaxLenLinearCT = s[iBase].nLenCT + 1; - pCD[iBase].maxlenNumH = maxlenNumH; - /* return values & input/output */ - pCD[iBase].nLenLinearCT = s[iBase].nLenCT; - pCD[iBase].nLenCTAtOnly = s[iBase].nLenCTAtOnly; - pCD[iBase].lenNumH = lenNumH /* = k */; - pCD[iBase].lenNumHfixed = 0; - pCD[iBase].len_iso_sort_key = len_iso_sort_keyBase; - pCD[iBase].maxlen_iso_sort_key = maxlen_iso_sort_keyBase; - pCD[iBase].len_iso_exchg_atnos = len_iso_exchg_atnos; - pCD[iBase].maxlen_iso_exchg_atnos = maxlen_iso_exchg_atnos; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iBase].len_iso_sort_key_Hfixed = 0; - pCD[iBase].maxlen_iso_sort_key_Hfixed = 0; -#endif - pCD[iBase].nAuxRank = NULL; - - if ( num_iso_NoTautH || num_iso_Base || num_iso_NoAuxBase ) { - /* check whether we need actual canonicalization */ - memset( nTempRank, 0, num_max * sizeof(nTempRank[0]) ); - for ( i = 0; i < num_at_tg; i ++ ) { - if ( nTempRank[nSymmRankBase[i]-1] < i ) { - nTempRank[nSymmRankBase[i]-1] = i; /* greatest class representative */ - } - } - for ( i = 0; i < num_at_tg; i ++ ) { - if ( (iso_sort_keyBase? (iso_sort_keyBase[i] != iso_sort_keyBase[nTempRank[nSymmRankBase[i]-1]]):0) || - (iso_exchg_atnos? (iso_exchg_atnos[i] != iso_exchg_atnos[nTempRank[nSymmRankBase[i]-1]]):0)) { - pCD[iBase].nCanonFlags |= CANON_FLAG_ISO_TAUT_DIFF; - break; /* atoms so far found to be equivalent have different number of H; the canonicalization is needed */ - } - } - } else { - i = num_at_tg; /* should not happen */ - } - /* i = 0; *//* debug: force to call the canonicalization */ - if ( i < num_at_tg ) { - /* we need canonicalization */ - /* get aux canonical ranking of the structure with isotopic non-tautomeric H */ - - #if ( USE_AUX_RANKING == 1 ) - /* refine no-taut-H partition according to non-taut H + t-groups isotopic distribution */ - memset( pAtomInvariantAux, 0, num_max * sizeof(pAtomInvariantAux[0]) ); - for ( i = 0; i < num_at_tg; i ++ ) { - pAtomInvariantAux[i].val[0] = nSymmRankBase[i]; - pAtomInvariantAux[i].iso_sort_key = iso_sort_keyBase? iso_sort_keyBase[i] : 0; /* additional differentiation */ - pAtomInvariantAux[i].iso_aux_key = iso_exchg_atnos? iso_exchg_atnos[i] : 0; - } - /* initial ranks for non-taut H isotopic distribution */ - nNumCurrRanks = SetInitialRanks2( num_at_tg, pAtomInvariantAux, nRankAux, nAtomNumberAux ); - /* make equitable, not a digraph procedure */ - nNumCurrRanks = DifferentiateRanks2( num_at_tg, NeighList[TAUT_YES], - nNumCurrRanks, nRankAux, - nTempRank, nAtomNumberAux, &lCount, 0 /* 0 means first use qsort */ ); - /* to accelerate do not call CanonGraph() to find really equivalent atoms */ - pCD[iBase].nAuxRank = nRankAux; - #endif - - - ret = CanonGraph06( num_atoms, num_at_tg, num_max, 1 /* digraph */, NeighList[TAUT_YES], (Partition *)pBCN->pRankStack, - nSymmRankBaseIso, nCanonRankBaseIso, nAtomNumberCanonBaseIso, pCD+iBase, pCC, &Ct_Base, &Ct_BaseIso ); - if ( ret < 0 ) { - goto exit_error; - } - /* in case of a tautomeric structure the final results are in: - - nSymmRankBaseIso - nCanonRankBaseIso - nAtomNumberCanonBaseIso - Ct_BaseIso - iso_sort_keyBase (original isotopic atom & t-group positions) - Ct_BaseIso->iso_exchg_atnos: 0=>can exchange isotopic H, including tautomeric atoms - iso_exchg_atnos : same, in order of t_group_info->nIsotopicEndpointAtomNumber[] - */ - } else { - /* copy the results of the previous (no taut H) canonicalization */ - /* in this case numHNoTautH[] is not needed for the next canonicalization(s) */ - if ( (Ct_Temp = (ConTable *)inchi_calloc( 1, sizeof( *Ct_Temp ) ) ) && - CTableCreate( Ct_Temp, num_atoms, pCD+iBase) ) { - CtFullCopy( Ct_Temp, Ct_Base ); - /* since Ct_Base does not have Ct_Base->iso_sort_key we - have to fill out Ct_Temp->iso_sort_key separately */ - if ( iso_sort_keyBase ) { - for ( i = 0; i < num_at_tg; i ++ ) { - Ct_Temp->iso_sort_key[nCanonRankBase[i]-1] = iso_sort_keyBase[i]; - } - Ct_Temp->len_iso_sort_key = num_at_tg; - } else { - Ct_Temp->len_iso_sort_key = 0; - } - if ( iso_exchg_atnos ) { - for ( i = 0; i < num_atoms; i ++ ) { - Ct_Temp->iso_exchg_atnos[nCanonRankBase[i]-1] = iso_exchg_atnos[i]; - } - Ct_Temp->len_iso_exchg_atnos = num_at_tg; - } else { - Ct_Temp->len_iso_exchg_atnos = 0; - } - } else { - goto exit_error_alloc; - } - Ct_BaseIso = Ct_Temp; - Ct_Temp = NULL; - memcpy( nSymmRankBaseIso, nSymmRankBase, num_at_tg*sizeof(nSymmRankBaseIso[0]) ); - memcpy( nCanonRankBaseIso, nCanonRankBase, num_at_tg*sizeof(nCanonRankBaseIso[0]) ); - memcpy( nAtomNumberCanonBaseIso, nAtomNumberCanonBase, num_at_tg*sizeof(nAtomNumberCanonBaseIso[0]) ); - } - /* in case of non-tautomeric component this is the final result */ - /* i = CtFullCompare( Ct_BaseIso, Ct_Temp, num_at_tg, 0, 0 );*/ - - t_group_info->bIgnoreIsotopic = 1; - } - } - } - - /**********************************************************************************/ - /* get "fixed H" canonical numbering, connection table, and equivalence partition */ - /**********************************************************************************/ - - if ( bReqTaut && bReqNonTaut ) { - maxlenNumHfixed = num_atoms + 1; - nSymmRankFixH = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankFixH[0] ) ); - nCanonRankFixH = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankFixH[0] ) ); - nAtomNumberCanonFixH = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonFixH[0]) ); - NumHfixed = (NUM_H *) inchi_calloc( maxlenNumHfixed, sizeof(NumHfixed[0]) ); - if ( !NumHfixed || !nSymmRankFixH || !nCanonRankFixH || !nAtomNumberCanonFixH ) { - goto exit_error_alloc; - } - for ( i = 0; i < num_atoms; i ++ ) { - /* fixed and non-tautomeric H different in taut and non-taut structures */ - if ( at_base[i].endpoint ) { - NumHfixed[i] = at_other[i].num_H? at_other[i].num_H+BASE_H_NUMBER : EMPTY_H_NUMBER; - } else - if ( at_other[i].num_H != at_base[i].num_H ) { - NumHfixed[i] = (NUM_H)at_other[i].num_H - (NUM_H)at_base[i].num_H + BASE_H_NUMBER; - } else { - NumHfixed[i] = EMPTY_H_NUMBER; - } - } - /* pointers */ - pCD[iOther].LinearCT = NULL; /* LinearCT; */ - pCD[iOther].NumH = numHNoTautH; - pCD[iOther].NumHfixed = NumHfixed;/* variables - unchanged */ - pCD[iOther].iso_sort_key = NULL; - pCD[iOther].iso_exchg_atnos = NULL; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iOther].iso_sort_key_Hfixed = NULL; -#endif - pCD[iOther].ulTimeOutTime = pBCN->ulTimeOutTime; - pCD[iOther].nMaxLenLinearCT = s[iOther].nLenCTAtOnly + 1; - pCD[iOther].maxlenNumH = maxlenNumHNoTautH; - pCD[iOther].maxlenNumHfixed = maxlenNumHfixed; - /* return values & input/output */ - pCD[iOther].nLenLinearCT = s[iOther].nLenCTAtOnly; - pCD[iOther].nLenCTAtOnly = s[iOther].nLenCTAtOnly; - pCD[iOther].lenNumH = lenNumHNoTautH = num_atoms; - pCD[iOther].lenNumHfixed = num_atoms; - pCD[iOther].len_iso_sort_key = 0; - pCD[iOther].maxlen_iso_sort_key = 0; - pCD[iOther].len_iso_exchg_atnos = 0; - pCD[iOther].maxlen_iso_exchg_atnos = 0; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iOther].len_iso_sort_key_Hfixed = 0; - pCD[iOther].maxlen_iso_sort_key_Hfixed = 0; -#endif - pCD[iOther].nAuxRank = NULL; - -#if ( USE_AUX_RANKING == 1 ) - if ( !nRankAux ) - nRankAux = (AT_RANK *) inchi_calloc( num_max, sizeof(nRankAux[0] ) ); - if ( !nAtomNumberAux ) - nAtomNumberAux = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberAux[0] ) ); - if ( !pAtomInvariantAux ) - pAtomInvariantAux = (ATOM_INVARIANT2 *) inchi_malloc( num_max * sizeof(pAtomInvariantAux[0]) ); - - if ( !nRankAux || !nAtomNumberAux || - !pAtomInvariantAux ) { - goto exit_error_alloc; - } - /* refine no-H partition according to non-taut H distribution */ - memset( pAtomInvariantAux, 0, num_max * sizeof(pAtomInvariantAux[0]) ); - for ( i = 0; i < num_atoms; i ++ ) { - pAtomInvariantAux[i].val[0] = nSymmRankBase[i]; - pAtomInvariantAux[i].val[1] = NumHfixed[i]; /* additional differentiation */ - } - - /* initial ranks for t-group(s) */ - nNumCurrRanks = SetInitialRanks2( num_atoms, pAtomInvariantAux, nRankAux, nAtomNumberAux ); - /* make equitable, digraph procedure */ - nNumCurrRanks = DifferentiateRanks2( num_atoms, NeighList[TAUT_NON], - nNumCurrRanks, nRankAux, - nTempRank, nAtomNumberAux, &lCount, 0 /* 0 means use qsort */ ); - /* to accelerate do not call CanonGraph() to find really equivalent atoms */ - pCD[iOther].nAuxRank = nRankAux; -#endif - - ret = CanonGraph07( num_atoms, num_atoms, num_max, 0, NeighList[TAUT_NON], (Partition *)pBCN->pRankStack, - nSymmRankFixH, nCanonRankFixH, nAtomNumberCanonFixH, pCD+iOther, pCC, &Ct_NoTautH, &Ct_FixH ); - if ( ret < 0 ) { - goto exit_error; - } - - /*******************************************************************************************/ - /* get "fixed H" isotopic canonical numbering, connection table, and equivalence partition */ - /*******************************************************************************************/ - iflag = s[iBase].num_isotopic_atoms && !s[iBase].bIgnoreIsotopic || - s[iBase].bHasIsotopicTautGroups && !bTautIgnoreIsotopic; - if (bFixIsoFixedH) /* #if ( FIX_ISO_FIXEDH_BUG == 1 ) */ - /* fix bug when iso H was removed as a proton and fixed-H isotopic layer is missing - 2008-09-24 DT*/ - iflag = iflag || s[iOther].num_isotopic_atoms && !s[iOther].bIgnoreIsotopic; - if (iflag) { - -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - maxlen_iso_sort_key_Hfixed = -#endif - maxlen_iso_sort_key_NoTautH = num_atoms+1; - nSymmRankFixHIso = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankFixHIso[0] ) ); - nCanonRankFixHIso = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankFixHIso[0] ) ); - nAtomNumberCanonFixHIso = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonFixHIso[0]) ); -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - iso_sort_key_Hfixed = (AT_ISO_SORT_KEY *) inchi_calloc( maxlen_iso_sort_key_Hfixed, sizeof(iso_sort_key_Hfixed[0]) ); -#endif - iso_sort_key_NoTautH = (AT_ISO_SORT_KEY *) inchi_calloc( maxlen_iso_sort_key_NoTautH, sizeof(iso_sort_key_NoTautH[0]) ); - - if ( !nSymmRankFixHIso || !nCanonRankFixHIso || !nAtomNumberCanonFixHIso || -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - !iso_sort_key_Hfixed || -#endif - !iso_sort_key_NoTautH ) { - goto exit_error_alloc; - } - -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - /* fill out isotopic non-tautomeric keys */ - for ( i = 0; i < num_atoms; i ++ ) { - if ( at_base[i].endpoint ) { - iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, 0, 0, 0); - iso_sort_key2 = make_iso_sort_key( 0, at_base[i].num_iso_H[0], at_base[i].num_iso_H[1], at_base[i].num_iso_H[2]); - } else { - iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, at_base[i].num_iso_H[0], at_base[i].num_iso_H[1], at_base[i].num_iso_H[2]); - iso_sort_key2 = 0; - } - if ( iso_sort_key ) { - iso_sort_key_NoTautH[i] = iso_sort_key; - num_iso_NoTautH ++; - } else { - iso_sort_key_NoTautH[i] = EMPTY_ISO_SORT_KEY; - } - if ( iso_sort_key2 ) { - num_iso_Hfixed ++; - iso_sort_key_Hfixed[i] = iso_sort_key2; - } else { - iso_sort_key_Hfixed[i] = EMPTY_ISO_SORT_KEY; - } - } -#else - /* fill out isotopic non-tautomeric keys */ - for ( i = 0; i < num_atoms; i ++ ) { - - if (bFixIsoFixedH) /* #if ( FIX_ISO_FIXEDH_BUG == 1 ) */ - { - /* fix bug when iso H was removed as a proton and fixed-H isotopic layer is missing - 2008-09-24 DT*/ - if ( at_other ) - { - iso_sort_key = make_iso_sort_key( at_other[i].iso_atw_diff, at_other[i].num_iso_H[0], at_other[i].num_iso_H[1], at_other[i].num_iso_H[2]); - } - else - { - iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, at_base[i].num_iso_H[0], at_base[i].num_iso_H[1], at_base[i].num_iso_H[2]); - } - } - else - iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, at_base[i].num_iso_H[0], at_base[i].num_iso_H[1], at_base[i].num_iso_H[2]); - - - - - if ( iso_sort_key ) { - iso_sort_key_NoTautH[i] = iso_sort_key; - num_iso_NoTautH ++; - } else { - iso_sort_key_NoTautH[i] = EMPTY_ISO_SORT_KEY; - } - } -#endif - /* pointers */ - pCD[iOther].LinearCT = NULL; /* LinearCT; */ - pCD[iOther].NumH = numHNoTautH; - pCD[iOther].NumHfixed = NumHfixed;/* variables - unchanged */ - pCD[iOther].iso_sort_key = iso_sort_key_NoTautH; - pCD[iOther].iso_exchg_atnos = NULL; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iOther].iso_sort_key_Hfixed = iso_sort_key_Hfixed; -#endif - pCD[iOther].ulTimeOutTime = pBCN->ulTimeOutTime; - pCD[iOther].nMaxLenLinearCT = s[iOther].nLenCTAtOnly + 1; - pCD[iOther].maxlenNumH = maxlenNumHNoTautH; - pCD[iOther].maxlenNumHfixed = maxlenNumHfixed; - /* return values & input/output */ - pCD[iOther].nLenLinearCT = s[iOther].nLenCTAtOnly; - pCD[iOther].nLenCTAtOnly = s[iOther].nLenCTAtOnly; - pCD[iOther].lenNumH = lenNumHNoTautH = num_atoms; - pCD[iOther].lenNumHfixed = num_atoms; - pCD[iOther].len_iso_sort_key = len_iso_sort_key_NoTautH = num_atoms; - pCD[iOther].maxlen_iso_sort_key = maxlen_iso_sort_key_NoTautH; - pCD[iOther].len_iso_exchg_atnos = 0; - pCD[iOther].maxlen_iso_exchg_atnos = 0; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - pCD[iOther].len_iso_sort_key_Hfixed = len_iso_sort_key_Hfixed = num_atoms; - pCD[iOther].maxlen_iso_sort_key_Hfixed = maxlen_iso_sort_key_Hfixed; -#endif - pCD[iOther].nAuxRank = NULL; - -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - if ( num_iso_Hfixed || num_iso_NoTautH ) -#else - if ( num_iso_NoTautH ) -#endif - { - /* check whether we need NoTautH cononicalization */ - memset( nTempRank, 0, num_max * sizeof(nTempRank[0]) ); - for ( i = 0; i < num_atoms; i ++ ) { - if ( nTempRank[nSymmRankFixH[i]-1] < i ) { - nTempRank[nSymmRankFixH[i]-1] = i; /* greatest class representative */ - } - } - for ( i = 0; i < num_atoms; i ++ ) { -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - if ( iso_sort_key_Hfixed[i] != iso_sort_key_Hfixed[nTempRank[nSymmRankFixH[i]-1]] ) - break; -#endif - if ( iso_sort_key_NoTautH[i] != iso_sort_key_NoTautH[nTempRank[nSymmRankFixH[i]-1]]) - break; /* atoms so far found to be equivalent have different isotopic shifts; the canonicalization is needed */ - } - } else { - i = num_atoms; /* should not happen */ - } - /* i = 0; *//* debug: force to call the canonicalization */ - if ( i < num_atoms ) { - pCD[iOther].nCanonFlags |= CANON_FLAG_ISO_FIXED_H_DIFF; - /* we need canonicalization */ - /* get aux canonical ranking of the structure with isotopic non-tautomeric H */ - -#if ( USE_AUX_RANKING == 1 ) - /* refine fixed-taut-H partition according to the isotopic distribution */ - memset( pAtomInvariantAux, 0, num_max * sizeof(pAtomInvariantAux[0]) ); - for ( i = 0; i < num_atoms; i ++ ) { - pAtomInvariantAux[i].val[0] = nSymmRankFixH[i]; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - iso_sort_key = 0; - if (iso_sort_key_NoTautH[i]!=EMPTY_ISO_SORT_KEY) - iso_sort_key |= iso_sort_key_NoTautH[i]; - if (iso_sort_key_Hfixed[i] !=EMPTY_ISO_SORT_KEY) - iso_sort_key |= iso_sort_key_Hfixed[i]; - if ( !iso_sort_key ) - iso_sort_key = EMPTY_ISO_SORT_KEY; -#else - iso_sort_key = iso_sort_key_NoTautH[i]; -#endif - pAtomInvariantAux[i].iso_sort_key = iso_sort_key; /* additional differentiation */ - } - - /* initial ranks for non-taut H isotopic distribution */ - nNumCurrRanks = SetInitialRanks2( num_atoms, pAtomInvariantAux, nRankAux, nAtomNumberAux ); - /* make equitable, digraph procedure */ - nNumCurrRanks = DifferentiateRanks2( num_atoms, NeighList[TAUT_NON], - nNumCurrRanks, nRankAux, - nTempRank, nAtomNumberAux, &lCount, 0 /* 0 means use qsort */ ); - /* to accelerate do not call CanonGraph() to find really equivalent atoms */ - pCD[iOther].nAuxRank = nRankAux; -#endif - - - ret = CanonGraph08( num_atoms, num_atoms, num_max, 1 /* digraph?? was 0 */, NeighList[TAUT_NON], (Partition *)pBCN->pRankStack, - nSymmRankFixHIso, nCanonRankFixHIso, nAtomNumberCanonFixHIso, pCD+iOther, pCC, &Ct_FixH, &Ct_FixHIso ); - if ( ret < 0 ) { - goto exit_error; - } - /* in case of non-tautomeric structure the final results are in: - - nSymmRankFixHIso - nCanonRankFixHIso - nAtomNumberCanonFixHIso - Ct_FixHIso - iso_sort_keyBase ([0..num_atoms] original isotopic atom positions) - iso_sort_key_Hfixed (original fixed tautomeric H distribution) - */ - } else { - /* copy the results of the previous (no taut H) canonicalization */ - /* in this case numHNoTautH[] is not needed for the next canonicalization(s) */ - if ( (Ct_Temp = (ConTable *)inchi_calloc( 1, sizeof( *Ct_Temp ) ) ) && - CTableCreate( Ct_Temp, num_atoms, pCD+iOther) ) { - CtFullCopy( Ct_Temp, Ct_FixH ); - /* since Ct_FixH does not have Ct_FixH->iso_sort_key and Ct_FixH->iso_sort_key_Hfixed we - have to fill out Ct_Temp->iso_sort_key and Ct_Temp->iso_sort_key_Hfixed separately */ - for ( i = 0; i < num_atoms; i ++ ) { - Ct_Temp->iso_sort_key[nCanonRankFixH[i]-1] = iso_sort_key_NoTautH[i]; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - Ct_Temp->iso_sort_key_Hfixed[nCanonRankFixH[i]-1] = iso_sort_key_Hfixed[i]; -#endif - } - Ct_Temp->len_iso_sort_key = num_atoms; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - Ct_Temp->len_iso_sort_key_Hfixed = num_atoms; -#endif - /*Ct_Temp->lenNumH = num_atoms;*/ - } else { - goto exit_error_alloc; - } - Ct_FixHIso = Ct_Temp; - Ct_Temp = NULL; - memcpy( nSymmRankFixHIso, nSymmRankFixH, num_atoms*sizeof(nSymmRankFixHIso[0]) ); - memcpy( nCanonRankFixHIso, nCanonRankFixH, num_atoms*sizeof(nCanonRankFixHIso[0]) ); - memcpy( nAtomNumberCanonFixHIso, nAtomNumberCanonFixH, num_atoms*sizeof(nAtomNumberCanonFixHIso[0]) ); - } - /* in case of non-tautomeric component this is the final result */ - /* i = CtFullCompare( Ct_NoTautHIso, Ct_Temp, num_atoms, 0, 0 );*/ - } - } /* "fixed H" canonical numbering */ - - /* consistency check: compare canonical connection tables, H-atoms, isotopic H & taut groups */ - ret = 0; - ret |= (Ct_NoH->lenCt != Ct_NoTautH->lenCt) || memcmp(Ct_NoH->Ctbl, Ct_NoTautH->Ctbl, Ct_NoH->lenCt * sizeof(Ct_NoH->Ctbl[0])); - if ( bReqTaut ) { - if ( Ct_FixH ) { - ret |= (Ct_NoTautH->lenCt != Ct_FixH->lenCt) || memcmp(Ct_NoTautH->Ctbl, Ct_FixH->Ctbl, Ct_NoTautH->lenCt * sizeof(Ct_NoTautH->Ctbl[0])); - ret |= (Ct_NoTautH->lenNumH != Ct_FixH->lenNumH) || memcmp( Ct_NoTautH->NumH, Ct_FixH->NumH, Ct_NoTautH->lenNumH*sizeof(Ct_Base->NumH[0])); - } - ret |= (Ct_NoTautH->lenCt > Ct_Base->lenCt) || memcmp(Ct_NoTautH->Ctbl, Ct_Base->Ctbl, Ct_NoTautH->lenCt * sizeof(Ct_NoTautH->Ctbl[0])); - ret |= (Ct_NoTautH->lenNumH > Ct_Base->lenNumH) || memcmp( Ct_NoTautH->NumH, Ct_Base->NumH, Ct_NoTautH->lenNumH*sizeof(Ct_Base->NumH[0])); - } - - /* isotopic canonicalization */ - if ( Ct_NoTautHIso ) { - ret |= (Ct_NoH->lenCt != Ct_NoTautHIso->lenCt) || memcmp(Ct_NoH->Ctbl, Ct_NoTautHIso->Ctbl, Ct_NoH->lenCt * sizeof(Ct_NoH->Ctbl[0])); - ret |= (Ct_NoTautH->lenNumH != Ct_NoTautHIso->lenNumH) || memcmp( Ct_NoTautH->NumH, Ct_NoTautHIso->NumH, Ct_NoTautH->lenNumH*sizeof(Ct_Base->NumH[0])); - } else - if ( Ct_BaseIso ) { - ret |= (Ct_BaseIso->lenCt != Ct_Base->lenCt) || memcmp(Ct_BaseIso->Ctbl, Ct_Base->Ctbl, Ct_BaseIso->lenCt * sizeof(Ct_BaseIso->Ctbl[0])); - ret |= (Ct_BaseIso->lenNumH != Ct_Base->lenNumH) || memcmp( Ct_BaseIso->NumH, Ct_Base->NumH, Ct_BaseIso->lenNumH*sizeof(Ct_BaseIso->NumH[0])); - if ( Ct_FixHIso ) { - ret |= (Ct_FixHIso->lenCt > Ct_BaseIso->lenCt) || memcmp(Ct_FixHIso->Ctbl, Ct_BaseIso->Ctbl, Ct_FixHIso->lenCt * sizeof(Ct_FixHIso->Ctbl[0])); - ret |= (Ct_FixHIso->lenNumH > Ct_BaseIso->lenNumH) || memcmp( Ct_FixHIso->NumH, Ct_BaseIso->NumH, Ct_FixHIso->lenNumH*sizeof(Ct_BaseIso->NumH[0])); - } - } - - if ( ret ) { - goto exit_error; - } - - if ( bReqTaut ) { - /* restore save "process isotopic" mark; temporarily set it to NO */ - t_group_info->bIgnoreIsotopic = bTautIgnoreIsotopic; - } - - - /* output the canonicalization results */ - pBCN->num_max = num_max; - pBCN->num_at_tg = num_at_tg; - pBCN->num_atoms = num_atoms; - - pBCN->ftcn[TAUT_NON].NeighList = NeighList[TAUT_NON]; NeighList[TAUT_NON] = NULL; - pBCN->ftcn[TAUT_YES].NeighList = NeighList[TAUT_YES]; NeighList[TAUT_YES] = NULL; - - if ( bReqTaut ) { /* tautomeric results */ - /* base tautomeric structure, iBase = TAUT_YES */ - - pBCN->ftcn[TAUT_YES].num_at_tg = num_at_tg; - pBCN->ftcn[TAUT_YES].num_atoms = num_atoms; - - pBCN->ftcn[TAUT_YES].LinearCt = Ct_Base->Ctbl; Ct_Base->Ctbl = NULL; - pBCN->ftcn[TAUT_YES].nLenLinearCtAtOnly = s[iBase].nLenCTAtOnly; - pBCN->ftcn[TAUT_YES].nMaxLenLinearCt = s[iBase].nLenCT+1; - pBCN->ftcn[TAUT_YES].nLenLinearCt = s[iBase].nLenCT; - - pBCN->ftcn[TAUT_YES].PartitionCt.Rank = nCanonRankBase; nCanonRankBase = NULL; - pBCN->ftcn[TAUT_YES].PartitionCt.AtNumber = nAtomNumberCanonBase; nAtomNumberCanonBase = NULL; - pBCN->ftcn[TAUT_YES].nSymmRankCt = nSymmRankBase; nSymmRankBase = NULL; - - pBCN->ftcn[TAUT_YES].nNumHOrig = numH; numH = NULL; - pBCN->ftcn[TAUT_YES].nNumH = Ct_Base->NumH; Ct_Base->NumH = NULL; - pBCN->ftcn[TAUT_YES].nLenNumH = inchi_min(maxlenNumH, Ct_Base->maxlenNumH); - - /* fixed H structure: exists only if the structure is tautomeric */ - pBCN->ftcn[TAUT_YES].nNumHOrigFixH = NULL; - pBCN->ftcn[TAUT_YES].nNumHFixH = NULL; - pBCN->ftcn[TAUT_YES].nLenNumHFixH = 0; - pBCN->ftcn[TAUT_YES].nCanonFlags |= pCD[iBase].nCanonFlags; - - CleanNumH( pBCN->ftcn[TAUT_YES].nNumHOrig, pBCN->ftcn[TAUT_YES].nLenNumH ); - CleanNumH( pBCN->ftcn[TAUT_YES].nNumH, pBCN->ftcn[TAUT_YES].nLenNumH ); - CleanCt ( pBCN->ftcn[TAUT_YES].LinearCt, pBCN->ftcn[TAUT_YES].nLenLinearCt ); - - /* isotopic canonicalization */ - if ( Ct_BaseIso ) { - pBCN->ftcn[TAUT_YES].PartitionCtIso.Rank = nCanonRankBaseIso; nCanonRankBaseIso = NULL; - pBCN->ftcn[TAUT_YES].PartitionCtIso.AtNumber = nAtomNumberCanonBaseIso; nAtomNumberCanonBaseIso = NULL; - pBCN->ftcn[TAUT_YES].nSymmRankCtIso = nSymmRankBaseIso; nSymmRankBaseIso = NULL; - pBCN->ftcn[TAUT_YES].iso_sort_keys = Ct_BaseIso->iso_sort_key; Ct_BaseIso->iso_sort_key = NULL; - pBCN->ftcn[TAUT_YES].iso_sort_keysOrig = iso_sort_keyBase; iso_sort_keyBase = NULL; - pBCN->ftcn[TAUT_YES].len_iso_sort_keys = len_iso_sort_keyBase; - pBCN->ftcn[TAUT_YES].iso_exchg_atnos = Ct_BaseIso->iso_exchg_atnos; Ct_BaseIso->iso_exchg_atnos = NULL; - pBCN->ftcn[TAUT_YES].iso_exchg_atnosOrig = iso_exchg_atnos; iso_exchg_atnos = NULL; - - CleanIsoSortKeys( pBCN->ftcn[TAUT_YES].iso_sort_keys, pBCN->ftcn[TAUT_YES].len_iso_sort_keys ); - CleanIsoSortKeys( pBCN->ftcn[TAUT_YES].iso_sort_keysOrig, pBCN->ftcn[TAUT_YES].len_iso_sort_keys ); - } - - } /* tautomeric results */ - - if ( bReqNonTaut ) { /* non-tautomeric results */ - /* TAUT_NON if tautomeric + non-tautomeric or special non-taut request - TAUT_YES if the structure happened to be non-tautomeric while user requested tautomeric processing - In both cases the correct index is iOther. TAUT_NON replaced with iOther 4-2-2004 */ - - if ( !bReqTaut ) { - /* rearrange the results for a non-tautomeric structure */ - nSymmRankFixH = nSymmRankNoTautH; nSymmRankNoTautH = NULL; - nCanonRankFixH = nCanonRankNoTautH; nCanonRankNoTautH = NULL; - nAtomNumberCanonFixH = nAtomNumberCanonNoTautH; nAtomNumberCanonNoTautH = NULL; - Ct_FixH = Ct_NoTautH; Ct_NoTautH = NULL; - /* isotopic canonicalization */ - nSymmRankFixHIso = nSymmRankNoTautHIso; nSymmRankNoTautHIso = NULL; - nCanonRankFixHIso = nCanonRankNoTautHIso; nCanonRankNoTautHIso = NULL; - nAtomNumberCanonFixHIso = nAtomNumberCanonNoTautHIso; nAtomNumberCanonNoTautHIso = NULL; - Ct_FixHIso = Ct_NoTautHIso; Ct_NoTautHIso = NULL; - - if ( iOther == TAUT_YES && pBCN->ftcn[TAUT_NON].NeighList && !pBCN->ftcn[TAUT_YES].NeighList ) { - /* here only non-taut results go to pBCN->ftcn[TAUT_YES] - Since non-taut NeighList is always in pBCN->ftcn[TAUT_NON].NeighList, move it to - pBCN->ftcn[TAUT_YES].NeighList. 2004-04-02. - */ - pBCN->ftcn[TAUT_YES].NeighList = pBCN->ftcn[TAUT_NON].NeighList; - pBCN->ftcn[TAUT_NON].NeighList = NULL; - } - } - pBCN->ftcn[iOther].num_at_tg = num_atoms; - pBCN->ftcn[iOther].num_atoms = num_atoms; - - pBCN->ftcn[iOther].LinearCt = Ct_FixH->Ctbl; Ct_FixH->Ctbl = NULL; - pBCN->ftcn[iOther].nLenLinearCtAtOnly = s[iOther].nLenCTAtOnly; - pBCN->ftcn[iOther].nMaxLenLinearCt = s[iOther].nLenCTAtOnly+1; - pBCN->ftcn[iOther].nLenLinearCt = s[iOther].nLenCTAtOnly; - - pBCN->ftcn[iOther].PartitionCt.Rank = nCanonRankFixH; nCanonRankFixH = NULL; - pBCN->ftcn[iOther].PartitionCt.AtNumber = nAtomNumberCanonFixH; nAtomNumberCanonFixH = NULL; - pBCN->ftcn[iOther].nSymmRankCt = nSymmRankFixH; nSymmRankFixH = NULL; - - pBCN->ftcn[iOther].nNumHOrig = numHNoTautH; numHNoTautH = NULL; - pBCN->ftcn[iOther].nNumH = Ct_FixH->NumH; Ct_FixH->NumH = NULL; - pBCN->ftcn[iOther].nLenNumH = inchi_min(maxlenNumHNoTautH,Ct_FixH->maxlenNumH); - - /* fixed H structure: exists only if the structure is tautomeric */ - pBCN->ftcn[iOther].nNumHOrigFixH = NumHfixed; NumHfixed = NULL; - pBCN->ftcn[iOther].nNumHFixH = Ct_FixH->NumHfixed; Ct_FixH->NumHfixed = NULL; - pBCN->ftcn[iOther].nLenNumHFixH = num_atoms; - pBCN->ftcn[iOther].nCanonFlags |= pCD[iOther].nCanonFlags; - - /* original H */ - CleanNumH( pBCN->ftcn[iOther].nNumHOrig, pBCN->ftcn[iOther].nLenNumH ); - CleanNumH( pBCN->ftcn[iOther].nNumHOrigFixH, pBCN->ftcn[iOther].nLenNumH ); - /* canonical H positions */ - CleanNumH( pBCN->ftcn[iOther].nNumH, pBCN->ftcn[iOther].nLenNumH ); - CleanNumH( pBCN->ftcn[iOther].nNumHFixH, pBCN->ftcn[iOther].nLenNumH ); - /* connection table */ - CleanCt( pBCN->ftcn[iOther].LinearCt, pBCN->ftcn[iOther].nLenLinearCt ); - - /* isotopic canonicalization */ - if ( Ct_FixHIso ) { - pBCN->ftcn[iOther].PartitionCtIso.Rank = nCanonRankFixHIso; nCanonRankFixHIso = NULL; - pBCN->ftcn[iOther].PartitionCtIso.AtNumber = nAtomNumberCanonFixHIso; nAtomNumberCanonFixHIso = NULL; - pBCN->ftcn[iOther].nSymmRankCtIso = nSymmRankFixHIso; nSymmRankFixHIso = NULL; - pBCN->ftcn[iOther].iso_sort_keys = Ct_FixHIso->iso_sort_key; Ct_FixHIso->iso_sort_key = NULL; - pBCN->ftcn[iOther].iso_sort_keysOrig = iso_sort_key_NoTautH; iso_sort_key_NoTautH = NULL; - pBCN->ftcn[iOther].len_iso_sort_keys = len_iso_sort_key_NoTautH; -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - MergeCleanIsoSortKeys( pBCN->ftcn[iOther].iso_sort_keys, Ct_FixHIso->iso_sort_key_Hfixed, pBCN->ftcn[iOther].len_iso_sort_keys ); - MergeCleanIsoSortKeys( pBCN->ftcn[iOther].iso_sort_keysOrig, iso_sort_key_Hfixed, pBCN->ftcn[iOther].len_iso_sort_keys ); -#else - CleanIsoSortKeys( pBCN->ftcn[iOther].iso_sort_keys, pBCN->ftcn[iOther].len_iso_sort_keys ); - CleanIsoSortKeys( pBCN->ftcn[iOther].iso_sort_keysOrig, pBCN->ftcn[iOther].len_iso_sort_keys ); -#endif - } - - } /* non-tautomeric results */ - goto exit_function; - -exit_error_alloc: - ret = CT_OUT_OF_RAM; - goto exit_function; - -exit_error: - if ( !RETURNED_ERROR(ret) ) { - ret = CT_CANON_ERR; - } - goto exit_function; - -exit_function: - -#define FREE_CONTABLE(X) if (X) {CTableFree(X);inchi_free(X);} -#define FREE_ARRAY(X) if (X) inchi_free(X); - - FreeNeighList( NeighList[TAUT_NON] ); - FreeNeighList( NeighList[TAUT_YES] ); - - FREE_CONTABLE( Ct_NoH ) - FREE_CONTABLE( Ct_NoTautH ) - FREE_CONTABLE( Ct_Base ) - FREE_CONTABLE( Ct_FixH ) - FREE_CONTABLE( Ct_Temp ) - /* isotopic canonicalization */ - FREE_CONTABLE( Ct_NoTautHIso ) - FREE_CONTABLE( Ct_BaseIso ) - FREE_CONTABLE( Ct_FixHIso ) - - /* free the first two pointers from pBCN->pRankStack */ - FREE_ARRAY( nRank ) - FREE_ARRAY( nAtomNumber ) - - if ( pBCN->pRankStack ) { - pBCN->pRankStack[0] = - pBCN->pRankStack[1] = NULL; - } - -#if ( USE_AUX_RANKING == 1 ) - FREE_ARRAY( nRankAux ) - FREE_ARRAY( nAtomNumberAux ) - FREE_ARRAY( pAtomInvariantAux ) -#endif - - FREE_ARRAY( pAtomInvariant ) - - FREE_ARRAY( nCanonRankNoH ) - FREE_ARRAY( nAtomNumberCanonNoH ) - FREE_ARRAY( nSymmRankNoH ) - - FREE_ARRAY( nSymmRankNoTautH ) - FREE_ARRAY( nCanonRankNoTautH ) - FREE_ARRAY( nAtomNumberCanonNoTautH ) - FREE_ARRAY( numHNoTautH ) - - FREE_ARRAY( nSymmRankBase ) - FREE_ARRAY( nCanonRankBase ) - FREE_ARRAY( nAtomNumberCanonBase ) - FREE_ARRAY( numH ) - - FREE_ARRAY( nSymmRankFixH ) - FREE_ARRAY( nCanonRankFixH ) - FREE_ARRAY( nAtomNumberCanonFixH ) - FREE_ARRAY( NumHfixed ) - - /* isotopic canonicalization */ - - FREE_ARRAY( nSymmRankNoTautHIso ) - FREE_ARRAY( nCanonRankNoTautHIso ) - FREE_ARRAY( nAtomNumberCanonNoTautHIso ) - FREE_ARRAY( iso_sort_key_NoTautH ) - - FREE_ARRAY( nSymmRankBaseIso ) - FREE_ARRAY( nCanonRankBaseIso ) - FREE_ARRAY( nAtomNumberCanonBaseIso ) - FREE_ARRAY( iso_sort_keyBase ) - FREE_ARRAY( iso_exchg_atnos ) - - FREE_ARRAY( nSymmRankFixHIso ) - FREE_ARRAY( nCanonRankFixHIso ) - FREE_ARRAY( nAtomNumberCanonFixHIso ) -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - FREE_ARRAY( iso_sort_key_Hfixed ) -#endif - - FREE_ARRAY( nTempRank ) - -#undef FREE_CONTABLE -#undef FREE_ARRAY - - return ret; -} +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +/* #define CHECK_WIN32_VC_HEAP */ + +#include +#include +#include +#include + + +#include "mode.h" +#include "ichicano.h" + +#include "ichicomn.h" +#include "ichitime.h" + + +#define MAX_CELLS 32766 +#define MAX_NODES 32766 +#define MAX_SET_SIZE 32766 /*16384*/ +#define NORMALLY_ALLOWED_MAX_SET_SIZE 2048 +#define MAX_LAYERS 100 + +#define INFINITY 0x7FFF +#define EMPTY_CT 0 +#define EMPTY_H_NUMBER (INFINITY-1) +#define BASE_H_NUMBER ((INFINITY-1)/2) +#define EMPTY_ISO_SORT_KEY LONG_MAX + +#define SEPARATE_CANON_CALLS 0 + +/* #define INCHI_CANON_USE_HASH */ +#define INCHI_CANON_MIN + +/****************************************************************/ +#ifdef INCHI_CANON_USE_HASH +typedef unsigned long U_INT_32; +typedef unsigned char U_INT_08; +typedef U_INT_32 CtHash; +CtHash hash_mark_bit; +#endif + +/* -- moved to ichi_bns.h -- +typedef U_SHORT bitWord; +#define BIT_WORD_MASK ((bitWord)~0) +*/ + +/*bitWord mark_bit; */ /* highest bit in AT_NUMB */ +/*bitWord mask_bit; */ /* ~mark_bit */ + +static AT_NUMB rank_mark_bit; +static AT_NUMB rank_mask_bit; + + +typedef AT_NUMB Node; +typedef NEIGH_LIST Graph; +/* +typedef struct tagGraph +{ + int dummy; +} Graph; +*/ +typedef struct tagUnorderedPartition +{ + /* AT_NUMB *next; */ /* links */ + AT_NUMB *equ2; /* mcr */ +} UnorderedPartition; + +typedef struct tagCell +{ + int first; /* index of the first cell element in Partition::AtNumber[] */ + int next; /* next after the last index */ + int prev; /* position of the previously returned cell element */ +} Cell; + +#ifdef NEVER /* moved to ichi_bns.h */ +typedef struct tagNodeSet { + bitWord **bitword; + int num_set; /* number of sets */ + int len_set; /* number of bitWords in each set */ +} NodeSet; +#endif + +typedef struct tagTransposition +{ + AT_NUMB *nAtNumb; +} Transposition; + + +typedef struct tagCTable +{ + AT_RANK *Ctbl; /* connection table */ + /* Format-atoms: atom_rank[k] neigh_rank[k][1]...neigh_rank[k][n] + 1) atom_rank[k1] < atom_rank[k2] <=> k1 < k2 + where 2) atom_rank[k] > neigh_rank[k][i], i=1..n + 3) neigh_rank[k][i] < neigh_rank[k][j] <=> i < j + + Format-tgroup: tgroup_rank[k] endpoint_rank[k][1]...endpoint_rank[k][n] + where 1) tgroup_rank[k1] < tgroup_rank[k2] <=> k1 < k2 + 2) endpoint_rank[k][i] < endpoint_rank[k][j] <=> i < j + + Note: tgroup_rank[k] > endpoint_rank[k][j] for all j by construction + */ + + int lenCt; /* used length */ + int nLenCTAtOnly; /* to split Ctnl comparison in case of bDigraph != 0 */ + int maxlenCt; /* allocated length of Ctbl */ + int maxPos; /* allocated length of nextCtblPos */ + int maxVert; /* max number of vertices to separate atoms from taut groups */ + int lenPos; /* first unused element of nextCtblPos */ + AT_RANK *nextAtRank; /* rank (k value) after the last node of the Ctbl portion*/ + AT_NUMB *nextCtblPos; /* first unused element of Ctbl */ + + /* hydrogen atoms fixed in tautomeric representation: + compare before diff sign inversion: (+) <=> Ct1->() > Ct2->() */ + NUM_H *NumH; + int lenNumH; /* used length */ + int maxlenNumH; /* n + T_NUM_NO_ISOTOPIC*(n_tg-n) + 1 */ + + /* hydrogen atoms fixed in non-tautomeric representation only: + compare before diff sign inversion: (+) <=> Ct1->() > Ct2->() */ + NUM_H *NumHfixed; + /*int lenNumHfixed; */ /* used length */ + /*int maxlenNumHfixed; */ /* max length = n+1 */ + + /* isotopic atoms (without tautomeric H) and isotopic tautomeric groups */ + /* note: AT_ISO_SORT_KEY and T_GROUP_ISOWT are identical types: long */ + AT_ISO_SORT_KEY *iso_sort_key; + int len_iso_sort_key; /* used length */ + int maxlen_iso_sort_key; /* max length = n_tg+1 */ + + S_CHAR *iso_exchg_atnos; + int len_iso_exchg_atnos; + int maxlen_iso_exchg_atnos; + + /* isotopic hydrogen atoms fixed in non-tautomeric representation only */ +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + AT_ISO_SORT_KEY *iso_sort_key_Hfixed; + int len_iso_sort_key_Hfixed; /* used length */ + int maxlen_iso_sort_key_Hfixed; /* max length = n+1 */ +#endif + +#ifdef INCHI_CANON_USE_HASH + CtHash *hash; +#endif +} ConTable; + + + +/**************************************************************/ +typedef struct tagkLeast +{ + int k; + int i; +} kLeast; + + + +/*************local prototypes **********************************/ +struct tagINCHI_CLOCK; +int CanonGraph( struct tagINCHI_CLOCK *ic, + CANON_GLOBALS *pCG, + int n, + int n_tg, + int n_max, + int bDigraph, + Graph *G, + Partition pi[], + AT_RANK *nSymmRank, + AT_RANK *nCanonRank, + AT_NUMB *nAtomNumberCanon, + CANON_DATA *pCD, + CANON_COUNTS *pCC, + ConTable **pp_zb_rho_inp, + ConTable **pp_zb_rho_out, + int LargeMolecules ); + +void CtPartFill( Graph *G, CANON_DATA *pCD, Partition *p, + ConTable *Ct, int k, int n, int n_tg ); + +void CtPartClear( ConTable *Ct, int k ); + +void CtPartInfinity( ConTable *Ct, S_CHAR *cmp, int k ); + +int CtPartCompare( ConTable *Ct1, ConTable *Ct2, S_CHAR *cmp, + kLeast *kLeastForLayer, int k, int bOnlyCommon, int bSplitTautCompare ); + +int CtFullCompare( ConTable *Ct1, ConTable *Ct2, int bOnlyCommon, int bSplitTautCompare ); + +void CtPartCopy( ConTable *Ct1 /* to */, ConTable *Ct2 /* from */, int k ); + +void CtFullCopy( ConTable *Ct1, ConTable *Ct2 ); + +int CtFullCompareLayers( kLeast *kLeastForLayer ); + +int CtCompareLayersGetFirstDiff( kLeast *kLeast_rho, int nOneAdditionalLayer, + int *L_rho, int *I_rho, int *k_rho ); + +int CtPartCompareLayers( kLeast *kLeast_rho, int L_rho_fix_prev, int nOneAdditionalLayer ); + +void UpdateCompareLayers( kLeast kLeastForLayer[], int hzz ); + +int GetOneAdditionalLayer( CANON_DATA *pCD, ConTable *pzb_rho_fix ); + +void CleanNumH( NUM_H *NumH, int len ); + +int CleanCt( AT_RANK *Ct, int len ); + +void CleanIsoSortKeys( AT_ISO_SORT_KEY * isk, int len ); + +void MergeCleanIsoSortKeys( AT_ISO_SORT_KEY * isk1, AT_ISO_SORT_KEY * isk2, int len ); + +int UnorderedPartitionJoin( UnorderedPartition *p1, UnorderedPartition *p2, int n ); + +Node GetUnorderedPartitionMcrNode( UnorderedPartition *p1, Node v ); + +int nJoin2Mcrs2( AT_RANK *nEqArray, AT_RANK n1, AT_RANK n2 ); + +AT_RANK nGetMcr2( AT_RANK *nEqArray, AT_RANK n ); + +int AllNodesAreInSet( NodeSet *cur_nodes, int lcur_nodes, NodeSet *set, int lset ); + +void NodeSetFromVertices( CANON_GLOBALS *pCG, NodeSet *cur_nodes, int l, Node *v, int num_v); + +void CellMakeEmpty( Cell *baseW, int k ); + +Node CellGetMinNode( Partition *p, Cell *W, Node v, CANON_DATA *pCD ); + +int CellGetNumberOfNodes( Partition *p, Cell *W ); + +int CellIntersectWithSet( struct tagCANON_GLOBALS *pCG, Partition *p, Cell *W, NodeSet *Mcr, int l ); + +int PartitionColorVertex( CANON_GLOBALS *pCG, Graph *G, Partition *p, Node v, int n, int n_tg, int n_max, int bDigraph, int nNumPrevRanks ); + +void PartitionCopy( Partition *To, Partition *From, int n ); + +int PartitionSatisfiesLemma_2_25( Partition *p, int n ); + +void PartitionGetTransposition( Partition *pFrom, Partition *pTo, int n, Transposition *gamma ); + +void PartitionGetMcrAndFixSet( CANON_GLOBALS *pCG, + Partition *p, + NodeSet *Mcr, + NodeSet *Fix, + int n, + int l ); + +int PartitionGetFirstCell( Partition *p, Cell *baseW, int k, int n ); + +int PartitionIsDiscrete( Partition *p, int n); + +void PartitionFree( Partition *p ); + +int PartitionCreate( Partition *p, int n); + +void UnorderedPartitionMakeDiscrete( UnorderedPartition *p, int n); + +void UnorderedPartitionFree( UnorderedPartition *p ); + +int UnorderedPartitionCreate( UnorderedPartition *p, int n ); + +void CTableFree( ConTable *Ct ); + +int CTableCreate( ConTable *Ct, int n, CANON_DATA *pCD ); + +void TranspositionFree( Transposition *p ); + +int TranspositionCreate( Transposition *p, int n ); + +void TranspositionGetMcrAndFixSetAndUnorderedPartition( struct tagCANON_GLOBALS *pCG, Transposition *gamma, NodeSet *McrSet, NodeSet *FixSet, int n, int l, UnorderedPartition *p ); + +void insertions_sort_NeighList_AT_NUMBERS2( NEIGH_LIST base, AT_RANK *nRank, AT_RANK max_rj ); + +int WriteGraph( Graph *G, int n, int gnum, char *fname, char *fmode ); + +int SetInitialRanks2( int num_atoms, + ATOM_INVARIANT2* pAtomInvariant2, + AT_RANK *nNewRank, + AT_RANK *nAtomNumber, + CANON_GLOBALS *pCG ); + +void FillOutAtomInvariant2( sp_ATOM* at, int num_atoms, int num_at_tg, ATOM_INVARIANT2* pAtomInvariant, + int bIgnoreIsotopic, int bHydrogensInRanks, int bHydrogensFixedInRanks, + int bDigraph, int bTautGroupsOnly, T_GROUP_INFO *t_group_info ); + +int GetCanonRanking2( int num_atoms, int num_at_tg, int num_max, int bDigraph, sp_ATOM* at, + AT_RANK **pRankStack, int nNumPrevRanks, + AT_RANK *nSymmRank, AT_RANK *nCanonRank, + NEIGH_LIST *NeighList, AT_RANK *nTempRank, + CANON_STAT* pCS ); + + +#if ( SEPARATE_CANON_CALLS == 1 ) +/* for profiling purposes */ + +int CanonGraph01( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], + AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, + CANON_DATA *pCD, CANON_COUNTS *pCC, + ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out, int LargeMolecules ) +{ + return + CanonGraph( n, n_tg, n_max, bDigraph, G, pi , + nSymmRank, nCanonRank, nAtomNumberCanon, + pCD, pCC, + pp_zb_rho_inp, pp_zb_rho_out, LargeMolecules ); +} +int CanonGraph02( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], + AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, + CANON_DATA *pCD, CANON_COUNTS *pCC, + ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out, int LargeMolecules ) +{ + return + CanonGraph( n, n_tg, n_max, bDigraph, G, pi , + nSymmRank, nCanonRank, nAtomNumberCanon, + pCD, pCC, + pp_zb_rho_inp, pp_zb_rho_out ); +} +int CanonGraph03( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], + AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, + CANON_DATA *pCD, CANON_COUNTS *pCC, + ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out, int LargeMolecules ) +{ + return + CanonGraph( n, n_tg, n_max, bDigraph, G, pi , + nSymmRank, nCanonRank, nAtomNumberCanon, + pCD, pCC, + pp_zb_rho_inp, pp_zb_rho_out ); +} +int CanonGraph04( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], + AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, + CANON_DATA *pCD, CANON_COUNTS *pCC, + ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out, int LargeMolecules ) +{ + return + CanonGraph( n, n_tg, n_max, bDigraph, G, pi , + nSymmRank, nCanonRank, nAtomNumberCanon, + pCD, pCC, + pp_zb_rho_inp, pp_zb_rho_out ); +} +int CanonGraph05( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], + AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, + CANON_DATA *pCD, CANON_COUNTS *pCC, + ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out, int LargeMolecules ) +{ + return + CanonGraph( n, n_tg, n_max, bDigraph, G, pi , + nSymmRank, nCanonRank, nAtomNumberCanon, + pCD, pCC, + pp_zb_rho_inp, pp_zb_rho_out ); +} +int CanonGraph06( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], + AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, + CANON_DATA *pCD, CANON_COUNTS *pCC, + ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out, int LargeMolecules ) +{ + return + CanonGraph( n, n_tg, n_max, bDigraph, G, pi , + nSymmRank, nCanonRank, nAtomNumberCanon, + pCD, pCC, + pp_zb_rho_inp, pp_zb_rho_out ); +} +int CanonGraph07( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], + AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, + CANON_DATA *pCD, CANON_COUNTS *pCC, + ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out, int LargeMolecules ) +{ + return + CanonGraph( n, n_tg, n_max, bDigraph, G, pi , + nSymmRank, nCanonRank, nAtomNumberCanon, + pCD, pCC, + pp_zb_rho_inp, pp_zb_rho_out ); +} +int CanonGraph08( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], + AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, + CANON_DATA *pCD, CANON_COUNTS *pCC, + ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out, int LargeMolecules ) +{ + return + CanonGraph( n, n_tg, n_max, bDigraph, G, pi , + nSymmRank, nCanonRank, nAtomNumberCanon, + pCD, pCC, + pp_zb_rho_inp, pp_zb_rho_out ); +} +int CanonGraph09( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], + AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, + CANON_DATA *pCD, CANON_COUNTS *pCC, + ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out, int LargeMolecules ) +{ + return + CanonGraph( n, n_tg, n_max, bDigraph, G, pi , + nSymmRank, nCanonRank, nAtomNumberCanon, + pCD, pCC, + pp_zb_rho_inp, pp_zb_rho_out ); +} +int CanonGraph10( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], + AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, + CANON_DATA *pCD, CANON_COUNTS *pCC, + ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out, int LargeMolecules ) +{ + return + CanonGraph( n, n_tg, n_max, bDigraph, G, pi , + nSymmRank, nCanonRank, nAtomNumberCanon, + pCD, pCC, + pp_zb_rho_inp, pp_zb_rho_out ); +} +int CanonGraph11( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], + AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, + CANON_DATA *pCD, CANON_COUNTS *pCC, + ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out, int LargeMolecules ) +{ + return + CanonGraph( n, n_tg, n_max, bDigraph, G, pi , + nSymmRank, nCanonRank, nAtomNumberCanon, + pCD, pCC, + pp_zb_rho_inp, pp_zb_rho_out ); +} +int CanonGraph12( int n, int n_tg, int n_max, int bDigraph, Graph *G, Partition pi[], + AT_RANK *nSymmRank, AT_RANK *nCanonRank, AT_NUMB *nAtomNumberCanon, + CANON_DATA *pCD, CANON_COUNTS *pCC, + ConTable **pp_zb_rho_inp, ConTable **pp_zb_rho_out, int LargeMolecules ) +{ + return + CanonGraph( n, n_tg, n_max, bDigraph, G, pi , + nSymmRank, nCanonRank, nAtomNumberCanon, + pCD, pCC, + pp_zb_rho_inp, pp_zb_rho_out, LargeMolecules ); +} +#else + +#define CanonGraph01 CanonGraph +#define CanonGraph02 CanonGraph +#define CanonGraph03 CanonGraph +#define CanonGraph04 CanonGraph +#define CanonGraph05 CanonGraph +#define CanonGraph06 CanonGraph +#define CanonGraph07 CanonGraph +#define CanonGraph08 CanonGraph +#define CanonGraph09 CanonGraph +#define CanonGraph10 CanonGraph +#define CanonGraph11 CanonGraph +#define CanonGraph12 CanonGraph + +#endif + + +#ifdef INCHI_CANON_USE_HASH +/****************************************************************/ +static call_fill_crc32_data = 1; +static const U_INT_32 crc32_data[256]; + +void fill_crc32_data() +{ + U_INT_32 c; + int n, k; + + for (n = 0; n < 256; n++) + { + c = (U_INT_32)n; + for (k = 0; k < 8; k++) + c = c & 1 ? 0xEDB88320L ^ (c >> 1) : c >> 1; + crc32_data[n] = c; + } + call_fill_crc32_data = 0; +} + + +/****************************************************************/ +unsigned long add2crc32( unsigned long crc32, AT_NUMB n ) +{ + U_INT_08 chr; + if (call_fill_crc32_data) + { + fill_crc32_data(); + } + chr = n % 128; + crc32 = crc32_data[((int)crc32 ^ (int)chr) & 0xff] ^ (crc32 >> 8); + chr = n / 128; + crc32 = crc32_data[((int)crc32 ^ (int)chr) & 0xff] ^ (crc32 >> 8); + return crc32; +} +#endif + + + +/****************************************************************/ +int TranspositionCreate( Transposition *p, int n ) +{ + p->nAtNumb = (AT_NUMB*)inchi_calloc( n, sizeof(p->nAtNumb[0]) ); + if ( p->nAtNumb ) + { + return 1; + } + return 0; +} + + +/****************************************************************/ +void TranspositionFree( Transposition *p ) +{ + if ( p && p->nAtNumb ) + { + inchi_free( p->nAtNumb ); + p->nAtNumb = NULL; + } +} + + +/****************************************************************/ +int NodeSetCreate( struct tagCANON_GLOBALS *pCG, NodeSet *pSet, int n, int L ) +{ + int i, len; + + len = (n+ pCG->m_num_bit - 1)/pCG->m_num_bit; + + pSet->bitword = (bitWord**)inchi_calloc( L, sizeof(pSet->bitword[0])); + + if ( !pSet->bitword ) { + return 0; + } + pSet->bitword[0] = (bitWord*)inchi_calloc( len*L, sizeof(pSet->bitword[0][0])); + if ( !pSet->bitword[0] ) + { + /* cleanup */ + inchi_free( pSet->bitword ); + pSet->bitword = NULL; + return 0; /* failed */ + } + for ( i = 1; i < L; i ++ ) + { + pSet->bitword[i] = pSet->bitword[i-1]+len; + } + + pSet->len_set = len; + pSet->num_set = L; + + return 1; +} + + +/****************************************************************/ +void NodeSetFree( struct tagCANON_GLOBALS *pCG, NodeSet *pSet ) +{ + if ( pSet && pSet->bitword ) + { + if ( pSet->bitword[0] ) + { + inchi_free( pSet->bitword[0] ); + } + inchi_free( pSet->bitword ); + pSet->bitword = NULL; + } +} + + +/****************************************************************/ +int CTableCreate( ConTable *Ct, int n, CANON_DATA *pCD ) +{ + int maxlenCt = pCD->nMaxLenLinearCT + 1; /* add one element for CtPartInfinity() */ + int maxlenNumH = pCD->NumH? (pCD->maxlenNumH + 1) : 0; + int maxlenNumHfixed = pCD->NumHfixed? (pCD->maxlenNumHfixed + 1) : 0; + int maxlenIso = pCD->maxlen_iso_sort_key? (pCD->maxlen_iso_sort_key+1) : 0; + int maxlenIsoExchg = pCD->iso_exchg_atnos? (pCD->maxlen_iso_exchg_atnos+1) : 0; + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + int maxlenIsoHfixed = pCD->maxlen_iso_sort_key_Hfixed? (pCD->maxlen_iso_sort_key_Hfixed+1):0; +#endif + + memset( Ct, 0, sizeof(Ct[0]) ); + + Ct->maxVert = n; + + n++; + + Ct->Ctbl = (AT_RANK*) inchi_calloc( maxlenCt, sizeof(Ct->Ctbl[0]) ); + Ct->nextCtblPos = (AT_NUMB*) inchi_calloc( n, sizeof(Ct->nextCtblPos[0]) ); + Ct->nextAtRank = (AT_RANK*) inchi_calloc( n, sizeof(Ct->nextAtRank[0]) ); + if ( maxlenNumH ) { + Ct->NumH = (NUM_H *) inchi_calloc( maxlenNumH, sizeof(Ct->NumH[0])); + } + if ( maxlenNumHfixed ) { + Ct->NumHfixed = (NUM_H *) inchi_calloc( maxlenNumHfixed, sizeof(Ct->NumH[0])); + } + if ( maxlenIso ) { + Ct->iso_sort_key = (AT_ISO_SORT_KEY *)inchi_calloc( maxlenIso, sizeof(Ct->iso_sort_key[0])); + } + if ( maxlenIsoExchg ) { + Ct->iso_exchg_atnos = (S_CHAR *)inchi_calloc( maxlenIsoExchg, sizeof(Ct->iso_exchg_atnos[0])); + } +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + if ( maxlenIsoHfixed ) { + Ct->iso_sort_key_Hfixed = (AT_ISO_SORT_KEY *)inchi_calloc( maxlenIsoHfixed, sizeof(Ct->iso_sort_key_Hfixed[0])); + } +#endif +#ifdef INCHI_CANON_USE_HASH + Ct->hash = (CtHash*) inchi_calloc( n, sizeof(Ct->hash[0]) ); +#endif + + Ct->lenCt = 0; + Ct->nLenCTAtOnly = pCD->nLenCTAtOnly; + Ct->maxlenCt = maxlenCt; + Ct->lenNumH = 0; + Ct->maxlenNumH = maxlenNumH; + Ct->len_iso_sort_key = 0; + Ct->maxlen_iso_sort_key = maxlenIso; + Ct->len_iso_exchg_atnos = 0; + Ct->maxlen_iso_exchg_atnos = maxlenIso; + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + Ct->len_iso_sort_key_Hfixed = 0; + Ct->maxlen_iso_sort_key_Hfixed = maxlenIsoHfixed; +#endif + + Ct->maxPos = n; + Ct->lenPos = 0; + Ct->nextAtRank[0] = 0; + Ct->nextCtblPos[0] = 0; + + if ( Ct->Ctbl && Ct->nextCtblPos && + (!maxlenNumH || Ct->NumH) && + (!maxlenNumHfixed || Ct->NumHfixed ) ) + { + return 1; + } + + return 0; +} + + +/****************************************************************/ +void CTableFree( ConTable *Ct ) +{ + if ( Ct ) + { + if ( Ct->Ctbl ) + inchi_free( Ct->Ctbl ); + if ( Ct->nextCtblPos ) + inchi_free( Ct->nextCtblPos ); + if ( Ct->nextAtRank ) + inchi_free( Ct->nextAtRank ); + if ( Ct->NumH ) + inchi_free( Ct->NumH ); + if ( Ct->NumHfixed ) + inchi_free( Ct->NumHfixed ); + if ( Ct->iso_sort_key ) + inchi_free( Ct->iso_sort_key ); + if ( Ct->iso_exchg_atnos ) + inchi_free( Ct->iso_exchg_atnos ); + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + if ( Ct->iso_sort_key_Hfixed ) + inchi_free( Ct->iso_sort_key_Hfixed ); +#endif + +#ifdef INCHI_CANON_USE_HASH + if ( Ct->hash ) + inchi_free( Ct->hash ); +#endif + + memset( Ct, 0, sizeof( Ct[0] ) ); + } +} + + +/****************************************************************/ +int UnorderedPartitionCreate( UnorderedPartition *p, int n ) +{ + p->equ2 = (AT_NUMB*)inchi_calloc( n, sizeof(p->equ2[0])); + /* p->next = (AT_NUMB*)inchi_calloc( n, sizeof(p->next[0])); */ + if ( p->equ2 /*&& p->next*/ ) + return 1; + + return 0; +} + + +/****************************************************************/ +void UnorderedPartitionFree( UnorderedPartition *p ) +{ + if (p->equ2) inchi_free( p->equ2); + /* if (p->next) inchi_free( p->next); */ + p->equ2 = NULL; + /* p->next = NULL; */ +} + + +/****************************************************************/ +void UnorderedPartitionMakeDiscrete( UnorderedPartition *p, int n) +{ + int i; + for ( i = 0; i < n; i ++ ) + { + p->equ2[i] = (AT_NUMB)i; + /* p->next[i] = INFINITY; */ + } + INCHI_HEAPCHK +} + + +/****************************************************************/ +int PartitionCreate( Partition *p, int n) +{ + p->AtNumber = (AT_NUMB*)inchi_calloc( n, sizeof(p->AtNumber[0])); + p->Rank = (AT_RANK*)inchi_calloc( n, sizeof(p->Rank[0])); + if ( p->AtNumber && p->Rank ) + { + return 1; + } + return 0; +} + + +/****************************************************************/ +void PartitionFree( Partition *p ) +{ + if ( p ) { + if ( p->AtNumber ) + { + inchi_free( p->AtNumber ); + p->AtNumber = NULL; + } + if ( p->Rank ) + { + inchi_free( p->Rank ); + p->Rank = NULL; + } + } +} + + +/****************************************************************/ +int PartitionIsDiscrete( Partition *p, int n) +{ + int i; + AT_RANK r; + for ( i = 0, r = 1; i < n; i ++, r ++ ) { + if ( r != (rank_mask_bit & p->Rank[p->AtNumber[i]]) ) { + INCHI_HEAPCHK + return 0; + } + } + + INCHI_HEAPCHK + + return 1; +} + + +/****************************************************************/ +int PartitionGetFirstCell( Partition *p, Cell *baseW, int k, int n ) +{ + int i; + AT_RANK r; + Cell *W = baseW+k-1; + + i = (k > 1)? baseW[k-2].first+1 + : 0; + + if ( i < n ) + { + /* bypass single vertex cells */ + for ( r = (AT_RANK)(i+1); i < n && r == (rank_mask_bit & p->Rank[(int)p->AtNumber[i]]); i ++, r++ ) + ; + } + if ( i < n ) + { + W->first = i; + for ( r = (rank_mask_bit & p->Rank[(int)p->AtNumber[i]]), i++ ; + i < n && r == (rank_mask_bit & p->Rank[(int)p->AtNumber[i]]); + i ++ ) + ; + W->next = i; + + INCHI_HEAPCHK + + return (W->next - W->first); + } + + W->first = INFINITY; + W->next = 0; + + INCHI_HEAPCHK + + return 0; +} + + +/****************************************************************/ +void CellMakeEmpty( Cell *baseW, int k ) +{ + k --; + baseW[k].first = INFINITY; + baseW[k].next = 0; + baseW[k].prev = -1; + + INCHI_HEAPCHK +} + + +/****************************************************************/ +void NodeSetFromVertices( CANON_GLOBALS *pCG, NodeSet *cur_nodes, int l, Node *v, int num_v) +{ + bitWord *Bits = cur_nodes->bitword[l-1]; + int len = cur_nodes->len_set*sizeof(bitWord); + int i, j; + + memset( Bits, 0, len ); + + for ( i = 0; i < num_v; i ++ ) + { + j = (int)v[i]-1; + Bits[ j / pCG->m_num_bit ] |= pCG->m_bBit[ j % pCG->m_num_bit ]; + } + + INCHI_HEAPCHK +} + + +/****************************************************************/ +int AllNodesAreInSet( NodeSet *cur_nodes, int lcur_nodes, NodeSet *set, int lset ) +{ + int i; + int n = cur_nodes->len_set; + + bitWord *BitsNode = cur_nodes->bitword[lcur_nodes-1]; + bitWord *BitsSet = set->bitword[lset-1]; + + /* find any BitsNode[i] bit not in BitsSet[i] */ + for ( i = 0; i < n; i ++ ) + { + if ( BitsNode[i] & ~BitsSet[i] ) + { + + INCHI_HEAPCHK + + return 0; + } + } + + INCHI_HEAPCHK + + return 1; +} + + +/****************************************************************/ +void PartitionGetMcrAndFixSet( CANON_GLOBALS *pCG, + Partition *p, + NodeSet *Mcr, + NodeSet *Fix, + int n, + int l ) +{ + int i, j1, j2; + AT_RANK r, r1; + bitWord *McrBits = Mcr->bitword[l-1]; + bitWord *FixBits = Fix->bitword[l-1]; + int len = Mcr->len_set*sizeof(bitWord); + + memset( McrBits, 0, len ); + memset( FixBits, 0, len ); + for ( i = 0, r = 1; i < n; i ++, r ++ ) + { + if ( r == (r1=(rank_mask_bit&p->Rank[j1=(int)p->AtNumber[i]])) ) + { + FixBits[j1 / pCG->m_num_bit] |= pCG->m_bBit[j1 % pCG->m_num_bit]; + McrBits[j1 / pCG->m_num_bit] |= pCG->m_bBit[j1 % pCG->m_num_bit]; + } + else + { + for ( r = r1; i+1 < n && r == (rank_mask_bit&p->Rank[j2=(int)p->AtNumber[i+1]]); i ++ ) + { + if ( j1 > j2 ) + { + j1 = j2; + } + } + McrBits[j1 / pCG->m_num_bit] |= pCG->m_bBit[j1 % pCG->m_num_bit]; + } + } + + INCHI_HEAPCHK +} + + +/************* used in ichi_bns.c ********************************/ +void NodeSetFromRadEndpoints( CANON_GLOBALS *pCG, + NodeSet *cur_nodes, + int k, /*Node *v*/ + Vertex RadEndpoints[], + int num_v) +{ + bitWord *Bits = cur_nodes->bitword[k]; + int len = cur_nodes->len_set*sizeof(bitWord); + int i, j; + + memset( Bits, 0, len ); + + for ( i = 1; i < num_v; i += 2 ) + { + j = (int)RadEndpoints[i]; + Bits[ j / pCG->m_num_bit ] |= pCG->m_bBit[ j % pCG->m_num_bit ]; + } +} + + +/************* used in ichi_bns.c ********************************/ +void RemoveFromNodeSet( CANON_GLOBALS *pCG, + NodeSet *cur_nodes, int k, Vertex v[], int num_v) +{ + if ( cur_nodes->bitword ) + { + bitWord *Bits = cur_nodes->bitword[k]; + /*int len = cur_nodes->len_set*sizeof(bitWord);*/ + int i, j; + + for ( i = 0; i < num_v; i ++ ) + { + j = (int) v[i]; + Bits[ j / pCG->m_num_bit ] &= ~pCG->m_bBit[ j % pCG->m_num_bit ]; + } + } +} + + +/************* used in ichi_bns.c ********************************/ +int DoNodeSetsIntersect( NodeSet *cur_nodes, int k1, int k2) +{ + if ( cur_nodes->bitword ) { + bitWord *Bits1 = cur_nodes->bitword[k1]; + bitWord *Bits2 = cur_nodes->bitword[k2]; + int len = cur_nodes->len_set; + int i; + + for ( i = 0; i < len; i ++ ) + { + if ( Bits1[i] & Bits2[i] ) + return 1; + } + } + + return 0; +} + + +/************* used in ichi_bns.c ********************************/ +int IsNodeSetEmpty( NodeSet *cur_nodes, int k) +{ + if ( cur_nodes->bitword ) + { + bitWord *Bits = cur_nodes->bitword[k]; + int len = cur_nodes->len_set; + int i; + + for ( i = 0; i < len; i ++ ) + { + if ( Bits[i] ) + return 0; + } + } + + return 1; +} + + +/************* used in ichi_bns.c ********************************/ +void AddNodeSet2ToNodeSet1( NodeSet *cur_nodes, int k1, int k2) +{ + if ( cur_nodes->bitword ) + { + bitWord *Bits1 = cur_nodes->bitword[k1]; + bitWord *Bits2 = cur_nodes->bitword[k2]; + int len = cur_nodes->len_set; + int i; + + for ( i = 0; i < len; i ++ ) + { + Bits1[i] |= Bits2[i]; + } + } +} + + +/************* used in ichi_bns.c ********************************/ +int AddNodesToRadEndpoints( CANON_GLOBALS *pCG, + NodeSet *cur_nodes, + int k, + Vertex RadEndpoints[], + Vertex vRad, + int nStart, int nLen ) +{ + int n = nStart; + if ( cur_nodes->bitword ) + { + bitWord *Bits = cur_nodes->bitword[k]; + int len = cur_nodes->len_set; + int i, j; + Vertex v; + + for ( i = 0, v = 0; i < len; i ++ ) + { + if ( Bits[i] ) + { + for ( j = 0; j < pCG->m_num_bit; j ++, v ++ ) + { + if ( Bits[i] & pCG->m_bBit[j] ) + { + if ( n >= nLen ) + { + return -1; /* overflow */ + } + RadEndpoints[n ++] = vRad; + RadEndpoints[n ++] = v; + } + } + } + else + { + v += pCG->m_num_bit; + } + } + } + + return n; +} + + +/****************************************************************/ +void PartitionGetTransposition( Partition *pFrom, Partition *pTo, int n, Transposition *gamma ) +{ + int i; + + for ( i = 0; i < n; i ++ ) + { + gamma->nAtNumb[(int)pFrom->AtNumber[i]] =pTo->AtNumber[i]; + } + + INCHI_HEAPCHK +} + + +/**************************************************************************************/ +/* Get minimal set (class) representative and partially compress the partitioning */ +/* mcr = minimal class representative. */ +AT_RANK nGetMcr2( AT_RANK *nEqArray, AT_RANK n ) +{ + AT_RANK n1, n2, mcr; /* recursive version is much shorter. */ + + INCHI_HEAPCHK + + n1=nEqArray[(int)n]; + + if ( n == n1 ) + { + return n; + } + + /* 1st pass: find mcr */ + while ( n1 != (n2=nEqArray[(int)n1])) + { + n1 = n2; + } + + /* 2nd pass: copy mcr to each element of the set starting from nEqArray[n] */ + mcr = n1; + n1 = n; + while ( /*n1*/ mcr != (n2=nEqArray[(int)n1]) ) + { + nEqArray[(int)n1]=mcr; + n1 = n2; + } + + INCHI_HEAPCHK + + return ( mcr ); +} + + +/**************************************************************************************/ +/* Join 2 sets (classes) that have members n1 and n2 */ +int nJoin2Mcrs2( AT_RANK *nEqArray, AT_RANK n1, AT_RANK n2 ) +{ + n1 = nGetMcr2( nEqArray, n1 ); + n2 = nGetMcr2( nEqArray, n2 ); + + if ( n1 < n2 ) + { + nEqArray[n2] = n1; + + INCHI_HEAPCHK + + return 1; /* a change has been made */ + } + + if ( n2 < n1 ) + { + nEqArray[n1] = n2; + + INCHI_HEAPCHK + + return 1; /* a change has been made */ + } + + INCHI_HEAPCHK + + return 0; /* no changes */ +} + + +/****************************************************************/ +Node GetUnorderedPartitionMcrNode( UnorderedPartition *p1, Node v ) +{ + Node ret = (Node)(1+ nGetMcr2( p1->equ2, (AT_RANK)(v-1) )); + + INCHI_HEAPCHK + + return ret; +} + + +/****************************************************************/ +/* change p2 to (p2 v p1) */ +int UnorderedPartitionJoin( UnorderedPartition *p1, UnorderedPartition *p2, int n ) +{ + int i, j; + int nNumChanges = 0; + for ( i = 0; i < n; i ++ ) + { + if ( (j=(int)p1->equ2[i]) == i || p2->equ2[(int)i] == p2->equ2[(int)j] ) + { + continue; + } + nNumChanges += nJoin2Mcrs2(p2->equ2, (AT_NUMB)i, (AT_NUMB)j ); + } + + INCHI_HEAPCHK + + return nNumChanges; +} + + +/****************************************************************/ +int PartitionSatisfiesLemma_2_25( Partition *p, int n ) +{ + int nPartitionSize = 0; + int nNumNonTrivialCells = 0; + AT_RANK r; + int i, num; + + for ( i = num = 0, r=1; i < n; i ++, r++ ) + { + if ( (rank_mask_bit & p->Rank[(int)p->AtNumber[i]]) == r ) + { + nPartitionSize ++; + if ( num ) + { + /* num+1 = cell size > 1 */ + nNumNonTrivialCells ++; + num = 0; + } + } + else + { + num ++; + } + } + + /* check Lemma_2_25 conditions */ + if ( n <= nPartitionSize+4 || + n == nPartitionSize + nNumNonTrivialCells || + n == nPartitionSize + nNumNonTrivialCells + 1 ) + { + return 1; + } + + return 0; +} + + +/****************************************************************/ +void PartitionCopy( Partition *To, Partition *From, int n ) +{ + int i; + memcpy( To->AtNumber, From->AtNumber, n*sizeof(To->AtNumber[0])); + memcpy( To->Rank, From->Rank, n*sizeof(To->AtNumber[0])); + + for ( i = 0; i < n; i ++ ) + { + To->Rank[i] &= rank_mask_bit; + } + + INCHI_HEAPCHK +} + + +/****************************************************************/ +/* makes new equitable partition (p+1) out of p; first reduce the rank of vertex v */ +int PartitionColorVertex( CANON_GLOBALS *pCG, + Graph *G, Partition *p, Node v, + int n, int n_tg, int n_max, int bDigraph, int nNumPrevRanks ) +{ + int nNumNewRanks, i, j; + long lNumNeighListIter = 0; + AT_RANK rv, r; + AT_NUMB s, sv; + + for ( i = 1; i <= 2; i ++ ) + { + if ( !p[i].AtNumber ) + { + p[i].AtNumber = (AT_NUMB *)inchi_malloc( n_max*sizeof(p[0].AtNumber[0])); + } + if ( !p[i].Rank ) + { + p[i].Rank = (AT_RANK *)inchi_malloc( n_max*sizeof(p[0].Rank[0])); + } + if ( !p[i].AtNumber || !p[i].Rank ) + { + + INCHI_HEAPCHK + + return CT_OUT_OF_RAM; + } + } + + PartitionCopy( p+1, p, n_tg ); + + sv = v-1; /* atom number we are looking for */ + if ( sv >= (AT_NUMB) n_tg ) + { + + INCHI_HEAPCHK + + return CT_CANON_ERR; /* !!! severe program error: sv not found !!! */ + } + + rv = p[1].Rank[(int)sv]; /* rank of this atom */ + + /* second, locate sv among all vertices that have same rank as v */ + s = n_max + 1; /* always greater than sv; this initialization is needed only to keep the compiler happy */ + for ( j = (int)rv-1; 0 <= j && rv == (r = p[1].Rank[(int)(s=p[1].AtNumber[j])]) && s != sv; j -- ) + ; + + if ( s != sv ) + { + + INCHI_HEAPCHK + return CT_CANON_ERR; /* !!! severe program error: sv not found !!! */ + } + + /* shift preceding atom numbers to the right to fill the gap after removing sv */ + r = rv-1; /* initialization only to keep compiler happy */ + for ( i = j--; 0 <= j && rv == (r = p[1].Rank[(int)(s=p[1].AtNumber[j])]); i = j, j -- ) + { + p[1].AtNumber[i] = s; + } + + r = (i > 0)? (r+1):1; /* new reduced rank = (next lower rank)+1 or 1 */ + /* insert sv and adjust its rank */ + p[1].AtNumber[i] = sv; + p[1].Rank[(int)sv] = r; + + + /* make equitable partition */ + if ( bDigraph ) { + + /* + nNumNewRanks = DifferentiateRanks2( pCG, n_tg, G, + nNumPrevRanks+1, p[1].Rank, p[2].Rank, + p[1].AtNumber, &lNumNeighListIter, 1 ); + */ + nNumNewRanks = DifferentiateRanks4( pCG, n_tg, G, + nNumPrevRanks+1, p[1].Rank, p[2].Rank /* temp array */, + p[1].AtNumber, (AT_RANK)n, &lNumNeighListIter ); + } else { + /* + nNumNewRanks = DifferentiateRanks2( pCG, n_tg, G, + nNumPrevRanks+1, p[1].Rank, p[2].Rank, + p[1].AtNumber, &lNumNeighListIter, 1 ); + */ + nNumNewRanks = DifferentiateRanks3( pCG, n_tg, G, + nNumPrevRanks+1, p[1].Rank, p[2].Rank /* temp array */, + p[1].AtNumber, &lNumNeighListIter ); + } + INCHI_HEAPCHK + + return nNumNewRanks; +} + + +/****************************************************************/ +typedef struct tagNodeValues +{ + NUM_H NumH; + AT_ISO_SORT_KEY iso_sort_key; + NUM_H NumHfixed; + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + AT_ISO_SORT_KEY iso_sort_key_Hfixed; +#endif + + AT_NUMB nAtNumber; +} NV; + + + +/****************************************************************/ +/* return min node > vPrev or INFINITY if not found */ +/* Input: v = previous atom number + 1 or 0 on first call*/ +Node CellGetMinNode( Partition *p, Cell *W, Node v, CANON_DATA *pCD ) +{ + AT_NUMB i; + AT_NUMB uCurAtNumb, uMinAtNumb = INFINITY; + + /* in case of emty cell: (W->first=INFINITY) > (W->next=0); returns INFINITY */ + if ( W->first > W->next ) + { + return INFINITY; + } + +#if ( USE_AUX_RANKING == 1 ) + if ( pCD && pCD->nAuxRank ) + { + AT_RANK uMinAuxRank, uCurAuxRank; + int nCurAtNumb; + +#if ( USE_AUX_RANKING_ALL == 1 ) + AT_RANK uInpAuxRank; + int nInpAtNumb, nMinAtNumb; +#endif + + for ( i = W->first; i < W->next; i ++ ) + { + uCurAtNumb = p->AtNumber[(int)i]; + if ( !(p->Rank[(int)uCurAtNumb] & rank_mark_bit) ) + { + break; /* found the first unmarked yet node */ + } + } + if ( i == W->next ) + { + return INFINITY; + } + +#if ( USE_AUX_RANKING_ALL == 1 ) + /*==== vertex ordering definition === + * vertex v1 < v2 <=> (AuxRank(v1)==AuxRank(v2) && AtNumb(v1) < AtNumb(v2)) || (AuxRank(v1) < AuxRank(v2)) + * vertex v1 > v2 <=> (AuxRank(v1)==AuxRank(v2) && AtNumb(v1) > AtNumb(v2)) || (AuxRank(v1) > AuxRank(v2)) + * vertex v1 = v2 <=> (AuxRank(v1)==AuxRank(v2) && AtNumb(v1) == AtNumb(v2)) + */ + + /* set initial vMin so that vMin > any vertex */ + uMinAuxRank = INFINITY; + nMinAtNumb = INFINITY; + /* set vInp */ + if ( v ) + { + nInpAtNumb = (int)v - 1; /* previous vertex */ + uInpAuxRank = pCD->nAuxRank[nInpAtNumb]; + } + else + { + nInpAtNumb = -1; /* less than any vertex */ + uInpAuxRank = 0; + } + /* find vMin = min { vCur : (vCur > vInp) && (vCur in W) } */ + for ( ; i < W->next; i ++ ) + { + nCurAtNumb = (int)p->AtNumber[(int)i]; + if ( !(p->Rank[nCurAtNumb] & rank_mark_bit) ) + { + /* vertex nCurAtNumb is not marked, find whether it fits the conditions */ + uCurAuxRank = pCD->nAuxRank[nCurAtNumb]; + if ( uCurAuxRank == uInpAuxRank && nCurAtNumb > nInpAtNumb || uCurAuxRank > uInpAuxRank ) + { + /* here vCur > vInp */ + if ( uCurAuxRank == uMinAuxRank && nCurAtNumb < nMinAtNumb ) + { + /* vCur < vMin (1) */ + nMinAtNumb = nCurAtNumb; + } + else if ( uCurAuxRank < uMinAuxRank ) + { + /* vCur < vMin (2) */ + uMinAuxRank = uCurAuxRank; + nMinAtNumb = nCurAtNumb; + } + } + } + } + + uMinAtNumb = (nMinAtNumb==INFINITY)? INFINITY : (AT_NUMB)nMinAtNumb; + +#else + + if ( v ) + { + nCurAtNumb = (int)v-1; + /* any valid found node must have nAuxRank == uMinAuxRank */ + uMinAuxRank = pCD->nAuxRank[nCurAtNumb]; + } + else + { + /* any valid found node must have minimal uMinAuxRank from pCD->nAuxRank[] */ + uMinAuxRank = INFINITY; /* undefined */ + } + + for ( ; i < W->next; i ++ ) + { + uCurAtNumb = p->AtNumber[(int)i]; + nCurAtNumb = (int)uCurAtNumb; + if ( uCurAtNumb >= v && !(p->Rank[nCurAtNumb] & rank_mark_bit) ) + { + uCurAuxRank = pCD->nAuxRank[nCurAtNumb]; + if ( v ) + { + /* get next node */ + /* find node with smallest uCurAtNumb among nodes with aux. ranks equal to uMinAuxRank */ + if ( uCurAuxRank == uMinAuxRank && uCurAtNumb < uMinAtNumb ) + { + uMinAtNumb = uCurAtNumb; + } + } + else + { + /* get first node */ + /* find node with smallest smallest uCurAtNumb among nodes with smallest aux. ranks */ + if ( uMinAuxRank > uCurAuxRank ) + { + uMinAuxRank = uCurAuxRank; + uMinAtNumb = uCurAtNumb; + } + else + if ( uMinAuxRank == uCurAuxRank && uCurAtNumb < uMinAtNumb ) + { + uMinAtNumb = uCurAtNumb; + } + } + } + } +#endif + } + + else + +#endif /* } USE_AUX_RANKING */ + + { + for ( i = W->first; i < W->next; i ++ ) + { + uCurAtNumb = p->AtNumber[(int)i]; + if ( uCurAtNumb >= v && !(p->Rank[(int)uCurAtNumb] & rank_mark_bit) && uCurAtNumb < uMinAtNumb ) + { + uMinAtNumb = uCurAtNumb; + } + } + } + + if ( uMinAtNumb != INFINITY ) uMinAtNumb ++; + + INCHI_HEAPCHK + + return uMinAtNumb; +} + + +/****************************************************************/ +int CellGetNumberOfNodes( Partition *p, Cell *W ) +{ + int first = W->first; + int next = W->next; + int i, num; + for ( i = first, num = 0; i < next; i ++ ) + { + if ( !( rank_mark_bit & p->Rank[(int)p->AtNumber[i]] ) ) + { + num++; + } + } + + INCHI_HEAPCHK + + return num; +} + + +/****************************************************************/ +int CellIntersectWithSet( CANON_GLOBALS *pCG, + Partition *p, Cell *W, NodeSet *Mcr, int l ) +{ + bitWord *McrBits = Mcr->bitword[l-1]; + int first = W->first; + int next = W->next; + int i, j, k; + if ( first >= next ) { /* for testing only */ + return 0; + } + for ( i = first, k = 0; i < next; i ++ ) { + j = (int)p->AtNumber[i]; + if ( !(McrBits[ j / pCG->m_num_bit ] & pCG->m_bBit[ j % pCG->m_num_bit ]) ) { /* BC: reading uninit memory ???-not examined yet */ + k += !(p->Rank[j] & rank_mark_bit); /* for testing only */ + p->Rank[j] |= rank_mark_bit; + } + } + INCHI_HEAPCHK + return k; +} + + +/****************************************************************/ +void CtPartClear( ConTable *Ct, int k ) +{ + int start; + int len; + /* connection table */ + start = k>1? Ct->nextCtblPos[k-1] : 0; + len = Ct->lenCt - start; + if ( len > 0 ) + { + memset( Ct->Ctbl + start, 0, (Ct->lenCt - start)*sizeof(Ct->Ctbl[0]) ); + } + Ct->lenCt = start; + Ct->lenPos = k; + + INCHI_HEAPCHK +} + + +/**********************************************************************************/ +/* Sort neighbors according to ranks in ascending order */ +void insertions_sort_NeighList_AT_NUMBERS2( NEIGH_LIST base, + AT_RANK *nRank, + AT_RANK max_rj ) +{ + AT_NUMB *i, *j, *pk, tmp, rj; + int k, num = (int)*base++; + for( k=1, pk = base; k < num; k++, pk ++ ) + { + i = pk; + j = i + 1; + rj = (rank_mask_bit & nRank[(int)*j]); + if ( rj < max_rj ) { + while ( j > base && rj < (rank_mask_bit & nRank[(int)*i])) + { + tmp = *i; + *i = *j; + *j = tmp; + j = i --; + } + } + } + + INCHI_HEAPCHK +} + + +/****************************************************************/ +/* may need previous Lambda */ +void CtPartFill( Graph *G, + CANON_DATA *pCD, + Partition *p, + ConTable *Ct, + int k, + int n, + int n_tg ) +/* k = (new index in Ct->nextAtRank[] and Ct->nextCtblPos[]) + 1 */ +{ + int startCtbl; + int startAtOrd; + AT_RANK r, rj, nn, j, rj_prev; + int i, m; + +#ifdef INCHI_CANON_USE_HASH + CtHash hash = 0; +#endif + + static int count; /* for debug only */ + count ++; + + INCHI_HEAPCHK + + k --; + if ( k ) + { + startCtbl = Ct->nextCtblPos[k-1]; + startAtOrd = Ct->nextAtRank[k-1]-1; /* here p->Rank[p->AtNumber[r-1]] = r */ + } + else + { + startCtbl = 0; + startAtOrd = 0; + } + + /******* well-defined (by fixed ranks) part of the connection table ************/ + r = (rank_mask_bit & p->Rank[(int)p->AtNumber[startAtOrd]]); + + for ( i = startAtOrd; i < n_tg && r == (rank_mask_bit&p->Rank[m=(int)p->AtNumber[i]]); i++, r ++ ) + { + Ct->Ctbl[startCtbl++] = r; + insertions_sort_NeighList_AT_NUMBERS2( G[m], p->Rank, r ); + nn = G[m][0]; /* number of neighbors */ + rj_prev = 0; /* debug only */ + +#ifdef INCHI_CANON_USE_HASH + hash = add2crc32( hash, (AT_NUMB)(r + n) ); +#endif + + for ( j = 1; j <= nn && (rj=(rank_mask_bit&p->Rank[(int)G[m][j]])) < r; j ++ ) + { + Ct->Ctbl[startCtbl++] = rj; + +#ifdef INCHI_CANON_USE_HASH + hash = add2crc32( hash, rj ); +#endif + +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + /* debug only */ + if ( rj < rj_prev ) + { + int stop = 1; /* */ + } +#endif + + rj_prev = rj; + } + } + + INCHI_HEAPCHK + + /****************** well-defined part of base hydrogen atoms *******************/ + if ( pCD->NumH && Ct->NumH ) + { + nn = inchi_min(n, i); + for ( j = startAtOrd; j < nn; j ++ ) + { + /* atoms */ + Ct->NumH[j] = pCD->NumH[p->AtNumber[j]]; + } + for ( ; j < i; j ++ ) + { + /* t-groups */ + int data_pos = n + T_NUM_NO_ISOTOPIC * ((int)p->AtNumber[j] - n); + for ( m = 0; m < T_NUM_NO_ISOTOPIC; m ++ ) + { + Ct->NumH[nn ++] = pCD->NumH[data_pos ++]; + } + } + Ct->lenNumH = nn; + } + else + { + Ct->lenNumH = 0; + } + + INCHI_HEAPCHK + + /****************** well-defined part of fixed hydrogen atoms *******************/ + if ( pCD->NumHfixed && Ct->NumHfixed ) + { + nn = inchi_min(n, i); + for ( j = startAtOrd; j < nn; j ++ ) + { + Ct->NumHfixed[j] = pCD->NumHfixed[p->AtNumber[j]]; + + INCHI_HEAPCHK + } + /* Ct->lenNumHfixed = nn; */ + } + else + { + ;/* Ct->lenNumHfixed = 0; */ + } + + INCHI_HEAPCHK + + /****************** well-defined part of isotopic keys ***************************/ + if ( pCD->iso_sort_key && Ct->iso_sort_key ) + { + for ( j = startAtOrd; j < i; j ++ ) + { + Ct->iso_sort_key[j] = pCD->iso_sort_key[p->AtNumber[j]]; + } + Ct->len_iso_sort_key = i; + } + else + { + Ct->len_iso_sort_key = 0; + } + + INCHI_HEAPCHK + + /****************** well-defined part of isotopic iso_exchg_atnos ***************************/ + if ( pCD->iso_exchg_atnos && Ct->iso_exchg_atnos ) + { + for ( j = startAtOrd; j < i; j ++ ) + { + Ct->iso_exchg_atnos[j] = pCD->iso_exchg_atnos[p->AtNumber[j]]; + } + Ct->len_iso_exchg_atnos = i; + } + else + { + Ct->len_iso_exchg_atnos = 0; + } + + INCHI_HEAPCHK + + /******** well-defined part of isotopic keys for fixed hydrogen atoms ************/ +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + if ( pCD->iso_sort_key_Hfixed && Ct->iso_sort_key_Hfixed ) + { + nn = inchi_min(n, i); + for ( j = startAtOrd; j < nn; j ++ ) + { + Ct->iso_sort_key_Hfixed[j] = pCD->iso_sort_key_Hfixed[p->AtNumber[j]]; + } + Ct->len_iso_sort_key_Hfixed = nn; + } + else + { + Ct->len_iso_sort_key_Hfixed = 0; + } +#endif + + INCHI_HEAPCHK + + Ct->lenCt = startCtbl; /* not aways increases */ + Ct->nextCtblPos[k] = startCtbl; + Ct->nextAtRank[k] = r; + Ct->lenPos = k+1; + + /* the rest of the CTable */ + +#ifdef INCHI_CANON_USE_HASH + while ( i < n ) + { + r = (rank_mask_bit&p->Rank[m=(int)p->AtNumber[i]]); + hash = add2crc32( hash, (AT_NUMB)(r + n) ); + r++; + insertions_sort_NeighList_AT_NUMBERS2( G[m], p->Rank, r ); + nn = G[m][0]; + rj_prev = 0; /* debug only */ + for ( j = 1; j <= nn && (rj=(rank_mask_bit&p->Rank[(int)G[m][j]])) < r; j ++ ) + { + hash = add2crc32( hash, rj ); + } + i ++; + } + Ct->hash[k] = hash; +#endif + + INCHI_HEAPCHK +} + + +/****************************************************************/ +void CtPartInfinity( ConTable *Ct, S_CHAR *cmp, int k ) +{ + int startCtbl; + /*int startAtOrd;*/ + k --; + if ( k ) + { + startCtbl = Ct->nextCtblPos[k-1]; + /*startAtOrd = Ct->nextAtRank[k-1]-1;*/ /* here p->Rank[p->AtNumber[r-1]] = r */ + if ( cmp ) + { + memset( cmp, 0, k*sizeof(cmp[0]) ); + } + } + else + { + startCtbl = 0; + /*startAtOrd = 0;*/ + } + if ( !startCtbl || Ct->Ctbl[startCtbl-1] != EMPTY_CT ) + { + Ct->Ctbl[startCtbl] = EMPTY_CT; + } + + INCHI_HEAPCHK +} + + +/****************************************************************/ +/* Return value: + -1 <=> *Lambda1 < *Lambda2 + 0 <=> *Lambda1 = *Lambda2 + +1 <=> *Lambda1 > *Lambda2 + + Input: k+1 = value of level at which the comparison is executed + (that is, in the calling program k(caller) = k+1) + + Stars (*) below mark the differences: + + bSplitTautCompare != 0 => directed graph; compare: + non-tautomeric part of CT in layer 0; (*) + non-tautomeric H in layer 1; (*) + tautomeric part of CT & H in layer 2; (*) + fixed H in layer 3; + isotopic atoms, non-taut + H & t-groups in layer 4; + fixed isotopic H in layer 5; <- move to layer 4 + + bSplitTautCompare == 0 => undirected graph; compare: + full CT in Layer 0; (*) + taut and non-taut H in Layer 1; (*) + * nothing * in layer 2; (*) + fixed H in layer 3; + isotopic atoms, non-taut + H & t-groups in layer 4; + fixed isotopic H in layer 5; <- move to layer 4 + +*/ + + + +/****************************************************************/ +int CtPartCompare( ConTable *Ct1, + ConTable *Ct2, + S_CHAR *cmp, + kLeast *kLeastForLayer, + int k, + int bOnlyCommon, + int bSplitTautCompare ) +{ + int startCt1, endCt1, startCt2, endCt2; /*endCt,*/ + int startAt1, endAt1, startAt2, endAt2; /*endCt,*/ + int midCt /* end of atoms only Ct */, midNumH=0 /* end of atoms only NumH */, maxVert; + int diff, i, k1, k2, lenNumH, len_iso_sort_key, /*mid_iso_sort_key,*/ midAt; + int nLayer = 0; + + k --; + i = -1; + + /* set kLeastForLayer[nLayer].k = (k+1) or -(k+1) + kLeastForLayer[nLayer].i = iDiff + if all the conditions are met: + 1) kLeastForLayer[nLayer].k = 0 + 2) diff==0 for all layers < nLayer + + sign: + if the final diff < 0 then kLeastForLayer[nLayer].k = -(k+1) else + if the final diff > 0 then kLeastForLayer[nLayer].k = +(k+1) + + k+1 instead of k takes into account k--; statememt above) + + meaning: + ======== + abs(kLeastForLayer[nLayer].k) is the greatest level k at which + difference at layer nLayer are zeroes of hidden by differences in smaller nLayer. + + "Hidden by difference in smaller level" means that nLayer of comparison + has not been reached because the difference was discovered at a previous layer. + + + Lambda vs zf_zeta comparison + ============================================= + accept only diff == 0 + + Lambda vs pzb_rho and pzb_rho_fix comparison + ============================================= + Maintain kLeastForLayer[] and kLeastForLayerFix[] + + The algorithm provides that pzb_rho(m-1) < pzb_rho(m) <= pzb_rho_fix + + Definition: pzb_rho(m-1) < pzb_rho(m) means that + ----------------------------------------------- + pzb_rho(m-1)[nLayerCurr] == pzb_rho(m)[nLayerCurr] for nLayerCurr = 0..nLayerDiff-1 + pzb_rho(m-1)[nLayerDiff] < pzb_rho(m)[nLayerDiff] + + Definition: pzb_rho(m-1)[nLayerDiff] < pzb_rho(m)[nLayerDiff] means that + ------------------------------------------------------------------------- + pzb_rho(m-1)[nLayerDiff][i] == pzb_rho(m)[nLayerDiff][i] for i=0..iDdiff-1 + pzb_rho(m-1)[nLayerDiff][iDdiff] < pzb_rho(m)[nLayerDiff][iDdiff] + + This defines nLayerDiff(pzb1, pzb2) where pszb1 = pzb_rho(a), pzb2=pzb_rho(b) (a= 0 *) && + ((L_fix < L_rho) || (L_fix == L_rho && I_fix < I_rho)) + => + qzb_rho_fix = kLeastForLayerFix[L_fix].k if prevoiusly qzb_rho_fix == 0 + + b) otherwise do not change qzb_rho_fix, except the following: + + c) Special case L_rho == L_fix && I_rho == I_fix. Let L=L_rho, I = I_rho. + + Compare 3 valirs: Lambda[L][I], pzb_rho(m)[L][I], pzb_rho_fix[L][I] + The algorithm provides pzb_rho(m)[L][I] < pzb_rho_fix[L][I] + (pzb_rho(m)[L][I]==pzb_rho_fix[L][I] <=> pzb_rho(m)[L][I]==pzb_rho_fix[L][I] + is impossible by construction) + There are 3 possibilities: + c1) Lambda[L][I] < pzb_rho(m)[L][I] < pzb_rho_fix[L][I] <=> + kLeastForLayer[L].k < 0 && kLeastForLayerFix[L].k < 0 + => qzb_rho := kLeastForLayer[L].k, reject too small Lambda + c2) pzb_rho(m)[L][I] < Lambda[L][I] < pzb_rho_fix[L][I] + kLeastForLayer[L].k > 0 && kLeastForLayerFix[L].k < 0 + => qzb_rho := kLeastForLayer[L].k, accept Lambda, rho:=nu + c3) pzb_rho(m)[L][I] < pzb_rho_fix[L][I] < Lambda[L][I] + kLeastForLayer[L].k > 0 && kLeastForLayerFix[L].k > 0 + => qzb_rho_fix := kLeastForLayerFix[L].k, reject too big Lambda + + Case + kLeastForLayer[L].k < 0 && kLeastForLayerFix[L].k > 0 is impossible + because it means + pzb_rho_fix < Lambda < pzb_rho(m) <=> pzb_rho_fix < pzb_rho(m) + + + Case (c3) occurs in case of (a) + Case (c1) + + 2. Comparison Lambda vs pzb_rho before reaching discrete partition + ---------------------------------------------------------------------- + a) (L_rho < L_fix) || (L_rho == L_fix && I_rho < I_fix) => + + Lambda differs from pzb_rho(m) in the part of pzb_rho(m) that will never change + qzb_rho = kLeastForLayer[L_rho].k; reject Labmda or accept pzb_rho(m+1):=Labmda + + b) (L_rho == L_fix && I_rho > I_fix) && kLeastForLayer[L_rho].k < 0 + Lambda < pzb_rho(m), therefore + qzb_rho = kLeastForLayer[L_rho].k; reject Labmda + + c) (L_rho > L_fix) => + qzb_rho := 0 because more significant difference may be discovered + in layer < L_rho later. The final comparison may be needed at the + level of discrete partition. + + + */ + + if ( cmp ) + { + for ( i = 0; i <= k && !cmp[i]; i++ ) + ; + if ( i < k ) + { + cmp[k] = cmp[i]; + return (int)cmp[i]; + } + } + + k1 = Ct1->lenPos-1; + k2 = Ct2->lenPos-1; + +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + if ( k > k1 || k > k2 ) { + int stop = 1; + } +#endif + + diff = 0; + + if ( k ) + { + startCt1 = Ct1->nextCtblPos[k-1]; + startCt2 = Ct2->nextCtblPos[k-1]; + startAt1 = Ct1->nextAtRank[k-1]-1; + startAt2 = Ct2->nextAtRank[k-1]-1; + } + else + { + startCt1 = startCt2 = 0; + startAt1 = startAt2 = 0; + } + + endCt1 = Ct1->nextCtblPos[k]; + endCt2 = Ct2->nextCtblPos[k]; + endAt1 = (int)Ct1->nextAtRank[k]-1; + endAt2 = (int)Ct2->nextAtRank[k]-1; + + maxVert = inchi_min(Ct1->maxVert, Ct2->maxVert); + +#ifdef INCHI_CANON_USE_HASH + if ( !diff ) + { + if ( Ct1->hash[k] > Ct2->hash[k] ) + diff = 1; + else + if ( Ct1->hash[k] < Ct2->hash[k] ) + diff = -1; + } + if ( diff ) + { + goto done; + } +#endif + + /************************** lengths **************************************************/ + if ( diff = -(startCt1 - startCt2) ) + { + /* comparing two INFINITY terminations */ + if ( bOnlyCommon && + startCt1 >= Ct1->nLenCTAtOnly && startCt2 >= Ct2->nLenCTAtOnly && + Ct1->Ctbl[startCt1] == EMPTY_CT && Ct2->Ctbl[startCt2] == EMPTY_CT ) + { + return 0; + } + if ( bOnlyCommon ) + { + startCt1 = startCt2 = inchi_min(startCt1, startCt2); + startAt1 = startAt2 = inchi_min(startAt1, startAt2); + if ( Ct1->lenCt == Ct2->lenCt ) + { + endCt1 = endCt2 = inchi_max(endCt1, endCt2); + endAt1 = endAt2 = inchi_max(endAt1, endAt2); + } + +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + else { + int stop = 1; + } +#endif + } + else + /* comparing (taut tail) vs INFINITY termination -- ??? */ + if ( startCt1 > startCt2 && + Ct1->maxVert > Ct2->maxVert && + startAt2 == Ct2->maxVert ) + { + return 0; + } + else + { + goto done; + } + } + + lenNumH = Ct1->lenNumH; + len_iso_sort_key = Ct1->len_iso_sort_key; + + if ( diff = -(endCt1 - endCt2) ) + { + /* negative sign reproduces results for NSC=28393 */ + if ( bOnlyCommon ) + { + endCt1 = endCt2 = inchi_min(endCt1, endCt2); + endAt1 = endAt2 = inchi_min(endAt1, endAt2); + lenNumH = inchi_min(Ct1->lenNumH, Ct2->lenNumH); + len_iso_sort_key = inchi_min(Ct1->len_iso_sort_key, Ct1->len_iso_sort_key); + } + else + /* take care of case when comparing tautomeric vs non-tautomeric: + since (taut)->maxVert > (non-taut)->maxVert, --??? + (taut)->maxlenCt > (non-taut)->maxlenCt --!!! + compare up to min out of the two, ignoring INFINITY in the last position */ + if ( endCt1 > endCt2 && Ct1->maxlenCt > Ct2->maxlenCt ) + { + if ( endAt2 == Ct2->maxVert + 1 ) + { + /* remove INFINITY termination of the shorter CT */ + /* should never happen */ + endAt2 --; + len_iso_sort_key = lenNumH = endAt1 = endAt2; + endCt2 --; + endCt1 = endCt2; + diff = 0; + } + else if ( endAt2 == Ct2->maxVert ) + { + /* remove INFINITY termination of CT */ + len_iso_sort_key = lenNumH = endAt1 = endAt2; + endCt1 = endCt2; + diff = 0; + } + else + { + goto done; + } + } + else + { + goto done; + } + } + + if ( bSplitTautCompare ) + { + midCt = inchi_min(Ct1->nLenCTAtOnly, Ct2->nLenCTAtOnly); + if ( midCt > endCt1 ) + { + midCt = endCt1; + } + midAt = inchi_min(maxVert, endAt1); + } + else + { + midCt = endCt1; + midAt = endAt1; + } + + + /*endCt = min(endCt1, endCt2);*/ + /*************************************************************************/ + /************ layer 0: connection table without tautomeric groups ********/ + /*************************************************************************/ + + for ( i = startCt1; i < midCt && Ct1->Ctbl[i] == Ct2->Ctbl[i]; i ++ ) + /*for ( i = startCt1; i < endCt && !(diff = (int)Ct1->Ctbl[i] - (int)Ct2->Ctbl[i]); i ++ )*/ + ; + + if ( i < midCt ) + { + diff = (int)Ct1->Ctbl[i] - (int)Ct2->Ctbl[i]; + goto done; + } + + /*************************************************************************/ + /******** layer 1 NumH: H atoms without tautomeric H *********************/ + /*************************************************************************/ + + nLayer ++; + + /*============= check limits for consistency ==========*/ + if ( diff = -(startAt1 - startAt2) ) + { + goto done; /* should not happen */ + } + if ( diff = -(endAt1 - endAt2) ) + { + goto done; /* should not happen */ + } + + /*============= comparison =============================*/ + if ( Ct1->NumH && Ct2->NumH ) + { + if ( endAt1 < maxVert ) + { + midNumH = lenNumH = endAt1; + } + else if ( bSplitTautCompare ) { + midNumH = maxVert; + } else + { + midNumH = lenNumH; + } + + /* lenNumH = (endAt2 >= maxVert)? lenNumH : endAt2; */ + /* endAt1 = (endAt2 == n)? lenNumH : endAt2; */ + + for ( i = startAt1; i < midNumH && Ct1->NumH[i] == Ct2->NumH[i]; i ++ ) + ; + if ( i < midNumH ) + { + diff = (int)Ct1->NumH[i] - (int)Ct2->NumH[i]; + goto done; + } + } + + + /*************************************************************************/ + /************** layer 2: tautomeric part of CT and tautomeric H **********/ + /*************************************************************************/ + nLayer ++; + for ( i = midCt; i < endCt1 && Ct1->Ctbl[i] == Ct2->Ctbl[i]; i ++ ) + ; /* compare tautomeric groups part of CT */ + if ( i < endCt1 ) + { + diff = (int)Ct1->Ctbl[i] - (int)Ct2->Ctbl[i]; + goto done; + } + if ( Ct1->NumH && Ct2->NumH ) + { + for ( i = midNumH; i < lenNumH && Ct1->NumH[i] == Ct2->NumH[i]; i ++ ) + ; /* compare tautomeric H */ + if ( i < lenNumH ) + { + diff = (int)Ct1->NumH[i] - (int)Ct2->NumH[i]; + i += endCt1 - midCt; + goto done; + } + } + + + /*************************************************************************/ + /************** layer 3: Fixed H atoms ***********************************/ + /*************************************************************************/ + nLayer ++; + if ( Ct1->NumHfixed && Ct2->NumHfixed ) + { + for ( i = startAt1; i < midAt && Ct1->NumHfixed[i] == Ct2->NumHfixed[i]; i ++ ) + ; + + if ( i < midAt ) { + diff = (int)Ct1->NumHfixed[i] - (int)Ct2->NumHfixed[i]; + goto done; + } + } + + + /*************************************************************************/ + /************** layer 4: isotopic atoms H, incl. tautomeric **************/ + /*************************************************************************/ + nLayer ++; + if ( Ct1->iso_sort_key && Ct2->iso_sort_key ) + { + for ( i = startAt1; i < endAt1 && Ct1->iso_sort_key[i] == Ct2->iso_sort_key[i]; i ++ ) + ; + if ( i < endAt1 ) + { + diff = Ct1->iso_sort_key[i] > Ct2->iso_sort_key[i]? 1:-1; + goto done; + } + } + if ( Ct1->iso_exchg_atnos && Ct2->len_iso_exchg_atnos ) + { + for ( i = startAt1; i < endAt1 && Ct1->iso_exchg_atnos[i] == Ct2->iso_exchg_atnos[i]; i ++ ) + ; + if ( i < endAt1 ) + { + diff = Ct1->iso_exchg_atnos[i] > Ct2->iso_exchg_atnos[i]? 1:-1; + goto done; + } + } + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + + /*************************************************************************/ + /************** layer 6: Fixed isotopic H atoms **************************/ + /*************************************************************************/ + nLayer ++; + if ( Ct1->iso_sort_key_Hfixed && Ct2->iso_sort_key_Hfixed ) + { + for ( i = startAt1; i < midAt && Ct1->iso_sort_key_Hfixed[i] == Ct2->iso_sort_key_Hfixed[i]; i ++ ) + ; + if ( i < midAt ) + { + diff = Ct1->iso_sort_key_Hfixed[i] > Ct2->iso_sort_key_Hfixed[i]? 1:-1; + goto done; + } + } +#endif + + + +done: + +#ifdef INCHI_CANON_MIN + diff = -diff; +#endif + + if ( diff ) + { + diff = (diff > 0)? (nLayer+1) : -(nLayer+1); /* return the discovered difference layer number >= 1 */ + if ( kLeastForLayer ) + { + +#if ( bRELEASE_VERSION != 1 ) + if ( abs(kLeastForLayer[nLayer].k) > k+1 ) { /* for debug only */ + int stop = 1; /* */ + } +#endif + + if ( !kLeastForLayer[nLayer].k ) + { + kLeastForLayer[nLayer].k = (diff > 0)? (k+1) : -(k+1); + kLeastForLayer[nLayer].i = i; + } + if ( nLayer /* && !bOnlyCommon */) + { + diff = 0; + } + } + } + +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + else + { + int stop = 1; /* for debug only */ + } +#endif + + if ( cmp ) + { + cmp[k] = (diff > 0)? 1 : (diff < 0)? -1 : 0; + } + + return diff; +} + + +/**************************************************************************************************************/ +int CtFullCompare( ConTable *Ct1, + ConTable *Ct2, + int bOnlyCommon, + int bSplitTautCompare ) +{ + int startCt1, endCt1, startCt2, endCt2; /*endCt,*/ + int startAt1, endAt1, startAt2, endAt2; /*endCt,*/ + int midCt /* end of atoms only in Ctbl */, + midNumH = 0 /* end of atoms only NumH */, + midAt /* end of atoms only */; + int diff, i, k1, k2, lenNumH1, lenNumH2, lenNumH, maxVert /* min num atoms */; + int len_iso_sort_key1, len_iso_sort_key2, len_iso_sort_key /*, mid_iso_sort_key*/; + int nLayer = 0; + + k1 = Ct1->lenPos-1; + k2 = Ct2->lenPos-1; + + diff = 0; + + startCt1 = startCt2 = 0; + startAt1 = startAt2 = 0; + + endCt1 = Ct1->nextCtblPos[k1]; + endCt2 = Ct2->nextCtblPos[k2]; + endAt1 = (int)Ct1->nextAtRank[k1]-1; + endAt2 = (int)Ct2->nextAtRank[k2]-1; + + maxVert = inchi_min(Ct1->maxVert, Ct2->maxVert); + + if ( bOnlyCommon ) + { + + endCt1 = inchi_min(endCt1, endCt2); + endCt1 = endCt2 = inchi_min(endCt1, Ct1->lenCt); + endAt1 = endAt2 = inchi_min(endAt1, endAt2); + + if ( Ct1->Ctbl[endCt1] == EMPTY_CT || Ct1->Ctbl[endCt1] == 0 || + Ct2->Ctbl[endCt1] == EMPTY_CT || Ct2->Ctbl[endCt1] == 0 ) + { + endCt1 = endCt2 = endCt1-1; + } + lenNumH = + lenNumH1 = + lenNumH2 = inchi_min(Ct1->lenNumH, Ct2->lenNumH); + + len_iso_sort_key = + len_iso_sort_key1 = + len_iso_sort_key2 = inchi_min(Ct1->len_iso_sort_key, Ct1->len_iso_sort_key); + } + else + { + if ( Ct1->Ctbl[endCt1-1] == EMPTY_CT ) + { + endCt1 --; + } + if ( Ct2->Ctbl[endCt2-1] == EMPTY_CT ) + { + endCt2 --; + } + lenNumH1 = Ct1->lenNumH; + lenNumH2 = Ct2->lenNumH; + lenNumH = inchi_min(lenNumH1, lenNumH2); + + len_iso_sort_key1 = Ct1->len_iso_sort_key; + len_iso_sort_key2 = Ct2->len_iso_sort_key; + len_iso_sort_key = inchi_min(len_iso_sort_key1, len_iso_sort_key2); + } + + if ( diff = -(endCt1 - endCt2) ) + { /* negative sign reproduces results for NSC=28393 */ + goto done; + } + + if ( bSplitTautCompare ) + { + midCt = inchi_min(Ct1->nLenCTAtOnly, Ct2->nLenCTAtOnly); + if ( midCt > endCt1 ) + { + midCt = endCt1; + } + midAt = inchi_min(maxVert, endAt1); + } + else + { + midCt = endCt1; + midAt = endAt1; + } + + + /*************************************************************************/ + /************ layer 0: connection table without tautomeric groups ********/ + /*************************************************************************/ + for ( i = startCt1; i < midCt && Ct1->Ctbl[i] == Ct2->Ctbl[i]; i ++ ) + ; + if ( i < midCt ) + { + diff = (int)Ct1->Ctbl[i] - (int)Ct2->Ctbl[i]; + goto done; + } + + + /*************************************************************************/ + /************* layer 1: H atoms without tautomeric H *********************/ + /*************************************************************************/ + nLayer ++; + if ( Ct1->NumH && Ct2->NumH ) + { + if ( diff = -(lenNumH1 - lenNumH2) ) + { + /* negative sign reproduces results for NSC=28393 */ + goto done; + } + if ( endAt1 < maxVert ) + { + midNumH = lenNumH1 = endAt1; + } + else if ( bSplitTautCompare ) + { + midNumH = maxVert; + } else + { + midNumH = lenNumH1; + } + + for ( i = startAt1; i < midNumH && Ct1->NumH[i] == Ct2->NumH[i]; i ++ ) + ; + if ( i < midNumH ) + { + diff = (int)Ct1->NumH[i] - (int)Ct2->NumH[i]; + goto done; + } + } + + + /*************************************************************************/ + /************** layer 2: tautomeric part of CT and tautomeric H **********/ + /*************************************************************************/ + nLayer ++; + for ( i = midCt; i < endCt1 && Ct1->Ctbl[i] == Ct2->Ctbl[i]; i ++ ) + ; /* compare tautomeric groups part of CT */ + if ( i < endCt1 ) + { + diff = (int)Ct1->Ctbl[i] - (int)Ct2->Ctbl[i]; + goto done; + } + if ( Ct1->NumH && Ct2->NumH ) + { + for ( i = midNumH; i < lenNumH1 && Ct1->NumH[i] == Ct2->NumH[i]; i ++ ) + ; /* compare tautomeric H */ + if ( i < lenNumH1 ) + { + diff = (int)Ct1->NumH[i] - (int)Ct2->NumH[i]; + goto done; + } + } + + + /*************************************************************************/ + /************** layer 3: Fixed H atoms ***********************************/ + /*************************************************************************/ + nLayer ++; + if ( Ct1->NumHfixed && Ct2->NumHfixed ) + { + for ( i = startAt1; i < endAt1 && Ct1->NumHfixed[i] == Ct2->NumHfixed[i]; i ++ ) + ; + if ( i < endAt1 ) + { + diff = (int)Ct1->NumHfixed[i] - (int)Ct2->NumHfixed[i]; + goto done; + } + } + + + /*************************************************************************/ + /************** layer 4: isotopic atoms, H and isotopic taut H ***********/ + /*************************************************************************/ + nLayer ++; + if ( Ct1->iso_sort_key && Ct2->iso_sort_key ) + { + if ( diff = -(len_iso_sort_key1 - len_iso_sort_key2) ) + { + /* negative sign reproduces results for NSC=28393 */ + goto done; + } + for ( i = startAt1; i < endAt1 && Ct1->iso_sort_key[i] == Ct2->iso_sort_key[i]; i ++ ) + ; + if ( i < endAt1 ) + { + diff = Ct1->iso_sort_key[i] > Ct2->iso_sort_key[i]? 1:-1; + goto done; + } + } + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + + + /*************************************************************************/ + /************** layer 6: Fixed isotopic H atoms **************************/ + /*************************************************************************/ + nLayer ++; + if ( Ct1->iso_sort_key_Hfixed && Ct2->iso_sort_key_Hfixed ) + { + for ( i = startAt1; i < midAt && Ct1->iso_sort_key_Hfixed[i] == Ct2->iso_sort_key_Hfixed[i]; i ++ ) + ; + if ( i < midAt ) + { + diff = Ct1->iso_sort_key_Hfixed[i] > Ct2->iso_sort_key_Hfixed[i]? 1:-1; + goto done; + } + } +#endif + + +done: + +#ifdef INCHI_CANON_MIN + diff = -diff; +#endif + + if ( diff ) + { + diff = (diff > 0)? (nLayer+1) : -(nLayer+1); /* return the discovered difference layer number >= 1 */ + } + + return diff; +} + + +/****************************************************************/ +int CtFullCompareLayers( kLeast *kLeastForLayer ) +{ + int iLayer; + /* check for the rejection condition: Lambda > zb_rho_fix */ + for ( iLayer = 0; iLayer < MAX_LAYERS; iLayer ++ ) + { + if ( kLeastForLayer[iLayer].k ) + { + return (kLeastForLayer[iLayer].k > 0)? (iLayer+1) : -(iLayer+1); + } + } + + return 0; +} + + +/**************************************************************/ +int CtCompareLayersGetFirstDiff( kLeast *kLeast_rho, + int nOneAdditionalLayer, + int *L_rho, + int *I_rho, + int *k_rho ) +{ + int iLayer; + if ( kLeast_rho ) + { + for ( iLayer = 0; iLayer < MAX_LAYERS; iLayer ++ ) + { + if ( kLeast_rho[iLayer].k ) + { + *L_rho = iLayer; + *I_rho = kLeast_rho[iLayer].i; + *k_rho = kLeast_rho[iLayer].k; + break; + } + } + if ( iLayer == MAX_LAYERS ) + { + if ( nOneAdditionalLayer ) + { + *L_rho = nOneAdditionalLayer; /* ??? subtract 1 ??? */ + *I_rho = -1; + *k_rho = 0; + return 0; /* difference may be in the first additional layer */ + } + else + { + *L_rho = INFINITY; + *I_rho = -1; + *k_rho = 0; + return 0; /* no difference found */ + } + } + else + { + return 1; /* difference in a real layer */ + } + } + else + { + return -1; /* no input, should not happen */ + } +} + + +/**************************************************************/ +int CtPartCompareLayers( kLeast *kLeast_rho, + int L_rho_fix_prev, + int nOneAdditionalLayer ) +{ + int L_rho, I_rho, k_rho; + if ( 0 < CtCompareLayersGetFirstDiff( kLeast_rho, nOneAdditionalLayer, &L_rho, &I_rho, &k_rho ) && + /* differences has been found in a real layer or all real layers are identical */ + L_rho <= L_rho_fix_prev ) + { + /* in this layer pzb_rho == pzb_rho_fix or in the previous real layer */ + return k_rho > 0 ? (L_rho+1) + : -(L_rho+1); + } + + return 0; +} + + +/****************************************************************/ +void UpdateCompareLayers( kLeast kLeastForLayer[], int hzz ) +{ + int i; + if ( kLeastForLayer ) + { + for ( i = 0; i < MAX_LAYERS; i ++ ) + { + if ( abs(kLeastForLayer[i].k) >= hzz ) + { + kLeastForLayer[i].k = 0; + kLeastForLayer[i].i = 0; + } + } + } +} + + + +/****************************************************************/ +void CtPartCopy( ConTable *Ct1 /* to */, + ConTable *Ct2 /* from */, + int k ) +{ + int startCt1, startCt2, endCt1, endCt2; + int len2, len2H, len2Hfixed, len2iso_sort_key, len2iso_exchg_atnos, i; + int startAt1, endAt1, startAt2, endAt2; /*endCt,*/ + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + int len2iso_sort_key_Hfixed; +#endif + + k --; + + if ( k ) + { + startCt1 = Ct1->nextCtblPos[k-1]; + startCt2 = Ct2->nextCtblPos[k-1]; + startAt1 = Ct1->nextAtRank[k-1]-1; + startAt2 = Ct2->nextAtRank[k-1]-1; + } + else + { + startCt1 = startCt2 = 0; + startAt1 = startAt2 = 0; + } + + endCt1 = Ct1->nextCtblPos[k]; + endCt2 = Ct2->nextCtblPos[k]; + endAt1 = (int)Ct1->nextAtRank[k]-1; + endAt2 = (int)Ct2->nextAtRank[k]-1; + + len2 = endCt2-startCt2; + /* len = min(len1, len2); */ + +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + if ( startCt1 != startCt2 || startAt1 != startAt2 ) + { + int stop = 1; + } +#endif + + /* copy connection table: Ctbl */ + for ( i = 0; i < len2; i ++ ) + { + Ct1->Ctbl[startCt1+i] = Ct2->Ctbl[startCt2+i]; + } + + /* copy number of H: NumH */ + len2H = 0; + if ( Ct1->NumH && Ct2->NumH ) + { + len2H = endAt2-startAt2; + if ( endAt2 > Ct2->maxVert ) + { + len2H = Ct2->lenNumH - startAt2; + } + for ( i = 0; i < len2H; i ++ ) + { + Ct1->NumH[startAt1+i] = Ct2->NumH[startAt2+i]; + } + } + + /* copy number of fixed H */ + len2Hfixed = 0; + if ( Ct1->NumHfixed && Ct2->NumHfixed ) + { + len2Hfixed = endAt2-startAt2; + for ( i = 0; i < len2Hfixed; i ++ ) { + Ct1->NumHfixed[startAt1+i] = Ct2->NumHfixed[startAt2+i]; + } + } + + /* copy isotopic keys */ + len2iso_sort_key = 0; + if ( Ct1->iso_sort_key && Ct2->iso_sort_key ) + { + len2iso_sort_key = endAt2-startAt2; + for ( i = 0; i < len2iso_sort_key; i ++ ) + { + Ct1->iso_sort_key[startAt1+i] = Ct2->iso_sort_key[startAt2+i]; + } + } + + len2iso_exchg_atnos = 0; + if ( Ct1->iso_exchg_atnos && Ct2->iso_exchg_atnos ) + { + len2iso_exchg_atnos = endAt2-startAt2; + for ( i = 0; i < len2iso_exchg_atnos; i ++ ) + { + Ct1->iso_exchg_atnos[startAt1+i] = Ct2->iso_exchg_atnos[startAt2+i]; + } + } + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + len2iso_sort_key_Hfixed = 0; + if ( Ct1->iso_sort_key_Hfixed && Ct2->iso_sort_key_Hfixed ) + { + len2iso_sort_key_Hfixed = endAt2-startAt2; + for ( i = 0; i < len2iso_sort_key; i ++ ) + { + Ct1->iso_sort_key_Hfixed[startAt1+i] = Ct2->iso_sort_key_Hfixed[startAt2+i]; + } + } +#endif + + Ct1->lenCt = startCt1 + len2; + Ct1->nextCtblPos[k] = startCt1 + len2; + Ct1->nextAtRank[k] = Ct2->nextAtRank[k]; + if ( len2H ) { + Ct1->lenNumH = startAt1 + len2H; + } + + /* + if ( len2Hfixed ) + { + Ct1->lenNumHfixed = startAt1 + len2Hfixed; + } + */ + + if ( len2iso_sort_key ) + { + Ct1->len_iso_sort_key = startAt1 + len2iso_sort_key; + } + if ( len2iso_exchg_atnos ) + { + Ct1->len_iso_exchg_atnos = startAt1 + len2iso_exchg_atnos; + } + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + if ( len2iso_sort_key_Hfixed ) + { + Ct1->len_iso_sort_key_Hfixed = startAt1 + len2iso_sort_key_Hfixed; + } +#endif + +#ifdef INCHI_CANON_USE_HASH + Ct1->hash[k] = Ct2->hash[k]; +#endif + + Ct1->lenPos = k+1; + INCHI_HEAPCHK +} + + +/****************************************************************/ +void CtFullCopy( ConTable *Ct1, ConTable *Ct2 ) +{ + /* Ct1 does not have INFINITY termination */ + int k; + for ( k = 0; k < Ct2->lenPos; k ++ ) + { + CtPartCopy( Ct1 /* to */, Ct2 /* from */, k+1 ); + } +} + + +/****************************************************************/ +void TranspositionGetMcrAndFixSetAndUnorderedPartition( CANON_GLOBALS *pCG, + Transposition *gamma, + NodeSet *McrSet, + NodeSet *FixSet, + int n, int l, + UnorderedPartition *p ) +{ + int i, j, k, mcr, num; + AT_RANK next; + bitWord *McrBits = McrSet->bitword[l-1]; + bitWord *FixBits = FixSet->bitword[l-1]; + int len = McrSet->len_set*sizeof(bitWord); + + memset( McrBits, 0, len ); + memset( FixBits, 0, len ); + for ( i = 0; i < n; i ++ ) + { + p->equ2[i] = INFINITY; /* for debug only */ + } + + for ( i = 0; i < n; i ++ ) + { + j = (int)(next = gamma->nAtNumb[i]); + + if ( j == i ) + { + FixBits[ i / pCG->m_num_bit ] |= pCG->m_bBit[ i % pCG->m_num_bit ]; + McrBits[ i / pCG->m_num_bit ] |= pCG->m_bBit[ i % pCG->m_num_bit ]; + /* p->next[i] = INFINITY; */ /* no link to same orbit points */ + p->equ2[i] = next; /* fixed point */ + } + else if ( !(rank_mark_bit & next) ) + { + gamma->nAtNumb[i] |= rank_mark_bit; + mcr = inchi_min(j, i); + num = 0; + /* mark all nodes in the cycle to ignore later; find mcr */ + while( !(rank_mark_bit & (next = gamma->nAtNumb[j])) ) + { + gamma->nAtNumb[j] |= rank_mark_bit; + j = (int)next; + if ( mcr > j ) + { + mcr = j; + } + num ++; + } + McrBits[ mcr / pCG->m_num_bit ] |= pCG->m_bBit[mcr % pCG->m_num_bit]; /* save mcr */ + /* fill out the unordered partition, the mcr first, other in the cycle after that */ + p->equ2[mcr] = mcr; + for ( k = mcr; mcr != (j = (int)(rank_mask_bit & gamma->nAtNumb[k])); k = j ) + { + p->equ2[j] = mcr; + } + } + } + +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + /* for debug only */ + for ( i = 0; i < n; i ++ ) + { + if ( p->equ2[i] >= n ) + { + int stop = 1; + } + } +#endif + + /* remove the marks */ + for ( i = 0; i < n; i ++ ) + { + gamma->nAtNumb[i] &= rank_mask_bit; + } + + INCHI_HEAPCHK +} + + +/****************************************************************/ +int SetBitCreate( CANON_GLOBALS *pCG) +{ + bitWord b1, b2; + AT_NUMB n1, n2; +#ifdef INCHI_CANON_USE_HASH + CtHash h1, h2; +#endif + int i; + + if (pCG->m_bBitInitialized) { + INCHI_HEAPCHK + return 0; /* already created */ + } + + b1 = 1; + pCG->m_num_bit = 1; + for ( b1=1, pCG->m_num_bit=1; b1 < (b2 = (bitWord)((b1 << 1)& BIT_WORD_MASK)); b1 = b2, pCG->m_num_bit ++ ) + ; + pCG->m_bBit = (bitWord*)inchi_calloc( pCG->m_num_bit, sizeof(bitWord)); + if ( !pCG->m_bBit ) { + INCHI_HEAPCHK + return -1; /* failed */ + } + for ( i = 0, b1=1; i < pCG->m_num_bit; i++, b1 <<= 1 ) { + pCG->m_bBit[i] = b1; + } + + for ( n1 = 1; n1 < (n2 = (AT_RANK)((n1 << 1)& AT_RANK_MASK) ); n1 = n2 ) + ; + rank_mark_bit = n1; + rank_mask_bit = ~n1; + +#ifdef INCHI_CANON_USE_HASH + for ( h1 = 1; h1 < (h2 = (h1 << 1)); h1 = h2 ) + ; + hash_mark_bit = h1; +#endif + pCG->m_bBitInitialized=1; + INCHI_HEAPCHK + return 1; +} + + +/****************************************************************/ +int SetBitFree( CANON_GLOBALS *pCG ) +{ + if ( pCG->m_bBit ) + { + inchi_free( pCG->m_bBit ); + pCG->m_bBit = NULL; + INCHI_HEAPCHK + return 1; /* success */ + } + + INCHI_HEAPCHK + + return 0; /* already destroyed */ +} + + + +#ifdef NEVER /* { how to renumber a graph */ +/*********************************************************************/ +void RenumberTheGraph( int n, + NEIGH_LIST *NeighList, + AT_NUMB *old2new, + AT_NUMB *new2old, + S_CHAR *mark, + int bDone ) +{ + int i, k, j; + NEIGH_LIST nl; + + /* renumber neighbors */ + for ( i = 0; i < n; i ++ ) + { + for ( j = 1; j <= NeighList[i][0]; j ++ ) + { + NeighList[i][j] = old2new[NeighList[i][j]]; + } + } + + /* rearrange NeighList in situ using new2old[] */ + for ( k = 0; k < n; k ++ ) + { + if ( mark[k] & bDone ) + continue; + if ( k == ( j = new2old[k] ) ) + { + mark[k] |= bDone; + continue; + } + /* transposition cycle */ + i = k; + nl = NeighList[k]; + do + { + NeighList[i] = NeighList[j]; + mark[i] |= bDone; + i = j; + } while ( k != ( j = new2old[i] ) ); + NeighList[i] = nl; + mark[i] |= bDone; + } + +#ifdef NEVER + /* rearrange NeighList in situ using old2new[] */ + s = 0; + for ( k = 0; k < n; k ++ ) + { + if ( mark[k] & bDone ) + continue; + if ( k == ( j = old2new[k] ) ) + { + mark[k] |= bDone; + continue; + } + /* transposition cycle */ + i = k; + /* NeighList[j] goes to ith position */ + nl2[s] = NeighList[j]; + s ^= 1; + do + { + nl2[s] = NeighList[i]; + NeighList[i] = nl2[s ^= 1]; + mark[i] |= bDone; + i = j; + j = old2new[i]; + } while ( k != ( j = old2new[i] ) ); + NeighList[j] = nl2[s ^= 1]; + mark[j] |= bDone; + } +#endif +} + + +/***************************************************************************************/ +void RearrangeAtRankArray ( int n, AT_RANK *nRank, AT_NUMB *new2old, S_CHAR *mark, int bDone ) +{ + int i, k, j; + AT_RANK r; + /* rearrange the array in situ using new2old[] */ + for ( k = 0; k < n; k ++ ) + { + if ( mark[k] & bDone ) + continue; + if ( k == ( j = new2old[k] ) ) + { + mark[k] |= bDone; + continue; + } + /* transposition cycle */ + i = k; + r = nRank[k]; + do + { + nRank[i] = nRank[j]; + mark[i] |= bDone; + i = j; + } while ( k != ( j = new2old[i] ) ); + nRank[i] = r; + mark[i] |= bDone; + } +} + + +/***************************************************************************************/ +void RenumberAtNumbArray( int n, AT_NUMB *nAtNumb, AT_NUMB *old2new ) +{ + int i; + for ( i = 0; i < n; i ++ ) + { + nAtNumb[i] = old2new[nAtNumb[i]]; + } +} + + +/****************************************************************/ +int GetCanonRanking2( int num_atoms, + int num_at_tg, + int num_max, + int bDigraph, + sp_ATOM* at, + AT_RANK **pRankStack, + int nNumPrevRanks, + AT_RANK *nSymmRank, + AT_RANK *nCanonRank, + NEIGH_LIST *NeighList, + AT_RANK *nTempRank, + CANON_STAT* pCS ) +{ + void *pzb_rho=NULL; + int ret, cmp1=0, cmp2=0; + + int i, j, k, n; + AT_NUMB *old2new = NULL, *new2old = NULL, m, r1, r2; + S_CHAR *mark = NULL; + + int nMaxLenCt = pCS->nMaxLenLinearCT; + AT_RANK *pCt = pCS->LinearCT; + int nLenCt = pCS->nLenLinearCT; + AT_RANK *pCt0 = pCS->LinearCT; + int nLenCt0 = pCS->nLenLinearCT; + + + CANON_DATA CanonData; + CANON_DATA *pCD = &CanonData; + CANON_COUNTS CanonCounts; + CANON_COUNTS *pCC = &CanonCounts; + memset (pCD, 0, sizeof(pCD[0])); + memset (pCC, 0, sizeof(pCC[0])); + /* pointers */ + pCD->LinearCT = pCS->LinearCT; + /* variables - unchanged */ + pCD->ulTimeOutTime = pCS->ulTimeOutTime; + pCD->nMaxLenLinearCT = pCS->nMaxLenLinearCT; + /* return values & input/output */ + pCD->nLenLinearCT = pCS->nLenLinearCT; + + pCC->lNumBreakTies = pCS->lNumBreakTies; + pCC->lNumDecreasedCT = pCS->lNumDecreasedCT; + pCC->lNumRejectedCT = pCS->lNumRejectedCT; + pCC->lNumEqualCT = pCS->lNumEqualCT; + pCC->lNumTotCT = pCS->lNumTotCT; + + ret = CanonGraph( ic, pCG, num_atoms, num_at_tg, num_max, bDigraph, NeighList, (Partition *)pRankStack, + nSymmRank, nCanonRank, pCS->nPrevAtomNumber, pCD, pCC, NULL, &pzb_rho, LargeMolecules ); + + pCS->nLenLinearCT = pCD->nLenLinearCT; + pCS->lNumBreakTies = pCC->lNumBreakTies; + pCS->lNumDecreasedCT = pCC->lNumDecreasedCT; + pCS->lNumRejectedCT = pCC->lNumRejectedCT; + pCS->lNumEqualCT = pCC->lNumEqualCT; + pCS->lNumTotCT = pCC->lNumTotCT; + + + /* save the connection table for comparison with the 2nd one */ + pCt0 = (AT_RANK*)inchi_calloc( nMaxLenCt, sizeof(pCt0[0])); + memcpy(pCt0, pCS->LinearCT, nMaxLenCt*sizeof(pCt0[0])); + nLenCt0 = pCS->nLenLinearCT; + + /**********************************************************/ + /* rearrange numbering to make canon. numbering the first */ + /**********************************************************/ + n = num_at_tg; + /* 1. get transpositions */ + old2new = (AT_NUMB*) inchi_calloc( n, sizeof(old2new[0]) ); + new2old = (AT_NUMB*) inchi_calloc( n, sizeof(new2old[0]) ); + mark = (S_CHAR *) inchi_calloc( n, sizeof(mark[0]) ); + for ( i = 0; i < n; i ++ ) + { + /* forward transposition: at[i] -> at[old2new[i]] position */ + old2new[i] = m = nCanonRank[i]-1; + /* forward transposition: at[new2old[i]] -> at[i] position */ + new2old[m] = i; + } + /* rearrange input data according to the new numbering */ + RenumberTheGraph( n, NeighList, old2new, new2old, mark, 1 ); + RearrangeAtRankArray ( n, pRankStack[0], new2old, mark, 2 ); + RenumberAtNumbArray ( n, pRankStack[1], old2new ); + /* make sure the atom numbers are sorted */ + for ( i = k = 0, r1 = pRankStack[0][pRankStack[1][i]]; i < n; r1 = r2) + { + for ( j = i++; i < n && r1 == (r2 = pRankStack[0][pRankStack[1][i]]); i ++ ) + ; + if ( i - j > 1 ) + { + k += insertions_sort_AT_RANK( pRankStack[1]+j, i-j ); + } + } + + ret = CanonGraph( ic, pCG, num_atoms, num_at_tg, num_max, + bDigraph, NeighList, (Partition *)pRankStack, + nSymmRank, nCanonRank, + NULL, LargeMolecules ); + + pCS->nLenLinearCT = pCD->nLenLinearCT; + pCS->lNumBreakTies = pCC->lNumBreakTies; + pCS->lNumDecreasedCT = pCC->lNumDecreasedCT; + pCS->lNumRejectedCT = pCC->lNumRejectedCT; + pCS->lNumEqualCT = pCC->lNumEqualCT; + pCS->lNumTotCT = pCC->lNumTotCT; + + + /* compare the connection tables */ + cmp1 = nLenCt0 - pCS->nLenLinearCT; + cmp2 = memcmp( pCt0, pCS->LinearCT, pCS->nLenLinearCT*sizeof(pCt0[0])); + +#ifdef _DEBUG + if ( cmp1 || cmp2 ) + { + int stop = 1; + } +#endif + + /**********************************************************/ + /* rearrange numbering back to the original numbering */ + /**********************************************************/ + + /* restore the input data to its original numbering */ + + RenumberTheGraph( n, NeighList, new2old, old2new, mark, 4 ); + + RearrangeAtRankArray ( n, pRankStack[0], old2new, mark, 8 ); + + RenumberAtNumbArray ( n, pRankStack[1], new2old ); + + /* rearrange the output data to the original numbering */ + + RearrangeAtRankArray ( n, nCanonRank, old2new, mark, 16 ); + RenumberAtNumbArray ( n, pCS->nPrevAtomNumber, new2old ); + RearrangeAtRankArray ( n, nSymmRank, old2new, mark, 32 ); + + /* free memory */ + CTableFree( pzb_rho ); + if ( pzb_rho ) + { + inchi_free( pzb_rho ); + } + + inchi_free( old2new ); + inchi_free( new2old ); + inchi_free( mark ); + inchi_free( pCt0 ); + + return ret; +} + +#endif /* } */ + + +#define QZFIX_OK(X) ((X)<=0) + +int GetOneAdditionalLayer( CANON_DATA *pCD, ConTable *pzb_rho_fix ) +{ + int nLastLayer = -1, nNumLast = 0, nLayer = 0; + + if ( !pCD || !pzb_rho_fix ) + { + return 0; + } + + nLayer ++; /* 1 */ + if ( pCD->NumH && !pzb_rho_fix->NumH ) + { + nLastLayer = nLayer; + nNumLast ++; + } + + nLayer ++; /* 2 */ + if ( pCD->nLenCTAtOnly < pCD->nLenLinearCT && pzb_rho_fix->nLenCTAtOnly == pzb_rho_fix->lenCt ) + { + nLastLayer = nLayer; + nNumLast ++; + } + + nLayer ++; /* 3 */ + if ( pCD->NumHfixed && !pzb_rho_fix->NumHfixed ) + { + nLastLayer = nLayer; + nNumLast ++; + } + + nLayer ++; /* 4 */ + if ( pCD->iso_sort_key && !pzb_rho_fix->iso_sort_key ) + { + nLastLayer = nLayer; + nNumLast ++; + } + + /* + nLayer ++; // 5 + if ( pCD->nLenCTAtOnly < pCD->nLenLinearCT && pCD->iso_sort_key && + (pzb_rho_fix->nLenCTAtOnly == pzb_rho_fix->lenCt || !pzb_rho_fix->iso_sort_key ) ) + { + nLastLayer = nLayer; + nNumLast ++; + } + */ + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + + nLayer ++; /* 6 */ + if ( pCD->iso_sort_key_Hfixed && !pzb_rho_fix->iso_sort_key_Hfixed ) + { + nLastLayer = nLayer; + nNumLast ++; + } +#endif + if ( 1 == nNumLast ) + { + return nLastLayer; + } + + return 0; +} + + +/*#define QZFIX_OK(X) (!(X))*/ + + + +/********************** CanonGraph ************************************* + * A naive implementation of graph canonical numbering algorithm * + * from "Practical Graph Isomorphism" by Brendan D. McKay, * + * Congressus Numerantium, Vol. 30 (1981), pp. 45 - 87. * + * Note: Several typos fixed, added chem. struct. specifics * + ***********************************************************************/ + +/* on entry: pi[0] is equitable */ +/*******************************************************10/21/2003****** + * Later add optimization: if Aut(G) <= Aut(G0) due to some additional * + * layer of coloring applied to G and the following is known about G0: * * + * * + * 0) canonical numbering of G should be same as that of G0 * + * * + * 1) canonical numbering as v= v0[n] {vertex number v from * + * G0 canonical number n) * + * 2) orbits of Aut(G0) as UnorderedPartition theta0 * + * * + * then when choosing next v[i] for refining the partition consider * + * only vertices from the Aut(G0) orbit of V(i), that is, only such * + * v[i] that: * + * * + * GetUnorderedPartitionMcrNode( &theta0, v[i] ) == * + * GetUnorderedPartitionMcrNode( &theta0, v0[i] ) * + ***********************************************************************/ + + +int CanonGraph( INCHI_CLOCK *ic, + CANON_GLOBALS *pCG, + int n, + int n_tg, + int n_max, + int bDigraph, + Graph *G, + Partition pi[], + AT_RANK *nSymmRank, + AT_RANK *nCanonRank, + AT_NUMB *nAtomNumberCanon, + CANON_DATA *pCD, + CANON_COUNTS *pCC, + ConTable **pp_zb_rho_inp, + ConTable **pp_zb_rho_out, + int LargeMolecules ) +{ + + /* bDigraph != 0 + means consider edges from atoms to t-groups + as directed, that is, do not include + t-group ranks in comparing neighbors + when refining partition + */ + + /* Always set + lab = true + dig = true + */ + + /* in the comments: + m = |zeta| + r = |rho| + + m < n or r < n in case pi[k] in P (i.e. satisfies Lemma 2.25) + + Just after passing point B: + =========================== + K = k-1 + wi = v[i], i = 1..K + Gamma(0) = Gamma = Aut(G)pi + Gamma(i) = Gamma(w1,w2,...,wi) pointwise stabilizer for i=1..K + zeta is a terminal node => + the coarsest equitable partition that fixes w1,...,wK is discrete => + Gamma(K)=1 + At point A only: + index = |Gamma(k-1)|/|Gamma(k)| + At points A and B: + size = |Gamma(k-1)| + theta = theta(Gamma(k-1)); + Gamma(k-1) = , where Y is the set of all automprhisms output up + to the present stage (in Step 10 = L10 ) + |Y| <= n - |theta| + */ + + AT_RANK *pCt = pCD->LinearCT; + /*int nMaxLenCt = pCD->nMaxLenLinearCT;*/ + int *nLenCt = &pCD->nLenLinearCT; + CANON_DATA *pCD1 = pCD; + + int i, k, k2, index, l, ok, ret=0, res; + int t_Lemma; /* hh: if pi[k-1] satisfies Lemma 2-25 then + t_Lemma = min{i| i=1..k && pi[i-1] satisfies Lemma 2-25}*/ + /* otherwise t_Lemma = k --> here this is always the case */ + int t_eq_zeta; /* ht: min{i|i=1..m && all terminal modes descended from or equal + to zeta(i) have been shown to be equivalent}. */ + int h_zeta; /* h: the longest common ancestor of zeta and nu is nu(h_zeta) */ + int h_rho; /* hb: the longest common ancestor of rho and nu is nu(h_rho) */ + int hz_rho; /* hzb: max{i|i=1..min(k,r) && Lambda(G,pi,nu(i)) == Lambda(G,pi,rho(i))} */ + int hz_zeta; /* hzf: max{i|i=1..min(k,m) && Lambda(G,pi,nu(i)) == Lambda(G,pi,zeta(i))} */ + int qzb_rho; /* Ct(Lambda[k]) - Ct(rho[k]) */ + double size; /* |Aut(G)| */ + + + int nNumLayers = (NULL != pCD->NumH) + (NULL != pCD->NumHfixed) + + /* (bDigraph && pCD->nLenLinearCT > pCD->nLenCTAtOnly)*/ /* ??? tautomeric */ + (NULL != pCD->iso_sort_key) +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + + (NULL != pCD->iso_sort_key_Hfixed) +#endif + ; + + + int dig = (bDigraph || nNumLayers); + int bSplitTautCompare = (bDigraph || nNumLayers); /* compare taut. H and tgroups connections after H */ + /* digraph: 1=>do not use Lemma 2.25, 0 => use */ + int lab = 1; /* label: 1=>find canonical numbering; + 0=>do not find canonical numbering, do not use rho */ + int r; /* |rho| */ + int bZetaEqRho = lab; + int bZetaIsomorph; + + long lNumEqlZeta; + + /*const int L = MAX_SET_SIZE;*/ + int L_curr_max_set_size = MAX_SET_SIZE; + + UnorderedPartition theta, theta_from_gamma; + + Cell *W; /* W[i] is the first non-trivial cell of pi[i+1] */ + Node *v; /* v[i] is in W[i] to create T(G,pi,nu[i+1]) */ + Node tvc, tvh; + S_CHAR *e, *qzb=NULL; /* qzb = NULL always */ + + /* current node CT */ + ConTable Lambda; + /* first leaf CT */ + ConTable zf_zeta; /* Ct for zeta, the first discovered terminal node */ + /* best leaf/node CT: find the greatest pzb_rho possibly subject to pzb_rho[k] <= pzb_rho_fix[k] condition */ + ConTable *pzb_rho = NULL; /* Ct for rho, the best discovered terminal node */ + /* fixed input CT: for all k pzb_rho[k] <= pzb_rho_fix[k]; at the end pzb_rho == pzb_rho_fix */ + ConTable *pzb_rho_fix = (pp_zb_rho_inp && *pp_zb_rho_inp)? *pp_zb_rho_inp:NULL; + + NodeSet Omega; /* MAX_SET_SIZE */ + NodeSet Phi; /* MAX_SET_SIZE */ + NodeSet cur_nodes; /* 1 each */ + + Transposition gamma; + + Partition zeta; /* the first discovered terminal node */ + Partition rho; /* the best discovered terminal node */ + + int nNumFoundGenerators=0; + int qzb_rho_fix = 0; + int hzb_rho_fix = 0; + int bRhoIsDiscrete = 1; + kLeast kLeast_rho[MAX_LAYERS]; + kLeast kLeast_rho_fix[MAX_LAYERS]; + int nOneAdditionalLayer; + int pzb_rho_fix_reached = 0; + int L_rho_fix_prev = 0, I_rho_fix_prev=-1, k_rho_fix_prev=0; + + if ( !LargeMolecules ) + L_curr_max_set_size = NORMALLY_ALLOWED_MAX_SET_SIZE; + + + /* Note: Layered comparison should be consistent, especially in layer numbers. + Layered comparison is implemented in: + CtFullCompare() + CtPartCompare() + GetOneAdditionalLayer() + + The partial comparison results in kLeast[] are used in + CtFullCompareLayers() + CtPartCompareLayers() + CtCompareLayersGetFirstDiff() + UpdateCompareLayers() + */ + + + nOneAdditionalLayer = GetOneAdditionalLayer( pCD1, pzb_rho_fix ); + + + /* next 2 lines for debug only */ + /* num_g++; */ + /* WriteGraph( G, n_tg, num_g, "V:\\IChI_v10\\Gordon-Graphs\\hard\\k06g08v312-alt.dre", "a+" ); */ + + /* memory allocation */ + + if ( 0 > SetBitCreate(pCG) ) + { + return -1; + } + if ( pzb_rho_fix && pzb_rho_fix->nLenCTAtOnly != pCD->nLenCTAtOnly ) + { + /* consistency check */ + return -2; + } + ok = 1; + + + ok &= UnorderedPartitionCreate( &theta, n_tg ); + ok &= UnorderedPartitionCreate( &theta_from_gamma, n_tg ); + + ok &= (NULL != (W = (Cell*)inchi_calloc( n_tg, sizeof(W[0])))); + ok &= (NULL != (v = (Node*)inchi_calloc( n_tg, sizeof(v[0])))); + ok &= (NULL != (e = (S_CHAR*)inchi_calloc( n_tg, sizeof(e[0])))); + +/* + ok &= (NULL != (v = (Node*)inchi_calloc( n_tg, sizeof(W[0])))); + ok &= (NULL != (e = (S_CHAR*)inchi_calloc( n_tg, sizeof(W[0])))); +*/ + + /* ok &= (NULL != (qzb = (S_CHAR*)inchi_calloc( n_tg, sizeof(W[0])))); */ + ok &= CTableCreate( &Lambda, n, pCD ); + ok &= CTableCreate( &zf_zeta, n, pCD ); + ok &= ( (pzb_rho = (ConTable *)inchi_calloc( 1, sizeof( *pzb_rho ) ) ) && + CTableCreate( pzb_rho, n, pCD ) ); + + ok &= NodeSetCreate( pCG, &Omega, n_tg, L_curr_max_set_size ); + ok &= NodeSetCreate( pCG, &Phi, n_tg, L_curr_max_set_size ); + ok &= NodeSetCreate( pCG, &cur_nodes, n_tg, 1 ); + + ok &= PartitionCreate( &zeta, n_tg); + ok &= PartitionCreate( &rho, n_tg); + ok &= TranspositionCreate( &gamma, n_tg ); + + INCHI_HEAPCHK + + + +/*L1:*/ + k = 1; + size = 1.0; + h_zeta = hz_rho = index = l = 0; + + if ( !ok ) { + goto exit_function; /* initialization failed */ + } + + UnorderedPartitionMakeDiscrete(&theta, n_tg); + t_Lemma = 2; + + pCC->lNumBreakTies = 0; + pCC->lNumDecreasedCT = 0; + pCC->lNumRejectedCT = 0; + pCC->lNumEqualCT = 1; + pCC->lNumTotCT = 0; + lNumEqlZeta = 1; + + hzb_rho_fix = 1; + + memset( kLeast_rho, 0, sizeof(kLeast_rho) ); + memset( kLeast_rho_fix, 0, sizeof(kLeast_rho_fix) ); + + if ( PartitionIsDiscrete( &pi[k-1], n_tg ) ) + { + /* added the following 3 lines to the original to create Ct */ + PartitionCopy( &rho, &pi[k-1], n_tg ); + CtPartFill( G, pCD, &pi[k-1], pzb_rho, 1, n, n_tg ); + CtPartInfinity( pzb_rho, qzb, 2 ); + pCC->lNumTotCT ++; + r = k; + /* goto L18; */ + goto exit_function; + } + + if ( !dig && PartitionSatisfiesLemma_2_25( &pi[0], n ) ) + t_Lemma = 1; + + /* + PartitionGetFirstCell( &pi[k-1], &W[k-1], k, n ); + v[k-1] = CellGetMinNode( &pi[k-1], &W[k-1], 0, pCD1 ); + CtPartClear( &Lambda, 1 ); + e[k-1] = 0; + */ + CtPartClear( &Lambda, 1 ); + INCHI_HEAPCHK + + +/* L2: reach the first leaf and save it in zeta and rho */ + while( k ) + { + /* the two next lines intentionally switched */ + /* Create equitable partition in pi[k] */ + PartitionGetFirstCell( &pi[k-1], W, k, n ); + v[k-1] = CellGetMinNode( &pi[k-1], &W[k-1], 0, pCD1 ); + e[k-1] = 0; + if ( dig || !PartitionSatisfiesLemma_2_25(&pi[k-1], n) ) + t_Lemma = k+1; + /* e[k-1] = 0; */ + { + Node vv = v[k-1]; + if ( 0 > (ret=PartitionColorVertex( pCG, G, &pi[k-1], vv /*v[k-1]*/, n, n_tg, n_max, bDigraph, 0 )) ) + { + goto exit_error; + } + } + pCC->lNumBreakTies ++; + k ++; + + CtPartFill( G, pCD, &pi[k-1], &Lambda, k-1, n, n_tg ); + + /* return -1; *//* debug only */ + /* if(h_zeta==0)goto L5; L5: */ + /* the first terminal node has not been reached yet */ + /* search for the predefined numbering */ + if ( pzb_rho_fix && QZFIX_OK(qzb_rho_fix) ) + { + qzb_rho_fix = CtPartCompare( &Lambda, pzb_rho_fix, qzb, kLeast_rho_fix, k-1, 1, bSplitTautCompare ); + if ( QZFIX_OK(qzb_rho_fix) ) + { + hzb_rho_fix = k; + } + } + + if ( lab && QZFIX_OK(qzb_rho_fix) ) /* DCh */ + CtPartCopy( pzb_rho, &Lambda, k-1 ); + CtPartCopy( &zf_zeta, &Lambda, k-1 ); + /*goto L4; L4:*/ + if ( PartitionIsDiscrete( &pi[k-1], n ) ) + { + break; /* goto L7; */ + } + /* goto L2; */ + } + + pCC->lNumTotCT ++; + + /* L7; L7: */ + /* if ( h_zeta == 0 ) goto L18; L18:*/ + h_zeta = t_eq_zeta = hz_zeta = k; + CtPartInfinity( &zf_zeta, NULL, k ); + /******************** <<<===== B **************************/ + PartitionCopy( &zeta, &pi[k-1], n_tg ); + if ( lab ) + { + if ( pzb_rho_fix ) + { + if ( 0 == qzb_rho_fix ) + { + qzb_rho_fix = CtFullCompare( &Lambda, pzb_rho_fix, 1, bSplitTautCompare ); + if ( qzb_rho_fix > 0 ) + { + hzb_rho_fix = 1; + } + } + if ( hzb_rho_fix > 1 ) + { + PartitionCopy( &rho, &pi[hzb_rho_fix-1], n_tg ); + /*CtPartInfinity( pzb_rho, qzb, k );*/ + } + hz_rho = h_rho = hzb_rho_fix; + bRhoIsDiscrete = (hzb_rho_fix == k); + if ( bRhoIsDiscrete ) + { + CtPartInfinity( pzb_rho, qzb, k ); + pzb_rho_fix_reached = !qzb_rho_fix; + CtCompareLayersGetFirstDiff( kLeast_rho_fix, nOneAdditionalLayer, + &L_rho_fix_prev, &I_rho_fix_prev, &k_rho_fix_prev ); + } + +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + else { + int stop = 1; + } +#endif + } + else + { + PartitionCopy( &rho, &pi[k-1], n_tg ); + hz_rho = h_rho = k; + CtPartInfinity( pzb_rho, qzb, k ); + } + qzb_rho = 0; + } + + r = k; + v[k-1] = INFINITY; /* DCh */ + CellMakeEmpty( W, k ); /* DCh */ + k --; + goto L13; + + +L2: + /* the two next lines intentionally switched */ + /* Create equitable partition in pi[k] */ + if ( 0 > (ret=PartitionColorVertex( pCG, G, &pi[k-1], v[k-1], + n, n_tg, n_max, bDigraph, 0 )) ) + { + goto exit_error; + } + pCC->lNumBreakTies ++; + k ++; + CtPartFill( G, pCD, &pi[k-1], &Lambda, k-1, n, n_tg ); + e[k-1] = 0; /* moved */ + v[k-1] = INFINITY; /* added by DCh. */ + CellMakeEmpty( W, k ); /* DCh */ + + if ( hz_zeta == k-1 && + 0 == CtPartCompare( &Lambda, &zf_zeta, NULL, NULL, k-1, 0, bSplitTautCompare ) ) + { + hz_zeta = k; /* max{k|Lambda(G,pi,nu(k))==Lambda(G,pi,zeta) } */ + } /* added */ + + + /* -- old code --- + if ( pzb_rho_fix && QZFIX_OK(qzb_rho_fix) ) { + qzb_rho_fix = CtPartCompare( &Lambda, pzb_rho_fix, qzb, kLeast_rho_fix, k-1, 1, bSplitTautCompare ); + if ( QZFIX_OK(qzb_rho_fix) ) { + hzb_rho_fix = k; + } else { + pCC->lNumRejectedCT ++; + } + } + */ + + /* --- new code ---*/ + if ( pzb_rho_fix && !qzb_rho_fix ) + { + qzb_rho_fix = CtPartCompare( &Lambda, pzb_rho_fix, qzb, kLeast_rho_fix, k-1, 1, bSplitTautCompare ); + if ( !qzb_rho_fix && bRhoIsDiscrete ) + { + qzb_rho_fix = CtPartCompareLayers( kLeast_rho_fix, L_rho_fix_prev, nOneAdditionalLayer ); + +#if ( FIX_ChCh_CONSTIT_CANON_BUG == 1 ) + if ( qzb_rho_fix ) { + int L_rho_fix_diff = abs(qzb_rho_fix)-1; + if ( L_rho_fix_diff < L_rho_fix_prev || + L_rho_fix_diff == L_rho_fix_prev && kLeast_rho_fix[L_rho_fix_diff].i < I_rho_fix_prev ) { + qzb_rho_fix = L_rho_fix_diff+1; /* positive difference will be rejected */ + } + } +#endif + +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + if ( qzb_rho_fix ) { + int stop = 1; /* debug only */ + } +#endif + } + if ( !QZFIX_OK(qzb_rho_fix) ) + { + pCC->lNumRejectedCT ++; + } + } + if ( pzb_rho_fix && QZFIX_OK(qzb_rho_fix) ) + { + hzb_rho_fix = k; + } + /* if (!lab) goto L3; */ + if ( lab && QZFIX_OK(qzb_rho_fix) ) + { + /* once the difference has been found it is meaningful as long as k increments */ + /* cur_qzb2 = CtPartCompare( &Lambda, pzb_rho, qzb, k-1 ); */ /* rho compare */ + if ( hz_rho == k-1 && !qzb_rho && bRhoIsDiscrete ) + { + int qzb_rho_temp = 0; + qzb_rho = CtPartCompare( &Lambda, pzb_rho, qzb, kLeast_rho, k-1, 0, bSplitTautCompare ); + /* old code */ + if ( !qzb_rho && pzb_rho_fix_reached && + nOneAdditionalLayer && 0 > kLeast_rho[nOneAdditionalLayer].k ) + { + qzb_rho_temp = -(nOneAdditionalLayer+1); + /* qzb_rho = -(nOneAdditionalLayer+1); *//* early rejection */ + } + /* new code */ + if ( !qzb_rho && bRhoIsDiscrete ) + { + qzb_rho = CtPartCompareLayers( kLeast_rho, L_rho_fix_prev, 0 ); +#if ( FIX_ChCh_CONSTIT_CANON_BUG == 1 ) + if ( qzb_rho ) + { + int L_rho_diff = abs(qzb_rho)-1; + if ( L_rho_diff < L_rho_fix_prev || + L_rho_diff == L_rho_fix_prev && kLeast_rho[L_rho_diff].i < I_rho_fix_prev ) { + qzb_rho = -(L_rho_diff+1); /* negative difference will be rejected */ + } + } +#endif + } + /* compare old results to new */ +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + if ( qzb_rho_temp && qzb_rho_temp != qzb_rho ) { + int stop = 1; /* */ + } +#endif + if ( !qzb_rho ) + { + hz_rho = k; + } else + if ( qzb_rho < 0 ) + { + pCC->lNumRejectedCT ++; + } + } + if ( qzb_rho > 0 || !qzb_rho && !bRhoIsDiscrete ) + { + /* found better rho */ + if ( !nNumLayers ) + { + CtPartCopy( pzb_rho, &Lambda, k-1 ); + } + } + } + + +/*L3:*/ + /*if ( hz_rho == k || (lab && qzb_rho >= 0 ) )*/ + /*if ( hz_zeta == k || hz_rho == k || (lab && qzb_rho >= 0 ) ) goto L4; else goto L6;*/ + if ( hz_zeta == k || hz_rho == k || + (lab && qzb_rho >= 0 && QZFIX_OK(qzb_rho_fix) ) ) + { + /*L4: check for possible isomorphism or found a better rho */ + if ( PartitionIsDiscrete( &pi[k-1], n ) ) + { + pCC->lNumTotCT ++; + goto L7; + } + PartitionGetFirstCell( &pi[k-1], W, k, n ); + v[k-1] = CellGetMinNode( &pi[k-1], &W[k-1], 0, pCD1 ); + if ( !dig && PartitionSatisfiesLemma_2_25(&pi[k-1], n) ) + { + ; /* found additional isomprphism */ + } + else + { + t_Lemma = k+1; + } + e[k-1] = 0; /* created new cell W[k-1] */ + goto L2; + } + + +L6: + /* a better rho or no good node was found at this level; return to smaller k */ + k2 = k; + k = inchi_min(t_Lemma-1, inchi_max(t_eq_zeta-1, hz_rho)); + if ( k2 == t_Lemma ) + goto L13; + + /* store isomorphism found from Lemma 2.25. should be dig=0 !!! */ + if ( dig ) + { +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + int stop = 1; +#endif + goto L13; + } + + l = inchi_min(l+1, L_curr_max_set_size); + PartitionGetMcrAndFixSet( pCG, &pi[t_Lemma-1], &Omega, &Phi, n_tg, l ); + goto L12; + + + +L7: + /* from L4: pi[k-1] is discrete */ + if ( h_zeta == 0 ) + { + /*goto L18;*/ /* error. the first T(nu) leaf was found */ + ret = CT_CANON_ERR; + goto exit_error; + } + if ( k != hz_zeta ) + goto L8; + /* here zeta^gamma == nu */ + /* if ( G^gamma == G ) goto L10; */ + if ( 0 == (res=CtFullCompare( &Lambda, &zf_zeta, 0, bSplitTautCompare )) ) + { + PartitionGetTransposition( &zeta, &pi[k-1], n_tg, &gamma ); + bZetaIsomorph = 1; /* for testing only */ + lNumEqlZeta ++; + goto L10; + } + else +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + { + int stop = 1; + } +#endif + /* !!! we should never come here !!! */ + if ( !nNumLayers ) + { + ret = -2; + goto exit_error; + } + + + +L8: /* here nu is discrete: check rho for being a bettere leaf or isomorphism */ + /*if ( !lab || qzb_rho < 0 || !QZFIX_OK(qzb_rho_fix) )*/ + if ( !lab || qzb_rho < 0 && ( !pzb_rho_fix || qzb_rho_fix > 0 ) ) + goto L6; + if ( pzb_rho_fix && kLeast_rho_fix && 0 == qzb_rho_fix ) + { + /* check for the rejection condition: Lambda > zb_rho_fix */ + if ( kLeast_rho_fix ) + { + int qzb_rho_fix_alt; + qzb_rho_fix = CtFullCompareLayers( kLeast_rho_fix ); + /* for debug only */ + qzb_rho_fix_alt = CtFullCompare( &Lambda, pzb_rho_fix, 1, bSplitTautCompare ); + if ( qzb_rho_fix != qzb_rho_fix_alt ) + { +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + int stop = 1; +#endif + qzb_rho_fix = qzb_rho_fix_alt; + } + /* end debug */ + } + else + { + qzb_rho_fix = CtFullCompare( &Lambda, pzb_rho_fix, 1, bSplitTautCompare ); + } + if ( !pzb_rho_fix_reached ) + { + pzb_rho_fix_reached = !qzb_rho_fix; + } + if ( 0 < qzb_rho_fix ) + { + /* Lambda > pzb_rho_fix, ignore this node */ + /* hzb_rho_fix = min( hzb_rho_fix, hz_rho ); */ /* ??? */ + qzb_rho_fix = 0; + goto L6; + } + qzb_rho_fix = 0; + } + + if ( qzb_rho < 0 ) + goto L6; + if ( qzb_rho > 0 || !bRhoIsDiscrete ) + goto L9; /* note: p67 says k > PartitionSize( &rho, n ) */ + if ( k < r ) { + goto L9; /* cannot understand it... */ + } + + /* !!! we should never come here if G(nu) != G(rho): CtPartCompare must be enough !!! */ + + /* if ( G(nu) > G(rho) ) goto L9; */ + if ( kLeast_rho ) + { + int cur_qzb_alt; + qzb_rho = CtFullCompareLayers( kLeast_rho ); + /* for debug only */ + cur_qzb_alt = CtFullCompare( &Lambda, pzb_rho, 0, bSplitTautCompare ); + if ( qzb_rho != cur_qzb_alt ) + { +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + int stop = 1; +#endif + qzb_rho = cur_qzb_alt; + } + /* end debug */ + } + else + { + qzb_rho = CtFullCompare( &Lambda, pzb_rho, 0, bSplitTautCompare ); + } + /* qzb_rho difference can be due to layers 1..MAX_LAYERS-1 only */ + if ( 0 < qzb_rho ) + { + /* CtFullCompare( &Lambda, pzb_rho, 0, bSplitTautCompare ); */ + qzb_rho = 0; + goto L9; + } + /* if ( G(nu) < G(rho) ) goto L6; */ + if ( 0 > qzb_rho ) + { + qzb_rho = 0; + goto L6; + } + /* nu^gamma == rho */ + if ( r != k ) { /* if() is for debug only */ + r = k; + } + + PartitionGetTransposition( &pi[k-1], &rho, n_tg, &gamma ); + bZetaIsomorph = 0; /* DCh */ + pCC->lNumEqualCT ++; + goto L10; + + + +L9: + /* rho := nu; */ + PartitionCopy( &rho, &pi[k-1], n_tg ); + if ( nNumLayers ) + { + CtFullCopy( pzb_rho, &Lambda ); + } + bZetaEqRho = 0; + qzb_rho = 0; + CtCompareLayersGetFirstDiff( kLeast_rho_fix, nOneAdditionalLayer, + &L_rho_fix_prev, &I_rho_fix_prev, &k_rho_fix_prev ); + memset( kLeast_rho, 0, sizeof(kLeast_rho) ); + h_rho = hz_rho = k; + CtPartInfinity( pzb_rho, qzb, k ); + pCC->lNumDecreasedCT ++; + pCC->lNumEqualCT = 1; + bRhoIsDiscrete = 1; + goto L6; + + +L10: /* discrete pi[k-1] && G^gamma == G */ + + pCC->lNumEqualCT += bZetaEqRho || !(bZetaIsomorph || qzb_rho); + l = inchi_min(l+1, L_curr_max_set_size); + /* Omega[l] := mcr(gamma); + Phi[l] := fix(gamma); + */ + TranspositionGetMcrAndFixSetAndUnorderedPartition( pCG, &gamma, &Omega, &Phi, + n_tg, l, &theta_from_gamma ); + + /* + if ( theta(gamma) <= theta ) goto L11; + theta := theta v theta(gamma); + UnorderedPartitionJoin() returns 0 if theta_from_gamma is finer than theta, + which means no changes in theta: theta_from_gamma ^ theta == theta. + */ + if ( !UnorderedPartitionJoin( &theta_from_gamma, &theta, n_tg ) ) + goto L11; /* no new isomorphism found */ + + /* Output gamma (it is the Aut(G) generator) -- omitted -- */ + nNumFoundGenerators ++; + /* if ( tvc in mcr(theta) ) goto L11; */ + if ( tvc == GetUnorderedPartitionMcrNode( &theta, tvc ) ) + goto L11; + k = h_zeta; + goto L13; + + + +L11: + k = lab? h_rho : h_zeta; /***Changed*** originally was k = h_rho; */ + + +L12: + /* if ( e[k-1] == 1 ) */ +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + if ( e[k-1] == 1 && v[k-1] == INFINITY ) + { + int stop = 1; /* testing only */ + } +#endif + if ( e[k-1] == 1 && v[k-1] != INFINITY ) + { + /* INFINITY for testing only */ + CellIntersectWithSet( pCG, &pi[k-1], &W[k-1], &Omega, l ); + } + + + +L13: + + if ( UserAction && USER_ACTION_QUIT == (*UserAction)() || + ConsoleQuit && (*ConsoleQuit)() ) { + ret = CT_USER_QUIT_ERR; + goto exit_error; + } + + if ( bInchiTimeIsOver(ic, pCD->ulTimeOutTime) ) + { + ret = CT_TIMEOUT_ERR; + goto exit_error; + } + + if ( k == 0 ) + goto exit_function; /* stop */ + + if ( lab && k < h_rho ) + { + /***Added***/ + h_rho = k; + } + + if ( k > h_zeta ) + { + if ( v[k-1] == INFINITY ) + { + /*** Added by DCh for testing only ****/ + k --; + goto L13; + } + goto L17; + } + if ( k == h_zeta ) + goto L14; + h_zeta = k; + tvc = tvh = CellGetMinNode( &pi[k-1], &W[k-1], 0, pCD1 ); + + + +L14: + /* if v[k] and tvh are in the same cell of theta then index ++ */ + if ( GetUnorderedPartitionMcrNode( &theta, v[k-1] ) == + GetUnorderedPartitionMcrNode( &theta, tvh ) ) + { + index ++; + } + v[k-1] = CellGetMinNode( &pi[k-1], &W[k-1], v[k-1], pCD1 ); + + if ( v[k-1] == INFINITY ) + goto L16; + if ( v[k-1] != GetUnorderedPartitionMcrNode( &theta, v[k-1] ) ) + goto L14; + + + +L15: + t_Lemma = inchi_min(t_Lemma, k+1); + hz_zeta = inchi_min(hz_zeta, k); +/* + if ( lab && hz_rho >= k ) { + hz_rho = k; + qzb_rho = 0; + } +*/ + if ( lab ) + { + if ( hz_rho >= k /*-1*/ ) + qzb_rho = 0; + if ( hz_rho > k ) + hz_rho = k; + UpdateCompareLayers( kLeast_rho, hz_rho ); + } + if ( pzb_rho_fix ) + { + if ( hzb_rho_fix >= k /*-1*/ ) + qzb_rho_fix = 0; + if ( hzb_rho_fix > k ) + hzb_rho_fix = k; + UpdateCompareLayers( kLeast_rho_fix, hzb_rho_fix ); + } + + goto L2; + + + +L16: + if ( t_eq_zeta == k+1 && index == CellGetNumberOfNodes( &pi[k-1], &W[k-1] ) ) + t_eq_zeta = k; + + size *= (double)index; + /******************** <<<===== A **************************/ + /* passed K times after passing point A. At these passes + k = K, K-1, ..., 1 in this order + */ + index = 0; + k --; + goto L13; + + + +L17: + /* if ( e[k-1] == 0 ) */ +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + if ( e[k-1] == 0 && v[k-1] == INFINITY ) + { + /* testing only */ + int stop = 1; /* */ + } +#endif + /* + if ( e[k] == 0 set W[k] = Intersection(W[k], Omega[i]) for each i = 1..l, + such that {v[1]..v[k-1]} in Phi[i] + */ + if ( e[k-1] == 0 && v[k-1] != INFINITY ) /* Added v[k-1]!=... DCh */ + { + NodeSetFromVertices( pCG, &cur_nodes, 1, v, k-1 ); + for ( i = 1; i <= l; i ++ ) + { + if ( AllNodesAreInSet( &cur_nodes, 1, &Phi, i ) ) + { + CellIntersectWithSet( pCG, &pi[k-1], &W[k-1], &Omega, i ); + } + } + } + + e[k-1] = 1; + v[k-1] = CellGetMinNode( &pi[k-1], &W[k-1], v[k-1], pCD1 ); + if ( v[k-1] != INFINITY ) + goto L15; + k --; + goto L13; +/* L18: see above */ + + + +exit_function: + + /* CtPartFill( G, pCD, &rho, pzb_rho, 1, n, n_tg ); */ + if ( !bRhoIsDiscrete ) + { + ret = CT_CANON_ERR; + goto exit_error; + } + + if ( pzb_rho_fix ) + { + qzb_rho_fix = CtFullCompare( pzb_rho_fix, pzb_rho, 1, bSplitTautCompare ); + if ( qzb_rho_fix ) + { + ret = CT_CANON_ERR; + goto exit_error; + } + } + + /* SymmRank */ + memset( nSymmRank, 0, n_tg * sizeof(nSymmRank[0]) ); + for ( i = 0; i < n_tg; i ++ ) + { + k = rho.AtNumber[i]; + k2 = (int)GetUnorderedPartitionMcrNode( &theta, (AT_NUMB)(k+1) ) - 1; + if ( !nSymmRank[k2] || nSymmRank[k2] > rho.Rank[k] ) + { + nSymmRank[k2] = rho.Rank[k]; + } + } + for ( i = 0; i < n_tg; i ++ ) + { + k = rho.AtNumber[i]; + k2 = (int)GetUnorderedPartitionMcrNode( &theta, (AT_NUMB)(k+1) ) - 1; + nSymmRank[k] = nSymmRank[k2]; + } + /* CanonRank, nAtomNumberCanon */ + memcpy( nCanonRank, rho.Rank, n_tg * sizeof(nCanonRank[0]) ); + memcpy( nAtomNumberCanon, rho.AtNumber, n_tg * sizeof(nAtomNumberCanon[0]) ); + /* LinearCT */ + *nLenCt = pzb_rho->lenCt-1; + if ( pCt ) { + memcpy( pCt, pzb_rho->Ctbl, *nLenCt*sizeof(pCt[0]) ); + } + pCC->lNumTotCT = pCC->lNumDecreasedCT + pCC->lNumRejectedCT + pCC->lNumEqualCT; + pCC->dGroupSize = size; + pCC->lNumGenerators = nNumFoundGenerators; + pCC->lNumStoredIsomorphisms = l; + /* Note: check nNumFoundGenerators */ + + if ( pp_zb_rho_out && !*pp_zb_rho_out ) + { + *pp_zb_rho_out = pzb_rho; + pzb_rho = NULL; + } + + + +exit_error: + INCHI_HEAPCHK + + UnorderedPartitionFree( &theta ); + UnorderedPartitionFree( &theta_from_gamma ); + if ( W ) + inchi_free( W ); + if ( v ) + inchi_free( v ); + if ( e ) + inchi_free( e ); + if ( qzb ) + inchi_free( qzb ); + CTableFree( &Lambda ); + CTableFree( &zf_zeta ); + if ( pzb_rho ) + { + CTableFree( pzb_rho ); + inchi_free( pzb_rho ); + pzb_rho = NULL; + } + /* CTableFree( &zf_zeta2 ); */ + NodeSetFree( pCG, &Omega ); + NodeSetFree( pCG, &Phi ); + /* NodeSetFree( &mcr_theta, n, 1 ); */ + NodeSetFree( pCG, &cur_nodes ); + PartitionFree( &zeta ); + /* PartitionFree( &zeta2 ); */ + PartitionFree( &rho ); + TranspositionFree( &gamma ); + + return ret; +} + + + + +/********************************************************************************************** + * SetInitialRanks2: Set initial ranks in nRank according to pAtomInvariant[] values + * Make sure enough prines have been generated. + **********************************************************************************************/ +/* Upon exit: */ +/* nAtomNumber[i]: number (from 0) of an atom in the ith (from 0) position of the sorted order */ +/* nNewRank[i]: initial rank of the atom[i] based on atom invariants; from 1 to num_atoms */ +/* Return value: Number of different ranks */ +int SetInitialRanks2( int num_atoms, + ATOM_INVARIANT2* pAtomInvariant2, + AT_RANK *nNewRank, + AT_RANK *nAtomNumber, + CANON_GLOBALS *pCG ) +{ + int i, nNumDiffRanks; + AT_RANK nCurrentRank; + + for ( i = 0; i < num_atoms; i++ ) + nAtomNumber[i] = (AT_RANK)i; + + /* global for qsort */ + pCG->m_pAtomInvariant2ForSort = pAtomInvariant2; + + inchi_qsort( pCG, nAtomNumber, num_atoms, sizeof(nAtomNumber[0]), CompAtomInvariants2 ); + + /* nNewRank[i]: non-decreading order; do not increment nCurrentRank */ + /* if consecutive sorted atom invariants are identical */ + + for ( i=num_atoms-1, nCurrentRank=nNewRank[nAtomNumber[i]] = (AT_RANK)num_atoms, nNumDiffRanks = 1; 0 < i ; i -- ) + { + /* Note: CompAtomInvariants2Only() in following line implicitly reads pAtomInvariant2 pointed by pAtomInvariant2ForSort */ + if ( CompAtomInvariants2Only( &nAtomNumber[i-1], &nAtomNumber[i] , pCG) ) + { + nNumDiffRanks ++; + nCurrentRank = (AT_RANK)i; + } + nNewRank[nAtomNumber[i - 1]] = nCurrentRank; + } + + return nNumDiffRanks; +} + + +/****************************************************************************/ +void FillOutAtomInvariant2( sp_ATOM* at, + int num_atoms, + int num_at_tg, + ATOM_INVARIANT2* pAtomInvariant, + int bIgnoreIsotopic, + int bHydrogensInRanks, + int bHydrogensFixedInRanks, + int bDigraph, + int bTautGroupsOnly, + T_GROUP_INFO *t_group_info ) +{ + int i, k, j, i_t_group; + /* tautomers */ + T_GROUP *t_group=NULL; + int num_t_groups = 0; + int num_tautomer_iso = 0; +#define ELEM_NAME_LEN 2 + char ChemElements[ELEM_NAME_LEN*NUM_CHEM_ELEMENTS+ELEM_NAME_LEN]; + char CurElement[ELEM_NAME_LEN + ELEM_NAME_LEN], *pCurElem; + int nNumChemElements = 0; + int nNumHydrogenAtoms = 0; + int nNumCarbonAtoms = 0; + memset( ChemElements, 0, sizeof(ChemElements) ); + memset( CurElement, 0, sizeof(CurElement) ); + nNumChemElements = 0; + + if ( num_at_tg > num_atoms && t_group_info ) + { + t_group = t_group_info->t_group; + num_t_groups = t_group_info->num_t_groups; + num_tautomer_iso = t_group_info->bIgnoreIsotopic? 0 : T_NUM_ISOTOPIC; + } + + if ( !bTautGroupsOnly ) + { + + for ( i = 0; i < num_atoms; i ++ ) + { + if ( !strcmp( at[i].elname, "C" ) ) + { + nNumCarbonAtoms ++; + } + else if ( !strcmp( at[i].elname, "H" ) || + !strcmp( at[i].elname, "D" ) || + !strcmp( at[i].elname, "T" ) ) + { + nNumHydrogenAtoms ++; + } + else + { + CurElement[0] = at[i].elname[0]; + CurElement[1] = at[i].elname[1]? at[i].elname[1] : ' '; + if ( ! (pCurElem = strstr( ChemElements, CurElement ) ) ) { + strcat( ChemElements, CurElement ); + nNumChemElements ++; + } + } + } + if ( nNumChemElements > 1 ) + { + qsort( ChemElements, nNumChemElements, ELEM_NAME_LEN, CompChemElemLex ); + } + if ( nNumCarbonAtoms ) + { + if ( nNumChemElements ) + { + memmove( ChemElements + ELEM_NAME_LEN, ChemElements, ELEM_NAME_LEN*nNumChemElements ); + } + ChemElements[0] = 'C'; + ChemElements[1] = ' '; + nNumChemElements ++; + } + if ( nNumHydrogenAtoms ) + { + ChemElements[ ELEM_NAME_LEN*nNumChemElements ] = 'H'; + ChemElements[ ELEM_NAME_LEN*nNumChemElements+1 ] = ' '; + nNumChemElements ++; + } + + /* general */ + for ( i = 0; i < num_atoms; i ++ ) + { + memset( &pAtomInvariant[i], 0, sizeof(pAtomInvariant[0]) ); + CurElement[0] = at[i].elname[0]; + CurElement[1] = at[i].elname[1]? at[i].elname[1] : ' '; + pCurElem = strstr( ChemElements, CurElement ); + if ( pCurElem ) + { + j = (int) (pCurElem - ChemElements)/ELEM_NAME_LEN + 1; + } + else + { + j = nNumChemElements; /* must be D or T */ + } + /* at[i].hill_type = (U_CHAR) j; */ + pAtomInvariant[i].val[AT_INV_HILL_ORDER] = j; + + pAtomInvariant[i].val[AT_INV_NUM_CONNECTIONS] = at[i].valence; + if ( bHydrogensInRanks ) + { + pAtomInvariant[i].val[AT_INV_NUM_H] = ((t_group && at[i].endpoint>0)? 0 : at[i].num_H); + } + if ( bHydrogensFixedInRanks ) + { + pAtomInvariant[i].val[AT_INV_NUM_H_FIX] = ((t_group && at[i].endpoint>0)? at[i].num_H : 0); + } + if ( !bDigraph && t_group && (i_t_group = (int)at[i].endpoint-1) >= 0 && i_t_group < num_t_groups ) + { + pAtomInvariant[i].val[AT_INV_NUM_TG_ENDPOINTS] = t_group[i_t_group].nNumEndpoints; + for ( j = 0; j < T_NUM_NO_ISOTOPIC; j ++ ) + { + pAtomInvariant[i].val[AT_INV_TG_NUMBERS+j] = t_group[i_t_group].num[j]; + } + for ( j = 0; j < num_tautomer_iso; j ++ ) + { + pAtomInvariant[i].val[AT_INV_TAUT_ISO+j] = t_group[i_t_group].num[j + T_NUM_NO_ISOTOPIC]; + } + } + pAtomInvariant[i].iso_sort_key = bIgnoreIsotopic? 0 : at[i].iso_sort_key; + } + } else + { + /* fill tautomeric groups only */ + memset ( pAtomInvariant, 0, num_at_tg*sizeof(pAtomInvariant[0]) ); + } + + /**************************************/ + /* tautomeric groups */ + /**************************************/ + for ( i = num_atoms; i < num_at_tg; i ++ ) + { + + k = i - num_atoms; + memset( &pAtomInvariant[i], 0, sizeof(pAtomInvariant[0]) ); + if ( !t_group ) + continue; + /* make sure ranks of t-groups are larger than that of any atom */ + /* greater than for any real atom */ + pAtomInvariant[i].val[AT_INV_HILL_ORDER] = bTautGroupsOnly? num_at_tg : nNumChemElements+1; + /* greater than for any real atom */ + pAtomInvariant[i].val[AT_INV_NUM_CONNECTIONS] = MAXVAL+1; + if ( k < num_t_groups ) + { + pAtomInvariant[i].val[AT_INV_NUM_TG_ENDPOINTS] = t_group[k].nNumEndpoints; + for ( j = 0; j < T_NUM_NO_ISOTOPIC; j ++ ) + { + pAtomInvariant[i].val[AT_INV_TAUT_ISO+j] = t_group[k].num[j]; + } + for ( j = 0; j < num_tautomer_iso; j ++ ) + { + pAtomInvariant[i].val[AT_INV_TAUT_ISO+j] = t_group[k].num[j + T_NUM_NO_ISOTOPIC]; + } + } + } +} + + +/*****************************************************************************/ +void CleanNumH( NUM_H *NumH, int len ) +{ + int i; + if ( NumH ) + { + for ( i = 0; i < len; i ++ ) + { + if ( NumH[i] == EMPTY_H_NUMBER ) + { + NumH[i] = 0; + } + else + { + NumH[i] -= BASE_H_NUMBER; + } + } + } +} + + +/*****************************************************************************/ +int CleanCt( AT_RANK *Ct, int len ) +{ + if ( Ct && Ct[len] == EMPTY_CT ) + { + Ct[len] = 0; + return 1; + } + + return 0; +} + + +/*****************************************************************************/ +void CleanIsoSortKeys( AT_ISO_SORT_KEY * isk, int len ) +{ + int i; + if ( isk ) { + for ( i = 0; i < len; i ++ ) { + if ( isk[i] == EMPTY_ISO_SORT_KEY ) { + isk[i] = 0; + } + } + } +} + + +/*****************************************************************************/ + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) +void MergeCleanIsoSortKeys( AT_ISO_SORT_KEY * isk1, AT_ISO_SORT_KEY * isk2, int len ) +{ + int i; + AT_ISO_SORT_KEY k1, k2; + if ( isk1 && isk2 ) { + for ( i = 0; i < len; i ++ ) + { + k1 = (isk1[i] == EMPTY_ISO_SORT_KEY)? 0 : isk1[i]; + k2 = (isk2[i] == EMPTY_ISO_SORT_KEY)? 0 : isk2[i]; + isk1[i] = k1 | k2; + } + } + else if ( isk1 ) + { + CleanIsoSortKeys( isk1, len ); + } +} +#endif + +#define FREE_CONTABLE( X) if (X) {CTableFree( X);inchi_free( X);} +#define FREE_ARRAY( X) if (X) inchi_free( X); + +/*****************************************************************************/ +void DeAllocBCN( BCN *pBCN ) +{ + int i, k; + FTCN *ftcn; + if ( !pBCN ) + return; + if ( pBCN->pRankStack ) + { + for ( i = 0; i < pBCN->nMaxLenRankStack; i ++ ) + { + FREE_ARRAY( pBCN->pRankStack[i] ) + } + FREE_ARRAY( pBCN->pRankStack ) + } + for ( k = 0; k < TAUT_NUM; k ++ ) + { + ftcn = pBCN->ftcn + k; + FreeNeighList( ftcn->NeighList ); + FREE_ARRAY( ftcn->LinearCt ) + PartitionFree( &ftcn->PartitionCt ); + FREE_ARRAY( ftcn->nSymmRankCt ) + FREE_ARRAY( ftcn->nNumHOrig ) + FREE_ARRAY( ftcn->nNumH ) + FREE_ARRAY( ftcn->nNumHOrigFixH ) + FREE_ARRAY( ftcn->nNumHFixH ) + PartitionFree( &ftcn->PartitionCtIso ); + FREE_ARRAY( ftcn->nSymmRankCtIso ) + FREE_ARRAY( ftcn->iso_sort_keys ) + FREE_ARRAY( ftcn->iso_sort_keysOrig ) + FREE_ARRAY( ftcn->iso_exchg_atnos ) + FREE_ARRAY( ftcn->iso_exchg_atnosOrig ) + } +} + +#undef FREE_CONTABLE +#undef FREE_ARRAY + + + +/*****************************************************************************/ + +#if ( bRELEASE_VERSION == 0 && FIND_CANON_NE_EQUITABLE == 1 ) + +/* debug: find whether canonical equivalence is different from equitable partition */ +int bCanonIsFinerThanEquitablePartition( int num_atoms, + sp_ATOM* at, + AT_RANK *nSymmRank ) +{ + AT_RANK *nRank = NULL; + AT_RANK *nAtomNumber = NULL; + AT_RANK *nTempRank = NULL; + AT_RANK nCurSymm, nCurRank; + ATOM_INVARIANT2 *pAtomInvariant = NULL; + NEIGH_LIST *NeighList = NULL; + + int nNumCurrRanks, i, is, ir, j; + long lCount; + int bIsNotSame = 0; + + if ( at && nSymmRank ) + { + if ( !(nRank = (AT_RANK*)inchi_calloc( num_atoms, sizeof(nRank[0]))) || + !(nAtomNumber = (AT_RANK*)inchi_calloc( num_atoms, sizeof(nAtomNumber[0]))) || + !(nTempRank = (AT_RANK*)inchi_calloc( num_atoms, sizeof(nTempRank[0]))) || + !(pAtomInvariant = (ATOM_INVARIANT2 *)inchi_calloc( num_atoms, sizeof(pAtomInvariant[0]))) + ) + { + goto exit_err; + } + if ( !(NeighList = CreateNeighList( num_atoms, num_atoms, at, 0, NULL )) ) + { + goto exit_err; + } + + FillOutAtomInvariant2( at, + num_atoms, + num_atoms, + pAtomInvariant, + 1 /*bIgnoreIsotopic*/, + 1 /*bHydrogensInRanks*/, + 1 /*bHydrogensFixedInRanks*/, + 0 /*bTaut=bDigraph*/, + 0 /* bTautGroupsOnly */, + NULL /*t_group_info*/ ); + + /* initial partitioning of a hydrogenless skeleton: create equitable partition (assign initial ranks) */ + nNumCurrRanks = SetInitialRanks2( num_atoms, pAtomInvariant, nRank, nAtomNumber, pCG ); + + lCount = 0; + /* make equitable partition in pBCN->pRankStack[0,1] */ + nNumCurrRanks = DifferentiateRanks2( pCG, num_atoms, NeighList, + nNumCurrRanks, nRank, + nTempRank, nAtomNumber, &lCount, 0 /* 0 means use qsort */ ); + + + /* at this point the equitable partition is in nRank; the order of atoms is in nAtomNumber*/ + /* compare */ + nCurSymm = nCurRank = 0; + for ( i = 0; i < num_atoms; i ++ ) + { + j = (int)nAtomNumber[i]; + if ( nCurSymm != nSymmRank[j] ) + { + nCurSymm = nSymmRank[j]; + is = i; + } + if ( nCurRank != nRank[j] ) + { + nCurRank = nRank[j]; + ir = i; + } + if ( is != ir ) + { + bIsNotSame = 1; + break; + } + } + } + +exit_err: + if ( nRank ) + inchi_free( nRank ); + if ( nAtomNumber ) + inchi_free( nAtomNumber ); + if ( nTempRank ) + inchi_free( nTempRank ); + if ( pAtomInvariant ) + inchi_free( pAtomInvariant ); + if ( NeighList ) + FreeNeighList( NeighList ); + + return bIsNotSame; +} + +#endif + +/*****************************************************************************/ +int GetBaseCanonRanking( INCHI_CLOCK *ic, + int num_atoms, + int num_at_tg, + sp_ATOM* at[], + T_GROUP_INFO *t_group_info, + ATOM_SIZES s[], + BCN *pBCN, + struct tagInchiTime *ulTimeOutTime, + CANON_GLOBALS *pCG, + int bFixIsoFixedH, + int LargeMolecules) +{ + int ret = 0; + int iBase; /* base structure index, always valid; = TAUT_YES except special fully non-taut mode */ + int iOther; /* other than basic structure index, usually non-taut; may be = iBase */ + int bReqNonTaut; /* 1 => requested non-tautomeric results */ + int bReqTaut; /* 1 => requested tautomeric results and the base structure is tautomeric */ + int bChanged; + + sp_ATOM *at_base = NULL; + sp_ATOM *at_other = NULL; + + int bTautIgnoreIsotopic = 0; + /*int bIgnoreIsotopic = 0;*/ + int nNumCurrRanks = 0; + int nMaxLenRankStack = 0; + int num_max = num_at_tg; + long lCount; + + /* local allocations */ + ATOM_INVARIANT2 *pAtomInvariant = NULL; + NEIGH_LIST *NeighList[TAUT_NUM]; + ConTable *Ct_Temp = NULL; + + /* initial partition for canonicalization */ + AT_RANK *nRank = NULL; + AT_NUMB *nAtomNumber = NULL; + + /* canonicalization output */ + + ConTable *Ct_NoH = NULL; + AT_RANK *nCanonRankNoH = NULL; + AT_NUMB *nAtomNumberCanonNoH = NULL; + AT_RANK *nSymmRankNoH = NULL; + + ConTable *Ct_NoTautH = NULL; + AT_RANK *nSymmRankNoTautH = NULL; + AT_RANK *nCanonRankNoTautH = NULL; + AT_NUMB *nAtomNumberCanonNoTautH = NULL; + NUM_H *numHNoTautH = NULL; + int lenNumHNoTautH; + int maxlenNumHNoTautH; + + ConTable *Ct_Base = NULL; + AT_RANK *nSymmRankBase = NULL; + AT_RANK *nCanonRankBase = NULL; + AT_NUMB *nAtomNumberCanonBase = NULL; + NUM_H *numH = NULL; + int lenNumH; + int maxlenNumH = 0; + +#if ( USE_AUX_RANKING == 1 ) + AT_RANK *nRankAux = NULL; + AT_NUMB *nAtomNumberAux = NULL; + ATOM_INVARIANT2 *pAtomInvariantAux= NULL; +#endif + + ConTable *Ct_FixH = NULL; + AT_RANK *nSymmRankFixH = NULL; + AT_RANK *nCanonRankFixH = NULL; + AT_NUMB *nAtomNumberCanonFixH = NULL; + NUM_H *NumHfixed = NULL; + int maxlenNumHfixed; + + /* isotopic canonicalization */ + + ConTable *Ct_NoTautHIso = NULL; + AT_RANK *nSymmRankNoTautHIso = NULL; + AT_RANK *nCanonRankNoTautHIso = NULL; + AT_NUMB *nAtomNumberCanonNoTautHIso = NULL; + AT_ISO_SORT_KEY *iso_sort_key_NoTautH = NULL; + int maxlen_iso_sort_key_NoTautH; + int len_iso_sort_key_NoTautH; + int num_iso_NoTautH, num_iso_NoAuxBase; + + ConTable *Ct_BaseIso = NULL; + AT_RANK *nSymmRankBaseIso = NULL; + AT_RANK *nCanonRankBaseIso = NULL; + AT_NUMB *nAtomNumberCanonBaseIso = NULL; + + AT_ISO_SORT_KEY *iso_sort_keyBase = NULL; + int maxlen_iso_sort_keyBase; + int len_iso_sort_keyBase; + + int bUseIsoAuxBase[TAUT_NUM]; + S_CHAR *iso_exchg_atnos = NULL; + int len_iso_exchg_atnos; + int maxlen_iso_exchg_atnos; + int num_iso_Base; + + AT_ISO_SORT_KEY iso_sort_key; + + ConTable *Ct_FixHIso = NULL; + AT_RANK *nSymmRankFixHIso = NULL; + AT_RANK *nCanonRankFixHIso = NULL; + AT_NUMB *nAtomNumberCanonFixHIso = NULL; + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + AT_ISO_SORT_KEY iso_sort_key2; + AT_ISO_SORT_KEY *iso_sort_key_Hfixed = NULL; + int maxlen_iso_sort_key_Hfixed; + int len_iso_sort_key_Hfixed; + int num_iso_Hfixed; +#endif + + AT_RANK *nTempRank = NULL; + + CANON_DATA pCD[3]; /* = &CanonData; */ + CANON_COUNTS CanonCounts; + CANON_COUNTS *pCC = &CanonCounts; + + int i, j, k, m; + int nCanonFlags[2]; + + /* */ + int iflag; + + memset (pCD, 0, sizeof(pCD)); + memset (pCC, 0, sizeof(pCC[0])); + memset ( bUseIsoAuxBase, 0, sizeof(bUseIsoAuxBase) ); + memset ( nCanonFlags, 0, sizeof(nCanonFlags) ); + NeighList[TAUT_NON] = NULL; + NeighList[TAUT_YES] = NULL; + + /* select base structure, find whether it is tautomeric or not */ + + if ( at[TAUT_YES] && s[TAUT_YES].nLenCT && + t_group_info && (s[TAUT_YES].nLenLinearCTTautomer > 0 && /* ordinary tautomerism */ + t_group_info->t_group && t_group_info->num_t_groups > 0 || + /* protons have been moved */ + (t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) || + /* tautomerism due to possible isotopic proton exchange */ + t_group_info->nNumIsotopicEndpoints > 1 && + (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE|TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE)) ) ) + { + /* tautomeric: (1) has tautomeric atoms OR + (2) H-atoms have been rearranged due to proton addition/removal OR + (3) Found isotopic H-atoms on tautomeric or hetero atoms + */ + iBase = TAUT_YES; + bReqTaut = 1; + bUseIsoAuxBase[iBase] = (s[iBase].nLenIsotopicEndpoints > 1) && + (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE|TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE)); + if ( at[TAUT_NON] && s[TAUT_NON].nLenCT ) + { + iOther = TAUT_NON; /* tautomeric and non-tautomeric */ + bReqNonTaut = 1; + } + else + { + iOther = iBase; /* tautomeric only */ + bReqNonTaut = 0; + } + } + + else if ( at[TAUT_NON] && s[TAUT_NON].nLenCT ) + { + /* force pure non-tautomeric processing; happens for testing only */ + iBase = TAUT_NON; + bReqTaut = 0; + iOther = iBase; + bReqNonTaut = 1; + num_at_tg = num_atoms; + } + + else if ( at[TAUT_YES] && s[TAUT_YES].nLenCT ) + { + /* although the user requested tautomeric processing, tautomerism has not been found */ + /* however, the results should be saved in the TAUT_YES elements of the arrays */ + iBase = TAUT_YES; + bReqTaut = 0; + bUseIsoAuxBase[iBase] = (s[iBase].nLenIsotopicEndpoints > 1); + iOther = iBase; + bReqNonTaut = 1; + num_at_tg = num_atoms; + } + else + { + ret = CT_UNKNOWN_ERR; + goto exit_error; + } + + + if ( bReqTaut ) + { + /* save "process isotopic" mark; temporarily set it to NO */ + bTautIgnoreIsotopic = t_group_info->bIgnoreIsotopic; + t_group_info->bIgnoreIsotopic = 1; + } + lenNumH = num_atoms; + + /* isotopic canonicalization */ + num_iso_NoTautH = 0; + len_iso_sort_key_NoTautH = 0; + maxlen_iso_sort_key_NoTautH = 0; + num_iso_Base = 0; + len_iso_sort_keyBase = 0; + maxlen_iso_sort_keyBase = 0; + len_iso_exchg_atnos = 0; + maxlen_iso_exchg_atnos = 0; + len_iso_exchg_atnos = 0; + maxlen_iso_exchg_atnos = 0; + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + num_iso_Hfixed = + len_iso_sort_key_Hfixed = + maxlen_iso_sort_key_Hfixed = 0; +#endif + + /* prepare initial data */ + at_base = at[iBase]; + at_other = at[iOther]; + pAtomInvariant = (ATOM_INVARIANT2 *)inchi_calloc( num_max, sizeof(pAtomInvariant[0]) ); + nSymmRankNoH = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankNoH[0] ) ); + nCanonRankNoH = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankNoH[0] ) ); + nAtomNumberCanonNoH = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonNoH[0]) ); + nRank = (AT_RANK *) inchi_calloc( num_max, sizeof(nRank[0] ) ); + nAtomNumber = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumber[0]) ); + nTempRank = (AT_RANK *) inchi_calloc( num_max, sizeof(nTempRank[0] ) ); + + if ( !pAtomInvariant || + !nSymmRankNoH || !nCanonRankNoH || !nAtomNumberCanonNoH || + !nRank || !nAtomNumber || !nTempRank ) + { + goto exit_error_alloc; + } + +#if ( USE_AUX_RANKING == 1 ) + nRankAux = (AT_RANK *) inchi_calloc( num_max, sizeof(nRankAux[0] ) ); + nAtomNumberAux = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberAux[0] ) ); + pAtomInvariantAux = (ATOM_INVARIANT2 *)inchi_malloc( num_max * sizeof(pAtomInvariantAux[0]) ); + if ( !nRankAux || !nAtomNumberAux || !pAtomInvariantAux ) + { + goto exit_error_alloc; + } +#endif + + if ( bReqTaut ) + { + if ( !(NeighList[TAUT_YES] = + CreateNeighList( num_atoms, num_at_tg, at_base, 0, t_group_info )) ) + goto exit_error_alloc; + /* needed for the hydrogenless structure */ + if ( !(NeighList[TAUT_NON] = + CreateNeighList( num_atoms, num_atoms, at_base, 0, NULL )) ) + goto exit_error_alloc; + } + else + { + if ( !(NeighList[TAUT_NON] = + CreateNeighList( num_atoms, num_atoms, at_base, 0, NULL )) ) + goto exit_error_alloc; + NeighList[TAUT_YES] = NULL; + + INCHI_HEAPCHK + } + + + /* avoid memory leaks in case of error */ + /* + pBCN->ftcn[TAUT_NON].NeighList = NeighList[TAUT_NON]; + pBCN->ftcn[TAUT_YES].NeighList = NeighList[TAUT_YES]; + */ + pBCN->nMaxLenRankStack = 0; + pBCN->num_max = num_max; /* allocated nRank[] arrays lengths in pRankStack */ + pBCN->num_at_tg = num_at_tg; /* all of the following arrays have this length */ + pBCN->num_atoms = num_atoms; + pBCN->ulTimeOutTime = ulTimeOutTime; + + /* initial partitioning of a hydrogenless skeleton: fill out the inveriant */ + FillOutAtomInvariant2( at_base, + num_atoms, + num_atoms, + pAtomInvariant, + 1 /*bIgnoreIsotopic*/, + 0 /*bHydrogensInRanks*/, + 0 /*bHydrogensFixedInRanks*/, + 0 /*bTaut=bDigraph*/, + 0 /* bTautGroupsOnly */, + NULL /*t_group_info*/ ); + + /* initial partitioning of a hydrogenless skeleton: create equitable partition (assign initial ranks) */ + nNumCurrRanks = SetInitialRanks2( num_atoms, pAtomInvariant, nRank, nAtomNumber, pCG ); + + lCount = 0; + + /* make equitable partition in pBCN->pRankStack[0,1] */ + nNumCurrRanks = DifferentiateRanks2( pCG, + num_atoms, + NeighList[TAUT_NON], + nNumCurrRanks, nRank, + nTempRank, + nAtomNumber, + &lCount, + 0 /* 0 means use qsort */ ); + + /* allocate partition stack */ + nMaxLenRankStack = 2*(num_at_tg-nNumCurrRanks) + 8; /* was 2*(...) + 6 */ + pBCN->pRankStack = (AT_RANK **) inchi_calloc( nMaxLenRankStack, sizeof(pBCN->pRankStack[0]) ); + if ( !pBCN->pRankStack ) + { + pBCN->nMaxLenRankStack = 0; /* avoid memory leaks in case of error */ + goto exit_error_alloc; + } + pBCN->nMaxLenRankStack = nMaxLenRankStack; /* avoid memory leaks in case of error */ + /* init partition stack */ + pBCN->pRankStack[0] = nRank; + pBCN->pRankStack[1] = nAtomNumber; + + + /********************************************************************************************/ + /* get NoH/no taut groups canonical numbering, connection table, and equivalence partition */ + /********************************************************************************************/ + + /* pointers */ + pCD[iOther].LinearCT = NULL; + pCD[iOther].NumH = NULL; + pCD[iOther].NumHfixed = NULL; + pCD[iOther].iso_sort_key = NULL; + pCD[iOther].iso_exchg_atnos = NULL; + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + pCD[iOther].iso_sort_key_Hfixed = NULL; +#endif + + /* variables - unchanged */ + pCD[iOther].ulTimeOutTime = pBCN->ulTimeOutTime; + pCD[iOther].nMaxLenLinearCT = s[iOther].nLenCTAtOnly + 1; + /* return values & input/output */ + pCD[iOther].nLenLinearCT = s[iOther].nLenCTAtOnly; + pCD[iOther].nLenCTAtOnly = s[iOther].nLenCTAtOnly; + pCD[iOther].lenNumH = 0; + pCD[iOther].lenNumHfixed = 0; + pCD[iOther].len_iso_sort_key = 0; + pCD[iOther].maxlen_iso_sort_key = 0; + pCD[iOther].len_iso_exchg_atnos = 0; + pCD[iOther].maxlen_iso_exchg_atnos = 0; + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + pCD[iOther].len_iso_sort_key_Hfixed = 0; + pCD[iOther].maxlen_iso_sort_key_Hfixed = 0; +#endif + + ret = CanonGraph01( ic, pCG, num_atoms, num_atoms, num_max, 0, + NeighList[TAUT_NON], (Partition *)pBCN->pRankStack, + nSymmRankNoH, nCanonRankNoH, nAtomNumberCanonNoH, + pCD+iOther, pCC, NULL, &Ct_NoH, LargeMolecules ); + + if ( ret < 0 ) + { + goto exit_error; + } + + /* update initial partitioning */ + nNumCurrRanks = FixCanonEquivalenceInfo( pCG, num_atoms, nSymmRankNoH, nRank, nTempRank, nAtomNumber, &bChanged ); + + /* repartition if necessary */ + if ( bChanged & 3 ) + { + if ( Ct_NoH ) + { + CTableFree( Ct_NoH ); + inchi_free( Ct_NoH ); + Ct_NoH = NULL; + } + pCD[iOther].nCanonFlags |= CANON_FLAG_NO_H_RECANON; + + ret = CanonGraph02( ic, pCG, num_atoms, num_atoms, num_max, 0, + NeighList[TAUT_NON], (Partition *)pBCN->pRankStack, + nSymmRankNoH, nCanonRankNoH, nAtomNumberCanonNoH, + pCD+iOther, pCC, NULL, &Ct_NoH, LargeMolecules ); + + if ( ret < 0 ) + { + goto exit_error; + } + } + + + /********************************************************************************/ + /* get NoTautH canonical numbering, connection table, and equivalence partition */ + /********************************************************************************/ + maxlenNumHNoTautH = num_atoms + 1; + nSymmRankNoTautH = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankNoTautH[0] ) ); + nCanonRankNoTautH = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankNoTautH[0] ) ); + nAtomNumberCanonNoTautH = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonNoTautH[0]) ); + numHNoTautH = (NUM_H *) inchi_calloc( maxlenNumHNoTautH, sizeof(numHNoTautH[0]) ); + if ( !numHNoTautH || !nSymmRankNoTautH || !nCanonRankNoTautH || !nAtomNumberCanonNoTautH ) + { + goto exit_error_alloc; + } + + /* find number of H atoms attached to not-a-tautomeric-endpoint atoms */ + for ( i = 0; i < num_atoms; i ++ ) + { + numHNoTautH[i] = (!at_base[i].endpoint && at_base[i].num_H)? at_base[i].num_H+BASE_H_NUMBER : EMPTY_H_NUMBER; + } + + /* pointers */ + pCD[iOther].LinearCT = NULL; + pCD[iOther].NumH = numHNoTautH; + pCD[iOther].NumHfixed = NULL; + pCD[iOther].iso_sort_key = NULL; + pCD[iOther].iso_exchg_atnos = NULL; + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + pCD[iOther].iso_sort_key_Hfixed = NULL; +#endif + + /* variables - unchanged */ + pCD[iOther].ulTimeOutTime = pBCN->ulTimeOutTime; + pCD[iOther].nMaxLenLinearCT = s[iOther].nLenCTAtOnly + 1; + pCD[iOther].maxlenNumH = maxlenNumHNoTautH; + /* return values & input/output */ + pCD[iOther].nLenLinearCT = s[iOther].nLenCTAtOnly; + pCD[iOther].nLenCTAtOnly = s[iOther].nLenCTAtOnly; + pCD[iOther].lenNumH = lenNumHNoTautH = num_atoms; + pCD[iOther].lenNumHfixed = 0; + pCD[iOther].len_iso_sort_key = 0; + pCD[iOther].maxlen_iso_sort_key = 0; + pCD[iOther].len_iso_exchg_atnos = 0; + pCD[iOther].maxlen_iso_exchg_atnos = 0; + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + pCD[iOther].len_iso_sort_key_Hfixed = 0; + pCD[iOther].maxlen_iso_sort_key_Hfixed = 0; +#endif + + pCD[iOther].nAuxRank = NULL; + + /* check whether we need NoTautH cononicalization */ + memset( nTempRank, 0, num_max * sizeof(nTempRank[0]) ); + for ( i = 0; i < num_atoms; i ++ ) + { + if ( nTempRank[nSymmRankNoH[i]-1] < i ) + { + nTempRank[nSymmRankNoH[i]-1] = i; /* greatest class representative */ + } + } + for ( i = 0; i < num_atoms; i ++ ) + { + if ( numHNoTautH[i] != numHNoTautH[nTempRank[nSymmRankNoH[i]-1]] ) + { + pCD[iOther].nCanonFlags |= CANON_FLAG_NO_TAUT_H_DIFF; + break; /* atoms so far found to be equivalent have different number of H; the canonicalization is needed */ + } + } + + /* i = 0; *//* debug: force to call the canonicalization */ + if ( i < num_atoms ) + { + /* needs canonicalization */ + /* get aux canonical ranking of the structure with attached H */ + +#if ( USE_AUX_RANKING == 1 ) + /* refine no-H partition according to not-a-taut-H distribution */ + memset( pAtomInvariantAux, 0, num_max * sizeof(pAtomInvariantAux[0]) ); + for ( i = 0; i < num_atoms; i ++ ) + { + pAtomInvariantAux[i].val[0] = nSymmRankNoH[i]; + pAtomInvariantAux[i].val[1] = numHNoTautH[i]; /* additional differentiation: not-a-taut-H distribution */ + } + + /* initial partitioning */ + nNumCurrRanks = SetInitialRanks2( num_atoms, pAtomInvariantAux, nRankAux, nAtomNumberAux, pCG ); + + /* make equitable partition */ + nNumCurrRanks = DifferentiateRanks2( pCG, num_atoms, NeighList[TAUT_NON], + nNumCurrRanks, nRankAux, + nTempRank, nAtomNumberAux, &lCount, 0 /* 0 means use qsort */ ); + + /* to accelerate do not call CanonGraph() to find really equivalent atoms */ + pCD[iOther].nAuxRank = nRankAux; +#endif + + ret = CanonGraph03( ic, pCG, num_atoms, num_atoms, num_max, 1 /* digraph?? was 0 */, + NeighList[TAUT_NON], (Partition *)pBCN->pRankStack, + nSymmRankNoTautH, nCanonRankNoTautH, nAtomNumberCanonNoTautH, + pCD+iOther, pCC, &Ct_NoH, &Ct_NoTautH, LargeMolecules ); + + if ( ret < 0 ) + { + goto exit_error; + } + + /* in case of non-tautomeric structure the final results are in: + + nSymmRankNoTautH + nCanonRankNoTautH + nAtomNumberCanonNoTautH + Ct_NoTautH + numHNoTautH (original H positions) + */ + } /* if ( i < num_atoms ) */ + + else + + { + + /* copy the results of the previous (no H) canonicalization */ + /* in this case numHNoTautH[] is not needed for the next canonicalization(s) */ + if ( (Ct_Temp = (ConTable *)inchi_calloc( 1, sizeof( *Ct_Temp ) ) ) && + CTableCreate( Ct_Temp, num_atoms, pCD+iOther) ) + { + CtFullCopy( Ct_Temp, Ct_NoH ); + /* since Ct_NoH does not have Ct_NoH->NumH we have to fill out Ct_Temp->NumH separately */ + for ( i = 0; i < num_atoms; i ++ ) + { + Ct_Temp->NumH[nCanonRankNoH[i]-1] = numHNoTautH[i]; + /*Ct_Temp->NumH[i] = numHNoTautH[nAtomNumberCanonNoH[i]]; -- alternative */ + } + Ct_Temp->lenNumH = num_atoms; + } + else + { + goto exit_error_alloc; + } + + Ct_NoTautH = Ct_Temp; + Ct_Temp = NULL; + memcpy( nSymmRankNoTautH, nSymmRankNoH, num_atoms*sizeof(nSymmRankNoTautH[0]) ); + memcpy( nCanonRankNoTautH, nCanonRankNoH, num_atoms*sizeof(nCanonRankNoTautH[0]) ); + memcpy( nAtomNumberCanonNoTautH, nAtomNumberCanonNoH, num_atoms*sizeof(nAtomNumberCanonNoTautH[0]) ); + } + + /* in case of non-tautomeric component this is the final result */ + /* i = CtFullCompare( Ct_NoTautH, Ct_Temp, num_atoms, 0, 0 );*/ + + /*******************************************************************************************/ + /* If only Isotopic atoms and isotopic H, tautomerism has not been found: */ + /* get isotopic canonical numbering, connection table, and equivalence partition */ + /*******************************************************************************************/ + + if ( s[iOther].num_isotopic_atoms && !s[iOther].bIgnoreIsotopic && !bReqTaut && bReqNonTaut ) + { + + maxlen_iso_sort_key_NoTautH = num_atoms+1; + nSymmRankNoTautHIso = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankNoTautHIso[0] ) ); + nCanonRankNoTautHIso = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankNoTautHIso[0] ) ); + nAtomNumberCanonNoTautHIso = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonNoTautHIso[0]) ); + iso_sort_key_NoTautH = (AT_ISO_SORT_KEY *) inchi_calloc( maxlen_iso_sort_key_NoTautH, sizeof(iso_sort_key_NoTautH[0]) ); + + if ( !nSymmRankNoTautHIso || !nCanonRankNoTautHIso || !nAtomNumberCanonNoTautHIso || !iso_sort_key_NoTautH ) + { + goto exit_error_alloc; + } + + /* fill out isotopic non-tautomeric keys */ + num_iso_NoTautH = 0; + for ( i = 0; i < num_atoms; i ++ ) + { + if ( at_base[i].endpoint ) + { + /* should not happen */ + iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, 0, 0, 0); + } + else + { + iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, at_base[i].num_iso_H[0], at_base[i].num_iso_H[1], at_base[i].num_iso_H[2]); + } + if ( iso_sort_key ) + { + iso_sort_key_NoTautH[i] = iso_sort_key; + num_iso_NoTautH ++; + } + else + { + iso_sort_key_NoTautH[i] = EMPTY_ISO_SORT_KEY; + } + } + + /* pointers */ + pCD[iOther].LinearCT = NULL; /* LinearCT; */ + pCD[iOther].NumH = numHNoTautH; + pCD[iOther].NumHfixed = NULL; + pCD[iOther].iso_sort_key = iso_sort_key_NoTautH; + pCD[iOther].iso_exchg_atnos = NULL; + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + pCD[iOther].iso_sort_key_Hfixed = NULL; +#endif + + /* variables - unchanged */ + pCD[iOther].ulTimeOutTime = pBCN->ulTimeOutTime; + pCD[iOther].nMaxLenLinearCT = s[iOther].nLenCTAtOnly + 1; + pCD[iOther].maxlenNumH = maxlenNumHNoTautH; + /* return values & input/output */ + pCD[iOther].nLenLinearCT = s[iOther].nLenCTAtOnly; + pCD[iOther].nLenCTAtOnly = s[iOther].nLenCTAtOnly; + pCD[iOther].lenNumH = lenNumHNoTautH /*= num_atoms*/; + pCD[iOther].lenNumHfixed = 0; + pCD[iOther].len_iso_sort_key = len_iso_sort_key_NoTautH = num_atoms; + pCD[iOther].maxlen_iso_sort_key = maxlen_iso_sort_key_NoTautH; + pCD[iOther].len_iso_exchg_atnos = 0; + pCD[iOther].maxlen_iso_exchg_atnos = 0; + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + pCD[iOther].len_iso_sort_key_Hfixed = 0; + pCD[iOther].maxlen_iso_sort_key_Hfixed = 0; +#endif + + pCD[iOther].nAuxRank = NULL; + + if ( num_iso_NoTautH ) + { + /* check whether we need NoTautH cononicalization */ + memset( nTempRank, 0, num_max * sizeof(nTempRank[0]) ); + for ( i = 0; i < num_atoms; i ++ ) + { + if ( nTempRank[nSymmRankNoTautH[i]-1] < i ) + { + nTempRank[nSymmRankNoTautH[i]-1] = i; /* greatest class representative */ + } + } + for ( i = 0; i < num_atoms; i ++ ) + { + if ( iso_sort_key_NoTautH[i] != iso_sort_key_NoTautH[nTempRank[nSymmRankNoTautH[i]-1]] ) { + pCD[iOther].nCanonFlags |= CANON_FLAG_ISO_ONLY_NON_TAUT_DIFF; + break; /* atoms so far found to be equivalent differ in isotopes; the canonicalization is needed */ + } + } + } + else + { + i = num_atoms; + } + /* i = 0; *//* debug: force to call the canonicalization */ + if ( i < num_atoms ) + { + /* we need canonicalization */ + /* get aux canonical ranking of the structure with isotopic non-tautomeric H */ + +#if ( USE_AUX_RANKING == 1 ) + /* refine no-taut-H partition according to non-taut H isotopic distribution */ + memset( pAtomInvariantAux, 0, num_max * sizeof(pAtomInvariantAux[0]) ); + for ( i = 0; i < num_atoms; i ++ ) + { + pAtomInvariantAux[i].val[0] = nSymmRankNoTautH[i]; + pAtomInvariantAux[i].iso_sort_key = iso_sort_key_NoTautH[i]; /* additional differentiation */ + } + /* initial ranks for non-taut H isotopic distribution */ + nNumCurrRanks = SetInitialRanks2( num_atoms, pAtomInvariantAux, nRankAux, nAtomNumberAux, pCG ); + /* make equitable */ + nNumCurrRanks = DifferentiateRanks2( pCG, num_atoms, NeighList[TAUT_NON], + nNumCurrRanks, nRankAux, + nTempRank, nAtomNumberAux, &lCount, 0 /* 0 means use qsort */ ); + /* to accelerate do not call CanonGraph() to find really equivalent atoms */ + pCD[iOther].nAuxRank = nRankAux; +#endif + + ret = CanonGraph04( ic, pCG, + num_atoms, num_atoms, num_max, 1 /* digraph?? was 0 */, + NeighList[TAUT_NON], (Partition *)pBCN->pRankStack, + nSymmRankNoTautHIso, nCanonRankNoTautHIso, nAtomNumberCanonNoTautHIso, + pCD+iOther, pCC, &Ct_NoTautH, &Ct_NoTautHIso, LargeMolecules ); + if ( ret < 0 ) + { + goto exit_error; + } + + /* in case of non-tautomeric structure the final results are in: + + nSymmRankNoTautHIso + nCanonRankNoTautHIso + nAtomNumberCanonNoTautHIso + Ct_NoTautHIso + iso_sort_key_NoTautH (original isotopic atom positions) + */ + } + else + { + /* copy the results of the previous (no taut H) canonicalization */ + /* in this case numHNoTautH[] is not needed for the next canonicalization(s) */ + if ( (Ct_Temp = (ConTable *)inchi_calloc( 1, sizeof( *Ct_Temp ) ) ) && + CTableCreate( Ct_Temp, num_atoms, pCD+iOther) ) + { + CtFullCopy( Ct_Temp, Ct_NoTautH ); + /* since Ct_NoTautH does not have Ct_NoTautH->iso_sort_key we have to fill out Ct_Temp->iso_sort_key separately */ + for ( i = 0; i < num_atoms; i ++ ) + { + Ct_Temp->iso_sort_key[nCanonRankNoTautH[i]-1] = iso_sort_key_NoTautH[i]; + } + Ct_Temp->len_iso_sort_key = num_atoms; + } + else + { + goto exit_error_alloc; + } + Ct_NoTautHIso = Ct_Temp; + Ct_Temp = NULL; + memcpy( nSymmRankNoTautHIso, nSymmRankNoTautH, num_atoms*sizeof(nSymmRankNoTautHIso[0]) ); + memcpy( nCanonRankNoTautHIso, nCanonRankNoTautH, num_atoms*sizeof(nCanonRankNoTautHIso[0]) ); + memcpy( nAtomNumberCanonNoTautHIso, nAtomNumberCanonNoTautH, num_atoms*sizeof(nAtomNumberCanonNoTautHIso[0]) ); + } + /* in case of non-tautomeric component this is the final result */ + /* i = CtFullCompare( Ct_NoTautHIso, Ct_Temp, num_atoms, 0, 0 );*/ + } + + + if ( bReqTaut ) + { + /*****************************************************************************/ + /* Tautomeric Structure Canonicalizaton: */ + /* get base canonical numbering, connection table, and equivalence partition */ + /*****************************************************************************/ + /* find H atoms attached to non-tautomeric-endpoints and to tautomeric endpoints */ + maxlenNumH = num_atoms + T_NUM_NO_ISOTOPIC*(num_at_tg-num_atoms) + 1; /* including negative charges */ + nSymmRankBase = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankBase[0] ) ); + nCanonRankBase = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankBase[0] ) ); + nAtomNumberCanonBase = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonBase[0]) ); + numH = (NUM_H *) inchi_calloc( maxlenNumH, sizeof(numH[0]) ); + + if ( !numH || !nSymmRankBase || !nCanonRankBase || !nAtomNumberCanonBase ) + { + goto exit_error_alloc; + } + + /* non-tautomeric H counts */ + for ( i = 0; i < num_atoms; i ++ ) + { + numH[i] = (!at_base[i].endpoint && at_base[i].num_H)? at_base[i].num_H+BASE_H_NUMBER : EMPTY_H_NUMBER; + } + + /* tautomeric H and negative charge counts */ + for ( i = k = num_atoms; i < num_at_tg; i ++ ) + { + m = i-num_atoms; + for ( j = 0; j < T_NUM_NO_ISOTOPIC; j ++ ) + { + /* non-zeroes for j=1 are negative charge counts; T_NUM_NO_ISOTOPIC=2 entry per t-group */ + numH[k ++] = t_group_info->t_group[m].num[j]? t_group_info->t_group[m].num[j]+BASE_H_NUMBER : EMPTY_H_NUMBER; + } + } + + /* pointers */ + pCD[iBase].LinearCT = NULL; + pCD[iBase].NumH = numH; /* num_atoms non-tautomeric H; num_tg pairs of H and (-) in t-groups */ + pCD[iBase].NumHfixed = NULL; + pCD[iBase].iso_sort_key = NULL; + pCD[iBase].iso_exchg_atnos = NULL; + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + pCD[iBase].iso_sort_key_Hfixed = NULL; +#endif + + /* variables - unchanged */ + pCD[iBase].ulTimeOutTime = pBCN->ulTimeOutTime; + pCD[iBase].nMaxLenLinearCT = s[iBase].nLenCT + 1; + pCD[iBase].maxlenNumH = maxlenNumH; + /* return values & input/output */ + pCD[iBase].nLenLinearCT = s[iBase].nLenCT; + pCD[iBase].nLenCTAtOnly = s[iBase].nLenCTAtOnly; + pCD[iBase].lenNumH = lenNumH = k; + pCD[iBase].lenNumHfixed = 0; + pCD[iBase].len_iso_sort_key = 0; + pCD[iBase].maxlen_iso_sort_key = 0; + pCD[iBase].len_iso_exchg_atnos = 0; + pCD[iBase].maxlen_iso_exchg_atnos = 0; + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + pCD[iBase].len_iso_sort_key_Hfixed = 0; + pCD[iBase].maxlen_iso_sort_key_Hfixed = 0; +#endif + + pCD[iBase].nAuxRank = NULL; + + /* make sure the initial partition is equitable (at this point t-groups do not have ranks yet) */ + FillOutAtomInvariant2( at_base, + num_atoms, + num_at_tg, + pAtomInvariant, + 1 /*bIgnoreIsotopic*/, + 0 /*bHydrogensInRanks*/, + 0 /*bHydrogensFixedInRanks*/, + 1 /*bTaut=bDigraph*/, + 1 /* bTautGroupsOnly */, + t_group_info ); + + for ( i = 0; i < num_atoms; i ++ ) + { + pAtomInvariant[i].val[0] = pBCN->pRankStack[0][i]; + } + + /* initial ranks for t-group(s) only */ + nNumCurrRanks = SetInitialRanks2( num_at_tg, pAtomInvariant, nRank, nAtomNumber, pCG ); + + /* make equitable, call digraph procedure; + pBCN->pRankStack[0] is nRank, pBCN->pRankStack[1] is nAtomNumber + This should only split ranks of tautomeric groups */ + nNumCurrRanks = DifferentiateRanks4( pCG, num_at_tg, NeighList[TAUT_YES], + nNumCurrRanks, pBCN->pRankStack[0], nTempRank /* temp array */, + pBCN->pRankStack[1], (AT_RANK)num_atoms, &lCount ); + +#if ( USE_AUX_RANKING == 1 ) + /* refine no-H partition according to non-taut H distribution */ + memset( pAtomInvariantAux, 0, num_max * sizeof(pAtomInvariantAux[0]) ); + for ( i = 0; i < num_atoms; i ++ ) + { + pAtomInvariantAux[i].val[0] = nSymmRankNoTautH[i]; + pAtomInvariantAux[i].val[1] = numH[i]; /* additional differentiation */ + } + for ( j = i; i < num_at_tg; i ++ ) + { + pAtomInvariantAux[i].val[0] = nRank[i]; + } + /* initial ranks for t-group(s) */ + nNumCurrRanks = SetInitialRanks2( num_at_tg, pAtomInvariantAux, nRankAux, nAtomNumberAux, pCG ); + /* make equitable, call digraph procedure */ + nNumCurrRanks = DifferentiateRanks4( pCG, num_at_tg, NeighList[TAUT_YES], + nNumCurrRanks, nRankAux, nTempRank /* temp array */, + nAtomNumberAux, (AT_RANK)num_atoms, &lCount ); + /* to accelerate do not call CanonGraph() to find really equivalent atoms */ + pCD[iBase].nAuxRank = nRankAux; +#endif + + ret = CanonGraph05( ic, pCG, num_atoms, num_at_tg, num_max, 1 /* digraph*/, + NeighList[TAUT_YES], (Partition *)pBCN->pRankStack, + nSymmRankBase, nCanonRankBase, nAtomNumberCanonBase, + pCD+iBase, pCC, &Ct_NoTautH, &Ct_Base, LargeMolecules ); + if ( ret < 0 ) + { + goto exit_error; + } + + /* tautomeric isotopic structure */ + /**************************************************************************************/ + /* Isotopic atoms and isotopic H atoms and isotopic tautomeric groups */ + /* get isotopic canonical numbering, connection table, and equivalence partition */ + /**************************************************************************************/ + if ( s[iBase].num_isotopic_atoms && !s[iBase].bIgnoreIsotopic || + s[iBase].bHasIsotopicTautGroups && !bTautIgnoreIsotopic || + bUseIsoAuxBase[iBase] && !bTautIgnoreIsotopic ) + { + + t_group_info->bIgnoreIsotopic = bTautIgnoreIsotopic; + + nSymmRankBaseIso = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankBaseIso[0] ) ); + nCanonRankBaseIso = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankBaseIso[0] ) ); + nAtomNumberCanonBaseIso = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonBaseIso[0]) ); + if ( bUseIsoAuxBase[iBase] ) + { + maxlen_iso_exchg_atnos = num_max+1; + iso_exchg_atnos = (S_CHAR *) inchi_calloc( maxlen_iso_exchg_atnos, sizeof(iso_exchg_atnos[0]) ); + } + maxlen_iso_sort_keyBase = num_max+1; /* num_at_tg+1;*/ + iso_sort_keyBase = (AT_ISO_SORT_KEY *) inchi_calloc( maxlen_iso_sort_keyBase, sizeof(iso_sort_keyBase[0]) ); + if ( !nSymmRankBaseIso || !nCanonRankBaseIso || !nAtomNumberCanonBaseIso || + !iso_sort_keyBase || + maxlen_iso_exchg_atnos && !iso_exchg_atnos ) + { + goto exit_error_alloc; + } + /* atoms */ + num_iso_NoTautH = 0; + num_iso_NoAuxBase = 0; + if ( iso_exchg_atnos ) + { + len_iso_exchg_atnos = num_at_tg; + } + for ( i = 0; i < num_atoms; i ++ ) { + if ( at_base[i].endpoint || iso_exchg_atnos && (at_base[i].cFlags & AT_FLAG_ISO_H_POINT) ) + { + /* tautomeric or may have exchangeable isotopic H */ + iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, 0, 0, 0); + if ( iso_exchg_atnos ) + { + num_iso_NoAuxBase += !at_base[i].endpoint; /* these non-taut atom may exchange isotopic H as tautomeric atoms do */ + } + } + else + { + /* non-mobile H */ + iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, at_base[i].num_iso_H[0], at_base[i].num_iso_H[1], at_base[i].num_iso_H[2]); + if ( iso_exchg_atnos ) + { + iso_exchg_atnos[i] = 1; /* atom cannot have exchangable isotopic H atom(s) */ + } + } + if ( iso_sort_key ) + { + num_iso_NoTautH ++; + iso_sort_keyBase[i] = iso_sort_key; + } + else + { + iso_sort_keyBase[i] = EMPTY_ISO_SORT_KEY; + } + } + + /* check marking and count of non-taut atoms that may exchange isotopic H -- debug only */ + if ( iso_exchg_atnos ) + { + if ( num_iso_NoAuxBase != t_group_info->nIsotopicEndpointAtomNumber[0] ) + { + ret = CT_ISOCOUNT_ERR; + goto exit_error; + } + for ( i = 1; i <= num_iso_NoAuxBase; i ++ ) + { + j = t_group_info->nIsotopicEndpointAtomNumber[i]; + if ( at_base[j].endpoint || !(at_base[j].cFlags & AT_FLAG_ISO_H_POINT) ) + { + ret = CT_ISOCOUNT_ERR; + goto exit_error; + } + } + } + + /* t-groups */ + num_iso_Base = 0; + if ( iso_exchg_atnos ) + { + for ( i = num_atoms; i < num_at_tg; i ++ ) + { + iso_sort_keyBase[i] = EMPTY_ISO_SORT_KEY; /* new mode: do not provide info about isotopic tautomeric H */ + } + } + else + { + for ( i = num_atoms; i < num_at_tg; i ++ ) + { + /* should not happen anymore */ + m = i-num_atoms; + if ( iso_sort_key = t_group_info->t_group[m].iWeight ) + { + /* old approach: each t-group has its own isotopic "weight" */ + num_iso_Base ++; + iso_sort_keyBase[i] = iso_sort_key; + } + else + { + iso_sort_keyBase[i] = EMPTY_ISO_SORT_KEY; + } + } + } + + if ( !num_iso_NoAuxBase && iso_exchg_atnos ) + { + /* all atoms that may exchange isotopic H are either tautomeric or not present */ + inchi_free( iso_exchg_atnos ); + iso_exchg_atnos = NULL; + len_iso_exchg_atnos = 0; + maxlen_iso_exchg_atnos = 0; + } + + if ( !num_iso_NoTautH && !num_iso_Base && iso_sort_keyBase ) + { + /* no isotopic atoms present */ + inchi_free( iso_sort_keyBase ); + iso_sort_keyBase = NULL; + maxlen_iso_sort_keyBase = 0; + } + else + { + len_iso_sort_keyBase = num_at_tg; + } + + if ( !iso_exchg_atnos && !iso_sort_keyBase ) + { + /* no isotopic part at all or only tautomeric groups */ + inchi_free( nSymmRankBaseIso ); nSymmRankBaseIso = NULL; + inchi_free( nCanonRankBaseIso ); nCanonRankBaseIso = NULL; + inchi_free( nAtomNumberCanonBaseIso ); nAtomNumberCanonBaseIso = NULL; + } + else + { + /* proceed with tautomeric isotopic canonicalization */ + /* pointers */ + pCD[iBase].LinearCT = NULL; + pCD[iBase].NumH = numH; /* num_atoms non-tautomeric H; num_tg pairs of H and (-) in t-groups */ + pCD[iBase].NumHfixed = NULL; + pCD[iBase].iso_sort_key = iso_sort_keyBase; + pCD[iBase].iso_exchg_atnos = iso_exchg_atnos; + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + pCD[iBase].iso_sort_key_Hfixed = NULL; +#endif + + /* variables - unchanged */ + pCD[iBase].ulTimeOutTime = pBCN->ulTimeOutTime; + pCD[iBase].nMaxLenLinearCT = s[iBase].nLenCT + 1; + pCD[iBase].maxlenNumH = maxlenNumH; + /* return values & input/output */ + pCD[iBase].nLenLinearCT = s[iBase].nLenCT; + pCD[iBase].nLenCTAtOnly = s[iBase].nLenCTAtOnly; + pCD[iBase].lenNumH = lenNumH /* = k */; + pCD[iBase].lenNumHfixed = 0; + pCD[iBase].len_iso_sort_key = len_iso_sort_keyBase; + pCD[iBase].maxlen_iso_sort_key = maxlen_iso_sort_keyBase; + pCD[iBase].len_iso_exchg_atnos = len_iso_exchg_atnos; + pCD[iBase].maxlen_iso_exchg_atnos = maxlen_iso_exchg_atnos; + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + pCD[iBase].len_iso_sort_key_Hfixed = 0; + pCD[iBase].maxlen_iso_sort_key_Hfixed = 0; +#endif + + pCD[iBase].nAuxRank = NULL; + + if ( num_iso_NoTautH || num_iso_Base || num_iso_NoAuxBase ) + { + /* check whether we need actual canonicalization */ + memset( nTempRank, 0, num_max * sizeof(nTempRank[0]) ); + for ( i = 0; i < num_at_tg; i ++ ) + { + if ( nTempRank[nSymmRankBase[i]-1] < i ) + { + nTempRank[nSymmRankBase[i]-1] = i; /* greatest class representative */ + } + } + for ( i = 0; i < num_at_tg; i ++ ) + { + if ( (iso_sort_keyBase? (iso_sort_keyBase[i] != iso_sort_keyBase[nTempRank[nSymmRankBase[i]-1]]):0) || + (iso_exchg_atnos? (iso_exchg_atnos[i] != iso_exchg_atnos[nTempRank[nSymmRankBase[i]-1]]):0)) { + pCD[iBase].nCanonFlags |= CANON_FLAG_ISO_TAUT_DIFF; + break; /* atoms so far found to be equivalent have different number of H; the canonicalization is needed */ + } + } + } + else + { + i = num_at_tg; /* should not happen */ + } + + /* i = 0; *//* debug: force to call the canonicalization */ + + if ( i < num_at_tg ) + { + /* we need canonicalization */ + /* get aux canonical ranking of the structure with isotopic non-tautomeric H */ + + #if ( USE_AUX_RANKING == 1 ) + /* refine no-taut-H partition according to non-taut H + t-groups isotopic distribution */ + memset( pAtomInvariantAux, 0, num_max * sizeof(pAtomInvariantAux[0]) ); + for ( i = 0; i < num_at_tg; i ++ ) { + pAtomInvariantAux[i].val[0] = nSymmRankBase[i]; + pAtomInvariantAux[i].iso_sort_key = iso_sort_keyBase? iso_sort_keyBase[i] : 0; /* additional differentiation */ + pAtomInvariantAux[i].iso_aux_key = iso_exchg_atnos? iso_exchg_atnos[i] : 0; + } + /* initial ranks for non-taut H isotopic distribution */ + nNumCurrRanks = SetInitialRanks2( num_at_tg, pAtomInvariantAux, nRankAux, nAtomNumberAux, pCG ); + /* make equitable, not a digraph procedure */ + nNumCurrRanks = DifferentiateRanks2( pCG, num_at_tg, NeighList[TAUT_YES], + nNumCurrRanks, nRankAux, + nTempRank, nAtomNumberAux, &lCount, 0 /* 0 means first use qsort */ ); + /* to accelerate do not call CanonGraph() to find really equivalent atoms */ + pCD[iBase].nAuxRank = nRankAux; + #endif + + ret = CanonGraph06( ic, pCG, num_atoms, num_at_tg, num_max, 1 /* digraph */, + NeighList[TAUT_YES], (Partition *)pBCN->pRankStack, + nSymmRankBaseIso, nCanonRankBaseIso, nAtomNumberCanonBaseIso, + pCD+iBase, pCC, &Ct_Base, &Ct_BaseIso, LargeMolecules ); + if ( ret < 0 ) + { + goto exit_error; + } + /* in case of a tautomeric structure the final results are in: + + nSymmRankBaseIso + nCanonRankBaseIso + nAtomNumberCanonBaseIso + Ct_BaseIso + iso_sort_keyBase (original isotopic atom & t-group positions) + Ct_BaseIso->iso_exchg_atnos: 0=>can exchange isotopic H, including tautomeric atoms + iso_exchg_atnos : same, in order of t_group_info->nIsotopicEndpointAtomNumber[] + */ + } + else + { + /* copy the results of the previous (no taut H) canonicalization */ + /* in this case numHNoTautH[] is not needed for the next canonicalization(s) */ + if ( (Ct_Temp = (ConTable *)inchi_calloc( 1, sizeof( *Ct_Temp ) ) ) && + CTableCreate( Ct_Temp, num_atoms, pCD+iBase) ) + { + CtFullCopy( Ct_Temp, Ct_Base ); + /* since Ct_Base does not have Ct_Base->iso_sort_key we + have to fill out Ct_Temp->iso_sort_key separately */ + if ( iso_sort_keyBase ) + { + for ( i = 0; i < num_at_tg; i ++ ) + { + Ct_Temp->iso_sort_key[nCanonRankBase[i]-1] = iso_sort_keyBase[i]; + } + Ct_Temp->len_iso_sort_key = num_at_tg; + } + else + { + Ct_Temp->len_iso_sort_key = 0; + } + if ( iso_exchg_atnos ) + { + for ( i = 0; i < num_atoms; i ++ ) + { + Ct_Temp->iso_exchg_atnos[nCanonRankBase[i]-1] = iso_exchg_atnos[i]; + } + Ct_Temp->len_iso_exchg_atnos = num_at_tg; + } + else + { + Ct_Temp->len_iso_exchg_atnos = 0; + } + } + else + { + goto exit_error_alloc; + } + Ct_BaseIso = Ct_Temp; + Ct_Temp = NULL; + memcpy( nSymmRankBaseIso, nSymmRankBase, num_at_tg*sizeof(nSymmRankBaseIso[0]) ); + memcpy( nCanonRankBaseIso, nCanonRankBase, num_at_tg*sizeof(nCanonRankBaseIso[0]) ); + memcpy( nAtomNumberCanonBaseIso, nAtomNumberCanonBase, num_at_tg*sizeof(nAtomNumberCanonBaseIso[0]) ); + } + /* in case of non-tautomeric component this is the final result */ + /* i = CtFullCompare( Ct_BaseIso, Ct_Temp, num_at_tg, 0, 0 );*/ + + t_group_info->bIgnoreIsotopic = 1; + } + } + } + + /**********************************************************************************/ + /* get "fixed H" canonical numbering, connection table, and equivalence partition */ + /**********************************************************************************/ + + if ( bReqTaut && bReqNonTaut ) + { + maxlenNumHfixed = num_atoms + 1; + nSymmRankFixH = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankFixH[0] ) ); + nCanonRankFixH = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankFixH[0] ) ); + nAtomNumberCanonFixH = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonFixH[0]) ); + NumHfixed = (NUM_H *) inchi_calloc( maxlenNumHfixed, sizeof(NumHfixed[0]) ); + if ( !NumHfixed || !nSymmRankFixH || !nCanonRankFixH || !nAtomNumberCanonFixH ) + { + goto exit_error_alloc; + } + for ( i = 0; i < num_atoms; i ++ ) + { + /* fixed and non-tautomeric H different in taut and non-taut structures */ + + if ( at_base[i].endpoint ) + { + NumHfixed[i] = at_other[i].num_H? at_other[i].num_H+BASE_H_NUMBER : EMPTY_H_NUMBER; + } + else if ( at_other[i].num_H != at_base[i].num_H ) + { + NumHfixed[i] = (NUM_H)at_other[i].num_H - (NUM_H)at_base[i].num_H + BASE_H_NUMBER; + } + else + { + NumHfixed[i] = EMPTY_H_NUMBER; + } + } + + /* pointers */ + pCD[iOther].LinearCT = NULL; /* LinearCT; */ + pCD[iOther].NumH = numHNoTautH; + pCD[iOther].NumHfixed = NumHfixed;/* variables - unchanged */ + pCD[iOther].iso_sort_key = NULL; + pCD[iOther].iso_exchg_atnos = NULL; + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + pCD[iOther].iso_sort_key_Hfixed = NULL; +#endif + + pCD[iOther].ulTimeOutTime = pBCN->ulTimeOutTime; + pCD[iOther].nMaxLenLinearCT = s[iOther].nLenCTAtOnly + 1; + pCD[iOther].maxlenNumH = maxlenNumHNoTautH; + pCD[iOther].maxlenNumHfixed = maxlenNumHfixed; + /* return values & input/output */ + pCD[iOther].nLenLinearCT = s[iOther].nLenCTAtOnly; + pCD[iOther].nLenCTAtOnly = s[iOther].nLenCTAtOnly; + pCD[iOther].lenNumH = lenNumHNoTautH = num_atoms; + pCD[iOther].lenNumHfixed = num_atoms; + pCD[iOther].len_iso_sort_key = 0; + pCD[iOther].maxlen_iso_sort_key = 0; + pCD[iOther].len_iso_exchg_atnos = 0; + pCD[iOther].maxlen_iso_exchg_atnos = 0; + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + pCD[iOther].len_iso_sort_key_Hfixed = 0; + pCD[iOther].maxlen_iso_sort_key_Hfixed = 0; +#endif + + pCD[iOther].nAuxRank = NULL; + +#if ( USE_AUX_RANKING == 1 ) + if ( !nRankAux ) + nRankAux = (AT_RANK *) inchi_calloc( num_max, sizeof(nRankAux[0] ) ); + if ( !nAtomNumberAux ) + nAtomNumberAux = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberAux[0] ) ); + if ( !pAtomInvariantAux ) + pAtomInvariantAux = (ATOM_INVARIANT2 *)inchi_malloc( num_max * sizeof(pAtomInvariantAux[0]) ); + + if ( !nRankAux || !nAtomNumberAux || + !pAtomInvariantAux ) + { + goto exit_error_alloc; + } + + /* refine no-H partition according to non-taut H distribution */ + memset( pAtomInvariantAux, 0, num_max * sizeof(pAtomInvariantAux[0]) ); + for ( i = 0; i < num_atoms; i ++ ) + { + pAtomInvariantAux[i].val[0] = nSymmRankBase[i]; + pAtomInvariantAux[i].val[1] = NumHfixed[i]; /* additional differentiation */ + } + + /* initial ranks for t-group(s) */ + nNumCurrRanks = SetInitialRanks2( num_atoms, pAtomInvariantAux, nRankAux, nAtomNumberAux, pCG ); + + /* make equitable, digraph procedure */ + nNumCurrRanks = DifferentiateRanks2( pCG, num_atoms, NeighList[TAUT_NON], + nNumCurrRanks, nRankAux, + nTempRank, nAtomNumberAux, &lCount, 0 /* 0 means use qsort */ ); + /* to accelerate do not call CanonGraph() to find really equivalent atoms */ + pCD[iOther].nAuxRank = nRankAux; +#endif + + ret = CanonGraph07( ic, pCG, num_atoms, num_atoms, num_max, 0, + NeighList[TAUT_NON], (Partition *)pBCN->pRankStack, + nSymmRankFixH, nCanonRankFixH, nAtomNumberCanonFixH, + pCD+iOther, pCC, &Ct_NoTautH, &Ct_FixH, LargeMolecules ); + if ( ret < 0 ) + { + goto exit_error; + } + + /*******************************************************************************************/ + /* get "fixed H" isotopic canonical numbering, connection table, and equivalence partition */ + /*******************************************************************************************/ + iflag = s[iBase].num_isotopic_atoms && !s[iBase].bIgnoreIsotopic || + s[iBase].bHasIsotopicTautGroups && !bTautIgnoreIsotopic; + if (bFixIsoFixedH) /* #if ( FIX_ISO_FIXEDH_BUG == 1 ) */ + /* fix bug when iso H was removed as a proton and fixed-H isotopic layer is missing - 2008-09-24 DT*/ + iflag = iflag || s[iOther].num_isotopic_atoms && !s[iOther].bIgnoreIsotopic; + if (iflag) + { + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + maxlen_iso_sort_key_Hfixed = +#endif + + maxlen_iso_sort_key_NoTautH = num_atoms+1; + nSymmRankFixHIso = (AT_RANK *) inchi_calloc( num_max, sizeof(nSymmRankFixHIso[0] ) ); + nCanonRankFixHIso = (AT_RANK *) inchi_calloc( num_max, sizeof(nCanonRankFixHIso[0] ) ); + nAtomNumberCanonFixHIso = (AT_NUMB *) inchi_calloc( num_max, sizeof(nAtomNumberCanonFixHIso[0]) ); + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + iso_sort_key_Hfixed = (AT_ISO_SORT_KEY *) inchi_calloc( maxlen_iso_sort_key_Hfixed, sizeof(iso_sort_key_Hfixed[0]) ); +#endif + + iso_sort_key_NoTautH = (AT_ISO_SORT_KEY *) inchi_calloc( maxlen_iso_sort_key_NoTautH, sizeof(iso_sort_key_NoTautH[0]) ); + + if ( !nSymmRankFixHIso || !nCanonRankFixHIso || !nAtomNumberCanonFixHIso || + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + !iso_sort_key_Hfixed || +#endif + + !iso_sort_key_NoTautH ) + { + goto exit_error_alloc; + } + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + /* fill out isotopic non-tautomeric keys */ + for ( i = 0; i < num_atoms; i ++ ) { + if ( at_base[i].endpoint ) { + iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, 0, 0, 0); + iso_sort_key2 = make_iso_sort_key( 0, at_base[i].num_iso_H[0], at_base[i].num_iso_H[1], at_base[i].num_iso_H[2]); + } else { + iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, at_base[i].num_iso_H[0], at_base[i].num_iso_H[1], at_base[i].num_iso_H[2]); + iso_sort_key2 = 0; + } + if ( iso_sort_key ) { + iso_sort_key_NoTautH[i] = iso_sort_key; + num_iso_NoTautH ++; + } else { + iso_sort_key_NoTautH[i] = EMPTY_ISO_SORT_KEY; + } + if ( iso_sort_key2 ) { + num_iso_Hfixed ++; + iso_sort_key_Hfixed[i] = iso_sort_key2; + } else { + iso_sort_key_Hfixed[i] = EMPTY_ISO_SORT_KEY; + } + } +#else + /* fill out isotopic non-tautomeric keys */ + for ( i = 0; i < num_atoms; i ++ ) + { + + if (bFixIsoFixedH) /* #if ( FIX_ISO_FIXEDH_BUG == 1 ) */ + { + /* fix bug when iso H was removed as a proton and fixed-H isotopic layer is missing - 2008-09-24 DT*/ + if ( at_other ) + { + iso_sort_key = make_iso_sort_key( at_other[i].iso_atw_diff, at_other[i].num_iso_H[0], at_other[i].num_iso_H[1], at_other[i].num_iso_H[2]); + } + else + { + iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, at_base[i].num_iso_H[0], at_base[i].num_iso_H[1], at_base[i].num_iso_H[2]); + } + } + else + iso_sort_key = make_iso_sort_key( at_base[i].iso_atw_diff, at_base[i].num_iso_H[0], at_base[i].num_iso_H[1], at_base[i].num_iso_H[2]); + + if ( iso_sort_key ) + { + iso_sort_key_NoTautH[i] = iso_sort_key; + num_iso_NoTautH ++; + } + else + { + iso_sort_key_NoTautH[i] = EMPTY_ISO_SORT_KEY; + } + } +#endif + + /* pointers */ + pCD[iOther].LinearCT = NULL; /* LinearCT; */ + pCD[iOther].NumH = numHNoTautH; + pCD[iOther].NumHfixed = NumHfixed;/* variables - unchanged */ + pCD[iOther].iso_sort_key = iso_sort_key_NoTautH; + pCD[iOther].iso_exchg_atnos = NULL; + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + pCD[iOther].iso_sort_key_Hfixed = iso_sort_key_Hfixed; +#endif + + pCD[iOther].ulTimeOutTime = pBCN->ulTimeOutTime; + pCD[iOther].nMaxLenLinearCT = s[iOther].nLenCTAtOnly + 1; + pCD[iOther].maxlenNumH = maxlenNumHNoTautH; + pCD[iOther].maxlenNumHfixed = maxlenNumHfixed; + /* return values & input/output */ + pCD[iOther].nLenLinearCT = s[iOther].nLenCTAtOnly; + pCD[iOther].nLenCTAtOnly = s[iOther].nLenCTAtOnly; + pCD[iOther].lenNumH = lenNumHNoTautH = num_atoms; + pCD[iOther].lenNumHfixed = num_atoms; + pCD[iOther].len_iso_sort_key = len_iso_sort_key_NoTautH = num_atoms; + pCD[iOther].maxlen_iso_sort_key = maxlen_iso_sort_key_NoTautH; + pCD[iOther].len_iso_exchg_atnos = 0; + pCD[iOther].maxlen_iso_exchg_atnos = 0; + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + pCD[iOther].len_iso_sort_key_Hfixed = len_iso_sort_key_Hfixed = num_atoms; + pCD[iOther].maxlen_iso_sort_key_Hfixed = maxlen_iso_sort_key_Hfixed; +#endif + + pCD[iOther].nAuxRank = NULL; + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + if ( num_iso_Hfixed || num_iso_NoTautH ) +#else + if ( num_iso_NoTautH ) +#endif + + { + /* check whether we need NoTautH cononicalization */ + memset( nTempRank, 0, num_max * sizeof(nTempRank[0]) ); + for ( i = 0; i < num_atoms; i ++ ) + { + if ( nTempRank[nSymmRankFixH[i]-1] < i ) + { + nTempRank[nSymmRankFixH[i]-1] = i; /* greatest class representative */ + } + } + for ( i = 0; i < num_atoms; i ++ ) + { +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + if ( iso_sort_key_Hfixed[i] != iso_sort_key_Hfixed[nTempRank[nSymmRankFixH[i]-1]] ) + break; +#endif + if ( iso_sort_key_NoTautH[i] != iso_sort_key_NoTautH[nTempRank[nSymmRankFixH[i]-1]]) + break; /* atoms so far found to be equivalent have different isotopic shifts; the canonicalization is needed */ + } + } + else + { + i = num_atoms; /* should not happen */ + } + + /* i = 0; *//* debug: force to call the canonicalization */ + + if ( i < num_atoms ) + { + pCD[iOther].nCanonFlags |= CANON_FLAG_ISO_FIXED_H_DIFF; + /* we need canonicalization */ + /* get aux canonical ranking of the structure with isotopic non-tautomeric H */ + +#if ( USE_AUX_RANKING == 1 ) + /* refine fixed-taut-H partition according to the isotopic distribution */ + memset( pAtomInvariantAux, 0, num_max * sizeof(pAtomInvariantAux[0]) ); + for ( i = 0; i < num_atoms; i ++ ) + { + pAtomInvariantAux[i].val[0] = nSymmRankFixH[i]; + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + iso_sort_key = 0; + if (iso_sort_key_NoTautH[i]!=EMPTY_ISO_SORT_KEY) + iso_sort_key |= iso_sort_key_NoTautH[i]; + if (iso_sort_key_Hfixed[i] !=EMPTY_ISO_SORT_KEY) + iso_sort_key |= iso_sort_key_Hfixed[i]; + if ( !iso_sort_key ) + iso_sort_key = EMPTY_ISO_SORT_KEY; +#else + iso_sort_key = iso_sort_key_NoTautH[i]; +#endif + + pAtomInvariantAux[i].iso_sort_key = iso_sort_key; /* additional differentiation */ + } + + /* initial ranks for non-taut H isotopic distribution */ + nNumCurrRanks = SetInitialRanks2( num_atoms, pAtomInvariantAux, nRankAux, nAtomNumberAux, pCG ); + /* make equitable, digraph procedure */ + nNumCurrRanks = DifferentiateRanks2( pCG, num_atoms, NeighList[TAUT_NON], + nNumCurrRanks, nRankAux, + nTempRank, nAtomNumberAux, &lCount, 0 /* 0 means use qsort */ ); + + /* to accelerate do not call CanonGraph() to find really equivalent atoms */ + pCD[iOther].nAuxRank = nRankAux; +#endif + + ret = CanonGraph08( ic, pCG, num_atoms, num_atoms, num_max, 1 /* digraph?? was 0 */, + NeighList[TAUT_NON], (Partition *)pBCN->pRankStack, + nSymmRankFixHIso, nCanonRankFixHIso, nAtomNumberCanonFixHIso, + pCD+iOther, pCC, &Ct_FixH, &Ct_FixHIso, LargeMolecules ); + if ( ret < 0 ) + { + goto exit_error; + } + /* in case of non-tautomeric structure the final results are in: + + nSymmRankFixHIso + nCanonRankFixHIso + nAtomNumberCanonFixHIso + Ct_FixHIso + iso_sort_keyBase ([0..num_atoms] original isotopic atom positions) + iso_sort_key_Hfixed (original fixed tautomeric H distribution) + */ + } + else + { + /* copy the results of the previous (no taut H) canonicalization */ + /* in this case numHNoTautH[] is not needed for the next canonicalization(s) */ + if ( (Ct_Temp = (ConTable *)inchi_calloc( 1, sizeof( *Ct_Temp ) ) ) && + CTableCreate( Ct_Temp, num_atoms, pCD+iOther) ) + { + CtFullCopy( Ct_Temp, Ct_FixH ); + /* since Ct_FixH does not have Ct_FixH->iso_sort_key and Ct_FixH->iso_sort_key_Hfixed we + have to fill out Ct_Temp->iso_sort_key and Ct_Temp->iso_sort_key_Hfixed separately */ + for ( i = 0; i < num_atoms; i ++ ) + { + Ct_Temp->iso_sort_key[nCanonRankFixH[i]-1] = iso_sort_key_NoTautH[i]; + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + Ct_Temp->iso_sort_key_Hfixed[nCanonRankFixH[i]-1] = iso_sort_key_Hfixed[i]; +#endif + } + Ct_Temp->len_iso_sort_key = num_atoms; + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + Ct_Temp->len_iso_sort_key_Hfixed = num_atoms; +#endif + + /*Ct_Temp->lenNumH = num_atoms;*/ + } + else + { + goto exit_error_alloc; + } + Ct_FixHIso = Ct_Temp; + Ct_Temp = NULL; + memcpy( nSymmRankFixHIso, nSymmRankFixH, num_atoms*sizeof(nSymmRankFixHIso[0]) ); + memcpy( nCanonRankFixHIso, nCanonRankFixH, num_atoms*sizeof(nCanonRankFixHIso[0]) ); + memcpy( nAtomNumberCanonFixHIso, nAtomNumberCanonFixH, num_atoms*sizeof(nAtomNumberCanonFixHIso[0]) ); + } + /* in case of non-tautomeric component this is the final result */ + /* i = CtFullCompare( Ct_NoTautHIso, Ct_Temp, num_atoms, 0, 0 );*/ + } + } /* "fixed H" canonical numbering */ + + + /* consistency check: compare canonical connection tables, H-atoms, isotopic H & taut groups */ + ret = 0; + ret |= (Ct_NoH->lenCt != Ct_NoTautH->lenCt) || memcmp(Ct_NoH->Ctbl, Ct_NoTautH->Ctbl, Ct_NoH->lenCt * sizeof(Ct_NoH->Ctbl[0])); + if ( bReqTaut ) + { + if ( Ct_FixH ) + { + ret |= (Ct_NoTautH->lenCt != Ct_FixH->lenCt) || memcmp(Ct_NoTautH->Ctbl, Ct_FixH->Ctbl, Ct_NoTautH->lenCt * sizeof(Ct_NoTautH->Ctbl[0])); + ret |= (Ct_NoTautH->lenNumH != Ct_FixH->lenNumH) || memcmp( Ct_NoTautH->NumH, Ct_FixH->NumH, Ct_NoTautH->lenNumH*sizeof(Ct_Base->NumH[0])); + } + ret |= (Ct_NoTautH->lenCt > Ct_Base->lenCt) || memcmp(Ct_NoTautH->Ctbl, Ct_Base->Ctbl, Ct_NoTautH->lenCt * sizeof(Ct_NoTautH->Ctbl[0])); + ret |= (Ct_NoTautH->lenNumH > Ct_Base->lenNumH) || memcmp( Ct_NoTautH->NumH, Ct_Base->NumH, Ct_NoTautH->lenNumH*sizeof(Ct_Base->NumH[0])); + } + + /* isotopic canonicalization */ + if ( Ct_NoTautHIso ) + { + ret |= (Ct_NoH->lenCt != Ct_NoTautHIso->lenCt) || memcmp(Ct_NoH->Ctbl, Ct_NoTautHIso->Ctbl, Ct_NoH->lenCt * sizeof(Ct_NoH->Ctbl[0])); + ret |= (Ct_NoTautH->lenNumH != Ct_NoTautHIso->lenNumH) || memcmp( Ct_NoTautH->NumH, Ct_NoTautHIso->NumH, Ct_NoTautH->lenNumH*sizeof(Ct_Base->NumH[0])); + } + else if ( Ct_BaseIso ) + { + ret |= (Ct_BaseIso->lenCt != Ct_Base->lenCt) || memcmp(Ct_BaseIso->Ctbl, Ct_Base->Ctbl, Ct_BaseIso->lenCt * sizeof(Ct_BaseIso->Ctbl[0])); + ret |= (Ct_BaseIso->lenNumH != Ct_Base->lenNumH) || memcmp( Ct_BaseIso->NumH, Ct_Base->NumH, Ct_BaseIso->lenNumH*sizeof(Ct_BaseIso->NumH[0])); + if ( Ct_FixHIso ) + { + ret |= (Ct_FixHIso->lenCt > Ct_BaseIso->lenCt) || memcmp(Ct_FixHIso->Ctbl, Ct_BaseIso->Ctbl, Ct_FixHIso->lenCt * sizeof(Ct_FixHIso->Ctbl[0])); + ret |= (Ct_FixHIso->lenNumH > Ct_BaseIso->lenNumH) || memcmp( Ct_FixHIso->NumH, Ct_BaseIso->NumH, Ct_FixHIso->lenNumH*sizeof(Ct_BaseIso->NumH[0])); + } + } + + if ( ret ) + { + goto exit_error; + } + + if ( bReqTaut ) + { + /* restore save "process isotopic" mark; temporarily set it to NO */ + t_group_info->bIgnoreIsotopic = bTautIgnoreIsotopic; + } + + + /* output the canonicalization results */ + pBCN->num_max = num_max; + pBCN->num_at_tg = num_at_tg; + pBCN->num_atoms = num_atoms; + + pBCN->ftcn[TAUT_NON].NeighList = NeighList[TAUT_NON]; NeighList[TAUT_NON] = NULL; + pBCN->ftcn[TAUT_YES].NeighList = NeighList[TAUT_YES]; NeighList[TAUT_YES] = NULL; + + if ( bReqTaut ) + { + /* tautomeric results */ + + /* base tautomeric structure, iBase = TAUT_YES */ + + pBCN->ftcn[TAUT_YES].num_at_tg = num_at_tg; + pBCN->ftcn[TAUT_YES].num_atoms = num_atoms; + + pBCN->ftcn[TAUT_YES].LinearCt = Ct_Base->Ctbl; Ct_Base->Ctbl = NULL; + pBCN->ftcn[TAUT_YES].nLenLinearCtAtOnly = s[iBase].nLenCTAtOnly; + pBCN->ftcn[TAUT_YES].nMaxLenLinearCt = s[iBase].nLenCT+1; + pBCN->ftcn[TAUT_YES].nLenLinearCt = s[iBase].nLenCT; + + pBCN->ftcn[TAUT_YES].PartitionCt.Rank = nCanonRankBase; nCanonRankBase = NULL; + pBCN->ftcn[TAUT_YES].PartitionCt.AtNumber = nAtomNumberCanonBase; nAtomNumberCanonBase = NULL; + pBCN->ftcn[TAUT_YES].nSymmRankCt = nSymmRankBase; nSymmRankBase = NULL; + + pBCN->ftcn[TAUT_YES].nNumHOrig = numH; numH = NULL; + pBCN->ftcn[TAUT_YES].nNumH = Ct_Base->NumH; Ct_Base->NumH = NULL; + pBCN->ftcn[TAUT_YES].nLenNumH = inchi_min(maxlenNumH, Ct_Base->maxlenNumH); + + /* fixed H structure: exists only if the structure is tautomeric */ + pBCN->ftcn[TAUT_YES].nNumHOrigFixH = NULL; + pBCN->ftcn[TAUT_YES].nNumHFixH = NULL; + pBCN->ftcn[TAUT_YES].nLenNumHFixH = 0; + pBCN->ftcn[TAUT_YES].nCanonFlags |= pCD[iBase].nCanonFlags; + + CleanNumH( pBCN->ftcn[TAUT_YES].nNumHOrig, pBCN->ftcn[TAUT_YES].nLenNumH ); + CleanNumH( pBCN->ftcn[TAUT_YES].nNumH, pBCN->ftcn[TAUT_YES].nLenNumH ); + CleanCt ( pBCN->ftcn[TAUT_YES].LinearCt, pBCN->ftcn[TAUT_YES].nLenLinearCt ); + + /* isotopic canonicalization */ + if ( Ct_BaseIso ) + { + pBCN->ftcn[TAUT_YES].PartitionCtIso.Rank = nCanonRankBaseIso; nCanonRankBaseIso = NULL; + pBCN->ftcn[TAUT_YES].PartitionCtIso.AtNumber = nAtomNumberCanonBaseIso; nAtomNumberCanonBaseIso = NULL; + pBCN->ftcn[TAUT_YES].nSymmRankCtIso = nSymmRankBaseIso; nSymmRankBaseIso = NULL; + pBCN->ftcn[TAUT_YES].iso_sort_keys = Ct_BaseIso->iso_sort_key; Ct_BaseIso->iso_sort_key = NULL; + pBCN->ftcn[TAUT_YES].iso_sort_keysOrig = iso_sort_keyBase; iso_sort_keyBase = NULL; + pBCN->ftcn[TAUT_YES].len_iso_sort_keys = len_iso_sort_keyBase; + pBCN->ftcn[TAUT_YES].iso_exchg_atnos = Ct_BaseIso->iso_exchg_atnos; Ct_BaseIso->iso_exchg_atnos = NULL; + pBCN->ftcn[TAUT_YES].iso_exchg_atnosOrig = iso_exchg_atnos; iso_exchg_atnos = NULL; + + CleanIsoSortKeys( pBCN->ftcn[TAUT_YES].iso_sort_keys, pBCN->ftcn[TAUT_YES].len_iso_sort_keys ); + CleanIsoSortKeys( pBCN->ftcn[TAUT_YES].iso_sort_keysOrig, pBCN->ftcn[TAUT_YES].len_iso_sort_keys ); + } + } /* tautomeric results */ + + + if ( bReqNonTaut ) + { + /* non-tautomeric results */ + + /* TAUT_NON if tautomeric + non-tautomeric or special non-taut request + TAUT_YES if the structure happened to be non-tautomeric while user requested tautomeric processing + In both cases the correct index is iOther. TAUT_NON replaced with iOther 4-2-2004 */ + + if ( !bReqTaut ) + { + /* rearrange the results for a non-tautomeric structure */ + nSymmRankFixH = nSymmRankNoTautH; nSymmRankNoTautH = NULL; + nCanonRankFixH = nCanonRankNoTautH; nCanonRankNoTautH = NULL; + nAtomNumberCanonFixH = nAtomNumberCanonNoTautH; nAtomNumberCanonNoTautH = NULL; + Ct_FixH = Ct_NoTautH; Ct_NoTautH = NULL; + /* isotopic canonicalization */ + nSymmRankFixHIso = nSymmRankNoTautHIso; nSymmRankNoTautHIso = NULL; + nCanonRankFixHIso = nCanonRankNoTautHIso; nCanonRankNoTautHIso = NULL; + nAtomNumberCanonFixHIso = nAtomNumberCanonNoTautHIso; nAtomNumberCanonNoTautHIso = NULL; + Ct_FixHIso = Ct_NoTautHIso; Ct_NoTautHIso = NULL; + + if ( iOther == TAUT_YES && pBCN->ftcn[TAUT_NON].NeighList && !pBCN->ftcn[TAUT_YES].NeighList ) + { + /* here only non-taut results go to pBCN->ftcn[TAUT_YES] + Since non-taut NeighList is always in pBCN->ftcn[TAUT_NON].NeighList, move it to + pBCN->ftcn[TAUT_YES].NeighList. 2004-04-02. + */ + pBCN->ftcn[TAUT_YES].NeighList = pBCN->ftcn[TAUT_NON].NeighList; + pBCN->ftcn[TAUT_NON].NeighList = NULL; + } + } + + pBCN->ftcn[iOther].num_at_tg = num_atoms; + pBCN->ftcn[iOther].num_atoms = num_atoms; + + pBCN->ftcn[iOther].LinearCt = Ct_FixH->Ctbl; + Ct_FixH->Ctbl = NULL; + pBCN->ftcn[iOther].nLenLinearCtAtOnly = s[iOther].nLenCTAtOnly; + pBCN->ftcn[iOther].nMaxLenLinearCt = s[iOther].nLenCTAtOnly+1; + pBCN->ftcn[iOther].nLenLinearCt = s[iOther].nLenCTAtOnly; + + pBCN->ftcn[iOther].PartitionCt.Rank = nCanonRankFixH; + nCanonRankFixH = NULL; + pBCN->ftcn[iOther].PartitionCt.AtNumber = nAtomNumberCanonFixH; + nAtomNumberCanonFixH = NULL; + pBCN->ftcn[iOther].nSymmRankCt = nSymmRankFixH; + nSymmRankFixH = NULL; + + pBCN->ftcn[iOther].nNumHOrig = numHNoTautH; + numHNoTautH = NULL; + pBCN->ftcn[iOther].nNumH = Ct_FixH->NumH; + Ct_FixH->NumH = NULL; + pBCN->ftcn[iOther].nLenNumH = inchi_min(maxlenNumHNoTautH,Ct_FixH->maxlenNumH); + + /* fixed H structure: exists only if the structure is tautomeric */ + pBCN->ftcn[iOther].nNumHOrigFixH = NumHfixed; + NumHfixed = NULL; + pBCN->ftcn[iOther].nNumHFixH = Ct_FixH->NumHfixed; + Ct_FixH->NumHfixed = NULL; + pBCN->ftcn[iOther].nLenNumHFixH = num_atoms; + pBCN->ftcn[iOther].nCanonFlags |= pCD[iOther].nCanonFlags; + + /* original H */ + CleanNumH( pBCN->ftcn[iOther].nNumHOrig, pBCN->ftcn[iOther].nLenNumH ); + CleanNumH( pBCN->ftcn[iOther].nNumHOrigFixH, pBCN->ftcn[iOther].nLenNumH ); + /* canonical H positions */ + CleanNumH( pBCN->ftcn[iOther].nNumH, pBCN->ftcn[iOther].nLenNumH ); + CleanNumH( pBCN->ftcn[iOther].nNumHFixH, pBCN->ftcn[iOther].nLenNumH ); + + /* connection table */ + CleanCt( pBCN->ftcn[iOther].LinearCt, pBCN->ftcn[iOther].nLenLinearCt ); + + /* isotopic canonicalization */ + if ( Ct_FixHIso ) + { + pBCN->ftcn[iOther].PartitionCtIso.Rank = nCanonRankFixHIso; nCanonRankFixHIso = NULL; + pBCN->ftcn[iOther].PartitionCtIso.AtNumber = nAtomNumberCanonFixHIso; nAtomNumberCanonFixHIso = NULL; + pBCN->ftcn[iOther].nSymmRankCtIso = nSymmRankFixHIso; nSymmRankFixHIso = NULL; + pBCN->ftcn[iOther].iso_sort_keys = Ct_FixHIso->iso_sort_key; Ct_FixHIso->iso_sort_key = NULL; + pBCN->ftcn[iOther].iso_sort_keysOrig = iso_sort_key_NoTautH; iso_sort_key_NoTautH = NULL; + pBCN->ftcn[iOther].len_iso_sort_keys = len_iso_sort_key_NoTautH; + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + MergeCleanIsoSortKeys( pBCN->ftcn[iOther].iso_sort_keys, Ct_FixHIso->iso_sort_key_Hfixed, pBCN->ftcn[iOther].len_iso_sort_keys ); + MergeCleanIsoSortKeys( pBCN->ftcn[iOther].iso_sort_keysOrig, iso_sort_key_Hfixed, pBCN->ftcn[iOther].len_iso_sort_keys ); +#else + CleanIsoSortKeys( pBCN->ftcn[iOther].iso_sort_keys, pBCN->ftcn[iOther].len_iso_sort_keys ); + CleanIsoSortKeys( pBCN->ftcn[iOther].iso_sort_keysOrig, pBCN->ftcn[iOther].len_iso_sort_keys ); +#endif + } + } /* non-tautomeric results */ + + goto exit_function; + + +exit_error_alloc: + ret = CT_OUT_OF_RAM; + goto exit_function; + +exit_error: + if ( !RETURNED_ERROR(ret) ) + { + ret = CT_CANON_ERR; + } + goto exit_function; + + +exit_function: + +#define FREE_CONTABLE( X) if (X) {CTableFree( X);inchi_free( X);} +#define FREE_ARRAY( X) if (X) inchi_free( X); + + FreeNeighList( NeighList[TAUT_NON] ); + FreeNeighList( NeighList[TAUT_YES] ); + + FREE_CONTABLE( Ct_NoH ) + FREE_CONTABLE( Ct_NoTautH ) + FREE_CONTABLE( Ct_Base ) + FREE_CONTABLE( Ct_FixH ) + FREE_CONTABLE( Ct_Temp ) + /* isotopic canonicalization */ + FREE_CONTABLE( Ct_NoTautHIso ) + FREE_CONTABLE( Ct_BaseIso ) + FREE_CONTABLE( Ct_FixHIso ) + + /* free the first two pointers from pBCN->pRankStack */ + FREE_ARRAY( nRank ) + FREE_ARRAY( nAtomNumber ) + + if ( pBCN->pRankStack ) + { + pBCN->pRankStack[0] = + pBCN->pRankStack[1] = NULL; + } + +#if ( USE_AUX_RANKING == 1 ) + FREE_ARRAY( nRankAux ) + FREE_ARRAY( nAtomNumberAux ) + FREE_ARRAY( pAtomInvariantAux ) +#endif + + FREE_ARRAY( pAtomInvariant ) + + FREE_ARRAY( nCanonRankNoH ) + FREE_ARRAY( nAtomNumberCanonNoH ) + FREE_ARRAY( nSymmRankNoH ) + + FREE_ARRAY( nSymmRankNoTautH ) + FREE_ARRAY( nCanonRankNoTautH ) + FREE_ARRAY( nAtomNumberCanonNoTautH ) + FREE_ARRAY( numHNoTautH ) + + FREE_ARRAY( nSymmRankBase ) + FREE_ARRAY( nCanonRankBase ) + FREE_ARRAY( nAtomNumberCanonBase ) + FREE_ARRAY( numH ) + + FREE_ARRAY( nSymmRankFixH ) + FREE_ARRAY( nCanonRankFixH ) + FREE_ARRAY( nAtomNumberCanonFixH ) + FREE_ARRAY( NumHfixed ) + + /* isotopic canonicalization */ + + FREE_ARRAY( nSymmRankNoTautHIso ) + FREE_ARRAY( nCanonRankNoTautHIso ) + FREE_ARRAY( nAtomNumberCanonNoTautHIso ) + FREE_ARRAY( iso_sort_key_NoTautH ) + + FREE_ARRAY( nSymmRankBaseIso ) + FREE_ARRAY( nCanonRankBaseIso ) + FREE_ARRAY( nAtomNumberCanonBaseIso ) + FREE_ARRAY( iso_sort_keyBase ) + FREE_ARRAY( iso_exchg_atnos ) + + FREE_ARRAY( nSymmRankFixHIso ) + FREE_ARRAY( nCanonRankFixHIso ) + FREE_ARRAY( nAtomNumberCanonFixHIso ) + +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + FREE_ARRAY( iso_sort_key_Hfixed ) +#endif + + FREE_ARRAY( nTempRank ) + +#undef FREE_CONTABLE +#undef FREE_ARRAY + + return ret; +} diff --git a/INCHI-1-SRC/INCHI/common/ichicano.c b/INCHI-1-SRC/INCHI_BASE/src/ichicano.c similarity index 75% rename from INCHI-1-SRC/INCHI/common/ichicano.c rename to INCHI-1-SRC/INCHI_BASE/src/ichicano.c index 381b9a8..e8f4ab2 100644 --- a/INCHI-1-SRC/INCHI/common/ichicano.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichicano.c @@ -1,2252 +1,2667 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include - -#include "mode.h" - -#include "ichitime.h" -#include "ichi.h" -#include "util.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "inpdef.h" -#include "ichinorm.h" -#include "ichicant.h" -#include "ichicano.h" -#include "ichicomn.h" - -#include "ichicomp.h" - -/**************************************************************************** - * - * Globals for sorting - */ - -const NEIGH_LIST *pNeighList_RankForSort; -const ATOM_INVARIANT2 *pAtomInvariant2ForSort; -const AT_NUMB *pNeighborsForSort; -const AT_RANK *pn_RankForSort; - -AT_RANK nMaxAtNeighRankForSort; - -int nNumCompNeighborsRanksCountEql; - - -#define tsort insertions_sort - -/* local prototypes */ - - -void FillOutAtomInvariant( sp_ATOM* at, int num_atoms, int num_at_tg, ATOM_INVARIANT* pAtomInvariant, CANON_STAT* pCS ); - -int Canon_INChI1( int num_atoms, int num_at_tg, sp_ATOM* at, CANON_STAT* pCS, INCHI_MODE nMode); -int Canon_INChI2( int num_atoms, int num_at_tg, sp_ATOM* at, CANON_STAT* pCS, INCHI_MODE nMode); -int Canon_INChI3(int num_atoms, int num_at_tg, sp_ATOM* at, - CANON_STAT* pCS, INCHI_MODE nMode, int bTautFtcn); - - -#ifdef COMPILE_ANSI_ONLY - -static clock_t InchiClock(void); - -#ifdef INCHI_USETIMES -static clock_t InchiClock(void) -{ - struct tms buf; - clock_t c = times( &buf ); - if ( c != (clock_t)-1 ) { - return buf.tms_utime; - } - return 0; -} -#else -static clock_t InchiClock(void) -{ - clock_t c = clock(); - if ( c != (clock_t)-1 ) { - return c; - } - return 0; -} -#endif - -#define INCHI_MSEC(X) (long)((1000.0/(double)CLOCKS_PER_SEC)*(X)) -#define INCHI_CLOCK_T(X) (clock_t)( (double)(X) / 1000.0 * (double)CLOCKS_PER_SEC ) -const clock_t FullMaxClock = (clock_t)(-1); -const clock_t HalfMaxClock = (clock_t)(-1) / 2; -clock_t MaxPositiveClock = 0; -clock_t MinNegativeClock = 0; -clock_t HalfMaxPositiveClock = 0; -clock_t HalfMinNegativeClock = 0; - -static void FillMaxMinClock(void); /* keep compiler happy */ - -static void FillMaxMinClock(void) -{ - if ( !MaxPositiveClock ) { - clock_t valPos=0, val1 = 1; - while ( 0 < ((val1 <<= 1), (val1 |= 1)) ) { - valPos = val1; - } - MaxPositiveClock = valPos; - MinNegativeClock = -valPos; - HalfMaxPositiveClock = MaxPositiveClock / 2; - HalfMinNegativeClock = MinNegativeClock / 2; - } -} - - -/******** get current process time ****************************************/ -void InchiTimeGet( inchiTime *TickEnd ) -{ - TickEnd->clockTime = InchiClock(); -} -/******** returns difference TickEnd - TickStart in milliseconds **********/ -long InchiTimeMsecDiff( inchiTime *TickEnd, inchiTime *TickStart ) -{ - if ( FullMaxClock > 0 ) { - clock_t delta; - if ( !TickEnd || !TickStart ) - return 0; - /* clock_t is unsigned */ - if ( TickEnd->clockTime > TickStart->clockTime ) { - if ( TickEnd->clockTime > HalfMaxClock && - TickEnd->clockTime - TickStart->clockTime > HalfMaxClock ) { - /* overflow in TickStart->clockTime, actually TickStart->clockTime was later */ - delta = (FullMaxClock - TickEnd->clockTime) + TickStart->clockTime; - return -INCHI_MSEC(delta); - } - delta = TickEnd->clockTime - TickStart->clockTime; - return INCHI_MSEC(delta); - } else - if ( TickEnd->clockTime < TickStart->clockTime ) { - if ( TickStart->clockTime > HalfMaxClock && - TickStart->clockTime - TickEnd->clockTime > HalfMaxClock ) { - /* overflow in TickEnd->clockTime, actually TickEnd->clockTime was later */ - delta = (FullMaxClock - TickStart->clockTime) + TickEnd->clockTime; - return INCHI_MSEC(delta); - } - delta = TickStart->clockTime - TickEnd->clockTime; - return -INCHI_MSEC(delta); - } - return 0; /* TickEnd->clockTime == TickStart->clockTime */ - } else { - /* may happen under Win32 only where clock_t is SIGNED long */ - clock_t delta; - FillMaxMinClock( ); - if ( !TickEnd || !TickStart ) - return 0; - if ( TickEnd->clockTime >= 0 && TickStart->clockTime >= 0 || - TickEnd->clockTime <= 0 && TickStart->clockTime <= 0) { - delta = TickEnd->clockTime - TickStart->clockTime; - } else - if ( TickEnd->clockTime >= HalfMaxPositiveClock && - TickStart->clockTime <= HalfMinNegativeClock ) { - /* end is earlier than start */ - delta = (MaxPositiveClock - TickEnd->clockTime) + (TickStart->clockTime - MinNegativeClock); - delta = -delta; - } else - if ( TickEnd->clockTime <= HalfMinNegativeClock && - TickStart->clockTime >= HalfMaxPositiveClock ) { - /* start was earlier than end */ - delta = (MaxPositiveClock - TickStart->clockTime) + (TickEnd->clockTime - MinNegativeClock); - } else { - /* there was no overflow, clock passed zero */ - delta = TickEnd->clockTime - TickStart->clockTime; - } - return INCHI_MSEC(delta); - } -} -/******************* get elapsed time from TickStart ************************/ -long InchiTimeElapsed( inchiTime *TickStart ) -{ - inchiTime TickEnd; - if ( !TickStart ) - return 0; - InchiTimeGet( &TickEnd ); - return InchiTimeMsecDiff( &TickEnd, TickStart ); -} -/******************* add number of milliseconds to time *********************/ -void InchiTimeAddMsec( inchiTime *TickEnd, unsigned long nNumMsec ) -{ - clock_t delta; - if ( !TickEnd ) - return; - if ( FullMaxClock > 0 ) { - /* clock_t is unsigned */ - delta = INCHI_CLOCK_T(nNumMsec); - TickEnd->clockTime += delta; - } else { - /* may happen under Win32 only where clock_t is SIGNED long */ - /* clock_t is unsigned */ - FillMaxMinClock( ); - delta = INCHI_CLOCK_T(nNumMsec); - TickEnd->clockTime += delta; - } -} -/******************* check whether time has expired *********************/ -int bInchiTimeIsOver( inchiTime *TickStart ) -{ - if ( FullMaxClock > 0 ) { - clock_t clockCurrTime; - if ( !TickStart ) - return 0; - clockCurrTime = InchiClock(); - /* clock_t is unsigned */ - if ( TickStart->clockTime > clockCurrTime ) { - if ( TickStart->clockTime > HalfMaxClock && - TickStart->clockTime - clockCurrTime > HalfMaxClock ) { - /* overflow in clockCurrTime, actually clockCurrTime was later */ - return 1; - } - return 0; - } else - if ( TickStart->clockTime < clockCurrTime ) { - if ( clockCurrTime > HalfMaxClock && - clockCurrTime - TickStart->clockTime > HalfMaxClock ) { - /* overflow in TickStart->clockTime, actually TickStart->clockTime was later */ - return 0; - } - return 1; - } - return 0; /* TickStart->clockTime == clockCurrTime */ - } else { - /* may happen under Win32 only where clock_t is SIGNED long */ - clock_t clockCurrTime; - FillMaxMinClock( ); - if ( !TickStart ) - return 0; - clockCurrTime = InchiClock(); - if ( clockCurrTime >= 0 && TickStart->clockTime >= 0 || - clockCurrTime <= 0 && TickStart->clockTime <= 0) { - return (clockCurrTime > TickStart->clockTime); - } else - if ( clockCurrTime >= HalfMaxPositiveClock && - TickStart->clockTime <= HalfMinNegativeClock ) { - /* curr is earlier than start */ - return 0; - } else - if ( clockCurrTime <= HalfMinNegativeClock && - TickStart->clockTime >= HalfMaxPositiveClock ) { - /* start was earlier than curr */ - return 1; - } else { - /* there was no overflow, clock passed zero */ - return (clockCurrTime > TickStart->clockTime); - } - } -} - -#else - -/******** get current process time ****************************************/ -void InchiTimeGet( inchiTime *TickEnd ) -{ - if ( TickEnd ) { - struct _timeb timeb; - _ftime( &timeb ); - TickEnd->clockTime = (unsigned long)timeb.time; - TickEnd->millitime = (long)timeb.millitm; - } -} -/******** returns difference TickEnd - TickStart in milliseconds **********/ -long InchiTimeMsecDiff( inchiTime *TickEnd, inchiTime *TickStart ) -{ - long delta; - if ( !TickEnd || !TickStart ) { - return 0; - } - if ( TickEnd->clockTime >= TickStart->clockTime ) { - delta = (long)(TickEnd->clockTime - TickStart->clockTime); - delta *= 1000; - delta += TickEnd->millitime - TickStart->millitime; - } else { - delta = -(long)(TickStart->clockTime - TickEnd->clockTime); - delta *= 1000; - delta += TickEnd->millitime - TickStart->millitime; - } - return delta; -} -/******************* get elapsed time from TickStart ************************/ -long InchiTimeElapsed( inchiTime *TickStart ) -{ - inchiTime TickEnd; - if ( !TickStart ) - return 0; - InchiTimeGet( &TickEnd ); - return InchiTimeMsecDiff( &TickEnd, TickStart ); -} -/******************* add number of milliseconds to time *********************/ -void InchiTimeAddMsec( inchiTime *TickEnd, unsigned long nNumMsec ) -{ - long delta; - if ( !TickEnd ) - return; - TickEnd->clockTime += nNumMsec / 1000; - delta = nNumMsec % 1000 + TickEnd->millitime; - TickEnd->clockTime += delta / 1000; - TickEnd->millitime = delta % 1000; -} -/******************* check whether time has expired *********************/ -int bInchiTimeIsOver( inchiTime *TickEnd ) -{ - struct _timeb timeb; - if ( !TickEnd ) - return 0; - _ftime( &timeb ); - if ( TickEnd->clockTime > (unsigned long)timeb.time ) - return 0; - if ( TickEnd->clockTime < (unsigned long)timeb.time || - TickEnd->millitime < (long)timeb.millitm ) { - return 1; - } - return 0; -} -#endif - - -/****************************************************************************/ -/* length of canonic representation in sizeof(AT_NUMB) units */ -int GetCanonLengths( int num_at, sp_ATOM* at, ATOM_SIZES *s, T_GROUP_INFO *t_group_info ) -{ /* include taut. groups as additional "atoms" to the connection table 07-22-2002 */ - int i, nNumCT, nNumBonds, nNumTBonds=0, nNumDblBondsStereo=0, nNumAsymCarbStereo=0, nNumIsotopic=0; - T_GROUP *t_group = (s->nLenLinearCTTautomer && t_group_info)? t_group_info->t_group : NULL; - for (nNumBonds = 0, i = 0; i < num_at; i ++ ) { - nNumBonds += at[i].valence; - if ( at[i].iso_sort_key ) { - nNumIsotopic ++; /* not including tautomeric endpoints that are isotopic only due to mobile atoms */ - } - - if ( at[i].parity > 0 ) { /* ignore hydrogen isotope parities in at[i].parity2 */ - int j = 0, nStereoBondsToAtom=0; /* number of stereo double bonds at this atom */ - int k; - for ( ; j < MAX_NUM_STEREO_BONDS && (k=at[i].stereo_bond_neighbor[j]); j ++ ) { - nStereoBondsToAtom += (at[k-1].parity > 0); - } - nNumDblBondsStereo += nStereoBondsToAtom; - nNumAsymCarbStereo += !j; - } - } - nNumDblBondsStereo /= 2; - nNumBonds /= 2; - - s->nLenBonds = inchi_max( s->nLenBonds, nNumBonds ); - nNumCT = nNumBonds; /* total number of neighbors in the CT */ - -#if ( CT_ATOMID != CT_ATOMID_DONTINCLUDE ) - nNumCT += num_at; -#endif - - s->nLenCTAtOnly = inchi_max(s->nLenCTAtOnly, nNumCT); - - if ( t_group ) { - for ( i = 0; i < t_group_info->num_t_groups; i ++ ) { - nNumTBonds += t_group[i].nNumEndpoints; - } - nNumCT += nNumTBonds; -#if ( CT_ATOMID != CT_ATOMID_DONTINCLUDE ) - nNumCT += t_group_info->num_t_groups; -#endif - } - - nNumCT = inchi_max( 1, nNumCT ); /* keep GetBaseCanonRanking() happy */ - s->nLenCT = inchi_max(s->nLenCT, nNumCT); - s->nLenIsotopic = inchi_max(s->nLenIsotopic, nNumIsotopic); - s->nLenLinearCTStereoDble = inchi_max(s->nLenLinearCTStereoDble, nNumDblBondsStereo); - s->nLenLinearCTStereoCarb = inchi_max(s->nLenLinearCTStereoCarb, nNumAsymCarbStereo); - if ( t_group_info ) - s->nLenIsotopicEndpoints = inchi_max(s->nLenIsotopicEndpoints, t_group_info->nNumIsotopicEndpoints); - - return 0; -} -/****************************************************************************/ -int DeAllocateCS( CANON_STAT *pCS ) -{ -#define LOCAL_FREE(X) do{if(X){inchi_free(X); X=NULL;}}while(0) - - /* connection table */ - LOCAL_FREE( pCS->LinearCT ); - LOCAL_FREE( pCS->nCanonOrd ); - LOCAL_FREE( pCS->nSymmRank ); - LOCAL_FREE( pCS->nNum_H ); - LOCAL_FREE( pCS->nNum_H_fixed ); - LOCAL_FREE( pCS->nExchgIsoH ); - /* isotopic */ - LOCAL_FREE( pCS->LinearCTIsotopic ); - LOCAL_FREE( pCS->nSymmRankIsotopic ); - LOCAL_FREE( pCS->nCanonOrdIsotopic ); - /* isotopic tautomeric */ - LOCAL_FREE( pCS->LinearCTIsotopicTautomer ); - LOCAL_FREE( pCS->nCanonOrdIsotopicTaut ); - LOCAL_FREE( pCS->nSymmRankIsotopicTaut ); - /* stereo */ - LOCAL_FREE( pCS->LinearCTStereoDble ); - LOCAL_FREE( pCS->LinearCTStereoCarb ); - LOCAL_FREE( pCS->LinearCTStereoDbleInv ); - LOCAL_FREE( pCS->LinearCTStereoCarbInv ); - LOCAL_FREE( pCS->nCanonOrdStereo ); - LOCAL_FREE( pCS->nCanonOrdStereoInv ); - LOCAL_FREE( pCS->nCanonOrdStereoTaut ); - /* isotopic stereo */ - LOCAL_FREE( pCS->LinearCTIsotopicStereoDble ); - LOCAL_FREE( pCS->LinearCTIsotopicStereoCarb ); - LOCAL_FREE( pCS->LinearCTIsotopicStereoDbleInv ); - LOCAL_FREE( pCS->LinearCTIsotopicStereoCarbInv ); - LOCAL_FREE( pCS->bRankUsedForStereo ); - LOCAL_FREE( pCS->bAtomUsedForStereo ); - - LOCAL_FREE( pCS->nCanonOrdIsotopicStereo ); - LOCAL_FREE( pCS->nCanonOrdIsotopicStereoInv ); - LOCAL_FREE( pCS->nCanonOrdIsotopicStereoTaut ); - /* tautomeric part of the connection table */ - LOCAL_FREE( pCS->LinearCTTautomer ); - LOCAL_FREE( pCS->nCanonOrdTaut ); - LOCAL_FREE( pCS->nSymmRankTaut ); - - LOCAL_FREE( pCS->LinearCT2 ); - - /* for establishing constitutional equivalence */ - LOCAL_FREE( pCS->nPrevAtomNumber ); - - FreeNeighList( pCS->NeighList ); - pCS->NeighList = NULL; - - /* set zero lengths */ - pCS->nMaxLenLinearCTStereoDble = 0; - pCS->nLenLinearCTStereoDble = 0; - pCS->nMaxLenLinearCTStereoCarb = 0; - pCS->nLenLinearCTStereoCarb = 0; - pCS->nMaxLenLinearCTIsotopicStereoDble = 0; - pCS->nLenLinearCTIsotopicStereoDble = 0; - pCS->nMaxLenLinearCTIsotopicStereoCarb = 0; - pCS->nLenLinearCTIsotopicStereoCarb = 0; - pCS->nMaxLenLinearCTTautomer = 0; - pCS->nLenLinearCTTautomer = 0; - pCS->nMaxLenLinearCTIsotopic = 0; - pCS->nLenLinearCTIsotopic = 0; - pCS->nMaxLenLinearCTIsotopicTautomer = 0; - pCS->nLenLinearCTIsotopicTautomer = 0; - - /* set canon numbering lengths to zero */ - pCS->nLenCanonOrd = 0; - pCS->nLenCanonOrdIsotopic = 0; - pCS->nLenCanonOrdIsotopicTaut = 0; - pCS->nLenCanonOrdStereo = 0; - pCS->nLenCanonOrdStereoTaut = 0; - pCS->nLenCanonOrdIsotopicStereo = 0; - pCS->nLenCanonOrdIsotopicStereoTaut = 0; - pCS->nLenCanonOrdTaut = 0; - - return 0; - -#undef LOCAL_FREE -} -/****************************************************************************/ -int AllocateCS( CANON_STAT *pCS, int num_at, int num_at_tg, int nLenCT, int nLenCTAtOnly, - int nLenLinearCTStereoDble, int nLenLinearCTIsotopicStereoDble, - int nLenLinearCTStereoCarb, int nLenLinearCTIsotopicStereoCarb, - int nLenLinearCTTautomer, int nLenLinearCTIsotopicTautomer, - int nLenIsotopic, INCHI_MODE nMode, BCN *pBCN ) -{ -#define pCS_CALLOC(PTR,TYPE,LEN) (pCS->PTR=(TYPE*)inchi_calloc((size_t)(LEN),sizeof(*pCS->PTR))) - - int num_err = 0; - int num_t_groups = num_at_tg - num_at; - - pCS->nMode = nMode; - /* connection table */ - if ( (nMode & CMODE_CT) && nLenCT > 0 ) { - num_err += !pCS_CALLOC(LinearCT, AT_NUMB, nLenCT); - pCS->nMaxLenLinearCT = - pCS->nLenLinearCT = nLenCT; - pCS->nLenLinearCTAtOnly = nLenCTAtOnly; - num_err += !pCS_CALLOC(nCanonOrd, AT_RANK, num_at_tg); - num_err += !pCS_CALLOC(nSymmRank, AT_RANK, num_at_tg); - if ( pBCN ) { - num_err += !pCS_CALLOC(nNum_H, S_CHAR, num_at); - num_err += !pCS_CALLOC(nNum_H_fixed, S_CHAR, num_at); - num_err += !pCS_CALLOC(nExchgIsoH, S_CHAR, num_at); - } - } - /* isotopic */ - if ( (nMode & CMODE_ISO) && nLenIsotopic > 0 ) { - num_err += !pCS_CALLOC(LinearCTIsotopic, AT_ISOTOPIC, nLenIsotopic); - pCS->nMaxLenLinearCTIsotopic = - pCS->nLenLinearCTIsotopic = nLenIsotopic; - } - /* isotopic tautomeric */ - if ( (nMode & CMODE_ISO) && CANON_MODE_TAUT == (nMode & CANON_MODE_TAUT) ) { - if ( nLenLinearCTIsotopicTautomer > 0 ) { - num_err += !pCS_CALLOC(LinearCTIsotopicTautomer, AT_ISO_TGROUP, nLenLinearCTIsotopicTautomer); - pCS->nMaxLenLinearCTIsotopicTautomer = - pCS->nLenLinearCTIsotopicTautomer = nLenLinearCTIsotopicTautomer; - } - if ( num_t_groups > 0 ) { - num_err += !pCS_CALLOC(nCanonOrdIsotopicTaut, AT_RANK, num_t_groups); - num_err += !pCS_CALLOC(nSymmRankIsotopicTaut, AT_RANK, num_t_groups); - } - } - /* isotopic atoms & t-groups */ - if ( (nMode & CMODE_ISO) /*&& nLenIsotopic > 0*/ || - (nMode & CMODE_ISO) && CANON_MODE_TAUT == (nMode & CANON_MODE_TAUT) && nLenLinearCTIsotopicTautomer > 0 - ) { - num_err += !pCS_CALLOC(nSymmRankIsotopic, AT_RANK, num_at_tg); - num_err += !pCS_CALLOC(nCanonOrdIsotopic, AT_RANK, num_at_tg); - } - /* stereo */ - if ( (nMode & CMODE_STEREO) && nLenLinearCTStereoDble > 0 ) { - num_err += !pCS_CALLOC(LinearCTStereoDble, AT_STEREO_DBLE, nLenLinearCTStereoDble); - num_err += !pCS_CALLOC(LinearCTStereoDbleInv, AT_STEREO_DBLE, nLenLinearCTStereoDble); - pCS->nLenLinearCTStereoDbleInv = - pCS->nMaxLenLinearCTStereoDble = - pCS->nLenLinearCTStereoDble = nLenLinearCTStereoDble; - } - if ( (nMode & CMODE_STEREO) && nLenLinearCTStereoCarb > 0 ) { - num_err += !pCS_CALLOC(LinearCTStereoCarb, AT_STEREO_CARB, nLenLinearCTStereoCarb); - num_err += !pCS_CALLOC(LinearCTStereoCarbInv, AT_STEREO_CARB, nLenLinearCTStereoCarb); - pCS->nLenLinearCTStereoCarbInv = - pCS->nMaxLenLinearCTStereoCarb = - pCS->nLenLinearCTStereoCarb = nLenLinearCTStereoCarb; - } - if ( (nMode & CMODE_STEREO) && (nLenLinearCTStereoDble > 0 || nLenLinearCTStereoCarb > 0 ) ) { - num_err += !pCS_CALLOC(nCanonOrdStereo, AT_RANK, num_at_tg); - num_err += !pCS_CALLOC(nCanonOrdStereoInv, AT_RANK, num_at_tg); - if ( (nMode & CMODE_TAUT) && nLenLinearCTTautomer > 0 && num_t_groups > 0 ) { - num_err += !pCS_CALLOC(nCanonOrdStereoTaut, AT_RANK, num_t_groups); - } - } - /* isotopic stereo */ - if ( (nMode & CMODE_ISO_STEREO) && nLenLinearCTIsotopicStereoDble > 0 ) { - num_err += !pCS_CALLOC(LinearCTIsotopicStereoDble, AT_STEREO_DBLE, nLenLinearCTIsotopicStereoDble); - num_err += !pCS_CALLOC(LinearCTIsotopicStereoDbleInv, AT_STEREO_DBLE, nLenLinearCTIsotopicStereoDble); - pCS->nLenLinearCTIsotopicStereoDbleInv = - pCS->nMaxLenLinearCTIsotopicStereoDble = - pCS->nLenLinearCTIsotopicStereoDble = nLenLinearCTIsotopicStereoDble; - } - if ( (nMode & CMODE_ISO_STEREO) && nLenLinearCTIsotopicStereoCarb > 0 ) { - num_err += !pCS_CALLOC(LinearCTIsotopicStereoCarb, AT_STEREO_CARB, nLenLinearCTIsotopicStereoCarb); - num_err += !pCS_CALLOC(LinearCTIsotopicStereoCarbInv, AT_STEREO_CARB, nLenLinearCTIsotopicStereoCarb); - pCS->nLenLinearCTIsotopicStereoCarbInv = - pCS->nMaxLenLinearCTIsotopicStereoCarb = - pCS->nLenLinearCTIsotopicStereoCarb = nLenLinearCTIsotopicStereoCarb; - } - if ( (nMode & CMODE_ISO_STEREO) && (nLenLinearCTIsotopicStereoDble > 0 || nLenLinearCTIsotopicStereoCarb > 0 ) ) { - num_err += !pCS_CALLOC(nCanonOrdIsotopicStereo, AT_RANK, num_at_tg); - num_err += !pCS_CALLOC(nCanonOrdIsotopicStereoInv, AT_RANK, num_at_tg); - if ( (nMode & CMODE_TAUT) && nLenLinearCTTautomer > 0 && num_t_groups > 0 ) { - num_err += !pCS_CALLOC(nCanonOrdIsotopicStereoTaut, AT_RANK, num_t_groups); - } - } - if ( (nMode & CMODE_STEREO) && (nLenLinearCTStereoDble > 0 || nLenLinearCTStereoCarb > 0 ) || - (nMode & CMODE_ISO_STEREO) && (nLenLinearCTIsotopicStereoDble > 0 || nLenLinearCTIsotopicStereoCarb > 0 ) ) { - num_err += !pCS_CALLOC(bRankUsedForStereo, S_CHAR, num_at); - num_err += !pCS_CALLOC(bAtomUsedForStereo, S_CHAR, num_at); - } - /* tautomeric part of the connection table */ - if ( (nMode & CMODE_CT) && (nMode & CMODE_TAUT) && nLenLinearCTTautomer > 0 ) { - num_err += !pCS_CALLOC(LinearCTTautomer, AT_TAUTOMER, nLenLinearCTTautomer); - pCS->nMaxLenLinearCTTautomer = - pCS->nLenLinearCTTautomer = nLenLinearCTTautomer; - if ( num_t_groups > 0 ) { - num_err += !pCS_CALLOC(nCanonOrdTaut, AT_RANK, num_t_groups); - num_err += !pCS_CALLOC(nSymmRankTaut, AT_RANK, num_t_groups); - } - } - - if ( nMode & CMODE_CT ) - num_err += !pCS_CALLOC(LinearCT2, AT_NUMB, nLenCT); - - /* for establishing constitutional equivalence */ - num_err += !pCS_CALLOC(nPrevAtomNumber, AT_RANK, num_at_tg); - - /* set canon numbering lengths to zero */ - pCS->nLenCanonOrd = 0; - pCS->nLenCanonOrdIsotopic = 0; - pCS->nLenCanonOrdIsotopicTaut = 0; - pCS->nLenCanonOrdStereo = 0; - pCS->nLenCanonOrdStereoTaut = 0; - pCS->nLenCanonOrdIsotopicStereo = 0; - pCS->nLenCanonOrdIsotopicStereoTaut = 0; - pCS->nLenCanonOrdTaut = 0; - - - if ( num_err ) { - DeAllocateCS( pCS ); - return CT_OUT_OF_RAM; /* */ - } - return 0; - -#undef pCS_CALLOC -} -/****************************************************************************/ -#define COMPARE_WITH_CT(CT, CTLEN, VALUE, CONDITION) \ - if ( CONDITION ) { \ - if ( (VALUE) CT_GREATER_THAN (CT)[CTLEN] ) \ - return 1; /* not a minimal CT */ \ - (CONDITION) = (VALUE) == (CT)[CTLEN]; \ - } \ - (CT)[CTLEN] = VALUE; \ - (CTLEN)++ - -#define COMPARE_WITH_CTVAL(CTVAL, VALUE, CONDITION) \ - if ( CONDITION ) { \ - if ( (VALUE) CT_GREATER_THAN (CTVAL) ) \ - return 1; /* not a minimal CT */ \ - (CONDITION) = (VALUE) == (CTVAL); \ - } \ - (CTVAL) = VALUE - -#define COMPARE_WITH_CT2(CT, CTLEN, VALUE, CONDITION, OPER) \ - if ( CONDITION ) { \ - if ( (VALUE) CT_GREATER_THAN (CT)[CTLEN] ) { \ - (OPER); \ - return 1; /* not a minimal CT */ \ - } \ - (CONDITION) = (VALUE) == (CT)[CTLEN]; \ - } \ - (CT)[CTLEN] = VALUE; \ - (CTLEN)++ - -/****************************************************************************/ -int FillIsotopicAtLinearCT(int num_atoms, sp_ATOM* at, - const AT_RANK *nAtomNumber, - AT_ISOTOPIC *LinearCTIsotopic, - int nMaxLenLinearCTIsotopic, int *pnLenLinearCTIsotopic) -{ - /* at[i].init_rank = initial ranks before canonizing */ - /* nRank[i] = new ordering number for atoms: nRank=1,2,.. */ - /* nAtomNumber[r] = orig. atom number= 0,1,... for r = nRank-1 */ - /* nRank[nAtomNumber[r]] = r; r = 0,1,... */ - /* nAtomNumber[nRank[i]-1] = i; */ - - int i, k, rank; - int nLinearCTIsotopicLen=0; - - /* the following parts of the "name" should be compared */ - /* after the connection table comparison is done */ - /* to avoid wrong difference sign. So, these parts */ - /* go to a separate buffers. */ - if ( LinearCTIsotopic && nMaxLenLinearCTIsotopic > 0 ) { - memset( LinearCTIsotopic, 0, nMaxLenLinearCTIsotopic * sizeof(LinearCTIsotopic[0]) ); - } else { - return 0; - } - - /* rank = nRank[nAtomNumber[rank-1]] -- proposed atoms canon. numbers */ - for ( rank = 1; rank <= num_atoms; rank ++ ) { - - i = (int)nAtomNumber[rank-1]; /* current atom */ - - /**************************************************** - add isotopic atom info to LinearCTIsotopic - *****************************************************/ - - /* if the atom itself is not isotopic then add it only if */ - /* the atom is not an endpoint AND has attached T or D or 1H. */ - k = ( !at[i].endpoint && !(at[i].cFlags & AT_FLAG_ISO_H_POINT) && (at[i].num_iso_H[0] || at[i].num_iso_H[1] || at[i].num_iso_H[2]) ); - if ( at[i].iso_atw_diff || k ) { - if ( CHECK_OVERFLOW(nLinearCTIsotopicLen, nMaxLenLinearCTIsotopic) ) - return CT_OVERFLOW; /* */ - LinearCTIsotopic[nLinearCTIsotopicLen].at_num = (AT_RANK)rank; - LinearCTIsotopic[nLinearCTIsotopicLen].iso_atw_diff = at[i].iso_atw_diff; - LinearCTIsotopic[nLinearCTIsotopicLen].num_1H = (NUM_H)(k? at[i].num_iso_H[0] : 0); - LinearCTIsotopic[nLinearCTIsotopicLen].num_D = (NUM_H)(k? at[i].num_iso_H[1] : 0); - LinearCTIsotopic[nLinearCTIsotopicLen].num_T = (NUM_H)(k? at[i].num_iso_H[2] : 0); - nLinearCTIsotopicLen ++; - } - - } /* end of cycle over all atoms. */ - - if ( LinearCTIsotopic ) { - if ( *pnLenLinearCTIsotopic ) { - if ( *pnLenLinearCTIsotopic != nLinearCTIsotopicLen ) - return CT_LEN_MISMATCH; /* */ - }else - *pnLenLinearCTIsotopic = nLinearCTIsotopicLen; - } - - /* Return value: >0 => OK */ - return nLinearCTIsotopicLen; -} - -/****************************************************************************/ -int FillTautLinearCT2(int num_atoms, int num_at_tg, int bIsoTaut, - const AT_RANK *nRank, const AT_RANK *nAtomNumber, - const AT_RANK *nSymmRank, const AT_RANK *nRankIso, - const AT_RANK *nAtomNumberIso, const AT_RANK *nSymmRankIso, - AT_TAUTOMER *LinearCTTautomer, - int nMaxLenLinearCTTautomer, int *pnLenLinearCTTautomer, - AT_ISO_TGROUP *LinearCTIsotopicTautomer, - int nMaxLenLinearCTIsotopicTautomer, - int *pnLenLinearCTIsotopicTautomer, - T_GROUP_INFO *t_group_info) -{ - /* nRank[i] = Canonical numbers of atoms,.. */ - /* nAtomNumber[r] = orig. atom number= 0,1,... for r = nRank-1 */ - /* nRank[nAtomNumber[r]] = r; r = 0,1,... */ - /* nAtomNumber[nRank[i]-1] = i; */ - - T_GROUP *t_group; - - int i, j, len=0, g, num_num, offset, max_len = 0, len_iso=0; - const static int max_num_num = sizeof(t_group->num)/sizeof(t_group->num[0]); - const static int max_num_iso = sizeof(LinearCTIsotopicTautomer->num)/sizeof(LinearCTIsotopicTautomer->num[0])+T_NUM_NO_ISOTOPIC; - - /**************************************************************************** - - Tautomeric groups 07-22-2002, modified 12-2003 - - ****************************************************************************/ - - if ( num_at_tg > num_atoms && t_group_info && t_group_info->num_t_groups ) { - int num_t_groups = t_group_info->num_t_groups; - AT_NUMB *tGroupNumber = t_group_info->tGroupNumber; - AT_NUMB *tSymmRank = tGroupNumber + TGSO_SYMM_RANK*num_t_groups; /* equivalence */ - AT_NUMB *tiSymmRank = tGroupNumber + TGSO_SYMM_IRANK*num_t_groups; - AT_NUMB *tiGroupNumber = tGroupNumber + TGSO_SYMM_IORDER*num_t_groups; - AT_RANK nOffset = (AT_RANK)num_atoms; - /* Fill Canonical ranks and Symmetry Ranks */ - /* memcpy( tPrevGroupNumber, tGroupNumber, num_t_groups*sizeof(tPrevGroupNumber[0])); */ - for ( i = num_atoms, j = 0; i < num_at_tg; i ++, j ++ ) { - /* tPrevGroupNumber[j] = */ - tGroupNumber[j] = nAtomNumber[i] - nOffset; - tSymmRank[j] = nSymmRank[i] - nOffset; - if ( bIsoTaut ) { - tiGroupNumber[j] = nAtomNumberIso[i] - nOffset; - tiSymmRank[j] = nSymmRankIso[i] - nOffset; - } - } - /* Sort enpoints within each tautomeric group according to the canonical ranks */ - pn_RankForSort = nRank; - for ( i = 0; i < num_t_groups; i ++ ) { - qsort( t_group_info->nEndpointAtomNumber + (int)t_group_info->t_group[i].nFirstEndpointAtNoPos, - t_group_info->t_group[i].nNumEndpoints, - sizeof(t_group_info->nEndpointAtomNumber[0]), - CompRank ); - } - /* fill out LinearCTTautomer */ - if ( nMaxLenLinearCTTautomer ) { - max_len = T_GROUP_HDR_LEN * t_group_info->num_t_groups + t_group_info->nNumEndpoints+1; - if ( max_len > nMaxLenLinearCTTautomer ) - return CT_OVERFLOW; /* */ - } - - /**************************************************************** - * tautomer group format (#: is an offset) - **************************************************************** - * HEADER (T_GROUP_HDR_LEN=3+3iso) - * 0: N = number of endpoints ( t_group->nNumEndpoints ) - * 1: number of mobile groups ( t_group->num[0] ) - * 2: number of neg. charges ( t_group->num[1] ) {note: T_NUM_NO_ISOTOPIC=2} - * ENDPOINT RANKS - * 3..N+2: sorted tautomer group endpoint ranks; the sorting order is in - * t_group_info->nEndpointAtomNumber[t_group->nFirstEndpointAtNoPos+j], j=0..N-1 - * - * End mark : N==0 - ****************************************************************/ - /* num_num = t_group_info->bIgnoreIsotopic? T_NUM_NO_ISOTOPIC : max_num_num; */ - num_num = max_num_num; /* always include isotopic info; ignore it at the CT comparison step. */ - for ( i = 0; i < t_group_info->num_t_groups; i ++ ) { - g = tGroupNumber[i]; /* ith tautomeric group number in canonical order */ - t_group = t_group_info->t_group + g; - /******************************************************* - * Tautomer non-isotopic part: LinearCTTautomer - *******************************************************/ - /* check length */ - if ( CHECK_OVERFLOW(len + T_GROUP_HDR_LEN + t_group->nNumEndpoints, max_len) ) - return CT_OVERFLOW; /* */ - - /* t_group header: number of endpoints */ - LinearCTTautomer[len++] = t_group->nNumEndpoints; - /* t_group header: */ - /* (a) number of mobile groups in the t_group (number of H + number of (-) ) and */ - /* (b) number of mobile negative charges (-) in the t_group */ - for ( j = 0; j < T_NUM_NO_ISOTOPIC; j ++ ) { - LinearCTTautomer[len++] = t_group->num[j]; - } - /* t_group endpoint ranks link the group to the tautomeric endpoint atoms in the structure */ - /* according to their ranks */ - for ( j = 0, offset = t_group->nFirstEndpointAtNoPos; j < t_group->nNumEndpoints; j ++ ) { - LinearCTTautomer[len++] = nRank[(int)t_group_info->nEndpointAtomNumber[offset+j]]; - } - } - if ( nMaxLenLinearCTTautomer ) { - LinearCTTautomer[len++] = 0; /* or CT_INITVALUE ??? */ - if ( len != max_len ) { - len = -len; /* program error */ /* */ - } else - if ( *pnLenLinearCTTautomer && *pnLenLinearCTTautomer != len ) { - return CT_LEN_MISMATCH; - } else { - *pnLenLinearCTTautomer = len; - } - } else { - *pnLenLinearCTTautomer = 0; - } - /****************************************************************** - * Isotopic Tautomeric mobile groups part: LinearCTIsotopicTautomer - ******************************************************************/ - if ( nMaxLenLinearCTIsotopicTautomer && !t_group_info->nNumIsotopicEndpoints ) { - for ( i = 0; i < t_group_info->num_t_groups; i ++ ) { - g = tiGroupNumber[i]; /* ith tautomeric group number in canonical order */ - t_group = t_group_info->t_group + g; - /* find if mobile hydrogens are isotopic */ - if ( !t_group->iWeight ) { - continue; /* no isotopic H */ - } - if ( CHECK_OVERFLOW(len_iso, nMaxLenLinearCTIsotopicTautomer) ) - return CT_OVERFLOW; /* */ - for ( j = T_NUM_NO_ISOTOPIC; j < max_num_num && j < max_num_iso; j ++ ) { - /* num_T, num_D, num_1H */ - LinearCTIsotopicTautomer[len_iso].num[j-T_NUM_NO_ISOTOPIC] = t_group->num[j]; - } - /* link to tautomer group LinearCTTautomer[i]: */ - LinearCTIsotopicTautomer[len_iso++].tgroup_num = (AT_NUMB)(i + 1); /* t_group isotopic rank */ - } - } - if ( nMaxLenLinearCTIsotopicTautomer ) { - if ( *pnLenLinearCTIsotopicTautomer && *pnLenLinearCTIsotopicTautomer != len_iso ) { - return CT_LEN_MISMATCH; - } - *pnLenLinearCTIsotopicTautomer = len_iso; - } else { - *pnLenLinearCTIsotopicTautomer = 0; - } - - } - return len; -} -/**************************************************************************** - * - * Update a linear connection table out of final ranks - */ -int UpdateFullLinearCT( int num_atoms, int num_at_tg, sp_ATOM* at, AT_RANK *nRank, AT_RANK *nAtomNumber, - CANON_STAT* pCS, int bFirstTime ) -{ - /* at[i].init_rank = initial ranks before canonizing */ - /* nRank[i] = new ordering number for atoms: nRank=1,2,.. */ - /* nAtomNumber[r] = orig. atom number= 0,1,... for r = nRank-1 */ - /* nRank[nAtomNumber[r]] = r; r = 0,1,... */ - /* nAtomNumber[nRank[i]-1] = i; */ - - AT_NUMB nNeighborNumber[MAXVAL]; - int i, j, k, num_neigh, rank, bCompare; /*, nRetVal; */ - - T_GROUP_INFO *t_group_info = NULL; - T_GROUP *t_group = NULL; - AT_NUMB *nEndpointAtomNumber = NULL; - - int nCTLen=0, nCTLenAtOnly=0; - - AT_NUMB r_neigh; - AT_NUMB *LinearCT = pCS->LinearCT; - - /* the following parts of the "name" should be compared */ - /* after the connection table comparison is done */ - /* to avoid wrong difference sign. So, these parts */ - /* go to a separate buffers. */ - /* -- currently not used at all at all -- */ - -#if CT_ATOMID != CT_ATOMID_DONTINCLUDE - AT_NUMB r0_at_type; -#endif - - bCompare = bFirstTime? 0 : 1; - - if ( num_at_tg > num_atoms ) { - t_group_info = pCS->t_group_info; - t_group = t_group_info->t_group; - } else { - t_group_info = NULL; - t_group = NULL; - } - - /**********************************************************************/ - /* */ - /* CYCLE 1: FILL OUT CONNECTION TABLE(S) FOR ALL ATOMS */ - /* ** NOT INCLUDING ISOTOPIC ATOMS AND 1H, 2H(D), 3H(T) ** */ - /* */ - /* rank = nRank[nAtomNumber[rank-1]] -- proposed atoms canon. numbers */ - /**********************************************************************/ - for ( rank = 1; rank <= num_atoms; rank ++ ) { - i = (int)nAtomNumber[rank-1]; /* current atom */ -#if ( CT_ATOMID == CT_ATOMID_IS_CURRANK ) - r0_at_type = (AT_NUMB)rank; /* current Rank */ -#else -#if ( CT_ATOMID == CT_ATOMID_IS_INITRANK ) - r0_at_type = (AT_NUMB)at[i].init_rank; /* chemical + neighborhood ID */ -#else -#if ( CT_ATOMID == CT_ATOMID_DONTINCLUDE ) -#else - #error Undefined or wrong definition of CT_ATOMID -#endif -#endif -#endif - - /* add atom to the CT */ -#if ( CT_ATOMID != CT_ATOMID_DONTINCLUDE ) - if ( CHECK_OVERFLOW(nCTLen, pCS->nMaxLenLinearCT) ) - return CT_OVERFLOW; /* */ - COMPARE_WITH_CT(LinearCT, nCTLen, r0_at_type, bCompare); -#endif - /******************************************************* - add neighbors and (if required) bonds to CT - ********************************************************/ - - /* sort neighbors */ - num_neigh = at[i].valence; - for ( k = 0; k < num_neigh; k ++) { - nNeighborNumber[k] = (AT_NUMB)k; - } - pNeighborsForSort = at[i].neighbor; - pn_RankForSort = nRank; - insertions_sort( nNeighborNumber, (size_t)num_neigh, sizeof(nNeighborNumber[0]), CompNeighborsAT_NUMBER ); - - for ( k = 0; k < num_neigh; k ++) { - /* rank = (new current atom Rank) */ - if ( (int)(r_neigh = (AT_NUMB)nRank[(int)at[i].neighbor[(int)nNeighborNumber[k]]]) - CT_NEIGH_SMALLER_THAN rank ) { - if ( CHECK_OVERFLOW(nCTLen, pCS->nMaxLenLinearCT) ) - return CT_OVERFLOW; /* */ - COMPARE_WITH_CT( LinearCT, nCTLen, r_neigh, bCompare); - } - } - - /* add CT row delimiter */ - - } /* end of cycle over all atoms. */ - - nCTLenAtOnly = nCTLen; - - /************************************************************** - - Tautomeric groups 07-22-2002 - - ***************************************************************/ - - for ( rank = num_atoms + 1; rank <= num_at_tg; rank ++ ) { - j = (int)nAtomNumber[rank-1]; /* current "atom" */ - i = j - num_atoms; /* current t-group */ -#if ( CT_ATOMID == CT_ATOMID_IS_CURRANK ) - r0_at_type = (AT_NUMB)rank; /* current Rank */ -#else -#if ( CT_ATOMID == CT_ATOMID_IS_INITRANK ) - r0_at_type = (AT_NUMB)rank; /* current Rank or (AT_NUMB)at[i].init_rank; ==> chemical + neighborhood ID */ -#else -#if ( CT_ATOMID == CT_ATOMID_DONTINCLUDE ) -#else - #error Undefined or wrong definition of CT_ATOMID -#endif -#endif -#endif - - /* add atom to the CT */ -#if ( CT_ATOMID != CT_ATOMID_DONTINCLUDE ) - if ( CHECK_OVERFLOW(nCTLen, pCS->nMaxLenLinearCT) ) - return CT_OVERFLOW; /* */ - COMPARE_WITH_CT(LinearCT, nCTLen, r0_at_type, bCompare); -#endif - - /******************************************************* - add neighbors and (if required) bonds to CT - ********************************************************/ - - /* sort endpoints */ - nEndpointAtomNumber = t_group_info->nEndpointAtomNumber+(int)t_group[i].nFirstEndpointAtNoPos; - pn_RankForSort = nRank; - num_neigh = (int)t_group[i].nNumEndpoints; - insertions_sort( nEndpointAtomNumber, (size_t)num_neigh, sizeof(nEndpointAtomNumber[0]), CompRank); - - for ( k = 0; k < num_neigh; k ++) { - /* rank = (new current atom Rank) */ - if ( (int)(r_neigh = (AT_NUMB)nRank[(int)nEndpointAtomNumber[k]]) - CT_NEIGH_SMALLER_THAN rank ) { - if ( CHECK_OVERFLOW(nCTLen, pCS->nMaxLenLinearCT) ) - return CT_OVERFLOW; /* */ - COMPARE_WITH_CT( LinearCT, nCTLen, r_neigh, bCompare); - } - } - } /* end of cycle over all tautomeric groups. */ - - /* compare bonds types */ - /* compare elements */ - - if ( LinearCT ) { - - if ( pCS->nLenLinearCT ) { - if ( pCS->nLenLinearCT != nCTLen ) - return CT_LEN_MISMATCH; /* */ - } else { - pCS->nLenLinearCT = nCTLen; - } - - if ( pCS->nLenLinearCT ) { - if ( pCS->nLenLinearCTAtOnly != nCTLenAtOnly ) - return CT_LEN_MISMATCH; /* */ - } else { - pCS->nLenLinearCTAtOnly = nCTLenAtOnly; - } - - } - - /* Return: 0=> identical CT; -1=> new CT is smaller than the previous one */ - return (bCompare-1); -} - -/*********************************************************************************************/ -/* if (*bChanged & 1) then nSymmRank has been rearranged because for some r - min{i: r=nSymmRank[nAtomNumber[i]]}+1 != r - if (*bChanged & 2) then ranks nTempRank[] from nSymmRank[] differ from input nCurrRank[] - - on exit: - - nSymmRank[] have been updated if (*bChanged & 1) - nCurrRank[] have been updated if (*bChanged & 1) - nTempRank[] is always same as nCurrRank[] - nAtomNumber[] have been sorted so that - (i < j) <=> (nSymmRank[nAtomNumber[i]] <= nSymmRank[nAtomNumber[j]]) -*/ -int FixCanonEquivalenceInfo( int num_at_tg, AT_RANK *nSymmRank, AT_RANK *nCurrRank, - AT_RANK *nTempRank, AT_NUMB *nAtomNumber, int *bChanged) -{ - int nNumDiffRanks, bChangeSymmRank, bChangeCurrRank=0; - /* sort equivalence information */ - /* - int i; - for ( i = 0; i < num_at_tg; i ++ ) { - nAtomNumber[i] = i; - } - */ - pn_RankForSort = nSymmRank; /* minimal class representatives: min ranks for equiv. atoms */ - qsort( nAtomNumber, num_at_tg, sizeof(nAtomNumber[0]), CompRanksOrd ); - - /* convert equivalence information nSymmRank[] into ranks array nTempRank[] */ - /* eq. info contains min. possible ranks for eq. atoms; nCurrRank contains max. possible ranks */ - nNumDiffRanks = SortedEquInfoToRanks( nSymmRank/*inp*/, nTempRank/*out*/, nAtomNumber, num_at_tg, &bChangeSymmRank ); - /* check whether nCurrRank is same as new initial ranks calculated from nSymmRank[] */ - bChangeCurrRank = memcmp( nCurrRank, nTempRank, num_at_tg*sizeof(nTempRank[0])); - - /*----------------------------------------------------------------------- - if ( bChangeSymmRank || bChangeCurrRank ) { - This is the case when the initial equitable partitioning does not produce - constitutionally equivalent classes of atoms. - Rebuild nSymmRank[] according to the new nCurrRank[] := nTempRank[] - For such structures the found canonical numbers of the constitutionally equivalent atoms - are not contiguous (see nCanonRank and nSymmRank examples below). Here arrays - nCurrRank, nAtomNumber, and nSymmRank are changed so that later the - contiguous canonical numbers for equivalent atoms can be obtained - (see GetCanonRanking under - "III. Get final canonical numbering (no stereo, no isotopic)". - - Example: for CAS=37520-11-9 (ID=21247: Ethane, 1,2-dicyclopropyl-), - - the numbers are the "final canon. numbers, nCanonRank" - 1 - - HC 7 5 3 - | \ - | >CH--CH2 CH - | / \ / | - HC H2C--CH< | - \ | - 2 6 8 CH - - 4 - - the arrays (arranged according to ordering in nAtomNumberTemp) are: - before SortedEquInfoToRanks after SortedRanksToEquInfo - orig. atom nos.,nAtomNumberTemp: {4 5 6 7 0 1 2 3} {4 5 6 7 0 1 2 3} - order numbers for sorted ranks: {0 1 2 3 4 5 6 7} {0 1 2 3 4 5 6 7} - canonical numbering, nCanonRank: {1 2 5 6 3 4 7 8} {1 2 5 6 3 4 7 8} - constit. equivalence, nSymmRank: {1 1 1 1 3 3 7 7} {1 1 1 1 5 5 7 7} used later - initial equivalence, nCurrRank: {6 6 6 6 6 6 8 8} {4 4 4 4 6 6 8 8} used later - initial numbering, nAtomNumber: {2 3 4 7 0 1 6 7} {0 1 2 3 4 5 6 7} used later - final, no stereo, no isotopic, after III. GetCanonRanking: - final canon. numbers, nCanonRank: {1 2 3 4 5 6 7 8} final - } - ----------------------------------------------------------------------------------*/ - if ( bChangeCurrRank ) { - memcpy( nCurrRank, nTempRank, num_at_tg*sizeof(nCurrRank[0]) ); - } - if ( bChangeSymmRank ) { - SortedRanksToEquInfo( nSymmRank/*out*/, nTempRank/*inp*/, nAtomNumber, num_at_tg ); - } - if ( bChanged ) { - *bChanged = (0 != bChangeSymmRank) | 2*(0 != bChangeCurrRank); - } - return nNumDiffRanks; -} -/* isotopic canonicalization */ -/*********************************************************************** - * - * Canon_INChI (former GetCanonRankingUsingEquivInfo) - * - */ -int Canon_INChI3(int num_atoms, int num_at_tg, sp_ATOM* at, - CANON_STAT* pCS, INCHI_MODE nMode, int bTautFtcn) -{ -/**************************************************************** - -0. Initiation, Prepare initial ranks for GetCanonRanking() - -I. Find constitutionally equivalent atoms and possibly canonical numbering -I.1 Set tautomer=On, stereo=isotopic=Off -I.2 GetCanonRanking(): Find constitutionally equivalent atoms and possibly canonical numbering -1.3 Fix canonical equivalence info if needed (if the fix is needed then the numbering is not canonical) - -II. Get final non-isotopic canonical numbering. Simultaneously obtain non-minimal isotopic and stereo CTs - GetCanonRanking() with pCS->bKeepSymmRank = 1 - FillOutStereoParities() (create initial stereo descriptors) - save non-isotopic canonicalization final results - hide isotopic and tautomeric results (for historical reasons only) - - -III. Find constitutionally equivalent isotopic atoms (for isotopic stereo canonicalization) -III.1 Allocate more memory -III.2 fill allocated memory with the initial data -III.3 duplicate, save old and add isotopic info to the new pCS->t_group_info -III.4 Prepare initial isotopic ranks for GetCanonRanking() -III.5 GetCanonRanking() to Find constitutionally equivalent ISOTOPIC atoms and tautomer groups -III.6 Fix canonical isotopic equivalence information and derive ranks out of it - -IV. Prepare a second Rank/AtomNumber Stack for mapping. - -V. Optimize isotopic part (optimized) - map_isotopic_atoms2() - save isotopic canonical numbering - -VI. Optimize stereo descriptors (optimized) - map_stereo_bonds4() - - -VII. Optimize isotopic stereo descriptors (optimized) - SwitchAtomStereoAndIsotopicStereo() - SetCtToIsotopicStereo() - FillOutStereoParities() - SetUseAtomForStereo() - map_stereo_bonds4() - - SwitchAtomStereoAndIsotopicStereo() - SetCtToNonIsotopicStereo() - - - - -*****************************************************************/ - - int nRet = 0, i, n; - - - /******************************************************** - input non-stereo canonical info - ********************************************************/ - BCN *pBCN = pCS->pBCN; - FTCN *ftcn = pBCN->ftcn + bTautFtcn; - - /******************************************************** - set mode flags - ********************************************************/ - /* tautomeric structure */ - int bTaut = (num_at_tg > num_atoms) && pCS->t_group_info && pCS->t_group_info->num_t_groups && pCS->t_group_info->t_group; - /* special case: induced by exchangable isotopic H inequivalence of atoms in formally non-tautomeric structure */ - int bIsoXchgH = pCS->t_group_info && pCS->t_group_info->nNumIsotopicEndpoints > 1 && - pCS->t_group_info->nIsotopicEndpointAtomNumber && pCS->t_group_info->nIsotopicEndpointAtomNumber[0] && - (pCS->t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE|TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE)) - /* && (ftcn->nCanonFlags & CANON_FLAG_ISO_TAUT_DIFF)*/; - int bHasIsotopicCanonData = (ftcn->PartitionCtIso.AtNumber && ftcn->PartitionCtIso.Rank && ftcn->nSymmRankCtIso); - /* bHasIsotopicCanonData==0 means - * (1) No isotopic atoms in the component OR - * (2) the component has only exchangable isotopic H that do not change canonical numbering and equivalence. - */ - T_GROUP_INFO *t_group_info1 = bTaut? pCS->t_group_info : NULL; - /*int bIsoXchgH = t_group_info1 && t_group_info1->nNumIsotopicEndpoints && t_group_info1->nIsotopicEndpointAtomNumber;*/ - /* isotopic canonicalization */ - int bCanonIsotopic = bHasIsotopicCanonData && ( nMode & CMODE_ISO ) && ( pCS->LinearCTIsotopic || pCS->LinearCTIsotopicTautomer || bIsoXchgH ); - /* stereo canonicalization */ - int bCanonStereo = ( nMode & CMODE_STEREO ) && ( pCS->LinearCTStereoDble || pCS->LinearCTStereoCarb ); - /* stereo isotopic canonicalization */ - int bCanonIsoStereo = bHasIsotopicCanonData && ( nMode & CMODE_ISO_STEREO ) && (pCS->LinearCTIsotopicStereoDble || pCS->LinearCTIsotopicStereoCarb) && bCanonIsotopic; - int bIsoTaut = (bTaut && bCanonIsotopic); - - int bIgnoreIsotopicInputGroups; - int bIgnoreIsotopicInputAtoms; - - AT_RANK **pRankStack1 = pBCN->pRankStack; - int nRankStackLen = pBCN->nMaxLenRankStack; - int num_max = pBCN->num_max; /* allocation lengths in *pRankStack1[] */ - NEIGH_LIST *NeighList = ftcn->NeighList; - - int nNumCurrRanks = 0; - AT_RANK *nTempRank = NULL; - - AT_RANK *nSymmRank = NULL; - - AT_RANK *nAtomNumber = NULL; - AT_RANK *nRank = NULL; - - AT_RANK **pRankStack2 = NULL; - AT_RANK *nCanonRankStereo = NULL; - AT_RANK *nCanonRankStereoInv = NULL; - AT_RANK *nSymmStereo = NULL; - - AT_RANK *nCanonRankIsotopicStereo = NULL; - AT_RANK *nCanonRankIsotopicStereoInv = NULL; - - CUR_TREE *cur_tree = NULL; - CUR_TREE CurrentTree; - - - /*AT_ISO_TGROUP *LinearCTIsotopicTautomer = NULL; */ - - - CANON_STAT CS2; - CANON_STAT* pCS2 = &CS2; - - inchiTime ulStartTime, ulEndTime; - /*=========== Mode Bits (low 8 bits, bit 0 is Least Significant Bit) =========== - - Mode Bits Description - '0' c 0 Only one connection table canonicalization - '1' C 1 Recalculate CT using fixed nSymmRank - '2' i 1|2 Isotopic canonicalization (internal) - '3' I 1|2|4 Isotopic canonicalization (output) - '4' s 1|8 Stereo canonicalization - '5' S 1|2|4|16 Stereo isotopic canonicalization - '6' A 1|2|4|8|16 Output All - - --- high 8 bits ---- - --- obsolete, only historical interest. ------ - 1-2 : 0 => at[i].init_rank from Morgan+NeighList - 1 => at[i].init_rank from Atom Invariants - 2 => at[i].init_rank from nSymmRank[] - (at[i].init_rank is included in LinearCT - depending on CT_ATOMID definition) - 3 : 1 => Get Stereo canonical info - 4 : 1 => Get Isotopic canonical info - 5 : 1 => Get Charge/Radical canonical info - ==================================================================*/ - /*int nOutputMode = 0;*/ /* obsolete */ - - - int bSwitchedAtomToIsotopic = 0; - - - /* vABParityUnknown holds actual value of an internal constant signifying */ - /* unknown parity: either the same as for undefined parity (default==standard) */ - /* or a specific one (non-std; requested by SLUUD switch). */ - int vABParityUnknown = AB_PARITY_UNDF; - if ( 0 != ( nMode & REQ_MODE_DIFF_UU_STEREO) ) - { - /* Make labels for unknown and undefined stereo different */ - vABParityUnknown = AB_PARITY_UNKN; - } - - - InchiTimeGet( &ulStartTime ); - - - - - *pCS2 = *pCS; /* save input information and pointers to allocated memory */ - - /* set "ignore isotopic differences in tautomer groups" true */ - if ( bTaut ) { - /* save request for isotopic tautomeric groups */ - bIgnoreIsotopicInputGroups = pCS->t_group_info->bIgnoreIsotopic; - pCS->t_group_info->bIgnoreIsotopic = 1; - } else { - bIgnoreIsotopicInputGroups = 1; - } - /* save request for isotopic name */ - bIgnoreIsotopicInputAtoms = pCS->bIgnoreIsotopic; - /* set "ignore isotopic differences in atoms" true */ - pCS->bIgnoreIsotopic = 1; - - - /* save non-isotopic and isotopic canonicalization results */ - pCS->nCanonFlags = ftcn->nCanonFlags; - /* 1. non-isotopic */ - - /* linear CT, H */ - memcpy( pCS->LinearCT, ftcn->LinearCt, ftcn->nLenLinearCt * sizeof(pCS->LinearCT[0]) ); - if ( pCS->nNum_H && ftcn->nNumH ) { - for ( i = 0; i < num_atoms; i ++ ) { - pCS->nNum_H[i] = /*(S_CHAR)*/(CHAR_MASK & ftcn->nNumH[i]); - } - } - if ( pCS->nNum_H_fixed && ftcn->nNumHFixH ) { - for ( i = 0; i < num_atoms; i ++ ) { - pCS->nNum_H_fixed[i] = /*(S_CHAR)*/(CHAR_MASK & ftcn->nNumHFixH[i]); - } - } - pCS->nLenLinearCT = ftcn->nLenLinearCt; - pCS->nLenLinearCTAtOnly = ftcn->nLenLinearCtAtOnly; - - /* save non-isotopic atoms equivalence and numbering */ - if ( pCS->nSymmRank ) { - memcpy( pCS->nSymmRank, ftcn->nSymmRankCt, num_at_tg * sizeof(pCS->nSymmRank[0]) ); - } - if ( pCS->nCanonOrd ) { - memcpy( pCS->nCanonOrd, ftcn->PartitionCt.AtNumber, num_at_tg * sizeof(pCS->nCanonOrd[0]) ); - pCS->nLenCanonOrd = num_atoms; - } - if ( ftcn->iso_exchg_atnos && pCS->nExchgIsoH ) { - for ( i = 0; i < num_atoms; i ++ ) { - pCS->nExchgIsoH[i] = !ftcn->iso_exchg_atnos[i]; /* (pCS->nExchgIsoH[i]==1) => tautomeric or hetero atoms that may exchange isotopic H */ - } - } - /* 2. isotopic */ - - if ( bCanonIsotopic ) { - /* linear CT, num_H are same as non-isotopic */ - /* save atoms equivalence and numbering */ - if ( pCS->nSymmRankIsotopic ) { - memcpy( pCS->nSymmRankIsotopic, ftcn->nSymmRankCtIso, num_at_tg * sizeof(pCS->nSymmRankIsotopic[0])); - } - if ( pCS->nCanonOrdIsotopic ) { - memcpy( pCS->nCanonOrdIsotopic, ftcn->PartitionCtIso.AtNumber, num_at_tg * sizeof(pCS->nCanonOrdIsotopic[0]) ); - pCS->nLenCanonOrdIsotopic = num_at_tg; - } - nRet = FillIsotopicAtLinearCT( num_atoms, at, ftcn->PartitionCtIso.AtNumber, - pCS->LinearCTIsotopic, pCS->nMaxLenLinearCTIsotopic, &pCS->nLenLinearCTIsotopic ); - if ( RETURNED_ERROR(nRet) ) { - goto exit_function; - } - if ( nRet < 0 ) { - nRet = CT_TAUCOUNT_ERR; - goto exit_function; - } - } else { - pCS->nMaxLenLinearCTIsotopic = 0; - pCS->nMaxLenLinearCTIsotopicTautomer = 0; - } - - /* fill out tautomeric groups, isotopic and non-isotopic tautomeric CT and t_group_info1->tGroupNumber */ - if ( bTaut ) { - bIsoTaut = bIsoTaut && ftcn->PartitionCtIso.Rank && - ftcn->PartitionCtIso.AtNumber && ftcn->nSymmRankCtIso; - nRet = FillTautLinearCT2( num_atoms, num_at_tg, bIsoTaut, - ftcn->PartitionCt.Rank, ftcn->PartitionCt.AtNumber, ftcn->nSymmRankCt, - ftcn->PartitionCtIso.Rank, ftcn->PartitionCtIso.AtNumber, ftcn->nSymmRankCtIso, - pCS->LinearCTTautomer, pCS->nMaxLenLinearCTTautomer, &pCS->nLenLinearCTTautomer, - pCS->LinearCTIsotopicTautomer, pCS->nMaxLenLinearCTIsotopicTautomer, &pCS->nLenLinearCTIsotopicTautomer, - t_group_info1 ); - - if ( RETURNED_ERROR(nRet) ) { - goto exit_function; - } - if ( nRet <= 0 ) { - nRet = CT_TAUCOUNT_ERR; - goto exit_function; - } else { - /* tautomeric groups: save non-isotopic symmetry & t_group order */ - int num_t_groups = t_group_info1->num_t_groups; - AT_NUMB *tGroupNumber = t_group_info1->tGroupNumber; - AT_NUMB *tSymmRank = tGroupNumber + TGSO_SYMM_RANK*num_t_groups; - if ( pCS->nSymmRankTaut ) { - memcpy( pCS->nSymmRankTaut, tSymmRank, num_t_groups * sizeof(pCS->nSymmRank[0]) ); /* fixed 5-23-02 */ - } - if ( pCS->nCanonOrdTaut ) { - memcpy( pCS->nCanonOrdTaut, tGroupNumber, num_t_groups * sizeof(pCS->nCanonOrdTaut[0]) ); - pCS->nLenCanonOrdTaut = num_t_groups; - } - if ( bCanonIsotopic /*&& pCS->nLenLinearCTIsotopicTautomer*/ ) { - /* tautomeric groups: save isotopic symmetry & t_group order */ - /*AT_NUMB ntRankOffset = (AT_RANK)num_atoms;*/ - AT_NUMB *tiSymmRank = tGroupNumber + TGSO_SYMM_IRANK*num_t_groups; - AT_NUMB *tiGroupNumber = tGroupNumber + TGSO_SYMM_IORDER*num_t_groups; - if ( pCS->nSymmRankIsotopicTaut ) { - memcpy( pCS->nSymmRankIsotopicTaut, tiSymmRank, num_t_groups * sizeof(pCS->nSymmRankIsotopicTaut[0]) ); - } - memcpy( pCS->nCanonOrdIsotopicTaut, tiGroupNumber, num_t_groups * sizeof(pCS->nCanonOrdIsotopicTaut[0]) ); - pCS->nLenCanonOrdIsotopicTaut = num_t_groups; - } - } - } - /* save connection table if requested */ - if ( pCS->LinearCT2 ) { - memcpy( pCS->LinearCT2, pCS->LinearCT, sizeof(pCS->LinearCT2[0])*pCS->nLenLinearCT ); - pCS->nLenLinearCT2 = pCS->nLenLinearCT; - pCS->nLenLinearCTAtOnly2 = pCS->nLenLinearCTAtOnly; - } - - if ( num_atoms <= 1 ) { - bCanonStereo = 0; /* a sinle atom + possibly terminal hydrogen atoms */ - if ( num_atoms < 1 || !at[0].parity2 ) { - bCanonIsoStereo = 0; /* structure; for example Cl- or CH4 */ - } - } - - if ( !bCanonStereo && !(bCanonIsotopic && bCanonIsoStereo) ) { - goto exit_function; /* skip stereo canonicalization */ - } - - - - /********************************************************** - Mode - ***********************************************************/ - nMode = nMode & CANON_MODE_MASK; - - /* memory allocation */ - - nAtomNumber = (AT_RANK *)qmalloc(num_max*sizeof(*nAtomNumber)); - nRank = (AT_RANK *)qmalloc(num_max*sizeof(*nRank)); - nTempRank = (AT_RANK *)qmalloc(num_max*sizeof(*nTempRank)); - nSymmRank = (AT_RANK *)qmalloc(num_max*sizeof(*nSymmRank)); - /*********************************************** - 0.1 Initialization - ************************************************/ - - - if ( !NeighList || !nAtomNumber || !nTempRank || - !nRank || !pCS->LinearCT ) { - nRet = CT_OUT_OF_RAM; /* program error */ /* */ - goto exit_function; - } - - pCS->NeighList = NeighList; - - *pCS2 = *pCS; /* save input information and pointers to allocated memory */ - - if ( !(nMode & CMODE_NOEQ_STEREO) && (bCanonStereo || bCanonIsoStereo ) ) { - /* will be used to discover vertex equivalences in stereo canonicalization */ - memset( &CurrentTree, 0, sizeof(CurrentTree) ); - cur_tree = &CurrentTree; - } - - - pCS->bCmpStereo = 0; - pCS->bCmpIsotopicStereo = 0; - - - if ( bCanonStereo || bCanonIsoStereo ) { - int ii, nn; - - /* stereo or isotopic canonicalization: we need a second set of ranks for mapping */ - /* (isotopic atoms or stereo can only increase nNumCurrRanks) */ - pRankStack2 = (AT_RANK **) inchi_calloc( nRankStackLen, sizeof(AT_RANK *) ); - if ( pRankStack2 ) { - /* prepare for ranks reuse */ - for ( nn = 2; nn < nRankStackLen && pRankStack1[nn]; nn ++ ) { - pRankStack1[nn][0] = 0; /* means ranks have to be calculated */ - } - /* reuse memory to reduce number of allocations: */ - /* move last half of pointers from pRankStack1 to pRankStack2 */ - /* The first 2 elements will be assigned separately */ - if ( (nn = (nn-2)/2) > 0 ) { - for ( ii = 2+nn; ii < nRankStackLen && pRankStack1[ii]; ii ++ ) { - pRankStack2[ii-nn] = pRankStack1[ii]; - pRankStack1[ii] = NULL; - } - } - } else { - nRet = CT_OUT_OF_RAM; /* */ - goto exit_function; /* program error */ - } - } - - if ( bCanonStereo ) { - - /* *pCS2 = *pCS; */ /* save input information and pointers to allocated memory */ - - /* initial ranking for non-isotopic mapping */ - memcpy( nAtomNumber, ftcn->PartitionCt.AtNumber, num_at_tg * sizeof(nAtomNumber[0]) ); - memcpy( nRank, ftcn->PartitionCt.Rank, num_at_tg * sizeof(nRank[0]) ); - memcpy( nSymmRank, ftcn->nSymmRankCt, num_at_tg * sizeof(nSymmRank[0]) ); - - /* nSymmRank changes if canonical numbers of constitutionally equivalent atoms are not contiguous */ - nNumCurrRanks = FixCanonEquivalenceInfo( num_at_tg, nSymmRank /* in&out*/, - nRank, nTempRank /* out */, nAtomNumber /* in&out */, NULL); - /* atom numbers in canonical order */ - memcpy( pCS->nPrevAtomNumber, ftcn->PartitionCt.AtNumber, num_at_tg * sizeof(nAtomNumber[0]) ); - - /* fill stereo part of the connection table with initial (not optimized) parities */ - /* input - pCS->LinearCTStereoDble - pCS->LinearCTStereoCarb - pCS->nMaxLenLinearCTStereoCarb - pCS->nMaxLenLinearCTStereoDble - */ - nRet = FillOutStereoParities( at, num_atoms, ftcn->PartitionCt.Rank, ftcn->PartitionCt.AtNumber, - nRank, nAtomNumber, pCS, 0 /* bIsotopic */ ); - /* output - pCS->LinearCTStereoDble - pCS->LinearCTStereoCarb - pCS2->nLenLinearCTStereoCarb - pCS2->nLenLinearCTStereoDble - */ - if ( RETURNED_ERROR( nRet ) ) { - goto exit_function; - } - if ( nRet < 0 ) { - nRet = CT_STEREOCOUNT_ERR; - goto exit_function; - } - - /*************************************************************** - * - * VI. Optimize non-isotopic stereo descriptors (optimized) - * - ***************************************************************/ - - /* allocate memory for stereo canonicalization */ - - if ( !nCanonRankStereo ) - nCanonRankStereo = (AT_RANK *) qmalloc(num_max*sizeof(*nCanonRankStereo)); - if ( !nSymmStereo && !(nMode & CMODE_NOEQ_STEREO) ) - nSymmStereo = (AT_RANK *) qmalloc((num_max+1)*sizeof(*nSymmStereo)); - if ( !(nMode & CMODE_NOEQ_STEREO) && 0 > CurTreeAlloc( cur_tree, num_at_tg ) ) { - nRet = CT_OUT_OF_RAM; /* */ - goto exit_function; - } - /* check allocations and assign first 2 elements of pRankStack2 */ - if ( pRankStack1 && pRankStack2 && - nCanonRankStereo && - /* nCurrRankStereo && nAtomNumberCurrStereo &&*/ - (nSymmStereo || (nMode & CMODE_NOEQ_STEREO)) ) { - pRankStack1[0] = pRankStack2[0] = nRank; - pRankStack1[1] = pRankStack2[1] = nAtomNumber; - } else { - nRet = CT_OUT_OF_RAM; /* */ - goto exit_function; - } - - /**************************************************************** - * - * VI-A. Optimize non-isotopic non-inverted stereo descriptors - * - ****************************************************************/ - - /* set the 1st ranks in the rest of the stack to zero: prepare for ranks reuse */ - for ( n = 2; n < nRankStackLen && pRankStack1[n]; n ++ ) { - pRankStack1[n][0] = 0; /* means ranks have to be recalculated */ - } - /* set the 1st ranks to zero: prepare for ranks reuse */ - for ( n = 2; n < nRankStackLen && pRankStack2[n]; n ++ ) { - pRankStack2[n][0] = 0; /* means ranks have to be recalculated */ - } - - /* for debugging or statistics */ - pCS->lNumBreakTies = - pCS->lNumNeighListIter= - pCS->lNumTotCT = - pCS->lNumDecreasedCT = - pCS->lNumRejectedCT = - pCS->lNumEqualCT = 0; - pCS->bKeepSymmRank = 0; - pCS->bFirstCT = 1; /* To fill out nCanonRankStereo[] in map_stero_atoms2() */ - - /****************************************************************************** - nCanonRank contains input canonical numbering - nCanonRankStereo will be filled with a transposition of canonical numbering - which (1) keeps connection table unchanged and - (2) provides minimal stereo descriptors in - pCS->LinearCTStereoDble (length=pCS->nLenLinearCTStereoDble) - pCS->LinearCTStereoCarb (length=pCS->nLenLinearCTStereoCarb) - */ - nRet = map_stereo_bonds4 - ( at, num_atoms, num_at_tg, num_max, 0, ftcn->PartitionCt.Rank, ftcn->PartitionCt.AtNumber, - nCanonRankStereo, nSymmRank, - pRankStack1, pRankStack2, nTempRank, nNumCurrRanks, - nSymmStereo, NeighList, pCS, cur_tree, 0 /* nNumMappedBonds */, - vABParityUnknown); - - if ( RETURNED_ERROR( nRet ) ) { - if ( nRet == CT_TIMEOUT_ERR ) - goto exit_function; - else - goto exit_function; /* program error */ - } else { - int bFailed = 0; - if ( !nRet ) { - bFailed = 1; /* progrm error */ - pCS2->nLenLinearCTStereoCarb = - pCS->nLenLinearCTStereoCarb = -abs(pCS->nLenLinearCTStereoCarb); - pCS2->nLenLinearCTStereoDble = - pCS->nLenLinearCTStereoDble = -abs(pCS->nLenLinearCTStereoDble); - nRet = CT_STEREOCOUNT_ERR; /* */ - goto exit_function; /* program error */ - } else { - /* save non-isotopic lengths */ - pCS2->nLenLinearCTStereoDble = pCS->nLenLinearCTStereoDble; - pCS2->nLenLinearCTStereoCarb = pCS->nLenLinearCTStereoCarb; - nRet = 0; - } - - /* save stereo canonical numbering */ - if ( pCS->nCanonOrdStereo ) { - for ( i = n = 0; i < num_at_tg; i ++ ) { - if ( nCanonRankStereo[i] && (int)nCanonRankStereo[i] <= num_at_tg ) { - pCS->nCanonOrdStereo[ (int)nCanonRankStereo[i] - 1 ] = (AT_NUMB)i; - } else { - bFailed ++; - } - } - pCS->nLenCanonOrdStereo = ( bFailed )? -num_atoms : num_atoms; - } - /* save stereo tautomer groups numbering */ - if ( bTaut && pCS->nCanonOrdStereoTaut ) { - if ( 0 < (nRet = SortTautomerGroupsAndEndpoints( t_group_info1, num_atoms, num_at_tg, nCanonRankStereo ) ) ) { - /*non-isotopic contains symmetry ranks */ - int num_t_groups = t_group_info1->num_t_groups; - AT_NUMB *tGroupNumber = t_group_info1->tGroupNumber; - /*AT_NUMB *tiSymmRank = tGroupNumber + TGSO_SYMM_IRANK*num_t_groups; */ - memcpy( pCS->nCanonOrdStereoTaut, tGroupNumber, num_t_groups*sizeof(pCS->nCanonOrdStereoTaut[0]) ); - pCS->nLenCanonOrdStereoTaut = ( bFailed ) ? - -num_t_groups : num_t_groups; - } else - if ( RETURNED_ERROR( nRet ) ) { - goto exit_function; - } else { - nRet = 0; - } - /*SortTautomerGroupsAndEndpoints( t_group_info1, nCanonRank ); */ /* ??? return to non-isotopic canonical numbering */ - } - } - - /**************************************************** - * - * VI-B. Optimize INVERTED stereo descriptors - * - ****************************************************/ - if ( !nCanonRankStereoInv ) - nCanonRankStereoInv = (AT_RANK *) qmalloc(num_max*sizeof(*nCanonRankStereoInv)); - if ( !nCanonRankStereoInv ) { - nRet = CT_OUT_OF_RAM; /* */ - goto exit_function; - } - /* copy previous non-isotopic stereo canonicalization results to Inv initial data */ - /* assign pointers */ - pCS->LinearCTStereoDble = pCS2->LinearCTStereoDbleInv; - pCS->LinearCTStereoCarb = pCS2->LinearCTStereoCarbInv; - - /* copy the lengths */ - pCS2->nLenLinearCTStereoDbleInv = - pCS->nLenLinearCTStereoDbleInv = - pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTStereoDble; - - pCS2->nLenLinearCTStereoCarbInv = - pCS->nLenLinearCTStereoCarbInv = - pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTStereoCarb; - - if ( pCS->nLenLinearCTStereoDble > 0 || pCS->nLenLinearCTStereoCarb > 0 ) { - /* copy previous results, the canonical stereo CT */ - memcpy( pCS->LinearCTStereoDble, pCS2->LinearCTStereoDble, pCS->nLenLinearCTStereoDble*sizeof(pCS->LinearCTStereoDble[0]) ); - memcpy( pCS->LinearCTStereoCarb, pCS2->LinearCTStereoCarb, pCS->nLenLinearCTStereoCarb*sizeof(pCS->LinearCTStereoCarb[0]) ); - } - memcpy( nCanonRankStereoInv, nCanonRankStereo, num_max * sizeof(nCanonRankStereoInv[0]) ); - if ( pCS->nCanonOrdStereoInv && pCS->nCanonOrdStereo ) { - /* in case there is nothing to invert */ - memcpy( pCS->nCanonOrdStereoInv, pCS->nCanonOrdStereo, num_at_tg*sizeof(pCS->nCanonOrdStereoInv[0])); - } - - /****************************** - * - * Invert stereo - * - ******************************/ - - /********************************************************************************* - * Create initial approximation for the minimization of the stereo descriptors: - * invert stereogenic atom parities, one parity in each allene, all parities in - * pCS->LinearCTStereoCarb and allene parities in pCS->nLenLinearCTStereoDble - */ - nRet = InvertStereo( at, num_at_tg, nCanonRankStereo, nTempRank, pCS, 1 /* bInvertLinearCTStereo */ ); - if ( RETURNED_ERROR( nRet ) ) { - goto exit_function; - } else - if ( nRet > 0 ) { - /* InvertStereo() has done some changes */ - nRet = 0; - /* FillOutStereoParities() has already been called to fill out these 2 LinearCTs */ - - /* set the 1st ranks in the rest of the stack to zero: prepare for ranks reuse */ - for ( n = 2; n < nRankStackLen && pRankStack1[n]; n ++ ) { - pRankStack1[n][0] = 0; /* means ranks have to be recalculated */ - } - /* set the 1st ranks to zero: prepare for ranks reuse */ - for ( n = 2; n < nRankStackLen && pRankStack2[n]; n ++ ) { - pRankStack2[n][0] = 0; /* means ranks have to be recalculated */ - } - - /* for debugging or statistics */ - pCS->lNumBreakTies = - pCS->lNumNeighListIter= - pCS->lNumTotCT = - pCS->lNumDecreasedCT = - pCS->lNumRejectedCT = - pCS->lNumEqualCT = 0; - pCS->bKeepSymmRank = 0; - pCS->bFirstCT = 1; /* To fill out nCanonRankStereo[] in map_stero_atoms2() */ - - /****************************************************************************** - ftcn->PartitionCt.Rank contains input canonical numbering - nCanonRankStereoInv will be filled with a transposition of canonical numbering - which (1) keeps connection table unchanged and - (2) provides minimal stereo descriptors in - pCS->LinearCTStereoDble (length=pCS->nLenLinearCTStereoDble) - pCS->LinearCTStereoCarb (length=pCS->nLenLinearCTStereoCarb) - ******************************************************************************/ - nRet = map_stereo_bonds4 - ( at, num_atoms, num_at_tg, num_max, 0, ftcn->PartitionCt.Rank, ftcn->PartitionCt.AtNumber, - nCanonRankStereoInv, nSymmRank, - pRankStack1, pRankStack2, nTempRank, nNumCurrRanks, nSymmStereo, - NeighList, pCS, cur_tree, 0, - vABParityUnknown); - if ( RETURNED_ERROR( nRet ) ) { - if ( nRet == CT_TIMEOUT_ERR ) - goto exit_function; - else - goto exit_function; /* program error */ - } else { - int bFailed = 0; - if ( !nRet ) { - bFailed = 1; /* progrm error */ - pCS2->nLenLinearCTStereoCarb = - pCS->nLenLinearCTStereoCarb = -abs(pCS->nLenLinearCTStereoCarb); - pCS2->nLenLinearCTStereoDble = - pCS->nLenLinearCTStereoDble = -abs(pCS->nLenLinearCTStereoDble); - nRet = CT_STEREOCOUNT_ERR; /* */ - goto exit_function; /* program error */ - } - - /* save non-isotopic pointers & lengths for INVERTED stereo */ - pCS->nLenLinearCTStereoDbleInv = - pCS2->nLenLinearCTStereoDbleInv = pCS->nLenLinearCTStereoDble; - pCS->nLenLinearCTStereoCarbInv = - pCS2->nLenLinearCTStereoCarbInv = pCS->nLenLinearCTStereoCarb; - - /* restore pointers and lengths to non-inverted stereo */ - /* -- this is needed for InvertStereo() back, see below */ - pCS->LinearCTStereoDble = pCS2->LinearCTStereoDble; - pCS->LinearCTStereoCarb = pCS2->LinearCTStereoCarb; - pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTStereoDble; - pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTStereoCarb; - /* consistency check */ - if ( pCS->nLenLinearCTStereoDbleInv != pCS->nLenLinearCTStereoDble || - pCS->nLenLinearCTStereoCarbInv != pCS->nLenLinearCTStereoCarb ) { - nRet = CT_CALC_STEREO_ERR; - goto exit_function; /* program error */ - } - /****************************** - * - * Invert stereo back - * - ****************************** - * (make sure that pointers - * pCS->LinearCTStereoCarb, - * pCS->LinearCTStereoDble - * and corresponding lengths - * have been restored) - ******************************/ - /********************************************************************************* - * invert only stereogenic atom parities and one parity in each allene, DO NOT - * change parities in pCS->LinearCTStereoCarb and pCS->nLenLinearCTStereoDble - */ - nRet = InvertStereo( at, num_at_tg, nCanonRankStereo, nTempRank, pCS, 0 ); - if ( RETURNED_ERROR( nRet ) ) { - goto exit_function; - } - nRet = 0; - - - /* save stereo canonical numbering */ - if ( pCS->nCanonOrdStereoInv ) { - for ( i = n = 0; i < num_at_tg; i ++ ) { - if ( nCanonRankStereoInv[i] && (int)nCanonRankStereoInv[i] <= num_at_tg ) { - pCS->nCanonOrdStereoInv[ (int)nCanonRankStereoInv[i] - 1 ] = (AT_NUMB)i; - } else { - bFailed ++; - } - } - pCS->nLenCanonOrdStereo = ( bFailed )? -num_atoms : num_atoms; - } - - /* compare inverted and non-inverted stereo */ - pCS->bCmpStereo = 2 + CompareLinCtStereo( - pCS->LinearCTStereoDbleInv, pCS->nLenLinearCTStereoDbleInv, - pCS->LinearCTStereoCarbInv, pCS->nLenLinearCTStereoCarbInv, - pCS->LinearCTStereoDble, pCS->nLenLinearCTStereoDble, - pCS->LinearCTStereoCarb, pCS->nLenLinearCTStereoCarb - ); - - } - } else - if ( 0 == nRet ) { - /* nothing has been done, restore pointers and lengths for stereo */ - pCS->LinearCTStereoDble = pCS2->LinearCTStereoDble; - pCS->LinearCTStereoCarb = pCS2->LinearCTStereoCarb; - pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTStereoDble; - pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTStereoCarb; - } - - - } - /* restore "ignore isotopic differences in tautomer groups" */ - if ( bTaut ) { - /* save request for isotopic tautomeric groups */ - pCS->t_group_info->bIgnoreIsotopic = bIgnoreIsotopicInputGroups; - } - /* restore request for isotopic name */ - pCS->bIgnoreIsotopic = bIgnoreIsotopicInputAtoms; - - if ( bCanonIsoStereo && bCanonIsotopic ) { - - /**************************************************************** - * - * VII. Optimize isotopic stereo descriptors (optimized) - * - ****************************************************************/ - /* - pCS->LinearCTIsotopic = NULL; - */ - - /* initial ranking for isotopic mapping */ - memcpy( nAtomNumber, ftcn->PartitionCtIso.AtNumber, num_at_tg * sizeof(nAtomNumber[0]) ); - memcpy( nRank, ftcn->PartitionCtIso.Rank, num_at_tg * sizeof(nRank[0]) ); - memcpy( nSymmRank, ftcn->nSymmRankCtIso, num_at_tg * sizeof(nSymmRank[0]) ); - - /* nSymmRank will change if canonical numbers of of constitutionally equivalent atoms are not contiguous */ - nNumCurrRanks = FixCanonEquivalenceInfo( num_at_tg, nSymmRank /* in&out*/, - nRank, nTempRank /* out */, nAtomNumber /* in&out */, NULL); - - memcpy( pCS->nPrevAtomNumber, ftcn->PartitionCtIso.AtNumber, num_at_tg * sizeof(nAtomNumber[0]) ); - - /* allocate memory for optimized stereo canonicalization */ - /* for stereo canonical numbering to be found. */ - if ( !nCanonRankIsotopicStereo ) - nCanonRankIsotopicStereo = (AT_RANK *) qmalloc(num_max*sizeof(*nCanonRankIsotopicStereo)); - if ( !nSymmStereo && !(nMode & CMODE_NOEQ_STEREO) ) - nSymmStereo = (AT_RANK *) qmalloc((num_max+1)*sizeof(*nSymmStereo)); - - if ( !(nMode & CMODE_NOEQ_STEREO) && CurTreeAlloc( cur_tree, num_at_tg ) ) { - nRet = CT_OUT_OF_RAM; /* */ - goto exit_function; - } - /* check allocations and assign first 2 elements of pRankStack2 */ - if ( pRankStack1 && pRankStack2 && - nCanonRankIsotopicStereo && - (nSymmStereo || (nMode & CMODE_NOEQ_STEREO)) ) { - - pRankStack1[0] = pRankStack2[0] = nRank; /* pRankStack1[0,1] shall be unchanged */ - pRankStack1[1] = pRankStack2[1] = nAtomNumber; - } else { - nRet = CT_OUT_OF_RAM; /* */ - goto exit_function; - } - - /****************************************************************** - Important: fill out a list of stereo atoms and bonds including - those which are stereo due to isotopic atoms only and create - LinearCT stereo descriptors for the canonical numbering - ******************************************************************/ - - - /* at[] has certain members for non-isotopic and isotopic stereo; switch them */ - SwitchAtomStereoAndIsotopicStereo( at, num_atoms, &bSwitchedAtomToIsotopic ); - /* prepare stereo connection tables' pointers */ - SetCtToIsotopicStereo( pCS, pCS2 ); - - nRet = FillOutStereoParities( at, num_atoms, ftcn->PartitionCtIso.Rank, ftcn->PartitionCtIso.AtNumber, - nRank, nAtomNumber, pCS, 1 /* bIsotopic */); - if (RETURNED_ERROR(nRet)) { - goto exit_function; /* program error */ - } else - if ( !nRet ) { - /* no isotopic stereo */ - pCS2->nLenLinearCTIsotopicStereoDble = pCS->nLenLinearCTIsotopicStereoDble = 0; - pCS2->nLenLinearCTIsotopicStereoCarb = pCS->nLenLinearCTIsotopicStereoCarb = 0; - pCS->nLenCanonOrdIsotopicStereo = 0; - pCS->nLenCanonOrdIsotopicStereoTaut = 0; - pCS2->nLenLinearCTIsotopicStereoDbleInv = pCS->nLenLinearCTIsotopicStereoDbleInv = 0; - pCS2->nLenLinearCTIsotopicStereoCarbInv = pCS->nLenLinearCTIsotopicStereoCarbInv = 0; - goto bypass_isotopic_stereo; - } else { - nRet = 0; /* not an error */ - } - - - - /************************************************************* - * - * VII-A. Optimize non-inverted isotopic stereo descriptors - * - *************************************************************/ - - /* set the 1st ranks in the rest of the stack to zero: prepare for ranks reuse */ - for ( n = 2; n < nRankStackLen && pRankStack1[n]; n ++ ) { - pRankStack1[n][0] = 0; /* means ranks have to be recalculated */ - } - /* set the 1st ranks to zero: prepare for ranks reuse */ - for ( n = 2; n < nRankStackLen && pRankStack2[n]; n ++ ) { - pRankStack2[n][0] = 0; /* means ranks have to be recalculated */ - } - - /* for debugging or statistics */ - pCS->lNumBreakTies = - pCS->lNumNeighListIter= - pCS->lNumTotCT = - pCS->lNumDecreasedCT = - pCS->lNumRejectedCT = - pCS->lNumEqualCT = 0; - pCS->bKeepSymmRank = 0; - pCS->bFirstCT = 1; /* To fill out nCanonRankStereo[] in map_stero_atoms2() */ - - /************************************************************************************** - nCanonRankIsotopic contains input canonical numbering - nCanonRankIsotopicStereo will be filled with a transposition of canonical numbering - that (1) keeps connection table unchanged and - (2) provides minimal stereo descriptors in - pCS->LinearCTStereoDble (length=pCS->nLenLinearCTStereoDble) - pCS->LinearCTStereoCarb (length=pCS->nLenLinearCTStereoCarb) - ***************************************************************************************/ - nRet = map_stereo_bonds4 - ( at, num_atoms, num_at_tg, num_max, 0, ftcn->PartitionCtIso.Rank, - ftcn->PartitionCtIso.AtNumber, - nCanonRankIsotopicStereo, nSymmRank, - pRankStack1, pRankStack2, nTempRank, nNumCurrRanks, - nSymmStereo, NeighList, pCS, cur_tree, 0, - vABParityUnknown); - if ( RETURNED_ERROR( nRet ) ) { - goto exit_function; - } else { - int bFailed = 0; - - if ( !nRet ) { - bFailed = 1; /* program error */ - pCS2->nLenLinearCTIsotopicStereoDble = - pCS->nLenLinearCTIsotopicStereoDble = -abs(pCS->nLenLinearCTStereoDble); - pCS2->nLenLinearCTIsotopicStereoCarb = - pCS->nLenLinearCTIsotopicStereoCarb = -abs(pCS->nLenLinearCTStereoCarb); - nRet = CT_STEREOCOUNT_ERR; /* */ - goto exit_function; /* program error */ - } else { - /* save isotopic lengths */ - pCS->nLenLinearCTIsotopicStereoDble = - pCS2->nLenLinearCTIsotopicStereoDble = pCS->nLenLinearCTStereoDble; - pCS->nLenLinearCTIsotopicStereoCarb = - pCS2->nLenLinearCTIsotopicStereoCarb = pCS->nLenLinearCTStereoCarb; - - /* save stereo canonical numbering */ - if ( pCS->nCanonOrdIsotopicStereo ) { - for ( i = n = 0; i < num_at_tg; i ++ ) { - if ( nCanonRankIsotopicStereo[i] && (int)nCanonRankIsotopicStereo[i] <= num_at_tg ) { - pCS->nCanonOrdIsotopicStereo[ (int)nCanonRankIsotopicStereo[i] - 1 ] = (AT_NUMB)i; - } else { - bFailed ++; - } - } - pCS->nLenCanonOrdIsotopicStereo = bFailed? -num_atoms : num_atoms; - } - /* save stereo tautomer groups numbering */ - if ( pCS->nCanonOrdIsotopicStereoTaut ) { - if ( 0 < (nRet=SortTautomerGroupsAndEndpoints( t_group_info1, num_atoms, num_at_tg, nCanonRankIsotopicStereo ) ) ) { - /*non-isotopic contains symmetry ranks */ - int num_t_groups = t_group_info1->num_t_groups; - AT_NUMB *tGroupNumber = t_group_info1->tGroupNumber; - /*AT_NUMB *tiSymmRank = tGroupNumber + TGSO_SYMM_IRANK*num_t_groups; */ - memcpy( pCS->nCanonOrdIsotopicStereoTaut, tGroupNumber, num_t_groups*sizeof(pCS->nCanonOrdIsotopicStereoTaut[0]) ); - pCS->nLenCanonOrdIsotopicStereoTaut = bFailed? -num_t_groups:num_t_groups; - - /*SortTautomerGroupsAndEndpoints( t_group_info1, nCanonRank ); */ /* ??? return to non-isotopic canonical numbering */ - } else - if ( RETURNED_ERROR( nRet ) ) { - goto exit_function; - } else { - nRet = 0; - } - } - } - } - - /********************************************************** - * - * VII-B. Optimize INVERTED isotopic stereo descriptors - * - **********************************************************/ - if ( !nCanonRankIsotopicStereoInv ) - nCanonRankIsotopicStereoInv = (AT_RANK *) qmalloc(num_max*sizeof(*nCanonRankIsotopicStereoInv)); - if ( !nCanonRankIsotopicStereoInv ) { - nRet = CT_OUT_OF_RAM; /* */ - goto exit_function; - } - /* copy previous isotopic stereo canonicalization results to Inv initial data */ - /* assign pointers */ - pCS->LinearCTStereoDble = pCS2->LinearCTIsotopicStereoDbleInv; /* enable stereo */ - pCS->LinearCTStereoCarb = pCS2->LinearCTIsotopicStereoCarbInv; - - - /* copy the lengths */ - pCS2->nLenLinearCTIsotopicStereoDbleInv = - pCS->nLenLinearCTStereoDbleInv = - pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTIsotopicStereoDble; - - pCS2->nLenLinearCTIsotopicStereoCarbInv = - pCS->nLenLinearCTStereoCarbInv = - pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTIsotopicStereoCarb; - - if ( pCS->nLenLinearCTStereoDble > 0 || pCS->nLenLinearCTStereoCarb > 0 ) { - /* copy previous results, the canonical stereo CT */ - memcpy( pCS->LinearCTStereoDble, pCS2->LinearCTIsotopicStereoDble, pCS->nLenLinearCTStereoDble*sizeof(pCS->LinearCTStereoDble[0]) ); - memcpy( pCS->LinearCTStereoCarb, pCS2->LinearCTIsotopicStereoCarb, pCS->nLenLinearCTStereoCarb*sizeof(pCS->LinearCTStereoCarb[0]) ); - } - memcpy( nCanonRankIsotopicStereoInv, nCanonRankIsotopicStereo, num_max * sizeof(nCanonRankIsotopicStereoInv[0]) ); - if ( pCS->nCanonOrdIsotopicStereoInv && pCS->nCanonOrdIsotopicStereo ) { - /* in case there is nothing to invert */ - memcpy( pCS->nCanonOrdIsotopicStereoInv, pCS->nCanonOrdIsotopicStereo, num_at_tg*sizeof(pCS->nCanonOrdIsotopicStereoInv[0])); - } - - /****************************** - * - * Invert isotopic stereo - * - ******************************/ - - /********************************************************************************* - * Create initial approximation for the minimization of the stereo descriptors: - * invert stereogenic atom parities, one parity in each allene, all parities in - * pCS->LinearCTStereoCarb and allene parities in pCS->nLenLinearCTStereoDble - */ - nRet = InvertStereo( at, num_at_tg, nCanonRankIsotopicStereo, nTempRank, pCS, 1 ); - if ( RETURNED_ERROR( nRet ) ) { - goto exit_function; - } else - if ( nRet > 0 ) { - /* InvertStereo() has done some changes */ - nRet = 0; - /* FillOutStereoParities() has already been called to fill out these 2 LinearCTs */ - - /* set the 1st ranks in the rest of the stack to zero: prepare for ranks reuse */ - for ( n = 2; n < nRankStackLen && pRankStack1[n]; n ++ ) { - pRankStack1[n][0] = 0; /* means ranks have to be recalculated */ - } - /* set the 1st ranks to zero: prepare for ranks reuse */ - for ( n = 2; n < nRankStackLen && pRankStack2[n]; n ++ ) { - pRankStack2[n][0] = 0; /* means ranks have to be recalculated */ - } - - /* for debugging or statistics */ - pCS->lNumBreakTies = - pCS->lNumNeighListIter= - pCS->lNumTotCT = - pCS->lNumDecreasedCT = - pCS->lNumRejectedCT = - pCS->lNumEqualCT = 0; - pCS->bKeepSymmRank = 0; - pCS->bFirstCT = 1; /* To fill out nCanonRankStereo[] in map_stero_atoms2() */ - - /************************************************************************************** - nCanonRankIsotopic contains input canonical numbering - nCanonRankIsotopicStereo will be filled with a transposition of canonical numbering - that (1) keeps connection table unchanged and - (2) provides minimal stereo descriptors in - pCS->LinearCTStereoDble (length=pCS->nLenLinearCTStereoDble) - pCS->LinearCTStereoCarb (length=pCS->nLenLinearCTStereoCarb) - */ - nRet = map_stereo_bonds4 - ( at, num_atoms, num_at_tg, num_max, 0, ftcn->PartitionCtIso.Rank, ftcn->PartitionCtIso.AtNumber, - nCanonRankIsotopicStereoInv, nSymmRank, - pRankStack1, pRankStack2, nTempRank, nNumCurrRanks, - nSymmStereo, NeighList, pCS, cur_tree, 0, - vABParityUnknown); - if ( RETURNED_ERROR( nRet ) ) { - if ( nRet == CT_TIMEOUT_ERR ) - goto exit_function; - else - goto exit_function; /* program error */ - } else { - int bFailed = 0; - - if ( !nRet ) { - bFailed = 1; /* program error */ - pCS2->nLenLinearCTIsotopicStereoDble = - pCS->nLenLinearCTIsotopicStereoDble = -abs(pCS->nLenLinearCTStereoDble); - pCS2->nLenLinearCTIsotopicStereoCarb = - pCS->nLenLinearCTIsotopicStereoCarb = -abs(pCS->nLenLinearCTStereoCarb); - nRet = CT_STEREOCOUNT_ERR; /* */ - goto exit_function; /* program error */ - } - /* save isotopic pointers & lengths for INVERTED stereo */ - - /* save isotopic lengths */ - pCS->nLenLinearCTIsotopicStereoDbleInv = - pCS2->nLenLinearCTIsotopicStereoDbleInv = pCS->nLenLinearCTStereoDble; - pCS->nLenLinearCTIsotopicStereoCarbInv = - pCS2->nLenLinearCTIsotopicStereoCarbInv = pCS->nLenLinearCTStereoCarb; - - /* restore pointers and lengths to non-inverted isotopic stereo */ - /* -- this is needed for InvertStereo() back, see below */ - pCS->LinearCTStereoDble = pCS2->LinearCTIsotopicStereoDble; - pCS->LinearCTStereoCarb = pCS2->LinearCTIsotopicStereoCarb; - pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTIsotopicStereoDble; - pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTIsotopicStereoCarb; - - /* consistency check */ - if ( pCS->nLenLinearCTIsotopicStereoDbleInv != pCS->nLenLinearCTIsotopicStereoDble || - pCS->nLenLinearCTIsotopicStereoCarbInv != pCS->nLenLinearCTIsotopicStereoCarb ) { - nRet = CT_CALC_STEREO_ERR; - goto exit_function; /* program error */ - } - /****************************** - * - * Invert stereo back - * - ****************************** - * (make sure that pointers - * pCS->LinearCTStereoCarb, - * pCS->LinearCTStereoDble - * and corresponding lengths - * have been restored) - ******************************/ - nRet = InvertStereo( at, num_at_tg, nCanonRankIsotopicStereo, nTempRank, pCS, 0 ); - if ( RETURNED_ERROR( nRet ) ) { - goto exit_function; - } - nRet = 0; - - /* save stereo canonical numbering */ - if ( pCS->nCanonOrdIsotopicStereoInv ) { - for ( i = n = 0; i < num_at_tg; i ++ ) { - if ( nCanonRankIsotopicStereoInv[i] && (int)nCanonRankIsotopicStereoInv[i] <= num_at_tg ) { - pCS->nCanonOrdIsotopicStereoInv[ (int)nCanonRankIsotopicStereoInv[i] - 1 ] = (AT_NUMB)i; - } else { - bFailed ++; - } - } - pCS->nLenCanonOrdIsotopicStereo = bFailed? -num_atoms : num_atoms; - } - /* compare inverted and non-inverted isotopic stereo */ - pCS->bCmpIsotopicStereo = 2 + CompareLinCtStereo( - pCS->LinearCTIsotopicStereoDbleInv, pCS->nLenLinearCTIsotopicStereoDbleInv, - pCS->LinearCTIsotopicStereoCarbInv, pCS->nLenLinearCTIsotopicStereoCarbInv, - pCS->LinearCTIsotopicStereoDble, pCS->nLenLinearCTIsotopicStereoDble, - pCS->LinearCTIsotopicStereoCarb, pCS->nLenLinearCTIsotopicStereoCarb - ); - - } - } else - if ( 0 == nRet ) { - /* nothing has been done, restore pointers and lengths for stereo */ - pCS->LinearCTStereoDble = pCS2->LinearCTIsotopicStereoDble; - pCS->LinearCTStereoCarb = pCS2->LinearCTIsotopicStereoCarb; - pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTIsotopicStereoDble; - pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTIsotopicStereoCarb; - } - -bypass_isotopic_stereo:; /* ??? */ - - pCS->LinearCTIsotopic = pCS2->LinearCTIsotopic; - } - - - -exit_function: - - if ( bSwitchedAtomToIsotopic ) { - SwitchAtomStereoAndIsotopicStereo( at, num_atoms, &bSwitchedAtomToIsotopic ); - SetCtToNonIsotopicStereo( pCS, pCS2 ); /* ??? */ - } - - /* restore non-isotopic connection table */ - if ( pCS->LinearCT2 ) { - inchi_swap( (char*)&pCS->LinearCT, (char*)&pCS->LinearCT2, sizeof(pCS->LinearCT) ); - inchi_swap( (char*)&pCS->nLenLinearCT, (char*)&pCS->nLenLinearCT2, sizeof(pCS->nLenLinearCT) ); - inchi_swap( (char*)&pCS->nLenLinearCTAtOnly, (char*)&pCS->nLenLinearCTAtOnly2, sizeof(pCS->nLenLinearCTAtOnly) ); - } - - /* free memory */ - i = 2; - if ( pRankStack1 ) { - pRankStack1[0] = - pRankStack1[1] = NULL; /* deallocated separately */ - for ( ; i < nRankStackLen && pRankStack1[i]; i ++ ) - ; - } - if ( pRankStack1 && pRankStack2 ) { - for ( n = 2; n < nRankStackLen && pRankStack2[n]; n ++ ) { - if ( i < nRankStackLen - 1 ) { - pRankStack1[i++] = pRankStack2[n]; - } else { - inchi_free( pRankStack2[n] ); - } - } - inchi_free( pRankStack2 ); - } - - pCS->NeighList = NULL; /* keep the pointer in pBCN->ftcn[bTaut].NeighList for further deallocation */ - qfree ( nAtomNumber ); - qfree ( nTempRank ); - qfree ( nRank ); - qfree ( nSymmRank ); - - qfree( nSymmStereo ); - CurTreeFree( cur_tree ); -/* memory leak fix */ -/* - qfree ( nCurrRankIsotopicStereo ); - qfree ( nAtomNumberCurrIsotopicStereo); -*/ - qfree ( nCanonRankIsotopicStereo ); - qfree ( nCanonRankIsotopicStereoInv ); - - qfree( nCanonRankStereo ); - qfree( nCanonRankStereoInv ); - - InchiTimeGet( &ulEndTime ); - pCS->lTotalTime = InchiTimeMsecDiff(&ulEndTime, &ulStartTime); - return (nRet >= -1)? num_atoms : nRet; /* cannot easily get number of ranks for now */ - -} - -/**************************************************************************************/ -int Canon_INChI(int num_atoms, int num_at_tg, sp_ATOM* at, - CANON_STAT* pCS, INCHI_MODE nMode, int bTautFtcn) -{ - if ( pCS->pBCN && !pCS->NeighList ) { - /* new version */ - return Canon_INChI3( num_atoms, num_at_tg, at, pCS, nMode, bTautFtcn); - } - return CT_CANON_ERR; -} +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include +#include +#include +#include +#include + +#include "mode.h" +#include "ichicano.h" + +#include "ichitime.h" +#include "ichi.h" +#include "ichicomn.h" + + + +/**************************************************************************** + * + * Globals for sorting + */ + +#define tsort insertions_sort + +/* local prototypes */ + +void FillOutAtomInvariant( sp_ATOM* at, + int num_atoms, + int num_at_tg, + ATOM_INVARIANT* pAtomInvariant, + CANON_STAT* pCS ); + +int Canon_INChI1( int num_atoms, + int num_at_tg, + sp_ATOM* at, + CANON_STAT* pCS, + INCHI_MODE nMode); + +int Canon_INChI2( int num_atoms, + int num_at_tg, + sp_ATOM* at, + CANON_STAT* pCS, + INCHI_MODE nMode); + +int Canon_INChI3( INCHI_CLOCK *ic, + int num_atoms, + int num_at_tg, + sp_ATOM* at, + CANON_STAT* pCS, + CANON_GLOBALS *pCG, + INCHI_MODE nMode, + int bTautFtcn); + + +#ifdef COMPILE_ANSI_ONLY + +static clock_t InchiClock(void); + +#ifdef INCHI_USETIMES +static clock_t InchiClock(void) +{ + struct tms buf; + clock_t c = times( &buf ); + if ( c != (clock_t)-1 ) + { + return buf.tms_utime; + } + return 0; +} +#else +static clock_t InchiClock(void) +{ + clock_t c = clock(); + if ( c != (clock_t)-1 ) + { + return c; + } + return 0; +} +#endif + +#define INCHI_MSEC(X) (long)((1000.0/(double)CLOCKS_PER_SEC)*(X)) +#define INCHI_CLOCK_T(X) (clock_t)( (double)(X) / 1000.0 * (double)CLOCKS_PER_SEC ) +const clock_t FullMaxClock = (clock_t)(-1); +const clock_t HalfMaxClock = (clock_t)(-1) / 2; + +static void FillMaxMinClock(INCHI_CLOCK *ic); /* keep compiler happy */ +static void FillMaxMinClock(INCHI_CLOCK *ic) +{ + if ( !ic->m_MaxPositiveClock ) { + clock_t valPos=0, val1 = 1; + while ( 0 < ((val1 <<= 1), (val1 |= 1)) ) { + valPos = val1; + } + ic->m_MaxPositiveClock = valPos; + ic->m_MinNegativeClock = -valPos; + ic->m_HalfMaxPositiveClock = ic->m_MaxPositiveClock / 2; + ic->m_HalfMinNegativeClock = ic->m_MinNegativeClock / 2; + } +} + + +/******** get current process time ****************************************/ +void InchiTimeGet( inchiTime *TickEnd ) +{ + TickEnd->clockTime = InchiClock(); +} +/******** returns difference TickEnd - TickStart in milliseconds **********/ +long InchiTimeMsecDiff(INCHI_CLOCK *ic, inchiTime *TickEnd, inchiTime *TickStart ) +{ + if ( FullMaxClock > 0 ) + { + clock_t delta; + if ( !TickEnd || !TickStart ) + return 0; + /* clock_t is unsigned */ + if ( TickEnd->clockTime > TickStart->clockTime ) + { + if ( TickEnd->clockTime > HalfMaxClock && + TickEnd->clockTime - TickStart->clockTime > HalfMaxClock ) + { + /* overflow in TickStart->clockTime, actually TickStart->clockTime was later */ + delta = (FullMaxClock - TickEnd->clockTime) + TickStart->clockTime; + return -INCHI_MSEC(delta); + } + delta = TickEnd->clockTime - TickStart->clockTime; + return INCHI_MSEC(delta); + } + else if ( TickEnd->clockTime < TickStart->clockTime ) + { + if ( TickStart->clockTime > HalfMaxClock && + TickStart->clockTime - TickEnd->clockTime > HalfMaxClock ) + { + /* overflow in TickEnd->clockTime, actually TickEnd->clockTime was later */ + delta = (FullMaxClock - TickStart->clockTime) + TickEnd->clockTime; + return INCHI_MSEC(delta); + } + delta = TickStart->clockTime - TickEnd->clockTime; + return -INCHI_MSEC(delta); + } + return 0; /* TickEnd->clockTime == TickStart->clockTime */ + } + else + { + /* may happen under Win32 only where clock_t is SIGNED long */ + clock_t delta; + FillMaxMinClock(ic); + if ( !TickEnd || !TickStart ) + return 0; + if ( TickEnd->clockTime >= 0 && TickStart->clockTime >= 0 || + TickEnd->clockTime <= 0 && TickStart->clockTime <= 0) { + delta = TickEnd->clockTime - TickStart->clockTime; + } else + if ( TickEnd->clockTime >= ic->m_HalfMaxPositiveClock && + TickStart->clockTime <= ic->m_HalfMinNegativeClock ) { + /* end is earlier than start */ + delta = (ic->m_MaxPositiveClock - TickEnd->clockTime) + (TickStart->clockTime - ic->m_MinNegativeClock); + delta = -delta; + } else + if ( TickEnd->clockTime <= ic->m_HalfMinNegativeClock && + TickStart->clockTime >= ic->m_HalfMaxPositiveClock ) { + /* start was earlier than end */ + delta = (ic->m_MaxPositiveClock - TickStart->clockTime) + (TickEnd->clockTime - ic->m_MinNegativeClock); + } else { + /* there was no overflow, clock passed zero */ + delta = TickEnd->clockTime - TickStart->clockTime; + } + return INCHI_MSEC(delta); + } +} + + +/******************* get elapsed time from TickStart ************************/ +long InchiTimeElapsed(INCHI_CLOCK *ic, inchiTime *TickStart ) +{ + inchiTime TickEnd; + if ( !TickStart ) + return 0; + InchiTimeGet( &TickEnd ); + return InchiTimeMsecDiff(ic, &TickEnd, TickStart ); +} + + +/******************* add number of milliseconds to time *********************/ +void InchiTimeAddMsec(INCHI_CLOCK *ic, inchiTime *TickEnd, unsigned long nNumMsec ) +{ + clock_t delta; + if ( !TickEnd ) + return; + if ( FullMaxClock > 0 ) + { + /* clock_t is unsigned */ + delta = INCHI_CLOCK_T(nNumMsec); + TickEnd->clockTime += delta; + } + else + { + /* may happen under Win32 only where clock_t is SIGNED long */ + /* clock_t is unsigned */ + FillMaxMinClock(ic); + delta = INCHI_CLOCK_T(nNumMsec); + TickEnd->clockTime += delta; + } +} + + +/******************* check whether time has expired *********************/ +int bInchiTimeIsOver(INCHI_CLOCK *ic, inchiTime *TickStart ) +{ + if ( FullMaxClock > 0 ) + { + clock_t clockCurrTime; + if ( !TickStart ) + return 0; + clockCurrTime = InchiClock(); + /* clock_t is unsigned */ + if ( TickStart->clockTime > clockCurrTime ) + { + if ( TickStart->clockTime > HalfMaxClock && + TickStart->clockTime - clockCurrTime > HalfMaxClock ) + { + /* overflow in clockCurrTime, actually clockCurrTime was later */ + return 1; + } + return 0; + } + else if ( TickStart->clockTime < clockCurrTime ) + { + if ( clockCurrTime > HalfMaxClock && + clockCurrTime - TickStart->clockTime > HalfMaxClock ) + { + /* overflow in TickStart->clockTime, actually TickStart->clockTime was later */ + return 0; + } + return 1; + } + return 0; /* TickStart->clockTime == clockCurrTime */ + } + else + { + /* may happen under Win32 only where clock_t is SIGNED long */ + clock_t clockCurrTime; + FillMaxMinClock(ic); + if ( !TickStart ) + return 0; + clockCurrTime = InchiClock(); + if ( clockCurrTime >= 0 && TickStart->clockTime >= 0 || + clockCurrTime <= 0 && TickStart->clockTime <= 0) { + return (clockCurrTime > TickStart->clockTime); + } else + if ( clockCurrTime >= ic->m_HalfMaxPositiveClock && + TickStart->clockTime <= ic->m_HalfMinNegativeClock ) { + /* curr is earlier than start */ + return 0; + } else + if ( clockCurrTime <= ic->m_HalfMinNegativeClock && + TickStart->clockTime >= ic->m_HalfMaxPositiveClock ) { + /* start was earlier than curr */ + return 1; + } else { + /* there was no overflow, clock passed zero */ + return (clockCurrTime > TickStart->clockTime); + } + } +} + + + +#else + +/******** get current process time ****************************************/ +void InchiTimeGet( inchiTime *TickEnd ) +{ + if ( TickEnd ) + { + struct _timeb timeb; + _ftime( &timeb ); + TickEnd->clockTime = (unsigned long)timeb.time; + TickEnd->millitime = (long)timeb.millitm; + } +} + + +/******** returns difference TickEnd - TickStart in milliseconds **********/ +long InchiTimeMsecDiff(INCHI_CLOCK *ic, inchiTime *TickEnd, inchiTime *TickStart ) +{ + long delta; + if ( !TickEnd || !TickStart ) + { + return 0; + } + if ( TickEnd->clockTime >= TickStart->clockTime ) + { + delta = (long)(TickEnd->clockTime - TickStart->clockTime); + delta *= 1000; + delta += TickEnd->millitime - TickStart->millitime; + } + else + { + delta = -(long)(TickStart->clockTime - TickEnd->clockTime); + delta *= 1000; + delta += TickEnd->millitime - TickStart->millitime; + } + return delta; +} + + +/******************* get elapsed time from TickStart ************************/ +long InchiTimeElapsed(INCHI_CLOCK *ic, inchiTime *TickStart ) +{ + inchiTime TickEnd; + if ( !TickStart ) + return 0; + InchiTimeGet( &TickEnd ); + return InchiTimeMsecDiff(ic, &TickEnd, TickStart ); +} + + +/******************* add number of milliseconds to time *********************/ +void InchiTimeAddMsec(INCHI_CLOCK *ic, inchiTime *TickEnd, unsigned long nNumMsec ) +{ + long delta; + if ( !TickEnd ) + return; + TickEnd->clockTime += nNumMsec / 1000; + delta = nNumMsec % 1000 + TickEnd->millitime; + TickEnd->clockTime += delta / 1000; + TickEnd->millitime = delta % 1000; +} + + +/******************* check whether time has expired *********************/ +int bInchiTimeIsOver(INCHI_CLOCK *ic, inchiTime *TickEnd ) +{ + struct _timeb timeb; + if ( !TickEnd ) + return 0; + _ftime( &timeb ); + if ( TickEnd->clockTime > (unsigned long)timeb.time ) + return 0; + if ( TickEnd->clockTime < (unsigned long)timeb.time || + TickEnd->millitime < (long)timeb.millitm ) + { + return 1; + } + return 0; +} + +#endif + + + +/****************************************************************************/ +/* length of canonic representation in sizeof(AT_NUMB) units */ +int GetCanonLengths( int num_at, + sp_ATOM* at, + ATOM_SIZES *s, + T_GROUP_INFO *t_group_info ) +{ + /* include taut. groups as additional "atoms" to the connection table 07-22-2002 */ + + int i, nNumCT, nNumBonds, nNumTBonds=0, nNumDblBondsStereo=0, nNumAsymCarbStereo=0, nNumIsotopic=0; + T_GROUP *t_group = (s->nLenLinearCTTautomer && t_group_info)? t_group_info->t_group : NULL; + + for (nNumBonds = 0, i = 0; i < num_at; i ++ ) + { + nNumBonds += at[i].valence; + if ( at[i].iso_sort_key ) + { + nNumIsotopic ++; /* not including tautomeric endpoints that are isotopic only due to mobile atoms */ + } + + if ( at[i].parity > 0 ) + { + /* ignore hydrogen isotope parities in at[i].parity2 */ + int j = 0, nStereoBondsToAtom=0; /* number of stereo double bonds at this atom */ + int k; + for ( ; j < MAX_NUM_STEREO_BONDS && (k=at[i].stereo_bond_neighbor[j]); j ++ ) + { + nStereoBondsToAtom += (at[k-1].parity > 0); + } + nNumDblBondsStereo += nStereoBondsToAtom; + nNumAsymCarbStereo += !j; + } + } + + nNumDblBondsStereo /= 2; + nNumBonds /= 2; + + s->nLenBonds = inchi_max( s->nLenBonds, nNumBonds ); + nNumCT = nNumBonds; /* total number of neighbors in the CT */ + +#if ( CT_ATOMID != CT_ATOMID_DONTINCLUDE ) + nNumCT += num_at; +#endif + + s->nLenCTAtOnly = inchi_max(s->nLenCTAtOnly, nNumCT); + + if ( t_group ) + { + for ( i = 0; i < t_group_info->num_t_groups; i ++ ) + { + nNumTBonds += t_group[i].nNumEndpoints; + } + nNumCT += nNumTBonds; + +#if ( CT_ATOMID != CT_ATOMID_DONTINCLUDE ) + nNumCT += t_group_info->num_t_groups; +#endif + } + + nNumCT = inchi_max( 1, nNumCT ); /* keep GetBaseCanonRanking() happy */ + s->nLenCT = inchi_max(s->nLenCT, nNumCT); + s->nLenIsotopic = inchi_max(s->nLenIsotopic, nNumIsotopic); + s->nLenLinearCTStereoDble = inchi_max(s->nLenLinearCTStereoDble, nNumDblBondsStereo); + s->nLenLinearCTStereoCarb = inchi_max(s->nLenLinearCTStereoCarb, nNumAsymCarbStereo); + + if ( t_group_info ) + s->nLenIsotopicEndpoints = inchi_max(s->nLenIsotopicEndpoints, t_group_info->nNumIsotopicEndpoints); + + return 0; +} + + +/****************************************************************************/ +int DeAllocateCS( CANON_STAT *pCS ) +{ +#define LOCAL_FREE( X) do{if(X){inchi_free( X); X=NULL;}}while(0) + + /* connection table */ + LOCAL_FREE( pCS->LinearCT ); + LOCAL_FREE( pCS->nCanonOrd ); + LOCAL_FREE( pCS->nSymmRank ); + LOCAL_FREE( pCS->nNum_H ); + LOCAL_FREE( pCS->nNum_H_fixed ); + LOCAL_FREE( pCS->nExchgIsoH ); + /* isotopic */ + LOCAL_FREE( pCS->LinearCTIsotopic ); + LOCAL_FREE( pCS->nSymmRankIsotopic ); + LOCAL_FREE( pCS->nCanonOrdIsotopic ); + /* isotopic tautomeric */ + LOCAL_FREE( pCS->LinearCTIsotopicTautomer ); + LOCAL_FREE( pCS->nCanonOrdIsotopicTaut ); + LOCAL_FREE( pCS->nSymmRankIsotopicTaut ); + /* stereo */ + LOCAL_FREE( pCS->LinearCTStereoDble ); + LOCAL_FREE( pCS->LinearCTStereoCarb ); + LOCAL_FREE( pCS->LinearCTStereoDbleInv ); + LOCAL_FREE( pCS->LinearCTStereoCarbInv ); + LOCAL_FREE( pCS->nCanonOrdStereo ); + LOCAL_FREE( pCS->nCanonOrdStereoInv ); + LOCAL_FREE( pCS->nCanonOrdStereoTaut ); + /* isotopic stereo */ + LOCAL_FREE( pCS->LinearCTIsotopicStereoDble ); + LOCAL_FREE( pCS->LinearCTIsotopicStereoCarb ); + LOCAL_FREE( pCS->LinearCTIsotopicStereoDbleInv ); + LOCAL_FREE( pCS->LinearCTIsotopicStereoCarbInv ); + LOCAL_FREE( pCS->bRankUsedForStereo ); + LOCAL_FREE( pCS->bAtomUsedForStereo ); + + LOCAL_FREE( pCS->nCanonOrdIsotopicStereo ); + LOCAL_FREE( pCS->nCanonOrdIsotopicStereoInv ); + LOCAL_FREE( pCS->nCanonOrdIsotopicStereoTaut ); + /* tautomeric part of the connection table */ + LOCAL_FREE( pCS->LinearCTTautomer ); + LOCAL_FREE( pCS->nCanonOrdTaut ); + LOCAL_FREE( pCS->nSymmRankTaut ); + + LOCAL_FREE( pCS->LinearCT2 ); + + /* for establishing constitutional equivalence */ + LOCAL_FREE( pCS->nPrevAtomNumber ); + + FreeNeighList( pCS->NeighList ); + pCS->NeighList = NULL; + + /* set zero lengths */ + pCS->nMaxLenLinearCTStereoDble = 0; + pCS->nLenLinearCTStereoDble = 0; + pCS->nMaxLenLinearCTStereoCarb = 0; + pCS->nLenLinearCTStereoCarb = 0; + pCS->nMaxLenLinearCTIsotopicStereoDble = 0; + pCS->nLenLinearCTIsotopicStereoDble = 0; + pCS->nMaxLenLinearCTIsotopicStereoCarb = 0; + pCS->nLenLinearCTIsotopicStereoCarb = 0; + pCS->nMaxLenLinearCTTautomer = 0; + pCS->nLenLinearCTTautomer = 0; + pCS->nMaxLenLinearCTIsotopic = 0; + pCS->nLenLinearCTIsotopic = 0; + pCS->nMaxLenLinearCTIsotopicTautomer = 0; + pCS->nLenLinearCTIsotopicTautomer = 0; + + /* set canon numbering lengths to zero */ + pCS->nLenCanonOrd = 0; + pCS->nLenCanonOrdIsotopic = 0; + pCS->nLenCanonOrdIsotopicTaut = 0; + pCS->nLenCanonOrdStereo = 0; + pCS->nLenCanonOrdStereoTaut = 0; + pCS->nLenCanonOrdIsotopicStereo = 0; + pCS->nLenCanonOrdIsotopicStereoTaut = 0; + pCS->nLenCanonOrdTaut = 0; + + return 0; + +#undef LOCAL_FREE +} + + +/****************************************************************************/ +int AllocateCS( CANON_STAT *pCS, int num_at, int num_at_tg, int nLenCT, int nLenCTAtOnly, + int nLenLinearCTStereoDble, int nLenLinearCTIsotopicStereoDble, + int nLenLinearCTStereoCarb, int nLenLinearCTIsotopicStereoCarb, + int nLenLinearCTTautomer, int nLenLinearCTIsotopicTautomer, + int nLenIsotopic, INCHI_MODE nMode, BCN *pBCN ) +{ +#define pCS_CALLOC( PTR,TYPE,LEN) (pCS->PTR=(TYPE*)inchi_calloc( (size_t)(LEN),sizeof(*pCS->PTR))) + + int num_err = 0; + int num_t_groups = num_at_tg - num_at; + + pCS->nMode = nMode; + + /* connection table */ + if ( (nMode & CMODE_CT) && nLenCT > 0 ) { + num_err += !pCS_CALLOC( LinearCT, AT_NUMB, nLenCT); + pCS->nMaxLenLinearCT = + pCS->nLenLinearCT = nLenCT; + pCS->nLenLinearCTAtOnly = nLenCTAtOnly; + num_err += !pCS_CALLOC( nCanonOrd, AT_RANK, num_at_tg); + num_err += !pCS_CALLOC( nSymmRank, AT_RANK, num_at_tg); + if ( pBCN ) { + num_err += !pCS_CALLOC( nNum_H, S_CHAR, num_at); + num_err += !pCS_CALLOC( nNum_H_fixed, S_CHAR, num_at); + num_err += !pCS_CALLOC( nExchgIsoH, S_CHAR, num_at); + } + } + + /* isotopic */ + if ( (nMode & CMODE_ISO) && nLenIsotopic > 0 ) + { + num_err += !pCS_CALLOC( LinearCTIsotopic, AT_ISOTOPIC, nLenIsotopic); + pCS->nMaxLenLinearCTIsotopic = + pCS->nLenLinearCTIsotopic = nLenIsotopic; + } + + /* isotopic tautomeric */ + if ( (nMode & CMODE_ISO) && CANON_MODE_TAUT == (nMode & CANON_MODE_TAUT) ) { + if ( nLenLinearCTIsotopicTautomer > 0 ) { + num_err += !pCS_CALLOC( LinearCTIsotopicTautomer, AT_ISO_TGROUP, nLenLinearCTIsotopicTautomer); + pCS->nMaxLenLinearCTIsotopicTautomer = + pCS->nLenLinearCTIsotopicTautomer = nLenLinearCTIsotopicTautomer; + } + if ( num_t_groups > 0 ) { + num_err += !pCS_CALLOC( nCanonOrdIsotopicTaut, AT_RANK, num_t_groups); + num_err += !pCS_CALLOC( nSymmRankIsotopicTaut, AT_RANK, num_t_groups); + } + } + /* isotopic atoms & t-groups */ + if ( (nMode & CMODE_ISO) /*&& nLenIsotopic > 0*/ || + (nMode & CMODE_ISO) && CANON_MODE_TAUT == (nMode & CANON_MODE_TAUT) && nLenLinearCTIsotopicTautomer > 0 + ) { + num_err += !pCS_CALLOC( nSymmRankIsotopic, AT_RANK, num_at_tg); + num_err += !pCS_CALLOC( nCanonOrdIsotopic, AT_RANK, num_at_tg); + } + /* stereo */ + if ( (nMode & CMODE_STEREO) && nLenLinearCTStereoDble > 0 ) { + num_err += !pCS_CALLOC( LinearCTStereoDble, AT_STEREO_DBLE, nLenLinearCTStereoDble); + num_err += !pCS_CALLOC( LinearCTStereoDbleInv, AT_STEREO_DBLE, nLenLinearCTStereoDble); + pCS->nLenLinearCTStereoDbleInv = + pCS->nMaxLenLinearCTStereoDble = + pCS->nLenLinearCTStereoDble = nLenLinearCTStereoDble; + } + if ( (nMode & CMODE_STEREO) && nLenLinearCTStereoCarb > 0 ) { + num_err += !pCS_CALLOC( LinearCTStereoCarb, AT_STEREO_CARB, nLenLinearCTStereoCarb); + num_err += !pCS_CALLOC( LinearCTStereoCarbInv, AT_STEREO_CARB, nLenLinearCTStereoCarb); + pCS->nLenLinearCTStereoCarbInv = + pCS->nMaxLenLinearCTStereoCarb = + pCS->nLenLinearCTStereoCarb = nLenLinearCTStereoCarb; + } + if ( (nMode & CMODE_STEREO) && (nLenLinearCTStereoDble > 0 || nLenLinearCTStereoCarb > 0 ) ) { + num_err += !pCS_CALLOC( nCanonOrdStereo, AT_RANK, num_at_tg); + num_err += !pCS_CALLOC( nCanonOrdStereoInv, AT_RANK, num_at_tg); + if ( (nMode & CMODE_TAUT) && nLenLinearCTTautomer > 0 && num_t_groups > 0 ) { + num_err += !pCS_CALLOC( nCanonOrdStereoTaut, AT_RANK, num_t_groups); + } + } + /* isotopic stereo */ + if ( (nMode & CMODE_ISO_STEREO) && nLenLinearCTIsotopicStereoDble > 0 ) { + num_err += !pCS_CALLOC( LinearCTIsotopicStereoDble, AT_STEREO_DBLE, nLenLinearCTIsotopicStereoDble); + num_err += !pCS_CALLOC( LinearCTIsotopicStereoDbleInv, AT_STEREO_DBLE, nLenLinearCTIsotopicStereoDble); + pCS->nLenLinearCTIsotopicStereoDbleInv = + pCS->nMaxLenLinearCTIsotopicStereoDble = + pCS->nLenLinearCTIsotopicStereoDble = nLenLinearCTIsotopicStereoDble; + } + if ( (nMode & CMODE_ISO_STEREO) && nLenLinearCTIsotopicStereoCarb > 0 ) { + num_err += !pCS_CALLOC( LinearCTIsotopicStereoCarb, AT_STEREO_CARB, nLenLinearCTIsotopicStereoCarb); + num_err += !pCS_CALLOC( LinearCTIsotopicStereoCarbInv, AT_STEREO_CARB, nLenLinearCTIsotopicStereoCarb); + pCS->nLenLinearCTIsotopicStereoCarbInv = + pCS->nMaxLenLinearCTIsotopicStereoCarb = + pCS->nLenLinearCTIsotopicStereoCarb = nLenLinearCTIsotopicStereoCarb; + } + if ( (nMode & CMODE_ISO_STEREO) && (nLenLinearCTIsotopicStereoDble > 0 || nLenLinearCTIsotopicStereoCarb > 0 ) ) { + num_err += !pCS_CALLOC( nCanonOrdIsotopicStereo, AT_RANK, num_at_tg); + num_err += !pCS_CALLOC( nCanonOrdIsotopicStereoInv, AT_RANK, num_at_tg); + if ( (nMode & CMODE_TAUT) && nLenLinearCTTautomer > 0 && num_t_groups > 0 ) { + num_err += !pCS_CALLOC( nCanonOrdIsotopicStereoTaut, AT_RANK, num_t_groups); + } + } + if ( (nMode & CMODE_STEREO) && (nLenLinearCTStereoDble > 0 || nLenLinearCTStereoCarb > 0 ) || + (nMode & CMODE_ISO_STEREO) && (nLenLinearCTIsotopicStereoDble > 0 || nLenLinearCTIsotopicStereoCarb > 0 ) ) { + num_err += !pCS_CALLOC( bRankUsedForStereo, S_CHAR, num_at); + num_err += !pCS_CALLOC( bAtomUsedForStereo, S_CHAR, num_at); + } + /* tautomeric part of the connection table */ + if ( (nMode & CMODE_CT) && (nMode & CMODE_TAUT) && nLenLinearCTTautomer > 0 ) { + num_err += !pCS_CALLOC( LinearCTTautomer, AT_TAUTOMER, nLenLinearCTTautomer); + pCS->nMaxLenLinearCTTautomer = + pCS->nLenLinearCTTautomer = nLenLinearCTTautomer; + if ( num_t_groups > 0 ) { + num_err += !pCS_CALLOC( nCanonOrdTaut, AT_RANK, num_t_groups); + num_err += !pCS_CALLOC( nSymmRankTaut, AT_RANK, num_t_groups); + } + } + + if ( nMode & CMODE_CT ) + num_err += !pCS_CALLOC( LinearCT2, AT_NUMB, nLenCT); + + /* for establishing constitutional equivalence */ + num_err += !pCS_CALLOC( nPrevAtomNumber, AT_RANK, num_at_tg); + + /* set canon numbering lengths to zero */ + pCS->nLenCanonOrd = 0; + pCS->nLenCanonOrdIsotopic = 0; + pCS->nLenCanonOrdIsotopicTaut = 0; + pCS->nLenCanonOrdStereo = 0; + pCS->nLenCanonOrdStereoTaut = 0; + pCS->nLenCanonOrdIsotopicStereo = 0; + pCS->nLenCanonOrdIsotopicStereoTaut = 0; + pCS->nLenCanonOrdTaut = 0; + + + if ( num_err ) + { + DeAllocateCS( pCS ); + return CT_OUT_OF_RAM; /* */ + } + return 0; + +#undef pCS_CALLOC +} + + +/****************************************************************************/ +#define COMPARE_WITH_CT(CT, CTLEN, VALUE, CONDITION) \ + if ( CONDITION ) { \ + if ( (VALUE) CT_GREATER_THAN (CT)[CTLEN] ) \ + return 1; /* not a minimal CT */ \ + (CONDITION) = (VALUE) == (CT)[CTLEN]; \ + } \ + (CT)[CTLEN] = VALUE; \ + (CTLEN)++ + +#define COMPARE_WITH_CTVAL(CTVAL, VALUE, CONDITION) \ + if ( CONDITION ) { \ + if ( (VALUE) CT_GREATER_THAN (CTVAL) ) \ + return 1; /* not a minimal CT */ \ + (CONDITION) = (VALUE) == (CTVAL); \ + } \ + (CTVAL) = VALUE + +#define COMPARE_WITH_CT2(CT, CTLEN, VALUE, CONDITION, OPER) \ + if ( CONDITION ) { \ + if ( (VALUE) CT_GREATER_THAN (CT)[CTLEN] ) { \ + (OPER); \ + return 1; /* not a minimal CT */ \ + } \ + (CONDITION) = (VALUE) == (CT)[CTLEN]; \ + } \ + (CT)[CTLEN] = VALUE; \ + (CTLEN)++ + + + +/****************************************************************************/ +int FillIsotopicAtLinearCT(int num_atoms, sp_ATOM* at, + const AT_RANK *nAtomNumber, + AT_ISOTOPIC *LinearCTIsotopic, + int nMaxLenLinearCTIsotopic, + int *pnLenLinearCTIsotopic) +{ + /* at[i].init_rank = initial ranks before canonizing */ + /* nRank[i] = new ordering number for atoms: nRank=1,2,.. */ + /* nAtomNumber[r] = orig. atom number= 0,1,... for r = nRank-1 */ + /* nRank[nAtomNumber[r]] = r; r = 0,1,... */ + /* nAtomNumber[nRank[i]-1] = i; */ + + int i, k, rank; + int nLinearCTIsotopicLen=0; + + /* the following parts of the "name" should be compared */ + /* after the connection table comparison is done */ + /* to avoid wrong difference sign. So, these parts */ + /* go to a separate buffers. */ + if ( LinearCTIsotopic && nMaxLenLinearCTIsotopic > 0 ) + { + memset( LinearCTIsotopic, 0, nMaxLenLinearCTIsotopic * sizeof(LinearCTIsotopic[0]) ); + } + else + { + return 0; + } + + /* rank = nRank[nAtomNumber[rank-1]] -- proposed atoms canon. numbers */ + for ( rank = 1; rank <= num_atoms; rank ++ ) + { + + i = (int)nAtomNumber[rank-1]; /* current atom */ + + /**************************************************** + add isotopic atom info to LinearCTIsotopic + *****************************************************/ + + /* if the atom itself is not isotopic then add it only if */ + /* the atom is not an endpoint AND has attached T or D or 1H. */ + k = ( !at[i].endpoint && !(at[i].cFlags & AT_FLAG_ISO_H_POINT) && (at[i].num_iso_H[0] || at[i].num_iso_H[1] || at[i].num_iso_H[2]) ); + if ( at[i].iso_atw_diff || k ) + { + if ( CHECK_OVERFLOW(nLinearCTIsotopicLen, nMaxLenLinearCTIsotopic) ) + return CT_OVERFLOW; /* */ + LinearCTIsotopic[nLinearCTIsotopicLen].at_num = (AT_RANK)rank; + LinearCTIsotopic[nLinearCTIsotopicLen].iso_atw_diff = at[i].iso_atw_diff; + LinearCTIsotopic[nLinearCTIsotopicLen].num_1H = (NUM_H)(k? at[i].num_iso_H[0] : 0); + LinearCTIsotopic[nLinearCTIsotopicLen].num_D = (NUM_H)(k? at[i].num_iso_H[1] : 0); + LinearCTIsotopic[nLinearCTIsotopicLen].num_T = (NUM_H)(k? at[i].num_iso_H[2] : 0); + nLinearCTIsotopicLen ++; + } + } /* end of cycle over all atoms. */ + + if ( LinearCTIsotopic ) + { + if ( *pnLenLinearCTIsotopic ) + { + if ( *pnLenLinearCTIsotopic != nLinearCTIsotopicLen ) + return CT_LEN_MISMATCH; /* */ + } + else + *pnLenLinearCTIsotopic = nLinearCTIsotopicLen; + } + + /* Return value: >0 => OK */ + return nLinearCTIsotopicLen; +} + + +/****************************************************************************/ +int FillTautLinearCT2(CANON_GLOBALS *pCG, + int num_atoms, + int num_at_tg, + int bIsoTaut, + const AT_RANK *nRank, + const AT_RANK *nAtomNumber, + const AT_RANK *nSymmRank, + const AT_RANK *nRankIso, + const AT_RANK *nAtomNumberIso, + const AT_RANK *nSymmRankIso, + AT_TAUTOMER *LinearCTTautomer, + int nMaxLenLinearCTTautomer, + int *pnLenLinearCTTautomer, + AT_ISO_TGROUP *LinearCTIsotopicTautomer, + int nMaxLenLinearCTIsotopicTautomer, + int *pnLenLinearCTIsotopicTautomer, + T_GROUP_INFO *t_group_info) +{ + /* nRank[i] = Canonical numbers of atoms,.. */ + /* nAtomNumber[r] = orig. atom number= 0,1,... for r = nRank-1 */ + /* nRank[nAtomNumber[r]] = r; r = 0,1,... */ + /* nAtomNumber[nRank[i]-1] = i; */ + + T_GROUP *t_group; + + int i, j, len=0, g, num_num, offset, max_len = 0, len_iso=0; + const static int max_num_num = sizeof(t_group->num)/sizeof(t_group->num[0]); + const static int max_num_iso = sizeof(LinearCTIsotopicTautomer->num)/sizeof(LinearCTIsotopicTautomer->num[0])+T_NUM_NO_ISOTOPIC; + + /**************************************************************************** + + Tautomeric groups 07-22-2002, modified 12-2003 + + ****************************************************************************/ + + if ( num_at_tg > num_atoms && t_group_info && t_group_info->num_t_groups ) + { + int num_t_groups = t_group_info->num_t_groups; + AT_NUMB *tGroupNumber = t_group_info->tGroupNumber; + AT_NUMB *tSymmRank = tGroupNumber + TGSO_SYMM_RANK*num_t_groups; /* equivalence */ + AT_NUMB *tiSymmRank = tGroupNumber + TGSO_SYMM_IRANK*num_t_groups; + AT_NUMB *tiGroupNumber = tGroupNumber + TGSO_SYMM_IORDER*num_t_groups; + AT_RANK nOffset = (AT_RANK)num_atoms; + + /* Fill Canonical ranks and Symmetry Ranks */ + /* memcpy( tPrevGroupNumber, tGroupNumber, num_t_groups*sizeof(tPrevGroupNumber[0])); */ + for ( i = num_atoms, j = 0; i < num_at_tg; i ++, j ++ ) + { + /* tPrevGroupNumber[j] = */ + tGroupNumber[j] = nAtomNumber[i] - nOffset; + tSymmRank[j] = nSymmRank[i] - nOffset; + if ( bIsoTaut ) { + tiGroupNumber[j] = nAtomNumberIso[i] - nOffset; + tiSymmRank[j] = nSymmRankIso[i] - nOffset; + } + } + + /* Sort enpoints within each tautomeric group according to the canonical ranks */ + pCG->m_pn_RankForSort = nRank; + for ( i = 0; i < num_t_groups; i ++ ) + { + inchi_qsort( pCG, + t_group_info->nEndpointAtomNumber + (int)t_group_info->t_group[i].nFirstEndpointAtNoPos, + t_group_info->t_group[i].nNumEndpoints, + sizeof(t_group_info->nEndpointAtomNumber[0]), + CompRank ); + } + /* fill out LinearCTTautomer */ + if ( nMaxLenLinearCTTautomer ) + { + max_len = T_GROUP_HDR_LEN * t_group_info->num_t_groups + t_group_info->nNumEndpoints+1; + if ( max_len > nMaxLenLinearCTTautomer ) + return CT_OVERFLOW; /* */ + } + + /**************************************************************** + * tautomer group format (#: is an offset) + **************************************************************** + * HEADER (T_GROUP_HDR_LEN=3+3iso) + * 0: N = number of endpoints ( t_group->nNumEndpoints ) + * 1: number of mobile groups ( t_group->num[0] ) + * 2: number of neg. charges ( t_group->num[1] ) {note: T_NUM_NO_ISOTOPIC=2} + * ENDPOINT RANKS + * 3..N+2: sorted tautomer group endpoint ranks; the sorting order is in + * t_group_info->nEndpointAtomNumber[t_group->nFirstEndpointAtNoPos+j], j=0..N-1 + * + * End mark : N==0 + ****************************************************************/ + /* num_num = t_group_info->bIgnoreIsotopic? T_NUM_NO_ISOTOPIC : max_num_num; */ + num_num = max_num_num; /* always include isotopic info; ignore it at the CT comparison step. */ + for ( i = 0; i < t_group_info->num_t_groups; i ++ ) + { + g = tGroupNumber[i]; /* ith tautomeric group number in canonical order */ + t_group = t_group_info->t_group + g; + /******************************************************* + * Tautomer non-isotopic part: LinearCTTautomer + *******************************************************/ + /* check length */ + if ( CHECK_OVERFLOW(len + T_GROUP_HDR_LEN + t_group->nNumEndpoints, max_len) ) + return CT_OVERFLOW; /* */ + + /* t_group header: number of endpoints */ + LinearCTTautomer[len++] = t_group->nNumEndpoints; + /* t_group header: */ + /* (a) number of mobile groups in the t_group (number of H + number of (-) ) and */ + /* (b) number of mobile negative charges (-) in the t_group */ + for ( j = 0; j < T_NUM_NO_ISOTOPIC; j ++ ) + { + LinearCTTautomer[len++] = t_group->num[j]; + } + /* t_group endpoint ranks link the group to the tautomeric endpoint atoms in the structure */ + /* according to their ranks */ + for ( j = 0, offset = t_group->nFirstEndpointAtNoPos; j < t_group->nNumEndpoints; j ++ ) + { + LinearCTTautomer[len++] = nRank[(int)t_group_info->nEndpointAtomNumber[offset+j]]; + } + } + if ( nMaxLenLinearCTTautomer ) + { + LinearCTTautomer[len++] = 0; /* or CT_INITVALUE ??? */ + if ( len != max_len ) { + len = -len; /* program error */ /* */ + } + else if ( *pnLenLinearCTTautomer && *pnLenLinearCTTautomer != len ) + { + return CT_LEN_MISMATCH; + } + else + { + *pnLenLinearCTTautomer = len; + } + } + else + { + *pnLenLinearCTTautomer = 0; + } + + /****************************************************************** + * Isotopic Tautomeric mobile groups part: LinearCTIsotopicTautomer + ******************************************************************/ + if ( nMaxLenLinearCTIsotopicTautomer && !t_group_info->nNumIsotopicEndpoints ) + { + for ( i = 0; i < t_group_info->num_t_groups; i ++ ) + { + g = tiGroupNumber[i]; /* ith tautomeric group number in canonical order */ + t_group = t_group_info->t_group + g; + /* find if mobile hydrogens are isotopic */ + if ( !t_group->iWeight ) + { + continue; /* no isotopic H */ + } + if ( CHECK_OVERFLOW(len_iso, nMaxLenLinearCTIsotopicTautomer) ) + return CT_OVERFLOW; /* */ + for ( j = T_NUM_NO_ISOTOPIC; j < max_num_num && j < max_num_iso; j ++ ) + { + /* num_T, num_D, num_1H */ + LinearCTIsotopicTautomer[len_iso].num[j-T_NUM_NO_ISOTOPIC] = t_group->num[j]; + } + /* link to tautomer group LinearCTTautomer[i]: */ + LinearCTIsotopicTautomer[len_iso++].tgroup_num = (AT_NUMB)(i + 1); /* t_group isotopic rank */ + } + } + if ( nMaxLenLinearCTIsotopicTautomer ) + { + if ( *pnLenLinearCTIsotopicTautomer && *pnLenLinearCTIsotopicTautomer != len_iso ) + { + return CT_LEN_MISMATCH; + } + *pnLenLinearCTIsotopicTautomer = len_iso; + } + else + { + *pnLenLinearCTIsotopicTautomer = 0; + } + } + + return len; +} + + +/**************************************************************************** + * + * Update a linear connection table out of final ranks + */ +int UpdateFullLinearCT( int num_atoms, + int num_at_tg, + sp_ATOM* at, + AT_RANK *nRank, + AT_RANK *nAtomNumber, + CANON_STAT* pCS, + CANON_GLOBALS *pCG, + int bFirstTime ) +{ + /* at[i].init_rank = initial ranks before canonizing */ + /* nRank[i] = new ordering number for atoms: nRank=1,2,.. */ + /* nAtomNumber[r] = orig. atom number= 0,1,... for r = nRank-1 */ + /* nRank[nAtomNumber[r]] = r; r = 0,1,... */ + /* nAtomNumber[nRank[i]-1] = i; */ + + AT_NUMB nNeighborNumber[MAXVAL]; + int i, j, k, num_neigh, rank, bCompare; /*, nRetVal; */ + + T_GROUP_INFO *t_group_info = NULL; + T_GROUP *t_group = NULL; + AT_NUMB *nEndpointAtomNumber = NULL; + + int nCTLen=0, nCTLenAtOnly=0; + + AT_NUMB r_neigh; + AT_NUMB *LinearCT = pCS->LinearCT; + + /* the following parts of the "name" should be compared */ + /* after the connection table comparison is done */ + /* to avoid wrong difference sign. So, these parts */ + /* go to a separate buffers. */ + /* -- currently not used at all at all -- */ + +#if CT_ATOMID != CT_ATOMID_DONTINCLUDE + AT_NUMB r0_at_type; +#endif + + bCompare = bFirstTime? 0 : 1; + + if ( num_at_tg > num_atoms ) + { + t_group_info = pCS->t_group_info; + t_group = t_group_info->t_group; + } + else + { + t_group_info = NULL; + t_group = NULL; + } + + /**********************************************************************/ + /* */ + /* CYCLE 1: FILL OUT CONNECTION TABLE(S) FOR ALL ATOMS */ + /* ** NOT INCLUDING ISOTOPIC ATOMS AND 1H, 2H(D), 3H(T) ** */ + /* */ + /* rank = nRank[nAtomNumber[rank-1]] -- proposed atoms canon. numbers */ + /**********************************************************************/ + for ( rank = 1; rank <= num_atoms; rank ++ ) + { + i = (int)nAtomNumber[rank-1]; /* current atom */ + +#if ( CT_ATOMID == CT_ATOMID_IS_CURRANK ) + r0_at_type = (AT_NUMB)rank; /* current Rank */ +#else +#if ( CT_ATOMID == CT_ATOMID_IS_INITRANK ) + r0_at_type = (AT_NUMB)at[i].init_rank; /* chemical + neighborhood ID */ +#else +#if ( CT_ATOMID == CT_ATOMID_DONTINCLUDE ) +#else + #error Undefined or wrong definition of CT_ATOMID +#endif +#endif +#endif + + /* add atom to the CT */ +#if ( CT_ATOMID != CT_ATOMID_DONTINCLUDE ) + if ( CHECK_OVERFLOW(nCTLen, pCS->nMaxLenLinearCT) ) + return CT_OVERFLOW; /* */ + COMPARE_WITH_CT(LinearCT, nCTLen, r0_at_type, bCompare); +#endif + + /******************************************************* + add neighbors and (if required) bonds to CT + ********************************************************/ + + /* sort neighbors */ + num_neigh = at[i].valence; + for ( k = 0; k < num_neigh; k ++) + { + nNeighborNumber[k] = (AT_NUMB)k; + } + pCG->m_pNeighborsForSort = at[i].neighbor; + pCG->m_pn_RankForSort = nRank; + insertions_sort( pCG, nNeighborNumber, (size_t)num_neigh, sizeof(nNeighborNumber[0]), CompNeighborsAT_NUMBER ); + + for ( k = 0; k < num_neigh; k ++) + { + /* rank = (new current atom Rank) */ + if ( (int)(r_neigh = (AT_NUMB)nRank[(int)at[i].neighbor[(int)nNeighborNumber[k]]]) + CT_NEIGH_SMALLER_THAN rank ) + { + if ( CHECK_OVERFLOW(nCTLen, pCS->nMaxLenLinearCT) ) + return CT_OVERFLOW; /* */ + COMPARE_WITH_CT( LinearCT, nCTLen, r_neigh, bCompare); + } + } + + /* add CT row delimiter */ + } /* end of cycle over all atoms. */ + + nCTLenAtOnly = nCTLen; + + /************************************************************** + + Tautomeric groups 07-22-2002 + + ***************************************************************/ + + for ( rank = num_atoms + 1; rank <= num_at_tg; rank ++ ) + { + j = (int)nAtomNumber[rank-1]; /* current "atom" */ + i = j - num_atoms; /* current t-group */ + +#if ( CT_ATOMID == CT_ATOMID_IS_CURRANK ) + r0_at_type = (AT_NUMB)rank; /* current Rank */ +#else +#if ( CT_ATOMID == CT_ATOMID_IS_INITRANK ) + r0_at_type = (AT_NUMB)rank; /* current Rank or (AT_NUMB)at[i].init_rank; ==> chemical + neighborhood ID */ +#else +#if ( CT_ATOMID == CT_ATOMID_DONTINCLUDE ) +#else + #error Undefined or wrong definition of CT_ATOMID +#endif +#endif +#endif + + /* add atom to the CT */ +#if ( CT_ATOMID != CT_ATOMID_DONTINCLUDE ) + if ( CHECK_OVERFLOW(nCTLen, pCS->nMaxLenLinearCT) ) + return CT_OVERFLOW; /* */ + COMPARE_WITH_CT(LinearCT, nCTLen, r0_at_type, bCompare); +#endif + + /******************************************************* + add neighbors and (if required) bonds to CT + ********************************************************/ + + /* sort endpoints */ + nEndpointAtomNumber = t_group_info->nEndpointAtomNumber+(int)t_group[i].nFirstEndpointAtNoPos; + pCG->m_pn_RankForSort = nRank; + num_neigh = (int)t_group[i].nNumEndpoints; + insertions_sort( pCG, nEndpointAtomNumber, (size_t)num_neigh, sizeof(nEndpointAtomNumber[0]), CompRank); + + for ( k = 0; k < num_neigh; k ++) + { + /* rank = (new current atom Rank) */ + if ( (int)(r_neigh = (AT_NUMB)nRank[(int)nEndpointAtomNumber[k]]) + CT_NEIGH_SMALLER_THAN rank ) { + if ( CHECK_OVERFLOW(nCTLen, pCS->nMaxLenLinearCT) ) + return CT_OVERFLOW; /* */ + COMPARE_WITH_CT( LinearCT, nCTLen, r_neigh, bCompare); + } + } + } /* end of cycle over all tautomeric groups. */ + + /* compare bonds types */ + /* compare elements */ + + if ( LinearCT ) + { + + if ( pCS->nLenLinearCT ) { + if ( pCS->nLenLinearCT != nCTLen ) + return CT_LEN_MISMATCH; /* */ + } + else + { + pCS->nLenLinearCT = nCTLen; + } + + if ( pCS->nLenLinearCT ) + { + if ( pCS->nLenLinearCTAtOnly != nCTLenAtOnly ) + return CT_LEN_MISMATCH; /* */ + } + else + { + pCS->nLenLinearCTAtOnly = nCTLenAtOnly; + } + } + + /* Return: 0=> identical CT; -1=> new CT is smaller than the previous one */ + return (bCompare-1); +} + + +/****************************************************************************/ +/* if (*bChanged & 1) then nSymmRank has been rearranged because for some r + min{i: r=nSymmRank[nAtomNumber[i]]}+1 != r + if (*bChanged & 2) then ranks nTempRank[] from nSymmRank[] differ from input nCurrRank[] + + on exit: + + nSymmRank[] have been updated if (*bChanged & 1) + nCurrRank[] have been updated if (*bChanged & 1) + nTempRank[] is always same as nCurrRank[] + nAtomNumber[] have been sorted so that + (i < j) <=> (nSymmRank[nAtomNumber[i]] <= nSymmRank[nAtomNumber[j]]) +*/ +int FixCanonEquivalenceInfo( CANON_GLOBALS *pCG, + int num_at_tg, + AT_RANK *nSymmRank, + AT_RANK *nCurrRank, + AT_RANK *nTempRank, + AT_NUMB *nAtomNumber, + int *bChanged) +{ + int nNumDiffRanks, bChangeSymmRank, bChangeCurrRank=0; + /* sort equivalence information */ + /* + int i; + for ( i = 0; i < num_at_tg; i ++ ) + { + nAtomNumber[i] = i; + } + */ + + pCG->m_pn_RankForSort = nSymmRank; /* minimal class representatives: min ranks for equiv. atoms */ + inchi_qsort( pCG, nAtomNumber, num_at_tg, sizeof(nAtomNumber[0]), CompRanksOrd ); + + /* convert equivalence information nSymmRank[] into ranks array nTempRank[] */ + /* eq. info contains min. possible ranks for eq. atoms; nCurrRank contains max. possible ranks */ + nNumDiffRanks = SortedEquInfoToRanks( nSymmRank/*inp*/, nTempRank/*out*/, nAtomNumber, num_at_tg, &bChangeSymmRank ); + + /* check whether nCurrRank is same as new initial ranks calculated from nSymmRank[] */ + bChangeCurrRank = memcmp( nCurrRank, nTempRank, num_at_tg*sizeof(nTempRank[0])); + + /*----------------------------------------------------------------------- + if ( bChangeSymmRank || bChangeCurrRank ) { + This is the case when the initial equitable partitioning does not produce + constitutionally equivalent classes of atoms. + Rebuild nSymmRank[] according to the new nCurrRank[] := nTempRank[] + For such structures the found canonical numbers of the constitutionally equivalent atoms + are not contiguous (see nCanonRank and nSymmRank examples below). Here arrays + nCurrRank, nAtomNumber, and nSymmRank are changed so that later the + contiguous canonical numbers for equivalent atoms can be obtained + (see GetCanonRanking under + "III. Get final canonical numbering (no stereo, no isotopic)". + + Example: for CAS=37520-11-9 (ID=21247: Ethane, 1,2-dicyclopropyl-), + + the numbers are the "final canon. numbers, nCanonRank" + 1 + + HC 7 5 3 + | \ + | >CH--CH2 CH + | / \ / | + HC H2C--CH< | + \ | + 2 6 8 CH + + 4 + + the arrays (arranged according to ordering in nAtomNumberTemp) are: + before SortedEquInfoToRanks after SortedRanksToEquInfo + orig. atom nos.,nAtomNumberTemp: {4 5 6 7 0 1 2 3} {4 5 6 7 0 1 2 3} + order numbers for sorted ranks: {0 1 2 3 4 5 6 7} {0 1 2 3 4 5 6 7} + canonical numbering, nCanonRank: {1 2 5 6 3 4 7 8} {1 2 5 6 3 4 7 8} + constit. equivalence, nSymmRank: {1 1 1 1 3 3 7 7} {1 1 1 1 5 5 7 7} used later + initial equivalence, nCurrRank: {6 6 6 6 6 6 8 8} {4 4 4 4 6 6 8 8} used later + initial numbering, nAtomNumber: {2 3 4 7 0 1 6 7} {0 1 2 3 4 5 6 7} used later + final, no stereo, no isotopic, after III. GetCanonRanking: + final canon. numbers, nCanonRank: {1 2 3 4 5 6 7 8} final + } + ----------------------------------------------------------------------------------*/ + if ( bChangeCurrRank ) + { + memcpy( nCurrRank, nTempRank, num_at_tg*sizeof(nCurrRank[0]) ); + } + if ( bChangeSymmRank ) + { + SortedRanksToEquInfo( nSymmRank/*out*/, nTempRank/*inp*/, nAtomNumber, num_at_tg ); + } + if ( bChanged ) + { + *bChanged = (0 != bChangeSymmRank) | 2*(0 != bChangeCurrRank); + } + + return nNumDiffRanks; +} + + +/* isotopic canonicalization */ +/*********************************************************************** + * + * Canon_INChI (former GetCanonRankingUsingEquivInfo) + * + */ +int Canon_INChI3(INCHI_CLOCK *ic, + int num_atoms, + int num_at_tg, + sp_ATOM* at, + CANON_STAT* pCS, + CANON_GLOBALS *pCG, + INCHI_MODE nMode, + int bTautFtcn) +{ + +/**************************************************************** + +0. Initiation, Prepare initial ranks for GetCanonRanking() + +I. Find constitutionally equivalent atoms and possibly canonical numbering +I.1 Set tautomer=On, stereo=isotopic=Off +I.2 GetCanonRanking(): Find constitutionally equivalent atoms and possibly canonical numbering +1.3 Fix canonical equivalence info if needed (if the fix is needed then the numbering is not canonical) + +II. Get final non-isotopic canonical numbering. Simultaneously obtain non-minimal isotopic and stereo CTs + GetCanonRanking() with pCS->bKeepSymmRank = 1 + FillOutStereoParities() (create initial stereo descriptors) + save non-isotopic canonicalization final results + hide isotopic and tautomeric results (for historical reasons only) + + +III. Find constitutionally equivalent isotopic atoms (for isotopic stereo canonicalization) +III.1 Allocate more memory +III.2 fill allocated memory with the initial data +III.3 duplicate, save old and add isotopic info to the new pCS->t_group_info +III.4 Prepare initial isotopic ranks for GetCanonRanking() +III.5 GetCanonRanking() to Find constitutionally equivalent ISOTOPIC atoms and tautomer groups +III.6 Fix canonical isotopic equivalence information and derive ranks out of it + +IV. Prepare a second Rank/AtomNumber Stack for mapping. + +V. Optimize isotopic part (optimized) + map_isotopic_atoms2() + save isotopic canonical numbering + +VI. Optimize stereo descriptors (optimized) + map_stereo_bonds4() + + +VII. Optimize isotopic stereo descriptors (optimized) + SwitchAtomStereoAndIsotopicStereo() + SetCtToIsotopicStereo() + FillOutStereoParities() + SetUseAtomForStereo() + map_stereo_bonds4() + + SwitchAtomStereoAndIsotopicStereo() + SetCtToNonIsotopicStereo() + + + + +*****************************************************************/ + + int nRet = 0, i, n; + + + /******************************************************** + input non-stereo canonical info + ********************************************************/ + BCN *pBCN = pCS->pBCN; + FTCN *ftcn = pBCN->ftcn + bTautFtcn; + + /******************************************************** + set mode flags + ********************************************************/ + + /* tautomeric structure */ + int bTaut = (num_at_tg > num_atoms) && pCS->t_group_info && pCS->t_group_info->num_t_groups && pCS->t_group_info->t_group; + + /* special case: induced by exchangable isotopic H inequivalence of atoms in formally non-tautomeric structure */ + int bIsoXchgH = pCS->t_group_info && pCS->t_group_info->nNumIsotopicEndpoints > 1 && + pCS->t_group_info->nIsotopicEndpointAtomNumber && pCS->t_group_info->nIsotopicEndpointAtomNumber[0] && + (pCS->t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE|TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE)) + /* && (ftcn->nCanonFlags & CANON_FLAG_ISO_TAUT_DIFF)*/; + int bHasIsotopicCanonData = (ftcn->PartitionCtIso.AtNumber && ftcn->PartitionCtIso.Rank && ftcn->nSymmRankCtIso); + + /* bHasIsotopicCanonData==0 means + * (1) No isotopic atoms in the component OR + * (2) the component has only exchangable isotopic H that do not change canonical numbering and equivalence. + */ + T_GROUP_INFO *t_group_info1 = bTaut? pCS->t_group_info : NULL; + + /*int bIsoXchgH = t_group_info1 && t_group_info1->nNumIsotopicEndpoints && t_group_info1->nIsotopicEndpointAtomNumber;*/ + /* isotopic canonicalization */ + int bCanonIsotopic = bHasIsotopicCanonData && ( nMode & CMODE_ISO ) && ( pCS->LinearCTIsotopic || pCS->LinearCTIsotopicTautomer || bIsoXchgH ); + + /* stereo canonicalization */ + int bCanonStereo = ( nMode & CMODE_STEREO ) && ( pCS->LinearCTStereoDble || pCS->LinearCTStereoCarb ); + + /* stereo isotopic canonicalization */ + int bCanonIsoStereo = bHasIsotopicCanonData && ( nMode & CMODE_ISO_STEREO ) && (pCS->LinearCTIsotopicStereoDble || pCS->LinearCTIsotopicStereoCarb) && bCanonIsotopic; + int bIsoTaut = (bTaut && bCanonIsotopic); + + int bIgnoreIsotopicInputGroups; + int bIgnoreIsotopicInputAtoms; + + AT_RANK **pRankStack1 = pBCN->pRankStack; + int nRankStackLen = pBCN->nMaxLenRankStack; + int num_max = pBCN->num_max; /* allocation lengths in *pRankStack1[] */ + NEIGH_LIST *NeighList = ftcn->NeighList; + + int nNumCurrRanks = 0; + AT_RANK *nTempRank = NULL; + + AT_RANK *nSymmRank = NULL; + + AT_RANK *nAtomNumber = NULL; + AT_RANK *nRank = NULL; + + AT_RANK **pRankStack2 = NULL; + AT_RANK *nCanonRankStereo = NULL; + AT_RANK *nCanonRankStereoInv = NULL; + AT_RANK *nSymmStereo = NULL; + + AT_RANK *nCanonRankIsotopicStereo = NULL; + AT_RANK *nCanonRankIsotopicStereoInv = NULL; + + CUR_TREE *cur_tree = NULL; + CUR_TREE CurrentTree; + + + /*AT_ISO_TGROUP *LinearCTIsotopicTautomer = NULL; */ + + + CANON_STAT CS2; + CANON_STAT* pCS2 = &CS2; + + inchiTime ulStartTime, ulEndTime; + /*=========== Mode Bits (low 8 bits, bit 0 is Least Significant Bit) =========== + + Mode Bits Description + '0' c 0 Only one connection table canonicalization + '1' C 1 Recalculate CT using fixed nSymmRank + '2' i 1|2 Isotopic canonicalization (internal) + '3' I 1|2|4 Isotopic canonicalization (output) + '4' s 1|8 Stereo canonicalization + '5' S 1|2|4|16 Stereo isotopic canonicalization + '6' A 1|2|4|8|16 Output All + + --- high 8 bits ---- + --- obsolete, only historical interest. ------ + 1-2 : 0 => at[i].init_rank from Morgan+NeighList + 1 => at[i].init_rank from Atom Invariants + 2 => at[i].init_rank from nSymmRank[] + (at[i].init_rank is included in LinearCT + depending on CT_ATOMID definition) + 3 : 1 => Get Stereo canonical info + 4 : 1 => Get Isotopic canonical info + 5 : 1 => Get Charge/Radical canonical info + ==================================================================*/ + /*int nOutputMode = 0;*/ /* obsolete */ + + + int bSwitchedAtomToIsotopic = 0; + + + /* vABParityUnknown holds actual value of an internal constant signifying */ + /* unknown parity: either the same as for undefined parity (default==standard) */ + /* or a specific one (non-std; requested by SLUUD switch). */ + int vABParityUnknown = AB_PARITY_UNDF; + if ( 0 != ( nMode & REQ_MODE_DIFF_UU_STEREO) ) + { + /* Make labels for unknown and undefined stereo different */ + vABParityUnknown = AB_PARITY_UNKN; + } + + InchiTimeGet( &ulStartTime ); + + *pCS2 = *pCS; /* save input information and pointers to allocated memory */ + + /* set "ignore isotopic differences in tautomer groups" true */ + if ( bTaut ) + { + /* save request for isotopic tautomeric groups */ + bIgnoreIsotopicInputGroups = pCS->t_group_info->bIgnoreIsotopic; + pCS->t_group_info->bIgnoreIsotopic = 1; + } + else + { + bIgnoreIsotopicInputGroups = 1; + } + /* save request for isotopic name */ + bIgnoreIsotopicInputAtoms = pCS->bIgnoreIsotopic; + /* set "ignore isotopic differences in atoms" true */ + pCS->bIgnoreIsotopic = 1; + + + /* save non-isotopic and isotopic canonicalization results */ + pCS->nCanonFlags = ftcn->nCanonFlags; + /* 1. non-isotopic */ + + /* linear CT, H */ + memcpy( pCS->LinearCT, ftcn->LinearCt, ftcn->nLenLinearCt * sizeof(pCS->LinearCT[0]) ); + if ( pCS->nNum_H && ftcn->nNumH ) + { + for ( i = 0; i < num_atoms; i ++ ) + { + pCS->nNum_H[i] = /*(S_CHAR)*/(CHAR_MASK & ftcn->nNumH[i]); + } + } + if ( pCS->nNum_H_fixed && ftcn->nNumHFixH ) + { + for ( i = 0; i < num_atoms; i ++ ) + { + pCS->nNum_H_fixed[i] = /*(S_CHAR)*/(CHAR_MASK & ftcn->nNumHFixH[i]); + } + } + pCS->nLenLinearCT = ftcn->nLenLinearCt; + pCS->nLenLinearCTAtOnly = ftcn->nLenLinearCtAtOnly; + + /* save non-isotopic atoms equivalence and numbering */ + if ( pCS->nSymmRank ) + { + memcpy( pCS->nSymmRank, ftcn->nSymmRankCt, num_at_tg * sizeof(pCS->nSymmRank[0]) ); + } + if ( pCS->nCanonOrd ) + { + memcpy( pCS->nCanonOrd, ftcn->PartitionCt.AtNumber, num_at_tg * sizeof(pCS->nCanonOrd[0]) ); + pCS->nLenCanonOrd = num_atoms; + } + if ( ftcn->iso_exchg_atnos && pCS->nExchgIsoH ) + { + for ( i = 0; i < num_atoms; i ++ ) + { + pCS->nExchgIsoH[i] = !ftcn->iso_exchg_atnos[i]; /* (pCS->nExchgIsoH[i]==1) => tautomeric or hetero atoms that may exchange isotopic H */ + } + } + /* 2. isotopic */ + + if ( bCanonIsotopic ) + { + /* linear CT, num_H are same as non-isotopic */ + /* save atoms equivalence and numbering */ + if ( pCS->nSymmRankIsotopic ) + { + memcpy( pCS->nSymmRankIsotopic, ftcn->nSymmRankCtIso, num_at_tg * sizeof(pCS->nSymmRankIsotopic[0])); + } + if ( pCS->nCanonOrdIsotopic ) + { + memcpy( pCS->nCanonOrdIsotopic, ftcn->PartitionCtIso.AtNumber, num_at_tg * sizeof(pCS->nCanonOrdIsotopic[0]) ); + pCS->nLenCanonOrdIsotopic = num_at_tg; + } + + nRet = FillIsotopicAtLinearCT( num_atoms, + at, + ftcn->PartitionCtIso.AtNumber, + pCS->LinearCTIsotopic, + pCS->nMaxLenLinearCTIsotopic, + &pCS->nLenLinearCTIsotopic ); + + if ( RETURNED_ERROR(nRet) ) + { + goto exit_function; + } + if ( nRet < 0 ) + { + nRet = CT_TAUCOUNT_ERR; + goto exit_function; + } + } + else + { + pCS->nMaxLenLinearCTIsotopic = 0; + pCS->nMaxLenLinearCTIsotopicTautomer = 0; + } + + /* fill out tautomeric groups, isotopic and non-isotopic tautomeric CT and t_group_info1->tGroupNumber */ + if ( bTaut ) + { + bIsoTaut = bIsoTaut && ftcn->PartitionCtIso.Rank && + ftcn->PartitionCtIso.AtNumber && ftcn->nSymmRankCtIso; + + nRet = FillTautLinearCT2( pCG, num_atoms, num_at_tg, bIsoTaut, + ftcn->PartitionCt.Rank, ftcn->PartitionCt.AtNumber, ftcn->nSymmRankCt, + ftcn->PartitionCtIso.Rank, ftcn->PartitionCtIso.AtNumber, ftcn->nSymmRankCtIso, + pCS->LinearCTTautomer, pCS->nMaxLenLinearCTTautomer, &pCS->nLenLinearCTTautomer, + pCS->LinearCTIsotopicTautomer, pCS->nMaxLenLinearCTIsotopicTautomer, &pCS->nLenLinearCTIsotopicTautomer, + t_group_info1 ); + + if ( RETURNED_ERROR(nRet) ) + { + goto exit_function; + } + if ( nRet <= 0 ) + { + nRet = CT_TAUCOUNT_ERR; + goto exit_function; + } + else + { + /* tautomeric groups: save non-isotopic symmetry & t_group order */ + int num_t_groups = t_group_info1->num_t_groups; + AT_NUMB *tGroupNumber = t_group_info1->tGroupNumber; + AT_NUMB *tSymmRank = tGroupNumber + TGSO_SYMM_RANK*num_t_groups; + if ( pCS->nSymmRankTaut ) + { + memcpy( pCS->nSymmRankTaut, tSymmRank, num_t_groups * sizeof(pCS->nSymmRank[0]) ); /* fixed 5-23-02 */ + } + if ( pCS->nCanonOrdTaut ) + { + memcpy( pCS->nCanonOrdTaut, tGroupNumber, num_t_groups * sizeof(pCS->nCanonOrdTaut[0]) ); + pCS->nLenCanonOrdTaut = num_t_groups; + } + if ( bCanonIsotopic /*&& pCS->nLenLinearCTIsotopicTautomer*/ ) + { + /* tautomeric groups: save isotopic symmetry & t_group order */ + /*AT_NUMB ntRankOffset = (AT_RANK)num_atoms;*/ + AT_NUMB *tiSymmRank = tGroupNumber + TGSO_SYMM_IRANK*num_t_groups; + AT_NUMB *tiGroupNumber = tGroupNumber + TGSO_SYMM_IORDER*num_t_groups; + if ( pCS->nSymmRankIsotopicTaut ) + { + memcpy( pCS->nSymmRankIsotopicTaut, tiSymmRank, num_t_groups * sizeof(pCS->nSymmRankIsotopicTaut[0]) ); + } + memcpy( pCS->nCanonOrdIsotopicTaut, tiGroupNumber, num_t_groups * sizeof(pCS->nCanonOrdIsotopicTaut[0]) ); + pCS->nLenCanonOrdIsotopicTaut = num_t_groups; + } + } + } + /* save connection table if requested */ + if ( pCS->LinearCT2 ) + { + memcpy( pCS->LinearCT2, pCS->LinearCT, sizeof(pCS->LinearCT2[0])*pCS->nLenLinearCT ); + pCS->nLenLinearCT2 = pCS->nLenLinearCT; + pCS->nLenLinearCTAtOnly2 = pCS->nLenLinearCTAtOnly; + } + + if ( num_atoms <= 1 ) + { + bCanonStereo = 0; /* a sinle atom + possibly terminal hydrogen atoms */ + if ( num_atoms < 1 || !at[0].parity2 ) + { + bCanonIsoStereo = 0; /* structure; for example Cl- or CH4 */ + } + } + + if ( !bCanonStereo && !(bCanonIsotopic && bCanonIsoStereo) ) + { + goto exit_function; /* skip stereo canonicalization */ + } + + + + /********************************************************** + Mode + ***********************************************************/ + nMode = nMode & CANON_MODE_MASK; + + /* memory allocation */ + + nAtomNumber = (AT_RANK *)qmalloc( num_max*sizeof(*nAtomNumber)); + nRank = (AT_RANK *)qmalloc( num_max*sizeof(*nRank)); + nTempRank = (AT_RANK *)qmalloc( num_max*sizeof(*nTempRank)); + nSymmRank = (AT_RANK *)qmalloc( num_max*sizeof(*nSymmRank)); + + /*********************************************** + 0.1 Initialization + ************************************************/ + + + if ( !NeighList || !nAtomNumber || !nTempRank || + !nRank || !pCS->LinearCT ) + { + nRet = CT_OUT_OF_RAM; /* program error */ /* */ + goto exit_function; + } + + pCS->NeighList = NeighList; + + *pCS2 = *pCS; /* save input information and pointers to allocated memory */ + + if ( !(nMode & CMODE_NOEQ_STEREO) && (bCanonStereo || bCanonIsoStereo ) ) + { + /* will be used to discover vertex equivalences in stereo canonicalization */ + memset( &CurrentTree, 0, sizeof(CurrentTree) ); + cur_tree = &CurrentTree; + } + + + pCS->bCmpStereo = 0; + pCS->bCmpIsotopicStereo = 0; + + + if ( bCanonStereo || bCanonIsoStereo ) + { + int ii, nn; + + /* stereo or isotopic canonicalization: we need a second set of ranks for mapping */ + /* (isotopic atoms or stereo can only increase nNumCurrRanks) */ + pRankStack2 = (AT_RANK **) inchi_calloc( nRankStackLen, sizeof(AT_RANK *) ); + if ( pRankStack2 ) + { + /* prepare for ranks reuse */ + for ( nn = 2; nn < nRankStackLen && pRankStack1[nn]; nn ++ ) + { + pRankStack1[nn][0] = 0; /* means ranks have to be calculated */ + } + /* reuse memory to reduce number of allocations: */ + /* move last half of pointers from pRankStack1 to pRankStack2 */ + /* The first 2 elements will be assigned separately */ + if ( (nn = (nn-2)/2) > 0 ) { + for ( ii = 2+nn; ii < nRankStackLen && pRankStack1[ii]; ii ++ ) + { + pRankStack2[ii-nn] = pRankStack1[ii]; + pRankStack1[ii] = NULL; + } + } + } + else + { + nRet = CT_OUT_OF_RAM; /* */ + goto exit_function; /* program error */ + } + } + + if ( bCanonStereo ) + { + + /* *pCS2 = *pCS; */ /* save input information and pointers to allocated memory */ + + /* initial ranking for non-isotopic mapping */ + memcpy( nAtomNumber, ftcn->PartitionCt.AtNumber, num_at_tg * sizeof(nAtomNumber[0]) ); + memcpy( nRank, ftcn->PartitionCt.Rank, num_at_tg * sizeof(nRank[0]) ); + memcpy( nSymmRank, ftcn->nSymmRankCt, num_at_tg * sizeof(nSymmRank[0]) ); + + /* nSymmRank changes if canonical numbers of constitutionally equivalent atoms are not contiguous */ + nNumCurrRanks = FixCanonEquivalenceInfo( pCG, num_at_tg, + nSymmRank /* in&out*/, + nRank, nTempRank /* out */, + nAtomNumber /* in&out */, + NULL); + /* atom numbers in canonical order */ + memcpy( pCS->nPrevAtomNumber, ftcn->PartitionCt.AtNumber, num_at_tg * sizeof(nAtomNumber[0]) ); + + /* fill stereo part of the connection table with initial (not optimized) parities */ + /* input + pCS->LinearCTStereoDble + pCS->LinearCTStereoCarb + pCS->nMaxLenLinearCTStereoCarb + pCS->nMaxLenLinearCTStereoDble + */ + + nRet = FillOutStereoParities( at, num_atoms, ftcn->PartitionCt.Rank, ftcn->PartitionCt.AtNumber, + nRank, nAtomNumber, pCS, pCG, 0 /* bIsotopic */ ); + + /* output + pCS->LinearCTStereoDble + pCS->LinearCTStereoCarb + pCS2->nLenLinearCTStereoCarb + pCS2->nLenLinearCTStereoDble + */ + + if ( RETURNED_ERROR( nRet ) ) + { + goto exit_function; + } + if ( nRet < 0 ) + { + nRet = CT_STEREOCOUNT_ERR; + goto exit_function; + } + + /*************************************************************** + * + * VI. Optimize non-isotopic stereo descriptors (optimized) + * + ***************************************************************/ + + /* allocate memory for stereo canonicalization */ + + if ( !nCanonRankStereo ) + nCanonRankStereo = (AT_RANK *) qmalloc( num_max*sizeof(*nCanonRankStereo)); + if ( !nSymmStereo && !(nMode & CMODE_NOEQ_STEREO) ) + nSymmStereo = (AT_RANK *) qmalloc( (num_max+1)*sizeof(*nSymmStereo)); + + if ( !(nMode & CMODE_NOEQ_STEREO) && 0 > CurTreeAlloc( cur_tree, num_at_tg ) ) { + nRet = CT_OUT_OF_RAM; /* */ + goto exit_function; + } + /* check allocations and assign first 2 elements of pRankStack2 */ + if ( pRankStack1 && pRankStack2 && + nCanonRankStereo && + /* nCurrRankStereo && nAtomNumberCurrStereo &&*/ + (nSymmStereo || (nMode & CMODE_NOEQ_STEREO)) ) + { + pRankStack1[0] = pRankStack2[0] = nRank; + pRankStack1[1] = pRankStack2[1] = nAtomNumber; + } + else + { + nRet = CT_OUT_OF_RAM; /* */ + goto exit_function; + } + + /**************************************************************** + * + * VI-A. Optimize non-isotopic non-inverted stereo descriptors + * + ****************************************************************/ + + /* set the 1st ranks in the rest of the stack to zero: prepare for ranks reuse */ + for ( n = 2; n < nRankStackLen && pRankStack1[n]; n ++ ) + { + pRankStack1[n][0] = 0; /* means ranks have to be recalculated */ + } + /* set the 1st ranks to zero: prepare for ranks reuse */ + for ( n = 2; n < nRankStackLen && pRankStack2[n]; n ++ ) + { + pRankStack2[n][0] = 0; /* means ranks have to be recalculated */ + } + + /* for debugging or statistics */ + pCS->lNumBreakTies = + pCS->lNumNeighListIter= + pCS->lNumTotCT = + pCS->lNumDecreasedCT = + pCS->lNumRejectedCT = + pCS->lNumEqualCT = 0; + pCS->bKeepSymmRank = 0; + pCS->bFirstCT = 1; /* To fill out nCanonRankStereo[] in map_stero_atoms2() */ + + /****************************************************************************** + nCanonRank contains input canonical numbering + nCanonRankStereo will be filled with a transposition of canonical numbering + which (1) keeps connection table unchanged and + (2) provides minimal stereo descriptors in + pCS->LinearCTStereoDble (length=pCS->nLenLinearCTStereoDble) + pCS->LinearCTStereoCarb (length=pCS->nLenLinearCTStereoCarb) + */ + + nRet = map_stereo_bonds4( ic, pCG, + at, + num_atoms, + num_at_tg, + num_max, + 0, + ftcn->PartitionCt.Rank, + ftcn->PartitionCt.AtNumber, + nCanonRankStereo, + nSymmRank, + pRankStack1, + pRankStack2, + nTempRank, + nNumCurrRanks, + nSymmStereo, + NeighList, + pCS, + cur_tree, + 0 /* nNumMappedBonds */, + vABParityUnknown); + + if ( RETURNED_ERROR( nRet ) ) + { + if ( nRet == CT_TIMEOUT_ERR ) + goto exit_function; + else + goto exit_function; /* program error */ + } + else + { + int bFailed = 0; + if ( !nRet ) + { + bFailed = 1; /* progrm error */ + pCS2->nLenLinearCTStereoCarb = + pCS->nLenLinearCTStereoCarb = -abs(pCS->nLenLinearCTStereoCarb); + pCS2->nLenLinearCTStereoDble = + pCS->nLenLinearCTStereoDble = -abs(pCS->nLenLinearCTStereoDble); + nRet = CT_STEREOCOUNT_ERR; /* */ + goto exit_function; /* program error */ + } + else + { + /* save non-isotopic lengths */ + pCS2->nLenLinearCTStereoDble = pCS->nLenLinearCTStereoDble; + pCS2->nLenLinearCTStereoCarb = pCS->nLenLinearCTStereoCarb; + nRet = 0; + } + + /* save stereo canonical numbering */ + if ( pCS->nCanonOrdStereo ) + { + for ( i = n = 0; i < num_at_tg; i ++ ) + { + if ( nCanonRankStereo[i] && (int)nCanonRankStereo[i] <= num_at_tg ) + { + pCS->nCanonOrdStereo[ (int)nCanonRankStereo[i] - 1 ] = (AT_NUMB)i; + } + else + { + bFailed ++; + } + } + pCS->nLenCanonOrdStereo = ( bFailed )? -num_atoms : num_atoms; + } + + /* save stereo tautomer groups numbering */ + if ( bTaut && pCS->nCanonOrdStereoTaut ) + { + if ( 0 < (nRet = SortTautomerGroupsAndEndpoints( pCG, t_group_info1, num_atoms, num_at_tg, nCanonRankStereo ) ) ) + { + /*non-isotopic contains symmetry ranks */ + int num_t_groups = t_group_info1->num_t_groups; + AT_NUMB *tGroupNumber = t_group_info1->tGroupNumber; + /*AT_NUMB *tiSymmRank = tGroupNumber + TGSO_SYMM_IRANK*num_t_groups; */ + memcpy( pCS->nCanonOrdStereoTaut, tGroupNumber, num_t_groups*sizeof(pCS->nCanonOrdStereoTaut[0]) ); + pCS->nLenCanonOrdStereoTaut = ( bFailed ) ? -num_t_groups + : num_t_groups; + } + else if ( RETURNED_ERROR( nRet ) ) + { + goto exit_function; + } + else + { + nRet = 0; + } + /*SortTautomerGroupsAndEndpoints( t_group_info1, nCanonRank ); */ /* ??? return to non-isotopic canonical numbering */ + } + } + + + /**************************************************** + * + * VI-B. Optimize INVERTED stereo descriptors + * + ****************************************************/ + if ( !nCanonRankStereoInv ) + nCanonRankStereoInv = (AT_RANK *) qmalloc( num_max*sizeof(*nCanonRankStereoInv)); + if ( !nCanonRankStereoInv ) + { + nRet = CT_OUT_OF_RAM; /* */ + goto exit_function; + } + + /* copy previous non-isotopic stereo canonicalization results to Inv initial data */ + /* assign pointers */ + pCS->LinearCTStereoDble = pCS2->LinearCTStereoDbleInv; + pCS->LinearCTStereoCarb = pCS2->LinearCTStereoCarbInv; + + /* copy the lengths */ + pCS2->nLenLinearCTStereoDbleInv = + pCS->nLenLinearCTStereoDbleInv = + pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTStereoDble; + + pCS2->nLenLinearCTStereoCarbInv = + pCS->nLenLinearCTStereoCarbInv = + pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTStereoCarb; + + if ( pCS->nLenLinearCTStereoDble > 0 || pCS->nLenLinearCTStereoCarb > 0 ) + { + /* copy previous results, the canonical stereo CT */ + memcpy( pCS->LinearCTStereoDble, pCS2->LinearCTStereoDble, pCS->nLenLinearCTStereoDble*sizeof(pCS->LinearCTStereoDble[0]) ); + memcpy( pCS->LinearCTStereoCarb, pCS2->LinearCTStereoCarb, pCS->nLenLinearCTStereoCarb*sizeof(pCS->LinearCTStereoCarb[0]) ); + } + memcpy( nCanonRankStereoInv, nCanonRankStereo, num_max * sizeof(nCanonRankStereoInv[0]) ); + if ( pCS->nCanonOrdStereoInv && pCS->nCanonOrdStereo ) + { + /* in case there is nothing to invert */ + memcpy( pCS->nCanonOrdStereoInv, pCS->nCanonOrdStereo, num_at_tg*sizeof(pCS->nCanonOrdStereoInv[0])); + } + + /****************************** + * + * Invert stereo + * + ******************************/ + + /********************************************************************************* + * Create initial approximation for the minimization of the stereo descriptors: + * invert stereogenic atom parities, one parity in each allene, all parities in + * pCS->LinearCTStereoCarb and allene parities in pCS->nLenLinearCTStereoDble + */ + nRet = InvertStereo( at, num_at_tg, nCanonRankStereo, nTempRank, pCS, 1 /* bInvertLinearCTStereo */ ); + if ( RETURNED_ERROR( nRet ) ) + { + goto exit_function; + } + else if ( nRet > 0 ) + { + /* InvertStereo() has done some changes */ + nRet = 0; + /* FillOutStereoParities() has already been called to fill out these 2 LinearCTs */ + + /* set the 1st ranks in the rest of the stack to zero: prepare for ranks reuse */ + for ( n = 2; n < nRankStackLen && pRankStack1[n]; n ++ ) + { + pRankStack1[n][0] = 0; /* means ranks have to be recalculated */ + } + /* set the 1st ranks to zero: prepare for ranks reuse */ + for ( n = 2; n < nRankStackLen && pRankStack2[n]; n ++ ) + { + pRankStack2[n][0] = 0; /* means ranks have to be recalculated */ + } + + /* for debugging or statistics */ + pCS->lNumBreakTies = + pCS->lNumNeighListIter= + pCS->lNumTotCT = + pCS->lNumDecreasedCT = + pCS->lNumRejectedCT = + pCS->lNumEqualCT = 0; + pCS->bKeepSymmRank = 0; + pCS->bFirstCT = 1; /* To fill out nCanonRankStereo[] in map_stero_atoms2() */ + + /****************************************************************************** + ftcn->PartitionCt.Rank contains input canonical numbering + nCanonRankStereoInv will be filled with a transposition of canonical numbering + which (1) keeps connection table unchanged and + (2) provides minimal stereo descriptors in + pCS->LinearCTStereoDble (length=pCS->nLenLinearCTStereoDble) + pCS->LinearCTStereoCarb (length=pCS->nLenLinearCTStereoCarb) + ******************************************************************************/ + nRet = map_stereo_bonds4( ic, + pCG, + at, + num_atoms, + num_at_tg, + num_max, + 0, + ftcn->PartitionCt.Rank, + ftcn->PartitionCt.AtNumber, + nCanonRankStereoInv, + nSymmRank, + pRankStack1, + pRankStack2, + nTempRank, + nNumCurrRanks, + nSymmStereo, + NeighList, + pCS, + cur_tree, + 0, + vABParityUnknown); + + if ( RETURNED_ERROR( nRet ) ) + { + if ( nRet == CT_TIMEOUT_ERR ) + goto exit_function; + else + goto exit_function; /* program error */ + } + else + { + int bFailed = 0; + if ( !nRet ) + { + bFailed = 1; /* progrm error */ + pCS2->nLenLinearCTStereoCarb = + pCS->nLenLinearCTStereoCarb = -abs(pCS->nLenLinearCTStereoCarb); + pCS2->nLenLinearCTStereoDble = + pCS->nLenLinearCTStereoDble = -abs(pCS->nLenLinearCTStereoDble); + nRet = CT_STEREOCOUNT_ERR; /* */ + goto exit_function; /* program error */ + } + + /* save non-isotopic pointers & lengths for INVERTED stereo */ + pCS->nLenLinearCTStereoDbleInv = + pCS2->nLenLinearCTStereoDbleInv = pCS->nLenLinearCTStereoDble; + pCS->nLenLinearCTStereoCarbInv = + pCS2->nLenLinearCTStereoCarbInv = pCS->nLenLinearCTStereoCarb; + + /* restore pointers and lengths to non-inverted stereo */ + /* -- this is needed for InvertStereo() back, see below */ + pCS->LinearCTStereoDble = pCS2->LinearCTStereoDble; + pCS->LinearCTStereoCarb = pCS2->LinearCTStereoCarb; + pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTStereoDble; + pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTStereoCarb; + /* consistency check */ + if ( pCS->nLenLinearCTStereoDbleInv != pCS->nLenLinearCTStereoDble || + pCS->nLenLinearCTStereoCarbInv != pCS->nLenLinearCTStereoCarb ) + { + nRet = CT_CALC_STEREO_ERR; + goto exit_function; /* program error */ + } + + /****************************** + * + * Invert stereo back + * + ****************************** + * (make sure that pointers + * pCS->LinearCTStereoCarb, + * pCS->LinearCTStereoDble + * and corresponding lengths + * have been restored) + ******************************/ + /********************************************************************************* + * invert only stereogenic atom parities and one parity in each allene, DO NOT + * change parities in pCS->LinearCTStereoCarb and pCS->nLenLinearCTStereoDble + */ + nRet = InvertStereo( at, + num_at_tg, + nCanonRankStereo, + nTempRank, + pCS, + 0 ); + + if ( RETURNED_ERROR( nRet ) ) + { + goto exit_function; + } + nRet = 0; + + + /* save stereo canonical numbering */ + if ( pCS->nCanonOrdStereoInv ) + { + for ( i = n = 0; i < num_at_tg; i ++ ) + { + if ( nCanonRankStereoInv[i] && (int)nCanonRankStereoInv[i] <= num_at_tg ) + { + pCS->nCanonOrdStereoInv[ (int)nCanonRankStereoInv[i] - 1 ] = (AT_NUMB)i; + } + else + { + bFailed ++; + } + } + pCS->nLenCanonOrdStereo = ( bFailed )? -num_atoms : num_atoms; + } + + /* compare inverted and non-inverted stereo */ + pCS->bCmpStereo = 2 + CompareLinCtStereo( + pCS->LinearCTStereoDbleInv, pCS->nLenLinearCTStereoDbleInv, + pCS->LinearCTStereoCarbInv, pCS->nLenLinearCTStereoCarbInv, + pCS->LinearCTStereoDble, pCS->nLenLinearCTStereoDble, + pCS->LinearCTStereoCarb, pCS->nLenLinearCTStereoCarb + ); + } + } + else if ( 0 == nRet ) + { + /* nothing has been done, restore pointers and lengths for stereo */ + pCS->LinearCTStereoDble = pCS2->LinearCTStereoDble; + pCS->LinearCTStereoCarb = pCS2->LinearCTStereoCarb; + pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTStereoDble; + pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTStereoCarb; + } + } + + /* restore "ignore isotopic differences in tautomer groups" */ + if ( bTaut ) + { + /* save request for isotopic tautomeric groups */ + pCS->t_group_info->bIgnoreIsotopic = bIgnoreIsotopicInputGroups; + } + + /* restore request for isotopic name */ + pCS->bIgnoreIsotopic = bIgnoreIsotopicInputAtoms; + + if ( bCanonIsoStereo && bCanonIsotopic ) + { + + /**************************************************************** + * + * VII. Optimize isotopic stereo descriptors (optimized) + * + ****************************************************************/ + /* + pCS->LinearCTIsotopic = NULL; + */ + + /* initial ranking for isotopic mapping */ + memcpy( nAtomNumber, ftcn->PartitionCtIso.AtNumber, num_at_tg * sizeof(nAtomNumber[0]) ); + memcpy( nRank, ftcn->PartitionCtIso.Rank, num_at_tg * sizeof(nRank[0]) ); + memcpy( nSymmRank, ftcn->nSymmRankCtIso, num_at_tg * sizeof(nSymmRank[0]) ); + + /* nSymmRank will change if canonical numbers of of constitutionally equivalent atoms are not contiguous */ + nNumCurrRanks = FixCanonEquivalenceInfo( pCG, num_at_tg, nSymmRank /* in&out*/, + nRank, nTempRank /* out */, nAtomNumber /* in&out */, NULL); + + memcpy( pCS->nPrevAtomNumber, ftcn->PartitionCtIso.AtNumber, num_at_tg * sizeof(nAtomNumber[0]) ); + + /* allocate memory for optimized stereo canonicalization */ + /* for stereo canonical numbering to be found. */ + if ( !nCanonRankIsotopicStereo ) + nCanonRankIsotopicStereo = (AT_RANK *) qmalloc( num_max*sizeof(*nCanonRankIsotopicStereo)); + if ( !nSymmStereo && !(nMode & CMODE_NOEQ_STEREO) ) + nSymmStereo = (AT_RANK *) qmalloc( (num_max+1)*sizeof(*nSymmStereo)); + + if ( !(nMode & CMODE_NOEQ_STEREO) && CurTreeAlloc( cur_tree, num_at_tg ) ) + { + nRet = CT_OUT_OF_RAM; /* */ + goto exit_function; + } + + /* check allocations and assign first 2 elements of pRankStack2 */ + if ( pRankStack1 && pRankStack2 && + nCanonRankIsotopicStereo && + (nSymmStereo || (nMode & CMODE_NOEQ_STEREO)) ) + { + + pRankStack1[0] = pRankStack2[0] = nRank; /* pRankStack1[0,1] shall be unchanged */ + pRankStack1[1] = pRankStack2[1] = nAtomNumber; + } + else + { + nRet = CT_OUT_OF_RAM; /* */ + goto exit_function; + } + + /****************************************************************** + Important: fill out a list of stereo atoms and bonds including + those which are stereo due to isotopic atoms only and create + LinearCT stereo descriptors for the canonical numbering + ******************************************************************/ + + + /* at[] has certain members for non-isotopic and isotopic stereo; switch them */ + SwitchAtomStereoAndIsotopicStereo( at, num_atoms, &bSwitchedAtomToIsotopic ); + /* prepare stereo connection tables' pointers */ + SetCtToIsotopicStereo( pCS, pCS2 ); + + nRet = FillOutStereoParities( at, num_atoms, + ftcn->PartitionCtIso.Rank, + ftcn->PartitionCtIso.AtNumber, + nRank, nAtomNumber, + pCS, pCG, 1 /* bIsotopic */); + + + if (RETURNED_ERROR(nRet)) + { + goto exit_function; /* program error */ + } + else if ( !nRet ) + { + /* no isotopic stereo */ + pCS2->nLenLinearCTIsotopicStereoDble = pCS->nLenLinearCTIsotopicStereoDble = 0; + pCS2->nLenLinearCTIsotopicStereoCarb = pCS->nLenLinearCTIsotopicStereoCarb = 0; + pCS->nLenCanonOrdIsotopicStereo = 0; + pCS->nLenCanonOrdIsotopicStereoTaut = 0; + pCS2->nLenLinearCTIsotopicStereoDbleInv = pCS->nLenLinearCTIsotopicStereoDbleInv = 0; + pCS2->nLenLinearCTIsotopicStereoCarbInv = pCS->nLenLinearCTIsotopicStereoCarbInv = 0; + goto bypass_isotopic_stereo; + } else + { + nRet = 0; /* not an error */ + } + + + /************************************************************* + * + * VII-A. Optimize non-inverted isotopic stereo descriptors + * + *************************************************************/ + + /* set the 1st ranks in the rest of the stack to zero: prepare for ranks reuse */ + for ( n = 2; n < nRankStackLen && pRankStack1[n]; n ++ ) + { + pRankStack1[n][0] = 0; /* means ranks have to be recalculated */ + } + /* set the 1st ranks to zero: prepare for ranks reuse */ + for ( n = 2; n < nRankStackLen && pRankStack2[n]; n ++ ) + { + pRankStack2[n][0] = 0; /* means ranks have to be recalculated */ + } + + /* for debugging or statistics */ + pCS->lNumBreakTies = + pCS->lNumNeighListIter= + pCS->lNumTotCT = + pCS->lNumDecreasedCT = + pCS->lNumRejectedCT = + pCS->lNumEqualCT = 0; + pCS->bKeepSymmRank = 0; + pCS->bFirstCT = 1; /* To fill out nCanonRankStereo[] in map_stero_atoms2() */ + + /************************************************************************************** + nCanonRankIsotopic contains input canonical numbering + nCanonRankIsotopicStereo will be filled with a transposition of canonical numbering + that (1) keeps connection table unchanged and + (2) provides minimal stereo descriptors in + pCS->LinearCTStereoDble (length=pCS->nLenLinearCTStereoDble) + pCS->LinearCTStereoCarb (length=pCS->nLenLinearCTStereoCarb) + ***************************************************************************************/ + + nRet = map_stereo_bonds4 ( ic, + pCG, + at, + num_atoms, + num_at_tg, + num_max, + 0, + ftcn->PartitionCtIso.Rank, + ftcn->PartitionCtIso.AtNumber, + nCanonRankIsotopicStereo, + nSymmRank, + pRankStack1, + pRankStack2, + nTempRank, + nNumCurrRanks, + nSymmStereo, + NeighList, + pCS, + cur_tree, + 0, + vABParityUnknown); + + if ( RETURNED_ERROR( nRet ) ) + { + goto exit_function; + } + else + { + int bFailed = 0; + + if ( !nRet ) + { + bFailed = 1; /* program error */ + pCS2->nLenLinearCTIsotopicStereoDble = + pCS->nLenLinearCTIsotopicStereoDble = -abs(pCS->nLenLinearCTStereoDble); + pCS2->nLenLinearCTIsotopicStereoCarb = + pCS->nLenLinearCTIsotopicStereoCarb = -abs(pCS->nLenLinearCTStereoCarb); + nRet = CT_STEREOCOUNT_ERR; /* */ + goto exit_function; /* program error */ + } + else + { + /* save isotopic lengths */ + pCS->nLenLinearCTIsotopicStereoDble = + pCS2->nLenLinearCTIsotopicStereoDble = pCS->nLenLinearCTStereoDble; + pCS->nLenLinearCTIsotopicStereoCarb = + pCS2->nLenLinearCTIsotopicStereoCarb = pCS->nLenLinearCTStereoCarb; + + /* save stereo canonical numbering */ + if ( pCS->nCanonOrdIsotopicStereo ) + { + for ( i = n = 0; i < num_at_tg; i ++ ) + { + if ( nCanonRankIsotopicStereo[i] && (int)nCanonRankIsotopicStereo[i] <= num_at_tg ) + { + pCS->nCanonOrdIsotopicStereo[ (int)nCanonRankIsotopicStereo[i] - 1 ] = (AT_NUMB)i; + } + else + { + bFailed ++; + } + } + pCS->nLenCanonOrdIsotopicStereo = bFailed? -num_atoms : num_atoms; + } + + /* save stereo tautomer groups numbering */ + if ( pCS->nCanonOrdIsotopicStereoTaut ) + { + if ( 0 < (nRet=SortTautomerGroupsAndEndpoints( pCG, t_group_info1, num_atoms, num_at_tg, nCanonRankIsotopicStereo ) ) ) + { + /*non-isotopic contains symmetry ranks */ + int num_t_groups = t_group_info1->num_t_groups; + AT_NUMB *tGroupNumber = t_group_info1->tGroupNumber; + /*AT_NUMB *tiSymmRank = tGroupNumber + TGSO_SYMM_IRANK*num_t_groups; */ + memcpy( pCS->nCanonOrdIsotopicStereoTaut, tGroupNumber, num_t_groups*sizeof(pCS->nCanonOrdIsotopicStereoTaut[0]) ); + pCS->nLenCanonOrdIsotopicStereoTaut = bFailed? -num_t_groups:num_t_groups; + + /*SortTautomerGroupsAndEndpoints( t_group_info1, nCanonRank ); */ /* ??? return to non-isotopic canonical numbering */ + } + else if ( RETURNED_ERROR( nRet ) ) + { + goto exit_function; + } + else + { + nRet = 0; + } + } + } + } + + /********************************************************** + * + * VII-B. Optimize INVERTED isotopic stereo descriptors + * + **********************************************************/ + if ( !nCanonRankIsotopicStereoInv ) + nCanonRankIsotopicStereoInv = (AT_RANK *) qmalloc( num_max*sizeof(*nCanonRankIsotopicStereoInv)); + if ( !nCanonRankIsotopicStereoInv ) + { + nRet = CT_OUT_OF_RAM; /* */ + goto exit_function; + } + + /* copy previous isotopic stereo canonicalization results to Inv initial data */ + /* assign pointers */ + pCS->LinearCTStereoDble = pCS2->LinearCTIsotopicStereoDbleInv; /* enable stereo */ + pCS->LinearCTStereoCarb = pCS2->LinearCTIsotopicStereoCarbInv; + + /* copy the lengths */ + pCS2->nLenLinearCTIsotopicStereoDbleInv = + pCS->nLenLinearCTStereoDbleInv = + pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTIsotopicStereoDble; + + pCS2->nLenLinearCTIsotopicStereoCarbInv = + pCS->nLenLinearCTStereoCarbInv = + pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTIsotopicStereoCarb; + + if ( pCS->nLenLinearCTStereoDble > 0 || pCS->nLenLinearCTStereoCarb > 0 ) + { + /* copy previous results, the canonical stereo CT */ + memcpy( pCS->LinearCTStereoDble, pCS2->LinearCTIsotopicStereoDble, pCS->nLenLinearCTStereoDble*sizeof(pCS->LinearCTStereoDble[0]) ); + memcpy( pCS->LinearCTStereoCarb, pCS2->LinearCTIsotopicStereoCarb, pCS->nLenLinearCTStereoCarb*sizeof(pCS->LinearCTStereoCarb[0]) ); + } + memcpy( nCanonRankIsotopicStereoInv, nCanonRankIsotopicStereo, num_max * sizeof(nCanonRankIsotopicStereoInv[0]) ); + if ( pCS->nCanonOrdIsotopicStereoInv && pCS->nCanonOrdIsotopicStereo ) + { + /* in case there is nothing to invert */ + memcpy( pCS->nCanonOrdIsotopicStereoInv, pCS->nCanonOrdIsotopicStereo, num_at_tg*sizeof(pCS->nCanonOrdIsotopicStereoInv[0])); + } + + /****************************** + * + * Invert isotopic stereo + * + ******************************/ + + /********************************************************************************* + * Create initial approximation for the minimization of the stereo descriptors: + * invert stereogenic atom parities, one parity in each allene, all parities in + * pCS->LinearCTStereoCarb and allene parities in pCS->nLenLinearCTStereoDble + */ + + nRet = InvertStereo( at, + num_at_tg, + nCanonRankIsotopicStereo, + nTempRank, + pCS, + 1 ); + + if ( RETURNED_ERROR( nRet ) ) + { + goto exit_function; + } + else if ( nRet > 0 ) + { + /* InvertStereo() has done some changes */ + nRet = 0; + /* FillOutStereoParities() has already been called to fill out these 2 LinearCTs */ + + /* set the 1st ranks in the rest of the stack to zero: prepare for ranks reuse */ + for ( n = 2; n < nRankStackLen && pRankStack1[n]; n ++ ) + { + pRankStack1[n][0] = 0; /* means ranks have to be recalculated */ + } + /* set the 1st ranks to zero: prepare for ranks reuse */ + for ( n = 2; n < nRankStackLen && pRankStack2[n]; n ++ ) + { + pRankStack2[n][0] = 0; /* means ranks have to be recalculated */ + } + /* for debugging or statistics */ + pCS->lNumBreakTies = + pCS->lNumNeighListIter= + pCS->lNumTotCT = + pCS->lNumDecreasedCT = + pCS->lNumRejectedCT = + pCS->lNumEqualCT = 0; + pCS->bKeepSymmRank = 0; + pCS->bFirstCT = 1; /* To fill out nCanonRankStereo[] in map_stero_atoms2() */ + + /************************************************************************************** + nCanonRankIsotopic contains input canonical numbering + nCanonRankIsotopicStereo will be filled with a transposition of canonical numbering + that (1) keeps connection table unchanged and + (2) provides minimal stereo descriptors in + pCS->LinearCTStereoDble (length=pCS->nLenLinearCTStereoDble) + pCS->LinearCTStereoCarb (length=pCS->nLenLinearCTStereoCarb) + */ + nRet = map_stereo_bonds4( ic, pCG, + at, + num_atoms, + num_at_tg, + num_max, + 0, + ftcn->PartitionCtIso.Rank, + ftcn->PartitionCtIso.AtNumber, + nCanonRankIsotopicStereoInv, + nSymmRank, + pRankStack1, + pRankStack2, + nTempRank, + nNumCurrRanks, + nSymmStereo, + NeighList, + pCS, + cur_tree, + 0, + vABParityUnknown); + + if ( RETURNED_ERROR( nRet ) ) + { + if ( nRet == CT_TIMEOUT_ERR ) + goto exit_function; + else + goto exit_function; /* program error */ + } + else + { + int bFailed = 0; + + if ( !nRet ) + { + bFailed = 1; /* program error */ + pCS2->nLenLinearCTIsotopicStereoDble = + pCS->nLenLinearCTIsotopicStereoDble = -abs(pCS->nLenLinearCTStereoDble); + pCS2->nLenLinearCTIsotopicStereoCarb = + pCS->nLenLinearCTIsotopicStereoCarb = -abs(pCS->nLenLinearCTStereoCarb); + nRet = CT_STEREOCOUNT_ERR; /* */ + goto exit_function; /* program error */ + } + + /* save isotopic pointers & lengths for INVERTED stereo */ + + /* save isotopic lengths */ + pCS->nLenLinearCTIsotopicStereoDbleInv = + pCS2->nLenLinearCTIsotopicStereoDbleInv = pCS->nLenLinearCTStereoDble; + pCS->nLenLinearCTIsotopicStereoCarbInv = + pCS2->nLenLinearCTIsotopicStereoCarbInv = pCS->nLenLinearCTStereoCarb; + + /* restore pointers and lengths to non-inverted isotopic stereo */ + /* -- this is needed for InvertStereo() back, see below */ + pCS->LinearCTStereoDble = pCS2->LinearCTIsotopicStereoDble; + pCS->LinearCTStereoCarb = pCS2->LinearCTIsotopicStereoCarb; + pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTIsotopicStereoDble; + pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTIsotopicStereoCarb; + + /* consistency check */ + if ( pCS->nLenLinearCTIsotopicStereoDbleInv != pCS->nLenLinearCTIsotopicStereoDble || + pCS->nLenLinearCTIsotopicStereoCarbInv != pCS->nLenLinearCTIsotopicStereoCarb ) + { + nRet = CT_CALC_STEREO_ERR; + goto exit_function; /* program error */ + } + + /****************************** + * + * Invert stereo back + * + ****************************** + * (make sure that pointers + * pCS->LinearCTStereoCarb, + * pCS->LinearCTStereoDble + * and corresponding lengths + * have been restored) + ******************************/ + + nRet = InvertStereo( at, + num_at_tg, + nCanonRankIsotopicStereo, + nTempRank, + pCS, + 0 ); + + if ( RETURNED_ERROR( nRet ) ) + { + goto exit_function; + } + nRet = 0; + + /* save stereo canonical numbering */ + if ( pCS->nCanonOrdIsotopicStereoInv ) + { + for ( i = n = 0; i < num_at_tg; i ++ ) + { + if ( nCanonRankIsotopicStereoInv[i] && (int)nCanonRankIsotopicStereoInv[i] <= num_at_tg ) + { + pCS->nCanonOrdIsotopicStereoInv[ (int)nCanonRankIsotopicStereoInv[i] - 1 ] = (AT_NUMB)i; + } + else + { + bFailed ++; + } + } + pCS->nLenCanonOrdIsotopicStereo = bFailed? -num_atoms : num_atoms; + } + + /* compare inverted and non-inverted isotopic stereo */ + pCS->bCmpIsotopicStereo = 2 + CompareLinCtStereo( + pCS->LinearCTIsotopicStereoDbleInv, pCS->nLenLinearCTIsotopicStereoDbleInv, + pCS->LinearCTIsotopicStereoCarbInv, pCS->nLenLinearCTIsotopicStereoCarbInv, + pCS->LinearCTIsotopicStereoDble, pCS->nLenLinearCTIsotopicStereoDble, + pCS->LinearCTIsotopicStereoCarb, pCS->nLenLinearCTIsotopicStereoCarb + ); + } + } + else if ( 0 == nRet ) + { + /* nothing has been done, restore pointers and lengths for stereo */ + pCS->LinearCTStereoDble = pCS2->LinearCTIsotopicStereoDble; + pCS->LinearCTStereoCarb = pCS2->LinearCTIsotopicStereoCarb; + pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTIsotopicStereoDble; + pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTIsotopicStereoCarb; + } + + +bypass_isotopic_stereo:; /* ??? */ + + pCS->LinearCTIsotopic = pCS2->LinearCTIsotopic; + } + + + +exit_function: + + if ( bSwitchedAtomToIsotopic ) + { + SwitchAtomStereoAndIsotopicStereo( at, num_atoms, &bSwitchedAtomToIsotopic ); + SetCtToNonIsotopicStereo( pCS, pCS2 ); /* ??? */ + } + + /* restore non-isotopic connection table */ + if ( pCS->LinearCT2 ) + { + inchi_swap( (char*)&pCS->LinearCT, (char*)&pCS->LinearCT2, sizeof(pCS->LinearCT) ); + inchi_swap( (char*)&pCS->nLenLinearCT, (char*)&pCS->nLenLinearCT2, sizeof(pCS->nLenLinearCT) ); + inchi_swap( (char*)&pCS->nLenLinearCTAtOnly, (char*)&pCS->nLenLinearCTAtOnly2, sizeof(pCS->nLenLinearCTAtOnly) ); + } + + /* free memory */ + i = 2; + if ( pRankStack1 ) + { + pRankStack1[0] = + pRankStack1[1] = NULL; /* deallocated separately */ + for ( ; i < nRankStackLen && pRankStack1[i]; i ++ ) + ; + } + if ( pRankStack1 && pRankStack2 ) + { + for ( n = 2; n < nRankStackLen && pRankStack2[n]; n ++ ) + { + if ( i < nRankStackLen - 1 ) + { + pRankStack1[i++] = pRankStack2[n]; + } + else + { + inchi_free( pRankStack2[n] ); + } + } + inchi_free( pRankStack2 ); + } + + pCS->NeighList = NULL; /* keep the pointer in pBCN->ftcn[bTaut].NeighList for further deallocation */ + qfree ( nAtomNumber ); + qfree ( nTempRank ); + qfree ( nRank ); + qfree ( nSymmRank ); + + qfree( nSymmStereo ); + CurTreeFree( cur_tree ); +/* memory leak fix */ +/* + qfree ( nCurrRankIsotopicStereo ); + qfree ( nAtomNumberCurrIsotopicStereo); +*/ + qfree ( nCanonRankIsotopicStereo ); + qfree ( nCanonRankIsotopicStereoInv ); + + qfree( nCanonRankStereo ); + qfree( nCanonRankStereoInv ); + + InchiTimeGet( &ulEndTime ); + + pCS->lTotalTime = InchiTimeMsecDiff(ic, &ulEndTime, &ulStartTime); + + return (nRet >= -1) ? num_atoms + : nRet; + /* cannot easily get number of ranks for now */ +} + + +/**************************************************************************************/ +int Canon_INChI(INCHI_CLOCK *ic, + int num_atoms, + int num_at_tg, + sp_ATOM* at, + CANON_STAT* pCS, + CANON_GLOBALS *pCG, + INCHI_MODE nMode, + int bTautFtcn) +{ + if ( pCS->pBCN && !pCS->NeighList ) + { + return Canon_INChI3(ic, num_atoms, num_at_tg, at, pCS, pCG, nMode, bTautFtcn); + } + + return CT_CANON_ERR; +} diff --git a/INCHI-1-SRC/INCHI_BASE/src/ichicano.h b/INCHI-1-SRC/INCHI_BASE/src/ichicano.h new file mode 100644 index 0000000..015f653 --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/ichicano.h @@ -0,0 +1,120 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _INCHICANO_H_ +#define _INCHICANO_H_ + + +#include "ichicant.h" + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + + +int GetCanonLengths( int num_at, + sp_ATOM* at, + ATOM_SIZES *s, + T_GROUP_INFO *t_group_info ); +int AllocateCS( CANON_STAT *pCS, + int num_at, + int num_at_tg, + int nLenCT, + int nLenCTAtOnly, + int nLenLinearCTStereoDble, + int nLenLinearCTIsotopicStereoDble, + int nLenLinearCTStereoCarb, + int nLenLinearCTIsotopicStereoCarb, + int nLenLinearCTTautomer, + int nLenLinearCTIsotopicTautomer, + int nLenIsotopic, + INCHI_MODE nMode, + BCN *pBCN ); +int DeAllocateCS( CANON_STAT *pCS ); +void DeAllocBCN( BCN *pBCN ); + +struct tagINCHI_CLOCK; + +int Canon_INChI( struct tagINCHI_CLOCK *ic, + int num_atoms, + int num_at_tg, + sp_ATOM* at, + CANON_STAT* pCS, + CANON_GLOBALS *pCG, + INCHI_MODE nMode, + int bTautFtcn); +int GetBaseCanonRanking( struct tagINCHI_CLOCK *ic, + int num_atoms, + int num_at_tg, + sp_ATOM* at[], + T_GROUP_INFO *t_group_info, + ATOM_SIZES s[], + BCN *pBCN, + struct tagInchiTime *ulTimeOutTime, + CANON_GLOBALS *pCG, + int bFixIsoFixedH, + int LargeMolecules); +int bCanonIsFinerThanEquitablePartition( int num_atoms, + sp_ATOM* at, + AT_RANK *nSymmRank ); +int UpdateFullLinearCT( int num_atoms, + int num_at_tg, + sp_ATOM* at, + AT_RANK *nRank, + AT_RANK *nAtomNumber, + CANON_STAT* pCS, + CANON_GLOBALS *pCG, + int bFirstTime ); +int FixCanonEquivalenceInfo( CANON_GLOBALS *pCG, + int num_at_tg, + AT_RANK *nSymmRank, + AT_RANK *nCurrRank, + AT_RANK *nTempRank, + AT_NUMB *nAtomNumber, + int *bChanged); +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + + +#endif /* _INCHICANO_H_ */ diff --git a/INCHI-1-SRC/INCHI/common/ichicans.c b/INCHI-1-SRC/INCHI_BASE/src/ichicans.c similarity index 66% rename from INCHI-1-SRC/INCHI/common/ichicans.c rename to INCHI-1-SRC/INCHI_BASE/src/ichicans.c index 9648484..9a32ec9 100644 --- a/INCHI-1-SRC/INCHI/common/ichicans.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichicans.c @@ -1,1662 +1,2176 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - - -#include "mode.h" - -#include "incomdef.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichicant.h" -#include "ichicomn.h" -#include "ichister.h" - -#include "ichicomp.h" - - - - -typedef struct tagStereoBondNeighbor { /* *n = sort key */ - AT_RANK nRank; /* *1 opposite atom rank; equal ranks mean constit. equivalence */ - AT_RANK nNeighRank1; /* rank of the neighbor in the direction to the opposite atom */ - AT_RANK nNeighRank2; /* rank of the opposite atom neighbor in the direction to the current atom */ - AT_RANK num; /* number of same type bonds to constitutionally equivalent neighbors */ - AT_RANK num_any_parity; /* at least one atom has parity in 1..4 range */ - AT_RANK num_defined_parity; /* number of neighbors with defined parity <= num */ - /* AT_RANK num_undef_parity; */ - /* AT_RANK num_unkn_parity; */ - AT_RANK what2do; - U_CHAR cumulene_len; /* high nimble bits: (cumulene length - 1) */ - U_CHAR bond_type; /* *2 all same, not a real bond type */ -} STEREO_BOND_NEIGH; - -/* local prototypes */ -int SetHalfStereoBondIllDefPariy( sp_ATOM *at, int jn, /* atom number*/ int k1 /* stereo bond number*/, int new_parity ); -int RemoveHalfStereoBond( sp_ATOM *at, int jn, /* atom number*/ int k1 /* stereo bond number*/ ); -int SetKnownStereoBondParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, - const AT_RANK *nRank, const AT_RANK *nAtomNumber ); -int MarkKnownEqualStereoBondParities( sp_ATOM *at, int num_atoms, - const AT_RANK *nRank, const AT_RANK *nAtomNumber ); -int GetNextNeighborAndRank( sp_ATOM *at, AT_RANK cur, AT_RANK prev, AT_RANK *n, AT_RANK *cr, const AT_RANK *nCanonRank ); -int GetAndCheckNextNeighbors( sp_ATOM *at, AT_RANK cur1, AT_RANK prev1, AT_RANK cur2, AT_RANK prev2, - AT_RANK *n1, AT_RANK *n2, AT_RANK *nVisited1, AT_RANK *nVisited2, - const AT_RANK *nRank, const AT_RANK *nCanonRank ); -AT_RANK PathsHaveIdenticalKnownParities( sp_ATOM *at, AT_RANK prev1, AT_RANK cur1, AT_RANK prev2, AT_RANK cur2, - AT_RANK *nVisited1, AT_RANK *nVisited2, - const AT_RANK *nRank, const AT_RANK *nCanonRank, AT_RANK nLength ); -int RemoveKnownNonStereoBondParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, - const AT_RANK *nRank, CANON_STAT *pCS); -int SetKnownStereoCenterParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, - const AT_RANK *nRank, const AT_RANK *nAtomNumber ); -int RemoveKnownNonStereoCenterParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, - const AT_RANK *nRank, CANON_STAT *pCS); -int MarkKnownEqualStereoCenterParities( sp_ATOM *at, int num_atoms, - const AT_RANK *nRank, const AT_RANK *nAtomNumber ); - -/**********************************************************************************/ -/* Depth First Search for an atom with parity */ -int find_atoms_with_parity( sp_ATOM *at, S_CHAR *visited, int from_atom, int cur_atom ) -{ - int i, next_atom; - if ( visited[cur_atom] ) - return 0; - if ( at[cur_atom].parity ) - return 1; - visited[cur_atom] = 1; - for ( i = 0; i < at[cur_atom].valence; i ++ ) { - next_atom = at[cur_atom].neighbor[i]; - if ( next_atom != from_atom && find_atoms_with_parity( at, visited, cur_atom, next_atom ) ) - return 1; - } - return 0; -} -/**********************************************************************************/ -int SetHalfStereoBondIllDefPariy( sp_ATOM *at, int jn, /* atom number*/ int k1 /* stereo bond number*/, int new_parity ) -{ - int parity; - if ( k1 < MAX_NUM_STEREO_BOND_NEIGH && at[jn].stereo_bond_neighbor[k1] ) { - parity = at[jn].stereo_bond_parity[k1] ^ PARITY_VAL(at[jn].stereo_bond_parity[k1]); - at[jn].stereo_bond_parity[k1] = parity | PARITY_VAL(new_parity); - at[jn].parity = PARITY_VAL(new_parity); - return 1; /* success */ - } - return 0; /* failed */ -} - -/**********************************************************************************/ -int RemoveHalfStereoBond( sp_ATOM *at, int jn, /* atom number*/ int k1 /* stereo bond number*/ ) -{ - int k2; - if ( k1 < MAX_NUM_STEREO_BOND_NEIGH && at[jn].stereo_bond_neighbor[k1] ) { - for ( k2 = k1; k2+1 < MAX_NUM_STEREO_BOND_NEIGH; k2++ ) { - at[jn].stereo_bond_neighbor[k2] = at[jn].stereo_bond_neighbor[k2+1]; - at[jn].stereo_bond_ord[k2] = at[jn].stereo_bond_ord[k2+1]; - at[jn].stereo_bond_z_prod[k2] = at[jn].stereo_bond_z_prod[k2+1]; - at[jn].stereo_bond_parity[k2] = at[jn].stereo_bond_parity[k2+1]; - } - at[jn].stereo_bond_neighbor[k2] = 0; - at[jn].stereo_bond_ord[k2] = 0; - at[jn].stereo_bond_z_prod[k2] = 0; - at[jn].stereo_bond_parity[k2] = 0; - - if ( !at[jn].stereo_bond_neighbor[0] ) { /* curled braces added 6-6-2002 */ - at[jn].parity = 0; - at[jn].stereo_atom_parity = 0; - at[jn].final_parity = 0; - /* at[jn].bHasStereoOrEquToStereo = 0; */ - } - return 1; /* success */ - } - return 0; /* failed */ -} - -/**********************************************************************************/ -int SetOneStereoBondIllDefParity( sp_ATOM *at, int jc, /* atom number*/ int k /* stereo bond ord. number*/, int new_parity ) -{ - int k1, ret=0, kn, jn = (int)at[jc].stereo_bond_neighbor[k]-1; - - /* opposite end */ - for ( k1 = kn = ret = 0; k1 < MAX_NUM_STEREO_BOND_NEIGH && (kn=at[jn].stereo_bond_neighbor[k1]); k1++ ) { - if ( kn - 1 == jc ) { - ret = SetHalfStereoBondIllDefPariy( at, jn, /* atom number*/ k1 /* stereo bond number*/, new_parity ); - break; - } - } - if ( ret ) { - ret = SetHalfStereoBondIllDefPariy( at, jc, k, new_parity ); - } - return ret; -} - -/**********************************************************************************/ -int RemoveOneStereoBond( sp_ATOM *at, int jc, /* atom number*/ int k /* stereo bond number*/ ) -{ - int k1, ret=0, kn, jn = (int)at[jc].stereo_bond_neighbor[k]-1; - - /* opposite end */ - for ( k1 = kn = ret = 0; k1 < MAX_NUM_STEREO_BOND_NEIGH && (kn=at[jn].stereo_bond_neighbor[k1]); k1++ ) { - if ( kn - 1 == jc ) { - ret = RemoveHalfStereoBond( at, jn, k1 ); - break; - } - } - if ( ret ) { - ret = RemoveHalfStereoBond( at, jc, k ); - } - return ret; -} -/**********************************************************************************/ -int RemoveOneStereoCenter( sp_ATOM *at, int jc /* atom number*/ ) -{ - if ( at[jc].parity ) { - at[jc].parity = 0; /* remove parity */ - at[jc].stereo_atom_parity = 0; - at[jc].final_parity = 0; - /* at[jc].bHasStereoOrEquToStereo = 0; */ - return 1; - } - return 0; /* failed: not a stereo center */ -} -/**********************************************************************************/ -/* Remove stereo parity from centers having constitutionally equivalent */ -/* cut-vertex neighbors whose attachments do not have stereogenic elements. */ -/* Currently checks ALL constitutionally equivalent neighbors. */ -/* To optimize, check only one. */ -int UnmarkNonStereo( sp_ATOM *at, int num_atoms, const AT_RANK *nRank, const AT_RANK *nAtomNumber, int bIsotopic ) -{ - int i, i1, i2, j, k, k1, k2, kn /* neigh*/, val, ic/* center*/, jc, num_implicit_H; - int num_neighbors_with_parity, num_no_parity_atoms, num_removed_parities=-1, num_removed_parities0; - AT_RANK nNeighborNumber[MAX_NUM_STEREO_ATOM_NEIGH]; - AT_RANK nPrevAtomRank, nPrevNeighRank; - S_CHAR *visited = (S_CHAR *) inchi_malloc(num_atoms * sizeof(visited[0])); - if ( !visited ) - goto exit_function; - num_removed_parities = 0; - num_no_parity_atoms = 0; - - do { - num_removed_parities0 = num_removed_parities; - for ( i = i1 = 0, nPrevAtomRank = 0; i <= num_atoms; i++ ) { - /* bounds violation check (i!=num_atoms) added 6-21-2002 */ - if ( i == num_atoms || nPrevAtomRank != nRank[j = nAtomNumber[i]] - /* at[j].parity && 1 < at[j].valence && at[j].valence < MAX_NUM_STEREO_ATOM_NEIGH*/ ) { - - /* end of constitutionally equivalent atoms sequence */ - - /* nPrevRank = nRank[j]; */ - i2 = i; - if ( i2 - i1 > num_no_parity_atoms /*&& at[jc = nAtomNumber[i1]].parity*/ ) { - /* at[nAtomNumber[i1]]..at[nAtomNumber[i2-1]] are constitutionally equivalent and some of them have parity */ - jc = nAtomNumber[i1]; - num_no_parity_atoms = 0; - val = at[jc].valence; /* all equivalent atoms have equal valences, etc. (except parities) */ - num_implicit_H = at[jc].endpoint? 0 : at[jc].num_H; - /* Only atoms with valence <= MAX_NUM_STEREO_ATOM_NEIGH may have parity. However, check: */ - if ( val + num_implicit_H > MAX_NUM_STEREO_ATOM_NEIGH ) { - continue; /* program error ??? */ /* */ - } - for ( k = 0; k < val; k ++ ) { - nNeighborNumber[k] = k; /* initialize an array of indexes for sorting */ - } - /* check parities */ - for ( ic = i1; ic < i2; ic ++ ) { - jc = nAtomNumber[ic]; - /* sort neighbors according to their canon. equivalence ranks */ - pNeighborsForSort = at[jc].neighbor; - pn_RankForSort = nRank; - insertions_sort( nNeighborNumber, val, sizeof(nNeighborNumber[0]), CompNeighborsAT_NUMBER ); - num_neighbors_with_parity = -1; /* non-zero */ - for ( k = k1 = 0, nPrevNeighRank = 0; k <= val; k ++ ) { - if ( k == val || nPrevNeighRank != nRank[at[jc].neighbor[nNeighborNumber[k]]] ) { - k2 = k; - if ( k2 - k1 > 1 ) { - /* found 2 or more constitutionally equivalent neighbors */ - /* Check if they have only non-stereogenic neighbors */ - for ( kn = k1, num_neighbors_with_parity = 0; kn < k2; kn ++ ) { - memset( visited, 0, num_atoms * sizeof(visited[0])); - visited[jc] = 1; /* starting point; the only atom with parity */ - num_neighbors_with_parity += - find_atoms_with_parity( at, visited, jc, (int)at[jc].neighbor[nNeighborNumber[kn]] ); - } - } - if ( !num_neighbors_with_parity ) { - break; /* at[jc] cannot have defined parity */ - } - if ( k + 1 < val ) { - k1 = k; /* at least 2 more neighbors left */ - nPrevNeighRank = nRank[at[jc].neighbor[nNeighborNumber[k]]]; - } else { - break; - } - } - } - if ( num_implicit_H > 1 ) { - if ( bIsotopic && (at[jc].num_iso_H[0] > 1 || - at[jc].num_iso_H[1] > 1 || - at[jc].num_iso_H[2] > 1 ) || - num_implicit_H > NUM_H_ISOTOPES || - !bIsotopic - ) { - num_neighbors_with_parity = 0; - } - } - /* increment if: */ - /* (a) constitutionally equivalent neighbors do exist, and */ - /* (b) all constitutionally equivalent neighbors do not have parity, and */ - /* (c) all constitutionally equivalent neighbors are not connected to atoms with parity */ - num_no_parity_atoms += !num_neighbors_with_parity; - } - if ( num_no_parity_atoms == i2 - i1 ) { - /* all atoms at[nAtomNumber[i1]]..at[nAtomNumber[i2-1]] cannot be */ - /* stereo centers or belong to stereo bonds */ - for ( ic = i1; ic < i2; ic ++ ) { - int jn; - jc = nAtomNumber[ic]; - at[jc].parity = 0; /* remove parity */ - at[jc].stereo_atom_parity = 0; - at[jc].final_parity = 0; - at[jc].bHasStereoOrEquToStereo = 0; - /* remove stereo bonds */ - for ( k = 0; k < MAX_NUM_STEREO_BOND_NEIGH && (jn=at[jc].stereo_bond_neighbor[k]); k++ ) { - jn--; /* stereo bond neighbor */ - /* opposite end */ - for ( k1 = 0; k1 < MAX_NUM_STEREO_BOND_NEIGH && (kn=at[jn].stereo_bond_neighbor[k1]); k1++ ) { - if ( kn - 1 == jc ) { - RemoveHalfStereoBond( at, jn, k1 ); - break; - } - } - /* at at[jc] stereo bond end; since references to all at[jc] */ - /* stereo bond neighbors are to be removed, do not shift them */ - at[jc].stereo_bond_neighbor[k] = 0; - at[jc].stereo_bond_ord[k] = 0; - at[jc].stereo_bond_z_prod[k] = 0; - at[jc].stereo_bond_parity[k] = 0; - - } - - } - num_removed_parities += num_no_parity_atoms; - } - - } - if ( i < num_atoms ) { - nPrevAtomRank = nRank[j]; - i1 = i; - } - num_no_parity_atoms = 0; - } - num_no_parity_atoms += (i < num_atoms && !at[j].parity); - } - } while ( num_removed_parities != num_removed_parities0 ); - -exit_function: - if ( visited ) - inchi_free( visited ); - return num_removed_parities; -} -/********************************************************************************** - * - * Add stereo descriptor(s) for atom #i - * - **********************************************************************************/ -int FillSingleStereoDescriptors(sp_ATOM *at, int i, int num_trans, const AT_RANK *nRank - , AT_STEREO_CARB *LinearCTStereoCarb, int *nStereoCarbLen, int nMaxStereoCarbLen - , AT_STEREO_DBLE *LinearCTStereoDble, int *nStereoDbleLen, int nMaxStereoDbleLen - , int bAllene ) -{ - if ( !LinearCTStereoDble && !LinearCTStereoCarb ) { - return 0; /* return immediately if no stereo have been requested */ - } - - /*************************************************** - add stereo centers and stereo bonds to the CT - ***************************************************/ - if ( at[i].parity || at[i].stereo_bond_neighbor[0] ) { - AT_RANK r_neigh, rank = nRank[i]; - AT_NUMB nNeighborNumber2[MAXVAL]; - unsigned parity; - int k; - int num_allene = 0; - if ( ATOM_PARITY_WELL_DEF(at[i].parity) && num_trans < 0 ) { - /* number of neighbors transpositions to the sorted order is unknown. Find it. */ - /* If parity is not well-defined then doing this is a waste of time */ - int num_neigh = at[i].valence; - for ( k = 0; k < num_neigh; k ++) { - nNeighborNumber2[k] = k; - } - pNeighborsForSort = at[i].neighbor; - pn_RankForSort = nRank; - num_trans=insertions_sort( nNeighborNumber2, num_neigh, sizeof(nNeighborNumber2[0]), CompNeighborsAT_NUMBER ); -#ifndef CT_NEIGH_INCREASE - num_trans += ((num_neigh*(num_neigh-1))/2)%2; /* get correct parity for ascending order */ -#endif - } - - /* stereo bonds */ - if ( LinearCTStereoDble && at[i].stereo_bond_neighbor[0] ) { - - /* HalfStereoBondParity( sp_ATOM *at, int at_no1, int i_sb_neigh, AT_RANK *nRank ) */ - AT_NUMB nStereoNeighNumber[MAX_NUM_STEREO_BONDS], nStereoNeigh[MAX_NUM_STEREO_BONDS], n; - int num_stereo, stereo_neigh, stereo_neigh_ord, stereo_bond_parity; - for ( num_stereo = 0; - num_stereo < MAX_NUM_STEREO_BONDS && - (n=at[i].stereo_bond_neighbor[num_stereo]); num_stereo ++ ) { - nStereoNeighNumber[num_stereo] = num_stereo; - nStereoNeigh[num_stereo] = n-1; - num_allene += IS_ALLENE_CHAIN(at[i].stereo_bond_parity[num_stereo]); - } - if ( bAllene > 0 && !num_allene || bAllene == 0 && num_allene ) { - return 0; - } - /* sort stereo bonds according to the ranks of the neighbors */ - pNeighborsForSort = nStereoNeigh; - pn_RankForSort = nRank; - insertions_sort( nStereoNeighNumber, num_stereo, sizeof(nStereoNeighNumber[0]), CompNeighborsAT_NUMBER ); - - /* process stereo bonds one by one */ - for ( k = 0; k < num_stereo; k ++ ) { - stereo_neigh = nStereoNeigh[stereo_neigh_ord=(int)nStereoNeighNumber[k]]; - if ( (r_neigh = (AT_NUMB)nRank[stereo_neigh]) CT_NEIGH_SMALLER_THAN rank ) { - /* accept only neighbors that have smaller ranks */ - stereo_bond_parity = PARITY_VAL(at[i].stereo_bond_parity[stereo_neigh_ord]); - if ( stereo_bond_parity == AB_PARITY_NONE ) - continue; - - /* stereo_neigh = at[i].stereo_bond_neighbor[nStereoNeighNumber[k]]-1; */ - if ( ATOM_PARITY_KNOWN(stereo_bond_parity) ) { - parity = stereo_bond_parity; - } else - if ( ATOM_PARITY_WELL_DEF(at[i].parity) && - ATOM_PARITY_WELL_DEF(at[stereo_neigh].parity) && - MIN_DOT_PROD <= abs(at[i].stereo_bond_z_prod[stereo_neigh_ord]) ) { - /* bond parity can be calculated */ - int half_parity1, half_parity2, j, nn, stereo_neigh_ord2; - stereo_neigh_ord2 = -1; - for ( j = 0; j < MAX_NUM_STEREO_BONDS && - (nn=(int)at[stereo_neigh].stereo_bond_neighbor[j]); j++ ) { - if ( i+1 == nn ) { - /* found the opposite end of the stereo bond */ - stereo_neigh_ord2 = j; - break; - } - } - if ( stereo_neigh_ord2 >= 0 ) { - half_parity1 = HalfStereoBondParity( at, i, stereo_neigh_ord, nRank ); - half_parity2 = HalfStereoBondParity( at, stereo_neigh, stereo_neigh_ord2, nRank ); - if ( ATOM_PARITY_WELL_DEF(half_parity1) && - ATOM_PARITY_WELL_DEF(half_parity2) ) { - parity = 2 - ( half_parity1 + half_parity2 - + (at[i].stereo_bond_z_prod[stereo_neigh_ord] < 0))%2; - } else { - return CT_STEREOBOND_ERROR; /* */ - } - } else { - return CT_STEREOBOND_ERROR; /* */ - } - } else { - /* parity cannot be calculated: not enough info or 'unknown' */ - if ( AB_PARITY_NONE == (parity = inchi_max(at[i].parity, at[stereo_neigh].parity)) ) - continue; - if ( ATOM_PARITY_WELL_DEF(parity) ) - parity = AB_PARITY_UNDF; /* should not happen */ - } - if ( CHECK_OVERFLOW(*nStereoDbleLen, nMaxStereoDbleLen) ) - return CT_OVERFLOW; /* */ - /* first stereo bond atom */ - LinearCTStereoDble[*nStereoDbleLen].at_num1 = rank; - /* second stereo bond atom (opposite end) */ - LinearCTStereoDble[*nStereoDbleLen].at_num2 = r_neigh; - /* bond parity */ - LinearCTStereoDble[*nStereoDbleLen].parity = parity; - (*nStereoDbleLen) ++; - } - } - } - - - /* stereo carbon */ - if ( bAllene > 0 ) { - return 0; - } - if ( LinearCTStereoCarb && !at[i].stereo_bond_neighbor[0] ) { - if ( CHECK_OVERFLOW(*nStereoCarbLen, nMaxStereoCarbLen) ) - return CT_OVERFLOW; /* */ - /* stereo atom rank */ - LinearCTStereoCarb[*nStereoCarbLen].at_num = rank; - /* stereo atom parity */ - parity = ATOM_PARITY_WELL_DEF(at[i].parity)? (2 - (at[i].parity + num_trans)%2) : at[i].parity; - LinearCTStereoCarb[*nStereoCarbLen].parity = parity; - (*nStereoCarbLen) ++; - } - - } - - return 0; -} -/**********************************************************************************/ -void SwitchAtomStereoAndIsotopicStereo( sp_ATOM *at, int num_atoms, int *bSwitched ) -{ - int i; - /* switch atom stereo data */ - for ( i = 0; i < num_atoms; i ++ ) { - inchi_swap( (char*)&at[i].parity, (char*)&at[i].parity2, sizeof( at[i].parity ) ); - inchi_swap( (char*)&at[i].final_parity, (char*)&at[i].final_parity2, sizeof( at[i].final_parity ) ); - inchi_swap( (char*)&at[i].stereo_atom_parity, (char*)&at[i].stereo_atom_parity2, sizeof( at[i].stereo_atom_parity ) ); - inchi_swap( (char*)&at[i].bHasStereoOrEquToStereo, (char*)&at[i].bHasStereoOrEquToStereo2, sizeof( at[i].bHasStereoOrEquToStereo ) ); - - inchi_swap( (char*)at[i].stereo_bond_neighbor, (char*)at[i].stereo_bond_neighbor2, sizeof( at[i].stereo_bond_neighbor ) ); - inchi_swap( (char*)at[i].stereo_bond_ord, (char*)at[i].stereo_bond_ord2, sizeof( at[i].stereo_bond_ord ) ); - inchi_swap( (char*)at[i].stereo_bond_z_prod, (char*)at[i].stereo_bond_z_prod2, sizeof( at[i].stereo_bond_z_prod ) ); - inchi_swap( (char*)at[i].stereo_bond_parity, (char*)at[i].stereo_bond_parity2, sizeof( at[i].stereo_bond_parity ) ); - } - *bSwitched = !*bSwitched; -} -/**********************************************************************************/ -void SetCtToIsotopicStereo( CANON_STAT *pCS, CANON_STAT *pCS2 ) -{ - pCS->LinearCTStereoDble = pCS2->LinearCTIsotopicStereoDble; /* enable stereo */ - pCS->LinearCTStereoCarb = pCS2->LinearCTIsotopicStereoCarb; - - pCS->LinearCTStereoDbleInv = pCS2->LinearCTIsotopicStereoDbleInv; /* enable inv. stereo */ - pCS->LinearCTStereoCarbInv = pCS2->LinearCTIsotopicStereoCarbInv; - pCS->nMaxLenLinearCTStereoDble = pCS2->nMaxLenLinearCTIsotopicStereoDble; - pCS->nMaxLenLinearCTStereoCarb = pCS2->nMaxLenLinearCTIsotopicStereoCarb; - - pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTIsotopicStereoDble; - pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTIsotopicStereoCarb; -} -/**********************************************************************************/ -void SetCtToNonIsotopicStereo( CANON_STAT *pCS, CANON_STAT *pCS2 ) -{ - pCS->LinearCTStereoDble = pCS2->LinearCTStereoDble; /* enable stereo */ - pCS->LinearCTStereoCarb = pCS2->LinearCTStereoCarb; - - pCS->LinearCTStereoDbleInv = pCS2->LinearCTStereoDbleInv; /* enable inv. stereo */ - pCS->LinearCTStereoCarbInv = pCS2->LinearCTStereoCarbInv; - pCS->nMaxLenLinearCTStereoDble = pCS2->nMaxLenLinearCTStereoDble; - pCS->nMaxLenLinearCTStereoCarb = pCS2->nMaxLenLinearCTStereoCarb; - - pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTStereoDble; - pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTStereoCarb; - - pCS->nLenLinearCTIsotopicStereoDble = pCS2->nLenLinearCTIsotopicStereoDble; - pCS->nLenLinearCTIsotopicStereoCarb = pCS2->nLenLinearCTIsotopicStereoCarb; -} - -/**********************************************************************************/ -int FillAllStereoDescriptors( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, const AT_RANK *nAtomNumberCanon, CANON_STAT *pCS ) -{ - int ret=0, i; - /* initialize zero lengths */ - pCS->nLenLinearCTStereoCarb = 0; - pCS->nLenLinearCTStereoDble = 0; - - /* fill atom by atom */ - for ( i = 0; !ret && i < num_atoms; i ++ ) { - ret = FillSingleStereoDescriptors( at, (int)nAtomNumberCanon[i], -1, nCanonRank - , pCS->LinearCTStereoCarb, &pCS->nLenLinearCTStereoCarb, pCS->nMaxLenLinearCTStereoCarb - , pCS->LinearCTStereoDble, &pCS->nLenLinearCTStereoDble, pCS->nMaxLenLinearCTStereoDble - , 0 /* bAllene */ ); - } - for ( i = 0; !ret && i < num_atoms; i ++ ) { - ret = FillSingleStereoDescriptors( at, (int)nAtomNumberCanon[i], -1, nCanonRank - , pCS->LinearCTStereoCarb, &pCS->nLenLinearCTStereoCarb, pCS->nMaxLenLinearCTStereoCarb - , pCS->LinearCTStereoDble, &pCS->nLenLinearCTStereoDble, pCS->nMaxLenLinearCTStereoDble - , 1 /* bAllene */); - } - - return ret; -} -/**********************************************************************************/ -/* Find stereo bond parities known in advance */ -int SetKnownStereoBondParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, - const AT_RANK *nRank, const AT_RANK *nAtomNumber ) -{ - int i, j, n, m, j1, k, num_neigh1, num_neigh2, iMax1, parity; - int trans_i1, trans_i2, trans_k1, trans_k2, prev_trans, trans_k, num_set; - int i1, i2, k1, k2, n1, n2, m1, m2, /*stereo_bond_parity,*/ cumulene_len; - AT_RANK nAtomRank1, nAtomRank2, nAtom1NeighRank; - AT_RANK nNeighRank1[MAX_NUM_STEREO_BONDS], nNeighRank2[MAX_NUM_STEREO_BONDS]; - AT_RANK nNeighCanonRank1[MAX_NUM_STEREO_BONDS], nNeighCanonRank2[MAX_NUM_STEREO_BONDS]; - for ( i1 = 0, num_set = 0; i1 < num_atoms; i1 ++ ) { - if ( !at[i1].parity || !at[i1].stereo_bond_neighbor[0] ) { - continue; - } - - if ( !PARITY_WELL_DEF(at[i1].parity) ) { - continue; - } - nAtomRank1 = nRank[i1]; - iMax1 = (int)nAtomRank1-1; - num_neigh1 = at[i1].valence; - for ( n1 = 0; n1 < MAX_NUM_STEREO_BONDS && (i2=(int)at[i1].stereo_bond_neighbor[n1]); n1++ ) { - i2 --; - /* found a stereo bond at[i1]-at[i2] adjacent to at[i1] */ - for ( n2 = 0, m=0; n2 < MAX_NUM_STEREO_BONDS && (m=(int)at[i2].stereo_bond_neighbor[n2]) && m-1 != i1; n2++ ) - ; /* locate stereo bond (#n2) at the opposite atom at[i2] */ - if ( m-1 != i1 || at[i1].stereo_bond_parity[n1] != at[i2].stereo_bond_parity[n2] ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - if ( i1 < i2 ) { - continue; /* do not process same bond 2 times */ - } - - if ( PARITY_KNOWN(at[i1].stereo_bond_parity[n1]) || !PARITY_VAL(at[i1].stereo_bond_parity[n1]) ) { - continue; - } - if ( !PARITY_WELL_DEF(at[i1].parity) || !PARITY_WELL_DEF(at[i2].parity) ) { - continue; - } - if ( PARITY_VAL(at[i1].stereo_bond_parity[n1]) != AB_PARITY_CALC ) { - continue; /* ?? program error ?? should not happen */ /* */ - } - /*stereo_bond_parity = PARITY_VAL(at[i1].stereo_bond_parity[n1]);*/ - cumulene_len = BOND_CHAIN_LEN(at[i1].stereo_bond_parity[n1]); - nAtomRank2 = nRank[i2]; - nAtom1NeighRank = nRank[(int)at[i1].neighbor[(int)at[i1].stereo_bond_ord[n1]]]; - num_neigh2 = at[i2].valence; - /* store ranks of at[i1] stereo bond neighbors except one connected by a stereo bond */ - k = (int)at[i1].stereo_bond_ord[n1]; - trans_i1 = 0; - for ( i = j = 0; i < num_neigh1; i ++ ) { - if ( i != k ) { - nNeighRank1[j] = nRank[(int)at[i1].neighbor[i]]; - j ++; - } - } - if ( j == 2 ) { - if ( nNeighRank1[0] == nNeighRank1[1] ) { - /* neighbors are constitutionally identical, can't find bond parity */ - continue; - } - trans_i1 = insertions_sort(nNeighRank1, j, sizeof(nNeighRank1[0]), comp_AT_RANK); - } - /* store ranks of at[i2] stereo bond neighbors except one connected by a stereo bond */ - k = (int)at[i2].stereo_bond_ord[n2]; - trans_i2 = 0; - for ( i = j = 0; i < num_neigh2; i ++ ) { - if ( i != k ) { - nNeighRank2[j] = nRank[(int)at[i2].neighbor[i]]; - j ++; - } - } - if ( j == 2 ) { - if ( nNeighRank2[0] == nNeighRank2[1] ) { - /* neighbors are constitutionally identical, can't find bond parity */ - continue; - } - trans_i2 = insertions_sort(nNeighRank2, j, sizeof(nNeighRank2[0]), comp_AT_RANK); - } - prev_trans = -1; - trans_k1 = -2; - trans_k = -4; /* 2004-04-28 */ - /* find all pairs of atoms that can be mapped on at[i1], at[i2] pair */ - for ( j1 = 0; j1 <= iMax1 && nAtomRank1==nRank[k1=(int)nAtomNumber[iMax1-j1]]; j1 ++ ) { - /* at[k1] is constitutionally equivalent to at[i1] */ - /* find all at[k1] neighbors that have rank nAtomRank2; */ - /* then find at[k2] constitutionally equivalent at at[i2] */ - if ( at[k1].valence != num_neigh1 ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - for ( m1 = 0; m1 < num_neigh1; m1 ++ ) { - int prev, next, len; - if ( nAtom1NeighRank != nRank[k2=(int)at[k1].neighbor[m1]] ) { - continue; - } - m2 = -1; /* undefined yet */ - prev = k1; - len = 0; - if ( cumulene_len ) { - for ( len=0, next = (int)at[k1].neighbor[m1]; len < cumulene_len; len ++ ) { - if ( at[next].valence == 2 && !at[next].num_H ) { - j = ((int)at[next].neighbor[0] == prev); - prev = next; - next = at[next].neighbor[j]; - } else { - break; /* cannot continue */ - } - } - if ( len != cumulene_len || nAtomRank2 != nRank[next] ) { - continue; /* not found */ - } - k2 = next; - } - if ( at[k2].valence != num_neigh2 ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - /* store canon. ranks of at[k1] neighbors */ /* use i,j,k,m,n */ - for ( n = j = 0; n < num_neigh1; n ++ ) { - if ( n != m1 ) { - i=(int)at[k1].neighbor[n]; - for ( m = 0; m < num_neigh1-1; m ++ ) { - if ( nRank[i] == nNeighRank1[m] ) { - nNeighCanonRank1[m] = nCanonRank[i]; - j ++; - break; - } - } - } - } - if ( j != num_neigh1-1 ) { - return CT_STEREOCOUNT_ERR; /* */ - } - if ( j == 2 ) { - trans_k1 = insertions_sort(nNeighCanonRank1, j, sizeof(nNeighCanonRank1[0]), comp_AT_RANK); - } else { - trans_k1 = 0; - } - /* store canon. ranks of at[k2] neighbors */ /* use i,j,k,m,n */ - for ( n = j = 0; n < num_neigh2; n ++ ) { - i=(int)at[k2].neighbor[n]; - if ( i == prev ) { /* neighbor belongs to the stereobond */ - m2 = n; - } else { - for ( m = 0; m < num_neigh2-1; m ++ ) { - if ( nRank[i] == nNeighRank2[m] ) { - nNeighCanonRank2[m] = nCanonRank[i]; - j ++; - break; - } - } - } - } - if ( j != num_neigh2-1 || m2 < 0 ) { - return CT_STEREOCOUNT_ERR; /* */ - } - if ( j == 2 ) { - trans_k2 = insertions_sort(nNeighCanonRank2, j, sizeof(nNeighCanonRank2[0]), comp_AT_RANK); - } else { - trans_k2 = 0; - } - trans_k = (trans_k1 + trans_k2)%2; - if ( prev_trans < 0 ) { - prev_trans = trans_k; - } else - if ( prev_trans != trans_k ) { /* was != trans_k1, changed 9-23-2003 */ - break; /* different number of transpositions */ - } - } /* end of the second atom mapping cycle */ - if ( prev_trans >= 0 && prev_trans != trans_k ) { /* was != trans_k1, changed 9-23-2003 */ - break; - } - } /* end of the first atom mapping cycle */ - if ( prev_trans == trans_k ) { /* was == trans_k1, changed 9-23-2003 */ - int z_prod; - /* all mappings of canonical numbers on the */ - /* stereo bond at[i1]-at[i2] produce equivalent numberings. */ - /* Therefore the stereo bond parity is known at this time. */ - /* parity_1 = at[i1].parity + (trans_i1 + trans_k1 + num_neigh1 - 1) + (int)at[i1].stereo_bond_ord[n1] */ - /* expression in parentheses is equivalent to rank[first neigh] > rank[second neigh] */ - /* same for parity_2. */ - /* parity_2 = at[i2].parity + (trans_i2 + trans_k2 + num_neigh2 - 1) + (int)at[i2].stereo_bond_ord[n2] */ - /* Sum of the two parities (without stereo_bond_z_prod) is: */ - parity = (at[i1].parity + at[i2].parity + prev_trans + trans_i1 + trans_i2 - + num_neigh1 + num_neigh2 - + (int)at[i1].stereo_bond_ord[n1] + (int)at[i2].stereo_bond_ord[n2] ) %2; - z_prod = at[i1].stereo_bond_z_prod[n1]; - if ( MIN_DOT_PROD > abs(z_prod)) { - parity = AB_PARITY_UNDF; /* undefined because of geometry */ - } else { - parity = (z_prod > 0)? 2 - parity : 1 + parity; - } - at[i1].stereo_bond_parity[n1] = ALL_BUT_PARITY(at[i1].stereo_bond_parity[n1]) | parity; - at[i2].stereo_bond_parity[n2] = ALL_BUT_PARITY(at[i2].stereo_bond_parity[n2]) | parity; - num_set ++; - } - } - } - return num_set; -} -/**********************************************************************************/ -/* Find stereo center parities known in advance */ -int MarkKnownEqualStereoBondParities( sp_ATOM *at, int num_atoms, - const AT_RANK *nRank, const AT_RANK *nAtomNumber ) -{ - int j, n, m, j1, num_neigh1, num_neigh2, iMax1; - int num_set, /*num_sb1, num_sb2,*/ bDifferentParities; - int i1, i2, k1, k2, n1, n2, m1, m2, s1, s2, stereo_bond_parity, stereo_bond_parity2, cumulene_len; - AT_RANK nAtomRank1, nAtomRank2, nAtom1NeighRank, nAtom2NeighRank; - num_set = 0; - for ( i1 = 0, num_set = 0; i1 < num_atoms; i1 ++ ) { - if ( !at[i1].parity || !at[i1].stereo_bond_neighbor[0] ) { - continue; - } - nAtomRank1 = nRank[i1]; - iMax1 = (int)nAtomRank1-1; - num_neigh1 = at[i1].valence; - /* count stereogenic bonds adjacent to at[i1] */ - for ( n1 = 0; n1 < MAX_NUM_STEREO_BONDS && at[i1].stereo_bond_neighbor[n1]; n1++ ) - ; - /*num_sb1 = n1;*/ - /* search for bonds possibly constitutionally equivalent to each of the adjacent bonds */ - /* and find if all of them have same already known parity */ - for ( n1 = 0, i2 = 0; n1 < MAX_NUM_STEREO_BONDS && (i2=(int)at[i1].stereo_bond_neighbor[n1]); n1++ ) { - i2 --; - nAtomRank2 = nRank[i2]; - if ( nAtomRank2 < nAtomRank1 || nAtomRank2 == nAtomRank1 && i1 < i2 ) { - /* An attempt to reduce unnecessary repetitions. */ - /* We still have repetitions because we do not accumulate a list of */ - /* processed (nAtomRank2, nAtomRank1) pairs. */ - continue; - } - bDifferentParities = -1; /* parities have not been compared yet */ - /* found a stereo bond at[i1]-at[i2] (adjacent to at[i1]) */ - /* - if ( !PARITY_KNOWN(at[i1].stereo_bond_parity[n1]) || (at[i1].stereo_bond_parity[n1] & KNOWN_PARITIES_EQL) ) { - continue; - } - */ - if ( at[i1].stereo_bond_parity[n1] & KNOWN_PARITIES_EQL ) { - continue; - } - /* stereo bond has known or unknown parity; we have not checked it yet */ - for ( n2 = 0; n2 < MAX_NUM_STEREO_BONDS && at[i2].stereo_bond_neighbor[n2]; n2++ ) - ; - /*num_sb2 = n2;*/ - for ( n2 = 0, m = 0; n2 < MAX_NUM_STEREO_BONDS && (m=(int)at[i2].stereo_bond_neighbor[n2]) && m-1 != i1; n2++ ) - ; - if ( m-1 != i1 || at[i1].stereo_bond_parity[n1] != at[i2].stereo_bond_parity[n2] ) { - return CT_STEREOCOUNT_ERR; /* program error: stereo bonds data in two directions are different */ /* */ - } - stereo_bond_parity = PARITY_VAL(at[i1].stereo_bond_parity[n1]); - cumulene_len = BOND_CHAIN_LEN(at[i1].stereo_bond_parity[n1]); - nAtom1NeighRank = nRank[(int)at[i1].neighbor[(int)at[i1].stereo_bond_ord[n1]]]; - nAtom2NeighRank = nRank[(int)at[i2].neighbor[(int)at[i2].stereo_bond_ord[n2]]]; - num_neigh2 = at[i2].valence; - /* find all pairs of atoms that possibly can be mapped on at[i1], at[i2] pair */ - /* (we may also find pairs that cannot be mapped, but we cannot miss any pair */ - /* that can be mapped) */ - for ( j1 = 0; j1 <= iMax1 && nAtomRank1==nRank[k1=(int)nAtomNumber[iMax1-j1]]; j1 ++ ) { - /* at[k1] is constitutionally equivalent to at[i1] */ - /* find all at[k1] stereo bond neighbors at[k2] that have rank nAtomRank2; */ - /* then find at[k2] constitutionally equivalent at at[i2] */ - if ( at[k1].valence != num_neigh1 ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - if ( !at[k1].bHasStereoOrEquToStereo ) { - at[k1].bHasStereoOrEquToStereo = 1; - } - /* -- do not check number of stereo bonds, check bonds themselves -- - for ( s1 = 0; s1 < MAX_NUM_STEREO_BONDS && at[k1].stereo_bond_neighbor[s1]; s1++ ) - ; - if ( num_sb1 != s1 ) { - bDifferentParities = 1; - } - */ - for ( m1 = 0; m1 < num_neigh1; m1 ++ ) { - /* Looking for at[k1] neighbor with nRank=nAtom1NeighRank. */ - /* This neighbor may be on the bond constit. equivalent to at[i1]-at[i2] stereo bond */ - /* (or may be constit. equivalent an adjacent to at[i1] atom in a stereogenic cumulene chain) */ - int prev, next, len; - if ( nAtom1NeighRank != nRank[k2=(int)at[k1].neighbor[m1]] ) { - continue; - } - /* found at[k1] neighbor with nRank=nAtom1NeighRank */ - m2 = -1; /* undefined yet */ - prev = k1; - len = 0; - /* if cumulene then bypass the cumulene chain */ - if ( cumulene_len ) { - for ( len=0, next = (int)at[k1].neighbor[m1]; len < cumulene_len; len ++ ) { - if ( at[next].valence == 2 && !at[next].num_H ) { - j = ((int)at[next].neighbor[0] == prev); - prev = next; - next = at[next].neighbor[j]; - } else { - break; /* cannot continue: end of cumulene chain */ - } - } - if ( len != cumulene_len || nAtomRank2 != nRank[next] ) { - continue; /* cumulene chain not found at this neighbor */ - } - if ( nAtom2NeighRank != nRank[prev] ) { - /* continue; */ /* ??? program error ??? If not, must be a very rare event */ - return CT_STEREOCOUNT_ERR; /* */ - } - k2 = next; - } - - /* a connected pair of constit. equivalent atoms found */ - if ( at[k2].valence != num_neigh2 ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - for ( n = 0; n < num_neigh2; n ++ ) { - if ( prev == (int)at[k2].neighbor[n] ) { - m2 = n; /* found bond from the opposite end of a possibly stereogenic bond */ - break; - } - } - if ( m2 < 0 ) { - return CT_STEREOCOUNT_ERR; /* program error: opposite direction bond not found */ /* */ - } - if ( !at[k2].bHasStereoOrEquToStereo ) { - at[k2].bHasStereoOrEquToStereo = 1; - } - - /* check if atoms at[k1] and at[k2] are connected by a stereo bond */ - for ( s1 = 0, m = 0; s1 < MAX_NUM_STEREO_BONDS && (m=(int)at[k1].stereo_bond_neighbor[s1]) && m-1 != k2; s1++ ) - ; - if ( m-1 != k2 ) { - bDifferentParities = 1; /* cannot find the stereo bond */ - at[k1].bHasStereoOrEquToStereo = - at[k2].bHasStereoOrEquToStereo = 2; - continue; - } - /* -- do not check number of stereo bonds, check bonds themselves -- - for ( s2 = 0; s2 < MAX_NUM_STEREO_BONDS && at[k2].stereo_bond_neighbor[s2]; s2++ ) - ; - if ( num_sb2 != s2 ) { - bDifferentParities = 1; - continue; - } - */ - for ( s2 = 0, m = 0; s2 < MAX_NUM_STEREO_BONDS && (m=(int)at[k2].stereo_bond_neighbor[s2]) && m-1 != k1; s2++ ) - ; - if ( m-1 != k1 ) { - /* - bDifferentParities = 1; // cannot find the stereo bond - continue; - */ - return CT_STEREOCOUNT_ERR; /* program error: opposite direction bond not found */ /* */ - } - if ( at[k1].stereo_bond_parity[s1] != at[k2].stereo_bond_parity[s2] ) { - bDifferentParities = 1; - continue; - } - stereo_bond_parity2 = PARITY_VAL(at[k1].stereo_bond_parity[s1]); - if ( stereo_bond_parity2 != stereo_bond_parity ) { - bDifferentParities = 1; - continue; - } - if ( stereo_bond_parity2 == stereo_bond_parity && bDifferentParities < 0 ) { - bDifferentParities = 0; - } - } - } - /* mark equal parities */ - if ( 0 == bDifferentParities && PARITY_KNOWN( stereo_bond_parity ) ) { - for ( j1 = 0; j1 <= iMax1 && nAtomRank1==nRank[k1=(int)nAtomNumber[iMax1-j1]]; j1 ++ ) { - /* at[k1] is constitutionally equivalent to at[i1] */ - for ( s1 = 0, k2 = 0; s1 < MAX_NUM_STEREO_BONDS && (k2=(int)at[k1].stereo_bond_neighbor[s1]); s1++ ) { - k2--; - if ( nRank[k2] == nAtomRank2 ) { - int b1, b2; - for ( s2 = 0, m = 0; s2 < MAX_NUM_STEREO_BONDS && (m=(int)at[k2].stereo_bond_neighbor[s2]) - && m-1 != k1; s2++ ) - ; - if ( m-1 != k1 ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - /* mark the stereo bonds */ - b1 = !(at[k1].stereo_bond_parity[s1] & KNOWN_PARITIES_EQL); - b2 = !(at[k2].stereo_bond_parity[s2] & KNOWN_PARITIES_EQL); - if ( 2 == b1 + b2 ) { - at[k1].stereo_bond_parity[s1] |= KNOWN_PARITIES_EQL; - at[k2].stereo_bond_parity[s2] |= KNOWN_PARITIES_EQL; - num_set ++; - } else - if ( b1 || b2 ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - } - } - } - } - } - } - return num_set; -} -#if ( REMOVE_KNOWN_NONSTEREO == 1 ) /* { */ -/**********************************************************************************/ -/* Return next atom number (and its canon. rank) on the path prev->cur->next */ -/* in order of ascending canonical ranks of the next atoms: *cr(output) > *cr(input) */ -/* To start the sequence let *cr=0 */ -/* If no more neighbors available the return value = 0; if successgul then the return value = 1. */ -int GetNextNeighborAndRank( sp_ATOM *at, AT_RANK cur, AT_RANK prev, AT_RANK *n, AT_RANK *cr, const AT_RANK *nCanonRank ) -{ - int i, val; - AT_RANK cr1 = MAX_ATOMS+1, j, j1=MAX_ATOMS+1, crj; - - for ( i = 0, val = at[(int)cur].valence; i < val; i ++ ) { - if ( (j=at[cur].neighbor[i]) != prev && - cr1 > (crj=nCanonRank[(int)j]) && crj > *cr ) { - cr1 = crj; - j1 = j; - } - } - if ( cr1 <= MAX_ATOMS ) { - *cr = cr1; - *n = (AT_RANK)j1; - return 1; - } - return 0; /* program error */ /* */ -} - -/**********************************************************************************/ -/* Find next pair of neighbors having the next greater canon. rank */ -/* The neighbors should be constitutionally identical and traversed simultaneouly or not traversed at all */ -/* If a bond cur1-*n1 or cur2-*n2 is a stereo bond then reject if their stereo bond parities are different or */ -/* cannot be calculated without breaking ties. */ -int GetAndCheckNextNeighbors( sp_ATOM *at, AT_RANK cur1, AT_RANK prev1, AT_RANK cur2, AT_RANK prev2, - AT_RANK *n1, AT_RANK *n2, AT_RANK *nVisited1, AT_RANK *nVisited2, - const AT_RANK *nRank, const AT_RANK *nCanonRank ) -{ - AT_RANK cr1, cr2, s1, s2; - int i1, i2, k1, k2; - cr1 = ( *n1 > MAX_ATOMS )? 0 : nCanonRank[(int)*n1]; - cr2 = ( *n2 > MAX_ATOMS )? 0 : nCanonRank[(int)*n2]; - if ( !GetNextNeighborAndRank( at, cur1, prev1, n1, &cr1, nCanonRank ) || - !GetNextNeighborAndRank( at, cur2, prev2, n2, &cr2, nCanonRank ) || - nRank[(int)*n1] != nRank[(int)*n2] || nVisited1[(int)*n1] != nVisited2[(int)*n2] ) { - return 0; /* program error; no breakpoint here */ /* */ - } - - /* Even though the bond or cumulene might have already been checked, check it: this is */ - /* the only place we can check stereo bonds and cumulenes that are not edges of the DFS tree */ - /* The code works both for a stereo bond and a stereogenic cumulene. */ - for ( i1 = 0, k1 = 0; i1 < MAX_NUM_STEREO_BONDS && - (s1=at[cur1].stereo_bond_neighbor[i1]) && - !(k1=(at[cur1].neighbor[(int)at[cur1].stereo_bond_ord[i1]] == *n1)); i1 ++ ) - ; - for ( i2 = 0, k2 = 0; i2 < MAX_NUM_STEREO_BONDS && - (s2=at[cur2].stereo_bond_neighbor[i2]) && - !(k2=(at[cur2].neighbor[(int)at[cur2].stereo_bond_ord[i2]] == *n2)); i2 ++ ) - ; - if ( k1 != k2 ) { - return 0; /* possibly not an error: constit. equivalent atoms on a stereo bond and not on a stereo bond */ - } - if ( k1 /* yes, it is a stero bond */ && - ( at[cur1].stereo_bond_parity[i1] != at[cur2].stereo_bond_parity[i2] || - /* PARITY_KNOWN (at[cur1].stereo_bond_parity[i1] ) */ /* replaced 08-13-2002 with the next: */ - !PARITY_WELL_DEF (at[cur1].stereo_bond_parity[i1] ) /* it suffices to check only one parity */ - ) ) { - return 0; /* different or (currently) unknown stereo bond parities */ - } - return 1; /* stereo bonds have known parities */ -} - -/**********************************************************************************/ -/* Simultaneously DFS-traverse 2 paths starting at the bonds prev1->cur1 and prev2->cur2 */ -/* The two paths MUST go through the pairs of constitutionally identical atoms, each atom being on one path. */ -/* Reject if encountered atoms having currently unknown (without breaking ties) */ -/* parities or having different known or unknown or undefined parities. */ -/* Save length of the path into nVisited[cur. atom number]. */ -/* Only one nVisited[] array is sufficient because the paths from the beginning are in different ring systems. */ -AT_RANK PathsHaveIdenticalKnownParities( sp_ATOM *at, AT_RANK prev1, AT_RANK cur1, AT_RANK prev2, AT_RANK cur2, - AT_RANK *nVisited1, AT_RANK *nVisited2, - const AT_RANK *nRank, const AT_RANK *nCanonRank, AT_RANK nLength ) -{ - int k; - AT_RANK n1, n2; - - nLength ++; /* number of successfully traversed pairs of atoms */ - nVisited1[cur1] = nLength; - nVisited2[cur2] = nLength; - /* the atoms must be either both stereogenic and have well-defined parities or non-stereogenic at all. */ - if ( at[cur1].stereo_atom_parity != at[cur2].stereo_atom_parity || - at[cur1].stereo_atom_parity && !PARITY_WELL_DEF (at[cur1].stereo_atom_parity) - ) { - return 0; /* Reject: Different or unknown in advance parities */ - } - - if ( at[cur1].valence != at[cur2].valence ) { - return 0; /* program error */ /* */ - } - if ( at[cur1].valence == 1 ) { - return nLength; /* so far success */ - } - - - for ( k = 1, n1 = MAX_ATOMS+1, n2=MAX_ATOMS+1; k < at[cur1].valence; k ++ ) { - /* start from 1: since we do not go back, we have only (at[cur1].valence-1) bonds to try */ - if ( !GetAndCheckNextNeighbors( at, cur1, prev1, cur2, prev2, - &n1, &n2, nVisited1, nVisited2, nRank, nCanonRank ) ) { - return 0; /* different neighbors */ - } - /* In a DFS we do not traverse already visited atoms */ - if ( !nVisited1[n1] ) { /* recursion */ - if ( ! (nLength = PathsHaveIdenticalKnownParities( at, cur1, n1, cur2, n2, nVisited1, nVisited2, nRank, nCanonRank, nLength ) ) ) { - return 0; - } - } - } - /* To be on a safe side, recheck after all nVisited[] have been set */ - for ( k = 1, n1 = MAX_ATOMS+1, n2=MAX_ATOMS+1; k < at[cur1].valence; k ++ ) { - /* start from 1: since we do not go back, we have only (at[cur1].valence-1) bonds to try */ - if ( !GetAndCheckNextNeighbors( at, cur1, prev1, cur2, prev2, - &n1, &n2, nVisited1, nVisited2, nRank, nCanonRank ) ) { - return 0; /* different neighbors */ - } - } - - - return nLength; - -} - -/**********************************************************************************/ -/* Remove stereo marks from the bonds that are known to be non-stereo */ -/* (compare neighbors if they are attached by cut-edges) */ -int RemoveKnownNonStereoBondParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, - const AT_RANK *nRank, CANON_STAT *pCS) -{ - int j, n, m, ret; - - int i1, n1, s2; - AT_RANK nAtomRank1, nAtomRank2, neigh[3], opposite_atom, *nVisited = NULL; - ret = 0; - for ( i1 = 0; i1 < num_atoms; i1 ++ ) { - if ( at[i1].valence != 3 || !at[i1].stereo_bond_neighbor[0] ) { - continue; - } - for ( n1 = 0; n1 < MAX_NUM_STEREO_BONDS && (s2=at[i1].stereo_bond_neighbor[n1]); n1++ ) { - if ( !PARITY_CALCULATE(at[i1].stereo_bond_parity[n1]) && PARITY_WELL_DEF(at[i1].stereo_bond_parity[n1]) ) { - continue; - } - opposite_atom = (AT_RANK)(s2-1); - /* s2 = at[i1].neighbor[m=(int)at[i1].stereo_bond_ord[n1]]; */ - m=(int)at[i1].stereo_bond_ord[n1]; - for ( j = 0, n = 0; j < at[i1].valence; j ++ ) { - /* if ( at[i1].neighbor[j] != s2 ) */ - if ( j != m ) - { - neigh[n++] = at[i1].neighbor[j]; - } - } - if ( n > 2 ) { - ret = CT_STEREOBOND_ERROR; /* */ - goto exit_function; - } - if ( n != 2 || nRank[(int)neigh[0]] != nRank[(int)neigh[1]] ) { - continue; /* may happen if another half-bond has not a defined parity */ - } - if ( at[i1].nRingSystem == at[(int)neigh[0]].nRingSystem ) { - continue; /* no more ring system membership check is necessary because */ - } /* the two neighbors are to be constitutionally equivalent atoms */ - if ( !nVisited && !(nVisited = (AT_RANK*) inchi_malloc( sizeof(nVisited[0])*num_atoms ) ) ) { - ret = CT_OUT_OF_RAM; /* */ - goto exit_function; - } - memset( nVisited, 0, sizeof(nVisited[0])*num_atoms ); - nVisited[i1] = 1; - if ( PathsHaveIdenticalKnownParities( at, (AT_RANK)i1, neigh[0], (AT_RANK)i1, neigh[1], nVisited, nVisited, nRank, nCanonRank, 1 ) ) { - if ( !RemoveOneStereoBond( at, i1, /* atom number*/ n1 /* stereo bond number*/ ) ) { - ret = CT_STEREOBOND_ERROR; /* */ - goto exit_function; - } - n1 --; /* cycle counter may temporarily become negative */ - /* Remove from pCS */ - nAtomRank1 = inchi_max( nCanonRank[i1], nCanonRank[opposite_atom]); - nAtomRank2 = inchi_min( nCanonRank[i1], nCanonRank[opposite_atom]); - for ( n = 0, m = pCS->nLenLinearCTStereoDble-1; n <= m; n ++ ) { - if ( pCS->LinearCTStereoDble[n].at_num1 == nAtomRank1 && - pCS->LinearCTStereoDble[n].at_num2 == nAtomRank2 ) { - if ( n < m ) { /* remove pCS->LinearCTStereoDble[n] */ - memmove( pCS->LinearCTStereoDble + n, - pCS->LinearCTStereoDble + n + 1, - (m-n)*sizeof(pCS->LinearCTStereoDble[0]) ); - } - pCS->nLenLinearCTStereoDble --; -#if ( bRELEASE_VERSION == 0 ) - pCS->bExtract |= EXTR_KNOWN_USED_TO_REMOVE_PARITY; -#endif - m = -1; /* set flag "found" */ - break; - } - } - if ( m >= 0) { - ret = CT_STEREOCOUNT_ERR; /* bond not found */ - goto exit_function; - } - ret ++; /* number of removed known in advance non-stereo bonds */ - } - } - } - -exit_function: - - if ( nVisited ) { - inchi_free( nVisited ); - } - return ret; -} -#endif /* } REMOVE_KNOWN_NONSTEREO */ -/**********************************************************************************/ -/* Find stereo center parities known in advance */ -int SetKnownStereoCenterParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, - const AT_RANK *nRank, const AT_RANK *nAtomNumber ) -{ - int i, j, n, m, j1, k, num_neigh, iMax, trans_i, trans_k, prev_trans, num_set; - AT_RANK nAtomRank; - AT_RANK nNeighRank[MAX_NUM_STEREO_ATOM_NEIGH]; - AT_RANK nNeighCanonRank[MAX_NUM_STEREO_ATOM_NEIGH]; - for ( i = 0, num_set = 0; i < num_atoms; i ++ ) { - if ( !at[i].parity || at[i].stereo_bond_neighbor[0] ) { - continue; - } - if ( at[i].stereo_atom_parity != AB_PARITY_CALC || !PARITY_WELL_DEF(at[i].parity) ) { - continue; - } - num_neigh = at[i].valence; - for ( j = 0; j < num_neigh; j ++ ) { - nNeighRank[j] = nRank[(int)at[i].neighbor[j]]; - } - nAtomRank = nRank[i]; - if ( num_neigh == 1 ) { /* other neighbors must be implicit H */ - at[i].stereo_atom_parity = at[i].parity; - trans_i = 0; - } else { - /* sort constitutional equivalence ranks of the neighbors */ - trans_i = insertions_sort(nNeighRank, num_neigh, sizeof(nNeighRank[0]), comp_AT_RANK); - for ( j = 1; j < num_neigh; j ++ ) { - if ( nNeighRank[j-1] == nNeighRank[j] ) - break; /* at[i] has consitutionally identical neighbors */ - } - if ( j < num_neigh ) { - /* at least 2 neighbors are const. identical; parity cannot be calculated at this time */ - continue; /* try next stereo atom */ - } - } - prev_trans = -1; - trans_k = 0; - /* find neighbors of constitutionally equivalent stereo centers */ - /* and at[i] parities in case those centers are mapped on at[i] */ - for ( iMax = (int)nAtomRank-1, j1 = 0; j1 <= iMax && nAtomRank==nRank[k=(int)nAtomNumber[iMax-j1]]; j1 ++ ) { - /* at[k] is constitutionally equivalent to at[i] */ - if ( (int)at[k].valence != num_neigh ) { - return CT_STEREOCOUNT_ERR; /* */ - } - /* -- commented out to accept non-stereogenic atoms since -- - -- they may participate in mapping stereocenters 12-16-2003 -- - if ( !PARITY_VAL(at[k].parity) ) { - continue; // not a stereogenic atom - } - */ - for ( j = 0, m = 0; m < num_neigh; m ++ ) { - for ( n = 0; n < num_neigh; n ++ ) { - if ( nRank[(int)at[k].neighbor[n]] == nNeighRank[m] ) { - /* save canonical numbers (ranks) of the neighbors in - * order of increasing constit. equivalence ranks */ - nNeighCanonRank[m] = nCanonRank[(int)at[k].neighbor[n]]; - j ++; - break; - } - } - } - if ( j != num_neigh ) { - return CT_STEREOCOUNT_ERR; /* */ - } - trans_k = insertions_sort(nNeighCanonRank, num_neigh, sizeof(nNeighCanonRank[0]), comp_AT_RANK); - trans_k %= 2; - if ( prev_trans < 0 ) { - prev_trans = trans_k; - } else - if ( trans_k != prev_trans ) { - /* different mappings may produce different parities. Cannot find the parity at this time */ - /* this may happen when a set of constit. equivalent atoms has non-contiguous canonical numbers */ - break; - } - } - if ( trans_k == prev_trans ) { - at[i].stereo_atom_parity = 2 - (at[i].parity + trans_i + prev_trans ) % 2; - num_set ++; - } - } - return num_set; -} - -#if ( REMOVE_KNOWN_NONSTEREO == 1 ) /* { */ -/**********************************************************************************/ -/* DFS along paths starting from the stereocenter through pairs of cut-edges */ -int RemoveKnownNonStereoCenterParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, - const AT_RANK *nRank, CANON_STAT *pCS) -{ - int i, j, n, m, k, num_neigh, ret = 0; - /*AT_RANK nAtomRank;*/ - AT_RANK nNeighRank[MAX_NUM_STEREO_ATOM_NEIGH], nNeighOrd[MAX_NUM_STEREO_ATOM_NEIGH]; - - AT_RANK *nVisited = NULL; - - for ( i = 0; i < num_atoms; i ++ ) { - if ( !at[i].parity || at[i].stereo_bond_neighbor[0] ) { - continue; - } - if ( !PARITY_CALCULATE(at[i].stereo_atom_parity) && PARITY_WELL_DEF(at[i].stereo_atom_parity) ) { - continue; - } - num_neigh = at[i].valence; - for ( j = 0; j < num_neigh; j ++ ) { - nNeighRank[j] = nRank[(int)at[i].neighbor[j]]; - nNeighOrd[j] = j; - } - /*nAtomRank = nRank[i];*/ - if ( num_neigh == 1 ) { - continue; - } - pn_RankForSort = nNeighRank; - insertions_sort(nNeighOrd, num_neigh, sizeof(nNeighRank[0]), CompRanksOrd); - for ( j = k = 1; k && j < num_neigh; j ++ ) { - if ( at[i].nRingSystem != at[(int)at[i].neighbor[(int)nNeighOrd[j]]].nRingSystem && - /* no more ring system membership check is necessary because */ - /* the two neighbors are to be constitutionally equivalent atoms: */ - nNeighRank[nNeighOrd[j-1]] == nNeighRank[nNeighOrd[j]] ) { - k = j; - do { - if ( !nVisited && !(nVisited = (AT_RANK*) inchi_malloc( sizeof(nVisited[0])*num_atoms ) ) ) { - ret = CT_OUT_OF_RAM; /* */ - goto exit_function; - } - memset( nVisited, 0, sizeof(nVisited[0])*num_atoms ); - nVisited[i] = 1; - if ( PathsHaveIdenticalKnownParities( at, (AT_RANK)i, at[i].neighbor[(int)nNeighOrd[j-1]], - (AT_RANK)i, at[i].neighbor[(int)nNeighOrd[k]], - nVisited, nVisited, nRank, nCanonRank, 1 ) ) { - at[i].parity = 0; /* remove parity */ - at[i].stereo_atom_parity = 0; - at[i].final_parity = 0; - /* at[i].bHasStereoOrEquToStereo = 0; */ - for ( n = 0, m = pCS->nLenLinearCTStereoCarb-1; n <= m; n ++ ) { - if ( pCS->LinearCTStereoCarb[n].at_num == nCanonRank[i] ) { - if ( n < m ) { /* remove pCS->LinearCTStereoCarb[n] */ - memmove( pCS->LinearCTStereoCarb + n, - pCS->LinearCTStereoCarb + n + 1, - (m-n)*sizeof(pCS->LinearCTStereoCarb[0]) ); - } - pCS->nLenLinearCTStereoCarb --; - k = 0; -#if ( bRELEASE_VERSION == 0 ) - pCS->bExtract |= EXTR_KNOWN_USED_TO_REMOVE_PARITY; -#endif - break; - } - } - if ( k ) { - ret = CT_STEREOCOUNT_ERR; /* */ - goto exit_function; - } - ret ++; - break; - } - } while ( ++k < num_neigh && nNeighRank[nNeighOrd[j-1]] == nNeighRank[nNeighOrd[k]] ); - } - } - } - -exit_function: - - if ( nVisited ) { - inchi_free( nVisited ); - } - - return ret; -} -#endif /* } REMOVE_KNOWN_NONSTEREO */ -/**********************************************************************************/ -/* Find stereo center parities known in advance */ -int MarkKnownEqualStereoCenterParities( sp_ATOM *at, int num_atoms, - const AT_RANK *nRank, const AT_RANK *nAtomNumber ) -{ - int i, j1, k, num_centers, iMax, bDifferentParities; - AT_RANK nAtomRank; - int parity, parity_k; - num_centers = 0; - for ( i = 0; i < num_atoms; i ++ ) { - if ( !at[i].parity || at[i].stereo_bond_neighbor[0] ) { - continue; - } - if ( at[i].bHasStereoOrEquToStereo ) { - continue; /* already marked */ - } - if ( /*!PARITY_KNOWN(at[i].stereo_atom_parity) ||*/ (at[i].stereo_atom_parity & KNOWN_PARITIES_EQL) ) { - continue; - } - parity = PARITY_VAL(at[i].stereo_atom_parity); - if ( parity == AB_PARITY_NONE ) { - continue; - } - nAtomRank = nRank[i]; - bDifferentParities = -1; - /* find constitutionally equivalent stereo centers and compare their known at this time parities */ - for ( iMax = (int)nAtomRank-1, j1 = 0; j1 <= iMax && nAtomRank==nRank[k=(int)nAtomNumber[iMax-j1]]; j1 ++ ) { - /* at[k] is constitutionally equivalent to at[i] */ - parity_k = PARITY_VAL(at[k].stereo_atom_parity); - if ( parity_k != parity ) { - bDifferentParities = 1; - } else - if ( parity_k == parity && bDifferentParities < 0 ) { - bDifferentParities = 0; - } - if ( !parity_k ) { - at[k].bHasStereoOrEquToStereo = 2; - } else - if ( !at[k].bHasStereoOrEquToStereo ) { - at[k].bHasStereoOrEquToStereo = 1; - } - } - if ( 0 == bDifferentParities && PARITY_KNOWN( parity ) ) { - for ( iMax = (int)nAtomRank-1, j1 = 0; j1 <= iMax && nAtomRank==nRank[k=(int)nAtomNumber[iMax-j1]]; j1 ++ ) { - /* at[k] is constitutionally equivalent to at[i] */ - at[k].stereo_atom_parity |= KNOWN_PARITIES_EQL; - num_centers ++; - } - } - } - return num_centers; -} -/*****************************************************************************/ -/* invert known parities in at[] and in pCS->LinearCTStereoDble */ -/* pCS->LinearCTStereoCarb */ -/* nCanonRank[] contains canonical ranks used to fill pCS->LinearCTStereo... */ -/* nAtomNumberCanon[] will be filled with atom numbers in canonical order */ -/*****************************************************************************/ -int InvertStereo( sp_ATOM *at, int num_at_tg, - AT_RANK *nCanonRank, AT_RANK *nAtomNumberCanon, - CANON_STAT *pCS, int bInvertLinearCTStereo ) -{ - int i, j, j1, j2, num_changes, parity, cumulene_len; - num_changes = 0; - for ( i = 0; i < num_at_tg; i ++ ) { - nAtomNumberCanon[(int)nCanonRank[i]-1] = i; - } - for ( i = 0; i < pCS->nLenLinearCTStereoCarb; i ++ ) { - parity = pCS->LinearCTStereoCarb[i].parity; - if ( ATOM_PARITY_WELL_DEF( parity ) ) { - j = nAtomNumberCanon[(int)pCS->LinearCTStereoCarb[i].at_num-1]; - if ( PARITY_WELL_DEF(at[j].parity) ) { - at[j].parity ^= AB_INV_PARITY_BITS; - } else { - goto exit_error; /* inconsistency */ - } - if ( bInvertLinearCTStereo ) { - pCS->LinearCTStereoCarb[i].parity ^= AB_INV_PARITY_BITS; - } - num_changes ++; - if ( PARITY_WELL_DEF(at[j].stereo_atom_parity) ) { - at[j].stereo_atom_parity ^= AB_INV_PARITY_BITS; - } - if ( PARITY_WELL_DEF(at[j].final_parity) ) { - at[j].final_parity ^= AB_INV_PARITY_BITS; - } - } - } - for ( i = 0; i < pCS->nLenLinearCTStereoDble; i ++ ) { - parity = pCS->LinearCTStereoDble[i].parity; - if ( ATOM_PARITY_WELL_DEF( parity ) ) { - j1 = nAtomNumberCanon[(int)pCS->LinearCTStereoDble[i].at_num1-1]; - cumulene_len = BOND_CHAIN_LEN(at[j1].stereo_bond_parity[0]); - if ( cumulene_len % 2 ) { - /* invert only in case of allene */ - j2 = nAtomNumberCanon[(int)pCS->LinearCTStereoDble[i].at_num2-1]; - /* checks for debug only */ - if ( 1 < MAX_NUM_STEREO_BONDS ) { - if ( at[j1].stereo_bond_neighbor[1] || - at[j2].stereo_bond_neighbor[1] ) { - goto exit_error; /* inconsitency: atom has more than one cumulene bond */ - } - } - if ( cumulene_len != BOND_CHAIN_LEN(at[j2].stereo_bond_parity[0]) || - j1+1 != at[j2].stereo_bond_neighbor[0] || - j2+1 != at[j1].stereo_bond_neighbor[0] ) { - goto exit_error; /* inconsitency: atoms should refer to each other */ - } - /* invert parities */ - if ( PARITY_WELL_DEF(at[j1].parity) && PARITY_WELL_DEF(at[j2].parity) ) { - j = inchi_min( j1, j2 ); - at[j].parity ^= AB_INV_PARITY_BITS; /* for reversability always invert only atom with the smaller number */ - } else { - goto exit_error; /* inconsistency */ - } - if ( bInvertLinearCTStereo ) { - pCS->LinearCTStereoDble[i].parity ^= AB_INV_PARITY_BITS; - } - num_changes ++; - if ( PARITY_WELL_DEF(at[j1].stereo_bond_parity[0]) ) { - at[j1].stereo_bond_parity[0] ^= AB_INV_PARITY_BITS; - } - if ( PARITY_WELL_DEF(at[j2].stereo_bond_parity[0]) ) { - at[j2].stereo_bond_parity[0] ^= AB_INV_PARITY_BITS; - } - } - } - } - - return num_changes; - -exit_error: - - return CT_STEREOCOUNT_ERR; -} -/**********************************************************************************/ -/* Make sure atoms stereo descriptors fit molecular symmetry and remove */ -/* parity from obviously non-stereo atoms and bonds */ -int FillOutStereoParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, const AT_RANK *nAtomNumberCanon, - const AT_RANK *nRank, const AT_RANK *nAtomNumber, CANON_STAT *pCS, int bIsotopic ) -{ - int ret; - /* unmark atoms with 2 or more constitutionally equivalent neighbors */ - /* such that there is no path through them to an atom with parity */ - ret = UnmarkNonStereo( at, num_atoms, nRank, nAtomNumber, bIsotopic ); - if ( ret < 0 ) - return ret; /* program error? */ /* */ - ret = FillAllStereoDescriptors( at, num_atoms, nCanonRank, nAtomNumberCanon, pCS ); /* ret<0: error */ - if ( !ret ) { - ret = pCS->nLenLinearCTStereoCarb + pCS->nLenLinearCTStereoDble; - } - if ( ret < 0 ) { - return ret; /* program error? */ /* */ - } - - if ( ret >= 0 ) { - int ret2; - ret2 = SetKnownStereoCenterParities( at, num_atoms, nCanonRank, nRank, nAtomNumber ); - if ( ret2 >= 0 ) { - ret2 = MarkKnownEqualStereoCenterParities( at, num_atoms, nRank, nAtomNumber ); - } - if ( ret2 >= 0 ) { - ret2 = SetKnownStereoBondParities( at, num_atoms, nCanonRank, nRank, nAtomNumber ); - if ( ret2 >= 0 ) { - ret2 = MarkKnownEqualStereoBondParities( at, num_atoms, nRank, nAtomNumber); - } - } -#if ( REMOVE_KNOWN_NONSTEREO == 1 ) /* { */ - if ( ret2 >= 0 ) { - int ret3; - do { - ret2 = RemoveKnownNonStereoCenterParities( at, num_atoms, nCanonRank, nRank, pCS); - if ( ret2 >= 0 ) { - ret3 = RemoveKnownNonStereoBondParities( at, num_atoms, nCanonRank, nRank, pCS); - ret2 = ret3 >= 0? ret2+ret3 : ret3; - } - } - while( ret2 > 0 ); - } - if ( RETURNED_ERROR( ret2 ) ) { - ret = ret2; - } -#endif /* } REMOVE_KNOWN_NONSTEREO */ - } - - return ret; /* non-zero means error */ -} -/**********************************************************************************/ -int GetStereoNeighborPos( sp_ATOM *at, int iAt1, int iAt2 ) -{ - int k1; - AT_RANK sNeigh = (AT_RANK)(iAt2+1); - AT_RANK s; - for ( k1 = 0; k1 < MAX_NUM_STEREO_BONDS && (s = at[iAt1].stereo_bond_neighbor[k1]); k1 ++ ) { - if ( s == sNeigh ) { - return k1; - } - } - return -1; /* neighbor not found */ -} - -/**********************************************************************************/ -/* Extracted from FillSingleStereoDescriptors(...) */ -/**********************************************************************************/ -int GetStereoBondParity(sp_ATOM *at, int i, int n, AT_RANK *nRank ) -{ -int k1, k2, s, parity; - - if ( at[i].stereo_bond_neighbor[0] ) { - for ( k1 = 0; k1 < MAX_NUM_STEREO_BONDS && (s = (int)at[i].stereo_bond_neighbor[k1]); k1 ++ ) { - if ( --s == n ) { - goto neigh1_found; - } - } - return -1; /* error: not a stereo neighbor */ -neigh1_found: - if ( PARITY_KNOWN( at[i].stereo_bond_parity[k1] ) ) { - return PARITY_VAL( at[i].stereo_bond_parity[k1] ); - } - for ( k2 = 0; k2 < MAX_NUM_STEREO_BONDS && (s = (int)at[n].stereo_bond_neighbor[k2]); k2 ++ ) { - if ( --s == i ) { - goto neigh2_found; - } - } - return -1; /* error: not a stereo neighbor */ -neigh2_found:; - } else { - return -1; /* error: not a stereo bond */ - } - - if ( ATOM_PARITY_WELL_DEF(at[i].parity) && - ATOM_PARITY_WELL_DEF(at[n].parity) && - MIN_DOT_PROD <= abs(at[i].stereo_bond_z_prod[k1]) ) { - /* bond parity can be calculated */ - int half_parity1, half_parity2; - /* check whether all neighbors are defined */ - - - half_parity1 = HalfStereoBondParity( at, i, k1, nRank ); - half_parity2 = HalfStereoBondParity( at, n, k2, nRank ); - if ( !half_parity1 || !half_parity2 ) - return 0; /* ranks undefined or not a stereo bond */ - if ( ATOM_PARITY_WELL_DEF(half_parity1) && - ATOM_PARITY_WELL_DEF(half_parity2) ) { - parity = 2 - ( half_parity1 + half_parity2 - + (at[i].stereo_bond_z_prod[k1] < 0))%2; - } else { - return CT_STEREOBOND_ERROR; /* */ - } - } else { - /* parity cannot be calculated: not enough info or 'unknown' */ - if ( AB_PARITY_NONE != (parity = inchi_max(at[i].parity, at[n].parity)) ) { - parity = AB_PARITY_UNDF; /* should not happen */ - } - } - return parity; -} - - - - -/**********************************************************************************/ -/* Extracted from FillSingleStereoDescriptors(...) */ -/**********************************************************************************/ -int GetPermutationParity( sp_ATOM *at, AT_RANK nAvoidNeighbor, AT_RANK *nCanonRank ) -{ - AT_RANK nNeighRank[MAX_NUM_STEREO_ATOM_NEIGH]; - int j, k, parity; - if ( at->valence > MAX_NUM_STEREO_ATOM_NEIGH ) { - parity = -1; /* error */ - } else { - for ( j = k = 0; j < at->valence; j ++ ) { - if ( at->neighbor[j] != nAvoidNeighbor ) { - nNeighRank[k++] = nCanonRank[(int)at->neighbor[j]]; - } - } - if ( k ) { - parity = insertions_sort( nNeighRank, k, sizeof(nNeighRank[0]), comp_AT_RANK); - if ( nNeighRank[0] ) { - parity = 2 - parity % 2; - } else { - parity = 0; /* not all ranks are known */ - } - } else { - /* special case: HX= with implicit H */ - parity = 2; - } - } - return parity; -} -/**********************************************************************************/ -int GetStereoCenterParity(sp_ATOM *at, int i, AT_RANK *nRank ) -{ - AT_NUMB nNeighborNumber2[MAXVAL]; - int parity; - int k, num_trans; - - if ( !at[i].parity ) { - return 0; /* not a stereo center */ - } - if ( at[i].stereo_bond_neighbor[0] ) { - return -1; /* a stereo bond atom, not a stereo center */ - } - - if ( ATOM_PARITY_WELL_DEF(at[i].parity) ) { - /* number of neighbors transpositions to the sorted order is unknown. Find it. */ - /* If parity is not well-defined then doing this is a waste of time */ - int num_neigh = at[i].valence; - for ( k = 0; k < num_neigh; k ++) { - if ( !nRank[(int)at[i].neighbor[k]] ) - return 0; /* stereo at[i] does not belong to the traversed part of the structure */ - nNeighborNumber2[k] = k; - } - pNeighborsForSort = at[i].neighbor; - pn_RankForSort = nRank; - num_trans=insertions_sort( nNeighborNumber2, num_neigh, sizeof(nNeighborNumber2[0]), CompNeighborsAT_NUMBER ); -#ifndef CT_NEIGH_INCREASE - num_trans += ((num_neigh*(num_neigh-1))/2)%2; /* get correct parity for ascending order */ -#endif - parity = 2 - (at[i].parity + num_trans)%2; - } else { - parity = at[i].parity; - } - - return parity; -} - +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include + +#include "mode.h" +#include "ichicant.h" +#include "ichicomn.h" +#include "ichister.h" + +typedef struct tagStereoBondNeighbor +{ + /* *n = sort key */ + AT_RANK nRank; /* *1 opposite atom rank; equal ranks mean constit. equivalence */ + + AT_RANK nNeighRank1; /* rank of the neighbor in the direction to the opposite atom */ + + AT_RANK nNeighRank2; /* rank of the opposite atom neighbor in the direction to the current atom */ + + AT_RANK num; /* number of same type bonds to constitutionally equivalent neighbors */ + + AT_RANK num_any_parity; /* at least one atom has parity in 1..4 range */ + + AT_RANK num_defined_parity; /* number of neighbors with defined parity <= num */ + + /* AT_RANK num_undef_parity; */ + /* AT_RANK num_unkn_parity; */ + AT_RANK what2do; + + U_CHAR cumulene_len; /* high nimble bits: (cumulene length - 1) */ + + U_CHAR bond_type; /* *2 all same, not a real bond type */ +} STEREO_BOND_NEIGH; + + + +/* Local prototypes */ + +int SetHalfStereoBondIllDefPariy( sp_ATOM *at, + int jn, /* atom number*/ + int k1 /* stereo bond number*/, + int new_parity ); + +int RemoveHalfStereoBond( sp_ATOM *at, + int jn, /* atom number*/ + int k1 /* stereo bond number*/ ); + +int SetKnownStereoBondParities( CANON_GLOBALS *pCG, sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, + const AT_RANK *nRank, const AT_RANK *nAtomNumber ); + +int MarkKnownEqualStereoBondParities( sp_ATOM *at, + int num_atoms, + const AT_RANK *nRank, + const AT_RANK *nAtomNumber ); + +int GetNextNeighborAndRank( sp_ATOM *at, + AT_RANK cur, + AT_RANK prev, + AT_RANK *n, + AT_RANK *cr, + const AT_RANK *nCanonRank ); + +int GetAndCheckNextNeighbors( sp_ATOM *at, + AT_RANK cur1, + AT_RANK prev1, + AT_RANK cur2, + AT_RANK prev2, + AT_RANK *n1, + AT_RANK *n2, + AT_RANK *nVisited1, + AT_RANK *nVisited2, + const AT_RANK *nRank, + const AT_RANK *nCanonRank ); + +AT_RANK PathsHaveIdenticalKnownParities( sp_ATOM *at, + AT_RANK prev1, + AT_RANK cur1, + AT_RANK prev2, + AT_RANK cur2, + AT_RANK *nVisited1, + AT_RANK *nVisited2, + const AT_RANK *nRank, + const AT_RANK *nCanonRank, + AT_RANK nLength ); + +int RemoveKnownNonStereoBondParities( sp_ATOM *at, + int num_atoms, + const AT_RANK *nCanonRank, + const AT_RANK *nRank, + CANON_STAT *pCS); + +int SetKnownStereoCenterParities( CANON_GLOBALS *pCG, sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, + const AT_RANK *nRank, const AT_RANK *nAtomNumber ); +int RemoveKnownNonStereoCenterParities( CANON_GLOBALS *pCG, sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, + const AT_RANK *nRank, CANON_STAT *pCS); + +int MarkKnownEqualStereoCenterParities( sp_ATOM *at, + int num_atoms, + const AT_RANK *nRank, + const AT_RANK *nAtomNumber ); + + + + +/**********************************************************************************/ +/* Depth First Search for an atom with parity */ +int find_atoms_with_parity( sp_ATOM *at, S_CHAR *visited, + int from_atom, int cur_atom ) +{ + int i, next_atom; + + if ( visited[cur_atom] ) + return 0; + + if ( at[cur_atom].parity ) + return 1; + + visited[cur_atom] = 1; + + for ( i = 0; i < at[cur_atom].valence; i ++ ) + { + next_atom = at[cur_atom].neighbor[i]; + + if ( next_atom != from_atom && + find_atoms_with_parity( at, visited, cur_atom, next_atom ) ) + { + return 1; + } + } + + return 0; +} + + +/**********************************************************************************/ +int SetHalfStereoBondIllDefPariy( sp_ATOM *at, + int jn, /* atom number*/ + int k1 /* stereo bond number*/, + int new_parity ) +{ + int parity; + if ( k1 < MAX_NUM_STEREO_BOND_NEIGH && at[jn].stereo_bond_neighbor[k1] ) + { + parity = at[jn].stereo_bond_parity[k1] ^ PARITY_VAL(at[jn].stereo_bond_parity[k1]); + at[jn].stereo_bond_parity[k1] = parity | PARITY_VAL(new_parity); + at[jn].parity = PARITY_VAL(new_parity); + return 1; /* success */ + } + + return 0; /* failed */ +} + + +/**********************************************************************************/ +int RemoveHalfStereoBond( sp_ATOM *at, + int jn, /* atom number*/ + int k1 /* stereo bond number*/ ) +{ + int k2; + if ( k1 < MAX_NUM_STEREO_BOND_NEIGH && at[jn].stereo_bond_neighbor[k1] ) + { + for ( k2 = k1; k2+1 < MAX_NUM_STEREO_BOND_NEIGH; k2++ ) + { + at[jn].stereo_bond_neighbor[k2] = at[jn].stereo_bond_neighbor[k2+1]; + at[jn].stereo_bond_ord[k2] = at[jn].stereo_bond_ord[k2+1]; + at[jn].stereo_bond_z_prod[k2] = at[jn].stereo_bond_z_prod[k2+1]; + at[jn].stereo_bond_parity[k2] = at[jn].stereo_bond_parity[k2+1]; + } + at[jn].stereo_bond_neighbor[k2] = 0; + at[jn].stereo_bond_ord[k2] = 0; + at[jn].stereo_bond_z_prod[k2] = 0; + at[jn].stereo_bond_parity[k2] = 0; + + if ( !at[jn].stereo_bond_neighbor[0] ) + { /* curled braces added 6-6-2002 */ + at[jn].parity = 0; + at[jn].stereo_atom_parity = 0; + at[jn].final_parity = 0; + /* at[jn].bHasStereoOrEquToStereo = 0; */ + } + return 1; /* success */ + } + + return 0; /* failed */ +} + + +/**********************************************************************************/ +int SetOneStereoBondIllDefParity( sp_ATOM *at, int jc, /* atom number*/ int k /* stereo bond ord. number*/, int new_parity ) +{ + int k1, ret=0, kn, jn = (int)at[jc].stereo_bond_neighbor[k]-1; + + /* opposite end */ + for ( k1 = kn = ret = 0; + k1 < MAX_NUM_STEREO_BOND_NEIGH && (kn=at[jn].stereo_bond_neighbor[k1]); + k1++ ) + { + if ( kn - 1 == jc ) + { + ret = SetHalfStereoBondIllDefPariy( at, jn, /* atom number*/ k1 /* stereo bond number*/, new_parity ); + break; + } + } + + if ( ret ) + { + ret = SetHalfStereoBondIllDefPariy( at, jc, k, new_parity ); + } + + return ret; +} + + +/**********************************************************************************/ +int RemoveOneStereoBond( sp_ATOM *at, int jc, /* atom number*/ int k /* stereo bond number*/ ) +{ + int k1, ret=0, kn, jn = (int)at[jc].stereo_bond_neighbor[k]-1; + + /* opposite end */ + for ( k1 = kn = ret = 0; + k1 < MAX_NUM_STEREO_BOND_NEIGH && (kn=at[jn].stereo_bond_neighbor[k1]); + k1++ ) + { + if ( kn - 1 == jc ) + { + ret = RemoveHalfStereoBond( at, jn, k1 ); + break; + } + } + + if ( ret ) + { + ret = RemoveHalfStereoBond( at, jc, k ); + } + + return ret; +} + + +/**********************************************************************************/ +int RemoveOneStereoCenter( sp_ATOM *at, + int jc /* atom number*/ ) +{ + if ( at[jc].parity ) + { + at[jc].parity = 0; /* remove parity */ + at[jc].stereo_atom_parity = 0; + at[jc].final_parity = 0; + /* at[jc].bHasStereoOrEquToStereo = 0; */ + return 1; + } + + return 0; /* failed: not a stereo center */ +} + + +/**********************************************************************************/ +/* Remove stereo parity from centers having constitutionally equivalent */ +/* cut-vertex neighbors whose attachments do not have stereogenic elements. */ +/* Currently checks ALL constitutionally equivalent neighbors. */ +/* To optimize, check only one. */ +int UnmarkNonStereo( CANON_GLOBALS *pCG, sp_ATOM *at, int num_atoms, + const AT_RANK *nRank, const AT_RANK *nAtomNumber, int bIsotopic ) +{ + int i, i1, i2, j, k, k1, k2, kn /* neigh*/, val, ic/* center*/, jc, num_implicit_H; + int num_neighbors_with_parity, num_no_parity_atoms, num_removed_parities=-1, num_removed_parities0; + AT_RANK nNeighborNumber[MAX_NUM_STEREO_ATOM_NEIGH]; + AT_RANK nPrevAtomRank, nPrevNeighRank; + + S_CHAR *visited = (S_CHAR *)inchi_malloc( num_atoms * sizeof(visited[0])); + + if ( !visited ) + goto exit_function; + + num_removed_parities = 0; + num_no_parity_atoms = 0; + + do + { + num_removed_parities0 = num_removed_parities; + + for ( i = i1 = 0, nPrevAtomRank = 0; i <= num_atoms; i++ ) + { + /* bounds violation check (i!=num_atoms) added 6-21-2002 */ + if ( i == num_atoms || nPrevAtomRank != nRank[j = nAtomNumber[i]] + /* at[j].parity && 1 < at[j].valence && at[j].valence < MAX_NUM_STEREO_ATOM_NEIGH*/ ) { + + /* end of constitutionally equivalent atoms sequence */ + + /* nPrevRank = nRank[j]; */ + i2 = i; + if ( i2 - i1 > num_no_parity_atoms /*&& at[jc = nAtomNumber[i1]].parity*/ ) + { + /* at[nAtomNumber[i1]]..at[nAtomNumber[i2-1]] are constitutionally equivalent and some of them have parity */ + jc = nAtomNumber[i1]; + num_no_parity_atoms = 0; + val = at[jc].valence; /* all equivalent atoms have equal valences, etc. (except parities) */ + num_implicit_H = at[jc].endpoint? 0 : at[jc].num_H; + /* Only atoms with valence <= MAX_NUM_STEREO_ATOM_NEIGH may have parity. However, check: */ + if ( val + num_implicit_H > MAX_NUM_STEREO_ATOM_NEIGH ) + { + continue; /* program error ??? */ /* */ + } + for ( k = 0; k < val; k ++ ) + { + nNeighborNumber[k] = k; /* initialize an array of indexes for sorting */ + } + /* check parities */ + for ( ic = i1; ic < i2; ic ++ ) + { + jc = nAtomNumber[ic]; + /* sort neighbors according to their canon. equivalence ranks */ + pCG->m_pNeighborsForSort = at[jc].neighbor; + pCG->m_pn_RankForSort = nRank; + insertions_sort( pCG, nNeighborNumber, val, sizeof(nNeighborNumber[0]), CompNeighborsAT_NUMBER ); + num_neighbors_with_parity = -1; /* non-zero */ + for ( k = k1 = 0, nPrevNeighRank = 0; k <= val; k ++ ) + { + if ( k == val || nPrevNeighRank != nRank[at[jc].neighbor[nNeighborNumber[k]]] ) + { + k2 = k; + if ( k2 - k1 > 1 ) + { + /* found 2 or more constitutionally equivalent neighbors */ + /* Check if they have only non-stereogenic neighbors */ + for ( kn = k1, num_neighbors_with_parity = 0; kn < k2; kn ++ ) + { + memset( visited, 0, num_atoms * sizeof(visited[0])); + visited[jc] = 1; /* starting point; the only atom with parity */ + num_neighbors_with_parity += + find_atoms_with_parity( at, visited, jc, (int)at[jc].neighbor[nNeighborNumber[kn]] ); + } + } + if ( !num_neighbors_with_parity ) + { + break; /* at[jc] cannot have defined parity */ + } + if ( k + 1 < val ) + { + k1 = k; /* at least 2 more neighbors left */ + nPrevNeighRank = nRank[at[jc].neighbor[nNeighborNumber[k]]]; + } else + { + break; + } + } + } + if ( num_implicit_H > 1 ) + { + if ( bIsotopic && (at[jc].num_iso_H[0] > 1 || + at[jc].num_iso_H[1] > 1 || + at[jc].num_iso_H[2] > 1 ) || + num_implicit_H > NUM_H_ISOTOPES || + !bIsotopic + ) + { + num_neighbors_with_parity = 0; + } + } + /* increment if: */ + /* (a) constitutionally equivalent neighbors do exist, and */ + /* (b) all constitutionally equivalent neighbors do not have parity, and */ + /* (c) all constitutionally equivalent neighbors are not connected to atoms with parity */ + num_no_parity_atoms += !num_neighbors_with_parity; + } + if ( num_no_parity_atoms == i2 - i1 ) + { + /* all atoms at[nAtomNumber[i1]]..at[nAtomNumber[i2-1]] cannot be */ + /* stereo centers or belong to stereo bonds */ + for ( ic = i1; ic < i2; ic ++ ) + { + int jn; + jc = nAtomNumber[ic]; + at[jc].parity = 0; /* remove parity */ + at[jc].stereo_atom_parity = 0; + at[jc].final_parity = 0; + at[jc].bHasStereoOrEquToStereo = 0; + /* remove stereo bonds */ + for ( k = 0; k < MAX_NUM_STEREO_BOND_NEIGH && (jn=at[jc].stereo_bond_neighbor[k]); k++ ) + { + jn--; /* stereo bond neighbor */ + /* opposite end */ + for ( k1 = 0; k1 < MAX_NUM_STEREO_BOND_NEIGH && (kn=at[jn].stereo_bond_neighbor[k1]); k1++ ) + { + if ( kn - 1 == jc ) + { + RemoveHalfStereoBond( at, jn, k1 ); + break; + } + } + /* at at[jc] stereo bond end; since references to all at[jc] */ + /* stereo bond neighbors are to be removed, do not shift them */ + at[jc].stereo_bond_neighbor[k] = 0; + at[jc].stereo_bond_ord[k] = 0; + at[jc].stereo_bond_z_prod[k] = 0; + at[jc].stereo_bond_parity[k] = 0; + } + } + num_removed_parities += num_no_parity_atoms; + } + } + if ( i < num_atoms ) + { + nPrevAtomRank = nRank[j]; + i1 = i; + } + num_no_parity_atoms = 0; + } + num_no_parity_atoms += (i < num_atoms && !at[j].parity); + } + } while ( num_removed_parities != num_removed_parities0 ); + +exit_function: + if ( visited ) + inchi_free( visited ); + + return num_removed_parities; +} + + +/********************************************************************************** + * + * Add stereo descriptor(s) for atom #i + * + **********************************************************************************/ +int FillSingleStereoDescriptors( CANON_GLOBALS *pCG, + sp_ATOM *at, + int i, + int num_trans, + const AT_RANK *nRank, + AT_STEREO_CARB *LinearCTStereoCarb, + int *nStereoCarbLen, + int nMaxStereoCarbLen, + AT_STEREO_DBLE *LinearCTStereoDble, + int *nStereoDbleLen, int nMaxStereoDbleLen, + int bAllene ) +{ + + if ( !LinearCTStereoDble && !LinearCTStereoCarb ) + { + return 0; /* return immediately if no stereo have been requested */ + } + + /*************************************************** + add stereo centers and stereo bonds to the CT + ***************************************************/ + if ( at[i].parity || at[i].stereo_bond_neighbor[0] ) + { + AT_RANK r_neigh, rank = nRank[i]; + AT_NUMB nNeighborNumber2[MAXVAL]; + unsigned parity; + int k; + int num_allene = 0; + + if ( ATOM_PARITY_WELL_DEF(at[i].parity) && num_trans < 0 ) + { + /* number of neighbors transpositions to the sorted order is unknown. Find it. */ + /* If parity is not well-defined then doing this is a waste of time */ + int num_neigh = at[i].valence; + for ( k = 0; k < num_neigh; k ++) + { + nNeighborNumber2[k] = k; + } + + pCG->m_pNeighborsForSort = at[i].neighbor; + pCG->m_pn_RankForSort = nRank; + num_trans=insertions_sort( pCG, nNeighborNumber2, num_neigh, sizeof(nNeighborNumber2[0]), CompNeighborsAT_NUMBER ); + +#ifndef CT_NEIGH_INCREASE + num_trans += ((num_neigh*(num_neigh-1))/2)%2; /* get correct parity for ascending order */ +#endif + } + + /* stereo bonds */ + if ( LinearCTStereoDble && at[i].stereo_bond_neighbor[0] ) + { + + /* HalfStereoBondParity( sp_ATOM *at, int at_no1, int i_sb_neigh, AT_RANK *nRank ) */ + AT_NUMB nStereoNeighNumber[MAX_NUM_STEREO_BONDS], nStereoNeigh[MAX_NUM_STEREO_BONDS], n; + int num_stereo, stereo_neigh, stereo_neigh_ord, stereo_bond_parity; + for ( num_stereo = 0; + num_stereo < MAX_NUM_STEREO_BONDS && + (n=at[i].stereo_bond_neighbor[num_stereo]); num_stereo ++ ) + { + nStereoNeighNumber[num_stereo] = num_stereo; + nStereoNeigh[num_stereo] = n-1; + num_allene += IS_ALLENE_CHAIN(at[i].stereo_bond_parity[num_stereo]); + } + if ( bAllene > 0 && !num_allene || bAllene == 0 && num_allene ) + { + return 0; + } + + /* sort stereo bonds according to the ranks of the neighbors */ + pCG->m_pNeighborsForSort = nStereoNeigh; + pCG->m_pn_RankForSort = nRank; + insertions_sort( pCG, nStereoNeighNumber, num_stereo, sizeof(nStereoNeighNumber[0]), CompNeighborsAT_NUMBER ); + + /* process stereo bonds one by one */ + for ( k = 0; k < num_stereo; k ++ ) + { + stereo_neigh = nStereoNeigh[stereo_neigh_ord=(int)nStereoNeighNumber[k]]; + + if ( (r_neigh = (AT_NUMB)nRank[stereo_neigh]) CT_NEIGH_SMALLER_THAN rank ) + { + /* accept only neighbors that have smaller ranks */ + stereo_bond_parity = PARITY_VAL(at[i].stereo_bond_parity[stereo_neigh_ord]); + if ( stereo_bond_parity == AB_PARITY_NONE ) + continue; + + /* stereo_neigh = at[i].stereo_bond_neighbor[nStereoNeighNumber[k]]-1; */ + if ( ATOM_PARITY_KNOWN(stereo_bond_parity) ) + { + parity = stereo_bond_parity; + } + else if ( ATOM_PARITY_WELL_DEF(at[i].parity) && + ATOM_PARITY_WELL_DEF(at[stereo_neigh].parity) && + MIN_DOT_PROD <= abs(at[i].stereo_bond_z_prod[stereo_neigh_ord]) ) + { + /* bond parity can be calculated */ + int half_parity1, half_parity2, j, nn, stereo_neigh_ord2; + stereo_neigh_ord2 = -1; + for ( j = 0; j < MAX_NUM_STEREO_BONDS && + (nn=(int)at[stereo_neigh].stereo_bond_neighbor[j]); + j++ ) + { + if ( i+1 == nn ) + { + /* found the opposite end of the stereo bond */ + stereo_neigh_ord2 = j; + break; + } + } + if ( stereo_neigh_ord2 >= 0 ) + { + half_parity1 = HalfStereoBondParity( at, i, stereo_neigh_ord, nRank ); + half_parity2 = HalfStereoBondParity( at, stereo_neigh, stereo_neigh_ord2, nRank ); + if ( ATOM_PARITY_WELL_DEF(half_parity1) && + ATOM_PARITY_WELL_DEF(half_parity2) ) + { + parity = 2 - ( half_parity1 + half_parity2 + + (at[i].stereo_bond_z_prod[stereo_neigh_ord] < 0))%2; + } + else + { + return CT_STEREOBOND_ERROR; /* */ + } + } + else + { + return CT_STEREOBOND_ERROR; /* */ + } + } + else + { + /* parity cannot be calculated: not enough info or 'unknown' */ + if ( AB_PARITY_NONE == (parity = inchi_max(at[i].parity, at[stereo_neigh].parity)) ) + continue; + if ( ATOM_PARITY_WELL_DEF(parity) ) + parity = AB_PARITY_UNDF; /* should not happen */ + } + if ( CHECK_OVERFLOW(*nStereoDbleLen, nMaxStereoDbleLen) ) + return CT_OVERFLOW; /* */ + /* first stereo bond atom */ + LinearCTStereoDble[*nStereoDbleLen].at_num1 = rank; + /* second stereo bond atom (opposite end) */ + LinearCTStereoDble[*nStereoDbleLen].at_num2 = r_neigh; + /* bond parity */ + LinearCTStereoDble[*nStereoDbleLen].parity = parity; + (*nStereoDbleLen) ++; + } + } + } + + + /* stereo carbon */ + if ( bAllene > 0 ) + { + return 0; + } + + if ( LinearCTStereoCarb && !at[i].stereo_bond_neighbor[0] ) + { + if ( CHECK_OVERFLOW(*nStereoCarbLen, nMaxStereoCarbLen) ) + return CT_OVERFLOW; /* */ + /* stereo atom rank */ + LinearCTStereoCarb[*nStereoCarbLen].at_num = rank; + /* stereo atom parity */ + parity = ATOM_PARITY_WELL_DEF(at[i].parity)? (2 - (at[i].parity + num_trans)%2) : at[i].parity; + LinearCTStereoCarb[*nStereoCarbLen].parity = parity; + (*nStereoCarbLen) ++; + } + } + + return 0; +} + + +/**********************************************************************************/ +void SwitchAtomStereoAndIsotopicStereo( sp_ATOM *at, int num_atoms, int *bSwitched ) +{ + int i; + /* switch atom stereo data */ + for ( i = 0; i < num_atoms; i ++ ) + { + inchi_swap( (char*)&at[i].parity, (char*)&at[i].parity2, sizeof( at[i].parity ) ); + inchi_swap( (char*)&at[i].final_parity, (char*)&at[i].final_parity2, sizeof( at[i].final_parity ) ); + inchi_swap( (char*)&at[i].stereo_atom_parity, (char*)&at[i].stereo_atom_parity2, sizeof( at[i].stereo_atom_parity ) ); + inchi_swap( (char*)&at[i].bHasStereoOrEquToStereo, (char*)&at[i].bHasStereoOrEquToStereo2, sizeof( at[i].bHasStereoOrEquToStereo ) ); + + inchi_swap( (char*)at[i].stereo_bond_neighbor, (char*)at[i].stereo_bond_neighbor2, sizeof( at[i].stereo_bond_neighbor ) ); + inchi_swap( (char*)at[i].stereo_bond_ord, (char*)at[i].stereo_bond_ord2, sizeof( at[i].stereo_bond_ord ) ); + inchi_swap( (char*)at[i].stereo_bond_z_prod, (char*)at[i].stereo_bond_z_prod2, sizeof( at[i].stereo_bond_z_prod ) ); + inchi_swap( (char*)at[i].stereo_bond_parity, (char*)at[i].stereo_bond_parity2, sizeof( at[i].stereo_bond_parity ) ); + } + + *bSwitched = !*bSwitched; +} + + +/**********************************************************************************/ +void SetCtToIsotopicStereo( CANON_STAT *pCS, CANON_STAT *pCS2 ) +{ + pCS->LinearCTStereoDble = pCS2->LinearCTIsotopicStereoDble; /* enable stereo */ + pCS->LinearCTStereoCarb = pCS2->LinearCTIsotopicStereoCarb; + + pCS->LinearCTStereoDbleInv = pCS2->LinearCTIsotopicStereoDbleInv; /* enable inv. stereo */ + pCS->LinearCTStereoCarbInv = pCS2->LinearCTIsotopicStereoCarbInv; + pCS->nMaxLenLinearCTStereoDble = pCS2->nMaxLenLinearCTIsotopicStereoDble; + pCS->nMaxLenLinearCTStereoCarb = pCS2->nMaxLenLinearCTIsotopicStereoCarb; + + pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTIsotopicStereoDble; + pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTIsotopicStereoCarb; +} + + +/**********************************************************************************/ +void SetCtToNonIsotopicStereo( CANON_STAT *pCS, CANON_STAT *pCS2 ) +{ + pCS->LinearCTStereoDble = pCS2->LinearCTStereoDble; /* enable stereo */ + pCS->LinearCTStereoCarb = pCS2->LinearCTStereoCarb; + + pCS->LinearCTStereoDbleInv = pCS2->LinearCTStereoDbleInv; /* enable inv. stereo */ + pCS->LinearCTStereoCarbInv = pCS2->LinearCTStereoCarbInv; + pCS->nMaxLenLinearCTStereoDble = pCS2->nMaxLenLinearCTStereoDble; + pCS->nMaxLenLinearCTStereoCarb = pCS2->nMaxLenLinearCTStereoCarb; + + pCS->nLenLinearCTStereoDble = pCS2->nLenLinearCTStereoDble; + pCS->nLenLinearCTStereoCarb = pCS2->nLenLinearCTStereoCarb; + + pCS->nLenLinearCTIsotopicStereoDble = pCS2->nLenLinearCTIsotopicStereoDble; + pCS->nLenLinearCTIsotopicStereoCarb = pCS2->nLenLinearCTIsotopicStereoCarb; +} + + +/**********************************************************************************/ +int FillAllStereoDescriptors( CANON_GLOBALS *pCG, + sp_ATOM *at, + int num_atoms, + const AT_RANK *nCanonRank, + const AT_RANK *nAtomNumberCanon, + CANON_STAT *pCS ) +{ + int ret=0, i; + /* initialize zero lengths */ + pCS->nLenLinearCTStereoCarb = 0; + pCS->nLenLinearCTStereoDble = 0; + + /* fill atom by atom */ + for ( i = 0; !ret && i < num_atoms; i ++ ) { + ret = FillSingleStereoDescriptors( pCG, at, (int)nAtomNumberCanon[i], -1, nCanonRank + , pCS->LinearCTStereoCarb, &pCS->nLenLinearCTStereoCarb, pCS->nMaxLenLinearCTStereoCarb + , pCS->LinearCTStereoDble, &pCS->nLenLinearCTStereoDble, pCS->nMaxLenLinearCTStereoDble + , 0 /* bAllene */ ); + } + for ( i = 0; !ret && i < num_atoms; i ++ ) { + ret = FillSingleStereoDescriptors( pCG, at, (int)nAtomNumberCanon[i], -1, nCanonRank + , pCS->LinearCTStereoCarb, &pCS->nLenLinearCTStereoCarb, pCS->nMaxLenLinearCTStereoCarb + , pCS->LinearCTStereoDble, &pCS->nLenLinearCTStereoDble, pCS->nMaxLenLinearCTStereoDble + , 1 /* bAllene */); + } + + return ret; +} + + +/**********************************************************************************/ +/* Find stereo bond parities known in advance */ +int SetKnownStereoBondParities( CANON_GLOBALS *pCG, + sp_ATOM *at, + int num_atoms, + const AT_RANK *nCanonRank, + const AT_RANK *nRank, + const AT_RANK *nAtomNumber ) +{ + int i, j, n, m, j1, k, num_neigh1, num_neigh2, iMax1, parity; + int trans_i1, trans_i2, trans_k1, trans_k2, prev_trans, trans_k, num_set; + int i1, i2, k1, k2, n1, n2, m1, m2, /*stereo_bond_parity,*/ cumulene_len; + + AT_RANK nAtomRank1, nAtomRank2, nAtom1NeighRank; + AT_RANK nNeighRank1[MAX_NUM_STEREO_BONDS], + nNeighRank2[MAX_NUM_STEREO_BONDS]; + AT_RANK nNeighCanonRank1[MAX_NUM_STEREO_BONDS], + nNeighCanonRank2[MAX_NUM_STEREO_BONDS]; + + for ( i1 = 0, num_set = 0; i1 < num_atoms; i1 ++ ) + { + if ( !at[i1].parity || !at[i1].stereo_bond_neighbor[0] ) + continue; + + if ( !PARITY_WELL_DEF(at[i1].parity) ) + continue; + + nAtomRank1 = nRank[i1]; + iMax1 = (int)nAtomRank1-1; + num_neigh1 = at[i1].valence; + + for ( n1 = 0; n1 < MAX_NUM_STEREO_BONDS && + (i2=(int)at[i1].stereo_bond_neighbor[n1]); + n1++ ) + { + i2 --; + + /* found a stereo bond at[i1]-at[i2] adjacent to at[i1] */ + for ( n2 = 0, m=0; + n2 < MAX_NUM_STEREO_BONDS && + (m=(int)at[i2].stereo_bond_neighbor[n2]) && m-1 != i1; + n2++ ) + ; /* locate stereo bond (#n2) at the opposite atom at[i2] */ + + if ( m-1 != i1 || at[i1].stereo_bond_parity[n1] != at[i2].stereo_bond_parity[n2] ) + { + return CT_STEREOCOUNT_ERR; /* program error */ /* */ + } + if ( i1 < i2 ) + continue; /* do not process same bond 2 times */ + + if ( PARITY_KNOWN(at[i1].stereo_bond_parity[n1]) || + !PARITY_VAL(at[i1].stereo_bond_parity[n1]) ) + continue; + + if ( !PARITY_WELL_DEF(at[i1].parity) || + !PARITY_WELL_DEF(at[i2].parity) ) + continue; + + if ( PARITY_VAL(at[i1].stereo_bond_parity[n1]) != AB_PARITY_CALC ) + continue; /* ?? program error ?? should not happen */ /* */ + + /*stereo_bond_parity = PARITY_VAL(at[i1].stereo_bond_parity[n1]);*/ + cumulene_len = BOND_CHAIN_LEN(at[i1].stereo_bond_parity[n1]); + nAtomRank2 = nRank[i2]; + nAtom1NeighRank = nRank[(int)at[i1].neighbor[(int)at[i1].stereo_bond_ord[n1]]]; + num_neigh2 = at[i2].valence; + + /* store ranks of at[i1] stereo bond neighbors except one connected by a stereo bond */ + k = (int)at[i1].stereo_bond_ord[n1]; + trans_i1 = 0; + for ( i = j = 0; i < num_neigh1; i ++ ) + { + if ( i != k ) + { + nNeighRank1[j] = nRank[(int)at[i1].neighbor[i]]; + j ++; + } + } + if ( j == 2 ) + { + if ( nNeighRank1[0] == nNeighRank1[1] ) + { + /* neighbors are constitutionally identical, can't find bond parity */ + continue; + } + trans_i1 = insertions_sort(pCG, nNeighRank1, j, sizeof(nNeighRank1[0]), comp_AT_RANK); + } + + /* store ranks of at[i2] stereo bond neighbors except one connected by a stereo bond */ + k = (int)at[i2].stereo_bond_ord[n2]; + trans_i2 = 0; + for ( i = j = 0; i < num_neigh2; i ++ ) + { + if ( i != k ) + { + nNeighRank2[j] = nRank[(int)at[i2].neighbor[i]]; + j ++; + } + } + + if ( j == 2 ) + { + if ( nNeighRank2[0] == nNeighRank2[1] ) + { + /* neighbors are constitutionally identical, can't find bond parity */ + continue; + } + trans_i2 = insertions_sort(pCG, nNeighRank2, j, sizeof(nNeighRank2[0]), comp_AT_RANK); + } + + prev_trans = -1; + trans_k1 = -2; + trans_k = -4; /* 2004-04-28 */ + + /* find all pairs of atoms that can be mapped on at[i1], at[i2] pair */ + for ( j1 = 0; + j1 <= iMax1 && nAtomRank1==nRank[k1=(int)nAtomNumber[iMax1-j1]]; + j1 ++ ) + { + /* at[k1] is constitutionally equivalent to at[i1] */ + /* find all at[k1] neighbors that have rank nAtomRank2; */ + /* then find at[k2] constitutionally equivalent at at[i2] */ + if ( at[k1].valence != num_neigh1 ) + { + return CT_STEREOCOUNT_ERR; /* program error */ /* */ + } + for ( m1 = 0; m1 < num_neigh1; m1 ++ ) + { + int prev, next, len; + if ( nAtom1NeighRank != nRank[k2=(int)at[k1].neighbor[m1]] ) + { + continue; + } + m2 = -1; /* undefined yet */ + prev = k1; + len = 0; + if ( cumulene_len ) + { + for ( len=0, next = (int)at[k1].neighbor[m1]; len < cumulene_len; len ++ ) { + if ( at[next].valence == 2 && !at[next].num_H ) + { + j = ((int)at[next].neighbor[0] == prev); + prev = next; + next = at[next].neighbor[j]; + } + else + { + break; /* cannot continue */ + } + } + if ( len != cumulene_len || nAtomRank2 != nRank[next] ) + { + continue; /* not found */ + } + k2 = next; + } + if ( at[k2].valence != num_neigh2 ) + { + return CT_STEREOCOUNT_ERR; /* program error */ /* */ + } + + /* store canon. ranks of at[k1] neighbors */ /* use i,j,k,m,n */ + for ( n = j = 0; n < num_neigh1; n ++ ) + { + if ( n != m1 ) + { + i=(int)at[k1].neighbor[n]; + for ( m = 0; m < num_neigh1-1; m ++ ) + { + if ( nRank[i] == nNeighRank1[m] ) + { + nNeighCanonRank1[m] = nCanonRank[i]; + j ++; + break; + } + } + } + } + if ( j != num_neigh1-1 ) + { + return CT_STEREOCOUNT_ERR; /* */ + } + if ( j == 2 ) + { + trans_k1 = insertions_sort(pCG, nNeighCanonRank1, j, sizeof(nNeighCanonRank1[0]), comp_AT_RANK); + } + else + { + trans_k1 = 0; + } + + /* store canon. ranks of at[k2] neighbors */ /* use i,j,k,m,n */ + for ( n = j = 0; n < num_neigh2; n ++ ) + { + i=(int)at[k2].neighbor[n]; + if ( i == prev ) + { /* neighbor belongs to the stereobond */ + m2 = n; + } + else + { + for ( m = 0; m < num_neigh2-1; m ++ ) + { + if ( nRank[i] == nNeighRank2[m] ) + { + nNeighCanonRank2[m] = nCanonRank[i]; + j ++; + break; + } + } + } + } + if ( j != num_neigh2-1 || m2 < 0 ) + { + return CT_STEREOCOUNT_ERR; /* */ + } + if ( j == 2 ) + { + trans_k2 = insertions_sort(pCG, nNeighCanonRank2, j, sizeof(nNeighCanonRank2[0]), comp_AT_RANK); + } + else + { + trans_k2 = 0; + } + trans_k = (trans_k1 + trans_k2)%2; + if ( prev_trans < 0 ) + { + prev_trans = trans_k; + } + else if ( prev_trans != trans_k ) + { + /* was != trans_k1, changed 9-23-2003 */ + break; /* different number of transpositions */ + } + } /* end of the second atom mapping cycle */ + if ( prev_trans >= 0 && prev_trans != trans_k ) { /* was != trans_k1, changed 9-23-2003 */ + break; + } + } /* end of the first atom mapping cycle */ + + if ( prev_trans == trans_k ) + { + /* was == trans_k1, changed 9-23-2003 */ + int z_prod; + + /* all mappings of canonical numbers on the */ + /* stereo bond at[i1]-at[i2] produce equivalent numberings. */ + /* Therefore the stereo bond parity is known at this time. */ + /* parity_1 = at[i1].parity + (trans_i1 + trans_k1 + num_neigh1 - 1) + (int)at[i1].stereo_bond_ord[n1] */ + /* expression in parentheses is equivalent to rank[first neigh] > rank[second neigh] */ + /* same for parity_2. */ + /* parity_2 = at[i2].parity + (trans_i2 + trans_k2 + num_neigh2 - 1) + (int)at[i2].stereo_bond_ord[n2] */ + /* Sum of the two parities (without stereo_bond_z_prod) is: */ + + parity = (at[i1].parity + at[i2].parity + prev_trans + trans_i1 + trans_i2 + + num_neigh1 + num_neigh2 + + (int)at[i1].stereo_bond_ord[n1] + (int)at[i2].stereo_bond_ord[n2] ) %2; + + z_prod = at[i1].stereo_bond_z_prod[n1]; + if ( MIN_DOT_PROD > abs(z_prod)) { + parity = AB_PARITY_UNDF; /* undefined because of geometry */ + } + else + { + parity = (z_prod > 0)? 2 - parity : 1 + parity; + } + at[i1].stereo_bond_parity[n1] = ALL_BUT_PARITY(at[i1].stereo_bond_parity[n1]) | parity; + at[i2].stereo_bond_parity[n2] = ALL_BUT_PARITY(at[i2].stereo_bond_parity[n2]) | parity; + num_set ++; + } + } + } + + return num_set; +} + + +/**********************************************************************************/ +/* Find stereo center parities known in advance */ +int MarkKnownEqualStereoBondParities( sp_ATOM *at, + int num_atoms, + const AT_RANK *nRank, + const AT_RANK *nAtomNumber ) +{ +int j, n, m, j1, num_neigh1, num_neigh2, iMax1; +int num_set, /*num_sb1, num_sb2,*/ bDifferentParities; +int i1, i2, k1, k2, n1, n2, m1, m2, s1, s2, stereo_bond_parity, stereo_bond_parity2, cumulene_len; +AT_RANK nAtomRank1, nAtomRank2, nAtom1NeighRank, nAtom2NeighRank; + + num_set = 0; + + for ( i1 = 0, num_set = 0; i1 < num_atoms; i1 ++ ) + { + if ( !at[i1].parity || !at[i1].stereo_bond_neighbor[0] ) + continue; + + nAtomRank1 = nRank[i1]; + iMax1 = (int)nAtomRank1-1; + num_neigh1 = at[i1].valence; + + /* count stereogenic bonds adjacent to at[i1] */ + for ( n1 = 0; + n1 < MAX_NUM_STEREO_BONDS && at[i1].stereo_bond_neighbor[n1]; + n1++ ) ; + + + /*num_sb1 = n1;*/ + /* search for bonds possibly constitutionally equivalent to each of the adjacent bonds */ + /* and find if all of them have same already known parity */ + + for ( n1 = 0, i2 = 0; + n1 < MAX_NUM_STEREO_BONDS && (i2=(int)at[i1].stereo_bond_neighbor[n1]); + n1++ ) + { + i2 --; + + nAtomRank2 = nRank[i2]; + if ( nAtomRank2 < nAtomRank1 || nAtomRank2 == nAtomRank1 && i1 < i2 ) + { + /* An attempt to reduce unnecessary repetitions. */ + /* We still have repetitions because we do not accumulate a list of */ + /* processed (nAtomRank2, nAtomRank1) pairs. */ + continue; + } + + bDifferentParities = -1; /* parities have not been compared yet */ + + /* found a stereo bond at[i1]-at[i2] (adjacent to at[i1]) */ + /* + if ( !PARITY_KNOWN(at[i1].stereo_bond_parity[n1]) || (at[i1].stereo_bond_parity[n1] & KNOWN_PARITIES_EQL) ) + { + continue; + } + */ + if ( at[i1].stereo_bond_parity[n1] & KNOWN_PARITIES_EQL ) + continue; + + + /* stereo bond has known or unknown parity; we have not checked it yet */ + + for ( n2 = 0; + n2 < MAX_NUM_STEREO_BONDS && at[i2].stereo_bond_neighbor[n2]; + n2++ ) + ; + + /*num_sb2 = n2;*/ + for ( n2 = 0, m = 0; + n2 < MAX_NUM_STEREO_BONDS && + (m=(int)at[i2].stereo_bond_neighbor[n2]) && + m-1 != i1; + n2++ ) ; + + if ( m-1 != i1 || at[i1].stereo_bond_parity[n1] != at[i2].stereo_bond_parity[n2] ) + { + return CT_STEREOCOUNT_ERR; /* program error: stereo bonds data in two directions are different */ /* */ + } + + stereo_bond_parity = PARITY_VAL(at[i1].stereo_bond_parity[n1]); + cumulene_len = BOND_CHAIN_LEN(at[i1].stereo_bond_parity[n1]); + nAtom1NeighRank = nRank[(int)at[i1].neighbor[(int)at[i1].stereo_bond_ord[n1]]]; + nAtom2NeighRank = nRank[(int)at[i2].neighbor[(int)at[i2].stereo_bond_ord[n2]]]; + num_neigh2 = at[i2].valence; + + /* find all pairs of atoms that possibly can be mapped on at[i1], at[i2] pair */ + /* (we may also find pairs that cannot be mapped, but we cannot miss any pair */ + /* that can be mapped) */ + + for ( j1 = 0; j1 <= iMax1 && nAtomRank1==nRank[k1=(int)nAtomNumber[iMax1-j1]]; j1 ++ ) + { + /* at[k1] is constitutionally equivalent to at[i1] */ + /* find all at[k1] stereo bond neighbors at[k2] that have rank nAtomRank2; */ + /* then find at[k2] constitutionally equivalent at at[i2] */ + + if ( at[k1].valence != num_neigh1 ) + return CT_STEREOCOUNT_ERR; /* program error */ /* */ + + if ( !at[k1].bHasStereoOrEquToStereo ) + at[k1].bHasStereoOrEquToStereo = 1; + + /* -- do not check number of stereo bonds, check bonds themselves -- + for ( s1 = 0; s1 < MAX_NUM_STEREO_BONDS && at[k1].stereo_bond_neighbor[s1]; s1++ ) + ; + if ( num_sb1 != s1 ) { + bDifferentParities = 1; + } + */ + + for ( m1 = 0; m1 < num_neigh1; m1 ++ ) + { + /* Looking for at[k1] neighbor with nRank=nAtom1NeighRank. */ + /* This neighbor may be on the bond constit. equivalent to at[i1]-at[i2] stereo bond */ + /* (or may be constit. equivalent an adjacent to at[i1] atom in a stereogenic cumulene chain) */ + int prev, next, len; + if ( nAtom1NeighRank != nRank[k2=(int)at[k1].neighbor[m1]] ) + continue; + + /* found at[k1] neighbor with nRank=nAtom1NeighRank */ + + m2 = -1; /* undefined yet */ + prev = k1; + len = 0; + + /* if cumulene then bypass the cumulene chain */ + + if ( cumulene_len ) + { + + for ( len=0, next = (int)at[k1].neighbor[m1]; len < cumulene_len; len ++ ) + { + if ( at[next].valence == 2 && !at[next].num_H ) + { + j = ((int)at[next].neighbor[0] == prev); + prev = next; + next = at[next].neighbor[j]; + } + else + break; /* cannot continue: end of cumulene chain */ + } + + if ( len != cumulene_len || nAtomRank2 != nRank[next] ) + continue; /* cumulene chain not found at this neighbor */ + + if ( nAtom2NeighRank != nRank[prev] ) + /* continue; */ /* ??? program error ??? If not, must be a very rare event */ + return CT_STEREOCOUNT_ERR; /* */ + + k2 = next; + } + + /* a connected pair of constit. equivalent atoms found */ + + if ( at[k2].valence != num_neigh2 ) + return CT_STEREOCOUNT_ERR; /* program error */ /* */ + + for ( n = 0; n < num_neigh2; n ++ ) + { + if ( prev == (int)at[k2].neighbor[n] ) + { + m2 = n; /* found bond from the opposite end of a possibly stereogenic bond */ + break; + } + } + + if ( m2 < 0 ) + return CT_STEREOCOUNT_ERR; /* program error: opposite direction bond not found */ /* */ + + if ( !at[k2].bHasStereoOrEquToStereo ) + at[k2].bHasStereoOrEquToStereo = 1; + + + /* check if atoms at[k1] and at[k2] are connected by a stereo bond */ + for ( s1 = 0, m = 0; + s1 < MAX_NUM_STEREO_BONDS && + (m=(int)at[k1].stereo_bond_neighbor[s1]) && + m-1 != k2; + s1++ ) + ; + if ( m-1 != k2 ) + { + bDifferentParities = 1; /* cannot find the stereo bond */ + at[k1].bHasStereoOrEquToStereo = + at[k2].bHasStereoOrEquToStereo = 2; + continue; + } + + /* -- do not check number of stereo bonds, check bonds themselves -- + for ( s2 = 0; s2 < MAX_NUM_STEREO_BONDS && at[k2].stereo_bond_neighbor[s2]; s2++ ) + ; + if ( num_sb2 != s2 ) { + bDifferentParities = 1; + continue; + } + */ + + for ( s2 = 0, m = 0; + s2 < MAX_NUM_STEREO_BONDS && + (m=(int)at[k2].stereo_bond_neighbor[s2]) && + m-1 != k1; + s2++ ) ; + + if ( m-1 != k1 ) + { + /* + bDifferentParities = 1; // cannot find the stereo bond + continue; + */ + return CT_STEREOCOUNT_ERR; /* program error: opposite direction bond not found */ /* */ + } + + if ( at[k1].stereo_bond_parity[s1] != at[k2].stereo_bond_parity[s2] ) + { + bDifferentParities = 1; + continue; + } + stereo_bond_parity2 = PARITY_VAL(at[k1].stereo_bond_parity[s1]); + if ( stereo_bond_parity2 != stereo_bond_parity ) + { + bDifferentParities = 1; + continue; + } + if ( stereo_bond_parity2 == stereo_bond_parity && bDifferentParities < 0 ) + { + bDifferentParities = 0; + } + } + } + + /* mark equal parities */ + if ( 0 == bDifferentParities && PARITY_KNOWN( stereo_bond_parity ) ) + { + for ( j1 = 0; + j1 <= iMax1 && nAtomRank1==nRank[k1=(int)nAtomNumber[iMax1-j1]]; + j1 ++ ) + { + /* at[k1] is constitutionally equivalent to at[i1] */ + for ( s1 = 0, k2 = 0; s1 < MAX_NUM_STEREO_BONDS && (k2=(int)at[k1].stereo_bond_neighbor[s1]); s1++ ) + { + k2--; + if ( nRank[k2] == nAtomRank2 ) + { + int b1, b2; + for ( s2 = 0, m = 0; + s2 < MAX_NUM_STEREO_BONDS && + (m=(int)at[k2].stereo_bond_neighbor[s2]) && + m-1 != k1; + s2++ ) ; + + if ( m-1 != k1 ) + { + return CT_STEREOCOUNT_ERR; /* program error */ /* */ + } + /* mark the stereo bonds */ + b1 = !(at[k1].stereo_bond_parity[s1] & KNOWN_PARITIES_EQL); + b2 = !(at[k2].stereo_bond_parity[s2] & KNOWN_PARITIES_EQL); + if ( 2 == b1 + b2 ) + { + at[k1].stereo_bond_parity[s1] |= KNOWN_PARITIES_EQL; + at[k2].stereo_bond_parity[s2] |= KNOWN_PARITIES_EQL; + num_set ++; + } + else if ( b1 || b2 ) + { + return CT_STEREOCOUNT_ERR; /* program error */ /* */ + } + } + } + } + } + } + } + + return num_set; +} + + + +#if ( REMOVE_KNOWN_NONSTEREO == 1 ) /* { */ + +/**********************************************************************************/ +/* Return next atom number (and its canon. rank) on the path prev->cur->next */ +/* in order of ascending canonical ranks of the next atoms: *cr(output) > *cr(input) */ +/* To start the sequence let *cr=0 */ +/* If no more neighbors available the return value = 0; if successgul then the return value = 1. */ +int GetNextNeighborAndRank( sp_ATOM *at, + AT_RANK cur, + AT_RANK prev, + AT_RANK *n, + AT_RANK *cr, + const AT_RANK *nCanonRank ) +{ +int i, val; +AT_RANK cr1 = MAX_ATOMS+1, j, j1=MAX_ATOMS+1, crj; + + for ( i = 0, val = at[(int)cur].valence; i < val; i ++ ) + { + if ( (j=at[cur].neighbor[i]) != prev && + cr1 > (crj=nCanonRank[(int)j]) && crj > *cr ) + { + cr1 = crj; + j1 = j; + } + } + if ( cr1 <= MAX_ATOMS ) + { + *cr = cr1; + *n = (AT_RANK)j1; + return 1; + } + + return 0; /* program error */ /* */ +} + + +/**********************************************************************************/ +/* Find next pair of neighbors having the next greater canon. rank */ +/* The neighbors should be constitutionally identical and traversed simultaneouly or not traversed at all */ +/* If a bond cur1-*n1 or cur2-*n2 is a stereo bond then reject if their stereo bond parities are different or */ +/* cannot be calculated without breaking ties. */ +int GetAndCheckNextNeighbors( sp_ATOM *at, + AT_RANK cur1, + AT_RANK prev1, + AT_RANK cur2, + AT_RANK prev2, + AT_RANK *n1, + AT_RANK *n2, + AT_RANK *nVisited1, + AT_RANK *nVisited2, + const AT_RANK *nRank, + const AT_RANK *nCanonRank ) +{ + AT_RANK cr1, cr2, s1, s2; + int i1, i2, k1, k2; + + cr1 = ( *n1 > MAX_ATOMS )? 0 : nCanonRank[(int)*n1]; + cr2 = ( *n2 > MAX_ATOMS )? 0 : nCanonRank[(int)*n2]; + + if ( !GetNextNeighborAndRank( at, cur1, prev1, n1, &cr1, nCanonRank ) || + !GetNextNeighborAndRank( at, cur2, prev2, n2, &cr2, nCanonRank ) || + nRank[(int)*n1] != nRank[(int)*n2] || nVisited1[(int)*n1] != nVisited2[(int)*n2] ) + { + return 0; /* program error; no breakpoint here */ /* */ + } + + /* Even though the bond or cumulene might have already been checked, check it: this is */ + /* the only place we can check stereo bonds and cumulenes that are not edges of the DFS tree */ + /* The code works both for a stereo bond and a stereogenic cumulene. */ + for ( i1 = 0, k1 = 0; + i1 < MAX_NUM_STEREO_BONDS && + (s1=at[cur1].stereo_bond_neighbor[i1]) && + !(k1=(at[cur1].neighbor[(int)at[cur1].stereo_bond_ord[i1]] == *n1)); + i1 ++ ) ; + + for ( i2 = 0, k2 = 0; + i2 < MAX_NUM_STEREO_BONDS && + (s2=at[cur2].stereo_bond_neighbor[i2]) && + !(k2=(at[cur2].neighbor[(int)at[cur2].stereo_bond_ord[i2]] == *n2)); + i2 ++ ) ; + + if ( k1 != k2 ) + { + return 0; /* possibly not an error: constit. equivalent atoms on a stereo bond and not on a stereo bond */ + } + + if ( k1 /* yes, it is a stereo bond */ && + ( at[cur1].stereo_bond_parity[i1] != at[cur2].stereo_bond_parity[i2] || + /* PARITY_KNOWN (at[cur1].stereo_bond_parity[i1] ) */ /* replaced 08-13-2002 with the next: */ + !PARITY_WELL_DEF (at[cur1].stereo_bond_parity[i1] ) /* it suffices to check only one parity */ + ) ) + { + return 0; /* different or (currently) unknown stereo bond parities */ + } + + return 1; /* stereo bonds have known parities */ +} + + +/**********************************************************************************/ +/* Simultaneously DFS-traverse 2 paths starting at the bonds prev1->cur1 and prev2->cur2 */ +/* The two paths MUST go through the pairs of constitutionally identical atoms, each atom being on one path. */ +/* Reject if encountered atoms having currently unknown (without breaking ties) */ +/* parities or having different known or unknown or undefined parities. */ +/* Save length of the path into nVisited[cur. atom number]. */ +/* Only one nVisited[] array is sufficient because the paths from the beginning are in different ring systems. */ +AT_RANK PathsHaveIdenticalKnownParities( sp_ATOM *at, + AT_RANK prev1, + AT_RANK cur1, + AT_RANK prev2, + AT_RANK cur2, + AT_RANK *nVisited1, + AT_RANK *nVisited2, + const AT_RANK *nRank, + const AT_RANK *nCanonRank, + AT_RANK nLength ) +{ + int k; + AT_RANK n1, n2; + + nLength ++; /* number of successfully traversed pairs of atoms */ + nVisited1[cur1] = nLength; + nVisited2[cur2] = nLength; + + /* the atoms must be either both stereogenic and have well-defined parities or non-stereogenic at all. */ + if ( at[cur1].stereo_atom_parity != at[cur2].stereo_atom_parity || + at[cur1].stereo_atom_parity && !PARITY_WELL_DEF (at[cur1].stereo_atom_parity) + ) + { + return 0; /* Reject: Different or unknown in advance parities */ + } + + if ( at[cur1].valence != at[cur2].valence ) + { + return 0; /* program error */ /* */ + } + + if ( at[cur1].valence == 1 ) + { + return nLength; /* so far success */ + } + + + for ( k = 1, n1 = MAX_ATOMS+1, n2=MAX_ATOMS+1; k < at[cur1].valence; k ++ ) + { + /* start from 1: since we do not go back, we have only (at[cur1].valence-1) bonds to try */ + + if ( !GetAndCheckNextNeighbors( at, cur1, prev1, cur2, prev2, + &n1, &n2, nVisited1, nVisited2, + nRank, nCanonRank ) ) + { + return 0; /* different neighbors */ + } + + /* In a DFS we do not traverse already visited atoms */ + if ( !nVisited1[n1] ) + { + /* recursion */ + if ( ! (nLength = PathsHaveIdenticalKnownParities( at, cur1, n1, cur2, n2, nVisited1, nVisited2, nRank, nCanonRank, nLength ) ) ) + { + return 0; + } + } + } + + /* To be on a safe side, recheck after all nVisited[] have been set */ + for ( k = 1, n1 = MAX_ATOMS+1, n2=MAX_ATOMS+1; k < at[cur1].valence; k ++ ) + { + /* start from 1: since we do not go back, we have only (at[cur1].valence-1) bonds to try */ + if ( !GetAndCheckNextNeighbors( at, cur1, prev1, cur2, prev2, + &n1, &n2, nVisited1, nVisited2, + nRank, nCanonRank ) ) + { + return 0; /* different neighbors */ + } + } + + return nLength; +} + + +/**********************************************************************************/ +/* Remove stereo marks from the bonds that are known to be non-stereo */ +/* (compare neighbors if they are attached by cut-edges) */ +int RemoveKnownNonStereoBondParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, + const AT_RANK *nRank, CANON_STAT *pCS) +{ + int j, n, m, ret; + + int i1, n1, s2; + AT_RANK nAtomRank1, nAtomRank2, neigh[3], opposite_atom, *nVisited = NULL; + ret = 0; + for ( i1 = 0; i1 < num_atoms; i1 ++ ) { + if ( at[i1].valence != 3 || !at[i1].stereo_bond_neighbor[0] ) { + continue; + } + for ( n1 = 0; n1 < MAX_NUM_STEREO_BONDS && (s2=at[i1].stereo_bond_neighbor[n1]); n1++ ) { + if ( !PARITY_CALCULATE(at[i1].stereo_bond_parity[n1]) && PARITY_WELL_DEF(at[i1].stereo_bond_parity[n1]) ) { + continue; + } + opposite_atom = (AT_RANK)(s2-1); + /* s2 = at[i1].neighbor[m=(int)at[i1].stereo_bond_ord[n1]]; */ + m=(int)at[i1].stereo_bond_ord[n1]; + for ( j = 0, n = 0; j < at[i1].valence; j ++ ) { + /* if ( at[i1].neighbor[j] != s2 ) */ + if ( j != m ) + { + neigh[n++] = at[i1].neighbor[j]; + } + } + if ( n > 2 ) { + ret = CT_STEREOBOND_ERROR; /* */ + goto exit_function; + } + if ( n != 2 || nRank[(int)neigh[0]] != nRank[(int)neigh[1]] ) { + continue; /* may happen if another half-bond has not a defined parity */ + } + if ( at[i1].nRingSystem == at[(int)neigh[0]].nRingSystem ) { + continue; /* no more ring system membership check is necessary because */ + } /* the two neighbors are to be constitutionally equivalent atoms */ + if ( !nVisited && !(nVisited = (AT_RANK*)inchi_malloc( sizeof(nVisited[0])*num_atoms ) ) ) { + ret = CT_OUT_OF_RAM; /* */ + goto exit_function; + } + memset( nVisited, 0, sizeof(nVisited[0])*num_atoms ); + nVisited[i1] = 1; + if ( PathsHaveIdenticalKnownParities( at, (AT_RANK)i1, neigh[0], (AT_RANK)i1, neigh[1], nVisited, nVisited, nRank, nCanonRank, 1 ) ) { + if ( !RemoveOneStereoBond( at, i1, /* atom number*/ n1 /* stereo bond number*/ ) ) { + ret = CT_STEREOBOND_ERROR; /* */ + goto exit_function; + } + n1 --; /* cycle counter may temporarily become negative */ + /* Remove from pCS */ + nAtomRank1 = inchi_max( nCanonRank[i1], nCanonRank[opposite_atom]); + nAtomRank2 = inchi_min( nCanonRank[i1], nCanonRank[opposite_atom]); + for ( n = 0, m = pCS->nLenLinearCTStereoDble-1; n <= m; n ++ ) { + if ( pCS->LinearCTStereoDble[n].at_num1 == nAtomRank1 && + pCS->LinearCTStereoDble[n].at_num2 == nAtomRank2 ) { + if ( n < m ) { /* remove pCS->LinearCTStereoDble[n] */ + memmove( pCS->LinearCTStereoDble + n, + pCS->LinearCTStereoDble + n + 1, + (m-n)*sizeof(pCS->LinearCTStereoDble[0]) ); + } + pCS->nLenLinearCTStereoDble --; +#if ( bRELEASE_VERSION == 0 ) + pCS->bExtract |= EXTR_KNOWN_USED_TO_REMOVE_PARITY; +#endif + m = -1; /* set flag "found" */ + break; + } + } + if ( m >= 0) { + ret = CT_STEREOCOUNT_ERR; /* bond not found */ + goto exit_function; + } + ret ++; /* number of removed known in advance non-stereo bonds */ + } + } + } + +exit_function: + + if ( nVisited ) { + inchi_free( nVisited ); + } + return ret; +} +#endif /* } REMOVE_KNOWN_NONSTEREO */ + + + +/**********************************************************************************/ +/* Find stereo center parities known in advance */ +int SetKnownStereoCenterParities( CANON_GLOBALS *pCG, + sp_ATOM *at, + int num_atoms, + const AT_RANK *nCanonRank, + const AT_RANK *nRank, + const AT_RANK *nAtomNumber ) +{ + int i, j, n, m, j1, k, num_neigh, iMax, trans_i, trans_k, prev_trans, num_set; + AT_RANK nAtomRank; + AT_RANK nNeighRank[MAX_NUM_STEREO_ATOM_NEIGH]; + AT_RANK nNeighCanonRank[MAX_NUM_STEREO_ATOM_NEIGH]; + + for ( i = 0, num_set = 0; i < num_atoms; i ++ ) + { + if ( !at[i].parity || at[i].stereo_bond_neighbor[0] ) + { + continue; + } + if ( at[i].stereo_atom_parity != AB_PARITY_CALC || + !PARITY_WELL_DEF(at[i].parity) ) + { + continue; + } + num_neigh = at[i].valence; + for ( j = 0; j < num_neigh; j ++ ) + { + nNeighRank[j] = nRank[(int)at[i].neighbor[j]]; + } + nAtomRank = nRank[i]; + if ( num_neigh == 1 ) + { + /* other neighbors must be implicit H */ + at[i].stereo_atom_parity = at[i].parity; + trans_i = 0; + } + else + { + /* sort constitutional equivalence ranks of the neighbors */ + trans_i = insertions_sort(pCG, nNeighRank, num_neigh, sizeof(nNeighRank[0]), comp_AT_RANK); + for ( j = 1; j < num_neigh; j ++ ) + { + if ( nNeighRank[j-1] == nNeighRank[j] ) + break; /* at[i] has consitutionally identical neighbors */ + } + if ( j < num_neigh ) + { + /* at least 2 neighbors are const. identical; parity cannot be calculated at this time */ + continue; /* try next stereo atom */ + } + } + prev_trans = -1; + trans_k = 0; + /* find neighbors of constitutionally equivalent stereo centers */ + /* and at[i] parities in case those centers are mapped on at[i] */ + for ( iMax = (int)nAtomRank-1, j1 = 0; + j1 <= iMax && nAtomRank==nRank[k=(int)nAtomNumber[iMax-j1]]; + j1 ++ ) + { + /* at[k] is constitutionally equivalent to at[i] */ + if ( (int)at[k].valence != num_neigh ) + { + return CT_STEREOCOUNT_ERR; /* */ + } + /* -- commented out to accept non-stereogenic atoms since -- + -- they may participate in mapping stereocenters 12-16-2003 -- + if ( !PARITY_VAL(at[k].parity) ) { + continue; // not a stereogenic atom + } + */ + for ( j = 0, m = 0; m < num_neigh; m ++ ) + { + for ( n = 0; n < num_neigh; n ++ ) + { + if ( nRank[(int)at[k].neighbor[n]] == nNeighRank[m] ) + { + /* save canonical numbers (ranks) of the neighbors in + * order of increasing constit. equivalence ranks */ + nNeighCanonRank[m] = nCanonRank[(int)at[k].neighbor[n]]; + j ++; + break; + } + } + } + if ( j != num_neigh ) + { + return CT_STEREOCOUNT_ERR; /* */ + } + trans_k = insertions_sort(pCG, nNeighCanonRank, num_neigh, sizeof(nNeighCanonRank[0]), comp_AT_RANK); + trans_k %= 2; + if ( prev_trans < 0 ) + { + prev_trans = trans_k; + } + else if ( trans_k != prev_trans ) + { + /* different mappings may produce different parities. Cannot find the parity at this time */ + /* this may happen when a set of constit. equivalent atoms has non-contiguous canonical numbers */ + break; + } + } + if ( trans_k == prev_trans ) + { + at[i].stereo_atom_parity = 2 - (at[i].parity + trans_i + prev_trans ) % 2; + num_set ++; + } + } + + return num_set; +} + + + +#if ( REMOVE_KNOWN_NONSTEREO == 1 ) /* { */ + +/**********************************************************************************/ +/* DFS along paths starting from the stereocenter through pairs of cut-edges */ +int RemoveKnownNonStereoCenterParities( CANON_GLOBALS *pCG, + sp_ATOM *at, + int num_atoms, + const AT_RANK *nCanonRank, + const AT_RANK *nRank, + CANON_STAT *pCS) +{ + int i, j, n, m, k, num_neigh, ret = 0; + /*AT_RANK nAtomRank;*/ + AT_RANK nNeighRank[MAX_NUM_STEREO_ATOM_NEIGH], nNeighOrd[MAX_NUM_STEREO_ATOM_NEIGH]; + AT_RANK *nVisited = NULL; + + for ( i = 0; i < num_atoms; i ++ ) + { + if ( !at[i].parity || at[i].stereo_bond_neighbor[0] ) + { + continue; + } + if ( !PARITY_CALCULATE(at[i].stereo_atom_parity) && PARITY_WELL_DEF(at[i].stereo_atom_parity) ) + { + continue; + } + num_neigh = at[i].valence; + for ( j = 0; j < num_neigh; j ++ ) + { + nNeighRank[j] = nRank[(int)at[i].neighbor[j]]; + nNeighOrd[j] = j; + } + /*nAtomRank = nRank[i];*/ + if ( num_neigh == 1 ) + { + continue; + } + pCG->m_pn_RankForSort = nNeighRank; + insertions_sort(pCG, nNeighOrd, num_neigh, sizeof(nNeighRank[0]), CompRanksOrd); + for ( j = k = 1; k && j < num_neigh; j ++ ) + { + if ( at[i].nRingSystem != at[(int)at[i].neighbor[(int)nNeighOrd[j]]].nRingSystem && + /* no more ring system membership check is necessary because */ + /* the two neighbors are to be constitutionally equivalent atoms: */ + nNeighRank[nNeighOrd[j-1]] == nNeighRank[nNeighOrd[j]] ) + { + k = j; + do + { + if ( !nVisited && !(nVisited = (AT_RANK*)inchi_malloc( sizeof(nVisited[0])*num_atoms ) ) ) + { + ret = CT_OUT_OF_RAM; /* */ + goto exit_function; + } + memset( nVisited, 0, sizeof(nVisited[0])*num_atoms ); + nVisited[i] = 1; + if ( PathsHaveIdenticalKnownParities( at, (AT_RANK)i, at[i].neighbor[(int)nNeighOrd[j-1]], + (AT_RANK)i, at[i].neighbor[(int)nNeighOrd[k]], + nVisited, nVisited, nRank, nCanonRank, 1 ) ) + { + at[i].parity = 0; /* remove parity */ + at[i].stereo_atom_parity = 0; + at[i].final_parity = 0; + /* at[i].bHasStereoOrEquToStereo = 0; */ + for ( n = 0, m = pCS->nLenLinearCTStereoCarb-1; n <= m; n ++ ) + { + if ( pCS->LinearCTStereoCarb[n].at_num == nCanonRank[i] ) + { + if ( n < m ) + { /* remove pCS->LinearCTStereoCarb[n] */ + memmove( pCS->LinearCTStereoCarb + n, + pCS->LinearCTStereoCarb + n + 1, + (m-n)*sizeof(pCS->LinearCTStereoCarb[0]) ); + } + pCS->nLenLinearCTStereoCarb --; + k = 0; + +#if ( bRELEASE_VERSION == 0 ) + pCS->bExtract |= EXTR_KNOWN_USED_TO_REMOVE_PARITY; +#endif + + break; + } + } + if ( k ) + { + ret = CT_STEREOCOUNT_ERR; /* */ + goto exit_function; + } + ret ++; + break; + } + } while ( ++k < num_neigh && nNeighRank[nNeighOrd[j-1]] == nNeighRank[nNeighOrd[k]] ); + } + } + } + + +exit_function: + + if ( nVisited ) + inchi_free( nVisited ); + + return ret; +} + + + +#endif /* } REMOVE_KNOWN_NONSTEREO */ + +/**********************************************************************************/ +/* Find stereo center parities known in advance */ +int MarkKnownEqualStereoCenterParities( sp_ATOM *at, + int num_atoms, + const AT_RANK *nRank, + const AT_RANK *nAtomNumber ) +{ + int i, j1, k, num_centers, iMax, bDifferentParities; + AT_RANK nAtomRank; + int parity, parity_k; + + num_centers = 0; + for ( i = 0; i < num_atoms; i ++ ) + { + if ( !at[i].parity || at[i].stereo_bond_neighbor[0] ) + { + continue; + } + if ( at[i].bHasStereoOrEquToStereo ) + { + continue; /* already marked */ + } + if ( /*!PARITY_KNOWN(at[i].stereo_atom_parity) ||*/ (at[i].stereo_atom_parity & KNOWN_PARITIES_EQL) ) + { + continue; + } + parity = PARITY_VAL(at[i].stereo_atom_parity); + if ( parity == AB_PARITY_NONE ) + { + continue; + } + nAtomRank = nRank[i]; + bDifferentParities = -1; + /* find constitutionally equivalent stereo centers and compare their known at this time parities */ + for ( iMax = (int)nAtomRank-1, j1 = 0; j1 <= iMax && nAtomRank==nRank[k=(int)nAtomNumber[iMax-j1]]; j1 ++ ) + { + /* at[k] is constitutionally equivalent to at[i] */ + parity_k = PARITY_VAL(at[k].stereo_atom_parity); + if ( parity_k != parity ) + { + bDifferentParities = 1; + } + else if ( parity_k == parity && bDifferentParities < 0 ) + { + bDifferentParities = 0; + } + if ( !parity_k ) + { + at[k].bHasStereoOrEquToStereo = 2; + } + else if ( !at[k].bHasStereoOrEquToStereo ) + { + at[k].bHasStereoOrEquToStereo = 1; + } + } + if ( 0 == bDifferentParities && PARITY_KNOWN( parity ) ) + { + for ( iMax = (int)nAtomRank-1, j1 = 0; j1 <= iMax && nAtomRank==nRank[k=(int)nAtomNumber[iMax-j1]]; j1 ++ ) + { + /* at[k] is constitutionally equivalent to at[i] */ + at[k].stereo_atom_parity |= KNOWN_PARITIES_EQL; + num_centers ++; + } + } + } + + return num_centers; +} + + +/*****************************************************************************/ +/* invert known parities in at[] and in pCS->LinearCTStereoDble */ +/* pCS->LinearCTStereoCarb */ +/* nCanonRank[] contains canonical ranks used to fill pCS->LinearCTStereo... */ +/* nAtomNumberCanon[] will be filled with atom numbers in canonical order */ +/*****************************************************************************/ +int InvertStereo( sp_ATOM *at, + int num_at_tg, + AT_RANK *nCanonRank, + AT_RANK *nAtomNumberCanon, + CANON_STAT *pCS, + int bInvertLinearCTStereo ) +{ + int i, j, j1, j2, num_changes, parity, cumulene_len; + + num_changes = 0; + for ( i = 0; i < num_at_tg; i ++ ) + { + nAtomNumberCanon[(int)nCanonRank[i]-1] = i; + } + for ( i = 0; i < pCS->nLenLinearCTStereoCarb; i ++ ) + { + parity = pCS->LinearCTStereoCarb[i].parity; + if ( ATOM_PARITY_WELL_DEF( parity ) ) + { + j = nAtomNumberCanon[(int)pCS->LinearCTStereoCarb[i].at_num-1]; + if ( PARITY_WELL_DEF(at[j].parity) ) + { + at[j].parity ^= AB_INV_PARITY_BITS; + } + + else { + goto exit_error; /* inconsistency */ + } + if ( bInvertLinearCTStereo ) + { + pCS->LinearCTStereoCarb[i].parity ^= AB_INV_PARITY_BITS; + } + num_changes ++; + if ( PARITY_WELL_DEF(at[j].stereo_atom_parity) ) + { + at[j].stereo_atom_parity ^= AB_INV_PARITY_BITS; + } + if ( PARITY_WELL_DEF(at[j].final_parity) ) + { + at[j].final_parity ^= AB_INV_PARITY_BITS; + } + } + } + for ( i = 0; i < pCS->nLenLinearCTStereoDble; i ++ ) + { + parity = pCS->LinearCTStereoDble[i].parity; + if ( ATOM_PARITY_WELL_DEF( parity ) ) + { + j1 = nAtomNumberCanon[(int)pCS->LinearCTStereoDble[i].at_num1-1]; + cumulene_len = BOND_CHAIN_LEN(at[j1].stereo_bond_parity[0]); + if ( cumulene_len % 2 ) + { + /* invert only in case of allene */ + j2 = nAtomNumberCanon[(int)pCS->LinearCTStereoDble[i].at_num2-1]; + /* checks for debug only */ + if ( 1 < MAX_NUM_STEREO_BONDS ) + { + if ( at[j1].stereo_bond_neighbor[1] || + at[j2].stereo_bond_neighbor[1] ) + { + goto exit_error; /* inconsitency: atom has more than one cumulene bond */ + } + } + if ( cumulene_len != BOND_CHAIN_LEN(at[j2].stereo_bond_parity[0]) || + j1+1 != at[j2].stereo_bond_neighbor[0] || + j2+1 != at[j1].stereo_bond_neighbor[0] ) + { + goto exit_error; /* inconsitency: atoms should refer to each other */ + } + /* invert parities */ + if ( PARITY_WELL_DEF(at[j1].parity) && PARITY_WELL_DEF(at[j2].parity) ) { + j = inchi_min( j1, j2 ); + at[j].parity ^= AB_INV_PARITY_BITS; /* for reversability always invert only atom with the smaller number */ + } + else + { + goto exit_error; /* inconsistency */ + } + if ( bInvertLinearCTStereo ) + { + pCS->LinearCTStereoDble[i].parity ^= AB_INV_PARITY_BITS; + } + num_changes ++; + if ( PARITY_WELL_DEF(at[j1].stereo_bond_parity[0]) ) + { + at[j1].stereo_bond_parity[0] ^= AB_INV_PARITY_BITS; + } + if ( PARITY_WELL_DEF(at[j2].stereo_bond_parity[0]) ) { + at[j2].stereo_bond_parity[0] ^= AB_INV_PARITY_BITS; + } + } + } + } + + return num_changes; + +exit_error: + return CT_STEREOCOUNT_ERR; +} + + +/**********************************************************************************/ +/* Make sure atoms stereo descriptors fit molecular symmetry and remove */ +/* parity from obviously non-stereo atoms and bonds */ +int FillOutStereoParities( sp_ATOM *at, + int num_atoms, + const AT_RANK *nCanonRank, + const AT_RANK *nAtomNumberCanon, + const AT_RANK *nRank, + const AT_RANK *nAtomNumber, + CANON_STAT *pCS, + CANON_GLOBALS *pCG, + int bIsotopic ) +{ + int ret; + /* unmark atoms with 2 or more constitutionally equivalent neighbors */ + /* such that there is no path through them to an atom with parity */ + ret = UnmarkNonStereo( pCG, at, num_atoms, nRank, nAtomNumber, bIsotopic ); + if ( ret < 0 ) + return ret; /* program error? */ /* */ + ret = FillAllStereoDescriptors( pCG, at, num_atoms, nCanonRank, nAtomNumberCanon, pCS ); /* ret<0: error */ + if ( !ret ) { + ret = pCS->nLenLinearCTStereoCarb + pCS->nLenLinearCTStereoDble; + } + if ( ret < 0 ) { + return ret; /* program error? */ /* */ + } + + if ( ret >= 0 ) { + int ret2; + ret2 = SetKnownStereoCenterParities( pCG, at, num_atoms, nCanonRank, nRank, nAtomNumber ); + if ( ret2 >= 0 ) { + ret2 = MarkKnownEqualStereoCenterParities( at, num_atoms, nRank, nAtomNumber ); + } + if ( ret2 >= 0 ) { + ret2 = SetKnownStereoBondParities( pCG, at, num_atoms, nCanonRank, nRank, nAtomNumber ); + if ( ret2 >= 0 ) { + ret2 = MarkKnownEqualStereoBondParities( at, num_atoms, nRank, nAtomNumber); + } + } +#if ( REMOVE_KNOWN_NONSTEREO == 1 ) /* { */ + if ( ret2 >= 0 ) { + int ret3; + do { + ret2 = RemoveKnownNonStereoCenterParities( pCG, at, num_atoms, nCanonRank, nRank, pCS); + if ( ret2 >= 0 ) { + ret3 = RemoveKnownNonStereoBondParities( at, num_atoms, nCanonRank, nRank, pCS); + ret2 = ret3 >= 0? ret2+ret3 : ret3; + } + } + while( ret2 > 0 ); + } + if ( RETURNED_ERROR( ret2 ) ) { + ret = ret2; + } +#endif /* } REMOVE_KNOWN_NONSTEREO */ + } + + return ret; /* non-zero means error */ +} + + + +/**********************************************************************************/ +int GetStereoNeighborPos( sp_ATOM *at, int iAt1, int iAt2 ) +{ + int k1; + AT_RANK sNeigh = (AT_RANK)(iAt2+1); + AT_RANK s; + for ( k1 = 0; k1 < MAX_NUM_STEREO_BONDS && (s = at[iAt1].stereo_bond_neighbor[k1]); k1 ++ ) { + if ( s == sNeigh ) { + return k1; + } + } + return -1; /* neighbor not found */ +} + +/**********************************************************************************/ +/* Extracted from FillSingleStereoDescriptors(...) */ +/**********************************************************************************/ +int GetStereoBondParity(sp_ATOM *at, int i, int n, AT_RANK *nRank ) +{ +int k1, k2, s, parity; + + if ( at[i].stereo_bond_neighbor[0] ) { + for ( k1 = 0; k1 < MAX_NUM_STEREO_BONDS && (s = (int)at[i].stereo_bond_neighbor[k1]); k1 ++ ) { + if ( --s == n ) { + goto neigh1_found; + } + } + return -1; /* error: not a stereo neighbor */ +neigh1_found: + if ( PARITY_KNOWN( at[i].stereo_bond_parity[k1] ) ) { + return PARITY_VAL( at[i].stereo_bond_parity[k1] ); + } + for ( k2 = 0; k2 < MAX_NUM_STEREO_BONDS && (s = (int)at[n].stereo_bond_neighbor[k2]); k2 ++ ) { + if ( --s == i ) { + goto neigh2_found; + } + } + return -1; /* error: not a stereo neighbor */ +neigh2_found:; + } else { + return -1; /* error: not a stereo bond */ + } + + if ( ATOM_PARITY_WELL_DEF(at[i].parity) && + ATOM_PARITY_WELL_DEF(at[n].parity) && + MIN_DOT_PROD <= abs(at[i].stereo_bond_z_prod[k1]) ) { + /* bond parity can be calculated */ + int half_parity1, half_parity2; + /* check whether all neighbors are defined */ + + + half_parity1 = HalfStereoBondParity( at, i, k1, nRank ); + half_parity2 = HalfStereoBondParity( at, n, k2, nRank ); + if ( !half_parity1 || !half_parity2 ) + return 0; /* ranks undefined or not a stereo bond */ + if ( ATOM_PARITY_WELL_DEF(half_parity1) && + ATOM_PARITY_WELL_DEF(half_parity2) ) { + parity = 2 - ( half_parity1 + half_parity2 + + (at[i].stereo_bond_z_prod[k1] < 0))%2; + } else { + return CT_STEREOBOND_ERROR; /* */ + } + } else { + /* parity cannot be calculated: not enough info or 'unknown' */ + if ( AB_PARITY_NONE != (parity = inchi_max(at[i].parity, at[n].parity)) ) { + parity = AB_PARITY_UNDF; /* should not happen */ + } + } + return parity; +} + + + + +/**********************************************************************************/ +/* Extracted from FillSingleStereoDescriptors(...) */ +/**********************************************************************************/ +int GetPermutationParity( CANON_GLOBALS *pCG, + sp_ATOM *at, + AT_RANK nAvoidNeighbor, + AT_RANK *nCanonRank ) +{ + AT_RANK nNeighRank[MAX_NUM_STEREO_ATOM_NEIGH]; + int j, k, parity; + if ( at->valence > MAX_NUM_STEREO_ATOM_NEIGH ) + { + parity = -1; /* error */ + } + else + { + for ( j = k = 0; j < at->valence; j ++ ) + { + if ( at->neighbor[j] != nAvoidNeighbor ) + { + nNeighRank[k++] = nCanonRank[(int)at->neighbor[j]]; + } + } + if ( k ) + { + parity = insertions_sort( pCG, nNeighRank, k, sizeof(nNeighRank[0]), comp_AT_RANK); + if ( nNeighRank[0] ) + { + parity = 2 - parity % 2; + } + else + { + parity = 0; /* not all ranks are known */ + } + } + else + { + /* special case: HX= with implicit H */ + parity = 2; + } + } + return parity; +} + + +/**********************************************************************************/ +int GetStereoCenterParity(CANON_GLOBALS *pCG, sp_ATOM *at, int i, AT_RANK *nRank ) +{ + AT_NUMB nNeighborNumber2[MAXVAL]; + int parity; + int k, num_trans; + + if ( !at[i].parity ) + { + return 0; /* not a stereo center */ + } + if ( at[i].stereo_bond_neighbor[0] ) + { + return -1; /* a stereo bond atom, not a stereo center */ + } + + if ( ATOM_PARITY_WELL_DEF(at[i].parity) ) + { + /* number of neighbors transpositions to the sorted order is unknown. Find it. */ + /* If parity is not well-defined then doing this is a waste of time */ + int num_neigh = at[i].valence; + for ( k = 0; k < num_neigh; k ++) + { + if ( !nRank[(int)at[i].neighbor[k]] ) + return 0; /* stereo at[i] does not belong to the traversed part of the structure */ + nNeighborNumber2[k] = k; + } + pCG->m_pNeighborsForSort = at[i].neighbor; + pCG->m_pn_RankForSort = nRank; + num_trans=insertions_sort( pCG, nNeighborNumber2, num_neigh, sizeof(nNeighborNumber2[0]), CompNeighborsAT_NUMBER ); +#ifndef CT_NEIGH_INCREASE + num_trans += ((num_neigh*(num_neigh-1))/2)%2; /* get correct parity for ascending order */ +#endif + parity = 2 - (at[i].parity + num_trans)%2; + } + else + { + parity = at[i].parity; + } + + return parity; +} diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichicant.h b/INCHI-1-SRC/INCHI_BASE/src/ichicant.h similarity index 88% rename from INCHI-1-SRC/INCHI_API/inchi_dll/ichicant.h rename to INCHI-1-SRC/INCHI_BASE/src/ichicant.h index 107b9ec..e5ae385 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichicant.h +++ b/INCHI-1-SRC/INCHI_BASE/src/ichicant.h @@ -1,410 +1,430 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHICANT_H__ -#define __INCHICANT_H__ - -/******************************************************/ -/* */ -/* Canonicalization definitions */ -/* */ -/******************************************************/ -#include "ichisize.h" - - -#ifndef INCHI_US_SHORT_DEF -typedef signed short S_SHORT; -typedef unsigned short U_SHORT; -#define INCHI_US_SHORT_DEF -#endif - -/*typedef unsigned long INCHI_MODE;*/ - -typedef union tagSplitLong { - unsigned long ul; - U_SHORT us[2]; -}SU_LONG; - -#define _HI 1 /* Intel platform */ -#define _LO 0 - -#define NEIGH_LIST_LEN 4 -#define U_LONG_LEN 2 - -#ifndef defined_NEIGH_LIST -typedef AT_RANK *NEIGH_LIST; -#define defined_NEIGH_LIST -#endif - -typedef struct tagEQUIV_INFO { - int nNumSets; - int *nCutVertexAtom; /* cut-vertex atom for the set of equivalent atoms */ - int *nFirstInSet; /* first of equivalent atoms in the connected to the cut-vertex atom parts of the structure */ - int *nNumInSet; /* number of the equivalent atoms connected to the cut-vertex atom */ - int *nAtomNo; /* eqivalent atom number */ - int *nAddToRank; /* number to add to the rank to normalize */ -} EQUIV_INFO; - -#define MOL_PART_MASK (~0x0U ^ 0x07U) - - -typedef struct tagAtData_dch { - char element[3]; - int valence; - }AT_DATA; - - -#define MAXVAL 20 /* maximum valence */ - -#define ATOM_EL_LEN 6 - -typedef struct tagAtomInvariantBytes { - S_CHAR cNotExactlyHillOrderNumber; - S_CHAR cNumberOfConnections; - /* S_CHAR cNumberOfNonHydrogenBonds; */ - S_CHAR cAtomicNumber; -#if ( HYDROGENS_IN_INIT_RANKS == 1 ) - S_CHAR cNumberOfAttachedHydrogens; -#endif -} ATOM_INVARIANT_BYTES; - -typedef struct tagAtomInvariant { - /* non-isotopic part */ -#if ( USE_DISTANCES_FOR_RANKING == 1 ) - AT_RANK nDistanceFromTerminal; -#endif - ATOM_INVARIANT_BYTES b; - AT_RANK cNum_tautomer; /* 0 or for tautomer endpoint: number of endpoints in the group */ - AT_RANK cNum_tautomer_num[T_NUM_NO_ISOTOPIC]; /* 0 or numbers from t_gtroup */ - /* isotopic part */ - AT_ISO_SORT_KEY iso_sort_key; - AT_RANK cNum_tautomer_iso[T_NUM_ISOTOPIC]; /* 0 or numbers from t_group */ -} ATOM_INVARIANT; -/**********************************/ -typedef enum tagAtInvariantIndexes { - AT_INV_HILL_ORDER, - AT_INV_NUM_CONNECTIONS, - AT_INV_NUM_H, - /* for endpoint + undirected graph, otherwise 0 */ - AT_INV_NUM_TG_ENDPOINTS, - AT_INV_TG_NUMBERS, /* num H, num (-) */ - AT_INV_NUM_H_FIX = AT_INV_TG_NUMBERS+T_NUM_NO_ISOTOPIC, - AT_INV_BREAK1, - /* here compare iso sort key */ - AT_INV_TAUT_ISO = AT_INV_BREAK1, - AT_INV_LENGTH = AT_INV_TAUT_ISO + T_NUM_ISOTOPIC -} AT_INV_INDEXES; - -typedef struct tagAtomInvariant2 { - AT_NUMB val[AT_INV_LENGTH]; - AT_ISO_SORT_KEY iso_sort_key; - S_CHAR iso_aux_key; -} ATOM_INVARIANT2; - -/******************* Partition **********************************/ -typedef struct tagPartition { - AT_RANK *Rank; - AT_NUMB *AtNumber; -} Partition; - -/********************* BFCN *************************************/ -typedef struct tagFixHOrTautCanonNumbering { - - int num_at_tg; /* = num_atoms for non-taut */ - int num_atoms; - int nCanonFlags; - NEIGH_LIST *NeighList; /* length = num_at_tg */ - /****************************/ - /* base structure */ - /****************************/ - AT_RANK *LinearCt; /* connection table atoms (+taut. groups, directed graph)*/ - int nLenLinearCtAtOnly; - int nLenLinearCt; - int nMaxLenLinearCt; - - Partition PartitionCt; /* canonical numbering */ - AT_RANK *nSymmRankCt; /* orbits */ - - /* orig. fixed by tautomerism H positions */ - NUM_H *nNumHOrig; /* original H atoms positions + taut. info, excluding tautomeric H */ - NUM_H *nNumH; /* canonical H atoms positions + taut. info, excluding tautomeric H */ - int nLenNumH; /* length = num_atoms + 2*num_taut_groups */ - - /* fixed H: original positions of tautomeric H; exists obly for tautomeric structures */ - NUM_H *nNumHOrigFixH; /* original fixed positions of tautomeric H */ - NUM_H *nNumHFixH; /* canonical fixed positions of tautomeric H */ - int nLenNumHFixH; /* length = num_atoms */ - - /*******************************************************************************/ - /* the following exists only if isotopic and isotopic results requested */ - /*******************************************************************************/ - Partition PartitionCtIso; /* canonical numbering of isotopic base structure, defined later */ - AT_RANK *nSymmRankCtIso; /* orbits of isotopic structure */ - AT_ISO_SORT_KEY *iso_sort_keys; /* original isotopic sort keys for atoms and taut groups */ - AT_ISO_SORT_KEY *iso_sort_keysOrig; /* canonical isotopic sort keys for atoms and taut groups */ - int len_iso_sort_keys; - S_CHAR *iso_exchg_atnos; /* canonical: 0=> tautomeric or may have isotopic H exchanged */ - S_CHAR *iso_exchg_atnosOrig; /* original: 0=> tautomeric or may have isotopic H exchanged */ - -} FTCN; - -/******************** BCN *************************************/ -typedef struct tagBaseCanonNumbering { - - AT_RANK **pRankStack; - int nMaxLenRankStack; - int num_max; /* allocated nRank[] arrays lengths in pRankStack */ - int num_at_tg; /* all of the following arrays have this length */ - int num_atoms; - struct tagInchiTime *ulTimeOutTime; - FTCN ftcn[TAUT_NUM]; - -} BCN; - -/*********************************** - * - * CANON_STAT - */ -typedef struct tagCanonStat { - /* statistics */ - long lNumBreakTies; - long lNumNeighListIter; - long lNumTotCT; - long lNumDecreasedCT; - long lNumRejectedCT; - long lNumEqualCT; - struct tagInchiTime *ulTimeOutTime; - long lTotalTime; - - /* control */ - int bFirstCT; - int bKeepSymmRank; - int bStereoIsBetter; - - int nCanonFlags; - - /* data : */ - - AT_NUMB *LinearCT; /* connection table only */ - AT_ISOTOPIC *LinearCTIsotopic; - AT_ISO_TGROUP *LinearCTIsotopicTautomer; - AT_STEREO_DBLE *LinearCTStereoDble; - AT_STEREO_CARB *LinearCTStereoCarb; - AT_STEREO_DBLE *LinearCTStereoDbleInv; - AT_STEREO_CARB *LinearCTStereoCarbInv; - AT_STEREO_DBLE *LinearCTIsotopicStereoDble; - AT_STEREO_CARB *LinearCTIsotopicStereoCarb; - AT_STEREO_DBLE *LinearCTIsotopicStereoDbleInv; - AT_STEREO_CARB *LinearCTIsotopicStereoCarbInv; - AT_TAUTOMER *LinearCTTautomer; /* minimal */ - -/* second copies of line notation arrays */ - - AT_NUMB *LinearCT2; /* to save non-isotopic CT */ - - int nLenLinearCTStereoDble; - int nLenLinearCTStereoDbleInv; - int nMaxLenLinearCTStereoDble; /* new */ - - int bCmpStereo; /* 0 => no stereo to invert; - 1 => StereoCtInv < StereoCt; - 2 => StereoCtInv = StereoCt; - 3 => StereoCtInv > StereoCt; - */ - int nLenLinearCTStereoCarb; - int nLenLinearCTStereoCarbInv; - int nMaxLenLinearCTStereoCarb; /* new */ - - int nLenLinearCTIsotopic; - int nMaxLenLinearCTIsotopic; - - int nLenLinearCTIsotopicTautomer; - int nMaxLenLinearCTIsotopicTautomer; - - int nLenLinearCT; /* connection table only */ - int nLenLinearCT2; /* connection table only, non-isotopic result */ - int nLenLinearCTAtOnly; /* connection table only without tautomeric pseudoatoms */ - int nLenLinearCTAtOnly2; /* connection table only, non-isotopic result without tautomeric pseudoatoms */ - int nMaxLenLinearCT; /* connection table only */ - - int nLenLinearCTTautomer; - int nMaxLenLinearCTTautomer; - - int bCmpIsotopicStereo; /* 0 => no stereo to invert; - 1 => StereoCtInv < StereoCt; - 2 => StereoCtInv = StereoCt; - 3 => StereoCtInv > StereoCt; - */ - int nLenLinearCTIsotopicStereoDble; - int nLenLinearCTIsotopicStereoDbleInv; - int nMaxLenLinearCTIsotopicStereoDble; - - int nLenLinearCTIsotopicStereoCarb; /* new */ - int nLenLinearCTIsotopicStereoCarbInv; /* new */ - int nMaxLenLinearCTIsotopicStereoCarb; - S_CHAR *bRankUsedForStereo; /* canon. rank used for stereo mapping */ - S_CHAR *bAtomUsedForStereo; /* 0 if not a stereo atom or during a canon. rank being mapped on this atom; */ - /* STEREO_AT_MARK if an unpapped stereogenic atom */ - /* or a number of stereogenic bonds adjacent to an atom */ - - AT_RANK *nPrevAtomNumber; - - AT_RANK *nCanonOrd; /* atom numbers in order of increasing canon. ranks */ - AT_RANK *nSymmRank; /* symmetry numbers in order of atoms */ - AT_RANK *nCanonOrdTaut; /* t-group numbers numbers in order of increasing canon. ranks */ - AT_RANK *nSymmRankTaut; /* t-group symmetry numbers in order of t-groups */ - - AT_RANK *nCanonOrdStereo; /* atom numbers in order of increasing canon. ranks */ - AT_RANK *nCanonOrdStereoInv; /* atom numbers in order of increasing canon. ranks */ - AT_RANK *nCanonOrdStereoTaut; /* t-group numbers in order of increasing canon. ranks */ - - AT_RANK *nSymmRankIsotopic; - AT_RANK *nCanonOrdIsotopic; /* atom numbers in order of increasing canon. ranks */ - AT_RANK *nSymmRankIsotopicTaut; /* !!! */ - AT_RANK *nCanonOrdIsotopicTaut; /*/ t-group numbers in order of increasing canon. ranks */ - - AT_RANK *nCanonOrdIsotopicStereo; - AT_RANK *nCanonOrdIsotopicStereoInv; - AT_RANK *nCanonOrdIsotopicStereoTaut; /* !!! */ - - /* actual lengths if successfully calculated */ - - int nLenCanonOrd; /* Superceded by any of the following > 0 */ - int nLenCanonOrdTaut; /* !!! Superceded by any of the following > 0 */ - int nLenCanonOrdIsotopic; - int nLenCanonOrdIsotopicTaut; /* !!! */ - int nLenCanonOrdStereo; - int nLenCanonOrdStereoTaut; /* !!! */ - int nLenCanonOrdIsotopicStereo; - int nLenCanonOrdIsotopicStereoTaut; /* !!! */ - - /* other */ - - int bHasIsotopicInTautomerGroups; - T_GROUP_INFO *t_group_info; - int bIgnoreIsotopic; - int bDoubleBondSquare; /* 0 or 2 */ - INCHI_MODE nMode; -#if ( bRELEASE_VERSION == 0 ) - int bExtract; /* for debug only */ -#endif - NEIGH_LIST *NeighList; - BCN *pBCN; - S_CHAR *nNum_H; /* number of terminal hydrogen atoms on each atom except tautomeric [num_atoms], in order of canonical numbers */ - S_CHAR *nNum_H_fixed;/* number of terminal hydrogen atoms on tautomeric atoms (for non-atautomeric representation) [num_atoms] */ - S_CHAR *nExchgIsoH; -} CANON_STAT; - -/**************************************************/ -typedef struct tagCanonData { - - /* same names/types as in ConTable; here the order is from original numbering */ - - AT_NUMB *LinearCT; /* output ?? */ - - int nMaxLenLinearCT; - int nLenLinearCT; - int nLenCTAtOnly; - int nCanonFlags; - /* hydrogen atoms fixed in tautomeric representation: - compare before diff sign inversion: (+) <=> Ct1->() > Ct2->() */ - NUM_H *NumH; - int lenNumH; /* used length */ - int maxlenNumH; /* n + T_NUM_NO_ISOTOPIC*(n_tg-n) + 1 */ - - /* hydrogen atoms fixed in non-tautomeric representation only: - compare before diff sign inversion: (+) <=> Ct1->() > Ct2->() */ - NUM_H *NumHfixed; - int lenNumHfixed; /* used length */ - int maxlenNumHfixed; /* max length = n+1 */ - - /* isotopic atoms (without tautomeric H) and isotopic tautomeric groups */ - /* note: AT_ISO_SORT_KEY and T_GROUP_ISOWT are identical types: long */ - AT_ISO_SORT_KEY *iso_sort_key; - int len_iso_sort_key; /* used length */ - int maxlen_iso_sort_key; /* max length = n_tg+1 */ - S_CHAR *iso_exchg_atnos; - int len_iso_exchg_atnos; /* used length */ - int maxlen_iso_exchg_atnos; - - /* isotopic hydrogen atoms fixed in non-tautomeric representation only */ -#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) - AT_ISO_SORT_KEY *iso_sort_key_Hfixed; - int len_iso_sort_key_Hfixed; /* used length */ - int maxlen_iso_sort_key_Hfixed; /* max length = n+1 */ -#endif - /* auxiliary ranking */ - - AT_RANK *nAuxRank; - - struct tagInchiTime *ulTimeOutTime; /* timeout */ - -} CANON_DATA; -/**************************************************/ - -typedef struct tagCanonCounts { - long lNumBreakTies; - long lNumDecreasedCT; - long lNumRejectedCT; - long lNumEqualCT; - long lNumTotCT; - double dGroupSize; - long lNumGenerators; - long lNumStoredIsomorphisms; - -} CANON_COUNTS; -/*********************************************** - tree structure: one segment - - canon. rank - at.no orig. atom numbers on which the canon. rank has been successfully mapped - ... - at.no except the last at.no: it is not known if it has been mapped until all atoms are mapped - num.at+1 number of atoms in this segment -*/ - -typedef struct tagCurTree { - AT_NUMB *tree; - int max_len; /* allocated length of tree in sizeof(tree[0]) units */ - int cur_len; /* currently used length */ - int incr_len; /* reallocation increment */ -} CUR_TREE; - -#endif /* __INCHICANT_H__ */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _INCHICANT_H_ +#define _INCHICANT_H_ + + +#include "ichisize.h" +#include "ichinorm.h" + + +/* + Canonicalization definitions +*/ + +#ifndef INCHI_US_SHORT_DEF +typedef signed short S_SHORT; +typedef unsigned short U_SHORT; +#define INCHI_US_SHORT_DEF +#endif + +/*typedef unsigned long INCHI_MODE;*/ + +typedef union tagSplitLong { + unsigned long ul; + U_SHORT us[2]; +}SU_LONG; + +#define _HI 1 /* Intel platform */ +#define _LO 0 + +#define NEIGH_LIST_LEN 4 +#define U_LONG_LEN 2 + +#ifndef defined_NEIGH_LIST +typedef AT_RANK *NEIGH_LIST; +#define defined_NEIGH_LIST +#endif + +typedef struct tagEQUIV_INFO { + int nNumSets; + int *nCutVertexAtom; /* cut-vertex atom for the set of equivalent atoms */ + int *nFirstInSet; /* first of equivalent atoms in the connected to the cut-vertex atom parts of the structure */ + int *nNumInSet; /* number of the equivalent atoms connected to the cut-vertex atom */ + int *nAtomNo; /* eqivalent atom number */ + int *nAddToRank; /* number to add to the rank to normalize */ +} EQUIV_INFO; + +#define MOL_PART_MASK (~0x0U ^ 0x07U) + +typedef struct tagAtData_dch { + char element[3]; + int valence; + }AT_DATA; + + +#define MAXVAL 20 /* maximum valence */ + +#define ATOM_EL_LEN 6 + +typedef struct tagAtomInvariantBytes { + S_CHAR cNotExactlyHillOrderNumber; + S_CHAR cNumberOfConnections; + /* S_CHAR cNumberOfNonHydrogenBonds; */ + S_CHAR cAtomicNumber; +#if ( HYDROGENS_IN_INIT_RANKS == 1 ) + S_CHAR cNumberOfAttachedHydrogens; +#endif +} ATOM_INVARIANT_BYTES; + +typedef struct tagAtomInvariant { + /* non-isotopic part */ +#if ( USE_DISTANCES_FOR_RANKING == 1 ) + AT_RANK nDistanceFromTerminal; +#endif + ATOM_INVARIANT_BYTES b; + AT_RANK cNum_tautomer; /* 0 or for tautomer endpoint: number of endpoints in the group */ + AT_RANK cNum_tautomer_num[T_NUM_NO_ISOTOPIC]; /* 0 or numbers from t_gtroup */ + /* isotopic part */ + AT_ISO_SORT_KEY iso_sort_key; + AT_RANK cNum_tautomer_iso[T_NUM_ISOTOPIC]; /* 0 or numbers from t_group */ +} ATOM_INVARIANT; +/**********************************/ +typedef enum tagAtInvariantIndexes { + AT_INV_HILL_ORDER, + AT_INV_NUM_CONNECTIONS, + AT_INV_NUM_H, + /* for endpoint + undirected graph, otherwise 0 */ + AT_INV_NUM_TG_ENDPOINTS, + AT_INV_TG_NUMBERS, /* num H, num (-) */ + AT_INV_NUM_H_FIX = AT_INV_TG_NUMBERS+T_NUM_NO_ISOTOPIC, + AT_INV_BREAK1, + /* here compare iso sort key */ + AT_INV_TAUT_ISO = AT_INV_BREAK1, + AT_INV_LENGTH = AT_INV_TAUT_ISO + T_NUM_ISOTOPIC +} AT_INV_INDEXES; + +typedef struct tagAtomInvariant2 { + AT_NUMB val[AT_INV_LENGTH]; + AT_ISO_SORT_KEY iso_sort_key; + S_CHAR iso_aux_key; +} ATOM_INVARIANT2; + +/******************* Partition **********************************/ +typedef struct tagPartition { + AT_RANK *Rank; + AT_NUMB *AtNumber; +} Partition; + +/********************* BFCN *************************************/ +typedef struct tagFixHOrTautCanonNumbering { + + int num_at_tg; /* = num_atoms for non-taut */ + int num_atoms; + int nCanonFlags; + NEIGH_LIST *NeighList; /* length = num_at_tg */ + /****************************/ + /* base structure */ + /****************************/ + AT_RANK *LinearCt; /* connection table atoms (+taut. groups, directed graph)*/ + int nLenLinearCtAtOnly; + int nLenLinearCt; + int nMaxLenLinearCt; + + Partition PartitionCt; /* canonical numbering */ + AT_RANK *nSymmRankCt; /* orbits */ + + /* orig. fixed by tautomerism H positions */ + NUM_H *nNumHOrig; /* original H atoms positions + taut. info, excluding tautomeric H */ + NUM_H *nNumH; /* canonical H atoms positions + taut. info, excluding tautomeric H */ + int nLenNumH; /* length = num_atoms + 2*num_taut_groups */ + + /* fixed H: original positions of tautomeric H; exists obly for tautomeric structures */ + NUM_H *nNumHOrigFixH; /* original fixed positions of tautomeric H */ + NUM_H *nNumHFixH; /* canonical fixed positions of tautomeric H */ + int nLenNumHFixH; /* length = num_atoms */ + + /*******************************************************************************/ + /* the following exists only if isotopic and isotopic results requested */ + /*******************************************************************************/ + Partition PartitionCtIso; /* canonical numbering of isotopic base structure, defined later */ + AT_RANK *nSymmRankCtIso; /* orbits of isotopic structure */ + AT_ISO_SORT_KEY *iso_sort_keys; /* original isotopic sort keys for atoms and taut groups */ + AT_ISO_SORT_KEY *iso_sort_keysOrig; /* canonical isotopic sort keys for atoms and taut groups */ + int len_iso_sort_keys; + S_CHAR *iso_exchg_atnos; /* canonical: 0=> tautomeric or may have isotopic H exchanged */ + S_CHAR *iso_exchg_atnosOrig; /* original: 0=> tautomeric or may have isotopic H exchanged */ +} FTCN; + +/******************** BCN *************************************/ +typedef struct tagBaseCanonNumbering { + + AT_RANK **pRankStack; + int nMaxLenRankStack; + int num_max; /* allocated nRank[] arrays lengths in pRankStack */ + int num_at_tg; /* all of the following arrays have this length */ + int num_atoms; + struct tagInchiTime *ulTimeOutTime; + FTCN ftcn[TAUT_NUM]; +} BCN; + +/*********************************** + * + * CANON_STAT + */ +typedef struct tagCanonStat { + /* statistics */ + long lNumBreakTies; + long lNumNeighListIter; + long lNumTotCT; + long lNumDecreasedCT; + long lNumRejectedCT; + long lNumEqualCT; + struct tagInchiTime *ulTimeOutTime; + long lTotalTime; + + /* control */ + int bFirstCT; + int bKeepSymmRank; + int bStereoIsBetter; + + int nCanonFlags; + + /* data : */ + + AT_NUMB *LinearCT; /* connection table only */ + AT_ISOTOPIC *LinearCTIsotopic; + AT_ISO_TGROUP *LinearCTIsotopicTautomer; + AT_STEREO_DBLE *LinearCTStereoDble; + AT_STEREO_CARB *LinearCTStereoCarb; + AT_STEREO_DBLE *LinearCTStereoDbleInv; + AT_STEREO_CARB *LinearCTStereoCarbInv; + AT_STEREO_DBLE *LinearCTIsotopicStereoDble; + AT_STEREO_CARB *LinearCTIsotopicStereoCarb; + AT_STEREO_DBLE *LinearCTIsotopicStereoDbleInv; + AT_STEREO_CARB *LinearCTIsotopicStereoCarbInv; + AT_TAUTOMER *LinearCTTautomer; /* minimal */ + +/* second copies of line notation arrays */ + + AT_NUMB *LinearCT2; /* to save non-isotopic CT */ + + int nLenLinearCTStereoDble; + int nLenLinearCTStereoDbleInv; + int nMaxLenLinearCTStereoDble; /* new */ + + int bCmpStereo; /* 0 => no stereo to invert; + 1 => StereoCtInv < StereoCt; + 2 => StereoCtInv = StereoCt; + 3 => StereoCtInv > StereoCt; + */ + int nLenLinearCTStereoCarb; + int nLenLinearCTStereoCarbInv; + int nMaxLenLinearCTStereoCarb; /* new */ + + int nLenLinearCTIsotopic; + int nMaxLenLinearCTIsotopic; + + int nLenLinearCTIsotopicTautomer; + int nMaxLenLinearCTIsotopicTautomer; + + int nLenLinearCT; /* connection table only */ + int nLenLinearCT2; /* connection table only, non-isotopic result */ + int nLenLinearCTAtOnly; /* connection table only without tautomeric pseudoatoms */ + int nLenLinearCTAtOnly2; /* connection table only, non-isotopic result without tautomeric pseudoatoms */ + int nMaxLenLinearCT; /* connection table only */ + + int nLenLinearCTTautomer; + int nMaxLenLinearCTTautomer; + + int bCmpIsotopicStereo; /* 0 => no stereo to invert; + 1 => StereoCtInv < StereoCt; + 2 => StereoCtInv = StereoCt; + 3 => StereoCtInv > StereoCt; + */ + int nLenLinearCTIsotopicStereoDble; + int nLenLinearCTIsotopicStereoDbleInv; + int nMaxLenLinearCTIsotopicStereoDble; + + int nLenLinearCTIsotopicStereoCarb; /* new */ + int nLenLinearCTIsotopicStereoCarbInv; /* new */ + int nMaxLenLinearCTIsotopicStereoCarb; + S_CHAR *bRankUsedForStereo; /* canon. rank used for stereo mapping */ + S_CHAR *bAtomUsedForStereo; /* 0 if not a stereo atom or during a canon. rank being mapped on this atom; */ + /* STEREO_AT_MARK if an unpapped stereogenic atom */ + /* or a number of stereogenic bonds adjacent to an atom */ + + AT_RANK *nPrevAtomNumber; + + AT_RANK *nCanonOrd; /* atom numbers in order of increasing canon. ranks */ + AT_RANK *nSymmRank; /* symmetry numbers in order of atoms */ + AT_RANK *nCanonOrdTaut; /* t-group numbers numbers in order of increasing canon. ranks */ + AT_RANK *nSymmRankTaut; /* t-group symmetry numbers in order of t-groups */ + + AT_RANK *nCanonOrdStereo; /* atom numbers in order of increasing canon. ranks */ + AT_RANK *nCanonOrdStereoInv; /* atom numbers in order of increasing canon. ranks */ + AT_RANK *nCanonOrdStereoTaut; /* t-group numbers in order of increasing canon. ranks */ + + AT_RANK *nSymmRankIsotopic; + AT_RANK *nCanonOrdIsotopic; /* atom numbers in order of increasing canon. ranks */ + AT_RANK *nSymmRankIsotopicTaut; /* !!! */ + AT_RANK *nCanonOrdIsotopicTaut; /*/ t-group numbers in order of increasing canon. ranks */ + + AT_RANK *nCanonOrdIsotopicStereo; + AT_RANK *nCanonOrdIsotopicStereoInv; + AT_RANK *nCanonOrdIsotopicStereoTaut; /* !!! */ + + /* actual lengths if successfully calculated */ + + int nLenCanonOrd; /* Superceded by any of the following > 0 */ + int nLenCanonOrdTaut; /* !!! Superceded by any of the following > 0 */ + int nLenCanonOrdIsotopic; + int nLenCanonOrdIsotopicTaut; /* !!! */ + int nLenCanonOrdStereo; + int nLenCanonOrdStereoTaut; /* !!! */ + int nLenCanonOrdIsotopicStereo; + int nLenCanonOrdIsotopicStereoTaut; /* !!! */ + + /* other */ + + int bHasIsotopicInTautomerGroups; + T_GROUP_INFO *t_group_info; + int bIgnoreIsotopic; + int bDoubleBondSquare; /* 0 or 2 */ + INCHI_MODE nMode; +#if ( bRELEASE_VERSION == 0 ) + int bExtract; /* for debug only */ +#endif + NEIGH_LIST *NeighList; + BCN *pBCN; + S_CHAR *nNum_H; /* number of terminal hydrogen atoms on each atom except tautomeric [num_atoms], in order of canonical numbers */ + S_CHAR *nNum_H_fixed;/* number of terminal hydrogen atoms on tautomeric atoms (for non-atautomeric representation) [num_atoms] */ + S_CHAR *nExchgIsoH; +} CANON_STAT; + + + + +typedef struct tagCANON_GLOBALS +{ + const NEIGH_LIST *m_pNeighList_RankForSort; + const ATOM_INVARIANT2 *m_pAtomInvariant2ForSort; + const AT_NUMB *m_pNeighborsForSort; + const AT_RANK *m_pn_RankForSort; + AT_RANK m_nMaxAtNeighRankForSort; + int m_nNumCompNeighborsRanksCountEql; + bitWord *m_bBit; + int m_bBitInitialized; + int m_num_bit; +} CANON_GLOBALS; + +int SetBitCreate( struct tagCANON_GLOBALS *pCG); + +void inchi_qsort(void *pParam, void *base, size_t num, size_t width, int (*comp)(const void *, const void *, void *)); + + + + + +/**************************************************/ +typedef struct tagCanonData { + + /* same names/types as in ConTable; here the order is from original numbering */ + + AT_NUMB *LinearCT; /* output ?? */ + + int nMaxLenLinearCT; + int nLenLinearCT; + int nLenCTAtOnly; + int nCanonFlags; + /* hydrogen atoms fixed in tautomeric representation: + compare before diff sign inversion: (+) <=> Ct1->() > Ct2->() */ + NUM_H *NumH; + int lenNumH; /* used length */ + int maxlenNumH; /* n + T_NUM_NO_ISOTOPIC*(n_tg-n) + 1 */ + + /* hydrogen atoms fixed in non-tautomeric representation only: + compare before diff sign inversion: (+) <=> Ct1->() > Ct2->() */ + NUM_H *NumHfixed; + int lenNumHfixed; /* used length */ + int maxlenNumHfixed; /* max length = n+1 */ + + /* isotopic atoms (without tautomeric H) and isotopic tautomeric groups */ + /* note: AT_ISO_SORT_KEY and T_GROUP_ISOWT are identical types: long */ + AT_ISO_SORT_KEY *iso_sort_key; + int len_iso_sort_key; /* used length */ + int maxlen_iso_sort_key; /* max length = n_tg+1 */ + S_CHAR *iso_exchg_atnos; + int len_iso_exchg_atnos; /* used length */ + int maxlen_iso_exchg_atnos; + + /* isotopic hydrogen atoms fixed in non-tautomeric representation only */ +#if ( USE_ISO_SORT_KEY_HFIXED == 1 ) + AT_ISO_SORT_KEY *iso_sort_key_Hfixed; + int len_iso_sort_key_Hfixed; /* used length */ + int maxlen_iso_sort_key_Hfixed; /* max length = n+1 */ +#endif + /* auxiliary ranking */ + + AT_RANK *nAuxRank; + + struct tagInchiTime *ulTimeOutTime; /* timeout */ +} CANON_DATA; +/**************************************************/ + +typedef struct tagCanonCounts { + long lNumBreakTies; + long lNumDecreasedCT; + long lNumRejectedCT; + long lNumEqualCT; + long lNumTotCT; + double dGroupSize; + long lNumGenerators; + long lNumStoredIsomorphisms; +} CANON_COUNTS; +/*********************************************** + tree structure: one segment + + canon. rank + at.no orig. atom numbers on which the canon. rank has been successfully mapped + ... + at.no except the last at.no: it is not known if it has been mapped until all atoms are mapped + num.at+1 number of atoms in this segment +*/ + +typedef struct tagCurTree { + AT_NUMB *tree; + int max_len; /* allocated length of tree in sizeof(tree[0]) units */ + int cur_len; /* currently used length */ + int incr_len; /* reallocation increment */ +} CUR_TREE; + + +#endif /* _INCHICANT_H_ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichicomn.h b/INCHI-1-SRC/INCHI_BASE/src/ichicomn.h similarity index 76% rename from INCHI-1-SRC/INCHI_API/inchi_dll/ichicomn.h rename to INCHI-1-SRC/INCHI_BASE/src/ichicomn.h index d448183..850ddd9 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichicomn.h +++ b/INCHI-1-SRC/INCHI_BASE/src/ichicomn.h @@ -1,296 +1,290 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHICOMN_H__ -#define __INCHICOMN_H__ - -#include "ichierr.h" - - -/******************************** - * - * Globals for sorting - */ - -extern const NEIGH_LIST *pNeighList_RankForSort; -extern const ATOM_INVARIANT2 *pAtomInvariant2ForSort; -extern const AT_NUMB *pNeighborsForSort; -extern const AT_RANK *pn_RankForSort; - -extern AT_RANK nMaxAtNeighRankForSort; - -extern int nNumCompNeighborsRanksCountEql; - - -#define tsort insertions_sort - -typedef struct tagEquNeigh { - int num_to; /* number of neighbors with equal mapping ranks; one of them has min. canon. number */ - AT_RANK to_at[4]; /* to_atom neighbors #s with equal mapping ranks */ - AT_RANK from_at; /* from_at neighbor # which has min. canon. number and can be mapped on any of the above to_at[] */ - AT_RANK rank; /* equal mapping rank value */ - AT_RANK canon_rank; /* min. canon. number */ -} EQ_NEIGH; - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -/*******************************************************************/ -/* ichiisot.c */ -int unpack_iso_sort_key( AT_ISO_SORT_KEY iso_sort_key, S_CHAR *num_1H, S_CHAR *num_2H, S_CHAR *num_3H, S_CHAR *iso_atw_diff ); -AT_ISO_SORT_KEY make_iso_sort_key( int iso_atw_diff, int num_1H, int num_2H, int num_3H); -int set_atom_iso_sort_keys( int num_at, sp_ATOM *at, T_GROUP_INFO* t_group_info, int *bHasIsotopicInTautomerGroups ); -/***********************************************************************/ -/* ichisort.c */ - -void insertions_sort_NeighList_AT_NUMBERS( NEIGH_LIST base, AT_RANK *nRank ); -int insertions_sort_NeighList_AT_NUMBERS3( NEIGH_LIST base, AT_RANK *nRank ); -int insertions_sort_AT_RANK( AT_RANK *base, int num ); -void insertions_sort_NeighListBySymmAndCanonRank( NEIGH_LIST base, const AT_RANK *nSymmRank, const AT_RANK *nCanonRank ); -int CompareNeighListLex( NEIGH_LIST pp1, NEIGH_LIST pp2, const AT_RANK *nRank); -int CompareNeighListLexUpToMaxRank( NEIGH_LIST pp1, NEIGH_LIST pp2, const AT_RANK *nRank, AT_RANK nMaxAtNeighRank ); -int compare_NeighLists( const NEIGH_LIST *op1, const NEIGH_LIST *op2 ); -int CompNeighborsAT_NUMBER( const void* a1, const void* a2); -int comp_AT_RANK( const void* a1, const void* a2); -int CompRank(const void* a1, const void* a2 ); -int CompRanksOrd( const void* a1, const void* a2 ); - -int CompAtomInvariants2Only( const void* a1, const void* a2 ); -int CompAtomInvariants2( const void* a1, const void* a2 ); - -int CompNeighListRanks( const void* a1, const void* a2 ); -int CompNeighListRanksOrd( const void* a1, const void* a2 ); -int CompNeighLists( const void* a1, const void* a2 ); -int CompNeighListsUpToMaxRank( const void* a1, const void* a2 ); -int CompNeighborsRanksCountEql( const void* a1, const void* a2 ); -int CompRanksInvOrd( const void* a1, const void* a2 ); -int CompChemElemLex( const void *a1, const void *a2 ); - - - -NEIGH_LIST *CreateNeighList( int num_atoms, int num_at_tg, sp_ATOM* at, int bDoubleBondSquare, T_GROUP_INFO *t_group_info ); -NEIGH_LIST *CreateNeighListFromLinearCT( AT_NUMB *LinearCT, int nLenCT, int num_atoms ); - -void FreeNeighList( NEIGH_LIST *pp ); -int BreakAllTies( int num_atoms, int num_max, AT_RANK **pRankStack, - NEIGH_LIST *NeighList, AT_RANK *nTempRank, CANON_STAT *pCS); - -/******************************************************************************/ -/* ichimap.c */ -void switch_ptrs( AT_RANK **p1, AT_RANK **p2 ); - -int SortedEquInfoToRanks( const AT_RANK* nSymmRank, AT_RANK* nRank, const AT_RANK* nAtomNumber, int num_atoms, int *bChanged ); -int SortedRanksToEquInfo( AT_RANK* nSymmRank, const AT_RANK* nRank, const AT_RANK* nAtomNumber, int num_atoms ); - -int SetNewRanksFromNeighLists( int num_atoms, NEIGH_LIST *NeighList, AT_RANK *nRank, AT_RANK *nNewRank, - AT_RANK *nAtomNumber, int bUseAltSort, int ( *comp )(const void *, const void *) ); -int SetNewRanksFromNeighLists3( int num_atoms, NEIGH_LIST *NeighList, AT_RANK *nRank, AT_RANK *nNewRank, - AT_RANK *nAtomNumber ); -int SetNewRanksFromNeighLists4( int num_atoms, NEIGH_LIST *NeighList, AT_RANK *nRank, AT_RANK *nNewRank, - AT_RANK *nAtomNumber, AT_RANK nMaxAtRank ); -void SortNeighListsBySymmAndCanonRank( int num_atoms, NEIGH_LIST *NeighList, const AT_RANK *nSymmRank, const AT_RANK *nCanonRank ); -int SortNeighLists2( int num_atoms, AT_RANK *nRank, NEIGH_LIST *NeighList, AT_RANK *nAtomNumber ); -int DifferentiateRanks2( int num_atoms, NEIGH_LIST *NeighList, - int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, - AT_RANK *nAtomNumber, long *lNumIter, int bUseAltSort ); - -int DifferentiateRanks3( int num_atoms, NEIGH_LIST *NeighList, - int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, - AT_RANK *nAtomNumber, long *lNumIter ); -int DifferentiateRanks4( int num_atoms, NEIGH_LIST *NeighList, - int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, - AT_RANK *nAtomNumber, AT_RANK nMaxAtRank, long *lNumIter ); -int DifferentiateRanksBasic( int num_atoms, NEIGH_LIST *NeighList, - int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, - AT_RANK *nAtomNumber, long *lNumIter, int bUseAltSort ); - -int parity_of_mapped_atom2( int from_at, int to_at, const sp_ATOM *at, EQ_NEIGH *pEN, - const AT_RANK *nCanonRankFrom, const AT_RANK *nRankFrom, const AT_RANK *nRankTo ); - -int parity_of_mapped_half_bond( int from_at, int to_at, int from_neigh, int to_neigh, - sp_ATOM *at, EQ_NEIGH *pEN, - const AT_RANK *nCanonRankFrom, const AT_RANK *nRankFrom, const AT_RANK *nRankTo ); - -int HalfStereoBondParity( sp_ATOM *at, int at_no1, int i_sb_neigh, const AT_RANK *nRank ); - -int NumberOfTies( AT_RANK **pRankStack1, AT_RANK **pRankStack2, int length, int at_no1, int at_no2, AT_RANK *nNewRank, int *bAddStack, int *bMapped1 ); - -int map_an_atom2( int num_atoms, int num_max, int at_no1/*from*/, int at_no2/*to*/, - AT_RANK *nTempRank, - int nNumMappedRanks, int *pnNewNumMappedRanks, - CANON_STAT *pCS, - NEIGH_LIST *NeighList, - AT_RANK **pRankStack1, AT_RANK **pRankStack2, int *bAddStack ); -int ClearPreviousMappings( AT_RANK **pRankStack1 ); - -int SetOneStereoBondIllDefParity( sp_ATOM *at, int jc, /* atom number*/ int k /* stereo bond ord. number*/, int new_parity ); -int RemoveOneStereoBond( sp_ATOM *at, int jc, /* atom number*/ int k /* stereo bond number*/ ); -int RemoveOneStereoCenter( sp_ATOM *at, int jc /* atom number*/ ); -int RemoveCalculatedNonStereo( sp_ATOM *at, int num_atoms, int num_at_tg, - AT_RANK **pRankStack1, AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, - const AT_RANK *nSymmRank, AT_RANK *nCanonRank, - AT_RANK *nAtomNumberCanon, CANON_STAT *pCS, - int vABParityUnknown); - - -int might_change_other_atom_parity( sp_ATOM *at, int num_atoms, int at_no, AT_RANK *nRank2, AT_RANK *nRank1 ); - -int map_stereo_bonds4 ( - sp_ATOM *at, int num_atoms, int num_at_tg, int num_max, int bAllene, - const AT_RANK *nCanonRankFrom, const AT_RANK *nAtomNumberCanonFrom, /* non-stereo canon ranking */ - AT_RANK *nCanonRankTo, /* output canonical numbering*/ - const AT_RANK *nSymmRank, AT_RANK **pRankStack1/*from*/, AT_RANK **pRankStack2/*to*/, - AT_RANK *nTempRank, int nNumMappedRanksInput, - AT_RANK *nSymmStereo, NEIGH_LIST *NeighList, - CANON_STAT *pCS, CUR_TREE *cur_tree, int nNumMappedBonds, - int vABParityUnknown); - -int map_stereo_atoms4 ( - sp_ATOM *at, int num_atoms, int num_at_tg, int num_max, - const AT_RANK *nCanonRankFrom, const AT_RANK *nAtomNumberCanonFrom, AT_RANK *nCanonRankTo, /* canonical numbering to be mapped */ - const AT_RANK *nSymmRank, AT_RANK **pRankStack1/*from*/, AT_RANK **pRankStack2/*to*/, - AT_RANK *nTempRank, int nNumMappedRanksInput, - AT_RANK *nSymmStereo, NEIGH_LIST *NeighList, - CANON_STAT *pCS, CUR_TREE *cur_tree, int nNumMappedAtoms, - int vABParityUnknown); - - -int CurTreeAlloc( CUR_TREE *cur_tree, int num_atoms ); -int CurTreeReAlloc( CUR_TREE *cur_tree ); -void CurTreeFree( CUR_TREE *cur_tree ); -int CurTreeAddRank( CUR_TREE *cur_tree, AT_NUMB rank ); -int CurTreeRemoveLastRank( CUR_TREE *cur_tree ); -int CurTreeReplaceLastRank( CUR_TREE *cur_tree, AT_NUMB rank ); -int CurTreeFindTheRankPos( CUR_TREE *cur_tree, AT_NUMB rank ); -int CurTreeGetPos( CUR_TREE *cur_tree ); -int CurTreeSetPos( CUR_TREE *cur_tree, int len ); -int CurTreeAddAtom( CUR_TREE *cur_tree, int at_no ); -int CurTreeRemoveLastAtom( CUR_TREE *cur_tree ); -int CurTreeIsLastRank( CUR_TREE *cur_tree, AT_NUMB rank ); -int CurTreeIsLastAtomEqu( CUR_TREE *cur_tree, int at_no, AT_NUMB *nSymmStereo ); -int CurTreeRemoveIfLastAtom( CUR_TREE *cur_tree, int at_no ); -int CurTreeRemoveLastRankIfNoAtoms( CUR_TREE *cur_tree ); -void CurTreeKeepLastAtomsOnly( CUR_TREE *cur_tree, int tpos, int shift ); - - -void SetUseAtomForStereo( S_CHAR *bAtomUsedForStereo, sp_ATOM *at, int num_atoms ); - -int nJoin2Mcrs( AT_RANK *nEqArray, AT_RANK n1, AT_RANK n2 ); -AT_RANK nGetMcr( AT_RANK *nEqArray, AT_RANK n ); -int bUniqueAtNbrFromMappingRank( AT_RANK **pRankStack, AT_RANK nAtRank, AT_NUMB *nAtNumber ); - -int Next_SB_At_CanonRanks2( AT_RANK *canon_rank1, AT_RANK *canon_rank2, /* canonical numbers */ - AT_RANK *canon_rank1_min, AT_RANK *canon_rank2_min, - int *bFirstTime, S_CHAR *bAtomUsedForStereo, - const ppAT_RANK pRankStack1, const ppAT_RANK pRankStack2, - const AT_RANK *nCanonRankFrom, const AT_RANK *nAtomNumberCanonFrom, - const sp_ATOM *at, int num_atoms, int bAllene ); - -int Next_SC_At_CanonRank2( AT_RANK *canon_rank1, /* 1st call input: largest canon number mapped so far or 0 */ - /* output: suggested canon. rank > than input if success */ - AT_RANK *canon_rank1_min, /* 1st call:0 next calls: first tried canon. number */ - int *bFirstTime, /* 1 at the time of the 1st call */ - S_CHAR *bAtomUsedForStereo, /* STEREO_AT_MARK if the atom has not been mapped yet */ - const ppAT_RANK pRankStack1, /* mapping ranks/sort order of atoms with canon. numbers (from) */ - const ppAT_RANK pRankStack2, /* mapping ranks/sort order of atoms with stereo (to) */ - const AT_RANK *nAtomNumberCanonFrom, /* sorted order of the canon. numbers */ - int num_atoms ); - -int NextStereoParity2Test( int *stereo_bond_parity, int *sb_parity_calc, - int nNumBest, int nNumWorse, int nNumUnkn, int nNumUndf, int nNumCalc, - int vABParityUnknown); - -int All_SB_Same( AT_RANK canon_rank1, AT_RANK canon_rank2, /* canonical numbers */ - const ppAT_RANK pRankStack1, const ppAT_RANK pRankStack2, - const AT_RANK *nAtomNumberCanonFrom, - sp_ATOM *at ); - -int All_SC_Same( AT_RANK canon_rank1, /* canonical number */ - const ppAT_RANK pRankStack1, const ppAT_RANK pRankStack2, - const AT_RANK *nAtomNumberCanonFrom, - const sp_ATOM *at ); - - -int CompareLinCtStereoDoubleToValues( AT_STEREO_DBLE *LinearCTStereoDble, - AT_RANK at_rank_canon1, AT_RANK at_rank_canon2, U_CHAR bond_parity ); - -int CompareLinCtStereoAtomToValues( AT_STEREO_CARB *LinearCTStereoCarb, - AT_RANK at_rank_canon1, U_CHAR parity ); -int CompareLinCtStereoDble ( AT_STEREO_DBLE *LinearCTStereoDble1, int nLenLinearCTStereoDble1, - AT_STEREO_DBLE *LinearCTStereoDble2, int nLenLinearCTStereoDble2 ); -int CompareLinCtStereoCarb ( AT_STEREO_CARB *LinearCTStereoCarb1, int nLenLinearCTStereoCarb1, - AT_STEREO_CARB *LinearCTStereoCarb2, int nLenLinearCTStereoCarb2 ); -int CompareLinCtStereo ( AT_STEREO_DBLE *LinearCTStereoDble1, int nLenLinearCTStereoDble1, - AT_STEREO_CARB *LinearCTStereoCarb1, int nLenLinearCTStereoCarb1, - AT_STEREO_DBLE *LinearCTStereoDble2, int nLenLinearCTStereoDble2, - AT_STEREO_CARB *LinearCTStereoCarb2, int nLenLinearCTStereoCarb2 ); -/***************************************************************************/ -/* ichicans.c */ -int UnmarkNonStereo( sp_ATOM *at, int num_atoms, const AT_RANK *nRank, const AT_RANK *nAtomNumber, int bIsotopic ); -int FillSingleStereoDescriptors(sp_ATOM *at, int i, int num_trans, const AT_RANK *nRank - , AT_STEREO_CARB *LinearCTStereoCarb, int *nStereoCarbLen, int nMaxStereoCarbLen - , AT_STEREO_DBLE *LinearCTStereoDble, int *nStereoDbleLen, int nMaxStereoDbleLen - , int bAllene ); -void SwitchAtomStereoAndIsotopicStereo( sp_ATOM *at, int num_atoms, int *bSwitched ); -void SetCtToIsotopicStereo( CANON_STAT *pCS, CANON_STAT *pCS2 ); -void SetCtToNonIsotopicStereo( CANON_STAT *pCS, CANON_STAT *pCS2 ); -int FillAllStereoDescriptors( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, const AT_RANK *nAtomNumberCanon, CANON_STAT *pCS ); -int FillOutStereoParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, const AT_RANK *nAtomNumberCanon, - const AT_RANK *nRank, const AT_RANK *nAtomNumber, CANON_STAT *pCS, int bIsotopic ); -int InvertStereo( sp_ATOM *at, int num_at_tg, - AT_RANK *nCanonRank, AT_RANK *nAtomNumberCanon, - CANON_STAT *pCS, int bInvertLinearCTStereo ); -int find_atoms_with_parity( sp_ATOM *at, S_CHAR *visited, int from_atom, int cur_atom ); -int GetStereoNeighborPos( sp_ATOM *at, int iAt1, int iAt2 ); -int GetStereoBondParity(sp_ATOM *at, int i, int n, AT_RANK *nRank ); -int GetStereoCenterParity(sp_ATOM *at, int i, AT_RANK *nRank ); -int GetPermutationParity( sp_ATOM *at, AT_RANK nAvoidNeighbor, AT_RANK *nCanonRank ); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /* __INCHICOMN_H__ */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _INCHICOMN_H_ +#define _INCHICOMN_H_ + + +#include "ichierr.h" +#include "ichicano.h" + +/* + Globals for sorting + */ + +#define tsort insertions_sort + +typedef struct tagEquNeigh { + int num_to; /* number of neighbors with equal mapping ranks; one of them has min. canon. number */ + AT_RANK to_at[4]; /* to_atom neighbors #s with equal mapping ranks */ + AT_RANK from_at; /* from_at neighbor # which has min. canon. number and can be mapped on any of the above to_at[] */ + AT_RANK rank; /* equal mapping rank value */ + AT_RANK canon_rank; /* min. canon. number */ +} EQ_NEIGH; + +struct tagINCHI_CLOCK; +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + + +struct tagINCHI_CLOCK; + + + +/*******************************************************************/ +/* ichiisot.c */ +int unpack_iso_sort_key( AT_ISO_SORT_KEY iso_sort_key, S_CHAR *num_1H, S_CHAR *num_2H, S_CHAR *num_3H, S_CHAR *iso_atw_diff ); +AT_ISO_SORT_KEY make_iso_sort_key( int iso_atw_diff, int num_1H, int num_2H, int num_3H); +int set_atom_iso_sort_keys( int num_at, sp_ATOM *at, T_GROUP_INFO* t_group_info, int *bHasIsotopicInTautomerGroups ); +/***********************************************************************/ +/* ichisort.c */ + +void insertions_sort_NeighList_AT_NUMBERS( NEIGH_LIST base, AT_RANK *nRank ); +int insertions_sort_NeighList_AT_NUMBERS3( NEIGH_LIST base, AT_RANK *nRank ); +int insertions_sort_AT_RANK( AT_RANK *base, int num ); +void insertions_sort_NeighListBySymmAndCanonRank( NEIGH_LIST base, const AT_RANK *nSymmRank, const AT_RANK *nCanonRank ); +int CompareNeighListLex( NEIGH_LIST pp1, NEIGH_LIST pp2, const AT_RANK *nRank); +int CompareNeighListLexUpToMaxRank( NEIGH_LIST pp1, NEIGH_LIST pp2, const AT_RANK *nRank, AT_RANK nMaxAtNeighRank ); +int compare_NeighLists( const NEIGH_LIST *op1, const NEIGH_LIST *op2, void *p ); +int CompNeighborsAT_NUMBER( const void* a1, const void* a2, void *p); +int comp_AT_RANK( const void* a1, const void* a2, void *); +int CompRank(const void* a1, const void* a2, void *p ); +int CompRanksOrd( const void* a1, const void* a2, void *p ); + +int CompAtomInvariants2Only( const void* a1, const void* a2, void *p ); +int CompAtomInvariants2( const void* a1, const void* a2, void *p ); + +int CompNeighListRanks( const void* a1, const void* a2, void *p ); +int CompNeighListRanksOrd( const void* a1, const void* a2, void *p ); +int CompNeighLists( const void* a1, const void* a2, void *p ); +int CompNeighListsUpToMaxRank( const void* a1, const void* a2, void *p ); +int CompNeighborsRanksCountEql( const void* a1, const void* a2, void *p ); +int CompRanksInvOrd( const void* a1, const void* a2, void * ); +int CompChemElemLex( const void *a1, const void *a2 ); +int* iisort( int *list, int num ); + + +NEIGH_LIST *CreateNeighList( int num_atoms, int num_at_tg, sp_ATOM* at, int bDoubleBondSquare, T_GROUP_INFO *t_group_info ); +NEIGH_LIST *CreateNeighListFromLinearCT( AT_NUMB *LinearCT, int nLenCT, int num_atoms ); + +void FreeNeighList( NEIGH_LIST *pp ); +int BreakAllTies( CANON_GLOBALS *pCG, int num_atoms, int num_max, AT_RANK **pRankStack, + NEIGH_LIST *NeighList, AT_RANK *nTempRank, CANON_STAT *pCS); + +/******************************************************************************/ +/* ichimap.c */ +void switch_ptrs( AT_RANK **p1, AT_RANK **p2 ); + +int SortedEquInfoToRanks( const AT_RANK* nSymmRank, AT_RANK* nRank, const AT_RANK* nAtomNumber, int num_atoms, int *bChanged ); +int SortedRanksToEquInfo( AT_RANK* nSymmRank, const AT_RANK* nRank, const AT_RANK* nAtomNumber, int num_atoms ); + +int SetNewRanksFromNeighLists( CANON_GLOBALS *pCG, int num_atoms, NEIGH_LIST *NeighList, AT_RANK *nRank, AT_RANK *nNewRank, + AT_RANK *nAtomNumber, int bUseAltSort, int ( *comp )(const void *, const void *, void *) ); +int SetNewRanksFromNeighLists3( CANON_GLOBALS *pCG, int num_atoms, NEIGH_LIST *NeighList, AT_RANK *nRank, AT_RANK *nNewRank, + AT_RANK *nAtomNumber ); +int SetNewRanksFromNeighLists4( CANON_GLOBALS *pCG, int num_atoms, NEIGH_LIST *NeighList, AT_RANK *nRank, AT_RANK *nNewRank, + AT_RANK *nAtomNumber, AT_RANK nMaxAtRank ); +void SortNeighListsBySymmAndCanonRank( int num_atoms, NEIGH_LIST *NeighList, const AT_RANK *nSymmRank, const AT_RANK *nCanonRank ); +int SortNeighLists2( int num_atoms, AT_RANK *nRank, NEIGH_LIST *NeighList, AT_RANK *nAtomNumber ); +int DifferentiateRanks2( CANON_GLOBALS *pCG, int num_atoms, NEIGH_LIST *NeighList, + int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, + AT_RANK *nAtomNumber, long *lNumIter, int bUseAltSort ); + +int DifferentiateRanks3( CANON_GLOBALS *pCG, int num_atoms, NEIGH_LIST *NeighList, + int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, + AT_RANK *nAtomNumber, long *lNumIter ); +int DifferentiateRanks4( CANON_GLOBALS *pCG, int num_atoms, NEIGH_LIST *NeighList, + int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, + AT_RANK *nAtomNumber, AT_RANK nMaxAtRank, long *lNumIter ); +int DifferentiateRanksBasic( CANON_GLOBALS *pCG, int num_atoms, NEIGH_LIST *NeighList, + int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, + AT_RANK *nAtomNumber, long *lNumIter, int bUseAltSort ); + +int parity_of_mapped_atom2( CANON_GLOBALS *pCG, int from_at, int to_at, const sp_ATOM *at, EQ_NEIGH *pEN, + const AT_RANK *nCanonRankFrom, const AT_RANK *nRankFrom, const AT_RANK *nRankTo ); + +int parity_of_mapped_half_bond( int from_at, int to_at, int from_neigh, int to_neigh, + sp_ATOM *at, EQ_NEIGH *pEN, + const AT_RANK *nCanonRankFrom, const AT_RANK *nRankFrom, const AT_RANK *nRankTo ); + +int HalfStereoBondParity( sp_ATOM *at, int at_no1, int i_sb_neigh, const AT_RANK *nRank ); + +int NumberOfTies( AT_RANK **pRankStack1, AT_RANK **pRankStack2, int length, int at_no1, int at_no2, AT_RANK *nNewRank, int *bAddStack, int *bMapped1 ); + +int map_an_atom2( CANON_GLOBALS *pCG, int num_atoms, int num_max, int at_no1/*from*/, int at_no2/*to*/, + AT_RANK *nTempRank, + int nNumMappedRanks, int *pnNewNumMappedRanks, + CANON_STAT *pCS, + NEIGH_LIST *NeighList, + AT_RANK **pRankStack1, AT_RANK **pRankStack2, int *bAddStack ); +int ClearPreviousMappings( AT_RANK **pRankStack1 ); + +int SetOneStereoBondIllDefParity( sp_ATOM *at, int jc, /* atom number*/ int k /* stereo bond ord. number*/, int new_parity ); +int RemoveOneStereoBond( sp_ATOM *at, int jc, /* atom number*/ int k /* stereo bond number*/ ); +int RemoveOneStereoCenter( sp_ATOM *at, int jc /* atom number*/ ); +int RemoveCalculatedNonStereo( CANON_GLOBALS *pCG, sp_ATOM *at, int num_atoms, int num_at_tg, + AT_RANK **pRankStack1, AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, + const AT_RANK *nSymmRank, AT_RANK *nCanonRank, + AT_RANK *nAtomNumberCanon, CANON_STAT *pCS, + int vABParityUnknown); + + +int might_change_other_atom_parity( sp_ATOM *at, int num_atoms, int at_no, AT_RANK *nRank2, AT_RANK *nRank1 ); + +int map_stereo_bonds4 ( + struct tagINCHI_CLOCK *ic, CANON_GLOBALS *pCG, sp_ATOM *at, int num_atoms, int num_at_tg, int num_max, int bAllene, + const AT_RANK *nCanonRankFrom, const AT_RANK *nAtomNumberCanonFrom, /* non-stereo canon ranking */ + AT_RANK *nCanonRankTo, /* output canonical numbering*/ + const AT_RANK *nSymmRank, AT_RANK **pRankStack1/*from*/, AT_RANK **pRankStack2/*to*/, + AT_RANK *nTempRank, int nNumMappedRanksInput, + AT_RANK *nSymmStereo, NEIGH_LIST *NeighList, + CANON_STAT *pCS, CUR_TREE *cur_tree, int nNumMappedBonds, + int vABParityUnknown); + +int map_stereo_atoms4 (struct tagINCHI_CLOCK *ic, CANON_GLOBALS *pCG, + sp_ATOM *at, int num_atoms, int num_at_tg, int num_max, + const AT_RANK *nCanonRankFrom, const AT_RANK *nAtomNumberCanonFrom, AT_RANK *nCanonRankTo, /* canonical numbering to be mapped */ + const AT_RANK *nSymmRank, AT_RANK **pRankStack1/*from*/, AT_RANK **pRankStack2/*to*/, + AT_RANK *nTempRank, int nNumMappedRanksInput, + AT_RANK *nSymmStereo, NEIGH_LIST *NeighList, + CANON_STAT *pCS, CUR_TREE *cur_tree, int nNumMappedAtoms, + int vABParityUnknown); + +int CurTreeAlloc( CUR_TREE *cur_tree, int num_atoms ); +int CurTreeReAlloc( CUR_TREE *cur_tree ); +void CurTreeFree( CUR_TREE *cur_tree ); +int CurTreeAddRank( CUR_TREE *cur_tree, AT_NUMB rank ); +int CurTreeRemoveLastRank( CUR_TREE *cur_tree ); +int CurTreeReplaceLastRank( CUR_TREE *cur_tree, AT_NUMB rank ); +int CurTreeFindTheRankPos( CUR_TREE *cur_tree, AT_NUMB rank ); +int CurTreeGetPos( CUR_TREE *cur_tree ); +int CurTreeSetPos( CUR_TREE *cur_tree, int len ); +int CurTreeAddAtom( CUR_TREE *cur_tree, int at_no ); +int CurTreeRemoveLastAtom( CUR_TREE *cur_tree ); +int CurTreeIsLastRank( CUR_TREE *cur_tree, AT_NUMB rank ); +int CurTreeIsLastAtomEqu( CUR_TREE *cur_tree, int at_no, AT_NUMB *nSymmStereo ); +int CurTreeRemoveIfLastAtom( CUR_TREE *cur_tree, int at_no ); +int CurTreeRemoveLastRankIfNoAtoms( CUR_TREE *cur_tree ); +void CurTreeKeepLastAtomsOnly( CUR_TREE *cur_tree, int tpos, int shift ); + + +void SetUseAtomForStereo( S_CHAR *bAtomUsedForStereo, sp_ATOM *at, int num_atoms ); + +int nJoin2Mcrs( AT_RANK *nEqArray, AT_RANK n1, AT_RANK n2 ); +AT_RANK nGetMcr( AT_RANK *nEqArray, AT_RANK n ); +int bUniqueAtNbrFromMappingRank( AT_RANK **pRankStack, AT_RANK nAtRank, AT_NUMB *nAtNumber ); + +int Next_SB_At_CanonRanks2( AT_RANK *canon_rank1, AT_RANK *canon_rank2, /* canonical numbers */ + AT_RANK *canon_rank1_min, AT_RANK *canon_rank2_min, + int *bFirstTime, S_CHAR *bAtomUsedForStereo, + const ppAT_RANK pRankStack1, const ppAT_RANK pRankStack2, + const AT_RANK *nCanonRankFrom, const AT_RANK *nAtomNumberCanonFrom, + const sp_ATOM *at, int num_atoms, int bAllene ); + +int Next_SC_At_CanonRank2( AT_RANK *canon_rank1, /* 1st call input: largest canon number mapped so far or 0 */ + /* output: suggested canon. rank > than input if success */ + AT_RANK *canon_rank1_min, /* 1st call:0 next calls: first tried canon. number */ + int *bFirstTime, /* 1 at the time of the 1st call */ + S_CHAR *bAtomUsedForStereo, /* STEREO_AT_MARK if the atom has not been mapped yet */ + const ppAT_RANK pRankStack1, /* mapping ranks/sort order of atoms with canon. numbers (from) */ + const ppAT_RANK pRankStack2, /* mapping ranks/sort order of atoms with stereo (to) */ + const AT_RANK *nAtomNumberCanonFrom, /* sorted order of the canon. numbers */ + int num_atoms ); + +int NextStereoParity2Test( int *stereo_bond_parity, int *sb_parity_calc, + int nNumBest, int nNumWorse, int nNumUnkn, int nNumUndf, int nNumCalc, + int vABParityUnknown); + +int All_SB_Same( AT_RANK canon_rank1, AT_RANK canon_rank2, /* canonical numbers */ + const ppAT_RANK pRankStack1, const ppAT_RANK pRankStack2, + const AT_RANK *nAtomNumberCanonFrom, + sp_ATOM *at ); + +int All_SC_Same( AT_RANK canon_rank1, /* canonical number */ + const ppAT_RANK pRankStack1, const ppAT_RANK pRankStack2, + const AT_RANK *nAtomNumberCanonFrom, + const sp_ATOM *at ); + + +int CompareLinCtStereoDoubleToValues( AT_STEREO_DBLE *LinearCTStereoDble, + AT_RANK at_rank_canon1, AT_RANK at_rank_canon2, U_CHAR bond_parity ); + +int CompareLinCtStereoAtomToValues( AT_STEREO_CARB *LinearCTStereoCarb, + AT_RANK at_rank_canon1, U_CHAR parity ); +int CompareLinCtStereoDble ( AT_STEREO_DBLE *LinearCTStereoDble1, int nLenLinearCTStereoDble1, + AT_STEREO_DBLE *LinearCTStereoDble2, int nLenLinearCTStereoDble2 ); +int CompareLinCtStereoCarb ( AT_STEREO_CARB *LinearCTStereoCarb1, int nLenLinearCTStereoCarb1, + AT_STEREO_CARB *LinearCTStereoCarb2, int nLenLinearCTStereoCarb2 ); +int CompareLinCtStereo ( AT_STEREO_DBLE *LinearCTStereoDble1, int nLenLinearCTStereoDble1, + AT_STEREO_CARB *LinearCTStereoCarb1, int nLenLinearCTStereoCarb1, + AT_STEREO_DBLE *LinearCTStereoDble2, int nLenLinearCTStereoDble2, + AT_STEREO_CARB *LinearCTStereoCarb2, int nLenLinearCTStereoCarb2 ); +/***************************************************************************/ +/* ichicans.c */ +int UnmarkNonStereo( CANON_GLOBALS *pCG, sp_ATOM *at, int num_atoms, const AT_RANK *nRank, const AT_RANK *nAtomNumber, int bIsotopic ); +int FillSingleStereoDescriptors(CANON_GLOBALS *pCG, sp_ATOM *at, int i, int num_trans, const AT_RANK *nRank + , AT_STEREO_CARB *LinearCTStereoCarb, int *nStereoCarbLen, int nMaxStereoCarbLen + , AT_STEREO_DBLE *LinearCTStereoDble, int *nStereoDbleLen, int nMaxStereoDbleLen + , int bAllene ); +void SwitchAtomStereoAndIsotopicStereo( sp_ATOM *at, int num_atoms, int *bSwitched ); +void SetCtToIsotopicStereo( CANON_STAT *pCS, CANON_STAT *pCS2 ); +void SetCtToNonIsotopicStereo( CANON_STAT *pCS, CANON_STAT *pCS2 ); +int FillAllStereoDescriptors( CANON_GLOBALS *pCG, sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, const AT_RANK *nAtomNumberCanon, CANON_STAT *pCS ); +int FillOutStereoParities( sp_ATOM *at, int num_atoms, const AT_RANK *nCanonRank, const AT_RANK *nAtomNumberCanon, + const AT_RANK *nRank, const AT_RANK *nAtomNumber, CANON_STAT *pCS, CANON_GLOBALS *pCG, int bIsotopic ); +int InvertStereo( sp_ATOM *at, int num_at_tg, + AT_RANK *nCanonRank, AT_RANK *nAtomNumberCanon, + CANON_STAT *pCS, int bInvertLinearCTStereo ); +int find_atoms_with_parity( sp_ATOM *at, S_CHAR *visited, int from_atom, int cur_atom ); +int GetStereoNeighborPos( sp_ATOM *at, int iAt1, int iAt2 ); +int GetStereoBondParity(sp_ATOM *at, int i, int n, AT_RANK *nRank ); +int GetStereoCenterParity(CANON_GLOBALS *pCG, sp_ATOM *at, int i, AT_RANK *nRank ); +int GetPermutationParity( CANON_GLOBALS *pCG, sp_ATOM *at, AT_RANK nAvoidNeighbor, AT_RANK *nCanonRank ); + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + + +#endif /* _INCHICOMN_H_ */ diff --git a/INCHI-1-SRC/INCHI/common/ichicomp.h b/INCHI-1-SRC/INCHI_BASE/src/ichicomp.h similarity index 60% rename from INCHI-1-SRC/INCHI/common/ichicomp.h rename to INCHI-1-SRC/INCHI_BASE/src/ichicomp.h index d5451f9..d8c68ea 100644 --- a/INCHI-1-SRC/INCHI/common/ichicomp.h +++ b/INCHI-1-SRC/INCHI_BASE/src/ichicomp.h @@ -1,96 +1,93 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHI_COMPAT_H__ -#define __INCHI_COMPAT_H__ - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - /* compatibility */ - -#if ( defined(__GNUC__) && defined(__MINGW32__) && __GNUC__ == 3 && __GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ == 0 && defined(_WIN32) ) -/* replace with the proper definition for GNU gcc & MinGW-2.0.0-3 (mingw special 20020817-1), gcc 3.2 core */ -#define my_va_start(A,B) ((A)=(va_list)__builtin_next_arg(lpszFormat)) -#else -#define my_va_start va_start -#endif - - - -/* ANSI redefinitions */ -#ifdef COMPILE_ANSI_ONLY /* { */ -#ifndef __isascii -#define __isascii(val) ((unsigned)(val) <= 0x7F) -#endif - -/* #ifndef __GNUC__ */ -/* these non-ANSI functions are implemented in gcc */ -#include - /* this #include provides size_t definition */ - /* implementation is located in util.c */ -/*#if ( !defined(_MSC_VER) || defined(__STDC__) && __STDC__ == 1 )*/ - -#if ( defined(COMPILE_ADD_NON_ANSI_FUNCTIONS) || defined(__STDC__) && __STDC__ == 1 ) - -/* support (VC++ Language extensions) = OFF && defined(COMPILE_ANSI_ONLY) */ -int memicmp (const void*, const void*, size_t); -int stricmp( const char *s1, const char *s2 ); -char *_strnset( char *string, int c, size_t count ); -char *_strdup( const char *string ); -#endif -/* #endif */ - -#endif /* } */ - -#define inchi_max(a,b) (((a)>(b))?(a):(b)) -#define inchi_min(a,b) (((a)<(b))?(a):(b)) - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /* __INCHI_COMPAT_H__ */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _INCHI_COMP_H_ +#define _INCHI_COMP_H_ + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + + /* compatibility */ + +#if ( defined(__GNUC__) && defined(__MINGW32__) && __GNUC__ == 3 && __GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ == 0 && defined(_WIN32) ) +/* replace with the proper definition for GNU gcc & MinGW-2.0.0-3 (mingw special 20020817-1), gcc 3.2 core */ +#define my_va_start(A,B) ((A)=(va_list)__builtin_next_arg(lpszFormat)) +#else +#define my_va_start va_start +#endif + + + +/* ANSI redefinitions */ +#ifdef COMPILE_ANSI_ONLY /* { */ +#ifndef __isascii +#define __isascii(val) ((unsigned)(val) <= 0x7F) +#endif + +/* #ifndef __GNUC__ */ +/* these non-ANSI functions are implemented in gcc */ +#include + /* this #include provides size_t definition */ + /* implementation is located in util.c */ +/*#if ( !defined(_MSC_VER) || defined(__STDC__) && __STDC__ == 1 )*/ + +#if ( defined(COMPILE_ADD_NON_ANSI_FUNCTIONS) || defined(__STDC__) && __STDC__ == 1 ) + +/* support (VC++ Language extensions) = OFF && defined(COMPILE_ANSI_ONLY) */ +int inchi_memicmp (const void*, const void*, size_t); +int inchi_stricmp( const char *s1, const char *s2 ); +char *inchi__strnset( char *string, int c, size_t count ); +char *inchi__strdup( const char *string ); +#endif +/* #endif */ + +#endif /* } */ + + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + + +#endif /* _INCHI_COMP_H_ */ diff --git a/INCHI-1-SRC/INCHI/common/ichidrp.h b/INCHI-1-SRC/INCHI_BASE/src/ichidrp.h similarity index 76% rename from INCHI-1-SRC/INCHI/common/ichidrp.h rename to INCHI-1-SRC/INCHI_BASE/src/ichidrp.h index 89c9f63..e54776f 100644 --- a/INCHI-1-SRC/INCHI/common/ichidrp.h +++ b/INCHI-1-SRC/INCHI_BASE/src/ichidrp.h @@ -1,180 +1,197 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHIDRP_H__ -#define __INCHIDRP_H__ - -#ifndef COMPILE_ANSI_ONLY /* { */ -/******************************************** - * Parameters for the structure drawing - ********************************************/ -#define TDP_LEN_LBL 16 /* length of a label (label: Req., Shown, Found) */ -/* #define TDP_NUM_LBL 3 */ /* number of labels */ -/* #define TDP_NUM_PAR 3 */ /* number of types per label (types: B/T, I/N, S) */ -typedef enum tagTblTypes {itBASIC, itISOTOPIC, itSTEREO, TDP_NUM_PAR} TBL_TYPES; /* types */ -typedef enum tagTblLabels{ ilSHOWN, TDP_NUM_LBL} TBL_LABELS; /* labels */ -typedef struct tagTblDrawPatms { - char ReqShownFoundTxt[TDP_NUM_LBL][TDP_LEN_LBL]; - char ReqShownFound[TDP_NUM_LBL][TDP_NUM_PAR]; - int nOrientation; /* 10*degrees: 0 or 2700 */ - int bDrawTbl; -} TBL_DRAW_PARMS; -/*********************************************/ -typedef struct tagDrawParmsSettings { - TBL_DRAW_PARMS *tdp; - unsigned long ulDisplTime; - int bOrigAtom; - int nFontSize; -} SET_DRAW_PARMS; /* input only: how to draw or calculate */ -/*********************************************/ -typedef struct tagReturnedDrawParms { - int bEsc; -} RET_DRAW_PARMS; -/*********************************************/ -typedef struct tagPersistDrawParms { - int rcPict[4]; -} PER_DRAW_PARMS; /* saved between displaying different structures */ -/*********************************************/ -typedef struct tagDrawParms { - SET_DRAW_PARMS sdp; /* how to draw: fill on the 1st call */ - RET_DRAW_PARMS rdp; /* returned when drawing window is closed */ - PER_DRAW_PARMS *pdp; /* persistent: save between calls (window size) */ -#ifndef TARGET_LIB_FOR_WINCHI -#ifndef COMPILE_ANSI_ONLY - AT_NUMB *nEquLabels; /* num_inp_atoms elements, value>0 marks atoms in the set #value */ - AT_NUMB nNumEquSets; /* max mark value */ - AT_NUMB nCurEquLabel; /* current mark */ -#endif -#endif -} DRAW_PARMS; /* Settings: How to draw the structure */ - -#endif /* } COMPILE_ANSI_ONLY */ - -#define MAX_NUM_PATHS 4 - - -typedef enum tagInputType { INPUT_NONE=0, INPUT_MOLFILE=1, INPUT_SDFILE=2, INPUT_INCHI_XML=3, INPUT_INCHI_PLAIN=4, INPUT_CMLFILE=5, INPUT_INCHI=6, INPUT_MAX } INPUT_TYPE; - -/* bCalcInChIHash values */ -typedef enum tagInChIHashCalc -{ - INCHIHASH_NONE=0, - INCHIHASH_KEY=1, - INCHIHASH_KEY_XTRA1=2, - INCHIHASH_KEY_XTRA2=3, - INCHIHASH_KEY_XTRA1_XTRA2=4 -} -INCHI_HASH_CALC; - -typedef struct tagInputParms { - char szSdfDataHeader[MAX_SDF_HEADER+1]; - char *pSdfLabel; - char *pSdfValue; - long lSdfId; - long lMolfileNumber; -#ifndef COMPILE_ANSI_ONLY - DRAW_PARMS dp; - PER_DRAW_PARMS pdp; - TBL_DRAW_PARMS tdp; -#endif -/* - -- Files -- - ip->path[0] => Input - ip->path[1] => Output (INChI) - ip->path[2] => Log - ip->path[3] => Problem structures - ip->path[4] => Errors file (ACD) - -*/ - const char *path[MAX_NUM_PATHS]; - int num_paths; - long first_struct_number; - long last_struct_number; - INPUT_TYPE nInputType; - INCHI_MODE nMode; - int bAbcNumbers; - /*int bXml;*/ - int bINChIOutputOptions; /* !(ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) */ - int bCtPredecessors; - int bXmlStarted; - int bDisplayEachComponentINChI; - - long msec_MaxTime; /* was ulMaxTime; max time to run ProsessOneStructure */ - long msec_LeftTime; - - long ulDisplTime; /* not used: max structure or question display time */ - int bDisplay; - int bDisplayIfRestoreWarnings; /* InChI->Struct debug */ - int bMergeAllInputStructures; - int bSaveWarningStructsAsProblem; - int bSaveAllGoodStructsAsProblem; - int bGetSdfileId; - int bGetMolfileNumber; /* read molfile number from the name line like "Structure #22" */ - int bCompareComponents; /* see flags CMP_COMPONENTS, etc. */ - int bDisplayCompositeResults; - int bDoNotAddH; - int bNoStructLabels; - int bChiralFlag; - int bAllowEmptyStructure; - /*^^^ */ - int bCalcInChIHash; - int bFixNonUniformDraw; /* correct non-uniformly drawn oxoanions and amidinium cations. */ - /*^^^ */ - INCHI_MODE bTautFlags; - INCHI_MODE bTautFlagsDone; - -#if ( READ_INCHI_STRING == 1 ) - int bReadInChIOptions; -#endif - -/* post v.1 features */ -#if ( UNDERIVATIZE == 1 ) - int bUnderivatize; -#endif -#if ( RING2CHAIN == 1 ) - int bRing2Chain; -#endif -#if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) - int bIngnoreUnchanged; -#endif - -} INPUT_PARMS; - -#endif /* __INCHIDRP_H__ */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _ICHIDRP_H_ +#define _ICHIDRP_H_ + + +#include "incomdef.h" + +/******************************************** + * Parameters for the structure drawing + ********************************************/ +#define TDP_LEN_LBL 16 /* length of a label (label: Req., Shown, Found) */ +/* #define TDP_NUM_LBL 3 */ /* number of labels */ +/* #define TDP_NUM_PAR 3 */ /* number of types per label (types: B/T, I/N, S) */ +typedef enum tagTblTypes {itBASIC, itISOTOPIC, itSTEREO, TDP_NUM_PAR} TBL_TYPES; /* types */ +typedef enum tagTblLabels{ ilSHOWN, TDP_NUM_LBL} TBL_LABELS; /* labels */ +typedef struct tagTblDrawPatms { + char ReqShownFoundTxt[TDP_NUM_LBL][TDP_LEN_LBL]; + char ReqShownFound[TDP_NUM_LBL][TDP_NUM_PAR]; + int nOrientation; /* 10*degrees: 0 or 2700 */ + int bDrawTbl; +} TBL_DRAW_PARMS; +/*********************************************/ +typedef struct tagDrawParmsSettings { + TBL_DRAW_PARMS *tdp; + unsigned long ulDisplTime; + int bOrigAtom; + int nFontSize; +} SET_DRAW_PARMS; /* input only: how to draw or calculate */ +/*********************************************/ +typedef struct tagReturnedDrawParms { + int bEsc; +} RET_DRAW_PARMS; +/*********************************************/ +typedef struct tagPersistDrawParms { + int rcPict[4]; +} PER_DRAW_PARMS; /* saved between displaying different structures */ +/*********************************************/ +typedef struct tagDrawParms { + SET_DRAW_PARMS sdp; /* how to draw: fill on the 1st call */ + RET_DRAW_PARMS rdp; /* returned when drawing window is closed */ + PER_DRAW_PARMS *pdp; /* persistent: save between calls (window size) */ +#ifndef TARGET_LIB_FOR_WINCHI +#ifndef COMPILE_ANSI_ONLY + AT_NUMB *nEquLabels; /* num_inp_atoms elements, value>0 marks atoms in the set #value */ + AT_NUMB nNumEquSets; /* max mark value */ + AT_NUMB nCurEquLabel; /* current mark */ +#endif +#endif +} DRAW_PARMS; /* Settings: How to draw the structure */ + +/* @@@ #endif */ /* } COMPILE_ANSI_ONLY */ + +#define MAX_NUM_PATHS 4 + + +typedef enum tagInputType +{ + INPUT_NONE=0, + INPUT_MOLFILE=1, + INPUT_SDFILE=2, + INPUT_INCHI_XML=3, /* obsolete */ + INPUT_INCHI_PLAIN=4, + INPUT_CMLFILE=5, /* obsolete */ + INPUT_INCHI=6, + INPUT_MAX +} +INPUT_TYPE; + +/* bCalcInChIHash values */ +typedef enum tagInChIHashCalc +{ + INCHIHASH_NONE=0, + INCHIHASH_KEY=1, + INCHIHASH_KEY_XTRA1=2, + INCHIHASH_KEY_XTRA2=3, + INCHIHASH_KEY_XTRA1_XTRA2=4 +} +INCHI_HASH_CALC; + +typedef struct tagInputParms { + char szSdfDataHeader[MAX_SDF_HEADER+1]; + char *pSdfLabel; + char *pSdfValue; + long lSdfId; + long lMolfileNumber; + +/* @@@ +#if ( defined( TARGET_LIB_FOR_WINCHI ) || defined(TARGET_EXE_STANDALONE) ) +#ifndef COMPILE_ANSI_ONLY +*/ + DRAW_PARMS dp; + PER_DRAW_PARMS pdp; + TBL_DRAW_PARMS tdp; +/* @@@ #endif +#endif +*/ + +/* + -- Files -- + ip->path[0] => Input + ip->path[1] => Output (INChI) + ip->path[2] => Log + ip->path[3] => Problem structures + ip->path[4] => Errors file (ACD) + +*/ + const char *path[MAX_NUM_PATHS]; + int num_paths; + long first_struct_number; + long last_struct_number; + INPUT_TYPE nInputType; + INCHI_MODE nMode; + int bAbcNumbers; + int bINChIOutputOptions; /* !(ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) */ + int bINChIOutputOptions2; /* v. 1.05 */ + int bCtPredecessors; + int bDisplayEachComponentINChI; + long msec_MaxTime; /* was ulMaxTime; max time to run ProsessOneStructure */ + long msec_LeftTime; + long ulDisplTime; /* not used: max structure or question display time */ + int bDisplay; + int bDisplayIfRestoreWarnings; /* InChI->Struct debug */ + int bMergeAllInputStructures; + int bSaveWarningStructsAsProblem; + int bSaveAllGoodStructsAsProblem; + int bGetSdfileId; + int bGetMolfileNumber; /* read molfile number from the name line like "Structure #22" */ + int bCompareComponents; /* see flags CMP_COMPONENTS, etc. */ + int bDisplayCompositeResults; + int bDoNotAddH; + int bNoStructLabels; + int bChiralFlag; + int bAllowEmptyStructure; + int bLargeMolecules; /* v. 1.05 */ + int bPolymers; /* v. 1.05 */ + int bCalcInChIHash; + int bFixNonUniformDraw; /* correct non-uniformly drawn oxoanions and amidinium cations. */ + /* */ + INCHI_MODE bTautFlags; + INCHI_MODE bTautFlagsDone; + +#if ( READ_INCHI_STRING == 1 ) + int bReadInChIOptions; +#endif + +/* post v.1 features */ +#if ( UNDERIVATIZE == 1 ) + int bUnderivatize; +#endif +#if ( RING2CHAIN == 1 ) + int bRing2Chain; +#endif +#if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) + int bIngnoreUnchanged; +#endif +} INPUT_PARMS; + + +#endif /* _ICHIDRP_H_ */ diff --git a/INCHI-1-SRC/INCHI_BASE/src/ichierr.c b/INCHI-1-SRC/INCHI_BASE/src/ichierr.c new file mode 100644 index 0000000..604ca60 --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/ichierr.c @@ -0,0 +1,130 @@ +#pragma warning( disable : 4706 4127 4514 4100 4786 4996 4244 4267 ) + + + +#include + +#include "mode.h" +#include "ichierr.h" + + + +static int already_have_this_message( char *prev_messages, const char *new_message ); + + + +/****************************************************************************/ +const char *ErrMsg( int nErrorCode ) +{ + const char *p; + static char szErrMsg[64]; + switch( nErrorCode ) { + case 0: p = ""; break; + case CT_OVERFLOW: p = "ARRAY OVERFLOW"; break; + case CT_LEN_MISMATCH: p = "LENGTH_MISMATCH"; break; + case CT_OUT_OF_RAM: p = "Out of RAM"; break; + case CT_RANKING_ERR: p = "RANKING_ERR"; break; + case CT_ISOCOUNT_ERR: p = "ISOCOUNT_ERR"; break; + case CT_TAUCOUNT_ERR: p = "TAUCOUNT_ERR"; break; + case CT_ISOTAUCOUNT_ERR: p = "ISOTAUCOUNT_ERR"; break; + case CT_MAPCOUNT_ERR: p = "MAPCOUNT_ERR"; break; + case CT_TIMEOUT_ERR: p = "Time limit exceeded"; break; + case CT_ISO_H_ERR: p = "ISO_H_ERR"; break; + case CT_STEREOCOUNT_ERR: p = "STEREOCOUNT_ERR"; break; + case CT_ATOMCOUNT_ERR: p = "ATOMCOUNT_ERR"; break; + case CT_STEREOBOND_ERROR: p = "STEREOBOND_ERR"; break; + case CT_USER_QUIT_ERR: p = "User requested termination"; break; + case CT_REMOVE_STEREO_ERR: p = "REMOVE_STEREO_ERR"; break; + case CT_CALC_STEREO_ERR: p = "CALC_STEREO_ERR"; break; + case CT_STEREO_CANON_ERR: p = "STEREO_CANON_ERR"; break; + case CT_CANON_ERR: p = "CANON_ERR"; break; + case CT_WRONG_FORMULA: p = "Wrong or missing chemical formula"; break; + /*case CT_CANON_ERR2: p = "CT_CANON_ERR2"; break;*/ + case CT_UNKNOWN_ERR: p = "UNKNOWN_ERR"; break; + case BNS_RADICAL_ERR: p = "Cannot process free radical center"; break; + case BNS_ALTBOND_ERR: p = "Cannot process aromatic bonds"; break; + /* v. 1.05 */ + case BNS_TIMEOUT: p = "Structure normalization timeout"; break; + + default: + if ( nErrorCode > CT_UNKNOWN_ERR ) + { + sprintf( szErrMsg, "No description(%d)", nErrorCode ); + p = szErrMsg; + } + else + { + sprintf( szErrMsg, "UNKNOWN_ERR(%d)", CT_UNKNOWN_ERR - nErrorCode ); + p = szErrMsg; + } + break; + } + + return p; +} + + +/*************************************************************************/ +int AddErrorMessage( char *all_messages, const char *new_message ) +{ +int len_all, len; + + if ( !all_messages ) + return 0; + if ( !new_message ) + return 0; + if ( !new_message[0] ) + return 0; + + if ( already_have_this_message(all_messages, new_message) ) + return 1; + + + len_all = (int) strlen(all_messages); + len = (int) strlen(new_message); + + if ( len_all + len + 2*(len_all > 0) < STR_ERR_LEN ) + { + /* enough room... add message and return */ + if (len_all > 0) + { + if ( all_messages[len_all-1] != ':' ) + { + strcat( all_messages, ";" ); + } + strcat( all_messages, " " ); + } + strcat( all_messages, new_message ); + return 1; + } + + /* not enough room... add no-room marker if not yet added */ + if ( strstr( all_messages, "..." ) ) + return 0; + if ( len_all + 3 < STR_ERR_LEN ) + strcat( all_messages, "..." ); + + return 0; +} + + +/*************** static **********************************************************/ +int already_have_this_message( char *prev_messages, const char *new_message ) +{ +int have=0; + + char *p = strstr( prev_messages, new_message ); + + if ( p ) + { + have = ( p==prev_messages || *(p-1) == ' ' && (*(p-2) == ';' || *(p-2) == ':' )); + if ( have ) + { + int len_prev = (int) strlen(prev_messages); + int len = (int) strlen(new_message); + have = ( p+len==prev_messages+len_prev || p[len] == ';' && p[len+1] == ' ' || p[len-1]==':' && p[len]==' ' ); + } + } + + return have; +} diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichierr.h b/INCHI-1-SRC/INCHI_BASE/src/ichierr.h similarity index 78% rename from INCHI-1-SRC/INCHI_API/inchi_dll/ichierr.h rename to INCHI-1-SRC/INCHI_BASE/src/ichierr.h index a9a1e7a..570f5b8 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichierr.h +++ b/INCHI-1-SRC/INCHI_BASE/src/ichierr.h @@ -1,157 +1,173 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHIERR_H__ -#define __INCHIERR_H__ - -#define _IS_OKAY 0 -#define _IS_WARNING 1 -#define _IS_ERROR 2 /* Microsoft defined its own IS_ERROR() macro */ -#define _IS_FATAL 3 -#define _IS_UNKNOWN 4 /* unknown error: used in INChI DLL only */ -#define _IS_EOF -1 /* end of file */ -#define _IS_SKIP -2 - -#define CT_ERR_FIRST (-30000) -#define CT_OVERFLOW (CT_ERR_FIRST- 0) /*(-30000) */ -#define CT_LEN_MISMATCH (CT_ERR_FIRST- 1) /*(-30001) */ -#define CT_OUT_OF_RAM (CT_ERR_FIRST- 2) /*(-30002) */ -#define CT_RANKING_ERR (CT_ERR_FIRST- 3) /*(-30003) */ -#define CT_ISOCOUNT_ERR (CT_ERR_FIRST- 4) /*(-30004) */ -#define CT_TAUCOUNT_ERR (CT_ERR_FIRST- 5) /*(-30005) */ -#define CT_ISOTAUCOUNT_ERR (CT_ERR_FIRST- 6) /*(-30006) */ -#define CT_MAPCOUNT_ERR (CT_ERR_FIRST- 7) /*(-30007) */ -#define CT_TIMEOUT_ERR (CT_ERR_FIRST- 8) /*(-30008) */ -#define CT_ISO_H_ERR (CT_ERR_FIRST- 9) /*(-30009) */ -#define CT_STEREOCOUNT_ERR (CT_ERR_FIRST-10) /*(-30010) */ -#define CT_ATOMCOUNT_ERR (CT_ERR_FIRST-11) /*(-30011) */ -#define CT_STEREOBOND_ERROR (CT_ERR_FIRST-12) /*(-30012) */ -#define CT_USER_QUIT_ERR (CT_ERR_FIRST-13) /*(-30013) */ -#define CT_REMOVE_STEREO_ERR (CT_ERR_FIRST-14) /*(-30014) */ -#define CT_CALC_STEREO_ERR (CT_ERR_FIRST-15) /*(-30015) */ -#define CT_CANON_ERR (CT_ERR_FIRST-16) /*(-30016) */ -#define CT_STEREO_CANON_ERR (CT_ERR_FIRST-17) /*(-30017) */ -#define CT_WRONG_FORMULA (CT_ERR_FIRST-18) /*(-30017) */ -#define CT_UNKNOWN_ERR (CT_ERR_FIRST-19) /*(-30019) */ - -#define CT_ERR_MIN CT_UNKNOWN_ERR -#define CT_ERR_MAX CT_ERR_FIRST - -#define CHECK_OVERFLOW(Len, Maxlen) ( (Len) >= (Maxlen) ) -#define RETURNED_ERROR(nVal) (CT_ERR_MIN<=(nVal) && (nVal)<=CT_ERR_MAX) - - -#define BNS_ERR -9999 -#define BNS_WRONG_PARMS (BNS_ERR + 0) /*(-9999)*/ -#define BNS_OUT_OF_RAM (BNS_ERR + 1) /*(-9998)*/ -#define BNS_PROGRAM_ERR (BNS_ERR + 2) /*(-9997)*/ -#define BNS_ALTPATH_OVFL (BNS_ERR + 3) /*(-9996)*/ -#define BNS_BOND_ERR (BNS_ERR + 4) /*(-9995)*/ -#define BNS_VERT_NUM_ERR (BNS_ERR + 5) /*(-9994)*/ -#define BNS_VERT_EDGE_OVFL (BNS_ERR + 6) /*(-9993)*/ -#define BNS_SET_ALTP_ERR (BNS_ERR + 7) /*(-9992)*/ -#define BNS_CPOINT_ERR (BNS_ERR + 8) /*(-9991)*/ -#define BNS_CANT_SET_BOND (BNS_ERR + 9) /*(-9990)*/ -#define BNS_CAP_FLOW_ERR (BNS_ERR + 10) /*(-9989)*/ -#define BNS_RADICAL_ERR (BNS_ERR + 11) /*(-9988)*/ -#define BNS_REINIT_ERR (BNS_ERR + 12) /*(-9987)*/ -#define BNS_ALTBOND_ERR (BNS_ERR + 13) /*(-9986)*/ - -#define BNS_MAX_ERR_VALUE (BNS_ERR + 19) /*(-9980)*/ - -#define IS_BNS_ERROR(X) (BNS_ERR <= (X) && (X) <= BNS_MAX_ERR_VALUE) - - -#define INCHI_INP_ERROR_ERR 40 -#define INCHI_INP_ERROR_RET (-1) - -#define INCHI_INP_FATAL_ERR 1 -#define INCHI_INP_FATAL_RET 0 - -#define INCHI_INP_EOF_ERR 11 -#define INCHI_INP_EOF_RET 0 - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -extern int (*UserAction)(void); /* callback */ -extern int (*ConsoleQuit)(void); /* Console user issued CTRL+C etc. */ - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -#define LOG_MASK_WARN 1 -#define LOG_MASK_ERR 2 -#define LOG_MASK_FATAL 4 - -#define LOG_MASK_ALL (LOG_MASK_WARN | LOG_MASK_ERR | LOG_MASK_FATAL) -#define LOG_MASK_NO_WARN (LOG_MASK_ERR | LOG_MASK_FATAL) - -#ifdef TARGET_LIB_FOR_WINCHI -#include - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -extern void (*FWPRINT) (const char * format, va_list argptr ); -extern void (*DRAWDATA) ( struct DrawData * pDrawData); -extern int (*DRAWDATA_EXISTS) ( int nComponent, int nType, int bReconnected ); -extern struct DrawData * (*GET_DRAWDATA) ( int nComponent, int nType, int bReconnected ); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -#endif - -#define USER_ACTION_QUIT 1 - -#endif /* __INCHIERR_H__ */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _ICCHIERR_H_ +#define _ICCHIERR_H_ + +#define _IS_OKAY 0 +#define _IS_WARNING 1 +#define _IS_ERROR 2 /* Microsoft defined its own IS_ERROR() macro */ +#define _IS_FATAL 3 +#define _IS_UNKNOWN 4 /* unknown error: used in INChI DLL only */ +#define _IS_EOF -1 /* end of file */ +#define _IS_SKIP -2 + +#define CT_ERR_FIRST (-30000) +#define CT_OVERFLOW (CT_ERR_FIRST- 0) /*(-30000) */ +#define CT_LEN_MISMATCH (CT_ERR_FIRST- 1) /*(-30001) */ +#define CT_OUT_OF_RAM (CT_ERR_FIRST- 2) /*(-30002) */ +#define CT_RANKING_ERR (CT_ERR_FIRST- 3) /*(-30003) */ +#define CT_ISOCOUNT_ERR (CT_ERR_FIRST- 4) /*(-30004) */ +#define CT_TAUCOUNT_ERR (CT_ERR_FIRST- 5) /*(-30005) */ +#define CT_ISOTAUCOUNT_ERR (CT_ERR_FIRST- 6) /*(-30006) */ +#define CT_MAPCOUNT_ERR (CT_ERR_FIRST- 7) /*(-30007) */ +#define CT_TIMEOUT_ERR (CT_ERR_FIRST- 8) /*(-30008) */ +#define CT_ISO_H_ERR (CT_ERR_FIRST- 9) /*(-30009) */ +#define CT_STEREOCOUNT_ERR (CT_ERR_FIRST-10) /*(-30010) */ +#define CT_ATOMCOUNT_ERR (CT_ERR_FIRST-11) /*(-30011) */ +#define CT_STEREOBOND_ERROR (CT_ERR_FIRST-12) /*(-30012) */ +#define CT_USER_QUIT_ERR (CT_ERR_FIRST-13) /*(-30013) */ +#define CT_REMOVE_STEREO_ERR (CT_ERR_FIRST-14) /*(-30014) */ +#define CT_CALC_STEREO_ERR (CT_ERR_FIRST-15) /*(-30015) */ +#define CT_CANON_ERR (CT_ERR_FIRST-16) /*(-30016) */ +#define CT_STEREO_CANON_ERR (CT_ERR_FIRST-17) /*(-30017) */ +#define CT_WRONG_FORMULA (CT_ERR_FIRST-18) /*(-30017) */ +#define CT_UNKNOWN_ERR (CT_ERR_FIRST-19) /*(-30019) */ + +#define CT_ERR_MIN CT_UNKNOWN_ERR +#define CT_ERR_MAX CT_ERR_FIRST + +#define CHECK_OVERFLOW(Len, Maxlen) ( (Len) >= (Maxlen) ) +#define RETURNED_ERROR(nVal) (CT_ERR_MIN<=(nVal) && (nVal)<=CT_ERR_MAX) + + +#define BNS_ERR -9999 +#define BNS_WRONG_PARMS (BNS_ERR + 0) /*(-9999)*/ +#define BNS_OUT_OF_RAM (BNS_ERR + 1) /*(-9998)*/ +#define BNS_PROGRAM_ERR (BNS_ERR + 2) /*(-9997)*/ +#define BNS_ALTPATH_OVFL (BNS_ERR + 3) /*(-9996)*/ +#define BNS_BOND_ERR (BNS_ERR + 4) /*(-9995)*/ +#define BNS_VERT_NUM_ERR (BNS_ERR + 5) /*(-9994)*/ +#define BNS_VERT_EDGE_OVFL (BNS_ERR + 6) /*(-9993)*/ +#define BNS_SET_ALTP_ERR (BNS_ERR + 7) /*(-9992)*/ +#define BNS_CPOINT_ERR (BNS_ERR + 8) /*(-9991)*/ +#define BNS_CANT_SET_BOND (BNS_ERR + 9) /*(-9990)*/ +#define BNS_CAP_FLOW_ERR (BNS_ERR + 10) /*(-9989)*/ +#define BNS_RADICAL_ERR (BNS_ERR + 11) /*(-9988)*/ +#define BNS_REINIT_ERR (BNS_ERR + 12) /*(-9987)*/ +#define BNS_ALTBOND_ERR (BNS_ERR + 13) /*(-9986)*/ + +#define BNS_TIMEOUT (BNS_ERR + 14) /*(-9985)*/ /* v. 1.05 */ + +#define BNS_MAX_ERR_VALUE (BNS_ERR + 19) /*(-9980)*/ + +#define IS_BNS_ERROR(X) (BNS_ERR <= (X) && (X) <= BNS_MAX_ERR_VALUE) + + +#define INCHI_INP_ERROR_ERR 40 +#define INCHI_INP_ERROR_RET (-1) + +#define INCHI_INP_FATAL_ERR 1 +#define INCHI_INP_FATAL_RET 0 + +#define INCHI_INP_EOF_ERR 11 +#define INCHI_INP_EOF_RET 0 + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + +extern int (*UserAction)(void); /* callback */ +extern int (*ConsoleQuit)(void); /* Console user issued CTRL+C etc. */ + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + +#define LOG_MASK_WARN 1 +#define LOG_MASK_ERR 2 +#define LOG_MASK_FATAL 4 + +#define LOG_MASK_ALL (LOG_MASK_WARN | LOG_MASK_ERR | LOG_MASK_FATAL) +#define LOG_MASK_NO_WARN (LOG_MASK_ERR | LOG_MASK_FATAL) + +#ifdef TARGET_LIB_FOR_WINCHI +#include + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + +extern void (*FWPRINT) (const char * format, va_list argptr ); +extern void (*DRAWDATA) ( struct DrawData * pDrawData); +extern int (*DRAWDATA_EXISTS) ( int nComponent, int nType, int bReconnected ); +extern struct DrawData * (*GET_DRAWDATA) ( int nComponent, int nType, int bReconnected ); + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + +#endif + +#define USER_ACTION_QUIT 1 + +#define STR_ERR_LEN 256 + +const char *ErrMsg( int nErrorCode ); + +int AddErrorMessage( char *pStrErr, const char *szMsg ); + +#define WarningMessage AddErrorMessage + + +#define TREAT_ERR_AND_FIN(err, new_err, err_fin, msg) \ + if ( !(err) && (new_err) ) { (err) = (new_err);} AddErrorMessage(pStrErr, (msg)); goto err_fin +#define TREAT_ERR(err, new_err, msg) \ + if ( !(err) && (new_err) ) { (err) = (new_err);} AddErrorMessage(pStrErr, (msg)) + + +#endif /* _ICCHIERR_H_ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichiisot.c b/INCHI-1-SRC/INCHI_BASE/src/ichiisot.c similarity index 80% rename from INCHI-1-SRC/INCHI_API/inchi_dll/ichiisot.c rename to INCHI-1-SRC/INCHI_BASE/src/ichiisot.c index c761ea8..c8cadbc 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichiisot.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichiisot.c @@ -1,132 +1,126 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - -#include "mode.h" - -#include "incomdef.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichicant.h" -#include "ichicomn.h" - -#ifdef NEVER -/**********************************************************************************/ -int unpack_iso_sort_key( AT_ISO_SORT_KEY iso_sort_key, S_CHAR *num_1H, S_CHAR *num_2H, S_CHAR *num_3H, S_CHAR *iso_atw_diff ) -{ - int is_negative; - AT_ISO_SORT_KEY HOnlyAtwPart; - static const AT_ISO_SORT_KEY MultAtwDiff = AT_ISO_SORT_KEY_MULT*AT_ISO_SORT_KEY_MULT*AT_ISO_SORT_KEY_MULT; - if ( !iso_sort_key ) { - *num_1H = *num_2H = *num_3H = *iso_atw_diff = 0; - return 0; - } else - if ( iso_sort_key < 0 ) { - is_negative = 1; - iso_sort_key = -iso_sort_key; - HOnlyAtwPart = MultAtwDiff - iso_sort_key % MultAtwDiff; - iso_sort_key += HOnlyAtwPart; - } else { - is_negative = 0; - HOnlyAtwPart = iso_sort_key % MultAtwDiff; - iso_sort_key -= HOnlyAtwPart; - } - - iso_sort_key /= MultAtwDiff; - - *num_1H = (S_CHAR)(HOnlyAtwPart % AT_ISO_SORT_KEY_MULT); - HOnlyAtwPart /= AT_ISO_SORT_KEY_MULT; - *num_2H = (S_CHAR)(HOnlyAtwPart % AT_ISO_SORT_KEY_MULT); - HOnlyAtwPart /= AT_ISO_SORT_KEY_MULT; - *num_3H = (S_CHAR)(HOnlyAtwPart % AT_ISO_SORT_KEY_MULT); - - *iso_atw_diff = (S_CHAR)(is_negative? -iso_sort_key : iso_sort_key); - - return 1; -} -#endif - -/**********************************************************************************/ -AT_ISO_SORT_KEY make_iso_sort_key( int iso_atw_diff, int num_1H, int num_2H, int num_3H) -{ - AT_ISO_SORT_KEY iso_sort_key = 0, mult=1; - - iso_sort_key += mult * num_1H; - mult *= AT_ISO_SORT_KEY_MULT; - iso_sort_key += mult * num_2H; - mult *= AT_ISO_SORT_KEY_MULT; - iso_sort_key += mult * num_3H; - mult *= AT_ISO_SORT_KEY_MULT; - iso_sort_key += mult * iso_atw_diff; - return iso_sort_key; -} -/**********************************************************************************/ -/* set sp_ATOM isotopic sort keys */ -int set_atom_iso_sort_keys( int num_at, sp_ATOM *at, T_GROUP_INFO* t_group_info, int *bHasIsotopicInTautomerGroups ) -{ - int i, num_isotopic = 0, bMergedTgroup; - AT_ISO_SORT_KEY iso_sort_key; - T_GROUP *t_group = - (t_group_info && - t_group_info->t_group && - t_group_info->num_t_groups > 0)? t_group_info->t_group : NULL; - - if ( bHasIsotopicInTautomerGroups ) - *bHasIsotopicInTautomerGroups = 0; - for ( i = 0; i < num_at; i ++ ) { - bMergedTgroup = (t_group_info && t_group_info->nIsotopicEndpointAtomNumber && (at[i].cFlags & AT_FLAG_ISO_H_POINT)); - if ( (!at[i].endpoint || !t_group) && !bMergedTgroup ) { - iso_sort_key = make_iso_sort_key(at[i].iso_atw_diff, at[i].num_iso_H[0], at[i].num_iso_H[1], at[i].num_iso_H[2]); - } else { - /* H isotopes go to the tautomer part of the CT (name) */ - /* if (at[i].endpoint && t_group) ... */ - iso_sort_key = make_iso_sort_key(at[i].iso_atw_diff, 0, 0, 0); - if ( bHasIsotopicInTautomerGroups ) - *bHasIsotopicInTautomerGroups += (at[i].num_iso_H[0] || at[i].num_iso_H[1] || at[i].num_iso_H[2] || bMergedTgroup); - } - at[i].iso_sort_key = iso_sort_key; - num_isotopic += (iso_sort_key != 0); - } - return num_isotopic; -} - +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include "mode.h" +#include "ichicomn.h" + +/**********************************************************************************/ +AT_ISO_SORT_KEY make_iso_sort_key( int iso_atw_diff, int num_1H, int num_2H, int num_3H) +{ + AT_ISO_SORT_KEY iso_sort_key = 0, mult=1; + + iso_sort_key += mult * num_1H; + mult *= AT_ISO_SORT_KEY_MULT; + iso_sort_key += mult * num_2H; + mult *= AT_ISO_SORT_KEY_MULT; + iso_sort_key += mult * num_3H; + mult *= AT_ISO_SORT_KEY_MULT; + iso_sort_key += mult * iso_atw_diff; + return iso_sort_key; +} + + + +/**********************************************************************************/ +/* set sp_ATOM isotopic sort keys */ +int set_atom_iso_sort_keys( int num_at, sp_ATOM *at, T_GROUP_INFO* t_group_info, int *bHasIsotopicInTautomerGroups ) +{ + int i, num_isotopic = 0, bMergedTgroup; + AT_ISO_SORT_KEY iso_sort_key; + T_GROUP *t_group = + (t_group_info && + t_group_info->t_group && + t_group_info->num_t_groups > 0)? t_group_info->t_group : NULL; + + if ( bHasIsotopicInTautomerGroups ) + *bHasIsotopicInTautomerGroups = 0; + for ( i = 0; i < num_at; i ++ ) { + bMergedTgroup = (t_group_info && t_group_info->nIsotopicEndpointAtomNumber && (at[i].cFlags & AT_FLAG_ISO_H_POINT)); + if ( (!at[i].endpoint || !t_group) && !bMergedTgroup ) { + iso_sort_key = make_iso_sort_key(at[i].iso_atw_diff, at[i].num_iso_H[0], at[i].num_iso_H[1], at[i].num_iso_H[2]); + } else { + /* H isotopes go to the tautomer part of the CT (name) */ + /* if (at[i].endpoint && t_group) ... */ + iso_sort_key = make_iso_sort_key(at[i].iso_atw_diff, 0, 0, 0); + if ( bHasIsotopicInTautomerGroups ) + *bHasIsotopicInTautomerGroups += (at[i].num_iso_H[0] || at[i].num_iso_H[1] || at[i].num_iso_H[2] || bMergedTgroup); + } + at[i].iso_sort_key = iso_sort_key; + num_isotopic += (iso_sort_key != 0); + } + return num_isotopic; +} + + + +#ifdef NEVER +/**********************************************************************************/ +int unpack_iso_sort_key( AT_ISO_SORT_KEY iso_sort_key, S_CHAR *num_1H, S_CHAR *num_2H, S_CHAR *num_3H, S_CHAR *iso_atw_diff ) +{ + int is_negative; + AT_ISO_SORT_KEY HOnlyAtwPart; + static const AT_ISO_SORT_KEY MultAtwDiff = AT_ISO_SORT_KEY_MULT*AT_ISO_SORT_KEY_MULT*AT_ISO_SORT_KEY_MULT; + if ( !iso_sort_key ) { + *num_1H = *num_2H = *num_3H = *iso_atw_diff = 0; + return 0; + } else + if ( iso_sort_key < 0 ) { + is_negative = 1; + iso_sort_key = -iso_sort_key; + HOnlyAtwPart = MultAtwDiff - iso_sort_key % MultAtwDiff; + iso_sort_key += HOnlyAtwPart; + } else { + is_negative = 0; + HOnlyAtwPart = iso_sort_key % MultAtwDiff; + iso_sort_key -= HOnlyAtwPart; + } + + iso_sort_key /= MultAtwDiff; + + *num_1H = (S_CHAR)(HOnlyAtwPart % AT_ISO_SORT_KEY_MULT); + HOnlyAtwPart /= AT_ISO_SORT_KEY_MULT; + *num_2H = (S_CHAR)(HOnlyAtwPart % AT_ISO_SORT_KEY_MULT); + HOnlyAtwPart /= AT_ISO_SORT_KEY_MULT; + *num_3H = (S_CHAR)(HOnlyAtwPart % AT_ISO_SORT_KEY_MULT); + + *iso_atw_diff = (S_CHAR)(is_negative? -iso_sort_key : iso_sort_key); + + return 1; +} +#endif diff --git a/INCHI-1-SRC/INCHI_BASE/src/ichimain.h b/INCHI-1-SRC/INCHI_BASE/src/ichimain.h new file mode 100644 index 0000000..22b6e57 --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/ichimain.h @@ -0,0 +1,426 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _ICHIMAIN_H_ +#define _ICHIMAIN_H_ + +#include "strutil.h" +#include "ichicomn.h" + +#define ESC_KEY 27 +#define INCHI_SEGM_BUFLEN 511999 + +/* for DisplayTheWholeStructure() */ +#define COMP_ORIG_0_MAIN 0x0001 +#define COMP_ORIG_0_RECN 0x0002 +#define COMP_PREP_0_MAIN 0x0004 +#define COMP_PREP_0_RECN 0x0008 +#define COMP_ORIG_1_MAIN 0x0010 +#define COMP_ORIG_1_RECN 0x0020 + + +typedef struct tagStructData +{ + unsigned long ulStructTime; + int nErrorCode; + int nErrorType; + int nStructReadError; + char pStrErrStruct[STR_ERR_LEN]; + long fPtrStart; /* or number of processed structures */ + long fPtrEnd; /* or number of errors */ + int bUserQuit; + int bUserQuitComponent; + int bUserQuitComponentDisplay; + int bChiralFlag; + + /* information related to normal or disconnected layers */ + int num_taut[INCHI_NUM]; + int num_non_taut[INCHI_NUM]; + INCHI_MODE bTautFlags[INCHI_NUM]; /* reconnected does not have TG_FLAG_DISCONNECT_COORD_DONE flag */ + INCHI_MODE bTautFlagsDone[INCHI_NUM]; /* reconnected does not have TG_FLAG_DISCONNECT_COORD_DONE flag */ + int num_components[INCHI_NUM]; /* number of allocated INChI, INChI_Aux data structures */ + /* debugging info */ + +#if ( bRELEASE_VERSION == 0 ) + int bExtract; +#endif +} STRUCT_DATA; + + +#define PRINT_INCHI_MAX_TAG_LEN 64 + +/* Convenience storage for InChI serialization control data */ +typedef struct OutputINCHI_CtlData +{ + int ATOM_MODE; + int TAUT_MODE; + + int *pSortPrintINChIFlags; + + int bOverflow; + int bAlways; + int bOutputType; + int bOutType; + int bPlainTextTags; + int bOmitRepetitions; + int bUseMulipliers; + int bNonTautNonIsoIdentifierNotEmpty; + int bNonTautIsoIdentifierNotEmpty; + int bSecondNonTautPass; + int bTautomericOutputAllowed; + int bTautomeric; + int bNonTautomeric; + int bNonTautIsIdenticalToTaut; + int bFhTag; + int bRelRac; + int bAbcNumbers; + int bIsotopic; + + int iCurTautMode; + + int num_components; + int nNumRemovedProtons; + int nTag; + int bTag1; + int bTag2; + int bTag3; + int tot_len; + int tot_len2; + + int nCurINChISegment; + int nSegmAction; + + int num_comp[TAUT_NUM]; + int num_iso_H[NUM_H_ISOTOPES]; + int bAtomEqu[TAUT_NUM]; + int bTautEqu[TAUT_NUM]; + int bInvStereo[TAUT_NUM]; + int bInvStereoOrigNumb[TAUT_NUM]; + int bRacemicStereo[TAUT_NUM]; + int bRelativeStereo[TAUT_NUM]; + int bIsotopicOrigNumb[TAUT_NUM]; + int bIsotopicAtomEqu[TAUT_NUM]; + int bIsotopicTautEqu[TAUT_NUM]; + int bInvIsotopicStereo[TAUT_NUM]; + int bInvIsotopicStereoOrigNumb[TAUT_NUM]; + int bIsotopicRacemicStereo[TAUT_NUM]; + int bIsotopicRelativeStereo[TAUT_NUM]; + int bIgn_UU_Sp3[TAUT_NUM]; + int bIgn_UU_Sp2[TAUT_NUM]; + int bIgn_UU_Sp3_Iso[TAUT_NUM]; + int bIgn_UU_Sp2_Iso[TAUT_NUM]; + int bChargesRadVal[TAUT_NUM]; + int bOrigCoord[TAUT_NUM]; + + char sDifSegs[DIFL_LENGTH][DIFS_LENGTH]; + char szTag1[PRINT_INCHI_MAX_TAG_LEN]; + char szTag2[PRINT_INCHI_MAX_TAG_LEN]; + char szTag3[PRINT_INCHI_MAX_TAG_LEN]; + + INCHI_SORT **pINChISortTautAndNonTaut; + INCHI_SORT *pINChISort; + INCHI_SORT *pINChISort2; +} +OutputINCHI_CtlData; + + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + + +int ProcessSingleInputFile( int argc, char *argv[ ] ); +int ProcessMultipleInputFiles( int argc, char *argv[ ] ); +int ReadCommandLineParms( int argc, const char *argv[], + INPUT_PARMS *ip, + char *szSdfDataValue, + unsigned long *ulDisplTime, + int bReleaseVersion, + INCHI_IOSTREAM *log_file); +void HelpCommandLineParms( INCHI_IOSTREAM *f); +int OpenFiles( FILE **inp_file, + FILE **out_file, + FILE **log_file, + FILE **prb_file, + INPUT_PARMS *ip ); +int PrintInputParms( INCHI_IOSTREAM *log_file, + INPUT_PARMS *ip); +int SortAndPrintINChI( struct tagCANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + INCHI_IOSTREAM *log_file, + INPUT_PARMS *ip, + ORIG_ATOM_DATA *orig_inp_data, + ORIG_ATOM_DATA *prep_inp_data, + COMP_ATOM_DATA composite_norm_data[INCHI_NUM][TAUT_NUM+1], + ORIG_STRUCT *pOrigStruct, + int num_components[INCHI_NUM], + int num_non_taut[INCHI_NUM], + int num_taut[INCHI_NUM], + INCHI_MODE bTautFlags[INCHI_NUM], + INCHI_MODE bTautFlagsDone[INCHI_NUM], + NORM_CANON_FLAGS *pncFlags, + long num_inp, + PINChI2 *pINChI[INCHI_NUM], + PINChI_Aux2 *pINChI_Aux[INCHI_NUM], + int *pSortPrintINChIFlags, + unsigned char save_opt_bits); +void FreeAllINChIArrays( PINChI2 *pINChI[INCHI_NUM], + PINChI_Aux2 *pINChI_Aux[INCHI_NUM], + int num_components[2]); +void FreeINChIArrays( PINChI2 *pINChI, + PINChI_Aux2 *pINChI_Aux, + int num_components ); +void SplitTime( unsigned long ulTotalTime, + int *hours, + int *minutes, + int *seconds, + int *mseconds ); +int ReadTheStructure( struct tagINCHI_CLOCK *ic, + STRUCT_DATA *sd, + INPUT_PARMS *ip, + INCHI_IOSTREAM *inp_file, + ORIG_ATOM_DATA *orig_inp_data, + int inp_index, + int *out_index ); +int TreatErrorsInReadTheStructure( STRUCT_DATA *sd, + INPUT_PARMS *ip, + int nLogMask, + INCHI_IOSTREAM *inp_file, + INCHI_IOSTREAM *log_file, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM *prb_file, + ORIG_ATOM_DATA *orig_inp_data, + long *num_inp ); +int GetOneComponent( struct tagINCHI_CLOCK *ic, + STRUCT_DATA *sd, + INPUT_PARMS *ip, + INCHI_IOSTREAM *log_file, + INCHI_IOSTREAM *out_file, + INP_ATOM_DATA *inp_cur_data, + ORIG_ATOM_DATA *orig_inp_data, + int i, long num_inp ); +int CreateOneComponentINChI( struct tagCANON_GLOBALS *pCG, + struct tagINCHI_CLOCK *ic, + STRUCT_DATA *sd, + INPUT_PARMS *ip, + INP_ATOM_DATA *inp_cur_data, + ORIG_ATOM_DATA *orig_inp_data, + PINChI2 *pINChI, + PINChI_Aux2 *pINChI_Aux, + int iINChI, + int i, long num_inp, + INP_ATOM_DATA **inp_norm_data, + NORM_CANON_FLAGS *pncFlags, + INCHI_IOSTREAM *log_file); +int TreatErrorsInCreateOneComponentINChI( STRUCT_DATA *sd, + INPUT_PARMS *ip, + ORIG_ATOM_DATA *orig_inp_data, + int i, long num_inp, + INCHI_IOSTREAM *inp_file, + INCHI_IOSTREAM *log_file, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM *prb_file ); +int TreatCreateINChIWarning( STRUCT_DATA *sd, + INPUT_PARMS *ip, + ORIG_ATOM_DATA *orig_inp_data, + long num_inp, + INCHI_IOSTREAM *inp_file, + INCHI_IOSTREAM *log_file, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM *prb_file ); +int GetProcessingWarningsOneComponentInChI( INChI *cur_INChI[], + INP_ATOM_DATA **inp_norm_data, + STRUCT_DATA *sd); +#ifndef COMPILE_ANSI_ONLY +void eat_keyboard_input( void ); +int user_quit( struct tagINCHI_CLOCK *ic, const char *msg, unsigned long ulMaxTime ); +#endif +int GetOneStructure( struct tagINCHI_CLOCK *ic, + STRUCT_DATA *sd, + INPUT_PARMS *ip, + char *szTitle, + INCHI_IOSTREAM *inp_file, + INCHI_IOSTREAM *log_file, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM *prb_file, + ORIG_ATOM_DATA *orig_inp_data, + long *num_inp, + STRUCT_FPTRS *struct_fptrs ); +int ProcessOneStructure( struct tagINCHI_CLOCK *ic, + struct tagCANON_GLOBALS *pCG, + STRUCT_DATA *sd, + INPUT_PARMS *ip, + char *szTitle, + PINChI2 *pINChI2[INCHI_NUM], + PINChI_Aux2 *pINChI_Aux2[INCHI_NUM], + INCHI_IOSTREAM *inp_file, + INCHI_IOSTREAM *log_file, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM *prb_file, + ORIG_ATOM_DATA *orig_inp_data, + ORIG_ATOM_DATA *prep_inp_data, + long num_inp, + INCHI_IOSTREAM_STRING *strbuf, + unsigned char save_opt_bits); +int ProcessOneStructureEx( struct tagINCHI_CLOCK *ic, + struct tagCANON_GLOBALS *pCG, + STRUCT_DATA *sd, + INPUT_PARMS *ip, + char *szTitle, + PINChI2 *pINChI2[INCHI_NUM], + PINChI_Aux2 *pINChI_Aux2[INCHI_NUM], + INCHI_IOSTREAM *inp_file, + INCHI_IOSTREAM *log_file, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM *prb_file, + ORIG_ATOM_DATA *orig_inp_data, + ORIG_ATOM_DATA *prep_inp_data, + long num_inp, + INCHI_IOSTREAM_STRING *strbuf, + unsigned char save_opt_bits); + +int OrigAtData_CreateCopy( ORIG_ATOM_DATA *new_orig_atom, ORIG_ATOM_DATA *orig_atom ); +void OrigAtData_DebugTrace( ORIG_ATOM_DATA *at_data ); +int OrigAtData_RemoveHalfBond( int this_atom, int other_atom, inp_ATOM *at, int *bond_type, int *bond_stereo ); +int OrigAtData_DestroyBond( int this_atom, int other_atom, inp_ATOM *at, int *num_inp_bonds); +int OrigAtData_RemoveBond( int this_atom, int other_atom, inp_ATOM *at, + int *bond_type, int *bond_stereo, int *num_inp_bonds); +int OrigAtData_AddBond( int this_atom, int other_atom, inp_ATOM *at, + int bond_type, int bond_stereo, int *num_bonds ); +int OrigAtData_AddSingleStereolessBond( int this_atom, int other_atom, + inp_ATOM *at, int *num_inp_bonds ); +int OrigAtData_IncreaseBondOrder( int this_atom, int other_atom, inp_ATOM *at ); +int OrigAtData_DecreaseBondOrder( int this_atom, int other_atom, inp_ATOM *at ); +void OrigAtData_CheckAndMakePolymerPhaseShifts( OrigAtDataPolymer *p, + inp_ATOM *at, + int nat, + int *num_inp_bonds); +int OrigAtData_FindRingSystems( OrigAtDataPolymer *pd, + inp_ATOM *at, + int nat, + int *num_inp_bonds, + int *num_ring_sys, + int *size_ring_sys, + int start ); +void OrigAtData_FillAtProps( OrigAtDataPolymer *pd, + inp_ATOM *at, + int nat, + int *num_inp_bonds, + OrigAtDataPolymerAtomProps *aprops ); + +int get_canonical_atom_numbers_and_component_numbers( CANON_GLOBALS *pCG, + INCHI_IOSTREAM_STRING *strbuf, + OutputINCHI_CtlData *io, + int nat, int *cano_nums, + int *compnt_nums ); + + +int CreateOneStructureINChI( struct tagCANON_GLOBALS *pCG, + struct tagINCHI_CLOCK *ic, + STRUCT_DATA *sd, + INPUT_PARMS *ip, + char *szTitle, + PINChI2 *pINChI2[INCHI_NUM], + PINChI_Aux2 *pINChI_Aux2[INCHI_NUM], + int iINChI, + INCHI_IOSTREAM *inp_file, + INCHI_IOSTREAM *log_file, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM *prb_file, + ORIG_ATOM_DATA *orig_inp_data, + ORIG_ATOM_DATA *prep_inp_data, + COMP_ATOM_DATA composite_norm_data2[][TAUT_NUM+1], + long num_inp, + INCHI_IOSTREAM_STRING *strbuf, + NORM_CANON_FLAGS *pncFlags ); +int bIsStructChiral( PINChI2 *pINChI2[INCHI_NUM], + int num_components[]); +int PreprocessOneStructure( struct tagINCHI_CLOCK *ic, + STRUCT_DATA *sd, + INPUT_PARMS *ip, + ORIG_ATOM_DATA *orig_inp_data, + ORIG_ATOM_DATA *prep_inp_data ); +int DuplicateOrigAtom( ORIG_ATOM_DATA *new_orig_atom, + ORIG_ATOM_DATA *orig_atom ); +int OrigStruct_FillOut( struct tagCANON_GLOBALS *pCG, + ORIG_ATOM_DATA *orig_inp_data, + ORIG_STRUCT *pOrigStruct, + STRUCT_DATA *sd); +void FreeOrigStruct( ORIG_STRUCT *pOrigStruct); +int ReadWriteInChI( INCHI_IOSTREAM *pInp, + INCHI_IOSTREAM *pOut, + INCHI_IOSTREAM *pLog, + INPUT_PARMS *ip_inp, + STRUCT_DATA *sd_inp, + /* the following are InChI library-specific parameters */ + inp_ATOM **at, + int *num_at, + int *num_bonds, + OrigAtDataPolymer **polymer, + OrigAtDataV3000 **v3000, + /* end of InChI library-specific parameters */ + char *szMsg, + int nMsgLen, + unsigned long WarningFlags[2][2], + struct tagINCHI_CLOCK *ic, + struct tagCANON_GLOBALS *pCG); +int CompareHillFormulasNoH( const char *f1, + const char *f2, + int *num_H1, + int *num_H2); +int CreateCompositeNormAtom( COMP_ATOM_DATA *composite_norm_data, + INP_ATOM_DATA2 *all_inp_norm_data, + int num_components ); +void set_line_separators( int bINChIOutputOptions, char **pLF, char **pTAB ); +void save_command_line( int argc, char *argv[ ], INCHI_IOSTREAM *plog); +void emit_error_inchi_text( INPUT_PARMS *ip, + long num_inp, + char *pLF, + char *pTAB, + INCHI_IOSTREAM *pout); +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + +#endif /* _ICHIMAIN_H_ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichimak2.c b/INCHI-1-SRC/INCHI_BASE/src/ichimak2.c similarity index 61% rename from INCHI-1-SRC/INCHI_API/inchi_dll/ichimak2.c rename to INCHI-1-SRC/INCHI_BASE/src/ichimak2.c index 05dc618..dd5506a 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichimak2.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichimak2.c @@ -1,1225 +1,1711 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include - - -#include "mode.h" - -#include "inpdef.h" -#include "ichi.h" -#include "strutil.h" -#include "util.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "ichicant.h" -#include "ichicano.h" -#include "ichicomn.h" - -#include "ichicomp.h" -#include "ichimain.h" -#include "ichimake.h" - - -int GetHillFormulaCounts( U_CHAR *nAtom, S_CHAR *nNum_H, int num_atoms, - AT_NUMB *nTautomer, int lenTautomer, - int *pnum_C, int *pnum_H, int *pnLen, int *pnNumNonHAtoms ); -int MakeHillFormula( U_CHAR *nAtom, int num_atoms, - char *szLinearCT, int nLen_szLinearCT, int num_C, int num_H, int *bOverflow ); - -#if ( FIX_DALKE_BUGS == 1 ) -#else -char *AllocateAndFillHillFormula( INChI *pINChI ); -#endif - -int AddElementAndCount( const char *szElement, int mult, char *szLinearCT, int nLenLinearCT, int *bOverflow ); - -int Copy2StereoBondOrAllene( INChI_Stereo *Stereo, int *nNumberOfStereoCenters, int *nNumberOfStereoBonds, - AT_STEREO_DBLE *LinearCTStereoDble, - AT_NUMB *pCanonOrd, AT_RANK *pCanonRank, sp_ATOM *at, int bIsotopic ); - -int CopyLinearCTStereoToINChIStereo( INChI_Stereo *Stereo, - AT_STEREO_CARB *LinearCTStereoCarb, int nLenLinearCTStereoCarb, - AT_STEREO_DBLE *LinearCTStereoDble, int nLenLinearCTStereoDble - , AT_NUMB *pCanonOrd, AT_RANK *pCanonRank, sp_ATOM *at, int bIsotopic - , AT_STEREO_CARB *LinearCTStereoCarbInv - , AT_STEREO_DBLE *LinearCTStereoDbleInv - , AT_NUMB *pCanonOrdInv, AT_RANK *pCanonRankInv ); -int GetHillFormulaIndexLength( int count ); - -int MarkAmbiguousStereo( sp_ATOM *at, inp_ATOM *norm_at, int bIsotopic, AT_NUMB *pCanonOrd, - AT_STEREO_CARB *LinearCTStereoCarb, int nLenLinearCTStereoCarb, - AT_STEREO_DBLE *LinearCTStereoDble, int nLenLinearCTStereoDble ); - -INCHI_MODE UnmarkAllUndefinedUnknownStereo( INChI_Stereo *Stereo, INCHI_MODE nUserMode ); - -int CleanCoord( MOL_COORD szCoord, int delim ); - -/**********************************************************************************************/ -int MakeHillFormulaString( char *szHillFormula, char *szLinearCT, int nLen_szLinearCT, int *bOverflow) -{ - int nLen; - if ( szHillFormula && !*bOverflow ) { - if ( nLen_szLinearCT > ( nLen = strlen(szHillFormula) ) ) { - memcpy( szLinearCT, szHillFormula, nLen+1 ); - return nLen; - } - *bOverflow |= 1; - return nLen_szLinearCT+1; - } - return 0; -} -/********************************************************************************************** - * MS Windows dependent: sprintf() is supposed to return the length of the output string - * Carbon atoms are always first - * Bridging hydrogen atoms are always last - **********************************************************************************************/ -int GetHillFormulaIndexLength( int count ) -{ - char szCount[16]; - if ( count > 1 ) { - return sprintf( szCount, "%d", count ); - } - return 0; -} -/**********************************************************************************************/ -int GetHillFormulaCounts( U_CHAR *nAtom, S_CHAR *nNum_H, int num_atoms, - AT_NUMB *nTautomer, int lenTautomer, - int *pnum_C, int *pnum_H, int *pnLen, int *pnNumNonHAtoms ) -{ - char szElement[4]; - U_CHAR nPrevAtom = (U_CHAR)-2; - int bCarbon, bHydrogen, nElemLen, nFormLen, nNumNonHAtoms; - int mult, i, num_H, num_C; - - num_H = 0; - num_C = 0; - bCarbon = 0; - bHydrogen = 0; - nElemLen = 0; - nFormLen = 0; - mult = 0; - nNumNonHAtoms = num_atoms; - for ( i = 0; i < num_atoms; i ++ ) { - if ( nPrevAtom != nAtom[i] ) { - if ( mult ) { - if ( bHydrogen ) { - num_H += mult; - }else - if ( bCarbon ) { - num_C += mult; - } else { - nFormLen += nElemLen; - nFormLen += GetHillFormulaIndexLength( mult ); - } - } - - if ( GetElementFormulaFromAtNum((int)nAtom[i], szElement ) ) { - return -1; /* wrong element */ - } - mult = 1; - - nElemLen = strlen(szElement); - nPrevAtom = nAtom[i]; - bCarbon = !strcmp( szElement, "C" ); - bHydrogen = !strcmp( szElement, "H" ); - if ( bHydrogen ) { - nNumNonHAtoms = i; - } - } else { - mult ++; - } - - num_H += nNum_H[i]; - } - /* NumGroups; ((NumAt+1, NumH, At1..AtNumAt),...) */ - if ( nTautomer && lenTautomer > 0 ) { - int num_groups = nTautomer[0]; - for ( i = 1; i < lenTautomer && num_groups > 0; i += nTautomer[i]+1, num_groups -- ) { - num_H += nTautomer[i+1]; - } - } - - if ( mult ) { - if ( bHydrogen ) { - num_H += mult; - } else - if ( bCarbon ) { - num_C += mult; - } else { - nFormLen += nElemLen; - nFormLen += GetHillFormulaIndexLength( mult ); - } - } - - if ( num_C ) { - nFormLen += strlen( "C" ); - nFormLen += GetHillFormulaIndexLength( num_C ); - } - - if ( num_H ) { - nFormLen += strlen( "H" ); - nFormLen += GetHillFormulaIndexLength( num_H ); - } - *pnum_C = num_C; - *pnum_H = num_H; - *pnLen = nFormLen; - *pnNumNonHAtoms = nNumNonHAtoms; - - return 0; -} -/**********************************************************************************************/ -int AddElementAndCount( const char *szElement, int mult, char *szLinearCT, int nLenLinearCT, int *bOverflow ) -{ - char szMult[16]; - int len1, len2; - if ( mult > 0 && !*bOverflow && 0 < (len1 = strlen( szElement )) ) { - if ( mult > 1 ) { - len2 = sprintf( szMult, "%d", mult ); - } else { - len2 = 0; - szMult[0] = '\0'; - } - if ( len1 + len2 < nLenLinearCT ) { - memcpy( szLinearCT, szElement, len1 ); - memcpy( szLinearCT+len1, szMult, len2+1 ); /* adding zero termination */ - return len1+len2; - } else { - (*bOverflow) ++; - } - } - return 0; -} -/**********************************************************************************************/ -/* if num_C > 0 then nAtom does not contain C or H */ -/* otherwise all elements are in alphabetic order */ -int MakeHillFormula( U_CHAR *nAtom, int num_atoms, - char *szLinearCT, int nLen_szLinearCT, int num_C, int num_H, int *bOverflow ) -{ - char szElement[4]; - int mult, compare2H; - int i, nLen, bOvfl; - U_CHAR nPrevAtom; - - nLen = 0; - mult = 0; - bOvfl = 0; - nPrevAtom = (U_CHAR)-2; /* non-existent number */ - - - if ( num_C ) { - nLen += AddElementAndCount( "C", num_C, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl ); - if ( num_H ) { - nLen += AddElementAndCount( "H", num_H, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl ); - num_H = 0; - } - } - - for ( i = 0; i < num_atoms; i ++ ) { - - if ( nPrevAtom != nAtom[i] ) { - if ( mult ) { - nLen += AddElementAndCount( szElement, mult, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl ); - } - mult = 1; - if ( GetElementFormulaFromAtNum((int)nAtom[i], szElement ) ) { - return -1; /* wrong element */ - } - nPrevAtom = nAtom[i]; - if ( !strcmp( "C", szElement ) ) { - return -1; - } - compare2H = strcmp( "H", szElement ); - if ( !compare2H ) { - return -1; - } - if ( compare2H < 0 && num_H ) { - /* H-atom should be located in front of szElement */ - nLen += AddElementAndCount( "H", num_H, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl ); - num_H = 0; - } - } else { - mult ++; - } - } - if ( mult ) { - /* the last element if any */ - nLen += AddElementAndCount( szElement, mult, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl ); - } - if ( num_H ) { - /* if H has not been output... */ - nLen += AddElementAndCount( "H", num_H, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl ); - } - *bOverflow |= (0 != bOvfl); - return bOvfl? nLen_szLinearCT+1: nLen; -} -/**********************************************************************************************/ -char *AllocateAndFillHillFormula( INChI *pINChI ) -{ - int num_C, num_H, nLen, nNumNonHAtoms, ret, bOverflow; - char *pHillFormula = NULL; - bOverflow = 0; - if ( !GetHillFormulaCounts( pINChI->nAtom, pINChI->nNum_H, pINChI->nNumberOfAtoms, - pINChI->nTautomer, pINChI->lenTautomer, - &num_C, &num_H, &nLen, &nNumNonHAtoms ) ) { - if ( pHillFormula = (char*) inchi_malloc( nLen+1 ) ) { - ret = MakeHillFormula( pINChI->nAtom+num_C, nNumNonHAtoms-num_C, - pHillFormula, nLen+1, num_C, num_H, &bOverflow ); - if ( ret != nLen || bOverflow ) { - inchi_free( pHillFormula ); - pHillFormula = NULL; - } - } - } - return pHillFormula; -} - -/************************************************************************************/ -/* return value: 0 => copied to stereo bonds; 1=> Allene copied to stereocenters */ -/* on input nNumberOfStereoBonds==NULL means second call, use Stereo->...Inv */ -/************************************************************************************/ -int Copy2StereoBondOrAllene( INChI_Stereo *Stereo, int *nNumberOfStereoCenters, int *nNumberOfStereoBonds, - AT_STEREO_DBLE *LinearCTStereoDble, - AT_NUMB *pCanonOrd, AT_RANK *pCanonRank, sp_ATOM *at, int bIsotopic ) -{ - int cumulene_len, j, next_j /* ordering number of the central allene atom */, next_neigh; - AT_RANK at_num; - int parity; - if ( pCanonOrd && pCanonRank ) { - j = pCanonOrd[(int)LinearCTStereoDble->at_num1-1]; - /* if allene then find the central atom, at[next_j] */ - if ( bIsotopic ) { - cumulene_len = BOND_CHAIN_LEN(at[j].stereo_bond_parity2[0]); - if ( cumulene_len % 2 && (1 >= MAX_NUM_STEREO_BONDS || !at[j].stereo_bond_neighbor2[1]) ) { - next_j = at[j].neighbor[(int)at[j].stereo_bond_ord2[0]]; - for ( cumulene_len = (cumulene_len-1)/2; cumulene_len && 2==at[next_j].valence; cumulene_len -- ) { - next_neigh = (j == at[next_j].neighbor[0]); - j = next_j; - next_j = at[next_j].neighbor[next_neigh]; - } - /* next_j is the central atom */ - } else { - cumulene_len = -1; /* not an allene */ - } - - } else { - cumulene_len = BOND_CHAIN_LEN(at[j].stereo_bond_parity[0]); - if ( cumulene_len % 2 && (1 >= MAX_NUM_STEREO_BONDS || !at[j].stereo_bond_neighbor[1]) ) { - next_j = at[j].neighbor[(int)at[j].stereo_bond_ord[0]]; - for ( cumulene_len = (cumulene_len-1)/2; cumulene_len && 2==at[next_j].valence; cumulene_len -- ) { - next_neigh = (j == at[next_j].neighbor[0]); - j = next_j; - next_j = at[next_j].neighbor[next_neigh]; - } - } else { - cumulene_len = -1; /* not an allene */ - } - } - if ( !cumulene_len ) { - /* allene has been found; insert new stereocenter and parity */ - AT_NUMB *nNumber; - S_CHAR *t_parity; - nNumber = nNumberOfStereoBonds? Stereo->nNumber : Stereo->nNumberInv; - t_parity = nNumberOfStereoBonds? Stereo->t_parity : Stereo->t_parityInv; - at_num = pCanonRank[next_j]; - parity = LinearCTStereoDble->parity; - /* free room for the new stereocenter */ - for ( j = 0; j < *nNumberOfStereoCenters && Stereo->nNumber[j] < at_num; j ++ ) - ; - if ( j < *nNumberOfStereoCenters ) { - memmove( nNumber + j + 1, nNumber + j, (*nNumberOfStereoCenters-j)*sizeof(nNumber[0]) ); - memmove( t_parity + j + 1, t_parity + j, (*nNumberOfStereoCenters-j)*sizeof(t_parity[0]) ); - } - /* fill the new stereo center info */ - - nNumber[j] = at_num; - t_parity[j] = parity; - (*nNumberOfStereoCenters) ++; - return 1; - } - } - /* save the stereo bond info */ - if ( nNumberOfStereoBonds ) { - j = *nNumberOfStereoBonds; - Stereo->b_parity[j] = LinearCTStereoDble->parity; - Stereo->nBondAtom1[j] = LinearCTStereoDble->at_num1; - Stereo->nBondAtom2[j] = LinearCTStereoDble->at_num2; - (*nNumberOfStereoBonds) ++; - } - return 0; -} -/***************************************************************************/ -int CopyLinearCTStereoToINChIStereo( INChI_Stereo *Stereo, - AT_STEREO_CARB *LinearCTStereoCarb, int nLenLinearCTStereoCarb, - AT_STEREO_DBLE *LinearCTStereoDble, int nLenLinearCTStereoDble - , AT_NUMB *pCanonOrd, AT_RANK *pCanonRank, sp_ATOM *at, int bIsotopic - , AT_STEREO_CARB *LinearCTStereoCarbInv - , AT_STEREO_DBLE *LinearCTStereoDbleInv - , AT_NUMB *pCanonOrdInv, AT_RANK *pCanonRankInv ) -{ - int n, i, nErrorCode = 0, len; - int bAllene; - int diff; - int lenInv, bAlleneInv; - /* stereo centers */ - n = Stereo->nNumberOfStereoCenters = nLenLinearCTStereoCarb; - for ( i = 0; i < n; i ++ ) { - Stereo->nNumber[i] = LinearCTStereoCarb[i].at_num; - Stereo->t_parity[i] = LinearCTStereoCarb[i].parity; - Stereo->nNumberInv[i] = LinearCTStereoCarbInv[i].at_num; - Stereo->t_parityInv[i] = LinearCTStereoCarbInv[i].parity; - } - /* stereo bonds */ - n = nLenLinearCTStereoDble; - lenInv = Stereo->nNumberOfStereoCenters; - for ( i = len = 0; i < n; i ++ ) { - bAllene = - Copy2StereoBondOrAllene( Stereo, &Stereo->nNumberOfStereoCenters, - &len, LinearCTStereoDble+i, pCanonOrd, pCanonRank, at, bIsotopic ); - bAlleneInv = - Copy2StereoBondOrAllene( Stereo, &lenInv, - NULL, LinearCTStereoDbleInv+i, pCanonOrdInv, pCanonRankInv, at, bIsotopic ); - /* make sure double bond stereo is identical in original and inverted geometry */ - /* Note: all allenes are AFTER double bonds in LinearCTStereoDble... */ - if ( bAllene != bAlleneInv || !bAllene && - CompareLinCtStereoDble ( LinearCTStereoDble+i, 1, - LinearCTStereoDbleInv+i, 1 ) ) { - nErrorCode = -4; /* double bond stereo Inv is NOT identical to Abs */ - goto exit_function; - } - } - Stereo->nNumberOfStereoBonds = len; - - if ( lenInv != Stereo->nNumberOfStereoCenters ) { - nErrorCode = -5; /* different number of stereo centers in Abs and Inv */ - goto exit_function; - } - /* compare inverted stereocenters to absolute */ - n = Stereo->nNumberOfStereoCenters; - diff = 0; - for ( i = 0, diff = 0; i < n; i ++ ) { - if ( Stereo->nNumberInv[i] != Stereo->nNumber[i] ) { - diff = (Stereo->nNumberInv[i] > Stereo->nNumber[i])? 2 : -2; - break; /* Abs != Inv */ - } - if ( Stereo->t_parityInv[i] != Stereo->t_parity[i] ) { - diff = (Stereo->t_parityInv[i] > Stereo->t_parity[i])? 1 : -1; - break; /* Abs != Inv */ - } - } - Stereo->nCompInv2Abs = (diff > 0)? 1 : (diff < 0)? -1 : 0; - if ( diff == -1 || diff == 1 ) { - /* the first found difference was in parities */ - for ( i = 0, diff = 0; i < n; i ++ ) { - if ( Stereo->nNumberInv[i] != Stereo->nNumber[i] ) { - diff = 2; /* difference in stereo center numbering */ - break; - } - /* parities can be only 1, 2, 3, 4. Therefore only mutually inverted pairs - * (t_parityInv, t_parity) = (1,2) or (2,1) statisfy conditions - * (t_parityInv != t_parity) && (t_parityInv + t_parity == 3) - */ - if ( Stereo->t_parityInv[i] == Stereo->t_parity[i] || - Stereo->t_parityInv[i] + Stereo->t_parity[i] != 3 ) { - diff = 1; /* parities are same or different and cannot be obtained by simple inversion */ - break; - } - } - Stereo->bTrivialInv = !diff; - } else { - Stereo->bTrivialInv = 0; - } -exit_function: - - return nErrorCode; -} -/***************************************************************************/ -int MarkAmbiguousStereo( sp_ATOM *at, inp_ATOM *norm_at, int bIsotopic, AT_NUMB *pCanonOrd, - AT_STEREO_CARB *LinearCTStereoCarb, int nLenLinearCTStereoCarb, - AT_STEREO_DBLE *LinearCTStereoDble, int nLenLinearCTStereoDble ) -{ - int n, i, j1, j2, num, mark_atom, mark_bond; - - if ( !pCanonOrd ) - return -1; - num = 0; - n = nLenLinearCTStereoCarb; - mark_atom = bIsotopic? AMBIGUOUS_STEREO_ATOM_ISO : AMBIGUOUS_STEREO_ATOM; - for ( i = 0; i < n; i ++ ) { - /* mark ambiguous stereo centers (for displaying and "Ambiguous stereo" message) */ - if ( ATOM_PARITY_NOT_UNKN(LinearCTStereoCarb[i].parity) && - at[j1=pCanonOrd[(int)LinearCTStereoCarb[i].at_num-1]].bAmbiguousStereo ) { - at[j1].bAmbiguousStereo |= mark_atom; - norm_at[j1].bAmbiguousStereo |= mark_atom; - num ++; - } - } - - n = nLenLinearCTStereoDble; - mark_bond = bIsotopic? AMBIGUOUS_STEREO_BOND_ISO : AMBIGUOUS_STEREO_BOND; - for ( i = 0; i < n; i ++ ) { - /* mark ambiguous stereo bonds or allenes (for displaying and "Ambiguous stereo" message) */ - if ( ATOM_PARITY_WELL_DEF(LinearCTStereoDble[i].parity) ) { - j1=pCanonOrd[(int)LinearCTStereoDble[i].at_num1-1]; - j2=pCanonOrd[(int)LinearCTStereoDble[i].at_num2-1]; - if ( at[j1].bAmbiguousStereo || at[j2].bAmbiguousStereo ) { - /* if it is an allene then mark the central atom only - because the bonds should not be marked to avoid misleading - message "Ambiguous stereo: bond(s)": Allene makes a stereocenter - */ - int j1_parity = bIsotopic? at[j1].stereo_bond_parity2[0] : - at[j1].stereo_bond_parity[0]; - int cumulene_len = BOND_CHAIN_LEN(j1_parity); /* 0 => double bond, 1 => allene, 2 => cumulene,..*/ - if ( cumulene_len % 2 && (1 >= MAX_NUM_STEREO_BONDS || - !(bIsotopic? at[j1].stereo_bond_neighbor2[1] : - at[j1].stereo_bond_neighbor[1] )) ) { - /* found an allene; locate its central atom */ - int next_j, next_neigh; - int j = j1; - next_j = at[j].neighbor[bIsotopic? at[j].stereo_bond_ord2[0] : - at[j].stereo_bond_ord[0] ]; - for ( cumulene_len = (cumulene_len-1)/2; - cumulene_len && 2==at[next_j].valence; - cumulene_len -- ) { - next_neigh = (j == at[next_j].neighbor[0]); - j = next_j; - next_j = at[next_j].neighbor[next_neigh]; - } - /* next_j is the central atom */ - if ( 2==at[next_j].valence ) { - at[next_j].bAmbiguousStereo |= mark_atom; - norm_at[next_j].bAmbiguousStereo |= mark_atom; - num ++; - continue; /* do not mark the cumulene "bond" endpoints */ - } - } - /* not an allene, mark double bond or cumulene end atoms */ - if ( at[j1].bAmbiguousStereo ) { - at[j1].bAmbiguousStereo |= mark_bond; /* ??? */ - norm_at[j1].bAmbiguousStereo |= mark_bond; - num ++; - } - if ( at[j2].bAmbiguousStereo ) { - at[j2].bAmbiguousStereo |= mark_bond; /* ??? */ - norm_at[j2].bAmbiguousStereo |= mark_bond; - num ++; - } - } - } - } - return num; - -} -/**********************************************************************************************/ -INCHI_MODE UnmarkAllUndefinedUnknownStereo( INChI_Stereo *Stereo, INCHI_MODE nUserMode ) -{ - INCHI_MODE nRet = 0; - int i, n; - if ( !Stereo || Stereo && !Stereo->nNumberOfStereoCenters && !Stereo->nNumberOfStereoBonds) { - return nRet; - } - - /* stereocenters */ - if ( !Stereo->nCompInv2Abs && - (n=Stereo->nNumberOfStereoCenters) > 0 && (nUserMode & REQ_MODE_SC_IGN_ALL_UU) ) { - - for ( i = 0; i < n && !ATOM_PARITY_WELL_DEF(Stereo->t_parity[i]); i ++ ) - ; - if ( i == n ) { - Stereo->nNumberOfStereoCenters = 0; - for ( i = 0; i < n; i ++ ) { - Stereo->t_parity[i] = 0; - Stereo->nNumber[i] = 0; - Stereo->t_parityInv[i] = 0; - Stereo->nNumberInv[i] = 0; - } - nRet |= REQ_MODE_SC_IGN_ALL_UU; - } - } - /* stereobonds */ - if ( (n=Stereo->nNumberOfStereoBonds) > 0 && (nUserMode & REQ_MODE_SB_IGN_ALL_UU) ) { - for ( i = 0; i < n && !ATOM_PARITY_WELL_DEF(Stereo->b_parity[i]); i ++ ) - ; - if ( i == n ) { - Stereo->nNumberOfStereoBonds = 0; - for ( i = 0; i < n; i ++ ) { - Stereo->b_parity[i] = 0; - Stereo->nBondAtom1[i] = 0; - Stereo->nBondAtom2[i] = 0; - } - nRet |= REQ_MODE_SB_IGN_ALL_UU; - } - } - - return nRet; -} -#if ( defined(TARGET_API_LIB) || ADD_CMLPP==1 ) -/**********************************************************************************************/ -void WriteCoord( char *str, double x ) -{ - if ( x < -9999999.9 ) { - sprintf( str, "%10.2e", x ); - } else - if ( x < -999999.99 ) { - sprintf( str, "%10.2f", x ); - } else - if ( x < -99999.999 ) { - sprintf( str, "%10.3f", x ); - } else - if ( x < 99999.9999 ) { - sprintf( str, "%10.4f", x ); - } else - if ( x < 999999.999 ) { - sprintf( str, "%10.3f", x ); - } else - if ( x < 9999999.99 ) { - sprintf( str, "%10.2f", x ); - } else - if ( x < 99999999.9 ) { - sprintf( str, "%10.1f", x ); - } else { - sprintf( str, "%10.3e", x ); - } -} -#endif -/* used CANON_STAT members - - pCS->LinearCT - pCS->LinearCTIsotopic - pCS->LinearCTIsotopicStereoCarb - pCS->LinearCTIsotopicStereoCarbInv - pCS->LinearCTIsotopicStereoDble - pCS->LinearCTIsotopicStereoDbleInv - pCS->LinearCTIsotopicTautomer - pCS->LinearCTStereoCarb - pCS->LinearCTStereoCarbInv - pCS->LinearCTStereoDble - pCS->LinearCTStereoDbleInv - pCS->nCanonOrd - pCS->nCanonOrdIsotopic - pCS->nCanonOrdIsotopicStereo - pCS->nCanonOrdIsotopicStereoInv - pCS->nCanonOrdIsotopicStereoTaut - pCS->nCanonOrdIsotopicTaut - pCS->nCanonOrdStereo - pCS->nCanonOrdStereoInv - pCS->nCanonOrdStereoTaut - pCS->nCanonOrdTaut - pCS->nLenCanonOrd - pCS->nLenCanonOrdIsotopic - pCS->nLenCanonOrdIsotopicStereo - pCS->nLenCanonOrdIsotopicStereoTaut - pCS->nLenCanonOrdIsotopicTaut - pCS->nLenCanonOrdStereo - pCS->nLenCanonOrdStereoTaut - pCS->nLenCanonOrdTaut - pCS->nLenLinearCTAtOnly - pCS->nLenLinearCTIsotopic - pCS->nLenLinearCTIsotopicStereoCarb - pCS->nLenLinearCTIsotopicStereoDble - pCS->nLenLinearCTIsotopicTautomer - pCS->nLenLinearCTStereoCarb - pCS->nLenLinearCTStereoDble - pCS->nNum_H - pCS->nNum_H_fixed - pCS->nSymmRank - pCS->nSymmRankIsotopic - pCS->nSymmRankIsotopicTaut - pCS->nSymmRankTaut - pCS->t_group_info - pCS->t_group_info->num_t_groups - -*/ -/**********************************************************************************************/ -int FillOutINChI( INChI *pINChI, INChI_Aux *pINChI_Aux, - int num_atoms, int num_at_tg, int num_removed_H, - sp_ATOM *at, inp_ATOM *norm_at, CANON_STAT *pCS, int bTautomeric, - INCHI_MODE nUserMode, char *pStrErrStruct ) -{ - int i, j, m, n, g, len, ii, ret=0; - - AT_NUMB *pSymmRank, *pOrigNosInCanonOrd, *pConstitEquNumb, *pCanonOrd=NULL, *pCanonOrdInv=NULL, *pCanonOrdTaut; - T_GROUP_INFO *t_group_info = pCS->t_group_info; - T_GROUP *t_group; - int nErrorCode = 0; - AT_NUMB *pCanonRank, *pCanonRankInv; /* canonical ranks of the atoms or tautomeric groups */ - AT_NUMB *pCanonRankAtoms=NULL, *pSortOrd = NULL; - AT_RANK nMinOrd; - INChI_Stereo *Stereo; - int bUseNumberingInv = 0, bUseIsotopicNumberingInv = 0; - INCHI_MODE nStereoUnmarkMode; - - /*AT_NUMB *pCanonOrdNonIso = NULL, *pCanonOrdIso = NULL;*/ - /*AT_NUMB *nOrigAtNosInCanonOrdNonIso = NULL, *nOrigAtNosInCanonOrdIso = NULL;*/ - - /* Check for warnings */ - if ( pCS->nLenLinearCTStereoCarb < 0 || pCS->nLenLinearCTStereoDble < 0 || - pCS->nLenCanonOrdStereo < 0 || pCS->nLenCanonOrdStereoTaut < 0) { - nErrorCode |= WARN_FAILED_STEREO; - } - if ( pCS->nLenLinearCTIsotopic < 0 || pCS->nLenLinearCTIsotopicTautomer < 0 || - pCS->nLenCanonOrdIsotopic < 0 || pCS->nLenCanonOrdIsotopicTaut < 0 ) { - nErrorCode |= WARN_FAILED_ISOTOPIC; - } - if ( pCS->nLenLinearCTIsotopicStereoCarb < 0 || pCS->nLenLinearCTIsotopicStereoDble < 0 || - pCS->nLenCanonOrdIsotopicStereo < 0 || pCS->nLenCanonOrdIsotopicStereoTaut < 0) { - nErrorCode |= WARN_FAILED_ISOTOPIC_STEREO; - } - pCanonRankAtoms = (AT_NUMB *)inchi_calloc( num_at_tg+1, sizeof(pCanonRankAtoms[0]) ); - pSortOrd = (AT_NUMB *)inchi_calloc( num_at_tg+1, sizeof(pSortOrd[0]) ); /* must have more than num_atoms */ - - if ( !pCanonRankAtoms || !pSortOrd ) { - nErrorCode = 0; - ret = CT_OUT_OF_RAM; /* */ - pINChI->nErrorCode = pINChI_Aux->nErrorCode = CT_OUT_OF_RAM; - goto exit_function; - } - - /* total charge */ - for ( i = 0, n = 0; i < num_atoms+num_removed_H; i ++ ) { - n += at[i].charge; - } - pINChI->nTotalCharge = n; - - /* number of atoms */ - pINChI->nNumberOfAtoms = num_atoms; - pINChI_Aux->nNumberOfAtoms = num_atoms; - - /* removed protons and detachable isotopic H */ - if ( bTautomeric && t_group_info ) { - pINChI_Aux->nNumRemovedProtons = t_group_info->tni.nNumRemovedProtons; - for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { - pINChI_Aux->nNumRemovedIsotopicH[i] = t_group_info->num_iso_H[i] - + t_group_info->tni.nNumRemovedProtonsIsotopic[i]; - } - if ( pINChI_Aux->bNormalizationFlags & FLAG_FORCE_SALT_TAUT ) { - pINChI->nFlags |= INCHI_FLAG_HARD_ADD_REM_PROTON; - } - if ( pINChI_Aux->bNormalizationFlags & (FLAG_NORM_CONSIDER_TAUT &~FLAG_PROTON_CHARGE_CANCEL) ) { - AddMOLfileError(pStrErrStruct, "Proton(s) added/removed"); - } - if ( pINChI_Aux->bNormalizationFlags & FLAG_PROTON_CHARGE_CANCEL ) { - AddMOLfileError(pStrErrStruct, "Charges neutralized"); - } - } - - /* abs or rel stereo may establish one of two canonical numberings */ - if ( (pCS->nLenLinearCTStereoCarb > 0 || pCS->nLenLinearCTStereoDble > 0) && - pCS->nLenCanonOrdStereo > 0 && - (pCS->LinearCTStereoCarb && pCS->LinearCTStereoCarbInv || - pCS->LinearCTStereoDble && pCS->LinearCTStereoDbleInv) && - pCS->nCanonOrdStereo && pCS->nCanonOrdStereoInv - ) { - - pCanonRank = pCanonRankAtoms; - pCanonOrd = pCS->nCanonOrdStereo; - pCanonRankInv = pSortOrd; - pCanonOrdInv = pCS->nCanonOrdStereoInv; - Stereo = pINChI->Stereo; - for ( i = 0; i < num_at_tg; i ++ ) { - pCanonRankInv[pCanonOrdInv[i]] = - pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); - } - /********************************************************************/ - /* copy stereo bonds and stereo centers; compare Inv and Abs stereo */ - /********************************************************************/ - nErrorCode = CopyLinearCTStereoToINChIStereo( Stereo, - pCS->LinearCTStereoCarb, pCS->nLenLinearCTStereoCarb, - pCS->LinearCTStereoDble, pCS->nLenLinearCTStereoDble - , pCanonOrd, pCanonRank, at, 0 /* non-isotopic */ - , pCS->LinearCTStereoCarbInv - , pCS->LinearCTStereoDbleInv - , pCanonOrdInv, pCanonRankInv ); - - if ( Stereo->t_parityInv && Stereo->nNumberInv ) { - if ( nUserMode & REQ_MODE_RELATIVE_STEREO ) { - pINChI->nFlags |= INCHI_FLAG_REL_STEREO; - } - if ( nUserMode & REQ_MODE_RACEMIC_STEREO ) { - pINChI->nFlags |= INCHI_FLAG_RAC_STEREO; - } - if ( Stereo->nCompInv2Abs ) { - if ( Stereo->nCompInv2Abs == -1 ) { - /* switch pointers in Stereo so that the stereo becomes the smallest (relative) */ - /* flag Stereo->nCompInv2Abs == -1 will keep track of this exchange */ - AT_NUMB *nNumberInv = Stereo->nNumberInv; - S_CHAR *t_parityInv = Stereo->t_parityInv; - Stereo->nNumberInv = Stereo->nNumber; - Stereo->t_parityInv = Stereo->t_parity; - Stereo->nNumber = nNumberInv; - Stereo->t_parity = t_parityInv; - /* switch pointers to set rel. stereo to pINChI_Aux->nOrigAtNosInCanonOrd - and inv. stereo to pINChI_Aux->nOrigAtNosInCanonOrdInv */ - switch_ptrs( &pCanonRank, &pCanonRankInv ); - switch_ptrs( &pCanonOrd, &pCanonOrdInv ); - bUseNumberingInv = 1; /* use inverted stereo numbering instead of normal */ - } - } - } - - for ( i = 0; i < num_atoms; i ++ ) { - pINChI_Aux->nOrigAtNosInCanonOrdInv[i] = at[pCanonOrdInv[i]].orig_at_number; - pINChI_Aux->nOrigAtNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; - } - if ( bUseNumberingInv ) { - /* switch ptrs back to avoid confusion */ - switch_ptrs( &pCanonRank, &pCanonRankInv ); - switch_ptrs( &pCanonOrd, &pCanonOrdInv ); - /* save inverted stereo ranks & order because it represents the smallest (relative) */ - memcpy( pCanonRank, pCanonRankInv, num_at_tg * sizeof(pCanonRank[0]) ); - /* change pCS->nCanonOrdStereo[] to inverted: */ - memcpy( pCanonOrd, pCanonOrdInv, num_at_tg * sizeof(pCanonOrd[0]) ); - } - pCanonRankInv = NULL; - pCanonOrdInv = NULL; - pOrigNosInCanonOrd = NULL; - - } else { /*------------------------------ no stereo */ - - pCanonOrd = pCS->nLenCanonOrdStereo > 0? pCS->nCanonOrdStereo : - pCS->nLenCanonOrd > 0? pCS->nCanonOrd : NULL; - pCanonRank = pCanonRankAtoms; - pOrigNosInCanonOrd = pINChI_Aux->nOrigAtNosInCanonOrd; - if ( pCanonOrd && pCanonRank ) { - for ( i = 0; i < num_atoms; i ++ ) { - pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); - pOrigNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; - } - for ( ; i < num_at_tg; i ++ ) { - pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); - } - } - } - /*pCanonOrdNonIso = pCanonOrd;*/ /* save for aux info */ - - - if ( pINChI_Aux->OrigInfo ) { - /* charges, radicals, valences */ - for ( i = 0; i < num_atoms; i ++ ) { - ii = pCanonOrd[i]; - if ( norm_at[ii].valence || norm_at[ii].num_H ) { - pINChI_Aux->OrigInfo[i].cCharge = norm_at[ii].charge; - pINChI_Aux->OrigInfo[i].cRadical = (norm_at[ii].radical==RADICAL_SINGLET)? 0 : - (norm_at[ii].radical==RADICAL_DOUBLET)? 1 : - (norm_at[ii].radical==RADICAL_TRIPLET)? 2 : - norm_at[ii].radical? 3 : 0 ; - pINChI_Aux->OrigInfo[i].cUnusualValence = - get_unusual_el_valence( norm_at[ii].el_number, norm_at[ii].charge, norm_at[ii].radical, - norm_at[ii].chem_bonds_valence, norm_at[ii].num_H, norm_at[ii].valence ); - } else { - /* charge of a single atom component is in the INChI; valence = 0 is standard */ - pINChI_Aux->OrigInfo[i].cRadical = (norm_at[ii].radical==RADICAL_SINGLET)? 0 : - (norm_at[ii].radical==RADICAL_DOUBLET)? 1 : - (norm_at[ii].radical==RADICAL_TRIPLET)? 2 : - norm_at[ii].radical? 3 : 0 ; - } - - } - } - - /* non-isotopic canonical numbers and equivalence of atoms (Aux) */ - pConstitEquNumb = pINChI_Aux->nConstitEquNumbers; /* contitutional equivalence */ - pSymmRank = pCS->nSymmRank; - if ( pCanonOrd && pCanonRank && pSymmRank && pConstitEquNumb ) { - for ( i = 0; i < num_atoms; i ++ ) { - pConstitEquNumb[i] = pSymmRank[pCanonOrd[i]]; /* constit. equ. ranks in order of canonical numbers */ - pSortOrd[i] = i; - } - for ( ; i < num_at_tg; i ++ ) { - pSortOrd[i] = MAX_ATOMS; /* for debugging only */ - } - pn_RankForSort = pConstitEquNumb; - qsort( pSortOrd, num_atoms, sizeof(pSortOrd[0]), CompRanksOrd ); - for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= num_atoms; j ++ ) { - if ( j == num_atoms || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) { - nMinOrd ++; - if ( j - i > 1 ) { - /* found a sequence of equivalent atoms: i..j-1 */ - while ( i < j ) { - pConstitEquNumb[pSortOrd[i++]] = nMinOrd; /* = min. canon. rank in the group of equ. atoms */ - } - /* at this point j == i */ - } else { - pConstitEquNumb[pSortOrd[i++]] = 0; /* means the atom is not equivalent to any other */ - } - nMinOrd = pSortOrd[j]; /* at the end j = num_atoms */ - } - } - } else { - nErrorCode |= ERR_NO_CANON_RESULTS; - ret = -1; /* program error; no breakpoint here */ - goto exit_function; - } - /* atomic numbers from the Periodic Table */ - for ( i = 0; i < num_atoms; i ++ ) { - pINChI->nAtom[i] = (int)at[pCanonOrd[i]].el_number; - } - /* connection table: atoms only (before 7-29-2003 pCS->LinearCT2 contained non-isotopic CT) */ - if ( pCS->nLenLinearCTAtOnly <= 0 || !pCS->LinearCT || !pINChI->nConnTable ) { - nErrorCode |= ERR_NO_CANON_RESULTS; - ret = -2; - goto exit_function; - } - memcpy( pINChI->nConnTable, pCS->LinearCT, sizeof(pINChI->nConnTable[0])*pCS->nLenLinearCTAtOnly); - pINChI->lenConnTable = pCS->nLenLinearCTAtOnly; - - /* tautomeric group(s) canonical representation */ - len = 0; - if ( bTautomeric && 0 < (n = SortTautomerGroupsAndEndpoints( t_group_info, num_atoms, num_at_tg, pCanonRank )) ) { - /* SortTautomerGroupsAndEndpoints() produces canonically ordered t-groups */ - pINChI->nFlags |= (t_group_info->bTautFlagsDone & TG_FLAG_ALL_SALT_DONE)? INCHI_FLAG_ACID_TAUT : 0; - /* number of tautomeric groups */ - pINChI->nTautomer[len ++] = (AT_NUMB)n; - /* store each tautomeric group, one by one */ - for ( i = 0; i < n; i ++ ) { - g = (int)t_group_info->tGroupNumber[i]; /* original group numbers in sorted order */ - t_group = t_group_info->t_group + g; /* pointer to the tautomeric group */ - /* NumAt+INCHI_T_NUM_MOVABLE (group length excluding this number) */ - pINChI->nTautomer[len ++] = t_group->nNumEndpoints+INCHI_T_NUM_MOVABLE; - /* Num(H), Num(-) */ - for ( j = 0; j < INCHI_T_NUM_MOVABLE && j < T_NUM_NO_ISOTOPIC; j ++ ) - pINChI->nTautomer[len ++] = t_group->num[j]; - for ( j = T_NUM_NO_ISOTOPIC; j < INCHI_T_NUM_MOVABLE; j ++ ) - pINChI->nTautomer[len ++] = 0; /* should not happen */ - /* tautomeric group endpoint canonical numbers, pre-sorted in ascending order */ - for ( j = (int)t_group->nFirstEndpointAtNoPos, - m = j + (int)t_group->nNumEndpoints; j < m; j ++ ) { - pINChI->nTautomer[len ++] = pCanonRank[(int)t_group_info->nEndpointAtomNumber[j]]; /* At[j] */ - } - } - pINChI->lenTautomer = len; - pINChI_Aux->nNumberOfTGroups = n; - } else { - pINChI->lenTautomer = 0; - pINChI_Aux->nNumberOfTGroups = 0; - if ( t_group_info && ((t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) || - t_group_info->nNumIsotopicEndpoints>1 && - (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE))) - ) { - /* only protons (re)moved or added */ - pINChI->lenTautomer = 1; - pINChI->nTautomer[0] = 0; - } - } - - /* number of H (excluding tautomeric) */ - if ( pCS->nNum_H ) { - for ( i = 0; i < num_atoms; i ++ ) { - pINChI->nNum_H[i] = pCS->nNum_H[i]; - } - } - /* number of fixed H (tautomeric H in non-tautomeric representation) */ - if ( pCS->nNum_H_fixed && !pINChI->lenTautomer ) { - for ( i = 0; i < num_atoms; i ++ ) { - pINChI->nNum_H_fixed[i] = pCS->nNum_H_fixed[i]; - pINChI->nNum_H[i] += pCS->nNum_H_fixed[i]; - } - } - - /*********************************************************** - * tautomeric group(s) numbering and symmetry; - * should not depend on switching to rel. stereo numbering - */ - if ( pINChI->lenTautomer && (n=pINChI_Aux->nNumberOfTGroups) ) { - pCanonOrdTaut = pCS->nLenCanonOrdStereoTaut > 0? pCS->nCanonOrdStereoTaut : - pCS->nLenCanonOrdTaut > 0? pCS->nCanonOrdTaut : NULL; - pConstitEquNumb = pINChI_Aux->nConstitEquTGroupNumbers; - pSymmRank = pCS->nSymmRankTaut; - if ( pCanonOrdTaut && pSymmRank && pConstitEquNumb ) { - for ( i = 0; i < n; i ++ ) { - pConstitEquNumb[i] = pSymmRank[pCanonOrdTaut[i]]; - pSortOrd[i] = i; - } - pn_RankForSort = pConstitEquNumb; - qsort( pSortOrd, n, sizeof(pSortOrd[0]), CompRanksOrd ); - for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= n; j ++ ) { - if ( j == n || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) { - nMinOrd ++; /* make is start from 1, not from zero */ - if ( j - i > 1 ) { - /* found a sequence of more than one equivalent t-groups: i..j-1 */ - while ( i < j ) { - pConstitEquNumb[pSortOrd[i++]] = nMinOrd; - } - } else { - pConstitEquNumb[pSortOrd[i++]] = 0; - } - nMinOrd = pSortOrd[j]; /* at the end j == n */ - } - } - } - } - - /* Allocate and fill Hill formula */ - if ( !(pINChI->szHillFormula = AllocateAndFillHillFormula( pINChI ) ) ) { - nErrorCode = 0; - ret = CT_WRONG_FORMULA; /* CT_OUT_OF_RAM;*/ /* */ - pINChI->nErrorCode = pINChI_Aux->nErrorCode = ret; - goto exit_function; - } - - if ( nStereoUnmarkMode = UnmarkAllUndefinedUnknownStereo( pINChI->Stereo, nUserMode ) ) { - pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU)? INCHI_FLAG_SC_IGN_ALL_UU : 0; - pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU)? INCHI_FLAG_SB_IGN_ALL_UU : 0; - if ( (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU) || - (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU) ) { - AddMOLfileError(pStrErrStruct, "Omitted undefined stereo"); - } - } - - /*************************/ - /* mark ambiguous stereo */ - /*************************/ - MarkAmbiguousStereo( at, norm_at, 0 /* non-isotopic */, pCanonOrd, - pCS->LinearCTStereoCarb, pCS->nLenLinearCTStereoCarb, - pCS->LinearCTStereoDble, pCS->nLenLinearCTStereoDble ); - - - /************************************************************************ - * - * isotopic part - */ - /* abs or rel stereo may establish one of two canonical numberings */ - if ( (pCS->nLenLinearCTIsotopicStereoCarb > 0 || pCS->nLenLinearCTIsotopicStereoDble > 0) && - pCS->nLenCanonOrdIsotopicStereo > 0 && - (pCS->LinearCTIsotopicStereoCarb && pCS->LinearCTIsotopicStereoCarbInv || - pCS->LinearCTIsotopicStereoDble && pCS->LinearCTIsotopicStereoDbleInv) && - pCS->nCanonOrdIsotopicStereo && pCS->nCanonOrdIsotopicStereoInv - ) { - /* found isotopic stereo */ - pCanonRank = pCanonRankAtoms; - pCanonOrd = pCS->nCanonOrdIsotopicStereo; - pCanonRankInv = pSortOrd; - pCanonOrdInv = pCS->nCanonOrdIsotopicStereoInv; - Stereo = pINChI->StereoIsotopic; - for ( i = 0; i < num_at_tg; i ++ ) { - pCanonRankInv[pCanonOrdInv[i]] = - pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); - } - /********************************************************************/ - /* copy stereo bonds and stereo centers; compare Inv and Abs stereo */ - /********************************************************************/ - nErrorCode = CopyLinearCTStereoToINChIStereo( Stereo, - pCS->LinearCTIsotopicStereoCarb, pCS->nLenLinearCTIsotopicStereoCarb, - pCS->LinearCTIsotopicStereoDble, pCS->nLenLinearCTIsotopicStereoDble - , pCanonOrd, pCanonRank, at, 1 /* isotopic */ - , pCS->LinearCTIsotopicStereoCarbInv - , pCS->LinearCTIsotopicStereoDbleInv - , pCanonOrdInv, pCanonRankInv ); - - if ( Stereo->t_parityInv && Stereo->nNumberInv ) { - if ( nUserMode & REQ_MODE_RELATIVE_STEREO ) { - pINChI->nFlags |= INCHI_FLAG_REL_STEREO; - } - if ( nUserMode & REQ_MODE_RACEMIC_STEREO ) { - pINChI->nFlags |= INCHI_FLAG_RAC_STEREO; - } - if ( Stereo->nCompInv2Abs ) { - if ( Stereo->nCompInv2Abs == -1 ) { - /* switch pointers so that the stereo becomes the smallest (relative) */ - /* flag Stereo->nCompInv2Abs == -1 will keep track of this exchange */ - AT_NUMB *nNumberInv = Stereo->nNumberInv; - S_CHAR *t_parityInv = Stereo->t_parityInv; - Stereo->nNumberInv = Stereo->nNumber; - Stereo->t_parityInv = Stereo->t_parity; - Stereo->nNumber = nNumberInv; - Stereo->t_parity = t_parityInv; - switch_ptrs( &pCanonRank, &pCanonRankInv ); - switch_ptrs( &pCanonOrd, &pCanonOrdInv ); - bUseIsotopicNumberingInv = 1; - } - } - } - - for ( i = 0; i < num_atoms; i ++ ) { - pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv[i] = at[pCanonOrdInv[i]].orig_at_number; - pINChI_Aux->nIsotopicOrigAtNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; - } - if ( bUseIsotopicNumberingInv ) { - switch_ptrs( &pCanonRank, &pCanonRankInv ); - switch_ptrs( &pCanonOrd, &pCanonOrdInv ); - memcpy( pCanonRank, pCanonRankInv, num_at_tg * sizeof(pCanonRank[0]) ); - memcpy( pCanonOrd, pCanonOrdInv, num_at_tg * sizeof(pCanonOrd[0]) ); - } - pCanonRankInv = NULL; - pCanonOrdInv = NULL; - pOrigNosInCanonOrd = NULL; - - } else { - /* no isotopic stereo */ - pCanonOrd = pCS->nLenCanonOrdIsotopicStereo > 0? pCS->nCanonOrdIsotopicStereo : - pCS->nLenCanonOrdIsotopic > 0? pCS->nCanonOrdIsotopic : NULL; - pCanonRank = pCanonRankAtoms; - pOrigNosInCanonOrd = pINChI_Aux->nIsotopicOrigAtNosInCanonOrd; - if ( pCanonOrd && pCanonRank ) { - for ( i = 0; i < num_atoms; i ++ ) { /* Fix13 -- out of bounds */ - pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); - pOrigNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; - } - for ( ; i < num_at_tg; i ++ ) { /* Fix13 -- out of bounds */ - pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); - } - } - } - /*pCanonOrdIso = pCanonOrd;*/ - - pConstitEquNumb = pINChI_Aux->nConstitEquIsotopicNumbers; - pSymmRank = pCS->nSymmRankIsotopic; - if ( pCanonOrd && pCanonRank && pConstitEquNumb && pSymmRank ) { - for ( i = 0; i < num_atoms; i ++ ) { - pConstitEquNumb[i] = pSymmRank[pCanonOrd[i]]; - pSortOrd[i] = i; - } - for ( ; i < num_at_tg; i ++ ) { - pSortOrd[i] = i; - } - pn_RankForSort = pConstitEquNumb; - qsort( pSortOrd, num_atoms, sizeof(pSortOrd[0]), CompRanksOrd ); - for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= num_atoms; j ++ ) { - if ( j == num_atoms || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) { - nMinOrd ++; - if ( j - i > 1 ) { - /* found a sequence of equivalent atoms: i..j-1 */ - while ( i < j ) { - pConstitEquNumb[pSortOrd[i++]] = nMinOrd; - } - } else { - pConstitEquNumb[pSortOrd[i++]] = 0; /* nMinOrd; */ - } - nMinOrd = pSortOrd[j]; - } - } - } else { - goto exit_function; /* no isotopic info available */ - } - /* isotopic atoms */ - n = pINChI->nNumberOfIsotopicAtoms = pCS->nLenLinearCTIsotopic; - for ( i = 0; i < n; i ++ ) { - pINChI->IsotopicAtom[i].nAtomNumber = pCS->LinearCTIsotopic[i].at_num; - pINChI->IsotopicAtom[i].nIsoDifference = pCS->LinearCTIsotopic[i].iso_atw_diff; - pINChI->IsotopicAtom[i].nNum_H = pCS->LinearCTIsotopic[i].num_1H; - pINChI->IsotopicAtom[i].nNum_D = pCS->LinearCTIsotopic[i].num_D; - pINChI->IsotopicAtom[i].nNum_T = pCS->LinearCTIsotopic[i].num_T; - } - /* isotopic tautomeric groups */ - n = pINChI->nNumberOfIsotopicTGroups = pCS->nLenLinearCTIsotopicTautomer; - for ( i = 0; i < n; i ++ ) { - pINChI->IsotopicTGroup[i].nTGroupNumber = pCS->LinearCTIsotopicTautomer[i].tgroup_num; - pINChI->IsotopicTGroup[i].nNum_H = pCS->LinearCTIsotopicTautomer[i].num[2]; - pINChI->IsotopicTGroup[i].nNum_D = pCS->LinearCTIsotopicTautomer[i].num[1]; - pINChI->IsotopicTGroup[i].nNum_T = pCS->LinearCTIsotopicTautomer[i].num[0]; - } - /* atoms that may exchange isotopic H-atoms */ - if ( pCS->nExchgIsoH && pINChI->nPossibleLocationsOfIsotopicH ) { - for ( i = 0, j = 1; i < num_atoms; i ++ ) { - if ( pCS->nExchgIsoH[i] ) { - pINChI->nPossibleLocationsOfIsotopicH[j++] = (AT_NUMB)(i+1); /* canonical number */ - } - } - pINChI->nPossibleLocationsOfIsotopicH[0] = (AT_NUMB)j; /* length including the 0th element */ - } - - if ( nStereoUnmarkMode = UnmarkAllUndefinedUnknownStereo( pINChI->StereoIsotopic, nUserMode ) ) { - pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU)? INCHI_FLAG_SC_IGN_ALL_ISO_UU : 0; - pINChI->nFlags |= (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU)? INCHI_FLAG_SC_IGN_ALL_ISO_UU : 0; - if ( (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU) || - (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU) ) { - AddMOLfileError(pStrErrStruct, "Omitted undefined stereo"); - } - } - /* mark ambiguous stereo */ - MarkAmbiguousStereo( at, norm_at, 1 /* isotopic */, pCanonOrd, - pCS->LinearCTIsotopicStereoCarb, pCS->nLenLinearCTIsotopicStereoCarb, - pCS->LinearCTIsotopicStereoDble, pCS->nLenLinearCTIsotopicStereoDble ); - - /*********************************************************** - * isotopic tautomeric group(s) numbering and symmetry; - * should not depend on switching to rel. stereo numbering - */ - if ( pINChI->lenTautomer && pINChI_Aux->nConstitEquIsotopicTGroupNumbers && pCS->nSymmRankIsotopicTaut && - (pCS->nLenLinearCTIsotopic || pCS->nLenLinearCTIsotopicTautomer) && - t_group_info && t_group_info->num_t_groups > 0 ) { - n = t_group_info->num_t_groups; - pCanonOrdTaut = pCS->nLenCanonOrdIsotopicStereoTaut > 0? - (n=pCS->nLenCanonOrdIsotopicStereoTaut, pCS->nCanonOrdIsotopicStereoTaut) : - pCS->nLenCanonOrdIsotopicTaut > 0? - (n=pCS->nLenCanonOrdIsotopicTaut,pCS->nCanonOrdIsotopicTaut) : (n=0,(AT_RANK*)NULL); - pConstitEquNumb = pINChI_Aux->nConstitEquIsotopicTGroupNumbers; - pSymmRank = pCS->nSymmRankIsotopicTaut; - if ( pCanonOrdTaut && pSymmRank && pConstitEquNumb && n > 0 ) { - for ( i = 0; i < n; i ++ ) { - pConstitEquNumb[i] = pSymmRank[pCanonOrdTaut[i]]; - pSortOrd[i] = i; - } - pn_RankForSort = pConstitEquNumb; - qsort( pSortOrd, n, sizeof(pSortOrd[0]), CompRanksOrd ); - for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= n; j ++ ) { - if ( j == n || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) { - nMinOrd ++; - if ( j - i > 1 ) { - /* found a sequence of equivalent t-groups: i..j-1 */ - while ( i < j ) { - pConstitEquNumb[pSortOrd[i++]] = nMinOrd; - } - } else { - pConstitEquNumb[pSortOrd[i++]] = 0; /* nMinOrd; */ - } - nMinOrd = pSortOrd[j]; /* at the end j = n */ - } - } - } - } - - -exit_function: - if ( pCanonRankAtoms ) - inchi_free( pCanonRankAtoms ); - if ( pSortOrd ) - inchi_free( pSortOrd ); - - pINChI->nErrorCode |= nErrorCode; - pINChI_Aux->nErrorCode |= nErrorCode; - - return ret; -} +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include + +#include "mode.h" +#include "ichimain.h" +#include "ichi_io.h" + +/* Local */ + +int GetHillFormulaCounts( U_CHAR *nAtom, + S_CHAR *nNum_H, + int num_atoms, + AT_NUMB *nTautomer, + int lenTautomer, + int *pnum_C, + int *pnum_H, + int *pnLen, + int *pnNumNonHAtoms ); +int MakeHillFormula( U_CHAR *nAtom, + int num_atoms, + char *szLinearCT, + int nLen_szLinearCT, + int num_C, + int num_H, + int *bOverflow ); + +#if ( FIX_DALKE_BUGS == 1 ) +#else +char *AllocateAndFillHillFormula( INChI *pINChI ); +#endif + +int AddElementAndCount( const char *szElement, + int mult, char *szLinearCT, + int nLenLinearCT, + int *bOverflow ); + +int Copy2StereoBondOrAllene( INChI_Stereo *Stereo, + int *nNumberOfStereoCenters, + int *nNumberOfStereoBonds, + AT_STEREO_DBLE *LinearCTStereoDble, + AT_NUMB *pCanonOrd, AT_RANK *pCanonRank, + sp_ATOM *at, + int bIsotopic ); + +int CopyLinearCTStereoToINChIStereo( INChI_Stereo *Stereo, + AT_STEREO_CARB *LinearCTStereoCarb, + int nLenLinearCTStereoCarb, + AT_STEREO_DBLE *LinearCTStereoDble, + int nLenLinearCTStereoDble, + AT_NUMB *pCanonOrd, AT_RANK *pCanonRank, + sp_ATOM *at, + int bIsotopic, + AT_STEREO_CARB *LinearCTStereoCarbInv, + AT_STEREO_DBLE *LinearCTStereoDbleInv, + AT_NUMB *pCanonOrdInv, + AT_RANK *pCanonRankInv ); + +int GetHillFormulaIndexLength( int count ); + +int MarkAmbiguousStereo( sp_ATOM *at, + inp_ATOM *norm_at, + int bIsotopic, + AT_NUMB *pCanonOrd, + AT_STEREO_CARB *LinearCTStereoCarb, int nLenLinearCTStereoCarb, + AT_STEREO_DBLE *LinearCTStereoDble, int nLenLinearCTStereoDble ); + +INCHI_MODE UnmarkAllUndefinedUnknownStereo( INChI_Stereo *Stereo, INCHI_MODE nUserMode ); + +int CleanCoord( MOL_COORD szCoord, int delim ); + + + +/* Print single-component Hill formula whole structure output. */ +int MakeHillFormulaString( char *szHillFormula, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow) +{ + int nUsedLength0 = strbuf->nUsedLength; + if ( szHillFormula && !*bOverflow ) + { + if ( -1==inchi_strbuf_printf( strbuf, "%s", szHillFormula ) ) + { + *bOverflow |= 1; + return nUsedLength0+1; + } + } + return ( strbuf->nUsedLength - nUsedLength0 ); +} + + +/* + MS Windows dependent: sprintf() is supposed to return the length + of the output string. + Carbon atoms are always first. + Bridging hydrogen atoms are always last. +*/ +int GetHillFormulaIndexLength( int count ) +{ +char szCount[16]; + if ( count > 1 ) + return sprintf( szCount, "%d", count ); + return 0; +} + + +/* + Estimate dimensions of Hill formula for single component +*/ +int GetHillFormulaCounts( U_CHAR *nAtom, S_CHAR *nNum_H, int num_atoms, + AT_NUMB *nTautomer, int lenTautomer, + int *pnum_C, int *pnum_H, + int *pnLen, int *pnNumNonHAtoms ) +{ +char szElement[4]; +U_CHAR nPrevAtom = (U_CHAR)-2; +int bCarbon, bHydrogen, nElemLen, nFormLen, nNumNonHAtoms; +int mult, i, num_H, num_C; + + num_H = 0; + num_C = 0; + bCarbon = 0; + bHydrogen = 0; + nElemLen = 0; + nFormLen = 0; + mult = 0; + nNumNonHAtoms = num_atoms; + + for ( i = 0; i < num_atoms; i ++ ) + { + + if ( nPrevAtom != nAtom[i] ) + { + if ( mult ) + { + if ( bHydrogen ) + { + num_H += mult; + } + else if ( bCarbon ) + { + num_C += mult; + } + else + { + nFormLen += nElemLen; + nFormLen += GetHillFormulaIndexLength( mult ); + } + } + + if ( -1==get_element_chemical_symbol((int)nAtom[i], szElement ) ) + { + return -1; + } + mult = 1; + + nElemLen = (int) strlen(szElement); + nPrevAtom = nAtom[i]; + bCarbon = !strcmp( szElement, "C" ); + bHydrogen = !strcmp( szElement, "H" ); + if ( bHydrogen ) + { + nNumNonHAtoms = i; + } + } + else + { + mult ++; + } + + num_H += nNum_H[i]; + } + + /* NumGroups; ((NumAt+1, NumH, At1..AtNumAt),...) */ + + if ( nTautomer && lenTautomer > 0 ) + { + int num_groups = nTautomer[0]; + + for ( i = 1; i < lenTautomer && num_groups > 0; i += nTautomer[i]+1, num_groups -- ) + { + num_H += nTautomer[i+1]; + } + } + + if ( mult ) + { + if ( bHydrogen ) + { + num_H += mult; + } + else if ( bCarbon ) + { + num_C += mult; + } + else + { + nFormLen += nElemLen; + nFormLen += GetHillFormulaIndexLength( mult ); + } + } + + if ( num_C ) + { + nFormLen += (int) strlen( "C" ); + nFormLen += GetHillFormulaIndexLength( num_C ); + } + + if ( num_H ) + { + nFormLen += (int) strlen( "H" ); + nFormLen += GetHillFormulaIndexLength( num_H ); + } + + *pnum_C = num_C; + *pnum_H = num_H; + *pnLen = nFormLen; + *pnNumNonHAtoms = nNumNonHAtoms; + + return 0; +} + + +/* Add a portion to Hill formula for single component. */ +int AddElementAndCount( const char *szElement, int mult, char *szLinearCT, int nLenLinearCT, int *bOverflow ) +{ + char szMult[16]; + int len1, len2; + if ( mult > 0 && !*bOverflow && 0 < (len1 = strlen( szElement )) ) { + if ( mult > 1 ) { + len2 = sprintf( szMult, "%d", mult ); + } else { + len2 = 0; + szMult[0] = '\0'; + } + if ( len1 + len2 < nLenLinearCT ) { + memcpy( szLinearCT, szElement, len1 ); + memcpy( szLinearCT+len1, szMult, len2+1 ); /* adding zero termination */ + return len1+len2; + } else { + (*bOverflow) ++; + } + } + return 0; +} + + +/* + Make Hill formula for single component. + + If num_C > 0 then nAtom does not contain C or H + otherwise all elements are in alphabetic order +*/ +int MakeHillFormula( U_CHAR *nAtom, + int num_atoms, + char *szLinearCT, + int nLen_szLinearCT, + int num_C, + int num_H, + int *bOverflow ) +{ + char szElement[4]; + int mult, compare2H; + int i, nLen, bOvfl; + U_CHAR nPrevAtom; + + nLen = 0; + mult = 0; + bOvfl = 0; + nPrevAtom = (U_CHAR)-2; /* non-existent number */ + + if ( num_C ) { + nLen += AddElementAndCount( "C", num_C, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl ); + if ( num_H ) { + nLen += AddElementAndCount( "H", num_H, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl ); + num_H = 0; + } + } + + for ( i = 0; i < num_atoms; i ++ ) { + + if ( nPrevAtom != nAtom[i] ) { + if ( mult ) { + nLen += AddElementAndCount( szElement, mult, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl ); + } + mult = 1; + if ( -1==get_element_chemical_symbol((int)nAtom[i], szElement ) ) { + return -1; /* wrong element */ + } + nPrevAtom = nAtom[i]; + if ( !strcmp( "C", szElement ) ) { + return -1; + } + compare2H = strcmp( "H", szElement ); + if ( !compare2H ) { + return -1; + } + if ( compare2H < 0 && num_H ) { + /* H-atom should be located in front of szElement */ + nLen += AddElementAndCount( "H", num_H, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl ); + num_H = 0; + } + } else { + mult ++; + } + } + if ( mult ) { + /* the last element if any */ + nLen += AddElementAndCount( szElement, mult, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl ); + } + if ( num_H ) { + /* if H has not been output... */ + nLen += AddElementAndCount( "H", num_H, szLinearCT+nLen, nLen_szLinearCT-nLen, &bOvfl ); + } + *bOverflow |= (0 != bOvfl); + return bOvfl? nLen_szLinearCT+1: nLen; +} + + +/* + Allocate space and produce Hill formula for single component +*/ +char *AllocateAndFillHillFormula( INChI *pINChI ) +{ +int num_C, num_H, nLen, nNumNonHAtoms, ret, bOverflow; +char *pHillFormula = NULL; + + bOverflow = 0; + + if ( !GetHillFormulaCounts( pINChI->nAtom, + pINChI->nNum_H, + pINChI->nNumberOfAtoms, + pINChI->nTautomer, + pINChI->lenTautomer, + &num_C, &num_H, + &nLen, + &nNumNonHAtoms ) ) + { + + pHillFormula = (char*) inchi_malloc( nLen+1 ); + + if ( pHillFormula ) + { + + ret = MakeHillFormula( pINChI->nAtom+num_C, + nNumNonHAtoms-num_C, + pHillFormula, + nLen+1, + num_C, num_H, + &bOverflow ); + + if ( ret != nLen || bOverflow ) + { + inchi_free( pHillFormula ); + pHillFormula = NULL; + } + } + } + + return pHillFormula; +} + + +/************************************************************************************/ +/* Return value: 0 => copied to stereo bonds; 1=> Allene copied to stereocenters */ +/* on input nNumberOfStereoBonds==NULL means second call, use Stereo->...Inv */ +/************************************************************************************/ +int Copy2StereoBondOrAllene( INChI_Stereo *Stereo, + int *nNumberOfStereoCenters, + int *nNumberOfStereoBonds, + AT_STEREO_DBLE *LinearCTStereoDble, + AT_NUMB *pCanonOrd, AT_RANK *pCanonRank, + sp_ATOM *at, + int bIsotopic ) +{ +int cumulene_len, + j, + next_j=0 /* ordering number of the central allene atom */, + next_neigh; +AT_RANK + at_num; +int parity; + + if ( pCanonOrd && pCanonRank ) + { + j = pCanonOrd[(int)LinearCTStereoDble->at_num1-1]; + + /* if allene then find the central atom, at[next_j] */ + + if ( bIsotopic ) + { + cumulene_len = BOND_CHAIN_LEN(at[j].stereo_bond_parity2[0]); + + if ( cumulene_len % 2 && (1 >= MAX_NUM_STEREO_BONDS || !at[j].stereo_bond_neighbor2[1]) ) + { + next_j = at[j].neighbor[(int)at[j].stereo_bond_ord2[0]]; + + for ( cumulene_len = (cumulene_len-1)/2; cumulene_len && 2==at[next_j].valence; cumulene_len -- ) + { + next_neigh = (j == at[next_j].neighbor[0]); + j = next_j; + next_j = at[next_j].neighbor[next_neigh]; + } + /* next_j is the central atom */ + } + else + { + cumulene_len = -1; /* not an allene */ + } + } + else + { + cumulene_len = BOND_CHAIN_LEN(at[j].stereo_bond_parity[0]); + + if ( cumulene_len % 2 && (1 >= MAX_NUM_STEREO_BONDS || !at[j].stereo_bond_neighbor[1]) ) + { + next_j = at[j].neighbor[(int)at[j].stereo_bond_ord[0]]; + + for ( cumulene_len = (cumulene_len-1)/2; cumulene_len && 2==at[next_j].valence; cumulene_len -- ) + { + next_neigh = (j == at[next_j].neighbor[0]); + j = next_j; + next_j = at[next_j].neighbor[next_neigh]; + } + } + else + { + cumulene_len = -1; /* not an allene */ + } + } + + if ( !cumulene_len ) + { + /* allene has been found; insert new stereocenter and parity */ + + AT_NUMB *nNumber; + S_CHAR *t_parity; + + nNumber = nNumberOfStereoBonds? Stereo->nNumber : Stereo->nNumberInv; + t_parity = nNumberOfStereoBonds? Stereo->t_parity : Stereo->t_parityInv; + + at_num = pCanonRank[next_j]; + parity = LinearCTStereoDble->parity; + + /* free room for the new stereocenter */ + for ( j = 0; j < *nNumberOfStereoCenters && Stereo->nNumber[j] < at_num; j ++ ) + ; + + if ( j < *nNumberOfStereoCenters ) + { + memmove( nNumber + j + 1, nNumber + j, (*nNumberOfStereoCenters-j)*sizeof(nNumber[0]) ); + memmove( t_parity + j + 1, t_parity + j, (*nNumberOfStereoCenters-j)*sizeof(t_parity[0]) ); + } + + /* fill the new stereo center info */ + + nNumber[j] = at_num; + t_parity[j] = parity; + (*nNumberOfStereoCenters) ++; + return 1; + } + } + + + /* Save the stereo bond info */ + + if ( nNumberOfStereoBonds ) + { + j = *nNumberOfStereoBonds; + Stereo->b_parity[j] = LinearCTStereoDble->parity; + Stereo->nBondAtom1[j] = LinearCTStereoDble->at_num1; + Stereo->nBondAtom2[j] = LinearCTStereoDble->at_num2; + (*nNumberOfStereoBonds) ++; + } + + return 0; +} + + +/***************************************************************************/ +int CopyLinearCTStereoToINChIStereo( INChI_Stereo *Stereo, + AT_STEREO_CARB *LinearCTStereoCarb, int nLenLinearCTStereoCarb, + AT_STEREO_DBLE *LinearCTStereoDble, int nLenLinearCTStereoDble, + AT_NUMB *pCanonOrd, AT_RANK *pCanonRank, + sp_ATOM *at, + int bIsotopic, + AT_STEREO_CARB *LinearCTStereoCarbInv, + AT_STEREO_DBLE *LinearCTStereoDbleInv, + AT_NUMB *pCanonOrdInv, AT_RANK *pCanonRankInv ) +{ +int n, i, nErrorCode = 0, len; +int bAllene; +int diff; +int lenInv, bAlleneInv; + + + /* Stereo centers */ + + n = Stereo->nNumberOfStereoCenters = nLenLinearCTStereoCarb; + + for ( i = 0; i < n; i ++ ) + { + Stereo->nNumber[i] = LinearCTStereoCarb[i].at_num; + Stereo->t_parity[i] = LinearCTStereoCarb[i].parity; + Stereo->nNumberInv[i] = LinearCTStereoCarbInv[i].at_num; + Stereo->t_parityInv[i] = LinearCTStereoCarbInv[i].parity; + } + + + /* Stereo bonds */ + + n = nLenLinearCTStereoDble; + lenInv = Stereo->nNumberOfStereoCenters; + + for ( i = len = 0; i < n; i ++ ) + { + bAllene = + Copy2StereoBondOrAllene( Stereo, + &Stereo->nNumberOfStereoCenters, + &len, + LinearCTStereoDble+i, + pCanonOrd, pCanonRank, + at, + bIsotopic ); + + bAlleneInv = + Copy2StereoBondOrAllene( Stereo, + &lenInv, + NULL, + LinearCTStereoDbleInv+i, + pCanonOrdInv, pCanonRankInv, + at, + bIsotopic ); + + /* make sure double bond stereo is identical in original and inverted geometry */ + /* Note: all allenes are AFTER double bonds in LinearCTStereoDble... */ + if ( bAllene != bAlleneInv || !bAllene && + CompareLinCtStereoDble ( LinearCTStereoDble+i, 1, + LinearCTStereoDbleInv+i, 1 ) ) + { + /* double bond stereo Inv is NOT identical to Abs */ + nErrorCode = -4; + goto exit_function; + } + } + + Stereo->nNumberOfStereoBonds = len; + + if ( lenInv != Stereo->nNumberOfStereoCenters ) + { + nErrorCode = -5; /* different number of stereo centers in Abs and Inv */ + goto exit_function; + } + + + /* compare inverted stereocenters to absolute */ + + n = Stereo->nNumberOfStereoCenters; + diff = 0; + + for ( i = 0, diff = 0; i < n; i ++ ) + { + if ( Stereo->nNumberInv[i] != Stereo->nNumber[i] ) + { + diff = (Stereo->nNumberInv[i] > Stereo->nNumber[i])? 2 : -2; + break; /* Abs != Inv */ + } + if ( Stereo->t_parityInv[i] != Stereo->t_parity[i] ) { + diff = (Stereo->t_parityInv[i] > Stereo->t_parity[i])? 1 : -1; + break; /* Abs != Inv */ + } + } + + Stereo->nCompInv2Abs = + (diff > 0) ? 1 + : (diff < 0) ? -1 + : 0; + + if ( diff == -1 || diff == 1 ) + { + /* the first found difference was in parities */ + + for ( i = 0, diff = 0; i < n; i ++ ) + { + if ( Stereo->nNumberInv[i] != Stereo->nNumber[i] ) + { + diff = 2; /* difference in stereo center numbering */ + break; + } + + /* parities can be only 1, 2, 3, 4. Therefore only mutually inverted pairs + * (t_parityInv, t_parity) = (1,2) or (2,1) statisfy conditions + * (t_parityInv != t_parity) && (t_parityInv + t_parity == 3) + */ + + if ( Stereo->t_parityInv[i] == Stereo->t_parity[i] || + Stereo->t_parityInv[i] + Stereo->t_parity[i] != 3 ) + { + diff = 1; /* parities are same or different and cannot be obtained by simple inversion */ + break; + } + } + Stereo->bTrivialInv = !diff; + } + else + { + Stereo->bTrivialInv = 0; + } + + +exit_function: + return nErrorCode; +} + + +/***************************************************************************/ +int MarkAmbiguousStereo( sp_ATOM *at, + inp_ATOM *norm_at, + int bIsotopic, + AT_NUMB *pCanonOrd, + AT_STEREO_CARB *LinearCTStereoCarb, int nLenLinearCTStereoCarb, + AT_STEREO_DBLE *LinearCTStereoDble, int nLenLinearCTStereoDble ) +{ +int n, i, j1, j2, num, mark_atom, mark_bond; + + if ( !pCanonOrd ) + return -1; + + num = 0; + + n = nLenLinearCTStereoCarb; + + mark_atom = bIsotopic ? AMBIGUOUS_STEREO_ATOM_ISO + : AMBIGUOUS_STEREO_ATOM; + + for ( i = 0; i < n; i ++ ) + { + /* mark ambiguous stereo centers (for displaying and "Ambiguous stereo" message) */ + if ( ATOM_PARITY_NOT_UNKN(LinearCTStereoCarb[i].parity) && + at[j1=pCanonOrd[(int)LinearCTStereoCarb[i].at_num-1]].bAmbiguousStereo ) + { + at[j1].bAmbiguousStereo |= mark_atom; + norm_at[j1].bAmbiguousStereo |= mark_atom; + num ++; + } + } + + n = nLenLinearCTStereoDble; + + mark_bond = bIsotopic ? AMBIGUOUS_STEREO_BOND_ISO + : AMBIGUOUS_STEREO_BOND; + + for ( i = 0; i < n; i ++ ) + { + /* mark ambiguous stereo bonds or allenes (for displaying and "Ambiguous stereo" message) */ + + if ( ATOM_PARITY_WELL_DEF(LinearCTStereoDble[i].parity) ) + { + j1=pCanonOrd[(int)LinearCTStereoDble[i].at_num1-1]; + j2=pCanonOrd[(int)LinearCTStereoDble[i].at_num2-1]; + + if ( at[j1].bAmbiguousStereo || at[j2].bAmbiguousStereo ) + { + /* if it is an allene then mark the central atom only + because the bonds should not be marked to avoid misleading + message "Ambiguous stereo: bond(s)": Allene makes a stereocenter + */ + + int j1_parity = bIsotopic ? at[j1].stereo_bond_parity2[0] + : at[j1].stereo_bond_parity[0]; + + int cumulene_len = BOND_CHAIN_LEN(j1_parity); /* 0 => double bond, 1 => allene, 2 => cumulene,..*/ + + if ( cumulene_len % 2 && (1 >= MAX_NUM_STEREO_BONDS || + ! ( bIsotopic ? at[j1].stereo_bond_neighbor2[1] : at[j1].stereo_bond_neighbor[1] ) ) + ) + { + /* found an allene; locate its central atom */ + + int next_j, next_neigh; + int j = j1; + + next_j = at[j].neighbor[bIsotopic ? at[j].stereo_bond_ord2[0] + : at[j].stereo_bond_ord[0] ]; + + for ( cumulene_len = (cumulene_len-1)/2; + cumulene_len && 2==at[next_j].valence; + cumulene_len -- ) + { + next_neigh = ( j==at[next_j].neighbor[0]); + j = next_j; + next_j = at[next_j].neighbor[next_neigh]; + } + /* next_j is the central atom */ + if ( 2==at[next_j].valence ) + { + at[next_j].bAmbiguousStereo |= mark_atom; + norm_at[next_j].bAmbiguousStereo |= mark_atom; + num ++; + continue; /* do not mark the cumulene "bond" endpoints */ + } + } + + /* not an allene, mark double bond or cumulene end atoms */ + if ( at[j1].bAmbiguousStereo ) + { + at[j1].bAmbiguousStereo |= mark_bond; /* ??? */ + norm_at[j1].bAmbiguousStereo |= mark_bond; + num ++; + } + + if ( at[j2].bAmbiguousStereo ) + { + at[j2].bAmbiguousStereo |= mark_bond; /* ??? */ + norm_at[j2].bAmbiguousStereo |= mark_bond; + num ++; + } + } + } + } + + return num; +} + + +/**********************************************************************************************/ +INCHI_MODE UnmarkAllUndefinedUnknownStereo( INChI_Stereo *Stereo, INCHI_MODE nUserMode ) +{ +INCHI_MODE nRet = 0; +int i, n; + + if ( !Stereo || Stereo && !Stereo->nNumberOfStereoCenters && !Stereo->nNumberOfStereoBonds) + return nRet; + + /* Stereocenters */ + if ( !Stereo->nCompInv2Abs && + (n=Stereo->nNumberOfStereoCenters) > 0 && (nUserMode & REQ_MODE_SC_IGN_ALL_UU) ) + { + + for ( i = 0; i < n && !ATOM_PARITY_WELL_DEF(Stereo->t_parity[i]); i ++ ) + ; + + if ( i == n ) + { + Stereo->nNumberOfStereoCenters = 0; + + for ( i = 0; i < n; i ++ ) + { + Stereo->t_parity[i] = 0; + Stereo->nNumber[i] = 0; + Stereo->t_parityInv[i] = 0; + Stereo->nNumberInv[i] = 0; + } + + nRet |= REQ_MODE_SC_IGN_ALL_UU; + } + } + + /* Stereobonds */ + + if ( (n=Stereo->nNumberOfStereoBonds) > 0 && (nUserMode & REQ_MODE_SB_IGN_ALL_UU) ) + { + for ( i = 0; i < n && !ATOM_PARITY_WELL_DEF(Stereo->b_parity[i]); i ++ ) + ; + + if ( i == n ) + { + Stereo->nNumberOfStereoBonds = 0; + for ( i = 0; i < n; i ++ ) + { + Stereo->b_parity[i] = 0; + Stereo->nBondAtom1[i] = 0; + Stereo->nBondAtom2[i] = 0; + } + nRet |= REQ_MODE_SB_IGN_ALL_UU; + } + } + + return nRet; +} + + + +#if ( defined(TARGET_API_LIB) ) + + + +/**********************************************************************************************/ +void WriteCoord( char *str, double x ) +{ + if ( x < -9999999.9 ) { + sprintf( str, "%10.2e", x ); + } else + if ( x < -999999.99 ) { + sprintf( str, "%10.2f", x ); + } else + if ( x < -99999.999 ) { + sprintf( str, "%10.3f", x ); + } else + if ( x < 99999.9999 ) { + sprintf( str, "%10.4f", x ); + } else + if ( x < 999999.999 ) { + sprintf( str, "%10.3f", x ); + } else + if ( x < 9999999.99 ) { + sprintf( str, "%10.2f", x ); + } else + if ( x < 99999999.9 ) { + sprintf( str, "%10.1f", x ); + } else { + sprintf( str, "%10.3e", x ); + } +} +#endif + + + +/* used CANON_STAT members + + pCS->LinearCT + pCS->LinearCTIsotopic + pCS->LinearCTIsotopicStereoCarb + pCS->LinearCTIsotopicStereoCarbInv + pCS->LinearCTIsotopicStereoDble + pCS->LinearCTIsotopicStereoDbleInv + pCS->LinearCTIsotopicTautomer + pCS->LinearCTStereoCarb + pCS->LinearCTStereoCarbInv + pCS->LinearCTStereoDble + pCS->LinearCTStereoDbleInv + pCS->nCanonOrd + pCS->nCanonOrdIsotopic + pCS->nCanonOrdIsotopicStereo + pCS->nCanonOrdIsotopicStereoInv + pCS->nCanonOrdIsotopicStereoTaut + pCS->nCanonOrdIsotopicTaut + pCS->nCanonOrdStereo + pCS->nCanonOrdStereoInv + pCS->nCanonOrdStereoTaut + pCS->nCanonOrdTaut + pCS->nLenCanonOrd + pCS->nLenCanonOrdIsotopic + pCS->nLenCanonOrdIsotopicStereo + pCS->nLenCanonOrdIsotopicStereoTaut + pCS->nLenCanonOrdIsotopicTaut + pCS->nLenCanonOrdStereo + pCS->nLenCanonOrdStereoTaut + pCS->nLenCanonOrdTaut + pCS->nLenLinearCTAtOnly + pCS->nLenLinearCTIsotopic + pCS->nLenLinearCTIsotopicStereoCarb + pCS->nLenLinearCTIsotopicStereoDble + pCS->nLenLinearCTIsotopicTautomer + pCS->nLenLinearCTStereoCarb + pCS->nLenLinearCTStereoDble + pCS->nNum_H + pCS->nNum_H_fixed + pCS->nSymmRank + pCS->nSymmRankIsotopic + pCS->nSymmRankIsotopicTaut + pCS->nSymmRankTaut + pCS->t_group_info + pCS->t_group_info->num_t_groups + +*/ + + +/* + Serialization: one-component InChI +*/ +int FillOutINChI( INChI *pINChI, + INChI_Aux *pINChI_Aux, + int num_atoms, + int num_at_tg, + int num_removed_H, + sp_ATOM *at, + inp_ATOM *norm_at, + CANON_STAT *pCS, + CANON_GLOBALS *pCG, + int bTautomeric, + INCHI_MODE nUserMode, + char *pStrErrStruct ) +{ +int i, j, m, n, g, len, ii, ret=0; + +AT_NUMB *pSymmRank, *pOrigNosInCanonOrd, *pConstitEquNumb, *pCanonOrd=NULL, *pCanonOrdInv=NULL, *pCanonOrdTaut; +T_GROUP_INFO *t_group_info = pCS->t_group_info; +T_GROUP *t_group; + +int nErrorCode = 0; +AT_NUMB *pCanonRank, *pCanonRankInv; /* canonical ranks of the atoms or tautomeric groups */ +AT_NUMB *pCanonRankAtoms=NULL, *pSortOrd = NULL; +AT_RANK nMinOrd; + +INChI_Stereo *Stereo; +int bUseNumberingInv = 0, bUseIsotopicNumberingInv = 0; + +INCHI_MODE nStereoUnmarkMode; + +/*AT_NUMB *pCanonOrdNonIso = NULL, *pCanonOrdIso = NULL;*/ +/*AT_NUMB *nOrigAtNosInCanonOrdNonIso = NULL, *nOrigAtNosInCanonOrdIso = NULL;*/ + + + /* Check for warnings */ + + if ( pCS->nLenLinearCTStereoCarb < 0 || pCS->nLenLinearCTStereoDble < 0 || + pCS->nLenCanonOrdStereo < 0 || pCS->nLenCanonOrdStereoTaut < 0) + { + nErrorCode |= WARN_FAILED_STEREO; + } + + if ( pCS->nLenLinearCTIsotopic < 0 || pCS->nLenLinearCTIsotopicTautomer < 0 || + pCS->nLenCanonOrdIsotopic < 0 || pCS->nLenCanonOrdIsotopicTaut < 0 ) + { + nErrorCode |= WARN_FAILED_ISOTOPIC; + } + + if ( pCS->nLenLinearCTIsotopicStereoCarb < 0 || pCS->nLenLinearCTIsotopicStereoDble < 0 || + pCS->nLenCanonOrdIsotopicStereo < 0 || pCS->nLenCanonOrdIsotopicStereoTaut < 0) + { + nErrorCode |= WARN_FAILED_ISOTOPIC_STEREO; + } + + pCanonRankAtoms = (AT_NUMB *)inchi_calloc( num_at_tg+1, sizeof(pCanonRankAtoms[0]) ); + + pSortOrd = (AT_NUMB *)inchi_calloc( num_at_tg+1, sizeof(pSortOrd[0]) ); /* must have more than num_atoms */ + + if ( !pCanonRankAtoms || !pSortOrd ) + { + nErrorCode = 0; + ret = CT_OUT_OF_RAM; /* */ + pINChI->nErrorCode = pINChI_Aux->nErrorCode = CT_OUT_OF_RAM; + goto exit_function; + } + + + /* Total charge */ + for ( i = 0, n = 0; i < num_atoms+num_removed_H; i ++ ) + { + n += at[i].charge; + } + pINChI->nTotalCharge = n; + + + /* Number of atoms */ + + pINChI->nNumberOfAtoms = num_atoms; + pINChI_Aux->nNumberOfAtoms = num_atoms; + + + /* Removed protons and detachable isotopic H */ + if ( bTautomeric && t_group_info ) + { + + pINChI_Aux->nNumRemovedProtons = t_group_info->tni.nNumRemovedProtons; + + for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) + { + pINChI_Aux->nNumRemovedIsotopicH[i] = t_group_info->num_iso_H[i] + + t_group_info->tni.nNumRemovedProtonsIsotopic[i]; + } + + if ( pINChI_Aux->bNormalizationFlags & FLAG_FORCE_SALT_TAUT ) + { + pINChI->nFlags |= INCHI_FLAG_HARD_ADD_REM_PROTON; + } + + if ( pINChI_Aux->bNormalizationFlags & (FLAG_NORM_CONSIDER_TAUT &~FLAG_PROTON_CHARGE_CANCEL) ) + { + WarningMessage(pStrErrStruct, "Proton(s) added/removed"); + } + + if ( pINChI_Aux->bNormalizationFlags & FLAG_PROTON_CHARGE_CANCEL ) + { + WarningMessage(pStrErrStruct, "Charges neutralized"); + } + } + + + /* Abs or rel stereo may establish one of two canonical numberings */ + + if ( (pCS->nLenLinearCTStereoCarb > 0 || pCS->nLenLinearCTStereoDble > 0) && + pCS->nLenCanonOrdStereo > 0 && + (pCS->LinearCTStereoCarb && pCS->LinearCTStereoCarbInv || + pCS->LinearCTStereoDble && pCS->LinearCTStereoDbleInv) && + pCS->nCanonOrdStereo && pCS->nCanonOrdStereoInv + ) + { + + pCanonRank = pCanonRankAtoms; + pCanonOrd = pCS->nCanonOrdStereo; + pCanonRankInv = pSortOrd; + pCanonOrdInv = pCS->nCanonOrdStereoInv; + Stereo = pINChI->Stereo; + + for ( i = 0; i < num_at_tg; i ++ ) + { + pCanonRankInv[pCanonOrdInv[i]] = + pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); + } + + /********************************************************************/ + /* copy stereo bonds and stereo centers; compare Inv and Abs stereo */ + /********************************************************************/ + + nErrorCode = CopyLinearCTStereoToINChIStereo( Stereo, + pCS->LinearCTStereoCarb, pCS->nLenLinearCTStereoCarb, + pCS->LinearCTStereoDble, pCS->nLenLinearCTStereoDble + , pCanonOrd, pCanonRank, at, 0 /* non-isotopic */ + , pCS->LinearCTStereoCarbInv + , pCS->LinearCTStereoDbleInv + , pCanonOrdInv, pCanonRankInv ); + + if ( Stereo->t_parityInv && Stereo->nNumberInv ) + { + if ( nUserMode & REQ_MODE_RELATIVE_STEREO ) + { + pINChI->nFlags |= INCHI_FLAG_REL_STEREO; + } + if ( nUserMode & REQ_MODE_RACEMIC_STEREO ) + { + pINChI->nFlags |= INCHI_FLAG_RAC_STEREO; + } + if ( Stereo->nCompInv2Abs ) + { + if ( Stereo->nCompInv2Abs == -1 ) + { + /* switch pointers in Stereo so that the stereo becomes the smallest (relative) */ + /* flag Stereo->nCompInv2Abs == -1 will keep track of this exchange */ + AT_NUMB *nNumberInv = Stereo->nNumberInv; + S_CHAR *t_parityInv = Stereo->t_parityInv; + Stereo->nNumberInv = Stereo->nNumber; + Stereo->t_parityInv = Stereo->t_parity; + Stereo->nNumber = nNumberInv; + Stereo->t_parity = t_parityInv; + /* switch pointers to set rel. stereo to pINChI_Aux->nOrigAtNosInCanonOrd + and inv. stereo to pINChI_Aux->nOrigAtNosInCanonOrdInv */ + switch_ptrs( &pCanonRank, &pCanonRankInv ); + switch_ptrs( &pCanonOrd, &pCanonOrdInv ); + bUseNumberingInv = 1; /* use inverted stereo numbering instead of normal */ + } + } + } + + for ( i = 0; i < num_atoms; i ++ ) + { + pINChI_Aux->nOrigAtNosInCanonOrdInv[i] = at[pCanonOrdInv[i]].orig_at_number; + pINChI_Aux->nOrigAtNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; + } + + if ( bUseNumberingInv ) + { + /* switch ptrs back to avoid confusion */ + switch_ptrs( &pCanonRank, &pCanonRankInv ); + switch_ptrs( &pCanonOrd, &pCanonOrdInv ); + /* save inverted stereo ranks & order because it represents the smallest (relative) */ + memcpy( pCanonRank, pCanonRankInv, num_at_tg * sizeof(pCanonRank[0]) ); + /* change pCS->nCanonOrdStereo[] to inverted: */ + memcpy( pCanonOrd, pCanonOrdInv, num_at_tg * sizeof(pCanonOrd[0]) ); + } + + pCanonRankInv = NULL; + pCanonOrdInv = NULL; + pOrigNosInCanonOrd = NULL; + } + else + { + /*------------------------------ no stereo */ + + pCanonOrd = pCS->nLenCanonOrdStereo > 0? pCS->nCanonOrdStereo : + pCS->nLenCanonOrd > 0? pCS->nCanonOrd : NULL; + pCanonRank = pCanonRankAtoms; + pOrigNosInCanonOrd = pINChI_Aux->nOrigAtNosInCanonOrd; + + if ( pCanonOrd && pCanonRank ) + { + for ( i = 0; i < num_atoms; i ++ ) + { + pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); + pOrigNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; + } + for ( ; i < num_at_tg; i ++ ) + { + pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); + } + } + } + /*pCanonOrdNonIso = pCanonOrd;*/ /* save for aux info */ + + + if ( pINChI_Aux->OrigInfo ) + { + /* charges, radicals, valences */ + + for ( i = 0; i < num_atoms; i ++ ) + { + ii = pCanonOrd[i]; + + if ( norm_at[ii].valence || norm_at[ii].num_H ) + { + pINChI_Aux->OrigInfo[i].cCharge = norm_at[ii].charge; + + pINChI_Aux->OrigInfo[i].cRadical = (norm_at[ii].radical==RADICAL_SINGLET)? 0 : + (norm_at[ii].radical==RADICAL_DOUBLET)? 1 : + (norm_at[ii].radical==RADICAL_TRIPLET)? 2 : + norm_at[ii].radical? 3 : 0 ; + + pINChI_Aux->OrigInfo[i].cUnusualValence = + get_unusual_el_valence( norm_at[ii].el_number, norm_at[ii].charge, norm_at[ii].radical, + norm_at[ii].chem_bonds_valence, norm_at[ii].num_H, norm_at[ii].valence ); + } + else + { + /* charge of a single atom component is in the INChI; valence = 0 is standard */ + pINChI_Aux->OrigInfo[i].cRadical = (norm_at[ii].radical==RADICAL_SINGLET)? 0 : + (norm_at[ii].radical==RADICAL_DOUBLET)? 1 : + (norm_at[ii].radical==RADICAL_TRIPLET)? 2 : + norm_at[ii].radical? 3 : 0 ; + } + } + } + + /* non-isotopic canonical numbers and equivalence of atoms (Aux) */ + + pConstitEquNumb = pINChI_Aux->nConstitEquNumbers; /* contitutional equivalence */ + pSymmRank = pCS->nSymmRank; + + if ( pCanonOrd && pCanonRank && pSymmRank && pConstitEquNumb ) + { + for ( i = 0; i < num_atoms; i ++ ) + { + pConstitEquNumb[i] = pSymmRank[pCanonOrd[i]]; /* constit. equ. ranks in order of canonical numbers */ + pSortOrd[i] = i; + } + for ( ; i < num_at_tg; i ++ ) + { + pSortOrd[i] = MAX_ATOMS; /* for debugging only */ + } + + pCG->m_pn_RankForSort = pConstitEquNumb; + inchi_qsort( pCG, pSortOrd, num_atoms, sizeof(pSortOrd[0]), CompRanksOrd ); + + for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= num_atoms; j ++ ) + { + if ( j == num_atoms || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) + { + nMinOrd ++; + if ( j - i > 1 ) + { + /* found a sequence of equivalent atoms: i..j-1 */ + while ( i < j ) + { + pConstitEquNumb[pSortOrd[i++]] = nMinOrd; /* = min. canon. rank in the group of equ. atoms */ + } + /* at this point j == i */ + } + else + { + pConstitEquNumb[pSortOrd[i++]] = 0; /* means the atom is not equivalent to any other */ + } + + nMinOrd = pSortOrd[j]; /* at the end j = num_atoms */ + } + } + } + + else + { + nErrorCode |= ERR_NO_CANON_RESULTS; + ret = -1; /* program error; no breakpoint here */ + goto exit_function; + } + + + /* atomic numbers from the Periodic Table */ + + for ( i = 0; i < num_atoms; i ++ ) + { + pINChI->nAtom[i] = (int)at[pCanonOrd[i]].el_number; + } + + + /* connection table: atoms only (before 7-29-2003 pCS->LinearCT2 contained non-isotopic CT) */ + + if ( pCS->nLenLinearCTAtOnly <= 0 || !pCS->LinearCT || !pINChI->nConnTable ) + { + nErrorCode |= ERR_NO_CANON_RESULTS; + ret = -2; + goto exit_function; + } + + memcpy( pINChI->nConnTable, pCS->LinearCT, sizeof(pINChI->nConnTable[0])*pCS->nLenLinearCTAtOnly); + + pINChI->lenConnTable = pCS->nLenLinearCTAtOnly; + + + /* tautomeric group(s) canonical representation */ + + len = 0; + if ( bTautomeric && 0 < (n = SortTautomerGroupsAndEndpoints( + pCG, t_group_info, + num_atoms, num_at_tg, pCanonRank )) ) + { + /* SortTautomerGroupsAndEndpoints() produces canonically ordered t-groups */ + + pINChI->nFlags |= + (t_group_info->bTautFlagsDone & TG_FLAG_ALL_SALT_DONE) ? INCHI_FLAG_ACID_TAUT + : 0; + + /* number of tautomeric groups */ + pINChI->nTautomer[len ++] = (AT_NUMB)n; + + /* store each tautomeric group, one by one */ + for ( i = 0; i < n; i ++ ) + { + g = (int)t_group_info->tGroupNumber[i]; /* original group numbers in sorted order */ + t_group = t_group_info->t_group + g; /* pointer to the tautomeric group */ + + /* NumAt+INCHI_T_NUM_MOVABLE (group length excluding this number) */ + + pINChI->nTautomer[len ++] = t_group->nNumEndpoints+INCHI_T_NUM_MOVABLE; + + /* Num(H), Num(-) */ + + for ( j = 0; j < INCHI_T_NUM_MOVABLE && j < T_NUM_NO_ISOTOPIC; j ++ ) + pINChI->nTautomer[len ++] = t_group->num[j]; + + for ( j = T_NUM_NO_ISOTOPIC; j < INCHI_T_NUM_MOVABLE; j ++ ) + pINChI->nTautomer[len ++] = 0; /* should not happen */ + + /* tautomeric group endpoint canonical numbers, pre-sorted in ascending order */ + + for ( j = (int)t_group->nFirstEndpointAtNoPos, + m = j + (int)t_group->nNumEndpoints; j < m; j ++ ) + { + pINChI->nTautomer[len ++] = pCanonRank[(int)t_group_info->nEndpointAtomNumber[j]]; /* At[j] */ + } + } + + pINChI->lenTautomer = len; + pINChI_Aux->nNumberOfTGroups = n; + } + else + { + pINChI->lenTautomer = 0; + pINChI_Aux->nNumberOfTGroups = 0; + + if ( t_group_info && ((t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) || + t_group_info->nNumIsotopicEndpoints>1 && + (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE))) + ) + { + /* only protons (re)moved or added */ + pINChI->lenTautomer = 1; + pINChI->nTautomer[0] = 0; + } + } + + + /* number of H (excluding tautomeric) */ + + if ( pCS->nNum_H ) + { + for ( i = 0; i < num_atoms; i ++ ) + { + pINChI->nNum_H[i] = pCS->nNum_H[i]; + } + } + + + /* number of fixed H (tautomeric H in non-tautomeric representation) */ + + if ( pCS->nNum_H_fixed && !pINChI->lenTautomer ) + { + for ( i = 0; i < num_atoms; i ++ ) + { + pINChI->nNum_H_fixed[i] = pCS->nNum_H_fixed[i]; + pINChI->nNum_H[i] += pCS->nNum_H_fixed[i]; + } + } + + + /*********************************************************** + * tautomeric group(s) numbering and symmetry; + * should not depend on switching to rel. stereo numbering + */ + + if ( pINChI->lenTautomer && (n=pINChI_Aux->nNumberOfTGroups) ) + { + pCanonOrdTaut = pCS->nLenCanonOrdStereoTaut > 0? pCS->nCanonOrdStereoTaut : + pCS->nLenCanonOrdTaut > 0? pCS->nCanonOrdTaut : NULL; + pConstitEquNumb = pINChI_Aux->nConstitEquTGroupNumbers; + pSymmRank = pCS->nSymmRankTaut; + + if ( pCanonOrdTaut && pSymmRank && pConstitEquNumb ) + { + for ( i = 0; i < n; i ++ ) + { + pConstitEquNumb[i] = pSymmRank[pCanonOrdTaut[i]]; + pSortOrd[i] = i; + } + + pCG->m_pn_RankForSort = pConstitEquNumb; + inchi_qsort( pCG, pSortOrd, n, sizeof(pSortOrd[0]), CompRanksOrd ); + for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= n; j ++ ) + { + + if ( j == n || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) + { + nMinOrd ++; /* make is start from 1, not from zero */ + + if ( j - i > 1 ) + { + /* found a sequence of more than one equivalent t-groups: i..j-1 */ + while ( i < j ) + { + pConstitEquNumb[pSortOrd[i++]] = nMinOrd; + } + } + else + { + pConstitEquNumb[pSortOrd[i++]] = 0; + } + nMinOrd = pSortOrd[j]; /* at the end j == n */ + } + } + } + } + + + /* Allocate and fill Hill formula */ + + pINChI->szHillFormula = AllocateAndFillHillFormula( pINChI ); + + if ( !pINChI->szHillFormula ) + { + nErrorCode = 0; + ret = CT_WRONG_FORMULA; /* CT_OUT_OF_RAM;*/ /* */ + pINChI->nErrorCode = pINChI_Aux->nErrorCode = ret; + goto exit_function; + } + + + nStereoUnmarkMode = UnmarkAllUndefinedUnknownStereo( pINChI->Stereo, nUserMode ); + + if ( nStereoUnmarkMode ) + { + pINChI->nFlags |= + (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU) ? INCHI_FLAG_SC_IGN_ALL_UU + : 0; + pINChI->nFlags |= + (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU) ? INCHI_FLAG_SB_IGN_ALL_UU + : 0; + + if ( (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU) || + (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU) ) { + WarningMessage(pStrErrStruct, "Omitted undefined stereo"); + } + } + + + /*************************/ + /* Mark ambiguous stereo */ + /*************************/ + + MarkAmbiguousStereo( at, norm_at, 0 /* non-isotopic */, pCanonOrd, + pCS->LinearCTStereoCarb, pCS->nLenLinearCTStereoCarb, + pCS->LinearCTStereoDble, pCS->nLenLinearCTStereoDble ); + + + + /************************************************************************ + * + * Isotopic part + */ + + /* abs or rel stereo may establish one of two canonical numberings */ + + if ( (pCS->nLenLinearCTIsotopicStereoCarb > 0 || pCS->nLenLinearCTIsotopicStereoDble > 0) && + pCS->nLenCanonOrdIsotopicStereo > 0 && + (pCS->LinearCTIsotopicStereoCarb && pCS->LinearCTIsotopicStereoCarbInv || + pCS->LinearCTIsotopicStereoDble && pCS->LinearCTIsotopicStereoDbleInv) && + pCS->nCanonOrdIsotopicStereo && pCS->nCanonOrdIsotopicStereoInv + ) + { + /* found isotopic stereo */ + + pCanonRank = pCanonRankAtoms; + pCanonOrd = pCS->nCanonOrdIsotopicStereo; + pCanonRankInv = pSortOrd; + pCanonOrdInv = pCS->nCanonOrdIsotopicStereoInv; + Stereo = pINChI->StereoIsotopic; + + for ( i = 0; i < num_at_tg; i ++ ) + { + pCanonRankInv[pCanonOrdInv[i]] = + pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); + } + + + /********************************************************************/ + /* copy stereo bonds and stereo centers; compare Inv and Abs stereo */ + /********************************************************************/ + + nErrorCode = CopyLinearCTStereoToINChIStereo( Stereo, + pCS->LinearCTIsotopicStereoCarb, + pCS->nLenLinearCTIsotopicStereoCarb, + pCS->LinearCTIsotopicStereoDble, + pCS->nLenLinearCTIsotopicStereoDble, + pCanonOrd, pCanonRank, at, 1 /* isotopic */, + pCS->LinearCTIsotopicStereoCarbInv, + pCS->LinearCTIsotopicStereoDbleInv, + pCanonOrdInv, pCanonRankInv ); + + if ( Stereo->t_parityInv && Stereo->nNumberInv ) + { + if ( nUserMode & REQ_MODE_RELATIVE_STEREO ) + { + pINChI->nFlags |= INCHI_FLAG_REL_STEREO; + } + + if ( nUserMode & REQ_MODE_RACEMIC_STEREO ) + { + pINChI->nFlags |= INCHI_FLAG_RAC_STEREO; + } + + if ( Stereo->nCompInv2Abs ) + { + if ( Stereo->nCompInv2Abs == -1 ) + { + /* switch pointers so that the stereo becomes the smallest (relative) */ + /* flag Stereo->nCompInv2Abs == -1 will keep track of this exchange */ + AT_NUMB *nNumberInv = Stereo->nNumberInv; + S_CHAR *t_parityInv = Stereo->t_parityInv; + Stereo->nNumberInv = Stereo->nNumber; + Stereo->t_parityInv = Stereo->t_parity; + Stereo->nNumber = nNumberInv; + Stereo->t_parity = t_parityInv; + switch_ptrs( &pCanonRank, &pCanonRankInv ); + switch_ptrs( &pCanonOrd, &pCanonOrdInv ); + bUseIsotopicNumberingInv = 1; + } + } + } + + for ( i = 0; i < num_atoms; i ++ ) + { + pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv[i] = at[pCanonOrdInv[i]].orig_at_number; + pINChI_Aux->nIsotopicOrigAtNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; + } + + if ( bUseIsotopicNumberingInv ) + { + switch_ptrs( &pCanonRank, &pCanonRankInv ); + switch_ptrs( &pCanonOrd, &pCanonOrdInv ); + memcpy( pCanonRank, pCanonRankInv, num_at_tg * sizeof(pCanonRank[0]) ); + memcpy( pCanonOrd, pCanonOrdInv, num_at_tg * sizeof(pCanonOrd[0]) ); + } + + pCanonRankInv = NULL; + pCanonOrdInv = NULL; + pOrigNosInCanonOrd = NULL; + } + else + { + /* no isotopic stereo */ + + pCanonOrd = pCS->nLenCanonOrdIsotopicStereo > 0 ? pCS->nCanonOrdIsotopicStereo + : pCS->nLenCanonOrdIsotopic > 0 ? pCS->nCanonOrdIsotopic + : NULL; + pCanonRank = pCanonRankAtoms; + pOrigNosInCanonOrd = pINChI_Aux->nIsotopicOrigAtNosInCanonOrd; + + if ( pCanonOrd && pCanonRank ) + { + for ( i = 0; i < num_atoms; i ++ ) + { + /* Fix13 -- out of bounds */ + pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); + pOrigNosInCanonOrd[i] = at[pCanonOrd[i]].orig_at_number; + } + for ( ; i < num_at_tg; i ++ ) + { + /* Fix13 -- out of bounds */ + pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); + } + } + } + /*pCanonOrdIso = pCanonOrd;*/ + + + pConstitEquNumb = pINChI_Aux->nConstitEquIsotopicNumbers; + pSymmRank = pCS->nSymmRankIsotopic; + + if ( pCanonOrd && pCanonRank && pConstitEquNumb && pSymmRank ) + { + for ( i = 0; i < num_atoms; i ++ ) + { + pConstitEquNumb[i] = pSymmRank[pCanonOrd[i]]; + pSortOrd[i] = i; + } + + for ( ; i < num_at_tg; i ++ ) + { + pSortOrd[i] = i; + } + + pCG->m_pn_RankForSort = pConstitEquNumb; + inchi_qsort( pCG, pSortOrd, num_atoms, sizeof(pSortOrd[0]), CompRanksOrd ); + + for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= num_atoms; j ++ ) + { + if ( j == num_atoms || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) + { + nMinOrd ++; + + if ( j - i > 1 ) + { + /* found a sequence of equivalent atoms: i..j-1 */ + while ( i < j ) + { + pConstitEquNumb[pSortOrd[i++]] = nMinOrd; + } + } + else + { + pConstitEquNumb[pSortOrd[i++]] = 0; /* nMinOrd; */ + } + nMinOrd = pSortOrd[j]; + } + } + } + else + { + goto exit_function; /* no isotopic info available */ + } + + + /* Isotopic atoms */ + + n = pINChI->nNumberOfIsotopicAtoms = pCS->nLenLinearCTIsotopic; + + for ( i = 0; i < n; i ++ ) + { + pINChI->IsotopicAtom[i].nAtomNumber = pCS->LinearCTIsotopic[i].at_num; + pINChI->IsotopicAtom[i].nIsoDifference = pCS->LinearCTIsotopic[i].iso_atw_diff; + pINChI->IsotopicAtom[i].nNum_H = pCS->LinearCTIsotopic[i].num_1H; + pINChI->IsotopicAtom[i].nNum_D = pCS->LinearCTIsotopic[i].num_D; + pINChI->IsotopicAtom[i].nNum_T = pCS->LinearCTIsotopic[i].num_T; + } + + + /* Isotopic tautomeric groups */ + + n = pINChI->nNumberOfIsotopicTGroups = pCS->nLenLinearCTIsotopicTautomer; + + for ( i = 0; i < n; i ++ ) + { + pINChI->IsotopicTGroup[i].nTGroupNumber = pCS->LinearCTIsotopicTautomer[i].tgroup_num; + pINChI->IsotopicTGroup[i].nNum_H = pCS->LinearCTIsotopicTautomer[i].num[2]; + pINChI->IsotopicTGroup[i].nNum_D = pCS->LinearCTIsotopicTautomer[i].num[1]; + pINChI->IsotopicTGroup[i].nNum_T = pCS->LinearCTIsotopicTautomer[i].num[0]; + } + + + /* Atoms that may exchange isotopic H-atoms */ + + if ( pCS->nExchgIsoH && pINChI->nPossibleLocationsOfIsotopicH ) + { + for ( i = 0, j = 1; i < num_atoms; i ++ ) + { + if ( pCS->nExchgIsoH[i] ) + { + pINChI->nPossibleLocationsOfIsotopicH[j++] = (AT_NUMB)(i+1); /* canonical number */ + } + } + pINChI->nPossibleLocationsOfIsotopicH[0] = (AT_NUMB)j; /* length including the 0th element */ + } + + if ( nStereoUnmarkMode = UnmarkAllUndefinedUnknownStereo( pINChI->StereoIsotopic, nUserMode ) ) + { + pINChI->nFlags |= + (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU) ? INCHI_FLAG_SC_IGN_ALL_ISO_UU + : 0; + pINChI->nFlags |= + (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU) ? INCHI_FLAG_SC_IGN_ALL_ISO_UU + : 0; + if ( (nStereoUnmarkMode & REQ_MODE_SC_IGN_ALL_UU) || + (nStereoUnmarkMode & REQ_MODE_SB_IGN_ALL_UU) ) + { + WarningMessage(pStrErrStruct, "Omitted undefined stereo"); + } + } + + + /* Mark ambiguous stereo */ + + MarkAmbiguousStereo( at, norm_at, 1 /* isotopic */, pCanonOrd, + pCS->LinearCTIsotopicStereoCarb, pCS->nLenLinearCTIsotopicStereoCarb, + pCS->LinearCTIsotopicStereoDble, pCS->nLenLinearCTIsotopicStereoDble ); + + + + /*********************************************************** + * Isotopic tautomeric group(s) numbering and symmetry; + * should not depend on switching to rel. stereo numbering + */ + + if ( pINChI->lenTautomer && + pINChI_Aux->nConstitEquIsotopicTGroupNumbers && + pCS->nSymmRankIsotopicTaut && + (pCS->nLenLinearCTIsotopic || pCS->nLenLinearCTIsotopicTautomer) && + t_group_info && t_group_info->num_t_groups > 0 ) + { + + n = t_group_info->num_t_groups; + + pCanonOrdTaut = + pCS->nLenCanonOrdIsotopicStereoTaut > 0 ? (n=pCS->nLenCanonOrdIsotopicStereoTaut, pCS->nCanonOrdIsotopicStereoTaut) + : pCS->nLenCanonOrdIsotopicTaut > 0 ? (n=pCS->nLenCanonOrdIsotopicTaut,pCS->nCanonOrdIsotopicTaut) + : (n=0,(AT_RANK*)NULL); + + pConstitEquNumb = pINChI_Aux->nConstitEquIsotopicTGroupNumbers; + + pSymmRank = pCS->nSymmRankIsotopicTaut; + + if ( pCanonOrdTaut && pSymmRank && pConstitEquNumb && n > 0 ) + { + for ( i = 0; i < n; i ++ ) + { + pConstitEquNumb[i] = pSymmRank[pCanonOrdTaut[i]]; + pSortOrd[i] = i; + } + + pCG->m_pn_RankForSort = pConstitEquNumb; + inchi_qsort( pCG, pSortOrd, n, sizeof(pSortOrd[0]), CompRanksOrd ); + for ( i = 0, nMinOrd = pSortOrd[0], j = 1; j <= n; j ++ ) + { + if ( j == n || pConstitEquNumb[pSortOrd[i]] != pConstitEquNumb[pSortOrd[j]] ) + { + nMinOrd ++; + if ( j - i > 1 ) + { + /* found a sequence of equivalent t-groups: i..j-1 */ + while ( i < j ) + { + pConstitEquNumb[pSortOrd[i++]] = nMinOrd; + } + } + else + { + pConstitEquNumb[pSortOrd[i++]] = 0; /* nMinOrd; */ + } + nMinOrd = pSortOrd[j]; /* at the end j = n */ + } + } + } + } + + +exit_function: + + if ( pCanonRankAtoms ) + inchi_free( pCanonRankAtoms ); + if ( pSortOrd ) + inchi_free( pSortOrd ); + + pINChI->nErrorCode |= nErrorCode; + pINChI_Aux->nErrorCode |= nErrorCode; + + return ret; +} diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichimake.c b/INCHI-1-SRC/INCHI_BASE/src/ichimake.c similarity index 82% rename from INCHI-1-SRC/INCHI_API/inchi_dll/ichimake.c rename to INCHI-1-SRC/INCHI_BASE/src/ichimake.c index 5135d2c..51d88ae 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichimake.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichimake.c @@ -1,4747 +1,4894 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include - - -#include "mode.h" - -#if ( TEST_RENUMB_ATOMS == 1 ) -#include "ichitime.h" -#endif -#include "inpdef.h" -#include "ichi.h" -#include "strutil.h" -#include "util.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "ichicant.h" -#include "ichicano.h" -#include "ichicomn.h" - -#include "ichicomp.h" -#include "ichimain.h" -#include "ichimake.h" -#include "ichister.h" -#include "ichi_io.h" - -int inp2spATOM( inp_ATOM *inp_at, int num_inp_at, sp_ATOM *at ); -int GetElementAndCount( const char **f, char *szEl, int *count ); -int CompareHillFormulas( const char *f1, const char *f2 ); -int CompareInchiStereo( INChI_Stereo *Stereo1, INCHI_MODE nFlags1, INChI_Stereo *Stereo2, INCHI_MODE nFlags2 ); -int CompareReversedStereoINChI( INChI_Stereo *s1/* InChI from reversed struct */, INChI_Stereo *s2 /* input InChI */); -int GetAtomOrdNbrInCanonOrd( inp_ATOM *norm_at, AT_NUMB *nAtomOrdNbr, - AT_NUMB *nOrigAtNosInCanonOrd, int num_at ); -int FillOutCanonInfAtom(inp_ATOM *norm_at, INF_ATOM_DATA *inf_norm_at_data, int init_num_at, int bIsotopic, - INChI *pINChI, INChI_Aux *pINChI_Aux, int bAbcNumbers, INCHI_MODE nMode); -int FillOutOneCanonInfAtom(inp_ATOM *inp_norm_at, INF_ATOM_DATA *inf_norm_at_data, - AT_NUMB *pStereoFlags, int init_num_at, int offset, int offset_H, int bIsotopic, - INChI *pINChI, INChI_Aux *pINChI_Aux, int bAbcNumbers, INCHI_MODE nMode); -int FillOutInputInfAtom(inp_ATOM *inp_at, INF_ATOM_DATA *inf_at_data, int init_num_at, int num_removed_H, - int bAdd_DT_to_num_H, int nNumRemovedProtons, NUM_H *nNumRemovedProtonsIsotopic, int bIsotopic, int bAbcNumbers); - -int CheckCanonNumberingCorrectness( - int num_atoms, int num_at_tg, - sp_ATOM *at, CANON_STAT *pCS, int bTautomeric, - char *pStrErrStruct ); - -static int CompareDfsDescendants4CT( const void *a1, const void *a2 ); -int GetSp3RelRacAbs( const INChI *pINChI, INChI_Stereo *Stereo ); - - -#if ( TEST_RENUMB_ATOMS == 1 || READ_INCHI_STRING == 1 ) /* { */ -int CompareStereoINChI( INChI_Stereo *s1, INChI_Stereo *s2 ); -#endif - -#if ( READ_INCHI_STRING == 1 ) /* { */ -/*************************************************************************************/ - -int CompareReversedStereoINChI2( INChI_Stereo *s1, INChI_Stereo *s2, ICR *picr); - -#endif -/**********************************************************************************************/ -int inp2spATOM( inp_ATOM *inp_at, int num_inp_at, sp_ATOM *at ) -{ - int i, j, val; - memset( at, 0, sizeof(at[0])*num_inp_at ); - for ( i = 0; i < num_inp_at; i ++ ) { - strncpy( at[i].elname, inp_at[i].elname, sizeof(at[0].elname) ); - at[i].el_number = (U_CHAR)get_periodic_table_number( at[i].elname ); - val = at[i].valence = inp_at[i].valence; - for ( j = 0; j < val; j ++ ) { - at[i].neighbor[j] = inp_at[i].neighbor[j]; - at[i].bond_type[j] = inp_at[i].bond_type[j]; - } - at[i].chem_bonds_valence = inp_at[i].chem_bonds_valence; - at[i].orig_at_number = inp_at[i].orig_at_number; - at[i].orig_compt_at_numb= inp_at[i].orig_compt_at_numb; - at[i].endpoint = inp_at[i].endpoint; - at[i].iso_atw_diff = inp_at[i].iso_atw_diff; - at[i].num_H = inp_at[i].num_H; - at[i].cFlags = inp_at[i].cFlags; - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { - at[i].num_iso_H[j] = inp_at[i].num_iso_H[j]; - } - at[i].charge = inp_at[i].charge; - at[i].radical = inp_at[i].radical; - -#if ( FIND_RING_SYSTEMS == 1 ) - at[i].nBlockSystem = inp_at[i].nBlockSystem; - at[i].bCutVertex = inp_at[i].bCutVertex; - at[i].nRingSystem = inp_at[i].nRingSystem; - at[i].nNumAtInRingSystem = inp_at[i].nNumAtInRingSystem; -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) - at[i].nDistanceFromTerminal = inp_at[i].nDistanceFromTerminal; -#endif -#endif - -/* - at[i].x = inp_at[i].x; - at[i].y = inp_at[i].y; - at[i].z = inp_at[i].z; -*/ - } - return 0; -} -/**********************************************************************************************/ -int GetElementAndCount( const char **f, char *szEl, int *count ) -{ - const char *p = *f; - char *q; - int i = 0; - if ( *p ) { - if ( isupper( UCINT *p ) ) { - szEl[i++] = *p++; - if ( *p && islower( UCINT *p ) ) { - szEl[i++] = *p++; - } - szEl[i] = '\0'; - if ( 1 == i && szEl[0] == 'C' ) { - szEl[0] = 'A'; /* less than any element: */ - /* carbon-containing compounds should be first */ - } - if ( *p && isdigit( UCINT *p ) ) { - *count = strtol( p, &q, 10 ); - p = q; - } else { - *count = 1; - } - *f = p; /* next element; */ - return 1; - } - return -1; /* not a chemical formula */ - } - strcpy( szEl, "Zz" ); /* zero termination 'element' is larger than any other element */ - *count = 9999; /* zero termination 'element count' is larger than any other count */ - return 0; -} -/********************************************************************************************** - - E1 < E2 if strcmp( E1, E2) < 0 OR E2 is empty and E1 is not - n1 < n2 if value n1 > n2 - - Sorting order: - - C10H22N - C10H22 - C2 - Ag2Cl2 - Ag2Cl - Ag2F2 - Ag2 - AgCl - AgF - F6S - F2S - -**********************************************************************************************/ -int CompareHillFormulas( const char *f1, const char *f2 ) -{ - char szEl1[4], szEl2[4]; - int count1, count2, ret1, ret2, ret; - - do { - ret1 = GetElementAndCount( &f1, szEl1, &count1 ); - ret2 = GetElementAndCount( &f2, szEl2, &count2 ); - if ( 0 <= ret1 && 0 <= ret2 ) { - if ( ret = strcmp( szEl1, szEl2 ) ) { - return ret; /* lexicographic order, string termination > any character */ - } - if ( ret = count2 - count1 ) { - return ret; /* inverse atom count order */ - } - } else { - return 0; /* program error */ - } - - } while ( 0 < ret1 && 0 < ret2 ); - - return 0; -} -/**********************************************************************************************/ -int CompareHillFormulasNoH( const char *f1, const char *f2, int *num_H1, int *num_H2 ) -{ - char szEl1[4], szEl2[4]; - int count1, count2, ret1, ret2, ret; - - do { - ret1 = GetElementAndCount( &f1, szEl1, &count1 ); - if ( 0 < ret1 && szEl1[0] == 'H' && !szEl1[1] ) { - *num_H1 += count1; - ret1 = GetElementAndCount( &f1, szEl1, &count1 ); - } - ret2 = GetElementAndCount( &f2, szEl2, &count2 ); - if ( 0 < ret2 && szEl2[0] == 'H' && !szEl2[1] ) { - *num_H2 += count2; - ret2 = GetElementAndCount( &f2, szEl2, &count2 ); - } - if ( 0 <= ret1 && 0 <= ret2 ) { - if ( ret = strcmp( szEl1, szEl2 ) ) { - return ret; /* lexicographic order, string termination > any character */ - } - if ( ret = count2 - count1 ) { - return ret; /* inverse atom count order */ - } - } else { - return 0; /* program error */ - } - - } while ( 0 < ret1 && 0 < ret2 ); - - return 0; -} - -/**************************************************************/ -int CompareTautNonIsoPartOfINChI( const INChI *i1, const INChI *i2 ) -{ - int len1, len2, ret, i; - - len1 = i1->lenTautomer > 0 && i1->nTautomer[0]? i1->lenTautomer:0; - len2 = i2->lenTautomer > 0 && i2->nTautomer[0]? i2->lenTautomer:0; - if ( ret = len2 - len1 ) { - return ret; - } - for ( i = 0; i < len1; i ++ ) { - if ( ret = (int)i2->nTautomer[i] - (int)i1->nTautomer[i] ) - return ret; - } - return 0; -} - -/**********************************************************************************************/ -/* sorting in descending order: return -1 if *p1 > *p2, return +1 if *p1 < *p2 */ -/**********************************************************************************************/ -int CompINChITautVsNonTaut(const INCHI_SORT *p1, const INCHI_SORT *p2, int bCompareIsotopic) -{ - int ret, num, i, num_H1, num_H2; - - const INChI *i1 = NULL; /* Mobile-H layers in Mobile-H sorting order */ - const INChI *i2 = NULL; /* Fixed-H layers in Fixed-H sorting order */ - - int n1; /* TAUT_YES if tautomeric i1 exists, otherwise TAUT_NON */ - - /* INChI_Stereo *Stereo1, *Stereo2; */ - - n1 = ( p1->pINChI[TAUT_YES] && p1->pINChI[TAUT_YES]->nNumberOfAtoms )? TAUT_YES : TAUT_NON; - - i1 = p1->pINChI[n1]; - i2 = (n1 == TAUT_YES && p2->pINChI[TAUT_NON] && - p2->pINChI[TAUT_NON]->nNumberOfAtoms)? p2->pINChI[TAUT_NON] : (const INChI *)NULL; - - - /* non-deleted-non-empty < deleted < empty */ - if ( i1 && !i2 ) - return 0; /* non-empty is the smallest (first) */ - if ( !i1 && i2 ) - return 0; - if ( !i1 && !i2 ) - return 0; - if ( i1->bDeleted ) - return 1; /* deleted is the largest (last) among non-empty */ - if ( i2->bDeleted ) - return -1; - - if ( i1->nNumberOfAtoms > 0 && !i2->nNumberOfAtoms ) - return 0; - - i2 = i2; - - num_H1 = num_H2 = 0; - - /* do not compare terminal H */ - if ( ret = CompareHillFormulasNoH( i1->szHillFormula, i2->szHillFormula, &num_H1, &num_H2 ) ) { - return ret; /* lexicographic order except the shorter one is greater (last): CH2O < CH2; C3XX < C2XX */ - } - - /********************************************************* - compare non-isotopic non-tautomeric part - *********************************************************/ - - /* compare number of atoms (excluding terminal H) */ - if ( ret = i2->nNumberOfAtoms - i1->nNumberOfAtoms ) - return ret; /* more atoms first */ - - /* compare elements (excluding terminal H) */ - num = i1->nNumberOfAtoms; - for ( i = 0; i < num; i ++ ) { /* should always be equal if Hill formulas are same */ - if ( ret = (int)i2->nAtom[i] - (int)i1->nAtom[i] ) - return ret; /* greater periodic number first */ - } - /********************************************************** - compare connection tables - ***********************************************************/ - if ( ret = i2->lenConnTable - i1->lenConnTable ) - return ret; /* longer connection table first */ - num = i2->lenConnTable; - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)i2->nConnTable[i] - (int)i1->nConnTable[i] ) - return ret; /* greater connection table first */ - } - /********************************************************* - compare compare total number of H (inverse: H3 < H2 ) - **********************************************************/ - if ( ret = num_H2 - num_H1 ) - return ret; - /********************************************************* - compare non-tautomeric num_H: N < NH3 < NH2 < NH - **********************************************************/ - num = i1->nNumberOfAtoms; - for ( i = 0; i < num; i ++ ) { - if ( i2->nNum_H[i] != i1->nNum_H[i] ) { - return !i2->nNum_H[i]? 1 : /* no H first */ - !i1->nNum_H[i]? -1 : - (int)i2->nNum_H[i] - (int)i1->nNum_H[i]; - } - } - /********************************************************* - compare non-isotopic tautomeric part - *********************************************************/ - if ( ret = CompareTautNonIsoPartOfINChI( i1, i2) ) { - return ret; - } - /* - if ( ret = i2->lenTautomer - i1->lenTautomer ) - return ret; - num = inchi_min( i2->lenTautomer, i1->lenTautomer ); - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)i2->nTautomer[i] - (int)i1->nTautomer[i] ) - return ret; - } - */ - /********************************************************* - * * - * at this point both components are either tautomeric * - * or non-tautomeric * - * * - *********************************************************/ - - /********************************************************* - non-tautomeric "fixed H" specific - *********************************************************/ - if ( /*TAUT_NON == bTaut &&*/ (i2 && i2->nNum_H_fixed ) ) { - /* first, compare non-tautomeric chem. formulas -- they may be different */ - /* secondly, compare fixed-H distribution */ - if ( i2->nNum_H_fixed ) { - num = i2->nNumberOfAtoms; - for ( i = 0; i < num; i ++ ) { - if ( i2->nNum_H_fixed[i] != 0 ) { - return 1; - } - } - } - } - /********************************************************* - compare non-isotopic stereo - *********************************************************/ - ret = CompareInchiStereo( i1->Stereo, i1->nFlags, i2->Stereo, i2->nFlags ); - if ( ret ) { - return ret; - } - /******************************************************* - do not switch back to tautomeric i1, i2 - *******************************************************/ - /* -- how to switch back -- - if ( i1t ) { - i1 = i1t; - i1t = NULL; - } - if ( i2t ) { - i2 = i2t; - i2t = NULL; - } - */ - /****************************************************** - compare isotopic non-tautomeric part - ******************************************************/ - if ( bCompareIsotopic ) { - if ( ret = i2->nNumberOfIsotopicAtoms - i1->nNumberOfIsotopicAtoms ) - return ret; - num = i1->nNumberOfIsotopicAtoms; - /* compare isotopic atoms */ - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)i2->IsotopicAtom[i].nAtomNumber - (int)i1->IsotopicAtom[i].nAtomNumber ) - return ret; - if ( ret = (int)i2->IsotopicAtom[i].nIsoDifference - (int)i1->IsotopicAtom[i].nIsoDifference ) - return ret; - } - /* compare isotopic H */ - /* if tautomeric comparison mode then here are compared only non-tautomeric H */ - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)i2->IsotopicAtom[i].nNum_T - (int)i1->IsotopicAtom[i].nNum_T ) - return ret; - if ( ret = (int)i2->IsotopicAtom[i].nNum_D - (int)i1->IsotopicAtom[i].nNum_D ) - return ret; - if ( ret = (int)i2->IsotopicAtom[i].nNum_H - (int)i1->IsotopicAtom[i].nNum_H ) - return ret; - } - /***************************************************** - compare isotopic tautomeric part - *****************************************************/ - if ( ret = i2->nNumberOfIsotopicTGroups || i1->nNumberOfIsotopicTGroups ) - return ret; - /* - num = i1->nNumberOfIsotopicTGroups; - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)i2->IsotopicTGroup[i].nTGroupNumber - (int)i1->IsotopicTGroup[i].nTGroupNumber ) - return ret; - if ( ret = (int)i2->IsotopicTGroup[i].nNum_T - (int)i1->IsotopicTGroup[i].nNum_T ) - return ret; - if ( ret = (int)i2->IsotopicTGroup[i].nNum_D - (int)i1->IsotopicTGroup[i].nNum_D ) - return ret; - if ( ret = (int)i2->IsotopicTGroup[i].nNum_H - (int)i1->IsotopicTGroup[i].nNum_H ) - return ret; - } - */ - /**************************************************** - compare isotopic stereo - ****************************************************/ - ret = CompareInchiStereo( i1->StereoIsotopic, i1->nFlags, i2->StereoIsotopic, i2->nFlags ); - if ( ret ) { - return ret; - } - - } - - - /********************************************************** - compare charges: non-charged first, then in order of - ascending charges (negative first) - ***********************************************************/ - if ( i2->nTotalCharge && i1->nTotalCharge ) { - /* both are charged; smaller charges first */ - ret = (int)i1->nTotalCharge - (int)i2->nTotalCharge; - return ret; - } - if ( ret = (i1->nTotalCharge? 1:0) - (i2->nTotalCharge? 1:0) ) { - /* only one is charged; uncharged first */ - return ret; - } - /* stable sort */ - /*ret = p1->ord_number - p2->ord_number;*/ - - return ret; -} - -/*************************** stereo ***********************************************************/ -typedef enum tagSp3StereoTypeTmp { - SP3_NONE = 0, /* no sp3 stereo: no /t, /m, /s segments */ - /* /t is present: */ - SP3_ONLY = 1, /* no /s or /m segment: inversion leaves the structure unchanged */ - SP3_ABS = 2, /* abs stereo: both /m and /s are present */ - SP3_REL = 4, /* rel stereo: /s is present, /m is not */ - SP3_RAC = 8, /* racemic stereo: /s is presen, /m is nott */ - SP3_TYPE = (SP3_ABS|SP3_REL|SP3_RAC), /* bitmap for checking the presence of /m */ - SP3_ANY = (SP3_ABS|SP3_REL|SP3_RAC|SP3_ONLY) /* bitmap for checking the presence of /t */ -} SP3_TYPE_TMP; - -/**********************************************************************************************/ -int GetSp3RelRacAbs( const INChI *pINChI, INChI_Stereo *Stereo ) -{ - int nRet = SP3_NONE; - if ( pINChI && !pINChI->bDeleted && Stereo && 0 < Stereo->nNumberOfStereoCenters ) { - if ( 0 != Stereo->nCompInv2Abs ) { - if ( pINChI->nFlags & INCHI_FLAG_REL_STEREO ) { -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - if ( 1 < Stereo->nNumberOfStereoCenters ) { - nRet = SP3_REL; - } -#else - nRet = SP3_REL; -#endif - } else - if ( pINChI->nFlags & INCHI_FLAG_RAC_STEREO ) { -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - if ( 1 < Stereo->nNumberOfStereoCenters ) { - nRet = SP3_REL; - } -#else - nRet = SP3_RAC; -#endif - } else { - nRet = SP3_ABS; - } - } else -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - if ( !(( pINChI->nFlags & (INCHI_FLAG_REL_STEREO|INCHI_FLAG_RAC_STEREO) ) && 1 == Stereo->nNumberOfStereoCenters) ) -#endif - { - nRet = SP3_ONLY; /* SP3_NONE if relative stereo and 1 stereocenter */ - } - } - return nRet; -} - -/* char sDifSegs[DIFL_LENGTH][DIFS_LENGTH]; */ - -/**********************************************************************************************/ -/* sorting in descending order: return -1 if *p1 > *p2, return +1 if *p1 < *p2 */ -/**********************************************************************************************/ -int CompINChILayers(const INCHI_SORT *p1, const INCHI_SORT *p2, - char sDifSegs[][DIFS_LENGTH], int bFixTranspChargeBug ) -{ - int ret = 0, num, i, num_H1, num_H2; - - const INChI *i1 = NULL; /* Mobile-H layers in Mobile-H sorting order */ - const INChI *i2 = NULL; /* Fixed-H layers in Fixed-H sorting order */ - - int n1; /* TAUT_YES if tautomeric i1 exists, otherwise TAUT_NON */ - - INChI_Stereo *Stereo1, *Stereo2; - INChI_Stereo *IsoStereo1, *IsoStereo2; - int bRelRac[DIFL_LENGTH]; - char *psDifSegs; - - n1 = ( p1->pINChI[TAUT_YES] && p1->pINChI[TAUT_YES]->nNumberOfAtoms )? TAUT_YES : TAUT_NON; - - i1 = p1->pINChI[n1]; - i2 = (n1 == TAUT_YES && p2->pINChI[TAUT_NON] && - p2->pINChI[TAUT_NON]->nNumberOfAtoms)? p2->pINChI[TAUT_NON] : (const INChI *)NULL; - - num_H1 = num_H2 = 0; - memset( bRelRac, DIFV_BOTH_EMPTY, sizeof(bRelRac) ); - /*=====================*/ - /*==== /f ======*/ - /*=====================*/ - if ( i1 && !i1->bDeleted && i1->szHillFormula && i1->szHillFormula[0] ) { - sDifSegs[DIFL_M][DIFS_f_FORMULA] |= DIFV_NEQ2PRECED; - if ( i2 && !i2->bDeleted && i2->szHillFormula && i2->szHillFormula[0] ) { - if ( !CompareHillFormulasNoH( i1->szHillFormula, i2->szHillFormula, &num_H1, &num_H2 ) && - num_H1 == num_H2 ) { - sDifSegs[DIFL_F][DIFS_f_FORMULA] |= DIFV_EQL2PRECED; - } else { - sDifSegs[DIFL_F][DIFS_f_FORMULA] |= DIFV_NEQ2PRECED; - } - } else { - sDifSegs[DIFL_F][DIFS_f_FORMULA] |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; - } - } else { - sDifSegs[DIFL_M][DIFS_f_FORMULA] |= DIFV_BOTH_EMPTY; - if ( i2 && !i2->bDeleted && i2->szHillFormula && i2->szHillFormula[0] ) { - sDifSegs[DIFL_F][DIFS_f_FORMULA] |= DIFV_NEQ2PRECED; - } else { - sDifSegs[DIFL_F][DIFS_f_FORMULA] |= DIFV_BOTH_EMPTY; - } - } - /*=====================*/ - /*==== /c ======*/ - /*=====================*/ - if ( i1 && !i1->bDeleted && i1->lenConnTable > 1 ) { - sDifSegs[DIFL_M][DIFS_f_FORMULA] |= DIFV_NEQ2PRECED; - } else { - sDifSegs[DIFL_M][DIFS_f_FORMULA] |= DIFV_BOTH_EMPTY; - } - /*=====================*/ - /*==== /h ======*/ - /*=====================*/ - /* M: H atoms */ - if ( i1 && !i1->bDeleted ) { - num_H1 = (i1->lenTautomer > 0 && i1->nTautomer && i1->nTautomer[0])? 1 : 0; /* number of t-groups */ - if ( !num_H1 && i1->nNum_H ) { - for ( i = 0; i < i1->nNumberOfAtoms; i ++ ) { /* immobile H */ - if ( i1->nNum_H[i] ) { - num_H1 = 1; - break; - } - } - } - sDifSegs[DIFL_M][DIFS_h_H_ATOMS] |= num_H1? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; - } else { - sDifSegs[DIFL_M][DIFS_h_H_ATOMS] |= DIFV_BOTH_EMPTY; - } - /* F: fixed mobile H */ - if ( i2 && !i2->bDeleted && i2->nNum_H_fixed ) { - num_H2 = 0; - if ( i1 && !i1->bDeleted ) { - for ( i = 0; i < i1->nNumberOfAtoms; i ++ ) { - if ( i2->nNum_H_fixed[i] ) { - num_H2 = 1; - break; - } - } - } - sDifSegs[DIFL_F][DIFS_h_H_ATOMS] |= num_H2? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; - } else { - sDifSegs[DIFL_F][DIFS_h_H_ATOMS] |= DIFV_BOTH_EMPTY; - } - /* MI: exchangable isotopic H: see OutputINChI1(), num_iso_H[] */ - - /*=====================*/ - /*==== /q ======*/ - /*=====================*/ - psDifSegs = &sDifSegs[DIFL_F][DIFS_q_CHARGE]; - if ( i1 && !i1->bDeleted ) { - if ( i1->nTotalCharge ) { - sDifSegs[DIFL_M][DIFS_q_CHARGE] |= DIFV_NEQ2PRECED; - } else { - sDifSegs[DIFL_M][DIFS_q_CHARGE] |= DIFV_BOTH_EMPTY; - } - if ( i2 && !i2->bDeleted ) { - if ( i1->nTotalCharge ) { - if ( i1->nTotalCharge == i2->nTotalCharge ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else - if ( i2->nTotalCharge ) { - *psDifSegs |= DIFV_NEQ2PRECED; - } else { - *psDifSegs |= DIFV_IS_EMPTY; - } - } else { - if ( i2->nTotalCharge ) { - *psDifSegs |= DIFV_NEQ2PRECED; - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - } - } else - if ( !i2 ) { - if (bFixTranspChargeBug==1) - { - /* bug explanation: - - component #1 is tautomeric, component #2 is not - Mobile-H(#2) > Mobile-H(#1) - Fixed-H(#2) = Mobile-H(#2) < Fixed-H(#1) - - Layer first_charge second_charge - - Mobile-H 0 (comp#1) -1 (comp#2) - Fixed-H none (comp#2) -1 (comp#1) - - v1.01 charge compared decided that charge layers are same and omitted Fixed-H /q layer - - Solution: when component permutation is detected AND fixed-H component does not exist, - compare Mobile-H charge [0 (comp#1) in the example] to the charge of Mobile-H [-1 (comp#2)] - of the component that has none Fixed-H charge - */ - - /* Fixed-H i2 is empty because Fixed-H struct is same as Mobile-H */ - if ( p1->ord_number != p2->ord_number && /* component order in Fixed-H is different from Mobile-H */ - n1 == TAUT_YES && p2->pINChI[TAUT_YES] && !p2->pINChI[TAUT_YES]->bDeleted && - p2->pINChI[TAUT_YES]->nNumberOfAtoms ) { - int i2_nTotalCharge = p2->pINChI[TAUT_YES]->nTotalCharge; - - if ( i1->nTotalCharge ) { - if ( i1->nTotalCharge == i2_nTotalCharge ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else - if ( i2_nTotalCharge ) { - *psDifSegs |= DIFV_NEQ2PRECED; - } else { - *psDifSegs |= DIFV_IS_EMPTY; - } - } else { - if ( i2_nTotalCharge ) { - *psDifSegs |= DIFV_NEQ2PRECED; - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - } - } else { - *psDifSegs |= i1->nTotalCharge? DIFV_EQL2PRECED : DIFV_BOTH_EMPTY; - } - } - else /* if (bFixTranspChargeBug==1) */ - { - *psDifSegs |= i1->nTotalCharge? DIFV_EQL2PRECED : DIFV_BOTH_EMPTY; - } - } - - else /* if ( !i2 ) { */ - { - /* i2 && i2->bDeleted */ - *psDifSegs |= i1->nTotalCharge? DIFV_IS_EMPTY : DIFV_BOTH_EMPTY; - } - - } else { - sDifSegs[DIFL_M][DIFS_q_CHARGE] |= DIFV_BOTH_EMPTY; - if ( i2 && !i2->bDeleted ) { - if ( i2->nTotalCharge ) { - sDifSegs[DIFL_F][DIFS_q_CHARGE] |= DIFV_NEQ2PRECED; - } else { - sDifSegs[DIFL_F][DIFS_q_CHARGE] |= DIFV_BOTH_EMPTY; - } - } - } - /*************** stereo *****************/ - if ( i1 && !i1->bDeleted ) { - Stereo1 = i1->Stereo; - IsoStereo1 = i1->StereoIsotopic; - } else { - Stereo1 = NULL; - IsoStereo1 = NULL; - } - if ( i2 && !i2->bDeleted ) { - Stereo2 = i2->Stereo; - IsoStereo2 = i2->StereoIsotopic; - } else { - Stereo2 = NULL; - IsoStereo2 = NULL; - } - /*=====================*/ - /*==== /b ======*/ - /*=====================*/ - /* M double bond stereo */ - psDifSegs = &sDifSegs[DIFL_M][DIFS_b_SBONDS]; - if ( Stereo1 && Stereo1->nNumberOfStereoBonds ) { - *psDifSegs |= DIFV_NEQ2PRECED; - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - /* F double bond stereo */ - psDifSegs = &sDifSegs[DIFL_F][DIFS_b_SBONDS]; - if ( Stereo2 && Stereo2->nNumberOfStereoBonds ) { - if ( Stereo1 && Stereo1->nNumberOfStereoBonds ) { - if ( Eql_INChI_Stereo( Stereo1, EQL_SP2, Stereo2, EQL_SP2, 0 ) ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_NEQ2PRECED; - } - } else { - *psDifSegs |= DIFV_NEQ2PRECED; - } - } else { - if ( Stereo1 && Stereo1->nNumberOfStereoBonds ) { - *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - } - /* MI double bond stereo */ - psDifSegs = &sDifSegs[DIFL_MI][DIFS_b_SBONDS]; - if ( IsoStereo1 && IsoStereo1->nNumberOfStereoBonds ) { - if ( Eql_INChI_Stereo( IsoStereo1, EQL_SP2, Stereo1, EQL_SP2, 0 ) ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_NEQ2PRECED; - } - } else { - if ( Stereo1 && Stereo1->nNumberOfStereoBonds ) { - *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - } - /* FI double bond stereo */ - psDifSegs = &sDifSegs[DIFL_FI][DIFS_b_SBONDS]; - if ( IsoStereo2 && IsoStereo2->nNumberOfStereoBonds ) { - if ( Eql_INChI_Stereo( IsoStereo2, EQL_SP2, Stereo2, EQL_SP2, 0 ) ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else { - if ( !(Stereo1 && Stereo1->nNumberOfStereoBonds) && - !(Stereo2 && Stereo2->nNumberOfStereoBonds) && - Eql_INChI_Stereo( IsoStereo2, EQL_SP2, IsoStereo1, EQL_SP2, 0 ) ) { - *psDifSegs |= DIFV_FI_EQ_MI; - } else { - *psDifSegs |= DIFV_NEQ2PRECED; - } - } - } else { - /* the solution table for FI stereo, - in case of FI stereo is empty - E = segment is empty, NE = not empty - +==============================+ - | M | MI | F | result | - +=====+=====+=====+============+ - | E | E | E | both empty | - +-----+-----+-----+------------+ - | NE | E | E | both empty | - +-----+-----+-----+------------+ - | E | NE | E | is empty | - +-----+-----+-----+------------+ - | NE | NE | E | both empty | - +-----+-----+-----+------------+ - | E | E | NE | is empty | - +-----+-----+-----+------------+ - | NE | E | NE | is empty | - +-----+-----+-----+------------+ - | E | NE | NE | is empty | - +-----+-----+-----+------------+ - | NE | NE | ME | is empty | - +==============================+ - */ - if ( Stereo2 && Stereo2->nNumberOfStereoBonds ) { - *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ - } else - if ( IsoStereo1 && IsoStereo1->nNumberOfStereoBonds && - !(Stereo1 && Stereo1->nNumberOfStereoBonds) - ) { - *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - } - /*==================================*/ - /*==== /t, /m, /s for M ======*/ - /*==================================*/ - /* M sp3 stereo */ - - - bRelRac[DIFL_M ] = GetSp3RelRacAbs( i1, Stereo1 ); /* Mobile-H */ - bRelRac[DIFL_MI] = GetSp3RelRacAbs( i1, IsoStereo1 ); - bRelRac[DIFL_F ] = GetSp3RelRacAbs( i2, Stereo2 ); /* Fixed-H */ - bRelRac[DIFL_FI] = GetSp3RelRacAbs( i2, IsoStereo2 ); - if ( SP3_NONE != bRelRac[DIFL_M] ) { - sDifSegs[DIFL_M][DIFS_t_SATOMS] |= (bRelRac[DIFL_M] & SP3_ANY)? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; - sDifSegs[DIFL_M][DIFS_m_SP3INV] |= (bRelRac[DIFL_M] & SP3_ABS)? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; - sDifSegs[DIFL_M][DIFS_s_STYPE] |= (bRelRac[DIFL_M] & SP3_TYPE)? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; - } else { - sDifSegs[DIFL_M][DIFS_t_SATOMS] |= DIFV_BOTH_EMPTY; - sDifSegs[DIFL_M][DIFS_m_SP3INV] |= DIFV_BOTH_EMPTY; - sDifSegs[DIFL_M][DIFS_s_STYPE] |= DIFV_BOTH_EMPTY; - } - /*=====================*/ - /*==== /t ======*/ - /*=====================*/ - /* F sp3 stereo */ - psDifSegs = &sDifSegs[DIFL_F][DIFS_t_SATOMS]; - if ( SP3_ANY & bRelRac[DIFL_F] ) { - if ( Eql_INChI_Stereo( Stereo2, EQL_SP3, Stereo1, EQL_SP3, 0 ) ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_NEQ2PRECED; - } - } else - if ( SP3_ANY & bRelRac[DIFL_M] ) { - *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - /* MI sp3 stereo */ - psDifSegs = &sDifSegs[DIFL_MI][DIFS_t_SATOMS]; - if ( SP3_ANY & bRelRac[DIFL_MI] ) { - if ( Eql_INChI_Stereo( IsoStereo1, EQL_SP3, Stereo1, EQL_SP3, 0 ) ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_NEQ2PRECED; - } - } else - if ( SP3_ANY & bRelRac[DIFL_M] ) { - *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - /* FI sp3 stereo */ - psDifSegs = &sDifSegs[DIFL_FI][DIFS_t_SATOMS]; - if ( SP3_ANY & bRelRac[DIFL_FI] ) { - if ( Eql_INChI_Stereo( IsoStereo2, EQL_SP3, Stereo2, EQL_SP3, 0 ) ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else - if ( !(SP3_ANY & bRelRac[DIFL_M]) && - !(SP3_ANY & bRelRac[DIFL_F]) && - Eql_INChI_Stereo( IsoStereo2, EQL_SP3, IsoStereo1, EQL_SP3, 0 ) ) { - *psDifSegs |= DIFV_FI_EQ_MI; - } else { - *psDifSegs |= DIFV_NEQ2PRECED; - } - } else /* similar to /b */ - if ( (SP3_ANY & bRelRac[DIFL_F]) ) { - *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ - } else - if ( (SP3_ANY & bRelRac[DIFL_MI]) && !(SP3_ANY & bRelRac[DIFL_M]) ) { - *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - /*=====================*/ - /*==== /m ======*/ - /*=====================*/ - /* F sp3 abs stereo inversion */ - psDifSegs = &sDifSegs[DIFL_F][DIFS_m_SP3INV]; - if ( bRelRac[DIFL_F] & SP3_ABS ) { - /* the order of || operands below is critically important: || is not a commutative operation */ - if ( !(bRelRac[DIFL_M] & SP3_ABS) || Stereo2->nCompInv2Abs != Stereo1->nCompInv2Abs ) { - *psDifSegs |= DIFV_NEQ2PRECED; - } else { - *psDifSegs |= DIFV_EQL2PRECED; - } - } else - if ( bRelRac[DIFL_M] & SP3_ABS ) { - *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - /* MI sp3 abs stereo inversion */ - psDifSegs = &sDifSegs[DIFL_MI][DIFS_m_SP3INV]; - if ( SP3_ABS & bRelRac[DIFL_MI] ) { - if ( (SP3_ABS & bRelRac[DIFL_M]) && IsoStereo1->nCompInv2Abs == Stereo1->nCompInv2Abs ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_NEQ2PRECED; - } - } else - if ( SP3_ABS & bRelRac[DIFL_M] ) { - *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - /* FI sp3 abs stereo inversion */ - psDifSegs = &sDifSegs[DIFL_FI][DIFS_m_SP3INV]; - if ( SP3_ABS & bRelRac[DIFL_FI] ) { - if ( (SP3_ABS & bRelRac[DIFL_F]) && IsoStereo2->nCompInv2Abs == Stereo2->nCompInv2Abs ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else - if ( !(SP3_ABS & bRelRac[DIFL_M]) && - !(SP3_ABS & bRelRac[DIFL_F]) && - (SP3_ABS & bRelRac[DIFL_MI]) && /* make sure IsoStereo1 != NULL */ - IsoStereo2->nCompInv2Abs == IsoStereo1->nCompInv2Abs ) { - *psDifSegs |= DIFV_FI_EQ_MI; - } else { - *psDifSegs |= DIFV_NEQ2PRECED; - } - } else /* similar to /b */ - /* the order of || operands below is critically important: || is no a commutative operation */ - if ( (SP3_ABS & bRelRac[DIFL_F]) ) { - *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ - } else - if ( (SP3_ABS & bRelRac[DIFL_MI]) && !(SP3_ABS & bRelRac[DIFL_M]) ) { - *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - /*=====================*/ - /*==== /s ======*/ - /*=====================*/ - /* F sp3 stereo type */ - psDifSegs = &sDifSegs[DIFL_F][DIFS_s_STYPE]; - if ( bRelRac[DIFL_F] & SP3_TYPE ) { - if ( (bRelRac[DIFL_F] & SP3_TYPE) == (bRelRac[DIFL_M] & SP3_TYPE) ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_NEQ2PRECED; - } - } else - if ( bRelRac[DIFL_M] & SP3_TYPE ) { - *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - /* MI sp3 stereo type */ - psDifSegs = &sDifSegs[DIFL_MI][DIFS_s_STYPE]; - if ( SP3_TYPE & bRelRac[DIFL_MI] ) { - if ( (SP3_TYPE & bRelRac[DIFL_MI]) == (SP3_TYPE & bRelRac[DIFL_M]) ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_NEQ2PRECED; - } - } else - if ( SP3_TYPE & bRelRac[DIFL_M] ) { - *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - /* FI sp3 stereo type */ - psDifSegs = &sDifSegs[DIFL_FI][DIFS_s_STYPE]; - if ( SP3_TYPE & bRelRac[DIFL_FI] ) { - if ( (SP3_TYPE & bRelRac[DIFL_FI]) == (SP3_TYPE & bRelRac[DIFL_F]) ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else - if ( !(SP3_TYPE & bRelRac[DIFL_M]) && - !(SP3_TYPE & bRelRac[DIFL_F]) && - (SP3_TYPE & bRelRac[DIFL_MI]) ) { - *psDifSegs |= DIFV_FI_EQ_MI; - } else { - *psDifSegs |= DIFV_NEQ2PRECED; - } - } else /* similar to /b */ - /* the order of || operands below is critically important: || is not a commutative operation */ - if ( (SP3_TYPE & bRelRac[DIFL_F]) ) { - *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ - } else - if ( (SP3_TYPE & bRelRac[DIFL_MI]) && !(SP3_TYPE & bRelRac[DIFL_M]) ) { - *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - /*=====================*/ - /*==== /o ======*/ - /*=====================*/ - if ( p1 && p2 && p1->ord_number != p2->ord_number ) { - sDifSegs[DIFL_F][DIFS_o_TRANSP] |= DIFV_NEQ2PRECED; - } - /*=====================*/ - /*==== /i ======*/ - /*=====================*/ - /* M isotopic atoms */ - psDifSegs = &sDifSegs[DIFL_MI][DIFS_i_IATOMS]; - if ( i1 && !i1->bDeleted && (i1->nNumberOfIsotopicAtoms || i1->nNumberOfIsotopicTGroups) ) { - *psDifSegs |= DIFV_NEQ2PRECED; - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - /* F isotopic atoms */ - psDifSegs = &sDifSegs[DIFL_FI][DIFS_i_IATOMS]; - if ( i2 && !i2->bDeleted ) { - if ( i2->nNumberOfIsotopicAtoms || i2->nNumberOfIsotopicTGroups ) { - if ( !i1 || i1->bDeleted || - i2->nNumberOfIsotopicAtoms != i1->nNumberOfIsotopicAtoms || - i2->nNumberOfIsotopicTGroups != i1->nNumberOfIsotopicTGroups ) { - *psDifSegs |= DIFV_NEQ2PRECED; - } else { - int diff; - num = i1->nNumberOfIsotopicAtoms; - diff = 0; - for ( i = 0; i < num; i ++ ) { - /* compare isotopic atoms */ - if ( diff = (int)i2->IsotopicAtom[i].nAtomNumber - (int)i1->IsotopicAtom[i].nAtomNumber ) - break; - if ( diff = (int)i2->IsotopicAtom[i].nIsoDifference - (int)i1->IsotopicAtom[i].nIsoDifference ) - break; - /* compare isotopic H */ - if ( diff = (int)i2->IsotopicAtom[i].nNum_T - (int)i1->IsotopicAtom[i].nNum_T ) - break; - if ( diff = (int)i2->IsotopicAtom[i].nNum_D - (int)i1->IsotopicAtom[i].nNum_D ) - break; - if ( diff = (int)i2->IsotopicAtom[i].nNum_H - (int)i1->IsotopicAtom[i].nNum_H ) - break; - } - if ( !diff ) { - num = i1->nNumberOfIsotopicTGroups; - for ( i = 0; i < num; i ++ ) { - if ( diff = (int)i2->IsotopicTGroup[i].nTGroupNumber - (int)i1->IsotopicTGroup[i].nTGroupNumber ) - break; - if ( diff = (int)i2->IsotopicTGroup[i].nNum_T - (int)i1->IsotopicTGroup[i].nNum_T ) - break; - if ( diff = (int)i2->IsotopicTGroup[i].nNum_D - (int)i1->IsotopicTGroup[i].nNum_D ) - return diff; - if ( diff = (int)i2->IsotopicTGroup[i].nNum_H - (int)i1->IsotopicTGroup[i].nNum_H ) - break; - } - } - *psDifSegs |= diff? DIFV_NEQ2PRECED : DIFV_FI_EQ_MI; - - } - } else - if ( i1 && !i1->bDeleted && (i1->nNumberOfIsotopicAtoms || i1->nNumberOfIsotopicTGroups) ) { - *psDifSegs |= DIFV_IS_EMPTY; - } - } else - if ( !i2 ) { - if ( i1 && !i1->bDeleted && (i1->nNumberOfIsotopicAtoms || i1->nNumberOfIsotopicTGroups) ) { - *psDifSegs |= DIFV_EQL2PRECED; - } else { - *psDifSegs |= DIFV_BOTH_EMPTY; - } - } - - return ret; -} -/**********************************************************************************************/ -int INChI_SegmentAction( char cDifSegs ) -{ - if ( !(cDifSegs & DIFV_OUTPUT_OMIT_F) ) { - return INCHI_SEGM_OMIT; - } - if ( (cDifSegs & DIFV_OUTPUT_EMPTY_T) && !(cDifSegs & DIFV_OUTPUT_EMPTY_F) ) { - return INCHI_SEGM_EMPTY; - } - if ( (cDifSegs & DIFV_OUTPUT_FILL_T) ) { - return INCHI_SEGM_FILL; - } - return INCHI_SEGM_OMIT; /* the control flow shoul never reach this point */ -} -/**********************************************************************************************/ -int MarkUnusedAndEmptyLayers( char sDifSegs[][DIFS_LENGTH] ) -{ - /**************************************************** - 1. If all elements of a layer are DIFV_IS_EMPTY and/or DIFV_BOTH_EMPTY - and/or DIFV_EQL2PRECED and/or DIFV_FI_EQ_MI - and there is NO succeeding non-empty layer then mark the 1st element - of the layer DIFV_BOTH_EMPTY; this layerr will be omitted. - - 2. If all elements of a layer are DIFV_IS_EMPTY and/or DIFV_BOTH_EMPTY - and/or DIFV_EQL2PRECED and/or DIFV_FI_EQ_MI - and there IS a succeeding non-empty layer then mark the 1st element - of the layer DIFV_IS_EMPTY and all other elements DIFV_BOTH_EMPTY; - only the first empty segment of this layerr will be output. - - 3. If NOT all elements of a layer are DIFV_IS_EMPTY and/or DIFV_BOTH_EMPTY - and/or DIFV_EQL2PRECED and/or DIFV_FI_EQ_MI - and the 1st element of the layer is DIFV_BOTH_EMPTY then mark it - DIFV_IS_EMPTY; it will be output as empty (except M layer). - */ - - int i, nLayer, sBits, nFirstSegm; -#define nFirstFmlSegm DIFS_f_FORMULA -#define nFirstIsoSegm DIFS_i_IATOMS - /* FI */ - nLayer = DIFL_FI; - nFirstSegm = nFirstIsoSegm; - sBits = 0; - for ( i = 0; i < DIFS_idf_LENGTH; i ++ ) { - sBits |= sDifSegs[nLayer][i]; - } - if ( !(sBits & DIFV_OUTPUT_OMIT_F) ) { - /* Omit the FI layer */ - memset( sDifSegs[nLayer], DIFV_BOTH_EMPTY, DIFS_idf_LENGTH); - } else - if ( sDifSegs[nLayer][nFirstSegm] == DIFV_BOTH_EMPTY || - !(sDifSegs[nLayer][nFirstSegm] & DIFV_OUTPUT_OMIT_F) ) { - sDifSegs[nLayer][nFirstSegm] = DIFV_IS_EMPTY; - } - - /* MI */ - nLayer = DIFL_MI; - nFirstSegm = nFirstIsoSegm; - sBits = 0; - for ( i = 0; i < DIFS_idf_LENGTH; i ++ ) { - sBits |= sDifSegs[nLayer][i]; - } - if ( !(sBits & DIFV_OUTPUT_OMIT_F) ) { - /* Omit the MI layer */ - memset( sDifSegs[nLayer], DIFV_BOTH_EMPTY, DIFS_idf_LENGTH); - } else - if ( sDifSegs[nLayer][nFirstSegm] == DIFV_BOTH_EMPTY || - !(sDifSegs[nLayer][nFirstSegm] & DIFV_OUTPUT_OMIT_F) ) { - sDifSegs[nLayer][nFirstSegm] = DIFV_IS_EMPTY; - } - - /* F */ - nLayer = DIFL_F; - nFirstSegm = nFirstFmlSegm; - sBits = 0; - for ( i = 0; i < DIFS_idf_LENGTH; i ++ ) { - sBits |= sDifSegs[nLayer][i]; - } - if ( !(sBits & DIFV_OUTPUT_OMIT_F) && - sDifSegs[DIFL_FI][nFirstIsoSegm] == DIFV_BOTH_EMPTY ) { - /* Omit the F layer: no non-iotopic and no isotopic segments */ - memset( sDifSegs[nLayer], DIFV_BOTH_EMPTY, DIFS_idf_LENGTH); - } else - /* do not omit fixed-H layer */ - if ( sDifSegs[nLayer][nFirstSegm] == DIFV_BOTH_EMPTY || - !(sDifSegs[nLayer][nFirstSegm] & DIFV_OUTPUT_OMIT_F) ) { - sDifSegs[nLayer][nFirstSegm] = DIFV_IS_EMPTY; - } - - /* M -- leave as it is */ - return 0; -#undef nFirstFmlSegm -#undef nFirstIsoSegm -} -/*********************************************************************************************/ -int CompareInchiStereo( INChI_Stereo *Stereo1, INCHI_MODE nFlags1, INChI_Stereo *Stereo2, INCHI_MODE nFlags2 ) -{ - int i, num, ret; - if ( Stereo2 && Stereo1 ) { - /* compare stereogenic bonds */ - num = inchi_min( Stereo2->nNumberOfStereoBonds, Stereo1->nNumberOfStereoBonds ); - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)Stereo2->nBondAtom1[i] - (int)Stereo1->nBondAtom1[i] ) - return ret; - if ( ret = (int)Stereo2->nBondAtom2[i] - (int)Stereo1->nBondAtom2[i] ) - return ret; - if ( ret = (int)Stereo2->b_parity[i] - (int)Stereo1->b_parity[i] ) - return ret; - } - if ( ret = (int)Stereo2->nNumberOfStereoBonds - (int)Stereo1->nNumberOfStereoBonds ) - return ret; - /* compare stereogenic atoms */ -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - if ( ((nFlags1 | nFlags2) & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO) ) && - 1 == Stereo2->nNumberOfStereoCenters && - 1 == Stereo1->nNumberOfStereoCenters ) { - ; /* do not compare single stereocenters in case of relative stereo */ - } else -#endif - { - num = inchi_min( Stereo2->nNumberOfStereoCenters, Stereo1->nNumberOfStereoCenters ); - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)Stereo2->nNumber[i] - (int)Stereo1->nNumber[i] ) - return ret; - if ( ret = (int)Stereo2->t_parity[i] - (int)Stereo1->t_parity[i] ) - return ret; - } - if ( ret = (int)Stereo2->nNumberOfStereoCenters - (int)Stereo1->nNumberOfStereoCenters ) - return ret; - /* compare stereo-abs-is-inverted flags for non-relative, non-racemic */ - if ( !((nFlags1 | nFlags2) & (INCHI_FLAG_RAC_STEREO | INCHI_FLAG_REL_STEREO)) ) { - if ( ret = (Stereo2->nCompInv2Abs < 0) - (Stereo1->nCompInv2Abs < 0) ) { - return ret; - } - } - } - } else - if ( Stereo2 && ( Stereo2->nNumberOfStereoBonds > 0 || - Stereo2->nNumberOfStereoCenters > 0 -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - && /* do not compare single stereocenters in case of relative stereo */ - !((nFlags2 & (INCHI_FLAG_REL_STEREO|INCHI_FLAG_RAC_STEREO)) && - 1 == Stereo2->nNumberOfStereoCenters - ) -#endif - ) ) { - return 1; - }else - if ( Stereo1 && ( Stereo1->nNumberOfStereoBonds > 0 || - Stereo1->nNumberOfStereoCenters > 0 -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - && /* do not compare single stereocenters in case of relative stereo */ - !((nFlags1 & (INCHI_FLAG_REL_STEREO|INCHI_FLAG_RAC_STEREO)) && - 1 == Stereo1->nNumberOfStereoCenters - ) -#endif - ) ) { - return -1; - } - return 0; -} -/**********************************************************************************************/ -/* sorting in descending order: return -1 if *p1 > *p2, return +1 if *p1 < *p2 */ -/**********************************************************************************************/ -int CompINChI2(const INCHI_SORT *p1, const INCHI_SORT *p2, int bTaut, int bCompareIsotopic) -{ - int ret, num, i, num_H1, num_H2; - - const INChI *i1 = NULL; /* tautomeric if exists, otherwise non-tautomeric */ - const INChI *i2 = NULL; /* tautomeric if exists, otherwise non-tautomeric */ - - int n1; /* TAUT_YES if tautomeric i1 exists, otherwise TAUT_NON */ - int n2; /* TAUT_YES if tautomeric i2 exists, otherwise TAUT_NON */ - - const INChI *i1n = NULL; /* non-tautomeric if both tautomeric AND non-tautomeric exist */ - const INChI *i2n = NULL; /* non-tautomeric if both tautomeric AND non-tautomeric exist */ - - /*const INChI *i1t = NULL;*/ /* temp for i1 if both tautomeric AND non-tautomeric exist */ - /*const INChI *i2t = NULL;*/ /* temp for i2 if both tautomeric AND non-tautomeric exist */ - - - /* INChI_Stereo *Stereo1, *Stereo2; */ - - n1 = ( p1->pINChI[TAUT_YES] && p1->pINChI[TAUT_YES]->nNumberOfAtoms )? TAUT_YES : TAUT_NON; - n2 = ( p2->pINChI[TAUT_YES] && p2->pINChI[TAUT_YES]->nNumberOfAtoms )? TAUT_YES : TAUT_NON; - - i1 = p1->pINChI[n1]; - i1n = (n1 == TAUT_YES && p1->pINChI[TAUT_NON] && - p1->pINChI[TAUT_NON]->nNumberOfAtoms)? p1->pINChI[TAUT_NON] : (const INChI *)NULL; - - i2 = p2->pINChI[n2]; - i2n = (n2 == TAUT_YES && p2->pINChI[TAUT_NON] && - p2->pINChI[TAUT_NON]->nNumberOfAtoms)? p2->pINChI[TAUT_NON] : (const INChI *)NULL; - - /* non-deleted-non-empty < deleted < empty */ - if ( i1 && !i2 ) - return -1; /* non-empty is the smallest (first) */ - if ( !i1 && i2 ) - return 1; - if ( !i1 && !i2 ) - return 0; - if ( i1->bDeleted && !i2->bDeleted ) - return 1; /* deleted is the largest (last) among non-empty */ - if ( !i1->bDeleted && i2->bDeleted ) - return -1; - - num_H1 = num_H2 = 0; - - /* do not compare terminal H */ - if ( ret = CompareHillFormulasNoH( i1->szHillFormula, i2->szHillFormula, &num_H1, &num_H2 ) ) { - return ret; /* lexicographic order except the shorter one is greater (last): CH2O < CH2; C3XX < C2XX */ - } - - /********************************************************* - compare non-isotopic non-tautomeric part - *********************************************************/ - - /* compare number of atoms (excluding terminal H) */ - if ( ret = i2->nNumberOfAtoms - i1->nNumberOfAtoms ) - return ret; /* more atoms first */ - - /* compare elements (excluding terminal H) */ - num = i1->nNumberOfAtoms; - for ( i = 0; i < num; i ++ ) { /* should always be equal if Hill formulas are same */ - if ( ret = (int)i2->nAtom[i] - (int)i1->nAtom[i] ) - return ret; /* greater periodic number first */ - } - /********************************************************** - compare connection tables - ***********************************************************/ - if ( ret = i2->lenConnTable - i1->lenConnTable ) - return ret; /* longer connection table first */ - num = i2->lenConnTable; - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)i2->nConnTable[i] - (int)i1->nConnTable[i] ) - return ret; /* greater connection table first */ - } - /********************************************************* - compare compare total number of H (inverse: H3 < H2 ) - **********************************************************/ - if ( ret = num_H2 - num_H1 ) - return ret; - /********************************************************* - compare non-tautomeric num_H: N < NH3 < NH2 < NH - **********************************************************/ - num = i1->nNumberOfAtoms; - for ( i = 0; i < num; i ++ ) { - if ( i2->nNum_H[i] != i1->nNum_H[i] ) { - return !i2->nNum_H[i]? 1 : /* no H first */ - !i1->nNum_H[i]? -1 : - (int)i2->nNum_H[i] - (int)i1->nNum_H[i]; - } - } - /********************************************************* - compare non-isotopic tautomeric part - *********************************************************/ - if ( ret = CompareTautNonIsoPartOfINChI( i1, i2) ) { - return ret; - } - /* - if ( ret = i2->lenTautomer - i1->lenTautomer ) - return ret; - num = inchi_min( i2->lenTautomer, i1->lenTautomer ); - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)i2->nTautomer[i] - (int)i1->nTautomer[i] ) - return ret; - } - */ - /********************************************************* - * * - * at this point both components are either tautomeric * - * or non-tautomeric * - * * - *********************************************************/ - - /********************************************************* - non-tautomeric "fixed H" specific - *********************************************************/ - if ( TAUT_NON == bTaut && (i1n && i1n->nNum_H_fixed || i2n && i2n->nNum_H_fixed) ) { - /* first, compare non-tautomeric chem. formulas -- they may be different */ - const char *f1 = (i1n /*&& i1n->nNum_H_fixed*/)? i1n->szHillFormula : i1->szHillFormula; - const char *f2 = (i2n /*&& i2n->nNum_H_fixed*/)? i2n->szHillFormula : i2->szHillFormula; - if ( f1 && f2 &&(ret = CompareHillFormulas( f1, f2 ))) { - return ret; - } - /* secondly, compare fixed-H distribution */ - if ( i1n && i1n->nNum_H_fixed && i2n && i2n->nNum_H_fixed ) { - num = inchi_min( i1n->nNumberOfAtoms, i2n->nNumberOfAtoms); - for ( i = 0; i < num; i ++ ) { - if ( i2n->nNum_H_fixed[i] != i1n->nNum_H_fixed[i] ) { - return !i2n->nNum_H_fixed[i]? 1 : /* no fixed H first */ - !i1n->nNum_H_fixed[i]? -1 : - (int)i2n->nNum_H_fixed[i] - (int)i1n->nNum_H_fixed[i]; - } - } - if ( ret = (int)i2n->nNumberOfAtoms - (int)i1n->nNumberOfAtoms ) { - return ret; /* should not happen */ - } - } else - if ( i1n && i1n->nNum_H_fixed ) { - num = i1n->nNumberOfAtoms; - for ( i = 0; i < num; i ++ ) { /* added 2004-05-04 */ - if ( i1n->nNum_H_fixed[i] ) { - return -1; /* i1n->nNum_H_fixed[i] > 0? -1:1;*/ - } - } - /* p1 is tautomeric, p2 is not tautomeric; this must have been detected earlier */ - /*return -1;*/ /* has fixed H first *//* */ /* removed 2004-05-04 */ - } else { - num = i2n->nNumberOfAtoms; - for ( i = 0; i < num; i ++ ) { /* added 2004-05-04 */ - if ( i2n->nNum_H_fixed[i] ) { - return 1; /* i2n->nNum_H_fixed[i] > 0? 1:-1;*/ - } - } - /* p2 is tautomeric, p1 is not tautomeric; this must have been detected earlier */ - /*return 1; */ /* has fixed H first *//* */ /* removed 2004-05-04 */ - } - } - - /************************************************************************* - if requested non-tautomeric comparison then - prepare to compare non-taut non-isotopic stereo, etc. - *************************************************************************/ - if ( TAUT_NON == bTaut ) { - if ( i1n ) { - /*i1t = i1;*/ - i1 = i1n; - } - if ( i2n ) { - /*i2t = i2;*/ - i2 = i2n; - } - } - - /********************************************************* - compare non-isotopic stereo - *********************************************************/ - ret = CompareInchiStereo( i1->Stereo, i1->nFlags, i2->Stereo, i2->nFlags ); - if ( ret ) { - return ret; - } - /******************************************************* - do not switch back to tautomeric i1, i2 - *******************************************************/ - /* -- how to switch back -- - if ( i1t ) { - i1 = i1t; - i1t = NULL; - } - if ( i2t ) { - i2 = i2t; - i2t = NULL; - } - */ - /****************************************************** - compare isotopic non-tautomeric part - ******************************************************/ - if ( bCompareIsotopic ) { - if ( ret = i2->nNumberOfIsotopicAtoms - i1->nNumberOfIsotopicAtoms ) - return ret; - num = i1->nNumberOfIsotopicAtoms; - /* compare isotopic atoms */ - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)i2->IsotopicAtom[i].nAtomNumber - (int)i1->IsotopicAtom[i].nAtomNumber ) - return ret; - if ( ret = (int)i2->IsotopicAtom[i].nIsoDifference - (int)i1->IsotopicAtom[i].nIsoDifference ) - return ret; - } - /* compare isotopic H */ - /* if tautomeric comparison mode then here are compared only non-tautomeric H */ - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)i2->IsotopicAtom[i].nNum_T - (int)i1->IsotopicAtom[i].nNum_T ) - return ret; - if ( ret = (int)i2->IsotopicAtom[i].nNum_D - (int)i1->IsotopicAtom[i].nNum_D ) - return ret; - if ( ret = (int)i2->IsotopicAtom[i].nNum_H - (int)i1->IsotopicAtom[i].nNum_H ) - return ret; - } - /***************************************************** - compare isotopic tautomeric part - *****************************************************/ - if ( ret = i2->nNumberOfIsotopicTGroups - i1->nNumberOfIsotopicTGroups ) - return ret; - num = i1->nNumberOfIsotopicTGroups; - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)i2->IsotopicTGroup[i].nTGroupNumber - (int)i1->IsotopicTGroup[i].nTGroupNumber ) - return ret; - if ( ret = (int)i2->IsotopicTGroup[i].nNum_T - (int)i1->IsotopicTGroup[i].nNum_T ) - return ret; - if ( ret = (int)i2->IsotopicTGroup[i].nNum_D - (int)i1->IsotopicTGroup[i].nNum_D ) - return ret; - if ( ret = (int)i2->IsotopicTGroup[i].nNum_H - (int)i1->IsotopicTGroup[i].nNum_H ) - return ret; - } - - /**************************************************** - compare isotopic stereo - ****************************************************/ - ret = CompareInchiStereo( i1->StereoIsotopic, i1->nFlags, i2->StereoIsotopic, i2->nFlags ); - if ( ret ) { - return ret; - } - } - - /********************************************************** - compare charges: non-charged first, then in order of - ascending charges (negative first) - ***********************************************************/ - if ( i2->nTotalCharge && i1->nTotalCharge ) { - /* both are charged; smaller charges first */ - ret = (int)i1->nTotalCharge - (int)i2->nTotalCharge; - return ret; - } - if ( ret = (i1->nTotalCharge? 1:0) - (i2->nTotalCharge? 1:0) ) { - /* only one is charged; uncharged first */ - return ret; - } - /* stable sort */ - /*ret = p1->ord_number - p2->ord_number;*/ - - return ret; -} -/***********************************************************************/ -int CompINChINonTaut2(const void *p1, const void *p2) -{ - int ret; - ret = CompINChI2( (const INCHI_SORT *)p1, (const INCHI_SORT *)p2, TAUT_NON, 1 ); -#if ( CANON_FIXH_TRANS == 1 ) - if ( !ret ) { - /* to obtain canonical transposition 2004-05-10 */ - ret = CompINChI2( (const INCHI_SORT *)p1, (const INCHI_SORT *)p2, TAUT_YES, 1 ); - } -#endif - if ( !ret ) { - /* stable sort */ - ret = ((const INCHI_SORT *)p1)->ord_number - ((const INCHI_SORT *)p2)->ord_number; - } - return ret; -} -/***********************************************************************/ -int CompINChITaut2(const void *p1, const void *p2) -{ - int ret; - ret = CompINChI2( (const INCHI_SORT *)p1, (const INCHI_SORT *)p2, TAUT_YES, 1 ); -#if ( CANON_FIXH_TRANS == 1 ) - if ( !ret ) { - /* to obtain canonical transposition 2004-05-10 */ - ret = CompINChI2( (const INCHI_SORT *)p1, (const INCHI_SORT *)p2, TAUT_NON, 1 ); - } -#endif - if ( !ret ) { - /* stable sort */ - ret = ((const INCHI_SORT *)p1)->ord_number - ((const INCHI_SORT *)p2)->ord_number; - } - return ret; -} -/**********************************************************************************************/ -/* strrev from K&R is not in ANSI-compatible C library */ -void mystrrev( char *p ) -{ - char c, *q = p; - while( *q++ ) - ; - q -= 2; /* pointer to the last character */ - while ( p < q ) { - c = *q; /* swap */ - *q-- = *p; - *p++ = c; - } -} -/*****************************************************************************************/ -/* Find DFS order for CT(canon. numbers and Hs) output */ -/*****************************************************************************************/ - -static AT_NUMB *gDfs4CT_nDfsNumber; -static AT_NUMB *gDfs4CT_nNumDescendants; -static int gDfs4CT_nCurrentAtom; - -/**********************************************************************************************/ -static int CompareDfsDescendants4CT( const void *a1, const void *a2 ) -{ - int neigh1 = (int)*(const AT_RANK*)a1; - int neigh2 = (int)*(const AT_RANK*)a2; - if ( neigh1 > MAX_ATOMS ) { - if ( neigh2 > MAX_ATOMS ) { - return 0; - } - return 1; - } else - if ( neigh2 > MAX_ATOMS ) { - return -1; - } else { - AT_RANK nCurDfsNumber = gDfs4CT_nDfsNumber[gDfs4CT_nCurrentAtom]; - int nDesc1 = nCurDfsNumber > gDfs4CT_nDfsNumber[neigh1]? - 0 : (int)gDfs4CT_nNumDescendants[neigh1]; - int nDesc2 = nCurDfsNumber > gDfs4CT_nDfsNumber[neigh2]? - 0 : (int)gDfs4CT_nNumDescendants[neigh2]; - int ret; - if ( ret = nDesc1 - nDesc2 ) { - return ret; - } - return (int)neigh1 - (int)neigh2; /* canon. numbers difference */ - } -} -/**********************************************************************************************/ -/* sp_ATOM *at, AT_RANK *nRank, int num_atoms */ -AT_NUMB *GetDfsOrder4CT( AT_NUMB *LinearCT, int nLenCT, S_CHAR *nNum_H, int num_atoms, int nCtMode ) -{ - AT_NUMB *nStackAtom = NULL; - int nTopStackAtom=-1; - AT_NUMB *nNumDescendants = NULL; /* number of descendants incl. closures and the atom itself */ - AT_NUMB *nDfsNumber = NULL; - S_CHAR *cNeighNumb = NULL; - NEIGH_LIST *nl = NULL; - AT_NUMB nDfs; - int i, j, u, k, start, num_rings, nTotOutputStringLen; - AT_NUMB *nOutputString = NULL, cDelim; - int bCtPredecessors = (nCtMode & CT_MODE_PREDECESSORS); - - /* int nNumStartChildren; */ - - - /* allocate arrays */ - nStackAtom = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nStackAtom[0])); - nNumDescendants = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nNumDescendants[0])); - nDfsNumber = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nDfsNumber[0])); - cNeighNumb = (S_CHAR *) inchi_malloc(num_atoms*sizeof(cNeighNumb[0])); - nl = CreateNeighListFromLinearCT( LinearCT, nLenCT, num_atoms ); - /* check allocation */ - if ( !nStackAtom || !nNumDescendants || !nDfsNumber || !cNeighNumb || !nl ) { - /* ret = CT_OUT_OF_RAM; */ /* program error */ /* */ - goto exit_function; - } - if ( bCtPredecessors ) { - start = 0; - } else { - /* find DFS start vertex (atom) */ - for ( i = 1, start = 0; i < num_atoms; i ++ ) { - if ( nl[i][0] < nl[start][0] ) { /* index = nRank-1 */ - start = i; - } - } - } - /* - vertex information: - 1. Number of (forward edges) + (back edges, first visit -- ring closures): nl[i][0] - 2. Number of vertices traversed from this vertex, including the vertex: nNumDescendants[i] - 3. Each edge information: - a. forward edge (0) or back edge (1) indicator: nDfsNumber[i] > nDfsNumber[neigh] - b. neighbor at another end of the edge neigh = nl[i][k+1], k < i - - Total per edge: 2 + 2*(number of edges) - */ - - /* DFS initiation */ - u = start; /* start atom */ - nDfs = 0; - nTopStackAtom =-1; - memset( nDfsNumber, 0, num_atoms*sizeof(nDfsNumber[0])); - memset( nNumDescendants, 0, num_atoms*sizeof(nNumDescendants[0])); - memset( cNeighNumb, 0, num_atoms*sizeof(cNeighNumb[0])); - /* push the start atom on the stack */ - nDfsNumber[u] = ++nDfs; - if ( bCtPredecessors ) { - nNumDescendants[u] = 0; /* atom #1 has no predecessor */ - } else { - nNumDescendants[u] = 1; /* count itself as a descendant */ - } - nStackAtom[++nTopStackAtom] = (AT_NUMB)u; - /* nNumStartChildren = 0; */ - num_rings = 0; - - /* DFS */ - - do { - /* advance */ - while ( i=(int)nStackAtom[nTopStackAtom], j = (int)cNeighNumb[i]+1, (int)nl[i][0] >= j ) - /*while ( (int)nl[i=nStackAtom[nTopStackAtom]][0] >= (j = (int)cNeighNumb[i]+1) )*/ - /* replaced due to missing sequence point; undefined behavior, pointed by Geoffrey Hutchison */ - { - cNeighNumb[i] ++; - u = (int)nl[i][j]; /* jth neighbor of the vertex i */ - if ( !nDfsNumber[u] ) { - /* tree edge, 1st visit -- advance */ - /* put unexplored vertex u on the stack for further examination */ - nStackAtom[++nTopStackAtom] = (AT_NUMB)u; - nDfsNumber[u] = ++nDfs; - if ( bCtPredecessors ) { - nNumDescendants[u] = i+1; /* predecessor's rank */ - } else { - nNumDescendants[u] ++; /* count atom u as its descendant */ - } - } else - if ( nTopStackAtom && u != (int)nStackAtom[nTopStackAtom-1] && - /* back edge: u is not a predecessor of i */ - nDfsNumber[u] < nDfsNumber[i] ) { - /* Back edge, 1st visit: u is an ancestor of i (ring closure) */ - if ( !bCtPredecessors ) { - nNumDescendants[i] ++; /* count closures as descendants */ - } - num_rings ++; /* count ring closures */ - } else { - nl[i][j] = MAX_ATOMS+1; /* back edge, 2nd visit: mark as deleted */ - } - } - cNeighNumb[i] = 0; /* all neighbors of the ith atom have been - traversed; resore the neighbor counter */ - /* back up */ - if ( !bCtPredecessors && nTopStackAtom /* that is, i != start */) { - u = (int)nStackAtom[nTopStackAtom-1]; /* predecessor of i */ - nNumDescendants[u] += nNumDescendants[i]; /* add descendants */ - } - } while ( --nTopStackAtom >= 0 ); - - /* Sort the neighbors in ascending order so that: - primary key = number of descendants in the DFS tree; closure neighbor is 0 - secondary key = canonical number (here vertex number = canonical number - 1) - */ - - /* set static globals for the sorting: */ - gDfs4CT_nDfsNumber = nDfsNumber; - gDfs4CT_nNumDescendants = nNumDescendants; - gDfs4CT_nCurrentAtom = -1; - - /* sorting; deleted will be the last neighbors */ - for ( i = 0; i < num_atoms; i ++ ) { - if ( nl[i][0] > 1 ) { - gDfs4CT_nCurrentAtom = i; - insertions_sort( &nl[i][1], nl[i][0], sizeof(nl[i][1]), CompareDfsDescendants4CT ); - } - /* reduce number of neighbors to exclude deleted */ - for ( k = 0; k < nl[i][0] && nl[i][k+1] <= MAX_ATOMS; k ++ ) - ; - nl[i][0] = k; - } - - nTotOutputStringLen = 3*(num_atoms+num_rings+1); /* last 3 elements are a 'zero termination' */ - - if ( bCtPredecessors ) { - if ( nOutputString = (AT_RANK *)inchi_calloc( nTotOutputStringLen, sizeof(nOutputString[0]) ) ) { - cDelim = '-'; - for ( u = 0, k = -3 ; u < num_atoms; u ++ ) { - k += 3; - if ( k+6 > nTotOutputStringLen ) { - goto exit_error; /* program error */ - } - nOutputString[k] = nNumDescendants[u]? nNumDescendants[u] : MAX_ATOMS+1; - nOutputString[k+1] = nNum_H? 16+nNum_H[u]:0; - nOutputString[k+2] = k? ',' : '\0'; - for ( j = 1; j <= nl[u][0] && nDfsNumber[u] > nDfsNumber[i=nl[u][j]]; j ++ ) { - /* closures */ - k += 3; - if ( k+6 > nTotOutputStringLen ) { - goto exit_error; /* program error */ - } - nOutputString[k] = i+1; /* closure */ - nOutputString[k+1] = 0; - nOutputString[k+2] = cDelim; - } - } - } - } else { - if ( nNumDescendants ) { /* do not need anymore */ - inchi_free( nNumDescendants ); - nNumDescendants = NULL; - } - /* - the output string contains: - (num_atoms) atoms for the DFS (spanning) tree - (num_atoms-1) delimiters for the DFS (spanning) tree - 1 character for each atom that has 1 terminal hydrogen atoms - 2 characters for each atom that has 2-9 terminal hydrogen atoms - 3 characters for each atom that has 10-99 terminal hydrogen atoms, etc. - (num_rings) atoms for the ring closures - (num_rings) delimiters for the ring closures - */ - - if ( nOutputString = (AT_RANK *)inchi_calloc( nTotOutputStringLen, sizeof(nOutputString[0]) ) ) { - u = start; /* start atom */ - nTopStackAtom =-1; - memset( cNeighNumb, 0, num_atoms*sizeof(cNeighNumb[0])); - /* push the start atom on the stack */ - nStackAtom[++nTopStackAtom] = (AT_NUMB)u; - /* output the starting atom */ - k = 0; - nOutputString[k] = u+1; - nOutputString[k+1] = nNum_H? 16+nNum_H[u]:0; - nOutputString[k+2] = '\0'; - - do { - /* advance */ - while ( i=(int)nStackAtom[nTopStackAtom], j = (int)cNeighNumb[i]+1, (int)nl[i][0] >= j ) - /*while ( (int)nl[i=nStackAtom[nTopStackAtom]][0] >= (j = (int)cNeighNumb[i]+1) )*/ - /* replaced due to missing sequence point; undefined behavior, reported by Geoffrey Hutchison */ - { - k += 3; - if ( k+6 > nTotOutputStringLen ) { - goto exit_error; /* program error */ - } - cNeighNumb[i] ++; - u = (int)nl[i][j]; /* neighbor */ - - /* output neighbor's canonical number */ - nOutputString[k] = u+1; - - if ( nDfsNumber[u] > nDfsNumber[i] ) { - /* tree edge, 1st visit -- advance */ - /* put 'unexplored' vertex u on the stack */ - nStackAtom[++nTopStackAtom] = (AT_NUMB)u; - - /* output neighbor's number of H */ - nOutputString[k+1] = nNum_H? 16+nNum_H[u]:0; - } else { - nOutputString[k+1] = 0; - } - /* output a delimiter preceding the neighbor */ - if ( 1 < nl[i][0] ) { - if ( j == 1 ) { - cDelim = '('; - } else - if ( j == nl[i][0] ) { - cDelim = ')'; - } else { - cDelim = ','; - } - } else { - cDelim = '-'; - } - nOutputString[k+2] = cDelim; - } - cNeighNumb[i] = 0; - - /* back up: nothing else to do */ - } while ( --nTopStackAtom >= 0 ); - } - } - goto exit_function; - -exit_error: - if ( nOutputString ) { - inchi_free( nOutputString ); - nOutputString = NULL; - } - -exit_function: - if ( nStackAtom ) - inchi_free( nStackAtom ); - if ( nNumDescendants ) - inchi_free( nNumDescendants ); - if ( nDfsNumber ) - inchi_free( nDfsNumber ); - if ( cNeighNumb ) - inchi_free( cNeighNumb ); - if ( nl ) - FreeNeighList( nl ); - return nOutputString; -} -/**********************************************************************************************/ -int GetInpStructErrorType( INPUT_PARMS *ip, int err, char *pStrErrStruct, int num_inp_atoms ) -{ - if ( err && err == 9 ) - return _IS_ERROR; /* sdfile bypassed to $$$$ */ - if ( err && err < 30 ) - return _IS_FATAL; - if ( num_inp_atoms <= 0 || err ) { - if ( 98 == err && 0 == num_inp_atoms && ip->bAllowEmptyStructure ) - return _IS_WARNING; - return _IS_ERROR; - } - if ( pStrErrStruct[0] ) - return _IS_WARNING; - return _IS_OKAY; -} -/**********************************************************************************************/ -int ProcessStructError( INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *log_file, /*int err,*/ - char *pStrErrStruct, int nErrorType, - int *bXmlStructStarted, long num_inp, INPUT_PARMS *ip, char *pStr, int nStrLen ) -{ - int b_ok; -#ifdef TARGET_LIB_FOR_WINCHI - int bPlainText = (ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) && - (ip->bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW ) && - !(ip->bINChIOutputOptions & INCHI_OUT_XML); -#else - int bPlainText = 0; -#endif - if ( !bPlainText && *bXmlStructStarted <= 0 ) { - return nErrorType; - } - /* Fatal error, Error, Warning */ - if ( nErrorType ) { - if ( bPlainText ) { - if ( !(b_ok=OutputINChIPlainError( output_file, pStr, nStrLen, pStrErrStruct, nErrorType ) ) ) { - inchi_ios_eprint( log_file, "Cannot create message for error (structure #%ld.%s%s%s%s) Terminating.\n", - num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - } else { - inchi_ios_print( output_file, "\n" ); /* add a blank line after the WINCHI Window message */ - } - } else { - if ( !(b_ok=OutputINChIXmlError( output_file, pStr, nStrLen, 2, /*err,*/ pStrErrStruct, nErrorType ) ) ) { - inchi_ios_eprint( log_file, "Cannot create xml tag for error (structure #%ld.%s%s%s%s) Terminating.\n", - num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - } - if ( !b_ok || nErrorType == _IS_FATAL || nErrorType == _IS_ERROR ) { - /* close current structure output */ - if ( !OutputINChIXmlStructEndTag( output_file, pStr, nStrLen, 1 ) ) { - inchi_ios_eprint( log_file, "Cannot create end xml tag for structure #%ld.%s%s%s%s Terminating.\n", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - *bXmlStructStarted = -1; - b_ok = 0; - } else { - *bXmlStructStarted = 0; - } - } - } - return b_ok? nErrorType : _IS_FATAL; - } - return nErrorType; - -} - -#if ( TEST_RENUMB_ATOMS == 1 ) /* { */ -/***************************************************************************************/ -int CompareStereoINChI( INChI_Stereo *s1, INChI_Stereo *s2 ) -{ - if ( s1 == NULL && s2 == NULL ) - return 0; - if ( (s1 == NULL) ^ (s2 == NULL) ) - return 20; - - if ( s1->nNumberOfStereoCenters != s2->nNumberOfStereoCenters ) - return 21; - if ( s1->nNumberOfStereoCenters > 0 ) { - if ( memcmp( s1->nNumber, s2->nNumber, s1->nNumberOfStereoCenters*sizeof(s1->nNumber[0]) ) ) - return 22; - if ( memcmp( s1->t_parity, s2->t_parity, s1->nNumberOfStereoCenters*sizeof(s1->t_parity[0]) ) ) - return 23; - if ( s1->nNumberInv && s2->nNumberInv ) { - if ( memcmp( s1->nNumberInv, s2->nNumberInv, s1->nNumberOfStereoCenters*sizeof(s1->nNumber[0]) ) ) - return 28; - if ( memcmp( s1->t_parityInv, s2->t_parityInv, s1->nNumberOfStereoCenters*sizeof(s1->t_parity[0]) ) ) - return 29; - if ( s1->nCompInv2Abs != s2->nCompInv2Abs || - s1->bTrivialInv != s2->bTrivialInv ) { - return 30; - } - } else - if ( s1->nNumberInv || s2->nNumberInv ) { - return 31; - } - } - if ( s1->nNumberOfStereoBonds != s2->nNumberOfStereoBonds ) - return 24; - if ( s1->nNumberOfStereoBonds > 0 ) { - if ( memcmp( s1->nBondAtom1, s2->nBondAtom1, s1->nNumberOfStereoBonds*sizeof(s1->nBondAtom1[0]) ) ) - return 25; - if ( memcmp( s1->nBondAtom2, s2->nBondAtom2, s1->nNumberOfStereoBonds*sizeof(s1->nBondAtom2[0]) ) ) - return 26; - if ( memcmp( s1->b_parity, s2->b_parity, s1->nNumberOfStereoBonds*sizeof(s1->b_parity[0]) ) ) - return 27; - } - return 0; -} -/***************************************************************************************/ -int CompareINChI( INChI *i1, INChI *i2, INChI_Aux *a1, INChI_Aux *a2 ) -{ - int ret; - if ( i1 == NULL && i2 == NULL ) - return 0; - if ( (i1 == NULL) ^ (i2 == NULL) ) - return 1; - - if ( i1->nErrorCode == i2->nErrorCode ) { - if ( i1->nErrorCode ) - return 0; - } else { - return 2; - } - - if ( i1->nNumberOfAtoms != i2->nNumberOfAtoms ) - return 3; - if ( i1->nNumberOfAtoms > 0 ) { - if ( memcmp( i1->nAtom, i2->nAtom, i1->nNumberOfAtoms*sizeof(i1->nAtom[0]) ) ) - return 4; - if ( memcmp( i1->nNum_H, i2->nNum_H, i1->nNumberOfAtoms*sizeof(i1->nNum_H[0]) ) ) - return 5; - if ( i1->nNum_H_fixed && i2->nNum_H_fixed && - memcmp( i1->nNum_H_fixed, i2->nNum_H_fixed, i1->nNumberOfAtoms*sizeof(i1->nNum_H_fixed[0]) ) ) { - return 6; - } - if ( strcmp( i1->szHillFormula, i2->szHillFormula ) ) - return 7; - } - - if ( i1->lenConnTable != i2->lenConnTable ) - return 8; - if ( i1->lenConnTable > 0 && memcmp( i1->nConnTable, i2->nConnTable, i1->lenConnTable*sizeof(i1->nConnTable[0]) ) ) - return 9; - - if ( i1->lenTautomer != i2->lenTautomer ) - return 10; - if ( i1->lenTautomer > 0 && memcmp( i1->nTautomer, i2->nTautomer, i1->lenTautomer*sizeof(i1->nTautomer[0]) ) ) - return 11; - - if ( i1->nNumberOfIsotopicAtoms != i2->nNumberOfIsotopicAtoms ) - return 12; - if ( i1->nNumberOfIsotopicAtoms > 0 && memcmp( i1->IsotopicAtom, i2->IsotopicAtom, i1->nNumberOfIsotopicAtoms*sizeof(i1->IsotopicAtom[0]) ) ) - return 13; - - if ( i1->nNumberOfIsotopicTGroups != i2->nNumberOfIsotopicTGroups ) - return 14; - if ( i1->nNumberOfIsotopicTGroups > 0 && memcmp( i1->IsotopicTGroup, i2->IsotopicTGroup, i1->nNumberOfIsotopicTGroups*sizeof(i1->IsotopicTGroup[0]) ) ) - return 15; - if ( a1->nNumRemovedProtons != a2->nNumRemovedProtons ) - return 16; - if ( memcmp( a1->nNumRemovedIsotopicH, a2->nNumRemovedIsotopicH, sizeof(a1->nNumRemovedIsotopicH) ) ) - return 17; - if ( i1->nPossibleLocationsOfIsotopicH && i2->nPossibleLocationsOfIsotopicH ) { - if ( i1->nPossibleLocationsOfIsotopicH[0] != i2->nPossibleLocationsOfIsotopicH[0] || - memcmp(i1->nPossibleLocationsOfIsotopicH, i2->nPossibleLocationsOfIsotopicH, - sizeof(i1->nPossibleLocationsOfIsotopicH[0])*i1->nPossibleLocationsOfIsotopicH[0]) ) - return 18; - } else - if ( !i1->nPossibleLocationsOfIsotopicH != !i2->nPossibleLocationsOfIsotopicH ) { - return 19; - } - /* ret = 20..31 */ - if ( ret = CompareStereoINChI( i1->Stereo, i2->Stereo ) ) - return ret; - /* ret = 40..51 */ - if ( ret = CompareStereoINChI( i1->StereoIsotopic, i2->StereoIsotopic ) ) - return ret+20; - - return 0; -} -#endif /* } TEST_RENUMB_ATOMS == 1 */ -#if ( READ_INCHI_STRING == 1 ) /* { */ -/*************************************************************************************/ -int CompareReversedStereoINChI( INChI_Stereo *s1/* InChI from reversed struct */, INChI_Stereo *s2 /* input InChI */) -{ - if ( s1 == NULL && s2 == NULL ) - return 0; - if ( (s1 == NULL) ^ (s2 == NULL) ) { - INChI_Stereo *s = s1? s1 : s2; - if ( s->nNumberOfStereoCenters || s->nNumberOfStereoBonds ) { - return 20; /* Diff: Missing Stereo */ - } else { - return 0; - } - } - - if ( s1->nNumberOfStereoCenters != s2->nNumberOfStereoCenters ) - return 21; /* Diff: Number of sp3 stereocenters */ - if ( s1->nNumberOfStereoCenters > 0 ) { - if ( memcmp( s1->nNumber, s2->nNumber, s1->nNumberOfStereoCenters*sizeof(s1->nNumber[0]) ) ) - return 22; /* Diff: sp3 stereocenter locations */ - if ( memcmp( s1->t_parity, s2->t_parity, s1->nNumberOfStereoCenters*sizeof(s1->t_parity[0]) ) ) - return 23; /* Diff: sp3 stereocenter parities */ - if ( s1->nCompInv2Abs != s2->nCompInv2Abs && s1->nCompInv2Abs && s2->nCompInv2Abs ) - return 24; /* Diff: sp3 inversion */ - /* - if ( s1->nNumberInv && s2->nNumberInv ) { - if ( memcmp( s1->nNumberInv, s2->nNumberInv, s1->nNumberOfStereoCenters*sizeof(s1->nNumber[0]) ) ) - return 25; - if ( memcmp( s1->t_parityInv, s2->t_parityInv, s1->nNumberOfStereoCenters*sizeof(s1->t_parity[0]) ) ) - return 26; - if ( s1->nCompInv2Abs != s2->nCompInv2Abs || - s1->bTrivialInv != s2->bTrivialInv ) { - return 27; - } - } else - if ( s1->nNumberInv || s2->nNumberInv ) { - return 28; - } - */ - } - if ( s1->nNumberOfStereoBonds != s2->nNumberOfStereoBonds ) - return 25; /* Diff: Number of stereobonds */ - if ( s1->nNumberOfStereoBonds > 0 ) { - if ( memcmp( s1->nBondAtom1, s2->nBondAtom1, s1->nNumberOfStereoBonds*sizeof(s1->nBondAtom1[0]) ) ) - return 26; /* Diff: Stereobond 1st atom locations */ - if ( memcmp( s1->nBondAtom2, s2->nBondAtom2, s1->nNumberOfStereoBonds*sizeof(s1->nBondAtom2[0]) ) ) - return 27; /* Diff: Stereobond 2nd atom locations */ - if ( memcmp( s1->b_parity, s2->b_parity, s1->nNumberOfStereoBonds*sizeof(s1->b_parity[0]) ) ) - return 28; /* Diff: Stereobond parities */ - } - return 0; -} -/*************************************************************************************/ -int CompareReversedStereoINChI2( INChI_Stereo *s1/* InChI from reversed struct */, INChI_Stereo *s2 /* input InChI */, ICR *picr) -{ - int ret = 0; - int j1, j2, num_eq, num_dif, num_extra_undf, num_miss_undf, num_in1_only, num_in2_only; - int bAddSb = !(picr->num_sb_undef_in1_only + picr->num_sb_in1_only + picr->num_sb_in2_only); - int bAddSc = !(picr->num_sc_undef_in1_only + picr->num_sc_in1_only + picr->num_sc_in2_only); - - int nNumSc1 = s1? s1->nNumberOfStereoCenters : 0; - int nNumSc2 = s2? s2->nNumberOfStereoCenters : 0; - int nNumSb1 = s1? s1->nNumberOfStereoBonds : 0; - int nNumSb2 = s2? s2->nNumberOfStereoBonds : 0; - - if ( (nNumSc1 || nNumSc1) && - ( nNumSc1 != nNumSc2 || - memcmp( s1->nNumber, s2->nNumber, nNumSc1*sizeof(s1->nNumber[0] ) ) || - memcmp( s1->t_parity, s2->t_parity, nNumSc1*sizeof(s1->t_parity[0]) ) ) ) { - - num_eq = num_dif = num_extra_undf = num_miss_undf = num_in1_only = num_in2_only = 0; - for ( j1 = j2 = 0; j1 < nNumSc1 && j2 < nNumSc2; ) { - if ( s1->nNumber[j1] == s2->nNumber[j2] ) { - if ( s1->t_parity[j1] == s2->t_parity[j2] ) { - num_eq ++; - } else { - num_dif ++; - } - j1 ++; - j2 ++; - } else - if ( s1->nNumber[j1] < s2->nNumber[j2] ) { - num_in1_only ++; - if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { - num_extra_undf ++; - } - if ( bAddSc ) { - if ( picr->num_sc_in1_only < ICR_MAX_SC_IN1_ONLY ) - picr->sc_in1_only[picr->num_sc_in1_only ++] = j1; - if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { - if ( picr->num_sc_undef_in1_only < ICR_MAX_SC_UNDF ) - picr->sc_undef_in1_only[picr->num_sc_undef_in1_only ++] = j1; - } - } - j1 ++; - } else { - num_in2_only ++; - if ( s2->t_parity[j2] == AB_PARITY_UNDF ) { - num_miss_undf ++; - } - if ( bAddSc ) { - if ( picr->num_sc_in2_only < ICR_MAX_SC_IN2_ONLY ) - picr->sc_in2_only[picr->num_sc_in2_only ++] = j2; - if ( s2->t_parity[j2] == AB_PARITY_UNDF ) { - if ( picr->num_sc_undef_in2_only < ICR_MAX_SC_UNDF ) - picr->sc_undef_in2_only[picr->num_sc_undef_in2_only ++] = j1; - } - } - j2 ++; - } - } - while ( j1 < nNumSc1 ) { - if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { - num_extra_undf ++; - } - num_in1_only ++; - if ( bAddSc ) { - if ( picr->num_sc_in1_only < ICR_MAX_SC_IN1_ONLY ) - picr->sc_in1_only[picr->num_sc_in1_only ++] = j1; - if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { - if ( picr->num_sc_undef_in1_only < ICR_MAX_SC_UNDF ) - picr->sc_undef_in1_only[picr->num_sc_undef_in1_only ++] = j1; - } - } - j1 ++; - } - while ( j2 < nNumSc2 ) { - if ( s2->t_parity[j2] == AB_PARITY_UNDF ) { - num_miss_undf ++; - } - num_in2_only ++; - if ( bAddSc ) { - if ( picr->num_sc_in2_only < ICR_MAX_SC_IN2_ONLY ) - picr->sc_in2_only[picr->num_sc_in2_only ++] = j2; - } - j2 ++; - } - if ( num_dif ) { - ret |= IDIF_SC_PARITY; - } - if ( num_in1_only ) { - if ( num_extra_undf ) { - ret |= IDIF_SC_EXTRA_UNDF; - } - if ( num_in1_only != num_extra_undf ) { - ret |= IDIF_SC_EXTRA; - } - } - if ( num_in2_only ) { - if ( num_miss_undf ) { - ret |= IDIF_SC_MISS_UNDF; - } - if ( num_in2_only != num_miss_undf ) { - ret |= IDIF_SC_MISS; - } - } - } - if ( s1 && s2 && s1->nCompInv2Abs != s2->nCompInv2Abs && s1->nCompInv2Abs && s2->nCompInv2Abs ) { - ret |= IDIF_SC_INV; - } - - if ( (nNumSb1 || nNumSb2 ) && - (nNumSb1 != nNumSb2 || - memcmp( s1->nBondAtom1, s2->nBondAtom1, nNumSb1*sizeof(s1->nBondAtom1[0]) ) || - memcmp( s1->nBondAtom2, s2->nBondAtom2, nNumSb1*sizeof(s1->nBondAtom2[0]) ) || - memcmp( s1->b_parity, s2->b_parity, nNumSb1*sizeof(s1->b_parity[0]) ) ) ) { - - num_eq = num_dif = num_extra_undf = num_miss_undf = num_in1_only = num_in2_only = 0; - for ( j1 = j2 = 0; j1 < nNumSb1 && j2 < nNumSb2; ) { - if ( s1->nBondAtom1[j1] == s2->nBondAtom1[j2] && - s1->nBondAtom2[j1] == s2->nBondAtom2[j2] ) { - if ( s1->b_parity[j1] == s2->b_parity[j2] ) { - num_eq ++; - } else { - num_dif ++; - } - j1 ++; - j2 ++; - } else - if ( s1->nBondAtom1[j1] < s2->nBondAtom1[j2] || - s1->nBondAtom1[j1] == s2->nBondAtom1[j2] && s1->nBondAtom2[j1] < s2->nBondAtom2[j2]) { - num_in1_only ++; - if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { - num_extra_undf ++; - } - if ( bAddSb ) { - if ( picr->num_sb_in1_only < ICR_MAX_SB_IN1_ONLY ) - picr->sb_in1_only[picr->num_sb_in1_only ++] = j1; - if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { - if ( picr->num_sb_undef_in1_only < ICR_MAX_SB_UNDF ) - picr->sb_undef_in1_only[picr->num_sb_undef_in1_only ++] = j1; - } - } - j1 ++; - } else { - num_in2_only ++; - if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { - num_miss_undf ++; - } - if ( bAddSb ) { - if ( picr->num_sb_in2_only < ICR_MAX_SB_IN2_ONLY ) - picr->sb_in2_only[picr->num_sb_in2_only ++] = j2; - if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { - if ( picr->num_sb_undef_in2_only < ICR_MAX_SB_UNDF ) - picr->sb_undef_in2_only[picr->num_sb_undef_in2_only ++] = j1; - } - } - j2 ++; - } - } - while ( j1 < nNumSb1 ) { - num_in1_only ++; - if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { - num_extra_undf ++; - } - if ( bAddSb ) { - if ( picr->num_sb_in1_only < ICR_MAX_SB_IN1_ONLY ) - picr->sb_in1_only[picr->num_sb_in1_only ++] = j1; - if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { - if ( picr->num_sb_undef_in1_only < ICR_MAX_SB_UNDF ) - picr->sb_undef_in1_only[picr->num_sb_undef_in1_only ++] = j1; - } - } - j1 ++; - } - while ( j2 < nNumSb2 ) { - num_in2_only ++; - if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { - num_miss_undf ++; - } - if ( bAddSb ) { - if ( picr->num_sb_in2_only < ICR_MAX_SB_IN2_ONLY ) - picr->sb_in2_only[picr->num_sb_in2_only ++] = j2; - if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { - if ( picr->num_sb_undef_in2_only < ICR_MAX_SB_UNDF ) - picr->sb_undef_in2_only[picr->num_sb_undef_in2_only ++] = j1; - } - } - j2 ++; - } - if ( num_dif ) { - ret |= IDIF_SB_PARITY; - } - if ( num_in1_only ) { - if ( num_extra_undf ) { - ret |= IDIF_SB_EXTRA_UNDF; - } - if ( num_in1_only != num_extra_undf ) { - ret |= IDIF_SB_EXTRA; - } - } - if ( num_in2_only ) { - if ( num_miss_undf ) { - ret |= IDIF_SB_MISS_UNDF; - } - if ( num_in2_only != num_miss_undf ) { - ret |= IDIF_SB_MISS; - } - } - } - - return ret; -} -/*************************************************************************************/ -int CompareReversedINChI( INChI *i1 /* InChI from reversed struct */, INChI *i2 /* input InChI */, INChI_Aux *a1, INChI_Aux *a2 ) -{ - int ret; - if ( i1 == NULL && i2 == NULL ) - return 0; - if ( (i1 == NULL) ^ (i2 == NULL) ) - return 1; /* Diff: Missing InChI */ - - if ( i1->nErrorCode == i2->nErrorCode ) { - if ( i1->nErrorCode ) - return 0; - } else { - return 2; /* Diff: Error codes */ - } - if ( i1->bDeleted != i2->bDeleted ) { - return 1; /* Diff: Missing InChI */ - } - if ( i1->nNumberOfAtoms != i2->nNumberOfAtoms ) - return 3; /* Diff: Num. atoms */ - if ( i1->nNumberOfAtoms > 0 ) { - if ( memcmp( i1->nAtom, i2->nAtom, i1->nNumberOfAtoms*sizeof(i1->nAtom[0]) ) ) - return 4; /* Diff: Elements */ - if ( strcmp( i1->szHillFormula, i2->szHillFormula ) ) - return 7; /* Diff: Hill Formulas */ - if ( memcmp( i1->nNum_H, i2->nNum_H, i1->nNumberOfAtoms*sizeof(i1->nNum_H[0]) ) ) { - if ( i1->lenConnTable > 1 || i2->lenConnTable > 1 ) { - return 5; /* Diff: H Locations (mobile H present) */ - } else { - return 6; /* Diff: H Locations (no mobile H) */ - } - } - /* fixed H */ - if ( i1->nNum_H_fixed || i2->nNum_H_fixed ) { - int bHasFixedH1 = 0, bHasFixedH2 = 0, i, j1, j2; - if ( i1->nNum_H_fixed ) { - for ( i = 0; i < i1->nNumberOfAtoms; i ++ ) { - if ( i1->nNum_H_fixed[i] ) { - bHasFixedH1 ++; - } - } - } - if ( i2->nNum_H_fixed ) { - for ( i = 0; i < i2->nNumberOfAtoms; i ++ ) { - if ( i2->nNum_H_fixed[i] ) { - bHasFixedH2 ++; - } - } - } - /* count the differences */ - j1 = j2 = 0; - if ( bHasFixedH1 && !bHasFixedH2 ) { - for ( i = 0; i < i1->nNumberOfAtoms; i ++ ) { - if ( i1->nNum_H_fixed[i] > 0 ) { - j1 ++; - } else - if ( i1->nNum_H_fixed[i] < 0 ) { - j2 ++; - } - } - - return 18; /* Diff: Extra Fixed-H */ - } else - if ( !bHasFixedH1 && bHasFixedH2 ) { - for ( i = j1 = j2 = 0; i < i1->nNumberOfAtoms; i ++ ) { - if ( 0 > i2->nNum_H_fixed[i] ) { - j1 ++; - } else - if ( 0 < i2->nNum_H_fixed[i] ) { - j2 ++; - } - } - return 19; /* Diff: Missed Fixed-H */ - } else - if ( bHasFixedH1 && bHasFixedH2 && - memcmp( i1->nNum_H_fixed, i2->nNum_H_fixed, i1->nNumberOfAtoms*sizeof(i1->nNum_H_fixed[0]) ) ) { - for ( i = j1 = j2 = 0; i < i1->nNumberOfAtoms; i ++ ) { - if ( i1->nNum_H_fixed[i] > i2->nNum_H_fixed[i] ) { - j1 ++; - } else - if ( i1->nNum_H_fixed[i] < i2->nNum_H_fixed[i] ) { - j2 ++; - } - } - } - ret = (j1 && j2)? 20 : j1? 18 : j2? 19 : 0; - if ( ret ) { - return ret; /* 20 => Diff: NotEql Fixed-H */ - /* 19 => Diff: Missed Fixed-H (i1 has less) */ - /* 18 => Diff: Extra Fixed-H (i1 has more) */ - } - } - } - - if ( i1->lenConnTable != i2->lenConnTable ) - return 8; /* Diff: Connections length */ - if ( i1->lenConnTable > 0 && memcmp( i1->nConnTable, i2->nConnTable, i1->lenConnTable*sizeof(i1->nConnTable[0]) ) ) - return 9; /* Diff: Connections */ - /* output special cases: different number of t-groups, different sizes of t-groups, different endpoints */ - if ( i1->lenTautomer != i2->lenTautomer && (i1->lenTautomer > 1 || i2->lenTautomer > 1) ) - return 10; /* Diff: Mobile groups length */ /* in isotopic or deprotonated cases i1->lenTautomer == 1 && i1->nTautomer[0] = 0 */ - if ( (i1->lenTautomer > 1 && i2->lenTautomer > 1) && - memcmp( i1->nTautomer, i2->nTautomer, i1->lenTautomer*sizeof(i1->nTautomer[0]) ) ) - return 11; /* Diff: Mobile groups */ - - if ( i1->nNumberOfIsotopicAtoms != i2->nNumberOfIsotopicAtoms ) - return 12; /* Diff: Isotopic atoms number */ - if ( i1->nNumberOfIsotopicAtoms > 0 && memcmp( i1->IsotopicAtom, i2->IsotopicAtom, i1->nNumberOfIsotopicAtoms*sizeof(i1->IsotopicAtom[0]) ) ) - return 13; /* Diff: Isotopic atoms */ - if ( i1->nTotalCharge != i2->nTotalCharge ) - return 14; /* Diff: Charge */ -/* - if ( i1->nNumberOfIsotopicTGroups != i2->nNumberOfIsotopicTGroups ) - return 14; - if ( i1->nNumberOfIsotopicTGroups > 0 && memcmp( i1->IsotopicTGroup, i2->IsotopicTGroup, i1->nNumberOfIsotopicTGroups*sizeof(i1->IsotopicTGroup[0]) ) ) - return 15; -*/ - if ( a1 && a2 ) { - if ( a1->nNumRemovedProtons != a2->nNumRemovedProtons ) - return 16; /* Diff: Number of removed protons */ - if ( memcmp( a1->nNumRemovedIsotopicH, a2->nNumRemovedIsotopicH, sizeof(a1->nNumRemovedIsotopicH) ) ) - return 17; /* Diff: Removed isotopic H */ - } -/* - if ( i1->nPossibleLocationsOfIsotopicH && i2->nPossibleLocationsOfIsotopicH ) { - if ( i1->nPossibleLocationsOfIsotopicH[0] != i2->nPossibleLocationsOfIsotopicH[0] || - memcmp(i1->nPossibleLocationsOfIsotopicH, i2->nPossibleLocationsOfIsotopicH, - sizeof(i1->nPossibleLocationsOfIsotopicH[0])*i1->nPossibleLocationsOfIsotopicH[0]) ) - return 18; - } else - if ( !i1->nPossibleLocationsOfIsotopicH != !i2->nPossibleLocationsOfIsotopicH ) { - return 19; - } -*/ - /* ret = 20..31 => 40..51 */ - if ( ret = CompareReversedStereoINChI( i1->Stereo, i2->Stereo ) ) - return ret+20; - /* ret = 40..51 => 60..71 */ - - if ( !i2->StereoIsotopic && i2->Stereo && i1->StereoIsotopic && - 0 < (i1->StereoIsotopic->nNumberOfStereoBonds + i1->StereoIsotopic->nNumberOfStereoCenters) && - 0 == CompareReversedStereoINChI( i1->StereoIsotopic, i2->Stereo ) ) { - /* InChI from reversed structure does not contain fully duplicated isotopic stereo */ - ; - } else - - if ( ret = CompareReversedStereoINChI( i1->StereoIsotopic, i2->StereoIsotopic ) ) { - return ret+40; - } - - return 0; -} - -/*******************************************************************************/ -int CompareIcr( ICR *picr1, ICR *picr2, INCHI_MODE *pin1, INCHI_MODE *pin2, INCHI_MODE mask ) -{ - int nNumExtraBits1 = 0, nNumExtraBits2 = 0, bit1, bit2; - INCHI_MODE Flg1=picr1->flags, Flg2 = picr2->flags, cur_bit = 1, in1, in2; - int i, ret; - - /* compare flags */ - in1 = in2 = 0; - for ( i = 0; Flg1 || Flg2; i ++, Flg1 >>= 1, Flg2 >>= 1, cur_bit <<= 1 ) { - if ( !(mask & cur_bit) ) { - continue; - } - bit1 = Flg1 & 1; - bit2 = Flg2 & 1; - if ( bit1 && !bit2 ) { - in1 |= 1 << i; - nNumExtraBits1 ++; - } else - if ( !bit1 && bit2 ) { - in2 |= 1 << i; - nNumExtraBits2 ++; - } - } - if ( nNumExtraBits1 && !nNumExtraBits2 ) { - ret = 1; - } else - if ( !nNumExtraBits1 && nNumExtraBits2 ) { - ret = -1; - } else - if ( !in1 && !in2 ) { - ret = 0; - } else { - ret = 2; /* compare produced undefined results */ - } - if ( pin1 ) *pin1 = in1; - if ( pin2 ) *pin2 = in2; - /* more detailed compare not implemented */ - return ret; -} - -/*********************************************************************************************************/ -INCHI_MODE CompareReversedINChI2( INChI *i1 /* InChI from reversed struct */, INChI *i2 /* input InChI */, - INChI_Aux *a1, INChI_Aux *a2, ICR *picr, int *err ) -{ - INCHI_MODE ret = 0; - INChI_Stereo *Stereo1=NULL, *Stereo2=NULL; - int n1, n2, m, j, j1, j2, ret2, num_H1, num_H2; - - *err = 0; - - memset( picr, 0, sizeof(*picr) ); - - if ( i1 == NULL && i2 == NULL ) - return 0; - if ( (i1 == NULL) ^ (i2 == NULL) ) { - ret |= IDIF_PROBLEM; /* one InChI exists while another doesn't */ - goto exit_function; - } - - if ( i1->nErrorCode == i2->nErrorCode ) { - if ( i1->nErrorCode ) { - ret |= IDIF_PROBLEM; /* both InChI have same error codes */ - goto exit_function; - } - } else { - ret |= IDIF_PROBLEM; /* at least one InChI has an error code */ - goto exit_function; - } - - if ( i1->nNumberOfAtoms != i2->nNumberOfAtoms ) { - ret |= IDIF_NUM_AT; - goto exit_function; - } - if ( i1->nNumberOfAtoms > 0 ) { - if ( memcmp( i1->nAtom, i2->nAtom, i1->nNumberOfAtoms*sizeof(i1->nAtom[0]) ) ) { - ret |= IDIF_ATOMS; - goto exit_function; - } - /* IDIF_NON_TAUT_H, IDIF_MORE_FH, IDIF_LESS_FH */ - if ( memcmp( i1->nNum_H, i2->nNum_H, i1->nNumberOfAtoms*sizeof(i1->nNum_H[0]) ) ) { - ret |= IDIF_POSITION_H; - for ( j1 = 0; j1 < i1->nNumberOfAtoms; j1 ++ ) { - if ( i1->nNum_H[j1] != i2->nNum_H[j1] && picr->num_diff_pos_H < ICR_MAX_DIFF_FIXED_H ) { - picr->diff_pos_H_at[picr->num_diff_pos_H] = j1; - picr->diff_pos_H_nH[picr->num_diff_pos_H] = i1->nNum_H[j1] - i2->nNum_H[j1]; - picr->num_diff_pos_H ++; - } - } - } - /* fixed H */ - if ( i1->nNum_H_fixed || i2->nNum_H_fixed ) { - int bHasFixedH1 = 0, bHasFixedH2 = 0, i; - if ( i1->nNum_H_fixed ) { - for ( i = 0; i < i1->nNumberOfAtoms; i ++ ) { - if ( i1->nNum_H_fixed[i] ) { - bHasFixedH1 ++; - } - } - } - if ( i2->nNum_H_fixed ) { - for ( i = 0; i < i2->nNumberOfAtoms; i ++ ) { - if ( i2->nNum_H_fixed[i] ) { - bHasFixedH2 ++; - } - } - } - if ( bHasFixedH1 && !bHasFixedH2 ) { - for ( i = j = 0; i < i1->nNumberOfAtoms; i ++ ) { - if ( i1->nNum_H_fixed[i] ) { - if ( j < ICR_MAX_DIFF_FIXED_H ) { - picr->fixed_H_at1_more[j] = i; - picr->fixed_H_nH1_more[j] = i1->nNum_H_fixed[i]; - j ++; - } - } - } - picr->num_fixed_H1_more = j; - ret |= IDIF_MORE_FH; /* Extra Fixed-H */ - } else - if ( !bHasFixedH1 && bHasFixedH2 ) { - for ( i = j = 0; i < i2->nNumberOfAtoms; i ++ ) { - if ( i2->nNum_H_fixed[i] ) { - if ( j < ICR_MAX_DIFF_FIXED_H ) { - picr->fixed_H_at2_more[j] = i; - picr->fixed_H_nH2_more[j] = i2->nNum_H_fixed[i]; - j ++; - } - } - } - picr->num_fixed_H2_more = j; - ret |= IDIF_LESS_FH; /* Missed Fixed-H */ - } else - if ( bHasFixedH1 && bHasFixedH2 && - memcmp( i1->nNum_H_fixed, i2->nNum_H_fixed, i1->nNumberOfAtoms*sizeof(i1->nNum_H_fixed[0]) ) ) { - for ( i = j1 = j2 = 0; i < i1->nNumberOfAtoms; i ++ ) { - if ( i1->nNum_H_fixed[i] > i2->nNum_H_fixed[i] ) { - if ( j1 < ICR_MAX_DIFF_FIXED_H ) { - picr->fixed_H_at1_more[j1] = i; - picr->fixed_H_nH1_more[j1] = i1->nNum_H_fixed[i] - i2->nNum_H_fixed[i]; - j1 ++; - } - } else - if ( i1->nNum_H_fixed[i] < i2->nNum_H_fixed[i] ) { - if ( j2 < ICR_MAX_DIFF_FIXED_H ) { - picr->fixed_H_at2_more[j2] = i; - picr->fixed_H_nH2_more[j2] = i2->nNum_H_fixed[i] - i1->nNum_H_fixed[i]; - j2 ++; - } - } - } - ret |= (j1? IDIF_MORE_FH:0) | (j2? IDIF_LESS_FH:0); - picr->num_fixed_H1_more = j1; - picr->num_fixed_H2_more = j2; - } - } - } - /* compare formulas and H */ - num_H1 = 0; - num_H2 = 0; - ret2 = CompareHillFormulasNoH( i1->szHillFormula, i2->szHillFormula, &num_H1, &num_H2 ); - picr->tot_num_H1 = num_H1; - picr->tot_num_H2 = num_H2; - if ( ret2 ) { - ret |= IDIF_NUM_EL; - goto exit_function; - } - if ( num_H1 > num_H2 ) { - ret |= IDIF_MORE_H; - } - if ( num_H1 < num_H2 ) { - ret |= IDIF_LESS_H; - } - - if ( i1->lenConnTable != i2->lenConnTable ) { - ret |= IDIF_CON_LEN; - goto exit_function; - } else - if ( i1->lenConnTable > 0 && memcmp( i1->nConnTable, i2->nConnTable, i1->lenConnTable*sizeof(i1->nConnTable[0]) ) ) { - ret |= IDIF_CON_TBL; - goto exit_function; - } - /* output special cases: different number of t-groups, different sizes of t-groups, different endpoints */ - /* in isotopic or deprotonated cases i1->lenTautomer == 1 && i1->nTautomer[0] = 0 */ -/* - if ( i1->lenTautomer != i2->lenTautomer && (i1->lenTautomer > 1 || i2->lenTautomer > 1) ) { - ret |= IDIF_TAUT_LEN; - } -*/ - /* compare number of t-groups */ - n1 = i1->lenTautomer? i1->nTautomer[0] : 0; - n2 = i2->lenTautomer? i2->nTautomer[0] : 0; - if ( !n1 && n2 ) { - ret |= IDIF_NO_TAUT; - } else - if ( n1 && !n2 ) { - ret |= IDIF_WRONG_TAUT; - } else - if ( n1 == 1 && n2 > 1 ) { - ret |= IDIF_SINGLE_TG; - } else - if ( n1 > 1 && n2 == 1 ) { - ret |= IDIF_MULTIPLE_TG; - } else - if ( n1 != n2 ) { - ret |= IDIF_NUM_TG; - } - if ( n1 || n2 ) { - /* number of endpoints */ - int num1 = 0, num2 = 0, num_M1=0, num_M2=0; - int len, num_eq, num_in1_only, num_in2_only; - AT_NUMB *pe1 = (AT_NUMB *) inchi_malloc( (i1->lenTautomer+1) * sizeof(pe1[0]) ); - AT_NUMB *pe2 = (AT_NUMB *) inchi_malloc( (i2->lenTautomer+1) * sizeof(pe2[0]) ); - num_H1 = num_H2=0; - /* collect endpoints, H, (-) */ - if ( !pe1 || !pe2 ) { - if ( pe1 ) inchi_free( pe1 ); - if ( pe2 ) inchi_free( pe2 ); - *err = -1; /* allocation error */ - goto exit_function; - } - for ( m = 1; m < i1->lenTautomer; m += len ) { - len = i1->nTautomer[m ++]; - num_H1 += i1->nTautomer[m]; - num_M1 += i1->nTautomer[m+1]; - for ( j = 2; j < len; j ++ ) { - pe1[num1 ++] = i1->nTautomer[m + j]; - } - } - for ( m = 1; m < i2->lenTautomer; m += len ) { - len = i2->nTautomer[m ++]; - num_H2 += i2->nTautomer[m]; - num_M2 += i2->nTautomer[m+1]; - for ( j = 2; j < len; j ++ ) { - pe2[num2 ++] = i2->nTautomer[m + j]; - } - } - picr->num_taut_H1 = num_H1; - picr->num_taut_H2 = num_H2; - picr->num_taut_M1 = num_M1; - picr->num_taut_M2 = num_M2; - /* sort endpoints */ - insertions_sort_AT_RANK( pe1, num1 ); - insertions_sort_AT_RANK( pe2, num2 ); - /* compare */ - /* - if ( num1 < num2 ) { - ret |= IDIF_LESS_TG_ENDP; - } else - if ( num1 > num2 ) { - ret |= IDIF_MORE_TG_ENDP; - } - */ - /* compare all */ - num_eq = num_in1_only = num_in2_only = 0; - for ( j1 = j2 = 0; j1 < num1 && j2 < num2; ) { - if( pe1[j1] == pe2[j2] ) { - j1 ++; - j2 ++; - num_eq ++; - } else - if ( pe1[j1] < pe2[j2] ) { /* BC: fixed, was pe2[j1] 2006-03-27 */ - if ( picr->num_endp_in1_only < ICR_MAX_ENDP_IN1_ONLY ) { - picr->endp_in1_only[picr->num_endp_in1_only ++] = pe1[j1]; - } - j1 ++; - num_in1_only ++; - } else { - if ( picr->num_endp_in2_only < ICR_MAX_ENDP_IN2_ONLY ) { - picr->endp_in2_only[picr->num_endp_in2_only ++] = pe2[j2]; - } - j2 ++; - num_in2_only ++; - } - } - while ( j1 < num1 ) { - if ( picr->num_endp_in1_only < ICR_MAX_ENDP_IN1_ONLY ) { - picr->endp_in1_only[picr->num_endp_in1_only ++] = pe1[j1]; - } - j1 ++; - num_in1_only ++; - } - while ( j2 < num2 ) { - if ( picr->num_endp_in2_only < ICR_MAX_ENDP_IN2_ONLY ) { - picr->endp_in2_only[picr->num_endp_in2_only ++] = pe2[j2]; - } - j2 ++; - num_in2_only ++; - } - if ( num_in1_only ) { - ret |= IDIF_EXTRA_TG_ENDP; - } - if ( num_in2_only ) { - ret |= IDIF_MISS_TG_ENDP; - } - if ( !num_in1_only && !num_in2_only && num_eq ) { - ; /* same t-groups endpoints */ - } else { - ret |= IDIF_DIFF_TG_ENDP; - } - inchi_free( pe1 ); - inchi_free( pe2 ); - - } - - if ( (i1->lenTautomer > 1 && i2->lenTautomer > 1) && - ( i1->lenTautomer != i2->lenTautomer || - memcmp( i1->nTautomer, i2->nTautomer, i1->lenTautomer*sizeof(i1->nTautomer[0]) ) ) ) - ret |= IDIF_TG; - - if ( i1->nNumberOfIsotopicAtoms != i2->nNumberOfIsotopicAtoms ) { - ret |= IDIF_NUM_ISO_AT; - } else - if ( i1->nNumberOfIsotopicAtoms > 0 && memcmp( i1->IsotopicAtom, i2->IsotopicAtom, i1->nNumberOfIsotopicAtoms*sizeof(i1->IsotopicAtom[0]) ) ) - ret |= IDIF_ISO_AT; - if ( i1->nTotalCharge != i2->nTotalCharge ) - ret |= IDIF_CHARGE; - if ( a1 && a1->nNumRemovedProtons && (!a2 || a2->nNumRemovedProtons != a1->nNumRemovedProtons) ) { - ret |= IDIF_REM_PROT; - } - if ( a1 && (!a2 || - a2->nNumRemovedIsotopicH[0] != a1->nNumRemovedIsotopicH[0] || - a2->nNumRemovedIsotopicH[1] != a1->nNumRemovedIsotopicH[1] || - a2->nNumRemovedIsotopicH[2] != a1->nNumRemovedIsotopicH[2]) ) { - ret |= IDIF_REM_ISO_H; - } - -/* - if ( i1->nPossibleLocationsOfIsotopicH && i2->nPossibleLocationsOfIsotopicH ) { - if ( i1->nPossibleLocationsOfIsotopicH[0] != i2->nPossibleLocationsOfIsotopicH[0] || - memcmp(i1->nPossibleLocationsOfIsotopicH, i2->nPossibleLocationsOfIsotopicH, - sizeof(i1->nPossibleLocationsOfIsotopicH[0])*i1->nPossibleLocationsOfIsotopicH[0]) ) - return 18; - } else - if ( !i1->nPossibleLocationsOfIsotopicH != !i2->nPossibleLocationsOfIsotopicH ) { - return 19; - } -*/ - if ( i1->StereoIsotopic && - i1->StereoIsotopic->nNumberOfStereoBonds + i1->StereoIsotopic->nNumberOfStereoCenters ) { - Stereo1 = i1->StereoIsotopic; - } else { - Stereo1 = i1->Stereo; - } - if ( i2->StereoIsotopic && - i2->StereoIsotopic->nNumberOfStereoBonds + i2->StereoIsotopic->nNumberOfStereoCenters ) { - Stereo2 = i2->StereoIsotopic; - } else { - Stereo2 = i2->Stereo; - } - ret |= CompareReversedStereoINChI2( Stereo1, Stereo2, picr ); - -exit_function: - - picr->flags = ret; - - return ret; -} -#endif /* } READ_INCHI_STRING */ -/***************************************************************************************/ -int Create_INChI( INChI **ppINChI, INChI_Aux **ppINChI_Aux, ORIG_ATOM_DATA *orig_inp_data, /* not used */ - inp_ATOM *inp_at, INP_ATOM_DATA *out_norm_data[2], - int num_inp_at, INCHI_MODE nUserMode, - INCHI_MODE *pbTautFlags, INCHI_MODE *pbTautFlagsDone, - struct tagInchiTime *ulMaxTime, T_GROUP_INFO *ti_out, char *pStrErrStruct) -{ -/* -#define NON_TAUT 0 -#define TAUT 1 -*/ - sp_ATOM *at[TAUT_NUM]; /* at[0]=>non-tautomeric, at[1]=>tautomeric */ - /* inp_ATOM *out_norm_taut_at, *out_norm_nontaut_at; */ - int i, n1, n2, num_atoms, num_at_tg, num_removed_H, num_removed_H_taut=0, ret=0, ret2=0; - INCHI_MODE nMode=0; - T_GROUP_INFO vt_group_info; - T_GROUP_INFO vt_group_info_orig; - T_GROUP_INFO * /*const*/ t_group_info = &vt_group_info; - T_GROUP_INFO * /*const*/ t_group_info_orig = &vt_group_info_orig; - - CANON_STAT CS, CS2; - CANON_STAT *pCS = &CS; - CANON_STAT *pCS2 = &CS2; /* save all allocations to avoid memory leaks in case Canon_INChI() removes the pointer */ - - ATOM_SIZES s[TAUT_NUM]; - - BCN Bcn; - BCN *pBCN = &Bcn; - - int bHasIsotopicAtoms = 0; - int bMayHaveStereo = 0; - int num_taut_at = 0; - - inp_ATOM *out_at = NULL; /*, *norm_at_fixed_bonds[TAUT_NUM]; */ /* = {out_norm_nontaut_at, out_norm_taut_at} ; */ - INChI *pINChI=NULL; /* added initialization 2006-03 */ - INChI_Aux *pINChI_Aux=NULL; /* added initialization 2006-03 */ - int bPointedEdgeStereo = ((TG_FLAG_POINTED_EDGE_STEREO & *pbTautFlags)? PES_BIT_POINT_EDGE_STEREO:0) - | ((TG_FLAG_PHOSPHINE_STEREO & *pbTautFlags)? PES_BIT_PHOSPHINE_STEREO :0) - | ((TG_FLAG_ARSINE_STEREO & *pbTautFlags)? PES_BIT_ARSINE_STEREO :0) - | ((TG_FLAG_FIX_SP3_BUG & *pbTautFlags)? PES_BIT_FIX_SP3_BUG :0); - INCHI_MODE bTautFlags = (*pbTautFlags & (~(INCHI_MODE)TG_FLAG_ALL_TAUTOMERIC) ); - INCHI_MODE bTautFlagsDone = (*pbTautFlagsDone /*& (~(INCHI_MODE)TG_FLAG_ALL_TAUTOMERIC) */); -#if ( bRELEASE_VERSION == 0 ) - int bExtract = 0; /* EXTR_HAS_ATOM_WITH_DEFINED_PARITY; */ -#endif - -/*^^^ */ - int bFixIsoFixedH = 0; - int bFixTermHChrg = 0; - -#if ( TEST_RENUMB_ATOMS == 1 ) - long ulNormTime=0; - long ulCanonTime=0, ulCanonTime2=0; - - inchiTime ulNormTimeStart; - inchiTime ulCanonTimeStart; - - InchiTimeGet( &ulNormTimeStart ); -#endif - - /* vABParityUnknown holds actual value of an internal constant signifying */ - /* unknown parity: either the same as for undefined parity (default==standard) */ - /* or a specific one (non-std; requested by SLUUD switch). */ - int vABParityUnknown = AB_PARITY_UNDF; - if ( 0 != ( nUserMode & REQ_MODE_DIFF_UU_STEREO) ) - { - /* Make labels for unknown and undefined stereo different */ - vABParityUnknown = AB_PARITY_UNKN; - } - - - -/*^^^ */ -#if ( FIX_ISO_FIXEDH_BUG == 1 ) - if (TG_FLAG_FIX_ISO_FIXEDH_BUG & *pbTautFlags) - bFixIsoFixedH = 1; -#endif -#if ( FIX_TERM_H_CHRG_BUG == 1 ) - if (TG_FLAG_FIX_TERM_H_CHRG_BUG & *pbTautFlags) - bFixTermHChrg = 1; -#endif -/*^^^ */ - - memset( s, 0, sizeof(s) ); - if ( pBCN ) { - memset( pBCN, 0, sizeof( pBCN[0] ) ); - } - memset( t_group_info, 0, sizeof(*t_group_info) ); - memset( t_group_info_orig, 0, sizeof(*t_group_info_orig) ); - /*norm_at[TAUT_NON] = out_norm_data[TAUT_NON]->at; *//* output normalized non-tautomeric component */ - /*norm_at[TAUT_YES] = out_norm_data[TAUT_YES]->at; *//* output normalized tautomeric component */ - /*norm_at_fixed_bonds[TAUT_NON] = NULL;*/ - /*norm_at_fixed_bonds[TAUT_YES] = out_norm_data[TAUT_YES]->at_fixed_bonds;*/ - for ( i = 0; i < TAUT_NUM; i ++ ) { - if ( out_norm_data[i]->at ) { - if ( !(at[i] = (sp_ATOM *) inchi_malloc( num_inp_at * sizeof(*at[0]) ) ) ) { - ret = -1; - } - } else { - at[i] = NULL; - } - } - if ( !out_norm_data[TAUT_NON]->at && !out_norm_data[TAUT_YES]->at || !inp_at || ret ) { - ret = -1; - goto exit_function; - } - /* the first struct to process: tautomeric if exists else non-tautomeric */ - out_at = out_norm_data[TAUT_YES]->at? out_norm_data[TAUT_YES]->at : out_norm_data[TAUT_NON]->at; - /* copy the input structure to be normalized to the buffer for the normalization data */ - memcpy( out_at, inp_at, num_inp_at*sizeof(out_at[0]) ); - - /* tautomeric groups setting */ - t_group_info->bIgnoreIsotopic = 0; /* include tautomeric group isotopic info in MarkTautomerGroups() */ - t_group_info->bTautFlags = *pbTautFlags; - t_group_info->bTautFlagsDone = *pbTautFlagsDone; - - /* Preprocess the structure; here THE NUMBER OF ATOMS MAY BE REDUCED */ - /* ??? Ambiguity: H-D may become HD or DH (that is, H+implicit D or D+implicit H) */ - if ( TG_FLAG_H_ALREADY_REMOVED & bTautFlags ) { - INP_ATOM_DATA *out_norm_data1 = out_norm_data[TAUT_YES]->at? out_norm_data[TAUT_YES] : - out_norm_data[TAUT_NON]->at? out_norm_data[TAUT_NON] : NULL; - if ( out_norm_data1 ) { - num_at_tg = - num_atoms = out_norm_data1->num_at - out_norm_data1->num_removed_H; - num_removed_H = out_norm_data1->num_removed_H; - t_group_info->tni.nNumRemovedExplicitH = num_removed_H; - } else { - ret = -1; - goto exit_function; - } - } else { - num_at_tg = - num_atoms = remove_terminal_HDT( num_inp_at, out_at, bFixTermHChrg ); - num_removed_H = num_inp_at - num_atoms; - t_group_info->tni.nNumRemovedExplicitH = num_removed_H; - add_DT_to_num_H( num_atoms, out_at ); - } - /*fix_odd_things( num_atoms, out_at );*/ -#if ( FIND_RING_SYSTEMS == 1 ) - MarkRingSystemsInp( out_at, num_atoms, 0 ); -#endif - /* duplicate the preprocessed structure so that all supplied out_norm_data[]->at buffers are filled */ - if ( out_at != out_norm_data[TAUT_YES]->at && out_norm_data[TAUT_YES]->at ) { - memcpy( out_norm_data[TAUT_YES]->at, out_at, num_inp_at*sizeof(out_at[0]) ); - } - if ( out_norm_data[TAUT_YES]->at_fixed_bonds && out_norm_data[TAUT_YES]->at ) { - memcpy( out_norm_data[TAUT_YES]->at_fixed_bonds, out_at, num_inp_at*sizeof(out_at[0]) ); - } - if ( out_at != out_norm_data[TAUT_NON]->at && out_norm_data[TAUT_NON]->at ) { - memcpy( out_norm_data[TAUT_NON]->at, out_at, num_inp_at*sizeof(out_at[0]) ); - } - - /******************************************************************************* - * ??? not true ??? duplicate inp_at and keep inp_at[] unchanged after terminal hydrogens removal - * set stereo parities in taut_at[], non_taut_at[] - * obtain max. lenghts of the name stereo parts - * Ignore absence/presence of isotopic stereo for now - * mark isotopic atoms - *******************************************************************************/ - if ( out_norm_data[TAUT_YES]->at && at[TAUT_YES] ) { - /* final normalization of possibly tautomeric structure */ - ret = mark_alt_bonds_and_taut_groups ( out_norm_data[TAUT_YES]->at, out_norm_data[TAUT_YES]->at_fixed_bonds, num_atoms, - t_group_info, NULL, NULL ); - if ( ret < 0 ) { - goto exit_function;/* out of RAM or other normalization problem */ - } - num_taut_at = ret; /* number of atoms without removed H? */ - num_removed_H_taut = t_group_info->tni.nNumRemovedExplicitH; - out_norm_data[TAUT_YES]->num_at = num_atoms + num_removed_H_taut; /* protons might have been removed */ - out_norm_data[TAUT_YES]->num_removed_H = num_removed_H_taut; - out_norm_data[TAUT_YES]->nNumRemovedProtons += t_group_info->tni.nNumRemovedProtons; - for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { - out_norm_data[TAUT_YES]->nNumRemovedProtonsIsotopic[i] += t_group_info->tni.nNumRemovedProtonsIsotopic[i] /*+ t_group_info->num_iso_H[i]*/; - out_norm_data[TAUT_YES]->num_iso_H[i] += t_group_info->num_iso_H[i]; - } - /* mark deleted isolated tautomeric H(+) */ - if ( num_taut_at == 1 && out_norm_data[TAUT_YES]->at[0].at_type == ATT_PROTON && - t_group_info && t_group_info->tni.nNumRemovedProtons == 1 ) { - out_norm_data[TAUT_YES]->bDeleted = 1; - FreeInpAtom( &out_norm_data[TAUT_YES]->at_fixed_bonds ); - } else - if ( (t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) && - out_norm_data[TAUT_YES]->at_fixed_bonds) { - out_norm_data[TAUT_YES]->bTautPreprocessed = 1; - } - /* - if ( !(t_group_info->tni.bNormalizationFlags & (FLAG_NORM_CONSIDER_TAUT & ~FLAG_PROTON_SINGLE_REMOVED)) && - out_norm_data[TAUT_YES]->at_fixed_bonds) { - FreeInpAtom( &out_norm_data[TAUT_YES]->at_fixed_bonds ); - } - */ - /*out_norm_data[TAUT_YES]->num_removed_H = num_removed_H_taut;*/ - out_norm_data[TAUT_YES]->bTautFlags = *pbTautFlags = t_group_info->bTautFlags; - out_norm_data[TAUT_YES]->bTautFlagsDone = *pbTautFlagsDone = t_group_info->bTautFlagsDone; - out_norm_data[TAUT_YES]->bNormalizationFlags = t_group_info->tni.bNormalizationFlags; - /* create internal sp_ATOM at[] out of out_norm_data[]->at */ - inp2spATOM( out_norm_data[TAUT_YES]->at, num_inp_at, at[TAUT_YES] ); - /* set stereo parities to at[]; nUserMode: accept alt. stereo bonds, min ring size */ - ret = set_stereo_parity( out_norm_data[TAUT_YES]->at, at[TAUT_YES], num_taut_at, num_removed_H_taut, - &s[TAUT_YES].nMaxNumStereoAtoms, &s[TAUT_YES].nMaxNumStereoBonds, nUserMode, - bPointedEdgeStereo, vABParityUnknown ); -#if ( bRELEASE_VERSION == 0 ) - if ( 0 < ret ) { - bExtract |= EXTR_HAS_ATOM_WITH_DEFINED_PARITY; - } - if ( t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT ) { - bExtract |= EXTR_TAUT_TREATMENT_CHARGES; - } -#endif - if ( RETURNED_ERROR( ret ) ) { - goto exit_function; /* stereo bond error */ - } - s[TAUT_YES].bMayHaveStereo = (s[TAUT_YES].nMaxNumStereoAtoms || s[TAUT_YES].nMaxNumStereoBonds); - /* - * mark isotopic atoms and atoms that have non-tautomeric - * isotopic terminal hydrogen atoms 1H, 2H(D), 3H(T) - */ - s[TAUT_YES].num_isotopic_atoms = set_atom_iso_sort_keys( num_taut_at, at[TAUT_YES], t_group_info, - &s[TAUT_YES].bHasIsotopicTautGroups ); - /************************************************************************** - * prepare tautomeric (if no tautomerism found then prepare non-tautomeric) - * structure for canonicalizaton: - ************************************************************************** - * remove t-groups that have no H, - * remove charges from t-groups if requested - * renumber t-groups and find final t_group_info->num_t_groups - * add to t-groups lists of endpoints tgroup->nEndpointAtomNumber[] - * calculate length of the t-group part of the connection table - **************************************************************************/ - s[TAUT_YES].nLenLinearCTTautomer = CountTautomerGroups( at[TAUT_YES], num_taut_at, t_group_info ); - if ( RETURNED_ERROR(s[TAUT_YES].nLenLinearCTTautomer) ) { /* added error treatment 9-11-2003 */ - ret = s[TAUT_YES].nLenLinearCTTautomer; - goto exit_function; - /* error has happened; no breakpoint here - s[TAUT_YES].nLenLinearCTTautomer = 0; - */ - } else - if ( s[TAUT_YES].nLenLinearCTTautomer > 0 ) { - num_at_tg = num_taut_at+t_group_info->num_t_groups; - /* ??? -not true- create t_group_info_orig for multiple calls with atom renumbering */ - make_a_copy_of_t_group_info( t_group_info_orig /* dest*/, t_group_info /* source*/ ); - /* mark isotopic tautomer groups: calculate t_group->iWeight */ - s[TAUT_YES].nLenLinearCTIsotopicTautomer=set_tautomer_iso_sort_keys( t_group_info ); - if ( s[TAUT_YES].nLenLinearCTIsotopicTautomer < 0 ) { - /* ??? -error cannot happen- error has happened; no breakpoint here */ - s[TAUT_YES].nLenLinearCTIsotopicTautomer = 0; - } - out_norm_data[TAUT_YES]->bTautomeric = s[TAUT_YES].nLenLinearCTTautomer; - } - /* new variable: s[TAUT_YES].nLenCT introduced 7-22-2002 */ - GetCanonLengths( num_taut_at, at[TAUT_YES], &s[TAUT_YES], t_group_info ); - } - if ( out_norm_data[TAUT_NON]->at && out_norm_data[TAUT_YES]->at && at[TAUT_NON] && !s[TAUT_YES].nLenLinearCTTautomer ) { - /* the structure is non-tautomeric: use tautomeric treatment results only for it */ - inchi_free( at[TAUT_NON] ); - at[TAUT_NON] = NULL; - } else - if ( !out_norm_data[TAUT_NON]->at && out_norm_data[TAUT_YES]->at && - !at[TAUT_NON] && at[TAUT_YES] && !s[TAUT_YES].nLenLinearCTTautomer ) { - /* requested tautomeric; found non-tautomeric; it is located in out_norm_data[TAUT_YES]->at */ - out_norm_data[TAUT_YES]->bTautomeric = 0; - } else - if ( out_norm_data[TAUT_NON]->at && at[TAUT_NON] ) { - /* the structure needs non-tautomeric treatment: final normalization of non-tautomeric structure */ - ret = mark_alt_bonds_and_taut_groups ( out_norm_data[TAUT_NON]->at, NULL, num_atoms, NULL, &bTautFlags, &bTautFlagsDone ); - if ( ret < 0 ) { - goto exit_function; /* out of RAM or other normalization problem */ - } - out_norm_data[TAUT_NON]->num_at = num_atoms + num_removed_H; - out_norm_data[TAUT_NON]->num_removed_H = num_removed_H; - out_norm_data[TAUT_NON]->bTautFlags = *pbTautFlags; - out_norm_data[TAUT_NON]->bTautFlagsDone = *pbTautFlagsDone; - out_norm_data[TAUT_NON]->bNormalizationFlags = 0; - /* create internal sp_ATOM at[] out of out_norm_data[]->at */ - inp2spATOM( out_norm_data[TAUT_NON]->at, num_inp_at, at[TAUT_NON] ); - /* set stereo parities to at[]; nUserMode: accept alt. stereo bonds, min ring size */ - ret = set_stereo_parity( out_norm_data[TAUT_NON]->at, at[TAUT_NON], num_atoms, num_removed_H, - &s[TAUT_NON].nMaxNumStereoAtoms, &s[TAUT_NON].nMaxNumStereoBonds, nUserMode, - bPointedEdgeStereo, vABParityUnknown ); -#if ( bRELEASE_VERSION == 0 ) - if ( 0 < ret ) { - bExtract |= EXTR_HAS_ATOM_WITH_DEFINED_PARITY; - } -#endif - if ( RETURNED_ERROR( ret ) ) { - goto exit_function; /* stereo bond error */ - } - s[TAUT_NON].bMayHaveStereo = (s[TAUT_NON].nMaxNumStereoAtoms || s[TAUT_NON].nMaxNumStereoBonds); - /* - * mark isotopic atoms and atoms that have non-tautomeric - * isotopic terminal hydrogen atoms 1H, 2H(D), 3H(T) - */ - s[TAUT_NON].num_isotopic_atoms = set_atom_iso_sort_keys( num_atoms, at[TAUT_NON], NULL, NULL ); - GetCanonLengths( num_atoms, at[TAUT_NON], &s[TAUT_NON], NULL); - out_norm_data[TAUT_NON]->bTautomeric = 0; - } - - /**********************************************************/ - /* common */ - bMayHaveStereo = s[TAUT_YES].bMayHaveStereo || s[TAUT_NON].bMayHaveStereo; - bHasIsotopicAtoms = s[TAUT_NON].num_isotopic_atoms > 0 || s[TAUT_NON].bHasIsotopicTautGroups > 0 || - s[TAUT_YES].num_isotopic_atoms > 0 || s[TAUT_YES].bHasIsotopicTautGroups > 0 ; -/*^^^ */ - if (bFixIsoFixedH) /* 2008-03-21 DT */ - bHasIsotopicAtoms = bHasIsotopicAtoms || - s[TAUT_YES].nLenLinearCTTautomer > 0 && t_group_info && - (0 < NUM_H_ISOTOPES && t_group_info->tni.nNumRemovedProtonsIsotopic[0] || - 1 < NUM_H_ISOTOPES && t_group_info->tni.nNumRemovedProtonsIsotopic[1] || - 2 < NUM_H_ISOTOPES && t_group_info->tni.nNumRemovedProtonsIsotopic[2]) ; -/*^^^ */ - bHasIsotopicAtoms = bHasIsotopicAtoms || - s[TAUT_YES].nLenIsotopicEndpoints > 1 && t_group_info && - (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE|TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE)); - - /* default mode */ - if ( !(nUserMode & REQ_MODE_DEFAULT) ) { - /* default */ - nUserMode |= REQ_MODE_DEFAULT; - } - - /* adjust the mode to the reality */ - if ( ( nUserMode & REQ_MODE_ISO ) && !bHasIsotopicAtoms ) { - nUserMode ^= REQ_MODE_ISO; - nUserMode |= REQ_MODE_NON_ISO; /* at least one is needed */ - } - if ( (nUserMode & REQ_MODE_STEREO) && ( nUserMode & REQ_MODE_ISO ) ) { - nUserMode |= REQ_MODE_ISO_STEREO; - } - if ( (nUserMode & REQ_MODE_STEREO) && !( nUserMode & REQ_MODE_NON_ISO ) ) { - nUserMode ^= REQ_MODE_STEREO; - } - if ( !bMayHaveStereo ) { - if ( nUserMode & REQ_MODE_STEREO ) - nUserMode ^= REQ_MODE_STEREO; - if ( nUserMode & REQ_MODE_ISO_STEREO ) - nUserMode ^= REQ_MODE_ISO_STEREO; - } - - if ( (nUserMode & REQ_MODE_BASIC) && (!out_norm_data[TAUT_NON]->at || !ppINChI[TAUT_NON] || !ppINChI_Aux[TAUT_NON] || !at[TAUT_NON]) ) { - nUserMode ^= REQ_MODE_BASIC; - } - if ( (nUserMode & REQ_MODE_TAUT) && (!out_norm_data[TAUT_YES]->at || !ppINChI[TAUT_YES] || !ppINChI_Aux[TAUT_YES] || !at[TAUT_YES]) ) { - nUserMode ^= REQ_MODE_TAUT; - } - - - switch ((int)nUserMode & (REQ_MODE_BASIC | REQ_MODE_TAUT)) { - case REQ_MODE_BASIC: - n1 = TAUT_NON; - n2 = TAUT_NON; - break; - case REQ_MODE_TAUT: - n1 = TAUT_YES; - n2 = TAUT_YES; - break; - case (REQ_MODE_BASIC | REQ_MODE_TAUT): - n1 = TAUT_NON; - n2 = TAUT_YES; - break; - default: - ret = -3; - goto exit_function; /* program error: inconsistent nUserMode or missing taut/non-taut allocation */ /* */ - } -#if ( TEST_RENUMB_ATOMS == 1 ) - ulNormTime = InchiTimeElapsed( &ulNormTimeStart); -#endif - /************************************************************ - * * - * Obtain all non-stereo canonical numberings * - * * - ************************************************************/ -#if ( TEST_RENUMB_ATOMS == 1 ) - InchiTimeGet( &ulCanonTimeStart ); -#endif - if ( (nUserMode & REQ_MODE_NON_ISO) && !(nUserMode & REQ_MODE_ISO) ) { - /* added for special non-isotopic test mode 2004-10-04 */ - if ( t_group_info ) { - t_group_info->bIgnoreIsotopic = 1; - if ( t_group_info->nIsotopicEndpointAtomNumber ) { - t_group_info->nIsotopicEndpointAtomNumber[0] = inchi_min(1, t_group_info->nIsotopicEndpointAtomNumber[0]); - } - memset( t_group_info->num_iso_H, 0, sizeof(t_group_info->num_iso_H) ); - memset ( t_group_info->tni.nNumRemovedProtonsIsotopic, 0, sizeof(t_group_info->tni.nNumRemovedProtonsIsotopic)); - t_group_info->bTautFlagsDone &= ~(TG_FLAG_FOUND_ISOTOPIC_H_DONE|TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE); - } - for ( i = 0; i < TAUT_NUM; i ++ ) { - s[i].bHasIsotopicTautGroups = 0; - s[i].bIgnoreIsotopic = 1; - s[i].nLenIsotopic = 0; - s[i].nLenIsotopicEndpoints = 0; - s[i].nLenLinearCTIsotopicTautomer = 0; - s[i].num_isotopic_atoms = 0; - } - bHasIsotopicAtoms = 0; - } - ret = GetBaseCanonRanking( num_atoms, num_at_tg, at, t_group_info, s, pBCN, ulMaxTime, bFixIsoFixedH ); -#if ( TEST_RENUMB_ATOMS == 1 ) - ulCanonTime = InchiTimeElapsed( &ulCanonTimeStart ); -#endif - if ( ret < 0 ) { - goto exit_function; /* program error */ - } -#if ( bRELEASE_VERSION == 0 && FIND_CANON_NE_EQUITABLE == 1 ) - /* Debug only: find whether canonical equivalence is different from equitable partition */ - if ( bCanonIsFinerThanEquitablePartition( num_atoms, at[n1], pBCN->ftcn[TAUT_NON].nSymmRankCt ) ) { - bExtract |= EXTR_CANON_NE_EQUITABLE; - } -#endif - /* added for special non-isotopic test mode 2004-10-04 */ - if ( !pBCN->ftcn[n1].PartitionCt.Rank ) { - n1 = ALT_TAUT(n1); - } - if ( !pBCN->ftcn[n2].PartitionCt.Rank ) { - n2 = ALT_TAUT(n2); - } - if ( n1 > n2 ) { - ret = CT_TAUCOUNT_ERR; - goto exit_function; /* program error */ - } - - /************************************************************ - * * - * Obtain stereo canonical numberings * - * * - ************************************************************/ - - for ( i = n2; i >= n1 && !RETURNED_ERROR( ret ); i -- ) { - - memset( pCS, 0, sizeof(*pCS) ); - - switch( i ) { - case TAUT_NON: /* non-tautomeric */ - nMode = 0; - nMode = (s[i].nLenLinearCTTautomer == 0)? CANON_MODE_CT:CANON_MODE_TAUT; - nMode |= (bHasIsotopicAtoms && (nUserMode & REQ_MODE_ISO))? CANON_MODE_ISO:0; - nMode |= (s[TAUT_NON].bMayHaveStereo && (nUserMode & REQ_MODE_STEREO) )? CANON_MODE_STEREO:0; - nMode |= (bHasIsotopicAtoms && s[TAUT_NON].bMayHaveStereo && (nUserMode & REQ_MODE_ISO_STEREO))? CANON_MODE_ISO_STEREO:0; - nMode |= (nUserMode & REQ_MODE_NOEQ_STEREO )? CMODE_NOEQ_STEREO : 0; - nMode |= (nUserMode & REQ_MODE_REDNDNT_STEREO)? CMODE_REDNDNT_STEREO : 0; - nMode |= (nUserMode & REQ_MODE_NO_ALT_SBONDS )? CMODE_NO_ALT_SBONDS : 0; - if ( (nMode & CANON_MODE_STEREO) == CANON_MODE_STEREO || - (nMode & CANON_MODE_ISO_STEREO) == CANON_MODE_ISO_STEREO ) { - nMode |= (nUserMode & REQ_MODE_RELATIVE_STEREO)? CMODE_RELATIVE_STEREO: 0; - nMode |= (nUserMode & REQ_MODE_RACEMIC_STEREO )? CMODE_RACEMIC_STEREO : 0; - nMode |= (nUserMode & REQ_MODE_SC_IGN_ALL_UU )? CMODE_SC_IGN_ALL_UU : 0; - nMode |= (nUserMode & REQ_MODE_SB_IGN_ALL_UU )? CMODE_SB_IGN_ALL_UU : 0; - } - if ( ret= AllocateCS( pCS, num_atoms, num_atoms, s[TAUT_NON].nLenCT, s[TAUT_NON].nLenCTAtOnly, - s[TAUT_NON].nLenLinearCTStereoDble, s[TAUT_NON].nMaxNumStereoBonds, - s[TAUT_NON].nLenLinearCTStereoCarb, s[TAUT_NON].nMaxNumStereoAtoms, - 0, 0, s[TAUT_NON].nLenIsotopic, nMode, pBCN ) ) { - goto exit_function; - } - *pCS2 = *pCS; - break; - case TAUT_YES: /* tautomeric */ - nMode = 0; - nMode = (s[i].nLenLinearCTTautomer == 0)? CANON_MODE_CT:CANON_MODE_TAUT; - nMode |= (bHasIsotopicAtoms && (nUserMode & REQ_MODE_ISO) )? CANON_MODE_ISO:0; - nMode |= (s[TAUT_YES].bMayHaveStereo && (nUserMode & REQ_MODE_STEREO) )? CANON_MODE_STEREO:0; - nMode |= (bHasIsotopicAtoms && s[TAUT_YES].bMayHaveStereo && (nUserMode & REQ_MODE_ISO_STEREO))? CANON_MODE_ISO_STEREO:0; - nMode |= (nUserMode & REQ_MODE_NOEQ_STEREO )? CMODE_NOEQ_STEREO : 0; - nMode |= (nUserMode & REQ_MODE_REDNDNT_STEREO)? CMODE_REDNDNT_STEREO : 0; - nMode |= (nUserMode & REQ_MODE_NO_ALT_SBONDS )? CMODE_NO_ALT_SBONDS : 0; - if ( (nMode & CANON_MODE_STEREO) == CANON_MODE_STEREO || - (nMode & CANON_MODE_ISO_STEREO) == CANON_MODE_ISO_STEREO ) { - nMode |= (nUserMode & REQ_MODE_RELATIVE_STEREO)? CMODE_RELATIVE_STEREO: 0; - nMode |= (nUserMode & REQ_MODE_RACEMIC_STEREO )? CMODE_RACEMIC_STEREO : 0; - nMode |= (nUserMode & REQ_MODE_SC_IGN_ALL_UU )? CMODE_SC_IGN_ALL_UU : 0; - nMode |= (nUserMode & REQ_MODE_SB_IGN_ALL_UU )? CMODE_SB_IGN_ALL_UU : 0; - } - if ( ret= AllocateCS( pCS, num_atoms, num_at_tg, s[TAUT_YES].nLenCT, s[TAUT_YES].nLenCTAtOnly, - s[TAUT_YES].nLenLinearCTStereoDble, s[TAUT_YES].nMaxNumStereoBonds, - s[TAUT_YES].nLenLinearCTStereoCarb, s[TAUT_YES].nMaxNumStereoAtoms, - s[TAUT_YES].nLenLinearCTTautomer, s[TAUT_YES].nLenLinearCTIsotopicTautomer, - s[TAUT_YES].nLenIsotopic, nMode, pBCN ) ) { - goto exit_function; - } - *pCS2 = *pCS; - break; - } - - - /*^^^ 2009-12-05 */ - nMode |= (nUserMode & REQ_MODE_DIFF_UU_STEREO)? REQ_MODE_DIFF_UU_STEREO : 0; - /*^^^ 2009-12-05 */ - - - /* settings */ - pCS->lNumDecreasedCT = -1; - pCS->bDoubleBondSquare = DOUBLE_BOND_NEIGH_LIST? 2:0; /* 2 => special mode */ - pCS->bIgnoreIsotopic = !((s[TAUT_NON].num_isotopic_atoms || - s[TAUT_YES].num_isotopic_atoms || - s[TAUT_YES].bHasIsotopicTautGroups) || - (nUserMode & REQ_MODE_NON_ISO) || - !(nUserMode & REQ_MODE_ISO)); - - if ( (nUserMode & REQ_MODE_NON_ISO) && !(nUserMode & REQ_MODE_ISO) ) { - pCS->bIgnoreIsotopic = 1; /* 10-04-2004 */ - } - - if ( i == TAUT_YES ) { /* tautomeric */ - pCS->t_group_info = t_group_info; /* ??? make a copy or reuse ??? */ - pCS->t_group_info->bIgnoreIsotopic = !(s[TAUT_YES].bHasIsotopicTautGroups || - (nUserMode & REQ_MODE_NON_ISO) || - !(nUserMode & REQ_MODE_ISO)); - if ( (nUserMode & REQ_MODE_NON_ISO) && !(nUserMode & REQ_MODE_ISO) ) { - pCS->t_group_info->bIgnoreIsotopic = 1; /* 10-04-2004 */ - } - } - pCS->ulTimeOutTime = pBCN->ulTimeOutTime; - /*=========== Obsolete Mode Bits (bit 0 is Least Significant Bit) =========== - * - * Mode Bits Description - * '0' c 0 Only one connection table canonicalization - * '1' C 1 Recalculate CT using fixed nSymmRank - * '2' i 1|2 Isotopic canonicalization (internal) - * '3' I 1|2|4 Isotopic canonicalization (output) - * '4' s 1|8 Stereo canonicalization - * '5' S 1|2|4|16 Stereo isotopic canonicalization - * '6' A 1|2|4|8|16 Output All - */ -#if ( TEST_RENUMB_ATOMS == 1 ) - InchiTimeGet( &ulCanonTimeStart ); -#endif - /*************************************** - The last canonicalization step - ***************************************/ - if ( pBCN ) { - /* USE_CANON2 == 1 */ - pCS->NeighList = NULL; - pCS->pBCN = pBCN; - ret = Canon_INChI( num_atoms, i?num_at_tg:num_atoms, at[i], pCS, nMode, i); - } else { - /* old way */ - pCS->NeighList = CreateNeighList( num_atoms, i?num_at_tg:num_atoms, at[i], pCS->bDoubleBondSquare, pCS->t_group_info ); - pCS->pBCN = NULL; - ret = Canon_INChI( num_atoms, i?num_at_tg:num_atoms, at[i], pCS, nMode, i); - } - - pINChI = ppINChI[i]; /* pointers to already allocated still empty InChI */ - pINChI_Aux = ppINChI_Aux[i]; - if ( ret <= 0 ) { - /***************************************/ - /* failure in Canon_INChI() */ - /***************************************/ - pINChI->nErrorCode = ret; - pINChI_Aux->nErrorCode = ret; - } else { - /***************************************/ - /* success Canon_INChI() */ - /* save canonicalization results in */ - /* pINChI and pINChI_Aux */ - /***************************************/ - pINChI->nErrorCode = 0; - pINChI_Aux->nErrorCode = 0; - pINChI->bDeleted = pINChI_Aux->bDeleted = out_norm_data[i]->bDeleted; - pINChI_Aux->nCanonFlags = pCS->nCanonFlags; - pINChI_Aux->bTautFlags = out_norm_data[i]->bTautFlags; - pINChI_Aux->bTautFlagsDone = out_norm_data[i]->bTautFlagsDone; - pINChI_Aux->bNormalizationFlags = out_norm_data[i]->bNormalizationFlags; - /* may return an error or a warning */ - ret = FillOutINChI( pINChI, pINChI_Aux, - num_atoms, i?num_at_tg:num_atoms, - i?num_removed_H_taut:num_removed_H, at[i], - out_norm_data[i]->at, pCS, i, nUserMode, - pStrErrStruct ); - if ( RETURNED_ERROR( ret ) ) { - /* failure in FillOutINChI() */ - pINChI->nErrorCode = ret; - pINChI_Aux->nErrorCode = ret; - } else { - /****************************/ - /* success in FillOutINChI() */ - /****************************/ -#if ( bRELEASE_VERSION == 0 ) - if ( pINChI->Stereo && - (pINChI->Stereo->nCompInv2Abs && !pINChI->Stereo->bTrivialInv) || - pINChI->StereoIsotopic && - (pINChI->StereoIsotopic->nCompInv2Abs && !pINChI->StereoIsotopic->bTrivialInv) ) { - bExtract |= EXTR_NON_TRIVIAL_STEREO; - } -#endif - /* mark non-tautomeric representation as having another, tautomeric representation */ - if ( pINChI_Aux && s[TAUT_YES].nLenLinearCTTautomer ) { - pINChI_Aux->bIsTautomeric = s[TAUT_YES].nLenLinearCTTautomer; - } -#if ( bRELEASE_VERSION == 0 ) - pCS->bExtract |= bExtract; - pINChI->bExtract |= pCS->bExtract; -#endif - ret2 = CheckCanonNumberingCorrectness( - num_atoms, i?num_at_tg:num_atoms, - at[i], pCS, i, pStrErrStruct ); - if ( ret2 ) { - pINChI->nErrorCode = ret2; - pINChI_Aux->nErrorCode = ret2; - ret = ret2; - } - } - } -#if ( TEST_RENUMB_ATOMS == 1 ) - ulCanonTime2 = InchiTimeElapsed( &ulCanonTimeStart ); - pINChI_Aux->ulCanonTime = ulCanonTime+ulCanonTime2; - pINChI_Aux->ulNormTime = ulNormTime; -#endif - FreeNeighList( pCS->NeighList ); - DeAllocateCS( pCS2 ); - - pINChI = NULL; /* avoid dangling pointers */ - pINChI_Aux = NULL; /* avoid dangling pointers */ - } - if ( ret == 0 ) { - ret = num_atoms; - } - /* treat the results later */ - -exit_function: - DeAllocBCN( pBCN ); - if ( at[TAUT_YES] ) - inchi_free( at[TAUT_YES] ); - if ( at[TAUT_NON] ) - inchi_free( at[TAUT_NON] ); - if ( ti_out ) { - *ti_out = *t_group_info; - } else { - free_t_group_info( t_group_info ); - } - free_t_group_info( t_group_info_orig ); - return ret; -} -#ifndef COMPILE_ANSI_ONLY /* { */ -/***************************************************************************************/ -int GetAtomOrdNbrInCanonOrd( inp_ATOM *norm_at, AT_NUMB *nAtomOrdNbr, - AT_NUMB *nOrigAtNosInCanonOrd, int num_at ) -{ - AT_NUMB *nCanonNbr, *nOrigAtNos, *nOrigAtNosOrd; - int i, ret; - - ret = 0; - - nCanonNbr = (AT_NUMB *)inchi_calloc( num_at, sizeof(nCanonNbr[0]) ); - nOrigAtNos = (AT_NUMB *)inchi_calloc( num_at, sizeof(nOrigAtNos[0]) ); - nOrigAtNosOrd = (AT_NUMB *)inchi_calloc( num_at, sizeof(nOrigAtNosOrd[0]) ); - - if ( !nCanonNbr || !nOrigAtNos || !nAtomOrdNbr || !nOrigAtNosOrd ) { - ret = CT_OUT_OF_RAM; /* */ - goto exit_function; - } - for ( i = 0; i < num_at; i ++ ) { - nCanonNbr[i] = nAtomOrdNbr[i] = nOrigAtNosOrd[i] = (AT_NUMB)i; - nOrigAtNos[i] = norm_at[i].orig_at_number; - } - /* get nCanonNbr[]: canon. numbers-1 in order of increasing original atom numbers */ - pn_RankForSort = nOrigAtNosInCanonOrd; - qsort( nCanonNbr, num_at, sizeof(nCanonNbr[0]), CompRank ); - /* get nOrigAtNosOrd[]: norm_atom ord. numbers the same order of increasing original atom numbers */ - pn_RankForSort = nOrigAtNos; - qsort( nOrigAtNosOrd, num_at, sizeof(nOrigAtNosOrd[0]), CompRank ); - /* check whether the 2 sets of origiginal atom numbers have identical elements */ - for ( i = 0; i < num_at; i ++ ) { - if ( nOrigAtNosInCanonOrd[nCanonNbr[i]] != nOrigAtNos[nOrigAtNosOrd[i]] ) { - ret = CT_RANKING_ERR; /* */ - goto exit_function; - } - } - for ( i = 0; i < num_at; i ++ ) { - nAtomOrdNbr[(int)nCanonNbr[i]] = nOrigAtNosOrd[i]; - } - -/* - pn_RankForSort = nCanonNbr; - qsort( nAtomOrdNbr, num_at, sizeof(nCanonNbr[0]), CompRank ); -*/ - -exit_function: - if ( nCanonNbr ) - inchi_free( nCanonNbr ); - if ( nOrigAtNos ) - inchi_free( nOrigAtNos ); - if ( nOrigAtNosOrd ) - inchi_free( nOrigAtNosOrd ); - return ret; -} -/***************************************************************************************/ -int FillOutCanonInfAtom(inp_ATOM *norm_at, INF_ATOM_DATA *inf_norm_at_data, int init_num_at, int bIsotopic, - INChI *pINChI, INChI_Aux *pINChI_Aux, int bAbcNumbers, INCHI_MODE nMode) -{ - int i, j, m, n, num_stereo, k, c, ret, len_str, len, atw, num_err; - int next_atom[MAX_CUMULENE_LEN+1], best_next_atom[MAX_CUMULENE_LEN+1], cur_atom; - int next_neigh[MAX_CUMULENE_LEN+1], best_next_neigh[MAX_CUMULENE_LEN+1], best_len; - int num_iso_H[NUM_H_ISOTOPES]; - char *str; - AT_NUMB g, e; - int num_at = pINChI->nNumberOfAtoms; - int nNumberOfTGroups = (pINChI->lenTautomer && pINChI->nTautomer && pINChI->nTautomer[0])? (int)pINChI->nTautomer[0] : 0; - AT_NUMB *nOrigAtNosInCanonOrd; - INChI_Stereo *Stereo; - AT_NUMB *nConstitEquNumbers; - AT_NUMB *nConstitEquTGroupNumbers; - S_CHAR *t_parity = NULL; - AT_NUMB *nNumber = NULL; - int bIncludeIsotopicH; - - AT_NUMB *nNormAtNosInCanonOrd; - int (*MakeNumber)(char*, int, const char*, int) = bAbcNumbers? MakeAbcNumber:MakeDecNumber; - int bRel= (0 != (nMode & ( REQ_MODE_RELATIVE_STEREO))); - int bRac= (0 != (nMode & ( REQ_MODE_RACEMIC_STEREO))); - int bRelRac= bRel || bRac; - int bDoDisplaySp3 = 1; - - inf_ATOM *inf_norm_at = inf_norm_at_data? inf_norm_at_data->at : NULL; - - ret = 0; - num_err = 0; - - if ( !inf_norm_at ) - return ret; - /* prepare removeable protons and H info */ - inf_norm_at_data->nNumRemovedProtons = pINChI_Aux->nNumRemovedProtons; - MakeRemovedProtonsString( pINChI_Aux->nNumRemovedProtons, pINChI_Aux->nNumRemovedIsotopicH, NULL, bIsotopic, - inf_norm_at_data->szRemovedProtons, &inf_norm_at_data->num_removed_iso_H ); - /* fill out info atom */ - if ( bIsotopic && !(pINChI->nNumberOfIsotopicAtoms || pINChI->nNumberOfIsotopicTGroups || - pINChI->nPossibleLocationsOfIsotopicH && pINChI->nPossibleLocationsOfIsotopicH[0]>1) ) - bIsotopic = 0; - - Stereo = bIsotopic? pINChI->StereoIsotopic : - pINChI->Stereo; - bDoDisplaySp3 = (NULL != Stereo) && (Stereo->nNumberOfStereoCenters > 0); - -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - if ( bDoDisplaySp3 && bRelRac && Stereo->nNumberOfStereoCenters < 2 && - (Stereo->nCompInv2Abs || ATOM_PARITY_ILL_DEF(Stereo->t_parity[0]) ) ) { - bDoDisplaySp3 = 0; - if ( Stereo->nCompInv2Abs ) { - inf_norm_at_data->StereoFlags |= bRel? INF_STEREO_REL : bRac? INF_STEREO_RAC : 0; - } - } -#endif - /* flag has stereo */ - if ( (NULL != Stereo) && (bDoDisplaySp3 || Stereo->nNumberOfStereoBonds > 0) ) { - inf_norm_at_data->StereoFlags |= INF_STEREO; - } - - /* - if ( bDoDisplaySp3 && bRelRac && Stereo->nNumberOfStereoCenters < 2 && - (Stereo->nCompInv2Abs || ATOM_PARITY_ILL_DEF(Stereo->t_parity[0]) ) ) { - bDoDisplaySp3 = 0; - } - */ - if ( bDoDisplaySp3 && Stereo->nCompInv2Abs ) { - /* inversion changes stereo */ - if ( bRel ) { - inf_norm_at_data->StereoFlags |= INF_STEREO_REL; - } else - if ( bRac ) { - inf_norm_at_data->StereoFlags |= INF_STEREO_RAC; - } else { - inf_norm_at_data->StereoFlags |= INF_STEREO_ABS; - } - if ( bRelRac ) { - inf_norm_at_data->StereoFlags |= (Stereo->nCompInv2Abs > 0)? INF_STEREO_NORM : INF_STEREO_INV; - } - } - if ( bDoDisplaySp3 && Stereo->nCompInv2Abs < 0 && !bRelRac ) { - /* display Inv stereo which is Absolute Stereo */ - nNumber = Stereo->nNumberInv; - t_parity = Stereo->t_parityInv; - nOrigAtNosInCanonOrd = bIsotopic? pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv : - pINChI_Aux->nOrigAtNosInCanonOrdInv; - } else { - /* display Inv stereo which is Absolute Stereo */ - if ( bDoDisplaySp3 ) { - nNumber = Stereo->nNumber; - t_parity = Stereo->t_parity; - } - nOrigAtNosInCanonOrd = bIsotopic? pINChI_Aux->nIsotopicOrigAtNosInCanonOrd : - pINChI_Aux->nOrigAtNosInCanonOrd; - } - - nConstitEquNumbers = bIsotopic? pINChI_Aux->nConstitEquIsotopicNumbers : - pINChI_Aux->nConstitEquNumbers; - nConstitEquTGroupNumbers = bIsotopic? pINChI_Aux->nConstitEquIsotopicTGroupNumbers : - pINChI_Aux->nConstitEquTGroupNumbers; - memset( inf_norm_at, 0, init_num_at*sizeof(inf_norm_at[0]) ); - - /* obtain norm_at[] atom numbers (from zero) in order of canonical numbers */ - nNormAtNosInCanonOrd = (AT_NUMB *)inchi_calloc( num_at, sizeof(nNormAtNosInCanonOrd[0]) ); - if ( ret = GetAtomOrdNbrInCanonOrd( norm_at, nNormAtNosInCanonOrd, nOrigAtNosInCanonOrd, num_at ) ) { - goto exit_function; - } - - /* atom canonical and equivalence numbers > 0 */ - for ( i = 0; i < num_at; i ++ ) { - j = (int)nNormAtNosInCanonOrd[i]; - if ( j < 0 || j >= num_at ) - continue; - inf_norm_at[j].nCanonNbr = (AT_NUMB)(i+1); - inf_norm_at[j].nCanonEquNbr = nConstitEquNumbers[i]; -#ifdef DISPLAY_DEBUG_DATA - inf_norm_at[j].nDebugData = 0; -#if ( DISPLAY_DEBUG_DATA == DISPLAY_DEBUG_DATA_C_POINT ) - inf_norm_at[j].nDebugData = norm_at[j].c_point; -#endif -#endif - } - /* tautomeric groups */ - if ( nNumberOfTGroups ) { - /* - : start from 1: bypass number of t-groups - : j is a counter within the current t-group - : g is a tautomeric group canonical number - : e is a tautomeric group equivalence - */ - for ( g = 1, i = 1; g <= nNumberOfTGroups; g ++ ) { - n = (int)pINChI->nTautomer[i] - INCHI_T_NUM_MOVABLE; /* number of atoms in t-group */ - e = nConstitEquTGroupNumbers[(int)g - 1]; - /* bypass number of hydrogen atoms, negative charges, ... */ - for ( i += INCHI_T_NUM_MOVABLE+1, j = 0; j < n && i < pINChI->lenTautomer; j ++, i ++ ) { - /* scan canonical numbers of atoms within the atom t-group */ - k = (int)nNormAtNosInCanonOrd[(int)pINChI->nTautomer[i]-1]; - inf_norm_at[k].nTautGroupCanonNbr = g; - inf_norm_at[k].nTautGroupEquNbr = e; - } - } - if ( i != pINChI->lenTautomer || g != nNumberOfTGroups+1 ) { - ret = CT_TAUCOUNT_ERR; /* */ - goto exit_function; - } - } - /* atoms that may exchange isotopic H */ - if ( bIsotopic && pINChI->nPossibleLocationsOfIsotopicH && (n = (int)pINChI->nPossibleLocationsOfIsotopicH[0]) ) { - for ( i = 1; i < n; i ++ ) { - j = (int)pINChI->nPossibleLocationsOfIsotopicH[i]; - k = (int)nNormAtNosInCanonOrd[j - 1]; - if ( !inf_norm_at[k].nTautGroupCanonNbr ) { - inf_norm_at[k].cFlags |= AT_FLAG_ISO_H_POINT; - } - } - } - -#if ( DISPLAY_RING_SYSTEMS == 1 ) - /* debug only */ - for ( j = 0; j < num_at; j ++ ) { - inf_norm_at[j].nCanonNbr = norm_at[j].nBlockSystem; - inf_norm_at[j].nCanonEquNbr = norm_at[j].nRingSystem; -#if ( USE_DISTANCES_FOR_RANKING == 1 ) - inf_norm_at[j].nTautGroupCanonNbr = norm_at[j].nDistanceFromTerminal; - inf_norm_at[j].nTautGroupEquNbr = norm_at[j].bCutVertex; -#else - inf_norm_at[j].nTautGroupCanonNbr = norm_at[j].bCutVertex; - inf_norm_at[j].nTautGroupEquNbr = 0; -#endif - } -#endif - - - - /* Write isotopic mass, chemical element symbols and hydrogens, charge, radical, canon. numbers */ - len_str = sizeof(inf_norm_at[0].at_string); - for ( i = 0; i < init_num_at; i ++ ) { - str = inf_norm_at[i].at_string; - len = 0; - bIncludeIsotopicH = bIsotopic && !inf_norm_at[i].nTautGroupCanonNbr && !(inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT); - /* isotopic mass */ - atw = 0; - if ( norm_at[i].iso_atw_diff && bIsotopic ) { - if ( norm_at[i].at_type == ATT_PROTON ) { - ; /* do not set isotopic mass of a tautomeric proton */ - } else - if ( norm_at[i].el_number == PERIODIC_NUMBER_H && norm_at[i].chem_bonds_valence == 1 && - !norm_at[i].charge && !norm_at[i].radical && !norm_at[i].num_H && - (inf_norm_at[j=(int)norm_at[i].neighbor[0]].nTautGroupCanonNbr || (inf_norm_at[j].cFlags & AT_FLAG_ISO_H_POINT) ) ) { - ; /* do not set isotopic mass of an exchangeable proton */ - } else { - atw = get_atw(norm_at[i].elname); - atw += (norm_at[i].iso_atw_diff>0)? norm_at[i].iso_atw_diff-1:norm_at[i].iso_atw_diff; - /*len += sprintf( str+len, "^%d", atw );*/ - } - } - /* element name */ - if ( norm_at[i].el_number == PERIODIC_NUMBER_H && 2 <= atw && atw <= 3 ) { - len += sprintf( str+len, "%s", atw==2? "D" : "T" ); - } else { - if ( atw ) { - len += sprintf( str+len, "^%d", atw ); - } - len += sprintf( str+len, "%s", norm_at[i].elname ); - } - /* hydrogens */ - /* find number of previuosly removed terminal hydrogen atoms because these terminal H will be displayed */ - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { - num_iso_H[j] = norm_at[i].num_iso_H[j]; - } - /* n = number of implicit H to display */ - for ( j = num_at, n = (int)norm_at[i].num_H; j < init_num_at; j ++ ) { - /* subtract number of removed terminal */ - /* H atoms from the total number of H atoms */ - if ( i == (int)norm_at[j].neighbor[0] ) { - n -= 1; /* found explicit H => decrement number of implicit H */ - m = (int)norm_at[j].iso_atw_diff-1; - if ( 0 <= m && m < NUM_H_ISOTOPES ) { - /* subtract number of removed terminal isotopic H */ - /* atoms from the total number of isotopic H atoms */ - num_iso_H[m] -= 1; - } - } - } - /* at this point n = number of implicit H to display, - num_iso_H[] contains number of implicit isotopic H among n */ - if ( bIncludeIsotopicH ) { - /* subtract number of isotopic H atoms from the total number of H atoms */ - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { - n -= num_iso_H[j]; - } - } - /* non-isotopic hydrogen atoms */ - if ( n > 1 ) { - len += sprintf( str+len, "H%d", n ); - } else - if ( n == 1 ) { - len += sprintf( str+len, "H" ); - } - /* isotopic hydrogen atoms */ - if ( bIncludeIsotopicH ) { - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { - if ( num_iso_H[j] ) { - if ( j == 0 || j != 1 && j != 2 ) { - len += sprintf( str+len, "^%dH", j+1 ); - } else { - len += sprintf( str+len, j == 1? "D" : "T" ); - } - if ( num_iso_H[j] != 1 ) { - len += sprintf( str+len, "%d", (int)num_iso_H[j] ); - } - } - } - } - if ( norm_at[i].el_number == PERIODIC_NUMBER_H && str[0] == str[1] ) { - char *q; - if ( !str[2] ) { - str[1] = '2'; /* quick fix: replace HH with H2 */ - } else - if ( isdigit( UCINT str[2] ) && (n = strtol( str+2, &q, 10 )) && !q[0] ) { - len = 1 + sprintf( str+1, "%d", n+1 ); - } - } - /* - if ( str[0] == 'H' && str[1] == 'H' && !str[2] ) { - str[1] = '2'; - } - */ - /* charge */ - if ( abs(norm_at[i].charge) > 1 ) - len += sprintf( str+len, "%+d", norm_at[i].charge ); - else - if ( abs(norm_at[i].charge) == 1 ) - len += sprintf( str+len, "%s", norm_at[i].charge>0? "+" : "-" ); - /* radical */ - if ( norm_at[i].radical ) - len += sprintf( str+len, "%s", norm_at[i].radical==RADICAL_SINGLET? ":" : - norm_at[i].radical==RADICAL_DOUBLET? "." : - norm_at[i].radical==RADICAL_TRIPLET? ".." : "?"); - } - - /* stereogenic centers */ - if ( bDoDisplaySp3 && Stereo && 0 < (num_stereo = Stereo->nNumberOfStereoCenters) ) { - for ( i = 0; i < num_stereo; i ++ ) { - j = (int)nNormAtNosInCanonOrd[(int)nNumber[i]-1]; - c = t_parity[i]; - c = c==1? '-' : c==2? '+' : c==3? 'u' : c== 4? '?' : '*'; - inf_norm_at[j].cStereoCenterParity = c; - str=inf_norm_at[j].at_string; - len = strlen(str); - if ( len + 3 < (int)sizeof(inf_norm_at[0].at_string) ) { - str[len++] = '('; - str[len++] = inf_norm_at[j].cStereoCenterParity; - str[len++] = ')'; - str[len] = '\0'; - /* mark ambuguous stereo center */ - if ( norm_at[j].bAmbiguousStereo && (c=='+' || c=='-' || c=='?') && str[0] != '!' && - len+1 < (int)sizeof(inf_norm_at[0].at_string) ) { - memmove( str+1, str, len+1 ); - str[0] = '!'; /* output the atom in red color */ - } - } - } - } - - /* stereogenic bonds */ - /* (cumulenes with odd number of double bonds are stereocenters, */ - /* and atom parity should be set) */ - if ( Stereo && 0 < (num_stereo = Stereo->nNumberOfStereoBonds) ) { - for ( i = 0; i < num_stereo; i ++ ) { - int start_at, num_eql=0, bAmbiguousStereoBond = 0; - j = (int)nNormAtNosInCanonOrd[(int)Stereo->nBondAtom1[i]-1]; - k = (int)nNormAtNosInCanonOrd[(int)Stereo->nBondAtom2[i]-1]; - start_at = j; - c = Stereo->b_parity[i]; - - c = c==1? '-' : c==2? '+' : c==3? 'u' : c== 4? '?' : '*'; - - /* mark ambuguous stereo bond atom(s) */ - if ( norm_at[j].bAmbiguousStereo && (c=='+' || c=='-' ) && - (len=strlen(str=inf_norm_at[j].at_string)+1) < (int)sizeof(inf_norm_at[0].at_string) && - str[0] != '!' ) { - memmove( str+1, str, len ); - str[0] = '!'; /* output the atom in red color */ - bAmbiguousStereoBond ++; - } - if ( norm_at[k].bAmbiguousStereo && (c=='+' || c=='-') && - (len=strlen(str=inf_norm_at[k].at_string)+1) < (int)sizeof(inf_norm_at[0].at_string) && - str[0] != '!' ) { - memmove( str+1, str, len ); - str[0] = '!'; /* output the atom in red color */ - bAmbiguousStereoBond ++; - } - - /* find the opposite atom k. */ - /* Note: since it may be a cumulene, find the shortest(best) path */ - /* to atom number k to avoid confusion in case of, for example, */ - /* 4-member aromatic rings. */ - best_len = MAX_CUMULENE_LEN+1; /* moved here from inside the cycle 1-8-2003 */ - for ( n = 0; n < norm_at[j].valence; n ++ ) { - if ( norm_at[j].bond_type[n] == BOND_SINGLE ) { - /* single bond cannot be stereogenic. */ - continue; - } - /* best_len = MAX_CUMULENE_LEN+1; */ - len = 0; /* number of bonds in cumulene - 1 */ - next_atom[len] = (int)norm_at[j].neighbor[n]; - next_neigh[len] = n; - cur_atom = j; - while ( next_atom[len] != k && len < MAX_CUMULENE_LEN && 2 == norm_at[next_atom[len]].valence ) { - next_neigh[len+1] = ((int)norm_at[next_atom[len]].neighbor[0] == cur_atom); - next_atom[len+1] = (int)norm_at[next_atom[len]].neighbor[next_neigh[len+1]]; - cur_atom = next_atom[len]; - len ++; - } - if ( next_atom[len] == k ) { - if ( len < best_len ) { - memcpy( best_next_neigh, next_neigh, sizeof(best_next_neigh) ); - memcpy( best_next_atom, next_atom, sizeof(best_next_atom) ); - best_len = len; - num_eql = 0; - if ( len == 0 ) { - break; /* path length cannot be smaller than 1 */ - } - } else - if ( len == best_len ) { - num_eql ++; - } - } - } - if ( best_len <= MAX_CUMULENE_LEN && best_next_atom[best_len] == k ) { - if ( num_eql ) { - num_err ++; /* program error; no breakpoint here */ - } - if ( best_len % 2 ) { - /* even number of bonds: chiral atom, draw parity on the cenrtal atom */ - j = best_next_atom[best_len/2]; - inf_norm_at[j].cStereoCenterParity = c; - str=inf_norm_at[j].at_string; - len = strlen(str); - if ( len + 3 < (int)sizeof(inf_norm_at[0].at_string) ) { - str[len++] = '('; - str[len++] = inf_norm_at[j].cStereoCenterParity; - str[len++] = ')'; - str[len] = '\0'; - } - } else { - /* odd number of bonds: draw parity on the central bond */ - if ( best_len == 0 ) { - /* double bond */ - j = start_at; - k = best_next_neigh[0]; - } else { - /* cumulene */ - best_len = best_len/2-1; - j = best_next_atom[best_len]; - k = best_next_neigh[best_len+1]; /* added +1 to display cumulene parity on the middle bond (6-24-2002) */ - } - /* mark "forward" bond */ - for ( m = 0; m < MAX_STEREO_BONDS && inf_norm_at[j].cStereoBondParity[m]; m ++ ) - ; - if ( m < MAX_STEREO_BONDS ) { - inf_norm_at[j].cStereoBondParity[m] = c; - inf_norm_at[j].cStereoBondNumber[m] = k; - inf_norm_at[j].cStereoBondWarning[m] = bAmbiguousStereoBond; - } else { - num_err ++; /* program error; no breakpoint here */ - } - /* mark "backward" bond */ - n = norm_at[j].neighbor[k]; - for ( k = 0; k < norm_at[n].valence && j != (int)norm_at[n].neighbor[k]; k ++ ) - ; - if ( k < norm_at[n].valence ) { - j = n; - for ( m = 0; m < MAX_STEREO_BONDS && inf_norm_at[j].cStereoBondParity[m]; m ++ ) - ; - if ( m < MAX_STEREO_BONDS ) { - inf_norm_at[j].cStereoBondParity[m] = c; - inf_norm_at[j].cStereoBondNumber[m] = k; - inf_norm_at[j].cStereoBondWarning[m] = bAmbiguousStereoBond; - } else { - num_err ++; /* program error; no breakpoint here */ - } - } else { - num_err ++; /* program error; no breakpoint here */ - } - } - } else { - num_err ++; /* program error; no breakpoint here */ - } - } - } - - for ( i = 0; i < init_num_at; i ++ ) { - /* canonical numbers */ - if ( inf_norm_at[i].nCanonNbr ) { - str = inf_norm_at[i].at_string; - len = strlen(str); - len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nCanonNbr ); - if ( inf_norm_at[i].nCanonEquNbr || inf_norm_at[i].nTautGroupCanonNbr || (inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT) ) { - if ( inf_norm_at[i].nCanonEquNbr ) { - len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nCanonEquNbr ); - } else - if ( len + 1 < len_str ) { - len += 1; - strcat( str, "/" ); - } - } - /* tautomeric groups */ - if ( inf_norm_at[i].nTautGroupCanonNbr ) { - len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nTautGroupCanonNbr ); - if ( inf_norm_at[i].nTautGroupEquNbr ) { - len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nTautGroupEquNbr ); - } - } - if ( (inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT) && len+2 <= len_str ) { - str[len++] = '/'; - str[len++] = '*'; - str[len] = '\0'; - } -#ifdef DISPLAY_DEBUG_DATA - if ( inf_norm_at[i].nDebugData ) { - len += (*MakeNumber)( str+len, len_str-len, "`", (int)inf_norm_at[i].nDebugData ); - } -#endif - } - } - - -exit_function: - - if ( nNormAtNosInCanonOrd ) - inchi_free( nNormAtNosInCanonOrd ); - - - return ret; -} -/***************************************************************************************/ -int FillOutOneCanonInfAtom(inp_ATOM *inp_norm_at, INF_ATOM_DATA *inf_norm_at_data, - AT_NUMB *pStereoFlags, int init_num_at, int offset, int offset_H, int bIsotopic, - INChI *pINChI, INChI_Aux *pINChI_Aux, int bAbcNumbers, INCHI_MODE nMode) -{ - int i, j, m, n, num_stereo, k, c, ret, len_str, len, atw, num_err; - int next_atom[MAX_CUMULENE_LEN+1], best_next_atom[MAX_CUMULENE_LEN+1], cur_atom; - int next_neigh[MAX_CUMULENE_LEN+1], best_next_neigh[MAX_CUMULENE_LEN+1], best_len, bIncludeIsotopicH; - int num_iso_H[NUM_H_ISOTOPES]; - char *str; - AT_NUMB g, e; - int num_at = pINChI->nNumberOfAtoms; - int num_H = init_num_at - num_at; /* number of removed H */ - int nNumberOfTGroups = (pINChI->lenTautomer && pINChI->nTautomer && pINChI->nTautomer[0])? (int)pINChI->nTautomer[0] : 0; - AT_NUMB *nOrigAtNosInCanonOrd; - INChI_Stereo *Stereo; - AT_NUMB *nConstitEquNumbers; - AT_NUMB *nConstitEquTGroupNumbers; - S_CHAR *t_parity = NULL; - AT_NUMB *nNumber = NULL; - - AT_NUMB *nNormAtNosInCanonOrd; - int (*MakeNumber)(char*, int, const char*, int) = bAbcNumbers? MakeAbcNumber:MakeDecNumber; - int bRel= (0 != (nMode & ( REQ_MODE_RELATIVE_STEREO))); - int bRac= (0 != (nMode & ( REQ_MODE_RACEMIC_STEREO))); - int bRelRac= bRel || bRac; - int bDoDisplaySp3 = 1; - - inf_ATOM *inf_norm_at = (inf_norm_at_data && inf_norm_at_data->at)? inf_norm_at_data->at+offset : NULL; - inp_ATOM *norm_at = inp_norm_at? inp_norm_at + offset : NULL; - inf_ATOM *inf_norm_at_H = (inf_norm_at_data && inf_norm_at_data->at)? inf_norm_at_data->at+offset_H : NULL; - inp_ATOM *norm_at_H = inp_norm_at? inp_norm_at + offset_H : NULL; - - ret = 0; - num_err = 0; - - if ( !inf_norm_at ) - return ret; - /* -- already added in FillOutCompositeCanonInfAtom() -- - if ( bIsotopic ) { - for ( i = 0, j = 0; i < NUM_H_ISOTOPES; i ++ ) { - if ( pINChI_Aux->nNumRemovedIsotopicH[i] ) { - inf_norm_at_data->num_iso_H[i] += pINChI_Aux->nNumRemovedIsotopicH[i]; - inf_norm_at_data->num_removed_iso_H ++; - } - } - } - */ - - if ( bIsotopic && !(pINChI->nNumberOfIsotopicAtoms || pINChI->nNumberOfIsotopicTGroups || - pINChI->nPossibleLocationsOfIsotopicH && pINChI->nPossibleLocationsOfIsotopicH[0]>1) ) - bIsotopic = 0; - - Stereo = bIsotopic? pINChI->StereoIsotopic : - pINChI->Stereo; - bDoDisplaySp3 = (NULL != Stereo) && (Stereo->nNumberOfStereoCenters > 0); - -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) - if ( bDoDisplaySp3 && bRelRac && Stereo->nNumberOfStereoCenters < 2 && - (Stereo->nCompInv2Abs || ATOM_PARITY_ILL_DEF(Stereo->t_parity[0]) ) ) { - bDoDisplaySp3 = 0; - if ( Stereo->nCompInv2Abs ) { - inf_norm_at_data->StereoFlags |= bRel? INF_STEREO_REL : bRac? INF_STEREO_RAC : 0; - } - } -#endif - /* flag has stereo */ - if ( (NULL != Stereo) && (bDoDisplaySp3 || Stereo->nNumberOfStereoBonds > 0) ) { - (*pStereoFlags) |= INF_STEREO; - } - - /* - if ( bDoDisplaySp3 && bRelRac && Stereo->nCompInv2Abs && Stereo->nNumberOfStereoCenters < 2 ) { - bDoDisplaySp3 = 0; - } - */ - if ( bDoDisplaySp3 && Stereo->nCompInv2Abs ) { - /* inversion changes stereo */ - if ( bRel ) { - (*pStereoFlags) |= INF_STEREO_REL; - } else - if ( bRac ) { - (*pStereoFlags) |= INF_STEREO_RAC; - } else { - (*pStereoFlags) |= INF_STEREO_ABS; - } - if ( bRelRac ) { - (*pStereoFlags) |= (Stereo->nCompInv2Abs > 0)? INF_STEREO_NORM : INF_STEREO_INV; - } - } - if ( bDoDisplaySp3 && Stereo->nCompInv2Abs < 0 && !bRelRac ) { - /* display Inv stereo which is Absolute Stereo */ - nNumber = Stereo->nNumberInv; - t_parity = Stereo->t_parityInv; - nOrigAtNosInCanonOrd = bIsotopic? pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv : - pINChI_Aux->nOrigAtNosInCanonOrdInv; - } else { - /* display Output stereo which is Absolute Stereo */ - if ( bDoDisplaySp3 ) { - nNumber = Stereo->nNumber; - t_parity = Stereo->t_parity; - } - nOrigAtNosInCanonOrd = bIsotopic? pINChI_Aux->nIsotopicOrigAtNosInCanonOrd : - pINChI_Aux->nOrigAtNosInCanonOrd; - } - - nConstitEquNumbers = bIsotopic? pINChI_Aux->nConstitEquIsotopicNumbers : - pINChI_Aux->nConstitEquNumbers; - nConstitEquTGroupNumbers = bIsotopic? pINChI_Aux->nConstitEquIsotopicTGroupNumbers : - pINChI_Aux->nConstitEquTGroupNumbers; - memset( inf_norm_at, 0, num_at*sizeof(inf_norm_at[0]) ); - if ( num_H > 0 ) { - memset( inf_norm_at_H, 0, num_H*sizeof(inf_norm_at[0]) ); - } - - /* obtain norm_at[] atom numbers (from zero) in order of canonical numbers */ - nNormAtNosInCanonOrd = (AT_NUMB *)inchi_calloc( num_at, sizeof(nNormAtNosInCanonOrd[0]) ); - if ( ret = GetAtomOrdNbrInCanonOrd( norm_at, nNormAtNosInCanonOrd, nOrigAtNosInCanonOrd, num_at ) ) { - goto exit_function; - } - - /* atom canonical and equivalence numbers > 0 */ - for ( i = 0; i < num_at; i ++ ) { - j = (int)nNormAtNosInCanonOrd[i]; - if ( j < 0 || j >= num_at ) - continue; - inf_norm_at[j].nCanonNbr = (AT_NUMB)(i+1); - inf_norm_at[j].nCanonEquNbr = nConstitEquNumbers[i]; -#ifdef DISPLAY_DEBUG_DATA - inf_norm_at[j].nDebugData = 0; -#if ( DISPLAY_DEBUG_DATA == DISPLAY_DEBUG_DATA_C_POINT ) - inf_norm_at[j].nDebugData = norm_at[j].c_point; -#endif -#endif - } - /* tautomeric groups */ - if ( nNumberOfTGroups ) { - /* - : start from 1: bypass number of t-groups - : j is a counter within the current t-group - : g is a tautomeric group canonical number - : e is a tautomeric group equivalence - */ - for ( g = 1, i = 1; g <= nNumberOfTGroups; g ++ ) { - n = (int)pINChI->nTautomer[i] - INCHI_T_NUM_MOVABLE; /* number of atoms in t-group */ - e = nConstitEquTGroupNumbers[(int)g - 1]; - /* bypass number of hydrogen atoms, negative charges, ... */ - for ( i += INCHI_T_NUM_MOVABLE+1, j = 0; j < n && i < pINChI->lenTautomer; j ++, i ++ ) { - /* scan canonical numbers of atoms within the atom t-group */ - k = (int)nNormAtNosInCanonOrd[(int)pINChI->nTautomer[i]-1]; - inf_norm_at[k].nTautGroupCanonNbr = g; - inf_norm_at[k].nTautGroupEquNbr = e; - } - } - if ( i != pINChI->lenTautomer || g != nNumberOfTGroups+1 ) { - ret = CT_TAUCOUNT_ERR; /* */ - goto exit_function; - } - } - /* atoms that may exchange isotopic H */ - if ( bIsotopic && pINChI->nPossibleLocationsOfIsotopicH && (n = (int)pINChI->nPossibleLocationsOfIsotopicH[0]) ) { - for ( i = 1; i < n; i ++ ) { - j = (int)pINChI->nPossibleLocationsOfIsotopicH[i]; - k = (int)nNormAtNosInCanonOrd[j - 1]; - if ( !inf_norm_at[k].nTautGroupCanonNbr ) { - inf_norm_at[k].cFlags |= AT_FLAG_ISO_H_POINT; - } - } - } -#if ( DISPLAY_RING_SYSTEMS == 1 ) - /* debug only */ - for ( j = 0; j < num_at; j ++ ) { - inf_norm_at[j].nCanonNbr = norm_at[j].nBlockSystem; - inf_norm_at[j].nCanonEquNbr = norm_at[j].nRingSystem; -#if ( USE_DISTANCES_FOR_RANKING == 1 ) - inf_norm_at[j].nTautGroupCanonNbr = norm_at[j].nDistanceFromTerminal; - inf_norm_at[j].nTautGroupEquNbr = norm_at[j].bCutVertex; -#else - inf_norm_at[j].nTautGroupCanonNbr = norm_at[j].bCutVertex; - inf_norm_at[j].nTautGroupEquNbr = 0; -#endif - } -#endif - - - - /* Write isotopic mass, chemical element symbols and hydrogens, charge, radical, canon. numbers */ - len_str = sizeof(inf_norm_at[0].at_string); - for ( i = 0; i < init_num_at; i ++ ) { - inf_ATOM *cur_inf_norm_at = (i < num_at)? inf_norm_at+i : inf_norm_at_H+i-num_at; - inp_ATOM *cur_norm_at = (i < num_at)? norm_at +i : norm_at_H +i-num_at; - str = cur_inf_norm_at->at_string; - len = 0; - bIncludeIsotopicH = bIsotopic && (i >= num_at || !inf_norm_at[i].nTautGroupCanonNbr && !(inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT)); - /* isotopic mass */ - atw = 0; - if ( cur_norm_at->iso_atw_diff && bIsotopic ) { - if ( cur_norm_at->at_type == ATT_PROTON ) { - ; /* do not set isotopic mass of a tautomeric proton */ - } else - if ( num_at <= i && cur_norm_at->el_number == PERIODIC_NUMBER_H && cur_norm_at->chem_bonds_valence == 1 && - !cur_norm_at->charge && !cur_norm_at->radical && !cur_norm_at->num_H && - 0 <= (j=(int)cur_norm_at->neighbor[0]-offset) && j < num_at && - (inf_norm_at[j].nTautGroupCanonNbr || (inf_norm_at[j].cFlags & AT_FLAG_ISO_H_POINT) ) ) { - ; /* do not set isotopic mass of an exchangeable proton */ - } else { - atw = get_atw(cur_norm_at->elname); - atw += (cur_norm_at->iso_atw_diff>0)? cur_norm_at->iso_atw_diff-1:cur_norm_at->iso_atw_diff; - /*len += sprintf( str+len, "^%d", atw );*/ - } - } - /* element name */ - if ( cur_norm_at->el_number == PERIODIC_NUMBER_H && 2 <= atw && atw <= 3 ) { - len += sprintf( str+len, "%s", atw==2? "D" : "T" ); - } else { - if ( atw ) { - len += sprintf( str+len, "^%d", atw ); - } - len += sprintf( str+len, "%s", cur_norm_at->elname ); - } - /* hydrogens */ - /* find number of previuosly removed terminal hydrogen atoms */ - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { - num_iso_H[j] = cur_norm_at->num_iso_H[j]; - } - for ( j = 0, n = (int)cur_norm_at->num_H; j < num_H; j ++ ) { - /* subtract number of removed terminal */ - /* H atoms from the total number of H atoms */ - if ( i == (int)norm_at_H[j].neighbor[0]-offset ) { - n -= 1; - m = (int)norm_at_H[j].iso_atw_diff-1; - if ( 0 <= m && m < NUM_H_ISOTOPES ) { - /* subtract number of removed terminal isotopic */ - /* H atoms from the total number of isotopic H atoms */ - num_iso_H[m] -= 1; - } - } - } - if ( bIncludeIsotopicH ) { - /* subtract number of isotopic H atoms from the total number of H atoms */ - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { - n -= num_iso_H[j]; - } - } - /* non-isotopic hydrogen atoms */ - if ( n > 1 ) { - len += sprintf( str+len, "H%d", n ); - } else - if ( n == 1 ) { - len += sprintf( str+len, "H" ); - } - /* isotopic hydrogen atoms */ - if ( bIncludeIsotopicH ) { - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { - if ( num_iso_H[j] ) { - if ( j == 0 || j != 1 && j != 2 ) { - len += sprintf( str+len, "^%dH", j+1 ); - } else { - len += sprintf( str+len, j == 1? "D" : "T" ); - } - if ( num_iso_H[j] != 1 ) { - len += sprintf( str+len, "%d", (int)num_iso_H[j] ); - } - } - } - } - if ( cur_norm_at->el_number == PERIODIC_NUMBER_H && str[0] == str[1] ) { - char *q; - if ( !str[2] ) { - str[1] = '2'; /* quick fix: replace HH with H2 */ - } else - if ( isdigit( UCINT str[2] ) && (n = strtol( str+2, &q, 10 )) && !q[0] ) { - len = 1 + sprintf( str+1, "%d", n+1 ); - } - } - /* charge */ - if ( abs(cur_norm_at->charge) > 1 ) - len += sprintf( str+len, "%+d", cur_norm_at->charge ); - else - if ( abs(cur_norm_at->charge) == 1 ) - len += sprintf( str+len, "%s", cur_norm_at->charge>0? "+" : "-" ); - /* radical */ - if ( cur_norm_at->radical ) - len += sprintf( str+len, "%s", cur_norm_at->radical==RADICAL_SINGLET? ":" : - cur_norm_at->radical==RADICAL_DOUBLET? "." : - cur_norm_at->radical==RADICAL_TRIPLET? ".." : "?"); - } - - /* stereogenic centers */ - if ( bDoDisplaySp3 && Stereo && 0 < (num_stereo = Stereo->nNumberOfStereoCenters) ) { - for ( i = 0; i < num_stereo; i ++ ) { - j = (int)nNormAtNosInCanonOrd[(int)nNumber[i]-1]; - c = t_parity[i]; - c = c==1? '-' : c==2? '+' : c==3? 'u' : c== 4? '?' : '*'; - inf_norm_at[j].cStereoCenterParity = c; - str=inf_norm_at[j].at_string; - len = strlen(str); - if ( len + 3 < (int)sizeof(inf_norm_at[0].at_string) ) { - str[len++] = '('; - str[len++] = inf_norm_at[j].cStereoCenterParity; - str[len++] = ')'; - str[len] = '\0'; - /* mark ambuguous stereo center */ - if ( norm_at[j].bAmbiguousStereo && (c=='+' || c=='-' || c=='?') && str[0] != '!' && - len+1 < (int)sizeof(inf_norm_at[0].at_string) ) { - memmove( str+1, str, len+1 ); - str[0] = '!'; /* output the atom in red color */ - } - } - } - } - - /* stereogenic bonds */ - /* (cumulenes with odd number of double bonds are stereocenters, */ - /* and atom parity should be set) */ - if ( Stereo && 0 < (num_stereo = Stereo->nNumberOfStereoBonds) ) { - for ( i = 0; i < num_stereo; i ++ ) { - int start_at, num_eql=0, bAmbiguousStereoBond = 0; - j = (int)nNormAtNosInCanonOrd[(int)Stereo->nBondAtom1[i]-1]; - k = (int)nNormAtNosInCanonOrd[(int)Stereo->nBondAtom2[i]-1]; - start_at = j; - c = Stereo->b_parity[i]; - - c = c==1? '-' : c==2? '+' : c==3? 'u' : c== 4? '?' : '*'; - - /* mark ambuguous stereo bond atom(s) */ - if ( norm_at[j].bAmbiguousStereo && (c=='+' || c=='-' ) && - (len=strlen(str=inf_norm_at[j].at_string)+1) < (int)sizeof(inf_norm_at[0].at_string) && - str[0] != '!' ) { - memmove( str+1, str, len ); - str[0] = '!'; /* output the atom in red color */ - bAmbiguousStereoBond ++; - } - if ( norm_at[k].bAmbiguousStereo && (c=='+' || c=='-') && - (len=strlen(str=inf_norm_at[k].at_string)+1) < (int)sizeof(inf_norm_at[0].at_string) && - str[0] != '!' ) { - memmove( str+1, str, len ); - str[0] = '!'; /* output the atom in red color */ - bAmbiguousStereoBond ++; - } - - /* find the opposite atom k. */ - /* Note: since it may be a cumulene, find the shortest(best) path */ - /* to atom number k to avoid confusion in case of, for example, */ - /* 4-member aromatic rings. */ - best_len = MAX_CUMULENE_LEN+1; /* moved here from inside the cycle 1-8-2003 */ - for ( n = 0; n < norm_at[j].valence; n ++ ) { - if ( norm_at[j].bond_type[n] == BOND_SINGLE ) { - /* single bond cannot be stereogenic. */ - continue; - } - /* best_len = MAX_CUMULENE_LEN+1; */ - len = 0; /* number of bonds in cumulene - 1 */ - next_atom[len] = (int)norm_at[j].neighbor[n]-offset; - next_neigh[len] = n; - cur_atom = j; - while ( next_atom[len] != k && len < MAX_CUMULENE_LEN && 2 == norm_at[next_atom[len]].valence ) { - next_neigh[len+1] = ((int)norm_at[next_atom[len]].neighbor[0]-offset == cur_atom); - next_atom[len+1] = (int)norm_at[next_atom[len]].neighbor[next_neigh[len+1]]-offset; - cur_atom = next_atom[len]; - len ++; - } - if ( next_atom[len] == k ) { - if ( len < best_len ) { - memcpy( best_next_neigh, next_neigh, sizeof(best_next_neigh) ); - memcpy( best_next_atom, next_atom, sizeof(best_next_atom) ); - best_len = len; - num_eql = 0; - if ( len == 0 ) { - break; /* path length cannot be smaller than 1 */ - } - } else - if ( len == best_len ) { - num_eql ++; - } - } - } - if ( best_len <= MAX_CUMULENE_LEN && best_next_atom[best_len] == k ) { - if ( num_eql ) { - num_err ++; /* program error; no breakpoint here */ - } - if ( best_len % 2 ) { - /* even number of bonds: chiral atom, draw parity on the cenrtal atom */ - j = best_next_atom[best_len/2]; - inf_norm_at[j].cStereoCenterParity = c; - str=inf_norm_at[j].at_string; - len = strlen(str); - if ( len + 3 < (int)sizeof(inf_norm_at[0].at_string) ) { - str[len++] = '('; - str[len++] = inf_norm_at[j].cStereoCenterParity; - str[len++] = ')'; - str[len] = '\0'; - } - } else { - /* odd number of bonds: draw parity on the central bond */ - if ( best_len == 0 ) { - /* double bond */ - j = start_at; - k = best_next_neigh[0]; - } else { - /* cumulene */ - best_len = best_len/2-1; - j = best_next_atom[best_len]; - k = best_next_neigh[best_len+1]; /* added +1 to display cumulene parity on the middle bond (6-24-2002) */ - } - /* mark "forward" bond */ - for ( m = 0; m < MAX_STEREO_BONDS && inf_norm_at[j].cStereoBondParity[m]; m ++ ) - ; - if ( m < MAX_STEREO_BONDS ) { - inf_norm_at[j].cStereoBondParity[m] = c; - inf_norm_at[j].cStereoBondNumber[m] = k; - inf_norm_at[j].cStereoBondWarning[m] = bAmbiguousStereoBond; - } else { - num_err ++; /* program error; no breakpoint here */ - } - /* mark "backward" bond */ - n = norm_at[j].neighbor[k]-offset; - for ( k = 0; k < norm_at[n].valence && j != (int)norm_at[n].neighbor[k]-offset; k ++ ) - ; - if ( k < norm_at[n].valence ) { - j = n; - for ( m = 0; m < MAX_STEREO_BONDS && inf_norm_at[j].cStereoBondParity[m]; m ++ ) - ; - if ( m < MAX_STEREO_BONDS ) { - inf_norm_at[j].cStereoBondParity[m] = c; - inf_norm_at[j].cStereoBondNumber[m] = k; - inf_norm_at[j].cStereoBondWarning[m] = bAmbiguousStereoBond; - } else { - num_err ++; /* program error; no breakpoint here */ - } - } else { - num_err ++; /* program error; no breakpoint here */ - } - } - } else { - num_err ++; /* program error; no breakpoint here */ - } - } - } - - for ( i = 0; i < num_at; i ++ ) { - /* canonical numbers */ - if ( inf_norm_at[i].nCanonNbr ) { - str = inf_norm_at[i].at_string; - len = strlen(str); - len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nCanonNbr ); - if ( inf_norm_at[i].nCanonEquNbr || inf_norm_at[i].nTautGroupCanonNbr || (inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT) ) { - if ( inf_norm_at[i].nCanonEquNbr ) { - len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nCanonEquNbr ); - } else - if ( len + 1 < len_str ) { - len += 1; - strcat( str, "/" ); - } - } - /* tautomeric groups */ - if ( inf_norm_at[i].nTautGroupCanonNbr ) { - len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nTautGroupCanonNbr ); - if ( inf_norm_at[i].nTautGroupEquNbr ) { - len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nTautGroupEquNbr ); - } - } - if ( (inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT) && len+2 <= len_str ) { - str[len++] = '/'; - str[len++] = '*'; - str[len] = '\0'; - } -#ifdef DISPLAY_DEBUG_DATA - if ( inf_norm_at[i].nDebugData ) { - len += (*MakeNumber)( str+len, len_str-len, "`", (int)inf_norm_at[i].nDebugData ); - } -#endif - } - } - - -exit_function: - - if ( nNormAtNosInCanonOrd ) - inchi_free( nNormAtNosInCanonOrd ); - - return ret; -} - -/***************************************************************************************/ -int FillOutInputInfAtom(inp_ATOM *inp_at, INF_ATOM_DATA *inf_at_data, int init_num_at, int num_removed_H, - int bAdd_DT_to_num_H, int nNumRemovedProtons, NUM_H *nNumRemovedProtonsIsotopic, int bIsotopic, int bAbcNumbers) -{ - int i, j, m, n, ret, len_str, len, atw; - int num_iso_H[NUM_H_ISOTOPES]; - char *str; - int num_at = init_num_at - num_removed_H; - int (*MakeNumber)(char*, int, const char*, int) = MakeDecNumber; - - inf_ATOM *inf_at = inf_at_data? inf_at_data->at : NULL; - - - ret = 0; - - - if ( !inf_at ) - return ret; - - memset( inf_at, 0, init_num_at*sizeof(inf_at[0]) ); - - inf_at_data->nNumRemovedProtons = nNumRemovedProtons; - MakeRemovedProtonsString( nNumRemovedProtons, nNumRemovedProtonsIsotopic, NULL, bIsotopic, inf_at_data->szRemovedProtons, NULL ); - /* atom canonical and equivalence numbers > 0 */ - for ( i = 0; i < num_at; i ++ ) { -#if ( DISPLAY_ORIG_AT_NUMBERS == 1 ) - inf_at[i].nCanonNbr = inp_at[i].orig_at_number; -#else - inf_at[i].nCanonNbr = (AT_NUMB)(i+1); -#endif - } - /* Write isotopic mass, chemical element symbols and hydrogens, charge, radical, canon. numbers */ - len_str = sizeof(inf_at[0].at_string); - for ( i = 0; i < init_num_at; i ++ ) { - str = inf_at[i].at_string; - len = 0; - /* isotopic mass */ - atw = 0; - if ( inp_at[i].iso_atw_diff && bIsotopic ) { - atw = get_atw(inp_at[i].elname); - atw += (inp_at[i].iso_atw_diff>0)? inp_at[i].iso_atw_diff-1:inp_at[i].iso_atw_diff; - /*len += sprintf( str+len, "^%d", atw );*/ - } - /* element name */ - if ( inp_at[i].el_number == PERIODIC_NUMBER_H && 2 <= atw && atw <= 3 ) { - len += sprintf( str+len, "%s", atw==2? "D" : "T" ); - } else { - if ( atw ) { - len += sprintf( str+len, "^%d", atw ); - } - len += sprintf( str+len, "%s", inp_at[i].elname ); - } - /* hydrogens */ - /* find number of previuosly removed terminal hydrogen atoms */ - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { - num_iso_H[j] = inp_at[i].num_iso_H[j]; - } - for ( j = num_at, n = (int)inp_at[i].num_H; j < init_num_at; j ++ ) { - /* subtract number of removed terminal */ - /* H atoms from the total number of H atoms */ - if ( i == (int)inp_at[j].neighbor[0] ) { - n -= 1; - m = (int)inp_at[j].iso_atw_diff-1; - if ( 0 <= m && m < NUM_H_ISOTOPES ) { - /* subtract number of removed terminal isotopic */ - /* H atoms from the total number of isotopic H atoms */ - num_iso_H[m] -= 1; - } - } - } - if ( bIsotopic && !bAdd_DT_to_num_H ) { - /* subtract number of isotopic H atoms from the total number of H atoms */ - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { - n -= num_iso_H[j]; - } - } - /* non-isotopic hydrogen atoms */ - if ( n > 1 ) { - len += sprintf( str+len, "H%d", n ); - } else - if ( n == 1 ) { - len += sprintf( str+len, "H" ); /* fixed 12-21-2002: removed 3rd argument */ - } - if ( bIsotopic ) { - /* isotopic hydrogen atoms */ - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { - if ( num_iso_H[j] ) { - if ( j == 0 || j != 1 && j != 2 ) { - len += sprintf( str+len, "^%dH", j+1 ); - } else { - len += sprintf( str+len, j == 1? "D" : "T" ); - } - if ( num_iso_H[j] != 1 ) { - len += sprintf( str+len, "%d", (int)num_iso_H[j] ); - } - } - } - } - if ( inp_at[i].el_number == PERIODIC_NUMBER_H && str[0] == str[1] ) { - char *q; - if ( !str[2] ) { - str[1] = '2'; /* quick fix: replace HH with H2 */ - } else - if ( isdigit( UCINT str[2] ) && (n = strtol( str+2, &q, 10 )) && !q[0] ) { - len = 1 + sprintf( str+1, "%d", n+1 ); - } - } - /* - if ( str[0] == 'H' && str[1] == 'H' && !str[2] ) { - str[1] = '2'; - } - */ - /* charge */ - if ( abs(inp_at[i].charge) > 1 ) - len += sprintf( str+len, "%+d", inp_at[i].charge ); - else - if ( abs(inp_at[i].charge) == 1 ) - len += sprintf( str+len, "%s", inp_at[i].charge>0? "+" : "-" ); - /* radical */ - if ( inp_at[i].radical ) - len += sprintf( str+len, "%s", inp_at[i].radical==RADICAL_SINGLET? ":" : - inp_at[i].radical==RADICAL_DOUBLET? "." : - inp_at[i].radical==RADICAL_TRIPLET? ".." : "?"); - } - - for ( i = 0; i < init_num_at; i ++ ) { - /* canonical numbers */ - if ( inf_at[i].nCanonNbr ) { - str = inf_at[i].at_string; - len = strlen(str); - len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_at[i].nCanonNbr ); - if ( inf_at[i].nCanonEquNbr || inf_at[i].nTautGroupCanonNbr ) { - len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_at[i].nCanonEquNbr ); - } - /* tautomeric groups */ - if ( inf_at[i].nTautGroupCanonNbr ) { - len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_at[i].nTautGroupCanonNbr ); - if ( inf_at[i].nTautGroupEquNbr ) { - len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_at[i].nTautGroupEquNbr ); - } - } - } - } - ret = init_num_at; - - return ret; -} -/**********************************************************************************************/ -int FillOutInfAtom(inp_ATOM *norm_at, INF_ATOM_DATA *inf_norm_at_data, int init_num_at, int num_removed_H, - int bAdd_DT_to_num_H, int nNumRemovedProtons, NUM_H *nNumRemovedProtonsIsotopic, int bIsotopic, - INChI *pINChI, INChI_Aux *pINChI_Aux, int bAbcNumbers, INCHI_MODE nMode ) -{ - if ( norm_at && inf_norm_at_data && inf_norm_at_data->at ) { - if ( pINChI && pINChI_Aux ) { - return FillOutCanonInfAtom( norm_at, inf_norm_at_data, init_num_at, bIsotopic, pINChI, - pINChI_Aux, bAbcNumbers, nMode); - } else { - return FillOutInputInfAtom( norm_at, inf_norm_at_data, init_num_at, num_removed_H, bAdd_DT_to_num_H, - nNumRemovedProtons, nNumRemovedProtonsIsotopic, bIsotopic, bAbcNumbers); - } - } - return 0; -} -/***************************************************************************************/ -int FillOutCompositeCanonInfAtom(COMP_ATOM_DATA *composite_norm_data, INF_ATOM_DATA *inf_norm_at_data, - int bIsotopic, int bTautomeric, - PINChI2 *pINChI2, PINChI_Aux2 *pINChI_Aux2, int bAbcNumbers, INCHI_MODE nMode) -{ - int i, num_components, j, k, ret; - inp_ATOM *inp_norm_at; - INChI *pINChI; - INChI_Aux *pINChI_Aux; - int num_inp_at, num_at, num_H, offset, offset_H, next_offset, next_offset_H; - - if ( composite_norm_data && inf_norm_at_data && (bTautomeric == TAUT_INI || pINChI2 && pINChI_Aux2) ) { - composite_norm_data += bTautomeric; - inp_norm_at = composite_norm_data->at; - num_components = composite_norm_data->num_components; - offset = 0; - offset_H = composite_norm_data->num_at - composite_norm_data->num_removed_H; - if ( bTautomeric == TAUT_INI ) { - ret = FillOutInputInfAtom( composite_norm_data->at, inf_norm_at_data, composite_norm_data->num_at, - composite_norm_data->num_removed_H, 0 /*bAdd_DT_to_num_H*/, - composite_norm_data->nNumRemovedProtons, - composite_norm_data->nNumRemovedProtonsIsotopic, - bIsotopic, bAbcNumbers); - return ret; - } else { - for ( i = 0; i < num_components; i ++ ) { - j = inchi_min(bTautomeric, TAUT_YES); - /* count isotopic H on removed atoms -- isolated H(+) cations */ - inf_norm_at_data->nNumRemovedProtons += pINChI_Aux2[i][j]->nNumRemovedProtons; - if ( bIsotopic && bTautomeric == TAUT_YES ) { - for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) { - if ( pINChI_Aux2[i][j]->nNumRemovedIsotopicH[k] ) { - inf_norm_at_data->num_iso_H[k] += pINChI_Aux2[i][j]->nNumRemovedIsotopicH[k]; - inf_norm_at_data->num_removed_iso_H += pINChI_Aux2[i][j]->nNumRemovedIsotopicH[k]; - } - } - } - /* ignore deleted components */ - if ( pINChI2[i][j] && pINChI2[i][j]->bDeleted ) { - continue; - } - if ( !pINChI2[i][j] || !pINChI2[i][j]->nNumberOfAtoms ) { - j = ALT_TAUT(j); - if ( !pINChI2[i][j] || !pINChI2[i][j]->nNumberOfAtoms ) { - continue; /* error ??? */ - } - } - pINChI = pINChI2[i][j]; - pINChI_Aux = pINChI_Aux2[i][j]; - next_offset = composite_norm_data->nOffsetAtAndH[2*i]; - next_offset_H = composite_norm_data->nOffsetAtAndH[2*i+1]; - num_at = next_offset - offset; - if ( num_at <= 0 ) - continue; - num_H = next_offset_H - offset_H; - num_inp_at = num_at + num_H; - if ( num_at != pINChI->nNumberOfAtoms || num_at != pINChI_Aux->nNumberOfAtoms ) { - return 0; /* error */ - } - ret = FillOutOneCanonInfAtom(inp_norm_at, inf_norm_at_data, - inf_norm_at_data->pStereoFlags+i+1, num_inp_at, - offset, offset_H, bIsotopic, pINChI, pINChI_Aux, bAbcNumbers, nMode); - if ( ret ) - return 0; /* error */ - - inf_norm_at_data->StereoFlags |= inf_norm_at_data->pStereoFlags[i+1]; - offset = next_offset; - offset_H = next_offset_H; - } - } - MakeRemovedProtonsString( inf_norm_at_data->nNumRemovedProtons, inf_norm_at_data->num_iso_H, NULL, bIsotopic, - inf_norm_at_data->szRemovedProtons, &inf_norm_at_data->num_removed_iso_H ); - } - return 1; -} -#endif /* } ifndef COMPILE_ANSI_ONLY */ -/**********************************************************************************************/ -int CheckCanonNumberingCorrectness(int num_atoms, int num_at_tg, - sp_ATOM *at, CANON_STAT *pCS, int bTautomeric, - char *pStrErrStruct ) -{ - int i, ret=0; - AT_NUMB *pCanonOrd=NULL; - int nErrorCode = 0; - AT_NUMB *pCanonRank; /* canonical ranks of the atoms or tautomeric groups */ - AT_NUMB *pCanonRankAtoms=NULL; - - static int count=0; /* for debug only */ - count ++; - - pCanonRankAtoms = (AT_NUMB *)inchi_calloc( num_at_tg+1, sizeof(pCanonRankAtoms[0]) ); - - /********************************************************************************************** - * - * non-isotopic part - */ - pCanonOrd = pCS->nLenCanonOrdStereo > 0? pCS->nCanonOrdStereo : - pCS->nLenCanonOrd > 0? pCS->nCanonOrd : NULL; - pCanonRank = pCanonRankAtoms; - if ( pCanonOrd && pCanonRank ) { - for ( i = 0; i < num_at_tg; i ++ ) { - pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); - } - ret = UpdateFullLinearCT( num_atoms, num_at_tg, at, pCanonRank, pCanonOrd, pCS, 0 ); - if ( ret /*|| memcmp(pCS->LinearCT, pCS->LinearCT2, sizeof(AT_RANK) * pCS->nLenLinearCT )*/ ) { - nErrorCode |= WARN_FAILED_STEREO; - } - - } else { - nErrorCode |= ERR_NO_CANON_RESULTS; - goto exit_function; - } - /********************************************************************************************** - * - * isotopic part - */ - pCanonOrd = pCS->nLenCanonOrdIsotopicStereo > 0? pCS->nCanonOrdIsotopicStereo : - pCS->nLenCanonOrdIsotopic > 0? pCS->nCanonOrdIsotopic : NULL; - pCanonRank = pCanonRankAtoms; - - if ( pCanonOrd && pCanonRank ) { - for ( i = 0; i < num_at_tg; i ++ ) { - pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); - } - ret = UpdateFullLinearCT( num_atoms, num_at_tg, at, pCanonRank, pCanonOrd, pCS, 0 ); - if ( ret /*|| memcmp(pCS->LinearCT, pCS->LinearCT2, sizeof(AT_RANK) * pCS->nLenLinearCT )*/ ) { - nErrorCode |= (pCS->nLenCanonOrdIsotopicStereo? WARN_FAILED_ISOTOPIC_STEREO : WARN_FAILED_ISOTOPIC); - } - - } - -exit_function: - if ( pCanonRankAtoms ) - inchi_free( pCanonRankAtoms ); - - if ( nErrorCode ) { - return CT_CANON_ERR; - } - return 0; -} +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + +#include +#include +#include + +#include "mode.h" +#include "ichimake.h" +#include "inchi_api.h" +#include "ichimain.h" +#include "ichister.h" +#include "ichi_io.h" +#include "ichitime.h" +#include "ichi_bns.h" + +/* + Local functions +*/ + +int inp2spATOM( inp_ATOM *inp_at, int num_inp_at, sp_ATOM *at ); +int GetElementAndCount( const char **f, char *szEl, int *count ); +int CompareHillFormulas( const char *f1, const char *f2 ); +int CompareInchiStereo( INChI_Stereo *Stereo1, + INCHI_MODE nFlags1, + INChI_Stereo *Stereo2, + INCHI_MODE nFlags2 ); +int CompareReversedStereoINChI( INChI_Stereo *s1, + INChI_Stereo *s2); +int GetAtomOrdNbrInCanonOrd( struct tagCANON_GLOBALS *pCG, + inp_ATOM *norm_at, + AT_NUMB *nAtomOrdNbr, + AT_NUMB *nOrigAtNosInCanonOrd, + int num_at ); +int FillOutCanonInfAtom( struct tagCANON_GLOBALS *pCG, + inp_ATOM *norm_at, + INF_ATOM_DATA *inf_norm_at_data, + int init_num_at, + int bIsotopic, + INChI *pINChI, + INChI_Aux *pINChI_Aux, + int bAbcNumbers, + INCHI_MODE nMode); +int FillOutOneCanonInfAtom( struct tagCANON_GLOBALS *pCG, + inp_ATOM *inp_norm_at, + INF_ATOM_DATA *inf_norm_at_data, + AT_NUMB *pStereoFlags, + int init_num_at, + int offset, + int offset_H, + int bIsotopic, + INChI *pINChI, + INChI_Aux *pINChI_Aux, + int bAbcNumbers, + INCHI_MODE nMode); +int FillOutInputInfAtom( inp_ATOM *inp_at, + INF_ATOM_DATA *inf_at_data, + int init_num_at, + int num_removed_H, + int bAdd_DT_to_num_H, + int nNumRemovedProtons, + NUM_H *nNumRemovedProtonsIsotopic, + int bIsotopic, + int bAbcNumbers); +int CheckCanonNumberingCorrectness( int num_atoms, + int num_at_tg, + sp_ATOM *at, + CANON_STAT *pCS, + CANON_GLOBALS *pCG, + int bTautomeric, + char *pStrErrStruct ); +static int CompareDfsDescendants4CT( const void *a1, const void *a2, void* ); +int GetSp3RelRacAbs( const INChI *pINChI, INChI_Stereo *Stereo ); +#if ( READ_INCHI_STRING == 1 ) /* { */ +int CompareReversedStereoINChI2( INChI_Stereo *s1, INChI_Stereo *s2, ICR *picr); +#endif + +/* + inp2spATOM( ... ) +*/ +int inp2spATOM( inp_ATOM *inp_at, int num_inp_at, sp_ATOM *at ) +{ +int i, j, val; + + memset( at, 0, sizeof(at[0])*num_inp_at ); + + for ( i = 0; i < num_inp_at; i ++ ) + { + strncpy( at[i].elname, inp_at[i].elname, sizeof(at[0].elname) ); + at[i].el_number = (U_CHAR)get_periodic_table_number( at[i].elname ); + val = at[i].valence = inp_at[i].valence; + for ( j = 0; j < val; j ++ ) + { + at[i].neighbor[j] = inp_at[i].neighbor[j]; + at[i].bond_type[j] = inp_at[i].bond_type[j]; + } + at[i].chem_bonds_valence = inp_at[i].chem_bonds_valence; + at[i].orig_at_number = inp_at[i].orig_at_number; + at[i].orig_compt_at_numb= inp_at[i].orig_compt_at_numb; + at[i].endpoint = inp_at[i].endpoint; + at[i].iso_atw_diff = inp_at[i].iso_atw_diff; + at[i].num_H = inp_at[i].num_H; + at[i].cFlags = inp_at[i].cFlags; + for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) + { + at[i].num_iso_H[j] = inp_at[i].num_iso_H[j]; + } + at[i].charge = inp_at[i].charge; + at[i].radical = inp_at[i].radical; + +#if ( FIND_RING_SYSTEMS == 1 ) + at[i].nBlockSystem = inp_at[i].nBlockSystem; + at[i].bCutVertex = inp_at[i].bCutVertex; + at[i].nRingSystem = inp_at[i].nRingSystem; + at[i].nNumAtInRingSystem = inp_at[i].nNumAtInRingSystem; +#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) + at[i].nDistanceFromTerminal = inp_at[i].nDistanceFromTerminal; +#endif +#endif + +/* + at[i].x = inp_at[i].x; + at[i].y = inp_at[i].y; + at[i].z = inp_at[i].z; +*/ + } + + return 0; +} + +int GetElementAndCount( const char **f, char *szEl, int *count ) +{ +const char *p = *f; +char *q; +int i = 0; + + if ( *p ) + { + if ( isupper( UCINT *p ) ) + { + szEl[i++] = *p++; + if ( *p && islower( UCINT *p ) ) + { + szEl[i++] = *p++; + } + szEl[i] = '\0'; + if ( 1 == i && szEl[0] == 'C' ) + { + szEl[0] = 'A'; /* less than any element: */ + /* carbon-containing compounds should be first */ + } + if ( *p && isdigit( UCINT *p ) ) + { + *count = strtol( p, &q, 10 ); + p = q; + } + else + { + *count = 1; + } + *f = p; /* next element; */ + return 1; + } + return -1; /* not a chemical formula */ + } + strcpy( szEl, "Zz" ); /* zero termination 'element' is larger than any other element */ + *count = 99999; /* zero termination 'element count' is larger than any other count */ + return 0; +} + +/* + CompareHillFormulas( ... ) + + E1 < E2 if strcmp( E1, E2) < 0 OR E2 is empty and E1 is not + n1 < n2 if value n1 > n2 + + Sorting order: + + C10H22N + C10H22 + C2 + Ag2Cl2 + Ag2Cl + Ag2F2 + Ag2 + AgCl + AgF + F6S + F2S +*/ +int CompareHillFormulas( const char *f1, const char *f2 ) +{ + char szEl1[4], szEl2[4]; + int count1, count2, ret1, ret2, ret; + + do { + ret1 = GetElementAndCount( &f1, szEl1, &count1 ); + ret2 = GetElementAndCount( &f2, szEl2, &count2 ); + if ( 0 <= ret1 && 0 <= ret2 ) { + if ( ret = strcmp( szEl1, szEl2 ) ) { + return ret; /* lexicographic order, string termination > any character */ + } + if ( ret = count2 - count1 ) { + return ret; /* inverse atom count order */ + } + } else { + return 0; /* program error */ + } + } while ( 0 < ret1 && 0 < ret2 ); + + return 0; +} + +int CompareHillFormulasNoH( const char *f1, + const char *f2, + int *num_H1, + int *num_H2 ) +{ + char szEl1[4], szEl2[4]; + int count1, count2, ret1, ret2, ret; + + do { + ret1 = GetElementAndCount( &f1, szEl1, &count1 ); + if ( 0 < ret1 && szEl1[0] == 'H' && !szEl1[1] ) { + *num_H1 += count1; + ret1 = GetElementAndCount( &f1, szEl1, &count1 ); + } + ret2 = GetElementAndCount( &f2, szEl2, &count2 ); + if ( 0 < ret2 && szEl2[0] == 'H' && !szEl2[1] ) { + *num_H2 += count2; + ret2 = GetElementAndCount( &f2, szEl2, &count2 ); + } + if ( 0 <= ret1 && 0 <= ret2 ) { + if ( ret = strcmp( szEl1, szEl2 ) ) { + return ret; /* lexicographic order, string termination > any character */ + } + if ( ret = count2 - count1 ) { + return ret; /* inverse atom count order */ + } + } else { + return 0; /* program error */ + } + } while ( 0 < ret1 && 0 < ret2 ); + + return 0; +} + +int CompareTautNonIsoPartOfINChI( const INChI *i1, const INChI *i2 ) +{ + int len1, len2, ret, i; + + len1 = i1->lenTautomer > 0 && i1->nTautomer[0]? i1->lenTautomer:0; + len2 = i2->lenTautomer > 0 && i2->nTautomer[0]? i2->lenTautomer:0; + if ( ret = len2 - len1 ) { + return ret; + } + for ( i = 0; i < len1; i ++ ) { + if ( ret = (int)i2->nTautomer[i] - (int)i1->nTautomer[i] ) + return ret; + } + return 0; +} + +/* + CompINChITautVsNonTaut( ... ) + + Sorting in descending order: + return -1 if *p1 > *p2, return +1 if *p1 < *p2 +*/ +int CompINChITautVsNonTaut( const INCHI_SORT *p1, + const INCHI_SORT *p2, + int bCompareIsotopic) +{ + int ret, num, i, num_H1, num_H2; + + const INChI *i1 = NULL; /* Mobile-H layers in Mobile-H sorting order */ + const INChI *i2 = NULL; /* Fixed-H layers in Fixed-H sorting order */ + + int n1; /* TAUT_YES if tautomeric i1 exists, otherwise TAUT_NON */ + + /* INChI_Stereo *Stereo1, *Stereo2; */ + + n1 = ( p1->pINChI[TAUT_YES] && p1->pINChI[TAUT_YES]->nNumberOfAtoms )? TAUT_YES : TAUT_NON; + + i1 = p1->pINChI[n1]; + i2 = (n1 == TAUT_YES && p2->pINChI[TAUT_NON] && + p2->pINChI[TAUT_NON]->nNumberOfAtoms)? p2->pINChI[TAUT_NON] : (const INChI *)NULL; + + /* non-deleted-non-empty < deleted < empty */ + if ( i1 && !i2 ) + return 0; /* non-empty is the smallest (first) */ + if ( !i1 && i2 ) + return 0; + if ( !i1 && !i2 ) + return 0; + if ( i1->bDeleted ) + return 1; /* deleted is the largest (last) among non-empty */ + if ( i2->bDeleted ) + return -1; + + if ( i1->nNumberOfAtoms > 0 && !i2->nNumberOfAtoms ) + return 0; + + i2 = i2; + + num_H1 = num_H2 = 0; + + /* do not compare terminal H */ + if ( ret = CompareHillFormulasNoH( i1->szHillFormula, i2->szHillFormula, &num_H1, &num_H2 ) ) { + return ret; /* lexicographic order except the shorter one is greater (last): CH2O < CH2; C3XX < C2XX */ + } + + /* + compare non-isotopic non-tautomeric part + */ + + /* compare number of atoms (excluding terminal H) */ + if ( ret = i2->nNumberOfAtoms - i1->nNumberOfAtoms ) + return ret; /* more atoms first */ + + /* compare elements (excluding terminal H) */ + num = i1->nNumberOfAtoms; + for ( i = 0; i < num; i ++ ) { /* should always be equal if Hill formulas are same */ + if ( ret = (int)i2->nAtom[i] - (int)i1->nAtom[i] ) + return ret; /* greater periodic number first */ + } + /* + compare connection tables + */ + if ( ret = i2->lenConnTable - i1->lenConnTable ) + return ret; /* longer connection table first */ + num = i2->lenConnTable; + for ( i = 0; i < num; i ++ ) { + if ( ret = (int)i2->nConnTable[i] - (int)i1->nConnTable[i] ) + return ret; /* greater connection table first */ + } + /* + compare total number of H (inverse: H3 < H2 ) + */ + if ( ret = num_H2 - num_H1 ) + return ret; + /* + compare non-tautomeric num_H: N < NH3 < NH2 < NH + */ + num = i1->nNumberOfAtoms; + for ( i = 0; i < num; i ++ ) { + if ( i2->nNum_H[i] != i1->nNum_H[i] ) { + return !i2->nNum_H[i]? 1 : /* no H first */ + !i1->nNum_H[i]? -1 : + (int)i2->nNum_H[i] - (int)i1->nNum_H[i]; + } + } + /* + compare non-isotopic tautomeric part + */ + if ( ret = CompareTautNonIsoPartOfINChI( i1, i2) ) { + return ret; + } + /* + if ( ret = i2->lenTautomer - i1->lenTautomer ) + return ret; + num = inchi_min( i2->lenTautomer, i1->lenTautomer ); + for ( i = 0; i < num; i ++ ) { + if ( ret = (int)i2->nTautomer[i] - (int)i1->nTautomer[i] ) + return ret; + } + */ + + /* + at this point both components are either tautomeric + or non-tautomeric + */ + + /* + non-tautomeric "fixed H" specific + */ + if ( /*TAUT_NON == bTaut &&*/ (i2 && i2->nNum_H_fixed ) ) + { + /* first, compare non-tautomeric chem. formulas -- they may be different */ + /* secondly, compare fixed-H distribution */ + if ( i2->nNum_H_fixed ) { + num = i2->nNumberOfAtoms; + for ( i = 0; i < num; i ++ ) { + if ( i2->nNum_H_fixed[i] != 0 ) { + return 1; + } + } + } + } + /* + compare non-isotopic stereo + */ + ret = CompareInchiStereo( i1->Stereo, i1->nFlags, i2->Stereo, i2->nFlags ); + if ( ret ) { + return ret; + } + + /* + do not switch back to tautomeric i1, i2 + */ + + /* -- how to switch back -- + if ( i1t ) { + i1 = i1t; + i1t = NULL; + } + if ( i2t ) { + i2 = i2t; + i2t = NULL; + } + */ + + /* + compare isotopic non-tautomeric part + */ + if ( bCompareIsotopic ) { + if ( ret = i2->nNumberOfIsotopicAtoms - i1->nNumberOfIsotopicAtoms ) + return ret; + num = i1->nNumberOfIsotopicAtoms; + + /* compare isotopic atoms */ + for ( i = 0; i < num; i ++ ) { + if ( ret = (int)i2->IsotopicAtom[i].nAtomNumber - (int)i1->IsotopicAtom[i].nAtomNumber ) + return ret; + if ( ret = (int)i2->IsotopicAtom[i].nIsoDifference - (int)i1->IsotopicAtom[i].nIsoDifference ) + return ret; + } + + /* compare isotopic H */ + /* if tautomeric comparison mode then here are compared only non-tautomeric H */ + for ( i = 0; i < num; i ++ ) { + if ( ret = (int)i2->IsotopicAtom[i].nNum_T - (int)i1->IsotopicAtom[i].nNum_T ) + return ret; + if ( ret = (int)i2->IsotopicAtom[i].nNum_D - (int)i1->IsotopicAtom[i].nNum_D ) + return ret; + if ( ret = (int)i2->IsotopicAtom[i].nNum_H - (int)i1->IsotopicAtom[i].nNum_H ) + return ret; + } + + /* compare isotopic tautomeric part */ + if ( ret = i2->nNumberOfIsotopicTGroups || i1->nNumberOfIsotopicTGroups ) + return ret; + + /* + num = i1->nNumberOfIsotopicTGroups; + for ( i = 0; i < num; i ++ ) { + if ( ret = (int)i2->IsotopicTGroup[i].nTGroupNumber - (int)i1->IsotopicTGroup[i].nTGroupNumber ) + return ret; + if ( ret = (int)i2->IsotopicTGroup[i].nNum_T - (int)i1->IsotopicTGroup[i].nNum_T ) + return ret; + if ( ret = (int)i2->IsotopicTGroup[i].nNum_D - (int)i1->IsotopicTGroup[i].nNum_D ) + return ret; + if ( ret = (int)i2->IsotopicTGroup[i].nNum_H - (int)i1->IsotopicTGroup[i].nNum_H ) + return ret; + } + */ + + /* compare isotopic stereo */ + ret = CompareInchiStereo( i1->StereoIsotopic, i1->nFlags, + i2->StereoIsotopic, i2->nFlags ); + if ( ret ) + { + return ret; + } + } + + /* + compare charges: non-charged first, then in order of + ascending charges (negative first) + */ + + if ( i2->nTotalCharge && i1->nTotalCharge ) + { + /* both are charged; smaller charges first */ + ret = (int)i1->nTotalCharge - (int)i2->nTotalCharge; + return ret; + } + if ( ret = (i1->nTotalCharge? 1:0) - (i2->nTotalCharge? 1:0) ) + { + /* only one is charged; uncharged first */ + return ret; + } + /* stable sort */ + /*ret = p1->ord_number - p2->ord_number;*/ + + return ret; +} + +/* + Stereo +*/ +typedef enum tagSp3StereoTypeTmp +{ + SP3_NONE = 0, /* no sp3 stereo: no /t, /m, /s segments */ + /* /t is present: */ + SP3_ONLY = 1, /* no /s or /m segment: inversion leaves the structure unchanged */ + SP3_ABS = 2, /* abs stereo: both /m and /s are present */ + SP3_REL = 4, /* rel stereo: /s is present, /m is not */ + SP3_RAC = 8, /* racemic stereo: /s is presen, /m is nott */ + SP3_TYPE = (SP3_ABS|SP3_REL|SP3_RAC), /* bitmap for checking the presence of /m */ + SP3_ANY = (SP3_ABS|SP3_REL|SP3_RAC|SP3_ONLY) /* bitmap for checking the presence of /t */ +} SP3_TYPE_TMP; + +int GetSp3RelRacAbs( const INChI *pINChI, INChI_Stereo *Stereo ) +{ + int nRet = SP3_NONE; + if ( pINChI && !pINChI->bDeleted && Stereo && 0 < Stereo->nNumberOfStereoCenters ) { + if ( 0 != Stereo->nCompInv2Abs ) { + if ( pINChI->nFlags & INCHI_FLAG_REL_STEREO ) { +#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) + if ( 1 < Stereo->nNumberOfStereoCenters ) { + nRet = SP3_REL; + } +#else + nRet = SP3_REL; +#endif + } else + if ( pINChI->nFlags & INCHI_FLAG_RAC_STEREO ) { +#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) + if ( 1 < Stereo->nNumberOfStereoCenters ) { + nRet = SP3_REL; + } +#else + nRet = SP3_RAC; +#endif + } else { + nRet = SP3_ABS; + } + } else +#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) + if ( !(( pINChI->nFlags & (INCHI_FLAG_REL_STEREO|INCHI_FLAG_RAC_STEREO) ) && 1 == Stereo->nNumberOfStereoCenters) ) +#endif + { + nRet = SP3_ONLY; /* SP3_NONE if relative stereo and 1 stereocenter */ + } + } + return nRet; +} + +/* + CompINChILayers( ... ) + + Sorting in descending order: return -1 if *p1 > *p2, return +1 if *p1 < *p2 +*/ +int CompINChILayers( const INCHI_SORT *p1, + const INCHI_SORT *p2, + char sDifSegs[][DIFS_LENGTH], + int bFixTranspChargeBug ) +{ + int ret = 0, num, i, num_H1, num_H2; + + const INChI *i1 = NULL; /* Mobile-H layers in Mobile-H sorting order */ + const INChI *i2 = NULL; /* Fixed-H layers in Fixed-H sorting order */ + + int n1; /* TAUT_YES if tautomeric i1 exists, otherwise TAUT_NON */ + + INChI_Stereo *Stereo1, *Stereo2; + INChI_Stereo *IsoStereo1, *IsoStereo2; + int bRelRac[DIFL_LENGTH]; + char *psDifSegs; + + n1 = ( p1->pINChI[TAUT_YES] && p1->pINChI[TAUT_YES]->nNumberOfAtoms )? TAUT_YES : TAUT_NON; + + i1 = p1->pINChI[n1]; + i2 = (n1 == TAUT_YES && p2->pINChI[TAUT_NON] && + p2->pINChI[TAUT_NON]->nNumberOfAtoms)? p2->pINChI[TAUT_NON] : (const INChI *)NULL; + + num_H1 = num_H2 = 0; + memset( bRelRac, DIFV_BOTH_EMPTY, sizeof(bRelRac) ); + /*=====================*/ + /*==== /f ======*/ + /*=====================*/ + if ( i1 && !i1->bDeleted && i1->szHillFormula && i1->szHillFormula[0] ) { + sDifSegs[DIFL_M][DIFS_f_FORMULA] |= DIFV_NEQ2PRECED; + if ( i2 && !i2->bDeleted && i2->szHillFormula && i2->szHillFormula[0] ) { + if ( !CompareHillFormulasNoH( i1->szHillFormula, i2->szHillFormula, &num_H1, &num_H2 ) && + num_H1 == num_H2 ) { + sDifSegs[DIFL_F][DIFS_f_FORMULA] |= DIFV_EQL2PRECED; + } else { + sDifSegs[DIFL_F][DIFS_f_FORMULA] |= DIFV_NEQ2PRECED; + } + } else { + sDifSegs[DIFL_F][DIFS_f_FORMULA] |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; + } + } else { + sDifSegs[DIFL_M][DIFS_f_FORMULA] |= DIFV_BOTH_EMPTY; + if ( i2 && !i2->bDeleted && i2->szHillFormula && i2->szHillFormula[0] ) { + sDifSegs[DIFL_F][DIFS_f_FORMULA] |= DIFV_NEQ2PRECED; + } else { + sDifSegs[DIFL_F][DIFS_f_FORMULA] |= DIFV_BOTH_EMPTY; + } + } + /*=====================*/ + /*==== /c ======*/ + /*=====================*/ + if ( i1 && !i1->bDeleted && i1->lenConnTable > 1 ) { + sDifSegs[DIFL_M][DIFS_f_FORMULA] |= DIFV_NEQ2PRECED; + } else { + sDifSegs[DIFL_M][DIFS_f_FORMULA] |= DIFV_BOTH_EMPTY; + } + /*=====================*/ + /*==== /h ======*/ + /*=====================*/ + /* M: H atoms */ + if ( i1 && !i1->bDeleted ) { + num_H1 = (i1->lenTautomer > 0 && i1->nTautomer && i1->nTautomer[0])? 1 : 0; /* number of t-groups */ + if ( !num_H1 && i1->nNum_H ) { + for ( i = 0; i < i1->nNumberOfAtoms; i ++ ) { /* immobile H */ + if ( i1->nNum_H[i] ) { + num_H1 = 1; + break; + } + } + } + sDifSegs[DIFL_M][DIFS_h_H_ATOMS] |= num_H1? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; + } else { + sDifSegs[DIFL_M][DIFS_h_H_ATOMS] |= DIFV_BOTH_EMPTY; + } + /* F: fixed mobile H */ + if ( i2 && !i2->bDeleted && i2->nNum_H_fixed ) { + num_H2 = 0; + if ( i1 && !i1->bDeleted ) { + for ( i = 0; i < i1->nNumberOfAtoms; i ++ ) { + if ( i2->nNum_H_fixed[i] ) { + num_H2 = 1; + break; + } + } + } + sDifSegs[DIFL_F][DIFS_h_H_ATOMS] |= num_H2? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; + } else { + sDifSegs[DIFL_F][DIFS_h_H_ATOMS] |= DIFV_BOTH_EMPTY; + } + /* MI: exchangable isotopic H: see OutputINChI1(), num_iso_H[] */ + + /*=====================*/ + /*==== /q ======*/ + /*=====================*/ + psDifSegs = &sDifSegs[DIFL_F][DIFS_q_CHARGE]; + if ( i1 && !i1->bDeleted ) { + if ( i1->nTotalCharge ) { + sDifSegs[DIFL_M][DIFS_q_CHARGE] |= DIFV_NEQ2PRECED; + } else { + sDifSegs[DIFL_M][DIFS_q_CHARGE] |= DIFV_BOTH_EMPTY; + } + if ( i2 && !i2->bDeleted ) { + if ( i1->nTotalCharge ) { + if ( i1->nTotalCharge == i2->nTotalCharge ) { + *psDifSegs |= DIFV_EQL2PRECED; + } else + if ( i2->nTotalCharge ) { + *psDifSegs |= DIFV_NEQ2PRECED; + } else { + *psDifSegs |= DIFV_IS_EMPTY; + } + } else { + if ( i2->nTotalCharge ) { + *psDifSegs |= DIFV_NEQ2PRECED; + } else { + *psDifSegs |= DIFV_BOTH_EMPTY; + } + } + } else + if ( !i2 ) { + if (bFixTranspChargeBug==1) + { + /* bug explanation: + + component #1 is tautomeric, component #2 is not + Mobile-H(#2) > Mobile-H(#1) + Fixed-H(#2) = Mobile-H(#2) < Fixed-H(#1) + + Layer first_charge second_charge + + Mobile-H 0 (comp#1) -1 (comp#2) + Fixed-H none (comp#2) -1 (comp#1) + + v1.01 charge compared decided that charge layers are same and omitted Fixed-H /q layer + + Solution: when component permutation is detected AND fixed-H component does not exist, + compare Mobile-H charge [0 (comp#1) in the example] to the charge of Mobile-H [-1 (comp#2)] + of the component that has none Fixed-H charge + */ + + /* Fixed-H i2 is empty because Fixed-H struct is same as Mobile-H */ + if ( p1->ord_number != p2->ord_number && /* component order in Fixed-H is different from Mobile-H */ + n1 == TAUT_YES && p2->pINChI[TAUT_YES] && !p2->pINChI[TAUT_YES]->bDeleted && + p2->pINChI[TAUT_YES]->nNumberOfAtoms ) { + int i2_nTotalCharge = p2->pINChI[TAUT_YES]->nTotalCharge; + + if ( i1->nTotalCharge ) { + if ( i1->nTotalCharge == i2_nTotalCharge ) { + *psDifSegs |= DIFV_EQL2PRECED; + } else + if ( i2_nTotalCharge ) { + *psDifSegs |= DIFV_NEQ2PRECED; + } else { + *psDifSegs |= DIFV_IS_EMPTY; + } + } else { + if ( i2_nTotalCharge ) { + *psDifSegs |= DIFV_NEQ2PRECED; + } else { + *psDifSegs |= DIFV_BOTH_EMPTY; + } + } + } else { + *psDifSegs |= i1->nTotalCharge? DIFV_EQL2PRECED : DIFV_BOTH_EMPTY; + } + } + else /* if (bFixTranspChargeBug==1) */ + { + *psDifSegs |= i1->nTotalCharge? DIFV_EQL2PRECED : DIFV_BOTH_EMPTY; + } + } + + else /* if ( !i2 ) { */ + { + /* i2 && i2->bDeleted */ + *psDifSegs |= i1->nTotalCharge? DIFV_IS_EMPTY : DIFV_BOTH_EMPTY; + } + } else { + sDifSegs[DIFL_M][DIFS_q_CHARGE] |= DIFV_BOTH_EMPTY; + if ( i2 && !i2->bDeleted ) { + if ( i2->nTotalCharge ) { + sDifSegs[DIFL_F][DIFS_q_CHARGE] |= DIFV_NEQ2PRECED; + } else { + sDifSegs[DIFL_F][DIFS_q_CHARGE] |= DIFV_BOTH_EMPTY; + } + } + } + /*************** stereo *****************/ + if ( i1 && !i1->bDeleted ) { + Stereo1 = i1->Stereo; + IsoStereo1 = i1->StereoIsotopic; + } else { + Stereo1 = NULL; + IsoStereo1 = NULL; + } + if ( i2 && !i2->bDeleted ) { + Stereo2 = i2->Stereo; + IsoStereo2 = i2->StereoIsotopic; + } else { + Stereo2 = NULL; + IsoStereo2 = NULL; + } + /*=====================*/ + /*==== /b ======*/ + /*=====================*/ + /* M double bond stereo */ + psDifSegs = &sDifSegs[DIFL_M][DIFS_b_SBONDS]; + if ( Stereo1 && Stereo1->nNumberOfStereoBonds ) { + *psDifSegs |= DIFV_NEQ2PRECED; + } else { + *psDifSegs |= DIFV_BOTH_EMPTY; + } + /* F double bond stereo */ + psDifSegs = &sDifSegs[DIFL_F][DIFS_b_SBONDS]; + if ( Stereo2 && Stereo2->nNumberOfStereoBonds ) { + if ( Stereo1 && Stereo1->nNumberOfStereoBonds ) { + if ( Eql_INChI_Stereo( Stereo1, EQL_SP2, Stereo2, EQL_SP2, 0 ) ) { + *psDifSegs |= DIFV_EQL2PRECED; + } else { + *psDifSegs |= DIFV_NEQ2PRECED; + } + } else { + *psDifSegs |= DIFV_NEQ2PRECED; + } + } else { + if ( Stereo1 && Stereo1->nNumberOfStereoBonds ) { + *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; + } else { + *psDifSegs |= DIFV_BOTH_EMPTY; + } + } + /* MI double bond stereo */ + psDifSegs = &sDifSegs[DIFL_MI][DIFS_b_SBONDS]; + if ( IsoStereo1 && IsoStereo1->nNumberOfStereoBonds ) { + if ( Eql_INChI_Stereo( IsoStereo1, EQL_SP2, Stereo1, EQL_SP2, 0 ) ) { + *psDifSegs |= DIFV_EQL2PRECED; + } else { + *psDifSegs |= DIFV_NEQ2PRECED; + } + } else { + if ( Stereo1 && Stereo1->nNumberOfStereoBonds ) { + *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ + } else { + *psDifSegs |= DIFV_BOTH_EMPTY; + } + } + /* FI double bond stereo */ + psDifSegs = &sDifSegs[DIFL_FI][DIFS_b_SBONDS]; + if ( IsoStereo2 && IsoStereo2->nNumberOfStereoBonds ) { + if ( Eql_INChI_Stereo( IsoStereo2, EQL_SP2, Stereo2, EQL_SP2, 0 ) ) { + *psDifSegs |= DIFV_EQL2PRECED; + } else { + if ( !(Stereo1 && Stereo1->nNumberOfStereoBonds) && + !(Stereo2 && Stereo2->nNumberOfStereoBonds) && + Eql_INChI_Stereo( IsoStereo2, EQL_SP2, IsoStereo1, EQL_SP2, 0 ) ) { + *psDifSegs |= DIFV_FI_EQ_MI; + } else { + *psDifSegs |= DIFV_NEQ2PRECED; + } + } + } else { + /* the solution table for FI stereo, + in case of FI stereo is empty + E = segment is empty, NE = not empty + +==============================+ + | M | MI | F | result | + +=====+=====+=====+============+ + | E | E | E | both empty | + +-----+-----+-----+------------+ + | NE | E | E | both empty | + +-----+-----+-----+------------+ + | E | NE | E | is empty | + +-----+-----+-----+------------+ + | NE | NE | E | both empty | + +-----+-----+-----+------------+ + | E | E | NE | is empty | + +-----+-----+-----+------------+ + | NE | E | NE | is empty | + +-----+-----+-----+------------+ + | E | NE | NE | is empty | + +-----+-----+-----+------------+ + | NE | NE | ME | is empty | + +==============================+ + */ + if ( Stereo2 && Stereo2->nNumberOfStereoBonds ) { + *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ + } else + if ( IsoStereo1 && IsoStereo1->nNumberOfStereoBonds && + !(Stereo1 && Stereo1->nNumberOfStereoBonds) + ) { + *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; + } else { + *psDifSegs |= DIFV_BOTH_EMPTY; + } + } + /*==================================*/ + /*==== /t, /m, /s for M ======*/ + /*==================================*/ + /* M sp3 stereo */ + + bRelRac[DIFL_M ] = GetSp3RelRacAbs( i1, Stereo1 ); /* Mobile-H */ + bRelRac[DIFL_MI] = GetSp3RelRacAbs( i1, IsoStereo1 ); + bRelRac[DIFL_F ] = GetSp3RelRacAbs( i2, Stereo2 ); /* Fixed-H */ + bRelRac[DIFL_FI] = GetSp3RelRacAbs( i2, IsoStereo2 ); + if ( SP3_NONE != bRelRac[DIFL_M] ) { + sDifSegs[DIFL_M][DIFS_t_SATOMS] |= (bRelRac[DIFL_M] & SP3_ANY)? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; + sDifSegs[DIFL_M][DIFS_m_SP3INV] |= (bRelRac[DIFL_M] & SP3_ABS)? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; + sDifSegs[DIFL_M][DIFS_s_STYPE] |= (bRelRac[DIFL_M] & SP3_TYPE)? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; + } else { + sDifSegs[DIFL_M][DIFS_t_SATOMS] |= DIFV_BOTH_EMPTY; + sDifSegs[DIFL_M][DIFS_m_SP3INV] |= DIFV_BOTH_EMPTY; + sDifSegs[DIFL_M][DIFS_s_STYPE] |= DIFV_BOTH_EMPTY; + } + /*=====================*/ + /*==== /t ======*/ + /*=====================*/ + /* F sp3 stereo */ + psDifSegs = &sDifSegs[DIFL_F][DIFS_t_SATOMS]; + if ( SP3_ANY & bRelRac[DIFL_F] ) { + if ( Eql_INChI_Stereo( Stereo2, EQL_SP3, Stereo1, EQL_SP3, 0 ) ) { + *psDifSegs |= DIFV_EQL2PRECED; + } else { + *psDifSegs |= DIFV_NEQ2PRECED; + } + } else + if ( SP3_ANY & bRelRac[DIFL_M] ) { + *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; + } else { + *psDifSegs |= DIFV_BOTH_EMPTY; + } + /* MI sp3 stereo */ + psDifSegs = &sDifSegs[DIFL_MI][DIFS_t_SATOMS]; + if ( SP3_ANY & bRelRac[DIFL_MI] ) { + if ( Eql_INChI_Stereo( IsoStereo1, EQL_SP3, Stereo1, EQL_SP3, 0 ) ) { + *psDifSegs |= DIFV_EQL2PRECED; + } else { + *psDifSegs |= DIFV_NEQ2PRECED; + } + } else + if ( SP3_ANY & bRelRac[DIFL_M] ) { + *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ + } else { + *psDifSegs |= DIFV_BOTH_EMPTY; + } + /* FI sp3 stereo */ + psDifSegs = &sDifSegs[DIFL_FI][DIFS_t_SATOMS]; + if ( SP3_ANY & bRelRac[DIFL_FI] ) { + if ( Eql_INChI_Stereo( IsoStereo2, EQL_SP3, Stereo2, EQL_SP3, 0 ) ) { + *psDifSegs |= DIFV_EQL2PRECED; + } else + if ( !(SP3_ANY & bRelRac[DIFL_M]) && + !(SP3_ANY & bRelRac[DIFL_F]) && + Eql_INChI_Stereo( IsoStereo2, EQL_SP3, IsoStereo1, EQL_SP3, 0 ) ) { + *psDifSegs |= DIFV_FI_EQ_MI; + } else { + *psDifSegs |= DIFV_NEQ2PRECED; + } + } else /* similar to /b */ + if ( (SP3_ANY & bRelRac[DIFL_F]) ) { + *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ + } else + if ( (SP3_ANY & bRelRac[DIFL_MI]) && !(SP3_ANY & bRelRac[DIFL_M]) ) { + *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; + } else { + *psDifSegs |= DIFV_BOTH_EMPTY; + } + /*=====================*/ + /*==== /m ======*/ + /*=====================*/ + /* F sp3 abs stereo inversion */ + psDifSegs = &sDifSegs[DIFL_F][DIFS_m_SP3INV]; + if ( bRelRac[DIFL_F] & SP3_ABS ) { + /* the order of || operands below is critically important: || is not a commutative operation */ + if ( !(bRelRac[DIFL_M] & SP3_ABS) || Stereo2->nCompInv2Abs != Stereo1->nCompInv2Abs ) { + *psDifSegs |= DIFV_NEQ2PRECED; + } else { + *psDifSegs |= DIFV_EQL2PRECED; + } + } else + if ( bRelRac[DIFL_M] & SP3_ABS ) { + *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; + } else { + *psDifSegs |= DIFV_BOTH_EMPTY; + } + /* MI sp3 abs stereo inversion */ + psDifSegs = &sDifSegs[DIFL_MI][DIFS_m_SP3INV]; + if ( SP3_ABS & bRelRac[DIFL_MI] ) { + if ( (SP3_ABS & bRelRac[DIFL_M]) && IsoStereo1->nCompInv2Abs == Stereo1->nCompInv2Abs ) { + *psDifSegs |= DIFV_EQL2PRECED; + } else { + *psDifSegs |= DIFV_NEQ2PRECED; + } + } else + if ( SP3_ABS & bRelRac[DIFL_M] ) { + *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ + } else { + *psDifSegs |= DIFV_BOTH_EMPTY; + } + /* FI sp3 abs stereo inversion */ + psDifSegs = &sDifSegs[DIFL_FI][DIFS_m_SP3INV]; + if ( SP3_ABS & bRelRac[DIFL_FI] ) { + if ( (SP3_ABS & bRelRac[DIFL_F]) && IsoStereo2->nCompInv2Abs == Stereo2->nCompInv2Abs ) { + *psDifSegs |= DIFV_EQL2PRECED; + } else + if ( !(SP3_ABS & bRelRac[DIFL_M]) && + !(SP3_ABS & bRelRac[DIFL_F]) && + (SP3_ABS & bRelRac[DIFL_MI]) && /* make sure IsoStereo1 != NULL */ + IsoStereo2->nCompInv2Abs == IsoStereo1->nCompInv2Abs ) { + *psDifSegs |= DIFV_FI_EQ_MI; + } else { + *psDifSegs |= DIFV_NEQ2PRECED; + } + } else /* similar to /b */ + /* the order of || operands below is critically important: || is no a commutative operation */ + if ( (SP3_ABS & bRelRac[DIFL_F]) ) { + *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ + } else + if ( (SP3_ABS & bRelRac[DIFL_MI]) && !(SP3_ABS & bRelRac[DIFL_M]) ) { + *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; + } else { + *psDifSegs |= DIFV_BOTH_EMPTY; + } + /*=====================*/ + /*==== /s ======*/ + /*=====================*/ + /* F sp3 stereo type */ + psDifSegs = &sDifSegs[DIFL_F][DIFS_s_STYPE]; + if ( bRelRac[DIFL_F] & SP3_TYPE ) { + if ( (bRelRac[DIFL_F] & SP3_TYPE) == (bRelRac[DIFL_M] & SP3_TYPE) ) { + *psDifSegs |= DIFV_EQL2PRECED; + } else { + *psDifSegs |= DIFV_NEQ2PRECED; + } + } else + if ( bRelRac[DIFL_M] & SP3_TYPE ) { + *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; + } else { + *psDifSegs |= DIFV_BOTH_EMPTY; + } + /* MI sp3 stereo type */ + psDifSegs = &sDifSegs[DIFL_MI][DIFS_s_STYPE]; + if ( SP3_TYPE & bRelRac[DIFL_MI] ) { + if ( (SP3_TYPE & bRelRac[DIFL_MI]) == (SP3_TYPE & bRelRac[DIFL_M]) ) { + *psDifSegs |= DIFV_EQL2PRECED; + } else { + *psDifSegs |= DIFV_NEQ2PRECED; + } + } else + if ( SP3_TYPE & bRelRac[DIFL_M] ) { + *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ + } else { + *psDifSegs |= DIFV_BOTH_EMPTY; + } + /* FI sp3 stereo type */ + psDifSegs = &sDifSegs[DIFL_FI][DIFS_s_STYPE]; + if ( SP3_TYPE & bRelRac[DIFL_FI] ) { + if ( (SP3_TYPE & bRelRac[DIFL_FI]) == (SP3_TYPE & bRelRac[DIFL_F]) ) { + *psDifSegs |= DIFV_EQL2PRECED; + } else + if ( !(SP3_TYPE & bRelRac[DIFL_M]) && + !(SP3_TYPE & bRelRac[DIFL_F]) && + (SP3_TYPE & bRelRac[DIFL_MI]) ) { + *psDifSegs |= DIFV_FI_EQ_MI; + } else { + *psDifSegs |= DIFV_NEQ2PRECED; + } + } else /* similar to /b */ + /* the order of || operands below is critically important: || is not a commutative operation */ + if ( (SP3_TYPE & bRelRac[DIFL_F]) ) { + *psDifSegs |= DIFV_EQL2PRECED; /* isotopic is missing because there is no isotopes */ + } else + if ( (SP3_TYPE & bRelRac[DIFL_MI]) && !(SP3_TYPE & bRelRac[DIFL_M]) ) { + *psDifSegs |= i2? DIFV_IS_EMPTY : DIFV_EQL2PRECED; + } else { + *psDifSegs |= DIFV_BOTH_EMPTY; + } + /*=====================*/ + /*==== /o ======*/ + /*=====================*/ + if ( p1 && p2 && p1->ord_number != p2->ord_number ) { + sDifSegs[DIFL_F][DIFS_o_TRANSP] |= DIFV_NEQ2PRECED; + } + /*=====================*/ + /*==== /i ======*/ + /*=====================*/ + /* M isotopic atoms */ + psDifSegs = &sDifSegs[DIFL_MI][DIFS_i_IATOMS]; + if ( i1 && !i1->bDeleted && (i1->nNumberOfIsotopicAtoms || i1->nNumberOfIsotopicTGroups) ) { + *psDifSegs |= DIFV_NEQ2PRECED; + } else { + *psDifSegs |= DIFV_BOTH_EMPTY; + } + /* F isotopic atoms */ + psDifSegs = &sDifSegs[DIFL_FI][DIFS_i_IATOMS]; + if ( i2 && !i2->bDeleted ) { + if ( i2->nNumberOfIsotopicAtoms || i2->nNumberOfIsotopicTGroups ) { + if ( !i1 || i1->bDeleted || + i2->nNumberOfIsotopicAtoms != i1->nNumberOfIsotopicAtoms || + i2->nNumberOfIsotopicTGroups != i1->nNumberOfIsotopicTGroups ) { + *psDifSegs |= DIFV_NEQ2PRECED; + } else { + int diff; + num = i1->nNumberOfIsotopicAtoms; + diff = 0; + for ( i = 0; i < num; i ++ ) { + /* compare isotopic atoms */ + if ( diff = (int)i2->IsotopicAtom[i].nAtomNumber - (int)i1->IsotopicAtom[i].nAtomNumber ) + break; + if ( diff = (int)i2->IsotopicAtom[i].nIsoDifference - (int)i1->IsotopicAtom[i].nIsoDifference ) + break; + /* compare isotopic H */ + if ( diff = (int)i2->IsotopicAtom[i].nNum_T - (int)i1->IsotopicAtom[i].nNum_T ) + break; + if ( diff = (int)i2->IsotopicAtom[i].nNum_D - (int)i1->IsotopicAtom[i].nNum_D ) + break; + if ( diff = (int)i2->IsotopicAtom[i].nNum_H - (int)i1->IsotopicAtom[i].nNum_H ) + break; + } + if ( !diff ) { + num = i1->nNumberOfIsotopicTGroups; + for ( i = 0; i < num; i ++ ) { + if ( diff = (int)i2->IsotopicTGroup[i].nTGroupNumber - (int)i1->IsotopicTGroup[i].nTGroupNumber ) + break; + if ( diff = (int)i2->IsotopicTGroup[i].nNum_T - (int)i1->IsotopicTGroup[i].nNum_T ) + break; + if ( diff = (int)i2->IsotopicTGroup[i].nNum_D - (int)i1->IsotopicTGroup[i].nNum_D ) + return diff; + if ( diff = (int)i2->IsotopicTGroup[i].nNum_H - (int)i1->IsotopicTGroup[i].nNum_H ) + break; + } + } + *psDifSegs |= diff? DIFV_NEQ2PRECED : DIFV_FI_EQ_MI; + } + } else + if ( i1 && !i1->bDeleted && (i1->nNumberOfIsotopicAtoms || i1->nNumberOfIsotopicTGroups) ) { + *psDifSegs |= DIFV_IS_EMPTY; + } + } else + if ( !i2 ) { + if ( i1 && !i1->bDeleted && (i1->nNumberOfIsotopicAtoms || i1->nNumberOfIsotopicTGroups) ) { + *psDifSegs |= DIFV_EQL2PRECED; + } else { + *psDifSegs |= DIFV_BOTH_EMPTY; + } + } + + return ret; +} + +/* + INChI_SegmentAction( ... ) +*/ +int INChI_SegmentAction( char cDifSegs ) +{ + if ( !(cDifSegs & DIFV_OUTPUT_OMIT_F) ) { + return INCHI_SEGM_OMIT; + } + if ( (cDifSegs & DIFV_OUTPUT_EMPTY_T) && !(cDifSegs & DIFV_OUTPUT_EMPTY_F) ) { + return INCHI_SEGM_EMPTY; + } + if ( (cDifSegs & DIFV_OUTPUT_FILL_T) ) { + return INCHI_SEGM_FILL; + } + return INCHI_SEGM_OMIT; /* the control flow shoul never reach this point */ +} + +/* + MarkUnusedAndEmptyLayers( ... ) + + 1. If all elements of a layer are DIFV_IS_EMPTY and/or DIFV_BOTH_EMPTY + and/or DIFV_EQL2PRECED and/or DIFV_FI_EQ_MI + and there is NO succeeding non-empty layer then mark the 1st element + of the layer DIFV_BOTH_EMPTY; this layerr will be omitted. + + 2. If all elements of a layer are DIFV_IS_EMPTY and/or DIFV_BOTH_EMPTY + and/or DIFV_EQL2PRECED and/or DIFV_FI_EQ_MI + and there IS a succeeding non-empty layer then mark the 1st element + of the layer DIFV_IS_EMPTY and all other elements DIFV_BOTH_EMPTY; + only the first empty segment of this layerr will be output. + + 3. If NOT all elements of a layer are DIFV_IS_EMPTY and/or DIFV_BOTH_EMPTY + and/or DIFV_EQL2PRECED and/or DIFV_FI_EQ_MI + and the 1st element of the layer is DIFV_BOTH_EMPTY then mark it + DIFV_IS_EMPTY; it will be output as empty (except M layer). + +*/ +int MarkUnusedAndEmptyLayers( char sDifSegs[][DIFS_LENGTH] ) +{ + int i, nLayer, sBits, nFirstSegm; +#define nFirstFmlSegm DIFS_f_FORMULA +#define nFirstIsoSegm DIFS_i_IATOMS + /* FI */ + nLayer = DIFL_FI; + nFirstSegm = nFirstIsoSegm; + sBits = 0; + for ( i = 0; i < DIFS_idf_LENGTH; i ++ ) { + sBits |= sDifSegs[nLayer][i]; + } + if ( !(sBits & DIFV_OUTPUT_OMIT_F) ) { + /* Omit the FI layer */ + memset( sDifSegs[nLayer], DIFV_BOTH_EMPTY, DIFS_idf_LENGTH); + } else + if ( sDifSegs[nLayer][nFirstSegm] == DIFV_BOTH_EMPTY || + !(sDifSegs[nLayer][nFirstSegm] & DIFV_OUTPUT_OMIT_F) ) { + sDifSegs[nLayer][nFirstSegm] = DIFV_IS_EMPTY; + } + + /* MI */ + nLayer = DIFL_MI; + nFirstSegm = nFirstIsoSegm; + sBits = 0; + for ( i = 0; i < DIFS_idf_LENGTH; i ++ ) { + sBits |= sDifSegs[nLayer][i]; + } + if ( !(sBits & DIFV_OUTPUT_OMIT_F) ) { + /* Omit the MI layer */ + memset( sDifSegs[nLayer], DIFV_BOTH_EMPTY, DIFS_idf_LENGTH); + } else + if ( sDifSegs[nLayer][nFirstSegm] == DIFV_BOTH_EMPTY || + !(sDifSegs[nLayer][nFirstSegm] & DIFV_OUTPUT_OMIT_F) ) { + sDifSegs[nLayer][nFirstSegm] = DIFV_IS_EMPTY; + } + + /* F */ + nLayer = DIFL_F; + nFirstSegm = nFirstFmlSegm; + sBits = 0; + for ( i = 0; i < DIFS_idf_LENGTH; i ++ ) { + sBits |= sDifSegs[nLayer][i]; + } + if ( !(sBits & DIFV_OUTPUT_OMIT_F) && + sDifSegs[DIFL_FI][nFirstIsoSegm] == DIFV_BOTH_EMPTY ) { + /* Omit the F layer: no non-iotopic and no isotopic segments */ + memset( sDifSegs[nLayer], DIFV_BOTH_EMPTY, DIFS_idf_LENGTH); + } else + /* do not omit fixed-H layer */ + if ( sDifSegs[nLayer][nFirstSegm] == DIFV_BOTH_EMPTY || + !(sDifSegs[nLayer][nFirstSegm] & DIFV_OUTPUT_OMIT_F) ) { + sDifSegs[nLayer][nFirstSegm] = DIFV_IS_EMPTY; + } + + /* M -- leave as it is */ + return 0; +#undef nFirstFmlSegm +#undef nFirstIsoSegm +} + +int CompareInchiStereo( INChI_Stereo *Stereo1, INCHI_MODE nFlags1, INChI_Stereo *Stereo2, INCHI_MODE nFlags2 ) +{ + int i, num, ret; + if ( Stereo2 && Stereo1 ) { + /* compare stereogenic bonds */ + num = inchi_min( Stereo2->nNumberOfStereoBonds, Stereo1->nNumberOfStereoBonds ); + for ( i = 0; i < num; i ++ ) { + if ( ret = (int)Stereo2->nBondAtom1[i] - (int)Stereo1->nBondAtom1[i] ) + return ret; + if ( ret = (int)Stereo2->nBondAtom2[i] - (int)Stereo1->nBondAtom2[i] ) + return ret; + if ( ret = (int)Stereo2->b_parity[i] - (int)Stereo1->b_parity[i] ) + return ret; + } + if ( ret = (int)Stereo2->nNumberOfStereoBonds - (int)Stereo1->nNumberOfStereoBonds ) + return ret; + /* compare stereogenic atoms */ +#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) + if ( ((nFlags1 | nFlags2) & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO) ) && + 1 == Stereo2->nNumberOfStereoCenters && + 1 == Stereo1->nNumberOfStereoCenters ) { + ; /* do not compare single stereocenters in case of relative stereo */ + } else +#endif + { + num = inchi_min( Stereo2->nNumberOfStereoCenters, Stereo1->nNumberOfStereoCenters ); + for ( i = 0; i < num; i ++ ) { + if ( ret = (int)Stereo2->nNumber[i] - (int)Stereo1->nNumber[i] ) + return ret; + if ( ret = (int)Stereo2->t_parity[i] - (int)Stereo1->t_parity[i] ) + return ret; + } + if ( ret = (int)Stereo2->nNumberOfStereoCenters - (int)Stereo1->nNumberOfStereoCenters ) + return ret; + /* compare stereo-abs-is-inverted flags for non-relative, non-racemic */ + if ( !((nFlags1 | nFlags2) & (INCHI_FLAG_RAC_STEREO | INCHI_FLAG_REL_STEREO)) ) { + if ( ret = (Stereo2->nCompInv2Abs < 0) - (Stereo1->nCompInv2Abs < 0) ) { + return ret; + } + } + } + } else + if ( Stereo2 && ( Stereo2->nNumberOfStereoBonds > 0 || + Stereo2->nNumberOfStereoCenters > 0 +#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) + && /* do not compare single stereocenters in case of relative stereo */ + !((nFlags2 & (INCHI_FLAG_REL_STEREO|INCHI_FLAG_RAC_STEREO)) && + 1 == Stereo2->nNumberOfStereoCenters + ) +#endif + ) ) { + return 1; + }else + if ( Stereo1 && ( Stereo1->nNumberOfStereoBonds > 0 || + Stereo1->nNumberOfStereoCenters > 0 +#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) + && /* do not compare single stereocenters in case of relative stereo */ + !((nFlags1 & (INCHI_FLAG_REL_STEREO|INCHI_FLAG_RAC_STEREO)) && + 1 == Stereo1->nNumberOfStereoCenters + ) +#endif + ) ) { + return -1; + } + return 0; +} + +/* + CompINChI2( ... ) + + Sorting in descending order: + return -1 if *p1 > *p2, return +1 if *p1 < *p2 + +*/ +int CompINChI2(const INCHI_SORT *p1, const INCHI_SORT *p2, int bTaut, int bCompareIsotopic) +{ + int ret, num, i, num_H1, num_H2; + + const INChI *i1 = NULL; /* tautomeric if exists, otherwise non-tautomeric */ + const INChI *i2 = NULL; /* tautomeric if exists, otherwise non-tautomeric */ + + int n1; /* TAUT_YES if tautomeric i1 exists, otherwise TAUT_NON */ + int n2; /* TAUT_YES if tautomeric i2 exists, otherwise TAUT_NON */ + + const INChI *i1n = NULL; /* non-tautomeric if both tautomeric AND non-tautomeric exist */ + const INChI *i2n = NULL; /* non-tautomeric if both tautomeric AND non-tautomeric exist */ + + /*const INChI *i1t = NULL;*/ /* temp for i1 if both tautomeric AND non-tautomeric exist */ + /*const INChI *i2t = NULL;*/ /* temp for i2 if both tautomeric AND non-tautomeric exist */ + + /* INChI_Stereo *Stereo1, *Stereo2; */ + + n1 = ( p1->pINChI[TAUT_YES] && p1->pINChI[TAUT_YES]->nNumberOfAtoms )? TAUT_YES : TAUT_NON; + n2 = ( p2->pINChI[TAUT_YES] && p2->pINChI[TAUT_YES]->nNumberOfAtoms )? TAUT_YES : TAUT_NON; + + i1 = p1->pINChI[n1]; + i1n = (n1 == TAUT_YES && p1->pINChI[TAUT_NON] && + p1->pINChI[TAUT_NON]->nNumberOfAtoms)? p1->pINChI[TAUT_NON] : (const INChI *)NULL; + + i2 = p2->pINChI[n2]; + i2n = (n2 == TAUT_YES && p2->pINChI[TAUT_NON] && + p2->pINChI[TAUT_NON]->nNumberOfAtoms)? p2->pINChI[TAUT_NON] : (const INChI *)NULL; + + /* non-deleted-non-empty < deleted < empty */ + if ( i1 && !i2 ) + return -1; /* non-empty is the smallest (first) */ + if ( !i1 && i2 ) + return 1; + if ( !i1 && !i2 ) + return 0; + if ( i1->bDeleted && !i2->bDeleted ) + return 1; /* deleted is the largest (last) among non-empty */ + if ( !i1->bDeleted && i2->bDeleted ) + return -1; + + num_H1 = num_H2 = 0; + + /* do not compare terminal H */ + if ( ret = CompareHillFormulasNoH( i1->szHillFormula, i2->szHillFormula, &num_H1, &num_H2 ) ) { + return ret; /* lexicographic order except the shorter one is greater (last): CH2O < CH2; C3XX < C2XX */ + } + + /********************************************************* + compare non-isotopic non-tautomeric part + *********************************************************/ + + /* compare number of atoms (excluding terminal H) */ + if ( ret = i2->nNumberOfAtoms - i1->nNumberOfAtoms ) + return ret; /* more atoms first */ + + /* compare elements (excluding terminal H) */ + num = i1->nNumberOfAtoms; + for ( i = 0; i < num; i ++ ) { /* should always be equal if Hill formulas are same */ + if ( ret = (int)i2->nAtom[i] - (int)i1->nAtom[i] ) + return ret; /* greater periodic number first */ + } + /********************************************************** + compare connection tables + ***********************************************************/ + if ( ret = i2->lenConnTable - i1->lenConnTable ) + return ret; /* longer connection table first */ + num = i2->lenConnTable; + for ( i = 0; i < num; i ++ ) { + if ( ret = (int)i2->nConnTable[i] - (int)i1->nConnTable[i] ) + return ret; /* greater connection table first */ + } + /********************************************************* + compare compare total number of H (inverse: H3 < H2 ) + **********************************************************/ + if ( ret = num_H2 - num_H1 ) + return ret; + /********************************************************* + compare non-tautomeric num_H: N < NH3 < NH2 < NH + **********************************************************/ + num = i1->nNumberOfAtoms; + for ( i = 0; i < num; i ++ ) { + if ( i2->nNum_H[i] != i1->nNum_H[i] ) { + return !i2->nNum_H[i]? 1 : /* no H first */ + !i1->nNum_H[i]? -1 : + (int)i2->nNum_H[i] - (int)i1->nNum_H[i]; + } + } + /********************************************************* + compare non-isotopic tautomeric part + *********************************************************/ + if ( ret = CompareTautNonIsoPartOfINChI( i1, i2) ) { + return ret; + } + /* + if ( ret = i2->lenTautomer - i1->lenTautomer ) + return ret; + num = inchi_min( i2->lenTautomer, i1->lenTautomer ); + for ( i = 0; i < num; i ++ ) { + if ( ret = (int)i2->nTautomer[i] - (int)i1->nTautomer[i] ) + return ret; + } + */ + /********************************************************* + * * + * at this point both components are either tautomeric * + * or non-tautomeric * + * * + *********************************************************/ + + /********************************************************* + non-tautomeric "fixed H" specific + *********************************************************/ + if ( TAUT_NON == bTaut && (i1n && i1n->nNum_H_fixed || i2n && i2n->nNum_H_fixed) ) { + /* first, compare non-tautomeric chem. formulas -- they may be different */ + const char *f1 = (i1n /*&& i1n->nNum_H_fixed*/)? i1n->szHillFormula : i1->szHillFormula; + const char *f2 = (i2n /*&& i2n->nNum_H_fixed*/)? i2n->szHillFormula : i2->szHillFormula; + if ( f1 && f2 &&(ret = CompareHillFormulas( f1, f2 ))) { + return ret; + } + /* secondly, compare fixed-H distribution */ + if ( i1n && i1n->nNum_H_fixed && i2n && i2n->nNum_H_fixed ) { + num = inchi_min( i1n->nNumberOfAtoms, i2n->nNumberOfAtoms); + for ( i = 0; i < num; i ++ ) { + if ( i2n->nNum_H_fixed[i] != i1n->nNum_H_fixed[i] ) { + return !i2n->nNum_H_fixed[i]? 1 : /* no fixed H first */ + !i1n->nNum_H_fixed[i]? -1 : + (int)i2n->nNum_H_fixed[i] - (int)i1n->nNum_H_fixed[i]; + } + } + if ( ret = (int)i2n->nNumberOfAtoms - (int)i1n->nNumberOfAtoms ) { + return ret; /* should not happen */ + } + } else + if ( i1n && i1n->nNum_H_fixed ) { + num = i1n->nNumberOfAtoms; + for ( i = 0; i < num; i ++ ) { /* added 2004-05-04 */ + if ( i1n->nNum_H_fixed[i] ) { + return -1; /* i1n->nNum_H_fixed[i] > 0? -1:1;*/ + } + } + /* p1 is tautomeric, p2 is not tautomeric; this must have been detected earlier */ + /*return -1;*/ /* has fixed H first *//* */ /* removed 2004-05-04 */ + } else { + num = i2n->nNumberOfAtoms; + for ( i = 0; i < num; i ++ ) { /* added 2004-05-04 */ + if ( i2n->nNum_H_fixed[i] ) { + return 1; /* i2n->nNum_H_fixed[i] > 0? 1:-1;*/ + } + } + /* p2 is tautomeric, p1 is not tautomeric; this must have been detected earlier */ + /*return 1; */ /* has fixed H first *//* */ /* removed 2004-05-04 */ + } + } + + /************************************************************************* + if requested non-tautomeric comparison then + prepare to compare non-taut non-isotopic stereo, etc. + *************************************************************************/ + if ( TAUT_NON == bTaut ) { + if ( i1n ) { + /*i1t = i1;*/ + i1 = i1n; + } + if ( i2n ) { + /*i2t = i2;*/ + i2 = i2n; + } + } + + /********************************************************* + compare non-isotopic stereo + *********************************************************/ + ret = CompareInchiStereo( i1->Stereo, i1->nFlags, i2->Stereo, i2->nFlags ); + if ( ret ) { + return ret; + } + /******************************************************* + do not switch back to tautomeric i1, i2 + *******************************************************/ + /* -- how to switch back -- + if ( i1t ) { + i1 = i1t; + i1t = NULL; + } + if ( i2t ) { + i2 = i2t; + i2t = NULL; + } + */ + /****************************************************** + compare isotopic non-tautomeric part + ******************************************************/ + if ( bCompareIsotopic ) { + if ( ret = i2->nNumberOfIsotopicAtoms - i1->nNumberOfIsotopicAtoms ) + return ret; + num = i1->nNumberOfIsotopicAtoms; + /* compare isotopic atoms */ + for ( i = 0; i < num; i ++ ) { + if ( ret = (int)i2->IsotopicAtom[i].nAtomNumber - (int)i1->IsotopicAtom[i].nAtomNumber ) + return ret; + if ( ret = (int)i2->IsotopicAtom[i].nIsoDifference - (int)i1->IsotopicAtom[i].nIsoDifference ) + return ret; + } + /* compare isotopic H */ + /* if tautomeric comparison mode then here are compared only non-tautomeric H */ + for ( i = 0; i < num; i ++ ) { + if ( ret = (int)i2->IsotopicAtom[i].nNum_T - (int)i1->IsotopicAtom[i].nNum_T ) + return ret; + if ( ret = (int)i2->IsotopicAtom[i].nNum_D - (int)i1->IsotopicAtom[i].nNum_D ) + return ret; + if ( ret = (int)i2->IsotopicAtom[i].nNum_H - (int)i1->IsotopicAtom[i].nNum_H ) + return ret; + } + /***************************************************** + compare isotopic tautomeric part + *****************************************************/ + if ( ret = i2->nNumberOfIsotopicTGroups - i1->nNumberOfIsotopicTGroups ) + return ret; + num = i1->nNumberOfIsotopicTGroups; + for ( i = 0; i < num; i ++ ) { + if ( ret = (int)i2->IsotopicTGroup[i].nTGroupNumber - (int)i1->IsotopicTGroup[i].nTGroupNumber ) + return ret; + if ( ret = (int)i2->IsotopicTGroup[i].nNum_T - (int)i1->IsotopicTGroup[i].nNum_T ) + return ret; + if ( ret = (int)i2->IsotopicTGroup[i].nNum_D - (int)i1->IsotopicTGroup[i].nNum_D ) + return ret; + if ( ret = (int)i2->IsotopicTGroup[i].nNum_H - (int)i1->IsotopicTGroup[i].nNum_H ) + return ret; + } + + /**************************************************** + compare isotopic stereo + ****************************************************/ + ret = CompareInchiStereo( i1->StereoIsotopic, i1->nFlags, i2->StereoIsotopic, i2->nFlags ); + if ( ret ) { + return ret; + } + } + + /********************************************************** + compare charges: non-charged first, then in order of + ascending charges (negative first) + ***********************************************************/ + if ( i2->nTotalCharge && i1->nTotalCharge ) { + /* both are charged; smaller charges first */ + ret = (int)i1->nTotalCharge - (int)i2->nTotalCharge; + return ret; + } + if ( ret = (i1->nTotalCharge? 1:0) - (i2->nTotalCharge? 1:0) ) { + /* only one is charged; uncharged first */ + return ret; + } + /* stable sort */ + /*ret = p1->ord_number - p2->ord_number;*/ + + return ret; +} + +int CompINChINonTaut2(const void *p1, const void *p2) +{ + int ret; + ret = CompINChI2( (const INCHI_SORT *)p1, (const INCHI_SORT *)p2, TAUT_NON, 1 ); +#if ( CANON_FIXH_TRANS == 1 ) + if ( !ret ) { + /* to obtain canonical transposition 2004-05-10 */ + ret = CompINChI2( (const INCHI_SORT *)p1, (const INCHI_SORT *)p2, TAUT_YES, 1 ); + } +#endif + if ( !ret ) { + /* stable sort */ + ret = ((const INCHI_SORT *)p1)->ord_number - ((const INCHI_SORT *)p2)->ord_number; + } + return ret; +} + +int CompINChITaut2(const void *p1, const void *p2) +{ + int ret; + ret = CompINChI2( (const INCHI_SORT *)p1, (const INCHI_SORT *)p2, TAUT_YES, 1 ); +#if ( CANON_FIXH_TRANS == 1 ) + if ( !ret ) { + /* to obtain canonical transposition 2004-05-10 */ + ret = CompINChI2( (const INCHI_SORT *)p1, (const INCHI_SORT *)p2, TAUT_NON, 1 ); + } +#endif + if ( !ret ) { + /* stable sort */ + ret = ((const INCHI_SORT *)p1)->ord_number - ((const INCHI_SORT *)p2)->ord_number; + } + return ret; +} + +/* + mystrrev( ... ) + NB: + strrev from K&R is not in ANSI-compatible C library +*/ +void mystrrev( char *p ) +{ + char c, *q = p; + while( *q++ ) + ; + q -= 2; /* pointer to the last character */ + while ( p < q ) { + c = *q; /* swap */ + *q-- = *p; + *p++ = c; + } +} + +/* + Find DFS order for CT(canon. numbers and Hs) output +*/ + +typedef struct tagOrderStruct +{ + AT_NUMB *m_gDfs4CT_nDfsNumber; + AT_NUMB *m_gDfs4CT_nNumDescendants; + int m_gDfs4CT_nCurrentAtom; +} OrderStruct; + +static int CompareDfsDescendants4CT( const void *a1, const void *a2, void *p ) +{ + OrderStruct *os=(OrderStruct *) p; + int neigh1 = (int)*(const AT_RANK*)a1; + int neigh2 = (int)*(const AT_RANK*)a2; + if ( neigh1 > MAX_ATOMS ) + { + if ( neigh2 > MAX_ATOMS ) + { + return 0; + } + return 1; + } + else if ( neigh2 > MAX_ATOMS ) + { + return -1; + } + else + { + AT_RANK nCurDfsNumber = os->m_gDfs4CT_nDfsNumber[os->m_gDfs4CT_nCurrentAtom]; + int nDesc1 = nCurDfsNumber > os->m_gDfs4CT_nDfsNumber[neigh1]? + 0 : (int)os->m_gDfs4CT_nNumDescendants[neigh1]; + int nDesc2 = nCurDfsNumber > os->m_gDfs4CT_nDfsNumber[neigh2]? + 0 : (int)os->m_gDfs4CT_nNumDescendants[neigh2]; + int ret; + if ( ret = nDesc1 - nDesc2 ) + { + return ret; + } + return (int)neigh1 - (int)neigh2; /* canon. numbers difference */ + } +} + +AT_NUMB *GetDfsOrder4CT( CANON_GLOBALS *pCG, + AT_NUMB *LinearCT, + int nLenCT, + S_CHAR *nNum_H, + int num_atoms, + int nCtMode ) +{ + AT_NUMB *nStackAtom = NULL; + int nTopStackAtom=-1; + AT_NUMB *nNumDescendants = NULL; /* number of descendants incl. closures and the atom itself */ + AT_NUMB *nDfsNumber = NULL; + S_CHAR *cNeighNumb = NULL; + NEIGH_LIST *nl = NULL; + AT_NUMB nDfs; + int i, j, u, k, start, num_rings, nTotOutputStringLen; + AT_NUMB *nOutputString = NULL, cDelim; + int bCtPredecessors = (nCtMode & CT_MODE_PREDECESSORS); + OrderStruct os; + + /* allocate arrays */ + nStackAtom = (AT_NUMB *)inchi_malloc( num_atoms*sizeof(nStackAtom[0])); + nNumDescendants = (AT_NUMB *)inchi_malloc( num_atoms*sizeof(nNumDescendants[0])); + nDfsNumber = (AT_NUMB *)inchi_malloc( num_atoms*sizeof(nDfsNumber[0])); + cNeighNumb = (S_CHAR *)inchi_malloc( num_atoms*sizeof(cNeighNumb[0])); + nl = CreateNeighListFromLinearCT( LinearCT, nLenCT, num_atoms ); + + /* check allocation */ + if ( !nStackAtom || !nNumDescendants || !nDfsNumber || !cNeighNumb || !nl ) { + /* ret = CT_OUT_OF_RAM; */ /* program error */ /* */ + goto exit_function; + } + if ( bCtPredecessors ) { + start = 0; + } else { + /* find DFS start vertex (atom) */ + for ( i = 1, start = 0; i < num_atoms; i ++ ) { + if ( nl[i][0] < nl[start][0] ) { /* index = nRank-1 */ + start = i; + } + } + } + /* + vertex information: + 1. Number of (forward edges) + (back edges, first visit -- ring closures): nl[i][0] + 2. Number of vertices traversed from this vertex, including the vertex: nNumDescendants[i] + 3. Each edge information: + a. forward edge (0) or back edge (1) indicator: nDfsNumber[i] > nDfsNumber[neigh] + b. neighbor at another end of the edge neigh = nl[i][k+1], k < i + + Total per edge: 2 + 2*(number of edges) + */ + + /* DFS initiation */ + u = start; /* start atom */ + nDfs = 0; + nTopStackAtom =-1; + memset( nDfsNumber, 0, num_atoms*sizeof(nDfsNumber[0])); + memset( nNumDescendants, 0, num_atoms*sizeof(nNumDescendants[0])); + memset( cNeighNumb, 0, num_atoms*sizeof(cNeighNumb[0])); + /* push the start atom on the stack */ + nDfsNumber[u] = ++nDfs; + if ( bCtPredecessors ) { + nNumDescendants[u] = 0; /* atom #1 has no predecessor */ + } else { + nNumDescendants[u] = 1; /* count itself as a descendant */ + } + nStackAtom[++nTopStackAtom] = (AT_NUMB)u; + /* nNumStartChildren = 0; */ + num_rings = 0; + + /* DFS */ + + do { + /* advance */ + while ( i=(int)nStackAtom[nTopStackAtom], j = (int)cNeighNumb[i]+1, (int)nl[i][0] >= j ) + /*while ( (int)nl[i=nStackAtom[nTopStackAtom]][0] >= (j = (int)cNeighNumb[i]+1) )*/ + /* replaced due to missing sequence point; undefined behavior, pointed by Geoffrey Hutchison */ + { + cNeighNumb[i] ++; + u = (int)nl[i][j]; /* jth neighbor of the vertex i */ + if ( !nDfsNumber[u] ) { + /* tree edge, 1st visit -- advance */ + /* put unexplored vertex u on the stack for further examination */ + nStackAtom[++nTopStackAtom] = (AT_NUMB)u; + nDfsNumber[u] = ++nDfs; + if ( bCtPredecessors ) { + nNumDescendants[u] = i+1; /* predecessor's rank */ + } else { + nNumDescendants[u] ++; /* count atom u as its descendant */ + } + } else + if ( nTopStackAtom && u != (int)nStackAtom[nTopStackAtom-1] && + /* back edge: u is not a predecessor of i */ + nDfsNumber[u] < nDfsNumber[i] ) { + /* Back edge, 1st visit: u is an ancestor of i (ring closure) */ + if ( !bCtPredecessors ) { + nNumDescendants[i] ++; /* count closures as descendants */ + } + num_rings ++; /* count ring closures */ + } else { + nl[i][j] = MAX_ATOMS+1; /* back edge, 2nd visit: mark as deleted */ + } + } + cNeighNumb[i] = 0; /* all neighbors of the ith atom have been + traversed; resore the neighbor counter */ + /* back up */ + if ( !bCtPredecessors && nTopStackAtom /* that is, i != start */) { + u = (int)nStackAtom[nTopStackAtom-1]; /* predecessor of i */ + nNumDescendants[u] += nNumDescendants[i]; /* add descendants */ + } + } while ( --nTopStackAtom >= 0 ); + + /* Sort the neighbors in ascending order so that: + primary key = number of descendants in the DFS tree; closure neighbor is 0 + secondary key = canonical number (here vertex number = canonical number - 1) + */ + + os.m_gDfs4CT_nDfsNumber = nDfsNumber; + os.m_gDfs4CT_nNumDescendants = nNumDescendants; + os.m_gDfs4CT_nCurrentAtom = -1; + + /* sorting; deleted will be the last neighbors */ + for ( i = 0; i < num_atoms; i ++ ) { + if ( nl[i][0] > 1 ) { + os.m_gDfs4CT_nCurrentAtom = i; + insertions_sort( &os, &nl[i][1], nl[i][0], sizeof(nl[i][1]), CompareDfsDescendants4CT ); + } + /* reduce number of neighbors to exclude deleted */ + for ( k = 0; k < nl[i][0] && nl[i][k+1] <= MAX_ATOMS; k ++ ) + ; + nl[i][0] = k; + } + + nTotOutputStringLen = 3*(num_atoms+num_rings+1); /* last 3 elements are a 'zero termination' */ + + if ( bCtPredecessors ) { + if ( nOutputString = (AT_RANK *)inchi_calloc( nTotOutputStringLen, sizeof(nOutputString[0]) ) ) { + cDelim = '-'; + for ( u = 0, k = -3 ; u < num_atoms; u ++ ) { + k += 3; + if ( k+6 > nTotOutputStringLen ) { + goto exit_error; /* program error */ + } + nOutputString[k] = nNumDescendants[u]? nNumDescendants[u] : MAX_ATOMS+1; + nOutputString[k+1] = nNum_H? 16+nNum_H[u]:0; + nOutputString[k+2] = k? ',' : '\0'; + for ( j = 1; j <= nl[u][0] && nDfsNumber[u] > nDfsNumber[i=nl[u][j]]; j ++ ) { + /* closures */ + k += 3; + if ( k+6 > nTotOutputStringLen ) { + goto exit_error; /* program error */ + } + nOutputString[k] = i+1; /* closure */ + nOutputString[k+1] = 0; + nOutputString[k+2] = cDelim; + } + } + } + } else { + if ( nNumDescendants ) { /* do not need anymore */ + inchi_free( nNumDescendants ); + nNumDescendants = NULL; + } + /* + the output string contains: + (num_atoms) atoms for the DFS (spanning) tree + (num_atoms-1) delimiters for the DFS (spanning) tree + 1 character for each atom that has 1 terminal hydrogen atoms + 2 characters for each atom that has 2-9 terminal hydrogen atoms + 3 characters for each atom that has 10-99 terminal hydrogen atoms, etc. + (num_rings) atoms for the ring closures + (num_rings) delimiters for the ring closures + */ + + if ( nOutputString = (AT_RANK *)inchi_calloc( nTotOutputStringLen, sizeof(nOutputString[0]) ) ) { + u = start; /* start atom */ + nTopStackAtom =-1; + memset( cNeighNumb, 0, num_atoms*sizeof(cNeighNumb[0])); + /* push the start atom on the stack */ + nStackAtom[++nTopStackAtom] = (AT_NUMB)u; + /* output the starting atom */ + k = 0; + nOutputString[k] = u+1; + nOutputString[k+1] = nNum_H? 16+nNum_H[u]:0; + nOutputString[k+2] = '\0'; + + do { + /* advance */ + while ( i=(int)nStackAtom[nTopStackAtom], j = (int)cNeighNumb[i]+1, (int)nl[i][0] >= j ) + /*while ( (int)nl[i=nStackAtom[nTopStackAtom]][0] >= (j = (int)cNeighNumb[i]+1) )*/ + /* replaced due to missing sequence point; undefined behavior, reported by Geoffrey Hutchison */ + { + k += 3; + if ( k+6 > nTotOutputStringLen ) { + goto exit_error; /* program error */ + } + cNeighNumb[i] ++; + u = (int)nl[i][j]; /* neighbor */ + + /* output neighbor's canonical number */ + nOutputString[k] = u+1; + + if ( nDfsNumber[u] > nDfsNumber[i] ) { + /* tree edge, 1st visit -- advance */ + /* put 'unexplored' vertex u on the stack */ + nStackAtom[++nTopStackAtom] = (AT_NUMB)u; + + /* output neighbor's number of H */ + nOutputString[k+1] = nNum_H? 16+nNum_H[u]:0; + } else { + nOutputString[k+1] = 0; + } + /* output a delimiter preceding the neighbor */ + if ( 1 < nl[i][0] ) { + if ( j == 1 ) { + cDelim = '('; + } else + if ( j == nl[i][0] ) { + cDelim = ')'; + } else { + cDelim = ','; + } + } else { + cDelim = '-'; + } + nOutputString[k+2] = cDelim; + } + cNeighNumb[i] = 0; + + /* back up: nothing else to do */ + } while ( --nTopStackAtom >= 0 ); + } + } + goto exit_function; + +exit_error: + if ( nOutputString ) { + inchi_free( nOutputString ); + nOutputString = NULL; + } + +exit_function: + if ( nStackAtom ) + inchi_free( nStackAtom ); + if ( nNumDescendants ) + inchi_free( nNumDescendants ); + if ( nDfsNumber ) + inchi_free( nDfsNumber ); + if ( cNeighNumb ) + inchi_free( cNeighNumb ); + if ( nl ) + FreeNeighList( nl ); + + return nOutputString; +} + +int GetInpStructErrorType( INPUT_PARMS *ip, int err, char *pStrErrStruct, int num_inp_atoms ) +{ + if ( err && err == 9 ) + return _IS_ERROR; /* sdfile bypassed to $$$$ */ + if ( err && err < 30 ) + return _IS_FATAL; + if ( num_inp_atoms <= 0 || err ) { + if ( 98 == err && 0 == num_inp_atoms && ip->bAllowEmptyStructure ) + return _IS_WARNING; + return _IS_ERROR; + } + if ( pStrErrStruct[0] ) + return _IS_WARNING; + return _IS_OKAY; +} + +int ProcessStructError( INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM *log_file, + char *pStrErrStruct, + int nErrorType, + long num_inp, + INPUT_PARMS *ip ) +{ + int b_ok; +#ifdef TARGET_LIB_FOR_WINCHI + int bPlainText = (ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) && + (ip->bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW ); +#else + int bPlainText = 0; +#endif + if ( !bPlainText ) + { + return nErrorType; + } + + /* Fatal error, Error, Warning */ + if ( nErrorType ) + { + b_ok = 0; /* to make VC++ happy */ + if ( bPlainText ) + { + if ( !(b_ok=OutputINChIPlainError( out_file, pStrErrStruct, nErrorType ) ) ) + { + inchi_ios_eprint( log_file, "Cannot create message for error (structure #%ld.%s%s%s%s) Terminating.\n", + num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); + } + else + { + inchi_ios_print( out_file, "\n" ); /* add a blank line after the WINCHI Window message */ + } + } + return b_ok? nErrorType : _IS_FATAL; + } + + return nErrorType; +} + +#if ( READ_INCHI_STRING == 1 ) /* { */ + +int CompareReversedStereoINChI( INChI_Stereo *s1/* InChI from reversed struct */, + INChI_Stereo *s2 /* input InChI */ + ) +{ + if ( s1 == NULL && s2 == NULL ) + return 0; + if ( (s1 == NULL) ^ (s2 == NULL) ) { + INChI_Stereo *s = s1? s1 : s2; + if ( s->nNumberOfStereoCenters || s->nNumberOfStereoBonds ) { + return 20; /* Diff: Missing Stereo */ + } else { + return 0; + } + } + + if ( s1->nNumberOfStereoCenters != s2->nNumberOfStereoCenters ) + return 21; /* Diff: Number of sp3 stereocenters */ + if ( s1->nNumberOfStereoCenters > 0 ) { + if ( memcmp( s1->nNumber, s2->nNumber, s1->nNumberOfStereoCenters*sizeof(s1->nNumber[0]) ) ) + return 22; /* Diff: sp3 stereocenter locations */ + if ( memcmp( s1->t_parity, s2->t_parity, s1->nNumberOfStereoCenters*sizeof(s1->t_parity[0]) ) ) + return 23; /* Diff: sp3 stereocenter parities */ + if ( s1->nCompInv2Abs != s2->nCompInv2Abs && s1->nCompInv2Abs && s2->nCompInv2Abs ) + return 24; /* Diff: sp3 inversion */ + /* + if ( s1->nNumberInv && s2->nNumberInv ) { + if ( memcmp( s1->nNumberInv, s2->nNumberInv, s1->nNumberOfStereoCenters*sizeof(s1->nNumber[0]) ) ) + return 25; + if ( memcmp( s1->t_parityInv, s2->t_parityInv, s1->nNumberOfStereoCenters*sizeof(s1->t_parity[0]) ) ) + return 26; + if ( s1->nCompInv2Abs != s2->nCompInv2Abs || + s1->bTrivialInv != s2->bTrivialInv ) { + return 27; + } + } else + if ( s1->nNumberInv || s2->nNumberInv ) { + return 28; + } + */ + } + if ( s1->nNumberOfStereoBonds != s2->nNumberOfStereoBonds ) + return 25; /* Diff: Number of stereobonds */ + if ( s1->nNumberOfStereoBonds > 0 ) { + if ( memcmp( s1->nBondAtom1, s2->nBondAtom1, s1->nNumberOfStereoBonds*sizeof(s1->nBondAtom1[0]) ) ) + return 26; /* Diff: Stereobond 1st atom locations */ + if ( memcmp( s1->nBondAtom2, s2->nBondAtom2, s1->nNumberOfStereoBonds*sizeof(s1->nBondAtom2[0]) ) ) + return 27; /* Diff: Stereobond 2nd atom locations */ + if ( memcmp( s1->b_parity, s2->b_parity, s1->nNumberOfStereoBonds*sizeof(s1->b_parity[0]) ) ) + return 28; /* Diff: Stereobond parities */ + } + return 0; +} + +int CompareReversedStereoINChI2( INChI_Stereo *s1/* InChI from reversed struct */, + INChI_Stereo *s2 /* input InChI */, + ICR *picr) +{ + int ret = 0; + int j1, j2, num_eq, num_dif, num_extra_undf, num_miss_undf, num_in1_only, num_in2_only; + int bAddSb = !(picr->num_sb_undef_in1_only + picr->num_sb_in1_only + picr->num_sb_in2_only); + int bAddSc = !(picr->num_sc_undef_in1_only + picr->num_sc_in1_only + picr->num_sc_in2_only); + + int nNumSc1 = s1? s1->nNumberOfStereoCenters : 0; + int nNumSc2 = s2? s2->nNumberOfStereoCenters : 0; + int nNumSb1 = s1? s1->nNumberOfStereoBonds : 0; + int nNumSb2 = s2? s2->nNumberOfStereoBonds : 0; + + if ( (nNumSc1 || nNumSc1) && + ( nNumSc1 != nNumSc2 || + memcmp( s1->nNumber, s2->nNumber, nNumSc1*sizeof(s1->nNumber[0] ) ) || + memcmp( s1->t_parity, s2->t_parity, nNumSc1*sizeof(s1->t_parity[0]) ) ) ) { + num_eq = num_dif = num_extra_undf = num_miss_undf = num_in1_only = num_in2_only = 0; + for ( j1 = j2 = 0; j1 < nNumSc1 && j2 < nNumSc2; ) { + if ( s1->nNumber[j1] == s2->nNumber[j2] ) { + if ( s1->t_parity[j1] == s2->t_parity[j2] ) { + num_eq ++; + } else { + num_dif ++; + } + j1 ++; + j2 ++; + } else + if ( s1->nNumber[j1] < s2->nNumber[j2] ) { + num_in1_only ++; + if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { + num_extra_undf ++; + } + if ( bAddSc ) { + if ( picr->num_sc_in1_only < ICR_MAX_SC_IN1_ONLY ) + picr->sc_in1_only[picr->num_sc_in1_only ++] = j1; + if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { + if ( picr->num_sc_undef_in1_only < ICR_MAX_SC_UNDF ) + picr->sc_undef_in1_only[picr->num_sc_undef_in1_only ++] = j1; + } + } + j1 ++; + } else { + num_in2_only ++; + if ( s2->t_parity[j2] == AB_PARITY_UNDF ) { + num_miss_undf ++; + } + if ( bAddSc ) { + if ( picr->num_sc_in2_only < ICR_MAX_SC_IN2_ONLY ) + picr->sc_in2_only[picr->num_sc_in2_only ++] = j2; + if ( s2->t_parity[j2] == AB_PARITY_UNDF ) { + if ( picr->num_sc_undef_in2_only < ICR_MAX_SC_UNDF ) + picr->sc_undef_in2_only[picr->num_sc_undef_in2_only ++] = j1; + } + } + j2 ++; + } + } + while ( j1 < nNumSc1 ) { + if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { + num_extra_undf ++; + } + num_in1_only ++; + if ( bAddSc ) { + if ( picr->num_sc_in1_only < ICR_MAX_SC_IN1_ONLY ) + picr->sc_in1_only[picr->num_sc_in1_only ++] = j1; + if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { + if ( picr->num_sc_undef_in1_only < ICR_MAX_SC_UNDF ) + picr->sc_undef_in1_only[picr->num_sc_undef_in1_only ++] = j1; + } + } + j1 ++; + } + while ( j2 < nNumSc2 ) { + if ( s2->t_parity[j2] == AB_PARITY_UNDF ) { + num_miss_undf ++; + } + num_in2_only ++; + if ( bAddSc ) { + if ( picr->num_sc_in2_only < ICR_MAX_SC_IN2_ONLY ) + picr->sc_in2_only[picr->num_sc_in2_only ++] = j2; + } + j2 ++; + } + if ( num_dif ) { + ret |= IDIF_SC_PARITY; + } + if ( num_in1_only ) { + if ( num_extra_undf ) { + ret |= IDIF_SC_EXTRA_UNDF; + } + if ( num_in1_only != num_extra_undf ) { + ret |= IDIF_SC_EXTRA; + } + } + if ( num_in2_only ) { + if ( num_miss_undf ) { + ret |= IDIF_SC_MISS_UNDF; + } + if ( num_in2_only != num_miss_undf ) { + ret |= IDIF_SC_MISS; + } + } + } + if ( s1 && s2 && s1->nCompInv2Abs != s2->nCompInv2Abs && s1->nCompInv2Abs && s2->nCompInv2Abs ) { + ret |= IDIF_SC_INV; + } + + if ( (nNumSb1 || nNumSb2 ) && + (nNumSb1 != nNumSb2 || + memcmp( s1->nBondAtom1, s2->nBondAtom1, nNumSb1*sizeof(s1->nBondAtom1[0]) ) || + memcmp( s1->nBondAtom2, s2->nBondAtom2, nNumSb1*sizeof(s1->nBondAtom2[0]) ) || + memcmp( s1->b_parity, s2->b_parity, nNumSb1*sizeof(s1->b_parity[0]) ) ) ) { + num_eq = num_dif = num_extra_undf = num_miss_undf = num_in1_only = num_in2_only = 0; + for ( j1 = j2 = 0; j1 < nNumSb1 && j2 < nNumSb2; ) { + if ( s1->nBondAtom1[j1] == s2->nBondAtom1[j2] && + s1->nBondAtom2[j1] == s2->nBondAtom2[j2] ) { + if ( s1->b_parity[j1] == s2->b_parity[j2] ) { + num_eq ++; + } else { + num_dif ++; + } + j1 ++; + j2 ++; + } else + if ( s1->nBondAtom1[j1] < s2->nBondAtom1[j2] || + s1->nBondAtom1[j1] == s2->nBondAtom1[j2] && s1->nBondAtom2[j1] < s2->nBondAtom2[j2]) { + num_in1_only ++; + if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { + num_extra_undf ++; + } + if ( bAddSb ) { + if ( picr->num_sb_in1_only < ICR_MAX_SB_IN1_ONLY ) + picr->sb_in1_only[picr->num_sb_in1_only ++] = j1; + if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { + if ( picr->num_sb_undef_in1_only < ICR_MAX_SB_UNDF ) + picr->sb_undef_in1_only[picr->num_sb_undef_in1_only ++] = j1; + } + } + j1 ++; + } else { + num_in2_only ++; + if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { + num_miss_undf ++; + } + if ( bAddSb ) { + if ( picr->num_sb_in2_only < ICR_MAX_SB_IN2_ONLY ) + picr->sb_in2_only[picr->num_sb_in2_only ++] = j2; + if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { + if ( picr->num_sb_undef_in2_only < ICR_MAX_SB_UNDF ) + picr->sb_undef_in2_only[picr->num_sb_undef_in2_only ++] = j1; + } + } + j2 ++; + } + } + while ( j1 < nNumSb1 ) { + num_in1_only ++; + if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { + num_extra_undf ++; + } + if ( bAddSb ) { + if ( picr->num_sb_in1_only < ICR_MAX_SB_IN1_ONLY ) + picr->sb_in1_only[picr->num_sb_in1_only ++] = j1; + if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { + if ( picr->num_sb_undef_in1_only < ICR_MAX_SB_UNDF ) + picr->sb_undef_in1_only[picr->num_sb_undef_in1_only ++] = j1; + } + } + j1 ++; + } + while ( j2 < nNumSb2 ) { + num_in2_only ++; + if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { + num_miss_undf ++; + } + if ( bAddSb ) { + if ( picr->num_sb_in2_only < ICR_MAX_SB_IN2_ONLY ) + picr->sb_in2_only[picr->num_sb_in2_only ++] = j2; + if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { + if ( picr->num_sb_undef_in2_only < ICR_MAX_SB_UNDF ) + picr->sb_undef_in2_only[picr->num_sb_undef_in2_only ++] = j1; + } + } + j2 ++; + } + if ( num_dif ) { + ret |= IDIF_SB_PARITY; + } + if ( num_in1_only ) { + if ( num_extra_undf ) { + ret |= IDIF_SB_EXTRA_UNDF; + } + if ( num_in1_only != num_extra_undf ) { + ret |= IDIF_SB_EXTRA; + } + } + if ( num_in2_only ) { + if ( num_miss_undf ) { + ret |= IDIF_SB_MISS_UNDF; + } + if ( num_in2_only != num_miss_undf ) { + ret |= IDIF_SB_MISS; + } + } + } + + return ret; +} + +int CompareReversedINChI( INChI *i1 /* InChI from reversed struct */, + INChI *i2 /* input InChI */, + INChI_Aux *a1, + INChI_Aux *a2 ) +{ + int ret; + if ( i1 == NULL && i2 == NULL ) + return 0; + if ( (i1 == NULL) ^ (i2 == NULL) ) + return 1; /* Diff: Missing InChI */ + + if ( i1->nErrorCode == i2->nErrorCode ) { + if ( i1->nErrorCode ) + return 0; + } else { + return 2; /* Diff: Error codes */ + } + if ( i1->bDeleted != i2->bDeleted ) { + return 1; /* Diff: Missing InChI */ + } + if ( i1->nNumberOfAtoms != i2->nNumberOfAtoms ) + return 3; /* Diff: Num. atoms */ + if ( i1->nNumberOfAtoms > 0 ) { + if ( memcmp( i1->nAtom, i2->nAtom, i1->nNumberOfAtoms*sizeof(i1->nAtom[0]) ) ) + return 4; /* Diff: Elements */ + if ( strcmp( i1->szHillFormula, i2->szHillFormula ) ) + return 7; /* Diff: Hill Formulas */ + if ( memcmp( i1->nNum_H, i2->nNum_H, i1->nNumberOfAtoms*sizeof(i1->nNum_H[0]) ) ) { + if ( i1->lenConnTable > 1 || i2->lenConnTable > 1 ) { + return 5; /* Diff: H Locations (mobile H present) */ + } else { + return 6; /* Diff: H Locations (no mobile H) */ + } + } + /* fixed H */ + if ( i1->nNum_H_fixed || i2->nNum_H_fixed ) { + int bHasFixedH1 = 0, bHasFixedH2 = 0, i, j1, j2; + if ( i1->nNum_H_fixed ) { + for ( i = 0; i < i1->nNumberOfAtoms; i ++ ) { + if ( i1->nNum_H_fixed[i] ) { + bHasFixedH1 ++; + } + } + } + if ( i2->nNum_H_fixed ) { + for ( i = 0; i < i2->nNumberOfAtoms; i ++ ) { + if ( i2->nNum_H_fixed[i] ) { + bHasFixedH2 ++; + } + } + } + /* count the differences */ + j1 = j2 = 0; + if ( bHasFixedH1 && !bHasFixedH2 ) { + for ( i = 0; i < i1->nNumberOfAtoms; i ++ ) { + if ( i1->nNum_H_fixed[i] > 0 ) { + j1 ++; + } else + if ( i1->nNum_H_fixed[i] < 0 ) { + j2 ++; + } + } + + return 18; /* Diff: Extra Fixed-H */ + } else + if ( !bHasFixedH1 && bHasFixedH2 ) { + for ( i = j1 = j2 = 0; i < i1->nNumberOfAtoms; i ++ ) { + if ( 0 > i2->nNum_H_fixed[i] ) { + j1 ++; + } else + if ( 0 < i2->nNum_H_fixed[i] ) { + j2 ++; + } + } + return 19; /* Diff: Missed Fixed-H */ + } else + if ( bHasFixedH1 && bHasFixedH2 && + memcmp( i1->nNum_H_fixed, i2->nNum_H_fixed, i1->nNumberOfAtoms*sizeof(i1->nNum_H_fixed[0]) ) ) { + for ( i = j1 = j2 = 0; i < i1->nNumberOfAtoms; i ++ ) { + if ( i1->nNum_H_fixed[i] > i2->nNum_H_fixed[i] ) { + j1 ++; + } else + if ( i1->nNum_H_fixed[i] < i2->nNum_H_fixed[i] ) { + j2 ++; + } + } + } + ret = (j1 && j2)? 20 : j1? 18 : j2? 19 : 0; + if ( ret ) { + return ret; /* 20 => Diff: NotEql Fixed-H */ + /* 19 => Diff: Missed Fixed-H (i1 has less) */ + /* 18 => Diff: Extra Fixed-H (i1 has more) */ + } + } + } + + if ( i1->lenConnTable != i2->lenConnTable ) + return 8; /* Diff: Connections length */ + if ( i1->lenConnTable > 0 && memcmp( i1->nConnTable, i2->nConnTable, i1->lenConnTable*sizeof(i1->nConnTable[0]) ) ) + return 9; /* Diff: Connections */ + /* output special cases: different number of t-groups, different sizes of t-groups, different endpoints */ + if ( i1->lenTautomer != i2->lenTautomer && (i1->lenTautomer > 1 || i2->lenTautomer > 1) ) + return 10; /* Diff: Mobile groups length */ /* in isotopic or deprotonated cases i1->lenTautomer == 1 && i1->nTautomer[0] = 0 */ + if ( (i1->lenTautomer > 1 && i2->lenTautomer > 1) && + memcmp( i1->nTautomer, i2->nTautomer, i1->lenTautomer*sizeof(i1->nTautomer[0]) ) ) + return 11; /* Diff: Mobile groups */ + + if ( i1->nNumberOfIsotopicAtoms != i2->nNumberOfIsotopicAtoms ) + return 12; /* Diff: Isotopic atoms number */ + if ( i1->nNumberOfIsotopicAtoms > 0 && memcmp( i1->IsotopicAtom, i2->IsotopicAtom, i1->nNumberOfIsotopicAtoms*sizeof(i1->IsotopicAtom[0]) ) ) + return 13; /* Diff: Isotopic atoms */ + if ( i1->nTotalCharge != i2->nTotalCharge ) + return 14; /* Diff: Charge */ +/* + if ( i1->nNumberOfIsotopicTGroups != i2->nNumberOfIsotopicTGroups ) + return 14; + if ( i1->nNumberOfIsotopicTGroups > 0 && memcmp( i1->IsotopicTGroup, i2->IsotopicTGroup, i1->nNumberOfIsotopicTGroups*sizeof(i1->IsotopicTGroup[0]) ) ) + return 15; +*/ + if ( a1 && a2 ) { + if ( a1->nNumRemovedProtons != a2->nNumRemovedProtons ) + return 16; /* Diff: Number of removed protons */ + if ( memcmp( a1->nNumRemovedIsotopicH, a2->nNumRemovedIsotopicH, sizeof(a1->nNumRemovedIsotopicH) ) ) + return 17; /* Diff: Removed isotopic H */ + } +/* + if ( i1->nPossibleLocationsOfIsotopicH && i2->nPossibleLocationsOfIsotopicH ) { + if ( i1->nPossibleLocationsOfIsotopicH[0] != i2->nPossibleLocationsOfIsotopicH[0] || + memcmp(i1->nPossibleLocationsOfIsotopicH, i2->nPossibleLocationsOfIsotopicH, + sizeof(i1->nPossibleLocationsOfIsotopicH[0])*i1->nPossibleLocationsOfIsotopicH[0]) ) + return 18; + } else + if ( !i1->nPossibleLocationsOfIsotopicH != !i2->nPossibleLocationsOfIsotopicH ) { + return 19; + } +*/ + /* ret = 20..31 => 40..51 */ + if ( ret = CompareReversedStereoINChI( i1->Stereo, i2->Stereo ) ) + return ret+20; + /* ret = 40..51 => 60..71 */ + + if ( !i2->StereoIsotopic && i2->Stereo && i1->StereoIsotopic && + 0 < (i1->StereoIsotopic->nNumberOfStereoBonds + i1->StereoIsotopic->nNumberOfStereoCenters) && + 0 == CompareReversedStereoINChI( i1->StereoIsotopic, i2->Stereo ) ) { + /* InChI from reversed structure does not contain fully duplicated isotopic stereo */ + ; + } else + + if ( ret = CompareReversedStereoINChI( i1->StereoIsotopic, i2->StereoIsotopic ) ) { + return ret+40; + } + + return 0; +} + +int CompareIcr( ICR *picr1, ICR *picr2, + INCHI_MODE *pin1, INCHI_MODE *pin2, + INCHI_MODE mask ) +{ + int nNumExtraBits1 = 0, nNumExtraBits2 = 0, bit1, bit2; + INCHI_MODE Flg1=picr1->flags, Flg2 = picr2->flags, cur_bit = 1, in1, in2; + int i, ret; + + /* compare flags */ + in1 = in2 = 0; + for ( i = 0; Flg1 || Flg2; i ++, Flg1 >>= 1, Flg2 >>= 1, cur_bit <<= 1 ) { + if ( !(mask & cur_bit) ) { + continue; + } + bit1 = Flg1 & 1; + bit2 = Flg2 & 1; + if ( bit1 && !bit2 ) { + in1 |= 1 << i; + nNumExtraBits1 ++; + } else + if ( !bit1 && bit2 ) { + in2 |= 1 << i; + nNumExtraBits2 ++; + } + } + if ( nNumExtraBits1 && !nNumExtraBits2 ) { + ret = 1; + } else + if ( !nNumExtraBits1 && nNumExtraBits2 ) { + ret = -1; + } else + if ( !in1 && !in2 ) { + ret = 0; + } else { + ret = 2; /* compare produced undefined results */ + } + if ( pin1 ) *pin1 = in1; + if ( pin2 ) *pin2 = in2; + /* more detailed compare not implemented */ + return ret; +} + +INCHI_MODE CompareReversedINChI2( INChI *i1 /* InChI from reversed struct */, + INChI *i2 /* input InChI */, + INChI_Aux *a1, INChI_Aux *a2, + ICR *picr, + int *err ) +{ + INCHI_MODE ret = 0; + INChI_Stereo *Stereo1=NULL, *Stereo2=NULL; + int n1, n2, m, j, j1, j2, ret2, num_H1, num_H2; + + *err = 0; + + memset( picr, 0, sizeof(*picr) ); + + if ( i1 == NULL && i2 == NULL ) + return 0; + if ( (i1 == NULL) ^ (i2 == NULL) ) { + ret |= IDIF_PROBLEM; /* one InChI exists while another doesn't */ + goto exit_function; + } + + if ( i1->nErrorCode == i2->nErrorCode ) { + if ( i1->nErrorCode ) { + ret |= IDIF_PROBLEM; /* both InChI have same error codes */ + goto exit_function; + } + } else { + ret |= IDIF_PROBLEM; /* at least one InChI has an error code */ + goto exit_function; + } + + if ( i1->nNumberOfAtoms != i2->nNumberOfAtoms ) { + ret |= IDIF_NUM_AT; + goto exit_function; + } + if ( i1->nNumberOfAtoms > 0 ) { + if ( memcmp( i1->nAtom, i2->nAtom, i1->nNumberOfAtoms*sizeof(i1->nAtom[0]) ) ) { + ret |= IDIF_ATOMS; + goto exit_function; + } + /* IDIF_NON_TAUT_H, IDIF_MORE_FH, IDIF_LESS_FH */ + if ( memcmp( i1->nNum_H, i2->nNum_H, i1->nNumberOfAtoms*sizeof(i1->nNum_H[0]) ) ) { + ret |= IDIF_POSITION_H; + for ( j1 = 0; j1 < i1->nNumberOfAtoms; j1 ++ ) { + if ( i1->nNum_H[j1] != i2->nNum_H[j1] && picr->num_diff_pos_H < ICR_MAX_DIFF_FIXED_H ) { + picr->diff_pos_H_at[picr->num_diff_pos_H] = j1; + picr->diff_pos_H_nH[picr->num_diff_pos_H] = i1->nNum_H[j1] - i2->nNum_H[j1]; + picr->num_diff_pos_H ++; + } + } + } + /* fixed H */ + if ( i1->nNum_H_fixed || i2->nNum_H_fixed ) { + int bHasFixedH1 = 0, bHasFixedH2 = 0, i; + if ( i1->nNum_H_fixed ) { + for ( i = 0; i < i1->nNumberOfAtoms; i ++ ) { + if ( i1->nNum_H_fixed[i] ) { + bHasFixedH1 ++; + } + } + } + if ( i2->nNum_H_fixed ) { + for ( i = 0; i < i2->nNumberOfAtoms; i ++ ) { + if ( i2->nNum_H_fixed[i] ) { + bHasFixedH2 ++; + } + } + } + if ( bHasFixedH1 && !bHasFixedH2 ) { + for ( i = j = 0; i < i1->nNumberOfAtoms; i ++ ) { + if ( i1->nNum_H_fixed[i] ) { + if ( j < ICR_MAX_DIFF_FIXED_H ) { + picr->fixed_H_at1_more[j] = i; + picr->fixed_H_nH1_more[j] = i1->nNum_H_fixed[i]; + j ++; + } + } + } + picr->num_fixed_H1_more = j; + ret |= IDIF_MORE_FH; /* Extra Fixed-H */ + } else + if ( !bHasFixedH1 && bHasFixedH2 ) { + for ( i = j = 0; i < i2->nNumberOfAtoms; i ++ ) { + if ( i2->nNum_H_fixed[i] ) { + if ( j < ICR_MAX_DIFF_FIXED_H ) { + picr->fixed_H_at2_more[j] = i; + picr->fixed_H_nH2_more[j] = i2->nNum_H_fixed[i]; + j ++; + } + } + } + picr->num_fixed_H2_more = j; + ret |= IDIF_LESS_FH; /* Missed Fixed-H */ + } else + if ( bHasFixedH1 && bHasFixedH2 && + memcmp( i1->nNum_H_fixed, i2->nNum_H_fixed, i1->nNumberOfAtoms*sizeof(i1->nNum_H_fixed[0]) ) ) { + for ( i = j1 = j2 = 0; i < i1->nNumberOfAtoms; i ++ ) { + if ( i1->nNum_H_fixed[i] > i2->nNum_H_fixed[i] ) { + if ( j1 < ICR_MAX_DIFF_FIXED_H ) { + picr->fixed_H_at1_more[j1] = i; + picr->fixed_H_nH1_more[j1] = i1->nNum_H_fixed[i] - i2->nNum_H_fixed[i]; + j1 ++; + } + } else + if ( i1->nNum_H_fixed[i] < i2->nNum_H_fixed[i] ) { + if ( j2 < ICR_MAX_DIFF_FIXED_H ) { + picr->fixed_H_at2_more[j2] = i; + picr->fixed_H_nH2_more[j2] = i2->nNum_H_fixed[i] - i1->nNum_H_fixed[i]; + j2 ++; + } + } + } + ret |= (j1? IDIF_MORE_FH:0) | (j2? IDIF_LESS_FH:0); + picr->num_fixed_H1_more = j1; + picr->num_fixed_H2_more = j2; + } + } + } + /* compare formulas and H */ + num_H1 = 0; + num_H2 = 0; + ret2 = CompareHillFormulasNoH( i1->szHillFormula, i2->szHillFormula, &num_H1, &num_H2 ); + picr->tot_num_H1 = num_H1; + picr->tot_num_H2 = num_H2; + if ( ret2 ) { + ret |= IDIF_NUM_EL; + goto exit_function; + } + if ( num_H1 > num_H2 ) { + ret |= IDIF_MORE_H; + } + if ( num_H1 < num_H2 ) { + ret |= IDIF_LESS_H; + } + + if ( i1->lenConnTable != i2->lenConnTable ) { + ret |= IDIF_CON_LEN; + goto exit_function; + } else + if ( i1->lenConnTable > 0 && memcmp( i1->nConnTable, i2->nConnTable, i1->lenConnTable*sizeof(i1->nConnTable[0]) ) ) { + ret |= IDIF_CON_TBL; + goto exit_function; + } + /* output special cases: different number of t-groups, different sizes of t-groups, different endpoints */ + /* in isotopic or deprotonated cases i1->lenTautomer == 1 && i1->nTautomer[0] = 0 */ +/* + if ( i1->lenTautomer != i2->lenTautomer && (i1->lenTautomer > 1 || i2->lenTautomer > 1) ) { + ret |= IDIF_TAUT_LEN; + } +*/ + /* compare number of t-groups */ + n1 = i1->lenTautomer? i1->nTautomer[0] : 0; + n2 = i2->lenTautomer? i2->nTautomer[0] : 0; + if ( !n1 && n2 ) { + ret |= IDIF_NO_TAUT; + } else + if ( n1 && !n2 ) { + ret |= IDIF_WRONG_TAUT; + } else + if ( n1 == 1 && n2 > 1 ) { + ret |= IDIF_SINGLE_TG; + } else + if ( n1 > 1 && n2 == 1 ) { + ret |= IDIF_MULTIPLE_TG; + } else + if ( n1 != n2 ) { + ret |= IDIF_NUM_TG; + } + if ( n1 || n2 ) { + /* number of endpoints */ + int num1 = 0, num2 = 0, num_M1=0, num_M2=0; + int len, num_eq, num_in1_only, num_in2_only; + AT_NUMB *pe1 = (AT_NUMB *) inchi_malloc( (i1->lenTautomer+1) * sizeof(pe1[0]) ); + AT_NUMB *pe2 = (AT_NUMB *) inchi_malloc( (i2->lenTautomer+1) * sizeof(pe2[0]) ); + num_H1 = num_H2=0; + /* collect endpoints, H, (-) */ + if ( !pe1 || !pe2 ) { + if ( pe1 ) inchi_free( pe1 ); + if ( pe2 ) inchi_free( pe2 ); + *err = -1; /* allocation error */ + goto exit_function; + } + for ( m = 1; m < i1->lenTautomer; m += len ) { + len = i1->nTautomer[m ++]; + num_H1 += i1->nTautomer[m]; + num_M1 += i1->nTautomer[m+1]; + for ( j = 2; j < len; j ++ ) { + pe1[num1 ++] = i1->nTautomer[m + j]; + } + } + for ( m = 1; m < i2->lenTautomer; m += len ) { + len = i2->nTautomer[m ++]; + num_H2 += i2->nTautomer[m]; + num_M2 += i2->nTautomer[m+1]; + for ( j = 2; j < len; j ++ ) { + pe2[num2 ++] = i2->nTautomer[m + j]; + } + } + picr->num_taut_H1 = num_H1; + picr->num_taut_H2 = num_H2; + picr->num_taut_M1 = num_M1; + picr->num_taut_M2 = num_M2; + /* sort endpoints */ + insertions_sort_AT_RANK( pe1, num1 ); + insertions_sort_AT_RANK( pe2, num2 ); + /* compare */ + /* + if ( num1 < num2 ) { + ret |= IDIF_LESS_TG_ENDP; + } else + if ( num1 > num2 ) { + ret |= IDIF_MORE_TG_ENDP; + } + */ + /* compare all */ + num_eq = num_in1_only = num_in2_only = 0; + for ( j1 = j2 = 0; j1 < num1 && j2 < num2; ) { + if( pe1[j1] == pe2[j2] ) { + j1 ++; + j2 ++; + num_eq ++; + } else + if ( pe1[j1] < pe2[j2] ) { /* BC: fixed, was pe2[j1] 2006-03-27 */ + if ( picr->num_endp_in1_only < ICR_MAX_ENDP_IN1_ONLY ) { + picr->endp_in1_only[picr->num_endp_in1_only ++] = pe1[j1]; + } + j1 ++; + num_in1_only ++; + } else { + if ( picr->num_endp_in2_only < ICR_MAX_ENDP_IN2_ONLY ) { + picr->endp_in2_only[picr->num_endp_in2_only ++] = pe2[j2]; + } + j2 ++; + num_in2_only ++; + } + } + while ( j1 < num1 ) { + if ( picr->num_endp_in1_only < ICR_MAX_ENDP_IN1_ONLY ) { + picr->endp_in1_only[picr->num_endp_in1_only ++] = pe1[j1]; + } + j1 ++; + num_in1_only ++; + } + while ( j2 < num2 ) { + if ( picr->num_endp_in2_only < ICR_MAX_ENDP_IN2_ONLY ) { + picr->endp_in2_only[picr->num_endp_in2_only ++] = pe2[j2]; + } + j2 ++; + num_in2_only ++; + } + if ( num_in1_only ) { + ret |= IDIF_EXTRA_TG_ENDP; + } + if ( num_in2_only ) { + ret |= IDIF_MISS_TG_ENDP; + } + if ( !num_in1_only && !num_in2_only && num_eq ) { + ; /* same t-groups endpoints */ + } else { + ret |= IDIF_DIFF_TG_ENDP; + } + inchi_free( pe1 ); + inchi_free( pe2 ); + } + + if ( (i1->lenTautomer > 1 && i2->lenTautomer > 1) && + ( i1->lenTautomer != i2->lenTautomer || + memcmp( i1->nTautomer, i2->nTautomer, i1->lenTautomer*sizeof(i1->nTautomer[0]) ) ) ) + ret |= IDIF_TG; + + if ( i1->nNumberOfIsotopicAtoms != i2->nNumberOfIsotopicAtoms ) { + ret |= IDIF_NUM_ISO_AT; + } else + if ( i1->nNumberOfIsotopicAtoms > 0 && memcmp( i1->IsotopicAtom, i2->IsotopicAtom, i1->nNumberOfIsotopicAtoms*sizeof(i1->IsotopicAtom[0]) ) ) + ret |= IDIF_ISO_AT; + if ( i1->nTotalCharge != i2->nTotalCharge ) + ret |= IDIF_CHARGE; + if ( a1 && a1->nNumRemovedProtons && (!a2 || a2->nNumRemovedProtons != a1->nNumRemovedProtons) ) { + ret |= IDIF_REM_PROT; + } + if ( a1 && (!a2 || + a2->nNumRemovedIsotopicH[0] != a1->nNumRemovedIsotopicH[0] || + a2->nNumRemovedIsotopicH[1] != a1->nNumRemovedIsotopicH[1] || + a2->nNumRemovedIsotopicH[2] != a1->nNumRemovedIsotopicH[2]) ) { + ret |= IDIF_REM_ISO_H; + } + +/* + if ( i1->nPossibleLocationsOfIsotopicH && i2->nPossibleLocationsOfIsotopicH ) { + if ( i1->nPossibleLocationsOfIsotopicH[0] != i2->nPossibleLocationsOfIsotopicH[0] || + memcmp(i1->nPossibleLocationsOfIsotopicH, i2->nPossibleLocationsOfIsotopicH, + sizeof(i1->nPossibleLocationsOfIsotopicH[0])*i1->nPossibleLocationsOfIsotopicH[0]) ) + return 18; + } else + if ( !i1->nPossibleLocationsOfIsotopicH != !i2->nPossibleLocationsOfIsotopicH ) { + return 19; + } +*/ + if ( i1->StereoIsotopic && + i1->StereoIsotopic->nNumberOfStereoBonds + i1->StereoIsotopic->nNumberOfStereoCenters ) { + Stereo1 = i1->StereoIsotopic; + } else { + Stereo1 = i1->Stereo; + } + if ( i2->StereoIsotopic && + i2->StereoIsotopic->nNumberOfStereoBonds + i2->StereoIsotopic->nNumberOfStereoCenters ) { + Stereo2 = i2->StereoIsotopic; + } else { + Stereo2 = i2->Stereo; + } + ret |= CompareReversedStereoINChI2( Stereo1, Stereo2, picr ); + +exit_function: + + picr->flags = ret; + + return ret; +} +#endif /* } READ_INCHI_STRING */ + +/* + Create_INChI( ... ) + +*/ +int Create_INChI( CANON_GLOBALS *pCG, + INCHI_CLOCK *ic, + INChI **ppINChI, + INChI_Aux **ppINChI_Aux, + ORIG_ATOM_DATA *orig_inp_data, + inp_ATOM *inp_at, + INP_ATOM_DATA *out_norm_data[2], + int num_inp_at, + INCHI_MODE nUserMode, + int LargeMolecules, + int Polymers, + INCHI_MODE *pbTautFlags, + INCHI_MODE *pbTautFlagsDone, + struct tagInchiTime *ulMaxTime, + T_GROUP_INFO *ti_out, + char *pStrErrStruct) +{ +/* +#define NON_TAUT 0 +#define TAUT 1 +*/ + int nebend = 0, *ebend = NULL; + + sp_ATOM *at[TAUT_NUM]; /* at[0]=>non-tautomeric, at[1]=>tautomeric */ + int i, n1, n2, num_atoms, num_at_tg, num_removed_H, num_removed_H_taut=0, ret=0, ret2=0; + INCHI_MODE nMode=0; + T_GROUP_INFO vt_group_info; + T_GROUP_INFO vt_group_info_orig; + T_GROUP_INFO * /*const*/ t_group_info = &vt_group_info; + T_GROUP_INFO * /*const*/ t_group_info_orig = &vt_group_info_orig; + + CANON_STAT CS, CS2; + CANON_STAT *pCS = &CS; + CANON_STAT *pCS2 = &CS2; /* save all allocations to avoid memory leaks in case Canon_INChI() removes the pointer */ + + ATOM_SIZES s[TAUT_NUM]; + + BCN Bcn; + BCN *pBCN = &Bcn; + + int bHasIsotopicAtoms = 0; + int bMayHaveStereo = 0; + int num_taut_at = 0; + + inp_ATOM *out_at = NULL; /*, *norm_at_fixed_bonds[TAUT_NUM]; */ /* = {out_norm_nontaut_at, out_norm_taut_at} ; */ + INChI *pINChI=NULL; /* added initialization 2006-03 */ + INChI_Aux *pINChI_Aux=NULL; /* added initialization 2006-03 */ + int bPointedEdgeStereo = ((TG_FLAG_POINTED_EDGE_STEREO & *pbTautFlags)? PES_BIT_POINT_EDGE_STEREO:0) + | ((TG_FLAG_PHOSPHINE_STEREO & *pbTautFlags)? PES_BIT_PHOSPHINE_STEREO :0) + | ((TG_FLAG_ARSINE_STEREO & *pbTautFlags)? PES_BIT_ARSINE_STEREO :0) + | ((TG_FLAG_FIX_SP3_BUG & *pbTautFlags)? PES_BIT_FIX_SP3_BUG :0); + INCHI_MODE bTautFlags = (*pbTautFlags & (~(INCHI_MODE)TG_FLAG_ALL_TAUTOMERIC) ); + INCHI_MODE bTautFlagsDone = (*pbTautFlagsDone /*& (~(INCHI_MODE)TG_FLAG_ALL_TAUTOMERIC) */); +#if ( bRELEASE_VERSION == 0 ) + int bExtract = 0; /* EXTR_HAS_ATOM_WITH_DEFINED_PARITY; */ +#endif + +/* */ + int bFixIsoFixedH = 0; + int bFixTermHChrg = 0; + + /* vABParityUnknown holds actual value of an internal constant signifying + unknown parity: either the same as for undefined parity (default==standard) + or a specific one (non-std; requested by SLUUD switch). */ + int vABParityUnknown = AB_PARITY_UNDF; + if ( 0 != ( nUserMode & REQ_MODE_DIFF_UU_STEREO) ) + { + /* Make labels for unknown and undefined stereo different */ + vABParityUnknown = AB_PARITY_UNKN; + } + +#if ( FIX_ISO_FIXEDH_BUG == 1 ) + if (TG_FLAG_FIX_ISO_FIXEDH_BUG & *pbTautFlags) + bFixIsoFixedH = 1; +#endif +#if ( FIX_TERM_H_CHRG_BUG == 1 ) + if (TG_FLAG_FIX_TERM_H_CHRG_BUG & *pbTautFlags) + bFixTermHChrg = 1; +#endif + +#ifdef FIX_SRU_CYCLIZING_PS_BONDS_IN_BNS + /* Polymer related */ + if ( orig_inp_data && orig_inp_data->polymer && orig_inp_data->polymer->n > 0 && orig_inp_data->polymer->valid ) + { + int j, jj; + nebend = 0; + for (j=0; jpolymer->n; j++) + if ( orig_inp_data->polymer->units[j]->already_closed == 1 ) + nebend++; + if ( nebend ) + { + nebend*= 2; + ebend = inchi_calloc( 2*nebend, sizeof(int) ); + if ( !ebend ) + { ret = CT_OUT_OF_RAM; goto exit_function; } + jj = 0; + for (j=0; jpolymer->n; j++) + { + if ( orig_inp_data->polymer->units[j]->already_closed == 1 ) + { + ebend[jj] = orig_inp_data->polymer->units[j]->star_partner1; + ebend[jj+1] = orig_inp_data->polymer->units[j]->star_partner2; + jj+=2; + } + } + } + } +#endif + + memset( s, 0, sizeof(s) ); + if ( pBCN ) + { + memset( pBCN, 0, sizeof( pBCN[0] ) ); + } + memset( t_group_info, 0, sizeof(*t_group_info) ); + memset( t_group_info_orig, 0, sizeof(*t_group_info_orig) ); + /*norm_at[TAUT_NON] = out_norm_data[TAUT_NON]->at; *//* output normalized non-tautomeric component */ + /*norm_at[TAUT_YES] = out_norm_data[TAUT_YES]->at; *//* output normalized tautomeric component */ + /*norm_at_fixed_bonds[TAUT_NON] = NULL;*/ + /*norm_at_fixed_bonds[TAUT_YES] = out_norm_data[TAUT_YES]->at_fixed_bonds;*/ + for ( i = 0; i < TAUT_NUM; i ++ ) + { + if ( out_norm_data[i]->at ) + { + if ( !(at[i] = (sp_ATOM *) inchi_malloc( num_inp_at * sizeof(*at[0]) ) ) ) + { + ret = -1; + } + } + else + { + at[i] = NULL; + } + } + if ( !out_norm_data[TAUT_NON]->at && !out_norm_data[TAUT_YES]->at + || !inp_at || ret ) + { + ret = -1; + goto exit_function; + } + + /* the first struct to process: tautomeric if exists else non-tautomeric */ + out_at = out_norm_data[TAUT_YES]->at? out_norm_data[TAUT_YES]->at : out_norm_data[TAUT_NON]->at; + /* copy the input structure to be normalized to the buffer for the normalization data */ + memcpy( out_at, inp_at, num_inp_at*sizeof(out_at[0]) ); + + /* tautomeric groups setting */ + t_group_info->bIgnoreIsotopic = 0; /* include tautomeric group isotopic info in MarkTautomerGroups() */ + t_group_info->bTautFlags = *pbTautFlags; + t_group_info->bTautFlagsDone = *pbTautFlagsDone; + + /* + Preprocess the structure + (here THE NUMBER OF ATOMS MAY BE REDUCED) + */ + + /* ??? Ambiguity: H-D may become HD or DH + (that is, H+implicit D or D+implicit H) */ + if ( TG_FLAG_H_ALREADY_REMOVED & bTautFlags ) + { + INP_ATOM_DATA *out_norm_data1 = out_norm_data[TAUT_YES]->at? out_norm_data[TAUT_YES] : + out_norm_data[TAUT_NON]->at? out_norm_data[TAUT_NON] : NULL; + if ( out_norm_data1 ) + { + num_at_tg = + num_atoms = out_norm_data1->num_at - out_norm_data1->num_removed_H; + num_removed_H = out_norm_data1->num_removed_H; + t_group_info->tni.nNumRemovedExplicitH = num_removed_H; + } + else + { + ret = -1; + goto exit_function; + } + } + else + { + num_at_tg = + num_atoms = remove_terminal_HDT( num_inp_at, out_at, bFixTermHChrg ); + num_removed_H = num_inp_at - num_atoms; + t_group_info->tni.nNumRemovedExplicitH = num_removed_H; + add_DT_to_num_H( num_atoms, out_at ); + } + /*fix_odd_things( num_atoms, out_at );*/ +#if ( FIND_RING_SYSTEMS == 1 ) + MarkRingSystemsInp( out_at, num_atoms, 0 ); +#endif + /* duplicate the preprocessed structure so that all supplied out_norm_data[]->at buffers are filled */ + if ( out_at != out_norm_data[TAUT_YES]->at && out_norm_data[TAUT_YES]->at ) { + memcpy( out_norm_data[TAUT_YES]->at, out_at, num_inp_at*sizeof(out_at[0]) ); + } + if ( out_norm_data[TAUT_YES]->at_fixed_bonds && out_norm_data[TAUT_YES]->at ) { + memcpy( out_norm_data[TAUT_YES]->at_fixed_bonds, out_at, num_inp_at*sizeof(out_at[0]) ); + } + if ( out_at != out_norm_data[TAUT_NON]->at && out_norm_data[TAUT_NON]->at ) { + memcpy( out_norm_data[TAUT_NON]->at, out_at, num_inp_at*sizeof(out_at[0]) ); + } + + /* + ??? not true ??? duplicate inp_at and keep inp_at[] unchanged after terminal hydrogens removal + set stereo parities in taut_at[], non_taut_at[] + obtain max. lenghts of the name stereo parts + Ignore absence/presence of isotopic stereo for now + mark isotopic atoms + */ + if ( out_norm_data[TAUT_YES]->at && at[TAUT_YES] ) + { + /* final normalization of possibly tautomeric structure */ + ret = mark_alt_bonds_and_taut_groups( ic, pCG, + out_norm_data[TAUT_YES]->at, + out_norm_data[TAUT_YES]->at_fixed_bonds, + num_atoms, + ulMaxTime, + t_group_info, + NULL, NULL, + nebend, ebend ); + if ( ret < 0 ) + { + goto exit_function;/* out of RAM or other normalization problem */ + } + num_taut_at = ret; /* number of atoms without removed H? */ + num_removed_H_taut = t_group_info->tni.nNumRemovedExplicitH; + out_norm_data[TAUT_YES]->num_at = num_atoms + num_removed_H_taut; /* protons might have been removed */ + out_norm_data[TAUT_YES]->num_removed_H = num_removed_H_taut; + out_norm_data[TAUT_YES]->nNumRemovedProtons += t_group_info->tni.nNumRemovedProtons; + for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) + { + out_norm_data[TAUT_YES]->nNumRemovedProtonsIsotopic[i] += + t_group_info->tni.nNumRemovedProtonsIsotopic[i] /*+ t_group_info->num_iso_H[i]*/; + out_norm_data[TAUT_YES]->num_iso_H[i] += + t_group_info->num_iso_H[i]; + } + /* mark deleted isolated tautomeric H(+) */ + if ( num_taut_at == 1 && + out_norm_data[TAUT_YES]->at[0].at_type == ATT_PROTON && + t_group_info && t_group_info->tni.nNumRemovedProtons == 1 ) + { + out_norm_data[TAUT_YES]->bDeleted = 1; + FreeInpAtom( &out_norm_data[TAUT_YES]->at_fixed_bonds ); + } + else + if ( (t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) && + out_norm_data[TAUT_YES]->at_fixed_bonds) + { + out_norm_data[TAUT_YES]->bTautPreprocessed = 1; + } + + /* + if ( !(t_group_info->tni.bNormalizationFlags & (FLAG_NORM_CONSIDER_TAUT & ~FLAG_PROTON_SINGLE_REMOVED)) && + out_norm_data[TAUT_YES]->at_fixed_bonds) { + FreeInpAtom( &out_norm_data[TAUT_YES]->at_fixed_bonds ); + } + */ + /*out_norm_data[TAUT_YES]->num_removed_H = num_removed_H_taut;*/ + + out_norm_data[TAUT_YES]->bTautFlags = *pbTautFlags = t_group_info->bTautFlags; + out_norm_data[TAUT_YES]->bTautFlagsDone = *pbTautFlagsDone = t_group_info->bTautFlagsDone; + out_norm_data[TAUT_YES]->bNormalizationFlags = t_group_info->tni.bNormalizationFlags; + + /* create internal sp_ATOM at[] out of out_norm_data[]->at */ + inp2spATOM( out_norm_data[TAUT_YES]->at, num_inp_at, at[TAUT_YES] ); + + /* set stereo parities to at[]; nUserMode: accept alt. stereo bonds, min ring size */ + ret = set_stereo_parity( pCG, + out_norm_data[TAUT_YES]->at, at[TAUT_YES], + num_taut_at, num_removed_H_taut, + &s[TAUT_YES].nMaxNumStereoAtoms, + &s[TAUT_YES].nMaxNumStereoBonds, + nUserMode, bPointedEdgeStereo, vABParityUnknown ); + +#if ( bRELEASE_VERSION == 0 ) + if ( 0 < ret ) { + bExtract |= EXTR_HAS_ATOM_WITH_DEFINED_PARITY; + } + if ( t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT ) { + bExtract |= EXTR_TAUT_TREATMENT_CHARGES; + } +#endif + + if ( RETURNED_ERROR( ret ) ) + { + goto exit_function; /* stereo bond error */ + } + + s[TAUT_YES].bMayHaveStereo = + (s[TAUT_YES].nMaxNumStereoAtoms || + s[TAUT_YES].nMaxNumStereoBonds); + /* + mark isotopic atoms and atoms that have non-tautomeric + isotopic terminal hydrogen atoms 1H, 2H(D), 3H(T) + */ + s[TAUT_YES].num_isotopic_atoms = + set_atom_iso_sort_keys( num_taut_at, + at[TAUT_YES], + t_group_info, + &s[TAUT_YES].bHasIsotopicTautGroups ); + + /* + Prepare tautomeric (if no tautomerism found then prepare non-tautomeric) + structure for canonicalizaton: + + remove t-groups that have no H, + remove charges from t-groups if requested + renumber t-groups and find final t_group_info->num_t_groups + add to t-groups lists of endpoints tgroup->nEndpointAtomNumber[] + calculate length of the t-group part of the connection table + */ + + s[TAUT_YES].nLenLinearCTTautomer = CountTautomerGroups( at[TAUT_YES], num_taut_at, t_group_info ); + + if ( RETURNED_ERROR(s[TAUT_YES].nLenLinearCTTautomer) ) + { + /* added error treatment 9-11-2003 */ + ret = s[TAUT_YES].nLenLinearCTTautomer; + goto exit_function; + /* error has happened; no breakpoint here + s[TAUT_YES].nLenLinearCTTautomer = 0; + */ + } + + else if ( s[TAUT_YES].nLenLinearCTTautomer > 0 ) + { + num_at_tg = num_taut_at+t_group_info->num_t_groups; + /* ??? -not true- create t_group_info_orig for multiple calls with atom renumbering */ + make_a_copy_of_t_group_info( t_group_info_orig /* dest*/, t_group_info /* source*/ ); + /* mark isotopic tautomer groups: calculate t_group->iWeight */ + s[TAUT_YES].nLenLinearCTIsotopicTautomer=set_tautomer_iso_sort_keys( t_group_info ); + if ( s[TAUT_YES].nLenLinearCTIsotopicTautomer < 0 ) + { + /* ??? -error cannot happen- error has happened; no breakpoint here */ + s[TAUT_YES].nLenLinearCTIsotopicTautomer = 0; + } + out_norm_data[TAUT_YES]->bTautomeric = s[TAUT_YES].nLenLinearCTTautomer; + } + + /* new variable: s[TAUT_YES].nLenCT introduced 7-22-2002 */ + GetCanonLengths( num_taut_at, at[TAUT_YES], &s[TAUT_YES], t_group_info ); + } + + if ( out_norm_data[TAUT_NON]->at && + out_norm_data[TAUT_YES]->at && + at[TAUT_NON] && + !s[TAUT_YES].nLenLinearCTTautomer ) + { + /* the structure is non-tautomeric: use tautomeric treatment results only for it */ + inchi_free( at[TAUT_NON] ); + at[TAUT_NON] = NULL; + } + + else if ( !out_norm_data[TAUT_NON]->at && + out_norm_data[TAUT_YES]->at && + !at[TAUT_NON] && + at[TAUT_YES] && + !s[TAUT_YES].nLenLinearCTTautomer ) + { + /* requested tautomeric; found non-tautomeric; it is located in out_norm_data[TAUT_YES]->at */ + out_norm_data[TAUT_YES]->bTautomeric = 0; + } + + else if ( out_norm_data[TAUT_NON]->at && at[TAUT_NON] ) + { + /* the structure needs non-tautomeric treatment: + final normalization of non-tautomeric structure */ + ret = mark_alt_bonds_and_taut_groups( ic, pCG, + out_norm_data[TAUT_NON]->at, + NULL, + num_atoms, + ulMaxTime, + NULL, + &bTautFlags, + &bTautFlagsDone, + nebend, ebend); + if ( ret < 0 ) { + goto exit_function; /* out of RAM or other normalization problem */ + } + out_norm_data[TAUT_NON]->num_at = num_atoms + num_removed_H; + out_norm_data[TAUT_NON]->num_removed_H = num_removed_H; + out_norm_data[TAUT_NON]->bTautFlags = *pbTautFlags; + out_norm_data[TAUT_NON]->bTautFlagsDone = *pbTautFlagsDone; + out_norm_data[TAUT_NON]->bNormalizationFlags = 0; + + /* create internal sp_ATOM at[] out of out_norm_data[]->at */ + inp2spATOM( out_norm_data[TAUT_NON]->at, num_inp_at, at[TAUT_NON] ); + + /* set stereo parities to at[]; nUserMode: accept alt. stereo bonds, min ring size */ + ret = set_stereo_parity( pCG, + out_norm_data[TAUT_NON]->at, at[TAUT_NON], num_atoms, num_removed_H, + &s[TAUT_NON].nMaxNumStereoAtoms, &s[TAUT_NON].nMaxNumStereoBonds, nUserMode, + bPointedEdgeStereo, vABParityUnknown ); +#if ( bRELEASE_VERSION == 0 ) + if ( 0 < ret ) { + bExtract |= EXTR_HAS_ATOM_WITH_DEFINED_PARITY; + } +#endif + if ( RETURNED_ERROR( ret ) ) + { + goto exit_function; /* stereo bond error */ + } + s[TAUT_NON].bMayHaveStereo = (s[TAUT_NON].nMaxNumStereoAtoms || + s[TAUT_NON].nMaxNumStereoBonds); + + /* + * mark isotopic atoms and atoms that have non-tautomeric + * isotopic terminal hydrogen atoms 1H, 2H(D), 3H(T) + */ + s[TAUT_NON].num_isotopic_atoms = set_atom_iso_sort_keys( num_atoms, at[TAUT_NON], NULL, NULL ); + GetCanonLengths( num_atoms, at[TAUT_NON], &s[TAUT_NON], NULL); + out_norm_data[TAUT_NON]->bTautomeric = 0; + } + + /* common */ + bMayHaveStereo = s[TAUT_YES].bMayHaveStereo || s[TAUT_NON].bMayHaveStereo; + bHasIsotopicAtoms = s[TAUT_NON].num_isotopic_atoms > 0 || s[TAUT_NON].bHasIsotopicTautGroups > 0 || + s[TAUT_YES].num_isotopic_atoms > 0 || s[TAUT_YES].bHasIsotopicTautGroups > 0 ; + if (bFixIsoFixedH) /* 2008-03-21 DT */ + bHasIsotopicAtoms = bHasIsotopicAtoms || + s[TAUT_YES].nLenLinearCTTautomer > 0 && t_group_info && + (0 < NUM_H_ISOTOPES && t_group_info->tni.nNumRemovedProtonsIsotopic[0] || + 1 < NUM_H_ISOTOPES && t_group_info->tni.nNumRemovedProtonsIsotopic[1] || + 2 < NUM_H_ISOTOPES && t_group_info->tni.nNumRemovedProtonsIsotopic[2]) ; + bHasIsotopicAtoms = bHasIsotopicAtoms || + s[TAUT_YES].nLenIsotopicEndpoints > 1 && t_group_info && + (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE|TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE)); + + /* default mode */ + if ( !(nUserMode & REQ_MODE_DEFAULT) ) + { + /* default */ + nUserMode |= REQ_MODE_DEFAULT; + } + + /* adjust the mode to the reality */ + if ( ( nUserMode & REQ_MODE_ISO ) && !bHasIsotopicAtoms ) + { + nUserMode ^= REQ_MODE_ISO; + nUserMode |= REQ_MODE_NON_ISO; /* at least one is needed */ + } + if ( (nUserMode & REQ_MODE_STEREO) && ( nUserMode & REQ_MODE_ISO ) ) + { + nUserMode |= REQ_MODE_ISO_STEREO; + } + if ( (nUserMode & REQ_MODE_STEREO) && !( nUserMode & REQ_MODE_NON_ISO ) ) + { + nUserMode ^= REQ_MODE_STEREO; + } + if ( !bMayHaveStereo ) + { + if ( nUserMode & REQ_MODE_STEREO ) + nUserMode ^= REQ_MODE_STEREO; + if ( nUserMode & REQ_MODE_ISO_STEREO ) + nUserMode ^= REQ_MODE_ISO_STEREO; + } + + if ( (nUserMode & REQ_MODE_BASIC) && + (!out_norm_data[TAUT_NON]->at || !ppINChI[TAUT_NON] || + !ppINChI_Aux[TAUT_NON] || !at[TAUT_NON]) ) + { + nUserMode ^= REQ_MODE_BASIC; + } + if ( (nUserMode & REQ_MODE_TAUT) && + (!out_norm_data[TAUT_YES]->at || !ppINChI[TAUT_YES] || + !ppINChI_Aux[TAUT_YES] || !at[TAUT_YES]) ) + { + nUserMode ^= REQ_MODE_TAUT; + } + + switch ((int)nUserMode & (REQ_MODE_BASIC | REQ_MODE_TAUT)) + { + case REQ_MODE_BASIC: + n1 = TAUT_NON; + n2 = TAUT_NON; + break; + case REQ_MODE_TAUT: + n1 = TAUT_YES; + n2 = TAUT_YES; + break; + case (REQ_MODE_BASIC | REQ_MODE_TAUT): + n1 = TAUT_NON; + n2 = TAUT_YES; + break; + default: + ret = -3; + goto exit_function; /* program error: inconsistent nUserMode or missing taut/non-taut allocation */ /* */ + } + + /* + Obtain all non-stereo canonical numberings + */ + + if ( (nUserMode & REQ_MODE_NON_ISO) && !(nUserMode & REQ_MODE_ISO) ) + { + /* added for special non-isotopic test mode 2004-10-04 */ + if ( t_group_info ) + { + t_group_info->bIgnoreIsotopic = 1; + if ( t_group_info->nIsotopicEndpointAtomNumber ) + { + t_group_info->nIsotopicEndpointAtomNumber[0] = inchi_min(1, t_group_info->nIsotopicEndpointAtomNumber[0]); + } + memset( t_group_info->num_iso_H, 0, sizeof(t_group_info->num_iso_H) ); + memset ( t_group_info->tni.nNumRemovedProtonsIsotopic, 0, sizeof(t_group_info->tni.nNumRemovedProtonsIsotopic)); + t_group_info->bTautFlagsDone &= ~(TG_FLAG_FOUND_ISOTOPIC_H_DONE|TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE); + } + for ( i = 0; i < TAUT_NUM; i ++ ) + { + s[i].bHasIsotopicTautGroups = 0; + s[i].bIgnoreIsotopic = 1; + s[i].nLenIsotopic = 0; + s[i].nLenIsotopicEndpoints = 0; + s[i].nLenLinearCTIsotopicTautomer = 0; + s[i].num_isotopic_atoms = 0; + } + bHasIsotopicAtoms = 0; + } + + ret = GetBaseCanonRanking( ic, num_atoms, num_at_tg, at, + t_group_info, s, pBCN, ulMaxTime, + pCG, bFixIsoFixedH, LargeMolecules); + + if ( ret < 0 ) { + goto exit_function; /* program error */ + } +#if ( bRELEASE_VERSION == 0 && FIND_CANON_NE_EQUITABLE == 1 ) + /* Debug only: find whether canonical equivalence is different from equitable partition */ + if ( bCanonIsFinerThanEquitablePartition( num_atoms, at[n1], pBCN->ftcn[TAUT_NON].nSymmRankCt ) ) { + bExtract |= EXTR_CANON_NE_EQUITABLE; + } +#endif + + /* added for special non-isotopic test mode 2004-10-04 */ + if ( !pBCN->ftcn[n1].PartitionCt.Rank ) { + n1 = ALT_TAUT(n1); + } + if ( !pBCN->ftcn[n2].PartitionCt.Rank ) { + n2 = ALT_TAUT(n2); + } + if ( n1 > n2 ) { + ret = CT_TAUCOUNT_ERR; + goto exit_function; /* program error */ + } + + /* + Obtain stereo canonical numberings + */ + + for ( i = n2; i >= n1 && !RETURNED_ERROR( ret ); i -- ) + { + memset( pCS, 0, sizeof(*pCS) ); + + switch( i ) + { + case TAUT_NON: + /* non-tautomeric */ + nMode = 0; + nMode = (s[i].nLenLinearCTTautomer == 0)? CANON_MODE_CT:CANON_MODE_TAUT; + nMode |= (bHasIsotopicAtoms && (nUserMode & REQ_MODE_ISO))? CANON_MODE_ISO:0; + nMode |= (s[TAUT_NON].bMayHaveStereo && (nUserMode & REQ_MODE_STEREO) )? CANON_MODE_STEREO:0; + nMode |= (bHasIsotopicAtoms && s[TAUT_NON].bMayHaveStereo && (nUserMode & REQ_MODE_ISO_STEREO))? CANON_MODE_ISO_STEREO:0; + nMode |= (nUserMode & REQ_MODE_NOEQ_STEREO )? CMODE_NOEQ_STEREO : 0; + nMode |= (nUserMode & REQ_MODE_REDNDNT_STEREO)? CMODE_REDNDNT_STEREO : 0; + nMode |= (nUserMode & REQ_MODE_NO_ALT_SBONDS )? CMODE_NO_ALT_SBONDS : 0; + if ( (nMode & CANON_MODE_STEREO) == CANON_MODE_STEREO || + (nMode & CANON_MODE_ISO_STEREO) == CANON_MODE_ISO_STEREO ) + { + nMode |= (nUserMode & REQ_MODE_RELATIVE_STEREO)? CMODE_RELATIVE_STEREO: 0; + nMode |= (nUserMode & REQ_MODE_RACEMIC_STEREO )? CMODE_RACEMIC_STEREO : 0; + nMode |= (nUserMode & REQ_MODE_SC_IGN_ALL_UU )? CMODE_SC_IGN_ALL_UU : 0; + nMode |= (nUserMode & REQ_MODE_SB_IGN_ALL_UU )? CMODE_SB_IGN_ALL_UU : 0; + } + if ( ret= AllocateCS( pCS, num_atoms, num_atoms, s[TAUT_NON].nLenCT, s[TAUT_NON].nLenCTAtOnly, + s[TAUT_NON].nLenLinearCTStereoDble, s[TAUT_NON].nMaxNumStereoBonds, + s[TAUT_NON].nLenLinearCTStereoCarb, s[TAUT_NON].nMaxNumStereoAtoms, + 0, 0, s[TAUT_NON].nLenIsotopic, nMode, pBCN ) ) + { + goto exit_function; + } + *pCS2 = *pCS; + break; + case TAUT_YES: /* tautomeric */ + nMode = 0; + nMode = (s[i].nLenLinearCTTautomer == 0)? CANON_MODE_CT:CANON_MODE_TAUT; + nMode |= (bHasIsotopicAtoms && (nUserMode & REQ_MODE_ISO) )? CANON_MODE_ISO:0; + nMode |= (s[TAUT_YES].bMayHaveStereo && (nUserMode & REQ_MODE_STEREO) )? CANON_MODE_STEREO:0; + nMode |= (bHasIsotopicAtoms && s[TAUT_YES].bMayHaveStereo && (nUserMode & REQ_MODE_ISO_STEREO))? CANON_MODE_ISO_STEREO:0; + nMode |= (nUserMode & REQ_MODE_NOEQ_STEREO )? CMODE_NOEQ_STEREO : 0; + nMode |= (nUserMode & REQ_MODE_REDNDNT_STEREO)? CMODE_REDNDNT_STEREO : 0; + nMode |= (nUserMode & REQ_MODE_NO_ALT_SBONDS )? CMODE_NO_ALT_SBONDS : 0; + if ( (nMode & CANON_MODE_STEREO) == CANON_MODE_STEREO || + (nMode & CANON_MODE_ISO_STEREO) == CANON_MODE_ISO_STEREO ) + { + nMode |= (nUserMode & REQ_MODE_RELATIVE_STEREO)? CMODE_RELATIVE_STEREO: 0; + nMode |= (nUserMode & REQ_MODE_RACEMIC_STEREO )? CMODE_RACEMIC_STEREO : 0; + nMode |= (nUserMode & REQ_MODE_SC_IGN_ALL_UU )? CMODE_SC_IGN_ALL_UU : 0; + nMode |= (nUserMode & REQ_MODE_SB_IGN_ALL_UU )? CMODE_SB_IGN_ALL_UU : 0; + } + if ( ret= AllocateCS( pCS, num_atoms, num_at_tg, s[TAUT_YES].nLenCT, s[TAUT_YES].nLenCTAtOnly, + s[TAUT_YES].nLenLinearCTStereoDble, s[TAUT_YES].nMaxNumStereoBonds, + s[TAUT_YES].nLenLinearCTStereoCarb, s[TAUT_YES].nMaxNumStereoAtoms, + s[TAUT_YES].nLenLinearCTTautomer, s[TAUT_YES].nLenLinearCTIsotopicTautomer, + s[TAUT_YES].nLenIsotopic, nMode, pBCN ) ) + { + goto exit_function; + } + *pCS2 = *pCS; + break; + } + + /* 2009-12-05 */ + nMode |= (nUserMode & REQ_MODE_DIFF_UU_STEREO)? REQ_MODE_DIFF_UU_STEREO : 0; + /* 2009-12-05 */ + + /* settings */ + pCS->lNumDecreasedCT = -1; + pCS->bDoubleBondSquare = DOUBLE_BOND_NEIGH_LIST? 2:0; /* 2 => special mode */ + pCS->bIgnoreIsotopic = !((s[TAUT_NON].num_isotopic_atoms || + s[TAUT_YES].num_isotopic_atoms || + s[TAUT_YES].bHasIsotopicTautGroups) || + (nUserMode & REQ_MODE_NON_ISO) || + !(nUserMode & REQ_MODE_ISO)); + + if ( (nUserMode & REQ_MODE_NON_ISO) && !(nUserMode & REQ_MODE_ISO) ) + { + pCS->bIgnoreIsotopic = 1; /* 10-04-2004 */ + } + + if ( i == TAUT_YES ) + { + /* tautomeric */ + pCS->t_group_info = t_group_info; /* ??? make a copy or reuse ??? */ + pCS->t_group_info->bIgnoreIsotopic = !(s[TAUT_YES].bHasIsotopicTautGroups || + (nUserMode & REQ_MODE_NON_ISO) || + !(nUserMode & REQ_MODE_ISO)); + if ( (nUserMode & REQ_MODE_NON_ISO) && !(nUserMode & REQ_MODE_ISO) ) { + pCS->t_group_info->bIgnoreIsotopic = 1; /* 10-04-2004 */ + } + } + + pCS->ulTimeOutTime = pBCN->ulTimeOutTime; + /*=========== Obsolete Mode Bits (bit 0 is Least Significant Bit) =========== + * + * Mode Bits Description + * '0' c 0 Only one connection table canonicalization + * '1' C 1 Recalculate CT using fixed nSymmRank + * '2' i 1|2 Isotopic canonicalization (internal) + * '3' I 1|2|4 Isotopic canonicalization (output) + * '4' s 1|8 Stereo canonicalization + * '5' S 1|2|4|16 Stereo isotopic canonicalization + * '6' A 1|2|4|8|16 Output All + */ + + /* + The last canonicalization step + */ + + if ( pBCN ) + { + /* USE_CANON2 == 1 */ + pCS->NeighList = NULL; + pCS->pBCN = pBCN; + + ret = Canon_INChI( ic, + num_atoms, + i ? num_at_tg : num_atoms, + at[i], pCS, + pCG, + nMode, i); + } + else + { + /* old way */ + pCS->NeighList = CreateNeighList( num_atoms, + i?num_at_tg:num_atoms, + at[i], + pCS->bDoubleBondSquare, + pCS->t_group_info ); + pCS->pBCN = NULL; + + ret = Canon_INChI( ic, + num_atoms, + i ? num_at_tg : num_atoms, + at[i], pCS, + pCG, + nMode, i); + } + + pINChI = ppINChI[i]; /* pointers to already allocated still empty InChI */ + pINChI_Aux = ppINChI_Aux[i]; + + if ( ret <= 0 ) + { + /* + Failure in Canon_INChI() + */ + pINChI->nErrorCode = ret; + pINChI_Aux->nErrorCode = ret; + } + else + { + /* + Success Canon_INChI() + + save canonicalization results in + pINChI and pINChI_Aux + */ + pINChI->nErrorCode = 0; + pINChI_Aux->nErrorCode = 0; + pINChI->bDeleted = pINChI_Aux->bDeleted = out_norm_data[i]->bDeleted; + pINChI_Aux->nCanonFlags = pCS->nCanonFlags; + pINChI_Aux->bTautFlags = out_norm_data[i]->bTautFlags; + pINChI_Aux->bTautFlagsDone = out_norm_data[i]->bTautFlagsDone; + pINChI_Aux->bNormalizationFlags = out_norm_data[i]->bNormalizationFlags; + + /* may return an error or a warning */ + ret = FillOutINChI( pINChI, pINChI_Aux, + num_atoms, i?num_at_tg:num_atoms, + i ? num_removed_H_taut : num_removed_H, at[i], + out_norm_data[i]->at, pCS, + pCG, + i, nUserMode, + pStrErrStruct ); + + if ( RETURNED_ERROR( ret ) ) + { + /* Failure in FillOutINChI() */ + pINChI->nErrorCode = ret; + pINChI_Aux->nErrorCode = ret; + } + else + { + /* Success in FillOutINChI() */ + +#if ( bRELEASE_VERSION == 0 ) + if ( pINChI->Stereo && + (pINChI->Stereo->nCompInv2Abs && !pINChI->Stereo->bTrivialInv) || + pINChI->StereoIsotopic && + (pINChI->StereoIsotopic->nCompInv2Abs && !pINChI->StereoIsotopic->bTrivialInv) ) { + bExtract |= EXTR_NON_TRIVIAL_STEREO; + } +#endif + /* Mark non-tautomeric representation as having + another, tautomeric representation */ + if ( pINChI_Aux && s[TAUT_YES].nLenLinearCTTautomer ) + { + pINChI_Aux->bIsTautomeric = s[TAUT_YES].nLenLinearCTTautomer; + } +#if ( bRELEASE_VERSION == 0 ) + pCS->bExtract |= bExtract; + pINChI->bExtract |= pCS->bExtract; +#endif + + ret2 = CheckCanonNumberingCorrectness(num_atoms, + i ? num_at_tg : num_atoms, + at[i], pCS, + pCG, + i, pStrErrStruct ); + if ( ret2 ) + { + pINChI->nErrorCode = ret2; + pINChI_Aux->nErrorCode = ret2; + ret = ret2; + } + } + } + + FreeNeighList( pCS->NeighList ); + DeAllocateCS( pCS2 ); + + pINChI = NULL; /* avoid dangling pointers */ + pINChI_Aux = NULL; /* avoid dangling pointers */ + } + + if ( ret == 0 ) + { + ret = num_atoms; + } + /* treat the results later */ + +exit_function: + + DeAllocBCN( pBCN ); + if ( at[TAUT_YES] ) + inchi_free( at[TAUT_YES] ); + if ( at[TAUT_NON] ) + inchi_free( at[TAUT_NON] ); + if ( ti_out ) + { + *ti_out = *t_group_info; + } else + { + free_t_group_info( t_group_info ); + } + free_t_group_info( t_group_info_orig ); + + if ( ebend ) + inchi_free( ebend ); + return ret; +} + +#ifndef COMPILE_ANSI_ONLY /* { */ +int GetAtomOrdNbrInCanonOrd( struct tagCANON_GLOBALS *pCG, + inp_ATOM *norm_at, + AT_NUMB *nAtomOrdNbr, + AT_NUMB *nOrigAtNosInCanonOrd, + int num_at ) +{ + AT_NUMB *nCanonNbr, *nOrigAtNos, *nOrigAtNosOrd; + int i, ret; + + ret = 0; + + nCanonNbr = (AT_NUMB *)inchi_calloc( num_at, sizeof(nCanonNbr[0]) ); + nOrigAtNos = (AT_NUMB *)inchi_calloc( num_at, sizeof(nOrigAtNos[0]) ); + nOrigAtNosOrd = (AT_NUMB *)inchi_calloc( num_at, sizeof(nOrigAtNosOrd[0]) ); + + if ( !nCanonNbr || !nOrigAtNos || !nAtomOrdNbr || !nOrigAtNosOrd ) { + ret = CT_OUT_OF_RAM; /* */ + goto exit_function; + } + for ( i = 0; i < num_at; i ++ ) { + nCanonNbr[i] = nAtomOrdNbr[i] = nOrigAtNosOrd[i] = (AT_NUMB)i; + nOrigAtNos[i] = norm_at[i].orig_at_number; + } + + /* get nCanonNbr[]: canon. numbers-1 in order of increasing original atom numbers */ + pCG->m_pn_RankForSort=nOrigAtNosInCanonOrd; + inchi_qsort( pCG, nCanonNbr, num_at, sizeof(nCanonNbr[0]), CompRank ); + /* get nOrigAtNosOrd[]: norm_atom ord. numbers the same order of increasing original atom numbers */ + pCG->m_pn_RankForSort=nOrigAtNos; + inchi_qsort( pCG, nOrigAtNosOrd, num_at, sizeof(nOrigAtNosOrd[0]), CompRank ); + + /* check whether the 2 sets of origiginal atom numbers have identical elements */ + for ( i = 0; i < num_at; i ++ ) { + if ( nOrigAtNosInCanonOrd[nCanonNbr[i]] != nOrigAtNos[nOrigAtNosOrd[i]] ) { + ret = CT_RANKING_ERR; /* */ + goto exit_function; + } + } + for ( i = 0; i < num_at; i ++ ) { + nAtomOrdNbr[(int)nCanonNbr[i]] = nOrigAtNosOrd[i]; + } + +/* + pn_RankForSort = nCanonNbr; + qsort( nAtomOrdNbr, num_at, sizeof(nCanonNbr[0]), CompRank ); +*/ + +exit_function: + if ( nCanonNbr ) + inchi_free( nCanonNbr ); + if ( nOrigAtNos ) + inchi_free( nOrigAtNos ); + if ( nOrigAtNosOrd ) + inchi_free( nOrigAtNosOrd ); + return ret; +} +/***************************************************************************************/ +int FillOutCanonInfAtom( struct tagCANON_GLOBALS *pCG, + inp_ATOM *norm_at, INF_ATOM_DATA *inf_norm_at_data, + int init_num_at, int bIsotopic, + INChI *pINChI, INChI_Aux *pINChI_Aux, + int bAbcNumbers, INCHI_MODE nMode) +{ + int i, j, m, n, num_stereo, k, c, ret, len_str, len, atw, num_err; + int next_atom[MAX_CUMULENE_LEN+1], best_next_atom[MAX_CUMULENE_LEN+1], cur_atom; + int next_neigh[MAX_CUMULENE_LEN+1], best_next_neigh[MAX_CUMULENE_LEN+1], best_len; + int num_iso_H[NUM_H_ISOTOPES]; + char *str; + AT_NUMB g, e; + int num_at = pINChI->nNumberOfAtoms; + int nNumberOfTGroups = (pINChI->lenTautomer && pINChI->nTautomer && pINChI->nTautomer[0])? (int)pINChI->nTautomer[0] : 0; + AT_NUMB *nOrigAtNosInCanonOrd; + INChI_Stereo *Stereo; + AT_NUMB *nConstitEquNumbers; + AT_NUMB *nConstitEquTGroupNumbers; + S_CHAR *t_parity = NULL; + AT_NUMB *nNumber = NULL; + int bIncludeIsotopicH; + + AT_NUMB *nNormAtNosInCanonOrd; + int (*MakeNumber)(char*, int, const char*, int) = bAbcNumbers? MakeAbcNumber:MakeDecNumber; + int bRel= (0 != (nMode & ( REQ_MODE_RELATIVE_STEREO))); + int bRac= (0 != (nMode & ( REQ_MODE_RACEMIC_STEREO))); + int bRelRac= bRel || bRac; + int bDoDisplaySp3 = 1; + + inf_ATOM *inf_norm_at = inf_norm_at_data? inf_norm_at_data->at : NULL; + + ret = 0; + num_err = 0; + + if ( !inf_norm_at ) + return ret; + /* prepare removeable protons and H info */ + inf_norm_at_data->nNumRemovedProtons = pINChI_Aux->nNumRemovedProtons; + MakeRemovedProtonsString( pINChI_Aux->nNumRemovedProtons, pINChI_Aux->nNumRemovedIsotopicH, NULL, bIsotopic, + inf_norm_at_data->szRemovedProtons, &inf_norm_at_data->num_removed_iso_H ); + /* fill out info atom */ + if ( bIsotopic && !(pINChI->nNumberOfIsotopicAtoms || pINChI->nNumberOfIsotopicTGroups || + pINChI->nPossibleLocationsOfIsotopicH && pINChI->nPossibleLocationsOfIsotopicH[0]>1) ) + bIsotopic = 0; + + Stereo = bIsotopic? pINChI->StereoIsotopic : + pINChI->Stereo; + bDoDisplaySp3 = (NULL != Stereo) && (Stereo->nNumberOfStereoCenters > 0); + +#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) + if ( bDoDisplaySp3 && bRelRac && Stereo->nNumberOfStereoCenters < 2 && + (Stereo->nCompInv2Abs || ATOM_PARITY_ILL_DEF(Stereo->t_parity[0]) ) ) { + bDoDisplaySp3 = 0; + if ( Stereo->nCompInv2Abs ) { + inf_norm_at_data->StereoFlags |= bRel? INF_STEREO_REL : bRac? INF_STEREO_RAC : 0; + } + } +#endif + /* flag has stereo */ + if ( (NULL != Stereo) && (bDoDisplaySp3 || Stereo->nNumberOfStereoBonds > 0) ) { + inf_norm_at_data->StereoFlags |= INF_STEREO; + } + + /* + if ( bDoDisplaySp3 && bRelRac && Stereo->nNumberOfStereoCenters < 2 && + (Stereo->nCompInv2Abs || ATOM_PARITY_ILL_DEF(Stereo->t_parity[0]) ) ) { + bDoDisplaySp3 = 0; + } + */ + if ( bDoDisplaySp3 && Stereo->nCompInv2Abs ) { + /* inversion changes stereo */ + if ( bRel ) { + inf_norm_at_data->StereoFlags |= INF_STEREO_REL; + } else + if ( bRac ) { + inf_norm_at_data->StereoFlags |= INF_STEREO_RAC; + } else { + inf_norm_at_data->StereoFlags |= INF_STEREO_ABS; + } + if ( bRelRac ) { + inf_norm_at_data->StereoFlags |= (Stereo->nCompInv2Abs > 0)? INF_STEREO_NORM : INF_STEREO_INV; + } + } + if ( bDoDisplaySp3 && Stereo->nCompInv2Abs < 0 && !bRelRac ) { + /* display Inv stereo which is Absolute Stereo */ + nNumber = Stereo->nNumberInv; + t_parity = Stereo->t_parityInv; + nOrigAtNosInCanonOrd = bIsotopic? pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv : + pINChI_Aux->nOrigAtNosInCanonOrdInv; + } else { + /* display Inv stereo which is Absolute Stereo */ + if ( bDoDisplaySp3 ) { + nNumber = Stereo->nNumber; + t_parity = Stereo->t_parity; + } + nOrigAtNosInCanonOrd = bIsotopic? pINChI_Aux->nIsotopicOrigAtNosInCanonOrd : + pINChI_Aux->nOrigAtNosInCanonOrd; + } + + nConstitEquNumbers = bIsotopic? pINChI_Aux->nConstitEquIsotopicNumbers : + pINChI_Aux->nConstitEquNumbers; + nConstitEquTGroupNumbers = bIsotopic? pINChI_Aux->nConstitEquIsotopicTGroupNumbers : + pINChI_Aux->nConstitEquTGroupNumbers; + memset( inf_norm_at, 0, init_num_at*sizeof(inf_norm_at[0]) ); + + /* obtain norm_at[] atom numbers (from zero) in order of canonical numbers */ + nNormAtNosInCanonOrd = (AT_NUMB *)inchi_calloc( num_at, sizeof(nNormAtNosInCanonOrd[0]) ); + if ( ret = GetAtomOrdNbrInCanonOrd( pCG, norm_at, nNormAtNosInCanonOrd, nOrigAtNosInCanonOrd, num_at ) ) { + goto exit_function; + } + + /* atom canonical and equivalence numbers > 0 */ + for ( i = 0; i < num_at; i ++ ) { + j = (int)nNormAtNosInCanonOrd[i]; + if ( j < 0 || j >= num_at ) + continue; + inf_norm_at[j].nCanonNbr = (AT_NUMB)(i+1); + inf_norm_at[j].nCanonEquNbr = nConstitEquNumbers[i]; +#ifdef DISPLAY_DEBUG_DATA + inf_norm_at[j].nDebugData = 0; +#if ( DISPLAY_DEBUG_DATA == DISPLAY_DEBUG_DATA_C_POINT ) + inf_norm_at[j].nDebugData = norm_at[j].c_point; +#endif +#endif + } + /* tautomeric groups */ + if ( nNumberOfTGroups ) { + /* + : start from 1: bypass number of t-groups + : j is a counter within the current t-group + : g is a tautomeric group canonical number + : e is a tautomeric group equivalence + */ + for ( g = 1, i = 1; g <= nNumberOfTGroups; g ++ ) { + n = (int)pINChI->nTautomer[i] - INCHI_T_NUM_MOVABLE; /* number of atoms in t-group */ + e = nConstitEquTGroupNumbers[(int)g - 1]; + /* bypass number of hydrogen atoms, negative charges, ... */ + for ( i += INCHI_T_NUM_MOVABLE+1, j = 0; j < n && i < pINChI->lenTautomer; j ++, i ++ ) { + /* scan canonical numbers of atoms within the atom t-group */ + k = (int)nNormAtNosInCanonOrd[(int)pINChI->nTautomer[i]-1]; + inf_norm_at[k].nTautGroupCanonNbr = g; + inf_norm_at[k].nTautGroupEquNbr = e; + } + } + if ( i != pINChI->lenTautomer || g != nNumberOfTGroups+1 ) { + ret = CT_TAUCOUNT_ERR; /* */ + goto exit_function; + } + } + /* atoms that may exchange isotopic H */ + if ( bIsotopic && pINChI->nPossibleLocationsOfIsotopicH && (n = (int)pINChI->nPossibleLocationsOfIsotopicH[0]) ) { + for ( i = 1; i < n; i ++ ) { + j = (int)pINChI->nPossibleLocationsOfIsotopicH[i]; + k = (int)nNormAtNosInCanonOrd[j - 1]; + if ( !inf_norm_at[k].nTautGroupCanonNbr ) { + inf_norm_at[k].cFlags |= AT_FLAG_ISO_H_POINT; + } + } + } + +#if ( DISPLAY_RING_SYSTEMS == 1 ) + /* debug only */ + for ( j = 0; j < num_at; j ++ ) { + inf_norm_at[j].nCanonNbr = norm_at[j].nBlockSystem; + inf_norm_at[j].nCanonEquNbr = norm_at[j].nRingSystem; +#if ( USE_DISTANCES_FOR_RANKING == 1 ) + inf_norm_at[j].nTautGroupCanonNbr = norm_at[j].nDistanceFromTerminal; + inf_norm_at[j].nTautGroupEquNbr = norm_at[j].bCutVertex; +#else + inf_norm_at[j].nTautGroupCanonNbr = norm_at[j].bCutVertex; + inf_norm_at[j].nTautGroupEquNbr = 0; +#endif + } +#endif + + /* Write isotopic mass, chemical element symbols and hydrogens, charge, radical, canon. numbers */ + len_str = sizeof(inf_norm_at[0].at_string); + for ( i = 0; i < init_num_at; i ++ ) { + str = inf_norm_at[i].at_string; + len = 0; + bIncludeIsotopicH = bIsotopic && !inf_norm_at[i].nTautGroupCanonNbr && !(inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT); + /* isotopic mass */ + atw = 0; + if ( norm_at[i].iso_atw_diff && bIsotopic ) { + if ( norm_at[i].at_type == ATT_PROTON ) { + ; /* do not set isotopic mass of a tautomeric proton */ + } else + if ( norm_at[i].el_number == PERIODIC_NUMBER_H && norm_at[i].chem_bonds_valence == 1 && + !norm_at[i].charge && !norm_at[i].radical && !norm_at[i].num_H && + (inf_norm_at[j=(int)norm_at[i].neighbor[0]].nTautGroupCanonNbr || (inf_norm_at[j].cFlags & AT_FLAG_ISO_H_POINT) ) ) { + ; /* do not set isotopic mass of an exchangeable proton */ + } else { + atw = get_atomic_mass(norm_at[i].elname); + atw += (norm_at[i].iso_atw_diff>0)? norm_at[i].iso_atw_diff-1:norm_at[i].iso_atw_diff; + /*len += sprintf( str+len, "^%d", atw );*/ + } + } + /* element name */ + if ( norm_at[i].el_number == PERIODIC_NUMBER_H && 2 <= atw && atw <= 3 ) { + len += sprintf( str+len, "%s", atw==2? "D" : "T" ); + } else { + if ( atw ) { + len += sprintf( str+len, "^%d", atw ); + } + if ( strcmp(norm_at[i].elname, "Zz") ) + len += sprintf( str+len, "%s", norm_at[i].elname ); + else + len += sprintf( str+len, "*" ); + } + /* hydrogens */ + /* find number of previuosly removed terminal hydrogen atoms because these terminal H will be displayed */ + for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { + num_iso_H[j] = norm_at[i].num_iso_H[j]; + } + /* n = number of implicit H to display */ + for ( j = num_at, n = (int)norm_at[i].num_H; j < init_num_at; j ++ ) { + /* subtract number of removed terminal */ + /* H atoms from the total number of H atoms */ + if ( i == (int)norm_at[j].neighbor[0] ) { + n -= 1; /* found explicit H => decrement number of implicit H */ + m = (int)norm_at[j].iso_atw_diff-1; + if ( 0 <= m && m < NUM_H_ISOTOPES ) { + /* subtract number of removed terminal isotopic H */ + /* atoms from the total number of isotopic H atoms */ + num_iso_H[m] -= 1; + } + } + } + /* at this point n = number of implicit H to display, + num_iso_H[] contains number of implicit isotopic H among n */ + if ( bIncludeIsotopicH ) { + /* subtract number of isotopic H atoms from the total number of H atoms */ + for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { + n -= num_iso_H[j]; + } + } + /* non-isotopic hydrogen atoms */ + if ( n > 1 ) { + len += sprintf( str+len, "H%d", n ); + } else + if ( n == 1 ) { + len += sprintf( str+len, "H" ); + } + /* isotopic hydrogen atoms */ + if ( bIncludeIsotopicH ) { + for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { + if ( num_iso_H[j] ) { + if ( j == 0 || j != 1 && j != 2 ) { + len += sprintf( str+len, "^%dH", j+1 ); + } else { + len += sprintf( str+len, j == 1? "D" : "T" ); + } + if ( num_iso_H[j] != 1 ) { + len += sprintf( str+len, "%d", (int)num_iso_H[j] ); + } + } + } + } + if ( norm_at[i].el_number == PERIODIC_NUMBER_H && str[0] == str[1] ) { + char *q; + if ( !str[2] ) { + str[1] = '2'; /* quick fix: replace HH with H2 */ + } else + if ( isdigit( UCINT str[2] ) && (n = strtol( str+2, &q, 10 )) && !q[0] ) { + len = 1 + sprintf( str+1, "%d", n+1 ); + } + } + /* + if ( str[0] == 'H' && str[1] == 'H' && !str[2] ) { + str[1] = '2'; + } + */ + /* charge */ + if ( abs(norm_at[i].charge) > 1 ) + len += sprintf( str+len, "%+d", norm_at[i].charge ); + else + if ( abs(norm_at[i].charge) == 1 ) + len += sprintf( str+len, "%s", norm_at[i].charge>0? "+" : "-" ); + /* radical */ + if ( norm_at[i].radical ) + len += sprintf( str+len, "%s", norm_at[i].radical==RADICAL_SINGLET? ":" : + norm_at[i].radical==RADICAL_DOUBLET? "." : + norm_at[i].radical==RADICAL_TRIPLET? ".." : "?"); + } + + /* stereogenic centers */ + if ( bDoDisplaySp3 && Stereo && 0 < (num_stereo = Stereo->nNumberOfStereoCenters) ) { + for ( i = 0; i < num_stereo; i ++ ) { + j = (int)nNormAtNosInCanonOrd[(int)nNumber[i]-1]; + c = t_parity[i]; + c = c==1? '-' : c==2? '+' : c==3? 'u' : c== 4? '?' : '*'; + inf_norm_at[j].cStereoCenterParity = c; + str=inf_norm_at[j].at_string; + len = (int) strlen(str); + if ( len + 3 < (int)sizeof(inf_norm_at[0].at_string) ) { + str[len++] = '('; + str[len++] = inf_norm_at[j].cStereoCenterParity; + str[len++] = ')'; + str[len] = '\0'; + /* mark ambuguous stereo center */ + if ( norm_at[j].bAmbiguousStereo && (c=='+' || c=='-' || c=='?') && str[0] != '!' && + len+1 < (int)sizeof(inf_norm_at[0].at_string) ) { + memmove( str+1, str, len+1 ); + str[0] = '!'; /* output the atom in red color */ + } + } + } + } + + /* stereogenic bonds */ + /* (cumulenes with odd number of double bonds are stereocenters, */ + /* and atom parity should be set) */ + if ( Stereo && 0 < (num_stereo = Stereo->nNumberOfStereoBonds) ) { + for ( i = 0; i < num_stereo; i ++ ) { + int start_at, num_eql=0, bAmbiguousStereoBond = 0; + j = (int)nNormAtNosInCanonOrd[(int)Stereo->nBondAtom1[i]-1]; + k = (int)nNormAtNosInCanonOrd[(int)Stereo->nBondAtom2[i]-1]; + start_at = j; + c = Stereo->b_parity[i]; + + c = c==1? '-' : c==2? '+' : c==3? 'u' : c== 4? '?' : '*'; + + /* mark ambuguous stereo bond atom(s) */ + if ( norm_at[j].bAmbiguousStereo && (c=='+' || c=='-' ) && + (len=strlen(str=inf_norm_at[j].at_string)+1) < (int)sizeof(inf_norm_at[0].at_string) && + str[0] != '!' ) { + memmove( str+1, str, len ); + str[0] = '!'; /* output the atom in red color */ + bAmbiguousStereoBond ++; + } + if ( norm_at[k].bAmbiguousStereo && (c=='+' || c=='-') && + (len=strlen(str=inf_norm_at[k].at_string)+1) < (int)sizeof(inf_norm_at[0].at_string) && + str[0] != '!' ) { + memmove( str+1, str, len ); + str[0] = '!'; /* output the atom in red color */ + bAmbiguousStereoBond ++; + } + + /* find the opposite atom k. */ + /* Note: since it may be a cumulene, find the shortest(best) path */ + /* to atom number k to avoid confusion in case of, for example, */ + /* 4-member aromatic rings. */ + best_len = MAX_CUMULENE_LEN+1; /* moved here from inside the cycle 1-8-2003 */ + for ( n = 0; n < norm_at[j].valence; n ++ ) { + if ( norm_at[j].bond_type[n] == BOND_SINGLE ) { + /* single bond cannot be stereogenic. */ + continue; + } + /* best_len = MAX_CUMULENE_LEN+1; */ + len = 0; /* number of bonds in cumulene - 1 */ + next_atom[len] = (int)norm_at[j].neighbor[n]; + next_neigh[len] = n; + cur_atom = j; + while ( next_atom[len] != k && len < MAX_CUMULENE_LEN && 2 == norm_at[next_atom[len]].valence ) { + next_neigh[len+1] = ((int)norm_at[next_atom[len]].neighbor[0] == cur_atom); + next_atom[len+1] = (int)norm_at[next_atom[len]].neighbor[next_neigh[len+1]]; + cur_atom = next_atom[len]; + len ++; + } + if ( next_atom[len] == k ) { + if ( len < best_len ) { + memcpy( best_next_neigh, next_neigh, sizeof(best_next_neigh) ); + memcpy( best_next_atom, next_atom, sizeof(best_next_atom) ); + best_len = len; + num_eql = 0; + if ( len == 0 ) { + break; /* path length cannot be smaller than 1 */ + } + } else + if ( len == best_len ) { + num_eql ++; + } + } + } + if ( best_len <= MAX_CUMULENE_LEN && best_next_atom[best_len] == k ) { + if ( num_eql ) { + num_err ++; /* program error; no breakpoint here */ + } + if ( best_len % 2 ) { + /* even number of bonds: chiral atom, draw parity on the cenrtal atom */ + j = best_next_atom[best_len/2]; + inf_norm_at[j].cStereoCenterParity = c; + str=inf_norm_at[j].at_string; + len = (int) strlen(str); + if ( len + 3 < (int)sizeof(inf_norm_at[0].at_string) ) { + str[len++] = '('; + str[len++] = inf_norm_at[j].cStereoCenterParity; + str[len++] = ')'; + str[len] = '\0'; + } + } else { + /* odd number of bonds: draw parity on the central bond */ + if ( best_len == 0 ) { + /* double bond */ + j = start_at; + k = best_next_neigh[0]; + } else { + /* cumulene */ + best_len = best_len/2-1; + j = best_next_atom[best_len]; + k = best_next_neigh[best_len+1]; /* added +1 to display cumulene parity on the middle bond (6-24-2002) */ + } + /* mark "forward" bond */ + for ( m = 0; m < MAX_STEREO_BONDS && inf_norm_at[j].cStereoBondParity[m]; m ++ ) + ; + if ( m < MAX_STEREO_BONDS ) { + inf_norm_at[j].cStereoBondParity[m] = c; + inf_norm_at[j].cStereoBondNumber[m] = k; + inf_norm_at[j].cStereoBondWarning[m] = bAmbiguousStereoBond; + } else { + num_err ++; /* program error; no breakpoint here */ + } + /* mark "backward" bond */ + n = norm_at[j].neighbor[k]; + for ( k = 0; k < norm_at[n].valence && j != (int)norm_at[n].neighbor[k]; k ++ ) + ; + if ( k < norm_at[n].valence ) { + j = n; + for ( m = 0; m < MAX_STEREO_BONDS && inf_norm_at[j].cStereoBondParity[m]; m ++ ) + ; + if ( m < MAX_STEREO_BONDS ) { + inf_norm_at[j].cStereoBondParity[m] = c; + inf_norm_at[j].cStereoBondNumber[m] = k; + inf_norm_at[j].cStereoBondWarning[m] = bAmbiguousStereoBond; + } else { + num_err ++; /* program error; no breakpoint here */ + } + } else { + num_err ++; /* program error; no breakpoint here */ + } + } + } else { + num_err ++; /* program error; no breakpoint here */ + } + } + } + + for ( i = 0; i < init_num_at; i ++ ) { + /* canonical numbers */ + if ( inf_norm_at[i].nCanonNbr ) { + str = inf_norm_at[i].at_string; + len = (int) strlen(str); + len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nCanonNbr ); + if ( inf_norm_at[i].nCanonEquNbr || inf_norm_at[i].nTautGroupCanonNbr || (inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT) ) { + if ( inf_norm_at[i].nCanonEquNbr ) { + len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nCanonEquNbr ); + } else + if ( len + 1 < len_str ) { + len += 1; + strcat( str, "/" ); + } + } + /* tautomeric groups */ + if ( inf_norm_at[i].nTautGroupCanonNbr ) { + len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nTautGroupCanonNbr ); + if ( inf_norm_at[i].nTautGroupEquNbr ) { + len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nTautGroupEquNbr ); + } + } + if ( (inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT) && len+2 <= len_str ) { + str[len++] = '/'; + str[len++] = '*'; + str[len] = '\0'; + } +#ifdef DISPLAY_DEBUG_DATA + if ( inf_norm_at[i].nDebugData ) { + len += (*MakeNumber)( str+len, len_str-len, "`", (int)inf_norm_at[i].nDebugData ); + } +#endif + } + } + +exit_function: + + if ( nNormAtNosInCanonOrd ) + inchi_free( nNormAtNosInCanonOrd ); + + return ret; +} +/***************************************************************************************/ +int FillOutOneCanonInfAtom( struct tagCANON_GLOBALS *pCG, + inp_ATOM *inp_norm_at, INF_ATOM_DATA *inf_norm_at_data, + AT_NUMB *pStereoFlags, int init_num_at, + int offset, int offset_H, int bIsotopic, + INChI *pINChI, INChI_Aux *pINChI_Aux, + int bAbcNumbers, INCHI_MODE nMode) +{ + int i, j, m, n, num_stereo, k, c, ret, len_str, len, atw, num_err; + int next_atom[MAX_CUMULENE_LEN+1], best_next_atom[MAX_CUMULENE_LEN+1], cur_atom; + int next_neigh[MAX_CUMULENE_LEN+1], best_next_neigh[MAX_CUMULENE_LEN+1], best_len, bIncludeIsotopicH; + int num_iso_H[NUM_H_ISOTOPES]; + char *str; + AT_NUMB g, e; + int num_at = pINChI->nNumberOfAtoms; + int num_H = init_num_at - num_at; /* number of removed H */ + int nNumberOfTGroups = (pINChI->lenTautomer && pINChI->nTautomer && pINChI->nTautomer[0])? (int)pINChI->nTautomer[0] : 0; + AT_NUMB *nOrigAtNosInCanonOrd; + INChI_Stereo *Stereo; + AT_NUMB *nConstitEquNumbers; + AT_NUMB *nConstitEquTGroupNumbers; + S_CHAR *t_parity = NULL; + AT_NUMB *nNumber = NULL; + + AT_NUMB *nNormAtNosInCanonOrd; + int (*MakeNumber)(char*, int, const char*, int) = bAbcNumbers? MakeAbcNumber:MakeDecNumber; + int bRel= (0 != (nMode & ( REQ_MODE_RELATIVE_STEREO))); + int bRac= (0 != (nMode & ( REQ_MODE_RACEMIC_STEREO))); + int bRelRac= bRel || bRac; + int bDoDisplaySp3 = 1; + + inf_ATOM *inf_norm_at = (inf_norm_at_data && inf_norm_at_data->at)? inf_norm_at_data->at+offset : NULL; + inp_ATOM *norm_at = inp_norm_at? inp_norm_at + offset : NULL; + inf_ATOM *inf_norm_at_H = (inf_norm_at_data && inf_norm_at_data->at)? inf_norm_at_data->at+offset_H : NULL; + inp_ATOM *norm_at_H = inp_norm_at? inp_norm_at + offset_H : NULL; + + ret = 0; + num_err = 0; + + if ( !inf_norm_at ) + return ret; + /* -- already added in FillOutCompositeCanonInfAtom() -- + if ( bIsotopic ) { + for ( i = 0, j = 0; i < NUM_H_ISOTOPES; i ++ ) { + if ( pINChI_Aux->nNumRemovedIsotopicH[i] ) { + inf_norm_at_data->num_iso_H[i] += pINChI_Aux->nNumRemovedIsotopicH[i]; + inf_norm_at_data->num_removed_iso_H ++; + } + } + } + */ + + if ( bIsotopic && !(pINChI->nNumberOfIsotopicAtoms || pINChI->nNumberOfIsotopicTGroups || + pINChI->nPossibleLocationsOfIsotopicH && pINChI->nPossibleLocationsOfIsotopicH[0]>1) ) + bIsotopic = 0; + + Stereo = bIsotopic? pINChI->StereoIsotopic : + pINChI->Stereo; + bDoDisplaySp3 = (NULL != Stereo) && (Stereo->nNumberOfStereoCenters > 0); + +#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) + if ( bDoDisplaySp3 && bRelRac && Stereo->nNumberOfStereoCenters < 2 && + (Stereo->nCompInv2Abs || ATOM_PARITY_ILL_DEF(Stereo->t_parity[0]) ) ) { + bDoDisplaySp3 = 0; + if ( Stereo->nCompInv2Abs ) { + inf_norm_at_data->StereoFlags |= bRel? INF_STEREO_REL : bRac? INF_STEREO_RAC : 0; + } + } +#endif + /* flag has stereo */ + if ( (NULL != Stereo) && (bDoDisplaySp3 || Stereo->nNumberOfStereoBonds > 0) ) { + (*pStereoFlags) |= INF_STEREO; + } + + /* + if ( bDoDisplaySp3 && bRelRac && Stereo->nCompInv2Abs && Stereo->nNumberOfStereoCenters < 2 ) { + bDoDisplaySp3 = 0; + } + */ + if ( bDoDisplaySp3 && Stereo->nCompInv2Abs ) { + /* inversion changes stereo */ + if ( bRel ) { + (*pStereoFlags) |= INF_STEREO_REL; + } else + if ( bRac ) { + (*pStereoFlags) |= INF_STEREO_RAC; + } else { + (*pStereoFlags) |= INF_STEREO_ABS; + } + if ( bRelRac ) { + (*pStereoFlags) |= (Stereo->nCompInv2Abs > 0)? INF_STEREO_NORM : INF_STEREO_INV; + } + } + if ( bDoDisplaySp3 && Stereo->nCompInv2Abs < 0 && !bRelRac ) { + /* display Inv stereo which is Absolute Stereo */ + nNumber = Stereo->nNumberInv; + t_parity = Stereo->t_parityInv; + nOrigAtNosInCanonOrd = bIsotopic? pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv : + pINChI_Aux->nOrigAtNosInCanonOrdInv; + } else { + /* display Output stereo which is Absolute Stereo */ + if ( bDoDisplaySp3 ) { + nNumber = Stereo->nNumber; + t_parity = Stereo->t_parity; + } + nOrigAtNosInCanonOrd = bIsotopic? pINChI_Aux->nIsotopicOrigAtNosInCanonOrd : + pINChI_Aux->nOrigAtNosInCanonOrd; + } + + nConstitEquNumbers = bIsotopic? pINChI_Aux->nConstitEquIsotopicNumbers : + pINChI_Aux->nConstitEquNumbers; + nConstitEquTGroupNumbers = bIsotopic? pINChI_Aux->nConstitEquIsotopicTGroupNumbers : + pINChI_Aux->nConstitEquTGroupNumbers; + memset( inf_norm_at, 0, num_at*sizeof(inf_norm_at[0]) ); + if ( num_H > 0 ) { + memset( inf_norm_at_H, 0, num_H*sizeof(inf_norm_at[0]) ); + } + + /* obtain norm_at[] atom numbers (from zero) in order of canonical numbers */ + nNormAtNosInCanonOrd = (AT_NUMB *)inchi_calloc( num_at, sizeof(nNormAtNosInCanonOrd[0]) ); + if ( ret = GetAtomOrdNbrInCanonOrd( pCG, norm_at, nNormAtNosInCanonOrd, nOrigAtNosInCanonOrd, num_at ) ) { + goto exit_function; + } + + /* atom canonical and equivalence numbers > 0 */ + for ( i = 0; i < num_at; i ++ ) { + j = (int)nNormAtNosInCanonOrd[i]; + if ( j < 0 || j >= num_at ) + continue; + inf_norm_at[j].nCanonNbr = (AT_NUMB)(i+1); + inf_norm_at[j].nCanonEquNbr = nConstitEquNumbers[i]; +#ifdef DISPLAY_DEBUG_DATA + inf_norm_at[j].nDebugData = 0; +#if ( DISPLAY_DEBUG_DATA == DISPLAY_DEBUG_DATA_C_POINT ) + inf_norm_at[j].nDebugData = norm_at[j].c_point; +#endif +#endif + } + /* tautomeric groups */ + if ( nNumberOfTGroups ) { + /* + : start from 1: bypass number of t-groups + : j is a counter within the current t-group + : g is a tautomeric group canonical number + : e is a tautomeric group equivalence + */ + for ( g = 1, i = 1; g <= nNumberOfTGroups; g ++ ) { + n = (int)pINChI->nTautomer[i] - INCHI_T_NUM_MOVABLE; /* number of atoms in t-group */ + e = nConstitEquTGroupNumbers[(int)g - 1]; + /* bypass number of hydrogen atoms, negative charges, ... */ + for ( i += INCHI_T_NUM_MOVABLE+1, j = 0; j < n && i < pINChI->lenTautomer; j ++, i ++ ) { + /* scan canonical numbers of atoms within the atom t-group */ + k = (int)nNormAtNosInCanonOrd[(int)pINChI->nTautomer[i]-1]; + inf_norm_at[k].nTautGroupCanonNbr = g; + inf_norm_at[k].nTautGroupEquNbr = e; + } + } + if ( i != pINChI->lenTautomer || g != nNumberOfTGroups+1 ) { + ret = CT_TAUCOUNT_ERR; /* */ + goto exit_function; + } + } + /* atoms that may exchange isotopic H */ + if ( bIsotopic && pINChI->nPossibleLocationsOfIsotopicH && (n = (int)pINChI->nPossibleLocationsOfIsotopicH[0]) ) { + for ( i = 1; i < n; i ++ ) { + j = (int)pINChI->nPossibleLocationsOfIsotopicH[i]; + k = (int)nNormAtNosInCanonOrd[j - 1]; + if ( !inf_norm_at[k].nTautGroupCanonNbr ) { + inf_norm_at[k].cFlags |= AT_FLAG_ISO_H_POINT; + } + } + } +#if ( DISPLAY_RING_SYSTEMS == 1 ) + /* debug only */ + for ( j = 0; j < num_at; j ++ ) { + inf_norm_at[j].nCanonNbr = norm_at[j].nBlockSystem; + inf_norm_at[j].nCanonEquNbr = norm_at[j].nRingSystem; +#if ( USE_DISTANCES_FOR_RANKING == 1 ) + inf_norm_at[j].nTautGroupCanonNbr = norm_at[j].nDistanceFromTerminal; + inf_norm_at[j].nTautGroupEquNbr = norm_at[j].bCutVertex; +#else + inf_norm_at[j].nTautGroupCanonNbr = norm_at[j].bCutVertex; + inf_norm_at[j].nTautGroupEquNbr = 0; +#endif + } +#endif + + /* Write isotopic mass, chemical element symbols and hydrogens, charge, radical, canon. numbers */ + len_str = sizeof(inf_norm_at[0].at_string); + for ( i = 0; i < init_num_at; i ++ ) { + inf_ATOM *cur_inf_norm_at = (i < num_at)? inf_norm_at+i : inf_norm_at_H+i-num_at; + inp_ATOM *cur_norm_at = (i < num_at)? norm_at +i : norm_at_H +i-num_at; + str = cur_inf_norm_at->at_string; + len = 0; + bIncludeIsotopicH = bIsotopic && (i >= num_at || !inf_norm_at[i].nTautGroupCanonNbr && !(inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT)); + /* isotopic mass */ + atw = 0; + if ( cur_norm_at->iso_atw_diff && bIsotopic ) { + if ( cur_norm_at->at_type == ATT_PROTON ) { + ; /* do not set isotopic mass of a tautomeric proton */ + } else + if ( num_at <= i && cur_norm_at->el_number == PERIODIC_NUMBER_H && cur_norm_at->chem_bonds_valence == 1 && + !cur_norm_at->charge && !cur_norm_at->radical && !cur_norm_at->num_H && + 0 <= (j=(int)cur_norm_at->neighbor[0]-offset) && j < num_at && + (inf_norm_at[j].nTautGroupCanonNbr || (inf_norm_at[j].cFlags & AT_FLAG_ISO_H_POINT) ) ) { + ; /* do not set isotopic mass of an exchangeable proton */ + } else { + atw = get_atomic_mass(cur_norm_at->elname); + atw += (cur_norm_at->iso_atw_diff>0)? cur_norm_at->iso_atw_diff-1:cur_norm_at->iso_atw_diff; + /*len += sprintf( str+len, "^%d", atw );*/ + } + } + /* element name */ + if ( cur_norm_at->el_number == PERIODIC_NUMBER_H && 2 <= atw && atw <= 3 ) { + len += sprintf( str+len, "%s", atw==2? "D" : "T" ); + } else { + if ( atw ) { + len += sprintf( str+len, "^%d", atw ); + } + if ( strcmp(cur_norm_at->elname, "Zz") ) + len += sprintf( str+len, "%s", cur_norm_at->elname ); + else + len += sprintf( str+len, "*" ); + } + /* hydrogens */ + /* find number of previuosly removed terminal hydrogen atoms */ + for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { + num_iso_H[j] = cur_norm_at->num_iso_H[j]; + } + for ( j = 0, n = (int)cur_norm_at->num_H; j < num_H; j ++ ) { + /* subtract number of removed terminal */ + /* H atoms from the total number of H atoms */ + if ( i == (int)norm_at_H[j].neighbor[0]-offset ) { + n -= 1; + m = (int)norm_at_H[j].iso_atw_diff-1; + if ( 0 <= m && m < NUM_H_ISOTOPES ) { + /* subtract number of removed terminal isotopic */ + /* H atoms from the total number of isotopic H atoms */ + num_iso_H[m] -= 1; + } + } + } + if ( bIncludeIsotopicH ) { + /* subtract number of isotopic H atoms from the total number of H atoms */ + for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { + n -= num_iso_H[j]; + } + } + /* non-isotopic hydrogen atoms */ + if ( n > 1 ) { + len += sprintf( str+len, "H%d", n ); + } else + if ( n == 1 ) { + len += sprintf( str+len, "H" ); + } + /* isotopic hydrogen atoms */ + if ( bIncludeIsotopicH ) { + for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { + if ( num_iso_H[j] ) { + if ( j == 0 || j != 1 && j != 2 ) { + len += sprintf( str+len, "^%dH", j+1 ); + } else { + len += sprintf( str+len, j == 1? "D" : "T" ); + } + if ( num_iso_H[j] != 1 ) { + len += sprintf( str+len, "%d", (int)num_iso_H[j] ); + } + } + } + } + if ( cur_norm_at->el_number == PERIODIC_NUMBER_H && str[0] == str[1] ) { + char *q; + if ( !str[2] ) { + str[1] = '2'; /* quick fix: replace HH with H2 */ + } else + if ( isdigit( UCINT str[2] ) && (n = strtol( str+2, &q, 10 )) && !q[0] ) { + len = 1 + sprintf( str+1, "%d", n+1 ); + } + } + /* charge */ + if ( abs(cur_norm_at->charge) > 1 ) + len += sprintf( str+len, "%+d", cur_norm_at->charge ); + else + if ( abs(cur_norm_at->charge) == 1 ) + len += sprintf( str+len, "%s", cur_norm_at->charge>0? "+" : "-" ); + /* radical */ + if ( cur_norm_at->radical ) + len += sprintf( str+len, "%s", cur_norm_at->radical==RADICAL_SINGLET? ":" : + cur_norm_at->radical==RADICAL_DOUBLET? "." : + cur_norm_at->radical==RADICAL_TRIPLET? ".." : "?"); + } + + /* stereogenic centers */ + if ( bDoDisplaySp3 && Stereo && 0 < (num_stereo = Stereo->nNumberOfStereoCenters) ) { + for ( i = 0; i < num_stereo; i ++ ) { + j = (int)nNormAtNosInCanonOrd[(int)nNumber[i]-1]; + c = t_parity[i]; + c = c==1? '-' : c==2? '+' : c==3? 'u' : c== 4? '?' : '*'; + inf_norm_at[j].cStereoCenterParity = c; + str=inf_norm_at[j].at_string; + len = (int) strlen(str); + if ( len + 3 < (int)sizeof(inf_norm_at[0].at_string) ) { + str[len++] = '('; + str[len++] = inf_norm_at[j].cStereoCenterParity; + str[len++] = ')'; + str[len] = '\0'; + /* mark ambuguous stereo center */ + if ( norm_at[j].bAmbiguousStereo && (c=='+' || c=='-' || c=='?') && str[0] != '!' && + len+1 < (int)sizeof(inf_norm_at[0].at_string) ) { + memmove( str+1, str, len+1 ); + str[0] = '!'; /* output the atom in red color */ + } + } + } + } + + /* stereogenic bonds */ + /* (cumulenes with odd number of double bonds are stereocenters, */ + /* and atom parity should be set) */ + if ( Stereo && 0 < (num_stereo = Stereo->nNumberOfStereoBonds) ) { + for ( i = 0; i < num_stereo; i ++ ) { + int start_at, num_eql=0, bAmbiguousStereoBond = 0; + j = (int)nNormAtNosInCanonOrd[(int)Stereo->nBondAtom1[i]-1]; + k = (int)nNormAtNosInCanonOrd[(int)Stereo->nBondAtom2[i]-1]; + start_at = j; + c = Stereo->b_parity[i]; + + c = c==1? '-' : c==2? '+' : c==3? 'u' : c== 4? '?' : '*'; + + /* mark ambuguous stereo bond atom(s) */ + if ( norm_at[j].bAmbiguousStereo && (c=='+' || c=='-' ) && + (len=strlen(str=inf_norm_at[j].at_string)+1) < (int)sizeof(inf_norm_at[0].at_string) && + str[0] != '!' ) { + memmove( str+1, str, len ); + str[0] = '!'; /* output the atom in red color */ + bAmbiguousStereoBond ++; + } + if ( norm_at[k].bAmbiguousStereo && (c=='+' || c=='-') && + (len=strlen(str=inf_norm_at[k].at_string)+1) < (int)sizeof(inf_norm_at[0].at_string) && + str[0] != '!' ) { + memmove( str+1, str, len ); + str[0] = '!'; /* output the atom in red color */ + bAmbiguousStereoBond ++; + } + + /* find the opposite atom k. */ + /* Note: since it may be a cumulene, find the shortest(best) path */ + /* to atom number k to avoid confusion in case of, for example, */ + /* 4-member aromatic rings. */ + best_len = MAX_CUMULENE_LEN+1; /* moved here from inside the cycle 1-8-2003 */ + for ( n = 0; n < norm_at[j].valence; n ++ ) { + if ( norm_at[j].bond_type[n] == BOND_SINGLE ) { + /* single bond cannot be stereogenic. */ + continue; + } + /* best_len = MAX_CUMULENE_LEN+1; */ + len = 0; /* number of bonds in cumulene - 1 */ + next_atom[len] = (int)norm_at[j].neighbor[n]-offset; + next_neigh[len] = n; + cur_atom = j; + while ( next_atom[len] != k && len < MAX_CUMULENE_LEN && 2 == norm_at[next_atom[len]].valence ) { + next_neigh[len+1] = ((int)norm_at[next_atom[len]].neighbor[0]-offset == cur_atom); + next_atom[len+1] = (int)norm_at[next_atom[len]].neighbor[next_neigh[len+1]]-offset; + cur_atom = next_atom[len]; + len ++; + } + if ( next_atom[len] == k ) { + if ( len < best_len ) { + memcpy( best_next_neigh, next_neigh, sizeof(best_next_neigh) ); + memcpy( best_next_atom, next_atom, sizeof(best_next_atom) ); + best_len = len; + num_eql = 0; + if ( len == 0 ) { + break; /* path length cannot be smaller than 1 */ + } + } else + if ( len == best_len ) { + num_eql ++; + } + } + } + if ( best_len <= MAX_CUMULENE_LEN && best_next_atom[best_len] == k ) { + if ( num_eql ) { + num_err ++; /* program error; no breakpoint here */ + } + if ( best_len % 2 ) { + /* even number of bonds: chiral atom, draw parity on the cenrtal atom */ + j = best_next_atom[best_len/2]; + inf_norm_at[j].cStereoCenterParity = c; + str=inf_norm_at[j].at_string; + len = (int) strlen(str); + if ( len + 3 < (int)sizeof(inf_norm_at[0].at_string) ) { + str[len++] = '('; + str[len++] = inf_norm_at[j].cStereoCenterParity; + str[len++] = ')'; + str[len] = '\0'; + } + } else { + /* odd number of bonds: draw parity on the central bond */ + if ( best_len == 0 ) { + /* double bond */ + j = start_at; + k = best_next_neigh[0]; + } else { + /* cumulene */ + best_len = best_len/2-1; + j = best_next_atom[best_len]; + k = best_next_neigh[best_len+1]; /* added +1 to display cumulene parity on the middle bond (6-24-2002) */ + } + /* mark "forward" bond */ + for ( m = 0; m < MAX_STEREO_BONDS && inf_norm_at[j].cStereoBondParity[m]; m ++ ) + ; + if ( m < MAX_STEREO_BONDS ) { + inf_norm_at[j].cStereoBondParity[m] = c; + inf_norm_at[j].cStereoBondNumber[m] = k; + inf_norm_at[j].cStereoBondWarning[m] = bAmbiguousStereoBond; + } else { + num_err ++; /* program error; no breakpoint here */ + } + /* mark "backward" bond */ + n = norm_at[j].neighbor[k]-offset; + for ( k = 0; k < norm_at[n].valence && j != (int)norm_at[n].neighbor[k]-offset; k ++ ) + ; + if ( k < norm_at[n].valence ) { + j = n; + for ( m = 0; m < MAX_STEREO_BONDS && inf_norm_at[j].cStereoBondParity[m]; m ++ ) + ; + if ( m < MAX_STEREO_BONDS ) { + inf_norm_at[j].cStereoBondParity[m] = c; + inf_norm_at[j].cStereoBondNumber[m] = k; + inf_norm_at[j].cStereoBondWarning[m] = bAmbiguousStereoBond; + } else { + num_err ++; /* program error; no breakpoint here */ + } + } else { + num_err ++; /* program error; no breakpoint here */ + } + } + } else { + num_err ++; /* program error; no breakpoint here */ + } + } + } + + for ( i = 0; i < num_at; i ++ ) { + /* canonical numbers */ + if ( inf_norm_at[i].nCanonNbr ) { + str = inf_norm_at[i].at_string; + len = (int) strlen(str); + len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nCanonNbr ); + if ( inf_norm_at[i].nCanonEquNbr || inf_norm_at[i].nTautGroupCanonNbr || (inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT) ) { + if ( inf_norm_at[i].nCanonEquNbr ) { + len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nCanonEquNbr ); + } else + if ( len + 1 < len_str ) { + len += 1; + strcat( str, "/" ); + } + } + /* tautomeric groups */ + if ( inf_norm_at[i].nTautGroupCanonNbr ) { + len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nTautGroupCanonNbr ); + if ( inf_norm_at[i].nTautGroupEquNbr ) { + len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_norm_at[i].nTautGroupEquNbr ); + } + } + if ( (inf_norm_at[i].cFlags & AT_FLAG_ISO_H_POINT) && len+2 <= len_str ) { + str[len++] = '/'; + str[len++] = '*'; + str[len] = '\0'; + } +#ifdef DISPLAY_DEBUG_DATA + if ( inf_norm_at[i].nDebugData ) { + len += (*MakeNumber)( str+len, len_str-len, "`", (int)inf_norm_at[i].nDebugData ); + } +#endif + } + } + +exit_function: + + if ( nNormAtNosInCanonOrd ) + inchi_free( nNormAtNosInCanonOrd ); + + return ret; +} + +/***************************************************************************************/ +int FillOutInputInfAtom(inp_ATOM *inp_at, INF_ATOM_DATA *inf_at_data, int init_num_at, int num_removed_H, + int bAdd_DT_to_num_H, int nNumRemovedProtons, NUM_H *nNumRemovedProtonsIsotopic, int bIsotopic, int bAbcNumbers) +{ + int i, j, m, n, ret, len_str, len, atw; + int num_iso_H[NUM_H_ISOTOPES]; + char *str; + int num_at = init_num_at - num_removed_H; + int (*MakeNumber)(char*, int, const char*, int) = MakeDecNumber; + + inf_ATOM *inf_at = inf_at_data? inf_at_data->at : NULL; + + ret = 0; + + if ( !inf_at ) + return ret; + + memset( inf_at, 0, init_num_at*sizeof(inf_at[0]) ); + + inf_at_data->nNumRemovedProtons = nNumRemovedProtons; + MakeRemovedProtonsString( nNumRemovedProtons, nNumRemovedProtonsIsotopic, NULL, bIsotopic, inf_at_data->szRemovedProtons, NULL ); + /* atom canonical and equivalence numbers > 0 */ + for ( i = 0; i < num_at; i ++ ) { +#if ( DISPLAY_ORIG_AT_NUMBERS == 1 ) + inf_at[i].nCanonNbr = inp_at[i].orig_at_number; +#else + inf_at[i].nCanonNbr = (AT_NUMB)(i+1); +#endif + } + /* Write isotopic mass, chemical element symbols and hydrogens, charge, radical, canon. numbers */ + len_str = sizeof(inf_at[0].at_string); + for ( i = 0; i < init_num_at; i ++ ) { + str = inf_at[i].at_string; + len = 0; + /* isotopic mass */ + atw = 0; + if ( inp_at[i].iso_atw_diff && bIsotopic ) { + atw = get_atomic_mass(inp_at[i].elname); + atw += (inp_at[i].iso_atw_diff>0)? inp_at[i].iso_atw_diff-1:inp_at[i].iso_atw_diff; + /*len += sprintf( str+len, "^%d", atw );*/ + } + /* element name */ + if ( inp_at[i].el_number == PERIODIC_NUMBER_H && 2 <= atw && atw <= 3 ) { + len += sprintf( str+len, "%s", atw==2? "D" : "T" ); + } else { + if ( atw ) { + len += sprintf( str+len, "^%d", atw ); + } + if ( strcmp(inp_at[i].elname, "Zz") ) + len += sprintf( str+len, "%s", inp_at[i].elname ); + else + len += sprintf( str+len, "*" ); + } + /* hydrogens */ + /* find number of previuosly removed terminal hydrogen atoms */ + for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { + num_iso_H[j] = inp_at[i].num_iso_H[j]; + } + for ( j = num_at, n = (int)inp_at[i].num_H; j < init_num_at; j ++ ) { + /* subtract number of removed terminal */ + /* H atoms from the total number of H atoms */ + if ( i == (int)inp_at[j].neighbor[0] ) { + n -= 1; + m = (int)inp_at[j].iso_atw_diff-1; + if ( 0 <= m && m < NUM_H_ISOTOPES ) { + /* subtract number of removed terminal isotopic */ + /* H atoms from the total number of isotopic H atoms */ + num_iso_H[m] -= 1; + } + } + } + if ( bIsotopic && !bAdd_DT_to_num_H ) { + /* subtract number of isotopic H atoms from the total number of H atoms */ + for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { + n -= num_iso_H[j]; + } + } + /* non-isotopic hydrogen atoms */ + if ( n > 1 ) { + len += sprintf( str+len, "H%d", n ); + } else + if ( n == 1 ) { + len += sprintf( str+len, "H" ); /* fixed 12-21-2002: removed 3rd argument */ + } + if ( bIsotopic ) { + /* isotopic hydrogen atoms */ + for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { + if ( num_iso_H[j] ) { + if ( j == 0 || j != 1 && j != 2 ) { + len += sprintf( str+len, "^%dH", j+1 ); + } else { + len += sprintf( str+len, j == 1? "D" : "T" ); + } + if ( num_iso_H[j] != 1 ) { + len += sprintf( str+len, "%d", (int)num_iso_H[j] ); + } + } + } + } + if ( inp_at[i].el_number == PERIODIC_NUMBER_H && str[0] == str[1] ) { + char *q; + if ( !str[2] ) { + str[1] = '2'; /* quick fix: replace HH with H2 */ + } else + if ( isdigit( UCINT str[2] ) && (n = strtol( str+2, &q, 10 )) && !q[0] ) { + len = 1 + sprintf( str+1, "%d", n+1 ); + } + } + /* + if ( str[0] == 'H' && str[1] == 'H' && !str[2] ) { + str[1] = '2'; + } + */ + /* charge */ + if ( abs(inp_at[i].charge) > 1 ) + len += sprintf( str+len, "%+d", inp_at[i].charge ); + else + if ( abs(inp_at[i].charge) == 1 ) + len += sprintf( str+len, "%s", inp_at[i].charge>0? "+" : "-" ); + /* radical */ + if ( inp_at[i].radical ) + len += sprintf( str+len, "%s", inp_at[i].radical==RADICAL_SINGLET? ":" : + inp_at[i].radical==RADICAL_DOUBLET? "." : + inp_at[i].radical==RADICAL_TRIPLET? ".." : "?"); + } + + for ( i = 0; i < init_num_at; i ++ ) { + /* canonical numbers */ + if ( inf_at[i].nCanonNbr ) { + str = inf_at[i].at_string; + len = (int) strlen(str); + len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_at[i].nCanonNbr ); + if ( inf_at[i].nCanonEquNbr || inf_at[i].nTautGroupCanonNbr ) { + len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_at[i].nCanonEquNbr ); + } + /* tautomeric groups */ + if ( inf_at[i].nTautGroupCanonNbr ) { + len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_at[i].nTautGroupCanonNbr ); + if ( inf_at[i].nTautGroupEquNbr ) { + len += (*MakeNumber)( str+len, len_str-len, "/", (int)inf_at[i].nTautGroupEquNbr ); + } + } + } + } + ret = init_num_at; + + return ret; +} +/**********************************************************************************************/ +int FillOutInfAtom( struct tagCANON_GLOBALS *pCG, + inp_ATOM *norm_at, INF_ATOM_DATA *inf_norm_at_data, int init_num_at, int num_removed_H, + int bAdd_DT_to_num_H, int nNumRemovedProtons, NUM_H *nNumRemovedProtonsIsotopic, int bIsotopic, + INChI *pINChI, INChI_Aux *pINChI_Aux, int bAbcNumbers, INCHI_MODE nMode ) +{ + if ( norm_at && inf_norm_at_data && inf_norm_at_data->at ) { + if ( pINChI && pINChI_Aux ) { + return FillOutCanonInfAtom( pCG, norm_at, inf_norm_at_data, init_num_at, bIsotopic, pINChI, + pINChI_Aux, bAbcNumbers, nMode); + } else { + return FillOutInputInfAtom( norm_at, inf_norm_at_data, init_num_at, num_removed_H, bAdd_DT_to_num_H, + nNumRemovedProtons, nNumRemovedProtonsIsotopic, bIsotopic, bAbcNumbers); + } + } + return 0; +} +/***************************************************************************************/ +int FillOutCompositeCanonInfAtom( struct tagCANON_GLOBALS *pCG, + COMP_ATOM_DATA *composite_norm_data, INF_ATOM_DATA *inf_norm_at_data, + int bIsotopic, int bTautomeric, + PINChI2 *pINChI2, PINChI_Aux2 *pINChI_Aux2, int bAbcNumbers, INCHI_MODE nMode) +{ + int i, num_components, j, k, ret; + inp_ATOM *inp_norm_at; + INChI *pINChI; + INChI_Aux *pINChI_Aux; + int num_inp_at, num_at, num_H, offset, offset_H, next_offset, next_offset_H; + + if ( composite_norm_data && inf_norm_at_data && (bTautomeric == TAUT_INI || pINChI2 && pINChI_Aux2) ) { + composite_norm_data += bTautomeric; + inp_norm_at = composite_norm_data->at; + num_components = composite_norm_data->num_components; + offset = 0; + offset_H = composite_norm_data->num_at - composite_norm_data->num_removed_H; + if ( bTautomeric == TAUT_INI ) { + ret = FillOutInputInfAtom( composite_norm_data->at, inf_norm_at_data, composite_norm_data->num_at, + composite_norm_data->num_removed_H, 0 /*bAdd_DT_to_num_H*/, + composite_norm_data->nNumRemovedProtons, + composite_norm_data->nNumRemovedProtonsIsotopic, + bIsotopic, bAbcNumbers); + return ret; + } else { + for ( i = 0; i < num_components; i ++ ) { + j = inchi_min(bTautomeric, TAUT_YES); + /* count isotopic H on removed atoms -- isolated H(+) cations */ + inf_norm_at_data->nNumRemovedProtons += pINChI_Aux2[i][j]->nNumRemovedProtons; + if ( bIsotopic && bTautomeric == TAUT_YES ) { + for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) { + if ( pINChI_Aux2[i][j]->nNumRemovedIsotopicH[k] ) { + inf_norm_at_data->num_iso_H[k] += pINChI_Aux2[i][j]->nNumRemovedIsotopicH[k]; + inf_norm_at_data->num_removed_iso_H += pINChI_Aux2[i][j]->nNumRemovedIsotopicH[k]; + } + } + } + /* ignore deleted components */ + if ( pINChI2[i][j] && pINChI2[i][j]->bDeleted ) { + continue; + } + if ( !pINChI2[i][j] || !pINChI2[i][j]->nNumberOfAtoms ) { + j = ALT_TAUT(j); + if ( !pINChI2[i][j] || !pINChI2[i][j]->nNumberOfAtoms ) { + continue; /* error ??? */ + } + } + pINChI = pINChI2[i][j]; + pINChI_Aux = pINChI_Aux2[i][j]; + next_offset = composite_norm_data->nOffsetAtAndH[2*i]; + next_offset_H = composite_norm_data->nOffsetAtAndH[2*i+1]; + num_at = next_offset - offset; + if ( num_at <= 0 ) + continue; + num_H = next_offset_H - offset_H; + num_inp_at = num_at + num_H; + if ( num_at != pINChI->nNumberOfAtoms || num_at != pINChI_Aux->nNumberOfAtoms ) { + return 0; /* error */ + } + ret = FillOutOneCanonInfAtom( pCG, + inp_norm_at, inf_norm_at_data, + inf_norm_at_data->pStereoFlags+i+1, + num_inp_at, + offset, offset_H, bIsotopic, + pINChI, pINChI_Aux, bAbcNumbers, nMode); + if ( ret ) + return 0; /* error */ + + inf_norm_at_data->StereoFlags |= inf_norm_at_data->pStereoFlags[i+1]; + offset = next_offset; + offset_H = next_offset_H; + } + } + MakeRemovedProtonsString( inf_norm_at_data->nNumRemovedProtons, inf_norm_at_data->num_iso_H, NULL, bIsotopic, + inf_norm_at_data->szRemovedProtons, &inf_norm_at_data->num_removed_iso_H ); + } + return 1; +} +#endif /* } ifndef COMPILE_ANSI_ONLY */ + +int CheckCanonNumberingCorrectness( int num_atoms, + int num_at_tg, + sp_ATOM *at, + CANON_STAT *pCS, + CANON_GLOBALS *pCG, + int bTautomeric, + char *pStrErrStruct ) +{ + int i, ret=0; + AT_NUMB *pCanonOrd=NULL; + int nErrorCode = 0; + AT_NUMB *pCanonRank; /* canonical ranks of the atoms or tautomeric groups */ + AT_NUMB *pCanonRankAtoms=NULL; + + static int count=0; /* for debug only */ + count ++; + + pCanonRankAtoms = (AT_NUMB *)inchi_calloc( num_at_tg+1, sizeof(pCanonRankAtoms[0]) ); + + /* + Non-isotopic part + */ + + pCanonOrd = pCS->nLenCanonOrdStereo > 0? pCS->nCanonOrdStereo : + pCS->nLenCanonOrd > 0? pCS->nCanonOrd : NULL; + pCanonRank = pCanonRankAtoms; + if ( pCanonOrd && pCanonRank ) { + for ( i = 0; i < num_at_tg; i ++ ) { + pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); + } + ret = UpdateFullLinearCT( num_atoms, num_at_tg, at, pCanonRank, pCanonOrd, pCS, + pCG, + 0 ); + if ( ret /*|| memcmp(pCS->LinearCT, pCS->LinearCT2, sizeof(AT_RANK) * pCS->nLenLinearCT )*/ ) { + nErrorCode |= WARN_FAILED_STEREO; + } + } else { + nErrorCode |= ERR_NO_CANON_RESULTS; + goto exit_function; + } + + /* + Isotopic part + */ + + pCanonOrd = pCS->nLenCanonOrdIsotopicStereo > 0? pCS->nCanonOrdIsotopicStereo : + pCS->nLenCanonOrdIsotopic > 0? pCS->nCanonOrdIsotopic : NULL; + pCanonRank = pCanonRankAtoms; + + if ( pCanonOrd && pCanonRank ) { + for ( i = 0; i < num_at_tg; i ++ ) { + pCanonRank[pCanonOrd[i]] = (AT_NUMB)(i+1); + } + ret = UpdateFullLinearCT( num_atoms, num_at_tg, at, pCanonRank, pCanonOrd, pCS, + pCG, + 0 ); + if ( ret /*|| memcmp(pCS->LinearCT, pCS->LinearCT2, sizeof(AT_RANK) * pCS->nLenLinearCT )*/ ) { + nErrorCode |= (pCS->nLenCanonOrdIsotopicStereo? WARN_FAILED_ISOTOPIC_STEREO : WARN_FAILED_ISOTOPIC); + } + } + +exit_function: + if ( pCanonRankAtoms ) + inchi_free( pCanonRankAtoms ); + + if ( nErrorCode ) { + return CT_CANON_ERR; + } + + return 0; +} diff --git a/INCHI-1-SRC/INCHI_BASE/src/ichimake.h b/INCHI-1-SRC/INCHI_BASE/src/ichimake.h new file mode 100644 index 0000000..e06c751 --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/ichimake.h @@ -0,0 +1,468 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _ICHIMAKE_H_ +#define _ICHIMAKE_H_ + +#include "ichisize.h" +#include "ichi.h" +#include "extr_ct.h" +#include "ichicant.h" + +/***********************************************************************/ +/* replace all ' ' delimiters with ',' */ +#define ITEM_DELIMETER "," +#define EXTRA_SPACE "" +#define COMMA_EXTRA_SPACE "," +#define LEN_EXTRA_SPACE 0 + +/**********************************************************************************************/ +/* nCtMode for output INChI */ + +#define CT_MODE_NO_ORPHANS 1 /* no orphans, that CT should have only atoms with neighbors */ +#define CT_MODE_ABC_NUMBERS 2 +#define CT_MODE_ATOM_COUNTS 4 +#define CT_MODE_PREDECESSORS 8 +#define CT_MODE_EQL_H_TOGETHER 16 +#define CT_MODE_ABC_NUM_CLOSURES 32 /* in CT_MODE_ABC_NUMBERS output AB1AC2AB instead of AB-AC-A-B */ + + +/*************** Macros for retrieving requested INChI and INChI_Aux *****************************/ +/* S->pINChI[TAUT_YES] has info: */ +#define HAS_T(S) (S->pINChI[TAUT_YES] && S->pINChI[TAUT_YES]->nNumberOfAtoms) +/* S->pINChI[TAUT_NON] has info: */ +#define HAS_N(S) (S->pINChI[TAUT_NON] && S->pINChI[TAUT_NON]->nNumberOfAtoms) + +/* S->pINChI[TAUT_YES] has tautomeric info: */ +#define HAS_TT(S) (S->pINChI[TAUT_YES] && S->pINChI[TAUT_YES]->nNumberOfAtoms && S->pINChI[TAUT_YES]->lenTautomer>0) +/* S->pINChI[TAUT_YES] has non-taitomeric info: */ +#define HAS_TN(S) (S->pINChI[TAUT_YES] && S->pINChI[TAUT_YES]->nNumberOfAtoms && !S->pINChI[TAUT_YES]->lenTautomer) +/* S->pINChI[TAUT_NON] has non-tautomeric info: */ +#define HAS_NN(S) (S->pINChI[TAUT_NON] && S->pINChI[TAUT_NON]->nNumberOfAtoms && !S->pINChI[TAUT_NON]->lenTautomer) +#define GET_II(M,S) ((M==OUT_N1)? (HAS_TN(S)? TAUT_YES : HAS_NN(S)? TAUT_NON : -1): \ + (M==OUT_T1 || M==OUT_TN)? (HAS_T(S) ? TAUT_YES : HAS_N(S) ? TAUT_NON : -1): \ + (M==OUT_NN)? (HAS_NN(S)? TAUT_NON : HAS_TN(S)? TAUT_YES : -1): \ + (M==OUT_NT)? ((HAS_TT(S) && HAS_NN(S)) ? TAUT_NON : -1) : -1) + +/*********************************/ +/* Equivalence flags definitions */ +/*********************************/ + +/* What is equal (ii = INChI_ITEM) */ +#define iiSTEREO 0x0001 /* stereo (sp2 or sp3) */ +#define iiSTEREO_INV 0x0002 /* inverted stereo (sp3) */ +#define iiNUMB 0x0004 /* numbering or inverted stereo numbering */ +#define iiEQU 0x0008 /* equivalence info */ +/* derived: + (iiSTEREO_INV | iiNUMB) = numbering of inverted stereo +*/ + +/* Additional info to what is equal (INCHI_ITEM_TYPE = iit) */ +#define iitISO 0x0010 /* Isotopic */ +#define iitNONTAUT 0x0020 /* Non-tautomeric */ +/* derived: + (iitISO | iitNONTAUT) = isotopic non-tautomeric +*/ + +/* Where is the equivalent item located (INChI_ITEM_EQUAL_TO = iiEq2) */ +#define iiEq2NONTAUT 0x0040 /* non-tautomeric */ +#define iiEq2ISO 0x0080 /* isotopic */ +#define iiEq2INV 0x0100 /* equal to inverted (stereo sp3) or to numbering of inverted stereo */ + +#define iiEmpty 0x0200 /* item is empty while in the preceding layer the item is not empty */ + +/*********************** Printing strings external declarations *******************************/ + +extern const char sCompDelim[]; + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + +/**********************************************************************************************/ +int CompareTautNonIsoPartOfINChI( const INChI *i1, + const INChI *i2 ); +const char *EquString( int EquVal ); +int FillOutINChI( INChI *pINChI, + INChI_Aux *pINChI_Aux, + int num_atoms, + int num_at_tg, + int num_removed_H, + sp_ATOM *at, + inp_ATOM *norm_at, + CANON_STAT *pCS, + CANON_GLOBALS *pCG, + int bTautomeric, + INCHI_MODE nUserMode, + char *pStrErrStruct ); +int MakeHillFormulaString( char *szHillFormula, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow); +int bHasOrigInfo( ORIG_INFO *OrigInfo, + int num_atoms ); +int EqlOrigInfo( INChI_Aux *a1, + INChI_Aux *a2 ); +int MakeAbcNumber( char *szString, + int nStringLen, + const char *szLeadingDelim, + int nValue ); +int MakeDecNumber( char *szString, + int nStringLen, + const char *szLeadingDelim, + int nValue ); +int MakeCtStringNew( CANON_GLOBALS *pCG, + AT_NUMB *LinearCT, + int nLenCT, + int bAddDelim, + S_CHAR *nNum_H, + int num_atoms, + INCHI_IOSTREAM_STRING *strbuf, + int nCtMode, + int *bOverflow); +int MakeCtStringOld( AT_NUMB *LinearCT, + int nLenCT, + int bAddDelim, + INCHI_IOSTREAM_STRING *strbuf, + int nCtMode, + int *bOverflow); +int MakeCtString( CANON_GLOBALS *pCG, + AT_NUMB *LinearCT, + int nLenCT, + int bAddDelim, + S_CHAR *nNum_H, /* not used here */ + int num_atoms, /* not used here */ + INCHI_IOSTREAM_STRING *strbuf, + int nCtMode, + int *bOverflow); +int MakeTautString( AT_NUMB *LinearCT, + int nLenCT, + int bAddDelim, + INCHI_IOSTREAM_STRING *strbuf, + int nCtMode, + int *bOverflow); +int MakeEquString( AT_NUMB *LinearCT, + int nLenCT, + int bAddDelim, + INCHI_IOSTREAM_STRING *strbuf, + int nCtMode, + int *bOverflow); +int MakeIsoAtomString( INChI_IsotopicAtom *IsotopicAtom, + int nNumberOfIsotopicAtoms, + INCHI_IOSTREAM_STRING *strbuf, + int nCtMode, + int *bOverflow); +int MakeIsoTautString( INChI_IsotopicTGroup *IsotopicTGroup, + int nNumberOfIsotopicTGroups, + INCHI_IOSTREAM_STRING *strbuf, + int nCtMode, + int *bOverflow); +int MakeIsoHString( int num_iso_H[], + INCHI_IOSTREAM_STRING *strbuf, + int nCtMode, int *bOverflow); +int MakeStereoString( AT_NUMB *at1, + AT_NUMB *at2, + S_CHAR *parity, + int bAddDelim, + int nLenCT, + INCHI_IOSTREAM_STRING *buf, + int nCtMode, + int *bOverflow); +int MakeCRVString( ORIG_INFO *OrigInfo, + int nLenCT, + int bAddDelim, + INCHI_IOSTREAM_STRING *strbuf, + int nCtMode, + int *bOverflow); +int MakeMult( int mult, + const char *szTailingDelim, + INCHI_IOSTREAM_STRING *buf, + int nCtMode, + int *bOverflow); +int MakeDelim( const char *szTailingDelim, + INCHI_IOSTREAM_STRING *buf, + int *bOverflow); +int MakeEqStr( const char *szTailingDelim, + int mult, + INCHI_IOSTREAM_STRING *buf, + int *bOverflow); +int MakeHString( int bAddDelim, + S_CHAR *LinearCT, + int nLenCT, + INCHI_IOSTREAM_STRING *buf, + int nCtMode, + int *bOverflow ); +AT_NUMB *GetDfsOrder4CT( CANON_GLOBALS *pCG, + AT_NUMB *LinearCT, + int nLenCT, + S_CHAR *nNum_H, + int num_atoms, + int nCtMode ); +int str_HillFormula( INCHI_SORT *pINChISort, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int num_components, + int bUseMulipliers); +int str_HillFormula2( INCHI_SORT *pINChISort /* non-taut */, + INCHI_SORT *pINChISort2 /* taut */, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int num_components, + int bUseMulipliers); +int str_Connections( CANON_GLOBALS *pCG, + INCHI_SORT *pINChISort, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int ATOM_MODE, + int num_components, + int bUseMulipliers); +int str_H_atoms( INCHI_SORT *pINChISort, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int ATOM_MODE, + int TAUT_MODE, + int num_components, + int bUseMulipliers); +int str_Charge2( INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int num_components, + int bSecondNonTautPass, + int bOmitRepetitions, + int bUseMulipliers); +int str_Sp2( INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int TAUT_MODE, + int num_components, + int bSecondNonTautPass, + int bOmitRepetitions, + int bUseMulipliers); +int str_IsoSp2( INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, int TAUT_MODE, + int num_components, + int bSecondNonTautPass, + int bOmitRepetitions, + int bUseMulipliers); +int str_Sp3( INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int TAUT_MODE, + int num_components, + int bRelRac, + int bSecondNonTautPass, + int bOmitRepetitions, + int bUseMulipliers); +int str_IsoSp3( INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int TAUT_MODE, + int num_components, + int bRelRac, + int bSecondNonTautPass, + int bOmitRepetitions, + int bUseMulipliers); +int str_StereoAbsInv( INCHI_SORT *pINChISort, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int num_components); +int str_IsoStereoAbsInv( INCHI_SORT *pINChISort, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int num_components); +int str_IsoAtoms( INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int TAUT_MODE, + int num_components, + int bAbcNumbers, + int bSecondNonTautPass, + int bOmitRepetitions, + int bUseMulipliers); +int str_FixedH_atoms( INCHI_SORT *pINChISort, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int ATOM_MODE, + int num_components, + int bUseMulipliers); +int str_AuxNumb( CANON_GLOBALS *pCG, + INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int TAUT_MODE, + int num_components, + int bSecondNonTautPass, + int bOmitRepetitions); +int str_AuxEqu( INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int TAUT_MODE, + int num_components, + int bSecondNonTautPass, + int bOmitRepetitions, + int bUseMulipliers); +int str_AuxTgroupEqu( INCHI_SORT *pINChISort, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int TAUT_MODE, + int num_components, + int bUseMulipliers); +int str_AuxIsoTgroupEqu( INCHI_SORT *pINChISort, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int TAUT_MODE, + int num_components, + int bOmitRepetitions, + int bUseMulipliers); +int str_AuxInvSp3( INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int TAUT_MODE, + int num_components, + int bSecondNonTautPass, + int bOmitRepetitions, + int bUseMulipliers); +int str_AuxInvSp3Numb( CANON_GLOBALS *pCG, + INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int TAUT_MODE, + int num_components, + int bSecondNonTautPass, + int bOmitRepetitions); +int str_AuxIsoNumb( CANON_GLOBALS *pCG, + INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int TAUT_MODE, + int num_components, + int bSecondNonTautPass, + int bOmitRepetitions); +int str_AuxIsoEqu( INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int TAUT_MODE, + int num_components, + int bSecondNonTautPass, + int bOmitRepetitions, + int bUseMulipliers); +int str_AuxInvIsoSp3( INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, int bOutType, + int TAUT_MODE, + int num_components, + int bSecondNonTautPass, + int bOmitRepetitions, + int bUseMulipliers); +int str_AuxInvIsoSp3Numb( CANON_GLOBALS *pCG, + INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int TAUT_MODE, + int num_components, + int bSecondNonTautPass, + int bOmitRepetitions); +int str_AuxChargeRadVal( INCHI_SORT *pINChISort, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int TAUT_MODE, + int num_components, + int bUseMulipliers); +int bin_AuxTautTrans( INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + AT_NUMB **pTrans_n, + AT_NUMB **pTrans_s, + int bOutType, + int num_components); +int str_AuxTautTrans( CANON_GLOBALS *pCG, + AT_NUMB *nTrans_n, + AT_NUMB *nTrans_s, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int TAUT_MODE, + int num_components); + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + + + +#endif /* _ICHIMAKE_H_ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichimap1.c b/INCHI-1-SRC/INCHI_BASE/src/ichimap1.c similarity index 93% rename from INCHI-1-SRC/INCHI_API/inchi_dll/ichimap1.c rename to INCHI-1-SRC/INCHI_BASE/src/ichimap1.c index 62d62d7..130bbbb 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichimap1.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichimap1.c @@ -1,887 +1,876 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - -#include "mode.h" - -#include "incomdef.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichicant.h" -#include "ichicomn.h" - -#include "ichicomp.h" - - - - -/**************************************************************************************/ -/* Check if all equivalent to cr1 possibly stereogenic atoms: */ -/* 1) have KNOWN parity, and */ -/* 2) their parities are same */ -/**************************************************************************************/ -int All_SC_Same( AT_RANK canon_rank1, /* canonical number */ - const ppAT_RANK pRankStack1, const ppAT_RANK pRankStack2, - const AT_RANK *nAtomNumberCanonFrom, - const sp_ATOM *at ) -{ - int n1 = (int)nAtomNumberCanonFrom[(int)canon_rank1-1]; - AT_RANK r1 = pRankStack1[0][n1]; - int iMax1 = (int)r1; - int i1, s1; - int bFound=0, stereo_atom_parity=-1; - - /* find one stereo atom such that canon_rank1 can be mapped on it */ - for ( i1 = 1; i1 <= iMax1 && r1 == pRankStack2[0][s1=(int)pRankStack2[1][iMax1-i1]]; i1++ ) { - if ( at[s1].stereo_bond_neighbor[0] ) { - bFound=0; /* at[s1] is not sp3-stereogenic: it belongs to a stereobond */ - break; - } else - if ( i1 == 1 ) { - stereo_atom_parity = PARITY_VAL(at[s1].stereo_atom_parity); - if ( !ATOM_PARITY_KNOWN(stereo_atom_parity) ) { - bFound=0; /* at[s1] does not have a KNOWN parity */ - break; - } - } else - if ( stereo_atom_parity != PARITY_VAL(at[s1].stereo_atom_parity) ) { - bFound=0; /* two equivalent atoms have different parities */ - break; - } - bFound ++; - } - return bFound; -} -/**************************************************************************************/ -/* get next available (not mapped yet) rank for a stereo center atom */ -int Next_SC_At_CanonRank2( AT_RANK *canon_rank1, /* 1st call input: largest canon number mapped so far or 0 */ - /* output: suggested canon. rank > than input if success */ - AT_RANK *canon_rank1_min, /* 1st call:0 next calls: first tried canon. number */ - int *bFirstTime, /* 1 at the time of the 1st call */ - S_CHAR *bAtomUsedForStereo, /* STEREO_AT_MARK if the atom has not been mapped yet */ - const ppAT_RANK pRankStack1, /* mapping ranks/sort order of atoms with canon. numbers (from) */ - const ppAT_RANK pRankStack2, /* mapping ranks/sort order of atoms with stereo (to) */ - const AT_RANK *nAtomNumberCanonFrom, /* sorted order of the canon. numbers */ - int num_atoms ) -{ - AT_RANK canon_rank1_inp = *canon_rank1; - AT_RANK cr1; /* canonical rank (canonical number) */ - AT_RANK r1; /* mapping rank */ - int n1; /* ord. number of an atom with the canon. number */ - int s1; /* ord. number of an atom with stereo */ - int i1, bFound=0; - int iMax1; - - if ( canon_rank1_inp < *canon_rank1_min ) { - canon_rank1_inp = *canon_rank1_min; - } else - if ( canon_rank1_inp < 1 ) { - canon_rank1_inp = 1; - } else { - canon_rank1_inp ++; /* next canonical rank */ - } - cr1 = canon_rank1_inp; - - while ( (int) cr1 <= num_atoms ) { - n1 = (int)nAtomNumberCanonFrom[(int)cr1-1]; /* atom1 (which has canon. rank cr1) ord. number */ - iMax1 = (int)(r1 = pRankStack1[0][n1]); /* mapping rank of atom1 */ - /* find atoms "to" to which the canon. number can be mapped; they have mapping rank r1, number s1 */ - for ( i1 = 1; i1 <= iMax1 && r1 == pRankStack2[0][s1=(int)pRankStack2[1][iMax1-i1]]; i1++ ) { - /* looking for a stereo center atom that has mapping rank r1 */ - if ( bAtomUsedForStereo[s1] == STEREO_AT_MARK ) { - /* found a sterogenic atom that has not been mapped yet */ - bFound = 1; - break; - } - } - if ( bFound ) { - /* one stereogenic not mapped yet atom "to" has been found */ - if ( *bFirstTime ) { - *canon_rank1_min = cr1; - *bFirstTime = 0; - } - break; - } else { - /* a not mapped yet stereogenic atom has not found */ - /* for the mapping rank r1 defined by the canonical rank cr1; try next cr1 */ - cr1 ++; - } - } - if ( bFound ) { - /* success */ - *canon_rank1 = cr1; - return 1; - } - return 0; -} -/**********************************************************************/ -int CompareLinCtStereoDble ( AT_STEREO_DBLE *LinearCTStereoDble1, int nLenLinearCTStereoDble1, - AT_STEREO_DBLE *LinearCTStereoDble2, int nLenLinearCTStereoDble2 ) -{ - int i, num, ret = 0; - - /* compare double bonds */ - if ( LinearCTStereoDble1 && LinearCTStereoDble2 ) { - num = inchi_min(nLenLinearCTStereoDble1, nLenLinearCTStereoDble2); - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)LinearCTStereoDble1[i].at_num1 - (int)LinearCTStereoDble2[i].at_num1 ) - break; - if ( ret = (int)LinearCTStereoDble1[i].at_num2 - (int)LinearCTStereoDble2[i].at_num2 ) - break; - if ( ret = (int)LinearCTStereoDble1[i].parity - (int)LinearCTStereoDble2[i].parity ) - break; - } - if ( !ret ) { - ret = nLenLinearCTStereoDble1 - nLenLinearCTStereoDble2; - } - } else - if ( LinearCTStereoDble1 && nLenLinearCTStereoDble1 > 0 ) { - ret = 1; - } else - if ( LinearCTStereoDble2 && nLenLinearCTStereoDble2 > 0 ) { - ret = -1; - } - return ret; -} -/**********************************************************************/ -int CompareLinCtStereoCarb ( AT_STEREO_CARB *LinearCTStereoCarb1, int nLenLinearCTStereoCarb1, - AT_STEREO_CARB *LinearCTStereoCarb2, int nLenLinearCTStereoCarb2 ) -{ - int i, num, ret = 0; - - /* compare stereocenters */ - if ( LinearCTStereoCarb1 && LinearCTStereoCarb2 ) { - num = inchi_min(nLenLinearCTStereoCarb1, nLenLinearCTStereoCarb2); - for ( i = 0; i < num; i ++ ) { - if ( ret = (int)LinearCTStereoCarb1[i].at_num - (int)LinearCTStereoCarb2[i].at_num ) - break; - if ( ret = (int)LinearCTStereoCarb1[i].parity - (int)LinearCTStereoCarb2[i].parity ) - break; - } - if ( !ret ) { - ret = nLenLinearCTStereoCarb1 - nLenLinearCTStereoCarb2; - } - } else - if ( LinearCTStereoCarb1 && nLenLinearCTStereoCarb1 > 0 ) { - ret = 1; - } else - if ( LinearCTStereoCarb2 && nLenLinearCTStereoCarb2 > 0 ) { - ret = -1; - } - - return ret; -} -/**********************************************************************/ -int CompareLinCtStereo ( AT_STEREO_DBLE *LinearCTStereoDble1, int nLenLinearCTStereoDble1, - AT_STEREO_CARB *LinearCTStereoCarb1, int nLenLinearCTStereoCarb1, - AT_STEREO_DBLE *LinearCTStereoDble2, int nLenLinearCTStereoDble2, - AT_STEREO_CARB *LinearCTStereoCarb2, int nLenLinearCTStereoCarb2 ) -{ - int ret; - - /* compare double bonds */ - ret = CompareLinCtStereoDble ( LinearCTStereoDble1, nLenLinearCTStereoDble1, - LinearCTStereoDble2, nLenLinearCTStereoDble2 ); - if ( !ret ) { - ret = CompareLinCtStereoCarb ( LinearCTStereoCarb1, nLenLinearCTStereoCarb1, - LinearCTStereoCarb2, nLenLinearCTStereoCarb2 ); - } - return ret; -} -/**************************************************************************************/ -int CompareLinCtStereoAtomToValues( AT_STEREO_CARB *LinearCTStereoCarb, - AT_RANK at_rank_canon1, U_CHAR parity ) -{ - if ( LinearCTStereoCarb->at_num CT_GREATER_THAN at_rank_canon1 ) - return 1; - if ( LinearCTStereoCarb->at_num != at_rank_canon1 ) - return -1; - if ( LinearCTStereoCarb->parity CT_GREATER_THAN parity ) - return 1; - if ( LinearCTStereoCarb->parity != parity ) - return -1; - return 0; -} -/**************************************************************************************/ -/* Find atom number from the mapping rank and return 1, or */ -/* if the mapping rank is tied and the atom number is not unique then return 0 */ -int bUniqueAtNbrFromMappingRank( AT_RANK **pRankStack, AT_RANK nAtRank, AT_NUMB *nAtNumber ) -{ - int r = (int)nAtRank-1; - AT_NUMB i = pRankStack[1][r]; - if ( nAtRank == pRankStack[0][(int)i] && - (!r || nAtRank != pRankStack[0][pRankStack[1][r-1]]) - ) { - *nAtNumber = i; - return 1; - } - return 0; -} -/**************************************************************************************/ -/* Get minimal set (class) representative and partially compress the partitioning */ -/* mcr = minimal class representative. */ -AT_RANK nGetMcr( AT_RANK *nEqArray, AT_RANK n ) -{ - AT_RANK n1, n2, mcr; /* recursive version is much shorter. */ - - n1=nEqArray[(int)n]; - if ( n == n1 ) { - return n; - } - /* 1st pass: find mcr */ - while ( n1 != (n2=nEqArray[(int)n1])) { - n1 = n2; - } - /* 2nd pass: copy mcr to each element of the set starting from nEqArray[n] */ - mcr = n1; - n1 = n; - while ( /*n1*/ mcr != (n2=nEqArray[(int)n1]) ) { - nEqArray[(int)n1]=mcr; - n1 = n2; - } - return ( mcr ); -} -/**************************************************************************************/ -/* Join 2 sets (classes) that have members n1 and n2 */ -int nJoin2Mcrs( AT_RANK *nEqArray, AT_RANK n1, AT_RANK n2 ) -{ - n1 = nGetMcr( nEqArray, n1 ); - n2 = nGetMcr( nEqArray, n2 ); - if ( n1 < n2 ) { - nEqArray[n2] = n1; - return 1; /* a change has been made */ - } - if ( n2 < n1 ) { - nEqArray[n1] = n2; - return 1; /* a change has been made */ - } - return 0; /* no changes */ -} - -/********************************************************************************* - * For all pairs of atoms that are: * - * (a) connected by a possibly stereogenic bond * - * (b) "equivalent" at this point to canon_rank1-canon_rank2 : * - * Check if they: * - * 1) are connected by a stereo bond or cumulene bonds of the same length * - * 2) have KNOWN parity, and * - * 3) their parities are same * - *********************************************************************************/ -int All_SB_Same( AT_RANK canon_rank1, AT_RANK canon_rank2, /* canonical numbers */ - const ppAT_RANK pRankStack1, const ppAT_RANK pRankStack2, - const AT_RANK *nAtomNumberCanonFrom, sp_ATOM *at ) -{ - int n1 = (int)nAtomNumberCanonFrom[(int)canon_rank1-1]; /* at1 has canon_rank1 */ - int n2 = (int)nAtomNumberCanonFrom[(int)canon_rank2-1]; /* at2 has canon_rank2 */ - AT_RANK r1 = pRankStack1[0][n1]; /* at1 mapping rank */ - AT_RANK r2 = pRankStack1[0][n2]; /* at2 mapping rank */ - AT_RANK rNeigh1, rNeigh2; - int iMax1 = (int)r1; - /* int iMax2 = (int)r2; */ - int i1, i2, s1, s2, k1, k2, m, k, num_equal; - int bNotFound=1, cumulene_len, stereo_bond_parity; - - /* at the first atom that possibly may have canon_rank1 find one stereo bond such that */ - /* canon_rank1-canon_rank2 possibly may be mapped on it */ - for ( i1 = 1; i1 <= iMax1 && r1 == pRankStack2[0][s1=(int)pRankStack2[1][iMax1-i1]]; i1++ ) { - /* at[n1] may be possible to map on at[s1] */ - for ( k1 = 0, s2= 0, bNotFound=1; - k1 < MAX_NUM_STEREO_BONDS && (s2=(int)at[s1].stereo_bond_neighbor[k1]) && - (bNotFound = (r2 != pRankStack2[0][--s2])); k1 ++ ) - ; /* continue until the 1st at[s2] (to which at[n2] may be mapped) have been found */ - if ( !bNotFound ) { - break; /* stop at 1st found */ - } - } - if ( bNotFound ) { - return -1; /* error: no mapping exists */ - } - for ( k2 = 0, m = 0; k2 < MAX_NUM_STEREO_BONDS && (m=(int)at[s2].stereo_bond_neighbor[k2]) && m-1 != s1; k2 ++ ) - ; - if ( m-1 != s1 ) { - return -1; /* program error: stereo bond in opposite direction not found */ - } - stereo_bond_parity = at[s1].stereo_bond_parity[k1]; - if ( !PARITY_KNOWN(stereo_bond_parity) ) { - return 0; - } - cumulene_len = BOND_CHAIN_LEN(stereo_bond_parity); - rNeigh1 = pRankStack2[0][(int)at[s1].neighbor[(int)at[s1].stereo_bond_ord[k1]]]; - rNeigh2 = pRankStack2[0][(int)at[s2].neighbor[(int)at[s2].stereo_bond_ord[k2]]]; - - num_equal = 0; - /* Search among ALL neighbors because sometimes a stereo bond may be mapped on a non-stereo bond. */ - /* If is so then return 0: not all mappings are stereo-equivalent */ - for ( s1 = 1; s1 <= iMax1 && r1 == pRankStack2[0][i1=(int)pRankStack2[1][iMax1-s1]]; s1++ ) { - for ( k = 0; k < at[i1].valence; k ++ ) { - n1 = at[i1].neighbor[k]; - if ( rNeigh1 != pRankStack2[0][n1] ) { - continue; /* wrong neighbor */ - } - if ( cumulene_len ) { - int prev, next, len, j; - for ( prev=i1, len=0, next = n1; len < cumulene_len; len ++ ) { - if ( at[next].valence == 2 && !at[next].num_H ) { - j = ((int)at[next].neighbor[0] == prev); - prev = next; - next = at[next].neighbor[j]; - } else { - break; /* cannot continue */ - } - } - if ( len != cumulene_len || - r2 != pRankStack2[0][next] || - rNeigh2 != pRankStack2[0][prev] ) { - /* cumulene chain not found */ - continue; - } - i2 = next; - } else { - i2 = n1; - } - /* find if a stereogenic bond between at[i1]-at[i2] exists */ - for ( k1 = 0; k1 < MAX_NUM_STEREO_BONDS && - (m=(int)at[i1].stereo_bond_neighbor[k1]) && m-1 != i2; k1 ++ ) - ; - if ( m-1 != i2 ) { - return 0; - } - for ( k2 = 0; k2 < MAX_NUM_STEREO_BONDS && - (m=(int)at[i2].stereo_bond_neighbor[k2]) && m-1 != i1; k2 ++ ) - ; - if ( m-1 != i1 ) { - return 0; - } - if ( at[i1].stereo_bond_parity[k1] != at[i2].stereo_bond_parity[k2] ) { - return -1; /* program error */ - } - if ( stereo_bond_parity != at[i1].stereo_bond_parity[k1] ) { - return 0; - } - num_equal ++; - } - } - return num_equal; -} - -/**************************************************************************************/ -/* get min. ranks for the stereo bond atoms */ -int Next_SB_At_CanonRanks2( AT_RANK *canon_rank1, AT_RANK *canon_rank2, /* canonical numbers */ - AT_RANK *canon_rank1_min, AT_RANK *canon_rank2_min, - int *bFirstTime, S_CHAR *bAtomUsedForStereo, - const ppAT_RANK pRankStack1, const ppAT_RANK pRankStack2, - const AT_RANK *nCanonRankFrom, const AT_RANK *nAtomNumberCanonFrom, - const sp_ATOM *at, int num_atoms, int bAllene ) -{ - AT_RANK canon_rank1_inp = *canon_rank1; - AT_RANK canon_rank2_inp = *canon_rank2; - AT_RANK cr1, cr2; /* canonical ranks (canonical numbers) */ - AT_RANK r1, r2; /* mapping ranks */ - int n1, n2; /* ord. numbers of atoms with stereo */ - int s1, s2; /* ord. numbers of atoms with canon. numbers */ - int i1, i2, k, m; - int iMax1, iMax2; - - if ( canon_rank1_inp < *canon_rank1_min || - canon_rank1_inp == *canon_rank1_min && - canon_rank2_inp < *canon_rank2_min ) { - - canon_rank1_inp = *canon_rank1_min; - canon_rank2_inp = *canon_rank2_min; - } else - if ( canon_rank1_inp < 2 ) { - canon_rank1_inp = 2; - canon_rank2_inp = 0; - } - cr1 = canon_rank1_inp; - cr2 = num_atoms; /* initialize. 1/8/2002 */ - while ( (int) cr1 <= num_atoms ) { - cr2 = cr1; - n1 = (int)nAtomNumberCanonFrom[(int)cr1-1]; /* atom1=at[n1] (which has canon. rank) ord. number */ - iMax1 = (int)(r1 = pRankStack1[0][n1]); /* mapping rank of atom1 */ - for ( i1 = 1; i1 <= iMax1 && r1 == pRankStack2[0][s1=(int)pRankStack2[1][iMax1-i1]]; i1++ ) { - /* looking for a stereo bond atom that has mapping rank r1 */ - /* found at[s1] such that rank cr1 can be mapped on at[s1] because cr1 and s1 have equal */ - /* mapping rank = r1. Check at[s1] stereo bonds */ - if ( bAtomUsedForStereo[s1] && bAtomUsedForStereo[s1] < STEREO_AT_MARK ) { - for ( k = 0, s2 = 0; k < MAX_NUM_STEREO_BONDS && (s2=(int)at[s1].stereo_bond_neighbor[k]); k ++ ) { - /* stereo bond at[s1]-at[s2] has been found */ - if ( bAtomUsedForStereo[--s2] ) { - /* stereo bonds have not been mapped. however, this check is not needed */ - int cumulene_len = BOND_CHAIN_LEN(at[s1].stereo_bond_parity[k]); - if ( cumulene_len%2 && !bAllene || /* 09-26-2003 */ - !(cumulene_len%2) && bAllene ) { /* 08-17-2003 Fix05 */ - continue; - } - iMax2 = (int)(r2 = pRankStack2[0][s2]); /* mapping rank of atom2 */ - /* Go back to canonical ranks and find an atom that has mapping rank r2 */ - /* and is connected to the atom with canonical rank cr1 (possibly by cumulene chain) */ - /* These cr1-cr2 canon. ranks possibly can be mapped on at[s1]-at[s2] stereo bond */ - for ( i2 = 1; i2 <= iMax2 && r2 == pRankStack1[0][n2=(int)pRankStack1[1][iMax2-i2]]; i2++ ) { - if ( cumulene_len ) { - int prev, next, len, j; - for ( m = 0; m < at[n1].valence; m ++ ) { - for ( prev=n1, len=0, next = (int)at[n1].neighbor[m]; len < cumulene_len; len ++ ) { - if ( at[next].valence == 2 && !at[next].num_H ) { - j = ((int)at[next].neighbor[0] == prev); - prev = next; - next = at[next].neighbor[j]; - } else { - break; /* cannot continue */ - } - } - if ( len == cumulene_len && n2 == next ) { - break; - } - } - } else { - for ( m = 0; m < at[n1].valence && n2 != (int)at[n1].neighbor[m]; m ++ ) - ; - } - if ( m < at[n1].valence && - nCanonRankFrom[n2] < cr2 && - nCanonRankFrom[n2] > canon_rank2_inp ) { - - cr2 = nCanonRankFrom[n2]; /* found a candidate for cr2 */ - } - } - } - } - } - } - if ( cr2 >= cr1 ) { - /* not found for this r1 */ - cr1 ++; - canon_rank2_inp = 0; - } else { - /* found cr2 < cr1 */ - if ( *bFirstTime ) { - *canon_rank1_min = cr1; - *canon_rank2_min = cr2; - *bFirstTime = 0; - } - break; - } - } - if ( cr1 > cr2 && cr1 <= num_atoms ) { - /* success */ - *canon_rank1 = cr1; - *canon_rank2 = cr2; - return 1; - } - return 0; -} -/******************************************************************************************/ -int NextStereoParity2Test( int *stereo_bond_parity, int *sb_parity_calc, - int nNumBest, int nNumWorse, int nNumUnkn, int nNumUndf, int nNumCalc, - int vABParityUnknown) -{ - /* sequence of (stereo_bond_parity, sb_parity_calc) pairs: - - (BEST_PARITY, BEST_PARITY) - | - (BEST_PARITY, WORSE_PARITY) - | - (WORSE_PARITY, WORSE_PARITY) (BEST_PARITY, 0) - \___________________________________________/ - | - (WORSE_PARITY, 0) - | - (AB_PARITY_UNKN, 0) - | - (AB_PARITY_UNDF, 0) - | - - Meaning: - stereo_bond_parity is the parity we are looking for - stereo_bond_parity==sb_parity_calc => parity to be calculated from canonical numbers - stereo_bond_parity!=sb_parity_calc => parity is already known - */ -get_next_parity: - switch ( *stereo_bond_parity ) { - case BEST_PARITY: - switch ( *sb_parity_calc ) { - case 0: /* BEST_PARITY(known) : (BEST_PARITY, 0) -> */ - *stereo_bond_parity = WORSE_PARITY; /* WORSE_PARITY(known): (WORSE_PARITY, 0) */ - if ( !nNumWorse ) { - goto get_next_parity; - } - break; - case BEST_PARITY: /* BEST_PARITY(calc) : (BEST_PARITY, BEST_PARITY) -> */ - *sb_parity_calc = WORSE_PARITY; /* BEST_PARITY(known): (BEST_PARITY, WORSE_PARITY) */ - if ( !nNumBest ) { - goto get_next_parity; - } - break; - case WORSE_PARITY: /* BEST_PARITY(known): (BEST_PARITY, WORSE_PARITY)-> */ - *stereo_bond_parity = WORSE_PARITY; /* WORSE_PARITY(calc): (WORSE_PARITY,WORSE_PARITY) */ - if ( !nNumCalc ) { /* added 12-17-2003 */ - goto get_next_parity; - } - break; - } - break; - case WORSE_PARITY: - switch ( *sb_parity_calc ) { - case 0: /* WORSE_PARITY(known) : (WORSE_PARITY, 0) -> */ - *stereo_bond_parity = vABParityUnknown /* AB_PARITY_UNKN */;/* AB_PARITY_UNKN(known): (AB_PARITY_UNKN, 0) */ - if ( !nNumUnkn ) { - goto get_next_parity; - } - break; - case BEST_PARITY: /* error */ - return CT_STEREOCOUNT_ERR; /* */ - case WORSE_PARITY: /* WORSE_PARITY(calc) : (WORSE_PARITY,WORSE_PARITY)-> */ - *sb_parity_calc = 0; /* WORSE_PARITY(known): (WORSE_PARITY, 0) */ - if ( !nNumWorse ) { - goto get_next_parity; - } - break; - } - break; - - case AB_PARITY_UNKN: /* AB_PARITY_UNKN(known): (AB_PARITY_UNKN, 0) -> */ - if ( *sb_parity_calc ) /* error */ - { - return CT_STEREOCOUNT_ERR; /* */ - } - *stereo_bond_parity = AB_PARITY_UNDF; /* AB_PARITY_UNDF(known): (AB_PARITY_UNDF, 0) */ - if ( !nNumUndf ) - { - return 1; /*goto next_canon_ranks;*/ - } - break; - - case AB_PARITY_UNDF: /* AB_PARITY_UNDF(known): (AB_PARITY_UNDF, 0) -> */ - if ( *sb_parity_calc ) { /* error */ - return CT_STEREOCOUNT_ERR; /* */ - } - return 1; /*goto next_canon_ranks;*/ /* next canon ranks */ - } - return 0; -} -/**************************************************************************************/ -int CompareLinCtStereoDoubleToValues( AT_STEREO_DBLE *LinearCTStereoDble, - AT_RANK at_rank_canon1, AT_RANK at_rank_canon2, U_CHAR bond_parity ) -{ - if ( LinearCTStereoDble->at_num1 CT_GREATER_THAN at_rank_canon1 ) - return 1; - if ( LinearCTStereoDble->at_num1 != at_rank_canon1 ) - return -1; - if ( LinearCTStereoDble->at_num2 CT_GREATER_THAN at_rank_canon2 ) - return 1; - if ( LinearCTStereoDble->at_num2 != at_rank_canon2 ) - return -1; - if ( LinearCTStereoDble->parity CT_GREATER_THAN bond_parity ) - return 1; - if ( LinearCTStereoDble->parity != bond_parity ) - return -1; - return 0; -} -/**************************************************************************************/ -/* Set for at[i]: */ -/* 0 if atom has no parity */ -/* STEREO_AT_MARK=8 if atom has stereo parity and has no stereo bonds */ -/* num_stereo_bonds number of stereogenic bonds adjacent to the atom <= 3 */ -void SetUseAtomForStereo( S_CHAR *bAtomUsedForStereo, sp_ATOM *at, int num_atoms ) -{ - int i, k; - memset( bAtomUsedForStereo, 0, sizeof( bAtomUsedForStereo[0] )*num_atoms ); - for ( i = 0; i < num_atoms; i ++ ) { - if ( at[i].parity ) { - for ( k = 0; k < MAX_NUM_STEREO_BONDS && at[i].stereo_bond_neighbor[k]; k ++ ) - ; - bAtomUsedForStereo[i] = k? k : STEREO_AT_MARK; - } - } -} - -/********************************************************************************* - * tree structure: one segment - * - * canon. rank - * at.no // orig. atom numbers on which the canon. - * // rank has been successfully mapped - * ... - * at.no // except the last at.no: it is not known if - * // it has been mapped until all atoms are mapped - * num.at+1 // number of atoms in this segment plus one canon. rank - * - *********************************************************************************/ - - - /******************************************************************************** -typedef struct tagCurTree { - AT_NUMB *tree; - int max_len; // allocated length of tree in sizeof(tree[0]) units - int cur_len; // currently used length - int incr_len; // reallocation increment -} CUR_TREE; - *********************************************************************************/ - - -/**************************************************************************************/ -int CurTreeAlloc( CUR_TREE *cur_tree, int num_atoms ) -{ - if ( cur_tree ) { - if ( cur_tree->tree && cur_tree->max_len > 0 && !(cur_tree->max_len % num_atoms) ) { - /* do not reallocate */ - cur_tree->cur_len = 0; - cur_tree->incr_len = num_atoms; - memset( cur_tree->tree, 0, cur_tree->max_len * sizeof(cur_tree->tree[0]) ); - return 0; /* ok */ - } - inchi_free( cur_tree->tree ); - memset( cur_tree, 0, sizeof(*cur_tree) ); - if ( cur_tree->tree = (AT_NUMB *)inchi_calloc( num_atoms, sizeof(cur_tree->tree[0]) ) ) { - cur_tree->incr_len = - cur_tree->max_len = num_atoms; - return 0; /* ok */ - } - } - return -1; /* error */ /* */ -} -/**************************************************************************************/ -int CurTreeReAlloc( CUR_TREE *cur_tree ) -{ - if ( cur_tree ) { - if ( cur_tree->tree && cur_tree->max_len > 0 && cur_tree->incr_len > 0 ) { - void *p = cur_tree->tree; - if ( cur_tree->tree = (AT_NUMB *)inchi_calloc( cur_tree->max_len + cur_tree->incr_len, sizeof(cur_tree->tree[0]) ) ) { - memcpy( cur_tree->tree, p, cur_tree->cur_len * sizeof(cur_tree->tree[0]) ); - inchi_free( p ); - cur_tree->max_len += cur_tree->incr_len; - return 0; /* ok */ - } - } - } - return -1; /* error */ /* */ -} -/**************************************************************************************/ -void CurTreeFree( CUR_TREE *cur_tree ) -{ - if ( cur_tree ) { - inchi_free( cur_tree->tree ); - memset( cur_tree, 0, sizeof(*cur_tree) ); - } -} -/**************************************************************************************/ -int CurTreeAddRank( CUR_TREE *cur_tree, AT_NUMB rank ) -{ - if ( cur_tree ) { - if ( cur_tree->cur_len + 2 > cur_tree->max_len ) { - if ( CurTreeReAlloc( cur_tree ) ) { - return -1; /* error */ /* */ - } - } - cur_tree->tree[cur_tree->cur_len++] = rank; - cur_tree->tree[cur_tree->cur_len++] = 1; - return 0; - } - return -1; /* error */ /* */ -} -/**************************************************************************************/ -int CurTreeIsLastRank( CUR_TREE *cur_tree, AT_NUMB rank ) -{ - if ( cur_tree && cur_tree->cur_len > 0 ) { - int rank_pos; - rank_pos = cur_tree->cur_len-1; - rank_pos -= cur_tree->tree[rank_pos]; - if ( rank_pos >= 0 ) { - return (rank == cur_tree->tree[rank_pos]); - } - } - return 0; /* not found */ -} -/**************************************************************************************/ -int CurTreeRemoveLastRankIfNoAtoms( CUR_TREE *cur_tree ) -{ - if ( cur_tree && cur_tree->tree && cur_tree->cur_len >= 2 ) { - if ( 1 == cur_tree->tree[ cur_tree->cur_len - 1 ] ) { - return CurTreeRemoveLastRank( cur_tree ); /* 0=> success, -1=>failed */ - } - return 1; /* cannot remove */ - } - return -1; /* error */ /* */ -} -/**************************************************************************************/ -int CurTreeAddAtom( CUR_TREE *cur_tree, int at_no ) -{ - if ( cur_tree ) { - if ( cur_tree->cur_len + 1 > cur_tree->max_len ) { - if ( CurTreeReAlloc( cur_tree ) ) { - return -1; /* error */ /* */ - } - } - if ( cur_tree->cur_len > 0 ) { - AT_NUMB new_len = cur_tree->tree[ --cur_tree->cur_len ] + 1; - cur_tree->tree[cur_tree->cur_len++] = (AT_NUMB)at_no; - cur_tree->tree[cur_tree->cur_len++] = new_len; - return 0; - } - } - return -1; -} -/**************************************************************************************/ -void CurTreeKeepLastAtomsOnly( CUR_TREE *cur_tree, int tpos, int shift ) -{ /* on first entry: shift = 1; other values may occur in subsequent recursion */ - /* cur_tree[cur_tree->cur_len - shift] is the length of a segment */ - /* action: remove all atoms except the last from all segments - that have length value positon to the right from tpos */ - int cur_length_pos; - if ( cur_tree && cur_tree->tree && (cur_length_pos = cur_tree->cur_len - shift) > tpos ) { - if ( cur_tree->tree[ cur_length_pos ] > 2 ) { - /* current segment contains more than 1 atom. Leave in the segment: rank, the last atom, length value */ - /* subtract (old segment length)-(new segment length) from the tree length */ - /* actual segment length including segment length value = (cur_tree->tree[cur_length_pos]+1) */ - cur_tree->cur_len -= (int)cur_tree->tree[ cur_length_pos ] - 2; - memmove( cur_tree->tree + cur_length_pos - cur_tree->tree[ cur_length_pos ] + 1, /* 1st atom pos */ - cur_tree->tree + cur_length_pos - 1, /* last atom in the current segment position */ - (shift+1)*sizeof(cur_tree->tree[0]) ); - /* (current segment length) distance from the last tree element has not changed */ - cur_tree->tree[ cur_tree->cur_len - shift] = 2; - /* add 3 to move to the previous segment length position */ - shift += 3; /* lenghth = 3 accounts for 3 currently present. segment items: - (1) the last atom, (2) rank, (3) length value */ - } else { - shift += (int)cur_tree->tree[ cur_length_pos ] + 1; /* cur_tree->cur_len - (previous segment length position) */ - } - CurTreeKeepLastAtomsOnly( cur_tree, tpos, shift ); - } -} -/**************************************************************************************/ -int CurTreeRemoveIfLastAtom( CUR_TREE *cur_tree, int at_no ) -{ - if ( cur_tree && cur_tree->tree && cur_tree->cur_len > 2 ) { - AT_NUMB len = cur_tree->tree[ cur_tree->cur_len - 1 ]; - if ( len >= 2 && (int)cur_tree->tree[ cur_tree->cur_len - 2 ] == at_no ) { - cur_tree->tree[--cur_tree->cur_len-1] = len-1; - return 0; - } - return 1; /* not found */ - } - return -1; /* error */ /* */ -} -/**************************************************************************************/ -int CurTreeGetPos( CUR_TREE *cur_tree ) -{ - if ( cur_tree ) { - return cur_tree->cur_len; - } - return -1; -} -/**************************************************************************************/ -int CurTreeSetPos( CUR_TREE *cur_tree, int len ) -{ - if ( cur_tree ) { - cur_tree->cur_len = len; - return 0; - } - return -1; -} -/**************************************************************************************/ -int CurTreeRemoveLastRank( CUR_TREE *cur_tree ) -{ - if ( cur_tree && cur_tree->cur_len > 0 ) { - cur_tree->cur_len -= cur_tree->tree[cur_tree->cur_len-1]+1; - if ( cur_tree->cur_len >= 0 ) { - return 0; - } - } - return -1; -} -/**************************************************************************************/ -/* Find if the atom is equivalent to already successfully tried current atoms */ -int CurTreeIsLastAtomEqu( CUR_TREE *cur_tree, int at_no, AT_NUMB *nSymmStereo ) -{ - if ( cur_tree && cur_tree->tree && nSymmStereo && cur_tree->cur_len > 1 ) { - AT_NUMB nEq = nSymmStereo[at_no]; - int end = cur_tree->cur_len-1; - int len = cur_tree->tree[end]-1; - for ( ; len > 0; len -- ) { - if ( nSymmStereo[(int)cur_tree->tree[end-len]] == nEq ) - return 1; - } - return 0; - } - return -1; /* error */ /* */ -} -#ifdef NEVER /* not used */ -/**************************************************************************************/ -int CurTreeRemoveLastAtom( CUR_TREE *cur_tree ) -{ - if ( cur_tree && cur_tree->tree && cur_tree->cur_len > 2 ) { - AT_NUMB len = cur_tree->tree[ --cur_tree->cur_len ]; - if ( len >= 2 ) { - cur_tree->tree[cur_tree->cur_len-1] = len-1; - return 0; - } - } - return -1; -} -/**************************************************************************************/ -int CurTreeReplaceLastRank( CUR_TREE *cur_tree, AT_NUMB rank ) -{ - if ( !CurTreeRemoveLastRank( cur_tree ) ) { - return CurTreeAddRank( cur_tree, rank ); - } - return -1; -} -/**************************************************************************************/ -/* returns cur_tree->cur_len for the block containing the rank */ -int CurTreeFindTheRankPos( CUR_TREE *cur_tree, AT_NUMB rank ) -{ - int i, k; - if ( cur_tree && cur_tree->tree && (i=cur_tree->cur_len) > 0 ) { - while( 0 <= (k = i-(int)cur_tree->tree[i-1]-1) ) { - if ( cur_tree->tree[k] == rank ) { - return i; - } - i = k; - } - } - return -1; /* error */ /* */ -} -#endif - - +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include + +#include "mode.h" +#include "ichicomn.h" + +/**************************************************************************************/ +/* Check if all equivalent to cr1 possibly stereogenic atoms: */ +/* 1) have KNOWN parity, and */ +/* 2) their parities are same */ +/**************************************************************************************/ +int All_SC_Same( AT_RANK canon_rank1, /* canonical number */ + const ppAT_RANK pRankStack1, + const ppAT_RANK pRankStack2, + const AT_RANK *nAtomNumberCanonFrom, + const sp_ATOM *at ) +{ + int n1 = (int)nAtomNumberCanonFrom[(int)canon_rank1-1]; + AT_RANK r1 = pRankStack1[0][n1]; + int iMax1 = (int)r1; + int i1, s1; + int bFound=0, stereo_atom_parity=-1; + + /* find one stereo atom such that canon_rank1 can be mapped on it */ + for ( i1 = 1; i1 <= iMax1 && r1 == pRankStack2[0][s1=(int)pRankStack2[1][iMax1-i1]]; i1++ ) { + if ( at[s1].stereo_bond_neighbor[0] ) { + bFound=0; /* at[s1] is not sp3-stereogenic: it belongs to a stereobond */ + break; + } else + if ( i1 == 1 ) { + stereo_atom_parity = PARITY_VAL(at[s1].stereo_atom_parity); + if ( !ATOM_PARITY_KNOWN(stereo_atom_parity) ) { + bFound=0; /* at[s1] does not have a KNOWN parity */ + break; + } + } else + if ( stereo_atom_parity != PARITY_VAL(at[s1].stereo_atom_parity) ) { + bFound=0; /* two equivalent atoms have different parities */ + break; + } + bFound ++; + } + return bFound; +} +/**************************************************************************************/ +/* get next available (not mapped yet) rank for a stereo center atom */ +int Next_SC_At_CanonRank2( AT_RANK *canon_rank1, /* 1st call input: largest canon number mapped so far or 0 */ + /* output: suggested canon. rank > than input if success */ + AT_RANK *canon_rank1_min, /* 1st call:0 next calls: first tried canon. number */ + int *bFirstTime, /* 1 at the time of the 1st call */ + S_CHAR *bAtomUsedForStereo, /* STEREO_AT_MARK if the atom has not been mapped yet */ + const ppAT_RANK pRankStack1, /* mapping ranks/sort order of atoms with canon. numbers (from) */ + const ppAT_RANK pRankStack2, /* mapping ranks/sort order of atoms with stereo (to) */ + const AT_RANK *nAtomNumberCanonFrom, /* sorted order of the canon. numbers */ + int num_atoms ) +{ + AT_RANK canon_rank1_inp = *canon_rank1; + AT_RANK cr1; /* canonical rank (canonical number) */ + AT_RANK r1; /* mapping rank */ + int n1; /* ord. number of an atom with the canon. number */ + int s1; /* ord. number of an atom with stereo */ + int i1, bFound=0; + int iMax1; + + if ( canon_rank1_inp < *canon_rank1_min ) { + canon_rank1_inp = *canon_rank1_min; + } else + if ( canon_rank1_inp < 1 ) { + canon_rank1_inp = 1; + } else { + canon_rank1_inp ++; /* next canonical rank */ + } + cr1 = canon_rank1_inp; + + while ( (int) cr1 <= num_atoms ) { + n1 = (int)nAtomNumberCanonFrom[(int)cr1-1]; /* atom1 (which has canon. rank cr1) ord. number */ + iMax1 = (int)(r1 = pRankStack1[0][n1]); /* mapping rank of atom1 */ + /* find atoms "to" to which the canon. number can be mapped; they have mapping rank r1, number s1 */ + for ( i1 = 1; i1 <= iMax1 && r1 == pRankStack2[0][s1=(int)pRankStack2[1][iMax1-i1]]; i1++ ) { + /* looking for a stereo center atom that has mapping rank r1 */ + if ( bAtomUsedForStereo[s1] == STEREO_AT_MARK ) { + /* found a sterogenic atom that has not been mapped yet */ + bFound = 1; + break; + } + } + if ( bFound ) { + /* one stereogenic not mapped yet atom "to" has been found */ + if ( *bFirstTime ) { + *canon_rank1_min = cr1; + *bFirstTime = 0; + } + break; + } else { + /* a not mapped yet stereogenic atom has not found */ + /* for the mapping rank r1 defined by the canonical rank cr1; try next cr1 */ + cr1 ++; + } + } + if ( bFound ) { + /* success */ + *canon_rank1 = cr1; + return 1; + } + return 0; +} +/**********************************************************************/ +int CompareLinCtStereoDble ( AT_STEREO_DBLE *LinearCTStereoDble1, int nLenLinearCTStereoDble1, + AT_STEREO_DBLE *LinearCTStereoDble2, int nLenLinearCTStereoDble2 ) +{ + int i, num, ret = 0; + + /* compare double bonds */ + if ( LinearCTStereoDble1 && LinearCTStereoDble2 ) { + num = inchi_min(nLenLinearCTStereoDble1, nLenLinearCTStereoDble2); + for ( i = 0; i < num; i ++ ) { + if ( ret = (int)LinearCTStereoDble1[i].at_num1 - (int)LinearCTStereoDble2[i].at_num1 ) + break; + if ( ret = (int)LinearCTStereoDble1[i].at_num2 - (int)LinearCTStereoDble2[i].at_num2 ) + break; + if ( ret = (int)LinearCTStereoDble1[i].parity - (int)LinearCTStereoDble2[i].parity ) + break; + } + if ( !ret ) { + ret = nLenLinearCTStereoDble1 - nLenLinearCTStereoDble2; + } + } else + if ( LinearCTStereoDble1 && nLenLinearCTStereoDble1 > 0 ) { + ret = 1; + } else + if ( LinearCTStereoDble2 && nLenLinearCTStereoDble2 > 0 ) { + ret = -1; + } + return ret; +} +/**********************************************************************/ +int CompareLinCtStereoCarb ( AT_STEREO_CARB *LinearCTStereoCarb1, int nLenLinearCTStereoCarb1, + AT_STEREO_CARB *LinearCTStereoCarb2, int nLenLinearCTStereoCarb2 ) +{ + int i, num, ret = 0; + + /* compare stereocenters */ + if ( LinearCTStereoCarb1 && LinearCTStereoCarb2 ) { + num = inchi_min(nLenLinearCTStereoCarb1, nLenLinearCTStereoCarb2); + for ( i = 0; i < num; i ++ ) { + if ( ret = (int)LinearCTStereoCarb1[i].at_num - (int)LinearCTStereoCarb2[i].at_num ) + break; + if ( ret = (int)LinearCTStereoCarb1[i].parity - (int)LinearCTStereoCarb2[i].parity ) + break; + } + if ( !ret ) { + ret = nLenLinearCTStereoCarb1 - nLenLinearCTStereoCarb2; + } + } else + if ( LinearCTStereoCarb1 && nLenLinearCTStereoCarb1 > 0 ) { + ret = 1; + } else + if ( LinearCTStereoCarb2 && nLenLinearCTStereoCarb2 > 0 ) { + ret = -1; + } + + return ret; +} +/**********************************************************************/ +int CompareLinCtStereo ( AT_STEREO_DBLE *LinearCTStereoDble1, int nLenLinearCTStereoDble1, + AT_STEREO_CARB *LinearCTStereoCarb1, int nLenLinearCTStereoCarb1, + AT_STEREO_DBLE *LinearCTStereoDble2, int nLenLinearCTStereoDble2, + AT_STEREO_CARB *LinearCTStereoCarb2, int nLenLinearCTStereoCarb2 ) +{ + int ret; + + /* compare double bonds */ + ret = CompareLinCtStereoDble ( LinearCTStereoDble1, nLenLinearCTStereoDble1, + LinearCTStereoDble2, nLenLinearCTStereoDble2 ); + if ( !ret ) { + ret = CompareLinCtStereoCarb ( LinearCTStereoCarb1, nLenLinearCTStereoCarb1, + LinearCTStereoCarb2, nLenLinearCTStereoCarb2 ); + } + return ret; +} +/**************************************************************************************/ +int CompareLinCtStereoAtomToValues( AT_STEREO_CARB *LinearCTStereoCarb, + AT_RANK at_rank_canon1, U_CHAR parity ) +{ + if ( LinearCTStereoCarb->at_num CT_GREATER_THAN at_rank_canon1 ) + return 1; + if ( LinearCTStereoCarb->at_num != at_rank_canon1 ) + return -1; + if ( LinearCTStereoCarb->parity CT_GREATER_THAN parity ) + return 1; + if ( LinearCTStereoCarb->parity != parity ) + return -1; + return 0; +} +/**************************************************************************************/ +/* Find atom number from the mapping rank and return 1, or */ +/* if the mapping rank is tied and the atom number is not unique then return 0 */ +int bUniqueAtNbrFromMappingRank( AT_RANK **pRankStack, AT_RANK nAtRank, AT_NUMB *nAtNumber ) +{ + int r = (int)nAtRank-1; + AT_NUMB i = pRankStack[1][r]; + if ( nAtRank == pRankStack[0][(int)i] && + (!r || nAtRank != pRankStack[0][pRankStack[1][r-1]]) + ) { + *nAtNumber = i; + return 1; + } + return 0; +} +/**************************************************************************************/ +/* Get minimal set (class) representative and partially compress the partitioning */ +/* mcr = minimal class representative. */ +AT_RANK nGetMcr( AT_RANK *nEqArray, AT_RANK n ) +{ + AT_RANK n1, n2, mcr; /* recursive version is much shorter. */ + + n1=nEqArray[(int)n]; + if ( n == n1 ) { + return n; + } + /* 1st pass: find mcr */ + while ( n1 != (n2=nEqArray[(int)n1])) { + n1 = n2; + } + /* 2nd pass: copy mcr to each element of the set starting from nEqArray[n] */ + mcr = n1; + n1 = n; + while ( /*n1*/ mcr != (n2=nEqArray[(int)n1]) ) { + nEqArray[(int)n1]=mcr; + n1 = n2; + } + return ( mcr ); +} +/**************************************************************************************/ +/* Join 2 sets (classes) that have members n1 and n2 */ +int nJoin2Mcrs( AT_RANK *nEqArray, AT_RANK n1, AT_RANK n2 ) +{ + n1 = nGetMcr( nEqArray, n1 ); + n2 = nGetMcr( nEqArray, n2 ); + if ( n1 < n2 ) { + nEqArray[n2] = n1; + return 1; /* a change has been made */ + } + if ( n2 < n1 ) { + nEqArray[n1] = n2; + return 1; /* a change has been made */ + } + return 0; /* no changes */ +} + +/********************************************************************************* + * For all pairs of atoms that are: * + * (a) connected by a possibly stereogenic bond * + * (b) "equivalent" at this point to canon_rank1-canon_rank2 : * + * Check if they: * + * 1) are connected by a stereo bond or cumulene bonds of the same length * + * 2) have KNOWN parity, and * + * 3) their parities are same * + *********************************************************************************/ +int All_SB_Same( AT_RANK canon_rank1, + AT_RANK canon_rank2, /* canonical numbers */ + const ppAT_RANK pRankStack1, + const ppAT_RANK pRankStack2, + const AT_RANK *nAtomNumberCanonFrom, + sp_ATOM *at ) +{ + int n1 = (int)nAtomNumberCanonFrom[(int)canon_rank1-1]; /* at1 has canon_rank1 */ + int n2 = (int)nAtomNumberCanonFrom[(int)canon_rank2-1]; /* at2 has canon_rank2 */ + AT_RANK r1 = pRankStack1[0][n1]; /* at1 mapping rank */ + AT_RANK r2 = pRankStack1[0][n2]; /* at2 mapping rank */ + AT_RANK rNeigh1, rNeigh2; + int iMax1 = (int)r1; + /* int iMax2 = (int)r2; */ + int i1, i2, s1=0, s2=0, k1=0, k2, m, k, num_equal; + int bNotFound=1, cumulene_len, stereo_bond_parity; + + /* at the first atom that possibly may have canon_rank1 find one stereo bond such that */ + /* canon_rank1-canon_rank2 possibly may be mapped on it */ + for ( i1 = 1; i1 <= iMax1 && r1 == pRankStack2[0][s1=(int)pRankStack2[1][iMax1-i1]]; i1++ ) { + /* at[n1] may be possible to map on at[s1] */ + for ( k1 = 0, s2= 0, bNotFound=1; + k1 < MAX_NUM_STEREO_BONDS && (s2=(int)at[s1].stereo_bond_neighbor[k1]) && + (bNotFound = (r2 != pRankStack2[0][--s2])); k1 ++ ) + ; /* continue until the 1st at[s2] (to which at[n2] may be mapped) have been found */ + if ( !bNotFound ) { + break; /* stop at 1st found */ + } + } + if ( bNotFound ) { + return -1; /* error: no mapping exists */ + } + for ( k2 = 0, m = 0; k2 < MAX_NUM_STEREO_BONDS && (m=(int)at[s2].stereo_bond_neighbor[k2]) && m-1 != s1; k2 ++ ) + ; + if ( m-1 != s1 ) { + return -1; /* program error: stereo bond in opposite direction not found */ + } + stereo_bond_parity = at[s1].stereo_bond_parity[k1]; + if ( !PARITY_KNOWN(stereo_bond_parity) ) { + return 0; + } + cumulene_len = BOND_CHAIN_LEN(stereo_bond_parity); + rNeigh1 = pRankStack2[0][(int)at[s1].neighbor[(int)at[s1].stereo_bond_ord[k1]]]; + rNeigh2 = pRankStack2[0][(int)at[s2].neighbor[(int)at[s2].stereo_bond_ord[k2]]]; + + num_equal = 0; + /* Search among ALL neighbors because sometimes a stereo bond may be mapped on a non-stereo bond. */ + /* If is so then return 0: not all mappings are stereo-equivalent */ + for ( s1 = 1; s1 <= iMax1 && r1 == pRankStack2[0][i1=(int)pRankStack2[1][iMax1-s1]]; s1++ ) { + for ( k = 0; k < at[i1].valence; k ++ ) { + n1 = at[i1].neighbor[k]; + if ( rNeigh1 != pRankStack2[0][n1] ) { + continue; /* wrong neighbor */ + } + if ( cumulene_len ) { + int prev, next, len, j; + for ( prev=i1, len=0, next = n1; len < cumulene_len; len ++ ) { + if ( at[next].valence == 2 && !at[next].num_H ) { + j = ((int)at[next].neighbor[0] == prev); + prev = next; + next = at[next].neighbor[j]; + } else { + break; /* cannot continue */ + } + } + if ( len != cumulene_len || + r2 != pRankStack2[0][next] || + rNeigh2 != pRankStack2[0][prev] ) { + /* cumulene chain not found */ + continue; + } + i2 = next; + } else { + i2 = n1; + } + /* find if a stereogenic bond between at[i1]-at[i2] exists */ + for ( k1 = 0; k1 < MAX_NUM_STEREO_BONDS && + (m=(int)at[i1].stereo_bond_neighbor[k1]) && m-1 != i2; k1 ++ ) + ; + if ( m-1 != i2 ) { + return 0; + } + for ( k2 = 0; k2 < MAX_NUM_STEREO_BONDS && + (m=(int)at[i2].stereo_bond_neighbor[k2]) && m-1 != i1; k2 ++ ) + ; + if ( m-1 != i1 ) { + return 0; + } + if ( at[i1].stereo_bond_parity[k1] != at[i2].stereo_bond_parity[k2] ) { + return -1; /* program error */ + } + if ( stereo_bond_parity != at[i1].stereo_bond_parity[k1] ) { + return 0; + } + num_equal ++; + } + } + return num_equal; +} + +/**************************************************************************************/ +/* get min. ranks for the stereo bond atoms */ +int Next_SB_At_CanonRanks2( AT_RANK *canon_rank1, AT_RANK *canon_rank2, /* canonical numbers */ + AT_RANK *canon_rank1_min, AT_RANK *canon_rank2_min, + int *bFirstTime, S_CHAR *bAtomUsedForStereo, + const ppAT_RANK pRankStack1, const ppAT_RANK pRankStack2, + const AT_RANK *nCanonRankFrom, const AT_RANK *nAtomNumberCanonFrom, + const sp_ATOM *at, int num_atoms, int bAllene ) +{ + AT_RANK canon_rank1_inp = *canon_rank1; + AT_RANK canon_rank2_inp = *canon_rank2; + AT_RANK cr1, cr2; /* canonical ranks (canonical numbers) */ + AT_RANK r1, r2; /* mapping ranks */ + int n1, n2; /* ord. numbers of atoms with stereo */ + int s1, s2; /* ord. numbers of atoms with canon. numbers */ + int i1, i2, k, m; + int iMax1, iMax2; + + if ( canon_rank1_inp < *canon_rank1_min || + canon_rank1_inp == *canon_rank1_min && + canon_rank2_inp < *canon_rank2_min ) { + + canon_rank1_inp = *canon_rank1_min; + canon_rank2_inp = *canon_rank2_min; + } else + if ( canon_rank1_inp < 2 ) { + canon_rank1_inp = 2; + canon_rank2_inp = 0; + } + cr1 = canon_rank1_inp; + cr2 = num_atoms; /* initialize. 1/8/2002 */ + while ( (int) cr1 <= num_atoms ) { + cr2 = cr1; + n1 = (int)nAtomNumberCanonFrom[(int)cr1-1]; /* atom1=at[n1] (which has canon. rank) ord. number */ + iMax1 = (int)(r1 = pRankStack1[0][n1]); /* mapping rank of atom1 */ + for ( i1 = 1; i1 <= iMax1 && r1 == pRankStack2[0][s1=(int)pRankStack2[1][iMax1-i1]]; i1++ ) { + /* looking for a stereo bond atom that has mapping rank r1 */ + /* found at[s1] such that rank cr1 can be mapped on at[s1] because cr1 and s1 have equal */ + /* mapping rank = r1. Check at[s1] stereo bonds */ + if ( bAtomUsedForStereo[s1] && bAtomUsedForStereo[s1] < STEREO_AT_MARK ) { + for ( k = 0, s2 = 0; k < MAX_NUM_STEREO_BONDS && (s2=(int)at[s1].stereo_bond_neighbor[k]); k ++ ) { + /* stereo bond at[s1]-at[s2] has been found */ + if ( bAtomUsedForStereo[--s2] ) { + /* stereo bonds have not been mapped. however, this check is not needed */ + int cumulene_len = BOND_CHAIN_LEN(at[s1].stereo_bond_parity[k]); + if ( cumulene_len%2 && !bAllene || /* 09-26-2003 */ + !(cumulene_len%2) && bAllene ) { /* 08-17-2003 Fix05 */ + continue; + } + iMax2 = (int)(r2 = pRankStack2[0][s2]); /* mapping rank of atom2 */ + /* Go back to canonical ranks and find an atom that has mapping rank r2 */ + /* and is connected to the atom with canonical rank cr1 (possibly by cumulene chain) */ + /* These cr1-cr2 canon. ranks possibly can be mapped on at[s1]-at[s2] stereo bond */ + for ( i2 = 1; i2 <= iMax2 && r2 == pRankStack1[0][n2=(int)pRankStack1[1][iMax2-i2]]; i2++ ) { + if ( cumulene_len ) { + int prev, next, len, j; + for ( m = 0; m < at[n1].valence; m ++ ) { + for ( prev=n1, len=0, next = (int)at[n1].neighbor[m]; len < cumulene_len; len ++ ) { + if ( at[next].valence == 2 && !at[next].num_H ) { + j = ((int)at[next].neighbor[0] == prev); + prev = next; + next = at[next].neighbor[j]; + } else { + break; /* cannot continue */ + } + } + if ( len == cumulene_len && n2 == next ) { + break; + } + } + } else { + for ( m = 0; m < at[n1].valence && n2 != (int)at[n1].neighbor[m]; m ++ ) + ; + } + if ( m < at[n1].valence && + nCanonRankFrom[n2] < cr2 && + nCanonRankFrom[n2] > canon_rank2_inp ) { + + cr2 = nCanonRankFrom[n2]; /* found a candidate for cr2 */ + } + } + } + } + } + } + if ( cr2 >= cr1 ) { + /* not found for this r1 */ + cr1 ++; + canon_rank2_inp = 0; + } else { + /* found cr2 < cr1 */ + if ( *bFirstTime ) { + *canon_rank1_min = cr1; + *canon_rank2_min = cr2; + *bFirstTime = 0; + } + break; + } + } + if ( cr1 > cr2 && cr1 <= num_atoms ) { + /* success */ + *canon_rank1 = cr1; + *canon_rank2 = cr2; + return 1; + } + return 0; +} +/******************************************************************************************/ +int NextStereoParity2Test( int *stereo_bond_parity, int *sb_parity_calc, + int nNumBest, int nNumWorse, int nNumUnkn, int nNumUndf, int nNumCalc, + int vABParityUnknown) +{ + /* sequence of (stereo_bond_parity, sb_parity_calc) pairs: + + (BEST_PARITY, BEST_PARITY) + | + (BEST_PARITY, WORSE_PARITY) + | + (WORSE_PARITY, WORSE_PARITY) (BEST_PARITY, 0) + \___________________________________________/ + | + (WORSE_PARITY, 0) + | + (AB_PARITY_UNKN, 0) + | + (AB_PARITY_UNDF, 0) + | + + Meaning: + stereo_bond_parity is the parity we are looking for + stereo_bond_parity==sb_parity_calc => parity to be calculated from canonical numbers + stereo_bond_parity!=sb_parity_calc => parity is already known + */ +get_next_parity: + switch ( *stereo_bond_parity ) { + case BEST_PARITY: + switch ( *sb_parity_calc ) { + case 0: /* BEST_PARITY(known) : (BEST_PARITY, 0) -> */ + *stereo_bond_parity = WORSE_PARITY; /* WORSE_PARITY(known): (WORSE_PARITY, 0) */ + if ( !nNumWorse ) { + goto get_next_parity; + } + break; + case BEST_PARITY: /* BEST_PARITY(calc) : (BEST_PARITY, BEST_PARITY) -> */ + *sb_parity_calc = WORSE_PARITY; /* BEST_PARITY(known): (BEST_PARITY, WORSE_PARITY) */ + if ( !nNumBest ) { + goto get_next_parity; + } + break; + case WORSE_PARITY: /* BEST_PARITY(known): (BEST_PARITY, WORSE_PARITY)-> */ + *stereo_bond_parity = WORSE_PARITY; /* WORSE_PARITY(calc): (WORSE_PARITY,WORSE_PARITY) */ + if ( !nNumCalc ) { /* added 12-17-2003 */ + goto get_next_parity; + } + break; + } + break; + case WORSE_PARITY: + switch ( *sb_parity_calc ) { + case 0: /* WORSE_PARITY(known) : (WORSE_PARITY, 0) -> */ + *stereo_bond_parity = vABParityUnknown /* AB_PARITY_UNKN */;/* AB_PARITY_UNKN(known): (AB_PARITY_UNKN, 0) */ + if ( !nNumUnkn ) { + goto get_next_parity; + } + break; + case BEST_PARITY: /* error */ + return CT_STEREOCOUNT_ERR; /* */ + case WORSE_PARITY: /* WORSE_PARITY(calc) : (WORSE_PARITY,WORSE_PARITY)-> */ + *sb_parity_calc = 0; /* WORSE_PARITY(known): (WORSE_PARITY, 0) */ + if ( !nNumWorse ) { + goto get_next_parity; + } + break; + } + break; + + case AB_PARITY_UNKN: /* AB_PARITY_UNKN(known): (AB_PARITY_UNKN, 0) -> */ + if ( *sb_parity_calc ) /* error */ + { + return CT_STEREOCOUNT_ERR; /* */ + } + *stereo_bond_parity = AB_PARITY_UNDF; /* AB_PARITY_UNDF(known): (AB_PARITY_UNDF, 0) */ + if ( !nNumUndf ) + { + return 1; /*goto next_canon_ranks;*/ + } + break; + + case AB_PARITY_UNDF: /* AB_PARITY_UNDF(known): (AB_PARITY_UNDF, 0) -> */ + if ( *sb_parity_calc ) { /* error */ + return CT_STEREOCOUNT_ERR; /* */ + } + return 1; /*goto next_canon_ranks;*/ /* next canon ranks */ + } + return 0; +} +/**************************************************************************************/ +int CompareLinCtStereoDoubleToValues( AT_STEREO_DBLE *LinearCTStereoDble, + AT_RANK at_rank_canon1, AT_RANK at_rank_canon2, U_CHAR bond_parity ) +{ + if ( LinearCTStereoDble->at_num1 CT_GREATER_THAN at_rank_canon1 ) + return 1; + if ( LinearCTStereoDble->at_num1 != at_rank_canon1 ) + return -1; + if ( LinearCTStereoDble->at_num2 CT_GREATER_THAN at_rank_canon2 ) + return 1; + if ( LinearCTStereoDble->at_num2 != at_rank_canon2 ) + return -1; + if ( LinearCTStereoDble->parity CT_GREATER_THAN bond_parity ) + return 1; + if ( LinearCTStereoDble->parity != bond_parity ) + return -1; + return 0; +} +/**************************************************************************************/ +/* Set for at[i]: */ +/* 0 if atom has no parity */ +/* STEREO_AT_MARK=8 if atom has stereo parity and has no stereo bonds */ +/* num_stereo_bonds number of stereogenic bonds adjacent to the atom <= 3 */ +void SetUseAtomForStereo( S_CHAR *bAtomUsedForStereo, sp_ATOM *at, int num_atoms ) +{ + int i, k; + memset( bAtomUsedForStereo, 0, sizeof( bAtomUsedForStereo[0] )*num_atoms ); + for ( i = 0; i < num_atoms; i ++ ) { + if ( at[i].parity ) { + for ( k = 0; k < MAX_NUM_STEREO_BONDS && at[i].stereo_bond_neighbor[k]; k ++ ) + ; + bAtomUsedForStereo[i] = k? k : STEREO_AT_MARK; + } + } +} + +/********************************************************************************* + * tree structure: one segment + * + * canon. rank + * at.no // orig. atom numbers on which the canon. + * // rank has been successfully mapped + * ... + * at.no // except the last at.no: it is not known if + * // it has been mapped until all atoms are mapped + * num.at+1 // number of atoms in this segment plus one canon. rank + * + *********************************************************************************/ + + + /******************************************************************************** +typedef struct tagCurTree { + AT_NUMB *tree; + int max_len; // allocated length of tree in sizeof(tree[0]) units + int cur_len; // currently used length + int incr_len; // reallocation increment +} CUR_TREE; + *********************************************************************************/ + + +/**************************************************************************************/ +int CurTreeAlloc( CUR_TREE *cur_tree, int num_atoms ) +{ + if ( cur_tree ) { + if ( cur_tree->tree && cur_tree->max_len > 0 && !(cur_tree->max_len % num_atoms) ) { + /* do not reallocate */ + cur_tree->cur_len = 0; + cur_tree->incr_len = num_atoms; + memset( cur_tree->tree, 0, cur_tree->max_len * sizeof(cur_tree->tree[0]) ); + return 0; /* ok */ + } + inchi_free( cur_tree->tree ); + memset( cur_tree, 0, sizeof(*cur_tree) ); + if ( cur_tree->tree = (AT_NUMB *)inchi_calloc( num_atoms, sizeof(cur_tree->tree[0]) ) ) { + cur_tree->incr_len = + cur_tree->max_len = num_atoms; + return 0; /* ok */ + } + } + return -1; /* error */ /* */ +} +/**************************************************************************************/ +int CurTreeReAlloc( CUR_TREE *cur_tree ) +{ + if ( cur_tree ) { + if ( cur_tree->tree && cur_tree->max_len > 0 && cur_tree->incr_len > 0 ) { + void *p = cur_tree->tree; + if ( cur_tree->tree = (AT_NUMB *)inchi_calloc( cur_tree->max_len + cur_tree->incr_len, sizeof(cur_tree->tree[0]) ) ) { + memcpy( cur_tree->tree, p, cur_tree->cur_len * sizeof(cur_tree->tree[0]) ); + inchi_free( p ); + cur_tree->max_len += cur_tree->incr_len; + return 0; /* ok */ + } + } + } + return -1; /* error */ /* */ +} +/**************************************************************************************/ +void CurTreeFree( CUR_TREE *cur_tree ) +{ + if ( cur_tree ) { + inchi_free( cur_tree->tree ); + memset( cur_tree, 0, sizeof(*cur_tree) ); + } +} +/**************************************************************************************/ +int CurTreeAddRank( CUR_TREE *cur_tree, AT_NUMB rank ) +{ + if ( cur_tree ) { + if ( cur_tree->cur_len + 2 > cur_tree->max_len ) { + if ( CurTreeReAlloc( cur_tree ) ) { + return -1; /* error */ /* */ + } + } + cur_tree->tree[cur_tree->cur_len++] = rank; + cur_tree->tree[cur_tree->cur_len++] = 1; + return 0; + } + return -1; /* error */ /* */ +} +/**************************************************************************************/ +int CurTreeIsLastRank( CUR_TREE *cur_tree, AT_NUMB rank ) +{ + if ( cur_tree && cur_tree->cur_len > 0 ) { + int rank_pos; + rank_pos = cur_tree->cur_len-1; + rank_pos -= cur_tree->tree[rank_pos]; + if ( rank_pos >= 0 ) { + return (rank == cur_tree->tree[rank_pos]); + } + } + return 0; /* not found */ +} +/**************************************************************************************/ +int CurTreeRemoveLastRankIfNoAtoms( CUR_TREE *cur_tree ) +{ + if ( cur_tree && cur_tree->tree && cur_tree->cur_len >= 2 ) { + if ( 1 == cur_tree->tree[ cur_tree->cur_len - 1 ] ) { + return CurTreeRemoveLastRank( cur_tree ); /* 0=> success, -1=>failed */ + } + return 1; /* cannot remove */ + } + return -1; /* error */ /* */ +} +/**************************************************************************************/ +int CurTreeAddAtom( CUR_TREE *cur_tree, int at_no ) +{ + if ( cur_tree ) { + if ( cur_tree->cur_len + 1 > cur_tree->max_len ) { + if ( CurTreeReAlloc( cur_tree ) ) { + return -1; /* error */ /* */ + } + } + if ( cur_tree->cur_len > 0 ) { + AT_NUMB new_len = cur_tree->tree[ --cur_tree->cur_len ] + 1; + cur_tree->tree[cur_tree->cur_len++] = (AT_NUMB)at_no; + cur_tree->tree[cur_tree->cur_len++] = new_len; + return 0; + } + } + return -1; +} +/**************************************************************************************/ +void CurTreeKeepLastAtomsOnly( CUR_TREE *cur_tree, int tpos, int shift ) +{ /* on first entry: shift = 1; other values may occur in subsequent recursion */ + /* cur_tree[cur_tree->cur_len - shift] is the length of a segment */ + /* action: remove all atoms except the last from all segments + that have length value positon to the right from tpos */ + int cur_length_pos; + if ( cur_tree && cur_tree->tree && (cur_length_pos = cur_tree->cur_len - shift) > tpos ) { + if ( cur_tree->tree[ cur_length_pos ] > 2 ) { + /* current segment contains more than 1 atom. Leave in the segment: rank, the last atom, length value */ + /* subtract (old segment length)-(new segment length) from the tree length */ + /* actual segment length including segment length value = (cur_tree->tree[cur_length_pos]+1) */ + cur_tree->cur_len -= (int)cur_tree->tree[ cur_length_pos ] - 2; + memmove( cur_tree->tree + cur_length_pos - cur_tree->tree[ cur_length_pos ] + 1, /* 1st atom pos */ + cur_tree->tree + cur_length_pos - 1, /* last atom in the current segment position */ + (shift+1)*sizeof(cur_tree->tree[0]) ); + /* (current segment length) distance from the last tree element has not changed */ + cur_tree->tree[ cur_tree->cur_len - shift] = 2; + /* add 3 to move to the previous segment length position */ + shift += 3; /* lenghth = 3 accounts for 3 currently present. segment items: + (1) the last atom, (2) rank, (3) length value */ + } else { + shift += (int)cur_tree->tree[ cur_length_pos ] + 1; /* cur_tree->cur_len - (previous segment length position) */ + } + CurTreeKeepLastAtomsOnly( cur_tree, tpos, shift ); + } +} +/**************************************************************************************/ +int CurTreeRemoveIfLastAtom( CUR_TREE *cur_tree, int at_no ) +{ + if ( cur_tree && cur_tree->tree && cur_tree->cur_len > 2 ) { + AT_NUMB len = cur_tree->tree[ cur_tree->cur_len - 1 ]; + if ( len >= 2 && (int)cur_tree->tree[ cur_tree->cur_len - 2 ] == at_no ) { + cur_tree->tree[--cur_tree->cur_len-1] = len-1; + return 0; + } + return 1; /* not found */ + } + return -1; /* error */ /* */ +} +/**************************************************************************************/ +int CurTreeGetPos( CUR_TREE *cur_tree ) +{ + if ( cur_tree ) { + return cur_tree->cur_len; + } + return -1; +} +/**************************************************************************************/ +int CurTreeSetPos( CUR_TREE *cur_tree, int len ) +{ + if ( cur_tree ) { + cur_tree->cur_len = len; + return 0; + } + return -1; +} +/**************************************************************************************/ +int CurTreeRemoveLastRank( CUR_TREE *cur_tree ) +{ + if ( cur_tree && cur_tree->cur_len > 0 ) { + cur_tree->cur_len -= cur_tree->tree[cur_tree->cur_len-1]+1; + if ( cur_tree->cur_len >= 0 ) { + return 0; + } + } + return -1; +} +/**************************************************************************************/ +/* Find if the atom is equivalent to already successfully tried current atoms */ +int CurTreeIsLastAtomEqu( CUR_TREE *cur_tree, int at_no, AT_NUMB *nSymmStereo ) +{ + if ( cur_tree && cur_tree->tree && nSymmStereo && cur_tree->cur_len > 1 ) { + AT_NUMB nEq = nSymmStereo[at_no]; + int end = cur_tree->cur_len-1; + int len = cur_tree->tree[end]-1; + for ( ; len > 0; len -- ) { + if ( nSymmStereo[(int)cur_tree->tree[end-len]] == nEq ) + return 1; + } + return 0; + } + return -1; /* error */ /* */ +} +#ifdef NEVER /* not used */ +/**************************************************************************************/ +int CurTreeRemoveLastAtom( CUR_TREE *cur_tree ) +{ + if ( cur_tree && cur_tree->tree && cur_tree->cur_len > 2 ) { + AT_NUMB len = cur_tree->tree[ --cur_tree->cur_len ]; + if ( len >= 2 ) { + cur_tree->tree[cur_tree->cur_len-1] = len-1; + return 0; + } + } + return -1; +} +/**************************************************************************************/ +int CurTreeReplaceLastRank( CUR_TREE *cur_tree, AT_NUMB rank ) +{ + if ( !CurTreeRemoveLastRank( cur_tree ) ) { + return CurTreeAddRank( cur_tree, rank ); + } + return -1; +} +/**************************************************************************************/ +/* returns cur_tree->cur_len for the block containing the rank */ +int CurTreeFindTheRankPos( CUR_TREE *cur_tree, AT_NUMB rank ) +{ + int i, k; + if ( cur_tree && cur_tree->tree && (i=cur_tree->cur_len) > 0 ) { + while( 0 <= (k = i-(int)cur_tree->tree[i-1]-1) ) { + if ( cur_tree->tree[k] == rank ) { + return i; + } + i = k; + } + } + return -1; /* error */ /* */ +} +#endif diff --git a/INCHI-1-SRC/INCHI/common/ichimap2.c b/INCHI-1-SRC/INCHI_BASE/src/ichimap2.c similarity index 86% rename from INCHI-1-SRC/INCHI/common/ichimap2.c rename to INCHI-1-SRC/INCHI_BASE/src/ichimap2.c index 99be79e..a4ff881 100644 --- a/INCHI-1-SRC/INCHI/common/ichimap2.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichimap2.c @@ -1,2907 +1,3027 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - - -#include "mode.h" - -#include "incomdef.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichicant.h" -#include "ichicomn.h" - -#include "ichicomp.h" - -#define MAP_MODE_STD 0 /* Standard approach: switch 2 neighbors */ -#define MAP_MODE_C2v 1 /* Check for C2v reflection leading to parity inversion */ -#define MAP_MODE_C2 2 /* Check for C2 rotation preserving parities */ -#define MAP_MODE_S4 3 /* Check for S4 rotation/reflection leading to parity inversion */ -/* important: MAP_MODE_STD < (MAP_MODE_C2v, MAP_MODE_C2) < MAP_MODE_S4 */ - -/* local prototypes */ -void DeAllocateForNonStereoRemoval( AT_RANK **nAtomNumberCanon1, AT_RANK **nAtomNumberCanon2, - NEIGH_LIST **nl, NEIGH_LIST **nl1, NEIGH_LIST **nl2, AT_RANK **nVisited1, AT_RANK **nVisited2 ); -int AllocateForNonStereoRemoval( sp_ATOM *at, int num_atoms, const AT_RANK *nSymmRank, AT_RANK *nCanonRank, - AT_RANK **nAtomNumberCanon1, AT_RANK **nAtomNumberCanon2, - NEIGH_LIST **nl, NEIGH_LIST **nl1, NEIGH_LIST **nl2, AT_RANK **nVisited1, AT_RANK **nVisited2 ); -AT_RANK GetMinNewRank(AT_RANK *nAtomRank, AT_RANK *nAtomNumb, AT_RANK nRank1 ); -int BreakNeighborsTie( sp_ATOM *at, int num_atoms, int num_at_tg, int ib, int ia, - AT_RANK *neigh_num, int in1, int in2, int mode, - AT_RANK **pRankStack1, AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, - const AT_RANK *nSymmRank, AT_RANK *nCanonRank, NEIGH_LIST *nl1, NEIGH_LIST *nl2, long *lNumIter ); -int CheckNextSymmNeighborsAndBonds( sp_ATOM *at, AT_RANK cur1, AT_RANK cur2, AT_RANK n1, AT_RANK n2, - AT_RANK *nAvoidCheckAtom, AT_RANK *nVisited1, AT_RANK *nVisited2, - AT_RANK *nVisitOrd1, AT_RANK *nVisitOrd2, const AT_RANK *nRank1, const AT_RANK *nRank2 ); -int CreateCheckSymmPaths( sp_ATOM *at, AT_RANK prev1, AT_RANK cur1, AT_RANK prev2, AT_RANK cur2, - AT_RANK *nAvoidCheckAtom, AT_RANK *nVisited1, AT_RANK *nVisited2, - AT_RANK *nVisitOrd1, AT_RANK *nVisitOrd2, - NEIGH_LIST *nl1, NEIGH_LIST *nl2, const AT_RANK *nRank1, const AT_RANK *nRank2, - AT_RANK *nCanonRank, AT_RANK *nLength, int *bParitiesInverted, int mode ); -int CalculatedPathsParitiesAreIdentical( sp_ATOM *at, int num_atoms, const AT_RANK *nSymmRank, - AT_RANK *nCanonRank, AT_RANK *nAtomNumberCanon, AT_RANK *nAtomNumberCanon1, AT_RANK *nAtomNumberCanon2, - AT_RANK *nVisited1, AT_RANK *nVisited2, - AT_RANK prev_sb_neigh, AT_RANK cur, AT_RANK next1, AT_RANK next2, int nNeighMode, - int bParitiesInverted, int mode, CANON_STAT *pCS, - int vABParityUnknown); -int RemoveCalculatedNonStereoBondParities( sp_ATOM *at, int num_atoms, int num_at_tg, - AT_RANK **pRankStack1, AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, - AT_RANK *nCanonRank, const AT_RANK *nSymmRank, - AT_RANK *nAtomNumberCanon, AT_RANK *nAtomNumberCanon1, AT_RANK *nAtomNumberCanon2, - NEIGH_LIST *nl, NEIGH_LIST *nl1, NEIGH_LIST *nl2, - AT_RANK *nVisited1, AT_RANK *nVisited2, CANON_STAT *pCS, - int vABParityUnknown); -int RemoveCalculatedNonStereoCenterParities( sp_ATOM *at, int num_atoms, int num_at_tg, - AT_RANK **pRankStack1, AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, - AT_RANK *nCanonRank, const AT_RANK *nSymmRank, - AT_RANK *nAtomNumberCanon, AT_RANK *nAtomNumberCanon1, AT_RANK *nAtomNumberCanon2, - NEIGH_LIST *nl, NEIGH_LIST *nl1, NEIGH_LIST *nl2, - AT_RANK *nVisited1, AT_RANK *nVisited2, CANON_STAT *pCS, - int vABParityUnknown); - -int SortNeighLists3( int num_atoms, AT_RANK *nRank, NEIGH_LIST *NeighList, AT_RANK *nAtomNumber ); - - - - - -/************************************************************************************** - * - * Convert sorted equivalence information (nSymmRank) to ranks (nRank) - * nSymmRank and nRank may point to the same array - * - */ -int SortedEquInfoToRanks( const AT_RANK* nSymmRank, AT_RANK* nRank, const AT_RANK* nAtomNumber, int num_atoms, int *bChanged ) -{ - AT_RANK rNew, rOld, nNumDiffRanks; - int i, j, nNumChanges = 0; - for ( i = num_atoms-1, j = (int)nAtomNumber[i], - rOld = nSymmRank[j], rNew = nRank[j] = (AT_RANK)num_atoms, - nNumDiffRanks = 1; - i > 0; - i -- ) { - - j = (int)nAtomNumber[i-1]; - - if ( nSymmRank[j] != rOld ) { - nNumDiffRanks ++; - rNew = (AT_RANK)i; - nNumChanges += (rOld != rNew+1); - rOld = nSymmRank[j]; - } - - nRank[j] = rNew; - } - if ( bChanged ) { - *bChanged = (0 != nNumChanges); - } - return nNumDiffRanks; -} -/************************************************************************************** - * - * Convert sorted ranks (nRank) to sorted equivalence information (nSymmRank) - * nSymmRank and nRank may point to the same array - * - */ -int SortedRanksToEquInfo( AT_RANK* nSymmRank, const AT_RANK* nRank, const AT_RANK* nAtomNumber, int num_atoms ) -{ - AT_RANK rNew, rOld, nNumDiffRanks; - int i, j; - for ( i = 1, j = (int)nAtomNumber[0], - rOld = nRank[j], rNew = nSymmRank[j] = 1, - nNumDiffRanks = 1; - i < num_atoms; - i ++ ) { - j = (int)nAtomNumber[i]; - if ( nRank[j] != rOld ) { - nNumDiffRanks ++; - rNew = (AT_RANK)(i+1); - rOld = nRank[j]; - } - nSymmRank[j] = rNew; - } - return nNumDiffRanks; -} - -/**************************************************************************************/ -void switch_ptrs( AT_RANK **p1, AT_RANK **p2 ) -{ - AT_RANK *tmp = *p1; - *p1 = *p2; - *p2 = tmp; -} - -/**************************************************************************************/ -/* Set ranks from the products vector and previous ranks */ -/* nRank[] and nNewRank[] should refer to different arrays for now */ -/**************************************************************************************/ -int SetNewRanksFromNeighLists3( int num_atoms, NEIGH_LIST *NeighList, AT_RANK *nRank, - AT_RANK *nNewRank, AT_RANK *nAtomNumber ) -{ - int i, j, nNumDiffRanks, nNumNewRanks; - AT_RANK r1, r2; - /* -- nAtomNumber[] is already properly set -- - for ( i = 0; i < num_atoms; i++ ) { - nAtomNumber[i] = (AT_RANK)i; - } - */ - /* set globals for qsort */ - pNeighList_RankForSort = NeighList; - pn_RankForSort = nRank; - nNumDiffRanks = 0; - nNumNewRanks = 0; - - memset(nNewRank, 0, num_atoms*sizeof(nNewRank[0])); - - /* sorting */ - for ( i = 0, r1 = 1; i < num_atoms; r1++ ) { - if ( r1 == (r2 = nRank[j=(int)nAtomNumber[i]]) ) { - nNewRank[j] = r2; - nNumDiffRanks ++; - i ++; - continue; - } - r1 = r2; - insertions_sort_AT_NUMBERS( nAtomNumber+i, (int)r2-i, CompNeighLists ); - /*insertions_sort( nAtomNumber+i, r2-i, sizeof( nAtomNumber[0] ), CompNeighLists );*/ - j = r2-1; - nNewRank[(int)nAtomNumber[j]] = r2; - nNumDiffRanks ++; - while( j > i ) { - if ( CompareNeighListLex( NeighList[(int)nAtomNumber[j-1]], - NeighList[(int)nAtomNumber[j]], nRank ) ) { - r2 = j; - nNumDiffRanks ++; - nNumNewRanks ++; - } - j --; - nNewRank[(int)nAtomNumber[j]] = r2; - } - i = r1; - } - return nNumNewRanks? -nNumDiffRanks : nNumDiffRanks; -} -/**************************************************************************************/ -/* Set ranks from the products vector and previous ranks */ -/* When comparing neigh lists ignore ranks > max_at_no */ -/* nRank[] and nNewRank[] should refer to different arrays for now */ -/**************************************************************************************/ -int SetNewRanksFromNeighLists4( int num_atoms, NEIGH_LIST *NeighList, AT_RANK *nRank, - AT_RANK *nNewRank, AT_RANK *nAtomNumber, AT_RANK nMaxAtRank ) -{ - int i, j, nNumDiffRanks, nNumNewRanks; - AT_RANK r1, r2; - /* -- nAtomNumber[] is already properly set -- - for ( i = 0; i < num_atoms; i++ ) { - nAtomNumber[i] = (AT_RANK)i; - } - */ - /* set globals for CompNeighListsUpToMaxRank */ - pNeighList_RankForSort = NeighList; - pn_RankForSort = nRank; - nNumDiffRanks = 0; - nNumNewRanks = 0; - nMaxAtNeighRankForSort = nMaxAtRank; - - memset(nNewRank, 0, num_atoms*sizeof(nNewRank[0])); - - /* sorting */ - for ( i = 0, r1 = 1; i < num_atoms; r1++ ) { - if ( r1 == (r2 = nRank[j=(int)nAtomNumber[i]]) ) { - /* non-tied rank: singleton */ - nNewRank[j] = r2; - nNumDiffRanks ++; - i ++; - continue; - } - /* tied rank r2 - r2-i atoms have rank r2 - next atom after them is in position r2 - */ - r1 = r2; - insertions_sort_AT_NUMBERS( nAtomNumber+i, (int)r2-i, CompNeighListsUpToMaxRank ); - /*insertions_sort( nAtomNumber+i, r2-i, sizeof( nAtomNumber[0] ), CompNeighListsUpToMaxRank );*/ - j = r2-1; /* prepare cycle backward, from j to i step -1 */ - nNewRank[(int)nAtomNumber[j]] = r2; - nNumDiffRanks ++; - while( j > i ) { - if ( CompareNeighListLexUpToMaxRank( NeighList[nAtomNumber[j-1]], - NeighList[nAtomNumber[j]], nRank, nMaxAtRank ) ) { - r2 = j; - nNumDiffRanks ++; - nNumNewRanks ++; - } - j --; - nNewRank[(int)nAtomNumber[j]] = r2; - } - i = r1; - } - return nNumNewRanks? -nNumDiffRanks : nNumDiffRanks; -} -/**************************************************************************************/ -/* Set ranks from the products vector and previous ranks */ -/* nRank[] and nNewRank[] should refer to different arrays for now */ -/**************************************************************************************/ -int SetNewRanksFromNeighLists( int num_atoms, NEIGH_LIST *NeighList, AT_RANK *nRank, AT_RANK *nNewRank, - AT_RANK *nAtomNumber, int bUseAltSort, int ( *comp )(const void *, const void *) ) -{ - int i, nNumDiffRanks; - AT_RANK nCurrentRank; - /* -- nAtomNumber[] is already properly set -- - for ( i = 0; i < num_atoms; i++ ) { - nAtomNumber[i] = (AT_RANK)i; - } - */ - /* set globals for qsort */ - pNeighList_RankForSort = NeighList; - pn_RankForSort = nRank; - - /* sorting */ - if ( bUseAltSort & 1 ) - tsort( nAtomNumber, num_atoms, sizeof( nAtomNumber[0] ), comp /*CompNeighListRanksOrd*/ ); - else - qsort( nAtomNumber, num_atoms, sizeof( nAtomNumber[0] ), comp /*CompNeighListRanksOrd*/ ); - - for ( i=num_atoms-1, nCurrentRank=nNewRank[(int)nAtomNumber[i]] = (AT_RANK)num_atoms, nNumDiffRanks = 1; - 0 < i ; - i -- ) { - /* Note: CompNeighListRanks() in following line implicitly reads nRank pointed by pn_RankForSort */ - if ( CompNeighListRanks( &nAtomNumber[i-1], &nAtomNumber[i] ) ) { - nNumDiffRanks ++; - nCurrentRank = (AT_RANK)i; - } - nNewRank[(int)nAtomNumber[i - 1]] = nCurrentRank; - } - - return nNumDiffRanks; -} -/**************************************************************************************/ -/* Sort NeighList[] lists of neighbors according to the ranks of the neighbors */ -/**************************************************************************************/ -void SortNeighListsBySymmAndCanonRank( int num_atoms, NEIGH_LIST *NeighList, const AT_RANK *nSymmRank, const AT_RANK *nCanonRank ) -{ - int i; - for ( i = 0; i < num_atoms; i ++ ) { - insertions_sort_NeighListBySymmAndCanonRank( NeighList[i], nSymmRank, nCanonRank ); - } -} -/**************************************************************************************/ -int SortNeighLists2( int num_atoms, AT_RANK *nRank, NEIGH_LIST *NeighList, AT_RANK *nAtomNumber ) -{ - int k, i; - AT_RANK nPrevRank = 0; - /* - * on entry nRank[nAtomNumber[k]] <= nRank[nAtomNumber[k+1]] ( k < num_atoms-1 ) - * nRank[nAtomNumber[k]] >= k+1 ( k < num_atoms ) - * nRank[nAtomNumber[k]] == k+1 if this nRank value is not tied OR if - * nRank[nAtomNumber[k]] < nRank[nAtomNumber[k+1]] OR if k = num_atoms-1. - * - */ - for ( k = 0; k < num_atoms; k ++ ) { - i = nAtomNumber[k]; - if ( (nRank[i] != k+1 || nRank[i] == nPrevRank) && NeighList[i][0] > 1 ) { - /* nRank[i] is tied (duplicated) */ - insertions_sort_NeighList_AT_NUMBERS( NeighList[i], nRank ); - } - nPrevRank = nRank[i]; - } - return 0; -} -/**************************************************************************************/ -int SortNeighLists3( int num_atoms, AT_RANK *nRank, NEIGH_LIST *NeighList, AT_RANK *nAtomNumber ) -{ - int k, i; - AT_RANK nPrevRank = 0; - /* - * on entry nRank[nAtomNumber[k]] <= nRank[nAtomNumber[k+1]] ( k < num_atoms-1 ) - * nRank[nAtomNumber[k]] >= k+1 ( k < num_atoms ) - * nRank[nAtomNumber[k]] == k+1 if this nRank value is not tied OR if - * nRank[nAtomNumber[k]] < nRank[nAtomNumber[k+1]] OR if k = num_atoms-1. - * - */ - for ( k = 0; k < num_atoms; k ++ ) { - i = nAtomNumber[k]; - if ( (nRank[i] != k+1 || nRank[i] == nPrevRank) && NeighList[i][0] > 1 ) { - /* nRank[i] is tied (duplicated) */ - insertions_sort_NeighList_AT_NUMBERS3( NeighList[i], nRank ); - } - nPrevRank = nRank[i]; - } - return 0; -} -/************************************************************************************** - * - * Differentiate2 - * - * Note: on entry nAtomNumber[] must contain a valid transposition of num_atoms length - * for example, nAtomNumber[i] = i; - * Note2: this version does not calculate neighbor lists for non-tied ranks - */ -int DifferentiateRanks2( int num_atoms, NEIGH_LIST *NeighList, - int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, - AT_RANK *nAtomNumber, long *lNumIter, int bUseAltSort ) -{ - /*int nNumPrevRanks;*/ - - /* SortNeighLists2 needs sorted ranks */ - pn_RankForSort = pnCurrRank; - if ( bUseAltSort & 1 ) - tsort( nAtomNumber, num_atoms, sizeof(nAtomNumber[0]), CompRank /* CompRanksOrd*/ ); - else - qsort( nAtomNumber, num_atoms, sizeof(nAtomNumber[0]), CompRanksOrd ); - - do { - *lNumIter += 1; - /*nNumPrevRanks = nNumCurrRanks;*/ - switch_ptrs( &pnCurrRank, &pnPrevRank ); - SortNeighLists2( num_atoms, pnPrevRank, NeighList, nAtomNumber ); - /* the following call creates pnCurrRank out of pnPrevRank */ - nNumCurrRanks = SetNewRanksFromNeighLists( num_atoms, NeighList, pnPrevRank, pnCurrRank, nAtomNumber, - 1, CompNeighListRanksOrd ); - } while ( /*nNumPrevRanks != nNumCurrRanks ||*/ memcmp( pnPrevRank, pnCurrRank, num_atoms*sizeof(pnCurrRank[0]) ) ); - - return nNumCurrRanks; -} -/************************************************************************************** - * - * Differentiate3 - * - * Note: on entry nAtomNumber[] must contain a valid transposition of num_atoms length - * for example, nAtomNumber[i] = i; - * Note2: this version does not calculate neighbor lists for non-tied ranks - */ -int DifferentiateRanks3( int num_atoms, NEIGH_LIST *NeighList, - int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, - AT_RANK *nAtomNumber, long *lNumIter ) -{ -/* - static long count = 0; - count ++; - if ( count == 103 ) { - int stop=1; - } -*/ - - /* SortNeighLists3 needs sorted ranks: ranks/atnumbers must have been already sorted */ - do { - *lNumIter += 1; - switch_ptrs( &pnCurrRank, &pnPrevRank ); - SortNeighLists3( num_atoms, pnPrevRank, NeighList, nAtomNumber ); - /* the following call creates pnCurrRank out of pnPrevRank */ - nNumCurrRanks = SetNewRanksFromNeighLists3( num_atoms, NeighList, pnPrevRank, - pnCurrRank, nAtomNumber); - } while ( nNumCurrRanks < 0 /* memcmp( pnPrevRank, pnCurrRank, num_atoms*sizeof(pnCurrRank[0]) )*/ ); - - return nNumCurrRanks; -} -/************************************************************************************** - * - * Differentiate4: ignore neighbors with rank > num_atoms - * - * Note: on entry nAtomNumber[] must contain a valid transposition of num_atoms length - * for example, nAtomNumber[i] = i; - * Note2: this version does not sort neighbor lists for non-tied ranks - */ -int DifferentiateRanks4( int num_atoms, NEIGH_LIST *NeighList, - int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, - AT_RANK *nAtomNumber, AT_RANK nMaxAtRank, long *lNumIter ) -{ -/* - static long count = 0; - count ++; - if ( count == 103 ) { - int stop=1; - } -*/ - /* SortNeighLists4 needs sorted ranks: ranks/atnumbers must have been already sorted */ - do { - *lNumIter += 1; - switch_ptrs( &pnCurrRank, &pnPrevRank ); - SortNeighLists3( num_atoms, pnPrevRank, NeighList, nAtomNumber ); - /* the following call creates pnCurrRank out of pnPrevRank */ - nNumCurrRanks = SetNewRanksFromNeighLists4( num_atoms, NeighList, pnPrevRank, - pnCurrRank, nAtomNumber, nMaxAtRank ); - } while ( nNumCurrRanks < 0 /* memcmp( pnPrevRank, pnCurrRank, num_atoms*sizeof(pnCurrRank[0]) )*/ ); - - return nNumCurrRanks; -} -/************************************************************************************** - * - * DifferentiateBasic (sort according to ranks only) - * - * Note: on entry nAtomNumber[] must contain a valid transposition of num_atoms length - * for example, nAtomNumber[i] = i; - * Note2: this version does not calculate neighbor lists for non-tied ranks - */ -int DifferentiateRanksBasic( int num_atoms, NEIGH_LIST *NeighList, - int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, - AT_RANK *nAtomNumber, long *lNumIter, int bUseAltSort ) -{ - int nNumPrevRanks; - - /* SortNeighLists2 needs sorted ranks */ - pn_RankForSort = pnCurrRank; - if ( bUseAltSort & 1 ) - tsort( nAtomNumber, num_atoms, sizeof(nAtomNumber[0]), CompRank ); - else - qsort( nAtomNumber, num_atoms, sizeof(nAtomNumber[0]), CompRank ); - - do { - *lNumIter += 1; - nNumPrevRanks = nNumCurrRanks; - switch_ptrs( &pnCurrRank, &pnPrevRank ); - SortNeighLists2( num_atoms, pnPrevRank, NeighList, nAtomNumber ); - /* the following call creates pnCurrRank out of pnPrevRank */ - nNumCurrRanks = SetNewRanksFromNeighLists( num_atoms, NeighList, pnPrevRank, pnCurrRank, nAtomNumber, bUseAltSort, CompNeighListRanks ); - } while ( nNumPrevRanks != nNumCurrRanks || memcmp( pnPrevRank, pnCurrRank, num_atoms*sizeof(pnCurrRank[0]) ) ); - return nNumCurrRanks; -} - -/************************************************************************************** - * For the purpose of mapping an atom to an atom: - * (a) find number of tied ranks - * (b) if number of tied ranks > 1 then: - * 1) find the rank for breaking a tie - * 2) allocate memory for breaking the tie if it has not been allocated - * 3) find out if atom 1 ("from") has already been mapped - * Return value: - * < 0: error - * = 1: has already been mapped, to tie to break - * > 1: we need to break a tie - */ -int NumberOfTies( AT_RANK **pRankStack1, AT_RANK **pRankStack2, int length, - int at_no1, int at_no2, AT_RANK *nNewRank, int *bAddStack, int *bMapped1 ) -{ - - AT_RANK *nRank1 = *pRankStack1++; - AT_RANK *nAtomNumber1 = *pRankStack1++; /* ranks for mapping "1", "from" */ - - AT_RANK *nRank2 = *pRankStack2++; - AT_RANK *nAtomNumber2 = *pRankStack2++; /* ranks for mapping "2", "to" */ - - AT_RANK r, *pTempArray; - - int iMax, i, i1, i2; - - *bAddStack = 0; - *bMapped1 = 0; - *nNewRank = 0; - r = nRank1[at_no1]; - if ( r != nRank2[at_no2] ) - return CT_MAPCOUNT_ERR; /* atoms cannot be mapped onto each other: they have different ranks */ /* */ - iMax = r - 1; - /* find i1 and i2 = numbers of ranks in nRank1[] and nRank2[] equal to r: */ - for ( i1 = 1; i1 <= iMax && r == nRank1[nAtomNumber1[iMax-i1]]; i1 ++ ) - ; - for ( i2 = 1; i2 <= iMax && r == nRank2[nAtomNumber2[iMax-i2]]; i2 ++ ) - ; - if ( i2 != i1 ) - return CT_MAPCOUNT_ERR; /* program error: must be identical number of equal ranks */ /* */ - /* found i1 equal rank(s); preceding (smaller) non-equal rank is r-i1 */ - /* To break the tie we have to reduce the rank r to r-i1+1 */ - - /************ Note ******************************* - * IF ( i=r-1 && 0 <= i && i < num_atoms AND - * nRank[nAtomNumber1[i]] == r ) - * THEN: - * nRank[nAtomNumber1[i+1]] > r; (if i+1 < num_atoms) - * nRank[nAtomNumber1[i-1]] <= r; (if i > 0) - * - * IF r = nRank[i] THEN - * nRank[nAtomNumber1[r-1]] == r - * nRank[nAtomNumber1[r-i-1]] <= nRank[nAtomNumber1[r-i]] (for 1 <= i < r ) - */ - if ( i1 > 1 ) { - /* int bAtFromHasAlreadyBeenMapped = 0; */ - *nNewRank = r - i1 + 1; - /* grab an existing or allocate a new array */ - /* we need 4 arrays: 2 for ranks + 2 for numbers */ - for ( i = 0; i < 4; i ++ ) { - if ( i < 2 ) { - pTempArray = *pRankStack1; - *bMapped1 += (pTempArray && pTempArray[0]); - } else { - pTempArray = *pRankStack2; - } - if ( !pTempArray && !(pTempArray = (AT_RANK *) inchi_malloc(length))) - return CT_OUT_OF_RAM; /* out of RAM */ /* */ - /* copy "to" contents */ - switch( i ) { - case 2: - memcpy( pTempArray, nRank2, length ); - break; - case 3: - memcpy( pTempArray, nAtomNumber2, length ); - break; - } - if ( i < 2 ) - *pRankStack1 ++ = pTempArray; - else { - *pRankStack2 ++ = pTempArray; - } - } - *bAddStack = 2; /* to break the tie we added 2 more arrays to pRankStack1 and pRankStack2 */ - } - return i1; -} - - -/************************************************************************************** - * - * - * - * Stereo Mappings - * - * - * - **************************************************************************************/ - -/************************************************************************************** - * Parity for a half of a stereo bond. If both halfs have the same parity - * then the bond is "trans" (E,-,1), otherwise it is "cis" (Z,+,2). - * The advantage of this approach is: The bond parity does not depend on the - * rank of the atom located on the opposite end of the stereogenic bond. - * As the result all bond parities of, for example, benzene, can be calculated - * from equivalence ranks only, without any mappings. - * - * Input: at_no1 = number of atom for which the half-bond parity is calculated - * i_sb_neigh = ordering number of the stereo bond in at->stereo_bond_neighbor[] - * - * Returns: 0=> no parity can be found; 1=> odd parity; 2=> even parity - * - */ -int HalfStereoBondParity( sp_ATOM *at, int at_no1, int i_sb_neigh, const AT_RANK *nRank ) -{ -/* - Suppose neighbors #0,#1,#2 have ranks a, b, c. Remove rank of the neighbor connected - by the stereogenic bond (NCSB) from the a, b, c list and denote the two left as r[0], r[1], - in the same order. Let iNCSB be an ordering number (0,1,or 2) of the NCSB. - Assume the neighbor connected by the stereogenic bond has infinite positive rank. - Position the half-bond so that the stereogenic bond neighbor is to the right from the atom (see below) - - Definition. - =========== - if rank(X) != rank(Y) then Half-bond parity = (rank(X) > rank(Y)), that is, - Y - \ if ( rank(X) < rank(Y) ) then Half-bond parity is Even - C==NCSB if ( rank(X) > rank(Y) ) then Half-bond parity is Odd - / if ( rank(X) = rank(Y) ) then Half-bond parity cannot be defined - X - - 1 2 1 - \ \ \ - C==NCSB C==NCSB C==NCSB C==NCSB - / / / - 2 1 1 - - Parity = 1 Parity = 1 Parity = 2 Parity = 2 - (Odd) (Odd) (Even) or 0 (Even) or 0 - - Half-bond parity = (iNCSB + (r[0] > r[1]) + (Atom C geometric parity))%2 - - Consider the following cases to prove the formula: - - Case 1: 3 explicit neighbors - ============================ - If (1) atom's geometric parity = even (which means neighbors #0, #1, #2 are located clockwise), - and (2) neighbors other than NCSB have different ranks, then, - assuming that NCSB always has the largest (infinite) rank (this is consistent with - the assumption that implicit hydrogens have smallest ranks), we have 3 possibilities: - - c a b - \ \ \ - C==a C==b C==c - / / / - b c a - - iNCSB = 0 1 2 - Half-bond parity = b>c ab (0=even, 1=odd) - r[0]>r[1] r[0]r[1] - Half-bond parity - for all 3 cases = (iNCSB + (r[0] > r[1]))%2 - - The following slight modification will work for both odd and even geometric parity: - - Half-bond parity = (iNCSB + (r[0] > r[1]) + (Atom C geometric parity))%2 - - even parity (0) => atom above the bond has lower rank than the atom below the bond. - - - Case 2: 2 explicit neighbors - ============================ - One implicit hydrogen atom H or hydrogen isotope (implicit rank=0). Assume r[1]=0 - - H a Note. The same method - \ \ works for - C==a C==b - / / N==a and a - b H / \ - b N==b - iNCSB = 0 1 - Half-bond parity = b>0 a<0 - (r[1]=0, r[0]>0) r[0]>r[1] r[0] r[1]) + (Atom C geometric parity))%2 - - Case 3: 1 explicit neighbor (NCSB) - ================================== - Two implicit hydrogens, (number of neighbors on non-streogenic bonds)==0: - - Atom C geometric parity: Even Odd Note. The same method - works for - D H - \ \ Even and Odd - C==a C==a - / / H N==a - H D \ / - N==a H - iNCSB = 0 0 - Half-bond parity = (0<0)=0 (0<0)+1 = 1 - (r[1]=0, r[0]=0) r[1] r[1]) + (Atom C geometric parity))%2 - -*/ - int i, j, k, iNeigh, parity, at1_parity, at_no2; - AT_RANK r[MAX_NUM_STEREO_BOND_NEIGH]; - - if ( at[at_no1].valence > MAX_NUM_STEREO_BOND_NEIGH || ( at1_parity = at[at_no1].parity ) <= 0 ) { - return 0; - } - if ( !PARITY_WELL_DEF( at1_parity ) ) { - if ( PARITY_KNOWN( at1_parity ) ) { - return at1_parity; - } - return -at1_parity; - } - if ( 0 > i_sb_neigh || i_sb_neigh >= MAX_NUM_STEREO_BOND_NEIGH ) { - return CT_STEREOBOND_ERROR; /* */ - } - for ( i = 0; i <= i_sb_neigh; i ++ ) { - if ( !at[at_no1].stereo_bond_neighbor[i] ) { - return CT_STEREOBOND_ERROR; /* */ - } - } - at_no2 = at[at_no1].neighbor[(int)at[at_no1].stereo_bond_ord[i_sb_neigh]]; - memset( r, 0, sizeof( r ) ); - for ( i = j = 0, iNeigh = -1; i < at[at_no1].valence; i ++ ) { - if ( (k = (int)at[at_no1].neighbor[i]) == at_no2 ) { - iNeigh = i; - } else { - r[j++] = nRank[k]; - } - } - if ( iNeigh < 0 || iNeigh != at[at_no1].stereo_bond_ord[i_sb_neigh] ) { - return CT_STEREOBOND_ERROR; /* */ - } - if ( j > 0 && !r[0] || j > 1 && !r[1] ) - return 0; /* undefined ranks */ - - if ( j == 2 && r[0] == r[1] || iNeigh < 0 ) { - parity = AB_PARITY_CALC; /* cannot calculate bond parity without additional breaking ties. */ - } else { - parity = 2 - (at[at_no1].parity + iNeigh + (r[1] < r[0])) % 2; - } - return parity; -} -/**************************************************************************************/ -int parity_of_mapped_half_bond( int from_at, int to_at, int from_neigh, int to_neigh, - sp_ATOM *at, EQ_NEIGH *pEN, - const AT_RANK *nCanonRankFrom, const AT_RANK *nRankFrom, const AT_RANK *nRankTo ) -{ - int i, j, k, num_neigh; - int to_sb_neigh_ord, from_sb_neigh_ord, parity; - AT_RANK r_to[MAX_NUM_STEREO_BOND_NEIGH], at_no_to[MAX_NUM_STEREO_BOND_NEIGH]; - AT_RANK r_canon_from[MAX_NUM_STEREO_BOND_NEIGH], at_no_from[MAX_NUM_STEREO_BOND_NEIGH]; - AT_RANK r, r_sb_neigh; - - for ( i = 0; i < MAX_NUM_STEREO_BOND_NEIGH; i ++ ) { - r_to[i] = r_canon_from[i] = 0; - } - - if ( pEN ) { - memset( pEN, 0, sizeof(*pEN)); - } - - /* for debug only */ - if ( nRankFrom[from_at] != nRankTo[to_at] || - nRankFrom[from_neigh] != nRankTo[to_neigh] || - at[to_at].valence != at[from_at].valence ) { - return 0; /* program error: both atoms must be mapped */ /* */ - } - - parity = PARITY_VAL(at[to_at].parity); - num_neigh = at[to_at].valence; - - if ( num_neigh > MAX_NUM_STEREO_BOND_NEIGH || num_neigh < MIN_NUM_STEREO_BOND_NEIGH ) { - /* 2 neighbors are possible in case of stereo bond with implicit H */ - /* or a stereocenter -CHD- with an implicit H */ - if ( num_neigh == 1 && at[to_at].stereo_bond_neighbor[0] ) { - /* 1 neighbor can happen in case of a terminal =CHD */ - if ( PARITY_WELL_DEF(parity) ) - return 2 - parity % 2; - else - if ( parity ) - return parity; - else - return AB_PARITY_UNDF; /* undefined parity */ - } - return 0; /* program error */ /* */ - } - if ( ATOM_PARITY_KNOWN(parity) ) { - if ( !ATOM_PARITY_WELL_DEF(parity) ) - return parity; - } else - if ( parity ) { - return 0; /* parity; */ - } else { - return 0; /* AB_PARITY_UNDF; */ /* possibly program error: undefined parity */ - } - /* locate at[to_at].stereo_bond_neighbor[] ordering numbers */ - for ( i = 0, to_sb_neigh_ord=-1; i < MAX_NUM_STEREO_BONDS && (k=(int)at[to_at].stereo_bond_neighbor[i]); i ++ ) { - if ( k == to_neigh+1 ) { - to_sb_neigh_ord = i; - break; - } - } - if ( to_sb_neigh_ord < 0 ) { - return 0; /* program error: not a stereo bond */ /* */ - } - to_sb_neigh_ord = (int)at[to_at].stereo_bond_ord[to_sb_neigh_ord]; - r_sb_neigh = nRankTo[(int)at[to_at].neighbor[to_sb_neigh_ord]]; - for ( i = j = 0; i < num_neigh; i ++ ) { - if ( i != to_sb_neigh_ord ) { - r_to[j] = nRankTo[(int)(at_no_to[j]=at[to_at].neighbor[i])]; - if ( r_sb_neigh == r_to[j] ) { - return 0; /* stereo bond atoms are not fully mapped */ - } - j ++; - } - } - if ( j+1 != num_neigh ) { - return 0; /* program error */ /* */ - } - if ( j == 1 ) { - /* only one neighbor; no mapping needed */ - return 2-(parity+1+to_sb_neigh_ord)%2; - } - if ( j != 2 ) { - return 0; /* program error: j can be only 0, 1, or 2 */ /* */ - } - - if ( r_to[0] == r_to[1] ) { - /* double bond neighbors need to be mapped */ - j = 0; - from_sb_neigh_ord = -1; - for ( i = 0; i < num_neigh; i ++ ) { - k = at[from_at].neighbor[i]; - r = nRankFrom[k]; - if ( r == r_sb_neigh ) { - from_sb_neigh_ord = i; /* we need this value only for error-checking */ - } else - if ( r == r_to[0] ) { - r_canon_from[j] = nCanonRankFrom[k]; - at_no_from[j] = (AT_RANK)k; - j ++; - } else { - return 0; /* program error: unexpected rank, not fully mapped adjacent to the stereo bond atoms */ /* */ - } - } - if ( from_sb_neigh_ord < 0 || j != 2 ) { - return 0; /* program error: rank of a neighbor not found */ /* */ - } - if ( pEN ) { /* j == 2 */ - pEN->to_at[0] = at_no_to[0]; - pEN->to_at[1] = at_no_to[1]; - pEN->num_to = 2; /* number of stored in pEN->to_at[] central atom neighbors */ - pEN->rank = r_to[0]; /* mapping rank of the tied neighbors */ - /* i := index of the smaller out of r_canon_from[1] and r_canon_from[0] */ - i = (r_canon_from[1] < r_canon_from[0]); - pEN->from_at = at_no_from[i]; - pEN->canon_rank = r_canon_from[i]; - } - return -((int)r_to[0]); - } - /* double bond neighbors a mapped: r_to[0] != r_to[1] */ - from_sb_neigh_ord = -1; - for ( i = 0; i < num_neigh; i ++ ) { - k = at[from_at].neighbor[i]; - r = nRankFrom[k]; - if ( r == r_sb_neigh ) { - from_sb_neigh_ord = i; /* we need this value only for error-checking */ - } else - if ( r == r_to[0] ) { - r_canon_from[0] = nCanonRankFrom[k]; - /* at_no_from[0] = (AT_RANK)k; */ - } else - if ( r == r_to[1] ) { - r_canon_from[1] = nCanonRankFrom[k]; - /* at_no_from[1] = (AT_RANK)k; */ - } else { - return 0; /* program error: unexpected rank, not fully mapped adjacent to the stereo bond atoms */ /* */ - } - } - if ( !r_canon_from[0] || !r_canon_from[1] || from_sb_neigh_ord < 0 ) { - return 0; /* program error: neighbor rank not found */ /* */ - } - return 2 - (parity + to_sb_neigh_ord + (r_canon_from[1] */ - if ( num_neigh > MAX_NUM_STEREO_ATOM_NEIGH || num_neigh < 2 ) { - /* 2 neighbors are possible in case of stereo bond with implicit H */ - /* or a stereocenter >CHD with two implicit H */ - if ( num_neigh == 1 ) { - /* 1 neighbor can happen in case of a terminal -CHDT or =CHD */ - if ( at[to_at].parity ) - return at[to_at].parity; - else - return AB_PARITY_UNDF; /* undefined parity */ - } - return 0; /* program error */ /* */ - } - for ( i = 0; i < num_neigh; i ++ ) { /* initialization of locals */ - nNeighNumberTo[i] = - nNeighNumberFrom[i] = i; - nNeighRankTo[i] = nRankTo[(int)at[to_at].neighbor[i]]; /* mapping rank */ - nNeighRankFrom[i] = nRankFrom[j=(int)at[from_at].neighbor[i]]; /* mapping rank */ - nNeighRankFromCanon[i] = nCanonRankFrom[j]; /* canonical number */ - } - - pn_RankForSort = nNeighRankFrom; - nNumCompNeighborsRanksCountEql = 0; /* sort mapping ranks-from */ - num_trans_from = insertions_sort( nNeighNumberFrom, num_neigh, sizeof(nNeighNumberFrom[0]), CompNeighborsRanksCountEql ); - - if ( nNumCompNeighborsRanksCountEql ) { - /* At least 2 neighbors have equal mapping ranks (are tied). */ - /* Find tied from-neighbors with minimal canonical rank (nCanonRankFrom[]) */ - r_canon_from_min = MAX_ATOMS+1; /* max possible rank + 1 */ - for ( i = 1, r = 0, r1 = nNeighRankFrom[neigh1=nNeighNumberFrom[0]]; i < num_neigh; i ++, r1 = r2, neigh1 = neigh2 ) { - r2 = nNeighRankFrom[neigh2=nNeighNumberFrom[i]]; - if ( r2 == r1 ) { - /* found neighbors with tied ranks */ - if ( r != r2 ) { - /* the 1st pair of neighbor with this rank */ - r = r2; - if ( (r_canon_from=nNeighRankFromCanon[neigh1]) < r_canon_from_min ) { - r_canon_from_min = r_canon_from; /* min canon rank */ - neigh_canon_from_min = neigh1; /* neighbor number */ - } - } - if ( (r_canon_from=nNeighRankFromCanon[neigh2]) < r_canon_from_min ) { - r_canon_from_min = r_canon_from; - neigh_canon_from_min = neigh2; - } - } - } - if ( r ) { - /* neighbors with tied ranks have been found => parity cannot be determined without additional mapping */ - /* find to-neighbors on which neigh_canon_from_min can be mapped */ - r1 = nNeighRankFrom[neigh_canon_from_min]; - if ( pEN ) { - for ( i = j = 0; i < num_neigh; i ++ ) { - if ( r1 == nNeighRankTo[i] ) { - pEN->to_at[j++] = at[to_at].neighbor[i]; - } - } - insertions_sort( pEN->to_at, j, sizeof(pEN->to_at[0]), CompRanksInvOrd ); - pEN->num_to = j; /* number of stored in pEN->to_at[] central atom neighbors */ - pEN->from_at = at[from_at].neighbor[neigh_canon_from_min]; /* neighbor with min. canon number */ - pEN->rank = r1; /* mapping rank of the tied neighbors */ - pEN->canon_rank = r_canon_from_min; /* canon. rank of the pEN->from_at */ - } else { - /* debug only */ - for ( i = j = 0; i < num_neigh; i ++ ) { - if ( r1 == nNeighRankTo[i] ) { - j++; - } - } - } - /* debug only */ - if ( j <= 1 || !r1 || r_canon_from_min > MAX_ATOMS ) { - return 0; /* program error */ /* */ - } - return -r; /* means parity cannot be determined */ - } - return 0; /* program error */ - } - /* All neighbors have different mapping ranks; */ - /* therefore no additional mapping of the neighbors is necessary */ - if ( !ATOM_PARITY_WELL_DEF(at[to_at].parity) ) - return at[to_at].parity; /* unknown parity or cannot be determined */ - - pn_RankForSort = nNeighRankTo; - num_trans_to = insertions_sort( nNeighNumberTo, num_neigh, sizeof(nNeighNumberTo[0]), CompNeighborsRanksCountEql ); - - /* Map canonical ranks of neighbors. Mapped on each other "to" and "from" atoms have equal mapping ranks */ - for ( i = 0; i < num_neigh; i ++ ) { - if ( nNeighRankTo[j=nNeighNumberTo[i]] != nNeighRankFrom[k=nNeighNumberFrom[i]] ) - return 0; /* program error: mapping ranks not equal, from_at neigborhood cannot be mapped on to_at neighbood. */ /* */ - nNeighRankToCanon[j] = nNeighRankFromCanon[k]; /* potential problem: other atom(s) may have same mapping rank and */ - /* different canon. rank(s). */ - /* we may save some memory by eliminating nNeighRankFromCanon[]: */ - /* nNeighRankToCanon[j] = nCanonRankFrom[at[from_at].neighbor[k]] */ - } - - pn_RankForSort = nNeighRankToCanon; - num_trans_to += insertions_sort( nNeighNumberTo, num_neigh, sizeof(nNeighNumberTo[0]), CompNeighborsRanksCountEql ); -#ifndef CT_NEIGH_INCREASE - num_trans_to += ((num_neigh*(num_neigh-1))/2)%2; /* get correct parity for ascending order of canon. numbers */ -#endif - - return 2 - (num_trans_to + at[to_at].parity)%2; -} - -/************************************************************************************** - * - * Phase II: map canonicaly numbrered structure onto itself - * to obtain a minimal or maximal stereo part of the CT - * - **************************************************************************************/ - -int ClearPreviousMappings( AT_RANK **pRankStack1 ) -{ - int i; - for ( i = 0; pRankStack1[i]; i ++ ) { - pRankStack1[i][0] = 0; - } - return i; - -} -/**************************************************************************************/ -/* map one atom ("from") onto another ("to"): untie their mapping ranks if they are tied. */ -int map_an_atom2( int num_atoms, int num_max, int at_no1/*from*/, int at_no2/*to*/, - AT_RANK *nTempRank, - int nNumMappedRanks, int *pnNewNumMappedRanks, - CANON_STAT *pCS, - NEIGH_LIST *NeighList, - AT_RANK **pRankStack1, AT_RANK **pRankStack2, int *bAddStack ) -{ - AT_RANK *nRank1, *nAtomNumber1; /* ranks for mapping "1", "from" */ - AT_RANK *nRank2, *nAtomNumber2; /* ranks for mapping "2", "to" */ - AT_RANK *nNewRank1=NULL, *nNewAtomNumber1=NULL; /* ranks for mapping "1", "from" */ - AT_RANK *nNewRank2=NULL, *nNewAtomNumber2=NULL; /* ranks for mapping "2", "to" */ - int length = num_max*sizeof(AT_RANK); - int nNewNumRanks2, nNewNumRanks1; - int i, bAtFromHasAlreadyBeenMapped, nNumTies; - AT_RANK nNewRank; - - nNumTies = NumberOfTies( pRankStack1, pRankStack2, length, at_no1, at_no2, &nNewRank, bAddStack, &bAtFromHasAlreadyBeenMapped ); - - if ( RETURNED_ERROR(nNumTies) ) - return nNumTies; /* error */ - - nRank1 = *pRankStack1++; - nAtomNumber1 = *pRankStack1++; /* ranks for mapping "1", "from" */ - - nRank2 = *pRankStack2++; - nAtomNumber2 = *pRankStack2++; /* ranks for mapping "2", "to" */ - - if ( nNumTies > 1 ) { - - nNewRank1 = *pRankStack1++; - nNewAtomNumber1 = *pRankStack1++; /* ranks for mapping "1", "from" */ - - nNewRank2 = *pRankStack2++; - nNewAtomNumber2 = *pRankStack2++; /* ranks for mapping "2", "to" */ - /* break a tie for "to" */ - memcpy( nNewRank2, nRank2, length ); - memcpy( nNewAtomNumber2, nAtomNumber2, length ); - nNewRank2[at_no2] = nNewRank; - nNewNumRanks2 = DifferentiateRanks2( num_atoms, NeighList, - nNumMappedRanks, nNewRank2, nTempRank, - nNewAtomNumber2, &pCS->lNumNeighListIter, 1 ); - pCS->lNumBreakTies ++; - - /* Check whether the old mapping can be reused */ - if ( 2 == bAtFromHasAlreadyBeenMapped && nNewRank == nNewRank1[at_no1] ) { - for ( i = 0; i < num_atoms; i ++ ) { - if ( nNewRank1[nNewAtomNumber1[i]] != nNewRank2[nNewAtomNumber2[i]] ) { - bAtFromHasAlreadyBeenMapped = 0; /* It cannot. */ - break; - } - } - } else { - bAtFromHasAlreadyBeenMapped = 0; - } - if ( 2 != bAtFromHasAlreadyBeenMapped ) { - /* break a tie for "from" */ - for ( i = 0; pRankStack1[i]; i ++ ) { - pRankStack1[i][0] = 0; - } - memcpy( nNewRank1, nRank1, length ); - memcpy( nNewAtomNumber1, nAtomNumber1, length ); /* GPF: bad nAtomNumber1 */ - nNewRank1[at_no1] = nNewRank; - nNewNumRanks1 = DifferentiateRanks2( num_atoms, NeighList, - nNumMappedRanks, nNewRank1, nTempRank, - nNewAtomNumber1, &pCS->lNumNeighListIter, 1 ); - pCS->lNumBreakTies ++; - } else { - nNewNumRanks1 = nNewNumRanks2; - } - - if ( nNewNumRanks1 != nNewNumRanks2 ) - return CT_MAPCOUNT_ERR; /* program error */ /* */ - *pnNewNumMappedRanks = nNewNumRanks2; - /* debug only */ - for ( i = 0; i < num_atoms; i ++ ) { - if ( nNewRank1[nNewAtomNumber1[i]] != nNewRank2[nNewAtomNumber2[i]] ) { - return CT_MAPCOUNT_ERR; /* program error */ /* */ - } - } - } else { - *pnNewNumMappedRanks = nNumMappedRanks; - } - return ( nNewRank1 )? nNewRank1[at_no1] : nRank1[at_no1]; /* mapping rank value */ -} - -/**************************************************************************************/ -int might_change_other_atom_parity( sp_ATOM *at, int num_atoms, int at_no, AT_RANK *nRank2, AT_RANK *nRank1 ) -{ - int i, j, neighbor_no; - for ( i = 0; i < num_atoms; i ++ ) { - if ( nRank2[i] != nRank1[i] ) { - if ( i != at_no /*&& ATOM_PARITY_WELL_DEF(at[i].parity)*/ - && at[i].bHasStereoOrEquToStereo - && !(at[i].stereo_atom_parity & KNOWN_PARITIES_EQL ) - && !at[i].stereo_bond_neighbor[0] - ) { - - return 1; /* may have changed stereo atoms order */ - } - for ( j = 0; j < at[i].valence; j ++ ) { - neighbor_no = at[i].neighbor[j]; - if ( neighbor_no != at_no - /*&& ATOM_PARITY_WELL_DEF(at[neighbor_no].parity)*/ - && at[neighbor_no].bHasStereoOrEquToStereo - && !(at[neighbor_no].stereo_atom_parity & KNOWN_PARITIES_EQL ) - && !at[neighbor_no].stereo_bond_neighbor[0] - ) - return 1; /* may have changed stereo atom parity */ - } - } - } - return 0; -} -/**************************************************************************************/ -#if ( REMOVE_CALC_NONSTEREO == 1 ) /* { */ -/**************************************************************************************/ -void DeAllocateForNonStereoRemoval( AT_RANK **nAtomNumberCanon1, AT_RANK **nAtomNumberCanon2, - NEIGH_LIST **nl, NEIGH_LIST **nl1, NEIGH_LIST **nl2, AT_RANK **nVisited1, AT_RANK **nVisited2 ) -{ - if ( *nAtomNumberCanon1 ) { - inchi_free( *nAtomNumberCanon1 ); - *nAtomNumberCanon1 = NULL; - } - if ( *nAtomNumberCanon2 ) { - inchi_free( *nAtomNumberCanon2 ); - *nAtomNumberCanon2 = NULL; - } - if ( *nl ) { - FreeNeighList( *nl ); - *nl = 0; - } - if ( *nl1 ) { - FreeNeighList( *nl1 ); - *nl1 = 0; - } - if ( *nl2 ) { - FreeNeighList( *nl2 ); - *nl2 = 0; - } - if ( *nVisited1 ) { - inchi_free( *nVisited1 ); - *nVisited1 = NULL; - } - if ( *nVisited2 ) { - inchi_free( *nVisited2 ); - *nVisited2 = NULL; - } - -} -/**************************************************************************************/ -int AllocateForNonStereoRemoval( sp_ATOM *at, int num_atoms, const AT_RANK *nSymmRank, AT_RANK *nCanonRank, - AT_RANK **nAtomNumberCanon1, AT_RANK **nAtomNumberCanon2, - NEIGH_LIST **nl, NEIGH_LIST **nl1, NEIGH_LIST **nl2, AT_RANK **nVisited1, AT_RANK **nVisited2 ) -{ - DeAllocateForNonStereoRemoval( nAtomNumberCanon1, nAtomNumberCanon2, nl, nl1, nl2, nVisited1, nVisited2 ); - *nAtomNumberCanon1 = (AT_RANK *) inchi_malloc( num_atoms * sizeof(**nAtomNumberCanon1) ); - *nAtomNumberCanon2 = (AT_RANK *) inchi_malloc( num_atoms * sizeof(**nAtomNumberCanon2) ); - *nl = CreateNeighList( num_atoms, num_atoms, at, 0, NULL ); - *nl1 = CreateNeighList( num_atoms, num_atoms, at, 0, NULL ); - *nl2 = CreateNeighList( num_atoms, num_atoms, at, 0, NULL ); - *nVisited1 = (AT_RANK *) inchi_malloc( num_atoms * sizeof(**nVisited1) ); - *nVisited2 = (AT_RANK *) inchi_malloc( num_atoms * sizeof(**nVisited2) ); - - if ( !*nl || !*nl1 || !*nl2 || !*nVisited1 || !*nVisited2 || !*nAtomNumberCanon1 || !*nAtomNumberCanon2 ) { - DeAllocateForNonStereoRemoval( nAtomNumberCanon1, nAtomNumberCanon2, nl, nl1, nl2, nVisited1, nVisited2 ); - return 0; - } - /* Sort neighbors according to symm. ranks (primary key) and canon. ranks (secondary key), in descending order */ - SortNeighListsBySymmAndCanonRank( num_atoms, *nl, nSymmRank, nCanonRank ); - SortNeighListsBySymmAndCanonRank( num_atoms, *nl1, nSymmRank, nCanonRank ); - SortNeighListsBySymmAndCanonRank( num_atoms, *nl2, nSymmRank, nCanonRank ); - return 1; -} -/**************************************************************************************/ -AT_RANK GetMinNewRank(AT_RANK *nAtomRank, AT_RANK *nAtomNumb, AT_RANK nRank1 ) -{ - int i; - AT_RANK nRank2; - for ( i = (int)nRank1-1; 0 <= i && nRank1 == (nRank2 = nAtomRank[(int)nAtomNumb[i]]); i -- ) - ; - if ( i >= 0 ) - nRank2 ++; - else - nRank2 = 1; - return nRank2; -} -/**************************************************************************************/ -int BreakNeighborsTie( sp_ATOM *at, int num_atoms, int num_at_tg, int ib, int ia, - AT_RANK *neigh_num, int in1, int in2, int mode, - AT_RANK **pRankStack1, AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, - const AT_RANK *nSymmRank, AT_RANK *nCanonRank, NEIGH_LIST *nl1, NEIGH_LIST *nl2, long *lNumIter ) -{ - AT_RANK nRank1, nRank2; - int nNumDiffRanks, nNumDiffRanks1, nNumDiffRanks2, i; - int n1 = (int)neigh_num[in1]; - int n2 = (int)neigh_num[in2]; - int other_neigh[2], other_neig_ord[2], num_other_neigh; - /* asymmetric calculation */ - - if ( mode == MAP_MODE_S4 && in1 || /* for S4 we need only (in1,in2) = (0,1) (0,2) (0,3) pairs of neighbors */ - mode != MAP_MODE_STD && at[ia].valence != MAX_NUM_STEREO_ATOM_NEIGH || - mode != MAP_MODE_STD && nSymmRank[n1] != nSymmRank[n2] ) { - return 0; - } - /* 1. Create initial ranks from equivalence information stored in nSymmRank */ - memcpy( pRankStack1[0], nSymmRank, num_at_tg * sizeof(pRankStack1[0][0]) ); - pn_RankForSort = pRankStack1[0]; - tsort( pRankStack1[1], num_at_tg, sizeof(pRankStack1[1][0]), CompRanksOrd ); - nNumDiffRanks = SortedEquInfoToRanks( pRankStack1[0]/*inp*/, pRankStack1[0]/*out*/, pRankStack1[1], num_at_tg, NULL ); - - /* other neighbors */ - num_other_neigh = 0; - if ( at[ia].valence <= MAX_NUM_STEREO_ATOM_NEIGH && mode ) { - for ( i = 0; i < at[ia].valence; i ++ ) { - if ( i != in1 && i != in2 ) { - other_neigh[num_other_neigh] = (int)neigh_num[i]; - other_neig_ord[num_other_neigh] = i; - num_other_neigh ++; - } - } - } - if ( mode != MAP_MODE_STD && nSymmRank[other_neigh[0]] != nSymmRank[other_neigh[1]] || - mode == MAP_MODE_S4 && nSymmRank[n1] != nSymmRank[other_neigh[1]] ) { - return 0; - } - - /* 2. Fix at[ia] */ - if ( pRankStack1[0][ia] != nSymmRank[ia] ) { - /* at[ia] is constitutionally equivalent to some other atom. Fix at[ia]. */ - pRankStack1[0][ia] = nSymmRank[ia]; - nNumDiffRanks = DifferentiateRanksBasic( num_at_tg, NeighList, - nNumDiffRanks, pRankStack1[0], nTempRank, - pRankStack1[1], lNumIter, 1 ); - } - /* 3. In case of a double bond/cumulene only: */ - /* fix at[ib] -- the opposite double bond/cumulene atom */ - if ( ib < num_atoms ) { - /* find the smallest possible rank */ - nRank1 = pRankStack1[0][ib]; - nRank2 = GetMinNewRank(pRankStack1[0], pRankStack1[1], nRank1 ); - /* if the rank is smaller than pRankStack1[0][ib] then fix at[ib] */ - if ( nRank2 != nRank1 ) { - pRankStack1[0][ib] = nRank2; - nNumDiffRanks = DifferentiateRanksBasic( num_at_tg, NeighList, - nNumDiffRanks, pRankStack1[0], nTempRank, - pRankStack1[1], lNumIter, 1 ); - } - } - - /************************************************************************************** - * Note: It may (or may not?) make sense to fix "other neighbors": - * in case of a stereo center fix neighbors other than n1, n2 - * in case of a double bond/cumulene fix the opposite atom neighbors - * The ranks assigned to the other neighbors in case of their equivalence - * should be in the ascending order of their canonical ranks ???? - * *** For now we do not fix other neighbors *** - **************************************************************************************/ - - /* 4. Check whether the neighbors still have equal ranks */ - if ( pRankStack1[0][n1] != pRankStack1[0][n2] ) { - return 0; /* the two neighbors are not constitutionally equivalent */ - } - /* 5. Find new smallest possible rank for n1 and n2 */ - nRank1 = pRankStack1[0][n1]; - nRank2 = GetMinNewRank(pRankStack1[0], pRankStack1[1], nRank1 ); - - /* 6. Copy the results to the 2nd eq. rank arrays */ - memcpy( pRankStack2[0], pRankStack1[0], num_at_tg * sizeof(pRankStack2[0][0]) ); - memcpy( pRankStack2[1], pRankStack1[1], num_at_tg * sizeof(pRankStack2[0][0]) ); - - /* 7. Break neighbor tie: map n1(1) <--> n2(2) */ - pRankStack1[0][n1] = nRank2; - nNumDiffRanks1 = DifferentiateRanksBasic( num_at_tg, NeighList, - nNumDiffRanks, pRankStack1[0], nTempRank, - pRankStack1[1], lNumIter, 1 ); - - pRankStack2[0][n2] = nRank2; - nNumDiffRanks2 = DifferentiateRanksBasic( num_at_tg, NeighList, - nNumDiffRanks, pRankStack2[0], nTempRank, - pRankStack2[1], lNumIter, 1 ); - - if ( nNumDiffRanks1 != nNumDiffRanks2 ) { - return -1; /* */ - } - if ( mode == MAP_MODE_C2v || mode == MAP_MODE_C2 ) { - /* Check for C2v reflection leading to parity inversion (mode=1) or C2 rotation (mode=2) */ - AT_RANK nRank10, nRank20; - int nn1, nn2; - /* - * C2v & C2: map - * n1(1) <--> n2(2) -- at this point already done - * n1(2) <--> n2(1) --> do at i = 0 - * - * C2v: other neighbors must be unmoved: map - * other_neigh[0](1) <--> other_neigh[0](2) - * other_neigh[1](1) <--> other_neigh[1](2) - * - * C2: other neighbors should be mapped on each other - * other_neigh[0](1) <--> other_neigh[1](2) - * other_neigh[1](1) <--> other_neigh[0](2) - */ - for ( i = 0; i <= 2; i ++ ) { - if ( i == 0 ) { - /* C2v & C2. Map n2(1) <--> n1(2) */ - nn1 = n2; - nn2 = n1; - } else - if ( mode == MAP_MODE_C2v ) { /* was '=', pointed by WDI */ - /* i = 1 or 2 - * C2v. Other neighbors must be unmoved: map - * i=1: other_neigh[0](1) <--> other_neigh[0](2) - * i=2: other_neigh[1](1) <--> other_neigh[1](2) - */ - nn1 = other_neigh[i-1]; /* 0 or 1 */ - nn2 = other_neigh[i-1]; /* 0 or 1 */ - } else - if ( mode == MAP_MODE_C2 ) { /* was '=', pointed by WDI */ - /* i = 1 or 2 - * C2. Other neighbors should be mapped on each other - * i=1: other_neigh[0](1) <--> other_neigh[1](2) - * i=2: other_neigh[1](1) <--> other_neigh[0](2) - */ - nn1 = other_neigh[i-1]; /* 0 or 1 */ - nn2 = other_neigh[2-i]; /* 1 or 0 */ - } else { - return -1; /* program error */ - } - /* map nn1(1) <--> nn2(2) */ - nRank10 = pRankStack1[0][nn1]; - nRank20 = pRankStack2[0][nn2]; - nRank1 = GetMinNewRank(pRankStack1[0], pRankStack1[1], nRank10 ); - nRank2 = GetMinNewRank(pRankStack2[0], pRankStack2[1], nRank20 ); - if ( nRank10 == nRank20 && nRank1 == nRank2 ) { - if ( nRank10 == nRank1 ) { - ;/* atoms are already mapped */ - } else { - /* need additional mapping: ranks are not fixed yet */ - pRankStack1[0][nn1] = nRank1; - nNumDiffRanks1 = DifferentiateRanksBasic( num_at_tg, NeighList, - nNumDiffRanks, pRankStack1[0], nTempRank, - pRankStack1[1], lNumIter, 1 ); - pRankStack2[0][nn2] = nRank2; - nNumDiffRanks2 = DifferentiateRanksBasic( num_at_tg, NeighList, - nNumDiffRanks, pRankStack2[0], nTempRank, - pRankStack2[1], lNumIter, 1 ); - if ( nNumDiffRanks1 != nNumDiffRanks2 ) { - return -1; /* */ - } - } - } else { - return 0; /* mapping is not possible */ - } - } - } - if ( mode == MAP_MODE_S4 ) { - /* - * Check for S4 reflection/rotation leading to parity inversion (mode=3) - * - * At this point n1(1) <--> n2(2) have been mapped and n1 has index in1 = 0 - * Below indexes in neigh_num[] are in brackets; [i] means neigh_num[i]. - * Numbers (#) in parentheses refer to pRankStack# - * - * in2=1: [0](1) <--> [1](2) mapping has been done; add more mappings: - * [1](1) <--> [2](2) [x]=[2] - * [2](1) <--> [3](2) [y]=[3] - * [3](1) <--> [0](2) - * this will succeed if C2 axis crosses middle of [0]-[2] and [1]-[3] lines - * - * in2=2: [0](1) <--> [2](2) mapping has been done; add more mappings: - * [2](1) <--> [3](2) [x]=[3] - * [3](1) <--> [1](2) [y]=[1] - * [1](1) <--> [0](2) - * this will succeed if C2 axis crosses middle of [0]-[3] and [1]-[2] lines - * - * in2=3: [0](1) <--> [3](2) mapping has been done; add more mappings: - * [3](1) <--> [1](2) [x]=[1] - * [1](1) <--> [2](2) [y]=[2] - * [2](1) <--> [0](2) - * this will succeed if C2 axis crosses middle of [0]-[1] and [2]-[3] lines - * - * In general: - * [in1](1) <--> [in2](2) - * [in2](1) <--> [x] (2) i=0 - * [x] (1) <--> [y] (2) i=1 - * [y] (1) <--> [in1](2) i=2 - * - * in1=0 always - * ===== how to find x, y from in2 ==== - * in2=1 => x,y = 2, 3 or [x] = other_neigh[0], [y] = other_neigh[1] - * in2=2 => x,y = 3, 1 or [x] = other_neigh[1], [y] = other_neigh[0] - * in2=3 => x,y = 1, 2 or [x] = other_neigh[0], [y] = other_neigh[1] - * ==================================== - */ - AT_RANK nRank10, nRank20; - int nn1, nn2; - for ( i = 0; i <= 2; i ++ ) { - switch( i ) { - case 0: /* [in2](1) <--> [x](2); */ - nn1 = n2; /* [in2] */ - nn2 = other_neigh[1-in2%2]; /* [x] */ - break; - case 1: /* [x](1) <--> [y](2) */ - nn1 = other_neigh[1-in2%2]; /* [x] */ - nn2 = other_neigh[ in2%2]; /* [y] */ - break; - case 2: - nn1 = other_neigh[ in2%2]; /* [y] */ - nn2 = n1; /* [in1] */ - break; - default: - return -1; /* program error */ - } - /* map nn1(1) <--> nn2(2) */ - nRank10 = pRankStack1[0][nn1]; - nRank20 = pRankStack2[0][nn2]; - nRank1 = GetMinNewRank(pRankStack1[0], pRankStack1[1], nRank10 ); - nRank2 = GetMinNewRank(pRankStack2[0], pRankStack2[1], nRank20 ); - if ( nRank10 == nRank20 && nRank1 == nRank2 ) { - if ( nRank10 == nRank1 ) { - ;/* atoms are already mapped */ - } else { - /* need additional mapping: ranks are not fixed yet */ - pRankStack1[0][nn1] = nRank1; - nNumDiffRanks1 = DifferentiateRanksBasic( num_at_tg, NeighList, - nNumDiffRanks, pRankStack1[0], nTempRank, - pRankStack1[1], lNumIter, 1 ); - pRankStack2[0][nn2] = nRank2; - nNumDiffRanks2 = DifferentiateRanksBasic( num_at_tg, NeighList, - nNumDiffRanks, pRankStack2[0], nTempRank, - pRankStack2[1], lNumIter, 1 ); - if ( nNumDiffRanks1 != nNumDiffRanks2 ) { - return -1; /* */ - } - } - } else { - return 0; /* mapping is not possible */ - } - } - } - - - -#if ( BREAK_ONE_MORE_SC_TIE == 1 ) /* { */ - /* Check for a very highly symmetrical stereo center 12-06-2002 */ - if ( ib >= num_atoms && at[ia].valence == MAX_NUM_STEREO_ATOM_NEIGH ) { - int num_eq; - nRank1 = pRankStack1[0][n2]; - for ( i = 0, num_eq = 0; i < at[ia].valence; i ++ ) { - num_eq += ( nRank1 == pRankStack1[0][at[ia].neighbor[i]]); - } - if ( num_eq == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { - for ( i = (int)nRank1-1; 0 <= i && nRank1 == (nRank2 = pRankStack1[0][(int)pRankStack1[1][i]]); i -- ) - ; - if ( i >= 0 ) - nRank2 ++; - else - nRank2 = 1; - - /* 7a. Break another neighbor tie */ - - nNumDiffRanks = nNumDiffRanks1; - - pRankStack1[0][n2] = nRank2; - nNumDiffRanks1 = DifferentiateRanksBasic( num_at_tg, NeighList, - nNumDiffRanks, pRankStack1[0], nTempRank, - pRankStack1[1], lNumIter, 1 ); - - pRankStack2[0][n1] = nRank2; - nNumDiffRanks2 = DifferentiateRanksBasic( num_at_tg, NeighList, - nNumDiffRanks, pRankStack2[0], nTempRank, - pRankStack2[1], lNumIter, 1 ); - } - } - - if ( nNumDiffRanks1 != nNumDiffRanks2 ) { - return -1; /* */ - } -#endif /* } BREAK_ONE_MORE_SC_TIE */ - -#if ( BREAK_ALSO_NEIGH_TIE == 1 ) - /* check whether neighbor's neighbors are tied and untie them */ - if ( at[n1].nRingSystem == at[n2].nRingSystem && ib >= num_atoms ) { - AT_RANK NeighNeighList[MAX_NUM_STEREO_ATOM_NEIGH+1]; - int m, neigh1=-1, neigh2=-1; - nRank1 = nRank2 = 0; - /* n1 */ - NeighNeighList[0] = at[n1].valence-1; /* for insertions_sort_NeighListBySymmAndCanonRank() */ - for ( i = 0, m = 1; i < at[n1].valence; i ++ ) { - int neigh = at[n1].neighbor[i]; - if ( neigh != ia ) { - NeighNeighList[m ++] = neigh; - } - } - insertions_sort_NeighListBySymmAndCanonRank( NeighNeighList, pRankStack1[0], nCanonRank ); - for ( m = 2; m < at[n1].valence; m ++ ) { - if ( pRankStack1[0][NeighNeighList[m]] == pRankStack1[0][NeighNeighList[m-1]] ) { - neigh1 = NeighNeighList[m-1]; - break; - } - } - /* n2 */ - NeighNeighList[0] = at[n2].valence-1; /* for insertions_sort_NeighListBySymmAndCanonRank() */ - for ( i = 0, m = 1; i < at[n2].valence; i ++ ) { - int neigh = at[n2].neighbor[i]; - if ( neigh != ia ) { - NeighNeighList[m ++] = neigh; - } - } - insertions_sort_NeighListBySymmAndCanonRank( NeighNeighList, pRankStack2[0], nCanonRank ); - for ( m = 2; m < at[n2].valence; m ++ ) { - if ( pRankStack2[0][NeighNeighList[m]] == pRankStack2[0][NeighNeighList[m-1]] ) { -#if ( BREAK_ALSO_NEIGH_TIE_ROTATE == 1 ) - neigh2 = NeighNeighList[m]; /* [m] to obtain same axis orientation around ia= 0 && neigh2 >= 0 && pRankStack1[0][neigh1] == pRankStack2[0][neigh2] ) { - /* neighbors' neighbors are tied */ - nRank1 = pRankStack1[0][neigh1]; - nRank2 = GetMinNewRank(pRankStack1[0], pRankStack1[1], nRank1 ); - - /* Break neighbor's neighbor tie */ - - nNumDiffRanks = nNumDiffRanks1; - - pRankStack1[0][neigh1] = nRank2; - nNumDiffRanks1 = DifferentiateRanksBasic( num_at_tg, NeighList, - nNumDiffRanks, pRankStack1[0], nTempRank, - pRankStack1[1], lNumIter, 1 ); - - pRankStack2[0][neigh2] = nRank2; - nNumDiffRanks2 = DifferentiateRanksBasic( num_at_tg, NeighList, - nNumDiffRanks, pRankStack2[0], nTempRank, - pRankStack2[1], lNumIter, 1 ); - } - } -#endif - - - /* for debug only */ - for ( i = 0; i < num_at_tg; i ++ ) { - if ( pRankStack1[0][(int)pRankStack1[1][i]] != pRankStack2[0][(int)pRankStack2[1][i]] ) { - return -1; /* */ - } - } - /* Resort lists of neighbors */ - SortNeighListsBySymmAndCanonRank( num_atoms, nl1, pRankStack1[0], nCanonRank ); - SortNeighListsBySymmAndCanonRank( num_atoms, nl2, pRankStack2[0], nCanonRank ); - - return nNumDiffRanks1+1; -} - -/**************************************************************************************/ -int CheckNextSymmNeighborsAndBonds( sp_ATOM *at, AT_RANK cur1, AT_RANK cur2, AT_RANK n1, AT_RANK n2, - AT_RANK *nAvoidCheckAtom, AT_RANK *nVisited1, AT_RANK *nVisited2, - AT_RANK *nVisitOrd1, AT_RANK *nVisitOrd2, const AT_RANK *nRank1, const AT_RANK *nRank2 ) -{ - AT_RANK s1, s2; - int i1, i2, k1, k2; - if ( nRank1[n1] != nRank2[n2] ) { - return -1; /* parallel traversal in stereo removal failed */ /* */ - } - switch ( !nVisited1[n1] + !nVisited2[n2] ) { - case 0: - if ( nVisited1[n1] != n2+1 || nVisited2[n2] != n1+1 ) { - return -1; /* 0; */ /* possibly error???: we have come to an alreardy traversed pair and */ - /* found that the pair previously has not been traversed synchroneously. */ - } /* -- Happens in C60. */ - break; - case 1: - return -1; /* 0; */ /* possibly error: one is zero, another is not a zero. Happens in C60 */ - - /* case 2: */ - /* both are zero, OK. */ - } - - if ( nVisitOrd1[n1] != nVisitOrd2[n2] ) { - return -1; /* 0; */ /* different DFS trees */ - } - /* at[n1] and at[n2] are next to at[cur1] and at[cur2] respectively */ - /* Even though the bond might have already been checked, check whether */ - /* it is a stereo bond/cumulene. If it is, check the bond/cumulene parity. */ - - /* Even though the bond or cumulene might have already been checked, check it: this is */ - /* the only place we can check stereo bonds and cumulenes that are not edges of the DFS tree */ - /* The code works both for a stereo bond and a stereogenic cumulene. */ - - for ( i1 = 0, k1 = 0; i1 < MAX_NUM_STEREO_BONDS && - (s1=at[cur1].stereo_bond_neighbor[i1]) && - !(k1=(at[cur1].neighbor[(int)at[cur1].stereo_bond_ord[i1]] == n1)); i1 ++ ) - ; - for ( i2 = 0, k2 = 0; i2 < MAX_NUM_STEREO_BONDS && - (s2=at[cur2].stereo_bond_neighbor[i2]) && - !(k2=(at[cur2].neighbor[(int)at[cur2].stereo_bond_ord[i2]] == n2)); i2 ++ ) - ; - - /* -- this does not work in case of cumulenes -- - for ( i1 = 0, k1 = 0; i1 < MAX_NUM_STEREO_BONDS && (s1=at[cur1].stereo_bond_neighbor[i1]) && !(k1=(s1-1 == n1)); i1 ++ ) - ; - for ( i2 = 0, k2 = 0; i2 < MAX_NUM_STEREO_BONDS && (s2=at[cur2].stereo_bond_neighbor[i2]) && !(k2=(s2-1 == n2)); i2 ++ ) - ; - */ - - if ( k1 != k2 ) { - return 0; /* not an error: a stereo bond and not a stereo bond */ - } - if ( k1 ) { - /* here k1 == k2 */ - int bCheckBond1, bCheckBond2; - s1 --; - s2 --; - - bCheckBond1 = (cur1 != nAvoidCheckAtom[0] || s1 != nAvoidCheckAtom[1]) && - (cur1 != nAvoidCheckAtom[1] || s1 != nAvoidCheckAtom[0]); - bCheckBond2 = (cur2 != nAvoidCheckAtom[0] || s2 != nAvoidCheckAtom[1]) && - (cur2 != nAvoidCheckAtom[1] || s2 != nAvoidCheckAtom[0]); - - if ( bCheckBond1 != bCheckBond2 ) - return 0; - - if ( !bCheckBond1 && !bCheckBond2 ) { - return 1; /* do not go any further in this direction */ - } - - if ( at[cur1].stereo_bond_parity[i1] != at[cur2].stereo_bond_parity[i2] ) { - /* different values of at[].stereo_bond_parity: definitely different bonds */ - /* known parities */ - if ( PARITY_KNOWN(at[cur1].stereo_bond_parity[i1] ) && - PARITY_KNOWN(at[cur2].stereo_bond_parity[i2] ) ) { - return 0; /* different currently known stereo bond parities */ - } -#if ( PROPAGATE_ILL_DEF_STEREO != 1 ) - /* well defined and to be calculated from the ranks */ - if ( !(PARITY_CALCULATE(at[cur1].stereo_bond_parity[i1]) && PARITY_WELL_DEF (at[cur2].stereo_bond_parity[i2]) || - PARITY_WELL_DEF (at[cur1].stereo_bond_parity[i1]) && PARITY_CALCULATE(at[cur2].stereo_bond_parity[i2]) || - PARITY_CALCULATE(at[cur1].stereo_bond_parity[i1]) && PARITY_CALCULATE(at[cur2].stereo_bond_parity[i2]) ) ) { - /* do not reject if: "well defined" and "calculate" or "calculate" and "calculate" */ - return 0; - } -#endif - } - -#if ( PROPAGATE_ILL_DEF_STEREO != 1 ) - if ( (cur1 != cur2 || s1 != s2) && (cur1 != s2 || cur2 != s1) ) { - /* two different stereo bonds */ - if ( PARITY_ILL_DEF( at[cur1].stereo_bond_parity[i1] ) || - PARITY_ILL_DEF( at[cur2].stereo_bond_parity[i2] ) ) { - return 0; - } - } -#endif - } - return 1; /* stereo bonds to n1 and n2 have same known parities or are not stereo bonds */ -} -/**************************************************************************************/ -int CreateCheckSymmPaths( sp_ATOM *at, AT_RANK prev1, AT_RANK cur1, AT_RANK prev2, AT_RANK cur2, - AT_RANK *nAvoidCheckAtom, AT_RANK *nVisited1, AT_RANK *nVisited2, - AT_RANK *nVisitOrd1, AT_RANK *nVisitOrd2, - NEIGH_LIST *nl1, NEIGH_LIST *nl2, const AT_RANK *nRank1, const AT_RANK *nRank2, - AT_RANK *nCanonRank, AT_RANK *nLength, int *bParitiesInverted, int mode ) -{ - int k, k1, k2, ret=0, bParitiesInvertedZero=0, *pbParitiesInverted; - AT_RANK n1, n2; - - nVisited1[cur1] = cur2+1; /* symmetrically exchange atom numbers */ - nVisited2[cur2] = cur1+1; - - (*nLength) ++; - - nVisitOrd1[cur1] = *nLength; /* save DFS visit order */ - nVisitOrd2[cur2] = *nLength; - - /* new version allows all inverted parities */ - if ( PARITY_WELL_DEF(at[cur1].stereo_atom_parity) && - PARITY_WELL_DEF(at[cur2].stereo_atom_parity) ) { - if ( *bParitiesInverted < 0 ) { - *bParitiesInverted = (at[cur1].stereo_atom_parity + at[cur2].stereo_atom_parity) % 2; - } else - if ( *bParitiesInverted != (at[cur1].stereo_atom_parity + at[cur2].stereo_atom_parity) % 2 ) { - return 0; /* Different known in advance parities have wrong "inverted" relation */ - } - } else - if ( PARITY_KNOWN(at[cur1].stereo_atom_parity) && - PARITY_KNOWN(at[cur2].stereo_atom_parity) && - at[cur1].stereo_atom_parity != at[cur2].stereo_atom_parity ) { - return 0; /* Different known in advance parities */ - } - - if ( cur1 != cur2 && - !at[cur1].stereo_bond_neighbor[0] && !at[cur2].stereo_bond_neighbor[0] && - PARITY_KNOWN(at[cur1].parity) != PARITY_KNOWN(at[cur2].parity) ) { - return 0; /* one atom is stereogenic, another (presumably equivalent) is not. 9-11-2002 */ - } -#if ( PROPAGATE_ILL_DEF_STEREO != 1 ) - if ( cur1 != cur2 && - (PARITY_ILL_DEF(at[cur1].stereo_atom_parity) || - PARITY_ILL_DEF(at[cur2].stereo_atom_parity)) - ) { - return 0; /* Cannot detect whether the paths are same or different */ - } -#endif - - if ( at[cur1].valence != at[cur2].valence ) { - return CT_REMOVE_STEREO_ERR; /* program error */ /* */ - } - if ( at[cur1].valence == 1 ) { - return 1; /* so far success */ - } - - if ( nl1[(int)cur1][0] != nl2[(int)cur2][0] || nl1[(int)cur1][0] != at[cur1].valence ) { - return CT_REMOVE_STEREO_ERR; /* error: different valences */ /* */ - } - - - for ( k = 1, k1 = 1, k2 = 1; k < at[cur1].valence; k ++, k1 ++, k2 ++ ) { - if ( (n1 = nl1[(int)cur1][k1]) == prev1 ) { - n1 = nl1[(int)cur1][++k1]; /* don't go back */ - } - if ( (n2 = nl2[(int)cur2][k2]) == prev2 ) { - n2 = nl2[(int)cur2][++k2]; /* don't go back */ - } - - if ( 0 >= (ret = CheckNextSymmNeighborsAndBonds( at, cur1, cur2, n1, n2, nAvoidCheckAtom, - nVisited1, nVisited2, nVisitOrd1, nVisitOrd2, nRank1, nRank2 ) ) ) { - return ret; /* different neighbors or bonds */ - } - - if ( !nVisited1[n1] ) { /* recursion */ - /* allow all inverted parities only inside a single ring system containing the starting point */ - pbParitiesInverted = (at[cur1].nRingSystem == at[n1].nRingSystem)? bParitiesInverted:&bParitiesInvertedZero; - if ( 0 >= (ret = CreateCheckSymmPaths( at, cur1, n1, cur2, n2, nAvoidCheckAtom, - nVisited1, nVisited2, nVisitOrd1, nVisitOrd2, - nl1, nl2, nRank1, nRank2, nCanonRank, nLength, pbParitiesInverted, mode ) ) ) { - return ret; - } - } - } - return 1; /* Success */ - -} -/**************************************************************************************/ -/* Compare parities */ -#define MAX_OTHER_NEIGH 2 -/* nNeighMode */ -#define NEIGH_MODE_RING 1 -#define NEIGH_MODE_CHAIN 2 - -#define CHECKING_STEREOCENTER 1 -#define CHECKING_STEREOBOND 2 - -#define COMP_STEREO_SUCCESS 1 -#define NOT_WELL_DEF_UNKN 2 -#define NOT_WELL_DEF_UNDF 4 - -#define PARITY_IMPOSSIBLE 999 -/************************************************************************************** - Note: the following C2v/S4 stereo center symmetry recognition - is not included in the final InChI version released in April 2005 - It is disabled in the mode.h (CHECK_C2v_S4_SYMM = 0) - As the result, the only central atom in S4 or atoms on C2v axis - may have pasrity (-) even though these atoms are not stereogenic. - - Reason: Not finished/tested yet - ************************************************************************************** - - In case of stereocenter with 2 pairs of constitutionally identical neighbors : - - G(n) > H(m) means group G has n elements; group H has m elements and - group H is a subgroup of G - - Td(24) > D2d(8> > D2(4) - > S4(4) > C2(2) -- Test for S4 - > C2v(4) > C2(2) -- Test for C2v - > Cs(2) - - Td(24) > C3v(6) > C3(3) -- does not have 2 pairs of constitutionally identical neighbors - > Cs(2) - - The pair of atoms to check for the existence of a steregenic atom: X, Y - - X Y - \ / - C - / \ - A B - - Conditions to check: - - (a) Old #0: Map canonical numbers X1 <--> Y2 - Traverse DFS from X and Y - If all parities vs. canon. numbers unchanged except that of C - then C is not stereogenic - - (b) C2v #1: discover ACB symmetry plain Cv - o Map canonical numbers X1 <--> Y2, Fix(Ai), Fix(Bi) - o Make sure that after mapping X1 <--> Y2 the atoms Ai and - Bi still have equal mapping ranks - Traverse DFS from X and Y - In this case canonical numbers will be reflected in plane ACB if it exists. - o Criterion of the presence of the symmetry plain is: - --> all stereogenic atoms and allenes parities are inverted - (c) C2v #2: discover vertical axis C2 - o Map canonical numbers X1 <--> Y2 and A1 <--> B2 - o Make sure that after mapping X1 <--> Y2 the atoms Ai and - Bi still have equal mapping ranks - o Traverse DFS from X1 and Y2 - In this case canonical numbers will be rotated by - 180 degrees around the vertical axis - (this may be considered as a superposition of two Cv - reflections in perpendicular vertical planes) - o Criterion of the presence of the C2 axis is: - --> all stereogenic atoms and allenes parities are not changed - (d) S4 #3: discover axis horizontal S4 axis - o Map canonical numbers X1 <--> Y2, Y1 <--> A2, A1 <--> B2, B1 <--> X2 - o Traverse DFS from X1 and Y2 - In this case the canonical numbers will be rotated by - 90 degrees and reflected in a horizontal plane. - 3 attempts corrresponding to transpositions 0132, 0213, 0321 - are sufficient (XY=01,02,03) - o Criterion of the presence of the S4 symmetry axis is: - --> all stereogenic atoms and allenes parities are inverted - -***************************************************************************************/ - -/**************************************************************************************/ -int CalculatedPathsParitiesAreIdentical( sp_ATOM *at, int num_atoms, const AT_RANK *nSymmRank, - AT_RANK *nCanonRank, AT_RANK *nAtomNumberCanon, - AT_RANK *nAtomNumberCanon1, AT_RANK *nAtomNumberCanon2, - AT_RANK *nVisited1, AT_RANK *nVisited2, - AT_RANK prev_sb_neigh, AT_RANK cur, AT_RANK next1, AT_RANK next2, int nNeighMode, - int bParitiesInverted, int mode, CANON_STAT *pCS, - int vABParityUnknown) -{ - int i, i01, i02, i11, i12, i21, i22, k, parity, parity1, parity2, parity12, num_other_neigh; - int nNumEqStereogenic, nCheckingMode, not_well_def_parities; - AT_RANK other_neigh[MAX_NUM_STEREO_ATOM_NEIGH], neigh, r1, r2; - int nNumComparedCenters = 0, nNumComparedBonds = 0, bCurParityInv1=0 /*, bCurParityInv2=0*/; - int bCurRotated=0, nNumDiff=0, nNumInv=0; - int s1, s2; - - nCheckingMode = ( prev_sb_neigh < num_atoms )? CHECKING_STEREOBOND : CHECKING_STEREOCENTER; - not_well_def_parities = 0; - nNumEqStereogenic = 0; - - if ( nNeighMode != NEIGH_MODE_RING && - bParitiesInverted != 0 || abs(bParitiesInverted) != 1 ) { - bParitiesInverted = 0; - } - - if ( bParitiesInverted ) { - for ( i = 0, i11 = i22 = 0; i < num_atoms; i ++ ) { - /* count number of atoms that have not been visited */ - i11 += !nVisited1[i]; - i22 += !nVisited2[i]; - nAtomNumberCanon1[i] = MAX_ATOMS+1; /* mark unchanged */ - nAtomNumberCanon2[i] = MAX_ATOMS+1; /* mark unchanged */ - } - if ( i11 || i22 ) { - if ( bParitiesInverted == 1 ) - return 0; /* only a part of the structure has been inverted */ - else - bParitiesInverted = 0; - } - } else { - for ( i = 0; i < num_atoms; i ++ ) { - nAtomNumberCanon1[i] = MAX_ATOMS+1; /* mark unchanged */ - nAtomNumberCanon2[i] = MAX_ATOMS+1; /* mark unchanged */ - } - } - if ( bParitiesInverted > 0 && !(mode == MAP_MODE_C2v || mode == MAP_MODE_S4) || - bParitiesInverted == 0 && !(mode == MAP_MODE_C2 || mode == MAP_MODE_STD)) { - return 0; - } - /************************************************************************************** - * The following discussion assumes that the canonical numbers are - * switched for some pairs of constitutionally identical atoms - * in such a way that the new numbering is an equivalent to the - * nCanonRank[] canonical numbering (the transposition belongs to the - * automorphism group of the chemical structure having no stereo). - * At this point non-zero elements of nVisited1[] and nVisited2[] - * together contain transposition P of the atom numbers. - * and P2 respectively of the ordering atom numbers: nVisitedi[k] = Pi(k)+1; - * In this implementation: - * P1(k)=k for all k - * P2(cur)=cur, P2(next1)=next2, P2(next2)=next1 - * - * Below we call one of the numberings "old", another "new". - * - * *IF* the old and the new canonical numberings produce same parities for stereogenic - * elements for the same canonical number(s) - * (that is, old_parity(canon_number) == new_parity(canon_number) - * *except* the currently being tested stereocenter at[cur] or stereobond/cumulene - * at[cur]=at[prev_sb_neigh], whose parity MUST be inverted - * - * *THEN* the stereocenter or stereobond/cumulene is not stereogenic with one - * - * *EXCEPTION* If the currently tested stereogenic element is constitutionally - * equivalent to two or more other stereogenic elements that have been - * permuted then the currently tested one is still stereogenic. - **************************************************************************************/ - - /* - * 1. replace the assigned in each of the parallel traversals atom numbers - * with the canon. ranks corresponding to the atom numbers in the - * currently numbered atoms at[]. - * One of obtained this way canonical numberings (probably nVisited1[]) - * is same as the nCanonRank[] because usually nVisited1[i] = i+1 or 0 - */ - for ( i = 0; i < num_atoms; i ++ ) { - - if ( nVisited1[i] ) { - /* canonical number of the atom mapped on atom #i in 'left' path */ - nVisited1[i] = nCanonRank[ (int)nVisited1[i] - 1 ]; - /* reverse: atom # from the mapped canonical rank in 'left' path */ - nAtomNumberCanon1[nVisited1[i] - 1] = i; - } - if ( nVisited2[i] ) { - /* canonical number of the atom mapped on atom #i in 'right' path */ - nVisited2[i] = nCanonRank[ (int)nVisited2[i] - 1 ]; - /* reverse: atom # from the mapped canonical rank in 'right' path */ - nAtomNumberCanon2[nVisited2[i] - 1] = i; - } - /* if 'left' and 'right' path do not have atoms in common except the - starting atom (and in case of stereobond, the end atom) some of - nVisitedi[i] elements may be zero. - */ - } - - /* - * if started with a stereobond then check whether its parity has changed. - * If yes then continue, otherwise parities are different - * - * if started with a stereo center then prev_sb_neigh = MAX_ATOMS+1 - * - * If the transposition of next1 and next2 changes only the parity of the starting stereo atom or stereo bond - * then the stereo bond or stereo atom is not stereogenic - * - * The exception: the stereogenic elememt in question is equivalent - * to two or more traversed other stereogenic elememts - * (see nNumEqStereogenic below, case similar to trimethylcyclopropane: - * 3 or more constitutionally equivalent stereogenic elements) - */ - if ( nCheckingMode == CHECKING_STEREOBOND ) { - /****************************************************************************** - * - * Possibly stereogenic starting bond or cumulene at[cur]-at[prev_sb_neigh] - * - *******************************************************************************/ - /* checking the starting stereo bond */ - if ( nVisited1[prev_sb_neigh] || nVisited2[prev_sb_neigh] ) { - /* the bond or cumulene is in the ring and the opposite atom has been visited */ - if ( nVisited1[prev_sb_neigh] != nVisited2[prev_sb_neigh] || - nCanonRank[prev_sb_neigh] != nVisited2[prev_sb_neigh] ) { - return 0; /* error: we came back to the same bond/cumulene and */ /* */ - /* assigned different canon. ranks to the opposite atom. */ - } - if ( at[prev_sb_neigh].valence + at[prev_sb_neigh].num_H > 3 ) - return 0; /* at[prev_sb_neigh] atom can not be adjacent to a stereo bond/cumulene */ - /* or does not have 3 attachments (hydrogens are not considered here) */ - for ( i = 0, k = 0; i < MAX_NUM_STEREO_BONDS && - (neigh=at[prev_sb_neigh].stereo_bond_neighbor[i]) && !(k=(neigh-1 == cur)); i ++ ) - ; - if ( !k ) { - return -1; /* program error: could not locate stereogenic bond mark on the opposite atom */ - } - k = (int)at[prev_sb_neigh].stereo_bond_ord[i]; /* seq. number of the double or cumulene bond on at[prev_sb_neigh] */ - - for ( i = 0, num_other_neigh = 0; i < at[prev_sb_neigh].valence && num_other_neigh <= MAX_OTHER_NEIGH; i ++ ) { - if ( i != k ) { /* do not include the double or cumulene bond */ - other_neigh[num_other_neigh ++] = at[prev_sb_neigh].neighbor[i]; - } - } - if ( num_other_neigh + at[prev_sb_neigh].num_H > MAX_OTHER_NEIGH ) { - return CT_STEREOCOUNT_ERR; /* */ - } - for ( i = 0; i < num_other_neigh; i ++ ) { - k = (int)other_neigh[i]; - if ( nVisited1[k] && nVisited1[k] != nCanonRank[k] ) { - return 0; /* parity of the statring stereo bond/cumulene has not changed. */ - } - if ( nVisited2[k] && nVisited2[k] != nCanonRank[k] ) { - return 0; /* parity of the statring stereo bond/cumulene has not changed. */ - } - } - } - } - if ( nCheckingMode == CHECKING_STEREOCENTER ) { - /************************************************** - * - * Possibly stereogenic starting atom at[cur] - * - **************************************************/ - /* checking the starting stereo center */ - for ( i = 0, num_other_neigh = 0; i < at[cur].valence && num_other_neigh <= MAX_OTHER_NEIGH; i ++ ) { - neigh = at[cur].neighbor[i]; - if ( neigh != next1 && neigh != next2 ) { - other_neigh[num_other_neigh ++] = neigh; - } - } - if ( num_other_neigh + at[cur].num_H > MAX_OTHER_NEIGH ) { - return CT_STEREOCOUNT_ERR; /* */ - } - /* - if ( bParitiesInverted && at[cur].valence == MAX_NUM_STEREO_ATOM_NEIGH ) { - if ( nVisited1[other_neigh[0]] == nCanonRank[other_neigh[0]] || - nVisited2[other_neigh[0]] == nCanonRank[other_neigh[0]] || - nVisited1[other_neigh[1]] == nCanonRank[other_neigh[1]] || - nVisited2[other_neigh[1]] == nCanonRank[other_neigh[1]] ) { - bParitiesInverted = 0; - bCurRotated = 1; - } - } - */ - /* bParitiesInverted = -1 means no predefined stereocenter has been checked */ - if ( bParitiesInverted && at[cur].valence == MAX_NUM_STEREO_ATOM_NEIGH ) { - /* special case: 4 canonically eq. neighbors */ - int canon_parity, parity_vis_1, parity_vis_2; - canon_parity = GetPermutationParity( at+cur, MAX_ATOMS+1, nCanonRank ); - parity_vis_1 = GetPermutationParity( at+cur, MAX_ATOMS+1, nVisited1 ); - parity_vis_2 = GetPermutationParity( at+cur, MAX_ATOMS+1, nVisited2 ); - if ( parity_vis_1 != parity_vis_2 ) { - return 0; - } - if ( bParitiesInverted == 1 && parity_vis_1 == canon_parity ) { - return 0; /* not a typical case of inversion during the mapping of D4h stereocenter */ - } else - if ( bParitiesInverted == -1 ) { - if ( parity_vis_1 == canon_parity ) { - bParitiesInverted = 0; - } else { - bParitiesInverted = 1; - } - } - } - /* at this point bParitiesInverted >= 0 */ - if ( !bParitiesInverted && !bCurRotated ) { - for ( i = 0; i < num_other_neigh; i ++ ) { - k = (int)other_neigh[i]; - if ( nVisited1[k] && nVisited1[k] != nCanonRank[k] ) { - return 0; /* parity of the statring stereo center has not changed. */ - } - if ( nVisited2[k] && nVisited2[k] != nCanonRank[k] ) { - return 0; /* parity of the statring stereo center has not changed. */ - } - } - } - } - - /***************************************************** - * Check other (non-starting) stereo centers - ******************************************************/ - for ( i = 0; i < pCS->nLenLinearCTStereoCarb; i ++, nNumComparedCenters += (k > 0) ) { - r1 = pCS->LinearCTStereoCarb[i].at_num; - i01 = nAtomNumberCanon[r1-1]; /* ord. number of the atom that has canon rank r1 */ - - i11 = nAtomNumberCanon1[r1-1]; /* = (MAX_ATOMS+1) > num_atoms if the atom has not been traversed */ - i12 = nAtomNumberCanon2[r1-1]; /* = otherwise < num_atoms */ - - s1 = (i11 < num_atoms); /* 1 => the center was traversed on path #1 */ - s2 = (i12 < num_atoms); /* 1 => the center was traversed on path #2 */ - - bCurParityInv1 = (bParitiesInverted && - at[cur].nRingSystem == at[i11].nRingSystem && - at[cur].nRingSystem == at[i12].nRingSystem ); - - - k = 0; - - /* check whether the two stereo centers (they can be one and the same atom) have been traversed */ - if ( !s1 && !s2 ) { - continue; /* Both stereo centers have not been traversed; check the next pair. */ - } - - if ( nCheckingMode == CHECKING_STEREOCENTER ) { - /* check whether the stereocenters are the starting stereocenter */ - switch( (cur == i11) + (cur == i12) ) { - case 2: - continue; /* do not recheck the starting atom */ - case 1: - return -1; /* possibly program error */ /* */ - /* case 0: */ - /* break; */ /* the stereo centers are not the sarting stereo center */ - } - if ( cur == i01 ) { - return -1; /* program error: in this case at least one of the i11, i12 must be == cur */ /* */ - } - } - - if ( nNeighMode == NEIGH_MODE_RING ) { - if ( i11 != i12 && !bCurParityInv1 ) { - return -1; /* failed: the two stereo atoms have not been traversed synchronously */ - } - if ( !at[i11].parity || !at[i12].parity ) { - return 0; /* another atom does not have parity (it might have been removed) 9-11-2002 */ - } - } - if ( nNeighMode == NEIGH_MODE_CHAIN ) { - if ( s1+s2 != 1 ) { - return -1; /* program error: only one out of s1 and s2 must be 1, another must be 0. */ - } - if ( s1 && !at[i11].parity || s2 && !at[i12].parity ) { - return 0; /* another atom does not have parity (it might have been removed) 9-11-2002 */ - } - } - - parity = pCS->LinearCTStereoCarb[i].parity; - if ( nNeighMode == NEIGH_MODE_RING && (i11 != i01) && (i12 != i01) || - /* in NEIGH_MODE_RING case we know that i11 == i12 except bCurParityInv1 == 1 */ - nNeighMode == NEIGH_MODE_CHAIN - /* in NEIGH_MODE_CHAIN case here we always have 2 different atoms */ - ) { - /**************************************************************** - * Case of two transposed atoms or a circular permutation in D4h - */ - parity1 = s1? GetStereoCenterParity( at, i11, nVisited1 ) : PARITY_IMPOSSIBLE; - parity2 = s2? GetStereoCenterParity( at, i12, nVisited2 ) : PARITY_IMPOSSIBLE; - if ( !ATOM_PARITY_KNOWN(parity1) && !ATOM_PARITY_KNOWN(parity2) ) { - return -1; /* should not happen: must have been detected at the time of the traversal */ - } - if ( s1 && s2 ) { - if ( bCurParityInv1 ) { - int parity1orig = GetStereoCenterParity( at, i11, nCanonRank ); - int parity2orig = GetStereoCenterParity( at, i12, nCanonRank ); - if ( i11 == i12 || - (parity1 == parity1orig || parity2 == parity2orig || parity1 != parity2) && - ATOM_PARITY_WELL_DEF(parity1) || - parity1 != parity2 && (!ATOM_PARITY_WELL_DEF(parity1) || - !ATOM_PARITY_WELL_DEF(parity2)) ) - /*return -1; */ /* should be different atoms with inverted parities */ - nNumDiff ++; - } else { - if ( i11 != i12 || parity1 != parity2 ) - return -1; /* program error: must be the same atom */ - } - } - parity12 = s1? parity1 : parity2; - - if ( ATOM_PARITY_WELL_DEF(parity) && parity == parity12 ) { - /* symmetrical neighbors have well-defined equal parities */ - k ++; - if ( nCheckingMode == CHECKING_STEREOCENTER && nNeighMode == NEIGH_MODE_RING ) { - /* all 3: cur, i01, i11 are different atoms (here i11==i12) */ - /* here nSymmRank[i01]==nSymmRank[i11] due to the parallel traversal */ - if ( nSymmRank[cur] == nSymmRank[i01] ) { - nNumEqStereogenic ++; /* all 3 are equ */ - } - } - } else - if ( ATOM_PARITY_WELL_DEF(parity) && ATOM_PARITY_WELL_DEF(parity12) ) { - /* apparently different well-defined parities */ - if ( !bCurParityInv1 ) { - nNumInv ++; - /* return 0; */ - } - } else { -#if ( PROPAGATE_ILL_DEF_STEREO == 1 ) - /* at least one parity is ill-defined. Use parity1 and parity2 to temporarily save bitmaps */ - parity1 = (parity ==vABParityUnknown /*AB_PARITY_UNKN*/)? NOT_WELL_DEF_UNKN : - (parity ==AB_PARITY_UNDF)? NOT_WELL_DEF_UNDF : 0; - parity2 = (parity12==vABParityUnknown /*AB_PARITY_UNKN*/)? NOT_WELL_DEF_UNKN : - (parity12==AB_PARITY_UNDF)? NOT_WELL_DEF_UNDF : 0; - if ( parity1 | parity2 ) { - not_well_def_parities |= ( parity1 | parity2 ); - k ++; - } else { - return -1; /* program error */ /* */ - } -#else - return 0; -#endif - } - } else - if ( i11 == i01 && i12 == i01 ) { - /********************************************************************/ - /* i11 == i12 are same atom as i01, nNeighMode == NEIGH_MODE_RING */ - if ( !s1 || !s2 ) { - return -1; - } - /* the parity of the new neighbors permutation must be same as the old one */ - /* this must work for well-defined and ill-defined parities. */ - /* actual parity (that includes the geometry) is not important here. */ - /* old permutation */ - parity = GetPermutationParity( at+i01, MAX_ATOMS+1, nCanonRank ); - /* new parmutation */ - parity1 = GetPermutationParity( at+i01, MAX_ATOMS+1, nVisited1 ); - parity2 = GetPermutationParity( at+i01, MAX_ATOMS+1, nVisited2 ); - if ( parity != parity1 || parity != parity2 ) { - return 0; - } - k ++; - } else { - /* nNeighMode == NEIGH_MODE_RING and only one out of the two (i11 == i01) (i12 == i01) is true */ - return -1; - } - /* nNumComparedCenters += (k > 0); */ - } - if ( bCurRotated || nNumDiff || nNumInv ) { - return 0; - } - - /* !!!! Add here bParitiesInverted == 1 case !!!! */ - /******************************************************/ - /* Check other (non-starting) stereo bonds/cumulenes */ - /******************************************************/ - for ( i = 0; i < pCS->nLenLinearCTStereoDble; i ++, nNumComparedBonds += (k > 0) ) { - r1 = pCS->LinearCTStereoDble[i].at_num1; - r2 = pCS->LinearCTStereoDble[i].at_num2; - i01 = nAtomNumberCanon[r1-1]; /* ord. number of the atom that originally has canon rank r1 */ - i02 = nAtomNumberCanon[r2-1]; /* ord. number of the atom that originally has canon rank r2 */ - - i11 = nAtomNumberCanon1[r1-1]; /* ord. number of the atom that got canon rank r1 during the parallel traversal */ - i12 = nAtomNumberCanon1[r2-1]; /* ord. number of the atom that got canon rank r2 during the parallel traversal */ - - i21 = nAtomNumberCanon2[r1-1]; - i22 = nAtomNumberCanon2[r2-1]; - - - s1 = (i11 < num_atoms && i12 < num_atoms); - s2 = (i21 < num_atoms && i22 < num_atoms); - - k = 0; - - /* check whether the two stereo bonds/allenes (they can be one and the same) have been traversed */ - if ( !s1 && !s2 ) { - continue; /* Both stereo bonds/cumulenes have not been traversed; check the next pair. */ - } - - if ( nCheckingMode == CHECKING_STEREOBOND ) { - switch ( (i11 == cur && i12 == prev_sb_neigh || i12 == cur && i11 == prev_sb_neigh) + - (i21 == cur && i22 == prev_sb_neigh || i22 == cur && i21 == prev_sb_neigh) ) { - case 2: - continue; /* do not recheck the starting bond/cumulene */ - case 1: - return -1; /* possibly program error */ /* */ - /* case 0: */ - /* break; */ /* the stereo centers are not the sarting stereo center */ - } - if ( (i01 == cur && i02 == prev_sb_neigh) || (i02 == cur && i01 == prev_sb_neigh) ) { - return -1; /* program error: in this case at least one of the i1x, i2x must be == cur */ /* */ - } - } - - if ( nNeighMode == NEIGH_MODE_RING ) { - if ( (i11 != i21 || i12 != i22) && (i11 != i22 || i12 != i21) ) { - return -1; /* failed: the two bonds/cumulenes have not been traversed synchronously */ - } - if ( 0 > GetStereoNeighborPos( at, i11, i12 ) ) { - return 0; /* another bond is not stereo (the stereo might have been removed) 9-11-2002 */ - } - - } - if ( nNeighMode == NEIGH_MODE_CHAIN ) { - if ( s1+s2 != 1 ) { - return -1; /* program error: only one out of s1 and s2 must be 1, another must be 0. */ - } - if ( s1 && 0 > GetStereoNeighborPos( at, i11, i12 ) || - s2 && 0 > GetStereoNeighborPos( at, i21, i22 ) ) { - return 0; /* another bond is not stereo (the stereo might have been removed) 9-11-2002 */ - } - } - - parity = pCS->LinearCTStereoDble[i].parity; - /* bMustBeIdentical = ATOM_PARITY_ILL_DEF(parity); */ - /* nNumEqStereogenic = 0; */ - - if ( nNeighMode == NEIGH_MODE_RING && (i11 != i01 || i12 != i02) && (i11 != i02 || i12 != i01) || - nNeighMode == NEIGH_MODE_CHAIN /* in NEIGH_MODE_CHAIN case here we always have 2 different atoms */ - ) { - /*******************************************/ - /* case of two transposed bonds/cumulenes */ - parity1 = s1? GetStereoBondParity( at, i11, i12, nVisited1 ) : PARITY_IMPOSSIBLE; - parity2 = s2? GetStereoBondParity( at, i21, i22, nVisited2 ) : PARITY_IMPOSSIBLE; - if ( !ATOM_PARITY_KNOWN(parity1) && !ATOM_PARITY_KNOWN(parity2) ) { - return -1; /* should not happen: must have been detected at the time of traversal */ - } - if ( s1 && s2 && ((i11 != i21 || i12 != i22) && (i11 != i22 || i12 != i21) || parity1 != parity2 ) ) { - return -1; /* program error: must be the same bond/cumulene */ - } - parity12 = s1? parity1 : parity2; - if ( ATOM_PARITY_WELL_DEF(parity) && parity == parity12 ) { - /* symmetrical neighbors have well-defined equal parities */ - k ++; - if ( nCheckingMode == CHECKING_STEREOBOND && nNeighMode == NEIGH_MODE_RING ) { - /* all 3 bonds: cur-prev_sb_neigh, i01-i02, i11-i12 are different */ - /* (here == compared as unordered pairs) */ - if ( nSymmRank[cur] == nSymmRank[i01] && nSymmRank[prev_sb_neigh] == nSymmRank[i02] || - nSymmRank[cur] == nSymmRank[i02] && nSymmRank[prev_sb_neigh] == nSymmRank[i01] ) { - nNumEqStereogenic ++; - } - } - } else - if ( ATOM_PARITY_WELL_DEF(parity) && ATOM_PARITY_WELL_DEF(parity12) ) { - /* apparently different well-defined parities */ - return 0; - } else { - /* at least one parity is ill-defined. Use parity1 and parity2 to temporarily save bitmaps */ -#if ( PROPAGATE_ILL_DEF_STEREO == 1 ) - parity1 = (parity ==vABParityUnknown /*AB_PARITY_UNKN*/)? NOT_WELL_DEF_UNKN : - (parity ==AB_PARITY_UNDF)? NOT_WELL_DEF_UNDF : 0; - parity2 = (parity12==vABParityUnknown /*AB_PARITY_UNKN*/)? NOT_WELL_DEF_UNKN : - (parity12==AB_PARITY_UNDF)? NOT_WELL_DEF_UNDF : 0; - if ( parity1 | parity2 ) { - not_well_def_parities |= ( parity1 | parity2 ); - k ++; - } else { - return -1; /* program error */ - } -#else - return 0; -#endif - } - } else { - /*****************************************************************************************/ - /* i11-i12 and i21-i22 are same as i01-i02 bond/cumulene, nNeighMode == NEIGH_MODE_RING */ - AT_NUMB n1, n2; - int j; - if ( !s1 || !s2 ) { - return -1; - } - /* find neighbors along the stereo bond/cumulene */ - for ( j = 0, n1 = MAX_ATOMS+1; j < MAX_NUM_STEREO_BOND_NEIGH && at[i01].stereo_bond_neighbor[j]; j ++ ) { - if ( (int)at[i01].stereo_bond_neighbor[j] == i02+1 ) { - n1 = at[i01].neighbor[ (int)at[i01].stereo_bond_ord[j] ]; - break; - } - } - for ( j = 0, n2 = MAX_ATOMS+1; j < MAX_NUM_STEREO_BOND_NEIGH && at[i02].stereo_bond_neighbor[j]; j ++ ) { - if ( (int)at[i02].stereo_bond_neighbor[j] == i01+1 ) { - n2 = at[i02].neighbor[ (int)at[i02].stereo_bond_ord[j] ]; - break; - } - } - if ( n1 > MAX_ATOMS || n2 > MAX_ATOMS ) { - return CT_REMOVE_STEREO_ERR; - } - /* the parity of the new neighbors permutation must be same as the old one */ - /* this must work for well-defined and ill-defined parities. */ - /* actual parity (that includes the geometry) is not important here. */ - /* old permutation */ - parity = GetPermutationParity( at+i01, n1, nCanonRank) + GetPermutationParity( at+i02, n2, nCanonRank); - /* new parmutation */ - parity1 = GetPermutationParity( at+i01, n1, nVisited1 ) + GetPermutationParity( at+i02, n2, nVisited1 ); - parity2 = GetPermutationParity( at+i01, n1, nVisited2 ) + GetPermutationParity( at+i02, n2, nVisited2 ); - if ( parity %2 != parity1 % 2 || parity1 % 2 != parity2 % 2 ) { - return 0; - } - k ++; - } - - /* nNumComparedBonds += ( k > 0 ); */ - } - - if ( nNumEqStereogenic > 0 ) { - /* case similar to trimethylcyclopropane: 3 constitutionally equivalent stereogenic elements */ - /* the transposition does not change the parities */ -#if ( bRELEASE_VERSION == 0 ) - pCS->bExtract |= EXTR_2EQL2CENTER_TO_REMOVE_PARITY; -#endif - return 0; - } -/* ========================================================================================= - Note - ==== - At this point the comparison is complete and no difference sufficient to establish - absence of stereo parity has been found. - However, non-zero not_well_def_parities means that an ill-defined parity was - compared to an ill-defined or well-defined parity. This means that the parity - of the atom or bond being checked cannot be well-defined anymore. - ========================================================================================*/ - - - not_well_def_parities |= COMP_STEREO_SUCCESS; - - return not_well_def_parities; - - /* Add 1 to indicate success. The stereogenic elements might have been */ - /* removed while checking existence of the previous atom/bond stereo */ - /* return (nNumComparedCenters + nNumComparedBonds + 1); */ -} -/********************************************************************************/ -/* Remove stereo marks from the bonds that are calculated to be non-stereo */ -/* Such bonds must have 2 constitutionally equivalent attachments */ -/* (can find two canonical numberings that change only one stereo bond parity) */ -int RemoveCalculatedNonStereoBondParities( sp_ATOM *at, int num_atoms, int num_at_tg, - AT_RANK **pRankStack1, AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, - AT_RANK *nCanonRank, const AT_RANK *nSymmRank, - AT_RANK *nAtomNumberCanon, AT_RANK *nAtomNumberCanon1, AT_RANK *nAtomNumberCanon2, - NEIGH_LIST *nl, NEIGH_LIST *nl1, NEIGH_LIST *nl2, - AT_RANK *nVisited1, AT_RANK *nVisited2, - CANON_STAT *pCS, - int vABParityUnknown) -{ - int j, n, m, ret, ret1, ret2, ret_failed=0; - - int i1, n1, s2; /* n1 must be SIGNED integer */ - AT_RANK nAtomRank1, nAtomRank2, neigh[3], nAvoidCheckAtom[2], opposite_atom, nLength; - int nNeighMode = NEIGH_MODE_CHAIN; - int nNumEqRingNeigh = 0, bRingNeigh, bSymmNeigh, bParitiesInverted; - NEIGH_LIST *nl01, *nl02; - const AT_RANK *nSymmRank1, *nSymmRank2; - - ret = 0; - -second_pass: - - for ( i1 = 0; i1 < num_atoms && !RETURNED_ERROR(ret_failed); i1 ++ ) { - if ( at[i1].valence != 3 || !at[i1].stereo_bond_neighbor[0] ) { - continue; - } - for ( n1 = 0; n1 < MAX_NUM_STEREO_BONDS && !RETURNED_ERROR(ret_failed) && (s2=at[i1].stereo_bond_neighbor[n1]); n1++ ) { - if ( !PARITY_CALCULATE(at[i1].stereo_bond_parity[n1]) && PARITY_WELL_DEF(at[i1].stereo_bond_parity[n1]) ) { - continue; - } - opposite_atom = (AT_RANK)(s2-1); - s2 = at[i1].neighbor[(int)at[i1].stereo_bond_ord[n1]]; /* different from opposite_atom in case of a cumulene */ - for ( j = 1, n = 0; j <= (int)at[i1].valence; j ++ ) { - if ( nl[i1][j] != s2 ) { - neigh[n++] = nl[i1][j]; /* sorting guarantees that canon. rank of neigh[0] is greater or equal */ - } - } - if ( n != 2 ) { - ret = CT_STEREOBOND_ERROR; /* */ - goto exit_function; - } - if ( nSymmRank[(int)neigh[0]] != nSymmRank[(int)neigh[1]] ) { - continue; /* may happen if another half-bond has not a defined parity */ - } - - bRingNeigh = (at[(int)neigh[0]].nRingSystem == at[(int)neigh[1]].nRingSystem); - switch ( nNeighMode ) { - case NEIGH_MODE_CHAIN: - if ( bRingNeigh ) { - nNumEqRingNeigh ++; - continue; - } - nl01 = nl; - nl02 = nl; - nSymmRank1 = nSymmRank; - nSymmRank2 = nSymmRank; - break; - - case NEIGH_MODE_RING: - if ( !bRingNeigh ) - continue; - /* break a tie between the two contitutionally equivalent neighbors, */ - /* refine the two partitions, sort neighbors lists nl1, nl2 */ - bSymmNeigh = BreakNeighborsTie( at, num_atoms, num_at_tg, opposite_atom, i1, - neigh, 0, 1, 0, - pRankStack1, pRankStack2, nTempRank, NeighList, nSymmRank, nCanonRank, - nl1, nl2, &pCS->lNumNeighListIter ); - if ( bSymmNeigh <= 0 ) { - if ( ret_failed > bSymmNeigh ) - ret_failed = bSymmNeigh; - continue; - } - nl01 = nl1; - nl02 = nl2; - nSymmRank1 = pRankStack1[0]; - nSymmRank2 = pRankStack2[0]; - break; - default: - return CT_STEREOCOUNT_ERR; /* */ - } - - /* initialize arrays */ - memset( nVisited1, 0, sizeof(nVisited1[0])*num_atoms ); - memset( nVisited2, 0, sizeof(nVisited2[0])*num_atoms ); - memset( nAtomNumberCanon1, 0, sizeof(nAtomNumberCanon1[0])*num_atoms ); - memset( nAtomNumberCanon2, 0, sizeof(nAtomNumberCanon2[0])*num_atoms ); - nLength = 1; - nVisited1[i1] = i1+1; /* start atoms are the same */ - nVisited2[i1] = i1+1; - nAtomNumberCanon1[i1] = nLength; - nAtomNumberCanon2[i1] = nLength; - nAvoidCheckAtom[0] = i1; - nAvoidCheckAtom[1] = opposite_atom; - bParitiesInverted = (nNeighMode == NEIGH_MODE_RING && - IS_ALLENE_CHAIN(at[i1].stereo_bond_parity[n1]) && - PARITY_CALCULATE(at[i1].stereo_bond_parity[n1]) && - at[i1].nRingSystem == at[opposite_atom].nRingSystem && - at[opposite_atom].valence==MAX_NUM_STEREO_BONDS)? -1 : 0; - ret1 = ret2 = 0; - if ( 0 < (ret1=CreateCheckSymmPaths( at, (AT_RANK)i1, neigh[0], (AT_RANK)i1, neigh[1], nAvoidCheckAtom, - nVisited1, nVisited2, nAtomNumberCanon1, nAtomNumberCanon2, - nl01, nl02, nSymmRank1, nSymmRank2, nCanonRank, &nLength, &bParitiesInverted, 0 ) ) && - 0 < (ret2=CalculatedPathsParitiesAreIdentical( at, num_atoms, nSymmRank, - nCanonRank, nAtomNumberCanon, nAtomNumberCanon1, nAtomNumberCanon2, - nVisited1, nVisited2, opposite_atom, (AT_RANK)i1, - neigh[0], neigh[1], nNeighMode, bParitiesInverted, 0, - pCS, vABParityUnknown ) ) ) { - if ( ret2 & ( NOT_WELL_DEF_UNKN | NOT_WELL_DEF_UNDF ) ) { - /* possibly change the parity to unknown or undefined */ - int new_parity = (ret2 & NOT_WELL_DEF_UNKN)? vABParityUnknown /*AB_PARITY_UNKN*/: AB_PARITY_UNDF; - if ( PARITY_ILL_DEF(at[i1].stereo_bond_parity[n1]) && PARITY_VAL(at[i1].stereo_bond_parity[n1]) > new_parity || - PARITY_CALCULATE(at[i1].stereo_bond_parity[n1]) ) { - /* set new unknown or undefined parity */ - SetOneStereoBondIllDefParity( at, i1, /* atom number*/ n1 /* stereo bond ord. number*/, new_parity ); - /* change in pCS */ - nAtomRank1 = inchi_max( nCanonRank[i1], nCanonRank[opposite_atom]); - nAtomRank2 = inchi_min( nCanonRank[i1], nCanonRank[opposite_atom]); - for ( n = 0, m = pCS->nLenLinearCTStereoDble-1; n <= m; n ++ ) { - if ( pCS->LinearCTStereoDble[n].at_num1 == nAtomRank1 && - pCS->LinearCTStereoDble[n].at_num2 == nAtomRank2 ) { - pCS->LinearCTStereoDble[n].parity = new_parity; -#if ( bRELEASE_VERSION == 0 ) - pCS->bExtract |= EXTR_CALC_USED_TO_REMOVE_PARITY; -#endif - m = -1; - break; - } - } - if ( m >= 0 ) { - ret = CT_STEREOCOUNT_ERR; /* */ - goto exit_function; - } - ret ++; - } - } else { - /* remove the parity */ - if ( !RemoveOneStereoBond( at, i1, /* atom number*/ n1 /* stereo bond ord. number*/ ) ) { - ret = CT_STEREOBOND_ERROR; /* */ - goto exit_function; - } - n1 --; /* cycle counter may temporarily become negative */ - /* Remove from the pCS */ - nAtomRank1 = inchi_max( nCanonRank[i1], nCanonRank[opposite_atom]); - nAtomRank2 = inchi_min( nCanonRank[i1], nCanonRank[opposite_atom]); - for ( n = 0, m = pCS->nLenLinearCTStereoDble-1; n <= m; n ++ ) { - if ( pCS->LinearCTStereoDble[n].at_num1 == nAtomRank1 && - pCS->LinearCTStereoDble[n].at_num2 == nAtomRank2 ) { - if ( n < m ) { /* remove pCS->LinearCTStereoDble[n] */ - memmove( pCS->LinearCTStereoDble + n, - pCS->LinearCTStereoDble + n + 1, - (m-n)*sizeof(pCS->LinearCTStereoDble[0]) ); - } - pCS->nLenLinearCTStereoDble --; -#if ( bRELEASE_VERSION == 0 ) - pCS->bExtract |= EXTR_CALC_USED_TO_REMOVE_PARITY; -#endif - m = -1; - break; - } - } - if ( m >= 0 ) { - ret = CT_STEREOCOUNT_ERR; /* */ - goto exit_function; - } - ret ++; - } - } else { - if ( !ret_failed ) { - ret_failed = (ret1<0)? ret1 : (ret2<0)? ret2 : 0; - } - if ( !RETURNED_ERROR(ret_failed) ) { - if ( RETURNED_ERROR( ret1 ) ) - ret_failed = ret1; - else - if ( RETURNED_ERROR( ret2 ) ) - ret_failed = ret2; - } - } - } - } - if ( nNeighMode == NEIGH_MODE_CHAIN && nNumEqRingNeigh && !RETURNED_ERROR(ret_failed) ) { - nNeighMode = NEIGH_MODE_RING; - goto second_pass; - } - -exit_function: - - return RETURNED_ERROR(ret_failed)? ret_failed : ret_failed? -(ret_failed+1) : ret; -} -/****************************************************************************/ -/* Remove stereo marks from the atoms that are calculated to be non-stereo */ -/* (can find two numberings that change only one stereo center parity) */ -int RemoveCalculatedNonStereoCenterParities( sp_ATOM *at, int num_atoms, int num_at_tg, - AT_RANK **pRankStack1, AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, - AT_RANK *nCanonRank, const AT_RANK *nSymmRank, - AT_RANK *nAtomNumberCanon, AT_RANK *nAtomNumberCanon1, AT_RANK *nAtomNumberCanon2, - NEIGH_LIST *nl, NEIGH_LIST *nl1, NEIGH_LIST *nl2, - AT_RANK *nVisited1, AT_RANK *nVisited2, - CANON_STAT *pCS, - int vABParityUnknown) -{ - int j, n, m, ret; - - int i, k, ret1, ret2, ret_failed=0, mode, max_mode; - AT_RANK nAtomRank1, neigh[MAX_NUM_STEREO_ATOM_NEIGH], nAvoidCheckAtom[2], nLength; - int nNeighMode = NEIGH_MODE_CHAIN; - int nNumEqRingNeigh = 0, bRingNeigh, bSymmNeigh, bParitiesInverted; - NEIGH_LIST *nl01, *nl02; - const AT_RANK *nSymmRank1, *nSymmRank2; - - ret = 0; - -second_pass: - for ( i = 0; i < num_atoms && !RETURNED_ERROR(ret_failed); i ++ ) { - if ( !at[i].parity || at[i].stereo_bond_neighbor[0] ) { - continue; - } - if ( at[i].valence > MAX_NUM_STEREO_ATOM_NEIGH ) { - continue; /* error: stereo center cannot have more than 4 neighbors */ /* */ - } - /* at[i1] is a stereo center */ - if ( !PARITY_CALCULATE(at[i].stereo_atom_parity) && !PARITY_ILL_DEF(at[i].stereo_atom_parity) ) { - continue; - } - /* neighbors sorted according to symm. ranks (primary key) and canon. ranks (secondary key), in descending order */ - /* sorting guarantees that for two constit. equ. neighbors canon. ranks of the first is greater */ - /* !!! previously (but not anymore) the canon. rank of neigh[0] was greater than the others !!! */ - for ( j = 0; j < at[i].valence; j ++ ) { - neigh[j] = nl[i][j+1]; /* sorting does NOT guarantee that canon. rank of neigh[0] is greater than others */ - } - /* - * mode = 0 => Standard approach: switch 2 neighbors - * 1 => Check for C2v reflection leading to parity inversion - * 2 => Check for C2 rotation preserving parities - * 3 => Check for S4 rotation/reflection leading to parity inversion - */ -#if ( CHECK_C2v_S4_SYMM == 1 ) - if ( nNeighMode = NEIGH_MODE_RING && at[i].valence == 4 && - nSymmRank[(int)neigh[0]] == nSymmRank[(int)neigh[1]] && - nSymmRank[(int)neigh[2]] == nSymmRank[(int)neigh[3]] && - !at[i].bCutVertex - ) { - if ( nSymmRank[(int)neigh[1]] == nSymmRank[(int)neigh[2]] ) { - max_mode = MAP_MODE_S4; - } else { - max_mode = inchi_max(MAP_MODE_C2v, MAP_MODE_C2); - } - } else { - max_mode = MAP_MODE_STD; - } -#else - max_mode = MAP_MODE_STD; -#endif - for ( j = 0; j < at[i].valence && at[i].parity && !RETURNED_ERROR(ret_failed); j ++ ) { - for ( k = j+1; k < at[i].valence && at[i].parity && !RETURNED_ERROR(ret_failed); k ++ ) { - for ( mode = 0; mode <= max_mode && at[i].parity && !RETURNED_ERROR(ret_failed); mode ++ ) { - if ( nSymmRank[(int)neigh[j]] != nSymmRank[(int)neigh[k]] ) { - continue; /* the two neighbors are not constitutionally identical */ - } - bRingNeigh = (at[(int)neigh[j]].nRingSystem == at[(int)neigh[k]].nRingSystem); - switch ( nNeighMode ) { - case NEIGH_MODE_CHAIN: - if ( bRingNeigh ) { - nNumEqRingNeigh ++; - continue; - } - nl01 = nl; - nl02 = nl; - nSymmRank1 = nSymmRank; - nSymmRank2 = nSymmRank; - break; - case NEIGH_MODE_RING: - if ( !bRingNeigh ) - continue; - /* break a tie between the two contitutionally equivalent neighbors, */ - /* refine the two partitions, sort neighbors lists nl1, nl2 */ - bSymmNeigh = BreakNeighborsTie( at, num_atoms, num_at_tg, MAX_ATOMS+1, i, - neigh, j, k, mode, - pRankStack1, pRankStack2, nTempRank, NeighList, nSymmRank, nCanonRank, - nl1, nl2, &pCS->lNumNeighListIter ); - if ( bSymmNeigh <= 0 ) { - if ( ret_failed > bSymmNeigh ) - ret_failed = bSymmNeigh; - continue; - } - nl01 = nl1; - nl02 = nl2; - nSymmRank1 = pRankStack1[0]; - nSymmRank2 = pRankStack2[0]; - break; - default: - return CT_STEREOCOUNT_ERR; /* */ - } - - /* initialize arrays */ - memset( nVisited1, 0, sizeof(nVisited1[0])*num_atoms ); - memset( nVisited2, 0, sizeof(nVisited2[0])*num_atoms ); - memset( nAtomNumberCanon1, 0, sizeof(nAtomNumberCanon1[0])*num_atoms ); - memset( nAtomNumberCanon2, 0, sizeof(nAtomNumberCanon2[0])*num_atoms ); - nLength = 1; - nVisited1[i] = i+1; /* start atom is same */ - nVisited2[i] = i+1; - nAtomNumberCanon1[i] = nLength; - nAtomNumberCanon2[i] = nLength; - nAvoidCheckAtom[0] = i; - nAvoidCheckAtom[1] = MAX_ATOMS+1; - - bParitiesInverted = (mode==MAP_MODE_C2v || mode==MAP_MODE_S4)? -1 : 0; - /* - if (nNeighMode==NEIGH_MODE_RING && at[i].valence==MAX_NUM_STEREO_ATOM_NEIGH) { - AT_RANK other_neigh[2]; - int n; - for ( m = n = 0; m < MAX_NUM_STEREO_ATOM_NEIGH; m ++ ) { - if ( at[i].neighbor[m] != neigh[j] && at[i].neighbor[m] != neigh[k] ) - other_neigh[n++] = at[i].neighbor[m]; - } - if ( nSymmRank[(int)other_neigh[0]] == nSymmRank[(int)other_neigh[1]] ) - bParitiesInverted = -1; - } - */ - /* allow matching inverted centers only in case all equivalent neighbors in same ring system */ - - ret2 = 0; /* initilize. 1/8/2002 */ - - if ( 0 < (ret1 = CreateCheckSymmPaths( at, (AT_RANK)i, neigh[j], (AT_RANK)i, neigh[k], - nAvoidCheckAtom, - nVisited1, nVisited2, nAtomNumberCanon1, nAtomNumberCanon2, - nl01, nl02, nSymmRank1, nSymmRank2, nCanonRank, &nLength, - &bParitiesInverted, mode ) ) && - 0 < (ret2 = CalculatedPathsParitiesAreIdentical( at, num_atoms, nSymmRank, - nCanonRank, nAtomNumberCanon, nAtomNumberCanon1, nAtomNumberCanon2, - nVisited1, nVisited2, (AT_RANK)MAX_ATOMS, (AT_RANK)i, - neigh[j], neigh[k], nNeighMode, - bParitiesInverted, mode, pCS, - vABParityUnknown) ) ) { - if ( ret2 & ( NOT_WELL_DEF_UNKN | NOT_WELL_DEF_UNDF ) ) { - /* possibly change the parity to unknown or undefined */ - int new_parity = (ret2 & NOT_WELL_DEF_UNKN)? vABParityUnknown /*AB_PARITY_UNKN*/: AB_PARITY_UNDF; - if ( PARITY_ILL_DEF(at[i].stereo_atom_parity) && - PARITY_VAL(at[i].stereo_atom_parity) > new_parity || - PARITY_CALCULATE(at[i].stereo_atom_parity) ) { - /* set new unknown or undefined parity */ - at[i].stereo_atom_parity = (at[i].stereo_atom_parity ^ PARITY_VAL(at[i].stereo_atom_parity)) | PARITY_VAL(new_parity); - at[i].parity = PARITY_VAL(new_parity); - /* Remove from pCS */ - nAtomRank1 = nCanonRank[i]; - for ( n = 0, m = pCS->nLenLinearCTStereoCarb-1; n <= m; n ++ ) { - if ( pCS->LinearCTStereoCarb[n].at_num == nAtomRank1 ) { - pCS->LinearCTStereoCarb[n].parity = PARITY_VAL(new_parity); - #if ( bRELEASE_VERSION == 0 ) - pCS->bExtract |= EXTR_CALC_USED_TO_REMOVE_PARITY; - #endif - m = -1; - break; - } - } - if ( m >= 0 ) { - ret = CT_STEREOCOUNT_ERR; /* */ - goto exit_function; - } - ret ++; /* number of removed or set unknown/undefined parities */ - } - } else { - RemoveOneStereoCenter( at, i /* atom number*/ ); - /* Remove from pCS */ - nAtomRank1 = nCanonRank[i]; - for ( n = 0, m = pCS->nLenLinearCTStereoCarb-1; n <= m; n ++ ) { - if ( pCS->LinearCTStereoCarb[n].at_num == nAtomRank1 ) { - if ( n < m ) { /* remove pCS->LinearCTStereoDble[n] */ - memmove( pCS->LinearCTStereoCarb + n, - pCS->LinearCTStereoCarb + n + 1, - (m-n)*sizeof(pCS->LinearCTStereoCarb[0]) ); - } - pCS->nLenLinearCTStereoCarb --; - #if ( bRELEASE_VERSION == 0 ) - pCS->bExtract |= EXTR_CALC_USED_TO_REMOVE_PARITY; - #endif - m = -1; - break; - } - } - if ( m >= 0 ) { - ret = CT_STEREOCOUNT_ERR; /* */ - goto exit_function; - } - ret ++; /* number of removed or set unknown/undefined parities */ - } - } else { - if ( !ret_failed ) { - if ( ret1 < 0 ) { - ret_failed = ret1; - } else - if ( ret2 < 0 ) { - ret_failed = ret2; - } - } - if ( !RETURNED_ERROR(ret_failed) ) { - if ( RETURNED_ERROR( ret1 ) ) - ret_failed = ret1; - else - if ( RETURNED_ERROR( ret2 ) ) - ret_failed = ret2; - } - } - } - } - } - } - if ( nNeighMode == NEIGH_MODE_CHAIN && nNumEqRingNeigh && !RETURNED_ERROR(ret_failed) ) { - nNeighMode = NEIGH_MODE_RING; - goto second_pass; - } - -exit_function: - - return RETURNED_ERROR(ret_failed)? ret_failed : ret_failed? -(ret+1) : ret; -} - -/**************************************************************************************/ -int RemoveCalculatedNonStereo( sp_ATOM *at, int num_atoms, int num_at_tg, - AT_RANK **pRankStack1, AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, - const AT_RANK *nSymmRank, AT_RANK *nCanonRank, - AT_RANK *nAtomNumberCanon, CANON_STAT *pCS, - int vABParityUnknown) -{ - NEIGH_LIST *nl = NULL, *nl1 = NULL, *nl2 = NULL; - AT_RANK *nVisited1 = NULL, *nVisited2 = NULL, *nAtomNumberCanon1 = NULL, *nAtomNumberCanon2 = NULL; - int nNumRemoved = 0, nTotRemoved = 0, ret = 0, ret1 = 0, ret2 = 0; - - if ( !AllocateForNonStereoRemoval( at, num_atoms, nSymmRank, nCanonRank, - &nAtomNumberCanon1, &nAtomNumberCanon2, - &nl, &nl1, &nl2, &nVisited1, &nVisited2 ) ) { - return CT_OUT_OF_RAM; /* */ - } - - do { - nNumRemoved = 0; - /* bonds */ - ret = RemoveCalculatedNonStereoBondParities( at, num_atoms, num_at_tg, - pRankStack1, pRankStack2, nTempRank, NeighList, - nCanonRank, nSymmRank, - nAtomNumberCanon, nAtomNumberCanon1, nAtomNumberCanon2, - nl, nl1, nl2, nVisited1, nVisited2, pCS, - vABParityUnknown); - if ( RETURNED_ERROR( ret ) ) { - goto exit_function; - } - if ( ret < 0 ) { - if ( ret < ret1 ) { /* */ - ret1 = ret; - } - ret = - ( ret + 1 ); /* number of removed */ - } - nNumRemoved += ret; - - /* centers */ - ret = RemoveCalculatedNonStereoCenterParities( at, num_atoms, num_at_tg, - pRankStack1, pRankStack2, nTempRank, NeighList, - nCanonRank, nSymmRank, - nAtomNumberCanon, nAtomNumberCanon1, nAtomNumberCanon2, - nl, nl1, nl2, nVisited1, nVisited2, pCS, - vABParityUnknown); - if ( RETURNED_ERROR( ret ) ) { - goto exit_function; - } - if ( ret < 0 ) { - if ( ret < ret2 ) { /* */ - ret2 = ret; - } - ret = - ( ret + 1 ); /* number of removed */ - } - nNumRemoved += ret; - - nTotRemoved += nNumRemoved; - - } while ( nNumRemoved ); - - if ( !RETURNED_ERROR( ret1 ) && !RETURNED_ERROR( ret2 ) ) { - ret = inchi_min( ret1, ret2 ); - ret = (ret >= 0)? nTotRemoved : -(1+nTotRemoved); - } - -exit_function: - - DeAllocateForNonStereoRemoval( &nAtomNumberCanon1, &nAtomNumberCanon2, &nl, &nl1, &nl2, &nVisited1, &nVisited2 ); - - return ret; -} -#endif /* } REMOVE_CALC_NONSTEREO */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include + +#include "mode.h" +#include "ichicomn.h" + +#define MAP_MODE_STD 0 /* Standard approach: switch 2 neighbors */ +#define MAP_MODE_C2v 1 /* Check for C2v reflection leading to parity inversion */ +#define MAP_MODE_C2 2 /* Check for C2 rotation preserving parities */ +#define MAP_MODE_S4 3 /* Check for S4 rotation/reflection leading to parity inversion */ +/* important: MAP_MODE_STD < (MAP_MODE_C2v, MAP_MODE_C2) < MAP_MODE_S4 */ + +/* local prototypes */ +void DeAllocateForNonStereoRemoval( AT_RANK **nAtomNumberCanon1, + AT_RANK **nAtomNumberCanon2, + NEIGH_LIST **nl, + NEIGH_LIST **nl1, NEIGH_LIST **nl2, + AT_RANK **nVisited1, AT_RANK **nVisited2 ); + +int AllocateForNonStereoRemoval( sp_ATOM *at, int num_atoms, + const AT_RANK *nSymmRank, AT_RANK *nCanonRank, + AT_RANK **nAtomNumberCanon1, + AT_RANK **nAtomNumberCanon2, + NEIGH_LIST **nl, NEIGH_LIST **nl1, NEIGH_LIST **nl2, + AT_RANK **nVisited1, AT_RANK **nVisited2 ); + +AT_RANK GetMinNewRank( AT_RANK *nAtomRank, AT_RANK *nAtomNumb, AT_RANK nRank1 ); + +int BreakNeighborsTie( CANON_GLOBALS *pCG, + sp_ATOM *at, int num_atoms, int num_at_tg, int ib, int ia, + AT_RANK *neigh_num, int in1, int in2, int mode, + AT_RANK **pRankStack1, AT_RANK **pRankStack2, + AT_RANK *nTempRank, NEIGH_LIST *NeighList, + const AT_RANK *nSymmRank, AT_RANK *nCanonRank, + NEIGH_LIST *nl1, NEIGH_LIST *nl2, long *lNumIter ); + +int CheckNextSymmNeighborsAndBonds( sp_ATOM *at, AT_RANK cur1, AT_RANK cur2, + AT_RANK n1, AT_RANK n2, + AT_RANK *nAvoidCheckAtom, AT_RANK *nVisited1, + AT_RANK *nVisited2, + AT_RANK *nVisitOrd1, AT_RANK *nVisitOrd2, + const AT_RANK *nRank1, const AT_RANK *nRank2 ); + +int CreateCheckSymmPaths( sp_ATOM *at, AT_RANK prev1, AT_RANK cur1, + AT_RANK prev2, AT_RANK cur2, AT_RANK *nAvoidCheckAtom, + AT_RANK *nVisited1, AT_RANK *nVisited2, + AT_RANK *nVisitOrd1, AT_RANK *nVisitOrd2, + NEIGH_LIST *nl1, NEIGH_LIST *nl2, + const AT_RANK *nRank1, const AT_RANK *nRank2, + AT_RANK *nCanonRank, AT_RANK *nLength, + int *bParitiesInverted, int mode ); + +int CalculatedPathsParitiesAreIdentical( CANON_GLOBALS *pCG, + sp_ATOM *at, int num_atoms, const AT_RANK *nSymmRank, + AT_RANK *nCanonRank, AT_RANK *nAtomNumberCanon, AT_RANK *nAtomNumberCanon1, AT_RANK *nAtomNumberCanon2, + AT_RANK *nVisited1, AT_RANK *nVisited2, + AT_RANK prev_sb_neigh, AT_RANK cur, AT_RANK next1, AT_RANK next2, int nNeighMode, + int bParitiesInverted, int mode, CANON_STAT *pCS, + int vABParityUnknown); + +int RemoveCalculatedNonStereoBondParities( CANON_GLOBALS *pCG, + sp_ATOM *at, + int num_atoms, int num_at_tg, + AT_RANK **pRankStack1, + AT_RANK **pRankStack2, + AT_RANK *nTempRank, NEIGH_LIST *NeighList, + AT_RANK *nCanonRank, + const AT_RANK *nSymmRank, + AT_RANK *nAtomNumberCanon, + AT_RANK *nAtomNumberCanon1, AT_RANK *nAtomNumberCanon2, + NEIGH_LIST *nl, + NEIGH_LIST *nl1, + NEIGH_LIST *nl2, + AT_RANK *nVisited1, AT_RANK *nVisited2, + CANON_STAT *pCS, + int vABParityUnknown); + +int RemoveCalculatedNonStereoCenterParities( CANON_GLOBALS *pCG, + sp_ATOM *at, + int num_atoms, int num_at_tg, + AT_RANK **pRankStack1, + AT_RANK **pRankStack2, + AT_RANK *nTempRank, NEIGH_LIST *NeighList, + AT_RANK *nCanonRank, + const AT_RANK *nSymmRank, + AT_RANK *nAtomNumberCanon, + AT_RANK *nAtomNumberCanon1, AT_RANK *nAtomNumberCanon2, + NEIGH_LIST *nl, NEIGH_LIST *nl1, NEIGH_LIST *nl2, + AT_RANK *nVisited1, AT_RANK *nVisited2, + CANON_STAT *pCS, + int vABParityUnknown); + +int SortNeighLists3( int num_atoms, + AT_RANK *nRank, + NEIGH_LIST *NeighList, + AT_RANK *nAtomNumber ); + + + +/************************************************************************************** + * + * Convert sorted equivalence information (nSymmRank) to ranks (nRank) + * nSymmRank and nRank may point to the same array + * + */ +int SortedEquInfoToRanks( const AT_RANK* nSymmRank, AT_RANK* nRank, const AT_RANK* nAtomNumber, int num_atoms, int *bChanged ) +{ + /* v. 1.05 - changed declaration of nNumDiffRanks as suggested by Burt Leland + to avoid the problem arising on compilation with VS2015 + + AT_RANK rNew, rOld, nNumDiffRanks;*/ + AT_RANK rNew, rOld; + int nNumDiffRanks = 1; + + + int i, j, nNumChanges = 0; + for ( i = num_atoms-1, j = (int)nAtomNumber[i], + rOld = nSymmRank[j], rNew = nRank[j] = (AT_RANK)num_atoms, + nNumDiffRanks = 1; + i > 0; + i -- ) + { + j = (int)nAtomNumber[i-1]; + + if ( nSymmRank[j] != rOld ) + { + nNumDiffRanks ++; + rNew = (AT_RANK)i; + nNumChanges += (rOld != rNew+1); + rOld = nSymmRank[j]; + } + + nRank[j] = rNew; + } + if ( bChanged ) + { + *bChanged = (0 != nNumChanges); + } + return nNumDiffRanks; +} + + +/************************************************************************************** + * + * Convert sorted ranks (nRank) to sorted equivalence information (nSymmRank) + * nSymmRank and nRank may point to the same array + * + */ +int SortedRanksToEquInfo( AT_RANK* nSymmRank, const AT_RANK* nRank, const AT_RANK* nAtomNumber, int num_atoms ) +{ + /* v. 1.05 - changed declaration of nNumDiffRanks as suggested by Burt Leland + to avoid the problem arising on compilation with VS2015 + + AT_RANK rNew, rOld, nNumDiffRanks;*/ + AT_RANK rNew, rOld; + int nNumDiffRanks=1; + + int i, j; + for ( i = 1, j = (int)nAtomNumber[0], + rOld = nRank[j], rNew = nSymmRank[j] = 1, + nNumDiffRanks = 1; + i < num_atoms; + i ++ ) + { + j = (int)nAtomNumber[i]; + if ( nRank[j] != rOld ) + { + nNumDiffRanks ++; + rNew = (AT_RANK)(i+1); + rOld = nRank[j]; + } + nSymmRank[j] = rNew; + } + return nNumDiffRanks; +} + + +/**************************************************************************************/ +void switch_ptrs( AT_RANK **p1, AT_RANK **p2 ) +{ + AT_RANK *tmp = *p1; + *p1 = *p2; + *p2 = tmp; +} + +/**************************************************************************************/ +/* Set ranks from the products vector and previous ranks */ +/* nRank[] and nNewRank[] should refer to different arrays for now */ +/**************************************************************************************/ +int SetNewRanksFromNeighLists3( CANON_GLOBALS *pCG, + int num_atoms, + NEIGH_LIST *NeighList, + AT_RANK *nRank, + AT_RANK *nNewRank, + AT_RANK *nAtomNumber ) +{ + int i, j, nNumDiffRanks, nNumNewRanks; + AT_RANK r1, r2; + /* -- nAtomNumber[] is already properly set -- + for ( i = 0; i < num_atoms; i++ ) { + nAtomNumber[i] = (AT_RANK)i; + } + */ + /* set globals for qsort */ + pCG->m_pNeighList_RankForSort = NeighList; + pCG->m_pn_RankForSort = nRank; + nNumDiffRanks = 0; + nNumNewRanks = 0; + + memset(nNewRank, 0, num_atoms*sizeof(nNewRank[0])); + + /* sorting */ + for ( i = 0, r1 = 1; i < num_atoms; r1++ ) { + if ( r1 == (r2 = nRank[j=(int)nAtomNumber[i]]) ) { + nNewRank[j] = r2; + nNumDiffRanks ++; + i ++; + continue; + } + r1 = r2; + insertions_sort_AT_NUMBERS( pCG, nAtomNumber+i, (int)r2-i, CompNeighLists ); + /*insertions_sort( nAtomNumber+i, r2-i, sizeof( nAtomNumber[0] ), CompNeighLists );*/ + j = r2-1; + nNewRank[(int)nAtomNumber[j]] = r2; + nNumDiffRanks ++; + while( j > i ) { + if ( CompareNeighListLex( NeighList[(int)nAtomNumber[j-1]], + NeighList[(int)nAtomNumber[j]], nRank ) ) { + r2 = j; + nNumDiffRanks ++; + nNumNewRanks ++; + } + j --; + nNewRank[(int)nAtomNumber[j]] = r2; + } + i = r1; + } + return nNumNewRanks? -nNumDiffRanks : nNumDiffRanks; +} +/**************************************************************************************/ +/* Set ranks from the products vector and previous ranks */ +/* When comparing neigh lists ignore ranks > max_at_no */ +/* nRank[] and nNewRank[] should refer to different arrays for now */ +/**************************************************************************************/ +int SetNewRanksFromNeighLists4( CANON_GLOBALS *pCG, + int num_atoms, NEIGH_LIST *NeighList, AT_RANK *nRank, + AT_RANK *nNewRank, AT_RANK *nAtomNumber, + AT_RANK nMaxAtRank ) +{ + int i, j, nNumDiffRanks, nNumNewRanks; + AT_RANK r1, r2; + /* -- nAtomNumber[] is already properly set -- + for ( i = 0; i < num_atoms; i++ ) { + nAtomNumber[i] = (AT_RANK)i; + } + */ + + /* set globals for CompNeighListsUpToMaxRank */ + pCG->m_pNeighList_RankForSort = NeighList; + pCG->m_pn_RankForSort = nRank; + nNumDiffRanks = 0; + nNumNewRanks = 0; + pCG->m_nMaxAtNeighRankForSort = nMaxAtRank; + + memset(nNewRank, 0, num_atoms*sizeof(nNewRank[0])); + + /* sorting */ + for ( i = 0, r1 = 1; i < num_atoms; r1++ ) { + if ( r1 == (r2 = nRank[j=(int)nAtomNumber[i]]) ) { + /* non-tied rank: singleton */ + nNewRank[j] = r2; + nNumDiffRanks ++; + i ++; + continue; + } + /* tied rank r2 + r2-i atoms have rank r2 + next atom after them is in position r2 + */ + r1 = r2; + + insertions_sort_AT_NUMBERS( pCG, nAtomNumber+i, + (int)r2-i, CompNeighListsUpToMaxRank ); + /*insertions_sort( nAtomNumber+i, r2-i, sizeof( nAtomNumber[0] ), CompNeighListsUpToMaxRank );*/ + + j = r2-1; /* prepare cycle backward, from j to i step -1 */ + nNewRank[(int)nAtomNumber[j]] = r2; + nNumDiffRanks ++; + while( j > i ) { + if ( CompareNeighListLexUpToMaxRank( NeighList[nAtomNumber[j-1]], + NeighList[nAtomNumber[j]], nRank, nMaxAtRank ) ) { + r2 = j; + nNumDiffRanks ++; + nNumNewRanks ++; + } + j --; + nNewRank[(int)nAtomNumber[j]] = r2; + } + i = r1; + } + return nNumNewRanks? -nNumDiffRanks : nNumDiffRanks; +} + + +/**************************************************************************************/ +/* Set ranks from the products vector and previous ranks */ +/* nRank[] and nNewRank[] should refer to different arrays for now */ +/**************************************************************************************/ +int SetNewRanksFromNeighLists( CANON_GLOBALS *pCG, + int num_atoms, NEIGH_LIST *NeighList, + AT_RANK *nRank, AT_RANK *nNewRank, + AT_RANK *nAtomNumber, int bUseAltSort, + int ( *comp )(const void *, const void *, void *) ) +{ + int i, nNumDiffRanks; + AT_RANK nCurrentRank; + /* -- nAtomNumber[] is already properly set -- + for ( i = 0; i < num_atoms; i++ ) { + nAtomNumber[i] = (AT_RANK)i; + } + */ + + /* set globals for qsort */ + pCG->m_pNeighList_RankForSort = NeighList; + pCG->m_pn_RankForSort = nRank; + + /* sorting */ + if ( bUseAltSort & 1 ) + tsort( pCG, nAtomNumber, num_atoms, sizeof( nAtomNumber[0] ), comp /*CompNeighListRanksOrd*/ ); + else + inchi_qsort( pCG, nAtomNumber, num_atoms, sizeof( nAtomNumber[0] ), comp /*CompNeighListRanksOrd*/ ); + + for ( i=num_atoms-1, nCurrentRank=nNewRank[(int)nAtomNumber[i]] = (AT_RANK)num_atoms, nNumDiffRanks = 1; + 0 < i ; + i -- ) { + /* Note: CompNeighListRanks() in following line implicitly reads nRank pointed by pn_RankForSort */ + if ( CompNeighListRanks( &nAtomNumber[i-1], &nAtomNumber[i] , pCG) ) + { + nNumDiffRanks ++; + nCurrentRank = (AT_RANK)i; + } + nNewRank[(int)nAtomNumber[i - 1]] = nCurrentRank; + } + + return nNumDiffRanks; +} +/**************************************************************************************/ +/* Sort NeighList[] lists of neighbors according to the ranks of the neighbors */ +/**************************************************************************************/ +void SortNeighListsBySymmAndCanonRank( int num_atoms, NEIGH_LIST *NeighList, const AT_RANK *nSymmRank, const AT_RANK *nCanonRank ) +{ + int i; + for ( i = 0; i < num_atoms; i ++ ) { + insertions_sort_NeighListBySymmAndCanonRank( NeighList[i], nSymmRank, nCanonRank ); + } +} +/**************************************************************************************/ +int SortNeighLists2( int num_atoms, AT_RANK *nRank, NEIGH_LIST *NeighList, AT_RANK *nAtomNumber ) +{ + int k, i; + AT_RANK nPrevRank = 0; + /* + * on entry nRank[nAtomNumber[k]] <= nRank[nAtomNumber[k+1]] ( k < num_atoms-1 ) + * nRank[nAtomNumber[k]] >= k+1 ( k < num_atoms ) + * nRank[nAtomNumber[k]] == k+1 if this nRank value is not tied OR if + * nRank[nAtomNumber[k]] < nRank[nAtomNumber[k+1]] OR if k = num_atoms-1. + * + */ + for ( k = 0; k < num_atoms; k ++ ) { + i = nAtomNumber[k]; + if ( (nRank[i] != k+1 || nRank[i] == nPrevRank) && NeighList[i][0] > 1 ) { + /* nRank[i] is tied (duplicated) */ + insertions_sort_NeighList_AT_NUMBERS( NeighList[i], nRank ); + } + nPrevRank = nRank[i]; + } + return 0; +} +/**************************************************************************************/ +int SortNeighLists3( int num_atoms, AT_RANK *nRank, NEIGH_LIST *NeighList, AT_RANK *nAtomNumber ) +{ + int k, i; + AT_RANK nPrevRank = 0; + /* + * on entry nRank[nAtomNumber[k]] <= nRank[nAtomNumber[k+1]] ( k < num_atoms-1 ) + * nRank[nAtomNumber[k]] >= k+1 ( k < num_atoms ) + * nRank[nAtomNumber[k]] == k+1 if this nRank value is not tied OR if + * nRank[nAtomNumber[k]] < nRank[nAtomNumber[k+1]] OR if k = num_atoms-1. + * + */ + for ( k = 0; k < num_atoms; k ++ ) { + i = nAtomNumber[k]; + if ( (nRank[i] != k+1 || nRank[i] == nPrevRank) && NeighList[i][0] > 1 ) { + /* nRank[i] is tied (duplicated) */ + insertions_sort_NeighList_AT_NUMBERS3( NeighList[i], nRank ); + } + nPrevRank = nRank[i]; + } + return 0; +} + + +/************************************************************************************** + * + * Differentiate2 + * + * Note: on entry nAtomNumber[] must contain a valid transposition of num_atoms length + * for example, nAtomNumber[i] = i; + * Note2: this version does not calculate neighbor lists for non-tied ranks + */ +int DifferentiateRanks2( CANON_GLOBALS *pCG, int num_atoms, NEIGH_LIST *NeighList, + int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, + AT_RANK *nAtomNumber, long *lNumIter, int bUseAltSort ) +{ + /*int nNumPrevRanks;*/ + + /* SortNeighLists2 needs sorted ranks */ + pCG->m_pn_RankForSort = pnCurrRank; + if ( bUseAltSort & 1 ) + tsort( pCG, nAtomNumber, num_atoms, sizeof(nAtomNumber[0]), CompRank /* CompRanksOrd*/ ); + else + inchi_qsort( pCG, nAtomNumber, num_atoms, sizeof(nAtomNumber[0]), CompRanksOrd ); + + do { + *lNumIter += 1; + /*nNumPrevRanks = nNumCurrRanks;*/ + switch_ptrs( &pnCurrRank, &pnPrevRank ); + SortNeighLists2( num_atoms, pnPrevRank, NeighList, nAtomNumber ); + /* the following call creates pnCurrRank out of pnPrevRank */ + nNumCurrRanks = SetNewRanksFromNeighLists( pCG, num_atoms, NeighList, pnPrevRank, pnCurrRank, nAtomNumber, + 1, CompNeighListRanksOrd ); + } while ( /*nNumPrevRanks != nNumCurrRanks ||*/ memcmp( pnPrevRank, pnCurrRank, num_atoms*sizeof(pnCurrRank[0]) ) ); + + return nNumCurrRanks; +} + + +/************************************************************************************** + * + * Differentiate3 + * + * Note: on entry nAtomNumber[] must contain a valid transposition of num_atoms length + * for example, nAtomNumber[i] = i; + * Note2: this version does not calculate neighbor lists for non-tied ranks + */ +int DifferentiateRanks3( CANON_GLOBALS *pCG, int num_atoms, NEIGH_LIST *NeighList, + int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, + AT_RANK *nAtomNumber, long *lNumIter ) +{ +/* + static long count = 0; + count ++; + if ( count == 103 ) { + int stop=1; + } +*/ + + /* SortNeighLists3 needs sorted ranks: ranks/atnumbers must have been already sorted */ + do { + *lNumIter += 1; + switch_ptrs( &pnCurrRank, &pnPrevRank ); + SortNeighLists3( num_atoms, pnPrevRank, NeighList, nAtomNumber ); + /* the following call creates pnCurrRank out of pnPrevRank */ + nNumCurrRanks = SetNewRanksFromNeighLists3( pCG, num_atoms, NeighList, pnPrevRank, + pnCurrRank, nAtomNumber); + } while ( nNumCurrRanks < 0 /* memcmp( pnPrevRank, pnCurrRank, num_atoms*sizeof(pnCurrRank[0]) )*/ ); + + return nNumCurrRanks; +} + + +/************************************************************************************** + * + * Differentiate4: ignore neighbors with rank > num_atoms + * + * Note: on entry nAtomNumber[] must contain a valid transposition of num_atoms length + * for example, nAtomNumber[i] = i; + * Note2: this version does not sort neighbor lists for non-tied ranks + */ +int DifferentiateRanks4( CANON_GLOBALS *pCG, int num_atoms, NEIGH_LIST *NeighList, + int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, + AT_RANK *nAtomNumber, AT_RANK nMaxAtRank, long *lNumIter ) +{ +/* + static long count = 0; + count ++; + if ( count == 103 ) { + int stop=1; + } +*/ + /* SortNeighLists4 needs sorted ranks: ranks/atnumbers must have been already sorted */ + do { + *lNumIter += 1; + switch_ptrs( &pnCurrRank, &pnPrevRank ); + SortNeighLists3( num_atoms, pnPrevRank, NeighList, nAtomNumber ); + /* the following call creates pnCurrRank out of pnPrevRank */ + nNumCurrRanks = SetNewRanksFromNeighLists4( pCG, num_atoms, NeighList, pnPrevRank, + pnCurrRank, nAtomNumber, nMaxAtRank ); + } while ( nNumCurrRanks < 0 /* memcmp( pnPrevRank, pnCurrRank, num_atoms*sizeof(pnCurrRank[0]) )*/ ); + + return nNumCurrRanks; +} + + +/************************************************************************************** + * + * DifferentiateBasic (sort according to ranks only) + * + * Note: on entry nAtomNumber[] must contain a valid transposition of num_atoms length + * for example, nAtomNumber[i] = i; + * Note2: this version does not calculate neighbor lists for non-tied ranks + */ +int DifferentiateRanksBasic( CANON_GLOBALS *pCG, int num_atoms, NEIGH_LIST *NeighList, + int nNumCurrRanks, AT_RANK *pnCurrRank, AT_RANK *pnPrevRank, + AT_RANK *nAtomNumber, long *lNumIter, int bUseAltSort ) +{ + int nNumPrevRanks; + + /* SortNeighLists2 needs sorted ranks */ + pCG->m_pn_RankForSort = pnCurrRank; + if ( bUseAltSort & 1 ) + tsort( pCG, nAtomNumber, num_atoms, sizeof(nAtomNumber[0]), CompRank ); + else + inchi_qsort( pCG, nAtomNumber, num_atoms, sizeof(nAtomNumber[0]), CompRank ); + + do { + *lNumIter += 1; + nNumPrevRanks = nNumCurrRanks; + switch_ptrs( &pnCurrRank, &pnPrevRank ); + SortNeighLists2( num_atoms, pnPrevRank, NeighList, nAtomNumber ); + /* the following call creates pnCurrRank out of pnPrevRank */ + nNumCurrRanks = SetNewRanksFromNeighLists( pCG, num_atoms, NeighList, pnPrevRank, pnCurrRank, nAtomNumber, bUseAltSort, CompNeighListRanks ); + } while ( nNumPrevRanks != nNumCurrRanks || memcmp( pnPrevRank, pnCurrRank, num_atoms*sizeof(pnCurrRank[0]) ) ); + return nNumCurrRanks; +} + + +/************************************************************************************** + * For the purpose of mapping an atom to an atom: + * (a) find number of tied ranks + * (b) if number of tied ranks > 1 then: + * 1) find the rank for breaking a tie + * 2) allocate memory for breaking the tie if it has not been allocated + * 3) find out if atom 1 ("from") has already been mapped + * Return value: + * < 0: error + * = 1: has already been mapped, to tie to break + * > 1: we need to break a tie + */ +int NumberOfTies( AT_RANK **pRankStack1, AT_RANK **pRankStack2, int length, + int at_no1, int at_no2, AT_RANK *nNewRank, int *bAddStack, int *bMapped1 ) +{ + + AT_RANK *nRank1 = *pRankStack1++; + AT_RANK *nAtomNumber1 = *pRankStack1++; /* ranks for mapping "1", "from" */ + + AT_RANK *nRank2 = *pRankStack2++; + AT_RANK *nAtomNumber2 = *pRankStack2++; /* ranks for mapping "2", "to" */ + + AT_RANK r, *pTempArray; + + int iMax, i, i1, i2; + + *bAddStack = 0; + *bMapped1 = 0; + *nNewRank = 0; + r = nRank1[at_no1]; + if ( r != nRank2[at_no2] ) + return CT_MAPCOUNT_ERR; /* atoms cannot be mapped onto each other: they have different ranks */ /* */ + iMax = r - 1; + /* find i1 and i2 = numbers of ranks in nRank1[] and nRank2[] equal to r: */ + for ( i1 = 1; i1 <= iMax && r == nRank1[nAtomNumber1[iMax-i1]]; i1 ++ ) + ; + for ( i2 = 1; i2 <= iMax && r == nRank2[nAtomNumber2[iMax-i2]]; i2 ++ ) + ; + if ( i2 != i1 ) + return CT_MAPCOUNT_ERR; /* program error: must be identical number of equal ranks */ /* */ + /* found i1 equal rank(s); preceding (smaller) non-equal rank is r-i1 */ + /* To break the tie we have to reduce the rank r to r-i1+1 */ + + /************ Note ******************************* + * IF ( i=r-1 && 0 <= i && i < num_atoms AND + * nRank[nAtomNumber1[i]] == r ) + * THEN: + * nRank[nAtomNumber1[i+1]] > r; (if i+1 < num_atoms) + * nRank[nAtomNumber1[i-1]] <= r; (if i > 0) + * + * IF r = nRank[i] THEN + * nRank[nAtomNumber1[r-1]] == r + * nRank[nAtomNumber1[r-i-1]] <= nRank[nAtomNumber1[r-i]] (for 1 <= i < r ) + */ + if ( i1 > 1 ) { + /* int bAtFromHasAlreadyBeenMapped = 0; */ + *nNewRank = r - i1 + 1; + /* grab an existing or allocate a new array */ + /* we need 4 arrays: 2 for ranks + 2 for numbers */ + for ( i = 0; i < 4; i ++ ) { + if ( i < 2 ) { + pTempArray = *pRankStack1; + *bMapped1 += (pTempArray && pTempArray[0]); + } else { + pTempArray = *pRankStack2; + } + if ( !pTempArray && !(pTempArray = (AT_RANK *) inchi_malloc(length))) + return CT_OUT_OF_RAM; /* out of RAM */ /* */ + /* copy "to" contents */ + switch( i ) { + case 2: + memcpy( pTempArray, nRank2, length ); + break; + case 3: + memcpy( pTempArray, nAtomNumber2, length ); + break; + } + if ( i < 2 ) + *pRankStack1 ++ = pTempArray; + else { + *pRankStack2 ++ = pTempArray; + } + } + *bAddStack = 2; /* to break the tie we added 2 more arrays to pRankStack1 and pRankStack2 */ + } + return i1; +} + + +/************************************************************************************** + * + * + * + * Stereo Mappings + * + * + * + **************************************************************************************/ + +/************************************************************************************** + * Parity for a half of a stereo bond. If both halfs have the same parity + * then the bond is "trans" (E,-,1), otherwise it is "cis" (Z,+,2). + * The advantage of this approach is: The bond parity does not depend on the + * rank of the atom located on the opposite end of the stereogenic bond. + * As the result all bond parities of, for example, benzene, can be calculated + * from equivalence ranks only, without any mappings. + * + * Input: at_no1 = number of atom for which the half-bond parity is calculated + * i_sb_neigh = ordering number of the stereo bond in at->stereo_bond_neighbor[] + * + * Returns: 0=> no parity can be found; 1=> odd parity; 2=> even parity + * + */ +int HalfStereoBondParity( sp_ATOM *at, int at_no1, int i_sb_neigh, const AT_RANK *nRank ) +{ +/* + Suppose neighbors #0,#1,#2 have ranks a, b, c. Remove rank of the neighbor connected + by the stereogenic bond (NCSB) from the a, b, c list and denote the two left as r[0], r[1], + in the same order. Let iNCSB be an ordering number (0,1,or 2) of the NCSB. + Assume the neighbor connected by the stereogenic bond has infinite positive rank. + Position the half-bond so that the stereogenic bond neighbor is to the right from the atom (see below) + + Definition. + =========== + if rank(X) != rank(Y) then Half-bond parity = (rank(X) > rank(Y)), that is, + Y + \ if ( rank(X) < rank(Y) ) then Half-bond parity is Even + C==NCSB if ( rank(X) > rank(Y) ) then Half-bond parity is Odd + / if ( rank(X) = rank(Y) ) then Half-bond parity cannot be defined + X + + 1 2 1 + \ \ \ + C==NCSB C==NCSB C==NCSB C==NCSB + / / / + 2 1 1 + + Parity = 1 Parity = 1 Parity = 2 Parity = 2 + (Odd) (Odd) (Even) or 0 (Even) or 0 + + Half-bond parity = (iNCSB + (r[0] > r[1]) + (Atom C geometric parity))%2 + + Consider the following cases to prove the formula: + + Case 1: 3 explicit neighbors + ============================ + If (1) atom's geometric parity = even (which means neighbors #0, #1, #2 are located clockwise), + and (2) neighbors other than NCSB have different ranks, then, + assuming that NCSB always has the largest (infinite) rank (this is consistent with + the assumption that implicit hydrogens have smallest ranks), we have 3 possibilities: + + c a b + \ \ \ + C==a C==b C==c + / / / + b c a + + iNCSB = 0 1 2 + Half-bond parity = b>c ab (0=even, 1=odd) + r[0]>r[1] r[0]r[1] + Half-bond parity + for all 3 cases = (iNCSB + (r[0] > r[1]))%2 + + The following slight modification will work for both odd and even geometric parity: + + Half-bond parity = (iNCSB + (r[0] > r[1]) + (Atom C geometric parity))%2 + + even parity (0) => atom above the bond has lower rank than the atom below the bond. + + + Case 2: 2 explicit neighbors + ============================ + One implicit hydrogen atom H or hydrogen isotope (implicit rank=0). Assume r[1]=0 + + H a Note. The same method + \ \ works for + C==a C==b + / / N==a and a + b H / \ + b N==b + iNCSB = 0 1 + Half-bond parity = b>0 a<0 + (r[1]=0, r[0]>0) r[0]>r[1] r[0] r[1]) + (Atom C geometric parity))%2 + + Case 3: 1 explicit neighbor (NCSB) + ================================== + Two implicit hydrogens, (number of neighbors on non-streogenic bonds)==0: + + Atom C geometric parity: Even Odd Note. The same method + works for + D H + \ \ Even and Odd + C==a C==a + / / H N==a + H D \ / + N==a H + iNCSB = 0 0 + Half-bond parity = (0<0)=0 (0<0)+1 = 1 + (r[1]=0, r[0]=0) r[1] r[1]) + (Atom C geometric parity))%2 + +*/ + int i, j, k, iNeigh, parity, at1_parity, at_no2; + AT_RANK r[MAX_NUM_STEREO_BOND_NEIGH]; + + if ( at[at_no1].valence > MAX_NUM_STEREO_BOND_NEIGH || ( at1_parity = at[at_no1].parity ) <= 0 ) { + return 0; + } + if ( !PARITY_WELL_DEF( at1_parity ) ) { + if ( PARITY_KNOWN( at1_parity ) ) { + return at1_parity; + } + return -at1_parity; + } + if ( 0 > i_sb_neigh || i_sb_neigh >= MAX_NUM_STEREO_BOND_NEIGH ) { + return CT_STEREOBOND_ERROR; /* */ + } + for ( i = 0; i <= i_sb_neigh; i ++ ) { + if ( !at[at_no1].stereo_bond_neighbor[i] ) { + return CT_STEREOBOND_ERROR; /* */ + } + } + at_no2 = at[at_no1].neighbor[(int)at[at_no1].stereo_bond_ord[i_sb_neigh]]; + memset( r, 0, sizeof( r ) ); + for ( i = j = 0, iNeigh = -1; i < at[at_no1].valence; i ++ ) { + if ( (k = (int)at[at_no1].neighbor[i]) == at_no2 ) { + iNeigh = i; + } else { + r[j++] = nRank[k]; + } + } + if ( iNeigh < 0 || iNeigh != at[at_no1].stereo_bond_ord[i_sb_neigh] ) { + return CT_STEREOBOND_ERROR; /* */ + } + if ( j > 0 && !r[0] || j > 1 && !r[1] ) + return 0; /* undefined ranks */ + + if ( j == 2 && r[0] == r[1] || iNeigh < 0 ) { + parity = AB_PARITY_CALC; /* cannot calculate bond parity without additional breaking ties. */ + } else { + parity = 2 - (at[at_no1].parity + iNeigh + (r[1] < r[0])) % 2; + } + return parity; +} +/**************************************************************************************/ +int parity_of_mapped_half_bond( int from_at, int to_at, int from_neigh, int to_neigh, + sp_ATOM *at, EQ_NEIGH *pEN, + const AT_RANK *nCanonRankFrom, const AT_RANK *nRankFrom, const AT_RANK *nRankTo ) +{ + int i, j, k, num_neigh; + int to_sb_neigh_ord, from_sb_neigh_ord, parity; + AT_RANK r_to[MAX_NUM_STEREO_BOND_NEIGH], at_no_to[MAX_NUM_STEREO_BOND_NEIGH]; + AT_RANK r_canon_from[MAX_NUM_STEREO_BOND_NEIGH], at_no_from[MAX_NUM_STEREO_BOND_NEIGH]; + AT_RANK r, r_sb_neigh; + + for ( i = 0; i < MAX_NUM_STEREO_BOND_NEIGH; i ++ ) { + r_to[i] = r_canon_from[i] = 0; + } + + if ( pEN ) { + memset( pEN, 0, sizeof(*pEN)); + } + + /* for debug only */ + if ( nRankFrom[from_at] != nRankTo[to_at] || + nRankFrom[from_neigh] != nRankTo[to_neigh] || + at[to_at].valence != at[from_at].valence ) { + return 0; /* program error: both atoms must be mapped */ /* */ + } + + parity = PARITY_VAL(at[to_at].parity); + num_neigh = at[to_at].valence; + + if ( num_neigh > MAX_NUM_STEREO_BOND_NEIGH || num_neigh < MIN_NUM_STEREO_BOND_NEIGH ) { + /* 2 neighbors are possible in case of stereo bond with implicit H */ + /* or a stereocenter -CHD- with an implicit H */ + if ( num_neigh == 1 && at[to_at].stereo_bond_neighbor[0] ) { + /* 1 neighbor can happen in case of a terminal =CHD */ + if ( PARITY_WELL_DEF(parity) ) + return 2 - parity % 2; + else + if ( parity ) + return parity; + else + return AB_PARITY_UNDF; /* undefined parity */ + } + return 0; /* program error */ /* */ + } + if ( ATOM_PARITY_KNOWN(parity) ) { + if ( !ATOM_PARITY_WELL_DEF(parity) ) + return parity; + } else + if ( parity ) { + return 0; /* parity; */ + } else { + return 0; /* AB_PARITY_UNDF; */ /* possibly program error: undefined parity */ + } + /* locate at[to_at].stereo_bond_neighbor[] ordering numbers */ + for ( i = 0, to_sb_neigh_ord=-1; i < MAX_NUM_STEREO_BONDS && (k=(int)at[to_at].stereo_bond_neighbor[i]); i ++ ) { + if ( k == to_neigh+1 ) { + to_sb_neigh_ord = i; + break; + } + } + if ( to_sb_neigh_ord < 0 ) { + return 0; /* program error: not a stereo bond */ /* */ + } + to_sb_neigh_ord = (int)at[to_at].stereo_bond_ord[to_sb_neigh_ord]; + r_sb_neigh = nRankTo[(int)at[to_at].neighbor[to_sb_neigh_ord]]; + for ( i = j = 0; i < num_neigh; i ++ ) { + if ( i != to_sb_neigh_ord ) { + r_to[j] = nRankTo[(int)(at_no_to[j]=at[to_at].neighbor[i])]; + if ( r_sb_neigh == r_to[j] ) { + return 0; /* stereo bond atoms are not fully mapped */ + } + j ++; + } + } + if ( j+1 != num_neigh ) { + return 0; /* program error */ /* */ + } + if ( j == 1 ) { + /* only one neighbor; no mapping needed */ + return 2-(parity+1+to_sb_neigh_ord)%2; + } + if ( j != 2 ) { + return 0; /* program error: j can be only 0, 1, or 2 */ /* */ + } + + if ( r_to[0] == r_to[1] ) { + /* double bond neighbors need to be mapped */ + j = 0; + from_sb_neigh_ord = -1; + for ( i = 0; i < num_neigh; i ++ ) { + k = at[from_at].neighbor[i]; + r = nRankFrom[k]; + if ( r == r_sb_neigh ) { + from_sb_neigh_ord = i; /* we need this value only for error-checking */ + } else + if ( r == r_to[0] ) { + r_canon_from[j] = nCanonRankFrom[k]; + at_no_from[j] = (AT_RANK)k; + j ++; + } else { + return 0; /* program error: unexpected rank, not fully mapped adjacent to the stereo bond atoms */ /* */ + } + } + if ( from_sb_neigh_ord < 0 || j != 2 ) { + return 0; /* program error: rank of a neighbor not found */ /* */ + } + if ( pEN ) { /* j == 2 */ + pEN->to_at[0] = at_no_to[0]; + pEN->to_at[1] = at_no_to[1]; + pEN->num_to = 2; /* number of stored in pEN->to_at[] central atom neighbors */ + pEN->rank = r_to[0]; /* mapping rank of the tied neighbors */ + /* i := index of the smaller out of r_canon_from[1] and r_canon_from[0] */ + i = (r_canon_from[1] < r_canon_from[0]); + pEN->from_at = at_no_from[i]; + pEN->canon_rank = r_canon_from[i]; + } + return -((int)r_to[0]); + } + /* double bond neighbors a mapped: r_to[0] != r_to[1] */ + from_sb_neigh_ord = -1; + for ( i = 0; i < num_neigh; i ++ ) { + k = at[from_at].neighbor[i]; + r = nRankFrom[k]; + if ( r == r_sb_neigh ) { + from_sb_neigh_ord = i; /* we need this value only for error-checking */ + } else + if ( r == r_to[0] ) { + r_canon_from[0] = nCanonRankFrom[k]; + /* at_no_from[0] = (AT_RANK)k; */ + } else + if ( r == r_to[1] ) { + r_canon_from[1] = nCanonRankFrom[k]; + /* at_no_from[1] = (AT_RANK)k; */ + } else { + return 0; /* program error: unexpected rank, not fully mapped adjacent to the stereo bond atoms */ /* */ + } + } + if ( !r_canon_from[0] || !r_canon_from[1] || from_sb_neigh_ord < 0 ) { + return 0; /* program error: neighbor rank not found */ /* */ + } + return 2 - (parity + to_sb_neigh_ord + (r_canon_from[1] */ + if ( num_neigh > MAX_NUM_STEREO_ATOM_NEIGH || num_neigh < 2 ) { + /* 2 neighbors are possible in case of stereo bond with implicit H */ + /* or a stereocenter >CHD with two implicit H */ + if ( num_neigh == 1 ) { + /* 1 neighbor can happen in case of a terminal -CHDT or =CHD */ + if ( at[to_at].parity ) + return at[to_at].parity; + else + return AB_PARITY_UNDF; /* undefined parity */ + } + return 0; /* program error */ /* */ + } + for ( i = 0; i < num_neigh; i ++ ) { /* initialization of locals */ + nNeighNumberTo[i] = + nNeighNumberFrom[i] = i; + nNeighRankTo[i] = nRankTo[(int)at[to_at].neighbor[i]]; /* mapping rank */ + nNeighRankFrom[i] = nRankFrom[j=(int)at[from_at].neighbor[i]]; /* mapping rank */ + nNeighRankFromCanon[i] = nCanonRankFrom[j]; /* canonical number */ + } + + pCG->m_pn_RankForSort = nNeighRankFrom; + pCG->m_nNumCompNeighborsRanksCountEql = 0; /* sort mapping ranks-from */ + num_trans_from = insertions_sort( pCG, nNeighNumberFrom, num_neigh, sizeof(nNeighNumberFrom[0]), CompNeighborsRanksCountEql ); + + if ( pCG->m_nNumCompNeighborsRanksCountEql ) { + /* At least 2 neighbors have equal mapping ranks (are tied). */ + /* Find tied from-neighbors with minimal canonical rank (nCanonRankFrom[]) */ + r_canon_from_min = MAX_ATOMS+1; /* max possible rank + 1 */ + for ( i = 1, r = 0, r1 = nNeighRankFrom[neigh1=nNeighNumberFrom[0]]; i < num_neigh; i ++, r1 = r2, neigh1 = neigh2 ) { + r2 = nNeighRankFrom[neigh2=nNeighNumberFrom[i]]; + if ( r2 == r1 ) { + /* found neighbors with tied ranks */ + if ( r != r2 ) { + /* the 1st pair of neighbor with this rank */ + r = r2; + if ( (r_canon_from=nNeighRankFromCanon[neigh1]) < r_canon_from_min ) { + r_canon_from_min = r_canon_from; /* min canon rank */ + neigh_canon_from_min = neigh1; /* neighbor number */ + } + } + if ( (r_canon_from=nNeighRankFromCanon[neigh2]) < r_canon_from_min ) { + r_canon_from_min = r_canon_from; + neigh_canon_from_min = neigh2; + } + } + } + if ( r ) { + /* neighbors with tied ranks have been found => parity cannot be determined without additional mapping */ + /* find to-neighbors on which neigh_canon_from_min can be mapped */ + r1 = nNeighRankFrom[neigh_canon_from_min]; + if ( pEN ) { + for ( i = j = 0; i < num_neigh; i ++ ) { + if ( r1 == nNeighRankTo[i] ) { + pEN->to_at[j++] = at[to_at].neighbor[i]; + } + } + insertions_sort( pCG, pEN->to_at, j, sizeof(pEN->to_at[0]), CompRanksInvOrd ); + pEN->num_to = j; /* number of stored in pEN->to_at[] central atom neighbors */ + pEN->from_at = at[from_at].neighbor[neigh_canon_from_min]; /* neighbor with min. canon number */ + pEN->rank = r1; /* mapping rank of the tied neighbors */ + pEN->canon_rank = r_canon_from_min; /* canon. rank of the pEN->from_at */ + } else { + /* debug only */ + for ( i = j = 0; i < num_neigh; i ++ ) { + if ( r1 == nNeighRankTo[i] ) { + j++; + } + } + } + /* debug only */ + if ( j <= 1 || !r1 || r_canon_from_min > MAX_ATOMS ) { + return 0; /* program error */ /* */ + } + return -r; /* means parity cannot be determined */ + } + return 0; /* program error */ + } + /* All neighbors have different mapping ranks; */ + /* therefore no additional mapping of the neighbors is necessary */ + if ( !ATOM_PARITY_WELL_DEF(at[to_at].parity) ) + return at[to_at].parity; /* unknown parity or cannot be determined */ + + pCG->m_pn_RankForSort = nNeighRankTo; + num_trans_to = insertions_sort( pCG, nNeighNumberTo, num_neigh, sizeof(nNeighNumberTo[0]), CompNeighborsRanksCountEql ); + + /* Map canonical ranks of neighbors. Mapped on each other "to" and "from" atoms have equal mapping ranks */ + for ( i = 0; i < num_neigh; i ++ ) { + if ( nNeighRankTo[j=nNeighNumberTo[i]] != nNeighRankFrom[k=nNeighNumberFrom[i]] ) + return 0; /* program error: mapping ranks not equal, from_at neigborhood cannot be mapped on to_at neighbood. */ /* */ + nNeighRankToCanon[j] = nNeighRankFromCanon[k]; /* potential problem: other atom(s) may have same mapping rank and */ + /* different canon. rank(s). */ + /* we may save some memory by eliminating nNeighRankFromCanon[]: */ + /* nNeighRankToCanon[j] = nCanonRankFrom[at[from_at].neighbor[k]] */ + } + + pCG->m_pn_RankForSort = nNeighRankToCanon; + num_trans_to += insertions_sort( pCG, nNeighNumberTo, num_neigh, sizeof(nNeighNumberTo[0]), CompNeighborsRanksCountEql ); +#ifndef CT_NEIGH_INCREASE + num_trans_to += ((num_neigh*(num_neigh-1))/2)%2; /* get correct parity for ascending order of canon. numbers */ +#endif + + return 2 - (num_trans_to + at[to_at].parity)%2; +} + +/************************************************************************************** + * + * Phase II: map canonicaly numbrered structure onto itself + * to obtain a minimal or maximal stereo part of the CT + * + **************************************************************************************/ + +int ClearPreviousMappings( AT_RANK **pRankStack1 ) +{ + int i; + for ( i = 0; pRankStack1[i]; i ++ ) { + pRankStack1[i][0] = 0; + } + return i; +} +/**************************************************************************************/ +/* map one atom ("from") onto another ("to"): untie their mapping ranks if they are tied. */ +int map_an_atom2( CANON_GLOBALS *pCG, + int num_atoms, int num_max, int at_no1/*from*/, int at_no2/*to*/, + AT_RANK *nTempRank, + int nNumMappedRanks, int *pnNewNumMappedRanks, + CANON_STAT *pCS, + NEIGH_LIST *NeighList, + AT_RANK **pRankStack1, AT_RANK **pRankStack2, int *bAddStack ) +{ + AT_RANK *nRank1, *nAtomNumber1; /* ranks for mapping "1", "from" */ + AT_RANK *nRank2, *nAtomNumber2; /* ranks for mapping "2", "to" */ + AT_RANK *nNewRank1=NULL, *nNewAtomNumber1=NULL; /* ranks for mapping "1", "from" */ + AT_RANK *nNewRank2=NULL, *nNewAtomNumber2=NULL; /* ranks for mapping "2", "to" */ + int length = num_max*sizeof(AT_RANK); + int nNewNumRanks2, nNewNumRanks1; + int i, bAtFromHasAlreadyBeenMapped, nNumTies; + AT_RANK nNewRank; + + nNumTies = NumberOfTies( pRankStack1, pRankStack2, length, at_no1, at_no2, &nNewRank, bAddStack, &bAtFromHasAlreadyBeenMapped ); + + if ( RETURNED_ERROR(nNumTies) ) + return nNumTies; /* error */ + + nRank1 = *pRankStack1++; + nAtomNumber1 = *pRankStack1++; /* ranks for mapping "1", "from" */ + + nRank2 = *pRankStack2++; + nAtomNumber2 = *pRankStack2++; /* ranks for mapping "2", "to" */ + + if ( nNumTies > 1 ) { + + nNewRank1 = *pRankStack1++; + nNewAtomNumber1 = *pRankStack1++; /* ranks for mapping "1", "from" */ + + nNewRank2 = *pRankStack2++; + nNewAtomNumber2 = *pRankStack2++; /* ranks for mapping "2", "to" */ + /* break a tie for "to" */ + memcpy( nNewRank2, nRank2, length ); + memcpy( nNewAtomNumber2, nAtomNumber2, length ); + nNewRank2[at_no2] = nNewRank; + nNewNumRanks2 = DifferentiateRanks2( pCG, num_atoms, NeighList, + nNumMappedRanks, nNewRank2, nTempRank, + nNewAtomNumber2, &pCS->lNumNeighListIter, 1 ); + pCS->lNumBreakTies ++; + + /* Check whether the old mapping can be reused */ + if ( 2 == bAtFromHasAlreadyBeenMapped && nNewRank == nNewRank1[at_no1] ) { + for ( i = 0; i < num_atoms; i ++ ) { + if ( nNewRank1[nNewAtomNumber1[i]] != nNewRank2[nNewAtomNumber2[i]] ) { + bAtFromHasAlreadyBeenMapped = 0; /* It cannot. */ + break; + } + } + } else { + bAtFromHasAlreadyBeenMapped = 0; + } + if ( 2 != bAtFromHasAlreadyBeenMapped ) { + /* break a tie for "from" */ + for ( i = 0; pRankStack1[i]; i ++ ) { + pRankStack1[i][0] = 0; + } + memcpy( nNewRank1, nRank1, length ); + memcpy( nNewAtomNumber1, nAtomNumber1, length ); /* GPF: bad nAtomNumber1 */ + nNewRank1[at_no1] = nNewRank; + nNewNumRanks1 = DifferentiateRanks2( pCG, num_atoms, NeighList, + nNumMappedRanks, nNewRank1, nTempRank, + nNewAtomNumber1, &pCS->lNumNeighListIter, 1 ); + pCS->lNumBreakTies ++; + } else { + nNewNumRanks1 = nNewNumRanks2; + } + + if ( nNewNumRanks1 != nNewNumRanks2 ) + return CT_MAPCOUNT_ERR; /* program error */ /* */ + *pnNewNumMappedRanks = nNewNumRanks2; + /* debug only */ + for ( i = 0; i < num_atoms; i ++ ) { + if ( nNewRank1[nNewAtomNumber1[i]] != nNewRank2[nNewAtomNumber2[i]] ) { + return CT_MAPCOUNT_ERR; /* program error */ /* */ + } + } + } else { + *pnNewNumMappedRanks = nNumMappedRanks; + } + return ( nNewRank1 )? nNewRank1[at_no1] : nRank1[at_no1]; /* mapping rank value */ +} + +/**************************************************************************************/ +int might_change_other_atom_parity( sp_ATOM *at, int num_atoms, int at_no, AT_RANK *nRank2, AT_RANK *nRank1 ) +{ + int i, j, neighbor_no; + for ( i = 0; i < num_atoms; i ++ ) { + if ( nRank2[i] != nRank1[i] ) { + if ( i != at_no /*&& ATOM_PARITY_WELL_DEF(at[i].parity)*/ + && at[i].bHasStereoOrEquToStereo + && !(at[i].stereo_atom_parity & KNOWN_PARITIES_EQL ) + && !at[i].stereo_bond_neighbor[0] + ) { + + return 1; /* may have changed stereo atoms order */ + } + for ( j = 0; j < at[i].valence; j ++ ) { + neighbor_no = at[i].neighbor[j]; + if ( neighbor_no != at_no + /*&& ATOM_PARITY_WELL_DEF(at[neighbor_no].parity)*/ + && at[neighbor_no].bHasStereoOrEquToStereo + && !(at[neighbor_no].stereo_atom_parity & KNOWN_PARITIES_EQL ) + && !at[neighbor_no].stereo_bond_neighbor[0] + ) + return 1; /* may have changed stereo atom parity */ + } + } + } + return 0; +} +/**************************************************************************************/ +#if ( REMOVE_CALC_NONSTEREO == 1 ) /* { */ +/**************************************************************************************/ +void DeAllocateForNonStereoRemoval( AT_RANK **nAtomNumberCanon1, AT_RANK **nAtomNumberCanon2, + NEIGH_LIST **nl, NEIGH_LIST **nl1, NEIGH_LIST **nl2, AT_RANK **nVisited1, AT_RANK **nVisited2 ) +{ + if ( *nAtomNumberCanon1 ) { + inchi_free( *nAtomNumberCanon1 ); + *nAtomNumberCanon1 = NULL; + } + if ( *nAtomNumberCanon2 ) { + inchi_free( *nAtomNumberCanon2 ); + *nAtomNumberCanon2 = NULL; + } + if ( *nl ) { + FreeNeighList( *nl ); + *nl = 0; + } + if ( *nl1 ) { + FreeNeighList( *nl1 ); + *nl1 = 0; + } + if ( *nl2 ) { + FreeNeighList( *nl2 ); + *nl2 = 0; + } + if ( *nVisited1 ) { + inchi_free( *nVisited1 ); + *nVisited1 = NULL; + } + if ( *nVisited2 ) { + inchi_free( *nVisited2 ); + *nVisited2 = NULL; + } +} +/**************************************************************************************/ +int AllocateForNonStereoRemoval( sp_ATOM *at, int num_atoms, const AT_RANK *nSymmRank, AT_RANK *nCanonRank, + AT_RANK **nAtomNumberCanon1, AT_RANK **nAtomNumberCanon2, + NEIGH_LIST **nl, NEIGH_LIST **nl1, NEIGH_LIST **nl2, AT_RANK **nVisited1, AT_RANK **nVisited2 ) +{ + DeAllocateForNonStereoRemoval( nAtomNumberCanon1, nAtomNumberCanon2, nl, nl1, nl2, nVisited1, nVisited2 ); + *nAtomNumberCanon1 = (AT_RANK *) inchi_malloc( num_atoms * sizeof(**nAtomNumberCanon1) ); + *nAtomNumberCanon2 = (AT_RANK *) inchi_malloc( num_atoms * sizeof(**nAtomNumberCanon2) ); + *nl = CreateNeighList( num_atoms, num_atoms, at, 0, NULL ); + *nl1 = CreateNeighList( num_atoms, num_atoms, at, 0, NULL ); + *nl2 = CreateNeighList( num_atoms, num_atoms, at, 0, NULL ); + *nVisited1 = (AT_RANK *) inchi_malloc( num_atoms * sizeof(**nVisited1) ); + *nVisited2 = (AT_RANK *) inchi_malloc( num_atoms * sizeof(**nVisited2) ); + + if ( !*nl || !*nl1 || !*nl2 || !*nVisited1 || !*nVisited2 || !*nAtomNumberCanon1 || !*nAtomNumberCanon2 ) { + DeAllocateForNonStereoRemoval( nAtomNumberCanon1, nAtomNumberCanon2, nl, nl1, nl2, nVisited1, nVisited2 ); + return 0; + } + /* Sort neighbors according to symm. ranks (primary key) and canon. ranks (secondary key), in descending order */ + SortNeighListsBySymmAndCanonRank( num_atoms, *nl, nSymmRank, nCanonRank ); + SortNeighListsBySymmAndCanonRank( num_atoms, *nl1, nSymmRank, nCanonRank ); + SortNeighListsBySymmAndCanonRank( num_atoms, *nl2, nSymmRank, nCanonRank ); + return 1; +} +/**************************************************************************************/ +AT_RANK GetMinNewRank(AT_RANK *nAtomRank, AT_RANK *nAtomNumb, AT_RANK nRank1 ) +{ + int i; + AT_RANK nRank2=0; + for ( i = (int)nRank1-1; 0 <= i && nRank1 == (nRank2 = nAtomRank[(int)nAtomNumb[i]]); i -- ) + ; + if ( i >= 0 ) + nRank2 ++; + else + nRank2 = 1; + return nRank2; +} + + +/**************************************************************************************/ +int BreakNeighborsTie( CANON_GLOBALS *pCG, + sp_ATOM *at, int num_atoms, int num_at_tg, int ib, int ia, + AT_RANK *neigh_num, int in1, int in2, int mode, + AT_RANK **pRankStack1, AT_RANK **pRankStack2, + AT_RANK *nTempRank, NEIGH_LIST *NeighList, + const AT_RANK *nSymmRank, AT_RANK *nCanonRank, + NEIGH_LIST *nl1, NEIGH_LIST *nl2, long *lNumIter ) +{ + AT_RANK nRank1, nRank2; + int nNumDiffRanks, nNumDiffRanks1, nNumDiffRanks2, i; + int n1 = (int)neigh_num[in1]; + int n2 = (int)neigh_num[in2]; + int other_neigh[2], other_neig_ord[2], num_other_neigh; + /* asymmetric calculation */ + + if ( mode == MAP_MODE_S4 && in1 || /* for S4 we need only (in1,in2) = (0,1) (0,2) (0,3) pairs of neighbors */ + mode != MAP_MODE_STD && at[ia].valence != MAX_NUM_STEREO_ATOM_NEIGH || + mode != MAP_MODE_STD && nSymmRank[n1] != nSymmRank[n2] ) { + return 0; + } + /* 1. Create initial ranks from equivalence information stored in nSymmRank */ + memcpy( pRankStack1[0], nSymmRank, num_at_tg * sizeof(pRankStack1[0][0]) ); + pCG->m_pn_RankForSort = pRankStack1[0]; + tsort( pCG, pRankStack1[1], num_at_tg, sizeof(pRankStack1[1][0]), CompRanksOrd ); + nNumDiffRanks = SortedEquInfoToRanks( pRankStack1[0]/*inp*/, pRankStack1[0]/*out*/, pRankStack1[1], num_at_tg, NULL ); + + /* other neighbors */ + num_other_neigh = 0; + if ( at[ia].valence <= MAX_NUM_STEREO_ATOM_NEIGH && mode ) { + for ( i = 0; i < at[ia].valence; i ++ ) { + if ( i != in1 && i != in2 ) { + other_neigh[num_other_neigh] = (int)neigh_num[i]; + other_neig_ord[num_other_neigh] = i; + num_other_neigh ++; + } + } + } + if ( mode != MAP_MODE_STD && nSymmRank[other_neigh[0]] != nSymmRank[other_neigh[1]] || + mode == MAP_MODE_S4 && nSymmRank[n1] != nSymmRank[other_neigh[1]] ) { + return 0; + } + + /* 2. Fix at[ia] */ + if ( pRankStack1[0][ia] != nSymmRank[ia] ) { + /* at[ia] is constitutionally equivalent to some other atom. Fix at[ia]. */ + pRankStack1[0][ia] = nSymmRank[ia]; + nNumDiffRanks = DifferentiateRanksBasic( pCG, num_at_tg, NeighList, + nNumDiffRanks, pRankStack1[0], nTempRank, + pRankStack1[1], lNumIter, 1 ); + } + /* 3. In case of a double bond/cumulene only: */ + /* fix at[ib] -- the opposite double bond/cumulene atom */ + if ( ib < num_atoms ) { + /* find the smallest possible rank */ + nRank1 = pRankStack1[0][ib]; + nRank2 = GetMinNewRank(pRankStack1[0], pRankStack1[1], nRank1 ); + /* if the rank is smaller than pRankStack1[0][ib] then fix at[ib] */ + if ( nRank2 != nRank1 ) { + pRankStack1[0][ib] = nRank2; + nNumDiffRanks = DifferentiateRanksBasic( pCG, num_at_tg, NeighList, + nNumDiffRanks, pRankStack1[0], nTempRank, + pRankStack1[1], lNumIter, 1 ); + } + } + + /************************************************************************************** + * Note: It may (or may not?) make sense to fix "other neighbors": + * in case of a stereo center fix neighbors other than n1, n2 + * in case of a double bond/cumulene fix the opposite atom neighbors + * The ranks assigned to the other neighbors in case of their equivalence + * should be in the ascending order of their canonical ranks ???? + * *** For now we do not fix other neighbors *** + **************************************************************************************/ + + /* 4. Check whether the neighbors still have equal ranks */ + if ( pRankStack1[0][n1] != pRankStack1[0][n2] ) { + return 0; /* the two neighbors are not constitutionally equivalent */ + } + /* 5. Find new smallest possible rank for n1 and n2 */ + nRank1 = pRankStack1[0][n1]; + nRank2 = GetMinNewRank(pRankStack1[0], pRankStack1[1], nRank1 ); + + /* 6. Copy the results to the 2nd eq. rank arrays */ + memcpy( pRankStack2[0], pRankStack1[0], num_at_tg * sizeof(pRankStack2[0][0]) ); + memcpy( pRankStack2[1], pRankStack1[1], num_at_tg * sizeof(pRankStack2[0][0]) ); + + /* 7. Break neighbor tie: map n1(1) <--> n2(2) */ + /* 7. Break neighbor tie: map n1(1) <--> n2(2) */ + pRankStack1[0][n1] = nRank2; + nNumDiffRanks1 = DifferentiateRanksBasic( pCG, num_at_tg, NeighList, + nNumDiffRanks, pRankStack1[0], nTempRank, + pRankStack1[1], lNumIter, 1 ); + + pRankStack2[0][n2] = nRank2; + nNumDiffRanks2 = DifferentiateRanksBasic( pCG, num_at_tg, NeighList, + nNumDiffRanks, pRankStack2[0], nTempRank, + pRankStack2[1], lNumIter, 1 ); + + if ( nNumDiffRanks1 != nNumDiffRanks2 ) { + return -1; /* */ + } + if ( mode == MAP_MODE_C2v || mode == MAP_MODE_C2 ) { + /* Check for C2v reflection leading to parity inversion (mode=1) or C2 rotation (mode=2) */ + AT_RANK nRank10, nRank20; + int nn1, nn2; + /* + * C2v & C2: map + * n1(1) <--> n2(2) -- at this point already done + * n1(2) <--> n2(1) --> do at i = 0 + * + * C2v: other neighbors must be unmoved: map + * other_neigh[0](1) <--> other_neigh[0](2) + * other_neigh[1](1) <--> other_neigh[1](2) + * + * C2: other neighbors should be mapped on each other + * other_neigh[0](1) <--> other_neigh[1](2) + * other_neigh[1](1) <--> other_neigh[0](2) + */ + for ( i = 0; i <= 2; i ++ ) { + if ( i == 0 ) { + /* C2v & C2. Map n2(1) <--> n1(2) */ + nn1 = n2; + nn2 = n1; + } else + if ( mode == MAP_MODE_C2v ) { /* was '=', pointed by WDI */ + /* i = 1 or 2 + * C2v. Other neighbors must be unmoved: map + * i=1: other_neigh[0](1) <--> other_neigh[0](2) + * i=2: other_neigh[1](1) <--> other_neigh[1](2) + */ + nn1 = other_neigh[i-1]; /* 0 or 1 */ + nn2 = other_neigh[i-1]; /* 0 or 1 */ + } else + if ( mode == MAP_MODE_C2 ) { /* was '=', pointed by WDI */ + /* i = 1 or 2 + * C2. Other neighbors should be mapped on each other + * i=1: other_neigh[0](1) <--> other_neigh[1](2) + * i=2: other_neigh[1](1) <--> other_neigh[0](2) + */ + nn1 = other_neigh[i-1]; /* 0 or 1 */ + nn2 = other_neigh[2-i]; /* 1 or 0 */ + } else { + return -1; /* program error */ + } + /* map nn1(1) <--> nn2(2) */ + nRank10 = pRankStack1[0][nn1]; + nRank20 = pRankStack2[0][nn2]; + nRank1 = GetMinNewRank(pRankStack1[0], pRankStack1[1], nRank10 ); + nRank2 = GetMinNewRank(pRankStack2[0], pRankStack2[1], nRank20 ); + if ( nRank10 == nRank20 && nRank1 == nRank2 ) { + if ( nRank10 == nRank1 ) { + ;/* atoms are already mapped */ + } else { + /* need additional mapping: ranks are not fixed yet */ + pRankStack1[0][nn1] = nRank1; + nNumDiffRanks1 = DifferentiateRanksBasic( pCG, num_at_tg, NeighList, + nNumDiffRanks, pRankStack1[0], nTempRank, + pRankStack1[1], lNumIter, 1 ); + pRankStack2[0][nn2] = nRank2; + nNumDiffRanks2 = DifferentiateRanksBasic( pCG, num_at_tg, NeighList, + nNumDiffRanks, pRankStack2[0], nTempRank, + pRankStack2[1], lNumIter, 1 ); + if ( nNumDiffRanks1 != nNumDiffRanks2 ) { + return -1; /* */ + } + } + } else { + return 0; /* mapping is not possible */ + } + } + } + if ( mode == MAP_MODE_S4 ) { + /* + * Check for S4 reflection/rotation leading to parity inversion (mode=3) + * + * At this point n1(1) <--> n2(2) have been mapped and n1 has index in1 = 0 + * Below indexes in neigh_num[] are in brackets; [i] means neigh_num[i]. + * Numbers (#) in parentheses refer to pRankStack# + * + * in2=1: [0](1) <--> [1](2) mapping has been done; add more mappings: + * [1](1) <--> [2](2) [x]=[2] + * [2](1) <--> [3](2) [y]=[3] + * [3](1) <--> [0](2) + * this will succeed if C2 axis crosses middle of [0]-[2] and [1]-[3] lines + * + * in2=2: [0](1) <--> [2](2) mapping has been done; add more mappings: + * [2](1) <--> [3](2) [x]=[3] + * [3](1) <--> [1](2) [y]=[1] + * [1](1) <--> [0](2) + * this will succeed if C2 axis crosses middle of [0]-[3] and [1]-[2] lines + * + * in2=3: [0](1) <--> [3](2) mapping has been done; add more mappings: + * [3](1) <--> [1](2) [x]=[1] + * [1](1) <--> [2](2) [y]=[2] + * [2](1) <--> [0](2) + * this will succeed if C2 axis crosses middle of [0]-[1] and [2]-[3] lines + * + * In general: + * [in1](1) <--> [in2](2) + * [in2](1) <--> [x] (2) i=0 + * [x] (1) <--> [y] (2) i=1 + * [y] (1) <--> [in1](2) i=2 + * + * in1=0 always + * ===== how to find x, y from in2 ==== + * in2=1 => x,y = 2, 3 or [x] = other_neigh[0], [y] = other_neigh[1] + * in2=2 => x,y = 3, 1 or [x] = other_neigh[1], [y] = other_neigh[0] + * in2=3 => x,y = 1, 2 or [x] = other_neigh[0], [y] = other_neigh[1] + * ==================================== + */ + AT_RANK nRank10, nRank20; + int nn1, nn2; + for ( i = 0; i <= 2; i ++ ) { + switch( i ) { + case 0: /* [in2](1) <--> [x](2); */ + nn1 = n2; /* [in2] */ + nn2 = other_neigh[1-in2%2]; /* [x] */ + break; + case 1: /* [x](1) <--> [y](2) */ + nn1 = other_neigh[1-in2%2]; /* [x] */ + nn2 = other_neigh[ in2%2]; /* [y] */ + break; + case 2: + nn1 = other_neigh[ in2%2]; /* [y] */ + nn2 = n1; /* [in1] */ + break; + default: + return -1; /* program error */ + } + /* map nn1(1) <--> nn2(2) */ + nRank10 = pRankStack1[0][nn1]; + nRank20 = pRankStack2[0][nn2]; + nRank1 = GetMinNewRank(pRankStack1[0], pRankStack1[1], nRank10 ); + nRank2 = GetMinNewRank(pRankStack2[0], pRankStack2[1], nRank20 ); + if ( nRank10 == nRank20 && nRank1 == nRank2 ) { + if ( nRank10 == nRank1 ) { + ;/* atoms are already mapped */ + } else { + /* need additional mapping: ranks are not fixed yet */ + pRankStack1[0][nn1] = nRank1; + nNumDiffRanks1 = DifferentiateRanksBasic( pCG, num_at_tg, NeighList, + nNumDiffRanks, pRankStack1[0], nTempRank, + pRankStack1[1], lNumIter, 1 ); + pRankStack2[0][nn2] = nRank2; + nNumDiffRanks2 = DifferentiateRanksBasic( pCG, num_at_tg, NeighList, + nNumDiffRanks, pRankStack2[0], nTempRank, + pRankStack2[1], lNumIter, 1 ); + + if ( nNumDiffRanks1 != nNumDiffRanks2 ) { + return -1; /* */ + } + } + } else { + return 0; /* mapping is not possible */ + } + } + } + + + +#if ( BREAK_ONE_MORE_SC_TIE == 1 ) /* { */ + /* Check for a very highly symmetrical stereo center 12-06-2002 */ + if ( ib >= num_atoms && at[ia].valence == MAX_NUM_STEREO_ATOM_NEIGH ) { + int num_eq; + nRank1 = pRankStack1[0][n2]; + for ( i = 0, num_eq = 0; i < at[ia].valence; i ++ ) { + num_eq += ( nRank1 == pRankStack1[0][at[ia].neighbor[i]]); + } + if ( num_eq == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { + for ( i = (int)nRank1-1; 0 <= i && nRank1 == (nRank2 = pRankStack1[0][(int)pRankStack1[1][i]]); i -- ) + ; + if ( i >= 0 ) + nRank2 ++; + else + nRank2 = 1; + + /* 7a. Break another neighbor tie */ + + nNumDiffRanks = nNumDiffRanks1; + + pRankStack1[0][n2] = nRank2; + nNumDiffRanks1 = DifferentiateRanksBasic( pCG, num_at_tg, NeighList, + nNumDiffRanks, pRankStack1[0], nTempRank, + pRankStack1[1], lNumIter, 1 ); + + pRankStack2[0][n1] = nRank2; + nNumDiffRanks2 = DifferentiateRanksBasic( pCG, num_at_tg, NeighList, + nNumDiffRanks, pRankStack2[0], nTempRank, + pRankStack2[1], lNumIter, 1 ); + } + } + + if ( nNumDiffRanks1 != nNumDiffRanks2 ) { + return -1; /* */ + } +#endif /* } BREAK_ONE_MORE_SC_TIE */ + +#if ( BREAK_ALSO_NEIGH_TIE == 1 ) + /* check whether neighbor's neighbors are tied and untie them */ + if ( at[n1].nRingSystem == at[n2].nRingSystem && ib >= num_atoms ) { + AT_RANK NeighNeighList[MAX_NUM_STEREO_ATOM_NEIGH+1]; + int m, neigh1=-1, neigh2=-1; + nRank1 = nRank2 = 0; + /* n1 */ + NeighNeighList[0] = at[n1].valence-1; /* for insertions_sort_NeighListBySymmAndCanonRank() */ + for ( i = 0, m = 1; i < at[n1].valence; i ++ ) { + int neigh = at[n1].neighbor[i]; + if ( neigh != ia ) { + NeighNeighList[m ++] = neigh; + } + } + insertions_sort_NeighListBySymmAndCanonRank( NeighNeighList, pRankStack1[0], nCanonRank ); + for ( m = 2; m < at[n1].valence; m ++ ) { + if ( pRankStack1[0][NeighNeighList[m]] == pRankStack1[0][NeighNeighList[m-1]] ) { + neigh1 = NeighNeighList[m-1]; + break; + } + } + /* n2 */ + NeighNeighList[0] = at[n2].valence-1; /* for insertions_sort_NeighListBySymmAndCanonRank() */ + for ( i = 0, m = 1; i < at[n2].valence; i ++ ) { + int neigh = at[n2].neighbor[i]; + if ( neigh != ia ) { + NeighNeighList[m ++] = neigh; + } + } + insertions_sort_NeighListBySymmAndCanonRank( NeighNeighList, pRankStack2[0], nCanonRank ); + for ( m = 2; m < at[n2].valence; m ++ ) { + if ( pRankStack2[0][NeighNeighList[m]] == pRankStack2[0][NeighNeighList[m-1]] ) { +#if ( BREAK_ALSO_NEIGH_TIE_ROTATE == 1 ) + neigh2 = NeighNeighList[m]; /* [m] to obtain same axis orientation around ia= 0 && neigh2 >= 0 && pRankStack1[0][neigh1] == pRankStack2[0][neigh2] ) { + /* neighbors' neighbors are tied */ + nRank1 = pRankStack1[0][neigh1]; + nRank2 = GetMinNewRank(pRankStack1[0], pRankStack1[1], nRank1 ); + + /* Break neighbor's neighbor tie */ + + nNumDiffRanks = nNumDiffRanks1; + + pRankStack1[0][neigh1] = nRank2; + nNumDiffRanks1 = DifferentiateRanksBasic( pCG, num_at_tg, NeighList, + nNumDiffRanks, pRankStack1[0], nTempRank, + pRankStack1[1], lNumIter, 1 ); + + pRankStack2[0][neigh2] = nRank2; + nNumDiffRanks2 = DifferentiateRanksBasic( pCG, num_at_tg, NeighList, + nNumDiffRanks, pRankStack2[0], nTempRank, + pRankStack2[1], lNumIter, 1 ); + } + } +#endif + + + /* for debug only */ + for ( i = 0; i < num_at_tg; i ++ ) { + if ( pRankStack1[0][(int)pRankStack1[1][i]] != pRankStack2[0][(int)pRankStack2[1][i]] ) { + return -1; /* */ + } + } + /* Resort lists of neighbors */ + SortNeighListsBySymmAndCanonRank( num_atoms, nl1, pRankStack1[0], nCanonRank ); + SortNeighListsBySymmAndCanonRank( num_atoms, nl2, pRankStack2[0], nCanonRank ); + + return nNumDiffRanks1+1; +} + +/**************************************************************************************/ +int CheckNextSymmNeighborsAndBonds( sp_ATOM *at, AT_RANK cur1, AT_RANK cur2, AT_RANK n1, AT_RANK n2, + AT_RANK *nAvoidCheckAtom, AT_RANK *nVisited1, AT_RANK *nVisited2, + AT_RANK *nVisitOrd1, AT_RANK *nVisitOrd2, const AT_RANK *nRank1, const AT_RANK *nRank2 ) +{ + AT_RANK s1=0, s2=0; + int i1, i2, k1, k2; + if ( nRank1[n1] != nRank2[n2] ) { + return -1; /* parallel traversal in stereo removal failed */ /* */ + } + switch ( !nVisited1[n1] + !nVisited2[n2] ) { + case 0: + if ( nVisited1[n1] != n2+1 || nVisited2[n2] != n1+1 ) { + return -1; /* 0; */ /* possibly error???: we have come to an alreardy traversed pair and */ + /* found that the pair previously has not been traversed synchroneously. */ + } /* -- Happens in C60. */ + break; + case 1: + return -1; /* 0; */ /* possibly error: one is zero, another is not a zero. Happens in C60 */ + + /* case 2: */ + /* both are zero, OK. */ + } + + if ( nVisitOrd1[n1] != nVisitOrd2[n2] ) { + return -1; /* 0; */ /* different DFS trees */ + } + /* at[n1] and at[n2] are next to at[cur1] and at[cur2] respectively */ + /* Even though the bond might have already been checked, check whether */ + /* it is a stereo bond/cumulene. If it is, check the bond/cumulene parity. */ + + /* Even though the bond or cumulene might have already been checked, check it: this is */ + /* the only place we can check stereo bonds and cumulenes that are not edges of the DFS tree */ + /* The code works both for a stereo bond and a stereogenic cumulene. */ + + for ( i1 = 0, k1 = 0; i1 < MAX_NUM_STEREO_BONDS && + (s1=at[cur1].stereo_bond_neighbor[i1]) && + !(k1=(at[cur1].neighbor[(int)at[cur1].stereo_bond_ord[i1]] == n1)); i1 ++ ) + ; + for ( i2 = 0, k2 = 0; i2 < MAX_NUM_STEREO_BONDS && + (s2=at[cur2].stereo_bond_neighbor[i2]) && + !(k2=(at[cur2].neighbor[(int)at[cur2].stereo_bond_ord[i2]] == n2)); i2 ++ ) + ; + + /* -- this does not work in case of cumulenes -- + for ( i1 = 0, k1 = 0; i1 < MAX_NUM_STEREO_BONDS && (s1=at[cur1].stereo_bond_neighbor[i1]) && !(k1=(s1-1 == n1)); i1 ++ ) + ; + for ( i2 = 0, k2 = 0; i2 < MAX_NUM_STEREO_BONDS && (s2=at[cur2].stereo_bond_neighbor[i2]) && !(k2=(s2-1 == n2)); i2 ++ ) + ; + */ + + if ( k1 != k2 ) { + return 0; /* not an error: a stereo bond and not a stereo bond */ + } + if ( k1 ) { + /* here k1 == k2 */ + int bCheckBond1, bCheckBond2; + s1 --; + s2 --; + + bCheckBond1 = (cur1 != nAvoidCheckAtom[0] || s1 != nAvoidCheckAtom[1]) && + (cur1 != nAvoidCheckAtom[1] || s1 != nAvoidCheckAtom[0]); + bCheckBond2 = (cur2 != nAvoidCheckAtom[0] || s2 != nAvoidCheckAtom[1]) && + (cur2 != nAvoidCheckAtom[1] || s2 != nAvoidCheckAtom[0]); + + if ( bCheckBond1 != bCheckBond2 ) + return 0; + + if ( !bCheckBond1 && !bCheckBond2 ) { + return 1; /* do not go any further in this direction */ + } + + if ( at[cur1].stereo_bond_parity[i1] != at[cur2].stereo_bond_parity[i2] ) { + /* different values of at[].stereo_bond_parity: definitely different bonds */ + /* known parities */ + if ( PARITY_KNOWN(at[cur1].stereo_bond_parity[i1] ) && + PARITY_KNOWN(at[cur2].stereo_bond_parity[i2] ) ) { + return 0; /* different currently known stereo bond parities */ + } +#if ( PROPAGATE_ILL_DEF_STEREO != 1 ) + /* well defined and to be calculated from the ranks */ + if ( !(PARITY_CALCULATE(at[cur1].stereo_bond_parity[i1]) && PARITY_WELL_DEF (at[cur2].stereo_bond_parity[i2]) || + PARITY_WELL_DEF (at[cur1].stereo_bond_parity[i1]) && PARITY_CALCULATE(at[cur2].stereo_bond_parity[i2]) || + PARITY_CALCULATE(at[cur1].stereo_bond_parity[i1]) && PARITY_CALCULATE(at[cur2].stereo_bond_parity[i2]) ) ) { + /* do not reject if: "well defined" and "calculate" or "calculate" and "calculate" */ + return 0; + } +#endif + } + +#if ( PROPAGATE_ILL_DEF_STEREO != 1 ) + if ( (cur1 != cur2 || s1 != s2) && (cur1 != s2 || cur2 != s1) ) { + /* two different stereo bonds */ + if ( PARITY_ILL_DEF( at[cur1].stereo_bond_parity[i1] ) || + PARITY_ILL_DEF( at[cur2].stereo_bond_parity[i2] ) ) { + return 0; + } + } +#endif + } + return 1; /* stereo bonds to n1 and n2 have same known parities or are not stereo bonds */ +} +/**************************************************************************************/ +int CreateCheckSymmPaths( sp_ATOM *at, AT_RANK prev1, AT_RANK cur1, AT_RANK prev2, AT_RANK cur2, + AT_RANK *nAvoidCheckAtom, AT_RANK *nVisited1, AT_RANK *nVisited2, + AT_RANK *nVisitOrd1, AT_RANK *nVisitOrd2, + NEIGH_LIST *nl1, NEIGH_LIST *nl2, const AT_RANK *nRank1, const AT_RANK *nRank2, + AT_RANK *nCanonRank, AT_RANK *nLength, int *bParitiesInverted, int mode ) +{ + int k, k1, k2, ret=0, bParitiesInvertedZero=0, *pbParitiesInverted; + AT_RANK n1, n2; + + nVisited1[cur1] = cur2+1; /* symmetrically exchange atom numbers */ + nVisited2[cur2] = cur1+1; + + (*nLength) ++; + + nVisitOrd1[cur1] = *nLength; /* save DFS visit order */ + nVisitOrd2[cur2] = *nLength; + + /* new version allows all inverted parities */ + if ( PARITY_WELL_DEF(at[cur1].stereo_atom_parity) && + PARITY_WELL_DEF(at[cur2].stereo_atom_parity) ) { + if ( *bParitiesInverted < 0 ) { + *bParitiesInverted = (at[cur1].stereo_atom_parity + at[cur2].stereo_atom_parity) % 2; + } else + if ( *bParitiesInverted != (at[cur1].stereo_atom_parity + at[cur2].stereo_atom_parity) % 2 ) { + return 0; /* Different known in advance parities have wrong "inverted" relation */ + } + } else + if ( PARITY_KNOWN(at[cur1].stereo_atom_parity) && + PARITY_KNOWN(at[cur2].stereo_atom_parity) && + at[cur1].stereo_atom_parity != at[cur2].stereo_atom_parity ) { + return 0; /* Different known in advance parities */ + } + + if ( cur1 != cur2 && + !at[cur1].stereo_bond_neighbor[0] && !at[cur2].stereo_bond_neighbor[0] && + PARITY_KNOWN(at[cur1].parity) != PARITY_KNOWN(at[cur2].parity) ) { + return 0; /* one atom is stereogenic, another (presumably equivalent) is not. 9-11-2002 */ + } +#if ( PROPAGATE_ILL_DEF_STEREO != 1 ) + if ( cur1 != cur2 && + (PARITY_ILL_DEF(at[cur1].stereo_atom_parity) || + PARITY_ILL_DEF(at[cur2].stereo_atom_parity)) + ) { + return 0; /* Cannot detect whether the paths are same or different */ + } +#endif + + if ( at[cur1].valence != at[cur2].valence ) { + return CT_REMOVE_STEREO_ERR; /* program error */ /* */ + } + if ( at[cur1].valence == 1 ) { + return 1; /* so far success */ + } + + if ( nl1[(int)cur1][0] != nl2[(int)cur2][0] || nl1[(int)cur1][0] != at[cur1].valence ) { + return CT_REMOVE_STEREO_ERR; /* error: different valences */ /* */ + } + + + for ( k = 1, k1 = 1, k2 = 1; k < at[cur1].valence; k ++, k1 ++, k2 ++ ) { + if ( (n1 = nl1[(int)cur1][k1]) == prev1 ) { + n1 = nl1[(int)cur1][++k1]; /* don't go back */ + } + if ( (n2 = nl2[(int)cur2][k2]) == prev2 ) { + n2 = nl2[(int)cur2][++k2]; /* don't go back */ + } + + if ( 0 >= (ret = CheckNextSymmNeighborsAndBonds( at, cur1, cur2, n1, n2, nAvoidCheckAtom, + nVisited1, nVisited2, nVisitOrd1, nVisitOrd2, nRank1, nRank2 ) ) ) { + return ret; /* different neighbors or bonds */ + } + + if ( !nVisited1[n1] ) { /* recursion */ + /* allow all inverted parities only inside a single ring system containing the starting point */ + pbParitiesInverted = (at[cur1].nRingSystem == at[n1].nRingSystem)? bParitiesInverted:&bParitiesInvertedZero; + if ( 0 >= (ret = CreateCheckSymmPaths( at, cur1, n1, cur2, n2, nAvoidCheckAtom, + nVisited1, nVisited2, nVisitOrd1, nVisitOrd2, + nl1, nl2, nRank1, nRank2, nCanonRank, nLength, pbParitiesInverted, mode ) ) ) { + return ret; + } + } + } + return 1; /* Success */ +} +/**************************************************************************************/ +/* Compare parities */ +#define MAX_OTHER_NEIGH 2 +/* nNeighMode */ +#define NEIGH_MODE_RING 1 +#define NEIGH_MODE_CHAIN 2 + +#define CHECKING_STEREOCENTER 1 +#define CHECKING_STEREOBOND 2 + +#define COMP_STEREO_SUCCESS 1 +#define NOT_WELL_DEF_UNKN 2 +#define NOT_WELL_DEF_UNDF 4 + +#define PARITY_IMPOSSIBLE 999 +/************************************************************************************** + Note: the following C2v/S4 stereo center symmetry recognition + is not included in the final InChI version released in April 2005 + It is disabled in the mode.h (CHECK_C2v_S4_SYMM = 0) + As the result, the only central atom in S4 or atoms on C2v axis + may have pasrity (-) even though these atoms are not stereogenic. + + Reason: Not finished/tested yet + ************************************************************************************** + + In case of stereocenter with 2 pairs of constitutionally identical neighbors : + + G(n) > H(m) means group G has n elements; group H has m elements and + group H is a subgroup of G + + Td(24) > D2d(8> > D2(4) + > S4(4) > C2(2) -- Test for S4 + > C2v(4) > C2(2) -- Test for C2v + > Cs(2) + + Td(24) > C3v(6) > C3(3) -- does not have 2 pairs of constitutionally identical neighbors + > Cs(2) + + The pair of atoms to check for the existence of a steregenic atom: X, Y + + X Y + \ / + C + / \ + A B + + Conditions to check: + + (a) Old #0: Map canonical numbers X1 <--> Y2 + Traverse DFS from X and Y + If all parities vs. canon. numbers unchanged except that of C + then C is not stereogenic + + (b) C2v #1: discover ACB symmetry plain Cv + o Map canonical numbers X1 <--> Y2, Fix(Ai), Fix(Bi) + o Make sure that after mapping X1 <--> Y2 the atoms Ai and + Bi still have equal mapping ranks + Traverse DFS from X and Y + In this case canonical numbers will be reflected in plane ACB if it exists. + o Criterion of the presence of the symmetry plain is: + --> all stereogenic atoms and allenes parities are inverted + (c) C2v #2: discover vertical axis C2 + o Map canonical numbers X1 <--> Y2 and A1 <--> B2 + o Make sure that after mapping X1 <--> Y2 the atoms Ai and + Bi still have equal mapping ranks + o Traverse DFS from X1 and Y2 + In this case canonical numbers will be rotated by + 180 degrees around the vertical axis + (this may be considered as a superposition of two Cv + reflections in perpendicular vertical planes) + o Criterion of the presence of the C2 axis is: + --> all stereogenic atoms and allenes parities are not changed + (d) S4 #3: discover axis horizontal S4 axis + o Map canonical numbers X1 <--> Y2, Y1 <--> A2, A1 <--> B2, B1 <--> X2 + o Traverse DFS from X1 and Y2 + In this case the canonical numbers will be rotated by + 90 degrees and reflected in a horizontal plane. + 3 attempts corrresponding to transpositions 0132, 0213, 0321 + are sufficient (XY=01,02,03) + o Criterion of the presence of the S4 symmetry axis is: + --> all stereogenic atoms and allenes parities are inverted + +***************************************************************************************/ + +/**************************************************************************************/ +int CalculatedPathsParitiesAreIdentical( CANON_GLOBALS *pCG, + sp_ATOM *at, int num_atoms, + const AT_RANK *nSymmRank, + AT_RANK *nCanonRank, + AT_RANK *nAtomNumberCanon, + AT_RANK *nAtomNumberCanon1, + AT_RANK *nAtomNumberCanon2, + AT_RANK *nVisited1, AT_RANK *nVisited2, + AT_RANK prev_sb_neigh, + AT_RANK cur, + AT_RANK next1, AT_RANK next2, + int nNeighMode, + int bParitiesInverted, int mode, + CANON_STAT *pCS, + int vABParityUnknown) +{ + int i, i01, i02, i11, i12, i21, i22, k, parity, parity1, parity2, parity12, num_other_neigh; + int nNumEqStereogenic, nCheckingMode, not_well_def_parities; + AT_RANK other_neigh[MAX_NUM_STEREO_ATOM_NEIGH], neigh, r1, r2; + int nNumComparedCenters = 0, nNumComparedBonds = 0, bCurParityInv1=0 /*, bCurParityInv2=0*/; + int bCurRotated=0, nNumDiff=0, nNumInv=0; + int s1, s2; + + nCheckingMode = ( prev_sb_neigh < num_atoms )? CHECKING_STEREOBOND : CHECKING_STEREOCENTER; + not_well_def_parities = 0; + nNumEqStereogenic = 0; + + if ( nNeighMode != NEIGH_MODE_RING && + bParitiesInverted != 0 || abs(bParitiesInverted) != 1 ) { + bParitiesInverted = 0; + } + + if ( bParitiesInverted ) { + for ( i = 0, i11 = i22 = 0; i < num_atoms; i ++ ) { + /* count number of atoms that have not been visited */ + i11 += !nVisited1[i]; + i22 += !nVisited2[i]; + nAtomNumberCanon1[i] = MAX_ATOMS+1; /* mark unchanged */ + nAtomNumberCanon2[i] = MAX_ATOMS+1; /* mark unchanged */ + } + if ( i11 || i22 ) { + if ( bParitiesInverted == 1 ) + return 0; /* only a part of the structure has been inverted */ + else + bParitiesInverted = 0; + } + } else { + for ( i = 0; i < num_atoms; i ++ ) { + nAtomNumberCanon1[i] = MAX_ATOMS+1; /* mark unchanged */ + nAtomNumberCanon2[i] = MAX_ATOMS+1; /* mark unchanged */ + } + } + if ( bParitiesInverted > 0 && !(mode == MAP_MODE_C2v || mode == MAP_MODE_S4) || + bParitiesInverted == 0 && !(mode == MAP_MODE_C2 || mode == MAP_MODE_STD)) { + return 0; + } + /************************************************************************************** + * The following discussion assumes that the canonical numbers are + * switched for some pairs of constitutionally identical atoms + * in such a way that the new numbering is an equivalent to the + * nCanonRank[] canonical numbering (the transposition belongs to the + * automorphism group of the chemical structure having no stereo). + * At this point non-zero elements of nVisited1[] and nVisited2[] + * together contain transposition P of the atom numbers. + * and P2 respectively of the ordering atom numbers: nVisitedi[k] = Pi(k)+1; + * In this implementation: + * P1(k)=k for all k + * P2(cur)=cur, P2(next1)=next2, P2(next2)=next1 + * + * Below we call one of the numberings "old", another "new". + * + * *IF* the old and the new canonical numberings produce same parities for stereogenic + * elements for the same canonical number(s) + * (that is, old_parity(canon_number) == new_parity(canon_number) + * *except* the currently being tested stereocenter at[cur] or stereobond/cumulene + * at[cur]=at[prev_sb_neigh], whose parity MUST be inverted + * + * *THEN* the stereocenter or stereobond/cumulene is not stereogenic with one + * + * *EXCEPTION* If the currently tested stereogenic element is constitutionally + * equivalent to two or more other stereogenic elements that have been + * permuted then the currently tested one is still stereogenic. + **************************************************************************************/ + + /* + * 1. replace the assigned in each of the parallel traversals atom numbers + * with the canon. ranks corresponding to the atom numbers in the + * currently numbered atoms at[]. + * One of obtained this way canonical numberings (probably nVisited1[]) + * is same as the nCanonRank[] because usually nVisited1[i] = i+1 or 0 + */ + for ( i = 0; i < num_atoms; i ++ ) { + + if ( nVisited1[i] ) { + /* canonical number of the atom mapped on atom #i in 'left' path */ + nVisited1[i] = nCanonRank[ (int)nVisited1[i] - 1 ]; + /* reverse: atom # from the mapped canonical rank in 'left' path */ + nAtomNumberCanon1[nVisited1[i] - 1] = i; + } + if ( nVisited2[i] ) { + /* canonical number of the atom mapped on atom #i in 'right' path */ + nVisited2[i] = nCanonRank[ (int)nVisited2[i] - 1 ]; + /* reverse: atom # from the mapped canonical rank in 'right' path */ + nAtomNumberCanon2[nVisited2[i] - 1] = i; + } + /* if 'left' and 'right' path do not have atoms in common except the + starting atom (and in case of stereobond, the end atom) some of + nVisitedi[i] elements may be zero. + */ + } + + /* + * if started with a stereobond then check whether its parity has changed. + * If yes then continue, otherwise parities are different + * + * if started with a stereo center then prev_sb_neigh = MAX_ATOMS+1 + * + * If the transposition of next1 and next2 changes only the parity of the starting stereo atom or stereo bond + * then the stereo bond or stereo atom is not stereogenic + * + * The exception: the stereogenic elememt in question is equivalent + * to two or more traversed other stereogenic elememts + * (see nNumEqStereogenic below, case similar to trimethylcyclopropane: + * 3 or more constitutionally equivalent stereogenic elements) + */ + if ( nCheckingMode == CHECKING_STEREOBOND ) { + /****************************************************************************** + * + * Possibly stereogenic starting bond or cumulene at[cur]-at[prev_sb_neigh] + * + *******************************************************************************/ + /* checking the starting stereo bond */ + if ( nVisited1[prev_sb_neigh] || nVisited2[prev_sb_neigh] ) { + /* the bond or cumulene is in the ring and the opposite atom has been visited */ + if ( nVisited1[prev_sb_neigh] != nVisited2[prev_sb_neigh] || + nCanonRank[prev_sb_neigh] != nVisited2[prev_sb_neigh] ) { + return 0; /* error: we came back to the same bond/cumulene and */ /* */ + /* assigned different canon. ranks to the opposite atom. */ + } + if ( at[prev_sb_neigh].valence + at[prev_sb_neigh].num_H > 3 ) + return 0; /* at[prev_sb_neigh] atom can not be adjacent to a stereo bond/cumulene */ + /* or does not have 3 attachments (hydrogens are not considered here) */ + for ( i = 0, k = 0; i < MAX_NUM_STEREO_BONDS && + (neigh=at[prev_sb_neigh].stereo_bond_neighbor[i]) && !(k=(neigh-1 == cur)); i ++ ) + ; + if ( !k ) { + return -1; /* program error: could not locate stereogenic bond mark on the opposite atom */ + } + k = (int)at[prev_sb_neigh].stereo_bond_ord[i]; /* seq. number of the double or cumulene bond on at[prev_sb_neigh] */ + + for ( i = 0, num_other_neigh = 0; i < at[prev_sb_neigh].valence && num_other_neigh <= MAX_OTHER_NEIGH; i ++ ) { + if ( i != k ) { /* do not include the double or cumulene bond */ + other_neigh[num_other_neigh ++] = at[prev_sb_neigh].neighbor[i]; + } + } + if ( num_other_neigh + at[prev_sb_neigh].num_H > MAX_OTHER_NEIGH ) { + return CT_STEREOCOUNT_ERR; /* */ + } + for ( i = 0; i < num_other_neigh; i ++ ) { + k = (int)other_neigh[i]; + if ( nVisited1[k] && nVisited1[k] != nCanonRank[k] ) { + return 0; /* parity of the statring stereo bond/cumulene has not changed. */ + } + if ( nVisited2[k] && nVisited2[k] != nCanonRank[k] ) { + return 0; /* parity of the statring stereo bond/cumulene has not changed. */ + } + } + } + } + if ( nCheckingMode == CHECKING_STEREOCENTER ) { + /************************************************** + * + * Possibly stereogenic starting atom at[cur] + * + **************************************************/ + /* checking the starting stereo center */ + for ( i = 0, num_other_neigh = 0; i < at[cur].valence && num_other_neigh <= MAX_OTHER_NEIGH; i ++ ) { + neigh = at[cur].neighbor[i]; + if ( neigh != next1 && neigh != next2 ) { + other_neigh[num_other_neigh ++] = neigh; + } + } + if ( num_other_neigh + at[cur].num_H > MAX_OTHER_NEIGH ) { + return CT_STEREOCOUNT_ERR; /* */ + } + /* + if ( bParitiesInverted && at[cur].valence == MAX_NUM_STEREO_ATOM_NEIGH ) { + if ( nVisited1[other_neigh[0]] == nCanonRank[other_neigh[0]] || + nVisited2[other_neigh[0]] == nCanonRank[other_neigh[0]] || + nVisited1[other_neigh[1]] == nCanonRank[other_neigh[1]] || + nVisited2[other_neigh[1]] == nCanonRank[other_neigh[1]] ) { + bParitiesInverted = 0; + bCurRotated = 1; + } + } + */ + /* bParitiesInverted = -1 means no predefined stereocenter has been checked */ + if ( bParitiesInverted && at[cur].valence == MAX_NUM_STEREO_ATOM_NEIGH ) { + /* special case: 4 canonically eq. neighbors */ + int canon_parity, parity_vis_1, parity_vis_2; + canon_parity = GetPermutationParity( pCG, at+cur, MAX_ATOMS+1, nCanonRank ); + parity_vis_1 = GetPermutationParity( pCG, at+cur, MAX_ATOMS+1, nVisited1 ); + parity_vis_2 = GetPermutationParity( pCG, at+cur, MAX_ATOMS+1, nVisited2 ); + if ( parity_vis_1 != parity_vis_2 ) { + return 0; + } + if ( bParitiesInverted == 1 && parity_vis_1 == canon_parity ) { + return 0; /* not a typical case of inversion during the mapping of D4h stereocenter */ + } else + if ( bParitiesInverted == -1 ) { + if ( parity_vis_1 == canon_parity ) { + bParitiesInverted = 0; + } else { + bParitiesInverted = 1; + } + } + } + /* at this point bParitiesInverted >= 0 */ + if ( !bParitiesInverted && !bCurRotated ) { + for ( i = 0; i < num_other_neigh; i ++ ) { + k = (int)other_neigh[i]; + if ( nVisited1[k] && nVisited1[k] != nCanonRank[k] ) { + return 0; /* parity of the statring stereo center has not changed. */ + } + if ( nVisited2[k] && nVisited2[k] != nCanonRank[k] ) { + return 0; /* parity of the statring stereo center has not changed. */ + } + } + } + } + + /***************************************************** + * Check other (non-starting) stereo centers + ******************************************************/ + for ( i = 0; i < pCS->nLenLinearCTStereoCarb; i ++, nNumComparedCenters += (k > 0) ) { + r1 = pCS->LinearCTStereoCarb[i].at_num; + i01 = nAtomNumberCanon[r1-1]; /* ord. number of the atom that has canon rank r1 */ + + i11 = nAtomNumberCanon1[r1-1]; /* = (MAX_ATOMS+1) > num_atoms if the atom has not been traversed */ + i12 = nAtomNumberCanon2[r1-1]; /* = otherwise < num_atoms */ + + s1 = (i11 < num_atoms); /* 1 => the center was traversed on path #1 */ + s2 = (i12 < num_atoms); /* 1 => the center was traversed on path #2 */ + + bCurParityInv1 = (bParitiesInverted && + at[cur].nRingSystem == at[i11].nRingSystem && + at[cur].nRingSystem == at[i12].nRingSystem ); + + + k = 0; + + /* check whether the two stereo centers (they can be one and the same atom) have been traversed */ + if ( !s1 && !s2 ) { + continue; /* Both stereo centers have not been traversed; check the next pair. */ + } + + if ( nCheckingMode == CHECKING_STEREOCENTER ) { + /* check whether the stereocenters are the starting stereocenter */ + switch( (cur == i11) + (cur == i12) ) { + case 2: + continue; /* do not recheck the starting atom */ + case 1: + return -1; /* possibly program error */ /* */ + /* case 0: */ + /* break; */ /* the stereo centers are not the sarting stereo center */ + } + if ( cur == i01 ) { + return -1; /* program error: in this case at least one of the i11, i12 must be == cur */ /* */ + } + } + + if ( nNeighMode == NEIGH_MODE_RING ) { + if ( i11 != i12 && !bCurParityInv1 ) { + return -1; /* failed: the two stereo atoms have not been traversed synchronously */ + } + if ( !at[i11].parity || !at[i12].parity ) { + return 0; /* another atom does not have parity (it might have been removed) 9-11-2002 */ + } + } + if ( nNeighMode == NEIGH_MODE_CHAIN ) { + if ( s1+s2 != 1 ) { + return -1; /* program error: only one out of s1 and s2 must be 1, another must be 0. */ + } + if ( s1 && !at[i11].parity || s2 && !at[i12].parity ) { + return 0; /* another atom does not have parity (it might have been removed) 9-11-2002 */ + } + } + + parity = pCS->LinearCTStereoCarb[i].parity; + if ( nNeighMode == NEIGH_MODE_RING && (i11 != i01) && (i12 != i01) || + /* in NEIGH_MODE_RING case we know that i11 == i12 except bCurParityInv1 == 1 */ + nNeighMode == NEIGH_MODE_CHAIN + /* in NEIGH_MODE_CHAIN case here we always have 2 different atoms */ + ) { + /**************************************************************** + * Case of two transposed atoms or a circular permutation in D4h + */ + parity1 = s1? GetStereoCenterParity( pCG, at, i11, nVisited1 ) : PARITY_IMPOSSIBLE; + parity2 = s2? GetStereoCenterParity( pCG, at, i12, nVisited2 ) : PARITY_IMPOSSIBLE; + if ( !ATOM_PARITY_KNOWN(parity1) && !ATOM_PARITY_KNOWN(parity2) ) { + return -1; /* should not happen: must have been detected at the time of the traversal */ + } + if ( s1 && s2 ) { + if ( bCurParityInv1 ) { + int parity1orig = GetStereoCenterParity( pCG, at, i11, nCanonRank ); + int parity2orig = GetStereoCenterParity( pCG, at, i12, nCanonRank ); + if ( i11 == i12 || + (parity1 == parity1orig || parity2 == parity2orig || parity1 != parity2) && + ATOM_PARITY_WELL_DEF(parity1) || + parity1 != parity2 && (!ATOM_PARITY_WELL_DEF(parity1) || + !ATOM_PARITY_WELL_DEF(parity2)) ) + /*return -1; */ /* should be different atoms with inverted parities */ + nNumDiff ++; + } else { + if ( i11 != i12 || parity1 != parity2 ) + return -1; /* program error: must be the same atom */ + } + } + parity12 = s1? parity1 : parity2; + + if ( ATOM_PARITY_WELL_DEF(parity) && parity == parity12 ) { + /* symmetrical neighbors have well-defined equal parities */ + k ++; + if ( nCheckingMode == CHECKING_STEREOCENTER && nNeighMode == NEIGH_MODE_RING ) { + /* all 3: cur, i01, i11 are different atoms (here i11==i12) */ + /* here nSymmRank[i01]==nSymmRank[i11] due to the parallel traversal */ + if ( nSymmRank[cur] == nSymmRank[i01] ) { + nNumEqStereogenic ++; /* all 3 are equ */ + } + } + } else + if ( ATOM_PARITY_WELL_DEF(parity) && ATOM_PARITY_WELL_DEF(parity12) ) { + /* apparently different well-defined parities */ + if ( !bCurParityInv1 ) { + nNumInv ++; + /* return 0; */ + } + } else { +#if ( PROPAGATE_ILL_DEF_STEREO == 1 ) + /* at least one parity is ill-defined. Use parity1 and parity2 to temporarily save bitmaps */ + parity1 = (parity ==vABParityUnknown /*AB_PARITY_UNKN*/)? NOT_WELL_DEF_UNKN : + (parity ==AB_PARITY_UNDF)? NOT_WELL_DEF_UNDF : 0; + parity2 = (parity12==vABParityUnknown /*AB_PARITY_UNKN*/)? NOT_WELL_DEF_UNKN : + (parity12==AB_PARITY_UNDF)? NOT_WELL_DEF_UNDF : 0; + if ( parity1 | parity2 ) { + not_well_def_parities |= ( parity1 | parity2 ); + k ++; + } else { + return -1; /* program error */ /* */ + } +#else + return 0; +#endif + } + } else + if ( i11 == i01 && i12 == i01 ) { + /********************************************************************/ + /* i11 == i12 are same atom as i01, nNeighMode == NEIGH_MODE_RING */ + if ( !s1 || !s2 ) { + return -1; + } + /* the parity of the new neighbors permutation must be same as the old one */ + /* this must work for well-defined and ill-defined parities. */ + /* actual parity (that includes the geometry) is not important here. */ + /* old permutation */ + parity = GetPermutationParity( pCG, at+i01, MAX_ATOMS+1, nCanonRank ); + /* new parmutation */ + parity1 = GetPermutationParity( pCG, at+i01, MAX_ATOMS+1, nVisited1 ); + parity2 = GetPermutationParity( pCG, at+i01, MAX_ATOMS+1, nVisited2 ); + if ( parity != parity1 || parity != parity2 ) { + return 0; + } + k ++; + } else { + /* nNeighMode == NEIGH_MODE_RING and only one out of the two (i11 == i01) (i12 == i01) is true */ + return -1; + } + /* nNumComparedCenters += (k > 0); */ + } + if ( bCurRotated || nNumDiff || nNumInv ) { + return 0; + } + + /* !!!! Add here bParitiesInverted == 1 case !!!! */ + /******************************************************/ + /* Check other (non-starting) stereo bonds/cumulenes */ + /******************************************************/ + for ( i = 0; i < pCS->nLenLinearCTStereoDble; i ++, nNumComparedBonds += (k > 0) ) { + r1 = pCS->LinearCTStereoDble[i].at_num1; + r2 = pCS->LinearCTStereoDble[i].at_num2; + i01 = nAtomNumberCanon[r1-1]; /* ord. number of the atom that originally has canon rank r1 */ + i02 = nAtomNumberCanon[r2-1]; /* ord. number of the atom that originally has canon rank r2 */ + + i11 = nAtomNumberCanon1[r1-1]; /* ord. number of the atom that got canon rank r1 during the parallel traversal */ + i12 = nAtomNumberCanon1[r2-1]; /* ord. number of the atom that got canon rank r2 during the parallel traversal */ + + i21 = nAtomNumberCanon2[r1-1]; + i22 = nAtomNumberCanon2[r2-1]; + + + s1 = (i11 < num_atoms && i12 < num_atoms); + s2 = (i21 < num_atoms && i22 < num_atoms); + + k = 0; + + /* check whether the two stereo bonds/allenes (they can be one and the same) have been traversed */ + if ( !s1 && !s2 ) { + continue; /* Both stereo bonds/cumulenes have not been traversed; check the next pair. */ + } + + if ( nCheckingMode == CHECKING_STEREOBOND ) { + switch ( (i11 == cur && i12 == prev_sb_neigh || i12 == cur && i11 == prev_sb_neigh) + + (i21 == cur && i22 == prev_sb_neigh || i22 == cur && i21 == prev_sb_neigh) ) { + case 2: + continue; /* do not recheck the starting bond/cumulene */ + case 1: + return -1; /* possibly program error */ /* */ + /* case 0: */ + /* break; */ /* the stereo centers are not the sarting stereo center */ + } + if ( (i01 == cur && i02 == prev_sb_neigh) || (i02 == cur && i01 == prev_sb_neigh) ) { + return -1; /* program error: in this case at least one of the i1x, i2x must be == cur */ /* */ + } + } + + if ( nNeighMode == NEIGH_MODE_RING ) { + if ( (i11 != i21 || i12 != i22) && (i11 != i22 || i12 != i21) ) { + return -1; /* failed: the two bonds/cumulenes have not been traversed synchronously */ + } + if ( 0 > GetStereoNeighborPos( at, i11, i12 ) ) { + return 0; /* another bond is not stereo (the stereo might have been removed) 9-11-2002 */ + } + } + if ( nNeighMode == NEIGH_MODE_CHAIN ) { + if ( s1+s2 != 1 ) { + return -1; /* program error: only one out of s1 and s2 must be 1, another must be 0. */ + } + if ( s1 && 0 > GetStereoNeighborPos( at, i11, i12 ) || + s2 && 0 > GetStereoNeighborPos( at, i21, i22 ) ) { + return 0; /* another bond is not stereo (the stereo might have been removed) 9-11-2002 */ + } + } + + parity = pCS->LinearCTStereoDble[i].parity; + /* bMustBeIdentical = ATOM_PARITY_ILL_DEF(parity); */ + /* nNumEqStereogenic = 0; */ + + if ( nNeighMode == NEIGH_MODE_RING && (i11 != i01 || i12 != i02) && (i11 != i02 || i12 != i01) || + nNeighMode == NEIGH_MODE_CHAIN /* in NEIGH_MODE_CHAIN case here we always have 2 different atoms */ + ) { + /*******************************************/ + /* case of two transposed bonds/cumulenes */ + parity1 = s1? GetStereoBondParity( at, i11, i12, nVisited1 ) : PARITY_IMPOSSIBLE; + parity2 = s2? GetStereoBondParity( at, i21, i22, nVisited2 ) : PARITY_IMPOSSIBLE; + if ( !ATOM_PARITY_KNOWN(parity1) && !ATOM_PARITY_KNOWN(parity2) ) { + return -1; /* should not happen: must have been detected at the time of traversal */ + } + if ( s1 && s2 && ((i11 != i21 || i12 != i22) && (i11 != i22 || i12 != i21) || parity1 != parity2 ) ) { + return -1; /* program error: must be the same bond/cumulene */ + } + parity12 = s1? parity1 : parity2; + if ( ATOM_PARITY_WELL_DEF(parity) && parity == parity12 ) { + /* symmetrical neighbors have well-defined equal parities */ + k ++; + if ( nCheckingMode == CHECKING_STEREOBOND && nNeighMode == NEIGH_MODE_RING ) { + /* all 3 bonds: cur-prev_sb_neigh, i01-i02, i11-i12 are different */ + /* (here == compared as unordered pairs) */ + if ( nSymmRank[cur] == nSymmRank[i01] && nSymmRank[prev_sb_neigh] == nSymmRank[i02] || + nSymmRank[cur] == nSymmRank[i02] && nSymmRank[prev_sb_neigh] == nSymmRank[i01] ) { + nNumEqStereogenic ++; + } + } + } else + if ( ATOM_PARITY_WELL_DEF(parity) && ATOM_PARITY_WELL_DEF(parity12) ) { + /* apparently different well-defined parities */ + return 0; + } else { + /* at least one parity is ill-defined. Use parity1 and parity2 to temporarily save bitmaps */ +#if ( PROPAGATE_ILL_DEF_STEREO == 1 ) + parity1 = (parity ==vABParityUnknown /*AB_PARITY_UNKN*/)? NOT_WELL_DEF_UNKN : + (parity ==AB_PARITY_UNDF)? NOT_WELL_DEF_UNDF : 0; + parity2 = (parity12==vABParityUnknown /*AB_PARITY_UNKN*/)? NOT_WELL_DEF_UNKN : + (parity12==AB_PARITY_UNDF)? NOT_WELL_DEF_UNDF : 0; + if ( parity1 | parity2 ) { + not_well_def_parities |= ( parity1 | parity2 ); + k ++; + } else { + return -1; /* program error */ + } +#else + return 0; +#endif + } + } else { + /*****************************************************************************************/ + /* i11-i12 and i21-i22 are same as i01-i02 bond/cumulene, nNeighMode == NEIGH_MODE_RING */ + AT_NUMB n1, n2; + int j; + if ( !s1 || !s2 ) { + return -1; + } + /* find neighbors along the stereo bond/cumulene */ + for ( j = 0, n1 = MAX_ATOMS+1; j < MAX_NUM_STEREO_BOND_NEIGH && at[i01].stereo_bond_neighbor[j]; j ++ ) { + if ( (int)at[i01].stereo_bond_neighbor[j] == i02+1 ) { + n1 = at[i01].neighbor[ (int)at[i01].stereo_bond_ord[j] ]; + break; + } + } + for ( j = 0, n2 = MAX_ATOMS+1; j < MAX_NUM_STEREO_BOND_NEIGH && at[i02].stereo_bond_neighbor[j]; j ++ ) { + if ( (int)at[i02].stereo_bond_neighbor[j] == i01+1 ) { + n2 = at[i02].neighbor[ (int)at[i02].stereo_bond_ord[j] ]; + break; + } + } + if ( n1 > MAX_ATOMS || n2 > MAX_ATOMS ) { + return CT_REMOVE_STEREO_ERR; + } + /* the parity of the new neighbors permutation must be same as the old one */ + /* this must work for well-defined and ill-defined parities. */ + /* actual parity (that includes the geometry) is not important here. */ + /* old permutation */ + parity = GetPermutationParity( pCG, at+i01, n1, nCanonRank) + GetPermutationParity( pCG, at+i02, n2, nCanonRank); + /* new parmutation */ + parity1 = GetPermutationParity( pCG, at+i01, n1, nVisited1 ) + GetPermutationParity( pCG, at+i02, n2, nVisited1 ); + parity2 = GetPermutationParity( pCG, at+i01, n1, nVisited2 ) + GetPermutationParity( pCG, at+i02, n2, nVisited2 ); + if ( parity %2 != parity1 % 2 || parity1 % 2 != parity2 % 2 ) { + return 0; + } + k ++; + } + + /* nNumComparedBonds += ( k > 0 ); */ + } + + if ( nNumEqStereogenic > 0 ) { + /* case similar to trimethylcyclopropane: 3 constitutionally equivalent stereogenic elements */ + /* the transposition does not change the parities */ +#if ( bRELEASE_VERSION == 0 ) + pCS->bExtract |= EXTR_2EQL2CENTER_TO_REMOVE_PARITY; +#endif + return 0; + } +/* ========================================================================================= + Note + ==== + At this point the comparison is complete and no difference sufficient to establish + absence of stereo parity has been found. + However, non-zero not_well_def_parities means that an ill-defined parity was + compared to an ill-defined or well-defined parity. This means that the parity + of the atom or bond being checked cannot be well-defined anymore. + ========================================================================================*/ + + + not_well_def_parities |= COMP_STEREO_SUCCESS; + + return not_well_def_parities; + + /* Add 1 to indicate success. The stereogenic elements might have been */ + /* removed while checking existence of the previous atom/bond stereo */ + /* return (nNumComparedCenters + nNumComparedBonds + 1); */ +} + + +/********************************************************************************/ +/* Remove stereo marks from the bonds that are calculated to be non-stereo */ +/* Such bonds must have 2 constitutionally equivalent attachments */ +/* (can find two canonical numberings that change only one stereo bond parity) */ +int RemoveCalculatedNonStereoBondParities( CANON_GLOBALS *pCG, + sp_ATOM *at, int num_atoms, int num_at_tg, + AT_RANK **pRankStack1, + AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, + AT_RANK *nCanonRank, + const AT_RANK *nSymmRank, + AT_RANK *nAtomNumberCanon, + AT_RANK *nAtomNumberCanon1, + AT_RANK *nAtomNumberCanon2, + NEIGH_LIST *nl, + NEIGH_LIST *nl1, + NEIGH_LIST *nl2, + AT_RANK *nVisited1, + AT_RANK *nVisited2, + CANON_STAT *pCS, + int vABParityUnknown) +{ + int j, n, m, ret, ret1, ret2, ret_failed=0; + + int i1, n1, s2; /* n1 must be SIGNED integer */ + AT_RANK nAtomRank1, nAtomRank2, neigh[3], nAvoidCheckAtom[2], opposite_atom, nLength; + int nNeighMode = NEIGH_MODE_CHAIN; + int nNumEqRingNeigh = 0, bRingNeigh, bSymmNeigh, bParitiesInverted; + NEIGH_LIST *nl01, *nl02; + const AT_RANK *nSymmRank1, *nSymmRank2; + + ret = 0; + +second_pass: + + for ( i1 = 0; i1 < num_atoms && !RETURNED_ERROR(ret_failed); i1 ++ ) { + if ( at[i1].valence != 3 || !at[i1].stereo_bond_neighbor[0] ) { + continue; + } + for ( n1 = 0; n1 < MAX_NUM_STEREO_BONDS && !RETURNED_ERROR(ret_failed) && (s2=at[i1].stereo_bond_neighbor[n1]); n1++ ) { + if ( !PARITY_CALCULATE(at[i1].stereo_bond_parity[n1]) && PARITY_WELL_DEF(at[i1].stereo_bond_parity[n1]) ) { + continue; + } + opposite_atom = (AT_RANK)(s2-1); + s2 = at[i1].neighbor[(int)at[i1].stereo_bond_ord[n1]]; /* different from opposite_atom in case of a cumulene */ + for ( j = 1, n = 0; j <= (int)at[i1].valence; j ++ ) { + if ( nl[i1][j] != s2 ) { + neigh[n++] = nl[i1][j]; /* sorting guarantees that canon. rank of neigh[0] is greater or equal */ + } + } + if ( n != 2 ) { + ret = CT_STEREOBOND_ERROR; /* */ + goto exit_function; + } + if ( nSymmRank[(int)neigh[0]] != nSymmRank[(int)neigh[1]] ) { + continue; /* may happen if another half-bond has not a defined parity */ + } + + bRingNeigh = (at[(int)neigh[0]].nRingSystem == at[(int)neigh[1]].nRingSystem); + switch ( nNeighMode ) { + case NEIGH_MODE_CHAIN: + if ( bRingNeigh ) { + nNumEqRingNeigh ++; + continue; + } + nl01 = nl; + nl02 = nl; + nSymmRank1 = nSymmRank; + nSymmRank2 = nSymmRank; + break; + + case NEIGH_MODE_RING: + if ( !bRingNeigh ) + continue; + /* break a tie between the two contitutionally equivalent neighbors, */ + /* refine the two partitions, sort neighbors lists nl1, nl2 */ + + bSymmNeigh = BreakNeighborsTie( pCG, + at, num_atoms, num_at_tg, + opposite_atom, i1, + neigh, 0, 1, 0, + pRankStack1, pRankStack2, nTempRank, + NeighList, nSymmRank, nCanonRank, + nl1, nl2, &pCS->lNumNeighListIter ); + + if ( bSymmNeigh <= 0 ) { + if ( ret_failed > bSymmNeigh ) + ret_failed = bSymmNeigh; + continue; + } + nl01 = nl1; + nl02 = nl2; + nSymmRank1 = pRankStack1[0]; + nSymmRank2 = pRankStack2[0]; + break; + default: + return CT_STEREOCOUNT_ERR; /* */ + } + + /* initialize arrays */ + memset( nVisited1, 0, sizeof(nVisited1[0])*num_atoms ); + memset( nVisited2, 0, sizeof(nVisited2[0])*num_atoms ); + memset( nAtomNumberCanon1, 0, sizeof(nAtomNumberCanon1[0])*num_atoms ); + memset( nAtomNumberCanon2, 0, sizeof(nAtomNumberCanon2[0])*num_atoms ); + nLength = 1; + nVisited1[i1] = i1+1; /* start atoms are the same */ + nVisited2[i1] = i1+1; + nAtomNumberCanon1[i1] = nLength; + nAtomNumberCanon2[i1] = nLength; + nAvoidCheckAtom[0] = i1; + nAvoidCheckAtom[1] = opposite_atom; + bParitiesInverted = (nNeighMode == NEIGH_MODE_RING && + IS_ALLENE_CHAIN(at[i1].stereo_bond_parity[n1]) && + PARITY_CALCULATE(at[i1].stereo_bond_parity[n1]) && + at[i1].nRingSystem == at[opposite_atom].nRingSystem && + at[opposite_atom].valence==MAX_NUM_STEREO_BONDS)? -1 : 0; + ret1 = ret2 = 0; + if ( 0 < (ret1=CreateCheckSymmPaths( at, (AT_RANK)i1, neigh[0], (AT_RANK)i1, neigh[1], nAvoidCheckAtom, + nVisited1, nVisited2, nAtomNumberCanon1, nAtomNumberCanon2, + nl01, nl02, nSymmRank1, nSymmRank2, nCanonRank, &nLength, &bParitiesInverted, 0 ) ) && + 0 < (ret2=CalculatedPathsParitiesAreIdentical( pCG, at, num_atoms, nSymmRank, + nCanonRank, nAtomNumberCanon, nAtomNumberCanon1, nAtomNumberCanon2, + nVisited1, nVisited2, opposite_atom, (AT_RANK)i1, + neigh[0], neigh[1], nNeighMode, bParitiesInverted, 0, + pCS, vABParityUnknown ) ) ) { + if ( ret2 & ( NOT_WELL_DEF_UNKN | NOT_WELL_DEF_UNDF ) ) { + /* possibly change the parity to unknown or undefined */ + int new_parity = (ret2 & NOT_WELL_DEF_UNKN)? vABParityUnknown /*AB_PARITY_UNKN*/: AB_PARITY_UNDF; + if ( PARITY_ILL_DEF(at[i1].stereo_bond_parity[n1]) && PARITY_VAL(at[i1].stereo_bond_parity[n1]) > new_parity || + PARITY_CALCULATE(at[i1].stereo_bond_parity[n1]) ) { + /* set new unknown or undefined parity */ + SetOneStereoBondIllDefParity( at, i1, /* atom number*/ n1 /* stereo bond ord. number*/, new_parity ); + /* change in pCS */ + nAtomRank1 = inchi_max( nCanonRank[i1], nCanonRank[opposite_atom]); + nAtomRank2 = inchi_min( nCanonRank[i1], nCanonRank[opposite_atom]); + for ( n = 0, m = pCS->nLenLinearCTStereoDble-1; n <= m; n ++ ) { + if ( pCS->LinearCTStereoDble[n].at_num1 == nAtomRank1 && + pCS->LinearCTStereoDble[n].at_num2 == nAtomRank2 ) { + pCS->LinearCTStereoDble[n].parity = new_parity; +#if ( bRELEASE_VERSION == 0 ) + pCS->bExtract |= EXTR_CALC_USED_TO_REMOVE_PARITY; +#endif + m = -1; + break; + } + } + if ( m >= 0 ) { + ret = CT_STEREOCOUNT_ERR; /* */ + goto exit_function; + } + ret ++; + } + } else { + /* remove the parity */ + if ( !RemoveOneStereoBond( at, i1, /* atom number*/ n1 /* stereo bond ord. number*/ ) ) { + ret = CT_STEREOBOND_ERROR; /* */ + goto exit_function; + } + n1 --; /* cycle counter may temporarily become negative */ + /* Remove from the pCS */ + nAtomRank1 = inchi_max( nCanonRank[i1], nCanonRank[opposite_atom]); + nAtomRank2 = inchi_min( nCanonRank[i1], nCanonRank[opposite_atom]); + for ( n = 0, m = pCS->nLenLinearCTStereoDble-1; n <= m; n ++ ) { + if ( pCS->LinearCTStereoDble[n].at_num1 == nAtomRank1 && + pCS->LinearCTStereoDble[n].at_num2 == nAtomRank2 ) { + if ( n < m ) { /* remove pCS->LinearCTStereoDble[n] */ + memmove( pCS->LinearCTStereoDble + n, + pCS->LinearCTStereoDble + n + 1, + (m-n)*sizeof(pCS->LinearCTStereoDble[0]) ); + } + pCS->nLenLinearCTStereoDble --; +#if ( bRELEASE_VERSION == 0 ) + pCS->bExtract |= EXTR_CALC_USED_TO_REMOVE_PARITY; +#endif + m = -1; + break; + } + } + if ( m >= 0 ) { + ret = CT_STEREOCOUNT_ERR; /* */ + goto exit_function; + } + ret ++; + } + } else { + if ( !ret_failed ) { + ret_failed = (ret1<0)? ret1 : (ret2<0)? ret2 : 0; + } + if ( !RETURNED_ERROR(ret_failed) ) { + if ( RETURNED_ERROR( ret1 ) ) + ret_failed = ret1; + else + if ( RETURNED_ERROR( ret2 ) ) + ret_failed = ret2; + } + } + } + } + if ( nNeighMode == NEIGH_MODE_CHAIN && nNumEqRingNeigh && !RETURNED_ERROR(ret_failed) ) { + nNeighMode = NEIGH_MODE_RING; + goto second_pass; + } + +exit_function: + + return RETURNED_ERROR(ret_failed)? ret_failed : ret_failed? -(ret_failed+1) : ret; +} + + +/****************************************************************************/ +/* Remove stereo marks from the atoms that are calculated to be non-stereo */ +/* (can find two numberings that change only one stereo center parity) */ +int RemoveCalculatedNonStereoCenterParities( CANON_GLOBALS *pCG, + sp_ATOM *at, + int num_atoms, + int num_at_tg, + AT_RANK **pRankStack1, + AT_RANK **pRankStack2, + AT_RANK *nTempRank, NEIGH_LIST *NeighList, + AT_RANK *nCanonRank, + const AT_RANK *nSymmRank, + AT_RANK *nAtomNumberCanon, + AT_RANK *nAtomNumberCanon1, + AT_RANK *nAtomNumberCanon2, + NEIGH_LIST *nl, + NEIGH_LIST *nl1, + NEIGH_LIST *nl2, + AT_RANK *nVisited1, + AT_RANK *nVisited2, + CANON_STAT *pCS, + int vABParityUnknown){ + int j, n, m, ret; + + int i, k, ret1, ret2, ret_failed=0, mode, max_mode; + AT_RANK nAtomRank1, neigh[MAX_NUM_STEREO_ATOM_NEIGH], nAvoidCheckAtom[2], nLength; + int nNeighMode = NEIGH_MODE_CHAIN; + int nNumEqRingNeigh = 0, bRingNeigh, bSymmNeigh, bParitiesInverted; + NEIGH_LIST *nl01, *nl02; + const AT_RANK *nSymmRank1, *nSymmRank2; + + ret = 0; + +second_pass: + for ( i = 0; i < num_atoms && !RETURNED_ERROR(ret_failed); i ++ ) { + if ( !at[i].parity || at[i].stereo_bond_neighbor[0] ) { + continue; + } + if ( at[i].valence > MAX_NUM_STEREO_ATOM_NEIGH ) { + continue; /* error: stereo center cannot have more than 4 neighbors */ /* */ + } + /* at[i1] is a stereo center */ + if ( !PARITY_CALCULATE(at[i].stereo_atom_parity) && !PARITY_ILL_DEF(at[i].stereo_atom_parity) ) { + continue; + } + /* neighbors sorted according to symm. ranks (primary key) and canon. ranks (secondary key), in descending order */ + /* sorting guarantees that for two constit. equ. neighbors canon. ranks of the first is greater */ + /* !!! previously (but not anymore) the canon. rank of neigh[0] was greater than the others !!! */ + for ( j = 0; j < at[i].valence; j ++ ) { + neigh[j] = nl[i][j+1]; /* sorting does NOT guarantee that canon. rank of neigh[0] is greater than others */ + } + /* + * mode = 0 => Standard approach: switch 2 neighbors + * 1 => Check for C2v reflection leading to parity inversion + * 2 => Check for C2 rotation preserving parities + * 3 => Check for S4 rotation/reflection leading to parity inversion + */ +#if ( CHECK_C2v_S4_SYMM == 1 ) + if ( nNeighMode = NEIGH_MODE_RING && at[i].valence == 4 && + nSymmRank[(int)neigh[0]] == nSymmRank[(int)neigh[1]] && + nSymmRank[(int)neigh[2]] == nSymmRank[(int)neigh[3]] && + !at[i].bCutVertex + ) { + if ( nSymmRank[(int)neigh[1]] == nSymmRank[(int)neigh[2]] ) { + max_mode = MAP_MODE_S4; + } else { + max_mode = inchi_max(MAP_MODE_C2v, MAP_MODE_C2); + } + } else { + max_mode = MAP_MODE_STD; + } +#else + max_mode = MAP_MODE_STD; +#endif + for ( j = 0; j < at[i].valence && at[i].parity && !RETURNED_ERROR(ret_failed); j ++ ) { + for ( k = j+1; k < at[i].valence && at[i].parity && !RETURNED_ERROR(ret_failed); k ++ ) { + for ( mode = 0; mode <= max_mode && at[i].parity && !RETURNED_ERROR(ret_failed); mode ++ ) { + if ( nSymmRank[(int)neigh[j]] != nSymmRank[(int)neigh[k]] ) { + continue; /* the two neighbors are not constitutionally identical */ + } + bRingNeigh = (at[(int)neigh[j]].nRingSystem == at[(int)neigh[k]].nRingSystem); + switch ( nNeighMode ) { + case NEIGH_MODE_CHAIN: + if ( bRingNeigh ) { + nNumEqRingNeigh ++; + continue; + } + nl01 = nl; + nl02 = nl; + nSymmRank1 = nSymmRank; + nSymmRank2 = nSymmRank; + break; + case NEIGH_MODE_RING: + if ( !bRingNeigh ) + continue; + /* break a tie between the two contitutionally equivalent neighbors, */ + /* refine the two partitions, sort neighbors lists nl1, nl2 */ + bSymmNeigh = BreakNeighborsTie( pCG, at, num_atoms, num_at_tg, MAX_ATOMS+1, i, + neigh, j, k, mode, + pRankStack1, pRankStack2, nTempRank, NeighList, nSymmRank, nCanonRank, + nl1, nl2, &pCS->lNumNeighListIter ); + if ( bSymmNeigh <= 0 ) { + if ( ret_failed > bSymmNeigh ) + ret_failed = bSymmNeigh; + continue; + } + nl01 = nl1; + nl02 = nl2; + nSymmRank1 = pRankStack1[0]; + nSymmRank2 = pRankStack2[0]; + break; + default: + return CT_STEREOCOUNT_ERR; /* */ + } + + /* initialize arrays */ + memset( nVisited1, 0, sizeof(nVisited1[0])*num_atoms ); + memset( nVisited2, 0, sizeof(nVisited2[0])*num_atoms ); + memset( nAtomNumberCanon1, 0, sizeof(nAtomNumberCanon1[0])*num_atoms ); + memset( nAtomNumberCanon2, 0, sizeof(nAtomNumberCanon2[0])*num_atoms ); + nLength = 1; + nVisited1[i] = i+1; /* start atom is same */ + nVisited2[i] = i+1; + nAtomNumberCanon1[i] = nLength; + nAtomNumberCanon2[i] = nLength; + nAvoidCheckAtom[0] = i; + nAvoidCheckAtom[1] = MAX_ATOMS+1; + + bParitiesInverted = (mode==MAP_MODE_C2v || mode==MAP_MODE_S4)? -1 : 0; + /* + if (nNeighMode==NEIGH_MODE_RING && at[i].valence==MAX_NUM_STEREO_ATOM_NEIGH) { + AT_RANK other_neigh[2]; + int n; + for ( m = n = 0; m < MAX_NUM_STEREO_ATOM_NEIGH; m ++ ) { + if ( at[i].neighbor[m] != neigh[j] && at[i].neighbor[m] != neigh[k] ) + other_neigh[n++] = at[i].neighbor[m]; + } + if ( nSymmRank[(int)other_neigh[0]] == nSymmRank[(int)other_neigh[1]] ) + bParitiesInverted = -1; + } + */ + /* allow matching inverted centers only in case all equivalent neighbors in same ring system */ + + ret2 = 0; /* initilize. 1/8/2002 */ + + if ( 0 < (ret1 = CreateCheckSymmPaths( at, (AT_RANK)i, neigh[j], (AT_RANK)i, neigh[k], + nAvoidCheckAtom, + nVisited1, nVisited2, nAtomNumberCanon1, nAtomNumberCanon2, + nl01, nl02, nSymmRank1, nSymmRank2, nCanonRank, &nLength, + &bParitiesInverted, mode ) ) && + 0 < (ret2 = CalculatedPathsParitiesAreIdentical( pCG, at, num_atoms, nSymmRank, + nCanonRank, nAtomNumberCanon, nAtomNumberCanon1, nAtomNumberCanon2, + nVisited1, nVisited2, (AT_RANK)MAX_ATOMS, (AT_RANK)i, + neigh[j], neigh[k], nNeighMode, + bParitiesInverted, mode, pCS, + vABParityUnknown) ) ) { + if ( ret2 & ( NOT_WELL_DEF_UNKN | NOT_WELL_DEF_UNDF ) ) { + /* possibly change the parity to unknown or undefined */ + int new_parity = (ret2 & NOT_WELL_DEF_UNKN)? vABParityUnknown /*AB_PARITY_UNKN*/: AB_PARITY_UNDF; + if ( PARITY_ILL_DEF(at[i].stereo_atom_parity) && + PARITY_VAL(at[i].stereo_atom_parity) > new_parity || + PARITY_CALCULATE(at[i].stereo_atom_parity) ) { + /* set new unknown or undefined parity */ + at[i].stereo_atom_parity = (at[i].stereo_atom_parity ^ PARITY_VAL(at[i].stereo_atom_parity)) | PARITY_VAL(new_parity); + at[i].parity = PARITY_VAL(new_parity); + /* Remove from pCS */ + nAtomRank1 = nCanonRank[i]; + for ( n = 0, m = pCS->nLenLinearCTStereoCarb-1; n <= m; n ++ ) { + if ( pCS->LinearCTStereoCarb[n].at_num == nAtomRank1 ) { + pCS->LinearCTStereoCarb[n].parity = PARITY_VAL(new_parity); + #if ( bRELEASE_VERSION == 0 ) + pCS->bExtract |= EXTR_CALC_USED_TO_REMOVE_PARITY; + #endif + m = -1; + break; + } + } + if ( m >= 0 ) { + ret = CT_STEREOCOUNT_ERR; /* */ + goto exit_function; + } + ret ++; /* number of removed or set unknown/undefined parities */ + } + } else { + RemoveOneStereoCenter( at, i /* atom number*/ ); + /* Remove from pCS */ + nAtomRank1 = nCanonRank[i]; + for ( n = 0, m = pCS->nLenLinearCTStereoCarb-1; n <= m; n ++ ) { + if ( pCS->LinearCTStereoCarb[n].at_num == nAtomRank1 ) { + if ( n < m ) { /* remove pCS->LinearCTStereoDble[n] */ + memmove( pCS->LinearCTStereoCarb + n, + pCS->LinearCTStereoCarb + n + 1, + (m-n)*sizeof(pCS->LinearCTStereoCarb[0]) ); + } + pCS->nLenLinearCTStereoCarb --; + #if ( bRELEASE_VERSION == 0 ) + pCS->bExtract |= EXTR_CALC_USED_TO_REMOVE_PARITY; + #endif + m = -1; + break; + } + } + if ( m >= 0 ) { + ret = CT_STEREOCOUNT_ERR; /* */ + goto exit_function; + } + ret ++; /* number of removed or set unknown/undefined parities */ + } + } else { + if ( !ret_failed ) { + if ( ret1 < 0 ) { + ret_failed = ret1; + } else + if ( ret2 < 0 ) { + ret_failed = ret2; + } + } + if ( !RETURNED_ERROR(ret_failed) ) { + if ( RETURNED_ERROR( ret1 ) ) + ret_failed = ret1; + else + if ( RETURNED_ERROR( ret2 ) ) + ret_failed = ret2; + } + } + } + } + } + } + if ( nNeighMode == NEIGH_MODE_CHAIN && nNumEqRingNeigh && !RETURNED_ERROR(ret_failed) ) { + nNeighMode = NEIGH_MODE_RING; + goto second_pass; + } + +exit_function: + + return RETURNED_ERROR(ret_failed)? ret_failed : ret_failed? -(ret+1) : ret; +} + + +/**************************************************************************************/ +int RemoveCalculatedNonStereo( CANON_GLOBALS *pCG, sp_ATOM *at, int num_atoms, int num_at_tg, + AT_RANK **pRankStack1, AT_RANK **pRankStack2, AT_RANK *nTempRank, NEIGH_LIST *NeighList, + const AT_RANK *nSymmRank, AT_RANK *nCanonRank, + AT_RANK *nAtomNumberCanon, CANON_STAT *pCS, + int vABParityUnknown) +{ + NEIGH_LIST *nl = NULL, *nl1 = NULL, *nl2 = NULL; + AT_RANK *nVisited1 = NULL, *nVisited2 = NULL, *nAtomNumberCanon1 = NULL, *nAtomNumberCanon2 = NULL; + int nNumRemoved = 0, nTotRemoved = 0, ret = 0, ret1 = 0, ret2 = 0; + + if ( !AllocateForNonStereoRemoval( at, num_atoms, nSymmRank, nCanonRank, + &nAtomNumberCanon1, &nAtomNumberCanon2, + &nl, &nl1, &nl2, &nVisited1, &nVisited2 ) ) { + return CT_OUT_OF_RAM; /* */ + } + + do { + nNumRemoved = 0; + /* bonds */ + ret = RemoveCalculatedNonStereoBondParities( pCG, at, num_atoms, num_at_tg, + pRankStack1, pRankStack2, nTempRank, NeighList, + nCanonRank, nSymmRank, + nAtomNumberCanon, nAtomNumberCanon1, nAtomNumberCanon2, + nl, nl1, nl2, nVisited1, nVisited2, pCS, + vABParityUnknown); + if ( RETURNED_ERROR( ret ) ) { + goto exit_function; + } + if ( ret < 0 ) { + if ( ret < ret1 ) { /* */ + ret1 = ret; + } + ret = - ( ret + 1 ); /* number of removed */ + } + nNumRemoved += ret; + + /* centers */ + ret = RemoveCalculatedNonStereoCenterParities( pCG, at, num_atoms, num_at_tg, + pRankStack1, pRankStack2, nTempRank, NeighList, + nCanonRank, nSymmRank, + nAtomNumberCanon, nAtomNumberCanon1, nAtomNumberCanon2, + nl, nl1, nl2, nVisited1, nVisited2, pCS, + vABParityUnknown); + if ( RETURNED_ERROR( ret ) ) { + goto exit_function; + } + if ( ret < 0 ) { + if ( ret < ret2 ) { /* */ + ret2 = ret; + } + ret = - ( ret + 1 ); /* number of removed */ + } + nNumRemoved += ret; + + nTotRemoved += nNumRemoved; + } while ( nNumRemoved ); + + if ( !RETURNED_ERROR( ret1 ) && !RETURNED_ERROR( ret2 ) ) { + ret = inchi_min( ret1, ret2 ); + ret = (ret >= 0)? nTotRemoved : -(1+nTotRemoved); + } + +exit_function: + + DeAllocateForNonStereoRemoval( &nAtomNumberCanon1, &nAtomNumberCanon2, &nl, &nl1, &nl2, &nVisited1, &nVisited2 ); + + return ret; +} + + +#endif /* } REMOVE_CALC_NONSTEREO */ diff --git a/INCHI-1-SRC/INCHI/common/ichimap4.c b/INCHI-1-SRC/INCHI_BASE/src/ichimap4.c similarity index 90% rename from INCHI-1-SRC/INCHI/common/ichimap4.c rename to INCHI-1-SRC/INCHI_BASE/src/ichimap4.c index b4c18d2..7943586 100644 --- a/INCHI-1-SRC/INCHI/common/ichimap4.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichimap4.c @@ -1,1669 +1,1750 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - -#include "mode.h" - -#include "incomdef.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichicant.h" -#include "ichicomn.h" - -#include "ichicomp.h" - - - - -#define SB_DEPTH 6 -/************************************************ - map_stereo_bonds4 and map_stereo_atoms4 use - the following members of CANON_STAT *pCS: - - pCS->bKeepSymmRank // ??? almost unused, replaced with nSymmStereo != NULL ??? - pCS->bFirstCT - pCS->bStereoIsBetter - pCS->lNumNeighListIter - pCS->lNumBreakTies - pCS->lNumRejectedCT - pCS->lNumTotCT - pCS->lNumEqualCT - pCS->lNumDecreasedCT - pCS->bExtract (bRELEASE_VERSION == 0) - pCS->ulTimeOutTime - - pCS->bRankUsedForStereo - pCS->bAtomUsedForStereo - - pCS->LinearCTStereoDble - pCS->LinearCTStereoCarb - pCS->nLenLinearCTStereoCarb - pCS->nLenLinearCTStereoDble - - pCS->nPrevAtomNumber - ************************************************/ -/********************************************************************************/ -int map_stereo_bonds4 ( - sp_ATOM *at, int num_atoms, int num_at_tg, int num_max, int bAllene, - const AT_RANK *nCanonRankFrom, const AT_RANK *nAtomNumberCanonFrom, /* non-stereo canon ranking */ - AT_RANK *nCanonRankTo, /* output canonical stereo numbering*/ - const AT_RANK *nSymmRank, AT_RANK **pRankStack1/*from*/, AT_RANK **pRankStack2/*to*/, - AT_RANK *nTempRank, int nNumMappedRanksInput, - AT_RANK *nSymmStereo, NEIGH_LIST *NeighList, - CANON_STAT *pCS, CUR_TREE *cur_tree, int nNumMappedBonds, - int vABParityUnknown) -{ - int nTotSuccess = 0; /* 1=>full mapping has been completed; - * 2=>obtained a better stereo; - * 4=>restart (stereo bond or atom removed from the stereo CT) - */ - int tpos1; - AT_STEREO_DBLE prevBond; - tpos1 = CurTreeGetPos( cur_tree ); - -total_restart: - - if ( !nNumMappedBonds ) { - - memset( pCS->bRankUsedForStereo, 0, sizeof( pCS->bRankUsedForStereo[0] )*num_atoms ); - SetUseAtomForStereo( pCS->bAtomUsedForStereo, at, num_atoms ); - - if ( pCS->bFirstCT && nSymmStereo && !pCS->bKeepSymmRank ) { - int i; - for ( i = 0; i < num_at_tg; i ++ ) { - /* nSymmStereo[i] = min. {k | at[k] stereo eq. to at[i]} */ - nSymmStereo[i] = i; /* for union-join to keep track of stereo-equivalent atoms */ - } - } - } - - - if ( nNumMappedBonds < pCS->nLenLinearCTStereoDble ) { - - int at_rank1, at_rank2, bStereoIsBetterWasSetHere; - /* AT_RANK *nRankFrom=*pRankStack1++, AT_RANK *nAtomNumberFrom=pRankStack1++; */ - /* AT_RANK *nRankTo =*pRankStack2++, AT_RANK *nAtomNumberTo =pRankStack2++; */ - AT_RANK canon_min1, canon_min2; - int bFirstCanonRank; - int i, j, j1, j2, at_from1, at_from2, at_to1, at_to2, iMax, c; - int nStackPtr[SB_DEPTH], nNumMappedRanks[SB_DEPTH], LastMappedTo1; - int istk, istk2, istk3, bAddStack, nNumAtTo1Success; - int ret1, ret2, parity1, parity2; - - AT_RANK at_rank_canon1; /* = pCS->LinearCTStereoDble[nNumMappedBonds].at_num1; */ /* canonical numbers of atoms */ - AT_RANK at_rank_canon2; /* = pCS->LinearCTStereoDble[nNumMappedBonds].at_num2; */ /* adjacent to the stereogenic bond */ - int nNumChoices, nNumUnkn, nNumUndf, nNumBest, nNumWorse, nNumCalc, sb_parity_calc; - int stereo_bond_parity, prev_stereo_bond_parity, pass, bAllParitiesIdentical, bAllParitiesIdentical2; - AT_STEREO_DBLE prevBond2; - - prevBond = pCS->LinearCTStereoDble[nNumMappedBonds]; - bFirstCanonRank=1; - canon_min1=canon_min2=0; -/* - // find candidates for atom_from1, atom_to1; they must have identical mapping ranks - at_rank1=pRankStack1[0][at_from1=nAtomNumberCanonFrom[(int)at_rank_canon1 - 1]]; // rank "from" for mapping - at_rank2=pRankStack1[0][at_from2=nAtomNumberCanonFrom[(int)at_rank_canon2 - 1]]; // rank "from" for mapping -*/ - if ( nNumMappedBonds ) { - at_rank_canon1 = pCS->LinearCTStereoDble[nNumMappedBonds-1].at_num1; - at_rank_canon2 = pCS->LinearCTStereoDble[nNumMappedBonds-1].at_num2; - } else { - at_rank_canon1 = 0; - at_rank_canon2 = 0; - } - goto bypass_next_canon_ranks_check; - -next_canon_ranks: - - /* Save time: avoid calling Next_SB_At_CanonRanks2() */ - if ( !pCS->bStereoIsBetter /* ??? && !pCS->bFirstCT ???*/ && - at_rank_canon1 > pCS->LinearCTStereoDble[nNumMappedBonds].at_num1 || - at_rank_canon1 == pCS->LinearCTStereoDble[nNumMappedBonds].at_num1 && - at_rank_canon2 >= pCS->LinearCTStereoDble[nNumMappedBonds].at_num2 ) { - - if ( !nTotSuccess ) { - pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond; - } - CurTreeSetPos( cur_tree, tpos1 ); - return nTotSuccess; - } - -bypass_next_canon_ranks_check: - - CurTreeSetPos( cur_tree, tpos1 ); - - /* find next available canon. numbers for a stereogenic bond pair of atoms */ - /* process allenes AFTER all double bonds and odd-number-of-double-bonds cumulenes */ - if ( !(ret1 = Next_SB_At_CanonRanks2( &at_rank_canon1, &at_rank_canon2, /* canonical numbers */ - &canon_min1, &canon_min2, - &bFirstCanonRank, pCS->bAtomUsedForStereo, - pRankStack1, pRankStack2, - nCanonRankFrom, nAtomNumberCanonFrom, - at, num_atoms, bAllene ) ) ) { - /* failed to find next stereo bond to assign parity */ - if ( !bAllene && bFirstCanonRank ) { - /* all stereobond have been processed; try to find allene to continue */ - AT_RANK at_rank_canon1_Allene = 0, canon_min1_Allene = 0; - AT_RANK at_rank_canon2_Allene = 0, canon_min2_Allene = 0; - if ( ret1 = Next_SB_At_CanonRanks2( &at_rank_canon1_Allene, &at_rank_canon2_Allene, - &canon_min1_Allene, &canon_min2_Allene, - &bFirstCanonRank, pCS->bAtomUsedForStereo, - pRankStack1, pRankStack2, - nCanonRankFrom, nAtomNumberCanonFrom, - at, num_atoms, 1 ) ) { - at_rank_canon1 = at_rank_canon1_Allene; - at_rank_canon2 = at_rank_canon2_Allene; - canon_min1 = canon_min1_Allene; - canon_min2 = canon_min2_Allene; - bAllene = 1; /* switch to allenes */ - } - } - } - - if ( !ret1 || !pCS->bStereoIsBetter && - (at_rank_canon1 > pCS->LinearCTStereoDble[nNumMappedBonds].at_num1 || - at_rank_canon1 == pCS->LinearCTStereoDble[nNumMappedBonds].at_num1 && - at_rank_canon2 > pCS->LinearCTStereoDble[nNumMappedBonds].at_num2 ) ) { - /* new ranks provide greater pCS->LinearCTStereoDble[nNumMappedBonds] and therefore rejected */ - if ( !nTotSuccess ) { - pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond; /* restore stereo bond CT for the current bond */ - } - return nTotSuccess; - } - /* current stereo bond initialization */ - nNumChoices = 0; - nNumUnkn = 0; - nNumUndf = 0; - nNumBest = 0; - nNumWorse = 0; - nNumCalc = 0; - pass=0; - prev_stereo_bond_parity = 0; - - at_rank1=pRankStack1[0][at_from1=nAtomNumberCanonFrom[(int)at_rank_canon1 - 1]]; /* atom 1 rank "from" for mapping */ - at_rank2=pRankStack1[0][at_from2=nAtomNumberCanonFrom[(int)at_rank_canon2 - 1]]; /* atom 2 rank "from" for mapping */ - /* we are going to map bond (at[at_from1], at[at_from2]) and - canonical ranks of its atoms (at_rank_canon1, at_rank_canon2) - onto a stereogenic bond (at[at_to1], at[at_to2]) - */ - iMax = at_rank1-1; - /* test correctness: sorted pRankStack2[0][] and pRankStack1[0][] should have same ranks for both atoms */ - if ( at_rank1 != pRankStack2[0][pRankStack2[1][at_rank1-1]] || - at_rank2 != pRankStack2[0][pRankStack2[1][at_rank2-1]] ) { - /* program error: "from" and "to" mapping ranks are not equal */ - return CT_STEREOCOUNT_ERR; /* */ - } - /* -- do not check stereo features of "from" atoms: - -- in case of "bond/charge isomerism" they may be missing. - if ( !at[at_from1].stereo_bond_neighbor[0] || - !at[at_from2].stereo_bond_neighbor[0] ) - return CT_STEREOCOUNT_ERR; // program error - */ - - /* find out if we have a choice in mapping: check all possible pairs (at_to1, at_to2) - such that at_from1 is possibly constitutionally equivalent to at_to1, at_from2 to at_to2 */ - for ( j1 = 0; j1 <= iMax && at_rank1 == pRankStack2[0][at_to1=pRankStack2[1][iMax-j1]]; j1 ++ ) { - if ( !at[at_to1].stereo_bond_neighbor[0] ) - continue; /* at_to1 does not belong to a stereo bond */ - for( j2 = 0; j2 < MAX_NUM_STEREO_BONDS && - (at_to2 =at[at_to1].stereo_bond_neighbor[j2]); j2 ++ ) { - at_to2 --; - if ( pRankStack1[0][at_from2] != pRankStack2[0][at_to2] ) - continue; /* at_from2 cannot be mapped on at_to2 */ - stereo_bond_parity = PARITY_VAL(at[at_to1].stereo_bond_parity[j2]); - i = 0; - switch(stereo_bond_parity) { - - case AB_PARITY_UNDF: nNumUndf ++; - break; /* 4 */ - case AB_PARITY_UNKN: nNumUnkn ++; - break; /* 3 (occurs if forced different to UNDF)*/ - - case BEST_PARITY: nNumBest ++; break; /* 1 */ - case WORSE_PARITY: nNumWorse ++; break; /* 2 */ - case AB_PARITY_CALC: nNumCalc ++; break; /* 6 */ - case AB_PARITY_NONE: i ++; break; /* 0 */ - } - nNumChoices += !i; - } - } - if ( nNumChoices != nNumCalc + nNumUndf + nNumUnkn + nNumBest + nNumWorse ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - if ( !nNumChoices ) { - goto next_canon_ranks; - } - /* Determine the first parity to search */ - sb_parity_calc = ( nNumCalc > 0 )? BEST_PARITY : 0; - - /* ============================================================== - Search sequence: sb_parity_calc stereo_bond_parity - ============================================================== - BEST_PARITY (calc) BEST_PARITY BEST_PARITY - BEST_PARITY (known) BEST_PARITY WORSE_PARITY or 0 - WORSE_PARITY (calc) WORSE_PARITY WORSE_PARITY - WORSE_PARITY (known) WORSE_PARITY 0 - AB_PARITY_UNKN(known) AB_PARITY_UNKN 0 - AB_PARITY_UNDF(known) AB_PARITY_UNDF 0 - - if (sb_parity_calc==stereo_bond_parity) then "calc" else "known" - */ - -repeat_all: - - if ( !nNumMappedBonds ) - pCS->bStereoIsBetter = 0; /* the first stereo feature in the canonical CT; moved here 7-13-2002 */ - - if ( !pass ++ ) { - /* select the smallest (best) parity to search */ - if ( sb_parity_calc ) { - stereo_bond_parity = BEST_PARITY; - } else { - stereo_bond_parity = nNumBest? BEST_PARITY : - nNumWorse? WORSE_PARITY : - nNumUnkn? AB_PARITY_UNKN : - nNumUndf? AB_PARITY_UNDF : AB_PARITY_NONE; - } - } else { - /* second pass: since the first pass failed, search for a worse result */ - prev_stereo_bond_parity = stereo_bond_parity; - i = NextStereoParity2Test( &stereo_bond_parity, &sb_parity_calc, - nNumBest, nNumWorse, nNumUnkn, nNumUndf, nNumCalc, vABParityUnknown); - switch ( i ) { - case 0: - break; /* obtained next parity to test */ - case 1: - goto next_canon_ranks; - default: - return i; /* program error */ - } - } - if ( stereo_bond_parity == AB_PARITY_NONE ) { - /* error? */ - return CT_STEREOCOUNT_ERR; /* */ - } - /* check if the new requested parity is good (small) enough */ - if ( !pCS->bStereoIsBetter ) { - c = CompareLinCtStereoDoubleToValues( nTotSuccess? pCS->LinearCTStereoDble+nNumMappedBonds : &prevBond, - at_rank_canon1, at_rank_canon2, (U_CHAR)stereo_bond_parity ); - if ( c < 0 ) { - if ( !nTotSuccess ) { - pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond; - } - CurTreeSetPos( cur_tree, tpos1 ); - return nTotSuccess; - } - } - - bAllParitiesIdentical = 0; - bAllParitiesIdentical2 = 0; - LastMappedTo1 = -1; - bStereoIsBetterWasSetHere = 0; - istk = istk2 = istk3 = 0; - - if ( !nNumMappedBonds && prev_stereo_bond_parity != stereo_bond_parity ) - pCS->bStereoIsBetter = 0; /* the first stereo feature in the canonical CT; moved here 5-24-2002 */ - - if ( prev_stereo_bond_parity != stereo_bond_parity ) { - CurTreeSetPos( cur_tree, tpos1 ); /* start over */ - } - - /* Mapping: here at_rank1 = nRankTo, at_to1 = nAtomNumberTo */ - for ( j1 = 0; j1 <= iMax && at_rank1 == pRankStack2[0][at_to1=pRankStack2[1][iMax-j1]]; j1 ++ ) { - nNumAtTo1Success = 0; - if ( !at[at_to1].stereo_bond_neighbor[0] ) - continue; /* at_to1 does not belong to a stereo bond */ - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) && - 1 == CurTreeIsLastAtomEqu( cur_tree, at_to1, nSymmStereo ) ) { - /* at_to1 is known to be stereogenically equivalent to another atom tried with at_rank_canon1 */ - continue; - } - bAllParitiesIdentical2 = 0; - for( j2 = 0; j2 < MAX_NUM_STEREO_BONDS && (at_to2 =at[at_to1].stereo_bond_neighbor[j2]); j2 ++ ) { - EQ_NEIGH EN1[2], EN2[2]; - int bond_parity, num1, num2; - AT_RANK at_rank_canon_n1, at_rank_canon_n2; - - at_to2 --; - if ( pRankStack1[0][at_from2] != pRankStack2[0][at_to2] ) - continue; /* at_from2 cannot be mapped on at_to2 even without mapping at_from1 to at_to1 */ - - /* check whether the bond parity corresponds to the requested bond parity */ - if ( PARITY_KNOWN(at[at_to1].stereo_bond_parity[j2]) ) { - if ( stereo_bond_parity == sb_parity_calc ) { - continue; /* requested parity to be calculated, found known parity */ - } - if ( stereo_bond_parity != PARITY_VAL(at[at_to1].stereo_bond_parity[j2]) ) { - continue; /* parity differs from the requested parity */ - } - } else - if ( PARITY_CALCULATE( at[at_to1].stereo_bond_parity[j2]) ) { - if ( stereo_bond_parity != sb_parity_calc ) { - continue; /* requested known parity, found parity to be calculated */ - } - } else { - return CT_STEREOCOUNT_ERR; /* unknown parity type */ /* */ - } - /* initialize stack pointer nStackPtr[istk] for "hand-made" recursion */ - /* stacks are pRankStack1[], pRankStack2[], nNumMappedRanks[] */ - istk = 0; - nStackPtr[0] = 0; - nNumMappedRanks[0] = nNumMappedRanksInput; - bAddStack = 0; - bAllParitiesIdentical = ((at[at_to1].stereo_bond_parity[j2] & KNOWN_PARITIES_EQL )) && - PARITY_KNOWN(at[at_to1].stereo_bond_parity[j2]); - - if ( !bAllParitiesIdentical && !nNumCalc && - (!nNumUndf + !nNumUnkn + !nNumBest + !nNumWorse )==3) { - /* only one kind of bond parity is present; check whether all parities are really same */ - bAllParitiesIdentical = All_SB_Same( at_rank_canon1, at_rank_canon2, /* canonical numbers */ - pRankStack1, pRankStack2, - nAtomNumberCanonFrom, at ); - if ( bAllParitiesIdentical < 0 ) { - return CT_STEREOCOUNT_ERR; /* */ - } - } - - /***************************************************************** - * do the mapping only if parities are not same - */ - if ( !bAllParitiesIdentical ) { - /* map atom 1 or reuse previous mapping */ - if ( LastMappedTo1 != at_to1 ) { - /* avoid repetitve mapping to the same first at_to1 using LastMappedTo1 variable */ - /* map atom 1 */ - ret1 = map_an_atom2( num_at_tg, num_max, at_from1, at_to1, - nTempRank, nNumMappedRanks[istk], &nNumMappedRanks[istk+1], pCS, - NeighList, pRankStack1+nStackPtr[istk], pRankStack2+nStackPtr[istk], - &bAddStack ); - if ( RETURNED_ERROR(ret1) ) { - return ret1; /* error */ - } - nStackPtr[istk+1] = nStackPtr[istk]+bAddStack; - LastMappedTo1 = at_to1; - if ( bAddStack ) { - if ( tpos1 == CurTreeGetPos( cur_tree ) || - 0 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { - CurTreeAddRank( cur_tree, at_rank_canon1 ); - } - CurTreeAddAtom( cur_tree, at_to1 ); - } - } - istk ++; /* = 1 */ - /* check if we can map atom 2 */ - if ( pRankStack1[nStackPtr[istk]][at_from2] != pRankStack2[nStackPtr[istk]][at_to2] ) { - /* - * This may happen when: - * A) Charge/bond isomerism, for example cyclopentadiene(-), or - * B) possibly stereogenic bond in an alternating ring has heighbors - * in 2 symmetrically attached rings. - * Such an alternating bond cannot be mapped on possibly stereogenic bond - * that has neighbors belonging to 1 of the symmetrically attached rings only. - * For example: - * A---B---C---D If all atoms are Carbons then B, C, F, G are constitutionally - * || || || || equivalent. However, bonds B-C, F-G are not equivalent to - * || || || || B-F and C-G and cannot be mapped on them. - * E---F---G---H If at_from1=B, at_from2=F, at_to1=B, then at_from2 cannot be mapped on at_to2=C - * If at_from1=B, at_from2=F, at_to1=C, then at_from2 cannot be mapped on at_to2=B - * etc. - */ - if ( sb_parity_calc != stereo_bond_parity) { - /* can be passed only once for each bond */ - nNumChoices --; - nNumUndf -= (stereo_bond_parity == AB_PARITY_UNDF); - nNumUnkn -= (stereo_bond_parity == AB_PARITY_UNKN); - nNumBest -= (stereo_bond_parity == BEST_PARITY); - nNumWorse-= (stereo_bond_parity == WORSE_PARITY); - /* nNumCalc = nNumChoices - (nNumUndf + nNumUnkn + nNumBest + nNumWorse); */ - } else - if ( sb_parity_calc == BEST_PARITY ) { - /* can be passed 2 times: for BEST_PARITY and WORSE_PARITY in this order */ - nNumChoices --; /* do not repeate for WORSE_PARITY */ - nNumCalc --; - } - continue; /* Happens for ID=80036,80253,91354,95532,101532,103788 */ - } - if ( nStackPtr[istk] > nStackPtr[istk-1] ) { - bAllParitiesIdentical2 = All_SB_Same( at_rank_canon1, at_rank_canon2, - pRankStack1+nStackPtr[istk], pRankStack2+nStackPtr[istk], - nAtomNumberCanonFrom, at ); - if ( bAllParitiesIdentical2 < 0 ) { - return CT_STEREOBOND_ERROR; /* */ - } - } else { - bAllParitiesIdentical2 = 0; - } - if ( bAllParitiesIdentical2 ) { - /* do no mapping when all equivalent bonds have same parity */ - /* stereo_bond_parity = PARITY_VAL(at[at_to1].stereo_bond_parity[j2]); */ - ClearPreviousMappings( pRankStack1+nStackPtr[istk]+2 ); - } else { - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon2 ) && - 1 == CurTreeIsLastAtomEqu( cur_tree, at_to2, nSymmStereo ) ) { - continue; - } - /* map atom 2 */ - ret2 = map_an_atom2( num_at_tg, num_max, at_from2, at_to2, - nTempRank, nNumMappedRanks[istk], &nNumMappedRanks[istk+1], pCS, - NeighList, pRankStack1+nStackPtr[istk], pRankStack2+nStackPtr[istk], - &bAddStack ); - if ( RETURNED_ERROR(ret2) ) { - return ret2; /* program error */ - } - nStackPtr[istk+1] = nStackPtr[istk]+bAddStack; - istk ++; /* = 2 */ - if ( bAddStack ) { - if ( tpos1 == CurTreeGetPos( cur_tree ) || - 0 == CurTreeIsLastRank( cur_tree, at_rank_canon2 ) ) { - CurTreeAddRank( cur_tree, at_rank_canon2 ); - } - CurTreeAddAtom( cur_tree, at_to2 ); - } - } - } else { - /* do no mapping when all equivalent bonds have same parity */ - /* stereo_bond_parity = PARITY_VAL(at[at_to1].stereo_bond_parity[j2]); */ - ClearPreviousMappings( pRankStack1+2 ); - } - - /* we have a precalculated (known) bond parity */ - - - /************************************************************ - * - * Known Bond Parity case: do not map stereo bond neighbors - */ - if ( stereo_bond_parity != sb_parity_calc ) /* parity is known */ - { - /* accept bond parity and do not map the neighbors */ - bond_parity = stereo_bond_parity; - /* same code as under " make a decision to accept current mapping" comment below */ - /* with one exception: istk instead of istk3 */ - c = CompareLinCtStereoDoubleToValues( pCS->LinearCTStereoDble+nNumMappedBonds, - at_rank_canon1, at_rank_canon2, (U_CHAR)bond_parity ); - if ( c < 0 && !pCS->bStereoIsBetter ) { - - /* reject */ - - pCS->lNumRejectedCT ++; - /* remove failed atom2 from the tree */ - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon2 ) ) { - CurTreeRemoveIfLastAtom( cur_tree, at_to2 ); - CurTreeRemoveLastRankIfNoAtoms( cur_tree ); - } - continue; /* to next at_to2; Reject this at_to2: not a minimal CT. */ - } else { - - /* accept */ - - if ( c > 0 && !pCS->bStereoIsBetter ) { - /* bond entry is less than the previusly found */ - pCS->bStereoIsBetter = bStereoIsBetterWasSetHere = 1; - prevBond2 = pCS->LinearCTStereoDble[nNumMappedBonds]; - } - pCS->LinearCTStereoDble[nNumMappedBonds].at_num1 = at_rank_canon1; - pCS->LinearCTStereoDble[nNumMappedBonds].at_num2 = at_rank_canon2; - pCS->LinearCTStereoDble[nNumMappedBonds].parity = bond_parity; - /* recursive call */ - pCS->bRankUsedForStereo[at_from1] ++; - pCS->bRankUsedForStereo[at_from2] ++; - if ( !bAllParitiesIdentical ) { - pCS->bAtomUsedForStereo[at_to1] --; - pCS->bAtomUsedForStereo[at_to2] --; - } - ret2 = map_stereo_bonds4 ( at, num_atoms, num_at_tg, num_max, bAllene, nCanonRankFrom, nAtomNumberCanonFrom, nCanonRankTo, - nSymmRank, pRankStack1+nStackPtr[istk], pRankStack2+nStackPtr[istk], - nTempRank, nNumMappedRanks[istk], nSymmStereo, NeighList, - pCS, cur_tree, nNumMappedBonds+1 , - vABParityUnknown); - if ( !bAllParitiesIdentical ) { - pCS->bAtomUsedForStereo[at_to1] ++; - pCS->bAtomUsedForStereo[at_to2] ++; - } - pCS->bRankUsedForStereo[at_from1] --; - pCS->bRankUsedForStereo[at_from2] --; - if ( ret2 == 4 ) { - if ( nNumMappedBonds ) { - return ret2; - } else { - pCS->bFirstCT = 1; - goto total_restart; - } - } - - if ( RETURNED_ERROR(ret2) ) { - if ( ret2 == CT_TIMEOUT_ERR ) - return ret2; - else - return ret2; /* program error */ - } - if ( ret2 > 0 ) { - nTotSuccess |= 1; - nNumAtTo1Success ++; - if ( bStereoIsBetterWasSetHere || (ret2 & 2) ) { - CurTreeKeepLastAtomsOnly( cur_tree, tpos1, 1 ); /* start over */ - nTotSuccess |= 2; /* Obtained a smaller CT */ - } - } else { - if ( bStereoIsBetterWasSetHere ) { /* rollback */ - pCS->bStereoIsBetter = 0; - pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond2; - } - /* remove failed atom2 from the tree */ - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon2 ) ) { - CurTreeRemoveIfLastAtom( cur_tree, at_to2 ); - CurTreeRemoveLastRankIfNoAtoms( cur_tree ); - } - /* - if ( 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { - CurTreeRemoveLastAtom( cur_tree, at_to1 ); - CurTreeRemoveLastRankIfNoAtoms( cur_tree ); - } - */ - } - bStereoIsBetterWasSetHere = 0; - } - if ( bAllParitiesIdentical || bAllParitiesIdentical2 ) { - break; /* j2 cycle, at_to2 (no need to repeat) */ - } - continue; /* to next at_to2 */ - } - /*************************************************************************** - * - * Unknown Bond Parity case: may need to map stereo bond neighbors - * - **************************************************************************** - * Ranks are not known in advance - * check if at_from1/at_to1 half-bond has neighbors with equal mapping ranks - */ - - parity1 = parity_of_mapped_half_bond( at_from1, at_to1, at_from2, at_to2, at, &EN1[0], - nCanonRankFrom, pRankStack1[nStackPtr[istk]], pRankStack2[nStackPtr[istk]] ); - /* old approach -- before E/Z parities - parity1 = parity_of_mapped_atom2( at_from1, at_to1, at, &EN1[0], - nCanonRankFrom, pRankStack1[nStackPtr[istk]], pRankStack2[nStackPtr[istk]] ); - */ - /* the following commented out statement is not needed here. */ - /* parity2 = parity_of_mapped_atom2( at_from2, at_to2, at, &EN2[0], - nCanonRankFrom, pRankStack1[nStackPtr[istk]], - pRankStack2[nStackPtr[istk]] ); - */ - if ( !parity1 ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - num1 = parity1 > 0? 1:2; /* parity < 0 means additional mapping is needed to set parity */ - /* --- try all possible mappings of the stereo bond ending atoms' neighbors --- */ - at_rank_canon_n1 = 0; - at_rank_canon_n2 = 0; - for ( i = 0; i < num1; i ++ ) { - int at_from_n1, at_to_n1, at_no_n1_num_success = 0; - istk2 = istk; - if ( num1 == 2 ) { - at_rank_canon_n1 = nCanonRankFrom[EN1[0].from_at]; - /* an additional neighbor mapping is necessary; */ - /* we need to map only one at_from1 neighbor to make all neighbors have different ranks */ - - at_from_n1 = EN1[0].from_at; - at_to_n1 = EN1[0].to_at[i]; - - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n1 ) && - 1 == CurTreeIsLastAtomEqu( cur_tree, at_to_n1, nSymmStereo ) ) - continue; - /* - if ( nSymmStereo && !pCS->bFirstCT ) { - if ( i && nSymmStereo[at_to_n1] == nSymmStereo[(int)EN1[0].to_at[0]] ) { - continue; // do not test stereo equivalent atoms except the first one - } - } - */ - /* neighbors are tied. Untie them by breaking a tie on ONE of them. */ - ret1 = map_an_atom2( num_at_tg, num_max, at_from_n1, at_to_n1, - nTempRank, nNumMappedRanks[istk2], &nNumMappedRanks[istk2+1], pCS, - NeighList, pRankStack1+nStackPtr[istk2], pRankStack2+nStackPtr[istk2], - &bAddStack ); - if ( RETURNED_ERROR(ret1) ) { - return ret1; /* program error */ /* */ - } - nStackPtr[istk2+1] = nStackPtr[istk2] + bAddStack; - istk2 ++; /* <= 3 */ - /* debug */ - if ( istk2 >= SB_DEPTH ) { - return CT_OVERFLOW; /* program error */ /* */ - } - if ( bAddStack ) { - if ( tpos1 == CurTreeGetPos( cur_tree ) || - 0 == CurTreeIsLastRank( cur_tree, at_rank_canon_n1 ) ) { - CurTreeAddRank( cur_tree, at_rank_canon_n1 ); - } - CurTreeAddAtom( cur_tree, at_to_n1 ); - } - - - /* now that all at_from1 neighbors have been mapped the parity must be defined */ - parity1 = parity_of_mapped_half_bond( at_from1, at_to1, at_from2, at_to2, at, &EN1[1], - nCanonRankFrom, pRankStack1[nStackPtr[istk2]], pRankStack2[nStackPtr[istk2]] ); - if ( parity1 <= 0 ) - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } else { - nNumMappedRanks[istk2+1] = nNumMappedRanks[istk2]; - nStackPtr[istk2+1] = nStackPtr[istk2]; - istk2 ++; /* <= 3 */ - } - - /* check if at_from2/at_to2 half-bond has neighbors with equal mapping ranks */ - parity2 = parity_of_mapped_half_bond( at_from2, at_to2, at_from1, at_to1, at, &EN2[0], - nCanonRankFrom, pRankStack1[nStackPtr[istk2]], pRankStack2[nStackPtr[istk2]] ); - if ( !parity2 ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - num2 = parity2 > 0? 1:2; - at_rank_canon_n2 = 0; - for ( j = 0; j < num2; j ++ ) { - int at_from_n2, at_to_n2; - istk3 = istk2; - if ( num2 == 2 ) { - at_rank_canon_n2 = nCanonRankFrom[EN2[0].from_at]; - /* we need to map only one at_from2 neighbor to make its neighbors have different ranks */ - at_from_n2 = EN2[0].from_at; - at_to_n2 = EN2[0].to_at[j]; - - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n2 ) && - 1 == CurTreeIsLastAtomEqu( cur_tree, at_to_n2, nSymmStereo ) ) - continue; - - /* - if ( nSymmStereo && !pCS->bFirstCT ) { - if ( j && nSymmStereo[at_to_n2] == nSymmStereo[(int)EN2[0].to_at[0]] ) { - continue; // do not test stereo equivalent atoms except the first one - } - } - */ - /* neighbors are tied. Untie them by breaking a tie on ONE of them. */ - ret1 = map_an_atom2( num_at_tg, num_max, at_from_n2, at_to_n2, - nTempRank, nNumMappedRanks[istk3], &nNumMappedRanks[istk3+1], pCS, - NeighList, pRankStack1+nStackPtr[istk3], - pRankStack2+nStackPtr[istk3], - &bAddStack ); - if ( RETURNED_ERROR(ret1) ) { - return ret1; /* program error */ - } - nStackPtr[istk3+1] = nStackPtr[istk3]+bAddStack; - istk3 ++; /* <= 4 */ - - if ( bAddStack ) { - if ( tpos1 == CurTreeGetPos( cur_tree ) || - 0 == CurTreeIsLastRank( cur_tree, at_rank_canon_n2 ) ) { - CurTreeAddRank( cur_tree, at_rank_canon_n2 ); - } - CurTreeAddAtom( cur_tree, at_to_n2 ); - } - - parity2 = parity_of_mapped_half_bond( at_from2, at_to2, at_from1, at_to1, at, &EN2[1], - nCanonRankFrom, pRankStack1[nStackPtr[istk3]], pRankStack2[nStackPtr[istk3]] ); - if ( parity2 <= 0 ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - } else { - /* no additional mapping is needed to set atom's parity */ - nNumMappedRanks[istk3+1] = nNumMappedRanks[istk3]; - nStackPtr[istk3+1] = nStackPtr[istk3]; - istk3 ++; /* <= 4 */ - } - - /******************************************************************* - * at this point the stereo bond is fully mapped to find its parity - *******************************************************************/ - - if ( parity1 <= 0 || parity2 <= 0 ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - - /* find current bond parity AB_PARITY_ODD */ - if ( ATOM_PARITY_WELL_DEF(parity1) && ATOM_PARITY_WELL_DEF(parity2) ) { - bond_parity = 2 - (parity1 + parity2)%2; - } else { - bond_parity = inchi_max(parity1, parity2); - } - if ( ATOM_PARITY_WELL_DEF(bond_parity) && at[at_to1].stereo_bond_z_prod[j2] < 0 ) - bond_parity = 2 - (bond_parity+1)%2; /* invert the bond parity */ - - - /******************************************************** - * make a decision whether to accept the current mapping - */ - c = CompareLinCtStereoDoubleToValues( pCS->LinearCTStereoDble+nNumMappedBonds, - at_rank_canon1, at_rank_canon2, (U_CHAR)bond_parity ); - if ( sb_parity_calc != bond_parity || - c < 0 && !pCS->bStereoIsBetter ) { - /* reject */ - pCS->lNumRejectedCT ++; - /* remove failed atom2 from the tree */ - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n2 ) ) { - CurTreeRemoveIfLastAtom( cur_tree, at_to_n2 ); - CurTreeRemoveLastRankIfNoAtoms( cur_tree ); - } - continue; /* Reject: not a minimal CT. */ - - } else { - - /* try to accept */ - - if ( c > 0 && !pCS->bStereoIsBetter ) { - /* bond_parity is less than the previusly found */ - pCS->bStereoIsBetter = bStereoIsBetterWasSetHere = 1; - prevBond2 = pCS->LinearCTStereoDble[nNumMappedBonds]; - } - /* accept */ - pCS->LinearCTStereoDble[nNumMappedBonds].at_num1 = at_rank_canon1; - pCS->LinearCTStereoDble[nNumMappedBonds].at_num2 = at_rank_canon2; - pCS->LinearCTStereoDble[nNumMappedBonds].parity = bond_parity; - /* recursive call */ - pCS->bRankUsedForStereo[at_from1] ++; - pCS->bRankUsedForStereo[at_from2] ++; - pCS->bAtomUsedForStereo[at_to1] --; - pCS->bAtomUsedForStereo[at_to2] --; - ret2 = map_stereo_bonds4 ( at, num_atoms, num_at_tg, num_max, bAllene, nCanonRankFrom, nAtomNumberCanonFrom, nCanonRankTo, - nSymmRank, pRankStack1+nStackPtr[istk3], pRankStack2+nStackPtr[istk3], - nTempRank, nNumMappedRanks[istk3], nSymmStereo, NeighList, - pCS, cur_tree, nNumMappedBonds+1 , - vABParityUnknown); - pCS->bRankUsedForStereo[at_from1] --; - pCS->bRankUsedForStereo[at_from2] --; - pCS->bAtomUsedForStereo[at_to1] ++; - pCS->bAtomUsedForStereo[at_to2] ++; - if ( ret2 == 4 ) { - if ( nNumMappedBonds ) { - return ret2; - } else { - pCS->bFirstCT = 1; - goto total_restart; - } - } - if ( RETURNED_ERROR(ret2) ) { - if ( ret2 == CT_TIMEOUT_ERR ) - return ret2; - else - return ret2; /* program error */ - } - if ( ret2 > 0 ) { - nTotSuccess |= 1; - nNumAtTo1Success ++; - if ( bStereoIsBetterWasSetHere || (ret2 & 2) ) { - CurTreeKeepLastAtomsOnly( cur_tree, tpos1, 1 ); /* start over */ - nTotSuccess |= 2; /* Obtained a smaller CT */ - } - at_no_n1_num_success ++; - } else { - if ( bStereoIsBetterWasSetHere ) { /* rollback */ - pCS->bStereoIsBetter = 0; - pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond2; - } - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n2 ) ) { - CurTreeRemoveIfLastAtom( cur_tree, at_to_n2 ); - CurTreeRemoveLastRankIfNoAtoms( cur_tree ); - } - } - bStereoIsBetterWasSetHere = 0; - } - } /* end choices in mapping neighbors of the 2nd half-bond */ - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n2 ) ) { - CurTreeRemoveLastRank( cur_tree ); - } - /* added 2006-07-20 */ - if ( !at_no_n1_num_success && tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n1 ) ) { - CurTreeRemoveIfLastAtom( cur_tree, at_to_n1 ); - } - - } /* end choices in mapping neighbors of the 1st half-bond */ - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n1 ) ) { - CurTreeRemoveLastRank( cur_tree ); - } - } /* end of choices in mapping at_from2 */ - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon2 ) ) { - CurTreeRemoveLastRank( cur_tree ); - } - if ( !nNumAtTo1Success ) { - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { - CurTreeRemoveIfLastAtom( cur_tree, at_to1 ); - CurTreeRemoveLastRankIfNoAtoms( cur_tree ); - } - } - if ( bAllParitiesIdentical /*&& !nSymmStereo*/ ) { - break; - } - } /* end of choices in mapping at_from1 */ - - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { - CurTreeRemoveLastRank( cur_tree ); - } else - /* CurTree consistecy check (debug only) */ - if ( tpos1 != CurTreeGetPos( cur_tree ) ) { - return CT_STEREOCOUNT_ERR; /* */ - } - - if ( !nTotSuccess || stereo_bond_parity == sb_parity_calc ) { - goto repeat_all; /* repeat with next parity if no success or with the same parity, now known */ - } - - /* Previously the control flow never came here... */ - if ( !nTotSuccess ) { - pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond; - CurTreeSetPos( cur_tree, tpos1 ); - /* Occurs when atoms are not really equvalent ( -O= without positive charge in "aromatic" ring) */ - return 0; /* Happens for ID=92439,100318,100319 when EXCL_ALL_AROM_BOND_PARITY=0 and - * nNumChoices=0. - * Results from impossible previous mapping of symmetric relatively - * to a central ring aromatic circles while central ring is not symmetrical due to - * alternate bonds (in the central ring number of pi-electrons, atoms and bonds - * are symmetrical). - * Does not happen when alternate bonds of the central ring - * are treated as aromatic by attaching a (+) charge to the oxygen. - */ - } - } else - - { - int ret; - - if ( !nNumMappedBonds ) { - pCS->bStereoIsBetter = 0; /* the first stereo feature in the canonical CT has not been processed yet */ - } - - if ( nNumMappedBonds < pCS->nLenLinearCTStereoDble ) { - prevBond = pCS->LinearCTStereoDble[nNumMappedBonds]; - } - - /* all stereo bonds have been mapped; now start processing stereo atoms... */ - ret = map_stereo_atoms4 ( at, num_atoms, num_at_tg, num_max, nCanonRankFrom, nAtomNumberCanonFrom, nCanonRankTo, - nSymmRank, pRankStack1, pRankStack2, nTempRank, nNumMappedRanksInput, - nSymmStereo, NeighList, pCS, cur_tree, 0 , vABParityUnknown); - if ( ret == 4 ) { - if ( nNumMappedBonds ) { - return ret; - } else { - pCS->bFirstCT = 1; - goto total_restart; - } - } - if ( RETURNED_ERROR(ret) ) { - if ( ret == CT_TIMEOUT_ERR ) - return ret; - else - return ret; /* program error */ - } - if ( ret > 0 ) { - nTotSuccess |= 1; - if ( ret & 2 ) { - CurTreeKeepLastAtomsOnly( cur_tree, tpos1, 1 ); /* start over */ - nTotSuccess |= 2; /* Obtained a smaller CT */ - } - } - } - if ( !nTotSuccess && pCS->nLenLinearCTStereoDble && - nNumMappedBonds < pCS->nLenLinearCTStereoDble ) { - pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond; - } - return nTotSuccess; /* ok */ -} - - - - - - - -/**************************************************************************************** - */ -int map_stereo_atoms4 ( - sp_ATOM *at, int num_atoms, int num_at_tg, int num_max, - const AT_RANK *nCanonRankFrom, const AT_RANK *nAtomNumberCanonFrom, AT_RANK *nCanonRankTo, /* canonical numbering to be mapped */ - const AT_RANK *nSymmRank, AT_RANK **pRankStack1/*from*/, AT_RANK **pRankStack2/*to*/, - AT_RANK *nTempRank, int nNumMappedRanksInput, - AT_RANK *nSymmStereo, NEIGH_LIST *NeighList, - CANON_STAT *pCS, CUR_TREE *cur_tree, int nNumMappedAtoms , - int vABParityUnknown ) -{ -/* - * Do not check whether "from" atoms have any stereo features. - */ - int nTotSuccess = 0; - AT_STEREO_CARB prevAtom; - int tpos1; - - tpos1 = CurTreeGetPos( cur_tree ); - - if ( nNumMappedAtoms < pCS->nLenLinearCTStereoCarb ) { - /* AT_RANK *nRankFrom=*pRankStack1++, AT_RANK *nAtomNumberFrom=pRankStack1++; */ - /* AT_RANK *nRankTo =*pRankStack2++, AT_RANK *nAtomNumberTo =pRankStack2++; */ - int j1, at_from1, at_to1, /*at_from2, at_to2,*/ iMax, lvl, bStereoIsBetterWasSetHere; - int istk, istk2, bAddStack, nNumAtTo1Success, c, bFirstTime=1, bAllParitiesIdentical; - EQ_NEIGH EN[5], *pEN; - int nStackPtr[5], nMappedRanks[5], j[5], *nSP, *nMR, bLastLvlFailed; - - AT_RANK at_rank_canon1, cr[5], at_to[5]; - AT_RANK canon_rank1_min = 0; - int at_rank1; /* rank for mapping */ - int nNumChoices, nNumUnkn, nNumUndf, nNumWorse, nNumBest, nNumCalc; - int stereo_center_parity, prev_stereo_center_parity, sb_parity_calc, pass; - AT_STEREO_CARB prevAtom2; - - prevAtom = pCS->LinearCTStereoCarb[nNumMappedAtoms]; /* save to restore in case of failure */ - at_rank_canon1 = nNumMappedAtoms? pCS->LinearCTStereoCarb[nNumMappedAtoms-1].at_num:0; - - goto bypass_next_canon_rank_check; - -next_canon_rank: - - if ( !pCS->bStereoIsBetter /*??? && !pCS->bFirstCT ???*/ && - at_rank_canon1 >= pCS->LinearCTStereoCarb[nNumMappedAtoms].at_num) { - /* cannot find next available canonical number */ - if ( !nTotSuccess ) { - pCS->LinearCTStereoCarb[nNumMappedAtoms] = prevAtom; /* restore because of failure */ - } - CurTreeSetPos( cur_tree, tpos1 ); - return nTotSuccess; - } - -bypass_next_canon_rank_check: - - CurTreeSetPos( cur_tree, tpos1 ); - - /* find next available canon. number for a stereogenic atom */ - if ( !Next_SC_At_CanonRank2( &at_rank_canon1, &canon_rank1_min, &bFirstTime, - pCS->bAtomUsedForStereo, pRankStack1, pRankStack2, - nAtomNumberCanonFrom, num_atoms ) || - !pCS->bStereoIsBetter && - at_rank_canon1 > pCS->LinearCTStereoCarb[nNumMappedAtoms].at_num) { - /* cannot find next available canonical number */ - if ( !nTotSuccess ) { - pCS->LinearCTStereoCarb[nNumMappedAtoms] = prevAtom; /* restore because of failure */ - } - return nTotSuccess; - } - - nNumChoices = 0; - nNumUnkn = 0; - nNumUndf = 0; - nNumBest = 0; - nNumWorse = 0; - nNumCalc = 0; - pass = 0; - prev_stereo_center_parity = 0; - - /* get mapping rank for the canon. number */ - at_rank1 = pRankStack1[0][at_from1=(int)nAtomNumberCanonFrom[at_rank_canon1 - 1]]; - iMax = at_rank1-1; - /* for debug only */ - if ( at_rank1 != pRankStack2[0][pRankStack2[1][at_rank1-1]] ) - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - - /* count special parities of the not mapped yet "to" atoms */ - for ( j1 = 0; j1 <= iMax && at_rank1 == pRankStack2[0][at_to1 =pRankStack2[1][iMax-j1]]; j1 ++ ) { - if ( !at[at_to1].stereo_bond_neighbor[0] && pCS->bAtomUsedForStereo[at_to1] == STEREO_AT_MARK ) { - int no_choice = 0; - stereo_center_parity = PARITY_VAL(at[at_to1].stereo_atom_parity); - switch(stereo_center_parity) { - - case AB_PARITY_UNDF: nNumUndf ++; break; /* 4 */ - - case AB_PARITY_UNKN: nNumUnkn ++; - break; /* 3 */ - - case BEST_PARITY: nNumBest ++; break; /* 1 */ - case WORSE_PARITY: nNumWorse ++; break; /* 2 */ - case AB_PARITY_CALC: nNumCalc ++; break; - case AB_PARITY_NONE: no_choice ++; break; /* 0 */ - } - nNumChoices += !no_choice; - } - } - if ( nNumChoices != nNumCalc + nNumUndf + nNumUnkn + nNumBest + nNumWorse ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - if ( !nNumChoices ) { - goto next_canon_rank; - } - /* Determine the first parity to search */ - sb_parity_calc = ( nNumCalc > 0 )? BEST_PARITY : 0; - - /* ============================================================== - Search sequence: sb_parity_calc stereo_center_parity - ============================================================== - BEST_PARITY (calc) BEST_PARITY BEST_PARITY - BEST_PARITY (known) BEST_PARITY WORSE_PARITY or 0 - WORSE_PARITY (calc) WORSE_PARITY WORSE_PARITY - WORSE_PARITY (known) WORSE_PARITY 0 - AB_PARITY_UNKN(known) AB_PARITY_UNKN 0 - AB_PARITY_UNDF(known) AB_PARITY_UNDF 0 - - if (sb_parity_calc==stereo_center_parity) then "calc" else "known" - */ - -repeat_all: - - if ( !pass ++ ) { - /* select the smallest parity to search */ - if ( sb_parity_calc ) { - stereo_center_parity = BEST_PARITY; - } else { - stereo_center_parity = nNumBest? BEST_PARITY : - nNumWorse? WORSE_PARITY : - nNumUnkn? AB_PARITY_UNKN : - nNumUndf? AB_PARITY_UNDF : AB_PARITY_NONE; - } - } else { - prev_stereo_center_parity = stereo_center_parity; - j1 = NextStereoParity2Test( &stereo_center_parity, &sb_parity_calc, - nNumBest, nNumWorse, nNumUnkn, nNumUndf, nNumCalc, - vABParityUnknown ); - switch ( j1 ) { - case 0: - break; /* obtained next parity to test */ - case 1: - goto next_canon_rank; - default: - return j1; /* program error */ - } - } - if ( stereo_center_parity == AB_PARITY_NONE ) { - /* error? */ - return CT_STEREOCOUNT_ERR; /* */ - } - /* check if the new requested parity is small enough */ - if ( !pCS->bStereoIsBetter ) { - c = CompareLinCtStereoAtomToValues( nTotSuccess? pCS->LinearCTStereoCarb+nNumMappedAtoms : &prevAtom, - at_rank_canon1, (U_CHAR)stereo_center_parity ); - if ( c < 0 ) { - if ( !nTotSuccess ) { - pCS->LinearCTStereoCarb[nNumMappedAtoms] = prevAtom; - } - CurTreeSetPos( cur_tree, tpos1 ); - return nTotSuccess; - } - } - - - bAllParitiesIdentical = 0; - bStereoIsBetterWasSetHere = 0; - istk = istk2 = 0; - CurTreeSetPos( cur_tree, tpos1 ); /* start over */ - /* - if ( prev_stereo_center_parity != stereo_center_parity ) { - CurTreeSetPos( cur_tree, tpos1 ); - } - */ /* nRankTo nAtomNumberTo */ - for ( j1 = 0; j1 <= iMax && at_rank1 == pRankStack2[0][at_to1 =pRankStack2[1][iMax-j1]]; j1 ++ ) { - int ret, ret1, ret2, parity1; - nNumAtTo1Success = 0; - /* - if ( !(at[at_to1].stereo_atom_parity && !at[at_to1].stereo_bond_neighbor[0] && - pCS->bAtomUsedForStereo[at_to1] == STEREO_AT_MARK ) ) - */ - if ( !at[at_to1].stereo_atom_parity || at[at_to1].stereo_bond_neighbor[0] || - pCS->bAtomUsedForStereo[at_to1] != STEREO_AT_MARK ) /* simplify 12-17-2003 */ - continue; - /* Do not map on non-stereogenic atom constitutionally - * equivalent to a steregenic atom. Here - * at[at_to1] is not a sterereo center; | | - * bonds tautomerism is a usual cause. -P(+)-CH=P- - * For example, consider a fragment: | | - * The two atoms P may be constitutionally - * equivalent, P(+) may be seen as a stereocenter - * while another P has a double bond (Now such a P(V) IS a stereocenter). - */ - /* check whether the stereocenter parity corresponds to the requested stereocenter parity */ - if ( PARITY_KNOWN(at[at_to1].stereo_atom_parity) ) { - if ( stereo_center_parity == sb_parity_calc ) { - continue; /* requested parity to be calculated, found known parity */ - } - if ( stereo_center_parity != PARITY_VAL(at[at_to1].stereo_atom_parity) ) { - continue; /* parity differs from the requested parity */ - } - } else - if ( PARITY_CALCULATE( at[at_to1].stereo_atom_parity) ) { - if ( stereo_center_parity != sb_parity_calc ) { - continue; /* requested known parity, found patity to be calculated */ - } - } else { - return CT_STEREOCOUNT_ERR; /* unknown parity type */ - } - - bAllParitiesIdentical = (( at[at_to1].stereo_atom_parity & KNOWN_PARITIES_EQL ) && - PARITY_KNOWN(at[at_to1].stereo_atom_parity)); - - if ( !bAllParitiesIdentical && !nNumCalc && - (!nNumUndf + !nNumUnkn + !nNumBest + !nNumWorse)==3 ) { - /* only one kind of stereocenter parity is present; check whether all parities are really same */ - bAllParitiesIdentical = All_SC_Same( at_rank_canon1, /* canonical number */ - pRankStack1, pRankStack2, - nAtomNumberCanonFrom, at ); - if ( bAllParitiesIdentical < 0 ) { - return CT_STEREOCOUNT_ERR; - } - } - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) && - 1 == CurTreeIsLastAtomEqu( cur_tree, at_to1, nSymmStereo ) ) - continue; - - /* initialize stack pointer nStackPtr[istk] for "hand-made" recursion */ - /* stacks are pRankStack1[], pRankStack2[], nNumMappedRanks[] */ - istk = 0; - nStackPtr[istk] = 0; - nMappedRanks[istk] = nNumMappedRanksInput; - bAddStack = 0; - /* if all equivalent atoms have same known parity, do not map any of them here */ - if ( !bAllParitiesIdentical ) { - /* map the central atom */ - /* this mapping is always possible */ - ret1 = map_an_atom2( num_at_tg, num_max, at_from1, at_to1, - nTempRank, nMappedRanks[istk], &nMappedRanks[istk+1], pCS, - NeighList, pRankStack1+nStackPtr[istk], pRankStack2+nStackPtr[istk], - &bAddStack ); - if ( RETURNED_ERROR(ret1) ) { - return ret1; /* error */ - } - nStackPtr[istk+1] = nStackPtr[istk] + bAddStack; - istk ++; - } else { - ClearPreviousMappings( pRankStack1+2 ); /* precaution */ - } - - /********************************************************************************* - * - * Unknown Stereocenter Parity case: possibly need to map stereo center neighbors - */ - if ( stereo_center_parity == sb_parity_calc ) - { - /* find out the parity */ - parity1 = parity_of_mapped_atom2( at_from1, at_to1, at, &EN[istk], - nCanonRankFrom, pRankStack1[nStackPtr[istk]], - pRankStack2[nStackPtr[istk]] ); - /* if parity is well-defined then returned EN[istk].num_to=0 */ - if ( !parity1 ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - if ( !EN[istk].num_to && parity1 != sb_parity_calc ) { - continue; /* looking for the parity value = sb_parity_calc */ - } - - } else { - /* Known parity */ - parity1 = stereo_center_parity; - EN[istk].num_to = 0; - } - - /*********************************************************************** - * no need to map the neighbors: parity is known or has been calculated - */ - if ( stereo_center_parity == sb_parity_calc && !EN[istk].num_to || - /* now well-defined, but unknown in advance atom parity OR */ - stereo_center_parity != sb_parity_calc ) - /* known in advance parity = stereo_center_parity */ - { - /* do not need to map the neighbors */ - c = CompareLinCtStereoAtomToValues( pCS->LinearCTStereoCarb+nNumMappedAtoms, - at_rank_canon1, (U_CHAR)parity1 ); - if ( c < 0 && !pCS->bStereoIsBetter ) { - /* reject */ - pCS->lNumRejectedCT ++; - continue; /* Reject: not a minimal CT. Should not happen */ - } else { - /* accept */ - - if ( bAddStack ) { - if ( tpos1 == CurTreeGetPos( cur_tree ) || - 0 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { - CurTreeAddRank( cur_tree, at_rank_canon1 ); - } - CurTreeAddAtom( cur_tree, at_to1 ); - } - - if ( c > 0 && !pCS->bStereoIsBetter ) { - /* stereo center entry is less than the previusly found */ - pCS->bStereoIsBetter = bStereoIsBetterWasSetHere = 1; - prevAtom2 = pCS->LinearCTStereoCarb[nNumMappedAtoms]; - } - pCS->LinearCTStereoCarb[nNumMappedAtoms].parity = parity1; - pCS->LinearCTStereoCarb[nNumMappedAtoms].at_num = at_rank_canon1; - pCS->bRankUsedForStereo[at_from1] = 3; -#if ( FIX_ChCh_STEREO_CANON_BUG == 1 ) - if ( !bAllParitiesIdentical ) -#endif - pCS->bAtomUsedForStereo[at_to1] -= STEREO_AT_MARK; - - ret = map_stereo_atoms4 ( at, num_atoms, num_at_tg, num_max, nCanonRankFrom, nAtomNumberCanonFrom, nCanonRankTo, - nSymmRank, pRankStack1+nStackPtr[istk], pRankStack2+nStackPtr[istk], - nTempRank, nMappedRanks[istk], nSymmStereo, NeighList, - pCS, cur_tree, nNumMappedAtoms+1 , - vABParityUnknown); - pCS->bRankUsedForStereo[at_from1] = 0; -#if ( FIX_ChCh_STEREO_CANON_BUG == 1 ) - if ( !bAllParitiesIdentical ) -#endif - pCS->bAtomUsedForStereo[at_to1] += STEREO_AT_MARK; - if ( ret == 4 ) { - return ret; - } - if ( RETURNED_ERROR(ret) ) { - if ( ret == CT_TIMEOUT_ERR ) - return ret; - else - return ret; /* program error */ - } - if ( ret > 0 ) { - nTotSuccess |= 1; - nNumAtTo1Success ++; - if ( bStereoIsBetterWasSetHere || (ret & 2) ) { - CurTreeKeepLastAtomsOnly( cur_tree, tpos1, 1 ); /* start over */ - nTotSuccess |= 2; /* Obtained a smaller CT */ - } - } else { - if ( bStereoIsBetterWasSetHere ) { - pCS->bStereoIsBetter = 0; - pCS->LinearCTStereoCarb[nNumMappedAtoms] = prevAtom2; - } - /* remove failed atom1 from the tree */ - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { - CurTreeRemoveIfLastAtom( cur_tree, at_to1 ); - CurTreeRemoveLastRankIfNoAtoms( cur_tree ); - } - - } - bStereoIsBetterWasSetHere = 0; - } - /* - if ( (at[at_to1].stereo_atom_parity & KNOWN_PARITIES_EQL ) && - ATOM_PARITY_KNOWN(stereo_center_parity) && !nSymmStereo ) { // ??? add && !nSymmStereo ??? - break; // do not repeat for the same kind of stereo atom with the parity known in advance - } - */ - if ( bAllParitiesIdentical ) { - break; /* do not repeat for the same kind of stereo atom with the parity known in advance */ - } - continue; - - } - - /*************************************************** - * - * Need to map the neighbors - */ - if ( stereo_center_parity != sb_parity_calc ) { - return CT_STEREOCOUNT_ERR; /* program error */ /* */ - } - /* -- has already been calculated -- - parity1 = parity_of_mapped_atom2( at_from1, at_to1, at, &EN[istk], - nCanonRankFrom, pRankStack1[nStackPtr[istk]], pRankStack2[nStackPtr[istk]] ); - */ - if ( !parity1 ) { - return CT_STEREOCOUNT_ERR; /* 1/25/2002 */ /* */ - } - - if ( bAddStack ) { - if ( tpos1 == CurTreeGetPos( cur_tree ) || - 0 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { - CurTreeAddRank( cur_tree, at_rank_canon1 ); - } - CurTreeAddAtom( cur_tree, at_to1 ); - } - /****************************************************** - * Need to fix the neighbors to define the atom parity - ******************************************************/ - /* a recursion replaced with the hand-made stack */ - lvl = 0; /* the "recursion" depth level */ - nSP = &nStackPtr[istk]; - nMR = &nMappedRanks[istk]; - pEN = &EN[istk]; - bLastLvlFailed = 0; - - /* entering "recursion" depth level lvl */ -next_lvl: - if ( pEN[lvl].num_to ) { - /* Found tied neighbors. Try all transpositions of the tied neighbors. - * j is a number of the "to" tied neighbor in the pEN[lvl].to_at[*] to - * which the pEN[lvl].from_at "from" neighbor's canonical number is mapped - */ - j[lvl] = 0; -next_j: - cr[lvl] = nCanonRankFrom[pEN[lvl].from_at]; - at_to[lvl] = pEN[lvl].to_at[j[lvl]]; - if ( j[lvl] && tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, cr[lvl] ) && - 1 == CurTreeIsLastAtomEqu( cur_tree, at_to[lvl], nSymmStereo ) ) { - lvl ++; - bLastLvlFailed = 0; - goto backup; /* do not test stereo equivalent atoms except the first one */ - } - - ret2 = map_an_atom2( num_at_tg, num_max, - pEN[lvl].from_at, /* from */ - pEN[lvl].to_at[j[lvl]], /* to */ - nTempRank, nMR[lvl], &nMR[lvl+1], pCS, - NeighList, pRankStack1+nSP[lvl], pRankStack2+nSP[lvl], - &bAddStack ); - if ( RETURNED_ERROR(ret2) ) { - return ret2; /* program error */ - } - - /* next recursion depth level */ - if ( bAddStack ) { - if ( tpos1 == CurTreeGetPos( cur_tree ) || - 0 == CurTreeIsLastRank( cur_tree, cr[lvl] ) ) { - CurTreeAddRank( cur_tree, cr[lvl] ); - } - CurTreeAddAtom( cur_tree, at_to[lvl] ); - } - nSP[lvl+1] = nSP[lvl] + bAddStack; - lvl ++; /* upon increment lvl = number of additionally mapped neighbors - * (entering next recursion level) */ - /* check if the mapping has defined the parity */ - parity1 = parity_of_mapped_atom2( at_from1, at_to1, at, &pEN[lvl], - nCanonRankFrom, pRankStack1[nSP[lvl]], pRankStack2[nSP[lvl]] ); - if ( !parity1 ) { - return CT_STEREOCOUNT_ERR; /* 1/25/2002 */ /* */ - } - if ( parity1 < 0 ) { - goto next_lvl; /* we need at least one more mapping to define the parity */ - } - - /********************************************************** - * - * Check the parity - * - ********************************************************** - * make a decision whether to accept the current mapping */ - - c = CompareLinCtStereoAtomToValues( pCS->LinearCTStereoCarb+nNumMappedAtoms, - at_rank_canon1, (U_CHAR)parity1 ); - if ( sb_parity_calc != parity1 || - c < 0 && !pCS->bStereoIsBetter ) { - pCS->lNumRejectedCT ++; - bLastLvlFailed = 1; - } else - /* the parity has been defined (all neighbors have untied ranks) */ - /* if ( bAcceptAllParities || parity1 == BEST_PARITY ) */ - { - /********************************************************************* - * - * Process the parity here. We are at the top of the recursion stack. - * - *********************************************************************/ - /* try to accept current neighbors mapping */ - if ( c > 0 && !pCS->bStereoIsBetter ) { - pCS->bStereoIsBetter = bStereoIsBetterWasSetHere = 1; - prevAtom2 = pCS->LinearCTStereoCarb[nNumMappedAtoms]; - } - pCS->LinearCTStereoCarb[nNumMappedAtoms].parity = parity1; - pCS->LinearCTStereoCarb[nNumMappedAtoms].at_num = at_rank_canon1; - pCS->bRankUsedForStereo[at_from1] = 3; - pCS->bAtomUsedForStereo[at_to1] -= STEREO_AT_MARK; - - ret = map_stereo_atoms4 ( at, num_atoms, num_at_tg, num_max, nCanonRankFrom, nAtomNumberCanonFrom, nCanonRankTo, - nSymmRank, pRankStack1+nSP[lvl], pRankStack2+nSP[lvl], - nTempRank, nMR[lvl], nSymmStereo, NeighList, - pCS, cur_tree, nNumMappedAtoms+1 , - vABParityUnknown ); - pCS->bRankUsedForStereo[at_from1] = 0; - pCS->bAtomUsedForStereo[at_to1] += STEREO_AT_MARK; - if ( ret == 4 ) { - return ret; - } - if ( RETURNED_ERROR(ret) ) { - if ( ret == CT_TIMEOUT_ERR ) - return ret; - else - return ret; /* program error */ - } - if ( ret > 0 ) { - nTotSuccess |= 1; - nNumAtTo1Success ++; - if ( bStereoIsBetterWasSetHere || (ret & 2) ) { - CurTreeKeepLastAtomsOnly( cur_tree, tpos1, 1 ); /* start over */ - nTotSuccess |= 2; /* Obtained a smaller CT */ - } - } else { - if ( bStereoIsBetterWasSetHere ) { - pCS->bStereoIsBetter = 0; - pCS->LinearCTStereoCarb[nNumMappedAtoms] = prevAtom2; - } - bLastLvlFailed = 1; - } - bStereoIsBetterWasSetHere = 0; - - /* avoid redundant repetitions: */ - /* check if neighbors mappings have altered another stereo center parity */ - if ( !nSymmStereo && !might_change_other_atom_parity( at, num_atoms, at_to1, - pRankStack2[nSP[lvl]] /* ranks after neigbors mapping */, - pRankStack2[nStackPtr[istk]] /* ranks before the mapping neighbors */) ) { - goto done; - } - } - /* Continue the cycle. Go to the previous "recursion" level */ -backup: - while (lvl -- > 0 ) { - - j[lvl] ++; /* next neighbor at this level */ - if ( j[lvl] < pEN[lvl].num_to ) { - if ( bLastLvlFailed ) { - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, cr[lvl] ) ) { - CurTreeRemoveIfLastAtom( cur_tree, at_to[lvl] ); - CurTreeRemoveLastRankIfNoAtoms( cur_tree ); - } - bLastLvlFailed = 0; - } - /* Done with this level. Go back one level */ - goto next_j; - } - /* remove failed atom from the tree */ - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, cr[lvl] ) ) { - CurTreeRemoveLastRank( cur_tree ); - } - } - goto done; - } else { - cr[lvl] = 0; - } - -done:; /* at this point lvl=0. */ - if ( !nNumAtTo1Success ) { - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { - CurTreeRemoveIfLastAtom( cur_tree, at_to1 ); - CurTreeRemoveLastRankIfNoAtoms( cur_tree ); - } - } - } /* end of stereo atom mapping cycle */ - - if ( tpos1 < CurTreeGetPos( cur_tree ) && - 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { - CurTreeRemoveLastRank( cur_tree ); - } else - /* CurTree consistency check (debug only) */ - if ( tpos1 != CurTreeGetPos( cur_tree ) ) { - return CT_STEREOCOUNT_ERR; /* */ - } - - if ( !nTotSuccess || stereo_center_parity == sb_parity_calc ) { - goto repeat_all; /* repeat with next parity if no success or with the same parity, now known */ - } - - } else { - - /**************************************************** - * - * All stereogenic atoms and bonds have been mapped - * - ****************************************************/ - - if ( UserAction && USER_ACTION_QUIT == (*UserAction)() || - ConsoleQuit && (*ConsoleQuit)() ) { - return CT_USER_QUIT_ERR; - } - - if ( pCS->bStereoIsBetter || pCS->bFirstCT ) { - /* All stereo atoms have been mapped. Current stereo name is better than all previous. - * Create new numbering for the new CT - * break all remaining "from" ties - */ - int i1, ret; - AT_RANK rc, n1, n2; - ret=BreakAllTies( num_at_tg, num_max, pRankStack1, NeighList, nTempRank, pCS); - if ( RETURNED_ERROR( ret ) ) { - return ret; - } - /* break all remaining "from" ties */ - ret=BreakAllTies( num_at_tg, num_max, pRankStack2, NeighList, nTempRank, pCS); - if ( RETURNED_ERROR( ret ) ) { - return ret; - } - /* move stack pointers to the "nAtomNumber[*]" after all ties are broken */ - pRankStack1 += 2; - pRankStack2 += 2; - /* Now final mapping ranks of "to" atom (*pRankStack2)[i] and "from" atom (*pRankStack1)[i] - * are equal and all ranks are different, that is, we have a full mapping - * Copy so far best canonical numbering from "from" to "to". - */ - memset( pCS->nPrevAtomNumber, 0, num_at_tg*sizeof(pCS->nPrevAtomNumber[0]) ); - for ( i1 = 0; i1 < num_at_tg; i1 ++ ) { - n1 = pRankStack1[1][i1]; - rc = nCanonRankFrom[n1]; /* new canon. rank */ - n2 = pRankStack2[1][i1]; /* orig. atom number */ - nCanonRankTo[n2] = rc; /* assign new canon. number to the atom */ - /* use this array to find stereo-equivalent atoms */ - pCS->nPrevAtomNumber[rc-1] = n2; /* ord. number of the atom having canon. rank = rc */ - nSymmStereo[i1] = i1; /* restart search for stereo equivalent atoms */ - /* check mapping correctness */ - if ( pRankStack1[0][n1] != pRankStack2[0][n2] || - nSymmRank[n1] != nSymmRank[n2] ) { - return CT_STEREO_CANON_ERR; /* stereo mapping error */ - } - } - /* statistics */ - pCS->lNumTotCT ++; - pCS->lNumEqualCT = 1; - pCS->lNumDecreasedCT ++; - pCS->bStereoIsBetter = 0; /* prepare to start over */ - nTotSuccess = 1; - pCS->bFirstCT = 0; -#if ( REMOVE_CALC_NONSTEREO == 1 ) /* { */ - if ( !(pCS->nMode & CMODE_REDNDNT_STEREO ) ) { - i1 = RemoveCalculatedNonStereo( at, num_atoms, num_at_tg, - pRankStack1, pRankStack2, nTempRank, NeighList, - nSymmRank, nCanonRankTo, pCS->nPrevAtomNumber, pCS, - vABParityUnknown); - if ( RETURNED_ERROR( i1 ) ) { - return i1; - } - if ( i1 < 0 ) { -#if ( bRELEASE_VERSION == 0 ) - pCS->bExtract |= EXTR_REMOVE_PARITY_WARNING; -#endif - i1 = -(1+i1); - } - if ( i1 > 0 ) { - return 4; /* total restart: due to newly found stereo equivalence */ - /* the length of the stereo CT has changed */ - } - } -#endif /* } REMOVE_CALC_NONSTEREO */ - pRankStack1 -= 2; - pRankStack2 -= 2; - } else { - /* current stereo name is same as previous. We do not need a full mapping. */ - if ( nSymmStereo ) { - int num_changes = 0; - AT_RANK r, n1, n2, r_max, cr; - r_max = (AT_RANK)num_at_tg; - for ( r = 1; r <= r_max; r ++ ) { - if ( bUniqueAtNbrFromMappingRank( pRankStack1, r, &n1 ) ) { - if ( bUniqueAtNbrFromMappingRank( pRankStack2, r, &n2 ) ) { - /* atoms at[n1], at[n2] have identical untied mapping rank r */ - cr = nCanonRankFrom[(int)n1]-1; /* (new at[n2] canonical rank)-1 */ - /* pCS->nPrevAtomNumber[(int)cr] = */ - /* previous ordering number of an atom with the canon. rank = cr+1; */ - /* make this atom equivalent to atom at[n2]: */ - num_changes += nJoin2Mcrs( nSymmStereo, pCS->nPrevAtomNumber[(int)cr], n2 ); - } else { - return CT_MAPCOUNT_ERR; /* mapping ranks must be either both tied or untied. */ /* */ - } - } - } - if ( num_changes ) { /* compress trees to stars */ - for ( r = r_max-1; r; r -- ) { - nGetMcr( nSymmStereo, r ); - } - } - } - /* statistics */ - pCS->lNumEqualCT ++; - pCS->lNumTotCT ++; - nTotSuccess = 1; - } - if ( bInchiTimeIsOver( pCS->ulTimeOutTime ) ) { - return CT_TIMEOUT_ERR; - } - } - if ( !nTotSuccess && nNumMappedAtoms < pCS->nLenLinearCTStereoCarb ) { - pCS->LinearCTStereoCarb[nNumMappedAtoms] = prevAtom; - CurTreeSetPos( cur_tree, tpos1 ); - } - return nTotSuccess; /* return to the previous level of the recursion. */ -} +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include + +#include "mode.h" +#include "ichicomn.h" + +#define SB_DEPTH 6 + +/* +static int deep_map_stereo_atoms4=0; +*/ + +/************************************************ + map_stereo_bonds4 and map_stereo_atoms4 use + the following members of CANON_STAT *pCS: + + pCS->bKeepSymmRank // ??? almost unused, replaced with nSymmStereo != NULL ??? + pCS->bFirstCT + pCS->bStereoIsBetter + pCS->lNumNeighListIter + pCS->lNumBreakTies + pCS->lNumRejectedCT + pCS->lNumTotCT + pCS->lNumEqualCT + pCS->lNumDecreasedCT + pCS->bExtract (bRELEASE_VERSION == 0) + pCS->ulTimeOutTime + + pCS->bRankUsedForStereo + pCS->bAtomUsedForStereo + + pCS->LinearCTStereoDble + pCS->LinearCTStereoCarb + pCS->nLenLinearCTStereoCarb + pCS->nLenLinearCTStereoDble + + pCS->nPrevAtomNumber + ************************************************/ + +/********************************************************************************/ +int map_stereo_bonds4 ( struct tagINCHI_CLOCK *ic, + CANON_GLOBALS *pCG, + sp_ATOM *at, + int num_atoms, + int num_at_tg, + int num_max, + int bAllene, + const AT_RANK *nCanonRankFrom, + const AT_RANK *nAtomNumberCanonFrom, /* non-stereo canon ranking */ + AT_RANK *nCanonRankTo, /* output canonical stereo numbering*/ + const AT_RANK *nSymmRank, + AT_RANK **pRankStack1/*from*/, + AT_RANK **pRankStack2/*to*/, + AT_RANK *nTempRank, + int nNumMappedRanksInput, + AT_RANK *nSymmStereo, + NEIGH_LIST *NeighList, + CANON_STAT *pCS, + CUR_TREE *cur_tree, + int nNumMappedBonds, + int vABParityUnknown) +{ + int nTotSuccess = 0; /* 1=>full mapping has been completed; + * 2=>obtained a better stereo; + * 4=>restart (stereo bond or atom removed from the stereo CT) + */ + int tpos1=0; + AT_STEREO_DBLE prevBond; + memset(&prevBond, 0, sizeof(prevBond)); + tpos1 = CurTreeGetPos( cur_tree ); + +total_restart: + + if ( !nNumMappedBonds ) { + + memset( pCS->bRankUsedForStereo, 0, sizeof( pCS->bRankUsedForStereo[0] )*num_atoms ); + SetUseAtomForStereo( pCS->bAtomUsedForStereo, at, num_atoms ); + + if ( pCS->bFirstCT && nSymmStereo && !pCS->bKeepSymmRank ) { + int i; + for ( i = 0; i < num_at_tg; i ++ ) { + /* nSymmStereo[i] = min. {k | at[k] stereo eq. to at[i]} */ + nSymmStereo[i] = i; /* for union-join to keep track of stereo-equivalent atoms */ + } + } + } + + + if ( nNumMappedBonds < pCS->nLenLinearCTStereoDble ) { + + int at_rank1, at_rank2, bStereoIsBetterWasSetHere; + /* AT_RANK *nRankFrom=*pRankStack1++, AT_RANK *nAtomNumberFrom=pRankStack1++; */ + /* AT_RANK *nRankTo =*pRankStack2++, AT_RANK *nAtomNumberTo =pRankStack2++; */ + AT_RANK canon_min1, canon_min2; + int bFirstCanonRank; + int i, j, j1, j2, at_from1, at_from2, at_to1, at_to2, iMax, c; + int nStackPtr[SB_DEPTH], nNumMappedRanks[SB_DEPTH], LastMappedTo1; + int istk, istk2, istk3, bAddStack, nNumAtTo1Success; + int ret1, ret2, parity1, parity2; + + AT_RANK at_rank_canon1; /* = pCS->LinearCTStereoDble[nNumMappedBonds].at_num1; */ /* canonical numbers of atoms */ + AT_RANK at_rank_canon2; /* = pCS->LinearCTStereoDble[nNumMappedBonds].at_num2; */ /* adjacent to the stereogenic bond */ + int nNumChoices, nNumUnkn, nNumUndf, nNumBest, nNumWorse, nNumCalc, sb_parity_calc; + int stereo_bond_parity=0, prev_stereo_bond_parity, pass, bAllParitiesIdentical, bAllParitiesIdentical2; + AT_STEREO_DBLE prevBond2; + + prevBond = pCS->LinearCTStereoDble[nNumMappedBonds]; + bFirstCanonRank=1; + canon_min1=canon_min2=0; +/* + // find candidates for atom_from1, atom_to1; they must have identical mapping ranks + at_rank1=pRankStack1[0][at_from1=nAtomNumberCanonFrom[(int)at_rank_canon1 - 1]]; // rank "from" for mapping + at_rank2=pRankStack1[0][at_from2=nAtomNumberCanonFrom[(int)at_rank_canon2 - 1]]; // rank "from" for mapping +*/ + if ( nNumMappedBonds ) { + at_rank_canon1 = pCS->LinearCTStereoDble[nNumMappedBonds-1].at_num1; + at_rank_canon2 = pCS->LinearCTStereoDble[nNumMappedBonds-1].at_num2; + } else { + at_rank_canon1 = 0; + at_rank_canon2 = 0; + } + goto bypass_next_canon_ranks_check; + +next_canon_ranks: + + /* Save time: avoid calling Next_SB_At_CanonRanks2() */ + if ( !pCS->bStereoIsBetter /* ??? && !pCS->bFirstCT ???*/ && + at_rank_canon1 > pCS->LinearCTStereoDble[nNumMappedBonds].at_num1 || + at_rank_canon1 == pCS->LinearCTStereoDble[nNumMappedBonds].at_num1 && + at_rank_canon2 >= pCS->LinearCTStereoDble[nNumMappedBonds].at_num2 ) { + + if ( !nTotSuccess ) { + pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond; + } + CurTreeSetPos( cur_tree, tpos1 ); + return nTotSuccess; + } + +bypass_next_canon_ranks_check: + + CurTreeSetPos( cur_tree, tpos1 ); + + /* find next available canon. numbers for a stereogenic bond pair of atoms */ + /* process allenes AFTER all double bonds and odd-number-of-double-bonds cumulenes */ + if ( !(ret1 = Next_SB_At_CanonRanks2( &at_rank_canon1, &at_rank_canon2, /* canonical numbers */ + &canon_min1, &canon_min2, + &bFirstCanonRank, pCS->bAtomUsedForStereo, + pRankStack1, pRankStack2, + nCanonRankFrom, nAtomNumberCanonFrom, + at, num_atoms, bAllene ) ) ) { + /* failed to find next stereo bond to assign parity */ + if ( !bAllene && bFirstCanonRank ) { + /* all stereobond have been processed; try to find allene to continue */ + AT_RANK at_rank_canon1_Allene = 0, canon_min1_Allene = 0; + AT_RANK at_rank_canon2_Allene = 0, canon_min2_Allene = 0; + if ( ret1 = Next_SB_At_CanonRanks2( &at_rank_canon1_Allene, &at_rank_canon2_Allene, + &canon_min1_Allene, &canon_min2_Allene, + &bFirstCanonRank, pCS->bAtomUsedForStereo, + pRankStack1, pRankStack2, + nCanonRankFrom, nAtomNumberCanonFrom, + at, num_atoms, 1 ) ) { + at_rank_canon1 = at_rank_canon1_Allene; + at_rank_canon2 = at_rank_canon2_Allene; + canon_min1 = canon_min1_Allene; + canon_min2 = canon_min2_Allene; + bAllene = 1; /* switch to allenes */ + } + } + } + + if ( !ret1 || !pCS->bStereoIsBetter && + (at_rank_canon1 > pCS->LinearCTStereoDble[nNumMappedBonds].at_num1 || + at_rank_canon1 == pCS->LinearCTStereoDble[nNumMappedBonds].at_num1 && + at_rank_canon2 > pCS->LinearCTStereoDble[nNumMappedBonds].at_num2 ) ) { + /* new ranks provide greater pCS->LinearCTStereoDble[nNumMappedBonds] and therefore rejected */ + if ( !nTotSuccess ) { + pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond; /* restore stereo bond CT for the current bond */ + } + return nTotSuccess; + } + /* current stereo bond initialization */ + nNumChoices = 0; + nNumUnkn = 0; + nNumUndf = 0; + nNumBest = 0; + nNumWorse = 0; + nNumCalc = 0; + pass=0; + prev_stereo_bond_parity = 0; + + at_rank1=pRankStack1[0][at_from1=nAtomNumberCanonFrom[(int)at_rank_canon1 - 1]]; /* atom 1 rank "from" for mapping */ + at_rank2=pRankStack1[0][at_from2=nAtomNumberCanonFrom[(int)at_rank_canon2 - 1]]; /* atom 2 rank "from" for mapping */ + /* we are going to map bond (at[at_from1], at[at_from2]) and + canonical ranks of its atoms (at_rank_canon1, at_rank_canon2) + onto a stereogenic bond (at[at_to1], at[at_to2]) + */ + iMax = at_rank1-1; + /* test correctness: sorted pRankStack2[0][] and pRankStack1[0][] should have same ranks for both atoms */ + if ( at_rank1 != pRankStack2[0][pRankStack2[1][at_rank1-1]] || + at_rank2 != pRankStack2[0][pRankStack2[1][at_rank2-1]] ) { + /* program error: "from" and "to" mapping ranks are not equal */ + return CT_STEREOCOUNT_ERR; /* */ + } + /* -- do not check stereo features of "from" atoms: + -- in case of "bond/charge isomerism" they may be missing. + if ( !at[at_from1].stereo_bond_neighbor[0] || + !at[at_from2].stereo_bond_neighbor[0] ) + return CT_STEREOCOUNT_ERR; // program error + */ + + /* find out if we have a choice in mapping: check all possible pairs (at_to1, at_to2) + such that at_from1 is possibly constitutionally equivalent to at_to1, at_from2 to at_to2 */ + for ( j1 = 0; j1 <= iMax && at_rank1 == pRankStack2[0][at_to1=pRankStack2[1][iMax-j1]]; j1 ++ ) { + if ( !at[at_to1].stereo_bond_neighbor[0] ) + continue; /* at_to1 does not belong to a stereo bond */ + for( j2 = 0; j2 < MAX_NUM_STEREO_BONDS && + (at_to2 =at[at_to1].stereo_bond_neighbor[j2]); j2 ++ ) { + at_to2 --; + if ( pRankStack1[0][at_from2] != pRankStack2[0][at_to2] ) + continue; /* at_from2 cannot be mapped on at_to2 */ + stereo_bond_parity = PARITY_VAL(at[at_to1].stereo_bond_parity[j2]); + i = 0; + switch(stereo_bond_parity) { + + case AB_PARITY_UNDF: nNumUndf ++; + break; /* 4 */ + case AB_PARITY_UNKN: nNumUnkn ++; + break; /* 3 (occurs if forced different to UNDF)*/ + + case BEST_PARITY: nNumBest ++; break; /* 1 */ + case WORSE_PARITY: nNumWorse ++; break; /* 2 */ + case AB_PARITY_CALC: nNumCalc ++; break; /* 6 */ + case AB_PARITY_NONE: i ++; break; /* 0 */ + } + nNumChoices += !i; + } + } + if ( nNumChoices != nNumCalc + nNumUndf + nNumUnkn + nNumBest + nNumWorse ) { + return CT_STEREOCOUNT_ERR; /* program error */ /* */ + } + if ( !nNumChoices ) { + goto next_canon_ranks; + } + /* Determine the first parity to search */ + sb_parity_calc = ( nNumCalc > 0 )? BEST_PARITY : 0; + + /* ============================================================== + Search sequence: sb_parity_calc stereo_bond_parity + ============================================================== + BEST_PARITY (calc) BEST_PARITY BEST_PARITY + BEST_PARITY (known) BEST_PARITY WORSE_PARITY or 0 + WORSE_PARITY (calc) WORSE_PARITY WORSE_PARITY + WORSE_PARITY (known) WORSE_PARITY 0 + AB_PARITY_UNKN(known) AB_PARITY_UNKN 0 + AB_PARITY_UNDF(known) AB_PARITY_UNDF 0 + + if (sb_parity_calc==stereo_bond_parity) then "calc" else "known" + */ + +repeat_all: + + if ( !nNumMappedBonds ) + pCS->bStereoIsBetter = 0; /* the first stereo feature in the canonical CT; moved here 7-13-2002 */ + + if ( !pass ++ ) { + /* select the smallest (best) parity to search */ + if ( sb_parity_calc ) { + stereo_bond_parity = BEST_PARITY; + } else { + stereo_bond_parity = nNumBest? BEST_PARITY : + nNumWorse? WORSE_PARITY : + nNumUnkn? AB_PARITY_UNKN : + nNumUndf? AB_PARITY_UNDF : AB_PARITY_NONE; + } + } else { + /* second pass: since the first pass failed, search for a worse result */ + prev_stereo_bond_parity = stereo_bond_parity; + i = NextStereoParity2Test( &stereo_bond_parity, &sb_parity_calc, + nNumBest, nNumWorse, nNumUnkn, nNumUndf, nNumCalc, vABParityUnknown); + switch ( i ) { + case 0: + break; /* obtained next parity to test */ + case 1: + goto next_canon_ranks; + default: + return i; /* program error */ + } + } + if ( stereo_bond_parity == AB_PARITY_NONE ) { + /* error? */ + return CT_STEREOCOUNT_ERR; /* */ + } + /* check if the new requested parity is good (small) enough */ + if ( !pCS->bStereoIsBetter ) { + c = CompareLinCtStereoDoubleToValues( nTotSuccess? pCS->LinearCTStereoDble+nNumMappedBonds : &prevBond, + at_rank_canon1, at_rank_canon2, (U_CHAR)stereo_bond_parity ); + if ( c < 0 ) { + if ( !nTotSuccess ) { + pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond; + } + CurTreeSetPos( cur_tree, tpos1 ); + return nTotSuccess; + } + } + + bAllParitiesIdentical = 0; + bAllParitiesIdentical2 = 0; + LastMappedTo1 = -1; + bStereoIsBetterWasSetHere = 0; + istk = istk2 = istk3 = 0; + + if ( !nNumMappedBonds && prev_stereo_bond_parity != stereo_bond_parity ) + pCS->bStereoIsBetter = 0; /* the first stereo feature in the canonical CT; moved here 5-24-2002 */ + + if ( prev_stereo_bond_parity != stereo_bond_parity ) { + CurTreeSetPos( cur_tree, tpos1 ); /* start over */ + } + + /* Mapping: here at_rank1 = nRankTo, at_to1 = nAtomNumberTo */ + for ( j1 = 0; j1 <= iMax && at_rank1 == pRankStack2[0][at_to1=pRankStack2[1][iMax-j1]]; j1 ++ ) { + nNumAtTo1Success = 0; + if ( !at[at_to1].stereo_bond_neighbor[0] ) + continue; /* at_to1 does not belong to a stereo bond */ + if ( tpos1 < CurTreeGetPos( cur_tree ) && + 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) && + 1 == CurTreeIsLastAtomEqu( cur_tree, at_to1, nSymmStereo ) ) { + /* at_to1 is known to be stereogenically equivalent to another atom tried with at_rank_canon1 */ + continue; + } + bAllParitiesIdentical2 = 0; + for( j2 = 0; j2 < MAX_NUM_STEREO_BONDS && (at_to2 =at[at_to1].stereo_bond_neighbor[j2]); j2 ++ ) { + EQ_NEIGH EN1[2], EN2[2]; + int bond_parity, num1, num2; + AT_RANK at_rank_canon_n1, at_rank_canon_n2; + + at_to2 --; + if ( pRankStack1[0][at_from2] != pRankStack2[0][at_to2] ) + continue; /* at_from2 cannot be mapped on at_to2 even without mapping at_from1 to at_to1 */ + + /* check whether the bond parity corresponds to the requested bond parity */ + if ( PARITY_KNOWN(at[at_to1].stereo_bond_parity[j2]) ) { + if ( stereo_bond_parity == sb_parity_calc ) { + continue; /* requested parity to be calculated, found known parity */ + } + if ( stereo_bond_parity != PARITY_VAL(at[at_to1].stereo_bond_parity[j2]) ) { + continue; /* parity differs from the requested parity */ + } + } else + if ( PARITY_CALCULATE( at[at_to1].stereo_bond_parity[j2]) ) { + if ( stereo_bond_parity != sb_parity_calc ) { + continue; /* requested known parity, found parity to be calculated */ + } + } else { + return CT_STEREOCOUNT_ERR; /* unknown parity type */ /* */ + } + /* initialize stack pointer nStackPtr[istk] for "hand-made" recursion */ + /* stacks are pRankStack1[], pRankStack2[], nNumMappedRanks[] */ + istk = 0; + nStackPtr[0] = 0; + nNumMappedRanks[0] = nNumMappedRanksInput; + bAddStack = 0; + bAllParitiesIdentical = ((at[at_to1].stereo_bond_parity[j2] & KNOWN_PARITIES_EQL )) && + PARITY_KNOWN(at[at_to1].stereo_bond_parity[j2]); + + if ( !bAllParitiesIdentical && !nNumCalc && + (!nNumUndf + !nNumUnkn + !nNumBest + !nNumWorse )==3) { + /* only one kind of bond parity is present; check whether all parities are really same */ + bAllParitiesIdentical = All_SB_Same( at_rank_canon1, at_rank_canon2, /* canonical numbers */ + pRankStack1, pRankStack2, + nAtomNumberCanonFrom, at ); + if ( bAllParitiesIdentical < 0 ) { + return CT_STEREOCOUNT_ERR; /* */ + } + } + + /***************************************************************** + * do the mapping only if parities are not same + */ + if ( !bAllParitiesIdentical ) { + /* map atom 1 or reuse previous mapping */ + if ( LastMappedTo1 != at_to1 ) { + /* avoid repetitve mapping to the same first at_to1 using LastMappedTo1 variable */ + /* map atom 1 */ + ret1 = map_an_atom2( pCG, num_at_tg, num_max, at_from1, at_to1, + nTempRank, nNumMappedRanks[istk], &nNumMappedRanks[istk+1], pCS, + NeighList, pRankStack1+nStackPtr[istk], pRankStack2+nStackPtr[istk], + &bAddStack ); + if ( RETURNED_ERROR(ret1) ) { + return ret1; /* error */ + } + nStackPtr[istk+1] = nStackPtr[istk]+bAddStack; + LastMappedTo1 = at_to1; + if ( bAddStack ) { + if ( tpos1 == CurTreeGetPos( cur_tree ) || + 0 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { + CurTreeAddRank( cur_tree, at_rank_canon1 ); + } + CurTreeAddAtom( cur_tree, at_to1 ); + } + } + istk ++; /* = 1 */ + /* check if we can map atom 2 */ + if ( pRankStack1[nStackPtr[istk]][at_from2] != pRankStack2[nStackPtr[istk]][at_to2] ) { + /* + * This may happen when: + * A) Charge/bond isomerism, for example cyclopentadiene(-), or + * B) possibly stereogenic bond in an alternating ring has heighbors + * in 2 symmetrically attached rings. + * Such an alternating bond cannot be mapped on possibly stereogenic bond + * that has neighbors belonging to 1 of the symmetrically attached rings only. + * For example: + * A---B---C---D If all atoms are Carbons then B, C, F, G are constitutionally + * || || || || equivalent. However, bonds B-C, F-G are not equivalent to + * || || || || B-F and C-G and cannot be mapped on them. + * E---F---G---H If at_from1=B, at_from2=F, at_to1=B, then at_from2 cannot be mapped on at_to2=C + * If at_from1=B, at_from2=F, at_to1=C, then at_from2 cannot be mapped on at_to2=B + * etc. + */ + if ( sb_parity_calc != stereo_bond_parity) { + /* can be passed only once for each bond */ + nNumChoices --; + nNumUndf -= (stereo_bond_parity == AB_PARITY_UNDF); + nNumUnkn -= (stereo_bond_parity == AB_PARITY_UNKN); + nNumBest -= (stereo_bond_parity == BEST_PARITY); + nNumWorse-= (stereo_bond_parity == WORSE_PARITY); + /* nNumCalc = nNumChoices - (nNumUndf + nNumUnkn + nNumBest + nNumWorse); */ + } else + if ( sb_parity_calc == BEST_PARITY ) { + /* can be passed 2 times: for BEST_PARITY and WORSE_PARITY in this order */ + nNumChoices --; /* do not repeate for WORSE_PARITY */ + nNumCalc --; + } + continue; /* Happens for ID=80036,80253,91354,95532,101532,103788 */ + } + if ( nStackPtr[istk] > nStackPtr[istk-1] ) { + bAllParitiesIdentical2 = All_SB_Same( at_rank_canon1, at_rank_canon2, + pRankStack1+nStackPtr[istk], pRankStack2+nStackPtr[istk], + nAtomNumberCanonFrom, at ); + if ( bAllParitiesIdentical2 < 0 ) { + return CT_STEREOBOND_ERROR; /* */ + } + } else { + bAllParitiesIdentical2 = 0; + } + if ( bAllParitiesIdentical2 ) { + /* do no mapping when all equivalent bonds have same parity */ + /* stereo_bond_parity = PARITY_VAL(at[at_to1].stereo_bond_parity[j2]); */ + ClearPreviousMappings( pRankStack1+nStackPtr[istk]+2 ); + } else { + if ( tpos1 < CurTreeGetPos( cur_tree ) && + 1 == CurTreeIsLastRank( cur_tree, at_rank_canon2 ) && + 1 == CurTreeIsLastAtomEqu( cur_tree, at_to2, nSymmStereo ) ) { + continue; + } + /* map atom 2 */ + ret2 = map_an_atom2( pCG, num_at_tg, num_max, at_from2, at_to2, + nTempRank, nNumMappedRanks[istk], &nNumMappedRanks[istk+1], pCS, + NeighList, pRankStack1+nStackPtr[istk], pRankStack2+nStackPtr[istk], + &bAddStack ); + if ( RETURNED_ERROR(ret2) ) { + return ret2; /* program error */ + } + nStackPtr[istk+1] = nStackPtr[istk]+bAddStack; + istk ++; /* = 2 */ + if ( bAddStack ) { + if ( tpos1 == CurTreeGetPos( cur_tree ) || + 0 == CurTreeIsLastRank( cur_tree, at_rank_canon2 ) ) { + CurTreeAddRank( cur_tree, at_rank_canon2 ); + } + CurTreeAddAtom( cur_tree, at_to2 ); + } + } + } else { + /* do no mapping when all equivalent bonds have same parity */ + /* stereo_bond_parity = PARITY_VAL(at[at_to1].stereo_bond_parity[j2]); */ + ClearPreviousMappings( pRankStack1+2 ); + } + + /* we have a precalculated (known) bond parity */ + + + /************************************************************ + * + * Known Bond Parity case: do not map stereo bond neighbors + */ + if ( stereo_bond_parity != sb_parity_calc ) /* parity is known */ + { + /* accept bond parity and do not map the neighbors */ + bond_parity = stereo_bond_parity; + /* same code as under " make a decision to accept current mapping" comment below */ + /* with one exception: istk instead of istk3 */ + c = CompareLinCtStereoDoubleToValues( pCS->LinearCTStereoDble+nNumMappedBonds, + at_rank_canon1, at_rank_canon2, (U_CHAR)bond_parity ); + if ( c < 0 && !pCS->bStereoIsBetter ) { + + /* reject */ + + pCS->lNumRejectedCT ++; + /* remove failed atom2 from the tree */ + if ( tpos1 < CurTreeGetPos( cur_tree ) && + 1 == CurTreeIsLastRank( cur_tree, at_rank_canon2 ) ) { + CurTreeRemoveIfLastAtom( cur_tree, at_to2 ); + CurTreeRemoveLastRankIfNoAtoms( cur_tree ); + } + continue; /* to next at_to2; Reject this at_to2: not a minimal CT. */ + } else { + + /* accept */ + + if ( c > 0 && !pCS->bStereoIsBetter ) { + /* bond entry is less than the previusly found */ + pCS->bStereoIsBetter = bStereoIsBetterWasSetHere = 1; + prevBond2 = pCS->LinearCTStereoDble[nNumMappedBonds]; + } + pCS->LinearCTStereoDble[nNumMappedBonds].at_num1 = at_rank_canon1; + pCS->LinearCTStereoDble[nNumMappedBonds].at_num2 = at_rank_canon2; + pCS->LinearCTStereoDble[nNumMappedBonds].parity = bond_parity; + /* recursive call */ + pCS->bRankUsedForStereo[at_from1] ++; + pCS->bRankUsedForStereo[at_from2] ++; + if ( !bAllParitiesIdentical ) { + pCS->bAtomUsedForStereo[at_to1] --; + pCS->bAtomUsedForStereo[at_to2] --; + } + ret2 = map_stereo_bonds4 ( ic, pCG, at, num_atoms, num_at_tg, num_max, bAllene, nCanonRankFrom, nAtomNumberCanonFrom, nCanonRankTo, + nSymmRank, pRankStack1+nStackPtr[istk], pRankStack2+nStackPtr[istk], + nTempRank, nNumMappedRanks[istk], nSymmStereo, NeighList, + pCS, cur_tree, nNumMappedBonds+1 , + vABParityUnknown); + if ( !bAllParitiesIdentical ) { + pCS->bAtomUsedForStereo[at_to1] ++; + pCS->bAtomUsedForStereo[at_to2] ++; + } + pCS->bRankUsedForStereo[at_from1] --; + pCS->bRankUsedForStereo[at_from2] --; + if ( ret2 == 4 ) { + if ( nNumMappedBonds ) { + return ret2; + } else { + pCS->bFirstCT = 1; + goto total_restart; + } + } + + if ( RETURNED_ERROR(ret2) ) { + if ( ret2 == CT_TIMEOUT_ERR ) + return ret2; + else + return ret2; /* program error */ + } + if ( ret2 > 0 ) { + nTotSuccess |= 1; + nNumAtTo1Success ++; + if ( bStereoIsBetterWasSetHere || (ret2 & 2) ) { + CurTreeKeepLastAtomsOnly( cur_tree, tpos1, 1 ); /* start over */ + nTotSuccess |= 2; /* Obtained a smaller CT */ + } + } else { + if ( bStereoIsBetterWasSetHere ) { /* rollback */ + pCS->bStereoIsBetter = 0; + pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond2; + } + /* remove failed atom2 from the tree */ + if ( tpos1 < CurTreeGetPos( cur_tree ) && + 1 == CurTreeIsLastRank( cur_tree, at_rank_canon2 ) ) { + CurTreeRemoveIfLastAtom( cur_tree, at_to2 ); + CurTreeRemoveLastRankIfNoAtoms( cur_tree ); + } + /* + if ( 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { + CurTreeRemoveLastAtom( cur_tree, at_to1 ); + CurTreeRemoveLastRankIfNoAtoms( cur_tree ); + } + */ + } + bStereoIsBetterWasSetHere = 0; + } + if ( bAllParitiesIdentical || bAllParitiesIdentical2 ) { + break; /* j2 cycle, at_to2 (no need to repeat) */ + } + continue; /* to next at_to2 */ + } + /*************************************************************************** + * + * Unknown Bond Parity case: may need to map stereo bond neighbors + * + **************************************************************************** + * Ranks are not known in advance + * check if at_from1/at_to1 half-bond has neighbors with equal mapping ranks + */ + + parity1 = parity_of_mapped_half_bond( at_from1, at_to1, at_from2, at_to2, at, &EN1[0], + nCanonRankFrom, pRankStack1[nStackPtr[istk]], pRankStack2[nStackPtr[istk]] ); + /* old approach -- before E/Z parities + parity1 = parity_of_mapped_atom2( pCG, at_from1, at_to1, at, &EN1[0], + nCanonRankFrom, pRankStack1[nStackPtr[istk]], pRankStack2[nStackPtr[istk]] ); + */ + /* the following commented out statement is not needed here. */ + /* parity2 = parity_of_mapped_atom2( pCG, at_from2, at_to2, at, &EN2[0], + nCanonRankFrom, pRankStack1[nStackPtr[istk]], + pRankStack2[nStackPtr[istk]] ); + */ + if ( !parity1 ) { + return CT_STEREOCOUNT_ERR; /* program error */ /* */ + } + num1 = parity1 > 0? 1:2; /* parity < 0 means additional mapping is needed to set parity */ + /* --- try all possible mappings of the stereo bond ending atoms' neighbors --- */ + at_rank_canon_n1 = 0; + at_rank_canon_n2 = 0; + for ( i = 0; i < num1; i ++ ) { + int at_from_n1, at_to_n1, at_no_n1_num_success = 0; + istk2 = istk; + if ( num1 == 2 ) { + at_rank_canon_n1 = nCanonRankFrom[EN1[0].from_at]; + /* an additional neighbor mapping is necessary; */ + /* we need to map only one at_from1 neighbor to make all neighbors have different ranks */ + + at_from_n1 = EN1[0].from_at; + at_to_n1 = EN1[0].to_at[i]; + + if ( tpos1 < CurTreeGetPos( cur_tree ) && + 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n1 ) && + 1 == CurTreeIsLastAtomEqu( cur_tree, at_to_n1, nSymmStereo ) ) + continue; + /* + if ( nSymmStereo && !pCS->bFirstCT ) { + if ( i && nSymmStereo[at_to_n1] == nSymmStereo[(int)EN1[0].to_at[0]] ) { + continue; // do not test stereo equivalent atoms except the first one + } + } + */ + /* neighbors are tied. Untie them by breaking a tie on ONE of them. */ + ret1 = map_an_atom2( pCG, num_at_tg, num_max, at_from_n1, at_to_n1, + nTempRank, nNumMappedRanks[istk2], &nNumMappedRanks[istk2+1], pCS, + NeighList, pRankStack1+nStackPtr[istk2], pRankStack2+nStackPtr[istk2], + &bAddStack ); + if ( RETURNED_ERROR(ret1) ) { + return ret1; /* program error */ /* */ + } + nStackPtr[istk2+1] = nStackPtr[istk2] + bAddStack; + istk2 ++; /* <= 3 */ + /* debug */ + if ( istk2 >= SB_DEPTH ) { + return CT_OVERFLOW; /* program error */ /* */ + } + if ( bAddStack ) { + if ( tpos1 == CurTreeGetPos( cur_tree ) || + 0 == CurTreeIsLastRank( cur_tree, at_rank_canon_n1 ) ) { + CurTreeAddRank( cur_tree, at_rank_canon_n1 ); + } + CurTreeAddAtom( cur_tree, at_to_n1 ); + } + + + /* now that all at_from1 neighbors have been mapped the parity must be defined */ + parity1 = parity_of_mapped_half_bond( at_from1, at_to1, at_from2, at_to2, at, &EN1[1], + nCanonRankFrom, pRankStack1[nStackPtr[istk2]], pRankStack2[nStackPtr[istk2]] ); + if ( parity1 <= 0 ) + return CT_STEREOCOUNT_ERR; /* program error */ /* */ + } else { + nNumMappedRanks[istk2+1] = nNumMappedRanks[istk2]; + nStackPtr[istk2+1] = nStackPtr[istk2]; + istk2 ++; /* <= 3 */ + } + + /* check if at_from2/at_to2 half-bond has neighbors with equal mapping ranks */ + parity2 = parity_of_mapped_half_bond( at_from2, at_to2, at_from1, at_to1, at, &EN2[0], + nCanonRankFrom, pRankStack1[nStackPtr[istk2]], pRankStack2[nStackPtr[istk2]] ); + if ( !parity2 ) { + return CT_STEREOCOUNT_ERR; /* program error */ /* */ + } + num2 = parity2 > 0? 1:2; + at_rank_canon_n2 = 0; + for ( j = 0; j < num2; j ++ ) { + int at_from_n2, at_to_n2; + istk3 = istk2; + if ( num2 == 2 ) { + at_rank_canon_n2 = nCanonRankFrom[EN2[0].from_at]; + /* we need to map only one at_from2 neighbor to make its neighbors have different ranks */ + at_from_n2 = EN2[0].from_at; + at_to_n2 = EN2[0].to_at[j]; + + if ( tpos1 < CurTreeGetPos( cur_tree ) && + 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n2 ) && + 1 == CurTreeIsLastAtomEqu( cur_tree, at_to_n2, nSymmStereo ) ) + continue; + + /* + if ( nSymmStereo && !pCS->bFirstCT ) { + if ( j && nSymmStereo[at_to_n2] == nSymmStereo[(int)EN2[0].to_at[0]] ) { + continue; // do not test stereo equivalent atoms except the first one + } + } + */ + /* neighbors are tied. Untie them by breaking a tie on ONE of them. */ + ret1 = map_an_atom2( pCG, num_at_tg, num_max, at_from_n2, at_to_n2, + nTempRank, nNumMappedRanks[istk3], &nNumMappedRanks[istk3+1], pCS, + NeighList, pRankStack1+nStackPtr[istk3], + pRankStack2+nStackPtr[istk3], + &bAddStack ); + if ( RETURNED_ERROR(ret1) ) { + return ret1; /* program error */ + } + nStackPtr[istk3+1] = nStackPtr[istk3]+bAddStack; + istk3 ++; /* <= 4 */ + + if ( bAddStack ) { + if ( tpos1 == CurTreeGetPos( cur_tree ) || + 0 == CurTreeIsLastRank( cur_tree, at_rank_canon_n2 ) ) { + CurTreeAddRank( cur_tree, at_rank_canon_n2 ); + } + CurTreeAddAtom( cur_tree, at_to_n2 ); + } + + parity2 = parity_of_mapped_half_bond( at_from2, at_to2, at_from1, at_to1, at, &EN2[1], + nCanonRankFrom, pRankStack1[nStackPtr[istk3]], pRankStack2[nStackPtr[istk3]] ); + if ( parity2 <= 0 ) { + return CT_STEREOCOUNT_ERR; /* program error */ /* */ + } + } else { + /* no additional mapping is needed to set atom's parity */ + nNumMappedRanks[istk3+1] = nNumMappedRanks[istk3]; + nStackPtr[istk3+1] = nStackPtr[istk3]; + istk3 ++; /* <= 4 */ + } + + /******************************************************************* + * at this point the stereo bond is fully mapped to find its parity + *******************************************************************/ + + if ( parity1 <= 0 || parity2 <= 0 ) { + return CT_STEREOCOUNT_ERR; /* program error */ /* */ + } + + /* find current bond parity AB_PARITY_ODD */ + if ( ATOM_PARITY_WELL_DEF(parity1) && ATOM_PARITY_WELL_DEF(parity2) ) { + bond_parity = 2 - (parity1 + parity2)%2; + } else { + bond_parity = inchi_max(parity1, parity2); + } + if ( ATOM_PARITY_WELL_DEF(bond_parity) && at[at_to1].stereo_bond_z_prod[j2] < 0 ) + bond_parity = 2 - (bond_parity+1)%2; /* invert the bond parity */ + + + /******************************************************** + * make a decision whether to accept the current mapping + */ + c = CompareLinCtStereoDoubleToValues( pCS->LinearCTStereoDble+nNumMappedBonds, + at_rank_canon1, at_rank_canon2, (U_CHAR)bond_parity ); + if ( sb_parity_calc != bond_parity || + c < 0 && !pCS->bStereoIsBetter ) { + /* reject */ + pCS->lNumRejectedCT ++; + /* remove failed atom2 from the tree */ + if ( tpos1 < CurTreeGetPos( cur_tree ) && + 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n2 ) ) { + CurTreeRemoveIfLastAtom( cur_tree, at_to_n2 ); + CurTreeRemoveLastRankIfNoAtoms( cur_tree ); + } + continue; /* Reject: not a minimal CT. */ + } else { + + /* try to accept */ + + if ( c > 0 && !pCS->bStereoIsBetter ) { + /* bond_parity is less than the previusly found */ + pCS->bStereoIsBetter = bStereoIsBetterWasSetHere = 1; + prevBond2 = pCS->LinearCTStereoDble[nNumMappedBonds]; + } + /* accept */ + pCS->LinearCTStereoDble[nNumMappedBonds].at_num1 = at_rank_canon1; + pCS->LinearCTStereoDble[nNumMappedBonds].at_num2 = at_rank_canon2; + pCS->LinearCTStereoDble[nNumMappedBonds].parity = bond_parity; + /* recursive call */ + pCS->bRankUsedForStereo[at_from1] ++; + pCS->bRankUsedForStereo[at_from2] ++; + pCS->bAtomUsedForStereo[at_to1] --; + pCS->bAtomUsedForStereo[at_to2] --; + ret2 = map_stereo_bonds4 ( ic, pCG, at, num_atoms, num_at_tg, num_max, bAllene, nCanonRankFrom, nAtomNumberCanonFrom, nCanonRankTo, + nSymmRank, pRankStack1+nStackPtr[istk3], pRankStack2+nStackPtr[istk3], + nTempRank, nNumMappedRanks[istk3], nSymmStereo, NeighList, + pCS, cur_tree, nNumMappedBonds+1 , + vABParityUnknown); + pCS->bRankUsedForStereo[at_from1] --; + pCS->bRankUsedForStereo[at_from2] --; + pCS->bAtomUsedForStereo[at_to1] ++; + pCS->bAtomUsedForStereo[at_to2] ++; + if ( ret2 == 4 ) { + if ( nNumMappedBonds ) { + return ret2; + } else { + pCS->bFirstCT = 1; + goto total_restart; + } + } + if ( RETURNED_ERROR(ret2) ) { + if ( ret2 == CT_TIMEOUT_ERR ) + return ret2; + else + return ret2; /* program error */ + } + if ( ret2 > 0 ) { + nTotSuccess |= 1; + nNumAtTo1Success ++; + if ( bStereoIsBetterWasSetHere || (ret2 & 2) ) { + CurTreeKeepLastAtomsOnly( cur_tree, tpos1, 1 ); /* start over */ + nTotSuccess |= 2; /* Obtained a smaller CT */ + } + at_no_n1_num_success ++; + } else { + if ( bStereoIsBetterWasSetHere ) { /* rollback */ + pCS->bStereoIsBetter = 0; + pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond2; + } + if ( tpos1 < CurTreeGetPos( cur_tree ) && + 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n2 ) ) { + CurTreeRemoveIfLastAtom( cur_tree, at_to_n2 ); + CurTreeRemoveLastRankIfNoAtoms( cur_tree ); + } + } + bStereoIsBetterWasSetHere = 0; + } + } /* end choices in mapping neighbors of the 2nd half-bond */ + if ( tpos1 < CurTreeGetPos( cur_tree ) && + 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n2 ) ) { + CurTreeRemoveLastRank( cur_tree ); + } + /* added 2006-07-20 */ + if ( !at_no_n1_num_success && tpos1 < CurTreeGetPos( cur_tree ) && + 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n1 ) ) { + CurTreeRemoveIfLastAtom( cur_tree, at_to_n1 ); + } + } /* end choices in mapping neighbors of the 1st half-bond */ + if ( tpos1 < CurTreeGetPos( cur_tree ) && + 1 == CurTreeIsLastRank( cur_tree, at_rank_canon_n1 ) ) { + CurTreeRemoveLastRank( cur_tree ); + } + } /* end of choices in mapping at_from2 */ + if ( tpos1 < CurTreeGetPos( cur_tree ) && + 1 == CurTreeIsLastRank( cur_tree, at_rank_canon2 ) ) { + CurTreeRemoveLastRank( cur_tree ); + } + if ( !nNumAtTo1Success ) { + if ( tpos1 < CurTreeGetPos( cur_tree ) && + 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { + CurTreeRemoveIfLastAtom( cur_tree, at_to1 ); + CurTreeRemoveLastRankIfNoAtoms( cur_tree ); + } + } + if ( bAllParitiesIdentical /*&& !nSymmStereo*/ ) { + break; + } + } /* end of choices in mapping at_from1 */ + + if ( tpos1 < CurTreeGetPos( cur_tree ) && + 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { + CurTreeRemoveLastRank( cur_tree ); + } else + /* CurTree consistecy check (debug only) */ + if ( tpos1 != CurTreeGetPos( cur_tree ) ) { + return CT_STEREOCOUNT_ERR; /* */ + } + + if ( !nTotSuccess || stereo_bond_parity == sb_parity_calc ) { + goto repeat_all; /* repeat with next parity if no success or with the same parity, now known */ + } + + /* Previously the control flow never came here... */ + if ( !nTotSuccess ) { + pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond; + CurTreeSetPos( cur_tree, tpos1 ); + /* Occurs when atoms are not really equvalent ( -O= without positive charge in "aromatic" ring) */ + return 0; /* Happens for ID=92439,100318,100319 when EXCL_ALL_AROM_BOND_PARITY=0 and + * nNumChoices=0. + * Results from impossible previous mapping of symmetric relatively + * to a central ring aromatic circles while central ring is not symmetrical due to + * alternate bonds (in the central ring number of pi-electrons, atoms and bonds + * are symmetrical). + * Does not happen when alternate bonds of the central ring + * are treated as aromatic by attaching a (+) charge to the oxygen. + */ + } + } else + + { + int ret; + + if ( !nNumMappedBonds ) { + pCS->bStereoIsBetter = 0; /* the first stereo feature in the canonical CT has not been processed yet */ + } + + if ( nNumMappedBonds < pCS->nLenLinearCTStereoDble ) { + prevBond = pCS->LinearCTStereoDble[nNumMappedBonds]; + } + +/*deep_map_stereo_atoms4=0;*/ + + /* all stereo bonds have been mapped; now start processing stereo atoms... */ + ret = map_stereo_atoms4 ( ic, pCG, at, num_atoms, num_at_tg, num_max, nCanonRankFrom, nAtomNumberCanonFrom, nCanonRankTo, + nSymmRank, pRankStack1, pRankStack2, nTempRank, nNumMappedRanksInput, + nSymmStereo, NeighList, pCS, cur_tree, 0 , vABParityUnknown); + + if ( ret == 4 ) { + if ( nNumMappedBonds ) { + return ret; + } else { + pCS->bFirstCT = 1; + goto total_restart; + } + } + if ( RETURNED_ERROR(ret) ) { + if ( ret == CT_TIMEOUT_ERR ) + return ret; + else + return ret; /* program error */ + } + if ( ret > 0 ) { + nTotSuccess |= 1; + if ( ret & 2 ) { + CurTreeKeepLastAtomsOnly( cur_tree, tpos1, 1 ); /* start over */ + nTotSuccess |= 2; /* Obtained a smaller CT */ + } + } + } + if ( !nTotSuccess && pCS->nLenLinearCTStereoDble && + nNumMappedBonds < pCS->nLenLinearCTStereoDble ) { + pCS->LinearCTStereoDble[nNumMappedBonds] = prevBond; + } + + return nTotSuccess; /* ok */ +} + + +/**************************************************************************************** + */ +int map_stereo_atoms4 ( struct tagINCHI_CLOCK *ic, + CANON_GLOBALS *pCG, + sp_ATOM *at, + int num_atoms, + int num_at_tg, + int num_max, + const AT_RANK *nCanonRankFrom, + const AT_RANK *nAtomNumberCanonFrom, + AT_RANK *nCanonRankTo, /* canonical numbering to be mapped */ + const AT_RANK *nSymmRank, + AT_RANK **pRankStack1/*from*/, + AT_RANK **pRankStack2/*to*/, + AT_RANK *nTempRank, + int nNumMappedRanksInput, + AT_RANK *nSymmStereo, + NEIGH_LIST *NeighList, + CANON_STAT *pCS, + CUR_TREE *cur_tree, int nNumMappedAtoms , + int vABParityUnknown ) +{ +/* + * Do not check whether "from" atoms have any stereo features. + */ + int nTotSuccess = 0; + AT_STEREO_CARB prevAtom; + int tpos1; + + memset(&prevAtom, 0, sizeof(prevAtom)); + + tpos1 = CurTreeGetPos( cur_tree ); + + if ( nNumMappedAtoms < pCS->nLenLinearCTStereoCarb ) + { + /* AT_RANK *nRankFrom=*pRankStack1++, AT_RANK *nAtomNumberFrom=pRankStack1++; */ + /* AT_RANK *nRankTo =*pRankStack2++, AT_RANK *nAtomNumberTo =pRankStack2++; */ + int j1, at_from1, at_to1, /*at_from2, at_to2,*/ iMax, lvl, bStereoIsBetterWasSetHere; + int istk, istk2, bAddStack, nNumAtTo1Success, c, bFirstTime=1, bAllParitiesIdentical; + EQ_NEIGH EN[5], *pEN; + int nStackPtr[5], nMappedRanks[5], j[5], *nSP, *nMR, bLastLvlFailed; + + AT_RANK at_rank_canon1, cr[5], at_to[5]; + AT_RANK canon_rank1_min = 0; + int at_rank1; /* rank for mapping */ + int nNumChoices, nNumUnkn, nNumUndf, nNumWorse, nNumBest, nNumCalc; + int stereo_center_parity=0, prev_stereo_center_parity, sb_parity_calc, pass; + AT_STEREO_CARB prevAtom2; + + prevAtom = pCS->LinearCTStereoCarb[nNumMappedAtoms]; /* save to restore in case of failure */ + at_rank_canon1 = nNumMappedAtoms? pCS->LinearCTStereoCarb[nNumMappedAtoms-1].at_num:0; + + goto bypass_next_canon_rank_check; + +next_canon_rank: + + if ( !pCS->bStereoIsBetter /*??? && !pCS->bFirstCT ???*/ && + at_rank_canon1 >= pCS->LinearCTStereoCarb[nNumMappedAtoms].at_num) { + /* cannot find next available canonical number */ + if ( !nTotSuccess ) { + pCS->LinearCTStereoCarb[nNumMappedAtoms] = prevAtom; /* restore because of failure */ + } + CurTreeSetPos( cur_tree, tpos1 ); + return nTotSuccess; + } + +bypass_next_canon_rank_check: + + CurTreeSetPos( cur_tree, tpos1 ); + + /* find next available canon. number for a stereogenic atom */ + if ( !Next_SC_At_CanonRank2( &at_rank_canon1, &canon_rank1_min, &bFirstTime, + pCS->bAtomUsedForStereo, pRankStack1, pRankStack2, + nAtomNumberCanonFrom, num_atoms ) || + !pCS->bStereoIsBetter && + at_rank_canon1 > pCS->LinearCTStereoCarb[nNumMappedAtoms].at_num) { + /* cannot find next available canonical number */ + if ( !nTotSuccess ) { + pCS->LinearCTStereoCarb[nNumMappedAtoms] = prevAtom; /* restore because of failure */ + } + return nTotSuccess; + } + + nNumChoices = 0; + nNumUnkn = 0; + nNumUndf = 0; + nNumBest = 0; + nNumWorse = 0; + nNumCalc = 0; + pass = 0; + prev_stereo_center_parity = 0; + + /* get mapping rank for the canon. number */ + at_rank1 = pRankStack1[0][at_from1=(int)nAtomNumberCanonFrom[at_rank_canon1 - 1]]; + iMax = at_rank1-1; + /* for debug only */ + if ( at_rank1 != pRankStack2[0][pRankStack2[1][at_rank1-1]] ) + return CT_STEREOCOUNT_ERR; /* program error */ /* */ + + /* count special parities of the not mapped yet "to" atoms */ + for ( j1 = 0; j1 <= iMax && at_rank1 == pRankStack2[0][at_to1 =pRankStack2[1][iMax-j1]]; j1 ++ ) + { + if ( !at[at_to1].stereo_bond_neighbor[0] && pCS->bAtomUsedForStereo[at_to1] == STEREO_AT_MARK ) + { + int no_choice = 0; + stereo_center_parity = PARITY_VAL(at[at_to1].stereo_atom_parity); + switch(stereo_center_parity) { + + case AB_PARITY_UNDF: nNumUndf ++; break; /* 4 */ + + case AB_PARITY_UNKN: nNumUnkn ++; + break; /* 3 */ + + case BEST_PARITY: nNumBest ++; break; /* 1 */ + case WORSE_PARITY: nNumWorse ++; break; /* 2 */ + case AB_PARITY_CALC: nNumCalc ++; break; + case AB_PARITY_NONE: no_choice ++; break; /* 0 */ + } + nNumChoices += !no_choice; + } + } + + if ( nNumChoices != nNumCalc + nNumUndf + nNumUnkn + nNumBest + nNumWorse ) + { + return CT_STEREOCOUNT_ERR; /* program error */ /* */ + } + if ( !nNumChoices ) + { + goto next_canon_rank; + } + /* Determine the first parity to search */ + sb_parity_calc = ( nNumCalc > 0 )? BEST_PARITY : 0; + + /* ============================================================== + Search sequence: sb_parity_calc stereo_center_parity + ============================================================== + BEST_PARITY (calc) BEST_PARITY BEST_PARITY + BEST_PARITY (known) BEST_PARITY WORSE_PARITY or 0 + WORSE_PARITY (calc) WORSE_PARITY WORSE_PARITY + WORSE_PARITY (known) WORSE_PARITY 0 + AB_PARITY_UNKN(known) AB_PARITY_UNKN 0 + AB_PARITY_UNDF(known) AB_PARITY_UNDF 0 + + if (sb_parity_calc==stereo_center_parity) then "calc" else "known" + */ + +repeat_all: + + if ( !pass ++ ) + { + /* select the smallest parity to search */ + if ( sb_parity_calc ) + { + stereo_center_parity = BEST_PARITY; + } + else + { + stereo_center_parity = nNumBest? BEST_PARITY : + nNumWorse? WORSE_PARITY : + nNumUnkn? AB_PARITY_UNKN : + nNumUndf? AB_PARITY_UNDF : AB_PARITY_NONE; + } + } + else + { + prev_stereo_center_parity = stereo_center_parity; + j1 = NextStereoParity2Test( &stereo_center_parity, &sb_parity_calc, + nNumBest, nNumWorse, nNumUnkn, nNumUndf, nNumCalc, + vABParityUnknown ); + switch ( j1 ) + { + case 0: + break; /* obtained next parity to test */ + case 1: + goto next_canon_rank; + default: + return j1; /* program error */ + } + } + + if ( stereo_center_parity == AB_PARITY_NONE ) + { + /* error? */ + return CT_STEREOCOUNT_ERR; /* */ + } + + /* check if the new requested parity is small enough */ + if ( !pCS->bStereoIsBetter ) + { + c = CompareLinCtStereoAtomToValues( nTotSuccess? pCS->LinearCTStereoCarb+nNumMappedAtoms : &prevAtom, + at_rank_canon1, (U_CHAR)stereo_center_parity ); + if ( c < 0 ) + { + if ( !nTotSuccess ) + { + pCS->LinearCTStereoCarb[nNumMappedAtoms] = prevAtom; + } + CurTreeSetPos( cur_tree, tpos1 ); + return nTotSuccess; + } + } + + bAllParitiesIdentical = 0; + bStereoIsBetterWasSetHere = 0; + istk = istk2 = 0; + CurTreeSetPos( cur_tree, tpos1 ); /* start over */ + + /* + if ( prev_stereo_center_parity != stereo_center_parity ) { + CurTreeSetPos( cur_tree, tpos1 ); + } + */ + /* nRankTo nAtomNumberTo */ + for ( j1 = 0; j1 <= iMax && at_rank1 == pRankStack2[0][at_to1 =pRankStack2[1][iMax-j1]]; j1 ++ ) + { + int ret, ret1, ret2, parity1; + nNumAtTo1Success = 0; + /* + if ( !(at[at_to1].stereo_atom_parity && !at[at_to1].stereo_bond_neighbor[0] && + pCS->bAtomUsedForStereo[at_to1] == STEREO_AT_MARK ) ) + */ + if ( !at[at_to1].stereo_atom_parity || at[at_to1].stereo_bond_neighbor[0] || + pCS->bAtomUsedForStereo[at_to1] != STEREO_AT_MARK ) /* simplify 12-17-2003 */ + continue; + /* Do not map on non-stereogenic atom constitutionally + * equivalent to a steregenic atom. Here + * at[at_to1] is not a sterereo center; | | + * bonds tautomerism is a usual cause. -P(+)-CH=P- + * For example, consider a fragment: | | + * The two atoms P may be constitutionally + * equivalent, P(+) may be seen as a stereocenter + * while another P has a double bond (Now such a P(V) IS a stereocenter). + */ + /* check whether the stereocenter parity corresponds to the requested stereocenter parity */ + if ( PARITY_KNOWN(at[at_to1].stereo_atom_parity) ) { + if ( stereo_center_parity == sb_parity_calc ) { + continue; /* requested parity to be calculated, found known parity */ + } + if ( stereo_center_parity != PARITY_VAL(at[at_to1].stereo_atom_parity) ) { + continue; /* parity differs from the requested parity */ + } + } else + if ( PARITY_CALCULATE( at[at_to1].stereo_atom_parity) ) { + if ( stereo_center_parity != sb_parity_calc ) { + continue; /* requested known parity, found patity to be calculated */ + } + } else { + return CT_STEREOCOUNT_ERR; /* unknown parity type */ + } + + bAllParitiesIdentical = (( at[at_to1].stereo_atom_parity & KNOWN_PARITIES_EQL ) && + PARITY_KNOWN(at[at_to1].stereo_atom_parity)); + + if ( !bAllParitiesIdentical && !nNumCalc && + (!nNumUndf + !nNumUnkn + !nNumBest + !nNumWorse)==3 ) { + /* only one kind of stereocenter parity is present; check whether all parities are really same */ + bAllParitiesIdentical = All_SC_Same( at_rank_canon1, /* canonical number */ + pRankStack1, pRankStack2, + nAtomNumberCanonFrom, at ); + if ( bAllParitiesIdentical < 0 ) { + return CT_STEREOCOUNT_ERR; + } + } + if ( tpos1 < CurTreeGetPos( cur_tree ) && + 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) && + 1 == CurTreeIsLastAtomEqu( cur_tree, at_to1, nSymmStereo ) ) + continue; + + + /* initialize stack pointer nStackPtr[istk] for "hand-made" recursion */ + /* stacks are pRankStack1[], pRankStack2[], nNumMappedRanks[] */ + istk = 0; + nStackPtr[istk] = 0; + nMappedRanks[istk] = nNumMappedRanksInput; + bAddStack = 0; + /* if all equivalent atoms have same known parity, do not map any of them here */ + if ( !bAllParitiesIdentical ) { + /* map the central atom */ + /* this mapping is always possible */ + ret1 = map_an_atom2( pCG, num_at_tg, num_max, at_from1, at_to1, + nTempRank, nMappedRanks[istk], &nMappedRanks[istk+1], pCS, + NeighList, pRankStack1+nStackPtr[istk], pRankStack2+nStackPtr[istk], + &bAddStack ); + if ( RETURNED_ERROR(ret1) ) { + return ret1; /* error */ + } + nStackPtr[istk+1] = nStackPtr[istk] + bAddStack; + istk ++; + } else { + ClearPreviousMappings( pRankStack1+2 ); /* precaution */ + } + + + /********************************************************************************* + * + * Unknown Stereocenter Parity case: possibly need to map stereo center neighbors + */ + if ( stereo_center_parity == sb_parity_calc ) + { + /* find out the parity */ + parity1 = parity_of_mapped_atom2( pCG, at_from1, at_to1, at, &EN[istk], + nCanonRankFrom, pRankStack1[nStackPtr[istk]], + pRankStack2[nStackPtr[istk]] ); + /* if parity is well-defined then returned EN[istk].num_to=0 */ + if ( !parity1 ) { + return CT_STEREOCOUNT_ERR; /* program error */ /* */ + } + if ( !EN[istk].num_to && parity1 != sb_parity_calc ) { + continue; /* looking for the parity value = sb_parity_calc */ + } + } else { + /* Known parity */ + parity1 = stereo_center_parity; + EN[istk].num_to = 0; + } + + + + /*********************************************************************** + * no need to map the neighbors: parity is known or has been calculated + */ + if ( stereo_center_parity == sb_parity_calc && !EN[istk].num_to || + /* now well-defined, but unknown in advance atom parity OR */ + stereo_center_parity != sb_parity_calc ) + /* known in advance parity = stereo_center_parity */ + { + /* do not need to map the neighbors */ + c = CompareLinCtStereoAtomToValues( pCS->LinearCTStereoCarb+nNumMappedAtoms, + at_rank_canon1, (U_CHAR)parity1 ); + if ( c < 0 && !pCS->bStereoIsBetter ) { + /* reject */ + pCS->lNumRejectedCT ++; + continue; /* Reject: not a minimal CT. Should not happen */ + } else { + /* accept */ + + if ( bAddStack ) { + if ( tpos1 == CurTreeGetPos( cur_tree ) || + 0 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { + CurTreeAddRank( cur_tree, at_rank_canon1 ); + } + CurTreeAddAtom( cur_tree, at_to1 ); + } + + if ( c > 0 && !pCS->bStereoIsBetter ) { + /* stereo center entry is less than the previusly found */ + pCS->bStereoIsBetter = bStereoIsBetterWasSetHere = 1; + prevAtom2 = pCS->LinearCTStereoCarb[nNumMappedAtoms]; + } + pCS->LinearCTStereoCarb[nNumMappedAtoms].parity = parity1; + pCS->LinearCTStereoCarb[nNumMappedAtoms].at_num = at_rank_canon1; + pCS->bRankUsedForStereo[at_from1] = 3; +#if ( FIX_ChCh_STEREO_CANON_BUG == 1 ) + if ( !bAllParitiesIdentical ) +#endif + pCS->bAtomUsedForStereo[at_to1] -= STEREO_AT_MARK; + + + ret = map_stereo_atoms4 ( ic, pCG, + at, num_atoms, num_at_tg, num_max, + nCanonRankFrom, + nAtomNumberCanonFrom, + nCanonRankTo, + nSymmRank, + pRankStack1+nStackPtr[istk], + pRankStack2+nStackPtr[istk], + nTempRank, + nMappedRanks[istk], + nSymmStereo, + NeighList, + pCS, + cur_tree, + nNumMappedAtoms+1 , + vABParityUnknown); + + pCS->bRankUsedForStereo[at_from1] = 0; +#if ( FIX_ChCh_STEREO_CANON_BUG == 1 ) + if ( !bAllParitiesIdentical ) +#endif + pCS->bAtomUsedForStereo[at_to1] += STEREO_AT_MARK; + + if ( ret == 4 ) + { + return ret; + } + + if ( RETURNED_ERROR(ret) ) + { + if ( ret == CT_TIMEOUT_ERR ) + return ret; + else + return ret; /* program error */ + } + + if ( ret > 0 ) + { + nTotSuccess |= 1; + nNumAtTo1Success ++; + if ( bStereoIsBetterWasSetHere || (ret & 2) ) + { + CurTreeKeepLastAtomsOnly( cur_tree, tpos1, 1 ); /* start over */ + nTotSuccess |= 2; /* Obtained a smaller CT */ + } + } + else + { + if ( bStereoIsBetterWasSetHere ) + { + pCS->bStereoIsBetter = 0; + pCS->LinearCTStereoCarb[nNumMappedAtoms] = prevAtom2; + } + /* remove failed atom1 from the tree */ + + if ( tpos1 < CurTreeGetPos( cur_tree ) && + 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) + { + CurTreeRemoveIfLastAtom( cur_tree, at_to1 ); + CurTreeRemoveLastRankIfNoAtoms( cur_tree ); + } + } + bStereoIsBetterWasSetHere = 0; + } + + /* + if ( (at[at_to1].stereo_atom_parity & KNOWN_PARITIES_EQL ) && + ATOM_PARITY_KNOWN(stereo_center_parity) && !nSymmStereo ) { // ??? add && !nSymmStereo ??? + break; // do not repeat for the same kind of stereo atom with the parity known in advance + } + */ + if ( bAllParitiesIdentical ) { + break; /* do not repeat for the same kind of stereo atom with the parity known in advance */ + } + continue; + } + + /*************************************************** + * + * Need to map the neighbors + */ + if ( stereo_center_parity != sb_parity_calc ) { + return CT_STEREOCOUNT_ERR; /* program error */ /* */ + } + /* -- has already been calculated -- + parity1 = parity_of_mapped_atom2( pCG, at_from1, at_to1, at, &EN[istk], + nCanonRankFrom, pRankStack1[nStackPtr[istk]], pRankStack2[nStackPtr[istk]] ); + */ + if ( !parity1 ) { + return CT_STEREOCOUNT_ERR; /* 1/25/2002 */ /* */ + } + + if ( bAddStack ) { + if ( tpos1 == CurTreeGetPos( cur_tree ) || + 0 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { + CurTreeAddRank( cur_tree, at_rank_canon1 ); + } + CurTreeAddAtom( cur_tree, at_to1 ); + } + /****************************************************** + * Need to fix the neighbors to define the atom parity + ******************************************************/ + /* a recursion replaced with the hand-made stack */ + lvl = 0; /* the "recursion" depth level */ + nSP = &nStackPtr[istk]; + nMR = &nMappedRanks[istk]; + pEN = &EN[istk]; + bLastLvlFailed = 0; + + /* entering "recursion" depth level lvl */ +next_lvl: + if ( pEN[lvl].num_to ) { + /* Found tied neighbors. Try all transpositions of the tied neighbors. + * j is a number of the "to" tied neighbor in the pEN[lvl].to_at[*] to + * which the pEN[lvl].from_at "from" neighbor's canonical number is mapped + */ + j[lvl] = 0; +next_j: + cr[lvl] = nCanonRankFrom[pEN[lvl].from_at]; + at_to[lvl] = pEN[lvl].to_at[j[lvl]]; + if ( j[lvl] && tpos1 < CurTreeGetPos( cur_tree ) && + 1 == CurTreeIsLastRank( cur_tree, cr[lvl] ) && + 1 == CurTreeIsLastAtomEqu( cur_tree, at_to[lvl], nSymmStereo ) ) { + lvl ++; + bLastLvlFailed = 0; + goto backup; /* do not test stereo equivalent atoms except the first one */ + } + + ret2 = map_an_atom2( pCG, num_at_tg, num_max, + pEN[lvl].from_at, /* from */ + pEN[lvl].to_at[j[lvl]], /* to */ + nTempRank, nMR[lvl], &nMR[lvl+1], pCS, + NeighList, pRankStack1+nSP[lvl], pRankStack2+nSP[lvl], + &bAddStack ); + + if ( RETURNED_ERROR(ret2) ) { + return ret2; /* program error */ + } + + /* next recursion depth level */ + if ( bAddStack ) { + if ( tpos1 == CurTreeGetPos( cur_tree ) || + 0 == CurTreeIsLastRank( cur_tree, cr[lvl] ) ) { + CurTreeAddRank( cur_tree, cr[lvl] ); + } + CurTreeAddAtom( cur_tree, at_to[lvl] ); + } + nSP[lvl+1] = nSP[lvl] + bAddStack; + lvl ++; /* upon increment lvl = number of additionally mapped neighbors + * (entering next recursion level) */ + + /* check if the mapping has defined the parity */ + parity1 = parity_of_mapped_atom2( pCG, at_from1, at_to1, at, &pEN[lvl], + nCanonRankFrom, pRankStack1[nSP[lvl]], pRankStack2[nSP[lvl]] ); + + if ( !parity1 ) { + return CT_STEREOCOUNT_ERR; /* 1/25/2002 */ /* */ + } + if ( parity1 < 0 ) { + goto next_lvl; /* we need at least one more mapping to define the parity */ + } + + /********************************************************** + * + * Check the parity + * + ********************************************************** + * make a decision whether to accept the current mapping */ + + c = CompareLinCtStereoAtomToValues( pCS->LinearCTStereoCarb+nNumMappedAtoms, + at_rank_canon1, (U_CHAR)parity1 ); + if ( sb_parity_calc != parity1 || + c < 0 && !pCS->bStereoIsBetter ) { + pCS->lNumRejectedCT ++; + bLastLvlFailed = 1; + } else + /* the parity has been defined (all neighbors have untied ranks) */ + /* if ( bAcceptAllParities || parity1 == BEST_PARITY ) */ + { + /********************************************************************* + * + * Process the parity here. We are at the top of the recursion stack. + * + *********************************************************************/ + /* try to accept current neighbors mapping */ + if ( c > 0 && !pCS->bStereoIsBetter ) { + pCS->bStereoIsBetter = bStereoIsBetterWasSetHere = 1; + prevAtom2 = pCS->LinearCTStereoCarb[nNumMappedAtoms]; + } + pCS->LinearCTStereoCarb[nNumMappedAtoms].parity = parity1; + pCS->LinearCTStereoCarb[nNumMappedAtoms].at_num = at_rank_canon1; + pCS->bRankUsedForStereo[at_from1] = 3; + pCS->bAtomUsedForStereo[at_to1] -= STEREO_AT_MARK; + + ret = map_stereo_atoms4 ( ic, pCG, at, num_atoms, num_at_tg, num_max, nCanonRankFrom, nAtomNumberCanonFrom, nCanonRankTo, + nSymmRank, pRankStack1+nSP[lvl], pRankStack2+nSP[lvl], + nTempRank, nMR[lvl], nSymmStereo, NeighList, + pCS, cur_tree, nNumMappedAtoms+1 , + vABParityUnknown ); + + pCS->bRankUsedForStereo[at_from1] = 0; + pCS->bAtomUsedForStereo[at_to1] += STEREO_AT_MARK; + if ( ret == 4 ) { + return ret; + } + if ( RETURNED_ERROR(ret) ) { + if ( ret == CT_TIMEOUT_ERR ) + return ret; + else + return ret; /* program error */ + } + if ( ret > 0 ) { + nTotSuccess |= 1; + nNumAtTo1Success ++; + if ( bStereoIsBetterWasSetHere || (ret & 2) ) { + CurTreeKeepLastAtomsOnly( cur_tree, tpos1, 1 ); /* start over */ + nTotSuccess |= 2; /* Obtained a smaller CT */ + } + } else { + if ( bStereoIsBetterWasSetHere ) { + pCS->bStereoIsBetter = 0; + pCS->LinearCTStereoCarb[nNumMappedAtoms] = prevAtom2; + } + bLastLvlFailed = 1; + } + bStereoIsBetterWasSetHere = 0; + + /* avoid redundant repetitions: */ + /* check if neighbors mappings have altered another stereo center parity */ + if ( !nSymmStereo && !might_change_other_atom_parity( at, num_atoms, at_to1, + pRankStack2[nSP[lvl]] /* ranks after neigbors mapping */, + pRankStack2[nStackPtr[istk]] /* ranks before the mapping neighbors */) ) { + goto done; + } + } + /* Continue the cycle. Go to the previous "recursion" level */ +backup: + while (lvl -- > 0 ) { + + j[lvl] ++; /* next neighbor at this level */ + if ( j[lvl] < pEN[lvl].num_to ) { + if ( bLastLvlFailed ) { + if ( tpos1 < CurTreeGetPos( cur_tree ) && + 1 == CurTreeIsLastRank( cur_tree, cr[lvl] ) ) { + CurTreeRemoveIfLastAtom( cur_tree, at_to[lvl] ); + CurTreeRemoveLastRankIfNoAtoms( cur_tree ); + } + bLastLvlFailed = 0; + } + /* Done with this level. Go back one level */ + goto next_j; + } + /* remove failed atom from the tree */ + if ( tpos1 < CurTreeGetPos( cur_tree ) && + 1 == CurTreeIsLastRank( cur_tree, cr[lvl] ) ) { + CurTreeRemoveLastRank( cur_tree ); + } + } + goto done; + } else { + cr[lvl] = 0; + } + +done:; /* at this point lvl=0. */ + if ( !nNumAtTo1Success ) { + if ( tpos1 < CurTreeGetPos( cur_tree ) && + 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { + CurTreeRemoveIfLastAtom( cur_tree, at_to1 ); + CurTreeRemoveLastRankIfNoAtoms( cur_tree ); + } + } + } /* end of stereo atom mapping cycle */ + + if ( tpos1 < CurTreeGetPos( cur_tree ) && + 1 == CurTreeIsLastRank( cur_tree, at_rank_canon1 ) ) { + CurTreeRemoveLastRank( cur_tree ); + } else + /* CurTree consistency check (debug only) */ + if ( tpos1 != CurTreeGetPos( cur_tree ) ) { + return CT_STEREOCOUNT_ERR; /* */ + } + + if ( !nTotSuccess || stereo_center_parity == sb_parity_calc ) { + goto repeat_all; /* repeat with next parity if no success or with the same parity, now known */ + } + } + else + { + + /**************************************************** + * + * All stereogenic atoms and bonds have been mapped + * + ****************************************************/ + + if ( UserAction && USER_ACTION_QUIT == (*UserAction)() || + ConsoleQuit && (*ConsoleQuit)() ) + { + return CT_USER_QUIT_ERR; + } + + if ( pCS->bStereoIsBetter || pCS->bFirstCT ) + { + /* All stereo atoms have been mapped. Current stereo name is better than all previous. + * Create new numbering for the new CT + * break all remaining "from" ties + */ + int i1, ret; + AT_RANK rc, n1, n2; + + ret=BreakAllTies( pCG, num_at_tg, num_max, pRankStack1, + NeighList, nTempRank, pCS); + + if ( RETURNED_ERROR( ret ) ) { + return ret; + } + + /* break all remaining "from" ties */ + ret=BreakAllTies( pCG, num_at_tg, num_max, pRankStack2, + NeighList, nTempRank, pCS); + + if ( RETURNED_ERROR( ret ) ) { + return ret; + } + /* move stack pointers to the "nAtomNumber[*]" after all ties are broken */ + pRankStack1 += 2; + pRankStack2 += 2; + /* Now final mapping ranks of "to" atom (*pRankStack2)[i] and "from" atom (*pRankStack1)[i] + * are equal and all ranks are different, that is, we have a full mapping + * Copy so far best canonical numbering from "from" to "to". + */ + memset( pCS->nPrevAtomNumber, 0, num_at_tg*sizeof(pCS->nPrevAtomNumber[0]) ); + for ( i1 = 0; i1 < num_at_tg; i1 ++ ) { + n1 = pRankStack1[1][i1]; + rc = nCanonRankFrom[n1]; /* new canon. rank */ + n2 = pRankStack2[1][i1]; /* orig. atom number */ + nCanonRankTo[n2] = rc; /* assign new canon. number to the atom */ + /* use this array to find stereo-equivalent atoms */ + pCS->nPrevAtomNumber[rc-1] = n2; /* ord. number of the atom having canon. rank = rc */ + nSymmStereo[i1] = i1; /* restart search for stereo equivalent atoms */ + /* check mapping correctness */ + if ( pRankStack1[0][n1] != pRankStack2[0][n2] || + nSymmRank[n1] != nSymmRank[n2] ) { + return CT_STEREO_CANON_ERR; /* stereo mapping error */ + } + } + /* statistics */ + pCS->lNumTotCT ++; + pCS->lNumEqualCT = 1; + pCS->lNumDecreasedCT ++; + pCS->bStereoIsBetter = 0; /* prepare to start over */ + nTotSuccess = 1; + pCS->bFirstCT = 0; + +#if ( REMOVE_CALC_NONSTEREO == 1 ) /* { */ + if ( !(pCS->nMode & CMODE_REDNDNT_STEREO ) ) { + i1 = RemoveCalculatedNonStereo( pCG, at, num_atoms, num_at_tg, + pRankStack1, pRankStack2, nTempRank, NeighList, + nSymmRank, nCanonRankTo, pCS->nPrevAtomNumber, pCS, + vABParityUnknown); + if ( RETURNED_ERROR( i1 ) ) { + return i1; + } + if ( i1 < 0 ) { +#if ( bRELEASE_VERSION == 0 ) + pCS->bExtract |= EXTR_REMOVE_PARITY_WARNING; +#endif + i1 = -(1+i1); + } + if ( i1 > 0 ) { + return 4; /* total restart: due to newly found stereo equivalence */ + /* the length of the stereo CT has changed */ + } + } +#endif /* } REMOVE_CALC_NONSTEREO */ + + pRankStack1 -= 2; + pRankStack2 -= 2; + } + else + { + /* current stereo name is same as previous. We do not need a full mapping. */ + if ( nSymmStereo ) { + int num_changes = 0; + AT_RANK r, n1, n2, r_max, cr; + r_max = (AT_RANK)num_at_tg; + for ( r = 1; r <= r_max; r ++ ) { + if ( bUniqueAtNbrFromMappingRank( pRankStack1, r, &n1 ) ) { + if ( bUniqueAtNbrFromMappingRank( pRankStack2, r, &n2 ) ) { + /* atoms at[n1], at[n2] have identical untied mapping rank r */ + cr = nCanonRankFrom[(int)n1]-1; /* (new at[n2] canonical rank)-1 */ + /* pCS->nPrevAtomNumber[(int)cr] = */ + /* previous ordering number of an atom with the canon. rank = cr+1; */ + /* make this atom equivalent to atom at[n2]: */ + num_changes += nJoin2Mcrs( nSymmStereo, pCS->nPrevAtomNumber[(int)cr], n2 ); + } else { + return CT_MAPCOUNT_ERR; /* mapping ranks must be either both tied or untied. */ /* */ + } + } + } + if ( num_changes ) { /* compress trees to stars */ + for ( r = r_max-1; r; r -- ) { + nGetMcr( nSymmStereo, r ); + } + } + } + /* statistics */ + pCS->lNumEqualCT ++; + pCS->lNumTotCT ++; + nTotSuccess = 1; + } + if ( bInchiTimeIsOver( ic, pCS->ulTimeOutTime ) ) { + return CT_TIMEOUT_ERR; + } + } + if ( !nTotSuccess && nNumMappedAtoms < pCS->nLenLinearCTStereoCarb ) { + pCS->LinearCTStereoCarb[nNumMappedAtoms] = prevAtom; + CurTreeSetPos( cur_tree, tpos1 ); + } + return nTotSuccess; /* return to the previous level of the recursion. */ +} diff --git a/INCHI-1-SRC/INCHI/common/ichinorm.c b/INCHI-1-SRC/INCHI_BASE/src/ichinorm.c similarity index 85% rename from INCHI-1-SRC/INCHI/common/ichinorm.c rename to INCHI-1-SRC/INCHI_BASE/src/ichinorm.c index 810514d..c841824 100644 --- a/INCHI-1-SRC/INCHI/common/ichinorm.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichinorm.c @@ -1,3346 +1,3055 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - -#include "mode.h" - -#include "inpdef.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "ichierr.h" -#include "util.h" - -#include "ichicomp.h" - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -/* defined in ichisort.c, prototype in ichicomn.h */ -int insertions_sort_AT_RANK( AT_RANK *base, int num ); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -/* local prototypes */ -int cmp_iso_atw_diff_component_no( const void *a1, const void *a2 ); - -/********************************************************************************/ -int cmp_iso_atw_diff_component_no( const void *a1, const void *a2 ) -{ - int ret = (int)((const inp_ATOM*)a1)->iso_atw_diff - (int)((const inp_ATOM*)a2)->iso_atw_diff; - if ( !ret ) /* make the sort stable */ - ret = (int)((const inp_ATOM*)a1)->component - (int)((const inp_ATOM*)a2)->component; - return ret; -} -typedef struct tagTreeAtom { - AT_NUMB neighbor[MAXVAL]; /* positions (from 0) of the neighbors in the inp_ATOM array */ - S_CHAR valence; /* number of bonds = number of neighbors */ - AT_NUMB nRingSystem; - AT_NUMB nBlockSystem; - S_CHAR bCutVertex; -} tre_ATOM; - -#if ( FIND_RING_SYSTEMS == 1 ) /* { */ -/********************************************************************************/ -int MarkRingSystemsInp( inp_ATOM *at, int num_atoms, int start ) -{ - AT_NUMB *nStackAtom = NULL; - int nTopStackAtom=-1; - AT_NUMB *nRingStack = NULL; - int nTopRingStack=-1; /* was AT_NUMB */ - AT_NUMB *nDfsNumber = NULL; - AT_NUMB *nLowNumber = NULL; - S_CHAR *cNeighNumb = NULL; - AT_NUMB nDfs; -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) - AT_NUMB nRs, *nRsConnect = NULL; - int k; - AT_NUMB *tree = NULL; - int nNumConnect, nMaxNumConnect, nLenConnect; -#endif - AT_NUMB nNumAtInRingSystem; - int i, j, u, /*start,*/ nNumRingSystems, nNumStartChildren; - - /* allocate arrays */ - nStackAtom = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nStackAtom[0])); - nRingStack = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nRingStack[0])); - nDfsNumber = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nDfsNumber[0])); - nLowNumber = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nLowNumber[0])); - cNeighNumb = (S_CHAR *) inchi_malloc(num_atoms*sizeof(cNeighNumb[0])); -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) - nRsConnect = (AT_NUMB *)inchi_calloc(3*num_atoms+3,sizeof(nRsConnect[0])); -#endif - /* check allocation */ - if ( !nStackAtom || !nRingStack || !nDfsNumber || !nLowNumber || !cNeighNumb -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) - || !nRsConnect -#endif - ) { - nNumRingSystems = CT_OUT_OF_RAM; /* program error */ /* */ - goto exit_function; - } - - /******************************************** - * - * Find Cut-vertices & Blocks - * - ********************************************/ - - /* initiation */ - /*start = 0;*/ - nNumRingSystems = 0; - u = start; /* start atom */ - nDfs = 0; - nTopStackAtom =-1; - nTopRingStack =-1; - memset( nDfsNumber, 0, num_atoms*sizeof(nDfsNumber[0])); - memset( cNeighNumb, 0, num_atoms*sizeof(cNeighNumb[0])); - /* push the start atom on the stack */ - nLowNumber[u] = nDfsNumber[u] = ++nDfs; - nStackAtom[++nTopStackAtom] = (AT_NUMB)u; - nRingStack[++nTopRingStack] = (AT_NUMB)u; - - nNumStartChildren = 0; - - do { - /* advance */ -advance_block: - /*if ( (int)at[i=nStackAtom[nTopStackAtom]].valence > (j = (int)cNeighNumb[i]) )*/ - /* replaced due to missing sequence point */ - if ( i=(int)nStackAtom[nTopStackAtom], j = (int)cNeighNumb[i], (int)at[i].valence > j ) - { - cNeighNumb[i] ++; - u = (int)at[i].neighbor[j]; - if ( !nDfsNumber[u] ) { - /* tree edge, 1st visit -- advance */ - nStackAtom[++nTopStackAtom] = (AT_NUMB)u; - nRingStack[++nTopRingStack] = (AT_NUMB)u; - nLowNumber[u] = nDfsNumber[u] = ++nDfs; - nNumStartChildren += (i == start); - } else - if ( !nTopStackAtom || u != (int)nStackAtom[nTopStackAtom-1] ) { /* may comment out ? */ - /* back edge: u is not a predecessor of i */ - if ( nDfsNumber[u] < nDfsNumber[i] ) { - /* Back edge, 1st visit: u is an ancestor of i. Compare */ - if ( nLowNumber[i] > nDfsNumber[u] ) { - nLowNumber[i] = nDfsNumber[u]; - } - } - } /* may comment out ? */ - goto advance_block; - } else { - cNeighNumb[i] = 0; - } - - /* back up */ - if ( i != start ) { - u = (int)nStackAtom[nTopStackAtom-1]; /* predecessor of i */ - if ( nLowNumber[i] >= nDfsNumber[u] ) { - /* output the block */ - nNumRingSystems ++; - at[u].nBlockSystem = nNumRingSystems; - if ( u != start || nNumStartChildren > 1 ) { - at[u].bCutVertex += 1; - } - while ( nTopRingStack >= 0 ) { - j = nRingStack[nTopRingStack--]; - at[j].nBlockSystem = nNumRingSystems; /* mark the atom */ - if ( i == j ) { - break; - } - } - } else - if ( nLowNumber[u] > nLowNumber[i] ) { - /* inherit */ - nLowNumber[u] = nLowNumber[i]; - } - } - } while ( --nTopStackAtom >= 0 ); - - - /******************************************** - * - * Find Ring Systems - * Including chain atoms X: A-X-B, where the bonds (of any kind) are bridges. - * - ********************************************/ - - /* initiation */ - /*start = 0;*/ - nNumRingSystems = 0; - u = start; /* start atom */ - nDfs = 0; - nTopStackAtom =-1; - nTopRingStack =-1; - memset( nDfsNumber, 0, num_atoms*sizeof(nDfsNumber[0])); - memset( cNeighNumb, 0, num_atoms*sizeof(cNeighNumb[0])); - /* push the start atom on the stack */ - nLowNumber[u] = nDfsNumber[u] = ++nDfs; - nStackAtom[++nTopStackAtom] = (AT_NUMB)u; - nRingStack[++nTopRingStack] = (AT_NUMB)u; -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) - nNumConnect = nLenConnect = nMaxNumConnect = 0; -#endif - - do { - /* advance */ -advance_ring: - /*if ( (int)at[i=nStackAtom[nTopStackAtom]].valence > (j = (int)cNeighNumb[i]) )*/ - /* replaced due to missing sequence point */ - if ( i=(int)nStackAtom[nTopStackAtom], j = (int)cNeighNumb[i], (int)at[i].valence > j ) - { - cNeighNumb[i] ++; - u = (int)at[i].neighbor[j]; - if ( !nDfsNumber[u] ) { - /* tree edge, 1st visit -- advance */ - nStackAtom[++nTopStackAtom] = (AT_NUMB)u; - nRingStack[++nTopRingStack] = (AT_NUMB)u; - nLowNumber[u] = nDfsNumber[u] = ++nDfs; - } else - if ( !nTopStackAtom || u != (int)nStackAtom[nTopStackAtom-1] ) { - /* back edge: u is not a predecessor of i */ - if ( nDfsNumber[u] < nDfsNumber[i] ) { - /* Back edge, 1st visit: u is ancestor of i. Compare */ - if ( nLowNumber[i] > nDfsNumber[u] ) { - nLowNumber[i] = nDfsNumber[u]; - } - } - } - goto advance_ring; - } else { - cNeighNumb[i] = 0; - } - - /* back up */ - if ( nDfsNumber[i] == nLowNumber[i] ) { - /* found a ring system */ - nNumRingSystems ++; - /* unwind nRingStack[] down to i */ -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) - nNumConnect = 2; - /* data structure: for each ring system nRsConnect[] contains: - * 1) nNumConnect+1 = (number of already discovered neighboring "ring systems" + 1)+1 - * 2) nNumAtInRingSystem - * 3) (nNumConnect-1) numbers (IDs) of neighboring ring systems. - * BFS guarantees that each neighboring ring system is encountered only one time - * Number of all neighboring ring systems = (nNumConnect-1)+1 = nNumConnect - * (One additional ring system is where the BFS retracts from the vertex #i, - * except when i=DFS root node. In the latter case there is/are only (nNumConnect-1) - * neighboring ring system(s). - */ -#endif - /* count atoms in a ring system */ - for ( nNumAtInRingSystem = 0, j = nTopRingStack; 0 <= j; j -- ) { - nNumAtInRingSystem ++; - if ( i == (int)nRingStack[j] ) { - break; - } - } - while ( nTopRingStack >= 0 ) { - j = (int)nRingStack[nTopRingStack--]; - at[j].nRingSystem = (AT_NUMB)nNumRingSystems; /* ring system id */ - at[j].nNumAtInRingSystem = nNumAtInRingSystem; -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) - for ( k = 0; k < at[j].valence; k ++ ) { - if ( (nRs = at[at[j].neighbor[k]].nRingSystem) && (int)nRs != nNumRingSystems ) { - nRsConnect[nLenConnect + (nNumConnect++)] = nRs; /* adjacent ring system id */ - } - } -#endif - if ( i == j ) { - /* reached atom on the top of nStackAtom[] stack */ - break; - } - } -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) - nRsConnect[nLenConnect] = nNumConnect; - nRsConnect[nLenConnect+1] = nNumAtInRingSystem; - nLenConnect += nNumConnect; - if ( nMaxNumConnect < nNumConnect ) { - /* max number of neighboring ring systems */ - nMaxNumConnect = nNumConnect; - } -#endif - } else - if ( nTopStackAtom > 0 ) { - j = (int)nStackAtom[nTopStackAtom-1]; - /* inherit nLowNumber */ - if ( nLowNumber[j] > nLowNumber[i] ) { - nLowNumber[j] = nLowNumber[i]; - } - } - } while ( --nTopStackAtom >= 0 ); - -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) /* normally disabled */ - nMaxNumConnect ++; - if ( nNumRingSystems > 1 ) { - int nCol = nMaxNumConnect+1; - int nNumInSyst= nMaxNumConnect; - int nMaxNeigh = nMaxNumConnect-1; -#define T(a,b) tree[(a)*nCol+b] - if ( tree = (AT_NUMB *)inchi_calloc( nCol * (nNumRingSystems+1), sizeof(tree[0])) ) { - int len, neigh; - /* reuse previous allocations */ - AT_NUMB *nNumVisitedNeighbors = nStackAtom; - AT_NUMB *nDistanceFromTerminal = nRingStack; - AT_NUMB *nCurrActiveRingSystem = nDfsNumber; - AT_NUMB *nNextActiveRingSystem = nLowNumber; - int nNumCurrActiveRingSystems, nNumNextActiveRingSystems, pass; - /* build a "condensation graph (actually, a tree)" in which - * each vertex corresponds to a ring system T(row, col) = T(ring syst, neighbors) - * Number of rows = column length = max. number of ring system neighbors + 2 - * Number of cols = row length = number of ring systems + 1 - * Neighboring ring systems are contiguously stored in a row - * T(i,0) = number of neighbors, 1 <= i <= nNumRingSystems; - * T(i,k) = number of a neighboring ring system, 1 <= k <= T(i,0) - * T(i,nCol-1) = number of atoms in the system #i - */ - for ( i = 1, j = 0; len=nRsConnect[j]; i ++ ) { - T(i, nNumInSyst) = nRsConnect[j+1]; - for ( k = 2; k < len; k ++ ) { - neigh = nRsConnect[j+k]; - if ( T(i,0) < nMaxNeigh && T(neigh,0) < nMaxNeigh ) { - T(i,0) ++; - T(neigh,0) ++; - T(i,T(i,0)) = neigh; - T(neigh,T(neigh,0)) = i; - } else { - nNumRingSystems = CT_OVERFLOW; /* program error */ /* */ - goto exit_function; - } - } - j += len; - } - /* clear memory */ - memset( nNumVisitedNeighbors, 0, nNumRingSystems*sizeof(nNumVisitedNeighbors[0]) ); - memset( nDistanceFromTerminal, 0, nNumRingSystems*sizeof(nDistanceFromTerminal[0]) ); - memset( nCurrActiveRingSystem, 0, nNumRingSystems*sizeof(nCurrActiveRingSystem[0]) ); - memset( nNextActiveRingSystem, 0, nNumRingSystems*sizeof(nNextActiveRingSystem[0]) ); - nNumNextActiveRingSystems = 0; - for ( i = 0; i < nNumRingSystems; i ++ ) { - if ( 1 == T(i+1,0) ) { - nNextActiveRingSystem[i] = 1; /* number of traversed neighbors + 1 */ - nDistanceFromTerminal[i] = 1; - nNumNextActiveRingSystems ++; - } else { - nNextActiveRingSystem[i] = 0; - nDistanceFromTerminal[i] = 0; - } - nNumVisitedNeighbors[i] = 0; - } - - /* nCurrActiveRingSystem[i] = a sum of: - * 1) +1 if it is or was active - * 2) +(number of neighbors from which it was reached) - * 3) +1 if it was left and not active anymore - */ - pass = 0; - do { - nNumCurrActiveRingSystems = nNumNextActiveRingSystems; - nNumNextActiveRingSystems = 0; - memcpy( nCurrActiveRingSystem, nNextActiveRingSystem, - nNumRingSystems*sizeof(nNextActiveRingSystem[0])); - for ( i = 0; i < nNumRingSystems; i ++ ) { - if ( T(i+1,0) == nCurrActiveRingSystem[i] ) { - /* on the previous pass currently active ring system i+1 bas been reached - * from all neighbors except one; - * the neighbors from which it was reached have - * T(neigh,0)+1 == nCurrActiveRingSystem[i] - * this ring system has not been left yet - */ - for ( k = 1, len=T(i+1,0); k <= len; k ++ ) { - neigh = (int)T(i+1,k); - if ( T(neigh,0) >= nCurrActiveRingSystem[neigh-1] ) { - if ( 0 == pass ) { - nDistanceFromTerminal[i] = 1; - } - break; - } - } - if ( k <= len ) { - /* neigh was not reached from at least 2 neighbors - * walk along -R- chain (T(neigh,0)==2) up to - * 1) a terminal system, not including it or - * 2) a branching point. - * - * pass = 0: started from terminal systems: - * reach the branching point. - * If chain system next to a terminal system has already been reached - * then walk along it according to Note below - * - * pass > 0: started from branching points - * 2a) If the branching point has not been reached from 2 or more neighbors, - * then include it - * 2b) If the branching point has not been reached from 1 neighbor only, - * then do not include it: it will be a starting point later - * Note: if a chain atom already has nDistanceFromTerminal[i] > 0, then - * the last atom should be the one such that - * its nDistanceFromTerminal[]+1>= nDistanceFromTerminal[] of the - * next in the chain - */ - int bOk = 0; - k = i+1; /* starting point */ - if ( 0 == pass && T(k,nNumInSyst) > 1 ) { - nNumNextActiveRingSystems ++; /* request next pass */ - continue; /* stop a the terminal ring system */ - } - while( 2 == T(neigh,0) ) { - /* walk along a chain */ - if ( !nNextActiveRingSystem[neigh-1] ) { - nNextActiveRingSystem[neigh-1] = 1; /* make neighbor active */ - } else - if ( nDistanceFromTerminal[k-1]+1 <= nDistanceFromTerminal[neigh-1] ) { - /* walking along the chain; already have had a walk */ - /* in the opposite direction at this pass */ - } else { - /* k is the last; neigh (it is a bridge -X-) has not been reached */ - bOk = 1; - break; - } - nNextActiveRingSystem[k-1] ++; /* leave system k */ - if ( nNextActiveRingSystem[neigh-1] < T(neigh,0) ) { - nNextActiveRingSystem[neigh-1] ++; /* add one connection to neigh */ - } - nDistanceFromTerminal[neigh-1] = nDistanceFromTerminal[k-1]+1; - j = (T(neigh,1)==k)? 2:1; - k = neigh; - neigh = T(k,j); /* next in the chain */ - nNumNextActiveRingSystems ++; - if ( T(k,nNumInSyst) > 1 ) { - bOk = 1; - break; /* stop on a ring system */ - } - } - /* neigh is a terminal or a bridge or a branching point */ - if ( 2 > T(neigh,0) ) { - /* neighbor is a terminal atom */ - if ( 1 < pass ) { - nNumRingSystems = CT_UNKNOWN_ERR; /* error (debug only) */ /* */ - goto exit_function; - } - continue; - } - if ( 2 == T(neigh,0) ) { - /* neighbor is a bridge */ - continue; - } - /* neighbor is a branching point */ - if ( T(neigh,0) > nCurrActiveRingSystem[neigh-1] ) { - /* move to the neigh (make neigh active): on previous pass it */ - /* has not been reached from 2 or more neighbors */ - if ( !nNextActiveRingSystem[neigh-1] ) { - nNextActiveRingSystem[neigh-1] = 1; - } - if ( nDistanceFromTerminal[neigh-1] < nDistanceFromTerminal[k-1]+1 ) { - nDistanceFromTerminal[neigh-1] = nDistanceFromTerminal[k-1]+1; - } - nNextActiveRingSystem[k-1] ++; /* leave system k */ - if ( nNextActiveRingSystem[neigh-1] < T(neigh,0) ) { - nNextActiveRingSystem[neigh-1] ++; /* add one connection to neigh */ - } - nNumNextActiveRingSystems ++; - } - } - } - } - pass ++; - } while ( nNumNextActiveRingSystems ); - - for ( i = 0; i < num_atoms; i ++ ) { - at[i].nDistanceFromTerminal = nDistanceFromTerminal[(int)at[i].nRingSystem-1]; - } - - inchi_free( tree ); - tree = NULL; -#undef T - } else { - nNumRingSystems = CT_OUT_OF_RAM; /* error */ /* */ - goto exit_function; - } - } -#endif - - -exit_function: - if ( nStackAtom ) - inchi_free( nStackAtom ); - if ( nRingStack ) - inchi_free( nRingStack ); - if ( nDfsNumber ) - inchi_free( nDfsNumber ); - if ( nLowNumber ) - inchi_free( nLowNumber ); - if ( cNeighNumb ) - inchi_free( cNeighNumb ); -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) - if ( nRsConnect ) - inchi_free( nRsConnect ); - if ( tree ) - inchi_free( tree ); -#endif - return nNumRingSystems; -} - - -#endif /* } FIND_RING_SYSTEMS */ - -/********************************************************************************/ -/* Return value: new number of atoms > 0 or -1=out of RAM */ -int remove_terminal_HDT( int num_atoms, inp_ATOM *at, int bFixTermHChrg ) -{ - AT_NUMB *new_ord; - inp_ATOM *new_at; - char *p; - const static char szHDT[]="HDT"; - const static int kMax = sizeof(szHDT); /* = 4 */ - int ret = -1; - int num_hydrogens=0, num_H = 0; /* number of terminal H, D, T */ - int i, j, k, n, m; - int val; - AT_RANK new_HydrogenAt_order[NUM_H_ISOTOPES+1]; - AT_RANK new_OtherNeigh_order[MAXVAL]; - S_CHAR old_trans[MAX_NUM_STEREO_BONDS]; - - int num_OtherNeigh, num_HydrogenAt; - - new_ord=(AT_NUMB *)inchi_calloc(num_atoms, sizeof(new_ord[0])); /* changed malloc to calloc 9-11-2003 */ - new_at =(inp_ATOM *) inchi_malloc(sizeof(new_at[0]) *num_atoms); - if (!new_ord || !new_at) - goto exit_function; - - /* move H. D, T to the end of the list of atoms */ - for ( i = 0; i < num_atoms; i ++ ) - { - at[i].component = i; /* temporarily save original numbering */ - /* get k = temp. hydrogen isotope/non-hydrogen atom type: */ - /* k=0:H, k=2:D, k=3:T, k=4=kMax: not a hydrogen */ - k = at[i].elname[1]? kMax : (p=(char*)strchr(szHDT, at[i].elname[0]))? p-szHDT : kMax; - /* set hydrogen isotope atw differences */ - /* Notes: k-value of isotopic H is incremented to correct iso_atw_diff value later. */ - /* 1H isotope cannot be detected here. */ - if ( k == ATW_H || k == ATW_H+1 ) - { - /* D or T, k = 1 or 2 */ - at[i].elname[0] = 'H'; /* hydrogen isotope */ - at[i].iso_atw_diff = ++k; /* increment k to make k = iso_atw_diff ( 2 for D, 3 for T ) */ - } - num_H += (k != kMax && at[i].valence == 1 && at[i].chem_bonds_valence == 1 && !NUMH(at,i) ); - } - - /* special case: HD, HT, DT, HH: the only non-isotopic H or - * the lightest isotopic H out of two is removed - * to become implicit (make the heavier H the "central atom"). - * Note: This must be consistent with mol_to_atom() - * treatment of isotopic Hn aliases. - */ - if ( 2 == num_H && 2 == num_atoms && !NUMH(at,0) && !NUMH(at,1) ) - { - - if ( at[0].iso_atw_diff >= at[1].iso_atw_diff ) { - new_ord[0] = 0; - new_ord[1] = 1; - } else { - new_ord[0] = 1; - new_ord[1] = 0; - } - if ( at[new_ord[1]].charge ) { - at[new_ord[0]].charge += at[new_ord[1]].charge; - at[new_ord[1]].charge = 0; - } - new_at[new_ord[0]] = at[0]; - new_at[new_ord[1]] = at[1]; - num_hydrogens = 1; - - } - else - { - /* general case except H-H */ - for ( i = 0; i < num_atoms; i ++ ) - { - k = (at[i].elname[1] || NUMH(at,i))? kMax : (at[i].elname[0]=='H')? at[i].iso_atw_diff : kMax; - if ( k < kMax && at[i].valence == 1 && at[i].chem_bonds_valence == 1 && - /* the order of comparison is important */ - ((n=(int)at[i].neighbor[0]) > i /* at[n] has not been encountered yet*/ || - (int)new_ord[n] < num_atoms - num_hydrogens) /* at[n] might have been encountered; it has not been moved */ ) - { - /* found an explicit terminal hydrogen */ - num_hydrogens ++; - if ( k==0 && ATW_H <= at[i].iso_atw_diff && at[i].iso_atw_diff < ATW_H+NUM_H_ISOTOPES ) - { - k = at[i].iso_atw_diff; /* H isotope has already been marked above or elsewhere */ - } - if ( at[i].charge ) - { - /* transfer charge from the hydrogen */ - at[n].charge += at[i].charge; - at[i].charge = 0; - if (bFixTermHChrg) - { - /*^^^^^ Fixed bug (July 6, 2008 IPl) : - if terminal H was charged (not neutralized before call of remove_terminal_HDT) - and had an ordering number > than that of heavy-atom neighbour, then - charge on neighbour atom was not adjusted (though charge on H was removed). - ^^^^^ */ - if ( i > n ) - /* new_at[new_ord[n]] has been created and filled already */ - new_at[new_ord[n]].charge = at[n].charge; - } - /*^^^^^ */ - } - new_ord[i] = num_atoms - num_hydrogens; /* move hydrogens to the end of the list */ - } - else - { - /* atom is not an explicit terminal hydrogen */ - new_ord[i] = i - num_hydrogens; /* adjust non-hydrogens positions */ - } - - /* copy atom to the new position */ - new_at[new_ord[i]] = at[i]; - - } /* i */ - - } /* general case except H-H */ - - if ( num_hydrogens ) { - int num_others = num_atoms-num_hydrogens; /* atoms which are not terminal H, D, T */ - if ( num_hydrogens > 1 ) { - /* sort hydrogen isotopes in ascending order, */ - /* orig, numbers being the secondary sorting key */ - qsort( new_at+num_others, num_hydrogens, sizeof(new_at[0]), cmp_iso_atw_diff_component_no ); - } - /* save new numbering of hydrogen atoms using temporarily saved orig numbering */ - for ( i = num_others; i < num_atoms; i ++ ) { - new_ord[(int)new_at[i].component] = i; - } - - /* renumber neighbors according to new_ord[] and detach terminal hydrogens */ - for ( i = 0; i < num_others; i++ ) { - memset( new_HydrogenAt_order, 0, sizeof(new_HydrogenAt_order) ); - memset( new_OtherNeigh_order, 0, sizeof(new_OtherNeigh_order) ); - num_OtherNeigh = 0; - num_HydrogenAt = 0; - num_H = 0; - - for ( m = 0; m < MAX_NUM_STEREO_BONDS && new_at[i].sb_parity[m]; m ++ ) { - old_trans[m] = 2 - (new_at[i].sn_ord[m] + new_at[i].sb_ord[m] + (new_at[i].sn_ord[m] > new_at[i].sb_ord[m]))%2; - } - - for ( k = j = val = 0; k < new_at[i].valence; k++ ) { - if ( num_others <= ( n = new_ord[new_at[i].neighbor[k]] ) ) { - /* discovered neighbor = disconnected explicit hydrogen - * i = new atom new_at[i] ordering number - * n = new number of the explicit H - * k = ordering number of the explicit H in new_at[i] adjacency list - */ - if ( 0 < new_at[n].iso_atw_diff && new_at[n].iso_atw_diff < ATW_H+NUM_H_ISOTOPES ) { - /* make explicit isotopic H implicit */ - new_at[i].num_iso_H[new_at[n].iso_atw_diff-1] ++; /* isotopic H */ - num_HydrogenAt += !new_HydrogenAt_order[new_at[n].iso_atw_diff]; - new_HydrogenAt_order[new_at[n].iso_atw_diff] = k+1; - } else { - /* make explicit non-isotopic H implicit */ - new_at[i].num_H ++; /* non-isotopic H */ - num_HydrogenAt += !num_H; - num_H ++; - new_HydrogenAt_order[0] = k+1; - } - /* decrement chem. bonds valence because one bond is removed */ - new_at[i].chem_bonds_valence = inchi_max( 0, new_at[i].chem_bonds_valence-1 ); - new_at[n].neighbor[0] = i; /* update removed hydrogen neighbor number */ - if ( new_at[i].sb_parity[0] ) { - /* if the removed H is an SB neighbor then mark it as removed */ - for ( m = 0; m < MAX_NUM_STEREO_BONDS && new_at[i].sb_parity[m]; m ++ ) { - if ( k == (int)new_at[i].sn_ord[m] ) { - new_at[i].sn_ord[m] = -(new_at[n].iso_atw_diff+1); - /* means the SB neighbor has been removed; (-4)=H, (-3)=1H, (-2)=D, (-1)=T */ - } - } - } - } else { - /* discovered a regular (not an explicit H) neighbor */ - if ( new_at[i].sb_parity[0] ) { - if ( num_OtherNeigh < MAX_NUM_STEREO_BONDS ) { - new_OtherNeigh_order[num_OtherNeigh] = k+1; - } - num_OtherNeigh ++; /* increment outside of if() to detect overflow */ - if ( val != k ) { - /* store new stereobond and sb-neighbor ordering numbers */ - for ( m = 0; m < MAX_NUM_STEREO_BONDS && new_at[i].sb_parity[m]; m ++ ) { - if ( k == (int)new_at[i].sb_ord[m] ) - new_at[i].sb_ord[m] = val; - else - if ( k == (int)new_at[i].sn_ord[m] ) - new_at[i].sn_ord[m] = val; - } - } - } - new_at[i].neighbor[val] = new_ord[new_at[i].neighbor[k]]; - new_at[i].bond_type[val] = new_at[i].bond_type[k]; - new_at[i].bond_stereo[val] = new_at[i].bond_stereo[k]; - val ++; - } - } - if ( new_at[i].valence > val && new_at[i].sb_parity[0] ) { - if ( num_HydrogenAt == new_at[i].valence - val && num_HydrogenAt + num_OtherNeigh <= MAXVAL ) { - /* recalculate parity so that it would describe neighbor sequence H,1H,D,T,neigh[0],neigh[1]... */ - memmove( new_OtherNeigh_order + num_HydrogenAt, new_OtherNeigh_order, num_OtherNeigh*sizeof(new_OtherNeigh_order[0])); - for ( k = 0, j = 1; k <= NUM_H_ISOTOPES; k ++ ) { - if ( new_HydrogenAt_order[k] ) { - new_OtherNeigh_order[num_HydrogenAt - j] = new_HydrogenAt_order[k]; - for ( m = 0; m < MAX_NUM_STEREO_BONDS && new_at[i].sb_parity[m]; m ++ ) { - if ( (int)new_at[i].sn_ord[m] == -(k+1) ) { - new_at[i].sn_ord[m] = -j; - /* negative means explicit H isotope ord are - (contiguously) in front of the adjacency list */ - } - } - j ++; - } - } - /* at this point new_OtherNeigh_order[] contains - incremented old ordering numbers in new order */ - k = insertions_sort_AT_RANK( new_OtherNeigh_order, num_HydrogenAt + num_OtherNeigh ); - k = k%2; /* seems to be of no use */ - /*if ( k ) {*/ - /* - for ( m = 0; m < MAX_NUM_STEREO_BONDS && new_at[i].sb_parity[m]; m ++ ) { - if ( PARITY_WELL_DEF(new_at[i].sb_parity[m]) ) { - if ( old_trans[m] != 2 - (4 + new_at[i].sn_ord[m] + new_at[i].sb_ord[m] + (new_at[i].sn_ord[m] > new_at[i].sb_ord[m]))%2 ) { - new_at[i].sb_parity[m] = 3 - new_at[i].sb_parity[m]; - } - } - } - */ - /*}*/ - } -#ifdef _DEBUG - else { - /* error */ - int stop = 1; - } -#endif - } - new_at[i].valence = val; - } - memcpy( at, new_at, sizeof(at[0])*num_atoms ); - ret = num_others; - } else { - ret = num_atoms; - } -exit_function: - if ( new_ord ) - inchi_free ( new_ord ); - if ( new_at ) - inchi_free ( new_at ); - return ret; -} -/************************************************************************/ -int add_DT_to_num_H( int num_atoms, inp_ATOM *at ) -/* assume num_1H, num_D and num_T are not included in num_H */ -{ - int i, j; - for ( i = 0; i < num_atoms; i ++ ) { - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) - at[i].num_H += at[i].num_iso_H[j]; - } - return 0; -} -/***************************************************************/ -/* not used --- -int FixAromaticOxygenAndSulfur( inp_ATOM *atom ) -{ - if ( !atom->elname[1] && (atom->elname[0]=='O' || atom->elname[0]=='S') && - atom->valence==2 && !atom->charge && !atom->radical && - atom->bond_type[0] + atom->bond_type[1] == 3 ) { - atom->charge = 1; - return 1; // fixed - } - return 0; -} -*/ -/******************************************************************** - - InChI post-version 1.01 features implementation - (v. 1.04 : underivatize is still experimental and for engineering mode) - - ********************************************************************/ -#if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) - -static U_CHAR el_number_O; -static U_CHAR el_number_C; -static U_CHAR el_number_N; -static U_CHAR el_number_P; -static U_CHAR el_number_S; -static U_CHAR el_number_Si; -static U_CHAR el_number_F; -static U_CHAR el_number_Cl; -static U_CHAR el_number_Br; -static U_CHAR el_number_I; -static U_CHAR el_number_B; - -typedef struct tagAtPair { - AT_NUMB at[2]; /* at[0] < at[1] */ -} R2C_ATPAIR; - -int DisconnectInpAtBond( inp_ATOM *at, AT_NUMB *nOldCompNumber, int iat, int neigh_ord ); -int ExtractConnectedComponent( inp_ATOM *at, int num_at, int component_number, inp_ATOM *component_at ); -int mark_arom_bonds( inp_ATOM *at, int num_atoms ); - -void set_R2C_el_numbers( void ); -int UnMarkDisconnectedComponents( ORIG_ATOM_DATA *orig_inp_data ); -int UnMarkRingSystemsInp( inp_ATOM *at, int num_atoms ); -int UnMarkOtherIndicators( inp_ATOM *at, int num_atoms ); -int UnMarkOneComponent( inp_ATOM *at, int num_atoms ); -int subtract_DT_from_num_H( int num_atoms, inp_ATOM *at ); -int add_inp_ATOM( inp_ATOM *at, int len_at, int len_cur, inp_ATOM *add, int len_add ); -int cmp_r2c_atpair( const void *p1, const void *p2 ); -int has_atom_pair( R2C_ATPAIR *ap, int num_ap, AT_NUMB at1, AT_NUMB at2 ); -int mark_atoms_ap( inp_ATOM *at, AT_NUMB start, R2C_ATPAIR *ap, int num_ap, int num, AT_NUMB cFlags ); - -/********************************************************************/ -void set_R2C_el_numbers( void ) -{ - if ( !el_number_O ) { - el_number_O = (U_CHAR)get_periodic_table_number( "O" ); - el_number_C = (U_CHAR)get_periodic_table_number( "C" ); - el_number_N = (U_CHAR)get_periodic_table_number( "N" ); - el_number_P = (U_CHAR)get_periodic_table_number( "P" ); - el_number_S = (U_CHAR)get_periodic_table_number( "S" ); - el_number_Si = (U_CHAR)get_periodic_table_number( "Si" ); - el_number_F = (U_CHAR)get_periodic_table_number( "F" ); - el_number_Cl = (U_CHAR)get_periodic_table_number( "Cl" ); - el_number_Br = (U_CHAR)get_periodic_table_number( "Br" ); - el_number_I = (U_CHAR)get_periodic_table_number( "I" ); - el_number_B = (U_CHAR)get_periodic_table_number( "B" ); - } -} -/***************************************************************/ -int UnMarkDisconnectedComponents( ORIG_ATOM_DATA *orig_inp_data ) -{ - int i; - for ( i = 0; i < orig_inp_data->num_inp_atoms; i ++ ) { - orig_inp_data->at[i].orig_compt_at_numb = 0; - orig_inp_data->at[i].component = 0; - } - if ( orig_inp_data->nCurAtLen ) { - inchi_free( orig_inp_data->nCurAtLen ); - orig_inp_data->nCurAtLen = NULL; - } - if ( orig_inp_data->nOldCompNumber ) { - inchi_free( orig_inp_data->nOldCompNumber ); - orig_inp_data->nOldCompNumber = NULL; - } - orig_inp_data->num_components = 0; - return 0; -} -/***************************************************************/ -int UnMarkRingSystemsInp( inp_ATOM *at, int num_atoms ) -{ - int i; - for ( i = 0; i < num_atoms; i ++ ) { - at[i].bCutVertex = 0; - at[i].nRingSystem = 0; - at[i].nNumAtInRingSystem = 0; - at[i].nBlockSystem = 0; - } - return 0; -} -/***************************************************************/ -int UnMarkOtherIndicators( inp_ATOM *at, int num_atoms ) -{ - int i; - for ( i = 0; i < num_atoms; i ++ ) { - at[i].at_type = 0; - at[i].cFlags = 0; - } - return 0; -} -/***************************************************************/ -int UnMarkOneComponent( inp_ATOM *at, int num_atoms ) -{ - int i; - for ( i = 0; i < num_atoms; i ++ ) { - at[i].orig_compt_at_numb = 0; - at[i].component = 0; - } - return 0; -} -/***************************************************************/ -int subtract_DT_from_num_H( int num_atoms, inp_ATOM *at ) -/* assume num_1H, num_D and num_T are included in num_H */ -{ - int i, j; - for ( i = 0; i < num_atoms; i ++ ) { - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) - at[i].num_H -= at[i].num_iso_H[j]; - } - return 0; -} -/***************************************************************/ -int add_inp_ATOM( inp_ATOM *at, int len_at, int len_cur, inp_ATOM *add, int len_add ) -{ - int i, j; - inp_ATOM *a; - /* chack correctness */ - if ( len_cur < 0 ) - return len_cur; - if ( len_add < 0 ) - return len_add; - if ( len_cur + len_add > len_at ) - return -1; - /* copy */ - memcpy( at+len_cur, add, len_add*sizeof(at[0]) ); - /* modify */ - if ( len_cur ) { - a = at + len_cur; - for ( i = 0; i < len_add; i ++ ) { - for ( j = 0; j < a[i].valence; j ++ ) { - a[i].neighbor[j] += len_cur; - } - } - } - return len_cur + len_add; -} -/****************************************************************************/ -int mark_arom_bonds( inp_ATOM *at, int num_atoms ) -{ - INCHI_MODE bTautFlags=0, bTautFlagsDone = 0; - inp_ATOM *at_fixed_bonds_out = NULL; - T_GROUP_INFO *t_group_info = NULL; - int ret; - ret = mark_alt_bonds_and_taut_groups ( at, at_fixed_bonds_out, num_atoms, - t_group_info, &bTautFlags, &bTautFlagsDone ); - return ret; - -} - -/********************************************************************/ -int cmp_r2c_atpair( const void *p1, const void *p2 ) -{ - const R2C_ATPAIR *ap1 = (const R2C_ATPAIR *)p1; - const R2C_ATPAIR *ap2 = (const R2C_ATPAIR *)p2; - int diff = (int)ap1->at[0] - (int)ap2->at[0]; - if ( !diff ) { - diff = (int)ap1->at[1] - (int)ap2->at[1]; - } - return diff; -} -/***************************************************************/ -int has_atom_pair( R2C_ATPAIR *ap, int num_ap, AT_NUMB at1, AT_NUMB at2 ) -{ - R2C_ATPAIR ap1; - int i1, i2, i3, diff; - int n = at1 > at2; - - ap1.at[n] = at1; - ap1.at[1-n] = at2; - i1 = 0; - i2 = num_ap-1; - /* search for ap1 by simple bisections */ - do { - i3 = (i1 + i2)/2; - if ( !(diff = cmp_r2c_atpair(&ap1, ap+i3) ) ) { - return i3+1; /* found => positive number */ - } else - if ( diff > 0 ) { - i1 = i3 + 1; - } else { - i2 = i3 - 1; - } - }while ( i2 >= i1 ); - return 0; /* not found */ -} - -/***************************************************************/ -/* DFS search for atoms that do not have a flag */ -int mark_atoms_ap( inp_ATOM *at, AT_NUMB start, R2C_ATPAIR *ap, int num_ap, int num, AT_NUMB cFlags ) -{ - if ( !at[start].at_type ) { - int i; - AT_NUMB neigh; - at[start].at_type = cFlags; - num ++; - for ( i = 0; i < at[start].valence; i ++ ) { - neigh = at[start].neighbor[i]; - if ( has_atom_pair( ap, num_ap, start, neigh ) ) - continue; - num = mark_atoms_ap( at, neigh, ap, num_ap, num, cFlags ); - } - } - return num; /* number of atoms traversed forward from at[start] */ -} - -#endif /* RING2CHAIN || UNDERIVATIZE */ - -#if ( UNDERIVATIZE == 1 ) -/***************************************************************/ - -#ifdef NEVER -typedef struct tagAtTypeBitmap { - AT_NUMB ord1 : 5; /* up to 2^5-1 = 31 = 0x0037 */ - AT_NUMB ord2 : 5; - AT_NUMB type : 6; /* up to 2^6-1 = 63 = 0x0077 */ -} AtTypeBitmap; -typedef union tagAtTypeUnion { - AT_NUMB num; - AtTypeBitmap bits; -} AtTypeUnion; -#endif - -#define DERIV_AT_LEN 4 -typedef struct tagDerivAttachment { - char typ[DERIV_AT_LEN]; - char ord[DERIV_AT_LEN]; - char num[DERIV_AT_LEN]; -} DERIV_AT; - - -#define DERIV_BRIDGE_O 0x0001 /* R1-O-R2 => R1-OH + HO-R2 */ -#define DERIV_BRIDGE_NH 0x0002 /* R1-NH-R2 amine */ -#define DERIV_AMINE_tN 0x0004 /* R1-N(-R2)-R3 tertiary amine */ -#define DERIV_RING_O 0x0008 /* -O- in a ring */ -#define DERIV_RING_NH 0x0010 /* -NH- in a ring */ -#define DERIV_UNMARK 0x0040 /* unmark the cut */ -#define DERIV_NOT 0x1000 /* cannot be a derivatization agent atom */ - -#define DERIV_DUPLIC 0x0080 /* duplicated disconnection */ - -#define DERIV_RING (DERIV_RING_O | DERIV_RING_NH) - -#define MAX_AT_DERIV 12 -#define NOT_AT_DERIV 99 -#define MIN_AT_LEFT_DERIV 3 -#define NO_ORD_VAL 0x0037 - -#define CFLAG_MARK_BRANCH 1 /* for main derivative traversal */ -#define CFLAG_MARK_BLOCK 2 /* for block detection */ -#define CFLAG_MARK_BLOCK_INV ((char)~(CFLAG_MARK_BLOCK)) /* for block detection */ -#define COUNT_ALL_NOT_DERIV 1 /* 1=> count ALL atoms that are not in deriv. agents */ - /* 0=> only atoms that are not in DERIV_RING */ - -int mark_atoms_cFlags( inp_ATOM *at, int start, int num, char cFlags ); -int un_mark_atoms_cFlags( inp_ATOM *at, int start, int num, char cFlags, char cInvFlags ); -int is_C_or_S_DB_O( inp_ATOM *at, int i ); -int is_C_unsat_not_arom( inp_ATOM *at, int i ); -int is_C_Alk( inp_ATOM *at, int i, char cFlags ); -int is_Si_IV( inp_ATOM *at, int i ); -int is_P_TB_N( inp_ATOM *at, int i ); -int is_possibly_deriv_neigh( inp_ATOM *at, int iat, int iord, int type, char cFlags ); -int get_traversed_deriv_type( inp_ATOM *at, DERIV_AT *da, int k, DERIV_AT *da1, char cFlags ); -int add_to_da( DERIV_AT *da, DERIV_AT *add ); -int mark_atoms_deriv( inp_ATOM *at, DERIV_AT *da, int start, int num, char cFlags, int *pbFound ); -int count_one_bond_atoms( inp_ATOM *at, DERIV_AT *da, int start, int ord, char cFlags, int *bFound ); -int is_silyl( inp_ATOM *at, int start, int ord_prev ); -int is_Me_or_Et( inp_ATOM *at, int start, int ord_prev ); -int is_CF3_or_linC3F7( inp_ATOM *at, int start, int ord_prev ); -int is_phenyl( inp_ATOM *at, int start, int ord_prev ); -int is_deriv_ring( inp_ATOM *at, int start, int num_atoms, DERIV_AT *da1, int idrv ); -int is_deriv_chain( inp_ATOM *at, int start, int num_atoms, DERIV_AT *da1, int idrv ); -int is_deriv_chain_or_ring( inp_ATOM *at, int start, int num_atoms, DERIV_AT *da1, int *idrv ); -int remove_deriv( DERIV_AT *da1, int idrv ); -int remove_deriv_mark( DERIV_AT *da1, int idrv ); -int EliminateDerivNotInList( inp_ATOM *at, DERIV_AT *da, int num_atoms ); -int make_single_cut( inp_ATOM *at, DERIV_AT *da, int iat, int icut ); - -/***************************************************************/ -/* DFS search for atoms that do not have a flag */ -int mark_atoms_cFlags( inp_ATOM *at, int start, int num, char cFlags ) -{ - if ( !(at[start].cFlags & cFlags) ) { - int i; - at[start].cFlags |= cFlags; - num ++; - for ( i = 0; i < at[start].valence; i ++ ) { - num = mark_atoms_cFlags( at, at[start].neighbor[i], num, cFlags ); - } - } - return num; /* number of atoms traversed forward from at[start] */ -} -/***************************************************************/ -/* DFS search for atoms that do have a flag */ -int un_mark_atoms_cFlags( inp_ATOM *at, int start, int num, char cFlags, char cInvFlags ) -{ - if ( at[start].cFlags & cFlags ) { - int i; - at[start].cFlags &= cInvFlags; - num ++; - for ( i = 0; i < at[start].valence; i ++ ) { - num = un_mark_atoms_cFlags( at, at[start].neighbor[i], num, cFlags, cInvFlags ); - } - } - return num; /* number of atoms traversed forward from at[start] */ -} -/***************************************************************/ -int is_C_or_S_DB_O( inp_ATOM *at, int i ) -{ - int j, neigh; - if ( at[i].el_number != el_number_C && - at[i].el_number != el_number_S || - at[i].charge || at[i].radical ) - return 0; - for ( j = 0; j < at[i].valence; j ++ ) { - neigh = at[i].neighbor[j]; - if ( (at[neigh].el_number == el_number_O || - at[neigh].el_number == el_number_S ) && - !at[neigh].num_H && 1 == at[neigh].valence && - 2 == at[neigh].chem_bonds_valence ) { - return 1; - } - } - return 0; -} -/***************************************************************/ -int is_C_unsat_not_arom( inp_ATOM *at, int i ) -{ - int j, neigh, num_arom, num_DB; - if ( at[i].el_number != el_number_C || - at[i].valence == at[i].chem_bonds_valence || - at[i].valence+1 < at[i].chem_bonds_valence || - at[i].chem_bonds_valence + at[i].num_H != 4 || - at[i].charge || at[i].radical ) - return 0; - num_arom = num_DB = 0; - for ( j = 0; j < at[i].valence; j ++ ) { - neigh = at[i].neighbor[j]; - num_arom += at[i].bond_type[j] == BOND_TYPE_ALTERN; - if ( (at[neigh].el_number == el_number_O || - at[neigh].el_number == el_number_S ) && - !at[neigh].num_H && 1 == at[neigh].valence && - 2 == at[neigh].chem_bonds_valence ) { - continue; - } - num_DB += at[i].bond_type[j] == BOND_TYPE_DOUBLE; - } - return num_DB && !num_arom; -} -/***************************************************************/ -int is_C_Alk( inp_ATOM *at, int i, char cFlags ) -{ - if ( at[i].el_number == el_number_C && - at[i].valence == at[i].chem_bonds_valence ) { - int j, k; - U_CHAR el; - for ( j = 0; j < at[i].valence; j ++ ) { - k = at[i].neighbor[j]; - if ( at[k].cFlags & cFlags ) - continue; - el = at[k].el_number; - if ( el != el_number_C && - el != el_number_F && - el != el_number_Cl && - el != el_number_Br && - el != el_number_I ) { - return 0; - } - } - return 1; - } - return 0; -} -/***************************************************************/ -int is_Si_IV( inp_ATOM *at, int i ) -{ - if ( at[i].el_number != el_number_Si || - at[i].charge || at[i].radical || at[i].valence != 4 || at[i].chem_bonds_valence != 4 ) - return 0; - return 1; -} -/***************************************************************/ -int is_P_TB_N( inp_ATOM *at, int i ) -{ - int j, k; - if ( at[i].el_number != el_number_P || at[i].chem_bonds_valence - at[i].valence != 2 ) - return 0; - for ( j = 0; j < at[i].valence; j ++ ) { - k = at[i].neighbor[j]; - if ( at[k].el_number == el_number_N && at[k].valence == 1 && at[k].chem_bonds_valence == 3 ) - return 1; - } - return 0; -} -/***************************************************************/ -int is_possibly_deriv_neigh( inp_ATOM *at, int iat, int iord, int type, char cFlags ) -{ - int neigh = at[iat].neighbor[iord]; - int neigh_from = -1; - U_CHAR el = at[neigh].el_number; - int bOk = 0; - switch ( type ) { - case DERIV_BRIDGE_O: - neigh_from = at[iat].neighbor[!iord]; - /* -> A--O--B -> traversing from A(neigh_from) to B(neigh); may we cut O--B bond? */ - /* do not cut bond "---" in A=Si(IV), B(=O), B=C: Si(IV)-O---B(=O) */ - if ( !(is_C_or_S_DB_O( at, neigh ) && is_Si_IV( at, neigh_from )) && - !is_C_unsat_not_arom( at, neigh ) ) { - bOk = ( el == el_number_C || - el == el_number_Si || - el == el_number_S || - el == el_number_P ); - } - break; - case DERIV_BRIDGE_NH: - /* -> A--NH--B -> traversing from A(neigh_from) to B(neigh); may we cut NH--B bond? */ - bOk = ( is_C_or_S_DB_O( at, neigh ) || - is_C_Alk( at, neigh, cFlags ) || - is_Si_IV( at, neigh ) || - is_P_TB_N( at, neigh ) ) && !(is_C_unsat_not_arom( at, neigh )); - break; - case DERIV_AMINE_tN: - bOk = ( is_C_or_S_DB_O( at, neigh ) || - is_C_Alk( at, neigh, cFlags ) || - is_Si_IV( at, neigh ) || - is_P_TB_N( at, neigh ) ) && !(is_C_unsat_not_arom( at, neigh )); - break; - } - return bOk; -} -/***************************************************************/ -/* determines derivative type on the forward step of the DFS */ -/***************************************************************/ -int get_traversed_deriv_type( inp_ATOM *at, DERIV_AT *da, int k, DERIV_AT *da1, char cFlags ) -{ - int i, j, m, nBlockSystemFrom, nOrdBack1, nOrdBack2, nOrdBack3, nBackType1, nBackType2; - memset( da1, 0, sizeof(*da1) ); - if ( at[k].cFlags & cFlags ) { - return 0; - } - for ( m = 0; m < at[k].valence && !(at[(int)at[k].neighbor[m]].cFlags & cFlags); m ++ ) - ; - if ( m == at[k].valence ) - return -1; /* error: at least one neighbor must have cFlags */ - if ( at[k].valence == 1 && at[k].num_H && ( - at[k].el_number == el_number_O || - at[k].el_number == el_number_N || - at[k].el_number == el_number_S || - at[k].el_number == el_number_P ) ) { - return DERIV_NOT; - } - if ( is_el_a_metal( at[k].el_number ) ) { - return DERIV_NOT; - } -#ifdef NEVER - if ( at[k].el_number == el_number_N && at[k].valence >= 2 && at[k].chem_bonds_valence <= 3 ) { - return DERIV_NOT; /* prohibit -N-, -N=, allow -N# as in isocyano -N#C or NO2 */ - } -#endif - /* m is the ord of the bond from which at[k] was reached first time */ - if ( at[k].nNumAtInRingSystem == 1 && (at[k].el_number == el_number_O || at[k].el_number == el_number_S) && - at[k].valence == 2 && at[k].chem_bonds_valence == 2 && - !at[k].num_H && !at[k].charge && !at[k].radical) { - /* -> A--O--B -> traversing from A to B; cut O--B */ - /* check for carboxy A(=O)-O-B and A--O--B(=O) */ - /* int has_A_CO = is_C_or_S_DB_O( at, at[k].neighbor[m] ); */ - int has_B_CO = is_C_or_S_DB_O( at, at[k].neighbor[!m] ); - int is_A_Si_IV = is_Si_IV( at, at[k].neighbor[m] ); - /* int is_B_Si_IV = is_Si_IV( at, at[k].neighbor[!m] );*/ - if ( is_A_Si_IV && has_B_CO ) { - ; /* do not cut bond --- in A=Si(IV), B(=O), B=C: Si(IV)-O---B(=O) */ - } else - if ( is_possibly_deriv_neigh( at, k, !m, DERIV_BRIDGE_O, cFlags ) ) { - da1->ord[0] = !m; /* ord of neighbor B, not traversed yet */ - da1->typ[0] = DERIV_BRIDGE_O; /* type */ - return DERIV_BRIDGE_O; /* R-O-R */ - } - } - if ( at[k].bCutVertex && at[k].el_number == el_number_N && - at[k].valence == 2 && at[k].chem_bonds_valence == at[k].valence && - at[k].valence+at[k].num_H == 3 && !at[k].charge && !at[k].radical) { - da1->ord[0] = !m; /* ord of neighbor B, not traversed yet */ - da1->typ[0] = DERIV_BRIDGE_NH; /* type */ - return DERIV_BRIDGE_NH; /* R1-N(-R2)-R3 or R1-NH-R2 amine */ - } - if ( at[k].bCutVertex && at[k].el_number == el_number_N && - at[k].valence == 3 && at[k].chem_bonds_valence == at[k].valence && - at[k].valence+at[k].num_H == 3 && !at[k].charge && !at[k].radical) { - int rm1 = ( at[at[k].neighbor[m]].nRingSystem == at[at[k].neighbor[(m+1)%3]].nRingSystem ); - int rm2 = ( at[at[k].neighbor[m]].nRingSystem == at[at[k].neighbor[(m+2)%3]].nRingSystem ); - int r12 = ( at[at[k].neighbor[(m+1)%3]].nRingSystem == at[at[k].neighbor[(m+2)%3]].nRingSystem ); - int ord[2]= {-1, -1}; - i = 0; /* type: tertriary amine */ - switch( rm1 + rm2 + r12 ) { - case 0: - /* -N< has no ring bonds */ - if ( is_possibly_deriv_neigh( at, k, (m+1)%3, DERIV_AMINE_tN, cFlags ) ) { - ord[i ++] = (m+1)%3; /* ord of a non-ring neighbor, not traversed yet */ - } - if ( is_possibly_deriv_neigh( at, k, (m+2)%3, DERIV_AMINE_tN, cFlags ) ) { - ord[i ++] = (m+2)%3; /* ord of another non-ring neighbor, not traversed yet */ - } - if ( i == 2 && ord[0] > ord[1] ) { - int tmp = ord[0]; - ord[0] = ord[1]; - ord[1] = tmp; - } - break; - - case 1: - if ( rm1 && is_possibly_deriv_neigh( at, k, (m+2)%3, DERIV_AMINE_tN, cFlags ) ) { - ord[i++] = (m+2)%3; /* ord of a single non-ring neighbor, not traversed yet */ - } else - if ( rm2 && is_possibly_deriv_neigh( at, k, (m+1)%3, DERIV_AMINE_tN, cFlags ) ) { - ord[i++] = (m+1)%3; /* ord of a single non-ring neighbor, not traversed yet */ - } - } - for ( j = 0; j < i; j ++ ) { - da1->ord[j] = ord[j]; - da1->typ[j] = DERIV_AMINE_tN; - } - if ( i ) { - return DERIV_AMINE_tN; - } - return 0; /* all neighbors or two untraversed edges are in one ring system */ - } - if ( at[k].bCutVertex && /* DD */ - at[k].valence == at[k].chem_bonds_valence && - (!at[k].num_H || at[k].el_number == el_number_C && 1 == at[k].num_H) && - !at[k].charge && !at[k].radical && - (at[k].el_number == el_number_C && at[k].valence+at[k].num_H == 4 || - at[k].el_number == el_number_Si && at[k].valence == 4 || - at[k].el_number == el_number_B && at[k].valence == 3) ) { - - /* entering path: ->X--O--DD - --X--O - | \ / DD = C, Si, B - | DD - | / \ O = O, NH - --Y--O - X, Y -- ignored for now - */ - nBlockSystemFrom = 0; - nBackType1 = nBackType2 = 0; - nOrdBack1 = nOrdBack2 = nOrdBack3 = -1; - j=(int)at[k].neighbor[m]; - if ( (at[j].el_number == el_number_O || at[j].el_number == el_number_S) && at[j].valence == 2 && - at[j].chem_bonds_valence == at[j].valence && - at[j].nNumAtInRingSystem >= 5 && - !at[j].num_H && !at[j].charge && !at[j].radical ) { - nBackType1 = DERIV_RING_O; - nBlockSystemFrom = at[j].nBlockSystem; - nOrdBack1 = m; /* predecessor */ - } else - if ( at[j].el_number == el_number_N && at[j].valence == 2 && - at[j].chem_bonds_valence == at[j].valence && - at[j].nNumAtInRingSystem >= 5 && - 1==at[j].num_H && !at[j].charge && !at[j].radical ) { - nBackType1 = DERIV_RING_NH; - nBlockSystemFrom = at[j].nBlockSystem; - nOrdBack1 = m; /* predecessor */ - } - if ( nBlockSystemFrom ) { - int num1, num2, bFound; - at[k].cFlags |= CFLAG_MARK_BLOCK; - num1 = mark_atoms_cFlags( at, at[k].neighbor[nOrdBack1], 1, CFLAG_MARK_BLOCK ); - for ( i = 0; i < at[k].valence; i ++ ) { - if ( i == nOrdBack1 ) - continue; - j=(int)at[k].neighbor[i]; - bFound = 0; - if ( at[j].cFlags & CFLAG_MARK_BLOCK ) { - if ( (at[j].el_number == el_number_O || at[j].el_number == el_number_S) && at[j].valence == 2 && - at[j].chem_bonds_valence == at[j].valence && - at[j].nNumAtInRingSystem >= 5 && - !at[j].num_H && !at[j].charge && !at[j].radical ) { - bFound = 1; - if ( nOrdBack2 < 0 ) { - nOrdBack2 = i; /* predecessor #2 */ - nBackType2 = DERIV_RING_O; - } else { - nOrdBack3 = i; /* predecessor #3 -- should not happen */ - } - } - if ( at[j].el_number == el_number_N && at[j].valence == 2 && - at[j].chem_bonds_valence == at[j].valence && - at[j].nNumAtInRingSystem >= 5 && - 1==at[j].num_H && !at[j].charge && !at[j].radical ) { - bFound = 1; - if ( nOrdBack2 < 0 ) { - nOrdBack2 = i; /* predecessor #2 */ - nBackType2 = DERIV_RING_NH; - } else { - nOrdBack3 = i; /* predecessor #3 -- should not happen */ - } - } - if ( !bFound ) { - nOrdBack3 = 99; /* reject: wrong neighboring atom in the same block */ - break; - } - } - } - num2 = un_mark_atoms_cFlags( at, k, 0, CFLAG_MARK_BLOCK, CFLAG_MARK_BLOCK_INV ); - if ( num1 != num2 ) { - return -1; /* mark_atoms_cFlags() program error */ - } - if ( nOrdBack2 >= 0 && nOrdBack3 < 0 ) { - if ( nOrdBack1 < nOrdBack2 ) { - da1->ord[0] = nOrdBack1; /* ord of a ring neighbor, traversed */ - da1->typ[0] = nBackType1; - da1->ord[1] = nOrdBack2; /* ord of another ring neighbor, not traversed yet */ - da1->typ[1] = nBackType2; - } else { - da1->ord[0] = nOrdBack2; /* ord of a ring neighbor, traversed */ - da1->typ[0] = nBackType2; - da1->ord[1] = nOrdBack1; /* ord of another ring neighbor, not traversed yet */ - da1->typ[1] = nBackType1; - } - return nBackType1 | nBackType2; - } - } - } - return 0; -} -/***************************************************************/ -int add_to_da( DERIV_AT *da, DERIV_AT *add ) -{ - /* if add has more than 1 element the elements are in ascending add.ord[] order */ - int i, j, len; - for ( len = 0; len < DERIV_AT_LEN && da->typ[len]; len ++ ) - ; - for ( j = 0; j < DERIV_AT_LEN && add->typ[j]; j ++ ) { - for ( i = 0; i < len; i ++ ) { - if ( add->ord[j] == da->ord[i] ) { - if ( add->typ[j] != da->typ[i] ) { - return -1; /* error, should not happen */ - } - if ( add->num[j] != da->num[i] ) { - return -2; /* error, should not happen */ - } - break; - } - } - if ( i == len ) { - if ( len < DERIV_AT_LEN ) { - da->ord[i] = add->ord[j]; - da->typ[i] = add->typ[j]; - da->num[i] = add->num[j]; - len ++; - } else { - return -3; /* overflow, should not happen */ - } - } - } - return 0; -} -/***************************************************************/ -/* DFS search for atoms that do not have a flag */ -int mark_atoms_deriv( inp_ATOM *at, DERIV_AT *da, int start, int num, char cFlags, int *pbFound ) -{ - int i, nFound=0; - DERIV_AT da1; - if ( !(at[start].cFlags & cFlags) ) { - if ( DERIV_NOT == get_traversed_deriv_type( at, da, start, &da1, cFlags ) ) { - nFound ++; /* at[start] cannot belong to a derivatizing agent */ - } - num ++; - at[start].cFlags |= cFlags; - if ( da1.typ[0] ) { - /* possibly a derivatization agent attachment point. */ - /* check neighbors that have not been traversed yet */ - int n1=0, n2=0, i1=-1, i2=-1, nFound1=0, nFound2=0; - switch( da1.typ[0] ) { - case DERIV_BRIDGE_O: - case DERIV_BRIDGE_NH: - n1 = mark_atoms_deriv( at, da, at[start].neighbor[(int)da1.ord[0]], 0, cFlags, &nFound1 ); - if ( n1 > MAX_AT_DERIV || nFound1 ) { - da1.typ[0] = 0; - } else { - da1.num[0] = n1; - nFound ++; - } - break; - case DERIV_AMINE_tN: - n1 = mark_atoms_deriv( at, da, at[start].neighbor[i1=da1.ord[0]], 0, cFlags, &nFound1 ); - if ( da1.typ[1] ) { - n2 = mark_atoms_deriv( at, da, at[start].neighbor[i2=da1.ord[1]], 0, cFlags, &nFound2 ); - } - if ( 0 < n1 && n1 <= MAX_AT_DERIV && !nFound1 ) { - da1.num[0] = n1; - i = 1; - nFound ++; - } else { - da1.ord[0] = da1.ord[1]; - da1.num[0] = da1.num[1]; - da1.typ[0] = da1.typ[1]; - da1.typ[1] = 0; - i = 0; - } - if ( 0 < n2 && n2 <= MAX_AT_DERIV && !nFound2 ) { - da1.num[i] = n2; - nFound ++; - } else { - da1.typ[i] = 0; - } - break; - case DERIV_RING_O: - case DERIV_RING_NH: - for ( i = 0; i < at[start].valence; i ++ ) { - if ( i != da1.ord[0] && i != da1.ord[1] && !(at[at[start].neighbor[i]].cFlags & cFlags) ) { - if ( !n1 ) - n1 = mark_atoms_deriv( at, da, at[start].neighbor[i1=i], 0, cFlags, &nFound1 ); - else - n2 = mark_atoms_deriv( at, da, at[start].neighbor[i2=i], 0, cFlags, &nFound2 ); - } - } - if ( n1+n2+1 > MAX_AT_DERIV || nFound1 || nFound2 ) { - da1.typ[1] = da1.typ[0] = 0; - } else { - da1.num[0] = n1; - da1.num[1] = n2; - nFound ++; - } - break; - } - if ( n1 < 0 ) - return n1; - if ( n2 < 0 ) - return n2; /* errors */ - - if ( i = add_to_da( da+start, &da1 ) ) { - return i; /* error */ - } - - *pbFound += nFound1 + nFound2 + nFound; - num += n1 + n2; - } else { - *pbFound += nFound; - } - for ( i = 0; i < at[start].valence; i ++ ) { - num = mark_atoms_deriv( at, da, at[start].neighbor[i], num, cFlags, pbFound ); - if ( num < 0 ) - return num; - } - } - /* *pbFound = number of derivatizer attachment points traversed forward from at[start] */ - return num; /* number of atoms traversed forward from at[start] */ -} -/***************************************************************/ -int count_one_bond_atoms( inp_ATOM *at, DERIV_AT *da, int start, int ord, char cFlags, int *bFound ) -{ - int num = 0; - if ( !(at[at[start].neighbor[ord]].cFlags & cFlags) ) { - at[at[start].neighbor[ord]].cFlags |= cFlags; - num ++; - num = mark_atoms_deriv( at, da, start, num, cFlags, bFound ); - } - return num; -} -/*************************************************************** - List of allowed derivatives - - Legend: - - ->- marks the bond to be disconnexted: X->-Y => XD + TY - where TY is a derivatizing agent eventually to be discarded - - Allowed Derivative Types List - ============================= - - DERIV_BRIDGE_O, DERIV_BRIDGE_NH, DERIV_AMINE_tN - ----------------------------------------------- - CH3 CH3 CH3 CH3 CH3 - | | | | | - X->-Si--CH3 X->-Si---Si--CH3 X->-Si----C--CH3 X= O, NH, N - | | | | | - CH3 CH3 CH3 CH3 CH3 - - 4 atoms 7 atoms 7 atoms is_silyl() - - - - - O O O F O - || || || | || - R--C--O->-CH3 R--C--O->-CH2--CH3 R--C--O->-C--F R--C--O->-CF2-CF2-CF3 - | - F - - - - 1 atom 2 atoms 3 atoms 10 atoms - is_Me_or_Et() is_Me_or_Et() is_CF3_or_linC3F7() - - - A. DERIV_BRIDGE_NH, DERIV_AMINE_tN - ----------------------------------- - - - O O O F O - || || || | || - N->-C--CH3 N->-C--CH2--CH3 N->-C---C--F N->-C--CF2-CF2-CF3 - | - F - - - - 3 atoms 5 atoms 8 atoms 12 atoms - is_Me_or_Et() is_Me_or_Et() is_CF3_or_linC3F7() - - DERIV_RING_O (da contains >B- or >C< or >CH- atom) - ------------ - - C----O R----O R----O - | \ | \ CH3 | \ - | > | > / | > - | \ | \ / | \ - | B--CH3 | C | CH--Ph - | / | / \ | / - | > | > \ | > - | / | / CH3 | / - C----O R----O R----O - - 5-member 5 or 6-member 5 or 6-member - - - 2 atoms 3 atoms 7 atoms - - DERIV_RING_NH - ------------ - - None in the list - -***************************************************************/ -int is_silyl( inp_ATOM *at, int start, int ord_prev ) -{ - int i, neigh, num_Me=0, iC_IV=-1, iSi_IV=-1, i_C_or_Si_IV; - - if ( at[start].el_number != el_number_Si || at[start].valence != 4 || - at[start].valence != at[start].chem_bonds_valence || - at[start].charge || at[start].radical ) - return 0; - for ( i = 0; i < at[start].valence; i ++ ) { - if ( i == ord_prev ) - continue; - neigh = at[start].neighbor[i]; - if ( at[neigh].charge || at[neigh].radical || - at[neigh].valence != at[neigh].chem_bonds_valence) - return 0; - if ( at[neigh].valence == 4 ) { - if ( at[neigh].el_number == el_number_C && iC_IV < 0 && iSi_IV < 0 ) - iC_IV = neigh; - else - if ( at[neigh].el_number == el_number_Si && iC_IV < 0 && iSi_IV < 0 ) - iSi_IV = neigh; - else - return 0; - } else - if ( at[neigh].valence == 1 && - at[neigh].valence == at[neigh].chem_bonds_valence && - at[neigh].el_number == el_number_C && at[neigh].num_H == 3 ) { - num_Me ++; - } else { - return 0; - } - } - if ( num_Me == 3 && iC_IV < 0 && iSi_IV < 0 ) - return 1; /* Si(CH3)3 */ - - /* next C(IV) or Si(IV) */ - i_C_or_Si_IV = iC_IV >= 0? iC_IV : iSi_IV >= 0? iSi_IV : -1; - if ( num_Me != 2 || i_C_or_Si_IV < 0 ) - return 0; - - num_Me = 0; - for ( i = 0; i < at[i_C_or_Si_IV].valence; i ++ ) { - neigh = at[i_C_or_Si_IV].neighbor[i]; - if ( neigh == start ) - continue; - if ( at[neigh].charge || at[neigh].radical || - at[neigh].valence != at[neigh].chem_bonds_valence) - return 0; - if ( at[neigh].valence == 1 && - at[neigh].valence == at[neigh].chem_bonds_valence && - at[neigh].el_number == el_number_C && at[neigh].num_H == 3 ) { - num_Me ++; - } else { - return 0; - } - } - if ( num_Me == 3 ) - return 2; /* Si(CH3)2Si/C(CH3)3 */ - return 0; -} -/****************************************************************/ -int is_Me_or_Et( inp_ATOM *at, int start, int ord_prev ) -{ - int i, neigh = -1; - if ( at[start].el_number != el_number_C || at[start].valence > 2 || - at[start].valence != at[start].chem_bonds_valence || - at[start].chem_bonds_valence + at[start].num_H != 4 || - at[start].charge || at[start].radical ) - return 0; - for ( i = 0; i < at[start].valence; i ++ ) { - if ( i == ord_prev ) - continue; - if ( neigh >= 0 ) - return 0; - - neigh = at[start].neighbor[i]; - if ( at[neigh].el_number != el_number_C || at[neigh].valence > 1 || - at[neigh].valence != at[neigh].chem_bonds_valence || - at[neigh].chem_bonds_valence + at[neigh].num_H != 4 || - at[neigh].charge || at[neigh].radical ) - return 0; - } - return 1 + (neigh >= 0); -} -#ifdef NEVER -/**************************************************************** - CF3 - -CF3 or -CF< - CF3 -*****************************************************************/ -int is_CF3_or_isoC3F7( inp_ATOM *at, int start, int ord_prev ) -{ - int i, k, num_C_IV, iC_IV[2], neigh, num_F, iC; - if ( at[start].el_number != el_number_C || at[start].valence != 4 || - at[start].valence != at[start].chem_bonds_valence || - at[start].chem_bonds_valence + at[start].num_H != 4 || - at[start].charge || at[start].radical ) - return 0; - - iC_IV[0] = iC_IV[1] = num_F = 0; - - for ( i = num_C_IV = 0; i < at[start].valence; i ++ ) { - if ( i == ord_prev ) - continue; - - neigh = at[start].neighbor[i]; - if ( at[neigh].valence != at[neigh].chem_bonds_valence || - at[neigh].charge || at[neigh].radical ) - return 0; - if ( at[neigh].el_number == el_number_F ) { - if ( at[neigh].chem_bonds_valence + at[neigh].num_H != 1 ) - return 0; - num_F ++; - } else - if ( at[neigh].el_number == el_number_C && - at[neigh].valence == 4 && - !at[neigh].num_H && !at[neigh].charge && !at[neigh].radical && num_C_IV < 2 ) { - - if ( num_C_IV > 1 ) - return 0; - - iC_IV[num_C_IV++] = neigh; - } - } - if ( !num_C_IV && 3 == num_F ) - return 1; /* -CF3 */ - if ( 2 != num_C_IV || 1 != num_F ) - return 0; - - /* detect iso-C3F7 */ - for ( k = 0; k < num_C_IV; k ++ ) { - iC = iC_IV[k]; - num_F = 0; - for ( i = 0; i < at[iC].valence; i ++ ) { - neigh = at[iC].neighbor[i]; - if ( neigh == start ) - continue; - if ( at[neigh].valence != at[neigh].chem_bonds_valence || - at[neigh].charge || at[neigh].radical ) - return 0; - if ( at[neigh].el_number == el_number_F ) { - if ( at[neigh].chem_bonds_valence + at[neigh].num_H != 1 ) - return 0; - num_F ++; - } else { - return 0; - } - } - if ( num_F != 3 ) - return 0; - } - return 2; /* iso-C3F7 */ -} -#endif -/**************************************************************/ -int is_CF3_or_linC3F7( inp_ATOM *at, int start, int ord_prev ) -{ - int i, num_C_IV, iC_IV, neigh, num_F, num_C=0; - AT_NUMB *p; - - while( num_C < 4 ) { - - if ( at[start].el_number != el_number_C || at[start].valence != 4 || - at[start].valence != at[start].chem_bonds_valence || - at[start].chem_bonds_valence + at[start].num_H != 4 || - at[start].charge || at[start].radical ) - return 0; - - iC_IV = num_F = 0; - - for ( i = num_C_IV = 0; i < at[start].valence; i ++ ) { - if ( i == ord_prev ) - continue; - - neigh = at[start].neighbor[i]; - if ( at[neigh].valence != at[neigh].chem_bonds_valence || - at[neigh].charge || at[neigh].radical ) - return 0; - if ( at[neigh].el_number == el_number_F ) { - if ( at[neigh].chem_bonds_valence + at[neigh].num_H != 1 ) - return 0; - num_F ++; - } else - if ( at[neigh].el_number == el_number_C && - at[neigh].valence == 4 && - !at[neigh].num_H && !at[neigh].charge && !at[neigh].radical && num_C_IV < 2 ) { - - if ( num_C_IV ) - return 0; - - iC_IV = neigh; - num_C_IV++; - } - } - if ( num_C_IV + num_F != 3 ) - return 0; - - num_C ++; /* found -CF2-C or -CF3 */ - if ( !num_C_IV ) - break; /* -CF3 */ - - /* treat next C(IV) as a new start atom */ - if ( p = is_in_the_list( at[iC_IV].neighbor, (AT_NUMB) start, at[iC_IV].valence ) ) { - start = iC_IV; - ord_prev = p - at[iC_IV].neighbor; - } else { - return -1; /* program error */ - } - } - return num_C == 1? 1 : num_C == 3? 2 : 0; -} -/****************************************************************/ -int is_phenyl( inp_ATOM *at, int start, int ord_prev ) -{ - int k, neigh, cur_at, ord; - if ( at[start].el_number != el_number_C || at[start].valence != 3 || - at[start].valence+1 != at[start].chem_bonds_valence || - at[start].chem_bonds_valence + at[start].num_H != 4 || - at[start].charge || at[start].radical ) - return 0; - - ord = (ord_prev + 1) % 3; - cur_at = start; - - for ( k = 0; k < 5; k ++ ) { - neigh = at[cur_at].neighbor[ord]; - if ( at[neigh].el_number != el_number_C || at[neigh].valence != 2 || - at[neigh].valence+1 != at[neigh].chem_bonds_valence || - at[neigh].chem_bonds_valence + at[neigh].num_H != 4 || - at[neigh].charge || at[neigh].radical ) - return 0; - ord = (at[neigh].neighbor[0] == cur_at); - cur_at = neigh; - } - return (at[cur_at].neighbor[ord] == start); -} -/****************************************************************/ -int is_deriv_ring( inp_ATOM *at, int start, int num_atoms, DERIV_AT *da1, int idrv ) -{ - int i, k, neigh_at[2], prev_ord[2], neigh, is_B=0, is_C=0; - AT_NUMB *p; - if ( da1->typ[idrv] != DERIV_RING_O || da1->typ[idrv+1] != DERIV_RING_O ) - return 0; - if ( at[start].charge || at[start].radical || at[start].valence != at[start].chem_bonds_valence ) - return 0; - if ( at[start].el_number == el_number_B && at[start].valence == 3 ) - is_B = 1; - else - if ( at[start].el_number == el_number_C && (at[start].valence == 3 || at[start].valence == 4) && - at[start].chem_bonds_valence == at[start].valence && - at[start].num_H + at[start].chem_bonds_valence == 4 ) - is_C = 1; - else - return 0; - /* locate bonds connecting >B- or >C< or >C- to the rest of the derivative */ - for ( i = k = 0; i < at[start].valence; i ++ ) { - if ( i != da1->ord[idrv] && i != da1->ord[idrv+1] ) { - neigh = at[start].neighbor[i]; - if ( k < 2 && (p = is_in_the_list( at[neigh].neighbor, (AT_NUMB) start, at[neigh].valence)) ) { - neigh_at[k] = neigh; - prev_ord[k] = p - at[neigh].neighbor; - k ++; - } else { - return -1; /* program error */ - } - } - } - if ( is_B && k == 1 && is_Me_or_Et( at, neigh_at[0], prev_ord[0]) ) - return 1; - if ( is_C && k == 1 && at[start].num_H == 1 && is_phenyl( at, neigh_at[0], prev_ord[0]) ) - return 1; - if ( is_C && k == 2 && is_Me_or_Et( at, neigh_at[0], prev_ord[0]) && - is_Me_or_Et( at, neigh_at[1], prev_ord[1]) ) - return 1; - - return 0; -} -/****************************************************************/ -int is_deriv_chain( inp_ATOM *at, int start, int num_atoms, DERIV_AT *da1, int idrv ) -{ - int i, k, prev_ord, neigh, iC, iO, iNxt; - AT_NUMB *p; - if ( !da1->typ[idrv] || (da1->typ[idrv] & DERIV_RING) ) - return 0; - if ( at[start].charge || at[start].radical || at[start].valence != at[start].chem_bonds_valence ) - return 0; - - neigh = at[start].neighbor[(int)da1->ord[idrv]]; - p = is_in_the_list( at[neigh].neighbor, (AT_NUMB) start, at[neigh].valence); - if ( !p ) - return -1; /* program error */ - prev_ord = p - at[neigh].neighbor; - - /* eliminate silyl possibility */ - if ( is_silyl( at, neigh, prev_ord ) ) - return 1; - - if ( da1->typ[idrv] == DERIV_BRIDGE_O ) { - /* check acetyl */ - iC = at[start].neighbor[!da1->ord[idrv]]; - if ( at[iC].charge || at[iC].radical || at[iC].num_H || - at[iC].el_number != el_number_C || at[iC].valence != 3 || - at[iC].valence+1 != at[iC].chem_bonds_valence ) - return 0; - for ( i = k = 0; i < at[iC].valence; i ++ ) { - iO = at[iC].neighbor[i]; - if ( at[iO].charge || at[iO].radical || at[iO].num_H || - at[iO].el_number != el_number_O || at[iO].valence != 1 || - at[iO].valence+1 != at[iO].chem_bonds_valence ) - continue; - k ++; /* number of =O */ - } - if ( k != 1 ) - return 0; - /* check derivative */ - return ( is_Me_or_Et( at, neigh, prev_ord ) || - is_CF3_or_linC3F7( at, neigh, prev_ord ) ); - } - - if ( da1->typ[idrv] == DERIV_BRIDGE_NH || da1->typ[idrv] == DERIV_AMINE_tN ) { - /* check acetyl */ - iNxt = -1; - iC = at[start].neighbor[(int)da1->ord[idrv]]; - if ( at[iC].charge || at[iC].radical || at[iC].num_H || - at[iC].el_number != el_number_C || at[iC].valence != 3 || - at[iC].valence+1 != at[iC].chem_bonds_valence ) - return 0; - for ( i = k = 0; i < at[iC].valence; i ++ ) { - iO = at[iC].neighbor[i]; - if ( at[iO].charge || at[iO].radical || at[iO].num_H || - at[iO].el_number != el_number_O || at[iO].valence != 1 || - at[iO].valence+1 != at[iO].chem_bonds_valence ) { - if ( iO != start ) { - if ( iNxt < 0 ) - iNxt = iO; - else - return 0; - } - continue; - } - k ++; /* number of =O */ - } - if ( k != 1 || iNxt < 0 ) - return 0; - /* find bond from iNxt to iC */ - p = is_in_the_list( at[iNxt].neighbor, (AT_NUMB) iC, at[iNxt].valence); - if ( !p ) - return -1; /* program error */ - prev_ord = p - at[iNxt].neighbor; - /* check derivative */ - return ( is_Me_or_Et( at, iNxt, prev_ord ) || - is_CF3_or_linC3F7( at, iNxt, prev_ord ) ); - } - return 0; -} -/****************************************************************/ -int is_deriv_chain_or_ring( inp_ATOM *at, int start, int num_atoms, DERIV_AT *da1, int *idrv ) -{ - int i, ret = -1; - if ( da1->typ[*idrv] & DERIV_RING ) { - /* find the first ord of this derivative; ord of ring derivatives are in pairs */ - int j = -1; - for ( i = 0; i < DERIV_AT_LEN && da1->typ[i]; i ++ ) { - if ( da1->typ[i] & DERIV_RING ) { - if ( i == *idrv || i+1 == *idrv ) { - *idrv = j = i; - break; - } - i ++; /* bypass the second bond to the same derivatization agent */ - } - } - /* check consistency */ - if ( j == -1 || j+1 >= DERIV_AT_LEN || - !(da1->typ[j] & DERIV_RING) || !(da1->typ[j+1] & DERIV_RING) ) { - ret = -1; /* program error */ - } else { - ret = is_deriv_ring( at, start, num_atoms, da1, j ); - } - } else - if ( da1->typ[*idrv] ) { - ret = is_deriv_ring( at, start, num_atoms, da1, *idrv ); - } - return ret; -} -/******************************************************/ -int remove_deriv( DERIV_AT *da1, int idrv ) -{ - int i, j, ret = -1; - if ( da1->typ[idrv] & DERIV_RING ) { - /* find the first ord of this derivative; ord of ring derivatives are in pairs */ - j = -1; - for ( i = 0; i < DERIV_AT_LEN && da1->typ[i]; i ++ ) { - if ( da1->typ[i] & DERIV_RING ) { - if ( i == idrv || i+1 == idrv ) { - j = i; - break; - } - i ++; /* bypass the second bond to the same derivatization agent */ - } - } - /* delete if data are consistent */ - if ( j >= 0 && j+1 < DERIV_AT_LEN && (da1->typ[j] & DERIV_RING) && (da1->typ[j+1] & DERIV_RING) ) { - for ( ; j < DERIV_AT_LEN-2 && da1->typ[j+2]; j ++ ) { - da1->typ[j] = da1->typ[j+2]; - da1->num[j] = da1->num[j+2]; - da1->ord[j] = da1->ord[j+2]; - } - for ( ; j < DERIV_AT_LEN; j ++ ) { - da1->typ[j] = 0; - da1->num[j] = 0; - da1->ord[j] = 0; - } - ret = 0; - } - } else { - j = idrv; - - for ( ; j < DERIV_AT_LEN-1 && da1->typ[j+1]; j ++ ) { - da1->typ[j] = da1->typ[j+1]; - da1->num[j] = da1->num[j+1]; - da1->ord[j] = da1->ord[j+1]; - } - for ( ; j < DERIV_AT_LEN; j ++ ) { - da1->typ[j] = 0; - da1->num[j] = 0; - da1->ord[j] = 0; - } - ret = 0; - } - return ret; -} -/******************************************************/ -int remove_deriv_mark( DERIV_AT *da1, int idrv ) -{ - int i, j, ret = -1; - if ( da1->typ[idrv] & DERIV_RING ) { - /* find the first ord of this derivative; ord of ring derivatives are in pairs */ - j = -1; - for ( i = 0; i < DERIV_AT_LEN && da1->typ[i]; i ++ ) { - if ( da1->typ[i] & DERIV_RING ) { - if ( i == idrv || i+1 == idrv ) { - j = i; - break; - } - i ++; /* bypass the second bond to the same derivatization agent */ - } - } - /* delete if data are consistent */ - if ( j >= 0 && j+1 < DERIV_AT_LEN && (da1->typ[j] & DERIV_RING) && (da1->typ[j+1] & DERIV_RING) ) { - da1->typ[j] |= DERIV_DUPLIC; - da1->typ[j+1] |= DERIV_DUPLIC; - ret = 0; - } - } else { - j = idrv; - da1->typ[j] |= DERIV_DUPLIC; - ret = 0; - } - return ret; -} -/****************************************************************/ -int EliminateDerivNotInList( inp_ATOM *at, DERIV_AT *da, int num_atoms ) -{ - int i, j, num_da, num_cuts=0, ret=0; - for ( i = 0; i < num_atoms; i ++ ) { - if ( !da[i].typ[0] ) - continue; - /* count deriative attachments */ - for ( num_da = 0; num_da < DERIV_AT_LEN && da[i].typ[num_da]; num_da ++ ) - ; - if ( num_da > 2 ) - return -1; /* should not happen */ - if ( num_da == 2 && da[i].typ[0] != da[i].typ[1] ) { - da[i].typ[0] = da[i].typ[1] = 0; /* do not allow */ - continue; - } - if ( da[i].typ[0] & DERIV_RING ) { - ret = 0; - if ( num_da == 2 && 1 + da[i].num[0] + da[i].num[1] <= MAX_AT_DERIV && - 0 < (ret=is_deriv_ring( at, i, num_atoms, da+i, 0 ) ) ) { - num_cuts += 2; - } else - if ( ret < 0 ) { - return ret; - } else { - da[i].typ[0] = da[i].typ[1] = 0; /* not a derivative */ - } - } else { - ret = 0; - if ( da[i].num[0] <= MAX_AT_DERIV && 0 < (ret = is_deriv_chain( at, i, num_atoms, da+i, 0 )) ) { - num_cuts ++; - j = 1; - } else - if ( ret < 0 ) { - return ret; - } else { - da[i].ord[0] = da[i].ord[1]; - da[i].num[0] = da[i].num[1]; - da[i].typ[0] = da[i].typ[1]; - da[i].typ[1] = 0; - j = 0; - } - if ( da[i].typ[j] && da[i].num[j] <= MAX_AT_DERIV && - 0 < (ret = is_deriv_chain( at, i, num_atoms, da+i, j )) ) { - num_cuts ++; - } else - if ( ret < 0 ) { - return ret; - } else { - da[i].typ[j] = 0; - } - } - } - return num_cuts; -} -/***************************************************************/ -int make_single_cut( inp_ATOM *at, DERIV_AT *da, int iat, int icut ) -{ - int ret = -1; /* error flag */ - int iord = (int)da[iat].ord[icut]; /* ord of the bond in iat */ - if ( da[iat].typ[icut] & DERIV_DUPLIC ) { - return 0; - } else - if ( iord < 0 ) { - return -1; /* program error */ - } else { - /* find other da[] that affect at[iat] */ - int jat = at[iat].neighbor[iord]; /* opposite atom */ - AT_NUMB *p = is_in_the_list( at[jat].neighbor, (AT_NUMB) iat, at[jat].valence ); - int jord = p? (p - at[jat].neighbor) : -1; - int i, iD=1, iT=2; - if ( jord < 0 ) { - return -1; /* program error */ - } - ret = DisconnectInpAtBond( at, NULL, iat, iord ); - if ( ret == 1 ) { - if ( da[iat].typ[icut] & DERIV_RING ) { - /* at[jat] belongs to the main structure */ - at[jat].num_H ++; /* add D to the main structure */ - at[jat].num_iso_H[iD] ++; - at[iat].num_H ++; /* add T to the derivatizing fragment */ - at[iat].num_iso_H[iT] ++; - } else { - at[iat].num_H ++; /* add D to the main structure */ - at[iat].num_iso_H[iD] ++; - at[jat].num_H ++; /* add T to the derivatizing fragment */ - at[jat].num_iso_H[iT] ++; - } - /* adjust ord for other bonds */ - for ( i = 0; i < DERIV_AT_LEN && da[iat].typ[i]; i ++ ) { - if ( da[iat].ord[i] == iord ) { - da[iat].ord[i] = -(1+da[iat].ord[i]); /* mark the bond being disconnected */ - } else - if ( da[iat].ord[i] > iord ) { - da[iat].ord[i] --; - } - } - for ( i = 0; i < DERIV_AT_LEN && da[jat].typ[i]; i ++ ) { - if ( da[jat].ord[i] == jord ) { - /* opposite atom needs the same bond to be disconnected */ - if ( da[iat].num[icut] == da[jat].num[i] ) { - iD=2; /* mark both as fragments */ - } else - if ( da[iat].num[icut] > da[jat].num[i] ) { - iD = 2; /* greater as a main structure */ - iT = 1; /* mark smaller as a derivatizing fragment */ - } - da[jat].ord[i] = -(1+da[jat].ord[i]); - da[jat].typ[i] |= DERIV_DUPLIC; - } else - if ( da[jat].ord[i] > jord ) { - da[jat].ord[i] --; - } - } - } - } - return ret; -} -/***************************************************************/ -int underivatize( ORIG_ATOM_DATA *orig_inp_data ) -{ -#define REMOVE_CUT_DERIV 1 /* remove disconnected derivatizing agents */ - int ret = 0, i, j, k, m, n, num_atoms, num_components, i_component, nFound, num, cur_num_at, len; - int num_cuts, num_ring_cuts, num_cut_pieces, num_cuts_to_check; - inp_ATOM *at = orig_inp_data->at; - INP_ATOM_DATA *inp_cur_data = NULL; - DERIV_AT *da = NULL; - int nTotNumCuts = 0; - - set_R2C_el_numbers( ); - /* prepare */ - num_atoms = remove_terminal_HDT( orig_inp_data->num_inp_atoms, at, 1 ); - /*^^^^^ always accomodate accomodate FIX_TERM_H_CHRG_BUG - IPl, July 2008*/ - orig_inp_data->num_inp_atoms = num_atoms; - - /* initialize */ - UnMarkDisconnectedComponents( orig_inp_data ); - num_cuts = 0; - /* mark */ - num_components = MarkDisconnectedComponents( orig_inp_data, 0 ); - inp_cur_data = (INP_ATOM_DATA *)inchi_calloc( num_components, sizeof(inp_cur_data[0]) ); - for ( i_component = 0; i_component < num_components; i_component ++ ) { - CreateInpAtomData( inp_cur_data+i_component, orig_inp_data->nCurAtLen[i_component], 0 ); - inp_cur_data[i_component].num_at = ExtractConnectedComponent( orig_inp_data->at, orig_inp_data->num_inp_atoms, i_component+1, inp_cur_data[i_component].at ); - /* error processing */ - if ( inp_cur_data[i_component].num_at <= 0 || orig_inp_data->nCurAtLen[i_component] != inp_cur_data[i_component].num_at ) { - ret = -(i_component+1); /* severe error */ - goto exit_function; - } - /* initialize */ - num_atoms = inp_cur_data[i_component].num_at; - at = inp_cur_data[i_component].at; - add_DT_to_num_H( num_atoms, at ); - - UnMarkRingSystemsInp( at, num_atoms ); - UnMarkOtherIndicators( at, num_atoms ); - UnMarkOneComponent( at, num_atoms ); - MarkRingSystemsInp( at, num_atoms, 0 ); - ret = mark_arom_bonds( at, num_atoms ); - if ( ret < 0 ) { - goto exit_function; - } - ret = 0; - if ( da ) inchi_free( da ); - da = (DERIV_AT *)inchi_calloc( num_atoms, sizeof(da[0]) ); - - /* detect derivatives */ - nFound = 0; - for ( i = 0; i < num_atoms; i ++ ) { - if ( at[i].bCutVertex && !da[i].typ[0] ) { - for ( k = 0; k < at[i].valence; k ++ ) { - num = count_one_bond_atoms( at, da, i, k, CFLAG_MARK_BRANCH, &nFound ); - UnMarkOtherIndicators( at, num_atoms ); - if ( num < 0 ) { - ret = num; /* severe error */ - goto exit_function; - } - } - } - } - /* prepare cuts: remove cuts that are not to be done */ - /* in addition, count ring cuts DERIV_RING */ - num_ring_cuts = 0; - num_cuts = 0; - num_cut_pieces = 0; - for ( i = num = 0; i < num_atoms; i ++ ) { - for ( len = 0; len < MAX_AT_DERIV && da[i].typ[len]; len ++ ) - ; - switch( len ) { - case 0: - continue; - case 1: - /* single cut: unconditional */ - num_cuts += 1; - num_cut_pieces += 1; - continue; - case 2: - if ( (da[i].typ[0] & DERIV_RING) && (da[i].typ[1] & DERIV_RING) ) { - /* double cut, unconditional */ - num_ring_cuts += 2; - num_cuts += 2; - num_cut_pieces += 1; - continue; - } else - if ( da[i].typ[0] == DERIV_AMINE_tN && da[i].typ[1] == DERIV_AMINE_tN ) { - /* double cut, unconditional */ - num_cuts += 2; - num_cut_pieces += 2; - continue; - } - if ( da[i].typ[0] == da[i].typ[1] ) { - /* DERIV_BRIDGE_O or DERIV_BRIDGE_NH; cut off the smallest */ - if ( 0 == is_deriv_chain( at, i, num_atoms, da+i, 0 ) ) { - da[i].num[0] = NOT_AT_DERIV; - } - if ( 0 == is_deriv_chain( at, i, num_atoms, da+i, 1 ) ) { - da[i].num[1] = NOT_AT_DERIV; - } - if ( da[i].num[0] > da[i].num[1] ) { - da[i].num[0] = da[i].num[1]; - da[i].ord[0] = da[i].ord[1]; - da[i].typ[0] = da[i].typ[1]; - da[i].typ[1] = 0; - num_cuts += 1; - num_cut_pieces += 1; - } else - if ( da[i].num[0] < da[i].num[1] ) { - da[i].typ[1] = 0; - num_cuts += 1; - num_cut_pieces += 1; - } else { - /* attachments have same size: ignore both */ - /* ??? check for standard derivatizations ??? */ - da[i].typ[0] = 0; - da[i].typ[1] = 0; - } - continue; - } - ret = -88; - goto exit_function; /* unexpected */ - case 3: - if ( da[i].typ[0] == da[i].typ[1] && - da[i].typ[0] == da[i].typ[2] && - da[i].typ[0] == DERIV_AMINE_tN ) { - int x, y, z; - if ( 0 == is_deriv_chain( at, i, num_atoms, da+i, 0 ) ) { - da[i].num[0] = NOT_AT_DERIV; - } - if ( 0 == is_deriv_chain( at, i, num_atoms, da+i, 1 ) ) { - da[i].num[1] = NOT_AT_DERIV; - } - if ( 0 == is_deriv_chain( at, i, num_atoms, da+i, 2 ) ) { - da[i].num[2] = NOT_AT_DERIV; - } - - x = (da[i].num[0] < da[i].num[1])? 0 : 1; - x = (da[i].num[x] < da[i].num[2])? x : 2; /* min */ - z = (da[i].num[0] < da[i].num[1])? 1 : 0; - z = (da[i].num[x] < da[i].num[2])? 2 : z; /* max */ - y = ((x+1)^(z+1))-1; /* median */ - - - if (da[i].num[x] == da[i].num[z] ) { - /* no cuts */ - da[i].typ[0] = 0; - da[i].typ[1] = 0; - da[i].typ[2] = 0; - continue; /* all deriv. agents have same size, ignore */ - /* ??? check for standard derivatizations ??? */ - } else - if ( da[i].num[x] == da[i].num[y] ) { - /* two smallest */ - switch( z ) { - case 0: - da[i].num[0] = da[i].num[1]; - da[i].ord[0] = da[i].ord[1]; - da[i].typ[0] = da[i].typ[1]; - - da[i].num[1] = da[i].num[2]; - da[i].ord[1] = da[i].ord[2]; - da[i].typ[1] = da[i].typ[2]; - break; - case 1: - da[i].num[1] = da[i].num[2]; - da[i].ord[1] = da[i].ord[2]; - da[i].typ[1] = da[i].typ[2]; - break; - case 2: - break; - } - da[i].typ[2] = 0; - num_cuts += 2; - num_cut_pieces += 2; - } else { - /* one smallest */ - if ( x ) { - da[i].num[0] = da[i].num[x]; - da[i].ord[0] = da[i].ord[x]; - da[i].typ[0] = da[i].typ[x]; - } - da[i].typ[1] = 0; - da[i].typ[2] = 0; - num_cuts += 1; - num_cut_pieces += 1; - } - continue; - } - ret = -88; - goto exit_function; /* unexpected */ - case 4: - if ( (da[i].typ[0] & DERIV_RING) && (da[i].typ[1] & DERIV_RING) && - (da[i].typ[2] & DERIV_RING) && (da[i].typ[3] & DERIV_RING) ) { - int n01 = inchi_max( da[i].num[0], da[i].num[1] ); - int n23 = inchi_max( da[i].num[2], da[i].num[3] ); - if ( n01 < n23 && 0 < is_deriv_ring( at, i, num_atoms, da+i, 0) ) { - da[i].typ[2] = 0; - da[i].typ[3] = 0; - num_cuts += 2; - num_ring_cuts += 2; - num_cut_pieces += 1; - } else - if ( n01 > n23 && 0 < is_deriv_ring( at, i, num_atoms, da+i, 2) ) { - da[i].num[0] = da[i].num[2]; - da[i].ord[0] = da[i].ord[2]; - da[i].typ[0] = da[i].typ[2]; - - da[i].num[1] = da[i].num[3]; - da[i].ord[1] = da[i].ord[3]; - da[i].typ[1] = da[i].typ[3]; - - da[i].typ[2] = 0; - da[i].typ[3] = 0; - num_cuts += 2; - num_ring_cuts += 2; - num_cut_pieces += 1; - } else { - da[i].typ[0] = 0; - da[i].typ[1] = 0; - da[i].typ[2] = 0; - da[i].typ[3] = 0; - } - continue; - } - ret = -88; - goto exit_function; /* unexpected */ - } - } - - /* eliminate cases when - da[i1].typ[j1] && da[i2].typ[j2] && - at[i1].neighbor[da[i1].ord[j1]] == i2 && at[i2].neighbor[da[i2].ord[j2]] == i1 - that is, same bond is in the da[] elements of the adjacent atoms */ - nFound = 0; /* number of cuts to remove */ - for ( i = 0; i < num_atoms; i ++ ) { - for ( j = 0; j < MAX_AT_DERIV && da[i].typ[j]; j ++ ) { - if ( da[i].typ[j] & DERIV_DUPLIC ) - continue; - n = at[i].neighbor[(int)da[i].ord[j]]; - if ( n < i ) - continue; - for ( k = 0; k < MAX_AT_DERIV && da[n].typ[k]; k ++ ) { - if ( da[n].typ[k] & DERIV_DUPLIC ) - continue; - m = at[n].neighbor[(int)da[n].ord[k]]; - if ( m == i ) { - /* same bond in da[i].typ[j] and da[n].typ[k] */ - /* check whether both derivatives are acceptable */ - int k1=k, j1=j; - int ret_i = is_deriv_chain_or_ring( at, i, num_atoms, da+i, &j1 ); - int ret_n = is_deriv_chain_or_ring( at, n, num_atoms, da+n, &k1 ); - if ( ret_i < 0 ) { - ret = ret_i; - goto exit_function; - } - if ( ret_n < 0 ) { - ret = ret_n; - goto exit_function; - } - if ( !ret_i || ret_i && ret_n ) { - if ( da[i].typ[j1] & DERIV_RING ) { - num_cuts -= 2; - num_ring_cuts -= 2; - } else { - num_cuts -= 1; - } - num_cut_pieces -= 1; - if (ret = remove_deriv_mark( da+i, j1 )) { - goto exit_function; - } - nFound ++; - } - if ( !ret_n || ret_i && ret_n ) { - if ( da[n].typ[k1] & DERIV_RING ) { - num_cuts -= 2; - num_ring_cuts -= 2; - } else { - num_cuts -= 1; - } - num_cut_pieces -= 1; - if ( ret = remove_deriv_mark( da+n, k1 ) ) { - goto exit_function; - } - nFound ++; - } - } - } - } - } - if ( nFound ) { - for ( i = 0; i < num_atoms; i ++ ) { - for ( j = 0; j < MAX_AT_DERIV && da[i].typ[j]; j ++ ) { - /* attn: j is changed inside the cycle body */ - if ( da[i].typ[j] & DERIV_DUPLIC ) { - if (ret = remove_deriv( da+i, j )) { - goto exit_function; - } - j --; - } - } - } - } - - /* make sure DERIV_RING type disconnections increase */ - /* number of components by the number of disconnected derivateves */ - /* Avoid cases like these: - - O--R--O DO--R--OD - / \ - R--X Y--R => R--XT2 T2Y--R - \ / - O--R--O DO--R--OD - - - - O--O DO--OD - / \ - R--X--O---Y--R => R--X OD2 Y--R - T2 T2 - - */ - /* count DERIV_RING-type attachments */ -#if ( COUNT_ALL_NOT_DERIV == 1 ) - num_cuts_to_check = num_cuts; -#else - num_cuts_to_check = num_ring_cuts; -#endif - if ( num_cuts_to_check >= 2 ) - { - /* check */ - R2C_ATPAIR *ap = (R2C_ATPAIR *) inchi_malloc( num_cuts_to_check * sizeof(ap[0]) ); - AT_NUMB comp_num; - int /*n,*/ m_at, m_ord; - AT_NUMB at1, at2; - if ( !ap ) { - ret = -1; - goto exit_function; /* malloc failure */ - } -repeat_without_deriv_ring: - comp_num = 0; - /* fill out the array of bonds to be cut */ - for ( i = j = 0; i < num_atoms; i ++ ) { - if ( (da[i].typ[0] & DERIV_RING) && (da[i].typ[1] & DERIV_RING) && - da[i].num[0] <= MAX_AT_DERIV && da[i].num[1] <= MAX_AT_DERIV ) { - if ( j+1 >= num_cuts_to_check ) { - ret = -2; - goto exit_r2c_num; /* wrong number of cuts = num */ - } - for ( k = 0; k < 2; k ++ ) { - at1 = i; - at2 = at[i].neighbor[(int)da[i].ord[k]]; - n = ( at1 > at2 ); - ap[j].at[n] = at1; - ap[j].at[1-n] = at2; /* ap[j].at[0] < ap[j].at[1] */ - j ++; - } - if ( 0 < cmp_r2c_atpair( ap+j-2, ap+j-1 ) ) { - R2C_ATPAIR ap1 = ap[j-2]; - ap[j-2] = ap[j-1]; - ap[j-1] = ap1; /* sort each pair */ - } - } -#if ( COUNT_ALL_NOT_DERIV == 1 ) - else { - for ( k = 0; k < DERIV_AT_LEN && da[i].typ[k]; k ++ ) { - if ( j >= num_cuts_to_check || (da[i].typ[k] & DERIV_RING) ) { - ret = -2; - goto exit_r2c_num; /* wrong number of cuts = num or wrong type */ - } - at1 = i; - at2 = at[i].neighbor[(int)da[i].ord[k]]; - n = ( at1 > at2 ); - ap[j].at[n] = at1; - ap[j].at[1-n] = at2; /* ap[j].at[0] < ap[j].at[1] */ - j ++; - } - } -#endif - - } - if ( j != num_cuts_to_check ) { - ret = -3; - goto exit_r2c_num; /* wrong number of cuts = num */ - } - /* !!!!!!!! check that there are no derivatives inside a derivative */ - comp_num = 0; /* here it is the number of removed cuts */ - for ( i = 0; i < num_cuts_to_check; i += j ) { - for ( j = n = 0; j < 2; j ++ ) { - int atj = (int)ap[i].at[j]; - if ( da[atj].typ[0] && at[atj].neighbor[(int)da[atj].ord[0]] == ap[i].at[1-j] ) { - k = j; - n ++; - m_at = atj; - m_ord = 0; - } else - if ( da[atj].typ[1] && at[atj].neighbor[(int)da[atj].ord[1]] == ap[i].at[1-j] ) { - k = j; - n ++; - m_at = atj; - m_ord = 1; - } - } - if ( n != 1 ) { - ret = -3; - goto exit_r2c_num; /* wrong atom pair */ - } - if ( (da[m_at].typ[m_ord] & DERIV_RING) ) { - n = (int)ap[i].at[k]; /* atom inside the derivation attachment */ - j = 2; /* number of bonds to cut */ - if ( i+j > num_cuts_to_check || (int)ap[i+1].at[0] != n && (int)ap[i+1].at[1] != n ) { - ret = -3; - goto exit_r2c_num; /* wrong atom pair */ - } - } else { - n = ap[i].at[1-k]; /* atom inside the derivation attachment */ - j = 1; /* number of bonds to cut */ - } - - /* at[n] belongs to the derivation agent */ - cur_num_at = mark_atoms_ap( at, n, ap+i, j, 0, 1 ); - for ( k = 0; k < num_cuts_to_check; k ++ ) { - if ( k == i || k==i+j-1 ) - continue; - if ( at[(int)ap[k].at[0]].at_type || at[(int)ap[k].at[1]].at_type ) { - /* unmark the cut: found a cut inside the derivatizing agent */ - da[m_at].typ[m_ord] |= DERIV_UNMARK; - num_cuts -= 1; - num_cut_pieces -= 1; - if ( j == 2 ) { - da[m_at].typ[1-m_ord] |= DERIV_UNMARK; - num_cuts -= 1; - num_ring_cuts -= 2; - } - comp_num ++; - break; - } - } - UnMarkOtherIndicators( at, num_atoms ); - } - if ( comp_num ) { - for ( i = 0; i < num_atoms; i ++ ) { - if ( da[i].typ[0] & DERIV_UNMARK ) { - da[i].num[0] = da[i].num[1]; - da[i].ord[0] = da[i].ord[1]; - da[i].typ[0] = da[i].typ[1]; - da[i].typ[1] = 0; - j = 0; - } else { - j = 1; - } - if ( da[i].typ[j] & DERIV_UNMARK ) { - da[i].typ[j] = 0; - } - } -#if ( COUNT_ALL_NOT_DERIV == 1 ) - num_cuts_to_check = num_cuts; -#else - num_cuts_to_check = num_ring_cuts; -#endif - if ( num_cuts < 0 || num_ring_cuts < 0 || num_cut_pieces < 0 ) { - ret = -3; - goto exit_r2c_num; /* wrong number of cuts = num */ - } - goto repeat_without_deriv_ring; - } - - /* sort the bonds for subsequent searching by bisections */ - if ( num_cuts_to_check > 1 ) { - qsort( ap, num_cuts_to_check, sizeof(ap[0]), cmp_r2c_atpair); - } - /* mark components to be disconnected */ - comp_num = 0; /* number of components */ - cur_num_at = 0; /* number of atoms left after disconnecting the derivatizing agent */ - UnMarkOtherIndicators( at, num_atoms ); - for ( i = 0; i < num_cuts_to_check; i ++ ) { - n = 0; - for ( j = 0; j < 2; j ++ ) { - if ( da[(int)ap[i].at[j]].typ[0] ) { - k = j; - n ++; - } - } - if ( n != 1 ) { - ret = -3; - goto exit_r2c_num; /* wrong atom pair */ - } - n = ap[i].at[k]; /* marked atom */ - if ( (da[n].typ[0] & DERIV_RING) ) { - n = ap[i].at[1-k]; - } - /* at[n] belongs to the derivation agent */ - if ( !at[n].at_type ) { - comp_num ++; - cur_num_at = mark_atoms_ap( at, n, ap, num_cuts_to_check, cur_num_at, comp_num ); - } - } - if ( comp_num > 1 ) { - /* eliminate offending DERIV_RING type derivatives */ - if ( num_ring_cuts <= 2 ) { - ret = -99; - goto exit_r2c_num; - } - n = 0; - for ( i = j = 0; i < num_atoms; i ++ ) { - if ( (da[i].typ[0] & DERIV_RING) && (da[i].typ[1] & DERIV_RING) ) { - int at1a = at[i].neighbor[(int)da[i].ord[0]]; - int at2a = at[i].neighbor[(int)da[i].ord[1]]; - if ( at[at1a].at_type != at[at2a].at_type ) { - da[i].typ[0] = 0; /* eliminate this cut */ - da[i].typ[1] = 0; - n ++; - num_cuts_to_check -= 2; - num_cuts -= 2; - num_ring_cuts -= 2; - num_cut_pieces -= 1; - } - } - } - if ( n > 0 && num_cuts_to_check > 2 ) { - goto repeat_without_deriv_ring; - } - } - ret = 0; -exit_r2c_num: - inchi_free( ap ); - UnMarkOtherIndicators( at, num_atoms ); - if ( ret < 0 || num_cuts_to_check >= 2 && cur_num_at < MIN_AT_LEFT_DERIV ) { - goto exit_function; /* unexpected error or nothing left */ - } - } - - if ( !num_cuts ) { - continue; /*goto exit_function;*/ - } - /* eliminate derivatives that are not in the list */ - num_cuts = EliminateDerivNotInList( at, da, num_atoms ); - if ( num_cuts < 0 ) { - ret = num_cuts; - goto exit_function; - } - - - /* make cuts */ - num_cuts = 0; - for ( i = num = 0; i < num_atoms; i ++ ) { - for ( len = 0; len < MAX_AT_DERIV && da[i].typ[len]; len ++ ) - ; - switch( len ) { - case 0: - continue; - case 1: - /* single cut: unconditional */ - make_single_cut( at, da, i, 0 ); - num_cuts += 1; - continue; - case 2: - if ( (da[i].typ[0] & DERIV_RING) && (da[i].typ[1] & DERIV_RING) || - da[i].typ[0] == DERIV_AMINE_tN && da[i].typ[1] == DERIV_AMINE_tN ) { - /* double cut, unconditional */ - make_single_cut( at, da, i, 1 ); - make_single_cut( at, da, i, 0 ); - num_cuts += 1; - continue; - } - if ( da[i].typ[0] == da[i].typ[1] ) { - /* DERIV_BRIDGE_O or DERIV_BRIDGE_NH; cut off the smallest */ - if ( da[i].num[0] > da[i].num[1] ) { - make_single_cut( at, da, i, 1 ); - num_cuts += 1; - } else - if ( da[i].num[0] < da[i].num[1] ) { - make_single_cut( at, da, i, 0 ); - num_cuts += 1; - } - continue; - } - ret = -88; - goto exit_function; /* unexpected */ - case 3: - if ( da[i].typ[0] == da[i].typ[1] && - da[i].typ[0] == da[i].typ[2] && - da[i].typ[0] == DERIV_AMINE_tN ) { - int x, y, z; - x = (da[i].num[0] < da[i].num[1])? 0 : 1; - x = (da[i].num[x] < da[i].num[2])? x : 2; /* min */ - z = (da[i].num[0] < da[i].num[1])? 1 : 0; - z = (da[i].num[x] < da[i].num[2])? 2 : z; /* max */ - y = ((x+1)^(z+1))-1; /* median */ - if (da[i].num[x] == da[i].num[z] ) - continue; /* all deriv. agents have same size */ - /* two smallest */ - if ( da[i].num[x] == da[i].num[y] && x < y ) { - int t = x; /* first cut x > y */ - x = y; - y = t; - } - make_single_cut( at, da, i, x ); - num_cuts += 1; - if ( da[i].num[x] == da[i].num[y] ) { - /* equally small */ - make_single_cut( at, da, i, y ); - num_cuts += 1; - } - continue; - } - ret = -88; - goto exit_function; /* unexpected */ - case 4: - if ( (da[i].typ[0] & DERIV_RING) && (da[i].typ[1] & DERIV_RING) && - (da[i].typ[2] & DERIV_RING) && (da[i].typ[3] & DERIV_RING) ) { - int n01 = inchi_max( da[i].num[0], da[i].num[1] ); - int n23 = inchi_max( da[i].num[2], da[i].num[3] ); - if ( n01 < n23 ) { - make_single_cut( at, da, i, 1 ); - make_single_cut( at, da, i, 0 ); - num_cuts += 1; - } else - if ( n01 > n23 ) { - make_single_cut( at, da, i, 3 ); - make_single_cut( at, da, i, 2 ); - num_cuts += 1; - } - continue; - } - } - } - nTotNumCuts += num_cuts; -#if ( REMOVE_CUT_DERIV == 1 ) /* normally YES */ - if ( num_cuts ) { - /* remove marked with Tritium disconnected derivative attachments */ - ORIG_ATOM_DATA Orig_inp_data1, *orig_inp_data1; - INP_ATOM_DATA *inp_cur_data1 = NULL; - int num_components1, i_component1, num_component_left=0; - orig_inp_data1 = &Orig_inp_data1; - memset( orig_inp_data1, 0, sizeof(orig_inp_data1[0]) ); - UnMarkRingSystemsInp( at, num_atoms ); - UnMarkOtherIndicators( at, num_atoms ); - UnMarkOneComponent( at, num_atoms ); - for (i = 0; i < num_atoms; i ++ ) { - orig_inp_data1->num_inp_bonds += at[i].valence; - } - orig_inp_data1->num_inp_bonds /= 2; - orig_inp_data1->num_inp_atoms = num_atoms; - orig_inp_data1->at = at; /* = from inp_cur_data[i_component].at */ - num_components1 = MarkDisconnectedComponents( orig_inp_data1, 0 ); - inp_cur_data1 = (INP_ATOM_DATA *)inchi_calloc( num_components1, sizeof(inp_cur_data1[0]) ); - /* extract components and discard disconnected derivatizing agents */ - for ( i_component1 = 0; i_component1 < num_components1; i_component1 ++ ) { - CreateInpAtomData( inp_cur_data1+i_component1, orig_inp_data1->nCurAtLen[i_component1], 0 ); - inp_cur_data1[i_component1].num_at = ExtractConnectedComponent( orig_inp_data1->at, orig_inp_data1->num_inp_atoms, - i_component1+1, inp_cur_data1[i_component1].at ); - /* error processing */ - if ( inp_cur_data1[i_component1].num_at <= 0 || orig_inp_data1->nCurAtLen[i_component1] != inp_cur_data1[i_component1].num_at ) { - ret = -(i_component1+1); /* severe error */ - break; - } - /* if the component has tritium then discard: it is a derivatizing agent */ - for (i = 0; i < inp_cur_data1[i_component1].num_at; i ++ ) { - if ( inp_cur_data1[i_component1].at[i].num_iso_H[1] ) { - inp_cur_data1[i_component1].at[i].num_iso_H[1] = 0; /* remove deuterium */ - } - if ( inp_cur_data1[i_component1].at[i].num_iso_H[2] ) { - FreeInpAtomData( inp_cur_data1+i_component1 ); - break; - } - } - } - /* merge components into one -- must be only one */ - for ( i_component1 = 0, num_atoms = 0; i_component1 < num_components1; i_component1 ++ ) { - num_atoms += inp_cur_data1[i_component1].num_at; - } - at = (inp_ATOM *) inchi_calloc( num_atoms, sizeof(at[0]) ); - cur_num_at = 0; - for ( i_component1 = 0; i_component1 < num_components1; i_component1 ++ ) { - /* clean and prepare */ - if ( !inp_cur_data1[i_component1].num_at ) - continue; /* removed derivatizing object */ - /*UnMarkOneComponent( inp_cur_data1[i_component1].at, inp_cur_data1[i_component1].num_at );*/ - /* merge one by one */ - cur_num_at = add_inp_ATOM( at, num_atoms, cur_num_at, inp_cur_data1[i_component1].at, inp_cur_data1[i_component1].num_at ); - FreeInpAtomData( inp_cur_data1+i_component1 ); /* cleanup */ - num_component_left ++; - } - /* replace the component */ - /* order of the following two statements is critically important */ - UnMarkDisconnectedComponents( orig_inp_data1 ); /* orig_inp_data1->at is same as inp_cur_data[i_component].at */ - FreeInpAtomData( inp_cur_data+i_component ); /* cleanup the original component */ - inp_cur_data[i_component].at = at; - inp_cur_data[i_component].num_at = cur_num_at; - inchi_free( inp_cur_data1 ); - } -#endif - } - if ( nTotNumCuts ) { - /* merge components into one */ - for ( i = 0, num_atoms = 0; i < num_components; i ++ ) { - num_atoms += inp_cur_data[i].num_at; - } - at = (inp_ATOM *) inchi_calloc( num_atoms, sizeof(at[0]) ); - cur_num_at = 0; - for ( i = 0; i < num_components; i ++ ) { - /* clean and prepare */ - UnMarkRingSystemsInp( inp_cur_data[i].at, inp_cur_data[i].num_at ); - UnMarkOtherIndicators( inp_cur_data[i].at, inp_cur_data[i].num_at ); - UnMarkOneComponent( inp_cur_data[i].at, inp_cur_data[i].num_at ); - subtract_DT_from_num_H( inp_cur_data[i].num_at, inp_cur_data[i].at ); - /* merge one by one */ - cur_num_at = add_inp_ATOM( at, num_atoms, cur_num_at, inp_cur_data[i].at, inp_cur_data[i].num_at ); - } - /* replace orig_inp_data */ - if ( cur_num_at == num_atoms ) { - inchi_free( orig_inp_data->at ); - orig_inp_data->at = at; - orig_inp_data->num_inp_atoms = cur_num_at; - if ( orig_inp_data->szCoord ) { - inchi_free( orig_inp_data->szCoord ); - orig_inp_data->szCoord = NULL; - } - UnMarkDisconnectedComponents( orig_inp_data ); - } else { - if ( at ) { - inchi_free( at ); - at = NULL; - } - ret = -999; /* num atoms mismatch */ - } - } -exit_function: - if ( da ) { - inchi_free( da ); - da = NULL; - } - for ( i_component = 0; i_component < num_components; i_component ++ ) { - FreeInpAtomData( inp_cur_data+i_component ); - } - inchi_free( inp_cur_data ); - inp_cur_data = NULL; - - return ret? ret : nTotNumCuts; -} - -#endif /* UNDERIVATIZE */ -/********************************************************************/ -#if ( RING2CHAIN == 1 ) -/* - type=1 (incl sugars: W=O, A=C(sat), Z=C(sat), Y=O, B=C(sat)-OH - - A---W A---WH - / | / - | | ---> | - \ | \ - B---Z---YH B---Z===Y - | | - | | - C(opt) C(opt) - - type=2 [not implemented] - - R---W R---WH - / \ / - | Z ---> | Z - \ / \ // - R---YH R---Y - -*/ -#define R2C_EMPTY 127 -typedef struct tagRing2Chain { /* atom Z */ - char type; /* 1 => sugar-like */ - char ordW; /* ordering number of W-neighbor; bond to break; H to add */ - char ordY; /* ordering number of YH-neighbor; bond to increment; H to remove */ - char ordC; /* atom B = C(sat) */ - char ordCopt; /* if exists, saturated C connected by a chain-bond to Z */ -} R2C_AT; - -int detect_r2c_Zatom( inp_ATOM *at, R2C_AT *da, int iZ ); -int cut_ring_to_chain( inp_ATOM *at, R2C_AT *da, int iZ ); - -/********************************************************************/ -int detect_r2c_Zatom( inp_ATOM *at, R2C_AT *da, int iZ ) -{ - int i, j, neigh, neighneigh, nRingSystem, num_found; - R2C_AT da1; - if ( at[iZ].valence > 4 ) - return 0; - if ( at[iZ].valence != at[iZ].chem_bonds_valence ) - return 0; /* approach limitation: no double bonds */ - - if ( at[iZ].el_number != el_number_C ) - return 0; /* sugar-specific */ - - if ( at[iZ].nNumAtInRingSystem < 5 ) - return 0; /* not in a suitable ring */ - - if ( !at[iZ].bCutVertex ) - return 0; /* recognize only type 1 for now */ - - nRingSystem = at[iZ].nRingSystem; - memset ( &da1, R2C_EMPTY, sizeof(da1) ); - - for ( i = 0, num_found = 0; i < at[iZ].valence; i ++ ) { - neigh = at[iZ].neighbor[i]; - if ( at[neigh].charge || at[neigh].radical ) - return 0; - if ( at[neigh].el_number == el_number_O && - at[neigh].valence == 1 && - at[neigh].chem_bonds_valence == 1 && - at[neigh].num_H == 1 ) { - /* found Z-OH, i.e. Z-YH */ - if ( da1.ordY == R2C_EMPTY ) { - da1.ordY = i; - num_found ++; - continue; - } else { - return 0; - } - } - if ( at[neigh].el_number == el_number_O && - at[neigh].valence == 2 && - at[neigh].chem_bonds_valence == 2 && - at[neigh].num_H == 0 && - at[neigh].nRingSystem == nRingSystem ) { - /* found Z-O-, i.e. Z-W- */ - if ( da1.ordW == R2C_EMPTY ) { - /* j = index of the oppozite to at[iZ] neighbor of at[neigh] */ - j = (at[neigh].neighbor[0] == iZ); - neighneigh = at[neigh].neighbor[j]; - if ( at[neighneigh].valence != at[neighneigh].chem_bonds_valence || - at[neighneigh].el_number != el_number_C ) - return 0; /* sugar-specific */ - da1.ordW = i; - num_found ++; - continue; - } else { - return 0; - } - } - if ( at[neigh].el_number == el_number_C && - at[neigh].valence > 2 && - at[neigh].chem_bonds_valence == at[neigh].valence && - at[neigh].num_H <= 1 && - at[neigh].nRingSystem == nRingSystem ) { - /* sugar-specfic: carbon in the ring should have -OH neighbor */ - int iOH; - for ( j = 0; j < at[neigh].valence; j ++ ) { - iOH = at[neigh].neighbor[j]; - if ( at[iOH].el_number == el_number_O && - at[iOH].valence == 1 && - at[iOH].chem_bonds_valence == 1 && - at[iOH].num_H == 1 && - !at[iOH].charge && !at[iOH].radical ) { - if ( da1.ordC == R2C_EMPTY ) { - da1.ordC = i; - num_found ++; - break; - } else { - return 0; - } - } - } - if ( j < at[neigh].valence ) - continue; - } - if ( at[neigh].el_number == el_number_C && - at[neigh].chem_bonds_valence == at[neigh].valence && - at[neigh].nRingSystem != nRingSystem ) { - /* extra carbon neighbor of Z */ - if ( da1.ordCopt == R2C_EMPTY ) { - da1.ordCopt = i; - continue; - } - } - return 0; /* unexpectd neighbor */ - } - if (num_found == 3) { - da1.type = 1; - da[iZ] = da1; - return 1; /* disconnection found */ - } - return 0; -} -/********************************************************************/ -int cut_ring_to_chain( inp_ATOM *at, R2C_AT *da, int iZ ) -{ - int ret = -1; /* error flag */ - int iordW = (int)da[iZ].ordW; /* ord of the bond in iZ */ - int iordY = (int)da[iZ].ordY; /* ord of the bond in iZ */ - int iordC = (int)da[iZ].ordC; - int iW, iY, num_iso_H, i, jordZ; - AT_NUMB *p; - - if ( da[iZ].type != 1 ) { - return 0; - } - if ( 0 > iordW || iordW >= at[iZ].valence || - 0 > iordY || iordY >= at[iZ].valence || - 0 > iordC || iordC >= at[iZ].valence /* suger-specific*/) { - return -1; /* program error */ - } - /* find other da[] that affect at[iZ] */ - iW = at[iZ].neighbor[iordW]; /* opposite atom to disconnect and add H */ - iY = at[iZ].neighbor[iordY]; /* opposite atom to increment the bond and remove H*/ - if ( !at[iY].num_H || at[iZ].bond_type[iordY] != BOND_TYPE_SINGLE ) { - return -2; /* program error */ - } - /* increment at[iZ]--at[iY] bond */ - p = is_in_the_list( at[iY].neighbor, (AT_NUMB) iZ, at[iY].valence ); - if ( !p ) { - return -3; /* program error */ - } - jordZ = p - at[iY].neighbor; - at[iZ].bond_type[iordY] ++; - at[iZ].chem_bonds_valence ++; - at[iY].bond_type[jordZ] ++; - at[iY].chem_bonds_valence ++; - - /* disconnect at[iZ]--at[iW] bond */ - ret = DisconnectInpAtBond( at, NULL, iZ, iordW ); - if ( ret != 1 ) { - return -4; /* program error */ - } - /* disconnection succeeded */ - /* transfer H from at[iY] to at[iW] */ - num_iso_H = NUM_ISO_H(at, iY); - if ( at[iY].num_H == num_iso_H ) { - for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { - if ( at[iY].num_iso_H[i] ) { - at[iY].num_iso_H[i] --; - at[iW].num_iso_H[i] ++; - } - } - } - at[iY].num_H --; - at[iW].num_H ++; - return 1; -} -/********************************************************************/ -int Ring2Chain( ORIG_ATOM_DATA *orig_inp_data ) -{ - int ret = 0, i, j, n, num_atoms, num_components, nFound, num, num_cuts, iZ, cur_num_at; - inp_ATOM *at = orig_inp_data->at; - INP_ATOM_DATA *inp_cur_data = NULL; - R2C_AT *da = NULL; - - set_R2C_el_numbers( ); - /* prepare */ - num_atoms = remove_terminal_HDT( orig_inp_data->num_inp_atoms, at, 1 ); - /*^^^^^ always accomodate accomodate FIX_TERM_H_CHRG_BUG - IPl, July 2008*/ - orig_inp_data->num_inp_atoms = num_atoms; - - /* initialize */ - UnMarkDisconnectedComponents( orig_inp_data ); - num_cuts = 0; - /* mark */ - num_components = MarkDisconnectedComponents( orig_inp_data, 0 ); - inp_cur_data = (INP_ATOM_DATA *)inchi_calloc( num_components, sizeof(inp_cur_data[0]) ); - iZ = -1; - for ( j = 0; j < num_components; j ++ ) { - CreateInpAtomData( inp_cur_data+j, orig_inp_data->nCurAtLen[j], 0 ); - inp_cur_data[j].num_at = ExtractConnectedComponent( orig_inp_data->at, orig_inp_data->num_inp_atoms, j+1, inp_cur_data[j].at ); - /* error processing */ - if ( inp_cur_data[j].num_at <= 0 || orig_inp_data->nCurAtLen[j] != inp_cur_data[j].num_at ) { - ret = -(j+1); /* severe error */ - goto exit_function; - } - /* initialize */ - num_atoms = inp_cur_data[j].num_at; - at = inp_cur_data[j].at; - add_DT_to_num_H( num_atoms, at ); - - UnMarkRingSystemsInp( at, num_atoms ); - UnMarkOtherIndicators( at, num_atoms ); - UnMarkOneComponent( at, num_atoms ); - MarkRingSystemsInp( at, num_atoms, 0 ); - ret = mark_arom_bonds( at, num_atoms ); - if ( ret < 0 ) { - goto exit_function; - } - ret = 0; - if ( da ) inchi_free( da ); - da = (R2C_AT *)inchi_calloc( num_atoms, sizeof(da[0]) ); - - /* detect ring-to-chain possibilities */ - nFound = 0; - for ( i = 0, num=0; i < num_atoms; i ++ ) { - if ( at[i].bCutVertex /* type 1 specific*/ && !da[i].type ) { - num += (n=detect_r2c_Zatom( at, da, i )); - if ( n == 1 ) - iZ = i; - UnMarkOtherIndicators( at, num_atoms ); - } - } - - if ( num == 1 ) { - /* convert ring to chain: make single cut */ - ret = cut_ring_to_chain( at, da, iZ ); - if ( ret < 0 ) { - goto exit_function; - } - num_cuts += (ret == 1); - } else - if ( num ) { - /* allocate an array of bonds to be cut */ - R2C_ATPAIR *ap = (R2C_ATPAIR *) inchi_malloc( sizeof(ap[0]) * num ); - AT_NUMB comp_num = 0; - if ( !ap ) { - ret = -1; /* malloc failure */ - goto exit_function; - } - /* fill out the array of bonds to be cut */ - for ( i = j = 0; i < num_atoms; i ++ ) { - if ( da[i].type == 1 ) { - AT_NUMB at1 = i; - AT_NUMB at2 = at[i].neighbor[(int)da[i].ordW]; - if ( j >= num ) { - ret = -2; - goto exit_r2c_num; /* wrong number of cuts = num */ - } - n = ( at1 > at2 ); - ap[j].at[n] = at1; - ap[j].at[1-n] = at2; /* ap[j].at[0] < ap[j].at[1] */ - j ++; - } - } - if ( j != num ) { - ret = -3; - goto exit_r2c_num; /* wrong number of cuts = num */ - } - /* sort the bonds for subsequent searching by bisections */ - qsort( ap, num, sizeof(ap[0]), cmp_r2c_atpair); - /* mark components to be disconnected */ - for ( i = 0; i < num; i ++ ) { - for ( j = 0; j < 2; j ++ ) { - if ( !at[ap[i].at[j]].at_type ) { - comp_num ++; - mark_atoms_ap( at, (int)ap[i].at[j], ap, num, 0, comp_num ); - } - } - } - /* convert ring to chain */ - for ( i = 0; i < num; i ++ ) { - int i1 = ap[i].at[0]; - int i2 = ap[i].at[1]; - iZ = -1; - if ( at[i1].at_type == at[i2].at_type ) { - /* by definition, there are no adjacent iZ atoms; one iZ atom per bond */ - if ( da[i1].type == 1 && at[i1].neighbor[(int)da[i1].ordW] == i2 ) { - iZ = i1; - } else - if ( da[i2].type == 1 && at[i2].neighbor[(int)da[i2].ordW] == i1 ) { - iZ = i2; - } else { - ret = -3; - goto exit_r2c_num; - } - ret = cut_ring_to_chain( at, da, iZ ); - if ( ret < 0 ) { - goto exit_r2c_num; - } - num_cuts += (ret == 1); - } - } - ret = 0; -exit_r2c_num: - inchi_free( ap ); - UnMarkOtherIndicators( at, num_atoms ); - if ( ret < 0 ) { - goto exit_function; - } - } - } - if ( num_cuts ) { - /* merge components into one */ - for ( i = 0, num_atoms = 0; i < num_components; i ++ ) { - num_atoms += inp_cur_data[i].num_at; - } - at = (inp_ATOM *) inchi_calloc( num_atoms, sizeof(at[0]) ); - cur_num_at = 0; - for ( i = 0; i < num_components; i ++ ) { - /* clean and prepare */ - UnMarkRingSystemsInp( inp_cur_data[i].at, inp_cur_data[i].num_at ); - UnMarkOtherIndicators( inp_cur_data[i].at, inp_cur_data[i].num_at ); - UnMarkOneComponent( inp_cur_data[i].at, inp_cur_data[i].num_at ); - subtract_DT_from_num_H( inp_cur_data[i].num_at, inp_cur_data[i].at ); - /* merge one by one */ - cur_num_at = add_inp_ATOM( at, num_atoms, cur_num_at, inp_cur_data[i].at, inp_cur_data[i].num_at ); - } - /* replace orig_inp_data */ - if ( cur_num_at == num_atoms ) { - inchi_free( orig_inp_data->at ); - orig_inp_data->at = at; - orig_inp_data->num_inp_atoms = cur_num_at; - if ( orig_inp_data->szCoord ) { - inchi_free( orig_inp_data->szCoord ); - orig_inp_data->szCoord = NULL; - } - UnMarkDisconnectedComponents( orig_inp_data ); - } else { - if ( at ) { - inchi_free( at ); - at = NULL; - } - ret = -999; /* num atoms mismatch */ - } - } -exit_function: - if ( da ) { - inchi_free( da ); - da = NULL; - } - for ( j = 0; j < num_components; j ++ ) { - FreeInpAtomData( inp_cur_data+j ); - } - inchi_free( inp_cur_data ); - inp_cur_data = NULL; - - return ret? ret : num_cuts; -} -#endif /* RING2CHAIN */ - +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +/* + Ring-chain tautomerism, 'underivatization', etc. +*/ + +#include +#include + +#include "mode.h" +#include "ichinorm.h" +#include "ichierr.h" + +#if ( FIND_RING_SYSTEMS == 1 ) /* { */ + +/********************************************************************************/ +int MarkRingSystemsInp( inp_ATOM *at, int num_atoms, int start ) +{ + AT_NUMB *nStackAtom = NULL; + int nTopStackAtom=-1; + AT_NUMB *nRingStack = NULL; + int nTopRingStack=-1; /* was AT_NUMB */ + AT_NUMB *nDfsNumber = NULL; + AT_NUMB *nLowNumber = NULL; + S_CHAR *cNeighNumb = NULL; + AT_NUMB nDfs; +#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) + AT_NUMB nRs, *nRsConnect = NULL; + int k; + AT_NUMB *tree = NULL; + int nNumConnect, nMaxNumConnect, nLenConnect; +#endif + AT_NUMB nNumAtInRingSystem; + int i, j, u, /*start,*/ nNumRingSystems, nNumStartChildren; + + /* allocate arrays */ + nStackAtom = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nStackAtom[0])); + nRingStack = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nRingStack[0])); + nDfsNumber = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nDfsNumber[0])); + nLowNumber = (AT_NUMB *) inchi_malloc(num_atoms*sizeof(nLowNumber[0])); + cNeighNumb = (S_CHAR *) inchi_malloc(num_atoms*sizeof(cNeighNumb[0])); +#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) + nRsConnect = (AT_NUMB *)inchi_calloc(3*num_atoms+3,sizeof(nRsConnect[0])); +#endif + /* check allocation */ + if ( !nStackAtom || !nRingStack || !nDfsNumber || !nLowNumber || !cNeighNumb +#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) + || !nRsConnect +#endif + ) { + nNumRingSystems = CT_OUT_OF_RAM; /* program error */ /* */ + goto exit_function; + } + + /******************************************** + * + * Find Cut-vertices & Blocks + * + ********************************************/ + + /* initiation */ + /*start = 0;*/ + nNumRingSystems = 0; + u = start; /* start atom */ + nDfs = 0; + nTopStackAtom =-1; + nTopRingStack =-1; + memset( nDfsNumber, 0, num_atoms*sizeof(nDfsNumber[0])); + memset( cNeighNumb, 0, num_atoms*sizeof(cNeighNumb[0])); + /* push the start atom on the stack */ + nLowNumber[u] = nDfsNumber[u] = ++nDfs; + nStackAtom[++nTopStackAtom] = (AT_NUMB)u; + nRingStack[++nTopRingStack] = (AT_NUMB)u; + + nNumStartChildren = 0; + + do { + /* advance */ +advance_block: + /*if ( (int)at[i=nStackAtom[nTopStackAtom]].valence > (j = (int)cNeighNumb[i]) )*/ + /* replaced due to missing sequence point */ + if ( i=(int)nStackAtom[nTopStackAtom], j = (int)cNeighNumb[i], (int)at[i].valence > j ) + { + cNeighNumb[i] ++; + u = (int)at[i].neighbor[j]; + if ( !nDfsNumber[u] ) { + /* tree edge, 1st visit -- advance */ + nStackAtom[++nTopStackAtom] = (AT_NUMB)u; + nRingStack[++nTopRingStack] = (AT_NUMB)u; + nLowNumber[u] = nDfsNumber[u] = ++nDfs; + nNumStartChildren += (i == start); + } else + if ( !nTopStackAtom || u != (int)nStackAtom[nTopStackAtom-1] ) { /* may comment out ? */ + /* back edge: u is not a predecessor of i */ + if ( nDfsNumber[u] < nDfsNumber[i] ) { + /* Back edge, 1st visit: u is an ancestor of i. Compare */ + if ( nLowNumber[i] > nDfsNumber[u] ) { + nLowNumber[i] = nDfsNumber[u]; + } + } + } /* may comment out ? */ + goto advance_block; + } else { + cNeighNumb[i] = 0; + } + + /* back up */ + if ( i != start ) { + u = (int)nStackAtom[nTopStackAtom-1]; /* predecessor of i */ + if ( nLowNumber[i] >= nDfsNumber[u] ) { + /* output the block */ + nNumRingSystems ++; + at[u].nBlockSystem = nNumRingSystems; + if ( u != start || nNumStartChildren > 1 ) { + at[u].bCutVertex += 1; + } + while ( nTopRingStack >= 0 ) { + j = nRingStack[nTopRingStack--]; + at[j].nBlockSystem = nNumRingSystems; /* mark the atom */ + if ( i == j ) { + break; + } + } + } else + if ( nLowNumber[u] > nLowNumber[i] ) { + /* inherit */ + nLowNumber[u] = nLowNumber[i]; + } + } + } while ( --nTopStackAtom >= 0 ); + + + /******************************************** + * + * Find Ring Systems + * Including chain atoms X: A-X-B, where the bonds (of any kind) are bridges. + * + ********************************************/ + + /* initiation */ + /*start = 0;*/ + nNumRingSystems = 0; + u = start; /* start atom */ + nDfs = 0; + nTopStackAtom =-1; + nTopRingStack =-1; + memset( nDfsNumber, 0, num_atoms*sizeof(nDfsNumber[0])); + memset( cNeighNumb, 0, num_atoms*sizeof(cNeighNumb[0])); + /* push the start atom on the stack */ + nLowNumber[u] = nDfsNumber[u] = ++nDfs; + nStackAtom[++nTopStackAtom] = (AT_NUMB)u; + nRingStack[++nTopRingStack] = (AT_NUMB)u; +#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) + nNumConnect = nLenConnect = nMaxNumConnect = 0; +#endif + + do { + /* advance */ +advance_ring: + /*if ( (int)at[i=nStackAtom[nTopStackAtom]].valence > (j = (int)cNeighNumb[i]) )*/ + /* replaced due to missing sequence point */ + if ( i=(int)nStackAtom[nTopStackAtom], j = (int)cNeighNumb[i], (int)at[i].valence > j ) + { + cNeighNumb[i] ++; + u = (int)at[i].neighbor[j]; + if ( !nDfsNumber[u] ) { + /* tree edge, 1st visit -- advance */ + nStackAtom[++nTopStackAtom] = (AT_NUMB)u; + nRingStack[++nTopRingStack] = (AT_NUMB)u; + nLowNumber[u] = nDfsNumber[u] = ++nDfs; + } else + if ( !nTopStackAtom || u != (int)nStackAtom[nTopStackAtom-1] ) { + /* back edge: u is not a predecessor of i */ + if ( nDfsNumber[u] < nDfsNumber[i] ) { + /* Back edge, 1st visit: u is ancestor of i. Compare */ + if ( nLowNumber[i] > nDfsNumber[u] ) { + nLowNumber[i] = nDfsNumber[u]; + } + } + } + goto advance_ring; + } else { + cNeighNumb[i] = 0; + } + + /* back up */ + if ( nDfsNumber[i] == nLowNumber[i] ) { + /* found a ring system */ + nNumRingSystems ++; + /* unwind nRingStack[] down to i */ +#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) + nNumConnect = 2; + /* data structure: for each ring system nRsConnect[] contains: + * 1) nNumConnect+1 = (number of already discovered neighboring "ring systems" + 1)+1 + * 2) nNumAtInRingSystem + * 3) (nNumConnect-1) numbers (IDs) of neighboring ring systems. + * BFS guarantees that each neighboring ring system is encountered only one time + * Number of all neighboring ring systems = (nNumConnect-1)+1 = nNumConnect + * (One additional ring system is where the BFS retracts from the vertex #i, + * except when i=DFS root node. In the latter case there is/are only (nNumConnect-1) + * neighboring ring system(s). + */ +#endif + /* count atoms in a ring system */ + for ( nNumAtInRingSystem = 0, j = nTopRingStack; 0 <= j; j -- ) { + nNumAtInRingSystem ++; + if ( i == (int)nRingStack[j] ) { + break; + } + } + while ( nTopRingStack >= 0 ) { + j = (int)nRingStack[nTopRingStack--]; + at[j].nRingSystem = (AT_NUMB)nNumRingSystems; /* ring system id */ + at[j].nNumAtInRingSystem = nNumAtInRingSystem; +#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) + for ( k = 0; k < at[j].valence; k ++ ) { + if ( (nRs = at[at[j].neighbor[k]].nRingSystem) && (int)nRs != nNumRingSystems ) { + nRsConnect[nLenConnect + (nNumConnect++)] = nRs; /* adjacent ring system id */ + } + } +#endif + if ( i == j ) { + /* reached atom on the top of nStackAtom[] stack */ + break; + } + } +#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) + nRsConnect[nLenConnect] = nNumConnect; + nRsConnect[nLenConnect+1] = nNumAtInRingSystem; + nLenConnect += nNumConnect; + if ( nMaxNumConnect < nNumConnect ) { + /* max number of neighboring ring systems */ + nMaxNumConnect = nNumConnect; + } +#endif + } else + if ( nTopStackAtom > 0 ) { + j = (int)nStackAtom[nTopStackAtom-1]; + /* inherit nLowNumber */ + if ( nLowNumber[j] > nLowNumber[i] ) { + nLowNumber[j] = nLowNumber[i]; + } + } + } while ( --nTopStackAtom >= 0 ); + +#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) /* normally disabled */ + nMaxNumConnect ++; + if ( nNumRingSystems > 1 ) { + int nCol = nMaxNumConnect+1; + int nNumInSyst= nMaxNumConnect; + int nMaxNeigh = nMaxNumConnect-1; +#define T(a,b) tree[(a)*nCol+b] + if ( tree = (AT_NUMB *)inchi_calloc( nCol * (nNumRingSystems+1), sizeof(tree[0])) ) { + int len, neigh; + /* reuse previous allocations */ + AT_NUMB *nNumVisitedNeighbors = nStackAtom; + AT_NUMB *nDistanceFromTerminal = nRingStack; + AT_NUMB *nCurrActiveRingSystem = nDfsNumber; + AT_NUMB *nNextActiveRingSystem = nLowNumber; + int nNumCurrActiveRingSystems, nNumNextActiveRingSystems, pass; + /* build a "condensation graph (actually, a tree)" in which + * each vertex corresponds to a ring system T(row, col) = T(ring syst, neighbors) + * Number of rows = column length = max. number of ring system neighbors + 2 + * Number of cols = row length = number of ring systems + 1 + * Neighboring ring systems are contiguously stored in a row + * T(i,0) = number of neighbors, 1 <= i <= nNumRingSystems; + * T(i,k) = number of a neighboring ring system, 1 <= k <= T(i,0) + * T(i,nCol-1) = number of atoms in the system #i + */ + for ( i = 1, j = 0; len=nRsConnect[j]; i ++ ) { + T(i, nNumInSyst) = nRsConnect[j+1]; + for ( k = 2; k < len; k ++ ) { + neigh = nRsConnect[j+k]; + if ( T(i,0) < nMaxNeigh && T(neigh,0) < nMaxNeigh ) { + T(i,0) ++; + T(neigh,0) ++; + T(i,T(i,0)) = neigh; + T(neigh,T(neigh,0)) = i; + } else { + nNumRingSystems = CT_OVERFLOW; /* program error */ /* */ + goto exit_function; + } + } + j += len; + } + /* clear memory */ + memset( nNumVisitedNeighbors, 0, nNumRingSystems*sizeof(nNumVisitedNeighbors[0]) ); + memset( nDistanceFromTerminal, 0, nNumRingSystems*sizeof(nDistanceFromTerminal[0]) ); + memset( nCurrActiveRingSystem, 0, nNumRingSystems*sizeof(nCurrActiveRingSystem[0]) ); + memset( nNextActiveRingSystem, 0, nNumRingSystems*sizeof(nNextActiveRingSystem[0]) ); + nNumNextActiveRingSystems = 0; + for ( i = 0; i < nNumRingSystems; i ++ ) { + if ( 1 == T(i+1,0) ) { + nNextActiveRingSystem[i] = 1; /* number of traversed neighbors + 1 */ + nDistanceFromTerminal[i] = 1; + nNumNextActiveRingSystems ++; + } else { + nNextActiveRingSystem[i] = 0; + nDistanceFromTerminal[i] = 0; + } + nNumVisitedNeighbors[i] = 0; + } + + /* nCurrActiveRingSystem[i] = a sum of: + * 1) +1 if it is or was active + * 2) +(number of neighbors from which it was reached) + * 3) +1 if it was left and not active anymore + */ + pass = 0; + do { + nNumCurrActiveRingSystems = nNumNextActiveRingSystems; + nNumNextActiveRingSystems = 0; + memcpy( nCurrActiveRingSystem, nNextActiveRingSystem, + nNumRingSystems*sizeof(nNextActiveRingSystem[0])); + for ( i = 0; i < nNumRingSystems; i ++ ) { + if ( T(i+1,0) == nCurrActiveRingSystem[i] ) { + /* on the previous pass currently active ring system i+1 bas been reached + * from all neighbors except one; + * the neighbors from which it was reached have + * T(neigh,0)+1 == nCurrActiveRingSystem[i] + * this ring system has not been left yet + */ + for ( k = 1, len=T(i+1,0); k <= len; k ++ ) { + neigh = (int)T(i+1,k); + if ( T(neigh,0) >= nCurrActiveRingSystem[neigh-1] ) { + if ( 0 == pass ) { + nDistanceFromTerminal[i] = 1; + } + break; + } + } + if ( k <= len ) { + /* neigh was not reached from at least 2 neighbors + * walk along -R- chain (T(neigh,0)==2) up to + * 1) a terminal system, not including it or + * 2) a branching point. + * + * pass = 0: started from terminal systems: + * reach the branching point. + * If chain system next to a terminal system has already been reached + * then walk along it according to Note below + * + * pass > 0: started from branching points + * 2a) If the branching point has not been reached from 2 or more neighbors, + * then include it + * 2b) If the branching point has not been reached from 1 neighbor only, + * then do not include it: it will be a starting point later + * Note: if a chain atom already has nDistanceFromTerminal[i] > 0, then + * the last atom should be the one such that + * its nDistanceFromTerminal[]+1>= nDistanceFromTerminal[] of the + * next in the chain + */ + int bOk = 0; + k = i+1; /* starting point */ + if ( 0 == pass && T(k,nNumInSyst) > 1 ) { + nNumNextActiveRingSystems ++; /* request next pass */ + continue; /* stop a the terminal ring system */ + } + while( 2 == T(neigh,0) ) { + /* walk along a chain */ + if ( !nNextActiveRingSystem[neigh-1] ) { + nNextActiveRingSystem[neigh-1] = 1; /* make neighbor active */ + } else + if ( nDistanceFromTerminal[k-1]+1 <= nDistanceFromTerminal[neigh-1] ) { + /* walking along the chain; already have had a walk */ + /* in the opposite direction at this pass */ + } else { + /* k is the last; neigh (it is a bridge -X-) has not been reached */ + bOk = 1; + break; + } + nNextActiveRingSystem[k-1] ++; /* leave system k */ + if ( nNextActiveRingSystem[neigh-1] < T(neigh,0) ) { + nNextActiveRingSystem[neigh-1] ++; /* add one connection to neigh */ + } + nDistanceFromTerminal[neigh-1] = nDistanceFromTerminal[k-1]+1; + j = (T(neigh,1)==k)? 2:1; + k = neigh; + neigh = T(k,j); /* next in the chain */ + nNumNextActiveRingSystems ++; + if ( T(k,nNumInSyst) > 1 ) { + bOk = 1; + break; /* stop on a ring system */ + } + } + /* neigh is a terminal or a bridge or a branching point */ + if ( 2 > T(neigh,0) ) { + /* neighbor is a terminal atom */ + if ( 1 < pass ) { + nNumRingSystems = CT_UNKNOWN_ERR; /* error (debug only) */ /* */ + goto exit_function; + } + continue; + } + if ( 2 == T(neigh,0) ) { + /* neighbor is a bridge */ + continue; + } + /* neighbor is a branching point */ + if ( T(neigh,0) > nCurrActiveRingSystem[neigh-1] ) { + /* move to the neigh (make neigh active): on previous pass it */ + /* has not been reached from 2 or more neighbors */ + if ( !nNextActiveRingSystem[neigh-1] ) { + nNextActiveRingSystem[neigh-1] = 1; + } + if ( nDistanceFromTerminal[neigh-1] < nDistanceFromTerminal[k-1]+1 ) { + nDistanceFromTerminal[neigh-1] = nDistanceFromTerminal[k-1]+1; + } + nNextActiveRingSystem[k-1] ++; /* leave system k */ + if ( nNextActiveRingSystem[neigh-1] < T(neigh,0) ) { + nNextActiveRingSystem[neigh-1] ++; /* add one connection to neigh */ + } + nNumNextActiveRingSystems ++; + } + } + } + } + pass ++; + } while ( nNumNextActiveRingSystems ); + + for ( i = 0; i < num_atoms; i ++ ) { + at[i].nDistanceFromTerminal = nDistanceFromTerminal[(int)at[i].nRingSystem-1]; + } + + inchi_free( tree ); + tree = NULL; +#undef T + } else { + nNumRingSystems = CT_OUT_OF_RAM; /* error */ /* */ + goto exit_function; + } + } +#endif + + +exit_function: + if ( nStackAtom ) + inchi_free( nStackAtom ); + if ( nRingStack ) + inchi_free( nRingStack ); + if ( nDfsNumber ) + inchi_free( nDfsNumber ); + if ( nLowNumber ) + inchi_free( nLowNumber ); + if ( cNeighNumb ) + inchi_free( cNeighNumb ); +#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) + if ( nRsConnect ) + inchi_free( nRsConnect ); + if ( tree ) + inchi_free( tree ); +#endif + return nNumRingSystems; +} + + +#endif /* } FIND_RING_SYSTEMS */ + + + +/******************************************************************** + + InChI post-version 1.01 features implementation + (v. 1.04 : underivatize is still experimental and for engineering mode) + + ********************************************************************/ + +#if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) + +static U_CHAR el_number_O; +static U_CHAR el_number_C; +static U_CHAR el_number_N; +static U_CHAR el_number_P; +static U_CHAR el_number_S; +static U_CHAR el_number_Si; +static U_CHAR el_number_F; +static U_CHAR el_number_Cl; +static U_CHAR el_number_Br; +static U_CHAR el_number_I; +static U_CHAR el_number_B; + +typedef struct tagAtPair { + AT_NUMB at[2]; /* at[0] < at[1] */ +} R2C_ATPAIR; + +int DisconnectInpAtBond( inp_ATOM *at, AT_NUMB *nOldCompNumber, int iat, int neigh_ord ); +int ExtractConnectedComponent( inp_ATOM *at, int num_at, int component_number, inp_ATOM *component_at ); +int mark_arom_bonds( inp_ATOM *at, int num_atoms ); + +void set_R2C_el_numbers( void ); +int UnMarkDisconnectedComponents( ORIG_ATOM_DATA *orig_inp_data ); +int UnMarkRingSystemsInp( inp_ATOM *at, int num_atoms ); +int UnMarkOtherIndicators( inp_ATOM *at, int num_atoms ); +int UnMarkOneComponent( inp_ATOM *at, int num_atoms ); +int subtract_DT_from_num_H( int num_atoms, inp_ATOM *at ); +int add_inp_ATOM( inp_ATOM *at, int len_at, int len_cur, inp_ATOM *add, int len_add ); +int cmp_r2c_atpair( const void *p1, const void *p2 ); +int has_atom_pair( R2C_ATPAIR *ap, int num_ap, AT_NUMB at1, AT_NUMB at2 ); +int mark_atoms_ap( inp_ATOM *at, AT_NUMB start, R2C_ATPAIR *ap, int num_ap, int num, AT_NUMB cFlags ); + + + +/********************************************************************/ +void set_R2C_el_numbers( void ) +{ + if ( !el_number_O ) { + el_number_O = (U_CHAR)get_periodic_table_number( "O" ); + el_number_C = (U_CHAR)get_periodic_table_number( "C" ); + el_number_N = (U_CHAR)get_periodic_table_number( "N" ); + el_number_P = (U_CHAR)get_periodic_table_number( "P" ); + el_number_S = (U_CHAR)get_periodic_table_number( "S" ); + el_number_Si = (U_CHAR)get_periodic_table_number( "Si" ); + el_number_F = (U_CHAR)get_periodic_table_number( "F" ); + el_number_Cl = (U_CHAR)get_periodic_table_number( "Cl" ); + el_number_Br = (U_CHAR)get_periodic_table_number( "Br" ); + el_number_I = (U_CHAR)get_periodic_table_number( "I" ); + el_number_B = (U_CHAR)get_periodic_table_number( "B" ); + } +} + + +/***************************************************************/ +int UnMarkDisconnectedComponents( ORIG_ATOM_DATA *orig_inp_data ) +{ + int i; + for ( i = 0; i < orig_inp_data->num_inp_atoms; i ++ ) { + orig_inp_data->at[i].orig_compt_at_numb = 0; + orig_inp_data->at[i].component = 0; + } + if ( orig_inp_data->nCurAtLen ) { + inchi_free( orig_inp_data->nCurAtLen ); + orig_inp_data->nCurAtLen = NULL; + } + if ( orig_inp_data->nOldCompNumber ) { + inchi_free( orig_inp_data->nOldCompNumber ); + orig_inp_data->nOldCompNumber = NULL; + } + orig_inp_data->num_components = 0; + return 0; +} + + +/***************************************************************/ +int UnMarkRingSystemsInp( inp_ATOM *at, int num_atoms ) +{ + int i; + for ( i = 0; i < num_atoms; i ++ ) { + at[i].bCutVertex = 0; + at[i].nRingSystem = 0; + at[i].nNumAtInRingSystem = 0; + at[i].nBlockSystem = 0; + } + return 0; +} + + +/***************************************************************/ +int UnMarkOtherIndicators( inp_ATOM *at, int num_atoms ) +{ + int i; + for ( i = 0; i < num_atoms; i ++ ) { + at[i].at_type = 0; + at[i].cFlags = 0; + } + return 0; +} + + +/***************************************************************/ +int UnMarkOneComponent( inp_ATOM *at, int num_atoms ) +{ + int i; + for ( i = 0; i < num_atoms; i ++ ) { + at[i].orig_compt_at_numb = 0; + at[i].component = 0; + } + return 0; +} + + +/***************************************************************/ +int subtract_DT_from_num_H( int num_atoms, inp_ATOM *at ) +/* assume num_1H, num_D and num_T are included in num_H */ +{ + int i, j; + for ( i = 0; i < num_atoms; i ++ ) { + for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) + at[i].num_H -= at[i].num_iso_H[j]; + } + return 0; +} + + + +/***************************************************************/ +int add_inp_ATOM( inp_ATOM *at, int len_at, int len_cur, inp_ATOM *add, int len_add ) +{ + int i, j; + inp_ATOM *a; + /* chack correctness */ + if ( len_cur < 0 ) + return len_cur; + if ( len_add < 0 ) + return len_add; + if ( len_cur + len_add > len_at ) + return -1; + /* copy */ + memcpy( at+len_cur, add, len_add*sizeof(at[0]) ); + /* modify */ + if ( len_cur ) { + a = at + len_cur; + for ( i = 0; i < len_add; i ++ ) { + for ( j = 0; j < a[i].valence; j ++ ) { + a[i].neighbor[j] += len_cur; + } + } + } + return len_cur + len_add; +} +/****************************************************************************/ +int mark_arom_bonds( ic, inp_ATOM *at, int num_atoms ) +{ + INCHI_MODE bTautFlags=0, bTautFlagsDone = 0; + inp_ATOM *at_fixed_bonds_out = NULL; + T_GROUP_INFO *t_group_info = NULL; + int ret; + ret = mark_alt_bonds_and_taut_groups ( ic, pCG, at, at_fixed_bonds_out, num_atoms, + t_group_info, &bTautFlags, &bTautFlagsDone ); + return ret; +} + + +/********************************************************************/ +int cmp_r2c_atpair( const void *p1, const void *p2 ) +{ + const R2C_ATPAIR *ap1 = (const R2C_ATPAIR *)p1; + const R2C_ATPAIR *ap2 = (const R2C_ATPAIR *)p2; + int diff = (int)ap1->at[0] - (int)ap2->at[0]; + if ( !diff ) { + diff = (int)ap1->at[1] - (int)ap2->at[1]; + } + return diff; +} +/***************************************************************/ +int has_atom_pair( R2C_ATPAIR *ap, int num_ap, AT_NUMB at1, AT_NUMB at2 ) +{ + R2C_ATPAIR ap1; + int i1, i2, i3, diff; + int n = at1 > at2; + + ap1.at[n] = at1; + ap1.at[1-n] = at2; + i1 = 0; + i2 = num_ap-1; + /* search for ap1 by simple bisections */ + do { + i3 = (i1 + i2)/2; + if ( !(diff = cmp_r2c_atpair(&ap1, ap+i3) ) ) { + return i3+1; /* found => positive number */ + } else + if ( diff > 0 ) { + i1 = i3 + 1; + } else { + i2 = i3 - 1; + } + }while ( i2 >= i1 ); + return 0; /* not found */ +} + + +/***************************************************************/ +/* DFS search for atoms that do not have a flag */ +int mark_atoms_ap( inp_ATOM *at, AT_NUMB start, R2C_ATPAIR *ap, int num_ap, int num, AT_NUMB cFlags ) +{ + if ( !at[start].at_type ) { + int i; + AT_NUMB neigh; + at[start].at_type = cFlags; + num ++; + for ( i = 0; i < at[start].valence; i ++ ) { + neigh = at[start].neighbor[i]; + if ( has_atom_pair( ap, num_ap, start, neigh ) ) + continue; + num = mark_atoms_ap( at, neigh, ap, num_ap, num, cFlags ); + } + } + return num; /* number of atoms traversed forward from at[start] */ +} + +#endif /* RING2CHAIN || UNDERIVATIZE */ + + + +#if ( UNDERIVATIZE == 1 ) + +/***************************************************************/ + +#ifdef NEVER +typedef struct tagAtTypeBitmap { + AT_NUMB ord1 : 5; /* up to 2^5-1 = 31 = 0x0037 */ + AT_NUMB ord2 : 5; + AT_NUMB type : 6; /* up to 2^6-1 = 63 = 0x0077 */ +} AtTypeBitmap; +typedef union tagAtTypeUnion { + AT_NUMB num; + AtTypeBitmap bits; +} AtTypeUnion; +#endif + +#define DERIV_AT_LEN 4 +typedef struct tagDerivAttachment { + char typ[DERIV_AT_LEN]; + char ord[DERIV_AT_LEN]; + char num[DERIV_AT_LEN]; +} DERIV_AT; + + +#define DERIV_BRIDGE_O 0x0001 /* R1-O-R2 => R1-OH + HO-R2 */ +#define DERIV_BRIDGE_NH 0x0002 /* R1-NH-R2 amine */ +#define DERIV_AMINE_tN 0x0004 /* R1-N(-R2)-R3 tertiary amine */ +#define DERIV_RING_O 0x0008 /* -O- in a ring */ +#define DERIV_RING_NH 0x0010 /* -NH- in a ring */ +#define DERIV_UNMARK 0x0040 /* unmark the cut */ +#define DERIV_NOT 0x1000 /* cannot be a derivatization agent atom */ + +#define DERIV_DUPLIC 0x0080 /* duplicated disconnection */ + +#define DERIV_RING (DERIV_RING_O | DERIV_RING_NH) + +#define MAX_AT_DERIV 12 +#define NOT_AT_DERIV 99 +#define MIN_AT_LEFT_DERIV 3 +#define NO_ORD_VAL 0x0037 + +#define CFLAG_MARK_BRANCH 1 /* for main derivative traversal */ +#define CFLAG_MARK_BLOCK 2 /* for block detection */ +#define CFLAG_MARK_BLOCK_INV ((char)~(CFLAG_MARK_BLOCK)) /* for block detection */ +#define COUNT_ALL_NOT_DERIV 1 /* 1=> count ALL atoms that are not in deriv. agents */ + /* 0=> only atoms that are not in DERIV_RING */ + +int mark_atoms_cFlags( inp_ATOM *at, int start, int num, char cFlags ); +int un_mark_atoms_cFlags( inp_ATOM *at, int start, int num, char cFlags, char cInvFlags ); +int is_C_or_S_DB_O( inp_ATOM *at, int i ); +int is_C_unsat_not_arom( inp_ATOM *at, int i ); +int is_C_Alk( inp_ATOM *at, int i, char cFlags ); +int is_Si_IV( inp_ATOM *at, int i ); +int is_P_TB_N( inp_ATOM *at, int i ); +int is_possibly_deriv_neigh( inp_ATOM *at, int iat, int iord, int type, char cFlags ); +int get_traversed_deriv_type( inp_ATOM *at, DERIV_AT *da, int k, DERIV_AT *da1, char cFlags ); +int add_to_da( DERIV_AT *da, DERIV_AT *add ); +int mark_atoms_deriv( inp_ATOM *at, DERIV_AT *da, int start, int num, char cFlags, int *pbFound ); +int count_one_bond_atoms( inp_ATOM *at, DERIV_AT *da, int start, int ord, char cFlags, int *bFound ); +int is_silyl( inp_ATOM *at, int start, int ord_prev ); +int is_Me_or_Et( inp_ATOM *at, int start, int ord_prev ); +int is_CF3_or_linC3F7( inp_ATOM *at, int start, int ord_prev ); +int is_phenyl( inp_ATOM *at, int start, int ord_prev ); +int is_deriv_ring( inp_ATOM *at, int start, int num_atoms, DERIV_AT *da1, int idrv ); +int is_deriv_chain( inp_ATOM *at, int start, int num_atoms, DERIV_AT *da1, int idrv ); +int is_deriv_chain_or_ring( inp_ATOM *at, int start, int num_atoms, DERIV_AT *da1, int *idrv ); +int remove_deriv( DERIV_AT *da1, int idrv ); +int remove_deriv_mark( DERIV_AT *da1, int idrv ); +int EliminateDerivNotInList( inp_ATOM *at, DERIV_AT *da, int num_atoms ); +int make_single_cut( inp_ATOM *at, DERIV_AT *da, int iat, int icut ); + +/***************************************************************/ +/* DFS search for atoms that do not have a flag */ +int mark_atoms_cFlags( inp_ATOM *at, int start, int num, char cFlags ) +{ + if ( !(at[start].cFlags & cFlags) ) { + int i; + at[start].cFlags |= cFlags; + num ++; + for ( i = 0; i < at[start].valence; i ++ ) { + num = mark_atoms_cFlags( at, at[start].neighbor[i], num, cFlags ); + } + } + return num; /* number of atoms traversed forward from at[start] */ +} +/***************************************************************/ +/* DFS search for atoms that do have a flag */ +int un_mark_atoms_cFlags( inp_ATOM *at, int start, int num, char cFlags, char cInvFlags ) +{ + if ( at[start].cFlags & cFlags ) { + int i; + at[start].cFlags &= cInvFlags; + num ++; + for ( i = 0; i < at[start].valence; i ++ ) { + num = un_mark_atoms_cFlags( at, at[start].neighbor[i], num, cFlags, cInvFlags ); + } + } + return num; /* number of atoms traversed forward from at[start] */ +} +/***************************************************************/ +int is_C_or_S_DB_O( inp_ATOM *at, int i ) +{ + int j, neigh; + if ( at[i].el_number != el_number_C && + at[i].el_number != el_number_S || + at[i].charge || at[i].radical ) + return 0; + for ( j = 0; j < at[i].valence; j ++ ) { + neigh = at[i].neighbor[j]; + if ( (at[neigh].el_number == el_number_O || + at[neigh].el_number == el_number_S ) && + !at[neigh].num_H && 1 == at[neigh].valence && + 2 == at[neigh].chem_bonds_valence ) { + return 1; + } + } + return 0; +} +/***************************************************************/ +int is_C_unsat_not_arom( inp_ATOM *at, int i ) +{ + int j, neigh, num_arom, num_DB; + if ( at[i].el_number != el_number_C || + at[i].valence == at[i].chem_bonds_valence || + at[i].valence+1 < at[i].chem_bonds_valence || + at[i].chem_bonds_valence + at[i].num_H != 4 || + at[i].charge || at[i].radical ) + return 0; + num_arom = num_DB = 0; + for ( j = 0; j < at[i].valence; j ++ ) { + neigh = at[i].neighbor[j]; + num_arom += at[i].bond_type[j] == BOND_TYPE_ALTERN; + if ( (at[neigh].el_number == el_number_O || + at[neigh].el_number == el_number_S ) && + !at[neigh].num_H && 1 == at[neigh].valence && + 2 == at[neigh].chem_bonds_valence ) { + continue; + } + num_DB += at[i].bond_type[j] == BOND_TYPE_DOUBLE; + } + return num_DB && !num_arom; +} +/***************************************************************/ +int is_C_Alk( inp_ATOM *at, int i, char cFlags ) +{ + if ( at[i].el_number == el_number_C && + at[i].valence == at[i].chem_bonds_valence ) { + int j, k; + U_CHAR el; + for ( j = 0; j < at[i].valence; j ++ ) { + k = at[i].neighbor[j]; + if ( at[k].cFlags & cFlags ) + continue; + el = at[k].el_number; + if ( el != el_number_C && + el != el_number_F && + el != el_number_Cl && + el != el_number_Br && + el != el_number_I ) { + return 0; + } + } + return 1; + } + return 0; +} +/***************************************************************/ +int is_Si_IV( inp_ATOM *at, int i ) +{ + if ( at[i].el_number != el_number_Si || + at[i].charge || at[i].radical || at[i].valence != 4 || at[i].chem_bonds_valence != 4 ) + return 0; + return 1; +} +/***************************************************************/ +int is_P_TB_N( inp_ATOM *at, int i ) +{ + int j, k; + if ( at[i].el_number != el_number_P || at[i].chem_bonds_valence - at[i].valence != 2 ) + return 0; + for ( j = 0; j < at[i].valence; j ++ ) { + k = at[i].neighbor[j]; + if ( at[k].el_number == el_number_N && at[k].valence == 1 && at[k].chem_bonds_valence == 3 ) + return 1; + } + return 0; +} +/***************************************************************/ +int is_possibly_deriv_neigh( inp_ATOM *at, int iat, int iord, int type, char cFlags ) +{ + int neigh = at[iat].neighbor[iord]; + int neigh_from = -1; + U_CHAR el = at[neigh].el_number; + int bOk = 0; + switch ( type ) { + case DERIV_BRIDGE_O: + neigh_from = at[iat].neighbor[!iord]; + /* -> A--O--B -> traversing from A(neigh_from) to B(neigh); may we cut O--B bond? */ + /* do not cut bond "---" in A=Si(IV), B(=O), B=C: Si(IV)-O---B(=O) */ + if ( !(is_C_or_S_DB_O( at, neigh ) && is_Si_IV( at, neigh_from )) && + !is_C_unsat_not_arom( at, neigh ) ) { + bOk = ( el == el_number_C || + el == el_number_Si || + el == el_number_S || + el == el_number_P ); + } + break; + case DERIV_BRIDGE_NH: + /* -> A--NH--B -> traversing from A(neigh_from) to B(neigh); may we cut NH--B bond? */ + bOk = ( is_C_or_S_DB_O( at, neigh ) || + is_C_Alk( at, neigh, cFlags ) || + is_Si_IV( at, neigh ) || + is_P_TB_N( at, neigh ) ) && !(is_C_unsat_not_arom( at, neigh )); + break; + case DERIV_AMINE_tN: + bOk = ( is_C_or_S_DB_O( at, neigh ) || + is_C_Alk( at, neigh, cFlags ) || + is_Si_IV( at, neigh ) || + is_P_TB_N( at, neigh ) ) && !(is_C_unsat_not_arom( at, neigh )); + break; + } + return bOk; +} +/***************************************************************/ +/* determines derivative type on the forward step of the DFS */ +/***************************************************************/ +int get_traversed_deriv_type( inp_ATOM *at, DERIV_AT *da, int k, DERIV_AT *da1, char cFlags ) +{ + int i, j, m, nBlockSystemFrom, nOrdBack1, nOrdBack2, nOrdBack3, nBackType1, nBackType2; + memset( da1, 0, sizeof(*da1) ); + if ( at[k].cFlags & cFlags ) { + return 0; + } + for ( m = 0; m < at[k].valence && !(at[(int)at[k].neighbor[m]].cFlags & cFlags); m ++ ) + ; + if ( m == at[k].valence ) + return -1; /* error: at least one neighbor must have cFlags */ + if ( at[k].valence == 1 && at[k].num_H && ( + at[k].el_number == el_number_O || + at[k].el_number == el_number_N || + at[k].el_number == el_number_S || + at[k].el_number == el_number_P ) ) { + return DERIV_NOT; + } + if ( is_el_a_metal( at[k].el_number ) ) { + return DERIV_NOT; + } +#ifdef NEVER + if ( at[k].el_number == el_number_N && at[k].valence >= 2 && at[k].chem_bonds_valence <= 3 ) { + return DERIV_NOT; /* prohibit -N-, -N=, allow -N# as in isocyano -N#C or NO2 */ + } +#endif + /* m is the ord of the bond from which at[k] was reached first time */ + if ( at[k].nNumAtInRingSystem == 1 && (at[k].el_number == el_number_O || at[k].el_number == el_number_S) && + at[k].valence == 2 && at[k].chem_bonds_valence == 2 && + !at[k].num_H && !at[k].charge && !at[k].radical) { + /* -> A--O--B -> traversing from A to B; cut O--B */ + /* check for carboxy A(=O)-O-B and A--O--B(=O) */ + /* int has_A_CO = is_C_or_S_DB_O( at, at[k].neighbor[m] ); */ + int has_B_CO = is_C_or_S_DB_O( at, at[k].neighbor[!m] ); + int is_A_Si_IV = is_Si_IV( at, at[k].neighbor[m] ); + /* int is_B_Si_IV = is_Si_IV( at, at[k].neighbor[!m] );*/ + if ( is_A_Si_IV && has_B_CO ) { + ; /* do not cut bond --- in A=Si(IV), B(=O), B=C: Si(IV)-O---B(=O) */ + } else + if ( is_possibly_deriv_neigh( at, k, !m, DERIV_BRIDGE_O, cFlags ) ) { + da1->ord[0] = !m; /* ord of neighbor B, not traversed yet */ + da1->typ[0] = DERIV_BRIDGE_O; /* type */ + return DERIV_BRIDGE_O; /* R-O-R */ + } + } + if ( at[k].bCutVertex && at[k].el_number == el_number_N && + at[k].valence == 2 && at[k].chem_bonds_valence == at[k].valence && + at[k].valence+at[k].num_H == 3 && !at[k].charge && !at[k].radical) { + da1->ord[0] = !m; /* ord of neighbor B, not traversed yet */ + da1->typ[0] = DERIV_BRIDGE_NH; /* type */ + return DERIV_BRIDGE_NH; /* R1-N(-R2)-R3 or R1-NH-R2 amine */ + } + if ( at[k].bCutVertex && at[k].el_number == el_number_N && + at[k].valence == 3 && at[k].chem_bonds_valence == at[k].valence && + at[k].valence+at[k].num_H == 3 && !at[k].charge && !at[k].radical) { + int rm1 = ( at[at[k].neighbor[m]].nRingSystem == at[at[k].neighbor[(m+1)%3]].nRingSystem ); + int rm2 = ( at[at[k].neighbor[m]].nRingSystem == at[at[k].neighbor[(m+2)%3]].nRingSystem ); + int r12 = ( at[at[k].neighbor[(m+1)%3]].nRingSystem == at[at[k].neighbor[(m+2)%3]].nRingSystem ); + int ord[2]= {-1, -1}; + i = 0; /* type: tertriary amine */ + switch( rm1 + rm2 + r12 ) { + case 0: + /* -N< has no ring bonds */ + if ( is_possibly_deriv_neigh( at, k, (m+1)%3, DERIV_AMINE_tN, cFlags ) ) { + ord[i ++] = (m+1)%3; /* ord of a non-ring neighbor, not traversed yet */ + } + if ( is_possibly_deriv_neigh( at, k, (m+2)%3, DERIV_AMINE_tN, cFlags ) ) { + ord[i ++] = (m+2)%3; /* ord of another non-ring neighbor, not traversed yet */ + } + if ( i == 2 && ord[0] > ord[1] ) { + int tmp = ord[0]; + ord[0] = ord[1]; + ord[1] = tmp; + } + break; + + case 1: + if ( rm1 && is_possibly_deriv_neigh( at, k, (m+2)%3, DERIV_AMINE_tN, cFlags ) ) { + ord[i++] = (m+2)%3; /* ord of a single non-ring neighbor, not traversed yet */ + } else + if ( rm2 && is_possibly_deriv_neigh( at, k, (m+1)%3, DERIV_AMINE_tN, cFlags ) ) { + ord[i++] = (m+1)%3; /* ord of a single non-ring neighbor, not traversed yet */ + } + } + for ( j = 0; j < i; j ++ ) { + da1->ord[j] = ord[j]; + da1->typ[j] = DERIV_AMINE_tN; + } + if ( i ) { + return DERIV_AMINE_tN; + } + return 0; /* all neighbors or two untraversed edges are in one ring system */ + } + if ( at[k].bCutVertex && /* DD */ + at[k].valence == at[k].chem_bonds_valence && + (!at[k].num_H || at[k].el_number == el_number_C && 1 == at[k].num_H) && + !at[k].charge && !at[k].radical && + (at[k].el_number == el_number_C && at[k].valence+at[k].num_H == 4 || + at[k].el_number == el_number_Si && at[k].valence == 4 || + at[k].el_number == el_number_B && at[k].valence == 3) ) { + + /* entering path: ->X--O--DD + --X--O + | \ / DD = C, Si, B + | DD + | / \ O = O, NH + --Y--O + X, Y -- ignored for now + */ + nBlockSystemFrom = 0; + nBackType1 = nBackType2 = 0; + nOrdBack1 = nOrdBack2 = nOrdBack3 = -1; + j=(int)at[k].neighbor[m]; + if ( (at[j].el_number == el_number_O || at[j].el_number == el_number_S) && at[j].valence == 2 && + at[j].chem_bonds_valence == at[j].valence && + at[j].nNumAtInRingSystem >= 5 && + !at[j].num_H && !at[j].charge && !at[j].radical ) { + nBackType1 = DERIV_RING_O; + nBlockSystemFrom = at[j].nBlockSystem; + nOrdBack1 = m; /* predecessor */ + } else + if ( at[j].el_number == el_number_N && at[j].valence == 2 && + at[j].chem_bonds_valence == at[j].valence && + at[j].nNumAtInRingSystem >= 5 && + 1==at[j].num_H && !at[j].charge && !at[j].radical ) { + nBackType1 = DERIV_RING_NH; + nBlockSystemFrom = at[j].nBlockSystem; + nOrdBack1 = m; /* predecessor */ + } + if ( nBlockSystemFrom ) { + int num1, num2, bFound; + at[k].cFlags |= CFLAG_MARK_BLOCK; + num1 = mark_atoms_cFlags( at, at[k].neighbor[nOrdBack1], 1, CFLAG_MARK_BLOCK ); + for ( i = 0; i < at[k].valence; i ++ ) { + if ( i == nOrdBack1 ) + continue; + j=(int)at[k].neighbor[i]; + bFound = 0; + if ( at[j].cFlags & CFLAG_MARK_BLOCK ) { + if ( (at[j].el_number == el_number_O || at[j].el_number == el_number_S) && at[j].valence == 2 && + at[j].chem_bonds_valence == at[j].valence && + at[j].nNumAtInRingSystem >= 5 && + !at[j].num_H && !at[j].charge && !at[j].radical ) { + bFound = 1; + if ( nOrdBack2 < 0 ) { + nOrdBack2 = i; /* predecessor #2 */ + nBackType2 = DERIV_RING_O; + } else { + nOrdBack3 = i; /* predecessor #3 -- should not happen */ + } + } + if ( at[j].el_number == el_number_N && at[j].valence == 2 && + at[j].chem_bonds_valence == at[j].valence && + at[j].nNumAtInRingSystem >= 5 && + 1==at[j].num_H && !at[j].charge && !at[j].radical ) { + bFound = 1; + if ( nOrdBack2 < 0 ) { + nOrdBack2 = i; /* predecessor #2 */ + nBackType2 = DERIV_RING_NH; + } else { + nOrdBack3 = i; /* predecessor #3 -- should not happen */ + } + } + if ( !bFound ) { + nOrdBack3 = 99; /* reject: wrong neighboring atom in the same block */ + break; + } + } + } + num2 = un_mark_atoms_cFlags( at, k, 0, CFLAG_MARK_BLOCK, CFLAG_MARK_BLOCK_INV ); + if ( num1 != num2 ) { + return -1; /* mark_atoms_cFlags() program error */ + } + if ( nOrdBack2 >= 0 && nOrdBack3 < 0 ) { + if ( nOrdBack1 < nOrdBack2 ) { + da1->ord[0] = nOrdBack1; /* ord of a ring neighbor, traversed */ + da1->typ[0] = nBackType1; + da1->ord[1] = nOrdBack2; /* ord of another ring neighbor, not traversed yet */ + da1->typ[1] = nBackType2; + } else { + da1->ord[0] = nOrdBack2; /* ord of a ring neighbor, traversed */ + da1->typ[0] = nBackType2; + da1->ord[1] = nOrdBack1; /* ord of another ring neighbor, not traversed yet */ + da1->typ[1] = nBackType1; + } + return nBackType1 | nBackType2; + } + } + } + return 0; +} +/***************************************************************/ +int add_to_da( DERIV_AT *da, DERIV_AT *add ) +{ + /* if add has more than 1 element the elements are in ascending add.ord[] order */ + int i, j, len; + for ( len = 0; len < DERIV_AT_LEN && da->typ[len]; len ++ ) + ; + for ( j = 0; j < DERIV_AT_LEN && add->typ[j]; j ++ ) { + for ( i = 0; i < len; i ++ ) { + if ( add->ord[j] == da->ord[i] ) { + if ( add->typ[j] != da->typ[i] ) { + return -1; /* error, should not happen */ + } + if ( add->num[j] != da->num[i] ) { + return -2; /* error, should not happen */ + } + break; + } + } + if ( i == len ) { + if ( len < DERIV_AT_LEN ) { + da->ord[i] = add->ord[j]; + da->typ[i] = add->typ[j]; + da->num[i] = add->num[j]; + len ++; + } else { + return -3; /* overflow, should not happen */ + } + } + } + return 0; +} +/***************************************************************/ +/* DFS search for atoms that do not have a flag */ +int mark_atoms_deriv( inp_ATOM *at, DERIV_AT *da, int start, int num, char cFlags, int *pbFound ) +{ + int i, nFound=0; + DERIV_AT da1; + if ( !(at[start].cFlags & cFlags) ) { + if ( DERIV_NOT == get_traversed_deriv_type( at, da, start, &da1, cFlags ) ) { + nFound ++; /* at[start] cannot belong to a derivatizing agent */ + } + num ++; + at[start].cFlags |= cFlags; + if ( da1.typ[0] ) { + /* possibly a derivatization agent attachment point. */ + /* check neighbors that have not been traversed yet */ + int n1=0, n2=0, i1=-1, i2=-1, nFound1=0, nFound2=0; + switch( da1.typ[0] ) { + case DERIV_BRIDGE_O: + case DERIV_BRIDGE_NH: + n1 = mark_atoms_deriv( at, da, at[start].neighbor[(int)da1.ord[0]], 0, cFlags, &nFound1 ); + if ( n1 > MAX_AT_DERIV || nFound1 ) { + da1.typ[0] = 0; + } else { + da1.num[0] = n1; + nFound ++; + } + break; + case DERIV_AMINE_tN: + n1 = mark_atoms_deriv( at, da, at[start].neighbor[i1=da1.ord[0]], 0, cFlags, &nFound1 ); + if ( da1.typ[1] ) { + n2 = mark_atoms_deriv( at, da, at[start].neighbor[i2=da1.ord[1]], 0, cFlags, &nFound2 ); + } + if ( 0 < n1 && n1 <= MAX_AT_DERIV && !nFound1 ) { + da1.num[0] = n1; + i = 1; + nFound ++; + } else { + da1.ord[0] = da1.ord[1]; + da1.num[0] = da1.num[1]; + da1.typ[0] = da1.typ[1]; + da1.typ[1] = 0; + i = 0; + } + if ( 0 < n2 && n2 <= MAX_AT_DERIV && !nFound2 ) { + da1.num[i] = n2; + nFound ++; + } else { + da1.typ[i] = 0; + } + break; + case DERIV_RING_O: + case DERIV_RING_NH: + for ( i = 0; i < at[start].valence; i ++ ) { + if ( i != da1.ord[0] && i != da1.ord[1] && !(at[at[start].neighbor[i]].cFlags & cFlags) ) { + if ( !n1 ) + n1 = mark_atoms_deriv( at, da, at[start].neighbor[i1=i], 0, cFlags, &nFound1 ); + else + n2 = mark_atoms_deriv( at, da, at[start].neighbor[i2=i], 0, cFlags, &nFound2 ); + } + } + if ( n1+n2+1 > MAX_AT_DERIV || nFound1 || nFound2 ) { + da1.typ[1] = da1.typ[0] = 0; + } else { + da1.num[0] = n1; + da1.num[1] = n2; + nFound ++; + } + break; + } + if ( n1 < 0 ) + return n1; + if ( n2 < 0 ) + return n2; /* errors */ + + if ( i = add_to_da( da+start, &da1 ) ) { + return i; /* error */ + } + + *pbFound += nFound1 + nFound2 + nFound; + num += n1 + n2; + } else { + *pbFound += nFound; + } + for ( i = 0; i < at[start].valence; i ++ ) { + num = mark_atoms_deriv( at, da, at[start].neighbor[i], num, cFlags, pbFound ); + if ( num < 0 ) + return num; + } + } + /* *pbFound = number of derivatizer attachment points traversed forward from at[start] */ + return num; /* number of atoms traversed forward from at[start] */ +} +/***************************************************************/ +int count_one_bond_atoms( inp_ATOM *at, DERIV_AT *da, int start, int ord, char cFlags, int *bFound ) +{ + int num = 0; + if ( !(at[at[start].neighbor[ord]].cFlags & cFlags) ) { + at[at[start].neighbor[ord]].cFlags |= cFlags; + num ++; + num = mark_atoms_deriv( at, da, start, num, cFlags, bFound ); + } + return num; +} +/*************************************************************** + List of allowed derivatives + + Legend: + + ->- marks the bond to be disconnexted: X->-Y => XD + TY + where TY is a derivatizing agent eventually to be discarded + + Allowed Derivative Types List + ============================= + + DERIV_BRIDGE_O, DERIV_BRIDGE_NH, DERIV_AMINE_tN + ----------------------------------------------- + CH3 CH3 CH3 CH3 CH3 + | | | | | + X->-Si--CH3 X->-Si---Si--CH3 X->-Si----C--CH3 X= O, NH, N + | | | | | + CH3 CH3 CH3 CH3 CH3 + + 4 atoms 7 atoms 7 atoms is_silyl() + + + + + O O O F O + || || || | || + R--C--O->-CH3 R--C--O->-CH2--CH3 R--C--O->-C--F R--C--O->-CF2-CF2-CF3 + | + F + + + + 1 atom 2 atoms 3 atoms 10 atoms + is_Me_or_Et() is_Me_or_Et() is_CF3_or_linC3F7() + + + A. DERIV_BRIDGE_NH, DERIV_AMINE_tN + ----------------------------------- + + + O O O F O + || || || | || + N->-C--CH3 N->-C--CH2--CH3 N->-C---C--F N->-C--CF2-CF2-CF3 + | + F + + + + 3 atoms 5 atoms 8 atoms 12 atoms + is_Me_or_Et() is_Me_or_Et() is_CF3_or_linC3F7() + + DERIV_RING_O (da contains >B- or >C< or >CH- atom) + ------------ + + C----O R----O R----O + | \ | \ CH3 | \ + | > | > / | > + | \ | \ / | \ + | B--CH3 | C | CH--Ph + | / | / \ | / + | > | > \ | > + | / | / CH3 | / + C----O R----O R----O + + 5-member 5 or 6-member 5 or 6-member + + + 2 atoms 3 atoms 7 atoms + + DERIV_RING_NH + ------------ + + None in the list + +***************************************************************/ +int is_silyl( inp_ATOM *at, int start, int ord_prev ) +{ + int i, neigh, num_Me=0, iC_IV=-1, iSi_IV=-1, i_C_or_Si_IV; + + if ( at[start].el_number != el_number_Si || at[start].valence != 4 || + at[start].valence != at[start].chem_bonds_valence || + at[start].charge || at[start].radical ) + return 0; + for ( i = 0; i < at[start].valence; i ++ ) { + if ( i == ord_prev ) + continue; + neigh = at[start].neighbor[i]; + if ( at[neigh].charge || at[neigh].radical || + at[neigh].valence != at[neigh].chem_bonds_valence) + return 0; + if ( at[neigh].valence == 4 ) { + if ( at[neigh].el_number == el_number_C && iC_IV < 0 && iSi_IV < 0 ) + iC_IV = neigh; + else + if ( at[neigh].el_number == el_number_Si && iC_IV < 0 && iSi_IV < 0 ) + iSi_IV = neigh; + else + return 0; + } else + if ( at[neigh].valence == 1 && + at[neigh].valence == at[neigh].chem_bonds_valence && + at[neigh].el_number == el_number_C && at[neigh].num_H == 3 ) { + num_Me ++; + } else { + return 0; + } + } + if ( num_Me == 3 && iC_IV < 0 && iSi_IV < 0 ) + return 1; /* Si(CH3)3 */ + + /* next C(IV) or Si(IV) */ + i_C_or_Si_IV = iC_IV >= 0? iC_IV : iSi_IV >= 0? iSi_IV : -1; + if ( num_Me != 2 || i_C_or_Si_IV < 0 ) + return 0; + + num_Me = 0; + for ( i = 0; i < at[i_C_or_Si_IV].valence; i ++ ) { + neigh = at[i_C_or_Si_IV].neighbor[i]; + if ( neigh == start ) + continue; + if ( at[neigh].charge || at[neigh].radical || + at[neigh].valence != at[neigh].chem_bonds_valence) + return 0; + if ( at[neigh].valence == 1 && + at[neigh].valence == at[neigh].chem_bonds_valence && + at[neigh].el_number == el_number_C && at[neigh].num_H == 3 ) { + num_Me ++; + } else { + return 0; + } + } + if ( num_Me == 3 ) + return 2; /* Si(CH3)2Si/C(CH3)3 */ + return 0; +} +/****************************************************************/ +int is_Me_or_Et( inp_ATOM *at, int start, int ord_prev ) +{ + int i, neigh = -1; + if ( at[start].el_number != el_number_C || at[start].valence > 2 || + at[start].valence != at[start].chem_bonds_valence || + at[start].chem_bonds_valence + at[start].num_H != 4 || + at[start].charge || at[start].radical ) + return 0; + for ( i = 0; i < at[start].valence; i ++ ) { + if ( i == ord_prev ) + continue; + if ( neigh >= 0 ) + return 0; + + neigh = at[start].neighbor[i]; + if ( at[neigh].el_number != el_number_C || at[neigh].valence > 1 || + at[neigh].valence != at[neigh].chem_bonds_valence || + at[neigh].chem_bonds_valence + at[neigh].num_H != 4 || + at[neigh].charge || at[neigh].radical ) + return 0; + } + return 1 + (neigh >= 0); +} +#ifdef NEVER +/**************************************************************** + CF3 + -CF3 or -CF< + CF3 +*****************************************************************/ +int is_CF3_or_isoC3F7( inp_ATOM *at, int start, int ord_prev ) +{ + int i, k, num_C_IV, iC_IV[2], neigh, num_F, iC; + if ( at[start].el_number != el_number_C || at[start].valence != 4 || + at[start].valence != at[start].chem_bonds_valence || + at[start].chem_bonds_valence + at[start].num_H != 4 || + at[start].charge || at[start].radical ) + return 0; + + iC_IV[0] = iC_IV[1] = num_F = 0; + + for ( i = num_C_IV = 0; i < at[start].valence; i ++ ) { + if ( i == ord_prev ) + continue; + + neigh = at[start].neighbor[i]; + if ( at[neigh].valence != at[neigh].chem_bonds_valence || + at[neigh].charge || at[neigh].radical ) + return 0; + if ( at[neigh].el_number == el_number_F ) { + if ( at[neigh].chem_bonds_valence + at[neigh].num_H != 1 ) + return 0; + num_F ++; + } else + if ( at[neigh].el_number == el_number_C && + at[neigh].valence == 4 && + !at[neigh].num_H && !at[neigh].charge && !at[neigh].radical && num_C_IV < 2 ) { + + if ( num_C_IV > 1 ) + return 0; + + iC_IV[num_C_IV++] = neigh; + } + } + if ( !num_C_IV && 3 == num_F ) + return 1; /* -CF3 */ + if ( 2 != num_C_IV || 1 != num_F ) + return 0; + + /* detect iso-C3F7 */ + for ( k = 0; k < num_C_IV; k ++ ) { + iC = iC_IV[k]; + num_F = 0; + for ( i = 0; i < at[iC].valence; i ++ ) { + neigh = at[iC].neighbor[i]; + if ( neigh == start ) + continue; + if ( at[neigh].valence != at[neigh].chem_bonds_valence || + at[neigh].charge || at[neigh].radical ) + return 0; + if ( at[neigh].el_number == el_number_F ) { + if ( at[neigh].chem_bonds_valence + at[neigh].num_H != 1 ) + return 0; + num_F ++; + } else { + return 0; + } + } + if ( num_F != 3 ) + return 0; + } + return 2; /* iso-C3F7 */ +} +#endif +/**************************************************************/ +int is_CF3_or_linC3F7( inp_ATOM *at, int start, int ord_prev ) +{ + int i, num_C_IV, iC_IV, neigh, num_F, num_C=0; + AT_NUMB *p; + + while( num_C < 4 ) { + + if ( at[start].el_number != el_number_C || at[start].valence != 4 || + at[start].valence != at[start].chem_bonds_valence || + at[start].chem_bonds_valence + at[start].num_H != 4 || + at[start].charge || at[start].radical ) + return 0; + + iC_IV = num_F = 0; + + for ( i = num_C_IV = 0; i < at[start].valence; i ++ ) { + if ( i == ord_prev ) + continue; + + neigh = at[start].neighbor[i]; + if ( at[neigh].valence != at[neigh].chem_bonds_valence || + at[neigh].charge || at[neigh].radical ) + return 0; + if ( at[neigh].el_number == el_number_F ) { + if ( at[neigh].chem_bonds_valence + at[neigh].num_H != 1 ) + return 0; + num_F ++; + } else + if ( at[neigh].el_number == el_number_C && + at[neigh].valence == 4 && + !at[neigh].num_H && !at[neigh].charge && !at[neigh].radical && num_C_IV < 2 ) { + + if ( num_C_IV ) + return 0; + + iC_IV = neigh; + num_C_IV++; + } + } + if ( num_C_IV + num_F != 3 ) + return 0; + + num_C ++; /* found -CF2-C or -CF3 */ + if ( !num_C_IV ) + break; /* -CF3 */ + + /* treat next C(IV) as a new start atom */ + if ( p = is_in_the_list( at[iC_IV].neighbor, (AT_NUMB) start, at[iC_IV].valence ) ) { + start = iC_IV; + ord_prev = p - at[iC_IV].neighbor; + } else { + return -1; /* program error */ + } + } + return num_C == 1? 1 : num_C == 3? 2 : 0; +} +/****************************************************************/ +int is_phenyl( inp_ATOM *at, int start, int ord_prev ) +{ + int k, neigh, cur_at, ord; + if ( at[start].el_number != el_number_C || at[start].valence != 3 || + at[start].valence+1 != at[start].chem_bonds_valence || + at[start].chem_bonds_valence + at[start].num_H != 4 || + at[start].charge || at[start].radical ) + return 0; + + ord = (ord_prev + 1) % 3; + cur_at = start; + + for ( k = 0; k < 5; k ++ ) { + neigh = at[cur_at].neighbor[ord]; + if ( at[neigh].el_number != el_number_C || at[neigh].valence != 2 || + at[neigh].valence+1 != at[neigh].chem_bonds_valence || + at[neigh].chem_bonds_valence + at[neigh].num_H != 4 || + at[neigh].charge || at[neigh].radical ) + return 0; + ord = (at[neigh].neighbor[0] == cur_at); + cur_at = neigh; + } + return (at[cur_at].neighbor[ord] == start); +} +/****************************************************************/ +int is_deriv_ring( inp_ATOM *at, int start, int num_atoms, DERIV_AT *da1, int idrv ) +{ + int i, k, neigh_at[2], prev_ord[2], neigh, is_B=0, is_C=0; + AT_NUMB *p; + if ( da1->typ[idrv] != DERIV_RING_O || da1->typ[idrv+1] != DERIV_RING_O ) + return 0; + if ( at[start].charge || at[start].radical || at[start].valence != at[start].chem_bonds_valence ) + return 0; + if ( at[start].el_number == el_number_B && at[start].valence == 3 ) + is_B = 1; + else + if ( at[start].el_number == el_number_C && (at[start].valence == 3 || at[start].valence == 4) && + at[start].chem_bonds_valence == at[start].valence && + at[start].num_H + at[start].chem_bonds_valence == 4 ) + is_C = 1; + else + return 0; + /* locate bonds connecting >B- or >C< or >C- to the rest of the derivative */ + for ( i = k = 0; i < at[start].valence; i ++ ) { + if ( i != da1->ord[idrv] && i != da1->ord[idrv+1] ) { + neigh = at[start].neighbor[i]; + if ( k < 2 && (p = is_in_the_list( at[neigh].neighbor, (AT_NUMB) start, at[neigh].valence)) ) { + neigh_at[k] = neigh; + prev_ord[k] = p - at[neigh].neighbor; + k ++; + } else { + return -1; /* program error */ + } + } + } + if ( is_B && k == 1 && is_Me_or_Et( at, neigh_at[0], prev_ord[0]) ) + return 1; + if ( is_C && k == 1 && at[start].num_H == 1 && is_phenyl( at, neigh_at[0], prev_ord[0]) ) + return 1; + if ( is_C && k == 2 && is_Me_or_Et( at, neigh_at[0], prev_ord[0]) && + is_Me_or_Et( at, neigh_at[1], prev_ord[1]) ) + return 1; + + return 0; +} +/****************************************************************/ +int is_deriv_chain( inp_ATOM *at, int start, int num_atoms, DERIV_AT *da1, int idrv ) +{ + int i, k, prev_ord, neigh, iC, iO, iNxt; + AT_NUMB *p; + if ( !da1->typ[idrv] || (da1->typ[idrv] & DERIV_RING) ) + return 0; + if ( at[start].charge || at[start].radical || at[start].valence != at[start].chem_bonds_valence ) + return 0; + + neigh = at[start].neighbor[(int)da1->ord[idrv]]; + p = is_in_the_list( at[neigh].neighbor, (AT_NUMB) start, at[neigh].valence); + if ( !p ) + return -1; /* program error */ + prev_ord = p - at[neigh].neighbor; + + /* eliminate silyl possibility */ + if ( is_silyl( at, neigh, prev_ord ) ) + return 1; + + if ( da1->typ[idrv] == DERIV_BRIDGE_O ) { + /* check acetyl */ + iC = at[start].neighbor[!da1->ord[idrv]]; + if ( at[iC].charge || at[iC].radical || at[iC].num_H || + at[iC].el_number != el_number_C || at[iC].valence != 3 || + at[iC].valence+1 != at[iC].chem_bonds_valence ) + return 0; + for ( i = k = 0; i < at[iC].valence; i ++ ) { + iO = at[iC].neighbor[i]; + if ( at[iO].charge || at[iO].radical || at[iO].num_H || + at[iO].el_number != el_number_O || at[iO].valence != 1 || + at[iO].valence+1 != at[iO].chem_bonds_valence ) + continue; + k ++; /* number of =O */ + } + if ( k != 1 ) + return 0; + /* check derivative */ + return ( is_Me_or_Et( at, neigh, prev_ord ) || + is_CF3_or_linC3F7( at, neigh, prev_ord ) ); + } + + if ( da1->typ[idrv] == DERIV_BRIDGE_NH || da1->typ[idrv] == DERIV_AMINE_tN ) { + /* check acetyl */ + iNxt = -1; + iC = at[start].neighbor[(int)da1->ord[idrv]]; + if ( at[iC].charge || at[iC].radical || at[iC].num_H || + at[iC].el_number != el_number_C || at[iC].valence != 3 || + at[iC].valence+1 != at[iC].chem_bonds_valence ) + return 0; + for ( i = k = 0; i < at[iC].valence; i ++ ) { + iO = at[iC].neighbor[i]; + if ( at[iO].charge || at[iO].radical || at[iO].num_H || + at[iO].el_number != el_number_O || at[iO].valence != 1 || + at[iO].valence+1 != at[iO].chem_bonds_valence ) { + if ( iO != start ) { + if ( iNxt < 0 ) + iNxt = iO; + else + return 0; + } + continue; + } + k ++; /* number of =O */ + } + if ( k != 1 || iNxt < 0 ) + return 0; + /* find bond from iNxt to iC */ + p = is_in_the_list( at[iNxt].neighbor, (AT_NUMB) iC, at[iNxt].valence); + if ( !p ) + return -1; /* program error */ + prev_ord = p - at[iNxt].neighbor; + /* check derivative */ + return ( is_Me_or_Et( at, iNxt, prev_ord ) || + is_CF3_or_linC3F7( at, iNxt, prev_ord ) ); + } + return 0; +} +/****************************************************************/ +int is_deriv_chain_or_ring( inp_ATOM *at, int start, int num_atoms, DERIV_AT *da1, int *idrv ) +{ + int i, ret = -1; + if ( da1->typ[*idrv] & DERIV_RING ) { + /* find the first ord of this derivative; ord of ring derivatives are in pairs */ + int j = -1; + for ( i = 0; i < DERIV_AT_LEN && da1->typ[i]; i ++ ) { + if ( da1->typ[i] & DERIV_RING ) { + if ( i == *idrv || i+1 == *idrv ) { + *idrv = j = i; + break; + } + i ++; /* bypass the second bond to the same derivatization agent */ + } + } + /* check consistency */ + if ( j == -1 || j+1 >= DERIV_AT_LEN || + !(da1->typ[j] & DERIV_RING) || !(da1->typ[j+1] & DERIV_RING) ) { + ret = -1; /* program error */ + } else { + ret = is_deriv_ring( at, start, num_atoms, da1, j ); + } + } else + if ( da1->typ[*idrv] ) { + ret = is_deriv_ring( at, start, num_atoms, da1, *idrv ); + } + return ret; +} +/******************************************************/ +int remove_deriv( DERIV_AT *da1, int idrv ) +{ + int i, j, ret = -1; + if ( da1->typ[idrv] & DERIV_RING ) { + /* find the first ord of this derivative; ord of ring derivatives are in pairs */ + j = -1; + for ( i = 0; i < DERIV_AT_LEN && da1->typ[i]; i ++ ) { + if ( da1->typ[i] & DERIV_RING ) { + if ( i == idrv || i+1 == idrv ) { + j = i; + break; + } + i ++; /* bypass the second bond to the same derivatization agent */ + } + } + /* delete if data are consistent */ + if ( j >= 0 && j+1 < DERIV_AT_LEN && (da1->typ[j] & DERIV_RING) && (da1->typ[j+1] & DERIV_RING) ) { + for ( ; j < DERIV_AT_LEN-2 && da1->typ[j+2]; j ++ ) { + da1->typ[j] = da1->typ[j+2]; + da1->num[j] = da1->num[j+2]; + da1->ord[j] = da1->ord[j+2]; + } + for ( ; j < DERIV_AT_LEN; j ++ ) { + da1->typ[j] = 0; + da1->num[j] = 0; + da1->ord[j] = 0; + } + ret = 0; + } + } else { + j = idrv; + + for ( ; j < DERIV_AT_LEN-1 && da1->typ[j+1]; j ++ ) { + da1->typ[j] = da1->typ[j+1]; + da1->num[j] = da1->num[j+1]; + da1->ord[j] = da1->ord[j+1]; + } + for ( ; j < DERIV_AT_LEN; j ++ ) { + da1->typ[j] = 0; + da1->num[j] = 0; + da1->ord[j] = 0; + } + ret = 0; + } + return ret; +} +/******************************************************/ +int remove_deriv_mark( DERIV_AT *da1, int idrv ) +{ + int i, j, ret = -1; + if ( da1->typ[idrv] & DERIV_RING ) { + /* find the first ord of this derivative; ord of ring derivatives are in pairs */ + j = -1; + for ( i = 0; i < DERIV_AT_LEN && da1->typ[i]; i ++ ) { + if ( da1->typ[i] & DERIV_RING ) { + if ( i == idrv || i+1 == idrv ) { + j = i; + break; + } + i ++; /* bypass the second bond to the same derivatization agent */ + } + } + /* delete if data are consistent */ + if ( j >= 0 && j+1 < DERIV_AT_LEN && (da1->typ[j] & DERIV_RING) && (da1->typ[j+1] & DERIV_RING) ) { + da1->typ[j] |= DERIV_DUPLIC; + da1->typ[j+1] |= DERIV_DUPLIC; + ret = 0; + } + } else { + j = idrv; + da1->typ[j] |= DERIV_DUPLIC; + ret = 0; + } + return ret; +} +/****************************************************************/ +int EliminateDerivNotInList( inp_ATOM *at, DERIV_AT *da, int num_atoms ) +{ + int i, j, num_da, num_cuts=0, ret=0; + for ( i = 0; i < num_atoms; i ++ ) { + if ( !da[i].typ[0] ) + continue; + /* count deriative attachments */ + for ( num_da = 0; num_da < DERIV_AT_LEN && da[i].typ[num_da]; num_da ++ ) + ; + if ( num_da > 2 ) + return -1; /* should not happen */ + if ( num_da == 2 && da[i].typ[0] != da[i].typ[1] ) { + da[i].typ[0] = da[i].typ[1] = 0; /* do not allow */ + continue; + } + if ( da[i].typ[0] & DERIV_RING ) { + ret = 0; + if ( num_da == 2 && 1 + da[i].num[0] + da[i].num[1] <= MAX_AT_DERIV && + 0 < (ret=is_deriv_ring( at, i, num_atoms, da+i, 0 ) ) ) { + num_cuts += 2; + } else + if ( ret < 0 ) { + return ret; + } else { + da[i].typ[0] = da[i].typ[1] = 0; /* not a derivative */ + } + } else { + ret = 0; + if ( da[i].num[0] <= MAX_AT_DERIV && 0 < (ret = is_deriv_chain( at, i, num_atoms, da+i, 0 )) ) { + num_cuts ++; + j = 1; + } else + if ( ret < 0 ) { + return ret; + } else { + da[i].ord[0] = da[i].ord[1]; + da[i].num[0] = da[i].num[1]; + da[i].typ[0] = da[i].typ[1]; + da[i].typ[1] = 0; + j = 0; + } + if ( da[i].typ[j] && da[i].num[j] <= MAX_AT_DERIV && + 0 < (ret = is_deriv_chain( at, i, num_atoms, da+i, j )) ) { + num_cuts ++; + } else + if ( ret < 0 ) { + return ret; + } else { + da[i].typ[j] = 0; + } + } + } + return num_cuts; +} +/***************************************************************/ +int make_single_cut( inp_ATOM *at, DERIV_AT *da, int iat, int icut ) +{ + int ret = -1; /* error flag */ + int iord = (int)da[iat].ord[icut]; /* ord of the bond in iat */ + if ( da[iat].typ[icut] & DERIV_DUPLIC ) { + return 0; + } else + if ( iord < 0 ) { + return -1; /* program error */ + } else { + /* find other da[] that affect at[iat] */ + int jat = at[iat].neighbor[iord]; /* opposite atom */ + AT_NUMB *p = is_in_the_list( at[jat].neighbor, (AT_NUMB) iat, at[jat].valence ); + int jord = p? (p - at[jat].neighbor) : -1; + int i, iD=1, iT=2; + if ( jord < 0 ) { + return -1; /* program error */ + } + ret = DisconnectInpAtBond( at, NULL, iat, iord ); + if ( ret == 1 ) { + if ( da[iat].typ[icut] & DERIV_RING ) { + /* at[jat] belongs to the main structure */ + at[jat].num_H ++; /* add D to the main structure */ + at[jat].num_iso_H[iD] ++; + at[iat].num_H ++; /* add T to the derivatizing fragment */ + at[iat].num_iso_H[iT] ++; + } else { + at[iat].num_H ++; /* add D to the main structure */ + at[iat].num_iso_H[iD] ++; + at[jat].num_H ++; /* add T to the derivatizing fragment */ + at[jat].num_iso_H[iT] ++; + } + /* adjust ord for other bonds */ + for ( i = 0; i < DERIV_AT_LEN && da[iat].typ[i]; i ++ ) { + if ( da[iat].ord[i] == iord ) { + da[iat].ord[i] = -(1+da[iat].ord[i]); /* mark the bond being disconnected */ + } else + if ( da[iat].ord[i] > iord ) { + da[iat].ord[i] --; + } + } + for ( i = 0; i < DERIV_AT_LEN && da[jat].typ[i]; i ++ ) { + if ( da[jat].ord[i] == jord ) { + /* opposite atom needs the same bond to be disconnected */ + if ( da[iat].num[icut] == da[jat].num[i] ) { + iD=2; /* mark both as fragments */ + } else + if ( da[iat].num[icut] > da[jat].num[i] ) { + iD = 2; /* greater as a main structure */ + iT = 1; /* mark smaller as a derivatizing fragment */ + } + da[jat].ord[i] = -(1+da[jat].ord[i]); + da[jat].typ[i] |= DERIV_DUPLIC; + } else + if ( da[jat].ord[i] > jord ) { + da[jat].ord[i] --; + } + } + } + } + return ret; +} +/***************************************************************/ +int underivatize( ORIG_ATOM_DATA *orig_inp_data ) +{ +#define REMOVE_CUT_DERIV 1 /* remove disconnected derivatizing agents */ + int ret = 0, i, j, k, m, n, num_atoms, num_components, i_component, nFound, num, cur_num_at, len; + int num_cuts, num_ring_cuts, num_cut_pieces, num_cuts_to_check; + inp_ATOM *at = orig_inp_data->at; + INP_ATOM_DATA *inp_cur_data = NULL; + DERIV_AT *da = NULL; + int nTotNumCuts = 0; + + set_R2C_el_numbers( ); + /* prepare */ + num_atoms = remove_terminal_HDT( orig_inp_data->num_inp_atoms, at, 1 ); + /*^^^^^ always accomodate accomodate FIX_TERM_H_CHRG_BUG - IPl, July 2008*/ + orig_inp_data->num_inp_atoms = num_atoms; + + /* initialize */ + UnMarkDisconnectedComponents( orig_inp_data ); + num_cuts = 0; + /* mark */ + num_components = MarkDisconnectedComponents( orig_inp_data, 0 ); + inp_cur_data = (INP_ATOM_DATA *)inchi_calloc( num_components, sizeof(inp_cur_data[0]) ); + for ( i_component = 0; i_component < num_components; i_component ++ ) { + CreateInpAtomData( inp_cur_data+i_component, orig_inp_data->nCurAtLen[i_component], 0 ); + inp_cur_data[i_component].num_at = ExtractConnectedComponent( orig_inp_data->at, orig_inp_data->num_inp_atoms, i_component+1, inp_cur_data[i_component].at ); + /* error processing */ + if ( inp_cur_data[i_component].num_at <= 0 || orig_inp_data->nCurAtLen[i_component] != inp_cur_data[i_component].num_at ) { + ret = -(i_component+1); /* severe error */ + goto exit_function; + } + /* initialize */ + num_atoms = inp_cur_data[i_component].num_at; + at = inp_cur_data[i_component].at; + add_DT_to_num_H( num_atoms, at ); + + UnMarkRingSystemsInp( at, num_atoms ); + UnMarkOtherIndicators( at, num_atoms ); + UnMarkOneComponent( at, num_atoms ); + MarkRingSystemsInp( at, num_atoms, 0 ); + ret = mark_arom_bonds( at, num_atoms ); + if ( ret < 0 ) { + goto exit_function; + } + ret = 0; + if ( da ) inchi_free( da ); + da = (DERIV_AT *)inchi_calloc( num_atoms, sizeof(da[0]) ); + + /* detect derivatives */ + nFound = 0; + for ( i = 0; i < num_atoms; i ++ ) { + if ( at[i].bCutVertex && !da[i].typ[0] ) { + for ( k = 0; k < at[i].valence; k ++ ) { + num = count_one_bond_atoms( at, da, i, k, CFLAG_MARK_BRANCH, &nFound ); + UnMarkOtherIndicators( at, num_atoms ); + if ( num < 0 ) { + ret = num; /* severe error */ + goto exit_function; + } + } + } + } + /* prepare cuts: remove cuts that are not to be done */ + /* in addition, count ring cuts DERIV_RING */ + num_ring_cuts = 0; + num_cuts = 0; + num_cut_pieces = 0; + for ( i = num = 0; i < num_atoms; i ++ ) { + for ( len = 0; len < MAX_AT_DERIV && da[i].typ[len]; len ++ ) + ; + switch( len ) { + case 0: + continue; + case 1: + /* single cut: unconditional */ + num_cuts += 1; + num_cut_pieces += 1; + continue; + case 2: + if ( (da[i].typ[0] & DERIV_RING) && (da[i].typ[1] & DERIV_RING) ) { + /* double cut, unconditional */ + num_ring_cuts += 2; + num_cuts += 2; + num_cut_pieces += 1; + continue; + } else + if ( da[i].typ[0] == DERIV_AMINE_tN && da[i].typ[1] == DERIV_AMINE_tN ) { + /* double cut, unconditional */ + num_cuts += 2; + num_cut_pieces += 2; + continue; + } + if ( da[i].typ[0] == da[i].typ[1] ) { + /* DERIV_BRIDGE_O or DERIV_BRIDGE_NH; cut off the smallest */ + if ( 0 == is_deriv_chain( at, i, num_atoms, da+i, 0 ) ) { + da[i].num[0] = NOT_AT_DERIV; + } + if ( 0 == is_deriv_chain( at, i, num_atoms, da+i, 1 ) ) { + da[i].num[1] = NOT_AT_DERIV; + } + if ( da[i].num[0] > da[i].num[1] ) { + da[i].num[0] = da[i].num[1]; + da[i].ord[0] = da[i].ord[1]; + da[i].typ[0] = da[i].typ[1]; + da[i].typ[1] = 0; + num_cuts += 1; + num_cut_pieces += 1; + } else + if ( da[i].num[0] < da[i].num[1] ) { + da[i].typ[1] = 0; + num_cuts += 1; + num_cut_pieces += 1; + } else { + /* attachments have same size: ignore both */ + /* ??? check for standard derivatizations ??? */ + da[i].typ[0] = 0; + da[i].typ[1] = 0; + } + continue; + } + ret = -88; + goto exit_function; /* unexpected */ + case 3: + if ( da[i].typ[0] == da[i].typ[1] && + da[i].typ[0] == da[i].typ[2] && + da[i].typ[0] == DERIV_AMINE_tN ) { + int x, y, z; + if ( 0 == is_deriv_chain( at, i, num_atoms, da+i, 0 ) ) { + da[i].num[0] = NOT_AT_DERIV; + } + if ( 0 == is_deriv_chain( at, i, num_atoms, da+i, 1 ) ) { + da[i].num[1] = NOT_AT_DERIV; + } + if ( 0 == is_deriv_chain( at, i, num_atoms, da+i, 2 ) ) { + da[i].num[2] = NOT_AT_DERIV; + } + + x = (da[i].num[0] < da[i].num[1])? 0 : 1; + x = (da[i].num[x] < da[i].num[2])? x : 2; /* min */ + z = (da[i].num[0] < da[i].num[1])? 1 : 0; + z = (da[i].num[x] < da[i].num[2])? 2 : z; /* max */ + y = ((x+1)^(z+1))-1; /* median */ + + + if (da[i].num[x] == da[i].num[z] ) { + /* no cuts */ + da[i].typ[0] = 0; + da[i].typ[1] = 0; + da[i].typ[2] = 0; + continue; /* all deriv. agents have same size, ignore */ + /* ??? check for standard derivatizations ??? */ + } else + if ( da[i].num[x] == da[i].num[y] ) { + /* two smallest */ + switch( z ) { + case 0: + da[i].num[0] = da[i].num[1]; + da[i].ord[0] = da[i].ord[1]; + da[i].typ[0] = da[i].typ[1]; + + da[i].num[1] = da[i].num[2]; + da[i].ord[1] = da[i].ord[2]; + da[i].typ[1] = da[i].typ[2]; + break; + case 1: + da[i].num[1] = da[i].num[2]; + da[i].ord[1] = da[i].ord[2]; + da[i].typ[1] = da[i].typ[2]; + break; + case 2: + break; + } + da[i].typ[2] = 0; + num_cuts += 2; + num_cut_pieces += 2; + } else { + /* one smallest */ + if ( x ) { + da[i].num[0] = da[i].num[x]; + da[i].ord[0] = da[i].ord[x]; + da[i].typ[0] = da[i].typ[x]; + } + da[i].typ[1] = 0; + da[i].typ[2] = 0; + num_cuts += 1; + num_cut_pieces += 1; + } + continue; + } + ret = -88; + goto exit_function; /* unexpected */ + case 4: + if ( (da[i].typ[0] & DERIV_RING) && (da[i].typ[1] & DERIV_RING) && + (da[i].typ[2] & DERIV_RING) && (da[i].typ[3] & DERIV_RING) ) { + int n01 = inchi_max( da[i].num[0], da[i].num[1] ); + int n23 = inchi_max( da[i].num[2], da[i].num[3] ); + if ( n01 < n23 && 0 < is_deriv_ring( at, i, num_atoms, da+i, 0) ) { + da[i].typ[2] = 0; + da[i].typ[3] = 0; + num_cuts += 2; + num_ring_cuts += 2; + num_cut_pieces += 1; + } else + if ( n01 > n23 && 0 < is_deriv_ring( at, i, num_atoms, da+i, 2) ) { + da[i].num[0] = da[i].num[2]; + da[i].ord[0] = da[i].ord[2]; + da[i].typ[0] = da[i].typ[2]; + + da[i].num[1] = da[i].num[3]; + da[i].ord[1] = da[i].ord[3]; + da[i].typ[1] = da[i].typ[3]; + + da[i].typ[2] = 0; + da[i].typ[3] = 0; + num_cuts += 2; + num_ring_cuts += 2; + num_cut_pieces += 1; + } else { + da[i].typ[0] = 0; + da[i].typ[1] = 0; + da[i].typ[2] = 0; + da[i].typ[3] = 0; + } + continue; + } + ret = -88; + goto exit_function; /* unexpected */ + } + } + + /* eliminate cases when + da[i1].typ[j1] && da[i2].typ[j2] && + at[i1].neighbor[da[i1].ord[j1]] == i2 && at[i2].neighbor[da[i2].ord[j2]] == i1 + that is, same bond is in the da[] elements of the adjacent atoms */ + nFound = 0; /* number of cuts to remove */ + for ( i = 0; i < num_atoms; i ++ ) { + for ( j = 0; j < MAX_AT_DERIV && da[i].typ[j]; j ++ ) { + if ( da[i].typ[j] & DERIV_DUPLIC ) + continue; + n = at[i].neighbor[(int)da[i].ord[j]]; + if ( n < i ) + continue; + for ( k = 0; k < MAX_AT_DERIV && da[n].typ[k]; k ++ ) { + if ( da[n].typ[k] & DERIV_DUPLIC ) + continue; + m = at[n].neighbor[(int)da[n].ord[k]]; + if ( m == i ) { + /* same bond in da[i].typ[j] and da[n].typ[k] */ + /* check whether both derivatives are acceptable */ + int k1=k, j1=j; + int ret_i = is_deriv_chain_or_ring( at, i, num_atoms, da+i, &j1 ); + int ret_n = is_deriv_chain_or_ring( at, n, num_atoms, da+n, &k1 ); + if ( ret_i < 0 ) { + ret = ret_i; + goto exit_function; + } + if ( ret_n < 0 ) { + ret = ret_n; + goto exit_function; + } + if ( !ret_i || ret_i && ret_n ) { + if ( da[i].typ[j1] & DERIV_RING ) { + num_cuts -= 2; + num_ring_cuts -= 2; + } else { + num_cuts -= 1; + } + num_cut_pieces -= 1; + if (ret = remove_deriv_mark( da+i, j1 )) { + goto exit_function; + } + nFound ++; + } + if ( !ret_n || ret_i && ret_n ) { + if ( da[n].typ[k1] & DERIV_RING ) { + num_cuts -= 2; + num_ring_cuts -= 2; + } else { + num_cuts -= 1; + } + num_cut_pieces -= 1; + if ( ret = remove_deriv_mark( da+n, k1 ) ) { + goto exit_function; + } + nFound ++; + } + } + } + } + } + if ( nFound ) { + for ( i = 0; i < num_atoms; i ++ ) { + for ( j = 0; j < MAX_AT_DERIV && da[i].typ[j]; j ++ ) { + /* attn: j is changed inside the cycle body */ + if ( da[i].typ[j] & DERIV_DUPLIC ) { + if (ret = remove_deriv( da+i, j )) { + goto exit_function; + } + j --; + } + } + } + } + + /* make sure DERIV_RING type disconnections increase */ + /* number of components by the number of disconnected derivateves */ + /* Avoid cases like these: + + O--R--O DO--R--OD + / \ + R--X Y--R => R--XT2 T2Y--R + \ / + O--R--O DO--R--OD + + + + O--O DO--OD + / \ + R--X--O---Y--R => R--X OD2 Y--R + T2 T2 + + */ + /* count DERIV_RING-type attachments */ +#if ( COUNT_ALL_NOT_DERIV == 1 ) + num_cuts_to_check = num_cuts; +#else + num_cuts_to_check = num_ring_cuts; +#endif + if ( num_cuts_to_check >= 2 ) + { + /* check */ + R2C_ATPAIR *ap = (R2C_ATPAIR *) inchi_malloc( num_cuts_to_check * sizeof(ap[0]) ); + AT_NUMB comp_num; + int /*n,*/ m_at, m_ord; + AT_NUMB at1, at2; + if ( !ap ) { + ret = -1; + goto exit_function; /* malloc failure */ + } +repeat_without_deriv_ring: + comp_num = 0; + /* fill out the array of bonds to be cut */ + for ( i = j = 0; i < num_atoms; i ++ ) { + if ( (da[i].typ[0] & DERIV_RING) && (da[i].typ[1] & DERIV_RING) && + da[i].num[0] <= MAX_AT_DERIV && da[i].num[1] <= MAX_AT_DERIV ) { + if ( j+1 >= num_cuts_to_check ) { + ret = -2; + goto exit_r2c_num; /* wrong number of cuts = num */ + } + for ( k = 0; k < 2; k ++ ) { + at1 = i; + at2 = at[i].neighbor[(int)da[i].ord[k]]; + n = ( at1 > at2 ); + ap[j].at[n] = at1; + ap[j].at[1-n] = at2; /* ap[j].at[0] < ap[j].at[1] */ + j ++; + } + if ( 0 < cmp_r2c_atpair( ap+j-2, ap+j-1 ) ) { + R2C_ATPAIR ap1 = ap[j-2]; + ap[j-2] = ap[j-1]; + ap[j-1] = ap1; /* sort each pair */ + } + } +#if ( COUNT_ALL_NOT_DERIV == 1 ) + else { + for ( k = 0; k < DERIV_AT_LEN && da[i].typ[k]; k ++ ) { + if ( j >= num_cuts_to_check || (da[i].typ[k] & DERIV_RING) ) { + ret = -2; + goto exit_r2c_num; /* wrong number of cuts = num or wrong type */ + } + at1 = i; + at2 = at[i].neighbor[(int)da[i].ord[k]]; + n = ( at1 > at2 ); + ap[j].at[n] = at1; + ap[j].at[1-n] = at2; /* ap[j].at[0] < ap[j].at[1] */ + j ++; + } + } +#endif + } + if ( j != num_cuts_to_check ) { + ret = -3; + goto exit_r2c_num; /* wrong number of cuts = num */ + } + /* !!!!!!!! check that there are no derivatives inside a derivative */ + comp_num = 0; /* here it is the number of removed cuts */ + for ( i = 0; i < num_cuts_to_check; i += j ) { + for ( j = n = 0; j < 2; j ++ ) { + int atj = (int)ap[i].at[j]; + if ( da[atj].typ[0] && at[atj].neighbor[(int)da[atj].ord[0]] == ap[i].at[1-j] ) { + k = j; + n ++; + m_at = atj; + m_ord = 0; + } else + if ( da[atj].typ[1] && at[atj].neighbor[(int)da[atj].ord[1]] == ap[i].at[1-j] ) { + k = j; + n ++; + m_at = atj; + m_ord = 1; + } + } + if ( n != 1 ) { + ret = -3; + goto exit_r2c_num; /* wrong atom pair */ + } + if ( (da[m_at].typ[m_ord] & DERIV_RING) ) { + n = (int)ap[i].at[k]; /* atom inside the derivation attachment */ + j = 2; /* number of bonds to cut */ + if ( i+j > num_cuts_to_check || (int)ap[i+1].at[0] != n && (int)ap[i+1].at[1] != n ) { + ret = -3; + goto exit_r2c_num; /* wrong atom pair */ + } + } else { + n = ap[i].at[1-k]; /* atom inside the derivation attachment */ + j = 1; /* number of bonds to cut */ + } + + /* at[n] belongs to the derivation agent */ + cur_num_at = mark_atoms_ap( at, n, ap+i, j, 0, 1 ); + for ( k = 0; k < num_cuts_to_check; k ++ ) { + if ( k == i || k==i+j-1 ) + continue; + if ( at[(int)ap[k].at[0]].at_type || at[(int)ap[k].at[1]].at_type ) { + /* unmark the cut: found a cut inside the derivatizing agent */ + da[m_at].typ[m_ord] |= DERIV_UNMARK; + num_cuts -= 1; + num_cut_pieces -= 1; + if ( j == 2 ) { + da[m_at].typ[1-m_ord] |= DERIV_UNMARK; + num_cuts -= 1; + num_ring_cuts -= 2; + } + comp_num ++; + break; + } + } + UnMarkOtherIndicators( at, num_atoms ); + } + if ( comp_num ) { + for ( i = 0; i < num_atoms; i ++ ) { + if ( da[i].typ[0] & DERIV_UNMARK ) { + da[i].num[0] = da[i].num[1]; + da[i].ord[0] = da[i].ord[1]; + da[i].typ[0] = da[i].typ[1]; + da[i].typ[1] = 0; + j = 0; + } else { + j = 1; + } + if ( da[i].typ[j] & DERIV_UNMARK ) { + da[i].typ[j] = 0; + } + } +#if ( COUNT_ALL_NOT_DERIV == 1 ) + num_cuts_to_check = num_cuts; +#else + num_cuts_to_check = num_ring_cuts; +#endif + if ( num_cuts < 0 || num_ring_cuts < 0 || num_cut_pieces < 0 ) { + ret = -3; + goto exit_r2c_num; /* wrong number of cuts = num */ + } + goto repeat_without_deriv_ring; + } + + /* sort the bonds for subsequent searching by bisections */ + if ( num_cuts_to_check > 1 ) { + qsort( ap, num_cuts_to_check, sizeof(ap[0]), cmp_r2c_atpair); + } + /* mark components to be disconnected */ + comp_num = 0; /* number of components */ + cur_num_at = 0; /* number of atoms left after disconnecting the derivatizing agent */ + UnMarkOtherIndicators( at, num_atoms ); + for ( i = 0; i < num_cuts_to_check; i ++ ) { + n = 0; + for ( j = 0; j < 2; j ++ ) { + if ( da[(int)ap[i].at[j]].typ[0] ) { + k = j; + n ++; + } + } + if ( n != 1 ) { + ret = -3; + goto exit_r2c_num; /* wrong atom pair */ + } + n = ap[i].at[k]; /* marked atom */ + if ( (da[n].typ[0] & DERIV_RING) ) { + n = ap[i].at[1-k]; + } + /* at[n] belongs to the derivation agent */ + if ( !at[n].at_type ) { + comp_num ++; + cur_num_at = mark_atoms_ap( at, n, ap, num_cuts_to_check, cur_num_at, comp_num ); + } + } + if ( comp_num > 1 ) { + /* eliminate offending DERIV_RING type derivatives */ + if ( num_ring_cuts <= 2 ) { + ret = -99; + goto exit_r2c_num; + } + n = 0; + for ( i = j = 0; i < num_atoms; i ++ ) { + if ( (da[i].typ[0] & DERIV_RING) && (da[i].typ[1] & DERIV_RING) ) { + int at1a = at[i].neighbor[(int)da[i].ord[0]]; + int at2a = at[i].neighbor[(int)da[i].ord[1]]; + if ( at[at1a].at_type != at[at2a].at_type ) { + da[i].typ[0] = 0; /* eliminate this cut */ + da[i].typ[1] = 0; + n ++; + num_cuts_to_check -= 2; + num_cuts -= 2; + num_ring_cuts -= 2; + num_cut_pieces -= 1; + } + } + } + if ( n > 0 && num_cuts_to_check > 2 ) { + goto repeat_without_deriv_ring; + } + } + ret = 0; +exit_r2c_num: + inchi_free( ap ); + UnMarkOtherIndicators( at, num_atoms ); + if ( ret < 0 || num_cuts_to_check >= 2 && cur_num_at < MIN_AT_LEFT_DERIV ) { + goto exit_function; /* unexpected error or nothing left */ + } + } + + if ( !num_cuts ) { + continue; /*goto exit_function;*/ + } + /* eliminate derivatives that are not in the list */ + num_cuts = EliminateDerivNotInList( at, da, num_atoms ); + if ( num_cuts < 0 ) { + ret = num_cuts; + goto exit_function; + } + + + /* make cuts */ + num_cuts = 0; + for ( i = num = 0; i < num_atoms; i ++ ) { + for ( len = 0; len < MAX_AT_DERIV && da[i].typ[len]; len ++ ) + ; + switch( len ) { + case 0: + continue; + case 1: + /* single cut: unconditional */ + make_single_cut( at, da, i, 0 ); + num_cuts += 1; + continue; + case 2: + if ( (da[i].typ[0] & DERIV_RING) && (da[i].typ[1] & DERIV_RING) || + da[i].typ[0] == DERIV_AMINE_tN && da[i].typ[1] == DERIV_AMINE_tN ) { + /* double cut, unconditional */ + make_single_cut( at, da, i, 1 ); + make_single_cut( at, da, i, 0 ); + num_cuts += 1; + continue; + } + if ( da[i].typ[0] == da[i].typ[1] ) { + /* DERIV_BRIDGE_O or DERIV_BRIDGE_NH; cut off the smallest */ + if ( da[i].num[0] > da[i].num[1] ) { + make_single_cut( at, da, i, 1 ); + num_cuts += 1; + } else + if ( da[i].num[0] < da[i].num[1] ) { + make_single_cut( at, da, i, 0 ); + num_cuts += 1; + } + continue; + } + ret = -88; + goto exit_function; /* unexpected */ + case 3: + if ( da[i].typ[0] == da[i].typ[1] && + da[i].typ[0] == da[i].typ[2] && + da[i].typ[0] == DERIV_AMINE_tN ) { + int x, y, z; + x = (da[i].num[0] < da[i].num[1])? 0 : 1; + x = (da[i].num[x] < da[i].num[2])? x : 2; /* min */ + z = (da[i].num[0] < da[i].num[1])? 1 : 0; + z = (da[i].num[x] < da[i].num[2])? 2 : z; /* max */ + y = ((x+1)^(z+1))-1; /* median */ + if (da[i].num[x] == da[i].num[z] ) + continue; /* all deriv. agents have same size */ + /* two smallest */ + if ( da[i].num[x] == da[i].num[y] && x < y ) { + int t = x; /* first cut x > y */ + x = y; + y = t; + } + make_single_cut( at, da, i, x ); + num_cuts += 1; + if ( da[i].num[x] == da[i].num[y] ) { + /* equally small */ + make_single_cut( at, da, i, y ); + num_cuts += 1; + } + continue; + } + ret = -88; + goto exit_function; /* unexpected */ + case 4: + if ( (da[i].typ[0] & DERIV_RING) && (da[i].typ[1] & DERIV_RING) && + (da[i].typ[2] & DERIV_RING) && (da[i].typ[3] & DERIV_RING) ) { + int n01 = inchi_max( da[i].num[0], da[i].num[1] ); + int n23 = inchi_max( da[i].num[2], da[i].num[3] ); + if ( n01 < n23 ) { + make_single_cut( at, da, i, 1 ); + make_single_cut( at, da, i, 0 ); + num_cuts += 1; + } else + if ( n01 > n23 ) { + make_single_cut( at, da, i, 3 ); + make_single_cut( at, da, i, 2 ); + num_cuts += 1; + } + continue; + } + } + } + nTotNumCuts += num_cuts; +#if ( REMOVE_CUT_DERIV == 1 ) /* normally YES */ + if ( num_cuts ) { + /* remove marked with Tritium disconnected derivative attachments */ + ORIG_ATOM_DATA Orig_inp_data1, *orig_inp_data1; + INP_ATOM_DATA *inp_cur_data1 = NULL; + int num_components1, i_component1, num_component_left=0; + orig_inp_data1 = &Orig_inp_data1; + memset( orig_inp_data1, 0, sizeof(orig_inp_data1[0]) ); + UnMarkRingSystemsInp( at, num_atoms ); + UnMarkOtherIndicators( at, num_atoms ); + UnMarkOneComponent( at, num_atoms ); + for (i = 0; i < num_atoms; i ++ ) { + orig_inp_data1->num_inp_bonds += at[i].valence; + } + orig_inp_data1->num_inp_bonds /= 2; + orig_inp_data1->num_inp_atoms = num_atoms; + orig_inp_data1->at = at; /* = from inp_cur_data[i_component].at */ + num_components1 = MarkDisconnectedComponents( orig_inp_data1, 0 ); + inp_cur_data1 = (INP_ATOM_DATA *)inchi_calloc( num_components1, sizeof(inp_cur_data1[0]) ); + /* extract components and discard disconnected derivatizing agents */ + for ( i_component1 = 0; i_component1 < num_components1; i_component1 ++ ) { + CreateInpAtomData( inp_cur_data1+i_component1, orig_inp_data1->nCurAtLen[i_component1], 0 ); + inp_cur_data1[i_component1].num_at = ExtractConnectedComponent( orig_inp_data1->at, orig_inp_data1->num_inp_atoms, + i_component1+1, inp_cur_data1[i_component1].at ); + /* error processing */ + if ( inp_cur_data1[i_component1].num_at <= 0 || orig_inp_data1->nCurAtLen[i_component1] != inp_cur_data1[i_component1].num_at ) { + ret = -(i_component1+1); /* severe error */ + break; + } + /* if the component has tritium then discard: it is a derivatizing agent */ + for (i = 0; i < inp_cur_data1[i_component1].num_at; i ++ ) { + if ( inp_cur_data1[i_component1].at[i].num_iso_H[1] ) { + inp_cur_data1[i_component1].at[i].num_iso_H[1] = 0; /* remove deuterium */ + } + if ( inp_cur_data1[i_component1].at[i].num_iso_H[2] ) { + FreeInpAtomData( inp_cur_data1+i_component1 ); + break; + } + } + } + /* merge components into one -- must be only one */ + for ( i_component1 = 0, num_atoms = 0; i_component1 < num_components1; i_component1 ++ ) { + num_atoms += inp_cur_data1[i_component1].num_at; + } + at = (inp_ATOM *) inchi_calloc( num_atoms, sizeof(at[0]) ); + cur_num_at = 0; + for ( i_component1 = 0; i_component1 < num_components1; i_component1 ++ ) { + /* clean and prepare */ + if ( !inp_cur_data1[i_component1].num_at ) + continue; /* removed derivatizing object */ + /*UnMarkOneComponent( inp_cur_data1[i_component1].at, inp_cur_data1[i_component1].num_at );*/ + /* merge one by one */ + cur_num_at = add_inp_ATOM( at, num_atoms, cur_num_at, inp_cur_data1[i_component1].at, inp_cur_data1[i_component1].num_at ); + FreeInpAtomData( inp_cur_data1+i_component1 ); /* cleanup */ + num_component_left ++; + } + /* replace the component */ + /* order of the following two statements is critically important */ + UnMarkDisconnectedComponents( orig_inp_data1 ); /* orig_inp_data1->at is same as inp_cur_data[i_component].at */ + FreeInpAtomData( inp_cur_data+i_component ); /* cleanup the original component */ + inp_cur_data[i_component].at = at; + inp_cur_data[i_component].num_at = cur_num_at; + inchi_free( inp_cur_data1 ); + } +#endif + } + if ( nTotNumCuts ) { + /* merge components into one */ + for ( i = 0, num_atoms = 0; i < num_components; i ++ ) { + num_atoms += inp_cur_data[i].num_at; + } + at = (inp_ATOM *) inchi_calloc( num_atoms, sizeof(at[0]) ); + cur_num_at = 0; + for ( i = 0; i < num_components; i ++ ) { + /* clean and prepare */ + UnMarkRingSystemsInp( inp_cur_data[i].at, inp_cur_data[i].num_at ); + UnMarkOtherIndicators( inp_cur_data[i].at, inp_cur_data[i].num_at ); + UnMarkOneComponent( inp_cur_data[i].at, inp_cur_data[i].num_at ); + subtract_DT_from_num_H( inp_cur_data[i].num_at, inp_cur_data[i].at ); + /* merge one by one */ + cur_num_at = add_inp_ATOM( at, num_atoms, cur_num_at, inp_cur_data[i].at, inp_cur_data[i].num_at ); + } + /* replace orig_inp_data */ + if ( cur_num_at == num_atoms ) { + inchi_free( orig_inp_data->at ); + orig_inp_data->at = at; + orig_inp_data->num_inp_atoms = cur_num_at; + if ( orig_inp_data->szCoord ) { + inchi_free( orig_inp_data->szCoord ); + orig_inp_data->szCoord = NULL; + } + UnMarkDisconnectedComponents( orig_inp_data ); + } else { + if ( at ) { + inchi_free( at ); + at = NULL; + } + ret = -999; /* num atoms mismatch */ + } + } +exit_function: + if ( da ) { + inchi_free( da ); + da = NULL; + } + for ( i_component = 0; i_component < num_components; i_component ++ ) { + FreeInpAtomData( inp_cur_data+i_component ); + } + inchi_free( inp_cur_data ); + inp_cur_data = NULL; + + return ret? ret : nTotNumCuts; +} + +#endif /* UNDERIVATIZE */ +/********************************************************************/ +#if ( RING2CHAIN == 1 ) +/* + type=1 (incl sugars: W=O, A=C(sat), Z=C(sat), Y=O, B=C(sat)-OH + + A---W A---WH + / | / + | | ---> | + \ | \ + B---Z---YH B---Z===Y + | | + | | + C(opt) C(opt) + + type=2 [not implemented] + + R---W R---WH + / \ / + | Z ---> | Z + \ / \ // + R---YH R---Y + +*/ +#define R2C_EMPTY 127 +typedef struct tagRing2Chain { /* atom Z */ + char type; /* 1 => sugar-like */ + char ordW; /* ordering number of W-neighbor; bond to break; H to add */ + char ordY; /* ordering number of YH-neighbor; bond to increment; H to remove */ + char ordC; /* atom B = C(sat) */ + char ordCopt; /* if exists, saturated C connected by a chain-bond to Z */ +} R2C_AT; + +int detect_r2c_Zatom( inp_ATOM *at, R2C_AT *da, int iZ ); +int cut_ring_to_chain( inp_ATOM *at, R2C_AT *da, int iZ ); + +/********************************************************************/ +int detect_r2c_Zatom( inp_ATOM *at, R2C_AT *da, int iZ ) +{ + int i, j, neigh, neighneigh, nRingSystem, num_found; + R2C_AT da1; + if ( at[iZ].valence > 4 ) + return 0; + if ( at[iZ].valence != at[iZ].chem_bonds_valence ) + return 0; /* approach limitation: no double bonds */ + + if ( at[iZ].el_number != el_number_C ) + return 0; /* sugar-specific */ + + if ( at[iZ].nNumAtInRingSystem < 5 ) + return 0; /* not in a suitable ring */ + + if ( !at[iZ].bCutVertex ) + return 0; /* recognize only type 1 for now */ + + nRingSystem = at[iZ].nRingSystem; + memset ( &da1, R2C_EMPTY, sizeof(da1) ); + + for ( i = 0, num_found = 0; i < at[iZ].valence; i ++ ) { + neigh = at[iZ].neighbor[i]; + if ( at[neigh].charge || at[neigh].radical ) + return 0; + if ( at[neigh].el_number == el_number_O && + at[neigh].valence == 1 && + at[neigh].chem_bonds_valence == 1 && + at[neigh].num_H == 1 ) { + /* found Z-OH, i.e. Z-YH */ + if ( da1.ordY == R2C_EMPTY ) { + da1.ordY = i; + num_found ++; + continue; + } else { + return 0; + } + } + if ( at[neigh].el_number == el_number_O && + at[neigh].valence == 2 && + at[neigh].chem_bonds_valence == 2 && + at[neigh].num_H == 0 && + at[neigh].nRingSystem == nRingSystem ) { + /* found Z-O-, i.e. Z-W- */ + if ( da1.ordW == R2C_EMPTY ) { + /* j = index of the oppozite to at[iZ] neighbor of at[neigh] */ + j = (at[neigh].neighbor[0] == iZ); + neighneigh = at[neigh].neighbor[j]; + if ( at[neighneigh].valence != at[neighneigh].chem_bonds_valence || + at[neighneigh].el_number != el_number_C ) + return 0; /* sugar-specific */ + da1.ordW = i; + num_found ++; + continue; + } else { + return 0; + } + } + if ( at[neigh].el_number == el_number_C && + at[neigh].valence > 2 && + at[neigh].chem_bonds_valence == at[neigh].valence && + at[neigh].num_H <= 1 && + at[neigh].nRingSystem == nRingSystem ) { + /* sugar-specfic: carbon in the ring should have -OH neighbor */ + int iOH; + for ( j = 0; j < at[neigh].valence; j ++ ) { + iOH = at[neigh].neighbor[j]; + if ( at[iOH].el_number == el_number_O && + at[iOH].valence == 1 && + at[iOH].chem_bonds_valence == 1 && + at[iOH].num_H == 1 && + !at[iOH].charge && !at[iOH].radical ) { + if ( da1.ordC == R2C_EMPTY ) { + da1.ordC = i; + num_found ++; + break; + } else { + return 0; + } + } + } + if ( j < at[neigh].valence ) + continue; + } + if ( at[neigh].el_number == el_number_C && + at[neigh].chem_bonds_valence == at[neigh].valence && + at[neigh].nRingSystem != nRingSystem ) { + /* extra carbon neighbor of Z */ + if ( da1.ordCopt == R2C_EMPTY ) { + da1.ordCopt = i; + continue; + } + } + return 0; /* unexpectd neighbor */ + } + if (num_found == 3) { + da1.type = 1; + da[iZ] = da1; + return 1; /* disconnection found */ + } + return 0; +} +/********************************************************************/ +int cut_ring_to_chain( inp_ATOM *at, R2C_AT *da, int iZ ) +{ + int ret = -1; /* error flag */ + int iordW = (int)da[iZ].ordW; /* ord of the bond in iZ */ + int iordY = (int)da[iZ].ordY; /* ord of the bond in iZ */ + int iordC = (int)da[iZ].ordC; + int iW, iY, num_iso_H, i, jordZ; + AT_NUMB *p; + + if ( da[iZ].type != 1 ) { + return 0; + } + if ( 0 > iordW || iordW >= at[iZ].valence || + 0 > iordY || iordY >= at[iZ].valence || + 0 > iordC || iordC >= at[iZ].valence /* suger-specific*/) { + return -1; /* program error */ + } + /* find other da[] that affect at[iZ] */ + iW = at[iZ].neighbor[iordW]; /* opposite atom to disconnect and add H */ + iY = at[iZ].neighbor[iordY]; /* opposite atom to increment the bond and remove H*/ + if ( !at[iY].num_H || at[iZ].bond_type[iordY] != BOND_TYPE_SINGLE ) { + return -2; /* program error */ + } + /* increment at[iZ]--at[iY] bond */ + p = is_in_the_list( at[iY].neighbor, (AT_NUMB) iZ, at[iY].valence ); + if ( !p ) { + return -3; /* program error */ + } + jordZ = p - at[iY].neighbor; + at[iZ].bond_type[iordY] ++; + at[iZ].chem_bonds_valence ++; + at[iY].bond_type[jordZ] ++; + at[iY].chem_bonds_valence ++; + + /* disconnect at[iZ]--at[iW] bond */ + ret = DisconnectInpAtBond( at, NULL, iZ, iordW ); + if ( ret != 1 ) { + return -4; /* program error */ + } + /* disconnection succeeded */ + /* transfer H from at[iY] to at[iW] */ + num_iso_H = NUM_ISO_H(at, iY); + if ( at[iY].num_H == num_iso_H ) { + for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { + if ( at[iY].num_iso_H[i] ) { + at[iY].num_iso_H[i] --; + at[iW].num_iso_H[i] ++; + } + } + } + at[iY].num_H --; + at[iW].num_H ++; + return 1; +} +/********************************************************************/ +int Ring2Chain( ORIG_ATOM_DATA *orig_inp_data ) +{ + int ret = 0, i, j, n, num_atoms, num_components, nFound, num, num_cuts, iZ, cur_num_at; + inp_ATOM *at = orig_inp_data->at; + INP_ATOM_DATA *inp_cur_data = NULL; + R2C_AT *da = NULL; + + set_R2C_el_numbers( ); + /* prepare */ + num_atoms = remove_terminal_HDT( orig_inp_data->num_inp_atoms, at, 1 ); + /*^^^^^ always accomodate accomodate FIX_TERM_H_CHRG_BUG - IPl, July 2008*/ + orig_inp_data->num_inp_atoms = num_atoms; + + /* initialize */ + UnMarkDisconnectedComponents( orig_inp_data ); + num_cuts = 0; + /* mark */ + num_components = MarkDisconnectedComponents( orig_inp_data, 0 ); + inp_cur_data = (INP_ATOM_DATA *)inchi_calloc( num_components, sizeof(inp_cur_data[0]) ); + iZ = -1; + for ( j = 0; j < num_components; j ++ ) { + CreateInpAtomData( inp_cur_data+j, orig_inp_data->nCurAtLen[j], 0 ); + inp_cur_data[j].num_at = ExtractConnectedComponent( orig_inp_data->at, orig_inp_data->num_inp_atoms, j+1, inp_cur_data[j].at ); + /* error processing */ + if ( inp_cur_data[j].num_at <= 0 || orig_inp_data->nCurAtLen[j] != inp_cur_data[j].num_at ) { + ret = -(j+1); /* severe error */ + goto exit_function; + } + /* initialize */ + num_atoms = inp_cur_data[j].num_at; + at = inp_cur_data[j].at; + add_DT_to_num_H( num_atoms, at ); + + UnMarkRingSystemsInp( at, num_atoms ); + UnMarkOtherIndicators( at, num_atoms ); + UnMarkOneComponent( at, num_atoms ); + MarkRingSystemsInp( at, num_atoms, 0 ); + ret = mark_arom_bonds( at, num_atoms ); + if ( ret < 0 ) { + goto exit_function; + } + ret = 0; + if ( da ) inchi_free( da ); + da = (R2C_AT *)inchi_calloc( num_atoms, sizeof(da[0]) ); + + /* detect ring-to-chain possibilities */ + nFound = 0; + for ( i = 0, num=0; i < num_atoms; i ++ ) { + if ( at[i].bCutVertex /* type 1 specific*/ && !da[i].type ) { + num += (n=detect_r2c_Zatom( at, da, i )); + if ( n == 1 ) + iZ = i; + UnMarkOtherIndicators( at, num_atoms ); + } + } + + if ( num == 1 ) { + /* convert ring to chain: make single cut */ + ret = cut_ring_to_chain( at, da, iZ ); + if ( ret < 0 ) { + goto exit_function; + } + num_cuts += (ret == 1); + } else + if ( num ) { + /* allocate an array of bonds to be cut */ + R2C_ATPAIR *ap = (R2C_ATPAIR *) inchi_malloc( sizeof(ap[0]) * num ); + AT_NUMB comp_num = 0; + if ( !ap ) { + ret = -1; /* malloc failure */ + goto exit_function; + } + /* fill out the array of bonds to be cut */ + for ( i = j = 0; i < num_atoms; i ++ ) { + if ( da[i].type == 1 ) { + AT_NUMB at1 = i; + AT_NUMB at2 = at[i].neighbor[(int)da[i].ordW]; + if ( j >= num ) { + ret = -2; + goto exit_r2c_num; /* wrong number of cuts = num */ + } + n = ( at1 > at2 ); + ap[j].at[n] = at1; + ap[j].at[1-n] = at2; /* ap[j].at[0] < ap[j].at[1] */ + j ++; + } + } + if ( j != num ) { + ret = -3; + goto exit_r2c_num; /* wrong number of cuts = num */ + } + /* sort the bonds for subsequent searching by bisections */ + qsort( ap, num, sizeof(ap[0]), cmp_r2c_atpair); + /* mark components to be disconnected */ + for ( i = 0; i < num; i ++ ) { + for ( j = 0; j < 2; j ++ ) { + if ( !at[ap[i].at[j]].at_type ) { + comp_num ++; + mark_atoms_ap( at, (int)ap[i].at[j], ap, num, 0, comp_num ); + } + } + } + /* convert ring to chain */ + for ( i = 0; i < num; i ++ ) { + int i1 = ap[i].at[0]; + int i2 = ap[i].at[1]; + iZ = -1; + if ( at[i1].at_type == at[i2].at_type ) { + /* by definition, there are no adjacent iZ atoms; one iZ atom per bond */ + if ( da[i1].type == 1 && at[i1].neighbor[(int)da[i1].ordW] == i2 ) { + iZ = i1; + } else + if ( da[i2].type == 1 && at[i2].neighbor[(int)da[i2].ordW] == i1 ) { + iZ = i2; + } else { + ret = -3; + goto exit_r2c_num; + } + ret = cut_ring_to_chain( at, da, iZ ); + if ( ret < 0 ) { + goto exit_r2c_num; + } + num_cuts += (ret == 1); + } + } + ret = 0; +exit_r2c_num: + inchi_free( ap ); + UnMarkOtherIndicators( at, num_atoms ); + if ( ret < 0 ) { + goto exit_function; + } + } + } + if ( num_cuts ) { + /* merge components into one */ + for ( i = 0, num_atoms = 0; i < num_components; i ++ ) { + num_atoms += inp_cur_data[i].num_at; + } + at = (inp_ATOM *) inchi_calloc( num_atoms, sizeof(at[0]) ); + cur_num_at = 0; + for ( i = 0; i < num_components; i ++ ) { + /* clean and prepare */ + UnMarkRingSystemsInp( inp_cur_data[i].at, inp_cur_data[i].num_at ); + UnMarkOtherIndicators( inp_cur_data[i].at, inp_cur_data[i].num_at ); + UnMarkOneComponent( inp_cur_data[i].at, inp_cur_data[i].num_at ); + subtract_DT_from_num_H( inp_cur_data[i].num_at, inp_cur_data[i].at ); + /* merge one by one */ + cur_num_at = add_inp_ATOM( at, num_atoms, cur_num_at, inp_cur_data[i].at, inp_cur_data[i].num_at ); + } + /* replace orig_inp_data */ + if ( cur_num_at == num_atoms ) { + inchi_free( orig_inp_data->at ); + orig_inp_data->at = at; + orig_inp_data->num_inp_atoms = cur_num_at; + if ( orig_inp_data->szCoord ) { + inchi_free( orig_inp_data->szCoord ); + orig_inp_data->szCoord = NULL; + } + UnMarkDisconnectedComponents( orig_inp_data ); + } else { + if ( at ) { + inchi_free( at ); + at = NULL; + } + ret = -999; /* num atoms mismatch */ + } + } +exit_function: + if ( da ) { + inchi_free( da ); + da = NULL; + } + for ( j = 0; j < num_components; j ++ ) { + FreeInpAtomData( inp_cur_data+j ); + } + inchi_free( inp_cur_data ); + inp_cur_data = NULL; + + return ret? ret : num_cuts; +} + + +#endif /* RING2CHAIN */ diff --git a/INCHI-1-SRC/INCHI_BASE/src/ichinorm.h b/INCHI-1-SRC/INCHI_BASE/src/ichinorm.h new file mode 100644 index 0000000..25fb9ae --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/ichinorm.h @@ -0,0 +1,125 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _ICHINORM_H_ +#define _ICHINORM_H_ + +#include "mode.h" +#include "ichitaut.h" + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + +/* main normalization procedure */ +int mark_alt_bonds_and_taut_groups( struct tagINCHI_CLOCK *ic, + struct tagCANON_GLOBALS *pCG, + inp_ATOM *at, + inp_ATOM *at_fixed_bonds_out, + int num_atoms, + struct tagInchiTime *ulTimeOutTime, + T_GROUP_INFO *t_group_info, + INCHI_MODE *inpbTautFlags, + INCHI_MODE *inpbTautFlagsDone, + int nebend, int *ebend ); + +int MarkTautomerGroups( struct tagCANON_GLOBALS *pCG, inp_ATOM *at, + int num_atoms, + T_GROUP_INFO *t_group_info, + C_GROUP_INFO *c_group_info, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD); + +int MarkChargeGroups(struct tagCANON_GLOBALS *pCG, inp_ATOM *at, int num_atoms, + C_GROUP_INFO *c_group_info, T_GROUP_INFO *t_group_info, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD); +int MarkSaltChargeGroups(struct tagCANON_GLOBALS *pCG, inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, + T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD); +int MarkSaltChargeGroups2(struct tagCANON_GLOBALS *pCG, inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, + T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD); +int MergeSaltTautGroups(struct tagCANON_GLOBALS *pCG, inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, + T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, + struct BalancedNetworkStructure *pBNS); +int MakeIsotopicHGroup( inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, + T_GROUP_INFO *t_group_info); + +int remove_terminal_HDT( int num_atoms, inp_ATOM *at, int bFixTermHChrg); +int RemoveExcessiveImplicitH(int num_atoms, int num_removed_H, inp_ATOM *at); +int add_DT_to_num_H(int num_atoms, inp_ATOM *at); +int MarkRingSystemsInp( inp_ATOM *at, int num_atoms, int start); +int free_t_group_info( T_GROUP_INFO *t_group_info); +int make_a_copy_of_t_group_info( T_GROUP_INFO *t_group_info, + T_GROUP_INFO *t_group_info_orig); +int set_tautomer_iso_sort_keys(T_GROUP_INFO *t_group_info); +int CountTautomerGroups( sp_ATOM *at, int num_atoms, T_GROUP_INFO *t_group_info); +int CountTautomerGroupsInpAt(inp_ATOM *at, int num_atoms, T_GROUP_INFO *t_group_info); +int SortTautomerGroupsAndEndpoints(struct tagCANON_GLOBALS *pCG, T_GROUP_INFO *t_group_info, + int num_atoms, int num_at_tg, AT_RANK *nRank); +int FillIsotopicAtLinearCT(int num_atoms, sp_ATOM* at, + const AT_RANK *nAtomNumber, + AT_ISOTOPIC *LinearCTIsotopic, + int nMaxLenLinearCTIsotopic, int *pnLenLinearCTIsotopic); +int FillTautLinearCT2(struct tagCANON_GLOBALS *pCG, int num_atoms, int num_at_tg, int bIsoTaut, + const AT_RANK *nRank, const AT_RANK *nAtomNumber, + const AT_RANK *nSymmRank, const AT_RANK *nRankIso, + const AT_RANK *nAtomNumberIso, const AT_RANK *nSymmRankIso, + AT_TAUTOMER *LinearCTTautomer, + int nMaxLenLinearCTTautomer, int *pnLenLinearCTTautomer, + AT_ISO_TGROUP *LinearCTIsotopicTautomer, + int nMaxLenLinearCTIsotopicTautomer, + int *pnLenLinearCTIsotopicTautomer, + T_GROUP_INFO *t_group_info); + + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + + + +#endif /* _ICHINORM_H_ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichiparm.c b/INCHI-1-SRC/INCHI_BASE/src/ichiparm.c similarity index 53% rename from INCHI-1-SRC/INCHI_API/inchi_dll/ichiparm.c rename to INCHI-1-SRC/INCHI_BASE/src/ichiparm.c index 04218c1..23cdadb 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichiparm.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichiparm.c @@ -1,77 +1,68 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include -/* #include */ -#include -#include - -#include "mode.h" /* moved from below, suggestion by David Mosenkis */ - -#ifndef COMPILE_ANSI_ONLY -#include -#endif - -#include "inpdef.h" -#include "ichi.h" -#include "strutil.h" -#include "util.h" -#include "ichidrp.h" -#include "ichierr.h" -#include "ichimain.h" -#include "extr_ct.h" - -#ifdef TARGET_LIB_FOR_WINCHI -#include "ichi_lib.h" -#endif - -#include "ichicomp.h" - -#if ( ADD_CMLPP == 1 ) -#include "readcml.hpp" -#include "debug.h" -#endif - -#include "ichi_io.h" -#include "ichiparm.h" +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include +#include +/* #include */ +#include +#include + +#include "mode.h" +#ifndef COMPILE_ANSI_ONLY +#include +#endif + +#include "incomdef.h" +#include "ichidrp.h" +#include "inpdef.h" +#include "ichi.h" +#include "strutil.h" +#include "util.h" +#include "ichidrp.h" +#include "ichierr.h" +#include "ichimain.h" +#include "extr_ct.h" +#ifdef TARGET_LIB_FOR_WINCHI +#include "../../../IChI_lib/src/ichi_lib.h" +#endif +#include "ichicomp.h" + +#include "ichi_io.h" +#include "ichiparm.h" diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichiparm.h b/INCHI-1-SRC/INCHI_BASE/src/ichiparm.h similarity index 63% rename from INCHI-1-SRC/INCHI_API/inchi_dll/ichiparm.h rename to INCHI-1-SRC/INCHI_BASE/src/ichiparm.h index 8772ed0..be09034 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichiparm.h +++ b/INCHI-1-SRC/INCHI_BASE/src/ichiparm.h @@ -1,2658 +1,2701 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -int DetectInputINChIFileType(FILE **inp_file, INPUT_PARMS *ip, const char *fmode); -void HelpCommandLineParmsReduced(INCHI_IOSTREAM *f); - - - - -/*****************************************************************************************/ -int ReadCommandLineParms(int argc, const char *argv[], INPUT_PARMS *ip, - char *szSdfDataValue, unsigned long *ulDisplTime, - int bReleaseVersion, INCHI_IOSTREAM *log_file) -{ - int i, k, c; - const char *q; - unsigned long ul; - int nFontSize = -9; - int nMode = 0; - int nReleaseMode = nMode | (REQ_MODE_BASIC | REQ_MODE_TAUT | REQ_MODE_ISO | REQ_MODE_STEREO); - - -#if ( MIN_SB_RING_SIZE > 0 ) - int nMinDbRinSize = MIN_SB_RING_SIZE, mdbr=0; -#endif - - /*int bNotRecognized=0;*/ - char szNameSuffix[32], szOutputPath[512]; - int bNameSuffix, bOutputPath; - int bMergeAllInputStructures; - int bDisconnectSalts = (DISCONNECT_SALTS==1); - int bDoNotAddH = 0; - - - - int bVer1Options = 1; - int bReconnectCoord = (RECONNECT_METALS==1); - int bDisconnectCoord = (DISCONNECT_METALS==1); - - - -#ifdef TARGET_LIB_FOR_WINCHI -/* int bVer1Options = 0; - int bReconnectCoord = 1; - int bDisconnectCoord = 1; */ - int is_gui = 1; - int bINChIOutputOptions = INCHI_OUT_EMBED_REC; /* embed reconnected & output full aux info */ - int bCompareComponents = CMP_COMPONENTS; -#else -/* int bVer1Options = 1; - int bReconnectCoord = (RECONNECT_METALS==1); - int bDisconnectCoord = (DISCONNECT_METALS==1); */ - int is_gui = 0; - int bINChIOutputOptions = ((EMBED_REC_METALS_INCHI==1)? INCHI_OUT_EMBED_REC : 0); - /*| INCHI_OUT_NO_AUX_INFO INCHI_OUT_SHORT_AUX_INFO*/ - int bCompareComponents = 0; -#endif - - int bDisconnectCoordChkVal = (CHECK_METAL_VALENCE == 1); - int bMovePositiveCharges = (MOVE_CHARGES==1); - int bAcidTautomerism = (DISCONNECT_SALTS==1)? (TEST_REMOVE_S_ATOMS==1? 2:1):0; - int bUnchargedAcidTaut = (CHARGED_SALTS_ONLY==0); - int bMergeSaltTGroups = (DISCONNECT_SALTS==1); - int bDisplayCompositeResults = 0; - -#define VER103_DEFAULT_MODE (REQ_MODE_TAUT | REQ_MODE_ISO | REQ_MODE_STEREO |\ - REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU) - - INCHI_MODE bVer1DefaultMode = VER103_DEFAULT_MODE; - - const char *ext[MAX_NUM_PATHS+1]; - const char *pArg; - double t; - int bRecognizedOption; - int bDisplay = 0; - int bNoStructLabels = 0; - int bOutputMolfileOnly = 0; - int bOutputMolfileDT = 0; - int bOutputMolfileSplit = 0; - int bForcedChiralFlag = 0; - -#if ( READ_INCHI_STRING == 1 ) - int bDisplayIfRestoreWarnings = 0; -#endif - -#ifdef TARGET_LIB_FOR_WINCHI - int bXml = INCHI_OUT_XML; -#else - int bXml = INCHI_OUT_PLAIN_TEXT; -#endif - int bTgFlagVariableProtons = 1; - int bTgFlagHardAddRenProtons = 1; -#ifdef STEREO_WEDGE_ONLY - int bPointedEdgeStereo = STEREO_WEDGE_ONLY; /* NEWPS TG_FLAG_POINTED_EDGE_STEREO*/ -#endif -#if ( FIX_ADJ_RAD == 1 ) - int bFixAdjacentRad = 0; -#endif - int bAddPhosphineStereo = 1; - int bAddArsineStereo = 1; - int bFixSp3bug = 1; - int bFixFB2 = 1; - int bKetoEnolTaut = 0; - int b15TautNonRing = 0; - int bStdFormat = 1; - int bHashKey = 0; - int bHashXtra1 = 0; - int bHashXtra2 = 0; - - ext[0] = ".mol"; - ext[1] = bVer1Options? ".txt" : ".ich"; - ext[2] = ".log"; - ext[3] = ".prb"; - ext[4] = ""; - -#if ( MAX_NUM_PATHS < 4 ) - #error Wrong initialization -#endif - - - - /* init table parms */ - memset(ip, 0, sizeof(*ip)); - - /* default are standard InChI generation options */ - bVer1DefaultMode &= ~REQ_MODE_BASIC; /* "FIXEDH - OFF" */ - bReconnectCoord = 0; /* "RECMET - OFF" */ - bPointedEdgeStereo = 1; /* "NEWPS" */ - ip->bFixNonUniformDraw = 1; /* "FNUD" */ - - - -#ifndef COMPILE_ANSI_ONLY - strcpy( ip->tdp.ReqShownFoundTxt[ilSHOWN], "Shown" ); - ip->dp.sdp.tdp = &ip->tdp; - ip->dp.pdp = &ip->pdp; -#endif - - memset( szNameSuffix, 0, sizeof(szNameSuffix) ); - bNameSuffix = 0; - memset(szOutputPath, 0, sizeof(szOutputPath) ); - bOutputPath = 0; - bMergeAllInputStructures = 0; - - *ulDisplTime = 0; -#ifdef TARGET_API_LIB - ip->msec_MaxTime = 0; /* milliseconds, default = unlimited in libinchi */ -#else - ip->msec_MaxTime = 60000; /* milliseconds, default = 60 sec */ -#endif - - - if ( bReleaseVersion ) - { - /* normal */ - /*ip->bINChIOutputOptions |= INCHI_OUT_PLAIN_TEXT;*/ - /*ip->bXml = 1;*/ - ip->bAbcNumbers = 0; - ip->bCtPredecessors = 0; - /* - -- testing -- - ip->bXml = 0; - ip->bAbcNumbers = 1; - */ - } - else - { - /*bXml = INCHI_OUT_PLAIN_TEXT;*/ - nReleaseMode = 0; - } - - if ( bVer1Options ) - { - bNameSuffix = 1; - szNameSuffix[0] = '\0'; - } - - - /* Parse */ - - for ( i = 1; i < argc; i ++ ) - { - - /* if ( !(bVer1Options & 1) && INCHI_OPTION_PREFX == argv[i][0] && INCHI_OPTION_PREFX != argv[i][1] ) */ - if ( is_gui && INCHI_OPTION_PREFX == argv[i][0] && INCHI_OPTION_PREFX != argv[i][1] ) - { - /*=== parsing TARGET_LIB_FOR_WINCHI GUI (and v. 0.9xx Beta)options ===*/ - - pArg = argv[i]+1; - - - /*--- Input options ---*/ - if ( !stricmp( pArg, "INPAUX" )) - { - if (INPUT_NONE == ip->nInputType) - ip->nInputType = INPUT_INCHI_PLAIN; - } - - - - else if ( INPUT_NONE == ip->nInputType && - (!memicmp( pArg, "SDF", 3 )) && - ( pArg[3] == ':' ) ) - { - k = 0; - mystrncpy( ip->szSdfDataHeader, pArg+4, MAX_SDF_HEADER+1 ); - LtrimRtrim( ip->szSdfDataHeader, &k ); - if ( k ) { - ip->pSdfLabel = ip->szSdfDataHeader; - ip->pSdfValue = szSdfDataValue; - ip->nInputType = INPUT_SDFILE; - } else { - ip->pSdfLabel = NULL; - ip->pSdfValue = NULL; - ip->nInputType = INPUT_MOLFILE; - } - } - - else if ( INPUT_NONE == ip->nInputType && !stricmp( pArg, "MOL" ) ) - { - ip->nInputType = INPUT_MOLFILE; - } - else if ( INPUT_NONE == ip->nInputType && !stricmp( pArg, "SDF" ) ) - { - ip->nInputType = INPUT_MOLFILE; - } -#if ( ADD_CMLPP == 1 ) - else if ( INPUT_NONE == ip->nInputType && !stricmp( pArg, "CML" ) ) { - /* CMLfile label */ - ip->nInputType = INPUT_CMLFILE; - } -#endif - - else if ( !memicmp( pArg, "START:", 6 ) ) - { - ip->first_struct_number = strtol(pArg+6, NULL, 10); - } - else if ( !memicmp( pArg, "END:", 4 ) ) - { - ip->last_struct_number = strtol(pArg+4, NULL, 10); - } -#ifdef BUILD_WITH_ENG_OPTIONS - else if ( !memicmp( pArg, "RSB:", 4 ) ) - { - mdbr = (int)strtol(pArg+4, NULL, 10); - } else - if ( !memicmp( pArg, "DISCONSALT:", 11 ) ) { - bDisconnectSalts = (0 != strtol(pArg+11, NULL, 10)); - } else - if ( !memicmp( pArg, "DISCONMETAL:", 12 ) ) { - bDisconnectCoord = (0 != strtol(pArg+12, NULL, 10)); - } else - if ( !memicmp( pArg, "RECONMETAL:", 11 ) ) { - bReconnectCoord = (0 != strtol(pArg+11, NULL, 10)); - } else - if ( !memicmp( pArg, "DISCONMETALCHKVAL:", 18 ) ) { - bDisconnectCoordChkVal = (0 != strtol(pArg+18, NULL, 10)); - } else - if ( !memicmp( pArg, "MOVEPOS:", 8 ) ) { - bMovePositiveCharges = (0 != strtol(pArg+8, NULL, 10)); - } else - if ( !memicmp( pArg, "MERGESALTTG:", 12 ) ) { - bMergeSaltTGroups = (0 != strtol(pArg+12, NULL, 10)); - } else - if ( !memicmp( pArg, "UNCHARGEDACIDS:", 15) ) { - bUnchargedAcidTaut = (0 != strtol(pArg+15, NULL, 16));; - } else - if ( !memicmp( pArg, "ACIDTAUT:", 9 ) ) { - bAcidTautomerism = c = (int)strtol(pArg+9, NULL, 10); - if ( 0 <= c && c <= 2 ) bAcidTautomerism = c; - /*else bNotRecognized = 2*bReleaseVersion;*/ - } -#endif - - /*--- Output options ---*/ -#if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) ) - else if ( !stricmp( pArg, "Tabbed" ) ) - { - bXml |= INCHI_OUT_TABBED_OUTPUT; - } -#endif - else if ( !stricmp( pArg, "NOLABELS" ) ) - { - bNoStructLabels = 1; - } - else if ( !stricmp( pArg, "SAVEOPT" ) ) - { - bINChIOutputOptions |= INCHI_OUT_SAVEOPT; - } - else if ( !stricmp( pArg, "AUXNONE" ) ) - { - /* no aux. info */ - bINChIOutputOptions |= INCHI_OUT_NO_AUX_INFO; /* no aux info */ - bINChIOutputOptions &= ~INCHI_OUT_SHORT_AUX_INFO; - } - - -#if ( defined(BUILD_WITH_ENG_OPTIONS) || defined(TARGET_LIB_FOR_WINCHI) ) - else if ( !stricmp( pArg, "SDFID" ) ) - { - ip->bGetSdfileId = 1; - } - else if ( !stricmp( pArg, "XML" ) ) - { - bXml &= ~INCHI_OUT_PLAIN_TEXT; - bXml |= INCHI_OUT_XML; - /*bNotRecognized = 2*bReleaseVersion;*/ - } - else if ( !stricmp( pArg, "PLAIN" ) ) - { - bXml |= INCHI_OUT_PLAIN_TEXT; - bXml &= ~INCHI_OUT_XML; - } - else if ( !stricmp( pArg, "ANNPLAIN" ) ) - { - bXml |= INCHI_OUT_PLAIN_TEXT_COMMENTS; - bXml &= ~INCHI_OUT_XML_TEXT_COMMENTS; - } - else if ( !stricmp( pArg, "ANNXML" ) ) - { - bXml |= INCHI_OUT_XML_TEXT_COMMENTS; - bXml &= ~INCHI_OUT_PLAIN_TEXT_COMMENTS; - } - else if ( !memicmp( pArg, "AUXINFO:", 8 ) && isdigit(UCINT pArg[8]) ) - { - k = strtol(pArg+8, NULL, 10); - if ( k == 0 ) - { - bINChIOutputOptions |= INCHI_OUT_NO_AUX_INFO; /* no aux info */ - bINChIOutputOptions &= ~INCHI_OUT_SHORT_AUX_INFO; - } - else if ( k == 1 ) - { - bINChIOutputOptions &= ~(INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO); /* include full aux info */ - } - else if ( k == 2 ) - { - bINChIOutputOptions &= ~INCHI_OUT_NO_AUX_INFO; /* include short aux info */ - bINChIOutputOptions |= INCHI_OUT_SHORT_AUX_INFO; - } - else - { - bINChIOutputOptions = k; /* override everything */ - } - } - else if ( !stricmp( pArg, "MERGE" ) ) - { - bMergeAllInputStructures = 1; - } - - else if ( !stricmp( pArg, "PGO" ) ) - { - ip->bSaveAllGoodStructsAsProblem = 1; - } - else if ( !stricmp( pArg, "DCR" ) ) - { - bDisplayCompositeResults = 1; - } - - else if ( !stricmp( pArg, "DSB" ) ) - { - nMode |= REQ_MODE_NO_ALT_SBONDS; - } - else if ( !stricmp( pArg, "NOHDR" ) ) - { - bNoStructLabels = 1; - } - - else if ( !stricmp( pArg, "NoVarH" ) ) - { - bTgFlagVariableProtons = 0; - } -#endif /* BUILD_WITH_ENG_OPTIONS */ - - - /*--- All modes (std and non-std InChI) structure perception options ---*/ - else if ( !stricmp( pArg, "SNON" ) ) - { - bVer1DefaultMode &= ~REQ_MODE_STEREO; - nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); - } - else if ( !stricmp( pArg, "NEWPSOFF" ) ) - { - bPointedEdgeStereo = 0; - } - else if ( !stricmp( pArg, "DONOTADDH" ) ) - { - bDoNotAddH = 1; - } - - /* Non-standard */ -#ifndef USE_STDINCHI_API - - /* Non-std InChI structure perception options */ - else if ( !stricmp( pArg, "SREL" ) ) - { - if ( nMode & REQ_MODE_RACEMIC_STEREO ) - { - nMode ^= REQ_MODE_RACEMIC_STEREO; - } - if ( nMode & REQ_MODE_CHIR_FLG_STEREO ) - { - nMode ^= REQ_MODE_CHIR_FLG_STEREO; - } - nMode |= REQ_MODE_RELATIVE_STEREO; - nMode |= REQ_MODE_STEREO; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "SRAC" ) ) - { - if ( nMode & REQ_MODE_RELATIVE_STEREO ) - { - nMode ^= REQ_MODE_RELATIVE_STEREO; - } - if ( nMode & REQ_MODE_CHIR_FLG_STEREO ) - { - nMode ^= REQ_MODE_CHIR_FLG_STEREO; - } - nMode |= REQ_MODE_RACEMIC_STEREO; - nMode |= REQ_MODE_STEREO; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "SUCF" ) ) - { - if ( nMode & REQ_MODE_RELATIVE_STEREO ) - { - nMode ^= REQ_MODE_RELATIVE_STEREO; - } - if ( nMode & REQ_MODE_RACEMIC_STEREO ) - { - nMode ^= REQ_MODE_RACEMIC_STEREO; - } - nMode |= REQ_MODE_CHIR_FLG_STEREO; /* stereo defined by the Chiral flag */ - nMode |= REQ_MODE_STEREO; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "ChiralFlagON" ) ) - { - /* used only with /SUCF */ - /* NB: do not toggle off bStdFormat! (if necessary SUCF will do) */ - bForcedChiralFlag &= ~FLAG_SET_INP_AT_NONCHIRAL; - bForcedChiralFlag |= FLAG_SET_INP_AT_CHIRAL; - } - else if ( !stricmp( pArg, "ChiralFlagOFF" ) ) - { - /* used only with /SUCF */ - /* NB: do not toggle off bStdFormat! (if necessary SUCF will do) */ - bForcedChiralFlag &= ~FLAG_SET_INP_AT_CHIRAL; - bForcedChiralFlag |= FLAG_SET_INP_AT_NONCHIRAL; - } - - /*--- Non-std InChI creation options ---*/ - - else if ( !stricmp( pArg, "SUU" ) ) - { - /* include omitted undef/unknown stereo */ - bVer1DefaultMode &= ~(REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU); - bStdFormat = 0; - } - else if ( !stricmp( pArg, "SLUUD" ) ) - { - /* make labels for unknown and undefined stereo different */ - bVer1DefaultMode |= REQ_MODE_DIFF_UU_STEREO; - bStdFormat = 0; - } - /* FixedH */ - else if ( !stricmp( pArg, "FIXEDH" ) ) - { - bVer1DefaultMode |= REQ_MODE_BASIC; /* tautomeric */ - bStdFormat = 0; - } - /* RecMet */ - else if ( !stricmp( pArg, "RECMET" ) ) - { - /* reconnect metals */ - bReconnectCoord = 1; - bStdFormat = 0; - } -#if ( KETO_ENOL_TAUT == 1 ) - else if ( !stricmp( pArg, "KET" ) ) - { - bKetoEnolTaut = 1; - bStdFormat = 0; - } -#endif -#if ( TAUT_15_NON_RING == 1 ) - else if ( !stricmp( pArg, "15T" ) ) - { - b15TautNonRing = 1; - bStdFormat = 0; - } -#endif - -#endif - /*--- Generation options ---*/ - - /* InChIKey/InChI hash */ - else if ( !stricmp( pArg, "Key" ) ) - { - bHashKey = 1; - } - else if ( !stricmp( pArg, "XHash1" ) ) - { - bHashXtra1 = 1; - } - else if ( !stricmp( pArg, "XHash2" ) ) - { - bHashXtra2 = 1; - } - - - /*--- (engineering) Undo bug/draw fixes options ---*/ -#ifdef BUILD_WITH_ENG_OPTIONS - else if ( !stricmp( pArg, "FixSp3bugOFF" ) ) - { - bFixSp3bug = 0; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "FBOFF" ) ) - { - bFixSp3bug = 0; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "FB2OFF" ) ) - { - bFixFB2 = 0; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "SPXYZOFF" ) ) - { - bAddPhosphineStereo = 0; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "SASXYZOFF" ) ) - { - bAddArsineStereo = 0; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "FNUDOFF" ) ) - { - ip->bFixNonUniformDraw = 0; - bStdFormat = 0; - } - - /*--- (hidden) Old structure-perception and InChI creation options ---*/ - /*--- (engineering) Old structure-perception and InChI creation options ---*/ - - else if ( !stricmp( pArg, "NOUUSB" ) ) - { - nMode |= REQ_MODE_SB_IGN_ALL_UU; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "NOUUSC" ) ) - { - nMode |= REQ_MODE_SC_IGN_ALL_UU; - bStdFormat = 0; - } - -#if ( FIX_ADJ_RAD == 1 ) - else if ( !stricmp( pArg, "FixRad" ) ) - { - bFixAdjacentRad = 1; - bStdFormat = 0; - } -#endif -#if ( UNDERIVATIZE == 1 ) - else if ( !stricmp( pArg, "DoDRV" ) ) - { - ip->bUnderivatize = 1; - bStdFormat = 0; - } -#endif -#if ( RING2CHAIN == 1 ) - else if ( !stricmp( pArg, "DoR2C" ) ) - { - ip->bRing2Chain = 1; - bStdFormat = 0; - } -#endif -#if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) - else if ( !stricmp( pArg, "DoneOnly" ) ) - { - ip->bIngnoreUnchanged = 1; - bStdFormat = 0; - } -#endif - else if ( !stricmp( pArg, "NoADP" ) ) - { - bTgFlagHardAddRenProtons = 0; - bStdFormat = 0; - } - else if ( !memicmp( pArg, "DISCONSALT:", 11 ) ) - { - bDisconnectSalts = (0 != strtol(pArg+11, NULL, 10)); - bStdFormat = 0; - } - else if ( !memicmp( pArg, "DISCONMETAL:", 12 ) ) - { - bDisconnectCoord = (0 != strtol(pArg+12, NULL, 10)); - bStdFormat = 0; - } - else if ( !memicmp( pArg, "RECONMETAL:", 11 ) ) - { - bReconnectCoord = (0 != strtol(pArg+11, NULL, 10)); - bStdFormat = 0; - } - else if ( !memicmp( pArg, "DISCONMETALCHKVAL:", 18 ) ) - { - bDisconnectCoordChkVal = (0 != strtol(pArg+18, NULL, 10)); - bStdFormat = 0; - } - else if ( !memicmp( pArg, "MOVEPOS:", 8 ) ) - { - bMovePositiveCharges = (0 != strtol(pArg+8, NULL, 10)); - bStdFormat = 0; - } - else if ( !memicmp( pArg, "MERGESALTTG:", 12 ) ) - { - bMergeSaltTGroups = (0 != strtol(pArg+12, NULL, 10)); - bStdFormat = 0; - } - else if ( !memicmp( pArg, "UNCHARGEDACIDS:", 15) ) - { - bUnchargedAcidTaut = (0 != strtol(pArg+15, NULL, 16)); - bStdFormat = 0; - } - else if ( !memicmp( pArg, "ACIDTAUT:", 9 ) ) - { - bAcidTautomerism = c = (int)strtol(pArg+9, NULL, 10); - if ( 0 <= c && c <= 2 ) bAcidTautomerism = c; - /*else bNotRecognized = 2*bReleaseVersion;*/ - bStdFormat = 0; - } - - /*--- (hidden) Old output and other options ---*/ - - else if ( !memicmp( pArg, "O:", 2 ) ) - { - bNameSuffix = 1; - strncpy(szNameSuffix, pArg+2, sizeof(szNameSuffix)-1); - } - else if ( !memicmp( pArg, "OP:", 3 ) ) - { - bOutputPath = 1; - strncpy(szOutputPath, pArg+3, sizeof(szOutputPath)-1); - } - else if ( !stricmp( pArg, "ALT" ) ) - { - ip->bAbcNumbers = 1; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "SCT" ) ) - { - ip->bCtPredecessors = 1; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "CMP" ) ) - { - bCompareComponents = CMP_COMPONENTS; - } - else if ( !stricmp( pArg, "CMPNONISO" ) ) - { - bCompareComponents = CMP_COMPONENTS | CMP_COMPONENTS_NONISO; - } - else if ( !stricmp( pArg, "PW" ) ) - { - ip->bSaveWarningStructsAsProblem = 1; - } - -#endif /* BUILD_WITH_ENG_OPTIONS */ - - - else - { - /*for ( k = 0; c=pArg[k]; k ++ )*/ - k = 0; - c=pArg[k]; /* prohibit multiple option concatenations, strict syntax check 2008-11-05 DT */ - { - c = toupper( c ); - switch ( c ) - { - case 'D': - bDisplay |= 1; - if ( (pArg[k+1] == 'C' || pArg[k+1] == 'c') && !pArg[k+2] ) - { - bDisplay |= 1; - k++; - ip->bDisplayEachComponentINChI = 1; - } - else if ( !pArg[k+1] ) - { - bDisplay |= 1; - } - break; - case 'W': - if ( pArg[k+1] == 'D' ) - { - /* restore Display Time functionality */ - c = 'D'; - k ++; - } - t = strtod( pArg+k+1, (char**)&q ); /* cast deliberately discards 'const' qualifier */ - if ( q > pArg+k+1 && errno == ERANGE || t < 0.0 || t*1000.0 > (double)ULONG_MAX) - { - ul = 0; - } - else - { - ul = (unsigned long)(t*1000.0); - } - if ( /*q > pArg+k &&*/ !*q ) - { - k = q - pArg - 1; /* k will be incremented by the for() cycle */ - switch( c ) - { - case 'D': - *ulDisplTime = ul; - break; - case 'W': - ip->msec_MaxTime = ul; - break; - } - } - break; - case 'F': - c = (int)strtol( pArg+k+1, (char**)&q, 10 ); /* cast deliberately discards 'const' qualifier */ - if ( q > pArg+k && !*q ) - { - k = q - pArg - 1; - if ( abs(c) > 5 ) - { - nFontSize = -c; /* font size 5 or less is too small */ - } - } - break; - default: - if ( !pArg[k+1] ) - { - switch ( c ) - { - case 'B': - nMode |= REQ_MODE_BASIC; - nReleaseMode = 0; - /*bNotRecognized = bReleaseVersion;*/ - bStdFormat = 0; - break; - case 'T': - nMode |= REQ_MODE_TAUT; - nReleaseMode = 0; - /*bNotRecognized = bReleaseVersion;*/ - break; - case 'I': - nMode |= REQ_MODE_ISO; - nReleaseMode = 0; - /*bNotRecognized = bReleaseVersion;*/ - break; - case 'N': - nMode |= REQ_MODE_NON_ISO; - nReleaseMode = 0; - bStdFormat = 0; - /*bNotRecognized = bReleaseVersion;*/ - break; - case 'S': - nMode |= REQ_MODE_STEREO; - nReleaseMode = 0; - /*bNotRecognized = bReleaseVersion;*/ - break; - case 'E': - if ( nReleaseMode & REQ_MODE_STEREO ) - { - nReleaseMode ^= REQ_MODE_STEREO; - bStdFormat = 0; - } - break; -#ifndef TARGET_LIB_FOR_WINCHI - default: - inchi_ios_eprint(log_file, "Unrecognized option: \"%c\".\n", c); - -#endif - } - } - - -#ifndef TARGET_LIB_FOR_WINCHI - else - { - inchi_ios_eprint(log_file, "Unrecognized option: \"%c\".\n", c); - } -#endif - } - /* - if ( bNotRecognized && bNotRecognized == bReleaseVersion ) { - inchi_ios_eprint(stderr, "Unrecognized option: \"%c\".\n", c); - bNotRecognized = 0; - } - */ - } - } - - - /* - if ( bNotRecognized && bNotRecognized == 2*bReleaseVersion ) - { - inchi_ios_eprint(stderr, "Unrecognized option: \"%s\".\n", argv[i]); - bNotRecognized = 0; - } - */ - - } - /*=== end of parsing TARGET_LIB_FOR_WINCHI GUI (and v. 0.9xx Beta)options ===*/ - - - - - else if ( (bVer1Options & 1) && INCHI_OPTION_PREFX == argv[i][0] && argv[i][1] ) - { - /*=== parsing stand-alone/library InChI options ===*/ - - - pArg = argv[i] + 1; - - bRecognizedOption = 2; - bVer1Options += 2; - /* always on: REQ_MODE_TAUT | REQ_MODE_ISO | REQ_MODE_STEREO */ -#ifdef CML_DEBUG - printf ("1 argv %d %s\n", i, argv[i]); -#endif - - - /*--- Input options ---*/ - if ( !stricmp( pArg, "STDIO" ) ) - { - bNameSuffix = 0; - } - else if ( !stricmp( pArg, "INPAUX" )) - { - if (INPUT_NONE == ip->nInputType) - ip->nInputType = INPUT_INCHI_PLAIN; - } - else if ( /* INPUT_NONE == ip->nInputType &&*/ - !memicmp( pArg, "SDF:", 4 ) ) - { - /* SDfile label */ - k = 0; - mystrncpy( ip->szSdfDataHeader, pArg+4, MAX_SDF_HEADER+1 ); - LtrimRtrim( ip->szSdfDataHeader, &k ); - if ( k ) - { - ip->pSdfLabel = ip->szSdfDataHeader; - ip->pSdfValue = szSdfDataValue; - if ( INPUT_NONE == ip->nInputType ) - { - ip->nInputType = INPUT_SDFILE; - } - } - else - { - ip->pSdfLabel = NULL; - ip->pSdfValue = NULL; - if ( INPUT_NONE == ip->nInputType ) - { - ip->nInputType = INPUT_MOLFILE; - } - } - } - -#if ( ADD_CMLPP == 1 ) - else if ( INPUT_NONE == ip->nInputType && !stricmp( pArg, "CML" ) ) - { - /* CMLfile label */ - ip->nInputType = INPUT_CMLFILE; - } -#endif - else if ( !memicmp( pArg, "START:", 6 ) ) - { - ip->first_struct_number = strtol(pArg+6, NULL, 10); - } - else if ( !memicmp( pArg, "END:", 4 ) ) - { - ip->last_struct_number = strtol(pArg+4, NULL, 10); - } -#ifdef BUILD_WITH_ENG_OPTIONS - else if ( !memicmp( pArg, "RSB:", 4 )) - { - mdbr = (int)strtol(pArg+4, NULL, 10); - } -#endif - - - /*--- Output options ---*/ -#if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) ) - else if ( !stricmp( pArg, "Tabbed" ) ) - { - bXml |= INCHI_OUT_TABBED_OUTPUT; - } -#endif - else if ( !stricmp( pArg, "NOLABELS" ) ) - { - bNoStructLabels = 1; - } - else if ( !stricmp( pArg, "SAVEOPT" ) ) - { - bINChIOutputOptions |= INCHI_OUT_SAVEOPT; - } - else if ( !stricmp( pArg, "AUXNONE" ) ) - { - /* no aux. info */ - bINChIOutputOptions |= INCHI_OUT_NO_AUX_INFO; /* no aux info */ - bINChIOutputOptions &= ~INCHI_OUT_SHORT_AUX_INFO; - } - else if ( !stricmp( pArg, "OUTPUTSDF" ) ) - { - /* output SDfile */ - bOutputMolfileOnly = 1; - } - else if ( !stricmp( pArg, "SdfAtomsDT" ) ) - { - /* output isotopes H as D and T in SDfile */ - bOutputMolfileDT = 1; - } - else if ( !stricmp( pArg, "D" ) ) - { - /* display the structures */ - bDisplay |= 1; - } - else if ( !memicmp( pArg, "F", 1 ) && (c = (int)strtol( pArg+1, (char**)&q, 10 ), q > pArg+1) ) - { - nFontSize = -c; /* struct. display font size */ - } - else if ( !stricmp( pArg, "EQU" ) ) - { - bCompareComponents = CMP_COMPONENTS; - } - - - /*--- All modes (std and non-std InChI) structure perception options ---*/ - else if ( !stricmp( pArg, "SNON" ) ) - { - bVer1DefaultMode &= ~REQ_MODE_STEREO; /* no stereo */ - nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); - } - else if ( !stricmp( pArg, "NEWPSOFF" ) ) - { - bPointedEdgeStereo = 0; - } - else if ( !stricmp( pArg, "DONOTADDH" ) ) - { - bDoNotAddH = 1; - } - - - /* Non-standard */ -#ifndef USE_STDINCHI_API - - /* Non-std InChI structure perception options */ - - else if ( !stricmp( pArg, "SREL" ) ) - { - bVer1DefaultMode |= REQ_MODE_STEREO; /* relative stereo */ - nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_CHIR_FLG_STEREO); - nMode |= REQ_MODE_RELATIVE_STEREO; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "SRAC" ) ) - { - /* REQ_MODE_CHIR_FLG_STEREO */ - bVer1DefaultMode |= REQ_MODE_STEREO; /* racemic stereo */ - nMode &= ~(REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); - nMode |= REQ_MODE_RACEMIC_STEREO; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "SUCF" ) ) - { - bVer1DefaultMode |= REQ_MODE_STEREO; /* stereo defined by the Chiral flag */ - nMode &= ~(REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO); - nMode |= REQ_MODE_CHIR_FLG_STEREO; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "ChiralFlagON" ) ) - { - /* used only with /SUCF */ - /* NB: do not toggle off bStdFormat! (if necessary SUCF will do) */ - bForcedChiralFlag &= ~FLAG_SET_INP_AT_NONCHIRAL; - bForcedChiralFlag |= FLAG_SET_INP_AT_CHIRAL; - } - else if ( !stricmp( pArg, "ChiralFlagOFF" ) ) - { - /* used only with /SUCF */ - /* NB: do not toggle off bStdFormat! (if necessary SUCF will do) */ - bForcedChiralFlag &= ~FLAG_SET_INP_AT_CHIRAL; - bForcedChiralFlag |= FLAG_SET_INP_AT_NONCHIRAL; - } - - - /*--- Non-std InChI creation options ---*/ - - /* Stereo */ - else if ( !stricmp( pArg, "SUU" ) ) - { - /* include omitted undef/unknown stereo */ - bVer1DefaultMode &= ~(REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU); - bStdFormat = 0; - } - else if ( !stricmp( pArg, "SLUUD" ) ) - { - /* Make labels for unknown and undefined stereo different */ - bVer1DefaultMode |= REQ_MODE_DIFF_UU_STEREO; - bStdFormat = 0; - } - /* FixedH */ - else if ( !stricmp( pArg, "FIXEDH" ) ) - { - bVer1DefaultMode |= REQ_MODE_BASIC; /* tautomeric */ - bStdFormat = 0; - } - /* RecMet */ - else if ( !stricmp( pArg, "RECMET" ) ) - { - /* reconnect metals */ - bReconnectCoord = 1; - bStdFormat = 0; - } -#if ( KETO_ENOL_TAUT == 1 ) - else if ( !stricmp( pArg, "KET" ) ) - { - bKetoEnolTaut = 1; - bStdFormat = 0; - } -#endif -#if ( TAUT_15_NON_RING == 1 ) - else if ( !stricmp( pArg, "15T" ) ) - { - b15TautNonRing = 1; - bStdFormat = 0; - } -#endif - -#endif - - /*--- Generation options ---*/ - else if ( !memicmp( pArg, "W", 1 ) && (t = strtod( pArg+1, (char**)&q ), q > pArg+1) ) - { - if ( errno == ERANGE || t < 0.0 || t*1000.0 > (double)ULONG_MAX) - { - ul = 0; - } - else - { - ul = (unsigned long)(t*1000.0); /* max. time per structure */ - } - ip->msec_MaxTime = ul; - } - else if ( !stricmp( pArg, "WarnOnEmptyStructure" ) ) - { - ip->bAllowEmptyStructure = 1; - } - - /* InChIKey/InChI hash */ - else if ( !stricmp( pArg, "Key" ) ) - { - bHashKey = 1; - } - else if ( !stricmp( pArg, "XHash1" ) ) - { - bHashXtra1 = 1; - } - else if ( !stricmp( pArg, "XHash2" ) ) - { - bHashXtra2 = 1; - } - - - /*--- Conversion modes ---*/ - - -#if ( READ_INCHI_STRING == 1 ) - else if ( !stricmp( pArg, "InChI2InChI" ) ) - { - /* Read InChI Identifiers and output InChI Identifiers */ - ip->nInputType = INPUT_INCHI; - ip->bReadInChIOptions |= READ_INCHI_OUTPUT_INCHI; - ip->bReadInChIOptions &= ~READ_INCHI_TO_STRUCTURE; - } - else if ( !stricmp( pArg, "InChI2Struct" ) ) - { - /* Split InChI Identifiers into components */ - ip->bReadInChIOptions |= READ_INCHI_TO_STRUCTURE; - ip->bReadInChIOptions &= ~READ_INCHI_OUTPUT_INCHI; - ip->nInputType = INPUT_INCHI; - } -#ifdef BUILD_WITH_ENG_OPTIONS - else if ( !stricmp( pArg, "KeepBalanceP" ) ) - { - /* When spliting InChI Identifiers into components: */ - /* If MobileH output then add p to each component; */ - /* Otherwise add one more component containing balance */ - /* of protons and exchangeable isotopic H */ - ip->bReadInChIOptions |= READ_INCHI_KEEP_BALANCE_P; - bStdFormat = 0; - } -#endif -#endif - - - - /*--- (engineering) Undo bug/draw fixes options ---*/ -#ifdef BUILD_WITH_ENG_OPTIONS - else if ( !stricmp( pArg, "FixSp3bugOFF" ) ) - { - bFixSp3bug = 0; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "FBOFF" ) ) - { - bFixSp3bug = 0; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "FB2OFF" ) ) - { - bFixFB2 = 0; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "SPXYZOFF" ) ) - { - bAddPhosphineStereo = 0; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "SASXYZOFF" ) ) - { - bAddArsineStereo = 0; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "FNUDOFF" ) ) - { - ip->bFixNonUniformDraw = 0; - bStdFormat = 0; - } - - /*--- (engineering) Old structure-perception and InChI creation options ---*/ -#if ( FIX_ADJ_RAD == 1 ) - else if ( !stricmp( pArg, "FixRad" ) ) - { - bFixAdjacentRad = 1; - bStdFormat = 0; - } -#endif -#if ( UNDERIVATIZE == 1 ) - else if ( !stricmp( pArg, "DoDRV" ) ) - { - ip->bUnderivatize = 1; - bStdFormat = 0; - } -#endif -#if ( RING2CHAIN == 1 ) - else if ( !stricmp( pArg, "DoR2C" ) ) - { - ip->bRing2Chain = 1; - bStdFormat = 0; - } -#endif -#if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) - else if ( !stricmp( pArg, "DoneOnly" ) ) - { - ip->bIngnoreUnchanged = 1; - bStdFormat = 0; - } -#endif - - else if ( !memicmp( pArg, "MOVEPOS:", 8 ) ) - { - bMovePositiveCharges = (0 != strtol(pArg+8, NULL, 10)); - bStdFormat = 0; - } - - else if ( !stricmp( pArg, "NoADP" ) ) - { - bTgFlagHardAddRenProtons = 0; - bStdFormat = 0; - } - /* Tautomer perception off */ - else if ( !stricmp( pArg, "EXACT" ) ) - { - bVer1DefaultMode |= REQ_MODE_BASIC; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "ONLYRECSALT" ) ) - { - /* do not disconnect salts */ - bDisconnectSalts = 0; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "ONLYEXACT" ) || !stricmp( pArg, "ONLYFIXEDH" ) ) - { - bVer1DefaultMode |= REQ_MODE_BASIC; - bVer1DefaultMode &= ~REQ_MODE_TAUT; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "ONLYNONISO" ) ) - { - bVer1DefaultMode |= REQ_MODE_NON_ISO; - bVer1DefaultMode &= ~REQ_MODE_ISO; - bStdFormat = 0; - } - else if ( !stricmp( pArg, "TAUT" ) ) - { - bVer1DefaultMode &= ~REQ_MODE_BASIC; - bVer1DefaultMode |= REQ_MODE_TAUT; - } - else if ( !stricmp( pArg, "ONLYRECMET" ) ) - { - /* do not disconnect metals */ - bDisconnectCoord = 0; - bStdFormat = 0; - } - - /*--- (hidden) Old output and other options ---*/ - - else if ( !stricmp( pArg, "SdfSplit" ) ) - { - /* Split single Molfiles into disconnected components */ - bOutputMolfileSplit = 1; - } - else if ( !stricmp( pArg, "DCR" ) ) - { - bDisplayCompositeResults = 1; - } - else if ( !stricmp( pArg, "AUXFULL" ) || !stricmp( pArg, "AUXMAX" ) ) - { - /* full aux info */ - bINChIOutputOptions &= ~(INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO); /* include short aux info */ - } - else if ( !stricmp( pArg, "AUXMIN" ) ) - { - /* minimal aux info */ - bINChIOutputOptions &= ~INCHI_OUT_NO_AUX_INFO; /* include short aux info */ - bINChIOutputOptions |= INCHI_OUT_SHORT_AUX_INFO; - } -#if ( READ_INCHI_STRING == 1 ) - else if ( !stricmp( pArg, "DDSRC" ) ) - { - bDisplayIfRestoreWarnings = 1; /* InChI->Structure debugging: Display Debug Structure Restore Components */ - } -#endif - else if ( !stricmp( pArg, "NoVarH" ) ) - { - bTgFlagVariableProtons = 0; - } - else if ( !stricmp( pArg, "FULL" ) ) - { - bVer1DefaultMode = VER103_DEFAULT_MODE; - nMode = 0; - bReconnectCoord = 1; /* full output */ - bINChIOutputOptions = ((EMBED_REC_METALS_INCHI==1)? INCHI_OUT_EMBED_REC : 0) | INCHI_OUT_SHORT_AUX_INFO; - ip->bCtPredecessors = 0; - ip->bAbcNumbers = 0; - bXml |= INCHI_OUT_PLAIN_TEXT | INCHI_OUT_PLAIN_TEXT_COMMENTS; - bXml &= ~(INCHI_OUT_XML | INCHI_OUT_XML_TEXT_COMMENTS); - } - else if ( !stricmp( pArg, "MIN" ) ) - { - bVer1DefaultMode = VER103_DEFAULT_MODE; - nMode = 0; - bReconnectCoord = 1; /* minimal output */ - bINChIOutputOptions = ((EMBED_REC_METALS_INCHI==1)? INCHI_OUT_EMBED_REC : 0) | INCHI_OUT_NO_AUX_INFO; /* minimal compressed output */ - ip->bCtPredecessors = 1; - ip->bAbcNumbers = 1; - bXml |= INCHI_OUT_PLAIN_TEXT | INCHI_OUT_PLAIN_TEXT_COMMENTS; - bXml &= ~(INCHI_OUT_XML | INCHI_OUT_XML_TEXT_COMMENTS); - } - else if ( !stricmp( pArg, "COMPRESS" ) ) - { - ip->bAbcNumbers = 1; - ip->bCtPredecessors = 1; /* compressed output */ - } - -#if ( READ_INCHI_STRING == 1 ) - else if ( !stricmp( pArg, "InChI2InChI" ) ) - { - /* Read InChI Identifiers and output InChI Identifiers */ - ip->nInputType = INPUT_INCHI; - ip->bReadInChIOptions |= READ_INCHI_OUTPUT_INCHI; - ip->bReadInChIOptions &= ~READ_INCHI_TO_STRUCTURE; - } - - else if ( !stricmp( pArg, "SplitInChI" ) ) - { - /* Split InChI Identifiers into components */ - ip->bReadInChIOptions |= READ_INCHI_SPLIT_OUTPUT; - } -#endif - else if ( !stricmp( pArg, "MOLFILENUMBER" ) ) - { - ip->bGetMolfileNumber |= 1; - } - else if ( !stricmp( pArg, "OutputPLAIN" ) ) - { - bXml |= INCHI_OUT_PLAIN_TEXT; - bXml &= ~INCHI_OUT_XML; - } - else if ( !stricmp( pArg, "OutputXML" ) ) - { - bXml |= INCHI_OUT_XML; - bXml &= ~INCHI_OUT_PLAIN_TEXT; - } - else if ( !stricmp( pArg, "OutputANNPLAIN" ) ) - { - bXml |= INCHI_OUT_PLAIN_TEXT_COMMENTS; - bXml &= ~INCHI_OUT_XML_TEXT_COMMENTS; - bXml |= INCHI_OUT_WINCHI_WINDOW; /* debug */ - } - else if ( !stricmp( pArg, "OutputANNXML" ) ) - { - bXml |= INCHI_OUT_XML_TEXT_COMMENTS; - bXml &= ~INCHI_OUT_PLAIN_TEXT_COMMENTS; - } else - if ( !stricmp( pArg, "ONLYEXACT" ) || !stricmp( pArg, "ONLYFIXEDH" ) ) { - bVer1DefaultMode |= REQ_MODE_BASIC; - bVer1DefaultMode &= ~REQ_MODE_TAUT; - } else - if ( !stricmp( pArg, "ONLYNONISO" ) ) { - bVer1DefaultMode |= REQ_MODE_NON_ISO; - bVer1DefaultMode &= ~REQ_MODE_ISO; - } else - if ( !stricmp( pArg, "TAUT" ) ) { - bVer1DefaultMode &= ~REQ_MODE_BASIC; - bVer1DefaultMode |= REQ_MODE_TAUT; - } else - if ( !stricmp( pArg, "ONLYRECMET" ) ) { /* do not disconnect metals */ - bDisconnectCoord = 0; - } else - if ( !stricmp( pArg, "ONLYRECSALT" ) ) { /* do not disconnect salts */ - bDisconnectSalts = 0; - } else - if ( !memicmp( pArg, "MOVEPOS:", 8 ) ) { /* added -- 2010-03-01 DT */ - bMovePositiveCharges = (0 != strtol(pArg+8, NULL, 10)); - } else - if ( !memicmp( pArg, "RSB:", 4 )) { - mdbr = (int)strtol(pArg+4, NULL, 10); - } else - if ( !stricmp( pArg, "EQU" ) ) { - bCompareComponents = CMP_COMPONENTS; - } else - if ( !stricmp( pArg, "EQUNONISO" ) ) - { - bCompareComponents = CMP_COMPONENTS | CMP_COMPONENTS_NONISO; - } - else if ( !memicmp( pArg, "OP:", 3 ) ) - { - bOutputPath = 1; - strncpy(szOutputPath, pArg+3, sizeof(szOutputPath)-1); - } -#endif /* BUILD_WITH_ENG_OPTIONS */ - - - - /* Display unrecognized option */ - else - { - bRecognizedOption = 0; -#ifndef TARGET_LIB_FOR_WINCHI - inchi_ios_eprint(log_file, "Unrecognized option: \"%s\".\n", pArg); -#endif - } - bVer1Options |= bRecognizedOption; - - - } - /*=== end of parsing stand-alone/library InChI options ===*/ - - - - else if ( ip->num_paths < MAX_NUM_PATHS ) - { - char *sz; - if ( argv[i] && argv[i][0] ) - { - if ( sz = (char*) inchi_malloc( (strlen(argv[i]) + 1)*sizeof(sz[0])) ) - { - strcpy( sz, argv[i] ); - } -#ifdef CML_DEBUG - printf ("1 path %d argv %d %s\n", ip -> num_paths, i, argv [i]); -#endif - ip->path[ip->num_paths++] = sz; - } - } - - - - } /* for ( i = 1; i < argc; i ++ ) */ - - - - if ( bHashKey != 0 ) - /* Suppress InChIKey calculation if: - compressed output OR Inchi2Struct OR Inchi2Inchi */ - { - - if ( (ip->bAbcNumbers ==1) && (ip->bCtPredecessors == 1) ) - { -#ifndef TARGET_LIB_FOR_WINCHI - inchi_ios_eprint(log_file, "Terminating: generation of InChIKey is not available with 'Compress' option\n"); - return -1; -#endif - bHashKey = 0; - } - if ( ip->nInputType == INPUT_INCHI ) - { -#ifndef TARGET_LIB_FOR_WINCHI - inchi_ios_eprint(log_file, "Terminating: generation of InChIKey is not available in InChI conversion mode\n"); - return -1; -#endif - bHashKey = 0; - } - else - if ( bOutputMolfileOnly == 1 ) - { -#ifndef TARGET_LIB_FOR_WINCHI - inchi_ios_eprint(log_file, "Terminating: generation of InChIKey is not available with 'OutputSDF' option\n"); - return -1; -#endif - bHashKey = 0; - } - - } - - - if ( bNameSuffix || bOutputPath ) - { - const char *p = NULL; - char *r = NULL; - char *sz; - int len; - const char szNUL[] = "NUL"; /* fix for AMD processor: use const char[] instead of just "NUL" constant 2008-11-5 DT */ - - /* find the 1st path */ - for ( i = 0; i < MAX_NUM_PATHS; i ++ ) - { - if ( !p && ip->path[i] && ip->path[i][0] ) - { - p = ip->path[i]; - break; - } - } - /* fix output path */ - if ( bOutputPath && szOutputPath[0] && p ) - { - /* remove last slash */ - len = strlen(szOutputPath); - if ( len > 0 && szOutputPath[len-1] != INCHI_PATH_DELIM ) - { - szOutputPath[len++] = INCHI_PATH_DELIM; - szOutputPath[len] = '\0'; - } - if ( len > 0 && (r = (char *)strrchr( p, INCHI_PATH_DELIM ) ) && r[1] ) - { - strcat( szOutputPath, r+1 ); - p = szOutputPath; - } - } /* add missing paths */ - for ( i = 0; p && i < MAX_NUM_PATHS; i ++ ) - { - /* fix for AMD processor: changed order 2008-11-5 DT */ - if ( !ip->path[i] || !ip->path[i][0] ) - { - len = strlen( p ) + strlen(szNameSuffix) + strlen( ext[i] ); - if ( sz = (char*) inchi_malloc( (len+1)*sizeof(sz[0]) ) ) - { - strcpy( sz, p ); - strcat( sz, szNameSuffix ); - strcat( sz, ext[i] ); - ip->num_paths++; - ip->path[i] =sz; - } - } else - if ( !stricmp( ip->path[i], szNUL ) ) - { - inchi_free( (char *)ip->path[i] ); /* cast deliberately const qualifier */ - ip->path[i] = NULL; - } - } - } - - - - -#if ( READ_INCHI_STRING == 1 ) - if ( INPUT_INCHI == ip->nInputType ) - { - bCompareComponents = 0; - /*bDisplayCompositeResults = 0;*/ -#if ( I2S_MODIFY_OUTPUT == 1 ) - if ( !(ip->bReadInChIOptions & READ_INCHI_TO_STRUCTURE ) ) -#endif - { - bOutputMolfileOnly = 0; - /*bNoStructLabels = 1;*/ - bINChIOutputOptions |= INCHI_OUT_NO_AUX_INFO; - bINChIOutputOptions &= ~INCHI_OUT_SHORT_AUX_INFO; - bINChIOutputOptions &= ~INCHI_OUT_ONLY_AUX_INFO; - } - ip->bDisplayIfRestoreWarnings = bDisplayIfRestoreWarnings; - if ( !(bINChIOutputOptions & - - (INCHI_OUT_SDFILE_ONLY | /* not in bINChIOutputOptions yet */ - INCHI_OUT_XML | /* not in bINChIOutputOptions yet */ - INCHI_OUT_PLAIN_TEXT | /* not in bINChIOutputOptions yet */ - INCHI_OUT_PLAIN_TEXT_COMMENTS | /* not in bINChIOutputOptions yet */ - INCHI_OUT_XML_TEXT_COMMENTS /* not in bINChIOutputOptions yet */ - ) ) -#if ( I2S_MODIFY_OUTPUT == 1 ) - && !bOutputMolfileOnly - && !(bXml & (INCHI_OUT_XML | INCHI_OUT_XML_TEXT_COMMENTS | INCHI_OUT_PLAIN_TEXT | INCHI_OUT_XML_TEXT_COMMENTS)) -#endif - ) { - bINChIOutputOptions |= INCHI_OUT_PLAIN_TEXT; - } - } -#endif - - - if ( bVer1Options ) - { - nMode |= bVer1DefaultMode; - } - else if ( bReleaseVersion ) - { - nMode |= nReleaseMode; - } - -#if ( defined(COMPILE_ANSI_ONLY) || defined(TARGET_LIB_FOR_WINCHI) ) - if ( bCompareComponents && !(bDisplay & 1) ) { - bCompareComponents = 0; - } -#endif - /* Save original options */ - /* nOrigMode = nMode; */ -#ifndef COMPILE_ANSI_ONLY - ip->dp.sdp.nFontSize = nFontSize; - ip->dp.sdp.ulDisplTime = *ulDisplTime; - ip->bDisplay = bDisplay; -#ifdef TARGET_LIB_FOR_WINCHI - ip->bDisplayCompositeResults = bDisplay; -#else - ip->bDisplayCompositeResults = bDisplayCompositeResults; -#endif -#else - ip->bDisplayEachComponentINChI = 0; - bCompareComponents = 0; -#endif - ip->bMergeAllInputStructures = bMergeAllInputStructures; - ip->bDoNotAddH = bDoNotAddH; - /* set default options */ - if ( !nMode || nMode == REQ_MODE_STEREO ) { - /* requested all output */ - nMode |= (REQ_MODE_BASIC | REQ_MODE_TAUT | REQ_MODE_ISO | REQ_MODE_NON_ISO | REQ_MODE_STEREO); - } else { - if ( !(nMode & (REQ_MODE_BASIC | REQ_MODE_TAUT)) ) { - nMode |= (REQ_MODE_BASIC | REQ_MODE_TAUT); - } - if ( (nMode & REQ_MODE_STEREO) && !(nMode & (REQ_MODE_ISO | REQ_MODE_NON_ISO)) ) { - nMode |= (REQ_MODE_ISO | REQ_MODE_NON_ISO); - } - } - /* if the user requested isotopic then unconditionally add non-isotopic output. */ - if ( nMode & REQ_MODE_ISO ) { - nMode |= REQ_MODE_NON_ISO; - } -#if ( MIN_SB_RING_SIZE > 0 ) - if ( mdbr ) { - nMinDbRinSize = mdbr; - } - nMode |= (nMinDbRinSize << REQ_MODE_MIN_SB_RING_SHFT) & REQ_MODE_MIN_SB_RING_MASK; -#endif - /* input file */ - if ( ip->nInputType == INPUT_NONE && ip->num_paths > 0 ) { - ip->nInputType = INPUT_MOLFILE; /* default */ -#if ( ADD_CMLPP == 1 ) - { - const char *p; - if ( ip->path[0] && ( p = strrchr(ip->path[0], '.' ) ) && - !stricmp( p, ".cml") ) { - ip->nInputType = INPUT_CMLFILE; - } - } -#endif - } - ip->nMode = nMode; - if ( (bCompareComponents & CMP_COMPONENTS) && (nMode & REQ_MODE_BASIC) ) { - bCompareComponents |= CMP_COMPONENTS_NONTAUT; /* compare non-tautomeric */ - } - ip->bCompareComponents = bCompareComponents; - - ip->bINChIOutputOptions = bINChIOutputOptions | (bOutputMolfileOnly? INCHI_OUT_SDFILE_ONLY : 0); - if ( bOutputMolfileOnly ) { - bXml &= ~(INCHI_OUT_XML | INCHI_OUT_PLAIN_TEXT | - INCHI_OUT_PLAIN_TEXT_COMMENTS | INCHI_OUT_XML_TEXT_COMMENTS | INCHI_OUT_TABBED_OUTPUT); -#if ( SDF_OUTPUT_DT == 1 ) - ip->bINChIOutputOptions |= bOutputMolfileDT? INCHI_OUT_SDFILE_ATOMS_DT : 0; - ip->bINChIOutputOptions |= bOutputMolfileSplit? INCHI_OUT_SDFILE_SPLIT : 0; -#endif - } - if ( bXml & INCHI_OUT_XML ) { - bXml &= ~(INCHI_OUT_PLAIN_TEXT | INCHI_OUT_XML_TEXT_COMMENTS | INCHI_OUT_TABBED_OUTPUT); - } -#ifdef TARGET_LIB_FOR_WINCHI - if ( !(bDisplay & 1) ) { - bXml &= ~(INCHI_OUT_PLAIN_TEXT_COMMENTS | INCHI_OUT_XML_TEXT_COMMENTS); /* do not ouput comments in wINChI text file results */ - } else { - bXml |= INCHI_OUT_WINCHI_WINDOW; - } -#endif - ip->bINChIOutputOptions |= bXml; - ip->bNoStructLabels = bNoStructLabels; - - if ( bForcedChiralFlag ) { - ip->bChiralFlag = bForcedChiralFlag; - } - - /*******************************************/ - /* tautomeric/salts settings */ - /*******************************************/ - - ip->bTautFlags = 0; /* initialize */ - ip->bTautFlagsDone = 0; /* initialize */ - - /* find regular tautomerism */ - ip->bTautFlags |= TG_FLAG_TEST_TAUT__ATOMS; - /* disconnect salts */ - ip->bTautFlags |= bDisconnectSalts? TG_FLAG_DISCONNECT_SALTS : 0; - /* if possible find long-range H/(-) taut. on =C-OH, >C=O */ - ip->bTautFlags |= bAcidTautomerism? TG_FLAG_TEST_TAUT__SALTS : 0; - /* allow long-range movement of N(+), P(+) charges */ - ip->bTautFlags |= bMovePositiveCharges? TG_FLAG_MOVE_POS_CHARGES : 0; - /* multi-attachement long-range H/(-) taut. on =C-OH, >C=O */ - ip->bTautFlags |= (bAcidTautomerism > 1)? TG_FLAG_TEST_TAUT2_SALTS : 0; - /* (debug) allow to find long-range H-only tautomerism on =C-OH, >C=O */ - ip->bTautFlags |= (bUnchargedAcidTaut==1)? TG_FLAG_ALLOW_NO_NEGTV_O : 0; - /* merge =C-OH and >C=O containing t-groups and other =C-OH groups */ - ip->bTautFlags |= bMergeSaltTGroups? TG_FLAG_MERGE_TAUT_SALTS : 0; - ip->bTautFlags |= bDisconnectCoord? TG_FLAG_DISCONNECT_COORD : 0; - ip->bTautFlags |=(bDisconnectCoord && - bReconnectCoord)? TG_FLAG_RECONNECT_COORD : 0; - ip->bTautFlags |= bDisconnectCoordChkVal? TG_FLAG_CHECK_VALENCE_COORD : 0; - ip->bTautFlags |= bTgFlagVariableProtons? TG_FLAG_VARIABLE_PROTONS : 0; - ip->bTautFlags |= bTgFlagHardAddRenProtons? TG_FLAG_HARD_ADD_REM_PROTONS : 0; - ip->bTautFlags |= bKetoEnolTaut? TG_FLAG_KETO_ENOL_TAUT : 0; - ip->bTautFlags |= b15TautNonRing? TG_FLAG_1_5_TAUT : 0; - -#ifdef STEREO_WEDGE_ONLY - ip->bTautFlags |= bPointedEdgeStereo? TG_FLAG_POINTED_EDGE_STEREO : 0; -#endif -#if ( FIX_ADJ_RAD == 1 ) - ip->bTautFlags |= bFixAdjacentRad? TG_FLAG_FIX_ADJ_RADICALS : 0; -#endif - ip->bTautFlags |= bAddPhosphineStereo? TG_FLAG_PHOSPHINE_STEREO : 0; - ip->bTautFlags |= bAddArsineStereo? TG_FLAG_ARSINE_STEREO : 0; - ip->bTautFlags |= bFixSp3bug? TG_FLAG_FIX_SP3_BUG : 0; - - - if (bFixFB2) - { -#if ( FIX_ISO_FIXEDH_BUG == 1 ) - ip->bTautFlags |= TG_FLAG_FIX_ISO_FIXEDH_BUG; /* accomodate FIX_ISO_FIXEDH_BUG */ -#endif -#if ( FIX_TERM_H_CHRG_BUG == 1 ) - ip->bTautFlags |= TG_FLAG_FIX_TERM_H_CHRG_BUG; /* accomodate FIX_TERM_H_CHRG_BUG */ -#endif -#if ( FIX_TRANSPOSITION_CHARGE_BUG == 1 ) - ip->bINChIOutputOptions |= INCHI_OUT_FIX_TRANSPOSITION_CHARGE_BUG; -#endif - } - - - - - if ( !ip->nInputType ) - ip->nInputType = INPUT_MOLFILE; - - /* Check if /SNon requested turn OFF SUU/SLUUD */ - if ( ! (ip->nMode & REQ_MODE_STEREO) ) - { - ip->nMode &= ~REQ_MODE_DIFF_UU_STEREO; - ip->nMode &= ~(REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU); - } - - - /* Standard InChI ? */ - if (bStdFormat) - { - ip->bINChIOutputOptions |= INCHI_OUT_STDINCHI; - } - - /* InChIKey ? */ - if ( !bHashKey ) - ip->bCalcInChIHash = INCHIHASH_NONE; - else - ip->bCalcInChIHash = INCHIHASH_KEY; - - /* Extension(s) to hash (in non-std mode only) ? */ - if ( !bHashKey ) - { - if ( (bHashXtra1!=0) || (bHashXtra2!=0) ) - inchi_ios_eprint(log_file, - "Hash extension(s) not generated: InChIKey not requested"); - } - else - { - if ( bHashXtra1 ) - { - if ( bHashXtra2 ) ip->bCalcInChIHash = INCHIHASH_KEY_XTRA1_XTRA2; - else ip->bCalcInChIHash = INCHIHASH_KEY_XTRA1; - } - else if ( bHashXtra2 ) - { - ip->bCalcInChIHash = INCHIHASH_KEY_XTRA2; - } - } - - - return 0; -} - - - -/*******************************************************************/ -int PrintInputParms( INCHI_IOSTREAM *log_file, INPUT_PARMS *ip ) -{ -INCHI_MODE nMode = ip->nMode; -int k; -int bStdFormat = 1; -int first=1; - - -#ifdef TARGET_LIB_FOR_WINCHI - int bInChI2Struct = 0; /* as of December 2008, winchi-1 does not convert InChI to structure */ -#else - int bInChI2Struct = (ip->bReadInChIOptions & READ_INCHI_TO_STRUCTURE) && ip->nInputType == INPUT_INCHI; -#endif - - if ( !(ip->bINChIOutputOptions & INCHI_OUT_STDINCHI) ) - bStdFormat = 0; - - - /* some stereo */ - if ( ! (nMode & REQ_MODE_STEREO) ) - { - inchi_ios_eprint( log_file, "Using specific structure perception features:\n"); - first = 0; - inchi_ios_eprint( log_file, " Stereo OFF\n"); - } - else - { - if ( ! (TG_FLAG_POINTED_EDGE_STEREO & ip->bTautFlags) ) - { - if (first) - { - inchi_ios_eprint( log_file, "Using specific structure perception features:\n"); - first = 0; - } - inchi_ios_eprint( log_file, " Both ends of wedge point to stereocenters\n"); - } - } - if ( ip->bDoNotAddH ) - { - if (first) - inchi_ios_eprint( log_file, "Using specific structure perception features:\n"); - inchi_ios_eprint( log_file, " Do not add H\n"); - } - - - /* Generation/conversion indicator */ - if (bStdFormat) - { - if ( !(ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY) && !bInChI2Struct ) - inchi_ios_eprint( log_file, "Generating standard InChI\n" ); - -#if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) && !defined(TARGET_EXE_USING_API) ) - /* effective only in command line program InChI or stdInChI */ - else if ( bInChI2Struct ) - inchi_ios_eprint( log_file, "Converting InChI(s) to structure(s) in %s\n", - (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY)? - "MOL format" : "aux. info format" ); -#endif - } - else - inchi_ios_eprint( log_file, "Generating non-standard InChI with the options: \n" ); - - - /* SDfile output */ - - if ( ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY ) - { - inchi_ios_eprint( log_file, - "Output SDfile only without stereochemical information and atom coordinates%s\n", - (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ATOMS_DT)? - "\n(write H isotopes as D, T)":"" ); - } - - /* Fixed/Mobile H */ - if (!bStdFormat) - { - if( (nMode & (REQ_MODE_BASIC | REQ_MODE_TAUT )) == (REQ_MODE_BASIC | REQ_MODE_TAUT) ) - inchi_ios_eprint( log_file, " Mobile H Perception OFF (include FixedH layer)\n" ); - else if( (nMode & (REQ_MODE_BASIC | REQ_MODE_TAUT )) == (REQ_MODE_TAUT) ) - inchi_ios_eprint( log_file, " Mobile H Perception ON (omit FixedH layer)\n" ); - else if( (nMode & (REQ_MODE_BASIC | REQ_MODE_TAUT )) == (REQ_MODE_BASIC) ) - inchi_ios_eprint( log_file, " Mobile H ignored\n" ); - else - inchi_ios_eprint( log_file, " Undefined Mobile H mode\n" ); - - if ( (ip->bTautFlags & TG_FLAG_VARIABLE_PROTONS) ) - if ( !(ip->bTautFlags & TG_FLAG_HARD_ADD_REM_PROTONS) ) - inchi_ios_eprint( log_file, " Disabled Aggressive (De)protonation\n" ); - -#if ( FIND_RING_SYSTEMS != 1 ) - inchi_ios_eprint( log_file, " %s5-, 6-, 7-memb. ring taut. ignored\n", i?"; ":""); -#endif - - /* RecMet */ - if ( ip->bTautFlags & TG_FLAG_DISCONNECT_COORD ) - { - if ( ip->bTautFlags & TG_FLAG_RECONNECT_COORD ) - inchi_ios_eprint( log_file, " Include bonds to metals\n"); - else - inchi_ios_eprint( log_file, " Do not reconnect metals (omit RecMet layer)\n"); - } - else - inchi_ios_eprint( log_file, " Do not disconnect metals\n"); - - /* isotopic - always ON, output disabled. 09-17-2009*/ - /* - if ( nMode & REQ_MODE_ISO ) - inchi_ios_eprint( log_file, " Isotopic ON\n"); - else if ( nMode & REQ_MODE_NON_ISO ) - inchi_ios_eprint( log_file, " Isotopic OFF\n"); - */ - -#if ( FIX_ADJ_RAD == 1 ) - if ( ip->bTautFlags & TG_FLAG_FIX_ADJ_RADICALS ) - inchi_ios_eprint( log_file, "Fix Adjacent Radicals\n" ); -#endif - - /* stereo */ - if ( nMode & REQ_MODE_STEREO ) - { - inchi_ios_eprint( log_file, " %s%s%s%sStereo ON\n", - ( nMode & REQ_MODE_NOEQ_STEREO )? "Slow ":"", - ( nMode & REQ_MODE_REDNDNT_STEREO )? "Redund. ":"", - ( nMode & REQ_MODE_NO_ALT_SBONDS)? "No AltBond ":"", - - ( nMode & REQ_MODE_RACEMIC_STEREO)? "Racemic " : - ( nMode & REQ_MODE_RELATIVE_STEREO)? "Relative " : - ( nMode & REQ_MODE_CHIR_FLG_STEREO)? "Chiral Flag " : "Absolute " ); - if ( 0 == (nMode & (REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU)) ) - inchi_ios_eprint( log_file, " Include undefined/unknown stereogenic centers and bonds\n"); - else if ( REQ_MODE_SC_IGN_ALL_UU == (nMode & (REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU)) ) - inchi_ios_eprint( log_file, " Omit undefined/unknown stereogenic centers\n"); - else if ( REQ_MODE_SB_IGN_ALL_UU == (nMode & (REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU)) ) - inchi_ios_eprint( log_file, " Omit undefined/unknown stereogenic bonds\n"); - else - /*case REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU*/ - inchi_ios_eprint( log_file, " Omit undefined/unknown stereogenic centers and bonds\n"); - - if ( 0 != (nMode & REQ_MODE_DIFF_UU_STEREO) ) - { - inchi_ios_eprint( log_file, " Make labels for unknown and undefined stereo different\n"); - } - - -#if ( defined(MIN_SB_RING_SIZE) && MIN_SB_RING_SIZE > 0 ) - k = (ip->nMode & REQ_MODE_MIN_SB_RING_MASK) >> REQ_MODE_MIN_SB_RING_SHFT; - if ( bRELEASE_VERSION != 1 || k != MIN_SB_RING_SIZE ) - { - if ( k >= 3 ) - inchi_ios_eprint( log_file, " Min. stereobond ring size: %d\n", k ); - else - inchi_ios_eprint( log_file, " Min. stereobond ring size: NONE\n" ); - } -#endif - } /* stereo */ - - } /* !bStdFormat */ - - - - if ( !bStdFormat ) - { - if ( TG_FLAG_KETO_ENOL_TAUT & ip->bTautFlags) - inchi_ios_eprint( log_file, " Account for keto-enol tautomerism\n"); - else - inchi_ios_eprint( log_file, " Do not account for keto-enol tautomerism\n"); - if ( TG_FLAG_1_5_TAUT & ip->bTautFlags) - inchi_ios_eprint( log_file, " Account for 1,5-tautomerism\n"); - else - inchi_ios_eprint( log_file, " Do not account for 1,5-tautomerism\n"); -#ifdef BUILD_WITH_ENG_OPTIONS - if ( TG_FLAG_PHOSPHINE_STEREO & ip->bTautFlags ) - inchi_ios_eprint( log_file, " Include phosphine stereochemistry\n"); - else - inchi_ios_eprint( log_file, " Do not include phosphine stereochemistry\n"); - if ( TG_FLAG_ARSINE_STEREO & ip->bTautFlags ) - inchi_ios_eprint( log_file, " Include arsine stereochemistry\n"); - else - inchi_ios_eprint( log_file, " Do not include arsine stereochemistry\n"); - if ( ! (TG_FLAG_FIX_SP3_BUG & ip->bTautFlags) ) - inchi_ios_eprint( log_file, " Turned OFF fix of bug leading to missing or undefined sp3 parity\n"); - if ( !(TG_FLAG_FIX_ISO_FIXEDH_BUG & ip->bTautFlags) ) - inchi_ios_eprint( log_file, " Turned OFF bug-fixes found after v.1.02b release\n"); - if ( !(ip->bFixNonUniformDraw) ) - inchi_ios_eprint( log_file, " Turned OFF fixes of non-uniform drawing issues\n"); - if ( ! (TG_FLAG_MOVE_POS_CHARGES & ip->bTautFlags) ) - inchi_ios_eprint( log_file, " MovePos turned OFF\n"); -#endif - } /* !bStdFormat */ - - if ( ip->bCalcInChIHash != INCHIHASH_NONE ) - { - if (bStdFormat) - inchi_ios_eprint( log_file, "Generating standard InChIKey\n"); - else - inchi_ios_eprint( log_file, "Generating InChIKey\n"); - if ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1 ) - inchi_ios_eprint( log_file, "Generating hash extension (1st block)\n"); - else if ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA2 ) - inchi_ios_eprint( log_file, "Generating hash extension (2nd block)\n"); - else if ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) - inchi_ios_eprint( log_file, "Generating hash extension (two blocks)\n"); - } - - if ( ip->bINChIOutputOptions & INCHI_OUT_SAVEOPT ) - { - inchi_ios_eprint( log_file, "Saving InChI creation options" ); - if ( bStdFormat ) - { - inchi_ios_eprint( log_file, " suppressed for standard InChI" ); - /* NB: actual suppression takes place on InChI serialization */ - /* (as on e.g. Inchi2Inchi conversion it may appear that we create non-std */ - /* InChI instead of standard one) */ - } - inchi_ios_eprint( log_file, "\n" ); - - } - - - if ( ip->bAllowEmptyStructure ) - inchi_ios_eprint( log_file, "Issue warning on empty structure\n" ); - - - - - /* Input */ - if ( ip->nInputType ) - { - inchi_ios_eprint( log_file, "Input format: %s", - ip->nInputType == INPUT_MOLFILE? "MOLfile" : - ip->nInputType == INPUT_SDFILE? "SDfile" : - ip->nInputType == INPUT_CMLFILE? "CMLfile" : -#if ( READ_INCHI_STRING == 1 ) - ip->nInputType == INPUT_INCHI? "InChI (plain identifier)" : -#endif - ip->nInputType == INPUT_INCHI_XML? "InChI AuxInfo (xml)" : - ip->nInputType == INPUT_INCHI_PLAIN? "InChI AuxInfo (plain)" : "Unknown" ); - if ( (ip->nInputType == INPUT_MOLFILE || ip->nInputType == INPUT_SDFILE) && - ip->bGetMolfileNumber ) - inchi_ios_eprint( log_file, " (attempting to read Molfile number)" ); - inchi_ios_eprint( log_file, "\n"); - } - - if ( ip->szSdfDataHeader[0] && ip->nInputType != INPUT_SDFILE ) - inchi_ios_eprint( log_file, " SDfile data header: \"%s\"\n", ip->szSdfDataHeader); - - /* Output */ - - inchi_ios_eprint( log_file, "Output format: %s%s\n", - (ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT)? "Plain text" : - (ip->bINChIOutputOptions & INCHI_OUT_XML)? "XML": - ((ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY) && bInChI2Struct )? "SDfile only (without stereochemical info and atom coordinates)" : - ((ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY) && !bInChI2Struct)? "SDfile only" : "Unknown", - - ((ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) && - (ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT))? ", tabbed":""); - -#if ( bRELEASE_VERSION == 1 ) - if ( ip->bCtPredecessors || ip->bAbcNumbers ) - { - if ( ip->bCtPredecessors && ip->bAbcNumbers ) - inchi_ios_eprint( log_file, "Representation: Compressed\n"); - else - inchi_ios_eprint( log_file, "Connection table: %s, %s\n", - ip->bCtPredecessors? "Predecessor_numbers(closures)":"Canon_numbers(branching, ring closures)", - ip->bAbcNumbers? "Shorter alternative":"Numerical"); - } -#else - if ( (bRELEASE_VERSION != 1) || ip->bCtPredecessors || ip->bAbcNumbers ) - inchi_ios_eprint( log_file, "Connection table: %s, %s\n", - ip->bCtPredecessors? "Predecessor_numbers(closures)":"Canon_numbers(branching, ring closures)", - ip->bAbcNumbers? "Shorter alternative":"Numerical"); - else - inchi_ios_eprint( log_file, "Representation: Numerical"); -#endif - - if ( !(ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY) ) - { - if( ip->bINChIOutputOptions & INCHI_OUT_NO_AUX_INFO ) - inchi_ios_eprint( log_file, "Aux. info suppressed\n"); - else if ( ip->bINChIOutputOptions & INCHI_OUT_SHORT_AUX_INFO ) - inchi_ios_eprint( log_file, "Minimal Aux. info\n"); - else - inchi_ios_eprint( log_file, "Full Aux. info\n"); - } - - - - if ( ip->msec_MaxTime ) - { - unsigned long seconds = ip->msec_MaxTime/1000; - unsigned long milliseconds = (ip->msec_MaxTime%1000); - inchi_ios_eprint( log_file, "Timeout per structure: %lu.%03lu sec\n", seconds, milliseconds); - } - else - inchi_ios_eprint( log_file, "No timeout"); - inchi_ios_eprint( log_file, "Up to %d atoms per structure\n", MAX_ATOMS); - if ( ip->first_struct_number > 1 ) - inchi_ios_eprint( log_file, "Skipping %ld structure%s\n", ip->first_struct_number-1, ip->first_struct_number==2? "":"s" ); - if ( ip->last_struct_number > 0 ) - inchi_ios_eprint( log_file, "Terminate after structure #%ld\n", ip->last_struct_number ); - if ( ip->bSaveWarningStructsAsProblem && ip->path[3] && ip->path[3][0] ) - inchi_ios_eprint( log_file, "Saving warning structures into the problem file\n"); - if ( ip->bSaveAllGoodStructsAsProblem && ip->path[3] && ip->path[3][0] ) - inchi_ios_eprint( log_file, "Saving only all good structures into the problem file\n"); - /* Report debug modes */ -#if ( bRELEASE_VERSION != 1 ) - inchi_ios_eprint( log_file, "Release version = NO\n"); -#endif - - -#if ( TRACE_MEMORY_LEAKS == 1 && defined(_DEBUG) ) - inchi_ios_eprint( log_file, "Tracing memory leaks (SLOW)\n"); -#endif - - inchi_ios_eprint( log_file, "\n" ); - - - -#if ( bRELEASE_VERSION != 1 ) -#if ( FIND_RING_SYSTEMS == 1 ) - inchi_ios_eprint( log_file, "Find ring systems=Y\nTautomers:\n" ); - inchi_ios_eprint( log_file, " 4-pyridinol=%s\n", TAUT_4PYRIDINOL_RINGS==1? "Y":"N"); - inchi_ios_eprint( log_file, " pyrazole=%s\n", TAUT_PYRAZOLE_RINGS==1? "Y":"N"); - inchi_ios_eprint( log_file, " tropolone=%s\n", TAUT_TROPOLONE_7==1? "Y":"N"); - inchi_ios_eprint( log_file, " tropolone-5=%s\n", TAUT_TROPOLONE_5==1? "Y":"N"); - inchi_ios_eprint( log_file, "Only chain attachments to tautomeric rings=%s\n", TAUT_RINGS_ATTACH_CHAIN==1? "Y":"N"); -#endif - if ( ip->bGetSdfileId ) - inchi_ios_eprint( log_file, "Extracting SDfile IDs\n"); - - inchi_ios_eprint( log_file, "\nDbg: MOVE_CHARGES=%d\n", - 0!=(ip->bTautFlags&TG_FLAG_MOVE_POS_CHARGES)); - inchi_ios_eprint( log_file, " REPLACE_ALT_WITH_TAUT=%d; NEUTRALIZE_ENDPOINTS=%d; BNS_PROTECT_FROM_TAUT=%d\n", - REPLACE_ALT_WITH_TAUT, NEUTRALIZE_ENDPOINTS, BNS_PROTECT_FROM_TAUT); - inchi_ios_eprint( log_file, " DISCONNECT_SALTS=%d; TEST_TAUT_SALTS=%d; TEST_TAUT2_SALTS=%d\n", - 0!=(ip->bTautFlags&TG_FLAG_DISCONNECT_SALTS), - 0!=(ip->bTautFlags&TG_FLAG_TEST_TAUT__SALTS), - 0!=(ip->bTautFlags&TG_FLAG_TEST_TAUT2_SALTS)); - - inchi_ios_eprint( log_file, " CHARGED_ACID_TAUT_ONLY=%d MERGE_TAUT_SALTS=%d\n", - 0==(ip->bTautFlags&TG_FLAG_ALLOW_NO_NEGTV_O), - 0!=(ip->bTautFlags&TG_FLAG_MERGE_TAUT_SALTS)); - inchi_ios_eprint( log_file, " DISCONNECT_COORD=%d\n", 0!=(ip->bTautFlags&TG_FLAG_DISCONNECT_COORD) ); -#if ( TEST_RENUMB_ATOMS == 1 ) - inchi_ios_eprint( log_file, "\nDbg: TEST_RENUMB_ATOMS=%d; TEST_RENUMB_NEIGH=%d; TEST_RENUMB_SWITCH=%d\n", - TEST_RENUMB_ATOMS, TEST_RENUMB_NEIGH, TEST_RENUMB_SWITCH ); - inchi_ios_eprint( log_file, " TEST_RENUMB_ATOMS_SAVE_LONGEST=%d\n", - TEST_RENUMB_ATOMS_SAVE_LONGEST); -#endif - -#endif /* ( bRELEASE_VERSION != 1 ) */ - - - return 0; -} - - - -/************************************************************************************/ -void HelpCommandLineParms( INCHI_IOSTREAM *f ) -{ - if ( !f ) - return; - -#if ( bRELEASE_VERSION == 1 ) - - inchi_ios_print_nodisplay( f, -#ifdef TARGET_EXE_USING_API - "%s ver %s%s.\n\nUsage:\ninchi_main inputFile [outputFile [logFile [problemFile]]] [%coption[ %coption...]]\n", - INCHI_NAME, INCHI_VERSION, TARGET_ID_STRING, - INCHI_OPTION_PREFX, INCHI_OPTION_PREFX); -#else - "%s ver %s%s.\n\nUsage:\ninchi-%s inputFile [outputFile [logFile [problemFile]]] [%coption [%coption...]]\n", - INCHI_NAME, INCHI_VERSION, TARGET_ID_STRING, - INCHI_VERSION, INCHI_OPTION_PREFX, INCHI_OPTION_PREFX); -#if ( BUILD_WITH_AMI == 1 ) - inchi_ios_print_nodisplay( f, - "inchi-%s inputFiles... %cAMI [%coption[ %coption...]]\n", - INCHI_VERSION, INCHI_OPTION_PREFX, INCHI_OPTION_PREFX, INCHI_OPTION_PREFX); -#endif -#endif - - inchi_ios_print_nodisplay( f, "\nOptions:\n"); - - inchi_ios_print_nodisplay( f, "\nInput\n"); - inchi_ios_print_nodisplay( f, " STDIO Use standard input/output streams\n"); - inchi_ios_print_nodisplay( f, " InpAux Input structures in %s default aux. info format\n (for use with STDIO)\n", INCHI_NAME); - inchi_ios_print_nodisplay( f, " SDF:DataHeader Read from the input SDfile the ID under this DataHeader\n"); -#if ( ADD_CMLPP == 1 ) - inchi_ios_print_nodisplay( f, " CML Input in CML format (default for input file .CML extension)\n"); -#endif -/* - inchi_ios_print_nodisplay( f, " START:n Skip structures up to n-th one\n"); - inchi_ios_print_nodisplay( f, " END:m Skip structures after m-th one\n"); -*/ -#if ( BUILD_WITH_AMI == 1 ) - inchi_ios_print_nodisplay( f, " AMI Allow multiple input files (wildcards supported)\n"); -#endif - - inchi_ios_print_nodisplay( f, "Output\n"); - inchi_ios_print_nodisplay( f, " AuxNone Omit auxiliary information (default: Include)\n"); - inchi_ios_print_nodisplay( f, " SaveOpt Save custom InChI creation options (non-standard InChI)\n"); - inchi_ios_print_nodisplay( f, " NoLabels Omit structure number, DataHeader and ID from %s output\n", INCHI_NAME); - inchi_ios_print_nodisplay( f, " Tabbed Separate structure number, %s, and AuxInfo with tabs\n", INCHI_NAME); - - /*inchi_ios_print_nodisplay( f, " Compress Compressed output\n"); */ - /*inchi_ios_print_nodisplay( f, " FULL Standard set of options for Full Verbose Output\n");*/ - /*inchi_ios_print_nodisplay( f, " MIN Standard set of options for Minimal Concise Output\n");*/ -#if ( defined(_WIN32) && defined(_MSC_VER) && !defined(COMPILE_ANSI_ONLY) && !defined(TARGET_API_LIB) ) - inchi_ios_print_nodisplay( f, " D Display the structures\n"); - inchi_ios_print_nodisplay( f, " EQU Display sets of identical components\n"); - inchi_ios_print_nodisplay( f, " Fnumber Set display Font size in number of points\n"); -#endif - /*inchi_ios_print_nodisplay( f, " PLAIN Plain text output (Default: XML format)\n");*/ - inchi_ios_print_nodisplay( f, " OutputSDF Convert %s created with default aux. info to SDfile\n", INCHI_NAME); -#if ( SDF_OUTPUT_DT == 1 ) - inchi_ios_print_nodisplay( f, " SdfAtomsDT Output Hydrogen Isotopes to SDfile as Atoms D and T\n"); -#endif -#if ( BUILD_WITH_AMI == 1 ) - inchi_ios_print_nodisplay( f, " AMIOutStd Write output to stdout (in AMI mode)\n"); - inchi_ios_print_nodisplay( f, " AMILogStd Write log to stderr (in AMI mode)\n"); - inchi_ios_print_nodisplay( f, " AMIPrbNone Suppress creation of problem files (in AMI mode)\n"); -#endif - inchi_ios_print_nodisplay( f, "Structure perception\n"); - inchi_ios_print_nodisplay( f, " SNon Exclude stereo (default: include absolute stereo)\n"); - inchi_ios_print_nodisplay( f, " NEWPSOFF Both ends of wedge point to stereocenters (default: a narrow end)\n"); - inchi_ios_print_nodisplay( f, " DoNotAddH All H are explicit (default: add H according to usual valences)\n"); - -#ifndef USE_STDINCHI_API - inchi_ios_print_nodisplay( f, "Stereo perception modifiers (non-standard InChI)\n"); - inchi_ios_print_nodisplay( f, " SRel Relative stereo\n"); - inchi_ios_print_nodisplay( f, " SRac Racemic stereo\n"); - inchi_ios_print_nodisplay( f, " SUCF Use Chiral Flag: On means Absolute stereo, Off - Relative\n"); - - inchi_ios_print_nodisplay( f, "Customizing InChI creation (non-standard InChI)\n"); - inchi_ios_print_nodisplay( f, " SUU Always include omitted unknown/undefined stereo\n"); - inchi_ios_print_nodisplay( f, " SLUUD Make labels for unknown and undefined stereo different\n"); - inchi_ios_print_nodisplay( f, " RecMet Include reconnected metals results\n"); - inchi_ios_print_nodisplay( f, " FixedH Include Fixed H layer\n"); - inchi_ios_print_nodisplay( f, " KET Account for keto-enol tautomerism (experimental)\n"); - inchi_ios_print_nodisplay( f, " 15T Account for 1,5-tautomerism (experimental)\n"); -#endif - - inchi_ios_print_nodisplay( f, "Generation\n"); - inchi_ios_print_nodisplay( f, " Wnumber Set time-out per structure in seconds; W0 means unlimited\n"); - inchi_ios_print_nodisplay( f, " WarnOnEmptyStructure Warn and produce empty %s for empty structure\n", INCHI_NAME); - inchi_ios_print_nodisplay( f, " Key Generate InChIKey\n"); - inchi_ios_print_nodisplay( f, " XHash1 Generate hash extension (to 256 bits) for 1st block of InChIKey\n"); - inchi_ios_print_nodisplay( f, " XHash2 Generate hash extension (to 256 bits) for 2nd block of InChIKey\n"); - - - inchi_ios_print_nodisplay( f, "Conversion\n"); -#ifdef TARGET_EXE_USING_API - inchi_ios_print_nodisplay( f, " InChI2InChI Test mode: Mol/SDfile->%s->%s\n", INCHI_NAME, INCHI_NAME); - inchi_ios_print_nodisplay( f, " InChI2Struct Test mode: Mol/SDfile->%s->Structure->(%s+AuxInfo)\n", INCHI_NAME, INCHI_NAME); -#else - inchi_ios_print_nodisplay( f, " InChI2InChI Convert %s string(s) into %s string(s)\n", INCHI_NAME, INCHI_NAME); - inchi_ios_print_nodisplay( f, " InChI2Struct Convert InChI string(s) to structure(s) in InChI aux.info format\n"); -#endif - -#ifdef BUILD_WITH_ENG_OPTIONS -#if 0 - inchi_ios_print_nodisplay( f, "Engineering options (for testing only)\n"); - inchi_ios_print_nodisplay( f, " NoADP Disable Aggressive Deprotonation\n"); -#if ( FIX_ADJ_RAD == 1 ) - inchi_ios_print_nodisplay( f, " FixRad Fix Adjacent Radicals\n"); -#endif - inchi_ios_print_nodisplay( f, " SPXYZOFF Do not include Phosphines Stereochemistry\n"); - inchi_ios_print_nodisplay( f, " SAsXYZOFF Do not include Arsines Stereochemistry\n"); - inchi_ios_print_nodisplay( f, " FBOFF Do not fix bug leading to missing or undefined sp3 parity\n" ); - inchi_ios_print_nodisplay( f, " FB2OFF Do not fix bugs found after v.1.02b release\n" ); - inchi_ios_print_nodisplay( f, " FNUDOFF Do not fix non-uniform drawing issues\n" ); -#endif -#endif - - -/* -breleaseversion<1 -#else - inchi_ios_print_nodisplay( f, "%s ver %s. Special testing version 12-12-2002.\n", INCHI_NAME, INCHI_VERSION); - inchi_ios_print_nodisplay( f, "\nUsage:\ncINChI09b inputFile [outputFile [logFile [problemFile]]] [%coption[ %coption...]]\n", INCHI_OPTION_PREFX, INCHI_OPTION_PREFX); - inchi_ios_print_nodisplay( f, "\nOptions:\n"); - inchi_ios_print_nodisplay( f, "\tB Basic\n"); - inchi_ios_print_nodisplay( f, "\tT basic Tautomeric\n"); - inchi_ios_print_nodisplay( f, "\tI Isotopic\n"); - inchi_ios_print_nodisplay( f, "\tN Non-isotopic\n"); - inchi_ios_print_nodisplay( f, "\tS Stereo\n"); - inchi_ios_print_nodisplay( f, "\tE Exclude Stereo\n"); - inchi_ios_print_nodisplay( f, "\tD Display the structures\n"); - inchi_ios_print_nodisplay( f, "\tALT produce shorter ALTernative representation (Abc)\n"); - inchi_ios_print_nodisplay( f, "\tSCT produce shorter connection table representation\n"); - inchi_ios_print_nodisplay( f, "\tXML output in xml format\n"); - inchi_ios_print_nodisplay( f, "\tPLAIN output in plain format\n"); - inchi_ios_print_nodisplay( f, "\tMERGE Merge all MOLfiles from the input file into one compound\n"); - inchi_ios_print_nodisplay( f, "\tWnumber time-out per structure in seconds, W0 means unlimited\n"); - inchi_ios_print_nodisplay( f, "\tFnumber set display Font size, points\n"); - inchi_ios_print_nodisplay( f, "\tSREL Relative Stereo\n"); - inchi_ios_print_nodisplay( f, "\tSRAC Racemic Stereo\n"); - inchi_ios_print_nodisplay( f, "\tNOUUSB Omit stereobonds if all are unknown/undefined\n"); - inchi_ios_print_nodisplay( f, "\tNOUUSC Omit stereocenters if all are unknown/undefined\n"); - inchi_ios_print_nodisplay( f, "\tSS Slow Stereo: do not use stereo equivalence\n"); - inchi_ios_print_nodisplay( f, "\tRS Do not test for Redundant Stereo elements\n"); - inchi_ios_print_nodisplay( f, "\tPW Save warning structures in the problems file\n"); - inchi_ios_print_nodisplay( f, "\tPGO Save only all good structures in the problems file\n"); - inchi_ios_print_nodisplay( f, "\tDSB Double Stereo Bonds only (ignore alternating bonds stereo)\n"); - inchi_ios_print_nodisplay( f, "\tRSB:n Min Ring Size for detecting for Stereo Bonds (n=1 => all)\n"); - inchi_ios_print_nodisplay( f, "\tAUXINFO:0 do not output auxiliary information (default:1)\n"); - inchi_ios_print_nodisplay( f, "\tDISCONSALT:0 do not disconnect salts (default:1)\n"); - inchi_ios_print_nodisplay( f, "\tDISCONMETAL:0 do not disconnect metals (default:1)\n"); - inchi_ios_print_nodisplay( f, "\tDISCONMETALCHKVAL:1 do not disconnect if typical valence (default:0)\n"); - inchi_ios_print_nodisplay( f, "\tRECONMETAL:0 do not reconnect metals (default:1)\n"); - inchi_ios_print_nodisplay( f, "\tMOVEPOS:0 do not check moveable positive charges (default:1)\n"); - inchi_ios_print_nodisplay( f, "\tACIDTAUT:n n=1: one H/(-) tautomerism, 2: more (deflt), 0:none\n"); - inchi_ios_print_nodisplay( f, "\tMERGESALTTG:1 merge salt t-groups (default), 0: do not merge\n"); - inchi_ios_print_nodisplay( f, "\tUNCHARGEDACIDS:1 Apply salt (acid) tautomerism in neutral species\n"); - inchi_ios_print_nodisplay( f, "\tO:[suffix] Open all 4 files adding suffix to the inputFile name\n"); - inchi_ios_print_nodisplay( f, "\tOP:outputpath Set output path\n"); - inchi_ios_print_nodisplay( f, "\tMOL input file is a MOLfile (default)\n"); - inchi_ios_print_nodisplay( f, "\tSDF[:DataHeader] Include SDfile data for the header into the results\n"); - inchi_ios_print_nodisplay( f, "\tSDFID extract CAS r.n. in addition to requested SDfile data\n"); - inchi_ios_print_nodisplay( f, "\tSTART:number Start at the given structure ordering number\n"); - inchi_ios_print_nodisplay( f, "\tEND:number Terminate after the given structure ordering number\n"); -*/ -#endif - -} - - - - -/*^^^ */ -/************************************************************************************/ -void HelpCommandLineParmsReduced( INCHI_IOSTREAM *f ) -{ - if ( !f ) - return; - -#if ( bRELEASE_VERSION == 1 ) - - - /*^^^ */ - inchi_ios_print_nodisplay( f, -#ifdef TARGET_EXE_USING_API - "%s ver %s%s.\n\nUsage:\ninchi_main inputFile [outputFile [logFile [problemFile]]] [%coption[ %coption...]]\n", - INCHI_NAME, INCHI_VERSION, TARGET_ID_STRING, - INCHI_OPTION_PREFX, INCHI_OPTION_PREFX); -#else - "%s ver %s%s.\n\nUsage:\nc%s-%s inputFile [outputFile [logFile [problemFile]]] [%coption[ %coption...]]\n", - INCHI_NAME, INCHI_VERSION, TARGET_ID_STRING, - INCHI_NAME, INCHI_VERSION, INCHI_OPTION_PREFX, INCHI_OPTION_PREFX); -#endif - /*^^^ */ - - inchi_ios_print_nodisplay( f, "\nOptions:\n"); - - - inchi_ios_print_nodisplay( f, "\nInput\n"); - inchi_ios_print_nodisplay( f, " STDIO Use standard input/output streams\n"); - inchi_ios_print_nodisplay( f, " InpAux Input structures in %s default aux. info format\n (for use with STDIO)\n", INCHI_NAME); - inchi_ios_print_nodisplay( f, " SDF:DataHeader Read from the input SDfile the ID under this DataHeader\n"); -#if ( ADD_CMLPP == 1 ) - inchi_ios_print_nodisplay( f, " CML Input in CML format (default for input file .CML extension)\n"); -#endif -/* - inchi_ios_print_nodisplay( f, " START:n Skip structures up to n-th one\n"); - inchi_ios_print_nodisplay( f, " END:m Skip structures after m-th one\n"); -*/ -#if ( BUILD_WITH_AMI == 1 ) - inchi_ios_print_nodisplay( f, " AMI Allow multiple input files (wildcards supported)\n"); -#endif - - inchi_ios_print_nodisplay( f, "Output\n"); - inchi_ios_print_nodisplay( f, " AuxNone Omit auxiliary information (default: Include)\n"); - inchi_ios_print_nodisplay( f, " SaveOpt Save custom InChI creation options (non-standard InChI)\n"); - inchi_ios_print_nodisplay( f, " NoLabels Omit structure number, DataHeader and ID from %s output\n", INCHI_NAME); - inchi_ios_print_nodisplay( f, " Tabbed Separate structure number, %s, and AuxInfo with tabs\n", INCHI_NAME); - /* inchi_ios_print_nodisplay( f, " Compress Compressed output\n"); */ -#if ( defined(_WIN32) && defined(_MSC_VER) && !defined(COMPILE_ANSI_ONLY) && !defined(TARGET_API_LIB) ) - inchi_ios_print_nodisplay( f, " D Display the structures\n"); - inchi_ios_print_nodisplay( f, " EQU Display sets of identical components\n"); - inchi_ios_print_nodisplay( f, " Fnumber Set display Font size in number of points\n"); -#endif - inchi_ios_print_nodisplay( f, " OutputSDF Convert %s created with default aux. info to SDfile\n", INCHI_NAME); -#if ( SDF_OUTPUT_DT == 1 ) - inchi_ios_print_nodisplay( f, " SdfAtomsDT Output Hydrogen Isotopes to SDfile as Atoms D and T\n"); -#endif -#if ( BUILD_WITH_AMI == 1 ) - inchi_ios_print_nodisplay( f, " AMIOutStd Write output to stdout (in AMI mode)\n"); - inchi_ios_print_nodisplay( f, " AMILogStd Write log to stderr (in AMI mode)\n"); - inchi_ios_print_nodisplay( f, " AMIPrbNone Suppress creation of problem files (in AMI mode)\n"); -#endif - inchi_ios_print_nodisplay( f, "Structure perception\n"); - inchi_ios_print_nodisplay( f, " SNon Exclude stereo (default: include absolute stereo)\n"); - inchi_ios_print_nodisplay( f, " NEWPSOFF Both ends of wedge point to stereocenters (default: a narrow end)\n"); - inchi_ios_print_nodisplay( f, " DoNotAddH All H are explicit (default: add H according to usual valences)\n"); - -#ifndef USE_STDINCHI_API - inchi_ios_print_nodisplay( f, "Stereo perception modifiers (non-standard InChI)\n"); - inchi_ios_print_nodisplay( f, " SRel Relative stereo\n"); - inchi_ios_print_nodisplay( f, " SRac Racemic stereo\n"); - inchi_ios_print_nodisplay( f, " SUCF Use Chiral Flag: On means Absolute stereo, Off - Relative\n"); - - inchi_ios_print_nodisplay( f, "Customizing InChI creation (non-standard InChI)\n"); - inchi_ios_print_nodisplay( f, " SUU Always include omitted unknown/undefined stereo\n"); - inchi_ios_print_nodisplay( f, " SLUUD Make labels for unknown and undefined stereo different\n"); - inchi_ios_print_nodisplay( f, " RecMet Include reconnected metals results\n"); - inchi_ios_print_nodisplay( f, " FixedH Include Fixed H layer\n"); - inchi_ios_print_nodisplay( f, " KET Account for keto-enol tautomerism (experimental)\n"); - inchi_ios_print_nodisplay( f, " 15T Account for 1,5-tautomerism (experimental)\n"); -#endif - - inchi_ios_print_nodisplay( f, "Generation\n"); - inchi_ios_print_nodisplay( f, " Wnumber Set time-out per structure in seconds; W0 means unlimited\n"); - inchi_ios_print_nodisplay( f, " WarnOnEmptyStructure Warn and produce empty %s for empty structure\n", INCHI_NAME); - inchi_ios_print_nodisplay( f, " Key Generate InChIKey\n"); - inchi_ios_print_nodisplay( f, " XHash1 Generate hash extension (to 256 bits) for 1st block of InChIKey\n"); - inchi_ios_print_nodisplay( f, " XHash2 Generate hash extension (to 256 bits) for 2nd block of InChIKey\n"); - -#ifdef BUILD_WITH_ENG_OPTIONS -#if 0 - inchi_ios_print_nodisplay( f, "Engineering options (for testing only)\n"); - inchi_ios_print_nodisplay( f, " NoADP Disable Aggressive Deprotonation\n"); -#if ( FIX_ADJ_RAD == 1 ) - inchi_ios_print_nodisplay( f, " FixRad Fix Adjacent Radicals\n"); -#endif - inchi_ios_print_nodisplay( f, " SPXYZOFF Do not include Phosphines Stereochemistry\n"); - inchi_ios_print_nodisplay( f, " SAsXYZOFF Do not include Arsines Stereochemistry\n"); - inchi_ios_print_nodisplay( f, " FBOFF Do not fix bug leading to missing or undefined sp3 parity\n" ); - inchi_ios_print_nodisplay( f, " FB2OFF Do not fix bugs found after v.1.02b release\n" ); - inchi_ios_print_nodisplay( f, " FNUDOFF Do not fix non-uniform drawing issues\n" ); -#endif -#endif - - - -#endif /* #if ( bRELEASE_VERSION == 1 ) */ -} - - - - -#define fprintf2 inchi_fprintf - -#ifndef TARGET_API_LIB -/************************************************************************************/ -int OpenFiles( FILE **inp_file, FILE **output_file, FILE **log_file, FILE **prb_file, INPUT_PARMS *ip ) -{ -/* - -- Files -- - ip->path[0] => Input - ip->path[1] => Output (INChI) - ip->path[2] => Log - ip->path[3] => Problem structures - ip->path[4] => Errors file (ACD Labs) - -*/ - /* logfile -- open as early as possible */ - if ( !ip->path[2] || !ip->path[2][0] ) { - fprintf2( stderr, "%s version %s%s%s\n", INCHI_NAME, INCHI_VERSION, TARGET_ID_STRING, bRELEASE_VERSION? "":""); /* (Pre-release, for evaluation purposes only)" ); */ - fprintf2( stderr, "Log file not specified. Using standard error output.\n"); - *log_file = stderr; - } else - if ( !(*log_file = fopen( ip->path[2], "w" ) ) ) { - fprintf2( stderr, "%s version %s%s%s\n", INCHI_NAME, INCHI_VERSION, TARGET_ID_STRING, bRELEASE_VERSION? "":""); /* (Pre-release, for evaluation purposes only)" );*/ - fprintf2( stderr, "Cannot open log file '%s'. Using standard error output.\n", ip->path[2] ); - *log_file = stderr; - } else { - fprintf2( *log_file, "%s version %s%s%s\n", INCHI_NAME, INCHI_VERSION, TARGET_ID_STRING, bRELEASE_VERSION? "":""); /* (Pre-release, for evaluation purposes only)" );*/ - fprintf2( *log_file, "Opened log file '%s'\n", ip->path[2] ); - } - /* input file */ - if ( (ip->nInputType == INPUT_MOLFILE || ip->nInputType == INPUT_SDFILE || - ip->nInputType == INPUT_CMLFILE || ip->nInputType == INPUT_INCHI || - ip->nInputType == INPUT_INCHI_PLAIN ) && ip->num_paths > 0 ) - { - const char *fmode = NULL; -#if ( defined(_MSC_VER)&&defined(_WIN32) || defined(__BORLANDC__)&&defined(__WIN32__) || defined(__GNUC__)&&defined(__MINGW32__)&&defined(_WIN32) ) - /* compilers that definitely allow fopen "rb" (binary read) mode */ - fmode = "rb"; - if ( !ip->path[0] || !ip->path[0][0] || !(*inp_file = fopen( ip->path[0], "rb" ) ) ) - { - fprintf2( *log_file, "Cannot open input file '%s'. Terminating.\n", ip->path[0]? ip->path[0] : "" ); - goto exit_function; - } - else - { - if ( ip->nInputType == INPUT_CMLFILE ) - { - int c; -#ifdef CML_DEBUG - printf ("cr %d lf %d ret %d\n", (int) '\r', (int) '\f', (int) '\n'); -#endif - /* read up to the end of the first line */ - while( (c = fgetc( *inp_file )) && c != EOF && c != '\n' && c != '\r' ) - ; - if ( c == '\r' || c == EOF ) - { - /* text file contains CR; close and reopen as "text" */ - fclose( *inp_file ); - if ( !(*inp_file = fopen( ip->path[0], "r" ) ) ) - { - fprintf2( *log_file, "Cannot open input file '%s' (2nd attempt). Terminating.\n", ip->path[0] ); - goto exit_function; - } - fprintf2( *log_file, "Opened input file '%s'\n", ip->path[0] ); - fmode = "r"; - } - else - { - fclose( *inp_file ); - if ( !(*inp_file = fopen( ip->path[0], "rb" ) ) ) - { - fprintf2( *log_file, "Cannot open input file '%s' (2nd attempt). Terminating.\n", ip->path[0] ); - goto exit_function; - } - fprintf2( *log_file, "Opened input file '%s': no CR.\n", ip->path[0] ); - fmode = "rb"; - } - } /* CML */ - else - fprintf2( *log_file, "Opened input file '%s'\n", ip->path[0] ); - - } -#else - if ( !ip->path[0] || !ip->path[0][0] || !(*inp_file = fopen( ip->path[0], "r" ) ) ) { - fprintf2( *log_file, "Cannot open input file '%s'. Terminating.\n", ip->path[0]? ip->path[0] : "" ); - goto exit_function; - } else { - fprintf2( *log_file, "Opened input file '%s'\n", ip->path[0] ); - } - fmode = "r"; -#endif - DetectInputINChIFileType( inp_file, ip, fmode ); - } else - if ( (ip->nInputType != INPUT_MOLFILE && ip->nInputType != INPUT_SDFILE && ip->nInputType != INPUT_CMLFILE && ip->nInputType != INPUT_INCHI - /*^^^ post-1.02b */ - && ip->nInputType != INPUT_INCHI_PLAIN - )) { - fprintf2( *log_file, "Input file type not specified. Terminating.\n"); - goto exit_function; - } else { - fprintf2( *log_file, "Input file not specified. Using standard input.\n"); - *inp_file = stdin; - } - /* output file */ - if ( !ip->path[1] || !ip->path[1][0] ) { - fprintf2( *log_file, "Output file not specified. Using standard output.\n"); - *output_file = stdout; - } else { - if ( !(*output_file = fopen( ip->path[1], "w" ) ) ) { - fprintf2( *log_file, "Cannot open output file '%s'. Terminating.\n", ip->path[1] ); - goto exit_function; - } else { - fprintf2( *log_file, "Opened output file '%s'\n", ip->path[1] ); - if ( (ip->bINChIOutputOptions & (INCHI_OUT_PLAIN_TEXT)) && - *inp_file != stdin && - !(ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY) && - !ip->bNoStructLabels && - !(ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT)) { - PrintFileName( "* Input_File: \"%s\"\n", *output_file, ip->path[0] ); - } - } - } - /* problem file */ - if ( ip->path[3] && ip->path[3][0] ) { - const char *fmode = "w"; -#if ( defined(_MSC_VER)&&defined(_WIN32) || defined(__BORLANDC__)&&defined(__WIN32__) || defined(__GNUC__)&&defined(__MINGW32__)&&defined(_WIN32) ) - if ( ip->nInputType != INPUT_CMLFILE ) { - fmode = "wb"; - } -#endif - if ( !(*prb_file = fopen( ip->path[3], fmode ) ) ) { - fprintf2( *log_file, "Cannot open problem file '%s'. Terminating.\n", ip->path[3] ); - goto exit_function; - } else { - fprintf2( *log_file, "Opened problem file '%s'\n", ip->path[3] ); - } - } - return 1; /* success */ - -exit_function: - return 0; /* failed */ - -} -#define NUM_VERSIONS 7 -#define LEN_VERSIONS 64 -/*******************************************************************/ -static int bMatchOnePrefix( int len, char *str, int lenPrefix[], - char strPrefix[][LEN_VERSIONS], int numPrefix); -/*******************************************************************/ -static int bMatchOnePrefix( int len, char *str, int lenPrefix[], - char strPrefix[][LEN_VERSIONS], int numPrefix) -{ - int i; - for ( i = 0; i < numPrefix; i ++ ) { - if ( len >= lenPrefix[i] && - !memcmp( str, strPrefix[i], lenPrefix[i] ) ) { - return 1; - } - } - return 0; -} -/*******************************************************************/ -int DetectInputINChIFileType( FILE **inp_file, INPUT_PARMS *ip, const char *fmode ) -{ - char szLine[256], ret = 0; - static char szPlnVersion[NUM_VERSIONS][LEN_VERSIONS]; /* = "INChI:1.1Beta/";*/ - static int lenPlnVersion[NUM_VERSIONS]; - static char szPlnAuxVer[NUM_VERSIONS][LEN_VERSIONS]; /* = "AuxInfo:1.1Beta/";*/ - static int lenPlnAuxVer[NUM_VERSIONS]; - static char szXmlVersion[NUM_VERSIONS][LEN_VERSIONS]; /* = "";*/ - static int lenXmlVersion[NUM_VERSIONS]; - static char szXmlStruct[LEN_VERSIONS] = "nInputType == INPUT_INCHI_XML || ip->nInputType == INPUT_INCHI_PLAIN || ip->nInputType == INPUT_INCHI ) { - return 1; - } - if ( !bInitilized ) { - lenPlnVersion[0] = sprintf( szPlnVersion[0], "%s=%s/", INCHI_NAME, INCHI_VERSION ); - lenPlnVersion[1] = sprintf( szPlnVersion[1], "INChI=1.12Beta/" ); - lenPlnVersion[2] = sprintf( szPlnVersion[2], "INChI=1.0RC/" ); - lenPlnVersion[3] = sprintf( szPlnVersion[3], "InChI=1.0RC/" ); - lenPlnVersion[4] = sprintf( szPlnVersion[4], "InChI=1/" ); - lenPlnVersion[5] = sprintf( szPlnVersion[5], "MoChI=1a/" ); - lenPlnVersion[6] = sprintf( szPlnVersion[6], "InChI=1S/" ); - lenPlnAuxVer[0] = sprintf( szPlnAuxVer[0], "AuxInfo=%s/", INCHI_VERSION ); - lenPlnAuxVer[1] = sprintf( szPlnAuxVer[1], "AuxInfo=1.12Beta/" ); - lenPlnAuxVer[2] = sprintf( szPlnAuxVer[2], "AuxInfo=1.0RC/" ); - lenPlnAuxVer[3] = sprintf( szPlnAuxVer[3], "AuxInfo=1.0RC/" ); - lenPlnAuxVer[4] = sprintf( szPlnAuxVer[4], "AuxInfo=1/" ); - lenPlnAuxVer[5] = sprintf( szPlnAuxVer[5], "AuxInfo=1a/" ); - lenPlnAuxVer[6] = sprintf( szPlnAuxVer[6], "AuxInfo=1/" ); - lenXmlVersion[0] = sprintf( szXmlVersion[0], "<%s version=\"%s\">", INCHI_NAME, INCHI_VERSION ); - lenXmlVersion[1] = sprintf( szXmlVersion[1], "" ); - lenXmlVersion[2] = sprintf( szXmlVersion[2], "" ); - lenXmlVersion[3] = sprintf( szXmlVersion[3], "" ); - lenXmlVersion[4] = sprintf( szXmlVersion[4], "" ); - lenXmlVersion[5] = sprintf( szXmlVersion[5], "" ); - lenXmlVersion[6] = sprintf( szXmlVersion[6], "" ); - lenXmlIdentVer[0] = sprintf( szXmlIdentVer[0], "= 2 && !bINChI_xml ) { - ip->nInputType = INPUT_INCHI_PLAIN; - ret = 1; - } else - if ( !bINChI_plain && bINChI_xml >= 3 ) { - ip->nInputType = INPUT_INCHI_XML; - ret = 1; - } -/*exit_function:*/ - fclose ( *inp_file ); - *inp_file = fopen( ip->path[0], fmode ); - return ret; -} -#undef NUM_VERSIONS -#undef LEN_VERSIONS - -#endif /* TARGET_API_LIB */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _ICHIPARM_H_ +#define _ICHIPARM_H_ + +/* Local */ + +int DetectInputINChIFileType(FILE **inp_file, INPUT_PARMS *ip, const char *fmode); +void HelpCommandLineParmsReduced(INCHI_IOSTREAM *f); + + +int ReadCommandLineParms( int argc, const char *argv[], + INPUT_PARMS *ip, + char *szSdfDataValue, + unsigned long *ulDisplTime, + int bReleaseVersion, + INCHI_IOSTREAM *log_file) +{ + int i, k, c; + const char *q; + unsigned long ul; + int nFontSize = -9; + int nMode = 0; + + int nReleaseMode = nMode | (REQ_MODE_BASIC | REQ_MODE_TAUT | REQ_MODE_ISO | REQ_MODE_STEREO); + +#if ( MIN_SB_RING_SIZE > 0 ) + int nMinDbRinSize = MIN_SB_RING_SIZE, mdbr=0; +#endif + + /*int bNotRecognized=0;*/ + char szNameSuffix[32], szOutputPath[512]; +#if ( BUILD_WITH_AMI == 1 ) && ( OUTPUT_FILE_EXT == 1 ) + char szOutNameExt[3][128]; + int numOutNameExt; +#endif + int bNameSuffix, bOutputPath; + int bMergeAllInputStructures; + int bDisconnectSalts = (DISCONNECT_SALTS==1); + int bDoNotAddH = 0; + + int bVer1Options = 1; + int bReconnectCoord = (RECONNECT_METALS==1); + int bDisconnectCoord = (DISCONNECT_METALS==1); + +#ifdef TARGET_LIB_FOR_WINCHI +/* int bVer1Options = 0; + int bReconnectCoord = 1; + int bDisconnectCoord = 1; */ + int is_gui = 1; + int bINChIOutputOptions = INCHI_OUT_EMBED_REC; /* embed reconnected & output full aux info */ + int bCompareComponents = CMP_COMPONENTS; +#else +/* int bVer1Options = 1; + int bReconnectCoord = (RECONNECT_METALS==1); + int bDisconnectCoord = (DISCONNECT_METALS==1); */ + int is_gui = 0; + int bINChIOutputOptions = ((EMBED_REC_METALS_INCHI==1)? INCHI_OUT_EMBED_REC : 0); + int bCompareComponents = 0; +#endif + + int bINChIOutputOptions2 = 0; + + int bDisconnectCoordChkVal = (CHECK_METAL_VALENCE == 1); + int bMovePositiveCharges = (MOVE_CHARGES==1); + int bAcidTautomerism = (DISCONNECT_SALTS==1)? (TEST_REMOVE_S_ATOMS==1? 2:1):0; + int bUnchargedAcidTaut = (CHARGED_SALTS_ONLY==0); + int bMergeSaltTGroups = (DISCONNECT_SALTS==1); + int bDisplayCompositeResults; + +#define VER103_DEFAULT_MODE (REQ_MODE_TAUT | REQ_MODE_ISO | REQ_MODE_STEREO |\ + REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU) + + INCHI_MODE bVer1DefaultMode = VER103_DEFAULT_MODE; + + const char *ext[MAX_NUM_PATHS+1]; + const char *pArg; + double t; + int bRecognizedOption; + int bDisplay = 0; + int bNoStructLabels = 0; + int bOutputMolfileOnly = 0; + int bOutputMolfileDT = 0; + int bOutputMolfileSplit = 0; + int bForcedChiralFlag = 0; + +#if ( READ_INCHI_STRING == 1 ) + int bDisplayIfRestoreWarnings = 0; +#endif + int bOutputStyle = INCHI_OUT_PLAIN_TEXT; + + int bTgFlagVariableProtons = 1; + int bTgFlagHardAddRenProtons = 1; + +#ifdef STEREO_WEDGE_ONLY + int bPointedEdgeStereo = STEREO_WEDGE_ONLY; /* NEWPS TG_FLAG_POINTED_EDGE_STEREO*/ +#endif + +#if ( FIX_ADJ_RAD == 1 ) + int bFixAdjacentRad = 0; +#endif + + int bAddPhosphineStereo = 1; + int bAddArsineStereo = 1; + int bFixSp3bug = 1; + int bFixFB2 = 1; + int bKetoEnolTaut = 0; + int b15TautNonRing = 0; + int bStdFormat = 1; + int bHashKey = 0; + int bHashXtra1 = 0; + int bHashXtra2 = 0; + int bLargeMolecules = 0; + int bPolymers = 0; + + bDisplayCompositeResults= 0; + + ext[0] = ".mol"; + ext[1] = bVer1Options? ".txt" : ".ich"; + ext[2] = ".log"; + ext[3] = ".prb"; + ext[4] = ""; + +#if ( MAX_NUM_PATHS < 4 ) + #error Wrong initialization +#endif + + + /* Init table parms */ + + memset(ip, 0, sizeof(*ip)); + + /* Default are standard InChI generation options */ + + bVer1DefaultMode &= ~REQ_MODE_BASIC; /* "FIXEDH - OFF" */ + bReconnectCoord = 0; /* "RECMET - OFF" */ + bPointedEdgeStereo = 1; /* "NEWPS" */ + ip->bFixNonUniformDraw = 1; /* "FNUD" */ + +#ifndef COMPILE_ANSI_ONLY + strcpy( ip->tdp.ReqShownFoundTxt[ilSHOWN], "Shown" ); + ip->dp.sdp.tdp = &ip->tdp; + ip->dp.pdp = &ip->pdp; +#endif + + memset( szNameSuffix, 0, sizeof(szNameSuffix) ); + bNameSuffix = 0; + memset(szOutputPath, 0, sizeof(szOutputPath) ); + bOutputPath = 0; +#if( OUTPUT_FILE_EXT == 1 ) + memset(szOutNameExt, 0, sizeof(szOutNameExt) ); + numOutNameExt = 0; +#endif + bMergeAllInputStructures = 0; + + *ulDisplTime = 0; + +#ifdef TARGET_API_LIB + ip->msec_MaxTime = 0; /* milliseconds, default = unlimited in libinchi */ +#else + ip->msec_MaxTime = 60000; /* milliseconds, default = 60 sec */ +#endif + + if ( bReleaseVersion ) + { + /* normal */ + + ip->bAbcNumbers = 0; + ip->bCtPredecessors = 0; + } + else + { + nReleaseMode = 0; + } + + if ( bVer1Options ) + { + bNameSuffix = 1; + szNameSuffix[0] = '\0'; + } + + + /* Parse */ + + for ( i = 1; i < argc; i ++ ) + { + + if ( is_gui && INCHI_OPTION_PREFX == argv[i][0] && INCHI_OPTION_PREFX != argv[i][1] ) + { + /*=== parsing TARGET_LIB_FOR_WINCHI GUI (and v. 0.9xx Beta)options ===*/ + + pArg = argv[i]+1; + + + /*--- Input options ---*/ + if ( !inchi_stricmp( pArg, "INPAUX" )) + { + if (INPUT_NONE == ip->nInputType) + ip->nInputType = INPUT_INCHI_PLAIN; + } + else if ( INPUT_NONE == ip->nInputType && + (!inchi_memicmp( pArg, "SDF", 3 )) && + ( pArg[3] == ':' ) ) + { + k = 0; + mystrncpy( ip->szSdfDataHeader, pArg+4, MAX_SDF_HEADER+1 ); + lrtrim( ip->szSdfDataHeader, &k ); + if ( k ) + { + ip->pSdfLabel = ip->szSdfDataHeader; + ip->pSdfValue = szSdfDataValue; + ip->nInputType = INPUT_SDFILE; + } + else + { + ip->pSdfLabel = NULL; + ip->pSdfValue = NULL; + ip->nInputType = INPUT_MOLFILE; + } + } + else if ( INPUT_NONE == ip->nInputType && !inchi_stricmp( pArg, "MOL" ) ) + { + ip->nInputType = INPUT_MOLFILE; + } + else if ( INPUT_NONE == ip->nInputType && !inchi_stricmp( pArg, "SDF" ) ) + { + ip->nInputType = INPUT_MOLFILE; + } + + else if ( !inchi_memicmp( pArg, "START:", 6 ) ) + { + ip->first_struct_number = strtol(pArg+6, NULL, 10); + } + else if ( !inchi_memicmp( pArg, "END:", 4 ) ) + { + ip->last_struct_number = strtol(pArg+4, NULL, 10); + } + else if ( !inchi_memicmp( pArg, "RECORD:", 7 ) ) + { + long num = strtol(pArg+6, NULL, 10); + ip->first_struct_number = num; + ip->last_struct_number = num; + } + +#ifdef BUILD_WITH_ENG_OPTIONS + else if ( !inchi_memicmp( pArg, "RSB:", 4 ) ) + { + mdbr = (int)strtol(pArg+4, NULL, 10); + } else + if ( !inchi_memicmp( pArg, "DISCONSALT:", 11 ) ) { + bDisconnectSalts = (0 != strtol(pArg+11, NULL, 10)); + } else + if ( !inchi_memicmp( pArg, "DISCONMETAL:", 12 ) ) { + bDisconnectCoord = (0 != strtol(pArg+12, NULL, 10)); + } else + if ( !inchi_memicmp( pArg, "RECONMETAL:", 11 ) ) { + bReconnectCoord = (0 != strtol(pArg+11, NULL, 10)); + } else + if ( !inchi_memicmp( pArg, "DISCONMETALCHKVAL:", 18 ) ) { + bDisconnectCoordChkVal = (0 != strtol(pArg+18, NULL, 10)); + } else + if ( !inchi_memicmp( pArg, "MOVEPOS:", 8 ) ) { + bMovePositiveCharges = (0 != strtol(pArg+8, NULL, 10)); + } else + if ( !inchi_memicmp( pArg, "MERGESALTTG:", 12 ) ) { + bMergeSaltTGroups = (0 != strtol(pArg+12, NULL, 10)); + } else + if ( !inchi_memicmp( pArg, "UNCHARGEDACIDS:", 15) ) { + bUnchargedAcidTaut = (0 != strtol(pArg+15, NULL, 16));; + } else + if ( !inchi_memicmp( pArg, "ACIDTAUT:", 9 ) ) { + bAcidTautomerism = c = (int)strtol(pArg+9, NULL, 10); + if ( 0 <= c && c <= 2 ) bAcidTautomerism = c; + /*else bNotRecognized = 2*bReleaseVersion;*/ + } +#endif + + + /*--- Output options ---*/ + +#if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) ) + else if ( !inchi_stricmp( pArg, "Tabbed" ) ) + { + bOutputStyle |= INCHI_OUT_TABBED_OUTPUT; + } +#endif + + else if ( !inchi_stricmp( pArg, "NOLABELS" ) ) + { + bNoStructLabels = 1; + } + else if ( !inchi_stricmp( pArg, "SAVEOPT" ) ) + { + bINChIOutputOptions |= INCHI_OUT_SAVEOPT; + } + else if ( !inchi_stricmp( pArg, "AUXNONE" ) ) + { + /* no aux. info */ + bINChIOutputOptions |= INCHI_OUT_NO_AUX_INFO; /* no aux info */ + bINChIOutputOptions &= ~INCHI_OUT_SHORT_AUX_INFO; + } + +#ifdef ERR_INCHI_STRING_ALLOWED + else if ( !inchi_stricmp( pArg, "OUTERRINCHI" ) ) + { + /* Signify InChI error generation on InChI strings output, not only report to log file */ + bINChIOutputOptions2 |= INCHI_OUT_INCHI_GEN_ERROR; + } +#endif + else if ( !inchi_stricmp( pArg, "MISMATCHISERROR" ) ) + { + /* Consider InChI conversion "problem/mismatch" as error */ + bINChIOutputOptions2 |= INCHI_OUT_MISMATCH_AS_ERROR; + } + + +#if ( defined(BUILD_WITH_ENG_OPTIONS) || defined(TARGET_LIB_FOR_WINCHI) ) + + else if ( !inchi_stricmp( pArg, "SDFID" ) ) + { + ip->bGetSdfileId = 1; + } + else if ( !inchi_stricmp( pArg, "PLAIN" ) ) + { + bOutputStyle |= INCHI_OUT_PLAIN_TEXT; + } + else if ( !inchi_stricmp( pArg, "ANNPLAIN" ) ) + { + bOutputStyle |= INCHI_OUT_PLAIN_TEXT_COMMENTS; + } + else if ( !inchi_memicmp( pArg, "AUXINFO:", 8 ) && isdigit(UCINT pArg[8]) ) + { + k = strtol(pArg+8, NULL, 10); + if ( k == 0 ) + { + bINChIOutputOptions |= INCHI_OUT_NO_AUX_INFO; /* no aux info */ + bINChIOutputOptions &= ~INCHI_OUT_SHORT_AUX_INFO; + } + else if ( k == 1 ) + { + bINChIOutputOptions &= ~(INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO); /* include full aux info */ + } + else if ( k == 2 ) + { + bINChIOutputOptions &= ~INCHI_OUT_NO_AUX_INFO; /* include short aux info */ + bINChIOutputOptions |= INCHI_OUT_SHORT_AUX_INFO; + } + else + { + bINChIOutputOptions = k; /* override everything */ + } + } + else if ( !inchi_stricmp( pArg, "MERGE" ) ) + { + bMergeAllInputStructures = 1; + } + + else if ( !inchi_stricmp( pArg, "PGO" ) ) + { + ip->bSaveAllGoodStructsAsProblem = 1; + } + else if ( !inchi_stricmp( pArg, "DCR" ) ) + { + bDisplayCompositeResults = 1; + } + + else if ( !inchi_stricmp( pArg, "DSB" ) ) + { + nMode |= REQ_MODE_NO_ALT_SBONDS; + } + else if ( !inchi_stricmp( pArg, "NOHDR" ) ) + { + bNoStructLabels = 1; + } + + else if ( !inchi_stricmp( pArg, "NoVarH" ) ) + { + bTgFlagVariableProtons = 0; + } + +#endif /* BUILD_WITH_ENG_OPTIONS */ + + /*--- All modes (std and non-std InChI) structure perception options ---*/ + else if ( !inchi_stricmp( pArg, "SNON" ) ) + { + bVer1DefaultMode &= ~REQ_MODE_STEREO; + nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); + } + else if ( !inchi_stricmp( pArg, "NEWPSOFF" ) ) + { + bPointedEdgeStereo = 0; + } + else if ( !inchi_stricmp( pArg, "DONOTADDH" ) ) + { + bDoNotAddH = 1; + } + +#ifndef USE_STDINCHI_API + + /* Non-std InChI structure perception options */ + else if ( !inchi_stricmp( pArg, "SREL" ) ) + { + if ( nMode & REQ_MODE_RACEMIC_STEREO ) + { + nMode ^= REQ_MODE_RACEMIC_STEREO; + } + if ( nMode & REQ_MODE_CHIR_FLG_STEREO ) + { + nMode ^= REQ_MODE_CHIR_FLG_STEREO; + } + nMode |= REQ_MODE_RELATIVE_STEREO; + nMode |= REQ_MODE_STEREO; + bStdFormat = 0; + } + else if ( !inchi_stricmp( pArg, "SRAC" ) ) + { + if ( nMode & REQ_MODE_RELATIVE_STEREO ) + { + nMode ^= REQ_MODE_RELATIVE_STEREO; + } + if ( nMode & REQ_MODE_CHIR_FLG_STEREO ) + { + nMode ^= REQ_MODE_CHIR_FLG_STEREO; + } + nMode |= REQ_MODE_RACEMIC_STEREO; + nMode |= REQ_MODE_STEREO; + bStdFormat = 0; + } + else if ( !inchi_stricmp( pArg, "SUCF" ) ) + { + if ( nMode & REQ_MODE_RELATIVE_STEREO ) + { + nMode ^= REQ_MODE_RELATIVE_STEREO; + } + if ( nMode & REQ_MODE_RACEMIC_STEREO ) + { + nMode ^= REQ_MODE_RACEMIC_STEREO; + } + nMode |= REQ_MODE_CHIR_FLG_STEREO; /* stereo defined by the Chiral flag */ + nMode |= REQ_MODE_STEREO; + bStdFormat = 0; + } + else if ( !inchi_stricmp( pArg, "ChiralFlagON" ) ) + { + /* used only with /SUCF */ + /* NB: do not toggle off bStdFormat! (if necessary SUCF will do) */ + bForcedChiralFlag &= ~FLAG_SET_INP_AT_NONCHIRAL; + bForcedChiralFlag |= FLAG_SET_INP_AT_CHIRAL; + } + else if ( !inchi_stricmp( pArg, "ChiralFlagOFF" ) ) + { + /* used only with /SUCF */ + /* NB: do not toggle off bStdFormat! (if necessary SUCF will do) */ + bForcedChiralFlag &= ~FLAG_SET_INP_AT_CHIRAL; + bForcedChiralFlag |= FLAG_SET_INP_AT_NONCHIRAL; + } + + /*--- Non-std InChI creation options ---*/ + + else if ( !inchi_stricmp( pArg, "SUU" ) ) + { + /* include omitted undef/unknown stereo */ + bVer1DefaultMode &= ~(REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU); + bStdFormat = 0; + } + else if ( !inchi_stricmp( pArg, "SLUUD" ) ) + { + /* make labels for unknown and undefined stereo different */ + bVer1DefaultMode |= REQ_MODE_DIFF_UU_STEREO; + bStdFormat = 0; + } + /* FixedH */ + else if ( !inchi_stricmp( pArg, "FIXEDH" ) ) + { + bVer1DefaultMode |= REQ_MODE_BASIC; /* tautomeric */ + bStdFormat = 0; + } + /* RecMet */ + else if ( !inchi_stricmp( pArg, "RECMET" ) ) + { + /* reconnect metals */ + bReconnectCoord = 1; + bStdFormat = 0; + } + +#if ( KETO_ENOL_TAUT == 1 ) + else if ( !inchi_stricmp( pArg, "KET" ) ) + { + bKetoEnolTaut = 1; + bStdFormat = 0; + } +#endif + +#if ( TAUT_15_NON_RING == 1 ) + else if ( !inchi_stricmp( pArg, "15T" ) ) + { + b15TautNonRing = 1; + bStdFormat = 0; + } +#endif + +#endif + + /*--- Generation options ---*/ + + /* InChIKey/InChI hash */ + else if ( !inchi_stricmp( pArg, "Key" ) ) + { + bHashKey = 1; + } + else if ( !inchi_stricmp( pArg, "XHash1" ) ) + { + bHashXtra1 = 1; + } + else if ( !inchi_stricmp( pArg, "XHash2" ) ) + { + bHashXtra2 = 1; + } + + /*--- (engineering) Undo bug/draw fixes options ---*/ + +#ifdef BUILD_WITH_ENG_OPTIONS + + else if ( !inchi_stricmp( pArg, "FixSp3bugOFF" ) ) + { + bFixSp3bug = 0; + bStdFormat = 0; + } + else if ( !inchi_stricmp( pArg, "FBOFF" ) ) + { + bFixSp3bug = 0; + bStdFormat = 0; + } + else if ( !inchi_stricmp( pArg, "FB2OFF" ) ) + { + bFixFB2 = 0; + bStdFormat = 0; + } + else if ( !inchi_stricmp( pArg, "SPXYZOFF" ) ) + { + bAddPhosphineStereo = 0; + bStdFormat = 0; + } + else if ( !inchi_stricmp( pArg, "SASXYZOFF" ) ) + { + bAddArsineStereo = 0; + bStdFormat = 0; + } + else if ( !inchi_stricmp( pArg, "FNUDOFF" ) ) + { + ip->bFixNonUniformDraw = 0; + bStdFormat = 0; + } + + /*--- (hidden) Old structure-perception and InChI creation options ---*/ + /*--- (engineering) Old structure-perception and InChI creation options ---*/ + + else if ( !inchi_stricmp( pArg, "NOUUSB" ) ) + { + nMode |= REQ_MODE_SB_IGN_ALL_UU; + bStdFormat = 0; + } + else if ( !inchi_stricmp( pArg, "NOUUSC" ) ) + { + nMode |= REQ_MODE_SC_IGN_ALL_UU; + bStdFormat = 0; + } + +#if ( FIX_ADJ_RAD == 1 ) + else if ( !inchi_stricmp( pArg, "FixRad" ) ) + { + bFixAdjacentRad = 1; + bStdFormat = 0; + } +#endif + +#if ( UNDERIVATIZE == 1 ) + else if ( !inchi_stricmp( pArg, "DoDRV" ) ) + { + ip->bUnderivatize = 1; + bStdFormat = 0; + } +#endif + +#if ( RING2CHAIN == 1 ) + else if ( !inchi_stricmp( pArg, "DoR2C" ) ) + { + ip->bRing2Chain = 1; + bStdFormat = 0; + } +#endif + +#if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) + else if ( !inchi_stricmp( pArg, "DoneOnly" ) ) + { + ip->bIngnoreUnchanged = 1; + bStdFormat = 0; + } +#endif + + else if ( !inchi_stricmp( pArg, "NoADP" ) ) + { + bTgFlagHardAddRenProtons = 0; + bStdFormat = 0; + } + else if ( !inchi_memicmp( pArg, "DISCONSALT:", 11 ) ) + { + bDisconnectSalts = (0 != strtol(pArg+11, NULL, 10)); + bStdFormat = 0; + } + else if ( !inchi_memicmp( pArg, "DISCONMETAL:", 12 ) ) + { + bDisconnectCoord = (0 != strtol(pArg+12, NULL, 10)); + bStdFormat = 0; + } + else if ( !inchi_memicmp( pArg, "RECONMETAL:", 11 ) ) + { + bReconnectCoord = (0 != strtol(pArg+11, NULL, 10)); + bStdFormat = 0; + } + else if ( !inchi_memicmp( pArg, "DISCONMETALCHKVAL:", 18 ) ) + { + bDisconnectCoordChkVal = (0 != strtol(pArg+18, NULL, 10)); + bStdFormat = 0; + } + else if ( !inchi_memicmp( pArg, "MOVEPOS:", 8 ) ) + { + bMovePositiveCharges = (0 != strtol(pArg+8, NULL, 10)); + bStdFormat = 0; + } + else if ( !inchi_memicmp( pArg, "MERGESALTTG:", 12 ) ) + { + bMergeSaltTGroups = (0 != strtol(pArg+12, NULL, 10)); + bStdFormat = 0; + } + else if ( !inchi_memicmp( pArg, "UNCHARGEDACIDS:", 15) ) + { + bUnchargedAcidTaut = (0 != strtol(pArg+15, NULL, 16)); + bStdFormat = 0; + } + else if ( !inchi_memicmp( pArg, "ACIDTAUT:", 9 ) ) + { + bAcidTautomerism = c = (int)strtol(pArg+9, NULL, 10); + if ( 0 <= c && c <= 2 ) bAcidTautomerism = c; + /*else bNotRecognized = 2*bReleaseVersion;*/ + bStdFormat = 0; + } + + /*--- (hidden) Old output and other options ---*/ + + else if ( !inchi_memicmp( pArg, "O:", 2 ) ) + { + bNameSuffix = 1; + strncpy(szNameSuffix, pArg+2, sizeof(szNameSuffix)-1); + } + else if ( !inchi_memicmp( pArg, "OP:", 3 ) ) + { + bOutputPath = 1; + strncpy(szOutputPath, pArg+3, sizeof(szOutputPath)-1); + } + else if ( !inchi_stricmp( pArg, "ALT" ) ) + { + ip->bAbcNumbers = 1; + bStdFormat = 0; + } + else if ( !inchi_stricmp( pArg, "SCT" ) ) + { + ip->bCtPredecessors = 1; + bStdFormat = 0; + } + else if ( !inchi_stricmp( pArg, "CMP" ) ) + { + bCompareComponents = CMP_COMPONENTS; + } + else if ( !inchi_stricmp( pArg, "CMPNONISO" ) ) + { + bCompareComponents = CMP_COMPONENTS | CMP_COMPONENTS_NONISO; + } + else if ( !inchi_stricmp( pArg, "PW" ) ) + { + ip->bSaveWarningStructsAsProblem = 1; + } + +#endif /* BUILD_WITH_ENG_OPTIONS */ + + else + { + /*for ( k = 0; c=pArg[k]; k ++ )*/ + k = 0; + c=pArg[k]; /* prohibit multiple option concatenations, strict syntax check 2008-11-05 DT */ + { + c = toupper( c ); + switch ( c ) + { + case 'D': + bDisplay |= 1; + if ( (pArg[k+1] == 'C' || pArg[k+1] == 'c') && !pArg[k+2] ) + { + bDisplay |= 1; + k++; + ip->bDisplayEachComponentINChI = 1; + } + else if ( !pArg[k+1] ) + { + bDisplay |= 1; + } + break; + case 'W': + if ( pArg[k+1] == 'D' ) + { + /* restore Display Time functionality */ + c = 'D'; + k ++; + } + t = strtod( pArg+k+1, (char**)&q ); /* cast deliberately discards 'const' qualifier */ + if ( q > pArg+k+1 && errno == ERANGE || t < 0.0 || t*1000.0 > (double)ULONG_MAX) + { + ul = 0; + } + else + { + ul = (unsigned long)(t*1000.0); + } + if ( /*q > pArg+k &&*/ !*q ) + { + k = q - pArg - 1; /* k will be incremented by the for() cycle */ + switch( c ) + { + case 'D': + *ulDisplTime = ul; + break; + case 'W': + ip->msec_MaxTime = ul; + break; + } + } + break; + case 'F': + c = (int)strtol( pArg+k+1, (char**)&q, 10 ); /* cast deliberately discards 'const' qualifier */ + if ( q > pArg+k && !*q ) + { + k = q - pArg - 1; + if ( abs(c) > 5 ) + { + nFontSize = -c; /* font size 5 or less is too small */ + } + } + break; + default: + if ( !pArg[k+1] ) + { + switch ( c ) + { + case 'B': + nMode |= REQ_MODE_BASIC; + nReleaseMode = 0; + /*bNotRecognized = bReleaseVersion;*/ + bStdFormat = 0; + break; + case 'T': + nMode |= REQ_MODE_TAUT; + nReleaseMode = 0; + /*bNotRecognized = bReleaseVersion;*/ + break; + case 'I': + nMode |= REQ_MODE_ISO; + nReleaseMode = 0; + /*bNotRecognized = bReleaseVersion;*/ + break; + case 'N': + nMode |= REQ_MODE_NON_ISO; + nReleaseMode = 0; + bStdFormat = 0; + /*bNotRecognized = bReleaseVersion;*/ + break; + case 'S': + nMode |= REQ_MODE_STEREO; + nReleaseMode = 0; + /*bNotRecognized = bReleaseVersion;*/ + break; + case 'E': + if ( nReleaseMode & REQ_MODE_STEREO ) + { + nReleaseMode ^= REQ_MODE_STEREO; + bStdFormat = 0; + } + break; + +#ifndef TARGET_LIB_FOR_WINCHI + default: + inchi_ios_eprint(log_file, "Unrecognized option: \"%c\".\n", c); + +#endif + } + } + + +#ifndef TARGET_LIB_FOR_WINCHI + else + { + inchi_ios_eprint(log_file, "Unrecognized option: \"%c\".\n", c); + } +#endif + } + /* + if ( bNotRecognized && bNotRecognized == bReleaseVersion ) { + inchi_ios_eprint(stderr, "Unrecognized option: \"%c\".\n", c); + bNotRecognized = 0; + } + */ + } + } + + + /* + if ( bNotRecognized && bNotRecognized == 2*bReleaseVersion ) + { + inchi_ios_eprint(stderr, "Unrecognized option: \"%s\".\n", argv[i]); + bNotRecognized = 0; + } + */ + } + + /*=== end of parsing TARGET_LIB_FOR_WINCHI GUI (and v. 0.9xx Beta)options ===*/ + + + else if ( (bVer1Options & 1) && INCHI_OPTION_PREFX == argv[i][0] && argv[i][1] ) + { + + /*=== parsing stand-alone/library InChI options ===*/ + + pArg = argv[i] + 1; + + bRecognizedOption = 2; + bVer1Options += 2; + /* always on: REQ_MODE_TAUT | REQ_MODE_ISO | REQ_MODE_STEREO */ + + + /*--- Input options ---*/ + if ( !inchi_stricmp( pArg, "STDIO" ) ) + { + bNameSuffix = 0; + } + else if ( !inchi_stricmp( pArg, "INPAUX" )) + { + if (INPUT_NONE == ip->nInputType) + ip->nInputType = INPUT_INCHI_PLAIN; + } + else if ( /* INPUT_NONE == ip->nInputType &&*/ + !inchi_memicmp( pArg, "SDF:", 4 ) ) + { + /* SDfile label */ + k = 0; + mystrncpy( ip->szSdfDataHeader, pArg+4, MAX_SDF_HEADER+1 ); + lrtrim( ip->szSdfDataHeader, &k ); + if ( k ) + { + ip->pSdfLabel = ip->szSdfDataHeader; + ip->pSdfValue = szSdfDataValue; + if ( INPUT_NONE == ip->nInputType ) + { + ip->nInputType = INPUT_SDFILE; + } + } + else + { + ip->pSdfLabel = NULL; + ip->pSdfValue = NULL; + if ( INPUT_NONE == ip->nInputType ) + { + ip->nInputType = INPUT_MOLFILE; + } + } + } + + else if ( !inchi_memicmp( pArg, "START:", 6 ) ) + { + ip->first_struct_number = strtol(pArg+6, NULL, 10); + } + else if ( !inchi_memicmp( pArg, "END:", 4 ) ) + { + ip->last_struct_number = strtol(pArg+4, NULL, 10); + } + else if ( !inchi_memicmp( pArg, "RECORD:", 7 ) ) + { + long num = strtol(pArg+7, NULL, 10); + ip->first_struct_number = num; + ip->last_struct_number = num; + } + +#ifdef BUILD_WITH_ENG_OPTIONS + else if ( !inchi_memicmp( pArg, "RSB:", 4 )) + { + mdbr = (int)strtol(pArg+4, NULL, 10); + } +#endif + + /*--- Output options ---*/ + +#if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) ) + else if ( !inchi_stricmp( pArg, "Tabbed" ) ) + { + bOutputStyle |= INCHI_OUT_TABBED_OUTPUT; + } +#endif + + else if ( !inchi_stricmp( pArg, "NOLABELS" ) ) + { + bNoStructLabels = 1; + } + else if ( !inchi_stricmp( pArg, "SAVEOPT" ) ) + { + bINChIOutputOptions |= INCHI_OUT_SAVEOPT; + } + else if ( !inchi_stricmp( pArg, "AUXNONE" ) ) + { + /* no aux. info */ + bINChIOutputOptions |= INCHI_OUT_NO_AUX_INFO; /* no aux info */ + bINChIOutputOptions &= ~INCHI_OUT_SHORT_AUX_INFO; + } +#ifdef ERR_INCHI_STRING_ALLOWED + else if ( !inchi_stricmp( pArg, "OUTERRINCHI" ) ) + { + /* Signify InChI error generation on InChI strings output, not only report to log file */ + bINChIOutputOptions2 |= INCHI_OUT_INCHI_GEN_ERROR; + } +#endif + else if ( !inchi_stricmp( pArg, "MISMATCHISERROR" ) ) + { + /* Consider InChI conversion "problem/mismatch" as error */ + bINChIOutputOptions2 |= INCHI_OUT_MISMATCH_AS_ERROR; + } + + else if ( !inchi_stricmp( pArg, "OUTPUTSDF" ) ) + { + /* output SDfile */ + bOutputMolfileOnly = 1; + } + else if ( !inchi_stricmp( pArg, "SdfAtomsDT" ) ) + { + /* output isotopes H as D and T in SDfile */ + bOutputMolfileDT = 1; + } + else if ( !inchi_stricmp( pArg, "D" ) ) + { + /* display the structures */ + bDisplay |= 1; + } + else if ( !inchi_memicmp( pArg, "F", 1 ) && (c = (int)strtol( pArg+1, (char**)&q, 10 ), q > pArg+1) ) + { + nFontSize = -c; /* struct. display font size */ + } + else if ( !inchi_stricmp( pArg, "EQU" ) ) + { + bCompareComponents = CMP_COMPONENTS; + } + +#if( OUTPUT_FILE_EXT == 1 ) + else if ( pArg[0]=='.' && numOutNameExt < (int)(sizeof(szOutNameExt)/sizeof(szOutNameExt[0])) ) + { + strncpy(szOutNameExt[numOutNameExt], pArg, sizeof(szOutNameExt[0])-1); + numOutNameExt ++; + if ( ip->path[numOutNameExt] ) + strcpy( ip->path[numOutNameExt], ""); + } +#endif + + /*--- All modes (std and non-std InChI) structure perception options ---*/ + + else if ( !inchi_stricmp( pArg, "SNON" ) ) + { + bVer1DefaultMode &= ~REQ_MODE_STEREO; /* no stereo */ + nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); + } + else if ( !inchi_stricmp( pArg, "NEWPSOFF" ) ) + { + bPointedEdgeStereo = 0; + } + else if ( !inchi_stricmp( pArg, "DONOTADDH" ) ) + { + bDoNotAddH = 1; + } + + + /* Non-standard */ + +#ifndef USE_STDINCHI_API + + /* Non-std InChI structure perception options */ + else if ( !inchi_stricmp( pArg, "SREL" ) ) + { + bVer1DefaultMode |= REQ_MODE_STEREO; /* relative stereo */ + nMode &= ~(REQ_MODE_RACEMIC_STEREO | REQ_MODE_CHIR_FLG_STEREO); + nMode |= REQ_MODE_RELATIVE_STEREO; + bStdFormat = 0; + } + else if ( !inchi_stricmp( pArg, "SRAC" ) ) + { + /* REQ_MODE_CHIR_FLG_STEREO */ + bVer1DefaultMode |= REQ_MODE_STEREO; /* racemic stereo */ + nMode &= ~(REQ_MODE_RELATIVE_STEREO | REQ_MODE_CHIR_FLG_STEREO); + nMode |= REQ_MODE_RACEMIC_STEREO; + bStdFormat = 0; + } + else if ( !inchi_stricmp( pArg, "SUCF" ) ) + { + bVer1DefaultMode |= REQ_MODE_STEREO; /* stereo defined by the Chiral flag */ + nMode &= ~(REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO); + nMode |= REQ_MODE_CHIR_FLG_STEREO; + bStdFormat = 0; + } + else if ( !inchi_stricmp( pArg, "ChiralFlagON" ) ) + { + /* used only with /SUCF */ + /* NB: do not toggle off bStdFormat! (if necessary SUCF will do) */ + bForcedChiralFlag &= ~FLAG_SET_INP_AT_NONCHIRAL; + bForcedChiralFlag |= FLAG_SET_INP_AT_CHIRAL; + } + else if ( !inchi_stricmp( pArg, "ChiralFlagOFF" ) ) + { + /* used only with /SUCF */ + /* NB: do not toggle off bStdFormat! (if necessary SUCF will do) */ + bForcedChiralFlag &= ~FLAG_SET_INP_AT_CHIRAL; + bForcedChiralFlag |= FLAG_SET_INP_AT_NONCHIRAL; + } + + + /*--- Non-std InChI creation options ---*/ + + /* Stereo */ + else if ( !inchi_stricmp( pArg, "SUU" ) ) + { + /* include omitted undef/unknown stereo */ + bVer1DefaultMode &= ~(REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU); + bStdFormat = 0; + } + else if ( !inchi_stricmp( pArg, "SLUUD" ) ) + { + /* Make labels for unknown and undefined stereo different */ + bVer1DefaultMode |= REQ_MODE_DIFF_UU_STEREO; + bStdFormat = 0; + } + /* FixedH */ + else if ( !inchi_stricmp( pArg, "FIXEDH" ) ) + { + bVer1DefaultMode |= REQ_MODE_BASIC; /* tautomeric */ + bStdFormat = 0; + } + /* RecMet */ + else if ( !inchi_stricmp( pArg, "RECMET" ) ) + { + /* reconnect metals */ + bReconnectCoord = 1; + bStdFormat = 0; + } + +#if ( KETO_ENOL_TAUT == 1 ) + else if ( !inchi_stricmp( pArg, "KET" ) ) + { + bKetoEnolTaut = 1; + bStdFormat = 0; + } +#endif + +#if ( TAUT_15_NON_RING == 1 ) + else if ( !inchi_stricmp( pArg, "15T" ) ) + { + b15TautNonRing = 1; + bStdFormat = 0; + } +#endif + +#endif + + /*--- Generation options ---*/ + else if ( !inchi_memicmp( pArg, "W", 1 ) && (t = strtod( pArg+1, (char**)&q ), q > pArg+1) ) + { + if ( errno == ERANGE || t < 0.0 || t*1000.0 > (double)ULONG_MAX) + { + ul = 0; + } + else + { + ul = (unsigned long)(t*1000.0); /* max. time per structure */ + } + ip->msec_MaxTime = ul; + } + else if ( !inchi_stricmp( pArg, "WarnOnEmptyStructure" ) ) + { + ip->bAllowEmptyStructure = 1; + } + else if ( !inchi_stricmp( pArg, "LargeMolecules" ) ) + { + bLargeMolecules = 1; + } + else if ( !inchi_stricmp( pArg, "Polymers" ) ) + { + bPolymers = 1; + } + /* InChIKey/InChI hash */ + else if ( !inchi_stricmp( pArg, "Key" ) ) + { + bHashKey = 1; + } + else if ( !inchi_stricmp( pArg, "XHash1" ) ) + { + bHashXtra1 = 1; + } + else if ( !inchi_stricmp( pArg, "XHash2" ) ) + { + bHashXtra2 = 1; + } + + /*--- Conversion modes ---*/ + +#if ( READ_INCHI_STRING == 1 ) + else if ( !inchi_stricmp( pArg, "InChI2InChI" ) ) + { + /* Read InChI Identifiers and output InChI Identifiers */ + ip->nInputType = INPUT_INCHI; + ip->bReadInChIOptions |= READ_INCHI_OUTPUT_INCHI; + ip->bReadInChIOptions &= ~READ_INCHI_TO_STRUCTURE; + } + else if ( !inchi_stricmp( pArg, "InChI2Struct" ) ) + { + /* Split InChI Identifiers into components */ + ip->bReadInChIOptions |= READ_INCHI_TO_STRUCTURE; + ip->bReadInChIOptions &= ~READ_INCHI_OUTPUT_INCHI; + ip->nInputType = INPUT_INCHI; + } + +#ifdef BUILD_WITH_ENG_OPTIONS + else if ( !inchi_stricmp( pArg, "KeepBalanceP" ) ) + { + /* When spliting InChI Identifiers into components: */ + /* If MobileH output then add p to each component; */ + /* Otherwise add one more component containing balance */ + /* of protons and exchangeable isotopic H */ + ip->bReadInChIOptions |= READ_INCHI_KEEP_BALANCE_P; + bStdFormat = 0; + } +#endif +#endif + + + /*--- (engineering) Undo bug/draw fixes options ---*/ + +#ifdef BUILD_WITH_ENG_OPTIONS + else if ( !inchi_stricmp( pArg, "FixSp3bugOFF" ) ) + { + bFixSp3bug = 0; + bStdFormat = 0; + } + else if ( !inchi_stricmp( pArg, "FBOFF" ) ) + { + bFixSp3bug = 0; + bStdFormat = 0; + } + else if ( !inchi_stricmp( pArg, "FB2OFF" ) ) + { + bFixFB2 = 0; + bStdFormat = 0; + } + else if ( !inchi_stricmp( pArg, "SPXYZOFF" ) ) + { + bAddPhosphineStereo = 0; + bStdFormat = 0; + } + else if ( !inchi_stricmp( pArg, "SASXYZOFF" ) ) + { + bAddArsineStereo = 0; + bStdFormat = 0; + } + else if ( !inchi_stricmp( pArg, "FNUDOFF" ) ) + { + ip->bFixNonUniformDraw = 0; + bStdFormat = 0; + } + + /*--- (engineering) Old structure-perception and InChI creation options ---*/ + +#if ( FIX_ADJ_RAD == 1 ) + else if ( !inchi_stricmp( pArg, "FixRad" ) ) + { + bFixAdjacentRad = 1; + bStdFormat = 0; + } +#endif + +#if ( UNDERIVATIZE == 1 ) + else if ( !inchi_stricmp( pArg, "DoDRV" ) ) + { + ip->bUnderivatize = 1; + bStdFormat = 0; + } +#endif + +#if ( RING2CHAIN == 1 ) + else if ( !inchi_stricmp( pArg, "DoR2C" ) ) + { + ip->bRing2Chain = 1; + bStdFormat = 0; + } +#endif + +#if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) + else if ( !inchi_stricmp( pArg, "DoneOnly" ) ) + { + ip->bIngnoreUnchanged = 1; + bStdFormat = 0; + } +#endif + + else if ( !inchi_memicmp( pArg, "MOVEPOS:", 8 ) ) + { + bMovePositiveCharges = (0 != strtol(pArg+8, NULL, 10)); + bStdFormat = 0; + } + + else if ( !inchi_stricmp( pArg, "NoADP" ) ) + { + bTgFlagHardAddRenProtons = 0; + bStdFormat = 0; + } + /* Tautomer perception off */ + else if ( !inchi_stricmp( pArg, "EXACT" ) ) + { + bVer1DefaultMode |= REQ_MODE_BASIC; + bStdFormat = 0; + } + else if ( !inchi_stricmp( pArg, "ONLYRECSALT" ) ) + { + /* do not disconnect salts */ + bDisconnectSalts = 0; + bStdFormat = 0; + } + else if ( !inchi_stricmp( pArg, "ONLYEXACT" ) || !inchi_stricmp( pArg, "ONLYFIXEDH" ) ) + { + bVer1DefaultMode |= REQ_MODE_BASIC; + bVer1DefaultMode &= ~REQ_MODE_TAUT; + bStdFormat = 0; + } + else if ( !inchi_stricmp( pArg, "ONLYNONISO" ) ) + { + bVer1DefaultMode |= REQ_MODE_NON_ISO; + bVer1DefaultMode &= ~REQ_MODE_ISO; + bStdFormat = 0; + } + else if ( !inchi_stricmp( pArg, "TAUT" ) ) + { + bVer1DefaultMode &= ~REQ_MODE_BASIC; + bVer1DefaultMode |= REQ_MODE_TAUT; + } + else if ( !inchi_stricmp( pArg, "ONLYRECMET" ) ) + { + /* do not disconnect metals */ + bDisconnectCoord = 0; + bStdFormat = 0; + } + + /*--- (hidden) Old output and other options ---*/ + + else if ( !inchi_stricmp( pArg, "SdfSplit" ) ) + { + /* Split single Molfiles into disconnected components */ + bOutputMolfileSplit = 1; + } + else if ( !inchi_stricmp( pArg, "DCR" ) ) + { + bDisplayCompositeResults = 1; + } + else if ( !inchi_stricmp( pArg, "AUXFULL" ) || !inchi_stricmp( pArg, "AUXMAX" ) ) + { + /* full aux info */ + bINChIOutputOptions &= ~(INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO); /* include short aux info */ + } + else if ( !inchi_stricmp( pArg, "AUXMIN" ) ) + { + /* minimal aux info */ + bINChIOutputOptions &= ~INCHI_OUT_NO_AUX_INFO; /* include short aux info */ + bINChIOutputOptions |= INCHI_OUT_SHORT_AUX_INFO; + } + +#if ( READ_INCHI_STRING == 1 ) + else if ( !inchi_stricmp( pArg, "DDSRC" ) ) + { + bDisplayIfRestoreWarnings = 1; /* InChI->Structure debugging: Display Debug Structure Restore Components */ + } +#endif + + else if ( !inchi_stricmp( pArg, "NoVarH" ) ) + { + bTgFlagVariableProtons = 0; + } + else if ( !inchi_stricmp( pArg, "FULL" ) ) + { + bVer1DefaultMode = VER103_DEFAULT_MODE; + nMode = 0; + bReconnectCoord = 1; /* full output */ + bINChIOutputOptions = ((EMBED_REC_METALS_INCHI==1)? INCHI_OUT_EMBED_REC : 0) | INCHI_OUT_SHORT_AUX_INFO; + ip->bCtPredecessors = 0; + ip->bAbcNumbers = 0; + bOutputStyle |= INCHI_OUT_PLAIN_TEXT | INCHI_OUT_PLAIN_TEXT_COMMENTS; + } + else if ( !inchi_stricmp( pArg, "MIN" ) ) + { + bVer1DefaultMode = VER103_DEFAULT_MODE; + nMode = 0; + bReconnectCoord = 1; /* minimal output */ + bINChIOutputOptions = ((EMBED_REC_METALS_INCHI==1)? INCHI_OUT_EMBED_REC : 0) | INCHI_OUT_NO_AUX_INFO; /* minimal compressed output */ + ip->bCtPredecessors = 1; + ip->bAbcNumbers = 1; + bOutputStyle |= INCHI_OUT_PLAIN_TEXT | INCHI_OUT_PLAIN_TEXT_COMMENTS; + } + else if ( !inchi_stricmp( pArg, "COMPRESS" ) ) + { + ip->bAbcNumbers = 1; + ip->bCtPredecessors = 1; /* compressed output */ + } + +#if ( READ_INCHI_STRING == 1 ) + else if ( !inchi_stricmp( pArg, "InChI2InChI" ) ) + { + /* Read InChI Identifiers and output InChI Identifiers */ + ip->nInputType = INPUT_INCHI; + ip->bReadInChIOptions |= READ_INCHI_OUTPUT_INCHI; + ip->bReadInChIOptions &= ~READ_INCHI_TO_STRUCTURE; + } + + else if ( !inchi_stricmp( pArg, "SplitInChI" ) ) + { + /* Split InChI Identifiers into components */ + ip->bReadInChIOptions |= READ_INCHI_SPLIT_OUTPUT; + } +#endif + + else if ( !inchi_stricmp( pArg, "MOLFILENUMBER" ) ) + { + ip->bGetMolfileNumber |= 1; + } + else if ( !inchi_stricmp( pArg, "OutputPLAIN" ) ) + { + bOutputStyle |= INCHI_OUT_PLAIN_TEXT; + } + else if ( !inchi_stricmp( pArg, "OutputANNPLAIN" ) ) + { + bOutputStyle |= INCHI_OUT_PLAIN_TEXT_COMMENTS; + bOutputStyle |= INCHI_OUT_WINCHI_WINDOW; /* debug */ + } + else if ( !inchi_stricmp( pArg, "ONLYEXACT" ) || !inchi_stricmp( pArg, "ONLYFIXEDH" ) ) { + bVer1DefaultMode |= REQ_MODE_BASIC; + bVer1DefaultMode &= ~REQ_MODE_TAUT; + } + else if ( !inchi_stricmp( pArg, "ONLYNONISO" ) ) { + bVer1DefaultMode |= REQ_MODE_NON_ISO; + bVer1DefaultMode &= ~REQ_MODE_ISO; + } + else if ( !inchi_stricmp( pArg, "TAUT" ) ) { + bVer1DefaultMode &= ~REQ_MODE_BASIC; + bVer1DefaultMode |= REQ_MODE_TAUT; + } + else if ( !inchi_stricmp( pArg, "ONLYRECMET" ) ) { /* do not disconnect metals */ + bDisconnectCoord = 0; + } + else if ( !inchi_stricmp( pArg, "ONLYRECSALT" ) ) { /* do not disconnect salts */ + bDisconnectSalts = 0; + } + else if ( !inchi_memicmp( pArg, "MOVEPOS:", 8 ) ) { /* added -- 2010-03-01 DT */ + bMovePositiveCharges = (0 != strtol(pArg+8, NULL, 10)); + } + else if ( !inchi_memicmp( pArg, "RSB:", 4 )) { + mdbr = (int)strtol(pArg+4, NULL, 10); + } + else if ( !inchi_stricmp( pArg, "EQU" ) ) { + bCompareComponents = CMP_COMPONENTS; + } + else if ( !inchi_stricmp( pArg, "EQUNONISO" ) ) + { + bCompareComponents = CMP_COMPONENTS | CMP_COMPONENTS_NONISO; + } + else if ( !inchi_memicmp( pArg, "OP:", 3 ) ) + { + bOutputPath = 1; + strncpy(szOutputPath, pArg+3, sizeof(szOutputPath)-1); + } + +#endif /* BUILD_WITH_ENG_OPTIONS */ + + /* Display unrecognized option */ + + else + { + bRecognizedOption = 0; + +#ifndef TARGET_LIB_FOR_WINCHI + inchi_ios_eprint(log_file, "Unrecognized option: \"%s\".\n", pArg); +#endif + } + bVer1Options |= bRecognizedOption; + } + + /*=== end of parsing stand-alone/library InChI options ===*/ + + + else if ( ip->num_paths < MAX_NUM_PATHS ) + { + char *sz; +#if( ALLOW_EMPTY_PATHS == 1 ) + if ( argv[i] ) +#else + if ( argv[i] && argv[i][0] ) +#endif + { + if ( sz = (char*) inchi_malloc( (strlen(argv[i]) + 1)*sizeof(sz[0])) ) + { + strcpy( sz, argv[i] ); + } + + ip->path[ip->num_paths++] = sz; + } + } + } /* for ( i = 1; i < argc; i ++ ) */ + + + + if ( bHashKey != 0 ) + /* Suppress InChIKey calculation if: + compressed output OR Inchi2Struct OR Inchi2Inchi */ + { + + if ( (ip->bAbcNumbers ==1) && (ip->bCtPredecessors == 1) ) + { + +#ifndef TARGET_LIB_FOR_WINCHI + inchi_ios_eprint(log_file, "Terminating: generation of InChIKey is not available with 'Compress' option\n"); + return -1; +#endif + + bHashKey = 0; + } + if ( ip->nInputType == INPUT_INCHI ) + { + +#ifndef TARGET_LIB_FOR_WINCHI + inchi_ios_eprint(log_file, "Terminating: generation of InChIKey is not available in InChI conversion mode\n"); + return -1; +#endif + + bHashKey = 0; + } + else + if ( bOutputMolfileOnly == 1 ) + { + +#ifndef TARGET_LIB_FOR_WINCHI + inchi_ios_eprint(log_file, "Terminating: generation of InChIKey is not available with 'OutputSDF' option\n"); + return -1; +#endif + + bHashKey = 0; + } + } + + + if ( bNameSuffix || bOutputPath ) + { + const char *p = NULL; + char *r = NULL; + char *sz; + int len; + const char szNUL[] = "NUL"; /* fix for AMD processor: use const char[] instead of just "NUL" constant 2008-11-5 DT */ + + /* find the 1st path */ + for ( i = 0; i < MAX_NUM_PATHS; i ++ ) + { + if ( !p && ip->path[i] && ip->path[i][0] ) + { + p = ip->path[i]; + break; + } + } + /* fix output path */ + if ( bOutputPath && szOutputPath[0] && p ) + { + /* remove last slash */ + len = (int) strlen(szOutputPath); + if ( len > 0 && szOutputPath[len-1] != INCHI_PATH_DELIM ) + { + szOutputPath[len++] = INCHI_PATH_DELIM; + szOutputPath[len] = '\0'; + } + if ( len > 0 && (r = (char *)strrchr( p, INCHI_PATH_DELIM ) ) && r[1] ) + { + strcat( szOutputPath, r+1 ); + p = szOutputPath; + } + } /* add missing paths */ + for ( i = 0; p && i < MAX_NUM_PATHS; i ++ ) + { + /* fix for AMD processor: changed order 2008-11-5 DT */ + if ( !ip->path[i] || !ip->path[i][0] ) + { +#if ( BUILD_WITH_AMI == 1 ) && ( OUTPUT_FILE_EXT == 1 ) + char *pLastExt = (i && numOutNameExt >= i) ? strrchr(p,'.') : 0; + char *pLastSlash = (i && numOutNameExt >= i) ? strrchr(p,INCHI_PATH_DELIM) : 0; + if ( pLastExt && pLastSlash && pLastSlash > pLastExt ) + pLastExt = NULL; +#else + char *pLastExt = NULL; +#endif + len = (int) strlen( p ) + strlen(szNameSuffix) + strlen( ext[i] ); + if ( sz = (char*) inchi_malloc( (len+1)*sizeof(sz[0]) ) ) + { + strcpy( sz, p ); +#if ( BUILD_WITH_AMI == 1 ) && ( OUTPUT_FILE_EXT == 1 ) + if ( pLastExt ) + { + strcpy( sz + (pLastExt-p), szOutNameExt[i-1] ); + } +#endif + strcat( sz, szNameSuffix ); + if ( !pLastExt ) + strcat( sz, ext[i] ); + ip->num_paths++; + if ( ip->path[i] ) + { + inchi_free( (char*)ip->path[i] ); /* eliminate memory leak 2013-12-18 DCh */ + } + ip->path[i] =sz; + } + } + else if ( !inchi_stricmp( ip->path[i], szNUL ) ) + { + inchi_free( (char *)ip->path[i] ); /* cast deliberately const qualifier */ + ip->path[i] = NULL; + } + } + } + + +#if ( READ_INCHI_STRING == 1 ) + if ( INPUT_INCHI == ip->nInputType ) + { + bCompareComponents = 0; + /*bDisplayCompositeResults = 0;*/ + +#if ( I2S_MODIFY_OUTPUT == 1 ) + if ( !(ip->bReadInChIOptions & READ_INCHI_TO_STRUCTURE ) ) +#endif + + { + bOutputMolfileOnly = 0; + /*bNoStructLabels = 1;*/ + bINChIOutputOptions |= INCHI_OUT_NO_AUX_INFO; + bINChIOutputOptions &= ~INCHI_OUT_SHORT_AUX_INFO; + bINChIOutputOptions &= ~INCHI_OUT_ONLY_AUX_INFO; + } + ip->bDisplayIfRestoreWarnings = bDisplayIfRestoreWarnings; + if ( !(bINChIOutputOptions & + + (INCHI_OUT_SDFILE_ONLY | /* not in bINChIOutputOptions yet */ + INCHI_OUT_PLAIN_TEXT | /* not in bINChIOutputOptions yet */ + INCHI_OUT_PLAIN_TEXT_COMMENTS /* not in bINChIOutputOptions yet */ + ) ) +#if ( I2S_MODIFY_OUTPUT == 1 ) + && !bOutputMolfileOnly + && !(bOutputStyle & (INCHI_OUT_PLAIN_TEXT)) +#endif + ) + { + bINChIOutputOptions |= INCHI_OUT_PLAIN_TEXT; + } + } +#endif + + + if ( bVer1Options ) + { + nMode |= bVer1DefaultMode; + } + else if ( bReleaseVersion ) + { + nMode |= nReleaseMode; + } + +#if ( defined(COMPILE_ANSI_ONLY) || defined(TARGET_LIB_FOR_WINCHI) ) + if ( bCompareComponents && !(bDisplay & 1) ) { + bCompareComponents = 0; + } +#endif + + /* Save original options */ + /* nOrigMode = nMode; */ + +#ifndef COMPILE_ANSI_ONLY + ip->dp.sdp.nFontSize = nFontSize; + ip->dp.sdp.ulDisplTime = *ulDisplTime; + ip->bDisplay = bDisplay; + +#ifdef TARGET_LIB_FOR_WINCHI + ip->bDisplayCompositeResults = bDisplay; +#else + ip->bDisplayCompositeResults = bDisplayCompositeResults; +#endif + +#else + ip->bDisplayEachComponentINChI = 0; + bCompareComponents = 0; +#endif + + + ip->bMergeAllInputStructures = bMergeAllInputStructures; + ip->bDoNotAddH = bDoNotAddH; + /* set default options */ + if ( !nMode || nMode == REQ_MODE_STEREO ) + { + /* requested all output */ + nMode |= (REQ_MODE_BASIC | REQ_MODE_TAUT | REQ_MODE_ISO | REQ_MODE_NON_ISO | REQ_MODE_STEREO); + } else { + if ( !(nMode & (REQ_MODE_BASIC | REQ_MODE_TAUT)) ) + { + nMode |= (REQ_MODE_BASIC | REQ_MODE_TAUT); + } + if ( (nMode & REQ_MODE_STEREO) && !(nMode & (REQ_MODE_ISO | REQ_MODE_NON_ISO)) ) + { + nMode |= (REQ_MODE_ISO | REQ_MODE_NON_ISO); + } + } + /* if the user requested isotopic then unconditionally add non-isotopic output. */ + if ( nMode & REQ_MODE_ISO ) { + nMode |= REQ_MODE_NON_ISO; + } + +#if ( MIN_SB_RING_SIZE > 0 ) + if ( mdbr ) { + nMinDbRinSize = mdbr; + } + nMode |= (nMinDbRinSize << REQ_MODE_MIN_SB_RING_SHFT) & REQ_MODE_MIN_SB_RING_MASK; +#endif + + /* input file */ + if ( ip->nInputType == INPUT_NONE && ip->num_paths > 0 ) + { + ip->nInputType = INPUT_MOLFILE; /* default */ + } + ip->nMode = nMode; + if ( (bCompareComponents & CMP_COMPONENTS) && (nMode & REQ_MODE_BASIC) ) + { + bCompareComponents |= CMP_COMPONENTS_NONTAUT; /* compare non-tautomeric */ + } + ip->bCompareComponents = bCompareComponents; + + ip->bINChIOutputOptions = bINChIOutputOptions | (bOutputMolfileOnly? INCHI_OUT_SDFILE_ONLY : 0); + + if ( bOutputMolfileOnly ) + { + bOutputStyle &= ~( INCHI_OUT_PLAIN_TEXT | + INCHI_OUT_PLAIN_TEXT_COMMENTS | + INCHI_OUT_TABBED_OUTPUT); + +#if ( SDF_OUTPUT_DT == 1 ) + ip->bINChIOutputOptions |= bOutputMolfileDT? INCHI_OUT_SDFILE_ATOMS_DT : 0; + ip->bINChIOutputOptions |= bOutputMolfileSplit? INCHI_OUT_SDFILE_SPLIT : 0; +#endif + } + +#ifdef TARGET_LIB_FOR_WINCHI + if ( !(bDisplay & 1) ) + { + bOutputStyle &= ~(INCHI_OUT_PLAIN_TEXT_COMMENTS); /* do not ouput comments in wINChI text file results */ + } + else + { + bOutputStyle |= INCHI_OUT_WINCHI_WINDOW; + } +#endif + + ip->bINChIOutputOptions |= bOutputStyle; + ip->bNoStructLabels = bNoStructLabels; + + if ( bForcedChiralFlag ) { + ip->bChiralFlag = bForcedChiralFlag; + } + + /*******************************************/ + /* tautomeric/salts settings */ + /*******************************************/ + + ip->bTautFlags = 0; /* initialize */ + ip->bTautFlagsDone = 0; /* initialize */ + + /* find regular tautomerism */ + ip->bTautFlags |= TG_FLAG_TEST_TAUT__ATOMS; + /* disconnect salts */ + ip->bTautFlags |= bDisconnectSalts? TG_FLAG_DISCONNECT_SALTS : 0; + /* if possible find long-range H/(-) taut. on =C-OH, >C=O */ + ip->bTautFlags |= bAcidTautomerism? TG_FLAG_TEST_TAUT__SALTS : 0; + /* allow long-range movement of N(+), P(+) charges */ + ip->bTautFlags |= bMovePositiveCharges? TG_FLAG_MOVE_POS_CHARGES : 0; + /* multi-attachement long-range H/(-) taut. on =C-OH, >C=O */ + ip->bTautFlags |= (bAcidTautomerism > 1)? TG_FLAG_TEST_TAUT2_SALTS : 0; + /* (debug) allow to find long-range H-only tautomerism on =C-OH, >C=O */ + ip->bTautFlags |= (bUnchargedAcidTaut==1)? TG_FLAG_ALLOW_NO_NEGTV_O : 0; + /* merge =C-OH and >C=O containing t-groups and other =C-OH groups */ + ip->bTautFlags |= bMergeSaltTGroups? TG_FLAG_MERGE_TAUT_SALTS : 0; + ip->bTautFlags |= bDisconnectCoord? TG_FLAG_DISCONNECT_COORD : 0; + ip->bTautFlags |=(bDisconnectCoord && + bReconnectCoord)? TG_FLAG_RECONNECT_COORD : 0; + ip->bTautFlags |= bDisconnectCoordChkVal? TG_FLAG_CHECK_VALENCE_COORD : 0; + ip->bTautFlags |= bTgFlagVariableProtons? TG_FLAG_VARIABLE_PROTONS : 0; + ip->bTautFlags |= bTgFlagHardAddRenProtons? TG_FLAG_HARD_ADD_REM_PROTONS : 0; + ip->bTautFlags |= bKetoEnolTaut? TG_FLAG_KETO_ENOL_TAUT : 0; + ip->bTautFlags |= b15TautNonRing? TG_FLAG_1_5_TAUT : 0; + +#ifdef STEREO_WEDGE_ONLY + ip->bTautFlags |= bPointedEdgeStereo? TG_FLAG_POINTED_EDGE_STEREO : 0; +#endif +#if ( FIX_ADJ_RAD == 1 ) + ip->bTautFlags |= bFixAdjacentRad? TG_FLAG_FIX_ADJ_RADICALS : 0; +#endif + ip->bTautFlags |= bAddPhosphineStereo? TG_FLAG_PHOSPHINE_STEREO : 0; + ip->bTautFlags |= bAddArsineStereo? TG_FLAG_ARSINE_STEREO : 0; + ip->bTautFlags |= bFixSp3bug? TG_FLAG_FIX_SP3_BUG : 0; + + + if (bFixFB2) + { +#if ( FIX_ISO_FIXEDH_BUG == 1 ) + ip->bTautFlags |= TG_FLAG_FIX_ISO_FIXEDH_BUG; /* accomodate FIX_ISO_FIXEDH_BUG */ +#endif + +#if ( FIX_TERM_H_CHRG_BUG == 1 ) + ip->bTautFlags |= TG_FLAG_FIX_TERM_H_CHRG_BUG; /* accomodate FIX_TERM_H_CHRG_BUG */ +#endif + +#if ( FIX_TRANSPOSITION_CHARGE_BUG == 1 ) + ip->bINChIOutputOptions |= INCHI_OUT_FIX_TRANSPOSITION_CHARGE_BUG; +#endif + } + + + + + if ( !ip->nInputType ) + ip->nInputType = INPUT_MOLFILE; + + /* Check if /SNon requested turn OFF SUU/SLUUD */ + if ( ! (ip->nMode & REQ_MODE_STEREO) ) + { + ip->nMode &= ~REQ_MODE_DIFF_UU_STEREO; + ip->nMode &= ~(REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU); + } + + + /* Standard InChI ? */ + if (bStdFormat) + { + ip->bINChIOutputOptions |= INCHI_OUT_STDINCHI; + } + + /* InChIKey ? */ + if ( !bHashKey ) + ip->bCalcInChIHash = INCHIHASH_NONE; + else + ip->bCalcInChIHash = INCHIHASH_KEY; + + /* Extension(s) to hash (in non-std mode only) ? */ + if ( !bHashKey ) + { + if ( (bHashXtra1!=0) || (bHashXtra2!=0) ) + inchi_ios_eprint(log_file, + "Hash extension(s) not generated: InChIKey not requested"); + } + else + { + if ( bHashXtra1 ) + { + if ( bHashXtra2 ) ip->bCalcInChIHash = INCHIHASH_KEY_XTRA1_XTRA2; + else ip->bCalcInChIHash = INCHIHASH_KEY_XTRA1; + } + else if ( bHashXtra2 ) + { + ip->bCalcInChIHash = INCHIHASH_KEY_XTRA2; + } + } + + ip->bLargeMolecules = bLargeMolecules; + ip->bPolymers = bPolymers; +#ifdef TARGET_LIB_FOR_WINCHI + ip->bLargeMolecules = 1; + ip->bPolymers = 1; +#endif + + + ip->bINChIOutputOptions2 = bINChIOutputOptions2; + + return 0; +} + + +/****************************************************************************/ +int PrintInputParms( INCHI_IOSTREAM *log_file, + INPUT_PARMS *ip ) +{ +INCHI_MODE nMode = ip->nMode; +int k; +int bStdFormat = 1; +int first=1; + + +#ifdef TARGET_LIB_FOR_WINCHI + int bInChI2Struct = 0; /* as of December 2008, winchi-1 does not convert InChI to structure */ +#else + int bInChI2Struct = (ip->bReadInChIOptions & READ_INCHI_TO_STRUCTURE) && ip->nInputType == INPUT_INCHI; +#endif + + if ( !(ip->bINChIOutputOptions & INCHI_OUT_STDINCHI) ) + bStdFormat = 0; + + + /* some stereo */ + if ( ! (nMode & REQ_MODE_STEREO) ) + { + inchi_ios_eprint( log_file, "Using specific structure perception features:\n"); + first = 0; + inchi_ios_eprint( log_file, " Stereo OFF\n"); + } + else + { + if ( ! (TG_FLAG_POINTED_EDGE_STEREO & ip->bTautFlags) ) + { + if (first) + { + inchi_ios_eprint( log_file, "Using specific structure perception features:\n"); + first = 0; + } + inchi_ios_eprint( log_file, " Both ends of wedge point to stereocenters\n"); + } + } + if ( ip->bDoNotAddH ) + { + if (first) + inchi_ios_eprint( log_file, "Using specific structure perception features:\n"); + inchi_ios_eprint( log_file, " Do not add H\n"); + } + + + /* Generation/conversion indicator */ + if (bStdFormat) + { + if ( !(ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY) && !bInChI2Struct ) + inchi_ios_eprint( log_file, "Generating standard InChI\n" ); + +#if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) && !defined(TARGET_EXE_USING_API) ) + /* effective only in command line program InChI or stdInChI */ + else if ( bInChI2Struct ) + inchi_ios_eprint( log_file, "Converting InChI(s) to structure(s) in %s\n", + (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY)? + "MOL format" : "aux. info format" ); +#endif + } + else + inchi_ios_eprint( log_file, "Generating non-standard InChI with the options: \n" ); + + + /* SDfile output */ + + if ( ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY ) + { + inchi_ios_eprint( log_file, + "Output SDfile only without stereochemical information and atom coordinates%s\n", + (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ATOMS_DT)? + "\n(write H isotopes as D, T)":"" ); + } + + + /* Fixed/Mobile H */ + + if (!bStdFormat) + { + if( (nMode & (REQ_MODE_BASIC | REQ_MODE_TAUT )) == (REQ_MODE_BASIC | REQ_MODE_TAUT) ) + inchi_ios_eprint( log_file, " Mobile H Perception OFF (include FixedH layer)\n" ); + else if( (nMode & (REQ_MODE_BASIC | REQ_MODE_TAUT )) == (REQ_MODE_TAUT) ) + inchi_ios_eprint( log_file, " Mobile H Perception ON (omit FixedH layer)\n" ); + else if( (nMode & (REQ_MODE_BASIC | REQ_MODE_TAUT )) == (REQ_MODE_BASIC) ) + inchi_ios_eprint( log_file, " Mobile H ignored\n" ); + else + inchi_ios_eprint( log_file, " Undefined Mobile H mode\n" ); + + if ( (ip->bTautFlags & TG_FLAG_VARIABLE_PROTONS) ) + if ( !(ip->bTautFlags & TG_FLAG_HARD_ADD_REM_PROTONS) ) + inchi_ios_eprint( log_file, " Disabled Aggressive (De)protonation\n" ); + +#if ( FIND_RING_SYSTEMS != 1 ) + inchi_ios_eprint( log_file, " %s5-, 6-, 7-memb. ring taut. ignored\n", i?"; ":""); +#endif + + /* RecMet */ + + if ( ip->bTautFlags & TG_FLAG_DISCONNECT_COORD ) + { + if ( ip->bTautFlags & TG_FLAG_RECONNECT_COORD ) + inchi_ios_eprint( log_file, " Include bonds to metals\n"); + else + inchi_ios_eprint( log_file, " Do not reconnect metals (omit RecMet layer)\n"); + } + else + inchi_ios_eprint( log_file, " Do not disconnect metals\n"); + + /* isotopic - always ON, output disabled. 09-17-2009*/ + /* + if ( nMode & REQ_MODE_ISO ) + inchi_ios_eprint( log_file, " Isotopic ON\n"); + else if ( nMode & REQ_MODE_NON_ISO ) + inchi_ios_eprint( log_file, " Isotopic OFF\n"); + */ + +#if ( FIX_ADJ_RAD == 1 ) + if ( ip->bTautFlags & TG_FLAG_FIX_ADJ_RADICALS ) + inchi_ios_eprint( log_file, "Fix Adjacent Radicals\n" ); +#endif + + + /* Stereo */ + + if ( nMode & REQ_MODE_STEREO ) + { + inchi_ios_eprint( log_file, " %s%s%s%sStereo ON\n", + ( nMode & REQ_MODE_NOEQ_STEREO )? "Slow ":"", + ( nMode & REQ_MODE_REDNDNT_STEREO )? "Redund. ":"", + ( nMode & REQ_MODE_NO_ALT_SBONDS)? "No AltBond ":"", + + ( nMode & REQ_MODE_RACEMIC_STEREO)? "Racemic " : + ( nMode & REQ_MODE_RELATIVE_STEREO)? "Relative " : + ( nMode & REQ_MODE_CHIR_FLG_STEREO)? "Chiral Flag " : "Absolute " ); + if ( 0 == (nMode & (REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU)) ) + inchi_ios_eprint( log_file, " Include undefined/unknown stereogenic centers and bonds\n"); + else if ( REQ_MODE_SC_IGN_ALL_UU == (nMode & (REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU)) ) + inchi_ios_eprint( log_file, " Omit undefined/unknown stereogenic centers\n"); + else if ( REQ_MODE_SB_IGN_ALL_UU == (nMode & (REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU)) ) + inchi_ios_eprint( log_file, " Omit undefined/unknown stereogenic bonds\n"); + else + /*case REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU*/ + inchi_ios_eprint( log_file, " Omit undefined/unknown stereogenic centers and bonds\n"); + + if ( 0 != (nMode & REQ_MODE_DIFF_UU_STEREO) ) + { + inchi_ios_eprint( log_file, " Make labels for unknown and undefined stereo different\n"); + } + + +#if ( defined(MIN_SB_RING_SIZE) && MIN_SB_RING_SIZE > 0 ) + k = (ip->nMode & REQ_MODE_MIN_SB_RING_MASK) >> REQ_MODE_MIN_SB_RING_SHFT; + if ( bRELEASE_VERSION != 1 || k != MIN_SB_RING_SIZE ) + { + if ( k >= 3 ) + inchi_ios_eprint( log_file, " Min. stereobond ring size: %d\n", k ); + else + inchi_ios_eprint( log_file, " Min. stereobond ring size: NONE\n" ); + } +#endif + } /* stereo */ + } /* !bStdFormat */ + + + + if ( !bStdFormat ) + { + if ( TG_FLAG_KETO_ENOL_TAUT & ip->bTautFlags) + inchi_ios_eprint( log_file, " Account for keto-enol tautomerism\n"); + else + inchi_ios_eprint( log_file, " Do not account for keto-enol tautomerism\n"); + if ( TG_FLAG_1_5_TAUT & ip->bTautFlags) + inchi_ios_eprint( log_file, " Account for 1,5-tautomerism\n"); + else + inchi_ios_eprint( log_file, " Do not account for 1,5-tautomerism\n"); + +#ifdef BUILD_WITH_ENG_OPTIONS + if ( TG_FLAG_PHOSPHINE_STEREO & ip->bTautFlags ) + inchi_ios_eprint( log_file, " Include phosphine stereochemistry\n"); + else + inchi_ios_eprint( log_file, " Do not include phosphine stereochemistry\n"); + if ( TG_FLAG_ARSINE_STEREO & ip->bTautFlags ) + inchi_ios_eprint( log_file, " Include arsine stereochemistry\n"); + else + inchi_ios_eprint( log_file, " Do not include arsine stereochemistry\n"); + if ( ! (TG_FLAG_FIX_SP3_BUG & ip->bTautFlags) ) + inchi_ios_eprint( log_file, " Turned OFF fix of bug leading to missing or undefined sp3 parity\n"); + if ( !(TG_FLAG_FIX_ISO_FIXEDH_BUG & ip->bTautFlags) ) + inchi_ios_eprint( log_file, " Turned OFF bug-fixes found after v.1.02b release\n"); + if ( !(ip->bFixNonUniformDraw) ) + inchi_ios_eprint( log_file, " Turned OFF fixes of non-uniform drawing issues\n"); + if ( ! (TG_FLAG_MOVE_POS_CHARGES & ip->bTautFlags) ) + inchi_ios_eprint( log_file, " MovePos turned OFF\n"); +#endif + } /* !bStdFormat */ + + + if ( ip->bCalcInChIHash != INCHIHASH_NONE ) + { + if (bStdFormat) + inchi_ios_eprint( log_file, "Generating standard InChIKey\n"); + else + inchi_ios_eprint( log_file, "Generating InChIKey\n"); + if ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1 ) + inchi_ios_eprint( log_file, "Generating hash extension (1st block)\n"); + else if ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA2 ) + inchi_ios_eprint( log_file, "Generating hash extension (2nd block)\n"); + else if ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) + inchi_ios_eprint( log_file, "Generating hash extension (two blocks)\n"); + } + + + if ( ip->bINChIOutputOptions & INCHI_OUT_SAVEOPT ) + { + inchi_ios_eprint( log_file, "Saving InChI creation options" ); + if ( bStdFormat ) + { + inchi_ios_eprint( log_file, " suppressed for standard InChI" ); + /* NB: actual suppression takes place on InChI serialization */ + /* (as on e.g. Inchi2Inchi conversion it may appear that we create non-std */ + /* InChI instead of standard one) */ + } + inchi_ios_eprint( log_file, "\n" ); + } + + + if ( ip->bAllowEmptyStructure ) + inchi_ios_eprint( log_file, "Issue warning on empty structure\n" ); + + if ( ip->bLargeMolecules ) + inchi_ios_eprint( log_file, "Allow processing of \'large\' molecules\n" ); + if ( ip->bPolymers ) + inchi_ios_eprint( log_file, "Allow processing of polymers\n" ); + + + /* Input */ + + if ( ip->nInputType ) + { + inchi_ios_eprint( log_file, "Input format: %s", + ip->nInputType == INPUT_MOLFILE? "MOLfile" : + ip->nInputType == INPUT_SDFILE? "SDfile" : +#if ( READ_INCHI_STRING == 1 ) + ip->nInputType == INPUT_INCHI? "InChI (plain identifier)" : +#endif + ip->nInputType == INPUT_INCHI_PLAIN? "InChI AuxInfo (plain)" : "Unknown" ); + if ( (ip->nInputType == INPUT_MOLFILE || ip->nInputType == INPUT_SDFILE) && + ip->bGetMolfileNumber ) + inchi_ios_eprint( log_file, " (attempting to read Molfile number)" ); + inchi_ios_eprint( log_file, "\n"); + } + + if ( ip->szSdfDataHeader[0] && ip->nInputType != INPUT_SDFILE ) + inchi_ios_eprint( log_file, " SDfile data header: \"%s\"\n", ip->szSdfDataHeader); + + /* Output */ + + inchi_ios_eprint( log_file, "Output format: %s%s\n", + (ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT)? "Plain text" : + + ((ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY) && bInChI2Struct )? "SDfile only (without stereochemical info and atom coordinates)" : + ((ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY) && !bInChI2Struct)? "SDfile only" : "Unknown", + + ((ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) && + (ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT))? ", tabbed":""); + +#if ( bRELEASE_VERSION == 1 ) + if ( ip->bCtPredecessors || ip->bAbcNumbers ) + { + if ( ip->bCtPredecessors && ip->bAbcNumbers ) + inchi_ios_eprint( log_file, "Representation: Compressed\n"); + else + inchi_ios_eprint( log_file, "Connection table: %s, %s\n", + ip->bCtPredecessors? "Predecessor_numbers(closures)":"Canon_numbers(branching, ring closures)", + ip->bAbcNumbers? "Shorter alternative":"Numerical"); + } +#else + if ( (bRELEASE_VERSION != 1) || ip->bCtPredecessors || ip->bAbcNumbers ) + inchi_ios_eprint( log_file, "Connection table: %s, %s\n", + ip->bCtPredecessors? "Predecessor_numbers(closures)":"Canon_numbers(branching, ring closures)", + ip->bAbcNumbers? "Shorter alternative":"Numerical"); + else + inchi_ios_eprint( log_file, "Representation: Numerical"); +#endif + + if ( !(ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY) ) + { + if( ip->bINChIOutputOptions & INCHI_OUT_NO_AUX_INFO ) + inchi_ios_eprint( log_file, "Aux. info suppressed\n"); + else if ( ip->bINChIOutputOptions & INCHI_OUT_SHORT_AUX_INFO ) + inchi_ios_eprint( log_file, "Minimal Aux. info\n"); + else + inchi_ios_eprint( log_file, "Full Aux. info\n"); + } + + + + if ( ip->msec_MaxTime ) + { + unsigned long seconds = ip->msec_MaxTime/1000; + /* unsigned long milliseconds = (ip->msec_MaxTime%1000); + inchi_ios_eprint( log_file, "Timeout per structure: %lu/*.%03lu sec\n", seconds, milliseconds);*/ + inchi_ios_eprint( log_file, "Timeout per structure: %lu sec\n", seconds); + } + else + inchi_ios_eprint( log_file, "No timeout\n"); + + inchi_ios_eprint( log_file, "Up to %d atoms per structure\n", + ip->bLargeMolecules?MAX_ATOMS:NORMALLY_ALLOWED_INP_MAX_ATOMS); + if ( ip->bPolymers ) + inchi_ios_eprint( log_file, "Specifically treating polymers\n" ); + + if ( ip->first_struct_number > 1 ) + inchi_ios_eprint( log_file, "Skipping %ld structure%s\n", ip->first_struct_number-1, ip->first_struct_number==2? "":"s" ); + if ( ip->last_struct_number > 0 ) + inchi_ios_eprint( log_file, "Terminate after structure #%ld\n", ip->last_struct_number ); + if ( ip->bSaveWarningStructsAsProblem && ip->path[3] && ip->path[3][0] ) + inchi_ios_eprint( log_file, "Saving warning structures into the problem file\n"); + if ( ip->bSaveAllGoodStructsAsProblem && ip->path[3] && ip->path[3][0] ) + inchi_ios_eprint( log_file, "Saving only all good structures into the problem file\n"); + + /* Report debug modes */ + +#if ( bRELEASE_VERSION != 1 ) + inchi_ios_eprint( log_file, "Release version = NO\n"); +#endif + + +#if ( TRACE_MEMORY_LEAKS == 1 && defined(_DEBUG) ) + inchi_ios_eprint( log_file, "Tracing memory leaks (SLOW)\n"); +#endif + + inchi_ios_eprint( log_file, "\n" ); + + + +#if ( bRELEASE_VERSION != 1 ) +#if ( FIND_RING_SYSTEMS == 1 ) + inchi_ios_eprint( log_file, "Find ring systems=Y\nTautomers:\n" ); + inchi_ios_eprint( log_file, " 4-pyridinol=%s\n", TAUT_4PYRIDINOL_RINGS==1? "Y":"N"); + inchi_ios_eprint( log_file, " pyrazole=%s\n", TAUT_PYRAZOLE_RINGS==1? "Y":"N"); + inchi_ios_eprint( log_file, " tropolone=%s\n", TAUT_TROPOLONE_7==1? "Y":"N"); + inchi_ios_eprint( log_file, " tropolone-5=%s\n", TAUT_TROPOLONE_5==1? "Y":"N"); + inchi_ios_eprint( log_file, "Only chain attachments to tautomeric rings=%s\n", TAUT_RINGS_ATTACH_CHAIN==1? "Y":"N"); +#endif + + if ( ip->bGetSdfileId ) + inchi_ios_eprint( log_file, "Extracting SDfile IDs\n"); + + inchi_ios_eprint( log_file, "\nDbg: MOVE_CHARGES=%d\n", + 0!=(ip->bTautFlags&TG_FLAG_MOVE_POS_CHARGES)); + inchi_ios_eprint( log_file, " REPLACE_ALT_WITH_TAUT=%d; NEUTRALIZE_ENDPOINTS=%d; BNS_PROTECT_FROM_TAUT=%d\n", + REPLACE_ALT_WITH_TAUT, NEUTRALIZE_ENDPOINTS, BNS_PROTECT_FROM_TAUT); + inchi_ios_eprint( log_file, " DISCONNECT_SALTS=%d; TEST_TAUT_SALTS=%d; TEST_TAUT2_SALTS=%d\n", + 0!=(ip->bTautFlags&TG_FLAG_DISCONNECT_SALTS), + 0!=(ip->bTautFlags&TG_FLAG_TEST_TAUT__SALTS), + 0!=(ip->bTautFlags&TG_FLAG_TEST_TAUT2_SALTS)); + + inchi_ios_eprint( log_file, " CHARGED_ACID_TAUT_ONLY=%d MERGE_TAUT_SALTS=%d\n", + 0==(ip->bTautFlags&TG_FLAG_ALLOW_NO_NEGTV_O), + 0!=(ip->bTautFlags&TG_FLAG_MERGE_TAUT_SALTS)); + inchi_ios_eprint( log_file, " DISCONNECT_COORD=%d\n", 0!=(ip->bTautFlags&TG_FLAG_DISCONNECT_COORD) ); + + +#endif /* ( bRELEASE_VERSION != 1 ) */ + + return 0; +} + + +/****************************************************************************/ +void HelpCommandLineParms( INCHI_IOSTREAM *f ) +{ + if ( !f ) + return; + +#if ( bRELEASE_VERSION == 1 ) + + inchi_ios_print_nodisplay( f, +#ifdef TARGET_EXE_USING_API + "%s\n%s Build of %s %s %s\n\nUsage:\ninchi_main inputFile [outputFile [logFile [problemFile]]] [%coption[ %coption...]]\n", + APP_DESCRIPTION, + TARGET_PLATFORM, __DATE__, __TIME__, + RELEASE_IS_FINAL?"":" *** pre-release, for evaluation only ***", + INCHI_OPTION_PREFX, INCHI_OPTION_PREFX); +#else + "%s\n%-s Build of %s %-s%-s\n\nUsage:\ninchi-1 inputFile [outputFile [logFile [problemFile]]] [%coption [%coption...]]\n", + APP_DESCRIPTION, + TARGET_PLATFORM, __DATE__, __TIME__, + RELEASE_IS_FINAL?"":" *** pre-release, for evaluation only ***", + INCHI_OPTION_PREFX, INCHI_OPTION_PREFX); +#if ( BUILD_WITH_AMI == 1 ) + + inchi_ios_print_nodisplay( f, + "inchi-1 inputFiles... %cAMI [%coption[ %coption...]]\n", + INCHI_OPTION_PREFX, INCHI_OPTION_PREFX, INCHI_OPTION_PREFX); +#endif +#endif + + inchi_ios_print_nodisplay( f, "\nOptions:\n"); + + inchi_ios_print_nodisplay( f, "\nInput\n"); + inchi_ios_print_nodisplay( f, " STDIO Use standard input/output streams\n"); + inchi_ios_print_nodisplay( f, " InpAux Input structures in %s default aux. info format\n (for use with STDIO)\n", INCHI_NAME); + inchi_ios_print_nodisplay( f, " SDF:DataHeader Read from the input SDfile the ID under this DataHeader\n"); + + /* + inchi_ios_print_nodisplay( f, " START:n Skip structures up to n-th one\n"); + inchi_ios_print_nodisplay( f, " END:m Skip structures after m-th one\n"); + */ + +#if ( BUILD_WITH_AMI == 1 ) + inchi_ios_print_nodisplay( f, " AMI Allow multiple input files (wildcards supported)\n"); +#endif + + inchi_ios_print_nodisplay( f, "Output\n"); + inchi_ios_print_nodisplay( f, " AuxNone Omit auxiliary information (default: Include)\n"); + inchi_ios_print_nodisplay( f, " SaveOpt Save custom InChI creation options (non-standard InChI)\n"); + inchi_ios_print_nodisplay( f, " NoLabels Omit structure number, DataHeader and ID from %s output\n", INCHI_NAME); + inchi_ios_print_nodisplay( f, " Tabbed Separate structure number, %s, and AuxInfo with tabs\n", INCHI_NAME); +#ifndef TARGET_EXE_USING_API +#ifdef ERR_INCHI_STRING_ALLOWED + inchi_ios_print_nodisplay( f, " OutErrInChI On fail, print empty InChI (default: nothing)\n"); +#endif +#endif + /*inchi_ios_print_nodisplay( f, " Compress Compressed output\n"); */ + /*inchi_ios_print_nodisplay( f, " FULL Standard set of options for Full Verbose Output\n");*/ + /*inchi_ios_print_nodisplay( f, " MIN Standard set of options for Minimal Concise Output\n");*/ + +#if ( defined(_WIN32) && defined(_MSC_VER) && !defined(COMPILE_ANSI_ONLY) && !defined(TARGET_API_LIB) ) + inchi_ios_print_nodisplay( f, " D Display the structures\n"); + inchi_ios_print_nodisplay( f, " EQU Display sets of identical components\n"); + inchi_ios_print_nodisplay( f, " Fnumber Set display Font size in number of points\n"); +#endif + + inchi_ios_print_nodisplay( f, " OutputSDF Convert %s created with default aux. info to SDfile\n", INCHI_NAME); + +#if ( SDF_OUTPUT_DT == 1 ) + inchi_ios_print_nodisplay( f, " SdfAtomsDT Output Hydrogen Isotopes to SDfile as Atoms D and T\n"); +#endif + +#if ( BUILD_WITH_AMI == 1 ) + inchi_ios_print_nodisplay( f, " AMIOutStd Write output to stdout (in AMI mode)\n"); + inchi_ios_print_nodisplay( f, " AMILogStd Write log to stderr (in AMI mode)\n"); + inchi_ios_print_nodisplay( f, " AMIPrbNone Suppress creation of problem files (in AMI mode)\n"); +#endif + + inchi_ios_print_nodisplay( f, "Structure perception\n"); + inchi_ios_print_nodisplay( f, " SNon Exclude stereo (default: include absolute stereo)\n"); + inchi_ios_print_nodisplay( f, " NEWPSOFF Both ends of wedge point to stereocenters (default: a narrow end)\n"); + inchi_ios_print_nodisplay( f, " DoNotAddH All H are explicit (default: add H according to usual valences)\n"); + +#ifndef USE_STDINCHI_API + inchi_ios_print_nodisplay( f, "Stereo perception modifiers (non-standard InChI)\n"); + inchi_ios_print_nodisplay( f, " SRel Relative stereo\n"); + inchi_ios_print_nodisplay( f, " SRac Racemic stereo\n"); + inchi_ios_print_nodisplay( f, " SUCF Use Chiral Flag: On means Absolute stereo, Off - Relative\n"); + + inchi_ios_print_nodisplay( f, "Customizing InChI creation (non-standard InChI)\n"); + inchi_ios_print_nodisplay( f, " SUU Always include omitted unknown/undefined stereo\n"); + inchi_ios_print_nodisplay( f, " SLUUD Make labels for unknown and undefined stereo different\n"); + inchi_ios_print_nodisplay( f, " RecMet Include reconnected metals results\n"); + inchi_ios_print_nodisplay( f, " FixedH Include Fixed H layer\n"); + inchi_ios_print_nodisplay( f, " KET Account for keto-enol tautomerism (experimental)\n"); + inchi_ios_print_nodisplay( f, " 15T Account for 1,5-tautomerism (experimental)\n"); +#endif + + inchi_ios_print_nodisplay( f, "Generation\n"); + inchi_ios_print_nodisplay( f, " Wnumber Set time-out per structure in seconds; W0 means unlimited\n"); + inchi_ios_print_nodisplay( f, " Polymers Allow processing of polymers (experimental)\n"); + inchi_ios_print_nodisplay( f, " LargeMolecules Treat molecules up to 32766 atoms (experimental)\n"); + inchi_ios_print_nodisplay( f, " WarnOnEmptyStructure Warn and produce empty %s for empty structure\n", INCHI_NAME); + /*inchi_ios_print_nodisplay( f, " MismatchIsError Treat problem/mismatch on inchi2struct conversion as error\n");*/ + inchi_ios_print_nodisplay( f, " Key Generate InChIKey\n"); + inchi_ios_print_nodisplay( f, " XHash1 Generate hash extension (to 256 bits) for 1st block of InChIKey\n"); + inchi_ios_print_nodisplay( f, " XHash2 Generate hash extension (to 256 bits) for 2nd block of InChIKey\n"); + + + inchi_ios_print_nodisplay( f, "Conversion\n"); + +#ifdef TARGET_EXE_USING_API + inchi_ios_print_nodisplay( f, " InChI2InChI Test mode: Mol/SDfile -> %s -> %s\n", INCHI_NAME, INCHI_NAME); + inchi_ios_print_nodisplay( f, " InChI2Struct Test mode: Mol/SDfile -> %s -> Structure -> (%s+AuxInfo)\n", INCHI_NAME, INCHI_NAME); +#else + inchi_ios_print_nodisplay( f, " InChI2InChI Convert %s string(s) into %s string(s)\n", INCHI_NAME, INCHI_NAME); + inchi_ios_print_nodisplay( f, " InChI2Struct Convert InChI string(s) to structure(s) in InChI aux.info format\n"); +#endif + +#endif +} + + + + +/************************************************************************************/ +/* Abridged version of HelpCommandLineParms,for use in the example of InChI DLL call*/ +void HelpCommandLineParmsReduced( INCHI_IOSTREAM *f ) +{ + if ( !f ) + return; + +#if ( bRELEASE_VERSION == 1 ) + + + inchi_ios_print_nodisplay( f, + +#ifdef TARGET_EXE_USING_API + "%s\n%-s Build of %s %s%s\n\nUsage:\ninchi_main inputFile [outputFile [logFile [problemFile]]] [%coption[ %coption...]]\n", + APP_DESCRIPTION, + TARGET_PLATFORM, __DATE__, __TIME__, + RELEASE_IS_FINAL?"":" *** pre-release, for evaluation only ***", + INCHI_OPTION_PREFX, INCHI_OPTION_PREFX); +#else + "%s\n%-s Build of %s %s%s\n\nUsage:\ninchi-1 inputFile [outputFile [logFile [problemFile]]] [%coption[ %coption...]]\n", + APP_DESCRIPTION, + TARGET_PLATFORM, __DATE__, __TIME__, + RELEASE_IS_FINAL?"":" *** pre-release, for evaluation only ***", + INCHI_OPTION_PREFX, INCHI_OPTION_PREFX); +#endif + + inchi_ios_print_nodisplay( f, "\nOptions:\n"); + + + inchi_ios_print_nodisplay( f, "\nInput\n"); + inchi_ios_print_nodisplay( f, " STDIO Use standard input/output streams\n"); + inchi_ios_print_nodisplay( f, " InpAux Input structures in %s default aux. info format\n (for use with STDIO)\n", INCHI_NAME); + inchi_ios_print_nodisplay( f, " SDF:DataHeader Read from the input SDfile the ID under this DataHeader\n"); + +/* + inchi_ios_print_nodisplay( f, " START:n Skip structures up to n-th one\n"); + inchi_ios_print_nodisplay( f, " END:m Skip structures after m-th one\n"); +*/ + +#if ( BUILD_WITH_AMI == 1 ) + inchi_ios_print_nodisplay( f, " AMI Allow multiple input files (wildcards supported)\n"); +#endif + + inchi_ios_print_nodisplay( f, "Output\n"); + inchi_ios_print_nodisplay( f, " AuxNone Omit auxiliary information (default: Include)\n"); + inchi_ios_print_nodisplay( f, " SaveOpt Save custom InChI creation options (non-standard InChI)\n"); + inchi_ios_print_nodisplay( f, " NoLabels Omit structure number, DataHeader and ID from %s output\n", INCHI_NAME); + inchi_ios_print_nodisplay( f, " Tabbed Separate structure number, %s, and AuxInfo with tabs\n", INCHI_NAME); + /* inchi_ios_print_nodisplay( f, " Compress Compressed output\n"); */ + +#if ( defined(_WIN32) && defined(_MSC_VER) && !defined(COMPILE_ANSI_ONLY) && !defined(TARGET_API_LIB) ) + inchi_ios_print_nodisplay( f, " D Display the structures\n"); + inchi_ios_print_nodisplay( f, " EQU Display sets of identical components\n"); + inchi_ios_print_nodisplay( f, " Fnumber Set display Font size in number of points\n"); +#endif + + inchi_ios_print_nodisplay( f, " OutputSDF Convert %s created with default aux. info to SDfile\n", INCHI_NAME); + +#if ( SDF_OUTPUT_DT == 1 ) + inchi_ios_print_nodisplay( f, " SdfAtomsDT Output Hydrogen Isotopes to SDfile as Atoms D and T\n"); +#endif + +#if ( BUILD_WITH_AMI == 1 ) + inchi_ios_print_nodisplay( f, " AMIOutStd Write output to stdout (in AMI mode)\n"); + inchi_ios_print_nodisplay( f, " AMILogStd Write log to stderr (in AMI mode)\n"); + inchi_ios_print_nodisplay( f, " AMIPrbNone Suppress creation of problem files (in AMI mode)\n"); +#endif + inchi_ios_print_nodisplay( f, "Structure perception\n"); + inchi_ios_print_nodisplay( f, " SNon Exclude stereo (default: include absolute stereo)\n"); + inchi_ios_print_nodisplay( f, " NEWPSOFF Both ends of wedge point to stereocenters (default: a narrow end)\n"); + inchi_ios_print_nodisplay( f, " DoNotAddH All H are explicit (default: add H according to usual valences)\n"); + +#ifndef USE_STDINCHI_API + inchi_ios_print_nodisplay( f, "Stereo perception modifiers (non-standard InChI)\n"); + inchi_ios_print_nodisplay( f, " SRel Relative stereo\n"); + inchi_ios_print_nodisplay( f, " SRac Racemic stereo\n"); + inchi_ios_print_nodisplay( f, " SUCF Use Chiral Flag: On means Absolute stereo, Off - Relative\n"); + + inchi_ios_print_nodisplay( f, "Customizing InChI creation (non-standard InChI)\n"); + inchi_ios_print_nodisplay( f, " SUU Always include omitted unknown/undefined stereo\n"); + inchi_ios_print_nodisplay( f, " SLUUD Make labels for unknown and undefined stereo different\n"); + inchi_ios_print_nodisplay( f, " RecMet Include reconnected metals results\n"); + inchi_ios_print_nodisplay( f, " FixedH Include Fixed H layer\n"); + inchi_ios_print_nodisplay( f, " KET Account for keto-enol tautomerism (experimental)\n"); + inchi_ios_print_nodisplay( f, " 15T Account for 1,5-tautomerism (experimental)\n"); +#endif + + inchi_ios_print_nodisplay( f, "Generation\n"); + inchi_ios_print_nodisplay( f, " Wnumber Set time-out per structure in seconds; W0 means unlimited\n"); + inchi_ios_print_nodisplay( f, " Polymers Allow processing of polymers (experimental)\n"); + inchi_ios_print_nodisplay( f, " LargeMolecules Treat molecules up to 32766 atoms (experimental)\n"); + inchi_ios_print_nodisplay( f, " WarnOnEmptyStructure Warn and produce empty %s for empty structure\n", INCHI_NAME); + /* inchi_ios_print_nodisplay( f, " MismatchIsError Treat problem/mismatch on inchi2struct conversion as error\n"); */ + inchi_ios_print_nodisplay( f, " Key Generate InChIKey\n"); + inchi_ios_print_nodisplay( f, " XHash1 Generate hash extension (to 256 bits) for 1st block of InChIKey\n"); + inchi_ios_print_nodisplay( f, " XHash2 Generate hash extension (to 256 bits) for 2nd block of InChIKey\n"); + + +#endif /* #if ( bRELEASE_VERSION == 1 ) */ +} + + + + +#define fprintf2 inchi_fprintf + +#ifndef TARGET_API_LIB + + + +/****************************************************************************/ +int OpenFiles( FILE **inp_file, + FILE **out_file, + FILE **log_file, + FILE **prb_file, + INPUT_PARMS *ip ) +{ +/* + -- Files -- + ip->path[0] => Input + ip->path[1] => Output (INChI) + ip->path[2] => Log + ip->path[3] => Problem structures + ip->path[4] => Errors file (ACD Labs) + +*/ + + + /* Logfile (open as early as possible) */ + + if ( !ip->path[2] || !ip->path[2][0] ) + { + fprintf2( stderr, "%s\n%-s Build of %s%s %-s\n\n", + APP_DESCRIPTION, TARGET_PLATFORM, __DATE__, __TIME__, + RELEASE_IS_FINAL ? "" : " *** pre-release, for evaluation only ***" ); + fprintf2( stderr, "Log file not specified. Using standard error output.\n"); + *log_file = stderr; + } + else if ( !(*log_file = fopen( ip->path[2], "w" ) ) ) + { + fprintf2( stderr, "%s\n%-s Build of %s %s%-s\n\n", + APP_DESCRIPTION, TARGET_PLATFORM, __DATE__,__TIME__, + RELEASE_IS_FINAL ? "" : " *** pre-release, for evaluation only ***" ); + fprintf2( stderr, "Cannot open log file '%s'. Using standard error output.\n", ip->path[2] ); + *log_file = stderr; + } + else + { + fprintf2( *log_file, "%s\n%-s Build of %s %s%-s\n\n", + APP_DESCRIPTION, TARGET_PLATFORM, __DATE__, __TIME__, + RELEASE_IS_FINAL ? "" : " *** pre-release, for evaluation only ***" ); + fprintf2( *log_file, "Opened log file '%s'\n", ip->path[2] ); + } + + + /* Input file */ + + if ( (ip->nInputType == INPUT_MOLFILE || ip->nInputType == INPUT_SDFILE || + ip->nInputType == INPUT_INCHI || + ip->nInputType == INPUT_INCHI_PLAIN ) && ip->num_paths > 0 ) + { + const char *fmode = NULL; + +#if ( defined(_MSC_VER)&&defined(_WIN32) || defined(__BORLANDC__)&&defined(__WIN32__) || defined(__GNUC__)&&defined(__MINGW32__)&&defined(_WIN32) ) + /* compilers that definitely allow fopen "rb" (binary read) mode */ + fmode = "rb"; + if ( !ip->path[0] || !ip->path[0][0] || !(*inp_file = fopen( ip->path[0], "rb" ) ) ) + { + fprintf2( *log_file, "Cannot open input file '%s'. Terminating.\n", ip->path[0]? ip->path[0] : "" ); + goto exit_function; + } + else + { + fprintf2( *log_file, "Opened input file '%s'\n", ip->path[0] ); + } + +#else + + if ( !ip->path[0] || !ip->path[0][0] || !(*inp_file = fopen( ip->path[0], "r" ) ) ) { + fprintf2( *log_file, "Cannot open input file '%s'. Terminating.\n", ip->path[0]? ip->path[0] : "" ); + goto exit_function; + } else { + fprintf2( *log_file, "Opened input file '%s'\n", ip->path[0] ); + } + fmode = "r"; + +#endif /* ( defined(_MSC_VER)&&defined(_WIN32) || defined(__BORLANDC__)&&defined(__WIN32__) || defined(__GNUC__)&&defined(__MINGW32__)&&defined(_WIN32) ) */ + + DetectInputINChIFileType( inp_file, ip, fmode ); + } + else if ( (ip->nInputType != INPUT_MOLFILE && + ip->nInputType != INPUT_SDFILE && + ip->nInputType != INPUT_INCHI && + /* post-1.02b */ + ip->nInputType != INPUT_INCHI_PLAIN ) ) + { + fprintf2( *log_file, "Input file type not specified. Terminating.\n"); + goto exit_function; + } + else + { + fprintf2( *log_file, "Input file not specified. Using standard input.\n"); + *inp_file = stdin; + } + + + /* Output file */ + + if ( !ip->path[1] || !ip->path[1][0] ) + { + fprintf2( *log_file, "Output file not specified. Using standard output.\n"); + *out_file = stdout; + } + else + { + if ( !(*out_file = fopen( ip->path[1], "w" ) ) ) + { + fprintf2( *log_file, "Cannot open output file '%s'. Terminating.\n", ip->path[1] ); + goto exit_function; + } + else + { + fprintf2( *log_file, "Opened output file '%s'\n", ip->path[1] ); + if ( (ip->bINChIOutputOptions & (INCHI_OUT_PLAIN_TEXT)) && + *inp_file != stdin && + !(ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY) && + !ip->bNoStructLabels && + !(ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT)) + { + PrintFileName( "* Input_File: \"%s\"\n", *out_file, ip->path[0] ); + } + } + } + + + /* Problem file */ + + if ( ip->path[3] && ip->path[3][0] ) + { + const char *fmode = "w"; + +#if ( defined(_MSC_VER)&&defined(_WIN32) || defined(__BORLANDC__)&&defined(__WIN32__) || defined(__GNUC__)&&defined(__MINGW32__)&&defined(_WIN32) ) + fmode = "wb"; +#endif + if ( !(*prb_file = fopen( ip->path[3], fmode ) ) ) + { + fprintf2( *log_file, "Cannot open problem file '%s'. Terminating.\n", ip->path[3] ); + goto exit_function; + } + else + { + fprintf2( *log_file, "Opened problem file '%s'\n", ip->path[3] ); + } + } + + /* success */ + return 1; + + +exit_function: + /* failed */ + return 0; +} + + + +#define NUM_VERSIONS 7 +#define LEN_VERSIONS 64 + + + +/****************************************************************************/ +static int bMatchOnePrefix( int len, char *str, int lenPrefix[], + char strPrefix[][LEN_VERSIONS], + int numPrefix); + +/****************************************************************************/ +static int bMatchOnePrefix( int len, char *str, int lenPrefix[], + char strPrefix[][LEN_VERSIONS], + int numPrefix) +{ + int i; + for ( i = 0; i < numPrefix; i ++ ) + { + if ( len >= lenPrefix[i] && + !memcmp( str, strPrefix[i], lenPrefix[i] ) ) + { + return 1; + } + } + + return 0; +} + + +/****************************************************************************/ +int DetectInputINChIFileType( FILE **inp_file, INPUT_PARMS *ip, const char *fmode ) +{ +char szLine[256], ret = 0; +static char szPlnVersion[NUM_VERSIONS][LEN_VERSIONS]; /* = "INChI:1.1Beta/";*/ +static int lenPlnVersion[NUM_VERSIONS]; +static char szPlnAuxVer[NUM_VERSIONS][LEN_VERSIONS]; /* = "AuxInfo:1.1Beta/";*/ +static int lenPlnAuxVer[NUM_VERSIONS]; +static int bInitilized = 0; +int bINChI_plain = 0, len, i; + + + if ( ip->nInputType == INPUT_INCHI_PLAIN || ip->nInputType == INPUT_INCHI ) + { + return 1; + } + + if ( !bInitilized ) + { + lenPlnVersion[0] = sprintf( szPlnVersion[0], "%s=%s/", INCHI_NAME, INCHI_VERSION ); + lenPlnVersion[1] = sprintf( szPlnVersion[1], "INChI=1.12Beta/" ); + lenPlnVersion[2] = sprintf( szPlnVersion[2], "INChI=1.0RC/" ); + lenPlnVersion[3] = sprintf( szPlnVersion[3], "InChI=1.0RC/" ); + lenPlnVersion[4] = sprintf( szPlnVersion[4], "InChI=1/" ); + lenPlnVersion[5] = sprintf( szPlnVersion[5], "MoChI=1a/" ); + lenPlnVersion[6] = sprintf( szPlnVersion[6], "InChI=1S/" ); + lenPlnAuxVer[0] = sprintf( szPlnAuxVer[0], "AuxInfo=%s/", INCHI_VERSION ); + lenPlnAuxVer[1] = sprintf( szPlnAuxVer[1], "AuxInfo=1.12Beta/" ); + lenPlnAuxVer[2] = sprintf( szPlnAuxVer[2], "AuxInfo=1.0RC/" ); + lenPlnAuxVer[3] = sprintf( szPlnAuxVer[3], "AuxInfo=1.0RC/" ); + lenPlnAuxVer[4] = sprintf( szPlnAuxVer[4], "AuxInfo=1/" ); + lenPlnAuxVer[5] = sprintf( szPlnAuxVer[5], "AuxInfo=1a/" ); + lenPlnAuxVer[6] = sprintf( szPlnAuxVer[6], "AuxInfo=1/" ); + +#if ( FIX_DALKE_BUGS == 1 ) + bInitilized = 1; +#endif + } + + for ( i = 0; i < 4; i ++ ) + { + + len = inchi_fgetsLfTab( szLine, sizeof(szLine)-1, *inp_file ); + + if ( len < 0 ) + break; + + if ( bMatchOnePrefix( len, szLine, lenPlnVersion, szPlnVersion, NUM_VERSIONS ) || + bMatchOnePrefix( len, szLine, lenPlnAuxVer, szPlnAuxVer, NUM_VERSIONS ) ) + { + bINChI_plain++; + } + } + + if ( bINChI_plain >= 2 ) + { + ip->nInputType = INPUT_INCHI_PLAIN; + ret = 1; + } + +/*exit_function:*/ + fclose ( *inp_file ); + *inp_file = fopen( ip->path[0], fmode ); + + return ret; +} +#undef NUM_VERSIONS +#undef LEN_VERSIONS + + +#endif /* TARGET_API_LIB */ + + +#endif /* _ICHIPARM_H_ */ diff --git a/INCHI-1-SRC/INCHI_BASE/src/ichiprt1.c b/INCHI-1-SRC/INCHI_BASE/src/ichiprt1.c new file mode 100644 index 0000000..a6925f5 --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/ichiprt1.c @@ -0,0 +1,4551 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include +#include +#include + +#include "mode.h" + +#include "ichister.h" +#include "ichimain.h" +#include "ichimake.h" +#include "ichi_io.h" + + +/* + Local functions +*/ + +static int str_LineStart( const char *tag, char *tag2, int val2, + INCHI_IOSTREAM_STRING *strbuf, int ind ); +static int str_LineEnd( const char *tag, int *bOverflow, + INCHI_IOSTREAM_STRING *buf, + int ind, int bPlainTextTags ); + +static int CleanOrigCoord( MOL_COORD szCoord, int delim ); +static int WriteOrigCoord( int num_inp_atoms, MOL_COORD *szMolCoord, int *i, + char *szBuf, int buf_len); +static int WriteOrigAtoms( CANON_GLOBALS *pCG, int num_inp_atoms, inp_ATOM *at, int *i, + char *szBuf, int buf_len, STRUCT_DATA *sd); +static int WriteOrigBonds( CANON_GLOBALS *pCG, int num_inp_atoms, inp_ATOM *at, int *i, + char *szBuf, int buf_len, + STRUCT_DATA *sd); + +static void GetSaveOptLetters(unsigned char save_opt_bits, char* let1, char* let2); + +static int Output_RecordInfo( INCHI_IOSTREAM *out_file, int num_input_struct, + int bNoStructLabels, const char *szSdfLabel, + const char *szSdfValue, long lSdfId, char *pLF, char *pTAB ); +static int OutputINCHI_VersionAndKind( INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + int bINChIOutputOptions, + int is_beta, char *pLF, char *pTAB ); +static int OutputINCHI_MainLayerFormula( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + int num_components2[], + int *INCHI_basic_or_INCHI_reconnected, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ); +static int OutputINCHI_MainLayerConnections( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + int num_components2[], + int *INCHI_basic_or_INCHI_reconnected, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ); +static int OutputINCHI_MainLayerHydrogens( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + int num_components2[], + int *INCHI_basic_or_INCHI_reconnected, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ); +static int OutputINCHI_ChargeAndRemovedAddedProtonsLayers( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ); +static int OutputINCHI_StereoLayer( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ); +static int OutputINCHI_IsotopicLayer( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, INCHI_IOSTREAM_STRING *strbuf, + int *INCHI_basic_or_INCHI_reconnected, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ); +static int OutputINCHI_FixedHLayerWithSublayers( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + int *INCHI_basic_or_INCHI_reconnected, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB, + int *then_goto_repeat ); +static int OutputINCHI_PolymerLayer( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + int *INCHI_basic_or_INCHI_reconnected, + ORIG_STRUCT *pOrigStruct, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ); +static int OutputAUXINFO_HeaderAndNormalization_type( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + int bINChIOutputOptions, + int *INCHI_basic_or_INCHI_reconnected, + int num_components2[], + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ); +static int OutputAUXINFO_OriginalNumbersAndEquivalenceClasses( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + int num_components2[], + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ); +static int OutputAUXINFO_TautomericGroupsEquivalence( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ); +static int OutputAUXINFO_Stereo( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ); +static int OutputAUXINFO_IsotopicInfo( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + int *INCHI_basic_or_INCHI_reconnected, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ); +static int OutputAUXINFO_Transposition( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + int *INCHI_basic_or_INCHI_reconnected, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ); +static int OutputAUXINFO_ChargesRadicalsAndUnusualValences( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ); +static int OutputAUXINFO_ReversibilityInfo( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + ORIG_STRUCT *pOrigStruct, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ); + +static int OutputAUXINFO_PolymerInfo( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + ORIG_STRUCT *pOrigStruct, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ); +int psbond_min_num_compare( int *bond1, int* bond2 ); + +void HidePolymerRelatedInternals( INCHI_IOSTREAM *out ); +int CountZzInFormula( char *s); + + + +/* + Local constants +*/ +const char sCompDelim[] = ";"; /* component delimiter */ +const char sIdenticalValues[] = "*"; /* identical component */ +const char x_space[] = " "; + + +/* + Output: words & additional tags +*/ +const char x_inchi[] = INCHI_NAME; +const char x_inchi_ver[] = "version"; /* "InChI.version"; */ +const char x_curr_ver[] = INCHI_VERSION; +const char x_structure[] = "structure"; +const char x_number[] = "number"; +const char x_header[] = "id.name"; +const char x_value[] = "id.value"; +const char x_empty[] = ""; +const char x_type[] = "type"; +const char x_message[] = "message"; +const char x_text[] = "value"; +const char x_ferr[] = "fatal (aborted)"; +const char x_err[] = "error (no InChI)"; +const char x_warn[] = "warning"; +const char x_basic[] = "identifier"; +const char x_tautomeric[] = "mobile-H"; +const char x_reconnected[] = "reconnected"; +const char x_ver[] = "version"; +const char x_type_alpha[] = "alpha"; +const char x_type_numer[] = "numeric"; +const char x_type_predec[] = "sct"; +const char x_type_normal[] = "normal"; +const char x_type_short[] = "compressed"; +const char x_basic_layer[] = "basic"; +const char x_aux_basic[] = "identifier.auxiliary-info"; +const char x_aux_comm[] = "!-- This section is NOT a part of the identifier, it is not unique --"; +const char x_ign_uu_sp2[] = "omit_undef_dbond"; +const char x_ign_uu_sp3[] = "omit_undef_sp3"; +const char x_line_opening[] = "<"; +const char x_line_closing[] = ""; +const char x_abs[] = "1"; +const char x_rel[] = "2"; +const char x_rac[] = "3"; + +typedef struct tagInchiTag +{ + const char *szPlainLabel; + const char *szPlainComment; + const char *szXmlLabel; + int bAlwaysOutput; +} INCHI_TAG; + + +/* + Identifier +*/ +const INCHI_TAG IdentLbl[] = +{ + /* prefixes: may be combined in this order */ +/* IL_FIXH_ORD, */ { "/", "fixed_H", "fixed-H", 0 }, /* fixed H */ +/* IL_ISOT_ORD, */ { "/", "isotopic", "isotopic", 0 }, /* isotopic */ +/* IL_STER_ORD, */ { "/", "stereo", "stereo", 0 }, /* stereo */ + /* items */ +/* IL_VERS_ORD, */ { "" , "version", "version", 1 }, +/* IL_FML__ORD, */ { "/", "formula", "formula", 1 }, /* basic part formula */ +/* IL_CONN_ORD, */ { "/c", "connections", "connections", 1 }, +/* IL_ALLH_ORD, */ { "/h", "H_atoms", "H", 1 }, +/* IL_CHRG_ORD, */ { "/q", "charge", "charge", 1 }, +/* IL_PROT_ORD, */ { "/p", "protons", "protons", 0 }, + /* stereo */ +/* IL_DBND_ORD, */ { "/b", "dbond", "dbond", 0 }, +/* IL_SP3S_ORD, */ { "/t", "sp3", "sp3", 0 }, +/* IL_INVS_ORD, */ { "/m", "sp3:inverted", "abs.inverted", 0 }, /* mirrored */ +/* IL_TYPS_ORD, */ { "/s", "type (1=abs, 2=rel, 3=rac)", "type", 0 }, /* stereo type */ + /* isotopic */ +/* IL_ATMS_ORD, */ { "/i", "atoms", "atoms", 1 }, + /* isotopic mobile H only */ +/* IL_XCGA_ORD, */ { "/h", "exchangeable_H", "H-isotopic", 1 }, + /* fixed H only */ +/* IL_FMLF_ORD, */ { "/f", "formula", "formula", 1 }, /* fixed H formula */ +/* IL_HFIX_ORD, */ { "/h", "H_fixed" , "H-fixed" , 1 }, /* fixed-H */ +/* IL_TRNS_ORD, */ { "/o", "transposition", "transposition", 0 }, /* order */ +/* IL_REC__ORD, */ { "/r", "reconnected bond(s) to metal(s) formula", "formula", 0 } +}; + +/* + + Parsing plain text InChI (FML is a chemical formula) + ======================== + + 1.12Beta/FML /i /f[FML] /i [/o] /rFML /i /f[FML] /i [/o] end + | | | | | | | | | +Labels | chqpbtms | hbtms | hqbtms | btms | chqpbtms | hbtms | hqbtms | btms | +inside: | | | | | | | | | + | non-iso- | iso- | fix- | iso- | non-iso- | iso- | fix- | iso- | +meaning: | topic | topic | ed H | topic | topic | topic | ed H | topic | + |----------+-------+--------+---------|----------+-------+--------+---------| + | mobile-H | fixed-H | mobile-H | fixed-H | + |----------+-------+--------+---------|----------+-------+--------+---------| + | | | + | normal or disconected metal | reconnected bonds to metal | + |_____________________________________|_____________________________________| + + meanings of h: + + /h - immobile H & mobile H group(s) + /i/h - exchangeable isotopic H (common) + /f/h - fixed-H + /f/i/h - never happens + +*/ +typedef enum tagIdentLblOrd +{ + IL_FIXH_ORD, + IL_ISOT_ORD, + IL_STER_ORD, + + IL_VERS_ORD, + IL_FML__ORD, + IL_CONN_ORD, + IL_ALLH_ORD, + IL_CHRG_ORD, + IL_PROT_ORD, + + IL_DBND_ORD, + IL_SP3S_ORD, + IL_INVS_ORD, + IL_TYPS_ORD, + + IL_ATMS_ORD, + + IL_XCGA_ORD, + + IL_FMLF_ORD, + IL_HFIX_ORD, + IL_TRNS_ORD, + IL_REC__ORD, + + IL_MAX_ORD /* max number of tags */ +} IDENT_LBL_ORD; + + +typedef enum tagIdentLblBit +{ + IL_FIXH = 1 << IL_FIXH_ORD, + IL_ISOT = 1 << IL_ISOT_ORD, + IL_STER = 1 << IL_STER_ORD, + + IL_VERS = 1 << IL_VERS_ORD, + IL_FML_ = 1 << IL_FML__ORD, + IL_CONN = 1 << IL_CONN_ORD, + IL_ALLH = 1 << IL_ALLH_ORD, + IL_CHRG = 1 << IL_CHRG_ORD, + IL_PROT = 1 << IL_PROT_ORD, + + IL_DBND = 1 << IL_DBND_ORD, + IL_SP3S = 1 << IL_SP3S_ORD, + IL_INVS = 1 << IL_INVS_ORD, + IL_TYPS = 1 << IL_TYPS_ORD, + + IL_ATMS = 1 << IL_ATMS_ORD, + + IL_XCGA = 1 << IL_XCGA_ORD, + + IL_FMLF = 1 << IL_FMLF_ORD, + IL_HFIX = 1 << IL_HFIX_ORD, + IL_TRNS = 1 << IL_TRNS_ORD, + IL_REC_ = 1 << IL_REC__ORD +} IDENT_LBL_BIT; + + + +/* + Aux Info constants +*/ +const INCHI_TAG AuxLbl[] = +{ + /* prefixes may be combined in this order */ +/* AL_FIXH_ORD, */ { "/", "fixed_H", "fixed-H", 0 }, /* fixed-H */ +/* AL_ISOT_ORD, */ { "/", "isotopic", "isotopic", 0 }, /* isotopic */ +/* AL_STER_ORD, */ { "/", "abs_stereo_inverted", "stereo.abs.inverted", 0 }, /* inv abs sp3 stereo */ +/* AL_REVR_ORD, */ { "/", "reversibility", "reversibility", 0 }, /* reversibility */ + /* items */ +/* AL_VERS_ORD, */ { "", "version", "version", 1 }, +/* AL_NORM_ORD, */ { "/", "normalization_type", "norm-type", 1 }, +/* AL_ANBR_ORD, */ { "/N:", "original_atom_numbers", "atom.orig-nbr", 1 }, +/* AL_AEQU_ORD, */ { "/E:", "atom_equivalence", "atom.equivalence", 0 }, +/* AL_GEQU_ORD, */ { "/gE:", "group_equivalence", "group.equivalence", 0 }, + /* inv abs sp3 stereo */ +/* AL_SP3I_ORD, */ { "/it:", "sp3", "sp3", 0 }, +/* AL_SP3N_ORD, */ { "/iN:", "original_atom_numbers", "atom.orig-nbr", 0 }, + +/* AL_CRV__ORD, */ { "/CRV:", "charge_radical_valence", "charges-rad-val", 0 }, + /* reversibility */ +/* AL_ATMR_ORD, */ { "/rA:", "atoms", "atoms", 0 }, +/* AL_BNDR_ORD, */ { "/rB:", "bonds", "bonds", 0 }, +/* AL_XYZR_ORD, */ { "/rC:", "xyz", "xyz", 0 }, + /* fixed-H only */ +/* AL_FIXN_ORD, */ { "/F:", "original_atom_numbers", "atom.orig-nbr", 1 }, + /* isotopic only */ +/* AL_ISON_ORD, */ { "/I:", "original_atom_numbers", "atom.orig-nbr", 1 }, + +/* AL_REC__ORD, */ { "/R:", "reconnected bond(s) to metal(s) part", "", 1 } +}; + + +typedef enum tagAuxLblOrd +{ + AL_FIXH_ORD, + AL_ISOT_ORD, + AL_STER_ORD, + AL_REVR_ORD, + + AL_VERS_ORD, + AL_NORM_ORD, + AL_ANBR_ORD, + AL_AEQU_ORD, + AL_GEQU_ORD, + + AL_SP3I_ORD, + AL_SP3N_ORD, + + AL_CRV__ORD, + + AL_ATMR_ORD, + AL_BNDR_ORD, + AL_XYZR_ORD, + + AL_FIXN_ORD, + + AL_ISON_ORD, + + AL_REC__ORD, + + AL_MAX_ORD /* max number of tags */ +} AUX_LBL_ORD; + + +typedef enum tagAuxLblBit +{ + AL_FIXH = 1 << AL_FIXH_ORD, + AL_ISOT = 1 << AL_ISOT_ORD, + AL_STER = 1 << AL_STER_ORD, + AL_REVR = 1 << AL_REVR_ORD, + + AL_VERS = 1 << AL_VERS_ORD, + AL_NORM = 1 << AL_NORM_ORD, + AL_ANBR = 1 << AL_ANBR_ORD, + AL_AEQU = 1 << AL_AEQU_ORD, + AL_GEQU = 1 << AL_GEQU_ORD, + + AL_SP3I = 1 << AL_SP3I_ORD, + AL_SP3N = 1 << AL_SP3N_ORD, + + AL_CRV_ = 1 << AL_CRV__ORD, + + AL_ATMR = 1 << AL_ATMR_ORD, + AL_BNDR = 1 << AL_BNDR_ORD, + AL_XYZR = 1 << AL_XYZR_ORD, + + AL_FIXN = 1 << AL_FIXN_ORD, + + AL_ISON = 1 << AL_ISON_ORD, + + AL_REC_ = 1 << AL_REC__ORD +} AUX_LBL_BIT; + +const int MAX_TAG_NUM = inchi_max((int)IL_MAX_ORD, (int)AL_MAX_ORD); + +char *szGetTag(const INCHI_TAG *Tag, int nTag, int bTag, char *szTag, int *bAlways); + +#define SP(N) (x_space+sizeof(x_space)-1-(N)) + + + +#define NOT_YET_I2I_FOR_POLYMERS 40 + + +/* + Print error message (plain text) +*/ +int OutputINChIPlainError( INCHI_IOSTREAM *out_file, + char *pErrorText, + int bError ) +{ + /* char szBuf[64]; */ + const char *pErr; + char *szErrorText = pErrorText; + int nEstLen, ret = 0; + + switch( bError ) { + case _IS_WARNING: + pErr = x_warn; + break; + case _IS_ERROR: + pErr = x_err; + break; + default: /* _IS_FATAL */ + pErr = x_ferr; + break; + } + /* <%s: >, x_message */ + nEstLen = (int) (sizeof(x_message)-1 + 1 + 1 + /* <%s=\"%s\">, x_type, pErr */ + + sizeof(x_type)-1 + 1 + 1 + strlen(pErr) + 1 + /* < %s=\"%s\"\n>, x_text, szErrorText */ + + 1 + sizeof(x_text)-1 + 1 + 1 + strlen(szErrorText) + 1 + 1); + + inchi_ios_print( out_file, + "%s: %s=\"%s\" %s=\"%s\"", + x_message, x_type, pErr, x_text, szErrorText ); + ret = 1; + + return ret; +} + + +#ifndef OUT_TN /* defined in mode.h; quoted here for reference purposes only */ + +#define OUT_N1 0 /* non-tautomeric only */ +#define OUT_T1 1 /* tautomeric if present otherwise non-tautomeric */ +#define OUT_NT 2 /* only non-taut representations of tautomeric */ +#define OUT_TN 3 /* tautomeric if present otherwise non-tautomeric; + sepatately output non-taut representations of tautomeric if present */ +/* OUT_TN = OUT_T1 + OUT_NT */ +#endif + + +/* + Calculate equivalence mark + (used to check for repeating (sub)layer(s) ) +*/ +const char *EquString( int EquVal ) +{ + int bFrom = EquVal & (iiSTEREO | iiSTEREO_INV | iiNUMB | iiEQU ); + int bType = EquVal & (iitISO | iitNONTAUT ); + int bEq2 = EquVal & (iiEq2NONTAUT | iiEq2ISO | iiEq2INV ); + const char *r = ""; + +#if ( FIX_EMPTY_LAYER_BUG == 1 ) + int bEmpty= EquVal & iiEmpty; + if ( bEmpty ) { + r = "e"; + return r; + } +#endif + + switch ( bFrom ) { + + case iiSTEREO: /* ------------ Stereo --------------------*/ + switch ( bType ) { + case iitISO: /* iso main stereo =... */ + switch( bEq2 ) { + case 0: + r = "m"; /* iso main stereo = main stereo */ + break; + default: + r = "??"; /* should not happen */ + break; + } + break; + case iitNONTAUT: /* non-taut stereo =... */ + switch( bEq2 ) { + case 0: + r = "m"; /* non-taut stereo = main stereo */ + break; + default: + r = "??"; /* should not happen */ + break; + } + break; + case (iitNONTAUT | iitISO): /* iso non-taut stereo = ... */ + switch( bEq2 ) { + case 0: + r = "m"; /* iso non-taut stereo = main stereo */ + break; + case iiEq2ISO: + r = "M"; /* iso non-taut stereo = main iso stereo */ + break; + case iiEq2NONTAUT: + r = "n"; /* iso non-taut stereo = non-taut stereo */ + break; + default: + r = "??"; /* should not happen */ + break; + } + break; + default: + r = "??"; /* should not happen */ + break; + } + break; + + case iiSTEREO_INV: /*---------- Inverted Aux Stereo ------*/ + if ( bEq2 & iiEq2INV ) { /* stereo = Inverted(another stereo) */ + bEq2 &= ~iiEq2INV; + switch( bType ) { + case 0: /* main = ...*/ + switch( bEq2 ) { + case 0: + r = "im"; /* main = Inv(main) */ + break; + case iiEq2ISO: + r = "iM"; /* main = Inv(main iso) */ + break; + case iiEq2NONTAUT: + r = "in"; /* maim = Inv(non-taut) */ + break; + case (iiEq2NONTAUT | iiEq2ISO): + r = "iN"; /* maim = Inv(non-taut iso ) */ + break; + default: + r = "??"; /* should not happen */ + break; + } + break; + case iitISO: /* main iso = ...*/ + switch( bEq2 ) { + case 0: + r = "im"; /* main iso = Inv(main) */ + break; + case iiEq2ISO: + r = "iM"; /* main iso = Inv(main iso) */ + break; + case iiEq2NONTAUT: + r = "in"; /* maim iso = Inv(non-taut) */ + break; + case (iiEq2NONTAUT | iiEq2ISO): + r = "iN"; /* maim = Inv(non-taut iso ) */ + break; + default: + r = "??"; /* should not happen */ + break; + } + break; + case iitNONTAUT: /* non-taut = ... */ + switch( bEq2 ) { + case 0: + r = "im"; /* non-taut = Inv(main) */ + break; + case iiEq2ISO: + r = "iM"; /* non-taut = Inv(main iso) */ + break; + case iiEq2NONTAUT: + r = "in"; /* non-taut = Inv(non-taut) */ + break; + case (iiEq2NONTAUT | iiEq2ISO): + r = "iN"; /* non-taut = Inv(non-taut iso ) */ + break; + default: + r = "??"; /* should not happen */ + break; + } + break; + case (iitNONTAUT | iitISO): + switch( bEq2 ) { + case 0: + r = "im"; /* non-taut iso = Inv(main) */ + break; + case iiEq2ISO: + r = "iM"; /* non-taut iso = Inv(main iso) */ + break; + case iiEq2NONTAUT: + r = "in"; /* non-taut iso = Inv(non-taut) */ + break; + case (iiEq2NONTAUT | iiEq2ISO): + r = "iN"; /* non-taut iso = Inv(non-taut iso ) */ + break; + default: + r = "??"; /* should not happen */ + } + break; + default: + r = "??"; /* should not happen */ + break; + } + } else { /* Inv stereo = another (non-inverted) stereo */ + + switch( bType ) { + case iitISO: /* main iso = ...*/ + switch( bEq2 ) { + case 0: + r = "m"; /* main = (inverted aux) main */ + break; + default: + r = "??"; /* should not happen */ + break; + } + break; + case iitNONTAUT: /* non-taut = ... */ + switch( bEq2 ) { + case 0: + r = "m"; /* non-taut = (inverted aux) main */ + break; + default: + r = "??"; /* should not happen */ + break; + } + break; + case (iitNONTAUT | iitISO): /* non-taut iso = ...*/ + switch( bEq2 ) { + case 0: + r = "m"; /* non-taut iso = (inverted aux) main */ + break; + case iiEq2ISO: + r = "M"; /* non-taut iso = (inverted aux) main iso */ + break; + case iiEq2NONTAUT: + r = "n"; /* non-taut iso = (inverted aux) non-taut */ + break; + default: + r = "??"; /* should not happen */ + break; + } + break; + default: + r = "??"; /* should not happen */ + break; + } + } + break; + + case ( iiNUMB | iiSTEREO_INV): /*------------- Inv Stereo Numbering ------------*/ + switch( bType ) { + case 0: /* inv stereo numb main = ...*/ + switch( bEq2 ) { + case 0: + r = "m"; /* inv stereo numb main = main numb */ + break; + default: + r = "??"; /* should not happen */ + break; + } + break; + case iitISO: /* inv stereo iso numb main = ...*/ + switch( bEq2 ) { + case 0: + r = "m"; /* inv stereo iso numb main = main numb */ + break; + case iiEq2INV: + r = "im"; /* inv stereo iso numb main = InvStereo(main) numb */ + break; + case iiEq2ISO: + r = "M"; /* inv stereo iso numb main = isotopic main numb */ + break; + default: + r = "??"; /* should not happen */ + break; + } + break; + case iitNONTAUT: /* inv stereo numb non-taut = ... */ + switch( bEq2 ) { + case 0: + r = "m"; /* inv stereo numb non-taut = main numb */ + break; + case iiEq2NONTAUT: + r = "n"; /* inv stereo numb non-taut = non-taut numb */ + break; + case iiEq2INV: + r = "im"; /* inv stereo numb non-taut = InvStereo(main) numb */ + break; + default: + r = "??"; /* should not happen */ + break; + } + break; + case (iitNONTAUT | iitISO): /* inv stereo numb non-taut iso = ... */ + switch( bEq2 ) { + case 0: + r = "m"; /* inv stereo numb non-taut iso = main numb */ + break; + case iiEq2ISO: + r = "M"; /* inv stereo numb non-taut iso = main numb iso */ + break; + case (iiEq2ISO | iiEq2INV): + r = "iM"; /* inv stereo numb non-taut iso = InvStereo(main iso) numb */ + break; + case iiEq2NONTAUT: + r = "n"; /* inv stereo numb non-taut iso = non-taut numb */ + break; + case (iiEq2NONTAUT | iiEq2ISO): + r = "N"; /* inv stereo numb non-taut iso = non-taut iso numb */ + break; + case iiEq2INV: + r = "im"; /* inv stereo numb non-taut iso = InvStereo(main) numb */ + break; + case (iiEq2NONTAUT | iiEq2INV): + r = "in"; /* inv stereo numb non-taut iso = InvStereo(non-taut) numb ) */ + break; + default: + r = "??"; /* should not happen */ + break; + } + break; + default: + r = "??"; /* should not happen */ + break; + } + break; + + case iiNUMB: /*------------- Canonical Numbering ------------*/ + switch( bType ) { + case 0: /* numb main = ...*/ + r = "??"; /* should not happen */ + break; + case iitISO: /* iso numb main = ...*/ + switch( bEq2 ) { + case 0: + r = "m"; /* iso numb main = main numb */ + break; + default: + r = "??"; /* should not happen */ + } + break; + case iitNONTAUT: /* numb non-taut = ... */ + switch( bEq2 ) { + case 0: + r = "m"; /* numb non-taut = main numb */ + break; + default: + r = "??"; /* should not happen */ + } + break; + case (iitNONTAUT | iitISO): /* numb non-taut iso = ... */ + switch( bEq2 ) { + case 0: + r = "m"; /* numb non-taut iso = main numb */ + break; + case iiEq2ISO: + r = "M"; /* numb non-taut iso = main numb iso */ + break; + case iiEq2NONTAUT: + r = "n"; /* numb non-taut iso = non-taut numb */ + break; + default: + r = "??"; /* should not happen */ + break; + } + break; + default: + r = "??"; /* should not happen */ + break; + } + break; + + case iiEQU: /*------------- Atom Equivalence ------------*/ + switch( bType ) { + case 0: /* equivalence main = ...*/ + r = "??"; /* should not happen */ + break; + case iitISO: /* equivalence main iso = ...*/ + switch( bEq2 ) { + case 0: + r = "m"; /* equivalence main = main equ */ + break; + default: + r = "??"; /* should not happen */ + break; + } + break; + case iitNONTAUT: /* equivalence non-taut = ... */ + switch( bEq2 ) { + case 0: + r = "m"; /* equivalence non-taut = main equ */ + break; + default: + r = "??"; /* should not happen */ + break; + } + break; + case (iitNONTAUT | iitISO): /* equivalence non-taut iso = ... */ + switch( bEq2 ) { + case 0: + r = "m"; /* equivalence non-taut iso = main equ */ + break; + case iiEq2ISO: + r = "M"; /* equivalence non-taut iso = main iso equ */ + break; + case iiEq2NONTAUT: + r = "n"; /* equivalence non-taut iso = non-taut equ */ + break; + default: + r = "??"; /* should not happen */ + break; + } + break; + default: + r = "??"; /* should not happen */ + break; + } + break; + default: + r = "??"; /* should not happen */ + break; + } + return r; +} + + +#define OUT_NONTAUT OUT_NN /* was OUT_NT until 2004-04-07 */ + + +/* + OutputINChI2( ... ) + + called from SortAndPrintINChI( ... ) +*/ +int OutputINChI2( CANON_GLOBALS *pCG, + INCHI_IOSTREAM_STRING *strbuf, + INCHI_SORT *pINChISortTautAndNonTaut2[][TAUT_NUM], + int INCHI_basic_or_INCHI_reconnected, + ORIG_STRUCT *pOrigStruct, + int bDisconnectedCoord, + int bOutputType, + int bINChIOutputOptions, + int bAbcNumbers, + int bCtPredecessors, + int bNoStructLabels, + int num_components2[], + int num_non_taut2[], + int num_taut2[], + INCHI_IOSTREAM *output_file, + INCHI_IOSTREAM *log_file, + int num_input_struct, + const char *szSdfLabel, + const char *szSdfValue, + long lSdfId, + int *pSortPrintINChIFlags, + unsigned char save_opt_bits) +{ + int bINChIOutputOptions0 = bINChIOutputOptions & ~(INCHI_OUT_PLAIN_TEXT | INCHI_OUT_PLAIN_TEXT_COMMENTS); + int bINChIOutputOptionsCur; + int bCurOption, ret, i; + + ret = 0; + + for ( i = 0; i < 3; i ++ ) + { + switch( i ) + { + case 1: + bCurOption = INCHI_OUT_PLAIN_TEXT; + break; + case 2: + bCurOption = INCHI_OUT_PLAIN_TEXT_COMMENTS; + break; + default: + continue; + } + if ( bINChIOutputOptions & bCurOption ) + { + bINChIOutputOptionsCur = bINChIOutputOptions0 | bCurOption; + if ( i != 1 ) + { + bINChIOutputOptionsCur &= ~INCHI_OUT_TABBED_OUTPUT; + } + ret |= OutputINChI1( pCG, + strbuf, + pINChISortTautAndNonTaut2, + INCHI_basic_or_INCHI_reconnected, + pOrigStruct, + bDisconnectedCoord, bOutputType, bINChIOutputOptionsCur, + bAbcNumbers, bCtPredecessors, bNoStructLabels, + num_components2, num_non_taut2, num_taut2, + output_file, log_file, num_input_struct, + szSdfLabel, szSdfValue, lSdfId, + pSortPrintINChIFlags, save_opt_bits); + } + } + + return ret; +} + + +/* + OutputINChI1( ... ) + + Main actual worker which serializes InChI to string. + + Called from OutputINChI2( ... ) and from itself + +*/ +int OutputINChI1( CANON_GLOBALS *pCG, + INCHI_IOSTREAM_STRING *strbuf, + INCHI_SORT *pINChISortTautAndNonTaut2[][TAUT_NUM], + int INCHI_basic_or_INCHI_reconnected, + ORIG_STRUCT *pOrigStruct, + int bDisconnectedCoord, + int bOutputType, + int bINChIOutputOptions, + int bAbcNumbers, + int bCtPredecessors, + int bNoStructLabels, + int num_components2[], + int num_non_taut2[], + int num_taut2[], + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM *log_file, + int num_input_struct, + const char *szSdfLabel, + const char *szSdfValue, + long lSdfId, + int *pSortPrintINChIFlags, + unsigned char save_opt_bits) +{ + OutputINCHI_CtlData io; + + + /* + + bINChIOutputOptions bits: + + INCHI_OUT_NO_AUX_INFO 0x0001 do not output Aux Info + INCHI_OUT_SHORT_AUX_INFO 0x0002 output short version of Aux Info + INCHI_OUT_ONLY_AUX_INFO 0x0004 output only Aux Info + INCHI_OUT_EMBED_REC 0x0008 embed reconnected INChI into disconnected INChI + + */ + + + /* + bOutputType = + TAUT_YES => tautomeric only (if no tautomeric components then no output; + TAUT_NON => only non-tautomeric output (if no non-taut present then no output; + TAUT_BOTH => tautomeric and non-tautomeric + */ + int i, j, ii, jj, /*ii2, jj2,*/ bEmbeddedOutputCalled=0; + int bTautIsoHNum, bTautIsoAt, bHasIsotopicAtoms[TAUT_NUM]; + int bStereoSp2[TAUT_NUM], bStereoSp3[TAUT_NUM]; + int bIsotopicStereoSp2[TAUT_NUM], bIsotopicStereoSp3[TAUT_NUM]; + int bStereoAbsInverted[TAUT_NUM], bIsotopicStereoAbsInverted[TAUT_NUM]; + int bStereoAbs[TAUT_NUM], bIsotopicStereoAbs[TAUT_NUM]; + int bTautomericAcid, bHardAddRemProton; + int bRequestedRacemicStereo=0, bRequestedRelativeStereo = 0; + int ind, inc, npass = 0; + + INCHI_SORT *is, *is2; + INChI *pINChI /*, *pINChI2*/; + INChI_Aux *pINChI_Aux = NULL; + + int ret = 0; /* 0=>failed, 1=>success */ + int intermediate_result = 0; + int then_goto_repeat = 0; + int max_num_comp; + int bHasIsoH; + int nNumMovedProtons; + int bTautAndNonTaut, bTautIsNonTaut; + int nAtomsAllComp1, nAtomsAllComp2; /* v. 1.05 Total atoms in all components */ + + int bPlainText = 0 != (bINChIOutputOptions & (INCHI_OUT_PLAIN_TEXT | INCHI_OUT_PLAIN_TEXT_COMMENTS)); + int bPlainTextCommnts = 0 != (bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT_COMMENTS); + + char *pLF, *pTAB; + +#ifdef TARGET_LIB_FOR_WINCHI + /* @@@ From now on we will go with silent output; it ends on @@@ below */ + int silent = 1; +#endif + + int bFixTranspChargeBug = 0; +#if ( FIX_TRANSPOSITION_CHARGE_BUG == 1 ) /* 2008-01-02 */ + if ( INCHI_OUT_FIX_TRANSPOSITION_CHARGE_BUG & bINChIOutputOptions ) + bFixTranspChargeBug = 1; +#endif + + io.bAbcNumbers = bAbcNumbers; + + io.ATOM_MODE = ((io.bAbcNumbers?CT_MODE_ABC_NUMBERS:0) + | CT_MODE_ATOM_COUNTS + | CT_MODE_NO_ORPHANS +#if ( EQL_H_NUM_TOGETHER == 1 ) + | CT_MODE_EQL_H_TOGETHER +#endif +#if ( ABC_CT_NUM_CLOSURES == 1 ) + | (io.bAbcNumbers && bCtPredecessors? CT_MODE_ABC_NUM_CLOSURES:0) +#endif + | (bCtPredecessors?CT_MODE_PREDECESSORS:0)); + + io.TAUT_MODE = (io.bAbcNumbers?CT_MODE_ABC_NUMBERS:0); + io.pSortPrintINChIFlags = pSortPrintINChIFlags; + io.num_components = num_components2[INCHI_basic_or_INCHI_reconnected]; + io.pINChISortTautAndNonTaut = pINChISortTautAndNonTaut2[INCHI_basic_or_INCHI_reconnected]; + io.pINChISort = io.pINChISortTautAndNonTaut[TAUT_YES]; + io.pINChISort2 = io.pINChISortTautAndNonTaut[TAUT_YES]; + io.bAlways = 0; + io.bUseMulipliers = 1; + io.bOmitRepetitions = 1; + io.bPlainTextTags = 2; /* 0 => no plain tags, 1=> plain text tags, 2=>plaintext tags without consecutive // */ + io.bOutputType = bOutputType; /* remains constant */ + io.bOutType = bOutputType; /* will change! */ + + io.bSecondNonTautPass = 0; + io.bNonTautIsoIdentifierNotEmpty = 0; + io.bNonTautNonIsoIdentifierNotEmpty = 0; + io.bNonTautIsIdenticalToTaut = 1; + io.bFhTag = 0; + io.nTag = bPlainTextCommnts? 3 : bPlainText? 2 : 0; /* tag type */ + + ind = -1; + inc = -1; + + + + set_line_separators( bINChIOutputOptions, &pLF, &pTAB ); + + memset( io.sDifSegs, DIFV_BOTH_EMPTY, sizeof(io.sDifSegs) ); + + if ( !strbuf || !(strbuf->pStr) || strbuf->nAllocatedLength<=0 ) + { + inchi_ios_eprint(log_file, + "Cannot allocate output buffer. No output for structure #%d.%s%s%s%s\n", + num_input_struct, SDF_LBL_VAL(szSdfLabel, szSdfValue)); + return ret; + } + + /* -- commented out to allow empty InChI -- + if (!io.num_components ) return 0; + */ + + for ( i = 0; i < TAUT_NUM; i ++ ) + { + bHasIsotopicAtoms[i] = io.num_comp[i] = + bStereoSp2[i] = bStereoSp3[i] = + bIsotopicStereoSp2[i] = bIsotopicStereoSp3[i] = + io.bIsotopicOrigNumb[i] = + bStereoAbs[i] = bIsotopicStereoAbs[i] = + bStereoAbsInverted[i] = bIsotopicStereoAbsInverted[i] = + io.bRacemicStereo[i] = io.bRelativeStereo[i] = + io.bIsotopicRacemicStereo[i] = io.bIsotopicRelativeStereo[i] = + io.bAtomEqu[i] = io.bTautEqu[i] = + io.bIsotopicAtomEqu[i] = io.bIsotopicTautEqu[i] = + io.bInvStereo[i] = io.bInvIsotopicStereo[i] = + io.bInvStereoOrigNumb[i] = io.bInvIsotopicStereoOrigNumb[i] = + io.bIgn_UU_Sp3[i] = io.bIgn_UU_Sp2[i] = + io.bIgn_UU_Sp3_Iso[i] = io.bIgn_UU_Sp2_Iso[i] = + io.bChargesRadVal[i] = io.bOrigCoord[i] = 0; + } + + /* Find if it is isotopic */ + io.bIsotopic = io.bTautomeric = io.bNonTautomeric = bTautomericAcid = + bHardAddRemProton = bTautIsoHNum = bTautIsoAt = 0; + bTautAndNonTaut = bTautIsNonTaut = 0; + + /* + x = bStereo, bStereoSp2, bStereoSp3, bStereoAbsInverted, + bIsotopicStereo, bIsotopicStereoSp2, bIsotopicStereoSp3, bIsotopicStereoAbsInverted + + OUT_N1: x[TAUT_NON] refers to non-tautomeric only + OUT_T1: x[TAUT_YES] refers to tautomeric if exists otherwise non-tautomeric + OUT_NT: x[TAUT_NON] refers to non-taut representations of tautomeric + OUT_TN: x[TAUT_YES] refers to tautomeric if exists otherwise non-tautomeric + x[TAUT_NON] refers to non-taut representations of tautomeric + */ + + memset( io.num_iso_H, 0, sizeof(io.num_iso_H) ); + io.nNumRemovedProtons = 0; + nNumMovedProtons = 0; + bHasIsoH = 0; + io.bTautomericOutputAllowed = (io.bOutType==OUT_T1 || io.bOutType== OUT_TN); + io.pINChISort = + io.pINChISortTautAndNonTaut[io.bTautomericOutputAllowed? TAUT_YES : TAUT_NON]; + is = io.pINChISort; + is2 = (io.bOutType== OUT_TN)? io.pINChISortTautAndNonTaut[TAUT_NON] : NULL; + + + for ( i = 0, is2 = io.pINChISortTautAndNonTaut[TAUT_NON]; + i < io.num_components; + i++, is++, is2? is2++ : NULL ) + { + + CompINChILayers( is, is2, io.sDifSegs, bFixTranspChargeBug ); + + io.bNonTautIsIdenticalToTaut = + io.bNonTautIsIdenticalToTaut && !CompINChITautVsNonTaut(is, is2, 1); + + if ( is && (pINChI_Aux = is->pINChI_Aux[TAUT_YES]) ) + { + for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) + { + bHasIsoH += abs(pINChI_Aux->nNumRemovedIsotopicH[j]); + io.num_iso_H[j] += pINChI_Aux->nNumRemovedIsotopicH[j]; + } + io.nNumRemovedProtons += pINChI_Aux->nNumRemovedProtons; + nNumMovedProtons += abs(pINChI_Aux->nNumRemovedProtons); + } + + if ( io.bTautomericOutputAllowed ) + { + /* check for removed isotopic H */ + for ( j = TAUT_YES; j < TAUT_NUM; j ++ ) + { + switch ( io.bOutType ) { + case OUT_N1: /* x[TAUT_NON]: non-tautomeric only -- never happens */ + jj = GET_II(io.bOutType,is); + if ( jj != j ) + continue; + ii = TAUT_NON; + break; + case OUT_T1: /* x[TAUT_YES]: tautomeric if present otherwise non-tautomeric */ + jj = GET_II(io.bOutType,is); + if ( jj != j ) + continue; + ii = TAUT_YES; + break; + case OUT_NT: /* x[TAUT_NON]: only non-taut representations of tautomeric -- never happens */ + jj = GET_II(io.bOutType,is); + if ( jj != j ) + continue; + ii = TAUT_NON; + break; + /* main path of control flow */ + case OUT_TN: /* x[TAUT_YES]: tautomeric if present otherwise non-tautomeric; + * x[TAUT_NON]: non-taut only if tautomeric is present */ + jj = ( j == TAUT_YES )? GET_II(OUT_T1,is) : ( j == TAUT_NON )? GET_II(OUT_NT,is) : -1; + if ( jj == TAUT_YES ) + { + /* Fix12 */ + if ( is->pINChI[jj]->lenTautomer > 0 ) + { + bTautAndNonTaut += (!is->pINChI[jj]->bDeleted && HAS_N(is)); + } else + { + bTautIsNonTaut ++; + } + } + if ( jj < 0 ) + continue; + ii = j; + break; + default: + continue; + } + if ( jj != j ) + continue; + if ( (pINChI = is->pINChI[jj]) && pINChI->nNumberOfAtoms > 0 && (pINChI_Aux = is->pINChI_Aux[jj]) ) + { + bTautIsoHNum += (pINChI_Aux->nNumRemovedIsotopicH[0] + + pINChI_Aux->nNumRemovedIsotopicH[1] + + pINChI_Aux->nNumRemovedIsotopicH[2]); + bTautIsoAt += (pINChI->nNumberOfIsotopicAtoms>0 || pINChI->nNumberOfIsotopicTGroups > 0 ); + } + } + } + } + + io.sDifSegs[DIFL_M ][DIFS_p_PROTONS] = io.nNumRemovedProtons? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; + io.sDifSegs[DIFL_MI][DIFS_h_H_ATOMS] = bHasIsoH? DIFV_NEQ2PRECED : DIFV_BOTH_EMPTY; + + MarkUnusedAndEmptyLayers( io.sDifSegs ); + + io.bNonTautIsIdenticalToTaut = io.bNonTautIsIdenticalToTaut && !bTautIsoHNum; + + + nAtomsAllComp1 = nAtomsAllComp2 = 0; + for ( i = 0, is = io.pINChISort; i < io.num_components; i ++, is ++ ) + { + int bCurIso, bCurStereo, bCurIsoStereo, bCurHasIsoStereo /* Fix14 */, bCurTaut /*, bCurTaut2*/; + int bCompExists, bCurIsoHPos, bCurIsoHStereo; + int bCurStereoSp2, bCurIsoStereoSp2, bCurStereoSp3, bCurIsoStereoSp3, bCurIsoStereoSp3Inv; + int bCurRacemic, bCurRelative, bCurIsoRacemic, bCurIsoRelative; + bCompExists = 0; + + for ( j = TAUT_NON; j < TAUT_NUM; j ++ ) + { + switch ( io.bOutType ) { + case OUT_N1: /* x[TAUT_NON]: non-tautomeric only */ + jj = GET_II(io.bOutType,is); + if ( jj != j ) + continue; + ii = TAUT_NON; + break; + case OUT_T1: /* x[TAUT_YES]: tautomeric if present otherwise non-tautomeric */ + jj = GET_II(io.bOutType,is); + if ( jj != j ) + continue; + ii = TAUT_YES; + break; + case OUT_NT: /* x[TAUT_NON]: only non-taut representations of tautomeric */ + jj = GET_II(io.bOutType,is); + if ( jj != j ) + continue; + ii = TAUT_NON; + break; + /* main control flow comes here: requested both mobile and fixed H results */ + case OUT_TN: /* x[TAUT_YES]: tautomeric if present otherwise non-tautomeric; + * x[TAUT_NON]: non-taut only if tautomeric is present */ + jj = ( j == TAUT_YES )? GET_II(OUT_T1,is) : ( j == TAUT_NON )? GET_II(OUT_NT,is) : -1; + if ( jj < 0 ) + { + /* Fix12 */ + if ( bTautAndNonTaut && bTautIsNonTaut && + j == TAUT_NON && 0 <= (jj = GET_II(OUT_T1,is)) && + !is->pINChI[jj]->bDeleted && !is->pINChI[jj]->lenTautomer ) + { + ; /* the requested non-tautomeric component is in tautomeric position + (is->pINChI[TAUT_YES]); + process it also as non-tautomeric if Fixed-H layer was requested */ + } + else + { + continue; + } + } + ii = j; /* ii is what we wanted; jj is what we found (0 = TAUT_NON: fixed_H, 1 = TAUT_YES: mobile_H) */ + /* -- not used 2004-09-16 --- + if ( is2 ) { + jj2 = ( j == TAUT_YES )? GET_II(OUT_T1,is2) : ( j == TAUT_NON )? GET_II(OUT_NT,is2) : -1; + if ( jj2 >= 0 ) { + ii2 = j; + } else { + ii2 = -1; + } + } else { + jj2 = ii2 = -1; + } + -----------------------------*/ + break; + default: + continue; + } + if ( (pINChI = is->pINChI[jj]) && pINChI->nNumberOfAtoms > 0 ) + { + /*pINChI_Aux = is->pINChI_Aux[jj];*/ + bCompExists++; + + if ( j == TAUT_NON ) + nAtomsAllComp1+= pINChI->nNumberOfAtoms; + else if ( j == TAUT_YES ) + nAtomsAllComp2+= pINChI->nNumberOfAtoms; + + + bCurTaut = (pINChI->lenTautomer > 0); + bCurIso = (pINChI->nNumberOfIsotopicAtoms>0 || pINChI->nNumberOfIsotopicTGroups > 0 ); + bCurIsoHPos = (pINChI->nPossibleLocationsOfIsotopicH && pINChI->nPossibleLocationsOfIsotopicH[0] > 1 || pINChI->lenTautomer > 1); + /* present isotopic H + their possible positions AND/OR isotopic atoms */ + bCurIsoHStereo = bCurIsoHPos && (bTautIsoHNum || bTautIsoAt) || bCurIso; + if ( jj == j && pINChI->bDeleted ) + { + io.num_comp[j] --; + if ( bCurTaut ) + { + io.bTautomeric |= 1; /* tautomeric representation is present */ + io.bNonTautomeric |= HAS_N(is); + } + io.bIsotopic |= bCurIso; + continue; /* deleted H(+) in tautomeric representation */ + } + bCurStereoSp2 = pINChI->Stereo && (pINChI->Stereo->nNumberOfStereoBonds > 0); + bCurHasIsoStereo = + bCurStereoSp3 = pINChI->Stereo && (pINChI->Stereo->nNumberOfStereoCenters > 0 ); + bCurIsoStereoSp2 = bCurIsoHStereo && pINChI->StereoIsotopic && (pINChI->StereoIsotopic->nNumberOfStereoBonds > 0); + bCurIsoStereoSp3 = bCurIsoHStereo && pINChI->StereoIsotopic && (pINChI->StereoIsotopic->nNumberOfStereoCenters > 0); + bCurIsoStereoSp3Inv = bCurIsoStereoSp3 && pINChI->StereoIsotopic->nCompInv2Abs; /* inversion changes sp3 stereo */ + bRequestedRacemicStereo |= (0!=(pINChI->nFlags & INCHI_FLAG_RAC_STEREO)); + + bRequestedRelativeStereo |= (0!=(pINChI->nFlags & INCHI_FLAG_REL_STEREO)); + /* check whether isotopic stereo is same as non-isotopic; if same than do not output isotopic stereo */ + if ( bCurStereoSp2 && bCurIsoStereoSp2 ) + { + bCurIsoStereoSp2 = !Eql_INChI_Stereo( pINChI->Stereo, EQL_SP2, pINChI->StereoIsotopic, EQL_SP2, 0 ); + } + if ( bCurStereoSp3 && bCurIsoStereoSp3 ) + { + /* bCurIsoStereoSp3=0 means (iso stereo sp3) = (non-iso stereo sp3) or (iso stereo sp3) = Inv(non-iso stereo sp3) */ + bCurIsoStereoSp3 = !Eql_INChI_Stereo( pINChI->Stereo, EQL_SP3, pINChI->StereoIsotopic, EQL_SP3, + (pINChI->nFlags & INCHI_FLAG_RAC_STEREO) || (pINChI->nFlags & INCHI_FLAG_REL_STEREO) ); + if ( !bCurIsoStereoSp3 ) { + /* inversion changes iso sp3 differently from non-iso sp3 Fix11 */ + bCurIsoStereoSp3Inv &= (pINChI->StereoIsotopic->nCompInv2Abs != pINChI->Stereo->nCompInv2Abs); + } + } + + bCurRelative = bRequestedRelativeStereo && bCurStereoSp3; +#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) + bCurRelative = bCurRelative && + (pINChI->Stereo->nNumberOfStereoCenters > 1 ) && + (pINChI->Stereo->nCompInv2Abs != 0) && +#endif + + + + bCurIsoRelative = bRequestedRelativeStereo && (bCurIsoStereoSp3 || bCurIsoStereoSp3Inv); +#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) + bCurIsoRelative = bCurIsoRelative && + (pINChI->StereoIsotopic->nNumberOfStereoCenters > 1 ) && + (pINChI->StereoIsotopic->nCompInv2Abs != 0) && +#endif + + + bCurRacemic = bRequestedRacemicStereo && bCurStereoSp3; +#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) + bCurRacemic = bCurRacemic && + (pINChI->Stereo->nCompInv2Abs != 0) && + (pINChI->Stereo->nNumberOfStereoCenters > 0 ) ? + pINChI->Stereo->nNumberOfStereoCenters : 0; +#endif + + bCurIsoRacemic = bRequestedRacemicStereo && (bCurIsoStereoSp3 || bCurIsoStereoSp3Inv); +#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) + bCurIsoRacemic = bCurIsoRacemic & + (pINChI->StereoIsotopic->nCompInv2Abs != 0) && + (pINChI->StereoIsotopic->nNumberOfStereoCenters > 0 ) ? + pINChI->StereoIsotopic->nNumberOfStereoCenters : 0; +#endif + if ( bRequestedRelativeStereo ) + { + bCurStereoSp3 = bCurRelative || bCurStereoSp3 && (pINChI->Stereo->nNumberOfStereoCenters > 1 ); /* Fix11 */ + bCurIsoStereoSp3 = bCurIsoRelative ? bCurIsoStereoSp3 : 0; + } + else + { + if ( bRequestedRacemicStereo ) + { + bCurStereoSp3 = bCurRacemic > 1 || bCurStereoSp3 && (pINChI->Stereo->nNumberOfStereoCenters > 1 ); /* Fix11 */ + bCurIsoStereoSp3 = bCurIsoRacemic > 1? bCurIsoStereoSp3 : 0; + } + } + bCurStereo = bCurStereoSp2 || bCurStereoSp3; + bCurIsoStereo = bCurIsoStereoSp2 || bCurIsoStereoSp3; + + io.bIsotopic |= bCurIso; + bHasIsotopicAtoms[ii] |= bCurIso; + bStereoSp2[ii] |= bCurStereoSp2; + bStereoSp3[ii] |= bCurStereoSp3; + io.bIgn_UU_Sp3[ii] |= !bCurStereoSp3 && (pINChI->nFlags & INCHI_FLAG_SC_IGN_ALL_UU); + io.bIgn_UU_Sp2[ii] |= !bCurStereoSp2 && (pINChI->nFlags & INCHI_FLAG_SB_IGN_ALL_UU); + bIsotopicStereoSp2[ii] |= bCurIsoStereoSp2; + bIsotopicStereoSp3[ii] |= bCurIsoStereoSp3; + io.bIgn_UU_Sp3_Iso[ii] |= !bCurIsoStereoSp3 && (pINChI->nFlags & INCHI_FLAG_SC_IGN_ALL_ISO_UU); + io.bIgn_UU_Sp2_Iso[ii] |= !bCurIsoStereoSp2 && (pINChI->nFlags & INCHI_FLAG_SB_IGN_ALL_ISO_UU); + bStereoAbs[ii] |= bCurStereoSp3 && (pINChI->Stereo->nCompInv2Abs != 0); + bStereoAbsInverted[ii] |= bCurStereoSp3 && (pINChI->Stereo->nCompInv2Abs < 0); + /* Fix08: missing isotopic inverted flag if isotopic = inverted non-isotopic */ + bIsotopicStereoAbsInverted[ii] |= bCurIsoStereoSp3 && (pINChI->StereoIsotopic->nCompInv2Abs < 0) || + !bCurIsoStereoSp3 && pINChI->StereoIsotopic && pINChI->Stereo && + pINChI->StereoIsotopic->nCompInv2Abs && + pINChI->StereoIsotopic->nCompInv2Abs != pINChI->Stereo->nCompInv2Abs; + /* Fix 11: missing /s1 if only isotopic stereo is inverted */ + bIsotopicStereoAbs[ii] |= bCurIsoStereoSp3 && (pINChI->StereoIsotopic->nCompInv2Abs != 0) || + !bCurIsoStereoSp3 && pINChI->StereoIsotopic && pINChI->Stereo && + pINChI->StereoIsotopic->nCompInv2Abs && + pINChI->StereoIsotopic->nCompInv2Abs != pINChI->Stereo->nCompInv2Abs; + + io.bRelativeStereo[ii] |= bCurRelative; + io.bIsotopicRelativeStereo[ii] |= bCurIsoRelative; + io.bRacemicStereo[ii] |= bCurRacemic; + io.bIsotopicRacemicStereo[ii] |= bCurIsoRacemic; + + + bTautomericAcid |= (0!=(pINChI->nFlags & INCHI_FLAG_ACID_TAUT)); + bHardAddRemProton |= (0!=(pINChI->nFlags & INCHI_FLAG_HARD_ADD_REM_PROTON)); + if ( bCurTaut ) + { + io.bTautomeric |= 1; /* tautomeric representation is present */ + /* does tautomeric structure have also a non-tautomeric repesentation? */ + io.bNonTautomeric |= HAS_N(is); + } + + /* auxiliary info */ + if ( !(bINChIOutputOptions & INCHI_OUT_NO_AUX_INFO) && (pINChI_Aux = is->pINChI_Aux[jj]) ) + { + /* detect presence of constitutional equivalence onfo */ + int bCurEqu, bCurTautEqu=0, bCurIsoEqu=0, bCurIsoTautEqu=0; /* Fix15-disabled */ + io.bAtomEqu[ii] |= (bCurEqu = bHasEquString( pINChI_Aux->nConstitEquNumbers, + pINChI_Aux->nNumberOfAtoms)); + if ( bCurTaut ) + { + io.bTautEqu[ii] |= (bCurTautEqu = bHasEquString( pINChI_Aux->nConstitEquTGroupNumbers, + pINChI_Aux->nNumberOfTGroups)); + } + if ( bCurIso ) + { + io.bIsotopicAtomEqu[ii] |= (bCurIsoEqu = bHasEquString( pINChI_Aux->nConstitEquIsotopicNumbers, + pINChI_Aux->nNumberOfAtoms)) /*|| bCurEqu*/; + if ( bCurTaut ) + { + io.bIsotopicTautEqu[ii] |= (bCurIsoTautEqu = bHasEquString( pINChI_Aux->nConstitEquIsotopicTGroupNumbers, + pINChI_Aux->nNumberOfTGroups)) /*|| bCurTautEqu*/; + } + /* non-zero if isotopic numbering for inverted isotopic stereo is different */ + io.bIsotopicOrigNumb[ii] |= bCurHasIsoStereo && /* Fix14 */ + pINChI_Aux->nOrigAtNosInCanonOrdInv && + pINChI_Aux->nIsotopicOrigAtNosInCanonOrd && + (0 != memcmp( pINChI_Aux->nOrigAtNosInCanonOrdInv, + pINChI_Aux->nIsotopicOrigAtNosInCanonOrd, + sizeof(pINChI_Aux->nOrigAtNosInCanonOrdInv[0]) + * pINChI_Aux->nNumberOfAtoms)); + } + /* inverted stereo */ + if ( bCurStereoSp3 && pINChI->Stereo->nCompInv2Abs ) + { + io.bInvStereo[ii] |= 1; + io.bInvStereoOrigNumb[ii] |= pINChI_Aux->nOrigAtNosInCanonOrd && + pINChI_Aux->nOrigAtNosInCanonOrdInv && + (0 != memcmp( pINChI_Aux->nOrigAtNosInCanonOrd, + pINChI_Aux->nOrigAtNosInCanonOrdInv, + sizeof(pINChI_Aux->nOrigAtNosInCanonOrd[0]) + * pINChI_Aux->nNumberOfAtoms)); + } + /* inverted isotopic stereo */ + if ( bCurIsoStereoSp3 && pINChI->StereoIsotopic->nCompInv2Abs ) + { + io.bInvIsotopicStereo[ii] |= 1; + io.bInvIsotopicStereoOrigNumb[ii] |= pINChI_Aux->nIsotopicOrigAtNosInCanonOrd && + pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv && + (0 != memcmp( pINChI_Aux->nIsotopicOrigAtNosInCanonOrd, + pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv, + sizeof(pINChI_Aux->nIsotopicOrigAtNosInCanonOrd[0]) + * pINChI_Aux->nNumberOfAtoms)); + } + if ( pINChI_Aux->OrigInfo && bHasOrigInfo(pINChI_Aux->OrigInfo, pINChI_Aux->nNumberOfAtoms) ) + { + io.bChargesRadVal[ii] |= 1; + } + } + } + } + if ( bCompExists ) + { + for ( j = TAUT_NON; j < TAUT_NUM; j ++ ) + { + io.num_comp[j] ++; + } + } + } + if ( io.bTautomeric /*&& bTautomericAcid*/ ) /* "&& bTautomericAcid" commented out 2004-06-02 */ + { + io.bTautomeric += bTautomericAcid; /* long-range tautomerism */ + io.bTautomeric += (bHardAddRemProton? 4 : 0); + } + if ( bRequestedRacemicStereo || bRequestedRelativeStereo ) + { + /* do not output inverted stereo info */ + for ( i = 0; i < TAUT_NUM; i ++ ) + { + /* Fix11 */ + bStereoAbsInverted[i] = + bStereoAbs[i] = + io.bInvStereo[i] = + io.bInvStereoOrigNumb[i] = 0; + /* io.bIsotopicRelativeStereo[i]=0 may happen because iso stereo is same or inverted non-iso stereo */ + bIsotopicStereoAbsInverted[i] = + bIsotopicStereoAbs[i] = + io.bInvIsotopicStereo[i] = + io.bInvIsotopicStereoOrigNumb[i] = 0; + } + } + + + io.iCurTautMode = io.bOutType == OUT_N1? TAUT_NON: /* only non-taut */ + io.bOutType == OUT_T1? TAUT_YES: /* tautomeric if present, otherwise non-tautomeric */ + io.bOutType == OUT_NT? TAUT_NON: /* only non-taut representations of tautomeric */ + io.bOutType == OUT_TN? TAUT_YES: /* tautomeric if present otherwise non-tautomeric; */ + -1; /* separately output non-taut representations of + tautomeric if present */ + + if ( io.iCurTautMode < 0 ) + { + return 0; /* error */ + } + + + /* + Now print out + */ + + io.bOverflow = 0; + io.num_components = io.num_comp[io.iCurTautMode]; + max_num_comp = inchi_max(io.num_comp[TAUT_NON], io.num_comp[TAUT_YES]); + + if ( bINChIOutputOptions & INCHI_OUT_ONLY_AUX_INFO ) + { + goto output_aux_info; + } + + io.nCurINChISegment = DIFL_M; + + + /* Structure (Compound) Header */ + if ( INCHI_basic_or_INCHI_reconnected == INCHI_BAS ) + Output_RecordInfo( out_file, num_input_struct, bNoStructLabels, szSdfLabel, szSdfValue, lSdfId, pLF, pTAB ); + + + /* InChI output: version and kind */ + if ( INCHI_basic_or_INCHI_reconnected == INCHI_BAS || !(bINChIOutputOptions & INCHI_OUT_EMBED_REC)) + { + int is_beta = 0; + int nAtomsAllComp = inchi_max(nAtomsAllComp1, nAtomsAllComp2); + + if ( nAtomsAllComp > NORMALLY_ALLOWED_INP_MAX_ATOMS ) + { + /* v. 1.05 for LargeMolecules */ + is_beta = 1; + } + if ( pOrigStruct && pOrigStruct->polymer ) + { + /* v. 1.05 for Polymers */ + is_beta = 1; + } + OutputINCHI_VersionAndKind( out_file, strbuf, bINChIOutputOptions, is_beta, pLF, pTAB ); + } + + + + /* InChI output: atoms */ + intermediate_result = OutputINCHI_MainLayerFormula( pCG, out_file, strbuf, num_components2, + &INCHI_basic_or_INCHI_reconnected, + &io, pLF, pTAB ); + if ( intermediate_result != 0 ) + goto exit_function; + + /* InChI output: connection table */ + intermediate_result = OutputINCHI_MainLayerConnections( pCG, out_file, strbuf, num_components2, + &INCHI_basic_or_INCHI_reconnected, + &io, pLF, pTAB ); + if ( intermediate_result != 0 ) + goto exit_function; + + /* InChI output: hydrogens (with tautomeric info) */ + intermediate_result = OutputINCHI_MainLayerHydrogens( pCG, out_file, strbuf, num_components2, + &INCHI_basic_or_INCHI_reconnected, + &io, pLF, pTAB ); + if ( intermediate_result != 0 ) + goto exit_function; + + io.bFhTag = 0; + npass = 0; + + +repeat_INChI_output: + + /* InChI output: charge and removed protons */ + intermediate_result = OutputINCHI_ChargeAndRemovedAddedProtonsLayers( pCG, out_file, strbuf, + &io, pLF, pTAB); + if ( intermediate_result != 0 ) + goto exit_function; + + /* InChI output: polymer layer */ + if ( npass == 0 ) + { + intermediate_result = OutputINCHI_PolymerLayer( pCG, out_file, strbuf, + &INCHI_basic_or_INCHI_reconnected, + pOrigStruct, &io, pLF, pTAB ); + if ( intermediate_result != 0 ) + goto exit_function; + } + + /* InChI output: stereo (non-isotopic) */ + intermediate_result = OutputINCHI_StereoLayer( pCG,out_file, strbuf, &io, pLF, pTAB ); + if ( intermediate_result != 0 ) + goto exit_function; + + + /* Switch from M to MI or from F to FI */ + io.nCurINChISegment ++; + + + /* InChI output: isotopic */ + intermediate_result = OutputINCHI_IsotopicLayer( pCG, out_file, strbuf, + &INCHI_basic_or_INCHI_reconnected, + &io,pLF, pTAB ); + + if ( intermediate_result != 0 ) + goto exit_function; + + + + /* + At this point the INChI part of the output has been done. + If this INChI is tautomeric and non-tautomeric results exist, + then we need to output non-tautomeric data: + fixed H + stereo + isotopic + isotopic stereo + */ + + + /* InChI output: FixedH and sublayers */ + intermediate_result = OutputINCHI_FixedHLayerWithSublayers( pCG, out_file, strbuf, + &INCHI_basic_or_INCHI_reconnected, + &io, pLF, pTAB, &then_goto_repeat ); + if ( intermediate_result != 0 ) + goto exit_function; + if ( then_goto_repeat ) + { + npass++; + goto repeat_INChI_output; + } + + + /* + InChI output: reconnected structure + */ + + bEmbeddedOutputCalled = 0; + if ( bDisconnectedCoord && INCHI_basic_or_INCHI_reconnected == INCHI_BAS && + (bINChIOutputOptions & INCHI_OUT_EMBED_REC) && num_components2[INCHI_REC] ) + { + int nRet; + bEmbeddedOutputCalled = 1; + + /* output blank line before /R: in case of bPlainTextCommnts=1 */ + inchi_ios_print_nodisplay( out_file, "%s", pLF ); + /* end of disconnected INChI output */ + + nRet = OutputINChI1( pCG, strbuf, pINChISortTautAndNonTaut2, + INCHI_REC, pOrigStruct, 0 /*bDisconnectedCoord*/, + bOutputType, bINChIOutputOptions | INCHI_OUT_NO_AUX_INFO, + bAbcNumbers, bCtPredecessors, bNoStructLabels, + num_components2, num_non_taut2, num_taut2, + out_file, log_file, num_input_struct, + szSdfLabel, szSdfValue, lSdfId, + pSortPrintINChIFlags, save_opt_bits); + + if ( !nRet ) + goto exit_function; /* error */ + } + + /* Here! Intercept point: edit polymer layer if it is present */ + if ( pOrigStruct && pOrigStruct->polymer ) + { + HidePolymerRelatedInternals( out_file ); + } + + + /* InChI output: save InChI creation options if requested */ + if ( !bEmbeddedOutputCalled && + ( bINChIOutputOptions & INCHI_OUT_SAVEOPT ) && + ( 0==(bINChIOutputOptions & INCHI_OUT_STDINCHI) ) /* not std-InChI output */ + ) + { + char let1, let2; + GetSaveOptLetters(save_opt_bits, &let1, &let2); + inchi_ios_print_nodisplay( out_file, "\\%c%c", let1, let2 ); + } + if ( !bEmbeddedOutputCalled && !bPlainTextCommnts ) + { /* plain text comment earlier ended with LF */ + inchi_ios_print_nodisplay( out_file, "%s%s", + (!num_components2[0] && !num_components2[1])? "//":"", /* empty InChI=// */ + (bINChIOutputOptions & INCHI_OUT_NO_AUX_INFO)? "\n" : pTAB ); + /* end of INChI= output */ + } + + inchi_strbuf_reset( strbuf ); + +#ifdef TARGET_LIB_FOR_WINCHI + /* @@@ Here we end up with silent output: display previously hidden output */ + if ( inchi_ios_flush_not_displayed( out_file ) != -1 ) + silent = 0; +#endif + +output_aux_info: + /* + Output Aux Info + */ + io.bFhTag = 0; + + if( (bINChIOutputOptions & INCHI_OUT_NO_AUX_INFO) == 0 ) + { + + io.num_components = io.num_comp[io.iCurTautMode]; + + /* AuxInfo: header and normalization type */ + intermediate_result = OutputAUXINFO_HeaderAndNormalization_type( pCG, out_file, strbuf, + bINChIOutputOptions, + &INCHI_basic_or_INCHI_reconnected, + num_components2, + &io, pLF, pTAB ); + if ( intermediate_result != 0 ) + goto exit_function; + + +repeat_INChI_Aux_output: + /* AuxInfo: original atom numbers and symmetry numbers (constit. equivalence /E: ) */ + intermediate_result = OutputAUXINFO_OriginalNumbersAndEquivalenceClasses( pCG, + out_file, strbuf, + num_components2, + &io, pLF, pTAB ); + if ( intermediate_result != 0 ) + goto exit_function; + + /* AuxInfo: tautomeric groups equivalence */ + intermediate_result = OutputAUXINFO_TautomericGroupsEquivalence( pCG, + out_file, strbuf, + &io, pLF, pTAB ); + if ( intermediate_result != 0 ) + goto exit_function; + + /* AuxInfo: stereo data */ + intermediate_result = OutputAUXINFO_Stereo( pCG, out_file, strbuf, &io, pLF, pTAB ); + if ( intermediate_result != 0 ) + goto exit_function; + +repeat_INChI_Aux_Iso_output: + /* AuxInfo: isotopic info */ + intermediate_result = OutputAUXINFO_IsotopicInfo( pCG, out_file, strbuf, + &INCHI_basic_or_INCHI_reconnected, + &io, pLF, pTAB ); + if ( intermediate_result != 0 ) + goto exit_function; + + + /* + At this point the INChI_Aux part of the output has been completed. + If this INChI is tautomeric and non-tautomeric results exist, + then we need to output non-tautomeric auxilialy data + (same as above excluding tautomeric information). + Currently, this is enabled for xml output only + */ + + if ( io.bOutType == OUT_TN && io.bTautomeric && io.bNonTautomeric && + /* Check whether the Fixed-H layer is empty */ + (*pSortPrintINChIFlags & ((INCHI_basic_or_INCHI_reconnected==INCHI_BAS)? FLAG_SORT_PRINT_NO_NFIX_H_BAS : + FLAG_SORT_PRINT_NO_NFIX_H_REC )) && + (*pSortPrintINChIFlags & ((INCHI_basic_or_INCHI_reconnected==INCHI_BAS)? FLAG_SORT_PRINT_NO_IFIX_H_BAS : + FLAG_SORT_PRINT_NO_IFIX_H_REC )) + ) + { + io.bNonTautomeric = 0; /* bNonTautIdentifierNotEmpty == 0 => no fixed H info 02-10-2995 */ + } + + if ( io.bOutType == OUT_TN && io.bTautomeric && io.bNonTautomeric ) + { + /* add the second (non-tautomeric) output */ + io.bOutType = OUT_NONTAUT; + io.iCurTautMode = TAUT_NON; + io.pINChISort = io.pINChISortTautAndNonTaut[TAUT_NON]; + io.bSecondNonTautPass = 1; + io.num_components = io.num_comp[io.iCurTautMode]; + io.bFhTag = AL_FIXH; + inchi_strbuf_reset( strbuf ); /*pStr[io.tot_len=0] = '\0';*/ + + /* if InChI Fixed-H isotopic is empty then do not output corresponding AuxInfo */ + if ( !(*pSortPrintINChIFlags & + ((INCHI_basic_or_INCHI_reconnected==INCHI_BAS)? FLAG_SORT_PRINT_NO_NFIX_H_BAS : + FLAG_SORT_PRINT_NO_NFIX_H_REC )) + ) + { + npass++; + goto repeat_INChI_Aux_output; + } + else + { + npass++; + goto repeat_INChI_Aux_Iso_output; + } + } + else + { + if ( io.bOutType == OUT_NONTAUT && io.bOutputType == OUT_TN && io.bTautomeric && io.bNonTautomeric ) + { + /* the second (non-taut) output has been done; restore variables */ + io.bOutType = OUT_TN; + io.iCurTautMode = TAUT_YES; + io.pINChISort = io.pINChISortTautAndNonTaut[TAUT_YES]; + io.bSecondNonTautPass = 0; + /* set correct num components for the reversibility info 02-10-2005 */ + io.num_components = io.num_comp[io.iCurTautMode]; + io.bFhTag = 0; + } + } + + /* Charges, radicals, unusual valences */ + intermediate_result = OutputAUXINFO_ChargesRadicalsAndUnusualValences( pCG, + out_file, strbuf, + &io, pLF, pTAB ); + if ( intermediate_result != 0 ) + goto exit_function; + + + /* Output the original input structure -- quick fix */ + intermediate_result = OutputAUXINFO_ReversibilityInfo( pCG, out_file, strbuf, + pOrigStruct, &io, pLF, pTAB ); + if ( intermediate_result != 0 ) + goto exit_function; + + /* Output polymeric Aux Info */ + intermediate_result = OutputAUXINFO_PolymerInfo( pCG,out_file, strbuf, + pOrigStruct, &io, pLF, pTAB ); + if ( intermediate_result != 0 ) + goto exit_function; + + /* + Output INChI_Aux of the reconnected structure + */ + + bEmbeddedOutputCalled = 0; + if ( bDisconnectedCoord && INCHI_basic_or_INCHI_reconnected == INCHI_BAS && (bINChIOutputOptions & INCHI_OUT_EMBED_REC) && + num_components2[INCHI_REC] && !(bINChIOutputOptions & INCHI_OUT_NO_AUX_INFO) ) + { + int nRet; + bEmbeddedOutputCalled = 1; + inchi_ios_print( out_file, "%s", pLF ); + + nRet = OutputINChI1( pCG, strbuf, pINChISortTautAndNonTaut2, + INCHI_REC, NULL, 0 /*bDisconnectedCoord*/, + bOutputType, INCHI_OUT_ONLY_AUX_INFO | bINChIOutputOptions, + bAbcNumbers, bCtPredecessors, bNoStructLabels, + num_components2, num_non_taut2, num_taut2, + out_file, log_file, num_input_struct, + szSdfLabel, szSdfValue, lSdfId, + pSortPrintINChIFlags,save_opt_bits); + + if ( !nRet ) + goto exit_function; /* error */ + } + + /* Close INChI_Aux */ + if ( !bEmbeddedOutputCalled && !bPlainTextCommnts ) + { + inchi_ios_print( out_file, "%s\n", (!num_components2[0] && !num_components2[1])? "//":"" ); + /* plain text comment earlier ended with LF */ + } + + /* in wINChI window, separate AuxInfo: from InChIKey: with blank line */ + inchi_ios_print( out_file, "%s", + (bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW) ? "\n":""); + } /* end of output AuxInfo */ + + ret = 1; + + +exit_function: + + +#ifdef TARGET_LIB_FOR_WINCHI + /* @@@ If for any error we get here silent, display previously hidden output */ + if ( silent ) + if ( !inchi_ios_flush_not_displayed( out_file ) != -1 ) + silent = 0; +#endif + + if ( io.bOverflow ) + inchi_ios_print( out_file, "\nFATAL ERROR: Output buffer overflow\n"); + + if ( intermediate_result ) + { + ret = 0; + inchi_ios_eprint(log_file, "InChI serialization error for structure #%d.%s%s%s%s\n", + num_input_struct, SDF_LBL_VAL(szSdfLabel, szSdfValue)); + } + + return ret; +} /* OutputINChI1 */ + + + + +/* + szGetTag( ... ) +*/ +char *szGetTag( const INCHI_TAG *Tag, int nTag, int bTag, char *szTag, int *bAlways ) +{ + int i, j, bit, num, len; + if ( 0 < nTag && nTag < 3 ) { + /* no plain text comments: pick up the last tag */ + for ( i = 0, j = -1, bit = 1; i < MAX_TAG_NUM; i ++, bit <<= 1 ) { + if ( bTag & bit ) { + j = i; + } + } + if ( j >= 0 ) { + strcpy( szTag, nTag == 1? Tag[j].szXmlLabel : nTag == 2? Tag[j].szPlainLabel : "???" ); + if ( nTag != 2 ) { + *bAlways = Tag[j].bAlwaysOutput; + } + return szTag; + } + } else + if ( nTag == 3 ) { + /* plain text with comments */ + szTag[0] = '{'; + szTag[1] = '\0'; + for ( i = 0, j = -1, bit = 1, num=0; i < MAX_TAG_NUM; i ++, bit <<= 1 ) { + if ( bTag & bit ) { + j = i; + if ( num ++ ) { + strcat( szTag, ":" ); + } + strcat( szTag, Tag[i].szPlainComment ); + } + } + if ( num ) { + strcat( szTag, "}" ); + num = (int) strlen( Tag[j].szPlainLabel ); + len = (int) strlen( szTag ); + if ( len ) { + memmove( szTag + num, szTag, len+1 ); + memcpy( szTag, Tag[j].szPlainLabel, num ); + } else { + strcpy ( szTag, Tag[j].szPlainLabel ); + } + *bAlways = Tag[j].bAlwaysOutput; + } else { + strcpy( szTag, "???" ); + } + return szTag; + } + strcpy( szTag, "???" ); + return szTag; +} + + +/* + str_LineStart( ... ) + + if ind < 0 (common usage, plain text output) + just resets buffer pStr (by placing '\0' into pStr[0] ) + *obsolete* if ind >=0 XML output embeds val in between XML tags + +*/ +int str_LineStart( const char *tag,char *tag2, int val2, + INCHI_IOSTREAM_STRING *buf, int ind ) +{ + if ( ind < 0 ) + { + /* plain text output */ + inchi_strbuf_reset( buf ); + } + /* else *obsolete* XML output, obsolete: NOT USED ANYMORE */ + + return 0; +} + + +/* + str_LineEnd( ... ) + + First, checks if buffer overflow; then: + if ind < 0 (common usage, plain text output) + sets terminating '\0' in pStr, + optionally adds leading tag (e.g., '/' or "/c" ) + *obsolete* if ind >=0 XML output + +*/ +int str_LineEnd( const char *tag, int *bOverflow, + INCHI_IOSTREAM_STRING *buf, + int ind, int bPlainTextTags ) +{ + static const int add_tag_len = sizeof(x_line_closing)-1 + sizeof(x_close_line)-1; + int tag_len; + + /* check buffer overflow */ + if ( *bOverflow ) + return 1; + + if ( ind < 0 ) + { + /* Plain text */ + /* insert plain text tag if: + (a) pStr has non-zero length, or + (b) ind < -1 + */ + if ( buf->pStr[0] || ind < -1 ) + { + tag_len = bPlainTextTags ? (int) strlen( tag ) : 0; + if ( tag_len > 0 ) + { + int n_added = tag_len + 2 + 2; + inchi_strbuf_update( buf, n_added ); + memmove( buf->pStr+tag_len, buf->pStr, buf->nUsedLength + 1 ); + /* NB: trailing 0 is also memmoved */ + memcpy( buf->pStr, tag, tag_len ); + /* to be sure... */ + buf->nUsedLength = strlen( buf->pStr ); + } + } + } + + return 0; +} + + +/* + CleanOrigCoord +*/ +int CleanOrigCoord( MOL_COORD szCoord, int delim ) +{ +#define MIN_BOND_LENGTH (1.0e-6) + char szVal[LEN_COORD+1]; + MOL_COORD szBuf; + char *q; + int len, last, fst, dec_pnt, num_zer=0, len_buf = 0, e; + int k, i; + double coord; + + for ( k = 0; k < NUM_COORD*LEN_COORD; k += LEN_COORD ) + { + memcpy( szVal, szCoord+k, LEN_COORD ); + szVal[LEN_COORD] = '\0'; + lrtrim(szVal, &len); + coord = strtod(szVal, &q); + if ( MIN_BOND_LENGTH > fabs(coord) ) + { + strcpy( szVal, "0" ); + len = 1; + num_zer ++; + } + else + { + len = (int) (q - szVal); + /* last = (last mantissa digit position + 1) */ + if ( (q = strchr(szVal, 'e')) || (q = strchr(szVal, 'E')) || + (q = strchr(szVal, 'd')) || (q = strchr(szVal, 'D')) ) { + /* floating point */ + last = q - szVal; + /* remove (+) and leading zeroes from the exponent */ + e = (int)strtol( szVal+last+1, &q, 10 ); /* exponent */ + if ( e ) { + /* new exp; update the length */ + len = last+1+sprintf( szVal+last+1, "%d", e ); /* print exp without leading zeroes and '+' */ + } else { + /* exponent is zero */ + len = last; + } + } else { + last = len; + } + /* fst = (first mantissa digit); fst=1 if the sign is present, otherwise 0 */ + fst = (szVal[0]!='.' && !isdigit( UCINT szVal[0] )); + /* dec_pnt = (decimal point position) or last */ + if ( q = strchr(szVal, '.') ) { + dec_pnt = (int) (q - szVal); + } else { + dec_pnt = last; + } + last -= 1; /* last mantissa digit position */ + /* remove trailing zeroes in the range dec_pnt+1..last-1 */ + for ( i = last; dec_pnt < i && '0' == szVal[i]; i -- ) + ; + if ( i == dec_pnt ) { + i --; /* remove decimal point, too */ + } + if ( i < last ) { + memmove( szVal+i+1, szVal+last+1, len-last ); + len -= last-i; + } + /* remove leading zeroes */ + for ( i = fst; i < len && '0' == szVal[i]; i ++ ) + ; + if ( i > fst ) { + memmove( szVal + fst, szVal+i, len-fst ); + len -= i-fst; + } + } + if ( len_buf ) + szBuf[len_buf++] = delim; + memcpy( szBuf + len_buf, szVal, len ); /* does not copy zero termination*/ + len_buf += len; + } + /* zero termination */ + if ( len_buf < (int)sizeof(MOL_COORD) ) { + memset( szBuf+len_buf, 0, sizeof(MOL_COORD) - len_buf); + } + memcpy( szCoord, szBuf, sizeof(MOL_COORD) ); + return num_zer; +#undef MIN_BOND_LENGTH +} + + +/* + WriteOrigCoord +*/ +int WriteOrigCoord( int num_inp_atoms, MOL_COORD *szMolCoord, int *i, char *szBuf, int buf_len ) +{ + + int j, num_zer, len, cur_len; + char *p; + MOL_COORD szCurCoord; + cur_len = 0; + for ( j = *i; j < num_inp_atoms; ) { + memcpy( szCurCoord, szMolCoord[j], sizeof(szCurCoord)); + num_zer = CleanOrigCoord( szCurCoord, ',' ); + if ( NUM_COORD == num_zer ) { + len = 0; + } else { + if ( p = (char *)memchr( szCurCoord, '\0', sizeof(szCurCoord)) ) { + len = (int) (p - szCurCoord); + } else { + len = sizeof(szCurCoord); + } + } + if ( len + cur_len + 1 < buf_len ) { + if ( len ) { + memcpy( szBuf + cur_len, szCurCoord, len * sizeof(szBuf[0]) ); + } + szBuf[cur_len += len] = ';'; + cur_len ++; + j ++; + } else { + break; + } + } + szBuf[cur_len] = '\0'; + *i = j; /* next item */ + return cur_len; +} + + +/* + WriteOrigAtoms + + number of atoms + [c|n] chiral/nonchiral + + Element + #valence + +/-[charge>1] + .#rad (#rad=1, 2, 3: singlet, doulet, triplet) + [.]i#iso_mass + [.]{o|e|u|?} atom parity = {1:2:3:4} + [.]h[#of 1H>1] + [.]d[#of 2H>1] + [.]t[#of 3H>1] + + Note: . occurs only once and only if radical or 1-character element +*/ +int WriteOrigAtoms( CANON_GLOBALS *pCG, + int num_inp_atoms, inp_ATOM *at, int *i, + char *szBuf, int buf_len, STRUCT_DATA *sd) +{ + int j, k, n, len, len0, cur_len, val, bonds_val, mw, parity, num_trans, is_ok, b_self; + static const char szIsoH[] = "hdt"; + char szCurAtom[32]; + AT_NUMB nNeighOrder[MAXVAL], neigh; + + cur_len = 0; + if ( 0 == *i ) { + cur_len = sprintf( szBuf, "%d%s", num_inp_atoms, + (sd->bChiralFlag & FLAG_INP_AT_CHIRAL)? "c" : + (sd->bChiralFlag & FLAG_INP_AT_NONCHIRAL)? "n" : "" ); + } + for ( j = *i; j < num_inp_atoms; ) { + /* tetrahedral parity treatment */ + parity = 0; + num_trans = 0; + if ( at[j].p_parity ) { + /* verify neighbors */ + is_ok = 1; + b_self = 0; + for ( n = 0, k = 0; n < MAX_NUM_STEREO_ATOM_NEIGH; n ++ ) { + neigh = at[j].p_orig_at_num[n]-1; + if ( is_in_the_list( at[j].neighbor, neigh, at[j].valence ) && + at[neigh].orig_at_number == at[j].p_orig_at_num[n] ) { + /* real neighbor */ + nNeighOrder[k ++] = at[j].p_orig_at_num[n]; + } else + if ( (int)neigh == j && at[neigh].orig_at_number == at[j].p_orig_at_num[n] ) { + /* central atom is a neighbor */ + num_trans = n; /* move this neighbor to 0 position permutation parity */ + b_self ++; + } else { + is_ok = 0; + break; + } + } + if ( is_ok && b_self <= 1 && b_self + k == MAX_NUM_STEREO_ATOM_NEIGH ) { + num_trans += insertions_sort( pCG, nNeighOrder, k, sizeof(nNeighOrder[0]), comp_AT_RANK ); + if ( ATOM_PARITY_WELL_DEF( at[j].p_parity ) ) { + parity = 2 - (num_trans + at[j].p_parity) % 2; + } else + if ( ATOM_PARITY_ILL_DEF( at[j].p_parity ) ) { + parity = at[j].p_parity; + } else { + ; /* invalid atom parity */ + } + } else { + ;/* add error message here */ + } + } + + len = len0 = (int) strlen( at[j].elname ); + memcpy( szCurAtom, at[j].elname, len ); + bonds_val = nBondsValenceInpAt( at+j, NULL, NULL ); + + if ( (val=needed_unusual_el_valence( at[j].el_number, at[j].charge, at[j].radical, + at[j].chem_bonds_valence, bonds_val, at[j].num_H, at[j].valence )) || + at[j].charge || at[j].radical || at[j].iso_atw_diff || NUM_ISO_H(at,j) || parity ) { + /* valence */ + if ( val ) { + len += sprintf( szCurAtom + len, "%d", val > 0? val : 0 ); + } + /* charge */ + if ( val = at[j].charge ) { + szCurAtom[len++] = val>0? '+' : '-'; + if ( (val = abs(val)) > 1 ) { + len += sprintf( szCurAtom + len, "%d", val ); + } + } + /* radical */ + if ( val = at[j].radical ) { + len += sprintf(szCurAtom + len, ".%d", val); + } + /* isotopic shift */ + if ( val = at[j].iso_atw_diff ) { + mw = get_atomic_mass_from_elnum( at[j].el_number ); + if ( val == 1 ) + val = mw; + else + if ( val > 0 ) + val = mw + val -1; + else + val = mw + val; + len += sprintf( szCurAtom + len, "%si%d", len == len0? ".":"", val ); + } + /* parity */ + if ( parity ) { + len += sprintf( szCurAtom + len, "%s%s", len == len0? ".":"", + parity == AB_PARITY_ODD? "o" : + parity == AB_PARITY_EVEN? "e" : + parity == AB_PARITY_UNKN? "u" : + parity == AB_PARITY_UNDF? "?" : "" ); + } + /* implicit isotopic H */ + if ( NUM_ISO_H(at,j) ) { + for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) { + if ( val = at[j].num_iso_H[k] ) { + len += sprintf( szCurAtom + len, "%s%c", len == len0? ".":"", szIsoH[k] ); + if ( val > 1 ) { + len += sprintf(szCurAtom + len, "%d", val); + } + } + } + } + } + if ( len + cur_len < buf_len ) { + memcpy( szBuf + cur_len, szCurAtom, len ); + cur_len += len; + j ++; + } else { + break; + } + szBuf[cur_len] = '\0'; + *i = j; + } + return cur_len; +} + + +/* + WriteOrigBonds( ... ) + + Output bonds in ascending order of the neighboring atom original numbers + + bpA;bpAbpA... + +b = bond type: +============= +w = undefined stereo, double +s = single +d = double +t = triple +a = aromatic +p = up from the current atom to the neighbor +P = uP from the neighbor to the current atom +v = undefined stereo Either, single from the current atom to the neighbor +V = undefined stereo Either, single from the neighbor to the current atom +n = down from the current atom to the neighbor +N = dowN from the neighbor to the current atom + +p = bond parity: +================ +- = odd ++ = even +u = unknown +? = undefined + = no parity (empty) + + +A = neighbor orig. atom number +=============== +neighbor orig. atom number < number of the current atom +Number of the current atom: 2 until first ";", 3 until 2nd ";", etc. + +*/ +int WriteOrigBonds( CANON_GLOBALS *pCG, + int num_inp_atoms, inp_ATOM *at, int *i, + char *szBuf, int buf_len, STRUCT_DATA *sd) +{ + int j, k, k2, kk, len, cur_len, j2=0, bond_stereo, bond_char, bond_parity, bond_parityNM, num_trans; + char szCurBonds[7*MAXVAL+2]; /* num_neigh*(1 byte bond type + 2 bytes for bond parity up to 4 digits per neighbor number) + at the end one ';' */ + AT_RANK nNeighOrder[MAXVAL]; + int chain_len, pnxt_atom, pinxt2cur, pinxt_sb_parity_ord; + int chain_len2, pnxt_atom2, pinxt2cur2, pinxt_sb_parity_ord2, m1, m2; + int pcur_atom, picur2nxt, picur_sb_parity_ord; + + cur_len = 0; + for ( j = *i; j < num_inp_atoms; ) { + len = 0; + if ( at[j].valence > 1 ) { + for ( k = 0; k < at[j].valence; k ++ ) { + nNeighOrder[k] = k; + } + pCG->m_pn_RankForSort = at[j].neighbor; + num_trans = insertions_sort( pCG, nNeighOrder, at[j].valence, sizeof(nNeighOrder[0]), CompRank ); + } else { + num_trans = 0; + nNeighOrder[0] = 0; + } + for ( kk = 0; kk < at[j].valence; kk ++ ) { + k = nNeighOrder[kk]; + j2 = at[j].neighbor[k]; + bond_parity = 0; + bond_parityNM = 0; + if ( j2 < j ) { + bond_stereo = at[j].bond_stereo[k]; + switch( at[j].bond_type[k] ) { + case BOND_TYPE_SINGLE: + switch( bond_stereo ) { + case STEREO_SNGL_UP: + bond_char = 'p'; + break; + case -STEREO_SNGL_UP: + bond_char = 'P'; + break; + case STEREO_SNGL_DOWN: + bond_char = 'n'; + break; + case -STEREO_SNGL_DOWN: + bond_char = 'N'; + break; +#if ( FIX_EITHER_STEREO_IN_AUX_INFO == 1 ) + case STEREO_SNGL_EITHER: + bond_char = 'v'; + break; + case -STEREO_SNGL_EITHER: + bond_char = 'V'; + break; +#else + case STEREO_SNGL_EITHER: + case -STEREO_SNGL_EITHER: + bond_char = 'v'; + break; +#endif + default: + bond_char = 's'; + break; + } + break; + case BOND_TYPE_DOUBLE: + switch( bond_stereo ) { + case STEREO_DBLE_EITHER: + case -STEREO_DBLE_EITHER: + bond_char = 'w'; + break; + default: + bond_char = 'd'; + break; + } + break; + case BOND_TYPE_TRIPLE: + bond_char = 't'; + break; + case BOND_TYPE_ALTERN: + bond_char = 'a'; + break; + default: + bond_char = 's'; + break; + } + /* check for allene/cumulene */ + k2 = (int) (is_in_the_list( at[j2].neighbor, (AT_NUMB)j, at[j2].valence ) - at[j2].neighbor); + chain_len = chain_len2 = 0; + if ( at[j].sb_parity[0] ) { + for ( m1 = 0; m1 < MAX_NUM_STEREO_BONDS && at[j].sb_parity[m1]; m1 ++ ) { + if ( k == at[j].sb_ord[m1] ) { + chain_len = get_opposite_sb_atom( at, j, k, + &pnxt_atom, &pinxt2cur, &pinxt_sb_parity_ord ); + break; + } + } + } + if ( at[j2].sb_parity[0] ) { + for ( m2 = 0; m2 < MAX_NUM_STEREO_BONDS && at[j2].sb_parity[m2]; m2 ++ ) { + if ( k2 == at[j2].sb_ord[m2] ) { + chain_len2 = get_opposite_sb_atom( at, j2, k2, + &pnxt_atom2, &pinxt2cur2, &pinxt_sb_parity_ord2 ); + break; + } + } + } + if ( chain_len == 1 && chain_len2 == 1 || /* regular stereobond */ + chain_len > 1 && j > pnxt_atom ) { /* j is a cumulene endpoint */ + int m; + pcur_atom = j; /* pcur_atom > pnxt_atom */ + picur2nxt = k; + picur_sb_parity_ord = -1; + for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[pcur_atom].sb_parity[m]; m ++ ) { + if ( at[pcur_atom].sb_ord[m] == k ) { + picur_sb_parity_ord = m; + break; + } + } + chain_len2 = 0; + } else + if ( chain_len2 > 1 && j2 > pnxt_atom2 ) { /* j2 is a cumulene endpoint */ + int m; + pcur_atom = j2; + picur2nxt = k2; + pnxt_atom = pnxt_atom2; + pinxt2cur = pinxt2cur2; + pinxt_sb_parity_ord = pinxt_sb_parity_ord2; + picur_sb_parity_ord = -1; + for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[pcur_atom].sb_parity[m]; m ++ ) { + if ( at[pcur_atom].sb_ord[m] == k2 ) + picur_sb_parity_ord = m; + } + chain_len = chain_len2; + chain_len2 = 0; + } else { + chain_len = chain_len2 = 0; + } + + /*len += sprintf( szCurBonds + len, "%c%d", bond_char, val+1);*/ + if ( chain_len ) { + /* both atoms belong to a stereo bond */ + int kc; + int p1, p2, p1NM, p2NM, neigh, neigh1, neigh2, bHasMetal, bWellDef; + int bNeighSwitched1, bNeighSwitched2; + + p1 = SB_PARITY_1(at[pcur_atom].sb_parity[picur_sb_parity_ord]); + p1NM = SB_PARITY_2(at[pcur_atom].sb_parity[picur_sb_parity_ord]); + p2 = SB_PARITY_1(at[pnxt_atom].sb_parity[pinxt_sb_parity_ord]); + p2NM = SB_PARITY_2(at[pnxt_atom].sb_parity[pinxt_sb_parity_ord]); + + bWellDef = ATOM_PARITY_WELL_DEF(p1) && ATOM_PARITY_WELL_DEF(p2); + bHasMetal = ATOM_PARITY_WELL_DEF(p1NM) && ATOM_PARITY_WELL_DEF(p2NM); + + bNeighSwitched1 = bNeighSwitched2 = 0; + + if ( bWellDef || bHasMetal ) { + + neigh1 = num_inp_atoms; + for ( kc = 0; kc < at[pcur_atom].valence; kc ++ ) { + if ( kc == picur2nxt ) + continue; + neigh = at[pcur_atom].neighbor[kc]; + if ( bHasMetal && is_el_a_metal( at[neigh].el_number ) ) + continue; + if ( neigh < neigh1 ) + neigh1 = neigh; + } + if ( neigh1 < num_inp_atoms ) { + bNeighSwitched1 = (neigh1 != at[pcur_atom].neighbor[(int)at[pcur_atom].sn_ord[picur_sb_parity_ord]]); + } else { + AddErrorMessage(sd->pStrErrStruct, "Cannot find 0D stereobond neighbor"); + /* + sd->nStructReadError = 99; + sd->nErrorType = _IS_ERROR; + */ + } + + neigh2 = num_inp_atoms; + for ( kc = 0; kc < at[pnxt_atom].valence; kc ++ ) { + if ( kc == pinxt2cur ) + continue; + neigh = at[pnxt_atom].neighbor[kc]; + if ( bHasMetal && is_el_a_metal( at[neigh].el_number ) ) + continue; + if ( neigh < neigh2 ) + neigh2 = neigh; + } + if ( neigh2 < num_inp_atoms ) { + bNeighSwitched2 = (neigh2 != at[pnxt_atom].neighbor[(int)at[pnxt_atom].sn_ord[pinxt_sb_parity_ord]]); + } else { + AddErrorMessage(sd->pStrErrStruct, "Cannot find 0D stereobond neighbor"); + /* + sd->nStructReadError = 99; + sd->nErrorType = _IS_ERROR; + */ + } + + if ( neigh1 < num_inp_atoms && neigh2 < num_inp_atoms ) { + if ( ATOM_PARITY_WELL_DEF(p1) && ATOM_PARITY_WELL_DEF(p2) ) { + bond_parity = 2 - (p1 + p2 + bNeighSwitched1 + bNeighSwitched2) % 2; + } else { + bond_parity = inchi_min( p1, p2 ); + } + + if ( bHasMetal ) { + bond_parityNM = 2 - (p1NM + p2NM + bNeighSwitched1 + bNeighSwitched2) % 2; + } else + if ( p1NM && p2NM ) { + bond_parityNM = inchi_min( p1NM, p2NM ); + } + } + } else { + if ( p1 && p2 ) { + bond_parity = inchi_min( p1, p2 ); + } + if ( p1NM && p2NM ) { + bond_parityNM = inchi_min( p1NM, p2NM ); + } + if ( bond_parityNM && !bond_parity ) { + bond_parity = AB_PARITY_UNDF; + } + } + } + len += sprintf( szCurBonds + len, "%c%s%s%d", + + bond_char, + + (bond_parity == AB_PARITY_ODD)? "-" : + (bond_parity == AB_PARITY_EVEN)? "+" : + (bond_parity == AB_PARITY_UNKN)? "u" : + (bond_parity == AB_PARITY_UNDF)? "?" : "", + + (bond_parityNM == AB_PARITY_ODD)? "-" : + (bond_parityNM == AB_PARITY_EVEN)? "+" : + (bond_parityNM == AB_PARITY_UNKN)? "u" : + (bond_parityNM == AB_PARITY_UNDF)? "?" : "", + + j2+1); + } + } + if ( len + cur_len + 2 < buf_len ) { + memcpy( szBuf + cur_len, szCurBonds, len ); + cur_len += len; + szBuf[ cur_len ++ ] = ';'; + j ++; + } else { + break; + } + } + szBuf[cur_len] = '\0'; + *i = num_inp_atoms>0? j : 0; + return cur_len; +} + + +#define ORIG_STR_BUFLEN (7*MAXVAL+2) /* > 7*MAXVAL+2 = 142 */ + + +/* + Fill out original input structure +*/ +int OrigStruct_FillOut( CANON_GLOBALS *pCG, + ORIG_ATOM_DATA *orig_inp_data, + ORIG_STRUCT *pOrigStruct, + STRUCT_DATA *sd ) +{ + char szBuf[ORIG_STR_BUFLEN]; + int i, len, len_coord, len_atoms, len_bonds; + + pOrigStruct->polymer = NULL; + pOrigStruct->v3000 = NULL; + + /* Coordinates */ + len_coord = i = 0; + + if (orig_inp_data->szCoord) { + + while ( len = WriteOrigCoord( orig_inp_data->num_inp_atoms, + orig_inp_data->szCoord, &i, szBuf, sizeof(szBuf) )) { + len_coord += len; + } + pOrigStruct->szCoord = (char*) inchi_malloc( (len_coord + 1)*sizeof(pOrigStruct->szCoord[0]) ); + i = 0; + if ( pOrigStruct->szCoord && + len_coord == WriteOrigCoord( orig_inp_data->num_inp_atoms, + orig_inp_data->szCoord, &i, pOrigStruct->szCoord, len_coord+1 ) && + i == orig_inp_data->num_inp_atoms ) { + /* success */ + if ( orig_inp_data->szCoord ) { + inchi_free( orig_inp_data->szCoord ); + orig_inp_data->szCoord = NULL; + } + } else { + return -1; + } + } + + /* Atoms */ + len_atoms = i = 0; + while ( len = WriteOrigAtoms( pCG, orig_inp_data->num_inp_atoms, + orig_inp_data->at, &i, szBuf, sizeof(szBuf), sd)) + { + len_atoms += len; + if ( !orig_inp_data->num_inp_atoms ) + break; + } + pOrigStruct->szAtoms = (char*) inchi_malloc( (len_atoms + 1)*sizeof(pOrigStruct->szAtoms[0]) ); + i = 0; + if ( pOrigStruct->szAtoms && + len_atoms == WriteOrigAtoms( pCG, orig_inp_data->num_inp_atoms, + orig_inp_data->at, &i, pOrigStruct->szAtoms, len_atoms+1, sd ) && + i == orig_inp_data->num_inp_atoms ) { + ; /* success */ + } else { + return -1; + } + + /* Bonds */ + len_bonds = 0; + i = 1; + while ( len = WriteOrigBonds( pCG, orig_inp_data->num_inp_atoms, + orig_inp_data->at, &i, szBuf, sizeof(szBuf), NULL)) + { + len_bonds += len; + if ( !orig_inp_data->num_inp_atoms ) + break; + } + + pOrigStruct->szBonds = (char*) inchi_malloc( (len_bonds + 2)*sizeof(pOrigStruct->szBonds[0]) ); + i = 1; + + if ( pOrigStruct->szBonds && + len_bonds == WriteOrigBonds( pCG, orig_inp_data->num_inp_atoms, + orig_inp_data->at, &i, pOrigStruct->szBonds, len_bonds+2, sd ) && + i == orig_inp_data->num_inp_atoms ) + { + ; /* success */ + } + else + { + return -1; + } + pOrigStruct->num_atoms = orig_inp_data->num_inp_atoms; + + /* Extensions of v. 1.05 */ + if ( orig_inp_data->polymer !=NULL + && orig_inp_data->polymer->n > 0 + && orig_inp_data->polymer->valid ) + { + pOrigStruct->polymer = orig_inp_data->polymer; + /* pointer copy, do not free after use! */ + } + if ( orig_inp_data->v3000 !=NULL) + { + pOrigStruct->v3000 = orig_inp_data->v3000; + /* pointer copy, do not free after use! */ + } + + return 0; +} + + +/* + FreeOrigStruct( ... ) +*/ +void FreeOrigStruct( ORIG_STRUCT *pOrigStruct) +{ + if ( pOrigStruct ) { + if ( pOrigStruct->szAtoms ) + inchi_free( pOrigStruct->szAtoms ); + if ( pOrigStruct->szBonds ) + inchi_free( pOrigStruct->szBonds ); + if ( pOrigStruct->szCoord ) + inchi_free( pOrigStruct->szCoord ); + /* For + + OrigAtDataPolymer *polymer; + OrigAtDataV3000 *v3000; + + we used shallow (pointer) copy of analogs from orig_inp_data, so do not free these here */ + + /*memset( pOrigStruct, 0, sizeof(*pOrigStruct) );*/ + pOrigStruct->szAtoms = NULL; + pOrigStruct->szBonds = NULL; + pOrigStruct->szCoord = NULL; + } +} + + +/* + GetSaveOptLetters + + Get the two letters encoding the saved InChI creation options. + + The first one encodes RecMet/FixedH/SUU/SLUUD options. + Each of options is a binary switch {ON,OFF}, so it totals to 2*2*2*2=16 values + which are encoded by capital letters �A� through �P�. + + The second character encodes experimental (InChI 1 extension) options KET and 15T. + Each of these options is a binary switch ON/OFF, so there are 2*2=4 combinations, + currently encoded by �A� through �D�. + Note that anything but 'A' here would indicate "extended" InChI 1 Also, there is a + reservation for future needs: the 2nd memo char may accommodate two more ON/OFF + binary options (at 26-base encoding). +*/ +void GetSaveOptLetters(unsigned char save_opt_bits, char* let1, char* let2) +{ +const char a2p[]="ABCDEFGHIJKLMNOP"; + /* SaveOptBits layout: {unused|unused|Ket|15T|RecMet|FixedH|SUU|SLUUD} */ + *let1 = a2p [ (size_t) ( save_opt_bits & 0x0f ) ]; + *let2 = a2p [ (size_t) ( (save_opt_bits & 0x30) >> 4 ) ]; +} + + +/* + Set line separators dependent on requested output mode +*/ +void set_line_separators( int bINChIOutputOptions, char **pLF, char **pTAB ) +{ + int bPlainTextCommnts = 0 != (bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT_COMMENTS); + + *pLF = bPlainTextCommnts? "\n" : "\0"; + +#if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) ) + { + int bPlainText = 0 != (bINChIOutputOptions & (INCHI_OUT_PLAIN_TEXT | INCHI_OUT_PLAIN_TEXT_COMMENTS)); + int bPlainTabbedOutput = 0 != (bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT) && + bPlainText && !bPlainTextCommnts; + + *pTAB = bPlainTabbedOutput? "\t" : "\n"; + } +#else + *pTAB = "\n"; +#endif + + return; +} + + + +/* + Output structure (compound) header +*/ +int Output_RecordInfo( INCHI_IOSTREAM *out_file, int num_input_struct, + int bNoStructLabels, const char *szSdfLabel, + const char *szSdfValue, long lSdfId, + char *pLF, char *pTAB ) +{ + if ( bNoStructLabels ) + return 0; + +#ifdef TARGET_LIB_FOR_WINCHI + /*out_file->s.nUsedLength = 0; + inchi_ios_reset( out_file );*/ +#endif + if ( !(szSdfLabel && szSdfLabel[0]) && !(szSdfValue && szSdfValue[0]) ) + { + inchi_ios_print_nodisplay( out_file, "%sStructure: %d", pLF, num_input_struct ); + inchi_ios_print_nodisplay( out_file, "%s", pTAB ); + } + else + { + inchi_ios_print_nodisplay( out_file, "%sStructure: %d.%s%s%s%s", pLF, num_input_struct, + SDF_LBL_VAL(szSdfLabel, szSdfValue) ); + if ( lSdfId ) + { + (out_file->s.nUsedLength)--; + inchi_ios_print_nodisplay( out_file, ":%ld", lSdfId ); + } + inchi_ios_print_nodisplay( out_file, "%s", pTAB ); + } + + return 0; +} + + +/* + Output InChI: InChI version and kind +*/ +int OutputINCHI_VersionAndKind( INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + int bINChIOutputOptions, + int is_beta, + char *pLF, char *pTAB ) +{ + inchi_ios_print_nodisplay( out_file, "%s%s=%s", pLF, INCHI_NAME, pLF ); + + inchi_strbuf_reset( strbuf ); + inchi_strbuf_printf( strbuf, "%s", x_curr_ver); + + /* - add 'Beta' flag if applicable */ + if ( is_beta ) + inchi_strbuf_printf( strbuf, "B"); + /* - add 'Standard' flag if applicable */ + else if ( bINChIOutputOptions & INCHI_OUT_STDINCHI ) + inchi_strbuf_printf( strbuf, "S"); + + inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF ); + + return 0; +} + + +/* + Output InChI: main layer - formula, connections and hydrogens (incl. tautomeric info == mobile H) +*/ + +int OutputINCHI_MainLayerFormula( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + int num_components2[], + int *INCHI_basic_or_INCHI_reconnected, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ) +{ + + /* constitution ( dot-disconnected Hill formulas: ) */ + + if ( num_components2[0] || num_components2[1] ) + { + szGetTag( IdentLbl, io->nTag, io->bTag1 = *INCHI_basic_or_INCHI_reconnected==INCHI_REC? IL_REC_ : IL_FML_, io->szTag1, &io->bAlways ); + inchi_strbuf_reset( strbuf ); + io->tot_len = str_HillFormula( io->pINChISort, strbuf, &io->bOverflow, io->bOutType, + io->num_components, io->bUseMulipliers); + + if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -1, 1 ) ) + return 1; + inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF ); + } + return 0; +} + + +int OutputINCHI_MainLayerConnections( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + int num_components2[], + int *INCHI_basic_or_INCHI_reconnected, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ) +{ + /* connections ( semicolon/dot-disconnected connection tables ) */ + + szGetTag( IdentLbl, io->nTag, io->bTag1 = IL_CONN, io->szTag1, &io->bAlways ); + inchi_strbuf_reset( strbuf ); + io->tot_len = 0; + io->tot_len2 = str_Connections( pCG, io->pINChISort, strbuf, &io->bOverflow, io->bOutType, + io->ATOM_MODE, io->num_components, io->bUseMulipliers); + + /* current version does not output empty (";;;;") connectivity */ + + if ( io->tot_len != io->tot_len2 ) { /* 2004-06-30: never output empty connection table */ + io->tot_len = io->tot_len2; + if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -2, io->bPlainTextTags ) ) + return 1; /* pStr overfow */ + inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF ); + } + + return 0; +} + + +int OutputINCHI_MainLayerHydrogens( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + int num_components2[], + int *INCHI_basic_or_INCHI_reconnected, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ) +{ + + /* hydrogen atoms (do not output empty) */ + + if ( INCHI_SEGM_FILL == INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_h_H_ATOMS] ) ) + { + szGetTag( IdentLbl, io->nTag, io->bTag1 = IL_ALLH, io->szTag1, &io->bAlways ); + inchi_strbuf_reset( strbuf ); + io->tot_len = 0; + io->tot_len2 = str_H_atoms( io->pINChISort, strbuf, &io->bOverflow, io->bOutType, + io->ATOM_MODE, io->TAUT_MODE, + io->num_components, io->bUseMulipliers); + if ( io->tot_len != io->tot_len2 ) { /* 2004-06-21: never output empty */ + io->tot_len = io->tot_len2; + if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -2, 1 ) ) + return 1; + inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF ); + } + } + + return 0; +} + + +/* + Output InChI: charge and removed protons layers +*/ +int OutputINCHI_ChargeAndRemovedAddedProtonsLayers( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, INCHI_IOSTREAM_STRING *strbuf, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ) +{ + + /* charge */ + + io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_q_CHARGE] ); + if ( io->nSegmAction ) + { + szGetTag( IdentLbl, io->nTag, io->bTag1 = IL_CHRG | io->bFhTag, io->szTag1, &io->bAlways ); + inchi_strbuf_reset( strbuf ); + io->tot_len = 0; + if ( INCHI_SEGM_FILL == io->nSegmAction ) + { + io->tot_len = str_Charge2( io->pINChISort, io->pINChISort2, + strbuf, &io->bOverflow, io->bOutType, io->num_components, + io->bSecondNonTautPass, io->bOmitRepetitions, io->bUseMulipliers); + io->bNonTautNonIsoIdentifierNotEmpty += io->bSecondNonTautPass; + } + if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags ) ) + return 1; + inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF ); + } + + /* removed protons */ + + if ( io->iCurTautMode == TAUT_YES && !io->bSecondNonTautPass ) + { + + io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_p_PROTONS] ); + if ( io->nSegmAction ) + { + szGetTag( IdentLbl, io->nTag, io->bTag1 = IL_PROT | io->bFhTag, io->szTag1, &io->bAlways ); + inchi_strbuf_reset( strbuf ); + io->tot_len = 0; + inchi_strbuf_printf( strbuf, "%+d", io->nNumRemovedProtons ); + if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags ) ) + return 1; + inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF ); + } + else + { + if ( io->bPlainTextTags == 1 ) inchi_ios_print_nodisplay( out_file, "/" ); + } + } + + return 0; +} + + +/* + Output InChI: stereo layer with sublayers +*/ +int OutputINCHI_StereoLayer( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, INCHI_IOSTREAM_STRING *strbuf, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ) +{ + + { + int i; + i = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_t_SATOMS] ); + i = i; + } + + if ( INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_b_SBONDS] ) || + INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_t_SATOMS] ) || + INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_m_SP3INV] ) || + INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_s_STYPE] ) ) + { + + /* stereo */ + + szGetTag( IdentLbl, io->nTag, io->bTag1 = IL_STER | io->bFhTag, io->szTag1, &io->bAlways ); + + /* sp2 */ + + /*if ( bStereoSp2[io->iCurTautMode] )*/ + if ( io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_b_SBONDS] ) ) + { + szGetTag( IdentLbl, io->nTag, io->bTag2 = io->bTag1 | IL_DBND, io->szTag2, &io->bAlways ); + inchi_strbuf_reset( strbuf ); + io->tot_len = 0; + if ( INCHI_SEGM_FILL == io->nSegmAction ) + { + io->tot_len = str_Sp2( io->pINChISort, + io->pINChISort2, + strbuf, + &io->bOverflow, + io->bOutType, + io->TAUT_MODE, + io->num_components, + io->bSecondNonTautPass, + io->bOmitRepetitions, + io->bUseMulipliers); + io->bNonTautNonIsoIdentifierNotEmpty += io->bSecondNonTautPass; + } + if ( str_LineEnd( io->szTag2, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags ) ) + return 1; + inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF ); + } + else + { + if ( io->bPlainTextTags == 1 ) inchi_ios_print_nodisplay( out_file, "/" ); /* sp2 */ + } + + /* sp3 */ + + /*if ( bStereoSp3[io->iCurTautMode] )*/ + if ( io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_t_SATOMS] ) ) + { + io->bRelRac = io->bRelativeStereo[io->iCurTautMode] || io->bRacemicStereo[io->iCurTautMode]; + szGetTag( IdentLbl, io->nTag, io->bTag2 = io->bTag1 | IL_SP3S, io->szTag2, &io->bAlways ); + inchi_strbuf_reset( strbuf ); + io->tot_len = 0; + if ( INCHI_SEGM_FILL == io->nSegmAction ) + { + io->tot_len = str_Sp3( io->pINChISort, io->pINChISort2, + strbuf, &io->bOverflow, io->bOutType, + io->TAUT_MODE, io->num_components, io->bRelRac, + io->bSecondNonTautPass, io->bOmitRepetitions, io->bUseMulipliers); + io->bNonTautNonIsoIdentifierNotEmpty += io->bSecondNonTautPass; + } + + if (str_LineEnd( io->szTag2, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags )) + return 2; + inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF ); + } + else + { + if ( io->bPlainTextTags == 1 ) inchi_ios_print_nodisplay( out_file, "/" ); /* sp3 */ + } + + /* bStereoAbsInverted[io->iCurTautMode] */ + + /* if ( bStereoAbs[io->iCurTautMode] ) */ + if ( io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_m_SP3INV] ) ) + { + szGetTag( IdentLbl, io->nTag, io->bTag2 = io->bTag1 | IL_INVS, io->szTag2, &io->bAlways ); + inchi_strbuf_reset( strbuf ); io->tot_len = 0; + if ( INCHI_SEGM_FILL == io->nSegmAction ) { + io->tot_len = str_StereoAbsInv( io->pINChISort, strbuf, + &io->bOverflow, io->bOutType, io->num_components); + io->bNonTautNonIsoIdentifierNotEmpty += io->bSecondNonTautPass; + } + + if (str_LineEnd( io->szTag2, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags )) + return 3; + inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF ); + } + else + { + if ( io->bPlainTextTags == 1 ) + inchi_ios_print_nodisplay( out_file, "/" ); /* stereo-abs-inv */ + } + + /* stereo type */ + + /*if ( io->bRacemicStereo[io->iCurTautMode] || io->bRelativeStereo[io->iCurTautMode] || bStereoAbs[io->iCurTautMode] )*/ + if ( io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_s_STYPE] ) ) + { + const char *p_stereo = io->bRelativeStereo[io->iCurTautMode]? x_rel : + io->bRacemicStereo[io->iCurTautMode] ? x_rac : x_abs; + szGetTag( IdentLbl, io->nTag, io->bTag2 = io->bTag1 | IL_TYPS, io->szTag2, &io->bAlways ); + inchi_strbuf_reset( strbuf ); io->tot_len = 0; + if ( INCHI_SEGM_FILL == io->nSegmAction ) { + (io->tot_len) += MakeDelim( p_stereo, strbuf, &io->bOverflow); + io->bNonTautNonIsoIdentifierNotEmpty += io->bSecondNonTautPass; + } + if (str_LineEnd( io->szTag2, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags )) + return 1; + inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF ); + } + if ( io->bPlainTextTags == 1 ) inchi_ios_print_nodisplay( out_file, "/" ); /* no abs, inv or racemic stereo */ + } + else + { + if ( io->bPlainTextTags == 1 ) inchi_ios_print_nodisplay( out_file, "////" ); /* sp3, sp2, abs-inv, stereo.type */ + } + + return 0; +} + + +/* + Output InChI: isotopic layer and sublayers +*/ +int OutputINCHI_IsotopicLayer( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + int *INCHI_basic_or_INCHI_reconnected, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ) +{ + + if ( INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_i_IATOMS] ) ) + { + /* isotopic #1: composition -- atoms -- do not output in xml if empty */ + szGetTag( IdentLbl, io->nTag, io->bTag1 = IL_ISOT | io->bFhTag, io->szTag1, &io->bAlways ); + /* isotopic atoms without mobile H. + * Fixed 2004-06-15: always output if not bXml. Note: + * Previous condition if( bHasIsotopicAtoms[io->iCurTautMode] || bIsotopic && !bXml) + * did not optput /i in case of only mobile isotopic H + */ + if ( io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_i_IATOMS] ) ) + { + szGetTag( IdentLbl, io->nTag, io->bTag2 = io->bTag1 | IL_ATMS, io->szTag2, &io->bAlways ); + inchi_strbuf_reset( strbuf ); + io->tot_len = 0; + /*if ( bHasIsotopicAtoms[io->iCurTautMode] )*/ + if ( INCHI_SEGM_FILL == io->nSegmAction ) + { + io->tot_len2 = str_IsoAtoms( io->pINChISort, io->pINChISort2, + strbuf, &io->bOverflow, io->bOutType, io->TAUT_MODE, + io->num_components, io->bAbcNumbers, + io->bSecondNonTautPass, io->bOmitRepetitions, + io->bUseMulipliers); + io->bNonTautIsoIdentifierNotEmpty += io->bSecondNonTautPass; + } + else + { + io->tot_len2 = io->tot_len; + } + + io->tot_len = io->tot_len2; + if ( str_LineEnd( io->szTag2, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags ) ) + return 1; + inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF ); + } + + /* isotopic #1a: composition -- exchangeable isotopic H (mobile H only) */ + /*if ( !io->bSecondNonTautPass && bHasIsoH )*/ + if ( io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_h_H_ATOMS] ) ) + { + szGetTag( IdentLbl, io->nTag, io->bTag2 = io->bTag1 | IL_XCGA, io->szTag2, &io->bAlways ); + inchi_strbuf_reset( strbuf ); + io->tot_len = 0; + (io->tot_len) += MakeIsoHString( io->num_iso_H, strbuf, io->TAUT_MODE, &io->bOverflow); + io->bNonTautIsoIdentifierNotEmpty += io->bSecondNonTautPass; + if ( str_LineEnd( io->szTag2, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags ) ) + return 2; + inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF ); + } + + /*************************************************** + * + * Isotopic stereo + * + ***************************************************/ + + /*if ( bIsotopicStereo[io->iCurTautMode] )*/ + if ( INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_b_SBONDS] ) || + INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_t_SATOMS] ) || + INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_m_SP3INV] ) || + INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_s_STYPE] ) ) + { + /* stereo */ + szGetTag( IdentLbl, io->nTag, io->bTag2 = io->bTag1 | IL_STER, io->szTag2, &io->bAlways ); + + /************************ + isotopic #2: sp2 + ************************/ + /*if ( bIsotopicStereoSp2[io->iCurTautMode] )*/ + if ( io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_b_SBONDS] ) ) + { + szGetTag( IdentLbl, io->nTag, io->bTag3 = io->bTag2 | IL_DBND, io->szTag3, &io->bAlways ); + inchi_strbuf_reset( strbuf ); + io->tot_len = 0; + if ( INCHI_SEGM_FILL == io->nSegmAction ) + { + io->tot_len = str_IsoSp2( io->pINChISort, io->pINChISort2, + strbuf, &io->bOverflow, io->bOutType, io->TAUT_MODE, io->num_components, + io->bSecondNonTautPass, io->bOmitRepetitions, io->bUseMulipliers); + io->bNonTautIsoIdentifierNotEmpty += io->bSecondNonTautPass; + } + if ( str_LineEnd( io->szTag3, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags ) ) + return 3; + inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF ); + } + else + { + if ( io->bPlainTextTags == 1 ) inchi_ios_print_nodisplay( out_file, "/" ); /* iso sp2 */ + } + + /************************ + isotopic #3: sp3 + ************************/ + /*if ( bIsotopicStereoSp3[io->iCurTautMode] )*/ + if ( io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_t_SATOMS] ) ) + { + io->bRelRac = io->bIsotopicRelativeStereo[io->iCurTautMode] || io->bIsotopicRacemicStereo[io->iCurTautMode]; + + szGetTag( IdentLbl, io->nTag, io->bTag3 = io->bTag2 | IL_SP3S, io->szTag3, &io->bAlways ); + inchi_strbuf_reset( strbuf ); + io->tot_len = 0; + if ( INCHI_SEGM_FILL == io->nSegmAction ) + { + io->tot_len = str_IsoSp3( io->pINChISort, io->pINChISort2, + strbuf, &io->bOverflow, io->bOutType, + io->TAUT_MODE, io->num_components, io->bRelRac, + io->bSecondNonTautPass, io->bOmitRepetitions, + io->bUseMulipliers); + io->bNonTautIsoIdentifierNotEmpty += io->bSecondNonTautPass; + } + if ( str_LineEnd( io->szTag3, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags ) ) + return 5; + inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF ); + } else + { + if ( io->bPlainTextTags == 1 ) + inchi_ios_print_nodisplay( out_file, "/" ); /* iso-sp3 */ + } + + /* isotopic #4: abs inverted */ + if ( io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_m_SP3INV] ) ) + { + szGetTag( IdentLbl, io->nTag, io->bTag3 = io->bTag2 | IL_INVS, io->szTag3, &io->bAlways ); + inchi_strbuf_reset( strbuf ); + io->tot_len = 0; + if ( INCHI_SEGM_FILL == io->nSegmAction ) + { + io->tot_len = str_IsoStereoAbsInv( io->pINChISort, strbuf, + &io->bOverflow, io->bOutType, io->num_components); + io->bNonTautIsoIdentifierNotEmpty += io->bSecondNonTautPass; + } + if ( str_LineEnd( io->szTag3, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags ) ) + return 5; + inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF ); + } + else + { + if ( io->bPlainTextTags == 1 ) + inchi_ios_print_nodisplay( out_file, "/" ); + } + + /* isotopic #5: stereo type. Do not output if it has already been output in non-iso */ + if ( io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_s_STYPE] ) ) + { + const char *p_stereo = io->bIsotopicRelativeStereo[io->iCurTautMode]? x_rel : + io->bIsotopicRacemicStereo[io->iCurTautMode] ? x_rac : x_abs; + szGetTag( IdentLbl, io->nTag, io->bTag3 = io->bTag2 | IL_TYPS, io->szTag3, &io->bAlways ); + inchi_strbuf_reset( strbuf ); + io->tot_len = 0; + if ( INCHI_SEGM_FILL == io->nSegmAction ) + { + io->tot_len += MakeDelim( p_stereo, strbuf, &io->bOverflow); + io->bNonTautIsoIdentifierNotEmpty += io->bSecondNonTautPass; + } + if ( str_LineEnd( io->szTag3, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags ) ) + return 6; + inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF ); + } + if ( io->bPlainTextTags == 1 ) + inchi_ios_print_nodisplay( out_file, "/" ); /* no abs, inv or racemic stereo */ + } + else + { + /* no isotopic stereo */ + if ( io->bPlainTextTags == 1 ) + inchi_ios_print_nodisplay( out_file, "////" ); /* sp3, sp2, abs-inv, stereo.type */ + } + } + else + { + if ( io->bPlainTextTags == 1 ) + inchi_ios_print_nodisplay( out_file, "///" ); /* isotopic composition, sp2, sp3 */ + if ( io->bPlainTextTags == 1 ) + inchi_ios_print_nodisplay( out_file, "//" ); /* inv or racemic stereo */ + } + +#if ( CANON_FIXH_TRANS == 1 ) + if ( io->bOutType == OUT_NONTAUT && io->bOutputType == OUT_TN && io->bSecondNonTautPass && + INCHI_SEGM_FILL == INChI_SegmentAction( io->sDifSegs[DIFL_F][DIFS_o_TRANSP] )) + { + /* find and print non-tautomeric components transposition, if non-trivial */ + AT_NUMB *nTrans_n, *nTrans_s; + + if ( 0 < bin_AuxTautTrans(io->pINChISort, io->pINChISort2, &nTrans_n, &nTrans_s, io->bOutType, io->num_components) ) + { + /* a non-trivial transposition does exist; output start tag */ + szGetTag( IdentLbl, io->nTag, io->bTag1 = IL_TRNS | io->bFhTag, io->szTag1, &io->bAlways ); + inchi_strbuf_reset( strbuf ); + io->tot_len = 0; + /* print the transposition, cycle after cycle */ + io->tot_len = str_AuxTautTrans( pCG, nTrans_n, nTrans_s, + strbuf, &io->bOverflow, + io->TAUT_MODE, io->num_components); + io->bNonTautIsoIdentifierNotEmpty += io->bSecondNonTautPass; + if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -1, io->bPlainTextTags ) ) + return 7; + inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF ); + /* detected transposition */ + (*io->pSortPrintINChIFlags) |= + ( *INCHI_basic_or_INCHI_reconnected == INCHI_BAS )? FLAG_SORT_PRINT_TRANSPOS_BAS : + FLAG_SORT_PRINT_TRANSPOS_REC; + } + else + { + if ( io->bPlainTextTags == 1 ) + inchi_ios_print_nodisplay( out_file, "/" ); + } + } +#endif + + return 0; +} + + +/* + Output InChI: FixedH layer and related sublayers +*/ +int OutputINCHI_FixedHLayerWithSublayers( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + int *INCHI_basic_or_INCHI_reconnected, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB, + int *then_goto_repeat ) +{ + + *then_goto_repeat = 0; + + if ( io->bOutType == OUT_TN && + !(io->bSecondNonTautPass) && + io->bNonTautIsIdenticalToTaut && + io->bTautomeric && + io->bNonTautomeric ) + { + /* Fixed-H layer is empty in the Identifier */ + (*io->pSortPrintINChIFlags) |= + (*INCHI_basic_or_INCHI_reconnected==INCHI_BAS)? FLAG_SORT_PRINT_NO_NFIX_H_BAS : + FLAG_SORT_PRINT_NO_NFIX_H_REC; + (*io->pSortPrintINChIFlags) |= + (*INCHI_basic_or_INCHI_reconnected==INCHI_BAS)? FLAG_SORT_PRINT_NO_IFIX_H_BAS : + FLAG_SORT_PRINT_NO_IFIX_H_REC; + } + + if ( io->bOutType == OUT_TN && + !io->bNonTautIsIdenticalToTaut && /* added 2004-10-04 Fix16 */ +#ifdef OLD_ITEM_DISCOVERY + io->bTautomeric && + io->bNonTautomeric && +#endif + INChI_SegmentAction( io->sDifSegs[DIFL_F][DIFS_f_FORMULA] ) + /* special case: removed isolated H(+): */ + /* || io->iCurTautMode == TAUT_YES && num_comp[TAUT_YES] < num_comp[TAUT_NON] && + 0 < num_comp[TAUT_NON]*/ + ) + + { + /* add the second (non-tautomeric) output */ + io->bOutType = OUT_NONTAUT; /* pick up only non-tautomeric representation of tautomeric */ + io->iCurTautMode = TAUT_NON; + io->pINChISort = io->pINChISortTautAndNonTaut[TAUT_NON]; + io->bSecondNonTautPass = 1; + io->nCurINChISegment = DIFL_F; + io->num_components = io->num_comp[io->iCurTautMode]; /* number of components could change due to removal of isolated H(+) from tautomeric */ + io->bFhTag = IL_FIXH; + szGetTag( IdentLbl, io->nTag, io->bTag1 = io->bFhTag, io->szTag1, &io->bAlways ); + /***** constitution non-taut: dot-disconnected Hill formulas: -- only if different */ + szGetTag( IdentLbl, io->nTag, io->bTag1 = IL_FMLF | io->bFhTag, io->szTag1, &io->bAlways ); + inchi_strbuf_reset( strbuf ); io->tot_len = 0; + io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_f_FORMULA] ); + if ( INCHI_SEGM_FILL == io->nSegmAction ) + { + io->tot_len2 = str_HillFormula2( io->pINChISort, io->pINChISort2, + strbuf, &io->bOverflow, io->bOutType, + io->num_components, io->bUseMulipliers); + io->bNonTautNonIsoIdentifierNotEmpty += io->bSecondNonTautPass; + } + else + { + io->tot_len2 = io->tot_len; + } + io->tot_len = io->tot_len2; + if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags ) ) + return 1; + inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF ); + + io->nSegmAction = INChI_SegmentAction( io->sDifSegs[io->nCurINChISegment][DIFS_h_H_ATOMS] ); + + if ( INCHI_SEGM_FILL == io->nSegmAction ) + { + szGetTag( IdentLbl, io->nTag, io->bTag1 = IL_HFIX | io->bFhTag, io->szTag1, &io->bAlways ); + inchi_strbuf_reset( strbuf ); io->tot_len = 0; /* open H-fixed */ + /* output the second non-tautomeric item: fixed H -- do not output in xml if empty */ + io->tot_len2 = str_FixedH_atoms( io->pINChISort, strbuf, + &io->bOverflow, io->bOutType, io->ATOM_MODE, + io->num_components, io->bUseMulipliers); + io->tot_len = io->tot_len2; + if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -io->nSegmAction, io->bPlainTextTags ) ) + return 2; + inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF ); + io->bNonTautNonIsoIdentifierNotEmpty += io->bSecondNonTautPass; + } + *then_goto_repeat = 1; + return 0; + } + else + { + if ( io->bOutType == OUT_NONTAUT && io->bOutputType == OUT_TN && io->bSecondNonTautPass /* && io->bTautomeric && io->bNonTautomeric*/ ) + { + /* the second (non-taut) output has been done; restore variables */ + io->bOutType = OUT_TN; + io->iCurTautMode = TAUT_YES; + io->pINChISort = io->pINChISortTautAndNonTaut[TAUT_YES]; + io->bSecondNonTautPass = 0; + io->num_components = io->num_comp[io->iCurTautMode]; + if ( !io->bNonTautNonIsoIdentifierNotEmpty ) + { + /* Fixed-H layer is empty in the Identifier */ + (*io->pSortPrintINChIFlags) |= (*INCHI_basic_or_INCHI_reconnected==INCHI_BAS)? FLAG_SORT_PRINT_NO_NFIX_H_BAS : + FLAG_SORT_PRINT_NO_NFIX_H_REC; + } + if ( !io->bNonTautIsoIdentifierNotEmpty ) + { + /* Fixed-H layer is empty in the Identifier */ + (*io->pSortPrintINChIFlags) |= (*INCHI_basic_or_INCHI_reconnected==INCHI_BAS)? FLAG_SORT_PRINT_NO_IFIX_H_BAS : + FLAG_SORT_PRINT_NO_IFIX_H_REC; + } + io->bFhTag = 0; + } + } + + return 0; +} + + +/* + Output InChI: polymer layer +*/ +static int OutputINCHI_PolymerLayer( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + int *INCHI_basic_or_INCHI_reconnected, + ORIG_STRUCT *pOrigStruct, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ) +{ + int i, j, b, k, err = 0; + int tmp, a1=0, a2=0, a3=0, a4=0, n_used_stars=0, curr_star_num; + int nunits2 = 0; + int *cano_nums = NULL, *compnt_nums = NULL, *unum = NULL, *old_stars=NULL; + OrigAtDataPolymerUnit *u=NULL; + OrigAtDataPolymerUnit **units2=NULL; + OrigAtDataPolymer *p = NULL; + + int is_inchi2inchi = 0; + + if ( pOrigStruct && pOrigStruct->polymer ) + p = pOrigStruct->polymer; + + if ( NULL== p ) + return 0; + + is_inchi2inchi = !pOrigStruct->szAtoms && !pOrigStruct->szBonds && !pOrigStruct->szCoord ; + if ( is_inchi2inchi ) + { + err = NOT_YET_I2I_FOR_POLYMERS; + goto exitf; + } + + + OrigAtDataPolymer_DebugTrace (p ); + + /* Get canonical numbers and numbers-of-components for each original atom */ + cano_nums = (int *) inchi_calloc( pOrigStruct->num_atoms + 1, sizeof(int) ); + if ( NULL == cano_nums ) + { + err = 1; + goto exitf; + } + compnt_nums = (int *) inchi_calloc( pOrigStruct->num_atoms + 1, sizeof(int) ); + if ( NULL == compnt_nums ) + { + err = 2; + goto exitf; + } + err = get_canonical_atom_numbers_and_component_numbers( pCG, strbuf, io, + pOrigStruct->num_atoms, + cano_nums, compnt_nums ); + if ( err != 0 ) + { + err = 3; + goto exitf; + } + + + /* Make a working copy of polymer units data */ + + units2 = ( OrigAtDataPolymerUnit ** ) inchi_calloc( p->n, sizeof(OrigAtDataPolymerUnit*) ); + /* units2 is a copy of original polymer units (p->units) with atomic numbers + changed to curr canonical ones; atoms in alists sorted; atoms in blists + and blists themselves sorted */ + if ( NULL==units2 ) + { + err = 3; + goto exitf; + } + memset( units2, 0, sizeof( *units2 ) ); + + old_stars = (int *) inchi_calloc( pOrigStruct->polymer->n_star_atoms, sizeof(int) ); + if ( NULL == old_stars) + { + err = 3; + goto exitf; + } + for (i=0; ipolymer->n_star_atoms; i++) + old_stars[i] = pOrigStruct->polymer->star_atoms[i]; + + + for (i=0; in; i++) + { + units2[i] = OrigAtDataPolymerUnit_CreateCopy( p->units[i] ); + if ( NULL == units2[i] ) + { + err = 4; + goto exitf; + } + nunits2 = i + 1; + } + + unum = (int *) inchi_calloc( p->n, sizeof(int) ); + /* unum contains numbers of units (0..p->n) as they go + when sorted by alist's in lexicographic orders */ + if ( NULL == unum ) + { + err = 4; + goto exitf; + } + + + err = OrigAtDataPolymer_PrepareWorkingSet( p, cano_nums, compnt_nums, units2, unum ); + + if ( err != 0 ) + { + err = 5; + goto exitf; + } + + + /* Prepare polymer substring */ + + /* Mark layer beginning */ + inchi_strbuf_printf( strbuf, "%s", "/z"); + + /* Print polymer units data */ + n_used_stars = 0; + for (i=0; in; i++) + { + /* For each unit u ... */ + u = units2[ unum[i] ]; + + /* print kinds of unit */ + inchi_strbuf_printf( strbuf, "%-d%-d%-d-", u->type, u->subtype, u->conn ); + + /* print unit atoms */ + print_sequence_of_nums_compressing_ranges( u->na, u->alist, strbuf ); + + /* Print unit-to-outside bonds or just outside-star-atoms (if phase-shiftable bonding) */ + if ( u->nb > 2 ) + { + /* not supported yet, too many bonds in SBL */ + err = 12; + goto exitf; + } + /* Print bonds from unit to otside */ + if ( u->nb == 2 && ( !u->closeable || !u->already_closed ) ) + { + a1 = u->blist[0]; a2 = u->blist[1]; a3 = u->blist[2]; a4 = u->blist[3]; + if ( is_in_the_ilist( u->alist, a1, u->na ) ) { tmp = a2; a2 = a1; a1 = tmp; } + if ( is_in_the_ilist( u->alist, a3, u->na ) ) { tmp = a4; a4 = a3; a3 = tmp; } + if ( a1 < a3 ) + inchi_strbuf_printf( strbuf, "(%-d-%-d,%-d-%-d)", a1, a2, a3, a4 ); + else + inchi_strbuf_printf( strbuf, "(%-d-%-d,%-d-%-d)", a3, a4, a1, a2 ); + } + else if ( u->nb <= 2 && ( u->closeable || u->npsbonds > 0 ) ) + { + /* indicate phase shifted */ + + /* Get actual star atoms numbers from all-units pool + NB: ordered according to already established in units2/unum */ + if ( u->star1 > 0 || u->star2 > 0 ) + { + int n_expl_H = 0, pos = 0; + char *sza = pOrigStruct->szAtoms; + while ( sza[pos] ) + { + if ( sza[pos]=='H' ) + { + if ( isupper(UCINT sza[pos+1]) || !sza[pos+1] ) /* if ( next_c is Uppercase or NUL ) */ + n_expl_H++; + if ( !sza[pos+1] ) + break; + } + pos++; + } + + if ( u->star1 > 0 ) + { + curr_star_num = pOrigStruct->num_atoms - n_expl_H - p->n_star_atoms + n_used_stars + 1; + if ( curr_star_num > pOrigStruct->num_atoms ) + { err = 11; goto exitf; } + a1 = curr_star_num; + n_used_stars++; + } + if ( u->star2 > 0 ) + { + curr_star_num = pOrigStruct->num_atoms - n_expl_H - p->n_star_atoms + n_used_stars + 1; + if ( curr_star_num > pOrigStruct->num_atoms ) + { err = 11; goto exitf; } + a2 = curr_star_num; + n_used_stars++; + } + } + + /* if ( a1 > a2 ) { tmp = a2; a2 = a1; a1 = tmp; } */ + inchi_strbuf_printf( strbuf, "(%-d,%-d-", a1,a2 ); + + if ( u->closeable == CLOSING_SRU_DIRADICAL ) + { + inchi_strbuf_printf( strbuf, "%-d)", u->star_partner1 ); + } + else if ( u->closeable == CLOSING_SRU_HIGHER_ORDER_BOND ) + { + /*a3 = u->psbonds[0][0]; a4 = u->psbonds[0][1]; */ + a3 = u->star_partner1; + a4 = u->star_partner2; + if ( a3 > a4 ) + { tmp = a4; a4 = a3; a3 = tmp; } + inchi_strbuf_printf( strbuf, "%-d.%-d)", a3, a4 ); + } + else if ( u->closeable == CLOSING_SRU_RING ) + { + if ( u->npsbonds == 0 ) + { + /* last resort */ + a3 = u->star_partner1; + a4 = u->star_partner2; + if ( a3 > a4 ) + { tmp = a4; a4 = a3; a3 = tmp; } + inchi_strbuf_printf( strbuf, "%-d,%-d)", a3, a4 ); + } + else + { + /* Sort all psbonds in min-at-number order */ + for (b=1; bnpsbonds; b++) + { + int *tmp = u->psbonds[b]; + j = b - 1; + while ( j >= 0 && psbond_min_num_compare( u->psbonds[j], tmp ) > 0 ) + { + u->psbonds[j+1] = u->psbonds[j]; + j--; + } + u->psbonds[j+1] = tmp; + } + for (k=0; knpsbonds;k++) + { + a3 = u->psbonds[k][0]; a4 = u->psbonds[k][1]; + /*if ( a3 > a4 ) { tmp = a4; a4 = a3; a3 = tmp; }*/ + inchi_strbuf_printf( strbuf, "%-d,%-d%-c", a3, a4, k==u->npsbonds-1?')':',' ); + } + } + } + } + + if ( i < p->n-1 ) inchi_strbuf_printf( strbuf, ";" ); + } + + + + + inchi_ios_print_nodisplay( out_file, "%s%s", strbuf->pStr, pLF ); + +exitf: + if ( NULL != cano_nums ) inchi_free( cano_nums ); + if ( NULL != compnt_nums ) inchi_free( compnt_nums ); + if ( unum ) inchi_free( unum ); + if ( NULL != units2 ) + { + for (i=0; i < nunits2; i++) + OrigAtDataPolymerUnit_Free( units2[i] ); + inchi_free( units2 ); + } + if (old_stars ) + { + for (i=0; ipolymer->n_star_atoms; i++) + pOrigStruct->polymer->star_atoms[i] = old_stars[i]; + inchi_free(old_stars ); + } + + return err; +} + + +/* + Output AuxInfo: header and normalization type +*/ +int OutputAUXINFO_HeaderAndNormalization_type( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + int bINChIOutputOptions, + int *INCHI_basic_or_INCHI_reconnected, + int num_components2[], + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ) +{ + /* AuxInfo header */ + if ( *INCHI_basic_or_INCHI_reconnected==INCHI_BAS ) + { + inchi_strbuf_printf( strbuf, "AuxInfo=" ); /* in wINChI window, separate INChI: from AuxInfo: with blank line */ + inchi_ios_print( out_file, "%s%s%s", + /* blank line before AuxInfo in winchi window unless it is an annotation */ + (bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW) ? "\n":"", + strbuf->pStr, pLF); + szGetTag( AuxLbl, io->nTag, io->bTag1 = AL_VERS, io->szTag1, &io->bAlways ); + inchi_strbuf_reset( strbuf ); io->tot_len = 0; + inchi_strbuf_printf( strbuf, "%s", x_curr_ver); + /* avoid leading slash in plain output */ + if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -1, io->bPlainTextTags ) ) + return 1; + inchi_ios_print( out_file, "%s%s", strbuf->pStr, pLF ); + } + else + { + if ( *INCHI_basic_or_INCHI_reconnected == INCHI_REC ) + { + szGetTag( AuxLbl, io->nTag, io->bTag1 = AL_REC_, io->szTag1, &io->bAlways ); + inchi_ios_print( out_file, "%s%s", io->szTag1, pLF ); + } + } + + /* AuxInfo normalization type */ + if ( num_components2[0] || num_components2[1] ) + { + szGetTag( AuxLbl, io->nTag, io->bTag1 = AL_NORM, io->szTag1, &io->bAlways ); + inchi_strbuf_reset( strbuf ); io->tot_len = 0; + inchi_strbuf_printf( strbuf, "%d", (io->bTautomeric && io->bTautomericOutputAllowed)? io->bTautomeric : 0); + if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -1, io->bPlainTextTags ) ) + return 1; + inchi_ios_print( out_file, "%s%s", strbuf->pStr, pLF ); + } + + return 0; +} + + +/* + Output AuxInfo: original atom numbers and symmetry numbers (constit. equivalence /E: ) +*/ +int OutputAUXINFO_OriginalNumbersAndEquivalenceClasses( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + int num_components2[], + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ) +{ + /* Original atom numbers in order of canonical numbers */ + if ( num_components2[0] || num_components2[1] ) + { + szGetTag( AuxLbl, io->nTag, + io->bTag1 = (io->bSecondNonTautPass? AL_FIXN : AL_ANBR) | io->bFhTag, io->szTag1, &io->bAlways ); + inchi_strbuf_reset( strbuf ); + io->tot_len = 0; + /* Original numbering output */ + io->tot_len = str_AuxNumb( pCG, io->pINChISort, io->pINChISort2, + strbuf, &io->bOverflow, + io->bOutType, io->TAUT_MODE, io->num_components, + io->bSecondNonTautPass, io->bOmitRepetitions); + + if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -1, io->bPlainTextTags ) ) + return 1; + inchi_ios_print( out_file, "%s%s", strbuf->pStr, pLF ); + } + + /* + Symmetry numbers (constit. equivalence) /E: + */ + if ( io->bAtomEqu[io->iCurTautMode] ) + { + /* aux equ atoms */ + /* 1. Compare to tautomeric equivalence (in case of second, non-taut, pass only) */ + /* 2. Compare to the previous component if (1) failed to find equivalence */ + szGetTag( AuxLbl, io->nTag, io->bTag1 = AL_AEQU | io->bFhTag, io->szTag1, &io->bAlways ); + inchi_strbuf_reset( strbuf ); + io->tot_len = 0; + io->tot_len = str_AuxEqu( io->pINChISort, io->pINChISort2, + strbuf, &io->bOverflow, io->bOutType, io->TAUT_MODE, + io->num_components, io->bSecondNonTautPass, + io->bOmitRepetitions, io->bUseMulipliers); + + if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -1, io->bPlainTextTags ) ) + return 1; + inchi_ios_print( out_file, "%s%s", strbuf->pStr, pLF ); + } + else + { + if ( io->bPlainTextTags == 1 ) + inchi_ios_print( out_file, "/" ); + } + + return 0; +} + + +/* + Output AuxInfo: tautomeric groups equivalence +*/ +int OutputAUXINFO_TautomericGroupsEquivalence( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ) +{ + if ( io->bTautomericOutputAllowed && io->bTautomeric && io->bTautEqu[io->iCurTautMode] && !io->bSecondNonTautPass ) + { + /*-- Tautomeric groups constitutional equivalence */ + + /*-- aux tgroup equ */ + szGetTag( AuxLbl, io->nTag, io->bTag1 = AL_GEQU | io->bFhTag, io->szTag1, &io->bAlways ); + inchi_strbuf_reset( strbuf ); io->tot_len = 0; + io->tot_len = str_AuxTgroupEqu( io->pINChISort, + strbuf, &io->bOverflow, io->bOutType, io->TAUT_MODE, + io->num_components, io->bUseMulipliers); + if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -1, io->bPlainTextTags ) ) + return 1; + inchi_ios_print( out_file, "%s", strbuf->pStr ); + } + else + { + if ( io->bTautomericOutputAllowed && io->bTautomeric ) + { + if ( io->bPlainTextTags == 1 ) + inchi_ios_print( out_file, "/" ); + } + } + + return 0; +} + + +/* + Output AuxInfo: stereo info +*/ +int OutputAUXINFO_Stereo( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ) +{ + /*-- Inverted stereo -- sp3 only + canonical numbering + */ + if ( io->bInvStereo[io->iCurTautMode] ) + { + szGetTag( AuxLbl, io->nTag, io->bTag1 = AL_STER | io->bFhTag, io->szTag1, &io->bAlways ); + /*-- inverted sp3 start tag */ + szGetTag( AuxLbl, io->nTag, io->bTag2 = io->bTag1 | AL_SP3I, io->szTag2, &io->bAlways ); + inchi_strbuf_reset( strbuf ); io->tot_len = 0; + io->tot_len = str_AuxInvSp3( io->pINChISort, io->pINChISort2, strbuf, + &io->bOverflow, io->bOutType, io->TAUT_MODE, io->num_components, + io->bSecondNonTautPass, io->bOmitRepetitions, io->bUseMulipliers); + if ( str_LineEnd( io->szTag2, &io->bOverflow, strbuf, -1, io->bPlainTextTags ) ) + return 1; + inchi_ios_print( out_file, "%s%s", strbuf->pStr, pLF ); + + /*-- inverted sp3 canonical numbering */ + if ( io->bInvStereoOrigNumb[io->iCurTautMode] ) + { + szGetTag( AuxLbl, io->nTag, io->bTag2 = io->bTag1 | AL_SP3N, io->szTag2, &io->bAlways ); + inchi_strbuf_reset( strbuf ); io->tot_len = 0; + + io->tot_len = str_AuxInvSp3Numb( pCG, io->pINChISort, io->pINChISort2, + strbuf, &io->bOverflow, io->bOutType, + io->TAUT_MODE, io->num_components, + io->bSecondNonTautPass, io->bOmitRepetitions); + + if ( str_LineEnd( io->szTag2, &io->bOverflow, strbuf, -1, io->bPlainTextTags ) ) + return 1; + inchi_ios_print( out_file, "%s%s", strbuf->pStr, pLF ); + } + else + { + if ( io->bPlainTextTags == 1 ) inchi_ios_print( out_file, "/" ); + } + } + else + { + if ( io->bPlainTextTags == 1 ) + inchi_ios_print( out_file, "//" ); + /* Inverted stereo -- sp3 only + canonical numbering */ + } + + return 0; +} + + +/* + Output AuxInfo: isotopic info +*/ +int OutputAUXINFO_IsotopicInfo( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + int *INCHI_basic_or_INCHI_reconnected, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ) +{ +int i; + + /* if InChI Fixed-H isotopic is empty, then do not output corresponding AuxInfo */ + + i = io->bSecondNonTautPass && + (*io->pSortPrintINChIFlags & ((*INCHI_basic_or_INCHI_reconnected==INCHI_BAS)? FLAG_SORT_PRINT_NO_IFIX_H_BAS : + FLAG_SORT_PRINT_NO_IFIX_H_REC )); + + if ( io->bIsotopic && !i && + (io->bIsotopicOrigNumb[io->iCurTautMode] || + io->bIsotopicAtomEqu[io->iCurTautMode] || + io->bTautomericOutputAllowed && io->bTautomeric && io->bIsotopicTautEqu[io->iCurTautMode] || + io->bInvIsotopicStereo[io->iCurTautMode] + && ( io->bIgn_UU_Sp3_Iso[io->iCurTautMode] || io->bIgn_UU_Sp2_Iso[io->iCurTautMode] ) ) ) + { + /*-- isotopic aux info header */ + szGetTag( AuxLbl, io->nTag, io->bTag1 = AL_ISOT | io->bFhTag, io->szTag1, &io->bAlways ); + inchi_strbuf_reset( strbuf ); /* pStr[io->tot_len = 0] = '\0'; */ + /*-- Original atom numbers in order of isotopic canonical numbers */ + szGetTag( AuxLbl, io->nTag, io->bTag2 = io->bTag1 | AL_ISON, io->szTag2, &io->bAlways ); + if ( io->bIsotopicOrigNumb[io->iCurTautMode] ) + { + inchi_strbuf_reset( strbuf ); + io->tot_len = 0; + io->tot_len = str_AuxIsoNumb( pCG, io->pINChISort, io->pINChISort2, + strbuf, &io->bOverflow, io->bOutType, + io->TAUT_MODE, io->num_components, + io->bSecondNonTautPass, io->bOmitRepetitions); + if ( str_LineEnd( io->szTag2, &io->bOverflow, strbuf, -1, io->bPlainTextTags ) ) + return 1; + inchi_ios_print( out_file, "%s%s", strbuf->pStr, pLF ); + } + else + { + /*if ( io->bPlainTextTags == 1 ) inchi_ios_print( out_file, "/" );*/ + inchi_ios_print( out_file, "%s%s", io->szTag2, pLF ); /* mark isotopic output */ + } + + /*-- Isotopic symmetry */ + if ( io->bIsotopicAtomEqu[io->iCurTautMode] ) + { + /*-- atoms */ + szGetTag( AuxLbl, io->nTag, io->bTag2 = io->bTag1 | AL_AEQU, io->szTag2, &io->bAlways ); + inchi_strbuf_reset( strbuf ); io->tot_len = 0; + io->tot_len = str_AuxIsoEqu( io->pINChISort, io->pINChISort2, + strbuf, + &io->bOverflow, io->bOutType, io->TAUT_MODE, io->num_components, + io->bSecondNonTautPass, io->bOmitRepetitions, io->bUseMulipliers); + if ( str_LineEnd( io->szTag2, &io->bOverflow, strbuf, -2/*was -1: Fix15*/, io->bPlainTextTags ) ) + return 1; + inchi_ios_print( out_file, "%s%s", strbuf->pStr, pLF ); + } + else + { + if ( io->bPlainTextTags == 1 ) + inchi_ios_print( out_file, "/" ); + } + + /*-- Tautomeric groups, isotopic */ + if ( io->bTautomericOutputAllowed && io->bTautomeric && io->bIsotopicTautEqu[io->iCurTautMode] ) + { + /*-- Isotopic tautomeric groups equivalence */ + szGetTag( AuxLbl, io->nTag, io->bTag2 = io->bTag1 | AL_GEQU, io->szTag2, &io->bAlways ); + inchi_strbuf_reset( strbuf ); io->tot_len = 0; + io->tot_len = str_AuxIsoTgroupEqu( io->pINChISort, + strbuf, &io->bOverflow, + io->bOutType, io->TAUT_MODE, io->num_components, + io->bOmitRepetitions, io->bUseMulipliers); + if ( str_LineEnd( io->szTag2, &io->bOverflow, strbuf, -2/*was -1: Fix15*/, io->bPlainTextTags ) ) + return 1; + inchi_ios_print( out_file, "%s%s", strbuf->pStr, pLF ); + } + else + { + if ( io->bTautomericOutputAllowed && io->bTautomeric ) + { + if ( io->bPlainTextTags == 1 ) + inchi_ios_print( out_file, "/" ); + } + } + /*-- Isotopic inverted stereo */ + if ( io->bInvIsotopicStereo[io->iCurTautMode] ) + { + szGetTag( AuxLbl, io->nTag, io->bTag2 = io->bTag1 | AL_STER, io->szTag2, &io->bAlways ); + /*-- inverted isotopic sp3 start tag */ + szGetTag( AuxLbl, io->nTag, io->bTag3 = io->bTag2 | AL_SP3I, io->szTag3, &io->bAlways ); + inchi_strbuf_reset( strbuf ); io->tot_len = 0; + io->tot_len = str_AuxInvIsoSp3( io->pINChISort, io->pINChISort2, + strbuf, &io->bOverflow, + io->bOutType, io->TAUT_MODE, io->num_components, + io->bSecondNonTautPass, io->bOmitRepetitions, io->bUseMulipliers); + if ( str_LineEnd( io->szTag3, &io->bOverflow, strbuf, -1, io->bPlainTextTags ) ) + return 1; + inchi_ios_print( out_file, "%s", strbuf->pStr ); + /*-- inverted isotopic sp3 canonical numbering */ + if ( io->bInvIsotopicStereoOrigNumb[io->iCurTautMode] ) + { + szGetTag( AuxLbl, io->nTag, io->bTag3 = io->bTag2 | AL_SP3N, io->szTag3, &io->bAlways ); + inchi_strbuf_reset( strbuf ); io->tot_len = 0; + io->tot_len = str_AuxInvIsoSp3Numb( pCG, io->pINChISort, io->pINChISort2, + strbuf, &io->bOverflow, + io->bOutType, io->TAUT_MODE, + io->num_components, + io->bSecondNonTautPass, + io->bOmitRepetitions); + + if ( str_LineEnd( io->szTag3, &io->bOverflow, strbuf, -1, io->bPlainTextTags ) ) + return 1; + inchi_ios_print( out_file, "%s%s", strbuf->pStr, pLF ); + } + else + { + if ( io->bPlainTextTags == 1 ) + inchi_ios_print( out_file, "/" ); + } + } + else + { + if ( io->bPlainTextTags == 1 ) + inchi_ios_print( out_file, "//" ); + } + /*-- totally omitted undefined/unknown isotopic stereo */ + } /* Aux info isotopic */ + +return 0; +} + + +/* + Output AuxInfo: + charges, radicals, unusual valences +*/ +int OutputAUXINFO_ChargesRadicalsAndUnusualValences( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ) +{ + if ( !io->bSecondNonTautPass && io->bChargesRadVal[io->iCurTautMode] ) + { + /* aux equ atoms */ + /* 1. Compare to tautomeric equivalence (in case of second, non-taut, pass only) */ + /* 2. Compare to the previous component if (1) failed to find equivalence */ + szGetTag( AuxLbl, io->nTag, io->bTag1 = AL_CRV_ | io->bFhTag, io->szTag1, &io->bAlways ); + + inchi_strbuf_reset( strbuf ); + io->tot_len = 0; + + io->tot_len = str_AuxChargeRadVal( io->pINChISort, strbuf, + &io->bOverflow, io->bOutType, io->TAUT_MODE, + io->num_components, io->bUseMulipliers); + + if ( str_LineEnd( io->szTag1, &io->bOverflow, strbuf, -1, io->bPlainTextTags ) ) + return 1; + + inchi_ios_print( out_file, "%s%s", strbuf->pStr, pLF ); + } + + return 0; +} + + +/* + Output AuxInfo: + reversibility info (to restore orig. structure) +*/ +int OutputAUXINFO_ReversibilityInfo( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + ORIG_STRUCT *pOrigStruct, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ) +{ + if ( !io->bSecondNonTautPass && + pOrigStruct && pOrigStruct->num_atoms && + pOrigStruct->szAtoms + && pOrigStruct->szBonds + && pOrigStruct->szCoord ) + { + int length, cur_pos, line_len, last_pos, nMaxLineLen; + char *p; + nMaxLineLen = inchi_min( 80, strbuf->nAllocatedLength ); /* restrict line length to 80 characters */ + + szGetTag( AuxLbl, io->nTag, io->bTag1 = AL_REVR | io->bFhTag, io->szTag1, &io->bAlways ); + + /* Atoms /A: */ + szGetTag( AuxLbl, io->nTag, io->bTag2 = io->bTag1 | AL_ATMR, io->szTag2, &io->bAlways ); + inchi_strbuf_reset( strbuf ); + inchi_ios_print( out_file, "%s%s", io->szTag2, strbuf->pStr ); + p = pOrigStruct->szAtoms; + length = (int) strlen( p ); + io->tot_len = strbuf->nUsedLength; + line_len = nMaxLineLen - io->tot_len; + for ( cur_pos = 0; cur_pos < length; cur_pos = last_pos ) + { + if ( length - cur_pos >= line_len ) + { + last_pos = cur_pos + line_len; + /* search backward for the nearest first atom letter (always uppercase) */ + while ( cur_pos < last_pos && !isupper( UCINT p[last_pos] ) ) { + last_pos --; + } + } + else + { + last_pos = length; + } + if ( last_pos > cur_pos ) + { + memcpy( strbuf->pStr + strbuf->nUsedLength, p+cur_pos, last_pos - cur_pos ); + strbuf->pStr[strbuf->nUsedLength + last_pos - cur_pos] = '\0'; + /*strbuf->nUsedLength = strbuf->nUsedLength + last_pos - cur_pos;*/ + inchi_ios_print( out_file, "%s%s", strbuf->pStr, io->bPlainTextTags? "" : "\n" ); + } + else + { + break; + } + } + if ( pLF[0] ) + inchi_ios_print( out_file, "%s", pLF ); + + inchi_strbuf_reset( strbuf ); + + /* Bonds /B: */ + szGetTag( AuxLbl, io->nTag, io->bTag2 = io->bTag1 | AL_BNDR, io->szTag2, &io->bAlways ); + inchi_strbuf_reset( strbuf ); + inchi_ios_print( out_file, "%s%s", io->szTag2, strbuf->pStr ); + + p = pOrigStruct->szBonds; + length = (int) strlen( p ); + line_len = nMaxLineLen - io->tot_len; + for ( cur_pos = 0; cur_pos < length; cur_pos = last_pos ) + { + if ( length - cur_pos >= line_len ) + { + last_pos = cur_pos + line_len - 1; + /* search backward for the nearest first bond delimiter ";" */ + while ( cur_pos < last_pos && p[last_pos] != ';' ) + { + last_pos --; + } + if ( cur_pos < last_pos ) + { + last_pos ++; /* include ';' at the end of the line */ + } + } + else + { + last_pos = length; + } + if ( last_pos > cur_pos ) + { + memcpy( strbuf->pStr, p+cur_pos, last_pos - cur_pos ); + strbuf->pStr[last_pos - cur_pos] = '\0'; + strbuf->nUsedLength = last_pos - cur_pos; + inchi_ios_print( out_file, "%s%s", strbuf->pStr, io->bPlainTextTags? "" : "\n" ); + inchi_strbuf_reset( strbuf ); + } + else + { + break; + } + } + if ( pLF[0] ) + { + inchi_ios_print( out_file, "%s", pLF ); + } + + /* Coordinates /C: */ + szGetTag( AuxLbl, io->nTag, io->bTag2 = io->bTag1 | AL_XYZR, io->szTag2, &io->bAlways ); + inchi_strbuf_reset( strbuf ); + inchi_ios_print( out_file, "%s%s", io->szTag2, strbuf->pStr ); + + p = pOrigStruct->szCoord; + length = (int) strlen( p ); + line_len = nMaxLineLen - io->tot_len; + for ( cur_pos = 0; cur_pos < length; cur_pos = last_pos ) + { + if ( length - cur_pos >= line_len ) + { + last_pos = cur_pos + line_len - 1; + /* search backward for the nearest first coord. delimiter ";" */ + while ( cur_pos < last_pos && p[last_pos] != ';' ) + { + last_pos --; + } + if ( cur_pos < last_pos ) + { + last_pos ++; /* include ';' at the end of the line */ + } + } + else + { + last_pos = length; + } + if ( last_pos > cur_pos ) + { + memcpy( strbuf->pStr, p+cur_pos, last_pos - cur_pos ); + strbuf->pStr[last_pos - cur_pos] = '\0'; + strbuf->nUsedLength = last_pos - cur_pos; + inchi_ios_print( out_file, "%s%s", strbuf->pStr, io->bPlainTextTags? "" : "\n" ); + inchi_strbuf_reset( strbuf ); + } + else + { + break; + } + } + + if ( pLF[0] ) + { + inchi_ios_print( out_file, "%s", pLF ); + } + } + + return 0; +} + + +/* + +*/ +int OutputAUXINFO_PolymerInfo( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + ORIG_STRUCT *pOrigStruct, + OutputINCHI_CtlData *io, + char *pLF, char *pTAB ) +{ +int k, i, q; +OrigAtDataPolymer *p; +OrigAtDataPolymerUnit *u; + + + if ( !pOrigStruct ) return 0; + p = pOrigStruct->polymer; + if ( !p ) return 0; + + inchi_strbuf_reset( strbuf ); + + inchi_ios_print( out_file, "/Z:" ); + + + /* Print polymer units data */ + for (i=0; in; i++) + { + /* For each unit u ... */ + u = p->units[i]; + + /* print kinds of unit */ + inchi_strbuf_printf( strbuf, "%-d%-d%-d-", u->type, u->subtype, u->conn ); + inchi_strbuf_printf( strbuf, "%-s-", u->smt[0]?u->smt:"n" ); + + /* Print unit atoms */ + print_sequence_of_nums_compressing_ranges( u->na, u->alist, strbuf ); + + /* Print bonds from unit to otside */ + if ( u->nb > 0 ) + { + inchi_strbuf_printf( strbuf, "(" ); + for (k=0; k<2*u->nb-1; k++) + inchi_strbuf_printf( strbuf, "%-d,", u->blist[k] ); + inchi_strbuf_printf( strbuf, "%-d)", u->blist[2*u->nb-1] ); + } + + + if ( fabs( -fabs(u->xbr1[0]) + 777777.777 ) > 1.e-7 ) + { + inchi_strbuf_printf( strbuf, "[" ); + for (q=0; q<3; q++) + inchi_strbuf_printf( strbuf, "%-f,", u->xbr1[q] ); + inchi_strbuf_printf( strbuf, "%-f]", u->xbr1[3] ); + } + if ( fabs( -fabs(u->xbr2[0]) + 777777.777 ) > 1.e-7 ) + { + inchi_strbuf_printf( strbuf, "[" ); + for (q=0; q<3; q++) + inchi_strbuf_printf( strbuf, "%-f,", u->xbr2[q] ); + inchi_strbuf_printf( strbuf, "%-f]", u->xbr2[3] ); + } + + if ( i < p->n-1 ) + inchi_strbuf_printf( strbuf, ";" ); + } + + inchi_ios_print( out_file, "%s%s", strbuf->pStr, pLF ); + + return 0; +} + + +int psbond_min_num_compare( int *bond1, int* bond2 ) +{ + int min1 = inchi_min( bond1[0], bond1[1] ); + int min2 = inchi_min( bond2[0], bond2[1] ); + int max1 = inchi_max( bond1[0], bond1[1] ); + int max2 = inchi_max( bond2[0], bond2[1] ); + + if ( min1 < min2 ) return -1; + if ( min1 > min2 ) return 1; + if ( min1 == min2 ) + { + if ( max1 < max2 ) return -1; + if ( max1 > max2 ) return 1; + } + return 0; +} + + +/* + +*/ +void HidePolymerRelatedInternals( INCHI_IOSTREAM *out ) +{ +char *s = out->s.pStr, *buf=NULL; +char prev_layer_symbol = '0'; +int i, j, ii, nzz, nzz1 = 0, /* nzz2 = 0, */ + nslash = 0, ncopied = 0, + in_z = 0, skip = 0; + + nzz1 = CountZzInFormula( s ); + if ( nzz1 == 0 ) return; + + buf = (char *) inchi_calloc( out->s.nUsedLength + 1, sizeof(char) ); + + nzz = nzz1; + in_z = skip = 0; + for (i=0; is.nUsedLength; i++) + { + int pre_eol = ( i == out->s.nUsedLength - 1 ); + + if ( !skip ) + { + buf[ncopied] = s[i]; + ncopied++; + } + + if ( in_z && !skip ) + { + if ( s[i] == '(' ) + { + skip = 1; + } + } + else if ( in_z && skip ) + { + if ( s[i] == '-' ) + { + skip = 0; + } + } + + if ( s[i] == '/' || pre_eol ) + { + if ( in_z ) + in_z = 0; + + if ( !pre_eol ) + { + nslash++; + } + + if ( nslash == 2 || + (nslash == 1 && pre_eol) || + ( prev_layer_symbol == 'f' ) + ) + { + if ( nzz ) + { + /* eat Zz's */ + ii = i; + if ( pre_eol ) ii = i + 1; + if ( s[ii-1] == 'z' && s[ii-2] == 'Z' ) + { + ncopied -= 2; + for (j=ii-3; j>=0; j--) + { + if ( s[j] == '.' ) + break; + ncopied--; + } + ncopied--; + if ( !pre_eol ) + buf[ncopied-1] = '/'; + else + buf[ncopied-1] = '\0'; + } + } + } + else if ( nslash > 2 || pre_eol ) + { + if ( nzz ) + { + if ( prev_layer_symbol != 'p' && + prev_layer_symbol != 's' && + prev_layer_symbol != 'f' && + prev_layer_symbol != 'z' + ) + { + /* eat nzz last ; if any */ + int n_eaten = 0; + char eatable = ';'; + if ( prev_layer_symbol == 'm' ) + eatable = '.'; + ii = i; + if ( pre_eol ) + ii = i + 1; + for (j=ii-1; j>=0; j--) + { + if ( s[j] == eatable && n_eaten < nzz ) + { + ncopied--; + n_eaten++; + } + else + break; + } + if ( !pre_eol ) + buf[ncopied-1] = '/'; + else + { + buf[ncopied] = '\0'; + break; + } + } + } + } + + prev_layer_symbol = s[i+1]; + if ( s[i+1] == 'r' ) + { + /* nzz = nzz2; paranoidal */ + nslash = 1; + } + + else if ( s[i+1] == 'z' ) + { + in_z = 1; + } + } + } + + + out->s.nUsedLength = 0; + + inchi_ios_print_nodisplay( out, "%s", buf ); + + inchi_free( buf ); + + return; +} + + +int CountZzInFormula( char *s) +{ + int i, j, len, nzz = 0, nslash = 0; + + if ( !s ) + return 0; + len = strlen(s); + for (i=0; i< len; i++) + { + if ( s[i]=='/' ) + nslash++; + if ( nslash == 2 ) + { + if ( s[i-1] != 'z' || s[i-2] != 'Z' ) break; + for ( j = i-3; j>=0; j-- ) + if ( !isdigit( UCINT s[j]) ) + break; + nzz = strtol( s + j +1, NULL, 10); + } + } + return nzz; +} diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichiprt2.c b/INCHI-1-SRC/INCHI_BASE/src/ichiprt2.c similarity index 70% rename from INCHI-1-SRC/INCHI_API/inchi_dll/ichiprt2.c rename to INCHI-1-SRC/INCHI_BASE/src/ichiprt2.c index f880fb4..f8e269e 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichiprt2.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichiprt2.c @@ -1,1613 +1,2004 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include - -#include "mode.h" - -#include "inpdef.h" -#include "ichi.h" -#include "strutil.h" -#include "util.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "ichicant.h" -#include "ichicano.h" -#include "ichicomn.h" - -#include "ichicomp.h" -#include "ichimain.h" -#include "ichimake.h" - -/*****************************************************************************************/ -int Eql_INChI_Stereo( INChI_Stereo *s1, int eql1, INChI_Stereo *s2, int eql2, int bRelRac ) -{ - int inv1=0, inv2=0, len; - - if ( !s1 ) { - return 0; - } -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) -#else - bRelRac = 0; -#endif - - if( EQL_SP2 == eql1 ) { - if ( (len=s1->nNumberOfStereoBonds) > 0 && s1->b_parity && s1->nBondAtom1 && s1->nBondAtom2 ) { - if ( !s2 ) { - if ( EQL_EXISTS == eql2 ) { - /* find whether double bond stereo exists*/ - return 1; - } - return 0; - } - if ( EQL_SP2 == eql2 && - len == s2->nNumberOfStereoBonds && s2->b_parity && s2->nBondAtom1 && s2->nBondAtom2 && - !memcmp( s1->nBondAtom1, s2->nBondAtom1, len * sizeof(s1->nBondAtom1[0])) && - !memcmp( s1->nBondAtom2, s2->nBondAtom2, len * sizeof(s1->nBondAtom2[0])) && - !memcmp( s1->b_parity, s2->b_parity, len * sizeof(s1->b_parity[0])) ) { - return 1; - } - } - return 0; - } else - if ( (eql1 == EQL_SP3 || (inv1 = (eql1 == EQL_SP3_INV))) && - (len=s1->nNumberOfStereoCenters) > (bRelRac? 1 : 0) ) { - - S_CHAR *t_parity1, *t_parity2; - AT_NUMB *nNumber1, *nNumber2; - if ( inv1 ) { - if ( s1->nCompInv2Abs ) { - t_parity1 = s1->t_parityInv; - nNumber1 = s1->nNumberInv; - } else { - return 0; - } - } else { - t_parity1 = s1->t_parity; - nNumber1 = s1->nNumber; - } - - if ( t_parity1 && nNumber1 ) { - if ( !s2 ) { - if ( EQL_EXISTS == eql2 && (!inv1 || s1->nCompInv2Abs) ) { - /* the 1st sp3 (inverted if requested) stereo exists*/ - return 1; - } - return 0; /* both sp3 do not exist */ - } - if( (eql2 == EQL_SP3 || (inv2 = (eql2 == EQL_SP3_INV))) && - len == s2->nNumberOfStereoCenters ) { - if ( inv2 ) { - if ( s2->nCompInv2Abs && s1->nCompInv2Abs ) { - t_parity2 = s2->t_parityInv; - nNumber2 = s2->nNumberInv; - } else { - /* if one sp3 is inverted then another should have non-trivial inverted stereo */ - return 0; - } - } else { - if ( inv1 && !s2->nCompInv2Abs ) { - /* if one sp3 is inverted then another should have non-trivial inverted stereo */ - return 0; - } - t_parity2 = s2->t_parity; - nNumber2 = s2->nNumber; - } - if ( t_parity2 && nNumber2 ) { - if ( inv1 ^ inv2 ) { - int i, num_inv; - for ( i = 0, num_inv = 0; i < len; i ++ ) { - if ( nNumber1[i] != nNumber2[i] ) - break; - if ( ATOM_PARITY_WELL_DEF(t_parity1[i]) && - ATOM_PARITY_WELL_DEF(t_parity2[i]) ) { - if ( 3 == t_parity1[i] + t_parity2[i] ) { - num_inv ++; - } else { - break; - } - } else - if ( t_parity1[i] != t_parity2[i] ) { - break; - } - } - return (len == i && num_inv > 0); - } else { - return !memcmp( t_parity1, t_parity2, len*sizeof(t_parity1[0])) && - !memcmp( nNumber1, nNumber2, len*sizeof(nNumber1[0])); - } - } - } - } - } - return 0; -} -/**********************************************************************************************/ -int Eql_INChI_Isotopic( INChI *i1, INChI *i2 ) -{ - int eq = i1 && i2 && !i1->bDeleted && !i2->bDeleted && - ( i1->nNumberOfIsotopicAtoms > 0 || i1->nNumberOfIsotopicTGroups > 0 ) && - i1->nNumberOfIsotopicAtoms == i2->nNumberOfIsotopicAtoms && - i1->nNumberOfIsotopicTGroups == i2->nNumberOfIsotopicTGroups && - ( !i1->nNumberOfIsotopicAtoms || - i1->IsotopicAtom && i2->IsotopicAtom && - !memcmp( i1->IsotopicAtom, i2->IsotopicAtom, - i1->nNumberOfIsotopicAtoms * sizeof(i1->IsotopicAtom[0]) ) ) && - ( !i1->nNumberOfIsotopicTGroups || - i1->IsotopicTGroup && i2->IsotopicTGroup && - !memcmp( i1->IsotopicTGroup, i2->IsotopicTGroup, - i1->nNumberOfIsotopicTGroups * sizeof(i1->IsotopicAtom[0]) ) ); - return eq; - -} -/**********************************************************************************************/ -int Eql_INChI_Aux_Equ( INChI_Aux *a1, int eql1, INChI_Aux *a2, int eql2 ) -{ - int t1=0, t2=0, len; - AT_NUMB *n1=NULL, *n2=NULL; - if ( !a1 || !a2 ) { - return 0; - } - t1 = (eql1 & EQL_EQU_TG); - t2 = (eql2 & EQL_EQU_TG); - if ( t1 && t2 ) { - if ( (len = a1->nNumberOfTGroups) > 0 && len == a2->nNumberOfTGroups && !a1->bDeleted && !a2->bDeleted ) { - if (eql1 & EQL_EQU_ISO) { - if ( a1->bIsIsotopic ) { - n1 = a1->nConstitEquIsotopicTGroupNumbers; - } - } else { - n1 = a1->nConstitEquTGroupNumbers; - } - if (eql2 & EQL_EQU_ISO) { - if ( a2->bIsIsotopic ) { - n2 = a2->nConstitEquIsotopicTGroupNumbers; - } - } else { - n2 = a2->nConstitEquTGroupNumbers; - } - } - } else - if ( !t1 && !t2 ) { - if ( (len = a1->nNumberOfAtoms) > 0 && len == a2->nNumberOfAtoms && !a1->bDeleted && !a2->bDeleted ) { - if (eql1 & EQL_EQU_ISO) { - if ( a1->bIsIsotopic ) { - n1 = a1->nConstitEquIsotopicNumbers; - } - } else { - n1 = a1->nConstitEquNumbers; - } - if (eql2 & EQL_EQU_ISO) { - if ( a2->bIsIsotopic ) { - n2 = a2->nConstitEquIsotopicNumbers; - } - } else { - n2 = a2->nConstitEquNumbers; - } - } - } - if ( n1 && n2 && !memcmp(n1, n2, len*sizeof(n1[0])) && bHasEquString( n1, len) ) { - return 1; - } - return 0; -} -/**********************************************************************************************/ -int Eql_INChI_Aux_Num( INChI_Aux *a1, int eql1, INChI_Aux *a2, int eql2 ) -{ - int len; - AT_NUMB *n1=NULL, *n2=NULL; - if ( !a1 || !a2 ) { - return 0; - } - if ( (len = a1->nNumberOfAtoms) <= 0 || len != a2->nNumberOfAtoms || a1->bDeleted || a2->bDeleted ) { - return 0; - } - if ( (eql1 & EQL_NUM_ISO) && !a1->bIsIsotopic || - (eql2 & EQL_NUM_ISO) && !a2->bIsIsotopic ) { - return 0; - } - - switch ( eql1 ) { - case EQL_NUM: - n1 = a1->nOrigAtNosInCanonOrd; - break; - case EQL_NUM_ISO: - n1 = a1->nIsotopicOrigAtNosInCanonOrd; - break; - case EQL_NUM_INV: - n1 = a1->nOrigAtNosInCanonOrdInv; - break; - case ( EQL_NUM_INV | EQL_NUM_ISO ): - n1 = a1->nIsotopicOrigAtNosInCanonOrdInv; - break; - default: - return 0; - } - - switch ( eql2 ) { - case EQL_NUM: - n2 = a2->nOrigAtNosInCanonOrd; - break; - case EQL_NUM_ISO: - n2 = a2->nIsotopicOrigAtNosInCanonOrd; - break; - case EQL_NUM_INV: - n2 = a2->nOrigAtNosInCanonOrdInv; - break; - case ( EQL_NUM_INV | EQL_NUM_ISO ): - n2 = a2->nIsotopicOrigAtNosInCanonOrdInv; - break; - default: - return 0; - } - - if ( n1 && n2 && !memcmp( n1, n2, len*sizeof(n1[0])) ) { - return 1; - } - return 0; -} -/**********************************************************************************************/ -int bHasOrigInfo( ORIG_INFO *OrigInfo, int num_atoms ) -{ - int i, bFound = 0; - if ( OrigInfo && num_atoms > 0 ) { - for ( i = 0; !bFound && i < num_atoms; i ++ ) { - bFound |= (0 != OrigInfo[i].cCharge) || - (0 != OrigInfo[i].cRadical) || - (0 != OrigInfo[i].cUnusualValence); - - } - } - return bFound; -} -/**********************************************************************************************/ -int EqlOrigInfo( INChI_Aux *a1, INChI_Aux *a2 ) -{ - int ret = a1 && a2 && a1->nNumberOfAtoms == a2->nNumberOfAtoms && - bHasOrigInfo( a1->OrigInfo, a1->nNumberOfAtoms ) && a2->OrigInfo && - !memcmp( a1->OrigInfo, a2->OrigInfo, a1->nNumberOfAtoms * sizeof(a1->OrigInfo[0]) ); - return ret; - -} - -/**********************************************************************************************/ -int bHasEquString( AT_NUMB *LinearCT, int nLenCT ) -{ - /* produce output string; */ - int i, k; - if ( !LinearCT ) - return 0; - for ( k = 0; k < nLenCT; k ++ ) { - /* find the first equivalence number */ - if ( k != (int)LinearCT[k] - 1 ) - continue; - for ( i = k; i < nLenCT; i ++ ) { - if ( k != (int)LinearCT[i]-1 ) - continue; - if ( k < i ) { - return 1; - } - } - } - return 0; -} -/********************************************************************************************/ -int MakeMult( int mult, const char *szTailingDelim, char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) -{ - char szValue[16]; - int len = 0, len_delim; - if ( mult == 1 || *bOverflow ) - return 0; - if ( nCtMode & CT_MODE_ABC_NUMBERS ) { - len += MakeAbcNumber( szValue, (int)sizeof(szValue), NULL, mult ); - } else { - len += MakeDecNumber( szValue, (int)sizeof(szValue), NULL, mult ); - } - len_delim = strlen(szTailingDelim); - if ( len + len_delim < (int)sizeof(szValue) ) { - strcpy( szValue+len, szTailingDelim ); - len += len_delim; - if ( len < nLen_szLinearCT ) { - strcpy( szLinearCT, szValue ); - return len; - } - } - *bOverflow |= 1; - return 0; -} -/********************************************************************************************/ -int MakeDelim( const char *szTailingDelim, char *szLinearCT, int nLen_szLinearCT, int *bOverflow) -{ - int len_delim; - if ( !szTailingDelim || !*szTailingDelim || *bOverflow ) - return 0; - len_delim = strlen(szTailingDelim); - if ( len_delim < nLen_szLinearCT ) { - strcpy( szLinearCT, szTailingDelim ); - return len_delim; - } - *bOverflow |= 1; - return 0; -} -/********************************************************************************************/ -int MakeEqStr( const char *szTailingDelim, int mult, char *szLinearCT, int nLen_szLinearCT, int *bOverflow) -{ - int len = 0, len_delim; - char szValue[16]; - if ( !szTailingDelim || !*szTailingDelim || *bOverflow ) - return 0; - if ( mult != 1 ) { - len = MakeDecNumber( szValue, (int)sizeof(szValue), NULL, mult ); - } - len_delim = strlen(szTailingDelim); - if ( len_delim + len < nLen_szLinearCT ) { - if ( len > 0 ) { - memcpy( szLinearCT, szValue, len ); - } - strcpy( szLinearCT+len, szTailingDelim ); - return len + len_delim; - } - *bOverflow |= 1; - return 0; -} -/********************************************************************************************** - * nCtMode = 0: full - * 1: censored CT (no orphans) - * 2: compressed CT (Abs numbers) - **********************************************************************************************/ -int MakeCtStringNew( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, - S_CHAR *nNum_H, int num_atoms, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) -{ - /* produce output string; */ - int nLen = 0, len, i, bOvfl = *bOverflow; - char szValue[16]; - int nValue, nDelim, num_H; - AT_NUMB *nDfsOrderCT = NULL; - int bNoNum_H = (NULL == nNum_H); - int nNumRingClosures; - int bAbcNumbers = (0 != ( nCtMode & CT_MODE_ABC_NUMBERS )); - int bPredecessors = (0 != ( nCtMode & CT_MODE_PREDECESSORS )); - int bCountRingClosures = bAbcNumbers && bPredecessors && (nCtMode & CT_MODE_ABC_NUM_CLOSURES); - if ( nLenCT <= 1 ) { - return 0; /* no atoms or a single atom: no connection table */ - } - /* make array containing connection string data */ - if ( !(nDfsOrderCT = GetDfsOrder4CT( LinearCT, nLenCT, nNum_H, num_atoms, nCtMode ) ) ) { - (*bOverflow) ++; - return 0; - } - - /* add connection table string */ - if ( !bOvfl && bAddDelim ) { - if ( nLen_szLinearCT > 1 ) { - strcpy( szLinearCT, "," ); - nLen ++; - } else { - bOvfl = 1; - } - } - - if ( !bOvfl ) { - nNumRingClosures = 0; - for ( i = 0; nDfsOrderCT[i] && nLen < nLen_szLinearCT; i += 3 ) { - nValue = (nDfsOrderCT[i] > MAX_ATOMS)? 0 : nDfsOrderCT[i]; - num_H = nDfsOrderCT[i+1]? nDfsOrderCT[i+1]-16:0; - nDelim = nDfsOrderCT[i+2]; - len = 0; - /* delimiter */ - if ( bPredecessors ) { - if ( bCountRingClosures ) { - if ( nDelim == '-' && i > 3 && bNoNum_H ) { - if ( !nNumRingClosures ) { - int j; - for ( j = i; nDfsOrderCT[j] && '-' == nDfsOrderCT[j+2]; j += 3 ) { - nNumRingClosures ++; - } - if ( nNumRingClosures ) { - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, nNumRingClosures ); - } - nNumRingClosures --; - } else { - nNumRingClosures --; - } - } else { - nNumRingClosures = 0; - } - } else - if ( nDelim && !( bAbcNumbers && nDelim == ',' ) ) { - if ( nNum_H || i > 3 ) { - szValue[len ++] = nDelim; - } - } - } else { - if ( nDelim && !( bAbcNumbers && nDelim == '-' ) ) { - szValue[len ++] = nDelim; - } - } - if ( bAbcNumbers ) { - if ( nValue || i ) { /* the 1st value may be zero in case of presdecessor list */ - len += MakeAbcNumber( szValue+len, (int)sizeof(szValue)-len, NULL, nValue ); - } - if ( num_H ) { - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, num_H ); - } - } else { - if ( nValue || i ) { /* the 1st value may be zero in case of presdecessor list */ - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, nValue ); - } - if ( num_H ) { - szValue[len] = 'H'; - len ++; - if ( num_H > 1 ) { - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, num_H ); - } - } - } - if ( 0 <= len && nLen+len < nLen_szLinearCT ) { - if ( len ) { - strcpy( szLinearCT+nLen, szValue ); - nLen += len; - } - } else { - bOvfl = 1; - break; - } - } - } - *bOverflow |= bOvfl; - if ( nDfsOrderCT ) - inchi_free( nDfsOrderCT ); - return nLen; -} -/********************************************************************************************** - * nCtMode = 0: full - * 1: censored CT (no orphans) - * 2: compressed CT (Abs numbers) - **********************************************************************************************/ -int MakeCtStringOld( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) -{ - /* produce output string; */ - int nLen = 0, len, i, bLessThanPrev, bOvfl = *bOverflow; - AT_NUMB nMax = 0; - char szValue[16]; - int nValue, bNext = 0; - /* add connection table string */ - if ( !( nCtMode & CT_MODE_ABC_NUMBERS ) && !bOvfl && bAddDelim ) { - if ( nLen_szLinearCT > 1 ) { - strcpy( szLinearCT, "," ); - nLen ++; - } else { - bOvfl = 1; - } - } - if ( !bOvfl ) { - for ( i = 0; i < nLenCT && nLen < nLen_szLinearCT; i ++ ) { - bLessThanPrev = 0; - if ( !(nCtMode & CT_MODE_NO_ORPHANS) || ((bLessThanPrev=LinearCT[i] < nMax) || - i+1 < nLenCT && LinearCT[i+1] < (nMax=LinearCT[i])) ) { - nValue = LinearCT[i]; - if ( nCtMode & CT_MODE_ABC_NUMBERS ) { - len = MakeAbcNumber( szValue, (int)sizeof(szValue), (!bNext && bAddDelim)? ITEM_DELIMETER : NULL, nValue ); - } else - if ( nCtMode & CT_MODE_NO_ORPHANS ) { /* censored CT */ - /* output '-' as a delimiter to show a bonding for decimal output of the connection table */ - len = MakeDecNumber( szValue, (int)sizeof(szValue), bLessThanPrev? "-":ITEM_DELIMETER, nValue ); - } else { - len = MakeDecNumber( szValue, (int)sizeof(szValue), i? ITEM_DELIMETER:NULL, nValue ); - } - if ( 0 <= len && nLen+len < nLen_szLinearCT ) { - if ( len ) { - strcpy( szLinearCT+nLen, szValue ); - nLen += len; - bNext ++; - } - } else { - bOvfl = 1; - break; - } - } - } - } - *bOverflow |= bOvfl; - return nLen; -} -/********************************************************************************************** - * nCtMode = 0: decimal - * 2: compressed CT (Abs numbers) - **********************************************************************************************/ -int MakeHString( int bAddDelim, S_CHAR *LinearCT, int nLenCT, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow ) -{ -#define INIT_MIN_NUM_H (-4) -#define INIT_MAX_NUM_H 16 -#define INIT_LEN_NUM_H (INIT_MAX_NUM_H - INIT_MIN_NUM_H + 1) - - /* produce output string; */ - int nLen = 0, len, i, iFirst, nVal, bOvfl = *bOverflow; - char szValue[32]; - const char *pH; - int bNext = 0; - /* add connection table string */ - if ( !( nCtMode & CT_MODE_ABC_NUMBERS ) && !bOvfl && bAddDelim ) { - if ( nLen_szLinearCT > 1 ) { - strcpy( szLinearCT, "," ); - nLen ++; - } else { - bOvfl = 1; - } - } - if ( !bOvfl && 0 < nLenCT && LinearCT ) { - if ( nCtMode & CT_MODE_EQL_H_TOGETHER ) { - int curMinH = INIT_MIN_NUM_H; - int curMaxH = INIT_MAX_NUM_H; - int curLenH = INIT_LEN_NUM_H; - int nInitNumH[INIT_LEN_NUM_H]; - int *nNumH = nInitNumH; - int numAt, curNumH; - int j, bOutOfRange, tot_num_no_H; - /* count atoms H */ - do { - bOutOfRange = 0; - tot_num_no_H = 0; /* number of atoms that have no H */ - memset( nNumH, 0, curLenH*sizeof(nNumH[0]) ); - for ( i = 0; i < nLenCT; i ++ ) { - curNumH = LinearCT[i]; - if ( curNumH < curMinH ) { - curMinH = curNumH; - bOutOfRange ++; - } else - if ( curNumH > curMaxH ) { - curMaxH = curNumH; - bOutOfRange ++; - } else - if ( !bOutOfRange ) { - nNumH[curNumH-curMinH] ++; - } - tot_num_no_H += !curNumH; - } - if ( tot_num_no_H == nLenCT ) { - return nLen; /* empty string */ - } - if ( bOutOfRange ) { - /* for debug only */ - if ( nNumH != nInitNumH ) { - *bOverflow |= 1; - inchi_free( nNumH ); - return nLen; - } - /* end debug */ - curLenH = curMaxH - curMinH + 1; - nNumH = (int*) inchi_malloc( curLenH * sizeof(nNumH[0]) ); - if ( !nNumH ) { - *bOverflow |= 1; - return nLen; - } - } - } while ( bOutOfRange ); /* the loop may be executed 1 or 2 times only */ - - for ( curNumH = curMinH; curNumH <= curMaxH; curNumH ++ ) { - numAt = nNumH[curNumH-curMinH]; /* number of atoms that have curNumH atoms H */ - if ( !numAt || !curNumH ) { - continue; /* no atom has this number of H or number of H = 0 */ - } - j = 0; - while ( j < nLenCT && numAt ) { - if ( curNumH == LinearCT[j] ) { - iFirst = ++j; - numAt --; - for ( ; j < nLenCT && curNumH == LinearCT[j] && numAt; j ++ ) { - numAt --; - } - if ( nCtMode & CT_MODE_ABC_NUMBERS ) { - len = MakeAbcNumber( szValue, (int)sizeof(szValue), NULL, iFirst ); - } else { - len = MakeDecNumber( szValue, (int)sizeof(szValue), bNext?ITEM_DELIMETER:NULL, iFirst ); - bNext ++; /* add a delimiter (comma) before all except the first */ - } - if ( iFirst < j ) { - /* output last canonical number */ - if ( nCtMode & CT_MODE_ABC_NUMBERS ) { - len += MakeAbcNumber( szValue+len, (int)sizeof(szValue), NULL, j ); - } else { - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, "-", j ); - } - } - if ( !numAt || ( nCtMode & CT_MODE_ABC_NUMBERS ) ) { - /* add number of H */ - /* output number of H */ - nVal = curNumH; - if ( nCtMode & CT_MODE_ABC_NUMBERS ) { - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, nVal ); - } else { - pH = nVal > 0? "H":"h"; - nVal = abs(nVal); - if ( nVal > 1 ) { - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, pH, nVal ); - } else { - strcpy( szValue+len, pH ); - len ++; - } - } - } - /* add to the output */ - if ( 0 <= len && nLen+len < nLen_szLinearCT ) { - if ( len ) { - strcpy( szLinearCT+nLen, szValue ); - nLen += len; - bNext ++; - } - } else { - bOvfl = 1; - break; - } - } else { - j ++; - } - } - } - if ( nNumH != nInitNumH ) { - inchi_free( nNumH ); - } - } else { - iFirst = 0; - for ( i = iFirst+1; i <= nLenCT && nLen < nLen_szLinearCT; i ++ ) { - if ( i < nLenCT && LinearCT[i] == LinearCT[iFirst] ) { - continue; - } - /* output identical values located at i = iFirst..i-1 */ - if ( LinearCT[iFirst] ) { /* output only non-zero values */ - /* first canonical number */ - nVal = LinearCT[iFirst]; - iFirst ++; - if ( nCtMode & CT_MODE_ABC_NUMBERS ) { - len = MakeAbcNumber( szValue, (int)sizeof(szValue), NULL, iFirst ); - } else { - len = MakeDecNumber( szValue, (int)sizeof(szValue), bNext?ITEM_DELIMETER:NULL, iFirst ); - } - if ( iFirst < i ) { - /* output last canonical number */ - if ( nCtMode & CT_MODE_ABC_NUMBERS ) { - len += MakeAbcNumber( szValue+len, (int)sizeof(szValue), NULL, i ); - } else { - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, "-", i ); - } - } - /* output number of H */ - if ( nCtMode & CT_MODE_ABC_NUMBERS ) { - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, nVal ); - } else { - pH = nVal > 0? "H":"h"; - nVal = abs(nVal); - if ( nVal > 1 ) { - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, pH, nVal ); - } else { - strcpy( szValue+len, pH ); - len ++; - } - } - if ( 0 <= len && nLen+len < nLen_szLinearCT ) { - if ( len ) { - strcpy( szLinearCT+nLen, szValue ); - nLen += len; - bNext ++; - } - } else { - bOvfl = 1; - break; - } - } - iFirst = i; - } - } - } - - *bOverflow |= bOvfl; - return nLen; - -#undef INIT_MIN_NUM_H -#undef INIT_MAX_NUM_H -#undef INIT_LEN_NUM_H -} -/********************************************************************************************** - * nCtMode = 0: full - * 1: censored CT (no orphans, that CT should have only atoms with neighbors) - * 2: compressed CT (Abc numbers) - **********************************************************************************************/ -int MakeCtString( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, - S_CHAR *nNum_H, int num_atoms, /* both parameters are not used here */ - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) -{ - - if ( !nNum_H || !(nCtMode & CT_MODE_NO_ORPHANS) ) { - return MakeCtStringOld( LinearCT, nLenCT, bAddDelim, - szLinearCT, nLen_szLinearCT, nCtMode, bOverflow); - } else { - return MakeCtStringNew( LinearCT, nLenCT, bAddDelim, - nNum_H, num_atoms, - szLinearCT, nLen_szLinearCT, nCtMode, bOverflow); - } -} - - - -/********************************************************************************************** - * nCtMode = 0: full: decimal-only, with parentheses around t-groups - * 2: compressed CT: do not add comma before the output string if bAddDelim != 0 - * do not add parentheses around t-groups - * atom canon numbers an Abc - * LinearCT format: - * N = number of tautomeric groups - * n = number of endpoints + 1 in a tautomeric group #1 - * next INCHI_T_NUM_MOVABLE lines (any after the first non-zero): - * h = number of hydrogen atoms in the tautomeric group - * m = number of negative charges - * ... (the rest of the INCHI_T_NUM_MOVABLE has not been established, ignore them) - * c(1) = canonical number of the first atom in the t-group - * ... - * c(n-1) = canonical number of the last atom in the t-group - * - **********************************************************************************************/ - -int MakeTautString( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) -{ - /* produce output string; */ - int nLen = 0, len, i, bOvfl = *bOverflow; - char szValue[16]; - const char *p; - int nValue, nGroupLen, iGroupOutputCount, bCompressed; - /* make tautomer string */ - if ( !nLenCT || !LinearCT || !*LinearCT ) { - return nLen; - } - bCompressed = ( nCtMode & CT_MODE_ABC_NUMBERS ); - if ( !bCompressed && !bOvfl && bAddDelim ) { - if ( nLen_szLinearCT > 1+LEN_EXTRA_SPACE ) { - strcpy( szLinearCT, COMMA_EXTRA_SPACE); - nLen += 1+LEN_EXTRA_SPACE; - } else { - bOvfl = 1; - } - } - LinearCT ++; /* bypass number of tautomeric groups */ - nLenCT --; - - if ( !bOvfl ) { - for ( i = nGroupLen = iGroupOutputCount = 0; i < nLenCT && nLen < nLen_szLinearCT; i ++ ) { - nValue = (int)LinearCT[i]; - if ( nGroupLen == iGroupOutputCount ) { - nGroupLen = nValue; - iGroupOutputCount = 0; - /* group delimiter (uncompressed) */ - if ( !bCompressed ) { - if ( !i ) { - strcpy( szValue, "(" ); - len = 1; - } else { - strcpy( szValue, ")(" ); - len = 2; - } - } else { - len = 0; - } - } else - if ( bCompressed && iGroupOutputCount >= INCHI_T_NUM_MOVABLE ) { - /* compressed canon number in Abc */ - len = MakeAbcNumber( szValue, (int)sizeof(szValue), NULL, nValue ); - iGroupOutputCount ++; - } else { - /* always output number of hydrogen atoms as a decimal */ - /* output leading space if: */ - /* (a) this is the first output value in compressed mode (i==1 && bCompressed) */ - /* (b) this is not the first output value in non-compressed mode ( iGroupOutputCount && !bCompressed) */ - if ( bCompressed ) { - p = NULL; - len = 0; - switch( iGroupOutputCount ) { - case 0: - len = MakeDecNumber( szValue, (int)sizeof(szValue), (i == 1)? ITEM_DELIMETER:NULL, nValue ); - break; - case 1: - p = "-"; - break; - case 2: - p = "+"; - break; - } - if ( p ) { - switch( nValue ) { - case 0: - len = 0; - break; - case 1: - strcpy(szValue, p); - len = strlen(szValue); - break; - default: - len = MakeDecNumber( szValue, (int)sizeof(szValue), p, nValue ); - break; - } - } - } else { - if ( iGroupOutputCount >= INCHI_T_NUM_MOVABLE ) { - /* canonical number of the atom in the tautomeric group */ - len = MakeDecNumber( szValue, (int)sizeof(szValue), ITEM_DELIMETER, nValue ); - } else { - p = NULL; - len = 0; - if ( nValue ) { - switch( iGroupOutputCount ) { - case 0: - p = "H"; - break; - case 1: - p = "-"; - break; - case 2: - p = "+"; - break; - } - if ( p ) { - /* number of hydrogens */ - if ( nValue == 1 ) { - strcpy(szValue, p); - len = strlen(szValue); - } else { - len = MakeDecNumber( szValue, (int)sizeof(szValue), p, nValue ); - } - } - } - } - } - iGroupOutputCount ++; - } - if ( 0 <= len && nLen+len < nLen_szLinearCT ) { - if ( len ) { - strcpy( szLinearCT+nLen, szValue ); - nLen += len; - } - } else { - bOvfl = 1; - break; - } - } - if ( !bOvfl && !bCompressed && i ) { - if ( nLen + 1 < nLen_szLinearCT ) { - strcpy( szLinearCT+nLen, ")" ); - nLen ++; - } else { - bOvfl = 1; - } - } - } - *bOverflow |= bOvfl; - return nLen; -} -/********************************************************************************************** - * nCtMode = 0: full - * 2: compressed CT - * 22+3s3: 22=canon. number; +3=charge; s=singlet (d=doublet, t=triplet, s is omitted if valence=0), 3 = valence - * 22+3.3, (charge, valence) 22.3 (valence) 22t3 (triplet, valence) - * Ab+3t4: Ab=canon. number; +3=charge or "." t=triplet (or s, d), 4=valence - **********************************************************************************************/ -int MakeCRVString( ORIG_INFO *OrigInfo, int nLenCT, int bAddDelim, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) -{ - /* produce output string; */ - int nLen = 0, len, k, bAbcNumbers; - int bOvfl = *bOverflow; - char szValue[32]; - int bNext=0; - bAbcNumbers = ( nCtMode & CT_MODE_ABC_NUMBERS ); - /* add connection table string */ - if ( !bOvfl && bAddDelim ) { - if ( nLen_szLinearCT > 2 ) { - strcpy( szLinearCT, ", " ); - nLen += 2; - } else { - bOvfl = 1; - } - } - for ( k = 0; !bOvfl && k < nLenCT && nLen < nLen_szLinearCT; k ++ ) { - /* find the next non-empty entry */ - if ( OrigInfo[k].cCharge || OrigInfo[k].cRadical || OrigInfo[k].cUnusualValence ) { - if ( bAbcNumbers ) { - /* - 3 items: Ad+3d4 (canon. numb=Ad, charge=+3, doublet, valence = 4 - 2 items: Ad.d4 Ad+3.4 Ad+3d - 1 item: Ad+3 Ad.d Ad4 - - dot output before radical: no charge, radical is present - dot before valence: charge is present, no radical, valence is present - */ - len = MakeAbcNumber( szValue, (int)sizeof(szValue), NULL, k+1 ); - - /* charge */ - if ( OrigInfo[k].cCharge ) { - if ( OrigInfo[k].cCharge > 0 ) { - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, "+", OrigInfo[k].cCharge ); - } else { - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, OrigInfo[k].cCharge ); - } - } - /* radical */ - if ( OrigInfo[k].cRadical ) { - if ( !OrigInfo[k].cCharge ) { - szValue[len ++] = '.'; - } - switch( OrigInfo[k].cRadical ) { - case 1: - szValue[len ++] = 'd'; - break; - case 2: - szValue[len ++] = 't'; - break; - default: - szValue[len ++] = 'u'; - break; - } - } - /* valence */ - if ( OrigInfo[k].cUnusualValence ) { - if ( OrigInfo[k].cCharge && !OrigInfo[k].cRadical ) { - szValue[len ++] = '.'; - } - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, OrigInfo[k].cUnusualValence ); - } - } else { - /* - 3 items: 22+3d4 (canon. numb=22, charge=+3, doublet, valence = 4 - 2 items: 22d4 22+3.4 22+3d - 1 item: 22+3 22d 22.4 - - dot output before valence: - (a) charge, no radical, valence - (b) no charge, no radical, valence - that is, whenever valence is present and no radical - */ - len = MakeDecNumber( szValue, (int)sizeof(szValue), bNext? ITEM_DELIMETER:NULL, k+1 ); - /* charge */ - if ( OrigInfo[k].cCharge ) { - if ( OrigInfo[k].cCharge > 0 ) { - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, "+", OrigInfo[k].cCharge ); - } else { - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, OrigInfo[k].cCharge ); - } - } - /* radical */ - if ( OrigInfo[k].cRadical ) { - switch( OrigInfo[k].cRadical ) { - case 1: - szValue[len ++] = 'd'; - break; - case 2: - szValue[len ++] = 't'; - break; - default: - szValue[len ++] = 'u'; - break; - } - } - /* valence */ - if ( OrigInfo[k].cUnusualValence ) { - if ( !OrigInfo[k].cRadical ) { - szValue[len ++] = '.'; - } - len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, OrigInfo[k].cUnusualValence ); - } - } - } else { - len = 0; - } - if ( len && nLen+len < nLen_szLinearCT ) { - strcpy( szLinearCT+nLen, szValue ); - nLen += len; - bNext ++; - } else - if ( len ) { - bOvfl = 1; - break; - } - } - *bOverflow |= bOvfl; - return nLen; -} - -/********************************************************************************************** - * nCtMode = 0: full - * 2: compressed CT - **********************************************************************************************/ -int MakeEquString( AT_NUMB *LinearCT, int nLenCT, int bAddDelim, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) -{ - /* produce output string; */ - int nLen = 0, len, i, k, bAbcNumbers; - int bOvfl = *bOverflow; - char szValue[16]; - int bNext=0; - bAbcNumbers = ( nCtMode & CT_MODE_ABC_NUMBERS ); - /* add connection table string */ - if ( !bOvfl && bAddDelim ) { - if ( nLen_szLinearCT > 2 ) { - strcpy( szLinearCT, ", " ); - nLen += 2; - } else { - bOvfl = 1; - } - } - for ( k = 0; !bOvfl && k < nLenCT && nLen < nLen_szLinearCT; k ++ ) { - /* find the first equivalence number */ - if ( k != (int)LinearCT[k] - 1 ) - continue; - for ( i = k; i < nLenCT && nLen < nLen_szLinearCT; i ++ ) { - if ( k != (int)LinearCT[i]-1 ) - continue; - /* equivalence number: a minimal canon_number out of a group of equivalent atoms */ - /* is at canon_number-1 position of each equivalent atom. */ - if ( bAbcNumbers ) { - len = MakeAbcNumber( szValue, (int)sizeof(szValue), (i==k && bNext)? ITEM_DELIMETER : NULL, i+1 ); - } else { - len = MakeDecNumber( szValue, (int)sizeof(szValue), (i==k)? "(":ITEM_DELIMETER, i+1 ); - } - if ( 0 <= len && nLen+len < nLen_szLinearCT ) { - strcpy( szLinearCT+nLen, szValue ); - nLen += len; - bNext ++; - } else - if ( 0 > len ) { - bOvfl = 1; - break; - } - } - if ( !bOvfl && !bAbcNumbers ) { - if ( nLen + 2 < nLen_szLinearCT ) { - strcpy( szLinearCT+nLen, ")" ); - nLen ++; - } else { - bOvfl = 1; - } - } - } - *bOverflow |= bOvfl; - return nLen; -} -/********************************************************************************************** - * nCtMode = 0: full - * 2: compressed CT - **********************************************************************************************/ -int MakeIsoAtomString( INChI_IsotopicAtom *IsotopicAtom, int nNumberOfIsotopicAtoms, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) -{ - /* produce output string; */ - int nLen = 0, len, tot_len, ret, i, j, bOvfl = *bOverflow; - char szValue[64]; - char *p; - int nValue; - int bAbcNumbers = (nCtMode & CT_MODE_ABC_NUMBERS ); - static const char letter[] = "itdh"; - static const char *h[] = {"T", "D", "H"}; - static const char *sign[] = {"-", "+"}; - - if ( !bOvfl ) { - for ( i = 0; i < nNumberOfIsotopicAtoms && nLen < nLen_szLinearCT; i ++ ) { - p = szValue; - tot_len = 0; - for ( j = 0; j < 5; j ++ ) { - len = 0; - switch( j ) { - case 0: - nValue = (int)IsotopicAtom[i].nAtomNumber; - break; - case 1: - nValue = (int)IsotopicAtom[i].nIsoDifference; - break; - case 2: - nValue = (int)IsotopicAtom[i].nNum_T; - break; - case 3: - nValue = (int)IsotopicAtom[i].nNum_D; - break; - case 4: - nValue = (int)IsotopicAtom[i].nNum_H; - break; - } - if ( !j ) { - /* atom canonical number */ - len = (bAbcNumbers? MakeAbcNumber:MakeDecNumber) - ( p, (int)sizeof(szValue)-tot_len, - bAbcNumbers?NULL:(i?ITEM_DELIMETER:EXTRA_SPACE), nValue - ); - } else - if ( bAbcNumbers ) { /* Abc output */ - switch ( j ) { - case 1: /* nIsoDifference */ - len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, NULL, nValue ); - break; - case 2: /* nNum_T */ - case 3: /* nNum_D */ - case 4: /* nNum_H */ - if ( nValue ) { - if ( (int)sizeof(szValue) - tot_len > 1 ) { - p[len++]=letter[j-1]; - if ( 1 == nValue ) { - p[len] = '\0'; - } else { - ret = MakeDecNumber( p+len, (int)sizeof(szValue)-tot_len-len, NULL, nValue ); - len = (ret >= 0)? len+ret : ret; - } - } else { - len = -1; /* overflow */ - } - } - } - } else - if ( nValue ) { - if ( j == 1 ) { /* Decimal output */ - /* signed isotopic mass difference */ - int subtract = (nValue > 0); - /* (n = mass difference) > 0 corresponds to nValue = n+1 */ - /* subtract 1 from it so that mass difference for 35Cl or 12C is zero */ - len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, sign[nValue>=0], abs(nValue-subtract) ); - } else { - /* hydrogen isotope */ - if ( nValue != 1 ) { - len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, h[j-2], nValue ); - } else - if ( (int)sizeof(szValue)-tot_len > 1 ) { - strcpy( p, h[j-2] ); - len = 1; - } else { - len = -1; /* overflow */ - } - } - } else { - continue; /* do not write zeroes */ - } - if ( len < 0 ) { - bOvfl = 1; - break; - } - tot_len += len; - p += len; - } - if ( nLen+tot_len < nLen_szLinearCT ) { - memcpy( szLinearCT+nLen, szValue, tot_len+1 ); - nLen += tot_len; - } else { - bOvfl = 1; - break; - } - } - } - *bOverflow |= bOvfl; - return nLen; -} -/**********************************************************************************************/ -int MakeIsoTautString( INChI_IsotopicTGroup *IsotopicTGroup, int nNumberOfIsotopicTGroups, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) -{ - /* produce output string; */ - int nLen = 0, len, tot_len, i, j, bOvfl = *bOverflow; - AT_NUMB nMax; - char szValue[32]; - char *p; - int nValue; - int bAbcNumbers = ( nCtMode & CT_MODE_ABC_NUMBERS ); - static const char letter[] = "tdh"; - static const char *h[] = {"T", "D", "H"}; - /* add connection table string */ - nMax = 0; - if ( !bOvfl ) { - for ( i = 0; i < nNumberOfIsotopicTGroups && nLen < nLen_szLinearCT; i ++ ) { - p = szValue; - tot_len = 0; - for ( j = 0; j < 4; j ++ ) { - switch( j ) { - case 0: - nValue = (int)IsotopicTGroup[i].nTGroupNumber; - break; - case 1: - nValue = (int)IsotopicTGroup[i].nNum_T; - break; - case 2: - nValue = (int)IsotopicTGroup[i].nNum_D; - break; - case 3: - nValue = (int)IsotopicTGroup[i].nNum_H; - break; - } - if ( !j ) { - /* atom canonical number */ - len = (bAbcNumbers?MakeAbcNumber:MakeDecNumber) - ( p, (int)sizeof(szValue)-tot_len, - bAbcNumbers?NULL:(i?ITEM_DELIMETER:EXTRA_SPACE), - nValue - ); - } else - if ( nValue ) { - if ( bAbcNumbers ) { - len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, NULL, nValue ); - if ( len > 0 ) { /* make sure overflow has not happened */ - if ( (int)sizeof(szValue)-tot_len-len > 1 ) { - p[len++]=letter[j-1]; - p[len] = '\0'; - } else { - len = -1; /* overflow */ - } - } - } else { - /* hydrogen isotope */ - if ( nValue != 1 ) { - len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, h[j-1], nValue ); - } else - if ( (int)sizeof(szValue)-tot_len > 1 ) { - strcpy( p, h[j-1] ); - len = 1; - } else { - len = -1; /* overflow */ - } - } - } else { - continue; /* do not write zeroes */ - } - if ( len < 0 ) { - bOvfl = 1; - break; - } - p += len; - tot_len += len; - } - if ( nLen+tot_len < nLen_szLinearCT ) { - memcpy( szLinearCT+nLen, szValue, tot_len+1 ); - nLen += tot_len; - } else { - bOvfl = 1; - break; - } - } - } - *bOverflow |= bOvfl; - return nLen; -} -/**********************************************************************************************/ -int MakeIsoHString( int num_iso_H[], char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) -{ - /* produce output string; */ - int nLen = 0, len, tot_len, j, bOvfl = *bOverflow; - AT_NUMB nMax; - char szValue[32]; - char *p; - int nValue; - int bAbcNumbers = ( nCtMode & CT_MODE_ABC_NUMBERS ); - static const char letter[] = "tdh"; - static const char *h[] = {"T", "D", "H"}; - /* add connection table string */ - nMax = 0; - if ( !bOvfl ) { - p = szValue; - tot_len = 0; - for ( j = 1; j < 4; j ++ ) { - nValue = num_iso_H[NUM_H_ISOTOPES-j];/* j: 1=>T, 2=>D, 3=>1H */ - if ( nValue ) { - if ( bAbcNumbers ) { - len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, NULL, nValue ); - if ( len > 0 ) { /* make sure overflow has not happened */ - if ( (int)sizeof(szValue)-tot_len-len > 1 ) { - p[len++]=letter[j-1]; - p[len] = '\0'; - } else { - len = -1; /* overflow */ - } - } - } else { - /* hydrogen isotope */ - if ( nValue != 1 ) { - len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, h[j-1], nValue ); - } else - if ( (int)sizeof(szValue)-tot_len > 1 ) { - strcpy( p, h[j-1] ); - len = 1; - } else { - len = -1; /* overflow */ - } - } - } else { - continue; /* do not write zeroes */ - } - if ( len < 0 ) { - bOvfl = 1; - break; - } - p += len; - tot_len += len; - } - if ( nLen+tot_len < nLen_szLinearCT ) { - memcpy( szLinearCT+nLen, szValue, tot_len+1 ); - nLen += tot_len; - } else { - bOvfl = 1; - } - } - *bOverflow |= bOvfl; - return nLen; -} -/**********************************************************************************************/ -int MakeStereoString( AT_NUMB *at1, AT_NUMB *at2, S_CHAR *parity, int bAddDelim, int nLenCT, - char *szLinearCT, int nLen_szLinearCT, int nCtMode, int *bOverflow) -{ - /* produce output string; */ - int nLen = 0, len, tot_len, i, j, bOvfl = *bOverflow; - char szValue[32]; - char *p; - int nValue; - static const char parity_char[] = "!-+u?"; - bAddDelim = 0; - if ( !bOvfl ) { - for ( i = 0; i < nLenCT && nLen < nLen_szLinearCT; i ++ ) { - p = szValue; - tot_len = 0; - for ( j = 0; j < 3; j ++ ) { - if ( j == 0 && at1 ) - nValue = (int)at1[i]; - else - if ( j == 1 && at2 ) - nValue = (int)at2[i]; - else - if ( j == 2 && parity ) - nValue = (int)parity[i]; - else - continue; - if ( nCtMode & CT_MODE_ABC_NUMBERS ) { - len = (j==2? MakeDecNumber : MakeAbcNumber)( p, (int)sizeof(szValue)-tot_len, NULL, nValue ); - } else { - if ( j < 2 ) { - len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, tot_len?"-":(i||bAddDelim)?ITEM_DELIMETER:NULL, nValue ); - } else - if ( tot_len + 1 < (int)sizeof(szValue) ) { - *p ++ = (0<=nValue && nValue<=4)? parity_char[nValue]:parity_char[0]; - *p = '\0'; - len = 1; - } else { - len = -1; /* Overflow */ - } - } - if ( len < 0 ) { - bOvfl = 1; - break; - } - p += len; - tot_len += len; - } - if ( nLen+tot_len < nLen_szLinearCT ) { - memcpy( szLinearCT+nLen, szValue, tot_len+1 ); - nLen += tot_len; - } else { - bOvfl = 1; - break; - } - } - } - *bOverflow |= bOvfl; - return nLen; -} -#ifdef ALPHA_BASE - -#if ( ALPHA_BASE != 27 ) -#error ALPHA_BASE definitions mismatch -#endif - -#else - -#define ALPHA_BASE 27 - -#endif - -#define ALPHA_MINUS '-' -#define ALPHA_ZERO_VAL '.' -#define ALPHA_ONE 'a' -#define ALPHA_ZERO '@' -/**********************************************************************************************/ -/* Produce an "Alphabetic" number, base 27 (27 digits: 0, a, b, ..., z) */ -/* The leading "digit" uppercase, the rest -- lowercase */ -/* szString length nStringLen includes 1 byte for zero termination */ -/* Return Value: length without zero termination; -1 means not enough room */ -/* Note: ASCII-encoding specific implementation */ -int MakeAbcNumber( char *szString, int nStringLen, const char *szLeadingDelim, int nValue ) -{ - char *p = szString; - char *q; - int nChar; - - if ( nStringLen < 2 ) - return -1; - while ( szLeadingDelim && *szLeadingDelim && --nStringLen ) { - *p ++ = *szLeadingDelim ++; - } - if ( nStringLen < 2 ) - return -1; - if ( !nValue ) { - *p++ = ALPHA_ZERO_VAL; /* zero value (cannot use 0) */ - *p = '\0'; - return 1; - } - if ( nValue < 0 ) { - *p++ = ALPHA_MINUS; - nStringLen --; - nValue = -nValue; - } - for ( q = p; nValue && --nStringLen; nValue /= ALPHA_BASE ) { - if ( nChar = nValue % ALPHA_BASE ) { - nChar = ALPHA_ONE + nChar - 1; - } else { - nChar = ALPHA_ZERO; - } - *q++ = nChar; - } - if ( nStringLen <= 0 ) - return -1; - *q = '\0'; - mystrrev( p ); - p[0] = toupper(p[0]); - return (q - szString); -} -#if ( READ_INCHI_STRING == 1 ) -/*****************************************************/ -static long abctol( const char *szString, char **q ); /* keep compiler happy */ - -long abctol( const char *szString, char **q ) -{ -#define __MYTOLOWER(c) ( ((c) >= 'A') && ((c) <= 'Z') ? ((c) - 'A' + 'a') : (c) ) - - long val = 0; - long sign = 1; - const char *p = szString; - if ( *p == ALPHA_MINUS ) { - p ++; - sign = -1; - } - if ( *p == ALPHA_ZERO ) { - p ++; - goto exit_function; - } - if ( !isupper(UCINT *p) ) { - p = szString; - goto exit_function; /* not an abc-number */ - } - val = __MYTOLOWER(*p) - ALPHA_ONE + 1; - p ++; - while ( *p ) { - if ( islower( UCINT *p ) ) { - val *= ALPHA_BASE; - val += *p - ALPHA_ONE + 1; - } else - if ( *p == ALPHA_ZERO ) { - val *= ALPHA_BASE; - } else { - break; - } - p ++; - } -exit_function: - if ( q ) { - *q = (char *)p; /* cast deliberately discards const qualifier */ - } - return val; -#undef __MYTOLOWER -} -/********************************************************/ -long inchi_strtol( const char *str, const char **p, int base) -{ - if ( base == ALPHA_BASE ) { - return abctol( str, (char **)p ); /* cast deliberately discards const qualifier */ - } else { - return strtol( str, (char **)p, base ); /* cast deliberately discards const qualifier */ - } -} -#endif -#undef ALPHA_BASE -#undef ALPHA_MINUS -#undef ALPHA_ZERO_VAL -#undef ALPHA_ONE -#undef ALPHA_ZERO - -/********************************************************/ -double inchi_strtod( const char *str, const char **p ) -{ - return strtod( str, (char **)p ); -} - -/**********************************************************************************************/ -/* Produce a decimal number */ -/* szString length nStringLen includes 1 byte for zero termination */ -/* Return Value: length without zero termination; -1 means not enough room */ -int MakeDecNumber( char *szString, int nStringLen, const char *szLeadingDelim, int nValue ) -{ -#define DECIMAL_BASE 10 -#define DECIMAL_MINUS '-' -#define DECIMAL_ZERO_VAL '0' -#define DECIMAL_ONE '1' -#define DECIMAL_ZERO '0' - char *p = szString; - char *q; - int nChar; - - if ( nStringLen < 2 ) - return -1; - while ( szLeadingDelim && *szLeadingDelim && --nStringLen ) { - *p ++ = *szLeadingDelim ++; - } - if ( nStringLen < 2 ) - return -1; - if ( !nValue ) { - *p++ = DECIMAL_ZERO_VAL; /* zero value (cannot use 0) */ - *p = '\0'; - return p-szString; - } - if ( nValue < 0 ) { - *p++ = DECIMAL_MINUS; - nStringLen --; - nValue = -nValue; - } - for ( q = p; nValue && --nStringLen; nValue /= DECIMAL_BASE ) { - if ( nChar = nValue % DECIMAL_BASE ) { - nChar = DECIMAL_ONE + nChar - 1; - } else { - nChar = DECIMAL_ZERO; - } - *q++ = nChar; - } - if ( nStringLen <= 0 ) - return -1; - *q = '\0'; - mystrrev( p ); - return (q - szString); -#undef DECIMAL_BASE -#undef DECIMAL_MINUS -#undef DECIMAL_ZERO_VAL -#undef DECIMAL_ONE -#undef DECIMAL_ZERO -} +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include +#include + +#include "mode.h" +#include "ichimain.h" +#include "ichimake.h" +#include "ichi_io.h" + + +/* + Eql_INChI_Stereo(...) +*/ +int Eql_INChI_Stereo( INChI_Stereo *s1, int eql1, INChI_Stereo *s2, int eql2, int bRelRac ) +{ + int inv1=0, inv2=0, len; + + if ( !s1 ) { + return 0; + } +#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) +#else + bRelRac = 0; +#endif + + if( EQL_SP2 == eql1 ) { + if ( (len=s1->nNumberOfStereoBonds) > 0 && s1->b_parity && s1->nBondAtom1 && s1->nBondAtom2 ) { + if ( !s2 ) { + if ( EQL_EXISTS == eql2 ) { + /* find whether double bond stereo exists*/ + return 1; + } + return 0; + } + if ( EQL_SP2 == eql2 && + len == s2->nNumberOfStereoBonds && s2->b_parity && s2->nBondAtom1 && s2->nBondAtom2 && + !memcmp( s1->nBondAtom1, s2->nBondAtom1, len * sizeof(s1->nBondAtom1[0])) && + !memcmp( s1->nBondAtom2, s2->nBondAtom2, len * sizeof(s1->nBondAtom2[0])) && + !memcmp( s1->b_parity, s2->b_parity, len * sizeof(s1->b_parity[0])) ) { + return 1; + } + } + return 0; + } else + if ( (eql1 == EQL_SP3 || (inv1 = (eql1 == EQL_SP3_INV))) && + (len=s1->nNumberOfStereoCenters) > (bRelRac? 1 : 0) ) { + + S_CHAR *t_parity1, *t_parity2; + AT_NUMB *nNumber1, *nNumber2; + if ( inv1 ) { + if ( s1->nCompInv2Abs ) { + t_parity1 = s1->t_parityInv; + nNumber1 = s1->nNumberInv; + } else { + return 0; + } + } else { + t_parity1 = s1->t_parity; + nNumber1 = s1->nNumber; + } + + if ( t_parity1 && nNumber1 ) { + if ( !s2 ) { + if ( EQL_EXISTS == eql2 && (!inv1 || s1->nCompInv2Abs) ) { + /* the 1st sp3 (inverted if requested) stereo exists*/ + return 1; + } + return 0; /* both sp3 do not exist */ + } + if( (eql2 == EQL_SP3 || (inv2 = (eql2 == EQL_SP3_INV))) && + len == s2->nNumberOfStereoCenters ) { + if ( inv2 ) { + if ( s2->nCompInv2Abs && s1->nCompInv2Abs ) { + t_parity2 = s2->t_parityInv; + nNumber2 = s2->nNumberInv; + } else { + /* if one sp3 is inverted then another should have non-trivial inverted stereo */ + return 0; + } + } else { + if ( inv1 && !s2->nCompInv2Abs ) { + /* if one sp3 is inverted then another should have non-trivial inverted stereo */ + return 0; + } + t_parity2 = s2->t_parity; + nNumber2 = s2->nNumber; + } + if ( t_parity2 && nNumber2 ) { + if ( inv1 ^ inv2 ) { + int i, num_inv; + for ( i = 0, num_inv = 0; i < len; i ++ ) { + if ( nNumber1[i] != nNumber2[i] ) + break; + if ( ATOM_PARITY_WELL_DEF(t_parity1[i]) && + ATOM_PARITY_WELL_DEF(t_parity2[i]) ) { + if ( 3 == t_parity1[i] + t_parity2[i] ) { + num_inv ++; + } else { + break; + } + } else + if ( t_parity1[i] != t_parity2[i] ) { + break; + } + } + return (len == i && num_inv > 0); + } else { + return !memcmp( t_parity1, t_parity2, len*sizeof(t_parity1[0])) && + !memcmp( nNumber1, nNumber2, len*sizeof(nNumber1[0])); + } + } + } + } + } + return 0; +} + + +/* + Eql_INChI_Isotopic(...) +*/ +int Eql_INChI_Isotopic( INChI *i1, INChI *i2 ) +{ + int eq = i1 && i2 && !i1->bDeleted && !i2->bDeleted && + ( i1->nNumberOfIsotopicAtoms > 0 || i1->nNumberOfIsotopicTGroups > 0 ) && + i1->nNumberOfIsotopicAtoms == i2->nNumberOfIsotopicAtoms && + i1->nNumberOfIsotopicTGroups == i2->nNumberOfIsotopicTGroups && + ( !i1->nNumberOfIsotopicAtoms || + i1->IsotopicAtom && i2->IsotopicAtom && + !memcmp( i1->IsotopicAtom, i2->IsotopicAtom, + i1->nNumberOfIsotopicAtoms * sizeof(i1->IsotopicAtom[0]) ) ) && + ( !i1->nNumberOfIsotopicTGroups || + i1->IsotopicTGroup && i2->IsotopicTGroup && + !memcmp( i1->IsotopicTGroup, i2->IsotopicTGroup, + i1->nNumberOfIsotopicTGroups * sizeof(i1->IsotopicAtom[0]) ) ); + return eq; +} + + +/* + Eql_INChI_Aux_Equ(...) +*/ +int Eql_INChI_Aux_Equ( INChI_Aux *a1, int eql1, INChI_Aux *a2, int eql2 ) +{ + int t1=0, t2=0, len=0; + AT_NUMB *n1=NULL, *n2=NULL; + if ( !a1 || !a2 ) { + return 0; + } + t1 = (eql1 & EQL_EQU_TG); + t2 = (eql2 & EQL_EQU_TG); + if ( t1 && t2 ) { + if ( (len = a1->nNumberOfTGroups) > 0 && len == a2->nNumberOfTGroups && !a1->bDeleted && !a2->bDeleted ) { + if (eql1 & EQL_EQU_ISO) { + if ( a1->bIsIsotopic ) { + n1 = a1->nConstitEquIsotopicTGroupNumbers; + } + } else { + n1 = a1->nConstitEquTGroupNumbers; + } + if (eql2 & EQL_EQU_ISO) { + if ( a2->bIsIsotopic ) { + n2 = a2->nConstitEquIsotopicTGroupNumbers; + } + } else { + n2 = a2->nConstitEquTGroupNumbers; + } + } + } else + if ( !t1 && !t2 ) { + if ( (len = a1->nNumberOfAtoms) > 0 && len == a2->nNumberOfAtoms && !a1->bDeleted && !a2->bDeleted ) { + if (eql1 & EQL_EQU_ISO) { + if ( a1->bIsIsotopic ) { + n1 = a1->nConstitEquIsotopicNumbers; + } + } else { + n1 = a1->nConstitEquNumbers; + } + if (eql2 & EQL_EQU_ISO) { + if ( a2->bIsIsotopic ) { + n2 = a2->nConstitEquIsotopicNumbers; + } + } else { + n2 = a2->nConstitEquNumbers; + } + } + } + if ( n1 && n2 && !memcmp(n1, n2, len*sizeof(n1[0])) && bHasEquString( n1, len) ) { + return 1; + } + return 0; +} + + +/* + Eql_INChI_Aux_Num +*/ +int Eql_INChI_Aux_Num( INChI_Aux *a1, int eql1, INChI_Aux *a2, int eql2 ) +{ + int len; + AT_NUMB *n1=NULL, *n2=NULL; + if ( !a1 || !a2 ) { + return 0; + } + if ( (len = a1->nNumberOfAtoms) <= 0 || len != a2->nNumberOfAtoms || a1->bDeleted || a2->bDeleted ) { + return 0; + } + if ( (eql1 & EQL_NUM_ISO) && !a1->bIsIsotopic || + (eql2 & EQL_NUM_ISO) && !a2->bIsIsotopic ) { + return 0; + } + + switch ( eql1 ) { + case EQL_NUM: + n1 = a1->nOrigAtNosInCanonOrd; + break; + case EQL_NUM_ISO: + n1 = a1->nIsotopicOrigAtNosInCanonOrd; + break; + case EQL_NUM_INV: + n1 = a1->nOrigAtNosInCanonOrdInv; + break; + case ( EQL_NUM_INV | EQL_NUM_ISO ): + n1 = a1->nIsotopicOrigAtNosInCanonOrdInv; + break; + default: + return 0; + } + + switch ( eql2 ) { + case EQL_NUM: + n2 = a2->nOrigAtNosInCanonOrd; + break; + case EQL_NUM_ISO: + n2 = a2->nIsotopicOrigAtNosInCanonOrd; + break; + case EQL_NUM_INV: + n2 = a2->nOrigAtNosInCanonOrdInv; + break; + case ( EQL_NUM_INV | EQL_NUM_ISO ): + n2 = a2->nIsotopicOrigAtNosInCanonOrdInv; + break; + default: + return 0; + } + + if ( n1 && n2 && !memcmp( n1, n2, len*sizeof(n1[0])) ) { + return 1; + } + return 0; +} + + +/* + bHasOrigInfo +*/ +int bHasOrigInfo( ORIG_INFO *OrigInfo, int num_atoms ) +{ + int i, bFound = 0; + if ( OrigInfo && num_atoms > 0 ) { + for ( i = 0; !bFound && i < num_atoms; i ++ ) { + bFound |= (0 != OrigInfo[i].cCharge) || + (0 != OrigInfo[i].cRadical) || + (0 != OrigInfo[i].cUnusualValence); + } + } + return bFound; +} + + +/* + EqlOrigInfo +*/ +int EqlOrigInfo( INChI_Aux *a1, INChI_Aux *a2 ) +{ + int ret = a1 && a2 && a1->nNumberOfAtoms == a2->nNumberOfAtoms && + bHasOrigInfo( a1->OrigInfo, a1->nNumberOfAtoms ) && a2->OrigInfo && + !memcmp( a1->OrigInfo, a2->OrigInfo, a1->nNumberOfAtoms * sizeof(a1->OrigInfo[0]) ); + return ret; +} + + +/* + bHasEquString +*/ +int bHasEquString( AT_NUMB *LinearCT, int nLenCT ) +{ + /* produce output string; */ + int i, k; + if ( !LinearCT ) + return 0; + for ( k = 0; k < nLenCT; k ++ ) { + /* find the first equivalence number */ + if ( k != (int)LinearCT[k] - 1 ) + continue; + for ( i = k; i < nLenCT; i ++ ) { + if ( k != (int)LinearCT[i]-1 ) + continue; + if ( k < i ) { + return 1; + } + } + } + return 0; +} + + +/* + MakeMult +*/ +int MakeMult( int mult, + const char *szTailingDelim, + INCHI_IOSTREAM_STRING *buf, + int nCtMode, + int *bOverflow) +{ + char szValue[2048]; + int len=0, len_delim, n; + + if ( mult == 1 || *bOverflow ) + return 0; + if ( nCtMode & CT_MODE_ABC_NUMBERS ) + { + len += MakeAbcNumber( szValue, (int)sizeof(szValue), NULL, mult ); + } + else + { + len += MakeDecNumber( szValue, (int)sizeof(szValue), NULL, mult ); + } + len_delim = (int) strlen(szTailingDelim); + + if ( len + len_delim < (int)sizeof(szValue) ) + { + strcpy( szValue+len, szTailingDelim ); + + n = inchi_strbuf_printf( buf, "%s", szValue ); + if ( -1==n ) *bOverflow |= 1; + return n; + /* + len += len_delim; + if ( len < nLen_szLinearCT ) + { + strcpy( szLinearCT, szValue ); + return len; + }*/ + } + + *bOverflow |= 1; + + return 0; +} + + +/* + MakeDelim +*/ +int MakeDelim( const char *szTailingDelim, + INCHI_IOSTREAM_STRING *buf, + int *bOverflow) +{ + int n; + if ( !szTailingDelim || !*szTailingDelim || *bOverflow ) + return 0; + + n = inchi_strbuf_printf( buf, szTailingDelim ); + if ( -1==n ) *bOverflow |= 1; + + return n; + /* + len_delim = (int) strlen(szTailingDelim); + if ( len_delim < nLen_szLinearCT ) { + strcpy( szLinearCT, szTailingDelim ); + return len_delim; + } + *bOverflow |= 1; + return 0; + */ +} + + +/* + MakeEqStr +*/ +int MakeEqStr( const char *szTailingDelim, + int mult, + INCHI_IOSTREAM_STRING *buf, + int *bOverflow) +{ + int n=0, n0; + char szValue[2048]; + if ( !szTailingDelim || !*szTailingDelim || *bOverflow ) + return 0; + + n0 = buf->nUsedLength; + + if ( mult != 1 ) + { + n = MakeDecNumber( szValue, (int)sizeof(szValue), NULL, mult ); + if ( -1==n ) + { + *bOverflow |= 1; + return -1; + } + } + if ( n>0 ) + { + n = inchi_strbuf_printf( buf, "%-s", szValue ); + if ( -1==n ) *bOverflow |= 1; + } + n = inchi_strbuf_printf( buf, "%-s", szTailingDelim ); + if ( -1==n ) *bOverflow |= 1; + + return (buf->nUsedLength - n0); +} + + +/* + Prepare InChI atomic numbers substring (connections, for InChI) + nCtMode = 0: full + 1: censored CT (no orphans) + 2: compressed CT (Abs numbers) + */ +int MakeCtStringNew( CANON_GLOBALS *pCG, + AT_NUMB *LinearCT, + int nLenCT, + int bAddDelim, + S_CHAR *nNum_H, + int num_atoms, + INCHI_IOSTREAM_STRING *strbuf, + int nCtMode, + int *bOverflow) +{ + /* produce output string; */ + int nUsedLength0=0, nLen=0, len, i, bOvfl = *bOverflow; + char szValue[2048]; + int nValue, nDelim, num_H; + AT_NUMB *nDfsOrderCT = NULL; + int bNoNum_H = (NULL == nNum_H); + int nNumRingClosures; + int bAbcNumbers = (0 != ( nCtMode & CT_MODE_ABC_NUMBERS )); + int bPredecessors = (0 != ( nCtMode & CT_MODE_PREDECESSORS )); + int bCountRingClosures = bAbcNumbers && bPredecessors && (nCtMode & CT_MODE_ABC_NUM_CLOSURES); + if ( nLenCT <= 1 ) + { + return 0; /* no atoms or a single atom: no connection table */ + } + + nUsedLength0 = strbuf->nUsedLength; + + /* make array containing connection string data */ + if ( !(nDfsOrderCT = GetDfsOrder4CT( pCG, LinearCT, nLenCT, + nNum_H, num_atoms, nCtMode ) ) ) + { + (*bOverflow) ++; + return 0; + } + + /* add connection table string */ + if ( !bOvfl && bAddDelim ) + { + inchi_strbuf_printf( strbuf, "," ); + } + + if ( !bOvfl ) + { + nNumRingClosures = 0; + for ( i = 0; nDfsOrderCT[i]/* && nLen < nLen_szLinearCT*/; i += 3 ) + { + nValue = (nDfsOrderCT[i] > MAX_ATOMS)? 0 : nDfsOrderCT[i]; + num_H = nDfsOrderCT[i+1]? nDfsOrderCT[i+1]-16:0; + nDelim = nDfsOrderCT[i+2]; + len = 0; + /* delimiter */ + if ( bPredecessors ) + { + if ( bCountRingClosures ) + { + if ( nDelim == '-' && i > 3 && bNoNum_H ) + { + if ( !nNumRingClosures ) + { + int j; + for ( j = i; nDfsOrderCT[j] && '-' == nDfsOrderCT[j+2]; j += 3 ) { + nNumRingClosures ++; + } + if ( nNumRingClosures ) + { + len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, nNumRingClosures ); + } + nNumRingClosures --; + } else + { + nNumRingClosures --; + } + } else + { + nNumRingClosures = 0; + } + } + else if ( nDelim && !( bAbcNumbers && nDelim == ',' ) ) + { + if ( nNum_H || i > 3 ) + { + szValue[len ++] = nDelim; + } + } + } + else + { + if ( nDelim && !( bAbcNumbers && nDelim == '-' ) ) + { + szValue[len ++] = nDelim; + } + } + if ( bAbcNumbers ) + { + if ( nValue || i ) + { + /* the 1st value may be zero in case of presdecessor list */ + len += MakeAbcNumber( szValue+len, (int)sizeof(szValue)-len, NULL, nValue ); + } + if ( num_H ) + { + len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, num_H ); + } + } + else + { + if ( nValue || i ) + { + /* the 1st value may be zero in case of presdecessor list */ + len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, nValue ); + } + if ( num_H ) + { + szValue[len] = 'H'; + len ++; + if ( num_H > 1 ) + { + len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, num_H ); + } + } + } + + if ( len>0 ) + { + inchi_strbuf_printf( strbuf, "%s", szValue ); + } + } + } + + *bOverflow |= bOvfl; + if ( nDfsOrderCT ) + inchi_free( nDfsOrderCT ); + + nLen = strbuf->nUsedLength - nUsedLength0; + + return nLen; +} + + +/* + Prepare InChI atomic numbers substring (for AuxInfo) + + nCtMode = 0: full + 1: censored CT (no orphans) + 2: compressed CT (Abs numbers) + */ +int MakeCtStringOld( AT_NUMB *LinearCT, + int nLenCT, + int bAddDelim, + INCHI_IOSTREAM_STRING *strbuf, + int nCtMode, + int *bOverflow) +{ + /* produce output string; */ + int nUsedLength0=0, nLen=0, len, i, bLessThanPrev, bOvfl = *bOverflow; + AT_NUMB nMax = 0; + char szValue[2048]; + int nValue, bNext = 0; + + nUsedLength0 = strbuf->nUsedLength; + + /* add connection table string */ + if ( !( nCtMode & CT_MODE_ABC_NUMBERS ) && !bOvfl && bAddDelim ) + { + inchi_strbuf_printf( strbuf, "," ); + /* + if ( nLen_szLinearCT > 1 ) { + strcpy( szLinearCT, "," ); + nLen ++; + } else { + bOvfl = 1; + }*/ + } + if ( !bOvfl ) { + for ( i = 0; i < nLenCT/* && nLen < nLen_szLinearCT*/; i ++ ) + { + bLessThanPrev = 0; + if ( !(nCtMode & CT_MODE_NO_ORPHANS) || ((bLessThanPrev=LinearCT[i] < nMax) || + i+1 < nLenCT && LinearCT[i+1] < (nMax=LinearCT[i])) ) + { + nValue = LinearCT[i]; + if ( nCtMode & CT_MODE_ABC_NUMBERS ) + { + len = MakeAbcNumber( szValue, (int)sizeof(szValue), (!bNext && bAddDelim)? ITEM_DELIMETER : NULL, nValue ); + } else + if ( nCtMode & CT_MODE_NO_ORPHANS ) + { + /* censored CT */ + /* output '-' as a delimiter to show a bonding for decimal output of the connection table */ + len = MakeDecNumber( szValue, (int)sizeof(szValue), bLessThanPrev? "-":ITEM_DELIMETER, nValue ); + } else + { + len = MakeDecNumber( szValue, (int)sizeof(szValue), i? ITEM_DELIMETER:NULL, nValue ); + } + if ( len>0 ) + { + inchi_strbuf_printf( strbuf, "%s", szValue ); + bNext ++; + } + /*if ( 0 <= len && nLen+len < nLen_szLinearCT) { + if ( len ) { + strcpy( szLinearCT+nLen, szValue ); + nLen += len; + bNext ++; + } + } else { + bOvfl = 1; + break; + }*/ + } + } + } + *bOverflow |= bOvfl; + + nLen = strbuf->nUsedLength - nUsedLength0; + return nLen; +} + + +/* + MakeHString( ... ) + + nCtMode = 0: decimal + 2: compressed CT (Abc numbers) + */ +int MakeHString( int bAddDelim, + S_CHAR *LinearCT, + int nLenCT, + INCHI_IOSTREAM_STRING *strbuf, + int nCtMode, int *bOverflow ) +{ +#define INIT_MIN_NUM_H (-4) +#define INIT_MAX_NUM_H 16 +#define INIT_LEN_NUM_H (INIT_MAX_NUM_H - INIT_MIN_NUM_H + 1) + + /* produce output string; */ + int nUsedLength0=0, nLen=0, len, i, iFirst, nVal, bOvfl = *bOverflow; + char szValue[2048]; + const char *pH; + int bNext = 0; + + nUsedLength0 = strbuf->nUsedLength; + + /* add connection table string */ + if ( !( nCtMode & CT_MODE_ABC_NUMBERS ) && !bOvfl && bAddDelim ) + { + inchi_strbuf_printf( strbuf, "," ); + /*if ( nLen_szLinearCT > 1 ) { + strcpy( szLinearCT, "," ); + nLen ++; + } else { + bOvfl = 1; + }*/ + } + if ( !bOvfl && 0 < nLenCT && LinearCT ) { + if ( nCtMode & CT_MODE_EQL_H_TOGETHER ) { + int curMinH = INIT_MIN_NUM_H; + int curMaxH = INIT_MAX_NUM_H; + int curLenH = INIT_LEN_NUM_H; + int nInitNumH[INIT_LEN_NUM_H]; + int *nNumH = nInitNumH; + int numAt, curNumH; + int j, bOutOfRange, tot_num_no_H; + /* count atoms H */ + do { + bOutOfRange = 0; + tot_num_no_H = 0; /* number of atoms that have no H */ + memset( nNumH, 0, curLenH*sizeof(nNumH[0]) ); + for ( i = 0; i < nLenCT; i ++ ) { + curNumH = LinearCT[i]; + if ( curNumH < curMinH ) { + curMinH = curNumH; + bOutOfRange ++; + } else + if ( curNumH > curMaxH ) { + curMaxH = curNumH; + bOutOfRange ++; + } else + if ( !bOutOfRange ) { + nNumH[curNumH-curMinH] ++; + } + tot_num_no_H += !curNumH; + } + if ( tot_num_no_H == nLenCT ) { + return nLen; /* empty string */ + } + if ( bOutOfRange ) { + /* for debug only */ + if ( nNumH != nInitNumH ) { + *bOverflow |= 1; + inchi_free( nNumH ); + return nLen; + } + /* end debug */ + curLenH = curMaxH - curMinH + 1; + nNumH = (int*) inchi_malloc( curLenH * sizeof(nNumH[0]) ); + if ( !nNumH ) { + *bOverflow |= 1; + return nLen; + } + } + } while ( bOutOfRange ); /* the loop may be executed 1 or 2 times only */ + + for ( curNumH = curMinH; curNumH <= curMaxH; curNumH ++ ) { + numAt = nNumH[curNumH-curMinH]; /* number of atoms that have curNumH atoms H */ + if ( !numAt || !curNumH ) { + continue; /* no atom has this number of H or number of H = 0 */ + } + j = 0; + while ( j < nLenCT && numAt ) { + if ( curNumH == LinearCT[j] ) { + iFirst = ++j; + numAt --; + for ( ; j < nLenCT && curNumH == LinearCT[j] && numAt; j ++ ) { + numAt --; + } + if ( nCtMode & CT_MODE_ABC_NUMBERS ) { + len = MakeAbcNumber( szValue, (int)sizeof(szValue), NULL, iFirst ); + } else { + len = MakeDecNumber( szValue, (int)sizeof(szValue), bNext?ITEM_DELIMETER:NULL, iFirst ); + bNext ++; /* add a delimiter (comma) before all except the first */ + } + if ( iFirst < j ) { + /* output last canonical number */ + if ( nCtMode & CT_MODE_ABC_NUMBERS ) { + len += MakeAbcNumber( szValue+len, (int)sizeof(szValue), NULL, j ); + } else { + len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, "-", j ); + } + } + if ( !numAt || ( nCtMode & CT_MODE_ABC_NUMBERS ) ) { + /* add number of H */ + /* output number of H */ + nVal = curNumH; + if ( nCtMode & CT_MODE_ABC_NUMBERS ) { + len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, nVal ); + } else { + pH = nVal > 0? "H":"h"; + nVal = abs(nVal); + if ( nVal > 1 ) { + len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, pH, nVal ); + } else { + strcpy( szValue+len, pH ); + len ++; + } + } + } + /* add to the output */ + if ( len > 0 ) + { + inchi_strbuf_printf( strbuf, "%-s", szValue ); + bNext ++; + } + /* + if ( 0 <= len && nLen+len < nLen_szLinearCT ) { + if ( len ) { + strcpy( szLinearCT+nLen, szValue ); + nLen += len; + bNext ++; + } + } else { + bOvfl = 1; + break; + }*/ + } + else + { + j ++; + } + } + } + if ( nNumH != nInitNumH ) { + inchi_free( nNumH ); + } + } + else + { + iFirst = 0; + for ( i = iFirst+1; i <= nLenCT/* && nLen < nLen_szLinearCT*/; i ++ ) { + if ( i < nLenCT && LinearCT[i] == LinearCT[iFirst] ) { + continue; + } + /* output identical values located at i = iFirst..i-1 */ + if ( LinearCT[iFirst] ) { /* output only non-zero values */ + /* first canonical number */ + nVal = LinearCT[iFirst]; + iFirst ++; + if ( nCtMode & CT_MODE_ABC_NUMBERS ) { + len = MakeAbcNumber( szValue, (int)sizeof(szValue), NULL, iFirst ); + } else { + len = MakeDecNumber( szValue, (int)sizeof(szValue), bNext?ITEM_DELIMETER:NULL, iFirst ); + } + if ( iFirst < i ) { + /* output last canonical number */ + if ( nCtMode & CT_MODE_ABC_NUMBERS ) { + len += MakeAbcNumber( szValue+len, (int)sizeof(szValue), NULL, i ); + } else { + len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, "-", i ); + } + } + /* output number of H */ + if ( nCtMode & CT_MODE_ABC_NUMBERS ) { + len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, nVal ); + } else { + pH = nVal > 0? "H":"h"; + nVal = abs(nVal); + if ( nVal > 1 ) { + len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, pH, nVal ); + } else { + strcpy( szValue+len, pH ); + len ++; + } + } + if ( 0 <= len/* && nLen+len < nLen_szLinearCT*/ ) { + if ( len ) { + inchi_strbuf_printf( strbuf, "%-s", szValue ); + /*strcpy( szLinearCT+nLen, szValue ); + nLen += len;*/ + bNext ++; + } + } else { + bOvfl = 1; + break; + } + } + iFirst = i; + } + } + } + *bOverflow |= bOvfl; + + nLen = strbuf->nUsedLength - nUsedLength0; + return nLen; + +#undef INIT_MIN_NUM_H +#undef INIT_MAX_NUM_H +#undef INIT_LEN_NUM_H +} + + +/* + Prepare InChI atomic numbers substring + + nCtMode = 0: full + 1: censored CT (no orphans, that CT should have only atoms with neighbors) + 2: compressed CT (Abc numbers) + */ +int MakeCtString( CANON_GLOBALS *pCG, + AT_NUMB *LinearCT, int nLenCT, int bAddDelim, + S_CHAR *nNum_H, int num_atoms, /* both parameters are not used here */ + INCHI_IOSTREAM_STRING *strbuf, int nCtMode, int *bOverflow) +{ + + if ( !nNum_H || !(nCtMode & CT_MODE_NO_ORPHANS) ) + { + return MakeCtStringOld( LinearCT, nLenCT, bAddDelim, + strbuf, nCtMode, bOverflow); + } + else + { + return MakeCtStringNew( pCG, LinearCT, nLenCT, bAddDelim, + nNum_H, num_atoms, + strbuf, nCtMode, bOverflow); + } +} + + +/* + MakeTautString( ) + + nCtMode = 0: full: decimal-only, with parentheses around t-groups + 2: compressed CT: do not add comma before the output string if bAddDelim != 0 + do not add parentheses around t-groups + atom canon numbers an Abc + LinearCT format: + N = number of tautomeric groups + n = number of endpoints + 1 in a tautomeric group #1 + next INCHI_T_NUM_MOVABLE lines (any after the first non-zero): + h = number of hydrogen atoms in the tautomeric group + m = number of negative charges + ... (the rest of the INCHI_T_NUM_MOVABLE has not been established, ignore them) + c(1) = canonical number of the first atom in the t-group + ... + c(n-1) = canonical number of the last atom in the t-group + +*/ +int MakeTautString( AT_NUMB *LinearCT, + int nLenCT, + int bAddDelim, + INCHI_IOSTREAM_STRING *strbuf, + int nCtMode, + int *bOverflow) +{ + /* produce output string; */ + int nUsedLength0=0, nLen=0, len, i, bOvfl = *bOverflow; + char szValue[2048]; + const char *p; + int nValue, nGroupLen, iGroupOutputCount, bCompressed; + /* make tautomer string */ + if ( !nLenCT || !LinearCT || !*LinearCT ) { + return nLen; + } + + nUsedLength0 = strbuf->nUsedLength; + + bCompressed = ( nCtMode & CT_MODE_ABC_NUMBERS ); + if ( !bCompressed && !bOvfl && bAddDelim ) + { + inchi_strbuf_printf( strbuf, "%s", COMMA_EXTRA_SPACE); + /*if ( nLen_szLinearCT > 1+LEN_EXTRA_SPACE ) { + strcpy( szLinearCT, COMMA_EXTRA_SPACE); + nLen += 1+LEN_EXTRA_SPACE; + } else { + bOvfl = 1; + }*/ + } + LinearCT ++; /* bypass number of tautomeric groups */ + nLenCT --; + + if ( !bOvfl ) { + for ( i = nGroupLen = iGroupOutputCount = 0; i < nLenCT/* && nLen < nLen_szLinearCT*/; i ++ ) { + nValue = (int)LinearCT[i]; + if ( nGroupLen == iGroupOutputCount ) { + nGroupLen = nValue; + iGroupOutputCount = 0; + /* group delimiter (uncompressed) */ + if ( !bCompressed ) { + if ( !i ) { + strcpy( szValue, "(" ); + len = 1; + } else { + strcpy( szValue, ")(" ); + len = 2; + } + } else { + len = 0; + } + } else + if ( bCompressed && iGroupOutputCount >= INCHI_T_NUM_MOVABLE ) { + /* compressed canon number in Abc */ + len = MakeAbcNumber( szValue, (int)sizeof(szValue), NULL, nValue ); + iGroupOutputCount ++; + } else { + /* always output number of hydrogen atoms as a decimal */ + /* output leading space if: */ + /* (a) this is the first output value in compressed mode (i==1 && bCompressed) */ + /* (b) this is not the first output value in non-compressed mode ( iGroupOutputCount && !bCompressed) */ + if ( bCompressed ) { + p = NULL; + len = 0; + switch( iGroupOutputCount ) { + case 0: + len = MakeDecNumber( szValue, (int)sizeof(szValue), (i == 1)? ITEM_DELIMETER:NULL, nValue ); + break; + case 1: + p = "-"; + break; + case 2: + p = "+"; + break; + } + if ( p ) { + switch( nValue ) { + case 0: + len = 0; + break; + case 1: + strcpy(szValue, p); + len = (int) strlen(szValue); + break; + default: + len = MakeDecNumber( szValue, (int)sizeof(szValue), p, nValue ); + break; + } + } + } else { + if ( iGroupOutputCount >= INCHI_T_NUM_MOVABLE ) { + /* canonical number of the atom in the tautomeric group */ + len = MakeDecNumber( szValue, (int)sizeof(szValue), ITEM_DELIMETER, nValue ); + } else { + p = NULL; + len = 0; + if ( nValue ) { + switch( iGroupOutputCount ) { + case 0: + p = "H"; + break; + case 1: + p = "-"; + break; + case 2: + p = "+"; + break; + } + if ( p ) { + /* number of hydrogens */ + if ( nValue == 1 ) { + strcpy(szValue, p); + len = (int) strlen(szValue); + } else { + len = MakeDecNumber( szValue, (int)sizeof(szValue), p, nValue ); + } + } + } + } + } + iGroupOutputCount ++; + } + if ( len>0 ) + { + inchi_strbuf_printf( strbuf, "%s", szValue ); + } + /*if ( 0 <= len && nLen+len < nLen_szLinearCT ) { + if ( len ) { + strcpy( szLinearCT+nLen, szValue ); + nLen += len; + } + } else { + bOvfl = 1; + break; + }*/ + } + if ( !bOvfl && !bCompressed && i ) + { + inchi_strbuf_printf( strbuf, ")" ); + /*if ( nLen + 1 < nLen_szLinearCT ) { + strcpy( szLinearCT+nLen, ")" ); + nLen ++; + } else { + bOvfl = 1; + }*/ + } + } + *bOverflow |= bOvfl; + + nLen = strbuf->nUsedLength - nUsedLength0; + return nLen; +} + + +/* + MakeCRVString( ... ) + + nCtMode = 0: full + 2: compressed CT + 22+3s3: 22=canon. number; +3=charge; s=singlet (d=doublet, t=triplet, s is omitted if valence=0), 3 = valence + 22+3.3, (charge, valence) 22.3 (valence) 22t3 (triplet, valence) + Ab+3t4: Ab=canon. number; +3=charge or "." t=triplet (or s, d), 4=valence +*/ +int MakeCRVString( ORIG_INFO *OrigInfo, + int nLenCT, + int bAddDelim, + INCHI_IOSTREAM_STRING *strbuf, + int nCtMode, + int *bOverflow) +{ + /* produce output string; */ + int nUsedLength0=0, nLen=0, len, k, bAbcNumbers; + int bOvfl = *bOverflow; + char szValue[2048] = {0}; + int bNext=0; + bAbcNumbers = ( nCtMode & CT_MODE_ABC_NUMBERS ); + + nUsedLength0 = strbuf->nUsedLength; + + /* add connection table string */ + if ( !bOvfl && bAddDelim ) { + inchi_strbuf_printf( strbuf, ", " ); + /*if ( nLen_szLinearCT > 2 ) { + strcpy( szLinearCT, ", " ); + nLen += 2; + } else { + bOvfl = 1; + }*/ + } + for ( k = 0; !bOvfl && k < nLenCT/* && nLen < nLen_szLinearCT*/; k ++ ) { + /* find the next non-empty entry */ + if ( OrigInfo[k].cCharge || OrigInfo[k].cRadical || OrigInfo[k].cUnusualValence ) { + if ( bAbcNumbers ) { + /* + 3 items: Ad+3d4 (canon. numb=Ad, charge=+3, doublet, valence = 4 + 2 items: Ad.d4 Ad+3.4 Ad+3d + 1 item: Ad+3 Ad.d Ad4 + + dot output before radical: no charge, radical is present + dot before valence: charge is present, no radical, valence is present + */ + len = MakeAbcNumber( szValue, (int)sizeof(szValue), NULL, k+1 ); + + /* charge */ + if ( OrigInfo[k].cCharge ) { + if ( OrigInfo[k].cCharge > 0 ) { + len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, "+", OrigInfo[k].cCharge ); + } else { + len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, OrigInfo[k].cCharge ); + } + } + /* radical */ + if ( OrigInfo[k].cRadical ) { + if ( !OrigInfo[k].cCharge ) { + szValue[len ++] = '.'; + } + switch( OrigInfo[k].cRadical ) { + case 1: + szValue[len ++] = 'd'; + break; + case 2: + szValue[len ++] = 't'; + break; + default: + szValue[len ++] = 'u'; + break; + } + } + /* valence */ + if ( OrigInfo[k].cUnusualValence ) { + if ( OrigInfo[k].cCharge && !OrigInfo[k].cRadical ) { + szValue[len ++] = '.'; + } + len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, OrigInfo[k].cUnusualValence ); + } + } else { + /* + 3 items: 22+3d4 (canon. numb=22, charge=+3, doublet, valence = 4 + 2 items: 22d4 22+3.4 22+3d + 1 item: 22+3 22d 22.4 + + dot output before valence: + (a) charge, no radical, valence + (b) no charge, no radical, valence + that is, whenever valence is present and no radical + */ + len = MakeDecNumber( szValue, (int)sizeof(szValue), bNext? ITEM_DELIMETER:NULL, k+1 ); + /* charge */ + if ( OrigInfo[k].cCharge ) { + if ( OrigInfo[k].cCharge > 0 ) { + len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, "+", OrigInfo[k].cCharge ); + } else { + len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, OrigInfo[k].cCharge ); + } + } + /* radical */ + if ( OrigInfo[k].cRadical ) { + switch( OrigInfo[k].cRadical ) { + case 1: + szValue[len ++] = 'd'; + break; + case 2: + szValue[len ++] = 't'; + break; + default: + szValue[len ++] = 'u'; + break; + } + } + /* valence */ + if ( OrigInfo[k].cUnusualValence ) { + if ( !OrigInfo[k].cRadical ) { + szValue[len ++] = '.'; + } + len += MakeDecNumber( szValue+len, (int)sizeof(szValue)-len, NULL, OrigInfo[k].cUnusualValence ); + } + } + } else { + len = 0; + } + if ( len ) + { + szValue[len] = '\0'; + inchi_strbuf_printf( strbuf, "%s", szValue ); + bNext ++; + szValue[0] = '\0'; + } + /*if ( len && nLen+len < nLen_szLinearCT ) { + strcpy( szLinearCT+nLen, szValue ); + nLen += len; + bNext ++; + } else + if ( len ) { + bOvfl = 1; + break; + }*/ + } + *bOverflow |= bOvfl; + + nLen = strbuf->nUsedLength - nUsedLength0; + return nLen; +} + + +/* + MakeEquString( ... ) + +nCtMode = 0: full + 2: compressed CT +*/ +int MakeEquString( AT_NUMB *LinearCT, + int nLenCT, + int bAddDelim, + INCHI_IOSTREAM_STRING *strbuf, + int nCtMode, + int *bOverflow) +{ + /* produce output string; */ + int nUsedLength0=0, nLen=0, len, i, k, bAbcNumbers; + int bOvfl = *bOverflow; + char szValue[2048]; + int bNext=0; + + nUsedLength0 = strbuf->nUsedLength; + + bAbcNumbers = ( nCtMode & CT_MODE_ABC_NUMBERS ); + /* add connection table string */ + if ( !bOvfl && bAddDelim ) + { + inchi_strbuf_printf( strbuf, ", " ); + /*if ( nLen_szLinearCT > 2 ) { + strcpy( szLinearCT, ", " ); + nLen += 2; + } else { + bOvfl = 1; + }*/ + } + for ( k = 0; !bOvfl && k < nLenCT/* && nLen < nLen_szLinearCT*/; k ++ ) { + /* find the first equivalence number */ + if ( k != (int)LinearCT[k] - 1 ) + continue; + for ( i = k; i < nLenCT/* && nLen < nLen_szLinearCT*/; i ++ ) { + if ( k != (int)LinearCT[i]-1 ) + continue; + /* equivalence number: a minimal canon_number out of a group of equivalent atoms */ + /* is at canon_number-1 position of each equivalent atom. */ + if ( bAbcNumbers ) { + len = MakeAbcNumber( szValue, (int)sizeof(szValue), (i==k && bNext)? ITEM_DELIMETER : NULL, i+1 ); + } else { + len = MakeDecNumber( szValue, (int)sizeof(szValue), (i==k)? "(":ITEM_DELIMETER, i+1 ); + } + inchi_strbuf_printf( strbuf, "%s", szValue ); + bNext ++; + /*if ( 0 <= len && nLen+len < nLen_szLinearCT ) { + strcpy( szLinearCT+nLen, szValue ); + nLen += len; + bNext ++; + } else + if ( 0 > len ) { + bOvfl = 1; + break; + }*/ + } + inchi_strbuf_printf( strbuf, ")" ); + /*if ( !bOvfl && !bAbcNumbers ) { + if ( nLen + 2 < nLen_szLinearCT ) { + strcpy( szLinearCT+nLen, ")" ); + nLen ++; + } else { + bOvfl = 1; + } + }*/ + } + *bOverflow |= bOvfl; + + nLen = strbuf->nUsedLength - nUsedLength0; + return nLen; +} + + +/* + MakeIsoAtomString( ... ) + + nCtMode = 0: full + 2: compressed CT +*/ +int MakeIsoAtomString( INChI_IsotopicAtom *IsotopicAtom, int nNumberOfIsotopicAtoms, + INCHI_IOSTREAM_STRING *strbuf, int nCtMode, int *bOverflow) +{ + /* produce output string; */ + int nUsedLength0=0, nLen=0, len, tot_len, ret, i, j, bOvfl = *bOverflow; + char szValue[2048]; + char *p; + int nValue; + int bAbcNumbers = (nCtMode & CT_MODE_ABC_NUMBERS ); + static const char letter[] = "itdh"; + static const char *h[] = {"T", "D", "H"}; + static const char *sign[] = {"-", "+"}; + + nUsedLength0 = strbuf->nUsedLength; + + if ( !bOvfl ) { + for ( i = 0; i < nNumberOfIsotopicAtoms/* && nLen < nLen_szLinearCT*/; i ++ ) { + p = szValue; + tot_len = 0; + for ( j = 0; j < 5; j ++ ) { + len = 0; + switch( j ) { + case 0: + nValue = (int)IsotopicAtom[i].nAtomNumber; + break; + case 1: + nValue = (int)IsotopicAtom[i].nIsoDifference; + break; + case 2: + nValue = (int)IsotopicAtom[i].nNum_T; + break; + case 3: + nValue = (int)IsotopicAtom[i].nNum_D; + break; + case 4: + nValue = (int)IsotopicAtom[i].nNum_H; + break; + } + if ( !j ) { + /* atom canonical number */ + len = (bAbcNumbers? MakeAbcNumber:MakeDecNumber) + ( p, (int)sizeof(szValue)-tot_len, + bAbcNumbers?NULL:(i?ITEM_DELIMETER:EXTRA_SPACE), nValue + ); + } else + if ( bAbcNumbers ) { /* Abc output */ + switch ( j ) { + case 1: /* nIsoDifference */ + len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, NULL, nValue ); + break; + case 2: /* nNum_T */ + case 3: /* nNum_D */ + case 4: /* nNum_H */ + if ( nValue ) { + if ( (int)sizeof(szValue) - tot_len > 1 ) { + p[len++]=letter[j-1]; + if ( 1 == nValue ) { + p[len] = '\0'; + } else { + ret = MakeDecNumber( p+len, (int)sizeof(szValue)-tot_len-len, NULL, nValue ); + len = (ret >= 0)? len+ret : ret; + } + } else { + len = -1; /* overflow */ + } + } + } + } else + if ( nValue ) { + if ( j == 1 ) { /* Decimal output */ + /* signed isotopic mass difference */ + int subtract = (nValue > 0); + /* (n = mass difference) > 0 corresponds to nValue = n+1 */ + /* subtract 1 from it so that mass difference for 35Cl or 12C is zero */ + len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, sign[nValue>=0], abs(nValue-subtract) ); + } else { + /* hydrogen isotope */ + if ( nValue != 1 ) { + len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, h[j-2], nValue ); + } else + if ( (int)sizeof(szValue)-tot_len > 1 ) { + strcpy( p, h[j-2] ); + len = 1; + } else { + len = -1; /* overflow */ + } + } + } else { + continue; /* do not write zeroes */ + } + if ( len < 0 ) { + bOvfl = 1; + break; + } + tot_len += len; + p += len; + } + inchi_strbuf_printf( strbuf, "%s", szValue ); + /*if ( nLen+tot_len < nLen_szLinearCT ) { + memcpy( szLinearCT+nLen, szValue, tot_len+1 ); + nLen += tot_len; + } else { + bOvfl = 1; + break; + }*/ + } + } + *bOverflow |= bOvfl; + + nLen = strbuf->nUsedLength - nUsedLength0; + return nLen; +} + + +/* + MakeIsoTautString( ... ) +*/ +int MakeIsoTautString( INChI_IsotopicTGroup *IsotopicTGroup, + int nNumberOfIsotopicTGroups, + INCHI_IOSTREAM_STRING *strbuf, + int nCtMode, + int *bOverflow) +{ + /* produce output string; */ + int nUsedLength0=0, nLen=0, len, tot_len, i, j, bOvfl = *bOverflow; + AT_NUMB nMax; + char szValue[2048]; + char *p; + int nValue; + int bAbcNumbers = ( nCtMode & CT_MODE_ABC_NUMBERS ); + static const char letter[] = "tdh"; + static const char *h[] = {"T", "D", "H"}; + + nUsedLength0 = strbuf->nUsedLength; + + /* add connection table string */ + nMax = 0; + if ( !bOvfl ) { + for ( i = 0; i < nNumberOfIsotopicTGroups/* && nLen < nLen_szLinearCT*/; i ++ ) { + p = szValue; + tot_len = 0; + for ( j = 0; j < 4; j ++ ) { + switch( j ) { + case 0: + nValue = (int)IsotopicTGroup[i].nTGroupNumber; + break; + case 1: + nValue = (int)IsotopicTGroup[i].nNum_T; + break; + case 2: + nValue = (int)IsotopicTGroup[i].nNum_D; + break; + case 3: + nValue = (int)IsotopicTGroup[i].nNum_H; + break; + } + if ( !j ) { + /* atom canonical number */ + len = (bAbcNumbers?MakeAbcNumber:MakeDecNumber) + ( p, (int)sizeof(szValue)-tot_len, + bAbcNumbers?NULL:(i?ITEM_DELIMETER:EXTRA_SPACE), + nValue + ); + } else + if ( nValue ) { + if ( bAbcNumbers ) { + len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, NULL, nValue ); + if ( len > 0 ) { /* make sure overflow has not happened */ + if ( (int)sizeof(szValue)-tot_len-len > 1 ) { + p[len++]=letter[j-1]; + p[len] = '\0'; + } else { + len = -1; /* overflow */ + } + } + } else { + /* hydrogen isotope */ + if ( nValue != 1 ) { + len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, h[j-1], nValue ); + } else + if ( (int)sizeof(szValue)-tot_len > 1 ) { + strcpy( p, h[j-1] ); + len = 1; + } else { + len = -1; /* overflow */ + } + } + } else { + continue; /* do not write zeroes */ + } + if ( len < 0 ) { + bOvfl = 1; + break; + } + p += len; + tot_len += len; + } + inchi_strbuf_printf( strbuf, "%s", szValue ); + /*if ( nLen+tot_len < nLen_szLinearCT ) { + memcpy( szLinearCT+nLen, szValue, tot_len+1 ); + nLen += tot_len; + } else { + bOvfl = 1; + break; + }*/ + } + } + *bOverflow |= bOvfl; + + nLen = strbuf->nUsedLength - nUsedLength0; + return nLen; +} + + +/* + MakeIsoHString( ... ) +*/ +int MakeIsoHString( int num_iso_H[], INCHI_IOSTREAM_STRING *strbuf, int nCtMode, int *bOverflow) +{ + /* produce output string; */ + int nUsedLength0=0, nLen=0, len, tot_len, j, bOvfl = *bOverflow; + AT_NUMB nMax; + char szValue[2048]; + char *p; + int nValue; + int bAbcNumbers = ( nCtMode & CT_MODE_ABC_NUMBERS ); + static const char letter[] = "tdh"; + static const char *h[] = {"T", "D", "H"}; + + nUsedLength0 = strbuf->nUsedLength; + + /* add connection table string */ + nMax = 0; + if ( !bOvfl ) { + p = szValue; + tot_len = 0; + for ( j = 1; j < 4; j ++ ) { + nValue = num_iso_H[NUM_H_ISOTOPES-j];/* j: 1=>T, 2=>D, 3=>1H */ + if ( nValue ) { + if ( bAbcNumbers ) { + len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, NULL, nValue ); + if ( len > 0 ) { /* make sure overflow has not happened */ + if ( (int)sizeof(szValue)-tot_len-len > 1 ) { + p[len++]=letter[j-1]; + p[len] = '\0'; + } else { + len = -1; /* overflow */ + } + } + } else { + /* hydrogen isotope */ + if ( nValue != 1 ) { + len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, h[j-1], nValue ); + } else + if ( (int)sizeof(szValue)-tot_len > 1 ) { + strcpy( p, h[j-1] ); + len = 1; + } else { + len = -1; /* overflow */ + } + } + } else { + continue; /* do not write zeroes */ + } + if ( len < 0 ) { + bOvfl = 1; + break; + } + p += len; + tot_len += len; + } + inchi_strbuf_printf( strbuf, "%s", szValue ); + /*if ( nLen+tot_len < nLen_szLinearCT ) { + memcpy( szLinearCT+nLen, szValue, tot_len+1 ); + nLen += tot_len; + } else { + bOvfl = 1; + }*/ + } + *bOverflow |= bOvfl; + + nLen = strbuf->nUsedLength - nUsedLength0; + return nLen; +} + + +/* + MakeStereoString( ... ) +*/ +int MakeStereoString( AT_NUMB *at1, + AT_NUMB *at2, + S_CHAR *parity, + int bAddDelim, + int nLenCT, + INCHI_IOSTREAM_STRING *strbuf, + int nCtMode, + int *bOverflow) +{ + /* produce output string; */ + int nUsedLength0=0, nLen=0, len, tot_len, i, j, bOvfl = *bOverflow; + char szValue[2048]; + char *p; + int nValue; + static const char parity_char[] = "!-+u?"; + + nUsedLength0 = strbuf->nUsedLength; + bAddDelim = 0; + + if ( !bOvfl ) { + for ( i = 0; i < nLenCT/* && nLen < nLen_szLinearCT*/; i ++ ) { + p = szValue; + tot_len = 0; + for ( j = 0; j < 3; j ++ ) { + if ( j == 0 && at1 ) + nValue = (int)at1[i]; + else + if ( j == 1 && at2 ) + nValue = (int)at2[i]; + else + if ( j == 2 && parity ) + nValue = (int)parity[i]; + else + continue; + if ( nCtMode & CT_MODE_ABC_NUMBERS ) { + len = (j==2? MakeDecNumber : MakeAbcNumber)( p, (int)sizeof(szValue)-tot_len, NULL, nValue ); + } else { + if ( j < 2 ) { + len = MakeDecNumber( p, (int)sizeof(szValue)-tot_len, tot_len?"-":(i||bAddDelim)?ITEM_DELIMETER:NULL, nValue ); + } else + if ( tot_len + 1 < (int)sizeof(szValue) ) { + *p ++ = (0<=nValue && nValue<=4)? parity_char[nValue]:parity_char[0]; + *p = '\0'; + len = 1; + } else { + len = -1; /* Overflow */ + } + } + if ( len < 0 ) { + bOvfl = 1; + break; + } + p += len; + tot_len += len; + } + + inchi_strbuf_printf( strbuf, "%s", szValue ); + /* + if ( nLen+tot_len < nLen_szLinearCT ) { + memcpy( szLinearCT+nLen, szValue, tot_len+1 ); + nLen += tot_len; + } else { + bOvfl = 1; + break; + }*/ + } + } + *bOverflow |= bOvfl; + + nLen = strbuf->nUsedLength - nUsedLength0; + return nLen; +} +#ifdef ALPHA_BASE +#if ( ALPHA_BASE != 27 ) +#error ALPHA_BASE definitions mismatch +#endif +#else +#define ALPHA_BASE 27 +#endif + +#define ALPHA_MINUS '-' +#define ALPHA_ZERO_VAL '.' +#define ALPHA_ONE 'a' +#define ALPHA_ZERO '@' + + +/* + MakeAbcNumber( ... ) + + Produce an "Alphabetic" number, base 27 (27 digits: 0, a, b, ..., z) + The leading "digit" uppercase, the rest -- lowercase + szString length nStringLen includes 1 byte for zero termination + Return Value: length without zero termination; -1 means not enough room + Note: ASCII-encoding specific implementation +*/ +int MakeAbcNumber( char *szString, int nStringLen, const char *szLeadingDelim, int nValue ) +{ + char *p = szString; + char *q; + int nChar; + + if ( nStringLen < 2 ) + return -1; + while ( szLeadingDelim && *szLeadingDelim && --nStringLen ) { + *p ++ = *szLeadingDelim ++; + } + if ( nStringLen < 2 ) + return -1; + if ( !nValue ) { + *p++ = ALPHA_ZERO_VAL; /* zero value (cannot use 0) */ + *p = '\0'; + return 1; + } + if ( nValue < 0 ) { + *p++ = ALPHA_MINUS; + nStringLen --; + nValue = -nValue; + } + for ( q = p; nValue && --nStringLen; nValue /= ALPHA_BASE ) { + if ( nChar = nValue % ALPHA_BASE ) { + nChar = ALPHA_ONE + nChar - 1; + } else { + nChar = ALPHA_ZERO; + } + *q++ = nChar; + } + if ( nStringLen <= 0 ) + return -1; + *q = '\0'; + mystrrev( p ); + p[0] = toupper(p[0]); + return (int) (q - szString); +} + + +#if ( READ_INCHI_STRING == 1 ) + + +/* + abctol( ... ) +*/ +static long abctol( const char *szString, char **q ); + /* keep compiler happy */ +long abctol( const char *szString, char **q ) +{ +#define __MYTOLOWER(c) ( ((c) >= 'A') && ((c) <= 'Z') ? ((c) - 'A' + 'a') : (c) ) + + long val = 0; + long sign = 1; + const char *p = szString; + if ( *p == ALPHA_MINUS ) { + p ++; + sign = -1; + } + if ( *p == ALPHA_ZERO ) { + p ++; + goto exit_function; + } + if ( !isupper(UCINT *p) ) { + p = szString; + goto exit_function; /* not an abc-number */ + } + val = __MYTOLOWER(*p) - ALPHA_ONE + 1; + p ++; + while ( *p ) { + if ( islower( UCINT *p ) ) { + val *= ALPHA_BASE; + val += *p - ALPHA_ONE + 1; + } else + if ( *p == ALPHA_ZERO ) { + val *= ALPHA_BASE; + } else { + break; + } + p ++; + } +exit_function: + if ( q ) { + *q = (char *)p; /* cast deliberately discards const qualifier */ + } + return val; +#undef __MYTOLOWER +} + + +/* + inchi_strtol( ... ) +*/ +long inchi_strtol( const char *str, const char **p, int base) +{ + if ( base == ALPHA_BASE ) { + return abctol( str, (char **)p ); /* cast deliberately discards const qualifier */ + } else { + return strtol( str, (char **)p, base ); /* cast deliberately discards const qualifier */ + } +} +#endif /* #if ( READ_INCHI_STRING == 1 ) */ + +#undef ALPHA_BASE +#undef ALPHA_MINUS +#undef ALPHA_ZERO_VAL +#undef ALPHA_ONE +#undef ALPHA_ZERO + + +/* + inchi_strtod( ... ) +*/ +double inchi_strtod( const char *str, const char **p ) +{ + return strtod( str, (char **)p ); +} + + +/* + MakeDecNumber( ... ) + + Produce a decimal number + + szString length nStringLen includes 1 byte for zero termination + Return Value: length without zero termination; -1 means not enough room +*/ +int MakeDecNumber( char *szString, + int nStringLen, + const char *szLeadingDelim, + int nValue ) +{ +#define DECIMAL_BASE 10 +#define DECIMAL_MINUS '-' +#define DECIMAL_ZERO_VAL '0' +#define DECIMAL_ONE '1' +#define DECIMAL_ZERO '0' + + char *p = szString; + char *q; + int nChar; + + if ( nStringLen < 2 ) + return -1; + while ( szLeadingDelim && *szLeadingDelim && --nStringLen ) { + *p ++ = *szLeadingDelim ++; + } + if ( nStringLen < 2 ) + return -1; + if ( !nValue ) { + *p++ = DECIMAL_ZERO_VAL; /* zero value (cannot use 0) */ + *p = '\0'; + return (int) (p-szString); + } + if ( nValue < 0 ) { + *p++ = DECIMAL_MINUS; + nStringLen --; + nValue = -nValue; + } + for ( q = p; nValue && --nStringLen; nValue /= DECIMAL_BASE ) { + if ( nChar = nValue % DECIMAL_BASE ) { + nChar = DECIMAL_ONE + nChar - 1; + } else { + nChar = DECIMAL_ZERO; + } + *q++ = nChar; + } + if ( nStringLen <= 0 ) + return -1; + *q = '\0'; + mystrrev( p ); + + return (int) (q - szString); + +#undef DECIMAL_BASE +#undef DECIMAL_MINUS +#undef DECIMAL_ZERO_VAL +#undef DECIMAL_ONE +#undef DECIMAL_ZERO +} + + +/* + Get canonical numbers and numbers-of-components for each original atom + + NB: cano_nums[ orig_num ] are in _orig_nums order + compnt_nums [cano_num] are in cano_nums order +*/ +int get_canonical_atom_numbers_and_component_numbers( CANON_GLOBALS *pCG, + INCHI_IOSTREAM_STRING *strbuf, + OutputINCHI_CtlData *io, + int nat, + int *cano_nums, + int *compnt_nums ) +{ +int orig_num, cano_num, icompnt, i, k, ndigit, err; +char c, cnum[8]; + + if ( NULL == cano_nums ) return 1; + if ( NULL == compnt_nums ) return 1; + if ( NULL == strbuf->pStr ) return 1; + + inchi_strbuf_reset( strbuf ); + + io->tot_len = str_AuxNumb( pCG, io->pINChISort, io->pINChISort2, + strbuf, &io->bOverflow, + io->bOutType, io->TAUT_MODE, io->num_components, + io->bSecondNonTautPass, io->bOmitRepetitions); + + for (i=0; i nUsedLength; k++) + { + c = strbuf->pStr[k]; + if ( c==',' || c==';' || c=='\0' ) + { + cnum[ndigit] = '\0'; + orig_num = atoi(cnum); + cano_nums[orig_num] = cano_num; /* from 0 yet */ + compnt_nums[cano_num] = icompnt; /* compnt_nums[orig_num] = icompnt; */ + cnum[0] = '\0'; + ndigit = 0; + cano_num++; + if ( c==';' ) icompnt++; + if ( c=='\0' ) break; + continue; + } + else if ( isdigit(c) ) + { + cnum[ndigit] = c; + ndigit++; + } + else + { + err = 2; + goto exitf; + } + } + +exitf: + inchi_strbuf_reset( strbuf ); + return err; +} + + +/* + Print out num[0..n-1] separated with commas + For shortness, comress consequtive numbers (>2 members in a series) to range: + '1,2,5, 7-10' means 1,2,5,7,8,9,10' +*/ +void print_sequence_of_nums_compressing_ranges( int n, int *num, INCHI_IOSTREAM_STRING *strbuf ) +{ + int k, range; + + range = 0; + for (k=0; k -#include -#include -#include - -#include "mode.h" - -#include "inpdef.h" -#include "ichi.h" -#include "strutil.h" -#include "util.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "ichicant.h" -#include "ichicano.h" -#include "ichicomn.h" - -#include "ichicomp.h" -#include "ichimain.h" -#include "ichimake.h" - - -/***************************************************************************/ -int str_Sp2(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) -{ - int i, ii, ii2; - INCHI_SORT *is, *is2, *is0, *is20; - INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; - INChI_Stereo *Stereo, *Stereo_Prev, *Stereo_Taut, *Stereo_Taut_Prev; - int mult, eq2prev, eq2taut, eq2tautPrev, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - - pINChI_Taut = NULL; - pINChI_Prev = NULL; - pINChI_Taut_Prev = NULL; - mult = 0; - bNext = 0; - is = NULL; - is2 = NULL; - is0 = pINChISort; - is20 = bSecondNonTautPass? pINChISort2 : NULL; - eq2taut = 0; /* may be non-zero only on the 2nd (non-taut) pass */ - eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; - for ( i = 0; i <= num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; - /*================ compare sp2 to previous =====================*/ - if ( bSecondNonTautPass ) { - /* component that was output on the 1st pass */ - pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; - } - /*========= if bSecondNonTautPass then compare non-iso non-taut stereo to non-iso taut ========*/ - eq2taut = 0; -#if ( FIX_EMPTY_LAYER_BUG == 1 ) - if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions && pINChI && pINChI_Taut ) { - Stereo = pINChI->Stereo; - Stereo_Taut = pINChI_Taut->Stereo; - eq2taut = Stereo && Stereo_Taut && - Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Taut, EQL_SP2, 0 ); - eq2taut = eq2taut? (iiSTEREO | iitNONTAUT) : 0; - - if ( !eq2taut && - !Eql_INChI_Stereo( Stereo, EQL_SP2, NULL, EQL_EXISTS, 0 ) && - Eql_INChI_Stereo( Stereo_Taut, EQL_SP2, NULL, EQL_EXISTS, 0 ) ) { - eq2taut = iiEmpty; /* the current is empty while the preceding (taut) is not */ - } - } -#else - if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions ) { - eq2taut = pINChI && pINChI_Taut && - (Stereo = pINChI->Stereo) && (Stereo_Taut = pINChI_Taut->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Taut, EQL_SP2, 0 ); - eq2taut = eq2taut? (iiSTEREO | iitNONTAUT) : 0; - } -#endif - if ( eq2taut ) { - /* we may be here only in case of the second (non-taut) pass */ - /* current non-taut stereo has been found to be same as tautomeric */ - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - /* previous component exists; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( (Stereo_Prev = pINChI_Prev->Stereo) && Stereo_Prev->nNumberOfStereoBonds > 0 ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - - tot_len += MakeStereoString( Stereo_Prev->nBondAtom1, Stereo_Prev->nBondAtom2, - Stereo_Prev->b_parity, - 0, Stereo_Prev->nNumberOfStereoBonds, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - } else - if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - /* previous non-taut component exists only in taut list */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - } - /* we have found pINChI->Stereo sp2 same as in pINChI_Taut */ - /* output this (current) equivalence as '*', that is, same as tautomeric */ - /* that was printed on the 1st pass. */ - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output the previous one */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ - pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ - mult = 0; - eq2tautPrev = 1; /* pINChI_Prev sp2 does not exist */ - } else - if ( eq2tautPrev ) { - /* at this point pINChI_Prev does not exist; however, pINChI */ - /*might have been discovered and it is different from pINChI_Taut */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - eq2tautPrev = 0; - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; - } else { - /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp2 */ - eq2prev =bUseMulipliers && - pINChI && pINChI_Prev && - (Stereo = pINChI->Stereo) && (Stereo_Prev = pINChI_Prev->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Prev, EQL_SP2, 0 ); - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - /* pINChI sp2 info is either different or trivial. Output pINChI_Prev anyway */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - if ( (Stereo_Prev = pINChI_Prev->Stereo) && Stereo_Prev->nNumberOfStereoBonds > 0 ) { - /* pINChI_Prev exists and has sp2 info */ - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - - tot_len += MakeStereoString( Stereo_Prev->nBondAtom1, Stereo_Prev->nBondAtom2, - Stereo_Prev->b_parity, - 0, Stereo_Prev->nNumberOfStereoBonds, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - /* else sp2 info is not present in pINChI_Prev */ - } else - if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - if ( (Stereo_Taut_Prev = pINChI_Taut_Prev->Stereo) && Stereo_Taut_Prev->nNumberOfStereoBonds > 0 ) { - /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ - /* and it has non-trivial sp2 info */ - /* - tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); - */ - ;/* pINChI_Taut_Prev sp2 info was output in the main stereo section */ - } else { - ; /* pINChI_Taut_Prev exists and has not sp2 info */ - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; /* */ - } -#endif - } - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; /* we do not know whether the item is empty */ - } - } - return tot_len; -} - -/***************************************************************************/ -int str_Sp3(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bRelRac, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) -{ - int i, ii, ii2; - INCHI_SORT *is, *is2, *is0, *is20; - INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; - INChI_Stereo *Stereo, *Stereo_Prev, *Stereo_Taut, *Stereo_Taut_Prev; - int mult, eq2prev, eq2taut, eq2tautPrev, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - pINChI_Taut = NULL; - pINChI_Prev = NULL; - pINChI_Taut_Prev = NULL; - mult = 0; - bNext = 0; - is = NULL; - is2 = NULL; - is0 = pINChISort; - is20 = bSecondNonTautPass? pINChISort2 : NULL; - eq2taut = 0; /* may be non-zero only on the 2nd (non-taut) pass */ - eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) -#else - bRelRac = 0; -#endif - for ( i = 0; i <= num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; - /*================ compare sp3 to previous =====================*/ - if ( bSecondNonTautPass ) { - /* component that was output on the 1st pass */ - pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; - } - /*========= if bSecondNonTautPass then compare non-iso non-taut stereo to non-iso taut ========*/ - eq2taut = 0; -#if ( FIX_EMPTY_LAYER_BUG == 1 ) - if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions && pINChI && pINChI_Taut ) { - Stereo = pINChI->Stereo; - Stereo_Taut = pINChI_Taut->Stereo; - eq2taut = Stereo && Stereo_Taut && - Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Taut, EQL_SP3, bRelRac ); - eq2taut = eq2taut? (iiSTEREO | iitNONTAUT) : 0; - if ( !eq2taut && - !Eql_INChI_Stereo( Stereo, EQL_SP3, NULL, EQL_EXISTS, 0 ) && - Eql_INChI_Stereo( Stereo_Taut, EQL_SP3, NULL, EQL_EXISTS, 0 ) ) { - eq2taut = iiEmpty; /* the current is empty while the preceding (taut) is not */ - } - } -#else - if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions ) { - eq2taut = pINChI && pINChI_Taut && - (Stereo = pINChI->Stereo) && (Stereo_Taut = pINChI_Taut->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Taut, EQL_SP3, bRelRac ); - eq2taut = eq2taut? (iiSTEREO | iitNONTAUT) : 0; - } -#endif - if ( eq2taut ) { - /* we may be here only in case of the second (non-taut) pass */ - /* current non-taut stereo has been found to be same as tautomeric */ - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - /* previous component exists; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( (Stereo_Prev = pINChI_Prev->Stereo) && Stereo_Prev->nNumberOfStereoCenters > 0 ) { - /* non-empty item */ - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - - tot_len += MakeStereoString( Stereo_Prev->nNumber, NULL, - Stereo_Prev->t_parity, - 0, Stereo_Prev->nNumberOfStereoCenters, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - } else - if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - /* previous non-taut component exists only in taut list */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - } - /* we have found pINChI->Stereo sp3 same as in pINChI_Taut */ - /* output this (current) equivalence as '*', that is, same as tautomeric */ - /* that was printed on the 1st pass. */ - - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - - pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ - pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ - mult = 0; - eq2tautPrev = 1; /* pINChI_Prev sp2 does not exist */ - } else - if ( eq2tautPrev ) { - /* at this point pINChI_Prev does not exist; however, pINChI */ - /*might have been discovered and it is different from pINChI_Taut */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - eq2tautPrev = 0; - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; - } else { - /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp3 */ - /*================ compare sp3 to previous =====================*/ - eq2prev =bUseMulipliers && - pINChI && pINChI_Prev && - (Stereo = pINChI->Stereo) && (Stereo_Prev = pINChI_Prev->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Prev, EQL_SP3, bRelRac ); - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - if ( (Stereo_Prev = pINChI_Prev->Stereo) && Stereo_Prev->nNumberOfStereoCenters > bRelRac ) { - /* pINChI_Prev exists and has sp3 info */ - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - - tot_len += MakeStereoString( Stereo_Prev->nNumber, NULL, Stereo_Prev->t_parity, - 0, Stereo_Prev->nNumberOfStereoCenters, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - /* else sp3 info is not present in pINChI_Prev */ - } else - if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - if ( (Stereo_Taut_Prev = pINChI_Taut_Prev->Stereo) && Stereo_Taut_Prev->nNumberOfStereoCenters > bRelRac ) { - /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ - /* and it has non-trivial sp3 info. This info has already been printed in the main section */ - /* - tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); - */ - ; /* pINChI_Taut_Prev sp3 info was output in the main stereo section */ - } else { - ; /* pINChI_Taut_Prev exists and has not sp3 info */ - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; /* */ - } -#endif - } - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; /* we do not know whether the item is empty */ - } - } - return tot_len; -} -/***************************************************************************/ -int str_IsoAtoms(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bAbcNumbers, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) -{ - int i, ii, ii2; - INCHI_SORT *is, *is2, *is0, *is20; - INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; - int mult, eq2prev, eq2taut, eq2tautPrev, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - pINChI_Taut = NULL; - pINChI_Prev = NULL; - pINChI_Taut_Prev = NULL; - mult = 0; - bNext = 0; - is = NULL; - is2 = NULL; - is0 = pINChISort; - is20 = bSecondNonTautPass? pINChISort2 : NULL; - eq2taut = 0; /* may be non-zero only on the 2nd (non-taut) pass */ - eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; - for ( i = 0; i <= num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; - /*================ compare isotopic info to previous component =====================*/ - if ( bSecondNonTautPass ) { - /* component that was output on the 1st pass */ - pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; - } - /*========= if bSecondNonTautPass then compare iso non-taut to taut non-iso ========*/ - eq2taut = 0; - if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions ) { - eq2taut = Eql_INChI_Isotopic( pINChI, pINChI_Taut ); - eq2taut = eq2taut? (iiNUMB | iitNONTAUT) : 0; - } - if ( eq2taut ) { - /* we may be here only in case of the second (non-taut) pass */ - /* current non-taut isotopic info has been found to be same as current tautomeric */ - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - /* previous component exists; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Prev && (pINChI_Prev->nNumberOfIsotopicAtoms > 0 || - pINChI_Prev->nNumberOfIsotopicTGroups > 0) ) { - /* non-empty item */ - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - /* Isotopic atoms */ - if ( pINChI_Prev->nNumberOfIsotopicAtoms > 0 && nStrLen-tot_len > 2 && !*bOverflow ) { /* dereferenced bOverflow 2004-06-07 */ - tot_len += MakeIsoAtomString( pINChI_Prev->IsotopicAtom, pINChI_Prev->nNumberOfIsotopicAtoms, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - /* Isotopic tautomeric groups */ - if ( pINChI_Prev->nNumberOfIsotopicTGroups > 0 && nStrLen-tot_len > 3 && !*bOverflow ) { - tot_len += MakeDelim( bAbcNumbers? ITEM_DELIMETER : "(", pStr + tot_len, nStrLen-tot_len, bOverflow); - tot_len += MakeIsoTautString( pINChI_Prev->IsotopicTGroup, pINChI_Prev->nNumberOfIsotopicTGroups, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - if ( !bAbcNumbers ) { - tot_len += MakeDelim( ")", pStr + tot_len, nStrLen-tot_len, bOverflow); - } - } - } - } else - if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - /* previous non-taut component exists only in taut list */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - } - /* we have found pINChI isotopic info to be same as in pINChI_Taut */ - /* output this (current) equivalence as '*', that is, same as tautomeric */ - /* that was printed on the 1st pass. */ - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - pINChI_Prev = NULL; /* pINChI_Prev isotopic info does not exist since */ - pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ - mult = 0; - eq2tautPrev = 1; /* pINChI_Prev isotopic info does not exist */ - } else - if ( eq2tautPrev ) { - /* at this point pINChI_Prev does not exist; however, pINChI */ - /* might have been discovered and it is different from pINChI_Taut */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - eq2tautPrev = 0; - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; - } else { - /*================ compare iso composition to previous =====================*/ - /* check whether pINChI and pINChI_Prev have non-zero identical isotopic info */ - eq2prev =bUseMulipliers && Eql_INChI_Isotopic( pINChI, pINChI_Prev ); - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - /* pINChI isotopic info is either different or empty. Output pINChI_Prev anyway */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - if ( (pINChI_Prev->nNumberOfIsotopicAtoms > 0 || - pINChI_Prev->nNumberOfIsotopicTGroups > 0) ) { - /* pINChI_Prev exists and has isotopic info */ - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - /* Isotopic atoms */ - if ( pINChI_Prev->nNumberOfIsotopicAtoms > 0 && nStrLen-tot_len > 2 && !*bOverflow ) { - tot_len += MakeIsoAtomString( pINChI_Prev->IsotopicAtom, pINChI_Prev->nNumberOfIsotopicAtoms, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - /* Isotopic tautomeric groups */ - if ( pINChI_Prev->nNumberOfIsotopicTGroups > 0 && nStrLen-tot_len > 3 && !*bOverflow ) { - tot_len += MakeDelim( bAbcNumbers? ITEM_DELIMETER : "(", pStr + tot_len, nStrLen-tot_len, bOverflow); - tot_len += MakeIsoTautString( pINChI_Prev->IsotopicTGroup, pINChI_Prev->nNumberOfIsotopicTGroups, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - if ( !bAbcNumbers ) { - tot_len += MakeDelim( ")", pStr + tot_len, nStrLen-tot_len, bOverflow); - } - } - } - /* else isotopic info is not present in pINChI_Prev */ - } else - if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - if ( (pINChI_Taut_Prev->nNumberOfIsotopicAtoms > 0 || - pINChI_Taut_Prev->nNumberOfIsotopicTGroups > 0) ) { - /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ - /* and it has non-trivial isotopic info */ - /* - tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); - */ - ;/* pINChI_Taut_Prev isotopic info was output in the main isotopic section */ - } else { - ; /* pINChI_Taut_Prev exists and has not isotopic info */ - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; /* */ - } -#endif - } - /* Fix17: moved here 2004-10-08 */ - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; /* we do not know whether the item is empty */ - } - /* Fix17: moved from here 2004-10-08 - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; - */ - } - return tot_len; -} -/***************************************************************************/ -int str_IsoSp2(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) -{ - int i, ii, ii2; - INCHI_SORT *is, *is2, *is0, *is20; - INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; - INChI_Stereo *Stereo, *Stereo_Prev, *Stereo_Taut, *Stereo_Taut_Prev; - int mult, eq2prev, eq2taut, eq2tautPrev, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - pINChI_Taut = NULL; - pINChI_Prev = NULL; - pINChI_Taut_Prev = NULL; - mult = 0; - bNext = 0; - is = NULL; - is2 = NULL; - is0 = pINChISort; - is20 = bSecondNonTautPass? pINChISort2 : NULL; - eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ - eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; - for ( i = 0; i <= num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; - /*================ compare sp2 to previous =====================*/ - if ( bSecondNonTautPass ) { - /* component that was output on the 1st pass */ - pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; - } - eq2taut = 0; - /*========= if bSecondNonTautPass then compare iso non-taut stereo to other stereo ========*/ - if ( bSecondNonTautPass && bOmitRepetitions ) { - /* compare non-tautomeric isotopic to: - * a) non-tautomeric non-isotopic - * b) tautomeric non-isotopic - * c) tautomeric isotopic - */ - /* a) compare non-tautomeric isotopic to non-tautomeric non-isotopic */ - if ( !eq2taut ) { - eq2taut = pINChI && - /* non-taut isotopic */ /* non-taut non-isotopic */ - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Taut, EQL_SP2, 0 ); - /* stereo isotopic non-taut = non-taut (stereo) */ - eq2taut = eq2taut? (iiSTEREO | iitISO | iitNONTAUT | iiEq2NONTAUT ) : 0; - } - /* b) compare non-tautomeric isotopic to tautomeric non-isotopic */ - if ( !eq2taut ) { - eq2taut = pINChI && pINChI_Taut && - /* non-taut isotopic */ /* taut non-isotopic */ - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Taut, EQL_SP2, 0 ); - /* stereo isotopic non-taut = taut (stereo) */ - eq2taut = eq2taut? (iiSTEREO | iitISO | iitNONTAUT ) : 0; - } - /* c) compare non-tautomeric isotopic to tautomeric isotopic */ - if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions ) { - eq2taut = pINChI && pINChI_Taut && - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Taut, EQL_SP2, 0 ); - /* stereo isotopic non-taut = isotopic taut (stereo) */ - eq2taut = eq2taut? (iiSTEREO | iitISO | iitNONTAUT | iiEq2ISO) : 0; - } -#if ( FIX_EMPTY_LAYER_BUG == 1 ) - if ( !eq2taut && pINChI && !((Stereo = pINChI->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP2, NULL, EQL_EXISTS, 0 )) ) { - /* component has no stereo; check whether it has stereo in the preceding layers */ - if ( pINChI_Taut && (Stereo_Taut = pINChI_Taut->Stereo) && /* F is not empty */ - Eql_INChI_Stereo( Stereo_Taut, EQL_SP2, NULL, EQL_EXISTS, 0 ) || - !(pINChI_Taut && (Stereo_Taut = pINChI_Taut->Stereo) && /* M is empty and ... */ - Eql_INChI_Stereo( Stereo_Taut, EQL_SP2, NULL, EQL_EXISTS, 0 )) && - (pINChI_Taut && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && /* ... MI is not empty */ - Eql_INChI_Stereo( Stereo_Taut, EQL_SP2, NULL, EQL_EXISTS, 0 )) ) { - - eq2taut = iiEmpty; /* the component has stereo in the preceding layer */ - } - } -#endif - } else - /*========= if not bSecondNonTautPass then compare iso taut stereo to non-iso taut ========*/ - if ( !bSecondNonTautPass && bOmitRepetitions ) { - /* compare tautomeric isotopic to tautomeric non-isotopic */ - if ( !eq2taut ) { - eq2taut = pINChI && - /* taut isotopic */ /* taut non-isotopic */ - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Taut, EQL_SP2, 0 ); - /* stereo isotopic taut = taut (stereo) */ - eq2taut = eq2taut? (iiSTEREO | iitISO ) : 0; -#if ( FIX_EMPTY_LAYER_BUG == 1 ) - if ( !eq2taut && pINChI && !((Stereo = pINChI->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP2, NULL, EQL_EXISTS, 0 ) ) ) { - /* component has no MI stereo; check whether it has stereo in the preceding layer M */ - if ( (Stereo_Taut = pINChI->Stereo) && - Eql_INChI_Stereo( Stereo_Taut, EQL_SP2, NULL, EQL_EXISTS, 0 ) ) { - eq2taut = iiEmpty; /* the component has stereo in the preceding layer */ - } - } -#endif - } - } - if ( eq2taut ) { - /* we may be here only in case of the current layer found equal in another layer the same component */ - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - /* previous component exists; output it before output the current component */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( (Stereo_Prev = pINChI_Prev->StereoIsotopic) && Stereo_Prev->nNumberOfStereoBonds > 0 ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - - tot_len += MakeStereoString( Stereo_Prev->nBondAtom1, Stereo_Prev->nBondAtom2, - Stereo_Prev->b_parity, - 0, Stereo_Prev->nNumberOfStereoBonds, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - } else - if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - /* previous non-taut component exists only in taut list */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - /* do not output stereo of non-tautomeric in non-taut layer: it has been output in the main layer */ - } - /* we have found another (previously printed) layer of the current component equal to this layer */ - /* output this (current) equivalence mark = EquString(eq2taut) */ - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ - pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ - mult = 0; - eq2tautPrev = 1; /* pINChI_Prev and pINChI_Taut_Prev have already been output */ - } else - if ( eq2tautPrev ) { - /* at this point pINChI_Prev does not exist; however, pINChI */ - /*might have been discovered and it is different from pINChI_Taut */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - eq2tautPrev = 0; - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; - } else { - /* current layer is different from previously printed layers of the current component */ - /* compare the current layer to this layer of the previous component: */ - /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp2 */ - /*================ compare iso sp2 to previous =====================*/ - eq2prev =bUseMulipliers && - pINChI && pINChI_Prev && - (Stereo = pINChI->StereoIsotopic) && (Stereo_Prev = pINChI_Prev->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Prev, EQL_SP2, 0 ); - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - /* the current layer is different from this layer of the previous component */ - /* therefore print the current layer */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - if ( (Stereo_Prev = pINChI_Prev->StereoIsotopic) && Stereo_Prev->nNumberOfStereoBonds > 0 ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - - tot_len += MakeStereoString( Stereo_Prev->nBondAtom1, Stereo_Prev->nBondAtom2, - Stereo_Prev->b_parity, - 0, Stereo_Prev->nNumberOfStereoBonds, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - /* else sp2 info is not present in pINChI_Prev */ - } else - /* do not print pINChI_Prev because it either do not exist of have already been printed */ - if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - if ( (Stereo_Taut_Prev = pINChI_Taut_Prev->StereoIsotopic) && Stereo_Taut_Prev->nNumberOfStereoBonds > 0 ) { - /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ - /* and it has non-trivial sp2 info */ - /* - tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); - */ - ;/* pINChI_Taut_Prev sp3 info was output in the main stereo section */ - } else { - ; /* pINChI_Taut_Prev exists and has not sp2 info */ - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; /* */ - } -#endif - } - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; /* we do not know whether the item is empty */ - } - } - return tot_len; -} -/******************************************************************************************/ -int str_IsoSp3(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bRelRac, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) -{ - int i, ii, ii2; - INCHI_SORT *is, *is2, *is0, *is20; - INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; - INChI_Stereo *Stereo, *Stereo_Prev, *Stereo_Taut, *Stereo_Taut_Prev; - int mult, eq2prev, eq2taut, eq2tautPrev, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - pINChI_Taut = NULL; - pINChI_Prev = NULL; - pINChI_Taut_Prev = NULL; - mult = 0; - bNext = 0; - is = NULL; - is2 = NULL; - is0 = pINChISort; - is20 = bSecondNonTautPass? pINChISort2 : NULL; - eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ - eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; -#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) -#else - bRelRac = 0; -#endif - for ( i = 0; i <= num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; - /*================ compare sp2 to previous =====================*/ - if ( bSecondNonTautPass ) { - /* component that was output on the 1st pass */ - pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; - } - eq2taut = 0; - /*========= if bSecondNonTautPass then compare iso non-taut stereo to other stereo ========*/ - if ( bSecondNonTautPass && bOmitRepetitions ) { - /* compare non-tautomeric isotopic to: - * a) non-tautomeric non-isotopic - * b) tautomeric non-isotopic - * c) tautomeric isotopic - */ - /* a) compare non-tautomeric isotopic to non-tautomeric non-isotopic */ - if ( !eq2taut ) { - eq2taut = pINChI && /* non-taut isotopic */ /* non-taut non-isotopic */ - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Taut, EQL_SP3, bRelRac ); - /* stereo isotopic non-taut = non-taut (stereo) */ - eq2taut = eq2taut? (iiSTEREO | iitISO | iitNONTAUT | iiEq2NONTAUT ) : 0; - } - /* b) compare non-tautomeric isotopic to tautomeric non-isotopic */ - if ( !eq2taut ) { - eq2taut = pINChI && pINChI_Taut && - /* non-taut isotopic */ /* taut non-isotopic */ - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Taut, EQL_SP3, bRelRac ); - /* stereo isotopic non-taut = taut (stereo) */ - eq2taut = eq2taut? (iiSTEREO | iitISO | iitNONTAUT ) : 0; - } - /* c) compare non-tautomeric isotopic to tautomeric isotopic */ - if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions ) { - eq2taut = pINChI && pINChI_Taut && - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Taut, EQL_SP3, bRelRac ); - /* stereo isotopic non-taut = isotopic taut (stereo) */ - eq2taut = eq2taut? (iiSTEREO | iitISO | iitNONTAUT | iiEq2ISO) : 0; - } -#if ( FIX_EMPTY_LAYER_BUG == 1 ) - if ( !eq2taut && pINChI && !((Stereo = pINChI->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP3, NULL, EQL_EXISTS, 0 )) ) { - /* component has no stereo; check whether it has stereo in the preceding layers */ - if ( pINChI_Taut && (Stereo_Taut = pINChI_Taut->Stereo) && /* F is not empty */ - Eql_INChI_Stereo( Stereo_Taut, EQL_SP3, NULL, EQL_EXISTS, 0 ) || - !(pINChI_Taut && (Stereo_Taut = pINChI_Taut->Stereo) && /* M is empty and ... */ - Eql_INChI_Stereo( Stereo_Taut, EQL_SP3, NULL, EQL_EXISTS, 0 )) && - (pINChI_Taut && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && /* ... MI is not empty */ - Eql_INChI_Stereo( Stereo_Taut, EQL_SP3, NULL, EQL_EXISTS, 0 )) ) { - - eq2taut = iiEmpty; /* the component has stereo in the preceding layer */ - } - } -#endif - } else - /*========= if not bSecondNonTautPass then compare iso taut stereo to non-iso taut ========*/ - if ( !bSecondNonTautPass && bOmitRepetitions ) { - /* compare tautomeric isotopic to tautomeric non-isotopic */ - if ( !eq2taut ) { - eq2taut = pINChI && - /* taut isotopic */ /* taut non-isotopic */ - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Taut, EQL_SP3, bRelRac ); - /* stereo isotopic taut = taut (stereo) */ - eq2taut = eq2taut? (iiSTEREO | iitISO ) : 0; -#if ( FIX_EMPTY_LAYER_BUG == 1 ) - if ( !eq2taut && pINChI && !((Stereo = pINChI->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP3, NULL, EQL_EXISTS, 0 ) ) ) { - /* component has no MI stereo; check whether it has stereo in the preceding layer M */ - if ( (Stereo_Taut = pINChI->Stereo) && - Eql_INChI_Stereo( Stereo_Taut, EQL_SP3, NULL, EQL_EXISTS, 0 ) ) { - eq2taut = iiEmpty; /* the component has stereo in the preceding layer */ - } - } -#endif - } - } - if ( eq2taut ) { - /* we may be here only in case of the current layer found equal in another layer the same component */ - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - /* previous component exists; output it before output the current component */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( (Stereo_Prev = pINChI_Prev->StereoIsotopic) && Stereo_Prev->nNumberOfStereoCenters > bRelRac ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - - tot_len += MakeStereoString( Stereo_Prev->nNumber, NULL, Stereo_Prev->t_parity, - 0, Stereo_Prev->nNumberOfStereoCenters, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - } else - if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - /* previous non-taut component exists only in taut list */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - /* do not output stereo of non-tautomeric in non-taut layer: it has been output in the main layer */ - } - /* we have found another (previously printed) layer of the current component equal to this layer */ - /* output this (current) equivalence mark = EquString(eq2taut) */ - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ - pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ - mult = 0; - eq2tautPrev = 1; /* pINChI_Prev and pINChI_Taut_Prev have already been output */ - } else - if ( eq2tautPrev ) { - /* at this point pINChI_Prev does not exist; however, pINChI */ - /*might have been discovered and it is different from pINChI_Taut */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - eq2tautPrev = 0; - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; - } else { - /* current layer is different from previously printed layers of the current component */ - /* compare the current layer to this layer of the previous component: */ - /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp2 */ - /*================ compare iso sp3 to previous =====================*/ - eq2prev =bUseMulipliers && pINChI && pINChI_Prev && - (Stereo = pINChI->StereoIsotopic) && (Stereo_Prev = pINChI_Prev->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Prev, EQL_SP3, bRelRac ); - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - if ( (Stereo_Prev = pINChI_Prev->StereoIsotopic) && Stereo_Prev->nNumberOfStereoCenters > bRelRac ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - - tot_len += MakeStereoString( Stereo_Prev->nNumber, NULL, Stereo_Prev->t_parity, - 0, Stereo_Prev->nNumberOfStereoCenters, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - /* else sp3 info is not present in pINChI_Prev */ - } else - /* do not print pINChI_Prev because it either do not exist of have already been printed */ - if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - if ( (Stereo_Taut_Prev = pINChI_Taut_Prev->StereoIsotopic) && Stereo_Taut_Prev->nNumberOfStereoCenters > bRelRac ) { - /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ - /* and it has non-trivial sp2 info */ - /* - tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); - */ - ;/* pINChI_Taut_Prev sp3 info was output in the main stereo section */ - } else { - ; /* pINChI_Taut_Prev exists and has not sp3 info */ - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; /* */ - } -#endif - } - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; /* we do not know whether the item is empty */ - } - } - return tot_len; -} -/***************************************************************************/ -int str_AuxEqu(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) -{ - int i, ii, ii2; - INCHI_SORT *is, *is2, *is0, *is20; - INChI_Aux *pINChI_Aux = NULL, *pINChI_Aux_Prev, *pINChI_Aux_Taut, *pINChI_Aux_Taut_Prev; - int mult, eq2prev, eq2taut, eq2tautPrev, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - pINChI_Aux_Prev = NULL; - pINChI_Aux_Taut = NULL; - pINChI_Aux_Taut_Prev = NULL; - - mult = 0; - bNext = 0; - is = NULL; - is2 = NULL; - is0 = pINChISort; - is20 = bSecondNonTautPass? pINChISort2 : NULL; - eq2taut = 0; /* may be non-zero only on the 2nd (non-taut) pass */ - eq2tautPrev = 1; /* pINChI_Aux_Prev (previous pINChI_Aux) does not exist */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; - for ( i = 0; i <= num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - pINChI_Aux = (i < num_components && (is = is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI_Aux[ii] : NULL; - if ( bSecondNonTautPass ) { - /* component that was output on the 1st pass */ - pINChI_Aux_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI_Aux[ii2] : NULL; - } - /*================ compare non-iso non-taut equivalence info to non-iso taut ========*/ - eq2taut = bSecondNonTautPass && bOmitRepetitions && - Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU, pINChI_Aux_Taut, EQL_EQU ); - eq2taut = eq2taut? (iiEQU | iitNONTAUT) : 0; - if ( eq2taut ) { - /* we may be here only in case of the second (non-taut) pass */ - /* current non-taut equivalence has been found to be same as tautomeric */ - if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { - /* previous component exists */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( bHasEquString( pINChI_Aux_Prev->nConstitEquNumbers, pINChI_Aux_Prev->nNumberOfAtoms) ) { - /* output previous component(s) equivalence since it was found to be non-trivial */ - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - tot_len += MakeEquString( pINChI_Aux_Prev->nConstitEquNumbers, pINChI_Aux_Prev->nNumberOfAtoms, 0, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } else { - ; /* pINChI_Aux_Prev exists and has only trivial equivalence info */ - } - } else - if ( pINChI_Aux_Taut_Prev && pINChI_Aux_Taut_Prev->nNumberOfAtoms ) { - /* previous non-taut component exists only in taut list */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - } - /* we have found pINChI_Aux->nConstitEquNumbers same as in pINChI_Aux_Taut */ - /* output this (current) equivalence as '*', that is, same as tautomeric */ - /* that was printed on the 1st pass. */ - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - pINChI_Aux_Prev = NULL; /* pINChI_Aux_Prev does not exist since */ - pINChI_Aux_Taut_Prev = NULL; /* pINChI_Aux has just been printed */ - mult = 0; - eq2tautPrev = 1; - } else - if ( eq2tautPrev ) { - /* at this point pINChI_Aux_Prev does not exist; however, pINChI_Aux */ - /*might have been discovered and it is different from pINChI_Aux_Taut */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - eq2tautPrev = 0; - pINChI_Aux_Prev = pINChI_Aux; - pINChI_Aux_Taut_Prev = pINChI_Aux_Taut; - mult = 0; - } else { - /* check whether pINChI_Aux and pINChI_Aux_Prev have identical non-trivial equivalence info */ - eq2prev = bUseMulipliers && - Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU, pINChI_Aux_Prev, EQL_EQU ); - if ( eq2prev ) { - /* eq. info is same and non-trivial */ - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - /* pINChI_Aux eq. info is either different or trivial. Output pINChI_Aux_Prev anyway */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { - if ( bHasEquString( pINChI_Aux_Prev->nConstitEquNumbers, pINChI_Aux_Prev->nNumberOfAtoms) ) { - /* pINChI_Aux_Prev exists and has equivalence info */ - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - tot_len += MakeEquString( pINChI_Aux_Prev->nConstitEquNumbers, pINChI_Aux_Prev->nNumberOfAtoms, 0, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } else { - ; /* pINChI_Aux_Prev exists and has only trivial equivalence info */ - } - } else - if ( bSecondNonTautPass && pINChI_Aux_Taut_Prev && pINChI_Aux_Taut_Prev->nNumberOfAtoms ) { - if ( bHasEquString( pINChI_Aux_Taut_Prev->nConstitEquNumbers, pINChI_Aux_Taut_Prev->nNumberOfAtoms) ) { - /* since pINChI_Aux_Prev does not exist, pINChI_Aux_Taut_Prev is non-tautomeric */ - /* and it has non-trivial equivalence info. This info has already been printed in the main section */ - /* - tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); - */ - } else { - ; /* pINChI_Aux_Taut_Prev exists and has only trivial equivalence info */ - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; /* */ - } -#endif - } - pINChI_Aux_Prev = pINChI_Aux; - pINChI_Aux_Taut_Prev = pINChI_Aux_Taut; - mult = 0; /* we do not know whether the item is empty */ - } - } - return tot_len; -} -/******************************************************************************************/ -int str_AuxInvSp3(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) -{ - int i, ii, ii2; - INCHI_SORT *is, *is2, *is0, *is20; - INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; - INChI_Stereo *Stereo, *Stereo_Prev, *Stereo_Taut, *Stereo_Taut_Prev; - int mult, eq2prev, eq2taut, eq2tautPrev, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - /*************** - inverted sp3 - ****************/ - pINChI_Taut = NULL; - pINChI_Prev = NULL; - pINChI_Taut_Prev = NULL; - mult = 0; - bNext = 0; - is = NULL; - is2 = NULL; - is0 = pINChISort; - is20 = bSecondNonTautPass? pINChISort2 : NULL; - eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ - eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; - for ( i = 0; i <= num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; - /*================ compare sp2 to previous =====================*/ - if ( bSecondNonTautPass ) { - /* component that was output on the 1st pass */ - pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; - } - eq2taut = 0; - /*========= if bSecondNonTautPass then compare iso non-taut stereo to other stereo ========*/ - if ( bSecondNonTautPass && bOmitRepetitions ) { - /* compare non-tautomeric inverted to: - * a) tautomeric inverted - * b) Inverted(tautomeric) - * c) Inverted(non-tautomeric) - */ - /* a) compare non-tautomeric inverted to tautomeric inverted */ - if ( !eq2taut ) { - eq2taut = pINChI && pINChI_Taut && - /* non-taut inverted */ /* taut invertedc */ - (Stereo = pINChI->Stereo) && (Stereo_Taut = pINChI_Taut->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3_INV, 0 ); - /* stereo-inv non-taut = taut (stereo-inv) */ - eq2taut = eq2taut? (iiSTEREO_INV | iitNONTAUT ) : 0; - } - /* b) compare non-tautomeric inverted to Inverted(tautomeric stereo) */ - if ( !eq2taut ) { - eq2taut = pINChI && pINChI_Taut && - (Stereo = pINChI->Stereo) && (Stereo_Taut = pINChI_Taut->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3, 0 ); - /* stereo-inv non-taut = Inv(taut stereo) */ - eq2taut = eq2taut? (iiSTEREO_INV | iitNONTAUT | iiEq2INV) : 0; - } - /* c) compare non-tautomeric inverted to Inverted(non-tautomeric stereo) */ - if ( !eq2taut ) { - eq2taut = pINChI && - (Stereo = pINChI->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo, EQL_SP3, 0 ); - /* stereo-inv non-taut = Inv(non-taut stereo) */ - eq2taut = eq2taut? (iiSTEREO_INV | iitNONTAUT | iiEq2INV | iiEq2NONTAUT) : 0; - } -#if ( FIX_EMPTY_LAYER_BUG == 1 ) - if ( !eq2taut && pINChI && pINChI_Taut && - !((Stereo = pINChI->Stereo) && Eql_INChI_Stereo( Stereo, EQL_SP3_INV, NULL, EQL_EXISTS, 0 ))) { - if ( (Stereo_Taut = pINChI_Taut->Stereo) && - Eql_INChI_Stereo( Stereo_Taut, EQL_SP3, NULL, EQL_EXISTS, 0 ) ) { - - eq2taut = iiEmpty; /* the current is empty while the preceding (taut) is not */ - } - } -#endif - } else - /*========= if not bSecondNonTautPass then compare inv taut stereo to various taut stereo ========*/ - if ( !bSecondNonTautPass && bOmitRepetitions ) { - /* compare tautomeric inverted to Invetred(tautomeric) */ - if ( !eq2taut ) { - eq2taut = pINChI && - (Stereo = pINChI->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo, EQL_SP3, 0 ); - /* stereo isotopic taut = taut (stereo) */ - eq2taut = eq2taut? (iiSTEREO_INV | iiEq2INV ) : 0; - } - } - if ( eq2taut ) { - /* we may be here only in case of the current layer found equal in another layer the same component */ - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - /* previous component exists; output it before output the current component */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( (Stereo_Prev = pINChI_Prev->Stereo) && Stereo_Prev->nNumberOfStereoCenters > 0 ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - - tot_len += MakeStereoString( Stereo_Prev->nNumber, NULL, Stereo_Prev->t_parityInv, - 0, Stereo_Prev->nNumberOfStereoCenters, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - } else - if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - /* previous non-taut component exists only in taut list */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - /* do not output stereo of non-tautomeric in non-taut layer: it has been output in the main layer */ - } - /* we have found another (previously printed) layer of the current component equal to this layer */ - /* output this (current) equivalence mark = EquString(eq2taut) */ - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ - pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ - mult = 0; - eq2tautPrev = 1; /* pINChI_Prev and pINChI_Taut_Prev have already been output */ - } else - if ( eq2tautPrev ) { - /* at this point pINChI_Prev does not exist; however, pINChI */ - /*might have been discovered and it is different from pINChI_Taut */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - eq2tautPrev = 0; - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; - } else { - /* current layer is different from previously printed layers of the current component */ - /* compare the current layer to this layer of the previous component: */ - /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp2 */ - /*================ compare iso sp3 to previous =====================*/ - eq2prev =bUseMulipliers && - pINChI && pINChI_Prev && - /* do both have stereo? */ - (Stereo = pINChI->Stereo) && (Stereo_Prev = pINChI_Prev->Stereo) && - /* is their inverted stereo same? */ - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Prev, EQL_SP3_INV, 0 ); - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - if ( (Stereo_Prev = pINChI_Prev->Stereo) && - Stereo_Prev->nNumberOfStereoCenters > 0 && Stereo_Prev->nCompInv2Abs ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - - tot_len += MakeStereoString( Stereo_Prev->nNumberInv, NULL, Stereo_Prev->t_parityInv, - 0, Stereo_Prev->nNumberOfStereoCenters, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - /* else sp3 info is not present in pINChI_Prev */ - } else - /* do not print pINChI_Prev because it either do not exist of have already been printed */ - if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - if ( (Stereo_Taut_Prev = pINChI_Taut_Prev->Stereo) && - Stereo_Taut_Prev->nNumberOfStereoCenters > 0 && Stereo_Taut_Prev->nCompInv2Abs ) { - /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ - /* and it has non-trivial inv sp3 info. It has already been printed in the main section */ - /* - tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); - */ - ;/* pINChI_Taut_Prev sp3 info was output in the main stereo section */ - } else { - ; /* pINChI_Taut_Prev exists and has not sp3 info */ - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; /* */ - } -#endif - } - pINChI_Prev = pINChI; - mult = 0; /* we do not know whether the item is empty */ - } - } - return tot_len; -} -/***************************************************************************/ -int str_AuxInvSp3Numb(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions) -{ - int i, ii, ii2; - INCHI_SORT *is, *is0 /*, *is2*/; - INChI *pINChI, *pINChI_Taut; - INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev, *pINChI_Aux_Taut; - INChI_Stereo *Stereo, *Stereo_Taut; - int eq2taut, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - /************************************************** - * specificity of numbering: there is no previous * - * component because no repetition is possible * - **************************************************/ - pINChI = NULL; - pINChI_Taut = NULL; - pINChI_Aux = NULL; - pINChI_Aux_Taut = NULL; - pINChI_Aux_Prev = NULL; - bNext = 0; - is = NULL; - is0 = pINChISort; - /*is2 = bSecondNonTautPass? pINChISort2 : NULL;*/ - eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; - for ( i = 0; i < num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - is=is0+i; - pINChI = (0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; - pINChI_Aux = pINChI? is->pINChI_Aux[ii] : NULL; - /*================ to compare to previously printed =====================*/ - if ( bSecondNonTautPass ) { - /* component that was printed on the 1st pass */ - pINChI_Taut = (0 <= (ii2=GET_II(OUT_T1,is)))? is->pINChI[ii2] : NULL; - pINChI_Aux_Taut = pINChI_Taut? is->pINChI_Aux[ii2] : NULL; - } - - eq2taut = 0; - /*========= if bSecondNonTautPass then compare inv non-taut stereo to other stereo ========*/ - if ( bSecondNonTautPass && bOmitRepetitions && - pINChI && (Stereo = pINChI->Stereo) && Stereo->nCompInv2Abs ) { - /* compare non-tautomeric inverted stereo numbering to: - * a) tautomeric numbering - * b) non-tautomeric numbering - * c) tautomeric inverted stereo numbering - */ - /* a) compare non-tautomeric inverted stereo numbering to tautomeric numbering */ - if ( !eq2taut ) { - eq2taut = pINChI_Taut && - Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV, pINChI_Aux_Taut, EQL_NUM ); - /* stereo-inv numbering non-taut = taut numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iiNUMB | iitNONTAUT ) : 0; - } - /* b) compare non-tautomeric inverted stereo numbering to non-tautomeric numbering */ - if ( !eq2taut ) { - eq2taut = - Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV, pINChI_Aux, EQL_NUM ); - /* stereo-inv numb. non-taut = non-taut numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iiNUMB | iitNONTAUT | iiEq2NONTAUT ) : 0; - } - /* c) compare non-tautomeric inverted stereo numbering to tautomeric inverted stereo numbering */ - if ( !eq2taut ) { - eq2taut = pINChI_Taut && - (Stereo_Taut = pINChI_Taut->Stereo) && Stereo_Taut->nCompInv2Abs && - Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV, pINChI_Aux_Taut, EQL_NUM_INV ); - /* stereo-inv numb. non-taut = taut inv stereo numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iiNUMB | iitNONTAUT | iiEq2INV ) : 0; - } - } else - /*========= if not bSecondNonTautPass then compare inv taut stereo numb to taut numb ========*/ - if ( !bSecondNonTautPass && bOmitRepetitions && - pINChI && (Stereo = pINChI->Stereo) && Stereo->nCompInv2Abs ) { - /* compare tautomeric inverted stereo numbering to tautomeric numbering */ - if ( !eq2taut ) { - eq2taut = - Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV, pINChI_Aux, EQL_NUM ); - /* stereo-inv numbering (taut) = taut numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iiNUMB ) : 0; - } - } - if ( eq2taut ) { - /* we have found another (previously printed) layer of the current component equal to this layer */ - /* output this (current) equivalence mark = EquString(eq2taut) */ - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - /* current layer is different from previously printed layers of the current component */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI && pINChI_Aux && pINChI_Aux->nNumberOfAtoms && - (Stereo = pINChI->Stereo) && Stereo->nNumberOfStereoCenters && - Stereo->nCompInv2Abs && pINChI_Aux->nOrigAtNosInCanonOrdInv ) { - tot_len += MakeCtString( pINChI_Aux->nOrigAtNosInCanonOrdInv, - pINChI_Aux->nNumberOfAtoms, 0, NULL, 0, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - /* else inv stereo info is not present in pINChI */ - } - } - if ( multPrevEquStr && pPrevEquStr ) { - /* the new EqStr of the last item has not been printed; output it now */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - return tot_len; -} -/***************************************************************************/ -int str_AuxIsoNumb(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions) -{ - int i, ii, ii2; - INCHI_SORT *is, *is0 /*, *is2*/; - INChI *pINChI, *pINChI_Taut; - INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev, *pINChI_Aux_Taut; - int eq2taut, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - /************************************************** - * specificity of numbering: there is no previous * - * component because no repetition is possible * - **************************************************/ - pINChI = NULL; /* not used here, for debug only */ - pINChI_Taut = NULL; /* not used here, for debug only */ - pINChI_Aux = NULL; - pINChI_Aux_Taut = NULL; - pINChI_Aux_Prev = NULL; - bNext = 0; - is = NULL; - is0 = pINChISort; - /*is2 = bSecondNonTautPass? pINChISort2 : NULL;*/ - eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; - for ( i = 0; i < num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - is=is0+i; - pINChI_Aux = (i < num_components && 0 <= (ii=GET_II(bOutType,is)))? is->pINChI_Aux[ii] : NULL; - /*================ to compare to previously printed =====================*/ - if ( bSecondNonTautPass ) { - pINChI_Aux_Taut = (0 <= (ii2=GET_II(OUT_T1,is)))? is->pINChI_Aux[ii2] : NULL; - } - eq2taut = 0; - /*========= if bSecondNonTautPass then compare iso non-taut numb to other numb ========*/ - if ( bSecondNonTautPass && bOmitRepetitions && pINChI_Aux && pINChI_Aux->bIsIsotopic ) { - /* compare non-tautomeric isotopic numbering to: - * a) tautomeric numbering - * b) non-tautomeric numbering - * c) tautomeric isotopic numbering - */ - /* a) compare non-tautomeric isotopic numbering to tautomeric numbering */ - if ( !eq2taut ) { - eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_ISO, pINChI_Aux_Taut, EQL_NUM ); - /* numbering non-taut isotopic = taut numbering */ - eq2taut = eq2taut? ( iiNUMB | iitNONTAUT | iitISO ) : 0; - } - /* b) compare non-tautomeric isotopic numbering to non-tautomeric numbering */ - if ( !eq2taut ) { - eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_ISO, pINChI_Aux, EQL_NUM ); - /* numbering non-taut isotopic = non-taut numbering */ - eq2taut = eq2taut? ( iiNUMB | iitNONTAUT | iitISO | iiEq2NONTAUT ) : 0; - } - /* c) compare non-tautomeric isotopic numbering to tautomeric isotopic numbering */ - if ( !eq2taut ) { - eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_ISO, pINChI_Aux_Taut, EQL_NUM_ISO ); - /* numbering non-taut isotopic = taut isotopic numbering */ - eq2taut = eq2taut? ( iiNUMB | iitNONTAUT | iitISO | iiEq2ISO ) : 0; - } - } else - /*========= if not bSecondNonTautPass then compare inv taut stereo numb to taut numb ========*/ - if ( !bSecondNonTautPass && bOmitRepetitions && pINChI_Aux && pINChI_Aux->bIsIsotopic ) { - /* compare tautomeric isotopic numbering to tautomeric non-isotopic numbering */ - if ( !eq2taut ) { - eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_ISO, pINChI_Aux, EQL_NUM_ISO ); - /* stereo-inv numbering (taut) = taut numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iiNUMB ) : 0; - } - } - if ( eq2taut ) { - /* we have found another (previously printed) layer of the current component equal to this layer */ - /* output this (current) equivalence mark = EquString(eq2taut) */ - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - /* current layer is different from previously printed layers of the current component */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Aux && pINChI_Aux->nNumberOfAtoms && pINChI_Aux->bIsIsotopic && - pINChI_Aux->nIsotopicOrigAtNosInCanonOrd ) { - tot_len += MakeCtString( pINChI_Aux->nIsotopicOrigAtNosInCanonOrd, - pINChI_Aux->nNumberOfAtoms, 0, NULL, 0, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - /* else isotopic numbering is not present in pINChI */ - } - } - if ( multPrevEquStr && pPrevEquStr ) { - /* the new EqStr of the last item has not been printed; output it now */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - return tot_len; -} -/***************************************************************************/ -int str_AuxIsoEqu(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) -{ - int i, ii, ii2; - INCHI_SORT *is, *is2, *is0, *is20; - INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev, *pINChI_Aux_Taut, *pINChI_Aux_Taut_Prev; - int mult, eq2prev, eq2taut, eq2tautPrev, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - pINChI_Aux = NULL; - pINChI_Aux_Prev = NULL; - pINChI_Aux_Taut = NULL; - pINChI_Aux_Taut_Prev = NULL; - mult = 0; - bNext = 0; - is = NULL; - is2 = NULL; - is0 = pINChISort; - is20 = bSecondNonTautPass? pINChISort2 : NULL; - eq2taut = 0; /* may be non-zero only on the 2nd (non-taut) pass */ - eq2tautPrev = 1; /* pINChI_Aux_Prev (previous pINChI_Aux) does not exist */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; - for ( i = 0; i <= num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - pINChI_Aux = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI_Aux[ii] : NULL; - if ( bSecondNonTautPass ) { - /* component that was output on the 1st pass */ - pINChI_Aux_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI_Aux[ii2] : NULL; - } - /*================ compare iso non-taut equivalence info to non-iso taut ========*/ - eq2taut = 0; - if ( bSecondNonTautPass && bOmitRepetitions && pINChI_Aux && pINChI_Aux->bIsIsotopic ) { - /************************************************** - * compare isotopic non-tautomeric equivalence to: - * a) tautomeric - * b) non-tautomeric - * c) isotopic tautomeric - */ - if ( !eq2taut ) { - /* compare isotopic non-tautomeric equivalence to tautomeric */ - eq2taut = Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_ISO, pINChI_Aux_Taut, EQL_EQU ); - /* equ non-taut isotopic = tautomeric*/ - eq2taut = eq2taut? (iiEQU | iitNONTAUT | iitISO) : 0; - } - if ( !eq2taut ) { - /* compare isotopic non-tautomeric equivalence to non-tautomeric */ - eq2taut = Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_ISO, pINChI_Aux, EQL_EQU ); - /* equ non-taut isotopic = non-tautomeric*/ - eq2taut = eq2taut? (iiEQU | iitNONTAUT | iitISO | iiEq2NONTAUT) : 0; - } - if ( !eq2taut ) { - /* compare isotopic non-tautomeric equivalence to isotopic tautomeric */ - eq2taut = Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_ISO, pINChI_Aux_Taut, EQL_EQU_ISO ); - /* equ non-taut isotopic = isotopic tautomeric*/ - eq2taut = eq2taut? (iiEQU | iitNONTAUT | iitISO | iiEq2ISO) : 0; - } - } else - if ( !bSecondNonTautPass && bOmitRepetitions && pINChI_Aux && pINChI_Aux->bIsIsotopic ) { - /************************************************** - * compare isotopic tautomeric equivalence to: - * a) non-isotopic tautomeric - */ - if ( !eq2taut ) { - /* compare isotopic tautomeric equivalence to tautomeric */ - eq2taut = Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_ISO, pINChI_Aux, EQL_EQU ); - /* equ taut-isotopic = tautomeric*/ - eq2taut = eq2taut? (iiEQU | iitISO) : 0; - } - } - if ( eq2taut ) { - /* we may be here only in case of the second (non-taut) pass */ - /* current non-taut equivalence has been found to be same as tautomeric */ - if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { - /* previous component exists */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( bHasEquString( pINChI_Aux_Prev->nConstitEquIsotopicNumbers, pINChI_Aux_Prev->nNumberOfAtoms) ) { - /* output previous component(s) equivalence since it was found to be non-trivial */ - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - tot_len += MakeEquString( pINChI_Aux_Prev->nConstitEquIsotopicNumbers, pINChI_Aux_Prev->nNumberOfAtoms, 0, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } else { - ; /* pINChI_Aux_Prev exists and has only trivial equivalence info */ - } - } else - if ( pINChI_Aux_Taut_Prev && pINChI_Aux_Taut_Prev->nNumberOfAtoms ) { - /* previous non-taut component exists only in taut list */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - } - /* we have found pINChI_Aux->pINChI_Aux->nConstitEquIsotopicNumbers same as in pINChI_Aux_Taut */ - /* output this (current) equivalence as '*', that is, same as tautomeric */ - /* that was printed on the 1st pass. */ - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - pINChI_Aux_Prev = NULL; /* pINChI_Aux_Prev does not exist since */ - pINChI_Aux_Taut_Prev = NULL; /* pINChI_Aux has just been printed */ - mult = 0; - eq2tautPrev = 1; - } else - if ( eq2tautPrev ) { - /* at this point pINChI_Aux_Prev does not exist; however, pINChI_Aux */ - /*might have been discovered and it is different from pINChI_Aux_Taut */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - eq2tautPrev = 0; - pINChI_Aux_Prev = pINChI_Aux; - pINChI_Aux_Taut_Prev = pINChI_Aux_Taut; - mult = 0; - } else { - /* check whether pINChI_Aux and pINChI_Aux_Prev have identical non-trivial equivalence info */ - eq2prev = bUseMulipliers && Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_ISO, pINChI_Aux_Prev, EQL_EQU_ISO ); - if ( eq2prev ) { - /* eq. info is same and non-trivial */ - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - /* pINChI_Aux eq. info is either different or trivial. Output pINChI_Aux_Prev anyway */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { - if ( bHasEquString( pINChI_Aux_Prev->nConstitEquIsotopicNumbers, pINChI_Aux_Prev->nNumberOfAtoms) ) { - /* pINChI_Aux_Prev exists and has equivalence info */ - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - tot_len += MakeEquString( pINChI_Aux_Prev->nConstitEquIsotopicNumbers, pINChI_Aux_Prev->nNumberOfAtoms, 0, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } else { - ; /* pINChI_Aux_Prev exists and has only trivial equivalence info */ - } - } else - if ( bSecondNonTautPass && pINChI_Aux_Taut_Prev && pINChI_Aux_Taut_Prev->nNumberOfAtoms ) { - if ( bHasEquString( pINChI_Aux_Taut_Prev->nConstitEquIsotopicNumbers, pINChI_Aux_Taut_Prev->nNumberOfAtoms) ) { - /* since pINChI_Aux_Prev does not exist, pINChI_Aux_Taut_Prev is non-tautomeric */ - /* and it has non-trivial equivalence info. This info has already been printed in the main section */ - /* - tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); - */ - } else { - ; /* pINChI_Aux_Taut_Prev exists and has only trivial equivalence info */ - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; /* */ - } -#endif - } - pINChI_Aux_Prev = pINChI_Aux; - pINChI_Aux_Taut_Prev = pINChI_Aux_Taut; - mult = 0; /* we do not know whether the item is empty */ - } - } - return tot_len; -} -/******************************************************************************************/ -int str_AuxInvIsoSp3(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) -{ - int i, ii, ii2; - INCHI_SORT *is, *is2, *is0, *is20; - INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; - INChI_Stereo *Stereo, *Stereo_Prev, *Stereo_Taut, *Stereo_Taut_Prev; - int mult, eq2prev, eq2taut, eq2tautPrev, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - /******************************** - inverted isotopic sp3 - *********************************/ - pINChI_Taut = NULL; - pINChI_Prev = NULL; - pINChI_Taut_Prev = NULL; - mult = 0; - bNext = 0; - is = NULL; - is2 = NULL; - is0 = pINChISort; - is20 = bSecondNonTautPass? pINChISort2 : NULL; - eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ - eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; - for ( i = 0; i <= num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; - /*================ compare sp2 to previous =====================*/ - if ( bSecondNonTautPass ) { - /* component that was output on the 1st pass */ - pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; - } - eq2taut = 0; - /*========= if bSecondNonTautPass then compare iso non-taut stereo to other stereo ========*/ - if ( bSecondNonTautPass && bOmitRepetitions && pINChI && pINChI->nNumberOfIsotopicAtoms+pINChI->nNumberOfIsotopicTGroups > 0 ) { - /* compare non-tautomeric isotopic inverted to: - * a) tautomeric inverted - * b) *non-tautomeric inverted - * c) *isotopic tautomeric inverted - * d) Inverted(tautomeric) - * e) *Inverted(tautomeric isotopic) - * f) Inverted(non-tautomeric) - * g) *Inverted(non-tautomeric isotopic) - */ - /* a) compare non-tautomeric isotopic inverted to tautomeric inverted */ - if ( !eq2taut ) { - eq2taut = pINChI && pINChI_Taut && - /* non-taut inverted */ /* taut invertedc */ - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3_INV, 0 ); - /* stereo-inv isotopic non-taut = taut (stereo-inv) */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT ) : 0; - } - /* b) compare non-tautomeric isotopic inverted to non-tautomeric inverted */ - if ( !eq2taut ) { - eq2taut = pINChI && /* it is non-taut non-iso stereo */ - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3_INV, 0 ); - /* stereo-inv isotopic non-taut = non-taut stereo-inv */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT | iiEq2NONTAUT) : 0; - } - /* c) compare non-tautomeric isotopic inverted to isotopic tautomeric inverted */ - if ( !eq2taut ) { - eq2taut = pINChI && pINChI_Taut && - /* non-taut iso. inverted */ /* taut iso. inverted */ - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3_INV, 0 ); - /* stereo-inv isotopic non-taut = taut iso. stereo-inv */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT | iiEq2ISO ) : 0; - } - /* d) compare non-tautomeric inverted to Inverted(tautomeric stereo) */ - if ( !eq2taut ) { - eq2taut = pINChI && pINChI_Taut && - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3_INV, 0 ); - /* stereo-inv isotopic non-taut = Inv(non-iso taut stereo) */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT | iiEq2INV) : 0; - } - /* e) compare non-tautomeric inverted to Inverted(isotopic tautomeric stereo) */ - if ( !eq2taut ) { - eq2taut = pINChI && pINChI_Taut && - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3, 0 ); - /* stereo-inv isotopic non-taut = Inv(iso taut stereo) */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT | iiEq2INV | iiEq2ISO) : 0; - } - /* f) compare non-tautomeric isotopic inverted to Inverted(non-tautomeric stereo) */ - if ( !eq2taut ) { - eq2taut = pINChI && /* it is non-taut non-iso stereo */ - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3, 0 ); - /* stereo-inv isotopic non-taut = Inv(non-taut stereo) */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT | iiEq2INV | iiEq2NONTAUT) : 0; - } - /* g) compare non-tautomeric isotopic inverted to Inverted(non-tautomeric isotopic stereo) */ - if ( !eq2taut ) { - eq2taut = pINChI && /* it is non-taut non-iso stereo */ - (Stereo = pINChI->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo, EQL_SP3, 0 ); - /* stereo-inv isotopic non-taut = Inv( iso non-taut stereo) */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT | iiEq2INV | iiEq2ISO | iiEq2NONTAUT) : 0; - } -#if ( FIX_EMPTY_LAYER_BUG == 1 ) - if ( !eq2taut && pINChI && !((Stereo = pINChI->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, NULL, EQL_EXISTS, 0 )) ) { - /* component has no stereo; check whether it has stereo in the preceding layers */ - if ( pINChI_Taut && (Stereo_Taut = pINChI_Taut->Stereo) && /* F is not empty */ - Eql_INChI_Stereo( Stereo_Taut, EQL_SP3_INV, NULL, EQL_EXISTS, 0 ) || - !(pINChI_Taut && (Stereo_Taut = pINChI_Taut->Stereo) && /* M is empty and ... */ - Eql_INChI_Stereo( Stereo_Taut, EQL_SP3_INV, NULL, EQL_EXISTS, 0 )) && - (pINChI_Taut && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && /* ... MI is not empty */ - Eql_INChI_Stereo( Stereo_Taut, EQL_SP3_INV, NULL, EQL_EXISTS, 0 )) ) { - - eq2taut = iiEmpty; /* the component has stereo in the preceding layer */ - } - } -#endif - } else - /*========= if not bSecondNonTautPass then compare inv taut stereo to various stereo ========*/ - if ( !bSecondNonTautPass && bOmitRepetitions && pINChI && - (pINChI->nNumberOfIsotopicAtoms > 0 || - pINChI->nNumberOfIsotopicTGroups > 0 || - pINChI->nPossibleLocationsOfIsotopicH && pINChI->nPossibleLocationsOfIsotopicH[0] > 1) ) { - /* compare tautomeric isotopic stereo-inverted to: - * a) tautomeric stereo-inverted - * b) Inverted(tautomeric stereo) - * c) Inverted(tautomeric isotopic stereo) - */ - /* a) compare tautomeric isotopic stereo-inverted to tautomeric stereo-inverted */ - if ( !eq2taut ) { - eq2taut = pINChI && - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3_INV, 0 ); - /* stereo-inv isotopic taut = taut stereo-inv */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO ) : 0; - } - /* b) compare tautomeric isotopic stereo-inverted to Inverted(tautomeric stereo) */ - if ( !eq2taut ) { - eq2taut = pINChI && - (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3, 0 ); - /* stereo-inv isotopic taut = Inv(taut stereo) */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiEq2INV ) : 0; - } - /* c) compare tautomeric isotopic stereo-inverted to Inverted(tautomeric isotopic stereo) */ - if ( !eq2taut ) { - eq2taut = pINChI && - (Stereo = pINChI->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo, EQL_SP3, 0 ); - /* stereo-inv isotopic taut = Inv(taut iso stereo) */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiEq2INV | iiEq2ISO ) : 0; - } -#if ( FIX_EMPTY_LAYER_BUG == 1 ) - if ( !eq2taut && pINChI && !((Stereo = pINChI->StereoIsotopic) && - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, NULL, EQL_EXISTS, 0 ) ) ) { - /* component has no MI stereo; check whether it has stereo in the preceding layer M */ - if ( (Stereo_Taut = pINChI->Stereo) && - Eql_INChI_Stereo( Stereo_Taut, EQL_SP3_INV, NULL, EQL_EXISTS, 0 ) ) { - eq2taut = iiEmpty; /* the component has stereo in the preceding layer */ - } - } -#endif - } - if ( eq2taut ) { - /* we may be here only in case of the current layer found equal in another layer the same component */ - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - /* previous component exists; output it before output the current component */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( (Stereo_Prev = pINChI_Prev->StereoIsotopic) && Stereo_Prev->nNumberOfStereoCenters > 0 ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - - tot_len += MakeStereoString( Stereo_Prev->nNumber, NULL, Stereo_Prev->t_parityInv, - 0, Stereo_Prev->nNumberOfStereoCenters, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - } else - if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - /* previous non-taut component exists only in taut list */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - /* do not output stereo of non-tautomeric in non-taut layer: it has been output in the main layer */ - } - /* we have found another (previously printed) layer of the current component equal to this layer */ - /* output this (current) equivalence mark = EquString(eq2taut) */ - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ - pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ - mult = 0; - eq2tautPrev = 1; /* pINChI_Prev and pINChI_Taut_Prev have already been output */ - } else - if ( eq2tautPrev ) { - /* at this point pINChI_Prev does not exist; however, pINChI */ - /*might have been discovered and it is different from pINChI_Taut */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - eq2tautPrev = 0; - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; - } else { - /* current layer is different from previously printed layers of the current component */ - /* compare the current layer to this layer of the previous component: */ - /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp2 */ - /*================ compare iso sp3 to previous =====================*/ - eq2prev =bUseMulipliers && - pINChI && pINChI->nNumberOfIsotopicAtoms + pINChI->nNumberOfIsotopicTGroups > 0 && - pINChI_Prev && pINChI_Prev->nNumberOfIsotopicAtoms + pINChI_Prev->nNumberOfIsotopicTGroups > 0 && - /* do both have stereo? */ - (Stereo = pINChI->StereoIsotopic) && (Stereo_Prev = pINChI_Prev->StereoIsotopic) && - /* is their inverted stereo same? */ - Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Prev, EQL_SP3_INV, 0 ); - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms && pINChI_Prev->nNumberOfIsotopicAtoms + pINChI_Prev->nNumberOfIsotopicTGroups > 0 ) { - if ( (Stereo_Prev = pINChI_Prev->StereoIsotopic) && - Stereo_Prev->nNumberOfStereoCenters > 0 && Stereo_Prev->nCompInv2Abs ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - - tot_len += MakeStereoString( Stereo_Prev->nNumberInv, NULL, Stereo_Prev->t_parityInv, - 0, Stereo_Prev->nNumberOfStereoCenters, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - /* else sp3 info is not present in pINChI_Prev */ - } else - /* do not print pINChI_Prev because it either do not exist of have already been printed */ - if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { - if ( (Stereo_Taut_Prev = pINChI_Taut_Prev->StereoIsotopic) && - Stereo_Taut_Prev->nNumberOfStereoCenters > 0 && Stereo_Taut_Prev->nCompInv2Abs ) { - /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ - /* and it has non-trivial inv sp3 info. It has already been printed in the main section */ - /* - tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); - */ - ;/* pINChI_Taut_Prev sp3 info was output in the main stereo section */ - } else { - ; /* pINChI_Taut_Prev exists and has not sp3 info */ - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; /* */ - } -#endif - } - pINChI_Prev = pINChI; - mult = 0; /* we do not know whether the item is empty */ - } - } - return tot_len; -} -/***************************************************************************/ -int str_AuxInvIsoSp3Numb(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions) -{ - int i, ii, ii2; - INCHI_SORT *is, *is0 /*, *is2*/; - INChI *pINChI, *pINChI_Taut; - INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev, *pINChI_Aux_Taut; - INChI_Stereo *Stereo, *Stereo_Taut; - int eq2taut, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - /************************************************** - * specificity of numbering: there is no previous * - * component because no repetition is possible * - **************************************************/ - pINChI = NULL; - pINChI_Taut = NULL; - pINChI_Aux = NULL; - pINChI_Aux_Taut = NULL; - pINChI_Aux_Prev = NULL; - bNext = 0; - is = NULL; - /* is2 = NULL;*/ - is0 = pINChISort; - /* is20 = bSecondNonTautPass? pINChISort2 : NULL;*/ - eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; - for ( i = 0; i < num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - is=is0+i; - pINChI = (0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; - pINChI_Aux = pINChI? is->pINChI_Aux[ii] : NULL; - /*================ to compare to previously printed =====================*/ - if ( bSecondNonTautPass ) { - /* component that was printed on the 1st pass */ - pINChI_Taut = (0 <= (ii2=GET_II(OUT_T1,is)))? is->pINChI[ii2] : NULL; - pINChI_Aux_Taut = pINChI_Taut? is->pINChI_Aux[ii2] : NULL; - } - eq2taut = 0; - /*========= if bSecondNonTautPass then compare iso non-taut stereo to other stereo ========*/ - if ( bSecondNonTautPass && bOmitRepetitions && pINChI && pINChI_Aux && pINChI_Aux->bIsIsotopic && - (Stereo = pINChI->StereoIsotopic) && Stereo->nCompInv2Abs && - pINChI_Aux->nNumberOfAtoms > 0 && pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv ) { - /* compare isotopic non-tautomeric inverted stereo numbering to: - * a) tautomeric numbering - * b) non-tautomeric numbering - * c) *tautomeric isotopic numbering - * d) *non-tautomeric isotopic numbering - * e) tautomeric inverted stereo numbering - * f) *non-tautomeric inverted stereo numbering - * g) tautomeric isotopic inverted stereo numbering - */ - /* a) compare isotopic non-tautomeric inverted stereo numbering to tautomeric numbering */ - if ( !eq2taut ) { - eq2taut = pINChI_Taut && - Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux_Taut, EQL_NUM ); - /* stereo-inv isotopic numbering non-taut = taut numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT ) : 0; - } - /* b) compare isotopic non-tautomeric inverted stereo numbering to non-tautomeric numbering */ - if ( !eq2taut ) { - eq2taut = - Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux, EQL_NUM ); - /* stereo-inv isotopic numb. non-taut = non-taut numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT | iiEq2NONTAUT ) : 0; - } - /* c) compare isotopic non-tautomeric inverted stereo numbering to tautomeric isotopic numbering */ - if ( !eq2taut ) { - eq2taut = pINChI_Taut && - Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux_Taut, EQL_NUM_ISO ); - /* stereo-inv isotopic numb. non-taut = taut iso numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT | iiEq2ISO ) : 0; - } - /* d) compare isotopic non-tautomeric inverted stereo numbering to non-tautomeric isotopic numbering */ - if ( !eq2taut ) { - eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux, EQL_NUM_ISO ); - /* stereo-inv isotopic numb. non-taut = non-taut isotopic numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT | iiEq2NONTAUT | iiEq2ISO) : 0; - } - /* e) compare isotopic non-tautomeric inverted stereo numbering to tautomeric inverted stereo numbering */ - if ( !eq2taut ) { - eq2taut = pINChI_Taut && pINChI_Aux_Taut && - (Stereo_Taut = pINChI_Taut->Stereo) && Stereo_Taut->nCompInv2Abs && - Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux_Taut, EQL_NUM_INV ); - /* stereo-inv isotopic numbering non-taut = stereo-inv taut numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT | iiEq2INV) : 0; - } - /* f) compare isotopic non-tautomeric inverted stereo numbering to non-tautomeric inverted stereo numbering */ - if ( !eq2taut ) { - eq2taut = - (Stereo_Taut = pINChI->StereoIsotopic) && Stereo_Taut->nCompInv2Abs && - Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux, EQL_NUM_INV ); - /* stereo-inv isotopic numbering non-taut = stereo-inv non-taut numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT | iiEq2INV | iiEq2NONTAUT) : 0; - } - - /* g) compare isotopic non-tautomeric inverted stereo numbering to tautomeric isotopic inverted stereo numbering */ - if ( !eq2taut ) { - eq2taut = pINChI_Taut && - (Stereo_Taut = pINChI_Taut->StereoIsotopic) && Stereo_Taut->nCompInv2Abs && - Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux_Taut, EQL_NUM_INV | EQL_NUM_ISO ); - /* stereo-inv isotopic numbering non-taut = stereo-inv iso taut numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT | iiEq2INV | iiEq2ISO) : 0; - } - } else - /*========= if not bSecondNonTautPass then compare inv taut stereo numb to taut numb ========*/ - if ( !bSecondNonTautPass && bOmitRepetitions && pINChI && pINChI_Aux && pINChI_Aux->bIsIsotopic && - (Stereo = pINChI->StereoIsotopic) && Stereo->nCompInv2Abs && - pINChI_Aux->nNumberOfAtoms > 0 && pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv ) { - /* compare isotopic tautomeric inverted stereo numbering to: - * a) tautomeric numbering - * b) tautomeric isotopic numbering - * c) tautomeric inverted stereo numbering - */ - /* a) compare isotopic tautomeric inverted stereo numbering to tautomeric numbering */ - if ( !eq2taut ) { - eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux, EQL_NUM ); - /* stereo-inv isotopic numbering (taut) = taut numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB ) : 0; - } - /* b) compare isotopic tautomeric inverted stereo numbering to tautomeric isotopic numbering */ - if ( !eq2taut ) { - eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux, EQL_NUM_ISO ); - /* stereo-inv isotopic numbering(taut) = isotopic taut numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iiEq2ISO ) : 0; - } - /* b) compare isotopic tautomeric inverted stereo numbering to tautomeric inverted stereo numbering */ - if ( !eq2taut ) { - eq2taut = (Stereo_Taut = pINChI->Stereo) && Stereo->nCompInv2Abs && - Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux, EQL_NUM_INV ); - /* stereo-inv isotopic numbering (taut) = taut stereo-inv numbering */ - eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iiEq2INV ) : 0; - } - } - if ( eq2taut ) { - /* we have found another (previously printed) layer of the current component equal to this layer */ - /* output this (current) equivalence mark = EquString(eq2taut) */ - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - /* current layer is different from previously printed layers of the current component */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI && pINChI_Aux && pINChI_Aux->bIsIsotopic && pINChI_Aux->nNumberOfAtoms && - (Stereo = pINChI->StereoIsotopic) && Stereo->nNumberOfStereoCenters && - Stereo->nCompInv2Abs && pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv ) { - tot_len += MakeCtString( pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv, - pINChI_Aux->nNumberOfAtoms, 0, NULL, 0, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - /* else isotopic inv stereo info is not present in pINChI */ - } - } - if ( multPrevEquStr && pPrevEquStr ) { - /* the new EqStr of the last item has not been printed; output it now */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - return tot_len; -} -/***************************************************************************/ -int str_HillFormula(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int num_components, int bUseMulipliers) -{ - int i, ii; - INCHI_SORT *is, *is0; - INChI *pINChI, *pINChI_Prev; - int mult, eq2prev, bNext; - - if ( !(is0 = pINChISort) ) { - return tot_len; - } - i = 0; - pINChI_Prev = (0 <= (ii=GET_II(bOutType,is0)))? is0->pINChI[ii] : NULL; - mult = 0; - bNext = 0; - for ( i++; i <= num_components; i ++ ) { - pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; - eq2prev = bUseMulipliers && - pINChI && pINChI_Prev && pINChI->szHillFormula && pINChI_Prev->szHillFormula && - pINChI->szHillFormula[0] && !strcmp(pINChI_Prev->szHillFormula, pINChI->szHillFormula); - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - if ( bNext ++ ) { - tot_len += MakeDelim( ".", pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Prev && pINChI_Prev->szHillFormula && pINChI_Prev->szHillFormula[0] ) { - tot_len += MakeMult( mult+1, "", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - tot_len += MakeHillFormulaString( pINChI_Prev->szHillFormula, pStr + tot_len, - nStrLen-tot_len, bOverflow); - } - } - pINChI_Prev = pINChI; - mult = 0; /* we do not know whether the item is empty */ - } - return tot_len; -} -/***************************************************************************/ -int str_HillFormula2(INCHI_SORT *pINChISort /* non-taut */, INCHI_SORT *pINChISort2 /* taut */, - char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int num_components, int bUseMulipliers) -{ - int i, ii, ii2; - INCHI_SORT *is, *is2, *is0, *is20; - INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; - int mult, eq2prev, bNext, bEqToTaut, tot_len_inp = tot_len; - - is = NULL; - is2 = NULL; - is0 = pINChISort; - is20 = pINChISort2; - i = 0; - - pINChI_Prev = (0 <= (ii=GET_II(bOutType,is0)))? is0->pINChI[ii] : NULL; - pINChI_Taut_Prev = (0 <= (ii2=GET_II(OUT_T1,is20)))? is20->pINChI[ii2] : NULL; - mult = 0; - bNext = 0; - bEqToTaut = 1; - bEqToTaut = bEqToTaut && - pINChI_Prev && pINChI_Taut_Prev && !pINChI_Taut_Prev->bDeleted && - pINChI_Prev->szHillFormula && pINChI_Taut_Prev->szHillFormula && - !strcmp(pINChI_Prev->szHillFormula, pINChI_Taut_Prev->szHillFormula); - for ( i++; i <= num_components; i ++ ) { - pINChI = (i < num_components && (is=is0+i, 0 <= (ii =GET_II(bOutType,is))))? is->pINChI[ii] : NULL; - pINChI_Taut = (i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; - if ( bEqToTaut && (pINChI || pINChI_Taut) ) { - bEqToTaut = pINChI && pINChI_Taut && !pINChI_Taut->bDeleted && - pINChI->szHillFormula && pINChI_Taut->szHillFormula && - !strcmp(pINChI->szHillFormula, pINChI_Taut->szHillFormula); - } - eq2prev = bUseMulipliers && - pINChI && pINChI_Prev && pINChI->szHillFormula && pINChI_Prev->szHillFormula && - pINChI->szHillFormula[0] && !strcmp(pINChI_Prev->szHillFormula, pINChI->szHillFormula); - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - if ( bNext ++ ) { - tot_len += MakeDelim( ".", pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Prev && pINChI_Prev->szHillFormula && pINChI_Prev->szHillFormula[0] ) { - tot_len += MakeMult( mult+1, "", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - tot_len += MakeHillFormulaString( pINChI_Prev->szHillFormula, pStr + tot_len, - nStrLen-tot_len, bOverflow); - } - } - pINChI_Prev = pINChI; - mult = 0; /* we do not know whether the item is empty */ - } - if ( bEqToTaut ) { - pStr[tot_len=tot_len_inp] = '\0'; - } - return tot_len; -} -/***************************************************************************/ -int str_Connections(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int ATOM_MODE, int num_components, int bUseMulipliers) -{ - int i, ii; - INCHI_SORT *is, *is0; - INChI *pINChI, *pINChI_Prev; - int mult, eq2prev, bNext, tot_len_inp, nNumEmpty; - - if ( !(is0 = pINChISort) ) { - return tot_len; - } - i = 0; - pINChI_Prev = (0 <= (ii=GET_II(bOutType,is0)))? is0->pINChI[ii] : NULL; - is = NULL; - mult = 0; - bNext = 0; - tot_len_inp = tot_len; - nNumEmpty = 0; - for ( i++; i <= num_components; i ++ ) { - pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; - eq2prev = bUseMulipliers && - pINChI && pINChI_Prev && pINChI->lenConnTable > 1 && - pINChI_Prev->lenConnTable==pINChI->lenConnTable && - !memcmp( pINChI_Prev->nConnTable, pINChI->nConnTable, - pINChI_Prev->lenConnTable*sizeof(pINChI->nConnTable[0]) ); - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else - if ( pINChI_Prev ) { - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Prev && pINChI_Prev->lenConnTable > 1 ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - tot_len += MakeCtStringNew( pINChI_Prev->nConnTable, pINChI_Prev->lenConnTable, 0, - NULL, pINChI_Prev->nNumberOfAtoms, - pStr + tot_len, nStrLen-tot_len, ATOM_MODE, bOverflow); - } else { - nNumEmpty ++; - } - } - pINChI_Prev = pINChI; - mult = 0; /* we do not know whether the item is empty */ - } - if ( nNumEmpty == num_components && tot_len > tot_len_inp ) { - tot_len = tot_len_inp; - pStr[tot_len] = '\0'; - } - return tot_len; -} -/***************************************************************************/ -int str_H_atoms(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int ATOM_MODE, int TAUT_MODE, - int num_components, int bUseMulipliers) -{ - int i, j, ii, len_H; - INCHI_SORT *is, *is0; - INChI *pINChI, *pINChI_Prev; - int mult, eq2prev, bNext, bNotEmpty, nNumEmpty, tot_len_inp; - - nNumEmpty = 0; - tot_len_inp = tot_len; - is0 = pINChISort; - is = NULL; - i = 0; - pINChI_Prev = (0 <= (ii=GET_II(bOutType,is0)))? is0->pINChI[ii] : NULL; - mult = 0; - bNext = 0; - for ( i++; i <= num_components; i ++) { - pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; - /*========== compare to previous ============*/ - eq2prev = bUseMulipliers && - pINChI && pINChI_Prev && (pINChI->nNumberOfAtoms > 0 || pINChI->lenTautomer>1) && - pINChI_Prev->nNumberOfAtoms==pINChI->nNumberOfAtoms && - (!pINChI_Prev->nNumberOfAtoms || !memcmp( pINChI_Prev->nNum_H, pINChI->nNum_H, - pINChI_Prev->nNumberOfAtoms*sizeof(pINChI->nNum_H[0]) ) ) && - !CompareTautNonIsoPartOfINChI( pINChI_Prev, pINChI ); - - if ( eq2prev && pINChI_Prev->lenTautomer <= 1 ) { - /* make sure it is not empty */ - eq2prev = 0; - for ( j = 0; j < pINChI_Prev->nNumberOfAtoms; j ++ ) { - if ( pINChI_Prev->nNum_H[j] ) { - eq2prev = 1; - break; - } - } - } - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else - if ( pINChI_Prev ) { - /* delimiter */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - /* verify non-empty */ - bNotEmpty = 0; - if ( pINChI_Prev ) { - bNotEmpty = (pINChI_Prev->lenTautomer > 1); - if ( !bNotEmpty ) { - for ( j = 0; j < pINChI_Prev->nNumberOfAtoms; j ++ ) { - if ( pINChI_Prev->nNum_H[j] ) { - bNotEmpty = 1; - break; - } - } - } - } - if ( bNotEmpty ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - /* H-atoms */ - tot_len += (len_H = MakeHString( 0, pINChI_Prev->nNum_H, pINChI_Prev->nNumberOfAtoms, - pStr + tot_len, nStrLen-tot_len, ATOM_MODE, bOverflow )); - /* tautomeric groups */ - tot_len += MakeTautString( pINChI_Prev->nTautomer, pINChI_Prev->lenTautomer, (0!=len_H), - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } else { - nNumEmpty ++; - } - } - pINChI_Prev = pINChI; - mult = 0; /* we do not know whether the item is empty */ - } - if ( nNumEmpty == num_components && tot_len > tot_len_inp ) { - tot_len = tot_len_inp; - pStr[tot_len] = '\0'; - } - return tot_len; -} -/***************************************************************************/ -int str_Charge2(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int num_components, - int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) -{ - int i, ii, ii2; - INCHI_SORT *is, *is2, *is0, *is20; - INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; - int nTotalCharge, nTotalCharge_Prev, nTotalCharge_Taut, nTotalCharge_Taut_Prev; - int mult, eq2prev, eq2taut, eq2tautPrev, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - pINChI_Taut = NULL; - pINChI_Prev = NULL; - pINChI_Taut_Prev = NULL; - mult = 0; - bNext = 0; - is = NULL; - is2 = NULL; - is0 = pINChISort; - is20 = bSecondNonTautPass? pINChISort2 : NULL; - eq2taut = 0; /* may be non-zero only on the 2nd (non-taut) pass */ - eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; - for ( i = 0; i <= num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; - /*================ compare sp3 to previous =====================*/ - if ( bSecondNonTautPass ) { - /* component that was output on the 1st pass */ - pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; - } - /*========= if bSecondNonTautPass then compare non-iso non-taut stereo to non-iso taut ========*/ - eq2taut = 0; - if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions ) { - eq2taut = pINChI && pINChI_Taut && !pINChI_Taut->bDeleted && - (nTotalCharge = pINChI->nTotalCharge) && (nTotalCharge_Taut = pINChI_Taut->nTotalCharge) && - nTotalCharge == nTotalCharge_Taut; - eq2taut = eq2taut? (iiEQU | iitNONTAUT) : 0; - } - if ( eq2taut ) { - /* we may be here only in case of the second (non-taut) pass */ - /* current non-taut stereo has been found to be same as tautomeric */ - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - /* previous component exists; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( nTotalCharge_Prev = pINChI_Prev->nTotalCharge ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - tot_len += sprintf( pStr + tot_len, "%+d", nTotalCharge_Prev ); - } - } else - if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms && !pINChI_Taut_Prev->bDeleted ) { - /* previous non-taut component exists only in taut list */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - } - /* we have found pINChI->nTotalCharge same as in pINChI_Taut */ - /* output this (current) equivalence as '*', that is, same as tautomeric */ - /* that was printed on the 1st pass. */ - - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - - pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ - pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ - mult = 0; - eq2tautPrev = 1; /* pINChI_Prev sp2 does not exist */ - } else - if ( eq2tautPrev ) { - /* at this point pINChI_Prev does not exist; however, pINChI */ - /*might have been discovered and it is different from pINChI_Taut */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - eq2tautPrev = 0; - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; - } else { - /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp3 */ - /*================ compare sp3 to previous =====================*/ - eq2prev =bUseMulipliers && - pINChI && pINChI_Prev && - (nTotalCharge = pINChI->nTotalCharge) && (nTotalCharge_Prev = pINChI_Prev->nTotalCharge) && - nTotalCharge == nTotalCharge_Prev; - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { - if ( nTotalCharge_Prev = pINChI_Prev->nTotalCharge ) { - /* pINChI_Prev exists and has charge info */ - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - tot_len += sprintf( pStr + tot_len, "%+d", nTotalCharge_Prev ); - } - /* else charge is not present in pINChI_Prev */ - } else - if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms && !pINChI_Taut_Prev->bDeleted ) { - if ( nTotalCharge_Taut_Prev = pINChI_Taut_Prev->nTotalCharge ) { - /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ - /* and it has charge info. This info has already been printed in the main section */ - /* - tot_len += MakeDelim( sIdenticalValues, pStr + tot_len, nStrLen-tot_len, bOverflow); - */ - ; /* pINChI_Taut_Prev sp3 info was output in the main stereo section */ - } else { - ; /* pINChI_Taut_Prev exists and has not sp3 info */ - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; /* */ - } -#endif - } - pINChI_Prev = pINChI; - pINChI_Taut_Prev = pINChI_Taut; - mult = 0; /* we do not know whether the item is empty */ - } - } - return tot_len; -} -/***************************************************************************/ -int str_FixedH_atoms(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int ATOM_MODE, int num_components, int bUseMulipliers) -{ - int i, j, ii, nNumEmpty; - INCHI_SORT *is, *is0; - INChI *pINChI, *pINChI_Prev; - int mult, eq2prev, bNext, bNotEmpty, tot_len_inp; - - is = NULL; - is0 = pINChISort; - i = 0; - pINChI_Prev = (0 <= (ii=GET_II(bOutType,is0)))? is0->pINChI[ii] : NULL; - mult = 0; - bNext = 0; - nNumEmpty = 0; - tot_len_inp = tot_len; - for ( i++; i <= num_components; i ++ ) { - /* only non-tautomeric representation of tautomeric */ - pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; - /*================ compare fixed H to previous =====================*/ - eq2prev =bUseMulipliers && - pINChI && pINChI_Prev && pINChI->nNumberOfAtoms > 0 && - pINChI_Prev->nNumberOfAtoms==pINChI->nNumberOfAtoms && - !memcmp( pINChI_Prev->nNum_H_fixed, pINChI->nNum_H_fixed, - pINChI_Prev->nNumberOfAtoms*sizeof(pINChI->nNum_H_fixed[0]) ); - if ( eq2prev ) { - /* make sure it is not empty */ - eq2prev = 0; - for ( j = 0; j < pINChI_Prev->nNumberOfAtoms; j ++ ) { - if ( pINChI_Prev->nNum_H_fixed[j] ) { - eq2prev = 1; - break; - } - } - } - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - /* print pINChI_Prev */ - /* delimiter */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Prev ) { - /* verify it is not empty */ - bNotEmpty = 0; - for ( j = 0; j < pINChI_Prev->nNumberOfAtoms; j ++ ) { - if ( pINChI_Prev->nNum_H_fixed[j] ) { - bNotEmpty = 1; - break; - } - } - if ( bNotEmpty ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - /* H-atoms-fixed */ - tot_len += MakeHString( 0, pINChI_Prev->nNum_H_fixed, pINChI_Prev->nNumberOfAtoms, - pStr + tot_len, nStrLen-tot_len, ATOM_MODE, bOverflow ); - } else { - nNumEmpty ++; - } - } - } - pINChI_Prev = pINChI; - mult = 0; /* we do not know whether the item is empty */ - } - if ( nNumEmpty == num_components && tot_len > tot_len_inp ) { - tot_len = tot_len_inp; - pStr[tot_len] = '\0'; - } - return tot_len; -} -/***************************************************************************/ -int str_AuxNumb(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, - int bSecondNonTautPass, int bOmitRepetitions) -{ - int i, ii, ii2; - INCHI_SORT *is, *is0 /*, *is2*/; - INChI *pINChI, *pINChI_Taut=NULL; - INChI_Aux *pINChI_Aux, *pINChI_Aux_Taut=NULL; - int eq2taut, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - bNext = 0; - /*is2 = bSecondNonTautPass? pINChISort2 : NULL;*/ - eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; - is = NULL; - if ( !(is0 = pINChISort) ) { - return tot_len; - } - for ( i = 0; i < num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - is=is0+i; - pINChI = ( 0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; - pINChI_Aux = pINChI? is->pINChI_Aux[ii] : NULL; - /*================ to compare to previously printed =====================*/ - if ( bSecondNonTautPass ) { - /* component that was printed on the 1st pass */ - pINChI_Taut = (0 <= (ii2=GET_II(OUT_T1,is)))? is->pINChI[ii2] : NULL; - pINChI_Aux_Taut = pINChI_Taut? is->pINChI_Aux[ii2] : NULL; - } - eq2taut = 0; - /*========= if bSecondNonTautPass then compare iso non-taut stereo to other stereo ========*/ - if ( bSecondNonTautPass && bOmitRepetitions && pINChI && pINChI_Aux && pINChI_Aux->nNumberOfAtoms > 0 ) { - /* compare non-tautomeric numbering to: - * a) tautomeric numbering - */ - /* a) compare non-tautomeric numbering to tautomeric numbering */ - if ( !eq2taut ) { - eq2taut = pINChI_Taut && !pINChI_Taut->bDeleted && - Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM, pINChI_Aux_Taut, EQL_NUM ); - /* numbering non-taut = taut numbering */ - eq2taut = eq2taut? ( iiNUMB | iitNONTAUT ) : 0; - } - } - if ( eq2taut ) { - /* we have found another (previously printed) layer of the current component equal to this layer */ - /* output this (current) equivalence mark = EquString(eq2taut) */ - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - /* current layer is different from previously printed layers of the current component */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI && pINChI_Aux && pINChI_Aux->nNumberOfAtoms ) { - tot_len += MakeCtString( pINChI_Aux->nOrigAtNosInCanonOrd, - pINChI_Aux->nNumberOfAtoms, 0, NULL, 0, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - } - } - if ( multPrevEquStr && pPrevEquStr ) { - /* the new EqStr of the last item has not been printed; output it now */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - return tot_len; -} -/***************************************************************************/ -int str_AuxTgroupEqu(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bUseMulipliers) -{ - int i, ii; - INCHI_SORT *is, *is0; - INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev; - int mult, eq2prev, bNext; - - is0 = pINChISort; - is = NULL; - i = 0; - pINChI_Aux_Prev = (0 <= (ii=GET_II(bOutType,is0)))? is0->pINChI_Aux[ii] : NULL; - mult = 0; - bNext = 0; - for ( i++; i <= num_components; i ++ ) { - pINChI_Aux = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI_Aux[ii] : NULL; - eq2prev = bUseMulipliers && - Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_TG, pINChI_Aux_Prev, EQL_EQU_TG ); - if ( eq2prev ) { - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfTGroups && - bHasEquString( pINChI_Aux_Prev->nConstitEquTGroupNumbers, pINChI_Aux_Prev->nNumberOfTGroups) ) { - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - tot_len += MakeEquString( pINChI_Aux_Prev->nConstitEquTGroupNumbers, pINChI_Aux_Prev->nNumberOfTGroups, 0, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } - } - pINChI_Aux_Prev = pINChI_Aux; - mult = 0; /* we do not know whether the item is empty */ - } - return tot_len; -} - -/***************************************************************************/ -int str_AuxChargeRadVal(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bUseMulipliers) -{ - int i, ii; - INCHI_SORT *is, *is0; - INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev; - int mult, eq2prev, bNext; - - pINChI_Aux_Prev = NULL; - mult = 0; - bNext = 0; - is = NULL; - is0 = pINChISort; - for ( i = 0; i <= num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - pINChI_Aux = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI_Aux[ii] : NULL; - /* check whether pINChI_Aux and pINChI_Aux_Prev have identical info */ - eq2prev = bUseMulipliers && - EqlOrigInfo( pINChI_Aux, pINChI_Aux_Prev ); - if ( eq2prev ) { - /* eq. info is same and non-trivial */ - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else - if ( i ) { - /* pINChI_Aux info is either different or trivial. Output pINChI_Aux_Prev anyway */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { - if ( bHasOrigInfo( pINChI_Aux_Prev->OrigInfo, pINChI_Aux_Prev->nNumberOfAtoms ) ) { - /* pINChI_Aux_Prev exists and has orig. info info */ - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - tot_len += MakeCRVString( pINChI_Aux_Prev->OrigInfo, pINChI_Aux_Prev->nNumberOfAtoms, 0, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } else { - ; /* pINChI_Aux_Prev exists and has only trivial info */ - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; /* */ - } -#endif - } - pINChI_Aux_Prev = pINChI_Aux; - mult = 0; /* we do not know whether the item is empty */ - } - return tot_len; -} -/******************************************************************************************/ -int bin_AuxTautTrans(INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, - AT_NUMB **pTrans_n, AT_NUMB **pTrans_s, int bOutType, int num_components) -{ - int i, ii, ii2, ret; - INCHI_SORT *is, *is2, *is0, *is20; - INChI *pINChI, *pINChI_Taut; - AT_NUMB *nTrans_n = NULL; - AT_NUMB *nTrans_s = NULL; - - ret = 0; - is0 = pINChISort; - is20 = pINChISort2; - /* pass 1: save new non-taut numbering */ - for ( i = 0; i < num_components; i ++ ) { - is=is0+i; - is2=is20+i; - pINChI = ( 0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; - pINChI_Taut = ( 0 <= (ii2=GET_II(OUT_T1,is2)))? is2->pINChI[ii2] : NULL; - if ( pINChI && pINChI->nNumberOfAtoms > 0 && - pINChI_Taut && pINChI_Taut->nNumberOfAtoms > 0 && - /* different components save equal new ord. numbers: */ - is->ord_number != is2->ord_number ) { - if ( (nTrans_n && nTrans_s) || - (nTrans_n = (AT_NUMB *)inchi_calloc( num_components+1, sizeof(nTrans_n[0]))) && - (nTrans_s = (AT_NUMB *)inchi_calloc( num_components+1, sizeof(nTrans_s[0]))) ) { - /* new ordering number for original non-tautomeric component number is->ord_number */ - nTrans_n[is->ord_number] = /*nTrans_t[is2->ord_number] =*/ i+1; - } - } - } - if ( nTrans_n && nTrans_s ) { - /* pass 2: get new taut numbering, retrieve new non-taut and save the transposition */ - for ( i = 0; i < num_components; i ++ ) { - is=is0+i; - is2=is20+i; - pINChI = ( 0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; - pINChI_Taut = ( 0 <= (ii2=GET_II(OUT_T1,is2)))? is2->pINChI[ii2] : NULL; - if ( pINChI && pINChI->nNumberOfAtoms > 0 && - pINChI_Taut && pINChI_Taut->nNumberOfAtoms > 0 && - is->ord_number != is2->ord_number && - nTrans_n[is2->ord_number] ) { - /* nTrans_n[is2->ord_number] is new ordering number of - the non-taut representation of the tautomeric component - that has new ord number i+1 and orig ordering number is2->ord_number. - Old numbers start from 0, new start from 1 - */ - - /* n = nTrans_s[t]: taut component #t is in position #n of the non-taut representation */ - nTrans_s[i+1] = nTrans_n[is2->ord_number]; - } - } - *pTrans_n = nTrans_n; - *pTrans_s = nTrans_s; - ret = 1; - } else { - if ( nTrans_n ) { - inchi_free( nTrans_n ); - ret = -1; - } - if ( nTrans_s ) { - inchi_free( nTrans_s ); - ret = -1; - } - } - return ret; -} -/******************************************************************************************/ -int str_AuxTautTrans(AT_NUMB *nTrans_n, AT_NUMB *nTrans_s, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int TAUT_MODE, int num_components) -{ - int i, k, len, j; - - if ( nTrans_n && nTrans_s ) { - /* print the transposition, cycle after cycle */ - for ( i = 1; i <= num_components; i ++ ) { - if ( nTrans_s[i] ) { - /* get one cycle of the transposition */ - for ( j = i, len = 0; (k = nTrans_s[j]); j = k, len ++ ) { - nTrans_n[len] = j; /* save the transposition */ - nTrans_s[j] = 0; /* clear used element to avoid repetitions */ - } - /* print one cycle of the transposition */ - tot_len += MakeDelim( "(", pStr + tot_len, nStrLen-tot_len, bOverflow); - tot_len += MakeCtString( nTrans_n, len, 0, NULL, 0, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - tot_len += MakeDelim( ")", pStr + tot_len, nStrLen-tot_len, bOverflow); - } - } - } - if ( nTrans_n ) - inchi_free( nTrans_n ); - if ( nTrans_s ) - inchi_free( nTrans_s ); - return tot_len; -} -/***************************************************************************/ -int str_StereoAbsInv(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int num_components) -{ - int i, j, ii; - INCHI_SORT *is, *is0; - INChI_Stereo *Stereo; - INChI *pINChI; - - is = NULL; - is0 = pINChISort; - - for ( i = 0; !*bOverflow && i < num_components; i ++ ) { - is=is0+i; - pINChI = (0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; - if ( pINChI && (Stereo = pINChI->Stereo) && (j=Stereo->nCompInv2Abs) ) { - tot_len += MakeDelim( j<0? "1":"0", pStr + tot_len, nStrLen-tot_len, bOverflow); - } else { - tot_len += MakeDelim( ".", pStr + tot_len, nStrLen-tot_len, bOverflow); - } - } - - return tot_len; -} -/***************************************************************************/ -int str_IsoStereoAbsInv(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int num_components) -{ - int i, j, ii; - INCHI_SORT *is, *is0; - INChI_Stereo *Stereo; - INChI *pINChI; - - is = NULL; - is0 = pINChISort; - - for ( i = 0; !*bOverflow && i < num_components; i ++ ) { - is=is0+i; - pINChI = (0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; - if ( pINChI && (Stereo = pINChI->StereoIsotopic) && (j=Stereo->nCompInv2Abs) ) { - tot_len += MakeDelim( j<0? "1":"0", pStr + tot_len, nStrLen-tot_len, bOverflow); - } else { - tot_len += MakeDelim( ".", pStr + tot_len, nStrLen-tot_len, bOverflow); - } - } - - return tot_len; -} -/***************************************************************************/ -int str_AuxIsoTgroupEqu(INCHI_SORT *pINChISort, char *pStr, int nStrLen, int tot_len, - int *bOverflow, int bOutType, int TAUT_MODE, int num_components, int bOmitRepetitions, int bUseMulipliers) -{ - int i, ii; - INCHI_SORT *is, *is0; - INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev; - int mult, eq2prev, eq2taut, eq2tautPrev, bNext; - const char *pPrevEquStr, *pCurrEquStr; - int multPrevEquStr; - pINChI_Aux = NULL; - pINChI_Aux_Prev = NULL; - mult = 0; - bNext = 0; - is = NULL; - is0 = pINChISort; - eq2taut = 0; /* equal to non-isotopic equivalence */ - eq2tautPrev = 1; /* pINChI_Aux_Prev (previous pINChI_Aux) does not exist */ - pPrevEquStr = NULL; /*, *pCurrEquStr;*/ - multPrevEquStr = 0; - for ( i = 0; i <= num_components; i ++ ) { - /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ - pINChI_Aux = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI_Aux[ii] : NULL; - /*================ compare iso non-taut equivalence info to non-iso taut ========*/ - eq2taut = 0; - if ( bOmitRepetitions && pINChI_Aux && pINChI_Aux->bIsIsotopic ) { - /************************************************** - * compare isotopic tautomeric equivalence to: - * a) non-isotopic tautomeric - */ - /* compare isotopic t-group equivalence to non-isotopic */ - eq2taut = Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_TG | EQL_EQU_ISO, pINChI_Aux, EQL_EQU_TG ); - /* equ taut-isotopic = tautomeric, same as for isotopic atom equivalence info*/ - eq2taut = eq2taut? (iiEQU | iitISO) : 0; - } - if ( eq2taut ) { - /* current isotopic t-group equivalence has been found to be same as non-isotopic */ - if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { - /* previous component exists */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( bHasEquString( pINChI_Aux_Prev->nConstitEquIsotopicTGroupNumbers, pINChI_Aux_Prev->nNumberOfTGroups) ) { - /* output previous component(s) equivalence since it was found to be non-trivial */ - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - tot_len += MakeEquString( pINChI_Aux_Prev->nConstitEquIsotopicTGroupNumbers, pINChI_Aux_Prev->nNumberOfTGroups, 0, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } else { - ; /* pINChI_Aux_Prev exists and does not have non-trivial t-group equivalence info */ - } - } - /* we have found pINChI_Aux->pINChI_Aux->nConstitEquIsotopicTGroupNumbers same as in pINChI_Aux->nConstitEquTGroupNumbers */ - pCurrEquStr = EquString(eq2taut); - if ( multPrevEquStr && pPrevEquStr ) { - if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { - multPrevEquStr ++; - } else { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - } else { - pPrevEquStr = pCurrEquStr; - multPrevEquStr = 1; - } - pINChI_Aux_Prev = NULL; /* pINChI_Aux_Prev has already been output */ - mult = 0; - eq2tautPrev = 1; - } else - if ( eq2tautPrev ) { - /* at this point pINChI_Aux_Prev does not exist; however, pINChI_Aux */ - /* might have been discovered and it may be different from non-isotopic */ - if ( multPrevEquStr && pPrevEquStr ) { - /* new EqStr is different; output it */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - tot_len += MakeEqStr( pPrevEquStr, multPrevEquStr, pStr + tot_len, nStrLen-tot_len, bOverflow); - pPrevEquStr = NULL; - multPrevEquStr = 0; - } - eq2tautPrev = 0; - pINChI_Aux_Prev = pINChI_Aux; - mult = 0; - } else { - /* check whether pINChI_Aux and pINChI_Aux_Prev have identical non-trivial isotopic t-group equivalence info */ - eq2prev = bUseMulipliers && Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_TG | EQL_EQU_ISO, pINChI_Aux_Prev, EQL_EQU_TG | EQL_EQU_ISO ); - if ( eq2prev ) { - /* eq. info is same and non-trivial */ - mult ++; /* mult = (number of non-empty equal items)-1 */ - continue; - } else { - /* pINChI_Aux eq. info is either different or trivial. Output pINChI_Aux_Prev anyway */ - if ( bNext ++ ) { - tot_len += MakeDelim( sCompDelim, pStr + tot_len, nStrLen-tot_len, bOverflow); - } - if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { - if ( bHasEquString( pINChI_Aux_Prev->nConstitEquIsotopicTGroupNumbers, pINChI_Aux_Prev->nNumberOfTGroups) ) { - /* pINChI_Aux_Prev exists and has equivalence info */ - tot_len += MakeMult( mult+1, "*", pStr + tot_len, nStrLen-tot_len, 0, bOverflow); - tot_len += MakeEquString( pINChI_Aux_Prev->nConstitEquIsotopicTGroupNumbers, pINChI_Aux_Prev->nNumberOfTGroups, 0, - pStr + tot_len, nStrLen-tot_len, TAUT_MODE, bOverflow); - } else { - ; /* pINChI_Aux_Prev exists and has only trivial equivalence info */ - } - } -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - else { - int stop = 1; /* */ - } -#endif - } - pINChI_Aux_Prev = pINChI_Aux; - mult = 0; /* we do not know whether the item is empty */ - } - } - return tot_len; -} +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include + +#include "mode.h" +#include "ichimain.h" +#include "ichimake.h" +#include "ichi_io.h" + + +/* + + Generate substrings of whole-structure InChI/AuxInfo + + */ + + +/* + Produce Hill formula substring of the whole structure InChI string +*/ +int str_HillFormula( INCHI_SORT *pINChISort, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int num_components, + int bUseMulipliers) +{ + int i, ii, nUsedLength0; + INCHI_SORT *is, *is0; + INChI *pINChI, *pINChI_Prev; + int mult, eq2prev, bNext; + + nUsedLength0 = strbuf->nUsedLength; + + if ( !(is0 = pINChISort) ) + { + return nUsedLength0; + } + i = 0; + pINChI_Prev = (0 <= (ii=GET_II(bOutType,is0)))? is0->pINChI[ii] : NULL; + mult = 0; + bNext = 0; + + /* For each connected component */ + for ( i++; i <= num_components; i ++ ) + { + + pINChI = ( i < num_components && + ( is=is0+i, 0 <= (ii=GET_II(bOutType,is)) ) + ) ? is->pINChI[ii] + : NULL; + + eq2prev = bUseMulipliers && + pINChI && + pINChI_Prev && + pINChI->szHillFormula && + pINChI_Prev->szHillFormula && + pINChI->szHillFormula[0] && + !strcmp(pINChI_Prev->szHillFormula, pINChI->szHillFormula); + + if ( eq2prev ) + { + mult ++; /* mult = (number of non-empty equal items)-1 */ + continue; + } + else + { + int ok; + if ( bNext ++ ) + { + MakeDelim( ".", strbuf, bOverflow); + } + + ok = pINChI_Prev && + pINChI_Prev->szHillFormula && + pINChI_Prev->szHillFormula[0]; + + if ( ok ) + { + MakeMult( mult+1, "", strbuf, 0, bOverflow); + MakeHillFormulaString( pINChI_Prev->szHillFormula, + strbuf, + bOverflow); + } + } + pINChI_Prev = pINChI; + mult = 0; /* we do not know whether the item is empty */ + } + + return ( strbuf->nUsedLength - nUsedLength0 ); +} + + +/* + Produce Hill formula substring of the whole structure InChI string: + 2nd appearance (FixedH). +*/ +int str_HillFormula2( INCHI_SORT *pINChISort /* non-taut */, + INCHI_SORT *pINChISort2 /* taut */, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int num_components, + int bUseMulipliers) +{ + int i, ii, ii2, nUsedLength0; + INCHI_SORT *is, *is2, *is0, *is20; + INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; + int mult, eq2prev, bNext, bEqToTaut; + + is = NULL; + is2 = NULL; + is0 = pINChISort; + is20 = pINChISort2; + i = 0; + nUsedLength0 = strbuf->nUsedLength; + + pINChI_Prev = (0 <= (ii=GET_II(bOutType,is0)))? is0->pINChI[ii] : NULL; + pINChI_Taut_Prev = (0 <= (ii2=GET_II(OUT_T1,is20)))? is20->pINChI[ii2] : NULL; + mult = 0; + bNext = 0; + bEqToTaut = 1; + bEqToTaut = bEqToTaut && + pINChI_Prev && pINChI_Taut_Prev && !pINChI_Taut_Prev->bDeleted && + pINChI_Prev->szHillFormula && pINChI_Taut_Prev->szHillFormula && + !strcmp(pINChI_Prev->szHillFormula, pINChI_Taut_Prev->szHillFormula); + + /* For each connected component */ + for ( i++; i <= num_components; i ++ ) + { + pINChI = (i < num_components && (is=is0+i, 0 <= (ii =GET_II(bOutType,is)))) ? is->pINChI[ii] : NULL; + pINChI_Taut = (i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2)))) ? is2->pINChI[ii2] : NULL; + if ( bEqToTaut && (pINChI || pINChI_Taut) ) + { + bEqToTaut = pINChI && pINChI_Taut && !pINChI_Taut->bDeleted && + pINChI->szHillFormula && pINChI_Taut->szHillFormula && + !strcmp(pINChI->szHillFormula, pINChI_Taut->szHillFormula); + } + eq2prev = bUseMulipliers && + pINChI && pINChI_Prev && + pINChI->szHillFormula && + pINChI_Prev->szHillFormula && + pINChI->szHillFormula[0] && + !strcmp(pINChI_Prev->szHillFormula, pINChI->szHillFormula); + if ( eq2prev ) + { + mult ++; /* mult = (number of non-empty equal items)-1 */ + continue; + } else + { + if ( bNext ++ ) + { + MakeDelim( ".", strbuf, bOverflow); + } + if ( pINChI_Prev && pINChI_Prev->szHillFormula && pINChI_Prev->szHillFormula[0] ) + { + MakeMult( mult+1, "", strbuf, 0, bOverflow); + MakeHillFormulaString( pINChI_Prev->szHillFormula, strbuf, bOverflow); + } + } + pINChI_Prev = pINChI; + mult = 0; /* we do not know whether the item is empty */ + } + if ( bEqToTaut ) + strbuf->nUsedLength = nUsedLength0; + { + strbuf->pStr[strbuf->nUsedLength] = '\0'; + } + + return ( strbuf->nUsedLength - nUsedLength0 ); +} + + +/* + Produce connections substring of InChI string for the whole structure +*/ +int str_Connections( CANON_GLOBALS *pCG, + INCHI_SORT *pINChISort, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int ATOM_MODE, + int num_components, + int bUseMulipliers) +{ + int i, ii, nUsedLength0; + INCHI_SORT *is, *is0; + INChI *pINChI, *pINChI_Prev; + int mult, eq2prev, bNext, nNumEmpty; + + nUsedLength0 = strbuf->nUsedLength; + if ( !(is0 = pINChISort) ) { + return nUsedLength0; + } + i = 0; + pINChI_Prev = (0 <= (ii=GET_II(bOutType,is0)))? is0->pINChI[ii] : NULL; + is = NULL; + mult = 0; + bNext = 0; + nNumEmpty = 0; + + /* For each connected component... */ + for ( i++; i <= num_components; i ++ ) + { + pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; + eq2prev = bUseMulipliers && + pINChI && pINChI_Prev && pINChI->lenConnTable > 1 && + pINChI_Prev->lenConnTable==pINChI->lenConnTable && + !memcmp( pINChI_Prev->nConnTable, pINChI->nConnTable, + pINChI_Prev->lenConnTable*sizeof(pINChI->nConnTable[0]) ); + if ( eq2prev ) + { + mult ++; /* mult = (number of non-empty equal items)-1 */ + continue; + } + else + if ( pINChI_Prev ) + { + if ( bNext ++ ) + { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( pINChI_Prev && pINChI_Prev->lenConnTable > 1 ) + { + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + MakeCtStringNew( pCG, pINChI_Prev->nConnTable, + pINChI_Prev->lenConnTable, + 0, NULL, + pINChI_Prev->nNumberOfAtoms, + strbuf, ATOM_MODE, bOverflow); + } + else + { + nNumEmpty ++; + } + } + pINChI_Prev = pINChI; + mult = 0; /* we do not know whether the item is empty */ + } + if ( nNumEmpty == num_components && strbuf->nUsedLength > nUsedLength0 ) + { + strbuf->nUsedLength = nUsedLength0; + strbuf->pStr[strbuf->nUsedLength] = '\0'; + } + /* + if ( nNumEmpty == num_components && tot_len > tot_len_inp ) { + tot_len = tot_len_inp; + strbuf->pStr[tot_len] = '\0'; + } + */ + + return ( strbuf->nUsedLength - nUsedLength0 ); +} + + +/* + Produce H[ydrogens] substring of the whole structure InChI string +*/ +int str_H_atoms( INCHI_SORT *pINChISort, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int ATOM_MODE, + int TAUT_MODE, + int num_components, + int bUseMulipliers) +{ + int i, j, ii, len_H, nUsedLength0; + INCHI_SORT *is, *is0; + INChI *pINChI, *pINChI_Prev; + int mult, eq2prev, bNext, bNotEmpty, nNumEmpty; + + nNumEmpty = 0; + is0 = pINChISort; + is = NULL; + i = 0; + pINChI_Prev = (0 <= (ii=GET_II(bOutType,is0)))? is0->pINChI[ii] : NULL; + mult = 0; + bNext = 0; + nUsedLength0 = strbuf->nUsedLength; + + /* For each connected component... */ + for ( i++; i <= num_components; i ++) + { + + pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; + /*========== compare to previous ============*/ + eq2prev = bUseMulipliers && + pINChI && pINChI_Prev && (pINChI->nNumberOfAtoms > 0 || pINChI->lenTautomer>1) && + pINChI_Prev->nNumberOfAtoms==pINChI->nNumberOfAtoms && + (!pINChI_Prev->nNumberOfAtoms || !memcmp( pINChI_Prev->nNum_H, pINChI->nNum_H, + pINChI_Prev->nNumberOfAtoms*sizeof(pINChI->nNum_H[0]) ) ) && + !CompareTautNonIsoPartOfINChI( pINChI_Prev, pINChI ); + + if ( eq2prev && pINChI_Prev->lenTautomer <= 1 ) { + /* make sure it is not empty */ + eq2prev = 0; + for ( j = 0; j < pINChI_Prev->nNumberOfAtoms; j ++ ) { + if ( pINChI_Prev->nNum_H[j] ) { + eq2prev = 1; + break; + } + } + } + if ( eq2prev ) { + mult ++; /* mult = (number of non-empty equal items)-1 */ + continue; + } else + if ( pINChI_Prev ) { + /* delimiter */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + /* verify non-empty */ + bNotEmpty = 0; + if ( pINChI_Prev ) { + bNotEmpty = (pINChI_Prev->lenTautomer > 1); + if ( !bNotEmpty ) { + for ( j = 0; j < pINChI_Prev->nNumberOfAtoms; j ++ ) { + if ( pINChI_Prev->nNum_H[j] ) { + bNotEmpty = 1; + break; + } + } + } + } + if ( bNotEmpty ) { + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + /* H-atoms */ + len_H = MakeHString( 0, pINChI_Prev->nNum_H, pINChI_Prev->nNumberOfAtoms, + strbuf, ATOM_MODE, bOverflow ); + /* tautomeric groups */ + MakeTautString( pINChI_Prev->nTautomer, pINChI_Prev->lenTautomer, (0!=len_H), + strbuf, TAUT_MODE, bOverflow); + } else { + nNumEmpty ++; + } + } + pINChI_Prev = pINChI; + mult = 0; /* we do not know whether the item is empty */ + } + if ( nNumEmpty == num_components && strbuf->nUsedLength > nUsedLength0 ) + { + strbuf->nUsedLength = nUsedLength0; + strbuf->pStr[nUsedLength0] = '\0'; + } + /* + if ( nNumEmpty == num_components && tot_len > tot_len_inp ) { + tot_len = tot_len_inp; + strbuf->pStr[tot_len] = '\0'; + } + */ + + return ( strbuf->nUsedLength - nUsedLength0 ); +} + + +/* + Produce charge substring of the whole structure InChI string +*/ +int str_Charge2( INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, int bOutType, + int num_components, + int bSecondNonTautPass, + int bOmitRepetitions, + int bUseMulipliers) +{ + int i, ii, ii2, nUsedLength0; + INCHI_SORT *is, *is2, *is0, *is20; + INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; + int nTotalCharge, nTotalCharge_Prev, nTotalCharge_Taut, nTotalCharge_Taut_Prev; + int mult, eq2prev, eq2taut, eq2tautPrev, bNext; + const char *pPrevEquStr, *pCurrEquStr; + int multPrevEquStr; + pINChI_Taut = NULL; + pINChI_Prev = NULL; + pINChI_Taut_Prev = NULL; + mult = 0; + bNext = 0; + is = NULL; + is2 = NULL; + is0 = pINChISort; + is20 = bSecondNonTautPass? pINChISort2 : NULL; + eq2taut = 0; /* may be non-zero only on the 2nd (non-taut) pass */ + eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ + pPrevEquStr = NULL; /*, *pCurrEquStr;*/ + multPrevEquStr = 0; + nUsedLength0 = strbuf->nUsedLength; + + /* For each connected component... */ + for ( i = 0; i <= num_components; i ++ ) + { + + /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ + pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; + /*================ compare sp3 to previous =====================*/ + if ( bSecondNonTautPass ) { + /* component that was output on the 1st pass */ + pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; + } + /*========= if bSecondNonTautPass then compare non-iso non-taut stereo to non-iso taut ========*/ + eq2taut = 0; + if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions ) { + eq2taut = pINChI && pINChI_Taut && !pINChI_Taut->bDeleted && + (nTotalCharge = pINChI->nTotalCharge) && (nTotalCharge_Taut = pINChI_Taut->nTotalCharge) && + nTotalCharge == nTotalCharge_Taut; + eq2taut = eq2taut? (iiEQU | iitNONTAUT) : 0; + } + if ( eq2taut ) { + /* we may be here only in case of the second (non-taut) pass */ + /* current non-taut stereo has been found to be same as tautomeric */ + if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { + /* previous component exists; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( nTotalCharge_Prev = pINChI_Prev->nTotalCharge ) { + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + inchi_strbuf_printf( strbuf, "%+d", nTotalCharge_Prev ); + } + } else + if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms && !pINChI_Taut_Prev->bDeleted ) { + /* previous non-taut component exists only in taut list */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + } + /* we have found pINChI->nTotalCharge same as in pINChI_Taut */ + /* output this (current) equivalence as '*', that is, same as tautomeric */ + /* that was printed on the 1st pass. */ + + pCurrEquStr = EquString(eq2taut); + if ( multPrevEquStr && pPrevEquStr ) { + if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { + multPrevEquStr ++; + } else { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + } else { + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + + pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ + pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ + mult = 0; + eq2tautPrev = 1; /* pINChI_Prev sp2 does not exist */ + } else + if ( eq2tautPrev ) { + /* at this point pINChI_Prev does not exist; however, pINChI */ + /*might have been discovered and it is different from pINChI_Taut */ + if ( multPrevEquStr && pPrevEquStr ) { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = NULL; + multPrevEquStr = 0; + } + eq2tautPrev = 0; + pINChI_Prev = pINChI; + pINChI_Taut_Prev = pINChI_Taut; + mult = 0; + } else { + /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp3 */ + /*================ compare sp3 to previous =====================*/ + eq2prev =bUseMulipliers && + pINChI && pINChI_Prev && + (nTotalCharge = pINChI->nTotalCharge) && (nTotalCharge_Prev = pINChI_Prev->nTotalCharge) && + nTotalCharge == nTotalCharge_Prev; + if ( eq2prev ) { + mult ++; /* mult = (number of non-empty equal items)-1 */ + continue; + } else { + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { + if ( nTotalCharge_Prev = pINChI_Prev->nTotalCharge ) { + /* pINChI_Prev exists and has charge info */ + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + inchi_strbuf_printf( strbuf, "%+d", nTotalCharge_Prev ); + } + /* else charge is not present in pINChI_Prev */ + } else + if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms && !pINChI_Taut_Prev->bDeleted ) { + if ( nTotalCharge_Taut_Prev = pINChI_Taut_Prev->nTotalCharge ) { + /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ + /* and it has charge info. This info has already been printed in the main section */ + /* + tot_len += MakeDelim( sIdenticalValues, strbuf, bOverflow); + */ + ; /* pINChI_Taut_Prev sp3 info was output in the main stereo section */ + } else { + ; /* pINChI_Taut_Prev exists and has not sp3 info */ + } + } +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + else { + int stop = 1; /* */ + } +#endif + } + pINChI_Prev = pINChI; + pINChI_Taut_Prev = pINChI_Taut; + mult = 0; /* we do not know whether the item is empty */ + } + } + + return ( strbuf->nUsedLength - nUsedLength0 ); +} + + +/* + Produce FixedH substring of the whole structure InChI string +*/ +int str_FixedH_atoms( INCHI_SORT *pINChISort, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int ATOM_MODE, + int num_components, + int bUseMulipliers) +{ + int i, j, ii, nNumEmpty, nUsedLength0; + INCHI_SORT *is, *is0; + INChI *pINChI, *pINChI_Prev; + int mult, eq2prev, bNext, bNotEmpty; + + is = NULL; + is0 = pINChISort; + i = 0; + pINChI_Prev = (0 <= (ii=GET_II(bOutType,is0)))? is0->pINChI[ii] : NULL; + mult = 0; + bNext = 0; + nNumEmpty = 0; + nUsedLength0 = strbuf->nUsedLength; + + /* For each connected component... */ + for ( i++; i <= num_components; i ++ ) + { + /* only non-tautomeric representation of tautomeric */ + pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; + /*================ compare fixed H to previous =====================*/ + eq2prev =bUseMulipliers && + pINChI && pINChI_Prev && pINChI->nNumberOfAtoms > 0 && + pINChI_Prev->nNumberOfAtoms==pINChI->nNumberOfAtoms && + !memcmp( pINChI_Prev->nNum_H_fixed, pINChI->nNum_H_fixed, + pINChI_Prev->nNumberOfAtoms*sizeof(pINChI->nNum_H_fixed[0]) ); + if ( eq2prev ) { + /* make sure it is not empty */ + eq2prev = 0; + for ( j = 0; j < pINChI_Prev->nNumberOfAtoms; j ++ ) { + if ( pINChI_Prev->nNum_H_fixed[j] ) { + eq2prev = 1; + break; + } + } + } + if ( eq2prev ) { + mult ++; /* mult = (number of non-empty equal items)-1 */ + continue; + } else { + /* print pINChI_Prev */ + /* delimiter */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( pINChI_Prev ) { + /* verify it is not empty */ + bNotEmpty = 0; + for ( j = 0; j < pINChI_Prev->nNumberOfAtoms; j ++ ) { + if ( pINChI_Prev->nNum_H_fixed[j] ) { + bNotEmpty = 1; + break; + } + } + if ( bNotEmpty ) { + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + /* H-atoms-fixed */ + MakeHString( 0, pINChI_Prev->nNum_H_fixed, + pINChI_Prev->nNumberOfAtoms, + strbuf, ATOM_MODE, bOverflow ); + } else { + nNumEmpty ++; + } + } + } + pINChI_Prev = pINChI; + mult = 0; /* we do not know whether the item is empty */ + } + if ( nNumEmpty == num_components && strbuf->nUsedLength > nUsedLength0 ) + { + strbuf->nUsedLength = nUsedLength0; + strbuf->pStr[nUsedLength0] = '\0'; + } + /* + if ( nNumEmpty == num_components && tot_len > tot_len_inp ) { + tot_len = tot_len_inp; + strbuf->pStr[tot_len] = '\0'; + } + */ + return ( strbuf->nUsedLength - nUsedLength0 ); +} + + +/* + Produce double bond stereo substring of the whole structure InChI string. +*/ +int str_Sp2( INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int TAUT_MODE, + int num_components, + int bSecondNonTautPass, + int bOmitRepetitions, + int bUseMulipliers) +{ + int i, ii, ii2, nUsedLength0; + INCHI_SORT *is, *is2, *is0, *is20; + INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; + INChI_Stereo *Stereo, *Stereo_Prev, *Stereo_Taut, *Stereo_Taut_Prev; + int mult, eq2prev, eq2taut, eq2tautPrev, bNext; + const char *pPrevEquStr, *pCurrEquStr; + int multPrevEquStr; + + pINChI_Taut = NULL; + pINChI_Prev = NULL; + pINChI_Taut_Prev = NULL; + mult = 0; + bNext = 0; + is = NULL; + is2 = NULL; + is0 = pINChISort; + is20 = bSecondNonTautPass? pINChISort2 : NULL; + eq2taut = 0; /* may be non-zero only on the 2nd (non-taut) pass */ + eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ + pPrevEquStr = NULL; /*, *pCurrEquStr;*/ + multPrevEquStr = 0; + nUsedLength0 = strbuf->nUsedLength; + + /* For each connected component ... */ + for ( i = 0; i <= num_components; i ++ ) + { + + /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ + pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; + /*================ compare sp2 to previous =====================*/ + if ( bSecondNonTautPass ) { + /* component that was output on the 1st pass */ + pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; + } + /*========= if bSecondNonTautPass then compare non-iso non-taut stereo to non-iso taut ========*/ + eq2taut = 0; +#if ( FIX_EMPTY_LAYER_BUG == 1 ) + if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions && pINChI && pINChI_Taut ) { + Stereo = pINChI->Stereo; + Stereo_Taut = pINChI_Taut->Stereo; + eq2taut = Stereo && Stereo_Taut && + Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Taut, EQL_SP2, 0 ); + eq2taut = eq2taut? (iiSTEREO | iitNONTAUT) : 0; + + if ( !eq2taut && + !Eql_INChI_Stereo( Stereo, EQL_SP2, NULL, EQL_EXISTS, 0 ) && + Eql_INChI_Stereo( Stereo_Taut, EQL_SP2, NULL, EQL_EXISTS, 0 ) ) { + eq2taut = iiEmpty; /* the current is empty while the preceding (taut) is not */ + } + } +#else + if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions ) { + eq2taut = pINChI && pINChI_Taut && + (Stereo = pINChI->Stereo) && (Stereo_Taut = pINChI_Taut->Stereo) && + Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Taut, EQL_SP2, 0 ); + eq2taut = eq2taut? (iiSTEREO | iitNONTAUT) : 0; + } +#endif + if ( eq2taut ) { + /* we may be here only in case of the second (non-taut) pass */ + /* current non-taut stereo has been found to be same as tautomeric */ + if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { + /* previous component exists; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( (Stereo_Prev = pINChI_Prev->Stereo) && Stereo_Prev->nNumberOfStereoBonds > 0 ) { + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + + MakeStereoString( Stereo_Prev->nBondAtom1, Stereo_Prev->nBondAtom2, + Stereo_Prev->b_parity, + 0, Stereo_Prev->nNumberOfStereoBonds, + strbuf, TAUT_MODE, bOverflow); + } + } else + if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { + /* previous non-taut component exists only in taut list */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + } + /* we have found pINChI->Stereo sp2 same as in pINChI_Taut */ + /* output this (current) equivalence as '*', that is, same as tautomeric */ + /* that was printed on the 1st pass. */ + pCurrEquStr = EquString(eq2taut); + if ( multPrevEquStr && pPrevEquStr ) { + if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { + multPrevEquStr ++; + } else { + /* new EqStr is different; output the previous one */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + } else { + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ + pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ + mult = 0; + eq2tautPrev = 1; /* pINChI_Prev sp2 does not exist */ + } else + if ( eq2tautPrev ) { + /* at this point pINChI_Prev does not exist; however, pINChI */ + /*might have been discovered and it is different from pINChI_Taut */ + if ( multPrevEquStr && pPrevEquStr ) { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = NULL; + multPrevEquStr = 0; + } + eq2tautPrev = 0; + pINChI_Prev = pINChI; + pINChI_Taut_Prev = pINChI_Taut; + mult = 0; + } else { + /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp2 */ + eq2prev =bUseMulipliers && + pINChI && pINChI_Prev && + (Stereo = pINChI->Stereo) && (Stereo_Prev = pINChI_Prev->Stereo) && + Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Prev, EQL_SP2, 0 ); + if ( eq2prev ) { + mult ++; /* mult = (number of non-empty equal items)-1 */ + continue; + } else { + /* pINChI sp2 info is either different or trivial. Output pINChI_Prev anyway */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { + if ( (Stereo_Prev = pINChI_Prev->Stereo) && Stereo_Prev->nNumberOfStereoBonds > 0 ) { + /* pINChI_Prev exists and has sp2 info */ + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + + MakeStereoString( Stereo_Prev->nBondAtom1, Stereo_Prev->nBondAtom2, + Stereo_Prev->b_parity, + 0, Stereo_Prev->nNumberOfStereoBonds, + strbuf, TAUT_MODE, bOverflow); + } + /* else sp2 info is not present in pINChI_Prev */ + } else + if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { + if ( (Stereo_Taut_Prev = pINChI_Taut_Prev->Stereo) && Stereo_Taut_Prev->nNumberOfStereoBonds > 0 ) { + /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ + /* and it has non-trivial sp2 info */ + /* + tot_len += MakeDelim( sIdenticalValues, strbuf, bOverflow); + */ + ;/* pINChI_Taut_Prev sp2 info was output in the main stereo section */ + } else { + ; /* pINChI_Taut_Prev exists and has not sp2 info */ + } + } +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + else { + int stop = 1; /* */ + } +#endif + } + pINChI_Prev = pINChI; + pINChI_Taut_Prev = pINChI_Taut; + mult = 0; /* we do not know whether the item is empty */ + } + } /* end of for each connected component ... */ + + return ( strbuf->nUsedLength - nUsedLength0 ); +} + + +/* + Produce tetrahedral stereo substring of the whole structure InChI string. +*/ +int str_Sp3( INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int TAUT_MODE, + int num_components, + int bRelRac, + int bSecondNonTautPass, + int bOmitRepetitions, + int bUseMulipliers) +{ + int i, ii, ii2, nUsedLength0; + INCHI_SORT *is, *is2, *is0, *is20; + INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; + INChI_Stereo *Stereo, *Stereo_Prev, *Stereo_Taut, *Stereo_Taut_Prev; + int mult, eq2prev, eq2taut, eq2tautPrev, bNext; + const char *pPrevEquStr, *pCurrEquStr; + int multPrevEquStr; + pINChI_Taut = NULL; + pINChI_Prev = NULL; + pINChI_Taut_Prev = NULL; + mult = 0; + bNext = 0; + is = NULL; + is2 = NULL; + is0 = pINChISort; + is20 = bSecondNonTautPass? pINChISort2 : NULL; + eq2taut = 0; /* may be non-zero only on the 2nd (non-taut) pass */ + eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ + pPrevEquStr = NULL; /*, *pCurrEquStr;*/ + multPrevEquStr = 0; +#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) +#else + bRelRac = 0; +#endif + nUsedLength0 = strbuf->nUsedLength; + + /* For each connected component... */ + for ( i = 0; i <= num_components; i ++ ) + { + + /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ + pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; + /*================ compare sp3 to previous =====================*/ + if ( bSecondNonTautPass ) { + /* component that was output on the 1st pass */ + pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; + } + /*========= if bSecondNonTautPass then compare non-iso non-taut stereo to non-iso taut ========*/ + eq2taut = 0; +#if ( FIX_EMPTY_LAYER_BUG == 1 ) + if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions && pINChI && pINChI_Taut ) { + Stereo = pINChI->Stereo; + Stereo_Taut = pINChI_Taut->Stereo; + eq2taut = Stereo && Stereo_Taut && + Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Taut, EQL_SP3, bRelRac ); + eq2taut = eq2taut? (iiSTEREO | iitNONTAUT) : 0; + if ( !eq2taut && + !Eql_INChI_Stereo( Stereo, EQL_SP3, NULL, EQL_EXISTS, 0 ) && + Eql_INChI_Stereo( Stereo_Taut, EQL_SP3, NULL, EQL_EXISTS, 0 ) ) { + eq2taut = iiEmpty; /* the current is empty while the preceding (taut) is not */ + } + } +#else + if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions ) { + eq2taut = pINChI && pINChI_Taut && + (Stereo = pINChI->Stereo) && (Stereo_Taut = pINChI_Taut->Stereo) && + Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Taut, EQL_SP3, bRelRac ); + eq2taut = eq2taut? (iiSTEREO | iitNONTAUT) : 0; + } +#endif + if ( eq2taut ) { + /* we may be here only in case of the second (non-taut) pass */ + /* current non-taut stereo has been found to be same as tautomeric */ + if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { + /* previous component exists; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( (Stereo_Prev = pINChI_Prev->Stereo) && Stereo_Prev->nNumberOfStereoCenters > 0 ) { + /* non-empty item */ + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + + MakeStereoString( Stereo_Prev->nNumber, NULL, + Stereo_Prev->t_parity, + 0, Stereo_Prev->nNumberOfStereoCenters, + strbuf, TAUT_MODE, bOverflow); + } + } else + if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { + /* previous non-taut component exists only in taut list */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + } + /* we have found pINChI->Stereo sp3 same as in pINChI_Taut */ + /* output this (current) equivalence as '*', that is, same as tautomeric */ + /* that was printed on the 1st pass. */ + + pCurrEquStr = EquString(eq2taut); + if ( multPrevEquStr && pPrevEquStr ) { + if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { + multPrevEquStr ++; + } else { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + } else { + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + + pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ + pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ + mult = 0; + eq2tautPrev = 1; /* pINChI_Prev sp2 does not exist */ + } else + if ( eq2tautPrev ) { + /* at this point pINChI_Prev does not exist; however, pINChI */ + /*might have been discovered and it is different from pINChI_Taut */ + if ( multPrevEquStr && pPrevEquStr ) { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = NULL; + multPrevEquStr = 0; + } + eq2tautPrev = 0; + pINChI_Prev = pINChI; + pINChI_Taut_Prev = pINChI_Taut; + mult = 0; + } else { + /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp3 */ + /*================ compare sp3 to previous =====================*/ + eq2prev =bUseMulipliers && + pINChI && pINChI_Prev && + (Stereo = pINChI->Stereo) && (Stereo_Prev = pINChI_Prev->Stereo) && + Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Prev, EQL_SP3, bRelRac ); + if ( eq2prev ) { + mult ++; /* mult = (number of non-empty equal items)-1 */ + continue; + } else { + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { + if ( (Stereo_Prev = pINChI_Prev->Stereo) && Stereo_Prev->nNumberOfStereoCenters > bRelRac ) { + /* pINChI_Prev exists and has sp3 info */ + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + + MakeStereoString( Stereo_Prev->nNumber, NULL, Stereo_Prev->t_parity, + 0, Stereo_Prev->nNumberOfStereoCenters, + strbuf, TAUT_MODE, bOverflow); + } + /* else sp3 info is not present in pINChI_Prev */ + } else + if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { + if ( (Stereo_Taut_Prev = pINChI_Taut_Prev->Stereo) && Stereo_Taut_Prev->nNumberOfStereoCenters > bRelRac ) { + /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ + /* and it has non-trivial sp3 info. This info has already been printed in the main section */ + /* + tot_len += MakeDelim( sIdenticalValues, strbuf, bOverflow); + */ + ; /* pINChI_Taut_Prev sp3 info was output in the main stereo section */ + } else { + ; /* pINChI_Taut_Prev exists and has not sp3 info */ + } + } +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + else { + int stop = 1; /* */ + } +#endif + } + pINChI_Prev = pINChI; + pINChI_Taut_Prev = pINChI_Taut; + mult = 0; /* we do not know whether the item is empty */ + } + } + return ( strbuf->nUsedLength - nUsedLength0 ); +} + + +/* + Output abs stero inversion substring of the whole structure InChI string +*/ +int str_StereoAbsInv( INCHI_SORT *pINChISort, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int num_components) +{ + int i, j, ii, nUsedLength0; + INCHI_SORT *is, *is0; + INChI_Stereo *Stereo; + INChI *pINChI; + + is = NULL; + is0 = pINChISort; + nUsedLength0 = strbuf->nUsedLength; + + /* For each connected component... */ + for ( i = 0; !*bOverflow && i < num_components; i ++ ) + { + + is=is0+i; + pINChI = (0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; + if ( pINChI && (Stereo = pINChI->Stereo) && (j=Stereo->nCompInv2Abs) ) + { + MakeDelim( j<0? "1":"0", strbuf, bOverflow); + } + else + { + MakeDelim( ".", strbuf, bOverflow); + } + } + + return ( strbuf->nUsedLength - nUsedLength0 ); +} + + +/* + Produce isotopic substring of the whole structure InChI string. +*/ +int str_IsoAtoms( INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int TAUT_MODE, + int num_components, + int bAbcNumbers, + int bSecondNonTautPass, + int bOmitRepetitions, + int bUseMulipliers) +{ + int i, ii, ii2, nUsedLength0; + INCHI_SORT *is, *is2, *is0, *is20; + INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; + int mult, eq2prev, eq2taut, eq2tautPrev, bNext; + const char *pPrevEquStr, *pCurrEquStr; + int multPrevEquStr; + pINChI_Taut = NULL; + pINChI_Prev = NULL; + pINChI_Taut_Prev = NULL; + mult = 0; + bNext = 0; + is = NULL; + is2 = NULL; + is0 = pINChISort; + is20 = bSecondNonTautPass? pINChISort2 : NULL; + eq2taut = 0; /* may be non-zero only on the 2nd (non-taut) pass */ + eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ + pPrevEquStr = NULL; /*, *pCurrEquStr;*/ + multPrevEquStr = 0; + nUsedLength0 = strbuf->nUsedLength; + + /* For each connected component... */ + for ( i = 0; i <= num_components; i ++ ) + { + + /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ + pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; + /*================ compare isotopic info to previous component =====================*/ + if ( bSecondNonTautPass ) { + /* component that was output on the 1st pass */ + pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; + } + /*========= if bSecondNonTautPass then compare iso non-taut to taut non-iso ========*/ + eq2taut = 0; + if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions ) { + eq2taut = Eql_INChI_Isotopic( pINChI, pINChI_Taut ); + eq2taut = eq2taut? (iiNUMB | iitNONTAUT) : 0; + } + if ( eq2taut ) { + /* we may be here only in case of the second (non-taut) pass */ + /* current non-taut isotopic info has been found to be same as current tautomeric */ + if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { + /* previous component exists; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( pINChI_Prev && (pINChI_Prev->nNumberOfIsotopicAtoms > 0 || + pINChI_Prev->nNumberOfIsotopicTGroups > 0) ) { + /* non-empty item */ + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + /* Isotopic atoms */ + if ( pINChI_Prev->nNumberOfIsotopicAtoms > 0/* && nStrLen-tot_len > 2*/ && !*bOverflow ) { /* dereferenced bOverflow 2004-06-07 */ + MakeIsoAtomString( pINChI_Prev->IsotopicAtom, + pINChI_Prev->nNumberOfIsotopicAtoms, + strbuf, + TAUT_MODE, bOverflow); + } + /* Isotopic tautomeric groups */ + if ( pINChI_Prev->nNumberOfIsotopicTGroups > 0 && + /*nStrLen-tot_len > 3 && */ + !*bOverflow ) + { + MakeDelim( bAbcNumbers? ITEM_DELIMETER : "(", strbuf, bOverflow); + MakeIsoTautString( pINChI_Prev->IsotopicTGroup, pINChI_Prev->nNumberOfIsotopicTGroups, + strbuf, TAUT_MODE, bOverflow); + if ( !bAbcNumbers ) { + MakeDelim( ")", strbuf, bOverflow); + } + } + } + } else + if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { + /* previous non-taut component exists only in taut list */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + } + /* we have found pINChI isotopic info to be same as in pINChI_Taut */ + /* output this (current) equivalence as '*', that is, same as tautomeric */ + /* that was printed on the 1st pass. */ + pCurrEquStr = EquString(eq2taut); + if ( multPrevEquStr && pPrevEquStr ) { + if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { + multPrevEquStr ++; + } else { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + } else { + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + pINChI_Prev = NULL; /* pINChI_Prev isotopic info does not exist since */ + pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ + mult = 0; + eq2tautPrev = 1; /* pINChI_Prev isotopic info does not exist */ + } else + if ( eq2tautPrev ) { + /* at this point pINChI_Prev does not exist; however, pINChI */ + /* might have been discovered and it is different from pINChI_Taut */ + if ( multPrevEquStr && pPrevEquStr ) { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = NULL; + multPrevEquStr = 0; + } + eq2tautPrev = 0; + pINChI_Prev = pINChI; + pINChI_Taut_Prev = pINChI_Taut; + mult = 0; + } else { + /*================ compare iso composition to previous =====================*/ + /* check whether pINChI and pINChI_Prev have non-zero identical isotopic info */ + eq2prev =bUseMulipliers && Eql_INChI_Isotopic( pINChI, pINChI_Prev ); + if ( eq2prev ) { + mult ++; /* mult = (number of non-empty equal items)-1 */ + continue; + } else { + /* pINChI isotopic info is either different or empty. Output pINChI_Prev anyway */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { + if ( (pINChI_Prev->nNumberOfIsotopicAtoms > 0 || + pINChI_Prev->nNumberOfIsotopicTGroups > 0) ) { + /* pINChI_Prev exists and has isotopic info */ + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + /* Isotopic atoms */ + if ( pINChI_Prev->nNumberOfIsotopicAtoms > 0 && + /*nStrLen-tot_len > 2 && */ + !*bOverflow ) + { + MakeIsoAtomString( pINChI_Prev->IsotopicAtom, + pINChI_Prev->nNumberOfIsotopicAtoms, + strbuf, TAUT_MODE, bOverflow); + } + /* Isotopic tautomeric groups */ + if ( pINChI_Prev->nNumberOfIsotopicTGroups > 0 && + /*nStrLen-tot_len > 3 && */ + !*bOverflow ) + { + MakeDelim( bAbcNumbers? ITEM_DELIMETER : "(", strbuf, bOverflow); + MakeIsoTautString( pINChI_Prev->IsotopicTGroup, pINChI_Prev->nNumberOfIsotopicTGroups, + strbuf, TAUT_MODE, bOverflow); + if ( !bAbcNumbers ) { + MakeDelim( ")", strbuf, bOverflow); + } + } + } + /* else isotopic info is not present in pINChI_Prev */ + } else + if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { + if ( (pINChI_Taut_Prev->nNumberOfIsotopicAtoms > 0 || + pINChI_Taut_Prev->nNumberOfIsotopicTGroups > 0) ) { + /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ + /* and it has non-trivial isotopic info */ + /* + tot_len += MakeDelim( sIdenticalValues, strbuf, bOverflow); + */ + ;/* pINChI_Taut_Prev isotopic info was output in the main isotopic section */ + } else { + ; /* pINChI_Taut_Prev exists and has not isotopic info */ + } + } +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + else { + int stop = 1; /* */ + } +#endif + } + /* Fix17: moved here 2004-10-08 */ + pINChI_Prev = pINChI; + pINChI_Taut_Prev = pINChI_Taut; + mult = 0; /* we do not know whether the item is empty */ + } + /* Fix17: moved from here 2004-10-08 + pINChI_Prev = pINChI; + pINChI_Taut_Prev = pINChI_Taut; + mult = 0; + */ + } + + return ( strbuf->nUsedLength - nUsedLength0 ); +} + + +/* + Produce isotopic-dbonds stereo substring of the whole structure InChI string. +*/ +int str_IsoSp2( INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int TAUT_MODE, + int num_components, + int bSecondNonTautPass, + int bOmitRepetitions, + int bUseMulipliers) +{ + int i, ii, ii2, nUsedLength0; + INCHI_SORT *is, *is2, *is0, *is20; + INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; + INChI_Stereo *Stereo, *Stereo_Prev, *Stereo_Taut, *Stereo_Taut_Prev; + int mult, eq2prev, eq2taut, eq2tautPrev, bNext; + const char *pPrevEquStr, *pCurrEquStr; + int multPrevEquStr; + pINChI_Taut = NULL; + pINChI_Prev = NULL; + pINChI_Taut_Prev = NULL; + mult = 0; + bNext = 0; + is = NULL; + is2 = NULL; + is0 = pINChISort; + is20 = bSecondNonTautPass? pINChISort2 : NULL; + eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ + eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ + pPrevEquStr = NULL; /*, *pCurrEquStr;*/ + multPrevEquStr = 0; + nUsedLength0 = strbuf->nUsedLength; + + /* For each connected component... */ + for ( i = 0; i <= num_components; i ++ ) + { + /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ + pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; + /*================ compare sp2 to previous =====================*/ + if ( bSecondNonTautPass ) { + /* component that was output on the 1st pass */ + pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; + } + eq2taut = 0; + /*========= if bSecondNonTautPass then compare iso non-taut stereo to other stereo ========*/ + if ( bSecondNonTautPass && bOmitRepetitions ) { + /* compare non-tautomeric isotopic to: + * a) non-tautomeric non-isotopic + * b) tautomeric non-isotopic + * c) tautomeric isotopic + */ + /* a) compare non-tautomeric isotopic to non-tautomeric non-isotopic */ + if ( !eq2taut ) { + eq2taut = pINChI && + /* non-taut isotopic */ /* non-taut non-isotopic */ + (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && + Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Taut, EQL_SP2, 0 ); + /* stereo isotopic non-taut = non-taut (stereo) */ + eq2taut = eq2taut? (iiSTEREO | iitISO | iitNONTAUT | iiEq2NONTAUT ) : 0; + } + /* b) compare non-tautomeric isotopic to tautomeric non-isotopic */ + if ( !eq2taut ) { + eq2taut = pINChI && pINChI_Taut && + /* non-taut isotopic */ /* taut non-isotopic */ + (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->Stereo) && + Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Taut, EQL_SP2, 0 ); + /* stereo isotopic non-taut = taut (stereo) */ + eq2taut = eq2taut? (iiSTEREO | iitISO | iitNONTAUT ) : 0; + } + /* c) compare non-tautomeric isotopic to tautomeric isotopic */ + if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions ) { + eq2taut = pINChI && pINChI_Taut && + (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && + Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Taut, EQL_SP2, 0 ); + /* stereo isotopic non-taut = isotopic taut (stereo) */ + eq2taut = eq2taut? (iiSTEREO | iitISO | iitNONTAUT | iiEq2ISO) : 0; + } +#if ( FIX_EMPTY_LAYER_BUG == 1 ) + if ( !eq2taut && pINChI && !((Stereo = pINChI->StereoIsotopic) && + Eql_INChI_Stereo( Stereo, EQL_SP2, NULL, EQL_EXISTS, 0 )) ) { + /* component has no stereo; check whether it has stereo in the preceding layers */ + if ( pINChI_Taut && (Stereo_Taut = pINChI_Taut->Stereo) && /* F is not empty */ + Eql_INChI_Stereo( Stereo_Taut, EQL_SP2, NULL, EQL_EXISTS, 0 ) || + !(pINChI_Taut && (Stereo_Taut = pINChI_Taut->Stereo) && /* M is empty and ... */ + Eql_INChI_Stereo( Stereo_Taut, EQL_SP2, NULL, EQL_EXISTS, 0 )) && + (pINChI_Taut && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && /* ... MI is not empty */ + Eql_INChI_Stereo( Stereo_Taut, EQL_SP2, NULL, EQL_EXISTS, 0 )) ) { + + eq2taut = iiEmpty; /* the component has stereo in the preceding layer */ + } + } +#endif + } else + /*========= if not bSecondNonTautPass then compare iso taut stereo to non-iso taut ========*/ + if ( !bSecondNonTautPass && bOmitRepetitions ) { + /* compare tautomeric isotopic to tautomeric non-isotopic */ + if ( !eq2taut ) { + eq2taut = pINChI && + /* taut isotopic */ /* taut non-isotopic */ + (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && + Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Taut, EQL_SP2, 0 ); + /* stereo isotopic taut = taut (stereo) */ + eq2taut = eq2taut? (iiSTEREO | iitISO ) : 0; +#if ( FIX_EMPTY_LAYER_BUG == 1 ) + if ( !eq2taut && pINChI && !((Stereo = pINChI->StereoIsotopic) && + Eql_INChI_Stereo( Stereo, EQL_SP2, NULL, EQL_EXISTS, 0 ) ) ) { + /* component has no MI stereo; check whether it has stereo in the preceding layer M */ + if ( (Stereo_Taut = pINChI->Stereo) && + Eql_INChI_Stereo( Stereo_Taut, EQL_SP2, NULL, EQL_EXISTS, 0 ) ) { + eq2taut = iiEmpty; /* the component has stereo in the preceding layer */ + } + } +#endif + } + } + if ( eq2taut ) { + /* we may be here only in case of the current layer found equal in another layer the same component */ + if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { + /* previous component exists; output it before output the current component */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( (Stereo_Prev = pINChI_Prev->StereoIsotopic) && Stereo_Prev->nNumberOfStereoBonds > 0 ) { + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + + MakeStereoString( Stereo_Prev->nBondAtom1, Stereo_Prev->nBondAtom2, + Stereo_Prev->b_parity, + 0, Stereo_Prev->nNumberOfStereoBonds, + strbuf, TAUT_MODE, bOverflow); + } + } else + if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { + /* previous non-taut component exists only in taut list */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + /* do not output stereo of non-tautomeric in non-taut layer: it has been output in the main layer */ + } + /* we have found another (previously printed) layer of the current component equal to this layer */ + /* output this (current) equivalence mark = EquString(eq2taut) */ + pCurrEquStr = EquString(eq2taut); + if ( multPrevEquStr && pPrevEquStr ) { + if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { + multPrevEquStr ++; + } else { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + } else { + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ + pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ + mult = 0; + eq2tautPrev = 1; /* pINChI_Prev and pINChI_Taut_Prev have already been */ + } else + if ( eq2tautPrev ) { + /* at this point pINChI_Prev does not exist; however, pINChI */ + /*might have been discovered and it is different from pINChI_Taut */ + if ( multPrevEquStr && pPrevEquStr ) { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = NULL; + multPrevEquStr = 0; + } + eq2tautPrev = 0; + pINChI_Prev = pINChI; + pINChI_Taut_Prev = pINChI_Taut; + mult = 0; + } else { + /* current layer is different from previously printed layers of the current component */ + /* compare the current layer to this layer of the previous component: */ + /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp2 */ + /*================ compare iso sp2 to previous =====================*/ + eq2prev =bUseMulipliers && + pINChI && pINChI_Prev && + (Stereo = pINChI->StereoIsotopic) && (Stereo_Prev = pINChI_Prev->StereoIsotopic) && + Eql_INChI_Stereo( Stereo, EQL_SP2, Stereo_Prev, EQL_SP2, 0 ); + if ( eq2prev ) { + mult ++; /* mult = (number of non-empty equal items)-1 */ + continue; + } else { + /* the current layer is different from this layer of the previous component */ + /* therefore print the current layer */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { + if ( (Stereo_Prev = pINChI_Prev->StereoIsotopic) && Stereo_Prev->nNumberOfStereoBonds > 0 ) { + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + + MakeStereoString( Stereo_Prev->nBondAtom1, Stereo_Prev->nBondAtom2, + Stereo_Prev->b_parity, + 0, Stereo_Prev->nNumberOfStereoBonds, + strbuf, TAUT_MODE, bOverflow); + } + /* else sp2 info is not present in pINChI_Prev */ + } else + /* do not print pINChI_Prev because it either do not exist of have already been printed */ + if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { + if ( (Stereo_Taut_Prev = pINChI_Taut_Prev->StereoIsotopic) && Stereo_Taut_Prev->nNumberOfStereoBonds > 0 ) { + /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ + /* and it has non-trivial sp2 info */ + /* + tot_len += MakeDelim( sIdenticalValues, strbuf, bOverflow); + */ + ;/* pINChI_Taut_Prev sp3 info was output in the main stereo section */ + } else { + ; /* pINChI_Taut_Prev exists and has not sp2 info */ + } + } +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + else { + int stop = 1; /* */ + } +#endif + } + pINChI_Prev = pINChI; + pINChI_Taut_Prev = pINChI_Taut; + mult = 0; /* we do not know whether the item is empty */ + } + } + + return ( strbuf->nUsedLength - nUsedLength0 ); +} + + +/* + Produce isotopic-tetr stereo substring of the whole structure InChI string. +*/ +int str_IsoSp3( INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, int TAUT_MODE, + int num_components, + int bRelRac, + int bSecondNonTautPass, + int bOmitRepetitions, + int bUseMulipliers) +{ + int i, ii, ii2, nUsedLength0; + INCHI_SORT *is, *is2, *is0, *is20; + INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; + INChI_Stereo *Stereo, *Stereo_Prev, *Stereo_Taut, *Stereo_Taut_Prev; + int mult, eq2prev, eq2taut, eq2tautPrev, bNext; + const char *pPrevEquStr, *pCurrEquStr; + int multPrevEquStr; + pINChI_Taut = NULL; + pINChI_Prev = NULL; + pINChI_Taut_Prev = NULL; + mult = 0; + bNext = 0; + is = NULL; + is2 = NULL; + is0 = pINChISort; + is20 = bSecondNonTautPass? pINChISort2 : NULL; + eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ + eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ + pPrevEquStr = NULL; /*, *pCurrEquStr;*/ + multPrevEquStr = 0; +#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) +#else + bRelRac = 0; +#endif + nUsedLength0 = strbuf->nUsedLength; + + /* For each connected component... */ + for ( i = 0; i <= num_components; i ++ ) + { + + /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ + pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; + /*================ compare sp2 to previous =====================*/ + if ( bSecondNonTautPass ) { + /* component that was output on the 1st pass */ + pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; + } + eq2taut = 0; + /*========= if bSecondNonTautPass then compare iso non-taut stereo to other stereo ========*/ + if ( bSecondNonTautPass && bOmitRepetitions ) { + /* compare non-tautomeric isotopic to: + * a) non-tautomeric non-isotopic + * b) tautomeric non-isotopic + * c) tautomeric isotopic + */ + /* a) compare non-tautomeric isotopic to non-tautomeric non-isotopic */ + if ( !eq2taut ) { + eq2taut = pINChI && /* non-taut isotopic */ /* non-taut non-isotopic */ + (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && + Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Taut, EQL_SP3, bRelRac ); + /* stereo isotopic non-taut = non-taut (stereo) */ + eq2taut = eq2taut? (iiSTEREO | iitISO | iitNONTAUT | iiEq2NONTAUT ) : 0; + } + /* b) compare non-tautomeric isotopic to tautomeric non-isotopic */ + if ( !eq2taut ) { + eq2taut = pINChI && pINChI_Taut && + /* non-taut isotopic */ /* taut non-isotopic */ + (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->Stereo) && + Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Taut, EQL_SP3, bRelRac ); + /* stereo isotopic non-taut = taut (stereo) */ + eq2taut = eq2taut? (iiSTEREO | iitISO | iitNONTAUT ) : 0; + } + /* c) compare non-tautomeric isotopic to tautomeric isotopic */ + if ( !eq2taut && bSecondNonTautPass && bOmitRepetitions ) { + eq2taut = pINChI && pINChI_Taut && + (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && + Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Taut, EQL_SP3, bRelRac ); + /* stereo isotopic non-taut = isotopic taut (stereo) */ + eq2taut = eq2taut? (iiSTEREO | iitISO | iitNONTAUT | iiEq2ISO) : 0; + } +#if ( FIX_EMPTY_LAYER_BUG == 1 ) + if ( !eq2taut && pINChI && !((Stereo = pINChI->StereoIsotopic) && + Eql_INChI_Stereo( Stereo, EQL_SP3, NULL, EQL_EXISTS, 0 )) ) { + /* component has no stereo; check whether it has stereo in the preceding layers */ + if ( pINChI_Taut && (Stereo_Taut = pINChI_Taut->Stereo) && /* F is not empty */ + Eql_INChI_Stereo( Stereo_Taut, EQL_SP3, NULL, EQL_EXISTS, 0 ) || + !(pINChI_Taut && (Stereo_Taut = pINChI_Taut->Stereo) && /* M is empty and ... */ + Eql_INChI_Stereo( Stereo_Taut, EQL_SP3, NULL, EQL_EXISTS, 0 )) && + (pINChI_Taut && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && /* ... MI is not empty */ + Eql_INChI_Stereo( Stereo_Taut, EQL_SP3, NULL, EQL_EXISTS, 0 )) ) { + + eq2taut = iiEmpty; /* the component has stereo in the preceding layer */ + } + } +#endif + } else + /*========= if not bSecondNonTautPass then compare iso taut stereo to non-iso taut ========*/ + if ( !bSecondNonTautPass && bOmitRepetitions ) { + /* compare tautomeric isotopic to tautomeric non-isotopic */ + if ( !eq2taut ) { + eq2taut = pINChI && + /* taut isotopic */ /* taut non-isotopic */ + (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && + Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Taut, EQL_SP3, bRelRac ); + /* stereo isotopic taut = taut (stereo) */ + eq2taut = eq2taut? (iiSTEREO | iitISO ) : 0; +#if ( FIX_EMPTY_LAYER_BUG == 1 ) + if ( !eq2taut && pINChI && !((Stereo = pINChI->StereoIsotopic) && + Eql_INChI_Stereo( Stereo, EQL_SP3, NULL, EQL_EXISTS, 0 ) ) ) { + /* component has no MI stereo; check whether it has stereo in the preceding layer M */ + if ( (Stereo_Taut = pINChI->Stereo) && + Eql_INChI_Stereo( Stereo_Taut, EQL_SP3, NULL, EQL_EXISTS, 0 ) ) { + eq2taut = iiEmpty; /* the component has stereo in the preceding layer */ + } + } +#endif + } + } + if ( eq2taut ) { + /* we may be here only in case of the current layer found equal in another layer the same component */ + if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { + /* previous component exists; output it before output the current component */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( (Stereo_Prev = pINChI_Prev->StereoIsotopic) && Stereo_Prev->nNumberOfStereoCenters > bRelRac ) { + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + + MakeStereoString( Stereo_Prev->nNumber, NULL, Stereo_Prev->t_parity, + 0, Stereo_Prev->nNumberOfStereoCenters, + strbuf, TAUT_MODE, bOverflow); + } + } else + if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { + /* previous non-taut component exists only in taut list */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + /* do not output stereo of non-tautomeric in non-taut layer: it has been output in the main layer */ + } + /* we have found another (previously printed) layer of the current component equal to this layer */ + /* output this (current) equivalence mark = EquString(eq2taut) */ + pCurrEquStr = EquString(eq2taut); + if ( multPrevEquStr && pPrevEquStr ) { + if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { + multPrevEquStr ++; + } else { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + } else { + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ + pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ + mult = 0; + eq2tautPrev = 1; /* pINChI_Prev and pINChI_Taut_Prev have already been output */ + } else + if ( eq2tautPrev ) { + /* at this point pINChI_Prev does not exist; however, pINChI */ + /*might have been discovered and it is different from pINChI_Taut */ + if ( multPrevEquStr && pPrevEquStr ) { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = NULL; + multPrevEquStr = 0; + } + eq2tautPrev = 0; + pINChI_Prev = pINChI; + pINChI_Taut_Prev = pINChI_Taut; + mult = 0; + } else { + /* current layer is different from previously printed layers of the current component */ + /* compare the current layer to this layer of the previous component: */ + /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp2 */ + /*================ compare iso sp3 to previous =====================*/ + eq2prev =bUseMulipliers && pINChI && pINChI_Prev && + (Stereo = pINChI->StereoIsotopic) && (Stereo_Prev = pINChI_Prev->StereoIsotopic) && + Eql_INChI_Stereo( Stereo, EQL_SP3, Stereo_Prev, EQL_SP3, bRelRac ); + if ( eq2prev ) { + mult ++; /* mult = (number of non-empty equal items)-1 */ + continue; + } else { + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { + if ( (Stereo_Prev = pINChI_Prev->StereoIsotopic) && Stereo_Prev->nNumberOfStereoCenters > bRelRac ) { + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + + MakeStereoString( Stereo_Prev->nNumber, NULL, Stereo_Prev->t_parity, + 0, Stereo_Prev->nNumberOfStereoCenters, + strbuf, TAUT_MODE, bOverflow); + } + /* else sp3 info is not present in pINChI_Prev */ + } else + /* do not print pINChI_Prev because it either do not exist of have already been printed */ + if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { + if ( (Stereo_Taut_Prev = pINChI_Taut_Prev->StereoIsotopic) && Stereo_Taut_Prev->nNumberOfStereoCenters > bRelRac ) { + /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ + /* and it has non-trivial sp2 info */ + /* + tot_len += MakeDelim( sIdenticalValues, strbuf, bOverflow); + */ + ;/* pINChI_Taut_Prev sp3 info was output in the main stereo section */ + } else { + ; /* pINChI_Taut_Prev exists and has not sp3 info */ + } + } +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + else { + int stop = 1; /* */ + } +#endif + } + pINChI_Prev = pINChI; + pINChI_Taut_Prev = pINChI_Taut; + mult = 0; /* we do not know whether the item is empty */ + } + } + + return ( strbuf->nUsedLength - nUsedLength0 ); +} + + +/* + Output isotopic abs stero inversion substring of the whole structure InChI string +*/ +int str_IsoStereoAbsInv( INCHI_SORT *pINChISort, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int num_components) +{ + int i, j, ii, nUsedLength0; + INCHI_SORT *is, *is0; + INChI_Stereo *Stereo; + INChI *pINChI; + + is = NULL; + is0 = pINChISort; + nUsedLength0 = strbuf->nUsedLength; + + /* For each connected component... */ + for ( i = 0; !*bOverflow && i < num_components; i ++ ) + { + is=is0+i; + pINChI = (0 <= (ii=GET_II(bOutType,is))) + ? is->pINChI[ii] + : NULL; + if ( pINChI && + (Stereo = pINChI->StereoIsotopic) && + (j=Stereo->nCompInv2Abs) ) + { + MakeDelim( j<0? "1":"0", strbuf, bOverflow); + } + else + { + MakeDelim( ".", strbuf, bOverflow); + } + } + + return ( strbuf->nUsedLength - nUsedLength0 ); +} + + + +/* + Generate AuxInfo substrings +*/ + + + +/* + Produce equivalence substring of AuxInfo +*/ +int str_AuxEqu( INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int TAUT_MODE, + int num_components, + int bSecondNonTautPass, + int bOmitRepetitions, + int bUseMulipliers) +{ + int i, ii, ii2, nUsedLength0; + INCHI_SORT *is, *is2, *is0, *is20; + INChI_Aux *pINChI_Aux = NULL, *pINChI_Aux_Prev, *pINChI_Aux_Taut, *pINChI_Aux_Taut_Prev; + int mult, eq2prev, eq2taut, eq2tautPrev, bNext; + const char *pPrevEquStr, *pCurrEquStr; + int multPrevEquStr; + pINChI_Aux_Prev = NULL; + pINChI_Aux_Taut = NULL; + pINChI_Aux_Taut_Prev = NULL; + + mult = 0; + bNext = 0; + is = NULL; + is2 = NULL; + is0 = pINChISort; + is20 = bSecondNonTautPass? pINChISort2 : NULL; + eq2taut = 0; /* may be non-zero only on the 2nd (non-taut) pass */ + eq2tautPrev = 1; /* pINChI_Aux_Prev (previous pINChI_Aux) does not exist */ + pPrevEquStr = NULL; /*, *pCurrEquStr;*/ + multPrevEquStr = 0; + nUsedLength0 = strbuf->nUsedLength; + + /* For each connected component... */ + for ( i = 0; i <= num_components; i ++ ) + { + + /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ + pINChI_Aux = (i < num_components && (is = is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI_Aux[ii] : NULL; + if ( bSecondNonTautPass ) { + /* component that was output on the 1st pass */ + pINChI_Aux_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI_Aux[ii2] : NULL; + } + /*================ compare non-iso non-taut equivalence info to non-iso taut ========*/ + eq2taut = bSecondNonTautPass && bOmitRepetitions && + Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU, pINChI_Aux_Taut, EQL_EQU ); + eq2taut = eq2taut? (iiEQU | iitNONTAUT) : 0; + if ( eq2taut ) { + /* we may be here only in case of the second (non-taut) pass */ + /* current non-taut equivalence has been found to be same as tautomeric */ + if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { + /* previous component exists */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( bHasEquString( pINChI_Aux_Prev->nConstitEquNumbers, pINChI_Aux_Prev->nNumberOfAtoms) ) { + /* output previous component(s) equivalence since it was found to be non-trivial */ + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + MakeEquString( pINChI_Aux_Prev->nConstitEquNumbers, + pINChI_Aux_Prev->nNumberOfAtoms, + 0, strbuf, TAUT_MODE, bOverflow); + } else { + ; /* pINChI_Aux_Prev exists and has only trivial equivalence info */ + } + } else + if ( pINChI_Aux_Taut_Prev && pINChI_Aux_Taut_Prev->nNumberOfAtoms ) { + /* previous non-taut component exists only in taut list */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + } + /* we have found pINChI_Aux->nConstitEquNumbers same as in pINChI_Aux_Taut */ + /* output this (current) equivalence as '*', that is, same as tautomeric */ + /* that was printed on the 1st pass. */ + pCurrEquStr = EquString(eq2taut); + if ( multPrevEquStr && pPrevEquStr ) { + if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { + multPrevEquStr ++; + } else { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + } else { + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + pINChI_Aux_Prev = NULL; /* pINChI_Aux_Prev does not exist since */ + pINChI_Aux_Taut_Prev = NULL; /* pINChI_Aux has just been printed */ + mult = 0; + eq2tautPrev = 1; + } else + if ( eq2tautPrev ) { + /* at this point pINChI_Aux_Prev does not exist; however, pINChI_Aux */ + /*might have been discovered and it is different from pINChI_Aux_Taut */ + if ( multPrevEquStr && pPrevEquStr ) { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = NULL; + multPrevEquStr = 0; + } + eq2tautPrev = 0; + pINChI_Aux_Prev = pINChI_Aux; + pINChI_Aux_Taut_Prev = pINChI_Aux_Taut; + mult = 0; + } else { + /* check whether pINChI_Aux and pINChI_Aux_Prev have identical non-trivial equivalence info */ + eq2prev = bUseMulipliers && + Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU, pINChI_Aux_Prev, EQL_EQU ); + if ( eq2prev ) { + /* eq. info is same and non-trivial */ + mult ++; /* mult = (number of non-empty equal items)-1 */ + continue; + } else { + /* pINChI_Aux eq. info is either different or trivial. Output pINChI_Aux_Prev anyway */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { + if ( bHasEquString( pINChI_Aux_Prev->nConstitEquNumbers, pINChI_Aux_Prev->nNumberOfAtoms) ) { + /* pINChI_Aux_Prev exists and has equivalence info */ + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + MakeEquString( pINChI_Aux_Prev->nConstitEquNumbers, pINChI_Aux_Prev->nNumberOfAtoms, 0, + strbuf, TAUT_MODE, bOverflow); + } else { + ; /* pINChI_Aux_Prev exists and has only trivial equivalence info */ + } + } else + if ( bSecondNonTautPass && pINChI_Aux_Taut_Prev && pINChI_Aux_Taut_Prev->nNumberOfAtoms ) { + if ( bHasEquString( pINChI_Aux_Taut_Prev->nConstitEquNumbers, pINChI_Aux_Taut_Prev->nNumberOfAtoms) ) { + /* since pINChI_Aux_Prev does not exist, pINChI_Aux_Taut_Prev is non-tautomeric */ + /* and it has non-trivial equivalence info. This info has already been printed in the main section */ + /* + MakeDelim( sIdenticalValues, strbuf, bOverflow); + */ + } else { + ; /* pINChI_Aux_Taut_Prev exists and has only trivial equivalence info */ + } + } +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + else { + int stop = 1; /* */ + } +#endif + } + pINChI_Aux_Prev = pINChI_Aux; + pINChI_Aux_Taut_Prev = pINChI_Aux_Taut; + mult = 0; /* we do not know whether the item is empty */ + } + } + return ( strbuf->nUsedLength - nUsedLength0 ); +} + + +/* + Produce tetr stereo inversion substring of AuxInfo. +*/ +int str_AuxInvSp3( INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, int *bOverflow, int bOutType, + int TAUT_MODE, int num_components, + int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) +{ + int i, ii, ii2, nUsedLength0; + INCHI_SORT *is, *is2, *is0, *is20; + INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; + INChI_Stereo *Stereo, *Stereo_Prev, *Stereo_Taut, *Stereo_Taut_Prev; + int mult, eq2prev, eq2taut, eq2tautPrev, bNext; + const char *pPrevEquStr, *pCurrEquStr; + int multPrevEquStr; + /*************** + inverted sp3 + ****************/ + pINChI_Taut = NULL; + pINChI_Prev = NULL; + pINChI_Taut_Prev = NULL; + mult = 0; + bNext = 0; + is = NULL; + is2 = NULL; + is0 = pINChISort; + is20 = bSecondNonTautPass? pINChISort2 : NULL; + eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ + eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ + pPrevEquStr = NULL; /*, *pCurrEquStr;*/ + multPrevEquStr = 0; + nUsedLength0 = strbuf->nUsedLength; + + /* For each connected component... */ + for ( i = 0; i <= num_components; i ++ ) + { + + /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ + pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; + /*================ compare sp2 to previous =====================*/ + if ( bSecondNonTautPass ) { + /* component that was output on the 1st pass */ + pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; + } + eq2taut = 0; + /*========= if bSecondNonTautPass then compare iso non-taut stereo to other stereo ========*/ + if ( bSecondNonTautPass && bOmitRepetitions ) { + /* compare non-tautomeric inverted to: + * a) tautomeric inverted + * b) Inverted(tautomeric) + * c) Inverted(non-tautomeric) + */ + /* a) compare non-tautomeric inverted to tautomeric inverted */ + if ( !eq2taut ) { + eq2taut = pINChI && pINChI_Taut && + /* non-taut inverted */ /* taut invertedc */ + (Stereo = pINChI->Stereo) && (Stereo_Taut = pINChI_Taut->Stereo) && + Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3_INV, 0 ); + /* stereo-inv non-taut = taut (stereo-inv) */ + eq2taut = eq2taut? (iiSTEREO_INV | iitNONTAUT ) : 0; + } + /* b) compare non-tautomeric inverted to Inverted(tautomeric stereo) */ + if ( !eq2taut ) { + eq2taut = pINChI && pINChI_Taut && + (Stereo = pINChI->Stereo) && (Stereo_Taut = pINChI_Taut->Stereo) && + Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3, 0 ); + /* stereo-inv non-taut = Inv(taut stereo) */ + eq2taut = eq2taut? (iiSTEREO_INV | iitNONTAUT | iiEq2INV) : 0; + } + /* c) compare non-tautomeric inverted to Inverted(non-tautomeric stereo) */ + if ( !eq2taut ) { + eq2taut = pINChI && + (Stereo = pINChI->Stereo) && + Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo, EQL_SP3, 0 ); + /* stereo-inv non-taut = Inv(non-taut stereo) */ + eq2taut = eq2taut? (iiSTEREO_INV | iitNONTAUT | iiEq2INV | iiEq2NONTAUT) : 0; + } +#if ( FIX_EMPTY_LAYER_BUG == 1 ) + if ( !eq2taut && pINChI && pINChI_Taut && + !((Stereo = pINChI->Stereo) && Eql_INChI_Stereo( Stereo, EQL_SP3_INV, NULL, EQL_EXISTS, 0 ))) { + if ( (Stereo_Taut = pINChI_Taut->Stereo) && + Eql_INChI_Stereo( Stereo_Taut, EQL_SP3, NULL, EQL_EXISTS, 0 ) ) { + + eq2taut = iiEmpty; /* the current is empty while the preceding (taut) is not */ + } + } +#endif + } else + /*========= if not bSecondNonTautPass then compare inv taut stereo to various taut stereo ========*/ + if ( !bSecondNonTautPass && bOmitRepetitions ) { + /* compare tautomeric inverted to Invetred(tautomeric) */ + if ( !eq2taut ) { + eq2taut = pINChI && + (Stereo = pINChI->Stereo) && + Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo, EQL_SP3, 0 ); + /* stereo isotopic taut = taut (stereo) */ + eq2taut = eq2taut? (iiSTEREO_INV | iiEq2INV ) : 0; + } + } + if ( eq2taut ) { + /* we may be here only in case of the current layer found equal in another layer the same component */ + if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { + /* previous component exists; output it before output the current component */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( (Stereo_Prev = pINChI_Prev->Stereo) && Stereo_Prev->nNumberOfStereoCenters > 0 ) { + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + + MakeStereoString( Stereo_Prev->nNumber, NULL, Stereo_Prev->t_parityInv, + 0, Stereo_Prev->nNumberOfStereoCenters, + strbuf, TAUT_MODE, bOverflow); + } + } else + if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { + /* previous non-taut component exists only in taut list */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + /* do not output stereo of non-tautomeric in non-taut layer: it has been output in the main layer */ + } + /* we have found another (previously printed) layer of the current component equal to this layer */ + /* output this (current) equivalence mark = EquString(eq2taut) */ + pCurrEquStr = EquString(eq2taut); + if ( multPrevEquStr && pPrevEquStr ) { + if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { + multPrevEquStr ++; + } else { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + } else { + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ + pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ + mult = 0; + eq2tautPrev = 1; /* pINChI_Prev and pINChI_Taut_Prev have already been output */ + } else + if ( eq2tautPrev ) { + /* at this point pINChI_Prev does not exist; however, pINChI */ + /*might have been discovered and it is different from pINChI_Taut */ + if ( multPrevEquStr && pPrevEquStr ) { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = NULL; + multPrevEquStr = 0; + } + eq2tautPrev = 0; + pINChI_Prev = pINChI; + pINChI_Taut_Prev = pINChI_Taut; + mult = 0; + } else { + /* current layer is different from previously printed layers of the current component */ + /* compare the current layer to this layer of the previous component: */ + /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp2 */ + /*================ compare iso sp3 to previous =====================*/ + eq2prev =bUseMulipliers && + pINChI && pINChI_Prev && + /* do both have stereo? */ + (Stereo = pINChI->Stereo) && (Stereo_Prev = pINChI_Prev->Stereo) && + /* is their inverted stereo same? */ + Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Prev, EQL_SP3_INV, 0 ); + if ( eq2prev ) { + mult ++; /* mult = (number of non-empty equal items)-1 */ + continue; + } else { + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { + if ( (Stereo_Prev = pINChI_Prev->Stereo) && + Stereo_Prev->nNumberOfStereoCenters > 0 && Stereo_Prev->nCompInv2Abs ) { + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + + MakeStereoString( Stereo_Prev->nNumberInv, NULL, Stereo_Prev->t_parityInv, + 0, Stereo_Prev->nNumberOfStereoCenters, + strbuf, TAUT_MODE, bOverflow); + } + /* else sp3 info is not present in pINChI_Prev */ + } else + /* do not print pINChI_Prev because it either do not exist of have already been printed */ + if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { + if ( (Stereo_Taut_Prev = pINChI_Taut_Prev->Stereo) && + Stereo_Taut_Prev->nNumberOfStereoCenters > 0 && Stereo_Taut_Prev->nCompInv2Abs ) { + /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ + /* and it has non-trivial inv sp3 info. It has already been printed in the main section */ + /* + MakeDelim( sIdenticalValues, strbuf, bOverflow); + */ + ;/* pINChI_Taut_Prev sp3 info was output in the main stereo section */ + } else { + ; /* pINChI_Taut_Prev exists and has not sp3 info */ + } + } +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + else { + int stop = 1; /* */ + } +#endif + } + pINChI_Prev = pINChI; + mult = 0; /* we do not know whether the item is empty */ + } + } + + return ( strbuf->nUsedLength - nUsedLength0 ); +} + + +/* + Produce tetr stereo inversion numbering substring of AuxInfo. +*/ +int str_AuxInvSp3Numb( CANON_GLOBALS *pCG, + INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int TAUT_MODE, + int num_components, + int bSecondNonTautPass, + int bOmitRepetitions) +{ + int i, ii, ii2, nUsedLength0; + INCHI_SORT *is, *is0 /*, *is2*/; + INChI *pINChI, *pINChI_Taut; + INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev, *pINChI_Aux_Taut; + INChI_Stereo *Stereo, *Stereo_Taut; + int eq2taut, bNext; + const char *pPrevEquStr, *pCurrEquStr; + int multPrevEquStr; + /************************************************** + * specificity of numbering: there is no previous * + * component because no repetition is possible * + **************************************************/ + pINChI = NULL; + pINChI_Taut = NULL; + pINChI_Aux = NULL; + pINChI_Aux_Taut = NULL; + pINChI_Aux_Prev = NULL; + bNext = 0; + is = NULL; + is0 = pINChISort; + /*is2 = bSecondNonTautPass? pINChISort2 : NULL;*/ + eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ + pPrevEquStr = NULL; /*, *pCurrEquStr;*/ + multPrevEquStr = 0; + nUsedLength0 = strbuf->nUsedLength; + + /* For each connected component... */ + for ( i = 0; i < num_components; i ++ ) + { + + /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ + is=is0+i; + pINChI = (0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; + pINChI_Aux = pINChI? is->pINChI_Aux[ii] : NULL; + /*================ to compare to previously printed =====================*/ + if ( bSecondNonTautPass ) { + /* component that was printed on the 1st pass */ + pINChI_Taut = (0 <= (ii2=GET_II(OUT_T1,is)))? is->pINChI[ii2] : NULL; + pINChI_Aux_Taut = pINChI_Taut? is->pINChI_Aux[ii2] : NULL; + } + + eq2taut = 0; + /*========= if bSecondNonTautPass then compare inv non-taut stereo to other stereo ========*/ + if ( bSecondNonTautPass && bOmitRepetitions && + pINChI && (Stereo = pINChI->Stereo) && Stereo->nCompInv2Abs ) { + /* compare non-tautomeric inverted stereo numbering to: + * a) tautomeric numbering + * b) non-tautomeric numbering + * c) tautomeric inverted stereo numbering + */ + /* a) compare non-tautomeric inverted stereo numbering to tautomeric numbering */ + if ( !eq2taut ) { + eq2taut = pINChI_Taut && + Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV, pINChI_Aux_Taut, EQL_NUM ); + /* stereo-inv numbering non-taut = taut numbering */ + eq2taut = eq2taut? (iiSTEREO_INV | iiNUMB | iitNONTAUT ) : 0; + } + /* b) compare non-tautomeric inverted stereo numbering to non-tautomeric numbering */ + if ( !eq2taut ) { + eq2taut = + Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV, pINChI_Aux, EQL_NUM ); + /* stereo-inv numb. non-taut = non-taut numbering */ + eq2taut = eq2taut? (iiSTEREO_INV | iiNUMB | iitNONTAUT | iiEq2NONTAUT ) : 0; + } + /* c) compare non-tautomeric inverted stereo numbering to tautomeric inverted stereo numbering */ + if ( !eq2taut ) { + eq2taut = pINChI_Taut && + (Stereo_Taut = pINChI_Taut->Stereo) && Stereo_Taut->nCompInv2Abs && + Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV, pINChI_Aux_Taut, EQL_NUM_INV ); + /* stereo-inv numb. non-taut = taut inv stereo numbering */ + eq2taut = eq2taut? (iiSTEREO_INV | iiNUMB | iitNONTAUT | iiEq2INV ) : 0; + } + } else + /*========= if not bSecondNonTautPass then compare inv taut stereo numb to taut numb ========*/ + if ( !bSecondNonTautPass && bOmitRepetitions && + pINChI && (Stereo = pINChI->Stereo) && Stereo->nCompInv2Abs ) { + /* compare tautomeric inverted stereo numbering to tautomeric numbering */ + if ( !eq2taut ) { + eq2taut = + Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV, pINChI_Aux, EQL_NUM ); + /* stereo-inv numbering (taut) = taut numbering */ + eq2taut = eq2taut? (iiSTEREO_INV | iiNUMB ) : 0; + } + } + if ( eq2taut ) { + /* we have found another (previously printed) layer of the current component equal to this layer */ + /* output this (current) equivalence mark = EquString(eq2taut) */ + pCurrEquStr = EquString(eq2taut); + if ( multPrevEquStr && pPrevEquStr ) { + if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { + multPrevEquStr ++; + } else { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + } else { + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + } else { + /* current layer is different from previously printed layers of the current component */ + if ( multPrevEquStr && pPrevEquStr ) { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = NULL; + multPrevEquStr = 0; + } + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( pINChI && pINChI_Aux && pINChI_Aux->nNumberOfAtoms && + (Stereo = pINChI->Stereo) && Stereo->nNumberOfStereoCenters && + Stereo->nCompInv2Abs && pINChI_Aux->nOrigAtNosInCanonOrdInv ) { + MakeCtString( pCG, pINChI_Aux->nOrigAtNosInCanonOrdInv, + pINChI_Aux->nNumberOfAtoms, 0, NULL, 0, + strbuf, TAUT_MODE, bOverflow); + } + /* else inv stereo info is not present in pINChI */ + } + } + if ( multPrevEquStr && pPrevEquStr ) { + /* the new EqStr of the last item has not been printed; output it now */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = NULL; + multPrevEquStr = 0; + } + + return ( strbuf->nUsedLength - nUsedLength0 ); +} + + +/* + Produce isotopic numbering substring of AuxInfo. +*/ +int str_AuxIsoNumb( CANON_GLOBALS *pCG, + INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int TAUT_MODE, + int num_components, + int bSecondNonTautPass, + int bOmitRepetitions) +{ + int i, ii, ii2, nUsedLength0; + INCHI_SORT *is, *is0 /*, *is2*/; + INChI *pINChI, *pINChI_Taut; + INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev, *pINChI_Aux_Taut; + int eq2taut, bNext; + const char *pPrevEquStr, *pCurrEquStr; + int multPrevEquStr; + /************************************************** + * specificity of numbering: there is no previous * + * component because no repetition is possible * + **************************************************/ + pINChI = NULL; /* not used here, for debug only */ + pINChI_Taut = NULL; /* not used here, for debug only */ + pINChI_Aux = NULL; + pINChI_Aux_Taut = NULL; + pINChI_Aux_Prev = NULL; + bNext = 0; + is = NULL; + is0 = pINChISort; + /*is2 = bSecondNonTautPass? pINChISort2 : NULL;*/ + eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ + pPrevEquStr = NULL; /*, *pCurrEquStr;*/ + multPrevEquStr = 0; + nUsedLength0 = strbuf->nUsedLength; + + /* For each connected component... */ + for ( i = 0; i < num_components; i ++ ) + { + + /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ + is=is0+i; + pINChI_Aux = (i < num_components && 0 <= (ii=GET_II(bOutType,is)))? is->pINChI_Aux[ii] : NULL; + /*================ to compare to previously printed =====================*/ + if ( bSecondNonTautPass ) { + pINChI_Aux_Taut = (0 <= (ii2=GET_II(OUT_T1,is)))? is->pINChI_Aux[ii2] : NULL; + } + eq2taut = 0; + /*========= if bSecondNonTautPass then compare iso non-taut numb to other numb ========*/ + if ( bSecondNonTautPass && bOmitRepetitions && pINChI_Aux && pINChI_Aux->bIsIsotopic ) { + /* compare non-tautomeric isotopic numbering to: + * a) tautomeric numbering + * b) non-tautomeric numbering + * c) tautomeric isotopic numbering + */ + /* a) compare non-tautomeric isotopic numbering to tautomeric numbering */ + if ( !eq2taut ) { + eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_ISO, pINChI_Aux_Taut, EQL_NUM ); + /* numbering non-taut isotopic = taut numbering */ + eq2taut = eq2taut? ( iiNUMB | iitNONTAUT | iitISO ) : 0; + } + /* b) compare non-tautomeric isotopic numbering to non-tautomeric numbering */ + if ( !eq2taut ) { + eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_ISO, pINChI_Aux, EQL_NUM ); + /* numbering non-taut isotopic = non-taut numbering */ + eq2taut = eq2taut? ( iiNUMB | iitNONTAUT | iitISO | iiEq2NONTAUT ) : 0; + } + /* c) compare non-tautomeric isotopic numbering to tautomeric isotopic numbering */ + if ( !eq2taut ) { + eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_ISO, pINChI_Aux_Taut, EQL_NUM_ISO ); + /* numbering non-taut isotopic = taut isotopic numbering */ + eq2taut = eq2taut? ( iiNUMB | iitNONTAUT | iitISO | iiEq2ISO ) : 0; + } + } else + +/* 2011-10-28 + Fix bug in src:cano mapping of atoms printed to AuxInfo + Reported by Sandor Mark on 2011-10-25 in inchi-discuss + See http://sourceforge.net/p/inchi/mailman/message/28292914/ also +*/ +#define AUX_ISO_NUMB_BUG_FIX +#ifdef AUX_ISO_NUMB_BUG_FIX +/* Bug-fixed version */ + + /*========= if not bSecondNonTautPass then compare isotopic taut numbering to taut numbering ========*/ + if ( !bSecondNonTautPass && bOmitRepetitions && pINChI_Aux && pINChI_Aux->bIsIsotopic ) + { + /* compare tautomeric isotopic numbering to tautomeric non-isotopic numbering */ + if ( !eq2taut ) + { + eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_ISO, pINChI_Aux, EQL_NUM ); + /* numbering isotopic (taut) = taut numbering */ + eq2taut = eq2taut? ( iiNUMB | iitISO ) : 0; + } + } +#else +/* Original (buggy) version */ + + /*========= if not bSecondNonTautPass then compare inv taut stereo numb to taut numb ========*/ + if ( !bSecondNonTautPass && bOmitRepetitions && pINChI_Aux && pINChI_Aux->bIsIsotopic ) { + /* compare tautomeric isotopic numbering to tautomeric non-isotopic numbering */ + if ( !eq2taut ) { + eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_ISO, pINChI_Aux, EQL_NUM_ISO ); + /* stereo-inv numbering (taut) = taut numbering */ + eq2taut = eq2taut? (iiSTEREO_INV | iiNUMB ) : 0; + } + } +#endif + + if ( eq2taut ) { + /* we have found another (previously printed) layer of the current component equal to this layer */ + /* output this (current) equivalence mark = EquString(eq2taut) */ + pCurrEquStr = EquString(eq2taut); + if ( multPrevEquStr && pPrevEquStr ) { + if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { + multPrevEquStr ++; + } else { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + } else { + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + } else { + /* current layer is different from previously printed layers of the current component */ + if ( multPrevEquStr && pPrevEquStr ) { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = NULL; + multPrevEquStr = 0; + } + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( pINChI_Aux && pINChI_Aux->nNumberOfAtoms && pINChI_Aux->bIsIsotopic && + pINChI_Aux->nIsotopicOrigAtNosInCanonOrd ) { + MakeCtString( pCG, pINChI_Aux->nIsotopicOrigAtNosInCanonOrd, + pINChI_Aux->nNumberOfAtoms, + 0, NULL, 0, strbuf, TAUT_MODE, bOverflow); + } + /* else isotopic numbering is not present in pINChI */ + } + } + if ( multPrevEquStr && pPrevEquStr ) { + /* the new EqStr of the last item has not been printed; output it now */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = NULL; + multPrevEquStr = 0; + } + + return ( strbuf->nUsedLength - nUsedLength0 ); +} + + +/* + Produce isotopic equivalence substring of AuxInfo. +*/ +int str_AuxIsoEqu(INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int TAUT_MODE, + int num_components, + int bSecondNonTautPass, + int bOmitRepetitions, + int bUseMulipliers) +{ + int i, ii, ii2, nUsedLength0; + INCHI_SORT *is, *is2, *is0, *is20; + INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev, *pINChI_Aux_Taut, *pINChI_Aux_Taut_Prev; + int mult, eq2prev, eq2taut, eq2tautPrev, bNext; + const char *pPrevEquStr, *pCurrEquStr; + int multPrevEquStr; + pINChI_Aux = NULL; + pINChI_Aux_Prev = NULL; + pINChI_Aux_Taut = NULL; + pINChI_Aux_Taut_Prev = NULL; + mult = 0; + bNext = 0; + is = NULL; + is2 = NULL; + is0 = pINChISort; + is20 = bSecondNonTautPass? pINChISort2 : NULL; + eq2taut = 0; /* may be non-zero only on the 2nd (non-taut) pass */ + eq2tautPrev = 1; /* pINChI_Aux_Prev (previous pINChI_Aux) does not exist */ + pPrevEquStr = NULL; /*, *pCurrEquStr;*/ + multPrevEquStr = 0; + nUsedLength0 = strbuf->nUsedLength; + + /* For each connected component... */ + for ( i = 0; i <= num_components; i ++ ) + { + + /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ + pINChI_Aux = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI_Aux[ii] : NULL; + if ( bSecondNonTautPass ) { + /* component that was output on the 1st pass */ + pINChI_Aux_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI_Aux[ii2] : NULL; + } + /*================ compare iso non-taut equivalence info to non-iso taut ========*/ + eq2taut = 0; + if ( bSecondNonTautPass && bOmitRepetitions && pINChI_Aux && pINChI_Aux->bIsIsotopic ) { + /************************************************** + * compare isotopic non-tautomeric equivalence to: + * a) tautomeric + * b) non-tautomeric + * c) isotopic tautomeric + */ + if ( !eq2taut ) { + /* compare isotopic non-tautomeric equivalence to tautomeric */ + eq2taut = Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_ISO, pINChI_Aux_Taut, EQL_EQU ); + /* equ non-taut isotopic = tautomeric*/ + eq2taut = eq2taut? (iiEQU | iitNONTAUT | iitISO) : 0; + } + if ( !eq2taut ) { + /* compare isotopic non-tautomeric equivalence to non-tautomeric */ + eq2taut = Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_ISO, pINChI_Aux, EQL_EQU ); + /* equ non-taut isotopic = non-tautomeric*/ + eq2taut = eq2taut? (iiEQU | iitNONTAUT | iitISO | iiEq2NONTAUT) : 0; + } + if ( !eq2taut ) { + /* compare isotopic non-tautomeric equivalence to isotopic tautomeric */ + eq2taut = Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_ISO, pINChI_Aux_Taut, EQL_EQU_ISO ); + /* equ non-taut isotopic = isotopic tautomeric*/ + eq2taut = eq2taut? (iiEQU | iitNONTAUT | iitISO | iiEq2ISO) : 0; + } + } else + if ( !bSecondNonTautPass && bOmitRepetitions && pINChI_Aux && pINChI_Aux->bIsIsotopic ) { + /************************************************** + * compare isotopic tautomeric equivalence to: + * a) non-isotopic tautomeric + */ + if ( !eq2taut ) { + /* compare isotopic tautomeric equivalence to tautomeric */ + eq2taut = Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_ISO, pINChI_Aux, EQL_EQU ); + /* equ taut-isotopic = tautomeric*/ + eq2taut = eq2taut? (iiEQU | iitISO) : 0; + } + } + if ( eq2taut ) { + /* we may be here only in case of the second (non-taut) pass */ + /* current non-taut equivalence has been found to be same as tautomeric */ + if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { + /* previous component exists */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( bHasEquString( pINChI_Aux_Prev->nConstitEquIsotopicNumbers, pINChI_Aux_Prev->nNumberOfAtoms) ) { + /* output previous component(s) equivalence since it was found to be non-trivial */ + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + MakeEquString( pINChI_Aux_Prev->nConstitEquIsotopicNumbers, pINChI_Aux_Prev->nNumberOfAtoms, 0, + strbuf, TAUT_MODE, bOverflow); + } else { + ; /* pINChI_Aux_Prev exists and has only trivial equivalence info */ + } + } else + if ( pINChI_Aux_Taut_Prev && pINChI_Aux_Taut_Prev->nNumberOfAtoms ) { + /* previous non-taut component exists only in taut list */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + } + /* we have found pINChI_Aux->pINChI_Aux->nConstitEquIsotopicNumbers same as in pINChI_Aux_Taut */ + /* output this (current) equivalence as '*', that is, same as tautomeric */ + /* that was printed on the 1st pass. */ + pCurrEquStr = EquString(eq2taut); + if ( multPrevEquStr && pPrevEquStr ) { + if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { + multPrevEquStr ++; + } else { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + } else { + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + pINChI_Aux_Prev = NULL; /* pINChI_Aux_Prev does not exist since */ + pINChI_Aux_Taut_Prev = NULL; /* pINChI_Aux has just been printed */ + mult = 0; + eq2tautPrev = 1; + } else + if ( eq2tautPrev ) { + /* at this point pINChI_Aux_Prev does not exist; however, pINChI_Aux */ + /*might have been discovered and it is different from pINChI_Aux_Taut */ + if ( multPrevEquStr && pPrevEquStr ) { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = NULL; + multPrevEquStr = 0; + } + eq2tautPrev = 0; + pINChI_Aux_Prev = pINChI_Aux; + pINChI_Aux_Taut_Prev = pINChI_Aux_Taut; + mult = 0; + } else { + /* check whether pINChI_Aux and pINChI_Aux_Prev have identical non-trivial equivalence info */ + eq2prev = bUseMulipliers && Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_ISO, pINChI_Aux_Prev, EQL_EQU_ISO ); + if ( eq2prev ) { + /* eq. info is same and non-trivial */ + mult ++; /* mult = (number of non-empty equal items)-1 */ + continue; + } else { + /* pINChI_Aux eq. info is either different or trivial. Output pINChI_Aux_Prev anyway */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { + if ( bHasEquString( pINChI_Aux_Prev->nConstitEquIsotopicNumbers, pINChI_Aux_Prev->nNumberOfAtoms) ) { + /* pINChI_Aux_Prev exists and has equivalence info */ + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + MakeEquString( pINChI_Aux_Prev->nConstitEquIsotopicNumbers, pINChI_Aux_Prev->nNumberOfAtoms, 0, + strbuf, TAUT_MODE, bOverflow); + } else { + ; /* pINChI_Aux_Prev exists and has only trivial equivalence info */ + } + } else + if ( bSecondNonTautPass && pINChI_Aux_Taut_Prev && pINChI_Aux_Taut_Prev->nNumberOfAtoms ) { + if ( bHasEquString( pINChI_Aux_Taut_Prev->nConstitEquIsotopicNumbers, pINChI_Aux_Taut_Prev->nNumberOfAtoms) ) { + /* since pINChI_Aux_Prev does not exist, pINChI_Aux_Taut_Prev is non-tautomeric */ + /* and it has non-trivial equivalence info. This info has already been printed in the main section */ + /* + MakeDelim( sIdenticalValues, strbuf, bOverflow); + */ + } else { + ; /* pINChI_Aux_Taut_Prev exists and has only trivial equivalence info */ + } + } +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + else { + int stop = 1; /* */ + } +#endif + } + pINChI_Aux_Prev = pINChI_Aux; + pINChI_Aux_Taut_Prev = pINChI_Aux_Taut; + mult = 0; /* we do not know whether the item is empty */ + } + } + + return ( strbuf->nUsedLength - nUsedLength0 ); +} + + +/* + Produce isotopic tetr stereo inversion substring of AuxInfo. +*/ +int str_AuxInvIsoSp3( INCHI_SORT *pINChISort, INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, int bOutType, int TAUT_MODE, int num_components, + int bSecondNonTautPass, int bOmitRepetitions, int bUseMulipliers) +{ + int i, ii, ii2, nUsedLength0; + INCHI_SORT *is, *is2, *is0, *is20; + INChI *pINChI, *pINChI_Prev, *pINChI_Taut, *pINChI_Taut_Prev; + INChI_Stereo *Stereo, *Stereo_Prev, *Stereo_Taut, *Stereo_Taut_Prev; + int mult, eq2prev, eq2taut, eq2tautPrev, bNext; + const char *pPrevEquStr, *pCurrEquStr; + int multPrevEquStr; + /******************************** + inverted isotopic sp3 + *********************************/ + pINChI_Taut = NULL; + pINChI_Prev = NULL; + pINChI_Taut_Prev = NULL; + mult = 0; + bNext = 0; + is = NULL; + is2 = NULL; + is0 = pINChISort; + is20 = bSecondNonTautPass? pINChISort2 : NULL; + eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ + eq2tautPrev = 1; /* pINChI_Prev (previous pINChI) does not exist */ + pPrevEquStr = NULL; /*, *pCurrEquStr;*/ + multPrevEquStr = 0; + nUsedLength0 = strbuf->nUsedLength; + + /* For each connected component... */ + for ( i = 0; i <= num_components; i ++ ) + { + + /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ + pINChI = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI[ii] : NULL; + /*================ compare sp2 to previous =====================*/ + if ( bSecondNonTautPass ) { + /* component that was output on the 1st pass */ + pINChI_Taut = ( i < num_components && (is2=is20+i, 0 <= (ii2=GET_II(OUT_T1,is2))))? is2->pINChI[ii2] : NULL; + } + eq2taut = 0; + /*========= if bSecondNonTautPass then compare iso non-taut stereo to other stereo ========*/ + if ( bSecondNonTautPass && bOmitRepetitions && pINChI && pINChI->nNumberOfIsotopicAtoms+pINChI->nNumberOfIsotopicTGroups > 0 ) { + /* compare non-tautomeric isotopic inverted to: + * a) tautomeric inverted + * b) *non-tautomeric inverted + * c) *isotopic tautomeric inverted + * d) Inverted(tautomeric) + * e) *Inverted(tautomeric isotopic) + * f) Inverted(non-tautomeric) + * g) *Inverted(non-tautomeric isotopic) + */ + /* a) compare non-tautomeric isotopic inverted to tautomeric inverted */ + if ( !eq2taut ) { + eq2taut = pINChI && pINChI_Taut && + /* non-taut inverted */ /* taut invertedc */ + (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->Stereo) && + Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3_INV, 0 ); + /* stereo-inv isotopic non-taut = taut (stereo-inv) */ + eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT ) : 0; + } + /* b) compare non-tautomeric isotopic inverted to non-tautomeric inverted */ + if ( !eq2taut ) { + eq2taut = pINChI && /* it is non-taut non-iso stereo */ + (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && + Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3_INV, 0 ); + /* stereo-inv isotopic non-taut = non-taut stereo-inv */ + eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT | iiEq2NONTAUT) : 0; + } + /* c) compare non-tautomeric isotopic inverted to isotopic tautomeric inverted */ + if ( !eq2taut ) { + eq2taut = pINChI && pINChI_Taut && + /* non-taut iso. inverted */ /* taut iso. inverted */ + (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && + Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3_INV, 0 ); + /* stereo-inv isotopic non-taut = taut iso. stereo-inv */ + eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT | iiEq2ISO ) : 0; + } + /* d) compare non-tautomeric inverted to Inverted(tautomeric stereo) */ + if ( !eq2taut ) { + eq2taut = pINChI && pINChI_Taut && + (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->Stereo) && + Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3_INV, 0 ); + /* stereo-inv isotopic non-taut = Inv(non-iso taut stereo) */ + eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT | iiEq2INV) : 0; + } + /* e) compare non-tautomeric inverted to Inverted(isotopic tautomeric stereo) */ + if ( !eq2taut ) { + eq2taut = pINChI && pINChI_Taut && + (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && + Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3, 0 ); + /* stereo-inv isotopic non-taut = Inv(iso taut stereo) */ + eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT | iiEq2INV | iiEq2ISO) : 0; + } + /* f) compare non-tautomeric isotopic inverted to Inverted(non-tautomeric stereo) */ + if ( !eq2taut ) { + eq2taut = pINChI && /* it is non-taut non-iso stereo */ + (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && + Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3, 0 ); + /* stereo-inv isotopic non-taut = Inv(non-taut stereo) */ + eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT | iiEq2INV | iiEq2NONTAUT) : 0; + } + /* g) compare non-tautomeric isotopic inverted to Inverted(non-tautomeric isotopic stereo) */ + if ( !eq2taut ) { + eq2taut = pINChI && /* it is non-taut non-iso stereo */ + (Stereo = pINChI->StereoIsotopic) && + Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo, EQL_SP3, 0 ); + /* stereo-inv isotopic non-taut = Inv( iso non-taut stereo) */ + eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iitNONTAUT | iiEq2INV | iiEq2ISO | iiEq2NONTAUT) : 0; + } +#if ( FIX_EMPTY_LAYER_BUG == 1 ) + if ( !eq2taut && pINChI && !((Stereo = pINChI->StereoIsotopic) && + Eql_INChI_Stereo( Stereo, EQL_SP3_INV, NULL, EQL_EXISTS, 0 )) ) { + /* component has no stereo; check whether it has stereo in the preceding layers */ + if ( pINChI_Taut && (Stereo_Taut = pINChI_Taut->Stereo) && /* F is not empty */ + Eql_INChI_Stereo( Stereo_Taut, EQL_SP3_INV, NULL, EQL_EXISTS, 0 ) || + !(pINChI_Taut && (Stereo_Taut = pINChI_Taut->Stereo) && /* M is empty and ... */ + Eql_INChI_Stereo( Stereo_Taut, EQL_SP3_INV, NULL, EQL_EXISTS, 0 )) && + (pINChI_Taut && (Stereo_Taut = pINChI_Taut->StereoIsotopic) && /* ... MI is not empty */ + Eql_INChI_Stereo( Stereo_Taut, EQL_SP3_INV, NULL, EQL_EXISTS, 0 )) ) { + + eq2taut = iiEmpty; /* the component has stereo in the preceding layer */ + } + } +#endif + } else + /*========= if not bSecondNonTautPass then compare inv taut stereo to various stereo ========*/ + if ( !bSecondNonTautPass && bOmitRepetitions && pINChI && + (pINChI->nNumberOfIsotopicAtoms > 0 || + pINChI->nNumberOfIsotopicTGroups > 0 || + pINChI->nPossibleLocationsOfIsotopicH && pINChI->nPossibleLocationsOfIsotopicH[0] > 1) ) { + /* compare tautomeric isotopic stereo-inverted to: + * a) tautomeric stereo-inverted + * b) Inverted(tautomeric stereo) + * c) Inverted(tautomeric isotopic stereo) + */ + /* a) compare tautomeric isotopic stereo-inverted to tautomeric stereo-inverted */ + if ( !eq2taut ) { + eq2taut = pINChI && + (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && + Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3_INV, 0 ); + /* stereo-inv isotopic taut = taut stereo-inv */ + eq2taut = eq2taut? (iiSTEREO_INV | iitISO ) : 0; + } + /* b) compare tautomeric isotopic stereo-inverted to Inverted(tautomeric stereo) */ + if ( !eq2taut ) { + eq2taut = pINChI && + (Stereo = pINChI->StereoIsotopic) && (Stereo_Taut = pINChI->Stereo) && + Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Taut, EQL_SP3, 0 ); + /* stereo-inv isotopic taut = Inv(taut stereo) */ + eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiEq2INV ) : 0; + } + /* c) compare tautomeric isotopic stereo-inverted to Inverted(tautomeric isotopic stereo) */ + if ( !eq2taut ) { + eq2taut = pINChI && + (Stereo = pINChI->StereoIsotopic) && + Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo, EQL_SP3, 0 ); + /* stereo-inv isotopic taut = Inv(taut iso stereo) */ + eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiEq2INV | iiEq2ISO ) : 0; + } +#if ( FIX_EMPTY_LAYER_BUG == 1 ) + if ( !eq2taut && pINChI && !((Stereo = pINChI->StereoIsotopic) && + Eql_INChI_Stereo( Stereo, EQL_SP3_INV, NULL, EQL_EXISTS, 0 ) ) ) { + /* component has no MI stereo; check whether it has stereo in the preceding layer M */ + if ( (Stereo_Taut = pINChI->Stereo) && + Eql_INChI_Stereo( Stereo_Taut, EQL_SP3_INV, NULL, EQL_EXISTS, 0 ) ) { + eq2taut = iiEmpty; /* the component has stereo in the preceding layer */ + } + } +#endif + } + if ( eq2taut ) { + /* we may be here only in case of the current layer found equal in another layer the same component */ + if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms ) { + /* previous component exists; output it before output the current component */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( (Stereo_Prev = pINChI_Prev->StereoIsotopic) && Stereo_Prev->nNumberOfStereoCenters > 0 ) { + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + + MakeStereoString( Stereo_Prev->nNumber, NULL, Stereo_Prev->t_parityInv, + 0, Stereo_Prev->nNumberOfStereoCenters, + strbuf, TAUT_MODE, bOverflow); + } + } else + if ( pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { + /* previous non-taut component exists only in taut list */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + /* do not output stereo of non-tautomeric in non-taut layer: it has been output in the main layer */ + } + /* we have found another (previously printed) layer of the current component equal to this layer */ + /* output this (current) equivalence mark = EquString(eq2taut) */ + pCurrEquStr = EquString(eq2taut); + if ( multPrevEquStr && pPrevEquStr ) { + if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { + multPrevEquStr ++; + } else { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + } else { + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + pINChI_Prev = NULL; /* pINChI_Prev sp2 does not exist since */ + pINChI_Taut_Prev = NULL; /* pINChI has just been printed */ + mult = 0; + eq2tautPrev = 1; /* pINChI_Prev and pINChI_Taut_Prev have already been output */ + } else + if ( eq2tautPrev ) { + /* at this point pINChI_Prev does not exist; however, pINChI */ + /*might have been discovered and it is different from pINChI_Taut */ + if ( multPrevEquStr && pPrevEquStr ) { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = NULL; + multPrevEquStr = 0; + } + eq2tautPrev = 0; + pINChI_Prev = pINChI; + pINChI_Taut_Prev = pINChI_Taut; + mult = 0; + } else { + /* current layer is different from previously printed layers of the current component */ + /* compare the current layer to this layer of the previous component: */ + /* check whether pINChI and pINChI_Prev have non-zero identical stereo sp2 */ + /*================ compare iso sp3 to previous =====================*/ + eq2prev =bUseMulipliers && + pINChI && pINChI->nNumberOfIsotopicAtoms + pINChI->nNumberOfIsotopicTGroups > 0 && + pINChI_Prev && pINChI_Prev->nNumberOfIsotopicAtoms + pINChI_Prev->nNumberOfIsotopicTGroups > 0 && + /* do both have stereo? */ + (Stereo = pINChI->StereoIsotopic) && (Stereo_Prev = pINChI_Prev->StereoIsotopic) && + /* is their inverted stereo same? */ + Eql_INChI_Stereo( Stereo, EQL_SP3_INV, Stereo_Prev, EQL_SP3_INV, 0 ); + if ( eq2prev ) { + mult ++; /* mult = (number of non-empty equal items)-1 */ + continue; + } else { + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( pINChI_Prev && pINChI_Prev->nNumberOfAtoms && pINChI_Prev->nNumberOfIsotopicAtoms + pINChI_Prev->nNumberOfIsotopicTGroups > 0 ) { + if ( (Stereo_Prev = pINChI_Prev->StereoIsotopic) && + Stereo_Prev->nNumberOfStereoCenters > 0 && Stereo_Prev->nCompInv2Abs ) { + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + + MakeStereoString( Stereo_Prev->nNumberInv, NULL, Stereo_Prev->t_parityInv, + 0, Stereo_Prev->nNumberOfStereoCenters, + strbuf, TAUT_MODE, bOverflow); + } + /* else sp3 info is not present in pINChI_Prev */ + } else + /* do not print pINChI_Prev because it either do not exist of have already been printed */ + if ( bSecondNonTautPass && pINChI_Taut_Prev && pINChI_Taut_Prev->nNumberOfAtoms ) { + if ( (Stereo_Taut_Prev = pINChI_Taut_Prev->StereoIsotopic) && + Stereo_Taut_Prev->nNumberOfStereoCenters > 0 && Stereo_Taut_Prev->nCompInv2Abs ) { + /* since pINChI_Prev does not exist, pINChI_Taut_Prev is non-tautomeric */ + /* and it has non-trivial inv sp3 info. It has already been printed in the main section */ + /* + tot_len += MakeDelim( sIdenticalValues, strbuf, bOverflow); + */ + ;/* pINChI_Taut_Prev sp3 info was output in the main stereo section */ + } else { + ; /* pINChI_Taut_Prev exists and has not sp3 info */ + } + } +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + else { + int stop = 1; /* */ + } +#endif + } + pINChI_Prev = pINChI; + mult = 0; /* we do not know whether the item is empty */ + } + } + + return ( strbuf->nUsedLength - nUsedLength0 ); +} + + +/* + Produce isotopic tetr stereo inversion numbering substring of AuxInfo. +*/ +int str_AuxInvIsoSp3Numb( CANON_GLOBALS *pCG, + INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int TAUT_MODE, + int num_components, + int bSecondNonTautPass, + int bOmitRepetitions) +{ + int i, ii, ii2, nUsedLength0; + INCHI_SORT *is, *is0 /*, *is2*/; + INChI *pINChI, *pINChI_Taut; + INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev, *pINChI_Aux_Taut; + INChI_Stereo *Stereo, *Stereo_Taut; + int eq2taut, bNext; + const char *pPrevEquStr, *pCurrEquStr; + int multPrevEquStr; + /************************************************** + * specificity of numbering: there is no previous * + * component because no repetition is possible * + **************************************************/ + pINChI = NULL; + pINChI_Taut = NULL; + pINChI_Aux = NULL; + pINChI_Aux_Taut = NULL; + pINChI_Aux_Prev = NULL; + bNext = 0; + is = NULL; + /* is2 = NULL;*/ + is0 = pINChISort; + /* is20 = bSecondNonTautPass? pINChISort2 : NULL;*/ + eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ + pPrevEquStr = NULL; /*, *pCurrEquStr;*/ + multPrevEquStr = 0; + nUsedLength0 = strbuf->nUsedLength; + + /* For each connected component... */ + for ( i = 0; i < num_components; i ++ ) + { + + /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ + is=is0+i; + pINChI = (0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; + pINChI_Aux = pINChI? is->pINChI_Aux[ii] : NULL; + /*================ to compare to previously printed =====================*/ + if ( bSecondNonTautPass ) { + /* component that was printed on the 1st pass */ + pINChI_Taut = (0 <= (ii2=GET_II(OUT_T1,is)))? is->pINChI[ii2] : NULL; + pINChI_Aux_Taut = pINChI_Taut? is->pINChI_Aux[ii2] : NULL; + } + eq2taut = 0; + /*========= if bSecondNonTautPass then compare iso non-taut stereo to other stereo ========*/ + if ( bSecondNonTautPass && bOmitRepetitions && pINChI && pINChI_Aux && pINChI_Aux->bIsIsotopic && + (Stereo = pINChI->StereoIsotopic) && Stereo->nCompInv2Abs && + pINChI_Aux->nNumberOfAtoms > 0 && pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv ) { + /* compare isotopic non-tautomeric inverted stereo numbering to: + * a) tautomeric numbering + * b) non-tautomeric numbering + * c) *tautomeric isotopic numbering + * d) *non-tautomeric isotopic numbering + * e) tautomeric inverted stereo numbering + * f) *non-tautomeric inverted stereo numbering + * g) tautomeric isotopic inverted stereo numbering + */ + /* a) compare isotopic non-tautomeric inverted stereo numbering to tautomeric numbering */ + if ( !eq2taut ) { + eq2taut = pINChI_Taut && + Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux_Taut, EQL_NUM ); + /* stereo-inv isotopic numbering non-taut = taut numbering */ + eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT ) : 0; + } + /* b) compare isotopic non-tautomeric inverted stereo numbering to non-tautomeric numbering */ + if ( !eq2taut ) { + eq2taut = + Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux, EQL_NUM ); + /* stereo-inv isotopic numb. non-taut = non-taut numbering */ + eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT | iiEq2NONTAUT ) : 0; + } + /* c) compare isotopic non-tautomeric inverted stereo numbering to tautomeric isotopic numbering */ + if ( !eq2taut ) { + eq2taut = pINChI_Taut && + Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux_Taut, EQL_NUM_ISO ); + /* stereo-inv isotopic numb. non-taut = taut iso numbering */ + eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT | iiEq2ISO ) : 0; + } + /* d) compare isotopic non-tautomeric inverted stereo numbering to non-tautomeric isotopic numbering */ + if ( !eq2taut ) { + eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux, EQL_NUM_ISO ); + /* stereo-inv isotopic numb. non-taut = non-taut isotopic numbering */ + eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT | iiEq2NONTAUT | iiEq2ISO) : 0; + } + /* e) compare isotopic non-tautomeric inverted stereo numbering to tautomeric inverted stereo numbering */ + if ( !eq2taut ) { + eq2taut = pINChI_Taut && pINChI_Aux_Taut && + (Stereo_Taut = pINChI_Taut->Stereo) && Stereo_Taut->nCompInv2Abs && + Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux_Taut, EQL_NUM_INV ); + /* stereo-inv isotopic numbering non-taut = stereo-inv taut numbering */ + eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT | iiEq2INV) : 0; + } + /* f) compare isotopic non-tautomeric inverted stereo numbering to non-tautomeric inverted stereo numbering */ + if ( !eq2taut ) { + eq2taut = + (Stereo_Taut = pINChI->StereoIsotopic) && Stereo_Taut->nCompInv2Abs && + Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux, EQL_NUM_INV ); + /* stereo-inv isotopic numbering non-taut = stereo-inv non-taut numbering */ + eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT | iiEq2INV | iiEq2NONTAUT) : 0; + } + + /* g) compare isotopic non-tautomeric inverted stereo numbering to tautomeric isotopic inverted stereo numbering */ + if ( !eq2taut ) { + eq2taut = pINChI_Taut && + (Stereo_Taut = pINChI_Taut->StereoIsotopic) && Stereo_Taut->nCompInv2Abs && + Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux_Taut, EQL_NUM_INV | EQL_NUM_ISO ); + /* stereo-inv isotopic numbering non-taut = stereo-inv iso taut numbering */ + eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iitNONTAUT | iiEq2INV | iiEq2ISO) : 0; + } + } else + /*========= if not bSecondNonTautPass then compare inv taut stereo numb to taut numb ========*/ + if ( !bSecondNonTautPass && bOmitRepetitions && pINChI && pINChI_Aux && pINChI_Aux->bIsIsotopic && + (Stereo = pINChI->StereoIsotopic) && Stereo->nCompInv2Abs && + pINChI_Aux->nNumberOfAtoms > 0 && pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv ) { + /* compare isotopic tautomeric inverted stereo numbering to: + * a) tautomeric numbering + * b) tautomeric isotopic numbering + * c) tautomeric inverted stereo numbering + */ + /* a) compare isotopic tautomeric inverted stereo numbering to tautomeric numbering */ + if ( !eq2taut ) { + eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux, EQL_NUM ); + /* stereo-inv isotopic numbering (taut) = taut numbering */ + eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB ) : 0; + } + /* b) compare isotopic tautomeric inverted stereo numbering to tautomeric isotopic numbering */ + if ( !eq2taut ) { + eq2taut = Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux, EQL_NUM_ISO ); + /* stereo-inv isotopic numbering(taut) = isotopic taut numbering */ + eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iiEq2ISO ) : 0; + } + /* b) compare isotopic tautomeric inverted stereo numbering to tautomeric inverted stereo numbering */ + if ( !eq2taut ) { + eq2taut = (Stereo_Taut = pINChI->Stereo) && Stereo->nCompInv2Abs && + Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM_INV | EQL_NUM_ISO, pINChI_Aux, EQL_NUM_INV ); + /* stereo-inv isotopic numbering (taut) = taut stereo-inv numbering */ + eq2taut = eq2taut? (iiSTEREO_INV | iitISO | iiNUMB | iiEq2INV ) : 0; + } + } + if ( eq2taut ) { + /* we have found another (previously printed) layer of the current component equal to this layer */ + /* output this (current) equivalence mark = EquString(eq2taut) */ + pCurrEquStr = EquString(eq2taut); + if ( multPrevEquStr && pPrevEquStr ) { + if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { + multPrevEquStr ++; + } else { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + } else { + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + } else { + /* current layer is different from previously printed layers of the current component */ + if ( multPrevEquStr && pPrevEquStr ) { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = NULL; + multPrevEquStr = 0; + } + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( pINChI && pINChI_Aux && pINChI_Aux->bIsIsotopic && pINChI_Aux->nNumberOfAtoms && + (Stereo = pINChI->StereoIsotopic) && Stereo->nNumberOfStereoCenters && + Stereo->nCompInv2Abs && pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv ) { + MakeCtString( pCG, pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv, + pINChI_Aux->nNumberOfAtoms, 0, NULL, 0, + strbuf, TAUT_MODE, bOverflow); + } + /* else isotopic inv stereo info is not present in pINChI */ + } + } + if ( multPrevEquStr && pPrevEquStr ) { + /* the new EqStr of the last item has not been printed; output it now */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = NULL; + multPrevEquStr = 0; + } + + return ( strbuf->nUsedLength - nUsedLength0 ); +} + + +/* + Produce numbering substring of AuxInfo +*/ +int str_AuxNumb( CANON_GLOBALS *pCG, + INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int TAUT_MODE, + int num_components, + int bSecondNonTautPass, + int bOmitRepetitions) +{ + int i, ii, ii2, nUsedLength0; + INCHI_SORT *is, *is0 /*, *is2*/; + INChI *pINChI, *pINChI_Taut=NULL; + INChI_Aux *pINChI_Aux, *pINChI_Aux_Taut=NULL; + int eq2taut, bNext; + const char *pPrevEquStr, *pCurrEquStr; + int multPrevEquStr; + bNext = 0; + /*is2 = bSecondNonTautPass? pINChISort2 : NULL;*/ + eq2taut = 0; /* may be non-zero if another layer of the current component = current layer */ + pPrevEquStr = NULL; /*, *pCurrEquStr;*/ + multPrevEquStr = 0; + is = NULL; + nUsedLength0 = strbuf->nUsedLength; + + if ( !(is0 = pINChISort) ) { + return nUsedLength0; + } + + /* For each connected component... */ + for ( i = 0; i < num_components; i ++ ) + { + /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ + is=is0+i; + pINChI = ( 0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; + pINChI_Aux = pINChI? is->pINChI_Aux[ii] : NULL; + /*================ to compare to previously printed =====================*/ + if ( bSecondNonTautPass ) { + /* component that was printed on the 1st pass */ + pINChI_Taut = (0 <= (ii2=GET_II(OUT_T1,is)))? is->pINChI[ii2] : NULL; + pINChI_Aux_Taut = pINChI_Taut? is->pINChI_Aux[ii2] : NULL; + } + eq2taut = 0; + /*========= if bSecondNonTautPass then compare iso non-taut stereo to other stereo ========*/ + if ( bSecondNonTautPass && bOmitRepetitions && pINChI && pINChI_Aux && pINChI_Aux->nNumberOfAtoms > 0 ) { + /* compare non-tautomeric numbering to: + * a) tautomeric numbering + */ + /* a) compare non-tautomeric numbering to tautomeric numbering */ + if ( !eq2taut ) { + eq2taut = pINChI_Taut && !pINChI_Taut->bDeleted && + Eql_INChI_Aux_Num( pINChI_Aux, EQL_NUM, pINChI_Aux_Taut, EQL_NUM ); + /* numbering non-taut = taut numbering */ + eq2taut = eq2taut? ( iiNUMB | iitNONTAUT ) : 0; + } + } + if ( eq2taut ) { + /* we have found another (previously printed) layer of the current component equal to this layer */ + /* output this (current) equivalence mark = EquString(eq2taut) */ + pCurrEquStr = EquString(eq2taut); + if ( multPrevEquStr && pPrevEquStr ) { + if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { + multPrevEquStr ++; + } else { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + } else { + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + } else { + /* current layer is different from previously printed layers of the current component */ + if ( multPrevEquStr && pPrevEquStr ) { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = NULL; + multPrevEquStr = 0; + } + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( pINChI && pINChI_Aux && pINChI_Aux->nNumberOfAtoms ) { + MakeCtString( pCG, pINChI_Aux->nOrigAtNosInCanonOrd, + pINChI_Aux->nNumberOfAtoms, 0, NULL, 0, + strbuf, TAUT_MODE, bOverflow); + } + } + } + if ( multPrevEquStr && pPrevEquStr ) { + /* the new EqStr of the last item has not been printed; output it now */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = NULL; + multPrevEquStr = 0; + } + + return ( strbuf->nUsedLength - nUsedLength0 ); +} + + +/* + Produce TGroup equivalence substring of AuxInfo +*/ +int str_AuxTgroupEqu( INCHI_SORT *pINChISort, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, int bOutType, int TAUT_MODE, int num_components, + int bUseMulipliers) +{ + int i, ii, nUsedLength0; + INCHI_SORT *is, *is0; + INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev; + int mult, eq2prev, bNext; + + nUsedLength0 = strbuf->nUsedLength; + + is0 = pINChISort; + is = NULL; + i = 0; + pINChI_Aux_Prev = (0 <= (ii=GET_II(bOutType,is0)))? is0->pINChI_Aux[ii] : NULL; + mult = 0; + bNext = 0; + + /* For each connected component... */ + for ( i++; i <= num_components; i ++ ) + { + pINChI_Aux = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI_Aux[ii] : NULL; + eq2prev = bUseMulipliers && + Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_TG, pINChI_Aux_Prev, EQL_EQU_TG ); + if ( eq2prev ) { + mult ++; /* mult = (number of non-empty equal items)-1 */ + continue; + } else { + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfTGroups && + bHasEquString( pINChI_Aux_Prev->nConstitEquTGroupNumbers, pINChI_Aux_Prev->nNumberOfTGroups) ) { + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + MakeEquString( pINChI_Aux_Prev->nConstitEquTGroupNumbers, + pINChI_Aux_Prev->nNumberOfTGroups, + 0, strbuf, + TAUT_MODE, bOverflow); + } + } + pINChI_Aux_Prev = pINChI_Aux; + mult = 0; /* we do not know whether the item is empty */ + } + + return ( strbuf->nUsedLength - nUsedLength0 ); +} + + +/* + Produce charge-radical-valence substring of AuxInfo +*/ +int str_AuxChargeRadVal( INCHI_SORT *pINChISort, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int TAUT_MODE, + int num_components, + int bUseMulipliers) +{ + int i, ii, nUsedLength0; + INCHI_SORT *is, *is0; + INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev; + int mult, eq2prev, bNext; + + pINChI_Aux_Prev = NULL; + mult = 0; + bNext = 0; + is = NULL; + is0 = pINChISort; + nUsedLength0 = strbuf->nUsedLength; + + /* For each connected component... */ + for ( i = 0; i <= num_components; i ++ ) + { + + /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ + pINChI_Aux = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI_Aux[ii] : NULL; + /* check whether pINChI_Aux and pINChI_Aux_Prev have identical info */ + eq2prev = bUseMulipliers && + EqlOrigInfo( pINChI_Aux, pINChI_Aux_Prev ); + if ( eq2prev ) { + /* eq. info is same and non-trivial */ + mult ++; /* mult = (number of non-empty equal items)-1 */ + continue; + } else + if ( i ) { + /* pINChI_Aux info is either different or trivial. Output pINChI_Aux_Prev anyway */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { + if ( bHasOrigInfo( pINChI_Aux_Prev->OrigInfo, pINChI_Aux_Prev->nNumberOfAtoms ) ) { + /* pINChI_Aux_Prev exists and has orig. info info */ + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + MakeCRVString( pINChI_Aux_Prev->OrigInfo, + pINChI_Aux_Prev->nNumberOfAtoms, + 0, strbuf, TAUT_MODE, bOverflow); + } else { + ; /* pINChI_Aux_Prev exists and has only trivial info */ + } + } +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + else { + int stop = 1; /* */ + } +#endif + } + pINChI_Aux_Prev = pINChI_Aux; + mult = 0; /* we do not know whether the item is empty */ + } + + return ( strbuf->nUsedLength - nUsedLength0 ); +} + + +/* + Prepare tautomeric transposition substring of AuxInfo +*/ +int bin_AuxTautTrans( INCHI_SORT *pINChISort, + INCHI_SORT *pINChISort2, + AT_NUMB **pTrans_n, + AT_NUMB **pTrans_s, + int bOutType, + int num_components) +{ + int i, ii, ii2, ret; + INCHI_SORT *is, *is2, *is0, *is20; + INChI *pINChI, *pINChI_Taut; + AT_NUMB *nTrans_n = NULL; + AT_NUMB *nTrans_s = NULL; + + ret = 0; + is0 = pINChISort; + is20 = pINChISort2; + + /* Pass 1: save new non-taut numbering */ + + /* For each connected component... */ + for ( i = 0; i < num_components; i ++ ) + { + is=is0+i; + is2=is20+i; + pINChI = ( 0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; + pINChI_Taut = ( 0 <= (ii2=GET_II(OUT_T1,is2)))? is2->pINChI[ii2] : NULL; + if ( pINChI && pINChI->nNumberOfAtoms > 0 && + pINChI_Taut && pINChI_Taut->nNumberOfAtoms > 0 && + /* different components save equal new ord. numbers: */ + is->ord_number != is2->ord_number ) { + if ( (nTrans_n && nTrans_s) || + (nTrans_n = (AT_NUMB *)inchi_calloc( num_components+1, sizeof(nTrans_n[0]))) && + (nTrans_s = (AT_NUMB *)inchi_calloc( num_components+1, sizeof(nTrans_s[0]))) ) { + /* new ordering number for original non-tautomeric component number is->ord_number */ + nTrans_n[is->ord_number] = /*nTrans_t[is2->ord_number] =*/ i+1; + } + } + } + if ( nTrans_n && nTrans_s ) { + /* pass 2: get new taut numbering, retrieve new non-taut and save the transposition */ + for ( i = 0; i < num_components; i ++ ) { + is=is0+i; + is2=is20+i; + pINChI = ( 0 <= (ii=GET_II(bOutType,is)))? is->pINChI[ii] : NULL; + pINChI_Taut = ( 0 <= (ii2=GET_II(OUT_T1,is2)))? is2->pINChI[ii2] : NULL; + if ( pINChI && pINChI->nNumberOfAtoms > 0 && + pINChI_Taut && pINChI_Taut->nNumberOfAtoms > 0 && + is->ord_number != is2->ord_number && + nTrans_n[is2->ord_number] ) { + /* nTrans_n[is2->ord_number] is new ordering number of + the non-taut representation of the tautomeric component + that has new ord number i+1 and orig ordering number is2->ord_number. + Old numbers start from 0, new start from 1 + */ + + /* n = nTrans_s[t]: taut component #t is in position #n of the non-taut representation */ + nTrans_s[i+1] = nTrans_n[is2->ord_number]; + } + } + *pTrans_n = nTrans_n; + *pTrans_s = nTrans_s; + ret = 1; + } else { + if ( nTrans_n ) { + inchi_free( nTrans_n ); + ret = -1; + } + if ( nTrans_s ) { + inchi_free( nTrans_s ); + ret = -1; + } + } + return ret; +} + + +/* + Output tautomeric transposition substring of AuxInfo +*/ +int str_AuxTautTrans( CANON_GLOBALS *pCG, + AT_NUMB *nTrans_n, + AT_NUMB *nTrans_s, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int TAUT_MODE, + int num_components) +{ + int i, k, len, j, nUsedLength0; + + nUsedLength0 = strbuf->nUsedLength; + + if ( nTrans_n && nTrans_s ) { + /* print the transposition, cycle after cycle */ + for ( i = 1; i <= num_components; i ++ ) { + if ( nTrans_s[i] ) { + /* get one cycle of the transposition */ + for ( j = i, len = 0; (k = nTrans_s[j]); j = k, len ++ ) + { + nTrans_n[len] = j; /* save the transposition */ + nTrans_s[j] = 0; /* clear used element to avoid repetitions */ + } + /* print one cycle of the transposition */ + MakeDelim( "(", strbuf, bOverflow); + MakeCtString( pCG, nTrans_n, len, 0, NULL, 0, + strbuf, TAUT_MODE, bOverflow); + MakeDelim( ")", strbuf, bOverflow); + } + } + } + if ( nTrans_n ) + inchi_free( nTrans_n ); + if ( nTrans_s ) + inchi_free( nTrans_s ); + + return ( strbuf->nUsedLength - nUsedLength0 ); +} + + +/* + Output isotopic TGroup equivalence substring of AuxInfo +*/ +int str_AuxIsoTgroupEqu( INCHI_SORT *pINChISort, + INCHI_IOSTREAM_STRING *strbuf, + int *bOverflow, + int bOutType, + int TAUT_MODE, + int num_components, + int bOmitRepetitions, + int bUseMulipliers) +{ + int i, ii, nUsedLength0; + INCHI_SORT *is, *is0; + INChI_Aux *pINChI_Aux, *pINChI_Aux_Prev; + int mult, eq2prev, eq2taut, eq2tautPrev, bNext; + const char *pPrevEquStr, *pCurrEquStr; + int multPrevEquStr; + pINChI_Aux = NULL; + pINChI_Aux_Prev = NULL; + mult = 0; + bNext = 0; + is = NULL; + is0 = pINChISort; + eq2taut = 0; /* equal to non-isotopic equivalence */ + eq2tautPrev = 1; /* pINChI_Aux_Prev (previous pINChI_Aux) does not exist */ + pPrevEquStr = NULL; /*, *pCurrEquStr;*/ + multPrevEquStr = 0; + nUsedLength0 = strbuf->nUsedLength; + + /* For each connected component... */ + for ( i = 0; i <= num_components; i ++ ) + { + /* 1st (taut) pass: bOutType=OUT_TN ; 2nd (non-taut pass) bOutType=OUT_NT */ + pINChI_Aux = (i < num_components && (is=is0+i, 0 <= (ii=GET_II(bOutType,is))))? is->pINChI_Aux[ii] : NULL; + /*================ compare iso non-taut equivalence info to non-iso taut ========*/ + eq2taut = 0; + if ( bOmitRepetitions && pINChI_Aux && pINChI_Aux->bIsIsotopic ) { + /************************************************** + * compare isotopic tautomeric equivalence to: + * a) non-isotopic tautomeric + */ + /* compare isotopic t-group equivalence to non-isotopic */ + eq2taut = Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_TG | EQL_EQU_ISO, pINChI_Aux, EQL_EQU_TG ); + /* equ taut-isotopic = tautomeric, same as for isotopic atom equivalence info*/ + eq2taut = eq2taut? (iiEQU | iitISO) : 0; + } + if ( eq2taut ) { + /* current isotopic t-group equivalence has been found to be same as non-isotopic */ + if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { + /* previous component exists */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( bHasEquString( pINChI_Aux_Prev->nConstitEquIsotopicTGroupNumbers, pINChI_Aux_Prev->nNumberOfTGroups) ) { + /* output previous component(s) equivalence since it was found to be non-trivial */ + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + MakeEquString( pINChI_Aux_Prev->nConstitEquIsotopicTGroupNumbers, pINChI_Aux_Prev->nNumberOfTGroups, 0, + strbuf, TAUT_MODE, bOverflow); + } else { + ; /* pINChI_Aux_Prev exists and does not have non-trivial t-group equivalence info */ + } + } + /* we have found pINChI_Aux->pINChI_Aux->nConstitEquIsotopicTGroupNumbers same as in pINChI_Aux->nConstitEquTGroupNumbers */ + pCurrEquStr = EquString(eq2taut); + if ( multPrevEquStr && pPrevEquStr ) { + if ( pCurrEquStr && !strcmp(pCurrEquStr, pPrevEquStr) ) { + multPrevEquStr ++; + } else { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + } else { + pPrevEquStr = pCurrEquStr; + multPrevEquStr = 1; + } + pINChI_Aux_Prev = NULL; /* pINChI_Aux_Prev has already been output */ + mult = 0; + eq2tautPrev = 1; + } else + if ( eq2tautPrev ) { + /* at this point pINChI_Aux_Prev does not exist; however, pINChI_Aux */ + /* might have been discovered and it may be different from non-isotopic */ + if ( multPrevEquStr && pPrevEquStr ) { + /* new EqStr is different; output it */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + MakeEqStr( pPrevEquStr, multPrevEquStr, strbuf, bOverflow); + pPrevEquStr = NULL; + multPrevEquStr = 0; + } + eq2tautPrev = 0; + pINChI_Aux_Prev = pINChI_Aux; + mult = 0; + } else { + /* check whether pINChI_Aux and pINChI_Aux_Prev have identical non-trivial isotopic t-group equivalence info */ + eq2prev = bUseMulipliers && Eql_INChI_Aux_Equ( pINChI_Aux, EQL_EQU_TG | EQL_EQU_ISO, pINChI_Aux_Prev, EQL_EQU_TG | EQL_EQU_ISO ); + if ( eq2prev ) { + /* eq. info is same and non-trivial */ + mult ++; /* mult = (number of non-empty equal items)-1 */ + continue; + } else { + /* pINChI_Aux eq. info is either different or trivial. Output pINChI_Aux_Prev anyway */ + if ( bNext ++ ) { + MakeDelim( sCompDelim, strbuf, bOverflow); + } + if ( pINChI_Aux_Prev && pINChI_Aux_Prev->nNumberOfAtoms ) { + if ( bHasEquString( pINChI_Aux_Prev->nConstitEquIsotopicTGroupNumbers, pINChI_Aux_Prev->nNumberOfTGroups) ) { + /* pINChI_Aux_Prev exists and has equivalence info */ + MakeMult( mult+1, "*", strbuf, 0, bOverflow); + MakeEquString( pINChI_Aux_Prev->nConstitEquIsotopicTGroupNumbers, pINChI_Aux_Prev->nNumberOfTGroups, 0, + strbuf, TAUT_MODE, bOverflow); + } else { + ; /* pINChI_Aux_Prev exists and has only trivial equivalence info */ + } + } +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + else { + int stop = 1; /* */ + } +#endif + } + pINChI_Aux_Prev = pINChI_Aux; + mult = 0; /* we do not know whether the item is empty */ + } + } + + return ( strbuf->nUsedLength - nUsedLength0 ); +} diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichiqueu.c b/INCHI-1-SRC/INCHI_BASE/src/ichiqueu.c similarity index 74% rename from INCHI-1-SRC/INCHI_API/inchi_dll/ichiqueu.c rename to INCHI-1-SRC/INCHI_BASE/src/ichiqueu.c index 362bd84..720454f 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichiqueu.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichiqueu.c @@ -1,1436 +1,1589 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include - -/*^^^ */ -#include "mode.h" - -#include "inpdef.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichi_bns.h" -/*^^^ */ - -/*******************************************************************/ - -#if ( FIND_RING_SYSTEMS == 1 ) /* { */ - -/* local prototypes */ -int are_alt_bonds( U_CHAR *bonds, int len ); -int AddBondsPos( inp_ATOM *atom, T_BONDPOS *BondPosTmp, int nNumBondPosTmp, T_BONDPOS *BondPos, int nMaxNumBondPos, int nNumBondPos ); -int AddEndPoints( T_ENDPOINT *EndPointTmp, int nNumNewEndPoint, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, int nNumEndPoint); - - - - - -/****************************************** - * - * Tautomerism in 5- and 6-member rings - * - ******************************************/ - -const int NONE = (AT_RANK)~0; - - -/* - 1,5 Tautomerism in 6-member alt ring: - - /=\ /==\ - HN C=O <-> N C-OH - \=/ \\-// - - - 1,2 Tautomerism in 5-member ring: - - - HN--X N==X - | \\ | \ - | Z <-> | Z - | / | // - N==Y HN--Y - - - 1,4 tautomerism in 7-member ring - - /C==D //C-D - O=B \ HO-B \\ - | E <-> | E - HO-A // O=A / - \\G-F \\G-F - - - 1,4 tautomerism in 5-member ring - - - O=B--C O-B==C - | \\ | \ - | D <-> | D - | / | // - HO-A==E HO=A--E - -*/ -typedef int CHECK_DFS_RING( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int nStartAtomNeighbor, - int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ); - -typedef int CHECK_CENTERPOINT ( inp_ATOM *atom, int iat ); - -CHECK_DFS_RING Check7MembTautRing; -CHECK_DFS_RING Check6MembTautRing; -CHECK_DFS_RING Check5MembTautRing; - -#if ( TAUT_15_NON_RING == 1 ) /* post v.1 feature */ -/* DFS simple alt path for 1,5 tautomerism, post v.1 feature */ -typedef int CHECK_DFS_PATH( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int jNxtNeigh, int nStartAtomNeighbor, - int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ); - -typedef int CHECK_DFS_CENTERPOINT( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int jNxtNeigh, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ); - - -CHECK_DFS_PATH Check15TautPath; -CHECK_DFS_CENTERPOINT Check15TautPathCenterpoint; - -int DFS_FindTautAltPath( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, - int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, - int nCycleLen, - AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, - CHECK_DFS_PATH *CheckDfsPath, CHECK_DFS_CENTERPOINT *CheckCenterPoint, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ); - -#define BOND_WRONG 64 -#define IS_ALT_OR_DBLBOND(X) (((X) == BOND_SINGLE || (X) == BOND_DOUBLE)? (X) : \ - ((X) == BOND_ALTERN || (X) == BOND_TAUTOM || (X) == BOND_ALT12NS)? BOND_ALTERN : \ - BOND_WRONG); - -#endif /* TAUT_15_NON_RING */ - -int DFS_FindTautInARing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, - int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, - int nCycleLen, - AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, - CHECK_DFS_RING *CheckDfsRing, CHECK_CENTERPOINT *CheckCenterPoint, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, int num_atoms ); - -#if ( REPLACE_ALT_WITH_TAUT == 1 ) -#define REPLACE_THE_BOND(X) ( (X) == BOND_SINGLE || (X) == BOND_DOUBLE || (X) == BOND_ALTERN || (X) == BOND_ALT12NS ) -#else -#define REPLACE_THE_BOND(X) ( (X) == BOND_SINGLE || (X) == BOND_DOUBLE ) -#endif - - -int bIsCenterPointStrict( inp_ATOM *atom, int iat ) -{ - if ( atom[iat].valence == atom[iat].chem_bonds_valence ) { - int endpoint_valence = get_endpoint_valence(atom[iat].el_number); - if ( endpoint_valence && (endpoint_valence > atom[iat].valence && /* added a check for negative charge or H 3-31-03 */ - (atom[iat].num_H || atom[iat].charge == -1) || - !atom[iat].charge && atom[iat].c_point) ) { - return 1; /* may appear to be tautomeric or chargable - (this increases chem_bonds_valence), should be explored */ - } - return 0; - } - if (atom[iat].valence+1 == atom[iat].chem_bonds_valence && - is_centerpoint_elem_strict( atom[iat].el_number ) ) { - return 1; - } - return 0; -} -/********************************************************************************/ -int nGet14TautIn7MembAltRing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, - int nStartAtomNeighborEndpoint, int nStartAtomNeighborNeighborEndpoint, - AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, int nMaxLenDfsPath, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ) -{ - int nRet; - - *pnNumEndPoint = 0; - *pnNumBondPos = 0; - - if ( nMaxLenDfsPath <= 7 ) { - return -1; /* path is too short */ - } - - nRet = DFS_FindTautInARing( atom, nStartAtom, nStartAtomNeighbor, - nStartAtomNeighborEndpoint, nStartAtomNeighborNeighborEndpoint, 7, - nDfsPathPos, DfsPath, - Check7MembTautRing, bIsCenterPointStrict, - EndPoint, nMaxNumEndPoint, - BondPos, nMaxNumBondPos, - pnNumEndPoint, pnNumBondPos, - pBNS, pBD, num_atoms - ); - - - return nRet; -} -/********************************************************************************/ -int nGet14TautIn5MembAltRing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, - int nStartAtomNeighborEndpoint, int nStartAtomNeighborNeighborEndpoint, - AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, int nMaxLenDfsPath, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ) -{ - int nRet; - - *pnNumEndPoint = 0; - *pnNumBondPos = 0; - - if ( nMaxLenDfsPath <= 5 ) { - return -1; /* path is too short */ - } - - nRet = DFS_FindTautInARing( atom, nStartAtom, nStartAtomNeighbor, - nStartAtomNeighborEndpoint, nStartAtomNeighborNeighborEndpoint, 5, - nDfsPathPos, DfsPath, - Check7MembTautRing, bIsCenterPointStrict, - EndPoint, nMaxNumEndPoint, - BondPos, nMaxNumBondPos, - pnNumEndPoint, pnNumBondPos, - pBNS, pBD, num_atoms - ); - - - return nRet; -} - -/********************************************************************************/ -int nGet12TautIn5MembAltRing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, - AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, int nMaxLenDfsPath, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ) -{ - int nRet; - - *pnNumEndPoint = 0; - *pnNumBondPos = 0; - - if ( nMaxLenDfsPath <= 5 ) { - return -1; /* path is too short */ - } - - nRet = DFS_FindTautInARing( atom, nStartAtom, nStartAtomNeighbor, -1, -1, 5, - nDfsPathPos, DfsPath, - Check5MembTautRing, bIsCenterPointStrict, - EndPoint, nMaxNumEndPoint, - BondPos, nMaxNumBondPos, - pnNumEndPoint, pnNumBondPos, - pBNS, pBD, num_atoms - ); - return nRet; -} - -/********************************************************************************/ -int nGet15TautIn6MembAltRing( inp_ATOM *atom, int nStartAtom, AT_RANK *nDfsPathPos, - DFS_PATH *DfsPath, int nMaxLenDfsPath, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ) -{ - int nRet; - - *pnNumEndPoint = 0; - *pnNumBondPos = 0; - - if ( nMaxLenDfsPath <= 7 ) { - return -1; /* path is too short */ - } - - nRet = DFS_FindTautInARing( atom, nStartAtom, -1/*nStartAtomNeighbor*/, -1/*nStartAtomNeighbor2*/, - -1/*nStartAtomNeighborNeighbor*/, 6 /* nCycleLen*/, - nDfsPathPos, DfsPath, - Check6MembTautRing, bIsCenterPointStrict, - EndPoint, nMaxNumEndPoint, - BondPos, nMaxNumBondPos, - pnNumEndPoint, pnNumBondPos, - pBNS, pBD, num_atoms - ); - return nRet; -} -#if ( TAUT_15_NON_RING == 1 ) /***** post v.1 feature *****/ -/********************************************************************************/ -int nGet15TautInAltPath( inp_ATOM *atom, int nStartAtom, AT_RANK *nDfsPathPos, - DFS_PATH *DfsPath, int nMaxLenDfsPath, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ) -{ - int nRet; - - *pnNumEndPoint = 0; - *pnNumBondPos = 0; - - if ( nMaxLenDfsPath <= 7 ) { - return -1; /* path is too short */ - } - - nRet = DFS_FindTautAltPath( atom, nStartAtom, -1/*nStartAtomNeighbor*/, -1/*nStartAtomNeighbor2*/, - -1/*nStartAtomNeighborNeighbor*/, 4 /* nCycleLen*/, - nDfsPathPos, DfsPath, - Check15TautPath, Check15TautPathCenterpoint, - EndPoint, nMaxNumEndPoint, - BondPos, nMaxNumBondPos, - pnNumEndPoint, pnNumBondPos, - pBNS, pBD, num_atoms - ); - return nRet; -} -#endif -/********************************************************************************/ -/* DFS version */ -#define MAX_DFS_DEPTH 16 - -/********************************************************************************/ -int DFS_FindTautInARing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, - int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, - int nCycleLen, - AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, - CHECK_DFS_RING *CheckDfsRing, CHECK_CENTERPOINT *CheckCenterPoint, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ) -{ - /* Depth First Search */ - /* Ignore all atoms not belonging to the current ring system (=biconnected component) */ - AT_RANK nMinLenDfsPath; - int j, cur_at, nxt_at, prv_at; - int nLenDfsPath, nNumFound, ret; - AT_RANK nRingSystem; - int nDoNotTouchAtom1 = -1, nDoNotTouchAtom2 = -1; - - nLenDfsPath=0; - nNumFound=0; - - nCycleLen --; - - DfsPath[nLenDfsPath].at_no = cur_at = nStartAtom; - DfsPath[nLenDfsPath].bond_type = 0; - DfsPath[nLenDfsPath].bond_pos = -1; - nDfsPathPos[cur_at] = nLenDfsPath+1; /* mark */ - nRingSystem = atom[nStartAtom].nRingSystem; - nMinLenDfsPath = 0; - if ( nStartAtomNeighbor2 >= 0 ) { - nDoNotTouchAtom1 = (int)atom[cur_at].neighbor[nStartAtomNeighbor2]; - } - - - /* add the first neighbor to the 2nd tree position if required */ - if ( nStartAtomNeighbor >= 0 ) { - j = nStartAtomNeighbor; - prv_at = cur_at; - cur_at = atom[prv_at].neighbor[j]; - DfsPath[nLenDfsPath].bond_type = (atom[prv_at].bond_type[j] & ~BOND_MARK_ALL); -#if ( FIX_BOND23_IN_TAUT == 1 ) - DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER(pBNS,prv_at,j,DfsPath[nLenDfsPath].bond_type); -#endif - DfsPath[nLenDfsPath].bond_pos = j; - - nLenDfsPath ++; - - DfsPath[nLenDfsPath].at_no = cur_at; - DfsPath[nLenDfsPath].bond_type = 0; - DfsPath[nLenDfsPath].bond_pos = -1; - nDfsPathPos[cur_at] = nLenDfsPath+1; - nMinLenDfsPath ++; - if ( nStartAtomNeighborNeighbor >= 0 ) { - nDoNotTouchAtom2 = (int)atom[cur_at].neighbor[nStartAtomNeighborNeighbor]; - } - } - - /* MAIN DFS CYCLE: may find one and the same t-group 2 times; saves only one instance */ - /* traverse *all* paths starting at atom[nStartAtom]; max. path length = (nCycleLen+1) */ - while ( nLenDfsPath >= nMinLenDfsPath ) { - j = ++DfsPath[nLenDfsPath].bond_pos; - if ( j < atom[cur_at=(int)DfsPath[nLenDfsPath].at_no].valence ) { - DfsPath[nLenDfsPath].bond_type = (atom[cur_at].bond_type[j] & ~BOND_MARK_ALL); -#if ( FIX_BOND23_IN_TAUT == 1 ) - DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER(pBNS,cur_at,j,DfsPath[nLenDfsPath].bond_type); -#endif - nxt_at = (int)atom[cur_at].neighbor[j]; - if ( nxt_at == nDoNotTouchAtom1 || - nxt_at == nDoNotTouchAtom2 ) { - ; /* ignore */ - } else - if ( nDfsPathPos[nxt_at] ) { - /* found a ring closure or a step backwards */ - if ( 1 == nDfsPathPos[nxt_at] && nLenDfsPath == nCycleLen ) { - /* we have found the cycle; check it */ - ret = (*CheckDfsRing)( atom, DfsPath, nLenDfsPath, nStartAtomNeighbor, - nStartAtomNeighbor2, nStartAtomNeighborNeighbor, - EndPoint, nMaxNumEndPoint, BondPos, nMaxNumBondPos, - pnNumEndPoint, pnNumBondPos, - pBNS, pBD, num_atoms - ); - if ( ret < 0 ) { - nNumFound = ret; - goto clear_path; - } - nNumFound += ret; - - } - } else - if ( !(*CheckCenterPoint)( atom, nxt_at ) ) { - ; /* cannot advance to a non-centerpoint; ignore */ - } else - if ( nLenDfsPath < nCycleLen ) { - /* advance */ - nLenDfsPath ++; - cur_at = nxt_at; - DfsPath[nLenDfsPath].at_no = cur_at; - DfsPath[nLenDfsPath].bond_type = 0; - DfsPath[nLenDfsPath].bond_pos = -1; - nDfsPathPos[cur_at] = nLenDfsPath+1; /* mark */ - } - } else { - /* retract */ - nDfsPathPos[(int)DfsPath[nLenDfsPath].at_no] = 0; - nLenDfsPath --; - } - } -clear_path: - while ( 0 <= nLenDfsPath ) { - nDfsPathPos[(int)DfsPath[nLenDfsPath].at_no] = 0; - nLenDfsPath --; - } - return nNumFound; -} -#if ( TAUT_15_NON_RING == 1 ) /***** post v.1 feature *****/ -/********************************************************************************/ -int DFS_FindTautAltPath( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, - int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, - int nCycleLen, - AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, - CHECK_DFS_PATH *CheckDfsPath, CHECK_DFS_CENTERPOINT *CheckCenterPointPath, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ) -{ - /* Naive Depth First Search: same atom may be approached along different alt paths */ - /* Ignore all atoms not belonging to the current ring system (=biconnected component) */ - AT_RANK nMinLenDfsPath; - int j, cur_at, nxt_at, prv_at; - int nLenDfsPath, nNumFound, ret; - AT_RANK nRingSystem; - int nDoNotTouchAtom1 = -1, nDoNotTouchAtom2 = -1; - - nLenDfsPath=0; - nNumFound=0; - - nCycleLen --; /* indef of the last atom in the alt path, statring from 0 */ - - DfsPath[nLenDfsPath].at_no = cur_at = nStartAtom; - DfsPath[nLenDfsPath].bond_type = 0; - DfsPath[nLenDfsPath].bond_pos = -1; /* initialize index of the bond to the next atom */ - nDfsPathPos[cur_at] = nLenDfsPath+1; /* mark with distance + 1 */ - nRingSystem = atom[nStartAtom].nRingSystem; - nMinLenDfsPath = 0; /* allow to restart from nStartAtom */ - if ( nStartAtomNeighbor2 >= 0 ) { - nDoNotTouchAtom1 = (int)atom[cur_at].neighbor[nStartAtomNeighbor2]; - } - - - /* add the first neighbor to the 2nd tree position if required */ - if ( nStartAtomNeighbor >= 0 ) { - j = nStartAtomNeighbor; - prv_at = cur_at; - cur_at = atom[prv_at].neighbor[j]; - DfsPath[nLenDfsPath].bond_type = (atom[prv_at].bond_type[j] & ~BOND_MARK_ALL); -#if ( FIX_BOND23_IN_TAUT == 1 ) - DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER(pBNS,prv_at,j,DfsPath[nLenDfsPath].bond_type); -#endif - DfsPath[nLenDfsPath].bond_pos = j; /* fix index of the bond to the next atom */ - - nLenDfsPath ++; - - DfsPath[nLenDfsPath].at_no = cur_at; - DfsPath[nLenDfsPath].bond_type = 0; - DfsPath[nLenDfsPath].bond_pos = -1; - nDfsPathPos[cur_at] = nLenDfsPath+1; /* mark with distance + 1 */ - nMinLenDfsPath ++; /* allow to restart from nStartAtom's neighbor */ - if ( nStartAtomNeighborNeighbor >= 0 ) { - nDoNotTouchAtom2 = (int)atom[cur_at].neighbor[nStartAtomNeighborNeighbor]; - } - } - - /* MAIN DFS CYCLE: may find one and the same t-group 2 times; saves only one instance */ - /* traverse *all* paths starting at atom[nStartAtom]; max. path length = (nCycleLen+1) */ - while ( nLenDfsPath >= nMinLenDfsPath ) { - j = ++DfsPath[nLenDfsPath].bond_pos; - if ( j < atom[cur_at=(int)DfsPath[nLenDfsPath].at_no].valence ) { - DfsPath[nLenDfsPath].bond_type = (atom[cur_at].bond_type[j] & ~BOND_MARK_ALL); -#if ( FIX_BOND23_IN_TAUT == 1 ) - DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER(pBNS,cur_at,j,DfsPath[nLenDfsPath].bond_type); -#endif - nxt_at = (int)atom[cur_at].neighbor[j]; - if ( nxt_at == nDoNotTouchAtom1 || /* forbidden */ - nxt_at == nDoNotTouchAtom2 || /* forbidden */ - nDfsPathPos[nxt_at] || /* ring closure */ - nLenDfsPath && nxt_at == (int)DfsPath[nLenDfsPath-1].at_no /* step backwards */ - ) { - ; /* ignore nxt_at */ - } else - if ( nLenDfsPath == nCycleLen && - /* 1,5 and at least one of the endpoints is not in a ring */ - (atom[nxt_at].nNumAtInRingSystem == 1 || atom[nStartAtom].nNumAtInRingSystem == 1) && - /* we have found the alt path of the requested length; check it */ - /* calling Check15TautPath() */ - (ret = (*CheckDfsPath)( atom, DfsPath, nLenDfsPath, j, nStartAtomNeighbor, - nStartAtomNeighbor2, nStartAtomNeighborNeighbor, - EndPoint, nMaxNumEndPoint, BondPos, nMaxNumBondPos, - pnNumEndPoint, pnNumBondPos, - pBNS, pBD, num_atoms ) ) ) { - if ( ret < 0 ) { - nNumFound = ret; - goto clear_path; /* program error */ - } - nNumFound += ret; /* success */ - } else /* calling Check15TautPathCenterpoint() */ - if ( !(*CheckCenterPointPath)( atom, DfsPath, nLenDfsPath, j, - pBNS, pBD, num_atoms ) ) { - ; /* cannot advance to a non-centerpoint; ignore */ - } else - if ( nLenDfsPath < nCycleLen ) { - /* advance */ - nLenDfsPath ++; - cur_at = nxt_at; - DfsPath[nLenDfsPath].at_no = cur_at; - DfsPath[nLenDfsPath].bond_type = 0; - DfsPath[nLenDfsPath].bond_pos = -1; - nDfsPathPos[cur_at] = nLenDfsPath+1; /* mark */ - } - } else { - /* retract */ - nDfsPathPos[(int)DfsPath[nLenDfsPath].at_no] = 0; - nLenDfsPath --; - } - } -clear_path: - while ( 0 <= nLenDfsPath ) { - nDfsPathPos[(int)DfsPath[nLenDfsPath].at_no] = 0; - nLenDfsPath --; - } - return nNumFound; -} -#endif /* TAUT_15_NON_RING */ -/******************************************* - * check if bonds are alternating */ -int are_alt_bonds( U_CHAR *bonds, int len ) -{ - U_CHAR next_bond; - int i, bAnyBond, bTautBondPresent=BOND_ALTERN; - if ( len < 2 || bonds[0] == BOND_TRIPLE || bonds[0] == BOND_ALT_13 ) { - return 0; - } - next_bond = bonds[0]==BOND_SINGLE? BOND_DOUBLE : bonds[0]==BOND_DOUBLE? BOND_SINGLE : 0; - if ( bonds[0] == BOND_TAUTOM ) { - bTautBondPresent= BOND_TAUTOM; - next_bond = 0; - } else { - next_bond = bonds[0]==BOND_SINGLE? BOND_DOUBLE : bonds[0]==BOND_DOUBLE? BOND_SINGLE : 0; - } - - for ( i = 1; i < len; i ++ ) { - if ( bonds[i] == BOND_TAUTOM ) { - bTautBondPresent = BOND_TAUTOM; - bAnyBond = 1; - } else { - bAnyBond = (bonds[i] == BOND_ALTERN || bonds[i] == BOND_ALT12NS); - } - if ( next_bond ) { - if ( bonds[i] == next_bond || bAnyBond ) { - next_bond = (next_bond == BOND_SINGLE)? BOND_DOUBLE : BOND_SINGLE; - continue; - } - return 0; - } else - if ( bonds[i] == BOND_SINGLE ) { - next_bond = BOND_DOUBLE; - continue; - } else - if ( bonds[i] == BOND_DOUBLE ) { - next_bond = BOND_SINGLE; - continue; - } else - if ( !bAnyBond ) { - return 0; - } - } - return !next_bond? bTautBondPresent : - (next_bond == BOND_SINGLE)? BOND_DOUBLE : BOND_SINGLE; /* bond to the end atom */ -} - -/********************************************************************************/ -int AddBondsPos( inp_ATOM *atom, T_BONDPOS *BondPosTmp, int nNumBondPosTmp, T_BONDPOS *BondPos, - int nMaxNumBondPos, int nNumBondPos ) -{ - int i, j, k, cur_at, nxt_at; - /* add opposite direction bonds to BondPosTmp */ - for ( j = 0; j < nNumBondPosTmp; j += 2 ) { - cur_at = BondPosTmp[j].nAtomNumber; - nxt_at = atom[cur_at].neighbor[(int)BondPosTmp[j].neighbor_index]; - for ( k = 0; k < atom[nxt_at].valence; k ++ ) { - if ( cur_at == atom[nxt_at].neighbor[k] ) { - BondPosTmp[j+1].nAtomNumber = nxt_at; - BondPosTmp[j+1].neighbor_index = k; - break; - } - } - } - /* add new tautomeric bonds */ - for ( j = 0; j < nNumBondPosTmp; j += 2 ) { - for ( i = 0; i < nNumBondPos; i ++ ) { - if ( BondPos[i].nAtomNumber == BondPosTmp[j].nAtomNumber && - BondPos[i].neighbor_index == BondPosTmp[j].neighbor_index || - BondPos[i].nAtomNumber == BondPosTmp[j+1].nAtomNumber && - BondPos[i].neighbor_index == BondPosTmp[j+1].neighbor_index ) { - break; /* bond has already been added */ - } - } - if ( i == nNumBondPos ) { - if ( i > nMaxNumBondPos ) { - return -1; /* overflow */ - } - BondPos[nNumBondPos ++] = BondPosTmp[j]; - } - } - return nNumBondPos; -} -/********************************************************************************/ -int AddEndPoints( T_ENDPOINT *EndPointTmp, int nNumNewEndPoint, T_ENDPOINT *EndPoint, - int nMaxNumEndPoint, int nNumEndPoint) -{ - int i, j; - /* add new endpoints */ - for ( j = 0; j < nNumNewEndPoint; j ++ ) { - for ( i = 0; i < nNumEndPoint; i ++ ) { - if ( EndPoint[i].nAtomNumber == EndPointTmp[j].nAtomNumber ) { - break; - } - } - if ( i == nNumEndPoint ) { - if ( i > nMaxNumEndPoint ) { - return -1; /* overflow */ - } - EndPoint[nNumEndPoint ++] = EndPointTmp[j]; - } - } - return nNumEndPoint; -} -/********************************************************************************/ -/* - - 1,4 tautomerism in 7-member ring - - /C==D //C-D A=DfsPath[0].at_no - O=B \ HO-B \\ B=DfsPath[1].at_no - | E <-> | E nStartAtomNeighbor2: from A to HO - HO-A // O=A / nStartAtomNeighborNeighbor: from B to O - \\G-F \\G-F - - - 1,4 tautomerism in 5-member ring - - - O=B--C O-B==C - | \\ | \ - | D <-> | D - | / | // - HO-A==E HO=A--E - -*/ -/********************************************************************************/ -int Check7MembTautRing( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int nStartAtomNeighbor, - int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ) -{ -#define PATH_LEN 8 - - int i, j, k, /*m,*/ nNumEndPoint, nNumEndPointTmp, nNumBondPos, nNumBondPosTmp; - int endpoint, /*nMobile, nMobile1, nMobile2,*/ o1_at, o2_at; - int ret; - U_CHAR path_bonds[PATH_LEN+1], bond_type; - T_ENDPOINT EndPointTmp[2]; - T_BONDPOS BondPosTmp[2*PATH_LEN]; - ENDPOINT_INFO eif1, eif2; - int nErr=0; - - - if ( nLenDfsPath + 2 > PATH_LEN ) { - return -1; /* too long path */ - } - if ( nLenDfsPath != 6 && nLenDfsPath != 4 ) { - return -1; /* wrong call */ - } - - - nNumBondPos = *pnNumBondPos; - nNumEndPoint = *pnNumEndPoint; - nNumBondPosTmp = 0; - nNumEndPointTmp = 0; - ret = 0; - - o1_at = atom[(int)DfsPath[1].at_no].neighbor[nStartAtomNeighborNeighbor]; - o2_at = atom[(int)DfsPath[0].at_no].neighbor[nStartAtomNeighbor2]; - /* - nMobile1 = (atom[o1_at].charge == -1) + atom[o1_at].num_H; - nMobile2 = (atom[o2_at].charge == -1) + atom[o2_at].num_H; - */ - if ( !nGetEndpointInfo( atom, o1_at, &eif1 ) || - !nGetEndpointInfo( atom, o2_at, &eif2 ) ) { - return 0; - } - - /* save endpoints */ - for ( j = 0; j < 2; j ++ ) { - endpoint = j? o2_at : o1_at; - if ( !atom[endpoint].endpoint ) { - AddAtom2num( EndPointTmp[nNumEndPointTmp].num, atom, endpoint, 2 ); /* fill out */ - AddAtom2DA( EndPointTmp[nNumEndPointTmp].num_DA, atom, endpoint, 2 ); - /* - nMobile = j? nMobile2 : nMobile1; - } else { - nMobile = 0; - } - if ( nMobile ) { - EndPointTmp[nNumEndPointTmp].num[1] = (atom[endpoint].charge == -1); - EndPointTmp[nNumEndPointTmp].num[0] = nMobile; - for ( m = 0; m < T_NUM_ISOTOPIC; m ++ ) { - EndPointTmp[nNumEndPointTmp].num[T_NUM_NO_ISOTOPIC+m] = atom[endpoint].num_iso_H[NUM_H_ISOTOPES-m-1]; - } - */ - } else { - memset( EndPointTmp + nNumEndPointTmp, 0, sizeof(EndPointTmp[0]) ); - } - EndPointTmp[nNumEndPointTmp].nAtomNumber = endpoint; - EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint; - EndPointTmp[nNumEndPointTmp].nEquNumber = 0; - nNumEndPointTmp ++; - } - - - /* extract bonds */ - k = (int)DfsPath[1].at_no; - bond_type = (atom[k].bond_type[nStartAtomNeighborNeighbor] & ~BOND_MARK_ALL); -#if ( FIX_BOND23_IN_TAUT == 1 ) - bond_type = ACTUAL_ORDER(pBNS,k,nStartAtomNeighborNeighbor,bond_type); -#endif - path_bonds[0] = bond_type; - if ( REPLACE_THE_BOND( bond_type ) ) { - BondPosTmp[nNumBondPosTmp].nAtomNumber = k; - BondPosTmp[nNumBondPosTmp].neighbor_index = nStartAtomNeighborNeighbor; - nNumBondPosTmp += 2; - } - for ( i = 1; i <= nLenDfsPath; i ++ ) { - bond_type = DfsPath[i].bond_type; - path_bonds[i] = bond_type; - if ( REPLACE_THE_BOND( bond_type ) ) { - BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[i].at_no; - BondPosTmp[nNumBondPosTmp].neighbor_index = DfsPath[i].bond_pos; - nNumBondPosTmp += 2; - } - } - bond_type = (atom[(int)DfsPath[0].at_no].bond_type[nStartAtomNeighbor2] & ~BOND_MARK_ALL); -#if ( FIX_BOND23_IN_TAUT == 1 ) - bond_type = ACTUAL_ORDER(pBNS,(int)DfsPath[0].at_no,nStartAtomNeighbor2,bond_type); -#endif - path_bonds[i++] = bond_type; - if ( REPLACE_THE_BOND( bond_type ) ) { - BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[0].at_no; - BondPosTmp[nNumBondPosTmp].neighbor_index = nStartAtomNeighbor2; - nNumBondPosTmp += 2; - } - - if ( !are_alt_bonds( path_bonds, i ) ) { - return 0; - } - - /* path_bonds is from at_n1 to at_n2 */ - if ( !(j=are_alt_bonds( path_bonds, i )) ) { - return 0; - } - /* j is a bond type of the last bond to o2_at, the first bond from o1_at is 2-j if j=1 or 2 */ - - /* single bond at o2_at: it should have a mobile atom, o1_at should not */ - if ( j == BOND_SINGLE && (!atom[o2_at].endpoint && !eif2.cDonor || !atom[o1_at].endpoint && !eif1.cAcceptor) || - /* double bond at o2_at: it should not have a mobile atom, o1_at should */ - j == BOND_DOUBLE && (!atom[o2_at].endpoint && !eif2.cAcceptor || !atom[o1_at].endpoint && !eif1.cDonor) ) { - return 0; /* bond pattern does not fit */ - } - - - nNumBondPos = AddBondsPos( atom, BondPosTmp, nNumBondPosTmp, BondPos, nMaxNumBondPos, nNumBondPos ); - nNumEndPoint = AddEndPoints( EndPointTmp, nNumEndPointTmp, EndPoint, nMaxNumEndPoint, nNumEndPoint); - - if ( nNumBondPos >= 0 && nNumEndPoint >= 0 ) { - if (ret = (nNumBondPos > *pnNumBondPos) || (nNumEndPoint > *pnNumEndPoint)) { - *pnNumBondPos = nNumBondPos ; - *pnNumEndPoint = nNumEndPoint ; - } - } - - if ( ret ) { - /* finally check whether the bonds allow moving the hydrogens */ - if ( (atom[o1_at].endpoint != atom[o2_at].endpoint || !atom[o1_at].endpoint) ) { - nErr = bExistsAnyAltPath( pBNS, pBD, atom, num_atoms, o1_at, o2_at, ALT_PATH_MODE_TAUTOM ); - if ( nErr <= 0 ) - return nErr; - } - } - - return ret; - - -#undef PATH_LEN -} - -/********************************************************************************/ -/* - 1,5 Tautomerism in 6-member alt ring: - - /=\ /==\ N = DfsPath[0].at_no - HN C=O <-> N C-OH C = DfsPath[3].at_no - \=/ \\-// - -*/ -/********************************************************************************/ -/* check if a tautomeric 6-member ring has been found */ -int Check6MembTautRing( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int nStartAtomNeighbor, - int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ) -{ -#define PATH_LEN 4 - int i, j, k, /*m,*/ nNumBondPos, nNumEndPoint; - int nNumEndPointTmp, nNumBondPosTmp, o_at, ret; - /* int num_taut_endpoints, num_H; */ - int middle_pos; - int nMobile, endpoint, endpoint_valence, chem_bonds_valence; - int nMobile1, endpoint_valence1; /* o_at */ - int nMobile2, endpoint_valence2; /* n_at */ - int nxt_at; - int n_at; - U_CHAR path_bonds[2][PATH_LEN+1], bond_type; - T_ENDPOINT EndPointTmp[2]; - T_BONDPOS BondPosTmp[4*PATH_LEN]; - ENDPOINT_INFO eif1, eif2; - - if ( nStartAtomNeighbor >= 0 || nStartAtomNeighbor2 >= 0 || nStartAtomNeighborNeighbor >= 0 ) - return -1; /* wrong call */ - - if ( nLenDfsPath != 5 ) - return -1; /* wrong call */ - - nNumBondPos = *pnNumBondPos; - nNumEndPoint = *pnNumEndPoint; - nNumBondPosTmp = 0; - nNumEndPointTmp = 0; - ret = 0; - - n_at = (int)DfsPath[0].at_no; /* -N= or -NH- atom */ - nxt_at = DfsPath[middle_pos = (nLenDfsPath+1)/2].at_no; /* must have tautomeric neighbor -OH or =O or -NH2 or =NH */ - - if ( atom[nxt_at].valence != 3 -#if ( TAUT_RINGS_ATTACH_CHAIN == 1 ) - || !atom[nxt_at].bCutVertex -#endif - ) { - return 0; - } - - for ( i = 0; i < atom[nxt_at].valence; i ++ ) { - o_at = atom[nxt_at].neighbor[i]; - if ( o_at != DfsPath[middle_pos-1].at_no && o_at != DfsPath[middle_pos+1].at_no ) { - break; /* >=O or />-OH has been found */ - } - } - if ( i == atom[nxt_at].valence ) { - return 0; /* no neighboring atom >=O or />-OH */ - } - bond_type = (atom[nxt_at].bond_type[i] & ~BOND_MARK_ALL); -#if ( FIX_BOND23_IN_TAUT == 1 ) - bond_type = ACTUAL_ORDER(pBNS,nxt_at,i,bond_type); -#endif - if ( bond_type != BOND_SINGLE && - bond_type != BOND_DOUBLE && - bond_type != BOND_TAUTOM && - bond_type != BOND_ALT12NS && - bond_type != BOND_ALTERN ) { - return 0; - } - - /* check whether the two atoms already belong to one tautomeric group */ -#if ( TAUT_IGNORE_EQL_ENDPOINTS == 1 ) - if ( atom[n_at].endpoint && atom[n_at].endpoint == atom[o_at].endpoint ) { - return 0; - } -#endif - /* check =O valence; must be 2 for O, S, Se or 3 for N */ - if ( !(endpoint_valence1=nGetEndpointInfo( atom, o_at, &eif1 )) ) - { - return 0; /* n_at has been checked in MarkTautomerGroups(...) */ - } -/* - if ( 2 != endpoint_valence1 ) - return 0; // accept only O, S, Se -*/ - /* check hydrogens/endpoints */ - nMobile1 = atom[o_at].num_H + (atom[o_at].charge==-1); - if ( bond_type == BOND_SINGLE && !eif1.cDonor && !atom[o_at].endpoint ) - return 0; - /* not needed since nGetEndpointInfo returned non-zero - if ( nMobile1 + atom[o_at].chem_bonds_valence != endpoint_valence1 ) - return 0; - */ - - if ( !(endpoint_valence2=nGetEndpointInfo( atom, n_at, &eif2 ) ) ) { - return 0; /* should not happen here */ - } - nMobile2 = atom[n_at].num_H + (atom[n_at].charge==-1); - - nMobile = 0; - - /* can mobile group move from o_at to n_at? */ - nMobile += (atom[o_at].endpoint || eif1.cDonor) && /* from o_at */ - bond_type != BOND_DOUBLE && - ( atom[n_at].endpoint || /* to n_at */ - eif2.cNeutralBondsValence > atom[n_at].valence ); - /* can mobile group move from n_at to o_at? */ - nMobile += (atom[n_at].endpoint || eif2.cDonor) && /* from n_at */ - (atom[o_at].endpoint || /* to o_at */ - eif1.cNeutralBondsValence > atom[o_at].valence ) && - bond_type != BOND_SINGLE; - - - if ( !nMobile ) - return 0; - /* - num_H = atom[n_at].num_H + atom[o_at].num_H; - num_taut_endpoints = (0!=atom[n_at].endpoint) + (0!=atom[o_at].endpoint); // if O, N already are endpoints - if ( num_H != 1 && num_taut_endpoints != 2 && !(num_H==2 && num_taut_endpoints >= 1) ) { - return 0; - } - */ - /* extract -OH bond */ - nNumBondPosTmp = 0; - - path_bonds[0][0] = path_bonds[1][0] = bond_type; - if ( REPLACE_THE_BOND( bond_type ) ) { - BondPosTmp[nNumBondPosTmp].nAtomNumber = nxt_at; /* accumulate bonds to be */ - BondPosTmp[nNumBondPosTmp].neighbor_index = i; /* marked as tautomeric */ - nNumBondPosTmp += 2; /* leave room for the same bond in the opposite direction */ - } - - /* extract other bonds */ - /* path_bonds[] contents: - - - O OH OH - || | | - / \ // \ / \\ - || || <--> | || <--> || | - \ / \\ / \ // - NH N N - - path[0]: O=NH-=- OH-N... OH.N... - path[1] O=NH-=- OH-N... OH.N... - bonds are all bonds all bonds - single and are either are either - double alt or taut alt or taut - */ - for ( j = 0; j < middle_pos; j ++ ) { - for ( i = 0; i < 2; i ++ ) { - /* k = i? j : middle_pos-1-j; */ - k = i? middle_pos+j : middle_pos-1-j; - /* i=0: from O neighbor i=0: down to N, i=1: up to N */ - bond_type = DfsPath[k].bond_type; - - path_bonds[i][j+1] = bond_type; - if ( REPLACE_THE_BOND( bond_type ) ) { - BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[k].at_no; /* accumulate bonds to be */ - BondPosTmp[nNumBondPosTmp].neighbor_index = DfsPath[k].bond_pos; /* marked as tautomeric */ - nNumBondPosTmp += 2; /* leave room for the same bond in the opposite direction */ - } - } - } - if ( !are_alt_bonds( path_bonds[0], middle_pos+1 ) || !are_alt_bonds( path_bonds[1], middle_pos+1 ) ) { - return 0; - } - - /* finally check whether the bonds allow moving the hydrogens */ - if ( (atom[o_at].endpoint != atom[n_at].endpoint || !atom[o_at].endpoint) ) { - int nErr; - nErr = bExistsAnyAltPath( pBNS, pBD, atom, num_atoms, n_at, o_at, ALT_PATH_MODE_TAUTOM ); - if ( nErr <= 0 ) - return nErr; - } - /* save endpoints */ - for ( j = 0; j < 2; j ++ ) { - endpoint = j? n_at : /* =N- 2 */ - o_at; /* -OH 1 */ - if ( !atom[endpoint].endpoint ) { /* not a known endpoint */ - endpoint_valence = j? endpoint_valence2 : endpoint_valence1; - chem_bonds_valence = j? eif2.cNeutralBondsValence : eif1.cNeutralBondsValence; - /* endpoint_valence = get_endpoint_valence( atom[endpoint].el_number ); */ - nMobile = j? nMobile2 : nMobile1; - /* nMobile = (atom[endpoint].charge == -1) + atom[endpoint].num_H; */ - /* if ( nMobile + atom[endpoint].chem_bonds_valence != endpoint_valence ) -- fixed 02-06-2003*/ - if ( nMobile + chem_bonds_valence != endpoint_valence ) - return 0; /* abnormal endpoint valence; ignore. */ - AddAtom2num( EndPointTmp[nNumEndPointTmp].num, atom, endpoint, 2 ); /* fill out */ - AddAtom2DA( EndPointTmp[nNumEndPointTmp].num_DA, atom, endpoint, 2 ); -/* - EndPointTmp[nNumEndPointTmp].num[1] = (atom[endpoint].charge == -1); - EndPointTmp[nNumEndPointTmp].num[0] = nMobile; - for ( m = 0; m < T_NUM_ISOTOPIC; m ++ ) { - EndPointTmp[nNumEndPointTmp].num[T_NUM_NO_ISOTOPIC+m] = atom[endpoint].num_iso_H[NUM_H_ISOTOPES-m-1]; - } -*/ - } else { /* already an endpoint */ /* **now it is wrong:** no mobile atom/charge at this endpoint */ - memset( EndPointTmp + nNumEndPointTmp, 0, sizeof(EndPointTmp[0]) ); - } - EndPointTmp[nNumEndPointTmp].nAtomNumber = endpoint; - EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint; - EndPointTmp[nNumEndPointTmp].nEquNumber = 0; - - nNumEndPointTmp ++; - } - /* add collected tautomeric bonds and endpoints to the input/output data */ - nNumBondPos = AddBondsPos( atom, BondPosTmp, nNumBondPosTmp, BondPos, nMaxNumBondPos, nNumBondPos ); - nNumEndPoint = AddEndPoints( EndPointTmp, nNumEndPointTmp, EndPoint, nMaxNumEndPoint, nNumEndPoint); - - if ( nNumBondPos >= 0 && nNumEndPoint >= 0 ) { - if (ret = (nNumBondPos > *pnNumBondPos) || (nNumEndPoint > *pnNumEndPoint)) { - *pnNumBondPos = nNumBondPos ; - *pnNumEndPoint = nNumEndPoint ; - } - } - return ret; - -#undef PATH_LEN -} -#if ( TAUT_15_NON_RING == 1 ) /* post v.1 feature */ -/******************************************************************************** -Check (1,5) taut alt path centerpoint (unfinished) [add path checking] -*********************************************************************************/ -int Check15TautPathCenterpoint( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int jNxtNeigh, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ) -{ - int nxt_at = atom[DfsPath[nLenDfsPath].at_no].neighbor[jNxtNeigh]; - /* atom[nxt_at].endpoint below allows for keto-enol -CH< or -CH2- endpoints */ - return atom[nxt_at].endpoint || bIsCenterPointStrict( atom, nxt_at ); -} - -/********************************************************************************/ -/* - 1,5 Tautomerism in general (unfinished) [just a copy from 6-memb case] - - AH--B==C--D==E C may be carbon exhibiting keto-enol tautomerism - 0 1 2 3 4 as well as A or E may be previously detected such a carbon - ^ nxt_at - | - +-- = nLenDfsPath - -*/ -/********************************************************************************/ -/* check if 1,5 tautomeric path has been found */ -int Check15TautPath( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int jNxtNeigh, int nStartAtomNeighbor, - int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ) -{ -#define PATH_LEN 4 - int i, j, k, /*m,*/ nNumBondPos, nNumEndPoint, cur_at, prv_at, at1, at2 /*, at3, step_at*/; - int nNumEndPointTmp, nNumBondPosTmp, ret; - /* int num_taut_endpoints, num_H; */ - int nMobile, endpoint, endpoint_valence, chem_bonds_valence; - int nMobile1, endpoint_valence1; /* start atom, at1 */ - int nMobile2, endpoint_valence2; /* end atom, at2 */ - /*int nMobile3, endpoint_valence3=-1;*/ /* middle atom, at3 */ - /*int nxt_at;*/ - int alt_bonds[2]; - U_CHAR /*path_bonds[2][PATH_LEN+1],*/ bond_type; - T_ENDPOINT EndPointTmp[2]; - T_BONDPOS BondPosTmp[4*PATH_LEN]; - ENDPOINT_INFO eif1, eif2/*, eif3*/; - - if ( nStartAtomNeighbor >= 0 || nStartAtomNeighbor2 >= 0 || nStartAtomNeighborNeighbor >= 0 ) - return -1; /* wrong call */ - - if ( nLenDfsPath != 3 ) - return -1; /* wrong call */ - - nNumBondPos = *pnNumBondPos; - nNumEndPoint = *pnNumEndPoint; - nNumBondPosTmp = 0; - nNumEndPointTmp = 0; - ret = 0; - -/*-------add the last atom, nLenDfsPath=4 --*/ - j = jNxtNeigh; - prv_at = DfsPath[nLenDfsPath].at_no; - cur_at = atom[prv_at].neighbor[j]; - DfsPath[nLenDfsPath].bond_type = (atom[prv_at].bond_type[j] & ~BOND_MARK_ALL); -#if ( FIX_BOND23_IN_TAUT == 1 ) - DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER(pBNS,prv_at,j,DfsPath[nLenDfsPath].bond_type); -#endif - DfsPath[nLenDfsPath].bond_pos = j; /* fix index of the bond to the next atom */ - - nLenDfsPath ++; - - DfsPath[nLenDfsPath].at_no = cur_at; - DfsPath[nLenDfsPath].bond_type = 0; - DfsPath[nLenDfsPath].bond_pos = -1; - /*nDfsPathPos[cur_at] = nLenDfsPath+1;*/ /* mark with distance + 1 */ -/*------------------------------------------*/ - at1 = (int)DfsPath[0].at_no; - at2 = (int)DfsPath[nLenDfsPath].at_no; - /*at3 = (int)DfsPath[2].at_no;*/ - if ( atom[at1].endpoint && atom[at1].endpoint == atom[at2].endpoint ) { - /* start & end already belong to the same taut group */ - goto exit_function; /* nothing to do */ - } - - /* check bond types along alt path */ - alt_bonds[0] = alt_bonds[1] = 0; - for( i = 0; i < nLenDfsPath; i ++ ) { - alt_bonds[i%2] |= IS_ALT_OR_DBLBOND(DfsPath[i].bond_type); - } - if ( (alt_bonds[0] & alt_bonds[1] & (BOND_SINGLE | BOND_DOUBLE)) || - (alt_bonds[0] & BOND_WRONG) || (alt_bonds[1] & BOND_WRONG ) ) { - goto exit_function; /* incompatible with alt path or wrong bonds */\ - } - /* check possibly tautomeric endpoints at the ends */ - endpoint_valence1 = nGetEndpointInfo( atom, at1, &eif1 ); - endpoint_valence2 = nGetEndpointInfo( atom, at2, &eif2 ); -#ifdef NEVER /* do not use C-endpoint of keto-enol tautomer to find 1,5 the taut path */ - if ( !endpoint_valence1 && !atom[at1].endpoint || - !endpoint_valence2 && !atom[at2].endpoint ) - goto exit_function; /* at least one of the end atoms cannot be an endpoint */ -#endif - if ( !endpoint_valence1 || !endpoint_valence2 ) - goto exit_function; /* require both endpoints be heteroatoms */ - /* check hydrogens/endpoints */ - nMobile1 = atom[at1].num_H + (atom[at1].charge==-1); - if ( !atom[at1].endpoint ) { - if ( (alt_bonds[0] & BOND_SINGLE) && !eif1.cDonor ) - goto exit_function; - if ( (alt_bonds[0] & BOND_DOUBLE) && !eif1.cAcceptor ) - goto exit_function; - } - nMobile2 = atom[at2].num_H + (atom[at2].charge==-1); - if ( !atom[at2].endpoint ) { - if ( (alt_bonds[1] & BOND_SINGLE) && !eif2.cDonor ) - goto exit_function; - if ( (alt_bonds[1] & BOND_DOUBLE) && !eif2.cAcceptor ) - goto exit_function; - } - - nMobile = 0; - - /* can mobile group move from at1=o_at to at2=n_at? */ - nMobile += (atom[at1].endpoint || eif1.cDonor) && /* from o_at */ - !(alt_bonds[0] & BOND_DOUBLE) && - ( atom[at2].endpoint || /* to n_at */ - eif2.cNeutralBondsValence > atom[at2].valence ); - /* can mobile group move from at2=n_at to at1=o_at? */ - nMobile += (atom[at2].endpoint || eif2.cDonor) && /* from n_at */ - !(alt_bonds[1] & BOND_DOUBLE) && - ( atom[at1].endpoint || /* to o_at */ - eif1.cNeutralBondsValence > atom[at1].valence ); - - - if ( !nMobile ) - goto exit_function; - - /* check whether the bonds allow moving the hydrogens between at1 and at2 */ - if ( (atom[at1].endpoint != atom[at2].endpoint || !atom[at1].endpoint) ) { - int nErr; - nErr = bExistsAnyAltPath( pBNS, pBD, atom, num_atoms, at1, at2, ALT_PATH_MODE_TAUTOM ); - if ( nErr <= 0 ) { - ret = nErr; - goto exit_function; - } - } - - /* save tautomeric bonds */ - nNumBondPosTmp = 0; - for ( k = 0; k < nLenDfsPath; k ++ ) { - bond_type = DfsPath[k].bond_type; - if ( REPLACE_THE_BOND( bond_type ) ) { - BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[k].at_no; /* accumulate bonds to be */ - BondPosTmp[nNumBondPosTmp].neighbor_index = DfsPath[k].bond_pos; /* marked as tautomeric */ - nNumBondPosTmp += 2; /* leave room for the same bond in opposite direction */ - } - } - /* save endpoints */ - for ( j = 0; j < 2; j ++ ) { - endpoint = j? at2 : at1; - if ( !atom[endpoint].endpoint ) { /* not a known endpoint */ - endpoint_valence = j? endpoint_valence2 : endpoint_valence1; - chem_bonds_valence = j? eif2.cNeutralBondsValence : eif1.cNeutralBondsValence; - /* endpoint_valence = get_endpoint_valence( atom[endpoint].el_number ); */ - nMobile = j? nMobile2 : nMobile1; - /* nMobile = (atom[endpoint].charge == -1) + atom[endpoint].num_H; */ - /* if ( nMobile + atom[endpoint].chem_bonds_valence != endpoint_valence ) -- fixed 02-06-2003*/ - if ( nMobile + chem_bonds_valence != endpoint_valence ) - goto exit_function; /* abnormal endpoint valence; ignore. */ - AddAtom2num( EndPointTmp[nNumEndPointTmp].num, atom, endpoint, 2 ); /* fill out */ - AddAtom2DA( EndPointTmp[nNumEndPointTmp].num_DA, atom, endpoint, 2 ); - } else { /* already an endpoint */ /* **now it is wrong:** no mobile atom/charge at this endpoint */ - memset( EndPointTmp + nNumEndPointTmp, 0, sizeof(EndPointTmp[0]) ); - } - EndPointTmp[nNumEndPointTmp].nAtomNumber = endpoint; - EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint; - EndPointTmp[nNumEndPointTmp].nEquNumber = 0; - - nNumEndPointTmp ++; - } - /* add collected tautomeric bonds and endpoints to the input/output data */ - nNumBondPos = AddBondsPos( atom, BondPosTmp, nNumBondPosTmp, BondPos, nMaxNumBondPos, nNumBondPos ); - nNumEndPoint = AddEndPoints( EndPointTmp, nNumEndPointTmp, EndPoint, nMaxNumEndPoint, nNumEndPoint); - - if ( nNumBondPos >= 0 && nNumEndPoint >= 0 ) { - if (ret = (nNumBondPos > *pnNumBondPos) || (nNumEndPoint > *pnNumEndPoint)) { - *pnNumBondPos = nNumBondPos ; - *pnNumEndPoint = nNumEndPoint ; - } - } - -exit_function: - /*nDfsPathPos[DfsPath[nLenDfsPath].at_no] = 0;*/ - - return ret; - -#undef PATH_LEN -} -#endif /* TAUT_15_NON_RING */ - -/********************************************************************************/ -/* - - 1,4 tautomerism in 5-member ring - - - O=N2-C O-N2=C N1 = DfsPath[0].at_no - | \\ | \ N2 = DfsPath[1].at_no - | D <-> | D - | / | // - HO-N1=E HO=N1-E - -*/ -/********************************************************************************/ -/* check if a tautomeric 5-member ring (pyrazole derivatives) has been found */ -int Check5MembTautRing( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int nStartAtomNeighbor, - int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ) -{ -#define PATH_LEN 4 - int i, j, /*m,*/ nMobile, nMobile1, nMobile2; - int num_taut_endpoints, nNumBondPos, nNumBondPosTmp, nNumEndPoint, nNumEndPointTmp, ret; - int endpoint; - int n1_at = (int)DfsPath[0].at_no; - int n2_at = (int)DfsPath[1].at_no; - U_CHAR path_bonds[PATH_LEN+1], bond_type; - T_ENDPOINT EndPointTmp[2]; - T_BONDPOS BondPosTmp[2*PATH_LEN]; - ENDPOINT_INFO eif1, eif2; - - /* the two root atoms (atom[n1_at] and atom[n2_at]) cannot belong */ - /* to one and the same tautomeric group: it has been verified in MarkTautomerGroups() */ - - /* check hydrogens/endpoints */ - if ( nLenDfsPath != 4 ) { - return 0; /* program error */ - } - if ( nStartAtomNeighbor2 >= 0 || nStartAtomNeighborNeighbor >= 0 ) - return 0; /* program error: wrong call */ - - nNumBondPos = *pnNumBondPos; - nNumEndPoint = *pnNumEndPoint; - nNumEndPointTmp = 0; - nNumBondPosTmp = 0; - ret = 0; - - if ( !nGetEndpointInfo( atom, n1_at, &eif1 ) || - !nGetEndpointInfo( atom, n2_at, &eif2 ) ) { - return 0; - } - - nMobile1 = atom[n1_at].num_H + (atom[n1_at].charge==-1); - nMobile2 = atom[n2_at].num_H + (atom[n2_at].charge==-1); - nMobile = nMobile1 + nMobile2; - num_taut_endpoints = (0!=atom[n1_at].endpoint) + (0!=atom[n2_at].endpoint); /* if both N atoms already are endpoints */ - /* - if ( !(nMobile == 1 || num_taut_endpoints == 2) && !(nMobile>1 && num_taut_endpoints >= 1) ) { - return 0; - } - */ - if ( num_taut_endpoints == 0 && nMobile != 1 ) { - return 0; - } - - /* finally check whether the bonds allow moving the hydrogens */ - if ( (atom[n1_at].endpoint != atom[n2_at].endpoint || !atom[n1_at].endpoint) ) { - int nErr; - nErr = bExistsAnyAltPath( pBNS, pBD, atom, num_atoms, n1_at, n2_at, ALT_PATH_MODE_TAUTOM ); - if ( nErr <= 0 ) - return nErr; - } - - /* save endpoints */ - for ( j = 0; j < 2; j ++ ) { - endpoint = j? n1_at : n2_at; - if ( !atom[endpoint].endpoint ) { /* not a known endpoint */ -/* - nMobile = (atom[endpoint].charge == -1) + atom[endpoint].num_H; - } else { - nMobile = 0; - } - if ( nMobile ) { -*/ - AddAtom2num( EndPointTmp[nNumEndPointTmp].num, atom, endpoint, 2 ); /* fill out */ - AddAtom2DA( EndPointTmp[nNumEndPointTmp].num_DA, atom, endpoint, 2 ); - /* - EndPointTmp[nNumEndPointTmp].num[1] = (atom[endpoint].charge == -1); - EndPointTmp[nNumEndPointTmp].num[0] = nMobile; - for ( m = 0; m < T_NUM_ISOTOPIC; m ++ ) { - EndPointTmp[nNumEndPointTmp].num[T_NUM_NO_ISOTOPIC+m] = atom[endpoint].num_iso_H[NUM_H_ISOTOPES-m-1]; - } - */ - } else { - memset( EndPointTmp + nNumEndPointTmp, 0, sizeof(EndPointTmp[0]) ); - } - EndPointTmp[nNumEndPointTmp].nAtomNumber = endpoint; - EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint; - EndPointTmp[nNumEndPointTmp].nEquNumber = 0; - - nNumEndPointTmp ++; - } - - /* extract bonds */ - nNumBondPosTmp = 0; - for ( i = 1; i <= nLenDfsPath; i ++ ) { - bond_type = DfsPath[i].bond_type; - path_bonds[i-1] = bond_type; - if ( REPLACE_THE_BOND( bond_type ) ) { - BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[i].at_no; - BondPosTmp[nNumBondPosTmp].neighbor_index = DfsPath[i].bond_pos; - nNumBondPosTmp += 2; - } - } - /* path_bonds is from at_n2 to at_n1 */ - if ( !(i=are_alt_bonds( path_bonds, nLenDfsPath )) ) { - return 0; - } - /* i is a bond type of the last bond to at_n1, the first bond from at_n2 is 2-i if i=1 or 2 */ - - /* single bond at n1_at: it should have a mobile atom, n2_at should not */ - if ( i == BOND_SINGLE && (!atom[n1_at].endpoint && !eif1.cDonor || !atom[n2_at].endpoint && !eif2.cAcceptor ) || - /* double bond at n1_at: it should not have a mobile atom, n2_at should */ - i == BOND_DOUBLE && (!atom[n1_at].endpoint && !eif1.cAcceptor || !atom[n2_at].endpoint && !eif2.cDonor) ) { - return 0; /* bond pattern does not fit */ - } - - nNumBondPos = AddBondsPos( atom, BondPosTmp, nNumBondPosTmp, BondPos, nMaxNumBondPos, nNumBondPos ); - nNumEndPoint = AddEndPoints( EndPointTmp, nNumEndPointTmp, EndPoint, nMaxNumEndPoint, nNumEndPoint); - - if ( nNumBondPos >= 0 && nNumEndPoint >= 0 ) { - if (ret = (nNumBondPos > *pnNumBondPos) || (nNumEndPoint > *pnNumEndPoint)) { - *pnNumBondPos = nNumBondPos ; - *pnNumEndPoint = nNumEndPoint ; - } - } - return ret; - -#undef PATH_LEN -} -#endif /* } */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include + +#include "mode.h" +#include "ichitaut.h" + +/*******************************************************************/ + +#if ( FIND_RING_SYSTEMS == 1 ) /* { */ + +/* local prototypes */ +int are_alt_bonds( U_CHAR *bonds, int len ); +int AddBondsPos( inp_ATOM *atom, T_BONDPOS *BondPosTmp, int nNumBondPosTmp, T_BONDPOS *BondPos, int nMaxNumBondPos, int nNumBondPos ); +int AddEndPoints( T_ENDPOINT *EndPointTmp, int nNumNewEndPoint, T_ENDPOINT *EndPoint, int nMaxNumEndPoint, int nNumEndPoint); + + + + + +/****************************************** + * + * Tautomerism in 5- and 6-member rings + * + ******************************************/ + +const int NONE = (AT_RANK)~0; + + +/* + 1,5 Tautomerism in 6-member alt ring: + + /=\ /==\ + HN C=O <-> N C-OH + \=/ \\-// + + + 1,2 Tautomerism in 5-member ring: + + + HN--X N==X + | \\ | \ + | Z <-> | Z + | / | // + N==Y HN--Y + + + 1,4 tautomerism in 7-member ring + + /C==D //C-D + O=B \ HO-B \\ + | E <-> | E + HO-A // O=A / + \\G-F \\G-F + + + 1,4 tautomerism in 5-member ring + + + O=B--C O-B==C + | \\ | \ + | D <-> | D + | / | // + HO-A==E HO=A--E + +*/ +typedef int CHECK_DFS_RING( struct tagCANON_GLOBALS *pCG, + inp_ATOM *atom, + DFS_PATH *DfsPath, + int nLenDfsPath, + int nStartAtomNeighbor, + int nStartAtomNeighbor2, + int nStartAtomNeighborNeighbor, + T_ENDPOINT *EndPoint, + int nMaxNumEndPoint, + T_BONDPOS *BondPos, + int nMaxNumBondPos, + int *pnNumEndPoint, + int *pnNumBondPos, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD, + int num_atoms ); + +typedef int CHECK_CENTERPOINT ( inp_ATOM *atom, int iat ); + +CHECK_DFS_RING Check7MembTautRing; +CHECK_DFS_RING Check6MembTautRing; +CHECK_DFS_RING Check5MembTautRing; + +#if ( TAUT_15_NON_RING == 1 ) /* post v.1 feature */ +/* DFS simple alt path for 1,5 tautomerism, post v.1 feature */ +typedef int CHECK_DFS_PATH( struct tagCANON_GLOBALS *pCG, + inp_ATOM *atom, + DFS_PATH *DfsPath, + int nLenDfsPath, + int jNxtNeigh, + int nStartAtomNeighbor, + int nStartAtomNeighbor2, + int nStartAtomNeighborNeighbor, + T_ENDPOINT *EndPoint, + int nMaxNumEndPoint, + T_BONDPOS *BondPos, + int nMaxNumBondPos, + int *pnNumEndPoint, + int *pnNumBondPos, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD, + int num_atoms ); + +typedef int CHECK_DFS_CENTERPOINT( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int jNxtNeigh, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD, int num_atoms ); + + +CHECK_DFS_PATH Check15TautPath; +CHECK_DFS_CENTERPOINT Check15TautPathCenterpoint; + +int DFS_FindTautAltPath( struct tagCANON_GLOBALS *pCG, + inp_ATOM *atom, + int nStartAtom, + int nStartAtomNeighbor, + int nStartAtomNeighbor2, + int nStartAtomNeighborNeighbor, + int nCycleLen, + AT_RANK *nDfsPathPos, + DFS_PATH *DfsPath, + CHECK_DFS_PATH *CheckDfsPath, + CHECK_DFS_CENTERPOINT *CheckCenterPoint, + T_ENDPOINT *EndPoint, + int nMaxNumEndPoint, + T_BONDPOS *BondPos, + int nMaxNumBondPos, + int *pnNumEndPoint, + int *pnNumBondPos, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD, + int num_atoms ); + +#define BOND_WRONG 64 +#define IS_ALT_OR_DBLBOND(X) (((X) == BOND_SINGLE || (X) == BOND_DOUBLE)? (X) : \ + ((X) == BOND_ALTERN || (X) == BOND_TAUTOM || (X) == BOND_ALT12NS)? BOND_ALTERN : \ + BOND_WRONG); + +#endif /* TAUT_15_NON_RING */ + +int DFS_FindTautInARing( struct tagCANON_GLOBALS *pCG, + inp_ATOM *atom, + int nStartAtom, + int nStartAtomNeighbor, + int nStartAtomNeighbor2, + int nStartAtomNeighborNeighbor, + int nCycleLen, + AT_RANK *nDfsPathPos, + DFS_PATH *DfsPath, + CHECK_DFS_RING *CheckDfsRing, + CHECK_CENTERPOINT *CheckCenterPoint, + T_ENDPOINT *EndPoint, + int nMaxNumEndPoint, + T_BONDPOS *BondPos, + int nMaxNumBondPos, + int *pnNumEndPoint, + int *pnNumBondPos, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD, + int num_atoms ); + +#if ( REPLACE_ALT_WITH_TAUT == 1 ) +#define REPLACE_THE_BOND(X) ( (X) == BOND_SINGLE || (X) == BOND_DOUBLE || (X) == BOND_ALTERN || (X) == BOND_ALT12NS ) +#else +#define REPLACE_THE_BOND(X) ( (X) == BOND_SINGLE || (X) == BOND_DOUBLE ) +#endif + + +int bIsCenterPointStrict( inp_ATOM *atom, int iat ) +{ + if ( atom[iat].valence == atom[iat].chem_bonds_valence ) { + int endpoint_valence = get_endpoint_valence(atom[iat].el_number); + if ( endpoint_valence && (endpoint_valence > atom[iat].valence && /* added a check for negative charge or H 3-31-03 */ + (atom[iat].num_H || atom[iat].charge == -1) || + !atom[iat].charge && atom[iat].c_point) ) { + return 1; /* may appear to be tautomeric or chargable + (this increases chem_bonds_valence), should be explored */ + } + return 0; + } + if (atom[iat].valence+1 == atom[iat].chem_bonds_valence && + is_centerpoint_elem_strict( atom[iat].el_number ) ) { + return 1; + } + return 0; +} +/********************************************************************************/ +int nGet14TautIn7MembAltRing( struct tagCANON_GLOBALS *pCG, + inp_ATOM *atom, + int nStartAtom, + int nStartAtomNeighbor, + int nStartAtomNeighborEndpoint, + int nStartAtomNeighborNeighborEndpoint, + AT_RANK *nDfsPathPos, + DFS_PATH *DfsPath, + int nMaxLenDfsPath, + T_ENDPOINT *EndPoint, + int nMaxNumEndPoint, + T_BONDPOS *BondPos, + int nMaxNumBondPos, + int *pnNumEndPoint, + int *pnNumBondPos, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD, + int num_atoms ) +{ + int nRet; + + *pnNumEndPoint = 0; + *pnNumBondPos = 0; + + if ( nMaxLenDfsPath <= 7 ) { + return -1; /* path is too short */ + } + + nRet = DFS_FindTautInARing( pCG, + atom, nStartAtom, nStartAtomNeighbor, + nStartAtomNeighborEndpoint, nStartAtomNeighborNeighborEndpoint, 7, + nDfsPathPos, DfsPath, + Check7MembTautRing, bIsCenterPointStrict, + EndPoint, nMaxNumEndPoint, + BondPos, nMaxNumBondPos, + pnNumEndPoint, pnNumBondPos, + pBNS, pBD, num_atoms ); + + + return nRet; +} + + +/********************************************************************************/ +int nGet14TautIn5MembAltRing( struct tagCANON_GLOBALS *pCG, + inp_ATOM *atom, + int nStartAtom, + int nStartAtomNeighbor, + int nStartAtomNeighborEndpoint, + int nStartAtomNeighborNeighborEndpoint, + AT_RANK *nDfsPathPos, + DFS_PATH *DfsPath, + int nMaxLenDfsPath, + T_ENDPOINT *EndPoint, + int nMaxNumEndPoint, + T_BONDPOS *BondPos, + int nMaxNumBondPos, + int *pnNumEndPoint, + int *pnNumBondPos, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD, + int num_atoms ) +{ + int nRet; + + *pnNumEndPoint = 0; + *pnNumBondPos = 0; + + if ( nMaxLenDfsPath <= 5 ) { + return -1; /* path is too short */ + } + + nRet = DFS_FindTautInARing( pCG, + atom, nStartAtom, nStartAtomNeighbor, + nStartAtomNeighborEndpoint, nStartAtomNeighborNeighborEndpoint, 5, + nDfsPathPos, DfsPath, + Check7MembTautRing, bIsCenterPointStrict, + EndPoint, nMaxNumEndPoint, + BondPos, nMaxNumBondPos, + pnNumEndPoint, pnNumBondPos, + pBNS, pBD, num_atoms ); + + + return nRet; +} + +/********************************************************************************/ +int nGet12TautIn5MembAltRing( struct tagCANON_GLOBALS *pCG, + inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, + AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, int nMaxLenDfsPath, + T_ENDPOINT *EndPoint, int nMaxNumEndPoint, + T_BONDPOS *BondPos, int nMaxNumBondPos, + int *pnNumEndPoint, int *pnNumBondPos, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD, int num_atoms ) +{ + int nRet; + + *pnNumEndPoint = 0; + *pnNumBondPos = 0; + + if ( nMaxLenDfsPath <= 5 ) { + return -1; /* path is too short */ + } + + nRet = DFS_FindTautInARing( pCG, + atom, nStartAtom, nStartAtomNeighbor, -1, -1, 5, + nDfsPathPos, DfsPath, + Check5MembTautRing, bIsCenterPointStrict, + EndPoint, nMaxNumEndPoint, + BondPos, nMaxNumBondPos, + pnNumEndPoint, pnNumBondPos, + pBNS, pBD, num_atoms ); + return nRet; +} + +/********************************************************************************/ +int nGet15TautIn6MembAltRing( struct tagCANON_GLOBALS *pCG, + inp_ATOM *atom, int nStartAtom, AT_RANK *nDfsPathPos, + DFS_PATH *DfsPath, int nMaxLenDfsPath, + T_ENDPOINT *EndPoint, int nMaxNumEndPoint, + T_BONDPOS *BondPos, int nMaxNumBondPos, + int *pnNumEndPoint, int *pnNumBondPos, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD, int num_atoms ) +{ + int nRet; + + *pnNumEndPoint = 0; + *pnNumBondPos = 0; + + if ( nMaxLenDfsPath <= 7 ) { + return -1; /* path is too short */ + } + + nRet = DFS_FindTautInARing( pCG, + atom, nStartAtom, -1/*nStartAtomNeighbor*/, -1/*nStartAtomNeighbor2*/, + -1/*nStartAtomNeighborNeighbor*/, 6 /* nCycleLen*/, + nDfsPathPos, DfsPath, + Check6MembTautRing, bIsCenterPointStrict, + EndPoint, nMaxNumEndPoint, + BondPos, nMaxNumBondPos, + pnNumEndPoint, pnNumBondPos, + pBNS, pBD, num_atoms ); + return nRet; +} + + + +#if ( TAUT_15_NON_RING == 1 ) /***** post v.1 feature *****/ +/********************************************************************************/ +int nGet15TautInAltPath( struct tagCANON_GLOBALS *pCG, + inp_ATOM *atom, int nStartAtom, AT_RANK *nDfsPathPos, + DFS_PATH *DfsPath, int nMaxLenDfsPath, + T_ENDPOINT *EndPoint, int nMaxNumEndPoint, + T_BONDPOS *BondPos, int nMaxNumBondPos, + int *pnNumEndPoint, int *pnNumBondPos, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD, int num_atoms ) +{ + int nRet; + + *pnNumEndPoint = 0; + *pnNumBondPos = 0; + + if ( nMaxLenDfsPath <= 7 ) { + return -1; /* path is too short */ + } + + nRet = DFS_FindTautAltPath( pCG, + atom, nStartAtom, -1/*nStartAtomNeighbor*/, -1/*nStartAtomNeighbor2*/, + -1/*nStartAtomNeighborNeighbor*/, 4 /* nCycleLen*/, + nDfsPathPos, DfsPath, + Check15TautPath, Check15TautPathCenterpoint, + EndPoint, nMaxNumEndPoint, + BondPos, nMaxNumBondPos, + pnNumEndPoint, pnNumBondPos, + pBNS, pBD, num_atoms ); + return nRet; +} +#endif + + + +/********************************************************************************/ +/* DFS version */ +#define MAX_DFS_DEPTH 16 + + + +/********************************************************************************/ +int DFS_FindTautInARing( struct tagCANON_GLOBALS *pCG, + inp_ATOM *atom, + int nStartAtom, + int nStartAtomNeighbor, + int nStartAtomNeighbor2, + int nStartAtomNeighborNeighbor, + int nCycleLen, + AT_RANK *nDfsPathPos, + DFS_PATH *DfsPath, + CHECK_DFS_RING *CheckDfsRing, + CHECK_CENTERPOINT *CheckCenterPoint, + T_ENDPOINT *EndPoint, + int nMaxNumEndPoint, + T_BONDPOS *BondPos, + int nMaxNumBondPos, + int *pnNumEndPoint, + int *pnNumBondPos, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD, + int num_atoms ) +{ + /* Depth First Search */ + /* Ignore all atoms not belonging to the current ring system (=biconnected component) */ + AT_RANK nMinLenDfsPath; + int j, cur_at, nxt_at, prv_at; + int nLenDfsPath, nNumFound, ret; + AT_RANK nRingSystem; + int nDoNotTouchAtom1 = -1, nDoNotTouchAtom2 = -1; + + nLenDfsPath=0; + nNumFound=0; + + nCycleLen --; + + DfsPath[nLenDfsPath].at_no = cur_at = nStartAtom; + DfsPath[nLenDfsPath].bond_type = 0; + DfsPath[nLenDfsPath].bond_pos = -1; + nDfsPathPos[cur_at] = nLenDfsPath+1; /* mark */ + nRingSystem = atom[nStartAtom].nRingSystem; + nMinLenDfsPath = 0; + if ( nStartAtomNeighbor2 >= 0 ) { + nDoNotTouchAtom1 = (int)atom[cur_at].neighbor[nStartAtomNeighbor2]; + } + + + /* add the first neighbor to the 2nd tree position if required */ + if ( nStartAtomNeighbor >= 0 ) { + j = nStartAtomNeighbor; + prv_at = cur_at; + cur_at = atom[prv_at].neighbor[j]; + DfsPath[nLenDfsPath].bond_type = (atom[prv_at].bond_type[j] & ~BOND_MARK_ALL); +#if ( FIX_BOND23_IN_TAUT == 1 ) + DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER(pBNS,prv_at,j,DfsPath[nLenDfsPath].bond_type); +#endif + DfsPath[nLenDfsPath].bond_pos = j; + + nLenDfsPath ++; + + DfsPath[nLenDfsPath].at_no = cur_at; + DfsPath[nLenDfsPath].bond_type = 0; + DfsPath[nLenDfsPath].bond_pos = -1; + nDfsPathPos[cur_at] = nLenDfsPath+1; + nMinLenDfsPath ++; + if ( nStartAtomNeighborNeighbor >= 0 ) { + nDoNotTouchAtom2 = (int)atom[cur_at].neighbor[nStartAtomNeighborNeighbor]; + } + } + + /* MAIN DFS CYCLE: may find one and the same t-group 2 times; saves only one instance */ + /* traverse *all* paths starting at atom[nStartAtom]; max. path length = (nCycleLen+1) */ + while ( nLenDfsPath >= nMinLenDfsPath ) { + j = ++DfsPath[nLenDfsPath].bond_pos; + if ( j < atom[cur_at=(int)DfsPath[nLenDfsPath].at_no].valence ) { + DfsPath[nLenDfsPath].bond_type = (atom[cur_at].bond_type[j] & ~BOND_MARK_ALL); +#if ( FIX_BOND23_IN_TAUT == 1 ) + DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER(pBNS,cur_at,j,DfsPath[nLenDfsPath].bond_type); +#endif + nxt_at = (int)atom[cur_at].neighbor[j]; + if ( nxt_at == nDoNotTouchAtom1 || + nxt_at == nDoNotTouchAtom2 ) { + ; /* ignore */ + } else + if ( nDfsPathPos[nxt_at] ) { + /* found a ring closure or a step backwards */ + if ( 1 == nDfsPathPos[nxt_at] && nLenDfsPath == nCycleLen ) { + /* we have found the cycle; check it */ + ret = (*CheckDfsRing)( pCG, + atom, + DfsPath, nLenDfsPath, + nStartAtomNeighbor, + nStartAtomNeighbor2, + nStartAtomNeighborNeighbor, + EndPoint, nMaxNumEndPoint, + BondPos, nMaxNumBondPos, + pnNumEndPoint, pnNumBondPos, + pBNS, pBD, num_atoms ); + if ( ret < 0 ) { + nNumFound = ret; + goto clear_path; + } + nNumFound += ret; + } + } else + if ( !(*CheckCenterPoint)( atom, nxt_at ) ) { + ; /* cannot advance to a non-centerpoint; ignore */ + } else + if ( nLenDfsPath < nCycleLen ) { + /* advance */ + nLenDfsPath ++; + cur_at = nxt_at; + DfsPath[nLenDfsPath].at_no = cur_at; + DfsPath[nLenDfsPath].bond_type = 0; + DfsPath[nLenDfsPath].bond_pos = -1; + nDfsPathPos[cur_at] = nLenDfsPath+1; /* mark */ + } + } else { + /* retract */ + nDfsPathPos[(int)DfsPath[nLenDfsPath].at_no] = 0; + nLenDfsPath --; + } + } +clear_path: + while ( 0 <= nLenDfsPath ) { + nDfsPathPos[(int)DfsPath[nLenDfsPath].at_no] = 0; + nLenDfsPath --; + } + return nNumFound; +} + + + +#if ( TAUT_15_NON_RING == 1 ) /***** post v.1 feature *****/ + +/********************************************************************************/ +int DFS_FindTautAltPath( struct tagCANON_GLOBALS *pCG, + inp_ATOM *atom, + int nStartAtom, + int nStartAtomNeighbor, + int nStartAtomNeighbor2, + int nStartAtomNeighborNeighbor, + int nCycleLen, + AT_RANK *nDfsPathPos, + DFS_PATH *DfsPath, + CHECK_DFS_PATH *CheckDfsPath, + CHECK_DFS_CENTERPOINT *CheckCenterPointPath, + T_ENDPOINT *EndPoint, + int nMaxNumEndPoint, + T_BONDPOS *BondPos, + int nMaxNumBondPos, + int *pnNumEndPoint, + int *pnNumBondPos, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD, + int num_atoms ) +{ + /* Naive Depth First Search: same atom may be approached along different alt paths */ + /* Ignore all atoms not belonging to the current ring system (=biconnected component) */ + AT_RANK nMinLenDfsPath; + int j, cur_at, nxt_at, prv_at; + int nLenDfsPath, nNumFound, ret; + AT_RANK nRingSystem; + int nDoNotTouchAtom1 = -1, nDoNotTouchAtom2 = -1; + + nLenDfsPath=0; + nNumFound=0; + + nCycleLen --; /* indef of the last atom in the alt path, statring from 0 */ + + DfsPath[nLenDfsPath].at_no = cur_at = nStartAtom; + DfsPath[nLenDfsPath].bond_type = 0; + DfsPath[nLenDfsPath].bond_pos = -1; /* initialize index of the bond to the next atom */ + nDfsPathPos[cur_at] = nLenDfsPath+1; /* mark with distance + 1 */ + nRingSystem = atom[nStartAtom].nRingSystem; + nMinLenDfsPath = 0; /* allow to restart from nStartAtom */ + if ( nStartAtomNeighbor2 >= 0 ) + { + nDoNotTouchAtom1 = (int)atom[cur_at].neighbor[nStartAtomNeighbor2]; + } + + + /* add the first neighbor to the 2nd tree position if required */ + if ( nStartAtomNeighbor >= 0 ) + { + j = nStartAtomNeighbor; + prv_at = cur_at; + cur_at = atom[prv_at].neighbor[j]; + DfsPath[nLenDfsPath].bond_type = (atom[prv_at].bond_type[j] & ~BOND_MARK_ALL); +#if ( FIX_BOND23_IN_TAUT == 1 ) + DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER(pBNS,prv_at,j,DfsPath[nLenDfsPath].bond_type); +#endif + DfsPath[nLenDfsPath].bond_pos = j; /* fix index of the bond to the next atom */ + + nLenDfsPath ++; + + DfsPath[nLenDfsPath].at_no = cur_at; + DfsPath[nLenDfsPath].bond_type = 0; + DfsPath[nLenDfsPath].bond_pos = -1; + nDfsPathPos[cur_at] = nLenDfsPath+1; /* mark with distance + 1 */ + nMinLenDfsPath ++; /* allow to restart from nStartAtom's neighbor */ + if ( nStartAtomNeighborNeighbor >= 0 ) + { + nDoNotTouchAtom2 = (int)atom[cur_at].neighbor[nStartAtomNeighborNeighbor]; + } + } + + /* MAIN DFS CYCLE: may find one and the same t-group 2 times; saves only one instance */ + /* traverse *all* paths starting at atom[nStartAtom]; max. path length = (nCycleLen+1) */ + while ( nLenDfsPath >= nMinLenDfsPath ) + { + j = ++DfsPath[nLenDfsPath].bond_pos; + if ( j < atom[cur_at=(int)DfsPath[nLenDfsPath].at_no].valence ) + { + DfsPath[nLenDfsPath].bond_type = (atom[cur_at].bond_type[j] & ~BOND_MARK_ALL); +#if ( FIX_BOND23_IN_TAUT == 1 ) + DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER(pBNS,cur_at,j,DfsPath[nLenDfsPath].bond_type); +#endif + nxt_at = (int)atom[cur_at].neighbor[j]; + if ( nxt_at == nDoNotTouchAtom1 || /* forbidden */ + nxt_at == nDoNotTouchAtom2 || /* forbidden */ + nDfsPathPos[nxt_at] || /* ring closure */ + nLenDfsPath && nxt_at == (int)DfsPath[nLenDfsPath-1].at_no /* step backwards */ + ) + { + ; /* ignore nxt_at */ + } + else + if ( nLenDfsPath == nCycleLen && + /* 1,5 and at least one of the endpoints is not in a ring */ + (atom[nxt_at].nNumAtInRingSystem == 1 || atom[nStartAtom].nNumAtInRingSystem == 1) && + /* we have found the alt path of the requested length; check it */ + /* calling Check15TautPath() */ + (ret = (*CheckDfsPath)( pCG, + atom, + DfsPath, nLenDfsPath, + j, nStartAtomNeighbor, + nStartAtomNeighbor2, + nStartAtomNeighborNeighbor, + EndPoint, nMaxNumEndPoint, BondPos, nMaxNumBondPos, + pnNumEndPoint, pnNumBondPos, + pBNS, pBD, num_atoms ) ) ) + { + if ( ret < 0 ) + { + nNumFound = ret; + goto clear_path; /* program error */ + } + nNumFound += ret; /* success */ + } + else /* calling Check15TautPathCenterpoint() */ + if ( !(*CheckCenterPointPath)( atom, DfsPath, nLenDfsPath, j, + pBNS, pBD, num_atoms ) ) + { + ; /* cannot advance to a non-centerpoint; ignore */ + } + else + if ( nLenDfsPath < nCycleLen ) + { + /* advance */ + nLenDfsPath ++; + cur_at = nxt_at; + DfsPath[nLenDfsPath].at_no = cur_at; + DfsPath[nLenDfsPath].bond_type = 0; + DfsPath[nLenDfsPath].bond_pos = -1; + nDfsPathPos[cur_at] = nLenDfsPath+1; /* mark */ + } + } + else + { + /* retract */ + nDfsPathPos[(int)DfsPath[nLenDfsPath].at_no] = 0; + nLenDfsPath --; + } + } + +clear_path: + while ( 0 <= nLenDfsPath ) + { + nDfsPathPos[(int)DfsPath[nLenDfsPath].at_no] = 0; + nLenDfsPath --; + } + + return nNumFound; +} + + + +#endif /* TAUT_15_NON_RING */ +/******************************************* + * check if bonds are alternating */ + +int are_alt_bonds( U_CHAR *bonds, int len ) +{ + U_CHAR next_bond; + int i, bAnyBond, bTautBondPresent=BOND_ALTERN; + if ( len < 2 || bonds[0] == BOND_TRIPLE || bonds[0] == BOND_ALT_13 ) { + return 0; + } + next_bond = bonds[0]==BOND_SINGLE? BOND_DOUBLE : bonds[0]==BOND_DOUBLE? BOND_SINGLE : 0; + if ( bonds[0] == BOND_TAUTOM ) { + bTautBondPresent= BOND_TAUTOM; + next_bond = 0; + } else { + next_bond = bonds[0]==BOND_SINGLE? BOND_DOUBLE : bonds[0]==BOND_DOUBLE? BOND_SINGLE : 0; + } + + for ( i = 1; i < len; i ++ ) { + if ( bonds[i] == BOND_TAUTOM ) { + bTautBondPresent = BOND_TAUTOM; + bAnyBond = 1; + } else { + bAnyBond = (bonds[i] == BOND_ALTERN || bonds[i] == BOND_ALT12NS); + } + if ( next_bond ) { + if ( bonds[i] == next_bond || bAnyBond ) { + next_bond = (next_bond == BOND_SINGLE)? BOND_DOUBLE : BOND_SINGLE; + continue; + } + return 0; + } else + if ( bonds[i] == BOND_SINGLE ) { + next_bond = BOND_DOUBLE; + continue; + } else + if ( bonds[i] == BOND_DOUBLE ) { + next_bond = BOND_SINGLE; + continue; + } else + if ( !bAnyBond ) { + return 0; + } + } + return !next_bond? bTautBondPresent : + (next_bond == BOND_SINGLE)? BOND_DOUBLE : BOND_SINGLE; /* bond to the end atom */ +} + +/********************************************************************************/ +int AddBondsPos( inp_ATOM *atom, T_BONDPOS *BondPosTmp, int nNumBondPosTmp, T_BONDPOS *BondPos, + int nMaxNumBondPos, int nNumBondPos ) +{ + int i, j, k, cur_at, nxt_at; + /* add opposite direction bonds to BondPosTmp */ + for ( j = 0; j < nNumBondPosTmp; j += 2 ) { + cur_at = BondPosTmp[j].nAtomNumber; + nxt_at = atom[cur_at].neighbor[(int)BondPosTmp[j].neighbor_index]; + for ( k = 0; k < atom[nxt_at].valence; k ++ ) { + if ( cur_at == atom[nxt_at].neighbor[k] ) { + BondPosTmp[j+1].nAtomNumber = nxt_at; + BondPosTmp[j+1].neighbor_index = k; + break; + } + } + } + /* add new tautomeric bonds */ + for ( j = 0; j < nNumBondPosTmp; j += 2 ) { + for ( i = 0; i < nNumBondPos; i ++ ) { + if ( BondPos[i].nAtomNumber == BondPosTmp[j].nAtomNumber && + BondPos[i].neighbor_index == BondPosTmp[j].neighbor_index || + BondPos[i].nAtomNumber == BondPosTmp[j+1].nAtomNumber && + BondPos[i].neighbor_index == BondPosTmp[j+1].neighbor_index ) { + break; /* bond has already been added */ + } + } + if ( i == nNumBondPos ) { + if ( i > nMaxNumBondPos ) { + return -1; /* overflow */ + } + BondPos[nNumBondPos ++] = BondPosTmp[j]; + } + } + return nNumBondPos; +} +/********************************************************************************/ +int AddEndPoints( T_ENDPOINT *EndPointTmp, int nNumNewEndPoint, T_ENDPOINT *EndPoint, + int nMaxNumEndPoint, int nNumEndPoint) +{ + int i, j; + /* add new endpoints */ + for ( j = 0; j < nNumNewEndPoint; j ++ ) { + for ( i = 0; i < nNumEndPoint; i ++ ) { + if ( EndPoint[i].nAtomNumber == EndPointTmp[j].nAtomNumber ) { + break; + } + } + if ( i == nNumEndPoint ) { + if ( i > nMaxNumEndPoint ) { + return -1; /* overflow */ + } + EndPoint[nNumEndPoint ++] = EndPointTmp[j]; + } + } + return nNumEndPoint; +} +/********************************************************************************/ +/* + + 1,4 tautomerism in 7-member ring + + /C==D //C-D A=DfsPath[0].at_no + O=B \ HO-B \\ B=DfsPath[1].at_no + | E <-> | E nStartAtomNeighbor2: from A to HO + HO-A // O=A / nStartAtomNeighborNeighbor: from B to O + \\G-F \\G-F + + + 1,4 tautomerism in 5-member ring + + + O=B--C O-B==C + | \\ | \ + | D <-> | D + | / | // + HO-A==E HO=A--E + +*/ +/********************************************************************************/ +int Check7MembTautRing( struct tagCANON_GLOBALS *pCG, + inp_ATOM *atom, + DFS_PATH *DfsPath, + int nLenDfsPath, + int nStartAtomNeighbor, + int nStartAtomNeighbor2, + int nStartAtomNeighborNeighbor, + T_ENDPOINT *EndPoint, + int nMaxNumEndPoint, + T_BONDPOS *BondPos, + int nMaxNumBondPos, + int *pnNumEndPoint, + int *pnNumBondPos, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD, + int num_atoms ) +{ +#define PATH_LEN 8 + + int i, j, k, /*m,*/ nNumEndPoint, nNumEndPointTmp, nNumBondPos, nNumBondPosTmp; + int endpoint, /*nMobile, nMobile1, nMobile2,*/ o1_at, o2_at; + int ret; + U_CHAR path_bonds[PATH_LEN+1], bond_type; + T_ENDPOINT EndPointTmp[2]; + T_BONDPOS BondPosTmp[2*PATH_LEN]; + ENDPOINT_INFO eif1, eif2; + int nErr=0; + + + if ( nLenDfsPath + 2 > PATH_LEN ) { + return -1; /* too long path */ + } + if ( nLenDfsPath != 6 && nLenDfsPath != 4 ) { + return -1; /* wrong call */ + } + + + nNumBondPos = *pnNumBondPos; + nNumEndPoint = *pnNumEndPoint; + nNumBondPosTmp = 0; + nNumEndPointTmp = 0; + ret = 0; + + o1_at = atom[(int)DfsPath[1].at_no].neighbor[nStartAtomNeighborNeighbor]; + o2_at = atom[(int)DfsPath[0].at_no].neighbor[nStartAtomNeighbor2]; + /* + nMobile1 = (atom[o1_at].charge == -1) + atom[o1_at].num_H; + nMobile2 = (atom[o2_at].charge == -1) + atom[o2_at].num_H; + */ + if ( !nGetEndpointInfo( atom, o1_at, &eif1 ) || + !nGetEndpointInfo( atom, o2_at, &eif2 ) ) { + return 0; + } + + /* save endpoints */ + for ( j = 0; j < 2; j ++ ) { + endpoint = j? o2_at : o1_at; + if ( !atom[endpoint].endpoint ) { + AddAtom2num( EndPointTmp[nNumEndPointTmp].num, atom, endpoint, 2 ); /* fill out */ + AddAtom2DA( EndPointTmp[nNumEndPointTmp].num_DA, atom, endpoint, 2 ); + /* + nMobile = j? nMobile2 : nMobile1; + } else { + nMobile = 0; + } + if ( nMobile ) { + EndPointTmp[nNumEndPointTmp].num[1] = (atom[endpoint].charge == -1); + EndPointTmp[nNumEndPointTmp].num[0] = nMobile; + for ( m = 0; m < T_NUM_ISOTOPIC; m ++ ) { + EndPointTmp[nNumEndPointTmp].num[T_NUM_NO_ISOTOPIC+m] = atom[endpoint].num_iso_H[NUM_H_ISOTOPES-m-1]; + } + */ + } else { + memset( EndPointTmp + nNumEndPointTmp, 0, sizeof(EndPointTmp[0]) ); + } + EndPointTmp[nNumEndPointTmp].nAtomNumber = endpoint; + EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint; + EndPointTmp[nNumEndPointTmp].nEquNumber = 0; + nNumEndPointTmp ++; + } + + + /* extract bonds */ + k = (int)DfsPath[1].at_no; + bond_type = (atom[k].bond_type[nStartAtomNeighborNeighbor] & ~BOND_MARK_ALL); +#if ( FIX_BOND23_IN_TAUT == 1 ) + bond_type = ACTUAL_ORDER(pBNS,k,nStartAtomNeighborNeighbor,bond_type); +#endif + path_bonds[0] = bond_type; + if ( REPLACE_THE_BOND( bond_type ) ) { + BondPosTmp[nNumBondPosTmp].nAtomNumber = k; + BondPosTmp[nNumBondPosTmp].neighbor_index = nStartAtomNeighborNeighbor; + nNumBondPosTmp += 2; + } + for ( i = 1; i <= nLenDfsPath; i ++ ) { + bond_type = DfsPath[i].bond_type; + path_bonds[i] = bond_type; + if ( REPLACE_THE_BOND( bond_type ) ) { + BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[i].at_no; + BondPosTmp[nNumBondPosTmp].neighbor_index = DfsPath[i].bond_pos; + nNumBondPosTmp += 2; + } + } + bond_type = (atom[(int)DfsPath[0].at_no].bond_type[nStartAtomNeighbor2] & ~BOND_MARK_ALL); +#if ( FIX_BOND23_IN_TAUT == 1 ) + bond_type = ACTUAL_ORDER(pBNS,(int)DfsPath[0].at_no,nStartAtomNeighbor2,bond_type); +#endif + path_bonds[i++] = bond_type; + if ( REPLACE_THE_BOND( bond_type ) ) { + BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[0].at_no; + BondPosTmp[nNumBondPosTmp].neighbor_index = nStartAtomNeighbor2; + nNumBondPosTmp += 2; + } + + if ( !are_alt_bonds( path_bonds, i ) ) { + return 0; + } + + /* path_bonds is from at_n1 to at_n2 */ + if ( !(j=are_alt_bonds( path_bonds, i )) ) { + return 0; + } + /* j is a bond type of the last bond to o2_at, the first bond from o1_at is 2-j if j=1 or 2 */ + + /* single bond at o2_at: it should have a mobile atom, o1_at should not */ + if ( j == BOND_SINGLE && (!atom[o2_at].endpoint && !eif2.cDonor || !atom[o1_at].endpoint && !eif1.cAcceptor) || + /* double bond at o2_at: it should not have a mobile atom, o1_at should */ + j == BOND_DOUBLE && (!atom[o2_at].endpoint && !eif2.cAcceptor || !atom[o1_at].endpoint && !eif1.cDonor) ) { + return 0; /* bond pattern does not fit */ + } + + + nNumBondPos = AddBondsPos( atom, BondPosTmp, nNumBondPosTmp, BondPos, nMaxNumBondPos, nNumBondPos ); + nNumEndPoint = AddEndPoints( EndPointTmp, nNumEndPointTmp, EndPoint, nMaxNumEndPoint, nNumEndPoint); + + if ( nNumBondPos >= 0 && nNumEndPoint >= 0 ) { + if (ret = (nNumBondPos > *pnNumBondPos) || (nNumEndPoint > *pnNumEndPoint)) { + *pnNumBondPos = nNumBondPos ; + *pnNumEndPoint = nNumEndPoint ; + } + } + + if ( ret ) + { + /* finally check whether the bonds allow moving the hydrogens */ + if ( (atom[o1_at].endpoint != atom[o2_at].endpoint || !atom[o1_at].endpoint) ) + { + + nErr = bExistsAnyAltPath( pCG, pBNS, pBD, atom, num_atoms, o1_at, o2_at, ALT_PATH_MODE_TAUTOM ); + + if ( nErr <= 0 ) + return nErr; + } + } + + return ret; + + +#undef PATH_LEN +} + + + + +/********************************************************************************/ +/* + 1,5 Tautomerism in 6-member alt ring: + + /=\ /==\ N = DfsPath[0].at_no + HN C=O <-> N C-OH C = DfsPath[3].at_no + \=/ \\-// + +*/ +/********************************************************************************/ +/* check if a tautomeric 6-member ring has been found */ +int Check6MembTautRing( struct tagCANON_GLOBALS *pCG, + inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int nStartAtomNeighbor, + int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, + T_ENDPOINT *EndPoint, int nMaxNumEndPoint, + T_BONDPOS *BondPos, int nMaxNumBondPos, + int *pnNumEndPoint, int *pnNumBondPos, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD, int num_atoms ) +{ +#define PATH_LEN 4 + int i, j, k, /*m,*/ nNumBondPos, nNumEndPoint; + int nNumEndPointTmp, nNumBondPosTmp, o_at=0, ret; + /* int num_taut_endpoints, num_H; */ + int middle_pos; + int nMobile, endpoint, endpoint_valence, chem_bonds_valence; + int nMobile1, endpoint_valence1; /* o_at */ + int nMobile2, endpoint_valence2; /* n_at */ + int nxt_at; + int n_at; + U_CHAR path_bonds[2][PATH_LEN+1], bond_type; + T_ENDPOINT EndPointTmp[2]; + T_BONDPOS BondPosTmp[4*PATH_LEN]; + ENDPOINT_INFO eif1, eif2; + + if ( nStartAtomNeighbor >= 0 || nStartAtomNeighbor2 >= 0 || nStartAtomNeighborNeighbor >= 0 ) + return -1; /* wrong call */ + + if ( nLenDfsPath != 5 ) + return -1; /* wrong call */ + + nNumBondPos = *pnNumBondPos; + nNumEndPoint = *pnNumEndPoint; + nNumBondPosTmp = 0; + nNumEndPointTmp = 0; + ret = 0; + + n_at = (int)DfsPath[0].at_no; /* -N= or -NH- atom */ + nxt_at = DfsPath[middle_pos = (nLenDfsPath+1)/2].at_no; /* must have tautomeric neighbor -OH or =O or -NH2 or =NH */ + + if ( atom[nxt_at].valence != 3 +#if ( TAUT_RINGS_ATTACH_CHAIN == 1 ) + || !atom[nxt_at].bCutVertex +#endif + ) { + return 0; + } + + for ( i = 0; i < atom[nxt_at].valence; i ++ ) { + o_at = atom[nxt_at].neighbor[i]; + if ( o_at != DfsPath[middle_pos-1].at_no && o_at != DfsPath[middle_pos+1].at_no ) { + break; /* >=O or />-OH has been found */ + } + } + if ( i == atom[nxt_at].valence ) { + return 0; /* no neighboring atom >=O or />-OH */ + } + bond_type = (atom[nxt_at].bond_type[i] & ~BOND_MARK_ALL); +#if ( FIX_BOND23_IN_TAUT == 1 ) + bond_type = ACTUAL_ORDER(pBNS,nxt_at,i,bond_type); +#endif + if ( bond_type != BOND_SINGLE && + bond_type != BOND_DOUBLE && + bond_type != BOND_TAUTOM && + bond_type != BOND_ALT12NS && + bond_type != BOND_ALTERN ) { + return 0; + } + + /* check whether the two atoms already belong to one tautomeric group */ +#if ( TAUT_IGNORE_EQL_ENDPOINTS == 1 ) + if ( atom[n_at].endpoint && atom[n_at].endpoint == atom[o_at].endpoint ) { + return 0; + } +#endif + /* check =O valence; must be 2 for O, S, Se or 3 for N */ + if ( !(endpoint_valence1=nGetEndpointInfo( atom, o_at, &eif1 )) ) + { + return 0; /* n_at has been checked in MarkTautomerGroups(...) */ + } +/* + if ( 2 != endpoint_valence1 ) + return 0; // accept only O, S, Se +*/ + /* check hydrogens/endpoints */ + nMobile1 = atom[o_at].num_H + (atom[o_at].charge==-1); + if ( bond_type == BOND_SINGLE && !eif1.cDonor && !atom[o_at].endpoint ) + return 0; + /* not needed since nGetEndpointInfo returned non-zero + if ( nMobile1 + atom[o_at].chem_bonds_valence != endpoint_valence1 ) + return 0; + */ + + if ( !(endpoint_valence2=nGetEndpointInfo( atom, n_at, &eif2 ) ) ) { + return 0; /* should not happen here */ + } + nMobile2 = atom[n_at].num_H + (atom[n_at].charge==-1); + + nMobile = 0; + + /* can mobile group move from o_at to n_at? */ + nMobile += (atom[o_at].endpoint || eif1.cDonor) && /* from o_at */ + bond_type != BOND_DOUBLE && + ( atom[n_at].endpoint || /* to n_at */ + eif2.cNeutralBondsValence > atom[n_at].valence ); + /* can mobile group move from n_at to o_at? */ + nMobile += (atom[n_at].endpoint || eif2.cDonor) && /* from n_at */ + (atom[o_at].endpoint || /* to o_at */ + eif1.cNeutralBondsValence > atom[o_at].valence ) && + bond_type != BOND_SINGLE; + + + if ( !nMobile ) + return 0; + /* + num_H = atom[n_at].num_H + atom[o_at].num_H; + num_taut_endpoints = (0!=atom[n_at].endpoint) + (0!=atom[o_at].endpoint); // if O, N already are endpoints + if ( num_H != 1 && num_taut_endpoints != 2 && !(num_H==2 && num_taut_endpoints >= 1) ) { + return 0; + } + */ + /* extract -OH bond */ + nNumBondPosTmp = 0; + + path_bonds[0][0] = path_bonds[1][0] = bond_type; + if ( REPLACE_THE_BOND( bond_type ) ) { + BondPosTmp[nNumBondPosTmp].nAtomNumber = nxt_at; /* accumulate bonds to be */ + BondPosTmp[nNumBondPosTmp].neighbor_index = i; /* marked as tautomeric */ + nNumBondPosTmp += 2; /* leave room for the same bond in the opposite direction */ + } + + /* extract other bonds */ + /* path_bonds[] contents: + + + O OH OH + || | | + / \ // \ / \\ + || || <--> | || <--> || | + \ / \\ / \ // + NH N N + + path[0]: O=NH-=- OH-N... OH.N... + path[1] O=NH-=- OH-N... OH.N... + bonds are all bonds all bonds + single and are either are either + double alt or taut alt or taut + */ + for ( j = 0; j < middle_pos; j ++ ) { + for ( i = 0; i < 2; i ++ ) { + /* k = i? j : middle_pos-1-j; */ + k = i? middle_pos+j : middle_pos-1-j; + /* i=0: from O neighbor i=0: down to N, i=1: up to N */ + bond_type = DfsPath[k].bond_type; + + path_bonds[i][j+1] = bond_type; + if ( REPLACE_THE_BOND( bond_type ) ) { + BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[k].at_no; /* accumulate bonds to be */ + BondPosTmp[nNumBondPosTmp].neighbor_index = DfsPath[k].bond_pos; /* marked as tautomeric */ + nNumBondPosTmp += 2; /* leave room for the same bond in the opposite direction */ + } + } + } + if ( !are_alt_bonds( path_bonds[0], middle_pos+1 ) || !are_alt_bonds( path_bonds[1], middle_pos+1 ) ) { + return 0; + } + + /* finally check whether the bonds allow moving the hydrogens */ + if ( (atom[o_at].endpoint != atom[n_at].endpoint || !atom[o_at].endpoint) ) + { + int nErr; + + nErr = bExistsAnyAltPath( pCG, pBNS, pBD, atom, num_atoms, n_at, o_at, ALT_PATH_MODE_TAUTOM ); + + if ( nErr <= 0 ) + return nErr; + } + + /* save endpoints */ + for ( j = 0; j < 2; j ++ ) { + endpoint = j? n_at : /* =N- 2 */ + o_at; /* -OH 1 */ + if ( !atom[endpoint].endpoint ) { /* not a known endpoint */ + endpoint_valence = j? endpoint_valence2 : endpoint_valence1; + chem_bonds_valence = j? eif2.cNeutralBondsValence : eif1.cNeutralBondsValence; + /* endpoint_valence = get_endpoint_valence( atom[endpoint].el_number ); */ + nMobile = j? nMobile2 : nMobile1; + /* nMobile = (atom[endpoint].charge == -1) + atom[endpoint].num_H; */ + /* if ( nMobile + atom[endpoint].chem_bonds_valence != endpoint_valence ) -- fixed 02-06-2003*/ + if ( nMobile + chem_bonds_valence != endpoint_valence ) + return 0; /* abnormal endpoint valence; ignore. */ + AddAtom2num( EndPointTmp[nNumEndPointTmp].num, atom, endpoint, 2 ); /* fill out */ + AddAtom2DA( EndPointTmp[nNumEndPointTmp].num_DA, atom, endpoint, 2 ); +/* + EndPointTmp[nNumEndPointTmp].num[1] = (atom[endpoint].charge == -1); + EndPointTmp[nNumEndPointTmp].num[0] = nMobile; + for ( m = 0; m < T_NUM_ISOTOPIC; m ++ ) { + EndPointTmp[nNumEndPointTmp].num[T_NUM_NO_ISOTOPIC+m] = atom[endpoint].num_iso_H[NUM_H_ISOTOPES-m-1]; + } +*/ + } else { /* already an endpoint */ /* **now it is wrong:** no mobile atom/charge at this endpoint */ + memset( EndPointTmp + nNumEndPointTmp, 0, sizeof(EndPointTmp[0]) ); + } + EndPointTmp[nNumEndPointTmp].nAtomNumber = endpoint; + EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint; + EndPointTmp[nNumEndPointTmp].nEquNumber = 0; + + nNumEndPointTmp ++; + } + /* add collected tautomeric bonds and endpoints to the input/output data */ + nNumBondPos = AddBondsPos( atom, BondPosTmp, nNumBondPosTmp, BondPos, nMaxNumBondPos, nNumBondPos ); + nNumEndPoint = AddEndPoints( EndPointTmp, nNumEndPointTmp, EndPoint, nMaxNumEndPoint, nNumEndPoint); + + if ( nNumBondPos >= 0 && nNumEndPoint >= 0 ) { + if (ret = (nNumBondPos > *pnNumBondPos) || (nNumEndPoint > *pnNumEndPoint)) { + *pnNumBondPos = nNumBondPos ; + *pnNumEndPoint = nNumEndPoint ; + } + } + return ret; + +#undef PATH_LEN +} + + + +#if ( TAUT_15_NON_RING == 1 ) /* post v.1 feature */ + +/******************************************************************************** +Check (1,5) taut alt path centerpoint (unfinished) [add path checking] +*********************************************************************************/ +int Check15TautPathCenterpoint( inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int jNxtNeigh, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD, int num_atoms ) +{ + int nxt_at = atom[DfsPath[nLenDfsPath].at_no].neighbor[jNxtNeigh]; + /* atom[nxt_at].endpoint below allows for keto-enol -CH< or -CH2- endpoints */ + return atom[nxt_at].endpoint || bIsCenterPointStrict( atom, nxt_at ); +} + +/********************************************************************************/ +/* + 1,5 Tautomerism in general (unfinished) [just a copy from 6-memb case] + + AH--B==C--D==E C may be carbon exhibiting keto-enol tautomerism + 0 1 2 3 4 as well as A or E may be previously detected such a carbon + ^ nxt_at + | + +-- = nLenDfsPath + +*/ + + + +/********************************************************************************/ +/* check if 1,5 tautomeric path has been found */ +int Check15TautPath( struct tagCANON_GLOBALS *pCG, + inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int jNxtNeigh, int nStartAtomNeighbor, + int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, + T_ENDPOINT *EndPoint, int nMaxNumEndPoint, + T_BONDPOS *BondPos, int nMaxNumBondPos, + int *pnNumEndPoint, int *pnNumBondPos, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD, int num_atoms ) +{ +#define PATH_LEN 4 + int i, j, k, /*m,*/ nNumBondPos, nNumEndPoint, cur_at, prv_at, at1, at2 /*, at3, step_at*/; + int nNumEndPointTmp, nNumBondPosTmp, ret; + /* int num_taut_endpoints, num_H; */ + int nMobile, endpoint, endpoint_valence, chem_bonds_valence; + int nMobile1, endpoint_valence1; /* start atom, at1 */ + int nMobile2, endpoint_valence2; /* end atom, at2 */ + /*int nMobile3, endpoint_valence3=-1;*/ /* middle atom, at3 */ + /*int nxt_at;*/ + int alt_bonds[2]; + U_CHAR /*path_bonds[2][PATH_LEN+1],*/ bond_type; + T_ENDPOINT EndPointTmp[2]; + T_BONDPOS BondPosTmp[4*PATH_LEN]; + ENDPOINT_INFO eif1, eif2/*, eif3*/; + + if ( nStartAtomNeighbor >= 0 || nStartAtomNeighbor2 >= 0 || nStartAtomNeighborNeighbor >= 0 ) + return -1; /* wrong call */ + + if ( nLenDfsPath != 3 ) + return -1; /* wrong call */ + + nNumBondPos = *pnNumBondPos; + nNumEndPoint = *pnNumEndPoint; + nNumBondPosTmp = 0; + nNumEndPointTmp = 0; + ret = 0; + +/*-------add the last atom, nLenDfsPath=4 --*/ + j = jNxtNeigh; + prv_at = DfsPath[nLenDfsPath].at_no; + cur_at = atom[prv_at].neighbor[j]; + DfsPath[nLenDfsPath].bond_type = (atom[prv_at].bond_type[j] & ~BOND_MARK_ALL); +#if ( FIX_BOND23_IN_TAUT == 1 ) + DfsPath[nLenDfsPath].bond_type = ACTUAL_ORDER(pBNS,prv_at,j,DfsPath[nLenDfsPath].bond_type); +#endif + DfsPath[nLenDfsPath].bond_pos = j; /* fix index of the bond to the next atom */ + + nLenDfsPath ++; + + DfsPath[nLenDfsPath].at_no = cur_at; + DfsPath[nLenDfsPath].bond_type = 0; + DfsPath[nLenDfsPath].bond_pos = -1; + /*nDfsPathPos[cur_at] = nLenDfsPath+1;*/ /* mark with distance + 1 */ +/*------------------------------------------*/ + at1 = (int)DfsPath[0].at_no; + at2 = (int)DfsPath[nLenDfsPath].at_no; + /*at3 = (int)DfsPath[2].at_no;*/ + if ( atom[at1].endpoint && atom[at1].endpoint == atom[at2].endpoint ) { + /* start & end already belong to the same taut group */ + goto exit_function; /* nothing to do */ + } + + /* check bond types along alt path */ + alt_bonds[0] = alt_bonds[1] = 0; + for( i = 0; i < nLenDfsPath; i ++ ) { + alt_bonds[i%2] |= IS_ALT_OR_DBLBOND(DfsPath[i].bond_type); + } + if ( (alt_bonds[0] & alt_bonds[1] & (BOND_SINGLE | BOND_DOUBLE)) || + (alt_bonds[0] & BOND_WRONG) || (alt_bonds[1] & BOND_WRONG ) ) { + goto exit_function; /* incompatible with alt path or wrong bonds */\ + } + /* check possibly tautomeric endpoints at the ends */ + endpoint_valence1 = nGetEndpointInfo( atom, at1, &eif1 ); + endpoint_valence2 = nGetEndpointInfo( atom, at2, &eif2 ); +#ifdef NEVER /* do not use C-endpoint of keto-enol tautomer to find 1,5 the taut path */ + if ( !endpoint_valence1 && !atom[at1].endpoint || + !endpoint_valence2 && !atom[at2].endpoint ) + goto exit_function; /* at least one of the end atoms cannot be an endpoint */ +#endif + if ( !endpoint_valence1 || !endpoint_valence2 ) + goto exit_function; /* require both endpoints be heteroatoms */ + /* check hydrogens/endpoints */ + nMobile1 = atom[at1].num_H + (atom[at1].charge==-1); + if ( !atom[at1].endpoint ) { + if ( (alt_bonds[0] & BOND_SINGLE) && !eif1.cDonor ) + goto exit_function; + if ( (alt_bonds[0] & BOND_DOUBLE) && !eif1.cAcceptor ) + goto exit_function; + } + nMobile2 = atom[at2].num_H + (atom[at2].charge==-1); + if ( !atom[at2].endpoint ) { + if ( (alt_bonds[1] & BOND_SINGLE) && !eif2.cDonor ) + goto exit_function; + if ( (alt_bonds[1] & BOND_DOUBLE) && !eif2.cAcceptor ) + goto exit_function; + } + + nMobile = 0; + + /* can mobile group move from at1=o_at to at2=n_at? */ + nMobile += (atom[at1].endpoint || eif1.cDonor) && /* from o_at */ + !(alt_bonds[0] & BOND_DOUBLE) && + ( atom[at2].endpoint || /* to n_at */ + eif2.cNeutralBondsValence > atom[at2].valence ); + /* can mobile group move from at2=n_at to at1=o_at? */ + nMobile += (atom[at2].endpoint || eif2.cDonor) && /* from n_at */ + !(alt_bonds[1] & BOND_DOUBLE) && + ( atom[at1].endpoint || /* to o_at */ + eif1.cNeutralBondsValence > atom[at1].valence ); + + + if ( !nMobile ) + goto exit_function; + + /* check whether the bonds allow moving the hydrogens between at1 and at2 */ + if ( (atom[at1].endpoint != atom[at2].endpoint || !atom[at1].endpoint) ) { + int nErr; + nErr = bExistsAnyAltPath( pCG, pBNS, pBD, atom, num_atoms, at1, at2, ALT_PATH_MODE_TAUTOM ); + if ( nErr <= 0 ) { + ret = nErr; + goto exit_function; + } + } + + /* save tautomeric bonds */ + nNumBondPosTmp = 0; + for ( k = 0; k < nLenDfsPath; k ++ ) { + bond_type = DfsPath[k].bond_type; + if ( REPLACE_THE_BOND( bond_type ) ) { + BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[k].at_no; /* accumulate bonds to be */ + BondPosTmp[nNumBondPosTmp].neighbor_index = DfsPath[k].bond_pos; /* marked as tautomeric */ + nNumBondPosTmp += 2; /* leave room for the same bond in opposite direction */ + } + } + /* save endpoints */ + for ( j = 0; j < 2; j ++ ) { + endpoint = j? at2 : at1; + if ( !atom[endpoint].endpoint ) { /* not a known endpoint */ + endpoint_valence = j? endpoint_valence2 : endpoint_valence1; + chem_bonds_valence = j? eif2.cNeutralBondsValence : eif1.cNeutralBondsValence; + /* endpoint_valence = get_endpoint_valence( atom[endpoint].el_number ); */ + nMobile = j? nMobile2 : nMobile1; + /* nMobile = (atom[endpoint].charge == -1) + atom[endpoint].num_H; */ + /* if ( nMobile + atom[endpoint].chem_bonds_valence != endpoint_valence ) -- fixed 02-06-2003*/ + if ( nMobile + chem_bonds_valence != endpoint_valence ) + goto exit_function; /* abnormal endpoint valence; ignore. */ + AddAtom2num( EndPointTmp[nNumEndPointTmp].num, atom, endpoint, 2 ); /* fill out */ + AddAtom2DA( EndPointTmp[nNumEndPointTmp].num_DA, atom, endpoint, 2 ); + } else { /* already an endpoint */ /* **now it is wrong:** no mobile atom/charge at this endpoint */ + memset( EndPointTmp + nNumEndPointTmp, 0, sizeof(EndPointTmp[0]) ); + } + EndPointTmp[nNumEndPointTmp].nAtomNumber = endpoint; + EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint; + EndPointTmp[nNumEndPointTmp].nEquNumber = 0; + + nNumEndPointTmp ++; + } + /* add collected tautomeric bonds and endpoints to the input/output data */ + nNumBondPos = AddBondsPos( atom, BondPosTmp, nNumBondPosTmp, BondPos, nMaxNumBondPos, nNumBondPos ); + nNumEndPoint = AddEndPoints( EndPointTmp, nNumEndPointTmp, EndPoint, nMaxNumEndPoint, nNumEndPoint); + + if ( nNumBondPos >= 0 && nNumEndPoint >= 0 ) { + if (ret = (nNumBondPos > *pnNumBondPos) || (nNumEndPoint > *pnNumEndPoint)) { + *pnNumBondPos = nNumBondPos ; + *pnNumEndPoint = nNumEndPoint ; + } + } + +exit_function: + /*nDfsPathPos[DfsPath[nLenDfsPath].at_no] = 0;*/ + + return ret; + +#undef PATH_LEN +} +#endif /* TAUT_15_NON_RING */ + + + +/********************************************************************************/ +/* + + 1,4 tautomerism in 5-member ring + + + O=N2-C O-N2=C N1 = DfsPath[0].at_no + | \\ | \ N2 = DfsPath[1].at_no + | D <-> | D + | / | // + HO-N1=E HO=N1-E + +*/ + +/********************************************************************************/ +/* check if a tautomeric 5-member ring (pyrazole derivatives) has been found */ +int Check5MembTautRing( struct tagCANON_GLOBALS *pCG, + inp_ATOM *atom, DFS_PATH *DfsPath, int nLenDfsPath, int nStartAtomNeighbor, + int nStartAtomNeighbor2, int nStartAtomNeighborNeighbor, + T_ENDPOINT *EndPoint, int nMaxNumEndPoint, + T_BONDPOS *BondPos, int nMaxNumBondPos, + int *pnNumEndPoint, int *pnNumBondPos, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD, int num_atoms ) +{ +#define PATH_LEN 4 + int i, j, /*m,*/ nMobile, nMobile1, nMobile2; + int num_taut_endpoints, nNumBondPos, nNumBondPosTmp, nNumEndPoint, nNumEndPointTmp, ret; + int endpoint; + int n1_at = (int)DfsPath[0].at_no; + int n2_at = (int)DfsPath[1].at_no; + U_CHAR path_bonds[PATH_LEN+1], bond_type; + T_ENDPOINT EndPointTmp[2]; + T_BONDPOS BondPosTmp[2*PATH_LEN]; + ENDPOINT_INFO eif1, eif2; + + /* the two root atoms (atom[n1_at] and atom[n2_at]) cannot belong */ + /* to one and the same tautomeric group: it has been verified in MarkTautomerGroups() */ + + /* check hydrogens/endpoints */ + if ( nLenDfsPath != 4 ) { + return 0; /* program error */ + } + if ( nStartAtomNeighbor2 >= 0 || nStartAtomNeighborNeighbor >= 0 ) + return 0; /* program error: wrong call */ + + nNumBondPos = *pnNumBondPos; + nNumEndPoint = *pnNumEndPoint; + nNumEndPointTmp = 0; + nNumBondPosTmp = 0; + ret = 0; + + if ( !nGetEndpointInfo( atom, n1_at, &eif1 ) || + !nGetEndpointInfo( atom, n2_at, &eif2 ) ) { + return 0; + } + + nMobile1 = atom[n1_at].num_H + (atom[n1_at].charge==-1); + nMobile2 = atom[n2_at].num_H + (atom[n2_at].charge==-1); + nMobile = nMobile1 + nMobile2; + num_taut_endpoints = (0!=atom[n1_at].endpoint) + (0!=atom[n2_at].endpoint); /* if both N atoms already are endpoints */ + /* + if ( !(nMobile == 1 || num_taut_endpoints == 2) && !(nMobile>1 && num_taut_endpoints >= 1) ) { + return 0; + } + */ + if ( num_taut_endpoints == 0 && nMobile != 1 ) { + return 0; + } + + /* finally check whether the bonds allow moving the hydrogens */ + if ( (atom[n1_at].endpoint != atom[n2_at].endpoint || !atom[n1_at].endpoint) ) { + int nErr; + nErr = bExistsAnyAltPath( pCG, pBNS, pBD, atom, num_atoms, n1_at, n2_at, ALT_PATH_MODE_TAUTOM ); + if ( nErr <= 0 ) + return nErr; + } + + /* save endpoints */ + for ( j = 0; j < 2; j ++ ) { + endpoint = j? n1_at : n2_at; + if ( !atom[endpoint].endpoint ) { /* not a known endpoint */ +/* + nMobile = (atom[endpoint].charge == -1) + atom[endpoint].num_H; + } else { + nMobile = 0; + } + if ( nMobile ) { +*/ + AddAtom2num( EndPointTmp[nNumEndPointTmp].num, atom, endpoint, 2 ); /* fill out */ + AddAtom2DA( EndPointTmp[nNumEndPointTmp].num_DA, atom, endpoint, 2 ); + /* + EndPointTmp[nNumEndPointTmp].num[1] = (atom[endpoint].charge == -1); + EndPointTmp[nNumEndPointTmp].num[0] = nMobile; + for ( m = 0; m < T_NUM_ISOTOPIC; m ++ ) { + EndPointTmp[nNumEndPointTmp].num[T_NUM_NO_ISOTOPIC+m] = atom[endpoint].num_iso_H[NUM_H_ISOTOPES-m-1]; + } + */ + } else { + memset( EndPointTmp + nNumEndPointTmp, 0, sizeof(EndPointTmp[0]) ); + } + EndPointTmp[nNumEndPointTmp].nAtomNumber = endpoint; + EndPointTmp[nNumEndPointTmp].nGroupNumber = atom[endpoint].endpoint; + EndPointTmp[nNumEndPointTmp].nEquNumber = 0; + + nNumEndPointTmp ++; + } + + /* extract bonds */ + nNumBondPosTmp = 0; + for ( i = 1; i <= nLenDfsPath; i ++ ) { + bond_type = DfsPath[i].bond_type; + path_bonds[i-1] = bond_type; + if ( REPLACE_THE_BOND( bond_type ) ) { + BondPosTmp[nNumBondPosTmp].nAtomNumber = DfsPath[i].at_no; + BondPosTmp[nNumBondPosTmp].neighbor_index = DfsPath[i].bond_pos; + nNumBondPosTmp += 2; + } + } + /* path_bonds is from at_n2 to at_n1 */ + if ( !(i=are_alt_bonds( path_bonds, nLenDfsPath )) ) { + return 0; + } + /* i is a bond type of the last bond to at_n1, the first bond from at_n2 is 2-i if i=1 or 2 */ + + /* single bond at n1_at: it should have a mobile atom, n2_at should not */ + if ( i == BOND_SINGLE && (!atom[n1_at].endpoint && !eif1.cDonor || !atom[n2_at].endpoint && !eif2.cAcceptor ) || + /* double bond at n1_at: it should not have a mobile atom, n2_at should */ + i == BOND_DOUBLE && (!atom[n1_at].endpoint && !eif1.cAcceptor || !atom[n2_at].endpoint && !eif2.cDonor) ) { + return 0; /* bond pattern does not fit */ + } + + nNumBondPos = AddBondsPos( atom, BondPosTmp, nNumBondPosTmp, BondPos, nMaxNumBondPos, nNumBondPos ); + nNumEndPoint = AddEndPoints( EndPointTmp, nNumEndPointTmp, EndPoint, nMaxNumEndPoint, nNumEndPoint); + + if ( nNumBondPos >= 0 && nNumEndPoint >= 0 ) { + if (ret = (nNumBondPos > *pnNumBondPos) || (nNumEndPoint > *pnNumEndPoint)) { + *pnNumBondPos = nNumBondPos ; + *pnNumEndPoint = nNumEndPoint ; + } + } + return ret; + +#undef PATH_LEN +} +#endif /* } */ diff --git a/INCHI-1-SRC/INCHI/common/ichiread.c b/INCHI-1-SRC/INCHI_BASE/src/ichiread.c similarity index 77% rename from INCHI-1-SRC/INCHI/common/ichiread.c rename to INCHI-1-SRC/INCHI_BASE/src/ichiread.c index 99e9d58..ecefd27 100644 --- a/INCHI-1-SRC/INCHI/common/ichiread.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichiread.c @@ -1,8139 +1,9654 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include - -/* #define CHECK_WIN32_VC_HEAP */ -#include "mode.h" - -#if ( READ_INCHI_STRING == 1 ) - -#include "ichicomp.h" -#include "ichi.h" -#include "ichitime.h" -#include "util.h" -#include "strutil.h" -#include "ichi_io.h" - -/* reverse InChI */ -#include "ichimain.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichister.h" -#include "strutil.h" -#include "ichisize.h" -#include "ichiring.h" -#include "ichinorm.h" -#include "ichierr.h" - -#include "ichirvrs.h" - - -/*^^^ */ -#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_STANDALONE) ) -#include "inchi_api.h" -#endif -/*^^^ */ - - - -typedef struct tagLine -{ - char *str; - int len; - int len_alloc; - int c; -} SEGM_LINE; -#define SEGM_LINE_ADD 128 - -typedef struct tagOneLinkedBond -{ - AT_NUMB neigh; /* canonical number of a neighbor */ - AT_NUMB prev; /* position of the previous neighbor in the list */ -} ONE_LINKED_BOND; - -typedef struct tagLinkedBonds -{ - ONE_LINKED_BOND *pBond; - int len; - int len_alloc; -}LINKED_BONDS; -#define LINKED_BOND_ADD 128 - -typedef enum tagModeProtonIsoExchgH -{ - MODE_PIXH_UNDEFINED, /* 0 */ - MODE_PIXH_ADD_TO_FIRST, /* 1 */ - MODE_PIXH_ADD_TO_EACH, /* 2 */ - MODE_PIXH_ADD_A_PIXH_COMPONENT, /* 3 */ - MODE_PIXH_KEEP_TOTALS /* 4 */ -} MODE_PIXH; - - -/* local prototypes */ -static int GetInChIFormulaNumH(INChI *pInChI, int *nNumH); -static int GetInChINumH(INChI *pInChI, int *nNumH); -static int GetInChIIsoH(INChI *pInChI, int nNumIsotopicH[NUM_H_ISOTOPES]); - -static int getInChIChar(INCHI_IOSTREAM *pInp); -static int AddInChIChar(INCHI_IOSTREAM *pInp, SEGM_LINE *Line, const char *pszToken); -static int AddLinkedBond(AT_NUMB at1, AT_NUMB at2, AT_NUMB num_at, LINKED_BONDS *pLB); -static int bInChIHasReconnectedMetal(INChI *pInChI); -static int SetProtonsAndXchgIsoH(int bInChI2Structure, - int bReqSplitOutputInChI, - int bReqProtonsForEachComponent, - int bReqNonTaut, int bReqStereo, - int num_components[INCHI_NUM], - MODE_PIXH nModeProtonIsoExchgH[INCHI_NUM], - InpInChI *OneInput); -#if ( FIX_DALKE_BUGS == 1 ) -static int SetHillFormFromInChI(InpInChI *OneInput); -#endif - -static int nGetInChISegment(INCHI_IOSTREAM *pInp, SEGM_LINE *Line, const char *pszToken); - -static int CopySegment(INChI *pInChITo, INChI *pInChIFrom, int StereoType, - int bIsotopicTo, int bIsotopicFrom); -static int nFillOutProtonMobileH(INChI *pInChI); -static int nProtonCopyIsotopicInfo(INChI *pInChI_to, INChI *pInChI_from); -static int CopyAtomNumbers(INChI *pInChI_To, int bIsoTo, INChI *pInChI_From, int bIsoFrom); - -static int ParseSegmentFormula(const char *str, int bMobileH, INChI *pInpInChI[], - int nNumComponents[]); -static int ParseSegmentConnections(const char *str, int bMobileH, INChI **pInpInChI, - int *pnNumComponents, int *pbAbc); -static int ParseSegmentMobileH(const char *str, int bMobileH, - INChI *pInpInChI[], int pnNumComponents[], int *pbAbc); -static int ParseSegmentCharge(const char *str, int bMobileH, - INChI *pInpInChI[], int nNumComponents[]); -static int ParseSegmentProtons(const char *str, int bMobileH, - REM_PROTONS nNumProtons[], int nNumComponents[]); -static int ParseSegmentSp2(const char *str, int bMobileH, - INChI *pInpInChI[], int nNumComponents[], int state, int *pbAbc); -static int ParseSegmentSp3(const char *str, int bMobileH, - INChI *pInpInChI[], int nNumComponents[], int state, int *pbAbc); -static int ParseSegmentSp3m(const char *str, int bMobileH, - INChI *pInpInChI[], int nNumComponents[], int state); -static int bIsSp3LayerNotEmpty(INChI *pInpInChI[], int bMobileH, - int bIso, int nNumComponents); -static int ParseSegmentSp3s(const char *str, int bMobileH, - INChI *pInpInChI[], int s[TAUT_NUM][2], int ppnNumComponents[], int state); -static int ParseSegmentIsoAtoms(const char *str, int bMobileH, INChI *pInpInChI[], - int nNumComponents[], int state, int *pbAbc); -static int ParseSegmentIsoExchgH(const char *str, int bMobileH, REM_PROTONS nNumProtons[], - int nNumComponents[], int state, int *pbAbc); -static int ParseSegmentPerm(const char *str, int bMobileH, INChI *pInpInChI[], - int ppnNumComponents[], int state, int *pbAbc); -#if ( FIX_ISO_FIXEDH_BUG_READ == 1 ) -static int bIsoMayBeArranged(int bInchi2Struct, int iso_diff[NUM_H_ISOTOPES], - REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM], - INChI *pInpInChI[INCHI_NUM][TAUT_NUM], int nNumComponents[INCHI_NUM][TAUT_NUM], int iINChI); -#endif - -static int ReadInChILine(INCHI_IOSTREAM *pInp, SEGM_LINE *pLine, - char **pStr, int *pState, - INChI *pInpInChI[INCHI_NUM][TAUT_NUM], - int nNumComponents[INCHI_NUM][TAUT_NUM], - REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM], - int s[INCHI_NUM][TAUT_NUM][2], - int *bStdFormat, - int *bInputHasSaveOpt, unsigned char *inp_save_opt_bits); - -int InChILine2Data(INCHI_IOSTREAM *pInp, SEGM_LINE *pLine, - char **pStr, int *pState, int *nErr, - INChI *pInpInChI[INCHI_NUM][TAUT_NUM], - int nNumComponents[INCHI_NUM][TAUT_NUM], - REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM], - int s[INCHI_NUM][TAUT_NUM][2], - int bReadCoord, int bInchi2Struct, - INCHI_MODE nMode, - int *bStdFormat, - int *bInputHasSaveOpt, unsigned char *inp_save_opt_bits); - -static int ReadInChICoord(INCHI_IOSTREAM *pInp, SEGM_LINE *pLine, int *pState, - INChI *pInpInChI[INCHI_NUM][TAUT_NUM], - int nNumComponents[INCHI_NUM][TAUT_NUM]); - -static int OutputInChIAsRequested(INCHI_IOSTREAM *pOut, INCHI_IOSTREAM *pLog, - ICHICONST INPUT_PARMS *ip_inp, - STRUCT_DATA *sd_inp, - InpInChI *OneInput, - int num_components[INCHI_NUM], - MODE_PIXH nModeProtonIsoExchgH[INCHI_NUM], - long num_inp, unsigned char save_opt_bits); - -static int ParseAuxSegmentVersion(const char *str, int bMobileH, INChI *pInpInChI[], - int ppnNumComponents[], int state); -static int ParseAuxSegmentNumbers(const char *str, int bMobileH, INChI *pInpInChI[], - int ppnNumComponents[], int state, int *pbAbc); -static int ParseAuxSegmentAtomEqu(const char *str, int bMobileH, INChI *pInpInChI[], - int ppnNumComponents[], int state); -static int ParseAuxSegmentGroupEqu(const char *str, int bMobileH, INChI *pInpInChI[], - int ppnNumComponents[], int state); -static int ParseAuxSegmentSp3Inv(const char *str, int bMobileH, INChI *pInpInChI[], - int ppnNumComponents[], int state); -static int ParseAuxSegmentSp3InvNumbers(const char *str, int bMobileH, INChI *pInpInChI[], - int ppnNumComponents[], int state); -static int ParseAuxSegmentReverseCRV(const char *str, int bMobileH, INChI *pInpInChI[], - int ppnNumComponents[], int state); -static int ParseAuxSegmentReverseAtoms(const char *str, int bMobileH, INChI *pInpInChI[], - int ppnNumComponents[], int state); -static int ParseAuxSegmentReverseBonds(const char *str, int bMobileH, INChI *pInpInChI[], - int ppnNumComponents[], int state); -static int ParseAuxSegmentReverseXYZ(const char *str, int bMobileH, XYZ_COORD **ppXYZ, - INChI *pInpInChI[], int ppnNumComponents[], int state); -static int AddAuxSegmentCoord(int nRet, XYZ_COORD *pXYZ, int nLenXYZ, - INChI *pInpInChI[INCHI_NUM][TAUT_NUM], - int nNumComponents[INCHI_NUM][TAUT_NUM]); - -static const char *getInchiStateReadErr(int stat); -static const char *getInchiErrName(int nErr); - -#define SEG_END '/' -/* the following 2 definitions are used to allow tab-delimited InChI input - 2008-11-17 DT */ -#define INCHI_INP_EOL(X) ((X)=='\n' || (X)=='\r' || (X)=='\t') -/*#define INCHI_TOKEN "/\n\r\t"*/ -#define INCHI_TOKEN "/\n\r\t\\" - -typedef enum tagInChI_STATE -{ - /* M */ - IST_MOBILE_H_FORMULA, /* 0 */ - IST_MOBILE_H_CONNECTIONS, /* 1 */ - IST_MOBILE_H, /* 2 */ - IST_MOBILE_H_CHARGE, /* 3 */ - IST_MOBILE_H_PROTONS, /* 4 */ - IST_MOBILE_H_SP2, /* 5 */ - IST_MOBILE_H_SP3, /* 6 */ - IST_MOBILE_H_SP3_M, /* 7 */ - IST_MOBILE_H_SP3_S, /* 8 */ - - /* Fork */ - IST_MOBILE_H_ISO_LAYER_FORK, /* 9 */ - - /* MI */ - IST_MOBILE_H_ISO_ATOMS, /* 10 */ - IST_MOBILE_H_ISO_EXCH_H, /* 11 */ - IST_MOBILE_H_ISO_SP2, /* 12 */ - IST_MOBILE_H_ISO_SP3, /* 13 */ - IST_MOBILE_H_ISO_SP3_M, /* 14 */ - IST_MOBILE_H_ISO_SP3_S, /* 15 */ - - /* Fork */ - IST_FIXED_H_LAYER_FORK, /* 16 */ - - /* F */ - IST_FIXED_H_FORMULA, /* 17 */ - IST_FIXED_H, /* 18 */ - IST_FIXED_H_CHARGE, /* 19 */ - IST_FIXED_H_SP2, /* 20 */ - IST_FIXED_H_SP3, /* 21 */ - IST_FIXED_H_SP3_M, /* 22 */ - IST_FIXED_H_SP3_S, /* 23 */ - IST_FIXED_H_PERMUTATION, /* 24 */ - - /* Fork */ - IST_FIXED_H_ISO_LAYER_FORK, /* 25 */ - - /* FI */ - IST_FIXED_H_ISO_ATOMS, /* 26 */ - IST_FIXED_H_ISO_LAYER, /* 27 */ - IST_FIXED_H_ISO_SP2, /* 28 */ - IST_FIXED_H_ISO_SP3, /* 29 */ - IST_FIXED_H_ISO_SP3_M, /* 30 */ - IST_FIXED_H_ISO_SP3_S, /* 31 */ - IST_FIXED_H_ISO_PERMUTATION, /* 32 */ - - /* Reconnected */ - IST_RECONNECTED_LAYER_FORK, /* 33 */ - IST_RECONNECTED_FORMULA, /* 34 */ - - /* Other reading errors */ - IST_MATERIAL_BALANCE_ERROR, /* 35 */ - - IST_END = -1 -}INCHI_STATE; - -#define IST_HAPPENED_IN_RECMET 100 - -typedef struct tagInchiReadErrMsg -{ - int stat; - const char *msg; -} INCHI_READ_ERR_MSG; - -ICHICONST INCHI_READ_ERR_MSG irErrMsg[] = -{ - /* M */ - {IST_MOBILE_H_FORMULA, "MOBILE_H_FORMULA" }, - {IST_MOBILE_H_CONNECTIONS, "MOBILE_H_CONNECTIONS" }, - {IST_MOBILE_H, "MOBILE_H" }, - {IST_MOBILE_H_CHARGE, "MOBILE_H_CHARGE" }, - {IST_MOBILE_H_PROTONS, "MOBILE_H_PROTONS" }, - {IST_MOBILE_H_SP2, "MOBILE_H_SP2" }, - {IST_MOBILE_H_SP3, "MOBILE_H_SP3" }, - {IST_MOBILE_H_SP3_M, "MOBILE_H_SP3_/m" }, - {IST_MOBILE_H_SP3_S, "MOBILE_H_SP3_/s" }, - - /* Fork */ - {IST_MOBILE_H_ISO_LAYER_FORK, "MOBILE_H_ISO_LAYER_FORK" }, - - /* MI */ - {IST_MOBILE_H_ISO_ATOMS, "MOBILE_H_ISO_ATOMS" }, - {IST_MOBILE_H_ISO_EXCH_H, "MOBILE_H_ISO_EXCH_H" }, - {IST_MOBILE_H_ISO_SP2, "MOBILE_H_ISO_SP2" }, - {IST_MOBILE_H_ISO_SP3, "MOBILE_H_ISO_SP3" }, - {IST_MOBILE_H_ISO_SP3_M, "MOBILE_H_ISO_SP3_/m" }, - {IST_MOBILE_H_ISO_SP3_S, "MOBILE_H_ISO_SP3_/s" }, - - /* Fork */ - {IST_FIXED_H_LAYER_FORK, "FIXED_H_LAYER_FORK" }, - - /* F */ - {IST_FIXED_H_FORMULA, "FIXED_H_FORMULA" }, - {IST_FIXED_H, "FIXED_H" }, - {IST_FIXED_H_CHARGE, "FIXED_H_CHARGE" }, - {IST_FIXED_H_SP2, "FIXED_H_SP2" }, - {IST_FIXED_H_SP3, "FIXED_H_SP3" }, - {IST_FIXED_H_SP3_M, "FIXED_H_SP3_/m" }, - {IST_FIXED_H_SP3_S, "FIXED_H_SP3_/s" }, - {IST_FIXED_H_PERMUTATION, "FIXED_H_PERMUTATION" }, - - /* Fork */ - {IST_FIXED_H_ISO_LAYER_FORK, "FIXED_H_ISO_LAYER_FORK" }, - - /* FI */ - {IST_FIXED_H_ISO_ATOMS, "FIXED_H_ISO_ATOMS" }, - {IST_FIXED_H_ISO_LAYER, "FIXED_H_ISO_LAYER" }, - {IST_FIXED_H_ISO_SP2, "FIXED_H_ISO_SP2" }, - {IST_FIXED_H_ISO_SP3, "FIXED_H_ISO_SP3" }, - {IST_FIXED_H_ISO_SP3_M, "FIXED_H_ISO_SP3_m" }, - {IST_FIXED_H_ISO_SP3_S, "FIXED_H_ISO_SP3_s" }, - {IST_FIXED_H_ISO_PERMUTATION, "FIXED_H_ISO_PERMUTATION" }, - - /* Reconnected */ - {IST_RECONNECTED_LAYER_FORK, "RECONNECTED_LAYER_FORK" }, - {IST_RECONNECTED_FORMULA, "RECONNECTED_FORMULA" }, - - {IST_MATERIAL_BALANCE_ERROR, "MATERIAL_BALANCE" }, - - {IST_END, "Unknown Error" } -}; - - - -typedef enum tagCopySegmentType -{ - CPY_SP2, - CPY_SP3, - CPY_SP3_M, - CPY_SP3_S, - CPY_ISO_AT -} COPY_SEG_TYPE; - -#define NSTRLEN 64000 -#define MAX_MSG_LEN 512 -#define MAX_MSG_BUF_LEN 128 - - -/*************************************************************************************/ -const char *getInchiStateReadErr(int stat) -{ - int i, bRecMet = 0; - static char szMsg[128]; - if ( stat >= IST_HAPPENED_IN_RECMET ) - { - bRecMet = 1; - stat -= IST_HAPPENED_IN_RECMET; - } - for ( i = 0; 0 <= irErrMsg[i].stat && stat != irErrMsg[i].stat; i ++ ) - ; - sprintf(szMsg, -#if ( FIX_DALKE_BUGS == 1 ) - "%s%.100s", -#else - "%s%s", -#endif - irErrMsg[i].msg, bRecMet? ", Reconnected layer" : ""); - return szMsg; -} - -/**************************************************************************************/ -const char *getInchiErrName(int nErr) -{ - switch ( nErr ) - { - case RI_ERR_ALLOC: - return "Allocation failed"; - case RI_ERR_PROGR: - return "Program error"; - case RI_ERR_SYNTAX: - return "Syntax error"; - case RI_ERR_EOL: - return "End of line"; - } - return "Unknown error"; -} -#if ( FIX_DALKE_BUGS == 1 ) -/*****************************************************************************************/ -int SetHillFormFromInChI(InpInChI *OneInput) -{ - int iINChI, iTaut, iComp, num_diff; - INChI *pINChI; - char *szHillFormulaOld; - for ( iINChI = 0, num_diff = 0; iINChI < INCHI_NUM; iINChI ++ ) - { - for ( iTaut = TAUT_NON; iTaut < TAUT_NUM; iTaut ++ ) - { - for ( iComp = 0; iComp < OneInput->nNumComponents[iINChI][iTaut]; iComp ++ ) - { - pINChI = &OneInput->pInpInChI[iINChI][iTaut][iComp]; - if ( !pINChI->nNumberOfAtoms || pINChI->bDeleted || !pINChI->szHillFormula || !pINChI->szHillFormula[0] ) - { - continue; - } - szHillFormulaOld = pINChI->szHillFormula; - pINChI->szHillFormula = AllocateAndFillHillFormula(pINChI); - num_diff += !pINChI->szHillFormula || !pINChI->szHillFormula[0] || strcmp(pINChI->szHillFormula, szHillFormulaOld); - inchi_free(szHillFormulaOld); - } - } - } - return num_diff; -} -#endif - - - -/********************** main entry point **********************************************/ - -int ReadWriteInChI(INCHI_IOSTREAM *pInp, INCHI_IOSTREAM *pOut, INCHI_IOSTREAM *pLog, - INPUT_PARMS *ip_inp, - STRUCT_DATA *sd_inp, - /* the following are InChI library-specific parameters */ - inp_ATOM **at, int *num_at, - char *szMsg, int nMsgLen, unsigned long WarningFlags[2][2]) -{ - InpInChI OneInput; - int i, j, nReadStatus, ret, nErr, iINChI; - char *strHdr=NULL; - char *szCurHdr = NULL; - int num_components[INCHI_NUM]; - int bReqNonTaut = (0 != ((ip_inp->nMode & REQ_MODE_BASIC) && - (ip_inp->nMode & REQ_MODE_TAUT))); - /* - int bReqRecmet = (0 != ((ip->bTautFlags & TG_FLAG_RECONNECT_COORD) && - (ip->bTautFlags & TG_FLAG_DISCONNECT_COORD))); - */ - int bReqStereo = (0 != (ip_inp->nMode & REQ_MODE_STEREO)); - int bHasSomeReconnected = 0, bHasSomeFixedH = 0, bHasMetal = 0; - int nModeFlagsStereo = 0, bTautFlags = 0; /* InChI creation flags modifications derived from current InChI */ - MODE_PIXH nModeProtonIsoExchgH[INCHI_NUM]; - - NORM_CANON_FLAGS ncFlags; - NORM_CANON_FLAGS *pncFlags = &ncFlags; - INPUT_PARMS ip_cur, *ip; - STRUCT_DATA sd_cur, *sd; - int nMessageLen = MAX_MSG_LEN; - char szMessage[MAX_MSG_LEN]; - int nInitLenMessage; - - int pState, bStereoType; - int bReqProtonsForEachComponent = 0; - int bReqSplitOutputInChI = 0; - SEGM_LINE Line; - SEGM_LINE *pLine = &Line; - long ulProcessingTime = 0; - inchiTime ulTStart; - long num_processed = 0, num_errors = 0; - int bPlainTabbedOutput; - const char *pTAB; - -#ifdef TARGET_API_LIB - const int bInChI2Structure = 0 != (ip_inp->bReadInChIOptions & READ_INCHI_TO_STRUCTURE); - const int bInChI2InChI = 0 != (ip_inp->bReadInChIOptions & READ_INCHI_OUTPUT_INCHI); -#else - const int bInChI2Structure = 0 != (ip_inp->bReadInChIOptions & READ_INCHI_TO_STRUCTURE); - const int bInChI2InChI = 0 != (ip_inp->bReadInChIOptions & READ_INCHI_OUTPUT_INCHI); -#endif - const int bReadCoord = bInChI2Structure; - long num_inp=0; - - int bInputInStdFormat=0; - int bInputHasSaveOpt=0; - unsigned char inp_save_opt_bits=0; - unsigned char save_opt_bits=0; - - ret = 0; - nReadStatus = RI_ERR_EOL; - - memset(szMessage, 0, sizeof(szMessage)); - memset(&OneInput, 0, sizeof(OneInput)); - memset(pLine, 0, sizeof(pLine[0])); - if ( szMsg ) - szMsg[0] = '\0'; - - - while( nReadStatus != RI_ERR_EOF ) - { - - for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) - { - for ( j = 0; j < TAUT_NUM; j ++ ) - { - if ( OneInput.nNumProtons[iINChI][j].pNumProtons ) - { - inchi_free(OneInput.nNumProtons[iINChI][j].pNumProtons); - OneInput.nNumProtons[iINChI][j].pNumProtons = NULL; - } - } - } - - memset(&OneInput, 0, sizeof(OneInput)); - memset(pncFlags, 0, sizeof(*pncFlags)); - bStereoType = 0; - ip_cur = *ip_inp; - ip = &ip_cur; - sd_cur = *sd_inp; - sd = &sd_cur; - bReqSplitOutputInChI = 0 != (ip->bReadInChIOptions & READ_INCHI_SPLIT_OUTPUT); - bReqProtonsForEachComponent = bReqSplitOutputInChI && - 0 != (READ_INCHI_KEEP_BALANCE_P & ip->bReadInChIOptions); - - bPlainTabbedOutput = 0 != (ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT); -#if ( !defined(TARGET_API_LIB) && !defined(TARGET_LIB_FOR_WINCHI) ) - pTAB = bPlainTabbedOutput? "\t" : "\n"; -#else - pTAB = "\n"; -#endif - - - if ( bInChI2Structure ) - { -#if ( bRELEASE_VERSION == 1 ) - bReqNonTaut = 1; /* bReqNonTaut=0 ignores Fixed-H layer in input InChI, for testing only */ -#endif - /* bReqRecmet = 1; */ - bReqStereo = 1; - bReqSplitOutputInChI = 1; - bReqProtonsForEachComponent = bReqNonTaut; - ip->bTautFlags |= (TG_FLAG_DISCONNECT_COORD | TG_FLAG_RECONNECT_COORD); - ip->nMode |= (REQ_MODE_BASIC | REQ_MODE_TAUT | REQ_MODE_STEREO | REQ_MODE_ISO_STEREO | REQ_MODE_ISO); - } - - - /************************************************************************/ - /* Read InChI string */ - /************************************************************************/ - InchiTimeGet(&ulTStart); - - - nReadStatus = InChILine2Data(pInp, pLine, &strHdr, &pState, &nErr, OneInput.pInpInChI, - OneInput.nNumComponents, OneInput.nNumProtons, - OneInput.s, bReadCoord, bInChI2Structure, - ip_inp->nMode, - &bInputInStdFormat,&bInputHasSaveOpt,&inp_save_opt_bits); - - - - ulProcessingTime += InchiTimeElapsed(&ulTStart); - - - if ( (nReadStatus == RI_ERR_EOL || nReadStatus == RI_ERR_EOF) && !nErr && - OneInput.nNumComponents[INCHI_BAS][TAUT_YES] - + OneInput.nNumComponents[INCHI_BAS][TAUT_NON] ) - { - /* InChI has been successfully read */ - - ret = 0; - num_inp ++; - bHasSomeReconnected = 0; - bHasSomeFixedH = 0; - - /* Does not allow conversion non-standard->standard */ - /* (force target to be non-standard also) */ - if ( ip_inp->bINChIOutputOptions & INCHI_OUT_STDINCHI ) - { - if ( !bInputInStdFormat ) /* Input InChI is a non-standard one */ - { - ip->bINChIOutputOptions &= ~INCHI_OUT_STDINCHI; - if ( szCurHdr && szCurHdr[0] ) - inchi_ios_eprint( pLog, "Warning: forced conversion to non-standard InChI for non-std input, %s\n", szCurHdr ); - else - inchi_ios_eprint( pLog, "Warning: forced conversion to non-standard InChI for non-std input, Structure %ld\n", num_inp ); - } - } - - if ( ip->bINChIOutputOptions & INCHI_OUT_SAVEOPT ) - { - if (!bInputHasSaveOpt) - { - /* Does not allow to create SaveOpt if the source lacks appendix */ - ip->bINChIOutputOptions &= ~INCHI_OUT_SAVEOPT; - if ( szCurHdr && szCurHdr[0] ) - inchi_ios_eprint( pLog, "Warning: ignore SaveOpt request for SaveOpt-less input, %s\n", szCurHdr ); - else - inchi_ios_eprint( pLog, "Warning: ignore SaveOpt request for SaveOpt-less input, Structure %ld\n", num_inp ); - } - else - { - /* Analyze existing and prepare new SaveOpt appendix */ - - if ( 0 != ( ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) - { - /* RecMet requested */ - if ( 0 != (inp_save_opt_bits & SAVE_OPT_RECMET) ) - { - save_opt_bits |= SAVE_OPT_RECMET; - } - else - { - ip->bTautFlags &= ~TG_FLAG_RECONNECT_COORD; - if ( szCurHdr && szCurHdr[0] ) - inchi_ios_eprint( pLog, "Warning: input created w/o RecMet - ignoring RecMet request, %s\n", szCurHdr ); - else - inchi_ios_eprint( pLog, "Warning: input created w/o RecMet - ignoring RecMet request, Structure %ld\n", num_inp ); - } - } - if ( 0 != (ip->nMode & REQ_MODE_BASIC) ) - { - /* FixedH requested */ - if ( 0 != (inp_save_opt_bits & SAVE_OPT_FIXEDH) ) - save_opt_bits |= SAVE_OPT_FIXEDH; - else - { - ip->nMode &= ~REQ_MODE_BASIC; - if ( szCurHdr && szCurHdr[0] ) - inchi_ios_eprint( pLog, "Warning: input created w/o FixedH - ignoring FixedH request, %s\n", szCurHdr ); - else - inchi_ios_eprint( pLog, "Warning: input created w/o FixedH - ignoring FixedH request, Structure %ld\n", num_inp ); - } - } - /* Copy from source SaveOpt the bits which we do not touch */ - /* while converting InChI: SUU SLUUD KET 15T */ - if ( 0 != ( inp_save_opt_bits & SAVE_OPT_SUU) ) - save_opt_bits |= SAVE_OPT_SUU; - if ( 0 != ( inp_save_opt_bits & SAVE_OPT_SLUUD) ) - save_opt_bits |= SAVE_OPT_SLUUD; - if ( 0 != ( inp_save_opt_bits & SAVE_OPT_KET) ) - save_opt_bits |= SAVE_OPT_KET; - if ( 0 != ( inp_save_opt_bits & SAVE_OPT_15T) ) - save_opt_bits |= SAVE_OPT_15T; - /* Check if /SNon requested and turn OFF stereo bits if so */ - if ( ! (ip->nMode & REQ_MODE_STEREO) ) - { - save_opt_bits &= ~SAVE_OPT_SUU; - save_opt_bits &= ~SAVE_OPT_SLUUD; - } - } - } - - - -#ifndef TARGET_API_LIB - /* - inchi_ios_eprint(stderr, "%ld: %s\r", num_inp, strHdr? strHdr : ""); - inchi_ios_eprint(pLog, "%ld: %s\n", num_inp, strHdr? strHdr : ""); - */ - - if ( !ip->bNoStructLabels && !(bInChI2Structure && (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY)) ) - { - /* Added 2nd item: Do not output this extra line into the output SDfile. 2008-11-17 DCh */ - if ( strHdr && strstr(strHdr, "Structure:")) - { - inchi_ios_print(pOut, "%s%s", strHdr, pTAB); /* output header */ -#if ( FIX_DALKE_BUGS == 1 ) -#else - sprintf(szMessage, "%s (%ld)", strHdr? strHdr : "", num_inp); -#endif - } - else - { - /* - inchi_ios_print(pOut, "Structure %ld (%s)%s", num_inp, strHdr? strHdr : "", pTAB); - sprintf(szMessage, "Structure %ld (%s)%s", num_inp, strHdr? strHdr : "" , pTAB); - */ - inchi_ios_print(pOut, "Structure: %ld. (%s)%s", num_inp, strHdr? strHdr : "No struct name" , pTAB); /* output header */ -#if ( FIX_DALKE_BUGS == 1 ) -#else - sprintf(szMessage, "Structure: %ld. (%s)%s", num_inp, strHdr? strHdr : "No struct name", pTAB); -#endif - } - if ( strHdr && strHdr[0] ) - { - strncpy(ip->szSdfDataHeader, strHdr, sizeof(ip->szSdfDataHeader)); - ip->szSdfDataHeader[sizeof(ip->szSdfDataHeader)-1] = '\0'; - ip->pSdfLabel = NULL; - ip->pSdfValue = ip->szSdfDataHeader; - } - else - { - ip->pSdfValue = NULL; - ip->szSdfDataHeader[0] = '\0'; - } - } - -#if ( FIX_DALKE_BUGS == 1 ) - sprintf(szMessage, "%ld: %.400s", num_inp, strHdr? strHdr : ""); -#else - sprintf(szMessage, "%ld: %s", num_inp, strHdr? strHdr : ""); -#endif -#endif - - nInitLenMessage = strlen(szMessage); - if ( strHdr ) - { - szCurHdr = strHdr; - strHdr = NULL; - } - if ( szCurHdr && ip && ip->first_struct_number > 0 ) - { - /* check whether the structure should be skipped */ - static char szStruct[] = "Structure:"; - char *pStrNum = strstr(szCurHdr, szStruct); - long cur_struct_number; - if ( pStrNum ) - { - pStrNum += sizeof(szStruct)-1; /* -1 takes care of the string terminal zero */ - cur_struct_number = inchi_strtol(pStrNum, NULL, 10); - if ( cur_struct_number ) - { - OneInput.num_inp = cur_struct_number; - } - /* process request to bypass first several InChIs */ - if ( cur_struct_number > 0 && cur_struct_number < ip->first_struct_number ) - { - -#if ( !defined(TARGET_API_LIB) && !defined(TARGET_EXE_STANDALONE) ) - inchi_fprintf(stderr, "Skipping %s\r", szMessage); -#endif - FreeInpInChI(&OneInput); - if ( szCurHdr ) - { - inchi_free(szCurHdr); - szCurHdr = NULL; - } - INCHI_HEAPCHK - continue; - } - } - } - - num_processed ++; - - /* In case of splitting InChI into separate components */ - /* decide whether to keep /p in each component or */ - /* output /p and /i/h as a separate component */ - /* Note: if InChI is not to be splitted DO NOT create */ - /* a separate component for /p, /i/h: it would be a bug*/ - - InchiTimeGet(&ulTStart); - - INCHI_HEAPCHK - ret = SetProtonsAndXchgIsoH(bInChI2Structure, bReqSplitOutputInChI, bReqProtonsForEachComponent, - bReqNonTaut, bReqStereo, num_components, nModeProtonIsoExchgH, &OneInput); - INCHI_HEAPCHK - if ( ret < 0 ) - { - num_errors ++; - goto exit_error; - } - - sd->num_components[INCHI_BAS] = num_components[INCHI_BAS]; - sd->num_components[INCHI_REC] = num_components[INCHI_REC]; - - /* do we have reconnected InChI ? */ - if ( (OneInput.nNumComponents[INCHI_REC][TAUT_YES] || - OneInput.nNumComponents[INCHI_REC][TAUT_NON]) && - (ip->bTautFlags & TG_FLAG_RECONNECT_COORD) && - (ip->bTautFlags & TG_FLAG_DISCONNECT_COORD) ) - { - /* needed for InChI string output to include reconnected InChI */ - sd->bTautFlagsDone[0] |= TG_FLAG_DISCONNECT_COORD_DONE; - bHasSomeReconnected = 1; - } - /* Do we have fixed H InChI ? */ - if ( bReqNonTaut && - /*OneInput.nNumComponents[bHasSomeReconnected?INCHI_REC:INCHI_BAS][TAUT_NON]*/ - (OneInput.nNumComponents[INCHI_REC][TAUT_NON] || - OneInput.nNumComponents[INCHI_BAS][TAUT_NON]) ) - { - bHasSomeFixedH = 1; - } - - ulProcessingTime += InchiTimeElapsed(&ulTStart); - - - - if ( bInChI2Structure && !bInChI2InChI ) - { - /**********************************************************************/ - /* InChi --> Structure */ - /**********************************************************************/ - - - int bINChIOutputOptions = -#if ( I2S_MODIFY_OUTPUT == 1 ) - /* transfer user's InChI output options to serialization 10-12-2007 */ - ip_inp->bINChIOutputOptions & ( - INCHI_OUT_NO_AUX_INFO | /* do not output Aux Info */ - INCHI_OUT_SHORT_AUX_INFO | /* output short version of Aux Info */ - INCHI_OUT_ONLY_AUX_INFO | /* output only Aux Info */ - /* INCHI_OUT_EMBED_REC |*/ /* embed reconnected INChI into disconnected INChI */ - INCHI_OUT_SDFILE_ONLY | /* save input data in a Molfile instead of creating INChI */ - INCHI_OUT_XML | /* output xml INChI */ - INCHI_OUT_PLAIN_TEXT | /* output plain text INChI */ - INCHI_OUT_PLAIN_TEXT_COMMENTS | /* output plain text annotation */ - INCHI_OUT_XML_TEXT_COMMENTS | /* output xml text annotation */ - /* INCHI_OUT_WINCHI_WINDOW |*/ /* output into wINChI text window */ - INCHI_OUT_TABBED_OUTPUT | /* tab-delimited (only for plain text) */ - INCHI_OUT_SDFILE_ATOMS_DT | /* SDfile output H isotopes as D and T */ - INCHI_OUT_SDFILE_SPLIT | /* Split SDfile into components */ - 0); - - -#else - 0; -#endif - - - SRM srm; /* rules how to handle bonds to metal atoms */ - StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM]; - - - /* prepare parameters */ - - InchiTimeGet(&ulTStart); - - if ( bInputInStdFormat ) - { - if ( ip_inp->bINChIOutputOptions & INCHI_OUT_STDINCHI ) - bINChIOutputOptions |= INCHI_OUT_STDINCHI; - } - else - { - if ( ip_inp->bINChIOutputOptions & INCHI_OUT_SAVEOPT ) - bINChIOutputOptions |= INCHI_OUT_SAVEOPT; - } - - - - memset(pStruct, 0, sizeof(pStruct)); - - /* structure restore parms */ - SetUpSrm(&srm); - - /* eliminate Fixed-H InChI that are exactly same as the corresponding Mobile-H structures */ - RemoveFixHInChIIdentical2MobH(&OneInput); - - /*-- recheck layers after the elimination; get optional stereo flags --*/ - ret = DetectInpInchiCreationOptions(&OneInput, - &bHasSomeReconnected, &bHasMetal, - &bHasSomeFixedH, &nModeFlagsStereo, - &bTautFlags); - if ( ret < 0 ) - { - AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, - "Error in detecting input InChI options", "; "); - num_errors ++; - goto dealloc; - } - if ( bHasSomeFixedH && !bReqNonTaut ) - { - bHasSomeFixedH = 0; - } - /*---------------- set stereo flags ---------------------*/ - ip->nMode &= ~(REQ_MODE_STEREO | REQ_MODE_ISO_STEREO | - REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO | - REQ_MODE_CHIR_FLG_STEREO | - REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU); - ip->nMode |= nModeFlagsStereo; - - /* Remove Phosphine and Arsine Stereo Flags */ - ip->bTautFlags &= ~TG_FLAG_PHOSPHINE_STEREO; - ip->bTautFlags &= ~TG_FLAG_ARSINE_STEREO; - ip->bTautFlags &= ~TG_FLAG_FIX_SP3_BUG; - - ip->bTautFlags |= bTautFlags; - - /* mark Disconnected InChI components that are exactly came as Reconnected ones */ - /* Disconnected will have a negative number of the reconnected component */ - /* Reconnected will have a positive number of the disconnected component */ - MarkDisconectedIdenticalToReconnected (&OneInput); - - /*****************************************************************************/ - /* pay attention to: */ - /* 1) .nLink < 0 in Disonnected which means InChI is same as in Reconnected */ - /* The component in Reconnected has .nLink pointing to the Disconnected; */ - /* each .nLink = (1+component index) or -(1+component index) */ - /* In the future .nLink>0 in Disconnected shall point to the Reconnectrd */ - /* component from which it was created */ - /* 2) Currently reversed structures from Disconnected components are created */ - /* and abandoned if Reconnected layer exists */ - /* 3) Connect/disconnect H depends on the presence of atom/bond parity */ - /* The combined Mobile/Fixed-H parity should be set for Fixed-H components*/ - /* 4) No comparison of the Disconnected layer is done if Reconnected exists */ - /* 5) Reading InChI was not fully tested in case one component has stereo in */ - /* both Mobile-H and Fixed-H layers while another component has stereo */ - /* only in Mobile-H layer */ - /*****************************************************************************/ - - /* main conversion InChI->Structure for each component and */ - /* after that pStruct[iRec][iMobH][iComponent].at2 is the structure, */ - /* pStruct[iRec][iMobH][iComponent].RevInChI full InChI for the structure */ - /* In case of both Fixed-H and Mobile-H layers the results are in iMobH=0 */ - /* In case of only Mobile-H/Main layer the results are in iMobH=1 */ - ulProcessingTime += InchiTimeElapsed(&ulTStart); - - sd->ulStructTime = 0; - ret = AllInchiToStructure(ip, sd, num_inp, szCurHdr, &srm, bHasSomeFixedH, pStruct, &OneInput); - - ulProcessingTime += sd->ulStructTime; - InchiTimeGet(&ulTStart); - /* ret < 0 is error code; ret > 0 is number of errors */ - /* in pStruct[iInchiRec][iMobileH][iComponent].nError */ - - if ( ret) - { - /* conversion error */ - num_errors ++; - goto dealloc; - } - - - /* an attempt to fix the numumber of removed protons in case of Mobile-H */ - if ( !OneInput.nNumProtons[INCHI_BAS][TAUT_YES].pNumProtons && - !OneInput.nNumProtons[INCHI_REC][TAUT_YES].pNumProtons ) - { - ret = AddProtonAndIsoHBalanceToMobHStruct(ip, sd, num_inp, bHasSomeFixedH, szCurHdr, pStruct, &OneInput); - if ( ret < 0 ) - { - AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "Add/Remove protons error", "; "); - num_errors ++; - goto dealloc; - } - } - - /* compare InChI from the Reversed Structure to the original input InChI */ - - ret = CompareAllOrigInchiToRevInChI(pStruct, &OneInput, bHasSomeFixedH, num_inp, szCurHdr); - if ( ret < 0 ) - { - AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "InChI compare error", "; "); - num_errors ++; - goto dealloc; - } - - ret = CompareAllDisconnectedOrigInchiToRevInChI(pStruct, &OneInput, bHasSomeFixedH, - num_inp, szCurHdr); - if ( ret < 0 ) - { - AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "InChI compare2 error", "; "); - num_errors ++; - goto dealloc; - } - - if ( WarningFlags ) - { - for ( i = 0; i < 2; i ++ ) - { - for ( j = 0; j < TAUT_NUM; j ++ ) - { - WarningFlags[i][j] = (unsigned long)OneInput.CompareInchiFlags[i][j]; - } - } - } - - ulProcessingTime += InchiTimeElapsed(&ulTStart); - -#ifndef COMPILE_ANSI_ONLY - ret = DisplayStructureComponents(ip, sd, num_inp, szCurHdr, - &srm, bReqNonTaut, pStruct, &OneInput); - if ( ret < 0 ) - { - AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "Display structure error", "; "); - } -#endif - InchiTimeGet(&ulTStart); - ret = MergeStructureComponents(ip, sd, num_inp, szCurHdr, &srm, bReqNonTaut, pStruct, &OneInput); - ulProcessingTime += InchiTimeElapsed(&ulTStart); - if ( ret < 0 ) - { - AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "Merge Components error", "; "); - num_errors ++; - goto dealloc; - } - - - -#ifdef TARGET_API_LIB -/*------------- for debug only ------------------- - InchiTimeGet(&ulTStart); - ret = OutputInChIOutOfStrFromINChI(ip, sd, num_inp, 0, - pOut, pLog, &OneInput, - save_opt_bits); - ulProcessingTime += InchiTimeElapsed(&ulTStart); - if ( ret < 0 ) { - AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "Restored structure to InChI conversion failed", "; "); - goto dealloc; - } --------------------------------------------------*/ - if ( at && num_at ) - { - *at = OneInput.atom; - *num_at = OneInput.num_atoms; - OneInput.atom = NULL; - } -#else - InchiTimeGet(&ulTStart); - ret = OutputInChIOutOfStrFromINChI(ip, sd, num_inp, bINChIOutputOptions, - pOut, /*pLog*/ NULL, &OneInput, - bHasSomeFixedH, save_opt_bits); - ulProcessingTime += InchiTimeElapsed(&ulTStart); - if ( ret < 0 ) - { - AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "Restored structure to InChI conversion error", "; "); - num_errors ++; - goto dealloc; - } -#endif - - if ( szMessage ) - { - int len; - InchiTimeGet(&ulTStart); - FillOutCompareMessage(szMessage, nMessageLen, OneInput.CompareInchiFlags[0]); - if ( OneInput.CompareInchiFlags[1][0] || OneInput.CompareInchiFlags[1][1] ) - { - AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "Disconnected: ", "; "); - FillOutCompareMessage(szMessage, nMessageLen, OneInput.CompareInchiFlags[1]); - } - /* add a metal warning */ - if ( bHasMetal && nInitLenMessage < (len=(int)strlen(szMessage)) ) - { - char szMetal[] = " (Metal compound)"; - int shift; - if ( len + (int)sizeof(szMetal) > nMessageLen ) { - len = nMessageLen - (int)sizeof(szMetal); - } - shift = nInitLenMessage + (int)sizeof(szMetal) - 1; - memmove(szMessage+shift, szMessage + nInitLenMessage, (len-nInitLenMessage)*sizeof(szMessage[0])); - memcpy(szMessage + nInitLenMessage, szMetal, sizeof(szMetal)-sizeof(szMessage[0])); - szMessage[shift+len-nInitLenMessage] = '\0'; - } - ulProcessingTime += InchiTimeElapsed(&ulTStart); - } - - ret = 0; - - /* deallocate */ -dealloc: - if ( ret ) - { - if ( ret > 0 ) - { - int iRec, iMob, iComp, nComp, len; - char szTemp[128]; - AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "*Conversion failed on component(s)", "; "); - len = strlen(szMessage); - for ( iRec = 0; iRec < INCHI_NUM; iRec ++ ) - { - for ( iMob = bHasSomeFixedH? TAUT_NON : TAUT_YES; iMob < TAUT_NUM; iMob ++ ) - { - nComp = OneInput.nNumComponents[iRec][iMob]; - if ( !pStruct[iRec][iMob] ) - { - continue; - } - for ( iComp = 0; iComp < nComp; iComp ++ ) - { - if ( pStruct[iRec][iMob][iComp].nError ) - { - char *szFormula = OneInput.pInpInChI[iRec][iMob][iComp].szHillFormula; - sprintf (szTemp, -#if ( FIX_DALKE_BUGS == 1 ) - " %s%s%d(%.96s)", -#else - " %s%s%d(%s)", -#endif - !bHasSomeReconnected? "" : iRec? "R" : "D", - !bHasSomeFixedH? "": iMob? "M" : "F", - iComp + 1, szFormula? szFormula : "???"); - AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, szTemp, NULL); - } - } - } - } - } - else - { - if ( ret == CT_USER_QUIT_ERR ) - { - AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "*Terminated by the user*", "; "); - } else { - AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "*Conversion failed*", "; "); - } - } - } - InchiTimeGet(&ulTStart); - /* print one structure report */ - if ( szMsg && nMsgLen > 1 ) - { - int len = inchi_min( (int)strlen(szMessage), nMsgLen-1); - if ( len > 0 ) - { - memcpy( szMsg, szMessage, len); - szMsg[len] = '\0'; - } - else - { - szMsg[0] = '\0'; - } - } - if ( nInitLenMessage < (int)strlen(szMessage) ) - { - inchi_ios_eprint(pLog, "%s\n", szMessage); - } -#ifndef TARGET_API_LIB - else - { - /*^^^inchi_ios_eprint( stderr, "%s\r", szMessage );*/ - inchi_fprintf( stderr, "%s\r", szMessage ); - } -#endif - - - FreeStrFromINChI( pStruct, OneInput.nNumComponents ); - FreeInpInChI( &OneInput ); - if ( szCurHdr ) - { - inchi_free( szCurHdr ); - szCurHdr = NULL; - } - INCHI_HEAPCHK - ulProcessingTime += InchiTimeElapsed( &ulTStart ); - if ( ret < 0 ) - { - goto exit_error; - } - - } /* if ( bInChI2Structure && !bInChI2InChI ) */ - - - - else if ( !bInChI2Structure && bInChI2InChI ) - { - /**********************************************************************/ - /* InChi --> InChI string(s) */ - /**********************************************************************/ - - int tmp = ip->bNoStructLabels; - - InchiTimeGet( &ulTStart ); - - ip->bNoStructLabels = 1; - INCHI_HEAPCHK - ip->pSdfValue = NULL; - ip->pSdfLabel = NULL; -#if ( FIX_DALKE_BUGS == 1 ) - SetHillFormFromInChI( &OneInput ); -#endif - - - ret = OutputInChIAsRequested(pOut, pLog, ip, sd, &OneInput, - num_components, nModeProtonIsoExchgH, - num_inp, save_opt_bits); - - -#if ( !defined(TARGET_API_LIB) && defined(TARGET_EXE_STANDALONE) ) - /*^^^ calculate InChIKey if requested */ - /* However, do not calculat/write it if this function is called from within dll */ - { - char ik_string[256]; /*^^^ Resulting InChIKey string */ - int ik_ret=0; /*^^^ InChIKey-calc result code */ - int xhash1, xhash2; - char szXtra1[65], szXtra2[65]; - - inchi_ios_flush2(pLog, stderr); - - /*^^^ post-1.02b addition - correctly treat tabbed output with InChIKey */ - if ( ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT ) - if ( ip->bCalcInChIHash != INCHIHASH_NONE ) - if (pOut->s.pStr) - if (pOut->s.nUsedLength>0) - if (pOut->s.pStr[pOut->s.nUsedLength-1]=='\n') - /* replace LF with TAB */ - pOut->s.pStr[pOut->s.nUsedLength-1] = '\t'; - - - if ( ip->bCalcInChIHash != INCHIHASH_NONE ) - { - char *buf = NULL; - size_t slen = pOut->s.nUsedLength; - extract_inchi_substring(&buf, pOut->s.pStr, slen); - - if (NULL!=buf) - { - xhash1 = xhash2 = 0; - if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1 ) || - ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) - xhash1 = 1; - if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA2 ) || - ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) - xhash2 = 1; - ik_ret = GetINCHIKeyFromINCHI(buf, xhash1, xhash2, - ik_string, szXtra1, szXtra2); - inchi_free(buf); - } - else - ik_ret = INCHIKEY_NOT_ENOUGH_MEMORY; - - - if (ik_ret==INCHIKEY_OK) - { - inchi_ios_print(pOut, "InChIKey=%-s\n",ik_string); - } - else - { - inchi_ios_print(pLog, "Warning (Could not compute InChIKey: ", num_inp); - switch(ik_ret) - { - case INCHIKEY_UNKNOWN_ERROR: - inchi_ios_print(pLog, "unresolved error)"); - break; - case INCHIKEY_EMPTY_INPUT: - inchi_ios_print(pLog, "got an empty string)"); - break; - case INCHIKEY_INVALID_INCHI_PREFIX: - case INCHIKEY_INVALID_INCHI: - case INCHIKEY_INVALID_STD_INCHI: - inchi_ios_print(pLog, "got non-InChI string)"); - break; - case INCHIKEY_NOT_ENOUGH_MEMORY: - inchi_ios_print(pLog, "not enough memory to treat the string)"); - break; - default:inchi_ios_print(pLog, "internal program error)"); - break; - } - inchi_ios_print(pLog, " structure #%-lu.\n", num_inp); - if ( ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT ) - inchi_ios_print(pOut, "\n"); - } /* if (ip->bCalcInChIHash!=INCHIHASH_NONE) */ - - - inchi_ios_flush(pOut); - inchi_ios_flush2(pLog, stderr); - - } - - else - inchi_ios_flush(pOut); - } /* calculate InChIKey if requested */ -#endif - - ip->bNoStructLabels = tmp; - -#ifndef TARGET_API_LIB - if ( ret < 0 ) - { - if ( szCurHdr && szCurHdr[0] ) - { - inchi_ios_eprint( pLog, "Error %d creating InChI string %s\n", ret, szCurHdr ); - } - else - { - inchi_ios_eprint( pLog, "Error %d creating InChI string, Structure %ld\n", ret, num_inp ); - } - num_errors ++; - } -#if ( !defined(TARGET_API_LIB) && !defined(TARGET_EXE_STANDALONE) ) - else - if ( szCurHdr && szCurHdr[0] ) - { - inchi_fprintf( stderr, "%s\r", szCurHdr ); - } -#endif -#endif - - if ( szCurHdr ) - { - inchi_free( szCurHdr ); - szCurHdr = NULL; - } - - INCHI_HEAPCHK - ulProcessingTime += InchiTimeElapsed( &ulTStart ); - - } /* if ( !bInChI2Structure && bInChI2InChI ) */ - - else - { - inchi_ios_eprint( pLog, "\nWrong command line options: expected Inch2Struct or Inchi2Inchi\n", num_inp ); - break; - } - - - if ( nReadStatus == RI_ERR_EOF ) - { - break; - } - - - } /* InChI has been successfully read */ - - else - - { - - /* InChI could not be read */ - if ( nReadStatus == RI_ERR_EOF && nErr == 0 && pState == 0 && !strHdr ) - { - inchi_ios_eprint( pLog, "\nEnd of file detected after structure %ld. \n", num_inp ); - } - else - { - /* output InChI parsing error message */ - char szHdrSimulation[128]; - num_inp ++; - sprintf( szHdrSimulation, "Structure: %ld", num_inp ); - inchi_ios_eprint( pLog, "\n%s %s (%d) in %s (%d)\n", strHdr? strHdr : szHdrSimulation, getInchiErrName(nErr), nErr, getInchiStateReadErr(pState), pState ); - num_errors ++; - num_processed ++; - } - if ( strHdr ) - { - inchi_free( strHdr ); - strHdr = NULL; - } - if ( szCurHdr ) - { - inchi_free( szCurHdr ); - szCurHdr = NULL; - } - FreeInpInChI( &OneInput ); - } - - -#ifdef TARGET_EXE_STANDALONE -#ifndef TARGET_API_LIB - inchi_ios_flush(pOut); - inchi_ios_flush2(pLog, stderr); -#endif -#endif - - -#ifdef TARGET_API_LIB - break; /* exit after the 1st structure */ -#endif - - - } /* while */ - - - - -exit_error: - - FreeInpInChI( &OneInput ); - if ( strHdr ) - { - inchi_free( strHdr ); - strHdr = NULL; - } - if ( pLine->str ) - { - inchi_free( pLine->str ); - } - if ( szCurHdr ) - { - inchi_free( szCurHdr ); - szCurHdr = NULL; - } - - INCHI_HEAPCHK - - if ( sd_inp ) - { - sd_inp->ulStructTime = ulProcessingTime; - sd_inp->fPtrStart = num_processed; - sd_inp->fPtrEnd = num_errors; - } - return ret; -} - - - -/**********************************************************************************************/ -int OutputInChIAsRequested(INCHI_IOSTREAM *pOut, INCHI_IOSTREAM *pLog, - ICHICONST INPUT_PARMS *ip_inp, - STRUCT_DATA *sd_inp, - InpInChI *OneInput, - int num_components[INCHI_NUM], - MODE_PIXH nModeProtonIsoExchgH[INCHI_NUM], - long num_inp, unsigned char save_opt_bits) -{ - int j, k, k1, k2, ret2=0, iINChI, iINChI1, iINChI2; - PINChI2 *pINChI[INCHI_NUM]; - PINChI_Aux2 *pINChI_Aux[INCHI_NUM]; - int bReqNonTaut; - int bHasSomeReconnected; - - INPUT_PARMS ip_local; - STRUCT_DATA sd_local; - INPUT_PARMS *ip = &ip_local; - STRUCT_DATA *sd = &sd_local; - NORM_CANON_FLAGS ncFlags; - NORM_CANON_FLAGS *pncFlags = &ncFlags; - const int nStrLen = NSTRLEN; - char *pStr = NULL; - int nRet1, bSortPrintINChIFlags; - int bReqSplitOutputInChI; - int nNumOutputComponents; - - nRet1 = 0; - k1 = k2 = 0; - memset( pncFlags, 0, sizeof(*pncFlags) ); - memset( pINChI, 0, sizeof(pINChI) ); - memset( pINChI_Aux, 0, sizeof(pINChI_Aux) ); - - *ip = *ip_inp; - *sd = *sd_inp; - bHasSomeReconnected = 0; - bSortPrintINChIFlags = 0; - nNumOutputComponents = 0; - bReqNonTaut = (0 != (ip->nMode & REQ_MODE_BASIC)); - bReqSplitOutputInChI = (0 != (ip->bReadInChIOptions & READ_INCHI_SPLIT_OUTPUT)); - - INCHI_HEAPCHK - - if ( num_components[INCHI_BAS] ) - { - MYREALLOC2(PINChI2, PINChI_Aux2, pINChI[INCHI_BAS], pINChI_Aux[INCHI_BAS], num_components[INCHI_BAS], num_components[INCHI_BAS], k1); - } - if ( num_components[INCHI_REC] ) - { - MYREALLOC2(PINChI2, PINChI_Aux2, pINChI[INCHI_REC], pINChI_Aux[INCHI_REC], num_components[INCHI_REC], num_components[INCHI_REC], k2); - } - pStr = (char*) inchi_malloc( nStrLen * sizeof(pStr[0]) ); - - INCHI_HEAPCHK - - if ( k1 || k2 || !pStr ) - { - ret2 = RI_ERR_ALLOC; - goto exit_error; - } - - if ( num_components[INCHI_REC] && - (ip->bTautFlags & TG_FLAG_RECONNECT_COORD) && - (ip->bTautFlags & TG_FLAG_DISCONNECT_COORD) ) - { - sd->bTautFlagsDone[0] |= TG_FLAG_DISCONNECT_COORD_DONE; - bHasSomeReconnected = 1; - } - - - for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) - { - for ( j = 0; j < TAUT_NUM; j ++ ) - { - if ( bReqNonTaut || j != TAUT_NON && OneInput->pInpInChI[iINChI][j] ) - { - for ( k = 0; k < num_components[iINChI]; k ++ ) - { - /* allocate InChI & AuxInfo */ - if ( !(pINChI[iINChI][k][j] = (INChI *) inchi_calloc(1, sizeof(INChI)) ) ) - { - ret2 = RI_ERR_ALLOC; - goto exit_error; - } - if ( !(pINChI_Aux[iINChI][k][j] = (INChI_Aux *) inchi_calloc(1, sizeof(INChI_Aux)) ) ) - { - ret2 = RI_ERR_ALLOC; - goto exit_error; - } - /* copy InChI & AuxInfo */ - if ( k < OneInput->nNumComponents[iINChI][j] ) - { - - /* copy InChI */ - *pINChI[iINChI][k][j] = OneInput->pInpInChI[iINChI][j][k]; - memset(&OneInput->pInpInChI[iINChI][j][k], 0, sizeof(OneInput->pInpInChI[iINChI][j][k])); - INCHI_HEAPCHK - /* take care of protons in AuxInfo */ - - if ( nModeProtonIsoExchgH[iINChI] == MODE_PIXH_ADD_TO_EACH && j == TAUT_YES ) - { - pINChI_Aux[iINChI][k][j]->nNumRemovedProtons = - OneInput->nNumProtons[iINChI][j].pNumProtons[k].nNumRemovedProtons; - for ( k1 = 0; k1 < NUM_H_ISOTOPES; k1 ++ ) - { - pINChI_Aux[iINChI][k][j]->nNumRemovedIsotopicH[k1] = - OneInput->nNumProtons[iINChI][j].pNumProtons[k].nNumRemovedIsotopicH[k1]; - } - INCHI_HEAPCHK - } - else if ( !k && nModeProtonIsoExchgH[iINChI] == MODE_PIXH_ADD_TO_FIRST || - k+1 == OneInput->nNumComponents[iINChI][j] && - nModeProtonIsoExchgH[iINChI] == MODE_PIXH_ADD_A_PIXH_COMPONENT ) - { - /* add protons and exchangeable isotopic H to the first component's AuxInfo */ - pINChI_Aux[iINChI][k][j]->nNumRemovedProtons = OneInput->nNumProtons[iINChI][j].nNumRemovedProtons; - for ( k1 = 0; k1 < NUM_H_ISOTOPES; k1 ++ ) - { - pINChI_Aux[iINChI][k][j]->nNumRemovedIsotopicH[k1] = - OneInput->nNumProtons[iINChI][j].nNumRemovedIsotopicH[k1]; - } - INCHI_HEAPCHK - } - else - { - pINChI_Aux[iINChI][k][j]->bDeleted = pINChI[iINChI][k][j]->bDeleted; - } - - if ( j == TAUT_YES && pINChI[iINChI][k][j] && pINChI[iINChI][k][j]->nNumberOfAtoms && - !pINChI[iINChI][k][j]->nNum_H_fixed ) - { - /* serializer crashes if it is not allocated */ - pINChI[iINChI][k][j]->nNum_H_fixed = (S_CHAR *)inchi_calloc(pINChI[iINChI][k][j]->nNumberOfAtoms+1, sizeof(pINChI[0][0][0]->nNum_H_fixed[0]) ); - } - - if ( j == TAUT_YES && k < OneInput->nNumComponents[iINChI][TAUT_NON] && - pINChI[iINChI][k][j] && pINChI[iINChI][k][j]->nNumberOfAtoms && - pINChI[iINChI][k][TAUT_NON] && pINChI[iINChI][k][TAUT_NON]->nNumberOfAtoms && - !CompareReversedINChI( pINChI[iINChI][k][j], pINChI[iINChI][k][TAUT_NON], NULL, NULL ) ) { - pINChI[iINChI][k][TAUT_NON]->nNumberOfAtoms = 0; /* eliminate non-taut equal to taut */ - } - - - } - else - { - /* extra component, usually it is a Mobile H component */ - /* corresponding to a free proton component in Fixed H */ - pINChI[iINChI][k][j]->bDeleted = 1; - pINChI_Aux[iINChI][k][j]->bDeleted = 1; - } - - } /* k */ - - } /* if ( bReqNonTaut || j != TAUT_NON && OneInput->pInpInChI[iINChI][j] ) */ - - if ( OneInput->pInpInChI[iINChI][j] ) - { - INCHI_HEAPCHK - inchi_free(OneInput->pInpInChI[iINChI][j]); - OneInput->pInpInChI[iINChI][j] = NULL; - } - - } /* j */ - - } /* iINChI */ - - if ( bReqSplitOutputInChI ) - { - if ( bHasSomeReconnected ) - { - iINChI1 = INCHI_REC; /* only reconnected */ - iINChI2 = INCHI_NUM; - sd->num_components[INCHI_BAS] = sd->num_components[INCHI_REC]; - } - else - { - iINChI1 = 0; /* only disconnected */ - iINChI2 = iINChI1+1; - } - sd->num_components[INCHI_REC] = 0; /* treat reconnected as connected */ - nNumOutputComponents = sd->num_components[INCHI_BAS]; - } - else - { - iINChI1 = 0; - iINChI2 = INCHI_NUM; - nNumOutputComponents = 1; - } - - - for ( k1 = 0, k2 = (bReqSplitOutputInChI? k1+1 : nNumOutputComponents); k1 < k2 && k1 < nNumOutputComponents; k1=k2, k2 ++ ) - { - - if ( bReqSplitOutputInChI ) - { - sd->num_components[INCHI_BAS] = 1; - sd->num_components[INCHI_REC] = 0; - /* additional data */ - sd->num_non_taut[INCHI_BAS] = - sd->num_taut[INCHI_BAS] = - sd->num_non_taut[INCHI_REC] = - sd->num_taut[INCHI_REC] = 0; - iINChI = iINChI1; - for ( j = 0; j < TAUT_NUM && sd->num_components[iINChI]; j ++ ) - { - for ( k = k1; k < k2; k ++ ) - { - /* find where the current processed structure is located */ - int cur_is_in_non_taut = (pINChI[iINChI][k][TAUT_NON] && pINChI[iINChI][k][TAUT_NON]->nNumberOfAtoms>0); - int cur_is_in_taut = (pINChI[iINChI][k][TAUT_YES] && pINChI[iINChI][k][TAUT_YES]->nNumberOfAtoms>0); - int cur_is_non_taut = cur_is_in_non_taut && 0 == pINChI[iINChI][k][TAUT_NON]->lenTautomer || - cur_is_in_taut && 0 == pINChI[iINChI][k][TAUT_YES]->lenTautomer; - int cur_is_taut = cur_is_in_taut && 0 < pINChI[iINChI][k][TAUT_YES]->lenTautomer; - if ( cur_is_non_taut + cur_is_taut ) - { - /* count tautomeric and non-tautomeric components of the structures */ - /* - int j1 = cur_is_in_non_taut? TAUT_NON:TAUT_YES; - int j2 = cur_is_in_taut? TAUT_YES:TAUT_NON; - */ - sd->num_non_taut[INCHI_BAS] += cur_is_non_taut; - sd->num_taut[INCHI_BAS] += cur_is_taut; - } - } - } - INCHI_HEAPCHK - } - else - { - sd->num_components[INCHI_BAS] = inchi_max( OneInput->nNumComponents[INCHI_BAS][TAUT_YES], - OneInput->nNumComponents[INCHI_BAS][TAUT_NON] ); - sd->num_components[INCHI_REC] = inchi_max( OneInput->nNumComponents[INCHI_REC][TAUT_YES], - OneInput->nNumComponents[INCHI_REC][TAUT_NON] ); - - /* additional data needed for SortAndPrintINChI() */ - for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) - { - sd->num_non_taut[iINChI] = - sd->num_taut[iINChI] = 0; - for ( j = 0; j < TAUT_NUM && sd->num_components[iINChI]; j ++ ) - { - for ( k = k1; k < k2; k ++ ) - { - /* find where the current processed structure is located */ - int cur_is_in_non_taut = (pINChI[iINChI][k][TAUT_NON] && pINChI[iINChI][k][TAUT_NON]->nNumberOfAtoms>0); - int cur_is_in_taut = (pINChI[iINChI][k][TAUT_YES] && pINChI[iINChI][k][TAUT_YES]->nNumberOfAtoms>0); - int cur_is_non_taut = cur_is_in_non_taut && 0 == pINChI[iINChI][k][TAUT_NON]->lenTautomer || - cur_is_in_taut && 0 == pINChI[iINChI][k][TAUT_YES]->lenTautomer; - int cur_is_taut = cur_is_in_taut && 0 < pINChI[iINChI][k][TAUT_YES]->lenTautomer; - if ( cur_is_non_taut + cur_is_taut ) { - /* count tautomeric and non-tautomeric components of the structures */ - /* - int j1 = cur_is_in_non_taut? TAUT_NON:TAUT_YES; - int j2 = cur_is_in_taut? TAUT_YES:TAUT_NON; - */ - sd->num_non_taut[iINChI] += cur_is_non_taut; - sd->num_taut[iINChI] += cur_is_taut; - } - } - } - } - INCHI_HEAPCHK - } - if ( bReqSplitOutputInChI ) - { - /* output components one by one (for splitting input InChI into components) */ - PINChI2 *pInChI_2[INCHI_NUM]; - PINChI_Aux2 *pInChI_Aux_2[INCHI_NUM]; - INChI *pInChI_1[1][2]; - INChI_Aux *pInChI_Aux_1[1][2]; - memset( pInChI_2, 0, sizeof(pInChI_2) ); - memset( pInChI_Aux_2, 0, sizeof(pInChI_Aux_2) ); - - for ( j = 0; j < TAUT_NUM; j ++ ) - { - pInChI_1[0][j] = pINChI[iINChI1][k1][j]; - pInChI_Aux_1[0][j] = pINChI_Aux[iINChI1][k1][j]; - } - pInChI_2[INCHI_BAS] = pInChI_1; - pInChI_Aux_2[INCHI_BAS] = pInChI_Aux_1; - /* make sure purely reconnected InChI is marked as ReChI, not InChI */ - if ( bHasSomeReconnected && - (bInChIHasReconnectedMetal( pInChI_1[0][TAUT_YES] ) || - bInChIHasReconnectedMetal( pInChI_1[0][TAUT_NON] ) ) ) - { - bSortPrintINChIFlags = FLAG_SORT_PRINT_ReChI_PREFIX; - } - else - { - bSortPrintINChIFlags = 0; - } - INCHI_HEAPCHK - nRet1 = SortAndPrintINChI(pOut, pStr, nStrLen, pLog, ip, NULL /*orig_inp_data*/, NULL /*prep_inp_data*/, - NULL /*composite_norm_data*/, NULL /*pOrigStruct*/, - sd->num_components, sd->num_non_taut, sd->num_taut, - sd->bTautFlags, sd->bTautFlagsDone, pncFlags, num_inp, - pInChI_2, pInChI_Aux_2, &bSortPrintINChIFlags, - save_opt_bits); - INCHI_HEAPCHK - } - else - { - INCHI_HEAPCHK - bSortPrintINChIFlags = 0; - nRet1 = SortAndPrintINChI(pOut, pStr, nStrLen, pLog, ip, NULL /*orig_inp_data*/, NULL /*prep_inp_data*/, - NULL /*composite_norm_data*/, NULL /*pOrigStruct*/, - sd->num_components, sd->num_non_taut, sd->num_taut, - sd->bTautFlags, sd->bTautFlagsDone, pncFlags, num_inp, - pINChI, pINChI_Aux, &bSortPrintINChIFlags, - save_opt_bits); - INCHI_HEAPCHK - } - if ( nRet1 == _IS_FATAL || nRet1 == _IS_ERROR ) - { - break; - } - } - - - INCHI_HEAPCHK - FreeAllINChIArrays( pINChI, pINChI_Aux, num_components ); - INCHI_HEAPCHK - - for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) - { - for ( j = 0; j < TAUT_NUM; j ++ ) - { - if ( OneInput->nNumProtons[iINChI][j].pNumProtons ) - { - inchi_free( OneInput->nNumProtons[iINChI][j].pNumProtons ); - OneInput->nNumProtons[iINChI][j].pNumProtons = NULL; - } - } - } - - INCHI_HEAPCHK - if ( nRet1 == _IS_FATAL || nRet1 == _IS_ERROR ) - { - ret2 = RI_ERR_PROGR; - } - -exit_error: - if ( pStr ) - inchi_free( pStr ); - return ret2; -} - -/**************************************************************************************/ -int GetNumNeighborsFromInchi( INChI *pInChI, AT_NUMB nAtNumber ) -{ - int i, j, n_vertex, n_neigh, nNumNeigh, bTautAtom, nNumH, nTotNumNeigh, num_atoms; - AT_NUMB taut_at_number; - nAtNumber -= 1; - nNumNeigh = 0; /* number of bonds */ - bTautAtom = 0; /* 1 if atom belongs to a Mobile-H group */ - nNumH = 0; /* number of terminal neighbors H */ - num_atoms = pInChI->nNumberOfAtoms; - /* from RestoreAtomConnectionsSetStereo() */ - /* Connection table structure: - Vert(1) [, Neigh(11), Neigh(12),...], Vert(2) [, Neigh(2,1), Neigh(2,2),...] ... - where Neigh(i,1) < Neigh(i,2) <... < Vert(i); - Vert(i) < Vert(i+1) - */ - for ( i = 1, n_vertex = pInChI->nConnTable[0]-1; i < pInChI->lenConnTable; i ++ ) { - if ( (n_neigh = pInChI->nConnTable[i]-1) < n_vertex ) { - /* vertex - neighbor connection */ - nNumNeigh += ( nAtNumber == n_vertex || nAtNumber == n_neigh ); - } else - /* n_neigh is the next vertex */ - if ( (n_vertex = n_neigh) >= num_atoms ) { - return RI_ERR_PROGR; - } - } - /* is atom tautomeric, from GetTgroupInfoFromInChI() */ - if ( pInChI && pInChI->lenTautomer > 1 && pInChI->nTautomer && pInChI->nTautomer[0] > 0 ) { - int itg, len_tg; - int tot_len_tg = pInChI->lenTautomer - T_GROUP_HDR_LEN*pInChI->nTautomer[0] - 1; /* number of endpoints */ - j = 1; /* index in pInChI->nTautomer[] */ - i = 0; /* index in ti->nEndpointAtomNumber[] */ - for ( itg = 0; itg < pInChI->nTautomer[0]; itg ++ ) { - len_tg = pInChI->nTautomer[j]; /* t-group length not including pInChI->nTautomer[j] */ - j += T_GROUP_HDR_LEN; /* skip t-group header */ - len_tg -= T_GROUP_HDR_LEN-1; - for( ; 0 < len_tg --; j ++, i ++ ) { - taut_at_number = pInChI->nTautomer[j]-1; /* Mobile-H group atom number */ - bTautAtom += (taut_at_number == nAtNumber); - } - } - if ( i != tot_len_tg ) { - return RI_ERR_PROGR; - } - } - /* count hydrogen neighbors */ - if ( pInChI->nNum_H ) { - nNumH = pInChI->nNum_H[nAtNumber]; - } - /* conclusion: if not tautomeric then return positive number, otherwise add 1000 */ - nTotNumNeigh = nNumNeigh + nNumH; - if ( bTautAtom ) { - nTotNumNeigh += 1000; - } - return nTotNumNeigh; -} -/**************************************************************************************/ -int CountStereoTypes( INChI *pInChI, int *num_known_SB, int *num_known_SC, - int *num_unk_und_SB, int *num_unk_und_SC, - int *num_SC_PIII, int *num_SC_AsIII) -{ - static U_CHAR el_number_P=0, el_number_As=0; - INChI_Stereo *Stereo; - int i, ret; - AT_NUMB nAtNumber; - U_CHAR el_number; - if ( !pInChI->nNumberOfAtoms || pInChI->bDeleted ) { - return 0; /* no InChI */ - } - Stereo = (pInChI->StereoIsotopic && - (pInChI->StereoIsotopic->nNumberOfStereoBonds + - pInChI->StereoIsotopic->nNumberOfStereoCenters ))? pInChI->StereoIsotopic: - (pInChI->Stereo && - (pInChI->Stereo->nNumberOfStereoBonds + - pInChI->Stereo->nNumberOfStereoCenters ))? pInChI->Stereo : NULL; - if ( !Stereo ) { - return 1; /* No Stereo */ - } - /* one-time initialization */ - if ( !el_number_P ) { - el_number_P = (U_CHAR)get_periodic_table_number( "P" ); - el_number_As = (U_CHAR)get_periodic_table_number( "As" ); - } - /* count SB and cumulenes */ - for ( i = 0; i < Stereo->nNumberOfStereoBonds; i ++ ) { - if ( ATOM_PARITY_WELL_DEF(Stereo->b_parity[i]) ) { - (*num_known_SB) ++; - } else { - (*num_unk_und_SB) ++; - } - } - /* count SC and allenes */ - for ( i = 0; i < Stereo->nNumberOfStereoCenters; i ++ ) { - if ( !(nAtNumber = Stereo->nNumber[i]) || nAtNumber > pInChI->nNumberOfAtoms ) { - return RI_ERR_PROGR; /* wrong data, should never happen */ - } - if ( ATOM_PARITY_WELL_DEF(Stereo->t_parity[i]) ) { - (*num_known_SC) ++; - } else { - (*num_unk_und_SC) ++; - } - el_number = pInChI->nAtom[nAtNumber-1]; - if ( el_number != el_number_P && el_number != el_number_As ) { - continue; - } - ret = GetNumNeighborsFromInchi( pInChI, nAtNumber ); - if ( ret < 0 ) { - return ret; - } - if ( 3 == ret ) { - *num_SC_PIII += (el_number_P == el_number); - *num_SC_AsIII += (el_number_As == el_number); - } - } - return 2; /* Has Stereo */ -} -/**************************************************************************************/ -int bInpInchiComponentExists( InpInChI *pOneInput, int iInChI, int bMobileH, int k ) -{ - if ( INCHI_BAS != iInChI && iInChI != INCHI_REC || - TAUT_NON != bMobileH && TAUT_YES != bMobileH || k < 0 ) { - return 0; - } - return ( k < pOneInput->nNumComponents[iInChI][bMobileH] && - pOneInput->pInpInChI[iInChI][bMobileH] && - pOneInput->pInpInChI[iInChI][bMobileH][k].nNumberOfAtoms > 0 && - !pOneInput->pInpInChI[iInChI][bMobileH][k].bDeleted ); -} -/**************************************************************************************/ -int bInpInchiComponentDeleted( InpInChI *pOneInput, int iInChI, int bMobileH, int k ) -{ - if ( INCHI_BAS != iInChI && iInChI != INCHI_REC || - TAUT_NON != bMobileH && TAUT_YES != bMobileH || k < 0 ) { - return 0; - } - return ( k < pOneInput->nNumComponents[iInChI][bMobileH] && - pOneInput->pInpInChI[iInChI][bMobileH] && - pOneInput->pInpInChI[iInChI][bMobileH][k].nNumberOfAtoms > 0 && - pOneInput->pInpInChI[iInChI][bMobileH][k].bDeleted ); -} -/**************************************************************************************/ -int bRevInchiComponentExists( StrFromINChI *pStruct, int iInChI, int bMobileH, int k ) -{ - if ( !pStruct || /*!pStruct->at2 ||*/ !pStruct->num_atoms || - INCHI_BAS != iInChI && iInChI != INCHI_REC || - TAUT_NON != bMobileH && TAUT_YES != bMobileH || k < 0 ) { - return 0; - } - return ( k < pStruct->RevInChI.num_components[iInChI] && - pStruct->RevInChI.pINChI[iInChI] && - pStruct->RevInChI.pINChI[iInChI][k][bMobileH] && - pStruct->RevInChI.pINChI[iInChI][k][bMobileH]->nNumberOfAtoms > 0 && - !pStruct->RevInChI.pINChI[iInChI][k][bMobileH]->bDeleted ); -} - -/**************************************************************************************/ -int bRevInchiComponentDeleted( StrFromINChI *pStruct, int iInChI, int bMobileH, int k ) -{ - if ( !pStruct || /*!pStruct->at2 ||*/ !pStruct->num_atoms || - INCHI_BAS != iInChI && iInChI != INCHI_REC || - TAUT_NON != bMobileH && TAUT_YES != bMobileH || k < 0 ) { - return 0; - } - return ( k < pStruct->RevInChI.num_components[iInChI] && - pStruct->RevInChI.pINChI[iInChI] && - pStruct->RevInChI.pINChI[iInChI][k][bMobileH] && - pStruct->RevInChI.pINChI[iInChI][k][bMobileH]->nNumberOfAtoms > 0 && - pStruct->RevInChI.pINChI[iInChI][k][bMobileH]->bDeleted ); -} - -/**************************************************************************************/ -int DetectInpInchiCreationOptions ( InpInChI *pOneInput, int *bHasReconnected, int *bHasMetal, - int *bHasFixedH, int *nModeFlagsStereo, int *bTautFlagsStereo ) -{ - int ret=0, bHasStereo; - int nModeFlagsValue=0, bTautFlagsValue; /* stereo flags */ - int iInChI, iMobileH, bIso, k, max_components, num_components; - INChI *pInChI; - int num_known_SB /*Stereo Bonds & Cumulenes >C==C==C==C< */; - int num_known_SC /* Stereo Centers & Allenes >C=C=C< */; - int num_unk_und_SB, num_unk_und_SC; - int num_SC_PIII, num_SC_AsIII; /* has Phosphine or Arsine stereo center(s) */ - - *bHasReconnected = *bHasFixedH = *nModeFlagsStereo = *bTautFlagsStereo = 0; - nModeFlagsValue = bTautFlagsValue = bHasStereo = 0; - num_known_SB = num_known_SC = num_unk_und_SB = num_unk_und_SC = num_SC_PIII = num_SC_AsIII = 0; - *bHasMetal = 0; - - for ( iInChI = 0; iInChI < INCHI_NUM; iInChI ++ ) - { - for ( iMobileH = 0; iMobileH < TAUT_NUM; iMobileH ++ ) - { - for ( bIso = 1; !nModeFlagsValue && 0 <= bIso; bIso -- ) - { - switch( pOneInput->s[iInChI][iMobileH][bIso] ) - { - case 1: /* SABS */ - nModeFlagsValue |= REQ_MODE_STEREO | REQ_MODE_ISO_STEREO; - break; - case 2: - nModeFlagsValue |= REQ_MODE_STEREO | REQ_MODE_ISO_STEREO | REQ_MODE_RELATIVE_STEREO; - break; - case 3: - nModeFlagsValue |= REQ_MODE_STEREO | REQ_MODE_ISO_STEREO | REQ_MODE_RACEMIC_STEREO; - } - } - - max_components = pOneInput->pInpInChI[iInChI][iMobileH]? - pOneInput->nNumComponents[iInChI][iMobileH] : 0; - - for ( k = num_components = 0; k < max_components; k ++ ) - { - pInChI = pOneInput->pInpInChI[iInChI][iMobileH] + k; - ret = CountStereoTypes(pInChI, - &num_known_SB, &num_known_SC, - &num_unk_und_SB, &num_unk_und_SC, - &num_SC_PIII, &num_SC_AsIII); - if ( ret < 0 ) - { - return ret; /* error */ - } - bHasStereo += (ret == 2); - if ( (ret > 0) ) - { - /* ret == 0 => Empty InChI, 1=> No Stereo, 2=> Has Stereo */ - num_components ++; - *bHasReconnected |= ( iInChI == INCHI_REC ); - *bHasFixedH |= ( iMobileH == TAUT_NON ); - } - *bHasMetal |= bInChIHasReconnectedMetal( pInChI ); - } - } - } - - if ( (nModeFlagsValue & REQ_MODE_RELATIVE_STEREO) && (nModeFlagsValue & REQ_MODE_RACEMIC_STEREO) ) - { - return RI_ERR_SYNTAX; - } - if ( bHasStereo && !nModeFlagsValue ) /* REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU*/ - { - /* inversion does not change the stereo or no stereo at all */ - nModeFlagsValue = REQ_MODE_STEREO | REQ_MODE_ISO_STEREO; /* Abs */ - } - - if ( !num_known_SB && num_unk_und_SB ) - { - ; /* full SUU option or SB part of it */ - } else - { - nModeFlagsValue |= REQ_MODE_SB_IGN_ALL_UU; /* ignore Unknown/Undefind SB if no well-defined SB exist */ - } - - if ( !num_known_SC && num_unk_und_SC ) - { - ; /* full SUU option or SB part of it */ - } else - { - nModeFlagsValue |= REQ_MODE_SC_IGN_ALL_UU; /* ignore Unknown/Undefind SC if no well-defined SB exist */ - } - - /* Phosphine and Arsine Stereo */ - if ( num_SC_PIII ) - { - bTautFlagsValue |= TG_FLAG_PHOSPHINE_STEREO; - } - /* Phosphine and Arsine Stereo */ - if ( num_SC_AsIII ) - { - bTautFlagsValue |= TG_FLAG_ARSINE_STEREO; - } - - *nModeFlagsStereo = nModeFlagsValue; - *bTautFlagsStereo = bTautFlagsValue; - - return 0; -} - -/******************************************************************************************************/ -int bInChIHasReconnectedMetal( INChI *pInChI ) -{ - int i; - if ( pInChI && !pInChI->bDeleted && pInChI->nNumberOfAtoms && pInChI->nAtom ) - { - for ( i = 0; i < pInChI->nNumberOfAtoms; i ++ ) - { - if ( is_el_a_metal( (int)pInChI->nAtom[i] ) ) - { - if ( pInChI->nNumberOfAtoms > 1 || pInChI->nNum_H && pInChI->nNum_H[0] ) - { - return 1; - } - } - } - } - return 0; -} - -/*****************************************************************************************/ -int SetProtonsAndXchgIsoH( int bInChI2Structure, int bReqSplitOutputInChI, int bReqProtonsForEachComponent, - int bReqNonTaut, int bReqStereo, int num_components[INCHI_NUM], - MODE_PIXH nModeProtonIsoExchgH[INCHI_NUM], InpInChI *OneInput ) -{ - int j, k, k1, ret2=0, iINChI; - int bAvailableProtonsForEachComponent, bAvailableProtonsTotal; - - INCHI_HEAPCHK - - num_components[INCHI_BAS] = num_components[INCHI_REC] = 0; - - for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) { - nModeProtonIsoExchgH[iINChI] = MODE_PIXH_UNDEFINED; - /* are totals of /p and/or /i/h available ? */ - bAvailableProtonsTotal = 0 != OneInput->nNumProtons[iINChI][TAUT_YES].nNumRemovedProtons; - for ( k1 = 0; k1 < NUM_H_ISOTOPES; k1 ++ ) { - bAvailableProtonsTotal |= 0 !=OneInput->nNumProtons[iINChI][TAUT_YES].nNumRemovedIsotopicH[k1]; - } - /* are /p and/or /i/h available for each component ? */ - bAvailableProtonsForEachComponent = (NULL != OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons); - /* decision: add /p to each component, add total to the 1st, add total as one more component */ - /* In case of bInChI2Structure just keep totals if not available for each component */ - if ( bInChI2Structure ) { - nModeProtonIsoExchgH[iINChI] = bAvailableProtonsForEachComponent? - MODE_PIXH_ADD_TO_EACH: - MODE_PIXH_KEEP_TOTALS; - } else - if ( !bReqSplitOutputInChI ) { - nModeProtonIsoExchgH[iINChI] = bAvailableProtonsForEachComponent? - MODE_PIXH_ADD_TO_EACH : - MODE_PIXH_ADD_TO_FIRST; - } else - if ( !bAvailableProtonsForEachComponent ) { - nModeProtonIsoExchgH[iINChI] = bAvailableProtonsTotal? - MODE_PIXH_ADD_A_PIXH_COMPONENT : - MODE_PIXH_ADD_TO_FIRST; - } else - /* bAvailableProtonsForEachComponent && bReqSplitOutputInChI */ - if ( bReqProtonsForEachComponent ) { - nModeProtonIsoExchgH[iINChI] = MODE_PIXH_ADD_TO_EACH; - } else { - nModeProtonIsoExchgH[iINChI] = bReqNonTaut? - MODE_PIXH_ADD_TO_EACH : - MODE_PIXH_ADD_A_PIXH_COMPONENT; - } - /* remove unneeded data: protons for each component */ - if ( bAvailableProtonsForEachComponent && - nModeProtonIsoExchgH[iINChI] != MODE_PIXH_ADD_TO_EACH ) { - inchi_free( OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons ); - OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons = NULL; - bAvailableProtonsForEachComponent = 0; - } - /* remove unneeded data: total protons all components */ - if ( bAvailableProtonsTotal && nModeProtonIsoExchgH[iINChI] == MODE_PIXH_ADD_TO_EACH ) { - OneInput->nNumProtons[iINChI][TAUT_YES].nNumRemovedProtons = 0; - for ( k1 = 0; k1 < NUM_H_ISOTOPES; k1 ++ ) { - OneInput->nNumProtons[iINChI][TAUT_YES].nNumRemovedIsotopicH[k1] = 0; - } - bAvailableProtonsTotal = 0; - } - /* remove unneeded data: Fixed-H InChI; no protons data exist for Fixed-H */ - if ( !bReqNonTaut && OneInput->nNumComponents[iINChI][TAUT_NON] ) { - j = TAUT_NON; - for ( k = 0; k < OneInput->nNumComponents[iINChI][j]; k ++ ) { - Free_INChI_Members( &OneInput->pInpInChI[iINChI][j][k] ); - } - inchi_free( OneInput->pInpInChI[iINChI][j] ); - OneInput->pInpInChI[iINChI][j] = NULL; - OneInput->nNumComponents[iINChI][j] = 0; - } -#ifdef NEVER - /* remove unneeded data: Mobile-H InChI ????? */ - if ( bReqNonTaut && OneInput->nNumComponents[iINChI][TAUT_NON] ) { - j = TAUT_YES; - for ( k = 0; k < OneInput->nNumComponents[iINChI][j]; k ++ ) { - Free_INChI_Members( &OneInput->pInpInChI[iINChI][j][k] ); - } - inchi_free( OneInput->pInpInChI[iINChI][j] ); - OneInput->pInpInChI[iINChI][j] = NULL; - OneInput->nNumComponents[iINChI][j] = 0; - nModeProtonIsoExchgH[iINChI] = MODE_PIXH_UNDEFINED; - if ( OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons ) { - inchi_free( OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons); - OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons = NULL; - } - } -#endif - /* add one more component containing only /p and /i/h */ - if ( nModeProtonIsoExchgH[iINChI] == MODE_PIXH_ADD_A_PIXH_COMPONENT && - OneInput->nNumComponents[iINChI][TAUT_YES] || - /* always add one deleted component if no non-taut InChI is available */ - bInChI2Structure && !bAvailableProtonsForEachComponent && - !OneInput->nNumComponents[iINChI][TAUT_NON] && - OneInput->nNumComponents[iINChI][TAUT_YES] ) { - int nPrevLen, nLen=0; - j = TAUT_YES; - nPrevLen = OneInput->nNumComponents[iINChI][j]; - for ( k = 0; k < nPrevLen; k ++ ) { - nLen += !OneInput->pInpInChI[iINChI][j][k].bDeleted; - } - if ( nLen == nPrevLen ) { - /* add one more component */ - INChI *pInChI = (INChI *)inchi_calloc( nLen+1, sizeof(*pInChI) ); - if ( !pInChI ) { - ret2 = RI_ERR_ALLOC; - goto exit_error; - } - memcpy( pInChI, OneInput->pInpInChI[iINChI][j], nLen*sizeof(*pInChI) ); - inchi_free( OneInput->pInpInChI[iINChI][j] ); - OneInput->pInpInChI[iINChI][j] = pInChI; - } - OneInput->nNumComponents[iINChI][j] = nLen+1; - - for ( k = nLen; k < nPrevLen; k ++ ) { - Free_INChI_Members( &OneInput->pInpInChI[iINChI][j][k] ); - memset( &OneInput->pInpInChI[iINChI][j][k], 0, sizeof(OneInput->pInpInChI[iINChI][j][k])); - } - /* mark the last component as a proton */ - if ( 0 > (ret2 = nFillOutProtonMobileH( OneInput->pInpInChI[iINChI][j]+nLen ) ) ) { - goto exit_error; - } - } - INCHI_HEAPCHK - - /* remove unneeded Stereo and/or Fixed H */ - if ( !bReqStereo ) { - for ( j = 0; j < TAUT_NUM; j ++ ) { - for ( k = 0; k < OneInput->nNumComponents[iINChI][j]; k ++ ) { - if ( OneInput->pInpInChI[iINChI][j][k].Stereo ) { - Free_INChI_Stereo(OneInput->pInpInChI[iINChI][j][k].Stereo); - inchi_free( OneInput->pInpInChI[iINChI][j][k].Stereo ); - OneInput->pInpInChI[iINChI][j][k].Stereo = NULL; - } - if ( OneInput->pInpInChI[iINChI][j][k].StereoIsotopic ) { - Free_INChI_Stereo(OneInput->pInpInChI[iINChI][j][k].StereoIsotopic); - inchi_free( OneInput->pInpInChI[iINChI][j][k].StereoIsotopic ); - OneInput->pInpInChI[iINChI][j][k].StereoIsotopic = NULL; - } - INCHI_HEAPCHK - } - } - } - } - - num_components[INCHI_BAS] = inchi_max( OneInput->nNumComponents[INCHI_BAS][TAUT_YES], - OneInput->nNumComponents[INCHI_BAS][TAUT_NON] ); - num_components[INCHI_REC] = inchi_max( OneInput->nNumComponents[INCHI_REC][TAUT_YES], - OneInput->nNumComponents[INCHI_REC][TAUT_NON] ); - -exit_error: - return ret2; -} -/******************************************************************************************/ -int GetInChIFormulaNumH( INChI *pInChI, int *nNumH ) -{ /* get number of H including bridging hydrogen atoms */ - const char *p, *q; - *nNumH = 0; - if ( pInChI->szHillFormula ) { - for ( p = strchr( pInChI->szHillFormula, 'H'); p; p = strchr(p, 'H') ) { - p ++; - if ( !islower( UCINT *p ) ) { - /* found hydrogen in the formula */ - if ( isdigit( UCINT *p ) ) { - *nNumH += (int)inchi_strtol( p, &q, 10 ); - p = q; - } else { - *nNumH += 1; - } - } - } - } - return 0; -} -/******************************************************************************************/ -int GetInChINumH( INChI *pInChI, int *nNumH ) -{ - int i, j, nNumTautGroups, iTautGroup, nTautGroupLen, lenTautomer; - *nNumH = 0; - for ( i = 0; i < pInChI->nNumberOfAtoms; i ++ ) { - *nNumH += ( pInChI->nAtom[i] == EL_NUMBER_H ); /* bridging H */ - *nNumH += pInChI->nNum_H[i]; - } - /* earlier nNum_H_fixed[] should have been added to pInChI->nNum_H[] */ - /* - if ( pInChI->nNum_H_fixed ) { - for ( i = 0; i < pInChI->nNumberOfAtoms; i ++ ) { - *nNumH += pInChI->nNum_H_fixed[i]; - } - } - */ - if ( pInChI->lenTautomer > 3 && pInChI->nTautomer ) { - lenTautomer = pInChI->lenTautomer; - j = 0; - nNumTautGroups = pInChI->nTautomer[j ++]; - for ( iTautGroup = 0; j < lenTautomer && iTautGroup < nNumTautGroups; iTautGroup ++, j += nTautGroupLen ) { - nTautGroupLen = pInChI->nTautomer[j]+1; - *nNumH += pInChI->nTautomer[j+1]; - } - if ( iTautGroup != nNumTautGroups || j != lenTautomer ) { - return RI_ERR_PROGR; - } - } - if ( pInChI->nNum_H_fixed && (pInChI->lenTautomer || pInChI->nTautomer) ) { - return RI_ERR_PROGR; - } - return 0; -} -/******************************************************************************************/ -int GetInChIIsoH( INChI *pInChI, int nNumIsotopicH[NUM_H_ISOTOPES] ) -{ - int i; - for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { - nNumIsotopicH[i] = 0; - } - for ( i = 0; i < pInChI->nNumberOfIsotopicAtoms; i ++ ) { - if ( pInChI->IsotopicAtom[i].nIsoDifference > 0 && - pInChI->IsotopicAtom[i].nIsoDifference <= NUM_H_ISOTOPES ) { - if ( !pInChI->nAtom || - !pInChI->IsotopicAtom[i].nAtomNumber || - pInChI->IsotopicAtom[i].nAtomNumber > pInChI->nNumberOfAtoms ) { - return RI_ERR_PROGR; - } - if ( pInChI->nAtom[pInChI->IsotopicAtom[i].nAtomNumber-1] == EL_NUMBER_H ) { - /* isotopic H in connection table */ - nNumIsotopicH[ pInChI->IsotopicAtom[i].nIsoDifference-1 ] ++; - } - } - nNumIsotopicH[0] += pInChI->IsotopicAtom[i].nNum_H; - nNumIsotopicH[1] += pInChI->IsotopicAtom[i].nNum_D; - nNumIsotopicH[2] += pInChI->IsotopicAtom[i].nNum_T; - } - return 0; -} -/******************************************************************************************/ -typedef struct tagNumElem -{ - int num; - /* - int iso; - */ -} NUM_ELEM; - - -/***************************************************************************************/ -int InChILine2Data(INCHI_IOSTREAM *pInp, SEGM_LINE *pLine, - char **pStr, int *pState, int *nErr, - INChI *pInpInChI[INCHI_NUM][TAUT_NUM], - int nNumComponents[INCHI_NUM][TAUT_NUM], - REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM], - int s[INCHI_NUM][TAUT_NUM][2], - int bReadCoord, int bInchi2Struct, INCHI_MODE nMode, - int *bStdFormat, int *bInputHasSaveOpt, unsigned char *inp_save_opt_bits) -{ - int iINChI, i, j, k, m, len1, len2, ret2 = 0, retAux = 0, stateAux = 0; - int ret, tot_charge[INCHI_NUM][TAUT_NUM]; - int i1, i2, i3; - int kc; - NUM_ELEM *num_elem[INCHI_NUM][TAUT_NUM]; - - -#if ( FIX_I2I_STEREOCONVERSION_BUG == 1 ) -/* (2008-03-06) 1=> Fix bug of i2i conversion SAbs-->(SRel||Srac) */ -/* (converter does not placed proper stereo to output) */ - - /* set new stereo type as requested by conversion option */ - int target_stereo_type = 1; - if (nMode & REQ_MODE_RELATIVE_STEREO) - target_stereo_type = 2; - else if (nMode & REQ_MODE_RACEMIC_STEREO) - target_stereo_type = 3; -#endif - - - memset( num_elem, 0, sizeof(num_elem) ); - - ret = ReadInChILine(pInp, pLine, pStr, pState, pInpInChI, - nNumComponents, nNumProtons, s, - bStdFormat, bInputHasSaveOpt, inp_save_opt_bits); - - -#if ( FIX_I2I_STEREOCONVERSION_BUG == 1 ) - /* modify stereo type for layers as requested */ - if (target_stereo_type > 1) - for (i1=0;i1SAbs, SRac=>SAbs */ - s[i1][i2][i3] = target_stereo_type; - } -#endif - - - *nErr = 0; - if ( (ret == RI_ERR_EOL) && - nNumComponents[INCHI_BAS][TAUT_YES] - + nNumComponents[INCHI_BAS][TAUT_NON] && bReadCoord ) { - retAux = ReadInChICoord( pInp, pLine, &stateAux, pInpInChI, nNumComponents ); - } - if ( (ret == RI_ERR_EOL || ret == RI_ERR_EOF) && - nNumComponents[INCHI_BAS][TAUT_YES] - + nNumComponents[INCHI_BAS][TAUT_NON] ) { - /* post-processing: add omitted layers */ - *pState = IST_MATERIAL_BALANCE_ERROR; - for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) - { - - for ( j = 0; j < TAUT_NUM; j ++ ) - { - /* for Mobile/Fixed H (j) ... */ - - int bIsotopic, bStereoType, bStereoTypeAlt; - int nMH2FH_AltInv=0, nFH2iFH_AltInv=0 /*, niMH2iFH_AltInv=0, nMH2iMH_AltInv=0*/; - int jAlt = ALT_TAUT(j); - INCHI_MODE nFlags = 0, nFlagsAlt = 0; - /* get stereo type: ABS, REL, RAC, or nothing */ - tot_charge[iINChI][j] = 0; - for ( bIsotopic = bStereoType = bStereoTypeAlt = 0; bIsotopic < 2; bIsotopic ++ ) { - if ( !bStereoType || bStereoType < s[iINChI][j][bIsotopic] ) { - bStereoType = s[iINChI][j][bIsotopic]; - } - if ( !bStereoTypeAlt || bStereoTypeAlt < s[iINChI][jAlt][bIsotopic] ) { - bStereoTypeAlt = s[iINChI][jAlt][bIsotopic]; - } - nFlags = bStereoType ==2? INCHI_FLAG_REL_STEREO : bStereoType ==3? INCHI_FLAG_RAC_STEREO : 0; - nFlagsAlt = bStereoTypeAlt==2? INCHI_FLAG_REL_STEREO : bStereoTypeAlt==3? INCHI_FLAG_RAC_STEREO : 0; - } - /* set stereo type to each component */ - /* add missing nNum_H and nConnTable */ - if ( nNumComponents[iINChI][j] ) { - num_elem[iINChI][j] = (NUM_ELEM *)inchi_calloc( nElDataLen+1, sizeof(num_elem[0][0][0]) ); - if ( !num_elem[iINChI][j] ) { - ret2 = RI_ERR_ALLOC; - goto exit_function; - } - } - for ( k = 0; k < nNumComponents[iINChI][j]; k ++ ) - { - /* for each component k ... */ - - if ( pInpInChI[iINChI][j] ) { - INChI *pInChI = &pInpInChI[iINChI][j][k]; - INChI *pInChI_Alt = (knNumberOfAtoms)? pInpInChI[iINChI][jAlt]:NULL;*/ /* 2007-09-25 DT */ - pInpInChI[iINChI][jAlt][k].nNumberOfAtoms)? &pInpInChI[iINChI][jAlt][k]:NULL; - - if ( nFlags ) { - pInChI->nFlags |= nFlags; - } else - if ( j == TAUT_NON && !nFlags && nFlagsAlt ) { - pInChI->nFlags |= nFlagsAlt; - } - /**** add empty immobile H (nNum_H) if it is missing ****/ - if ( !pInChI->nNum_H && - !(pInChI->nNum_H = (S_CHAR *)inchi_calloc( pInChI->nNumberOfAtoms+1, sizeof(pInChI->nNum_H[0]) ) ) ) { - ret2 = RI_ERR_ALLOC; - goto exit_function; - } - /**** add single atom nConnTable if it is missing ****/ - if ( !pInChI->nConnTable ) { - AT_NUMB *pCT; - int lenCT; - if ( j == TAUT_NON && k < nNumComponents[iINChI][TAUT_YES] && - (pCT = pInpInChI[iINChI][TAUT_YES][k].nConnTable) && - (lenCT = pInpInChI[iINChI][TAUT_YES][k].lenConnTable) > 0) { - if ( !(pInChI->nConnTable = (AT_NUMB *)inchi_calloc( lenCT+1, sizeof(pInChI->nConnTable[0]) ) ) ) { - ret2 = RI_ERR_ALLOC; - goto exit_function; - } - memcpy( pInChI->nConnTable, pCT, lenCT*sizeof(pInChI->nConnTable[0]) ); - pInChI->lenConnTable = lenCT; - } else { - if ( j == TAUT_YES && pInChI->nNumberOfAtoms > 1 ) { - *pState = IST_MOBILE_H_CONNECTIONS + (iINChI==INCHI_REC? IST_HAPPENED_IN_RECMET : 0); - ret2 = RI_ERR_SYNTAX; - goto exit_function; - } - if ( !(pInChI->nConnTable = (AT_NUMB *)inchi_calloc( pInChI->nNumberOfAtoms+1, sizeof(pInChI->nConnTable[0]) ) ) ) { - ret2 = RI_ERR_ALLOC; - goto exit_function; - } - pInChI->lenConnTable = 1; - pInChI->nConnTable[0] = 1; - } - } else - if ( pInChI->nConnTable && !pInChI->lenConnTable && pInChI->nNumberOfAtoms == 1 ) { - pInChI->nConnTable[0] = 1; - pInChI->lenConnTable = 1; - } - /**** copy charge: Mobile H --> Fixed H; ****/ - if ( j == TAUT_NON ) { - /* - if ( pInChI->nTotalCharge == NO_VALUE_INT ) { - pInChI->nTotalCharge = 0; - } else - */ - if ( !pInChI->nTotalCharge && k < nNumComponents[iINChI][TAUT_YES] ) { - INChI *pAltInChI = &pInpInChI[iINChI][TAUT_YES][k]; /* Mobile H InChI */ - if ( pAltInChI->nTotalCharge && pAltInChI->nTotalCharge != NO_VALUE_INT ) { - pInChI->nTotalCharge = pAltInChI->nTotalCharge; - } - } - } - /***** Fixed H: add pInChI->nNum_H_fixed to pInChI->nNum_H ****/ - if ( j == TAUT_NON && pInChI->nNum_H && pInChI->nNum_H_fixed ) { - for ( m = 0; m < pInChI->nNumberOfAtoms; m ++ ) { - pInChI->nNum_H[m] += pInChI->nNum_H_fixed[m]; - } - } - /***** copy isotopic atoms: Mobile H --> Fixed H ******/ - if ( j == TAUT_YES && pInChI->nNumberOfIsotopicAtoms && - k < nNumComponents[iINChI][TAUT_NON] ) { - INChI *pAltInChI = &pInpInChI[iINChI][TAUT_NON][k]; /* Fixed H InChI */ - - if ( !pAltInChI->nNumberOfIsotopicAtoms ) { - ret2=CopySegment( pAltInChI, pInChI, CPY_ISO_AT, 0, 0); - if ( ret2 < 0 ) { - goto exit_function; - } - } - } - /**** copy coordinates: Mobile H --> Fixed H ******/ - if ( j == TAUT_YES && pInChI->IsotopicTGroup && - k < nNumComponents[iINChI][TAUT_NON] ) { - INChI *pAltInChI = &pInpInChI[iINChI][TAUT_NON][k]; /* Fixed H InChI */ - - if ( !pAltInChI->IsotopicTGroup ) { - XYZ_COORD *pxyz = (XYZ_COORD *)inchi_calloc( pInChI->nNumberOfAtoms, sizeof(pxyz[0])); - if ( pxyz ) { - memcpy( pxyz, pInChI->IsotopicTGroup, pInChI->nNumberOfAtoms * sizeof(pxyz[0]) ); - pAltInChI->IsotopicTGroup = (INChI_IsotopicTGroup *)pxyz; - } else { - ret2 = RI_ERR_ALLOC; - goto exit_function; - } - } - } - - /******************************************************** - * * - * Restore omitted stereo seqments * - * * - * order of restoring: * - * * - * 1. Fixed H (F) -> (FI) Isotopic Fixed H * - * 2. Mobile H (M) -> (F) Fixed H * - * 3. Isotopic Mobile H (MI) -> (FI) Isotopic Fixed H * - * 4. Mobile H (M) -> (MI) Isotopic Mobile H * - * * - ********************************************************/ - - /***** (4) copy stereo: Mobile H --> isotopic Mobile H ******/ - if ( j == TAUT_YES ) { - int bIso = pInChI->nNumberOfIsotopicAtoms || - (pInChI->StereoIsotopic && - pInChI->StereoIsotopic->nNumberOfStereoCenters - + pInChI->StereoIsotopic->nNumberOfStereoBonds) || - pInChI_Alt && pInChI_Alt->nNumberOfIsotopicAtoms; - - /* non-isotopic Mobile H => isotopic Mobile H */ - if ( bIso ) { - if ( pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters && - (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->t_parity) ) { - if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3, 1, 0)) || - (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) && - 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3_M, 1, 0))) { - goto exit_function; - } - if ( (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { - if ( pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT ) { - pInChI->Stereo->nCompInv2Abs = s[iINChI][j][0]>0? 2 : 0; - } - if ( pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { - pInChI->StereoIsotopic->nCompInv2Abs = s[iINChI][j][1]>0? 2 : 0; - } - } - } else - /* copy sp3 inversion info: non-isotopic Mobile H => isotopic Mobile H */ - if ( pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters && - pInChI->StereoIsotopic && pInChI->StereoIsotopic->nNumberOfStereoCenters && - pInChI->Stereo->nCompInv2Abs ) { - if ( (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) && - pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT && - pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { - pInChI->Stereo->nCompInv2Abs = s[iINChI][j][0]>0? 2 : 0; - pInChI->StereoIsotopic->nCompInv2Abs = s[iINChI][j][1]>0? 2 : 0; - } else - if (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs ) { - pInChI->StereoIsotopic->nCompInv2Abs = pInChI->Stereo->nCompInv2Abs; - } - } - } - if ( bIso && - pInChI->Stereo && pInChI->Stereo->nNumberOfStereoBonds && - (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->b_parity) ) { - if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP2, 1, 0)) ) { - goto exit_function; - } - } - } - /***** (0) set nCompInv2Abs to Fixed-H *********************************/ - if ( j == TAUT_NON ) { - if ( pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters && - pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT ) { - /* case of /sN and /t... in non-isotopic Mobile-H, no /s in non-isotopic Fixed-H */ - if ( !s[iINChI][j][0] && s[iINChI][jAlt][0]>0 && /* /sN is not present in F and is present in M */ - pInChI_Alt && pInChI_Alt->Stereo && pInChI_Alt->Stereo->nNumberOfStereoCenters ) { - /* inherit from Mobile-H */ - /* /s1 in M and MI; /m1 or /m0 in MI; /m. in M; no /m in F. Inherit MI->FI. Added 10-15-2007 */ - if ( pInChI_Alt->Stereo->nCompInv2Abs == 0 && /* M: /m. ; means no /m for this component */ - pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT && /* F: no /m segment for all components */ - pInChI_Alt->StereoIsotopic && /* MI: present */ - pInChI_Alt->StereoIsotopic->nCompInv2Abs != 0 && - pInChI_Alt->StereoIsotopic->nCompInv2Abs != NO_VALUE_INT && /* MI: /m0 or /m1 */ - !s[iINChI][j][0] && !s[iINChI][j][1] && /* F, FI: no /s */ - s[iINChI][jAlt][0] == 1 && s[iINChI][jAlt][1] == 1 /* M, MI: /s1 and /s1 */ - ) { - /* copy /m from MI to FI */ - if ( 0 > (ret2 = CopySegment( pInChI, pInChI_Alt, CPY_SP3_M, 1, 1)) ) { - goto exit_function; - } - } else - /* the following if(){...} was added to fix m1 bug 2007-09-25 DT */ - if ( pInChI_Alt->Stereo->nCompInv2Abs != NO_VALUE_INT && s[iINChI][jAlt][0] == 1 ) { - pInChI->Stereo->nCompInv2Abs = pInChI_Alt->Stereo->nCompInv2Abs; - } else - /* M and MI contain /sN and /sN, N=2,3. Added 10-15-2007 */ - if ( pInChI_Alt->Stereo->nCompInv2Abs == NO_VALUE_INT && - pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT && - !s[iINChI][j][0] && !s[iINChI][j][1] && - (s[iINChI][jAlt][0] & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) && - (s[iINChI][jAlt][1] & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { - int bIso = pInChI->nNumberOfIsotopicAtoms || - (pInChI->StereoIsotopic && - pInChI->StereoIsotopic->nNumberOfStereoCenters - + pInChI->StereoIsotopic->nNumberOfStereoBonds) || - pInChI_Alt && pInChI_Alt->nNumberOfIsotopicAtoms; - if ( bIso ){ - if ( !pInChI_Alt->StereoIsotopic && /* create zero/NULL-initialized pInChI_Alt->StereoIsotopic */ - 0 > (ret2 = CopySegment( pInChI_Alt, pInChI_Alt, CPY_SP3_M, 1, -1))) { - goto exit_function; - } - pInChI_Alt->StereoIsotopic->nCompInv2Abs = 2; /* MI: /m1 or /m0 */ - pInChI_Alt->Stereo->nCompInv2Abs = 0; /* M: /m. ; no /m for this component */ - pInChI->Stereo->nCompInv2Abs = NO_VALUE_INT+1; /* FI: Stereo->CompInv2Abs=0, StereoIsotopic->CompInv2Abs=1 or -1 */ - } else { - pInChI->Stereo->nCompInv2Abs = 2; /* F: /m1 or /m0, omitted from InChI as a repetition */ - pInChI_Alt->Stereo->nCompInv2Abs = 2; /* M: /m1 or /m0; in Srel/SRac case the value = 2 */ - } - } else { - pInChI->Stereo->nCompInv2Abs = 2; /* F: /m1 or /m0, omitted from InChI as a repetition */ - pInChI_Alt->Stereo->nCompInv2Abs = 2; /* M: /m1 or /m0; in Srel/SRac case the value = 2 */ - } - } else - /* case of /sN in Isotopic Fixed-H only, /t... in Fixed-H, no /m (2007-08-27 DT) */ - if ( !s[iINChI][j][0] && !s[iINChI][jAlt][0] && /* /sN in Fixed-H isotopic only */ - (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) && - !(pInChI->StereoIsotopic && pInChI->StereoIsotopic->nNumberOfStereoCenters) && - /*!(pInChI_Alt && pInChI_Alt->Stereo && pInChI_Alt->Stereo->nNumberOfStereoCenters) &&*/ - !(pInChI_Alt && pInChI_Alt->StereoIsotopic && pInChI_Alt->StereoIsotopic->nNumberOfStereoCenters) ) { - pInChI->Stereo->nCompInv2Abs = NO_VALUE_INT+1; /* Stereo->CompInv2Abs=0, StereoIsotopic->CompInv2Abs=1 or -1 */ - } else { - pInChI->Stereo->nCompInv2Abs = s[iINChI][j][0]>0? 2 : 0; - } - } - } - /***** (1) copy stereo: non-isotopic Fixed H --> isotopic Fixed H ******/ - if ( j == TAUT_NON ) { - int bIso = pInChI->nNumberOfIsotopicAtoms || - (pInChI->StereoIsotopic && - pInChI->StereoIsotopic->nNumberOfStereoCenters - + pInChI->StereoIsotopic->nNumberOfStereoBonds) || - pInChI_Alt && pInChI_Alt->nNumberOfIsotopicAtoms; - /* non-isotopic Fixed H => isotopic Fixed H */ - if ( bIso ) { - if ( pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters && - (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->t_parity) ) { - /* -- replaced 2007-08-27 by (aaa), see below -- DT - if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3, 1, 0)) || - !(pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) && - 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3_M, 1, 0))) { - goto exit_function; - } - */ - /*----------- replacement (aaa) begin 2007-08-27 DT */ - if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3, 1, 0)) ) { - goto exit_function; - } - if ( pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT+1 ) { - pInChI->Stereo->nCompInv2Abs = 0; - pInChI->StereoIsotopic->nCompInv2Abs = 2; - } else - if ( !(pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) && - 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3_M, 1, 0))) { - goto exit_function; - } - /*----------- replacement (aaa) end 2007-08-27 DT */ - if ( (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { - if ( pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT ) { - pInChI->Stereo->nCompInv2Abs = s[iINChI][j][0]>0? 2 : 0; - } - if ( pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { - pInChI->StereoIsotopic->nCompInv2Abs = s[iINChI][j][1]>0? 2 : 0; - } - } -#ifdef NEVER - if ( (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) && - !s[iINChI][j][0] && s[iINChI][j][0]>0 ) { - /* copied Rel/Rac stereo to Iso; /s is in Iso /s is not in non-Iso */ - /* this means all difference in stereo is in inversion */ - if ( pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT && - pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { - pInChI->Stereo->nCompInv2Abs = 0; /* missing */ - pInChI->StereoIsotopic->nCompInv2Abs = 2; /* unusual value */ - } - } -#endif - } else - /* copy sp3 inversion info: non-isotopic Fixed H --> isotopic Fixed H */ - if ( pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters && - pInChI->StereoIsotopic && pInChI->StereoIsotopic->nNumberOfStereoCenters && - pInChI->Stereo->nCompInv2Abs ) { - if ( (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) && - pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT && - pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { - pInChI->Stereo->nCompInv2Abs = s[iINChI][j][0]>0? 2 : 0; - pInChI->StereoIsotopic->nCompInv2Abs = s[iINChI][j][1]>0? 2 : 0; - } else - if (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) { - pInChI->StereoIsotopic->nCompInv2Abs = pInChI->Stereo->nCompInv2Abs; - } - } - } - if ( bIso && - pInChI->Stereo && pInChI->Stereo->nNumberOfStereoBonds && - (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->b_parity) ) { - if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP2, 1, 0)) ) { - goto exit_function; - } - } - } - - /***** copy stereo: Mobile H --> Fixed H ******/ - if ( j == TAUT_NON && k < nNumComponents[iINChI][TAUT_YES] ) { - INChI *pAltInChI = &pInpInChI[iINChI][TAUT_YES][k]; /* Mobile H InChI */ - int bIso = pInChI->nNumberOfIsotopicAtoms || - (pInChI->StereoIsotopic && - pInChI->StereoIsotopic->nNumberOfStereoCenters - + pInChI->StereoIsotopic->nNumberOfStereoBonds) || - pAltInChI && ( - pAltInChI->nNumberOfIsotopicAtoms || - (pAltInChI->StereoIsotopic && - pAltInChI->StereoIsotopic->nNumberOfStereoCenters - + pAltInChI->StereoIsotopic->nNumberOfStereoBonds) ); - int bNo_InChI_t = (!pInChI->Stereo || !pInChI->Stereo->t_parity); - int bNo_InChI_m = (!pInChI->Stereo || NO_VALUE_INT == pInChI->Stereo->nCompInv2Abs); - - /* (2) non-isotopic Mobile H => non-isotopic Fixed H */ - if ( pAltInChI->Stereo && pAltInChI->Stereo->nNumberOfStereoCenters && - (!pInChI->Stereo || !pInChI->Stereo->t_parity) ) - { -#if ( FIX_I2I_STEREOCONVERSION_BUG2 == 1 ) - /* (2008-04-02) 1=> Fix bug of i2i conversion SAbs-->(SRel||Srac) */ - /* (converter skipped empty '/t' or sometimes produced an excess one */ - - /* check whether t stereo is actually present */ - int bHave_t_stereo = 1; - if (pInChI->Stereo) - bHave_t_stereo = pInChI ->Stereo->nNumberOfStereoCenters; - /* account for stereobonds present */ - if ( bHave_t_stereo < 1 ) - if ( pInChI->Stereo->nNumberOfStereoBonds > 0 ) - bHave_t_stereo=1; - /* copy stereo anyway ... */ -#endif - if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3, 0, 0)) || - (!pInChI->Stereo->nCompInv2Abs || NO_VALUE_INT == pInChI->Stereo->nCompInv2Abs) && - 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3_M, 0, 0)) ) - { - goto exit_function; - } - -#if ( FIX_I2I_STEREOCONVERSION_BUG2 == 1 ) - /* ... correct just copied stereo if applicable */ - if ( (s[iINChI][j][0] < 1) && - (bHave_t_stereo <1 ) && - (pAltInChI->Stereo->nNumberOfStereoCenters > 0) && - (s[iINChI][jAlt][0] < 1) ) - { - /* (2010-02-28) if not all stereo centers are unknown/undefined */ - /* at which condition stereo still should present .. */ - int all_UU = 1; - for (kc=0; kcStereo->nNumberOfStereoCenters; kc++) - { - if ( (pAltInChI->Stereo->t_parity[kc] != AB_PARITY_UNKN) && - (pAltInChI->Stereo->t_parity[kc] != AB_PARITY_UNDF) ) - { - all_UU=0; - break; - } - } - if (!all_UU) - pInChI->Stereo->nNumberOfStereoCenters = 0; - } -#endif - - /* in case of missing nCompInv2Abs, 2005-05-10 */ - if ( (pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT) && - (nFlagsAlt & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { - if ( s[iINChI][jAlt][0] > 0 && s[iINChI][j][0] > 0 ) { - /* suppose once in a while only non-taut stereo changes if inverted */ - pAltInChI->Stereo->nCompInv2Abs = (++nMH2FH_AltInv)%2? 2:0; - pInChI->Stereo->nCompInv2Abs = 2; - } else - /* Mobile-H: /t.. /sN; Mobile-H isotopic: /sN (n=2 or 3), not /t...; Fixed-H layer is present, has no /t, no /i/t */ - /* Mobile-H /sN was caused by another component that would have same /mN in all layers */ - /* therefore, in case of Abs. Stereo, Mobile-H stereo isotopic stereo would have /m1 */ - /* In case of Rel/Rac stereo, since no /m1 could occur in Mobile-H isotopic, */ - /* no pAltInChI->StereoIsotopic or pInChI->StereoIsotopic have been created yet. */ - /* added 10-11-2007 to fix i2i bug for Rel/Rac stereo */ - if ( nNumComponents[iINChI][j] > 1 && - bNo_InChI_t && bNo_InChI_m /* no /t... or /mN in Fixed-H */ && !nFlags && - !(pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->t_parity) && - !(pInChI->StereoIsotopic && pInChI->StereoIsotopic->t_parity) && - s[iINChI][j][0]==0 && s[iINChI][j][1] == 0 && - /* /sN, N=2 or 3 only in Mobile-H AND Mobile-H isotopic */ - (s[iINChI][jAlt][0] & ((INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO))) && - (s[iINChI][jAlt][1] & ((INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO))) ) - { - if ( bIso ) { - /* create two zero/NULL-initialized isotopic stereo if they do not exist */ - if ( !pInChI->StereoIsotopic && 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3_M, 1, -1)) - /* -- the following will be created later, in TAUT_YES part of the code -- */ - || !pAltInChI->StereoIsotopic && 0 > (ret2 = CopySegment( pAltInChI, pAltInChI, CPY_SP3_M, 1, -1)) ) { - goto exit_function; - } - /* same value = 2 for MI and FI; here we assign only FI */ - pInChI->StereoIsotopic->nCompInv2Abs = 2; - pInChI->Stereo->nCompInv2Abs = 0; - /* -- the following will NOT be assigned later, in TAUT_YES part of the code -- */ - pAltInChI->StereoIsotopic->nCompInv2Abs = 2; - pAltInChI->Stereo->nCompInv2Abs = 0; - /* */ - } else { - if ( NO_VALUE_INT == pInChI->Stereo->nCompInv2Abs && - NO_VALUE_INT == pAltInChI->Stereo->nCompInv2Abs ) { - pInChI->Stereo->nCompInv2Abs = 2; - pAltInChI->Stereo->nCompInv2Abs = 2; - } - } - } else - if ( (s[iINChI][jAlt][0] > 0 || s[iINChI][j][0] > 0) && s[iINChI][j][0] >= 0 ) - pInChI->Stereo->nCompInv2Abs = 2; - else - /* Mobile-H: /t..., no /sN; Mobile-H isotopic: /s2 or /s3, not /t; Fixed-H layer is present, has no /t, no /i/t */ - /* therefore, in case of Abs. Stereo, Mobile-H stereo isotopic stereo would have /m1 */ - /* In case of Rel/Rac stereo, since no /m1 could occur in Mobile-H isotopic, */ - /* no pAltInChI->StereoIsotopic or pInChI->StereoIsotopic have been created yet. */ - /* added 10-10-2007 to fix i2i bug for Rel/Rac stereo */ - if ( bIso && bNo_InChI_t && bNo_InChI_m /* no /t... or /mN in Fixed-H */ && !nFlags && - !(pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->t_parity) && - !(pInChI->StereoIsotopic && pInChI->StereoIsotopic->t_parity) && - s[iINChI][jAlt][0]==0 && s[iINChI][j][0]==0 && s[iINChI][j][1] == 0 && - /* /sN, N=2 or 3 only in Mobile-H isotopic */ - (s[iINChI][jAlt][1] & ((INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO))) ) - { - /* create two zero/NULL-initialized isotopic stereo if they do not exist */ - if ( !pInChI->StereoIsotopic && 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3_M, 1, -1)) - /* -- the following will be created later, in TAUT_YES part of the code -- */ - /*|| !pAltInChI->StereoIsotopic && 0 > (ret2 = CopySegment( pAltInChI, pAltInChI, CPY_SP3_M, 1, -1))*/ ) { - goto exit_function; - } - /* same value = 2 for MI and FI; here we assign only FI */ - pInChI->StereoIsotopic->nCompInv2Abs = 2; - pInChI->Stereo->nCompInv2Abs = 0; - /* -- the following will be assigned later, in TAUT_YES part of the code -- */ - /* - pAltInChI->StereoIsotopic->nCompInv2Abs = 2; - pAltInChI->Stereo->nCompInv2Abs = 0; - */ - } else - pInChI->Stereo->nCompInv2Abs = 0; - if ( !(pInChI->nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { - pInChI->nFlags |= ((nFlagsAlt|nFlags) & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)); - } - } - - } else - /* copy sp3 inversion info: non-isotopic Mobile H => non-isotopic Fixed H */ - if ( pAltInChI->Stereo && pAltInChI->Stereo->nNumberOfStereoCenters && - pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters && - pAltInChI->Stereo->nCompInv2Abs && - (!pInChI->Stereo->nCompInv2Abs || NO_VALUE_INT == pInChI->Stereo->nCompInv2Abs) ) { - if ( !(nFlagsAlt && !nFlags ) || NO_VALUE_INT == pInChI->Stereo->nCompInv2Abs ) { - /* ??? */ - pInChI->Stereo->nCompInv2Abs = pAltInChI->Stereo->nCompInv2Abs; - } - } - - /* use same rule to copy stereobonds */ - if ( pAltInChI->Stereo && pAltInChI->Stereo->nNumberOfStereoBonds && - (!pInChI->Stereo || !pInChI->Stereo->b_parity) ) { - if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP2, 0, 0)) ) { - goto exit_function; - } - } - /* (3) isotopic Mobile H -> isotopic Fixed H */ - /* if !FH_Stereo && !MH_Stereo && MH_IsoStereo!=NULL && FH_IsoStereo==NULL */ - if ( bIso ) { - if ( !(pInChI->Stereo && pInChI->Stereo->t_parity) && /* !FH_Stereo */ - !(pAltInChI->Stereo && pAltInChI->Stereo->t_parity) && /* !MH_Stereo */ - (pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->nNumberOfStereoCenters) && /* MH_IsoStereo */ - (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->t_parity) ) { /* !FH_IsoStereo */ - /* copy sp3 iso stereo MI->FI (/t) and, if FH nCompInv2Abs (/m) is missing, copy it, too, MI->FI */ - if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3, 1, 1)) || - (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) && - 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3_M, 1, 1)) ) { - goto exit_function; - } - /* in case of missing nCompInv2Abs, Relative or Racemic stereo 2005-05-10 */ - if ( pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT && - (nFlagsAlt & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { - pInChI->StereoIsotopic->nCompInv2Abs = s[iINChI][jAlt][1]>0? 2 : 0; - if ( !(pInChI->nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { - pInChI->nFlags |= (nFlagsAlt & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)); - } - } - } else - /* copy sp3 inversion info only: isotopic Mobile H -> isotopic Fixed H */ - if ( !(pInChI->Stereo && pInChI->Stereo->t_parity) && /* !FH_Stereo /t */ - !(pAltInChI->Stereo && pAltInChI->Stereo->t_parity) && /* !MH_Stereo /t */ - (pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->nNumberOfStereoCenters) && /* MH_IsoStereo /t */ - (pInChI->StereoIsotopic && pInChI->StereoIsotopic->nNumberOfStereoCenters) && /* FH_IsoStereo /t */ - pAltInChI->StereoIsotopic->nCompInv2Abs && /* MH_IsoStereo /m */ - (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) ) { /* !FH_IsoStereo /m */ - /* added 02-09-2006 */ - if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3_M, 1, 1)) ) { - goto exit_function; - } - } - /* use same rule to copy stereobonds */ - if ( !(pInChI->Stereo && pInChI->Stereo->b_parity) && - !(pAltInChI->Stereo && pAltInChI->Stereo->b_parity) && - (pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->nNumberOfStereoBonds) && - (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->b_parity) ) { - if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP2, 1, 1)) ) { - goto exit_function; - } - } - - /* (4) Copy Fixed-H -> isotopic Fixed-H */ - /* if FH_Stereo && !MH_IsoStereo && && !FH_IsoStereo */ - if ( (pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters) && /* FH_Stereo /t */ - !(pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->t_parity) && /* !MH_IsoStereo /t */ - !(pInChI->StereoIsotopic && pInChI->StereoIsotopic->t_parity) ) { /* !FH_IsoStereo /t */ - - /* added 10-10-2007 DT: copy MH_Iso /m => FH_Iso /m to fix i2i bug for Abs stereo */ - /* InChI string contains: MH(/t...), MH_Iso(/mN, no /t), FH(no /t /m), FH_Iso(no /t /m) */ - if ( pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->nCompInv2Abs && /* MH_IsoStereo /m */ - bNo_InChI_t && - NO_VALUE_INT != pAltInChI->StereoIsotopic->nCompInv2Abs && /* undef FH_IsoStereo /m */ - !(pInChI->StereoIsotopic && NO_VALUE_INT != pInChI->StereoIsotopic->nCompInv2Abs)) { - if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3_M, 1, 1))) { - goto exit_function; - } - } - - /* added 05-09-2006: copy sp3 FH=>FH_Iso */ - if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3, 1, 0)) || - (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) && - 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3_M, 1, 0)) ) { - goto exit_function; - } - /* in case of missing nCompInv2Abs, Relative or Racemic stereo, /sN in Fixed-H, 2005-05-10 */ - if ( pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT && - (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { - if ( s[iINChI][j][0] > 0 && s[iINChI][j][1] > 0 ) { - /* suppose once in a while only non-taut stereo changes if inverted */ - pInChI->StereoIsotopic->nCompInv2Abs = 2; - pInChI->Stereo->nCompInv2Abs = (++nFH2iFH_AltInv)%2? 2:0; - } else - if ( (s[iINChI][j][0] > 0 || s[iINChI][j][1] > 0) && s[iINChI][j][1] >= 0 ) /* ??? != NO_VALUE_INT ??? */ - pInChI->StereoIsotopic->nCompInv2Abs = 2; - else - pInChI->StereoIsotopic->nCompInv2Abs = 0; - if ( !(pInChI->nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { - pInChI->nFlags |= (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)); - } - } - - } else - /* copy sp3 inversion info only: Fixed-H -> isotopic Fixed H */ - if ( (pInChI->Stereo && pInChI->Stereo->t_parity) && - !(pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->t_parity) && - (pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->nNumberOfStereoCenters) && - (pInChI->StereoIsotopic && pInChI->StereoIsotopic->nNumberOfStereoCenters) && - pInChI->Stereo->nCompInv2Abs && - (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) ) { - /* added 05-09-2006 */ - if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3_M, 1, 0)) ) { - goto exit_function; - } - } - } - if ( bIso && - !(pInChI->Stereo && pInChI->Stereo->nNumberOfStereoBonds) && - !(pAltInChI->Stereo && pAltInChI->Stereo->nNumberOfStereoBonds) && - (pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->nNumberOfStereoBonds) && - (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->b_parity) ) { - if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP2, 1, 1)) ) { - goto exit_function; - } - } - } - } - } /* end of component cycle (k) */ - } /* end of Mobile/Fixed H cycle (j) */ - - /**** replace NO_VALUE_INT with zeroes in all Mobile & Fixed H components ****/ - for ( j = 0; j < TAUT_NUM; j ++ ) { - for ( k = 0; k < nNumComponents[iINChI][j]; k ++ ) { - if ( pInpInChI[iINChI][j] ) { - INChI *pInChI = &pInpInChI[iINChI][j][k]; - if ( pInChI->nTotalCharge == NO_VALUE_INT ) { - pInChI->nTotalCharge = 0; - } - if ( pInChI->Stereo && pInChI->StereoIsotopic && - pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { - if ( pInChI->Stereo->nNumberOfStereoCenters && - pInChI->Stereo->nCompInv2Abs != NO_VALUE_INT ) { - pInChI->StereoIsotopic->nCompInv2Abs = pInChI->Stereo->nCompInv2Abs; - } - } - /* Add special nCompInv2Abs=2 to force /s2 or /s3 in InChI output */ - if ( pInChI->Stereo && pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT ) { - if ( pInChI->nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO) && - pInChI->Stereo->nNumberOfStereoCenters ) { - pInChI->Stereo->nCompInv2Abs = (s[iINChI][j][0]>0 /*|| s[iINChI][j][1]>0*/)? 2 : 0; /* we do not know the real value */ - } else { - pInChI->Stereo->nCompInv2Abs = 0; - } - } - if ( pInChI->StereoIsotopic && pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { - if ( pInChI->nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO) && - pInChI->StereoIsotopic->nNumberOfStereoCenters ) { - pInChI->StereoIsotopic->nCompInv2Abs = s[iINChI][j][1]>0? 2 : 0; /* we do not know the real value */ - } else { - pInChI->StereoIsotopic->nCompInv2Abs = 0; - } - } - /* added 02-07-2006 */ - if ( pInChI->Stereo && pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT || - pInChI->StereoIsotopic && pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { - ret2 = RI_ERR_PROGR; - goto exit_function; - } - if ( !pInChI->bDeleted && pInChI->nNumberOfAtoms ) { - tot_charge[iINChI][j] += pInChI->nTotalCharge; - for ( m = 0; m < pInChI->nNumberOfAtoms; m ++ ) { - if ( pInChI->nAtom[m] < EL_NUMBER_H || pInChI->nAtom[m] > nElDataLen ) { - ret2 = RI_ERR_PROGR; - goto exit_function; - } - /* all atoms except H */ - if ( pInChI->nAtom[m] > EL_NUMBER_H ) { - num_elem[iINChI][j][pInChI->nAtom[m]].num ++; - } - } - if ( 0 > (ret2 = GetInChINumH( pInChI, &m ) ) ) { - goto exit_function; - } - num_elem[iINChI][j][EL_NUMBER_H].num += m; - } - } - } - } - - - for ( j = 0; j < TAUT_NUM; j ++ ) { - for ( k = 0; k < nNumComponents[iINChI][j]; k ++ ) - { - if ( pInpInChI[iINChI][j] ) - { - INChI *pInChI = &pInpInChI[iINChI][j][k]; - if ( pInChI->Stereo && !pInChI->Stereo->nNumberOfStereoCenters ) - { - pInChI->Stereo->nCompInv2Abs = 0; - } - if ( pInChI->StereoIsotopic && !pInChI->StereoIsotopic->nNumberOfStereoCenters ) - { - pInChI->StereoIsotopic->nCompInv2Abs = 0; - } - } - } - } - - -#if ( FIX_I2I_STEREOCONVERSION_BUG3 == 1 ) -/* (2008-04-10) 1=> Fix bug of i2i conversion */ -/* (missed repeating /s in FI after F for multi-component case) */ - if (nNumComponents[iINChI][TAUT_NON]>1) /* if multi-component */ - if ( !s[iINChI][TAUT_YES][0] && !s[iINChI][TAUT_YES][1] )/* if no /s in M, MI */ - if ( (s[iINChI][TAUT_NON][0]>1) && (s[iINChI][TAUT_NON][1]>1) ) /* if /srel/srac in both F, FI */ - if ( s[iINChI][TAUT_NON][0] == s[iINChI][TAUT_NON][1] ) /* if same stereo in F and FI */ - /* we assume that at least one component in F has no actual stereo */ - /* and place deliberately 0 to appropriate place */ - for ( k = 0; k < nNumComponents[iINChI][TAUT_NON]; k ++ ) - { - INChI *pInChI = &pInpInChI[iINChI][TAUT_NON][k]; - if (pInChI->Stereo->nCompInv2Abs!=0) - { - pInChI->Stereo->nCompInv2Abs = 0; - goto fini; - } - } -fini: ; -#endif - - - if ( num_elem[iINChI][TAUT_YES] ) { - tot_charge[iINChI][TAUT_YES] += nNumProtons[iINChI][TAUT_YES].nNumRemovedProtons; - num_elem[iINChI][TAUT_YES][EL_NUMBER_H].num += nNumProtons[iINChI][TAUT_YES].nNumRemovedProtons; - } - - /**** Count H and isotopic H in Mobile and Fixed H represntations of components */ - /* if at least one component has Fixed-H layer then all components have Fixed-H */ - /* layer; those whose Fixed-H layer is empty have Fixed-H layer same as Mobile-H layer */ - if ( nNumComponents[iINChI][TAUT_NON] ) { - /* only if both Mobile and Fixed H exist */ - int nFormulaH[TAUT_NUM], nNumH[TAUT_NUM], nCharge[TAUT_NUM], nNumIsotopicH[TAUT_NUM][NUM_H_ISOTOPES]; - int nRemovedCharge, nRemovedH, nRemovedIsotopicH[NUM_H_ISOTOPES], nFoundRemovedIsoH; - int nTotRemovedProtons, nTotRemovedIsotopicH[NUM_H_ISOTOPES], bExists[TAUT_NUM]; - INChI *pInChI[TAUT_NUM]; - nTotRemovedProtons = 0; - memset( nTotRemovedIsotopicH, 0, sizeof(nTotRemovedIsotopicH) ); - len2 = inchi_max( nNumComponents[iINChI][TAUT_YES], nNumComponents[iINChI][TAUT_NON] ); - - for ( k = 0; k < len2; k ++ ) { - /* k is a component index */ - for ( j = 0; j < TAUT_NUM; j ++ ) { - /* j is 0=TAUT_NON or 1=TAUT_YES */ - pInChI[j] = NULL; /* initialization 2006-03 */ - bExists[j] = (k < nNumComponents[iINChI][j]) && - pInpInChI[iINChI][j][k].nNumberOfAtoms && - !pInpInChI[iINChI][j][k].bDeleted; - } - if ( !bExists[TAUT_NON] ) { - /* TAUT_YES does not exist for a proton (H+) in TAUT_NON */ - ret2 = RI_ERR_SYNTAX; - goto exit_function; - } - /* at this point at least one of Mobile[k] and Fixed[k] real InChI exists */ - /* initialize for counting removed protons and isotopic H from kth Mobile-H component */ - for ( j = 0; j < TAUT_NUM; j ++ ) { - if ( bExists[j] ) { - pInChI[j] = &pInpInChI[iINChI][j][k]; /* BC: reading uninit memory (fixed?) */ - } - nFormulaH[j] = 0; - nNumH[j] = 0; - nCharge[j] = 0; - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - nNumIsotopicH[j][m] = 0; - } - } - /* extract number of H, isotopic H, and charge */ - for ( j = 0; j < TAUT_NUM; j ++ ) { - if ( !bExists[j] ) - continue; - if ( 0 > (ret2 = GetInChIFormulaNumH( pInChI[j], &nFormulaH[j] )) || - 0 > (ret2 = GetInChINumH( pInChI[j], &nNumH[j] )) || - 0 > (ret2 = GetInChIIsoH( pInChI[j], nNumIsotopicH[j] )) ) { - goto exit_function; - } - nCharge[j] = pInChI[j]->nTotalCharge; - } - for ( j = 0; j < TAUT_NUM; j ++ ) { - if ( !bExists[j] ) - continue; - if ( nFormulaH[j] != nNumH[j] ) { - ret2 = RI_ERR_SYNTAX; - goto exit_function; - } - } - nFoundRemovedIsoH = 0; - nRemovedCharge = nCharge[TAUT_NON] - nCharge[TAUT_YES]; - nRemovedH = nNumH[TAUT_NON] - nNumH[TAUT_YES]; - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - nFoundRemovedIsoH += 0 != (nRemovedIsotopicH[m] = nNumIsotopicH[TAUT_NON][m] - - nNumIsotopicH[TAUT_YES][m] ); - } - if ( nRemovedCharge != nRemovedH ) { - ret2 = RI_ERR_SYNTAX; - goto exit_function; - } - if ( nRemovedCharge || nFoundRemovedIsoH ) { - COMPONENT_REM_PROTONS *pNumProtons; - if ( !nNumProtons[iINChI][TAUT_YES].pNumProtons ) { - /* allocate only if needed */ - nNumProtons[iINChI][TAUT_YES].pNumProtons = - (COMPONENT_REM_PROTONS *) inchi_calloc(len2, - sizeof(nNumProtons[0][0].pNumProtons[0])); - if ( !nNumProtons[iINChI][TAUT_YES].pNumProtons ) { - ret2 = RI_ERR_ALLOC; - goto exit_function; - } - } - pNumProtons = nNumProtons[iINChI][TAUT_YES].pNumProtons+k; - pNumProtons->nNumRemovedProtons = nRemovedH; - nTotRemovedProtons += nRemovedH; - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - pNumProtons->nNumRemovedIsotopicH[m] = nRemovedIsotopicH[m]; - nTotRemovedIsotopicH[m] += nRemovedIsotopicH[m]; - } - /* make sure the Mobile-H InChI has nTautomer */ - if ( pInChI[TAUT_YES] && bExists[TAUT_YES] ) { - if ( !pInChI[TAUT_YES]->lenTautomer ) { - pInChI[TAUT_YES]->lenTautomer = 1; - } - if ( !pInChI[TAUT_YES]->nTautomer ) { - pInChI[TAUT_YES]->nTautomer = (AT_NUMB *)inchi_calloc(pInChI[TAUT_YES]->lenTautomer, sizeof(pInChI[0]->nTautomer[0]) ); - } - } - } - } - if ( nNumProtons[iINChI][TAUT_YES].pNumProtons ) { - /* check consistency */ -#if ( FIX_ISO_FIXEDH_BUG_READ == 1 ) - int iso_diff[NUM_H_ISOTOPES], iso_diff_tot=0; -#endif - if ( nTotRemovedProtons != nNumProtons[iINChI][TAUT_YES].nNumRemovedProtons ) { - ret2 = RI_ERR_SYNTAX; - goto exit_function; - } -#if ( FIX_ISO_FIXEDH_BUG_READ == 1 ) - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - iso_diff[m] = nNumProtons[iINChI][TAUT_YES].nNumRemovedIsotopicH[m]-nTotRemovedIsotopicH[m]; - if ( iso_diff[m] < 0 ) - { - ret2 = RI_ERR_SYNTAX; - goto exit_function; - } else { - /* InChI-1.02b bug: nTotRemovedIsotopicH[m] < nNumProtons[iINChI][TAUT_YES].nNumRemovedIsotopicH[m] */ - /* in non-tautomeric components where D(+) or T(+) was removed from -NH(+)= or =OH(+) */ - iso_diff_tot += iso_diff[m]; - } - } - if ( iso_diff_tot ) { - if ( 0 > bIsoMayBeArranged( bInchi2Struct, iso_diff, nNumProtons, pInpInChI, nNumComponents, iINChI )) { - ret2 = RI_ERR_SYNTAX; - goto exit_function; - } - } -#else - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - if ( nTotRemovedIsotopicH[m] != nNumProtons[iINChI][TAUT_YES].nNumRemovedIsotopicH[m] ) { - ret2 = RI_ERR_SYNTAX; - goto exit_function; - } - } -#endif - } - } - - /* make Mobile H and Fixed H InChI arrays have same length */ - len2 = len1 = 0; - if ( nNumComponents[iINChI][TAUT_YES] < nNumComponents[iINChI][TAUT_NON] ) { - j = TAUT_YES; /* less components in Mobile-H layer */ - len2 = nNumComponents[iINChI][TAUT_NON]; - len1 = nNumComponents[iINChI][TAUT_YES]; - } else - if ( nNumComponents[iINChI][TAUT_YES] > nNumComponents[iINChI][TAUT_NON] ) { - j = TAUT_NON; /* less components in Fixed-H layer */ - len2 = nNumComponents[iINChI][TAUT_YES]; - len1 = nNumComponents[iINChI][TAUT_NON]; - } - /* always len1 <= len2; if Mobile-H and Fixed-H have same number of components then len1=len2=0 */ - if ( len2 && len1 ) { - INChI *pInChI = (INChI *)inchi_calloc( len2, sizeof( pInChI[0] ) ); - if ( !pInChI ) { - ret2 = RI_ERR_ALLOC; - goto exit_function; - } - memcpy( pInChI, pInpInChI[iINChI][j], len1 * sizeof( pInChI[0] ) ); - inchi_free( pInpInChI[iINChI][j] ); - pInpInChI[iINChI][j] = pInChI; - nNumComponents[iINChI][j] = len2; - for ( ; len1 < len2; len1 ++ ) { - if ( j == TAUT_YES ) { - /* mark added to Mobile H layer components as deleted protons */ - if ( 0 > (ret2 = nFillOutProtonMobileH( pInpInChI[iINChI][j]+len1 ) ) ) { - goto exit_function; - } - if ( 0 > (ret2 = nProtonCopyIsotopicInfo( pInpInChI[iINChI][j]+len1/* to */, - pInpInChI[iINChI][TAUT_NON]+len1/* from */ ) ) ) { - goto exit_function; - } - } else { - /* mark added to Fixed H layer components as empty deleted */ - /* this should not happen */ - pInChI[len1].bDeleted = 1; - } - } - } - } /* end of iINChI cycle */ - /* check balances */ - for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) { - for ( i = iINChI; i < INCHI_NUM; i ++ ) { - for ( j = 0; j < TAUT_NUM; j ++ ) { - for ( k = j; k < TAUT_NUM; k ++ ) { - if ( (iINChI!=i || j !=k) && num_elem[iINChI][j] && num_elem[i][k] ) { - if ( tot_charge[iINChI][j] != tot_charge[i][k] ) { - ret2 = RI_ERR_SYNTAX; - goto exit_function; - } - for ( m = 0; m <= nElDataLen; m ++ ) { - if ( num_elem[iINChI][j][m].num != num_elem[i][k][m].num ) { - ret2 = RI_ERR_SYNTAX; - goto exit_function; - } - } - /* - if ( memcmp( num_elem[iINChI], num_elem[i][k], (nElDataLen+1)*sizeof(num_elem[0][0][0]) ) { - ret2 = RI_ERR_SYNTAX; - goto exit_function; - } - */ - } - } - } - } - } - - - } else { - ret2 = ret; - } -exit_function: - for ( i = 0; i < INCHI_NUM; i ++ ) { - for ( j = 0; j < TAUT_NUM; j ++ ) { - if ( num_elem[i][j] ) { - inchi_free( num_elem[i][j] ); - num_elem[i][j] = NULL; - } - } - } - *nErr = (ret2 < 0 && ret2 != RI_ERR_EOL)? ret2 : 0; - return ret; -} - -/**************************************************************************************/ -#if ( FIX_ISO_FIXEDH_BUG_READ == 1 ) -#undef TAUT_YES -int bIsoMayBeArranged( int bInchi2Struct, int iso_diff[NUM_H_ISOTOPES], REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM], - INChI *pInpInChI[INCHI_NUM][TAUT_NUM], int nNumComponents[INCHI_NUM][TAUT_NUM], int iINChI ) -{ -const int TAUT_YES = 1; - int i, k, m, n_found=0, n_found_at_in_component, n_found_H_in_component, i_iso_at, num_iso_H=0, num_iso_H_orig, num_add_iso_H, orig_add_H; - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - num_iso_H += iso_diff[m]; - } - num_iso_H_orig = num_iso_H; - for ( k = 0; k < nNumComponents[iINChI][TAUT_YES] && k < nNumComponents[iINChI][TAUT_NON]; k ++ ) { - INChI *pInChI = &pInpInChI[iINChI][TAUT_NON][k]; - INChI *pInChITaut = &pInpInChI[iINChI][TAUT_YES][k]; - if ( pInChITaut->bDeleted || pInChI->bDeleted || - pInChITaut->nNumberOfIsotopicAtoms > 0 || - pInChITaut->lenTautomer > 1 && pInChITaut->nTautomer && pInChITaut->nTautomer[0] > 0 || - NULL == nNumProtons[iINChI][TAUT_YES].pNumProtons || - nNumProtons[iINChI][TAUT_YES].pNumProtons[k].nNumRemovedProtons <= 0 || - pInChI->nNumberOfIsotopicAtoms > 0 || - nNumProtons[iINChI][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH[0] || - nNumProtons[iINChI][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH[1] || - nNumProtons[iINChI][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH[2] - ) { - continue; - } - /* check if fixed-H has isotopic H; count the possibilities */ - orig_add_H = nNumProtons[iINChI][TAUT_YES].pNumProtons[k].nNumRemovedProtons; - n_found_at_in_component = 0; /* number of atoms that may accept isotopic H */ - n_found_H_in_component = 0; - for ( i = 0; i < pInChI->nNumberOfAtoms; i ++ ) { - int nNumRemovedH = (int)pInChI->nNum_H[i] - (int)pInChITaut->nNum_H[i]; - if ( nNumRemovedH > 0) { - n_found_at_in_component ++; - n_found_H_in_component += nNumRemovedH; - } - } - if ( n_found_at_in_component > 0 && num_iso_H > 0 && bInchi2Struct ) { - pInChI->IsotopicAtom = (INChI_IsotopicAtom *)calloc(inchi_min(n_found_at_in_component, num_iso_H), sizeof(pInChI->IsotopicAtom[0])); - } - for ( i = 0, i_iso_at = 0; i < pInChI->nNumberOfAtoms; i ++ ) { - int nNumRemovedH = (int)pInChI->nNum_H[i] - (int)pInChITaut->nNum_H[i]; - n_found += nNumRemovedH; /* found H removed in mobile-H layer */ - if ( nNumRemovedH > 0 && num_iso_H > 0 && orig_add_H ) { - for ( m = 0; m < NUM_H_ISOTOPES && 0 < num_iso_H && 0 < orig_add_H && 0 < nNumRemovedH; m ++ ) { - if ( iso_diff[m] > 0 ) { - num_add_iso_H = inchi_min( iso_diff[m], nNumRemovedH ); /* atom limit */ - if ( num_add_iso_H > orig_add_H ) /* component limit */ - num_add_iso_H = orig_add_H; - iso_diff[m] -= num_add_iso_H; /* update tot removed single isotope H limit */ - num_iso_H -= num_add_iso_H; /* update tot removed isotopic H limit */ - orig_add_H -= num_add_iso_H; /* update component limit */ - nNumRemovedH -= num_add_iso_H; /* update atom limit */ - nNumProtons[iINChI][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH[m] += num_add_iso_H; - if ( pInChI->IsotopicAtom ) { - pInChI->IsotopicAtom[i_iso_at].nAtomNumber = i+1; - switch( m ) { - case 0: - pInChI->IsotopicAtom[i_iso_at].nNum_H += num_add_iso_H; - break; - case 1: - pInChI->IsotopicAtom[i_iso_at].nNum_D += num_add_iso_H; - break; - case 2: - pInChI->IsotopicAtom[i_iso_at].nNum_T += num_add_iso_H; - break; - } - } - } - } - if ( pInChI->IsotopicAtom ) { - i_iso_at ++; - } - } - } - if ( pInChI->IsotopicAtom && i_iso_at ) { - pInChI->nNumberOfIsotopicAtoms = i_iso_at; - } - } - if ( n_found - num_iso_H >= 0 ) { - /* Success. Arrange isotopic H between components */ - - } - - return n_found - num_iso_H_orig; /* >0 => ambiguous reconstruction, 0 => unambiguous, <0 => impossible */ -} -#define TAUT_YES 1 -#endif - -/******************************************************************************************************/ -typedef enum tagAuxInfoState { - AST_VERSION, /* 0 */ - - AST_MOBILE_H_NUMBERS, /* 1 /N: */ - AST_MOBILE_H_ATOM_EQ, /* 2 /E: */ - AST_MOBILE_H_GROUP_EQ, /* 3 /gE: */ - AST_MOBILE_H_SP3_INV, /* 4 /it: */ - AST_MOBILE_H_SP3_INV_NUMBERS, /* 5 /iN: */ - - AST_MOBILE_H_ISO_LAYER_FORK, /* 6 */ - - AST_MOBILE_H_ISO_NUMBERS, /* 7 /I: */ - AST_MOBILE_H_ISO_ATOM_EQ, /* 8 /E: */ - AST_MOBILE_H_ISO_GROUP_EQ, /* 9 /gE: */ - AST_MOBILE_H_ISO_SP3_INV, /* 10 /it: */ - AST_MOBILE_H_ISO_SP3_INV_NUMBERS, /* 11 /iN: */ - - AST_FIXED_H_LAYER_FORK, /* 12 */ - - AST_FIXED_H_NUMBERS, /* 13 /F: */ - AST_FIXED_H_ATOM_EQ, /* 14 /E: */ - AST_FIXED_H_SP3_INV, /* 15 /it: */ - AST_FIXED_H_SP3_INV_NUMBERS, /* 16 /iN: */ - - AST_FIXED_H_ISO_LAYER_FORK, /* 17 */ - - AST_FIXED_H_ISO_NUMBERS, /* 18 /I: */ - AST_FIXED_H_ISO_ATOM_EQ, /* 19 /E: */ - AST_FIXED_H_ISO_SP3_INV, /* 20 /it: */ - AST_FIXED_H_ISO_SP3_INV_NUMBERS, /* 21 /iN: */ - - AST_REVERSE_INFO_CRV, /* 22 /CRV: */ - AST_REVERSE_INFO_ATOMS, /* 23 /rA: */ - AST_REVERSE_INFO_BONDS, /* 24 /rB: */ - AST_REVERSE_INFO_XYZ, /* 25 /rC: */ - - AST_RECONNECTED_LAYER_FORK, /* 26 /R: */ - AST_RECONNECTED_LAYER_NUMBERS /* 27 */ - -}AUX_INFO_STATE; - -/************************************************************************************/ -int ParseAuxSegmentVersion( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) -{ - const char *q; - if ( isdigit( UCINT *str ) && (inchi_strtol( str, &q, 10), !*q) ) { - return 1; - } - return RI_ERR_SYNTAX; -} -/************************************************************************************/ -int CopyAtomNumbers( INChI *pInChI_To, int bIsoTo, INChI *pInChI_From, int bIsoFrom ) -{ - AT_NUMB *pTo, *pFrom; - if ( !pInChI_To || !pInChI_From || pInChI_To->bDeleted || pInChI_From->bDeleted || - !pInChI_To->nNumberOfAtoms || !pInChI_From->nNumberOfAtoms || - pInChI_To->nNumberOfAtoms != pInChI_From->nNumberOfAtoms || - !pInChI_From->nPossibleLocationsOfIsotopicH ) { - return RI_ERR_PROGR; - } - if ( !pInChI_To->nPossibleLocationsOfIsotopicH ) { - pInChI_To->nPossibleLocationsOfIsotopicH = (AT_NUMB *)inchi_calloc( 2*pInChI_To->nNumberOfAtoms, - sizeof(pInChI_To->nPossibleLocationsOfIsotopicH[0])); - if ( !pInChI_To->nPossibleLocationsOfIsotopicH ) { - return RI_ERR_ALLOC; - } - } - pTo = pInChI_To->nPossibleLocationsOfIsotopicH + (bIsoTo? 0 : pInChI_To->nNumberOfAtoms ); - pFrom = pInChI_From->nPossibleLocationsOfIsotopicH + (bIsoFrom? 0 : pInChI_To->nNumberOfAtoms ); - if ( pTo == pFrom ) { - return RI_ERR_PROGR; - } - memcpy( pTo, pFrom, pInChI_To->nNumberOfAtoms*sizeof(pTo[0]) ); - return 1; -} -/************************************************************************************/ -int ParseAuxSegmentNumbers( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state, int *pbAbc ) -{ - int bIso = 0, iComponent = 0, nNumComponents, bIso_From, bAltInChIExists; - INChI *pInChI = NULL, *pAltInChI = NULL, *pInChI_From = NULL; - const char *p, *q, *pStart, *pEnd, *t; - static const char mult_type[] = "mnM"; - int val, ret, k, mpy_component, num; - AT_NUMB *pNumb; - int base = 10; - /* save isotopic numbering into the first nNumberOfAtoms elements of INChI::nPossibleLocationsOfIsotopicH */ - /* save non-isotopic numbering into the second half of nNumberOfAtoms elements of INChI::nPossibleLocationsOfIsotopicH */ - - switch( state ) { - case AST_MOBILE_H_NUMBERS: - if ( bMobileH != TAUT_YES ) - return RI_ERR_PROGR; - if ( memcmp( str, "N:", 2 ) ) - return 0; - break; - case AST_FIXED_H_NUMBERS: - if ( bMobileH != TAUT_NON ) - return RI_ERR_PROGR; - if ( memcmp( str, "F:", 2 ) ) - return 0; - break; - case AST_MOBILE_H_ISO_NUMBERS: - if ( bMobileH != TAUT_YES ) - return RI_ERR_PROGR; - if ( memcmp( str, "I:", 2 ) ) - return 0; - bIso = 1; - break; - case AST_FIXED_H_ISO_NUMBERS: - if ( bMobileH != TAUT_NON ) - return RI_ERR_PROGR; - if ( memcmp( str, "I:", 2 ) ) - return 0; - bIso = 1; - break; - default: - return RI_ERR_PROGR; - } - pStart = str+2; - if ( !*pStart ) { - return 1; - } - iComponent = 0; - nNumComponents = ppnNumComponents[bMobileH]; - - bAltInChIExists = (NULL != pInpInChI[ALT_TAUT(bMobileH)]); - while( 1 ) { - /* cycle over components */ - if ( !(pEnd = strchr( pStart, ';' )) ) { - pEnd = pStart + strlen(pStart); - } - /* check */ - if ( !pInpInChI[bMobileH] ) { - return 1; /* invalid aux info */ - } - pInChI = pInpInChI[bMobileH] + iComponent; - pAltInChI = pInpInChI[ALT_TAUT(bMobileH)] + iComponent; - if ( (isdigit(UCINT *pStart) && - 0 < (val = (int)inchi_strtol( pStart, &q, 10)) || - (q = pStart, val=1) )&& - (t=strchr(mult_type, *q)) && q+1 == pEnd ) { - - /* process the abbreviation */ - - pInChI_From = NULL; - - switch( bMobileH ) { - - case TAUT_YES: - switch ( bIso ) { - case 0: - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - case 1: - if ( *q != 'm' ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - /* isotopic Mobile-H <-- non-isotopic Mobile H */ - pInChI_From = pInChI; - bIso_From = 0; - break; - default: - ret = RI_ERR_PROGR; - goto exit_function; - } - break; - - case TAUT_NON: - switch ( *q ) { - case 'm': /* same as mobile H */ - switch( bIso ) { - case 0: /* from Mobile-H not isotopic */ - pInChI_From = bAltInChIExists? pAltInChI:NULL; - bIso_From = 0; - break; - - case 1: - pInChI_From = bAltInChIExists? pAltInChI:NULL;; - bIso_From = 1; - break; - default: - ret = RI_ERR_PROGR; - goto exit_function; - } - break; - case 'n': /* same as non-isotopic Fixed-H */ - switch ( bIso ) { - case 0: - ret = 1; /*RI_ERR_SYNTAX;*/ - goto exit_function; - case 1: - pInChI_From = pInChI; - bIso_From = 0; - default: - ret = RI_ERR_PROGR; - goto exit_function; - } - break; - case 'M': /* same as isotopic Mobile-H */ - switch ( bIso ) { - case 0: - ret = RI_ERR_SYNTAX; - goto exit_function; - case 1: - pInChI_From = bAltInChIExists? pAltInChI:NULL;; - bIso_From = 1; - break; - default: - ret = RI_ERR_PROGR; - goto exit_function; - } - break; - default: - ret = 1; /*RI_ERR_SYNTAX;*/ - goto exit_function; - } - break; - - } - /* copy */ - if ( pInChI_From ) { - for ( k = 0; k < val; k ++ ) { - CopyAtomNumbers( pInChI+k, bIso, pInChI_From+k, bIso_From ); - } - } - mpy_component = val; - } else { - mpy_component = 1; - p = pStart; - pNumb = pInChI->nPossibleLocationsOfIsotopicH; - if ( !pNumb ) { - pNumb = (AT_NUMB *)inchi_calloc( 2*pInChI->nNumberOfAtoms, sizeof(pNumb[0] ) ); - if ( !pNumb ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - pInChI->nPossibleLocationsOfIsotopicH = pNumb; - } - pNumb += bIso? 0 : pInChI->nNumberOfAtoms; - if ( pStart < pEnd && *pbAbc == -1 ) { - /* check if compressed InChI */ - *pbAbc = isupper( UCINT *pStart)? 1 : 0; - } - base = (*pbAbc==1)? ALPHA_BASE : 10; - - if ( *pbAbc == 1 ) { - for ( k = 0, p = pStart; k < pInChI->nNumberOfAtoms && p < pEnd; k ++, p ++ ) { - num = (AT_NUMB)inchi_strtol( p, &q, base ); - if ( num <= 0 || p == q ) { - ret = RI_ERR_SYNTAX; - goto exit_function; - } - pNumb[k] = (AT_NUMB)num; - p = q; - if ( p == pEnd ) { - break; /* main end of cycle */ - } - } - } else { - for ( k = 0, p = pStart; k < pInChI->nNumberOfAtoms && p < pEnd; k ++, p ++ ) { - pNumb[k] = (AT_NUMB)inchi_strtol( p, &q, 10 ); - p = q; - if ( p == pEnd ) { - break; /* main end of cycle */ - } else - if ( *p != ',' ) { - ret = RI_ERR_SYNTAX; - goto exit_function; - } - } - } - if ( p != pEnd || k+1 != pInChI->nNumberOfAtoms ) { - ret = RI_ERR_SYNTAX; - goto exit_function; - } - } - - iComponent += mpy_component; - if ( *pEnd ) { - pStart = pEnd+1; - continue; - } else { - break; - } - } - - if ( nNumComponents != iComponent ) { - ret = 1; /*RI_ERR_SYNTAX;*/ /* syntax error */ - goto exit_function; - } - ret = iComponent + 1; - -exit_function: - return ret; - -} -/**********************************************************************************************************/ -int ParseAuxSegmentAtomEqu( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) -{ - switch( state ) { - case AST_MOBILE_H_ATOM_EQ: - if ( bMobileH != TAUT_YES ) - return RI_ERR_PROGR; - if ( memcmp( str, "E:", 2 ) ) - return 0; - break; - case AST_MOBILE_H_ISO_ATOM_EQ: - if ( bMobileH != TAUT_YES ) - return RI_ERR_PROGR; - if ( memcmp( str, "E:", 2 ) ) - return 0; - break; - case AST_FIXED_H_ATOM_EQ: - if ( bMobileH != TAUT_NON ) - return RI_ERR_PROGR; - if ( memcmp( str, "E:", 2 ) ) - return 0; - break; - case AST_FIXED_H_ISO_ATOM_EQ: - if ( bMobileH != TAUT_NON ) - return RI_ERR_PROGR; - if ( memcmp( str, "E:", 2 ) ) - return 0; - break; - default: - return RI_ERR_PROGR; - } - return 1; -} -/***********************************************************************************************************/ -int ParseAuxSegmentGroupEqu( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) -{ - switch( state ) { - case AST_MOBILE_H_GROUP_EQ: - if ( bMobileH != TAUT_YES ) - return RI_ERR_PROGR; - if ( memcmp( str, "gE:", 3 ) ) - return 0; - break; - case AST_MOBILE_H_ISO_GROUP_EQ: - if ( bMobileH != TAUT_YES ) - return RI_ERR_PROGR; - if ( memcmp( str, "gE:", 3 ) ) - return 0; - break; - default: - return RI_ERR_PROGR; - } - return 1; -} -/***********************************************************************************************************/ -int ParseAuxSegmentSp3Inv( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) -{ - switch( state ) { - case AST_MOBILE_H_SP3_INV: - if ( bMobileH != TAUT_YES ) - return RI_ERR_PROGR; - if ( memcmp( str, "it:", 3 ) ) - return 0; - break; - case AST_MOBILE_H_ISO_SP3_INV: - if ( bMobileH != TAUT_YES ) - return RI_ERR_PROGR; - if ( memcmp( str, "it:", 3 ) ) - return 0; - break; - case AST_FIXED_H_SP3_INV: - if ( bMobileH != TAUT_NON ) - return RI_ERR_PROGR; - if ( memcmp( str, "it:", 3 ) ) - return 0; - break; - case AST_FIXED_H_ISO_SP3_INV: - if ( bMobileH != TAUT_NON ) - return RI_ERR_PROGR; - if ( memcmp( str, "it:", 3 ) ) - return 0; - break; - default: - return RI_ERR_PROGR; - } - return 1; -} -/***********************************************************************************************************/ -int ParseAuxSegmentSp3InvNumbers( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) -{ - switch( state ) { - case AST_MOBILE_H_SP3_INV_NUMBERS: - if ( bMobileH != TAUT_YES ) - return RI_ERR_PROGR; - if ( memcmp( str, "iN:", 3 ) ) - return 0; - break; - case AST_MOBILE_H_ISO_SP3_INV_NUMBERS: - if ( bMobileH != TAUT_YES ) - return RI_ERR_PROGR; - if ( memcmp( str, "iN:", 3 ) ) - return 0; - break; - case AST_FIXED_H_SP3_INV_NUMBERS: - if ( bMobileH != TAUT_NON ) - return RI_ERR_PROGR; - if ( memcmp( str, "iN:", 3 ) ) - return 0; - break; - case AST_FIXED_H_ISO_SP3_INV_NUMBERS: - if ( bMobileH != TAUT_NON ) - return RI_ERR_PROGR; - if ( memcmp( str, "iN:", 3 ) ) - return 0; - break; - default: - return RI_ERR_PROGR; - } - return 1; -} -/***********************************************************************************************************/ -int ParseAuxSegmentReverseCRV( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) -{ - switch( state ) { - case AST_REVERSE_INFO_CRV: - if ( memcmp( str, "CRV:", 4 ) ) - return 0; - break; - default: - return RI_ERR_PROGR; - } - return 1; -} -/***********************************************************************************************************/ -int ParseAuxSegmentReverseAtoms( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) -{ - switch( state ) { - case AST_REVERSE_INFO_ATOMS: - if ( memcmp( str, "rA:", 3 ) ) - return 0; - break; - default: - return RI_ERR_PROGR; - } - return 1; -} -/***********************************************************************************************************/ -int ParseAuxSegmentReverseBonds( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) -{ - switch( state ) { - case AST_REVERSE_INFO_BONDS: - if ( memcmp( str, "rB:", 3 ) ) - return 0; - break; - default: - return RI_ERR_PROGR; - } - return 1; -} -/***********************************************************************************************************/ -int ParseAuxSegmentReverseXYZ( const char *str, int bMobileH, XYZ_COORD **ppXYZ, INChI *pInpInChI[], int ppnNumComponents[], int state ) -{ - const char *pStart, *p, *q; - XYZ_COORD *pXYZ = NULL; - int nLenXYZ=0, i, j; - switch( state ) { - case AST_REVERSE_INFO_XYZ: - if ( memcmp( str, "rC:", 3 ) ) - return 0; - break; - default: - return RI_ERR_PROGR; - } - pStart = str+3; - /* count coordinates */ - for ( p = pStart, nLenXYZ = 0; *p; p ++ ) { - nLenXYZ += ( *p == ';' ); - } - if ( !nLenXYZ ) { - return RI_ERR_SYNTAX; - } - if ( NULL == (pXYZ = (XYZ_COORD *)inchi_calloc( nLenXYZ, sizeof(pXYZ[0]) )) ) { - return RI_ERR_ALLOC; - } - for ( p = pStart, i = 0; *p && i < nLenXYZ; p ++, i ++ ) { - for ( j = 0; j < 3; j ++ ) { - pXYZ[i].xyz[j] = inchi_strtod( p, &q ); - p = q + (*q == ',' ); - } - if ( *p != ';' ) { - break; - } - } - if ( i != nLenXYZ || *p ) { - return RI_ERR_SYNTAX; - } - *ppXYZ = pXYZ; - return nLenXYZ+1; -} -/************************************************************************************/ -int AddAuxSegmentCoord( int nRet, XYZ_COORD *pXYZ, int nLenXYZ, INChI *pInpInChI[INCHI_NUM][TAUT_NUM], - int nNumComponents[INCHI_NUM][TAUT_NUM] ) -{ - int iINChI, j, k, n, m, numAt[TAUT_NUM], num_at, nNumMissingNumbers = 0, ret = 0; - INChI *pInChI = NULL; - INChI *pAltInChI = NULL; - XYZ_COORD *pxyz; - - /* propagate numberings */ - for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) { - for ( j = TAUT_YES; TAUT_NON <= j; j -- ) { - for ( k = 0; k < nNumComponents[iINChI][j]; k ++ ) { - int jj = ALT_TAUT(j); - pInChI = pInpInChI[iINChI][j] + k; - pAltInChI = (k < nNumComponents[iINChI][jj])? pInpInChI[iINChI][jj] + k : NULL; - numAt[j] = ( !pInChI->bDeleted )? pInChI->nNumberOfAtoms : 0; - numAt[jj] = ( pAltInChI && !pAltInChI->bDeleted )? pAltInChI->nNumberOfAtoms : 0; - switch( j ) { - case TAUT_YES: - if ( !numAt[j] ) { - break; /* component does not exist */ - } - if ( !pInChI->nPossibleLocationsOfIsotopicH ) { - nNumMissingNumbers ++; - break; - } - if ( !pInChI->nPossibleLocationsOfIsotopicH[0] ) { - if ( pInChI->nPossibleLocationsOfIsotopicH[numAt[j]] ) { - /* copy from non-isotopic (2nd half of the at. numbers array) to the isotopic (1st half) */ - ret = CopyAtomNumbers( pInChI, 1, pInChI, 0 ); - if ( ret < 0 ) { - goto exit_function; - } - } else { - inchi_free( pInChI->nPossibleLocationsOfIsotopicH ); - pInChI->nPossibleLocationsOfIsotopicH = NULL; - nNumMissingNumbers ++; - } - } - break; - - case TAUT_NON: - if ( !numAt[j] ) { - break; /* component does not exist */ - } - if ( !pInChI->nPossibleLocationsOfIsotopicH ) { - /* trying to get numbers from Mobile-H component */ - if ( !numAt[jj] || !(pAltInChI->nPossibleLocationsOfIsotopicH) ) { - nNumMissingNumbers ++; - break; - } - if ( pAltInChI->nPossibleLocationsOfIsotopicH[0] ) { - ret = CopyAtomNumbers( pInChI, 1, pAltInChI, 1 ); - if ( ret < 0 ) { - goto exit_function; - } - } else - if ( pAltInChI->nPossibleLocationsOfIsotopicH[numAt[jj]] ) { - ret = CopyAtomNumbers( pInChI, 1, pAltInChI, 0 ); - if ( ret < 0 ) { - goto exit_function; - } - } else { - /* pAltInChI->nPossibleLocationsOfIsotopicH should have */ - /* been deallocated on previous TAUT_YES pass */ - ret = RI_ERR_PROGR; - goto exit_function; - } - } else - if ( !pInChI->nPossibleLocationsOfIsotopicH[0] ) { - if ( pInChI->nPossibleLocationsOfIsotopicH[numAt[j]] ) { - /* copy from non-isotopic to isotopic */ - ret = CopyAtomNumbers( pInChI, 1, pInChI, 0 ); - if ( ret < 0 ) { - goto exit_function; - } - } else { - inchi_free( pInChI->nPossibleLocationsOfIsotopicH ); - pInChI->nPossibleLocationsOfIsotopicH = NULL; - nNumMissingNumbers ++; - } - } - break; - } - } - } - } - /* add coordinates */ - for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) { - for ( j = 0; j < TAUT_NUM; j ++ ) { - for ( k = 0; k < nNumComponents[iINChI][j]; k ++ ) { - pInChI = pInpInChI[iINChI][j] + k; - num_at = ( !pInChI->bDeleted )? pInChI->nNumberOfAtoms : 0; - if ( !num_at ) { - if ( pInChI->nPossibleLocationsOfIsotopicH ) { - inchi_free( pInChI->nPossibleLocationsOfIsotopicH ); - pInChI->nPossibleLocationsOfIsotopicH = NULL; - } - continue; - } - if ( !pInChI->nPossibleLocationsOfIsotopicH ) { - continue; - } - if ( iINChI == INCHI_BAS && num_at == 1 && - pInChI->szHillFormula && !strcmp(pInChI->szHillFormula, "H") && - (int)pInChI->nPossibleLocationsOfIsotopicH[0]-1 >= nLenXYZ ) { - ; /* a single atom H disconnected from a metal atom has no coordinates */ - } else { - /* add atom coordinates */ - pxyz = (XYZ_COORD *)inchi_calloc( num_at, sizeof(pxyz[0])); - if ( !pxyz ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - for ( n = 0; n < num_at; n ++ ) { - m = (int)pInChI->nPossibleLocationsOfIsotopicH[n]-1; - if ( m < 0 || m >= nLenXYZ ) { - inchi_free( pxyz ); - ret = RI_ERR_SYNTAX; - goto exit_function; - } - pxyz[n] = pXYZ[m]; - } - pInChI->IsotopicTGroup = (INChI_IsotopicTGroup *)pxyz; - } - inchi_free( pInChI->nPossibleLocationsOfIsotopicH ); - pInChI->nPossibleLocationsOfIsotopicH = NULL; - } - } - } - ret = nRet; /* normal exit */ - -exit_function: - return ret; -} -/************************************************************************************/ -int ReadInChICoord( INCHI_IOSTREAM *pInp, - SEGM_LINE *pLine, int *pState, - INChI *pInpInChI[INCHI_NUM][TAUT_NUM], - int nNumComponents[INCHI_NUM][TAUT_NUM] ) -{ - int c, fst, ret=RI_ERR_ALLOC; - int bMobileH = TAUT_YES, bReconn = INCHI_BAS; - const char szToken[] = INCHI_TOKEN; - int state=-1, prev_state=-1; - XYZ_COORD *pXYZ = NULL; - int nLenXYZ = 0; - int bAbc = -1; /* initially undefined */ - - *pState = 0; - - INCHI_HEAPCHK - /* Get "InChI=1/" */ - if ( pLine->len ) { - c = pLine->c; - } else { - c = nGetInChISegment( pInp, pLine, szToken ); - } - if ( c == RI_ERR_EOF && !pLine->len && !pLine->str[0] ) { - ret = c; - pLine->len = 0; - goto exit_error; - } - if ( pLine->len == 0 || c != SEG_END && c != RI_ERR_EOF && !INCHI_INP_EOL(c) ) { - *pState = -1; - pLine->len = 0; - ret = RI_ERR_PROGR; - goto exit_error; - } - if ( memcmp(pLine->str, "AuxInfo=", 8) ) { - *pState = -1; - return c; - } - state = AST_VERSION; - ret = 1; /* means read the next segment */ - do { - /* read the next segment up to the '/' */ - INCHI_HEAPCHK - if ( ret < 0 ) { - *pState = prev_state; - break; - } - prev_state = state + (bReconn? IST_HAPPENED_IN_RECMET : 0); - /* prev_state = state;*/ - if ( 0 < ret ) { - /* read next segment */ - if ( c != RI_ERR_EOF && c != SEG_END ) { - /* abnormal reading result; should not happen */ - while ( c != RI_ERR_EOF && !INCHI_INP_EOL(c) ) { - /* bypass to the end of line or file */ - c = getInChIChar(pInp); - } - ret = (c == RI_ERR_EOF)? RI_ERR_EOF : RI_ERR_EOL; /* end of line */ - pLine->len = 0; - pLine->c = ret; - break; - } - if ( c == RI_ERR_EOF ) { - ret = RI_ERR_EOF; /* end of line */ - break; - } - if ( c == SEG_END ) { - c = nGetInChISegment( pInp, pLine, szToken ); - } - if ( c < 0 ) { - goto exit_error; /* error */ - } - if ( !pLine->len ) { - ret = RI_ERR_EOL; /* end of line */ - break; - } - fst = UCINT pLine->str[0]; - } - /* process the seqment */ - switch ( state ) { - case AST_VERSION: - /* Mobile H */ - bMobileH = TAUT_YES; - ret = ParseAuxSegmentVersion( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_MOBILE_H_NUMBERS; - break; - case AST_MOBILE_H_NUMBERS: - ret = ParseAuxSegmentNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = AST_MOBILE_H_ATOM_EQ; - break; - case AST_MOBILE_H_ATOM_EQ: - ret = ParseAuxSegmentAtomEqu( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_MOBILE_H_GROUP_EQ; - break; - case AST_MOBILE_H_GROUP_EQ: - ret = ParseAuxSegmentGroupEqu( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_MOBILE_H_SP3_INV; - break; - case AST_MOBILE_H_SP3_INV: - ret = ParseAuxSegmentSp3Inv( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_MOBILE_H_SP3_INV_NUMBERS; - break; - case AST_MOBILE_H_SP3_INV_NUMBERS: - ret = ParseAuxSegmentSp3InvNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_MOBILE_H_ISO_LAYER_FORK; - break; - case AST_MOBILE_H_ISO_LAYER_FORK: - if ( !memcmp( pLine->str, "I:", 2 ) ) { - state = AST_MOBILE_H_ISO_NUMBERS; - } else - if ( !memicmp( pLine->str, "F:", 2 ) ) { - state = AST_FIXED_H_NUMBERS; - bMobileH = TAUT_NON; - } else - if ( /*bReconn == INCHI_BAS &&*/ !memicmp( pLine->str, "CRV:", 4 ) ) { - state = AST_REVERSE_INFO_CRV; - } else - if ( bReconn == INCHI_BAS && !memicmp( pLine->str, "rA:", 3 ) ) { - state = AST_REVERSE_INFO_ATOMS; - } else - if ( bReconn == INCHI_BAS && !memicmp( pLine->str, "R:", 3 ) ) { - ret = 1; /* read the next segment */ - state = AST_VERSION; - bMobileH = TAUT_YES; - bReconn = INCHI_REC; - } else { - ret = RI_ERR_SYNTAX; - } - break; - /* Mobile H, isotopic */ - case AST_MOBILE_H_ISO_NUMBERS: - ret = ParseAuxSegmentNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = AST_MOBILE_H_ISO_ATOM_EQ; - break; - case AST_MOBILE_H_ISO_ATOM_EQ: - ret = ParseAuxSegmentAtomEqu( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_MOBILE_H_ISO_GROUP_EQ; - break; - case AST_MOBILE_H_ISO_GROUP_EQ: - ret = ParseAuxSegmentGroupEqu( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_MOBILE_H_ISO_SP3_INV; - break; - case AST_MOBILE_H_ISO_SP3_INV: - ret = ParseAuxSegmentSp3Inv( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_MOBILE_H_ISO_SP3_INV_NUMBERS; - break; - case AST_MOBILE_H_ISO_SP3_INV_NUMBERS: - ret = ParseAuxSegmentSp3InvNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_FIXED_H_LAYER_FORK; - break; - case AST_FIXED_H_LAYER_FORK: - if ( !memicmp( pLine->str, "F:", 2 ) ) { - state = AST_FIXED_H_NUMBERS; - bMobileH = TAUT_NON; - } else - if ( /*bReconn == INCHI_BAS &&*/ !memicmp( pLine->str, "CRV:", 4 ) ) { - state = AST_REVERSE_INFO_CRV; - } else - if ( bReconn == INCHI_BAS && !memicmp( pLine->str, "rA:", 3 ) ) { - state = AST_REVERSE_INFO_ATOMS; - } else - if ( bReconn == INCHI_BAS && !memicmp( pLine->str, "R:", 3 ) ) { - ret = 1; /* read the next segment */ - state = AST_VERSION; - bMobileH = TAUT_YES; - bReconn = INCHI_REC; - } else { - ret = RI_ERR_SYNTAX; - } - break; - case AST_FIXED_H_NUMBERS: - ret = ParseAuxSegmentNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = AST_FIXED_H_ATOM_EQ; - break; - case AST_FIXED_H_ATOM_EQ: - ret = ParseAuxSegmentAtomEqu( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_FIXED_H_SP3_INV; - break; - case AST_FIXED_H_SP3_INV: - ret = ParseAuxSegmentSp3Inv( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_FIXED_H_SP3_INV_NUMBERS; - break; - case AST_FIXED_H_SP3_INV_NUMBERS: - ret = ParseAuxSegmentSp3InvNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_FIXED_H_ISO_LAYER_FORK; - break; - case AST_FIXED_H_ISO_LAYER_FORK: - if ( !memcmp( pLine->str, "I:", 2 ) ) { - state = AST_FIXED_H_ISO_NUMBERS; - } else - if ( /*bReconn == INCHI_BAS &&*/ !memicmp( pLine->str, "CRV:", 4 ) ) { - state = AST_REVERSE_INFO_CRV; - } else - if ( bReconn == INCHI_BAS && !memicmp( pLine->str, "rA:", 3 ) ) { - state = AST_REVERSE_INFO_ATOMS; - } else - if ( bReconn == INCHI_BAS && !memicmp( pLine->str, "R:", 3 ) ) { - ret = 1; /* read the next segment */ - state = AST_VERSION; - bMobileH = TAUT_YES; - bReconn = INCHI_REC; - } else { - ret = RI_ERR_SYNTAX; - } - break; - case AST_FIXED_H_ISO_NUMBERS: - ret = ParseAuxSegmentNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = AST_FIXED_H_ISO_ATOM_EQ; - break; - case AST_FIXED_H_ISO_ATOM_EQ: - ret = ParseAuxSegmentAtomEqu( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_FIXED_H_SP3_INV; - break; - case AST_FIXED_H_ISO_SP3_INV: - ret = ParseAuxSegmentSp3Inv( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_FIXED_H_ISO_SP3_INV_NUMBERS; - break; - case AST_FIXED_H_ISO_SP3_INV_NUMBERS: - ret = ParseAuxSegmentSp3InvNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_REVERSE_INFO_CRV; - break; - case AST_REVERSE_INFO_CRV: - ret = ParseAuxSegmentReverseCRV( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - /* state = (bReconn == INCHI_BAS)? AST_REVERSE_INFO_ATOMS : AST_RECONNECTED_LAYER_FORK;*/ - state = AST_REVERSE_INFO_ATOMS; - break; - case AST_REVERSE_INFO_ATOMS: - ret = ParseAuxSegmentReverseAtoms( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_REVERSE_INFO_BONDS; - break; - case AST_REVERSE_INFO_BONDS: - ret = ParseAuxSegmentReverseBonds( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_REVERSE_INFO_XYZ; - break; - case AST_REVERSE_INFO_XYZ: - ret = ParseAuxSegmentReverseXYZ( pLine->str, bMobileH, &pXYZ, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = AST_RECONNECTED_LAYER_FORK; - if ( ret > 0 ) { - nLenXYZ = ret - 1; - } - break; - case AST_RECONNECTED_LAYER_FORK: - if ( bReconn == INCHI_BAS && !memicmp( pLine->str, "R:", 3 ) ) { - ret = 1; /* read the next segment */ - state = AST_VERSION; - bMobileH = TAUT_YES; - bReconn = INCHI_REC; - } else { - ret = RI_ERR_SYNTAX; - } - break; - } - } while( c >= 0 ); - - ret = AddAuxSegmentCoord( ret, pXYZ, nLenXYZ, pInpInChI, nNumComponents ); - -exit_error: - if ( pXYZ ) { - inchi_free( pXYZ ); - } - if ( ret >= 0 || c == RI_ERR_EOF || c == RI_ERR_EOL ) { - pLine->len = 0; - } - - return ret; -} -/******************************************************************************************************/ -int ReadInChILine(INCHI_IOSTREAM *pInp, SEGM_LINE *pLine, - char **pStr, int *pState, - INChI *pInpInChI[INCHI_NUM][TAUT_NUM], - int nNumComponents[INCHI_NUM][TAUT_NUM], - REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM], - int s[INCHI_NUM][TAUT_NUM][2], - int *bStdFormat, - int *bInputHasSaveOpt, unsigned char *inp_save_opt_bits) -{ - int c, fst, ret=RI_ERR_ALLOC, len; - int bMobileH = TAUT_YES, bReconn = INCHI_BAS; - const char szToken[] = INCHI_TOKEN; - char *p; - int state=-1, prev_state=-1; - int bAbc = -1; /* -1=> undefined, 0=> decimal, 1=> abc (compressed) */ - - const int len_std_prefix=8; - size_t k=0; - unsigned char let1, let2; - const char a2p[]="ABCDEFGHIJKLMNOP"; - - /* memset( pLine, 0, sizeof( pLine[0] ) ); */ - *pState = 0; - -next_line: - INCHI_HEAPCHK - /* Got "InChI=1/" */ - if ( pLine->len ) - { - c = pLine->c; - } - else - { - INCHI_HEAPCHK - c = nGetInChISegment( pInp, pLine, szToken ); - INCHI_HEAPCHK - } - if ( c == RI_ERR_EOF && !pLine->len && !pLine->str[0] ) - { - ret = c; - goto exit_function; - } - INCHI_HEAPCHK - - if ( pLine->len == 0 || c != SEG_END && c != RI_ERR_EOF || !(p = strstr(pLine->str, "InChI=1")) ) - { - if ( pLine->str && pLine->str == strstr ( pLine->str, "Structure" ) ) - { - if ( *pStr ) - { - INCHI_HEAPCHK - inchi_free( *pStr ); - } - *pStr = pLine->str; - /* bypass to the end of the 'Structure nnn' line */ - memset( pLine, 0, sizeof( pLine[0] ) ); - while ( c && !INCHI_INP_EOL(c) ) - { - c = getInChIChar(pInp); - } - goto next_line; - } - /* bypass to the end of unrecognized line */ - while ( c != RI_ERR_EOF && !INCHI_INP_EOL(c) ) - { - c = getInChIChar(pInp); - } - pLine->len = 0; - INCHI_HEAPCHK - goto next_line; - } - - - /* Check if got a standard InChI */ - if ( ( pLine->len == len_std_prefix ) && (pLine->str[len_std_prefix-1]=='S') ) - *bStdFormat=1; - else - *bStdFormat=0; - - - state=IST_MOBILE_H_FORMULA; - ret = 1; /* means read the next segment */ - do { - /* read the next segment up to the '/' */ - INCHI_HEAPCHK - if ( ret < 0 ) - { - *pState = prev_state; - break; - } - prev_state = state + (bReconn? IST_HAPPENED_IN_RECMET : 0); - if ( 0 < ret ) - { - /* read next segment */ - if ( c != RI_ERR_EOF && c != SEG_END ) - { - /* abnormal reading result; should not happen */ - /* unless we got backslash-SaveOpt */ - if ( c=='\\' ) - { - /* May be SaveOpt */ - *bInputHasSaveOpt = 1; - } - k = 0; - while ( c != RI_ERR_EOF && !INCHI_INP_EOL(c) ) - { - /* bypass to the end of line or file */ - c = getInChIChar(pInp); - k++; - if ( k==1 ) let1 = c; - else if ( k==2 ) let2 = c; - } - if ( k != 3) - { - /* not a valid SaveOpt which must be of two chars */ - *bInputHasSaveOpt = 0; - let1 = let2 = '\0'; - } - else - { - /* may be SaveOpt - analyze the content */ - if ( ( let2 >= 'A') && ( let2 <= 'D') ) /* letter-2 OK */ - { - *bInputHasSaveOpt = 0; - *inp_save_opt_bits = 0; - for (k=0; k<16; k++) - { - if ( a2p[k] == let1) /* letter-1 OK */ - { - *inp_save_opt_bits = (unsigned char) k; - *bInputHasSaveOpt = 1; - break; - } - } - if ( *bInputHasSaveOpt ) - { - if ( let2=='B' || let2=='D' ) - *inp_save_opt_bits |= SAVE_OPT_15T; - if ( let2=='C' || let2=='D' ) - *inp_save_opt_bits |= SAVE_OPT_KET; - } - } - } - - ret = (c == RI_ERR_EOF)? RI_ERR_EOF : RI_ERR_EOL; /* end of line */ - pLine->len = 0; - pLine->c = ret; - break; /* exit */ - } - if ( c == RI_ERR_EOF ) { - ret = RI_ERR_EOF; /* end of line */ - break; - } - if ( c == SEG_END ) { - c = nGetInChISegment( pInp, pLine, szToken ); - } - if ( c < 0 ) { - goto exit_error; /* error */ - } - if ( !pLine->len ) { - ret = RI_ERR_EOL; /* end of line */ - break; - } - fst = UCINT pLine->str[0]; - } - /* process the seqment */ - switch ( state ) { - - /* Mobile H, M */ /* / */ - case IST_MOBILE_H_FORMULA: - bMobileH = TAUT_YES; - ret = ParseSegmentFormula( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn] ); - state = IST_MOBILE_H_CONNECTIONS; - break; - case IST_MOBILE_H_CONNECTIONS: /* /c */ - ret = ParseSegmentConnections( pLine->str, bMobileH, &pInpInChI[bReconn][bMobileH], &nNumComponents[bReconn][bMobileH], &bAbc ); - state = IST_MOBILE_H; - break; - case IST_MOBILE_H: /* /h */ - ret = ParseSegmentMobileH( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], &bAbc ); - state = IST_MOBILE_H_CHARGE; - break; - case IST_MOBILE_H_CHARGE: /* /q */ - ret = ParseSegmentCharge( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn] ); - state = IST_MOBILE_H_PROTONS; - break; - case IST_MOBILE_H_PROTONS: /* /p */ - ret = ParseSegmentProtons( pLine->str, bMobileH, nNumProtons[bReconn], nNumComponents[bReconn] ); - state = IST_MOBILE_H_SP2; - break; - case IST_MOBILE_H_SP2: /* /b */ - ret = ParseSegmentSp2( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = IST_MOBILE_H_SP3; - break; - case IST_MOBILE_H_SP3: /* t */ - ret = ParseSegmentSp3( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = IST_MOBILE_H_SP3_M; - break; - case IST_MOBILE_H_SP3_M: /* /m */ - ret = ParseSegmentSp3m( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = IST_MOBILE_H_SP3_S; - break; - case IST_MOBILE_H_SP3_S: /* /s */ - ret = ParseSegmentSp3s( pLine->str, bMobileH, pInpInChI[bReconn], s[bReconn], nNumComponents[bReconn], state ); - state = IST_MOBILE_H_ISO_LAYER_FORK; - break; - case IST_MOBILE_H_ISO_LAYER_FORK: - /* find layer type after M */ - ret = 0; - switch( pLine->str[0] ) { - case 'i': - state = IST_MOBILE_H_ISO_ATOMS; /* MI */ - break; - case 'f': - state = IST_FIXED_H_FORMULA; /* F */ - break; - case 'r': - state = IST_RECONNECTED_FORMULA; /* reconnected */ - break; - default: - ret = RI_ERR_SYNTAX; - } - if ( INCHI_INP_EOL(c) && ret == 0 && !pLine->str[1] ) { - prev_state = state + (bReconn? IST_HAPPENED_IN_RECMET : 0); - ret = RI_ERR_SYNTAX; /* empty layer /i or /f or /r at the end of InChI line */ - } else - if ( !ret && state != IST_MOBILE_H_ISO_ATOMS ) { - len = strlen( pLine->str ); - if ( len > 1 ) { - memmove( pLine->str, pLine->str+1, len ); - } else { - ret = 1; /* read the next segment */ - } - } - break; - /* Mobile H, isotopic, MI */ - case IST_MOBILE_H_ISO_ATOMS: /* i */ - ret = ParseSegmentIsoAtoms( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = IST_MOBILE_H_ISO_EXCH_H; - break; - case IST_MOBILE_H_ISO_EXCH_H: /* /i/h */ - ret = ParseSegmentIsoExchgH( pLine->str, bMobileH, nNumProtons[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = IST_MOBILE_H_ISO_SP2; - break; - case IST_MOBILE_H_ISO_SP2: /* /i/b */ - ret = ParseSegmentSp2( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = IST_MOBILE_H_ISO_SP3; - break; - case IST_MOBILE_H_ISO_SP3: /* /i/t */ - ret = ParseSegmentSp3( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = IST_MOBILE_H_ISO_SP3_M; - break; - case IST_MOBILE_H_ISO_SP3_M: /* /i/m */ - ret = ParseSegmentSp3m( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = IST_MOBILE_H_ISO_SP3_S; - - - break; - case IST_MOBILE_H_ISO_SP3_S: /* /i/s */ - ret = ParseSegmentSp3s( pLine->str, bMobileH, pInpInChI[bReconn], s[bReconn], nNumComponents[bReconn], state ); - state = IST_FIXED_H_LAYER_FORK; - break; - case IST_FIXED_H_LAYER_FORK: - /* find layer type after MI */ - ret = 0; - switch( pLine->str[0] ) { - case 'f': - state = IST_FIXED_H_FORMULA; /* F */ - break; - case 'r': - state = IST_RECONNECTED_FORMULA; /* reconnected */ - break; - default: - ret = RI_ERR_SYNTAX; - } - if ( INCHI_INP_EOL(c) && ret == 0 && !pLine->str[1] ) { - prev_state = state + (bReconn? IST_HAPPENED_IN_RECMET : 0); - ret = RI_ERR_SYNTAX; /* empty layer /f or /r at the end of InChI line */ - } else - if ( !ret ) { - len = strlen( pLine->str ); - if ( len > 1 ) { - memmove( pLine->str, pLine->str+1, len ); - } else { - ret = 1; /* read the next segment */ - } - } - break; - - /* Fixed H, F */ - case IST_FIXED_H_FORMULA: - bMobileH = TAUT_NON; - ret = ParseSegmentFormula( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn] ); - state = IST_FIXED_H; - break; - case IST_FIXED_H: /* /f/h */ - ret = ParseSegmentMobileH( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], &bAbc ); - state = IST_FIXED_H_CHARGE; - break; - case IST_FIXED_H_CHARGE: /* /f/q */ - ret = ParseSegmentCharge( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn] ); - state = IST_FIXED_H_SP2; - break; - case IST_FIXED_H_SP2: /* /f/b */ - ret = ParseSegmentSp2( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = IST_FIXED_H_SP3; - break; - case IST_FIXED_H_SP3: /* /f/t */ - ret = ParseSegmentSp3( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = IST_FIXED_H_SP3_M; - break; - case IST_FIXED_H_SP3_M: /* /f/m */ - ret = ParseSegmentSp3m( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = IST_FIXED_H_SP3_S; - break; - case IST_FIXED_H_SP3_S: /* /f/s */ - ret = ParseSegmentSp3s( pLine->str, bMobileH, pInpInChI[bReconn], s[bReconn], nNumComponents[bReconn], state ); - state = IST_FIXED_H_PERMUTATION; - break; - case IST_FIXED_H_PERMUTATION: /* /f/o */ - ret = ParseSegmentPerm( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = IST_FIXED_H_ISO_LAYER_FORK; - break; - case IST_FIXED_H_ISO_LAYER_FORK: - /* find layer type after M */ - ret = 0; - switch( pLine->str[0] ) { - case 'i': - state = IST_FIXED_H_ISO_ATOMS; /* FI */ - break; - case 'r': - state = IST_RECONNECTED_FORMULA; /* reconnected */ - break; - default: - ret = RI_ERR_SYNTAX; - } - if ( INCHI_INP_EOL(c) && ret == 0 && !pLine->str[1] ) { - prev_state = state + (bReconn? IST_HAPPENED_IN_RECMET : 0); - ret = RI_ERR_SYNTAX; /* empty layer /i or /r at the end of InChI line */ - } else - if ( !ret && state != IST_FIXED_H_ISO_ATOMS ) { - len = strlen( pLine->str ); - if ( len > 1 ) { - memmove( pLine->str, pLine->str+1, len ); - } else { - ret = 1; /* read the next segment */ - } - } - break; - - /* Fixed H, isotopic, FI */ - case IST_FIXED_H_ISO_ATOMS: /* /f/i */ - ret = ParseSegmentIsoAtoms( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = IST_FIXED_H_ISO_SP2; - break; - case IST_FIXED_H_ISO_SP2: /* /f/i/b */ - ret = ParseSegmentSp2( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = IST_FIXED_H_ISO_SP3; - break; - case IST_FIXED_H_ISO_SP3: /* /f/i/t */ - ret = ParseSegmentSp3( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = IST_FIXED_H_ISO_SP3_M; - break; - case IST_FIXED_H_ISO_SP3_M: /* /f/i/m */ - ret = ParseSegmentSp3m( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); - state = IST_FIXED_H_ISO_SP3_S; - break; - case IST_FIXED_H_ISO_SP3_S: /* /f/i/s */ - ret = ParseSegmentSp3s( pLine->str, bMobileH, pInpInChI[bReconn], s[bReconn], nNumComponents[bReconn], state ); - state = IST_FIXED_H_ISO_PERMUTATION; - break; - case IST_FIXED_H_ISO_PERMUTATION: /* /f/i/o */ - ret = ParseSegmentPerm( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); - state = IST_RECONNECTED_LAYER_FORK; - break; - case IST_RECONNECTED_LAYER_FORK: - /* find layer type after FI */ - ret = 0; - switch( pLine->str[0] ) { - case 'r': - state = IST_RECONNECTED_FORMULA; /* reconnected */ - break; - default: - ret = RI_ERR_SYNTAX; - } - if ( INCHI_INP_EOL(c) && ret == 0 && !pLine->str[1] ) { - prev_state = state + (bReconn? IST_HAPPENED_IN_RECMET : 0); - ret = RI_ERR_SYNTAX; /* empty layer /r at the end of InChI line */ - } else - if ( !ret ) { - len = strlen( pLine->str ); - if ( len > 1 ) { - memmove( pLine->str, pLine->str+1, len ); - } else { - ret = 1; /* read the next segment */ - } - } - break; - case IST_RECONNECTED_FORMULA: - bReconn = INCHI_REC; - bMobileH = TAUT_YES; - state = IST_MOBILE_H_FORMULA; - break; - } - - - } while( c >= 0 ); - -exit_function:; -exit_error:; - - INCHI_HEAPCHK - - if ( ret >= 0 || c == RI_ERR_EOF || c == RI_ERR_EOL ) { - pLine->len = 0; - } - return ret; -} -/****************************************************************************************/ -int ParseSegmentIsoExchgH( const char *str, int bMobileH, REM_PROTONS nNumProtons[], int pnNumComponents[], int state, int *pbAbc ) -{ - /* Pass 1: count bonds and find actual numbers of atom */ - const char *p, *q, *pStart, *pEnd; - int ret=0, num, i, i_prev; - static char abc_h[] = "hdt"; - - if ( str[0] != 'h' ) - return 0; - - pStart = str+1; - - if ( !(bMobileH==TAUT_YES && state == IST_MOBILE_H_ISO_EXCH_H ) ) { - return RI_ERR_PROGR; /* program error */ - } - - if ( !(pEnd = strchr( pStart, ';' )) ) { - pEnd = pStart + strlen(pStart); - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p = pStart; - - if ( p < pEnd && *pbAbc == -1 ) { - /* check if compressed InChI */ - /* compressed: /hNtNdNh where N is a decimal number */ - /* uncompressed: /hT[n]D[n]H[n] where n > 1 is a decimal number */ - *pbAbc = isdigit( UCINT *p)? 1 : 0; - } - - if ( *pbAbc == 1 ) { - i_prev = (int)sizeof(abc_h); - while ( p < pEnd ) { - num = (int)inchi_strtol( p, &q, 10 ); - if ( 0 >= num || p == q || q >= pEnd ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p = strchr( abc_h, *q); - if ( p && (i=p-abc_h) < i_prev ) { - nNumProtons[bMobileH].nNumRemovedIsotopicH[i] = (NUM_H)num; - p = q+1; - i_prev = i; - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - } else { - if ( *p == 'T' ) { - nNumProtons[bMobileH].nNumRemovedIsotopicH[2] = 1; - p ++; - if ( isdigit( UCINT p[0]) ) { - nNumProtons[bMobileH].nNumRemovedIsotopicH[2] = (NUM_H)inchi_strtol( p, &q, 10 ); - p = q; - } - } - if ( *p == 'D' ) { - nNumProtons[bMobileH].nNumRemovedIsotopicH[1] = 1; - p ++; - if ( isdigit( UCINT p[0]) ) { - nNumProtons[bMobileH].nNumRemovedIsotopicH[1] = (NUM_H)inchi_strtol( p, &q, 10 ); - p = q; - } - } - if ( *p == 'H' ) { - nNumProtons[bMobileH].nNumRemovedIsotopicH[0] = 1; - p ++; - if ( isdigit( UCINT p[0]) ) { - nNumProtons[bMobileH].nNumRemovedIsotopicH[0] = (NUM_H)inchi_strtol( p, &q, 10 ); - p = q; - } - } - } - if ( p != pEnd ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - ret = 1; - -exit_function: - return ret; - -} -/****************************************************************************************/ -int ParseSegmentPerm( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state, int *pbAbc ) -{ - int nNumComponents, iComponent1, iComponent2, numTrans; - const char *p, *q, *pStart, *pEnd, *pPermStart, *pPermEnd; - int ret=0; - INChI *pInChI = pInpInChI[bMobileH]; /* bMobileH should be TAUT_NON = 0 */ - INChI tmp; - int base = 10; - - - if ( str[0] != 'o' ) - return 0; - - pStart = str+1; - nNumComponents = ppnNumComponents[bMobileH]; - - if ( !(bMobileH==TAUT_NON && ( state == IST_FIXED_H_PERMUTATION || state == IST_FIXED_H_ISO_PERMUTATION) ) ) { - return RI_ERR_PROGR; /* program error */ - } - - if ( !(pEnd = strchr( pStart, ';' )) ) { - pEnd = pStart + strlen(pStart); - } else { - return RI_ERR_SYNTAX; /* syntax error */ - } - while( pStart < pEnd ) { - /* cycle over components; rearrange Fixed H components in order of Mobile H components */ - /* if /o(1,2,3) then reaarange Fixed H components in this way: tmp<-1, 1<-2, 2<-3, 3<-tmp */ - if ( *pStart != '(' ) { - ret = RI_ERR_SYNTAX; - goto exit_function; - } - pPermStart = pStart + 1; - memset( &tmp, 0, sizeof(tmp) ); /* initialization 2006-03 */ - if ( !(pPermEnd = strchr( pPermStart, ')' )) || pPermEnd == pPermStart ) { - ret = RI_ERR_SYNTAX; - goto exit_function; - } - - if ( pPermStart < pPermEnd && *pbAbc == -1 ) { - /* check if compressed InChI */ - *pbAbc = isupper( UCINT *pPermStart)? 1 : 0; - } - base = (*pbAbc==1)? ALPHA_BASE : 10; - - /* permutation cycle */ - if ( *pbAbc == 1 ) { - for ( p = pPermStart, iComponent2 = numTrans = 0; p < pPermEnd; iComponent2 = iComponent1, p = q ) { - /* get first atom number */ - if ( 0 >= (iComponent1 = (int)inchi_strtol( p, &q, base )) || iComponent1 > nNumComponents ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( iComponent2 ) { - pInChI[iComponent2-1] = pInChI[iComponent1-1]; - numTrans ++; - } else { - tmp = pInChI[iComponent1-1]; /* on the 1st pass save Component1 */ - } - } - } else { - for ( p = pPermStart, iComponent2 = numTrans = 0; p < pPermEnd; iComponent2 = iComponent1, p = q + (*q==',') ) { - /* get first atom number */ - if ( !isdigit( UCINT *p ) ) { - ret = RI_ERR_SYNTAX; - goto exit_function; - } - if ( !(iComponent1 = (int)inchi_strtol( p, &q, 10 )) || iComponent1 > nNumComponents ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( iComponent2 ) { - pInChI[iComponent2-1] = pInChI[iComponent1-1]; - numTrans ++; - } else { - tmp = pInChI[iComponent1-1]; /* on the 1st pass save Component1 */ - } - } - } - pInChI[iComponent2-1] = tmp; - if ( !numTrans || p != pPermEnd ) { - ret = RI_ERR_SYNTAX; - goto exit_function; - } else { - pStart = p+1; - } - } - ret = 1; - -exit_function: - return ret; - -} -/****************************************************************************************/ -int ParseSegmentIsoAtoms( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state, int *pbAbc ) -{ - int i, mpy_component, val; - int nNumComponents, iComponent, len, iAtom; - AT_NUMB nAtom1; - const char *p, *q, *t, *pStart, *pEnd, *r; - int ret=0; - INChI *pInChI = pInpInChI[bMobileH]; - INChI *pInChIFrom=NULL; - INChI_IsotopicAtom **pIsotopicAtom = NULL; - INChI_IsotopicAtom isoAtom; - - const char mult_type[] = "mnMNe"; - const char parity_type[] = "-+TDH"; - int bIsoFrom, nCpyType = CPY_ISO_AT; - int base = 10; - - if ( str[0] != 'i' ) - return 0; - - pStart = str+1; - iComponent = 0; - nNumComponents = ppnNumComponents[bMobileH]; - - if ( !(bMobileH==TAUT_YES && state == IST_MOBILE_H_ISO_ATOMS || - bMobileH==TAUT_NON && state == IST_FIXED_H_ISO_ATOMS ) ) { - return RI_ERR_PROGR; /* program error */ - } - if ( !*pStart ) { - return nNumComponents+1; /* no isotopic atoms */ - } - - while( 1 ) { - /* cycle over components */ - if ( !(pEnd = strchr( pStart, ';' )) ) { - pEnd = pStart + strlen(pStart); - } - if ( (p = strchr(pStart, '*')) && p < pEnd ) { - mpy_component = (int)inchi_strtol( pStart, &q, 10 ); - if ( p != q ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } -#if (FIX_DALKE_BUGS == 1) - if ( iComponent + mpy_component > nNumComponents ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } -#endif - p ++; /* move to the 1st character of the component */ - } else - if ( (isdigit(*pStart) && - 0 < (val = (int)inchi_strtol( pStart, &q, 10)) || - (q = pStart, val=1))&& - (t=strchr(mult_type, *q)) && q+1 == pEnd ) { - /* process the abbreviation */ - ret = 0; -#if (FIX_DALKE_BUGS == 1) - if ( iComponent + val > nNumComponents ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } -#endif - bIsoFrom = 0; - switch( bMobileH ) { - case TAUT_YES: - ret = RI_ERR_SYNTAX; - break; - case TAUT_NON: - if ( *q == 'm' ) { - /* copy from mobile H to fixed H */ - pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; - } else - if ( *q == 'e' ) { - /* copy from mobile H to isotopic mobile H */ - pInChIFrom = pInChI; - bIsoFrom = -1; /* empty */ - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - } - break; - default: - ret = RI_ERR_SYNTAX; - break; - } - if ( ret < 0 ) { - goto exit_function; - } - /* copy */ - for ( i = 0; i < val; i ++ ) { - ret = CopySegment( pInChI+iComponent+i, pInChIFrom+iComponent+i, nCpyType, 0, bIsoFrom ); - if ( !ret ) { - ret = RI_ERR_SYNTAX; - } - if ( ret < 0 ) { - goto exit_function; - } - } - iComponent += val; - /* continue to the next component(s) */ - if ( *pEnd ) { - pStart = pEnd+1; - continue; - } else { - break; - } - } else { - mpy_component = 1; - p = pStart; - } - pStart = p; - pIsotopicAtom = &pInChI[iComponent].IsotopicAtom; - if ( *pIsotopicAtom ) { - ret = RI_ERR_PROGR; /* program error */ - goto exit_function; - } - - if ( p < pEnd && *pbAbc == -1 ) { - /* check if compressed InChI */ - *pbAbc = isupper( UCINT *p)? 1 : 0; - } - base = (*pbAbc==1)? ALPHA_BASE : 10; - - -one_more_time: - if ( *pbAbc == 1 ) { - /* process the componnt: At[+/-Charge]TDH,... */ - /* pass 1: find number of stereoatoms */ - for ( p = pStart, iAtom = 0; p < pEnd; iAtom ++ ) { - nAtom1 = (AT_NUMB)inchi_strtol( p, &p, base ); - if ( !nAtom1 || - nAtom1 > pInChI[iComponent].nNumberOfAtoms ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - memset( &isoAtom, 0, sizeof(isoAtom) ); - isoAtom.nAtomNumber = nAtom1; - isoAtom.nIsoDifference = (NUM_H)inchi_strtol( p, &q, 10 ); /* alway in abc */ - if ( p == q ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p = q; - if ( *p == 't' ) { - isoAtom.nNum_T = 1; - p ++; - if ( isdigit( UCINT *p) ) { - isoAtom.nNum_T = (NUM_H)inchi_strtol( p, &q, 10 ); - p = q; - } - } - if ( *p == 'd' ) { - isoAtom.nNum_D = 1; - p ++; - if ( isdigit( UCINT *p) ) { - isoAtom.nNum_D = (NUM_H)inchi_strtol( p, &q, 10 ); - p = q; - } - } - if ( *p == 'h' ) { - isoAtom.nNum_H = 1; - p ++; - if ( isdigit( UCINT *p) ) { - isoAtom.nNum_H = (NUM_H)inchi_strtol( p, &q, 10 ); - p = q; - } - } - if ( p > pEnd || !isoAtom.nIsoDifference && !isoAtom.nNum_T && !isoAtom.nNum_D && !isoAtom.nNum_H ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( *pIsotopicAtom ) { - pIsotopicAtom[0][iAtom] = isoAtom; - } - } - } else { - /* process the componnt: At[+/-Charge]TDH,... */ - /* pass 1: find number of stereoatoms */ - for ( p = pStart, iAtom = 0; p < pEnd; iAtom ++ ) { - nAtom1 = (AT_NUMB)inchi_strtol( p, &q, 10 ); - p = q; - if ( !nAtom1 || - nAtom1 > pInChI[iComponent].nNumberOfAtoms || - !(r = strchr( parity_type, *p) ) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - memset( &isoAtom, 0, sizeof(isoAtom) ); - isoAtom.nAtomNumber = nAtom1; - if ( p[0] == '+' && isdigit( UCINT p[1]) ) { - isoAtom.nIsoDifference = (NUM_H)inchi_strtol( p+1, &q, 10 ); - if ( isoAtom.nIsoDifference >= 0 ) isoAtom.nIsoDifference ++; - p = q; - } else - if ( p[0] == '-' && isdigit( UCINT p[1]) ) { - isoAtom.nIsoDifference = -(NUM_H)inchi_strtol( p+1, &q, 10 ); - if ( isoAtom.nIsoDifference == 0 ) isoAtom.nIsoDifference ++; - p = q; - } - if ( *p == 'T' ) { - isoAtom.nNum_T = 1; - p ++; - if ( isdigit( UCINT *p) ) { - isoAtom.nNum_T = (NUM_H)inchi_strtol( p, &q, 10 ); - p = q; - } - } - if ( *p == 'D' ) { - isoAtom.nNum_D = 1; - p ++; - if ( isdigit( UCINT *p) ) { - isoAtom.nNum_D = (NUM_H)inchi_strtol( p, &q, 10 ); - p = q; - } - } - if ( *p == 'H' ) { - isoAtom.nNum_H = 1; - p ++; - if ( isdigit( UCINT *p) ) { - isoAtom.nNum_H = (NUM_H)inchi_strtol( p, &q, 10 ); - p = q; - } - } - if ( !isoAtom.nIsoDifference && !isoAtom.nNum_T && !isoAtom.nNum_D && !isoAtom.nNum_H ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( p < pEnd ) { - if ( *p == ',' ) { - p ++; - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - if ( *pIsotopicAtom ) { - pIsotopicAtom[0][iAtom] = isoAtom; - } - } - } - if ( p != pEnd ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - - if ( !*pIsotopicAtom ) { - /* end of the 1st pass */ - len = iAtom; - /* memory allocation */ - if ( !(*pIsotopicAtom = (INChI_IsotopicAtom *) inchi_calloc( len+1, sizeof(**pIsotopicAtom) ) ) ) { - ret = RI_ERR_ALLOC; /* memory allocation failed */ - goto exit_function; - } - goto one_more_time; /* goto the 2nd pass */ - } else { - /* 2nd pass */ - if ( len != iAtom ) { - ret = RI_ERR_PROGR; /* program error */ - goto exit_function; - } - pInChI[iComponent].nNumberOfIsotopicAtoms = len; - } - - /* multiplier */ - for ( i = 1; i < mpy_component; i ++ ) { - ret = CopySegment( pInChI+iComponent+i, pInChI+iComponent, nCpyType, 0, 0 ); - if ( !ret ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - } - if ( ret < 0 ) { - goto exit_function; - } - } - - iComponent += mpy_component; - if ( *pEnd ) { - pStart = pEnd+1; - continue; - } else { - break; - } - } - if ( nNumComponents != iComponent ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - ret = iComponent + 1; - -exit_function: - return ret; - -} -/****************************************************************************************/ -int ParseSegmentSp3s( const char *str, int bMobileH, INChI *pInpInChI[], int s[TAUT_NUM][2], int ppnNumComponents[], int state ) -{ - /* Pass 1: count bonds and find actual numbers of atom */ - int nNumComponents, iComponent, val; - const char *p, *q, *pStart, *pEnd; - int ret=0; - INChI *pInChI = pInpInChI[bMobileH]; - INChI_Stereo **pStereo = NULL; - - int bIso = (state==IST_MOBILE_H_ISO_SP3_S || state==IST_FIXED_H_ISO_SP3_S); - - if ( !bIso && state != IST_MOBILE_H_SP3_S && state != IST_FIXED_H_SP3_S ) { - return RI_ERR_PROGR; /* program error */ - } - - if ( str[0] != 's' ) - return 0; - - pStart = str+1; - iComponent = 0; - nNumComponents = ppnNumComponents[bMobileH]; - - /*if ( !(pEnd = strchr( pStart, ';' )) )*/ /* 2007-09-25 DT */ - if ( !(pEnd = strchr( pStart, '/' )) ){ - pEnd = pStart + strlen(pStart); - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p = pStart; - if ( pEnd == pStart ) { - /* create empty sp3 segment */ - int len = 0; - s[bMobileH][bIso] = NO_VALUE_INT; /* empty */ - /* create empty sp3 segment */ - for ( iComponent = 0; iComponent < nNumComponents; iComponent ++ ) { - pStereo = bIso? &pInChI[iComponent].StereoIsotopic : &pInChI[iComponent].Stereo; - if ( !*pStereo ) { - if ( !(*pStereo = (INChI_Stereo *) inchi_calloc( 1, sizeof(**pStereo) ) ) ) { - ret = RI_ERR_ALLOC; /* memory allocation failed */ - goto exit_function; - } - } - pStereo[0]->nCompInv2Abs = 0; /* deliberately empty */ - - if ( pStereo[0]->nNumberOfStereoCenters ) { - ret = RI_ERR_SYNTAX; /* syntax error: "/s" without a digit describes "no stereo" */ - goto exit_function; - } - /* allocate empty sp3 stereo */ - if ( !pStereo[0]->t_parity && - !(pStereo[0]->t_parity = (S_CHAR *)inchi_calloc( len+1, sizeof(pStereo[0]->b_parity[0]) ) ) || - !pStereo[0]->nNumber && - !(pStereo[0]->nNumber = (AT_NUMB *)inchi_calloc( len+1, sizeof(pStereo[0]->nNumber[0]) ) ) ) { - /* cleanup */ - if ( pStereo[0]->t_parity ) { - INCHI_HEAPCHK - inchi_free( pStereo[0]->t_parity ); - pStereo[0]->t_parity = NULL; - } - if ( pStereo[0]->nNumber ) { - INCHI_HEAPCHK - inchi_free( pStereo[0]->nNumber ); - pStereo[0]->nNumber = NULL; - } - ret = RI_ERR_ALLOC; /* memory allocation failed */ - goto exit_function; - } - } - ret = nNumComponents+1; - } else { - val = (int)inchi_strtol( p, &q, 10 ); - if ( q == pEnd && 1 <= val && val <= 3 ) { - s[bMobileH][bIso] = val; - ret = nNumComponents+1; - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - } - } -exit_function: - return ret; -} -/****************************************************************************************/ -int bIsSp3LayerNotEmpty( INChI *pInpInChI[], int bMobileH, int bIso, int nNumComponents ) -{ - INChI *pInChI; - INChI_Stereo *pStereo; - int iComponent, num_not_empty = 0; - - if ( pInpInChI[bMobileH] ) { - for ( iComponent = 0; iComponent < nNumComponents; iComponent ++ ) { - pInChI = pInpInChI[bMobileH] + iComponent; - if ( pInChI->bDeleted || !pInChI->nNumberOfAtoms ) { - continue; - } - pStereo = bIso? pInChI->StereoIsotopic : pInChI->Stereo; - if ( pStereo && pStereo->nNumberOfStereoCenters > 0 && pStereo->nNumber && pStereo->t_parity ) { - num_not_empty ++; - } - } - } - return num_not_empty; -} -/****************************************************************************************/ -int ParseSegmentSp3m( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) -{ - /* Pass 1: count bonds and find actual numbers of atom */ - int nNumComponents, iComponent; - const char *p, *pStart, *pEnd; - int ret=0; - INChI *pInChI = pInpInChI[bMobileH]; - INChI_Stereo **pStereo = NULL; - - int bIso = (state==IST_MOBILE_H_ISO_SP3_M || state==IST_FIXED_H_ISO_SP3_M); - - if ( !bIso && state != IST_MOBILE_H_SP3_M && state != IST_FIXED_H_SP3_M ) { - return RI_ERR_PROGR; /* program error */ - } - nNumComponents = ppnNumComponents[bMobileH]; - - if ( str[0] != 'm' ) { - /* /m is missing: check whether we have to inherit /m from a preceding stereo layer */ - INChI_Stereo *pStereoFrom, *pStereoTo; - INChI *pInChIFrom; - int nNumCopied = 0, bMobileHFrom=-1, bIsoFrom=-1; - if ( bMobileH && !bIso ) { - return 0; /* Main non-isotopic cannot inherit: it has no preceding layer */ - } else - if ( !bMobileH && !bIso ) { - /* fixed-H non-isotopic (F) inherits from Mobile-H non-isotopic (M) */ - bMobileHFrom = TAUT_YES; - bIsoFrom = 0; - } else - if ( bMobileH && bIso ) { - /* Mobile-H isotopic (MI) inherits from Mobile-H non-isotopic (M) */ - bMobileHFrom = TAUT_YES; - bIsoFrom = 0; - } else - if ( !bMobileH && bIso ) { - /* Fixed-H isotopic (FI) inherits from Fixed-H non-isotopic (F) */ - bMobileHFrom = TAUT_NON; - bIsoFrom = 0; - /* if Sp3 is empty in F as well as in M, then inherit from MI */ - if ( !bIsSp3LayerNotEmpty( pInpInChI, TAUT_NON, 0, ppnNumComponents[TAUT_NON /*bMobileH*/] ) /* F */ && - !bIsSp3LayerNotEmpty( pInpInChI, TAUT_YES, 0, ppnNumComponents[TAUT_YES /*bMobileH*/] ) /* M */ ) { - bMobileHFrom = TAUT_YES; - bIsoFrom = 1; - } - } - if ( bMobileHFrom < 0 || bIsoFrom < 0 ) { - return RI_ERR_PROGR; - } - if ( !bIsSp3LayerNotEmpty( pInpInChI, bMobileHFrom, bIsoFrom, ppnNumComponents[/*bMobileH*/ bMobileHFrom] ) ) { - /* nothing to copy; check whether it should have inherited from a preceding layer */ - if ( !bMobileHFrom && bIsoFrom || bMobileHFrom && !bIsoFrom ) { - /* MI or F inherit stereo from M */ - bMobileHFrom = TAUT_YES; - bIsoFrom = 0; - if ( !bIsSp3LayerNotEmpty( pInpInChI, bMobileHFrom, bIsoFrom, ppnNumComponents[bMobileHFrom /*bMobileH*/] ) ) { - return 0; - } - } else { - return 0; - } - } - nNumComponents = inchi_min( ppnNumComponents[bMobileH], ppnNumComponents[bMobileHFrom] ); - for ( iComponent = 0; iComponent < nNumComponents; iComponent ++ ) { - pInChIFrom = pInpInChI[bMobileHFrom] + iComponent; - pInChI = pInpInChI[bMobileH] + iComponent; - if ( pInChIFrom->nNumberOfAtoms > 0 && !pInChIFrom->bDeleted && - pInChI->nNumberOfAtoms > 0 && !pInChI->bDeleted ) { - pStereoFrom = bIsoFrom? pInChIFrom->StereoIsotopic : pInChIFrom->Stereo; - pStereoTo = bIso? pInChI->StereoIsotopic : pInChI->Stereo; - if ( pStereoFrom && pStereoTo ) { - pStereoTo->nCompInv2Abs = pStereoFrom->nCompInv2Abs; - nNumCopied ++; - } - } - } - return 0; /* return value > 0 means the non-/m segment has been processed here */ - } - - pStart = str+1; - iComponent = 0; - - /*if ( !(pEnd = strchr( pStart, ';' )) )*/ /* 2007-09-25 DT */ - if ( !(pEnd = strchr( pStart, '/' )) ) { - pEnd = pStart + strlen(pStart); - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p = pStart; - if ( pEnd == pStart ) { - /* create empty sp3 segment */ - int len = 0; - for ( iComponent = 0; iComponent < nNumComponents; iComponent ++ ) { - INChI *pIsoInChI = &pInChI[iComponent]; - pStereo = bIso? &pIsoInChI->StereoIsotopic : &pIsoInChI->Stereo; - if ( !*pStereo ) { - if ( !(*pStereo = (INChI_Stereo *) inchi_calloc( 1, sizeof(**pStereo) ) ) ) { - ret = RI_ERR_ALLOC; /* memory allocation failed */ - goto exit_function; - } - } - pStereo[0]->nCompInv2Abs = NO_VALUE_INT; /* deliberately empty */ -#ifdef NEVER - if ( pStereo[0]->nNumberOfStereoCenters ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } -#endif - /* allocate empty sp3 stereo */ - if ( !pStereo[0]->t_parity && - !(pStereo[0]->t_parity = (S_CHAR *)inchi_calloc( len+1, sizeof(pStereo[0]->b_parity[0]) ) ) || - !pStereo[0]->nNumber && - !(pStereo[0]->nNumber = (AT_NUMB *)inchi_calloc( len+1, sizeof(pStereo[0]->nNumber[0]) ) ) ) { - /* cleanup */ - if ( pStereo[0]->t_parity ) { - INCHI_HEAPCHK - inchi_free( pStereo[0]->t_parity ); - pStereo[0]->t_parity = NULL; - } - if ( pStereo[0]->nNumber ) { - INCHI_HEAPCHK - inchi_free( pStereo[0]->nNumber ); - pStereo[0]->nNumber = NULL; - } - ret = RI_ERR_ALLOC; /* memory allocation failed */ - goto exit_function; - } - } - ret = nNumComponents+1; - } else { - while( p < pEnd && iComponent < nNumComponents ) { - /* cycle over components */ - pStereo = bIso? &pInChI[iComponent].StereoIsotopic : &pInChI[iComponent].Stereo; - if ( *p != '.' && !*pStereo ) { - if ( !(*pStereo = (INChI_Stereo *) inchi_calloc( 1, sizeof(**pStereo) ) ) ) { - ret = RI_ERR_ALLOC; /* memory allocation failed */ - goto exit_function; - } - } - switch( *p ) { - case '1': - pStereo[0]->nCompInv2Abs = -1; - break; - case '0': - pStereo[0]->nCompInv2Abs = 1; - break; - case '.': - if ( *pStereo ) { - pStereo[0]->nCompInv2Abs = 0; - } - break; - default: - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - iComponent ++; - p ++; - } - if ( p != pEnd || iComponent != nNumComponents ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - ret = nNumComponents+1; - } -exit_function: - return ret; -} -/****************************************************************************************/ -int ParseSegmentSp3( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state, int *pbAbc ) -{ - /* Pass 1: count bonds and find actual numbers of atom */ - int i, mpy_component, val; - int nNumComponents, iComponent, len, iAtom; - AT_NUMB nAtom1; - int atomParity; - const char *p, *q, *t, *pStart, *pEnd, *r; - int ret=0; - INChI *pInChI = pInpInChI[bMobileH]; - INChI *pInChIFrom=NULL; - /* - INChI_Stereo *Stereo = NULL; - INChI_Stereo *StereoOther = NULL; - */ - INChI_Stereo **pStereo = NULL; - - const char mult_type[] = "mnMNe"; - const char parity_type[] = "-+u?"; - int bIsoTo, bIsoFrom, nCpyType = CPY_SP3; - int bIso = (state==IST_MOBILE_H_ISO_SP3 || state==IST_FIXED_H_ISO_SP3); - int base = 10; - - if ( !bIso && state != IST_MOBILE_H_SP3 && state != IST_FIXED_H_SP3 ) { - return RI_ERR_PROGR; /* program error */ - } - - if ( str[0] != 't' ) - return 0; - - pStart = str+1; - iComponent = 0; - nNumComponents = ppnNumComponents[bMobileH]; - - if ( !*pStart ) { - /* create empty sp3 segment */ - int len0 = 0; - for ( iComponent = 0; iComponent < nNumComponents; iComponent ++ ) { - INChI *pIsoInChI = &pInChI[iComponent]; - pStereo = bIso? &pIsoInChI->StereoIsotopic : &pIsoInChI->Stereo; - if ( !*pStereo ) { - if ( !(*pStereo = (INChI_Stereo *) inchi_calloc( 1, sizeof(**pStereo) ) ) ) { - ret = RI_ERR_ALLOC; /* memory allocation failed */ - goto exit_function; - } - } - /* allocate empty sp3 stereo */ - if ( !pStereo[0]->b_parity && - !(pStereo[0]->b_parity = (S_CHAR *)inchi_calloc( len0+1, sizeof(pStereo[0]->b_parity[0]) ) ) || - !pStereo[0]->nBondAtom1 && - !(pStereo[0]->nBondAtom1 = (AT_NUMB *)inchi_calloc( len0+1, sizeof(pStereo[0]->nBondAtom1[0]) ) ) || - !pStereo[0]->nBondAtom2 && - !(pStereo[0]->nBondAtom2 = (AT_NUMB *)inchi_calloc( len0+1, sizeof(pStereo[0]->nBondAtom2[0]) ) ) ) { - /* cleanup */ - if ( pStereo[0]->b_parity ) { - INCHI_HEAPCHK - inchi_free( pStereo[0]->b_parity ); - pStereo[0]->b_parity = NULL; - } - if ( pStereo[0]->nBondAtom1 ) { - INCHI_HEAPCHK - inchi_free( pStereo[0]->nBondAtom1 ); - pStereo[0]->nBondAtom1 = NULL; - } - if ( pStereo[0]->nBondAtom2 ) { - INCHI_HEAPCHK - inchi_free( pStereo[0]->nBondAtom2 ); - pStereo[0]->nBondAtom2 = NULL; - } - ret = RI_ERR_ALLOC; /* memory allocation failed */ - goto exit_function; - } - pStereo[0]->nCompInv2Abs = NO_VALUE_INT; - } - ret = nNumComponents+1; - goto exit_function; - } - - while( 1 ) { - /* cycle over components */ - if ( !(pEnd = strchr( pStart, ';' )) ) { - pEnd = pStart + strlen(pStart); - } - if ( (isdigit(*pStart) && - 0 < (val = (int)inchi_strtol( pStart, &q, 10)) || - (q = pStart, val=1))&& - (t=strchr(mult_type, *q)) && q+1 == pEnd ) { - /* process the abbreviation */ - ret = 0; -#if (FIX_DALKE_BUGS == 1) - if ( iComponent + val > nNumComponents ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } -#endif - switch( bMobileH ) { - case TAUT_YES: - switch( state ) { - case IST_MOBILE_H_ISO_SP3: - if ( *q == 'm' ) { - /* copy from mobile H to isotopic mobile H */ - pInChIFrom = pInChI; - bIsoTo = 1; - bIsoFrom = 0; - } else - if ( *q == 'e' ) { - /* copy from mobile H to isotopic mobile H */ - pInChIFrom = pInChI; - bIsoTo = 1; - bIsoFrom = -1; /* empty */ - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - } - break; - default: - ret = RI_ERR_SYNTAX; - break; - } - break; - case TAUT_NON: - switch( state ) { - case IST_FIXED_H_SP3: - if ( *q == 'm' ) { - /* copy from mobile H to fixed H */ - pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; - bIsoTo = 0; - bIsoFrom = 0; - } else - if ( *q == 'e' ) { - /* copy from mobile H to isotopic mobile H */ - pInChIFrom = pInChI; - bIsoTo = 1; - bIsoFrom = -1; /* empty */ - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - } - break; - case IST_FIXED_H_ISO_SP3: - if ( *q == 'm' ) { - /* copy from mobile H to fixed isotopic H */ - pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; - bIsoTo = 1; - bIsoFrom = 0; - } else - if ( *q == 'M' ) { - /* copy from isotopic mobile H to fixed isotopic H */ - pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; - bIsoTo = 1; - bIsoFrom = 1; - } else - if ( *q == 'n' ) { - /* copy from fixed H to fixed isotopic H */ - pInChIFrom = pInChI; - bIsoTo = 1; - bIsoFrom = 0; - } else - if ( *q == 'e' ) { - /* copy from mobile H to isotopic mobile H */ - pInChIFrom = pInChI; - bIsoTo = 1; - bIsoFrom = -1; /* empty */ - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - } - break; - default: - ret = RI_ERR_SYNTAX; - break; - } - break; - - default: - ret = RI_ERR_SYNTAX; - break; - - } - if ( ret < 0 ) { - goto exit_function; - } - /* copy */ - for ( i = 0; i < val; i ++ ) { - ret = CopySegment( pInChI+iComponent+i, pInChIFrom+iComponent+i, nCpyType, bIsoTo, bIsoFrom ); - if ( !ret ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - } - if ( ret < 0 ) { - goto exit_function; - } - if ( bIsoFrom >= 0 ) { - INChI_Stereo *pStereoTo = bIsoTo? pInChI[iComponent+i].StereoIsotopic : pInChI[iComponent+i].Stereo; - if ( pStereoTo ) { - pStereoTo->nCompInv2Abs = NO_VALUE_INT; /* in case there in no /m segment after this */ - } - } - } - - mpy_component = val; - goto end_main_cycle; - - } else - /* regular multiplier */ - if ( (p = strchr(pStart, '*')) && p < pEnd ) { - mpy_component = (int)inchi_strtol( pStart, &q, 10 ); - if ( p != q ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p ++; /* move to the 1st character of the component */ - } else { - mpy_component = 1; - p = pStart; - } -#if (FIX_DALKE_BUGS == 1) - if ( iComponent + mpy_component > nNumComponents ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } -#endif - pStart = p; - if ( p < pEnd && *pbAbc == -1 ) { - /* check if compressed InChI */ - *pbAbc = isupper( UCINT *p)? 1 : 0; - } - base = (*pbAbc==1)? ALPHA_BASE : 10; - /* process the componnt: at1p,at1p,... */ - /* pass 1: find number of stereoatoms */ - if ( *pbAbc == 1 ) { - for ( p = pStart, iAtom = 0; p < pEnd; iAtom ++ ) { - if ( (nAtom1 = (AT_NUMB)inchi_strtol( p, &p, base ) ) && - (atomParity = (int)inchi_strtol( p, &p, 10), - AB_MIN_KNOWN_PARITY <= atomParity && atomParity <= AB_MAX_KNOWN_PARITY) ) { - ; /* okay */ - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( nAtom1 > pInChI[iComponent].nNumberOfAtoms ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - } else { - for ( p = pStart, iAtom = 0; p < pEnd; iAtom ++, p += (*p == ',') ) { - nAtom1 = (AT_NUMB)inchi_strtol( p, &q, 10 ); - p = q+1; - if ( !nAtom1 || - nAtom1 > pInChI[iComponent].nNumberOfAtoms || - !(r = strchr( parity_type, *q) ) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - } - if ( p != pEnd ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - len = iAtom; - - /* memory allocation */ - - pStereo = bIso? &pInChI[iComponent].StereoIsotopic : &pInChI[iComponent].Stereo; - - if ( !*pStereo ) { - if ( !(*pStereo = (INChI_Stereo *) inchi_calloc( 1, sizeof(**pStereo) ) ) ) { - ret = RI_ERR_ALLOC; /* memory allocation failed */ - goto exit_function; - } - } - if ( pStereo[0]->t_parity || pStereo[0]->nNumberOfStereoCenters || - pStereo[0]->nNumber ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - /* allocate sp3 stereo */ - if ( !(pStereo[0]->t_parity = (S_CHAR *)inchi_calloc( len+1, sizeof(pStereo[0]->b_parity[0]) ) ) || - !(pStereo[0]->nNumber = (AT_NUMB *)inchi_calloc( len+1, sizeof(pStereo[0]->nNumber[0]) ) ) ) { - /* cleanup */ - if ( pStereo[0]->t_parity ) { - INCHI_HEAPCHK - inchi_free( pStereo[0]->t_parity ); - pStereo[0]->t_parity = NULL; - } - if ( pStereo[0]->nNumber ) { - INCHI_HEAPCHK - inchi_free( pStereo[0]->nNumber ); - pStereo[0]->nNumber = NULL; - } - ret = RI_ERR_ALLOC; /* memory allocation failed */ - goto exit_function; - } - - /* pass 2: store stereocenters */ - if ( *pbAbc == 1 ) { - for ( p = pStart, iAtom = 0; p < pEnd; iAtom ++ ) { - if ( (nAtom1 = (AT_NUMB)inchi_strtol( p, &p, base ) ) && - (atomParity = (int)inchi_strtol( p, &p, 10), - AB_MIN_KNOWN_PARITY <= atomParity && atomParity <= AB_MAX_KNOWN_PARITY) ) { - ; /* okay */ - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( nAtom1 > pInChI[iComponent].nNumberOfAtoms ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - pStereo[0]->t_parity[iAtom] = atomParity; - pStereo[0]->nNumber[iAtom] = nAtom1; - if ( iAtom && !(pStereo[0]->nNumber[iAtom-1] < nAtom1) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - } else { - for ( p = pStart, iAtom = 0; p < pEnd; iAtom ++, p += (*p==',') ) { - nAtom1 = (AT_NUMB)inchi_strtol( p, &q, 10 ); - if ( !(r = strchr( parity_type, *q) ) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p = q+1; - atomParity = (r - parity_type) + 1; - pStereo[0]->t_parity[iAtom] = atomParity; - pStereo[0]->nNumber[iAtom] = nAtom1; - if ( iAtom && !(pStereo[0]->nNumber[iAtom-1] < nAtom1) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - } - pStereo[0]->nNumberOfStereoCenters = iAtom; - /*if ( iAtom ) {*/ - pStereo[0]->nCompInv2Abs = NO_VALUE_INT; /* unknown yet */ - /*}*/ - - if ( p != pEnd ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - - /* multiplier */ - for ( i = 1; i < mpy_component; i ++ ) { - ret = CopySegment( pInChI+iComponent+i, pInChI+iComponent, nCpyType, bIso, bIso ); - if ( !ret ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - } - if ( ret < 0 ) { - goto exit_function; - } - ret = CopySegment( pInChI+iComponent+i, pInChI+iComponent, CPY_SP3_M, bIso, bIso ); - if ( !ret ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - } - if ( ret < 0 ) { - goto exit_function; - } - } - -end_main_cycle: - iComponent += mpy_component; - if ( *pEnd ) { - pStart = pEnd+1; - continue; - } else { - break; - } - } - if ( nNumComponents != iComponent ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - ret = iComponent + 1; - -exit_function: - return ret; - -} -/****************************************************************************************/ -int ParseSegmentSp2( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state, int *pbAbc ) -{ - /* Pass 1: count bonds and find actual numbers of atom */ - int i, mpy_component, val; - int nNumComponents, iComponent, len, iBond; - AT_NUMB nAtom1, nAtom2; - int bondParity; - const char *p, *q, *t, *pStart, *pEnd, *r; - int ret=0; - INChI *pInChI = pInpInChI[bMobileH]; - INChI *pInChIFrom=NULL; - /* - INChI_Stereo *Stereo = NULL; - INChI_Stereo *StereoOther = NULL; - */ - INChI_Stereo **pStereo = NULL; - - const char mult_type[] = "mnMNe"; - const char parity_type[] = "-+u?"; - int bIsoTo, bIsoFrom, nCpyType = CPY_SP2; - int bIso = (state==IST_MOBILE_H_ISO_SP2 || state==IST_FIXED_H_ISO_SP2); - int base = 10; - - if ( !bIso && state != IST_MOBILE_H_SP2 && state != IST_FIXED_H_SP2 ) { - return RI_ERR_PROGR; /* program error */ - } - - if ( str[0] != 'b' ) - return 0; - - pStart = str+1; - iComponent = 0; - nNumComponents = ppnNumComponents[bMobileH]; - - if ( !*pStart ) { - /* creaste empty sp3 segment which means no sp3 */ - for ( iComponent = 0; iComponent < nNumComponents; iComponent ++ ) { - INChI *pIsoInChI = &pInChI[iComponent]; - pStereo = bIso? &pIsoInChI->StereoIsotopic : &pIsoInChI->Stereo; - if ( *pStereo && (pStereo[0]->b_parity || pStereo[0]->nNumberOfStereoBonds || - pStereo[0]->nBondAtom1 || pStereo[0]->nBondAtom2 ) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - /* allocate empty sp3 stereo */ - ret = CopySegment( pIsoInChI, NULL, CPY_SP2, bIso, -1); - if ( ret < 0 ) { - goto exit_function; - } - } - ret = nNumComponents+1; - goto exit_function; - } - - while( 1 ) { - - /* cycle over components */ - if ( !(pEnd = strchr( pStart, ';' )) ) { - pEnd = pStart + strlen(pStart); - } - - if ( (isdigit(*pStart) && - 0 < (val = (int)inchi_strtol( pStart, &q, 10)) || - (q = pStart, val=1))&& - (t=strchr(mult_type, *q)) && q+1 == pEnd ) { - /* process the abbreviation */ - ret = 0; -#if (FIX_DALKE_BUGS == 1) - if ( iComponent + val > nNumComponents ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } -#endif - switch( bMobileH ) { - case TAUT_YES: - switch( state ) { - case IST_MOBILE_H_ISO_SP2: - if ( *q == 'm' ) { - /* copy from mobile H to isotopic mobile H */ - pInChIFrom = pInChI; - bIsoTo = 1; - bIsoFrom = 0; - } else - if ( *q == 'e' ) { - /* copy from mobile H to isotopic mobile H */ - pInChIFrom = pInChI; - bIsoTo = 1; - bIsoFrom = -1; /* empty */ - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - } - break; - default: - ret = RI_ERR_SYNTAX; - break; - } - break; - case TAUT_NON: - switch( state ) { - case IST_FIXED_H_SP2: - if ( *q == 'm' ) { - /* copy from mobile H to fixed H */ - pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; - bIsoTo = 0; - bIsoFrom = 0; - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - } - break; - case IST_FIXED_H_ISO_SP2: - if ( *q == 'm' ) { - /* copy from mobile H to fixed isotopic H */ - pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; - bIsoTo = 1; - bIsoFrom = 0; - } else - if ( *q == 'M' ) { - /* copy from isotopic mobile H to fixed isotopic H */ - pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; - bIsoTo = 1; - bIsoFrom = 1; - } else - if ( *q == 'n' ) { - /* copy from fixed H to fixed isotopic H */ - pInChIFrom = pInChI; - bIsoTo = 1; - bIsoFrom = 0; - } else - if ( *q == 'e' ) { - /* copy from mobile H to isotopic mobile H */ - pInChIFrom = pInChI; - bIsoTo = 1; - bIsoFrom = -1; /* empty */ - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - } - break; - default: - ret = RI_ERR_SYNTAX; - break; - } - break; - - default: - ret = RI_ERR_SYNTAX; - break; - - } - if ( ret < 0 ) { - goto exit_function; - } - /* copy */ - for ( i = 0; i < val; i ++ ) { - ret = CopySegment( pInChI+iComponent+i, pInChIFrom+iComponent+i, nCpyType, bIsoTo, bIsoFrom ); - if ( !ret ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - } - if ( ret < 0 ) { - goto exit_function; - } - } - mpy_component = val; - goto end_main_cycle; - - } else - /* regular multiplier */ - if ( (p = strchr(pStart, '*')) && p < pEnd ) { - mpy_component = (int)inchi_strtol( pStart, &q, 10 ); - if ( p != q ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - - p ++; /* move to the 1st character of the component */ - } else { - mpy_component = 1; - p = pStart; - } -#if (FIX_DALKE_BUGS == 1) - if ( iComponent + mpy_component > nNumComponents ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } -#endif - pStart = p; - if ( p < pEnd && *pbAbc == -1 ) { - /* check if compressed InChI */ - *pbAbc = isupper( UCINT *p)? 1 : 0; - } - base = (*pbAbc==1)? ALPHA_BASE : 10; - if ( *pbAbc == 1 ) { - /* process the componnt: at1-at2p,at1-at2p,... */ - /* pass 1: find number of stereobonds */ - for ( p = pStart, iBond = 0; p < pEnd; iBond ++ ) { - /* atoms 1, 2, and parity */ - if ( (nAtom1 = (AT_NUMB)inchi_strtol( p, &p, base ) ) && - (nAtom2 = (AT_NUMB)inchi_strtol( p, &p, base ) ) && - (bondParity = (int)inchi_strtol( p, &p, 10), - AB_MIN_KNOWN_PARITY <= bondParity && bondParity <= AB_MAX_KNOWN_PARITY) ) { - ; /* okay */ - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( nAtom1 <= nAtom2 || - nAtom1 > pInChI[iComponent].nNumberOfAtoms ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - } else { - /* process the componnt: at1-at2p,at1-at2p,... */ - /* pass 1: find number of stereobonds */ - for ( p = pStart, iBond = 0; p < pEnd; iBond ++, p += (*p==',') ) { - nAtom1 = (AT_NUMB)inchi_strtol( p, &q, 10 ); - if ( *q != '-' ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p = q+1; - nAtom2 = (AT_NUMB)inchi_strtol( p, &q, 10 ); - if ( !nAtom1 || !nAtom2 || - nAtom1 <= nAtom2 || - nAtom1 > pInChI[iComponent].nNumberOfAtoms || - !(r = strchr( parity_type, *q) ) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p = q+1; - } - } - - if ( p != pEnd ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - len = iBond; - - /* memory allocation */ - - pStereo = bIso? &pInChI[iComponent].StereoIsotopic : &pInChI[iComponent].Stereo; - - if ( !*pStereo ) { - if ( !(*pStereo = (INChI_Stereo *) inchi_calloc( 1, sizeof(**pStereo) ) ) ) { - ret = RI_ERR_ALLOC; /* memory allocation failed */ - goto exit_function; - } - } - if ( pStereo[0]->b_parity || pStereo[0]->nNumberOfStereoBonds || - pStereo[0]->nBondAtom1 || pStereo[0]->nBondAtom2 ) { - ret = RI_ERR_SYNTAX; /* syntax error: bonds have already been allocated */ - goto exit_function; - } - /* allocate sp2 stereo */ - if ( !(pStereo[0]->b_parity = (S_CHAR *)inchi_calloc( len+1, sizeof(pStereo[0]->b_parity[0]) ) ) || - !(pStereo[0]->nBondAtom1 = (AT_NUMB *)inchi_calloc( len+1, sizeof(pStereo[0]->nBondAtom1[0]) ) ) || - !(pStereo[0]->nBondAtom2 = (AT_NUMB *)inchi_calloc( len+1, sizeof(pStereo[0]->nBondAtom2[0]) ) ) ) { - /* cleanup */ - if ( pStereo[0]->b_parity ) { - INCHI_HEAPCHK - inchi_free( pStereo[0]->b_parity ); - pStereo[0]->b_parity = NULL; - } - if ( pStereo[0]->nBondAtom1 ) { - INCHI_HEAPCHK - inchi_free( pStereo[0]->nBondAtom1 ); - pStereo[0]->nBondAtom1 = NULL; - } - if ( pStereo[0]->nBondAtom2 ) { - INCHI_HEAPCHK - inchi_free( pStereo[0]->nBondAtom2 ); - pStereo[0]->nBondAtom2 = NULL; - } - INCHI_HEAPCHK - ret = RI_ERR_ALLOC; /* memory allocation failed */ - goto exit_function; - } - - /* pass 2: store stereobonds */ - if ( *pbAbc == 1 ) { - for ( p = pStart, iBond = 0; p < pEnd; iBond ++ ) { - if ( (nAtom1 = (AT_NUMB)inchi_strtol( p, &p, base ) ) && - (nAtom2 = (AT_NUMB)inchi_strtol( p, &p, base ) ) && - (bondParity = (int)inchi_strtol( p, &p, 10), - AB_MIN_KNOWN_PARITY <= bondParity && bondParity <= AB_MAX_KNOWN_PARITY) ) { - ; /* okay */ - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - pStereo[0]->b_parity[iBond] = bondParity; - pStereo[0]->nBondAtom1[iBond] = nAtom1; - pStereo[0]->nBondAtom2[iBond] = nAtom2; - - if ( iBond && - !(pStereo[0]->nBondAtom1[iBond-1] < nAtom1 || - pStereo[0]->nBondAtom1[iBond-1] == nAtom1 && - pStereo[0]->nBondAtom2[iBond-1] < nAtom2 ) ) { - ret = RI_ERR_SYNTAX; /* syntax error: wrong bond order */ - goto exit_function; - } - } - } else { - for ( p = pStart, iBond = 0; p < pEnd; iBond ++, p += (*p==',') ) { - nAtom1 = (AT_NUMB)inchi_strtol( p, &q, 10 ); - if ( *q != '-' ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p = q+1; - nAtom2 = (AT_NUMB)inchi_strtol( p, &q, 10 ); - if ( !(r = strchr( parity_type, *q) ) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p = q+1; - bondParity = (r - parity_type) + 1; - pStereo[0]->b_parity[iBond] = bondParity; - pStereo[0]->nBondAtom1[iBond] = nAtom1; - pStereo[0]->nBondAtom2[iBond] = nAtom2; - - if ( iBond && - !(pStereo[0]->nBondAtom1[iBond-1] < nAtom1 || - pStereo[0]->nBondAtom1[iBond-1] == nAtom1 && - pStereo[0]->nBondAtom2[iBond-1] < nAtom2 ) ) { - ret = RI_ERR_SYNTAX; /* syntax error: wrong bond order */ - goto exit_function; - } - } - } - pStereo[0]->nNumberOfStereoBonds = iBond; - - if ( p != pEnd ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - - /* multiplier */ - for ( i = 1; i < mpy_component; i ++ ) { - ret = CopySegment( pInChI+iComponent+i, pInChI+iComponent, nCpyType, bIso, bIso ); - if ( ret < 0 ) { - goto exit_function; - } - } - -end_main_cycle: - iComponent += mpy_component; - if ( *pEnd ) { - pStart = pEnd+1; - continue; - } else { - break; - } - } - if ( nNumComponents != iComponent ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - ret = iComponent + 1; - -exit_function: - return ret; - -} -/****************************************************************************************/ -int ParseSegmentProtons( const char *str, int bMobileH, REM_PROTONS nNumProtons[], int ppnNumComponents[] ) -{ - /* Pass 1: count bonds and find actual numbers of atom */ - int val; - const char *q, *pStart, *pEnd; - int ret; - - if ( str[0] != 'p' ) - return 0; - - pStart = str+1; - - while( 1 ) { - /* cycle over components */ - if ( !(pEnd = strchr( pStart, ';' )) ) { - pEnd = pStart + strlen(pStart); - } - - if ( pStart[0] == '+' && isdigit( UCINT pStart[1] ) ) { - val = (int)inchi_strtol( pStart+1, &q, 10 ); - } else - if ( pStart[0] == '-' && isdigit( UCINT pStart[1] ) ) { - val = -(int)inchi_strtol( pStart+1, &q, 10 ); - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( !val ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - nNumProtons[bMobileH].nNumRemovedProtons = val; - if ( *pEnd || q != pEnd ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } else { - break; - } - } - ret = 1; - -exit_function: - return ret; - -} -/****************************************************************************************/ -int ParseSegmentCharge( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[] ) -{ - /* Pass 1: count bonds and find actual numbers of atom */ - int i, mpy_component, val; - int nNumComponents, iComponent; - const char *p, *q, *t, *pStart, *pEnd; - int ret; - INChI *pInChI = pInpInChI[bMobileH]; - const char mult_type[] = "mnMNe"; - - if ( str[0] != 'q' ) { - return 0; - } - - pStart = str+1; - iComponent = 0; - nNumComponents = ppnNumComponents[bMobileH]; - - if ( !*pStart && bMobileH == TAUT_NON ) { - for ( i = 0; i < nNumComponents; i ++ ) { - pInChI[i].nTotalCharge = NO_VALUE_INT; - } - return nNumComponents+1; - } - - while( 1 ) { - /* cycle over components */ - if ( !(pEnd = strchr( pStart, ';' )) ) { - pEnd = pStart + strlen(pStart); - } - - if ( (isdigit(UCINT *pStart) && - 0 < (val = (int)inchi_strtol( pStart, &q, 10)) || - (q = pStart, val=1) )&& - (t=strchr(mult_type, *q)) && q+1 == pEnd ) { - /* process the abbreviation */ - - switch( bMobileH ) { - - case TAUT_YES: - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - - case TAUT_NON: - if ( *q != 'm' || - iComponent + val > nNumComponents || - iComponent + val > ppnNumComponents[TAUT_YES] ) { - - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - for ( i = 0; i < val; i ++ ) { - /* avoid 0 which means "omitted" */ - pInChI[iComponent+i].nTotalCharge = pInpInChI[TAUT_YES][iComponent+i].nTotalCharge? - pInpInChI[TAUT_YES][iComponent+i].nTotalCharge : - NO_VALUE_INT; - } - mpy_component = val; - goto end_main_cycle; - - default: - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - }else - if ( (p = strchr(pStart, '*')) && p < pEnd ) { - mpy_component = (int)inchi_strtol( pStart, &q, 10 ); - if ( p != q ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p ++; - } else { - mpy_component = 1; - p = pStart; - } -#if ( FIX_DALKE_BUGS == 1 ) - if ( mpy_component + iComponent > nNumComponents || mpy_component <= 0 ) { - ret = RI_ERR_SYNTAX; /* syntax error: too many components in charge layer */ - goto exit_function; - } -#endif - pStart = p; - - if ( pStart < pEnd ) { - if ( pStart[0] == '+' && isdigit( UCINT pStart[1] ) ) { - val = (int)inchi_strtol( pStart+1, &q, 10 ); - pStart = q; - } else - if ( pStart[0] == '-' && isdigit( UCINT pStart[1] ) ) { - val = -(int)inchi_strtol( pStart+1, &q, 10 ); - pStart = q; - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } -#if ( FIX_DALKE_BUGS == 1 ) - if ( val < -256 || val > 256 ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } -#endif - if ( !val ) { - if ( pStart != pEnd ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( bMobileH == TAUT_NON ) { - val = NO_VALUE_INT; /* avoid 0 which means "omitted" */ - } - } - } else { - val = NO_VALUE_INT; - } - for ( i = 0; i < mpy_component; i ++ ) { - pInChI[iComponent+i].nTotalCharge = val; - } - -end_main_cycle: - iComponent += mpy_component; - if ( *pEnd ) { - pStart = pEnd+1; - continue; - } else { - break; - } - } - - if ( nNumComponents != iComponent ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - ret = iComponent + 1; - -exit_function: - return ret; - -} -/****************************************************************************************/ -int ParseSegmentMobileH( const char *str, int bMobileH, INChI *pInpInChI[], int pnNumComponents[], int *pbAbc ) -{ -#define nNum_H( ICOMPONENT ) ((bMobileH==TAUT_YES)? pInChI[ICOMPONENT].nNum_H : pInChI[ICOMPONENT].nNum_H_fixed) - /* Pass 1: count bonds and find actual numbers of atom */ - int i, mpy_component, num_H, num_Minus, val, num_Atoms, numCtAtoms, tg_alloc_len, len, len2; - int num_H_component, num_H_formula, num_taut_H_component, num_H_InChI, ret2; - int nNumComponents, iComponent, nNumBonds, lenTautomer, tg_pos_Tautomer, iTGroup; - const char *p, *q, *h, *t, *p1, *pTaut, *pStart, *pEnd; - AT_NUMB curAtom, nxtAtom; - int num_open, state, ret, nAltMobileH = ALT_TAUT(bMobileH); - INChI *pInChI = pInpInChI[bMobileH]; - INChI *pAltInChI = pInpInChI[nAltMobileH]; - int base = 10; - - num_H = -999; /* impossible value */ - num_Minus = -999; /* impossible value */ - tg_pos_Tautomer = -999; /* impossible value */ - - /* number of immobile H is always allocated; immobile H are present in M layer only */ - nNumComponents = pnNumComponents[bMobileH]; - for ( i = 0; i < nNumComponents; i ++ ) { - len = pInChI[i].nNumberOfAtoms; - if ( bMobileH == TAUT_NON && i < pnNumComponents[nAltMobileH] ) { - if ( len < pAltInChI[i].nNumberOfAtoms ) { - len = pAltInChI[i].nNumberOfAtoms; - if ( pInChI[i].nNum_H ) { - inchi_free( pInChI[i].nNum_H ); - pInChI[i].nNum_H = NULL; - } - } - } - len ++; - if ( !pInChI[i].nNum_H && /* allocate immobile H segment if it has not been allocated yet */ - !(pInChI[i].nNum_H = (S_CHAR *)inchi_calloc( len, sizeof(pInChI[0].nNum_H[0]) )) ) { - ret = RI_ERR_ALLOC; /* allocation error */ - goto exit_function; - } - /* copy immobile H from Mobile-H layer to Fixed-H layer */ - if ( bMobileH == TAUT_NON && i < pnNumComponents[nAltMobileH] ) { - memcpy( pInChI[i].nNum_H, pAltInChI[i].nNum_H, (len-1) * sizeof(pInChI[0].nNum_H[0]) ); - } - } - - if ( str[0] != 'h' ) - return 0; - - /* Read Hydrogen info in 1 pass */ - pStart = str+1; - iComponent = 0; - nNumComponents = pnNumComponents[bMobileH]; - - while( 1 ) { - /* cycle over components */ - if ( !(pEnd = strchr( pStart, ';' )) ) { - pEnd = pStart + strlen(pStart); - } - if ( (p = strchr(pStart, '*')) && p < pEnd ) { - mpy_component = (int)inchi_strtol( pStart, &q, 10 ); -#if ( FIX_DALKE_BUGS == 1 ) - if ( p != q || !isdigit(UCINT *pStart) ) /* prevent non-positive multipliers */ -#else - if ( p != q ) -#endif - { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p ++; - } else { - mpy_component = 1; - p = pStart; - } - pStart = p; - /* Pass 1.1 parse a component */ - num_open = 0; - state='\0'; /* initial state */ - nNumBonds = 0; - curAtom = 0; - numCtAtoms = pInChI[iComponent].nNumberOfAtoms; - if ( bMobileH == TAUT_NON && iComponent < pnNumComponents[nAltMobileH] ) { - numCtAtoms = pAltInChI[iComponent].nNumberOfAtoms; - } - - if ( p < pEnd && *pbAbc == -1 ) { - /* check if compressed InChI */ - *pbAbc = (*p == ',' || isupper( UCINT *p))? 1 : 0; - } - base = (*pbAbc==1)? ALPHA_BASE : 10; - - /* immobile H */ - t = pTaut = (*pbAbc==1)? strchr( p, ',' ) : strchr( p, '(' ); /* locate the first tautomer group character */ - - if ( t && bMobileH == TAUT_NON ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( !pTaut || pTaut > pEnd ) { - pTaut = pEnd; - t = NULL; /* found no tautomeric group for this component */ - } - for ( i = 0; i < mpy_component; i ++ ) { - if ( bMobileH == TAUT_NON ) { - /* allocate nNum_H_fixed */ - if ( pInChI[iComponent+i].nNum_H_fixed ) { - ret = RI_ERR_PROGR; /* program error */ - goto exit_function; - } - if ( iComponent+i < pnNumComponents[nAltMobileH] ) { - len = inchi_max(pInChI[iComponent+i].nNumberOfAtoms, pAltInChI[iComponent+i].nNumberOfAtoms)+1; - } else { - len = pInChI[iComponent+i].nNumberOfAtoms + 1; - } - pInChI[iComponent+i].nNum_H_fixed = (S_CHAR *)inchi_calloc( len, sizeof(pInChI[0].nNum_H_fixed[0]) ); - if ( !pInChI[iComponent+i].nNum_H_fixed ) { - ret = RI_ERR_ALLOC; /* allocation error */ - goto exit_function; - } - /* compare nAtom */ - if ( iComponent+i < pnNumComponents[nAltMobileH] ) { - len2 = inchi_min(pInChI[iComponent+i].nNumberOfAtoms, pAltInChI[iComponent+i].nNumberOfAtoms); - if ( pInChI[iComponent+i].nAtom && len2 ) { - /* check */ - if ( memcmp( pInChI[iComponent+i].nAtom, pAltInChI[iComponent+i].nAtom, len2 * sizeof(pInChI[0].nAtom[0]) ) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - /* allocate and copy atom if bridging H are present */ - if ( pInChI[iComponent+i].nNumberOfAtoms < pAltInChI[iComponent+i].nNumberOfAtoms ) { - if ( pInChI[iComponent+i].nAtom ) - inchi_free( pInChI[iComponent+i].nAtom ); - if ( !(pInChI[iComponent+i].nAtom = (U_CHAR *)inchi_calloc( len, sizeof(pInChI[0].nAtom[0]) ) ) ) { - ret = RI_ERR_ALLOC; /* allocation error */ - goto exit_function; - } - if ( len > 1 ) { - memcpy( pInChI[iComponent+i].nAtom, pAltInChI[iComponent+i].nAtom, (len-1) * sizeof(pInChI[0].nAtom[0]) ); - } - /* correct number of atoms including bridging H */ - pInChI[iComponent+i].nNumberOfAtoms = pAltInChI[iComponent+i].nNumberOfAtoms; - } - } - } - } - - if ( *pbAbc == 1 ) { - /* read numbers of H: XnYn... or XYn... */ - p = pStart; - tg_alloc_len = 0; - num_H_component = num_taut_H_component = 0; - while ( p < pTaut ) { - /* syntax check: atom number */ - if ( !*p || !isupper( UCINT *p ) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( curAtom = nxtAtom = (int)inchi_strtol( p, &q, base ) ) { - p = q; - if ( isupper( UCINT *p ) ) { - nxtAtom = (int)inchi_strtol( p, &q, base ); - p = q; - } - } - if ( curAtom > nxtAtom || nxtAtom > numCtAtoms || p > pTaut ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - /* number of H, may be negative */ - if ( !(num_H = (int)inchi_strtol( p, &q, 10 )) || q > pTaut ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p = q; - /* set number of H */ - for ( i = curAtom; i <= nxtAtom; i ++ ) { - nNum_H(iComponent)[i-1] = num_H; - num_H_component += num_H; - } - } - if ( p != pTaut ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } else { - /* read numbers of H: 1-2,3H2,4,5H3 */ - p = pStart; - tg_alloc_len = 0; - num_H_component = num_taut_H_component = 0; - while ( p < pTaut ) { - /* syntax check: atom number */ - if ( !*p || !isdigit( UCINT *p ) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - /* number of H */ - h = p + strcspn( p, "Hh" ); - /*h = strchr( p, 'H' );*/ - if ( !*h || h >= pTaut ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - - /* - p = pTaut; - h = NULL; - break; */ /* no more H found */ - } - num_H = (*h == 'H')? 1 : (*h == 'h')? -1 : 0; - if ( !num_H ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( h[1] && isdigit( UCINT h[1] ) ) { - num_H *= (int)inchi_strtol( h+1, &p1, 10 ); - } else { - p1 = h+1; /* next set of immobile H */ - } - if ( *p1 == ',' ) { - p1 ++; /* next H-subsegment; otherwise (H or ; or end of the segment */ - } - /* list of atoms that have num_H */ - while ( p < h ) { - if ( !*p || !isdigit( UCINT *p ) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - nxtAtom = curAtom = (int)inchi_strtol( p, &q, 10 ); - if ( *q == '-' ) { - nxtAtom = (int)inchi_strtol( q+1, &q, 10 ); - } - /* consitency check */ - if ( !curAtom || curAtom > numCtAtoms || - nxtAtom < curAtom || nxtAtom > numCtAtoms ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - /* set number of H */ - for ( i = curAtom; i <= nxtAtom; i ++ ) { - nNum_H(iComponent)[i-1] = num_H; - num_H_component += num_H; - } - /* move to the next atom number if any */ - p = q; - if ( *p == ',' ) { - p ++; - } - } - - if ( p == h ) { - p = p1; - } else - if ( p == pTaut ) { - break; - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - } - - INCHI_HEAPCHK - /* ) -> (, H, N, [-, N,], AtNum,... AtNum) */ - lenTautomer = 0; - if ( p = t ) { - if ( *pbAbc == 1 ) { - /* tautomeric groups: pass 1 */ - iTGroup = 0; - state = ')'; /* init as if the prev. t-group just ended */ - num_Atoms = 0; - /* Tautomeric info storage */ - /* NumGroups; ((NumAt+2, NumH, Num(-), At1..AtNumAt),...); {INCHI_T_NUM_MOVABLE = 2} */ - /* Allocated length: [5*nNumberOfAtoms/2+1], see Alloc_INChI(...) */ - if ( *p == ',' ) { /* start t-group */ - p ++; - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - while ( p < pEnd ) { - /* start t-group */ - if ( !isdigit( UCINT *p ) || !(num_H = (int)inchi_strtol( p, &q, 10 ) ) || q > pEnd ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p = q; - num_Minus = 0; - if ( *p == '-' ) { - p ++; - if ( isdigit( UCINT *p ) ) { - num_Minus = (int)inchi_strtol( p, &q, 10 ); - p = q; - } else { - num_Minus = 1; - } - } - if ( p >= pEnd ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( !tg_alloc_len ) { - /* - --- header --- - [num_t_groups] - --- one t-group: --- - [len=group length no including this value] - [num_H] - [num_(-)] - [Endpoint(1),...,Endpoint(len-2)] - --- next t-group --- - ... - - Max. size = 1 + 3*max_num_t_groups + max_num_endpoints - - max_num_t_groups = num_at/2 - max_num_endpoints = num_at - - Max. size = 1 + 3*(num_at/2) + num_at = 1 + (5*num_at)/2 - 5 = 3 + INCHI_T_NUM_MOVABLE = 3 + num_types_of_attachments - - This does not include zero termination! - - */ - tg_alloc_len = ((3+INCHI_T_NUM_MOVABLE)*pInChI[iComponent].nNumberOfAtoms)/2+1; - for ( i = 0; i < mpy_component; i ++ ) { - pInChI[iComponent+i].nTautomer = (AT_NUMB*)inchi_calloc( tg_alloc_len+1, sizeof(pInChI->nTautomer[0])); - if ( !pInChI[iComponent+i].nTautomer ) { - ret = RI_ERR_ALLOC; /* allocation error */ - goto exit_function; - } - pInChI[iComponent+i].lenTautomer = 0; - } - tg_pos_Tautomer = 1; /* number atoms (NumAt+2) position */ - } else { - /* next t-group */ - tg_pos_Tautomer = lenTautomer; - } - if ( tg_pos_Tautomer+3 >= tg_alloc_len ) { - ret = RI_ERR_PROGR; /* wrong tautomer array length */ - goto exit_function; - } - pInChI[iComponent].nTautomer[tg_pos_Tautomer+1] = num_H; - pInChI[iComponent].nTautomer[tg_pos_Tautomer+2] = num_Minus; - lenTautomer = tg_pos_Tautomer+3; /* first atom number position */ - num_taut_H_component += num_H; - - while ( p < pEnd && isupper( UCINT *p) ) { - /* read list of tautomeric atoms */ - val = (int)inchi_strtol( p, &q, base ); - if ( lenTautomer >= tg_alloc_len || val > numCtAtoms ) { - ret = RI_ERR_PROGR; /* wrong tautomer array length */ - goto exit_function; - } - num_Atoms ++; - pInChI[iComponent].nTautomer[lenTautomer ++] = val; - p = q; - } - if ( !num_Atoms || p < pEnd && !isdigit( UCINT *p) ) { - ret = RI_ERR_PROGR; /* wrong tautomer array length */ - goto exit_function; - } - iTGroup ++; - pInChI[iComponent].nTautomer[tg_pos_Tautomer] = lenTautomer - tg_pos_Tautomer - 1; /* length of the rest of the t-group */ - pInChI[iComponent].lenTautomer = lenTautomer; - } - if ( !iTGroup || p != pEnd ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - pInChI[iComponent].nTautomer[0] = iTGroup; - - } else { - /* tautomeric groups: pass 1 */ - iTGroup = 0; - state = ')'; /* init as if the prev. t-group just ended */ - num_Atoms = 0; - /* Tautomeric info storage */ - /* NumGroups; ((NumAt+2, NumH, Num(-), At1..AtNumAt),...); {INCHI_T_NUM_MOVABLE = 2} */ - /* Allocated length: [5*nNumberOfAtoms/2+1], see Alloc_INChI(...) */ - while ( p < pEnd ) { - /* t-group */ - switch ( *p ) { - case '(': /* start t-group */ - switch ( state ) { - case ')': - state = *p ++; - num_H = 0; - num_Minus = 0; - continue; - default: - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - case ')': /* end t-group */ - switch ( state ) { - case 'A': /* previuos was atom number */ - if ( !tg_alloc_len ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - iTGroup ++; - state = *p ++; - pInChI[iComponent].nTautomer[tg_pos_Tautomer] = lenTautomer - tg_pos_Tautomer - 1; /* length of the rest of the t-group */ - pInChI[iComponent].lenTautomer = lenTautomer; - continue; - default: - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - case 'H': /* number of H */ - switch ( state ) { - case '(': - state = *p ++; - num_H = 1; - continue; - default: - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - case '-': /* number of (-) */ - switch ( state ) { - case 'N': /* previous was number of H */ - case 'H': /* previous was H */ - state = *p ++; - num_Minus = 1; - continue; - default: - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - case ',': - switch ( state ) { - case 'N': /* previous was number of H */ - case 'H': /* previous was H */ - case '-': /* previuos was - */ - case 'M': /* previous was number of (-) */ - /* the next must be the first tautomeric atom number; save num_H & num_Minus */ - if ( num_H <= 0 && num_Minus <= 0 ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( !tg_alloc_len ) { - /* - --- header --- - [num_t_groups] - --- one t-group: --- - [len=group length no including this value] - [num_H] - [num_(-)] - [Endpoint(1),...,Endpoint(len-2)] - --- next t-group --- - ... - - Max. size = 1 + 3*max_num_t_groups + max_num_endpoints - - max_num_t_groups = num_at/2 - max_num_endpoints = num_at - - Max. size = 1 + 3*(num_at/2) + num_at = 1 + (5*num_at)/2 - 5 = 3 + INCHI_T_NUM_MOVABLE = 3 + num_types_of_attachments - - This does not include zero termination! - - */ - tg_alloc_len = ((3+INCHI_T_NUM_MOVABLE)*pInChI[iComponent].nNumberOfAtoms)/2+1; - for ( i = 0; i < mpy_component; i ++ ) { - pInChI[iComponent+i].nTautomer = (AT_NUMB*)inchi_calloc( tg_alloc_len+1, sizeof(pInChI->nTautomer[0])); - if ( !pInChI[iComponent+i].nTautomer ) { - ret = RI_ERR_ALLOC; /* allocation error */ - goto exit_function; - } - pInChI[iComponent+i].lenTautomer = 0; - } - tg_pos_Tautomer = 1; /* number atoms (NumAt+2) position */ - } else { - /* next t-group */ - tg_pos_Tautomer = lenTautomer; - } - if ( tg_pos_Tautomer+3 >= tg_alloc_len ) { - ret = RI_ERR_PROGR; /* wrong tautomer array length */ - goto exit_function; - } - pInChI[iComponent].nTautomer[tg_pos_Tautomer+1] = num_H; - pInChI[iComponent].nTautomer[tg_pos_Tautomer+2] = num_Minus; - lenTautomer = tg_pos_Tautomer+3; /* first atom number position */ - num_taut_H_component += num_H; - state = *p ++; - continue; - case 'A': /* previuos was atom number */ - state = *p ++; - continue; - default: - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - default: - if ( isdigit( UCINT *p ) ) { - val = (int)inchi_strtol( p, &q, 10 ); - if ( val <= 0 ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p = q; - switch( state ) { - case 'H': - num_H = val; - state = 'N'; - continue; - case '-': - num_Minus = val; - state = 'M'; - continue; - case ',': - if ( lenTautomer >= tg_alloc_len || val > numCtAtoms ) { - ret = RI_ERR_PROGR; /* wrong tautomer array length */ - goto exit_function; - } - num_Atoms ++; - pInChI[iComponent].nTautomer[lenTautomer ++] = val; - state = 'A'; - continue; - default: - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - - } - } - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - if ( !iTGroup || state != ')' ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - pInChI[iComponent].nTautomer[0] = iTGroup; - } - } - /* check num_H in components; for bMobileH=TAUT_NON, pInChI->nNum_H_fixed[] has not been added to pInChI->nNum_H[] yet */ - if ( 0 > ( ret2 = GetInChIFormulaNumH( pInChI+iComponent, &num_H_formula) ) || - 0 > ( ret2 = GetInChINumH( pInChI+iComponent, &num_H_InChI ) ) ) { - ret = ret2; - goto exit_function; - } - if ( num_H_formula != num_H_InChI + (bMobileH==TAUT_NON? num_H_component:0) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - - /* duplicate according to multipolication */ - for ( i = 1; i < mpy_component; i ++ ) { - memcpy( nNum_H(iComponent+i), nNum_H(iComponent), pInChI[iComponent+i].nNumberOfAtoms * sizeof(nNum_H(0)[0]) ); - /* - memcpy( pInChI[iComponent+i].nNum_H, pInChI[iComponent].nNum_H, - pInChI[iComponent+i].nNumberOfAtoms * sizeof(pInChI[0].nNum_H[0]) ); - */ - if ( pInChI[iComponent+i].nTautomer && pInChI[iComponent].nTautomer && pInChI[iComponent].lenTautomer ) { - memcpy( pInChI[iComponent+i].nTautomer, pInChI[iComponent].nTautomer, - pInChI[iComponent].lenTautomer * sizeof(pInChI[0].nTautomer[0]) ); - pInChI[iComponent+i].lenTautomer = pInChI[iComponent].lenTautomer; - } - /* check num_H in components */ - if ( 0 > ( ret2 = GetInChIFormulaNumH( pInChI+iComponent+i, &num_H_formula) ) || - 0 > ( ret2 = GetInChINumH( pInChI+iComponent+i, &num_H_InChI ) ) ) { - ret = ret2; - goto exit_function; - } - if ( num_H_formula != num_H_InChI + (bMobileH==TAUT_NON? num_H_component:0) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - - /* prepare for the next component */ - iComponent += i; - if ( *pEnd ) { -#if (FIX_DALKE_BUGS == 1) - /* prevent crash on extra trailing ';' */ - if ( iComponent >= nNumComponents ) { - ret = RI_ERR_SYNTAX; /* syntax error: extra component */ - goto exit_function; - } -#endif - pStart = pEnd+1; - } else - break; - } - if ( nNumComponents != iComponent ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - ret = iComponent + 1; - -exit_function: - INCHI_HEAPCHK - return ret; - -} - -/****************************************************************************************/ -int ParseSegmentConnections( const char *str, int bMobileH, INChI **pInpInChI, int *pnNumComponents, int *pbAbc ) -{ -#define LAST_AT_LEN 256 - /* Pass 1: count bonds and find actual numbers of atom */ - int i, j, k, m, c, mpy_component; - int nNumComponents, iComponent, nNumAtoms, nNumBonds, lenConnTable, iBond; - const char *p, *q, *pStart, *pEnd; - AT_NUMB last_atom[LAST_AT_LEN], curAtom, maxAtom; - int num_open, state, ret, base; - INChI *pInChI = *pInpInChI; - LINKED_BONDS LB; - LINKED_BONDS *pLB = &LB; /* a list of linked lists of bonds, for each atom */ - AT_NUMB neighbor[MAXVAL]; - int bPrevVersion = -1; - - iComponent = 0; - if ( str[0] != 'c' ) { - if ( !pInChI && !*pnNumComponents ) { - int lenFormula = 1; - /* component has no formula; allocate InChI */ - lenConnTable = 0; - nNumComponents = 1; - /* allocate InChI */ - if ( !(pInChI = *pInpInChI = (INChI *)inchi_calloc( nNumComponents, sizeof(INChI) ) ) ) { - return RI_ERR_ALLOC; /* alloc failure */ - } - /* allocate empty formula */ - pInChI[iComponent].szHillFormula = (char *)inchi_calloc( lenFormula+1, sizeof(pInChI[0].szHillFormula[0]) ); - if ( !pInChI[iComponent].szHillFormula ) { - ret = RI_ERR_ALLOC; /* allocation failure */ - goto exit_function; - } - /* allocate empty connection table */ - pInChI[iComponent].nConnTable = (AT_NUMB *)inchi_calloc( lenConnTable+1, sizeof(pInChI[0].nConnTable[0]) ); - if ( !pInChI[iComponent].nConnTable ) { - ret = RI_ERR_ALLOC; /* allocation failure */ - goto exit_function; - } - pInChI[iComponent].lenConnTable = lenConnTable; - *pnNumComponents = nNumComponents; - } else { - lenConnTable = 1; - nNumComponents = *pnNumComponents; - for ( i = 0; i < nNumComponents; i ++ ) { - /* allocate 1 atom connection table */ - if ( pInChI[i].nConnTable ) { - inchi_free( pInChI[i].nConnTable ); - } - pInChI[i].nConnTable = (AT_NUMB *)inchi_calloc( lenConnTable+1, sizeof(pInChI[0].nConnTable[0]) ); - if ( !pInChI[i].nConnTable ) { - ret = RI_ERR_ALLOC; /* allocation failure */ - goto exit_function; - } - pInChI[i].nConnTable[0] = 1; - pInChI[i].lenConnTable = lenConnTable; - } - } - return 0; - } - - /* Pass 1. Re-Count atoms, count bonds */ - pStart = str+1; - nNumComponents = *pnNumComponents; -#if (FIX_DALKE_BUGS == 1) - /* prevent crash on too many components */ - if ( nNumComponents > MAX_ATOMS ) { - ret = RI_ERR_SYNTAX; /* syntax error: extra component */ - goto exit_function; - } -#endif - memset( pLB, 0, sizeof(pLB[0]) ); - - while( 1 ) { - /* cycle over components */ - if ( !(pEnd = strchr( pStart, ';' )) ) { - pEnd = pStart + strlen(pStart); - } - if ( (p = strchr(pStart, '*')) && p < pEnd ) { - mpy_component = (int)inchi_strtol( pStart, &q, 10 ); - if ( p != q -#if (FIX_DALKE_BUGS == 1) - || !isdigit( UCINT *pStart ) -#endif - ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - p ++; - } else { - mpy_component = 1; - p = pStart; - } -#if (FIX_DALKE_BUGS == 1) - if ( iComponent + mpy_component > MAX_ATOMS ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } -#endif - pStart = p; - /* Pass 1.1 parse a component */ - num_open = 0; - memset( last_atom, 0, sizeof(last_atom) ); - state='\0'; /* initial state */ - maxAtom = 0; - nNumBonds = 0; - curAtom = 0; - if ( p < pEnd && *pbAbc == -1 ) { - /* check if compressed InChI */ - *pbAbc = isupper( UCINT *p)? 1 : 0; - } - base = *pbAbc? ALPHA_BASE : 10; - - if ( *pbAbc == 1 ) { - nNumAtoms = 1; - while ( p < pEnd ) { - if ( *p == '-' ) { - if ( bPrevVersion == -1 ) { - /* previous InChI version */ - bPrevVersion = 1; - } else - if ( bPrevVersion != 1 ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - nNumAtoms --; - p ++; - } - if ( isdigit( UCINT *p ) ) { - if ( bPrevVersion == -1 ) { - /* curreny InChI, version 1 */ - bPrevVersion = 0; - } else - if ( bPrevVersion != 0 ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - nNumAtoms -= inchi_strtol( p, &p, 10 ); /* bypass digits */ - } - if ( *p != '-' && ( curAtom = (AT_NUMB)inchi_strtol( p, &q, base ) ) ) { - nNumAtoms ++; - nNumBonds ++; - p = q; - if ( maxAtom < curAtom ) - maxAtom = curAtom; - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - if ( maxAtom < nNumAtoms && nNumBonds ) { - maxAtom = nNumAtoms; - } - } else { - while ( p < pEnd ) { - /* atom number */ - c = UCINT *p ++; - switch ( c ) { - case '(': - case ')': - case ',': - case '-': - if ( state != 'N' ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - state = c; - num_open += (c=='(') - (c==')'); - if ( num_open < 0 ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - break; - default: - if ( isdigit( c ) && (curAtom = (AT_NUMB)inchi_strtol( p-1, &q, 10 )) ) { - p = q; - switch( state ) { - case '(': - case ')': - case ',': - case '-': - nNumBonds ++; - case '\0': - if ( maxAtom < curAtom ) - maxAtom = curAtom; - state = 'N'; - break; - default: - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - break; - } - } - if ( num_open ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - /* syntax error: parentheses do not match */ - } - } - /* Save the results and allocate memory */ - nNumAtoms = (int)maxAtom; /* 0 if empty connection table and no bonds present */ - lenConnTable = nNumAtoms + nNumBonds; - /* connection table format: At1[,Neigh11,Neigh12,...],At2[,Neigh21,Neigh22,...],AtN[NeighN1,NeighN2,...] */ - /* where AtK > NeighK1 > NeighK2,...; At(K) < At(K+1); the length = num.atoms + num.bonds */ - for ( i = 0; i < mpy_component; i ++ ) { - /* check number of atoms: the difference may be due to bridging H */ - if ( (j = pInChI[iComponent+i].nNumberOfAtoms) < nNumAtoms ) { - /* reallocate */ - U_CHAR *nAtomTmp = (U_CHAR *) inchi_malloc( nNumAtoms + 1 ); - if ( !nAtomTmp ) { - ret = RI_ERR_ALLOC; /* allocation failure */ - goto exit_function; - } - memcpy( nAtomTmp, pInChI[iComponent+i].nAtom, sizeof(nAtomTmp[0])*j); - while ( j < nNumAtoms ) { - nAtomTmp[j ++] = EL_NUMBER_H; /* bridging H */ - } - nAtomTmp[j] = '\0'; - INCHI_HEAPCHK - if ( pInChI[iComponent+i].nAtom ) { - inchi_free( pInChI[iComponent+i].nAtom ); - } - pInChI[iComponent+i].nAtom = nAtomTmp; - pInChI[iComponent+i].nNumberOfAtoms = nNumAtoms; - } else - if ( j > nNumAtoms && (lenConnTable || j != 1) ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - /* allocate connection table */ - if ( pInChI[iComponent+i].nConnTable ) { - inchi_free( pInChI[iComponent+i].nConnTable ); - } - if ( !nNumAtoms && !nNumBonds && !lenConnTable ) { - lenConnTable = 1; /* one atom, no bonds */ - } - pInChI[iComponent+i].nConnTable = (AT_NUMB *)inchi_calloc( lenConnTable+1, sizeof(pInChI[0].nConnTable[0]) ); - if ( !pInChI[iComponent+i].nConnTable ) { - ret = RI_ERR_ALLOC; /* allocation failure */ - goto exit_function; - } - pInChI[iComponent+i].lenConnTable = lenConnTable; - } - - /* Pass 1.2 parse a component and extract the bonds */ - num_open = 0; - memset( last_atom, 0, sizeof(last_atom) ); - state='\0'; /* initial state */ - iBond = 0; - p = pStart; - pLB->len = 0; - - if ( *pbAbc == 1 ) { - /* compressed */ - int num_neigh; - num_open = 0; - last_atom[num_open] = 2; - while ( p < pEnd ) { - if ( last_atom[num_open] > maxAtom ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( isupper( UCINT *p ) ) { - curAtom = (AT_NUMB)inchi_strtol( p, &q, base ); - if ( ret = AddLinkedBond( last_atom[num_open], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { - goto exit_function; - } - p = q; - if ( bPrevVersion == 1 ) { - while ( p < pEnd && *p == '-' ) { - p ++; - if ( curAtom = (AT_NUMB)inchi_strtol( p, &q, base ) ) { - if ( ret = AddLinkedBond( last_atom[num_open], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { - goto exit_function; - } - p = q; - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - } else - if ( bPrevVersion == 0 && isdigit( *p ) ) { - num_neigh = (int)inchi_strtol( p, &q, 10 ); - p = q; - while( num_neigh -- && p < pEnd ) { - if ( curAtom = (AT_NUMB)inchi_strtol( p, &q, base ) ) { - if ( ret = AddLinkedBond( last_atom[num_open], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { - goto exit_function; - } - p = q; - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - } - last_atom[num_open] ++; - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } - } else { - while ( p < pEnd ) { - /* each atom number except the first means a new bond */ - c = UCINT *p ++; - switch ( c ) { - case '(': - case ')': - case ',': - case '-': - switch ( state ) { - case 'N': - state = c; - break; - default: - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - break; - default: - if ( isdigit( c ) && (curAtom = (AT_NUMB)inchi_strtol( p-1, &q, 10 )) ) { - p = q; - switch( state ) { - case '\0': - last_atom[num_open] = curAtom; - state = 'N'; - break; - case '(': - if ( ret = AddLinkedBond( last_atom[num_open], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { - goto exit_function; - } - if ( ++ num_open >= LAST_AT_LEN ) { - ret = RI_ERR_PROGR; /* program error: buffer overflow */ - goto exit_function; - } - last_atom[num_open] = curAtom; - state = 'N'; - break; - - case ')': - if ( !num_open ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( ret = AddLinkedBond( last_atom[--num_open], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { - goto exit_function; - } - last_atom[num_open] = curAtom; - state = 'N'; - break; - - case ',': - if ( !num_open ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - if ( ret = AddLinkedBond( last_atom[num_open-1], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { - goto exit_function; - } - last_atom[num_open] = curAtom; - state = 'N'; - break; - case '-': - if ( ret = AddLinkedBond( last_atom[num_open], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { - goto exit_function; - } - last_atom[num_open] = curAtom; - state = 'N'; - break; - default: - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - } else { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - break; - } - } - } - /* store the bonds in connection table */ - if ( lenConnTable > 1 ) { - for ( i = 0, m = 0; i < nNumAtoms; i ++ ) { - k = 0; - if ( j = pLB->pBond[i+1].prev ) { - while( k < MAXVAL ) { - neighbor[k++] = pLB->pBond[j].neigh; - if ( j == i+1 ) - break; - j = pLB->pBond[j].prev; - } - } - if ( j != i+1 ) { - ret = RI_ERR_SYNTAX; /* syntax error */ - goto exit_function; - } - /* sort the neighbors */ - insertions_sort_AT_NUMB( neighbor, k ); - pInChI[iComponent].nConnTable[m ++] = i+1; /* atom number */ - for ( j = 0; j < k && (int)neighbor[j] <= i; j ++ ) { - pInChI[iComponent].nConnTable[m ++] = neighbor[j]; - } - } - if ( m != lenConnTable ) { - ret = RI_ERR_PROGR; /* program error */ - goto exit_function; - } - } else { - pInChI[iComponent].nConnTable[0] = 1; /* single atom */ - } - /* duplicate if needed */ - for ( i = 1; i < mpy_component; i ++ ) { - /* - if ( pInChI[iComponent+i].nConnTable ) { - inchi_free( pInChI[iComponent+i].nConnTable ); - } - pInChI[iComponent+i].nConnTable = (AT_NUMB *)inchi_calloc( lenConnTable+1, sizeof(pInChI[0].nConnTable[0]) ); - if ( !pInChI[iComponent+i].nConnTable ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - */ - if ( !pInChI[iComponent+i].nConnTable || pInChI[iComponent+i].lenConnTable != lenConnTable ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - memcpy ( pInChI[iComponent+i].nConnTable, pInChI[iComponent].nConnTable, lenConnTable*sizeof(pInChI[0].nConnTable[0])); - } - /* prepare for the next connection table */ - iComponent += i; - if ( *pEnd ) - pStart = pEnd+1; - else - break; - - } - ret = iComponent; - -exit_function: - if ( pLB->pBond ) { - INCHI_HEAPCHK - inchi_free( pLB->pBond ); - } - return ret; - -#undef LAST_AT_LEN -} -/****************************************************************************************/ -int nFillOutProtonMobileH( INChI *pInChI ) -{ - int len = 1; - pInChI->bDeleted = 1; - /* formula */ - if ( !pInChI->szHillFormula && - !( pInChI->szHillFormula = (char *) inchi_calloc( len+1, sizeof(pInChI->szHillFormula[0]) ) ) ) { - return RI_ERR_ALLOC; /* alloc failure */ - } - strcpy( pInChI->szHillFormula, "H" ); - pInChI->nNumberOfAtoms = 1; - - /* atoms */ - if ( !pInChI->nAtom && - !(pInChI->nAtom = (U_CHAR *) inchi_calloc( len+1, sizeof(pInChI->nAtom[0]) ) ) ) { - return RI_ERR_ALLOC; /* alloc failure */ - } - pInChI->nAtom[0] = 1; - /* charge */ - pInChI->nTotalCharge = 1; - /* connection table */ - if ( !pInChI->nConnTable && - !(pInChI->nConnTable = (AT_NUMB *) inchi_calloc( len+1, sizeof(pInChI->nConnTable[0]) ) ) ) { - return RI_ERR_ALLOC; /* alloc failure */ - } - pInChI->nConnTable[0] = 1; - pInChI->lenConnTable = len; - /* tautomer */ - if ( !pInChI->nTautomer && - !(pInChI->nTautomer = (AT_NUMB *) inchi_calloc( len+1, sizeof(pInChI->nTautomer[0]) ) ) ) { - return RI_ERR_ALLOC; /* alloc failure */ - } - /* nNum_H */ - if ( !pInChI->nNum_H && - !(pInChI->nNum_H = (S_CHAR *) inchi_calloc( len+1, sizeof(pInChI->nNum_H[0]) ) ) ) { - return RI_ERR_ALLOC; /* alloc failure */ - } - pInChI->nNum_H[0] = 0; - - pInChI->nTautomer[0] = 0; - pInChI->lenTautomer = 1; - return 0; -} -/****************************************************************************************/ -int nProtonCopyIsotopicInfo( INChI *pInChI_to, INChI *pInChI_from ) -{ - if ( pInChI_from->nNumberOfIsotopicAtoms ) { - if ( pInChI_to->nNumberOfIsotopicAtoms && - pInChI_from->nNumberOfIsotopicAtoms > pInChI_to->nNumberOfIsotopicAtoms ) { - - inchi_free( pInChI_to->IsotopicAtom ); - pInChI_to->IsotopicAtom = NULL; - pInChI_to->nNumberOfIsotopicAtoms = 0; - } - if ( !pInChI_to->IsotopicAtom && - !(pInChI_to->IsotopicAtom = - (INChI_IsotopicAtom *)inchi_calloc(pInChI_from->nNumberOfIsotopicAtoms, - sizeof(pInChI_to->IsotopicAtom[0]) ) ) ) { - return RI_ERR_ALLOC; - } - pInChI_to->nNumberOfIsotopicAtoms = pInChI_from->nNumberOfIsotopicAtoms; - memcpy( pInChI_to->IsotopicAtom, pInChI_from->IsotopicAtom, - pInChI_from->nNumberOfIsotopicAtoms * sizeof(pInChI_to->IsotopicAtom[0]) ); - } else { - if ( pInChI_to->IsotopicAtom ) inchi_free( pInChI_to->IsotopicAtom ); - pInChI_to->IsotopicAtom = NULL; - pInChI_to->nNumberOfIsotopicAtoms = 0; - } - return 0; -} -/****************************************************************************************/ -int ParseSegmentFormula( const char *str, int bMobileH, INChI *pInpInChI[], int pnNumComponents[] ) -{ - int i, j, mpy_component, mpy_atom, len, el_number; - int nNumComponents = 0, iComponent, nNumAtoms, nNumAtomsAndH, iAtom, nNumH, nAltMobileH = ALT_TAUT(bMobileH); - const char *p, *q, *e, *pStart, *pEnd; - INChI *pInChI; - char szEl[3]; - - nNumAtoms = -999; /* impossible value */ - - /* Pass 1. Count components */ - pStart = str; - while( 1 ) { - if ( !(pEnd = strchr( pStart, '.' )) ) { - pEnd = pStart + strlen(pStart); - } - p = pStart; - if ( isdigit( *p ) ) { - mpy_component = (int)inchi_strtol( p, &q, 10 ); - p = q; - } else { - mpy_component = 1; - } - if ( !mpy_component ) - break; - if ( !isupper( UCINT *p ) ) { - break; /* not a formula layer */ - } - if ( pEnd == p ) - break; /* zero length formula */ - nNumComponents += mpy_component; - if ( *pEnd ) - pStart = pEnd+1; - else - break; - } - pnNumComponents[bMobileH] = nNumComponents; - -#if ( FIX_DALKE_BUGS == 1 ) - if ( nNumComponents > MAX_ATOMS ) { - return RI_ERR_SYNTAX; /* syntax error */ - } -#endif - /* exit or error check */ - if ( !nNumComponents ) { - if ( !*pStart || islower( UCINT *pStart ) ) { - INCHI_HEAPCHK - if ( bMobileH == TAUT_NON && 0 < ( nNumComponents = pnNumComponents[nAltMobileH]) ) { - /* allocate InChI */ - if ( !( pInChI = (INChI *)inchi_calloc( nNumComponents, sizeof(INChI) ) ) ) { - return RI_ERR_ALLOC; /* alloc failure */ - } - pInpInChI[bMobileH] = pInChI; - pnNumComponents[bMobileH] = nNumComponents; - for ( i = 0; i < nNumComponents; i ++ ) { - /* copy number of atoms */ - len = pInpInChI[bMobileH][i].nNumberOfAtoms = pInpInChI[nAltMobileH][i].nNumberOfAtoms; - /* copy atoms */ - len = (len+1)*sizeof(pInpInChI[0][0].nAtom[0]); - if ( pInpInChI[bMobileH][i].nAtom ) { - inchi_free( pInpInChI[bMobileH][i].nAtom ); - } - if ( pInpInChI[bMobileH][i].nAtom = (U_CHAR *) inchi_malloc( (len + 1) * sizeof(pInpInChI[0][0].nAtom[0]) ) ) { - memcpy(pInpInChI[bMobileH][i].nAtom, pInpInChI[nAltMobileH][i].nAtom, len); - pInpInChI[bMobileH][i].nAtom[len] = 0; - } else { - return RI_ERR_ALLOC; /* alloc failure */ - } - /* copy Hill formula */ - len = strlen( pInpInChI[nAltMobileH][i].szHillFormula)+1; - if ( pInpInChI[bMobileH][i].szHillFormula ) { - inchi_free( pInpInChI[bMobileH][i].szHillFormula ); - } - if ( pInpInChI[bMobileH][i].szHillFormula = (char *) inchi_malloc( inchi_max(len,2) ) ) { - memcpy( pInpInChI[bMobileH][i].szHillFormula, pInpInChI[nAltMobileH][i].szHillFormula, len); - } else { - return RI_ERR_ALLOC; /* alloc failure */ - } - } - - } else - if ( bMobileH == TAUT_YES ) { - int ret; - /* allocate InChI */ - nNumComponents = 1; - /* InChI */ - pnNumComponents[bMobileH] = nNumComponents; - if ( !( pInChI = (INChI *)inchi_calloc( nNumComponents, sizeof(INChI) ) ) ) { - return RI_ERR_ALLOC; /* alloc failure */ - } - pInpInChI[bMobileH] = pInChI; - ret = nFillOutProtonMobileH( pInChI ); - if ( ret < 0 ) { - return ret; - } - } - return 0; - } - return RI_ERR_SYNTAX; /* syntax error */ - } - if ( *pEnd ) { - return RI_ERR_SYNTAX; /* syntax error */ - } - - /* allocate InChI */ - if ( !( pInpInChI[bMobileH] = (INChI *)inchi_calloc( nNumComponents, sizeof(INChI) ) ) ) { - return RI_ERR_ALLOC; /* alloc failure */ - } - pInChI = pInpInChI[bMobileH]; - - /* Pass 2. Count elements, save formulas and elements */ - pStart = str; - iComponent = 0; - while( 1 ) { - if ( !(pEnd = strchr( pStart, '.' )) ) { - pEnd = pStart + strlen(pStart); - } - p = pStart; - if ( isdigit( UCINT *p ) ) { - mpy_component = (int)inchi_strtol( p, &q, 10 ); - p = q; - } else { - mpy_component = 1; - } -#if ( FIX_DALKE_BUGS == 1 ) - if ( iComponent + mpy_component > MAX_ATOMS ) { - return RI_ERR_SYNTAX; /* syntax error */ - } -#endif - len = pEnd-p; - for ( i = 0; i < mpy_component; i ++ ) { - if ( pInChI[iComponent+i].szHillFormula ) { - inchi_free( pInChI[iComponent+i].szHillFormula ); - } - pInChI[iComponent+i].szHillFormula = (char*) inchi_malloc( inchi_max(len,1)+1 ); - memcpy( pInChI[iComponent].szHillFormula, p, len ); - pInChI[iComponent+i].szHillFormula[len] = '\0'; - if ( !i ) { - /* Pass 2.1 Parse formula and count atoms except H */ - nNumAtoms = 0; - nNumH = 0; - nNumAtomsAndH = 0; - e = pInChI[iComponent].szHillFormula; - while ( *e ) { - if ( !isupper( UCINT *e ) ) { - return RI_ERR_SYNTAX; - } - j = 0; - szEl[j ++] = *e ++; - if ( *e && islower( UCINT *e ) ) - szEl[j ++] = *e ++; - szEl[j ++] = '\0'; - if ( *e && isdigit( UCINT *e ) ) { - mpy_atom = (int)inchi_strtol( e, &q, 10 ); - e = q; - } else { - mpy_atom = 1; - } - if ( !mpy_atom ) { - return RI_ERR_SYNTAX; - } - if ( szEl[0] == 'H' && !szEl[1] ) { - nNumH += mpy_atom; - continue; /* ignore H in counting number of atoms */ - } - - nNumAtoms += mpy_atom; - } -#if ( FIX_DALKE_BUGS == 1 ) - if ( nNumAtoms > MAX_ATOMS ) { - return RI_ERR_SYNTAX; /* syntax error */ - } -#endif - nNumAtomsAndH = nNumAtoms? nNumAtoms : (nNumH > 0); - pInChI[iComponent+i].nNumberOfAtoms = nNumAtomsAndH; - if ( pInChI[iComponent+i].nAtom ) { - inchi_free( pInChI[iComponent+i].nAtom ); - } - pInChI[iComponent+i].nAtom = (U_CHAR *) inchi_malloc((nNumAtomsAndH+1)*sizeof(pInChI[0].nAtom[0])); - if ( !pInChI[iComponent+i].nAtom ) - return RI_ERR_ALLOC; /* failed allocation */ - /* Pass 2.2 Store elements; this assumes no bridging H. Bridging H will be found in connection table, /c */ - iAtom = 0; - if ( nNumAtoms > 0 ) { - e = pInChI[iComponent+i].szHillFormula; - while ( *e ) { - if ( !isupper( UCINT *e ) ) { - return RI_ERR_SYNTAX; - } - j = 0; - szEl[j ++] = *e ++; - if ( *e && islower( UCINT *e ) ) - szEl[j ++] = *e ++; - szEl[j ++] = '\0'; - if ( *e && isdigit( UCINT *e ) ) { - mpy_atom = (int)inchi_strtol( e, &q, 10 ); - e = q; - } else { - mpy_atom = 1; - } - if ( !mpy_atom ) { - return RI_ERR_SYNTAX; - } - if ( szEl[0] == 'H' && !szEl[1] ) - continue; /* ignore H */ - el_number = get_periodic_table_number( szEl ); - if ( el_number == ERR_ELEM ) { - return RI_ERR_SYNTAX; /* wrong element */ - } - while ( mpy_atom -- ) { - if ( iAtom >= nNumAtoms ) { - return RI_ERR_PROGR; /* program error */ - } - pInChI[iComponent+i].nAtom[iAtom ++] = (U_CHAR)el_number; - } - } - } else - if ( nNumH > 0 ) { - pInChI[iComponent+i].nAtom[iAtom ++] = EL_NUMBER_H; - nNumAtoms = 1; - } - pInChI[iComponent+i].nAtom[iAtom] = '\0'; - if ( nNumAtoms != iAtom ) { - return RI_ERR_PROGR; /* program error */ - } - } else { - /* Copy duplicated formula */ - strcpy(pInChI[iComponent+i].szHillFormula, pInChI[iComponent].szHillFormula); - /* Copy atoms in the duplicated formula */ - pInChI[iComponent+i].nNumberOfAtoms = nNumAtoms; - if ( pInChI[iComponent+i].nAtom ) { - inchi_free( pInChI[iComponent+i].nAtom ); - } - pInChI[iComponent+i].nAtom = (U_CHAR *) inchi_malloc(nNumAtoms+1); - if ( !pInChI[iComponent+i].nAtom ) - return RI_ERR_ALLOC; /* failed allocation */ - memcpy( pInChI[iComponent+i].nAtom, pInChI[iComponent].nAtom, nNumAtoms+1 ); - } - } - iComponent += i; - if ( *pEnd ) { - if ( *pEnd != '.' ) { - return RI_ERR_SYNTAX; /* syntax error */ - } - pStart = pEnd+1; - } else - break; - } - - if ( iComponent != nNumComponents ) { - return RI_ERR_PROGR; /* program error */ - } - if ( bMobileH == TAUT_NON ) { - /* at this point the exact number of atoms including bridging H is known from TAUT_YES */ - for ( i = 0; i < nNumComponents && i < pnNumComponents[nAltMobileH]; i ++ ) { - if ( pInpInChI[bMobileH][i].nNumberOfAtoms < (len=pInpInChI[nAltMobileH][i].nNumberOfAtoms) ) { - /* there are bridging H in this component */ - if ( pInpInChI[nAltMobileH][i].nAtom ) { - U_CHAR *nAtom = (U_CHAR *) inchi_malloc( (len+1) * sizeof(nAtom[0]) ); - if ( !nAtom ) { - return RI_ERR_ALLOC; - } - memcpy( nAtom, pInpInChI[nAltMobileH][i].nAtom, len*sizeof(nAtom[0]) ); - nAtom[ len ] = 0; - if ( pInpInChI[bMobileH][i].nAtom ) { - inchi_free( pInpInChI[bMobileH][i].nAtom ); - } - pInpInChI[bMobileH][i].nAtom = nAtom; - } - pInpInChI[bMobileH][i].nNumberOfAtoms = len; - } - } - } - - return nNumComponents+1; -} - -/****************************************************************************************/ -int CopySegment( INChI *pInChITo, INChI *pInChIFrom, int SegmentType, int bIsotopicTo, int bIsotopicFrom) -{ - int ret = RI_ERR_ALLOC; - int len; - - - if ( SegmentType==CPY_SP2 || - SegmentType==CPY_SP3 || - SegmentType==CPY_SP3_M || - SegmentType==CPY_SP3_S ) { - - INChI_Stereo **pstereoTo = NULL; - INChI_Stereo *stereoFrom = bIsotopicFrom==1? pInChIFrom->StereoIsotopic : - bIsotopicFrom==0? pInChIFrom->Stereo : NULL; - if ( stereoFrom || bIsotopicFrom < 0 ) { - if ( SegmentType==CPY_SP2 ) { - if ( bIsotopicFrom < 0 || - stereoFrom->b_parity && - stereoFrom->nBondAtom1 && - stereoFrom->nBondAtom2 ) { - - len = (bIsotopicFrom < 0)? 0 : stereoFrom->nNumberOfStereoBonds; - pstereoTo = bIsotopicTo? &pInChITo->StereoIsotopic : &pInChITo->Stereo; - if ( !pstereoTo[0] ) { - if ( !(pstereoTo[0] = (INChI_Stereo *)inchi_calloc( 1, sizeof(**pstereoTo))) ) { - goto exit_function; - } - } - if ( pstereoTo[0]->nNumberOfStereoBonds > 0 || pstereoTo[0]->b_parity || - pstereoTo[0]->nBondAtom1 || pstereoTo[0]->nBondAtom2 ) { - ret = RI_ERR_SYNTAX; /* stereo already exists */ - goto exit_function; - } - /* allocate sp2 stereo */ - if ( !(pstereoTo[0]->b_parity = (S_CHAR *)inchi_calloc( len+1, sizeof(pstereoTo[0]->b_parity[0]) ) ) || - !(pstereoTo[0]->nBondAtom1 = (AT_NUMB *)inchi_calloc( len+1, sizeof(pstereoTo[0]->nBondAtom1[0]) ) ) || - !(pstereoTo[0]->nBondAtom2 = (AT_NUMB *)inchi_calloc( len+1, sizeof(pstereoTo[0]->nBondAtom2[0]) ) ) ) { - /* cleanup */ - if ( pstereoTo[0]->b_parity ) { - INCHI_HEAPCHK - inchi_free( pstereoTo[0]->b_parity ); - pstereoTo[0]->b_parity = NULL; - } - if ( pstereoTo[0]->nBondAtom1 ) { - INCHI_HEAPCHK - inchi_free( pstereoTo[0]->nBondAtom1 ); - pstereoTo[0]->nBondAtom1 = NULL; - } - if ( pstereoTo[0]->nBondAtom2 ) { - INCHI_HEAPCHK - inchi_free( pstereoTo[0]->nBondAtom2 ); - pstereoTo[0]->nBondAtom2 = NULL; - } - INCHI_HEAPCHK - goto exit_function; - } - /* copy stereo */ - if ( bIsotopicFrom >= 0 && len ) { - memcpy( pstereoTo[0]->b_parity, stereoFrom->b_parity, (len+1)*sizeof(pstereoTo[0]->b_parity[0]) ); - memcpy( pstereoTo[0]->nBondAtom1, stereoFrom->nBondAtom1, (len+1)*sizeof(pstereoTo[0]->nBondAtom1[0]) ); - memcpy( pstereoTo[0]->nBondAtom2, stereoFrom->nBondAtom2, (len+1)*sizeof(pstereoTo[0]->nBondAtom2[0]) ); - } - pstereoTo[0]->nNumberOfStereoBonds = len; - - return len+1; - } else { - return 0; - } - } else - if ( SegmentType==CPY_SP3 ) { - if ( bIsotopicFrom < 0 || - stereoFrom->t_parity && - stereoFrom->nNumber ) { - - len = (bIsotopicFrom < 0)? 0 : stereoFrom->nNumberOfStereoCenters; - pstereoTo = bIsotopicTo? &pInChITo->StereoIsotopic : &pInChITo->Stereo; - if ( !pstereoTo[0] ) { - if ( !(pstereoTo[0] = (INChI_Stereo *)inchi_calloc( 1, sizeof(**pstereoTo))) ) { - goto exit_function; - } - } - if ( pstereoTo[0]->nNumberOfStereoCenters > 0 || pstereoTo[0]->t_parity || - pstereoTo[0]->nNumber ) { - ret = RI_ERR_SYNTAX; /* stereo already exists */ - goto exit_function; - } - /* allocate sp3 stereo */ - if ( !(pstereoTo[0]->t_parity = (S_CHAR *)inchi_calloc( len+1, sizeof(pstereoTo[0]->b_parity[0]) ) ) || - !(pstereoTo[0]->nNumber = (AT_NUMB *)inchi_calloc( len+1, sizeof(pstereoTo[0]->nBondAtom1[0]) ) ) ) { - /* cleanup */ - if ( pstereoTo[0]->t_parity ) { - inchi_free( pstereoTo[0]->t_parity ); - pstereoTo[0]->t_parity = NULL; - } - if ( pstereoTo[0]->nNumber ) { - inchi_free( pstereoTo[0]->nNumber ); - pstereoTo[0]->nNumber = NULL; - } - goto exit_function; - } - /* copy stereo */ - if ( bIsotopicFrom >= 0 && len ) { - memcpy( pstereoTo[0]->t_parity, stereoFrom->t_parity, (len+1)*sizeof(pstereoTo[0]->t_parity[0]) ); - memcpy( pstereoTo[0]->nNumber, stereoFrom->nNumber, (len+1)*sizeof(pstereoTo[0]->nNumber[0]) ); - } - pstereoTo[0]->nNumberOfStereoCenters = len; - return len+1; - } else { - return 0; - } - } else - if ( SegmentType==CPY_SP3_M ) { - pstereoTo = bIsotopicTo? &pInChITo->StereoIsotopic : &pInChITo->Stereo; - if ( !pstereoTo[0] ) { - if ( !(pstereoTo[0] = (INChI_Stereo *)inchi_calloc( 1, sizeof(**pstereoTo))) ) { - goto exit_function; - } - } - if ( pstereoTo[0]->nCompInv2Abs && NO_VALUE_INT != pstereoTo[0]->nCompInv2Abs ) { - ret = RI_ERR_SYNTAX; /* stereo already exists */ - goto exit_function; - } - if ( bIsotopicFrom < 0 ) { - pstereoTo[0]->nCompInv2Abs = 0; - } else { - pstereoTo[0]->nCompInv2Abs = stereoFrom->nCompInv2Abs; - } - return 1; - } else - /* use bTrivialInv to save /s1, /s2, /s3 */ - if ( SegmentType==CPY_SP3_S ) { - pstereoTo = bIsotopicFrom? &pInChITo->StereoIsotopic : &pInChITo->Stereo; - if ( !pstereoTo[0] ) { - if ( !(pstereoTo[0] = (INChI_Stereo *)inchi_calloc( 1, sizeof(**pstereoTo))) ) { - goto exit_function; - } - } - if ( pstereoTo[0]->bTrivialInv ) { - ret = RI_ERR_SYNTAX; /* stereo already exists */ - goto exit_function; - } - pstereoTo[0]->bTrivialInv = stereoFrom->bTrivialInv; - if ( bIsotopicFrom < 0 ) { - pstereoTo[0]->bTrivialInv = 0; - } else { - pstereoTo[0]->bTrivialInv = stereoFrom->bTrivialInv; - } - return 1; - } - } - return 0; /* nothing to copy */ - } else - if ( SegmentType == CPY_ISO_AT ) { - int nNumberOfIsotopicAtoms = pInChIFrom->nNumberOfIsotopicAtoms; - INChI_IsotopicAtom **pIsotopicAtomTo = NULL; - INChI_IsotopicAtom *IsotopicAtomFrom = pInChIFrom->IsotopicAtom; - if ( bIsotopicFrom < 0 || IsotopicAtomFrom ) { - len = (bIsotopicFrom < 0)? 0 : nNumberOfIsotopicAtoms; - pIsotopicAtomTo = &pInChITo->IsotopicAtom; - if ( !*pIsotopicAtomTo ) { - if ( !( *pIsotopicAtomTo = (INChI_IsotopicAtom *)inchi_calloc( len+1, sizeof(**pIsotopicAtomTo) ) ) ) { - goto exit_function; - } - } - if ( pInChITo->nNumberOfIsotopicAtoms ) { - ret = RI_ERR_SYNTAX; /* stereo already exists */ - goto exit_function; - } - if ( bIsotopicFrom >= 0 && len ) { - memcpy( *pIsotopicAtomTo, IsotopicAtomFrom, (len+1)*sizeof(**pIsotopicAtomTo) ); - } - pInChITo->nNumberOfIsotopicAtoms = len; - return len+1; - } - return 0; - } - ret = RI_ERR_PROGR; /* program error */ -exit_function: - return ret; - - -} - -/**********************************************************************************/ -/* Sort neighbors in ascending order */ -int insertions_sort_AT_NUMB( AT_NUMB *base, int num ) -{ - AT_NUMB *i, *j, *pk, tmp; - int k, num_trans = 0; - for( k=1, pk = base; k < num; k++, pk ++ ) { - for( j = (i = pk) + 1, tmp = *j; j > base && *i > tmp; j=i, i -- ) { - *j = *i; - num_trans ++; - } - *j = tmp; - } - return num_trans; -} - - -/* read */ - -int getInChIChar( INCHI_IOSTREAM *pInp ) -{ - if (pInp->type==INCHI_IOSTREAM_STRING) - { - /* input from string */ - if ( pInp->s.nPtr < pInp->s.nUsedLength ) - return (int) pInp->s.pStr[pInp->s.nPtr++]; - return RI_ERR_EOF; - } - - else - { - /* input from plain file */ - int c; -#if ( defined(_MSC_VER)&&defined(_WIN32) || defined(__BORLANDC__)&&defined(__WIN32__) || defined(__GNUC__)&&defined(__MINGW32__)&&defined(_WIN32) ) - do - { - c = getc( pInp->f ); - if ( c == EOF ) - { - c = RI_ERR_EOF; - break; - } - } - while( c == '\r' ); -#else - c = getc( pInp->f ); - if ( c == EOF ) - { - c = RI_ERR_EOF; - } -#endif - return c; - } - - -} - -int AddInChIChar( INCHI_IOSTREAM *pInp, SEGM_LINE *Line, const char *pszToken ) -{ - int c = getInChIChar( pInp ); - /* - while ( c == '\r' ) { - c = getInChIChar( pInp ); - } - */ - INCHI_HEAPCHK - if ( Line->len + 2 >= Line->len_alloc ) { - char *str = (char *) inchi_calloc( Line->len_alloc + SEGM_LINE_ADD, sizeof(str[0]) ); - INCHI_HEAPCHK - if ( str ) { - if ( Line->len > 0 && Line->str ) { - memcpy( str, Line->str, sizeof(str[0]) * Line->len ); - Line->len_alloc += SEGM_LINE_ADD; - inchi_free( Line->str ); - INCHI_HEAPCHK - } else { - Line->len_alloc += SEGM_LINE_ADD; - } - Line->str = str; - } else { - c = RI_ERR_ALLOC; /* fatal error */ - goto exit_function; - } - } - INCHI_HEAPCHK - if ( c < 0 ) { - Line->str[Line->len] = '\0'; - INCHI_HEAPCHK - c = RI_ERR_SYNTAX; /* fatal error: wrong char */ - goto exit_function; - } - if ( c && strchr( pszToken, c ) ) { - Line->str[Line->len] = '\0'; - INCHI_HEAPCHK - c = -(c+2); - goto exit_function; - } else - if ( !c && !Line->len ) { - Line->str[Line->len] = c; - INCHI_HEAPCHK - } else { - Line->str[Line->len ++] = c; - INCHI_HEAPCHK - } -exit_function: - INCHI_HEAPCHK - return c; -} -int nGetInChISegment( INCHI_IOSTREAM *pInp, SEGM_LINE *Line, const char *pszToken ) -{ - int c; - Line->len = 0; - while( 0 < (c = AddInChIChar( pInp, Line, pszToken ) ) ) - ; - if ( c < - 2 ) { - c = -(c+2); - } - Line->c = c; - return c; -} -/********************************************************************************/ -/* add one more bond to the linked lists for both neighbors */ -int AddLinkedBond( AT_NUMB at1, AT_NUMB at2, AT_NUMB num_at, LINKED_BONDS *pLB ) -{ - int nReqLen = inchi_max( 2*num_at+2, pLB->len + 2 ); - AT_NUMB prev; - if ( pLB->len_alloc <= nReqLen ) { - /*int nNewLen = nReqLen + (nReqLen + LINKED_BOND_ADD - 1)%LINKED_BOND_ADD + LINKED_BOND_ADD;*/ - int nNewLen = nReqLen - nReqLen%LINKED_BOND_ADD + 2*LINKED_BOND_ADD; - ONE_LINKED_BOND *pBond = (ONE_LINKED_BOND *)inchi_calloc( nNewLen, sizeof(pBond[0]) ); - if ( !pBond ) - return RI_ERR_ALLOC; /* allocation error */ - if ( pLB->pBond && pLB->len ) { - memcpy( pBond, pLB->pBond, pLB->len*sizeof(pBond[0]) ); - } - if ( pLB->pBond ) - inchi_free( pLB->pBond ); - pLB->pBond = pBond; - pLB->len_alloc = nNewLen; - } - if ( !pLB->len ) { - pLB->len = num_at+1; - memset( pLB->pBond, 0, (num_at+1)*sizeof(pLB->pBond[0]) ); - } - - prev = pLB->pBond[at1].prev; /* position of the last neighbor of at1 in the pLB->pBond */ - if ( !prev ) { - pLB->pBond[at1].neigh = at2; - pLB->pBond[at1].prev = at1; - } else { - pLB->pBond[pLB->len].neigh = at2; - pLB->pBond[pLB->len].prev = prev; - pLB->pBond[at1].prev = pLB->len ++; - } - - prev = pLB->pBond[at2].prev; /* position of the last neighbor of at2 in the pLB->pBond */ - if ( !prev ) { - pLB->pBond[at2].neigh = at1; - pLB->pBond[at2].prev = at2; - } else { - pLB->pBond[pLB->len].neigh = at1; - pLB->pBond[pLB->len].prev = prev; - pLB->pBond[at2].prev = pLB->len ++; - } - return 0; -} - -#endif /* READ_INCHI_STRING */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include + +/* #define CHECK_WIN32_VC_HEAP */ + +#include "mode.h" + +#if ( READ_INCHI_STRING == 1 ) + +#include "ichierr.h" +#include "ichi.h" +#include "ichitime.h" +#include "ichidrp.h" +#include "inpdef.h" +#include "util.h" +#include "strutil.h" +#include "ichi_io.h" + +/* reverse InChI */ +#include "ichimain.h" +#include "extr_ct.h" +#include "ichitaut.h" +#include "ichister.h" +#include "strutil.h" +#include "ichisize.h" +#include "ichiring.h" +#include "ichinorm.h" +#include "ichierr.h" +#include "ichicant.h" + +#include "ichirvrs.h" +#include "mol_fmt.h" + + +/* */ +#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_STANDALONE) ) +#include "inchi_api.h" +#endif +/* */ + + + +typedef struct tagLine +{ + char *str; + int len; + int len_alloc; + int c; +} SEGM_LINE; +#define SEGM_LINE_ADD 128 + +typedef struct tagOneLinkedBond +{ + AT_NUMB neigh; /* canonical number of a neighbor */ + AT_NUMB prev; /* position of the previous neighbor in the list */ +} ONE_LINKED_BOND; + +typedef struct tagLinkedBonds +{ + ONE_LINKED_BOND *pBond; + int len; + int len_alloc; +}LINKED_BONDS; +#define LINKED_BOND_ADD 128 + +typedef enum tagModeProtonIsoExchgH +{ + MODE_PIXH_UNDEFINED, /* 0 */ + MODE_PIXH_ADD_TO_FIRST, /* 1 */ + MODE_PIXH_ADD_TO_EACH, /* 2 */ + MODE_PIXH_ADD_A_PIXH_COMPONENT, /* 3 */ + MODE_PIXH_KEEP_TOTALS /* 4 */ +} MODE_PIXH; + + +/* local prototypes */ +static int GetInChIFormulaNumH(INChI *pInChI, int *nNumH); +static int GetInChINumH(INChI *pInChI, int *nNumH); +static int GetInChIIsoH(INChI *pInChI, int nNumIsotopicH[NUM_H_ISOTOPES]); + +static int getInChIChar(INCHI_IOSTREAM *pInp); +static int AddInChIChar(INCHI_IOSTREAM *pInp, SEGM_LINE *Line, const char *pszToken); +static int AddLinkedBond(AT_NUMB at1, AT_NUMB at2, AT_NUMB num_at, LINKED_BONDS *pLB); +static int bInChIHasReconnectedMetal(INChI *pInChI); +static int SetProtonsAndXchgIsoH(int bInChI2Structure, + int bReqSplitOutputInChI, + int bReqProtonsForEachComponent, + int bReqNonTaut, int bReqStereo, + int num_components[INCHI_NUM], + MODE_PIXH nModeProtonIsoExchgH[INCHI_NUM], + InpInChI *OneInput); +#if ( FIX_DALKE_BUGS == 1 ) +static int SetHillFormFromInChI(InpInChI *OneInput); +#endif + +static int nGetInChISegment(INCHI_IOSTREAM *pInp, SEGM_LINE *Line, const char *pszToken); + +static int CopySegment(INChI *pInChITo, INChI *pInChIFrom, int StereoType, + int bIsotopicTo, int bIsotopicFrom); +static int nFillOutProtonMobileH(INChI *pInChI); +static int nProtonCopyIsotopicInfo(INChI *pInChI_to, INChI *pInChI_from); +static int CopyAtomNumbers(INChI *pInChI_To, int bIsoTo, INChI *pInChI_From, int bIsoFrom); + +static int ParseSegmentFormula( const char *str, int bMobileH, INChI *pInpInChI[], + int nNumComponents[], int *na_total ); +static int ParseSegmentConnections( const char *str, int bMobileH, INChI **pInpInChI, + int *pnNumComponents, int *pbAbc, int *nb_total ); +static int ParseSegmentMobileH( const char *str, int bMobileH, INChI *pInpInChI[], + int pnNumComponents[], int *pbAbc); +static int ParseSegmentCharge( const char *str, int bMobileH, INChI *pInpInChI[], + int nNumComponents[]); +static int ParseSegmentProtons( const char *str, int bMobileH, + REM_PROTONS nNumProtons[], int nNumComponents[]); +static int ParseSegmentPolymer( const char *str, int bMobileH, + REM_PROTONS nNumProtons[], int nNumComponents[], + int na_total, int nb_total, + int bInchi2Struct, + OrigAtDataPolymer **ppPolymer,OrigAtDataV3000 **ppV3000 ); +static int ParseSegmentSp2( const char *str, int bMobileH, INChI *pInpInChI[], + int nNumComponents[], int state, int *pbAbc); +static int ParseSegmentSp3( const char *str, int bMobileH, INChI *pInpInChI[], + int nNumComponents[], int state, int *pbAbc); +static int ParseSegmentSp3m( const char *str, int bMobileH, INChI *pInpInChI[], + int nNumComponents[], int state); +static int bIsSp3LayerNotEmpty( INChI *pInpInChI[], int bMobileH, + int bIso, int nNumComponents); +static int ParseSegmentSp3s( const char *str, int bMobileH, INChI *pInpInChI[], + int s[TAUT_NUM][2], int ppnNumComponents[], int state); +static int ParseSegmentIsoAtoms( const char *str, int bMobileH, INChI *pInpInChI[], + int nNumComponents[], int state, int *pbAbc); +static int ParseSegmentIsoExchgH( const char *str, int bMobileH, + REM_PROTONS nNumProtons[], + int nNumComponents[], int state, int *pbAbc); +static int ParseSegmentPerm( const char *str, int bMobileH, INChI *pInpInChI[], + int ppnNumComponents[], int state, int *pbAbc); +#if ( FIX_ISO_FIXEDH_BUG_READ == 1 ) +static int bIsoMayBeArranged( int bInchi2Struct, int iso_diff[NUM_H_ISOTOPES], + REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM], + INChI *pInpInChI[INCHI_NUM][TAUT_NUM], int nNumComponents[INCHI_NUM][TAUT_NUM], int iINChI); +#endif + +static int ReadInChILine( INCHI_IOSTREAM *pInp, + SEGM_LINE *pLine, + char **pStr, + int *pState, + INChI *pInpInChI[INCHI_NUM][TAUT_NUM], + int nNumComponents[INCHI_NUM][TAUT_NUM], + REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM], + int s[INCHI_NUM][TAUT_NUM][2], + int *input_is_stdinchi, + int *input_has_save_opt, + unsigned char *input_save_opt_bits, + int bInchi2Struct, + OrigAtDataPolymer **ppolymer, + OrigAtDataV3000 **pv3000); + +int InChILine2Data( INCHI_IOSTREAM *pInp, + SEGM_LINE *pLine, + char **pStr, + int *pState, + int *nErr, + INChI *pInpInChI[INCHI_NUM][TAUT_NUM], + int nNumComponents[INCHI_NUM][TAUT_NUM], + REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM], + int s[INCHI_NUM][TAUT_NUM][2], + int bReadCoord, + int bInchi2Struct, + INCHI_MODE nMode, + int *bStdFormat, + int *input_has_save_opt, + unsigned char *input_save_opt_bits, + OrigAtDataPolymer **ppolymer, + OrigAtDataV3000 **pv3000); + +static int ReadInChICoord( INCHI_IOSTREAM *pInp, + SEGM_LINE *pLine, + int *pState, + INChI *pInpInChI[INCHI_NUM][TAUT_NUM], + int nNumComponents[INCHI_NUM][TAUT_NUM]); + +static int OutputInChIAsRequested( struct tagCANON_GLOBALS *pCG, + INCHI_IOSTREAM *pOut, + INCHI_IOSTREAM *pLog, + ICHICONST INPUT_PARMS *ip_inp, + STRUCT_DATA *sd_inp, + InpInChI *OneInput, + int num_components[INCHI_NUM], + MODE_PIXH nModeProtonIsoExchgH[INCHI_NUM], + long num_inp, + unsigned char save_opt_bits); + +static int ParseAuxSegmentVersion( const char *str, + int bMobileH, + INChI *pInpInChI[], + int ppnNumComponents[], + int state); + +static int ParseAuxSegmentNumbers( const char *str, + int bMobileH, + INChI *pInpInChI[], + int ppnNumComponents[], + int state, + int *pbAbc); + +static int ParseAuxSegmentAtomEqu( const char *str, + int bMobileH, + INChI *pInpInChI[], + int ppnNumComponents[], + int state); + +static int ParseAuxSegmentGroupEqu( const char *str, + int bMobileH, + INChI *pInpInChI[], + int ppnNumComponents[], + int state); + +static int ParseAuxSegmentSp3Inv( const char *str, + int bMobileH, + INChI *pInpInChI[], + int ppnNumComponents[], + int state); + +static int ParseAuxSegmentSp3InvNumbers( const char *str, + int bMobileH, + INChI *pInpInChI[], + int ppnNumComponents[], + int state); + +static int ParseAuxSegmentReverseCRV( const char *str, + int bMobileH, + INChI *pInpInChI[], + int ppnNumComponents[], + int state); + +static int ParseAuxSegmentReverseAtoms( const char *str, + int bMobileH, + INChI *pInpInChI[], + int ppnNumComponents[], + int state); + +static int ParseAuxSegmentReverseBonds( const char *str, + int bMobileH, + INChI *pInpInChI[], + int ppnNumComponents[], + int state); + +static int ParseAuxSegmentReverseXYZ( const char *str, + int bMobileH, + XYZ_COORD **ppXYZ, + INChI *pInpInChI[], + int ppnNumComponents[], + int state); + +static int AddAuxSegmentCoord( int nRet, + XYZ_COORD *pXYZ, + int nLenXYZ, + INChI *pInpInChI[INCHI_NUM][TAUT_NUM], + int nNumComponents[INCHI_NUM][TAUT_NUM]); + + +static void getInchiStateReadErr(int stat, char *szMsg); + /* static const char *getInchiStateReadErr(int stat); */ +static const char *getInchiErrName(int nErr); + +char * ParseSegmentReadDelimitedNumbers( const char *str, const char *pEnd, INT_ARRAY *numlist, char c_delim, char c_stop, int *ret ); +#define SEG_END '/' +/* the following 2 definitions are used to allow tab-delimited InChI input - 2008-11-17 DT */ +#define INCHI_INP_EOL(X) ((X)=='\n' || (X)=='\r' || (X)=='\t') +/*#define INCHI_TOKEN "/\n\r\t"*/ +#define INCHI_TOKEN "/\n\r\t\\" + +typedef enum tagInChI_STATE +{ + /* M */ + IST_MOBILE_H_FORMULA, /* 0 */ + IST_MOBILE_H_CONNECTIONS, /* 1 */ + IST_MOBILE_H, /* 2 */ + IST_MOBILE_H_CHARGE, /* 3 */ + IST_MOBILE_H_PROTONS, /* 4 */ + IST_MOBILE_H_SP2, /* 5 */ + IST_MOBILE_H_SP3, /* 6 */ + IST_MOBILE_H_SP3_M, /* 7 */ + IST_MOBILE_H_SP3_S, /* 8 */ + + /* Fork */ + IST_MOBILE_H_ISO_LAYER_FORK, /* 9 */ + + /* MI */ + IST_MOBILE_H_ISO_ATOMS, /* 10 */ + IST_MOBILE_H_ISO_EXCH_H, /* 11 */ + IST_MOBILE_H_ISO_SP2, /* 12 */ + IST_MOBILE_H_ISO_SP3, /* 13 */ + IST_MOBILE_H_ISO_SP3_M, /* 14 */ + IST_MOBILE_H_ISO_SP3_S, /* 15 */ + + /* Fork */ + IST_FIXED_H_LAYER_FORK, /* 16 */ + + /* F */ + IST_FIXED_H_FORMULA, /* 17 */ + IST_FIXED_H, /* 18 */ + IST_FIXED_H_CHARGE, /* 19 */ + IST_FIXED_H_SP2, /* 20 */ + IST_FIXED_H_SP3, /* 21 */ + IST_FIXED_H_SP3_M, /* 22 */ + IST_FIXED_H_SP3_S, /* 23 */ + IST_FIXED_H_PERMUTATION, /* 24 */ + + /* Fork */ + IST_FIXED_H_ISO_LAYER_FORK, /* 25 */ + + /* FI */ + IST_FIXED_H_ISO_ATOMS, /* 26 */ + IST_FIXED_H_ISO_LAYER, /* 27 */ + IST_FIXED_H_ISO_SP2, /* 28 */ + IST_FIXED_H_ISO_SP3, /* 29 */ + IST_FIXED_H_ISO_SP3_M, /* 30 */ + IST_FIXED_H_ISO_SP3_S, /* 31 */ + IST_FIXED_H_ISO_PERMUTATION, /* 32 */ + + /* Reconnected */ + IST_RECONNECTED_LAYER_FORK, /* 33 */ + IST_RECONNECTED_FORMULA, /* 34 */ + + /* Other reading errors */ + IST_MATERIAL_BALANCE_ERROR, /* 35 */ + + /* */ + IST_MOBILE_H_POLYMER, /* 36 */ + + IST_END = -1 +}INCHI_STATE; + + +#define IST_HAPPENED_IN_RECMET 100 + + +typedef struct tagInchiReadErrMsg +{ + int stat; + const char *msg; +} INCHI_READ_ERR_MSG; + +ICHICONST INCHI_READ_ERR_MSG irErrMsg[] = +{ + /* M */ + {IST_MOBILE_H_FORMULA, "MOBILE_H_FORMULA" }, + {IST_MOBILE_H_CONNECTIONS, "MOBILE_H_CONNECTIONS" }, + {IST_MOBILE_H, "MOBILE_H" }, + {IST_MOBILE_H_CHARGE, "MOBILE_H_CHARGE" }, + {IST_MOBILE_H_PROTONS, "MOBILE_H_PROTONS" }, + {IST_MOBILE_H_SP2, "MOBILE_H_SP2" }, + {IST_MOBILE_H_SP3, "MOBILE_H_SP3" }, + {IST_MOBILE_H_SP3_M, "MOBILE_H_SP3_/m" }, + {IST_MOBILE_H_SP3_S, "MOBILE_H_SP3_/s" }, + + /* Fork */ + {IST_MOBILE_H_ISO_LAYER_FORK, "MOBILE_H_ISO_LAYER_FORK" }, + + /* MI */ + {IST_MOBILE_H_ISO_ATOMS, "MOBILE_H_ISO_ATOMS" }, + {IST_MOBILE_H_ISO_EXCH_H, "MOBILE_H_ISO_EXCH_H" }, + {IST_MOBILE_H_ISO_SP2, "MOBILE_H_ISO_SP2" }, + {IST_MOBILE_H_ISO_SP3, "MOBILE_H_ISO_SP3" }, + {IST_MOBILE_H_ISO_SP3_M, "MOBILE_H_ISO_SP3_/m" }, + {IST_MOBILE_H_ISO_SP3_S, "MOBILE_H_ISO_SP3_/s" }, + + /* Fork */ + {IST_FIXED_H_LAYER_FORK, "FIXED_H_LAYER_FORK" }, + + /* F */ + {IST_FIXED_H_FORMULA, "FIXED_H_FORMULA" }, + {IST_FIXED_H, "FIXED_H" }, + {IST_FIXED_H_CHARGE, "FIXED_H_CHARGE" }, + {IST_FIXED_H_SP2, "FIXED_H_SP2" }, + {IST_FIXED_H_SP3, "FIXED_H_SP3" }, + {IST_FIXED_H_SP3_M, "FIXED_H_SP3_/m" }, + {IST_FIXED_H_SP3_S, "FIXED_H_SP3_/s" }, + {IST_FIXED_H_PERMUTATION, "FIXED_H_PERMUTATION" }, + + /* Fork */ + {IST_FIXED_H_ISO_LAYER_FORK, "FIXED_H_ISO_LAYER_FORK" }, + + /* FI */ + {IST_FIXED_H_ISO_ATOMS, "FIXED_H_ISO_ATOMS" }, + {IST_FIXED_H_ISO_LAYER, "FIXED_H_ISO_LAYER" }, + {IST_FIXED_H_ISO_SP2, "FIXED_H_ISO_SP2" }, + {IST_FIXED_H_ISO_SP3, "FIXED_H_ISO_SP3" }, + {IST_FIXED_H_ISO_SP3_M, "FIXED_H_ISO_SP3_m" }, + {IST_FIXED_H_ISO_SP3_S, "FIXED_H_ISO_SP3_s" }, + {IST_FIXED_H_ISO_PERMUTATION, "FIXED_H_ISO_PERMUTATION" }, + + /* Reconnected */ + {IST_RECONNECTED_LAYER_FORK, "RECONNECTED_LAYER_FORK" }, + {IST_RECONNECTED_FORMULA, "RECONNECTED_FORMULA" }, + + {IST_MATERIAL_BALANCE_ERROR, "MATERIAL_BALANCE" }, + + {IST_MOBILE_H_POLYMER, "POLYMER_LAYER" }, + + {IST_END, "Unknown Error" } +}; + + + +typedef enum tagCopySegmentType +{ + CPY_SP2, + CPY_SP3, + CPY_SP3_M, + CPY_SP3_S, + CPY_ISO_AT +} COPY_SEG_TYPE; + +#define NSTRLEN 511999 +#define MAX_MSG_LEN 512 +#define MAX_MSG_BUF_LEN 128 + + +void PrepareSaveOptBits( INPUT_PARMS *ip, + INCHI_IOSTREAM *pLog, + const long num_inp, + const char *szCurHdr, + int input_has_save_opt, + unsigned char input_save_opt_bits, + unsigned char *save_opt_bits ); + +void TreatErrorsInReadInChIString( int nReadStatus, + int nErr, + int pState, + INPUT_PARMS *ip, + INCHI_IOSTREAM *pOut, + INCHI_IOSTREAM *pLog, + long *num_inp, + long *num_errors, + long *num_processed, + char **pstrHdr, + char **pszCurHdr, + InpInChI *pOneInput); + +int ConvertInChI2Struct( ICHICONST INPUT_PARMS *ip_inp, + INPUT_PARMS *ip, + InpInChI *pOneInput, + inp_ATOM **at, + int *num_at, + OrigAtDataPolymer **polymer, + OrigAtDataV3000 **v3000, + INCHI_IOSTREAM *pOut, + INCHI_IOSTREAM *pLog, + STRUCT_DATA *sd, + int num_components[INCHI_NUM], + MODE_PIXH nModeProtonIsoExchgH[INCHI_NUM], + char **pszCurHdr, + char *szMsg, + int nMsgLen, + char szMessage[MAX_MSG_LEN], + int nInitLenMessage, + int nMessageLen, + int input_is_stdinchi, + int bHasSomeReconnected, + int bHasSomeFixedH, + int bHasMetal, + int nModeFlagsStereo, + int bTautFlags, + int bReqNonTaut, + unsigned long WarningFlags[2][2], + long num_inp, + long *num_errors, + unsigned char save_opt_bits, + inchiTime *pulTStart, + long *ulProcessingTime, + struct tagINCHI_CLOCK *ic, + struct tagCANON_GLOBALS *pCG); + +int ConvertInChI2InChI( INPUT_PARMS *ip, + InpInChI *pOneInput, + INCHI_IOSTREAM *pOut, + INCHI_IOSTREAM *pLog, + STRUCT_DATA *sd, + int num_components[INCHI_NUM], + MODE_PIXH nModeProtonIsoExchgH[INCHI_NUM], + char **pszCurHdr, + long num_inp, + long *num_errors, + unsigned char save_opt_bits, + inchiTime *pulTStart, + long *ulProcessingTime, + struct tagINCHI_CLOCK *ic, + struct tagCANON_GLOBALS *pCG); + +int DetectAndExposePolymerInternals( INCHI_IOSTREAM *is ); + +int DetectHiddenPolymerStuff( char *tmpstr, int tmpstrlen, + int *ninsert, int *insert_pos, + int insert_lead_offset, int *nstars ); + + +/* + getInchiStateReadErr +*/ +void getInchiStateReadErr( int stat, char *szMsg ) + /*const char *getInchiStateReadErr(int stat) */ +{ + int i, bRecMet = 0; + + if ( stat >= IST_HAPPENED_IN_RECMET ) + { + bRecMet = 1; + stat -= IST_HAPPENED_IN_RECMET; + } + for ( i = 0; 0 <= irErrMsg[i].stat && stat != irErrMsg[i].stat; i ++ ) + ; + sprintf( szMsg, +#if ( FIX_DALKE_BUGS == 1 ) + "%s%.100s", +#else + "%s%s", +#endif + irErrMsg[i].msg, bRecMet? ", Reconnected layer" : ""); +} + + +/* + getInchiErrName +*/ +const char *getInchiErrName(int nErr) +{ + switch ( nErr ) + { + case RI_ERR_ALLOC: + return "Allocation failed"; + case RI_ERR_PROGR: + return "Program error"; + case RI_ERR_SYNTAX: + return "Syntax error"; + case RI_ERR_EOL: + return "End of line"; + } + return "Unknown error"; +} + + + +#if ( FIX_DALKE_BUGS == 1 ) +/* + SetHillFormFromInChI +*/ +int SetHillFormFromInChI(InpInChI *OneInput) +{ + int iINChI, iTaut, iComp, num_diff; + INChI *pINChI; + char *szHillFormulaOld; + for ( iINChI = 0, num_diff = 0; iINChI < INCHI_NUM; iINChI ++ ) + { + for ( iTaut = TAUT_NON; iTaut < TAUT_NUM; iTaut ++ ) + { + for ( iComp = 0; iComp < OneInput->nNumComponents[iINChI][iTaut]; iComp ++ ) + { + pINChI = &OneInput->pInpInChI[iINChI][iTaut][iComp]; + if ( !pINChI->nNumberOfAtoms || pINChI->bDeleted || !pINChI->szHillFormula || !pINChI->szHillFormula[0] ) + { + continue; + } + szHillFormulaOld = pINChI->szHillFormula; + pINChI->szHillFormula = AllocateAndFillHillFormula(pINChI); + num_diff += !pINChI->szHillFormula || !pINChI->szHillFormula[0] || strcmp(pINChI->szHillFormula, szHillFormulaOld); + inchi_free(szHillFormulaOld); + } + } + } + return num_diff; +} +#endif + + +/* + Main entry point +*/ +int ReadWriteInChI( INCHI_IOSTREAM *pInp, + INCHI_IOSTREAM *pOut, + INCHI_IOSTREAM *pLog, + INPUT_PARMS *ip_inp, + STRUCT_DATA *sd_inp, + /* the following are InChI library-specific parameters */ + inp_ATOM **at, + int *num_at, + int *num_bonds, + OrigAtDataPolymer **polymer, + OrigAtDataV3000 **v3000, + /* end of InChI library-specific parameters */ + char *szMsg, + int nMsgLen, + unsigned long WarningFlags[2][2], + INCHI_CLOCK *ic, + struct tagCANON_GLOBALS *pCG) +{ + InpInChI OneInput; + char *strHdr=NULL; + char *szCurHdr = NULL; + int num_components[INCHI_NUM]; + int bReqNonTaut = (0 != ((ip_inp->nMode & REQ_MODE_BASIC) && + (ip_inp->nMode & REQ_MODE_TAUT))); + /* + int bReqRecmet = (0 != ((ip->bTautFlags & TG_FLAG_RECONNECT_COORD) && + (ip->bTautFlags & TG_FLAG_DISCONNECT_COORD))); + */ + int bReqStereo = (0 != (ip_inp->nMode & REQ_MODE_STEREO)); + int bHasSomeReconnected = 0, bHasSomeFixedH = 0, bHasMetal = 0; + int nModeFlagsStereo = 0, bTautFlags = 0; /* InChI creation flags modifications derived from current InChI */ + MODE_PIXH nModeProtonIsoExchgH[INCHI_NUM]; + + NORM_CANON_FLAGS ncFlags; + NORM_CANON_FLAGS *pncFlags = &ncFlags; + INPUT_PARMS ip_cur, *ip; + STRUCT_DATA sd_cur, *sd; + + int pState, bStereoType; + int bReqProtonsForEachComponent = 0; + int bReqSplitOutputInChI = 0; + SEGM_LINE Line; + SEGM_LINE *pLine = &Line; + long ulProcessingTime = 0; + inchiTime ulTStart; + long num_processed = 0, num_errors = 0; + int bPlainTabbedOutput; + const char *pTAB; + + long num_inp=0; + + int read_inchi_ok=0; + int end_of_data_reached=0; + int treat_save_opt=0; + int input_is_stdinchi=0; + int input_has_save_opt=0; + unsigned char input_save_opt_bits=0; + unsigned char save_opt_bits=0; + + const int bInChI2Structure = 0 != (ip_inp->bReadInChIOptions & READ_INCHI_TO_STRUCTURE); + const int bInChI2InChI = 0 != (ip_inp->bReadInChIOptions & READ_INCHI_OUTPUT_INCHI); + const int bReadCoord = bInChI2Structure; + + int nMessageLen = MAX_MSG_LEN; + char szMessage[MAX_MSG_LEN]; + int nInitLenMessage; + int invalid_opt = 0; + int j, nErr, iINChI; + + int treat_mismatch_as_error = ip_inp->bINChIOutputOptions2 & INCHI_OUT_MISMATCH_AS_ERROR; + +#ifdef ERR_INCHI_STRING_ALLOWED + int output_error_inchi = ip_inp->bINChIOutputOptions2 & INCHI_OUT_INCHI_GEN_ERROR; +#else + int output_error_inchi = 0 +#endif + + + int ret = 0; + int nReadStatus = RI_ERR_EOL; + + INCHI_IOSTREAM *pRealOut = pOut; + /* temporary output buffer pTmpOut may be used */ + /* locally instead of legal pOut to capture */ + /* InChI string which may then be recognised */ + /* as erratic (whence should not be finally */ + /* printed) */ + INCHI_IOSTREAM tmpoutputstr; + INCHI_IOSTREAM *pTmpOut = &tmpoutputstr; + if ( bInChI2Structure ) + { + inchi_ios_init( pTmpOut, INCHI_IOSTREAM_TYPE_STRING, NULL ); + if ( pTmpOut->s.pStr ) + pRealOut = pTmpOut; + } + + memset(szMessage, 0, sizeof(szMessage)); + memset(&OneInput, 0, sizeof(OneInput)); + memset(pLine, 0, sizeof(pLine[0])); + if ( szMsg ) + szMsg[0] = '\0'; + + OneInput.polymer = NULL; /* v. 1.05 added */ + OneInput.v3000 = NULL; + + + /* Read input, InChI by InChI*/ + while( nReadStatus != RI_ERR_EOF ) + { + + for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) + { + for ( j = 0; j < TAUT_NUM; j ++ ) + { + if ( OneInput.nNumProtons[iINChI][j].pNumProtons ) + { + inchi_free(OneInput.nNumProtons[iINChI][j].pNumProtons); + OneInput.nNumProtons[iINChI][j].pNumProtons = NULL; + } + } + } + + memset(&OneInput, 0, sizeof(OneInput)); + memset(pncFlags, 0, sizeof(*pncFlags)); + bStereoType = 0; + ip_cur = *ip_inp; + ip = &ip_cur; + sd_cur = *sd_inp; + sd = &sd_cur; + + bReqSplitOutputInChI = + 0 != (ip->bReadInChIOptions & READ_INCHI_SPLIT_OUTPUT); + bReqProtonsForEachComponent = + bReqSplitOutputInChI && + 0 != (READ_INCHI_KEEP_BALANCE_P & ip->bReadInChIOptions); + bPlainTabbedOutput = + 0 != (ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT); + + pTAB = +#if ( defined(TARGET_API_LIB) || defined(TARGET_LIB_FOR_WINCHI) ) + "\n"; +#else + bPlainTabbedOutput? "\t" : "\n"; +#endif + + + if ( bInChI2Structure ) + { + bReqStereo = 1; + bReqSplitOutputInChI = 1; + bReqProtonsForEachComponent = bReqNonTaut; + ip->bTautFlags |= (TG_FLAG_DISCONNECT_COORD | TG_FLAG_RECONNECT_COORD); + ip->nMode |= (REQ_MODE_BASIC | REQ_MODE_TAUT | REQ_MODE_STEREO | REQ_MODE_ISO_STEREO | REQ_MODE_ISO); + /* bReqRecmet = 1; */ +#if ( bRELEASE_VERSION == 1 ) + bReqNonTaut = 1; /* bReqNonTaut=0 ignores Fixed-H layer in input InChI, for testing only */ +#endif + /* polymer stuff added */ + if ( pInp->type == INCHI_IOSTREAM_TYPE_STRING ) + { + int res; + + if ( ip_inp->lMolfileNumber ) + /* get here from inchi-1 main emulation mode */ + num_inp = ip_inp->lMolfileNumber - 1; + + res = DetectAndExposePolymerInternals( pInp ); + /* proceed silently for now, errs mist be uncovered further on conversion */ + } + } + + + InchiTimeGet(&ulTStart); + + + /* Read InChI string */ + nReadStatus = InChILine2Data( pInp, pLine, &strHdr, &pState, &nErr, + OneInput.pInpInChI, OneInput.nNumComponents, + OneInput.nNumProtons, OneInput.s, + bReadCoord, bInChI2Structure, ip_inp->nMode, + &input_is_stdinchi, &input_has_save_opt, + &input_save_opt_bits, + &OneInput.polymer, &OneInput.v3000); + + + ulProcessingTime += InchiTimeElapsed( ic,&ulTStart); + +#if 0 + if ( !bInChI2Structure && OneInput.polymer ) + { + /* inchi2inchi for polymers: for now skip */ + pState==IST_MOBILE_H_POLYMER; + nReadStatus = -2; + } +#endif + + + + end_of_data_reached = nReadStatus==RI_ERR_EOL || nReadStatus==RI_ERR_EOF; + + if ( !bInChI2Structure ) + { + /* i2i, disable polymer related */ + if ( OneInput.polymer ) + { + pState = IST_MOBILE_H_POLYMER; + nErr = RI_ERR_PROGR; + } + } + + read_inchi_ok = end_of_data_reached && + !nErr && + OneInput.nNumComponents[INCHI_BAS][TAUT_YES] + + OneInput.nNumComponents[INCHI_BAS][TAUT_NON]; + + +#ifdef TARGET_EXE_STANDALONE + /* inchi-1: we currently disable conversion of polymeric InChI (inchi2struct) + to anything but SDF (would it be necessary in the future?) */ + if ( OneInput.polymer && + bInChI2Structure && + !(ip_inp->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY) ) + { + read_inchi_ok = 0; + nErr = RI_ERR_PROGR; + } +#endif + + + + if ( !read_inchi_ok ) + { + TreatErrorsInReadInChIString( nReadStatus, nErr, pState, ip_inp, pRealOut, pLog, + &num_inp, &num_errors, &num_processed, + &strHdr, &szCurHdr, &OneInput); + } + else + { + /* InChI has been successfully read */ + ret = 0; + num_inp++; + ip->lMolfileNumber = num_inp; + bHasSomeReconnected = 0; + bHasSomeFixedH = 0; + + if ( pRealOut == pTmpOut ) + { + inchi_ios_reset( pRealOut ); + /*inchi_ios_close( pRealOut ); + inchi_ios_init( pRealOut, INCHI_IOSTREAM_TYPE_STRING, NULL );*/ + } + /* inchi_ios_print( pRealOut, ""); */ + + /* Does not allow conversion non-standard->standard */ + /* (force target to be non-standard also) */ + if ( ip_inp->bINChIOutputOptions & INCHI_OUT_STDINCHI ) + { + if ( !input_is_stdinchi ) + { + /* Input InChI is a non-standard one */ + ip->bINChIOutputOptions &= ~INCHI_OUT_STDINCHI; + if ( szCurHdr && szCurHdr[0] ) + inchi_ios_eprint( pLog, "Warning: forced conversion to non-standard InChI for non-std input, %s\n", szCurHdr ); + else + inchi_ios_eprint( pLog, "Warning: forced conversion to non-standard InChI for non-std input, Structure %ld\n", num_inp ); + } + } + + + treat_save_opt = ip->bINChIOutputOptions & INCHI_OUT_SAVEOPT; + + if ( treat_save_opt ) + PrepareSaveOptBits( ip, pLog, num_inp, szCurHdr, + input_has_save_opt, + input_save_opt_bits, + &save_opt_bits ); + +#ifndef TARGET_API_LIB + /* + inchi_ios_eprint(stderr, "%ld: %s\r", num_inp, strHdr? strHdr : ""); + inchi_ios_eprint(pLog, "%ld: %s\n", num_inp, strHdr? strHdr : ""); + */ + + if ( !ip->bNoStructLabels && + !( bInChI2Structure && (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY) ) + ) + { + /* Added 2nd item: Do not output this extra line into the output SDfile. 2008-11-17 DCh */ + if ( strHdr && strstr(strHdr, "Structure:")) + { + inchi_ios_print( pRealOut, "%s%s", strHdr, pTAB ); /* output header */ +#if ( FIX_DALKE_BUGS == 1 ) +#else + sprintf(szMessage, "%s (%ld)", strHdr? strHdr : "", num_inp); +#endif + } + else + { + if ( bInChI2Structure ) inchi_ios_print( pRealOut, "Structure: %ld. (%s)%s", + num_inp, strHdr? strHdr : "No struct name", + pTAB); /* output header */ +#if ( FIX_DALKE_BUGS == 1 ) +#else + sprintf(szMessage, "Structure: %ld. (%s)%s", num_inp, strHdr? strHdr : "No struct name", pTAB); +#endif + } + if ( strHdr && strHdr[0] ) + { + strncpy(ip->szSdfDataHeader, strHdr, sizeof(ip->szSdfDataHeader)); + ip->szSdfDataHeader[sizeof(ip->szSdfDataHeader)-1] = '\0'; + ip->pSdfLabel = NULL; + ip->pSdfValue = ip->szSdfDataHeader; + } + else + { + ip->pSdfValue = NULL; + ip->szSdfDataHeader[0] = '\0'; + } + } + +#if ( FIX_DALKE_BUGS == 1 ) + sprintf(szMessage, "%ld: %.400s", num_inp, strHdr? strHdr : ""); +#else + sprintf(szMessage, "%ld: %s", num_inp, strHdr? strHdr : ""); +#endif +#endif + + nInitLenMessage = (int) strlen(szMessage); + if ( strHdr ) + { + szCurHdr = strHdr; + strHdr = NULL; + } + if ( szCurHdr && ip && ip->first_struct_number > 0 ) + { + /* Check whether the structure should be skipped */ + static const char szStruct[] = "Structure:"; + char *pStrNum = strstr(szCurHdr, szStruct); + long cur_struct_number; + if ( pStrNum ) + { + pStrNum += sizeof(szStruct)-1; /* -1 takes care of the string terminal zero */ + cur_struct_number = inchi_strtol(pStrNum, NULL, 10); + if ( cur_struct_number ) + { + OneInput.num_inp = cur_struct_number; + } + /* Process request to bypass first several InChIs */ + if ( cur_struct_number > 0 && cur_struct_number < ip->first_struct_number ) + { + +#if ( !defined(TARGET_API_LIB) && !defined(TARGET_EXE_STANDALONE) ) + inchi_fprintf(stderr, "Skipping %s\r", szMessage); +#endif + FreeInpInChI( &OneInput ); + if ( szCurHdr ) + { + inchi_free(szCurHdr); + szCurHdr = NULL; + } + INCHI_HEAPCHK + continue; + } + } + } + + num_processed ++; + + /* In case of splitting InChI into separate components */ + /* decide whether to keep /p in each component or */ + /* output /p and /i/h as a separate component */ + /* Note: if InChI is not to be splitted DO NOT create */ + /* a separate component for /p, /i/h: it would be a bug*/ + + InchiTimeGet(&ulTStart); + + INCHI_HEAPCHK + + ret = SetProtonsAndXchgIsoH( bInChI2Structure, bReqSplitOutputInChI, + bReqProtonsForEachComponent, bReqNonTaut, bReqStereo, + num_components, nModeProtonIsoExchgH, &OneInput); + INCHI_HEAPCHK + + if ( ret < 0 ) + { + num_errors ++; + goto exit_error; + } + + sd->num_components[INCHI_BAS] = num_components[INCHI_BAS]; + sd->num_components[INCHI_REC] = num_components[INCHI_REC]; + + /* Do we have reconnected InChI ? */ + if ( (OneInput.nNumComponents[INCHI_REC][TAUT_YES] || + OneInput.nNumComponents[INCHI_REC][TAUT_NON]) && + (ip->bTautFlags & TG_FLAG_RECONNECT_COORD) && + (ip->bTautFlags & TG_FLAG_DISCONNECT_COORD) + ) + { + /* needed for InChI string output to include reconnected InChI */ + sd->bTautFlagsDone[0] |= TG_FLAG_DISCONNECT_COORD_DONE; + bHasSomeReconnected = 1; + } + + /* Do we have fixed H InChI ? */ + if ( bReqNonTaut && + /*OneInput.nNumComponents[bHasSomeReconnected?INCHI_REC:INCHI_BAS][TAUT_NON]*/ + (OneInput.nNumComponents[INCHI_REC][TAUT_NON] || + OneInput.nNumComponents[INCHI_BAS][TAUT_NON]) ) + { + bHasSomeFixedH = 1; + } + + ulProcessingTime += InchiTimeElapsed( ic,&ulTStart); + + invalid_opt = ( !bInChI2Structure && !bInChI2InChI ) || + ( bInChI2Structure && bInChI2InChI ) ; + + if ( invalid_opt ) + { + inchi_ios_eprint( pLog, "\nWrong command line options: expected Inch2Struct or Inchi2Inchi\n", num_inp ); + break; + } + + + + /* InChI --> Structure */ + + + + else if ( bInChI2Structure ) + { + char *result_string; + + if ( OneInput.polymer && OneInput.polymer->n ) + OneInput.polymer->valid = 1; + + ret = ConvertInChI2Struct( ip_inp, ip, &OneInput, + at, num_at, + polymer, v3000, + pRealOut, pLog, + sd, num_components, nModeProtonIsoExchgH, + &szCurHdr, szMsg, nMsgLen, szMessage, + nInitLenMessage, nMessageLen, + input_is_stdinchi, bHasSomeReconnected, + bHasSomeFixedH, bHasMetal, + nModeFlagsStereo, bTautFlags, bReqNonTaut, + WarningFlags, num_inp, &num_errors, + save_opt_bits, &ulTStart, &ulProcessingTime, + ic, pCG ); + + result_string = pRealOut->s.pStr; + if ( ret == 0 ) /* no problems */ + { + ; + } + else if ( ret > 0 ) + { + /* error */ + if ( output_error_inchi ) + /* emit error string */ + inchi_ios_eprint( pRealOut, "InChI Creation Error!\n"); + } + else if ( ret < 0 ) + { + /* error or a mismatch possibly treated as error */ + if ( ret!=RI_ERR_MISMATCH ) + { + goto exit_error; + } + else + { + if ( !treat_mismatch_as_error ) + { + /* ignore mismatch, print InChI as usual */ + if ( result_string ) + inchi_ios_eprint( pRealOut, "%-s\n", result_string ); + } + else + { + /* concider mismatch an error */ + if ( !output_error_inchi ) + { + ; /* print nothing for now*/ + } + else + { + /* emit err string instead of InChI */ + if ( result_string ) + { + /* try to preserve header if any */ + char *pi= strstr( result_string, "InChI=" ); + if ( pi ) + { + int np = pi - result_string; + if ( np ) + result_string[np-1] = '\0'; + inchi_ios_eprint( pRealOut, "%-s\n", result_string ); + } + } + inchi_ios_eprint( pRealOut, "InChICreationError!\n"); /* emit err string */ + } + } + FreeInpInChI(&OneInput); + if ( strHdr ) + { + inchi_free( strHdr ); + strHdr = NULL; + } + if ( szCurHdr ) + { + inchi_free(szCurHdr); + szCurHdr = NULL; + } + INCHI_HEAPCHK + continue; + } + } + } /* ( bInChI2Structure ) */ + + + + + /* InChI --> InChI */ + + + else if ( bInChI2InChI ) + { + + ret = ConvertInChI2InChI( ip, &OneInput, pRealOut, pLog, sd, + num_components, nModeProtonIsoExchgH, + &szCurHdr, num_inp, &num_errors, + save_opt_bits, &ulTStart, + &ulProcessingTime, ic, pCG); + } + + + + if ( nReadStatus == RI_ERR_EOF ) + break; + } /* InChI has been successfully read */ + + + + +#ifdef TARGET_EXE_STANDALONE +#ifndef TARGET_API_LIB + inchi_ios_flush(pRealOut); + inchi_ios_flush2(pLog, stderr); +#endif +#endif +#ifdef TARGET_API_LIB + break; /* exit after the 1st structure */ +#endif + } /* while */ + + + +exit_error: + FreeInpInChI( &OneInput ); + if ( strHdr ) + { + inchi_free( strHdr ); + strHdr = NULL; + } + if ( pLine->str ) + { + inchi_free( pLine->str ); + } + if ( szCurHdr ) + { + inchi_free( szCurHdr ); + szCurHdr = NULL; + } + + INCHI_HEAPCHK + + if ( sd_inp ) + { + sd_inp->ulStructTime = ulProcessingTime; + sd_inp->fPtrStart = num_processed; + sd_inp->fPtrEnd = num_errors; + } + + if ( pRealOut == pTmpOut ) + inchi_ios_close( pTmpOut ); + + return ret; +} + + +/* + OutputInChIAsRequested +*/ +int OutputInChIAsRequested( struct tagCANON_GLOBALS *pCG, + INCHI_IOSTREAM *pOut, + INCHI_IOSTREAM *pLog, + ICHICONST INPUT_PARMS *ip_inp, + STRUCT_DATA *sd_inp, + InpInChI *OneInput, + int num_components[INCHI_NUM], + MODE_PIXH nModeProtonIsoExchgH[INCHI_NUM], + long num_inp, + unsigned char save_opt_bits) +{ + int j, k, k1, k2, ret2=0, iINChI, iINChI1, iINChI2; + PINChI2 *pINChI[INCHI_NUM]; + PINChI_Aux2 *pINChI_Aux[INCHI_NUM]; + int bReqNonTaut; + int bHasSomeReconnected; + + INPUT_PARMS ip_local; + STRUCT_DATA sd_local; + INPUT_PARMS *ip = &ip_local; + STRUCT_DATA *sd = &sd_local; + NORM_CANON_FLAGS ncFlags; + NORM_CANON_FLAGS *pncFlags = &ncFlags; + int nRet1, bSortPrintINChIFlags; + int bReqSplitOutputInChI; + int nNumOutputComponents; + + INCHI_IOSTREAM_STRING temp_string_container; + INCHI_IOSTREAM_STRING *strbuf = &temp_string_container; + memset( strbuf, 0, sizeof( *strbuf ) ); + + if ( 0>=inchi_strbuf_init( strbuf, INCHI_STRBUF_INITIAL_SIZE, INCHI_STRBUF_SIZE_INCREMENT ) ) + { + ret2 = RI_ERR_ALLOC; + goto exit_error; + } + + nRet1 = 0; + k1 = k2 = 0; + memset( pncFlags, 0, sizeof(*pncFlags) ); + memset( pINChI, 0, sizeof(pINChI) ); + memset( pINChI_Aux, 0, sizeof(pINChI_Aux) ); + + *ip = *ip_inp; + *sd = *sd_inp; + bHasSomeReconnected = 0; + bSortPrintINChIFlags = 0; + nNumOutputComponents = 0; + bReqNonTaut = (0 != (ip->nMode & REQ_MODE_BASIC)); + bReqSplitOutputInChI = (0 != (ip->bReadInChIOptions & READ_INCHI_SPLIT_OUTPUT)); + + INCHI_HEAPCHK + + if ( num_components[INCHI_BAS] ) + { + MYREALLOC2(PINChI2, PINChI_Aux2, pINChI[INCHI_BAS], pINChI_Aux[INCHI_BAS], num_components[INCHI_BAS], num_components[INCHI_BAS], k1); + } + if ( num_components[INCHI_REC] ) + { + MYREALLOC2(PINChI2, PINChI_Aux2, pINChI[INCHI_REC], pINChI_Aux[INCHI_REC], num_components[INCHI_REC], num_components[INCHI_REC], k2); + } + + + INCHI_HEAPCHK + + if ( k1 || k2 /*|| !pStr*/ ) + { + ret2 = RI_ERR_ALLOC; + goto exit_error; + } + + if ( num_components[INCHI_REC] && + (ip->bTautFlags & TG_FLAG_RECONNECT_COORD) && + (ip->bTautFlags & TG_FLAG_DISCONNECT_COORD) ) + { + sd->bTautFlagsDone[0] |= TG_FLAG_DISCONNECT_COORD_DONE; + bHasSomeReconnected = 1; + } + + + for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) + { + for ( j = 0; j < TAUT_NUM; j ++ ) + { + if ( bReqNonTaut || j != TAUT_NON && OneInput->pInpInChI[iINChI][j] ) + { + for ( k = 0; k < num_components[iINChI]; k ++ ) + { + /* allocate InChI & AuxInfo */ + if ( !(pINChI[iINChI][k][j] = (INChI *) inchi_calloc(1, sizeof(INChI)) ) ) + { + ret2 = RI_ERR_ALLOC; + goto exit_error; + } + if ( !(pINChI_Aux[iINChI][k][j] = (INChI_Aux *) inchi_calloc(1, sizeof(INChI_Aux)) ) ) + { + ret2 = RI_ERR_ALLOC; + goto exit_error; + } + /* copy InChI & AuxInfo */ + if ( k < OneInput->nNumComponents[iINChI][j] ) + { + + /* copy InChI */ + *pINChI[iINChI][k][j] = OneInput->pInpInChI[iINChI][j][k]; + memset(&OneInput->pInpInChI[iINChI][j][k], 0, sizeof(OneInput->pInpInChI[iINChI][j][k])); + INCHI_HEAPCHK + /* take care of protons in AuxInfo */ + + if ( nModeProtonIsoExchgH[iINChI] == MODE_PIXH_ADD_TO_EACH && j == TAUT_YES ) + { + pINChI_Aux[iINChI][k][j]->nNumRemovedProtons = + OneInput->nNumProtons[iINChI][j].pNumProtons[k].nNumRemovedProtons; + for ( k1 = 0; k1 < NUM_H_ISOTOPES; k1 ++ ) + { + pINChI_Aux[iINChI][k][j]->nNumRemovedIsotopicH[k1] = + OneInput->nNumProtons[iINChI][j].pNumProtons[k].nNumRemovedIsotopicH[k1]; + } + INCHI_HEAPCHK + } + else if ( !k && nModeProtonIsoExchgH[iINChI] == MODE_PIXH_ADD_TO_FIRST || + k+1 == OneInput->nNumComponents[iINChI][j] && + nModeProtonIsoExchgH[iINChI] == MODE_PIXH_ADD_A_PIXH_COMPONENT ) + { + /* add protons and exchangeable isotopic H to the first component's AuxInfo */ + pINChI_Aux[iINChI][k][j]->nNumRemovedProtons = OneInput->nNumProtons[iINChI][j].nNumRemovedProtons; + for ( k1 = 0; k1 < NUM_H_ISOTOPES; k1 ++ ) + { + pINChI_Aux[iINChI][k][j]->nNumRemovedIsotopicH[k1] = + OneInput->nNumProtons[iINChI][j].nNumRemovedIsotopicH[k1]; + } + INCHI_HEAPCHK + } + else + { + pINChI_Aux[iINChI][k][j]->bDeleted = pINChI[iINChI][k][j]->bDeleted; + } + + if ( j == TAUT_YES && pINChI[iINChI][k][j] && pINChI[iINChI][k][j]->nNumberOfAtoms && + !pINChI[iINChI][k][j]->nNum_H_fixed ) + { + /* serializer crashes if it is not allocated */ + pINChI[iINChI][k][j]->nNum_H_fixed = (S_CHAR *)inchi_calloc(pINChI[iINChI][k][j]->nNumberOfAtoms+1, sizeof(pINChI[0][0][0]->nNum_H_fixed[0]) ); + } + + if ( j == TAUT_YES && k < OneInput->nNumComponents[iINChI][TAUT_NON] && + pINChI[iINChI][k][j] && pINChI[iINChI][k][j]->nNumberOfAtoms && + pINChI[iINChI][k][TAUT_NON] && pINChI[iINChI][k][TAUT_NON]->nNumberOfAtoms && + !CompareReversedINChI( pINChI[iINChI][k][j], pINChI[iINChI][k][TAUT_NON], NULL, NULL ) ) { + pINChI[iINChI][k][TAUT_NON]->nNumberOfAtoms = 0; /* eliminate non-taut equal to taut */ + } + } + else + { + /* extra component, usually it is a Mobile H component */ + /* corresponding to a free proton component in Fixed H */ + pINChI[iINChI][k][j]->bDeleted = 1; + pINChI_Aux[iINChI][k][j]->bDeleted = 1; + } + } /* k */ + } /* if ( bReqNonTaut || j != TAUT_NON && OneInput->pInpInChI[iINChI][j] ) */ + + if ( OneInput->pInpInChI[iINChI][j] ) + { + INCHI_HEAPCHK + inchi_free(OneInput->pInpInChI[iINChI][j]); + OneInput->pInpInChI[iINChI][j] = NULL; + } + } /* j */ + } /* iINChI */ + + if ( bReqSplitOutputInChI ) + { + if ( bHasSomeReconnected ) + { + iINChI1 = INCHI_REC; /* only reconnected */ + iINChI2 = INCHI_NUM; + sd->num_components[INCHI_BAS] = sd->num_components[INCHI_REC]; + } + else + { + iINChI1 = 0; /* only disconnected */ + iINChI2 = iINChI1+1; + } + sd->num_components[INCHI_REC] = 0; /* treat reconnected as connected */ + nNumOutputComponents = sd->num_components[INCHI_BAS]; + } + else + { + iINChI1 = 0; + iINChI2 = INCHI_NUM; + nNumOutputComponents = 1; + } + + + for ( k1 = 0, k2 = (bReqSplitOutputInChI? k1+1 : nNumOutputComponents); k1 < k2 && k1 < nNumOutputComponents; k1=k2, k2 ++ ) + { + + if ( bReqSplitOutputInChI ) + { + sd->num_components[INCHI_BAS] = 1; + sd->num_components[INCHI_REC] = 0; + /* additional data */ + sd->num_non_taut[INCHI_BAS] = + sd->num_taut[INCHI_BAS] = + sd->num_non_taut[INCHI_REC] = + sd->num_taut[INCHI_REC] = 0; + iINChI = iINChI1; + for ( j = 0; j < TAUT_NUM && sd->num_components[iINChI]; j ++ ) + { + for ( k = k1; k < k2; k ++ ) + { + /* find where the current processed structure is located */ + int cur_is_in_non_taut = (pINChI[iINChI][k][TAUT_NON] && pINChI[iINChI][k][TAUT_NON]->nNumberOfAtoms>0); + int cur_is_in_taut = (pINChI[iINChI][k][TAUT_YES] && pINChI[iINChI][k][TAUT_YES]->nNumberOfAtoms>0); + int cur_is_non_taut = cur_is_in_non_taut && 0 == pINChI[iINChI][k][TAUT_NON]->lenTautomer || + cur_is_in_taut && 0 == pINChI[iINChI][k][TAUT_YES]->lenTautomer; + int cur_is_taut = cur_is_in_taut && 0 < pINChI[iINChI][k][TAUT_YES]->lenTautomer; + if ( cur_is_non_taut + cur_is_taut ) + { + /* count tautomeric and non-tautomeric components of the structures */ + /* + int j1 = cur_is_in_non_taut? TAUT_NON:TAUT_YES; + int j2 = cur_is_in_taut? TAUT_YES:TAUT_NON; + */ + sd->num_non_taut[INCHI_BAS] += cur_is_non_taut; + sd->num_taut[INCHI_BAS] += cur_is_taut; + } + } + } + INCHI_HEAPCHK + } + else + { + sd->num_components[INCHI_BAS] = inchi_max( OneInput->nNumComponents[INCHI_BAS][TAUT_YES], + OneInput->nNumComponents[INCHI_BAS][TAUT_NON] ); + sd->num_components[INCHI_REC] = inchi_max( OneInput->nNumComponents[INCHI_REC][TAUT_YES], + OneInput->nNumComponents[INCHI_REC][TAUT_NON] ); + + /* additional data needed for SortAndPrintINChI() */ + for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) + { + sd->num_non_taut[iINChI] = + sd->num_taut[iINChI] = 0; + for ( j = 0; j < TAUT_NUM && sd->num_components[iINChI]; j ++ ) + { + for ( k = k1; k < k2; k ++ ) + { + /* find where the current processed structure is located */ + int cur_is_in_non_taut = (pINChI[iINChI][k][TAUT_NON] && pINChI[iINChI][k][TAUT_NON]->nNumberOfAtoms>0); + int cur_is_in_taut = (pINChI[iINChI][k][TAUT_YES] && pINChI[iINChI][k][TAUT_YES]->nNumberOfAtoms>0); + int cur_is_non_taut = cur_is_in_non_taut && 0 == pINChI[iINChI][k][TAUT_NON]->lenTautomer || + cur_is_in_taut && 0 == pINChI[iINChI][k][TAUT_YES]->lenTautomer; + int cur_is_taut = cur_is_in_taut && 0 < pINChI[iINChI][k][TAUT_YES]->lenTautomer; + if ( cur_is_non_taut + cur_is_taut ) { + /* count tautomeric and non-tautomeric components of the structures */ + /* + int j1 = cur_is_in_non_taut? TAUT_NON:TAUT_YES; + int j2 = cur_is_in_taut? TAUT_YES:TAUT_NON; + */ + sd->num_non_taut[iINChI] += cur_is_non_taut; + sd->num_taut[iINChI] += cur_is_taut; + } + } + } + } + INCHI_HEAPCHK + } + if ( bReqSplitOutputInChI ) + { + /* output components one by one (for splitting input InChI into components) */ + PINChI2 *pInChI_2[INCHI_NUM]; + PINChI_Aux2 *pInChI_Aux_2[INCHI_NUM]; + INChI *pInChI_1[1][2]; + INChI_Aux *pInChI_Aux_1[1][2]; + memset( pInChI_2, 0, sizeof(pInChI_2) ); + memset( pInChI_Aux_2, 0, sizeof(pInChI_Aux_2) ); + + for ( j = 0; j < TAUT_NUM; j ++ ) + { + pInChI_1[0][j] = pINChI[iINChI1][k1][j]; + pInChI_Aux_1[0][j] = pINChI_Aux[iINChI1][k1][j]; + } + pInChI_2[INCHI_BAS] = pInChI_1; + pInChI_Aux_2[INCHI_BAS] = pInChI_Aux_1; + /* make sure purely reconnected InChI is marked as ReChI, not InChI */ + if ( bHasSomeReconnected && + (bInChIHasReconnectedMetal( pInChI_1[0][TAUT_YES] ) || + bInChIHasReconnectedMetal( pInChI_1[0][TAUT_NON] ) ) ) + { + bSortPrintINChIFlags = FLAG_SORT_PRINT_ReChI_PREFIX; + } + else + { + bSortPrintINChIFlags = 0; + } + INCHI_HEAPCHK + nRet1 = SortAndPrintINChI( pCG, pOut, strbuf, pLog, ip, + NULL /*orig_inp_data*/, NULL /*prep_inp_data*/, + NULL /*composite_norm_data*/, NULL /*pOrigStruct*/, + sd->num_components, sd->num_non_taut, sd->num_taut, + sd->bTautFlags, sd->bTautFlagsDone, pncFlags, num_inp, + pInChI_2, pInChI_Aux_2, &bSortPrintINChIFlags, + save_opt_bits); + INCHI_HEAPCHK + } + else + { + INCHI_HEAPCHK + bSortPrintINChIFlags = 0; + nRet1 = SortAndPrintINChI( pCG, pOut, strbuf, pLog, ip, + NULL /*orig_inp_data*/, NULL /*prep_inp_data*/, + NULL /*composite_norm_data*/, NULL /*pOrigStruct*/, + sd->num_components, sd->num_non_taut, sd->num_taut, + sd->bTautFlags, sd->bTautFlagsDone, pncFlags, num_inp, + pINChI, pINChI_Aux, &bSortPrintINChIFlags, + save_opt_bits); + INCHI_HEAPCHK + } + if ( nRet1 == _IS_FATAL || nRet1 == _IS_ERROR ) + { + break; + } + } + + + INCHI_HEAPCHK + FreeAllINChIArrays( pINChI, pINChI_Aux, num_components ); + INCHI_HEAPCHK + + for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) + { + for ( j = 0; j < TAUT_NUM; j ++ ) + { + if ( OneInput->nNumProtons[iINChI][j].pNumProtons ) + { + inchi_free( OneInput->nNumProtons[iINChI][j].pNumProtons ); + OneInput->nNumProtons[iINChI][j].pNumProtons = NULL; + } + } + } + + INCHI_HEAPCHK + if ( nRet1 == _IS_FATAL || nRet1 == _IS_ERROR ) + { + ret2 = RI_ERR_PROGR; + } + +exit_error: + + inchi_strbuf_close( strbuf ); + + return ret2; +} + + +/* + GetNumNeighborsFromInchi +*/ +int GetNumNeighborsFromInchi( INChI *pInChI, AT_NUMB nAtNumber ) +{ + int i, j, n_vertex, n_neigh, nNumNeigh, bTautAtom, nNumH, nTotNumNeigh, num_atoms; + AT_NUMB taut_at_number; + nAtNumber -= 1; + nNumNeigh = 0; /* number of bonds */ + bTautAtom = 0; /* 1 if atom belongs to a Mobile-H group */ + nNumH = 0; /* number of terminal neighbors H */ + num_atoms = pInChI->nNumberOfAtoms; + /* from RestoreAtomConnectionsSetStereo() */ + /* Connection table structure: + Vert(1) [, Neigh(11), Neigh(12),...], Vert(2) [, Neigh(2,1), Neigh(2,2),...] ... + where Neigh(i,1) < Neigh(i,2) <... < Vert(i); + Vert(i) < Vert(i+1) + */ + for ( i = 1, n_vertex = pInChI->nConnTable[0]-1; i < pInChI->lenConnTable; i ++ ) { + if ( (n_neigh = pInChI->nConnTable[i]-1) < n_vertex ) { + /* vertex - neighbor connection */ + nNumNeigh += ( nAtNumber == n_vertex || nAtNumber == n_neigh ); + } else + /* n_neigh is the next vertex */ + if ( (n_vertex = n_neigh) >= num_atoms ) { + return RI_ERR_PROGR; + } + } + /* is atom tautomeric, from GetTgroupInfoFromInChI() */ + if ( pInChI && pInChI->lenTautomer > 1 && pInChI->nTautomer && pInChI->nTautomer[0] > 0 ) { + int itg, len_tg; + int tot_len_tg = pInChI->lenTautomer - T_GROUP_HDR_LEN*pInChI->nTautomer[0] - 1; /* number of endpoints */ + j = 1; /* index in pInChI->nTautomer[] */ + i = 0; /* index in ti->nEndpointAtomNumber[] */ + for ( itg = 0; itg < pInChI->nTautomer[0]; itg ++ ) { + len_tg = pInChI->nTautomer[j]; /* t-group length not including pInChI->nTautomer[j] */ + j += T_GROUP_HDR_LEN; /* skip t-group header */ + len_tg -= T_GROUP_HDR_LEN-1; + for( ; 0 < len_tg --; j ++, i ++ ) { + taut_at_number = pInChI->nTautomer[j]-1; /* Mobile-H group atom number */ + bTautAtom += (taut_at_number == nAtNumber); + } + } + if ( i != tot_len_tg ) { + return RI_ERR_PROGR; + } + } + /* count hydrogen neighbors */ + if ( pInChI->nNum_H ) { + nNumH = pInChI->nNum_H[nAtNumber]; + } + /* conclusion: if not tautomeric then return positive number, otherwise add 1000 */ + nTotNumNeigh = nNumNeigh + nNumH; + if ( bTautAtom ) { + nTotNumNeigh += 1000; + } + return nTotNumNeigh; +} + + +/* + CountStereoTypes +*/ +int CountStereoTypes( INChI *pInChI, int *num_known_SB, int *num_known_SC, + int *num_unk_und_SB, int *num_unk_und_SC, + int *num_SC_PIII, int *num_SC_AsIII) +{ + static U_CHAR el_number_P=0, el_number_As=0; + INChI_Stereo *Stereo; + int i, ret; + AT_NUMB nAtNumber; + U_CHAR el_number; + if ( !pInChI->nNumberOfAtoms || pInChI->bDeleted ) { + return 0; /* no InChI */ + } + Stereo = (pInChI->StereoIsotopic && + (pInChI->StereoIsotopic->nNumberOfStereoBonds + + pInChI->StereoIsotopic->nNumberOfStereoCenters ))? pInChI->StereoIsotopic: + (pInChI->Stereo && + (pInChI->Stereo->nNumberOfStereoBonds + + pInChI->Stereo->nNumberOfStereoCenters ))? pInChI->Stereo : NULL; + if ( !Stereo ) { + return 1; /* No Stereo */ + } + /* one-time initialization */ + if ( !el_number_P ) { + el_number_P = (U_CHAR)get_periodic_table_number( "P" ); + el_number_As = (U_CHAR)get_periodic_table_number( "As" ); + } + /* count SB and cumulenes */ + for ( i = 0; i < Stereo->nNumberOfStereoBonds; i ++ ) { + if ( ATOM_PARITY_WELL_DEF(Stereo->b_parity[i]) ) { + (*num_known_SB) ++; + } else { + (*num_unk_und_SB) ++; + } + } + /* count SC and allenes */ + for ( i = 0; i < Stereo->nNumberOfStereoCenters; i ++ ) { + if ( !(nAtNumber = Stereo->nNumber[i]) || nAtNumber > pInChI->nNumberOfAtoms ) { + return RI_ERR_PROGR; /* wrong data, should never happen */ + } + if ( ATOM_PARITY_WELL_DEF(Stereo->t_parity[i]) ) { + (*num_known_SC) ++; + } else { + (*num_unk_und_SC) ++; + } + el_number = pInChI->nAtom[nAtNumber-1]; + if ( el_number != el_number_P && el_number != el_number_As ) { + continue; + } + ret = GetNumNeighborsFromInchi( pInChI, nAtNumber ); + if ( ret < 0 ) { + return ret; + } + if ( 3 == ret ) { + *num_SC_PIII += (el_number_P == el_number); + *num_SC_AsIII += (el_number_As == el_number); + } + } + return 2; /* Has Stereo */ +} + + +/* + bInpInchiComponentExists +*/ +int bInpInchiComponentExists( InpInChI *pOneInput, int iInChI, int bMobileH, int k ) +{ + if ( INCHI_BAS != iInChI && iInChI != INCHI_REC || + TAUT_NON != bMobileH && TAUT_YES != bMobileH || k < 0 ) { + return 0; + } + return ( k < pOneInput->nNumComponents[iInChI][bMobileH] && + pOneInput->pInpInChI[iInChI][bMobileH] && + pOneInput->pInpInChI[iInChI][bMobileH][k].nNumberOfAtoms > 0 && + !pOneInput->pInpInChI[iInChI][bMobileH][k].bDeleted ); +} + + +/* + bInpInchiComponentDeleted +*/ +int bInpInchiComponentDeleted( InpInChI *pOneInput, int iInChI, int bMobileH, int k ) +{ + if ( INCHI_BAS != iInChI && iInChI != INCHI_REC || + TAUT_NON != bMobileH && TAUT_YES != bMobileH || k < 0 ) { + return 0; + } + return ( k < pOneInput->nNumComponents[iInChI][bMobileH] && + pOneInput->pInpInChI[iInChI][bMobileH] && + pOneInput->pInpInChI[iInChI][bMobileH][k].nNumberOfAtoms > 0 && + pOneInput->pInpInChI[iInChI][bMobileH][k].bDeleted ); +} + + +/* + bRevInchiComponentExists +*/ +int bRevInchiComponentExists( StrFromINChI *pStruct, int iInChI, int bMobileH, int k ) +{ + if ( !pStruct || /*!pStruct->at2 ||*/ !pStruct->num_atoms || + INCHI_BAS != iInChI && iInChI != INCHI_REC || + TAUT_NON != bMobileH && TAUT_YES != bMobileH || k < 0 ) { + return 0; + } + return ( k < pStruct->RevInChI.num_components[iInChI] && + pStruct->RevInChI.pINChI[iInChI] && + pStruct->RevInChI.pINChI[iInChI][k][bMobileH] && + pStruct->RevInChI.pINChI[iInChI][k][bMobileH]->nNumberOfAtoms > 0 && + !pStruct->RevInChI.pINChI[iInChI][k][bMobileH]->bDeleted ); +} + + +/* + bRevInchiComponentDeleted +*/ +int bRevInchiComponentDeleted( StrFromINChI *pStruct, int iInChI, int bMobileH, int k ) +{ + if ( !pStruct || /*!pStruct->at2 ||*/ !pStruct->num_atoms || + INCHI_BAS != iInChI && iInChI != INCHI_REC || + TAUT_NON != bMobileH && TAUT_YES != bMobileH || k < 0 ) { + return 0; + } + return ( k < pStruct->RevInChI.num_components[iInChI] && + pStruct->RevInChI.pINChI[iInChI] && + pStruct->RevInChI.pINChI[iInChI][k][bMobileH] && + pStruct->RevInChI.pINChI[iInChI][k][bMobileH]->nNumberOfAtoms > 0 && + pStruct->RevInChI.pINChI[iInChI][k][bMobileH]->bDeleted ); +} + + +/* + DetectInpInchiCreationOptions +*/ +int DetectInpInchiCreationOptions( InpInChI *pOneInput, + int *bHasReconnected, + int *bHasMetal, + int *bHasFixedH, + int *nModeFlagsStereo, + int *bTautFlagsStereo ) +{ + int ret=0, bHasStereo; + int nModeFlagsValue=0, bTautFlagsValue; /* stereo flags */ + int iInChI, iMobileH, bIso, k, max_components, num_components; + INChI *pInChI; + int num_known_SB /*Stereo Bonds & Cumulenes >C==C==C==C< */; + int num_known_SC /* Stereo Centers & Allenes >C=C=C< */; + int num_unk_und_SB, num_unk_und_SC; + int num_SC_PIII, num_SC_AsIII; /* has Phosphine or Arsine stereo center(s) */ + + *bHasReconnected = *bHasFixedH = *nModeFlagsStereo = *bTautFlagsStereo = 0; + nModeFlagsValue = bTautFlagsValue = bHasStereo = 0; + num_known_SB = num_known_SC = num_unk_und_SB = num_unk_und_SC = num_SC_PIII = num_SC_AsIII = 0; + *bHasMetal = 0; + + for ( iInChI = 0; iInChI < INCHI_NUM; iInChI ++ ) + { + for ( iMobileH = 0; iMobileH < TAUT_NUM; iMobileH ++ ) + { + for ( bIso = 1; !nModeFlagsValue && 0 <= bIso; bIso -- ) + { + switch( pOneInput->s[iInChI][iMobileH][bIso] ) + { + case 1: /* SABS */ + nModeFlagsValue |= REQ_MODE_STEREO | REQ_MODE_ISO_STEREO; + break; + case 2: + nModeFlagsValue |= REQ_MODE_STEREO | REQ_MODE_ISO_STEREO | REQ_MODE_RELATIVE_STEREO; + break; + case 3: + nModeFlagsValue |= REQ_MODE_STEREO | REQ_MODE_ISO_STEREO | REQ_MODE_RACEMIC_STEREO; + } + } + + max_components = pOneInput->pInpInChI[iInChI][iMobileH]? + pOneInput->nNumComponents[iInChI][iMobileH] : 0; + + for ( k = num_components = 0; k < max_components; k ++ ) + { + pInChI = pOneInput->pInpInChI[iInChI][iMobileH] + k; + ret = CountStereoTypes(pInChI, + &num_known_SB, &num_known_SC, + &num_unk_und_SB, &num_unk_und_SC, + &num_SC_PIII, &num_SC_AsIII); + if ( ret < 0 ) + { + return ret; /* error */ + } + bHasStereo += (ret == 2); + if ( (ret > 0) ) + { + /* ret == 0 => Empty InChI, 1=> No Stereo, 2=> Has Stereo */ + num_components ++; + *bHasReconnected |= ( iInChI == INCHI_REC ); + *bHasFixedH |= ( iMobileH == TAUT_NON ); + } + *bHasMetal |= bInChIHasReconnectedMetal( pInChI ); + } + } + } + + if ( (nModeFlagsValue & REQ_MODE_RELATIVE_STEREO) && (nModeFlagsValue & REQ_MODE_RACEMIC_STEREO) ) + { + return RI_ERR_SYNTAX; + } + if ( bHasStereo && !nModeFlagsValue ) /* REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU*/ + { + /* inversion does not change the stereo or no stereo at all */ + nModeFlagsValue = REQ_MODE_STEREO | REQ_MODE_ISO_STEREO; /* Abs */ + } + + if ( !num_known_SB && num_unk_und_SB ) + { + ; /* full SUU option or SB part of it */ + } else + { + nModeFlagsValue |= REQ_MODE_SB_IGN_ALL_UU; /* ignore Unknown/Undefind SB if no well-defined SB exist */ + } + + if ( !num_known_SC && num_unk_und_SC ) + { + ; /* full SUU option or SB part of it */ + } else + { + nModeFlagsValue |= REQ_MODE_SC_IGN_ALL_UU; /* ignore Unknown/Undefind SC if no well-defined SB exist */ + } + + /* Phosphine and Arsine Stereo */ + if ( num_SC_PIII ) + { + bTautFlagsValue |= TG_FLAG_PHOSPHINE_STEREO; + } + /* Phosphine and Arsine Stereo */ + if ( num_SC_AsIII ) + { + bTautFlagsValue |= TG_FLAG_ARSINE_STEREO; + } + + *nModeFlagsStereo = nModeFlagsValue; + *bTautFlagsStereo = bTautFlagsValue; + + return 0; +} + + +/* + bInChIHasReconnectedMetal +*/ +int bInChIHasReconnectedMetal( INChI *pInChI ) +{ + int i; + if ( pInChI && !pInChI->bDeleted && pInChI->nNumberOfAtoms && pInChI->nAtom ) + { + for ( i = 0; i < pInChI->nNumberOfAtoms; i ++ ) + { + if ( is_el_a_metal( (int)pInChI->nAtom[i] ) ) + { + if ( pInChI->nNumberOfAtoms > 1 || pInChI->nNum_H && pInChI->nNum_H[0] ) + { + return 1; + } + } + } + } + return 0; +} + + +/* + SetProtonsAndXchgIsoH +*/ +int SetProtonsAndXchgIsoH( int bInChI2Structure, + int bReqSplitOutputInChI, + int bReqProtonsForEachComponent, + int bReqNonTaut, + int bReqStereo, + int num_components[INCHI_NUM], + MODE_PIXH nModeProtonIsoExchgH[INCHI_NUM], + InpInChI *OneInput ) +{ + int j, k, k1, ret2=0, iINChI; + int bAvailableProtonsForEachComponent, bAvailableProtonsTotal; + + INCHI_HEAPCHK + + num_components[INCHI_BAS] = num_components[INCHI_REC] = 0; + + for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) { + nModeProtonIsoExchgH[iINChI] = MODE_PIXH_UNDEFINED; + /* are totals of /p and/or /i/h available ? */ + bAvailableProtonsTotal = 0 != OneInput->nNumProtons[iINChI][TAUT_YES].nNumRemovedProtons; + for ( k1 = 0; k1 < NUM_H_ISOTOPES; k1 ++ ) { + bAvailableProtonsTotal |= 0 !=OneInput->nNumProtons[iINChI][TAUT_YES].nNumRemovedIsotopicH[k1]; + } + /* are /p and/or /i/h available for each component ? */ + bAvailableProtonsForEachComponent = (NULL != OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons); + /* decision: add /p to each component, add total to the 1st, add total as one more component */ + /* In case of bInChI2Structure just keep totals if not available for each component */ + if ( bInChI2Structure ) { + nModeProtonIsoExchgH[iINChI] = bAvailableProtonsForEachComponent? + MODE_PIXH_ADD_TO_EACH: + MODE_PIXH_KEEP_TOTALS; + } else + if ( !bReqSplitOutputInChI ) { + nModeProtonIsoExchgH[iINChI] = bAvailableProtonsForEachComponent? + MODE_PIXH_ADD_TO_EACH : + MODE_PIXH_ADD_TO_FIRST; + } else + if ( !bAvailableProtonsForEachComponent ) { + nModeProtonIsoExchgH[iINChI] = bAvailableProtonsTotal? + MODE_PIXH_ADD_A_PIXH_COMPONENT : + MODE_PIXH_ADD_TO_FIRST; + } else + /* bAvailableProtonsForEachComponent && bReqSplitOutputInChI */ + if ( bReqProtonsForEachComponent ) { + nModeProtonIsoExchgH[iINChI] = MODE_PIXH_ADD_TO_EACH; + } else { + nModeProtonIsoExchgH[iINChI] = bReqNonTaut? + MODE_PIXH_ADD_TO_EACH : + MODE_PIXH_ADD_A_PIXH_COMPONENT; + } + /* remove unneeded data: protons for each component */ + if ( bAvailableProtonsForEachComponent && + nModeProtonIsoExchgH[iINChI] != MODE_PIXH_ADD_TO_EACH ) { + inchi_free( OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons ); + OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons = NULL; + bAvailableProtonsForEachComponent = 0; + } + /* remove unneeded data: total protons all components */ + if ( bAvailableProtonsTotal && nModeProtonIsoExchgH[iINChI] == MODE_PIXH_ADD_TO_EACH ) { + OneInput->nNumProtons[iINChI][TAUT_YES].nNumRemovedProtons = 0; + for ( k1 = 0; k1 < NUM_H_ISOTOPES; k1 ++ ) { + OneInput->nNumProtons[iINChI][TAUT_YES].nNumRemovedIsotopicH[k1] = 0; + } + bAvailableProtonsTotal = 0; + } + /* remove unneeded data: Fixed-H InChI; no protons data exist for Fixed-H */ + if ( !bReqNonTaut && OneInput->nNumComponents[iINChI][TAUT_NON] ) { + j = TAUT_NON; + for ( k = 0; k < OneInput->nNumComponents[iINChI][j]; k ++ ) { + Free_INChI_Members( &OneInput->pInpInChI[iINChI][j][k] ); + } + inchi_free( OneInput->pInpInChI[iINChI][j] ); + OneInput->pInpInChI[iINChI][j] = NULL; + OneInput->nNumComponents[iINChI][j] = 0; + } +#ifdef NEVER + /* remove unneeded data: Mobile-H InChI ????? */ + if ( bReqNonTaut && OneInput->nNumComponents[iINChI][TAUT_NON] ) { + j = TAUT_YES; + for ( k = 0; k < OneInput->nNumComponents[iINChI][j]; k ++ ) { + Free_INChI_Members( &OneInput->pInpInChI[iINChI][j][k] ); + } + inchi_free( OneInput->pInpInChI[iINChI][j] ); + OneInput->pInpInChI[iINChI][j] = NULL; + OneInput->nNumComponents[iINChI][j] = 0; + nModeProtonIsoExchgH[iINChI] = MODE_PIXH_UNDEFINED; + if ( OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons ) { + inchi_free( OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons); + OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons = NULL; + } + } +#endif + /* add one more component containing only /p and /i/h */ + if ( nModeProtonIsoExchgH[iINChI] == MODE_PIXH_ADD_A_PIXH_COMPONENT && + OneInput->nNumComponents[iINChI][TAUT_YES] || + /* always add one deleted component if no non-taut InChI is available */ + bInChI2Structure && !bAvailableProtonsForEachComponent && + !OneInput->nNumComponents[iINChI][TAUT_NON] && + OneInput->nNumComponents[iINChI][TAUT_YES] ) { + int nPrevLen, nLen=0; + j = TAUT_YES; + nPrevLen = OneInput->nNumComponents[iINChI][j]; + for ( k = 0; k < nPrevLen; k ++ ) { + nLen += !OneInput->pInpInChI[iINChI][j][k].bDeleted; + } + if ( nLen == nPrevLen ) { + /* add one more component */ + INChI *pInChI = (INChI *)inchi_calloc( nLen+1, sizeof(*pInChI) ); + if ( !pInChI ) { + ret2 = RI_ERR_ALLOC; + goto exit_error; + } + memcpy( pInChI, OneInput->pInpInChI[iINChI][j], nLen*sizeof(*pInChI) ); + inchi_free( OneInput->pInpInChI[iINChI][j] ); + OneInput->pInpInChI[iINChI][j] = pInChI; + } + OneInput->nNumComponents[iINChI][j] = nLen+1; + + for ( k = nLen; k < nPrevLen; k ++ ) { + Free_INChI_Members( &OneInput->pInpInChI[iINChI][j][k] ); + memset( &OneInput->pInpInChI[iINChI][j][k], 0, sizeof(OneInput->pInpInChI[iINChI][j][k])); + } + /* mark the last component as a proton */ + if ( 0 > (ret2 = nFillOutProtonMobileH( OneInput->pInpInChI[iINChI][j]+nLen ) ) ) { + goto exit_error; + } + } + INCHI_HEAPCHK + + /* remove unneeded Stereo and/or Fixed H */ + if ( !bReqStereo ) { + for ( j = 0; j < TAUT_NUM; j ++ ) { + for ( k = 0; k < OneInput->nNumComponents[iINChI][j]; k ++ ) { + if ( OneInput->pInpInChI[iINChI][j][k].Stereo ) { + Free_INChI_Stereo(OneInput->pInpInChI[iINChI][j][k].Stereo); + inchi_free( OneInput->pInpInChI[iINChI][j][k].Stereo ); + OneInput->pInpInChI[iINChI][j][k].Stereo = NULL; + } + if ( OneInput->pInpInChI[iINChI][j][k].StereoIsotopic ) { + Free_INChI_Stereo(OneInput->pInpInChI[iINChI][j][k].StereoIsotopic); + inchi_free( OneInput->pInpInChI[iINChI][j][k].StereoIsotopic ); + OneInput->pInpInChI[iINChI][j][k].StereoIsotopic = NULL; + } + INCHI_HEAPCHK + } + } + } + } + + num_components[INCHI_BAS] = inchi_max( OneInput->nNumComponents[INCHI_BAS][TAUT_YES], + OneInput->nNumComponents[INCHI_BAS][TAUT_NON] ); + num_components[INCHI_REC] = inchi_max( OneInput->nNumComponents[INCHI_REC][TAUT_YES], + OneInput->nNumComponents[INCHI_REC][TAUT_NON] ); + +exit_error: + return ret2; +} + + +/* + GetInChIFormulaNumH +*/ +int GetInChIFormulaNumH( INChI *pInChI, int *nNumH ) +{ /* get number of H including bridging hydrogen atoms */ + const char *p, *q; + *nNumH = 0; + if ( pInChI->szHillFormula ) { + for ( p = strchr( pInChI->szHillFormula, 'H'); p; p = strchr(p, 'H') ) { + p ++; + if ( !islower( UCINT *p ) ) { + /* found hydrogen in the formula */ + if ( isdigit( UCINT *p ) ) { + *nNumH += (int)inchi_strtol( p, &q, 10 ); + p = q; + } else { + *nNumH += 1; + } + } + } + } + return 0; +} + + +/* + GetInChINumH +*/ +int GetInChINumH( INChI *pInChI, int *nNumH ) +{ + int i, j, nNumTautGroups, iTautGroup, nTautGroupLen, lenTautomer; + *nNumH = 0; + for ( i = 0; i < pInChI->nNumberOfAtoms; i ++ ) { + *nNumH += ( pInChI->nAtom[i] == EL_NUMBER_H ); /* bridging H */ + *nNumH += pInChI->nNum_H[i]; + } + /* earlier nNum_H_fixed[] should have been added to pInChI->nNum_H[] */ + /* + if ( pInChI->nNum_H_fixed ) { + for ( i = 0; i < pInChI->nNumberOfAtoms; i ++ ) { + *nNumH += pInChI->nNum_H_fixed[i]; + } + } + */ + if ( pInChI->lenTautomer > 3 && pInChI->nTautomer ) { + lenTautomer = pInChI->lenTautomer; + j = 0; + nNumTautGroups = pInChI->nTautomer[j ++]; + for ( iTautGroup = 0; j < lenTautomer && iTautGroup < nNumTautGroups; iTautGroup ++, j += nTautGroupLen ) { + nTautGroupLen = pInChI->nTautomer[j]+1; + *nNumH += pInChI->nTautomer[j+1]; + } + if ( iTautGroup != nNumTautGroups || j != lenTautomer ) { + return RI_ERR_PROGR; + } + } + if ( pInChI->nNum_H_fixed && (pInChI->lenTautomer || pInChI->nTautomer) ) { + return RI_ERR_PROGR; + } + return 0; +} + + +/* + GetInChIIsoH +*/ +int GetInChIIsoH( INChI *pInChI, int nNumIsotopicH[NUM_H_ISOTOPES] ) +{ + int i; + for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { + nNumIsotopicH[i] = 0; + } + for ( i = 0; i < pInChI->nNumberOfIsotopicAtoms; i ++ ) { + if ( pInChI->IsotopicAtom[i].nIsoDifference > 0 && + pInChI->IsotopicAtom[i].nIsoDifference <= NUM_H_ISOTOPES ) { + if ( !pInChI->nAtom || + !pInChI->IsotopicAtom[i].nAtomNumber || + pInChI->IsotopicAtom[i].nAtomNumber > pInChI->nNumberOfAtoms ) { + return RI_ERR_PROGR; + } + if ( pInChI->nAtom[pInChI->IsotopicAtom[i].nAtomNumber-1] == EL_NUMBER_H ) { + /* isotopic H in connection table */ + nNumIsotopicH[ pInChI->IsotopicAtom[i].nIsoDifference-1 ] ++; + } + } + nNumIsotopicH[0] += pInChI->IsotopicAtom[i].nNum_H; + nNumIsotopicH[1] += pInChI->IsotopicAtom[i].nNum_D; + nNumIsotopicH[2] += pInChI->IsotopicAtom[i].nNum_T; + } + return 0; +} + + +/******************************************************************************************/ +typedef struct tagNumElem +{ + int num; + /* + int iso; + */ +} NUM_ELEM; + + + +/* + Read a single InChI input Line and convert to Data +*/ +int InChILine2Data( INCHI_IOSTREAM *pInp, + SEGM_LINE *pLine, + char **pStr, + int *pState, + int *nErr, + INChI *pInpInChI[INCHI_NUM][TAUT_NUM], + int nNumComponents[INCHI_NUM][TAUT_NUM], + REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM], + int s[INCHI_NUM][TAUT_NUM][2], + int bReadCoord, + int bInchi2Struct, + INCHI_MODE nMode, + int *bStdFormat, + int *input_has_save_opt, + unsigned char *input_save_opt_bits, + OrigAtDataPolymer **ppolymer, + OrigAtDataV3000 **pv3000 ) +{ + int iINChI, i, j, k, m, len1, len2, ret2 = 0, retAux = 0, stateAux = 0; + int ret, tot_charge[INCHI_NUM][TAUT_NUM]; + int i1, i2, i3; + int kc; + NUM_ELEM *num_elem[INCHI_NUM][TAUT_NUM]; + + +#if ( FIX_I2I_STEREOCONVERSION_BUG == 1 ) +/* (2008-03-06) 1=> Fix bug of i2i conversion SAbs-->(SRel||Srac) */ +/* (converter does not placed proper stereo to output) */ + + /* set new stereo type as requested by conversion option */ + int target_stereo_type = 1; + if (nMode & REQ_MODE_RELATIVE_STEREO) + target_stereo_type = 2; + else if (nMode & REQ_MODE_RACEMIC_STEREO) + target_stereo_type = 3; +#endif + + + memset( num_elem, 0, sizeof(num_elem) ); + + ret = ReadInChILine( pInp, pLine, pStr, pState, pInpInChI, + nNumComponents, nNumProtons, s, bStdFormat, + input_has_save_opt, input_save_opt_bits, + bInchi2Struct, ppolymer, pv3000); + + +#if ( FIX_I2I_STEREOCONVERSION_BUG == 1 ) + /* modify stereo type for layers as requested */ + if (target_stereo_type > 1) + for (i1=0;i1SAbs, SRac=>SAbs */ + s[i1][i2][i3] = target_stereo_type; + } +#endif + + + *nErr = 0; + if ( (ret == RI_ERR_EOL) && + nNumComponents[INCHI_BAS][TAUT_YES] + + nNumComponents[INCHI_BAS][TAUT_NON] && bReadCoord ) { + retAux = ReadInChICoord( pInp, pLine, &stateAux, pInpInChI, nNumComponents ); + } + if ( (ret == RI_ERR_EOL || ret == RI_ERR_EOF) && + nNumComponents[INCHI_BAS][TAUT_YES] + + nNumComponents[INCHI_BAS][TAUT_NON] ) { + /* post-processing: add omitted layers */ + *pState = IST_MATERIAL_BALANCE_ERROR; + for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) + { + + for ( j = 0; j < TAUT_NUM; j ++ ) + { + /* for Mobile/Fixed H (j) ... */ + + int bIsotopic, bStereoType, bStereoTypeAlt; + int nMH2FH_AltInv=0, nFH2iFH_AltInv=0 /*, niMH2iFH_AltInv=0, nMH2iMH_AltInv=0*/; + int jAlt = ALT_TAUT(j); + INCHI_MODE nFlags = 0, nFlagsAlt = 0; + /* get stereo type: ABS, REL, RAC, or nothing */ + tot_charge[iINChI][j] = 0; + for ( bIsotopic = bStereoType = bStereoTypeAlt = 0; bIsotopic < 2; bIsotopic ++ ) { + if ( !bStereoType || bStereoType < s[iINChI][j][bIsotopic] ) { + bStereoType = s[iINChI][j][bIsotopic]; + } + if ( !bStereoTypeAlt || bStereoTypeAlt < s[iINChI][jAlt][bIsotopic] ) { + bStereoTypeAlt = s[iINChI][jAlt][bIsotopic]; + } + nFlags = bStereoType ==2? INCHI_FLAG_REL_STEREO : bStereoType ==3? INCHI_FLAG_RAC_STEREO : 0; + nFlagsAlt = bStereoTypeAlt==2? INCHI_FLAG_REL_STEREO : bStereoTypeAlt==3? INCHI_FLAG_RAC_STEREO : 0; + } + /* set stereo type to each component */ + /* add missing nNum_H and nConnTable */ + if ( nNumComponents[iINChI][j] ) { + num_elem[iINChI][j] = (NUM_ELEM *)inchi_calloc( nElDataLen+1, sizeof(num_elem[0][0][0]) ); + if ( !num_elem[iINChI][j] ) { + ret2 = RI_ERR_ALLOC; + goto exit_function; + } + } + for ( k = 0; k < nNumComponents[iINChI][j]; k ++ ) + { + /* for each component k ... */ + + if ( pInpInChI[iINChI][j] ) { + INChI *pInChI = &pInpInChI[iINChI][j][k]; + INChI *pInChI_Alt = (knNumberOfAtoms)? pInpInChI[iINChI][jAlt]:NULL;*/ /* 2007-09-25 DT */ + pInpInChI[iINChI][jAlt][k].nNumberOfAtoms)? &pInpInChI[iINChI][jAlt][k]:NULL; + + if ( nFlags ) { + pInChI->nFlags |= nFlags; + } else + if ( j == TAUT_NON && !nFlags && nFlagsAlt ) { + pInChI->nFlags |= nFlagsAlt; + } + /**** add empty immobile H (nNum_H) if it is missing ****/ + if ( !pInChI->nNum_H && + !(pInChI->nNum_H = (S_CHAR *)inchi_calloc( pInChI->nNumberOfAtoms+1, sizeof(pInChI->nNum_H[0]) ) ) ) { + ret2 = RI_ERR_ALLOC; + goto exit_function; + } + /**** add single atom nConnTable if it is missing ****/ + if ( !pInChI->nConnTable ) { + AT_NUMB *pCT; + int lenCT; + if ( j == TAUT_NON && k < nNumComponents[iINChI][TAUT_YES] && + (pCT = pInpInChI[iINChI][TAUT_YES][k].nConnTable) && + (lenCT = pInpInChI[iINChI][TAUT_YES][k].lenConnTable) > 0) { + if ( !(pInChI->nConnTable = (AT_NUMB *)inchi_calloc( lenCT+1, sizeof(pInChI->nConnTable[0]) ) ) ) { + ret2 = RI_ERR_ALLOC; + goto exit_function; + } + memcpy( pInChI->nConnTable, pCT, lenCT*sizeof(pInChI->nConnTable[0]) ); + pInChI->lenConnTable = lenCT; + } else { + if ( j == TAUT_YES && pInChI->nNumberOfAtoms > 1 ) { + *pState = IST_MOBILE_H_CONNECTIONS + (iINChI==INCHI_REC? IST_HAPPENED_IN_RECMET : 0); + ret2 = RI_ERR_SYNTAX; + goto exit_function; + } + if ( !(pInChI->nConnTable = (AT_NUMB *)inchi_calloc( pInChI->nNumberOfAtoms+1, sizeof(pInChI->nConnTable[0]) ) ) ) { + ret2 = RI_ERR_ALLOC; + goto exit_function; + } + pInChI->lenConnTable = 1; + pInChI->nConnTable[0] = 1; + } + } else + if ( pInChI->nConnTable && !pInChI->lenConnTable && pInChI->nNumberOfAtoms == 1 ) { + pInChI->nConnTable[0] = 1; + pInChI->lenConnTable = 1; + } + /**** copy charge: Mobile H --> Fixed H; ****/ + if ( j == TAUT_NON ) { + /* + if ( pInChI->nTotalCharge == NO_VALUE_INT ) { + pInChI->nTotalCharge = 0; + } else + */ + if ( !pInChI->nTotalCharge && k < nNumComponents[iINChI][TAUT_YES] ) { + INChI *pAltInChI = &pInpInChI[iINChI][TAUT_YES][k]; /* Mobile H InChI */ + if ( pAltInChI->nTotalCharge && pAltInChI->nTotalCharge != NO_VALUE_INT ) { + pInChI->nTotalCharge = pAltInChI->nTotalCharge; + } + } + } + /***** Fixed H: add pInChI->nNum_H_fixed to pInChI->nNum_H ****/ + if ( j == TAUT_NON && pInChI->nNum_H && pInChI->nNum_H_fixed ) { + for ( m = 0; m < pInChI->nNumberOfAtoms; m ++ ) { + pInChI->nNum_H[m] += pInChI->nNum_H_fixed[m]; + } + } + /***** copy isotopic atoms: Mobile H --> Fixed H ******/ + if ( j == TAUT_YES && pInChI->nNumberOfIsotopicAtoms && + k < nNumComponents[iINChI][TAUT_NON] ) { + INChI *pAltInChI = &pInpInChI[iINChI][TAUT_NON][k]; /* Fixed H InChI */ + + if ( !pAltInChI->nNumberOfIsotopicAtoms ) { + ret2=CopySegment( pAltInChI, pInChI, CPY_ISO_AT, 0, 0); + if ( ret2 < 0 ) { + goto exit_function; + } + } + } + /**** copy coordinates: Mobile H --> Fixed H ******/ + if ( j == TAUT_YES && pInChI->IsotopicTGroup && + k < nNumComponents[iINChI][TAUT_NON] ) { + INChI *pAltInChI = &pInpInChI[iINChI][TAUT_NON][k]; /* Fixed H InChI */ + + if ( !pAltInChI->IsotopicTGroup ) { + XYZ_COORD *pxyz = (XYZ_COORD *)inchi_calloc( pInChI->nNumberOfAtoms, sizeof(pxyz[0])); + if ( pxyz ) { + memcpy( pxyz, pInChI->IsotopicTGroup, pInChI->nNumberOfAtoms * sizeof(pxyz[0]) ); + pAltInChI->IsotopicTGroup = (INChI_IsotopicTGroup *)pxyz; + } else { + ret2 = RI_ERR_ALLOC; + goto exit_function; + } + } + } + + /******************************************************** + * * + * Restore omitted stereo seqments * + * * + * order of restoring: * + * * + * 1. Fixed H (F) -> (FI) Isotopic Fixed H * + * 2. Mobile H (M) -> (F) Fixed H * + * 3. Isotopic Mobile H (MI) -> (FI) Isotopic Fixed H * + * 4. Mobile H (M) -> (MI) Isotopic Mobile H * + * * + ********************************************************/ + + /***** (4) copy stereo: Mobile H --> isotopic Mobile H ******/ + if ( j == TAUT_YES ) { + int bIso = pInChI->nNumberOfIsotopicAtoms || + (pInChI->StereoIsotopic && + pInChI->StereoIsotopic->nNumberOfStereoCenters + + pInChI->StereoIsotopic->nNumberOfStereoBonds) || + pInChI_Alt && pInChI_Alt->nNumberOfIsotopicAtoms; + + /* non-isotopic Mobile H => isotopic Mobile H */ + if ( bIso ) { + if ( pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters && + (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->t_parity) ) { + if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3, 1, 0)) || + (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) && + 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3_M, 1, 0))) { + goto exit_function; + } + if ( (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { + if ( pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT ) { + pInChI->Stereo->nCompInv2Abs = s[iINChI][j][0]>0? 2 : 0; + } + if ( pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { + pInChI->StereoIsotopic->nCompInv2Abs = s[iINChI][j][1]>0? 2 : 0; + } + } + } else + /* copy sp3 inversion info: non-isotopic Mobile H => isotopic Mobile H */ + if ( pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters && + pInChI->StereoIsotopic && pInChI->StereoIsotopic->nNumberOfStereoCenters && + pInChI->Stereo->nCompInv2Abs ) { + if ( (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) && + pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT && + pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { + pInChI->Stereo->nCompInv2Abs = s[iINChI][j][0]>0? 2 : 0; + pInChI->StereoIsotopic->nCompInv2Abs = s[iINChI][j][1]>0? 2 : 0; + } else + if (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs ) { + pInChI->StereoIsotopic->nCompInv2Abs = pInChI->Stereo->nCompInv2Abs; + } + } + } + if ( bIso && + pInChI->Stereo && pInChI->Stereo->nNumberOfStereoBonds && + (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->b_parity) ) { + if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP2, 1, 0)) ) { + goto exit_function; + } + } + } + /***** (0) set nCompInv2Abs to Fixed-H *********************************/ + if ( j == TAUT_NON ) { + if ( pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters && + pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT ) { + /* case of /sN and /t... in non-isotopic Mobile-H, no /s in non-isotopic Fixed-H */ + if ( !s[iINChI][j][0] && s[iINChI][jAlt][0]>0 && /* /sN is not present in F and is present in M */ + pInChI_Alt && pInChI_Alt->Stereo && pInChI_Alt->Stereo->nNumberOfStereoCenters ) { + /* inherit from Mobile-H */ + /* /s1 in M and MI; /m1 or /m0 in MI; /m. in M; no /m in F. Inherit MI->FI. Added 10-15-2007 */ + if ( pInChI_Alt->Stereo->nCompInv2Abs == 0 && /* M: /m. ; means no /m for this component */ + pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT && /* F: no /m segment for all components */ + pInChI_Alt->StereoIsotopic && /* MI: present */ + pInChI_Alt->StereoIsotopic->nCompInv2Abs != 0 && + pInChI_Alt->StereoIsotopic->nCompInv2Abs != NO_VALUE_INT && /* MI: /m0 or /m1 */ + !s[iINChI][j][0] && !s[iINChI][j][1] && /* F, FI: no /s */ + s[iINChI][jAlt][0] == 1 && s[iINChI][jAlt][1] == 1 /* M, MI: /s1 and /s1 */ + ) { + /* copy /m from MI to FI */ + if ( 0 > (ret2 = CopySegment( pInChI, pInChI_Alt, CPY_SP3_M, 1, 1)) ) { + goto exit_function; + } + } else + /* the following if(){...} was added to fix m1 bug 2007-09-25 DT */ + if ( pInChI_Alt->Stereo->nCompInv2Abs != NO_VALUE_INT && s[iINChI][jAlt][0] == 1 ) { + pInChI->Stereo->nCompInv2Abs = pInChI_Alt->Stereo->nCompInv2Abs; + } else + /* M and MI contain /sN and /sN, N=2,3. Added 10-15-2007 */ + if ( pInChI_Alt->Stereo->nCompInv2Abs == NO_VALUE_INT && + pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT && + !s[iINChI][j][0] && !s[iINChI][j][1] && + (s[iINChI][jAlt][0] & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) && + (s[iINChI][jAlt][1] & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { + int bIso = pInChI->nNumberOfIsotopicAtoms || + (pInChI->StereoIsotopic && + pInChI->StereoIsotopic->nNumberOfStereoCenters + + pInChI->StereoIsotopic->nNumberOfStereoBonds) || + pInChI_Alt && pInChI_Alt->nNumberOfIsotopicAtoms; + if ( bIso ){ + if ( !pInChI_Alt->StereoIsotopic && /* create zero/NULL-initialized pInChI_Alt->StereoIsotopic */ + 0 > (ret2 = CopySegment( pInChI_Alt, pInChI_Alt, CPY_SP3_M, 1, -1))) { + goto exit_function; + } + pInChI_Alt->StereoIsotopic->nCompInv2Abs = 2; /* MI: /m1 or /m0 */ + pInChI_Alt->Stereo->nCompInv2Abs = 0; /* M: /m. ; no /m for this component */ + pInChI->Stereo->nCompInv2Abs = NO_VALUE_INT+1; /* FI: Stereo->CompInv2Abs=0, StereoIsotopic->CompInv2Abs=1 or -1 */ + } else { + pInChI->Stereo->nCompInv2Abs = 2; /* F: /m1 or /m0, omitted from InChI as a repetition */ + pInChI_Alt->Stereo->nCompInv2Abs = 2; /* M: /m1 or /m0; in Srel/SRac case the value = 2 */ + } + } else { + pInChI->Stereo->nCompInv2Abs = 2; /* F: /m1 or /m0, omitted from InChI as a repetition */ + pInChI_Alt->Stereo->nCompInv2Abs = 2; /* M: /m1 or /m0; in Srel/SRac case the value = 2 */ + } + } else + /* case of /sN in Isotopic Fixed-H only, /t... in Fixed-H, no /m (2007-08-27 DT) */ + if ( !s[iINChI][j][0] && !s[iINChI][jAlt][0] && /* /sN in Fixed-H isotopic only */ + (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) && + !(pInChI->StereoIsotopic && pInChI->StereoIsotopic->nNumberOfStereoCenters) && + /*!(pInChI_Alt && pInChI_Alt->Stereo && pInChI_Alt->Stereo->nNumberOfStereoCenters) &&*/ + !(pInChI_Alt && pInChI_Alt->StereoIsotopic && pInChI_Alt->StereoIsotopic->nNumberOfStereoCenters) ) { + pInChI->Stereo->nCompInv2Abs = NO_VALUE_INT+1; /* Stereo->CompInv2Abs=0, StereoIsotopic->CompInv2Abs=1 or -1 */ + } else { + pInChI->Stereo->nCompInv2Abs = s[iINChI][j][0]>0? 2 : 0; + } + } + } + /***** (1) copy stereo: non-isotopic Fixed H --> isotopic Fixed H ******/ + if ( j == TAUT_NON ) { + int bIso = pInChI->nNumberOfIsotopicAtoms || + (pInChI->StereoIsotopic && + pInChI->StereoIsotopic->nNumberOfStereoCenters + + pInChI->StereoIsotopic->nNumberOfStereoBonds) || + pInChI_Alt && pInChI_Alt->nNumberOfIsotopicAtoms; + /* non-isotopic Fixed H => isotopic Fixed H */ + if ( bIso ) { + if ( pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters && + (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->t_parity) ) { + /* -- replaced 2007-08-27 by (aaa), see below -- DT + if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3, 1, 0)) || + !(pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) && + 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3_M, 1, 0))) { + goto exit_function; + } + */ + /*----------- replacement (aaa) begin 2007-08-27 DT */ + if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3, 1, 0)) ) { + goto exit_function; + } + if ( pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT+1 ) { + pInChI->Stereo->nCompInv2Abs = 0; + pInChI->StereoIsotopic->nCompInv2Abs = 2; + } else + if ( !(pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) && + 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3_M, 1, 0))) { + goto exit_function; + } + /*----------- replacement (aaa) end 2007-08-27 DT */ + if ( (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { + if ( pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT ) { + pInChI->Stereo->nCompInv2Abs = s[iINChI][j][0]>0? 2 : 0; + } + if ( pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { + pInChI->StereoIsotopic->nCompInv2Abs = s[iINChI][j][1]>0? 2 : 0; + } + } +#ifdef NEVER + if ( (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) && + !s[iINChI][j][0] && s[iINChI][j][0]>0 ) { + /* copied Rel/Rac stereo to Iso; /s is in Iso /s is not in non-Iso */ + /* this means all difference in stereo is in inversion */ + if ( pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT && + pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { + pInChI->Stereo->nCompInv2Abs = 0; /* missing */ + pInChI->StereoIsotopic->nCompInv2Abs = 2; /* unusual value */ + } + } +#endif + } else + /* copy sp3 inversion info: non-isotopic Fixed H --> isotopic Fixed H */ + if ( pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters && + pInChI->StereoIsotopic && pInChI->StereoIsotopic->nNumberOfStereoCenters && + pInChI->Stereo->nCompInv2Abs ) { + if ( (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) && + pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT && + pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { + pInChI->Stereo->nCompInv2Abs = s[iINChI][j][0]>0? 2 : 0; + pInChI->StereoIsotopic->nCompInv2Abs = s[iINChI][j][1]>0? 2 : 0; + } else + if (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) { + pInChI->StereoIsotopic->nCompInv2Abs = pInChI->Stereo->nCompInv2Abs; + } + } + } + if ( bIso && + pInChI->Stereo && pInChI->Stereo->nNumberOfStereoBonds && + (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->b_parity) ) { + if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP2, 1, 0)) ) { + goto exit_function; + } + } + } + + /***** copy stereo: Mobile H --> Fixed H ******/ + if ( j == TAUT_NON && k < nNumComponents[iINChI][TAUT_YES] ) { + INChI *pAltInChI = &pInpInChI[iINChI][TAUT_YES][k]; /* Mobile H InChI */ + int bIso = pInChI->nNumberOfIsotopicAtoms || + (pInChI->StereoIsotopic && + pInChI->StereoIsotopic->nNumberOfStereoCenters + + pInChI->StereoIsotopic->nNumberOfStereoBonds) || + pAltInChI && ( + pAltInChI->nNumberOfIsotopicAtoms || + (pAltInChI->StereoIsotopic && + pAltInChI->StereoIsotopic->nNumberOfStereoCenters + + pAltInChI->StereoIsotopic->nNumberOfStereoBonds) ); + int bNo_InChI_t = (!pInChI->Stereo || !pInChI->Stereo->t_parity); + int bNo_InChI_m = (!pInChI->Stereo || NO_VALUE_INT == pInChI->Stereo->nCompInv2Abs); + + /* (2) non-isotopic Mobile H => non-isotopic Fixed H */ + if ( pAltInChI->Stereo && pAltInChI->Stereo->nNumberOfStereoCenters && + (!pInChI->Stereo || !pInChI->Stereo->t_parity) ) + { +#if ( FIX_I2I_STEREOCONVERSION_BUG2 == 1 ) + /* (2008-04-02) 1=> Fix bug of i2i conversion SAbs-->(SRel||Srac) */ + /* (converter skipped empty '/t' or sometimes produced an excess one */ + + /* check whether t stereo is actually present */ + int bHave_t_stereo = 1; + if (pInChI->Stereo) + bHave_t_stereo = pInChI ->Stereo->nNumberOfStereoCenters; + /* account for stereobonds present */ + if ( bHave_t_stereo < 1 ) + if ( pInChI->Stereo->nNumberOfStereoBonds > 0 ) + bHave_t_stereo=1; + /* copy stereo anyway ... */ +#endif + if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3, 0, 0)) || + (!pInChI->Stereo->nCompInv2Abs || NO_VALUE_INT == pInChI->Stereo->nCompInv2Abs) && + 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3_M, 0, 0)) ) + { + goto exit_function; + } + +#if ( FIX_I2I_STEREOCONVERSION_BUG2 == 1 ) + /* ... correct just copied stereo if applicable */ + if ( (s[iINChI][j][0] < 1) && + (bHave_t_stereo <1 ) && + (pAltInChI->Stereo->nNumberOfStereoCenters > 0) && + (s[iINChI][jAlt][0] < 1) ) + { + /* (2010-02-28) if not all stereo centers are unknown/undefined */ + /* at which condition stereo still should present .. */ + int all_UU = 1; + for (kc=0; kcStereo->nNumberOfStereoCenters; kc++) + { + if ( (pAltInChI->Stereo->t_parity[kc] != AB_PARITY_UNKN) && + (pAltInChI->Stereo->t_parity[kc] != AB_PARITY_UNDF) ) + { + all_UU=0; + break; + } + } + if (!all_UU) + pInChI->Stereo->nNumberOfStereoCenters = 0; + } +#endif + + /* in case of missing nCompInv2Abs, 2005-05-10 */ + if ( (pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT) && + (nFlagsAlt & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { + if ( s[iINChI][jAlt][0] > 0 && s[iINChI][j][0] > 0 ) { + /* suppose once in a while only non-taut stereo changes if inverted */ + pAltInChI->Stereo->nCompInv2Abs = (++nMH2FH_AltInv)%2? 2:0; + pInChI->Stereo->nCompInv2Abs = 2; + } else + /* Mobile-H: /t.. /sN; Mobile-H isotopic: /sN (n=2 or 3), not /t...; Fixed-H layer is present, has no /t, no /i/t */ + /* Mobile-H /sN was caused by another component that would have same /mN in all layers */ + /* therefore, in case of Abs. Stereo, Mobile-H stereo isotopic stereo would have /m1 */ + /* In case of Rel/Rac stereo, since no /m1 could occur in Mobile-H isotopic, */ + /* no pAltInChI->StereoIsotopic or pInChI->StereoIsotopic have been created yet. */ + /* added 10-11-2007 to fix i2i bug for Rel/Rac stereo */ + if ( nNumComponents[iINChI][j] > 1 && + bNo_InChI_t && bNo_InChI_m /* no /t... or /mN in Fixed-H */ && !nFlags && + !(pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->t_parity) && + !(pInChI->StereoIsotopic && pInChI->StereoIsotopic->t_parity) && + s[iINChI][j][0]==0 && s[iINChI][j][1] == 0 && + /* /sN, N=2 or 3 only in Mobile-H AND Mobile-H isotopic */ + (s[iINChI][jAlt][0] & ((INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO))) && + (s[iINChI][jAlt][1] & ((INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO))) ) + { + if ( bIso ) { + /* create two zero/NULL-initialized isotopic stereo if they do not exist */ + if ( !pInChI->StereoIsotopic && 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3_M, 1, -1)) + /* -- the following will be created later, in TAUT_YES part of the code -- */ + || !pAltInChI->StereoIsotopic && 0 > (ret2 = CopySegment( pAltInChI, pAltInChI, CPY_SP3_M, 1, -1)) ) { + goto exit_function; + } + /* same value = 2 for MI and FI; here we assign only FI */ + pInChI->StereoIsotopic->nCompInv2Abs = 2; + pInChI->Stereo->nCompInv2Abs = 0; + /* -- the following will NOT be assigned later, in TAUT_YES part of the code -- */ + pAltInChI->StereoIsotopic->nCompInv2Abs = 2; + pAltInChI->Stereo->nCompInv2Abs = 0; + /* */ + } else { + if ( NO_VALUE_INT == pInChI->Stereo->nCompInv2Abs && + NO_VALUE_INT == pAltInChI->Stereo->nCompInv2Abs ) { + pInChI->Stereo->nCompInv2Abs = 2; + pAltInChI->Stereo->nCompInv2Abs = 2; + } + } + } else + if ( (s[iINChI][jAlt][0] > 0 || s[iINChI][j][0] > 0) && s[iINChI][j][0] >= 0 ) + pInChI->Stereo->nCompInv2Abs = 2; + else + /* Mobile-H: /t..., no /sN; Mobile-H isotopic: /s2 or /s3, not /t; Fixed-H layer is present, has no /t, no /i/t */ + /* therefore, in case of Abs. Stereo, Mobile-H stereo isotopic stereo would have /m1 */ + /* In case of Rel/Rac stereo, since no /m1 could occur in Mobile-H isotopic, */ + /* no pAltInChI->StereoIsotopic or pInChI->StereoIsotopic have been created yet. */ + /* added 10-10-2007 to fix i2i bug for Rel/Rac stereo */ + if ( bIso && bNo_InChI_t && bNo_InChI_m /* no /t... or /mN in Fixed-H */ && !nFlags && + !(pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->t_parity) && + !(pInChI->StereoIsotopic && pInChI->StereoIsotopic->t_parity) && + s[iINChI][jAlt][0]==0 && s[iINChI][j][0]==0 && s[iINChI][j][1] == 0 && + /* /sN, N=2 or 3 only in Mobile-H isotopic */ + (s[iINChI][jAlt][1] & ((INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO))) ) + { + /* create two zero/NULL-initialized isotopic stereo if they do not exist */ + if ( !pInChI->StereoIsotopic && 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3_M, 1, -1)) + /* -- the following will be created later, in TAUT_YES part of the code -- */ + /*|| !pAltInChI->StereoIsotopic && 0 > (ret2 = CopySegment( pAltInChI, pAltInChI, CPY_SP3_M, 1, -1))*/ ) { + goto exit_function; + } + /* same value = 2 for MI and FI; here we assign only FI */ + pInChI->StereoIsotopic->nCompInv2Abs = 2; + pInChI->Stereo->nCompInv2Abs = 0; + /* -- the following will be assigned later, in TAUT_YES part of the code -- */ + /* + pAltInChI->StereoIsotopic->nCompInv2Abs = 2; + pAltInChI->Stereo->nCompInv2Abs = 0; + */ + } else + pInChI->Stereo->nCompInv2Abs = 0; + if ( !(pInChI->nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { + pInChI->nFlags |= ((nFlagsAlt|nFlags) & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)); + } + } + } else + /* copy sp3 inversion info: non-isotopic Mobile H => non-isotopic Fixed H */ + if ( pAltInChI->Stereo && pAltInChI->Stereo->nNumberOfStereoCenters && + pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters && + pAltInChI->Stereo->nCompInv2Abs && + (!pInChI->Stereo->nCompInv2Abs || NO_VALUE_INT == pInChI->Stereo->nCompInv2Abs) ) { + if ( !(nFlagsAlt && !nFlags ) || NO_VALUE_INT == pInChI->Stereo->nCompInv2Abs ) { + /* ??? */ + pInChI->Stereo->nCompInv2Abs = pAltInChI->Stereo->nCompInv2Abs; + } + } + + /* use same rule to copy stereobonds */ + if ( pAltInChI->Stereo && pAltInChI->Stereo->nNumberOfStereoBonds && + (!pInChI->Stereo || !pInChI->Stereo->b_parity) ) { + if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP2, 0, 0)) ) { + goto exit_function; + } + } + /* (3) isotopic Mobile H -> isotopic Fixed H */ + /* if !FH_Stereo && !MH_Stereo && MH_IsoStereo!=NULL && FH_IsoStereo==NULL */ + if ( bIso ) { + if ( !(pInChI->Stereo && pInChI->Stereo->t_parity) && /* !FH_Stereo */ + !(pAltInChI->Stereo && pAltInChI->Stereo->t_parity) && /* !MH_Stereo */ + (pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->nNumberOfStereoCenters) && /* MH_IsoStereo */ + (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->t_parity) ) { /* !FH_IsoStereo */ + /* copy sp3 iso stereo MI->FI (/t) and, if FH nCompInv2Abs (/m) is missing, copy it, too, MI->FI */ + if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3, 1, 1)) || + (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) && + 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3_M, 1, 1)) ) { + goto exit_function; + } + /* in case of missing nCompInv2Abs, Relative or Racemic stereo 2005-05-10 */ + if ( pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT && + (nFlagsAlt & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { + pInChI->StereoIsotopic->nCompInv2Abs = s[iINChI][jAlt][1]>0? 2 : 0; + if ( !(pInChI->nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { + pInChI->nFlags |= (nFlagsAlt & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)); + } + } + } else + /* copy sp3 inversion info only: isotopic Mobile H -> isotopic Fixed H */ + if ( !(pInChI->Stereo && pInChI->Stereo->t_parity) && /* !FH_Stereo /t */ + !(pAltInChI->Stereo && pAltInChI->Stereo->t_parity) && /* !MH_Stereo /t */ + (pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->nNumberOfStereoCenters) && /* MH_IsoStereo /t */ + (pInChI->StereoIsotopic && pInChI->StereoIsotopic->nNumberOfStereoCenters) && /* FH_IsoStereo /t */ + pAltInChI->StereoIsotopic->nCompInv2Abs && /* MH_IsoStereo /m */ + (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) ) { /* !FH_IsoStereo /m */ + /* added 02-09-2006 */ + if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3_M, 1, 1)) ) { + goto exit_function; + } + } + /* use same rule to copy stereobonds */ + if ( !(pInChI->Stereo && pInChI->Stereo->b_parity) && + !(pAltInChI->Stereo && pAltInChI->Stereo->b_parity) && + (pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->nNumberOfStereoBonds) && + (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->b_parity) ) { + if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP2, 1, 1)) ) { + goto exit_function; + } + } + + /* (4) Copy Fixed-H -> isotopic Fixed-H */ + /* if FH_Stereo && !MH_IsoStereo && && !FH_IsoStereo */ + if ( (pInChI->Stereo && pInChI->Stereo->nNumberOfStereoCenters) && /* FH_Stereo /t */ + !(pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->t_parity) && /* !MH_IsoStereo /t */ + !(pInChI->StereoIsotopic && pInChI->StereoIsotopic->t_parity) ) { /* !FH_IsoStereo /t */ + + /* added 10-10-2007 DT: copy MH_Iso /m => FH_Iso /m to fix i2i bug for Abs stereo */ + /* InChI string contains: MH(/t...), MH_Iso(/mN, no /t), FH(no /t /m), FH_Iso(no /t /m) */ + if ( pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->nCompInv2Abs && /* MH_IsoStereo /m */ + bNo_InChI_t && + NO_VALUE_INT != pAltInChI->StereoIsotopic->nCompInv2Abs && /* undef FH_IsoStereo /m */ + !(pInChI->StereoIsotopic && NO_VALUE_INT != pInChI->StereoIsotopic->nCompInv2Abs)) { + if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP3_M, 1, 1))) { + goto exit_function; + } + } + + /* added 05-09-2006: copy sp3 FH=>FH_Iso */ + if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3, 1, 0)) || + (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) && + 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3_M, 1, 0)) ) { + goto exit_function; + } + /* in case of missing nCompInv2Abs, Relative or Racemic stereo, /sN in Fixed-H, 2005-05-10 */ + if ( pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT && + (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { + if ( s[iINChI][j][0] > 0 && s[iINChI][j][1] > 0 ) { + /* suppose once in a while only non-taut stereo changes if inverted */ + pInChI->StereoIsotopic->nCompInv2Abs = 2; + pInChI->Stereo->nCompInv2Abs = (++nFH2iFH_AltInv)%2? 2:0; + } else + if ( (s[iINChI][j][0] > 0 || s[iINChI][j][1] > 0) && s[iINChI][j][1] >= 0 ) /* ??? != NO_VALUE_INT ??? */ + pInChI->StereoIsotopic->nCompInv2Abs = 2; + else + pInChI->StereoIsotopic->nCompInv2Abs = 0; + if ( !(pInChI->nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)) ) { + pInChI->nFlags |= (nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO)); + } + } + } else + /* copy sp3 inversion info only: Fixed-H -> isotopic Fixed H */ + if ( (pInChI->Stereo && pInChI->Stereo->t_parity) && + !(pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->t_parity) && + (pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->nNumberOfStereoCenters) && + (pInChI->StereoIsotopic && pInChI->StereoIsotopic->nNumberOfStereoCenters) && + pInChI->Stereo->nCompInv2Abs && + (!pInChI->StereoIsotopic->nCompInv2Abs || NO_VALUE_INT == pInChI->StereoIsotopic->nCompInv2Abs) ) { + /* added 05-09-2006 */ + if ( 0 > (ret2 = CopySegment( pInChI, pInChI, CPY_SP3_M, 1, 0)) ) { + goto exit_function; + } + } + } + if ( bIso && + !(pInChI->Stereo && pInChI->Stereo->nNumberOfStereoBonds) && + !(pAltInChI->Stereo && pAltInChI->Stereo->nNumberOfStereoBonds) && + (pAltInChI->StereoIsotopic && pAltInChI->StereoIsotopic->nNumberOfStereoBonds) && + (!pInChI->StereoIsotopic || !pInChI->StereoIsotopic->b_parity) ) { + if ( 0 > (ret2 = CopySegment( pInChI, pAltInChI, CPY_SP2, 1, 1)) ) { + goto exit_function; + } + } + } + } + } /* end of component cycle (k) */ + } /* end of Mobile/Fixed H cycle (j) */ + + /**** replace NO_VALUE_INT with zeroes in all Mobile & Fixed H components ****/ + for ( j = 0; j < TAUT_NUM; j ++ ) { + for ( k = 0; k < nNumComponents[iINChI][j]; k ++ ) { + if ( pInpInChI[iINChI][j] ) { + INChI *pInChI = &pInpInChI[iINChI][j][k]; + if ( pInChI->nTotalCharge == NO_VALUE_INT ) { + pInChI->nTotalCharge = 0; + } + if ( pInChI->Stereo && pInChI->StereoIsotopic && + pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { + if ( pInChI->Stereo->nNumberOfStereoCenters && + pInChI->Stereo->nCompInv2Abs != NO_VALUE_INT ) { + pInChI->StereoIsotopic->nCompInv2Abs = pInChI->Stereo->nCompInv2Abs; + } + } + /* Add special nCompInv2Abs=2 to force /s2 or /s3 in InChI output */ + if ( pInChI->Stereo && pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT ) { + if ( pInChI->nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO) && + pInChI->Stereo->nNumberOfStereoCenters ) { + pInChI->Stereo->nCompInv2Abs = (s[iINChI][j][0]>0 /*|| s[iINChI][j][1]>0*/)? 2 : 0; /* we do not know the real value */ + } else { + pInChI->Stereo->nCompInv2Abs = 0; + } + } + if ( pInChI->StereoIsotopic && pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { + if ( pInChI->nFlags & (INCHI_FLAG_REL_STEREO | INCHI_FLAG_RAC_STEREO) && + pInChI->StereoIsotopic->nNumberOfStereoCenters ) { + pInChI->StereoIsotopic->nCompInv2Abs = s[iINChI][j][1]>0? 2 : 0; /* we do not know the real value */ + } else { + pInChI->StereoIsotopic->nCompInv2Abs = 0; + } + } + /* added 02-07-2006 */ + if ( pInChI->Stereo && pInChI->Stereo->nCompInv2Abs == NO_VALUE_INT || + pInChI->StereoIsotopic && pInChI->StereoIsotopic->nCompInv2Abs == NO_VALUE_INT ) { + ret2 = RI_ERR_PROGR; + goto exit_function; + } + if ( !pInChI->bDeleted && pInChI->nNumberOfAtoms ) { + tot_charge[iINChI][j] += pInChI->nTotalCharge; + for ( m = 0; m < pInChI->nNumberOfAtoms; m ++ ) { + if ( pInChI->nAtom[m] < EL_NUMBER_H || pInChI->nAtom[m] > nElDataLen ) { + ret2 = RI_ERR_PROGR; + goto exit_function; + } + /* all atoms except H */ + if ( pInChI->nAtom[m] > EL_NUMBER_H ) { + num_elem[iINChI][j][pInChI->nAtom[m]].num ++; + } + } + if ( 0 > (ret2 = GetInChINumH( pInChI, &m ) ) ) { + goto exit_function; + } + num_elem[iINChI][j][EL_NUMBER_H].num += m; + } + } + } + } + + + for ( j = 0; j < TAUT_NUM; j ++ ) { + for ( k = 0; k < nNumComponents[iINChI][j]; k ++ ) + { + if ( pInpInChI[iINChI][j] ) + { + INChI *pInChI = &pInpInChI[iINChI][j][k]; + if ( pInChI->Stereo && !pInChI->Stereo->nNumberOfStereoCenters ) + { + pInChI->Stereo->nCompInv2Abs = 0; + } + if ( pInChI->StereoIsotopic && !pInChI->StereoIsotopic->nNumberOfStereoCenters ) + { + pInChI->StereoIsotopic->nCompInv2Abs = 0; + } + } + } + } + + +#if ( FIX_I2I_STEREOCONVERSION_BUG3 == 1 ) +/* (2008-04-10) 1=> Fix bug of i2i conversion */ +/* (missed repeating /s in FI after F for multi-component case) */ + if (nNumComponents[iINChI][TAUT_NON]>1) /* if multi-component */ + if ( !s[iINChI][TAUT_YES][0] && !s[iINChI][TAUT_YES][1] )/* if no /s in M, MI */ + if ( (s[iINChI][TAUT_NON][0]>1) && (s[iINChI][TAUT_NON][1]>1) ) /* if /srel/srac in both F, FI */ + if ( s[iINChI][TAUT_NON][0] == s[iINChI][TAUT_NON][1] ) /* if same stereo in F and FI */ + /* we assume that at least one component in F has no actual stereo */ + /* and place deliberately 0 to appropriate place */ + for ( k = 0; k < nNumComponents[iINChI][TAUT_NON]; k ++ ) + { + INChI *pInChI = &pInpInChI[iINChI][TAUT_NON][k]; + if (pInChI->Stereo->nCompInv2Abs!=0) + { + pInChI->Stereo->nCompInv2Abs = 0; + goto fini; + } + } +fini: ; +#endif + + + if ( num_elem[iINChI][TAUT_YES] ) { + tot_charge[iINChI][TAUT_YES] += nNumProtons[iINChI][TAUT_YES].nNumRemovedProtons; + num_elem[iINChI][TAUT_YES][EL_NUMBER_H].num += nNumProtons[iINChI][TAUT_YES].nNumRemovedProtons; + } + + /**** Count H and isotopic H in Mobile and Fixed H represntations of components */ + /* if at least one component has Fixed-H layer then all components have Fixed-H */ + /* layer; those whose Fixed-H layer is empty have Fixed-H layer same as Mobile-H layer */ + if ( nNumComponents[iINChI][TAUT_NON] ) { + /* only if both Mobile and Fixed H exist */ + int nFormulaH[TAUT_NUM], nNumH[TAUT_NUM], nCharge[TAUT_NUM], nNumIsotopicH[TAUT_NUM][NUM_H_ISOTOPES]; + int nRemovedCharge, nRemovedH, nRemovedIsotopicH[NUM_H_ISOTOPES], nFoundRemovedIsoH; + int nTotRemovedProtons, nTotRemovedIsotopicH[NUM_H_ISOTOPES], bExists[TAUT_NUM]; + INChI *pInChI[TAUT_NUM]; + nTotRemovedProtons = 0; + memset( nTotRemovedIsotopicH, 0, sizeof(nTotRemovedIsotopicH) ); + len2 = inchi_max( nNumComponents[iINChI][TAUT_YES], nNumComponents[iINChI][TAUT_NON] ); + + for ( k = 0; k < len2; k ++ ) { + /* k is a component index */ + for ( j = 0; j < TAUT_NUM; j ++ ) { + /* j is 0=TAUT_NON or 1=TAUT_YES */ + pInChI[j] = NULL; /* initialization 2006-03 */ + bExists[j] = (k < nNumComponents[iINChI][j]) && + pInpInChI[iINChI][j][k].nNumberOfAtoms && + !pInpInChI[iINChI][j][k].bDeleted; + } + if ( !bExists[TAUT_NON] ) { + /* TAUT_YES does not exist for a proton (H+) in TAUT_NON */ + ret2 = RI_ERR_SYNTAX; + goto exit_function; + } + /* at this point at least one of Mobile[k] and Fixed[k] real InChI exists */ + /* initialize for counting removed protons and isotopic H from kth Mobile-H component */ + for ( j = 0; j < TAUT_NUM; j ++ ) { + if ( bExists[j] ) { + pInChI[j] = &pInpInChI[iINChI][j][k]; /* BC: reading uninit memory (fixed?) */ + } + nFormulaH[j] = 0; + nNumH[j] = 0; + nCharge[j] = 0; + for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { + nNumIsotopicH[j][m] = 0; + } + } + /* extract number of H, isotopic H, and charge */ + for ( j = 0; j < TAUT_NUM; j ++ ) { + if ( !bExists[j] ) + continue; + if ( 0 > (ret2 = GetInChIFormulaNumH( pInChI[j], &nFormulaH[j] )) || + 0 > (ret2 = GetInChINumH( pInChI[j], &nNumH[j] )) || + 0 > (ret2 = GetInChIIsoH( pInChI[j], nNumIsotopicH[j] )) ) { + goto exit_function; + } + nCharge[j] = pInChI[j]->nTotalCharge; + } + for ( j = 0; j < TAUT_NUM; j ++ ) { + if ( !bExists[j] ) + continue; + if ( nFormulaH[j] != nNumH[j] ) { + ret2 = RI_ERR_SYNTAX; + goto exit_function; + } + } + nFoundRemovedIsoH = 0; + nRemovedCharge = nCharge[TAUT_NON] - nCharge[TAUT_YES]; + nRemovedH = nNumH[TAUT_NON] - nNumH[TAUT_YES]; + for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { + nFoundRemovedIsoH += 0 != (nRemovedIsotopicH[m] = nNumIsotopicH[TAUT_NON][m] - + nNumIsotopicH[TAUT_YES][m] ); + } + if ( nRemovedCharge != nRemovedH ) { + ret2 = RI_ERR_SYNTAX; + goto exit_function; + } + if ( nRemovedCharge || nFoundRemovedIsoH ) { + COMPONENT_REM_PROTONS *pNumProtons; + if ( !nNumProtons[iINChI][TAUT_YES].pNumProtons ) { + /* allocate only if needed */ + nNumProtons[iINChI][TAUT_YES].pNumProtons = + (COMPONENT_REM_PROTONS *) inchi_calloc(len2, + sizeof(nNumProtons[0][0].pNumProtons[0])); + if ( !nNumProtons[iINChI][TAUT_YES].pNumProtons ) { + ret2 = RI_ERR_ALLOC; + goto exit_function; + } + } + pNumProtons = nNumProtons[iINChI][TAUT_YES].pNumProtons+k; + pNumProtons->nNumRemovedProtons = nRemovedH; + nTotRemovedProtons += nRemovedH; + for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { + pNumProtons->nNumRemovedIsotopicH[m] = nRemovedIsotopicH[m]; + nTotRemovedIsotopicH[m] += nRemovedIsotopicH[m]; + } + /* make sure the Mobile-H InChI has nTautomer */ + if ( pInChI[TAUT_YES] && bExists[TAUT_YES] ) { + if ( !pInChI[TAUT_YES]->lenTautomer ) { + pInChI[TAUT_YES]->lenTautomer = 1; + } + if ( !pInChI[TAUT_YES]->nTautomer ) { + pInChI[TAUT_YES]->nTautomer = (AT_NUMB *)inchi_calloc(pInChI[TAUT_YES]->lenTautomer, sizeof(pInChI[0]->nTautomer[0]) ); + } + } + } + } + if ( nNumProtons[iINChI][TAUT_YES].pNumProtons ) { + /* check consistency */ +#if ( FIX_ISO_FIXEDH_BUG_READ == 1 ) + int iso_diff[NUM_H_ISOTOPES], iso_diff_tot=0; +#endif + if ( nTotRemovedProtons != nNumProtons[iINChI][TAUT_YES].nNumRemovedProtons ) { + ret2 = RI_ERR_SYNTAX; + goto exit_function; + } +#if ( FIX_ISO_FIXEDH_BUG_READ == 1 ) + for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { + iso_diff[m] = nNumProtons[iINChI][TAUT_YES].nNumRemovedIsotopicH[m]-nTotRemovedIsotopicH[m]; + if ( iso_diff[m] < 0 ) + { + ret2 = RI_ERR_SYNTAX; + goto exit_function; + } else { + /* InChI-1.02b bug: nTotRemovedIsotopicH[m] < nNumProtons[iINChI][TAUT_YES].nNumRemovedIsotopicH[m] */ + /* in non-tautomeric components where D(+) or T(+) was removed from -NH(+)= or =OH(+) */ + iso_diff_tot += iso_diff[m]; + } + } + if ( iso_diff_tot ) { + if ( 0 > bIsoMayBeArranged( bInchi2Struct, iso_diff, nNumProtons, pInpInChI, nNumComponents, iINChI )) { + ret2 = RI_ERR_SYNTAX; + goto exit_function; + } + } +#else + for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { + if ( nTotRemovedIsotopicH[m] != nNumProtons[iINChI][TAUT_YES].nNumRemovedIsotopicH[m] ) { + ret2 = RI_ERR_SYNTAX; + goto exit_function; + } + } +#endif + } + } + + /* make Mobile H and Fixed H InChI arrays have same length */ + len2 = len1 = 0; + if ( nNumComponents[iINChI][TAUT_YES] < nNumComponents[iINChI][TAUT_NON] ) { + j = TAUT_YES; /* less components in Mobile-H layer */ + len2 = nNumComponents[iINChI][TAUT_NON]; + len1 = nNumComponents[iINChI][TAUT_YES]; + } else + if ( nNumComponents[iINChI][TAUT_YES] > nNumComponents[iINChI][TAUT_NON] ) { + j = TAUT_NON; /* less components in Fixed-H layer */ + len2 = nNumComponents[iINChI][TAUT_YES]; + len1 = nNumComponents[iINChI][TAUT_NON]; + } + /* always len1 <= len2; if Mobile-H and Fixed-H have same number of components then len1=len2=0 */ + if ( len2 && len1 ) { + INChI *pInChI = (INChI *)inchi_calloc( len2, sizeof( pInChI[0] ) ); + if ( !pInChI ) { + ret2 = RI_ERR_ALLOC; + goto exit_function; + } + memcpy( pInChI, pInpInChI[iINChI][j], len1 * sizeof( pInChI[0] ) ); + inchi_free( pInpInChI[iINChI][j] ); + pInpInChI[iINChI][j] = pInChI; + nNumComponents[iINChI][j] = len2; + for ( ; len1 < len2; len1 ++ ) { + if ( j == TAUT_YES ) { + /* mark added to Mobile H layer components as deleted protons */ + if ( 0 > (ret2 = nFillOutProtonMobileH( pInpInChI[iINChI][j]+len1 ) ) ) { + goto exit_function; + } + if ( 0 > (ret2 = nProtonCopyIsotopicInfo( pInpInChI[iINChI][j]+len1/* to */, + pInpInChI[iINChI][TAUT_NON]+len1/* from */ ) ) ) { + goto exit_function; + } + } else { + /* mark added to Fixed H layer components as empty deleted */ + /* this should not happen */ + pInChI[len1].bDeleted = 1; + } + } + } + } /* end of iINChI cycle */ + /* check balances */ + for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) { + for ( i = iINChI; i < INCHI_NUM; i ++ ) { + for ( j = 0; j < TAUT_NUM; j ++ ) { + for ( k = j; k < TAUT_NUM; k ++ ) { + if ( (iINChI!=i || j !=k) && num_elem[iINChI][j] && num_elem[i][k] ) { + if ( tot_charge[iINChI][j] != tot_charge[i][k] ) { + ret2 = RI_ERR_SYNTAX; + goto exit_function; + } + for ( m = 0; m <= nElDataLen; m ++ ) { + if ( num_elem[iINChI][j][m].num != num_elem[i][k][m].num ) { + ret2 = RI_ERR_SYNTAX; + goto exit_function; + } + } + /* + if ( memcmp( num_elem[iINChI], num_elem[i][k], (nElDataLen+1)*sizeof(num_elem[0][0][0]) ) { + ret2 = RI_ERR_SYNTAX; + goto exit_function; + } + */ + } + } + } + } + } + } else { + ret2 = ret; + } +exit_function: + for ( i = 0; i < INCHI_NUM; i ++ ) { + for ( j = 0; j < TAUT_NUM; j ++ ) { + if ( num_elem[i][j] ) { + inchi_free( num_elem[i][j] ); + num_elem[i][j] = NULL; + } + } + } + *nErr = (ret2 < 0 && ret2 != RI_ERR_EOL)? ret2 : 0; + return ret; +} + + +/**************************************************************************************/ +#if ( FIX_ISO_FIXEDH_BUG_READ == 1 ) +#undef TAUT_YES +int bIsoMayBeArranged( int bInchi2Struct, int iso_diff[NUM_H_ISOTOPES], REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM], + INChI *pInpInChI[INCHI_NUM][TAUT_NUM], int nNumComponents[INCHI_NUM][TAUT_NUM], int iINChI ) +{ +const int TAUT_YES = 1; + int i, k, m, n_found=0, n_found_at_in_component, n_found_H_in_component, i_iso_at, num_iso_H=0, num_iso_H_orig, num_add_iso_H, orig_add_H; + for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { + num_iso_H += iso_diff[m]; + } + num_iso_H_orig = num_iso_H; + for ( k = 0; k < nNumComponents[iINChI][TAUT_YES] && k < nNumComponents[iINChI][TAUT_NON]; k ++ ) { + INChI *pInChI = &pInpInChI[iINChI][TAUT_NON][k]; + INChI *pInChITaut = &pInpInChI[iINChI][TAUT_YES][k]; + if ( pInChITaut->bDeleted || pInChI->bDeleted || + pInChITaut->nNumberOfIsotopicAtoms > 0 || + pInChITaut->lenTautomer > 1 && pInChITaut->nTautomer && pInChITaut->nTautomer[0] > 0 || + NULL == nNumProtons[iINChI][TAUT_YES].pNumProtons || + nNumProtons[iINChI][TAUT_YES].pNumProtons[k].nNumRemovedProtons <= 0 || + pInChI->nNumberOfIsotopicAtoms > 0 || + nNumProtons[iINChI][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH[0] || + nNumProtons[iINChI][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH[1] || + nNumProtons[iINChI][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH[2] + ) { + continue; + } + /* check if fixed-H has isotopic H; count the possibilities */ + orig_add_H = nNumProtons[iINChI][TAUT_YES].pNumProtons[k].nNumRemovedProtons; + n_found_at_in_component = 0; /* number of atoms that may accept isotopic H */ + n_found_H_in_component = 0; + for ( i = 0; i < pInChI->nNumberOfAtoms; i ++ ) { + int nNumRemovedH = (int)pInChI->nNum_H[i] - (int)pInChITaut->nNum_H[i]; + if ( nNumRemovedH > 0) { + n_found_at_in_component ++; + n_found_H_in_component += nNumRemovedH; + } + } + if ( n_found_at_in_component > 0 && num_iso_H > 0 && bInchi2Struct ) { + pInChI->IsotopicAtom = (INChI_IsotopicAtom *)calloc(inchi_min(n_found_at_in_component, num_iso_H), sizeof(pInChI->IsotopicAtom[0])); + } + for ( i = 0, i_iso_at = 0; i < pInChI->nNumberOfAtoms; i ++ ) { + int nNumRemovedH = (int)pInChI->nNum_H[i] - (int)pInChITaut->nNum_H[i]; + n_found += nNumRemovedH; /* found H removed in mobile-H layer */ + if ( nNumRemovedH > 0 && num_iso_H > 0 && orig_add_H ) { + for ( m = 0; m < NUM_H_ISOTOPES && 0 < num_iso_H && 0 < orig_add_H && 0 < nNumRemovedH; m ++ ) { + if ( iso_diff[m] > 0 ) { + num_add_iso_H = inchi_min( iso_diff[m], nNumRemovedH ); /* atom limit */ + if ( num_add_iso_H > orig_add_H ) /* component limit */ + num_add_iso_H = orig_add_H; + iso_diff[m] -= num_add_iso_H; /* update tot removed single isotope H limit */ + num_iso_H -= num_add_iso_H; /* update tot removed isotopic H limit */ + orig_add_H -= num_add_iso_H; /* update component limit */ + nNumRemovedH -= num_add_iso_H; /* update atom limit */ + nNumProtons[iINChI][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH[m] += num_add_iso_H; + if ( pInChI->IsotopicAtom ) { + pInChI->IsotopicAtom[i_iso_at].nAtomNumber = i+1; + switch( m ) { + case 0: + pInChI->IsotopicAtom[i_iso_at].nNum_H += num_add_iso_H; + break; + case 1: + pInChI->IsotopicAtom[i_iso_at].nNum_D += num_add_iso_H; + break; + case 2: + pInChI->IsotopicAtom[i_iso_at].nNum_T += num_add_iso_H; + break; + } + } + } + } + if ( pInChI->IsotopicAtom ) { + i_iso_at ++; + } + } + } + if ( pInChI->IsotopicAtom && i_iso_at ) { + pInChI->nNumberOfIsotopicAtoms = i_iso_at; + } + } + if ( n_found - num_iso_H >= 0 ) { + /* Success. Arrange isotopic H between components */ + } + + return n_found - num_iso_H_orig; /* >0 => ambiguous reconstruction, 0 => unambiguous, <0 => impossible */ +} +#define TAUT_YES 1 +#endif + + + +/****************************************************************************/ +typedef enum tagAuxInfoState { + AST_VERSION, /* 0 */ + + AST_MOBILE_H_NUMBERS, /* 1 /N: */ + AST_MOBILE_H_ATOM_EQ, /* 2 /E: */ + AST_MOBILE_H_GROUP_EQ, /* 3 /gE: */ + AST_MOBILE_H_SP3_INV, /* 4 /it: */ + AST_MOBILE_H_SP3_INV_NUMBERS, /* 5 /iN: */ + + AST_MOBILE_H_ISO_LAYER_FORK, /* 6 */ + + AST_MOBILE_H_ISO_NUMBERS, /* 7 /I: */ + AST_MOBILE_H_ISO_ATOM_EQ, /* 8 /E: */ + AST_MOBILE_H_ISO_GROUP_EQ, /* 9 /gE: */ + AST_MOBILE_H_ISO_SP3_INV, /* 10 /it: */ + AST_MOBILE_H_ISO_SP3_INV_NUMBERS, /* 11 /iN: */ + + AST_FIXED_H_LAYER_FORK, /* 12 */ + + AST_FIXED_H_NUMBERS, /* 13 /F: */ + AST_FIXED_H_ATOM_EQ, /* 14 /E: */ + AST_FIXED_H_SP3_INV, /* 15 /it: */ + AST_FIXED_H_SP3_INV_NUMBERS, /* 16 /iN: */ + + AST_FIXED_H_ISO_LAYER_FORK, /* 17 */ + + AST_FIXED_H_ISO_NUMBERS, /* 18 /I: */ + AST_FIXED_H_ISO_ATOM_EQ, /* 19 /E: */ + AST_FIXED_H_ISO_SP3_INV, /* 20 /it: */ + AST_FIXED_H_ISO_SP3_INV_NUMBERS, /* 21 /iN: */ + + AST_REVERSE_INFO_CRV, /* 22 /CRV: */ + AST_REVERSE_INFO_ATOMS, /* 23 /rA: */ + AST_REVERSE_INFO_BONDS, /* 24 /rB: */ + AST_REVERSE_INFO_XYZ, /* 25 /rC: */ + + AST_RECONNECTED_LAYER_FORK, /* 26 /R: */ + AST_RECONNECTED_LAYER_NUMBERS /* 27 */ +}AUX_INFO_STATE; + + +/* + ParseAuxSegmentVersion +*/ +int ParseAuxSegmentVersion( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) +{ + const char *q; + if ( isdigit( UCINT *str ) && (inchi_strtol( str, &q, 10), !*q) ) { + return 1; + } + return RI_ERR_SYNTAX; +} + + +/* + CopyAtomNumbers +*/ +int CopyAtomNumbers( INChI *pInChI_To, int bIsoTo, INChI *pInChI_From, int bIsoFrom ) +{ + AT_NUMB *pTo, *pFrom; + if ( !pInChI_To || !pInChI_From || pInChI_To->bDeleted || pInChI_From->bDeleted || + !pInChI_To->nNumberOfAtoms || !pInChI_From->nNumberOfAtoms || + pInChI_To->nNumberOfAtoms != pInChI_From->nNumberOfAtoms || + !pInChI_From->nPossibleLocationsOfIsotopicH ) { + return RI_ERR_PROGR; + } + if ( !pInChI_To->nPossibleLocationsOfIsotopicH ) { + pInChI_To->nPossibleLocationsOfIsotopicH = (AT_NUMB *)inchi_calloc( 2*pInChI_To->nNumberOfAtoms, + sizeof(pInChI_To->nPossibleLocationsOfIsotopicH[0])); + if ( !pInChI_To->nPossibleLocationsOfIsotopicH ) { + return RI_ERR_ALLOC; + } + } + pTo = pInChI_To->nPossibleLocationsOfIsotopicH + (bIsoTo? 0 : pInChI_To->nNumberOfAtoms ); + pFrom = pInChI_From->nPossibleLocationsOfIsotopicH + (bIsoFrom? 0 : pInChI_To->nNumberOfAtoms ); + if ( pTo == pFrom ) { + return RI_ERR_PROGR; + } + memcpy( pTo, pFrom, pInChI_To->nNumberOfAtoms*sizeof(pTo[0]) ); + return 1; +} + + +/* + ParseAuxSegmentNumbers +*/ +int ParseAuxSegmentNumbers( const char *str, + int bMobileH, + INChI *pInpInChI[], + int ppnNumComponents[], + int state, + int *pbAbc ) +{ + int bIso = 0, iComponent = 0, nNumComponents, bIso_From=0, bAltInChIExists; + INChI *pInChI = NULL, *pAltInChI = NULL, *pInChI_From = NULL; + const char *p, *q, *pStart, *pEnd, *t; + static const char mult_type[] = "mnM"; + int val, ret, k, mpy_component, num; + AT_NUMB *pNumb; + int base = 10; + /* save isotopic numbering into the first nNumberOfAtoms elements of INChI::nPossibleLocationsOfIsotopicH */ + /* save non-isotopic numbering into the second half of nNumberOfAtoms elements of INChI::nPossibleLocationsOfIsotopicH */ + + switch( state ) { + case AST_MOBILE_H_NUMBERS: + if ( bMobileH != TAUT_YES ) + return RI_ERR_PROGR; + if ( memcmp( str, "N:", 2 ) ) + return 0; + break; + case AST_FIXED_H_NUMBERS: + if ( bMobileH != TAUT_NON ) + return RI_ERR_PROGR; + if ( memcmp( str, "F:", 2 ) ) + return 0; + break; + case AST_MOBILE_H_ISO_NUMBERS: + if ( bMobileH != TAUT_YES ) + return RI_ERR_PROGR; + if ( memcmp( str, "I:", 2 ) ) + return 0; + bIso = 1; + break; + case AST_FIXED_H_ISO_NUMBERS: + if ( bMobileH != TAUT_NON ) + return RI_ERR_PROGR; + if ( memcmp( str, "I:", 2 ) ) + return 0; + bIso = 1; + break; + default: + return RI_ERR_PROGR; + } + pStart = str+2; + if ( !*pStart ) { + return 1; + } + iComponent = 0; + nNumComponents = ppnNumComponents[bMobileH]; + + bAltInChIExists = (NULL != pInpInChI[ALT_TAUT(bMobileH)]); + while( 1 ) { + /* cycle over components */ + if ( !(pEnd = strchr( pStart, ';' )) ) { + pEnd = pStart + strlen(pStart); + } + /* check */ + if ( !pInpInChI[bMobileH] ) { + return 1; /* invalid aux info */ + } + pInChI = pInpInChI[bMobileH] + iComponent; + pAltInChI = pInpInChI[ALT_TAUT(bMobileH)] + iComponent; + if ( (isdigit(UCINT *pStart) && + 0 < (val = (int)inchi_strtol( pStart, &q, 10)) || + (q = pStart, val=1) )&& + (t=strchr(mult_type, *q)) && q+1 == pEnd ) { + + /* process the abbreviation */ + + pInChI_From = NULL; + + switch( bMobileH ) { + + case TAUT_YES: + switch ( bIso ) { + case 0: + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + case 1: + if ( *q != 'm' ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + /* isotopic Mobile-H <-- non-isotopic Mobile H */ + pInChI_From = pInChI; + bIso_From = 0; + break; + default: + ret = RI_ERR_PROGR; + goto exit_function; + } + break; + + case TAUT_NON: + switch ( *q ) { + case 'm': /* same as mobile H */ + switch( bIso ) { + case 0: /* from Mobile-H not isotopic */ + pInChI_From = bAltInChIExists? pAltInChI:NULL; + bIso_From = 0; + break; + + case 1: + pInChI_From = bAltInChIExists? pAltInChI:NULL;; + bIso_From = 1; + break; + default: + ret = RI_ERR_PROGR; + goto exit_function; + } + break; + case 'n': /* same as non-isotopic Fixed-H */ + switch ( bIso ) { + case 0: + ret = 1; /*RI_ERR_SYNTAX;*/ + goto exit_function; + case 1: + pInChI_From = pInChI; + bIso_From = 0; + default: + ret = RI_ERR_PROGR; + goto exit_function; + } + break; + case 'M': /* same as isotopic Mobile-H */ + switch ( bIso ) { + case 0: + ret = RI_ERR_SYNTAX; + goto exit_function; + case 1: + pInChI_From = bAltInChIExists? pAltInChI:NULL;; + bIso_From = 1; + break; + default: + ret = RI_ERR_PROGR; + goto exit_function; + } + break; + default: + ret = 1; /*RI_ERR_SYNTAX;*/ + goto exit_function; + } + break; + } + /* copy */ + if ( pInChI_From ) { + for ( k = 0; k < val; k ++ ) { + CopyAtomNumbers( pInChI+k, bIso, pInChI_From+k, bIso_From ); + } + } + mpy_component = val; + } else { + mpy_component = 1; + p = pStart; + pNumb = pInChI->nPossibleLocationsOfIsotopicH; + if ( !pNumb ) { + pNumb = (AT_NUMB *)inchi_calloc( 2*pInChI->nNumberOfAtoms, sizeof(pNumb[0] ) ); + if ( !pNumb ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + pInChI->nPossibleLocationsOfIsotopicH = pNumb; + } + pNumb += bIso? 0 : pInChI->nNumberOfAtoms; + if ( pStart < pEnd && *pbAbc == -1 ) { + /* check if compressed InChI */ + *pbAbc = isupper( UCINT *pStart)? 1 : 0; + } + base = (*pbAbc==1)? ALPHA_BASE : 10; + + if ( *pbAbc == 1 ) { + for ( k = 0, p = pStart; k < pInChI->nNumberOfAtoms && p < pEnd; k ++, p ++ ) { + num = (AT_NUMB)inchi_strtol( p, &q, base ); + if ( num <= 0 || p == q ) { + ret = RI_ERR_SYNTAX; + goto exit_function; + } + pNumb[k] = (AT_NUMB)num; + p = q; + if ( p == pEnd ) { + break; /* main end of cycle */ + } + } + } else { + for ( k = 0, p = pStart; k < pInChI->nNumberOfAtoms && p < pEnd; k ++, p ++ ) { + pNumb[k] = (AT_NUMB)inchi_strtol( p, &q, 10 ); + p = q; + if ( p == pEnd ) { + break; /* main end of cycle */ + } else + if ( *p != ',' ) { + ret = RI_ERR_SYNTAX; + goto exit_function; + } + } + } + if ( p != pEnd || k+1 != pInChI->nNumberOfAtoms ) { + ret = RI_ERR_SYNTAX; + goto exit_function; + } + } + + iComponent += mpy_component; + if ( *pEnd ) { + pStart = pEnd+1; + continue; + } else { + break; + } + } + + if ( nNumComponents != iComponent ) { + ret = 1; /*RI_ERR_SYNTAX;*/ /* syntax error */ + goto exit_function; + } + ret = iComponent + 1; + +exit_function: + return ret; +} + + +/* + ParseAuxSegmentAtomEqu +*/ +int ParseAuxSegmentAtomEqu( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) +{ + switch( state ) { + case AST_MOBILE_H_ATOM_EQ: + if ( bMobileH != TAUT_YES ) + return RI_ERR_PROGR; + if ( memcmp( str, "E:", 2 ) ) + return 0; + break; + case AST_MOBILE_H_ISO_ATOM_EQ: + if ( bMobileH != TAUT_YES ) + return RI_ERR_PROGR; + if ( memcmp( str, "E:", 2 ) ) + return 0; + break; + case AST_FIXED_H_ATOM_EQ: + if ( bMobileH != TAUT_NON ) + return RI_ERR_PROGR; + if ( memcmp( str, "E:", 2 ) ) + return 0; + break; + case AST_FIXED_H_ISO_ATOM_EQ: + if ( bMobileH != TAUT_NON ) + return RI_ERR_PROGR; + if ( memcmp( str, "E:", 2 ) ) + return 0; + break; + default: + return RI_ERR_PROGR; + } + return 1; +} + + +/* + ParseAuxSegmentGroupEqu +*/ +int ParseAuxSegmentGroupEqu( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) +{ + switch( state ) { + case AST_MOBILE_H_GROUP_EQ: + if ( bMobileH != TAUT_YES ) + return RI_ERR_PROGR; + if ( memcmp( str, "gE:", 3 ) ) + return 0; + break; + case AST_MOBILE_H_ISO_GROUP_EQ: + if ( bMobileH != TAUT_YES ) + return RI_ERR_PROGR; + if ( memcmp( str, "gE:", 3 ) ) + return 0; + break; + default: + return RI_ERR_PROGR; + } + return 1; +} + + +/* + ParseAuxSegmentSp3Inv +*/ +int ParseAuxSegmentSp3Inv( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) +{ + switch( state ) { + case AST_MOBILE_H_SP3_INV: + if ( bMobileH != TAUT_YES ) + return RI_ERR_PROGR; + if ( memcmp( str, "it:", 3 ) ) + return 0; + break; + case AST_MOBILE_H_ISO_SP3_INV: + if ( bMobileH != TAUT_YES ) + return RI_ERR_PROGR; + if ( memcmp( str, "it:", 3 ) ) + return 0; + break; + case AST_FIXED_H_SP3_INV: + if ( bMobileH != TAUT_NON ) + return RI_ERR_PROGR; + if ( memcmp( str, "it:", 3 ) ) + return 0; + break; + case AST_FIXED_H_ISO_SP3_INV: + if ( bMobileH != TAUT_NON ) + return RI_ERR_PROGR; + if ( memcmp( str, "it:", 3 ) ) + return 0; + break; + default: + return RI_ERR_PROGR; + } + return 1; +} + + +/* + ParseAuxSegmentSp3InvNumbers +*/ +int ParseAuxSegmentSp3InvNumbers( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) +{ + switch( state ) { + case AST_MOBILE_H_SP3_INV_NUMBERS: + if ( bMobileH != TAUT_YES ) + return RI_ERR_PROGR; + if ( memcmp( str, "iN:", 3 ) ) + return 0; + break; + case AST_MOBILE_H_ISO_SP3_INV_NUMBERS: + if ( bMobileH != TAUT_YES ) + return RI_ERR_PROGR; + if ( memcmp( str, "iN:", 3 ) ) + return 0; + break; + case AST_FIXED_H_SP3_INV_NUMBERS: + if ( bMobileH != TAUT_NON ) + return RI_ERR_PROGR; + if ( memcmp( str, "iN:", 3 ) ) + return 0; + break; + case AST_FIXED_H_ISO_SP3_INV_NUMBERS: + if ( bMobileH != TAUT_NON ) + return RI_ERR_PROGR; + if ( memcmp( str, "iN:", 3 ) ) + return 0; + break; + default: + return RI_ERR_PROGR; + } + return 1; +} + + +/* + ParseAuxSegmentReverseCRV +*/ +int ParseAuxSegmentReverseCRV( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) +{ + switch( state ) { + case AST_REVERSE_INFO_CRV: + if ( memcmp( str, "CRV:", 4 ) ) + return 0; + break; + default: + return RI_ERR_PROGR; + } + return 1; +} + + +/* + ParseAuxSegmentReverseAtoms +*/ +int ParseAuxSegmentReverseAtoms( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) +{ + switch( state ) { + case AST_REVERSE_INFO_ATOMS: + if ( memcmp( str, "rA:", 3 ) ) + return 0; + break; + default: + return RI_ERR_PROGR; + } + return 1; +} + + +/* + ParseAuxSegmentReverseBonds +*/ +int ParseAuxSegmentReverseBonds( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) +{ + switch( state ) { + case AST_REVERSE_INFO_BONDS: + if ( memcmp( str, "rB:", 3 ) ) + return 0; + break; + default: + return RI_ERR_PROGR; + } + return 1; +} + + +/* + ParseAuxSegmentReverseXYZ +*/ +int ParseAuxSegmentReverseXYZ( const char *str, int bMobileH, XYZ_COORD **ppXYZ, INChI *pInpInChI[], int ppnNumComponents[], int state ) +{ + const char *pStart, *p, *q; + XYZ_COORD *pXYZ = NULL; + int nLenXYZ=0, i, j; + switch( state ) { + case AST_REVERSE_INFO_XYZ: + if ( memcmp( str, "rC:", 3 ) ) + return 0; + break; + default: + return RI_ERR_PROGR; + } + pStart = str+3; + /* count coordinates */ + for ( p = pStart, nLenXYZ = 0; *p; p ++ ) { + nLenXYZ += ( *p == ';' ); + } + if ( !nLenXYZ ) { + return RI_ERR_SYNTAX; + } + if ( NULL == (pXYZ = (XYZ_COORD *)inchi_calloc( nLenXYZ, sizeof(pXYZ[0]) )) ) { + return RI_ERR_ALLOC; + } + for ( p = pStart, i = 0; *p && i < nLenXYZ; p ++, i ++ ) { + for ( j = 0; j < 3; j ++ ) { + pXYZ[i].xyz[j] = inchi_strtod( p, &q ); + p = q + (*q == ',' ); + } + if ( *p != ';' ) { + break; + } + } + if ( i != nLenXYZ || *p ) { + return RI_ERR_SYNTAX; + } + *ppXYZ = pXYZ; + return nLenXYZ+1; +} + + +/* + AddAuxSegmentCoord +*/ +int AddAuxSegmentCoord( int nRet, XYZ_COORD *pXYZ, int nLenXYZ, INChI *pInpInChI[INCHI_NUM][TAUT_NUM], + int nNumComponents[INCHI_NUM][TAUT_NUM] ) +{ + int iINChI, j, k, n, m, numAt[TAUT_NUM], num_at, nNumMissingNumbers = 0, ret = 0; + INChI *pInChI = NULL; + INChI *pAltInChI = NULL; + XYZ_COORD *pxyz; + + /* propagate numberings */ + for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) { + for ( j = TAUT_YES; TAUT_NON <= j; j -- ) { + for ( k = 0; k < nNumComponents[iINChI][j]; k ++ ) { + int jj = ALT_TAUT(j); + pInChI = pInpInChI[iINChI][j] + k; + pAltInChI = (k < nNumComponents[iINChI][jj])? pInpInChI[iINChI][jj] + k : NULL; + numAt[j] = ( !pInChI->bDeleted )? pInChI->nNumberOfAtoms : 0; + numAt[jj] = ( pAltInChI && !pAltInChI->bDeleted )? pAltInChI->nNumberOfAtoms : 0; + switch( j ) { + case TAUT_YES: + if ( !numAt[j] ) { + break; /* component does not exist */ + } + if ( !pInChI->nPossibleLocationsOfIsotopicH ) { + nNumMissingNumbers ++; + break; + } + if ( !pInChI->nPossibleLocationsOfIsotopicH[0] ) { + if ( pInChI->nPossibleLocationsOfIsotopicH[numAt[j]] ) { + /* copy from non-isotopic (2nd half of the at. numbers array) to the isotopic (1st half) */ + ret = CopyAtomNumbers( pInChI, 1, pInChI, 0 ); + if ( ret < 0 ) { + goto exit_function; + } + } else { + inchi_free( pInChI->nPossibleLocationsOfIsotopicH ); + pInChI->nPossibleLocationsOfIsotopicH = NULL; + nNumMissingNumbers ++; + } + } + break; + + case TAUT_NON: + if ( !numAt[j] ) { + break; /* component does not exist */ + } + if ( !pInChI->nPossibleLocationsOfIsotopicH ) { + /* trying to get numbers from Mobile-H component */ + if ( !numAt[jj] || !(pAltInChI->nPossibleLocationsOfIsotopicH) ) { + nNumMissingNumbers ++; + break; + } + if ( pAltInChI->nPossibleLocationsOfIsotopicH[0] ) { + ret = CopyAtomNumbers( pInChI, 1, pAltInChI, 1 ); + if ( ret < 0 ) { + goto exit_function; + } + } else + if ( pAltInChI->nPossibleLocationsOfIsotopicH[numAt[jj]] ) { + ret = CopyAtomNumbers( pInChI, 1, pAltInChI, 0 ); + if ( ret < 0 ) { + goto exit_function; + } + } else { + /* pAltInChI->nPossibleLocationsOfIsotopicH should have */ + /* been deallocated on previous TAUT_YES pass */ + ret = RI_ERR_PROGR; + goto exit_function; + } + } else + if ( !pInChI->nPossibleLocationsOfIsotopicH[0] ) { + if ( pInChI->nPossibleLocationsOfIsotopicH[numAt[j]] ) { + /* copy from non-isotopic to isotopic */ + ret = CopyAtomNumbers( pInChI, 1, pInChI, 0 ); + if ( ret < 0 ) { + goto exit_function; + } + } else { + inchi_free( pInChI->nPossibleLocationsOfIsotopicH ); + pInChI->nPossibleLocationsOfIsotopicH = NULL; + nNumMissingNumbers ++; + } + } + break; + } + } + } + } + /* add coordinates */ + for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) { + for ( j = 0; j < TAUT_NUM; j ++ ) { + for ( k = 0; k < nNumComponents[iINChI][j]; k ++ ) { + pInChI = pInpInChI[iINChI][j] + k; + num_at = ( !pInChI->bDeleted )? pInChI->nNumberOfAtoms : 0; + if ( !num_at ) { + if ( pInChI->nPossibleLocationsOfIsotopicH ) { + inchi_free( pInChI->nPossibleLocationsOfIsotopicH ); + pInChI->nPossibleLocationsOfIsotopicH = NULL; + } + continue; + } + if ( !pInChI->nPossibleLocationsOfIsotopicH ) { + continue; + } + if ( iINChI == INCHI_BAS && num_at == 1 && + pInChI->szHillFormula && !strcmp(pInChI->szHillFormula, "H") && + (int)pInChI->nPossibleLocationsOfIsotopicH[0]-1 >= nLenXYZ ) { + ; /* a single atom H disconnected from a metal atom has no coordinates */ + } else { + /* add atom coordinates */ + pxyz = (XYZ_COORD *)inchi_calloc( num_at, sizeof(pxyz[0])); + if ( !pxyz ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + for ( n = 0; n < num_at; n ++ ) { + m = (int)pInChI->nPossibleLocationsOfIsotopicH[n]-1; + if ( m < 0 || m >= nLenXYZ ) { + inchi_free( pxyz ); + ret = RI_ERR_SYNTAX; + goto exit_function; + } + pxyz[n] = pXYZ[m]; + } + pInChI->IsotopicTGroup = (INChI_IsotopicTGroup *)pxyz; + } + inchi_free( pInChI->nPossibleLocationsOfIsotopicH ); + pInChI->nPossibleLocationsOfIsotopicH = NULL; + } + } + } + ret = nRet; /* normal exit */ + +exit_function: + return ret; +} + + +/* + ReadInChICoord (from AuxInfo if present) +*/ +int ReadInChICoord( INCHI_IOSTREAM *pInp, + SEGM_LINE *pLine, int *pState, + INChI *pInpInChI[INCHI_NUM][TAUT_NUM], + int nNumComponents[INCHI_NUM][TAUT_NUM] ) +{ + int c, fst, ret=RI_ERR_ALLOC; + int bMobileH = TAUT_YES, bReconn = INCHI_BAS; + const char szToken[] = INCHI_TOKEN; + int state=-1, prev_state=-1; + XYZ_COORD *pXYZ = NULL; + int nLenXYZ = 0; + int bAbc = -1; /* initially undefined */ + + *pState = 0; + + INCHI_HEAPCHK + /* Get "InChI=1/" */ + if ( pLine->len ) { + c = pLine->c; + } else { + c = nGetInChISegment( pInp, pLine, szToken ); + } + if ( c == RI_ERR_EOF && !pLine->len && !pLine->str[0] ) { + ret = c; + pLine->len = 0; + goto exit_error; + } + if ( pLine->len == 0 || c != SEG_END && c != RI_ERR_EOF && !INCHI_INP_EOL(c) ) { + *pState = -1; + pLine->len = 0; + ret = RI_ERR_PROGR; + goto exit_error; + } + if ( memcmp(pLine->str, "AuxInfo=", 8) ) { + *pState = -1; + return c; + } + state = AST_VERSION; + ret = 1; /* means read the next segment */ + do { + /* read the next segment up to the '/' */ + INCHI_HEAPCHK + if ( ret < 0 ) { + *pState = prev_state; + break; + } + prev_state = state + (bReconn? IST_HAPPENED_IN_RECMET : 0); + /* prev_state = state;*/ + if ( 0 < ret ) { + /* read next segment */ + if ( c != RI_ERR_EOF && c != SEG_END ) { + /* abnormal reading result; should not happen */ + while ( c != RI_ERR_EOF && !INCHI_INP_EOL(c) ) { + /* bypass to the end of line or file */ + c = getInChIChar(pInp); + } + ret = (c == RI_ERR_EOF)? RI_ERR_EOF : RI_ERR_EOL; /* end of line */ + pLine->len = 0; + pLine->c = ret; + break; + } + if ( c == RI_ERR_EOF ) { + ret = RI_ERR_EOF; /* end of line */ + break; + } + if ( c == SEG_END ) { + c = nGetInChISegment( pInp, pLine, szToken ); + } + if ( c < 0 ) { + goto exit_error; /* error */ + } + if ( !pLine->len ) { + ret = RI_ERR_EOL; /* end of line */ + break; + } + fst = UCINT pLine->str[0]; + } + /* process the seqment */ + switch ( state ) { + case AST_VERSION: + /* Mobile H */ + bMobileH = TAUT_YES; + ret = ParseAuxSegmentVersion( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); + state = AST_MOBILE_H_NUMBERS; + break; + case AST_MOBILE_H_NUMBERS: + ret = ParseAuxSegmentNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); + state = AST_MOBILE_H_ATOM_EQ; + break; + case AST_MOBILE_H_ATOM_EQ: + ret = ParseAuxSegmentAtomEqu( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); + state = AST_MOBILE_H_GROUP_EQ; + break; + case AST_MOBILE_H_GROUP_EQ: + ret = ParseAuxSegmentGroupEqu( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); + state = AST_MOBILE_H_SP3_INV; + break; + case AST_MOBILE_H_SP3_INV: + ret = ParseAuxSegmentSp3Inv( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); + state = AST_MOBILE_H_SP3_INV_NUMBERS; + break; + case AST_MOBILE_H_SP3_INV_NUMBERS: + ret = ParseAuxSegmentSp3InvNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); + state = AST_MOBILE_H_ISO_LAYER_FORK; + break; + case AST_MOBILE_H_ISO_LAYER_FORK: + if ( !memcmp( pLine->str, "I:", 2 ) ) { + state = AST_MOBILE_H_ISO_NUMBERS; + } else + if ( !inchi_memicmp( pLine->str, "F:", 2 ) ) { + state = AST_FIXED_H_NUMBERS; + bMobileH = TAUT_NON; + } else + if ( /*bReconn == INCHI_BAS &&*/ !inchi_memicmp( pLine->str, "CRV:", 4 ) ) { + state = AST_REVERSE_INFO_CRV; + } else + if ( bReconn == INCHI_BAS && !inchi_memicmp( pLine->str, "rA:", 3 ) ) { + state = AST_REVERSE_INFO_ATOMS; + } else + if ( bReconn == INCHI_BAS && !inchi_memicmp( pLine->str, "R:", 3 ) ) { + ret = 1; /* read the next segment */ + state = AST_VERSION; + bMobileH = TAUT_YES; + bReconn = INCHI_REC; + } else { + ret = RI_ERR_SYNTAX; + } + break; + /* Mobile H, isotopic */ + case AST_MOBILE_H_ISO_NUMBERS: + ret = ParseAuxSegmentNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); + state = AST_MOBILE_H_ISO_ATOM_EQ; + break; + case AST_MOBILE_H_ISO_ATOM_EQ: + ret = ParseAuxSegmentAtomEqu( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); + state = AST_MOBILE_H_ISO_GROUP_EQ; + break; + case AST_MOBILE_H_ISO_GROUP_EQ: + ret = ParseAuxSegmentGroupEqu( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); + state = AST_MOBILE_H_ISO_SP3_INV; + break; + case AST_MOBILE_H_ISO_SP3_INV: + ret = ParseAuxSegmentSp3Inv( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); + state = AST_MOBILE_H_ISO_SP3_INV_NUMBERS; + break; + case AST_MOBILE_H_ISO_SP3_INV_NUMBERS: + ret = ParseAuxSegmentSp3InvNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); + state = AST_FIXED_H_LAYER_FORK; + break; + case AST_FIXED_H_LAYER_FORK: + if ( !inchi_memicmp( pLine->str, "F:", 2 ) ) { + state = AST_FIXED_H_NUMBERS; + bMobileH = TAUT_NON; + } else + if ( /*bReconn == INCHI_BAS &&*/ !inchi_memicmp( pLine->str, "CRV:", 4 ) ) { + state = AST_REVERSE_INFO_CRV; + } else + if ( bReconn == INCHI_BAS && !inchi_memicmp( pLine->str, "rA:", 3 ) ) { + state = AST_REVERSE_INFO_ATOMS; + } else + if ( bReconn == INCHI_BAS && !inchi_memicmp( pLine->str, "R:", 3 ) ) { + ret = 1; /* read the next segment */ + state = AST_VERSION; + bMobileH = TAUT_YES; + bReconn = INCHI_REC; + } else { + ret = RI_ERR_SYNTAX; + } + break; + case AST_FIXED_H_NUMBERS: + ret = ParseAuxSegmentNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); + state = AST_FIXED_H_ATOM_EQ; + break; + case AST_FIXED_H_ATOM_EQ: + ret = ParseAuxSegmentAtomEqu( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); + state = AST_FIXED_H_SP3_INV; + break; + case AST_FIXED_H_SP3_INV: + ret = ParseAuxSegmentSp3Inv( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); + state = AST_FIXED_H_SP3_INV_NUMBERS; + break; + case AST_FIXED_H_SP3_INV_NUMBERS: + ret = ParseAuxSegmentSp3InvNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); + state = AST_FIXED_H_ISO_LAYER_FORK; + break; + case AST_FIXED_H_ISO_LAYER_FORK: + if ( !memcmp( pLine->str, "I:", 2 ) ) { + state = AST_FIXED_H_ISO_NUMBERS; + } else + if ( /*bReconn == INCHI_BAS &&*/ !inchi_memicmp( pLine->str, "CRV:", 4 ) ) { + state = AST_REVERSE_INFO_CRV; + } else + if ( bReconn == INCHI_BAS && !inchi_memicmp( pLine->str, "rA:", 3 ) ) { + state = AST_REVERSE_INFO_ATOMS; + } else + if ( bReconn == INCHI_BAS && !inchi_memicmp( pLine->str, "R:", 3 ) ) { + ret = 1; /* read the next segment */ + state = AST_VERSION; + bMobileH = TAUT_YES; + bReconn = INCHI_REC; + } else { + ret = RI_ERR_SYNTAX; + } + break; + case AST_FIXED_H_ISO_NUMBERS: + ret = ParseAuxSegmentNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); + state = AST_FIXED_H_ISO_ATOM_EQ; + break; + case AST_FIXED_H_ISO_ATOM_EQ: + ret = ParseAuxSegmentAtomEqu( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); + state = AST_FIXED_H_SP3_INV; + break; + case AST_FIXED_H_ISO_SP3_INV: + ret = ParseAuxSegmentSp3Inv( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); + state = AST_FIXED_H_ISO_SP3_INV_NUMBERS; + break; + case AST_FIXED_H_ISO_SP3_INV_NUMBERS: + ret = ParseAuxSegmentSp3InvNumbers( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); + state = AST_REVERSE_INFO_CRV; + break; + case AST_REVERSE_INFO_CRV: + ret = ParseAuxSegmentReverseCRV( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); + /* state = (bReconn == INCHI_BAS)? AST_REVERSE_INFO_ATOMS : AST_RECONNECTED_LAYER_FORK;*/ + state = AST_REVERSE_INFO_ATOMS; + break; + case AST_REVERSE_INFO_ATOMS: + ret = ParseAuxSegmentReverseAtoms( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); + state = AST_REVERSE_INFO_BONDS; + break; + case AST_REVERSE_INFO_BONDS: + ret = ParseAuxSegmentReverseBonds( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); + state = AST_REVERSE_INFO_XYZ; + break; + case AST_REVERSE_INFO_XYZ: + ret = ParseAuxSegmentReverseXYZ( pLine->str, bMobileH, &pXYZ, pInpInChI[bReconn], nNumComponents[bReconn], state ); + state = AST_RECONNECTED_LAYER_FORK; + if ( ret > 0 ) { + nLenXYZ = ret - 1; + } + break; + case AST_RECONNECTED_LAYER_FORK: + if ( bReconn == INCHI_BAS && !inchi_memicmp( pLine->str, "R:", 3 ) ) { + ret = 1; /* read the next segment */ + state = AST_VERSION; + bMobileH = TAUT_YES; + bReconn = INCHI_REC; + } else { + ret = RI_ERR_SYNTAX; + } + break; + } + } while( c >= 0 ); + + ret = AddAuxSegmentCoord( ret, pXYZ, nLenXYZ, pInpInChI, nNumComponents ); + +exit_error: + if ( pXYZ ) { + inchi_free( pXYZ ); + } + if ( ret >= 0 || c == RI_ERR_EOF || c == RI_ERR_EOL ) { + pLine->len = 0; + } + + return ret; +} + + +/* + Read a single InChI input line + +*/ +int ReadInChILine( INCHI_IOSTREAM *pInp, + SEGM_LINE *pLine, + char **pStr, + int *pState, + INChI *pInpInChI[INCHI_NUM][TAUT_NUM], + int nNumComponents[INCHI_NUM][TAUT_NUM], + REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM], + int s[INCHI_NUM][TAUT_NUM][2], + int *bStdFormat, + int *input_has_save_opt, + unsigned char *input_save_opt_bits, + int bInchi2Struct, + OrigAtDataPolymer **ppPolymer, + OrigAtDataV3000 **ppV3000 ) +{ + int c, fst, ret=RI_ERR_ALLOC, len; + int bMobileH = TAUT_YES, bReconn = INCHI_BAS; + const char szToken[] = INCHI_TOKEN; + char *p; + int state=-1, prev_state=-1; + int bAbc = -1; /* -1=> undefined, 0=> decimal, 1=> abc (compressed) */ + + const int len_std_prefix=8; + size_t k=0; + unsigned char let1=0, let2=0; + const char a2p[]="ABCDEFGHIJKLMNOP"; + + int na_total = 0; /* whole struct, without explH */ + int nb_total = 0; /* whole struct, without explH */ + + /* memset( pLine, 0, sizeof( pLine[0] ) ); */ + *pState = 0; + +next_line: + INCHI_HEAPCHK + /* Got "InChI=1/" */ + if ( pLine->len ) + { + c = pLine->c; + } + else + { + INCHI_HEAPCHK + c = nGetInChISegment( pInp, pLine, szToken ); + INCHI_HEAPCHK + } + if ( c == RI_ERR_EOF && !pLine->len && !pLine->str[0] ) + { + ret = c; + goto exit_function; + } + INCHI_HEAPCHK + + if ( pLine->len == 0 || c != SEG_END && c != RI_ERR_EOF || !(p = strstr(pLine->str, "InChI=1")) ) + { + if ( pLine->str && pLine->str == strstr ( pLine->str, "Structure" ) ) + { + if ( *pStr ) + { + INCHI_HEAPCHK + inchi_free( *pStr ); + } + *pStr = pLine->str; + /* bypass to the end of the 'Structure nnn' line */ + memset( pLine, 0, sizeof( pLine[0] ) ); + while ( c && !INCHI_INP_EOL(c) ) + { + c = getInChIChar(pInp); + } + goto next_line; + } + /* bypass to the end of unrecognized line */ + while ( c != RI_ERR_EOF && !INCHI_INP_EOL(c) ) + { + c = getInChIChar(pInp); + } + pLine->len = 0; + INCHI_HEAPCHK + goto next_line; + } + + + /* Check if got a standard InChI */ + if ( ( pLine->len == len_std_prefix ) && (pLine->str[len_std_prefix-1]=='S') ) + *bStdFormat=1; + else + *bStdFormat=0; + + + state=IST_MOBILE_H_FORMULA; + ret = 1; /* means read the next segment */ + do { + /* read the next segment up to the '/' */ + INCHI_HEAPCHK + if ( ret < 0 ) + { + *pState = prev_state; + break; + } + prev_state = state + (bReconn? IST_HAPPENED_IN_RECMET : 0); + if ( 0 < ret ) + { + /* read next segment */ + if ( c != RI_ERR_EOF && c != SEG_END ) + { + /* abnormal reading result; should not happen */ + /* unless we got backslash-SaveOpt */ + if ( c=='\\' ) + { + /* May be SaveOpt */ + *input_has_save_opt = 1; + } + k = 0; + while ( c != RI_ERR_EOF && !INCHI_INP_EOL(c) ) + { + /* bypass to the end of line or file */ + c = getInChIChar(pInp); + k++; + if ( k==1 ) let1 = c; + else if ( k==2 ) let2 = c; + } + if ( k != 3) + { + /* not a valid SaveOpt which must be of two chars */ + *input_has_save_opt = 0; + let1 = let2 = '\0'; + } + else + { + /* may be SaveOpt - analyze the content */ + if ( ( let2 >= 'A') && ( let2 <= 'D') ) /* letter-2 OK */ + { + *input_has_save_opt = 0; + *input_save_opt_bits = 0; + for (k=0; k<16; k++) + { + if ( a2p[k] == let1) /* letter-1 OK */ + { + *input_save_opt_bits = (unsigned char) k; + *input_has_save_opt = 1; + break; + } + } + if ( *input_has_save_opt ) + { + if ( let2=='B' || let2=='D' ) + *input_save_opt_bits |= SAVE_OPT_15T; + if ( let2=='C' || let2=='D' ) + *input_save_opt_bits |= SAVE_OPT_KET; + } + } + } + + ret = (c == RI_ERR_EOF)? RI_ERR_EOF : RI_ERR_EOL; /* end of line */ + pLine->len = 0; + pLine->c = ret; + break; /* exit */ + } + if ( c == RI_ERR_EOF ) { + ret = RI_ERR_EOF; /* end of line */ + break; + } + if ( c == SEG_END ) { + c = nGetInChISegment( pInp, pLine, szToken ); + } + if ( c < 0 ) { + goto exit_error; /* error */ + } + if ( !pLine->len ) + { + ret = RI_ERR_EOL; /* end of line */ + break; + } + fst = UCINT pLine->str[0]; + + /* + if ( fst == 'z' ) + { + ret = RI_ERR_EOL; + break; + }*/ + } + /* process the seqment */ + switch ( state ) { + + /* Mobile H, M */ /* / */ + case IST_MOBILE_H_FORMULA: + bMobileH = TAUT_YES; + ret = ParseSegmentFormula( pLine->str, bMobileH, pInpInChI[bReconn], + nNumComponents[bReconn], &na_total ); + state = IST_MOBILE_H_CONNECTIONS; + break; + case IST_MOBILE_H_CONNECTIONS: /* /c */ + ret = ParseSegmentConnections( pLine->str, bMobileH, &pInpInChI[bReconn][bMobileH], + &nNumComponents[bReconn][bMobileH], &bAbc, &nb_total ); + state = IST_MOBILE_H; + break; + case IST_MOBILE_H: /* /h */ + ret = ParseSegmentMobileH( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], &bAbc ); + state = IST_MOBILE_H_CHARGE; + break; + case IST_MOBILE_H_CHARGE: /* /q */ + ret = ParseSegmentCharge( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn] ); + state = IST_MOBILE_H_PROTONS; + break; + case IST_MOBILE_H_PROTONS: /* /p */ + ret = ParseSegmentProtons( pLine->str, bMobileH, nNumProtons[bReconn], nNumComponents[bReconn] ); + state = IST_MOBILE_H_POLYMER; + break; + case IST_MOBILE_H_POLYMER: /* /z */ + ret = ParseSegmentPolymer( pLine->str, bMobileH, + nNumProtons[bReconn], nNumComponents[bReconn], + na_total, nb_total, bInchi2Struct, ppPolymer, ppV3000 ); + if ( *ppPolymer ) + (*ppPolymer)->is_in_reconn = bReconn; + state = IST_MOBILE_H_SP2; + break; + case IST_MOBILE_H_SP2: /* /b */ + ret = ParseSegmentSp2( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); + state = IST_MOBILE_H_SP3; + break; + case IST_MOBILE_H_SP3: /* t */ + ret = ParseSegmentSp3( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); + state = IST_MOBILE_H_SP3_M; + break; + case IST_MOBILE_H_SP3_M: /* /m */ + ret = ParseSegmentSp3m( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); + state = IST_MOBILE_H_SP3_S; + break; + case IST_MOBILE_H_SP3_S: /* /s */ + ret = ParseSegmentSp3s( pLine->str, bMobileH, pInpInChI[bReconn], s[bReconn], nNumComponents[bReconn], state ); + state = IST_MOBILE_H_ISO_LAYER_FORK; + break; + case IST_MOBILE_H_ISO_LAYER_FORK: + /* find layer type after M */ + ret = 0; + switch( pLine->str[0] ) { + case 'i': + state = IST_MOBILE_H_ISO_ATOMS; /* MI */ + break; + case 'f': + state = IST_FIXED_H_FORMULA; /* F */ + break; + case 'r': + state = IST_RECONNECTED_FORMULA; /* reconnected */ + break; + default: + ret = RI_ERR_SYNTAX; + } + if ( INCHI_INP_EOL(c) && ret == 0 && !pLine->str[1] ) { + prev_state = state + (bReconn? IST_HAPPENED_IN_RECMET : 0); + ret = RI_ERR_SYNTAX; /* empty layer /i or /f or /r at the end of InChI line */ + } else + if ( !ret && state != IST_MOBILE_H_ISO_ATOMS ) { + len = (int) strlen( pLine->str ); + if ( len > 1 ) { + memmove( pLine->str, pLine->str+1, len ); + } else { + ret = 1; /* read the next segment */ + } + } + break; + /* Mobile H, isotopic, MI */ + case IST_MOBILE_H_ISO_ATOMS: /* i */ + ret = ParseSegmentIsoAtoms( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); + state = IST_MOBILE_H_ISO_EXCH_H; + break; + case IST_MOBILE_H_ISO_EXCH_H: /* /i/h */ + ret = ParseSegmentIsoExchgH( pLine->str, bMobileH, nNumProtons[bReconn], nNumComponents[bReconn], state, &bAbc ); + state = IST_MOBILE_H_ISO_SP2; + break; + case IST_MOBILE_H_ISO_SP2: /* /i/b */ + ret = ParseSegmentSp2( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); + state = IST_MOBILE_H_ISO_SP3; + break; + case IST_MOBILE_H_ISO_SP3: /* /i/t */ + ret = ParseSegmentSp3( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); + state = IST_MOBILE_H_ISO_SP3_M; + break; + case IST_MOBILE_H_ISO_SP3_M: /* /i/m */ + ret = ParseSegmentSp3m( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); + state = IST_MOBILE_H_ISO_SP3_S; + + + break; + case IST_MOBILE_H_ISO_SP3_S: /* /i/s */ + ret = ParseSegmentSp3s( pLine->str, bMobileH, pInpInChI[bReconn], s[bReconn], nNumComponents[bReconn], state ); + state = IST_FIXED_H_LAYER_FORK; + break; + case IST_FIXED_H_LAYER_FORK: + /* find layer type after MI */ + ret = 0; + switch( pLine->str[0] ) { + case 'f': + state = IST_FIXED_H_FORMULA; /* F */ + break; + case 'r': + state = IST_RECONNECTED_FORMULA; /* reconnected */ + break; + default: + ret = RI_ERR_SYNTAX; + } + if ( INCHI_INP_EOL(c) && ret == 0 && !pLine->str[1] ) { + prev_state = state + (bReconn? IST_HAPPENED_IN_RECMET : 0); + ret = RI_ERR_SYNTAX; /* empty layer /f or /r at the end of InChI line */ + } else + if ( !ret ) { + len = (int) strlen( pLine->str ); + if ( len > 1 ) { + memmove( pLine->str, pLine->str+1, len ); + } else { + ret = 1; /* read the next segment */ + } + } + break; + + /* Fixed H, F */ + case IST_FIXED_H_FORMULA: + bMobileH = TAUT_NON; + ret = ParseSegmentFormula( pLine->str, bMobileH, pInpInChI[bReconn], + nNumComponents[bReconn], &na_total ); + state = IST_FIXED_H; + break; + case IST_FIXED_H: /* /f/h */ + ret = ParseSegmentMobileH( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], &bAbc ); + state = IST_FIXED_H_CHARGE; + break; + case IST_FIXED_H_CHARGE: /* /f/q */ + ret = ParseSegmentCharge( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn] ); + state = IST_FIXED_H_SP2; + break; + case IST_FIXED_H_SP2: /* /f/b */ + ret = ParseSegmentSp2( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); + state = IST_FIXED_H_SP3; + break; + case IST_FIXED_H_SP3: /* /f/t */ + ret = ParseSegmentSp3( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); + state = IST_FIXED_H_SP3_M; + break; + case IST_FIXED_H_SP3_M: /* /f/m */ + ret = ParseSegmentSp3m( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); + state = IST_FIXED_H_SP3_S; + break; + case IST_FIXED_H_SP3_S: /* /f/s */ + ret = ParseSegmentSp3s( pLine->str, bMobileH, pInpInChI[bReconn], s[bReconn], nNumComponents[bReconn], state ); + state = IST_FIXED_H_PERMUTATION; + break; + case IST_FIXED_H_PERMUTATION: /* /f/o */ + ret = ParseSegmentPerm( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); + state = IST_FIXED_H_ISO_LAYER_FORK; + break; + case IST_FIXED_H_ISO_LAYER_FORK: + /* find layer type after M */ + ret = 0; + switch( pLine->str[0] ) { + case 'i': + state = IST_FIXED_H_ISO_ATOMS; /* FI */ + break; + case 'r': + state = IST_RECONNECTED_FORMULA; /* reconnected */ + break; + default: + ret = RI_ERR_SYNTAX; + } + if ( INCHI_INP_EOL(c) && ret == 0 && !pLine->str[1] ) { + prev_state = state + (bReconn? IST_HAPPENED_IN_RECMET : 0); + ret = RI_ERR_SYNTAX; /* empty layer /i or /r at the end of InChI line */ + } else + if ( !ret && state != IST_FIXED_H_ISO_ATOMS ) { + len = (int) strlen(pLine->str ); + if ( len > 1 ) { + memmove( pLine->str, pLine->str+1, len ); + } else { + ret = 1; /* read the next segment */ + } + } + break; + + /* Fixed H, isotopic, FI */ + case IST_FIXED_H_ISO_ATOMS: /* /f/i */ + ret = ParseSegmentIsoAtoms( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); + state = IST_FIXED_H_ISO_SP2; + break; + case IST_FIXED_H_ISO_SP2: /* /f/i/b */ + ret = ParseSegmentSp2( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); + state = IST_FIXED_H_ISO_SP3; + break; + case IST_FIXED_H_ISO_SP3: /* /f/i/t */ + ret = ParseSegmentSp3( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); + state = IST_FIXED_H_ISO_SP3_M; + break; + case IST_FIXED_H_ISO_SP3_M: /* /f/i/m */ + ret = ParseSegmentSp3m( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state ); + state = IST_FIXED_H_ISO_SP3_S; + break; + case IST_FIXED_H_ISO_SP3_S: /* /f/i/s */ + ret = ParseSegmentSp3s( pLine->str, bMobileH, pInpInChI[bReconn], s[bReconn], nNumComponents[bReconn], state ); + state = IST_FIXED_H_ISO_PERMUTATION; + break; + case IST_FIXED_H_ISO_PERMUTATION: /* /f/i/o */ + ret = ParseSegmentPerm( pLine->str, bMobileH, pInpInChI[bReconn], nNumComponents[bReconn], state, &bAbc ); + state = IST_RECONNECTED_LAYER_FORK; + break; + case IST_RECONNECTED_LAYER_FORK: + /* find layer type after FI */ + ret = 0; + switch( pLine->str[0] ) { + case 'r': + state = IST_RECONNECTED_FORMULA; /* reconnected */ + break; + default: + ret = RI_ERR_SYNTAX; + } + if ( INCHI_INP_EOL(c) && ret == 0 && !pLine->str[1] ) { + prev_state = state + (bReconn? IST_HAPPENED_IN_RECMET : 0); + ret = RI_ERR_SYNTAX; /* empty layer /r at the end of InChI line */ + } else + if ( !ret ) { + len = (int) strlen( pLine->str ); + if ( len > 1 ) { + memmove( pLine->str, pLine->str+1, len ); + } else { + ret = 1; /* read the next segment */ + } + } + break; + case IST_RECONNECTED_FORMULA: + bReconn = INCHI_REC; + bMobileH = TAUT_YES; + state = IST_MOBILE_H_FORMULA; + break; + } + } while( c >= 0 ); + +exit_function:; +exit_error:; + + INCHI_HEAPCHK + + if ( ret >= 0 || c == RI_ERR_EOF || c == RI_ERR_EOL ) { + pLine->len = 0; + } + return ret; +} + + +/* + Parse InChI layer '/i/h' +*/ +int ParseSegmentIsoExchgH( const char *str, int bMobileH, REM_PROTONS nNumProtons[], int pnNumComponents[], int state, int *pbAbc ) +{ + /* Pass 1: count bonds and find actual numbers of atom */ + const char *p, *q, *pStart, *pEnd; + int ret=0, num, i, i_prev; + static const char abc_h[] = "hdt"; + + if ( str[0] != 'h' ) + return 0; + + pStart = str+1; + + if ( !(bMobileH==TAUT_YES && state == IST_MOBILE_H_ISO_EXCH_H ) ) { + return RI_ERR_PROGR; /* program error */ + } + + if ( !(pEnd = strchr( pStart, ';' )) ) { + pEnd = pStart + strlen(pStart); + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + p = pStart; + + if ( p < pEnd && *pbAbc == -1 ) { + /* check if compressed InChI */ + /* compressed: /hNtNdNh where N is a decimal number */ + /* uncompressed: /hT[n]D[n]H[n] where n > 1 is a decimal number */ + *pbAbc = isdigit( UCINT *p)? 1 : 0; + } + + if ( *pbAbc == 1 ) { + i_prev = (int)sizeof(abc_h); + while ( p < pEnd ) { + num = (int)inchi_strtol( p, &q, 10 ); + if ( 0 >= num || p == q || q >= pEnd ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + p = strchr( abc_h, *q); + if ( p && (i=(int) (p-abc_h)) < i_prev ) { + nNumProtons[bMobileH].nNumRemovedIsotopicH[i] = (NUM_H)num; + p = q+1; + i_prev = i; + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + } + } else { + if ( *p == 'T' ) { + nNumProtons[bMobileH].nNumRemovedIsotopicH[2] = 1; + p ++; + if ( isdigit( UCINT p[0]) ) { + nNumProtons[bMobileH].nNumRemovedIsotopicH[2] = (NUM_H)inchi_strtol( p, &q, 10 ); + p = q; + } + } + if ( *p == 'D' ) { + nNumProtons[bMobileH].nNumRemovedIsotopicH[1] = 1; + p ++; + if ( isdigit( UCINT p[0]) ) { + nNumProtons[bMobileH].nNumRemovedIsotopicH[1] = (NUM_H)inchi_strtol( p, &q, 10 ); + p = q; + } + } + if ( *p == 'H' ) { + nNumProtons[bMobileH].nNumRemovedIsotopicH[0] = 1; + p ++; + if ( isdigit( UCINT p[0]) ) { + nNumProtons[bMobileH].nNumRemovedIsotopicH[0] = (NUM_H)inchi_strtol( p, &q, 10 ); + p = q; + } + } + } + if ( p != pEnd ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + ret = 1; + +exit_function: + return ret; +} + + +/* + ParseSegmentPerm +*/ +int ParseSegmentPerm( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state, int *pbAbc ) +{ + int nNumComponents, iComponent1, iComponent2, numTrans; + const char *p, *q, *pStart, *pEnd, *pPermStart, *pPermEnd; + int ret=0; + INChI *pInChI = pInpInChI[bMobileH]; /* bMobileH should be TAUT_NON = 0 */ + INChI tmp; + int base = 10; + + + if ( str[0] != 'o' ) + return 0; + + pStart = str+1; + nNumComponents = ppnNumComponents[bMobileH]; + + if ( !(bMobileH==TAUT_NON && ( state == IST_FIXED_H_PERMUTATION || state == IST_FIXED_H_ISO_PERMUTATION) ) ) { + return RI_ERR_PROGR; /* program error */ + } + + if ( !(pEnd = strchr( pStart, ';' )) ) { + pEnd = pStart + strlen(pStart); + } else { + return RI_ERR_SYNTAX; /* syntax error */ + } + while( pStart < pEnd ) { + /* cycle over components; rearrange Fixed H components in order of Mobile H components */ + /* if /o(1,2,3) then reaarange Fixed H components in this way: tmp<-1, 1<-2, 2<-3, 3<-tmp */ + if ( *pStart != '(' ) { + ret = RI_ERR_SYNTAX; + goto exit_function; + } + pPermStart = pStart + 1; + memset( &tmp, 0, sizeof(tmp) ); /* initialization 2006-03 */ + if ( !(pPermEnd = strchr( pPermStart, ')' )) || pPermEnd == pPermStart ) { + ret = RI_ERR_SYNTAX; + goto exit_function; + } + + if ( pPermStart < pPermEnd && *pbAbc == -1 ) { + /* check if compressed InChI */ + *pbAbc = isupper( UCINT *pPermStart)? 1 : 0; + } + base = (*pbAbc==1)? ALPHA_BASE : 10; + + /* permutation cycle */ + if ( *pbAbc == 1 ) { + for ( p = pPermStart, iComponent2 = numTrans = 0; p < pPermEnd; iComponent2 = iComponent1, p = q ) { + /* get first atom number */ + if ( 0 >= (iComponent1 = (int)inchi_strtol( p, &q, base )) || iComponent1 > nNumComponents ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + if ( iComponent2 ) { + pInChI[iComponent2-1] = pInChI[iComponent1-1]; + numTrans ++; + } else { + tmp = pInChI[iComponent1-1]; /* on the 1st pass save Component1 */ + } + } + } else { + for ( p = pPermStart, iComponent2 = numTrans = 0; p < pPermEnd; iComponent2 = iComponent1, p = q + (*q==',') ) { + /* get first atom number */ + if ( !isdigit( UCINT *p ) ) { + ret = RI_ERR_SYNTAX; + goto exit_function; + } + if ( !(iComponent1 = (int)inchi_strtol( p, &q, 10 )) || iComponent1 > nNumComponents ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + if ( iComponent2 ) { + pInChI[iComponent2-1] = pInChI[iComponent1-1]; + numTrans ++; + } else { + tmp = pInChI[iComponent1-1]; /* on the 1st pass save Component1 */ + } + } + } + pInChI[iComponent2-1] = tmp; + if ( !numTrans || p != pPermEnd ) { + ret = RI_ERR_SYNTAX; + goto exit_function; + } else { + pStart = p+1; + } + } + ret = 1; + +exit_function: + return ret; +} + + +/* + Parse InChI layer '/i' +*/ +int ParseSegmentIsoAtoms( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state, int *pbAbc ) +{ + int i, mpy_component, val; + int nNumComponents, iComponent, len=0, iAtom; + AT_NUMB nAtom1; + const char *p, *q, *t, *pStart, *pEnd, *r; + int ret=0; + INChI *pInChI = pInpInChI[bMobileH]; + INChI *pInChIFrom=NULL; + INChI_IsotopicAtom **pIsotopicAtom = NULL; + INChI_IsotopicAtom isoAtom; + + const char mult_type[] = "mnMNe"; + const char parity_type[] = "-+TDH"; + int bIsoFrom, nCpyType = CPY_ISO_AT; + int base = 10; + + if ( str[0] != 'i' ) + return 0; + + pStart = str+1; + iComponent = 0; + nNumComponents = ppnNumComponents[bMobileH]; + + if ( !(bMobileH==TAUT_YES && state == IST_MOBILE_H_ISO_ATOMS || + bMobileH==TAUT_NON && state == IST_FIXED_H_ISO_ATOMS ) ) { + return RI_ERR_PROGR; /* program error */ + } + if ( !*pStart ) { + return nNumComponents+1; /* no isotopic atoms */ + } + + while( 1 ) { + /* cycle over components */ + if ( !(pEnd = strchr( pStart, ';' )) ) { + pEnd = pStart + strlen(pStart); + } + if ( (p = strchr(pStart, '*')) && p < pEnd ) { + mpy_component = (int)inchi_strtol( pStart, &q, 10 ); + if ( p != q ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } +#if (FIX_DALKE_BUGS == 1) + if ( iComponent + mpy_component > nNumComponents ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } +#endif + p ++; /* move to the 1st character of the component */ + } else + if ( (isdigit(*pStart) && + 0 < (val = (int)inchi_strtol( pStart, &q, 10)) || + (q = pStart, val=1))&& + (t=strchr(mult_type, *q)) && q+1 == pEnd ) { + /* process the abbreviation */ + ret = 0; +#if (FIX_DALKE_BUGS == 1) + if ( iComponent + val > nNumComponents ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } +#endif + bIsoFrom = 0; + switch( bMobileH ) { + case TAUT_YES: + ret = RI_ERR_SYNTAX; + break; + case TAUT_NON: + if ( *q == 'm' ) { + /* copy from mobile H to fixed H */ + pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; + } else + if ( *q == 'e' ) { + /* copy from mobile H to isotopic mobile H */ + pInChIFrom = pInChI; + bIsoFrom = -1; /* empty */ + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + } + break; + default: + ret = RI_ERR_SYNTAX; + break; + } + if ( ret < 0 ) { + goto exit_function; + } + /* copy */ + for ( i = 0; i < val; i ++ ) { + ret = CopySegment( pInChI+iComponent+i, pInChIFrom+iComponent+i, nCpyType, 0, bIsoFrom ); + if ( !ret ) { + ret = RI_ERR_SYNTAX; + } + if ( ret < 0 ) { + goto exit_function; + } + } + iComponent += val; + /* continue to the next component(s) */ + if ( *pEnd ) { + pStart = pEnd+1; + continue; + } else { + break; + } + } else { + mpy_component = 1; + p = pStart; + } + pStart = p; + pIsotopicAtom = &pInChI[iComponent].IsotopicAtom; + if ( *pIsotopicAtom ) { + ret = RI_ERR_PROGR; /* program error */ + goto exit_function; + } + + if ( p < pEnd && *pbAbc == -1 ) { + /* check if compressed InChI */ + *pbAbc = isupper( UCINT *p)? 1 : 0; + } + base = (*pbAbc==1)? ALPHA_BASE : 10; + + +one_more_time: + if ( *pbAbc == 1 ) { + /* process the componnt: At[+/-Charge]TDH,... */ + /* pass 1: find number of stereoatoms */ + for ( p = pStart, iAtom = 0; p < pEnd; iAtom ++ ) { + nAtom1 = (AT_NUMB)inchi_strtol( p, &p, base ); + if ( !nAtom1 || + nAtom1 > pInChI[iComponent].nNumberOfAtoms ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + memset( &isoAtom, 0, sizeof(isoAtom) ); + isoAtom.nAtomNumber = nAtom1; + isoAtom.nIsoDifference = (NUM_H)inchi_strtol( p, &q, 10 ); /* alway in abc */ + if ( p == q ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + p = q; + if ( *p == 't' ) { + isoAtom.nNum_T = 1; + p ++; + if ( isdigit( UCINT *p) ) { + isoAtom.nNum_T = (NUM_H)inchi_strtol( p, &q, 10 ); + p = q; + } + } + if ( *p == 'd' ) { + isoAtom.nNum_D = 1; + p ++; + if ( isdigit( UCINT *p) ) { + isoAtom.nNum_D = (NUM_H)inchi_strtol( p, &q, 10 ); + p = q; + } + } + if ( *p == 'h' ) { + isoAtom.nNum_H = 1; + p ++; + if ( isdigit( UCINT *p) ) { + isoAtom.nNum_H = (NUM_H)inchi_strtol( p, &q, 10 ); + p = q; + } + } + if ( p > pEnd || !isoAtom.nIsoDifference && !isoAtom.nNum_T && !isoAtom.nNum_D && !isoAtom.nNum_H ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + if ( *pIsotopicAtom ) { + pIsotopicAtom[0][iAtom] = isoAtom; + } + } + } else { + /* process the componnt: At[+/-Charge]TDH,... */ + /* pass 1: find number of stereoatoms */ + for ( p = pStart, iAtom = 0; p < pEnd; iAtom ++ ) { + nAtom1 = (AT_NUMB)inchi_strtol( p, &q, 10 ); + p = q; + if ( !nAtom1 || + nAtom1 > pInChI[iComponent].nNumberOfAtoms || + !(r = strchr( parity_type, *p) ) ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + memset( &isoAtom, 0, sizeof(isoAtom) ); + isoAtom.nAtomNumber = nAtom1; + if ( p[0] == '+' && isdigit( UCINT p[1]) ) { + isoAtom.nIsoDifference = (NUM_H)inchi_strtol( p+1, &q, 10 ); + if ( isoAtom.nIsoDifference >= 0 ) isoAtom.nIsoDifference ++; + p = q; + } else + if ( p[0] == '-' && isdigit( UCINT p[1]) ) { + isoAtom.nIsoDifference = -(NUM_H)inchi_strtol( p+1, &q, 10 ); + if ( isoAtom.nIsoDifference == 0 ) isoAtom.nIsoDifference ++; + p = q; + } + if ( *p == 'T' ) { + isoAtom.nNum_T = 1; + p ++; + if ( isdigit( UCINT *p) ) { + isoAtom.nNum_T = (NUM_H)inchi_strtol( p, &q, 10 ); + p = q; + } + } + if ( *p == 'D' ) { + isoAtom.nNum_D = 1; + p ++; + if ( isdigit( UCINT *p) ) { + isoAtom.nNum_D = (NUM_H)inchi_strtol( p, &q, 10 ); + p = q; + } + } + if ( *p == 'H' ) { + isoAtom.nNum_H = 1; + p ++; + if ( isdigit( UCINT *p) ) { + isoAtom.nNum_H = (NUM_H)inchi_strtol( p, &q, 10 ); + p = q; + } + } + if ( !isoAtom.nIsoDifference && !isoAtom.nNum_T && !isoAtom.nNum_D && !isoAtom.nNum_H ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + if ( p < pEnd ) { + if ( *p == ',' ) { + p ++; + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + } + if ( *pIsotopicAtom ) { + pIsotopicAtom[0][iAtom] = isoAtom; + } + } + } + if ( p != pEnd ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + + if ( !*pIsotopicAtom ) { + /* end of the 1st pass */ + len = iAtom; + /* memory allocation */ + if ( !(*pIsotopicAtom = (INChI_IsotopicAtom *) inchi_calloc( len+1, sizeof(**pIsotopicAtom) ) ) ) { + ret = RI_ERR_ALLOC; /* memory allocation failed */ + goto exit_function; + } + goto one_more_time; /* goto the 2nd pass */ + } else { + /* 2nd pass */ + if ( len != iAtom ) { + ret = RI_ERR_PROGR; /* program error */ + goto exit_function; + } + pInChI[iComponent].nNumberOfIsotopicAtoms = len; + } + + /* multiplier */ + for ( i = 1; i < mpy_component; i ++ ) { + ret = CopySegment( pInChI+iComponent+i, pInChI+iComponent, nCpyType, 0, 0 ); + if ( !ret ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + } + if ( ret < 0 ) { + goto exit_function; + } + } + + iComponent += mpy_component; + if ( *pEnd ) { + pStart = pEnd+1; + continue; + } else { + break; + } + } + if ( nNumComponents != iComponent ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + ret = iComponent + 1; + +exit_function: + return ret; +} + + +/* + Parse InChI layer '/i/s' +*/ +int ParseSegmentSp3s( const char *str, int bMobileH, INChI *pInpInChI[], int s[TAUT_NUM][2], int ppnNumComponents[], int state ) +{ + /* Pass 1: count bonds and find actual numbers of atom */ + int nNumComponents, iComponent, val; + const char *p, *q, *pStart, *pEnd; + int ret=0; + INChI *pInChI = pInpInChI[bMobileH]; + INChI_Stereo **pStereo = NULL; + + int bIso = (state==IST_MOBILE_H_ISO_SP3_S || state==IST_FIXED_H_ISO_SP3_S); + + if ( !bIso && state != IST_MOBILE_H_SP3_S && state != IST_FIXED_H_SP3_S ) { + return RI_ERR_PROGR; /* program error */ + } + + if ( str[0] != 's' ) + return 0; + + pStart = str+1; + iComponent = 0; + nNumComponents = ppnNumComponents[bMobileH]; + + /*if ( !(pEnd = strchr( pStart, ';' )) )*/ /* 2007-09-25 DT */ + if ( !(pEnd = strchr( pStart, '/' )) ){ + pEnd = pStart + strlen(pStart); + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + p = pStart; + if ( pEnd == pStart ) { + /* create empty sp3 segment */ + int len = 0; + s[bMobileH][bIso] = NO_VALUE_INT; /* empty */ + /* create empty sp3 segment */ + for ( iComponent = 0; iComponent < nNumComponents; iComponent ++ ) { + pStereo = bIso? &pInChI[iComponent].StereoIsotopic : &pInChI[iComponent].Stereo; + if ( !*pStereo ) { + if ( !(*pStereo = (INChI_Stereo *) inchi_calloc( 1, sizeof(**pStereo) ) ) ) { + ret = RI_ERR_ALLOC; /* memory allocation failed */ + goto exit_function; + } + } + pStereo[0]->nCompInv2Abs = 0; /* deliberately empty */ + + if ( pStereo[0]->nNumberOfStereoCenters ) { + ret = RI_ERR_SYNTAX; /* syntax error: "/s" without a digit describes "no stereo" */ + goto exit_function; + } + /* allocate empty sp3 stereo */ + if ( !pStereo[0]->t_parity && + !(pStereo[0]->t_parity = (S_CHAR *)inchi_calloc( len+1, sizeof(pStereo[0]->b_parity[0]) ) ) || + !pStereo[0]->nNumber && + !(pStereo[0]->nNumber = (AT_NUMB *)inchi_calloc( len+1, sizeof(pStereo[0]->nNumber[0]) ) ) ) { + /* cleanup */ + if ( pStereo[0]->t_parity ) { + INCHI_HEAPCHK + inchi_free( pStereo[0]->t_parity ); + pStereo[0]->t_parity = NULL; + } + if ( pStereo[0]->nNumber ) { + INCHI_HEAPCHK + inchi_free( pStereo[0]->nNumber ); + pStereo[0]->nNumber = NULL; + } + ret = RI_ERR_ALLOC; /* memory allocation failed */ + goto exit_function; + } + } + ret = nNumComponents+1; + } else { + val = (int)inchi_strtol( p, &q, 10 ); + if ( q == pEnd && 1 <= val && val <= 3 ) { + s[bMobileH][bIso] = val; + ret = nNumComponents+1; + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + } + } +exit_function: + return ret; +} + + +/* + bIsSp3LayerNotEmpty +*/ +int bIsSp3LayerNotEmpty( INChI *pInpInChI[], int bMobileH, int bIso, int nNumComponents ) +{ + INChI *pInChI; + INChI_Stereo *pStereo; + int iComponent, num_not_empty = 0; + + if ( pInpInChI[bMobileH] ) { + for ( iComponent = 0; iComponent < nNumComponents; iComponent ++ ) { + pInChI = pInpInChI[bMobileH] + iComponent; + if ( pInChI->bDeleted || !pInChI->nNumberOfAtoms ) { + continue; + } + pStereo = bIso? pInChI->StereoIsotopic : pInChI->Stereo; + if ( pStereo && pStereo->nNumberOfStereoCenters > 0 && pStereo->nNumber && pStereo->t_parity ) { + num_not_empty ++; + } + } + } + return num_not_empty; +} + + +/* + Parse InChI layer '/i/m' +*/ +int ParseSegmentSp3m( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state ) +{ + /* Pass 1: count bonds and find actual numbers of atom */ + int nNumComponents, iComponent; + const char *p, *pStart, *pEnd; + int ret=0; + INChI *pInChI = pInpInChI[bMobileH]; + INChI_Stereo **pStereo = NULL; + + int bIso = (state==IST_MOBILE_H_ISO_SP3_M || state==IST_FIXED_H_ISO_SP3_M); + + if ( !bIso && state != IST_MOBILE_H_SP3_M && state != IST_FIXED_H_SP3_M ) { + return RI_ERR_PROGR; /* program error */ + } + nNumComponents = ppnNumComponents[bMobileH]; + + if ( str[0] != 'm' ) { + /* /m is missing: check whether we have to inherit /m from a preceding stereo layer */ + INChI_Stereo *pStereoFrom, *pStereoTo; + INChI *pInChIFrom; + int nNumCopied = 0, bMobileHFrom=-1, bIsoFrom=-1; + if ( bMobileH && !bIso ) { + return 0; /* Main non-isotopic cannot inherit: it has no preceding layer */ + } else + if ( !bMobileH && !bIso ) { + /* fixed-H non-isotopic (F) inherits from Mobile-H non-isotopic (M) */ + bMobileHFrom = TAUT_YES; + bIsoFrom = 0; + } else + if ( bMobileH && bIso ) { + /* Mobile-H isotopic (MI) inherits from Mobile-H non-isotopic (M) */ + bMobileHFrom = TAUT_YES; + bIsoFrom = 0; + } else + if ( !bMobileH && bIso ) { + /* Fixed-H isotopic (FI) inherits from Fixed-H non-isotopic (F) */ + bMobileHFrom = TAUT_NON; + bIsoFrom = 0; + /* if Sp3 is empty in F as well as in M, then inherit from MI */ + if ( !bIsSp3LayerNotEmpty( pInpInChI, TAUT_NON, 0, ppnNumComponents[TAUT_NON /*bMobileH*/] ) /* F */ && + !bIsSp3LayerNotEmpty( pInpInChI, TAUT_YES, 0, ppnNumComponents[TAUT_YES /*bMobileH*/] ) /* M */ ) { + bMobileHFrom = TAUT_YES; + bIsoFrom = 1; + } + } + if ( bMobileHFrom < 0 || bIsoFrom < 0 ) { + return RI_ERR_PROGR; + } + if ( !bIsSp3LayerNotEmpty( pInpInChI, bMobileHFrom, bIsoFrom, ppnNumComponents[/*bMobileH*/ bMobileHFrom] ) ) { + /* nothing to copy; check whether it should have inherited from a preceding layer */ + if ( !bMobileHFrom && bIsoFrom || bMobileHFrom && !bIsoFrom ) { + /* MI or F inherit stereo from M */ + bMobileHFrom = TAUT_YES; + bIsoFrom = 0; + if ( !bIsSp3LayerNotEmpty( pInpInChI, bMobileHFrom, bIsoFrom, ppnNumComponents[bMobileHFrom /*bMobileH*/] ) ) { + return 0; + } + } else { + return 0; + } + } + nNumComponents = inchi_min( ppnNumComponents[bMobileH], ppnNumComponents[bMobileHFrom] ); + for ( iComponent = 0; iComponent < nNumComponents; iComponent ++ ) { + pInChIFrom = pInpInChI[bMobileHFrom] + iComponent; + pInChI = pInpInChI[bMobileH] + iComponent; + if ( pInChIFrom->nNumberOfAtoms > 0 && !pInChIFrom->bDeleted && + pInChI->nNumberOfAtoms > 0 && !pInChI->bDeleted ) { + pStereoFrom = bIsoFrom? pInChIFrom->StereoIsotopic : pInChIFrom->Stereo; + pStereoTo = bIso? pInChI->StereoIsotopic : pInChI->Stereo; + if ( pStereoFrom && pStereoTo ) { + pStereoTo->nCompInv2Abs = pStereoFrom->nCompInv2Abs; + nNumCopied ++; + } + } + } + return 0; /* return value > 0 means the non-/m segment has been processed here */ + } + + pStart = str+1; + iComponent = 0; + + /*if ( !(pEnd = strchr( pStart, ';' )) )*/ /* 2007-09-25 DT */ + if ( !(pEnd = strchr( pStart, '/' )) ) { + pEnd = pStart + strlen(pStart); + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + p = pStart; + if ( pEnd == pStart ) { + /* create empty sp3 segment */ + int len = 0; + for ( iComponent = 0; iComponent < nNumComponents; iComponent ++ ) { + INChI *pIsoInChI = &pInChI[iComponent]; + pStereo = bIso? &pIsoInChI->StereoIsotopic : &pIsoInChI->Stereo; + if ( !*pStereo ) { + if ( !(*pStereo = (INChI_Stereo *) inchi_calloc( 1, sizeof(**pStereo) ) ) ) { + ret = RI_ERR_ALLOC; /* memory allocation failed */ + goto exit_function; + } + } + pStereo[0]->nCompInv2Abs = NO_VALUE_INT; /* deliberately empty */ +#ifdef NEVER + if ( pStereo[0]->nNumberOfStereoCenters ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } +#endif + /* allocate empty sp3 stereo */ + if ( !pStereo[0]->t_parity && + !(pStereo[0]->t_parity = (S_CHAR *)inchi_calloc( len+1, sizeof(pStereo[0]->b_parity[0]) ) ) || + !pStereo[0]->nNumber && + !(pStereo[0]->nNumber = (AT_NUMB *)inchi_calloc( len+1, sizeof(pStereo[0]->nNumber[0]) ) ) ) { + /* cleanup */ + if ( pStereo[0]->t_parity ) { + INCHI_HEAPCHK + inchi_free( pStereo[0]->t_parity ); + pStereo[0]->t_parity = NULL; + } + if ( pStereo[0]->nNumber ) { + INCHI_HEAPCHK + inchi_free( pStereo[0]->nNumber ); + pStereo[0]->nNumber = NULL; + } + ret = RI_ERR_ALLOC; /* memory allocation failed */ + goto exit_function; + } + } + ret = nNumComponents+1; + } else { + while( p < pEnd && iComponent < nNumComponents ) { + /* cycle over components */ + pStereo = bIso? &pInChI[iComponent].StereoIsotopic : &pInChI[iComponent].Stereo; + if ( *p != '.' && !*pStereo ) { + if ( !(*pStereo = (INChI_Stereo *) inchi_calloc( 1, sizeof(**pStereo) ) ) ) { + ret = RI_ERR_ALLOC; /* memory allocation failed */ + goto exit_function; + } + } + switch( *p ) { + case '1': + pStereo[0]->nCompInv2Abs = -1; + break; + case '0': + pStereo[0]->nCompInv2Abs = 1; + break; + case '.': + if ( *pStereo ) { + pStereo[0]->nCompInv2Abs = 0; + } + break; + default: + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + iComponent ++; + p ++; + } + if ( p != pEnd || iComponent != nNumComponents ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + ret = nNumComponents+1; + } +exit_function: + return ret; +} + + +/* + Parse InChI layer '/t' +*/ +int ParseSegmentSp3( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state, int *pbAbc ) +{ + /* Pass 1: count bonds and find actual numbers of atom */ + int i, mpy_component, val; + int nNumComponents, iComponent, len, iAtom; + AT_NUMB nAtom1; + int atomParity; + const char *p, *q, *t, *pStart, *pEnd, *r; + int ret=0; + INChI *pInChI = pInpInChI[bMobileH]; + INChI *pInChIFrom=NULL; + /* + INChI_Stereo *Stereo = NULL; + INChI_Stereo *StereoOther = NULL; + */ + INChI_Stereo **pStereo = NULL; + + const char mult_type[] = "mnMNe"; + const char parity_type[] = "-+u?"; + int bIsoTo, bIsoFrom, nCpyType = CPY_SP3; + int bIso = (state==IST_MOBILE_H_ISO_SP3 || state==IST_FIXED_H_ISO_SP3); + int base = 10; + + if ( !bIso && state != IST_MOBILE_H_SP3 && state != IST_FIXED_H_SP3 ) { + return RI_ERR_PROGR; /* program error */ + } + + if ( str[0] != 't' ) + return 0; + + pStart = str+1; + iComponent = 0; + nNumComponents = ppnNumComponents[bMobileH]; + + if ( !*pStart ) { + /* create empty sp3 segment */ + int len0 = 0; + for ( iComponent = 0; iComponent < nNumComponents; iComponent ++ ) { + INChI *pIsoInChI = &pInChI[iComponent]; + pStereo = bIso? &pIsoInChI->StereoIsotopic : &pIsoInChI->Stereo; + if ( !*pStereo ) { + if ( !(*pStereo = (INChI_Stereo *) inchi_calloc( 1, sizeof(**pStereo) ) ) ) { + ret = RI_ERR_ALLOC; /* memory allocation failed */ + goto exit_function; + } + } + /* allocate empty sp3 stereo */ + if ( !pStereo[0]->b_parity && + !(pStereo[0]->b_parity = (S_CHAR *)inchi_calloc( len0+1, sizeof(pStereo[0]->b_parity[0]) ) ) || + !pStereo[0]->nBondAtom1 && + !(pStereo[0]->nBondAtom1 = (AT_NUMB *)inchi_calloc( len0+1, sizeof(pStereo[0]->nBondAtom1[0]) ) ) || + !pStereo[0]->nBondAtom2 && + !(pStereo[0]->nBondAtom2 = (AT_NUMB *)inchi_calloc( len0+1, sizeof(pStereo[0]->nBondAtom2[0]) ) ) ) { + /* cleanup */ + if ( pStereo[0]->b_parity ) { + INCHI_HEAPCHK + inchi_free( pStereo[0]->b_parity ); + pStereo[0]->b_parity = NULL; + } + if ( pStereo[0]->nBondAtom1 ) { + INCHI_HEAPCHK + inchi_free( pStereo[0]->nBondAtom1 ); + pStereo[0]->nBondAtom1 = NULL; + } + if ( pStereo[0]->nBondAtom2 ) { + INCHI_HEAPCHK + inchi_free( pStereo[0]->nBondAtom2 ); + pStereo[0]->nBondAtom2 = NULL; + } + ret = RI_ERR_ALLOC; /* memory allocation failed */ + goto exit_function; + } + pStereo[0]->nCompInv2Abs = NO_VALUE_INT; + } + ret = nNumComponents+1; + goto exit_function; + } + + while( 1 ) { + /* cycle over components */ + if ( !(pEnd = strchr( pStart, ';' )) ) { + pEnd = pStart + strlen(pStart); + } + if ( (isdigit(*pStart) && + 0 < (val = (int)inchi_strtol( pStart, &q, 10)) || + (q = pStart, val=1))&& + (t=strchr(mult_type, *q)) && q+1 == pEnd ) { + /* process the abbreviation */ + ret = 0; +#if (FIX_DALKE_BUGS == 1) + if ( iComponent + val > nNumComponents ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } +#endif + switch( bMobileH ) { + case TAUT_YES: + switch( state ) { + case IST_MOBILE_H_ISO_SP3: + if ( *q == 'm' ) { + /* copy from mobile H to isotopic mobile H */ + pInChIFrom = pInChI; + bIsoTo = 1; + bIsoFrom = 0; + } else + if ( *q == 'e' ) { + /* copy from mobile H to isotopic mobile H */ + pInChIFrom = pInChI; + bIsoTo = 1; + bIsoFrom = -1; /* empty */ + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + } + break; + default: + ret = RI_ERR_SYNTAX; + break; + } + break; + case TAUT_NON: + switch( state ) { + case IST_FIXED_H_SP3: + if ( *q == 'm' ) { + /* copy from mobile H to fixed H */ + pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; + bIsoTo = 0; + bIsoFrom = 0; + } else + if ( *q == 'e' ) { + /* copy from mobile H to isotopic mobile H */ + pInChIFrom = pInChI; + bIsoTo = 1; + bIsoFrom = -1; /* empty */ + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + } + break; + case IST_FIXED_H_ISO_SP3: + if ( *q == 'm' ) { + /* copy from mobile H to fixed isotopic H */ + pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; + bIsoTo = 1; + bIsoFrom = 0; + } else + if ( *q == 'M' ) { + /* copy from isotopic mobile H to fixed isotopic H */ + pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; + bIsoTo = 1; + bIsoFrom = 1; + } else + if ( *q == 'n' ) { + /* copy from fixed H to fixed isotopic H */ + pInChIFrom = pInChI; + bIsoTo = 1; + bIsoFrom = 0; + } else + if ( *q == 'e' ) { + /* copy from mobile H to isotopic mobile H */ + pInChIFrom = pInChI; + bIsoTo = 1; + bIsoFrom = -1; /* empty */ + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + } + break; + default: + ret = RI_ERR_SYNTAX; + break; + } + break; + + default: + ret = RI_ERR_SYNTAX; + break; + } + if ( ret < 0 ) { + goto exit_function; + } + /* copy */ + for ( i = 0; i < val; i ++ ) { + ret = CopySegment( pInChI+iComponent+i, pInChIFrom+iComponent+i, nCpyType, bIsoTo, bIsoFrom ); + if ( !ret ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + } + if ( ret < 0 ) { + goto exit_function; + } + if ( bIsoFrom >= 0 ) { + INChI_Stereo *pStereoTo = bIsoTo? pInChI[iComponent+i].StereoIsotopic : pInChI[iComponent+i].Stereo; + if ( pStereoTo ) { + pStereoTo->nCompInv2Abs = NO_VALUE_INT; /* in case there in no /m segment after this */ + } + } + } + + mpy_component = val; + goto end_main_cycle; + } else + /* regular multiplier */ + if ( (p = strchr(pStart, '*')) && p < pEnd ) { + mpy_component = (int)inchi_strtol( pStart, &q, 10 ); + if ( p != q ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + p ++; /* move to the 1st character of the component */ + } else { + mpy_component = 1; + p = pStart; + } +#if (FIX_DALKE_BUGS == 1) + if ( iComponent + mpy_component > nNumComponents ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } +#endif + pStart = p; + if ( p < pEnd && *pbAbc == -1 ) { + /* check if compressed InChI */ + *pbAbc = isupper( UCINT *p)? 1 : 0; + } + base = (*pbAbc==1)? ALPHA_BASE : 10; + /* process the componnt: at1p,at1p,... */ + /* pass 1: find number of stereoatoms */ + if ( *pbAbc == 1 ) { + for ( p = pStart, iAtom = 0; p < pEnd; iAtom ++ ) { + if ( (nAtom1 = (AT_NUMB)inchi_strtol( p, &p, base ) ) && + (atomParity = (int)inchi_strtol( p, &p, 10), + AB_MIN_KNOWN_PARITY <= atomParity && atomParity <= AB_MAX_KNOWN_PARITY) ) { + ; /* okay */ + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + if ( nAtom1 > pInChI[iComponent].nNumberOfAtoms ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + } + } else { + for ( p = pStart, iAtom = 0; p < pEnd; iAtom ++, p += (*p == ',') ) { + nAtom1 = (AT_NUMB)inchi_strtol( p, &q, 10 ); + p = q+1; + if ( !nAtom1 || + nAtom1 > pInChI[iComponent].nNumberOfAtoms || + !(r = strchr( parity_type, *q) ) ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + } + } + if ( p != pEnd ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + len = iAtom; + + /* memory allocation */ + + pStereo = bIso? &pInChI[iComponent].StereoIsotopic : &pInChI[iComponent].Stereo; + + if ( !*pStereo ) { + if ( !(*pStereo = (INChI_Stereo *) inchi_calloc( 1, sizeof(**pStereo) ) ) ) { + ret = RI_ERR_ALLOC; /* memory allocation failed */ + goto exit_function; + } + } + if ( pStereo[0]->t_parity || pStereo[0]->nNumberOfStereoCenters || + pStereo[0]->nNumber ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + /* allocate sp3 stereo */ + if ( !(pStereo[0]->t_parity = (S_CHAR *)inchi_calloc( len+1, sizeof(pStereo[0]->b_parity[0]) ) ) || + !(pStereo[0]->nNumber = (AT_NUMB *)inchi_calloc( len+1, sizeof(pStereo[0]->nNumber[0]) ) ) ) { + /* cleanup */ + if ( pStereo[0]->t_parity ) { + INCHI_HEAPCHK + inchi_free( pStereo[0]->t_parity ); + pStereo[0]->t_parity = NULL; + } + if ( pStereo[0]->nNumber ) { + INCHI_HEAPCHK + inchi_free( pStereo[0]->nNumber ); + pStereo[0]->nNumber = NULL; + } + ret = RI_ERR_ALLOC; /* memory allocation failed */ + goto exit_function; + } + + /* pass 2: store stereocenters */ + if ( *pbAbc == 1 ) { + for ( p = pStart, iAtom = 0; p < pEnd; iAtom ++ ) { + if ( (nAtom1 = (AT_NUMB)inchi_strtol( p, &p, base ) ) && + (atomParity = (int)inchi_strtol( p, &p, 10), + AB_MIN_KNOWN_PARITY <= atomParity && atomParity <= AB_MAX_KNOWN_PARITY) ) { + ; /* okay */ + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + if ( nAtom1 > pInChI[iComponent].nNumberOfAtoms ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + pStereo[0]->t_parity[iAtom] = atomParity; + pStereo[0]->nNumber[iAtom] = nAtom1; + if ( iAtom && !(pStereo[0]->nNumber[iAtom-1] < nAtom1) ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + } + } else { + for ( p = pStart, iAtom = 0; p < pEnd; iAtom ++, p += (*p==',') ) { + nAtom1 = (AT_NUMB)inchi_strtol( p, &q, 10 ); + if ( !(r = strchr( parity_type, *q) ) ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + p = q+1; + atomParity = (int) (r - parity_type) + 1; + pStereo[0]->t_parity[iAtom] = atomParity; + pStereo[0]->nNumber[iAtom] = nAtom1; + if ( iAtom && !(pStereo[0]->nNumber[iAtom-1] < nAtom1) ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + } + } + pStereo[0]->nNumberOfStereoCenters = iAtom; + /*if ( iAtom ) {*/ + pStereo[0]->nCompInv2Abs = NO_VALUE_INT; /* unknown yet */ + /*}*/ + + if ( p != pEnd ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + + /* multiplier */ + for ( i = 1; i < mpy_component; i ++ ) { + ret = CopySegment( pInChI+iComponent+i, pInChI+iComponent, nCpyType, bIso, bIso ); + if ( !ret ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + } + if ( ret < 0 ) { + goto exit_function; + } + ret = CopySegment( pInChI+iComponent+i, pInChI+iComponent, CPY_SP3_M, bIso, bIso ); + if ( !ret ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + } + if ( ret < 0 ) { + goto exit_function; + } + } + +end_main_cycle: + iComponent += mpy_component; + if ( *pEnd ) { + pStart = pEnd+1; + continue; + } else { + break; + } + } + if ( nNumComponents != iComponent ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + ret = iComponent + 1; + +exit_function: + return ret; +} + + +/* + Parse InChI layer '/b' +*/ + +int ParseSegmentSp2( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[], int state, int *pbAbc ) +{ + /* Pass 1: count bonds and find actual numbers of atom */ + int i, mpy_component, val; + int nNumComponents, iComponent, len, iBond; + AT_NUMB nAtom1, nAtom2; + int bondParity; + const char *p, *q, *t, *pStart, *pEnd, *r; + int ret=0; + INChI *pInChI = pInpInChI[bMobileH]; + INChI *pInChIFrom=NULL; + /* + INChI_Stereo *Stereo = NULL; + INChI_Stereo *StereoOther = NULL; + */ + INChI_Stereo **pStereo = NULL; + + const char mult_type[] = "mnMNe"; + const char parity_type[] = "-+u?"; + int bIsoTo, bIsoFrom, nCpyType = CPY_SP2; + int bIso = (state==IST_MOBILE_H_ISO_SP2 || state==IST_FIXED_H_ISO_SP2); + int base = 10; + + if ( !bIso && state != IST_MOBILE_H_SP2 && state != IST_FIXED_H_SP2 ) { + return RI_ERR_PROGR; /* program error */ + } + + if ( str[0] != 'b' ) + return 0; + + pStart = str+1; + iComponent = 0; + nNumComponents = ppnNumComponents[bMobileH]; + + if ( !*pStart ) { + /* creaste empty sp3 segment which means no sp3 */ + for ( iComponent = 0; iComponent < nNumComponents; iComponent ++ ) { + INChI *pIsoInChI = &pInChI[iComponent]; + pStereo = bIso? &pIsoInChI->StereoIsotopic : &pIsoInChI->Stereo; + if ( *pStereo && (pStereo[0]->b_parity || pStereo[0]->nNumberOfStereoBonds || + pStereo[0]->nBondAtom1 || pStereo[0]->nBondAtom2 ) ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + /* allocate empty sp3 stereo */ + ret = CopySegment( pIsoInChI, NULL, CPY_SP2, bIso, -1); + if ( ret < 0 ) { + goto exit_function; + } + } + ret = nNumComponents+1; + goto exit_function; + } + + while( 1 ) { + + /* cycle over components */ + if ( !(pEnd = strchr( pStart, ';' )) ) { + pEnd = pStart + strlen(pStart); + } + + if ( (isdigit(*pStart) && + 0 < (val = (int)inchi_strtol( pStart, &q, 10)) || + (q = pStart, val=1))&& + (t=strchr(mult_type, *q)) && q+1 == pEnd ) { + /* process the abbreviation */ + ret = 0; +#if (FIX_DALKE_BUGS == 1) + if ( iComponent + val > nNumComponents ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } +#endif + switch( bMobileH ) { + case TAUT_YES: + switch( state ) { + case IST_MOBILE_H_ISO_SP2: + if ( *q == 'm' ) { + /* copy from mobile H to isotopic mobile H */ + pInChIFrom = pInChI; + bIsoTo = 1; + bIsoFrom = 0; + } else + if ( *q == 'e' ) { + /* copy from mobile H to isotopic mobile H */ + pInChIFrom = pInChI; + bIsoTo = 1; + bIsoFrom = -1; /* empty */ + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + } + break; + default: + ret = RI_ERR_SYNTAX; + break; + } + break; + case TAUT_NON: + switch( state ) { + case IST_FIXED_H_SP2: + if ( *q == 'm' ) { + /* copy from mobile H to fixed H */ + pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; + bIsoTo = 0; + bIsoFrom = 0; + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + } + break; + case IST_FIXED_H_ISO_SP2: + if ( *q == 'm' ) { + /* copy from mobile H to fixed isotopic H */ + pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; + bIsoTo = 1; + bIsoFrom = 0; + } else + if ( *q == 'M' ) { + /* copy from isotopic mobile H to fixed isotopic H */ + pInChIFrom = pInpInChI[ALT_TAUT(bMobileH)]; + bIsoTo = 1; + bIsoFrom = 1; + } else + if ( *q == 'n' ) { + /* copy from fixed H to fixed isotopic H */ + pInChIFrom = pInChI; + bIsoTo = 1; + bIsoFrom = 0; + } else + if ( *q == 'e' ) { + /* copy from mobile H to isotopic mobile H */ + pInChIFrom = pInChI; + bIsoTo = 1; + bIsoFrom = -1; /* empty */ + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + } + break; + default: + ret = RI_ERR_SYNTAX; + break; + } + break; + + default: + ret = RI_ERR_SYNTAX; + break; + } + if ( ret < 0 ) { + goto exit_function; + } + /* copy */ + for ( i = 0; i < val; i ++ ) { + ret = CopySegment( pInChI+iComponent+i, pInChIFrom+iComponent+i, nCpyType, bIsoTo, bIsoFrom ); + if ( !ret ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + } + if ( ret < 0 ) { + goto exit_function; + } + } + mpy_component = val; + goto end_main_cycle; + } else + /* regular multiplier */ + if ( (p = strchr(pStart, '*')) && p < pEnd ) { + mpy_component = (int)inchi_strtol( pStart, &q, 10 ); + if ( p != q ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + + p ++; /* move to the 1st character of the component */ + } else { + mpy_component = 1; + p = pStart; + } +#if (FIX_DALKE_BUGS == 1) + if ( iComponent + mpy_component > nNumComponents ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } +#endif + pStart = p; + if ( p < pEnd && *pbAbc == -1 ) { + /* check if compressed InChI */ + *pbAbc = isupper( UCINT *p)? 1 : 0; + } + base = (*pbAbc==1)? ALPHA_BASE : 10; + if ( *pbAbc == 1 ) { + /* process the componnt: at1-at2p,at1-at2p,... */ + /* pass 1: find number of stereobonds */ + for ( p = pStart, iBond = 0; p < pEnd; iBond ++ ) { + /* atoms 1, 2, and parity */ + if ( (nAtom1 = (AT_NUMB)inchi_strtol( p, &p, base ) ) && + (nAtom2 = (AT_NUMB)inchi_strtol( p, &p, base ) ) && + (bondParity = (int)inchi_strtol( p, &p, 10), + AB_MIN_KNOWN_PARITY <= bondParity && bondParity <= AB_MAX_KNOWN_PARITY) ) { + ; /* okay */ + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + if ( nAtom1 <= nAtom2 || + nAtom1 > pInChI[iComponent].nNumberOfAtoms ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + } + } else { + /* process the componnt: at1-at2p,at1-at2p,... */ + /* pass 1: find number of stereobonds */ + for ( p = pStart, iBond = 0; p < pEnd; iBond ++, p += (*p==',') ) { + nAtom1 = (AT_NUMB)inchi_strtol( p, &q, 10 ); + if ( *q != '-' ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + p = q+1; + nAtom2 = (AT_NUMB)inchi_strtol( p, &q, 10 ); + if ( !nAtom1 || !nAtom2 || + nAtom1 <= nAtom2 || + nAtom1 > pInChI[iComponent].nNumberOfAtoms || + !(r = strchr( parity_type, *q) ) ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + p = q+1; + } + } + + if ( p != pEnd ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + len = iBond; + + /* memory allocation */ + + pStereo = bIso? &pInChI[iComponent].StereoIsotopic : &pInChI[iComponent].Stereo; + + if ( !*pStereo ) { + if ( !(*pStereo = (INChI_Stereo *) inchi_calloc( 1, sizeof(**pStereo) ) ) ) { + ret = RI_ERR_ALLOC; /* memory allocation failed */ + goto exit_function; + } + } + if ( pStereo[0]->b_parity || pStereo[0]->nNumberOfStereoBonds || + pStereo[0]->nBondAtom1 || pStereo[0]->nBondAtom2 ) { + ret = RI_ERR_SYNTAX; /* syntax error: bonds have already been allocated */ + goto exit_function; + } + /* allocate sp2 stereo */ + if ( !(pStereo[0]->b_parity = (S_CHAR *)inchi_calloc( len+1, sizeof(pStereo[0]->b_parity[0]) ) ) || + !(pStereo[0]->nBondAtom1 = (AT_NUMB *)inchi_calloc( len+1, sizeof(pStereo[0]->nBondAtom1[0]) ) ) || + !(pStereo[0]->nBondAtom2 = (AT_NUMB *)inchi_calloc( len+1, sizeof(pStereo[0]->nBondAtom2[0]) ) ) ) { + /* cleanup */ + if ( pStereo[0]->b_parity ) { + INCHI_HEAPCHK + inchi_free( pStereo[0]->b_parity ); + pStereo[0]->b_parity = NULL; + } + if ( pStereo[0]->nBondAtom1 ) { + INCHI_HEAPCHK + inchi_free( pStereo[0]->nBondAtom1 ); + pStereo[0]->nBondAtom1 = NULL; + } + if ( pStereo[0]->nBondAtom2 ) { + INCHI_HEAPCHK + inchi_free( pStereo[0]->nBondAtom2 ); + pStereo[0]->nBondAtom2 = NULL; + } + INCHI_HEAPCHK + ret = RI_ERR_ALLOC; /* memory allocation failed */ + goto exit_function; + } + + /* pass 2: store stereobonds */ + if ( *pbAbc == 1 ) { + for ( p = pStart, iBond = 0; p < pEnd; iBond ++ ) { + if ( (nAtom1 = (AT_NUMB)inchi_strtol( p, &p, base ) ) && + (nAtom2 = (AT_NUMB)inchi_strtol( p, &p, base ) ) && + (bondParity = (int)inchi_strtol( p, &p, 10), + AB_MIN_KNOWN_PARITY <= bondParity && bondParity <= AB_MAX_KNOWN_PARITY) ) { + ; /* okay */ + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + pStereo[0]->b_parity[iBond] = bondParity; + pStereo[0]->nBondAtom1[iBond] = nAtom1; + pStereo[0]->nBondAtom2[iBond] = nAtom2; + + if ( iBond && + !(pStereo[0]->nBondAtom1[iBond-1] < nAtom1 || + pStereo[0]->nBondAtom1[iBond-1] == nAtom1 && + pStereo[0]->nBondAtom2[iBond-1] < nAtom2 ) ) { + ret = RI_ERR_SYNTAX; /* syntax error: wrong bond order */ + goto exit_function; + } + } + } else { + for ( p = pStart, iBond = 0; p < pEnd; iBond ++, p += (*p==',') ) { + nAtom1 = (AT_NUMB)inchi_strtol( p, &q, 10 ); + if ( *q != '-' ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + p = q+1; + nAtom2 = (AT_NUMB)inchi_strtol( p, &q, 10 ); + if ( !(r = strchr( parity_type, *q) ) ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + p = q+1; + bondParity = (int) (r - parity_type) + 1; + pStereo[0]->b_parity[iBond] = bondParity; + pStereo[0]->nBondAtom1[iBond] = nAtom1; + pStereo[0]->nBondAtom2[iBond] = nAtom2; + + if ( iBond && + !(pStereo[0]->nBondAtom1[iBond-1] < nAtom1 || + pStereo[0]->nBondAtom1[iBond-1] == nAtom1 && + pStereo[0]->nBondAtom2[iBond-1] < nAtom2 ) ) { + ret = RI_ERR_SYNTAX; /* syntax error: wrong bond order */ + goto exit_function; + } + } + } + pStereo[0]->nNumberOfStereoBonds = iBond; + + if ( p != pEnd ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + + /* multiplier */ + for ( i = 1; i < mpy_component; i ++ ) { + ret = CopySegment( pInChI+iComponent+i, pInChI+iComponent, nCpyType, bIso, bIso ); + if ( ret < 0 ) { + goto exit_function; + } + } + +end_main_cycle: + iComponent += mpy_component; + if ( *pEnd ) { + pStart = pEnd+1; + continue; + } else { + break; + } + } + if ( nNumComponents != iComponent ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + ret = iComponent + 1; + +exit_function: + return ret; +} + + +/* + Parse InChI layer '/p' +*/ +int ParseSegmentProtons( const char *str, int bMobileH, REM_PROTONS nNumProtons[], int ppnNumComponents[] ) +{ + /* Pass 1: count bonds and find actual numbers of atom */ + int val; + const char *q, *pStart, *pEnd; + int ret; + + if ( str[0] != 'p' ) + return 0; + + pStart = str+1; + + while( 1 ) { + /* cycle over components */ + if ( !(pEnd = strchr( pStart, ';' )) ) { + pEnd = pStart + strlen(pStart); + } + + if ( pStart[0] == '+' && isdigit( UCINT pStart[1] ) ) { + val = (int)inchi_strtol( pStart+1, &q, 10 ); + } else + if ( pStart[0] == '-' && isdigit( UCINT pStart[1] ) ) { + val = -(int)inchi_strtol( pStart+1, &q, 10 ); + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + if ( !val ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + nNumProtons[bMobileH].nNumRemovedProtons = val; + if ( *pEnd || q != pEnd ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } else { + break; + } + } + ret = 1; + +exit_function: + return ret; +} + + +/* + Parse InChI layer '/z' +*/ +int ParseSegmentPolymer( const char *str, + int bMobileH, + REM_PROTONS nNumProtons[], + int ppnNumComponents[], + int na_total, + int nb_total, + int bInchi2Struct, + OrigAtDataPolymer **ppPolymer, + OrigAtDataV3000 **ppV3000 ) +{ + int iunit, val, ret, prev, is_range; + const char *p=NULL, *q=NULL, *pStart=NULL, *pEnd=NULL; + const char comma = ',', dot = '.', dash = '-', lt_par = '(', rt_par = ')', *p0; + AT_NUMB num_atom; + int curr_atom, type=-1, subtype=-1, conn=-1; + INT_ARRAY alist; + OrigAtDataPolymer *pd = NULL; + + if ( str[0] != 'z' ) + return 0; + + if ( IntArray_Alloc( &alist, 4 ) ) + { + return RI_ERR_ALLOC; + } + + if ( *ppPolymer ) + OrigAtDataPolymer_Free( *ppPolymer ); + + pd = *ppPolymer = (OrigAtDataPolymer *) + inchi_calloc( 1, sizeof(OrigAtDataPolymer) ); + (*ppPolymer)->star_atoms = NULL; + + if ( !pd ) + { ret = RI_ERR_ALLOC; goto exit_function; } + + + if ( !bInchi2Struct ) + { ret = RI_ERR_SYNTAX; goto exit_function; } + + /* Count units */ + pd->n = 1; + p = str+1; + while ( p = strchr( p, ';' ) ) + { + p++; + pd->n++; + } + pd->units = (OrigAtDataPolymerUnit**) + inchi_calloc( pd->n, sizeof(OrigAtDataPolymerUnit *) ); + if ( !pd->units ) + { ret = RI_ERR_ALLOC; goto exit_function; } + + pStart = str; + pStart++; + if ( !pStart ) + { ret = RI_ERR_PROGR; goto exit_function; } + + iunit = 0; + while( *pStart ) + { + OrigAtDataPolymerUnit *unit=NULL; + + if ( *pStart==';' ) + pStart++; + + if ( !(pEnd = strchr( pStart, ';' )) ) + { + pEnd = pStart + strlen(pStart); + } + if ( isdigit( UCINT pStart[0] ) ) + { + val = (int)inchi_strtol( pStart+0, &q, 10 ); + type = val/100; + subtype = (val - (type*100) )/10; + conn = ( val - (type*100 + subtype*10) ); + } + if ( *q != '-' ) + { + ret = RI_ERR_SYNTAX; + goto exit_function; + } + q++; + prev = 0; + is_range = 0; + for ( p = q, curr_atom = 0; p < pEnd && *p!='('; curr_atom ++ ) + { + num_atom = (AT_NUMB)inchi_strtol( p, &p, 10 ); + if ( !num_atom || num_atom > na_total ) + { ret = RI_ERR_SYNTAX; goto exit_function; } + if ( is_range ) + { + int a; + for (a=prev+1; a<=num_atom; a++) + if ( 0!=IntArray_Append( &alist, a ) ) + { ret = RI_ERR_ALLOC; goto exit_function; } + is_range = 0; + prev = 0; + } + else + { + if ( 0!=IntArray_Append( &alist, num_atom )) + { ret = RI_ERR_ALLOC; goto exit_function; } + prev = num_atom; + } + if ( *p == '-') + { + p++; + is_range = 1; + } + else if ( *p == ',') p++; + } + + if ( alist.used ) + { + unit = OrigAtDataPolymerUnit_New( 4, 0, /* maxatoms, maxbonds=0 now */ + iunit + 1, iunit + 1, /* id, label */ + type, subtype, conn, + "", /* smt */ + alist.used, &alist, /* alist.used, &alist, */ + 0, NULL, /* blist.used, &blist, */ + 0, NULL /* npsbonds, int **psbonds */ + ); + + if ( !unit ) + { ret = RI_ERR_ALLOC; goto exit_function; } + pd->units[iunit] = unit; + IntArray_Reset( &alist ); + iunit++; + } + + + if ( *p == lt_par ) + { + /* Structure-based representn, read crossing bonds information */ + const int nothing = 0, endgroups = 1, stars = 2, stars_ring = 3, stars_bond=4, stars_atom = 5; + int have = nothing; + int res, ib, err=0; + INT_ARRAY numlist; + + if ( IntArray_Alloc( &numlist, 4 ) ) + { + ret = RI_ERR_ALLOC; + goto exit_function; + } + + p0 = p; + while ( *(++p) ) + { + if ( *p == '-' ) { have = endgroups; break; } + if ( *p == ',' ) { have = stars; break; } + } + if ( have == stars ) + { + while ( *(++p) ) + { + if ( *p == ',' ) { have = stars_ring; break; } + if ( *p == '.' ) { have = stars_bond; break; } + if ( *p == ')' ) { have = stars_atom; break; } + } + } + p = p0; + + unit->closeable = CLOSING_SRU_NOT_APPLICABLE; + if ( have == endgroups ) + { + /* Read end groups notation */ + p = ParseSegmentReadDelimitedNumbers( p, pEnd, &numlist, dash, comma, &res ); + if ( res == 1 && numlist.used == 2 ) + p = ParseSegmentReadDelimitedNumbers( p, pEnd, &numlist, dash, rt_par, &res ); + if ( res == 1 && numlist.used == 4 ) + { + pStart = p + 1; + unit->nb = 2; + unit->blist = (int *) inchi_calloc( 2*unit->nb, sizeof(int) ); + if (!unit->blist ) { ret = RI_ERR_ALLOC; IntArray_Free( &numlist ); goto exit_function; } + unit->blist[0] = numlist.item[0]; unit->blist[1] = numlist.item[1]; + unit->blist[2] = numlist.item[2]; unit->blist[3] = numlist.item[3]; + IntArray_Free( &numlist ); + continue; + } + } + else if ( have == stars_ring || + have == stars_bond || + have == stars_atom ) + { + /* Read star atoms - phase shiftable bonds notation */ + IntArray_Reset( &numlist ); + p = ParseSegmentReadDelimitedNumbers( p, pEnd, &numlist, comma, dash, &res ); + if ( res != 1 || numlist.used != 2 ) + { ret = RI_ERR_SYNTAX; IntArray_Free( &numlist ); goto exit_function; } + /* OK, we got star atom numbers */ + if ( have == stars_ring ) + { + p = ParseSegmentReadDelimitedNumbers( p, pEnd, &numlist, comma, rt_par, &res ); + if ( res != 1 || numlist.used < 4 ) { ret = RI_ERR_SYNTAX; IntArray_Free( &numlist ); goto exit_function; } + unit->closeable = CLOSING_SRU_RING; + unit->npsbonds = (numlist.used - 2)/2; + } + else if ( have == stars_bond ) + { + p = ParseSegmentReadDelimitedNumbers( p, pEnd, &numlist, dot, rt_par, &res ); + if ( res != 1 || numlist.used < 4 ) { ret = RI_ERR_SYNTAX; IntArray_Free( &numlist ); goto exit_function; } + unit->closeable = CLOSING_SRU_HIGHER_ORDER_BOND; + unit->npsbonds = 1; + } + else if ( have == stars_atom ) + { + int num = inchi_strtol( (const char *) ++p, (const char **) &p, 10 ); + if ( *p!= rt_par ) { ret = RI_ERR_SYNTAX; IntArray_Free( &numlist ); goto exit_function; } + IntArray_Append( &numlist, num ); + unit->closeable = CLOSING_SRU_DIRADICAL; + unit->npsbonds = 1; + } + } + else + { ret = RI_ERR_SYNTAX; IntArray_Free( &numlist ); goto exit_function; } + + unit->star1 = numlist.item[0]; + unit->star2 = numlist.item[1]; + unit->maxpsbonds = inchi_max( unit->maxpsbonds, unit->npsbonds ); + err = imat_new( unit->maxpsbonds, 2, &(unit->psbonds) ); + for (ib=0; ibnpsbonds; ib++) + { + unit->psbonds[ib][0] = numlist.item[ib*2 + 2 ]; + if ( numlist.used != 3 ) + unit->psbonds[ib][1] = numlist.item[ib*2 + 3 ]; + else + unit->psbonds[ib][1] = unit->psbonds[ib][0]; + } + if ( unit->npsbonds > 0 ) + { + /*unit->closeable = 1;*/ + unit->star1 = numlist.item[0]; + unit->star2 = numlist.item[1]; + } + + pStart = p + 1; + IntArray_Free( &numlist ); + continue; + } + + if ( *p == ';') p++; + q = p; + pStart = p; + } + + + pd->really_do_phase_shift = 1; + pd->is_in_reconn = 0; + OrigAtDataPolymer_DebugTrace( pd ); + ret = pd->n; + + +exit_function: + + IntArray_Free( &alist ); + if ( ret == RI_ERR_ALLOC || ret == RI_ERR_SYNTAX || ret == RI_ERR_PROGR ) + { + /*FreeExtOrigAtData( pd, pv );*/ + } + return ret; +} + +/* + Read sequence of integer numbers from str into growing int array numlist + until NULL, EOL or 'c_stop' symbol occurred, whichever is the first. + Numbers are assumed to be delimited with commas. + + NB: on success, returns 1. +*/ +char * ParseSegmentReadDelimitedNumbers( const char *str, const char *pEnd, INT_ARRAY *numlist, char c_delim, char c_stop, int *ret ) +{ + char *p=NULL, *pStart; + int num, curr_atom = 0; + + *ret = 1; + + if ( !str ) + { *ret = -1; return NULL; } + + pStart = (char *) (str + 1); + while( *pStart ) + { + for ( p = pStart, curr_atom = 0; p < pEnd && *p!=c_stop; curr_atom++ ) + { + num = (AT_NUMB)inchi_strtol( (const char *) p, (const char **) &p, 10 ); + { + if ( 0!=IntArray_Append( numlist, num ) ) + { + *ret = RI_ERR_SYNTAX; + return p; + } + } + if ( *p == c_delim ) + p++; + else if ( *p == c_stop ) + return p; + else + { *ret = -1; return NULL; } + } + if ( *p == c_stop ) + return p; + } + + return p; +} + +/* + Parse InChI layer '/q' +*/ +int ParseSegmentCharge( const char *str, int bMobileH, INChI *pInpInChI[], int ppnNumComponents[] ) +{ + /* Pass 1: count bonds and find actual numbers of atom */ + int i, mpy_component, val; + int nNumComponents, iComponent; + const char *p, *q, *t, *pStart, *pEnd; + int ret; + INChI *pInChI = pInpInChI[bMobileH]; + const char mult_type[] = "mnMNe"; + + if ( str[0] != 'q' ) { + return 0; + } + + pStart = str+1; + iComponent = 0; + nNumComponents = ppnNumComponents[bMobileH]; + + if ( !*pStart && bMobileH == TAUT_NON ) { + for ( i = 0; i < nNumComponents; i ++ ) { + pInChI[i].nTotalCharge = NO_VALUE_INT; + } + return nNumComponents+1; + } + + while( 1 ) { + /* cycle over components */ + if ( !(pEnd = strchr( pStart, ';' )) ) { + pEnd = pStart + strlen(pStart); + } + + if ( (isdigit(UCINT *pStart) && + 0 < (val = (int)inchi_strtol( pStart, &q, 10)) || + (q = pStart, val=1) )&& + (t=strchr(mult_type, *q)) && q+1 == pEnd ) { + /* process the abbreviation */ + + switch( bMobileH ) { + + case TAUT_YES: + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + + case TAUT_NON: + if ( *q != 'm' || + iComponent + val > nNumComponents || + iComponent + val > ppnNumComponents[TAUT_YES] ) { + + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + for ( i = 0; i < val; i ++ ) { + /* avoid 0 which means "omitted" */ + pInChI[iComponent+i].nTotalCharge = pInpInChI[TAUT_YES][iComponent+i].nTotalCharge? + pInpInChI[TAUT_YES][iComponent+i].nTotalCharge : + NO_VALUE_INT; + } + mpy_component = val; + goto end_main_cycle; + + default: + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + }else + if ( (p = strchr(pStart, '*')) && p < pEnd ) { + mpy_component = (int)inchi_strtol( pStart, &q, 10 ); + if ( p != q ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + p ++; + } else { + mpy_component = 1; + p = pStart; + } +#if ( FIX_DALKE_BUGS == 1 ) + if ( mpy_component + iComponent > nNumComponents || mpy_component <= 0 ) { + ret = RI_ERR_SYNTAX; /* syntax error: too many components in charge layer */ + goto exit_function; + } +#endif + pStart = p; + + if ( pStart < pEnd ) { + if ( pStart[0] == '+' && isdigit( UCINT pStart[1] ) ) { + val = (int)inchi_strtol( pStart+1, &q, 10 ); + pStart = q; + } else + if ( pStart[0] == '-' && isdigit( UCINT pStart[1] ) ) { + val = -(int)inchi_strtol( pStart+1, &q, 10 ); + pStart = q; + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } +#if ( FIX_DALKE_BUGS == 1 ) + if ( val < -256 || val > 256 ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } +#endif + if ( !val ) { + if ( pStart != pEnd ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + if ( bMobileH == TAUT_NON ) { + val = NO_VALUE_INT; /* avoid 0 which means "omitted" */ + } + } + } else { + val = NO_VALUE_INT; + } + for ( i = 0; i < mpy_component; i ++ ) { + pInChI[iComponent+i].nTotalCharge = val; + } + +end_main_cycle: + iComponent += mpy_component; + if ( *pEnd ) { + pStart = pEnd+1; + continue; + } else { + break; + } + } + + if ( nNumComponents != iComponent ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + ret = iComponent + 1; + +exit_function: + return ret; +} + + +/* + Parse InChI layer '/h' +*/ +int ParseSegmentMobileH( const char *str, int bMobileH, INChI *pInpInChI[], int pnNumComponents[], int *pbAbc ) +{ +#define nNum_H( ICOMPONENT ) ((bMobileH==TAUT_YES)? pInChI[ICOMPONENT].nNum_H : pInChI[ICOMPONENT].nNum_H_fixed) + /* Pass 1: count bonds and find actual numbers of atom */ + int i, mpy_component, num_H, num_Minus, val, num_Atoms, numCtAtoms, tg_alloc_len, len, len2; + int num_H_component, num_H_formula, num_taut_H_component, num_H_InChI, ret2; + int nNumComponents, iComponent, nNumBonds, lenTautomer, tg_pos_Tautomer, iTGroup; + const char *p, *q, *h, *t, *p1, *pTaut, *pStart, *pEnd; + AT_NUMB curAtom, nxtAtom; + int num_open, state, ret, nAltMobileH = ALT_TAUT(bMobileH); + INChI *pInChI = pInpInChI[bMobileH]; + INChI *pAltInChI = pInpInChI[nAltMobileH]; + int base = 10; + + num_H = -999; /* impossible value */ + num_Minus = -999; /* impossible value */ + tg_pos_Tautomer = -999; /* impossible value */ + + /* number of immobile H is always allocated; immobile H are present in M layer only */ + nNumComponents = pnNumComponents[bMobileH]; + for ( i = 0; i < nNumComponents; i ++ ) { + len = pInChI[i].nNumberOfAtoms; + if ( bMobileH == TAUT_NON && i < pnNumComponents[nAltMobileH] ) { + if ( len < pAltInChI[i].nNumberOfAtoms ) { + len = pAltInChI[i].nNumberOfAtoms; + if ( pInChI[i].nNum_H ) { + inchi_free( pInChI[i].nNum_H ); + pInChI[i].nNum_H = NULL; + } + } + } + len ++; + if ( !pInChI[i].nNum_H && /* allocate immobile H segment if it has not been allocated yet */ + !(pInChI[i].nNum_H = (S_CHAR *)inchi_calloc( len, sizeof(pInChI[0].nNum_H[0]) )) ) { + ret = RI_ERR_ALLOC; /* allocation error */ + goto exit_function; + } + /* copy immobile H from Mobile-H layer to Fixed-H layer */ + if ( bMobileH == TAUT_NON && i < pnNumComponents[nAltMobileH] ) { + memcpy( pInChI[i].nNum_H, pAltInChI[i].nNum_H, (len-1) * sizeof(pInChI[0].nNum_H[0]) ); + } + } + + if ( str[0] != 'h' ) + return 0; + + /* Read Hydrogen info in 1 pass */ + pStart = str+1; + iComponent = 0; + nNumComponents = pnNumComponents[bMobileH]; + + while( 1 ) { + /* cycle over components */ + if ( !(pEnd = strchr( pStart, ';' )) ) { + pEnd = pStart + strlen(pStart); + } + if ( (p = strchr(pStart, '*')) && p < pEnd ) { + mpy_component = (int)inchi_strtol( pStart, &q, 10 ); +#if ( FIX_DALKE_BUGS == 1 ) + if ( p != q || !isdigit(UCINT *pStart) ) /* prevent non-positive multipliers */ +#else + if ( p != q ) +#endif + { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + p ++; + } else { + mpy_component = 1; + p = pStart; + } + pStart = p; + /* Pass 1.1 parse a component */ + num_open = 0; + state='\0'; /* initial state */ + nNumBonds = 0; + curAtom = 0; + numCtAtoms = pInChI[iComponent].nNumberOfAtoms; + if ( bMobileH == TAUT_NON && iComponent < pnNumComponents[nAltMobileH] ) { + numCtAtoms = pAltInChI[iComponent].nNumberOfAtoms; + } + + if ( p < pEnd && *pbAbc == -1 ) { + /* check if compressed InChI */ + *pbAbc = (*p == ',' || isupper( UCINT *p))? 1 : 0; + } + base = (*pbAbc==1)? ALPHA_BASE : 10; + + /* immobile H */ + t = pTaut = (*pbAbc==1)? strchr( p, ',' ) : strchr( p, '(' ); /* locate the first tautomer group character */ + + if ( t && bMobileH == TAUT_NON ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + if ( !pTaut || pTaut > pEnd ) { + pTaut = pEnd; + t = NULL; /* found no tautomeric group for this component */ + } + for ( i = 0; i < mpy_component; i ++ ) { + if ( bMobileH == TAUT_NON ) { + /* allocate nNum_H_fixed */ + if ( pInChI[iComponent+i].nNum_H_fixed ) { + ret = RI_ERR_PROGR; /* program error */ + goto exit_function; + } + if ( iComponent+i < pnNumComponents[nAltMobileH] ) { + len = inchi_max(pInChI[iComponent+i].nNumberOfAtoms, pAltInChI[iComponent+i].nNumberOfAtoms)+1; + } else { + len = pInChI[iComponent+i].nNumberOfAtoms + 1; + } + pInChI[iComponent+i].nNum_H_fixed = (S_CHAR *)inchi_calloc( len, sizeof(pInChI[0].nNum_H_fixed[0]) ); + if ( !pInChI[iComponent+i].nNum_H_fixed ) { + ret = RI_ERR_ALLOC; /* allocation error */ + goto exit_function; + } + /* compare nAtom */ + if ( iComponent+i < pnNumComponents[nAltMobileH] ) { + len2 = inchi_min(pInChI[iComponent+i].nNumberOfAtoms, pAltInChI[iComponent+i].nNumberOfAtoms); + if ( pInChI[iComponent+i].nAtom && len2 ) { + /* check */ + if ( memcmp( pInChI[iComponent+i].nAtom, pAltInChI[iComponent+i].nAtom, len2 * sizeof(pInChI[0].nAtom[0]) ) ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + } + /* allocate and copy atom if bridging H are present */ + if ( pInChI[iComponent+i].nNumberOfAtoms < pAltInChI[iComponent+i].nNumberOfAtoms ) { + if ( pInChI[iComponent+i].nAtom ) + inchi_free( pInChI[iComponent+i].nAtom ); + if ( !(pInChI[iComponent+i].nAtom = (U_CHAR *)inchi_calloc( len, sizeof(pInChI[0].nAtom[0]) ) ) ) { + ret = RI_ERR_ALLOC; /* allocation error */ + goto exit_function; + } + if ( len > 1 ) { + memcpy( pInChI[iComponent+i].nAtom, pAltInChI[iComponent+i].nAtom, (len-1) * sizeof(pInChI[0].nAtom[0]) ); + } + /* correct number of atoms including bridging H */ + pInChI[iComponent+i].nNumberOfAtoms = pAltInChI[iComponent+i].nNumberOfAtoms; + } + } + } + } + + if ( *pbAbc == 1 ) { + /* read numbers of H: XnYn... or XYn... */ + p = pStart; + tg_alloc_len = 0; + num_H_component = num_taut_H_component = 0; + while ( p < pTaut ) { + /* syntax check: atom number */ + if ( !*p || !isupper( UCINT *p ) ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + if ( curAtom = nxtAtom = (int)inchi_strtol( p, &q, base ) ) { + p = q; + if ( isupper( UCINT *p ) ) { + nxtAtom = (int)inchi_strtol( p, &q, base ); + p = q; + } + } + if ( curAtom > nxtAtom || nxtAtom > numCtAtoms || p > pTaut ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + /* number of H, may be negative */ + if ( !(num_H = (int)inchi_strtol( p, &q, 10 )) || q > pTaut ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + p = q; + /* set number of H */ + for ( i = curAtom; i <= nxtAtom; i ++ ) { + nNum_H(iComponent)[i-1] = num_H; + num_H_component += num_H; + } + } + if ( p != pTaut ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + } else { + /* read numbers of H: 1-2,3H2,4,5H3 */ + p = pStart; + tg_alloc_len = 0; + num_H_component = num_taut_H_component = 0; + while ( p < pTaut ) { + /* syntax check: atom number */ + if ( !*p || !isdigit( UCINT *p ) ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + /* number of H */ + h = p + strcspn( p, "Hh" ); + /*h = strchr( p, 'H' );*/ + if ( !*h || h >= pTaut ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + + /* + p = pTaut; + h = NULL; + break; */ /* no more H found */ + } + num_H = (*h == 'H')? 1 : (*h == 'h')? -1 : 0; + if ( !num_H ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + if ( h[1] && isdigit( UCINT h[1] ) ) { + num_H *= (int)inchi_strtol( h+1, &p1, 10 ); + } else { + p1 = h+1; /* next set of immobile H */ + } + if ( *p1 == ',' ) { + p1 ++; /* next H-subsegment; otherwise (H or ; or end of the segment */ + } + /* list of atoms that have num_H */ + while ( p < h ) { + if ( !*p || !isdigit( UCINT *p ) ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + nxtAtom = curAtom = (int)inchi_strtol( p, &q, 10 ); + if ( *q == '-' ) { + nxtAtom = (int)inchi_strtol( q+1, &q, 10 ); + } + /* consitency check */ + if ( !curAtom || curAtom > numCtAtoms || + nxtAtom < curAtom || nxtAtom > numCtAtoms ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + /* set number of H */ + for ( i = curAtom; i <= nxtAtom; i ++ ) { + nNum_H(iComponent)[i-1] = num_H; + num_H_component += num_H; + } + /* move to the next atom number if any */ + p = q; + if ( *p == ',' ) { + p ++; + } + } + + if ( p == h ) { + p = p1; + } else + if ( p == pTaut ) { + break; + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + } + } + + INCHI_HEAPCHK + /* ) -> (, H, N, [-, N,], AtNum,... AtNum) */ + lenTautomer = 0; + if ( p = t ) { + if ( *pbAbc == 1 ) { + /* tautomeric groups: pass 1 */ + iTGroup = 0; + state = ')'; /* init as if the prev. t-group just ended */ + num_Atoms = 0; + /* Tautomeric info storage */ + /* NumGroups; ((NumAt+2, NumH, Num(-), At1..AtNumAt),...); {INCHI_T_NUM_MOVABLE = 2} */ + /* Allocated length: [5*nNumberOfAtoms/2+1], see Alloc_INChI(...) */ + if ( *p == ',' ) { /* start t-group */ + p ++; + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + while ( p < pEnd ) { + /* start t-group */ + if ( !isdigit( UCINT *p ) || !(num_H = (int)inchi_strtol( p, &q, 10 ) ) || q > pEnd ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + p = q; + num_Minus = 0; + if ( *p == '-' ) { + p ++; + if ( isdigit( UCINT *p ) ) { + num_Minus = (int)inchi_strtol( p, &q, 10 ); + p = q; + } else { + num_Minus = 1; + } + } + if ( p >= pEnd ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + if ( !tg_alloc_len ) { + /* + --- header --- + [num_t_groups] + --- one t-group: --- + [len=group length no including this value] + [num_H] + [num_(-)] + [Endpoint(1),...,Endpoint(len-2)] + --- next t-group --- + ... + + Max. size = 1 + 3*max_num_t_groups + max_num_endpoints + + max_num_t_groups = num_at/2 + max_num_endpoints = num_at + + Max. size = 1 + 3*(num_at/2) + num_at = 1 + (5*num_at)/2 + 5 = 3 + INCHI_T_NUM_MOVABLE = 3 + num_types_of_attachments + + This does not include zero termination! + + */ + tg_alloc_len = ((3+INCHI_T_NUM_MOVABLE)*pInChI[iComponent].nNumberOfAtoms)/2+1; + for ( i = 0; i < mpy_component; i ++ ) { + pInChI[iComponent+i].nTautomer = (AT_NUMB*)inchi_calloc( tg_alloc_len+1, sizeof(pInChI->nTautomer[0])); + if ( !pInChI[iComponent+i].nTautomer ) { + ret = RI_ERR_ALLOC; /* allocation error */ + goto exit_function; + } + pInChI[iComponent+i].lenTautomer = 0; + } + tg_pos_Tautomer = 1; /* number atoms (NumAt+2) position */ + } else { + /* next t-group */ + tg_pos_Tautomer = lenTautomer; + } + if ( tg_pos_Tautomer+3 >= tg_alloc_len ) { + ret = RI_ERR_PROGR; /* wrong tautomer array length */ + goto exit_function; + } + pInChI[iComponent].nTautomer[tg_pos_Tautomer+1] = num_H; + pInChI[iComponent].nTautomer[tg_pos_Tautomer+2] = num_Minus; + lenTautomer = tg_pos_Tautomer+3; /* first atom number position */ + num_taut_H_component += num_H; + + while ( p < pEnd && isupper( UCINT *p) ) { + /* read list of tautomeric atoms */ + val = (int)inchi_strtol( p, &q, base ); + if ( lenTautomer >= tg_alloc_len || val > numCtAtoms ) { + ret = RI_ERR_PROGR; /* wrong tautomer array length */ + goto exit_function; + } + num_Atoms ++; + pInChI[iComponent].nTautomer[lenTautomer ++] = val; + p = q; + } + if ( !num_Atoms || p < pEnd && !isdigit( UCINT *p) ) { + ret = RI_ERR_PROGR; /* wrong tautomer array length */ + goto exit_function; + } + iTGroup ++; + pInChI[iComponent].nTautomer[tg_pos_Tautomer] = lenTautomer - tg_pos_Tautomer - 1; /* length of the rest of the t-group */ + pInChI[iComponent].lenTautomer = lenTautomer; + } + if ( !iTGroup || p != pEnd ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + pInChI[iComponent].nTautomer[0] = iTGroup; + } else { + /* tautomeric groups: pass 1 */ + iTGroup = 0; + state = ')'; /* init as if the prev. t-group just ended */ + num_Atoms = 0; + /* Tautomeric info storage */ + /* NumGroups; ((NumAt+2, NumH, Num(-), At1..AtNumAt),...); {INCHI_T_NUM_MOVABLE = 2} */ + /* Allocated length: [5*nNumberOfAtoms/2+1], see Alloc_INChI(...) */ + while ( p < pEnd ) { + /* t-group */ + switch ( *p ) { + case '(': /* start t-group */ + switch ( state ) { + case ')': + state = *p ++; + num_H = 0; + num_Minus = 0; + continue; + default: + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + case ')': /* end t-group */ + switch ( state ) { + case 'A': /* previuos was atom number */ + if ( !tg_alloc_len ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + iTGroup ++; + state = *p ++; + pInChI[iComponent].nTautomer[tg_pos_Tautomer] = lenTautomer - tg_pos_Tautomer - 1; /* length of the rest of the t-group */ + pInChI[iComponent].lenTautomer = lenTautomer; + continue; + default: + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + case 'H': /* number of H */ + switch ( state ) { + case '(': + state = *p ++; + num_H = 1; + continue; + default: + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + case '-': /* number of (-) */ + switch ( state ) { + case 'N': /* previous was number of H */ + case 'H': /* previous was H */ + state = *p ++; + num_Minus = 1; + continue; + default: + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + case ',': + switch ( state ) { + case 'N': /* previous was number of H */ + case 'H': /* previous was H */ + case '-': /* previuos was - */ + case 'M': /* previous was number of (-) */ + /* the next must be the first tautomeric atom number; save num_H & num_Minus */ + if ( num_H <= 0 && num_Minus <= 0 ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + if ( !tg_alloc_len ) { + /* + --- header --- + [num_t_groups] + --- one t-group: --- + [len=group length no including this value] + [num_H] + [num_(-)] + [Endpoint(1),...,Endpoint(len-2)] + --- next t-group --- + ... + + Max. size = 1 + 3*max_num_t_groups + max_num_endpoints + + max_num_t_groups = num_at/2 + max_num_endpoints = num_at + + Max. size = 1 + 3*(num_at/2) + num_at = 1 + (5*num_at)/2 + 5 = 3 + INCHI_T_NUM_MOVABLE = 3 + num_types_of_attachments + + This does not include zero termination! + + */ + tg_alloc_len = ((3+INCHI_T_NUM_MOVABLE)*pInChI[iComponent].nNumberOfAtoms)/2+1; + for ( i = 0; i < mpy_component; i ++ ) { + pInChI[iComponent+i].nTautomer = (AT_NUMB*)inchi_calloc( tg_alloc_len+1, sizeof(pInChI->nTautomer[0])); + if ( !pInChI[iComponent+i].nTautomer ) { + ret = RI_ERR_ALLOC; /* allocation error */ + goto exit_function; + } + pInChI[iComponent+i].lenTautomer = 0; + } + tg_pos_Tautomer = 1; /* number atoms (NumAt+2) position */ + } else { + /* next t-group */ + tg_pos_Tautomer = lenTautomer; + } + if ( tg_pos_Tautomer+3 >= tg_alloc_len ) { + ret = RI_ERR_PROGR; /* wrong tautomer array length */ + goto exit_function; + } + pInChI[iComponent].nTautomer[tg_pos_Tautomer+1] = num_H; + pInChI[iComponent].nTautomer[tg_pos_Tautomer+2] = num_Minus; + lenTautomer = tg_pos_Tautomer+3; /* first atom number position */ + num_taut_H_component += num_H; + state = *p ++; + continue; + case 'A': /* previuos was atom number */ + state = *p ++; + continue; + default: + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + default: + if ( isdigit( UCINT *p ) ) { + val = (int)inchi_strtol( p, &q, 10 ); + if ( val <= 0 ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + p = q; + switch( state ) { + case 'H': + num_H = val; + state = 'N'; + continue; + case '-': + num_Minus = val; + state = 'M'; + continue; + case ',': + if ( lenTautomer >= tg_alloc_len || val > numCtAtoms ) { + ret = RI_ERR_PROGR; /* wrong tautomer array length */ + goto exit_function; + } + num_Atoms ++; + pInChI[iComponent].nTautomer[lenTautomer ++] = val; + state = 'A'; + continue; + default: + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + } + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + } + if ( !iTGroup || state != ')' ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + pInChI[iComponent].nTautomer[0] = iTGroup; + } + } + /* check num_H in components; for bMobileH=TAUT_NON, pInChI->nNum_H_fixed[] has not been added to pInChI->nNum_H[] yet */ + if ( 0 > ( ret2 = GetInChIFormulaNumH( pInChI+iComponent, &num_H_formula) ) || + 0 > ( ret2 = GetInChINumH( pInChI+iComponent, &num_H_InChI ) ) ) { + ret = ret2; + goto exit_function; + } + if ( num_H_formula != num_H_InChI + (bMobileH==TAUT_NON? num_H_component:0) ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + + /* duplicate according to multipolication */ + for ( i = 1; i < mpy_component; i ++ ) { + memcpy( nNum_H(iComponent+i), nNum_H(iComponent), pInChI[iComponent+i].nNumberOfAtoms * sizeof(nNum_H(0)[0]) ); + /* + memcpy( pInChI[iComponent+i].nNum_H, pInChI[iComponent].nNum_H, + pInChI[iComponent+i].nNumberOfAtoms * sizeof(pInChI[0].nNum_H[0]) ); + */ + if ( pInChI[iComponent+i].nTautomer && pInChI[iComponent].nTautomer && pInChI[iComponent].lenTautomer ) { + memcpy( pInChI[iComponent+i].nTautomer, pInChI[iComponent].nTautomer, + pInChI[iComponent].lenTautomer * sizeof(pInChI[0].nTautomer[0]) ); + pInChI[iComponent+i].lenTautomer = pInChI[iComponent].lenTautomer; + } + /* check num_H in components */ + if ( 0 > ( ret2 = GetInChIFormulaNumH( pInChI+iComponent+i, &num_H_formula) ) || + 0 > ( ret2 = GetInChINumH( pInChI+iComponent+i, &num_H_InChI ) ) ) { + ret = ret2; + goto exit_function; + } + if ( num_H_formula != num_H_InChI + (bMobileH==TAUT_NON? num_H_component:0) ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + } + + /* prepare for the next component */ + iComponent += i; + if ( *pEnd ) { +#if (FIX_DALKE_BUGS == 1) + /* prevent crash on extra trailing ';' */ + if ( iComponent >= nNumComponents ) { + ret = RI_ERR_SYNTAX; /* syntax error: extra component */ + goto exit_function; + } +#endif + pStart = pEnd+1; + } else + break; + } + if ( nNumComponents != iComponent ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + ret = iComponent + 1; + +exit_function: + INCHI_HEAPCHK + return ret; +} + +/* + Parse InChI layer '/c' +*/ +int ParseSegmentConnections( const char *str, + int bMobileH, + INChI **pInpInChI, + int *pnNumComponents, + int *pbAbc, + int *nb_total ) +{ +#define LAST_AT_LEN 256 + /* Pass 1: count bonds and find actual numbers of atom */ + int i, j, k, m, c, mpy_component; + int nNumComponents, iComponent, nNumAtoms, nNumBonds, lenConnTable, iBond; + const char *p, *q, *pStart, *pEnd; + AT_NUMB last_atom[LAST_AT_LEN], curAtom, maxAtom; + int num_open, state, ret, base; + INChI *pInChI = *pInpInChI; + LINKED_BONDS LB; + LINKED_BONDS *pLB = &LB; /* a list of linked lists of bonds, for each atom */ + AT_NUMB neighbor[MAXVAL]; + int bPrevVersion = -1; + + *nb_total = 0; + iComponent = 0; + if ( str[0] != 'c' ) { + if ( !pInChI && !*pnNumComponents ) { + int lenFormula = 1; + /* component has no formula; allocate InChI */ + lenConnTable = 0; + nNumComponents = 1; + /* allocate InChI */ + if ( !(pInChI = *pInpInChI = (INChI *)inchi_calloc( nNumComponents, sizeof(INChI) ) ) ) { + return RI_ERR_ALLOC; /* alloc failure */ + } + /* allocate empty formula */ + pInChI[iComponent].szHillFormula = (char *)inchi_calloc( lenFormula+1, sizeof(pInChI[0].szHillFormula[0]) ); + if ( !pInChI[iComponent].szHillFormula ) { + ret = RI_ERR_ALLOC; /* allocation failure */ + goto exit_function; + } + /* allocate empty connection table */ + pInChI[iComponent].nConnTable = (AT_NUMB *)inchi_calloc( lenConnTable+1, sizeof(pInChI[0].nConnTable[0]) ); + if ( !pInChI[iComponent].nConnTable ) { + ret = RI_ERR_ALLOC; /* allocation failure */ + goto exit_function; + } + pInChI[iComponent].lenConnTable = lenConnTable; + *pnNumComponents = nNumComponents; + } else { + lenConnTable = 1; + nNumComponents = *pnNumComponents; + for ( i = 0; i < nNumComponents; i ++ ) { + /* allocate 1 atom connection table */ + if ( pInChI[i].nConnTable ) { + inchi_free( pInChI[i].nConnTable ); + } + pInChI[i].nConnTable = (AT_NUMB *)inchi_calloc( lenConnTable+1, sizeof(pInChI[0].nConnTable[0]) ); + if ( !pInChI[i].nConnTable ) { + ret = RI_ERR_ALLOC; /* allocation failure */ + goto exit_function; + } + pInChI[i].nConnTable[0] = 1; + pInChI[i].lenConnTable = lenConnTable; + } + } + return 0; + } + + /* Pass 1. Re-Count atoms, count bonds */ + pStart = str+1; + nNumComponents = *pnNumComponents; +#if (FIX_DALKE_BUGS == 1) + /* prevent crash on too many components */ + if ( nNumComponents > MAX_ATOMS ) { + ret = RI_ERR_SYNTAX; /* syntax error: extra component */ + goto exit_function; + } +#endif + memset( pLB, 0, sizeof(pLB[0]) ); + + while( 1 ) { + /* cycle over components */ + if ( !(pEnd = strchr( pStart, ';' )) ) { + pEnd = pStart + strlen(pStart); + } + if ( (p = strchr(pStart, '*')) && p < pEnd ) { + mpy_component = (int)inchi_strtol( pStart, &q, 10 ); + if ( p != q +#if (FIX_DALKE_BUGS == 1) + || !isdigit( UCINT *pStart ) +#endif + ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + p ++; + } else { + mpy_component = 1; + p = pStart; + } +#if (FIX_DALKE_BUGS == 1) + if ( iComponent + mpy_component > MAX_ATOMS ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } +#endif + pStart = p; + /* Pass 1.1 parse a component */ + num_open = 0; + memset( last_atom, 0, sizeof(last_atom) ); + state='\0'; /* initial state */ + maxAtom = 0; + nNumBonds = 0; + curAtom = 0; + if ( p < pEnd && *pbAbc == -1 ) { + /* check if compressed InChI */ + *pbAbc = isupper( UCINT *p)? 1 : 0; + } + base = *pbAbc? ALPHA_BASE : 10; + + if ( *pbAbc == 1 ) { + nNumAtoms = 1; + while ( p < pEnd ) { + if ( *p == '-' ) { + if ( bPrevVersion == -1 ) { + /* previous InChI version */ + bPrevVersion = 1; + } else + if ( bPrevVersion != 1 ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + nNumAtoms --; + p ++; + } + if ( isdigit( UCINT *p ) ) { + if ( bPrevVersion == -1 ) { + /* curreny InChI, version 1 */ + bPrevVersion = 0; + } else + if ( bPrevVersion != 0 ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + nNumAtoms -= inchi_strtol( p, &p, 10 ); /* bypass digits */ + } + if ( *p != '-' && ( curAtom = (AT_NUMB)inchi_strtol( p, &q, base ) ) ) { + nNumAtoms ++; + nNumBonds ++; + p = q; + if ( maxAtom < curAtom ) + maxAtom = curAtom; + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + } + if ( maxAtom < nNumAtoms && nNumBonds ) { + maxAtom = nNumAtoms; + } + } else { + while ( p < pEnd ) { + /* atom number */ + c = UCINT *p ++; + switch ( c ) { + case '(': + case ')': + case ',': + case '-': + if ( state != 'N' ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + state = c; + num_open += (c=='(') - (c==')'); + if ( num_open < 0 ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + break; + default: + if ( isdigit( c ) && (curAtom = (AT_NUMB)inchi_strtol( p-1, &q, 10 )) ) { + p = q; + switch( state ) { + case '(': + case ')': + case ',': + case '-': + nNumBonds ++; + case '\0': + if ( maxAtom < curAtom ) + maxAtom = curAtom; + state = 'N'; + break; + default: + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + break; + } + } + if ( num_open ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + /* syntax error: parentheses do not match */ + } + } + (*nb_total)+= nNumBonds; + /* Save the results and allocate memory */ + nNumAtoms = (int)maxAtom; /* 0 if empty connection table and no bonds present */ + lenConnTable = nNumAtoms + nNumBonds; + /* connection table format: At1[,Neigh11,Neigh12,...],At2[,Neigh21,Neigh22,...],AtN[NeighN1,NeighN2,...] */ + /* where AtK > NeighK1 > NeighK2,...; At(K) < At(K+1); the length = num.atoms + num.bonds */ + for ( i = 0; i < mpy_component; i ++ ) { + /* check number of atoms: the difference may be due to bridging H */ + if ( (j = pInChI[iComponent+i].nNumberOfAtoms) < nNumAtoms ) { + /* reallocate */ + U_CHAR *nAtomTmp = (U_CHAR *) inchi_malloc( nNumAtoms + 1 ); + if ( !nAtomTmp ) { + ret = RI_ERR_ALLOC; /* allocation failure */ + goto exit_function; + } + memcpy( nAtomTmp, pInChI[iComponent+i].nAtom, sizeof(nAtomTmp[0])*j); + while ( j < nNumAtoms ) { + nAtomTmp[j ++] = EL_NUMBER_H; /* bridging H */ + } + nAtomTmp[j] = '\0'; + INCHI_HEAPCHK + if ( pInChI[iComponent+i].nAtom ) { + inchi_free( pInChI[iComponent+i].nAtom ); + } + pInChI[iComponent+i].nAtom = nAtomTmp; + pInChI[iComponent+i].nNumberOfAtoms = nNumAtoms; + } else + if ( j > nNumAtoms && (lenConnTable || j != 1) ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + /* allocate connection table */ + if ( pInChI[iComponent+i].nConnTable ) { + inchi_free( pInChI[iComponent+i].nConnTable ); + } + if ( !nNumAtoms && !nNumBonds && !lenConnTable ) { + lenConnTable = 1; /* one atom, no bonds */ + } + pInChI[iComponent+i].nConnTable = (AT_NUMB *)inchi_calloc( lenConnTable+1, sizeof(pInChI[0].nConnTable[0]) ); + if ( !pInChI[iComponent+i].nConnTable ) { + ret = RI_ERR_ALLOC; /* allocation failure */ + goto exit_function; + } + pInChI[iComponent+i].lenConnTable = lenConnTable; + } + + /* Pass 1.2 parse a component and extract the bonds */ + num_open = 0; + memset( last_atom, 0, sizeof(last_atom) ); + state='\0'; /* initial state */ + iBond = 0; + p = pStart; + pLB->len = 0; + + if ( *pbAbc == 1 ) { + /* compressed */ + int num_neigh; + num_open = 0; + last_atom[num_open] = 2; + while ( p < pEnd ) { + if ( last_atom[num_open] > maxAtom ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + if ( isupper( UCINT *p ) ) { + curAtom = (AT_NUMB)inchi_strtol( p, &q, base ); + if ( ret = AddLinkedBond( last_atom[num_open], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { + goto exit_function; + } + p = q; + if ( bPrevVersion == 1 ) { + while ( p < pEnd && *p == '-' ) { + p ++; + if ( curAtom = (AT_NUMB)inchi_strtol( p, &q, base ) ) { + if ( ret = AddLinkedBond( last_atom[num_open], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { + goto exit_function; + } + p = q; + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + } + } else + if ( bPrevVersion == 0 && isdigit( *p ) ) { + num_neigh = (int)inchi_strtol( p, &q, 10 ); + p = q; + while( num_neigh -- && p < pEnd ) { + if ( curAtom = (AT_NUMB)inchi_strtol( p, &q, base ) ) { + if ( ret = AddLinkedBond( last_atom[num_open], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { + goto exit_function; + } + p = q; + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + } + } + last_atom[num_open] ++; + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + } + } else + { + while ( p < pEnd ) + { + /* each atom number except the first means a new bond */ + c = UCINT *p ++; + switch ( c ) + { + case '(': + case ')': + case ',': + case '-': + switch ( state ) + { + case 'N': + state = c; + break; + default: + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + break; + default: + if ( isdigit( c ) && (curAtom = (AT_NUMB)inchi_strtol( p-1, &q, 10 )) ) { + p = q; + switch( state ) { + case '\0': + last_atom[num_open] = curAtom; + state = 'N'; + break; + case '(': + if ( ret = AddLinkedBond( last_atom[num_open], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { + goto exit_function; + } + if ( ++ num_open >= LAST_AT_LEN ) { + ret = RI_ERR_PROGR; /* program error: buffer overflow */ + goto exit_function; + } + last_atom[num_open] = curAtom; + state = 'N'; + break; + + case ')': + if ( !num_open ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + if ( ret = AddLinkedBond( last_atom[--num_open], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { + goto exit_function; + } + last_atom[num_open] = curAtom; + state = 'N'; + break; + + case ',': + if ( !num_open ) { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + if ( ret = AddLinkedBond( last_atom[num_open-1], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { + goto exit_function; + } + last_atom[num_open] = curAtom; + state = 'N'; + break; + case '-': + if ( ret = AddLinkedBond( last_atom[num_open], curAtom, (AT_NUMB)nNumAtoms, pLB ) ) { + goto exit_function; + } + last_atom[num_open] = curAtom; + state = 'N'; + break; + default: + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + } else { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + break; + } + } + } + /* store the bonds in connection table */ + if ( lenConnTable > 1 ) + { + for ( i = 0, m = 0; i < nNumAtoms; i ++ ) + { + k = 0; + + if (!pLB->pBond) + { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + + if ( j = pLB->pBond[i+1].prev ) + { + while( k < MAXVAL ) + { + neighbor[k++] = pLB->pBond[j].neigh; + if ( j == i+1 ) + break; + j = pLB->pBond[j].prev; + } + } + if ( j != i+1 ) + { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + + /* sort the neighbors */ + + insertions_sort_AT_NUMB( neighbor, k ); + + if (m==pInChI[iComponent].lenConnTable) + { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + + pInChI[iComponent].nConnTable[m ++] = i+1; /* atom number */ + for ( j = 0; j < k && (int)neighbor[j] <= i; j ++ ) + { + if (m==pInChI[iComponent].lenConnTable) + { + ret = RI_ERR_SYNTAX; /* syntax error */ + goto exit_function; + } + pInChI[iComponent].nConnTable[m ++] = neighbor[j]; + } + } + if ( m != lenConnTable ) { + ret = RI_ERR_PROGR; /* program error */ + goto exit_function; + } + } else { + pInChI[iComponent].nConnTable[0] = 1; /* single atom */ + } + /* duplicate if needed */ + for ( i = 1; i < mpy_component; i ++ ) { + /* + if ( pInChI[iComponent+i].nConnTable ) { + inchi_free( pInChI[iComponent+i].nConnTable ); + } + pInChI[iComponent+i].nConnTable = (AT_NUMB *)inchi_calloc( lenConnTable+1, sizeof(pInChI[0].nConnTable[0]) ); + if ( !pInChI[iComponent+i].nConnTable ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + */ + if ( !pInChI[iComponent+i].nConnTable || pInChI[iComponent+i].lenConnTable != lenConnTable ) { + ret = RI_ERR_PROGR; + goto exit_function; + } + memcpy ( pInChI[iComponent+i].nConnTable, pInChI[iComponent].nConnTable, lenConnTable*sizeof(pInChI[0].nConnTable[0])); + } + /* prepare for the next connection table */ + iComponent += i; + if ( *pEnd ) + pStart = pEnd+1; + else + break; + /* We must check if we have already read as many components as we have in the fragment. */ + /* If yes, then we break, because anything else that might follow cannot be useful information. */ + /* There are files with a trailing ";" that would cause a problem (memory allocation bug) if we did not do this. */ + if (iComponent==nNumComponents) + break; + } + ret = iComponent; + +exit_function: + if ( pLB->pBond ) { + INCHI_HEAPCHK + inchi_free( pLB->pBond ); + } + return ret; + +#undef LAST_AT_LEN +} + + +/****************************************************************************************/ +int nFillOutProtonMobileH( INChI *pInChI ) +{ + int len = 1; + pInChI->bDeleted = 1; + /* formula */ + if ( !pInChI->szHillFormula && + !( pInChI->szHillFormula = (char *) inchi_calloc( len+1, sizeof(pInChI->szHillFormula[0]) ) ) ) { + return RI_ERR_ALLOC; /* alloc failure */ + } + strcpy( pInChI->szHillFormula, "H" ); + pInChI->nNumberOfAtoms = 1; + + /* atoms */ + if ( !pInChI->nAtom && + !(pInChI->nAtom = (U_CHAR *) inchi_calloc( len+1, sizeof(pInChI->nAtom[0]) ) ) ) { + return RI_ERR_ALLOC; /* alloc failure */ + } + pInChI->nAtom[0] = 1; + /* charge */ + pInChI->nTotalCharge = 1; + /* connection table */ + if ( !pInChI->nConnTable && + !(pInChI->nConnTable = (AT_NUMB *) inchi_calloc( len+1, sizeof(pInChI->nConnTable[0]) ) ) ) { + return RI_ERR_ALLOC; /* alloc failure */ + } + pInChI->nConnTable[0] = 1; + pInChI->lenConnTable = len; + /* tautomer */ + if ( !pInChI->nTautomer && + !(pInChI->nTautomer = (AT_NUMB *) inchi_calloc( len+1, sizeof(pInChI->nTautomer[0]) ) ) ) { + return RI_ERR_ALLOC; /* alloc failure */ + } + /* nNum_H */ + if ( !pInChI->nNum_H && + !(pInChI->nNum_H = (S_CHAR *) inchi_calloc( len+1, sizeof(pInChI->nNum_H[0]) ) ) ) { + return RI_ERR_ALLOC; /* alloc failure */ + } + pInChI->nNum_H[0] = 0; + + pInChI->nTautomer[0] = 0; + pInChI->lenTautomer = 1; + return 0; +} +/****************************************************************************************/ +int nProtonCopyIsotopicInfo( INChI *pInChI_to, INChI *pInChI_from ) +{ + if ( pInChI_from->nNumberOfIsotopicAtoms ) { + if ( pInChI_to->nNumberOfIsotopicAtoms && + pInChI_from->nNumberOfIsotopicAtoms > pInChI_to->nNumberOfIsotopicAtoms ) { + + inchi_free( pInChI_to->IsotopicAtom ); + pInChI_to->IsotopicAtom = NULL; + pInChI_to->nNumberOfIsotopicAtoms = 0; + } + if ( !pInChI_to->IsotopicAtom && + !(pInChI_to->IsotopicAtom = + (INChI_IsotopicAtom *)inchi_calloc(pInChI_from->nNumberOfIsotopicAtoms, + sizeof(pInChI_to->IsotopicAtom[0]) ) ) ) { + return RI_ERR_ALLOC; + } + pInChI_to->nNumberOfIsotopicAtoms = pInChI_from->nNumberOfIsotopicAtoms; + memcpy( pInChI_to->IsotopicAtom, pInChI_from->IsotopicAtom, + pInChI_from->nNumberOfIsotopicAtoms * sizeof(pInChI_to->IsotopicAtom[0]) ); + } else { + if ( pInChI_to->IsotopicAtom ) inchi_free( pInChI_to->IsotopicAtom ); + pInChI_to->IsotopicAtom = NULL; + pInChI_to->nNumberOfIsotopicAtoms = 0; + } + return 0; +} + + +/* + Parse InChI formula layer +*/ +int ParseSegmentFormula( const char *str, + int bMobileH, + INChI *pInpInChI[], + int pnNumComponents[], + int *na_total ) +{ + int i, j, mpy_component, mpy_atom, len, el_number; + int nNumComponents = 0, iComponent, nNumAtoms, nNumAtomsAndH, iAtom, nNumH, nAltMobileH = ALT_TAUT(bMobileH); + const char *p, *q, *e, *pStart, *pEnd; + INChI *pInChI; + char szEl[3]; + + nNumAtoms = -999; /* impossible value */ + *na_total = 0; + + /* Pass 1. Count components */ + pStart = str; + while( 1 ) { + if ( !(pEnd = strchr( pStart, '.' )) ) { + pEnd = pStart + strlen(pStart); + } + p = pStart; + if ( isdigit( *p ) ) { + mpy_component = (int)inchi_strtol( p, &q, 10 ); + p = q; + } else { + mpy_component = 1; + } + if ( !mpy_component ) + break; + if ( !isupper( UCINT *p ) ) { + break; /* not a formula layer */ + } + if ( pEnd == p ) + break; /* zero length formula */ + nNumComponents += mpy_component; + if ( *pEnd ) + pStart = pEnd+1; + else + break; + } + pnNumComponents[bMobileH] = nNumComponents; + +#if ( FIX_DALKE_BUGS == 1 ) + if ( nNumComponents > MAX_ATOMS ) { + return RI_ERR_SYNTAX; /* syntax error */ + } +#endif + /* exit or error check */ + if ( !nNumComponents ) { + if ( !*pStart || islower( UCINT *pStart ) ) { + INCHI_HEAPCHK + if ( bMobileH == TAUT_NON && 0 < ( nNumComponents = pnNumComponents[nAltMobileH]) ) { + /* allocate InChI */ + if ( !( pInChI = (INChI *)inchi_calloc( nNumComponents, sizeof(INChI) ) ) ) { + return RI_ERR_ALLOC; /* alloc failure */ + } + pInpInChI[bMobileH] = pInChI; + pnNumComponents[bMobileH] = nNumComponents; + for ( i = 0; i < nNumComponents; i ++ ) { + /* copy number of atoms */ + len = pInpInChI[bMobileH][i].nNumberOfAtoms = pInpInChI[nAltMobileH][i].nNumberOfAtoms; + /* copy atoms */ + len = (len+1)*sizeof(pInpInChI[0][0].nAtom[0]); + if ( pInpInChI[bMobileH][i].nAtom ) { + inchi_free( pInpInChI[bMobileH][i].nAtom ); + } + if ( pInpInChI[bMobileH][i].nAtom = (U_CHAR *) inchi_malloc( (len + 1) * sizeof(pInpInChI[0][0].nAtom[0]) ) ) { + memcpy(pInpInChI[bMobileH][i].nAtom, pInpInChI[nAltMobileH][i].nAtom, len); + pInpInChI[bMobileH][i].nAtom[len] = 0; + } else { + return RI_ERR_ALLOC; /* alloc failure */ + } + /* copy Hill formula */ + len = (int) strlen( pInpInChI[nAltMobileH][i].szHillFormula)+1; + if ( pInpInChI[bMobileH][i].szHillFormula ) { + inchi_free( pInpInChI[bMobileH][i].szHillFormula ); + } + if ( pInpInChI[bMobileH][i].szHillFormula = (char *) inchi_malloc( inchi_max(len,2) ) ) { + memcpy( pInpInChI[bMobileH][i].szHillFormula, pInpInChI[nAltMobileH][i].szHillFormula, len); + } else { + return RI_ERR_ALLOC; /* alloc failure */ + } + } + } else + if ( bMobileH == TAUT_YES ) { + int ret; + /* allocate InChI */ + nNumComponents = 1; + /* InChI */ + pnNumComponents[bMobileH] = nNumComponents; + if ( !( pInChI = (INChI *)inchi_calloc( nNumComponents, sizeof(INChI) ) ) ) { + return RI_ERR_ALLOC; /* alloc failure */ + } + pInpInChI[bMobileH] = pInChI; + ret = nFillOutProtonMobileH( pInChI ); + if ( ret < 0 ) { + return ret; + } + } + return 0; + } + return RI_ERR_SYNTAX; /* syntax error */ + } + if ( *pEnd ) { + return RI_ERR_SYNTAX; /* syntax error */ + } + + /* allocate InChI */ + if ( !( pInpInChI[bMobileH] = (INChI *)inchi_calloc( nNumComponents, sizeof(INChI) ) ) ) { + return RI_ERR_ALLOC; /* alloc failure */ + } + pInChI = pInpInChI[bMobileH]; + + /* Pass 2. Count elements, save formulas and elements */ + pStart = str; + iComponent = 0; + while( 1 ) { + if ( !(pEnd = strchr( pStart, '.' )) ) { + pEnd = pStart + strlen(pStart); + } + p = pStart; + if ( isdigit( UCINT *p ) ) { + mpy_component = (int)inchi_strtol( p, &q, 10 ); + p = q; + } else { + mpy_component = 1; + } +#if ( FIX_DALKE_BUGS == 1 ) + if ( iComponent + mpy_component > MAX_ATOMS ) { + return RI_ERR_SYNTAX; /* syntax error */ + } +#endif + len = (int) (pEnd-p); + for ( i = 0; i < mpy_component; i ++ ) + { + if (iComponent+i>=nNumComponents) + return RI_ERR_SYNTAX; + if ( pInChI[iComponent+i].szHillFormula ) + { + inchi_free( pInChI[iComponent+i].szHillFormula ); + } + pInChI[iComponent+i].szHillFormula = (char*) inchi_malloc( inchi_max(len,1)+1 ); + memcpy( pInChI[iComponent].szHillFormula, p, len ); + pInChI[iComponent+i].szHillFormula[len] = '\0'; + if ( !i ) + { + /* Pass 2.1 Parse formula and count atoms except H */ + nNumAtoms = 0; + nNumH = 0; + nNumAtomsAndH = 0; + e = pInChI[iComponent].szHillFormula; + while ( *e ) { + if ( !isupper( UCINT *e ) ) { + return RI_ERR_SYNTAX; + } + j = 0; + szEl[j ++] = *e ++; + if ( *e && islower( UCINT *e ) ) + szEl[j ++] = *e ++; + szEl[j ++] = '\0'; + if ( *e && isdigit( UCINT *e ) ) { + mpy_atom = (int)inchi_strtol( e, &q, 10 ); + e = q; + } else { + mpy_atom = 1; + } + if ( !mpy_atom ) { + return RI_ERR_SYNTAX; + } + if ( szEl[0] == 'H' && !szEl[1] ) { + nNumH += mpy_atom; + continue; /* ignore H in counting number of atoms */ + } + + nNumAtoms += mpy_atom; + } +#if ( FIX_DALKE_BUGS == 1 ) + if ( nNumAtoms > MAX_ATOMS ) { + return RI_ERR_SYNTAX; /* syntax error */ + } +#endif + (*na_total)+= mpy_component * nNumAtoms; + + nNumAtomsAndH = nNumAtoms? nNumAtoms : (nNumH > 0); + pInChI[iComponent+i].nNumberOfAtoms = nNumAtomsAndH; + if ( pInChI[iComponent+i].nAtom ) { + inchi_free( pInChI[iComponent+i].nAtom ); + } + pInChI[iComponent+i].nAtom = (U_CHAR *) inchi_malloc((nNumAtomsAndH+1)*sizeof(pInChI[0].nAtom[0])); + if ( !pInChI[iComponent+i].nAtom ) + return RI_ERR_ALLOC; /* failed allocation */ + /* Pass 2.2 Store elements; this assumes no bridging H. Bridging H will be found in connection table, /c */ + iAtom = 0; + if ( nNumAtoms > 0 ) { + e = pInChI[iComponent+i].szHillFormula; + while ( *e ) { + if ( !isupper( UCINT *e ) ) { + return RI_ERR_SYNTAX; + } + j = 0; + szEl[j ++] = *e ++; + if ( *e && islower( UCINT *e ) ) + szEl[j ++] = *e ++; + szEl[j ++] = '\0'; + if ( *e && isdigit( UCINT *e ) ) { + mpy_atom = (int)inchi_strtol( e, &q, 10 ); + e = q; + } else { + mpy_atom = 1; + } + if ( !mpy_atom ) { + return RI_ERR_SYNTAX; + } + if ( szEl[0] == 'H' && !szEl[1] ) + continue; /* ignore H */ + el_number = get_periodic_table_number( szEl ); + if ( el_number == ERR_ELEM ) { + return RI_ERR_SYNTAX; /* wrong element */ + } + while ( mpy_atom -- ) { + if ( iAtom >= nNumAtoms ) { + return RI_ERR_PROGR; /* program error */ + } + pInChI[iComponent+i].nAtom[iAtom ++] = (U_CHAR)el_number; + } + } + } else + if ( nNumH > 0 ) { + pInChI[iComponent+i].nAtom[iAtom ++] = EL_NUMBER_H; + nNumAtoms = 1; + } + pInChI[iComponent+i].nAtom[iAtom] = '\0'; + if ( nNumAtoms != iAtom ) { + return RI_ERR_PROGR; /* program error */ + } + } else { + /* Copy duplicated formula */ + strcpy(pInChI[iComponent+i].szHillFormula, pInChI[iComponent].szHillFormula); + /* Copy atoms in the duplicated formula */ + pInChI[iComponent+i].nNumberOfAtoms = nNumAtoms; + if ( pInChI[iComponent+i].nAtom ) { + inchi_free( pInChI[iComponent+i].nAtom ); + } + pInChI[iComponent+i].nAtom = (U_CHAR *) inchi_malloc(nNumAtoms+1); + if ( !pInChI[iComponent+i].nAtom ) + return RI_ERR_ALLOC; /* failed allocation */ + memcpy( pInChI[iComponent+i].nAtom, pInChI[iComponent].nAtom, nNumAtoms+1 ); + } + } + iComponent += i; + if ( *pEnd ) { + if ( *pEnd != '.' ) { + return RI_ERR_SYNTAX; /* syntax error */ + } + pStart = pEnd+1; + } else + break; + } + + if ( iComponent != nNumComponents ) { + return RI_ERR_PROGR; /* program error */ + } + if ( bMobileH == TAUT_NON ) { + /* at this point the exact number of atoms including bridging H is known from TAUT_YES */ + for ( i = 0; i < nNumComponents && i < pnNumComponents[nAltMobileH]; i ++ ) { + if ( pInpInChI[bMobileH][i].nNumberOfAtoms < (len=pInpInChI[nAltMobileH][i].nNumberOfAtoms) ) { + /* there are bridging H in this component */ + if ( pInpInChI[nAltMobileH][i].nAtom ) { + U_CHAR *nAtom = (U_CHAR *) inchi_malloc( (len+1) * sizeof(nAtom[0]) ); + if ( !nAtom ) { + return RI_ERR_ALLOC; + } + memcpy( nAtom, pInpInChI[nAltMobileH][i].nAtom, len*sizeof(nAtom[0]) ); + nAtom[ len ] = 0; + if ( pInpInChI[bMobileH][i].nAtom ) { + inchi_free( pInpInChI[bMobileH][i].nAtom ); + } + pInpInChI[bMobileH][i].nAtom = nAtom; + } + pInpInChI[bMobileH][i].nNumberOfAtoms = len; + } + } + } + + return nNumComponents+1; +} + + +/* + CopySegment +*/ +int CopySegment( INChI *pInChITo, INChI *pInChIFrom, int SegmentType, int bIsotopicTo, int bIsotopicFrom) +{ + int ret = RI_ERR_ALLOC; + int len; + + + if ( SegmentType==CPY_SP2 || + SegmentType==CPY_SP3 || + SegmentType==CPY_SP3_M || + SegmentType==CPY_SP3_S ) { + + INChI_Stereo **pstereoTo = NULL; + INChI_Stereo *stereoFrom = bIsotopicFrom==1? pInChIFrom->StereoIsotopic : + bIsotopicFrom==0? pInChIFrom->Stereo : NULL; + if ( stereoFrom || bIsotopicFrom < 0 ) { + if ( SegmentType==CPY_SP2 ) { + if ( bIsotopicFrom < 0 || + stereoFrom->b_parity && + stereoFrom->nBondAtom1 && + stereoFrom->nBondAtom2 ) { + + len = (bIsotopicFrom < 0)? 0 : stereoFrom->nNumberOfStereoBonds; + pstereoTo = bIsotopicTo? &pInChITo->StereoIsotopic : &pInChITo->Stereo; + if ( !pstereoTo[0] ) { + if ( !(pstereoTo[0] = (INChI_Stereo *)inchi_calloc( 1, sizeof(**pstereoTo))) ) { + goto exit_function; + } + } + if ( pstereoTo[0]->nNumberOfStereoBonds > 0 || pstereoTo[0]->b_parity || + pstereoTo[0]->nBondAtom1 || pstereoTo[0]->nBondAtom2 ) { + ret = RI_ERR_SYNTAX; /* stereo already exists */ + goto exit_function; + } + /* allocate sp2 stereo */ + if ( !(pstereoTo[0]->b_parity = (S_CHAR *)inchi_calloc( len+1, sizeof(pstereoTo[0]->b_parity[0]) ) ) || + !(pstereoTo[0]->nBondAtom1 = (AT_NUMB *)inchi_calloc( len+1, sizeof(pstereoTo[0]->nBondAtom1[0]) ) ) || + !(pstereoTo[0]->nBondAtom2 = (AT_NUMB *)inchi_calloc( len+1, sizeof(pstereoTo[0]->nBondAtom2[0]) ) ) ) { + /* cleanup */ + if ( pstereoTo[0]->b_parity ) { + INCHI_HEAPCHK + inchi_free( pstereoTo[0]->b_parity ); + pstereoTo[0]->b_parity = NULL; + } + if ( pstereoTo[0]->nBondAtom1 ) { + INCHI_HEAPCHK + inchi_free( pstereoTo[0]->nBondAtom1 ); + pstereoTo[0]->nBondAtom1 = NULL; + } + if ( pstereoTo[0]->nBondAtom2 ) { + INCHI_HEAPCHK + inchi_free( pstereoTo[0]->nBondAtom2 ); + pstereoTo[0]->nBondAtom2 = NULL; + } + INCHI_HEAPCHK + goto exit_function; + } + /* copy stereo */ + if ( bIsotopicFrom >= 0 && len ) { + memcpy( pstereoTo[0]->b_parity, stereoFrom->b_parity, (len+1)*sizeof(pstereoTo[0]->b_parity[0]) ); + memcpy( pstereoTo[0]->nBondAtom1, stereoFrom->nBondAtom1, (len+1)*sizeof(pstereoTo[0]->nBondAtom1[0]) ); + memcpy( pstereoTo[0]->nBondAtom2, stereoFrom->nBondAtom2, (len+1)*sizeof(pstereoTo[0]->nBondAtom2[0]) ); + } + pstereoTo[0]->nNumberOfStereoBonds = len; + + return len+1; + } else { + return 0; + } + } else + if ( SegmentType==CPY_SP3 ) { + if ( bIsotopicFrom < 0 || + stereoFrom->t_parity && + stereoFrom->nNumber ) { + + len = (bIsotopicFrom < 0)? 0 : stereoFrom->nNumberOfStereoCenters; + pstereoTo = bIsotopicTo? &pInChITo->StereoIsotopic : &pInChITo->Stereo; + if ( !pstereoTo[0] ) { + if ( !(pstereoTo[0] = (INChI_Stereo *)inchi_calloc( 1, sizeof(**pstereoTo))) ) { + goto exit_function; + } + } + if ( pstereoTo[0]->nNumberOfStereoCenters > 0 || pstereoTo[0]->t_parity || + pstereoTo[0]->nNumber ) { + ret = RI_ERR_SYNTAX; /* stereo already exists */ + goto exit_function; + } + /* allocate sp3 stereo */ + if ( !(pstereoTo[0]->t_parity = (S_CHAR *)inchi_calloc( len+1, sizeof(pstereoTo[0]->b_parity[0]) ) ) || + !(pstereoTo[0]->nNumber = (AT_NUMB *)inchi_calloc( len+1, sizeof(pstereoTo[0]->nBondAtom1[0]) ) ) ) { + /* cleanup */ + if ( pstereoTo[0]->t_parity ) { + inchi_free( pstereoTo[0]->t_parity ); + pstereoTo[0]->t_parity = NULL; + } + if ( pstereoTo[0]->nNumber ) { + inchi_free( pstereoTo[0]->nNumber ); + pstereoTo[0]->nNumber = NULL; + } + goto exit_function; + } + /* copy stereo */ + if ( bIsotopicFrom >= 0 && len ) { + memcpy( pstereoTo[0]->t_parity, stereoFrom->t_parity, (len+1)*sizeof(pstereoTo[0]->t_parity[0]) ); + memcpy( pstereoTo[0]->nNumber, stereoFrom->nNumber, (len+1)*sizeof(pstereoTo[0]->nNumber[0]) ); + } + pstereoTo[0]->nNumberOfStereoCenters = len; + return len+1; + } else { + return 0; + } + } else + if ( SegmentType==CPY_SP3_M ) { + pstereoTo = bIsotopicTo? &pInChITo->StereoIsotopic : &pInChITo->Stereo; + if ( !pstereoTo[0] ) { + if ( !(pstereoTo[0] = (INChI_Stereo *)inchi_calloc( 1, sizeof(**pstereoTo))) ) { + goto exit_function; + } + } + if ( pstereoTo[0]->nCompInv2Abs && NO_VALUE_INT != pstereoTo[0]->nCompInv2Abs ) { + ret = RI_ERR_SYNTAX; /* stereo already exists */ + goto exit_function; + } + if ( bIsotopicFrom < 0 ) { + pstereoTo[0]->nCompInv2Abs = 0; + } else { + pstereoTo[0]->nCompInv2Abs = stereoFrom->nCompInv2Abs; + } + return 1; + } else + /* use bTrivialInv to save /s1, /s2, /s3 */ + if ( SegmentType==CPY_SP3_S ) { + pstereoTo = bIsotopicFrom? &pInChITo->StereoIsotopic : &pInChITo->Stereo; + if ( !pstereoTo[0] ) { + if ( !(pstereoTo[0] = (INChI_Stereo *)inchi_calloc( 1, sizeof(**pstereoTo))) ) { + goto exit_function; + } + } + if ( pstereoTo[0]->bTrivialInv ) { + ret = RI_ERR_SYNTAX; /* stereo already exists */ + goto exit_function; + } + pstereoTo[0]->bTrivialInv = stereoFrom->bTrivialInv; + if ( bIsotopicFrom < 0 ) { + pstereoTo[0]->bTrivialInv = 0; + } else { + pstereoTo[0]->bTrivialInv = stereoFrom->bTrivialInv; + } + return 1; + } + } + return 0; /* nothing to copy */ + } else + if ( SegmentType == CPY_ISO_AT ) { + int nNumberOfIsotopicAtoms = pInChIFrom->nNumberOfIsotopicAtoms; + INChI_IsotopicAtom **pIsotopicAtomTo = NULL; + INChI_IsotopicAtom *IsotopicAtomFrom = pInChIFrom->IsotopicAtom; + if ( bIsotopicFrom < 0 || IsotopicAtomFrom ) { + len = (bIsotopicFrom < 0)? 0 : nNumberOfIsotopicAtoms; + pIsotopicAtomTo = &pInChITo->IsotopicAtom; + if ( !*pIsotopicAtomTo ) { + if ( !( *pIsotopicAtomTo = (INChI_IsotopicAtom *)inchi_calloc( len+1, sizeof(**pIsotopicAtomTo) ) ) ) { + goto exit_function; + } + } + if ( pInChITo->nNumberOfIsotopicAtoms ) { + ret = RI_ERR_SYNTAX; /* stereo already exists */ + goto exit_function; + } + if ( bIsotopicFrom >= 0 && len ) { + memcpy( *pIsotopicAtomTo, IsotopicAtomFrom, (len+1)*sizeof(**pIsotopicAtomTo) ); + } + pInChITo->nNumberOfIsotopicAtoms = len; + return len+1; + } + return 0; + } + ret = RI_ERR_PROGR; /* program error */ +exit_function: + return ret; +} + + +/* + Sort neighbors in ascending order +*/ +int insertions_sort_AT_NUMB( AT_NUMB *base, int num ) +{ + AT_NUMB *i, *j, *pk, tmp; + int k, num_trans = 0; + for( k=1, pk = base; k < num; k++, pk ++ ) { + for( j = (i = pk) + 1, tmp = *j; j > base && *i > tmp; j=i, i -- ) { + *j = *i; + num_trans ++; + } + *j = tmp; + } + return num_trans; +} + + +/* + getInChIChar +*/ +int getInChIChar( INCHI_IOSTREAM *pInp ) +{ + if (pInp->type==INCHI_IOSTREAM_TYPE_STRING) + { + /* input from string */ + if ( pInp->s.nPtr < pInp->s.nUsedLength ) + return (int) pInp->s.pStr[pInp->s.nPtr++]; + return RI_ERR_EOF; + } + + else + { + /* input from plain file */ + int c; +#if ( defined(_MSC_VER)&&defined(_WIN32) || defined(__BORLANDC__)&&defined(__WIN32__) || defined(__GNUC__)&&defined(__MINGW32__)&&defined(_WIN32) ) + do + { + c = getc( pInp->f ); + if ( c == EOF ) + { + c = RI_ERR_EOF; + break; + } + } + while( c == '\r' ); +#else + c = getc( pInp->f ); + if ( c == EOF ) + { + c = RI_ERR_EOF; + } +#endif + return c; + } +} + + +/* + AddInChIChar +*/ +int AddInChIChar( INCHI_IOSTREAM *pInp, SEGM_LINE *Line, const char *pszToken ) +{ + int c = getInChIChar( pInp ); + /* + while ( c == '\r' ) { + c = getInChIChar( pInp ); + } + */ + + INCHI_HEAPCHK + + if ( Line->len + 2 >= Line->len_alloc ) + { + char *str = (char *) inchi_calloc( Line->len_alloc + SEGM_LINE_ADD, sizeof(str[0]) ); + INCHI_HEAPCHK + if ( str ) + { + if ( Line->len > 0 && Line->str ) + { + memcpy( str, Line->str, sizeof(str[0]) * Line->len ); + Line->len_alloc += SEGM_LINE_ADD; + inchi_free( Line->str ); + INCHI_HEAPCHK + } + else + { + Line->len_alloc += SEGM_LINE_ADD; + } + Line->str = str; + } else + { + c = RI_ERR_ALLOC; /* fatal error */ + goto exit_function; + } + } + INCHI_HEAPCHK + if ( c < 0 ) + { + Line->str[Line->len] = '\0'; + INCHI_HEAPCHK + c = RI_ERR_SYNTAX; /* fatal error: wrong char */ + goto exit_function; + } + + if ( c && strchr( pszToken, c ) ) /* /\ */ + { + Line->str[Line->len] = '\0'; + INCHI_HEAPCHK + c = -(c+2); + goto exit_function; + } + else if ( !c && !Line->len ) + { + Line->str[Line->len] = c; + INCHI_HEAPCHK + } + else + { + Line->str[Line->len ++] = c; + INCHI_HEAPCHK + } + +exit_function: + INCHI_HEAPCHK + + return c; +} + +/* + nGetInChISegment +*/ +int nGetInChISegment( INCHI_IOSTREAM *pInp, SEGM_LINE *Line, const char *pszToken ) +{ + int c; + Line->len = 0; + while( 0 < (c = AddInChIChar( pInp, Line, pszToken ) ) ) + ; + if ( c < - 2 ) { + c = -(c+2); + } + Line->c = c; + return c; +} + + +/* + Add one more bond to the linked lists for both neighbors +*/ +int AddLinkedBond( AT_NUMB at1, AT_NUMB at2, AT_NUMB num_at, LINKED_BONDS *pLB ) +{ + int nReqLen = inchi_max( 2*num_at+2, pLB->len + 2 ); + AT_NUMB prev; + if ( pLB->len_alloc <= nReqLen ) { + /*int nNewLen = nReqLen + (nReqLen + LINKED_BOND_ADD - 1)%LINKED_BOND_ADD + LINKED_BOND_ADD;*/ + int nNewLen = nReqLen - nReqLen%LINKED_BOND_ADD + 2*LINKED_BOND_ADD; + ONE_LINKED_BOND *pBond = (ONE_LINKED_BOND *)inchi_calloc( nNewLen, sizeof(pBond[0]) ); + if ( !pBond ) + return RI_ERR_ALLOC; /* allocation error */ + if ( pLB->pBond && pLB->len ) { + memcpy( pBond, pLB->pBond, pLB->len*sizeof(pBond[0]) ); + } + if ( pLB->pBond ) + inchi_free( pLB->pBond ); + pLB->pBond = pBond; + pLB->len_alloc = nNewLen; + } + if ( !pLB->len ) { + pLB->len = num_at+1; + memset( pLB->pBond, 0, (num_at+1)*sizeof(pLB->pBond[0]) ); + } + + prev = pLB->pBond[at1].prev; /* position of the last neighbor of at1 in the pLB->pBond */ + if ( !prev ) { + pLB->pBond[at1].neigh = at2; + pLB->pBond[at1].prev = at1; + } else { + pLB->pBond[pLB->len].neigh = at2; + pLB->pBond[pLB->len].prev = prev; + pLB->pBond[at1].prev = pLB->len ++; + } + + prev = pLB->pBond[at2].prev; /* position of the last neighbor of at2 in the pLB->pBond */ + if ( !prev ) { + pLB->pBond[at2].neigh = at1; + pLB->pBond[at2].prev = at2; + } else { + pLB->pBond[pLB->len].neigh = at1; + pLB->pBond[pLB->len].prev = prev; + pLB->pBond[at2].prev = pLB->len ++; + } + return 0; +} + + +/* + PrepareSaveOptBits +*/ +void PrepareSaveOptBits( INPUT_PARMS *ip, + INCHI_IOSTREAM *pLog, + const long num_inp, + const char *szCurHdr, + int input_has_save_opt, + unsigned char input_save_opt_bits, + unsigned char *save_opt_bits ) +{ + + if (!input_has_save_opt) + { + /* Does not allow to create SaveOpt if the source lacks appendix */ + ip->bINChIOutputOptions &= ~INCHI_OUT_SAVEOPT; + if ( szCurHdr && szCurHdr[0] ) + inchi_ios_eprint( pLog, + "Warning: ignore SaveOpt request for SaveOpt-less input, %s\n", + szCurHdr ); + else + inchi_ios_eprint( pLog, + "Warning: ignore SaveOpt request for SaveOpt-less input, Structure %ld\n", + num_inp ); + } + else + { + /* Analyze existing and prepare new SaveOpt appendix */ + + int input_save_opt_has_recmet = input_save_opt_bits & SAVE_OPT_RECMET; + int input_save_opt_has_fixedh = input_save_opt_bits & SAVE_OPT_FIXEDH; + int input_save_opt_has_suu = input_save_opt_bits & SAVE_OPT_SUU; + int input_save_opt_has_sluud = input_save_opt_bits & SAVE_OPT_SLUUD; + int input_save_opt_has_ket = input_save_opt_bits & SAVE_OPT_KET; + int input_save_opt_has_15t = input_save_opt_bits & SAVE_OPT_15T; + + if ( 0 != ( ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) + { + /* RecMet requested */ + if ( input_save_opt_has_recmet ) + { + *save_opt_bits |= SAVE_OPT_RECMET; + } + else + { + ip->bTautFlags &= ~TG_FLAG_RECONNECT_COORD; + if ( szCurHdr && szCurHdr[0] ) + inchi_ios_eprint( pLog, "Warning: input created w/o RecMet - ignoring RecMet request, %s\n", szCurHdr ); + else + inchi_ios_eprint( pLog, "Warning: input created w/o RecMet - ignoring RecMet request, Structure %ld\n", num_inp ); + } + } + + if ( 0 != (ip->nMode & REQ_MODE_BASIC) ) + { + /* FixedH requested */ + if ( input_save_opt_has_fixedh ) + *save_opt_bits |= SAVE_OPT_FIXEDH; + else + { + ip->nMode &= ~REQ_MODE_BASIC; + if ( szCurHdr && szCurHdr[0] ) + inchi_ios_eprint( pLog, "Warning: input created w/o FixedH - ignoring FixedH request, %s\n", szCurHdr ); + else + inchi_ios_eprint( pLog, "Warning: input created w/o FixedH - ignoring FixedH request, Structure %ld\n", num_inp ); + } + } + + /* Copy from source SaveOpt those bits which we do not touch */ + /* while converting InChI: SUU SLUUD KET 15T */ + + if ( input_save_opt_has_suu ) + *save_opt_bits |= SAVE_OPT_SUU; + if ( input_save_opt_has_sluud ) + *save_opt_bits |= SAVE_OPT_SLUUD; + if ( input_save_opt_has_ket ) + *save_opt_bits |= SAVE_OPT_KET; + if ( input_save_opt_has_15t ) + *save_opt_bits |= SAVE_OPT_15T; + + /* Check if /SNon requested and turn OFF stereo bits if so */ + if ( ! (ip->nMode & REQ_MODE_STEREO) ) + { + *save_opt_bits &= ~SAVE_OPT_SUU; + *save_opt_bits &= ~SAVE_OPT_SLUUD; + } + } + + return; +} + + +/* + TreatErrorsInReadInChIString +*/ +void TreatErrorsInReadInChIString( int nReadStatus, + int nErr, + int pState, + INPUT_PARMS *ip, + INCHI_IOSTREAM *pOut, + INCHI_IOSTREAM *pLog, + long *num_inp, + long *num_errors, + long *num_processed, + char **pstrHdr, + char **pszCurHdr, + InpInChI *pOneInput) +{ + int bInChI2Struct = (ip->bReadInChIOptions & READ_INCHI_TO_STRUCTURE) && ip->nInputType == INPUT_INCHI; + + /* InChI could not be read */ + if ( nReadStatus == RI_ERR_EOF && nErr == 0 && pState == 0 ) /* && !(*pstrHdr) ) */ + { + /*if ( !(*pstrHdr) ) */ + ;/*inchi_ios_eprint( pLog, "\nEnd of file detected after structure %ld. \n", *num_inp );*/ + } + else + { + /* Output InChI parsing error message */ + char szHdrSimulation[128]; + char szMsg2[1024]; + (*num_inp)++; + sprintf( szHdrSimulation, "Structure: %ld", *num_inp ); + getInchiStateReadErr(pState, szMsg2); + +#ifdef TARGET_EXE_STANDALONE + if ( pOneInput->polymer && + bInChI2Struct && + !(ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY) ) + { + inchi_ios_eprint( pLog, "%s Skipping polymer InChI (only conversion to Molfile is available, use OutputSDF option)\n", + *pstrHdr? *pstrHdr : szHdrSimulation ); + } + else +#endif + if ( !bInChI2Struct && + ( pState==IST_MOBILE_H_POLYMER || !ip->bPolymers ) ) + { + /* TO DO: implement InChI2InChI for polymers in a way similar to InChI2Struct + thru an external (to ReadWriteInchi) loop */ + inchi_ios_eprint( pLog, "%s Skipping polymer InChI for conversion of InChI to InChI\n", + *pstrHdr? *pstrHdr : szHdrSimulation ); + } + else + { + inchi_ios_eprint( pLog, "\n%s %s (%d) in %s (%d)\n", + *pstrHdr? *pstrHdr : szHdrSimulation, + getInchiErrName(nErr), nErr, + szMsg2, pState ); + } + +#ifdef ERR_INCHI_STRING_ALLOWED + if ( ip->bINChIOutputOptions2 & INCHI_OUT_INCHI_GEN_ERROR ) + { + inchi_ios_eprint( pOut, "%s\n", *pstrHdr? *pstrHdr : szHdrSimulation); + if ( ip->bINChIOutputOptions & INCHI_OUT_STDINCHI ) + inchi_ios_eprint( pOut, "InChI=1S//\n"); + else + inchi_ios_eprint( pOut, "InChI=1//\n"); + } +#endif + + (*num_errors)++; + (*num_processed)++; + } + if ( *pstrHdr ) + { + inchi_free( *pstrHdr ); + *pstrHdr = NULL; + } + if ( *pszCurHdr ) + { + inchi_free( *pszCurHdr ); + *pszCurHdr = NULL; + } + FreeInpInChI( pOneInput ); + + + return; +} + + +/* + InChi --> InChI string(s) +*/ +int ConvertInChI2InChI( INPUT_PARMS *ip, + InpInChI *pOneInput, + INCHI_IOSTREAM *pOut, + INCHI_IOSTREAM *pLog, + STRUCT_DATA *sd, + int num_components[INCHI_NUM], + MODE_PIXH nModeProtonIsoExchgH[INCHI_NUM], + char **pszCurHdr, + long num_inp, + long *num_errors, + unsigned char save_opt_bits, + inchiTime *pulTStart, + long *ulProcessingTime, + struct tagINCHI_CLOCK *ic, + struct tagCANON_GLOBALS *pCG) +{ +int ret, tmp; + + InchiTimeGet( pulTStart ); + + tmp = ip->bNoStructLabels; + ip->bNoStructLabels = 1; + INCHI_HEAPCHK + ip->pSdfValue = NULL; + ip->pSdfLabel = NULL; + +#if ( FIX_DALKE_BUGS == 1 ) + SetHillFormFromInChI( pOneInput ); +#endif + + ret = OutputInChIAsRequested( pCG, + pOut, + pLog, + ip, + sd, + pOneInput, + num_components, + nModeProtonIsoExchgH, + num_inp, + save_opt_bits); + + +#if ( !defined(TARGET_API_LIB) && defined(TARGET_EXE_STANDALONE) ) + + /* Calculate InChIKey if requested */ + /* However, do not calculate/write it if this function is called from within dll */ + { + char ik_string[256]; /* Resulting InChIKey string */ + int ik_ret=0; /* InChIKey-calc result code */ + int xhash1, xhash2; + char szXtra1[65], szXtra2[65]; + + inchi_ios_flush2(pLog, stderr); + + /* post-1.02b addition - correctly treat tabbed output with InChIKey */ + if ( ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT ) + if ( ip->bCalcInChIHash != INCHIHASH_NONE ) + if (pOut->s.pStr) + if (pOut->s.nUsedLength>0) + if (pOut->s.pStr[pOut->s.nUsedLength-1]=='\n') + /* replace LF with TAB */ + pOut->s.pStr[pOut->s.nUsedLength-1] = '\t'; + + + if ( ip->bCalcInChIHash == INCHIHASH_NONE ) + { + /* inchi_ios_flush(pOut); */ + } + else + { + char *buf = NULL; + size_t slen = pOut->s.nUsedLength; + extract_inchi_substring(&buf, pOut->s.pStr, slen); + + if (NULL!=buf) + { + xhash1 = xhash2 = 0; + if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1 ) || + ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) + xhash1 = 1; + if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA2 ) || + ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) + xhash2 = 1; + + ik_ret = GetINCHIKeyFromINCHI(buf, + xhash1, + xhash2, + ik_string, + szXtra1, + szXtra2); + inchi_free(buf); + } + else + ik_ret = INCHIKEY_NOT_ENOUGH_MEMORY; + + + if (ik_ret==INCHIKEY_OK) + { + inchi_ios_print(pOut, "InChIKey=%-s\n",ik_string); + } + + else + { + inchi_ios_print(pLog, "Warning (Could not compute InChIKey: ", num_inp); + switch(ik_ret) + { + case INCHIKEY_UNKNOWN_ERROR: + inchi_ios_print(pLog, "unresolved error)"); + break; + case INCHIKEY_EMPTY_INPUT: + inchi_ios_print(pLog, "got an empty string)"); + break; + case INCHIKEY_INVALID_INCHI_PREFIX: + case INCHIKEY_INVALID_INCHI: + case INCHIKEY_INVALID_STD_INCHI: + inchi_ios_print(pLog, "got non-InChI string)"); + break; + case INCHIKEY_NOT_ENOUGH_MEMORY: + inchi_ios_print(pLog, "not enough memory to treat the string)"); + break; + default:inchi_ios_print(pLog, "internal program error)"); + break; + } + + inchi_ios_print(pLog, " structure #%-lu.\n", num_inp); + if ( ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT ) + inchi_ios_print(pOut, "\n"); + } /* if (ip->bCalcInChIHash!=INCHIHASH_NONE) */ + + inchi_ios_flush(pOut); + inchi_ios_flush2(pLog, stderr); + } + } /* Calculate InChIKey if requested */ +#endif + + + ip->bNoStructLabels = tmp; + + +#ifndef TARGET_API_LIB + if ( ret < 0 ) + { + + if ( *pszCurHdr && (*pszCurHdr)[0] ) + { + inchi_ios_eprint( pLog, "Error %d creating InChI string %s\n", ret, *pszCurHdr ); + } + else + { + inchi_ios_eprint( pLog, "Error %d creating InChI string, Structure %ld\n", ret, num_inp ); + } +#ifdef ERR_INCHI_STRING_ALLOWED + if ( ip->bINChIOutputOptions2 & INCHI_OUT_INCHI_GEN_ERROR ) + /* inchi_ios_eprint( pOut, "InChICreationError!\n"); *//* emit err string */ + if ( ip->bINChIOutputOptions & INCHI_OUT_STDINCHI ) + inchi_ios_eprint( pOut, "InChI=1S//\n"); + else + inchi_ios_eprint( pOut, "InChI=1//\n"); + + +#endif + + (*num_errors)++; + } + +#if ( !defined(TARGET_API_LIB) && !defined(TARGET_EXE_STANDALONE) ) + else + if ( *pszCurHdr && (*pszCurHdr)[0] ) + { + inchi_fprintf( stderr, "%s\r", *pszCurHdr ); + } +#endif +#endif + + + if ( *pszCurHdr ) + { + inchi_free( *pszCurHdr ); + *pszCurHdr = NULL; + } + + + INCHI_HEAPCHK + + *ulProcessingTime += InchiTimeElapsed( ic, pulTStart ); + + return ret; +} + + + + +/* + InChi --> Structure (presented as AuxInfo or MolFile) +*/ +int ConvertInChI2Struct( ICHICONST INPUT_PARMS *ip_inp, + INPUT_PARMS *ip, + InpInChI *pOneInput, + inp_ATOM **at, + int *num_at, + OrigAtDataPolymer **polymer, + OrigAtDataV3000 **v3000, + INCHI_IOSTREAM *pOut, + INCHI_IOSTREAM *pLog, + STRUCT_DATA *sd, + int num_components[INCHI_NUM], + MODE_PIXH nModeProtonIsoExchgH[INCHI_NUM], + char **pszCurHdr, + char *szMsg, + int nMsgLen, + char szMessage[MAX_MSG_LEN], + int nInitLenMessage, + int nMessageLen, + int input_is_stdinchi, + int bHasSomeReconnected, + int bHasSomeFixedH, + int bHasMetal, + int nModeFlagsStereo, + int bTautFlags, + int bReqNonTaut, + unsigned long WarningFlags[2][2], + long num_inp, + long *num_errors , + unsigned char save_opt_bits, + inchiTime *pulTStart, + long *ulProcessingTime, + struct tagINCHI_CLOCK *ic, + struct tagCANON_GLOBALS *pCG) +{ +int ret, i, j; +SRM srm; /* rules how to handle bonds to metal atoms */ +StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM]; + +int bINChIOutputOptions = +#if ( I2S_MODIFY_OUTPUT != 1 ) + 0; +#else + /* transfer user's InChI output options to serialization 10-12-2007 */ + ip_inp->bINChIOutputOptions & + ( + INCHI_OUT_NO_AUX_INFO | /* do not output Aux Info */ + INCHI_OUT_SHORT_AUX_INFO | /* output short version of Aux Info */ + INCHI_OUT_ONLY_AUX_INFO | /* output only Aux Info */ + /* INCHI_OUT_EMBED_REC |*/ /* embed reconnected INChI into disconnected INChI */ + INCHI_OUT_SDFILE_ONLY | /* save input data in a Molfile instead of creating INChI */ + INCHI_OUT_PLAIN_TEXT | /* output plain text INChI */ + INCHI_OUT_PLAIN_TEXT_COMMENTS | /* output plain text annotation */ + /* INCHI_OUT_WINCHI_WINDOW |*/ /* output into wINChI text window */ + INCHI_OUT_TABBED_OUTPUT | /* tab-delimited (only for plain text) */ + INCHI_OUT_SDFILE_ATOMS_DT | /* SDfile output H isotopes as D and T */ + INCHI_OUT_SDFILE_SPLIT | /* Split SDfile into components */ + 0 + ); +#endif + + + + /* Preliminaries */ + + InchiTimeGet(pulTStart); + + if ( input_is_stdinchi ) + { + if ( ip_inp->bINChIOutputOptions & INCHI_OUT_STDINCHI ) + bINChIOutputOptions |= INCHI_OUT_STDINCHI; + } + else + { + if ( ip_inp->bINChIOutputOptions & INCHI_OUT_SAVEOPT ) + bINChIOutputOptions |= INCHI_OUT_SAVEOPT; + } + + memset(pStruct, 0, sizeof(pStruct)); + + SetUpSrm(&srm); /* structure restore parms */ + + + /* Eliminate Fixed-H InChI that are exactly same as the corresponding Mobile-H structures */ + RemoveFixHInChIIdentical2MobH(pOneInput); + + /* Recheck layers after thee elimination; get optional stereo flags */ + ret = DetectInpInchiCreationOptions( pOneInput, + &bHasSomeReconnected, + &bHasMetal, + &bHasSomeFixedH, + &nModeFlagsStereo, + &bTautFlags); + + if ( ret < 0 ) + { + AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, + "Error in detecting input InChI options", "; "); + (*num_errors)++; + goto dealloc; + } + + if ( bHasSomeFixedH && !bReqNonTaut ) + { + bHasSomeFixedH = 0; + } + + /* Set stereo flags */ + ip->nMode &= ~( REQ_MODE_STEREO | + REQ_MODE_ISO_STEREO | + REQ_MODE_RELATIVE_STEREO | + REQ_MODE_RACEMIC_STEREO | + REQ_MODE_CHIR_FLG_STEREO | + REQ_MODE_SB_IGN_ALL_UU | + REQ_MODE_SC_IGN_ALL_UU); + + ip->nMode |= nModeFlagsStereo; + + /* Remove Phosphine and Arsine Stereo Flags */ + ip->bTautFlags &= ~TG_FLAG_PHOSPHINE_STEREO; + ip->bTautFlags &= ~TG_FLAG_ARSINE_STEREO; + ip->bTautFlags &= ~TG_FLAG_FIX_SP3_BUG; + + ip->bTautFlags |= bTautFlags; + + /* Mark Disconnected InChI components that are exactly came as Reconnected ones */ + /* Disconnected will have a negative number of the reconnected component */ + /* Reconnected will have a positive number of the disconnected component */ + + MarkDisconectedIdenticalToReconnected (pOneInput); + + /*****************************************************************************/ + /* Pay attention to: */ + /* 1) .nLink < 0 in Disonnected which means InChI is same as in Reconnected */ + /* The component in Reconnected has .nLink pointing to the Disconnected; */ + /* each .nLink = (1+component index) or -(1+component index) */ + /* In the future .nLink>0 in Disconnected shall point to the Reconnectrd */ + /* component from which it was created */ + /* 2) Currently reversed structures from Disconnected components are created */ + /* and abandoned if Reconnected layer exists */ + /* 3) Connect/disconnect H depends on the presence of atom/bond parity */ + /* The combined Mobile/Fixed-H parity should be set for Fixed-H components*/ + /* 4) No comparison of the Disconnected layer is done if Reconnected exists */ + /* 5) Reading InChI was not fully tested in case one component has stereo in */ + /* both Mobile-H and Fixed-H layers while another component has stereo */ + /* only in Mobile-H layer */ + /*****************************************************************************/ + + + /* Main conversion InChI->Structure for each component and */ + /* after that pStruct[iRec][iMobH][iComponent].at2 is the structure, */ + /* pStruct[iRec][iMobH][iComponent].RevInChI full InChI for the structure */ + /* In case of both Fixed-H and Mobile-H layers the results are in iMobH=0 */ + /* In case of only Mobile-H/Main layer the results are in iMobH=1 */ + + ulProcessingTime += InchiTimeElapsed( ic, pulTStart); + + sd->ulStructTime = 0; + + ret = AllInchiToStructure( ic, pCG, ip, sd, num_inp, + *pszCurHdr, &srm, bHasSomeFixedH, + pStruct, pOneInput); + + ulProcessingTime += sd->ulStructTime; + InchiTimeGet(pulTStart); + + /* ret < 0 is error code; ret > 0 is number of errors */ + /* in pStruct[iInchiRec][iMobileH][iComponent].nError */ + if ( ret) + { + /* conversion error */ + (*num_errors)++; + goto dealloc; + } + + /* Attempt to fix the numumber of removed protons in case of Mobile-H */ + if ( !pOneInput->nNumProtons[INCHI_BAS][TAUT_YES].pNumProtons && + !pOneInput->nNumProtons[INCHI_REC][TAUT_YES].pNumProtons ) + { + ret = AddProtonAndIsoHBalanceToMobHStruct( ic, pCG, + ip, + sd, + num_inp, + bHasSomeFixedH, + *pszCurHdr, + pStruct, + pOneInput); + + if ( ret < 0 ) + { + AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "Add/Remove protons error", "; "); + (*num_errors)++; + goto dealloc; + } + } + + /* Compare InChI from the Reversed Structure to the original input InChI */ + ret = CompareAllOrigInchiToRevInChI(pStruct, + pOneInput, + bHasSomeFixedH, + num_inp, + *pszCurHdr); + if ( ret < 0 ) + { + AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "InChI compare error", "; "); + (*num_errors)++; + goto dealloc; + } + + /* Compare disconnected versions */ + ret = CompareAllDisconnectedOrigInchiToRevInChI(pStruct, + pOneInput, + bHasSomeFixedH, + num_inp, + *pszCurHdr); + if ( ret < 0 ) + { + AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "InChI compare2 error", "; "); + (*num_errors)++; + goto dealloc; + } + + if ( WarningFlags ) + { + for ( i = 0; i < 2; i ++ ) + { + for ( j = 0; j < TAUT_NUM; j ++ ) + { + WarningFlags[i][j] = (unsigned long) pOneInput->CompareInchiFlags[i][j]; + } + } + } + + ulProcessingTime += InchiTimeElapsed( ic, pulTStart); + +#ifndef COMPILE_ANSI_ONLY + ret = DisplayStructureComponents( pCG, + ip, + sd, + num_inp, + *pszCurHdr, + &srm, + bReqNonTaut, + pStruct, + pOneInput); + if ( ret < 0 ) + AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "Display structure error", "; "); +#endif + + + InchiTimeGet(pulTStart); + + ret = MergeStructureComponents( ip, + sd, + num_inp, + *pszCurHdr, + &srm, + bReqNonTaut, + pStruct, + pOneInput); + + ulProcessingTime += InchiTimeElapsed( ic, pulTStart); + + if ( ret < 0 ) + { + AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "Merge Components error", "; "); + (*num_errors)++; + goto dealloc; + } + +#ifdef TARGET_API_LIB +/*------------- for debug only ------------------- + InchiTimeGet(&ulTStart); + ret = OutputInChIOutOfStrFromINChI( ic, pCG, + ip, sd, num_inp, 0, + pOut, pLog, &OneInput, + save_opt_bits); + ulProcessingTime += InchiTimeElapsed( ic, pulTStart); + if ( ret < 0 ) + { + AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "Restored structure to InChI conversion failed", "; "); + goto dealloc; + } +-------------------------------------------------*/ + if ( at && num_at ) + { + *at = pOneInput->atom; + *num_at = pOneInput->num_atoms; + pOneInput->atom = NULL; + *polymer = pOneInput->polymer; + pOneInput->polymer = NULL; + *v3000 = pOneInput->v3000; + pOneInput->v3000 = NULL; + } +#else + + InchiTimeGet(pulTStart); + + ret = OutputInChIOutOfStrFromINChI( ic, pCG, + ip, + sd, + num_inp, + bINChIOutputOptions, + pOut, + NULL, + pOneInput, + bHasSomeFixedH, + save_opt_bits); + + ulProcessingTime += InchiTimeElapsed( ic, pulTStart); + + if ( ret < 0 ) + { + AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "Restored structure to InChI conversion error", "; "); + (*num_errors)++; + goto dealloc; + } +#endif + + if ( szMessage ) + { + int len, retcomp=0, retcomp1=0; + InchiTimeGet(pulTStart); + retcomp = FillOutCompareMessage(szMessage, nMessageLen, pOneInput->CompareInchiFlags[0]); + + if ( pOneInput->CompareInchiFlags[1][0] || pOneInput->CompareInchiFlags[1][1] ) + { + AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "Disconnected: ", "; "); + retcomp1 = FillOutCompareMessage(szMessage, nMessageLen, pOneInput->CompareInchiFlags[1]); + } + /* add a metal warning */ + if ( bHasMetal && nInitLenMessage < (len=(int)strlen(szMessage)) ) + { + char szMetal[] = " (Metal compound)"; + int shift; + if ( len + (int)sizeof(szMetal) > nMessageLen ) + { + len = nMessageLen - (int)sizeof(szMetal); + } + shift = nInitLenMessage + (int)sizeof(szMetal) - 1; + memmove(szMessage+shift, szMessage + nInitLenMessage, (len-nInitLenMessage)*sizeof(szMessage[0])); + memcpy(szMessage + nInitLenMessage, szMetal, sizeof(szMetal)-sizeof(szMessage[0])); + szMessage[shift+len-nInitLenMessage] = '\0'; + } + + retcomp = inchi_min(retcomp, retcomp1); + + if ( retcomp < 0 && + ( ip_inp->bINChIOutputOptions2 & INCHI_OUT_MISMATCH_AS_ERROR) + ) + { + ret = RI_ERR_MISMATCH; + /* AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "* Treated as error by user supplied option", "; "); */ + (*num_errors)++; + goto dealloc; + } + + ulProcessingTime += InchiTimeElapsed( ic, pulTStart); + } + + ret = 0; + + +dealloc: + /* Deallocate */ + if ( ret ) + { + if ( ret < 0 ) + { + if ( ret == CT_USER_QUIT_ERR ) + AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "*Terminated by the user*", "; "); + else + { + AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "*Conversion failed*", "; "); + } + } + else + { + int iRec, iMob, iComp, nComp, len; + char szTemp[128]; + AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, "*Conversion failed on component(s)", "; "); + len = (int) strlen(szMessage); + for ( iRec = 0; iRec < INCHI_NUM; iRec ++ ) + { + for ( iMob = bHasSomeFixedH? TAUT_NON : TAUT_YES; iMob < TAUT_NUM; iMob ++ ) + { + nComp = pOneInput->nNumComponents[iRec][iMob]; + if ( !pStruct[iRec][iMob] ) + { + continue; + } + for ( iComp = 0; iComp < nComp; iComp ++ ) + { + if ( pStruct[iRec][iMob][iComp].nError ) + { + char *szFormula = pOneInput->pInpInChI[iRec][iMob][iComp].szHillFormula; + sprintf (szTemp, +#if ( FIX_DALKE_BUGS == 1 ) + " %s%s%d(%.96s)", +#else + " %s%s%d(%s)", +#endif + !bHasSomeReconnected? "" : iRec? "R" : "D", + !bHasSomeFixedH? "": iMob? "M" : "F", + iComp + 1, szFormula? szFormula : "???"); + AddOneMsg(szMessage, (int)strlen(szMessage), nMessageLen, szTemp, NULL); + } + } + } + } + } /* if ( ret > 0 ) */ + } /* if ( ret ) */ + + + InchiTimeGet(pulTStart); + + /* Print one structure report */ + if ( szMsg && nMsgLen > 1 ) + { + int len = inchi_min( (int)strlen(szMessage), nMsgLen-1); + if ( len > 0 ) + { + memcpy( szMsg, szMessage, len); + szMsg[len] = '\0'; + } + else + { + szMsg[0] = '\0'; + } + } + + if ( nInitLenMessage < (int)strlen(szMessage) ) + { + inchi_ios_eprint(pLog, "%s\n", szMessage); + } +#ifndef TARGET_API_LIB + else + { + /*^^^inchi_ios_eprint( stderr, "%s\r", szMessage );*/ + inchi_fprintf( stderr, "%s\r", szMessage ); + } +#endif + + + + FreeStrFromINChI( pStruct, pOneInput->nNumComponents ); + FreeInpInChI( pOneInput ); + if ( *pszCurHdr ) + { + inchi_free( *pszCurHdr ); + *pszCurHdr = NULL; + } + + INCHI_HEAPCHK + + ulProcessingTime += InchiTimeElapsed( ic, pulTStart ); + + return ret; +} + + +int DetectAndExposePolymerInternals( INCHI_IOSTREAM *is ) +{ +int i, j, elindex, ret = 0, nheavy = 0, + nstars = 0, zlen = 0, star0 = 0, i_last_sym, + slen = 0, ninsert= 0, kinsert, lead_pos, nc, ntimes; +char *p = NULL, *pz = NULL, *pz2 = NULL, *pr = NULL, *pend = NULL, *q = NULL; +char prev_layer_symbol = '0'; +char element[3], *tmpstr = NULL, *edited_s = NULL; +int *insert_pos = NULL; /* inserts go before insert_pos[k] */ + + char *s = is->s.pStr; + + if ( !s ) goto endf; + + p = strstr( s, "InChI=1"); + if ( !p ) goto endf; + + pz = strstr( p, "/z"); + if ( !pz ) goto endf; + pz++; + + while ( isspace( UCINT s[is->s.nUsedLength-1] ) ) + { + s[--is->s.nUsedLength] = '\0'; + } + i_last_sym = is->s.nUsedLength - 1; + + /*if ( is->s.pStr[i_last_sym-1] == '\n' ) + i_last_sym--; + */ + + /* Check formula */ + p = strchr( p, '/' ); + p++; + pend = strchr( p, '/' ); + ntimes = 1; + while ( p != pend ) + { + if ( isdigit( *p ) ) + { + ntimes = (int) inchi_strtol( p, (const char **) &q, 10 ); + p = q; + } + else + { + ntimes = 1; + } + + if ( !isupper( UCINT *p ) ) + { ret = -1; goto endf; } + + j = 0; + element[j++] = *p++; + if ( *p && islower( UCINT *p ) ) + element[j++] = *p++; + element[j++] = '\0'; + if ( *p && isdigit( UCINT *p ) ) + { + elindex = (int) inchi_strtol( p, (const char **) &q, 10 ); + p = q; + } + else + elindex = 1; + if ( !elindex ) + { ret = -1; goto endf; } + + if ( element[0] != 'H' || element[1] ) + nheavy+= ntimes*elindex; + + if ( *p == '.' ) + p++; + } + + /* max num of insert positions is 2 in formulas + Npolymeric units, the latter may not be > nheavy */ + insert_pos = (int *) inchi_calloc( nheavy + 32, sizeof(int) ); + if ( !insert_pos ) + { ret = -2; goto endf; } + + ninsert = 0; + if ( pend ) insert_pos[ninsert] = (int) ( pend - s ); + else insert_pos[ninsert] = i_last_sym; + ninsert++; + + /* Check hidden stars */ + lead_pos = (int) (pz - s); + pend = strchr( pz, '/' ); + if ( pend ) + zlen = (int) ( pend - pz ); + else + zlen = strlen(pz); + tmpstr = (char *) inchi_calloc( zlen + 32, sizeof(char) ); + if ( !tmpstr) + { ret = -2; goto endf; } + memcpy( tmpstr, pz, zlen ); + + ret = DetectHiddenPolymerStuff( tmpstr, zlen, &ninsert, insert_pos, lead_pos, &nstars ); + if ( ret ) goto endf; + if ( !nstars ) goto endf; + + /* Have second '/z' ? */ + pr = strstr( s, "/r" ); + if ( pr ) + { + pr++; + + pend = strchr( pr, '/' ); + if ( pend ) insert_pos[ninsert] = (int) ( pend - s ); + else insert_pos[ninsert] = i_last_sym; + ninsert++; + + pz2 = strstr( pr, "/z"); + if ( pz2 ) + { + pz2++; + lead_pos = (int) ( pz2 - s ); + pend = strchr( pz2, '/' ); + if ( pend ) + zlen = (int) ( pend - pz2 ); + else + zlen = strlen(pz2); + if ( tmpstr ) + inchi_free (tmpstr); + tmpstr = (char *) inchi_calloc( zlen + 32, sizeof(char) ); + if ( !tmpstr) + { ret = -2; goto endf; } + memcpy( tmpstr, pz2, zlen ); + + nstars = 0; + ret = DetectHiddenPolymerStuff( tmpstr, zlen, &ninsert, insert_pos, lead_pos, &nstars ); + if ( ret ) goto endf; + } + } + + slen = strlen ( s ); + edited_s = (char *) inchi_calloc( slen*100 + 32*10*ninsert, sizeof(char) ); /* high reservation */ + if ( !edited_s ) + { ret = -2; goto endf; } + + + /* Edit */ + + nc = 0; + kinsert = 0; + star0 = nheavy + 1; + for (i=0; i< slen; i++) + { + if ( kinsert < ninsert && i == insert_pos[kinsert] ) + { + if ( kinsert == 0 || prev_layer_symbol == 'r') + { + sprintf( tmpstr, ".%dZz", nstars ); + star0 = nheavy + 1; /* reset star numbers pool */ + prev_layer_symbol = '0'; /* avoid printing ';' also */ + } + else + { + sprintf( tmpstr, "%d,%d-", star0, star0 + 1 ); + star0+= 2; + } + kinsert++; + for ( j=0; j < (int) strlen(tmpstr); j++ ) + { + edited_s[nc] = tmpstr[j]; + nc++; + } + } + + if ( i == i_last_sym ) + edited_s[nc++] = s[i]; + + if ( s[i] == '/' || i == i_last_sym ) + { + if ( prev_layer_symbol != '0' && + prev_layer_symbol != 'f' && + prev_layer_symbol != 'z' && + prev_layer_symbol != 'p' && + prev_layer_symbol != 'r' && + prev_layer_symbol != 's' + ) + { + char addon = ';'; + if ( prev_layer_symbol == 'm' ) + addon = '.'; + for ( j=0; js.pStr); + is->s.pStr = NULL; + is->s.nUsedLength = 0;*/ + /*inchi_strbuf_reset( &is->s );*/ + inchi_strbuf_close( &is->s ); + inchi_ios_print( is, "%s\n", edited_s ); + + + +endf: + if ( edited_s ) inchi_free( edited_s ); + if ( tmpstr) inchi_free( tmpstr ); + if ( insert_pos ) inchi_free( insert_pos ); + + return ret; +} + + +/* + +*/ +int DetectHiddenPolymerStuff( char *tmpstr, int tmpstrlen, + int *ninsert, int *insert_pos, + int insert_lead_offset, int *nstars ) +{ +char c; +int opened, skip, i, i0, closed, ret = 0; + + *nstars = opened = skip = i0 = 0; + closed = 1; + for (i=0; i -#include -#include - -#include "mode.h" - -#include "inpdef.h" -#include "extr_ct.h" -#include "ichiring.h" - -/* local prototypes */ -int GetMinRingSize( inp_ATOM* atom, QUEUE *q, AT_RANK *nAtomLevel, S_CHAR *cSource, AT_RANK nMaxRingSize ); - - - - -/*******************************************************************/ -/* add to the queue */ -int QueueAdd( QUEUE *q, QINT_TYPE *Val ); -/* read & remove from the queue */ -int QueueGet( QUEUE *q, QINT_TYPE *Val ); -/* read from the queue */ -int QueueGetAny( QUEUE *q, QINT_TYPE *, int ord ); -/* initialize the queue */ -int QueueReinit( QUEUE *q ); -/* current queue length */ -int QueueLength( QUEUE *q ); -/* number of used queue internal elements */ -int QueueWrittenLength( QUEUE *q ); - - -#if ( QUEUE_QINT == 1 ) /* { */ - -QUEUE *QueueCreate( int nTotLength, int nSize ) -{ - QUEUE *q = NULL; - QINT_TYPE *Val = NULL; - if ( nTotLength < 1 || nSize != (int)sizeof(QINT_TYPE) || - !(q = (QUEUE *) inchi_calloc( 1, sizeof(QUEUE)) ) || - !(Val = (QINT_TYPE *) inchi_calloc( nTotLength, nSize) )) { - if ( q ) inchi_free(q); - return NULL; - } - q->Val = Val; - /* q->nSize = nSize; */ - q->nTotLength = nTotLength; - return q; -} -int QueueAdd( QUEUE *q, QINT_TYPE *Val ) -{ - if ( q && Val && q->nLength < q->nTotLength ) { - q->Val[ (q->nFirst + q->nLength) % q->nTotLength ] = *Val; - q->nLength ++; - return q->nLength; - } - return -1; -} -int QueueGet( QUEUE *q, QINT_TYPE *Val ) -{ - if ( q && Val && q->nLength > 0 ) { - *Val = q->Val[ q->nFirst ]; - /* new: do not allow to overwrite the retrieved value */ - q->nFirst = (q->nFirst == q->nTotLength - 1)? 0 : q->nFirst + 1; - q->nLength --; - /* -- old -- - if ( -- q->nLength ) { - q->nFirst = (q->nFirst == q->nTotLength - 1)? 0 : q->nFirst + 1; - } - */ - return q->nLength; - } - return -1; -} -int QueueGetAny( QUEUE *q, QINT_TYPE *Val, int ord ) -{ - if ( 0 <= ord && ord < q->nTotLength ) { - *Val = q->Val[ ord ]; - return 1; /* success */ - } else { - return -1; /* error */ - } -} - -#else /* } QUEUE_QINT == 1 { */ - -QUEUE *QueueCreate( int nTotLength, int nSize ) -{ - QUEUE *q = NULL; - QINT_TYPE *Val = NULL; - if ( nTotLength < 1 || nSize < 1 || - !(q = (QUEUE *) inchi_calloc( 1, sizeof(QUEUE)) ) || - !(Val = (QINT_TYPE *) inchi_calloc( nTotLength, nSize) )) { - if ( q ) inchi_free(q); - return NULL; - } - q->Val = Val; - q->nSize = nSize; - q->nTotLength = nTotLength; - return q; -} -int QueueAdd( QUEUE *q, QINT_TYPE *Val ) -{ - if ( q && Val && q->nLength < q->nTotLength ) { - memcpy( (char*)q->Val + ((q->nFirst + q->nLength) % q->nTotLength)*q->nSize, Val, q->nSize); - q->nLength ++; - return q->nLength; - } - return -1; -} -int QueueGet( QUEUE *q, QINT_TYPE *Val ) -{ - if ( q && Val && q->nLength > 0 ) { - memcpy( Val, (char*)q->Val + q->nFirst * q->nSize, q->nSize); - if ( -- q->nLength ) { - q->nFirst = (q->nFirst == q->nTotLength - 1)? 0 : q->nFirst + 1; - } - return q->nLength; - } - return -1; -} -int QueueGetAny( QUEUE *q, QINT_TYPE *Val, int ord ) -{ - if ( 0 <= ord && ord < q->nTotLength ) { - memcpy( Val, (char*)q->Val + ord * q->nSize, q->nSize); - return 1; /* success */ - } else { - return -1; /* error */ - } -} - -#endif /* } QUEUE_QINT == 1 */ - -QUEUE *QueueDelete( QUEUE *q ) -{ - if ( q ) { - if ( q->Val ) inchi_free(q->Val); - inchi_free( q ); - } - return NULL; -} -int QueueReinit( QUEUE *q ) -{ - if ( q ) { - q->nFirst = 0; - q->nLength = 0; - /* memset( q->Val, 0, q->nTotLength*sizeof(q->Val[0])); */ /* for debug only */ - return q->nTotLength; - } - return -1; -} -int QueueLength( QUEUE *q ) -{ - if ( q ) { - return q->nLength; - } else { - return 0; - } -} -int QueueWrittenLength( QUEUE *q ) -{ - if ( q ) { - int len = q->nFirst+q->nLength; - return (len > q->nTotLength)? q->nTotLength : len; - } else { - return 0; - } -} - -/**********************************************************************************/ -/* BFS: Breadth First Search */ -int GetMinRingSize( inp_ATOM* atom, QUEUE *q, AT_RANK *nAtomLevel, S_CHAR *cSource, AT_RANK nMaxRingSize ) -{ - int qLen, i, j; - AT_RANK nCurLevel, nRingSize, nMinRingSize=MAX_ATOMS+1; - qInt at_no, next; - int iat_no, inext; - - while ( qLen = QueueLength( q ) ) { - /* traverse the next level (next outer ring) */ - for ( i = 0; i < qLen; i ++ ) { - if ( 0 <= QueueGet( q, &at_no ) ) { - iat_no = (int)at_no; - nCurLevel = nAtomLevel[iat_no] + 1; - if ( 2*nCurLevel > nMaxRingSize + 4 ) { - /* 2*nCurLevel = nRingSize + 3 + k, k = 0 or 1 */ - if ( nMinRingSize < MAX_ATOMS+1 ) { - return (nMinRingSize >= nMaxRingSize)? 0 : nMinRingSize; - } - return 0; /* min. ring size > nMaxRingSize */ - } - for ( j = 0; j < atom[iat_no].valence; j ++ ) { - next = (qInt)atom[iat_no].neighbor[j]; - inext = (int)next; - if ( !nAtomLevel[inext] ) { - /* the at_no neighbor has not been traversed yet. Add it to the queue */ - if ( 0 <= QueueAdd( q, &next ) ) { - nAtomLevel[inext] = nCurLevel; - cSource[inext] = cSource[iat_no]; /* keep the path number */ - } else { - return -1; /* error */ - } - } else - if ( nAtomLevel[inext]+1 >= nCurLevel && - cSource[inext] != cSource[iat_no] - /* && cSource[(int)next] != -1 */ - ) { - /* found a ring closure */ - /* debug */ - if ( cSource[inext] == -1 ) { - return -1; /* error */ - } - if ( (nRingSize = nAtomLevel[inext] + nCurLevel - 2) < nMinRingSize ) { - nMinRingSize = nRingSize; - } - /* return (nRingSize >= nMaxRingSize)? 0 : nRingSize; */ - } - } - } else { - return -1; /* error */ - } - } - } - - if ( nMinRingSize < MAX_ATOMS+1 ) { - return (nMinRingSize >= nMaxRingSize)? 0 : nMinRingSize; - } - - return 0; -} -/*******************************************************************/ -/* Return value: - 0: nMaxRingSize < 3 or - min. ring size >= nMaxRingSize or - not a ring bond (the last is currently impossible: bond is known to belong to a ring system. - n>0: min. ring size < nMaxRingSize - n<0: error - - Input: - atom[] - at_no number of the 1st atom adjacent to the bond - neigh_ord ordering number of the bond in question: at[at_no].bond_type[neigh_ord] - q queue structure - nAtomLevel work array, DFS distance - cSource work array, origin mark -*/ - -int is_bond_in_Nmax_memb_ring( inp_ATOM* atom, int at_no, int neigh_ord, QUEUE *q, AT_RANK *nAtomLevel, S_CHAR *cSource, AT_RANK nMaxRingSize ) -{ - int nMinRingSize = -1, i; - qInt n; - int nTotLen; - - if ( nMaxRingSize < 3 ) { - return 0; - } - - QueueReinit( q ); - - /* mark the starting atom */ - nAtomLevel[at_no] = 1; - cSource[at_no] = -1; - /* add neighbors */ - for ( i = 0; i < atom[at_no].valence; i ++ ) { - n = (qInt)atom[at_no].neighbor[i]; - nAtomLevel[(int)n] = 2; - cSource[(int)n] = 1 + (i==neigh_ord); - QueueAdd( q, &n ); - } - - nMinRingSize = GetMinRingSize( atom, q, nAtomLevel, cSource, nMaxRingSize ); - /* cleanup */ - nTotLen = QueueWrittenLength( q ); - for ( i = 0; i < nTotLen; i ++ ) { - if ( 0 < QueueGetAny( q, &n, i ) ) { - nAtomLevel[(int)n] = 0; - cSource[(int)n] = 0; - } - } - nAtomLevel[at_no] = 0; - cSource[at_no] = 0; - - -/* - if ( nAtomLevel ) - inchi_free ( nAtomLevel ); - if ( cSource ) - inchi_free ( cSource ); - QueueDelete( q ); -*/ - return nMinRingSize; -} -/*******************************************************************/ -int is_atom_in_3memb_ring( inp_ATOM* atom, int at_no ) -{ - AT_NUMB neigh_neigh; - int i, j, k, val, val_neigh, neigh; - - if ( atom[at_no].nNumAtInRingSystem < 3 ) { - return 0; - } - - for ( i = 0, val = atom[at_no].valence; i < val; i ++ ) { - neigh = (int)atom[at_no].neighbor[i]; - if ( atom[at_no].nRingSystem != atom[neigh].nRingSystem ) - continue; - for ( j = 0, val_neigh = atom[neigh].valence; j < val_neigh; j ++ ) { - neigh_neigh = atom[neigh].neighbor[j]; - if ( (int)neigh_neigh == at_no ) - continue; - for ( k = 0; k < val; k ++ ) { - if ( atom[at_no].neighbor[k] == neigh_neigh ) { - return 1; - } - } - } - } - return 0; -} +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include "mode.h" +#include "ichiring.h" + +/* Local prototypes */ +int GetMinRingSize( inp_ATOM* atom, QUEUE *q, AT_RANK *nAtomLevel, S_CHAR *cSource, AT_RANK nMaxRingSize ); +/*******************************************************************/ +/* add to the queue */ +int QueueAdd( QUEUE *q, QINT_TYPE *Val ); +/* read & remove from the queue */ +int QueueGet( QUEUE *q, QINT_TYPE *Val ); +/* read from the queue */ +int QueueGetAny( QUEUE *q, QINT_TYPE *, int ord ); +/* initialize the queue */ +int QueueReinit( QUEUE *q ); +/* current queue length */ +int QueueLength( QUEUE *q ); +/* number of used queue internal elements */ +int QueueWrittenLength( QUEUE *q ); + + +#if ( QUEUE_QINT == 1 ) /* { */ + +QUEUE *QueueCreate( int nTotLength, int nSize ) +{ + QUEUE *q = NULL; + QINT_TYPE *Val = NULL; + if ( nTotLength < 1 || nSize != (int)sizeof(QINT_TYPE) || + !(q = (QUEUE *) inchi_calloc( 1, sizeof(QUEUE)) ) || + !(Val = (QINT_TYPE *) inchi_calloc( nTotLength, nSize) )) { + if ( q ) inchi_free(q); + return NULL; + } + q->Val = Val; + /* q->nSize = nSize; */ + q->nTotLength = nTotLength; + return q; +} +int QueueAdd( QUEUE *q, QINT_TYPE *Val ) +{ + if ( q && Val && q->nLength < q->nTotLength ) { + q->Val[ (q->nFirst + q->nLength) % q->nTotLength ] = *Val; + q->nLength ++; + return q->nLength; + } + return -1; +} +int QueueGet( QUEUE *q, QINT_TYPE *Val ) +{ + if ( q && Val && q->nLength > 0 ) { + *Val = q->Val[ q->nFirst ]; + /* new: do not allow to overwrite the retrieved value */ + q->nFirst = (q->nFirst == q->nTotLength - 1)? 0 : q->nFirst + 1; + q->nLength --; + /* -- old -- + if ( -- q->nLength ) { + q->nFirst = (q->nFirst == q->nTotLength - 1)? 0 : q->nFirst + 1; + } + */ + return q->nLength; + } + return -1; +} +int QueueGetAny( QUEUE *q, QINT_TYPE *Val, int ord ) +{ + if ( 0 <= ord && ord < q->nTotLength ) { + *Val = q->Val[ ord ]; + return 1; /* success */ + } else { + return -1; /* error */ + } +} + +#else /* } QUEUE_QINT == 1 { */ + +QUEUE *QueueCreate( int nTotLength, int nSize ) +{ + QUEUE *q = NULL; + QINT_TYPE *Val = NULL; + if ( nTotLength < 1 || nSize < 1 || + !(q = (QUEUE *) inchi_calloc( 1, sizeof(QUEUE)) ) || + !(Val = (QINT_TYPE *) inchi_calloc( nTotLength, nSize) )) { + if ( q ) inchi_free(q); + return NULL; + } + q->Val = Val; + q->nSize = nSize; + q->nTotLength = nTotLength; + return q; +} +int QueueAdd( QUEUE *q, QINT_TYPE *Val ) +{ + if ( q && Val && q->nLength < q->nTotLength ) { + memcpy( (char*)q->Val + ((q->nFirst + q->nLength) % q->nTotLength)*q->nSize, Val, q->nSize); + q->nLength ++; + return q->nLength; + } + return -1; +} +int QueueGet( QUEUE *q, QINT_TYPE *Val ) +{ + if ( q && Val && q->nLength > 0 ) { + memcpy( Val, (char*)q->Val + q->nFirst * q->nSize, q->nSize); + if ( -- q->nLength ) { + q->nFirst = (q->nFirst == q->nTotLength - 1)? 0 : q->nFirst + 1; + } + return q->nLength; + } + return -1; +} +int QueueGetAny( QUEUE *q, QINT_TYPE *Val, int ord ) +{ + if ( 0 <= ord && ord < q->nTotLength ) { + memcpy( Val, (char*)q->Val + ord * q->nSize, q->nSize); + return 1; /* success */ + } else { + return -1; /* error */ + } +} + +#endif /* } QUEUE_QINT == 1 */ + +QUEUE *QueueDelete( QUEUE *q ) +{ + if ( q ) { + if ( q->Val ) inchi_free(q->Val); + inchi_free( q ); + } + return NULL; +} +int QueueReinit( QUEUE *q ) +{ + if ( q ) { + q->nFirst = 0; + q->nLength = 0; + /* memset( q->Val, 0, q->nTotLength*sizeof(q->Val[0])); */ /* for debug only */ + return q->nTotLength; + } + return -1; +} +int QueueLength( QUEUE *q ) +{ + if ( q ) { + return q->nLength; + } else { + return 0; + } +} +int QueueWrittenLength( QUEUE *q ) +{ + if ( q ) { + int len = q->nFirst+q->nLength; + return (len > q->nTotLength)? q->nTotLength : len; + } else { + return 0; + } +} + +/**********************************************************************************/ +/* BFS: Breadth First Search */ +int GetMinRingSize( inp_ATOM* atom, QUEUE *q, AT_RANK *nAtomLevel, S_CHAR *cSource, AT_RANK nMaxRingSize ) +{ + int qLen, i, j; + AT_RANK nCurLevel, nRingSize, nMinRingSize=MAX_ATOMS+1; + qInt at_no, next; + int iat_no, inext; + + while ( qLen = QueueLength( q ) ) { + /* traverse the next level (next outer ring) */ + for ( i = 0; i < qLen; i ++ ) { + if ( 0 <= QueueGet( q, &at_no ) ) { + iat_no = (int)at_no; + nCurLevel = nAtomLevel[iat_no] + 1; + if ( 2*nCurLevel > nMaxRingSize + 4 ) { + /* 2*nCurLevel = nRingSize + 3 + k, k = 0 or 1 */ + if ( nMinRingSize < MAX_ATOMS+1 ) { + return (nMinRingSize >= nMaxRingSize)? 0 : nMinRingSize; + } + return 0; /* min. ring size > nMaxRingSize */ + } + for ( j = 0; j < atom[iat_no].valence; j ++ ) { + next = (qInt)atom[iat_no].neighbor[j]; + inext = (int)next; + if ( !nAtomLevel[inext] ) { + /* the at_no neighbor has not been traversed yet. Add it to the queue */ + if ( 0 <= QueueAdd( q, &next ) ) { + nAtomLevel[inext] = nCurLevel; + cSource[inext] = cSource[iat_no]; /* keep the path number */ + } else { + return -1; /* error */ + } + } else + if ( nAtomLevel[inext]+1 >= nCurLevel && + cSource[inext] != cSource[iat_no] + /* && cSource[(int)next] != -1 */ + ) { + /* found a ring closure */ + /* debug */ + if ( cSource[inext] == -1 ) { + return -1; /* error */ + } + if ( (nRingSize = nAtomLevel[inext] + nCurLevel - 2) < nMinRingSize ) { + nMinRingSize = nRingSize; + } + /* return (nRingSize >= nMaxRingSize)? 0 : nRingSize; */ + } + } + } else { + return -1; /* error */ + } + } + } + + if ( nMinRingSize < MAX_ATOMS+1 ) { + return (nMinRingSize >= nMaxRingSize)? 0 : nMinRingSize; + } + + return 0; +} +/*******************************************************************/ +/* Return value: + 0: nMaxRingSize < 3 or + min. ring size >= nMaxRingSize or + not a ring bond (the last is currently impossible: bond is known to belong to a ring system. + n>0: min. ring size < nMaxRingSize + n<0: error + + Input: + atom[] + at_no number of the 1st atom adjacent to the bond + neigh_ord ordering number of the bond in question: at[at_no].bond_type[neigh_ord] + q queue structure + nAtomLevel work array, DFS distance + cSource work array, origin mark +*/ + +int is_bond_in_Nmax_memb_ring( inp_ATOM* atom, int at_no, int neigh_ord, QUEUE *q, AT_RANK *nAtomLevel, S_CHAR *cSource, AT_RANK nMaxRingSize ) +{ + int nMinRingSize = -1, i; + qInt n; + int nTotLen; + + if ( nMaxRingSize < 3 ) { + return 0; + } + + QueueReinit( q ); + + /* mark the starting atom */ + nAtomLevel[at_no] = 1; + cSource[at_no] = -1; + /* add neighbors */ + for ( i = 0; i < atom[at_no].valence; i ++ ) { + n = (qInt)atom[at_no].neighbor[i]; + nAtomLevel[(int)n] = 2; + cSource[(int)n] = 1 + (i==neigh_ord); + QueueAdd( q, &n ); + } + + nMinRingSize = GetMinRingSize( atom, q, nAtomLevel, cSource, nMaxRingSize ); + /* cleanup */ + nTotLen = QueueWrittenLength( q ); + for ( i = 0; i < nTotLen; i ++ ) { + if ( 0 < QueueGetAny( q, &n, i ) ) { + nAtomLevel[(int)n] = 0; + cSource[(int)n] = 0; + } + } + nAtomLevel[at_no] = 0; + cSource[at_no] = 0; + + +/* + if ( nAtomLevel ) + inchi_free ( nAtomLevel ); + if ( cSource ) + inchi_free ( cSource ); + QueueDelete( q ); +*/ + return nMinRingSize; +} +/*******************************************************************/ +int is_atom_in_3memb_ring( inp_ATOM* atom, int at_no ) +{ + AT_NUMB neigh_neigh; + int i, j, k, val, val_neigh, neigh; + + if ( atom[at_no].nNumAtInRingSystem < 3 ) { + return 0; + } + + for ( i = 0, val = atom[at_no].valence; i < val; i ++ ) { + neigh = (int)atom[at_no].neighbor[i]; + if ( atom[at_no].nRingSystem != atom[neigh].nRingSystem ) + continue; + for ( j = 0, val_neigh = atom[neigh].valence; j < val_neigh; j ++ ) { + neigh_neigh = atom[neigh].neighbor[j]; + if ( (int)neigh_neigh == at_no ) + continue; + for ( k = 0; k < val; k ++ ) { + if ( atom[at_no].neighbor[k] == neigh_neigh ) { + return 1; + } + } + } + } + return 0; +} diff --git a/INCHI-1-SRC/INCHI/common/ichiring.h b/INCHI-1-SRC/INCHI_BASE/src/ichiring.h similarity index 64% rename from INCHI-1-SRC/INCHI/common/ichiring.h rename to INCHI-1-SRC/INCHI_BASE/src/ichiring.h index 7ee2498..91acaec 100644 --- a/INCHI-1-SRC/INCHI/common/ichiring.h +++ b/INCHI-1-SRC/INCHI_BASE/src/ichiring.h @@ -1,80 +1,82 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHIRING_H__ -#define __INCHIRING_H__ -#define QUEUE_QINT 1 -typedef AT_RANK qInt; /* queue optimization: known type */ - -#if ( QUEUE_QINT == 1 ) -#define QINT_TYPE qInt -#else -#define QINT_TYPE void -#endif - -typedef struct tagQieue { - QINT_TYPE *Val; - int nTotLength; - int nFirst; /* element to remove if nLength > 0 */ - int nLength; /* (nFirst + nLength) is next free position */ -#if ( QUEUE_QINT != 1 ) - int nSize; -#endif -}QUEUE; - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -QUEUE *QueueCreate( int nTotLength, int nSize ); -QUEUE *QueueDelete( QUEUE *q ); -int is_bond_in_Nmax_memb_ring( inp_ATOM* atom, int at_no, int neigh_ord, QUEUE *q, AT_RANK *nAtomLevel, S_CHAR *cSource, AT_RANK nMaxRingSize ); -int is_atom_in_3memb_ring( inp_ATOM* atom, int at_no ); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /* __INCHIRING_H__ */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _INCHIRING_H_ +#define _INCHIRING_H_ + +#include "extr_ct.h" + +#define QUEUE_QINT 1 +typedef AT_RANK qInt; /* queue optimization: known type */ + +#if ( QUEUE_QINT == 1 ) +#define QINT_TYPE qInt +#else +#define QINT_TYPE void +#endif + +typedef struct tagQieue { + QINT_TYPE *Val; + int nTotLength; + int nFirst; /* element to remove if nLength > 0 */ + int nLength; /* (nFirst + nLength) is next free position */ +#if ( QUEUE_QINT != 1 ) + int nSize; +#endif +}QUEUE; + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + +QUEUE *QueueCreate( int nTotLength, int nSize ); +QUEUE *QueueDelete( QUEUE *q ); +int is_bond_in_Nmax_memb_ring( inp_ATOM* atom, int at_no, int neigh_ord, QUEUE *q, AT_RANK *nAtomLevel, S_CHAR *cSource, AT_RANK nMaxRingSize ); +int is_atom_in_3memb_ring( inp_ATOM* atom, int at_no ); + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + + +#endif /* _INCHIRING_H_ */ diff --git a/INCHI-1-SRC/INCHI/common/ichirvr1.c b/INCHI-1-SRC/INCHI_BASE/src/ichirvr1.c similarity index 81% rename from INCHI-1-SRC/INCHI/common/ichirvr1.c rename to INCHI-1-SRC/INCHI_BASE/src/ichirvr1.c index 4234e16..1f7ea08 100644 --- a/INCHI-1-SRC/INCHI/common/ichirvr1.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichirvr1.c @@ -1,4953 +1,5989 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - -/*^^^ */ -/*#define CHECK_WIN32_VC_HEAP*/ -#include "mode.h" - -#if ( READ_INCHI_STRING == 1 ) - -#include "ichi.h" -#include "ichitime.h" - -#include "inpdef.h" -#include "ichimain.h" -#include "ichierr.h" -#include "incomdef.h" -#include "ichiring.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "util.h" - -#include "ichicomp.h" -#include "ichister.h" - -#include "ichi_bns.h" - -#include "strutil.h" - -#include "ichirvrs.h" - - -/************************************************************************************************** - - ChargeStruct fictitios structures MY_CONST CN_LIST cnList[*] - ============================================================ - - - bond flow (+) => Positive charge c-group - ----------------- (-) => Negative charge c-group - Single 0 (+C) => Positive charge group for C, Si, Ge, Sn, Pb - Double 1 (-C) => Negative charge group for C, Si, Ge, Sn, Pb - Triple 2 (.) => additional one unit of st_cap - - A) Interpretation: - - X-(-) or X=(+) => zero charge - X=(-) or X-(+) => charge = -1 or +1, respectively - - B) Information to keep: - - ordering zero-based number of the edge - to (+) or (-) from the Interpretation (A) section - - vCap = vertex cap - vFlow = vertex flow - val = number of edges incident to the vertex - neigh = 1-based ordering number of the adjacent vertex; 0 => no more adjacent vertices - cap = cap of the edge to the adjacent vertex - flow = flow of the edge to the adjacent vertex - - atom (c-point) always has number 1 - c-group(s) always are the last vertices - always adjacent_neigh_number > vertex_number, that is, neigh > vertex - - Contribution to the Total Charge: - ---------------------------------- - edge_cap(+) - edge_flow(+) - edge_flow(-) - Delta(+) - Delta(-) - - where edge_cap(+) is edge capacity to c-group (+); - edge_flow(+) is edge capacity to c-group (?), (?)= (+) or (-); - Delta(?) = st_cap(?) - st_floe(?) of the c-group vertex (?), (?)= (+) or (-); - -***************************************************************************************************/ -/************************************************************************************************** - - Important: - vCap and vFlow Note: since metal charge group (vert. 2-4) MUST be registered before - marked with empty the "metal flower" (5-8) all charge group vertex numbers are - comments for vertices less than metal flower vertices: (2,3,4) < (5,6,7,8) - 1 and 5(M) should This MAY be neded for c-group enumeration. The order is: - be set separately t-groups, c-groups, M-flower. All types BNS_VT_M_GROUP allows to avoid duplications. - - 3(+) - || (Metal) - || \|/ init charge=0; MAX_METAL_CHARGE = 16 - 4(-) 5(M) 2 -Fe- CAP(BOND_TO_BNS_VT_M_GROUP) = NUM_BONDS*CAP - \ | / | - \ | / - 1 X(V), V=valence -*/ -MY_CONST C_NODE cnMe[5] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM,0/**/ ,0/**/,3}, {{ 2, 16,0, 0 },{ 4, 16,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_CHRG_STRUCT,16, 16, 2}, {{ 3, 16,0,16 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_C_POS_M, 16, 16, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 2 }, { 0, 0,0, 0 }} }, /* 3 */ - { {BNS_VT_C_NEG_M, 0+16, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ - { {BNS_VT_M_GROUP, 0/**/ ,0/**/,3}, {{ 1, 3,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ -}; -/* -#define cn_bits_Me (-1) -*/ - -/************************************************************************************************** - c=2 5(+.) - _____ / (PNPN) - 4=====3 |||| init charge=0 - c=2 \ / c=1 -N- - 2 | - |||| - 1 X+(V), X(V+1), X+(V+2), X(V+3); V=valence -*/ -MY_CONST C_NODE cnPNPN[5] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 3, 3, 1}, {{ 2, 3,0, 3 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_CHRG_STRUCT, 3, 3, 3}, {{ 3, 1,0, 0 },{ 4, 2,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 5, 1,0, 0 },{ 4, 2,0, 2 }, { 0, 0,0, 0 }} }, /* 3 */ - { {BNS_VT_CHRG_STRUCT, 2, 2, 2}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ - { {BNS_VT_C_POS, 1+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ -}; -/* -#define cn_bits_PNPN MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_P, cn_bits_N) -*/ -/************************************************************************************************** - 5(+) - c=1 // (NPNP) - 4=====3 |||| init charge=0 - c=1 \ / c=2 -N- - 2 | - |||| - 1 X(V), X+(V+1), X(V+2), X+(V+3); V=valence -*/ -MY_CONST C_NODE cnNPNP[5] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 3, 3, 1}, {{ 2, 3,0, 3 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_CHRG_STRUCT, 3, 3, 3}, {{ 3, 2,0, 0 },{ 4, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 5, 1,0, 1 },{ 4, 1,0, 1 }, { 0, 0,0, 0 }} }, /* 3 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ - { {BNS_VT_C_POS, 0+1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ -}; -/* -#define cn_bits_NPNP MAKE_CN_BITS(cn_bits_N, cn_bits_P, cn_bits_N, cn_bits_P) -*/ -/********************* end new ********************************************************************/ - - -/************************************************************************************************** - 5(+) - // (NPN) - 4=====3 ||| init charge=0 - \ / -N- - 2 | - ||| - 1 X(V), X+(V+1), X(V+2); V=valence -*/ -MY_CONST C_NODE cnNPN[5] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 2, 2, 1}, {{ 2, 2,0, 2 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 3, 1,0, 0 },{ 4, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 5, 1,0, 1 },{ 4, 1,0, 1 }, { 0, 0,0, 0 }} }, /* 3 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ - { {BNS_VT_C_POS, 1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ -}; -/* -#define cn_bits_NPN MAKE_CN_BITS(cn_bits_N, cn_bits_P, cn_bits_N, 0) -*/ -/************************************************************************************************** - 5(+.) - / (PNP) - 4=====3 ||| init charge=0 - \ / -Cl- - 2 /\ - ||| - 1 X+(V), X(V+1), X+(V+2); V=valence -*/ -MY_CONST C_NODE cnPNP[5] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 2, 2, 1}, {{ 2, 2,0, 2 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 3, 1,0, 0 },{ 4, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 3}, {{ 5, 1,0, 0 },{ 4, 1,0, 1 }, { 0, 0,0, 0 }} }, /* 3 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ - { {BNS_VT_C_POS, 1+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ -}; -/* -#define cn_bits_PNP MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_P, 0) -*/ -/************************************************************************************************** - - (MNP) - \ / init charge=0 - N(.) - 3(-) 2(+) / \ - \ // - 1(.) X-(V), X(V+1), X+(V+2); V=valence -*/ -MY_CONST C_NODE cnMNP[3] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 2, 1, 2}, {{ 2, 1,0, 1 },{ 3, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_C_POS, 1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} } /* 3 */ -}; -/* -#define cn_bits_MNP MAKE_CN_BITS(cn_bits_M, cn_bits_N, cn_bits_P, 0) -*/ -#ifdef NEVER -/************************** not used ************************************************************** - (PNM) - 5(-) 4(+) \\ / - \ // B(.) init charge=0 - 3 2 / \ - \\ / - 1(.) X+(V), X(V+1), X+(V+2); V=valence -*/ -MY_CONST C_NODE cnPNM[5] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 2, 1, 2}, {{ 2, 1,0, 0 },{ 3, 1,0, 1 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 4, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 5, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 3 */ - { {BNS_VT_C_POS, 1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ - { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ -}; - -#define cn_bits_PNM MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_M, 0) - -#endif - -/************************************************************************************************** - 4(-) 3(+.) (PNM) - \ / ||| init charge=0 - 2 --P-- - ||| / \ - 1 X-(V), X(V+1), X+(V+2); V=valence -*/ -MY_CONST C_NODE cnPNM[4] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 2, 2, 1}, {{ 2, 2,0, 2 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 3, 1,0, 0 },{ 4, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_C_POS, 1+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 3 */ - { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ -}; /* explanaton of vCap: ^ ^ */ -/* additional dot:/ \ capacity of the edge to (+) or (-) vertex */ -/* -#define cn_bits_PNM MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_M, 0) -*/ -/************************************************************************************************** - 5(+C) - // init charge=0 - 6(-C) 4 - \ / (EN) E=either +1 or -1 - 3 | - || -C(.)- - 2 | - | - 1(.) X-(V), X+(V), X(V+1); V=valence -*/ -MY_CONST C_NODE cnEN[6] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 1, 0, 1}, {{ 2, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 3, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 3}, {{ 4, 1,0, 0 },{ 6, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 3 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 5, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ - { {BNS_VT_C_POS_C, 0+1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ - { {BNS_VT_C_NEG_C, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} } /* 6 */ -}; -/* -#define cn_bits_EN MAKE_CN_BITS(cn_bits_P | cn_bits_M, cn_bits_N, 0, 0) -*/ -/************************************************************************************************** - 5(-) - / (NMN) init charge=0 - 4=====3 ||| - \ / -X- - 2 /\ - ||| - 1 X(V), X-(V+1), X(V+2); V=valence -*/ -MY_CONST C_NODE cnNMN[5] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 2, 2, 1}, {{ 2, 2,0, 2 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 3, 1,0, 0 },{ 4, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 3}, {{ 5, 1,0, 0 },{ 4, 1,0, 1 }, { 0, 0,0, 0 }} }, /* 3 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ - { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} } /* 5 */ -}; -/* -#define cn_bits_NMN MAKE_CN_BITS(cn_bits_N, cn_bits_M, cn_bits_N, 0) -*/ -/************************************************************************************************** - 4(+) - // (NE) E=either +1 or -1 - 5(-) 3 || - \ / -X- init charge=0 - 2 | - || - 1 X(V), X+(V+1), X-(V+1); V=valence -*/ -MY_CONST C_NODE cnNE[5] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 1, 1, 1}, {{ 2, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 3}, {{ 3, 1,0, 0 },{ 5, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 4, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 3 */ - { {BNS_VT_C_POS, 0+1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ - { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} } /* 5 */ -}; -/* -#define cn_bits_NE MAKE_CN_BITS(cn_bits_N, cn_bits_P | cn_bits_M, 0, 0) -*/ -/************************************************************************************************** -6(-) 5(+) - \ // (NEN) - 4=====3 ||| init charge=0 - \ / -X- - 2 | - ||| - 1 X(V), X+(V+1), X-(V+1), X(V+2); V=valence -*/ -MY_CONST C_NODE cnNEN[6] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 2, 2, 1}, {{ 2, 2,0, 2 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 3, 1,0, 0 },{ 4, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 5, 1,0, 1 },{ 4, 1,0, 1 }, { 0, 0,0, 0 }} }, /* 3 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 3}, {{ 6, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ - { {BNS_VT_C_POS, 0+1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ - { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 6 */ -}; -/* -#define cn_bits_NEN MAKE_CN_BITS(cn_bits_N, cn_bits_M | cn_bits_N, cn_bits_N, 0) -*/ -/*=======================================================*/ -/************************************************************************************************** - (NP) - || - -X- init charge=0 - 2(+) | - || - 1 X(V), X+(V+1); V=valence -*/ -MY_CONST C_NODE cnNP[2] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 1, 1, 1}, {{ 2, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_C_POS, 0+1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ -}; -/* -#define cn_bits_NP MAKE_CN_BITS(cn_bits_N, cn_bits_P, 0, 0) -*/ -/************************************************************************************************** - (PN) - 3(+.) || init charge=0 [because cap(+)-flow(+)-Delta=1-0-1=0] - | -X- - 2 | - || - 1 X+(V), X(V+1); V=valence -*/ -MY_CONST C_NODE cnPN[3] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 1, 1, 1}, {{ 2, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 3, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_C_POS, 1+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 3 */ -}; -/* -#define cn_bits_PN MAKE_CN_BITS(cn_bits_P, cn_bits_N, 0, 0) -*/ -/************************************************************************************************** - (NM) - 3(-) || init charge=0 - | -X- - 2 | - || - 1 X(V), X-(V+1); V=valence -*/ -MY_CONST C_NODE cnNM[3] = { - /* vertex type vCap vFlow; val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 1, 1, 1}, {{ 2, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 3, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ - { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 3 */ -}; -/* -#define cn_bits_NM MAKE_CN_BITS(cn_bits_N, cn_bits_M, 0, 0) -*/ -/************************************************************************************************** - (MN) - | - -X- init charge=0 - 2(-) | - | - 1(.) X-(V), X(V+1); V=valence -*/ -MY_CONST C_NODE cnMN[2] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 1, 0, 1}, {{ 2, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ -}; -/* -#define cn_bits_MN MAKE_CN_BITS(cn_bits_M, cn_bits_N, 0, 0) -*/ -/************************************************************************************************** - (P) - | - -X- init charge=0 - 2(+.) | - | - 1 X+(V); V=valence; all chemical (real) bonds to X have cap=0 -*/ -MY_CONST C_NODE cnP_[2] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 0, 0, 1}, {{ 2, 1,1, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_C_POS, 1+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ -}; -/* -#define cn_bits_P_ MAKE_CN_BITS(cn_bits_P, 0, 0, 0) -*/ -#ifdef NEVER -/************************************************************************************************** - (M) - | - -X- init charge=-1 on atom - 2(-) | - | - 1(.) X+(V); V=valence; all chemical (real) bonds to X have cap=0 -*/ -MY_CONST C_NODE cnM_[2] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 1, 0, 1}, {{ 2, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ - { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ -}; -#endif - -MY_CONST C_NODE cnM_[1] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 0, 0, 0}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ -}; -/* -#define cn_bits_M_ MAKE_CN_BITS(cn_bits_M, 0, 0, 0) -*/ -/************************************************************************************************** - - - -X- init charge=0 - | - - 1 X(V); V=valence; -*/ -MY_CONST C_NODE cnN_[1] = { - /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ - { {BNS_VERT_TYPE_ATOM, 0, 0, 0}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ -}; -/* -#define cn_bits_N_ MAKE_CN_BITS(cn_bits_N, 0, 0, 0) -*/ -/**************************************************************************************************/ - - -MY_CONST CN_LIST cnList[] = { - {cnPNPN, cn_bits_PNPN, 0, sizeof(cnPNPN)/sizeof(cnPNPN[0])}, /* 0 */ - {cnNPNP, cn_bits_NPNP, 0, sizeof(cnNPNP)/sizeof(cnNPNP[0])}, /* 1 */ - {cnNPN, cn_bits_NPN, 0, sizeof(cnNPN)/sizeof(cnNPN[0])}, /* 2 */ - {cnPNP, cn_bits_PNP, 0, sizeof(cnPNP)/sizeof(cnPNP[0])}, /* 3 */ - {cnMNP, cn_bits_MNP, 0, sizeof(cnMNP)/sizeof(cnMNP[0])}, /* 4 */ - {cnPNM, cn_bits_PNM, 0, sizeof(cnPNM)/sizeof(cnPNM[0])}, /* 5 */ - {cnEN, cn_bits_EN , 0, sizeof(cnEN)/sizeof(cnEN[0])}, /* 6 */ - {cnNMN, cn_bits_NMN, 0, sizeof(cnNMN)/sizeof(cnNMN[0])}, /* 7 */ - {cnNE, cn_bits_NE , 0, sizeof(cnNE)/sizeof(cnNE[0])}, /* 8 */ - {cnNEN, cn_bits_NEN, 0, sizeof(cnNEN)/sizeof(cnNEN[0])}, /* 9 */ - {cnNP, cn_bits_NP, 0, sizeof(cnNP)/sizeof(cnNP[0])}, /* 10 */ - {cnPN, cn_bits_PN, 0, sizeof(cnPN)/sizeof(cnPN[0])}, /* 11 */ - {cnNM, cn_bits_NM, 0, sizeof(cnNM)/sizeof(cnNM[0])}, /* 12 */ - {cnMN, cn_bits_MN, 0, sizeof(cnMN)/sizeof(cnMN[0])}, /* 13 */ - {cnP_, cn_bits_P_, 0, sizeof(cnP_)/sizeof(cnP_[0])}, /* 14 */ - {cnM_, cn_bits_M_, -1, sizeof(cnM_)/sizeof(cnM_[0])}, /* 15 */ - {cnN_, cn_bits_N_, 0, sizeof(cnN_)/sizeof(cnN_[0])}, /* 16 */ - {cnMe, cn_bits_Me, 0, sizeof(cnMe)/sizeof(cnMe[0])} /* 17 */ -}; - -#define cnListIndexMe (17) /* index of {cnMe, cn_bits_Me,... } element of cnList[] */ - -int cnListNumEl = (int)(sizeof(cnList)/sizeof(cnList[0])); -/**********************************************************************/ -void clear_t_group_info( T_GROUP_INFO *ti ) -{ - if ( !ti ) { - return; - } else { - T_GROUP *t_group = ti->t_group; - int max_num_t_groups = ti->max_num_t_groups; - AT_NUMB *tGroupNumber = ti->tGroupNumber; - int num_t_groups = ti->num_t_groups; - AT_NUMB *nEndpointAtomNumber = ti->nEndpointAtomNumber; - int nNumEndpoints = ti->nNumEndpoints; - AT_NUMB *nIsotopicEndpointAtomNumber = ti->nIsotopicEndpointAtomNumber; - int nNumIsotopicEndpoints = ti->nNumIsotopicEndpoints; - memset( ti, 0, sizeof(*ti) ); - if ( t_group ) { - memset( t_group, 0, sizeof(t_group[0])*max_num_t_groups ); - } else { - max_num_t_groups = 0; - } - if ( tGroupNumber ) { - memset( tGroupNumber, 0, sizeof(tGroupNumber[0])*num_t_groups ); - } else { - num_t_groups = 0; - } - if ( nEndpointAtomNumber ) { - memset( nEndpointAtomNumber, 0, sizeof(nEndpointAtomNumber[0])*nNumEndpoints ); - } else { - nNumEndpoints = 0; - } - if ( nIsotopicEndpointAtomNumber ) { - memset( nIsotopicEndpointAtomNumber, 0, sizeof(nIsotopicEndpointAtomNumber[0])*nNumIsotopicEndpoints ); - } else { - nNumIsotopicEndpoints = 0; - } - ti->t_group = t_group; - ti->max_num_t_groups = max_num_t_groups; - ti->tGroupNumber = tGroupNumber; - ti->num_t_groups = num_t_groups; - ti->nEndpointAtomNumber = nEndpointAtomNumber; - ti->nNumEndpoints = nNumEndpoints; - ti->nIsotopicEndpointAtomNumber = nIsotopicEndpointAtomNumber; - ti->nNumIsotopicEndpoints = nNumIsotopicEndpoints; - } - return; -} -/******************************************************************************************************/ -int GetTgroupInfoFromInChI( T_GROUP_INFO *ti, inp_ATOM *at, AT_NUMB *endpoint, INChI *pInChI ) -{ - int ret, i, j, k, itg, num_atoms, len_tg, bIso, num_t_groups; - AT_NUMB *tGroupNumber = NULL; - AT_NUMB *tSymmRank = NULL; - AT_NUMB *tiSymmRank = NULL; - AT_NUMB *tiGroupNumber = NULL; - - ret = 0; - - clear_t_group_info( ti ); - if ( pInChI && pInChI->lenTautomer > 1 && pInChI->nTautomer && pInChI->nTautomer[0] > 0 ) { - num_atoms = pInChI->nNumberOfAtoms; - bIso = pInChI->IsotopicAtom && pInChI->nNumberOfIsotopicAtoms; - num_t_groups = pInChI->nTautomer[0]; - len_tg = pInChI->lenTautomer - T_GROUP_HDR_LEN*pInChI->nTautomer[0] - 1; /* number of endpoints */ - - /* allocation ti->t_group */ - if ( ti->max_num_t_groups != num_atoms/2+1 || !ti->t_group ) { - ti->max_num_t_groups = num_atoms/2+1; - if ( ti->t_group ) - inchi_free( ti->t_group ); - ti->t_group = (T_GROUP *)inchi_calloc( ti->max_num_t_groups, sizeof(ti->t_group[0])); - } - /* allocation ti->tGroupNumber */ - if ( ti->num_t_groups != num_t_groups || !ti->tGroupNumber ) { - ti->num_t_groups = num_t_groups; - if ( ti->tGroupNumber ) - inchi_free( ti->tGroupNumber ); - ti->tGroupNumber = (AT_NUMB *)inchi_calloc((ti->num_t_groups+1)*TGSO_TOTAL_LEN, sizeof(ti->tGroupNumber[0])); - } - /* allocation ti->tGroupNumber */ - if ( len_tg != ti->nNumEndpoints || !ti->nEndpointAtomNumber ) { - ti->nNumEndpoints = len_tg; - if ( ti->nEndpointAtomNumber ) - inchi_free( ti->nEndpointAtomNumber ); - ti->nEndpointAtomNumber = (AT_NUMB *)inchi_calloc(len_tg+1, sizeof(ti->nEndpointAtomNumber[0])); - } - - - /* check */ - if ( !ti->t_group || !ti->tGroupNumber || !ti->nEndpointAtomNumber ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - tGroupNumber = ti->tGroupNumber; - tSymmRank = tGroupNumber + TGSO_SYMM_RANK * ti->num_t_groups; /* equivalence; cannot restore */ - tiSymmRank = tGroupNumber + TGSO_SYMM_IRANK * ti->num_t_groups; - tiGroupNumber = tGroupNumber + TGSO_SYMM_IORDER * ti->num_t_groups; - - - INCHI_HEAPCHK - j = 1; /* index in pInChI->nTautomer[] */ - i = 0; /* index in ti->nEndpointAtomNumber[] */ - for ( itg = 0; itg < pInChI->nTautomer[0]; itg ++ ) { - len_tg = pInChI->nTautomer[j]; /* t-group length not including pInChI->nTautomer[j] */ - ti->t_group[itg].num[0] = pInChI->nTautomer[j+1]+pInChI->nTautomer[j+2]; /* num mobile H & (-) */ - ti->t_group[itg].num[1] = pInChI->nTautomer[j+2]; /* num mobile (-) */ - tGroupNumber[itg] = tiGroupNumber[itg] = itg; /* index */ - ti->t_group[itg].nGroupNumber = /*tSymmRank[itg] = tiSymmRank[itg] =*/ itg+1; /* t-group number */ - j += T_GROUP_HDR_LEN; /* skip t-group header */ - len_tg -= T_GROUP_HDR_LEN-1; - - ti->t_group[itg].nNumEndpoints = len_tg; - ti->t_group[itg].nFirstEndpointAtNoPos = i; - - for( ; 0 < len_tg --; j ++, i ++ ) { - k = ti->nEndpointAtomNumber[i] = pInChI->nTautomer[j]-1; - if ( at ) { - at[k].endpoint = itg+1; - } - if ( endpoint ) { - endpoint[k] = itg+1; - } - } - } - if ( i != ti->nNumEndpoints ) { - ret = RI_ERR_PROGR; - } - INCHI_HEAPCHK - } -exit_function: - return ret; -} -/******************************************************************************************************/ -int FillOutpStructEndpointFromInChI( INChI *pInChI, AT_NUMB **pEndpoint ) -{ - int num_at = pInChI->nNumberOfAtoms; - AT_NUMB *endpoint = *pEndpoint; - int itg, i, j, k, len_tg; - - if ( !endpoint && !(endpoint = (AT_NUMB*) inchi_malloc(num_at * sizeof(endpoint[0]) ) ) ) { - return RI_ERR_ALLOC; - } - memset( endpoint, 0, num_at * sizeof(endpoint[0]) ); - if ( pInChI->lenTautomer <= 1 || !pInChI->nTautomer ) { - goto exit_function; - } - j = 1; /* index in pInChI->nTautomer[] */ - i = 0; /* index in ti->nEndpointAtomNumber[] */ - for ( itg = 0; itg < pInChI->nTautomer[0]; itg ++ ) { - len_tg = pInChI->nTautomer[j]; /* t-group length not including pInChI->nTautomer[j] */ - j += T_GROUP_HDR_LEN; /* skip t-group header */ - len_tg -= T_GROUP_HDR_LEN-1; - /* ti->t_group[itg].nNumEndpoints = len_tg; */ - for( ; 0 < len_tg --; j ++, i ++ ) { - k = pInChI->nTautomer[j]-1; - endpoint[k] = itg+1; - } - } -exit_function: - *pEndpoint = endpoint; - return 0; -} - - -/************************************************************************************/ -int cmp_charge_val( const void *a1, const void *a2 ) -{ - const CHARGE_VAL *p1 = (const CHARGE_VAL *) a1; - const CHARGE_VAL *p2 = (const CHARGE_VAL *) a2; - int diff; - - if ( diff = (int)p1->nValence - (int)p2->nValence ) /* smaller valence first */ - return diff; - if ( diff = abs((int)p1->nCharge) - abs((int)p2->nCharge )) /* smaller abs charge first */ - return diff; - if ( diff = (int)p2->nCharge - (int)p1->nCharge ) /* (+) first, (-) second */ - return diff; - return (int)p1->nValenceOrderingNumber - (int)p2->nValenceOrderingNumber; -} -/************************************************************************************/ -int bMayBeACationInMobileHLayer( inp_ATOM *at, VAL_AT *pVA, int iat, int bMobileH ) -{ - static const char szEl[] = "N;P;O;S;Se;Te;"; - static const char cVal[] = {4,4,3,3, 3, 3, 0}; - static char en[8]; - static int ne; - int i, j, neigh; - char *p; - if ( !bMobileH || !at[iat].num_H ) { - return 1; - } - if ( !ne ) { /* one time initialization */ - const char *b, *e; - int len; - char elname[ATOM_EL_LEN]; - for ( b = szEl; e = strchr( b, ';'); b = e+1 ) { - len = e-b; - memcpy( elname, b, len ); - elname[len] = '\0'; - en[ne++] = get_periodic_table_number( elname ); - } - en[ne] = '\0'; - } - if ( p = (char *)memchr( en, at[iat].el_number, ne ) ) { - i = p - en; - /* >B(-)< exception */ - if ( at[iat].valence + at[iat].num_H <= cVal[i] ) { - for ( j = 0; j < at[iat].valence; j ++ ) { - neigh = at[iat].neighbor[j]; - if ( at[neigh].valence == 4 && at[neigh].chem_bonds_valence == 4 && !at[neigh].num_H && - pVA[neigh].cNumValenceElectrons == 3 && pVA[neigh].cPeriodicRowNumber == 1 ) { - return 1; - } - } - return 0; - } - } - return 1; -} -/************************************************************************************/ -int clean_charge_val( CHARGE_VAL *pChargeVal, int len, inp_ATOM *atom, VAL_AT *pVA, - int iat, int bIsMetal, int bMobileH, AT_NUMB *endpoint ) -{ - inp_ATOM *at = atom + iat; - int nPeriodicNum = at->el_number; - int num_bonds = at->valence; - int min_valence = at->valence + at->num_H; - /* in fixed-H case treat tautomeric -O as tautomeric to avoid #O(+) */ - int bTautomeric = (at->endpoint != 0); - int bFixedHTautomeric = !bMobileH && (endpoint && endpoint[iat] && - pVA[iat].cNumValenceElectrons == 6 && 1==num_bonds && - !at->num_H && !bIsMetal); - /* int bIsMetal = is_el_a_metal( nPeriodicNum );*/ - int bDoNotAddH = do_not_add_H( nPeriodicNum ); - int nPeriod, nNumEqAbsCharges; - int nNumValenceEl = get_sp_element_type( nPeriodicNum, &nPeriod ) - 1; - - int i, j; - - if ( !len ) - return len; - - insertions_sort( pChargeVal, len, sizeof(pChargeVal[0]), cmp_charge_val ); - /* metals -- very preliminary code */ - if ( bIsMetal && bDoNotAddH ) { - /* keep the 1st found */ - return inchi_min( 1, len ); - } - /* Mobile-H layer cannot have H on positively charged N, P (all IV), O, S, Se, Te (all III) */ - - /* - if ( abs( pChargeVal[0].nCharge ) > 1 && pChargeVal[0].nValence >= min_valence ) { - return inchi_min( 1, len ); - } - */ - nNumEqAbsCharges = 0; - for ( i = j = 0; i < len && j < (nNumEqAbsCharges? 3+nNumEqAbsCharges:4); i ++ ) { - /* for now accept only charge = 0, -1, +1 */ - if ( abs( pChargeVal[i].nCharge ) > 1 ) { - continue; - } - if ( BOND_TYPE_TRIPLE + BOND_TYPE_DOUBLE * (min_valence - 1) < pChargeVal[i].nValence ) { - continue; /* not more than one triple and the rest - double bonds per atom */ - } - if ( (bTautomeric || j && bFixedHTautomeric) && pChargeVal[i].nCharge < 0 ) { - continue; /* negative charge must be included in the tautomeric group */ - } - if ( (bTautomeric || bFixedHTautomeric) && pChargeVal[i].nCharge > 0 ) { - continue; /* positive charge for now cannot reach a tautomeric group */ - } - if ( j && !bMayBeACationInMobileHLayer( atom, pVA, iat, bMobileH ) && pChargeVal[i].nCharge > 0 ) { - if ( i+1 < len && - pChargeVal[i].nValence == pChargeVal[i+1].nValence && - pChargeVal[i].nCharge == -pChargeVal[i+1].nCharge ) { - /* (-) if exists is always after (+) */ - i += 1; /* also skip the next element */ - } - continue; /* in case of Mobile-H, a hydrogen cannot be on a (+)-charged heteroatom */ - } - /* accept same valence opposite charges only for C and its group in Periodic Table */ - if ( j && !bTautomeric && - pChargeVal[i].nValence == pChargeVal[j-1].nValence && - pChargeVal[i].nCharge == -pChargeVal[j-1].nCharge ) { - if ( nNumValenceEl == VALUE_OCTET/2 && pChargeVal[i].nCharge && !nNumEqAbsCharges ) { - pChargeVal[j ++] = pChargeVal[i]; - nNumEqAbsCharges ++; - } - continue; - } - /* do not accept valence=5 for neutral NHn in case of not Mobile-H 2005-01-26 ???? */ - if ( nNumValenceEl == 5 && nPeriod == 1 && at->num_H && - j && !bMobileH && - pChargeVal[i].nValence == 5 && !pChargeVal[i].nCharge ) { - continue; - } - /* do not accept gaps in allowed valences */ - if ( j && pChargeVal[i].nValence > pChargeVal[j-1].nValence+1 ) { - break; - } - pChargeVal[j ++] = pChargeVal[i]; - } - len = j; - if ( !nNumEqAbsCharges && num_bonds < 3 && len == 4 ) { - len --; /* prohibit =S# where # is a triple bond */ - } - return len; - - -} -/************************************************************************************ -int GetAtomRestoreInfo( inp_ATOM *atom, int iat, VAL_AT *pVArray ) - - pVA->cDoNotAddH - pVA->cMetal - pVA->cNumValenceElectrons - pVA->cPeriodicRowNumber - pVA->cInitFreeValences - pVA->cnListIndex = index+1 - -return value: - -1 => error - 0 => do not know what to do; leave the atom unchanged - 1 => success -*************************************************************************************/ -int GetAtomRestoreInfo( inp_ATOM *atom, int iat, VAL_AT *pVArray, ICHICONST SRM *pSrm, int bMobileH, AT_NUMB *endpoint ) -{ -/* #defines from util.c */ -#define MIN_ATOM_CHARGE (-2) -#define MAX_ATOM_CHARGE 2 -#define NEUTRAL_STATE (-MIN_ATOM_CHARGE) -#define NUM_ATOM_CHARGES (MAX_ATOM_CHARGE - MIN_ATOM_CHARGE + 1) -#define MAX_NUM_VALENCES 5 /* max. number + 1 to provide zero termination */ - - int i, j, j2, k, k2, charge, cur_charge, num_non_bonding_electrons; - int nNumStates, nNumSelectedStates, num_H, num_bonds; - int nOctetNeutralValenceExcess, nFirstNeutralValenceExcess; - int nFoundNeutralValenceExcess, nFoundNeutralValenceOrdNumber; - int nLastFoundValenceOrdNumber, nLastFoundValenceState; - int cn_bits, cn_bits_array[5], len_cn_bits_array; - inp_ATOM *at = atom+iat; - VAL_AT *pVA = pVArray + iat; - int nPeriodicNum = at->el_number; - int cur_chem_valence, cur_chem_valence_fixed, min_chem_valence, known_chem_valence; - int metal_bonds_chem_valence, not_metal_bonds_chem_valence, alt_bonds_delta_valence, bonds_chem_valence, bond_type; - CHARGE_VAL ChargeVal[NUM_ATOM_CHARGES*MAX_NUM_VALENCES]; - - memset( ChargeVal, 0, sizeof(ChargeVal) ); - - pVA->cDoNotAddH = do_not_add_H( nPeriodicNum ); /* InChI never adds H to this atom */ - /*pVA->cMetal = is_el_a_metal( nPeriodicNum );*/ /* the atom is a metal */ - - /* count bonds to metal atoms; metals have already been marked */ - metal_bonds_chem_valence = not_metal_bonds_chem_valence = alt_bonds_delta_valence = 0; - if ( pVA->cMetal ) { - j = at->valence; /* all bonds to metal */ - for ( i = k = j2 = k2 = 0; i < at->valence; i ++ ) { - bond_type = (at->bond_type[i] & BOND_TYPE_MASK); - if ( bond_type <= BOND_TYPE_TRIPLE ) { - metal_bonds_chem_valence += inchi_max(BOND_TYPE_SINGLE, bond_type); - } else { - metal_bonds_chem_valence += BOND_TYPE_SINGLE; - k ++; /* count alternating bonds */ - } - } - } else { - for ( i = j = j2 = k = k2 = 0; i < at->valence; i ++ ) { - bond_type = (at->bond_type[i] & BOND_TYPE_MASK); - if ( pVArray[ (int)at->neighbor[i] ].cMetal ) { - j ++; /* number of bonds to metal atoms */ - if ( bond_type <= BOND_TYPE_TRIPLE ) { - metal_bonds_chem_valence += inchi_max(BOND_TYPE_SINGLE, bond_type); - } else { - metal_bonds_chem_valence += BOND_TYPE_SINGLE; - k ++; /* count alternating bonds */ - } - } else { - j2 ++; - if ( bond_type <= BOND_TYPE_TRIPLE ) { - not_metal_bonds_chem_valence += inchi_max(BOND_TYPE_SINGLE, bond_type); - } else { - not_metal_bonds_chem_valence += BOND_TYPE_SINGLE; - k2 ++; /* count alternating bonds */ - } - } - } - } - bonds_chem_valence = metal_bonds_chem_valence + not_metal_bonds_chem_valence; - if ( at->chem_bonds_valence > bonds_chem_valence ) { - if ( at->chem_bonds_valence - bonds_chem_valence > 1 ) { - at->chem_bonds_valence = bonds_chem_valence + 1; /* should not happen */ - } - alt_bonds_delta_valence = at->chem_bonds_valence - bonds_chem_valence; - } - - pVA->cNumBondsToMetal = j; - - if ( nPeriodicNum == EL_NUMBER_H ) { - /* ignore bridging H; ??? later add ??? */ - return 0; - } - - num_H = at->num_H; - num_bonds = at->valence; - - if ( !num_bonds && !num_H ) { - return 0; /* do not know the answer: isolated atom */ - } - /* at the beginning all bonds are single */ - min_chem_valence = num_bonds + num_H; - cur_chem_valence = bonds_chem_valence + alt_bonds_delta_valence + num_H; /* includes double & alternating bond contribution */ - - /* number of non-bonding electrons in case of all single bonds */ - num_non_bonding_electrons = (int)pVA->cNumValenceElectrons - min_chem_valence; - /* Octet rule: charge = bonds_valence + NumValenceElectrons - 8 */ - charge = min_chem_valence + (int)pVA->cNumValenceElectrons - VALUE_OCTET; /* wrong */ - - /* typical (ad hoc) minimal neutral valence */ - known_chem_valence = ( pVA->cNumValenceElectrons > VALUE_OCTET/2 )? - VALUE_OCTET - pVA->cNumValenceElectrons : - pVA->cNumValenceElectrons; - /* excess of typical valence over all-single-bonds valence */ - nOctetNeutralValenceExcess = known_chem_valence - min_chem_valence; - /* (NB=num.bonds, NV=neutral valence, NVX=neutral valence excess, LFVS=last found valence state, val.=valence) - - element NB knownFst octet Last octetNVX firstNVX foundNVX chargeLFVS LFVS - valence val. NV>= - - -B 1 3 3 3 2 2 = 2 +2 - >B 2 3 3 3 1 1 = 1 +1 - >B- 3 3 3 3 0 0 = 0 0 - >B< 4 3 3 3 -1 -1 <> N/A -1 - - -C 1 4 4 4 3 3 = 3 N/A - >C 2 4 4 4 2 2 = 2 +2 (-2) - >C- 3 4 4 4 1 1 = 1 +1 (-1) - >C< 4 4 4 4 0 0 = 0 0 - C(V) 5 4 4 N/A -1 -1 <> N/A N/A - - -Si 1 4 4 4 3 3 = 3 N/A - >Si 2 4 4 4 2 2 = 2 +2 (-2) - >Si- 3 4 4 4 1 1 = 1 +1 (-1) - >Si- 4 4 4 4 0 0 = 0 0 - Si(V) 5 4 4 N/A -1 -1 <> N/A -1 - - -N 1 3 3 3 2 2 = 2 -2 - >N 2 3 3 3 1 1 = 1 -1 - >N- 3 3 3 3 0 0 = 0 0 (+2) - >N< 4 3 3 5 -1 -1 <> 1 +1 - N(V) 5 3 3 5 -2 -2 <> 0 0 - N(VI) 6 3 3 N/A -3 -3 <> N/A N/A - N(VII) 7 3 3 N/A -4 -4 <> N/A N/A - - -P 1 3 3 3 2 2 = 2 -2 - >P 2 3 3 3 1 1 = 1 -1 - >P- 3 3 3 3 0 0 = 0 0 (-2, +2) - >P< 4 3 3 5 -1 -1 <> 1 +1 (-1) - P(V) 5 3 3 5 -2 -2 <> 0 0 (-2) - P(VI) 6 3 3 N/A -3 -3 <> N/A -1 - P(VII) 7 3 3 N/A -4 -4 <> N/A -2 - P(VIII) 8 3 3 N/A -5 -5 <> N/A N/A - - -O 1 2 2 2 1 1 = 1 -1 - >O 2 2 2 2 0 0 = 0 0 - >O- 3 2 2 N/A -1 -1 <> N/A +1 - >O< 4 2 2 N/A -2 -2 <> N/A +2 - O(V) 5 2 2 N/A -3 -3 <> N/A +1 - O(VI) 6 2 2 N/A -4 -4 <> N/A N/A - - -S 1 2 2 2 1 1 = 1 -1 - >S 2 2 2 2 0 0 = 0 0 NPNP - prohibit - >S- 3 2 2 4 -1 -1 <> 1 +1 (-1) PNPN - >S< 4 2 2 4 -2 -2 <> 0 0 (+2) - S(V) 5 2 2 6 -3 -3 <> 1 +1 (-1) - S(VI) 6 2 2 6 -4 -4 <> 0 0 - S(VII) 7 2 2 N/A -5 -5 <> 0 -1 - S(VIII) 8 2 2 N/A -6 -6 <> N/A N/A - - -F 1 1 1 1 0 0 = 0 0 - >F 2 1 1 1 -1 -1 <> N/A +1 - >F- 3 1 1 1 -2 -2 <> N/A +2 - >F< 4 1 1 1 -3 -3 <> N/A N/A - F(V) 5 1 1 1 -4 -4 <> N/A +2 - F(VI) 6 1 1 1 -5 -5 <> N/A N/A - - -Cl 1 1 1 1 0 0 = 0 0 NPNP - prohibit - >Cl 2 1 1 3 -1 -1 <> 1 +1 PNPN - prohibit - >Cl- 3 1 1 3 -2 -2 <> 0 0 (+2) NPNP - >Cl< 4 1 1 5 -3 -3 <> 1 +1 PNPN - Cl(V) 5 1 1 5 -4 -4 <> 0 0 - Cl(VI) 6 1 1 7 -5 -5 <> 1 +1 - Cl(VII) 7 1 1 7 -6 -6 <> 0 0 - Cl(VIII) 8 1 1 N/A -7 -7 <> N/A N/A - - - NB = num_bonds+num_H - - knownFst valence = nFirstNeutralValenceExcess + min_chem_valence - octet val. = nOctetNeutralValenceExcess + min_chem_valence - Last NV>= = nFoundNeutralValenceExcess + min_chem_valence - - octetNVX = nOctetNeutralValenceExcess - firstNVX = nFirstNeutralValenceExcess - foundNVX = nFoundNeutralValenceExcess - - chargeLFVS = ChargeVal[nLastFoundValenceState].nCharge - - */ - /* minimal known neutral atom valence; different for Sn(2/4), Tl(1/3), Pb(2/4): (known/typical ad hoc) */ - known_chem_valence = get_el_valence( nPeriodicNum, 0, 0 ); - - if ( pSrm->bMetalAddFlower ) { - /* bond orders of bonds to metal may be as they are (pSrm->nMetalInitBondOrder==1) - or decreased by one (pSrm->nMetalInitBondOrder==0) - nMetalInitBondOrder == nMetalMinBondOrder + nMetalInitEdgeFlow - */ - cur_chem_valence_fixed = cur_chem_valence - pVA->cNumBondsToMetal * (1-pSrm->nMetalInitBondOrder); - pVA->cInitOrigValenceToMetal = metal_bonds_chem_valence; - pVA->cInitValenceToMetal = metal_bonds_chem_valence - pVA->cNumBondsToMetal * (1-pSrm->nMetalInitBondOrder); - pVA->cInitFlowToMetal = pVA->cInitValenceToMetal - pVA->cNumBondsToMetal * pSrm->nMetalMinBondOrder; - if ( pVA->cMetal ) { - pVA->cInitFreeValences += alt_bonds_delta_valence; - } - if ( pSrm->nMetalInitEdgeFlow < pSrm->nMetalInitBondOrder - pSrm->nMetalMinBondOrder ) { - /* single bond has zero initial flow + 2 radicals at incident atoms */ - if ( pVA->cInitFlowToMetal <= pVA->cNumBondsToMetal ) { - if ( pVA->cMetal ) { - pVA->cInitFreeValences += pVA->cInitFlowToMetal; - } - pVA->cInitFlowToMetal = 0; - } else { - if ( pVA->cMetal ) { - pVA->cInitFreeValences += pVA->cNumBondsToMetal * (1 - pSrm->nMetalInitEdgeFlow); - } - pVA->cInitFlowToMetal -= pVA->cNumBondsToMetal * (1 - pSrm->nMetalInitEdgeFlow); - } - } - - } else { - /* treat metal atoms as ordinary non-metal atoms */ - cur_chem_valence_fixed = cur_chem_valence; - pVA->cInitFlowToMetal = metal_bonds_chem_valence - pVA->cNumBondsToMetal; - pVA->cInitValenceToMetal = metal_bonds_chem_valence; - pVA->cInitOrigValenceToMetal = metal_bonds_chem_valence; - } - - - if ( pVA->cMetal && pSrm->bMetalAddFlower ) { - pVA->cnListIndex = cnListIndexMe + 1; - /* - pVA->cInitOrigValenceToMetal += alt_bonds_delta_valence; - pVA->cInitValenceToMetal += alt_bonds_delta_valence; - pVA->cInitFreeValences = (pSrm->nMetalInitBondOrder + alt_bonds_delta_valence - - (pSrm->nMetalMinBondOrder + pSrm->nMetalInitEdgeFlow)) * pVA->cNumBondsToMetal; - */ - return 0; /* metal */ - } - - if ( !known_chem_valence ) { - /* a noble gas like He, Ne, ... */ - pVA->cInitFreeValences = at->chem_bonds_valence - at->valence; - return TREAT_ATOM_AS_METAL; /* do not know anything about this atom; needs 2nd pass */ - } - - nFirstNeutralValenceExcess = known_chem_valence - min_chem_valence; - - nFoundNeutralValenceExcess = NO_VALUE_INT; - nFoundNeutralValenceOrdNumber = NO_VALUE_INT; - nLastFoundValenceOrdNumber = NO_VALUE_INT; - nLastFoundValenceState = NO_VALUE_INT; - - /* find the lowest known valence >= all-single-bonds valence */ - for ( cur_charge = MIN_ATOM_CHARGE, nNumStates = 0; cur_charge <= MAX_ATOM_CHARGE; cur_charge ++ ) { - for ( i = 0; i < MAX_NUM_VALENCES; i ++ ) { - known_chem_valence = get_el_valence( nPeriodicNum, cur_charge, i ); - if ( cur_chem_valence_fixed > known_chem_valence || !known_chem_valence ) { - continue; /* known valence < all-single-bonds valence */ - } - if ( BOND_TYPE_TRIPLE + BOND_TYPE_DOUBLE * (num_bonds - 1) + num_H < known_chem_valence ) { - continue; /* not more than one triple and the rest - double bonds per atom */ - } - /* keep all found */ - ChargeVal[nNumStates].nValence = known_chem_valence; - ChargeVal[nNumStates].nCharge = cur_charge; - ChargeVal[nNumStates].nValenceOrderingNumber = i; - if ( !cur_charge && nFoundNeutralValenceExcess == NO_VALUE_INT ) { - /* neutral state; compare to the lowest typical valence */ - nFoundNeutralValenceExcess = known_chem_valence - min_chem_valence; - nFoundNeutralValenceOrdNumber = i; - } - if ( min_chem_valence == known_chem_valence ) { - if ( nLastFoundValenceState == NO_VALUE_INT ) { - /* accept the first found */ - nLastFoundValenceState = nNumStates; - } else - if ( abs( ChargeVal[nLastFoundValenceState].nCharge ) >= abs( cur_charge ) ) { - /* accept smaller abs(charge); if abs(charges) are same, accept (+) */ - nLastFoundValenceState = nNumStates; - } - } - nNumStates ++; - } - } - /***********************************************************************************/ - /* select only appropriate charge & valence so that a suitable ChargeStruct exists */ - /***********************************************************************************/ - - nNumSelectedStates = clean_charge_val( ChargeVal, nNumStates, atom, pVArray, iat, pVA->cMetal, bMobileH, endpoint ); - - if ( !nNumSelectedStates ) { - return TREAT_ATOM_AS_METAL; /* nothing to do */ - } - /***********************************************************************************/ - /* Find an appropriate ChargeStruct index for the ChargeVal found */ - /***********************************************************************************/ - cn_bits = 0; - memset( cn_bits_array, 0, sizeof(cn_bits_array) ); - /***** set bits identifying a suitable ChargeStruct ******/ - for ( i = len_cn_bits_array = 0; i < nNumSelectedStates && len_cn_bits_array < 4; i ++ ) { - switch( ChargeVal[i].nCharge ) { - case -1: - cn_bits_array[len_cn_bits_array] |= cn_bits_M; /* Minus 1 */ - break; - case 0: - cn_bits_array[len_cn_bits_array] |= cn_bits_N; /* Neutral */ - break; - case 1: - cn_bits_array[len_cn_bits_array] |= cn_bits_P; /* Plus 1 */ - break; - default: - return RI_ERR_PROGR; /* program error */ - } - if ( i+1 < nNumSelectedStates && - ChargeVal[i].nValence == ChargeVal[i+1].nValence && - ChargeVal[i].nCharge && - ChargeVal[i].nCharge == -ChargeVal[i+1].nCharge ) { - ; /* add opposite charge to the same element of cn_bits_array[] */ - } else { - len_cn_bits_array ++; - } - } - if ( !len_cn_bits_array || len_cn_bits_array > 4 ) { - return RI_ERR_PROGR; /* program error */ - } - /* accommodate added 4-state ChargeStruct: +/- cannot be in case of 4 states */ - if ( len_cn_bits_array + 1 == nNumSelectedStates && nNumSelectedStates == 4 ) { - len_cn_bits_array --; - nNumSelectedStates --; - cn_bits_array[len_cn_bits_array] = 0; - } - /* fix for terminal hydrogenless -C as in isocyano or CO: there is no just cnE_[] ChargeStruct */ - - if ( len_cn_bits_array == 1 && - cn_bits_array[0] == (cn_bits_P | cn_bits_M) && - ChargeVal[0].nValence + 1 > BOND_TYPE_TRIPLE + BOND_TYPE_DOUBLE * (num_bonds - 1) + num_H ) { - cn_bits_array[len_cn_bits_array ++] = cn_bits_N; - ChargeVal[nNumSelectedStates].nValence = ChargeVal[nNumSelectedStates-1].nValence; - ChargeVal[nNumSelectedStates].nCharge = 0; - ChargeVal[nNumSelectedStates].nValenceOrderingNumber = 0; - } - -make_cn_bits: - cn_bits = MAKE_CN_BITS(cn_bits_array[0], cn_bits_array[1], cn_bits_array[2], cn_bits_array[3]); - /*********** find ChargeStructure **************/ - for ( i = 0, j = -1; i < cnListNumEl; i ++ ) { - if ( cnList[i].bits == cn_bits ) { - j = i; - break; /* found */ - } - } - if ( j < 0 ) { - /* ChargeStructure was not found */ - if ( 1 < len_cn_bits_array && len_cn_bits_array + 1 == nNumSelectedStates ) { - /* a pair of opposite charges was combined */ - len_cn_bits_array --; - cn_bits_array[len_cn_bits_array] = 0; - goto make_cn_bits; - } else - if ( nNumSelectedStates == 4 ) { - /* reduce number of states */ - len_cn_bits_array --; - cn_bits_array[len_cn_bits_array] = 0; - nNumSelectedStates --; - goto make_cn_bits; - } - return RI_ERR_PROGR; /* charge structure not found */ - } - /********** ChargeStructure has been found **********/ - pVA->cnListIndex = j+1; /* charge structure index + 1 */ - pVA->cInitCharge = cnList[j].nInitialCharge; - /********** Calculate "Free Valence" ****************/ -#if ( ALLOW_METAL_BOND_ZERO == 1 ) - -#if ( INIT_METAL_BOND_ZERO == 1 ) - if ( pVA->cMetal ) { - j = 0; - } else { - j = ChargeVal[0].nValence - cur_chem_valence_fixed; - } -#else - j = ChargeVal[0].nValence - cur_chem_valence_fixed; -#endif - -#else - j = ChargeVal[0].nValence - cur_chem_valence_fixed; -#endif - if ( j < 0 ) { - return RI_ERR_PROGR; /* program error */ - } - pVA->cInitFreeValences = j; /* number of initial unsatisfied valences; should be combined with */ - /* (cap - flow) of vertex=0 in the charge structure[pVA->cnListIndex-1] */ - return 1; /* success */ - -#undef MIN_ATOM_CHARGE -#undef MAX_ATOM_CHARGE -#undef NEUTRAL_STATE -#undef NUM_ATOM_CHARGES -#undef MAX_NUM_VALENCES - -} - - -#ifdef NEVER -/******************************************************************************************************/ -int get_bonds_valences( int nPeriodicNum, int bonds_valence, int num_H, VAL_AT *pVA ) -{ - int i, j, charge, chem_valence, known_chem_valence; -#define MAX_NUM_VALENCES 5 /* defined in util.c */ - - memset( pVA, 0, sizeof( pVA[0] ) ); - - if ( !bonds_valence && !num_H ) - return 0; /* do not know the answer */ - - chem_valence = bonds_valence + num_H; - for ( charge = VAL_MIN_CHARGE; charge <= VAL_MAX_CHARGE; charge ++ ) { - for ( i = 0, j = 0; i < MAX_NUM_VALENCES, j < VAL_NUMBER; i ++ ) { - if ( chem_valence <= (known_chem_valence = get_el_valence( nPeriodicNum, charge, i ) ) ) { - if ( !charge ) { - pVA->cValence[j][VAL_NEUTR_ORDER] = i+1; - } - pVA->cValence[j++][charge+VAL_BASE] = known_chem_valence - num_H; - } - } - } - pVA->cDoNotAddH = do_not_add_H( nPeriodicNum ); - pVA->cMetal = is_el_a_metal( nPeriodicNum ); - return pVA->cValence[0][VAL_BASE]; /* 0 means do not know the answer */ -#undef MAX_NUM_VALENCES -} -#endif -/*********** calculate s or p-element type ************/ -int get_sp_element_type( int nPeriodicNumber, int *nRow ) -/* - num el - el neg - 1 => H ATYPE_H 1 1 21 - 2 => Li, Na, K, Rb, Cs, Fr ATYPE_Na 2 1 10 09 08 08 07 - 3 => Be, Mg, Ca, Sr, Ba, Ra ATYPE_Mg 3 2 15 12 10 10 09 - 4 => B, Al, Ga, In, Tl ATYPE_B 4 3 20 15 18 17 18 - 5 => C, Si, Ge, Sn, Pb ATYPE_C 5 4 25 18 18 18 18 - 6 => N, P, As, Sb, Bi ATYPE_N 6 5 30 21 20 19 19 - 7 => O, S, Se, Te, Po ATYPE_O 7 6 35 25 24 21 20 - 8 => F, Cl, Br, I, At ATYPE_Cl 8 7 40 30 28 25 22 - -number of valence electrons = (type>1)? type-1: type - - */ -{ - int row = 0, type = 0; - if ( nPeriodicNumber == 1 ) { - type = 1; /* H: 1 */ - row = 0; - } else - if ( nPeriodicNumber == 2 ) { - type = 0; row = 0; - } else - if ( nPeriodicNumber <= 10 ) { - /* Li: 2, Be: 3, B: 4, C: 5, N: 6, O: 7, F: 8, Ne: 9; later subtract 1 */ - type = nPeriodicNumber-1; row = 1; - } else - if ( nPeriodicNumber <= 18 ) { - type = nPeriodicNumber - 9; row = 2; - } else - if ( nPeriodicNumber <= 20 ) { - type = nPeriodicNumber - 17; row = 3; - } else - if ( nPeriodicNumber <= 30 ) { - type = 0; row = 3; - } else - if ( nPeriodicNumber <= 36 ) { - type = nPeriodicNumber - 27; row = 3; - } else - if ( nPeriodicNumber <= 38 ) { - type = nPeriodicNumber - 35; row = 4; - } else - if ( nPeriodicNumber <= 48 ) { - type = 0; row = 4; - } else - if ( nPeriodicNumber <= 54 ) { - type = nPeriodicNumber - 45; row = 4; - } else - if ( nPeriodicNumber <= 56 ) { - type = nPeriodicNumber - 53; row = 5; - } else - if ( nPeriodicNumber <= 80 ) { - type = 0; row = 5; - } else - if ( nPeriodicNumber <= 86 ) { - type = nPeriodicNumber - 77; row = 5; - } else - if ( nPeriodicNumber <= 88 ) { - type = nPeriodicNumber - 85; row = 6; - } else { - type = 0; row = 6; - } - *nRow = row; - return type==9? 0 : type; -} -/******************************************************************************************************/ -int ReallocTCGroups( ALL_TC_GROUPS *pTCGroups, int nAdd ) -{ - TC_GROUP *pTCGroup = (TC_GROUP *) inchi_malloc( sizeof(pTCGroup[0])*(pTCGroups->max_tc_groups + nAdd) ); - if ( pTCGroup ) { - if ( pTCGroups->num_tc_groups ) { - memcpy( pTCGroup, pTCGroups->pTCG, sizeof(pTCGroup[0])*pTCGroups->num_tc_groups ); - } - memset( pTCGroup + pTCGroups->max_tc_groups, 0, sizeof(pTCGroup[0])*nAdd ); - if ( pTCGroups->pTCG ) { - inchi_free( pTCGroups->pTCG ); - } - pTCGroups->pTCG = pTCGroup; - pTCGroups->max_tc_groups += nAdd; - return 0; - } - return RI_ERR_ALLOC; -} -/******************************************************************************************************/ -int RegisterTCGroup( ALL_TC_GROUPS *pTCGroups, int nGroupType, int nGroupOrdNum, - int nVertexCap, int nVertexFlow, int nEdgeCap, int nEdgeFlow, int nNumEdges) -{ - int i, ret = 0; - /* search */ - for ( i = 0; i < pTCGroups->num_tc_groups; i ++ ) { - if ( pTCGroups->pTCG[i].type == nGroupType && - pTCGroups->pTCG[i].ord_num == nGroupOrdNum ) { - break; - } - } - if ( i == pTCGroups->num_tc_groups ) { - /* add one more group */ - if ( pTCGroups->num_tc_groups == pTCGroups->max_tc_groups ) { - ret = ReallocTCGroups( pTCGroups, INC_NUM_TCGROUPS ); - if ( ret ) { - goto exit_function; - } - } - ret = i+1; /* added new group */ - pTCGroups->num_tc_groups ++; - pTCGroups->pTCG[i].type = nGroupType; - pTCGroups->pTCG[i].ord_num = nGroupOrdNum; - } - pTCGroups->pTCG[i].num_edges += nNumEdges; - - pTCGroups->pTCG[i].st_cap += nVertexCap; - pTCGroups->pTCG[i].st_flow += nVertexFlow; - - pTCGroups->pTCG[i].edges_cap += nEdgeCap; - pTCGroups->pTCG[i].edges_flow += nEdgeFlow; - -exit_function: - return ret; -} -/******************************************************************************************************/ -int nTautEndpointEdgeCap( inp_ATOM *at, VAL_AT *pVA, int i ) -{ - /* There are 3 sources of cap-flow = number of unsatisfied valences: - ----------------------------------------------------------------- - 1. pVA[i].cInitFreeValences - 2. pCN[0].v.cap - pCN[0].v.flow - 3. st[i].chem_bonds_valence - SUM(SINGLE, DOUBLE, TRIPLE bond orders) - Reasons: (a) This sum will not include 'ALTERN' bonds - (b) until now at[i].chem_bonds_valence was used as a - number of satisfied valences. In case of adjacent - stereobonds marked as BOND_TYPE_ALTERN the value of - at[i].chem_bonds_valence may be = at[i].valence+1. - 4. Since tautomerism is defined for a neutral atom, do not add - initial flows from the atom to the ChargeStruct - CORRECTION: tautomeric endpoints do not have ChargeStruct. - - */ - int j, k, nEdgeCap, bonds_valence, stereo_bond_excess_valence; - MY_CONST C_NODE *pCN = pVA[i].cnListIndex>0? cnList[pVA[i].cnListIndex-1].pCN:NULL; - - /* 1: free valences to reach the minimum known atom valence */ - nEdgeCap = pVA[i].cInitFreeValences; - /* 2: atom free valence in the ChargeStruct */ - if ( pCN ) { - nEdgeCap += pCN[0].v.cap - pCN[0].v.flow; /* normally should not happen */ - } - /* 3: atom free valence due to known from stereochemistry stereogenic bond types */ - /* - for ( j = 0, bonds_valence = 0; j < at[i].valence; j ++ ) { - if ( at[i].bond_type[j] <= BOND_TYPE_TRIPLE ) { - bonds_valence += at[i].bond_type[j]; - } - } - */ - /* bonds > SINGLE are assumed fixed stereobonds; fixed bond cannot increase t-group edge flow */ - for ( stereo_bond_excess_valence=0, j = 0; j < MAX_NUM_STEREO_BONDS && at[i].sb_parity[j]; j ++ ) { - k = at[i].sb_ord[j]; - if ( at[i].bond_type[k] < BOND_TYPE_TRIPLE ) { - stereo_bond_excess_valence += at[i].bond_type[k] - BOND_TYPE_SINGLE; - } - } - /* - bonds_valence = (at[i].chem_bonds_valence - bonds_valence) + (bonds_valence -at[i].valence - stereo_bond_excess_valence); - */ - bonds_valence = (at[i].chem_bonds_valence - at[i].valence) - stereo_bond_excess_valence; - - - /*---- add 1, 2, 3 ----*/ - if ( bonds_valence >= 0 ) { - nEdgeCap += bonds_valence; - } else { - nEdgeCap = RI_ERR_PROGR; - } - return nEdgeCap; -} -/******************************************************************************************************/ -/* If Metal flowers are allowed ( pSrm->bMetalAddFlower != 0), then: */ -/* */ -/* bond to a metal atom min_bond_order[i] = pSrm->nMetalMinBondOrder */ -/* taut endpoint - metal min_bond_order[i] = pSrm->nMetal2EndpointMinBondOrder */ -/* single bond to metal atom: initial_bond_order[i] = pSrm->nMetalInitBondOrder */ -/* n-order bond to metal atom initial_bond_order[i] = pSrm->nMetalInitBondOrder + n-1 */ -/* = bond_order[i]-BOND_TYPE_SINGLE+pSrm->nMetalInitBondOrder */ -/* single t-endpoint--atom bond initial_bond_order[i] = pSrm->nMetal2EndpointInitBondOrder */ -/* n-order t-endpoint--metal bond initial_bond_order[i] = pSrm->nMetal2EndpointInitBondOrder+n-1 */ -/* = bond_order[i]-BOND_TYPE_SINGLE+pSrm->nMetal2EndpointInitBondOrder*/ -/* */ -/* Exceptions from simple atom-metal conditions: */ -/* 1. Atom is a tautomeric endpoint: use pSrm->nMetal2Endpoint* instead of pSrm->nMetal* */ -/* 2. Atom is sp3-stereogenic and pSrm->bFixStereoBonds != 0: use atom-atom rules */ -/* 3. Atom has a sp2-stereo and pSrm->bFixStereoBonds != 0: use atom-atom rules */ -/* */ -/* Atom-atom rules (applies to all atoms if pSrm->bMetalAddFlower=0) */ -/* */ -/* min_bond_order[i] = BOND_TYPE_SINGLE (BOND_TYPE_SINGLE = 1) */ -/* initial_bond_order[i] = bond_type[i] */ -/* */ -/* General rules: */ -/* initial_bond_flow[i] = initial_bond_order[i]-min_bond_order[i] */ -/* atom[k] initial_st_cap = at[k].chem_bonds_valence - SUM{i; initial_bond_order[i]} */ -/* bond_cap[i] = BOND_TYPE_TRIPLE - min_bond_order[i] */ -/* (reason: quadruple and higher order bonds are not allowed) */ -/* Exception: in case of metal-atom bond, if pSrm->nMetal2EndpointInitEdgeFlow = 0 AND */ -/* pSrm->nMetalInitBondOrder - pSrm->nMetalMinBondOrder = 1 then */ -/* reduce bond to metal order by 1 and increase st_cap of both neighbors by 1: */ -/* initial_bond_flow[i] --; metal_initial_st_cap += num_bonds; */ -/* ==== Note: ONLY the INCREASE is already included in pVA->cInitFreeValences of both atoms */ -/* */ -/* Notes: initial_st_cap does not include: */ -/* 1. atom[k] additional st_cap from ChargeStruct pCN[0].v.cap */ -/* 2. pVA[k].cInitFreeValences due to a difference between the smallest known valence and st_cap */ -/* */ -/* here k=atom at[k] index, */ -/* i=bond index; i = 0..at[k].valence; */ -/* SUM{i; M[i]} is a sum of M[i] over all i */ -/* bond_order[i] = at[k].bond_type[i] >= BOND_TYPE_SINGLE - input bond order */ -/******************************************************************************************************/ - -/***************** new *************************************************************************************/ - -int BondFlowMaxcapMinorder( inp_ATOM *atom, VAL_AT *pVA, ICHICONST SRM *pSrm, int iat, int ineigh, - int *pnMaxcap, int *pnMinorder, int *pbNeedsFlower ) -{ - int nFlow, nMaxcap, nMinorder, nInitorder, bNeedsFlower = 0; - inp_ATOM *at = atom + iat; - int neigh = at->neighbor[ineigh]; - int bond_type = at->bond_type[ineigh] & BOND_TYPE_MASK; - int nMetal = (0 != pVA[iat].cMetal) + (0 != pVA[neigh].cMetal); - int nEndpoint = (0 != at->endpoint) + (0 != atom[neigh].endpoint); - int nStereo = (at->p_parity || at->sb_parity[0]) + (atom[neigh].p_parity || atom[neigh].sb_parity[0]); - - if ( bond_type > BOND_TYPE_TRIPLE ) { - bond_type = BOND_TYPE_SINGLE; - } - /* M=metal, A=non-metal atom, e=endpoint */ - if ( nStereo && pSrm->bFixStereoBonds || !nMetal || !pSrm->bMetalAddFlower ) { - /* atom-atom rules, no metal atoms involved (1: A-A, A-Ae, Ae-Ae) */ - nMinorder = BOND_TYPE_SINGLE; - nInitorder = bond_type; - nFlow = nInitorder - nMinorder; - } else - if ( nMetal && !nEndpoint ) { - /* M-a, M-M */ - /* atom - metal or metal-metal, none of them is an endpoint (2: M-M, M-A) */ - nMinorder = pSrm->nMetalMinBondOrder; - nInitorder = pSrm->nMetalInitBondOrder + bond_type - BOND_TYPE_SINGLE; - nFlow = nInitorder - nMinorder; - if ( !pSrm->nMetalInitEdgeFlow && - pSrm->nMetalInitBondOrder > pSrm->nMetalMinBondOrder && - nFlow > 0 ) { - /* reduce initial flow by 1 and increase st_cap on metal by 1 */ - nFlow --; - } - bNeedsFlower = (0 != pVA[iat].cMetal); - } else - if ( pVA[iat].cMetal && !at->endpoint && !pVA[neigh].cMetal && atom[neigh].endpoint|| - pVA[neigh].cMetal && !atom[neigh].endpoint && !pVA[iat].cMetal && at->endpoint ) { - /* M-ae */ - /* metal connected to a non-metal endpoint (3: M-Ae) */ - nMinorder = pSrm->nMetal2EndpointMinBondOrder; - nInitorder = pSrm->nMetal2EndpointInitBondOrder + bond_type - BOND_TYPE_SINGLE; - nFlow = nInitorder - nMinorder; - if ( !pSrm->nMetal2EndpointInitEdgeFlow && - pSrm->nMetal2EndpointInitBondOrder > pSrm->nMetal2EndpointMinBondOrder && - nFlow > 0 ) { - /* reduce initial flow by 1 and increase st_cap on metal by 1 */ - nFlow --; - } - bNeedsFlower = (0 != pVA[iat].cMetal); - } else { - /* endpoint is metal => no flower (4: M-Me, Me-Me, Me-A, Me-Ae) */ - nMinorder = pSrm->nMetal2EndpointMinBondOrder; - nInitorder = pSrm->nMetal2EndpointInitBondOrder + bond_type - BOND_TYPE_SINGLE; - nFlow = nInitorder - nMinorder; - if ( !pSrm->nMetal2EndpointInitEdgeFlow && - pSrm->nMetal2EndpointInitBondOrder > pSrm->nMetal2EndpointMinBondOrder && - nFlow > 0 ) { - /* reduce initial flow by 1 and increase st_cap on metal by 1 */ - nFlow --; - } - bNeedsFlower = (pVA[iat].cMetal && !at->endpoint); - } - nMaxcap = BOND_TYPE_TRIPLE - nMinorder; - if ( pnMaxcap ) { - *pnMaxcap = nMaxcap; - } - if ( pnMinorder ) { - *pnMinorder = nMinorder; - } - if ( pbNeedsFlower ) { - *pbNeedsFlower = bNeedsFlower; - } - return nFlow; -} -/*********** new *******************************************************************************************/ -int AtomStcapStflow( inp_ATOM *atom, VAL_AT *pVA, ICHICONST SRM *pSrm, int iat, int *pnStcap, int *pnStflow, - EdgeFlow *pnMGroupEdgeCap, EdgeFlow *pnMGroupEdgeFlow ) -{ - int ineigh, bFlower; - int nStflow=0, nMaxBondCap, nMinBondOrder, bNeedsFlower = 0; - int valence = atom[iat].valence; - int nStcap = atom[iat].chem_bonds_valence; - int nMGroupEdgeCap = 0, nMGroupEdgeFlow = 0, nFlow; - - if ( pSrm->bMetalAddFlower ) { - nStcap -= pVA[iat].cInitOrigValenceToMetal - pVA[iat].cInitValenceToMetal; - } - - for ( ineigh = 0; ineigh < valence; ineigh ++ ) { - nFlow = BondFlowMaxcapMinorder( atom, pVA, pSrm, iat, ineigh, &nMaxBondCap, &nMinBondOrder, &bFlower ); - nStflow += nFlow; - nStcap -= nMinBondOrder; - if ( bFlower ) { - bNeedsFlower ++; - nMGroupEdgeFlow += nFlow; - nMGroupEdgeCap += BOND_TYPE_TRIPLE - nMinBondOrder + pSrm->nMetalMaxCharge_D; - } - } - if ( pnStcap ) { - *pnStcap = bNeedsFlower? nStflow : nStcap; /* initially, metal atoms are not radicals */ - } - if ( pnStflow ) { - *pnStflow = nStflow; - } - if ( pnMGroupEdgeFlow ) { - *pnMGroupEdgeFlow = nMGroupEdgeCap - nMGroupEdgeFlow; - } - if ( pnMGroupEdgeCap ) { - *pnMGroupEdgeCap = nMGroupEdgeCap; - } - return bNeedsFlower; /* number of variable bonds to metal */ -} -/************************************************************************************** -int nCountBnsSizes( inp_ATOM *at, int num_at, int nAddEdges2eachAtom, int nAddVertices, - T_GROUP_INFO *ti, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups ) - -fills out totals: - - pTCGroups->num_atoms = number of atoms - pTCGroups->num_bonds = number of bonds between atoms - pTCGroups->num_tgroups = number of tautomeric groups - pTCGroups->num_tgroup_edges = number of edges to tautomeric groups - pTCGroups->tgroup_charge = total charge on thautomeric atoms (negative) - pTCGroups->num_tc_groups = total number of all groups - pTCGroups->nVertices = total number of vertices excluding groups interconnections - pTCGroups->nEdges = total number of edges excluding groups interconnections - -creates entries for the groups and adds to each group: - - TC_GROUP::type = BNS_VERT_TYPE_TGROUP, BNS_VT_C_POS, BNS_VT_C_NEG, BNS_VT_C_POS_C, BNS_VT_C_NEG_C - TC_GROUP::ord_num = ordering number within the type, e.g. t-group number - TC_GROUP::st_cap = all from the atoms in ChargeStruct or tautomeric group info. - TC_GROUP::st_flow = all from the atoms in ChargeStruct (0 for t-groups). - TC_GROUP::num_edges = number of edges to the atoms or ChargeStruct vertices. - TC_GROUP::edges_cap = sum of all incoming edge caps; see also nTautEndpointEdgeCap(..). - TC_GROUP::edges_flow = sum of all incoming edge flows; 0 for t-groups. - - TC_GROUP::nVertexNumber - NO FILLED WITH ANYTHING - - Note: the nDelta = st_cap - st_flow needs to be preserved when adding more vertices - -Return value: =0 => success - <0 => error - **************************************************************************************/ -int nCountBnsSizes( inp_ATOM *at, int num_at, int nAddEdges2eachAtom, int nAddVertices, - T_GROUP_INFO *ti, VAL_AT *pVA, ICHICONST SRM *pSrm, ALL_TC_GROUPS *pTCGroups ) -{ - int i, j, n, k, ret = 0, nBonds, nOtherEdges, nVertices, bMetalAtoms, bNeedsFlower; - int nTgroupEdges, nTgroupEdgesFromTg, nTotNegChargInTgroups, cap, flow; - MY_CONST C_NODE *pCN = NULL; - nVertices = nBonds = nOtherEdges = nTgroupEdges = nTgroupEdgesFromTg = nTotNegChargInTgroups = 0; - - /* count metal atoms and electrons */ - for ( i = 0; i < num_at; i ++ ) { - pTCGroups->num_metal_atoms += (pVA[i].cMetal != 0); - pTCGroups->num_metal_bonds += pVA[i].cNumBondsToMetal; - pTCGroups->total_electrons += at[i].el_number; - pTCGroups->total_electrons_metals += pVA[i].cMetal? at[i].el_number : 0; - } - pTCGroups->total_electrons -= pTCGroups->total_charge; - pTCGroups->num_metal_bonds /= 2; - - /* register tautomeric groups */ - for ( i = 0; i < ti->num_t_groups; i ++ ) { - ret = RegisterTCGroup( pTCGroups, BNS_VERT_TYPE_TGROUP, ti->t_group[i].nGroupNumber, - ti->t_group[i].num[0] /* st_cap */, 0 /* st_flow */, - 0 /* edge cap */, 0 /* edge flow */, ti->t_group[i].nNumEndpoints /* num Edges */ ); - if ( ret < 0 ) { - goto exit_function; - } - /* edges to tautomeric groups */ - nOtherEdges += ti->t_group[i].nNumEndpoints; - nTgroupEdgesFromTg += ti->t_group[i].nNumEndpoints; - /* total negative charge in t-groups */ - nTotNegChargInTgroups += ti->t_group[i].num[1]; - if ( ret > 0 ) { - /* should always happen since this is the first time this t-group is added */ - j = ret-1; - pTCGroups->pTCG[j].tg_num_H = ti->t_group[i].num[0] - ti->t_group[i].num[1]; - pTCGroups->pTCG[j].tg_num_Minus = ti->t_group[i].num[1]; - } - } - - bMetalAtoms = 0; - -repeat_for_metals: - - /* count vertices and register ChargeValence groups */ - /* for now an atom may belong either to a t-group or to a ChargeValence group, but not to both */ - for ( i = 0; i < num_at; i ++ ) { - /* number of bonds */ - nBonds += at[i].valence; - /* Process ChargeStruct vertices and edges */ - if ( pVA[i].cnListIndex ) { - /* count vertices & edges in the ChargeValence Substructure attached to an atom */ - /* Important: unlike inp_ATOM, each edge e appears in pCN[*].e[*] only ONE time */ - int len = cnList[j = pVA[i].cnListIndex-1].len; - int bits = cnList[j].bits; - int type, neigh_type, metal_group_number; - pCN = cnList[j].pCN; - - /* first process all non-metals, after that -- all metals */ - if ( (bits != cn_bits_Me) != !bMetalAtoms ) { - continue; - } - metal_group_number = 0; - for ( j = 0; j < len; j ++) { - type = pCN[j].v.type; /* ChargeStruct vertex type: atom is the first, c-groups are last */ - - /* process all pCN[j] neighbors */ - for ( k = 0; k < MAX_CN_VAL && (n = pCN[j].e[k].neigh); k ++ ) { - nOtherEdges ++; /* edges inside ChargeStruct */ - n --; /* neighbor vertex position inside cnList[j].pCN */ - neigh_type = pCN[n].v.type; /* type of the neighboring atom */ - - if ( IS_BNS_VT_C_GR(neigh_type) ) { - /* register this edge to a CN-group vertex */ - cap = !bMetalAtoms? pCN[j].e[k].cap : pCN[j].e[k].cap? pSrm->nMetalMaxCharge_D : 0; - flow = !bMetalAtoms? pCN[j].e[k].flow : pCN[j].e[k].flow? pSrm->nMetalMaxCharge_D : 0; - - ret = RegisterTCGroup( pTCGroups, neigh_type, 0 /* ord_num*/, - 0 /* st_cap */, 0 /* st_flow */, - cap /* edge cap*/, flow /* edge flow */, 1 /* nNumEdges*/); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret > 0 ) { - /* the group has just been created; add one more edge to (+/-) or supergroup */ - ret = RegisterTCGroup( pTCGroups, neigh_type, 0 /* ord_num*/, - 0 /* st_cap */, 0 /* st_flow */, - 0 /* edge cap*/, 0/* edge flow*/, 1 /* nNumEdges*/); - if ( ret < 0 ) { - goto exit_function; - } - nOtherEdges ++; - } - } - - if ( IS_BNS_VT_C_GR(type) ) { - /* register this edge to a CN-group vertex; normally this does not happen */ - cap = !bMetalAtoms? pCN[j].e[k].cap : pCN[j].e[k].cap? pSrm->nMetalMaxCharge_D : 0; - flow = !bMetalAtoms? pCN[j].e[k].flow : pCN[j].e[k].flow? pSrm->nMetalMaxCharge_D : 0; - ret = RegisterTCGroup( pTCGroups, type, 0 /* ord_num*/, - 0 /* st_cap */, 0 /* st_flow */, - cap /* edge cap*/, flow /* edge flow */, 1 /* nNumEdges*/); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret > 0 ) { - /* the group has just been created; add one more edge to (+/-) or supergroup */ - ret = RegisterTCGroup( pTCGroups, type, 0 /* ord_num*/, - 0 /* st_cap */, 0 /* st_flow */, - 0 /* edge cap*/, 0/* edge flow*/, 1 /* nNumEdges*/); - if ( ret < 0 ) { - goto exit_function; - } - nOtherEdges ++; - } - } - } /* end of the current vertex pCN[j] neighbors */ - - /* process pCN[j] vertex */ - - if ( type & BNS_VERT_TYPE_ATOM ) { - continue; /* do not count regular atoms here */ - } - if ( IS_BNS_VT_CHRG_STRUCT(type) ) { - nVertices ++; - continue; - } - - if ( pSrm->bMetalAddFlower && IS_BNS_VT_M_GR( type ) ) { - /* special treatment: flow and cap are known as well as structure */ - /* initial bond valence to metal is either 0 or 1 */ - EdgeFlow nEdgeFlow, nEdgeCap; - bNeedsFlower = AtomStcapStflow( at, pVA, pSrm, i, NULL /*pnStcap*/, NULL /*pnStflow*/, - &nEdgeCap, &nEdgeFlow ); - if ( !bNeedsFlower ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - /* - GetAtomToMCGroupInitEdgeCapFlow( &nEdgeCap, &nEdgeFlow, pSrm, at, pVA, i ); - GetAtomToMCGroupInitEdgeCapFlow( &nEdgeCap, &nEdgeFlow, pSrm ); - */ - /* the 1st is the flower base */ - /* atom - G0 edge and G0 vertex */ - ret = RegisterTCGroup( pTCGroups, type, 0 /* ord_num*/, - /*pVA[i].cInitFreeValences*/ 0 /* st_cap */, 0 /* st_flow */, - (int)nEdgeCap, (int)nEdgeFlow, 1 /* nNumEdges*/); - if ( ret < 0 ) { - goto exit_function; - } - /* count edge atom-G0 */ - nOtherEdges ++; - if ( ret > 0 ) { - /* first time registration: add G0-G1 and G0-G2 edges to G0 */ - ret = RegisterTCGroup( pTCGroups, type, 0 /* ord_num*/, - 0 /* st_cap */, 0 /* st_flow */, - 0,/* edge cap*/ 0 /*edge flow*/, 2 /* nNumEdges*/); - - if ( ret < 0 ) { - goto exit_function; - } - /* first time registration: add G1; it has 3 edges */ - ret = RegisterTCGroup( pTCGroups, type, 1 /* ord_num*/, - 0 /* st_cap */, 0 /* st_flow */, - 0,/* edge cap*/ 0 /*edge flow*/, 3 /* nNumEdges*/); - - if ( ret <= 0 ) { - ret = !ret? RI_ERR_PROGR : ret; - goto exit_function; - } - /* first time registration: add G2; it has 3 edges */ - ret = RegisterTCGroup( pTCGroups, type, 2 /* ord_num*/, - 0 /* st_cap */, 0 /* st_flow */, - 0,/* edge cap*/ 0 /*edge flow*/, 3 /* nNumEdges*/); - - if ( ret <= 0 ) { - ret = !ret? RI_ERR_PROGR : ret; - goto exit_function; - } - /* first time registration: add G3; it has 2 edges */ - ret = RegisterTCGroup( pTCGroups, type, 3 /* ord_num*/, - 0 /* st_cap */, 0 /* st_flow */, - 0,/* edge cap*/ 0 /*edge flow*/, 2 /* nNumEdges*/); - - if ( ret <= 0 ) { - ret = !ret? RI_ERR_PROGR : ret; - goto exit_function; - } - /* count added metal flower vertices: G0, G1, G2, G3 */ - nVertices += 4; - /* count added metal flower edges: C0-C1, C0-C2, C1-C2, C1-C3, C2-C3 */ - nOtherEdges += 5; - /* add connections of G0 to G1 and G2 */ - } - continue; - } - - nVertices ++; /* count BNS_VT_C_POS* types; all contain BNS_VERT_TYPE_C_GROUP bit */ - if ( !IS_BNS_VT_C_GR(type) ) { /* check */ - ret = RI_ERR_PROGR; - goto exit_function; - } - /* add st_cap and st_flow for a charge group */ - cap = !bMetalAtoms? pCN[j].v.cap : pCN[j].v.cap? pSrm->nMetalMaxCharge_D : 0; - flow = !bMetalAtoms? pCN[j].v.flow : pCN[j].v.flow? pSrm->nMetalMaxCharge_D : 0; - ret = RegisterTCGroup( pTCGroups, type, 0 /* ord_num*/, - cap /* st-cap*/, flow /* st-flow */, - 0 /* edge cap */, 0 /* edge flow */, 0 /* edges already counted */ ); - if ( ret < 0 ) { - goto exit_function; - } - } - } else { - pCN = NULL; - } - /* count edge caps to t-groups */ - if ( at[i].endpoint ) { - int nEdgeCap = nTautEndpointEdgeCap( at, pVA, i ); - nTgroupEdges ++; - if ( nEdgeCap < 0 ) { - ret = nEdgeCap; - goto exit_function; - } - /* add number of unsatisfied valences for a t-group; the unknown flow = 0 */ - ret = RegisterTCGroup( pTCGroups, BNS_VERT_TYPE_TGROUP, at[i].endpoint, - 0 /* st_cap */, 0 /* st_flow */, - nEdgeCap /* edge cap */, 0 /* edge flow */, - 0 /* t-group edges have already been counted */ ); - if ( ret < 0 ) { - goto exit_function; - } - - } - } - if ( !bMetalAtoms && pTCGroups->num_metal_atoms ) { - bMetalAtoms = 1; - nBonds = 0; /* added 2006-05-15 */ - goto repeat_for_metals; - } - - /* count real atoms and bonds */ - nBonds /= 2; - pTCGroups->num_atoms = num_at; - pTCGroups->num_bonds = nBonds; - - pTCGroups->num_tgroups = ti->num_t_groups; - pTCGroups->num_tgroup_edges = nTgroupEdges; - pTCGroups->tgroup_charge = -nTotNegChargInTgroups; - - if ( 0 <= ret && nTgroupEdgesFromTg != nTgroupEdges ) { - ret = BNS_PROGRAM_ERR; - } - - nVertices += num_at; - - - /* count other vertices */ - nVertices += ti->num_t_groups; - nBonds += nOtherEdges; - - /* return edges and vertices */ - pTCGroups->nVertices = nVertices; - pTCGroups->nEdges = nBonds; - -exit_function: - return ret; -} - -/**************************************************************** - int nAddSuperCGroups( ALL_TC_GROUPS *pTCGroups ) - - 1. adds BNS_VT_C_POS_ALL and BNS_VT_C_NEG_ALL ONLY if both - {TCG_Plus0 and TCG_Plus_C0} and/or - {TCG_Minus0 and TCG_Minus_C0} are present, respectively - - 2. fills pTCGroups->nGroup[]: - - pTCGroups->nGroup[k] < 0 => does not exist - pTCGroups->nGroup[k] = i => the group is pTCGroups->pTCG[i] - - where group group - k = type number - TCG_Plus0 BNS_VT_C_POS 0 - TCG_Plus1, BNS_VT_C_POS 1 - TCG_Minus0, BNS_VT_C_NEG 0 - TCG_Minus1, BNS_VT_C_NEG 1 - TCG_Plus_C0, BNS_VT_C_POS_C 0 - TCG_Plus_C1, BNS_VT_C_POS_C 1 - TCG_Minus_C0, BNS_VT_C_NEG_C 0 - TCG_Minus_C1, BNS_VT_C_NEG_C 1 - TCG_Plus, BNS_VT_C_POS_ALL 0 - TCG_Minus, BNS_VT_C_NEG_ALL 0 - -only groups with number 0 are processed - - 3. If only one of the groups in pairs mentioned in (1) above - is present then - - pTCGroups->nGroup[TCG_Plus] := pTCGroups->nGroup[TCG_Plus0] or - pTCGroups->nGroup[TCG_Plus] := pTCGroups->nGroup[TCG_Plus_C0]; - an additional BNS_VT_C_POS_ALL vertex is not created - - same for pTCGroups->nGroup[TCG_Minus] and BNS_VT_C_NEG_ALL - - 4. Adds to these new "supergroups" (TCG_Plus, TCG_Minus) - descriptions in pTCGroups->pTCG[k] - st_cap, st_flow, edges cap and flow from the corresponding - groups {TCG_Plus0 and TCG_Plus_C0}. Same for the Minus groups. - Stores indexes k in - pTCGroups->nGroup[TCG_Plus], pTCGroups->nGroup[TCG_Minus] - - ****************************************************************/ -int nAddSuperCGroups( ALL_TC_GROUPS *pTCGroups ) -{ - int i, k, n, n1, n2, n3, nNumTg = 0, ret = 0, nNumToConnect; - - for ( i = 0; i < pTCGroups->num_tc_groups; i ++ ) { - if ( pTCGroups->pTCG[i].type & BNS_VERT_TYPE_TGROUP ) { - nNumTg ++; - continue; /* t-group */ - } - if ( IS_BNS_VT_C_GR(pTCGroups->pTCG[i].type) || - IS_BNS_VT_M_GR(pTCGroups->pTCG[i].type) ) { - /* ChargeValence (cn) group */ - switch( pTCGroups->pTCG[i].type ) { - case BNS_VT_C_POS: - k = TCG_Plus0; - break; - case BNS_VT_C_NEG: - k = TCG_Minus0; - break; - case BNS_VT_C_POS_C: - k = TCG_Plus_C0; - break; - case BNS_VT_C_NEG_C: - k = TCG_Minus_C0; - break; - case BNS_VT_C_POS_M: - k = TCG_Plus_M0; - break; - case BNS_VT_C_NEG_M: - k = TCG_Minus_M0; - break; - case BNS_VT_M_GROUP: - switch( pTCGroups->pTCG[i].ord_num ) { - case 0: - k = TCG_MeFlower0; - break; - case 1: - k = TCG_MeFlower1; - break; - case 2: - k = TCG_MeFlower2; - break; - case 3: - k = TCG_MeFlower3; - break; - default: - ret = RI_ERR_PROGR; /* unexpected group type */ - goto exit_function; - } - break; - - default: - ret = RI_ERR_PROGR; /* unexpected group type */ - goto exit_function; - } - if ( pTCGroups->nGroup[k] >= 0 || pTCGroups->pTCG[i].ord_num && !IS_BNS_VT_M_GR(pTCGroups->pTCG[i].type) ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - pTCGroups->nGroup[k] = i; /* ordering number of the Charge group, starting from 0 */ - } - } - /* add (+) supergroup */ - n1 = pTCGroups->nGroup[TCG_Plus0]; - n2 = pTCGroups->nGroup[TCG_Plus_C0]; - n3 = pTCGroups->nGroup[TCG_Plus_M0]; - nNumToConnect = (n1>=0) + (n2>=0) + (n3>=0); - if ( nNumToConnect ) { - /* if both groups are present then add a supergroup */ - ret = RegisterTCGroup( pTCGroups, BNS_VT_C_POS_ALL, 0, - 0 /* st_cap */, - 0 /* st_flow */, - 0 /* edge cap */, - 0 /* edge flow */, - 1+nNumToConnect /* one more edge to connect to an additional (+/-) vertex */ ); - - if ( ret <= 0 ) { - ret = !ret? RI_ERR_PROGR : ret; - goto exit_function; - } - pTCGroups->nGroup[TCG_Plus] = ret - 1; /* newly added group number */ - pTCGroups->nVertices += 2; /* two vertices including itself */ - pTCGroups->nEdges += 1 + nNumToConnect; /* one more edge to connect to an additional (+/-) vertex */ - } - /* add (-) supergroup */ - n1 = pTCGroups->nGroup[TCG_Minus0]; - n2 = pTCGroups->nGroup[TCG_Minus_C0]; - n3 = pTCGroups->nGroup[TCG_Minus_M0]; - nNumToConnect = (n1>=0) + (n2>=0) + (n3>=0); - if ( nNumToConnect ) { - /* if both groups are present then add a supergroup */ - ret = RegisterTCGroup( pTCGroups, BNS_VT_C_NEG_ALL, 0, - 0 /* st_cap */, - 0 /* st_flow */, - 0 /* edge cap */, - 0 /* edge flow */, - 1+nNumToConnect /* one more edge to connect to an additional (+/-) vertex */ ); - - if ( ret < 0 ) { - goto exit_function; - } - pTCGroups->nGroup[TCG_Minus] = ret - 1; /* newly added group number */ - pTCGroups->nVertices += 2; /* needs two vertices including itself */ - pTCGroups->nEdges += 1 + nNumToConnect; /* one more edge to connect to an additional (+/-) vertex */ - } - - /* add neutralization vertex: (+)-()=(-) connection */ - k = pTCGroups->nGroup[TCG_Minus]; - n = pTCGroups->nGroup[TCG_Plus]; - nNumToConnect = (k>=0) + (n>=0); - if ( nNumToConnect ) { - pTCGroups->nVertices += 1; - pTCGroups->nEdges += nNumToConnect; /* one edge per super-c-group */ - } - - ret = 0; - -exit_function: - return ret; -} -/*********************************************************************************/ -int AddTGroups2TCGBnStruct( BN_STRUCT *pBNS, StrFromINChI *pStruct, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, int nMaxAddEdges ) -{ - int ret = 0; - inp_ATOM *at = pStruct->at; - int num_atoms = pStruct->num_atoms; - int tot_st_cap, tot_st_flow; - /* ret = ReInitBnStruct( pBNS ); */ - if ( pTCGroups->num_tgroups /* tgi && tgi->num_t_groups && tgi->t_group*/ ) { - int i, k, endpoint, /*centerpoint,*/ fictpoint; - int num_tg = pTCGroups->num_tgroups; - int num_edges = pBNS->num_edges; - int num_vertices = pBNS->num_vertices; - BNS_VERTEX *vert_ficpoint, *vert_ficpoint_prev; /* fictitious vertex describing t-group */ - BNS_VERTEX *vert_endpoint; - BNS_EDGE *edge; /* edge between that vertex and the tautomeric endpoint */ - int nMaxTGroupNumber = 0; - /*ENDPOINT_INFO eif;*/ - - /* Debug: check overflow */ - if ( num_vertices + num_tg >= pBNS->max_vertices ) { - return BNS_VERT_EDGE_OVFL; - } - if ( num_edges + pTCGroups->num_tgroup_edges >= pBNS->max_edges ) { - return BNS_VERT_EDGE_OVFL; - } - /* find the largest t-group ID */ - for ( i = 0; i < pTCGroups->num_tc_groups; i ++ ) { - if ( pTCGroups->pTCG[i].type & BNS_VERT_TYPE_TGROUP ) { - k = pTCGroups->pTCG[i].ord_num; - if ( k <= 0 ) { - return BNS_CPOINT_ERR; /* t-group does not have a number or has a wrong number */ - } - if ( k > pTCGroups->num_tc_groups ) { - return BNS_CPOINT_ERR; /* t-group has a wrong number */ - } - if ( k != nMaxTGroupNumber + 1 ) { - return BNS_CPOINT_ERR; /* t-group numbers are not contiguously ascending */ - } - nMaxTGroupNumber = k; - } else { - break; /* t-groups are contiguous and first in the list */ - } - } - if ( i != num_tg ) { - return BNS_CPOINT_ERR; /* number of t-groups is wrong */ - } - /* since t-group IDs may be not contiguous, clear all vertices that will be added. - all-zeroes-vertex will be ignored by the BNS - */ - memset( pBNS->vert+num_vertices, 0, nMaxTGroupNumber*sizeof(pBNS->vert[0]) ); - /* initialize new fictitious vertices */ - vert_ficpoint_prev = pBNS->vert+num_vertices - 1; - - tot_st_cap = tot_st_flow = 0; - - for ( i = 0; i < num_tg; i ++, vert_ficpoint_prev = vert_ficpoint ) { - /* - vert_ficpoint-1 is the last vertex; - vert_ficpoint is the vertex that is being added - Note: nGroupNumber are not contiguous - */ - vert_ficpoint = pBNS->vert+num_vertices + pTCGroups->pTCG[i].ord_num - 1; - vert_ficpoint->iedge = vert_ficpoint_prev->iedge + vert_ficpoint_prev->max_adj_edges; - vert_ficpoint->max_adj_edges = pTCGroups->pTCG[i].num_edges+nMaxAddEdges+BNS_ADD_SUPER_TGROUP; - vert_ficpoint->num_adj_edges = 0; - vert_ficpoint->st_edge.flow = vert_ficpoint->st_edge.flow0 = 0; - vert_ficpoint->st_edge.cap = vert_ficpoint->st_edge.cap0 = pTCGroups->pTCG[i].st_cap; - tot_st_cap += pTCGroups->pTCG[i].st_cap; - vert_ficpoint->type = pTCGroups->pTCG[i].type; - pTCGroups->pTCG[i].nVertexNumber = vert_ficpoint - pBNS->vert; - } - - for ( endpoint = 0; endpoint < num_atoms; endpoint ++ ) { - if ( !at[endpoint].endpoint ) - continue; - fictpoint = at[endpoint].endpoint + num_vertices - 1; - vert_ficpoint = pBNS->vert + fictpoint; /* t-group vertex */ - vert_endpoint = pBNS->vert + endpoint; /* endpoint vertex */ - /* Debug: check overflow */ - if ( fictpoint >= pBNS->max_vertices || - num_edges >= pBNS->max_edges || - vert_ficpoint->num_adj_edges >= vert_ficpoint->max_adj_edges || - vert_endpoint->num_adj_edges >= vert_endpoint->max_adj_edges ) { - ret = BNS_VERT_EDGE_OVFL; - break; - } -#ifdef NEVER - /* obtain donor/acceptor info */ - if ( !nGetEndpointInfo( at, endpoint, &eif ) ) { - ret = BNS_BOND_ERR; - break; - } -#endif - vert_endpoint->type |= BNS_VERT_TYPE_ENDPOINT; -#ifdef NEVER - /* set capacity = 1 to the edges from the endpoint to the centerpoint(s) */ - for ( k = 0; k < vert_endpoint->num_adj_edges; k ++ ) { - int iedge = vert_endpoint->iedge[k]; - if ( !pBNS->edge[iedge].cap ) { - /* single bond, possibly between endpoint and centerpoint */ - centerpoint = (pBNS->edge[iedge].neighbor12 ^ endpoint); - if ( centerpoint < pBNS->num_atoms && - pBNS->vert[centerpoint].st_edge.cap >= 1 ) { - int bond_type = (at[endpoint].bond_type[k] & BOND_TYPE_MASK); - if (bond_type == BOND_TAUTOM || - bond_type == BOND_ALTERN || - bond_type == BOND_ALT12NS || - bond_type == BOND_SINGLE ) { - pBNS->edge[iedge].cap = 1; - } - } - } - } -#endif - /* create a new edge connecting endpoint to the new fictitious t-group vertex vert_ficpoint */ - edge = pBNS->edge + num_edges; - edge->cap = vert_endpoint->st_edge.cap - vert_endpoint->st_edge.flow; - edge->cap = inchi_min( edge->cap, MAX_TGROUP_EDGE_CAP ); - edge->cap = inchi_max( edge->cap, 0 ); - edge->flow = 0; - edge->pass = 0; -#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) - edge->forbidden &= pBNS->edge_forbidden_mask; -#endif - -#ifdef NEVER - /* later include case when the charge change allows the endpoint to become tautomeric */ - /* mark endoint having moveable H atom with flow=1 */ - - /* -- old "no charges" version -- */ - /* if (at[endpoint].chem_bonds_valence == at[endpoint].valence) */ - /* -- the following line takes charges into account -- */ - if ( eif.cDonor ) /* means the endpoint has an H-atom to donate */ - { - /* increment edge flow */ - edge->flow ++; - /* increment one vertex st-flow & cap */ - vert_ficpoint->st_edge.flow ++; - vert_ficpoint->st_edge.cap ++; - /* increment another vertex st-flow & cap */ - vert_endpoint->st_edge.flow ++; - vert_endpoint->st_edge.cap ++; - } -#endif - /* connect edge to endpoint and fictpoint and increment the counters of neighbors and edges */ - ret = ConnectTwoVertices( vert_endpoint, vert_ficpoint, edge, pBNS, 0 ); - if ( IS_BNS_ERROR( ret ) ) { - break; - } - num_edges ++; - edge->cap0 = edge->cap; - edge->flow0 = edge->flow; - pVA[endpoint].nTautGroupEdge = num_edges; /* edge index + 1 */ - } - - pBNS->num_edges = num_edges; - pBNS->num_vertices += nMaxTGroupNumber; - pBNS->num_t_groups = num_tg; - pBNS->tot_st_cap += tot_st_cap; - pBNS->tot_st_flow += tot_st_flow; - - } - return ret; -} -/*****************************************************************************************************/ -int ConnectTwoVertices( BNS_VERTEX *p1, BNS_VERTEX *p2, BNS_EDGE *e, BN_STRUCT *pBNS, int bClearEdge ) -{ - int ip1 = p1 - pBNS->vert; - int ip2 = p2 - pBNS->vert; - int ie = e - pBNS->edge; - /* debug: check bounds */ - if ( ip1 >= pBNS->max_vertices || ip1 < 0 || - ip2 >= pBNS->max_vertices || ip2 < 0 || - ie >= pBNS->max_edges || ie < 0 || - (p1->iedge - pBNS->iedge) < 0 || - (p1->iedge - pBNS->iedge) + p1->max_adj_edges > pBNS->max_iedges || - (p2->iedge - pBNS->iedge) < 0 || - (p2->iedge - pBNS->iedge) + p2->max_adj_edges > pBNS->max_iedges || - p1->num_adj_edges >= p1->max_adj_edges || - p2->num_adj_edges >= p2->max_adj_edges ) { - return BNS_VERT_EDGE_OVFL; - } - /* clear the edge */ - if ( bClearEdge ) { - memset( e, 0, sizeof(*e) ); - } else - if ( e->neighbor1 || e->neighbor12 ) { - return BNS_PROGRAM_ERR; - } - /* connect */ - e->neighbor1 = inchi_min( ip1, ip2 ); - e->neighbor12 = ip1 ^ ip2; - p1->iedge[p1->num_adj_edges] = ie; - p2->iedge[p2->num_adj_edges] = ie; - e->neigh_ord[ip1 > ip2] = p1->num_adj_edges ++; - e->neigh_ord[ip1 < ip2] = p2->num_adj_edges ++; - return 0; -} - -/*********************************************************************************************************** - METAL ATOMS' FLOWER - Provides a source/sink of "free valences" - *********************************************************************************************************** - - c1+...+cn = 2c+dc - total cap and flow of edges to the flower base from metal atoms - f1+...+fn = 2f+df they should allow changing bonds to metals from 0-order to triple - dc,df = 0 or 1 hence c=3*n, f=0 (initial zero bond order) or n - Gi=vertex(M-group) - Ci=its st_cap [C3,F3] C0 = F0 = 2c + 2D + dc (st_cap & st_flow) - Fi=its st_flow G3 C2 = F2 = c + 2D - / \ C1 = F1 = c + 2D + dc-df - ci=cap of edge i cx,fx/ \cy,fy C3 = F3 = 0 - fi=edge flow / \ Constraints - [C2,F2]/ cd,fd \[C1,F1] ----------------- - G2--------G1 fa+fb+2f+df=F0=C0 - \ / ca = c + 2D (edge cap) fa+fd =F2=C2 - ca,fa \ / cb,fb fa = c + D - f (edge flow) fb+fd =C1=F1 - \ / fi <= ci - G0 [C0,F0] cb = c + 2D + dc ----------------- - /\ fb = c + D + dc - (f + df) - ci=3, fi=0 or 1 c1,f1 /... \ cn,fn ------------------------------------ - / \ cd = c + 2D D is an arbitrary integer > 0 - all n Metal atoms: M1 ... Mn fd = f + D it allows to apply - C3++ (add st_flow to cancel radicals) - For each Mi add cap and flow=cap cx = cy = D D times. - to M-charge group fx = fy = 0 - -------------------------------------------------------------------------------------- - | f=0 | f=c, dc>=df | 0 <= 2f+df <= 2c+dc - edge +------------------------+-----------+----------+-------------+------------- - | flow | rescap | flow | rescap | flow | rescap - ----------+------------+-----------+-----------+----------+-------------+------------- - f1+..+fn | df | 2c+dc-df | 2c+df | dc-df | 2f+df | 2c-2f+dc-df - fa | c+D | D | D | c+D | c+D-f | c+D - fb | c+D+dc-df | D+df | D+dc-df | c+D+df | c+D+dc-f-df| c+D+df - fd | D | c+D | c+D | D | f+D | D - -------------------------------------------------------------------------------------- -***********************************************************************************************************/ -int AddRadicalToMetal( int *tot_st_cap, int *tot_st_flow, ICHICONST SRM *pSrm, BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups ) -{ - int iG0 = pTCGroups->nGroup[TCG_MeFlower0]; /* index in pTCGroups->pTCG[] */ - int iG1 = pTCGroups->nGroup[TCG_MeFlower1]; - int iG2 = pTCGroups->nGroup[TCG_MeFlower2]; - int iG3 = pTCGroups->nGroup[TCG_MeFlower3]; - int n = (iG0>=0) + (iG1>=0) + (iG2>=0) + (iG3>=0); - int vG0, vG1, vG2, vG3; /* M-vertex number */ - BNS_VERTEX *pG0=NULL, *pG1=NULL, *pG2=NULL, *pG3=NULL; - - if ( pTCGroups->num_metal_atoms && - pSrm->bMetalAddFlower && - *tot_st_cap % 2 && - n == 4 ) { - vG0 = pTCGroups->pTCG[iG0].nVertexNumber; - vG1 = pTCGroups->pTCG[iG1].nVertexNumber; - vG2 = pTCGroups->pTCG[iG2].nVertexNumber; - vG3 = pTCGroups->pTCG[iG3].nVertexNumber; - - pG0 = pBNS->vert+vG0; - pG1 = pBNS->vert+vG1; - pG2 = pBNS->vert+vG2; - pG3 = pBNS->vert+vG3; - - /* add 1 unit to metal flower st_cap */ - pG3->st_edge.cap ++; - pG3->st_edge.cap0 ++; - (*tot_st_cap) ++; - return 1; - } - return 0; -} -/***********************************************************************************************************/ -int ConnectMetalFlower( int *pcur_num_vertices, int *pcur_num_edges, - int *tot_st_cap, int *tot_st_flow, ICHICONST SRM *pSrm, - BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups ) -{ - int iG0 = pTCGroups->nGroup[TCG_MeFlower0]; /* index in pTCGroups->pTCG[] */ - int iG1 = pTCGroups->nGroup[TCG_MeFlower1]; - int iG2 = pTCGroups->nGroup[TCG_MeFlower2]; - int iG3 = pTCGroups->nGroup[TCG_MeFlower3]; - int n = (iG0>=0) + (iG1>=0) + (iG2>=0) + (iG3>=0); - int vG0, vG1, vG2, vG3; /* M-vertex number */ - int cur_num_edges = *pcur_num_edges; - int cur_num_vertices = *pcur_num_vertices; - BNS_VERTEX *pG0=NULL, *pG1=NULL, *pG2=NULL, *pG3=NULL; - BNS_EDGE *ea=NULL, *eb=NULL, *ed=NULL, *ex=NULL, *ey=NULL, *e; - int ia, ib, id, ix, iy; - int c, f, dc, df, ca, fa, cb, fb, cd, fd, cx, fx, cy, fy; - int C0, F0, C1, F1, C2, F2, C3, F3, D; - int ret = 0, i; - - if ( 0 == n ) { - goto exit_function; - } - if ( 4 != n ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - vG0 = pTCGroups->pTCG[iG0].nVertexNumber; - vG1 = pTCGroups->pTCG[iG1].nVertexNumber; - vG2 = pTCGroups->pTCG[iG2].nVertexNumber; - vG3 = pTCGroups->pTCG[iG3].nVertexNumber; - - pG0 = pBNS->vert+vG0; - pG1 = pBNS->vert+vG1; - pG2 = pBNS->vert+vG2; - pG3 = pBNS->vert+vG3; - - /* count G0 edges cap and flow (currently only atoms are connected to G0) */ - for ( i = 0, c = 0, f = 0; i < pG0->num_adj_edges; i ++ ) { - e = pBNS->edge + pG0->iedge[i]; - c += e->cap; - f += e->flow; - } - - /* consistency checks */ - if ( !IS_BNS_VT_M_GR(pTCGroups->pTCG[iG0].type) && - (pTCGroups->pTCG[iG0].edges_cap != pG0->st_edge.cap || - pTCGroups->pTCG[iG0].edges_flow != pG0->st_edge.flow) ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - if ( pTCGroups->pTCG[iG0].edges_cap != c || - pTCGroups->pTCG[iG0].edges_flow != f ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - - /* get new edges */ - - ea = pBNS->edge + (ia=cur_num_edges++); - eb = pBNS->edge + (ib=cur_num_edges++); - ed = pBNS->edge + (id=cur_num_edges++); - ex = pBNS->edge + (ix=cur_num_edges++); - ey = pBNS->edge + (iy=cur_num_edges++); - - /* connect vertices with edges */ - ret = ConnectTwoVertices( pG0, pG1, eb, pBNS, 1 ); - if ( IS_BNS_ERROR( ret ) ) { - goto exit_function; - } - ret = ConnectTwoVertices( pG0, pG2, ea, pBNS, 1 ); - if ( IS_BNS_ERROR( ret ) ) { - goto exit_function; - } - ret = ConnectTwoVertices( pG1, pG2, ed, pBNS, 1 ); - if ( IS_BNS_ERROR( ret ) ) { - goto exit_function; - } - ret = ConnectTwoVertices( pG1, pG3, ey, pBNS, 1 ); - if ( IS_BNS_ERROR( ret ) ) { - goto exit_function; - } - ret = ConnectTwoVertices( pG2, pG3, ex, pBNS, 1 ); - if ( IS_BNS_ERROR( ret ) ) { - goto exit_function; - } - - /* calculate caps and flows */ - - dc = c % 2; - c /= 2; - df = f % 2; - f /= 2; - - D = pSrm->nMetalFlowerParam_D; - - C0 = F0 = 2*c + 2*D + dc; - C1 = F1 = c + 2*D + dc - df; - C2 = F2 = c + 2*D; - C3 = F3 = 0; - - ca = c + 2*D; - fa = c + D - f; - - cb = c + 2*D + dc; - fb = c + D + dc - ( f + df ); - - cd = c + 2*D; - fd = f + D; - - cx = cy = D; - fx = fy = 0; - - /* check overflow */ - if ( C0 >= EDGE_FLOW_ST_MASK || F0 >= EDGE_FLOW_ST_MASK || - C1 >= EDGE_FLOW_ST_MASK || F1 >= EDGE_FLOW_ST_MASK || - C2 >= EDGE_FLOW_ST_MASK || F2 >= EDGE_FLOW_ST_MASK || - C3 >= EDGE_FLOW_ST_MASK || F3 >= EDGE_FLOW_ST_MASK ) { - return BNS_PROGRAM_ERR; /* cannot handle too large st-cap or st-flow */ - } - - /* set st caps and flows */ - - SetStCapFlow( pG0, tot_st_flow, tot_st_cap, C0, F0 ); - SetStCapFlow( pG1, tot_st_flow, tot_st_cap, C1, F1 ); - SetStCapFlow( pG2, tot_st_flow, tot_st_cap, C2, F2 ); - SetStCapFlow( pG3, tot_st_flow, tot_st_cap, C3, F3 ); - - SetEdgeCapFlow( ea, ca, fa ); - SetEdgeCapFlow( eb, cb, fb ); - SetEdgeCapFlow( ed, cd, fd ); - SetEdgeCapFlow( ex, cx, fx ); - SetEdgeCapFlow( ey, cy, fy ); - - - *pcur_num_edges = cur_num_edges; - *pcur_num_vertices = cur_num_vertices; - - ret = 0; - -exit_function: - - return ret; -} -/********************************************************************************/ -void SetEdgeCapFlow( BNS_EDGE *e, int edge_cap, int edge_flow ) -{ - e->cap = e->cap0 = edge_cap; - e->flow = e->flow0 = edge_flow; -} - -/********************************************************************************* - Add cap and flow to an edge - Add edge flow to the source vertex st_flow - Add edge cap & flow to the destination vertex cap and flow - *********************************************************************************/ -int AddEdgeFlow( int edge_cap, int edge_flow, BNS_EDGE *e01, BNS_VERTEX *pSrc /*src*/, - BNS_VERTEX *pDst/*dest*/, int *tot_st_cap, int *tot_st_flow ) -{ - /* overflow chaeck */ - if ( e01->cap < 0 || edge_cap < 0 || (int)e01->cap + edge_cap >= EDGE_FLOW_MASK ) { - return BNS_PROGRAM_ERR; - } - if ( pDst->st_edge.cap < 0 || (int)pDst->st_edge.cap + edge_cap >= EDGE_FLOW_ST_MASK || - pDst->st_edge.flow < 0 || (int)pDst->st_edge.flow + edge_flow >= EDGE_FLOW_ST_MASK || - pSrc->st_edge.cap < 0 || pSrc->st_edge.flow < 0 || - (int)pSrc->st_edge.flow + edge_flow >= EDGE_FLOW_ST_MASK ) { - return BNS_PROGRAM_ERR; - } - /* add flow */ - e01->cap += edge_cap; - e01->flow += edge_flow; - e01->cap0 = e01->cap; - e01->flow0 = e01->flow; - - pDst->st_edge.cap += edge_cap; - pDst->st_edge.cap0 = pDst->st_edge.cap; - *tot_st_cap += edge_cap; - - pDst->st_edge.flow += edge_flow; - pDst->st_edge.flow0 = pDst->st_edge.flow; - *tot_st_flow += edge_flow; - - pSrc->st_edge.flow += edge_flow; - pSrc->st_edge.flow0 = pSrc->st_edge.flow; - *tot_st_flow += edge_flow; - -/* - pDst->st_edge.cap += e01->cap; - pDst->st_edge.cap0 = pDst->st_edge.cap; - *tot_st_cap += e01->cap; - - pDst->st_edge.flow += e01->flow; - pDst->st_edge.flow0 = pDst->st_edge.flow; - *tot_st_flow += e01->flow; - - pSrc->st_edge.flow += e01->flow; - pSrc->st_edge.flow0 = pSrc->st_edge.flow; - *tot_st_flow += e01->flow; -*/ - return 0; -} -/************************************************************** - (+) and (-) group V - connection - ================================ - - BNS_VERT_TYPE__AUX (+/-)-connection - (v) st_cap = - / \ st_flow = (cap0 - Delta0 - flow0) + (cap1 - Delta1 -flow1) - / \ - / \ cap = cap1 - / \ flow = (cap1 - Delta1 - flow1) - / \ - (-) (+) st_cap = cap1 - / \ / \ st_flow = cap1 - Delta1 - /cap0 \ /cap1 \ - flow0 flow1 - -*************************************************************** - - (+) supergroup Y - connection - ============================== - - (+) BNS_VT_C_POS_ALL (+) supergroup - Delta0 | ============== - not shown | cap = cap0+cap1 - | flow = flow0+flow1-Delta0-Delta1 - BNS_VERT_TYPE__AUX (y) <------------------ additional vertex: st_cap = cap0+cap1 - / \ st_flow = cap0+cap1 - cap=cap0 / \ cap = cap1 - flow=cap0-flow0 / \ flow = cap1 - flow1 - Delta1 - -Delta0 / \ - not-C (+) (+) Carbons st_cap = cap1 - BNS_VT_C_POS / \ / \ BNS_VT_C_POS_C st_flow = cap1 - Delta1 - / \ / \ - totals cap0 cap1 = sum of all cap going up into (+) from atoms or ChargeStruct - to (+): flow0 flow1= sum of all flow going up into (+) - Delta0 Delta1 = st_cap(+)-st_flow(+) before connection - Observations - ============ - A. Any Delta > 0 on (+) or (-) group decreases total (signed) charge by Delta - - B. Any alt path from an atom through ChargeStruct to an atom - does not change the total charge - - C. st_flow(+/-) = cap(+)-flow(+)-Delta(+) + cap(-)-flow(-)-Delta(-) = - = charge(+) + |max (-) charge| + charge(-) = const - (charge conservation) - - D. To decrease total charge: increase st_cap on (+) or (-) group, including supergroup - E. To increase total charge: increase st_cap on any (y) or (v)-connecting vertex - - F. To cancel charges: - 1. Forbid (+/-)-(+) or (+/-)-(-) edge - 2. Add delta>0 to (+/-) st_cap - 3. Add same delta to (+) or (-) st_cap - - -****************************************************************/ - -/************************************************************************************ - j2,j3 < j1 < j0 - - (+/-) <---- next step; if does not exist then - / \ st_cap1' := st_flow1' - / \ - / \ st_cap1' := cap01' - pv1 (+)super st_flow1':= cap01'-flow01' = flow02'+flow03' - j1 | - | cap01' := st_cap0' - | flow01':= st_cap0'-flow02'-flow03' - | - | st_cap0' := - ( ) pv0,j0 st_flow0':= cap2+st_cap3 - / \ - / \ cap03' = cap3 - / \ flow03' = cap3 - flow3 - Delta3 - / \ - st_cap2, st_flow2 (+) (+C) st_cap3' := cap3 - pv2,j2 pv3,j3 st_flow3' := cap3-Delta3 - / \ / \ Delta3 := st_cap3 - st_flow3 - cap2, flow2 cap3, flow3 = sums of incoming - **************************************************************************************/ - -int ConnectSuperCGroup( int nSuperCGroup, int nAddGroups[], int num_add, - int *pcur_num_vertices, int *pcur_num_edges, - int *tot_st_cap, int *tot_st_flow, - BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups ) -{ - BNS_EDGE **e0X = NULL, *e; - BNS_VERTEX **pvX = NULL, *pv0=NULL, *pv1=NULL, *pv=NULL; - int *jX = NULL, *iX = NULL; - int i, j, num_groups, j0, i1, j1, iXX, ret = 0, fst=0; - int cur_num_vertices = *pcur_num_vertices; - int cur_num_edges = *pcur_num_edges; - - if ( nSuperCGroup >= 0 ) { - i1 = pTCGroups->nGroup[nSuperCGroup]; /* the supergroup */ - if ( i1 < 0 ) - return 0; - } else { - i1 = -1; - fst = 1; - } - - for ( i = num_groups = 0; i < num_add; i ++ ) { - iXX = pTCGroups->nGroup[nAddGroups[i]]; - num_groups += (iXX >= 0 && iXX != i1); - } - if ( num_groups < 1 ) { /* Y connect only 2 or more groups; V connects even 1 group */ - return 0; - } - - e0X = (BNS_EDGE **)inchi_calloc( num_groups + 1, sizeof(e0X[0]) ); - pvX = (BNS_VERTEX **)inchi_calloc( num_groups + 1, sizeof(pvX[0]) ); - jX = (int *)inchi_calloc( num_groups + 1, sizeof(jX[0]) ); - iX = (int *)inchi_calloc( num_groups + 1, sizeof(iX[0]) ); - if ( !e0X || !pvX || !jX || !iX ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - /* create vert_ficpoint -- central Y-connection vertex */ - j0 = cur_num_vertices; - pv0 = pBNS->vert + j0; /* center of the Y-connection; has number j0 */ - pv0->iedge = (pv0 - 1)->iedge + (pv0 - 1)->max_adj_edges; - pv0->max_adj_edges = num_groups + 1 + BNS_ADD_EDGES; /* Y-connection num. edges */ - pv0->num_adj_edges = 0; /* nothing connected yet */ - pv0->type = BNS_VT_YVCONNECTOR; - cur_num_vertices ++; - - if ( fst == 0 ) { - /* find super c-group vertex pv1, number j1 */ - jX[0] = j1 = pTCGroups->pTCG[i1].nVertexNumber; - iX[0] = i1; - pvX[0] = pv1 = pBNS->vert + j1; - } - /* find other c-group vertices */ - for( i = 0, j = 1; i < num_add; i ++ ) { - iXX = pTCGroups->nGroup[nAddGroups[i]]; - if ( (iXX >= 0) && (iXX != i1) ) { - iX[j] = iXX; - jX[j] = pTCGroups->pTCG[iXX].nVertexNumber; - pvX[j] = pBNS->vert + jX[j]; - j ++; - } - } - - /* grab (num_groups+1) free edges */ - for ( i = fst; i <= num_groups; i ++ ) { - e = e0X[i] = pBNS->edge + cur_num_edges; - pv = pvX[i]; - j = jX[i]; - iXX = iX[i]; - /* connect all to pv0 */ - ret = ConnectTwoVertices( pv0, pv, e, pBNS, 1 ); - if ( IS_BNS_ERROR( ret ) ) { - goto exit_function; - } - if ( i ) { - /* from c-group to central Y-connecting vertex of from supergroup to (+/-) vertex */ - pTCGroups->pTCG[iX[i]].nForwardEdge = cur_num_edges; - } else { - /* from central Y-connecting vertex to supergroup */ - pTCGroups->pTCG[iX[i]].nBackwardEdge = cur_num_edges; - } - cur_num_edges ++; - } - /* set flow and cap for incoming into pv0 edges */ - for ( i = 1; i <= num_groups; i ++ ) { - int nDelta = pTCGroups->pTCG[iX[i]].st_cap - pTCGroups->pTCG[iX[i]].edges_cap; - int edge_cap = pTCGroups->pTCG[iX[i]].edges_cap + nDelta; /* added nDelta */ - int edge_flow = pTCGroups->pTCG[iX[i]].edges_cap-pTCGroups->pTCG[iX[i]].edges_flow /*-nDelta*/; - ret = AddEdgeFlow( edge_cap, edge_flow, - e0X[i], pvX[i]/*src*/, pv0 /* dest*/, tot_st_cap, tot_st_flow ); - if ( IS_BNS_ERROR( ret ) ) { - goto exit_function; - } - } - if ( fst == 0 ) { - /* set flow and cap for going out of pv0 and into pv1 edge */ - int edge_cap = pv0->st_edge.cap; - int edge_flow = pv0->st_edge.cap - pv0->st_edge.flow; - ret = AddEdgeFlow( pv0->st_edge.cap, pv0->st_edge.cap - pv0->st_edge.flow, - e0X[0], pv0/*src*/, pv1 /* dest*/, tot_st_cap, tot_st_flow ); - if ( IS_BNS_ERROR( ret ) ) { - goto exit_function; - } - pTCGroups->pTCG[iX[0]].edges_cap += edge_cap; - pTCGroups->pTCG[iX[0]].edges_flow += edge_flow; - pTCGroups->pTCG[iX[0]].st_cap += edge_cap; - pTCGroups->pTCG[iX[0]].st_flow += edge_flow; - } else { - /* no supergroup => change cap to flow */ - *tot_st_cap += pv0->st_edge.flow - pv0->st_edge.cap; - pv0->st_edge.cap += pv0->st_edge.flow - pv0->st_edge.cap; - pv0->st_edge.cap0 = pv0->st_edge.cap; - } - - *pcur_num_vertices = cur_num_vertices; - *pcur_num_edges = cur_num_edges; - ret = num_groups; -exit_function: - if ( e0X ) inchi_free( e0X ); - if ( pvX ) inchi_free( pvX ); - if ( jX ) inchi_free( jX ); - if ( iX ) inchi_free( iX ); - return ret; -} -/*********************************************************************************/ -void AddStCapFlow( BNS_VERTEX *vert_ficpoint, int *tot_st_flow, int *tot_st_cap, int cap, int flow ) -{ - vert_ficpoint->st_edge.flow += flow; - *tot_st_flow += flow; - vert_ficpoint->st_edge.cap += cap; - *tot_st_cap += cap; - - vert_ficpoint->st_edge.flow0 = vert_ficpoint->st_edge.flow; - vert_ficpoint->st_edge.cap0 = vert_ficpoint->st_edge.cap; -} -/*********************************************************************************/ -void SetStCapFlow( BNS_VERTEX *vert_ficpoint, int *tot_st_flow, int *tot_st_cap, int cap, int flow ) -{ - *tot_st_flow += flow - vert_ficpoint->st_edge.flow; - vert_ficpoint->st_edge.flow = flow; - *tot_st_cap += cap - vert_ficpoint->st_edge.cap; - vert_ficpoint->st_edge.cap = cap; - - vert_ficpoint->st_edge.flow0 = vert_ficpoint->st_edge.flow; - vert_ficpoint->st_edge.cap0 = vert_ficpoint->st_edge.cap; -} - -/********************************************************************************* -int AddCGroups2TCGBnStruct( BN_STRUCT *pBNS, StrFromINChI *pStruct, - VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups ) - - - *********************************************************************************/ -int AddCGroups2TCGBnStruct( BN_STRUCT *pBNS, StrFromINChI *pStruct, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, int nMaxAddEdges ) -{ - int ret = 0, ret1, ret2, ret3, bNeedsFlower; - inp_ATOM *at = pStruct->at; - int num_atoms = pStruct->num_atoms; - /*int num_tg = pTCGroups->num_tgroups;*/ - int num_cg = pTCGroups->num_tc_groups - pTCGroups->num_tgroups; - int fst_cg_vertex = pBNS->num_vertices; - int fst_cg_group = pTCGroups->num_tgroups; - int num_vertices = pBNS->num_vertices; - int num_edges = pBNS->num_edges; - int cg_charge = 0; - ICHICONST SRM *pSrm = pStruct->pSrm; - /* ret = ReInitBnStruct( pBNS ); */ - if ( num_cg > 0 ) { - /* if ( cgi && cgi->num_c_groups && cgi->c_group ) */ - int i, i1, i2, j, j1, j2, k, k1, k2, n, c_point, c_neigh, cap, flow; - int cur_num_vertices, cur_num_edges; - BNS_VERTEX *vert_ficpoint, *vert_ficpoint_prev, *vert_ficpoint_base; /* fictitious vertex describing charge c-group */ - BNS_VERTEX *pv1, *pv2; - BNS_EDGE *edge; /* edge between that vertex and the tautomeric c_point */ - int nMaxCGroupNumber = 0; - MY_CONST C_NODE *pCN; - int cn_len, cn_bits, bMetalAtoms; - int type; - int tot_st_cap, tot_st_flow; - int nAddGroups[16]; - - - /* Debug: check overflow */ - if ( num_vertices >= pBNS->max_vertices ) { - return BNS_VERT_EDGE_OVFL; - } - nMaxCGroupNumber = num_cg; - /* clear all vertices not used until now */ - memset( pBNS->vert+num_vertices, 0, (pBNS->max_vertices - num_vertices)*sizeof(pBNS->vert[0]) ); - tot_st_cap = pBNS->tot_st_cap; - tot_st_flow = pBNS->tot_st_flow; - /*****************************************/ - /* initialize new fictitious vertices */ - /* representing c-point groups, c-groups */ - /*****************************************/ - vert_ficpoint_prev = pBNS->vert + fst_cg_vertex - 1; - - for ( i = 0; i < num_cg; i ++ ) { - /* - vert_ficpoint-1 is the last vertex; - vert_ficpoint is the being added vertex - Note: nGroupNumber are not contiguous - */ - vert_ficpoint = vert_ficpoint_prev + 1; - vert_ficpoint->iedge = vert_ficpoint_prev->iedge + vert_ficpoint_prev->max_adj_edges; - vert_ficpoint->max_adj_edges = pTCGroups->pTCG[i+fst_cg_group].num_edges+nMaxAddEdges; - vert_ficpoint->num_adj_edges = 0; - - vert_ficpoint->st_edge.flow += pTCGroups->pTCG[i+fst_cg_group].st_flow; - tot_st_flow += pTCGroups->pTCG[i+fst_cg_group].st_flow; - vert_ficpoint->st_edge.cap += pTCGroups->pTCG[i+fst_cg_group].st_cap; - tot_st_cap += pTCGroups->pTCG[i+fst_cg_group].st_cap; - - vert_ficpoint->st_edge.flow0 = vert_ficpoint->st_edge.flow; - vert_ficpoint->st_edge.cap0 = vert_ficpoint->st_edge.cap; - - vert_ficpoint->type = pTCGroups->pTCG[i+fst_cg_group].type; - /* save the vertex number */ - pTCGroups->pTCG[i+fst_cg_group].nVertexNumber = vert_ficpoint - pBNS->vert; - - vert_ficpoint_prev = vert_ficpoint; /* keep track of iedges */ - } - cur_num_vertices = (vert_ficpoint_prev - pBNS->vert) + 1; - cur_num_edges = num_edges; - - /*************************************************************/ - /* pass 1: */ - /* create ChargeStruct for c-points and connect them to */ - /* the vertices representing c-point groups; */ - /* set final atom st_cap, st_flow */ - /*************************************************************/ - for ( c_point = 0; c_point < num_atoms; c_point ++ ) { - if ( !(k=pVA[c_point].cnListIndex) ) - continue; /* not a c-point */ - k --; - pCN = cnList[k].pCN; /* pointer to the ChargeStruct */ - cn_len = cnList[k].len; /* length of the ChargeStruct */ - cn_bits = cnList[k].bits; /* bits: for M-recognition */ - /* cn_bits = cnList[k].bits; */ /* ChargeStruct type */ - bMetalAtoms = (cn_bits == cn_bits_Me); - vert_ficpoint_base = vert_ficpoint_prev; /* add aux vertices after this */ - /* create disconnected auxiliary vertices of the at[c_point] ChargeStruct; add to them st_flow & st_cap */ - for ( i1 = 0; i1 < cn_len; i1 ++ ) { - if ( !IS_BNS_VT_CHRG_STRUCT(pCN[i1].v.type) ) { - continue; - } - /* the atom is always the first; the attached c-points are always the last */ - vert_ficpoint = vert_ficpoint_base + i1; /* i1 = 1, 2,.. less number of attached c-points */ - vert_ficpoint->iedge = vert_ficpoint_prev->iedge + vert_ficpoint_prev->max_adj_edges; - vert_ficpoint->max_adj_edges = pCN[i1].v.valence; /* do not add additional edges to aux vertices */ - vert_ficpoint->num_adj_edges = 0; - - cap = !bMetalAtoms? pCN[i1].v.cap : pCN[i1].v.cap? pSrm->nMetalMaxCharge_D : 0; - flow = !bMetalAtoms? pCN[i1].v.flow : pCN[i1].v.flow? pSrm->nMetalMaxCharge_D : 0; - - AddStCapFlow( vert_ficpoint, &tot_st_flow, &tot_st_cap, cap, flow ); - vert_ficpoint->type = pCN[i1].v.type; /* =BNS_VERT_TYPE__AUX */ - - vert_ficpoint_prev = vert_ficpoint; /* the last one will be vert_ficpoint for the next c-point */ - cur_num_vertices = (vert_ficpoint - pBNS->vert) + 1; - - if ( vert_ficpoint->iedge + vert_ficpoint->max_adj_edges - pBNS->iedge >= pBNS->max_iedges ) { - return BNS_VERT_EDGE_OVFL; - } - if ( cur_num_vertices >= pBNS->max_vertices ) { - return BNS_VERT_EDGE_OVFL; - } - } - /* connect the vertices with new edges, add edge flow and cap */ - for ( i1 = 0; i1 < cn_len; i1 ++ ) { - pv1 = NULL; - k1 = -1; - /* find vertex cooresponding to i1 */ - if ( pCN[i1].v.type & BNS_VERT_TYPE_ATOM ) { - pv1 = pBNS->vert+c_point; /* may be only one atom -- the current c_point at i1==0 */ - /* add atom vertex st_cap and st_flow */ - cap = !bMetalAtoms? pCN[i1].v.cap : pCN[i1].v.cap? pSrm->nMetalMaxCharge_D : 0; - flow = !bMetalAtoms? pCN[i1].v.flow : pCN[i1].v.flow? pSrm->nMetalMaxCharge_D : 0; - AddStCapFlow( pv1, &tot_st_flow, &tot_st_cap, cap, flow ); - } else - if ( IS_BNS_VT_C_GR(pCN[i1].v.type) ) { - /* find c-group vertex by looking for its type */ - for( j = 0; j < num_cg; j ++ ) { - if ( pCN[i1].v.type == pBNS->vert[fst_cg_vertex + j].type ) { - pv1 = pBNS->vert + fst_cg_vertex + j; - break; - } - } - /* index of the pTCGroups->pTCG[] */ - if ( pv1 ) { - k1 = j + fst_cg_group; - if ( pTCGroups->pTCG[k1].type != pCN[i1].v.type || - pTCGroups->pTCG[k1].ord_num ) { - return RI_ERR_PROGR; - } - } - } else - if ( IS_BNS_VT_M_GR( pCN[i1].v.type ) ) { - k1 = pTCGroups->nGroup[TCG_MeFlower0]; - if ( k1 < 0 || - pTCGroups->pTCG[k1].type != pCN[i1].v.type || - pTCGroups->pTCG[k1].ord_num || - !pSrm->bMetalAddFlower ) { - return RI_ERR_PROGR; - } - pv1 = pBNS->vert + pTCGroups->pTCG[k1].nVertexNumber; - } else - if ( IS_BNS_VT_CHRG_STRUCT(pCN[i1].v.type) ) { - /* aux vertex */ - pv1 = vert_ficpoint_base + i1; - } - if ( !pv1 ) { - return BNS_BOND_ERR; - } - - /* connect pairs of vertices with new edges */ - for ( k = 0; k < MAX_CN_VAL && (i2=pCN[i1].e[k].neigh); k ++ ) { - pv2 = NULL; - k2 = -1; - i2 --; /* neighbor */ - /* find vertex cooresponding to i2 */ - if ( pCN[i2].v.type & BNS_VERT_TYPE_ATOM ) { - pv2 = pBNS->vert+c_point; - cap = !bMetalAtoms? pCN[i2].v.cap : pCN[i2].v.cap? pSrm->nMetalMaxCharge_D : 0; - flow = !bMetalAtoms? pCN[i2].v.flow : pCN[i2].v.flow? pSrm->nMetalMaxCharge_D : 0; - /* add atom vertex st_cap and st_flow; this normally should not happen */ - AddStCapFlow( pv2, &tot_st_flow, &tot_st_cap, cap, flow ); - } else - if ( IS_BNS_VT_C_GR(pCN[i2].v.type) ) { - /* find c-group vertex by looking for its type */ - for( j = 0; j < num_cg; j ++ ) { - if ( pCN[i2].v.type == pBNS->vert[fst_cg_vertex + j].type ) { - pv2 = pBNS->vert + fst_cg_vertex + j; - break; - } - } - if ( pv2 ) { - k2 = j + fst_cg_group; - if ( pTCGroups->pTCG[k2].type != pCN[i2].v.type || - pTCGroups->pTCG[k2].ord_num ) { - return RI_ERR_PROGR; - } - } - } else - if ( IS_BNS_VT_M_GR( pCN[i2].v.type ) ) { - k2 = pTCGroups->nGroup[TCG_MeFlower0]; - if ( k2 < 0 || - pTCGroups->pTCG[k2].type != pCN[i2].v.type || - pTCGroups->pTCG[k2].ord_num || - !pSrm->bMetalAddFlower ) { - return RI_ERR_PROGR; - } - pv2 = pBNS->vert + pTCGroups->pTCG[k2].nVertexNumber; - } else - if ( IS_BNS_VT_CHRG_STRUCT(pCN[i2].v.type) ){ - pv2 = vert_ficpoint_base + i2; - } - - /* connect pv1 and pv2 */ - if ( !pv1 || !pv2 || pv1 == pv2 ) { - return BNS_BOND_ERR; - } - j1 = pv1 - pBNS->vert; - j2 = pv2 - pBNS->vert; - /* create a new edge connecting pv1 and pv2 */ - edge = pBNS->edge + cur_num_edges; - if ( IS_BNS_VT_M_GR( pCN[i1].v.type ) && IS_BNS_VT_ATOM( pCN[i2].v.type ) || - IS_BNS_VT_M_GR( pCN[i2].v.type ) && IS_BNS_VT_ATOM( pCN[i1].v.type ) ) { - /* at[c_point] is a metal or is treated as a metal; connect it to M-group */ - /* metal - M-group (i.e. Metal-Flower) edge */ - int nStCap, nStFlow; - bNeedsFlower = AtomStcapStflow( at, pVA, pSrm, c_point, &nStCap, &nStFlow, &edge->cap, &edge->flow ); - /* GetAtomToMCGroupInitEdgeCapFlow( &edge->cap, &edge->flow, pSrm, at, pVA, c_point ); */ - if ( !bNeedsFlower ) { - return RI_ERR_PROGR; - } - pVA[c_point].nMetalGroupEdge = cur_num_edges + 1; - /* pBNS->vert[c_point].st_edge.cap += edge->flow;*/ /* where was this done ???*/ - pBNS->vert[c_point].st_edge.flow += edge->flow; - pBNS->vert[c_point].st_edge.cap += edge->flow + pVA[c_point].cInitFreeValences; - pBNS->vert[c_point].st_edge.flow0 = pBNS->vert[c_point].st_edge.flow; - pBNS->vert[c_point].st_edge.cap0 = pBNS->vert[c_point].st_edge.cap; - tot_st_flow += edge->flow; - tot_st_cap += edge->flow + pVA[c_point].cInitFreeValences; - } else { - edge->cap = !bMetalAtoms? pCN[i1].e[k].cap : pCN[i1].e[k].cap? pSrm->nMetalMaxCharge_D : 0; - edge->flow = !bMetalAtoms? pCN[i1].e[k].flow : pCN[i1].e[k].flow? pSrm->nMetalMaxCharge_D : 0; - } - edge->forbidden = pCN[i1].e[k].bForbiddenEdge? BNS_EDGE_FORBIDDEN_MASK : 0; - /* c-group incoming edges cap and flow needed in ConnectSuperCGroup() */ - /* - if ( k1 >= 0 ) { - pTCGroups->pTCG[k1].edges_cap += pCN[i1].e[k].cap; - pTCGroups->pTCG[k1].edges_flow += pCN[i1].e[k].flow; - } - if ( k2 >= 0 ) { - pTCGroups->pTCG[k2].edges_cap += pCN[i1].e[k].cap; - pTCGroups->pTCG[k2].edges_flow += pCN[i1].e[k].flow; - } - */ - edge->pass = 0; -#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) - edge->forbidden &= pBNS->edge_forbidden_mask; -#endif - /* check edge overflow */ - if ( pv1->num_adj_edges >= pv1->max_adj_edges || - pv2->num_adj_edges >= pv2->max_adj_edges || - cur_num_edges >= pBNS->max_edges ) { - return BNS_VERT_EDGE_OVFL; - } - - /* connect edge to the incident vertices and increment the counters of neighbors and edges */ - ret = ConnectTwoVertices( pv1, pv2, edge, pBNS, 0 ); - if ( IS_BNS_ERROR( ret ) ) { - return ret; - } - edge->cap0 = edge->cap; - edge->flow0 = edge->flow; - /* save the edge index */ - type = IS_BNS_VT_C_GR(pv1->type)? pv1->type : - IS_BNS_VT_C_GR(pv2->type)? pv2->type : 0; - if ( type ) { - /* the edge connects to a c-group */ - if ( type & BNS_VERT_TYPE_C_NEGATIVE ) { - pVA[c_point].nCMinusGroupEdge = cur_num_edges+1; - } else { - pVA[c_point].nCPlusGroupEdge = cur_num_edges+1; - } - } - cur_num_edges ++; /* end of new edge creation */ - } - } - } - /*************************************************************/ - /* pass 2: */ - /* adjust bond cap, flow from the final atom st_cap, st_flow */ - /*************************************************************/ - for ( c_point = 0; c_point < num_atoms; c_point ++ ) { - int st_cap, st_cap2, max_edge_flow; - pv1 = pBNS->vert + c_point; /* atom vertex */ - st_cap = pv1->st_edge.cap; - for ( k = 0; k < pv1->num_adj_edges; k ++ ) { - edge = pBNS->edge + pv1->iedge[k]; /* incident edge */ - c_neigh = edge->neighbor12 ^ c_point; /* adjacent vertex */ - pv2 = pBNS->vert + c_neigh; - if ( c_neigh > c_point || !(pv2->type & BNS_VERT_TYPE_ATOM) ) { - continue; - } - /* adjacent vertex is an atom; the edge is a bond; process each bond only once */ - st_cap2 = pv2->st_edge.cap; - /* the edge flow <= min( incident atom st_caps) */ - max_edge_flow = inchi_min( st_cap, st_cap2 ); - /* bond order <= triple bond (flow=2) */ - if ( pSrm->bMetalAddFlower && !pSrm->nMetalMinBondOrder && - (pVA[c_point].cMetal && pVA[c_point].cNumBondsToMetal || - pVA[c_neigh].cMetal && pVA[c_neigh].cNumBondsToMetal) ) { - max_edge_flow = inchi_min( max_edge_flow, MAX_BOND_EDGE_CAP+1 ); - } else { - max_edge_flow = inchi_min( max_edge_flow, MAX_BOND_EDGE_CAP ); - } - if ( at[c_point].bond_type[k] == BOND_TYPE_SINGLE ) { - /* the bond has not been changed due to stereo */ - edge->cap = edge->cap0 = max_edge_flow; - } - } - } - /***********************************************************/ - /************** ************/ - /************** connect M-flower with new edges ************/ - /************** ************/ - /***********************************************************/ - ret = ConnectMetalFlower(&cur_num_vertices, &cur_num_edges, &tot_st_cap, &tot_st_flow, pSrm, pBNS, pTCGroups); - if ( ret < 0 ) { - goto exit_function; - } - /***********************************************************/ - /************** ************/ - /************** add additional vertices & edges ************/ - /************** to connect c-groups ************/ - /************** ************/ - /***********************************************************/ - /* (+) supergroup, Y-connection */ - k = 0; - nAddGroups[k ++] = TCG_Plus0; - nAddGroups[k ++] = TCG_Plus_C0; - nAddGroups[k ++] = TCG_Plus_M0; - ret1 = ConnectSuperCGroup( TCG_Plus, nAddGroups, k, &cur_num_vertices, &cur_num_edges, - &tot_st_cap, &tot_st_flow, pBNS, pTCGroups ); - /* (-) supergroup, Y-connection */ - k = 0; - nAddGroups[k ++] = TCG_Minus0; - nAddGroups[k ++] = TCG_Minus_C0; - nAddGroups[k ++] = TCG_Minus_M0; - ret2 = ConnectSuperCGroup( TCG_Minus, nAddGroups, k, &cur_num_vertices, &cur_num_edges, - &tot_st_cap, &tot_st_flow, pBNS, pTCGroups ); - /******** connect (+) and (-) ***************/ - k = 0; - nAddGroups[k ++] = TCG_Plus; - nAddGroups[k ++] = TCG_Minus; - ret3 = ConnectSuperCGroup( -1, nAddGroups, k, &cur_num_vertices, &cur_num_edges, - &tot_st_cap, &tot_st_flow, pBNS, pTCGroups ); - - /* Take care of the full charge */ - cg_charge = pTCGroups->total_charge - pTCGroups->tgroup_charge - pTCGroups->charge_on_atoms; - ret = 1; - if ( ret3 > 0 ) { - /* (+) and (-) or at least one of them have been connected */ - int nVertPlusMinus = cur_num_vertices - 1; - BNS_VERTEX *pVertPlusMinus = pBNS->vert + nVertPlusMinus; - BNS_VERTEX *pVertPlus = NULL, *pVertMinus = NULL, *pVert=NULL; - BNS_EDGE *pEdgePlus = NULL, *pEdgeMinus = NULL, *pEdge=NULL; - n = pTCGroups->nGroup[TCG_Plus] >= 0; /* (+)-supergroup exists */ - k = pTCGroups->nGroup[TCG_Minus] >= 0; /* (-)-supergroup exists */ - if ( pVertPlusMinus->num_adj_edges == 2 && k+n==2 ) { - pEdgePlus = pBNS->edge + pVertPlusMinus->iedge[0]; /* TCG_Plus was the 1st */ - pEdgeMinus = pBNS->edge + pVertPlusMinus->iedge[1]; /* TCG_Minus was the 2nd */ - } else - if ( pVertPlusMinus->num_adj_edges == 1 && k+n==1 ) { - if ( pTCGroups->nGroup[TCG_Plus] >= 0 ) { - pEdgePlus = pBNS->edge + pVertPlusMinus->iedge[0]; - } else - if ( pTCGroups->nGroup[TCG_Minus] >= 0 ) { - pEdgeMinus = pBNS->edge + pVertPlusMinus->iedge[0]; - } - } else - if ( k+n ) { - /* program error check */ - ret = BNS_BOND_ERR; - goto exit_function; - } - if ( pEdgePlus ) { - pVertPlus = pBNS->vert + (pEdgePlus->neighbor12 ^ nVertPlusMinus); - } - if ( pEdgeMinus ) { - pVertMinus = pBNS->vert + (pEdgeMinus->neighbor12 ^ nVertPlusMinus); - } - pVert = pVertPlus? pVertPlus : pVertMinus? pVertMinus : NULL; - pEdge = pEdgePlus? pEdgePlus : pEdgeMinus? pEdgeMinus : NULL; - if ( pEdgeMinus ) { - pTCGroups->nEdgeMinus = pEdgeMinus - pBNS->edge; - } - if ( pEdgePlus ) { - pTCGroups->nEdgePlus = pEdgePlus - pBNS->edge; - } - if ( pEdge ) { - pTCGroups->nEdge4charge = pEdge - pBNS->edge; - } - /* set total charge */ - if ( pVert && pEdge ) { - /* do not check rescaps for now */ - if ( cg_charge > 0 ) { - pVertPlusMinus->st_edge.cap += cg_charge; - tot_st_cap += cg_charge; - pVertPlusMinus->st_edge.cap0 = pVertPlusMinus->st_edge.cap; - } - if ( cg_charge < 0 ) { - pVert->st_edge.cap -= cg_charge; - tot_st_cap -= cg_charge; - pVert->st_edge.cap0 = pVert->st_edge.cap; - - if ( pEdge->cap - pEdge->flow + cg_charge < 0 ) { - /* 2006-02-06: increase edge capacity to avoid clogging */ - pEdge->cap = pEdge->flow - cg_charge; - } - } - pTCGroups->added_charge = cg_charge; - - } - if ( !cg_charge || (pVert && pEdge) ) { - ret = 2; - } - } - - AddRadicalToMetal( &tot_st_cap, &tot_st_flow, pSrm, pBNS, pTCGroups ); - - - pBNS->num_edges = cur_num_edges; - pBNS->num_vertices = cur_num_vertices; - pBNS->num_c_groups = num_cg; - pBNS->tot_st_cap = tot_st_cap; - pBNS->tot_st_flow = tot_st_flow; - - } -exit_function: - return ret; -} -/********************************************************************************/ -int nNumEdgesToCnVertex( MY_CONST C_NODE *pCN, int len, int v ) -{ - int i, j, n, num_edges, v1 = v+1; - for ( i = 0, num_edges = 0; i < len; i ++ ) { - for ( j = 0; j < MAX_CN_VAL && (n = pCN[i].e[j].neigh); j ++ ) { - num_edges += ( i == v || n == v1 ); - } - } - return num_edges; -} - -/********************************************************************************* -BN_STRUCT* AllocateAndInitTCGBnStruct( StrFromINChI *pStruct, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, - int nMaxAddAtoms, int nMaxAddEdges, - int max_altp, int *pNum_changed_bonds ) -allocate BN_STRUCT that has: - - pBNS->max_vertices = pTCGroups->nVertices + nMaxAddAtoms - pBNS->max_edges = pTCGroups->nEdges + - pBNS->max_vertices * (nMaxAddEdges + NUM_KINDS_OF_GROUPS) - pBNS->max_iedges = 2*pBNS->max_edges + pTCGroups->nAddIedges - - pBNS->len_alt_path = pBNS->max_vertices + iALTP_HDR_LEN + 1 + - max( pBNS->max_vertices/2, 16 ) - pBNS->max_altp = max_altp - -other members: - - pBNS->num_atoms = num_atoms; - pBNS->num_bonds = num_bonds; - pBNS->num_added_atoms = 0; - pBNS->num_t_groups = 0; - pBNS->num_c_groups = 0; - pBNS->nMaxAddAtoms = nMaxAddAtoms; - pBNS->nMaxAddEdges = nMaxAddEdges; - -atom vertices and bond edges: - --- vertex(atom) --- - st_cap = (at[].chem_bonds_valence - at[].valence) + pVA[].cInitFreeValences - st_flow = SUM{bond_orders; ALT_BOND counted as SINGLE} - at[].valence - --- edge(bond) --- - flow = bond_order - 1; for ALT_BOND flow = 0 - cap = min(min(st_cap of neighbors),2); for ALT_BOND cap = 1 - max number of edges per atom = number of bonds + - number of edges to ChargeStruct + - 1 (if atom is a tautomeric endpoint) + - nMaxAddEdges - --- NOTE --- - Here are not included nDelta(dots) from ChargeStruct and flow to ChargeStruct - - *********************************************************************************/ -BN_STRUCT* AllocateAndInitTCGBnStruct( StrFromINChI *pStruct, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, - int nMaxAddAtoms, int nMaxAddEdges, - int max_altp, int *pNum_changed_bonds ) -{ - inp_ATOM *at = pStruct->at; - int num_atoms = pStruct->num_atoms; - ICHICONST SRM *pSrm = pStruct->pSrm; - - BN_STRUCT *pBNS = NULL; - BNS_VERTEX *vert; - BNS_IEDGE *iedge; - - int neigh, num_changed_bonds=0; - U_CHAR bond_type, bond_mark; - int bNeedsFlower1, bNeedsFlower2, min_order; - - int i, j, k, m, n_edges, num_bonds, num_edges; - int f1, f2, c1, c2, edge_cap, edge_flow, st_cap, st_flow, flag_alt_bond; - int tot_st_cap, tot_st_flow; - int max_tg, max_edges, max_vertices, len_alt_path, max_iedges, num_iedges, num_altp; - - /* count vertices */ - max_tg = pTCGroups->num_tgroups; - /* +1 for a super-tautomeric group */ - /* max_vertices = num_atoms + nMaxAddAtoms + max_tg + 1; */ - max_vertices = pTCGroups->nVertices + nMaxAddAtoms; - - /* count edges */ - num_changed_bonds = 0; - num_bonds = pTCGroups->num_bonds; - - /* each atom has enough edges to belong to a tautomeric group + nMaxAddEdges */ - /* number of atoms is large enough to accommodate max. possible number of t-groups + nMaxAddAtoms */ - /* max_altp cannot be larger than BN_MAX_ALTP = 16 */ - num_edges = pTCGroups->nEdges; - /* +max_tg for edges between t-groups and super-tautomeric group */ - max_edges = num_edges + (nMaxAddEdges + NUM_KINDS_OF_GROUPS)*max_vertices; - max_iedges = 2*max_edges + pTCGroups->nAddIedges; - len_alt_path = max_vertices+iALTP_HDR_LEN + 1; /* may overflow if an edge is traversed in 2 directions */ - len_alt_path += inchi_max( max_vertices/2, 16 ); /* to avoid the overflow */ - - if ( !( pBNS = (BN_STRUCT *)inchi_calloc( 1, sizeof(BN_STRUCT)) ) || - !( pBNS->edge = (BNS_EDGE *)inchi_calloc( max_edges, sizeof(BNS_EDGE)) ) || - !( pBNS->vert = (BNS_VERTEX *)inchi_calloc( max_vertices,sizeof(BNS_VERTEX)) ) || - !( pBNS->iedge = (BNS_IEDGE *)inchi_calloc( max_iedges, sizeof(BNS_IEDGE)) ) ) { - return DeAllocateBnStruct( pBNS ); - } - /* alt path init (standard spell) */ - for ( num_altp = 0; num_altp < max_altp && num_altp < BN_MAX_ALTP; num_altp ++ ) { - if ( !( pBNS->altp[num_altp] = (BNS_ALT_PATH*)inchi_calloc( len_alt_path,sizeof(BNS_ALT_PATH))) ) { - return DeAllocateBnStruct( pBNS ); - } - ALTP_ALLOCATED_LEN(pBNS->altp[num_altp]) = len_alt_path; - pBNS->len_alt_path = len_alt_path; /* ??? duplication ??? */ - /* re-init */ - ALTP_DELTA(pBNS->altp[num_altp]) = 0; - ALTP_START_ATOM(pBNS->altp[num_altp]) = NO_VERTEX; - ALTP_END_ATOM(pBNS->altp[num_altp]) = NO_VERTEX; - ALTP_PATH_LEN(pBNS->altp[num_altp]) = 0; - } - pBNS->alt_path = NULL; - pBNS->num_altp = 0; - pBNS->max_altp = num_altp; - - - /* fill vertices (no connectivity) */ - iedge = pBNS->iedge; - num_iedges = 0; - tot_st_cap = tot_st_flow = 0; - for ( i = 0; i < num_atoms; i ++ ) { - /* count edges incident to pBNS->vert[i] */ - k = at[i].valence + (at[i].endpoint != 0) + (nMaxAddEdges /*+ NUM_KINDS_OF_GROUPS*/); - if ( (j = pVA[i].cnListIndex-1) >= 0 ) { - /* add number of neighbors in the ChargeStruct */ - k += nNumEdgesToCnVertex( cnList[j].pCN, cnList[j].len, 0 ); - } - /* set max number of edges for the vertex */ - pBNS->vert[i].max_adj_edges = k; - pBNS->vert[i].iedge = iedge; - iedge += k; - /* add atom vertex cap */ - st_cap = 0; - st_flow = 0; - bNeedsFlower1 = AtomStcapStflow( at, pVA, pSrm, i, &c1, &f1, NULL, NULL ); - /* pVA[i].cNumBondsToMetal = bNeedsFlower1; */ - /* GetAtomStCapFlow( at, pVA, pSrm, i, &c1, &f1 ); */ - st_cap += c1; - st_cap += bNeedsFlower1? 0 : pVA[i].cInitFreeValences; - pBNS->vert[i].st_edge.cap = st_cap; /* the 1st time st_cap is set */ - pBNS->vert[i].st_edge.cap0 = pBNS->vert[i].st_edge.cap; - tot_st_cap += st_cap; - } - num_iedges = iedge - pBNS->iedge; - if ( max_iedges - num_iedges < (nMaxAddEdges + NUM_KINDS_OF_GROUPS)*max_vertices ) { - return DeAllocateBnStruct( pBNS ); - } - - pBNS->num_atoms = num_atoms; /* number of real atoms */ - pBNS->num_added_atoms = 0; - pBNS->num_t_groups = 0; /* number of added t-groups */ - pBNS->num_c_groups = 0; - pBNS->nMaxAddAtoms = nMaxAddAtoms; - pBNS->nMaxAddEdges = nMaxAddEdges; - - pBNS->num_vertices = num_atoms; /* current number of vertices, in general a sum of - pBNS->num_atoms - pBNS->num_t_groups - number of c-groups - number of auxiliary vertices - pBNS->num_added_atoms - */ - pBNS->max_vertices = max_vertices; - - - pBNS->num_bonds = num_bonds; /* number of real edges (bonds) */ - pBNS->max_edges = max_edges; - pBNS->max_iedges = max_iedges; - - - /* - To remove t-groups and added atoms: - - for ( i = 0; i < pBNS->num_atoms; i ++ ) { - for ( j = pBNS->vert[i].num_adj_edges-1; 0 <= j; j -- ) { - k = pBNS->edge[pBNS->vert[i].iedge[j]].neighbor12 ^ i; - if ( pBNS->vert[k].type & BNS_VERT_TYPE_ATOM ) { - pBNS->vert[i].num_adj_edges = j+1; - break; - } - } - } - - pBNS->num_vertices = pBNS->num_atoms; - pBNS->num_edges = pBNS->num_bonds; - pBNS->num_added_atoms = 0; - pBNS->num_t_groups = 0; - pBNS->num_added_edges = 0; - - ALTP_DELTA(pBNS->alt_path) = 0; - ALTP_START_ATOM(pBNS->alt_path) = NO_VERTEX; - ALTP_END_ATOM(pBNS->alt_path) = NO_VERTEX; - ALTP_PATH_LEN(pBNS->alt_path) = 0; - - */ - - - /* add and fill edges and connectivity */ - for ( i = 0, n_edges = 0; i < num_atoms; i ++ ) { - vert = pBNS->vert + i; /* pointer to the ith vertex */ - st_cap = 0; - st_flow = 0; - flag_alt_bond = 0; - for ( j = 0; j < at[i].valence; j ++ ) { - neigh = at[i].neighbor[j]; - /* find this bond at the neighbor */ - for ( k = 0; k < at[neigh].valence; k ++ ) { - if ( at[neigh].neighbor[k] == i ) { - break; - } - } - bond_type = (at[i].bond_type[j] & BOND_TYPE_MASK); - bond_mark = (at[i].bond_type[j] & ~BOND_TYPE_MASK); - if ( bond_type != BOND_SINGLE && bond_type != BOND_DOUBLE && bond_type != BOND_TRIPLE ) { - /* make unknown bonds single */ - bond_type = BOND_SINGLE; - at[i].bond_type[j] = bond_mark | bond_type; - num_changed_bonds ++; - } - if ( neigh > i ) { - /* this is the first time we encounter this bond */ - bNeedsFlower1 = AtomStcapStflow( at, pVA, pSrm, i, &c1, &f1, NULL, NULL ); - /* GetAtomStCapFlow( at, pVA, pSrm, i, &c1, &f1 ); */ - c1 += bNeedsFlower1? 0 : pVA[i].cInitFreeValences; /* elevate cap to the lowest valence in ChargeStruct */ - bNeedsFlower2 = AtomStcapStflow( at, pVA, pSrm, neigh, &c2, &f2, NULL, NULL ); - /* GetAtomStCapFlow( at, pVA, pSrm, neigh, &c2, &f2 ); */ - c2 += bNeedsFlower2? 0 : pVA[neigh].cInitFreeValences; /* elevate cap to the lowest valence in ChargeStruct */ - /* at this point -O would have st_cap=st_flow=0 because the lowest valence=1 for charge=-1 */ - /* however, if -O belongs to a t-group its cap would be 1, flow = 0 */ - /*f1 = MAX_AT_FLOW(at[i]);*/ - /*f2 = MAX_AT_FLOW(at[neigh]);*/ - edge_flow = BondFlowMaxcapMinorder( at, pVA, pSrm, i, j, &edge_cap, &min_order, NULL); - - pBNS->edge[n_edges].neighbor1 = (AT_NUMB)i; - pBNS->edge[n_edges].neighbor12 = (AT_NUMB)(i ^ neigh); - pBNS->edge[n_edges].flow = - pBNS->edge[n_edges].flow0 = edge_flow; - pBNS->edge[n_edges].cap = - pBNS->edge[n_edges].cap0 = edge_cap; - pBNS->edge[n_edges].neigh_ord[0] = j; /* iedge to neigh index at vertex[i], i < neigh */ - pBNS->edge[n_edges].neigh_ord[1] = k; /* iedge to i index at vertex[neigh], i < neigh */ - pBNS->edge[n_edges].pass = 0; - pBNS->edge[n_edges].forbidden = 0; /* may be forbidden if edge_flow = 1: stereogenic fixed double bond */ - if ( bond_type == BOND_TYPE_DOUBLE ) { - /* forbid changing stereogenic double bonds */ - for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[i].sb_parity[m]; m ++ ) { - if ( at[i].sb_ord[m] == j ) { - pBNS->edge[n_edges].forbidden |= BNS_EDGE_FORBIDDEN_MASK; - break; - } - } - } - vert->iedge[j] = pBNS->vert[neigh].iedge[k] = n_edges ++; /* same iedge index as neighbor index in at[] */ - } else { - /* this is the second time we encounter this bond. It was stored at */ - int iedge2 = pBNS->vert[neigh].iedge[k]; - edge_cap = pBNS->edge[iedge2].cap; - edge_flow = pBNS->edge[iedge2].flow; - } - st_flow += edge_flow; - /* - st_cap += edge_cap; - */ - } - vert->num_adj_edges = j; - /* - vert->st_edge.cap = - vert->st_edge.cap0 = st_cap; - */ - vert->st_edge.flow = - vert->st_edge.flow0 = st_flow; - vert->type = BNS_VERT_TYPE_ATOM; - /* - tot_st_cap += vert->st_edge.cap; - */ - tot_st_flow += vert->st_edge.flow; - } - *pNum_changed_bonds = num_changed_bonds/2; - - pBNS->num_edges = n_edges; /* number of edges */ - pBNS->num_iedges = num_iedges; - pBNS->num_added_edges = 0; - - pBNS->tot_st_cap = tot_st_cap; - pBNS->tot_st_flow = tot_st_flow; - -/* exit_function: */ - - return pBNS; -} -/******************************************************************************************************/ -void IncrZeroBondsAndClearEndpts(inp_ATOM *at, int num_at, int iComponent ) -{ - int i, j; - for ( i = 0; i < num_at; i ++ ) { - at[i].endpoint = 0; - at[i].component = iComponent; - for ( j = 0; j < at[i].valence; j ++ ) { - if ( !at[i].bond_type[j] ) { - at[i].bond_type[j] = BOND_TYPE_SINGLE; - at[i].chem_bonds_valence += BOND_TYPE_SINGLE; - } - } - } -} -void IncrZeroBonds(inp_ATOM *at, int num_at, int iComponent ) -{ - int i, j; - for ( i = 0; i < num_at; i ++ ) { - at[i].component = iComponent; - for ( j = 0; j < at[i].valence; j ++ ) { - if ( !at[i].bond_type[j] ) { - at[i].bond_type[j] = BOND_TYPE_SINGLE; - at[i].chem_bonds_valence += BOND_TYPE_SINGLE; - } - } - } -} -void ClearEndpts(inp_ATOM *at, int num_at ) -{ - int i; - for ( i = 0; i < num_at; i ++ ) { - at[i].endpoint = 0; - } -} - - -/******************************************************************************************************/ -#define ANY_VERT_TYPE(X) (((X) & (BNS_VERT_TYPE_ATOM | BNS_VERT_TYPE_TGROUP | BNS_VERT_TYPE_C_GROUP)) && \ - !((X) & (BNS_VERT_TYPE_SUPER_TGROUP))) -#define GRP_VERT_TYPE(X) (((X) & (BNS_VERT_TYPE_TGROUP | BNS_VERT_TYPE_C_GROUP)) && \ - !((X) & (BNS_VERT_TYPE_SUPER_TGROUP))) -typedef struct tagVertexFlow { - int type; - Vertex v; - EdgeIndex e_In; - EdgeIndex e_Out; - EdgeFlow delta_In; - EdgeFlow delta_Out; - Vertex bUsed; /* indicates the charge edge belongs to already processed atom */ -} VF; -#define NUM_VF 3 -#define VF_USED_IN 1 -#define VF_USED_OUT 2 -#define VF_USED_ALL (VF_USED_IN | VF_USED_OUT) - -int GetDeltaChargeFromVF( BN_STRUCT *pBNS, VAL_AT *pVA, VF *vf ); -/******************************************************************************************************/ -int GetDeltaChargeFromVF( BN_STRUCT *pBNS, VAL_AT *pVA, VF *vf ) -{ - int i, v = NO_VERTEX; - int ieIn1 = (!(vf->bUsed & VF_USED_IN) && vf->e_In >= 0 && vf->delta_In )? vf->e_In+1 : NO_VERTEX; - int ieOut1 = (!(vf->bUsed & VF_USED_OUT) && vf->e_Out >= 0 && vf->delta_Out)? vf->e_Out+1 : NO_VERTEX; - int nInitCharge, nPlusFlow, nMinusFlow, nDeltaCharge, nNumDeltaCharge, eCPlus, eCMinus; - - if ( !(vf->type & BNS_VERT_TYPE_C_GROUP) || - (vf->type & BNS_VERT_TYPE_SUPER_TGROUP) || - (ieIn1 == NO_VERTEX && ieOut1 == NO_VERTEX ) ) { - return 0; - } - if ( vf->type & BNS_VERT_TYPE_C_NEGATIVE ) { - /* negative charge edge */ - for ( i = 0; i < pBNS->num_atoms; i ++ ) { - if ( pVA[i].nCMinusGroupEdge == ieIn1 || pVA[i].nCMinusGroupEdge == ieOut1 ) { - v = i; - break; - } - } - } else { - /* positive charge edge */ - for ( i = 0; i < pBNS->num_atoms; i ++ ) { - if ( pVA[i].nCPlusGroupEdge == ieIn1 || pVA[i].nCPlusGroupEdge == ieOut1 ) { - v = i; - break; - } - } - } - if ( v == NO_VERTEX ) - return 0; - - nInitCharge = pVA[v].cInitCharge; - nPlusFlow = nMinusFlow = 0; - nNumDeltaCharge = 0; - - if ( (eCPlus = pVA[v].nCPlusGroupEdge-1) >= 0 ) { - nPlusFlow = pBNS->edge[eCPlus].cap - - pBNS->edge[eCPlus].flow; - } - if ( (eCMinus = pVA[v].nCMinusGroupEdge-1) >= 0 ) { - nMinusFlow = -pBNS->edge[eCMinus].flow; - } - nInitCharge += nPlusFlow + nMinusFlow; - - nDeltaCharge = 0; - - if ( !(vf[0].bUsed & VF_USED_OUT) ) { - if ( vf[0].e_Out==eCPlus || vf[0].e_Out==eCMinus ) { - nDeltaCharge -= vf[0].delta_Out; - vf[0].bUsed |= VF_USED_OUT; - } - } - - if ( !(vf[0].bUsed & VF_USED_IN) ) { - if ( vf[0].e_In==eCPlus || vf[0].e_In==eCMinus ) { - nDeltaCharge -= vf[0].delta_In; - vf[0].bUsed |= VF_USED_IN; - } - } - if ( !nInitCharge && nDeltaCharge ) { - nNumDeltaCharge ++; - } else - if ( nInitCharge && 0 == nInitCharge + nDeltaCharge ) { - nNumDeltaCharge --; - } - return nNumDeltaCharge; -} -/******************************************************************************************************/ -int EvaluateChargeChanges( BN_STRUCT *pBNS, VAL_AT *pVA, int *pnDeltaH, int *pnDeltaCharge, int *pnNumVisitedAtoms ) -{ - int pass, i, j, v0, v1, v2, v, ineigh1, /*ineigh2,*/ vLast, n, delta, ret, ie, err = 0; - BNS_EDGE *edge; - int nDeltaH, nDeltaCharge, iPrev, nInitCharge, nPlusFlow, nMinusFlow; - int nNumDeltaH = 0; - int nNumDeltaCharge = 0; - int nNumVisitedAtoms = 0; - VF vf[NUM_VF+1]; - - *pnDeltaH = 0; - *pnDeltaCharge = 0; - *pnNumVisitedAtoms = 0; - - for ( pass = pBNS->num_altp-1, ret = 0; 0 <= pass; pass -- ) { - - pBNS->alt_path = pBNS->altp[pass]; - v1 = ALTP_START_ATOM(pBNS->alt_path); - n = ALTP_PATH_LEN(pBNS->alt_path); - delta = ALTP_DELTA(pBNS->alt_path); - vLast = ALTP_END_ATOM(pBNS->alt_path); - v0 = v2 = NO_VERTEX; - - memset( vf, 0, sizeof(vf) ); - for ( i = 0; i < (int)(sizeof(vf)/sizeof(vf[0])); i ++ ) { - vf[i].v = NO_VERTEX; /* = -2 */ - vf[i].e_In = NO_VERTEX; - vf[i].e_Out = NO_VERTEX; - } - iPrev = 0; - /* add to the queue */ - if ( ANY_VERT_TYPE(pBNS->vert[v1].type) ) { - if (pBNS->vert[v1].type & BNS_VERT_TYPE_ATOM) { - nNumVisitedAtoms ++; - } - vf[2].type = pBNS->vert[v1].type; - vf[2].v = v1; - iPrev = 2; - } - - nNumDeltaH = 0; - nNumDeltaCharge = 0; - nNumVisitedAtoms = 0; - - for ( i = 0; i < n; i ++, delta = -delta, v0 = v1, v1 = v2 ) { - ineigh1 = ALTP_THIS_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v1->v2 neighbor */ - /*ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i);*/ /* v2->v1 neighbor */ - edge = pBNS->edge + (ie=pBNS->vert[v1].iedge[ineigh1]); - /* follow the BN Structure, not the inp_ATOM, to take care of swithching to - t-groups, c-groups or other fictitious edges/vertices - */ - - if ( iPrev ) { - /* add exit delta and edge */ - vf[2].e_Out = ie; - vf[2].delta_Out = delta; - } - - v2 = edge->neighbor12 ^ v1; /* next vertex */ - if (pBNS->vert[v2].type & BNS_VERT_TYPE_ATOM) { - nNumVisitedAtoms ++; - } - - if ( (ANY_VERT_TYPE(pBNS->vert[v2].type) || i == n-1) && - (vf[0].type & BNS_VERT_TYPE_C_GROUP) && vf[0].bUsed != VF_USED_ALL ) { - /* unused vertex is about to be discarded */ - nNumDeltaCharge += GetDeltaChargeFromVF( pBNS, pVA, &vf[0] ); - } - - if ( ANY_VERT_TYPE(pBNS->vert[v2].type) ) { - /* shift the queue */ - vf[0] = vf[1]; - vf[1] = vf[2]; - vf[2] = vf[3]; /* make vf[2] empty */ - /* add next vertex */ - vf[2].v = v2; - vf[2].type = pBNS->vert[v2].type; - vf[2].e_In = ie; - vf[2].delta_In = delta; - iPrev = 2; /* indicates a newly added vertex */ - } else - if ( i == n-1 ) { - /* shift the queue */ - vf[0] = vf[1]; - vf[1] = vf[2]; - vf[2] = vf[3]; /* make vf[2] empty */ - iPrev = 1; /* indicates the last vertex */ - } else { - iPrev = 0; /* no new vertex has been added */ - } - - if ( iPrev && (vf[1].type & BNS_VERT_TYPE_ATOM)) { - /* a new vertex has just been added and */ - /* an atom is in the middle of the queue */ - EdgeIndex eCPlus, eCMinus; - v = vf[1].v; - nInitCharge = pVA[v].cInitCharge; - nPlusFlow = nMinusFlow = 0; - if ( (eCPlus = pVA[v].nCPlusGroupEdge-1) >= 0 ) { - nPlusFlow = pBNS->edge[eCPlus].cap - - pBNS->edge[eCPlus].flow; - } - if ( (eCMinus = pVA[v].nCMinusGroupEdge-1) >= 0 ) { - nMinusFlow = -pBNS->edge[eCMinus].flow; - } - nInitCharge += nPlusFlow + nMinusFlow; - - nDeltaH = nDeltaCharge = 0; - - if ( vf[0].type & BNS_VERT_TYPE_TGROUP ) { - nDeltaH -= delta; - } else - if ( (vf[0].type & BNS_VERT_TYPE_C_GROUP) && !(vf[0].bUsed & VF_USED_OUT) ) { - if ( vf[0].e_Out==eCPlus || vf[0].e_Out==eCMinus ) { - nDeltaCharge -= vf[0].delta_Out; - vf[0].bUsed |= VF_USED_OUT; - } - } - - if ( vf[2].type & BNS_VERT_TYPE_TGROUP ) { - nDeltaH += delta; - } else - if ( (vf[2].type & BNS_VERT_TYPE_C_GROUP) && !(vf[2].bUsed & VF_USED_IN) ) { - if ( vf[2].e_In==eCPlus || vf[2].e_In==eCMinus ) { - nDeltaCharge -= vf[2].delta_In; - vf[2].bUsed |= VF_USED_IN; - } - } - if ( !nInitCharge && nDeltaCharge ) { - nNumDeltaCharge ++; - } else - if ( nInitCharge && 0 == nInitCharge + nDeltaCharge ) { - nNumDeltaCharge --; - } - - nNumDeltaH += abs(nDeltaH); - /* nNumDeltaCharge += abs(nDeltaCharge); */ - vf[1].bUsed = VF_USED_ALL; - } - } - for ( j = 0; j < 3; j ++ ) { - nNumDeltaCharge += GetDeltaChargeFromVF( pBNS, pVA, &vf[j] ); - } - - *pnDeltaH += nNumDeltaH; - *pnDeltaCharge += nNumDeltaCharge; - *pnNumVisitedAtoms += nNumVisitedAtoms; - - - if ( v2 != vLast ) { - err = BNS_PROGRAM_ERR; - } - } - return err? err : ret; -} - -/******************************************************************************************************/ -int RunBnsTestOnce( BN_STRUCT *pBNS, BN_DATA *pBD, VAL_AT *pVA, Vertex *pvFirst, Vertex *pvLast, - int *pPathLen, int *pnDeltaH, int *pnDeltaCharge, int *pnNumVisitedAtoms ) -{ - int bChangeFlow = 0; /* do not change flow */ - int delta, ret, ret2, pass; - - ReInitBnStructAltPaths( pBNS ); - pass = 0; - pBNS->alt_path = pBNS->altp[pass]; - pBNS->num_altp = 0; - pBNS->bChangeFlow = 0; - delta=BalancedNetworkSearch ( pBNS, pBD, bChangeFlow ); - if ( delta > 0 ) { - pBNS->alt_path = pBNS->altp[pass]; - *pvFirst = ALTP_START_ATOM(pBNS->alt_path); - *pPathLen = ALTP_PATH_LEN(pBNS->alt_path); - *pvLast = ALTP_END_ATOM(pBNS->alt_path); - pBNS->num_altp ++; - ret2 = EvaluateChargeChanges( pBNS, pVA, pnDeltaH, pnDeltaCharge, pnNumVisitedAtoms ); - } else { - *pvFirst = NO_VERTEX; - *pPathLen = 0; - *pvLast = NO_VERTEX; - ret2 = 0; - } - ReInitBnStructAltPaths( pBNS ); - ret = ReInitBnData( pBD ); - return (delta >= 0 && ret > 0 )? -ret : delta; - -} -/******************************************************************************************************/ -int RunBnsRestoreOnce( BN_STRUCT *pBNS, BN_DATA *pBD, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups ) -{ - /* run BNS for the first time */ - int nTotalDelta = 0, ret = 0; - int nDelta; - ReInitBnStructAltPaths( pBNS ); - do { - nDelta = RunBalancedNetworkSearch( pBNS, pBD, BNS_EF_CHNG_FLOW ); - if ( IS_BNS_ERROR(nDelta) ) { - ret = nDelta; - goto exit_function; - } - nTotalDelta += nDelta; - ReInitBnStructAltPaths( pBNS ); - ret = ReInitBnData( pBD ); - if ( ret > 0 ) { - ret = -ret; - goto exit_function; - } - } while( nDelta > 0 && ret == 0 ); - pBNS->tot_st_flow += 2*nTotalDelta; - ret = nTotalDelta; -exit_function: - return ret; -} -/******************************************************************************************************/ -int comp_cc_cand( const void *a1, const void *a2 ) -{ - const CC_CAND *p1 = (const CC_CAND *) a1; - const CC_CAND *p2 = (const CC_CAND *) a2; - int ret; - if ( ret = (int)p2->cMetal - (int)p1->cMetal ) - return ret; /* metal first */ - if ( ret = (int)p2->cNumBondsToMetal - (int)p1->cNumBondsToMetal ) - return ret; /* connected to metal first */ - if ( ret = (int)p2->cPeriodicRowNumber - (int)p1->cPeriodicRowNumber ) - return ret; /* heaviest first */ - if ( ret = (int)p2->num_bonds - (int)p1->num_bonds ) - return ret; /* more bonds first */ - if ( ret = (int)p1->chem_valence - (int)p2->chem_valence ) - return ret; /* less bond order first */ - if ( !p1->cNumValenceElectrons && p2->cNumValenceElectrons ) - return -1; /* no valence electrons first */ - if ( !p2->cNumValenceElectrons && p1->cNumValenceElectrons ) - return -1; /* no valence electrons first */ - if ( (int)p2->cNumValenceElectrons - (int)p1->cNumValenceElectrons ) - return ret; /* more valence electrons first */ - ret = (int)p2->iat - (int)p1->iat; /* greater canon number first */ - return ret; -} -/***************************************************************************************************** -Locate E1=C-E2 where - e ev are the edges - - E1 and E2 are atoms that belong to the same t-group - C is an atom that does not belong to any t-group - e is a forbidden edge - ev is not a forbidden edge - - Make changes so that: - E1(d)-C(d)-E2 - - where (d) means doublet radical - -*/ -/**************************************************************************************************/ -int get_pVA_atom_type( VAL_AT *pVA, inp_ATOM *at, int iat, int bond_type ) -{ - int type = 0, val; - if ( pVA[iat].cNumValenceElectrons == 4 ) { - if ( pVA[iat].cPeriodicRowNumber == 1 ) { - type |= EL_TYPE_C; - } - } else - if ( pVA[iat].cNumValenceElectrons == 6 ) { - if ( pVA[iat].cPeriodicRowNumber == 1 ) { - type |= EL_TYPE_O; - } else - if ( pVA[iat].cPeriodicRowNumber < 5 ) { - type |= EL_TYPE_S; - } - if ( bond_type == BOND_TYPE_SINGLE && - (type & (EL_TYPE_O | EL_TYPE_S)) && - 1 == nNoMetalBondsValence(at, iat ) && - 1 == nNoMetalNumBonds(at, iat) ) { - type |= EL_TYPE_OSt; - } - } else - if ( pVA[iat].cNumValenceElectrons == 5 ) { - if ( pVA[iat].cPeriodicRowNumber == 1 ) { - type |= EL_TYPE_N; - } else { - type |= EL_TYPE_P; - } - } else - if ( !is_el_a_metal(pVA[iat].cPeriodicNumber) ) { - type |= EL_TYPE_X; - } - /* check for possibility to be a tautomeric endpoint (that is, be a Mobile H site) */ - val = get_endpoint_valence( at[iat].el_number ); - if ( val && val > at[iat].valence && !at[iat].radical && - -1 <= at[iat].charge && at[iat].charge <= 0 && - val == at[iat].chem_bonds_valence - at[iat].charge + at[iat].num_H ) { - type |= EL_TYPE_PT; - } - return type; -} - -/*************************************************************************************/ -int AllocEdgeList( EDGE_LIST *pEdges, int nLen ) -{ - switch( nLen ) { - case EDGE_LIST_FREE: - if ( NULL != pEdges->pnEdges ) { - inchi_free( pEdges->pnEdges ); - } - /* fall through */ - case EDGE_LIST_CLEAR: - memset( pEdges, 0, sizeof(*pEdges) ); - break; - default: - if ( nLen > 0 && nLen != pEdges->num_alloc ) { - EdgeIndex *tmp_edges = pEdges->pnEdges; - int tmp_num = pEdges->num_edges; - pEdges->pnEdges = (EdgeIndex *)inchi_calloc( nLen, sizeof(pEdges->pnEdges[0])); - if ( !pEdges->pnEdges ) { - return RI_ERR_ALLOC; - } - tmp_num = inchi_min( tmp_num, nLen ); - if ( tmp_edges && tmp_num > 0 ) { - memcpy( pEdges->pnEdges, tmp_edges, tmp_num * sizeof(pEdges->pnEdges[0]) ); - pEdges->num_edges = tmp_num; - } else { - pEdges->num_edges = 0; - } - if ( tmp_edges ) { - inchi_free( tmp_edges ); - } - pEdges->num_alloc = nLen; - return 0; - } - break; - } - return 0; -} -/********************************************************************/ -int AddToEdgeList( EDGE_LIST *pEdges, int iedge, int nAddLen ) -{ - if ( pEdges->num_alloc == pEdges->num_edges ) { - int ret; - if ( nAddLen <= 0 ) { - return RI_ERR_PROGR; - } - if ( ret = AllocEdgeList( pEdges, pEdges->num_alloc + nAddLen ) ) { - return ret; - } - } - pEdges->pnEdges[pEdges->num_edges ++] = (EdgeIndex)iedge; - return 0; -} -/********************************************************************/ -int RemoveFromEdgeListByIndex( EDGE_LIST *pEdges, int index ) -{ - int len; - if ( 0 <= (len = pEdges->num_edges - index - 1) ) { - if ( len ) { - memmove( pEdges->pnEdges+index, pEdges->pnEdges+index+1, len*sizeof(pEdges->pnEdges[0])); - } - pEdges->num_edges --; - pEdges->pnEdges[pEdges->num_edges] = 0; - return 0; - } - return -1; -} -/********************************************************************/ -int FindInEdgeList( EDGE_LIST *pEdges, int iedge ) -{ - int i; - EdgeIndex ie = iedge; - for ( i = pEdges->num_edges-1; 0 <= i; i -- ) { - if ( ie == pEdges->pnEdges[i] ) { - return i; - } - } - return -1; -} -/********************************************************************/ -int RemoveFromEdgeListByValue( EDGE_LIST *pEdges, int iedge ) -{ - int i, ret, n = 0; - EdgeIndex ie = iedge; - for ( i = pEdges->num_edges-1; 0 <= i; i -- ) { - if ( ie == pEdges->pnEdges[i] ) { - if ( ret = RemoveFromEdgeListByIndex( pEdges, i ) ) { - return ret; - } - n ++; - } - } - return n; -} -/********************************************************************/ -int AllocBfsQueue( BFS_Q *pQ, int num_at, int min_ring_size ) -{ - int ret = 0; - switch( num_at ) { - case BFS_Q_FREE: - if ( pQ->q ) { - pQ->q = QueueDelete( pQ->q ); - } - if ( pQ->nAtomLevel ) { - inchi_free( pQ->nAtomLevel ); - } - if ( pQ->cSource ) { - inchi_free( pQ->cSource ); - } - /* fall through */ - case BFS_Q_CLEAR: - memset( pQ, 0, sizeof( *pQ ) ); - return 0; - default: - if ( num_at <= 0 ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - if ( num_at > pQ->num_at ) { - if ( pQ->num_at ) { - AllocBfsQueue( pQ, BFS_Q_FREE, 0 ); - } - pQ->q = QueueCreate( num_at+1, sizeof(qInt) ); - pQ->nAtomLevel = (AT_RANK*)inchi_calloc( sizeof(pQ->nAtomLevel[0]), num_at ); - pQ->cSource = (S_CHAR *)inchi_calloc( sizeof(pQ->cSource[0]), num_at ); - if ( !pQ->q || !pQ->cSource || !pQ->nAtomLevel ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - pQ->num_at = num_at; - } - pQ->min_ring_size = min_ring_size; - } -exit_function: - return ret; -} -/*************************************************************************************/ -void RemoveForbiddenEdgeMask( BN_STRUCT *pBNS, EDGE_LIST *pEdges, int forbidden_edge_mask ) -{ - int i, mask = ~forbidden_edge_mask; - for ( i = 0; i < pEdges->num_edges; i ++ ) { - pBNS->edge[pEdges->pnEdges[i]].forbidden &= mask; - } -} -/*************************************************************************************/ -void SetForbiddenEdgeMask( BN_STRUCT *pBNS, EDGE_LIST *pEdges, int forbidden_edge_mask ) -{ - int i; - for ( i = 0; i < pEdges->num_edges; i ++ ) { - pBNS->edge[pEdges->pnEdges[i]].forbidden |= forbidden_edge_mask; - } -} -/******************************************************************************************************/ -void RemoveForbiddenBondFlowBits( BN_STRUCT *pBNS, int forbidden_edge_mask_int ) -{ - BNS_EDGE *e; - int i; - int inv_forbidden_edge_mask = ~forbidden_edge_mask_int; - for ( i = 0, e = pBNS->edge; i < pBNS->num_bonds; i ++, e ++ ) { - e->forbidden &= inv_forbidden_edge_mask; - } -} -/****************************************************************************************************** - upper vc - edge / - v1[i0]---v0 - \ / - \ / - \ / - v1[i1] - | - | - atom -*/ -int GetChargeFlowerUpperEdge( BN_STRUCT *pBNS, VAL_AT *pVA, int nChargeEdge ) -{ - int ret = NO_VERTEX, i, j, k, i0, i1; - Vertex v0, v1[3], vc, v_t, v; - BNS_EDGE *pe, *pe1[3], *pe_t; - BNS_VERTEX *pv0, *pv1[3], *pv_t; - - if ( nChargeEdge < 0 ) { - goto exit_function; - } - pe = pBNS->edge + nChargeEdge; - vc = pe->neighbor1; /* charge vertex */ - if ( !IS_BNS_VT_C_GR(pBNS->vert[vc].type) ) { - vc = vc ^ pe->neighbor12; - } - v0 = vc ^ pe->neighbor12; /* ChargeStruct vertex ? */ - pv0 = pBNS->vert + v0; - if ( IS_BNS_VT_ATOM(pv0->type) ) { - goto exit_function; /* no charge flower exists */ - } - /* 2 edges from v0 */ - for ( i = j = 0; i < pv0->num_adj_edges && j < 3; i ++ ) { - pe1[j] = pBNS->edge + pv0->iedge[i]; - if ( vc != ( v1[j] = pe1[j]->neighbor12 ^ v0 ) && - (pv1[j] = pBNS->vert + v1[j], - !IS_BNS_VT_ATOM(pv1[j]->type) && !IS_BNS_VT_C_GR(pv1[j]->type)) ) { - j ++; - } - } - if ( j != 2 || i != pv0->num_adj_edges ) { - goto exit_function; - } - - if ( pv1[1]->num_adj_edges == 2 && - pv1[0]->num_adj_edges == 3 ) { - i0 = 1; - i1 = 0; - } else - if ( pv1[0]->num_adj_edges == 2 && - pv1[1]->num_adj_edges == 3 ) { - i0 = 0; - i1 = 1; - } else { - goto exit_function; - } - /* additional check: traverse edges around v1[i1] */ - pv_t = pv1[i1]; - v_t = v1[i1]; - for ( i = k = 0; i < pv_t->num_adj_edges; i ++ ) { - pe_t = pBNS->edge + pv_t->iedge[i]; - v = pe_t->neighbor12 ^ v_t; /* v1[i1] neighbor */ - if ( v == v0 ) { - k += 1; - } - if ( v == v1[i0] ) { - k += 2; - } - if ( IS_BNS_VT_ATOM(pBNS->vert[v].type) ) { - k += 4; - } - } - if ( k != 7 ) { - goto exit_function; - } - ret = pe1[i0] - pBNS->edge; - -exit_function: - return ret; - -} -#if (INCLUDE_NORMALIZATION_ENTRY_POINT == 1 ) -/******************************************************************************************** -input: allocate (num_at+num_deleted_H) atoms in inp_ATOM *at_norm, *at_fixed_bonds_out - allocate t_group_info -*********************************************************************************************/ -int NormalizeStructure( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, - StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, - VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - inp_ATOM *at_norm, inp_ATOM *at_fixed_bonds_out, T_GROUP_INFO *t_group_info ) -{ - int i, ret, num_endpoints, nLenTaut; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - /* - T_GROUP_INFO tgi; - T_GROUP_INFO *t_group_info = &tgi; - inp_ATOM *at_fixed_bonds_out = NULL; - inp_ATOM *at_norm = NULL; - - at_norm = (inp_ATOM *)inchi_calloc( len_at, sizeof(at_norm[0]) ); - at_fixed_bonds_out = (inp_ATOM *)inchi_calloc( len_at, sizeof(at_fixed_bonds_out[0]) ); - if ( !at_norm || !at_fixed_bonds_out ) { - if ( at_norm ) inchi_free( at_norm ); - if ( at_fixed_bonds_out ) inchi_free( at_fixed_bonds_out ); - ret = RI_ERR_ALLOC; - goto exit_function; - } - */ -/* call normalization only */ - memset( t_group_info, 0, sizeof(t_group_info[0]) ); - t_group_info->tni.nNumRemovedExplicitH = pStruct->num_deleted_H; - t_group_info->bTautFlags = ip->bTautFlags; - t_group_info->bTautFlagsDone = 0; /* (ip->bTautFlagsDone | sd->bTautFlagsDone[INCHI_BAS]);*/ - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret < 0 ) { - goto exit_function; - } -#if ( FIND_RING_SYSTEMS == 1 ) - ret = MarkRingSystemsInp( at2, num_at, 0 ); - if ( ret < 0 ) { - goto exit_function; - } -#endif - memcpy( at_norm, at2, len_at * sizeof(at_norm[0]) ); - for ( i = 0, num_endpoints = 0; i < num_at; i ++ ) { - num_endpoints += (0 != at_norm[i].endpoint); - at_norm[i].endpoint = 0; - } - - ret = mark_alt_bonds_and_taut_groups ( at_norm, at_fixed_bonds_out, num_at, t_group_info, - NULL /* &inpbTautFlags*/, NULL /*inpbTautFlagsDone*/ ); - if ( ret < 0 ) { - goto exit_function;/* out of RAM or other normalization problem */ - } - /* after normalization, t_group_info->t_group[i].num[0] = number of H + number of (-) */ - /* t_group_info->t_group[i].num[1] = number of (-) */ - - /* --- count t-groups, remove (-)-only t-groups, replace -------------------------------*/ - /* t_group_info->t_group[i].num[0] with */ - /* t_group_info->t_group[i].num[0]-t_group_info->t_group[i].num[1] */ - nLenTaut = CountTautomerGroupsInpAt( at_norm, num_at, t_group_info ); - ret = nLenTaut; -exit_function: - return ret; -} -#endif -/******************************************************************************************************/ -int MakeOneInChIOutOfStrFromINChI2(ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd_inp, - BN_STRUCT *pBNS, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, - VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - T_GROUP_INFO **t_group_info, - inp_ATOM **at_norm, inp_ATOM **at_prep ) -{ - int ret; - INPUT_PARMS ip_loc, *ip; - STRUCT_DATA sd_loc, *sd; - - ip_loc = *ip_inp; - sd_loc = *sd_inp; - ip = &ip_loc; - sd = &sd_loc; - memset( sd, 0, sizeof(*sd) ); - /* create structure out of BNS */ - memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); - pStruct->at = at2; - ret = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret < 0 ) { - goto exit_function;/* out of RAM or other normalization problem */ - } - pStruct->at = at; - ret = MakeOneInChIOutOfStrFromINChI( ip, sd, pStruct, at2, at3, pTCGroups ); - if ( ret < 0 ) { - goto exit_function;/* out of RAM or other normalization problem */ - } - if ( at_norm ) { - *at_norm = pStruct->pOne_norm_data[0]->at; - } - if ( at_prep ) { - if ( pStruct->pOne_norm_data[0]->bTautPreprocessed && pStruct->pOne_norm_data[0]->at_fixed_bonds ) { - *at_prep = pStruct->pOne_norm_data[0]->at_fixed_bonds; - } else - /* get preprocessed structure in case of Fixed-H */ - if ( pStruct->iMobileH == TAUT_NON && pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->bTautPreprocessed ) { - *at_prep = pStruct->pOne_norm_data[1]->at_fixed_bonds; - } else { - *at_prep = NULL; - } - } - if ( t_group_info ) { - if ( pStruct->iMobileH == TAUT_YES && - pStruct->One_ti.num_t_groups && - pStruct->One_ti.t_group && pStruct->One_ti.nEndpointAtomNumber ) { - *t_group_info = &pStruct->One_ti; - } else { - *t_group_info = NULL; - } - } -exit_function: - return ret; -} - -/******************************************************************************************************/ -int MakeOneInChIOutOfStrFromINChI( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, StrFromINChI *pStruct, - inp_ATOM *at2, inp_ATOM *at3, ALL_TC_GROUPS *pTCGroups ) -{ - - INCHI_MODE bTautFlags = ip->bTautFlags | TG_FLAG_H_ALREADY_REMOVED; - INCHI_MODE bTautFlagsDone = 0; /*(ip->bTautFlagsDone | sd->bTautFlagsDone[INCHI_BAS]);*/ - INChI *cur_INChI[TAUT_NUM]; - INChI_Aux *cur_INChI_Aux[TAUT_NUM]; - int i, j, k; - int iComponent = pTCGroups->iComponent; - int len_at = pStruct->num_atoms + pStruct->num_deleted_H; - int num_atoms = pStruct->num_atoms; - long ulStructTime; - - INP_ATOM_DATA InpCurAtData; - INP_ATOM_DATA *inp_cur_data; - - INP_ATOM_DATA InpNormAtData, InpNormTautData; - INP_ATOM_DATA *inp_norm_data[TAUT_NUM]; /* = { &InpNormAtData, &InpNormTautData }; */ - - int bOrigCoord = 0; - int num_at, ret = RI_ERR_PROGR; - struct tagInchiTime ulMaxTime; - - T_GROUP_INFO *t_group_info = NULL; - /* initialization */ - inp_cur_data = &InpCurAtData; - inp_norm_data[TAUT_NON] = &InpNormAtData; - inp_norm_data[TAUT_YES] = &InpNormTautData; - - memset( inp_cur_data , 0, sizeof( *inp_cur_data ) ); - memset( inp_norm_data[TAUT_NON], 0, sizeof( *inp_norm_data[0] ) ); - memset( inp_norm_data[TAUT_YES], 0, sizeof( *inp_norm_data[0] ) ); - ulStructTime = sd->ulStructTime; - memset( sd, 0, sizeof(*sd) ); - - /* deallocate old results */ - free_t_group_info( &pStruct->One_ti ); - for ( k = 0; k < TAUT_NUM; k ++ ) { - Free_INChI( &pStruct->pOneINChI[k] ); - Free_INChI_Aux( &pStruct->pOneINChI_Aux[k] ); - if ( pStruct->pOne_norm_data[k] ) { - FreeInpAtomData( pStruct->pOne_norm_data[k] ); - inchi_free( pStruct->pOne_norm_data[k] ); - pStruct->pOne_norm_data[k] = NULL; - } - cur_INChI[k] = NULL; - cur_INChI_Aux[k] = NULL; - } - memcpy( at3, at2, sizeof(at3[0])*len_at ); - /* prepare the structure */ - IncrZeroBondsAndClearEndpts(at3, num_atoms, iComponent+1); - CopySt2At( at3, pStruct->st, pStruct->num_atoms ); - FixUnkn0DStereoBonds( at3, pStruct->num_atoms); - ret = ReconcileAllCmlBondParities( at3, pStruct->num_atoms, 0 ); - if ( ret < 0 ) { - goto exit_function; - } - if ( 0 < fix_odd_things( num_atoms, at3, 1, ip->bFixNonUniformDraw ) ) - { - if ( sd->nErrorType < _IS_WARNING ) - { - sd->nErrorType = _IS_WARNING; - } - sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ODD_THINGS_DONE; - } - /* allocate and set parameters */ - inp_cur_data->at = at3; - inp_cur_data->num_at = num_atoms; - inp_cur_data->num_removed_H = pStruct->num_deleted_H; - - bTautFlagsDone &= ~(TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE); - - if ( i = bNumHeterAtomHasIsotopicH( at3, num_atoms ) ) { - if ( i & 1 ) { - bTautFlagsDone |= TG_FLAG_FOUND_ISOTOPIC_H_DONE; - } - if ( i & 2 ) { - bTautFlagsDone |= TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE; - } - } - - memset( &ulMaxTime, 0, sizeof(ulMaxTime)); - - /* allocate memory for non-tautimeric (k=0) and tautomeric (k=1) results */ - for ( k = 0; k < TAUT_NUM; k ++ ) { - - if ( !pStruct->bMobileH || k == pStruct->bMobileH ) { - /* pStruct->bMobileH=0: k = 0, 1 => allow allocation of both Fixed-H and Mobile-H InChI - pStruct->bMobileH=1: k = 1 only => allow allocation of only Mobile-H InChI */ - int nAllocMode = (k==TAUT_YES? REQ_MODE_TAUT:0) | - (bTautFlagsDone & ( TG_FLAG_FOUND_ISOTOPIC_H_DONE | - TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE ))? - (ip->nMode & REQ_MODE_ISO):0; - - if ( k==TAUT_NON && (ip->nMode & REQ_MODE_BASIC ) || - k==TAUT_YES && (ip->nMode & REQ_MODE_TAUT ) ) { - /* alloc INChI and INChI_Aux only if ip->nMode allows this */ - cur_INChI[k] = Alloc_INChI( inp_cur_data->at, inp_cur_data->num_at, &inp_cur_data->num_bonds, - &inp_cur_data->num_isotopic, nAllocMode ); - cur_INChI_Aux[k] = Alloc_INChI_Aux( inp_cur_data->num_at, - inp_cur_data->num_isotopic, nAllocMode, bOrigCoord ); - if ( cur_INChI_Aux[k] ) { - cur_INChI_Aux[k]->bIsIsotopic = inp_cur_data->num_isotopic; - } - /* alloc memory for the output structure: non-tautomeric and tautomeric (for displaying) */ - CreateInpAtomData( inp_norm_data[k], inp_cur_data->num_at+inp_cur_data->num_removed_H, k ); - inp_norm_data[k]->num_removed_H = inp_cur_data->num_removed_H; - } else { - FreeInpAtomData( inp_norm_data[k] ); - } - } else { - FreeInpAtomData( inp_norm_data[k] ); - } - } - k = pStruct->bMobileH; - /* In case of Fixed-H we have to create InChI for both Fixed-H and Mobile-H */ - num_at = Create_INChI( cur_INChI, cur_INChI_Aux, NULL/* not used */, inp_cur_data->at, - inp_norm_data, - inp_cur_data->num_at+inp_cur_data->num_removed_H, - ip->nMode, &bTautFlags, &bTautFlagsDone, NULL /* &ulMaxTime*/, &pStruct->One_ti, sd->pStrErrStruct); - SetConnectedComponentNumber( inp_cur_data->at, inp_cur_data->num_at, iComponent+1 ); /* normalization alters structure component number */ - - /* detect InChI errors */ - - if ( num_at < 0 ) { - ret = num_at; - } else - if ( cur_INChI[k] && cur_INChI[k]->nErrorCode ) { - ret = cur_INChI[k]->nErrorCode; - } else - if ( cur_INChI_Aux[k] && cur_INChI_Aux[k]->nErrorCode ) { - ret = cur_INChI_Aux[k]->nErrorCode; - } else { - ret = 0; - } - - /* fill out the output */ - - if ( !ret ) { - int bMobileH = pStruct->bMobileH; - if ( bMobileH == TAUT_NON && - 0 == cur_INChI[TAUT_NON]->nNumberOfAtoms && - 0 < cur_INChI[TAUT_YES]->nNumberOfAtoms ) { - /* tautomerism or H(+) removal/addition was not discovered */ - bMobileH = TAUT_YES; - } - pStruct->nChargeRevrs = cur_INChI[TAUT_YES]->nTotalCharge; - - pStruct->pOneINChI[0] = cur_INChI[bMobileH]; - pStruct->pOneINChI_Aux[0] = cur_INChI_Aux[bMobileH]; - pStruct->nOneINChI_bMobileH = bMobileH; - cur_INChI[bMobileH] = NULL; /* remove pointer to avoid deallocation at exit_function */ - cur_INChI_Aux[bMobileH] = NULL; /* remove pointer to avoid deallocation at exit_function */ - - pStruct->nNumRemovedProtons = (pStruct->iMobileH == TAUT_YES)? pStruct->One_ti.tni.nNumRemovedProtons : 0; - - - /* set correct t-group numbers to endpoints */ - t_group_info = &pStruct->One_ti; - if ( t_group_info->num_t_groups && t_group_info->t_group && t_group_info->nEndpointAtomNumber ) { - inp_ATOM *at_norm = inp_norm_data[TAUT_YES]->at; - int num_at_norm = inp_norm_data[TAUT_YES]->num_at; - for ( i = 0; i < num_at_norm; i ++ ) { - at_norm[i].endpoint = 0; - } - for ( i = 0; i < t_group_info->num_t_groups; i ++ ) { - k = t_group_info->t_group[i].nFirstEndpointAtNoPos; - /* add number of mobile (-) to the number of mobile H */ - t_group_info->t_group[i].num[0] += t_group_info->t_group[i].num[1]; - for ( j = 0; j < t_group_info->t_group[i].nNumEndpoints; j ++, k ++ ) { - at_norm[t_group_info->nEndpointAtomNumber[k]].endpoint = t_group_info->t_group[i].nGroupNumber; - } - } - } - pStruct->pOne_norm_data[0] = (INP_ATOM_DATA *) inchi_malloc( sizeof(pStruct->pOne_norm_data[0][0]) ); - if ( pStruct->pOne_norm_data[0] ) { - memcpy( pStruct->pOne_norm_data[0], inp_norm_data[bMobileH], sizeof(pStruct->pOne_norm_data[0][0])); - memset( inp_norm_data[bMobileH], 0, sizeof(*inp_norm_data[0]) ); - } else { - ret = RI_ERR_ALLOC; - } - if ( bMobileH == TAUT_NON && cur_INChI[TAUT_YES]->nNumberOfAtoms > 0 ) { - int bMobileHalt = ALT_TAUT(bMobileH); /* = TAUT_YES */ - pStruct->pOneINChI[1] = cur_INChI[bMobileHalt]; - pStruct->pOneINChI_Aux[1] = cur_INChI_Aux[bMobileHalt]; - cur_INChI[bMobileHalt] = NULL; - cur_INChI_Aux[bMobileHalt] = NULL; - pStruct->pOne_norm_data[1] = (INP_ATOM_DATA *) inchi_malloc( sizeof(pStruct->pOne_norm_data[0][0]) ); - if ( pStruct->pOne_norm_data[1] ) { - memcpy( pStruct->pOne_norm_data[1], inp_norm_data[bMobileHalt], sizeof(pStruct->pOne_norm_data[0][0])); - memset( inp_norm_data[bMobileHalt], 0, sizeof(*inp_norm_data[0]) ); - } else { - ret = RI_ERR_ALLOC; - } - } - } else { -#if ( bRELEASE_VERSION != 1 ) -#ifndef TARGET_API_LIB - fprintf( stdout, "ERROR: Create_INChI returned %d\n", ret ); -#endif -#endif - } - -exit_function: - /* deallocate unused */ - for ( k = 0; k < TAUT_NUM; k ++ ) { - Free_INChI( &cur_INChI[k] ); - Free_INChI_Aux( &cur_INChI_Aux[k] ); - FreeInpAtomData( inp_norm_data[k] ); - } - sd->ulStructTime = ulStructTime; - - return ret; -} - -/****************************************************************************************************** -Input: - at[].num_H = total number of all terminal H connected to the atom - at[].num_iso_H[] = numbers of isotopic H among at[].num_H - Explicit H are disconnected - Calculate InChI with normalization only in MakeOneInChIOutOfStrFromINChI() - with (TG_FLAG_H_ALREADY_REMOVED & bTautFlags) != 0 -Output: - at[].num_H = number of implicit non-isotopic H connected to the atom - at[].num_iso_H[] = numbers of implicit isotopic H (not included in at[].num_H) - Explicit H are connected - Calculate InChI with full preprocessing MakeInChIOutOfStrFromINChI2() - with (TG_FLAG_H_ALREADY_REMOVED & bTautFlags) == 0 -*******************************************************************************************************/ -int ConnectDisconnectedH( inp_ATOM *at, int num_atoms, int num_deleted_H ) -{ - int i, j, k, n, m, num_H; - int tot_atoms = num_atoms + num_deleted_H; - - for ( i = num_atoms; i < tot_atoms; i = j ) { - k = at[i].neighbor[0]; /* a[k] is the atom connected to the explicit hydrogen at[i] */ - for ( j = i; j < tot_atoms && at[j].neighbor[0] == k; j ++ ) - ; - num_H = j-i; /* number of explicit H for at[k] */ - if ( num_H > at[k].num_H ) { - return RI_ERR_PROGR; - } - if ( num_H + at[k].valence > MAXVAL ) { - return RI_ERR_SYNTAX; - } - /* insert links to explicit H before all other links in the connection list */ - n = at[k].valence; - memmove( at[k].neighbor +num_H, at[k].neighbor, sizeof(at[k].neighbor[0]) * n ); - memmove( at[k].bond_stereo+num_H, at[k].bond_stereo, sizeof(at[k].bond_stereo[0]) * n ); - memmove( at[k].bond_type +num_H, at[k].bond_type , sizeof(at[k].bond_type[0]) * n ); - for ( n = 0; n < num_H; n ++ ) { - at[k].neighbor[n] = i + n; - at[k].bond_stereo[n] = 0; - at[k].bond_type[n] = BOND_TYPE_SINGLE; - } - for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[k].sb_parity[m]; m ++ ) { - at[k].sb_ord[m] += num_H; - if ( at[k].sn_ord[m] < 0 ) { - for ( n = i; n < j; n ++ ) { - if ( at[n].orig_at_number == at[k].sn_orig_at_num[m] ) { - at[k].sn_ord[m] = n-i; - break; - } - } - if ( n == j ) { - return RI_ERR_PROGR; - } - } else { - at[k].sn_ord[m] += num_H; - } - } - at[k].valence += num_H; - at[k].chem_bonds_valence += num_H; - at[k].num_H -= num_H; /* cannot be negative */ - /*memset( at[k].num_iso_H, 0, sizeof(at[0].num_iso_H) );*/ /* attached H must carry all isotopic shifts */ - for ( n = i; n < j; n ++ ) { - at[n].chem_bonds_valence = BOND_TYPE_SINGLE; - } - /* isotopic H */ - for ( m = j-1; i <= m && at[m].iso_atw_diff > 0 ; m -- ) { - if ( at[m].iso_atw_diff > NUM_H_ISOTOPES ) { - return RI_ERR_PROGR; - } - if ( 0 >= at[k].num_iso_H[(int)at[m].iso_atw_diff-1] -- ) { - return RI_ERR_PROGR; - } - } - - } - /* subtract isotopic H */ - for ( i = 0; i < num_atoms; i ++ ) { - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - at[i].num_H -= at[i].num_iso_H[m]; - } - if ( 0 > at[i].num_H ) { - return RI_ERR_PROGR; - } - } - - return tot_atoms; -} -/****************************************************************************************************** -Input: - at[].num_H = number of implicit non-isotopic H connected to the atom - at[].num_iso_H[] = numbers of implicit isotopic H (not included in at[].num_H) - Explicit H are connected - Calculate InChI with (TG_FLAG_H_ALREADY_REMOVED & bTautFlags) == 0 -Output: - at[].num_H = total number of all terminal H connected to the atom - at[].num_iso_H[] = numbers of isotopic H among at[].num_H - Explicit H are disconnected - Calculate InChI with (TG_FLAG_H_ALREADY_REMOVED & bTautFlags) != 0 -*******************************************************************************************************/ -int DisconnectedConnectedH( inp_ATOM *at, int num_atoms, int num_deleted_H ) -{ - int i, j, k, n, m, num_H, num_iso_H; - int tot_atoms = num_atoms + num_deleted_H; - - /* add implicit isotopic H to total implicit H */ - for ( i = 0; i < num_atoms; i ++ ) { - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - at[i].num_H += at[i].num_iso_H[m]; - } - } - for ( i = num_atoms; i < tot_atoms; i = j ) { - k = at[i].neighbor[0]; /* a[k] is the atom connected to the explicit hydrogen at[i] */ - for ( j = i; j < tot_atoms && at[j].neighbor[0] == k; j ++ ) { - at[j].chem_bonds_valence = 0; - } - num_H = j-i; /* number of explicit H for at[k] */ - /* verify correct number of explicit H */ - for ( n = 0; n < at[k].valence && at[k].neighbor[n] >= num_atoms; n ++ ) - ; - if ( n != num_H ) { - return RI_ERR_PROGR; - } - /* remove bonds to explicit H located in front of all other bonds in the connection list */ - n = (at[k].valence -= num_H); /* new number of bonds */ - at[k].chem_bonds_valence -= num_H; /* new no-H valence */ - if ( n ) { - memmove( at[k].neighbor, at[k].neighbor + num_H, sizeof(at[k].neighbor[0]) * n ); - memmove( at[k].bond_stereo, at[k].bond_stereo + num_H, sizeof(at[k].bond_stereo[0]) * n ); - memmove( at[k].bond_type, at[k].bond_type + num_H, sizeof(at[k].bond_type[0]) * n ); - } - /* clear the 'tails' */ - memset( at[k].neighbor+n, 0, sizeof(at[k].neighbor[0]) * num_H ); - memset( at[k].bond_stereo+n, 0, sizeof(at[k].bond_stereo[0]) * num_H ); - memset( at[k].bond_type+n, 0, sizeof(at[k].bond_type[0]) * num_H ); - - for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[k].sb_parity[m]; m ++ ) { - at[k].sb_ord[m] -= num_H; - if ( 0 <= at[k].sn_ord[m] && at[k].sn_ord[m] < num_H ) { - at[k].sn_ord[m] = -1; /* disconnected explicit H */ - } - } - /* add explicit isotopic H (already included in num_H) */ - for ( num_iso_H = 0, m = j-1; i <= m && at[m].iso_atw_diff > 0 ; m -- ) { - if ( at[m].iso_atw_diff > NUM_H_ISOTOPES ) { - return RI_ERR_PROGR; - } - at[k].num_iso_H[(int)at[m].iso_atw_diff-1] ++; - } - at[k].num_H += num_H; /* add all explicit H including isotopic */ - } - return tot_atoms; -} -/******************************************************************************************************/ -int MakeInChIOutOfStrFromINChI2( ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd_inp, StrFromINChI *pStruct, - int iComponent, int iAtNoOffset, long num_inp ) -{ - char szTitle[MAX_SDF_HEADER+MAX_SDF_VALUE+256]; - - int len, ret; - /* - PINChI2 *pINChI[INCHI_NUM]; - PINChI_Aux2 *pINChI_Aux[INCHI_NUM]; - */ - char pStr[256]; - INPUT_PARMS local_ip; - STRUCT_DATA local_sd; - INPUT_PARMS *ip = &local_ip; - STRUCT_DATA *sd = &local_sd; - - ORIG_ATOM_DATA OrigAtData; /* 0=> disconnected, 1=> original */ - ORIG_ATOM_DATA *orig_inp_data = &OrigAtData; - ORIG_ATOM_DATA PrepAtData[2]; /* 0=> disconnected, 1=> original */ - ORIG_ATOM_DATA *prep_inp_data = PrepAtData; - - *ip = *ip_inp; - ip->bDisplay = 0; - ip->bDisplayCompositeResults = 0; - ip->bDisplayEachComponentINChI = 0; - ip->bDisplayIfRestoreWarnings = 0; - ip->bINChIOutputOptions = INCHI_OUT_NO_AUX_INFO; - /* - if ( pStruct->bMobileH ) { - ip->nMode &= ~REQ_MODE_BASIC; - ip->nMode |= REQ_MODE_TAUT; - } else { - ip->nMode |= (REQ_MODE_TAUT | REQ_MODE_BASIC); - } - */ - memset( sd, 0, sizeof(*sd) ); - sd->fPtrStart = -1; - sd->fPtrEnd = -1; - /* - if ( ip->nMode & REQ_MODE_STEREO ) { - if ( ip->nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO) ) { - sd->bChiralFlag |= FLAG_INP_AT_NONCHIRAL; - } else { - sd->bChiralFlag |= FLAG_INP_AT_CHIRAL; - } - } - */ - memset( orig_inp_data , 0, sizeof( *orig_inp_data ) ); - memset( prep_inp_data , 0, 2*sizeof( *prep_inp_data ) ); - memset( pStruct->RevInChI.pINChI, 0, sizeof(pStruct->RevInChI.pINChI ) ); - memset( pStruct->RevInChI.pINChI_Aux, 0, sizeof(pStruct->RevInChI.pINChI_Aux) ); - memset( pStr, 0, sizeof(pStr) ); - memset( szTitle, 0, sizeof(szTitle) ); - - len = sizeof(orig_inp_data->at[0])*(pStruct->num_atoms + pStruct->num_deleted_H); - orig_inp_data->at = (inp_ATOM *) inchi_malloc( len ); - if ( orig_inp_data->at ) { - /*memcpy( orig_inp_data->at, pStruct->at2, len );*/ - /*ret = ConnectDisconnectedH( orig_inp_data->at, pStruct->num_atoms, pStruct->num_deleted_H );*/ - CopySt2At( pStruct->at2, pStruct->st, pStruct->num_atoms ); - ret = ConnectDisconnectedH( pStruct->at2, pStruct->num_atoms, pStruct->num_deleted_H ); - if ( ret < 0 ) { - goto exit_error; - } - orig_inp_data->num_inp_atoms = ret; - /* connections changed => reconcile parities even if they were reconciled before */ - /* remove t-group markings and increment zero-order bonds, - otherwise MakeInChIOutOfStrFromINChI2() woild fail */ - /* - IncrZeroBondsAndClearEndpts(pStruct->at2, pStruct->num_atoms, iComponent+1); - */ - IncrZeroBonds(pStruct->at2, pStruct->num_atoms, iComponent+1); - - /* CopySt2At() moved to the position before ConnectDisconnectedH() because - in case stereo exists only in Mobile-H layer and the processd here - component is restored in Fixed-H layer the parities needed by - ConnectDisconnectedH() must be there before calling - ConnectDisconnectedH() - */ - /*CopySt2At( pStruct->at2, pStruct->st, pStruct->num_atoms );*/ - - ret = ReconcileAllCmlBondParities( pStruct->at2, orig_inp_data->num_inp_atoms, 0 ); - if ( ret < 0 ) { - goto exit_error; - } - memcpy( orig_inp_data->at, pStruct->at2, len ); - ClearEndpts(orig_inp_data->at, pStruct->num_atoms); - if ( FixUnkn0DStereoBonds(orig_inp_data->at, pStruct->num_atoms) ) { - ret = ReconcileAllCmlBondParities( pStruct->at2, orig_inp_data->num_inp_atoms, 0 ); - if ( ret < 0 ) { - goto exit_error; - } - } - /* keep endpoint[] markings in at2[] for subsequent add/remove protons */ - } else { - ret = RI_ERR_ALLOC; - goto exit_error; - } - memset( sd->num_components, 0, sizeof(sd->num_components) ); - memset( sd->num_taut, 0, sizeof(sd->num_taut) ); - memset( sd->num_non_taut, 0, sizeof(sd->num_non_taut) ); - memset( sd->bTautFlagsDone, 0, sizeof(sd->bTautFlagsDone) ); - memset( sd->bTautFlags, 0, sizeof(sd->bTautFlags) ); - - ret = ProcessOneStructure( sd, ip, szTitle, pStruct->RevInChI.pINChI, pStruct->RevInChI.pINChI_Aux, - NULL /*inp_file*/, NULL /*log_file*/, NULL /*output_file*/, NULL /*prb_file*/, - orig_inp_data, prep_inp_data, - num_inp, pStr, sizeof(pStr), - 0 /* save_opt_bits */); - - memcpy(pStruct->RevInChI.num_components, sd->num_components, sizeof(pStruct->RevInChI.num_components) ); - memcpy(sd_inp->pStrErrStruct, sd->pStrErrStruct, sizeof(sd_inp->pStrErrStruct) ); - pStruct->RevInChI.nRetVal = ret; - /* translate returned value */ - if ( ret == _IS_ERROR || ret == _IS_FATAL || ret == _IS_UNKNOWN ) { - ret = RI_ERR_PROGR; - } else - if ( ret == _IS_OKAY ) { - ret = 0; - } else - if ( ret == _IS_WARNING ) { - ret = 1; - } else { - ret = RI_ERR_PROGR; - } - /* save total charge from Mobile-H layer */ - pStruct->nChargeRevrs = 0; - if ( ret >= 0 ) { - if ( bRevInchiComponentExists( pStruct, INCHI_REC, TAUT_YES, 0 ) ) { - pStruct->nChargeRevrs = pStruct->RevInChI.pINChI[INCHI_REC][0][TAUT_YES]->nTotalCharge; - } else - if ( bRevInchiComponentExists( pStruct, INCHI_BAS, TAUT_YES, 0 ) ) { - pStruct->nChargeRevrs = pStruct->RevInChI.pINChI[INCHI_BAS][0][TAUT_YES]->nTotalCharge; - } - } - - /* free structure data */ - FreeOrigAtData( orig_inp_data ); - FreeOrigAtData( prep_inp_data ); - FreeOrigAtData( prep_inp_data+1 ); - -exit_error: - return ret; -} -/******************************************************************************************************/ -int OutputInChIOutOfStrFromINChI(ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd_inp, - long num_inp, int bINChIOutputOptions, - INCHI_IOSTREAM *pout, INCHI_IOSTREAM *plog, - InpInChI *pOneInput, int bHasSomeFixedH, - unsigned char save_opt_bits) -{ - char szTitle[MAX_SDF_HEADER+MAX_SDF_VALUE+256]; - - int len, ret; -/* - PINChI2 *pINChI[INCHI_NUM]; - PINChI_Aux2 *pINChI_Aux[INCHI_NUM]; -*/ - REV_INCHI RevInChI; - int nStrLen = INCHI_SEGM_BUFLEN; - char *pStr = NULL; - - INPUT_PARMS local_ip; - STRUCT_DATA local_sd; - INPUT_PARMS *ip = &local_ip; - STRUCT_DATA *sd = &local_sd; - - ORIG_ATOM_DATA OrigAtData; /* 0=> disconnected, 1=> original */ - ORIG_ATOM_DATA *orig_inp_data = &OrigAtData; - ORIG_ATOM_DATA PrepAtData[2]; /* 0=> disconnected, 1=> original */ - ORIG_ATOM_DATA *prep_inp_data = PrepAtData; - - *ip = *ip_inp; - ip->bNoStructLabels = 1; - ip->bDisplay = 0; - ip->bDisplayCompositeResults = 0; - ip->bDisplayEachComponentINChI = 0; - ip->bDisplayIfRestoreWarnings = 0; -#if ( I2S_MODIFY_OUTPUT == 1 ) - if ( bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY ) - ip->bINChIOutputOptions = bINChIOutputOptions & ~(INCHI_OUT_PLAIN_TEXT | INCHI_OUT_XML | INCHI_OUT_PLAIN_TEXT_COMMENTS | INCHI_OUT_XML_TEXT_COMMENTS); - else - if ( bINChIOutputOptions & INCHI_OUT_XML ) - ip->bINChIOutputOptions = bINChIOutputOptions & ~(INCHI_OUT_PLAIN_TEXT | INCHI_OUT_SDFILE_ONLY) | INCHI_OUT_EMBED_REC; - else - if ( bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT ) - ip->bINChIOutputOptions = bINChIOutputOptions & ~(INCHI_OUT_XML | INCHI_OUT_SDFILE_ONLY) | INCHI_OUT_EMBED_REC; - else - if ( bINChIOutputOptions & (INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO | INCHI_OUT_ONLY_AUX_INFO | INCHI_OUT_TABBED_OUTPUT)) - ip->bINChIOutputOptions = (INCHI_OUT_PLAIN_TEXT | INCHI_OUT_EMBED_REC | bINChIOutputOptions); - else - ip->bINChIOutputOptions = (INCHI_OUT_PLAIN_TEXT | INCHI_OUT_EMBED_REC); -#else - ip->bINChIOutputOptions = (INCHI_OUT_PLAIN_TEXT | INCHI_OUT_EMBED_REC ); -#endif - - if ( bHasSomeFixedH ) - { - ip->nMode |= (REQ_MODE_TAUT | REQ_MODE_BASIC); - } else - { - ip->nMode &= ~REQ_MODE_BASIC; - ip->nMode |= REQ_MODE_TAUT; - } - - memset( sd, 0, sizeof(*sd) ); - sd->fPtrStart = -1; - sd->fPtrEnd = -1; - /* - if ( ip->nMode & REQ_MODE_STEREO ) { - if ( ip->nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO) ) { - sd->bChiralFlag |= FLAG_INP_AT_NONCHIRAL; - } else { - sd->bChiralFlag |= FLAG_INP_AT_CHIRAL; - } - } - */ - memset( orig_inp_data, 0, sizeof( *orig_inp_data ) ); - memset( prep_inp_data, 0, 2*sizeof( *prep_inp_data ) ); - memset( RevInChI.pINChI, 0, sizeof(RevInChI.pINChI ) ); - memset( RevInChI.pINChI_Aux, 0, sizeof(RevInChI.pINChI_Aux) ); - - len = sizeof(orig_inp_data->at[0]) * pOneInput->num_atoms; - orig_inp_data->at = (inp_ATOM *) inchi_malloc( len ); - orig_inp_data->szCoord = (MOL_COORD *)inchi_calloc( pOneInput->num_atoms, sizeof(orig_inp_data->szCoord[0])); - pStr = (char *)inchi_calloc( nStrLen, sizeof(char) ); - if ( orig_inp_data->at && orig_inp_data->szCoord && pStr ) { - int i, k; - memcpy( orig_inp_data->at, pOneInput->atom, len ); - orig_inp_data->num_inp_atoms = pOneInput->num_atoms; - ClearEndpts( orig_inp_data->at, orig_inp_data->num_inp_atoms ); - /* otherwise fails on CID=450438 */ - if ( FixUnkn0DStereoBonds(orig_inp_data->at, orig_inp_data->num_inp_atoms) ) { - ret = ReconcileAllCmlBondParities( orig_inp_data->at, orig_inp_data->num_inp_atoms, 0 ); - if ( ret < 0 ) { - goto exit_error; - } - } - /* To obtain rA,rB,rC in AuxInfo we have to emulate input coordinates; make all of them zeroes */ - for ( i = 0; i < pOneInput->num_atoms; i ++ ) { - for ( k = 0; k < NUM_COORD*LEN_COORD; k += LEN_COORD ) { - orig_inp_data->szCoord[i][k] = '0'; - } - } - } else { - ret = RI_ERR_ALLOC; - goto exit_error; - } - memset( sd->num_components, 0, sizeof(sd->num_components) ); - memset( sd->num_taut, 0, sizeof(sd->num_taut) ); - memset( sd->num_non_taut, 0, sizeof(sd->num_non_taut) ); - memset( sd->bTautFlagsDone, 0, sizeof(sd->bTautFlagsDone) ); - memset( sd->bTautFlags, 0, sizeof(sd->bTautFlags) ); - memset( szTitle, 0, sizeof(szTitle) ); - - ret = ProcessOneStructure(sd, ip, szTitle, RevInChI.pINChI, RevInChI.pINChI_Aux, - NULL /*inp_file*/, plog /*log_file*/, pout /*output_file*/, NULL /*prb_file*/, - orig_inp_data, prep_inp_data, - num_inp, pStr, nStrLen, - save_opt_bits); - memcpy(RevInChI.num_components, sd->num_components, sizeof(RevInChI.num_components) ); - /* - memcpy(sd_inp->pStrErrStruct, sd->pStrErrStruct, sizeof(sd_inp->pStrErrStruct) ); - */ - RevInChI.nRetVal = ret; - /* translate returned value */ - if ( ret == _IS_ERROR || ret == _IS_FATAL || ret == _IS_UNKNOWN ) { - ret = RI_ERR_PROGR; - } else - if ( ret == _IS_OKAY ) { - ret = 0; - } else - if ( ret == _IS_WARNING ) { - ret = 1; - } else { - ret = RI_ERR_PROGR; - } - - /* free structure data */ - FreeOrigAtData( orig_inp_data ); - FreeOrigAtData( prep_inp_data ); - FreeOrigAtData( prep_inp_data+1 ); - FreeAllINChIArrays( RevInChI.pINChI, - RevInChI.pINChI_Aux, - RevInChI.num_components ); - - -exit_error: - if ( pStr ) inchi_free( pStr ); - return ret; -} -#endif +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include + +/* */ +/*#define CHECK_WIN32_VC_HEAP*/ + +#include "mode.h" + +#if ( READ_INCHI_STRING == 1 ) + +#include "ichitime.h" +#include "ichirvrs.h" +#include "ichicant.h" +#include "ichi_io.h" + + +/************************************************************************************************** + + ChargeStruct fictitios structures MY_CONST CN_LIST cnList[*] + ============================================================ + + + bond flow (+) => Positive charge c-group + ----------------- (-) => Negative charge c-group + Single 0 (+C) => Positive charge group for C, Si, Ge, Sn, Pb + Double 1 (-C) => Negative charge group for C, Si, Ge, Sn, Pb + Triple 2 (.) => additional one unit of st_cap + + A) Interpretation: + + X-(-) or X=(+) => zero charge + X=(-) or X-(+) => charge = -1 or +1, respectively + + B) Information to keep: + + ordering zero-based number of the edge + to (+) or (-) from the Interpretation (A) section + + vCap = vertex cap + vFlow = vertex flow + val = number of edges incident to the vertex + neigh = 1-based ordering number of the adjacent vertex; 0 => no more adjacent vertices + cap = cap of the edge to the adjacent vertex + flow = flow of the edge to the adjacent vertex + + atom (c-point) always has number 1 + c-group(s) always are the last vertices + always adjacent_neigh_number > vertex_number, that is, neigh > vertex + + Contribution to the Total Charge: + ---------------------------------- + edge_cap(+) - edge_flow(+) - edge_flow(-) - Delta(+) - Delta(-) + + where edge_cap(+) is edge capacity to c-group (+); + edge_flow(+) is edge capacity to c-group (?), (?)= (+) or (-); + Delta(?) = st_cap(?) - st_floe(?) of the c-group vertex (?), (?)= (+) or (-); + +***************************************************************************************************/ +/************************************************************************************************** + + Important: + vCap and vFlow Note: since metal charge group (vert. 2-4) MUST be registered before + marked with empty the "metal flower" (5-8) all charge group vertex numbers are + comments for vertices less than metal flower vertices: (2,3,4) < (5,6,7,8) + 1 and 5(M) should This MAY be neded for c-group enumeration. The order is: + be set separately t-groups, c-groups, M-flower. All types BNS_VT_M_GROUP allows to avoid duplications. + + 3(+) + || (Metal) + || \|/ init charge=0; MAX_METAL_CHARGE = 16 + 4(-) 5(M) 2 -Fe- CAP(BOND_TO_BNS_VT_M_GROUP) = NUM_BONDS*CAP + \ | / | + \ | / + 1 X(V), V=valence +*/ +MY_CONST C_NODE cnMe[5] = { + /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ + { {BNS_VERT_TYPE_ATOM,0/**/ ,0/**/,3}, {{ 2, 16,0, 0 },{ 4, 16,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ + { {BNS_VT_CHRG_STRUCT,16, 16, 2}, {{ 3, 16,0,16 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ + { {BNS_VT_C_POS_M, 16, 16, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 2 }, { 0, 0,0, 0 }} }, /* 3 */ + { {BNS_VT_C_NEG_M, 0+16, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ + { {BNS_VT_M_GROUP, 0/**/ ,0/**/,3}, {{ 1, 3,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ +}; +/* +#define cn_bits_Me (-1) +*/ + +/************************************************************************************************** + c=2 5(+.) + _____ / (PNPN) + 4=====3 |||| init charge=0 + c=2 \ / c=1 -N- + 2 | + |||| + 1 X+(V), X(V+1), X+(V+2), X(V+3); V=valence +*/ +MY_CONST C_NODE cnPNPN[5] = { + /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ + { {BNS_VERT_TYPE_ATOM, 3, 3, 1}, {{ 2, 3,0, 3 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ + { {BNS_VT_CHRG_STRUCT, 3, 3, 3}, {{ 3, 1,0, 0 },{ 4, 2,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ + { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 5, 1,0, 0 },{ 4, 2,0, 2 }, { 0, 0,0, 0 }} }, /* 3 */ + { {BNS_VT_CHRG_STRUCT, 2, 2, 2}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ + { {BNS_VT_C_POS, 1+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ +}; +/* +#define cn_bits_PNPN MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_P, cn_bits_N) +*/ +/************************************************************************************************** + 5(+) + c=1 // (NPNP) + 4=====3 |||| init charge=0 + c=1 \ / c=2 -N- + 2 | + |||| + 1 X(V), X+(V+1), X(V+2), X+(V+3); V=valence +*/ +MY_CONST C_NODE cnNPNP[5] = { + /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ + { {BNS_VERT_TYPE_ATOM, 3, 3, 1}, {{ 2, 3,0, 3 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ + { {BNS_VT_CHRG_STRUCT, 3, 3, 3}, {{ 3, 2,0, 0 },{ 4, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ + { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 5, 1,0, 1 },{ 4, 1,0, 1 }, { 0, 0,0, 0 }} }, /* 3 */ + { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ + { {BNS_VT_C_POS, 0+1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ +}; +/* +#define cn_bits_NPNP MAKE_CN_BITS(cn_bits_N, cn_bits_P, cn_bits_N, cn_bits_P) +*/ +/********************* end new ********************************************************************/ + + +/************************************************************************************************** + 5(+) + // (NPN) + 4=====3 ||| init charge=0 + \ / -N- + 2 | + ||| + 1 X(V), X+(V+1), X(V+2); V=valence +*/ +MY_CONST C_NODE cnNPN[5] = { + /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ + { {BNS_VERT_TYPE_ATOM, 2, 2, 1}, {{ 2, 2,0, 2 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ + { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 3, 1,0, 0 },{ 4, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ + { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 5, 1,0, 1 },{ 4, 1,0, 1 }, { 0, 0,0, 0 }} }, /* 3 */ + { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ + { {BNS_VT_C_POS, 1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ +}; +/* +#define cn_bits_NPN MAKE_CN_BITS(cn_bits_N, cn_bits_P, cn_bits_N, 0) +*/ +/************************************************************************************************** + 5(+.) + / (PNP) + 4=====3 ||| init charge=0 + \ / -Cl- + 2 /\ + ||| + 1 X+(V), X(V+1), X+(V+2); V=valence +*/ +MY_CONST C_NODE cnPNP[5] = { + /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ + { {BNS_VERT_TYPE_ATOM, 2, 2, 1}, {{ 2, 2,0, 2 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ + { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 3, 1,0, 0 },{ 4, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ + { {BNS_VT_CHRG_STRUCT, 1, 1, 3}, {{ 5, 1,0, 0 },{ 4, 1,0, 1 }, { 0, 0,0, 0 }} }, /* 3 */ + { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ + { {BNS_VT_C_POS, 1+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ +}; +/* +#define cn_bits_PNP MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_P, 0) +*/ +/************************************************************************************************** + + (MNP) + \ / init charge=0 + N(.) + 3(-) 2(+) / \ + \ // + 1(.) X-(V), X(V+1), X+(V+2); V=valence +*/ +MY_CONST C_NODE cnMNP[3] = { + /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ + { {BNS_VERT_TYPE_ATOM, 2, 1, 2}, {{ 2, 1,0, 1 },{ 3, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ + { {BNS_VT_C_POS, 1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ + { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} } /* 3 */ +}; +/* +#define cn_bits_MNP MAKE_CN_BITS(cn_bits_M, cn_bits_N, cn_bits_P, 0) +*/ +#ifdef NEVER +/************************** not used ************************************************************** + (PNM) + 5(-) 4(+) \\ / + \ // B(.) init charge=0 + 3 2 / \ + \\ / + 1(.) X+(V), X(V+1), X+(V+2); V=valence +*/ +MY_CONST C_NODE cnPNM[5] = { + /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ + { {BNS_VERT_TYPE_ATOM, 2, 1, 2}, {{ 2, 1,0, 0 },{ 3, 1,0, 1 }, { 0, 0,0, 0 }} }, /* 1 */ + { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 4, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ + { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 5, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 3 */ + { {BNS_VT_C_POS, 1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ + { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ +}; + +#define cn_bits_PNM MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_M, 0) + +#endif + +/************************************************************************************************** + 4(-) 3(+.) (PNM) + \ / ||| init charge=0 + 2 --P-- + ||| / \ + 1 X-(V), X(V+1), X+(V+2); V=valence +*/ +MY_CONST C_NODE cnPNM[4] = { + /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ + { {BNS_VERT_TYPE_ATOM, 2, 2, 1}, {{ 2, 2,0, 2 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ + { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 3, 1,0, 0 },{ 4, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ + { {BNS_VT_C_POS, 1+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 3 */ + { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ +}; /* explanaton of vCap: ^ ^ */ +/* additional dot:/ \ capacity of the edge to (+) or (-) vertex */ +/* +#define cn_bits_PNM MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_M, 0) +*/ +/************************************************************************************************** + 5(+C) + // init charge=0 + 6(-C) 4 + \ / (EN) E=either +1 or -1 + 3 | + || -C(.)- + 2 | + | + 1(.) X-(V), X+(V), X(V+1); V=valence +*/ +MY_CONST C_NODE cnEN[6] = { + /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ + { {BNS_VERT_TYPE_ATOM, 1, 0, 1}, {{ 2, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ + { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 3, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ + { {BNS_VT_CHRG_STRUCT, 1, 1, 3}, {{ 4, 1,0, 0 },{ 6, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 3 */ + { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 5, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ + { {BNS_VT_C_POS_C, 0+1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ + { {BNS_VT_C_NEG_C, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} } /* 6 */ +}; +/* +#define cn_bits_EN MAKE_CN_BITS(cn_bits_P | cn_bits_M, cn_bits_N, 0, 0) +*/ +/************************************************************************************************** + 5(-) + / (NMN) init charge=0 + 4=====3 ||| + \ / -X- + 2 /\ + ||| + 1 X(V), X-(V+1), X(V+2); V=valence +*/ +MY_CONST C_NODE cnNMN[5] = { + /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ + { {BNS_VERT_TYPE_ATOM, 2, 2, 1}, {{ 2, 2,0, 2 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ + { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 3, 1,0, 0 },{ 4, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ + { {BNS_VT_CHRG_STRUCT, 1, 1, 3}, {{ 5, 1,0, 0 },{ 4, 1,0, 1 }, { 0, 0,0, 0 }} }, /* 3 */ + { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ + { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} } /* 5 */ +}; +/* +#define cn_bits_NMN MAKE_CN_BITS(cn_bits_N, cn_bits_M, cn_bits_N, 0) +*/ +/************************************************************************************************** + 4(+) + // (NE) E=either +1 or -1 + 5(-) 3 || + \ / -X- init charge=0 + 2 | + || + 1 X(V), X+(V+1), X-(V+1); V=valence +*/ +MY_CONST C_NODE cnNE[5] = { + /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ + { {BNS_VERT_TYPE_ATOM, 1, 1, 1}, {{ 2, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ + { {BNS_VT_CHRG_STRUCT, 1, 1, 3}, {{ 3, 1,0, 0 },{ 5, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ + { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 4, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 3 */ + { {BNS_VT_C_POS, 0+1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ + { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} } /* 5 */ +}; +/* +#define cn_bits_NE MAKE_CN_BITS(cn_bits_N, cn_bits_P | cn_bits_M, 0, 0) +*/ +/************************************************************************************************** +6(-) 5(+) + \ // (NEN) + 4=====3 ||| init charge=0 + \ / -X- + 2 | + ||| + 1 X(V), X+(V+1), X-(V+1), X(V+2); V=valence +*/ +MY_CONST C_NODE cnNEN[6] = { + /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ + { {BNS_VERT_TYPE_ATOM, 2, 2, 1}, {{ 2, 2,0, 2 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ + { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 3, 1,0, 0 },{ 4, 1,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ + { {BNS_VT_CHRG_STRUCT, 2, 2, 3}, {{ 5, 1,0, 1 },{ 4, 1,0, 1 }, { 0, 0,0, 0 }} }, /* 3 */ + { {BNS_VT_CHRG_STRUCT, 1, 1, 3}, {{ 6, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 4 */ + { {BNS_VT_C_POS, 0+1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 5 */ + { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 6 */ +}; +/* +#define cn_bits_NEN MAKE_CN_BITS(cn_bits_N, cn_bits_M | cn_bits_N, cn_bits_N, 0) +*/ +/*=======================================================*/ +/************************************************************************************************** + (NP) + || + -X- init charge=0 + 2(+) | + || + 1 X(V), X+(V+1); V=valence +*/ +MY_CONST C_NODE cnNP[2] = { + /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ + { {BNS_VERT_TYPE_ATOM, 1, 1, 1}, {{ 2, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ + { {BNS_VT_C_POS, 0+1, 1, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ +}; +/* +#define cn_bits_NP MAKE_CN_BITS(cn_bits_N, cn_bits_P, 0, 0) +*/ +/************************************************************************************************** + (PN) + 3(+.) || init charge=0 [because cap(+)-flow(+)-Delta=1-0-1=0] + | -X- + 2 | + || + 1 X+(V), X(V+1); V=valence +*/ +MY_CONST C_NODE cnPN[3] = { + /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ + { {BNS_VERT_TYPE_ATOM, 1, 1, 1}, {{ 2, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ + { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 3, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ + { {BNS_VT_C_POS, 1+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 3 */ +}; +/* +#define cn_bits_PN MAKE_CN_BITS(cn_bits_P, cn_bits_N, 0, 0) +*/ +/************************************************************************************************** + (NM) + 3(-) || init charge=0 + | -X- + 2 | + || + 1 X(V), X-(V+1); V=valence +*/ +MY_CONST C_NODE cnNM[3] = { + /* vertex type vCap vFlow; val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ + { {BNS_VERT_TYPE_ATOM, 1, 1, 1}, {{ 2, 1,0, 1 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ + { {BNS_VT_CHRG_STRUCT, 1, 1, 2}, {{ 3, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ + { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 3 */ +}; +/* +#define cn_bits_NM MAKE_CN_BITS(cn_bits_N, cn_bits_M, 0, 0) +*/ +/************************************************************************************************** + (MN) + | + -X- init charge=0 + 2(-) | + | + 1(.) X-(V), X(V+1); V=valence +*/ +MY_CONST C_NODE cnMN[2] = { + /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ + { {BNS_VERT_TYPE_ATOM, 1, 0, 1}, {{ 2, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ + { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ +}; +/* +#define cn_bits_MN MAKE_CN_BITS(cn_bits_M, cn_bits_N, 0, 0) +*/ +/************************************************************************************************** + (P) + | + -X- init charge=0 + 2(+.) | + | + 1 X+(V); V=valence; all chemical (real) bonds to X have cap=0 +*/ +MY_CONST C_NODE cnP_[2] = { + /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ + { {BNS_VERT_TYPE_ATOM, 0, 0, 1}, {{ 2, 1,1, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ + { {BNS_VT_C_POS, 1+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ +}; +/* +#define cn_bits_P_ MAKE_CN_BITS(cn_bits_P, 0, 0, 0) +*/ +#ifdef NEVER +/************************************************************************************************** + (M) + | + -X- init charge=-1 on atom + 2(-) | + | + 1(.) X+(V); V=valence; all chemical (real) bonds to X have cap=0 +*/ +MY_CONST C_NODE cnM_[2] = { + /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ + { {BNS_VERT_TYPE_ATOM, 1, 0, 1}, {{ 2, 1,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ + { {BNS_VT_C_NEG, 0+1, 0, 1}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 2 */ +}; +#endif + +MY_CONST C_NODE cnM_[1] = { + /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ + { {BNS_VERT_TYPE_ATOM, 0, 0, 0}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ +}; +/* +#define cn_bits_M_ MAKE_CN_BITS(cn_bits_M, 0, 0, 0) +*/ +/************************************************************************************************** + + + -X- init charge=0 + | + + 1 X(V); V=valence; +*/ +MY_CONST C_NODE cnN_[1] = { + /* vertex type vCap vFlow val; neigh cap flow; neigh cap flow; neigh cap flow vertex */ + { {BNS_VERT_TYPE_ATOM, 0, 0, 0}, {{ 0, 0,0, 0 },{ 0, 0,0, 0 }, { 0, 0,0, 0 }} }, /* 1 */ +}; +/* +#define cn_bits_N_ MAKE_CN_BITS(cn_bits_N, 0, 0, 0) +*/ +/**************************************************************************************************/ + + +MY_CONST CN_LIST cnList[] = { + {cnPNPN, cn_bits_PNPN, 0, sizeof(cnPNPN)/sizeof(cnPNPN[0])}, /* 0 */ + {cnNPNP, cn_bits_NPNP, 0, sizeof(cnNPNP)/sizeof(cnNPNP[0])}, /* 1 */ + {cnNPN, cn_bits_NPN, 0, sizeof(cnNPN)/sizeof(cnNPN[0])}, /* 2 */ + {cnPNP, cn_bits_PNP, 0, sizeof(cnPNP)/sizeof(cnPNP[0])}, /* 3 */ + {cnMNP, cn_bits_MNP, 0, sizeof(cnMNP)/sizeof(cnMNP[0])}, /* 4 */ + {cnPNM, cn_bits_PNM, 0, sizeof(cnPNM)/sizeof(cnPNM[0])}, /* 5 */ + {cnEN, cn_bits_EN , 0, sizeof(cnEN)/sizeof(cnEN[0])}, /* 6 */ + {cnNMN, cn_bits_NMN, 0, sizeof(cnNMN)/sizeof(cnNMN[0])}, /* 7 */ + {cnNE, cn_bits_NE , 0, sizeof(cnNE)/sizeof(cnNE[0])}, /* 8 */ + {cnNEN, cn_bits_NEN, 0, sizeof(cnNEN)/sizeof(cnNEN[0])}, /* 9 */ + {cnNP, cn_bits_NP, 0, sizeof(cnNP)/sizeof(cnNP[0])}, /* 10 */ + {cnPN, cn_bits_PN, 0, sizeof(cnPN)/sizeof(cnPN[0])}, /* 11 */ + {cnNM, cn_bits_NM, 0, sizeof(cnNM)/sizeof(cnNM[0])}, /* 12 */ + {cnMN, cn_bits_MN, 0, sizeof(cnMN)/sizeof(cnMN[0])}, /* 13 */ + {cnP_, cn_bits_P_, 0, sizeof(cnP_)/sizeof(cnP_[0])}, /* 14 */ + {cnM_, cn_bits_M_, -1, sizeof(cnM_)/sizeof(cnM_[0])}, /* 15 */ + {cnN_, cn_bits_N_, 0, sizeof(cnN_)/sizeof(cnN_[0])}, /* 16 */ + {cnMe, cn_bits_Me, 0, sizeof(cnMe)/sizeof(cnMe[0])} /* 17 */ +}; + +#define cnListIndexMe (17) /* index of {cnMe, cn_bits_Me,... } element of cnList[] */ + +const int cnListNumEl = (int)(sizeof(cnList)/sizeof(cnList[0])); + + + +/**********************************************************************/ +void clear_t_group_info( T_GROUP_INFO *ti ) +{ + if ( !ti ) + { + return; + } else + { + T_GROUP *t_group = ti->t_group; + int max_num_t_groups = ti->max_num_t_groups; + AT_NUMB *tGroupNumber = ti->tGroupNumber; + int num_t_groups = ti->num_t_groups; + AT_NUMB *nEndpointAtomNumber = ti->nEndpointAtomNumber; + int nNumEndpoints = ti->nNumEndpoints; + AT_NUMB *nIsotopicEndpointAtomNumber = ti->nIsotopicEndpointAtomNumber; + int nNumIsotopicEndpoints = ti->nNumIsotopicEndpoints; + memset( ti, 0, sizeof(*ti) ); + if ( t_group ) + { + memset( t_group, 0, sizeof(t_group[0])*max_num_t_groups ); + } else + { + max_num_t_groups = 0; + } + if ( tGroupNumber ) + { + memset( tGroupNumber, 0, sizeof(tGroupNumber[0])*num_t_groups ); + } else + { + num_t_groups = 0; + } + if ( nEndpointAtomNumber ) + { + memset( nEndpointAtomNumber, 0, sizeof(nEndpointAtomNumber[0])*nNumEndpoints ); + } + else + { + nNumEndpoints = 0; + } + if ( nIsotopicEndpointAtomNumber ) + { + memset( nIsotopicEndpointAtomNumber, 0, sizeof(nIsotopicEndpointAtomNumber[0])*nNumIsotopicEndpoints ); + } + else + { + nNumIsotopicEndpoints = 0; + } + + ti->t_group = t_group; + ti->max_num_t_groups = max_num_t_groups; + ti->tGroupNumber = tGroupNumber; + ti->num_t_groups = num_t_groups; + ti->nEndpointAtomNumber = nEndpointAtomNumber; + ti->nNumEndpoints = nNumEndpoints; + ti->nIsotopicEndpointAtomNumber = nIsotopicEndpointAtomNumber; + + ti->nNumIsotopicEndpoints = nNumIsotopicEndpoints; + } + + return; +} + + +/****************************************************************************/ +int GetTgroupInfoFromInChI( T_GROUP_INFO *ti, + inp_ATOM *at, + AT_NUMB *endpoint, + INChI *pInChI ) +{ + int ret, i, j, k, itg, num_atoms, len_tg, bIso, num_t_groups; + AT_NUMB *tGroupNumber = NULL; + AT_NUMB *tSymmRank = NULL; + AT_NUMB *tiSymmRank = NULL; + AT_NUMB *tiGroupNumber = NULL; + + ret = 0; + + clear_t_group_info( ti ); + if ( pInChI && pInChI->lenTautomer > 1 && + pInChI->nTautomer && pInChI->nTautomer[0] > 0 ) + { + num_atoms = pInChI->nNumberOfAtoms; + bIso = pInChI->IsotopicAtom && pInChI->nNumberOfIsotopicAtoms; + num_t_groups = pInChI->nTautomer[0]; + len_tg = pInChI->lenTautomer - T_GROUP_HDR_LEN*pInChI->nTautomer[0] - 1; /* number of endpoints */ + + /* allocation ti->t_group */ + if ( ti->max_num_t_groups != num_atoms/2+1 || !ti->t_group ) + { + ti->max_num_t_groups = num_atoms/2+1; + if ( ti->t_group ) + inchi_free( ti->t_group ); + ti->t_group = (T_GROUP *)inchi_calloc( ti->max_num_t_groups, sizeof(ti->t_group[0])); + } + + /* allocation ti->tGroupNumber */ + if ( ti->num_t_groups != num_t_groups || !ti->tGroupNumber ) + { + ti->num_t_groups = num_t_groups; + if ( ti->tGroupNumber ) + inchi_free( ti->tGroupNumber ); + ti->tGroupNumber = (AT_NUMB *)inchi_calloc((ti->num_t_groups+1)*TGSO_TOTAL_LEN, sizeof(ti->tGroupNumber[0])); + } + + /* allocation ti->tGroupNumber */ + if ( len_tg != ti->nNumEndpoints || !ti->nEndpointAtomNumber ) + { + ti->nNumEndpoints = len_tg; + if ( ti->nEndpointAtomNumber ) + inchi_free( ti->nEndpointAtomNumber ); + ti->nEndpointAtomNumber = (AT_NUMB *)inchi_calloc(len_tg+1, sizeof(ti->nEndpointAtomNumber[0])); + } + + /* check */ + if ( !ti->t_group || !ti->tGroupNumber || !ti->nEndpointAtomNumber ) + { + ret = RI_ERR_ALLOC; + goto exit_function; + } + + tGroupNumber = ti->tGroupNumber; + tSymmRank = tGroupNumber + TGSO_SYMM_RANK * ti->num_t_groups; /* equivalence; cannot restore */ + tiSymmRank = tGroupNumber + TGSO_SYMM_IRANK * ti->num_t_groups; + tiGroupNumber = tGroupNumber + TGSO_SYMM_IORDER * ti->num_t_groups; + + + INCHI_HEAPCHK + + j = 1; /* index in pInChI->nTautomer[] */ + i = 0; /* index in ti->nEndpointAtomNumber[] */ + + for ( itg = 0; itg < pInChI->nTautomer[0]; itg ++ ) + { + len_tg = pInChI->nTautomer[j]; /* t-group length not including pInChI->nTautomer[j] */ + ti->t_group[itg].num[0] = pInChI->nTautomer[j+1]+pInChI->nTautomer[j+2]; /* num mobile H & (-) */ + ti->t_group[itg].num[1] = pInChI->nTautomer[j+2]; /* num mobile (-) */ + tGroupNumber[itg] = tiGroupNumber[itg] = itg; /* index */ + ti->t_group[itg].nGroupNumber = /*tSymmRank[itg] = tiSymmRank[itg] =*/ itg+1; /* t-group number */ + j += T_GROUP_HDR_LEN; /* skip t-group header */ + len_tg -= T_GROUP_HDR_LEN-1; + + ti->t_group[itg].nNumEndpoints = len_tg; + ti->t_group[itg].nFirstEndpointAtNoPos = i; + + for( ; 0 < len_tg --; j ++, i ++ ) + { + k = ti->nEndpointAtomNumber[i] = pInChI->nTautomer[j]-1; + if ( at ) + { + at[k].endpoint = itg+1; + } + if ( endpoint ) + { + endpoint[k] = itg+1; + } + } + } + + if ( i != ti->nNumEndpoints ) + { + ret = RI_ERR_PROGR; + } + + INCHI_HEAPCHK + } + +exit_function: + return ret; +} + + +/****************************************************************************/ +int FillOutpStructEndpointFromInChI( INChI *pInChI, AT_NUMB **pEndpoint ) +{ + int num_at = pInChI->nNumberOfAtoms; + AT_NUMB *endpoint = *pEndpoint; + int itg, i, j, k, len_tg; + + if ( !endpoint && !(endpoint = (AT_NUMB*) inchi_malloc(num_at * sizeof(endpoint[0]) ) ) ) + { + return RI_ERR_ALLOC; + } + + memset( endpoint, 0, num_at * sizeof(endpoint[0]) ); + if ( pInChI->lenTautomer <= 1 || !pInChI->nTautomer ) + { + goto exit_function; + } + + j = 1; /* index in pInChI->nTautomer[] */ + i = 0; /* index in ti->nEndpointAtomNumber[] */ + for ( itg = 0; itg < pInChI->nTautomer[0]; itg ++ ) + { + len_tg = pInChI->nTautomer[j]; /* t-group length not including pInChI->nTautomer[j] */ + j += T_GROUP_HDR_LEN; /* skip t-group header */ + len_tg -= T_GROUP_HDR_LEN-1; + /* ti->t_group[itg].nNumEndpoints = len_tg; */ + for( ; 0 < len_tg --; j ++, i ++ ) + { + k = pInChI->nTautomer[j]-1; + endpoint[k] = itg+1; + } + } + +exit_function: + *pEndpoint = endpoint; + + return 0; +} + + +/************************************************************************************/ +int cmp_charge_val( const void *a1, const void *a2, void *p ) +{ + const CHARGE_VAL *p1 = (const CHARGE_VAL *) a1; + const CHARGE_VAL *p2 = (const CHARGE_VAL *) a2; + int diff; + + if ( diff = (int)p1->nValence - (int)p2->nValence ) /* smaller valence first */ + return diff; + if ( diff = abs((int)p1->nCharge) - abs((int)p2->nCharge )) /* smaller abs charge first */ + return diff; + if ( diff = (int)p2->nCharge - (int)p1->nCharge ) /* (+) first, (-) second */ + return diff; + return (int)p1->nValenceOrderingNumber - (int)p2->nValenceOrderingNumber; +} + + +/************************************************************************************/ +int bMayBeACationInMobileHLayer( inp_ATOM *at, VAL_AT *pVA, int iat, int bMobileH ) +{ + static const char szEl[] = "N;P;O;S;Se;Te;"; + static const char cVal[] = {4,4,3,3, 3, 3, 0}; + static char en[8]; + static int ne; + int ne2; + int i, j, neigh; + char *p; + if ( !bMobileH || !at[iat].num_H ) { + return 1; + } + if ( !ne ) + { /* one time initialization */ + const char *b, *e; + int len; + char elname[ATOM_EL_LEN]; + ne2=0; + for ( b = szEl; e = strchr( b, ';'); b = e+1 ) { + len = (int) (e-b); + memcpy( elname, b, len ); + elname[len] = '\0'; + en[ne2++] = get_periodic_table_number( elname ); + } + en[ne2] = '\0'; + ne=ne2; + } + if ( p = (char *)memchr( en, at[iat].el_number, ne ) ) { + i = (int) (p - en); + /* >B(-)< exception */ + if ( at[iat].valence + at[iat].num_H <= cVal[i] ) { + for ( j = 0; j < at[iat].valence; j ++ ) { + neigh = at[iat].neighbor[j]; + if ( at[neigh].valence == 4 && at[neigh].chem_bonds_valence == 4 && !at[neigh].num_H && + pVA[neigh].cNumValenceElectrons == 3 && pVA[neigh].cPeriodicRowNumber == 1 ) { + return 1; + } + } + return 0; + } + } + return 1; +} + + +/************************************************************************************/ +int clean_charge_val( struct tagCANON_GLOBALS *pCG, CHARGE_VAL *pChargeVal, int len, inp_ATOM *atom, VAL_AT *pVA, + int iat, int bIsMetal, int bMobileH, AT_NUMB *endpoint ) +{ + inp_ATOM *at = atom + iat; + int nPeriodicNum = at->el_number; + int num_bonds = at->valence; + int min_valence = at->valence + at->num_H; + /* in fixed-H case treat tautomeric -O as tautomeric to avoid #O(+) */ + int bTautomeric = (at->endpoint != 0); + int bFixedHTautomeric = !bMobileH && (endpoint && endpoint[iat] && + pVA[iat].cNumValenceElectrons == 6 && 1==num_bonds && + !at->num_H && !bIsMetal); + /* int bIsMetal = is_el_a_metal( nPeriodicNum );*/ + int bDoNotAddH = if_skip_add_H( nPeriodicNum ); + int nPeriod, nNumEqAbsCharges; + int nNumValenceEl = get_sp_element_type( nPeriodicNum, &nPeriod ) - 1; + + int i, j; + + if ( !len ) + return len; + + insertions_sort( pCG, pChargeVal, len, sizeof(pChargeVal[0]), cmp_charge_val ); + + /* metals -- very preliminary code */ + if ( bIsMetal && bDoNotAddH ) { + /* keep the 1st found */ + return inchi_min( 1, len ); + } + /* Mobile-H layer cannot have H on positively charged N, P (all IV), O, S, Se, Te (all III) */ + + /* + if ( abs( pChargeVal[0].nCharge ) > 1 && pChargeVal[0].nValence >= min_valence ) { + return inchi_min( 1, len ); + } + */ + nNumEqAbsCharges = 0; + for ( i = j = 0; i < len && j < (nNumEqAbsCharges? 3+nNumEqAbsCharges:4); i ++ ) + { + /* for now accept only charge = 0, -1, +1 */ + if ( abs( pChargeVal[i].nCharge ) > 1 ) + { + continue; + } + if ( BOND_TYPE_TRIPLE + BOND_TYPE_DOUBLE * (min_valence - 1) < pChargeVal[i].nValence ) + { + continue; /* not more than one triple and the rest - double bonds per atom */ + } + if ( (bTautomeric || j && bFixedHTautomeric) && pChargeVal[i].nCharge < 0 ) + { + continue; /* negative charge must be included in the tautomeric group */ + } + if ( (bTautomeric || bFixedHTautomeric) && pChargeVal[i].nCharge > 0 ) + { + continue; /* positive charge for now cannot reach a tautomeric group */ + } + if ( j && !bMayBeACationInMobileHLayer( atom, pVA, iat, bMobileH ) && pChargeVal[i].nCharge > 0 ) + { + if ( i+1 < len && + pChargeVal[i].nValence == pChargeVal[i+1].nValence && + pChargeVal[i].nCharge == -pChargeVal[i+1].nCharge ) + { + /* (-) if exists is always after (+) */ + i += 1; /* also skip the next element */ + } + continue; /* in case of Mobile-H, a hydrogen cannot be on a (+)-charged heteroatom */ + } + + /* accept same valence opposite charges only for C and its group in Periodic Table */ + if ( j && !bTautomeric && + pChargeVal[i].nValence == pChargeVal[j-1].nValence && + pChargeVal[i].nCharge == -pChargeVal[j-1].nCharge ) + { + if ( nNumValenceEl == VALUE_OCTET/2 && pChargeVal[i].nCharge && !nNumEqAbsCharges ) + { + pChargeVal[j ++] = pChargeVal[i]; + nNumEqAbsCharges ++; + } + continue; + } + /* do not accept valence=5 for neutral NHn in case of not Mobile-H 2005-01-26 ???? */ + if ( nNumValenceEl == 5 && nPeriod == 1 && at->num_H && + j && !bMobileH && + pChargeVal[i].nValence == 5 && !pChargeVal[i].nCharge ) + { + continue; + } + /* do not accept gaps in allowed valences */ + if ( j && pChargeVal[i].nValence > pChargeVal[j-1].nValence+1 ) + { + break; + } + pChargeVal[j ++] = pChargeVal[i]; + } + len = j; + if ( !nNumEqAbsCharges && num_bonds < 3 && len == 4 ) + { + len --; /* prohibit =S# where # is a triple bond */ + } + return len; +} + + +/**************************************************************************** +int GetAtomRestoreInfo( CANON_GLOBALS *pCG, inp_ATOM *atom, int iat, VAL_AT *pVArray ) + + + pVA->cDoNotAddH + pVA->cMetal + pVA->cNumValenceElectrons + pVA->cPeriodicRowNumber + pVA->cInitFreeValences + pVA->cnListIndex = index+1 + +return value: + -1 => error + 0 => do not know what to do; leave the atom unchanged + 1 => success +****************************************************************************/ +int GetAtomRestoreInfo( struct tagCANON_GLOBALS *pCG, + inp_ATOM *atom, int iat, VAL_AT *pVArray, + ICHICONST SRM *pSrm, int bMobileH, AT_NUMB *endpoint ) +{ +/* #defines from util.c */ +#define MIN_ATOM_CHARGE (-2) +#define MAX_ATOM_CHARGE 2 +#define NEUTRAL_STATE (-MIN_ATOM_CHARGE) +#define NUM_ATOM_CHARGES (MAX_ATOM_CHARGE - MIN_ATOM_CHARGE + 1) +#define MAX_NUM_VALENCES 5 /* max. number + 1 to provide zero termination */ + + int i, j, j2, k, k2, charge, cur_charge, num_non_bonding_electrons; + int nNumStates, nNumSelectedStates, num_H, num_bonds; + int nOctetNeutralValenceExcess, nFirstNeutralValenceExcess; + int nFoundNeutralValenceExcess, nFoundNeutralValenceOrdNumber; + int nLastFoundValenceOrdNumber, nLastFoundValenceState; + int cn_bits, cn_bits_array[5], len_cn_bits_array; + inp_ATOM *at = atom+iat; + VAL_AT *pVA = pVArray + iat; + int nPeriodicNum = at->el_number; + int cur_chem_valence, cur_chem_valence_fixed, min_chem_valence, known_chem_valence; + int metal_bonds_chem_valence, not_metal_bonds_chem_valence, alt_bonds_delta_valence, bonds_chem_valence, bond_type; + CHARGE_VAL ChargeVal[NUM_ATOM_CHARGES*MAX_NUM_VALENCES]; + + memset( ChargeVal, 0, sizeof(ChargeVal) ); + + pVA->cDoNotAddH = if_skip_add_H( nPeriodicNum ); /* InChI never adds H to this atom */ + /*pVA->cMetal = is_el_a_metal( nPeriodicNum );*/ /* the atom is a metal */ + + /* count bonds to metal atoms; metals have already been marked */ + metal_bonds_chem_valence = not_metal_bonds_chem_valence = alt_bonds_delta_valence = 0; + + if ( pVA->cMetal ) + { + j = at->valence; /* all bonds to metal */ + for ( i = k = j2 = k2 = 0; i < at->valence; i ++ ) + { + bond_type = (at->bond_type[i] & BOND_TYPE_MASK); + if ( bond_type <= BOND_TYPE_TRIPLE ) + { + metal_bonds_chem_valence += inchi_max(BOND_TYPE_SINGLE, bond_type); + } + else + { + metal_bonds_chem_valence += BOND_TYPE_SINGLE; + k ++; /* count alternating bonds */ + } + } + } + else + { + for ( i = j = j2 = k = k2 = 0; i < at->valence; i ++ ) + { + bond_type = (at->bond_type[i] & BOND_TYPE_MASK); + if ( pVArray[ (int)at->neighbor[i] ].cMetal ) + { + j ++; /* number of bonds to metal atoms */ + if ( bond_type <= BOND_TYPE_TRIPLE ) + { + metal_bonds_chem_valence += inchi_max(BOND_TYPE_SINGLE, bond_type); + } + else + { + metal_bonds_chem_valence += BOND_TYPE_SINGLE; + k ++; /* count alternating bonds */ + } + } + else + { + j2 ++; + if ( bond_type <= BOND_TYPE_TRIPLE ) + { + not_metal_bonds_chem_valence += inchi_max(BOND_TYPE_SINGLE, bond_type); + } + else + { + not_metal_bonds_chem_valence += BOND_TYPE_SINGLE; + k2 ++; /* count alternating bonds */ + } + } + } + } + + bonds_chem_valence = metal_bonds_chem_valence + not_metal_bonds_chem_valence; + + if ( at->chem_bonds_valence > bonds_chem_valence ) + { + if ( at->chem_bonds_valence - bonds_chem_valence > 1 ) + { + at->chem_bonds_valence = bonds_chem_valence + 1; /* should not happen */ + } + alt_bonds_delta_valence = at->chem_bonds_valence - bonds_chem_valence; + } + + pVA->cNumBondsToMetal = j; + + if ( nPeriodicNum == EL_NUMBER_H ) + { + /* ignore bridging H; ??? later add ??? */ + return 0; + } + + num_H = at->num_H; + num_bonds = at->valence; + + if ( !num_bonds && !num_H ) + { + return 0; /* do not know the answer: isolated atom */ + } + /* at the beginning all bonds are single */ + min_chem_valence = num_bonds + num_H; + cur_chem_valence = bonds_chem_valence + alt_bonds_delta_valence + num_H; /* includes double & alternating bond contribution */ + + /* number of non-bonding electrons in case of all single bonds */ + num_non_bonding_electrons = (int)pVA->cNumValenceElectrons - min_chem_valence; + /* Octet rule: charge = bonds_valence + NumValenceElectrons - 8 */ + charge = min_chem_valence + (int)pVA->cNumValenceElectrons - VALUE_OCTET; /* wrong */ + + /* typical (ad hoc) minimal neutral valence */ + known_chem_valence = ( pVA->cNumValenceElectrons > VALUE_OCTET/2 )? + VALUE_OCTET - pVA->cNumValenceElectrons : + pVA->cNumValenceElectrons; + /* excess of typical valence over all-single-bonds valence */ + nOctetNeutralValenceExcess = known_chem_valence - min_chem_valence; + /* (NB=num.bonds, NV=neutral valence, NVX=neutral valence excess, LFVS=last found valence state, val.=valence) + + element NB knownFst octet Last octetNVX firstNVX foundNVX chargeLFVS LFVS + valence val. NV>= + + -B 1 3 3 3 2 2 = 2 +2 + >B 2 3 3 3 1 1 = 1 +1 + >B- 3 3 3 3 0 0 = 0 0 + >B< 4 3 3 3 -1 -1 <> N/A -1 + + -C 1 4 4 4 3 3 = 3 N/A + >C 2 4 4 4 2 2 = 2 +2 (-2) + >C- 3 4 4 4 1 1 = 1 +1 (-1) + >C< 4 4 4 4 0 0 = 0 0 + C(V) 5 4 4 N/A -1 -1 <> N/A N/A + + -Si 1 4 4 4 3 3 = 3 N/A + >Si 2 4 4 4 2 2 = 2 +2 (-2) + >Si- 3 4 4 4 1 1 = 1 +1 (-1) + >Si- 4 4 4 4 0 0 = 0 0 + Si(V) 5 4 4 N/A -1 -1 <> N/A -1 + + -N 1 3 3 3 2 2 = 2 -2 + >N 2 3 3 3 1 1 = 1 -1 + >N- 3 3 3 3 0 0 = 0 0 (+2) + >N< 4 3 3 5 -1 -1 <> 1 +1 + N(V) 5 3 3 5 -2 -2 <> 0 0 + N(VI) 6 3 3 N/A -3 -3 <> N/A N/A + N(VII) 7 3 3 N/A -4 -4 <> N/A N/A + + -P 1 3 3 3 2 2 = 2 -2 + >P 2 3 3 3 1 1 = 1 -1 + >P- 3 3 3 3 0 0 = 0 0 (-2, +2) + >P< 4 3 3 5 -1 -1 <> 1 +1 (-1) + P(V) 5 3 3 5 -2 -2 <> 0 0 (-2) + P(VI) 6 3 3 N/A -3 -3 <> N/A -1 + P(VII) 7 3 3 N/A -4 -4 <> N/A -2 + P(VIII) 8 3 3 N/A -5 -5 <> N/A N/A + + -O 1 2 2 2 1 1 = 1 -1 + >O 2 2 2 2 0 0 = 0 0 + >O- 3 2 2 N/A -1 -1 <> N/A +1 + >O< 4 2 2 N/A -2 -2 <> N/A +2 + O(V) 5 2 2 N/A -3 -3 <> N/A +1 + O(VI) 6 2 2 N/A -4 -4 <> N/A N/A + + -S 1 2 2 2 1 1 = 1 -1 + >S 2 2 2 2 0 0 = 0 0 NPNP - prohibit + >S- 3 2 2 4 -1 -1 <> 1 +1 (-1) PNPN + >S< 4 2 2 4 -2 -2 <> 0 0 (+2) + S(V) 5 2 2 6 -3 -3 <> 1 +1 (-1) + S(VI) 6 2 2 6 -4 -4 <> 0 0 + S(VII) 7 2 2 N/A -5 -5 <> 0 -1 + S(VIII) 8 2 2 N/A -6 -6 <> N/A N/A + + -F 1 1 1 1 0 0 = 0 0 + >F 2 1 1 1 -1 -1 <> N/A +1 + >F- 3 1 1 1 -2 -2 <> N/A +2 + >F< 4 1 1 1 -3 -3 <> N/A N/A + F(V) 5 1 1 1 -4 -4 <> N/A +2 + F(VI) 6 1 1 1 -5 -5 <> N/A N/A + + -Cl 1 1 1 1 0 0 = 0 0 NPNP - prohibit + >Cl 2 1 1 3 -1 -1 <> 1 +1 PNPN - prohibit + >Cl- 3 1 1 3 -2 -2 <> 0 0 (+2) NPNP + >Cl< 4 1 1 5 -3 -3 <> 1 +1 PNPN + Cl(V) 5 1 1 5 -4 -4 <> 0 0 + Cl(VI) 6 1 1 7 -5 -5 <> 1 +1 + Cl(VII) 7 1 1 7 -6 -6 <> 0 0 + Cl(VIII) 8 1 1 N/A -7 -7 <> N/A N/A + + + NB = num_bonds+num_H + + knownFst valence = nFirstNeutralValenceExcess + min_chem_valence + octet val. = nOctetNeutralValenceExcess + min_chem_valence + Last NV>= = nFoundNeutralValenceExcess + min_chem_valence + + octetNVX = nOctetNeutralValenceExcess + firstNVX = nFirstNeutralValenceExcess + foundNVX = nFoundNeutralValenceExcess + + chargeLFVS = ChargeVal[nLastFoundValenceState].nCharge + + */ + /* minimal known neutral atom valence; different for Sn(2/4), Tl(1/3), Pb(2/4): (known/typical ad hoc) */ + known_chem_valence = get_el_valence( nPeriodicNum, 0, 0 ); + + if ( pSrm->bMetalAddFlower ) + { + /* bond orders of bonds to metal may be as they are (pSrm->nMetalInitBondOrder==1) + or decreased by one (pSrm->nMetalInitBondOrder==0) + nMetalInitBondOrder == nMetalMinBondOrder + nMetalInitEdgeFlow + */ + cur_chem_valence_fixed = cur_chem_valence - pVA->cNumBondsToMetal * (1-pSrm->nMetalInitBondOrder); + pVA->cInitOrigValenceToMetal = metal_bonds_chem_valence; + pVA->cInitValenceToMetal = metal_bonds_chem_valence - pVA->cNumBondsToMetal * (1-pSrm->nMetalInitBondOrder); + pVA->cInitFlowToMetal = pVA->cInitValenceToMetal - pVA->cNumBondsToMetal * pSrm->nMetalMinBondOrder; + if ( pVA->cMetal ) { + pVA->cInitFreeValences += alt_bonds_delta_valence; + } + if ( pSrm->nMetalInitEdgeFlow < pSrm->nMetalInitBondOrder - pSrm->nMetalMinBondOrder ) + { + /* single bond has zero initial flow + 2 radicals at incident atoms */ + if ( pVA->cInitFlowToMetal <= pVA->cNumBondsToMetal ) + { + if ( pVA->cMetal ) + { + pVA->cInitFreeValences += pVA->cInitFlowToMetal; + } + pVA->cInitFlowToMetal = 0; + } + else + { + if ( pVA->cMetal ) + { + pVA->cInitFreeValences += pVA->cNumBondsToMetal * (1 - pSrm->nMetalInitEdgeFlow); + } + pVA->cInitFlowToMetal -= pVA->cNumBondsToMetal * (1 - pSrm->nMetalInitEdgeFlow); + } + } + } + else + { + /* treat metal atoms as ordinary non-metal atoms */ + cur_chem_valence_fixed = cur_chem_valence; + pVA->cInitFlowToMetal = metal_bonds_chem_valence - pVA->cNumBondsToMetal; + pVA->cInitValenceToMetal = metal_bonds_chem_valence; + pVA->cInitOrigValenceToMetal = metal_bonds_chem_valence; + } + + + if ( pVA->cMetal && pSrm->bMetalAddFlower ) + { + pVA->cnListIndex = cnListIndexMe + 1; + /* + pVA->cInitOrigValenceToMetal += alt_bonds_delta_valence; + pVA->cInitValenceToMetal += alt_bonds_delta_valence; + pVA->cInitFreeValences = (pSrm->nMetalInitBondOrder + alt_bonds_delta_valence + - (pSrm->nMetalMinBondOrder + pSrm->nMetalInitEdgeFlow)) * pVA->cNumBondsToMetal; + */ + return 0; /* metal */ + } + + if ( !known_chem_valence ) + { + /* a noble gas like He, Ne, ... */ + pVA->cInitFreeValences = at->chem_bonds_valence - at->valence; + return TREAT_ATOM_AS_METAL; /* do not know anything about this atom; needs 2nd pass */ + } + + nFirstNeutralValenceExcess = known_chem_valence - min_chem_valence; + + nFoundNeutralValenceExcess = NO_VALUE_INT; + nFoundNeutralValenceOrdNumber = NO_VALUE_INT; + nLastFoundValenceOrdNumber = NO_VALUE_INT; + nLastFoundValenceState = NO_VALUE_INT; + + /* find the lowest known valence >= all-single-bonds valence */ + for ( cur_charge = MIN_ATOM_CHARGE, nNumStates = 0; cur_charge <= MAX_ATOM_CHARGE; cur_charge ++ ) + { + for ( i = 0; i < MAX_NUM_VALENCES; i ++ ) + { + known_chem_valence = get_el_valence( nPeriodicNum, cur_charge, i ); + if ( cur_chem_valence_fixed > known_chem_valence || !known_chem_valence ) + { + continue; /* known valence < all-single-bonds valence */ + } + if ( BOND_TYPE_TRIPLE + BOND_TYPE_DOUBLE * (num_bonds - 1) + num_H < known_chem_valence ) + { + continue; /* not more than one triple and the rest - double bonds per atom */ + } + + /* keep all found */ + ChargeVal[nNumStates].nValence = known_chem_valence; + ChargeVal[nNumStates].nCharge = cur_charge; + ChargeVal[nNumStates].nValenceOrderingNumber = i; + if ( !cur_charge && nFoundNeutralValenceExcess == NO_VALUE_INT ) + { + /* neutral state; compare to the lowest typical valence */ + nFoundNeutralValenceExcess = known_chem_valence - min_chem_valence; + nFoundNeutralValenceOrdNumber = i; + } + if ( min_chem_valence == known_chem_valence ) + { + if ( nLastFoundValenceState == NO_VALUE_INT ) + { + /* accept the first found */ + nLastFoundValenceState = nNumStates; + } + else + if ( abs( ChargeVal[nLastFoundValenceState].nCharge ) >= abs( cur_charge ) ) + { + /* accept smaller abs(charge); if abs(charges) are same, accept (+) */ + nLastFoundValenceState = nNumStates; + } + } + + nNumStates ++; + } + } + + /***********************************************************************************/ + /* select only appropriate charge & valence so that a suitable ChargeStruct exists */ + /***********************************************************************************/ + + nNumSelectedStates = clean_charge_val( pCG, ChargeVal, nNumStates, atom, + pVArray, iat, pVA->cMetal, + bMobileH, endpoint ); + + if ( !nNumSelectedStates ) + { + return TREAT_ATOM_AS_METAL; /* nothing to do */ + } + + /***********************************************************************************/ + /* Find an appropriate ChargeStruct index for the ChargeVal found */ + /***********************************************************************************/ + cn_bits = 0; + memset( cn_bits_array, 0, sizeof(cn_bits_array) ); + /***** set bits identifying a suitable ChargeStruct ******/ + for ( i = len_cn_bits_array = 0; i < nNumSelectedStates && len_cn_bits_array < 4; i ++ ) + { + switch( ChargeVal[i].nCharge ) + { + case -1: + cn_bits_array[len_cn_bits_array] |= cn_bits_M; /* Minus 1 */ + break; + case 0: + cn_bits_array[len_cn_bits_array] |= cn_bits_N; /* Neutral */ + break; + case 1: + cn_bits_array[len_cn_bits_array] |= cn_bits_P; /* Plus 1 */ + break; + default: + return RI_ERR_PROGR; /* program error */ + } + if ( i+1 < nNumSelectedStates && + ChargeVal[i].nValence == ChargeVal[i+1].nValence && + ChargeVal[i].nCharge && + ChargeVal[i].nCharge == -ChargeVal[i+1].nCharge ) + { + ; /* add opposite charge to the same element of cn_bits_array[] */ + } + else + { + len_cn_bits_array ++; + } + } + if ( !len_cn_bits_array || len_cn_bits_array > 4 ) + { + return RI_ERR_PROGR; /* program error */ + } + /* accommodate added 4-state ChargeStruct: +/- cannot be in case of 4 states */ + if ( len_cn_bits_array + 1 == nNumSelectedStates && nNumSelectedStates == 4 ) + { + len_cn_bits_array --; + nNumSelectedStates --; + cn_bits_array[len_cn_bits_array] = 0; + } + /* fix for terminal hydrogenless -C as in isocyano or CO: there is no just cnE_[] ChargeStruct */ + if ( len_cn_bits_array == 1 && + cn_bits_array[0] == (cn_bits_P | cn_bits_M) && + ChargeVal[0].nValence + 1 > BOND_TYPE_TRIPLE + BOND_TYPE_DOUBLE * (num_bonds - 1) + num_H ) + { + cn_bits_array[len_cn_bits_array ++] = cn_bits_N; + ChargeVal[nNumSelectedStates].nValence = ChargeVal[nNumSelectedStates-1].nValence; + ChargeVal[nNumSelectedStates].nCharge = 0; + ChargeVal[nNumSelectedStates].nValenceOrderingNumber = 0; + } + +make_cn_bits: + cn_bits = MAKE_CN_BITS(cn_bits_array[0], cn_bits_array[1], cn_bits_array[2], cn_bits_array[3]); + + /*********** find ChargeStructure **************/ + for ( i = 0, j = -1; i < cnListNumEl; i ++ ) + { + if ( cnList[i].bits == cn_bits ) + { + j = i; + break; /* found */ + } + } + + if ( j < 0 ) + { + /* ChargeStructure was not found */ + if ( 1 < len_cn_bits_array && len_cn_bits_array + 1 == nNumSelectedStates ) { + /* a pair of opposite charges was combined */ + len_cn_bits_array --; + cn_bits_array[len_cn_bits_array] = 0; + goto make_cn_bits; + } + else if ( nNumSelectedStates == 4 ) + { + /* reduce number of states */ + len_cn_bits_array --; + cn_bits_array[len_cn_bits_array] = 0; + nNumSelectedStates --; + goto make_cn_bits; + } + return RI_ERR_PROGR; /* charge structure not found */ + } + + /********** ChargeStructure has been found **********/ + pVA->cnListIndex = j+1; /* charge structure index + 1 */ + pVA->cInitCharge = cnList[j].nInitialCharge; + /********** Calculate "Free Valence" ****************/ +#if ( ALLOW_METAL_BOND_ZERO == 1 ) + +#if ( INIT_METAL_BOND_ZERO == 1 ) + if ( pVA->cMetal ) + { + j = 0; + } + else + { + j = ChargeVal[0].nValence - cur_chem_valence_fixed; + } +#else + j = ChargeVal[0].nValence - cur_chem_valence_fixed; +#endif + +#else + j = ChargeVal[0].nValence - cur_chem_valence_fixed; +#endif + if ( j < 0 ) + { + return RI_ERR_PROGR; /* program error */ + } + pVA->cInitFreeValences = j; /* number of initial unsatisfied valences; should be combined with */ + /* (cap - flow) of vertex=0 in the charge structure[pVA->cnListIndex-1] */ + return 1; /* success */ + +#undef MIN_ATOM_CHARGE +#undef MAX_ATOM_CHARGE +#undef NEUTRAL_STATE +#undef NUM_ATOM_CHARGES +#undef MAX_NUM_VALENCES +} + + +#ifdef NEVER +/****************************************************************************/ +int get_bonds_valences( int nPeriodicNum, int bonds_valence, int num_H, VAL_AT *pVA ) +{ + int i, j, charge, chem_valence, known_chem_valence; +#define MAX_NUM_VALENCES 5 /* defined in util.c */ + + memset( pVA, 0, sizeof( pVA[0] ) ); + + if ( !bonds_valence && !num_H ) + return 0; /* do not know the answer */ + + chem_valence = bonds_valence + num_H; + for ( charge = VAL_MIN_CHARGE; charge <= VAL_MAX_CHARGE; charge ++ ) { + for ( i = 0, j = 0; i < MAX_NUM_VALENCES, j < VAL_NUMBER; i ++ ) { + if ( chem_valence <= (known_chem_valence = get_el_valence( nPeriodicNum, charge, i ) ) ) { + if ( !charge ) { + pVA->cValence[j][VAL_NEUTR_ORDER] = i+1; + } + pVA->cValence[j++][charge+VAL_BASE] = known_chem_valence - num_H; + } + } + } + pVA->cDoNotAddH = if_skip_add_H( nPeriodicNum ); + pVA->cMetal = is_el_a_metal( nPeriodicNum ); + return pVA->cValence[0][VAL_BASE]; /* 0 means do not know the answer */ +#undef MAX_NUM_VALENCES +} +#endif +/*********** calculate s or p-element type ************/ +int get_sp_element_type( int nPeriodicNumber, int *nRow ) +/* + num el + el neg + 1 => H ATYPE_H 1 1 21 + 2 => Li, Na, K, Rb, Cs, Fr ATYPE_Na 2 1 10 09 08 08 07 + 3 => Be, Mg, Ca, Sr, Ba, Ra ATYPE_Mg 3 2 15 12 10 10 09 + 4 => B, Al, Ga, In, Tl ATYPE_B 4 3 20 15 18 17 18 + 5 => C, Si, Ge, Sn, Pb ATYPE_C 5 4 25 18 18 18 18 + 6 => N, P, As, Sb, Bi ATYPE_N 6 5 30 21 20 19 19 + 7 => O, S, Se, Te, Po ATYPE_O 7 6 35 25 24 21 20 + 8 => F, Cl, Br, I, At ATYPE_Cl 8 7 40 30 28 25 22 + +number of valence electrons = (type>1)? type-1: type + + */ +{ + int row = 0, type = 0; + if ( nPeriodicNumber == 1 ) + { + type = 1; /* H: 1 */ + row = 0; + } else if ( nPeriodicNumber == 2 ) + { + type = 0; row = 0; + } + else if ( nPeriodicNumber <= 10 ) + { + /* Li: 2, Be: 3, B: 4, C: 5, N: 6, O: 7, F: 8, Ne: 9; later subtract 1 */ + type = nPeriodicNumber-1; row = 1; + } + else if ( nPeriodicNumber <= 18 ) + { + type = nPeriodicNumber - 9; row = 2; + } + else if ( nPeriodicNumber <= 20 ) + { + type = nPeriodicNumber - 17; row = 3; + } + else if ( nPeriodicNumber <= 30 ) { + type = 0; row = 3; + } + else if ( nPeriodicNumber <= 36 ) { + type = nPeriodicNumber - 27; row = 3; + } + else if ( nPeriodicNumber <= 38 ) { + type = nPeriodicNumber - 35; row = 4; + } + else if ( nPeriodicNumber <= 48 ) { + type = 0; row = 4; + } + else if ( nPeriodicNumber <= 54 ) { + type = nPeriodicNumber - 45; row = 4; + } + else if ( nPeriodicNumber <= 56 ) { + type = nPeriodicNumber - 53; row = 5; + } + else if ( nPeriodicNumber <= 80 ) + { + type = 0; row = 5; + } + else if ( nPeriodicNumber <= 86 ) + { + type = nPeriodicNumber - 77; row = 5; + } + else if ( nPeriodicNumber <= 88 ) + { + type = nPeriodicNumber - 85; row = 6; + } + else + { + type = 0; row = 6; + } + + *nRow = row; + + return type==9? 0 : type; +} + + +/****************************************************************************/ +int ReallocTCGroups( ALL_TC_GROUPS *pTCGroups, int nAdd ) +{ + TC_GROUP *pTCGroup = (TC_GROUP *) inchi_malloc( sizeof(pTCGroup[0])*(pTCGroups->max_tc_groups + nAdd) ); + + if ( pTCGroup ) + { + if ( pTCGroups->num_tc_groups ) + { + memcpy( pTCGroup, pTCGroups->pTCG, sizeof(pTCGroup[0])*pTCGroups->num_tc_groups ); + } + memset( pTCGroup + pTCGroups->max_tc_groups, 0, sizeof(pTCGroup[0])*nAdd ); + if ( pTCGroups->pTCG ) + { + inchi_free( pTCGroups->pTCG ); + } + pTCGroups->pTCG = pTCGroup; + pTCGroups->max_tc_groups += nAdd; + return 0; + } + + return RI_ERR_ALLOC; +} + + +/****************************************************************************/ +int RegisterTCGroup( ALL_TC_GROUPS *pTCGroups, int nGroupType, int nGroupOrdNum, + int nVertexCap, int nVertexFlow, int nEdgeCap, int nEdgeFlow, int nNumEdges) +{ + int i, ret = 0; + + /* search */ + for ( i = 0; i < pTCGroups->num_tc_groups; i ++ ) + { + if ( pTCGroups->pTCG[i].type == nGroupType && + pTCGroups->pTCG[i].ord_num == nGroupOrdNum ) + { + break; + } + } + + if ( i == pTCGroups->num_tc_groups ) + { + /* add one more group */ + if ( pTCGroups->num_tc_groups == pTCGroups->max_tc_groups ) + { + ret = ReallocTCGroups( pTCGroups, INC_NUM_TCGROUPS ); + if ( ret ) + { + goto exit_function; + } + } + + ret = i+1; /* added new group */ + pTCGroups->num_tc_groups ++; + pTCGroups->pTCG[i].type = nGroupType; + pTCGroups->pTCG[i].ord_num = nGroupOrdNum; + } + + pTCGroups->pTCG[i].num_edges += nNumEdges; + + pTCGroups->pTCG[i].st_cap += nVertexCap; + pTCGroups->pTCG[i].st_flow += nVertexFlow; + + pTCGroups->pTCG[i].edges_cap += nEdgeCap; + pTCGroups->pTCG[i].edges_flow += nEdgeFlow; + +exit_function: + return ret; +} + + +/****************************************************************************/ +int nTautEndpointEdgeCap( inp_ATOM *at, VAL_AT *pVA, int i ) +{ + /* There are 3 sources of cap-flow = number of unsatisfied valences: + ----------------------------------------------------------------- + 1. pVA[i].cInitFreeValences + 2. pCN[0].v.cap - pCN[0].v.flow + 3. st[i].chem_bonds_valence - SUM(SINGLE, DOUBLE, TRIPLE bond orders) + Reasons: (a) This sum will not include 'ALTERN' bonds + (b) until now at[i].chem_bonds_valence was used as a + number of satisfied valences. In case of adjacent + stereobonds marked as BOND_TYPE_ALTERN the value of + at[i].chem_bonds_valence may be = at[i].valence+1. + 4. Since tautomerism is defined for a neutral atom, do not add + initial flows from the atom to the ChargeStruct + CORRECTION: tautomeric endpoints do not have ChargeStruct. + + */ + int j, k, nEdgeCap, bonds_valence, stereo_bond_excess_valence; + MY_CONST C_NODE *pCN = pVA[i].cnListIndex>0? cnList[pVA[i].cnListIndex-1].pCN:NULL; + + + /* 1: free valences to reach the minimum known atom valence */ + nEdgeCap = pVA[i].cInitFreeValences; + + /* 2: atom free valence in the ChargeStruct */ + if ( pCN ) + { + nEdgeCap += pCN[0].v.cap - pCN[0].v.flow; /* normally should not happen */ + } + + /* 3: atom free valence due to known from stereochemistry stereogenic bond types */ + /* + for ( j = 0, bonds_valence = 0; j < at[i].valence; j ++ ) + { + if ( at[i].bond_type[j] <= BOND_TYPE_TRIPLE ) + { + bonds_valence += at[i].bond_type[j]; + } + } + */ + + /* bonds > SINGLE are assumed fixed stereobonds; fixed bond cannot increase t-group edge flow */ + for ( stereo_bond_excess_valence=0, j = 0; j < MAX_NUM_STEREO_BONDS && at[i].sb_parity[j]; j ++ ) + { + k = at[i].sb_ord[j]; + if ( at[i].bond_type[k] < BOND_TYPE_TRIPLE ) + { + stereo_bond_excess_valence += at[i].bond_type[k] - BOND_TYPE_SINGLE; + } + } + + /* + bonds_valence = (at[i].chem_bonds_valence - bonds_valence) + (bonds_valence -at[i].valence - stereo_bond_excess_valence); + */ + bonds_valence = (at[i].chem_bonds_valence - at[i].valence) - stereo_bond_excess_valence; + + + /*---- add 1, 2, 3 ----*/ + if ( bonds_valence >= 0 ) + { + nEdgeCap += bonds_valence; + } + else + { + nEdgeCap = RI_ERR_PROGR; + } + + return nEdgeCap; +} + + +/****************************************************************************/ +/* If Metal flowers are allowed ( pSrm->bMetalAddFlower != 0), then: */ +/* */ +/* bond to a metal atom min_bond_order[i] = pSrm->nMetalMinBondOrder */ +/* taut endpoint - metal min_bond_order[i] = pSrm->nMetal2EndpointMinBondOrder */ +/* single bond to metal atom: initial_bond_order[i] = pSrm->nMetalInitBondOrder */ +/* n-order bond to metal atom initial_bond_order[i] = pSrm->nMetalInitBondOrder + n-1 */ +/* = bond_order[i]-BOND_TYPE_SINGLE+pSrm->nMetalInitBondOrder */ +/* single t-endpoint--atom bond initial_bond_order[i] = pSrm->nMetal2EndpointInitBondOrder */ +/* n-order t-endpoint--metal bond initial_bond_order[i] = pSrm->nMetal2EndpointInitBondOrder+n-1 */ +/* = bond_order[i]-BOND_TYPE_SINGLE+pSrm->nMetal2EndpointInitBondOrder*/ +/* */ +/* Exceptions from simple atom-metal conditions: */ +/* 1. Atom is a tautomeric endpoint: use pSrm->nMetal2Endpoint* instead of pSrm->nMetal* */ +/* 2. Atom is sp3-stereogenic and pSrm->bFixStereoBonds != 0: use atom-atom rules */ +/* 3. Atom has a sp2-stereo and pSrm->bFixStereoBonds != 0: use atom-atom rules */ +/* */ +/* Atom-atom rules (applies to all atoms if pSrm->bMetalAddFlower=0) */ +/* */ +/* min_bond_order[i] = BOND_TYPE_SINGLE (BOND_TYPE_SINGLE = 1) */ +/* initial_bond_order[i] = bond_type[i] */ +/* */ +/* General rules: */ +/* initial_bond_flow[i] = initial_bond_order[i]-min_bond_order[i] */ +/* atom[k] initial_st_cap = at[k].chem_bonds_valence - SUM{i; initial_bond_order[i]} */ +/* bond_cap[i] = BOND_TYPE_TRIPLE - min_bond_order[i] */ +/* (reason: quadruple and higher order bonds are not allowed) */ +/* Exception: in case of metal-atom bond, if pSrm->nMetal2EndpointInitEdgeFlow = 0 AND */ +/* pSrm->nMetalInitBondOrder - pSrm->nMetalMinBondOrder = 1 then */ +/* reduce bond to metal order by 1 and increase st_cap of both neighbors by 1: */ +/* initial_bond_flow[i] --; metal_initial_st_cap += num_bonds; */ +/* ==== Note: ONLY the INCREASE is already included in pVA->cInitFreeValences of both atoms */ +/* */ +/* Notes: initial_st_cap does not include: */ +/* 1. atom[k] additional st_cap from ChargeStruct pCN[0].v.cap */ +/* 2. pVA[k].cInitFreeValences due to a difference between the smallest known valence and st_cap */ +/* */ +/* here k=atom at[k] index, */ +/* i=bond index; i = 0..at[k].valence; */ +/* SUM{i; M[i]} is a sum of M[i] over all i */ +/* bond_order[i] = at[k].bond_type[i] >= BOND_TYPE_SINGLE - input bond order */ +/****************************************************************************/ + +/***************** new *************************************************************************************/ + +int BondFlowMaxcapMinorder( inp_ATOM *atom, + VAL_AT *pVA, + ICHICONST SRM *pSrm, + int iat, + int ineigh, + int *pnMaxcap, + int *pnMinorder, + int *pbNeedsFlower ) +{ + + int nFlow, nMaxcap, nMinorder, nInitorder, bNeedsFlower = 0; + inp_ATOM *at = atom + iat; + int neigh = at->neighbor[ineigh]; + int bond_type = at->bond_type[ineigh] & BOND_TYPE_MASK; + int nMetal = (0 != pVA[iat].cMetal) + (0 != pVA[neigh].cMetal); + int nEndpoint = (0 != at->endpoint) + (0 != atom[neigh].endpoint); + int nStereo = (at->p_parity || at->sb_parity[0]) + (atom[neigh].p_parity || atom[neigh].sb_parity[0]); + + if ( bond_type > BOND_TYPE_TRIPLE ) + { + bond_type = BOND_TYPE_SINGLE; + } + + /* M=metal, A=non-metal atom, e=endpoint */ + if ( nStereo && pSrm->bFixStereoBonds || !nMetal || !pSrm->bMetalAddFlower ) + { + /* atom-atom rules, no metal atoms involved (1: A-A, A-Ae, Ae-Ae) */ + nMinorder = BOND_TYPE_SINGLE; + nInitorder = bond_type; + nFlow = nInitorder - nMinorder; + } + else if ( nMetal && !nEndpoint ) + { + /* M-a, M-M */ + /* atom - metal or metal-metal, none of them is an endpoint (2: M-M, M-A) */ + nMinorder = pSrm->nMetalMinBondOrder; + nInitorder = pSrm->nMetalInitBondOrder + bond_type - BOND_TYPE_SINGLE; + nFlow = nInitorder - nMinorder; + if ( !pSrm->nMetalInitEdgeFlow && + pSrm->nMetalInitBondOrder > pSrm->nMetalMinBondOrder && + nFlow > 0 ) + { + /* reduce initial flow by 1 and increase st_cap on metal by 1 */ + nFlow --; + } + bNeedsFlower = (0 != pVA[iat].cMetal); + } + else + if ( pVA[iat].cMetal && !at->endpoint && !pVA[neigh].cMetal && atom[neigh].endpoint|| + pVA[neigh].cMetal && !atom[neigh].endpoint && !pVA[iat].cMetal && at->endpoint ) + { + /* M-ae */ + /* metal connected to a non-metal endpoint (3: M-Ae) */ + nMinorder = pSrm->nMetal2EndpointMinBondOrder; + nInitorder = pSrm->nMetal2EndpointInitBondOrder + bond_type - BOND_TYPE_SINGLE; + nFlow = nInitorder - nMinorder; + if ( !pSrm->nMetal2EndpointInitEdgeFlow && + pSrm->nMetal2EndpointInitBondOrder > pSrm->nMetal2EndpointMinBondOrder && + nFlow > 0 ) + { + /* reduce initial flow by 1 and increase st_cap on metal by 1 */ + nFlow --; + } + bNeedsFlower = (0 != pVA[iat].cMetal); + } + else + { + /* endpoint is metal => no flower (4: M-Me, Me-Me, Me-A, Me-Ae) */ + nMinorder = pSrm->nMetal2EndpointMinBondOrder; + nInitorder = pSrm->nMetal2EndpointInitBondOrder + bond_type - BOND_TYPE_SINGLE; + nFlow = nInitorder - nMinorder; + if ( !pSrm->nMetal2EndpointInitEdgeFlow && + pSrm->nMetal2EndpointInitBondOrder > pSrm->nMetal2EndpointMinBondOrder && + nFlow > 0 ) { + /* reduce initial flow by 1 and increase st_cap on metal by 1 */ + nFlow --; + } + bNeedsFlower = (pVA[iat].cMetal && !at->endpoint); + } + + nMaxcap = BOND_TYPE_TRIPLE - nMinorder; + if ( pnMaxcap ) + { + *pnMaxcap = nMaxcap; + } + if ( pnMinorder ) + { + *pnMinorder = nMinorder; + } + if ( pbNeedsFlower ) + { + *pbNeedsFlower = bNeedsFlower; + } + + return nFlow; +} + + +/*********** new *******************************************************************************************/ +int AtomStcapStflow( inp_ATOM *atom, VAL_AT *pVA, ICHICONST SRM *pSrm, int iat, int *pnStcap, int *pnStflow, + EdgeFlow *pnMGroupEdgeCap, EdgeFlow *pnMGroupEdgeFlow ) +{ + int ineigh, bFlower; + int nStflow=0, nMaxBondCap, nMinBondOrder, bNeedsFlower = 0; + int valence = atom[iat].valence; + int nStcap = atom[iat].chem_bonds_valence; + int nMGroupEdgeCap = 0, nMGroupEdgeFlow = 0, nFlow; + + if ( pSrm->bMetalAddFlower ) + { + nStcap -= pVA[iat].cInitOrigValenceToMetal - pVA[iat].cInitValenceToMetal; + } + + for ( ineigh = 0; ineigh < valence; ineigh ++ ) + { + nFlow = BondFlowMaxcapMinorder( atom, pVA, pSrm, iat, ineigh, &nMaxBondCap, &nMinBondOrder, &bFlower ); + nStflow += nFlow; + nStcap -= nMinBondOrder; + if ( bFlower ) { + bNeedsFlower ++; + nMGroupEdgeFlow += nFlow; + nMGroupEdgeCap += BOND_TYPE_TRIPLE - nMinBondOrder + pSrm->nMetalMaxCharge_D; + } + } + + if ( pnStcap ) + { + *pnStcap = bNeedsFlower? nStflow : nStcap; /* initially, metal atoms are not radicals */ + } + if ( pnStflow ) + { + *pnStflow = nStflow; + } + if ( pnMGroupEdgeFlow ) + { + *pnMGroupEdgeFlow = nMGroupEdgeCap - nMGroupEdgeFlow; + } + if ( pnMGroupEdgeCap ) + { + *pnMGroupEdgeCap = nMGroupEdgeCap; + } + + return bNeedsFlower; /* number of variable bonds to metal */ +} + + +/************************************************************************************** +int nCountBnsSizes( inp_ATOM *at, int num_at, int nAddEdges2eachAtom, int nAddVertices, + T_GROUP_INFO *ti, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups ) + +fills out totals: + + pTCGroups->num_atoms = number of atoms + pTCGroups->num_bonds = number of bonds between atoms + pTCGroups->num_tgroups = number of tautomeric groups + pTCGroups->num_tgroup_edges = number of edges to tautomeric groups + pTCGroups->tgroup_charge = total charge on thautomeric atoms (negative) + pTCGroups->num_tc_groups = total number of all groups + pTCGroups->nVertices = total number of vertices excluding groups interconnections + pTCGroups->nEdges = total number of edges excluding groups interconnections + +creates entries for the groups and adds to each group: + + TC_GROUP::type = BNS_VERT_TYPE_TGROUP, BNS_VT_C_POS, BNS_VT_C_NEG, BNS_VT_C_POS_C, BNS_VT_C_NEG_C + TC_GROUP::ord_num = ordering number within the type, e.g. t-group number + TC_GROUP::st_cap = all from the atoms in ChargeStruct or tautomeric group info. + TC_GROUP::st_flow = all from the atoms in ChargeStruct (0 for t-groups). + TC_GROUP::num_edges = number of edges to the atoms or ChargeStruct vertices. + TC_GROUP::edges_cap = sum of all incoming edge caps; see also nTautEndpointEdgeCap(..). + TC_GROUP::edges_flow = sum of all incoming edge flows; 0 for t-groups. + + TC_GROUP::nVertexNumber - NO FILLED WITH ANYTHING + + Note: the nDelta = st_cap - st_flow needs to be preserved when adding more vertices + +Return value: =0 => success + <0 => error + **************************************************************************************/ +int nCountBnsSizes( inp_ATOM *at, int num_at, int nAddEdges2eachAtom, int nAddVertices, + T_GROUP_INFO *ti, VAL_AT *pVA, ICHICONST SRM *pSrm, ALL_TC_GROUPS *pTCGroups ) +{ + int i, j, n, k, ret = 0, nBonds, nOtherEdges, nVertices, bMetalAtoms, bNeedsFlower; + int nTgroupEdges, nTgroupEdgesFromTg, nTotNegChargInTgroups, cap, flow; + MY_CONST C_NODE *pCN = NULL; + nVertices = nBonds = nOtherEdges = nTgroupEdges = nTgroupEdgesFromTg = nTotNegChargInTgroups = 0; + + /* count metal atoms and electrons */ + for ( i = 0; i < num_at; i ++ ) + { + pTCGroups->num_metal_atoms += (pVA[i].cMetal != 0); + pTCGroups->num_metal_bonds += pVA[i].cNumBondsToMetal; + pTCGroups->total_electrons += at[i].el_number; + pTCGroups->total_electrons_metals += pVA[i].cMetal? at[i].el_number : 0; + } + pTCGroups->total_electrons -= pTCGroups->total_charge; + pTCGroups->num_metal_bonds /= 2; + + /* register tautomeric groups */ + for ( i = 0; i < ti->num_t_groups; i ++ ) + { + ret = RegisterTCGroup( pTCGroups, + BNS_VERT_TYPE_TGROUP, + ti->t_group[i].nGroupNumber, + ti->t_group[i].num[0] /* st_cap */, + 0 /* st_flow */, + 0 /* edge cap */, + 0 /* edge flow */, + ti->t_group[i].nNumEndpoints /* num Edges */ + ); + if ( ret < 0 ) + { + goto exit_function; + } + + /* edges to tautomeric groups */ + nOtherEdges += ti->t_group[i].nNumEndpoints; + nTgroupEdgesFromTg += ti->t_group[i].nNumEndpoints; + /* total negative charge in t-groups */ + nTotNegChargInTgroups += ti->t_group[i].num[1]; + if ( ret > 0 ) + { + /* should always happen since this is the first time this t-group is added */ + j = ret-1; + pTCGroups->pTCG[j].tg_num_H = ti->t_group[i].num[0] - ti->t_group[i].num[1]; + pTCGroups->pTCG[j].tg_num_Minus = ti->t_group[i].num[1]; + } + } + + bMetalAtoms = 0; + +repeat_for_metals: + + /* count vertices and register ChargeValence groups */ + /* for now an atom may belong either to a t-group or to a ChargeValence group, but not to both */ + for ( i = 0; i < num_at; i ++ ) + { + /* number of bonds */ + nBonds += at[i].valence; + /* Process ChargeStruct vertices and edges */ + if ( pVA[i].cnListIndex ) + { + /* count vertices & edges in the ChargeValence Substructure attached to an atom */ + /* Important: unlike inp_ATOM, each edge e appears in pCN[*].e[*] only ONE time */ + int len = cnList[j = pVA[i].cnListIndex-1].len; + int bits = cnList[j].bits; + int type, neigh_type, metal_group_number; + pCN = cnList[j].pCN; + + /* first process all non-metals, after that -- all metals */ + if ( (bits != cn_bits_Me) != !bMetalAtoms ) + { + continue; + } + metal_group_number = 0; + for ( j = 0; j < len; j ++) + { + type = pCN[j].v.type; /* ChargeStruct vertex type: atom is the first, c-groups are last */ + + /* process all pCN[j] neighbors */ + for ( k = 0; k < MAX_CN_VAL && (n = pCN[j].e[k].neigh); k ++ ) + { + nOtherEdges ++; /* edges inside ChargeStruct */ + n --; /* neighbor vertex position inside cnList[j].pCN */ + neigh_type = pCN[n].v.type; /* type of the neighboring atom */ + + if ( IS_BNS_VT_C_GR(neigh_type) ) + { + /* register this edge to a CN-group vertex */ + cap = !bMetalAtoms? pCN[j].e[k].cap : pCN[j].e[k].cap? pSrm->nMetalMaxCharge_D : 0; + flow = !bMetalAtoms? pCN[j].e[k].flow : pCN[j].e[k].flow? pSrm->nMetalMaxCharge_D : 0; + + ret = RegisterTCGroup( pTCGroups, + neigh_type, + 0 /* ord_num*/, + 0 /* st_cap */, + 0 /* st_flow */, + cap /* edge cap*/, + flow /* edge flow */, + 1 /* nNumEdges*/); + if ( ret < 0 ) + { + goto exit_function; + } + if ( ret > 0 ) + { + /* the group has just been created; add one more edge to (+/-) or supergroup */ + ret = RegisterTCGroup( pTCGroups, + neigh_type, + 0 /* ord_num*/, + 0 /* st_cap */, + 0 /* st_flow */, + 0 /* edge cap*/, + 0/* edge flow*/, + 1 /* nNumEdges*/); + if ( ret < 0 ) + { + goto exit_function; + } + nOtherEdges ++; + } + } + + if ( IS_BNS_VT_C_GR(type) ) + { + /* register this edge to a CN-group vertex; normally this does not happen */ + + cap = !bMetalAtoms? pCN[j].e[k].cap : pCN[j].e[k].cap? pSrm->nMetalMaxCharge_D : 0; + flow = !bMetalAtoms? pCN[j].e[k].flow : pCN[j].e[k].flow? pSrm->nMetalMaxCharge_D : 0; + + ret = RegisterTCGroup( pTCGroups, type, 0 /* ord_num*/, + 0 /* st_cap */, 0 /* st_flow */, + cap /* edge cap*/, flow /* edge flow */, 1 /* nNumEdges*/); + if ( ret < 0 ) + { + goto exit_function; + } + if ( ret > 0 ) + { + /* the group has just been created; add one more edge to (+/-) or supergroup */ + ret = RegisterTCGroup( pTCGroups, type, 0 /* ord_num*/, + 0 /* st_cap */, 0 /* st_flow */, + 0 /* edge cap*/, 0/* edge flow*/, 1 /* nNumEdges*/); + if ( ret < 0 ) + { + goto exit_function; + } + nOtherEdges ++; + } + } + } /* end of the current vertex pCN[j] neighbors */ + + /* process pCN[j] vertex */ + + if ( type & BNS_VERT_TYPE_ATOM ) + { + continue; /* do not count regular atoms here */ + } + if ( IS_BNS_VT_CHRG_STRUCT(type) ) + { + nVertices ++; + continue; + } + + if ( pSrm->bMetalAddFlower && IS_BNS_VT_M_GR( type ) ) + { + /* special treatment: flow and cap are known as well as structure */ + /* initial bond valence to metal is either 0 or 1 */ + EdgeFlow nEdgeFlow, nEdgeCap; + + bNeedsFlower = AtomStcapStflow( at, pVA, pSrm, i, NULL /*pnStcap*/, NULL /*pnStflow*/, + &nEdgeCap, &nEdgeFlow ); + if ( !bNeedsFlower ) + { + ret = RI_ERR_PROGR; + goto exit_function; + } + /* + GetAtomToMCGroupInitEdgeCapFlow( &nEdgeCap, &nEdgeFlow, pSrm, at, pVA, i ); + GetAtomToMCGroupInitEdgeCapFlow( &nEdgeCap, &nEdgeFlow, pSrm ); + */ + /* the 1st is the flower base */ + /* atom - G0 edge and G0 vertex */ + ret = RegisterTCGroup( pTCGroups, + type, + 0 /* ord_num*/, + /*pVA[i].cInitFreeValences*/ + 0 /* st_cap */, + 0 /* st_flow */, + (int)nEdgeCap, + (int)nEdgeFlow, + 1 /* nNumEdges*/); + if ( ret < 0 ) + { + goto exit_function; + } + /* count edge atom-G0 */ + nOtherEdges ++; + if ( ret > 0 ) + { + /* first time registration: add G0-G1 and G0-G2 edges to G0 */ + + ret = RegisterTCGroup( pTCGroups, + type, + 0 /* ord_num*/, + 0 /* st_cap */, + 0 /* st_flow */, + 0,/* edge cap*/ + 0 /*edge flow*/, + 2 /* nNumEdges*/); + + if ( ret < 0 ) + { + goto exit_function; + } + + /* first time registration: add G1; it has 3 edges */ + + ret = RegisterTCGroup( pTCGroups, + type, + 1 /* ord_num*/, + 0 /* st_cap */, + 0 /* st_flow */, + 0,/* edge cap*/ + 0 /*edge flow*/, + 3 /* nNumEdges*/); + + if ( ret <= 0 ) + { + ret = !ret? RI_ERR_PROGR : ret; + goto exit_function; + } + + /* first time registration: add G2; it has 3 edges */ + + ret = RegisterTCGroup( pTCGroups, + type, + 2 /* ord_num*/, + 0 /* st_cap */, + 0 /* st_flow */, + 0,/* edge cap*/ + 0 /*edge flow*/, + 3 /* nNumEdges*/); + + if ( ret <= 0 ) + { + ret = !ret? RI_ERR_PROGR : ret; + goto exit_function; + } + + /* first time registration: add G3; it has 2 edges */ + + ret = RegisterTCGroup( pTCGroups, + type, + 3 /* ord_num*/, + 0 /* st_cap */, + 0 /* st_flow */, + 0,/* edge cap*/ + 0 /*edge flow*/, + 2 /* nNumEdges*/); + + if ( ret <= 0 ) + { + ret = !ret? RI_ERR_PROGR : ret; + goto exit_function; + } + /* count added metal flower vertices: G0, G1, G2, G3 */ + nVertices += 4; + /* count added metal flower edges: C0-C1, C0-C2, C1-C2, C1-C3, C2-C3 */ + nOtherEdges += 5; + /* add connections of G0 to G1 and G2 */ + } + continue; + } + + nVertices ++; /* count BNS_VT_C_POS* types; all contain BNS_VERT_TYPE_C_GROUP bit */ + if ( !IS_BNS_VT_C_GR(type) ) + { /* check */ + ret = RI_ERR_PROGR; + goto exit_function; + } + /* add st_cap and st_flow for a charge group */ + cap = !bMetalAtoms? pCN[j].v.cap : pCN[j].v.cap? pSrm->nMetalMaxCharge_D : 0; + flow = !bMetalAtoms? pCN[j].v.flow : pCN[j].v.flow? pSrm->nMetalMaxCharge_D : 0; + + ret = RegisterTCGroup( pTCGroups, type, 0 /* ord_num*/, + cap /* st-cap*/, flow /* st-flow */, + 0 /* edge cap */, 0 /* edge flow */, 0 /* edges already counted */ ); + if ( ret < 0 ) + { + goto exit_function; + } + } + } + else + { + pCN = NULL; + } + + /* count edge caps to t-groups */ + if ( at[i].endpoint ) + { + int nEdgeCap = nTautEndpointEdgeCap( at, pVA, i ); + nTgroupEdges ++; + if ( nEdgeCap < 0 ) + { + ret = nEdgeCap; + goto exit_function; + } + + /* add number of unsatisfied valences for a t-group; the unknown flow = 0 */ + ret = RegisterTCGroup( pTCGroups, + BNS_VERT_TYPE_TGROUP, + at[i].endpoint, + 0 /* st_cap */, + 0 /* st_flow */, + nEdgeCap /* edge cap */, + 0 /* edge flow */, + 0 /* t-group edges have already been counted */ ); + if ( ret < 0 ) + { + goto exit_function; + } + } + } + + if ( !bMetalAtoms && pTCGroups->num_metal_atoms ) + { + bMetalAtoms = 1; + nBonds = 0; /* added 2006-05-15 */ + goto repeat_for_metals; + } + + /* count real atoms and bonds */ + nBonds /= 2; + pTCGroups->num_atoms = num_at; + pTCGroups->num_bonds = nBonds; + + pTCGroups->num_tgroups = ti->num_t_groups; + pTCGroups->num_tgroup_edges = nTgroupEdges; + pTCGroups->tgroup_charge = -nTotNegChargInTgroups; + + if ( 0 <= ret && nTgroupEdgesFromTg != nTgroupEdges ) + { + ret = BNS_PROGRAM_ERR; + } + + nVertices += num_at; + + + /* count other vertices */ + nVertices += ti->num_t_groups; + nBonds += nOtherEdges; + + /* return edges and vertices */ + pTCGroups->nVertices = nVertices; + pTCGroups->nEdges = nBonds; + + +exit_function: + return ret; +} + + +/**************************************************************** + int nAddSuperCGroups( ALL_TC_GROUPS *pTCGroups ) + + 1. adds BNS_VT_C_POS_ALL and BNS_VT_C_NEG_ALL ONLY if both + {TCG_Plus0 and TCG_Plus_C0} and/or + {TCG_Minus0 and TCG_Minus_C0} are present, respectively + + 2. fills pTCGroups->nGroup[]: + + pTCGroups->nGroup[k] < 0 => does not exist + pTCGroups->nGroup[k] = i => the group is pTCGroups->pTCG[i] + + where group group + k = type number + TCG_Plus0 BNS_VT_C_POS 0 + TCG_Plus1, BNS_VT_C_POS 1 + TCG_Minus0, BNS_VT_C_NEG 0 + TCG_Minus1, BNS_VT_C_NEG 1 + TCG_Plus_C0, BNS_VT_C_POS_C 0 + TCG_Plus_C1, BNS_VT_C_POS_C 1 + TCG_Minus_C0, BNS_VT_C_NEG_C 0 + TCG_Minus_C1, BNS_VT_C_NEG_C 1 + TCG_Plus, BNS_VT_C_POS_ALL 0 + TCG_Minus, BNS_VT_C_NEG_ALL 0 + +only groups with number 0 are processed + + 3. If only one of the groups in pairs mentioned in (1) above + is present then + + pTCGroups->nGroup[TCG_Plus] := pTCGroups->nGroup[TCG_Plus0] or + pTCGroups->nGroup[TCG_Plus] := pTCGroups->nGroup[TCG_Plus_C0]; + an additional BNS_VT_C_POS_ALL vertex is not created + + same for pTCGroups->nGroup[TCG_Minus] and BNS_VT_C_NEG_ALL + + 4. Adds to these new "supergroups" (TCG_Plus, TCG_Minus) + descriptions in pTCGroups->pTCG[k] + st_cap, st_flow, edges cap and flow from the corresponding + groups {TCG_Plus0 and TCG_Plus_C0}. Same for the Minus groups. + Stores indexes k in + pTCGroups->nGroup[TCG_Plus], pTCGroups->nGroup[TCG_Minus] + + ****************************************************************/ +int nAddSuperCGroups( ALL_TC_GROUPS *pTCGroups ) +{ + int i, k, n, n1, n2, n3, nNumTg = 0, ret = 0, nNumToConnect; + + for ( i = 0; i < pTCGroups->num_tc_groups; i ++ ) + { + if ( pTCGroups->pTCG[i].type & BNS_VERT_TYPE_TGROUP ) + { + nNumTg ++; + continue; /* t-group */ + } + if ( IS_BNS_VT_C_GR(pTCGroups->pTCG[i].type) || + IS_BNS_VT_M_GR(pTCGroups->pTCG[i].type) ) + { + /* ChargeValence (cn) group */ + switch( pTCGroups->pTCG[i].type ) + { + case BNS_VT_C_POS: + k = TCG_Plus0; + break; + case BNS_VT_C_NEG: + k = TCG_Minus0; + break; + case BNS_VT_C_POS_C: + k = TCG_Plus_C0; + break; + case BNS_VT_C_NEG_C: + k = TCG_Minus_C0; + break; + case BNS_VT_C_POS_M: + k = TCG_Plus_M0; + break; + case BNS_VT_C_NEG_M: + k = TCG_Minus_M0; + break; + case BNS_VT_M_GROUP: + switch( pTCGroups->pTCG[i].ord_num ) + { + case 0: + k = TCG_MeFlower0; + break; + case 1: + k = TCG_MeFlower1; + break; + case 2: + k = TCG_MeFlower2; + break; + case 3: + k = TCG_MeFlower3; + break; + default: + ret = RI_ERR_PROGR; /* unexpected group type */ + goto exit_function; + } + break; + + default: + ret = RI_ERR_PROGR; /* unexpected group type */ + goto exit_function; + } + + if ( pTCGroups->nGroup[k] >= 0 || pTCGroups->pTCG[i].ord_num && !IS_BNS_VT_M_GR(pTCGroups->pTCG[i].type) ) + { + ret = RI_ERR_PROGR; + goto exit_function; + } + pTCGroups->nGroup[k] = i; /* ordering number of the Charge group, starting from 0 */ + } + } + + /* add (+) supergroup */ + n1 = pTCGroups->nGroup[TCG_Plus0]; + n2 = pTCGroups->nGroup[TCG_Plus_C0]; + n3 = pTCGroups->nGroup[TCG_Plus_M0]; + nNumToConnect = (n1>=0) + (n2>=0) + (n3>=0); + + if ( nNumToConnect ) + { + /* if both groups are present then add a supergroup */ + ret = RegisterTCGroup( pTCGroups, + BNS_VT_C_POS_ALL, + 0, + 0 /* st_cap */, + 0 /* st_flow */, + 0 /* edge cap */, + 0 /* edge flow */, + 1+nNumToConnect + /* one more edge to connect to */ + /* an additional (+/-) vertex */ ); + + if ( ret <= 0 ) + { + ret = !ret? RI_ERR_PROGR : ret; + goto exit_function; + } + + pTCGroups->nGroup[TCG_Plus] = ret - 1; /* newly added group number */ + pTCGroups->nVertices += 2; /* two vertices including itself */ + pTCGroups->nEdges += 1 + nNumToConnect; /* one more edge to connect to an additional (+/-) vertex */ + } + + /* add (-) supergroup */ + n1 = pTCGroups->nGroup[TCG_Minus0]; + n2 = pTCGroups->nGroup[TCG_Minus_C0]; + n3 = pTCGroups->nGroup[TCG_Minus_M0]; + nNumToConnect = (n1>=0) + (n2>=0) + (n3>=0); + if ( nNumToConnect ) + { + /* if both groups are present then add a supergroup */ + + ret = RegisterTCGroup( pTCGroups, + BNS_VT_C_NEG_ALL, + 0, + 0 /* st_cap */, + 0 /* st_flow */, + 0 /* edge cap */, + 0 /* edge flow */, + 1+nNumToConnect /* one more edge to connect to an additional (+/-) vertex */ ); + + if ( ret < 0 ) + { + goto exit_function; + } + pTCGroups->nGroup[TCG_Minus] = ret - 1; /* newly added group number */ + pTCGroups->nVertices += 2; /* needs two vertices including itself */ + pTCGroups->nEdges += 1 + nNumToConnect; /* one more edge to connect to an additional (+/-) vertex */ + } + + /* add neutralization vertex: (+)-()=(-) connection */ + k = pTCGroups->nGroup[TCG_Minus]; + n = pTCGroups->nGroup[TCG_Plus]; + nNumToConnect = (k>=0) + (n>=0); + if ( nNumToConnect ) + { + pTCGroups->nVertices += 1; + pTCGroups->nEdges += nNumToConnect; /* one edge per super-c-group */ + } + + ret = 0; + +exit_function: + return ret; +} + + +/*********************************************************************************/ +int AddTGroups2TCGBnStruct( BN_STRUCT *pBNS, + StrFromINChI *pStruct, + VAL_AT *pVA, + ALL_TC_GROUPS *pTCGroups, + int nMaxAddEdges ) +{ + int ret = 0; + inp_ATOM *at = pStruct->at; + int num_atoms = pStruct->num_atoms; + int tot_st_cap, tot_st_flow; + /* ret = ReInitBnStruct( pBNS ); */ + + if ( pTCGroups->num_tgroups /* tgi && tgi->num_t_groups && tgi->t_group*/ ) + { + int i, k, endpoint, /*centerpoint,*/ fictpoint; + int num_tg = pTCGroups->num_tgroups; + int num_edges = pBNS->num_edges; + int num_vertices = pBNS->num_vertices; + BNS_VERTEX *vert_ficpoint, *vert_ficpoint_prev; /* fictitious vertex describing t-group */ + BNS_VERTEX *vert_endpoint; + BNS_EDGE *edge; /* edge between that vertex and the tautomeric endpoint */ + int nMaxTGroupNumber = 0; + /*ENDPOINT_INFO eif;*/ + + /* Debug: check overflow */ + if ( num_vertices + num_tg >= pBNS->max_vertices ) + { + return BNS_VERT_EDGE_OVFL; + } + if ( num_edges + pTCGroups->num_tgroup_edges >= pBNS->max_edges ) + { + return BNS_VERT_EDGE_OVFL; + } + + /* find the largest t-group ID */ + for ( i = 0; i < pTCGroups->num_tc_groups; i ++ ) + { + if ( pTCGroups->pTCG[i].type & BNS_VERT_TYPE_TGROUP ) + { + k = pTCGroups->pTCG[i].ord_num; + if ( k <= 0 ) + { + return BNS_CPOINT_ERR; /* t-group does not have a number or has a wrong number */ + } + if ( k > pTCGroups->num_tc_groups ) + { + return BNS_CPOINT_ERR; /* t-group has a wrong number */ + } + if ( k != nMaxTGroupNumber + 1 ) + { + return BNS_CPOINT_ERR; /* t-group numbers are not contiguously ascending */ + } + nMaxTGroupNumber = k; + } else + { + break; /* t-groups are contiguous and first in the list */ + } + } + + if ( i != num_tg ) + { + return BNS_CPOINT_ERR; /* number of t-groups is wrong */ + } + + /* since t-group IDs may be not contiguous, clear all vertices that will be added. + all-zeroes-vertex will be ignored by the BNS + */ + memset( pBNS->vert+num_vertices, 0, nMaxTGroupNumber*sizeof(pBNS->vert[0]) ); + + /* initialize new fictitious vertices */ + vert_ficpoint_prev = pBNS->vert+num_vertices - 1; + + tot_st_cap = tot_st_flow = 0; + + for ( i = 0; i < num_tg; i ++, vert_ficpoint_prev = vert_ficpoint ) + { + /* + vert_ficpoint-1 is the last vertex; + vert_ficpoint is the vertex that is being added + Note: nGroupNumber are not contiguous + */ + vert_ficpoint = pBNS->vert+num_vertices + pTCGroups->pTCG[i].ord_num - 1; + vert_ficpoint->iedge = vert_ficpoint_prev->iedge + vert_ficpoint_prev->max_adj_edges; + vert_ficpoint->max_adj_edges = pTCGroups->pTCG[i].num_edges+nMaxAddEdges+BNS_ADD_SUPER_TGROUP; + vert_ficpoint->num_adj_edges = 0; + vert_ficpoint->st_edge.flow = vert_ficpoint->st_edge.flow0 = 0; + vert_ficpoint->st_edge.cap = vert_ficpoint->st_edge.cap0 = pTCGroups->pTCG[i].st_cap; + tot_st_cap += pTCGroups->pTCG[i].st_cap; + vert_ficpoint->type = pTCGroups->pTCG[i].type; + pTCGroups->pTCG[i].nVertexNumber = (int) (vert_ficpoint - pBNS->vert); + } + + for ( endpoint = 0; endpoint < num_atoms; endpoint ++ ) + { + if ( !at[endpoint].endpoint ) + continue; + fictpoint = at[endpoint].endpoint + num_vertices - 1; + vert_ficpoint = pBNS->vert + fictpoint; /* t-group vertex */ + vert_endpoint = pBNS->vert + endpoint; /* endpoint vertex */ + /* Debug: check overflow */ + if ( fictpoint >= pBNS->max_vertices || + num_edges >= pBNS->max_edges || + vert_ficpoint->num_adj_edges >= vert_ficpoint->max_adj_edges || + vert_endpoint->num_adj_edges >= vert_endpoint->max_adj_edges ) + { + ret = BNS_VERT_EDGE_OVFL; + break; + } + +#ifdef NEVER + /* obtain donor/acceptor info */ + if ( !nGetEndpointInfo( at, endpoint, &eif ) ) { + ret = BNS_BOND_ERR; + break; + } +#endif + + vert_endpoint->type |= BNS_VERT_TYPE_ENDPOINT; + +#ifdef NEVER + /* set capacity = 1 to the edges from the endpoint to the centerpoint(s) */ + for ( k = 0; k < vert_endpoint->num_adj_edges; k ++ ) { + int iedge = vert_endpoint->iedge[k]; + if ( !pBNS->edge[iedge].cap ) { + /* single bond, possibly between endpoint and centerpoint */ + centerpoint = (pBNS->edge[iedge].neighbor12 ^ endpoint); + if ( centerpoint < pBNS->num_atoms && + pBNS->vert[centerpoint].st_edge.cap >= 1 ) { + int bond_type = (at[endpoint].bond_type[k] & BOND_TYPE_MASK); + if (bond_type == BOND_TAUTOM || + bond_type == BOND_ALTERN || + bond_type == BOND_ALT12NS || + bond_type == BOND_SINGLE ) { + pBNS->edge[iedge].cap = 1; + } + } + } + } +#endif + + /* create a new edge connecting endpoint to the new fictitious t-group vertex vert_ficpoint */ + edge = pBNS->edge + num_edges; + edge->cap = vert_endpoint->st_edge.cap - vert_endpoint->st_edge.flow; + edge->cap = inchi_min( edge->cap, MAX_TGROUP_EDGE_CAP ); + edge->cap = inchi_max( edge->cap, 0 ); + edge->flow = 0; + edge->pass = 0; + +#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) + edge->forbidden &= pBNS->edge_forbidden_mask; +#endif + +#ifdef NEVER + /* later include case when the charge change allows the endpoint to become tautomeric */ + /* mark endoint having moveable H atom with flow=1 */ + + /* -- old "no charges" version -- */ + /* if (at[endpoint].chem_bonds_valence == at[endpoint].valence) */ + /* -- the following line takes charges into account -- */ + if ( eif.cDonor ) /* means the endpoint has an H-atom to donate */ + { + /* increment edge flow */ + edge->flow ++; + /* increment one vertex st-flow & cap */ + vert_ficpoint->st_edge.flow ++; + vert_ficpoint->st_edge.cap ++; + /* increment another vertex st-flow & cap */ + vert_endpoint->st_edge.flow ++; + vert_endpoint->st_edge.cap ++; + } +#endif + + /* connect edge to endpoint and fictpoint and increment the counters of neighbors and edges */ + ret = ConnectTwoVertices( vert_endpoint, vert_ficpoint, edge, pBNS, 0 ); + if ( IS_BNS_ERROR( ret ) ) + { + break; + } + num_edges ++; + edge->cap0 = edge->cap; + edge->flow0 = edge->flow; + pVA[endpoint].nTautGroupEdge = num_edges; /* edge index + 1 */ + } + + pBNS->num_edges = num_edges; + pBNS->num_vertices += nMaxTGroupNumber; + pBNS->num_t_groups = num_tg; + pBNS->tot_st_cap += tot_st_cap; + pBNS->tot_st_flow += tot_st_flow; + } + + return ret; +} + + +/*****************************************************************************************************/ +int ConnectTwoVertices( BNS_VERTEX *p1, BNS_VERTEX *p2, BNS_EDGE *e, BN_STRUCT *pBNS, int bClearEdge ) +{ + int ip1 = (int) (p1 - pBNS->vert); + int ip2 = (int) (p2 - pBNS->vert); + int ie = (int) (e - pBNS->edge); + + /* debug: check bounds */ + if ( ip1 >= pBNS->max_vertices || ip1 < 0 || + ip2 >= pBNS->max_vertices || ip2 < 0 || + ie >= pBNS->max_edges || ie < 0 || + (p1->iedge - pBNS->iedge) < 0 || + (p1->iedge - pBNS->iedge) + p1->max_adj_edges > pBNS->max_iedges || + (p2->iedge - pBNS->iedge) < 0 || + (p2->iedge - pBNS->iedge) + p2->max_adj_edges > pBNS->max_iedges || + p1->num_adj_edges >= p1->max_adj_edges || + p2->num_adj_edges >= p2->max_adj_edges ) + { + return BNS_VERT_EDGE_OVFL; + } + /* clear the edge */ + if ( bClearEdge ) + { + memset( e, 0, sizeof(*e) ); + } + else + if ( e->neighbor1 || e->neighbor12 ) + { + return BNS_PROGRAM_ERR; + } + + /* connect */ + e->neighbor1 = inchi_min( ip1, ip2 ); + e->neighbor12 = ip1 ^ ip2; + p1->iedge[p1->num_adj_edges] = ie; + p2->iedge[p2->num_adj_edges] = ie; + e->neigh_ord[ip1 > ip2] = p1->num_adj_edges ++; + + e->neigh_ord[ip1 < ip2] = p2->num_adj_edges ++; + return 0; +} + + +/*********************************************************************************************************** + METAL ATOMS' FLOWER - Provides a source/sink of "free valences" + *********************************************************************************************************** + + c1+...+cn = 2c+dc - total cap and flow of edges to the flower base from metal atoms + f1+...+fn = 2f+df they should allow changing bonds to metals from 0-order to triple + dc,df = 0 or 1 hence c=3*n, f=0 (initial zero bond order) or n + Gi=vertex(M-group) + Ci=its st_cap [C3,F3] C0 = F0 = 2c + 2D + dc (st_cap & st_flow) + Fi=its st_flow G3 C2 = F2 = c + 2D + / \ C1 = F1 = c + 2D + dc-df + ci=cap of edge i cx,fx/ \cy,fy C3 = F3 = 0 + fi=edge flow / \ Constraints + [C2,F2]/ cd,fd \[C1,F1] ----------------- + G2--------G1 fa+fb+2f+df=F0=C0 + \ / ca = c + 2D (edge cap) fa+fd =F2=C2 + ca,fa \ / cb,fb fa = c + D - f (edge flow) fb+fd =C1=F1 + \ / fi <= ci + G0 [C0,F0] cb = c + 2D + dc ----------------- + /\ fb = c + D + dc - (f + df) + ci=3, fi=0 or 1 c1,f1 /... \ cn,fn ------------------------------------ + / \ cd = c + 2D D is an arbitrary integer > 0 + all n Metal atoms: M1 ... Mn fd = f + D it allows to apply + C3++ (add st_flow to cancel radicals) + For each Mi add cap and flow=cap cx = cy = D D times. + to M-charge group fx = fy = 0 + -------------------------------------------------------------------------------------- + | f=0 | f=c, dc>=df | 0 <= 2f+df <= 2c+dc + edge +------------------------+-----------+----------+-------------+------------- + | flow | rescap | flow | rescap | flow | rescap + ----------+------------+-----------+-----------+----------+-------------+------------- + f1+..+fn | df | 2c+dc-df | 2c+df | dc-df | 2f+df | 2c-2f+dc-df + fa | c+D | D | D | c+D | c+D-f | c+D + fb | c+D+dc-df | D+df | D+dc-df | c+D+df | c+D+dc-f-df| c+D+df + fd | D | c+D | c+D | D | f+D | D + -------------------------------------------------------------------------------------- +***********************************************************************************************************/ +int AddRadicalToMetal( int *tot_st_cap, int *tot_st_flow, ICHICONST SRM *pSrm, BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups ) +{ + int iG0 = pTCGroups->nGroup[TCG_MeFlower0]; /* index in pTCGroups->pTCG[] */ + int iG1 = pTCGroups->nGroup[TCG_MeFlower1]; + int iG2 = pTCGroups->nGroup[TCG_MeFlower2]; + int iG3 = pTCGroups->nGroup[TCG_MeFlower3]; + int n = (iG0>=0) + (iG1>=0) + (iG2>=0) + (iG3>=0); + int vG0, vG1, vG2, vG3; /* M-vertex number */ + BNS_VERTEX *pG0=NULL, *pG1=NULL, *pG2=NULL, *pG3=NULL; + + if ( pTCGroups->num_metal_atoms && + pSrm->bMetalAddFlower && + *tot_st_cap % 2 && + n == 4 ) + { + vG0 = pTCGroups->pTCG[iG0].nVertexNumber; + vG1 = pTCGroups->pTCG[iG1].nVertexNumber; + vG2 = pTCGroups->pTCG[iG2].nVertexNumber; + vG3 = pTCGroups->pTCG[iG3].nVertexNumber; + + pG0 = pBNS->vert+vG0; + pG1 = pBNS->vert+vG1; + pG2 = pBNS->vert+vG2; + pG3 = pBNS->vert+vG3; + + /* add 1 unit to metal flower st_cap */ + pG3->st_edge.cap ++; + pG3->st_edge.cap0 ++; + (*tot_st_cap) ++; + return 1; + } + + return 0; +} + + +/***********************************************************************************************************/ +int ConnectMetalFlower( int *pcur_num_vertices, int *pcur_num_edges, + int *tot_st_cap, int *tot_st_flow, ICHICONST SRM *pSrm, + BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups ) +{ + int iG0 = pTCGroups->nGroup[TCG_MeFlower0]; /* index in pTCGroups->pTCG[] */ + int iG1 = pTCGroups->nGroup[TCG_MeFlower1]; + int iG2 = pTCGroups->nGroup[TCG_MeFlower2]; + int iG3 = pTCGroups->nGroup[TCG_MeFlower3]; + int n = (iG0>=0) + (iG1>=0) + (iG2>=0) + (iG3>=0); + int vG0, vG1, vG2, vG3; /* M-vertex number */ + int cur_num_edges = *pcur_num_edges; + int cur_num_vertices = *pcur_num_vertices; + BNS_VERTEX *pG0=NULL, *pG1=NULL, *pG2=NULL, *pG3=NULL; + BNS_EDGE *ea=NULL, *eb=NULL, *ed=NULL, *ex=NULL, *ey=NULL, *e; + int ia, ib, id, ix, iy; + int c, f, dc, df, ca, fa, cb, fb, cd, fd, cx, fx, cy, fy; + int C0, F0, C1, F1, C2, F2, C3, F3, D; + int ret = 0, i; + + if ( 0 == n ) + { + goto exit_function; + } + + if ( 4 != n ) + { + ret = RI_ERR_PROGR; + goto exit_function; + } + + vG0 = pTCGroups->pTCG[iG0].nVertexNumber; + vG1 = pTCGroups->pTCG[iG1].nVertexNumber; + vG2 = pTCGroups->pTCG[iG2].nVertexNumber; + vG3 = pTCGroups->pTCG[iG3].nVertexNumber; + + pG0 = pBNS->vert+vG0; + pG1 = pBNS->vert+vG1; + pG2 = pBNS->vert+vG2; + pG3 = pBNS->vert+vG3; + + /* count G0 edges cap and flow (currently only atoms are connected to G0) */ + for ( i = 0, c = 0, f = 0; i < pG0->num_adj_edges; i ++ ) + { + e = pBNS->edge + pG0->iedge[i]; + c += e->cap; + f += e->flow; + } + + /* consistency checks */ + if ( !IS_BNS_VT_M_GR(pTCGroups->pTCG[iG0].type) && + (pTCGroups->pTCG[iG0].edges_cap != pG0->st_edge.cap || + pTCGroups->pTCG[iG0].edges_flow != pG0->st_edge.flow) ) + { + ret = RI_ERR_PROGR; + goto exit_function; + } + + if ( pTCGroups->pTCG[iG0].edges_cap != c || + pTCGroups->pTCG[iG0].edges_flow != f ) + { + ret = RI_ERR_PROGR; + goto exit_function; + } + + /* get new edges */ + + ea = pBNS->edge + (ia=cur_num_edges++); + eb = pBNS->edge + (ib=cur_num_edges++); + ed = pBNS->edge + (id=cur_num_edges++); + ex = pBNS->edge + (ix=cur_num_edges++); + ey = pBNS->edge + (iy=cur_num_edges++); + + /* connect vertices with edges */ + ret = ConnectTwoVertices( pG0, pG1, eb, pBNS, 1 ); + if ( IS_BNS_ERROR( ret ) ) + { + goto exit_function; + } + + ret = ConnectTwoVertices( pG0, pG2, ea, pBNS, 1 ); + + if ( IS_BNS_ERROR( ret ) ) + { + goto exit_function; + } + + ret = ConnectTwoVertices( pG1, pG2, ed, pBNS, 1 ); + + if ( IS_BNS_ERROR( ret ) ) + { + goto exit_function; + } + + ret = ConnectTwoVertices( pG1, pG3, ey, pBNS, 1 ); + + if ( IS_BNS_ERROR( ret ) ) + { + goto exit_function; + } + + ret = ConnectTwoVertices( pG2, pG3, ex, pBNS, 1 ); + + if ( IS_BNS_ERROR( ret ) ) + { + goto exit_function; + } + + /* calculate caps and flows */ + + dc = c % 2; + c /= 2; + df = f % 2; + f /= 2; + + D = pSrm->nMetalFlowerParam_D; + + C0 = F0 = 2*c + 2*D + dc; + C1 = F1 = c + 2*D + dc - df; + C2 = F2 = c + 2*D; + C3 = F3 = 0; + + ca = c + 2*D; + fa = c + D - f; + + cb = c + 2*D + dc; + fb = c + D + dc - ( f + df ); + + cd = c + 2*D; + fd = f + D; + + cx = cy = D; + fx = fy = 0; + + /* check overflow */ + if ( C0 >= EDGE_FLOW_ST_MASK || F0 >= EDGE_FLOW_ST_MASK || + C1 >= EDGE_FLOW_ST_MASK || F1 >= EDGE_FLOW_ST_MASK || + C2 >= EDGE_FLOW_ST_MASK || F2 >= EDGE_FLOW_ST_MASK || + C3 >= EDGE_FLOW_ST_MASK || F3 >= EDGE_FLOW_ST_MASK ) + { + return BNS_PROGRAM_ERR; /* cannot handle too large st-cap or st-flow */ + } + + /* set st caps and flows */ + + SetStCapFlow( pG0, tot_st_flow, tot_st_cap, C0, F0 ); + SetStCapFlow( pG1, tot_st_flow, tot_st_cap, C1, F1 ); + SetStCapFlow( pG2, tot_st_flow, tot_st_cap, C2, F2 ); + SetStCapFlow( pG3, tot_st_flow, tot_st_cap, C3, F3 ); + + SetEdgeCapFlow( ea, ca, fa ); + SetEdgeCapFlow( eb, cb, fb ); + SetEdgeCapFlow( ed, cd, fd ); + SetEdgeCapFlow( ex, cx, fx ); + SetEdgeCapFlow( ey, cy, fy ); + + + *pcur_num_edges = cur_num_edges; + *pcur_num_vertices = cur_num_vertices; + + ret = 0; + +exit_function: + + return ret; +} + + +/********************************************************************************/ +void SetEdgeCapFlow( BNS_EDGE *e, int edge_cap, int edge_flow ) +{ + e->cap = e->cap0 = edge_cap; + e->flow = e->flow0 = edge_flow; +} + + +/********************************************************************************* + Add cap and flow to an edge + Add edge flow to the source vertex st_flow + Add edge cap & flow to the destination vertex cap and flow + *********************************************************************************/ +int AddEdgeFlow( int edge_cap, int edge_flow, BNS_EDGE *e01, BNS_VERTEX *pSrc /*src*/, + BNS_VERTEX *pDst/*dest*/, int *tot_st_cap, int *tot_st_flow ) +{ + + /* overflow check */ + if ( e01->cap < 0 || edge_cap < 0 || (int)e01->cap + edge_cap >= EDGE_FLOW_MASK ) + { + return BNS_PROGRAM_ERR; + } + + if ( pDst->st_edge.cap < 0 || (int)pDst->st_edge.cap + edge_cap >= EDGE_FLOW_ST_MASK || + pDst->st_edge.flow < 0 || (int)pDst->st_edge.flow + edge_flow >= EDGE_FLOW_ST_MASK || + pSrc->st_edge.cap < 0 || pSrc->st_edge.flow < 0 || + (int)pSrc->st_edge.flow + edge_flow >= EDGE_FLOW_ST_MASK ) { + return BNS_PROGRAM_ERR; + } + + /* add flow */ + e01->cap += edge_cap; + e01->flow += edge_flow; + e01->cap0 = e01->cap; + e01->flow0 = e01->flow; + + pDst->st_edge.cap += edge_cap; + pDst->st_edge.cap0 = pDst->st_edge.cap; + *tot_st_cap += edge_cap; + + pDst->st_edge.flow += edge_flow; + pDst->st_edge.flow0 = pDst->st_edge.flow; + *tot_st_flow += edge_flow; + + pSrc->st_edge.flow += edge_flow; + pSrc->st_edge.flow0 = pSrc->st_edge.flow; + *tot_st_flow += edge_flow; + +/* + pDst->st_edge.cap += e01->cap; + pDst->st_edge.cap0 = pDst->st_edge.cap; + *tot_st_cap += e01->cap; + + pDst->st_edge.flow += e01->flow; + pDst->st_edge.flow0 = pDst->st_edge.flow; + *tot_st_flow += e01->flow; + + pSrc->st_edge.flow += e01->flow; + pSrc->st_edge.flow0 = pSrc->st_edge.flow; + *tot_st_flow += e01->flow; +*/ + return 0; +} + + +/************************************************************** + (+) and (-) group V - connection + ================================ + + BNS_VERT_TYPE__AUX (+/-)-connection + (v) st_cap = + / \ st_flow = (cap0 - Delta0 - flow0) + (cap1 - Delta1 -flow1) + / \ + / \ cap = cap1 + / \ flow = (cap1 - Delta1 - flow1) + / \ + (-) (+) st_cap = cap1 + / \ / \ st_flow = cap1 - Delta1 + /cap0 \ /cap1 \ + flow0 flow1 + +*************************************************************** + + (+) supergroup Y - connection + ============================== + + (+) BNS_VT_C_POS_ALL (+) supergroup + Delta0 | ============== + not shown | cap = cap0+cap1 + | flow = flow0+flow1-Delta0-Delta1 + BNS_VERT_TYPE__AUX (y) <------------------ additional vertex: st_cap = cap0+cap1 + / \ st_flow = cap0+cap1 + cap=cap0 / \ cap = cap1 + flow=cap0-flow0 / \ flow = cap1 - flow1 - Delta1 + -Delta0 / \ + not-C (+) (+) Carbons st_cap = cap1 + BNS_VT_C_POS / \ / \ BNS_VT_C_POS_C st_flow = cap1 - Delta1 + / \ / \ + totals cap0 cap1 = sum of all cap going up into (+) from atoms or ChargeStruct + to (+): flow0 flow1= sum of all flow going up into (+) + Delta0 Delta1 = st_cap(+)-st_flow(+) before connection + Observations + ============ + A. Any Delta > 0 on (+) or (-) group decreases total (signed) charge by Delta + + B. Any alt path from an atom through ChargeStruct to an atom + does not change the total charge + + C. st_flow(+/-) = cap(+)-flow(+)-Delta(+) + cap(-)-flow(-)-Delta(-) = + = charge(+) + |max (-) charge| + charge(-) = const + (charge conservation) + + D. To decrease total charge: increase st_cap on (+) or (-) group, including supergroup + E. To increase total charge: increase st_cap on any (y) or (v)-connecting vertex + + F. To cancel charges: + 1. Forbid (+/-)-(+) or (+/-)-(-) edge + 2. Add delta>0 to (+/-) st_cap + 3. Add same delta to (+) or (-) st_cap + + +****************************************************************/ + +/************************************************************************************ + j2,j3 < j1 < j0 + + (+/-) <---- next step; if does not exist then + / \ st_cap1' := st_flow1' + / \ + / \ st_cap1' := cap01' + pv1 (+)super st_flow1':= cap01'-flow01' = flow02'+flow03' + j1 | + | cap01' := st_cap0' + | flow01':= st_cap0'-flow02'-flow03' + | + | st_cap0' := + ( ) pv0,j0 st_flow0':= cap2+st_cap3 + / \ + / \ cap03' = cap3 + / \ flow03' = cap3 - flow3 - Delta3 + / \ + st_cap2, st_flow2 (+) (+C) st_cap3' := cap3 + pv2,j2 pv3,j3 st_flow3' := cap3-Delta3 + / \ / \ Delta3 := st_cap3 - st_flow3 + cap2, flow2 cap3, flow3 = sums of incoming + **************************************************************************************/ + +int ConnectSuperCGroup( int nSuperCGroup, int nAddGroups[], int num_add, + int *pcur_num_vertices, int *pcur_num_edges, + int *tot_st_cap, int *tot_st_flow, + BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups ) +{ + BNS_EDGE **e0X = NULL, *e; + BNS_VERTEX **pvX = NULL, *pv0=NULL, *pv1=NULL, *pv=NULL; + int *jX = NULL, *iX = NULL; + int i, j, num_groups, j0, i1, j1, iXX, ret = 0, fst=0; + int cur_num_vertices = *pcur_num_vertices; + int cur_num_edges = *pcur_num_edges; + + if ( nSuperCGroup >= 0 ) + { + i1 = pTCGroups->nGroup[nSuperCGroup]; /* the supergroup */ + if ( i1 < 0 ) + return 0; + } + else + { + i1 = -1; + fst = 1; + } + + for ( i = num_groups = 0; i < num_add; i ++ ) + { + iXX = pTCGroups->nGroup[nAddGroups[i]]; + num_groups += (iXX >= 0 && iXX != i1); + } + if ( num_groups < 1 ) + { /* Y connect only 2 or more groups; V connects even 1 group */ + return 0; + } + + e0X = (BNS_EDGE **)inchi_calloc( num_groups + 1, sizeof(e0X[0]) ); + pvX = (BNS_VERTEX **)inchi_calloc( num_groups + 1, sizeof(pvX[0]) ); + jX = (int *)inchi_calloc( num_groups + 1, sizeof(jX[0]) ); + iX = (int *)inchi_calloc( num_groups + 1, sizeof(iX[0]) ); + + if ( !e0X || !pvX || !jX || !iX ) + { + ret = RI_ERR_ALLOC; + goto exit_function; + } + + /* create vert_ficpoint -- central Y-connection vertex */ + + j0 = cur_num_vertices; + pv0 = pBNS->vert + j0; /* center of the Y-connection; has number j0 */ + pv0->iedge = (pv0 - 1)->iedge + (pv0 - 1)->max_adj_edges; + pv0->max_adj_edges = num_groups + 1 + BNS_ADD_EDGES; /* Y-connection num. edges */ + pv0->num_adj_edges = 0; /* nothing connected yet */ + pv0->type = BNS_VT_YVCONNECTOR; + cur_num_vertices ++; + + if ( fst == 0 ) + { + /* find super c-group vertex pv1, number j1 */ + jX[0] = j1 = pTCGroups->pTCG[i1].nVertexNumber; + iX[0] = i1; + pvX[0] = pv1 = pBNS->vert + j1; + } + + /* find other c-group vertices */ + for( i = 0, j = 1; i < num_add; i ++ ) + { + iXX = pTCGroups->nGroup[nAddGroups[i]]; + if ( (iXX >= 0) && (iXX != i1) ) + { + iX[j] = iXX; + jX[j] = pTCGroups->pTCG[iXX].nVertexNumber; + pvX[j] = pBNS->vert + jX[j]; + j ++; + } + } + + /* grab (num_groups+1) free edges */ + for ( i = fst; i <= num_groups; i ++ ) + { + e = e0X[i] = pBNS->edge + cur_num_edges; + pv = pvX[i]; + j = jX[i]; + iXX = iX[i]; + /* connect all to pv0 */ + ret = ConnectTwoVertices( pv0, pv, e, pBNS, 1 ); + if ( IS_BNS_ERROR( ret ) ) + { + goto exit_function; + } + if ( i ) + { + /* from c-group to central Y-connecting vertex of from supergroup to (+/-) vertex */ + pTCGroups->pTCG[iX[i]].nForwardEdge = cur_num_edges; + } + else + { + /* from central Y-connecting vertex to supergroup */ + pTCGroups->pTCG[iX[i]].nBackwardEdge = cur_num_edges; + } + cur_num_edges ++; + } + + /* set flow and cap for incoming into pv0 edges */ + for ( i = 1; i <= num_groups; i ++ ) + { + int nDelta = pTCGroups->pTCG[iX[i]].st_cap - pTCGroups->pTCG[iX[i]].edges_cap; + int edge_cap = pTCGroups->pTCG[iX[i]].edges_cap + nDelta; /* added nDelta */ + int edge_flow = pTCGroups->pTCG[iX[i]].edges_cap-pTCGroups->pTCG[iX[i]].edges_flow /*-nDelta*/; + + ret = AddEdgeFlow( edge_cap, edge_flow, + e0X[i], pvX[i]/*src*/, pv0 /* dest*/, tot_st_cap, tot_st_flow ); + + if ( IS_BNS_ERROR( ret ) ) + { + goto exit_function; + } + } + + if ( fst == 0 ) + { + /* set flow and cap for going out of pv0 and into pv1 edge */ + int edge_cap = pv0->st_edge.cap; + int edge_flow = pv0->st_edge.cap - pv0->st_edge.flow; + + ret = AddEdgeFlow( pv0->st_edge.cap, pv0->st_edge.cap - pv0->st_edge.flow, + e0X[0], pv0/*src*/, pv1 /* dest*/, tot_st_cap, tot_st_flow ); + + if ( IS_BNS_ERROR( ret ) ) + { + goto exit_function; + } + + pTCGroups->pTCG[iX[0]].edges_cap += edge_cap; + pTCGroups->pTCG[iX[0]].edges_flow += edge_flow; + pTCGroups->pTCG[iX[0]].st_cap += edge_cap; + pTCGroups->pTCG[iX[0]].st_flow += edge_flow; + } + else + { + /* no supergroup => change cap to flow */ + *tot_st_cap += pv0->st_edge.flow - pv0->st_edge.cap; + pv0->st_edge.cap += pv0->st_edge.flow - pv0->st_edge.cap; + pv0->st_edge.cap0 = pv0->st_edge.cap; + } + + *pcur_num_vertices = cur_num_vertices; + *pcur_num_edges = cur_num_edges; + ret = num_groups; + +exit_function: + if ( e0X ) inchi_free( e0X ); + if ( pvX ) inchi_free( pvX ); + if ( jX ) inchi_free( jX ); + if ( iX ) inchi_free( iX ); + + return ret; +} + + +/*********************************************************************************/ +void AddStCapFlow( BNS_VERTEX *vert_ficpoint, int *tot_st_flow, int *tot_st_cap, int cap, int flow ) +{ + vert_ficpoint->st_edge.flow += flow; + *tot_st_flow += flow; + vert_ficpoint->st_edge.cap += cap; + *tot_st_cap += cap; + + vert_ficpoint->st_edge.flow0 = vert_ficpoint->st_edge.flow; + vert_ficpoint->st_edge.cap0 = vert_ficpoint->st_edge.cap; +} + + +/*********************************************************************************/ +void SetStCapFlow( BNS_VERTEX *vert_ficpoint, int *tot_st_flow, int *tot_st_cap, int cap, int flow ) +{ + *tot_st_flow += flow - vert_ficpoint->st_edge.flow; + vert_ficpoint->st_edge.flow = flow; + *tot_st_cap += cap - vert_ficpoint->st_edge.cap; + vert_ficpoint->st_edge.cap = cap; + + vert_ficpoint->st_edge.flow0 = vert_ficpoint->st_edge.flow; + vert_ficpoint->st_edge.cap0 = vert_ficpoint->st_edge.cap; +} + + +/********************************************************************************* +int AddCGroups2TCGBnStruct( BN_STRUCT *pBNS, StrFromINChI *pStruct, + VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups ) + + + *********************************************************************************/ +int AddCGroups2TCGBnStruct( BN_STRUCT *pBNS, StrFromINChI *pStruct, VAL_AT *pVA, + ALL_TC_GROUPS *pTCGroups, int nMaxAddEdges ) +{ + int ret = 0, ret1, ret2, ret3, bNeedsFlower; + inp_ATOM *at = pStruct->at; + int num_atoms = pStruct->num_atoms; + /*int num_tg = pTCGroups->num_tgroups;*/ + int num_cg = pTCGroups->num_tc_groups - pTCGroups->num_tgroups; + int fst_cg_vertex = pBNS->num_vertices; + int fst_cg_group = pTCGroups->num_tgroups; + int num_vertices = pBNS->num_vertices; + int num_edges = pBNS->num_edges; + int cg_charge = 0; + ICHICONST SRM *pSrm = pStruct->pSrm; + + /* ret = ReInitBnStruct( pBNS ); */ + + if ( num_cg > 0 ) + { + /* if ( cgi && cgi->num_c_groups && cgi->c_group ) */ + int i, i1, i2, j, j1, j2, k, k1, k2, n, c_point, c_neigh, cap, flow; + int cur_num_vertices, cur_num_edges; + BNS_VERTEX *vert_ficpoint, *vert_ficpoint_prev, *vert_ficpoint_base; /* fictitious vertex describing charge c-group */ + BNS_VERTEX *pv1, *pv2; + BNS_EDGE *edge; /* edge between that vertex and the tautomeric c_point */ + int nMaxCGroupNumber = 0; + MY_CONST C_NODE *pCN; + int cn_len, cn_bits, bMetalAtoms; + int type; + int tot_st_cap, tot_st_flow; + int nAddGroups[16]; + + + /* Debug: check overflow */ + if ( num_vertices >= pBNS->max_vertices ) + { + return BNS_VERT_EDGE_OVFL; + } + + nMaxCGroupNumber = num_cg; + /* clear all vertices not used until now */ + memset( pBNS->vert+num_vertices, 0, (pBNS->max_vertices - num_vertices)*sizeof(pBNS->vert[0]) ); + tot_st_cap = pBNS->tot_st_cap; + tot_st_flow = pBNS->tot_st_flow; + + /*****************************************/ + /* initialize new fictitious vertices */ + /* representing c-point groups, c-groups */ + /*****************************************/ + vert_ficpoint_prev = pBNS->vert + fst_cg_vertex - 1; + + for ( i = 0; i < num_cg; i ++ ) + { + /* + vert_ficpoint-1 is the last vertex; + vert_ficpoint is the being added vertex + Note: nGroupNumber are not contiguous + */ + vert_ficpoint = vert_ficpoint_prev + 1; + vert_ficpoint->iedge = vert_ficpoint_prev->iedge + vert_ficpoint_prev->max_adj_edges; + vert_ficpoint->max_adj_edges = pTCGroups->pTCG[i+fst_cg_group].num_edges+nMaxAddEdges; + vert_ficpoint->num_adj_edges = 0; + + vert_ficpoint->st_edge.flow += pTCGroups->pTCG[i+fst_cg_group].st_flow; + tot_st_flow += pTCGroups->pTCG[i+fst_cg_group].st_flow; + vert_ficpoint->st_edge.cap += pTCGroups->pTCG[i+fst_cg_group].st_cap; + tot_st_cap += pTCGroups->pTCG[i+fst_cg_group].st_cap; + + vert_ficpoint->st_edge.flow0 = vert_ficpoint->st_edge.flow; + vert_ficpoint->st_edge.cap0 = vert_ficpoint->st_edge.cap; + + vert_ficpoint->type = pTCGroups->pTCG[i+fst_cg_group].type; + /* save the vertex number */ + pTCGroups->pTCG[i+fst_cg_group].nVertexNumber = (int) (vert_ficpoint - pBNS->vert); + + vert_ficpoint_prev = vert_ficpoint; /* keep track of iedges */ + } + + cur_num_vertices = (int) (vert_ficpoint_prev - pBNS->vert) + 1; + cur_num_edges = num_edges; + + /*************************************************************/ + /* pass 1: */ + /* create ChargeStruct for c-points and connect them to */ + /* the vertices representing c-point groups; */ + /* set final atom st_cap, st_flow */ + /*************************************************************/ + for ( c_point = 0; c_point < num_atoms; c_point ++ ) + { + if ( !(k=pVA[c_point].cnListIndex) ) + continue; /* not a c-point */ + + k --; + pCN = cnList[k].pCN; /* pointer to the ChargeStruct */ + cn_len = cnList[k].len; /* length of the ChargeStruct */ + cn_bits = cnList[k].bits; /* bits: for M-recognition */ + /* cn_bits = cnList[k].bits; */ /* ChargeStruct type */ + bMetalAtoms = (cn_bits == cn_bits_Me); + vert_ficpoint_base = vert_ficpoint_prev; /* add aux vertices after this */ + + /* create disconnected auxiliary vertices of the at[c_point] ChargeStruct; add to them st_flow & st_cap */ + for ( i1 = 0; i1 < cn_len; i1 ++ ) + { + if ( !IS_BNS_VT_CHRG_STRUCT(pCN[i1].v.type) ) + { + continue; + } + /* the atom is always the first; the attached c-points are always the last */ + vert_ficpoint = vert_ficpoint_base + i1; /* i1 = 1, 2,.. less number of attached c-points */ + vert_ficpoint->iedge = vert_ficpoint_prev->iedge + vert_ficpoint_prev->max_adj_edges; + vert_ficpoint->max_adj_edges = pCN[i1].v.valence; /* do not add additional edges to aux vertices */ + vert_ficpoint->num_adj_edges = 0; + + cap = !bMetalAtoms? pCN[i1].v.cap : pCN[i1].v.cap? pSrm->nMetalMaxCharge_D : 0; + flow = !bMetalAtoms? pCN[i1].v.flow : pCN[i1].v.flow? pSrm->nMetalMaxCharge_D : 0; + + AddStCapFlow( vert_ficpoint, &tot_st_flow, &tot_st_cap, cap, flow ); + vert_ficpoint->type = pCN[i1].v.type; /* =BNS_VERT_TYPE__AUX */ + + vert_ficpoint_prev = vert_ficpoint; /* the last one will be vert_ficpoint for the next c-point */ + cur_num_vertices = (int) (vert_ficpoint - pBNS->vert) + 1; + + if ( vert_ficpoint->iedge + vert_ficpoint->max_adj_edges - pBNS->iedge >= pBNS->max_iedges ) + { + return BNS_VERT_EDGE_OVFL; + } + if ( cur_num_vertices >= pBNS->max_vertices ) + { + return BNS_VERT_EDGE_OVFL; + } + } + + /* connect the vertices with new edges, add edge flow and cap */ + for ( i1 = 0; i1 < cn_len; i1 ++ ) + { + pv1 = NULL; + k1 = -1; + + /* find vertex corresponding to i1 */ + + if ( pCN[i1].v.type & BNS_VERT_TYPE_ATOM ) + { + pv1 = pBNS->vert+c_point; /* may be only one atom -- the current c_point at i1==0 */ + /* add atom vertex st_cap and st_flow */ + cap = !bMetalAtoms? pCN[i1].v.cap : pCN[i1].v.cap? pSrm->nMetalMaxCharge_D : 0; + flow = !bMetalAtoms? pCN[i1].v.flow : pCN[i1].v.flow? pSrm->nMetalMaxCharge_D : 0; + AddStCapFlow( pv1, &tot_st_flow, &tot_st_cap, cap, flow ); + } + + else if ( IS_BNS_VT_C_GR(pCN[i1].v.type) ) + { + /* find c-group vertex by looking for its type */ + for( j = 0; j < num_cg; j ++ ) + { + if ( pCN[i1].v.type == pBNS->vert[fst_cg_vertex + j].type ) { + pv1 = pBNS->vert + fst_cg_vertex + j; + break; + } + } + /* index of the pTCGroups->pTCG[] */ + if ( pv1 ) + { + k1 = j + fst_cg_group; + if ( pTCGroups->pTCG[k1].type != pCN[i1].v.type || + pTCGroups->pTCG[k1].ord_num ) + { + return RI_ERR_PROGR; + } + } + } + + else if ( IS_BNS_VT_M_GR( pCN[i1].v.type ) ) + { + k1 = pTCGroups->nGroup[TCG_MeFlower0]; + if ( k1 < 0 || + pTCGroups->pTCG[k1].type != pCN[i1].v.type || + pTCGroups->pTCG[k1].ord_num || + !pSrm->bMetalAddFlower ) + { + return RI_ERR_PROGR; + } + pv1 = pBNS->vert + pTCGroups->pTCG[k1].nVertexNumber; + } + + else if ( IS_BNS_VT_CHRG_STRUCT(pCN[i1].v.type) ) + { + /* aux vertex */ + pv1 = vert_ficpoint_base + i1; + } + + if ( !pv1 ) + { + return BNS_BOND_ERR; + } + + /* connect pairs of vertices with new edges */ + for ( k = 0; k < MAX_CN_VAL && (i2=pCN[i1].e[k].neigh); k ++ ) + { + pv2 = NULL; + k2 = -1; + i2 --; /* neighbor */ + + /* find vertex corresponding to i2 */ + + if ( pCN[i2].v.type & BNS_VERT_TYPE_ATOM ) + { + pv2 = pBNS->vert+c_point; + cap = !bMetalAtoms? pCN[i2].v.cap : pCN[i2].v.cap? pSrm->nMetalMaxCharge_D : 0; + flow = !bMetalAtoms? pCN[i2].v.flow : pCN[i2].v.flow? pSrm->nMetalMaxCharge_D : 0; + /* add atom vertex st_cap and st_flow; this normally should not happen */ + AddStCapFlow( pv2, &tot_st_flow, &tot_st_cap, cap, flow ); + } + + else if ( IS_BNS_VT_C_GR(pCN[i2].v.type) ) + { + /* find c-group vertex by looking for its type */ + for( j = 0; j < num_cg; j ++ ) + { + if ( pCN[i2].v.type == pBNS->vert[fst_cg_vertex + j].type ) + { + pv2 = pBNS->vert + fst_cg_vertex + j; + break; + } + } + if ( pv2 ) { + k2 = j + fst_cg_group; + if ( pTCGroups->pTCG[k2].type != pCN[i2].v.type || + pTCGroups->pTCG[k2].ord_num ) + { + return RI_ERR_PROGR; + } + } + } + + else if ( IS_BNS_VT_M_GR( pCN[i2].v.type ) ) + { + k2 = pTCGroups->nGroup[TCG_MeFlower0]; + if ( k2 < 0 || + pTCGroups->pTCG[k2].type != pCN[i2].v.type || + pTCGroups->pTCG[k2].ord_num || + !pSrm->bMetalAddFlower ) + { + return RI_ERR_PROGR; + } + pv2 = pBNS->vert + pTCGroups->pTCG[k2].nVertexNumber; + } + + else if ( IS_BNS_VT_CHRG_STRUCT(pCN[i2].v.type) ) + { + pv2 = vert_ficpoint_base + i2; + } + + /* connect pv1 and pv2 */ + if ( !pv1 || !pv2 || pv1 == pv2 ) + { + return BNS_BOND_ERR; + } + + j1 = (int) (pv1 - pBNS->vert); + j2 = (int) (pv2 - pBNS->vert); + + /* create a new edge connecting pv1 and pv2 */ + edge = pBNS->edge + cur_num_edges; + if ( IS_BNS_VT_M_GR( pCN[i1].v.type ) && IS_BNS_VT_ATOM( pCN[i2].v.type ) || + IS_BNS_VT_M_GR( pCN[i2].v.type ) && IS_BNS_VT_ATOM( pCN[i1].v.type ) ) + { + /* at[c_point] is a metal or is treated as a metal; connect it to M-group */ + /* metal - M-group (i.e. Metal-Flower) edge */ + int nStCap, nStFlow; + bNeedsFlower = AtomStcapStflow( at, pVA, pSrm, c_point, &nStCap, &nStFlow, &edge->cap, &edge->flow ); + /* GetAtomToMCGroupInitEdgeCapFlow( &edge->cap, &edge->flow, pSrm, at, pVA, c_point ); */ + if ( !bNeedsFlower ) + { + return RI_ERR_PROGR; + } + pVA[c_point].nMetalGroupEdge = cur_num_edges + 1; + /* pBNS->vert[c_point].st_edge.cap += edge->flow;*/ /* where was this done ???*/ + pBNS->vert[c_point].st_edge.flow += edge->flow; + pBNS->vert[c_point].st_edge.cap += edge->flow + pVA[c_point].cInitFreeValences; + pBNS->vert[c_point].st_edge.flow0 = pBNS->vert[c_point].st_edge.flow; + pBNS->vert[c_point].st_edge.cap0 = pBNS->vert[c_point].st_edge.cap; + tot_st_flow += edge->flow; + tot_st_cap += edge->flow + pVA[c_point].cInitFreeValences; + } + else + { + edge->cap = !bMetalAtoms? pCN[i1].e[k].cap : pCN[i1].e[k].cap? pSrm->nMetalMaxCharge_D : 0; + edge->flow = !bMetalAtoms? pCN[i1].e[k].flow : pCN[i1].e[k].flow? pSrm->nMetalMaxCharge_D : 0; + } + + edge->forbidden = pCN[i1].e[k].bForbiddenEdge? BNS_EDGE_FORBIDDEN_MASK : 0; + + /* c-group incoming edges cap and flow needed in ConnectSuperCGroup() */ + /* + if ( k1 >= 0 ) { + pTCGroups->pTCG[k1].edges_cap += pCN[i1].e[k].cap; + pTCGroups->pTCG[k1].edges_flow += pCN[i1].e[k].flow; + } + if ( k2 >= 0 ) { + pTCGroups->pTCG[k2].edges_cap += pCN[i1].e[k].cap; + pTCGroups->pTCG[k2].edges_flow += pCN[i1].e[k].flow; + } + */ + + edge->pass = 0; + +#if ( RESET_EDGE_FORBIDDEN_MASK == 1 ) + edge->forbidden &= pBNS->edge_forbidden_mask; +#endif + + /* check edge overflow */ + if ( pv1->num_adj_edges >= pv1->max_adj_edges || + pv2->num_adj_edges >= pv2->max_adj_edges || + cur_num_edges >= pBNS->max_edges ) + { + return BNS_VERT_EDGE_OVFL; + } + + /* connect edge to the incident vertices and increment the counters of neighbors and edges */ + + ret = ConnectTwoVertices( pv1, pv2, edge, pBNS, 0 ); + + if ( IS_BNS_ERROR( ret ) ) + { + return ret; + } + + edge->cap0 = edge->cap; + edge->flow0 = edge->flow; + + /* save the edge index */ + type = IS_BNS_VT_C_GR(pv1->type)? pv1->type : + IS_BNS_VT_C_GR(pv2->type)? pv2->type : 0; + if ( type ) + { + /* the edge connects to a c-group */ + if ( type & BNS_VERT_TYPE_C_NEGATIVE ) + { + pVA[c_point].nCMinusGroupEdge = cur_num_edges+1; + } + else + { + pVA[c_point].nCPlusGroupEdge = cur_num_edges+1; + } + } + cur_num_edges ++; /* end of new edge creation */ + } + } + } + + + /*************************************************************/ + /* pass 2: */ + /* adjust bond cap, flow from the final atom st_cap, st_flow */ + /*************************************************************/ + for ( c_point = 0; c_point < num_atoms; c_point ++ ) + { + int st_cap, st_cap2, max_edge_flow; + pv1 = pBNS->vert + c_point; /* atom vertex */ + st_cap = pv1->st_edge.cap; + for ( k = 0; k < pv1->num_adj_edges; k ++ ) + { + edge = pBNS->edge + pv1->iedge[k]; /* incident edge */ + c_neigh = edge->neighbor12 ^ c_point; /* adjacent vertex */ + pv2 = pBNS->vert + c_neigh; + if ( c_neigh > c_point || !(pv2->type & BNS_VERT_TYPE_ATOM) ) + { + continue; + } + /* adjacent vertex is an atom; the edge is a bond; process each bond only once */ + st_cap2 = pv2->st_edge.cap; + /* the edge flow <= min( incident atom st_caps) */ + max_edge_flow = inchi_min( st_cap, st_cap2 ); + /* bond order <= triple bond (flow=2) */ + if ( pSrm->bMetalAddFlower && !pSrm->nMetalMinBondOrder && + (pVA[c_point].cMetal && pVA[c_point].cNumBondsToMetal || + pVA[c_neigh].cMetal && pVA[c_neigh].cNumBondsToMetal) ) + { + max_edge_flow = inchi_min( max_edge_flow, MAX_BOND_EDGE_CAP+1 ); + } + else + { + max_edge_flow = inchi_min( max_edge_flow, MAX_BOND_EDGE_CAP ); + } + if ( at[c_point].bond_type[k] == BOND_TYPE_SINGLE ) + { + /* the bond has not been changed due to stereo */ + edge->cap = edge->cap0 = max_edge_flow; + } + } + } + + /***********************************************************/ + /************** ************/ + /************** connect M-flower with new edges ************/ + /************** ************/ + /***********************************************************/ + + ret = ConnectMetalFlower(&cur_num_vertices, &cur_num_edges, &tot_st_cap, &tot_st_flow, pSrm, pBNS, pTCGroups); + + if ( ret < 0 ) + { + goto exit_function; + } + + /***********************************************************/ + /************** ************/ + /************** add additional vertices & edges ************/ + /************** to connect c-groups ************/ + /************** ************/ + /***********************************************************/ + + /* (+) supergroup, Y-connection */ + + k = 0; + nAddGroups[k ++] = TCG_Plus0; + nAddGroups[k ++] = TCG_Plus_C0; + nAddGroups[k ++] = TCG_Plus_M0; + + ret1 = ConnectSuperCGroup( TCG_Plus, + nAddGroups, + k, + &cur_num_vertices, + &cur_num_edges, + &tot_st_cap, + &tot_st_flow, + pBNS, + pTCGroups ); + + /* (-) supergroup, Y-connection */ + + k = 0; + nAddGroups[k ++] = TCG_Minus0; + nAddGroups[k ++] = TCG_Minus_C0; + nAddGroups[k ++] = TCG_Minus_M0; + + ret2 = ConnectSuperCGroup( TCG_Minus, + nAddGroups, + k, + &cur_num_vertices, + &cur_num_edges, + &tot_st_cap, + &tot_st_flow, + pBNS, + pTCGroups ); + + /******** connect (+) and (-) ***************/ + + k = 0; + nAddGroups[k ++] = TCG_Plus; + nAddGroups[k ++] = TCG_Minus; + + ret3 = ConnectSuperCGroup( -1, + nAddGroups, + k, + &cur_num_vertices, + &cur_num_edges, + &tot_st_cap, + &tot_st_flow, + pBNS, + pTCGroups ); + + /* Take care of the full charge */ + + cg_charge = pTCGroups->total_charge - pTCGroups->tgroup_charge - pTCGroups->charge_on_atoms; + ret = 1; + if ( ret3 > 0 ) + { + /* (+) and (-) or at least one of them have been connected */ + int nVertPlusMinus = cur_num_vertices - 1; + BNS_VERTEX *pVertPlusMinus = pBNS->vert + nVertPlusMinus; + BNS_VERTEX *pVertPlus = NULL, *pVertMinus = NULL, *pVert=NULL; + BNS_EDGE *pEdgePlus = NULL, *pEdgeMinus = NULL, *pEdge=NULL; + n = pTCGroups->nGroup[TCG_Plus] >= 0; /* (+)-supergroup exists */ + k = pTCGroups->nGroup[TCG_Minus] >= 0; /* (-)-supergroup exists */ + + if ( pVertPlusMinus->num_adj_edges == 2 && k+n==2 ) + { + pEdgePlus = pBNS->edge + pVertPlusMinus->iedge[0]; /* TCG_Plus was the 1st */ + pEdgeMinus = pBNS->edge + pVertPlusMinus->iedge[1]; /* TCG_Minus was the 2nd */ + } + + else if ( pVertPlusMinus->num_adj_edges == 1 && k+n==1 ) + { + if ( pTCGroups->nGroup[TCG_Plus] >= 0 ) + { + pEdgePlus = pBNS->edge + pVertPlusMinus->iedge[0]; + } + else if ( pTCGroups->nGroup[TCG_Minus] >= 0 ) + { + pEdgeMinus = pBNS->edge + pVertPlusMinus->iedge[0]; + } + } + + else if ( k+n ) + { + /* program error check */ + ret = BNS_BOND_ERR; + goto exit_function; + } + + if ( pEdgePlus ) + { + pVertPlus = pBNS->vert + (pEdgePlus->neighbor12 ^ nVertPlusMinus); + } + if ( pEdgeMinus ) + { + pVertMinus = pBNS->vert + (pEdgeMinus->neighbor12 ^ nVertPlusMinus); + } + + pVert = pVertPlus? pVertPlus : pVertMinus? pVertMinus : NULL; + pEdge = pEdgePlus? pEdgePlus : pEdgeMinus? pEdgeMinus : NULL; + if ( pEdgeMinus ) { + pTCGroups->nEdgeMinus = (int) (pEdgeMinus - pBNS->edge); + } + if ( pEdgePlus ) { + pTCGroups->nEdgePlus = (int) (pEdgePlus - pBNS->edge); + } + if ( pEdge ) { + pTCGroups->nEdge4charge = (int) (pEdge - pBNS->edge); + } + + /* set total charge */ + + if ( pVert && pEdge ) + { + /* do not check rescaps for now */ + if ( cg_charge > 0 ) + { + pVertPlusMinus->st_edge.cap += cg_charge; + tot_st_cap += cg_charge; + pVertPlusMinus->st_edge.cap0 = pVertPlusMinus->st_edge.cap; + } + + if ( cg_charge < 0 ) + { + pVert->st_edge.cap -= cg_charge; + tot_st_cap -= cg_charge; + pVert->st_edge.cap0 = pVert->st_edge.cap; + + if ( pEdge->cap - pEdge->flow + cg_charge < 0 ) + { + /* 2006-02-06: increase edge capacity to avoid clogging */ + pEdge->cap = pEdge->flow - cg_charge; + } + } + pTCGroups->added_charge = cg_charge; + } + if ( !cg_charge || (pVert && pEdge) ) + { + ret = 2; + } + } + + AddRadicalToMetal( &tot_st_cap, &tot_st_flow, pSrm, pBNS, pTCGroups ); + + + pBNS->num_edges = cur_num_edges; + pBNS->num_vertices = cur_num_vertices; + pBNS->num_c_groups = num_cg; + pBNS->tot_st_cap = tot_st_cap; + pBNS->tot_st_flow = tot_st_flow; + } + +exit_function: + return ret; +} + + +/********************************************************************************/ +int nNumEdgesToCnVertex( MY_CONST C_NODE *pCN, int len, int v ) +{ + int i, j, n, num_edges, v1 = v+1; + for ( i = 0, num_edges = 0; i < len; i ++ ) + { + for ( j = 0; j < MAX_CN_VAL && (n = pCN[i].e[j].neigh); j ++ ) + { + num_edges += ( i == v || n == v1 ); + } + } + return num_edges; +} + + +/********************************************************************************* +BN_STRUCT* AllocateAndInitTCGBnStruct( StrFromINChI *pStruct, VAL_AT *pVA, + ALL_TC_GROUPS *pTCGroups, + int nMaxAddAtoms, int nMaxAddEdges, + int max_altp, int *pNum_changed_bonds ) +allocate BN_STRUCT that has: + + pBNS->max_vertices = pTCGroups->nVertices + nMaxAddAtoms + pBNS->max_edges = pTCGroups->nEdges + + pBNS->max_vertices * (nMaxAddEdges + NUM_KINDS_OF_GROUPS) + pBNS->max_iedges = 2*pBNS->max_edges + pTCGroups->nAddIedges + + pBNS->len_alt_path = pBNS->max_vertices + iALTP_HDR_LEN + 1 + + max( pBNS->max_vertices/2, 16 ) + pBNS->max_altp = max_altp + +other members: + + pBNS->num_atoms = num_atoms; + pBNS->num_bonds = num_bonds; + pBNS->num_added_atoms = 0; + pBNS->num_t_groups = 0; + pBNS->num_c_groups = 0; + pBNS->nMaxAddAtoms = nMaxAddAtoms; + pBNS->nMaxAddEdges = nMaxAddEdges; + +atom vertices and bond edges: + --- vertex(atom) --- + st_cap = (at[].chem_bonds_valence - at[].valence) + pVA[].cInitFreeValences + st_flow = SUM{bond_orders; ALT_BOND counted as SINGLE} - at[].valence + --- edge(bond) --- + flow = bond_order - 1; for ALT_BOND flow = 0 + cap = min(min(st_cap of neighbors),2); for ALT_BOND cap = 1 + max number of edges per atom = number of bonds + + number of edges to ChargeStruct + + 1 (if atom is a tautomeric endpoint) + + nMaxAddEdges + --- NOTE --- + Here are not included nDelta(dots) from ChargeStruct and flow to ChargeStruct + + *********************************************************************************/ +BN_STRUCT* AllocateAndInitTCGBnStruct( StrFromINChI *pStruct, VAL_AT *pVA, + ALL_TC_GROUPS *pTCGroups, + int nMaxAddAtoms, int nMaxAddEdges, + int max_altp, int *pNum_changed_bonds ) +{ + inp_ATOM *at = pStruct->at; + int num_atoms = pStruct->num_atoms; + ICHICONST SRM *pSrm = pStruct->pSrm; + + BN_STRUCT *pBNS = NULL; + BNS_VERTEX *vert; + BNS_IEDGE *iedge; + + int neigh, num_changed_bonds=0; + U_CHAR bond_type, bond_mark; + int bNeedsFlower1, bNeedsFlower2, min_order; + + int i, j, k, m, n_edges, num_bonds, num_edges; + int f1, f2, c1, c2, edge_cap, edge_flow, st_cap, st_flow, flag_alt_bond; + int tot_st_cap, tot_st_flow; + int max_tg, max_edges, max_vertices, len_alt_path, max_iedges, num_iedges, num_altp; + + /* count vertices */ + max_tg = pTCGroups->num_tgroups; + /* +1 for a super-tautomeric group */ + /* max_vertices = num_atoms + nMaxAddAtoms + max_tg + 1; */ + max_vertices = pTCGroups->nVertices + nMaxAddAtoms; + + /* count edges */ + num_changed_bonds = 0; + num_bonds = pTCGroups->num_bonds; + + /* each atom has enough edges to belong to a tautomeric group + nMaxAddEdges */ + /* number of atoms is large enough to accommodate max. possible number of t-groups + nMaxAddAtoms */ + /* max_altp cannot be larger than BN_MAX_ALTP = 16 */ + num_edges = pTCGroups->nEdges; + + /* +max_tg for edges between t-groups and super-tautomeric group */ + max_edges = num_edges + (nMaxAddEdges + NUM_KINDS_OF_GROUPS)*max_vertices; + max_iedges = 2*max_edges + pTCGroups->nAddIedges; + len_alt_path = max_vertices+iALTP_HDR_LEN + 1; /* may overflow if an edge is traversed in 2 directions */ + len_alt_path += inchi_max( max_vertices/2, 16 ); /* to avoid the overflow */ + + if ( !( pBNS = (BN_STRUCT *)inchi_calloc( 1, sizeof(BN_STRUCT)) ) || + !( pBNS->edge = (BNS_EDGE *)inchi_calloc( max_edges, sizeof(BNS_EDGE)) ) || + !( pBNS->vert = (BNS_VERTEX *)inchi_calloc( max_vertices,sizeof(BNS_VERTEX)) ) || + !( pBNS->iedge = (BNS_IEDGE *)inchi_calloc( max_iedges, sizeof(BNS_IEDGE)) ) ) + { + return DeAllocateBnStruct( pBNS ); + } + + /* alt path init (standard spell) */ + for ( num_altp = 0; num_altp < max_altp && num_altp < BN_MAX_ALTP; num_altp ++ ) + { + if ( !( pBNS->altp[num_altp] = (BNS_ALT_PATH*)inchi_calloc( len_alt_path,sizeof(BNS_ALT_PATH))) ) + { + return DeAllocateBnStruct( pBNS ); + } + ALTP_ALLOCATED_LEN(pBNS->altp[num_altp]) = len_alt_path; + pBNS->len_alt_path = len_alt_path; /* ??? duplication ??? */ + /* re-init */ + ALTP_DELTA(pBNS->altp[num_altp]) = 0; + ALTP_START_ATOM(pBNS->altp[num_altp]) = NO_VERTEX; + ALTP_END_ATOM(pBNS->altp[num_altp]) = NO_VERTEX; + ALTP_PATH_LEN(pBNS->altp[num_altp]) = 0; + } + pBNS->alt_path = NULL; + pBNS->num_altp = 0; + pBNS->max_altp = num_altp; + + + /* fill vertices (no connectivity) */ + iedge = pBNS->iedge; + num_iedges = 0; + tot_st_cap = tot_st_flow = 0; + for ( i = 0; i < num_atoms; i ++ ) + { + /* count edges incident to pBNS->vert[i] */ + k = at[i].valence + (at[i].endpoint != 0) + (nMaxAddEdges /*+ NUM_KINDS_OF_GROUPS*/); + if ( (j = pVA[i].cnListIndex-1) >= 0 ) + { + /* add number of neighbors in the ChargeStruct */ + k += nNumEdgesToCnVertex( cnList[j].pCN, cnList[j].len, 0 ); + } + /* set max number of edges for the vertex */ + pBNS->vert[i].max_adj_edges = k; + pBNS->vert[i].iedge = iedge; + iedge += k; + /* add atom vertex cap */ + st_cap = 0; + st_flow = 0; + bNeedsFlower1 = AtomStcapStflow( at, pVA, pSrm, i, &c1, &f1, NULL, NULL ); + /* pVA[i].cNumBondsToMetal = bNeedsFlower1; */ + /* GetAtomStCapFlow( at, pVA, pSrm, i, &c1, &f1 ); */ + st_cap += c1; + st_cap += bNeedsFlower1? 0 : pVA[i].cInitFreeValences; + pBNS->vert[i].st_edge.cap = st_cap; /* the 1st time st_cap is set */ + pBNS->vert[i].st_edge.cap0 = pBNS->vert[i].st_edge.cap; + tot_st_cap += st_cap; + } + + num_iedges = (int) (iedge - pBNS->iedge); + if ( max_iedges - num_iedges < (nMaxAddEdges + NUM_KINDS_OF_GROUPS)*max_vertices ) + { + return DeAllocateBnStruct( pBNS ); + } + + pBNS->num_atoms = num_atoms; /* number of real atoms */ + pBNS->num_added_atoms = 0; + pBNS->num_t_groups = 0; /* number of added t-groups */ + pBNS->num_c_groups = 0; + pBNS->nMaxAddAtoms = nMaxAddAtoms; + pBNS->nMaxAddEdges = nMaxAddEdges; + + pBNS->num_vertices = num_atoms; /* current number of vertices, in general a sum of + pBNS->num_atoms + pBNS->num_t_groups + number of c-groups + number of auxiliary vertices + pBNS->num_added_atoms + */ + pBNS->max_vertices = max_vertices; + + + pBNS->num_bonds = num_bonds; /* number of real edges (bonds) */ + pBNS->max_edges = max_edges; + pBNS->max_iedges = max_iedges; + + + /* + To remove t-groups and added atoms: + + for ( i = 0; i < pBNS->num_atoms; i ++ ) + { + for ( j = pBNS->vert[i].num_adj_edges-1; 0 <= j; j -- ) + { + k = pBNS->edge[pBNS->vert[i].iedge[j]].neighbor12 ^ i; + if ( pBNS->vert[k].type & BNS_VERT_TYPE_ATOM ) + { + pBNS->vert[i].num_adj_edges = j+1; + break; + } + } + } + + pBNS->num_vertices = pBNS->num_atoms; + pBNS->num_edges = pBNS->num_bonds; + pBNS->num_added_atoms = 0; + pBNS->num_t_groups = 0; + pBNS->num_added_edges = 0; + + ALTP_DELTA(pBNS->alt_path) = 0; + ALTP_START_ATOM(pBNS->alt_path) = NO_VERTEX; + ALTP_END_ATOM(pBNS->alt_path) = NO_VERTEX; + ALTP_PATH_LEN(pBNS->alt_path) = 0; + + */ + + + /* add and fill edges and connectivity */ + for ( i = 0, n_edges = 0; i < num_atoms; i ++ ) + { + vert = pBNS->vert + i; /* pointer to the ith vertex */ + st_cap = 0; + st_flow = 0; + flag_alt_bond = 0; + for ( j = 0; j < at[i].valence; j ++ ) + { + neigh = at[i].neighbor[j]; + /* find this bond at the neighbor */ + for ( k = 0; k < at[neigh].valence; k ++ ) + { + if ( at[neigh].neighbor[k] == i ) + { + break; + } + } + bond_type = (at[i].bond_type[j] & BOND_TYPE_MASK); + bond_mark = (at[i].bond_type[j] & ~BOND_TYPE_MASK); + if ( bond_type != BOND_SINGLE && bond_type != BOND_DOUBLE && bond_type != BOND_TRIPLE ) + { + /* make unknown bonds single */ + bond_type = BOND_SINGLE; + at[i].bond_type[j] = bond_mark | bond_type; + num_changed_bonds ++; + } + if ( neigh > i ) + { + /* this is the first time we encounter this bond */ + bNeedsFlower1 = AtomStcapStflow( at, pVA, pSrm, i, &c1, &f1, NULL, NULL ); + /* GetAtomStCapFlow( at, pVA, pSrm, i, &c1, &f1 ); */ + c1 += bNeedsFlower1? 0 : pVA[i].cInitFreeValences; /* elevate cap to the lowest valence in ChargeStruct */ + bNeedsFlower2 = AtomStcapStflow( at, pVA, pSrm, neigh, &c2, &f2, NULL, NULL ); + /* GetAtomStCapFlow( at, pVA, pSrm, neigh, &c2, &f2 ); */ + c2 += bNeedsFlower2? 0 : pVA[neigh].cInitFreeValences; /* elevate cap to the lowest valence in ChargeStruct */ + + /* at this point -O would have st_cap=st_flow=0 because the lowest valence=1 for charge=-1 */ + /* however, if -O belongs to a t-group its cap would be 1, flow = 0 */ + /*f1 = MAX_AT_FLOW(at[i]);*/ + /*f2 = MAX_AT_FLOW(at[neigh]);*/ + + edge_flow = BondFlowMaxcapMinorder( at, pVA, pSrm, i, j, &edge_cap, &min_order, NULL); + + pBNS->edge[n_edges].neighbor1 = (AT_NUMB)i; + pBNS->edge[n_edges].neighbor12 = (AT_NUMB)(i ^ neigh); + pBNS->edge[n_edges].flow = + pBNS->edge[n_edges].flow0 = edge_flow; + pBNS->edge[n_edges].cap = + pBNS->edge[n_edges].cap0 = edge_cap; + pBNS->edge[n_edges].neigh_ord[0] = j; /* iedge to neigh index at vertex[i], i < neigh */ + pBNS->edge[n_edges].neigh_ord[1] = k; /* iedge to i index at vertex[neigh], i < neigh */ + pBNS->edge[n_edges].pass = 0; + pBNS->edge[n_edges].forbidden = 0; /* may be forbidden if edge_flow = 1: stereogenic fixed double bond */ + if ( bond_type == BOND_TYPE_DOUBLE ) + { + /* forbid changing stereogenic double bonds */ + for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[i].sb_parity[m]; m ++ ) { + if ( at[i].sb_ord[m] == j ) + { + pBNS->edge[n_edges].forbidden |= BNS_EDGE_FORBIDDEN_MASK; + break; + } + } + } + vert->iedge[j] = pBNS->vert[neigh].iedge[k] = n_edges ++; /* same iedge index as neighbor index in at[] */ + } + else + { + /* this is the second time we encounter this bond. It was stored at */ + int iedge2 = pBNS->vert[neigh].iedge[k]; + edge_cap = pBNS->edge[iedge2].cap; + edge_flow = pBNS->edge[iedge2].flow; + } + st_flow += edge_flow; + /* + st_cap += edge_cap; + */ + } + + vert->num_adj_edges = j; + /* + vert->st_edge.cap = + vert->st_edge.cap0 = st_cap; + */ + vert->st_edge.flow = + vert->st_edge.flow0 = st_flow; + vert->type = BNS_VERT_TYPE_ATOM; + /* + tot_st_cap += vert->st_edge.cap; + */ + tot_st_flow += vert->st_edge.flow; + } + + *pNum_changed_bonds = num_changed_bonds/2; + + pBNS->num_edges = n_edges; /* number of edges */ + pBNS->num_iedges = num_iedges; + pBNS->num_added_edges = 0; + + pBNS->tot_st_cap = tot_st_cap; + pBNS->tot_st_flow = tot_st_flow; + +/* exit_function: */ + + return pBNS; +} + + +/****************************************************************************/ +void IncrZeroBondsAndClearEndpts(inp_ATOM *at, int num_at, int iComponent ) +{ + int i, j; + for ( i = 0; i < num_at; i ++ ) + { + at[i].endpoint = 0; + at[i].component = iComponent; + for ( j = 0; j < at[i].valence; j ++ ) + { + if ( !at[i].bond_type[j] ) + { + at[i].bond_type[j] = BOND_TYPE_SINGLE; + at[i].chem_bonds_valence += BOND_TYPE_SINGLE; + } + } + } +} + + +/****************************************************************************/ +void IncrZeroBonds(inp_ATOM *at, int num_at, int iComponent ) +{ + int i, j; + for ( i = 0; i < num_at; i ++ ) + { + at[i].component = iComponent; + for ( j = 0; j < at[i].valence; j ++ ) + { + if ( !at[i].bond_type[j] ) + { + at[i].bond_type[j] = BOND_TYPE_SINGLE; + at[i].chem_bonds_valence += BOND_TYPE_SINGLE; + } + } + } +} + + +/****************************************************************************/ +void ClearEndpts(inp_ATOM *at, int num_at ) +{ + int i; + for ( i = 0; i < num_at; i ++ ) + { + at[i].endpoint = 0; + } +} + + +/****************************************************************************/ +#define ANY_VERT_TYPE(X) (((X) & (BNS_VERT_TYPE_ATOM | BNS_VERT_TYPE_TGROUP | BNS_VERT_TYPE_C_GROUP)) && \ + !((X) & (BNS_VERT_TYPE_SUPER_TGROUP))) +#define GRP_VERT_TYPE(X) (((X) & (BNS_VERT_TYPE_TGROUP | BNS_VERT_TYPE_C_GROUP)) && \ + !((X) & (BNS_VERT_TYPE_SUPER_TGROUP))) + +typedef struct tagVertexFlow +{ + int type; + Vertex v; + EdgeIndex e_In; + EdgeIndex e_Out; + EdgeFlow delta_In; + EdgeFlow delta_Out; + Vertex bUsed; /* indicates the charge edge belongs to already processed atom */ +} VF; +#define NUM_VF 3 +#define VF_USED_IN 1 +#define VF_USED_OUT 2 +#define VF_USED_ALL (VF_USED_IN | VF_USED_OUT) + + + +int GetDeltaChargeFromVF( BN_STRUCT *pBNS, VAL_AT *pVA, VF *vf ); + + + +/****************************************************************************/ +int GetDeltaChargeFromVF( BN_STRUCT *pBNS, VAL_AT *pVA, VF *vf ) +{ + int i, v = NO_VERTEX; + int ieIn1 = (!(vf->bUsed & VF_USED_IN) && vf->e_In >= 0 && vf->delta_In )? vf->e_In+1 : NO_VERTEX; + int ieOut1 = (!(vf->bUsed & VF_USED_OUT) && vf->e_Out >= 0 && vf->delta_Out)? vf->e_Out+1 : NO_VERTEX; + int nInitCharge, nPlusFlow, nMinusFlow, nDeltaCharge, nNumDeltaCharge, eCPlus, eCMinus; + + if ( !(vf->type & BNS_VERT_TYPE_C_GROUP) || + (vf->type & BNS_VERT_TYPE_SUPER_TGROUP) || + (ieIn1 == NO_VERTEX && ieOut1 == NO_VERTEX ) ) + { + return 0; + } + if ( vf->type & BNS_VERT_TYPE_C_NEGATIVE ) + { + /* negative charge edge */ + for ( i = 0; i < pBNS->num_atoms; i ++ ) + { + if ( pVA[i].nCMinusGroupEdge == ieIn1 || pVA[i].nCMinusGroupEdge == ieOut1 ) + { + v = i; + break; + } + } + } else + { + /* positive charge edge */ + for ( i = 0; i < pBNS->num_atoms; i ++ ) + { + if ( pVA[i].nCPlusGroupEdge == ieIn1 || pVA[i].nCPlusGroupEdge == ieOut1 ) + { + v = i; + break; + } + } + } + + if ( v == NO_VERTEX ) + return 0; + + nInitCharge = pVA[v].cInitCharge; + nPlusFlow = nMinusFlow = 0; + nNumDeltaCharge = 0; + + if ( (eCPlus = pVA[v].nCPlusGroupEdge-1) >= 0 ) + { + nPlusFlow = pBNS->edge[eCPlus].cap + - pBNS->edge[eCPlus].flow; + } + + if ( (eCMinus = pVA[v].nCMinusGroupEdge-1) >= 0 ) + { + nMinusFlow = -pBNS->edge[eCMinus].flow; + } + nInitCharge += nPlusFlow + nMinusFlow; + + nDeltaCharge = 0; + + if ( !(vf[0].bUsed & VF_USED_OUT) ) + { + if ( vf[0].e_Out==eCPlus || vf[0].e_Out==eCMinus ) + { + nDeltaCharge -= vf[0].delta_Out; + vf[0].bUsed |= VF_USED_OUT; + } + } + + if ( !(vf[0].bUsed & VF_USED_IN) ) + { + if ( vf[0].e_In==eCPlus || vf[0].e_In==eCMinus ) + { + nDeltaCharge -= vf[0].delta_In; + vf[0].bUsed |= VF_USED_IN; + } + } + + if ( !nInitCharge && nDeltaCharge ) + { + nNumDeltaCharge ++; + } + else if ( nInitCharge && 0 == nInitCharge + nDeltaCharge ) + { + nNumDeltaCharge --; + } + + return nNumDeltaCharge; +} + + +/****************************************************************************/ +int EvaluateChargeChanges( BN_STRUCT *pBNS, VAL_AT *pVA, int *pnDeltaH, int *pnDeltaCharge, int *pnNumVisitedAtoms ) +{ + int pass, i, j, v0, v1, v2, v, ineigh1, /*ineigh2,*/ vLast, n, delta, ret, ie, err = 0; + BNS_EDGE *edge; + int nDeltaH, nDeltaCharge, iPrev, nInitCharge, nPlusFlow, nMinusFlow; + int nNumDeltaH = 0; + int nNumDeltaCharge = 0; + int nNumVisitedAtoms = 0; + VF vf[NUM_VF+1]; + + *pnDeltaH = 0; + *pnDeltaCharge = 0; + *pnNumVisitedAtoms = 0; + + for ( pass = pBNS->num_altp-1, ret = 0; 0 <= pass; pass -- ) + { + + pBNS->alt_path = pBNS->altp[pass]; + v1 = ALTP_START_ATOM(pBNS->alt_path); + n = ALTP_PATH_LEN(pBNS->alt_path); + delta = ALTP_DELTA(pBNS->alt_path); + vLast = ALTP_END_ATOM(pBNS->alt_path); + v0 = v2 = NO_VERTEX; + + memset( vf, 0, sizeof(vf) ); + for ( i = 0; i < (int)(sizeof(vf)/sizeof(vf[0])); i ++ ) + { + vf[i].v = NO_VERTEX; /* = -2 */ + vf[i].e_In = NO_VERTEX; + vf[i].e_Out = NO_VERTEX; + } + iPrev = 0; + /* add to the queue */ + if ( ANY_VERT_TYPE(pBNS->vert[v1].type) ) + { + if (pBNS->vert[v1].type & BNS_VERT_TYPE_ATOM) + { + nNumVisitedAtoms ++; + } + vf[2].type = pBNS->vert[v1].type; + vf[2].v = v1; + iPrev = 2; + } + + nNumDeltaH = 0; + nNumDeltaCharge = 0; + nNumVisitedAtoms = 0; + + for ( i = 0; i < n; i ++, delta = -delta, v0 = v1, v1 = v2 ) + { + ineigh1 = ALTP_THIS_ATOM_NEIGHBOR(pBNS->alt_path, i); /* v1->v2 neighbor */ + /*ineigh2 = ALTP_NEXT_ATOM_NEIGHBOR(pBNS->alt_path, i);*/ /* v2->v1 neighbor */ + edge = pBNS->edge + (ie=pBNS->vert[v1].iedge[ineigh1]); + /* follow the BN Structure, not the inp_ATOM, to take care of swithching to + t-groups, c-groups or other fictitious edges/vertices + */ + + if ( iPrev ) + { + /* add exit delta and edge */ + vf[2].e_Out = ie; + vf[2].delta_Out = delta; + } + + v2 = edge->neighbor12 ^ v1; /* next vertex */ + if (pBNS->vert[v2].type & BNS_VERT_TYPE_ATOM) + { + nNumVisitedAtoms ++; + } + + if ( (ANY_VERT_TYPE(pBNS->vert[v2].type) || i == n-1) && + (vf[0].type & BNS_VERT_TYPE_C_GROUP) && vf[0].bUsed != VF_USED_ALL ) + { + /* unused vertex is about to be discarded */ + nNumDeltaCharge += GetDeltaChargeFromVF( pBNS, pVA, &vf[0] ); + } + + if ( ANY_VERT_TYPE(pBNS->vert[v2].type) ) + { + /* shift the queue */ + vf[0] = vf[1]; + vf[1] = vf[2]; + vf[2] = vf[3]; /* make vf[2] empty */ + /* add next vertex */ + vf[2].v = v2; + vf[2].type = pBNS->vert[v2].type; + vf[2].e_In = ie; + vf[2].delta_In = delta; + iPrev = 2; /* indicates a newly added vertex */ + } + else if ( i == n-1 ) + { + /* shift the queue */ + vf[0] = vf[1]; + vf[1] = vf[2]; + vf[2] = vf[3]; /* make vf[2] empty */ + iPrev = 1; /* indicates the last vertex */ + } + else + { + iPrev = 0; /* no new vertex has been added */ + } + + if ( iPrev && (vf[1].type & BNS_VERT_TYPE_ATOM)) + { + /* a new vertex has just been added and */ + /* an atom is in the middle of the queue */ + EdgeIndex eCPlus, eCMinus; + v = vf[1].v; + nInitCharge = pVA[v].cInitCharge; + nPlusFlow = nMinusFlow = 0; + if ( (eCPlus = pVA[v].nCPlusGroupEdge-1) >= 0 ) + { + nPlusFlow = pBNS->edge[eCPlus].cap + - pBNS->edge[eCPlus].flow; + } + if ( (eCMinus = pVA[v].nCMinusGroupEdge-1) >= 0 ) + { + nMinusFlow = -pBNS->edge[eCMinus].flow; + } + nInitCharge += nPlusFlow + nMinusFlow; + + nDeltaH = nDeltaCharge = 0; + + if ( vf[0].type & BNS_VERT_TYPE_TGROUP ) + { + nDeltaH -= delta; + } + else if ( (vf[0].type & BNS_VERT_TYPE_C_GROUP) && + !(vf[0].bUsed & VF_USED_OUT) ) + { + if ( vf[0].e_Out==eCPlus || vf[0].e_Out==eCMinus ) + { + nDeltaCharge -= vf[0].delta_Out; + vf[0].bUsed |= VF_USED_OUT; + } + } + + if ( vf[2].type & BNS_VERT_TYPE_TGROUP ) + { + nDeltaH += delta; + } + else if ( (vf[2].type & BNS_VERT_TYPE_C_GROUP) && + !(vf[2].bUsed & VF_USED_IN) ) + { + if ( vf[2].e_In==eCPlus || vf[2].e_In==eCMinus ) + { + nDeltaCharge -= vf[2].delta_In; + vf[2].bUsed |= VF_USED_IN; + } + } + + if ( !nInitCharge && nDeltaCharge ) + { + nNumDeltaCharge ++; + } + else if ( nInitCharge && 0 == nInitCharge + nDeltaCharge ) + { + nNumDeltaCharge --; + } + + nNumDeltaH += abs(nDeltaH); + /* nNumDeltaCharge += abs(nDeltaCharge); */ + vf[1].bUsed = VF_USED_ALL; + } + } + + for ( j = 0; j < 3; j ++ ) + { + nNumDeltaCharge += GetDeltaChargeFromVF( pBNS, pVA, &vf[j] ); + } + + *pnDeltaH += nNumDeltaH; + *pnDeltaCharge += nNumDeltaCharge; + *pnNumVisitedAtoms += nNumVisitedAtoms; + + + if ( v2 != vLast ) + { + err = BNS_PROGRAM_ERR; + } + } + return err? err : ret; +} + + +/****************************************************************************/ +int RunBnsTestOnce( BN_STRUCT *pBNS, BN_DATA *pBD, VAL_AT *pVA, Vertex *pvFirst, Vertex *pvLast, + int *pPathLen, int *pnDeltaH, int *pnDeltaCharge, int *pnNumVisitedAtoms ) +{ + int bChangeFlow = 0; /* do not change flow */ + int delta, ret, ret2, pass; + + ReInitBnStructAltPaths( pBNS ); + pass = 0; + pBNS->alt_path = pBNS->altp[pass]; + pBNS->num_altp = 0; + pBNS->bChangeFlow = 0; + delta=BalancedNetworkSearch ( pBNS, pBD, bChangeFlow ); + if ( delta > 0 ) + { + pBNS->alt_path = pBNS->altp[pass]; + *pvFirst = ALTP_START_ATOM(pBNS->alt_path); + *pPathLen = ALTP_PATH_LEN(pBNS->alt_path); + *pvLast = ALTP_END_ATOM(pBNS->alt_path); + pBNS->num_altp ++; + ret2 = EvaluateChargeChanges( pBNS, pVA, pnDeltaH, pnDeltaCharge, pnNumVisitedAtoms ); + } + else + { + *pvFirst = NO_VERTEX; + *pPathLen = 0; + *pvLast = NO_VERTEX; + ret2 = 0; + } + + ReInitBnStructAltPaths( pBNS ); + + ret = ReInitBnData( pBD ); + + return (delta >= 0 && ret > 0 )? -ret + : delta; +} + + +/****************************************************************************/ +int RunBnsRestoreOnce( BN_STRUCT *pBNS, BN_DATA *pBD, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups ) +{ + /* run BNS for the first time */ + int nTotalDelta = 0, ret = 0; + int nDelta; + + ReInitBnStructAltPaths( pBNS ); + + do + { + nDelta = RunBalancedNetworkSearch( pBNS, pBD, BNS_EF_CHNG_FLOW ); + if ( IS_BNS_ERROR(nDelta) ) + { + ret = nDelta; + goto exit_function; + } + nTotalDelta += nDelta; + ReInitBnStructAltPaths( pBNS ); + ret = ReInitBnData( pBD ); + if ( ret > 0 ) + { + ret = -ret; + goto exit_function; + } + } + while( nDelta > 0 && ret == 0 ); + + pBNS->tot_st_flow += 2*nTotalDelta; + + ret = nTotalDelta; + +exit_function: + return ret; +} + + +/****************************************************************************/ +int comp_cc_cand( const void *a1, const void *a2 ) +{ + const CC_CAND *p1 = (const CC_CAND *) a1; + const CC_CAND *p2 = (const CC_CAND *) a2; + int ret; + + if ( ret = (int)p2->cMetal - (int)p1->cMetal ) + return ret; /* metal first */ + if ( ret = (int)p2->cNumBondsToMetal - (int)p1->cNumBondsToMetal ) + return ret; /* connected to metal first */ + if ( ret = (int)p2->cPeriodicRowNumber - (int)p1->cPeriodicRowNumber ) + return ret; /* heaviest first */ + if ( ret = (int)p2->num_bonds - (int)p1->num_bonds ) + return ret; /* more bonds first */ + if ( ret = (int)p1->chem_valence - (int)p2->chem_valence ) + return ret; /* less bond order first */ + if ( !p1->cNumValenceElectrons && p2->cNumValenceElectrons ) + return -1; /* no valence electrons first */ + if ( !p2->cNumValenceElectrons && p1->cNumValenceElectrons ) + return -1; /* no valence electrons first */ + if ( (int)p2->cNumValenceElectrons - (int)p1->cNumValenceElectrons ) + return ret; /* more valence electrons first */ + ret = (int)p2->iat - (int)p1->iat; /* greater canon number first */ + + return ret; +} + + +/***************************************************************************************************** +Locate E1=C-E2 where + e ev are the edges + + E1 and E2 are atoms that belong to the same t-group + C is an atom that does not belong to any t-group + e is a forbidden edge + ev is not a forbidden edge + + Make changes so that: + E1(d)-C(d)-E2 + + where (d) means doublet radical + +*/ +/**************************************************************************************************/ +int get_pVA_atom_type( VAL_AT *pVA, inp_ATOM *at, int iat, int bond_type ) +{ + int type = 0, val; + if ( pVA[iat].cNumValenceElectrons == 4 ) + { + if ( pVA[iat].cPeriodicRowNumber == 1 ) + { + type |= EL_TYPE_C; + } + } + + else if ( pVA[iat].cNumValenceElectrons == 6 ) + { + if ( pVA[iat].cPeriodicRowNumber == 1 ) + { + type |= EL_TYPE_O; + } else if ( pVA[iat].cPeriodicRowNumber < 5 ) + { + type |= EL_TYPE_S; + } + if ( bond_type == BOND_TYPE_SINGLE && + (type & (EL_TYPE_O | EL_TYPE_S)) && + 1 == nNoMetalBondsValence(at, iat ) && + 1 == nNoMetalNumBonds(at, iat) ) + { + type |= EL_TYPE_OSt; + } + } + + else if ( pVA[iat].cNumValenceElectrons == 5 ) + { + if ( pVA[iat].cPeriodicRowNumber == 1 ) + { + type |= EL_TYPE_N; + } + else + { + type |= EL_TYPE_P; + } + } + + else if ( !is_el_a_metal(pVA[iat].cPeriodicNumber) ) + { + type |= EL_TYPE_X; + } + + /* check for possibility to be a tautomeric endpoint (that is, be a Mobile H site) */ + val = get_endpoint_valence( at[iat].el_number ); + + if ( val && val > at[iat].valence && !at[iat].radical && + -1 <= at[iat].charge && at[iat].charge <= 0 && + val == at[iat].chem_bonds_valence - at[iat].charge + at[iat].num_H ) + { + type |= EL_TYPE_PT; + } + + return type; +} + + +/*************************************************************************************/ +int AllocEdgeList( EDGE_LIST *pEdges, int nLen ) +{ + switch( nLen ) + { + case EDGE_LIST_FREE: + if ( NULL != pEdges->pnEdges ) + { + inchi_free( pEdges->pnEdges ); + } + /* fall through */ + case EDGE_LIST_CLEAR: + memset( pEdges, 0, sizeof(*pEdges) ); + break; + default: + if ( nLen > 0 && nLen != pEdges->num_alloc ) + { + EdgeIndex *tmp_edges = pEdges->pnEdges; + int tmp_num = pEdges->num_edges; + pEdges->pnEdges = (EdgeIndex *)inchi_calloc( nLen, sizeof(pEdges->pnEdges[0])); + if ( !pEdges->pnEdges ) + { + return RI_ERR_ALLOC; + } + tmp_num = inchi_min( tmp_num, nLen ); + if ( tmp_edges && tmp_num > 0 ) + { + memcpy( pEdges->pnEdges, tmp_edges, tmp_num * sizeof(pEdges->pnEdges[0]) ); + pEdges->num_edges = tmp_num; + } + else + { + pEdges->num_edges = 0; + } + if ( tmp_edges ) + { + inchi_free( tmp_edges ); + } + pEdges->num_alloc = nLen; + return 0; + } + break; + } + + return 0; +} + + +/********************************************************************/ +int AddToEdgeList( EDGE_LIST *pEdges, int iedge, int nAddLen ) +{ + if ( pEdges->num_alloc == pEdges->num_edges ) + { + int ret; + if ( nAddLen <= 0 ) + { + return RI_ERR_PROGR; + } + if ( ret = AllocEdgeList( pEdges, pEdges->num_alloc + nAddLen ) ) + { + return ret; + } + } + + pEdges->pnEdges[pEdges->num_edges ++] = (EdgeIndex)iedge; + return 0; +} + + +/********************************************************************/ +int RemoveFromEdgeListByIndex( EDGE_LIST *pEdges, int index ) +{ + int len; + if ( 0 <= (len = pEdges->num_edges - index - 1) ) + { + if ( len ) + { + memmove( pEdges->pnEdges+index, pEdges->pnEdges+index+1, len*sizeof(pEdges->pnEdges[0])); + } + pEdges->num_edges --; + pEdges->pnEdges[pEdges->num_edges] = 0; + return 0; + } + return -1; +} + + +/********************************************************************/ +int FindInEdgeList( EDGE_LIST *pEdges, int iedge ) +{ + int i; + EdgeIndex ie = iedge; + for ( i = pEdges->num_edges-1; 0 <= i; i -- ) + { + if ( ie == pEdges->pnEdges[i] ) + { + return i; + } + } + + return -1; +} + + +/********************************************************************/ +int RemoveFromEdgeListByValue( EDGE_LIST *pEdges, int iedge ) +{ + int i, ret, n = 0; + EdgeIndex ie = iedge; + for ( i = pEdges->num_edges-1; 0 <= i; i -- ) + { + if ( ie == pEdges->pnEdges[i] ) + { + if ( ret = RemoveFromEdgeListByIndex( pEdges, i ) ) + { + return ret; + } + n ++; + } + } + + return n; +} + + +/********************************************************************/ +int AllocBfsQueue( BFS_Q *pQ, int num_at, int min_ring_size ) +{ + int ret = 0; + switch( num_at ) + { + case BFS_Q_FREE: + if ( pQ->q ) + { + pQ->q = QueueDelete( pQ->q ); + } + if ( pQ->nAtomLevel ) + { + inchi_free( pQ->nAtomLevel ); + } + if ( pQ->cSource ) + { + inchi_free( pQ->cSource ); + } + /* fall through */ + case BFS_Q_CLEAR: + memset( pQ, 0, sizeof( *pQ ) ); + return 0; + + default: + if ( num_at <= 0 ) + { + ret = RI_ERR_PROGR; + goto exit_function; + } + if ( num_at > pQ->num_at ) + { + if ( pQ->num_at ) + { + AllocBfsQueue( pQ, BFS_Q_FREE, 0 ); + } + pQ->q = QueueCreate( num_at+1, sizeof(qInt) ); + pQ->nAtomLevel = (AT_RANK*)inchi_calloc( sizeof(pQ->nAtomLevel[0]), num_at ); + pQ->cSource = (S_CHAR *)inchi_calloc( sizeof(pQ->cSource[0]), num_at ); + if ( !pQ->q || !pQ->cSource || !pQ->nAtomLevel ) + { + ret = RI_ERR_ALLOC; + goto exit_function; + } + pQ->num_at = num_at; + } + pQ->min_ring_size = min_ring_size; + } + +exit_function: + return ret; +} + + +/*************************************************************************************/ +void RemoveForbiddenEdgeMask( BN_STRUCT *pBNS, EDGE_LIST *pEdges, int forbidden_edge_mask ) +{ + int i, mask = ~forbidden_edge_mask; + for ( i = 0; i < pEdges->num_edges; i ++ ) + { + pBNS->edge[pEdges->pnEdges[i]].forbidden &= mask; + } +} + + +/*************************************************************************************/ +void SetForbiddenEdgeMask( BN_STRUCT *pBNS, EDGE_LIST *pEdges, int forbidden_edge_mask ) +{ + int i; + for ( i = 0; i < pEdges->num_edges; i ++ ) + { + pBNS->edge[pEdges->pnEdges[i]].forbidden |= forbidden_edge_mask; + } +} + + +/****************************************************************************/ +void RemoveForbiddenBondFlowBits( BN_STRUCT *pBNS, int forbidden_edge_mask_int ) +{ + BNS_EDGE *e; + int i; + int inv_forbidden_edge_mask = ~forbidden_edge_mask_int; + for ( i = 0, e = pBNS->edge; i < pBNS->num_bonds; i ++, e ++ ) + { + e->forbidden &= inv_forbidden_edge_mask; + } +} + + +/****************************************************************************************************** + upper vc + edge / + v1[i0]---v0 + \ / + \ / + \ / + v1[i1] + | + | + atom +*/ +int GetChargeFlowerUpperEdge( BN_STRUCT *pBNS, VAL_AT *pVA, int nChargeEdge ) +{ + int ret = NO_VERTEX, i, j, k, i0, i1; + Vertex v0, v1[3], vc, v_t, v; + BNS_EDGE *pe, *pe1[3], *pe_t; + BNS_VERTEX *pv0, *pv1[3], *pv_t; + + if ( nChargeEdge < 0 ) + { + goto exit_function; + } + + pe = pBNS->edge + nChargeEdge; + vc = pe->neighbor1; /* charge vertex */ + if ( !IS_BNS_VT_C_GR(pBNS->vert[vc].type) ) + { + vc = vc ^ pe->neighbor12; + } + + v0 = vc ^ pe->neighbor12; /* ChargeStruct vertex ? */ + pv0 = pBNS->vert + v0; + if ( IS_BNS_VT_ATOM(pv0->type) ) { + goto exit_function; /* no charge flower exists */ + } + + /* 2 edges from v0 */ + for ( i = j = 0; i < pv0->num_adj_edges && j < 3; i ++ ) + { + pe1[j] = pBNS->edge + pv0->iedge[i]; + if ( vc != ( v1[j] = pe1[j]->neighbor12 ^ v0 ) && + (pv1[j] = pBNS->vert + v1[j], + !IS_BNS_VT_ATOM(pv1[j]->type) && !IS_BNS_VT_C_GR(pv1[j]->type)) ) + { + j ++; + } + } + + if ( j != 2 || i != pv0->num_adj_edges ) + { + goto exit_function; + } + + if ( pv1[1]->num_adj_edges == 2 && + pv1[0]->num_adj_edges == 3 ) + { + i0 = 1; + i1 = 0; + } + else if ( pv1[0]->num_adj_edges == 2 && + pv1[1]->num_adj_edges == 3 ) + { + i0 = 0; + i1 = 1; + } else + { + goto exit_function; + } + + /* additional check: traverse edges around v1[i1] */ + pv_t = pv1[i1]; + v_t = v1[i1]; + for ( i = k = 0; i < pv_t->num_adj_edges; i ++ ) + { + pe_t = pBNS->edge + pv_t->iedge[i]; + v = pe_t->neighbor12 ^ v_t; /* v1[i1] neighbor */ + if ( v == v0 ) + { + k += 1; + } + if ( v == v1[i0] ) + { + k += 2; + } + if ( IS_BNS_VT_ATOM(pBNS->vert[v].type) ) + { + k += 4; + } + } + if ( k != 7 ) + { + goto exit_function; + } + + ret = (int) (pe1[i0] - pBNS->edge); + +exit_function: + return ret; +} + + + +#if (INCLUDE_NORMALIZATION_ENTRY_POINT == 1 ) +/******************************************************************************************** +input: allocate (num_at+num_deleted_H) atoms in inp_ATOM *at_norm, *at_fixed_bonds_out + allocate t_group_info +*********************************************************************************************/ +int NormalizeStructure( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, + StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, + VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + inp_ATOM *at_norm, inp_ATOM *at_fixed_bonds_out, T_GROUP_INFO *t_group_info ) +{ + int i, ret, num_endpoints, nLenTaut; + int num_at = pStruct->num_atoms; + int num_deleted_H = pStruct->num_deleted_H; + int len_at = num_at + num_deleted_H; + /* + T_GROUP_INFO tgi; + T_GROUP_INFO *t_group_info = &tgi; + inp_ATOM *at_fixed_bonds_out = NULL; + inp_ATOM *at_norm = NULL; + + at_norm = (inp_ATOM *)inchi_calloc( len_at, sizeof(at_norm[0]) ); + at_fixed_bonds_out = (inp_ATOM *)inchi_calloc( len_at, sizeof(at_fixed_bonds_out[0]) ); + if ( !at_norm || !at_fixed_bonds_out ) { + if ( at_norm ) inchi_free( at_norm ); + if ( at_fixed_bonds_out ) inchi_free( at_fixed_bonds_out ); + ret = RI_ERR_ALLOC; + goto exit_function; + } + */ +/* call normalization only */ + memset( t_group_info, 0, sizeof(t_group_info[0]) ); + t_group_info->tni.nNumRemovedExplicitH = pStruct->num_deleted_H; + t_group_info->bTautFlags = ip->bTautFlags; + t_group_info->bTautFlagsDone = 0; /* (ip->bTautFlagsDone | sd->bTautFlagsDone[INCHI_BAS]);*/ + + memcpy( at2, at, len_at*sizeof(at2[0])); + pStruct->at = at2; + ret = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + pStruct->at = at; + if ( ret < 0 ) { + goto exit_function; + } +#if ( FIND_RING_SYSTEMS == 1 ) + ret = MarkRingSystemsInp( at2, num_at, 0 ); + if ( ret < 0 ) { + goto exit_function; + } +#endif + memcpy( at_norm, at2, len_at * sizeof(at_norm[0]) ); + for ( i = 0, num_endpoints = 0; i < num_at; i ++ ) { + num_endpoints += (0 != at_norm[i].endpoint); + at_norm[i].endpoint = 0; + } + + ret = mark_alt_bonds_and_taut_groups ( ic, pCG, at_norm, at_fixed_bonds_out, num_at, t_group_info, + NULL /* &inpbTautFlags*/, NULL /*inpbTautFlagsDone*/ ); + if ( ret < 0 ) { + goto exit_function;/* out of RAM or other normalization problem */ + } + /* after normalization, t_group_info->t_group[i].num[0] = number of H + number of (-) */ + /* t_group_info->t_group[i].num[1] = number of (-) */ + + /* --- count t-groups, remove (-)-only t-groups, replace -------------------------------*/ + /* t_group_info->t_group[i].num[0] with */ + /* t_group_info->t_group[i].num[0]-t_group_info->t_group[i].num[1] */ + nLenTaut = CountTautomerGroupsInpAt( at_norm, num_at, t_group_info ); + ret = nLenTaut; +exit_function: + return ret; +} +#endif + + + +/****************************************************************************/ +int MakeOneInChIOutOfStrFromINChI2( struct tagCANON_GLOBALS *pCG, + INCHI_CLOCK *ic, + ICHICONST INPUT_PARMS *ip_inp, + STRUCT_DATA *sd_inp, + BN_STRUCT *pBNS, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, + VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + T_GROUP_INFO **t_group_info, + inp_ATOM **at_norm, inp_ATOM **at_prep ) +{ + int ret; + INPUT_PARMS ip_loc, *ip; + STRUCT_DATA sd_loc, *sd; + + ip_loc = *ip_inp; + sd_loc = *sd_inp; + ip = &ip_loc; + sd = &sd_loc; + + memset( sd, 0, sizeof(*sd) ); + + /* create structure out of BNS */ + memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); + pStruct->at = at2; + ret = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + pStruct->at = at; + if ( ret < 0 ) + { + goto exit_function;/* out of RAM or other normalization problem */ + } + + pStruct->at = at; + + ret = MakeOneInChIOutOfStrFromINChI( pCG, ic, ip, sd, pStruct, + at2, at3, pTCGroups ); + + if ( ret < 0 ) + { + goto exit_function;/* out of RAM or other normalization problem */ + } + if ( at_norm ) + { + *at_norm = pStruct->pOne_norm_data[0]->at; + } + if ( at_prep ) + { + if ( pStruct->pOne_norm_data[0]->bTautPreprocessed && pStruct->pOne_norm_data[0]->at_fixed_bonds ) + { + *at_prep = pStruct->pOne_norm_data[0]->at_fixed_bonds; + } + else + /* get preprocessed structure in case of Fixed-H */ + if ( pStruct->iMobileH == TAUT_NON && pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->bTautPreprocessed ) + { + *at_prep = pStruct->pOne_norm_data[1]->at_fixed_bonds; + } + else + { + *at_prep = NULL; + } + } + if ( t_group_info ) + { + if ( pStruct->iMobileH == TAUT_YES && + pStruct->One_ti.num_t_groups && + pStruct->One_ti.t_group && pStruct->One_ti.nEndpointAtomNumber ) + { + *t_group_info = &pStruct->One_ti; + } + else + { + *t_group_info = NULL; + } + } + +exit_function: + return ret; +} + + +/****************************************************************************/ +int MakeOneInChIOutOfStrFromINChI( struct tagCANON_GLOBALS *pCG, + INCHI_CLOCK *ic, + ICHICONST INPUT_PARMS *ip, + STRUCT_DATA *sd, + StrFromINChI *pStruct, + inp_ATOM *at2, + inp_ATOM *at3, + ALL_TC_GROUPS *pTCGroups ) +{ + + INCHI_MODE bTautFlags = ip->bTautFlags | TG_FLAG_H_ALREADY_REMOVED; + INCHI_MODE bTautFlagsDone = 0; /*(ip->bTautFlagsDone | sd->bTautFlagsDone[INCHI_BAS]);*/ + INChI *cur_INChI[TAUT_NUM]; + INChI_Aux *cur_INChI_Aux[TAUT_NUM]; + int i, j, k; + int iComponent = pTCGroups->iComponent; + int len_at = pStruct->num_atoms + pStruct->num_deleted_H; + int num_atoms = pStruct->num_atoms; + long ulStructTime; + + INP_ATOM_DATA InpCurAtData; + INP_ATOM_DATA *inp_cur_data; + + INP_ATOM_DATA InpNormAtData, InpNormTautData; + INP_ATOM_DATA *inp_norm_data[TAUT_NUM]; /* = { &InpNormAtData, &InpNormTautData }; */ + + int bOrigCoord = 0; + int num_at, ret = RI_ERR_PROGR; + struct tagInchiTime ulMaxTime; + + T_GROUP_INFO *t_group_info = NULL; + /* initialization */ + inp_cur_data = &InpCurAtData; + inp_norm_data[TAUT_NON] = &InpNormAtData; + inp_norm_data[TAUT_YES] = &InpNormTautData; + + memset( inp_cur_data , 0, sizeof( *inp_cur_data ) ); + memset( inp_norm_data[TAUT_NON], 0, sizeof( *inp_norm_data[0] ) ); + memset( inp_norm_data[TAUT_YES], 0, sizeof( *inp_norm_data[0] ) ); + ulStructTime = sd->ulStructTime; + memset( sd, 0, sizeof(*sd) ); + + /* deallocate old results */ + free_t_group_info( &pStruct->One_ti ); + for ( k = 0; k < TAUT_NUM; k ++ ) + { + Free_INChI( &pStruct->pOneINChI[k] ); + Free_INChI_Aux( &pStruct->pOneINChI_Aux[k] ); + if ( pStruct->pOne_norm_data[k] ) + { + FreeInpAtomData( pStruct->pOne_norm_data[k] ); + inchi_free( pStruct->pOne_norm_data[k] ); + pStruct->pOne_norm_data[k] = NULL; + } + cur_INChI[k] = NULL; + cur_INChI_Aux[k] = NULL; + } + + memcpy( at3, at2, sizeof(at3[0])*len_at ); + + /* prepare the structure */ + IncrZeroBondsAndClearEndpts(at3, num_atoms, iComponent+1); + + CopySt2At( at3, pStruct->st, pStruct->num_atoms ); + + FixUnkn0DStereoBonds( at3, pStruct->num_atoms); + + ret = ReconcileAllCmlBondParities( at3, pStruct->num_atoms, 0 ); + + if ( ret < 0 ) + { + goto exit_function; + } + + if ( 0 < fix_odd_things( num_atoms, at3, 1, ip->bFixNonUniformDraw ) ) + { + if ( sd->nErrorType < _IS_WARNING ) + { + sd->nErrorType = _IS_WARNING; + } + sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ODD_THINGS_DONE; + } + + /* allocate and set parameters */ + inp_cur_data->at = at3; + inp_cur_data->num_at = num_atoms; + inp_cur_data->num_removed_H = pStruct->num_deleted_H; + + bTautFlagsDone &= ~(TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE); + + if ( i = bNumHeterAtomHasIsotopicH( at3, num_atoms ) ) + { + if ( i & 1 ) + { + bTautFlagsDone |= TG_FLAG_FOUND_ISOTOPIC_H_DONE; + } + if ( i & 2 ) + { + bTautFlagsDone |= TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE; + } + } + + memset( &ulMaxTime, 0, sizeof(ulMaxTime)); + + /* allocate memory for non-tautimeric (k=0) and tautomeric (k=1) results */ + for ( k = 0; k < TAUT_NUM; k ++ ) + { + + if ( !pStruct->bMobileH || k == pStruct->bMobileH ) + { + /* pStruct->bMobileH=0: k = 0, 1 => allow allocation of both Fixed-H and Mobile-H InChI + pStruct->bMobileH=1: k = 1 only => allow allocation of only Mobile-H InChI */ + int nAllocMode = (k==TAUT_YES? REQ_MODE_TAUT:0) | + (bTautFlagsDone & ( TG_FLAG_FOUND_ISOTOPIC_H_DONE | + TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE ))? + (ip->nMode & REQ_MODE_ISO):0; + + if ( k==TAUT_NON && (ip->nMode & REQ_MODE_BASIC ) || + k==TAUT_YES && (ip->nMode & REQ_MODE_TAUT ) ) + { + /* alloc INChI and INChI_Aux only if ip->nMode allows this */ + cur_INChI[k] = Alloc_INChI( inp_cur_data->at, inp_cur_data->num_at, &inp_cur_data->num_bonds, + &inp_cur_data->num_isotopic, nAllocMode ); + cur_INChI_Aux[k] = Alloc_INChI_Aux( inp_cur_data->num_at, + inp_cur_data->num_isotopic, nAllocMode, bOrigCoord ); + if ( cur_INChI_Aux[k] ) + { + cur_INChI_Aux[k]->bIsIsotopic = inp_cur_data->num_isotopic; + } + + /* alloc memory for the output structure: non-tautomeric and tautomeric (for displaying) */ + CreateInpAtomData( inp_norm_data[k], inp_cur_data->num_at+inp_cur_data->num_removed_H, k ); + + inp_norm_data[k]->num_removed_H = inp_cur_data->num_removed_H; + } + else + { + FreeInpAtomData( inp_norm_data[k] ); + } + } + else + { + FreeInpAtomData( inp_norm_data[k] ); + } + } + + k = pStruct->bMobileH; + + /* In case of Fixed-H we have to create InChI for both Fixed-H and Mobile-H */ + + num_at = Create_INChI( pCG, ic, + cur_INChI, + cur_INChI_Aux, + NULL/* not used */, + inp_cur_data->at, + inp_norm_data, + inp_cur_data->num_at + inp_cur_data->num_removed_H, + ip->nMode, + ip->bLargeMolecules, + ip->bPolymers, + &bTautFlags, + &bTautFlagsDone, + NULL /* &ulMaxTime*/, + &pStruct->One_ti, + sd->pStrErrStruct); + + SetConnectedComponentNumber( inp_cur_data->at, inp_cur_data->num_at, iComponent+1 ); /* normalization alters structure component number */ + + /* Detect InChI errors */ + + if ( num_at < 0 ) + { + ret = num_at; + } + else if ( cur_INChI[k] && cur_INChI[k]->nErrorCode ) + { + ret = cur_INChI[k]->nErrorCode; + } else if ( cur_INChI_Aux[k] && cur_INChI_Aux[k]->nErrorCode ) + { + ret = cur_INChI_Aux[k]->nErrorCode; + } + else + { + ret = 0; + } + + /* Fill out the output */ + + if ( !ret ) + { + int bMobileH = pStruct->bMobileH; + if ( bMobileH == TAUT_NON && + 0 == cur_INChI[TAUT_NON]->nNumberOfAtoms && + 0 < cur_INChI[TAUT_YES]->nNumberOfAtoms ) + { + /* tautomerism or H(+) removal/addition was not discovered */ + bMobileH = TAUT_YES; + } + + pStruct->nChargeRevrs = cur_INChI[TAUT_YES]->nTotalCharge; + + pStruct->pOneINChI[0] = cur_INChI[bMobileH]; + pStruct->pOneINChI_Aux[0] = cur_INChI_Aux[bMobileH]; + pStruct->nOneINChI_bMobileH = bMobileH; + cur_INChI[bMobileH] = NULL; /* remove pointer to avoid deallocation at exit_function */ + cur_INChI_Aux[bMobileH] = NULL; /* remove pointer to avoid deallocation at exit_function */ + + pStruct->nNumRemovedProtons = (pStruct->iMobileH == TAUT_YES)? pStruct->One_ti.tni.nNumRemovedProtons : 0; + + + /* set correct t-group numbers to endpoints */ + + t_group_info = &pStruct->One_ti; + + if ( t_group_info->num_t_groups && t_group_info->t_group && t_group_info->nEndpointAtomNumber ) + { + inp_ATOM *at_norm = inp_norm_data[TAUT_YES]->at; + int num_at_norm = inp_norm_data[TAUT_YES]->num_at; + for ( i = 0; i < num_at_norm; i ++ ) + { + at_norm[i].endpoint = 0; + } + for ( i = 0; i < t_group_info->num_t_groups; i ++ ) + { + k = t_group_info->t_group[i].nFirstEndpointAtNoPos; + /* add number of mobile (-) to the number of mobile H */ + t_group_info->t_group[i].num[0] += t_group_info->t_group[i].num[1]; + for ( j = 0; j < t_group_info->t_group[i].nNumEndpoints; j ++, k ++ ) + { + at_norm[t_group_info->nEndpointAtomNumber[k]].endpoint = t_group_info->t_group[i].nGroupNumber; + } + } + } + + pStruct->pOne_norm_data[0] = (INP_ATOM_DATA *) inchi_malloc( sizeof(pStruct->pOne_norm_data[0][0]) ); + + if ( pStruct->pOne_norm_data[0] ) + { + memcpy( pStruct->pOne_norm_data[0], inp_norm_data[bMobileH], sizeof(pStruct->pOne_norm_data[0][0])); + memset( inp_norm_data[bMobileH], 0, sizeof(*inp_norm_data[0]) ); + } + else + { + ret = RI_ERR_ALLOC; + } + + if ( bMobileH == TAUT_NON && cur_INChI[TAUT_YES]->nNumberOfAtoms > 0 ) + { + int bMobileHalt = ALT_TAUT(bMobileH); /* = TAUT_YES */ + pStruct->pOneINChI[1] = cur_INChI[bMobileHalt]; + pStruct->pOneINChI_Aux[1] = cur_INChI_Aux[bMobileHalt]; + cur_INChI[bMobileHalt] = NULL; + cur_INChI_Aux[bMobileHalt] = NULL; + pStruct->pOne_norm_data[1] = (INP_ATOM_DATA *) inchi_malloc( sizeof(pStruct->pOne_norm_data[0][0]) ); + if ( pStruct->pOne_norm_data[1] ) + { + memcpy( pStruct->pOne_norm_data[1], inp_norm_data[bMobileHalt], sizeof(pStruct->pOne_norm_data[0][0])); + memset( inp_norm_data[bMobileHalt], 0, sizeof(*inp_norm_data[0]) ); + } + else + { + ret = RI_ERR_ALLOC; + } + } + } + else + { + +#if ( bRELEASE_VERSION != 1 ) +#ifndef TARGET_API_LIB + fprintf( stdout, "ERROR: Create_INChI returned %d\n", ret ); +#endif +#endif + } + +exit_function: + /* deallocate unused */ + for ( k = 0; k < TAUT_NUM; k ++ ) + { + Free_INChI( &cur_INChI[k] ); + Free_INChI_Aux( &cur_INChI_Aux[k] ); + FreeInpAtomData( inp_norm_data[k] ); + } + sd->ulStructTime = ulStructTime; + + return ret; +} + + +/****************************************************************************************************** +Input: + at[].num_H = total number of all terminal H connected to the atom + at[].num_iso_H[] = numbers of isotopic H among at[].num_H + Explicit H are disconnected + Calculate InChI with normalization only in MakeOneInChIOutOfStrFromINChI() + with (TG_FLAG_H_ALREADY_REMOVED & bTautFlags) != 0 +Output: + at[].num_H = number of implicit non-isotopic H connected to the atom + at[].num_iso_H[] = numbers of implicit isotopic H (not included in at[].num_H) + Explicit H are connected + Calculate InChI with full preprocessing MakeInChIOutOfStrFromINChI2() + with (TG_FLAG_H_ALREADY_REMOVED & bTautFlags) == 0 +*******************************************************************************************************/ +int ConnectDisconnectedH( inp_ATOM *at, int num_atoms, int num_deleted_H ) +{ + int i, j, k, n, m, num_H; + int tot_atoms = num_atoms + num_deleted_H; + + for ( i = num_atoms; i < tot_atoms; i = j ) + { + k = at[i].neighbor[0]; /* a[k] is the atom connected to the explicit hydrogen at[i] */ + + for ( j = i; j < tot_atoms && at[j].neighbor[0] == k; j ++ ) + ; + + num_H = j-i; /* number of explicit H for at[k] */ + if ( num_H > at[k].num_H ) + { + return RI_ERR_PROGR; + } + if ( num_H + at[k].valence > MAXVAL ) + { + return RI_ERR_SYNTAX; + } + + /* insert links to explicit H before all other links in the connection list */ + n = at[k].valence; + memmove( at[k].neighbor +num_H, at[k].neighbor, sizeof(at[k].neighbor[0]) * n ); + memmove( at[k].bond_stereo+num_H, at[k].bond_stereo, sizeof(at[k].bond_stereo[0]) * n ); + memmove( at[k].bond_type +num_H, at[k].bond_type , sizeof(at[k].bond_type[0]) * n ); + for ( n = 0; n < num_H; n ++ ) + { + at[k].neighbor[n] = i + n; + at[k].bond_stereo[n] = 0; + at[k].bond_type[n] = BOND_TYPE_SINGLE; + } + + for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[k].sb_parity[m]; m ++ ) + { + at[k].sb_ord[m] += num_H; + if ( at[k].sn_ord[m] < 0 ) + { + for ( n = i; n < j; n ++ ) + { + if ( at[n].orig_at_number == at[k].sn_orig_at_num[m] ) + { + at[k].sn_ord[m] = n-i; + break; + } + } + + if ( n == j ) + { + return RI_ERR_PROGR; + } + } + else + { + at[k].sn_ord[m] += num_H; + } + } + + at[k].valence += num_H; + at[k].chem_bonds_valence += num_H; + at[k].num_H -= num_H; /* cannot be negative */ + + /*memset( at[k].num_iso_H, 0, sizeof(at[0].num_iso_H) );*/ /* attached H must carry all isotopic shifts */ + for ( n = i; n < j; n ++ ) + { + at[n].chem_bonds_valence = BOND_TYPE_SINGLE; + } + + /* isotopic H */ + for ( m = j-1; i <= m && at[m].iso_atw_diff > 0 ; m -- ) + { + if ( at[m].iso_atw_diff > NUM_H_ISOTOPES ) + { + return RI_ERR_PROGR; + } + if ( 0 >= at[k].num_iso_H[(int)at[m].iso_atw_diff-1] -- ) + { + return RI_ERR_PROGR; + } + } + } + + /* subtract isotopic H */ + for ( i = 0; i < num_atoms; i ++ ) + { + for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) + { + at[i].num_H -= at[i].num_iso_H[m]; + } + if ( 0 > at[i].num_H ) + { + return RI_ERR_PROGR; + } + } + + return tot_atoms; +} + + +/****************************************************************************************************** +Input: + at[].num_H = number of implicit non-isotopic H connected to the atom + at[].num_iso_H[] = numbers of implicit isotopic H (not included in at[].num_H) + Explicit H are connected + Calculate InChI with (TG_FLAG_H_ALREADY_REMOVED & bTautFlags) == 0 +Output: + at[].num_H = total number of all terminal H connected to the atom + at[].num_iso_H[] = numbers of isotopic H among at[].num_H + Explicit H are disconnected + Calculate InChI with (TG_FLAG_H_ALREADY_REMOVED & bTautFlags) != 0 +*******************************************************************************************************/ +int DisconnectedConnectedH( inp_ATOM *at, int num_atoms, int num_deleted_H ) +{ + int i, j, k, n, m, num_H, num_iso_H; + int tot_atoms = num_atoms + num_deleted_H; + + /* add implicit isotopic H to total implicit H */ + for ( i = 0; i < num_atoms; i ++ ) + { + for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) + { + at[i].num_H += at[i].num_iso_H[m]; + } + } + + for ( i = num_atoms; i < tot_atoms; i = j ) + { + k = at[i].neighbor[0]; /* a[k] is the atom connected to the explicit hydrogen at[i] */ + + for ( j = i; j < tot_atoms && at[j].neighbor[0] == k; j ++ ) + { + at[j].chem_bonds_valence = 0; + } + num_H = j-i; /* number of explicit H for at[k] */ + + /* verify correct number of explicit H */ + for ( n = 0; n < at[k].valence && at[k].neighbor[n] >= num_atoms; n ++ ) + ; + if ( n != num_H ) + { + return RI_ERR_PROGR; + } + + /* remove bonds to explicit H located in front of all other bonds in the connection list */ + n = (at[k].valence -= num_H); /* new number of bonds */ + at[k].chem_bonds_valence -= num_H; /* new no-H valence */ + if ( n ) + { + memmove( at[k].neighbor, at[k].neighbor + num_H, sizeof(at[k].neighbor[0]) * n ); + memmove( at[k].bond_stereo, at[k].bond_stereo + num_H, sizeof(at[k].bond_stereo[0]) * n ); + memmove( at[k].bond_type, at[k].bond_type + num_H, sizeof(at[k].bond_type[0]) * n ); + } + /* clear the 'tails' */ + memset( at[k].neighbor+n, 0, sizeof(at[k].neighbor[0]) * num_H ); + memset( at[k].bond_stereo+n, 0, sizeof(at[k].bond_stereo[0]) * num_H ); + memset( at[k].bond_type+n, 0, sizeof(at[k].bond_type[0]) * num_H ); + + for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[k].sb_parity[m]; m ++ ) + { + at[k].sb_ord[m] -= num_H; + if ( 0 <= at[k].sn_ord[m] && at[k].sn_ord[m] < num_H ) + { + at[k].sn_ord[m] = -1; /* disconnected explicit H */ + } + } + /* add explicit isotopic H (already included in num_H) */ + for ( num_iso_H = 0, m = j-1; i <= m && at[m].iso_atw_diff > 0 ; m -- ) + { + if ( at[m].iso_atw_diff > NUM_H_ISOTOPES ) + { + return RI_ERR_PROGR; + } + at[k].num_iso_H[(int)at[m].iso_atw_diff-1] ++; + } + at[k].num_H += num_H; /* add all explicit H including isotopic */ + } + + return tot_atoms; +} + + +/****************************************************************************/ +int MakeInChIOutOfStrFromINChI2( INCHI_CLOCK *ic, + CANON_GLOBALS *pCG, + ICHICONST INPUT_PARMS *ip_inp, + STRUCT_DATA *sd_inp, + StrFromINChI *pStruct, + int iComponent, + int iAtNoOffset, + long num_inp ) +{ + char szTitle[MAX_SDF_HEADER+MAX_SDF_VALUE+256]; + + int len, ret; + /* + PINChI2 *pINChI[INCHI_NUM]; + PINChI_Aux2 *pINChI_Aux[INCHI_NUM]; + */ + INPUT_PARMS local_ip; + STRUCT_DATA local_sd; + INPUT_PARMS *ip = &local_ip; + STRUCT_DATA *sd = &local_sd; + + ORIG_ATOM_DATA OrigAtData; /* 0=> disconnected, 1=> original */ + ORIG_ATOM_DATA *orig_inp_data = &OrigAtData; + ORIG_ATOM_DATA PrepAtData[2]; /* 0=> disconnected, 1=> original */ + ORIG_ATOM_DATA *prep_inp_data = PrepAtData; + + INCHI_IOSTREAM_STRING temp_string_container; + INCHI_IOSTREAM_STRING *strbuf = &temp_string_container; + memset( strbuf, 0, sizeof( *strbuf ) ); + + if ( 0>=inchi_strbuf_init( strbuf, INCHI_STRBUF_INITIAL_SIZE, INCHI_STRBUF_SIZE_INCREMENT ) ) + { + ret = RI_ERR_ALLOC; + goto exit_error; + } + + *ip = *ip_inp; + ip->bDisplay = 0; + ip->bDisplayCompositeResults = 0; + ip->bDisplayEachComponentINChI = 0; + ip->bDisplayIfRestoreWarnings = 0; + ip->bINChIOutputOptions = INCHI_OUT_NO_AUX_INFO; + + /* + if ( pStruct->bMobileH ) { + ip->nMode &= ~REQ_MODE_BASIC; + ip->nMode |= REQ_MODE_TAUT; + } else { + ip->nMode |= (REQ_MODE_TAUT | REQ_MODE_BASIC); + } + */ + + memset( sd, 0, sizeof(*sd) ); + sd->fPtrStart = -1; + sd->fPtrEnd = -1; + + /* + if ( ip->nMode & REQ_MODE_STEREO ) { + if ( ip->nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO) ) { + sd->bChiralFlag |= FLAG_INP_AT_NONCHIRAL; + } else { + sd->bChiralFlag |= FLAG_INP_AT_CHIRAL; + } + } + */ + + memset( orig_inp_data , 0, sizeof( *orig_inp_data ) ); + memset( prep_inp_data , 0, 2*sizeof( *prep_inp_data ) ); + memset( pStruct->RevInChI.pINChI, 0, sizeof(pStruct->RevInChI.pINChI ) ); + memset( pStruct->RevInChI.pINChI_Aux, 0, sizeof(pStruct->RevInChI.pINChI_Aux) ); + memset( szTitle, 0, sizeof(szTitle) ); + + len = sizeof(orig_inp_data->at[0])*(pStruct->num_atoms + pStruct->num_deleted_H); + + orig_inp_data->at = (inp_ATOM *) inchi_malloc( len ); + + if ( orig_inp_data->at ) + { + /*memcpy( orig_inp_data->at, pStruct->at2, len );*/ + /*ret = ConnectDisconnectedH( orig_inp_data->at, pStruct->num_atoms, pStruct->num_deleted_H );*/ + + CopySt2At( pStruct->at2, pStruct->st, pStruct->num_atoms ); + + ret = ConnectDisconnectedH( pStruct->at2, pStruct->num_atoms, pStruct->num_deleted_H ); + if ( ret < 0 ) + { + goto exit_error; + } + + orig_inp_data->num_inp_atoms = ret; + /* connections changed => reconcile parities even if they were reconciled before */ + /* remove t-group markings and increment zero-order bonds, + otherwise MakeInChIOutOfStrFromINChI2() woild fail */ + /* + IncrZeroBondsAndClearEndpts(pStruct->at2, pStruct->num_atoms, iComponent+1); + */ + + IncrZeroBonds(pStruct->at2, pStruct->num_atoms, iComponent+1); + + /* CopySt2At() moved to the position before ConnectDisconnectedH() because + in case stereo exists only in Mobile-H layer and the processd here + component is restored in Fixed-H layer the parities needed by + ConnectDisconnectedH() must be there before calling + ConnectDisconnectedH() + */ + /*CopySt2At( pStruct->at2, pStruct->st, pStruct->num_atoms );*/ + + ret = ReconcileAllCmlBondParities( pStruct->at2, orig_inp_data->num_inp_atoms, 0 ); + if ( ret < 0 ) + { + goto exit_error; + } + + memcpy( orig_inp_data->at, pStruct->at2, len ); + + ClearEndpts(orig_inp_data->at, pStruct->num_atoms); + + if ( FixUnkn0DStereoBonds(orig_inp_data->at, pStruct->num_atoms) ) + { + ret = ReconcileAllCmlBondParities( pStruct->at2, orig_inp_data->num_inp_atoms, 0 ); + if ( ret < 0 ) { + goto exit_error; + } + } + /* keep endpoint[] markings in at2[] for subsequent add/remove protons */ + } + else + { + ret = RI_ERR_ALLOC; + goto exit_error; + } + + memset( sd->num_components, 0, sizeof(sd->num_components) ); + memset( sd->num_taut, 0, sizeof(sd->num_taut) ); + memset( sd->num_non_taut, 0, sizeof(sd->num_non_taut) ); + memset( sd->bTautFlagsDone, 0, sizeof(sd->bTautFlagsDone) ); + memset( sd->bTautFlags, 0, sizeof(sd->bTautFlags) ); + + + ret = ProcessOneStructure( ic, pCG, sd, ip, szTitle, + pStruct->RevInChI.pINChI, + pStruct->RevInChI.pINChI_Aux, + NULL /*inp_file*/, + NULL /*log_file*/, + NULL /*out_file*/, + NULL /*prb_file*/, + orig_inp_data, prep_inp_data, + num_inp, strbuf, + 0 /* save_opt_bits */); + + + memcpy(pStruct->RevInChI.num_components, sd->num_components, sizeof(pStruct->RevInChI.num_components) ); + memcpy(sd_inp->pStrErrStruct, sd->pStrErrStruct, sizeof(sd_inp->pStrErrStruct) ); + pStruct->RevInChI.nRetVal = ret; + + /* translate returned value */ + if ( ret == _IS_ERROR || ret == _IS_FATAL || ret == _IS_UNKNOWN ) + { + ret = RI_ERR_PROGR; + } + else if ( ret == _IS_OKAY ) + { + ret = 0; + } + else if ( ret == _IS_WARNING ) + { + ret = 1; + } + else + { + ret = RI_ERR_PROGR; + } + + /* save total charge from Mobile-H layer */ + + pStruct->nChargeRevrs = 0; + if ( ret >= 0 ) + { + if ( bRevInchiComponentExists( pStruct, INCHI_REC, TAUT_YES, 0 ) ) + { + pStruct->nChargeRevrs = pStruct->RevInChI.pINChI[INCHI_REC][0][TAUT_YES]->nTotalCharge; + } + else if ( bRevInchiComponentExists( pStruct, INCHI_BAS, TAUT_YES, 0 ) ) + { + pStruct->nChargeRevrs = pStruct->RevInChI.pINChI[INCHI_BAS][0][TAUT_YES]->nTotalCharge; + } + } + + /* free structure data */ + FreeOrigAtData( orig_inp_data ); + FreeOrigAtData( prep_inp_data ); + FreeOrigAtData( prep_inp_data+1 ); + +exit_error: + inchi_strbuf_close( strbuf ); + + return ret; +} + + +/****************************************************************************/ +int OutputInChIOutOfStrFromINChI( struct tagINCHI_CLOCK *ic, + struct tagCANON_GLOBALS *pCG, + ICHICONST INPUT_PARMS *ip_inp, + STRUCT_DATA *sd_inp, + long num_inp, + int bINChIOutputOptions, + INCHI_IOSTREAM *pout, + INCHI_IOSTREAM *plog, + InpInChI *pOneInput, + int bHasSomeFixedH, + unsigned char save_opt_bits) +{ + char szTitle[MAX_SDF_HEADER+MAX_SDF_VALUE+256]; + + int len, ret; +/* + PINChI2 *pINChI[INCHI_NUM]; + PINChI_Aux2 *pINChI_Aux[INCHI_NUM]; +*/ + REV_INCHI RevInChI; + + INPUT_PARMS local_ip; + STRUCT_DATA local_sd; + INPUT_PARMS *ip = &local_ip; + STRUCT_DATA *sd = &local_sd; + + ORIG_ATOM_DATA OrigAtData; /* 0=> disconnected, 1=> original */ + ORIG_ATOM_DATA *orig_inp_data = &OrigAtData; + ORIG_ATOM_DATA PrepAtData[2]; /* 0=> disconnected, 1=> original */ + ORIG_ATOM_DATA *prep_inp_data = PrepAtData; + + INCHI_IOSTREAM_STRING temp_string_container; + INCHI_IOSTREAM_STRING *strbuf = &temp_string_container; + memset( strbuf, 0, sizeof( *strbuf ) ); + + if ( 0>=inchi_strbuf_init( strbuf, INCHI_STRBUF_INITIAL_SIZE, INCHI_STRBUF_SIZE_INCREMENT ) ) + { + ret = RI_ERR_ALLOC; + goto exit_error; + } + + *ip = *ip_inp; + ip->bNoStructLabels = 1; + ip->bDisplay = 0; + ip->bDisplayCompositeResults = 0; + ip->bDisplayEachComponentINChI = 0; + ip->bDisplayIfRestoreWarnings = 0; + +#if ( I2S_MODIFY_OUTPUT == 1 ) + if ( bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY ) + ip->bINChIOutputOptions = bINChIOutputOptions & ~(INCHI_OUT_PLAIN_TEXT | INCHI_OUT_PLAIN_TEXT_COMMENTS ); + else + if ( bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT ) + ip->bINChIOutputOptions = bINChIOutputOptions & ~INCHI_OUT_SDFILE_ONLY | INCHI_OUT_EMBED_REC; + else + if ( bINChIOutputOptions & (INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO | INCHI_OUT_ONLY_AUX_INFO | INCHI_OUT_TABBED_OUTPUT)) + ip->bINChIOutputOptions = (INCHI_OUT_PLAIN_TEXT | INCHI_OUT_EMBED_REC | bINChIOutputOptions); + else + ip->bINChIOutputOptions = (INCHI_OUT_PLAIN_TEXT | INCHI_OUT_EMBED_REC); +#else + ip->bINChIOutputOptions = (INCHI_OUT_PLAIN_TEXT | INCHI_OUT_EMBED_REC ); +#endif + + if ( bHasSomeFixedH ) + { + ip->nMode |= (REQ_MODE_TAUT | REQ_MODE_BASIC); + } + else + { + ip->nMode &= ~REQ_MODE_BASIC; + ip->nMode |= REQ_MODE_TAUT; + } + + memset( sd, 0, sizeof(*sd) ); + sd->fPtrStart = -1; + sd->fPtrEnd = -1; + /* + if ( ip->nMode & REQ_MODE_STEREO ) { + if ( ip->nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO) ) { + sd->bChiralFlag |= FLAG_INP_AT_NONCHIRAL; + } else { + sd->bChiralFlag |= FLAG_INP_AT_CHIRAL; + } + } + */ + memset( orig_inp_data, 0, sizeof( *orig_inp_data ) ); + memset( prep_inp_data, 0, 2*sizeof( *prep_inp_data ) ); + memset( RevInChI.pINChI, 0, sizeof(RevInChI.pINChI ) ); + memset( RevInChI.pINChI_Aux, 0, sizeof(RevInChI.pINChI_Aux) ); + + len = sizeof(orig_inp_data->at[0]) * pOneInput->num_atoms; + orig_inp_data->at = (inp_ATOM *) inchi_malloc( len ); + orig_inp_data->szCoord = (MOL_COORD *)inchi_calloc( pOneInput->num_atoms, sizeof(orig_inp_data->szCoord[0])); + + orig_inp_data->polymer = pOneInput->polymer; + orig_inp_data->v3000 = pOneInput->v3000; + pOneInput->polymer = NULL; + pOneInput->v3000 = NULL; + + if ( orig_inp_data->at && orig_inp_data->szCoord ) + { + int i, k; + memcpy( orig_inp_data->at, pOneInput->atom, len ); + orig_inp_data->num_inp_atoms = pOneInput->num_atoms; + ClearEndpts( orig_inp_data->at, orig_inp_data->num_inp_atoms ); + /* otherwise fails on CID=450438 */ + if ( FixUnkn0DStereoBonds(orig_inp_data->at, orig_inp_data->num_inp_atoms) ) + { + ret = ReconcileAllCmlBondParities( orig_inp_data->at, orig_inp_data->num_inp_atoms, 0 ); + if ( ret < 0 ) + { + goto exit_error; + } + } + + /* To obtain rA,rB,rC in AuxInfo we have to emulate input coordinates; make all of them zeroes */ + for ( i = 0; i < pOneInput->num_atoms; i ++ ) + { + for ( k = 0; k < NUM_COORD*LEN_COORD; k += LEN_COORD ) + { + orig_inp_data->szCoord[i][k] = '0'; + } + } + } + else + { + ret = RI_ERR_ALLOC; + goto exit_error; + } + + memset( sd->num_components, 0, sizeof(sd->num_components) ); + memset( sd->num_taut, 0, sizeof(sd->num_taut) ); + memset( sd->num_non_taut, 0, sizeof(sd->num_non_taut) ); + memset( sd->bTautFlagsDone, 0, sizeof(sd->bTautFlagsDone) ); + memset( sd->bTautFlags, 0, sizeof(sd->bTautFlags) ); + memset( szTitle, 0, sizeof(szTitle) ); + + + + ret = ProcessOneStructure( ic, pCG, sd, ip, szTitle, + RevInChI.pINChI, + RevInChI.pINChI_Aux, + NULL /*inp_file*/, + plog /*log_file*/, + pout /*out_file*/, + NULL /*prb_file*/, + orig_inp_data, prep_inp_data, + num_inp, strbuf, save_opt_bits); + + + memcpy(RevInChI.num_components, sd->num_components, sizeof(RevInChI.num_components) ); + + /* + memcpy(sd_inp->pStrErrStruct, sd->pStrErrStruct, sizeof(sd_inp->pStrErrStruct) ); + */ + + RevInChI.nRetVal = ret; + + /* translate returned value */ + if ( ret == _IS_ERROR || ret == _IS_FATAL || ret == _IS_UNKNOWN ) + { + ret = RI_ERR_PROGR; + } + else if ( ret == _IS_OKAY ) + { + ret = 0; + } + else if ( ret == _IS_WARNING ) + { + ret = 1; + } + else + { + ret = RI_ERR_PROGR; + } + + /* free structure data */ + FreeOrigAtData( orig_inp_data ); + FreeOrigAtData( prep_inp_data ); + FreeOrigAtData( prep_inp_data+1 ); + FreeAllINChIArrays( RevInChI.pINChI, + RevInChI.pINChI_Aux, + RevInChI.num_components ); + + +exit_error: + inchi_strbuf_close( strbuf ); + return ret; +} +#endif diff --git a/INCHI-1-SRC/INCHI/common/ichirvr2.c b/INCHI-1-SRC/INCHI_BASE/src/ichirvr2.c similarity index 93% rename from INCHI-1-SRC/INCHI/common/ichirvr2.c rename to INCHI-1-SRC/INCHI_BASE/src/ichirvr2.c index 00c5083..0595d60 100644 --- a/INCHI-1-SRC/INCHI/common/ichirvr2.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichirvr2.c @@ -1,6376 +1,6593 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - -/*^^^ */ -/*#define CHECK_WIN32_VC_HEAP*/ -#include "mode.h" - -#if ( READ_INCHI_STRING == 1 ) - -#include "ichi.h" -#include "ichitime.h" - -#include "inpdef.h" -#include "ichimain.h" -#include "ichierr.h" -#include "incomdef.h" -#include "ichiring.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "util.h" - -#include "ichicomp.h" -#include "ichister.h" - -#include "ichi_bns.h" - -#include "strutil.h" - -#include "ichirvrs.h" - -/******************************************************************************************************/ -void CopyAt2St( inp_ATOM *at, inp_ATOM_STEREO * st, int num_atoms ) -{ - int i; - for ( i = 0; i < num_atoms; i ++ ) { - if ( at[i].p_parity ) { - memcpy( st[i].p_orig_at_num, at[i].p_orig_at_num, sizeof(st[0].p_orig_at_num) ); - st[i].p_parity = at[i].p_parity; - } - if ( at[i].sb_parity[0] ) { - memcpy( st[i].sb_ord, at[i].sb_ord, sizeof(st[0].sb_ord) ); - memcpy( st[i].sb_parity, at[i].sb_parity, sizeof(st[0].sb_parity) ); - memcpy( st[i].sn_ord, at[i].sn_ord, sizeof(st[0].sn_ord) ); - memcpy( st[i].sn_orig_at_num, at[i].sn_orig_at_num, sizeof(st[0].sn_orig_at_num) ); - } - } -} -void CopySt2At( inp_ATOM *at, inp_ATOM_STEREO * st, int num_atoms ) -{ - int i; - if ( !st ) { - return; - } - for ( i = 0; i < num_atoms; i ++ ) { - if ( st[i].p_parity ) { - memcpy( at[i].p_orig_at_num, st[i].p_orig_at_num, sizeof(at[0].p_orig_at_num) ); - at[i].p_parity = st[i].p_parity; - } - if ( st[i].sb_parity[0] ) { - memcpy( at[i].sb_ord, st[i].sb_ord, sizeof(st[0].sb_ord) ); - memcpy( at[i].sb_parity, st[i].sb_parity, sizeof(at[0].sb_parity) ); - memcpy( at[i].sn_ord, st[i].sn_ord, sizeof(at[0].sn_ord) ); - memcpy( at[i].sn_orig_at_num, st[i].sn_orig_at_num, sizeof(at[0].sn_orig_at_num) ); - } - } -} - -/******************************************************************************************************/ -int RestoreAtomConnectionsSetStereo( StrFromINChI *pStruct, int iComponent, int iAtNoOffset, INChI *pInChI, INChI *pInChIMobH) -{ - inp_ATOM *at = NULL; - inp_ATOM_STEREO * st = NULL; - int num_atoms, i, jv, jn, n_vertex, n_neigh, num_H, parity; - int nNumDeletedH=0, iDeletedH=0, idelH1, idelH2, ret = 0, len; - int num_stereo_bonds, num_stereo_centers, num_stereo_bonds2, num_stereo_centers2; - INChI_Stereo *pStereo = NULL, *pStereo2 = NULL; - AT_NUMB nCumulene[MAX_CUMULENE_LEN+2]; - - num_atoms = pInChI->nNumberOfAtoms; - if ( num_atoms <= 0 ) { - return 0; - } - INCHI_HEAPCHK - /* atoms */ - pStruct->at = at = (inp_ATOM *) inchi_calloc ( num_atoms, sizeof(pStruct->at[0]) ); - if ( !at ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - pStruct->num_atoms = num_atoms; - /* charge */ - pStruct->charge = pInChI->nTotalCharge; - /* elements, terminal atoms H */ - for ( i = 0; i < num_atoms; i ++ ) { - at[i].el_number = pInChI->nAtom[i]; - if ( GetElementFormulaFromAtNum(UCINT pInChI->nAtom[i], at[i].elname ) ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - at[i].orig_at_number = iAtNoOffset + i+1; - at[i].orig_compt_at_numb = i + 1; - at[i].component = iComponent + 1; - num_H = pInChI->nNum_H[i]; - /* --- pInChI->nNum_H_fixed[i] was added to pInChI->nNum_H[i] --- - if ( pInChI->nNum_H_fixed ) { - num_H += pInChI->nNum_H_fixed[i]; - } - */ - at[i].num_H = num_H; - } - INCHI_HEAPCHK - /* connections */ - for ( i = 1, n_vertex = pInChI->nConnTable[0]-1; i < pInChI->lenConnTable; i ++ ) { - if ( (n_neigh = pInChI->nConnTable[i]-1) < n_vertex ) { - /* vertex - neighbor connection */ - jv = at[n_vertex].valence ++; - at[n_vertex].neighbor[jv] = n_neigh; - at[n_vertex].bond_type[jv] = BOND_TYPE_SINGLE; - at[n_vertex].chem_bonds_valence += at[n_vertex].bond_type[jv]; - /* neighbor - vertex connection */ - jn = at[n_neigh].valence ++; - at[n_neigh].neighbor[jn] = n_vertex; - at[n_neigh].bond_type[jn] = BOND_TYPE_SINGLE; - at[n_neigh].chem_bonds_valence += at[n_neigh].bond_type[jn]; - } else - if ( (n_vertex = n_neigh) >= num_atoms ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - } - INCHI_HEAPCHK - /* isotopic atoms */ - if ( pInChI->IsotopicAtom && pInChI->nNumberOfIsotopicAtoms ) { - for ( i = 0; i < pInChI->nNumberOfIsotopicAtoms; i ++ ) { - n_vertex = pInChI->IsotopicAtom[i].nAtomNumber-1; - at[n_vertex].iso_atw_diff = (char)pInChI->IsotopicAtom[i].nIsoDifference; - at[n_vertex].num_iso_H[0] = (char)pInChI->IsotopicAtom[i].nNum_H; - at[n_vertex].num_iso_H[1] = (char)pInChI->IsotopicAtom[i].nNum_D; - at[n_vertex].num_iso_H[2] = (char)pInChI->IsotopicAtom[i].nNum_T; - } - pStruct->bIsotopic |= 1; - } - INCHI_HEAPCHK - /* tautomeric groups */ - if ( ret = GetTgroupInfoFromInChI( &pStruct->ti, at, NULL, pInChI ) ) { - goto exit_function; - } - - /* coordinates: data from unused members: pInChI->IsotopicTGroup and InChI->nNumberOfIsotopicTGroups */ - if ( pInChI->IsotopicTGroup && !pInChI->nNumberOfIsotopicTGroups ) { - pStruct->pXYZ = (XYZ_COORD *) pInChI->IsotopicTGroup; - pInChI->IsotopicTGroup = NULL; - } - /* stereo */ - if ( pInChI->StereoIsotopic && - (pInChI->StereoIsotopic->nNumberOfStereoBonds + - pInChI->StereoIsotopic->nNumberOfStereoCenters) ) { - pStereo = pInChI->StereoIsotopic; - } else - if ( pInChI->Stereo && - (pInChI->Stereo->nNumberOfStereoBonds + - pInChI->Stereo->nNumberOfStereoCenters) ) { - pStereo = pInChI->Stereo; - } else { - pStereo = NULL; - } - /* stereo2: Mobile-H in addition to Fixed-H*/ - pStereo2 = NULL; - if ( pInChIMobH && pInChIMobH->nNumberOfAtoms ) { - if ( pInChIMobH->StereoIsotopic && - (pInChIMobH->StereoIsotopic->nNumberOfStereoBonds + - pInChIMobH->StereoIsotopic->nNumberOfStereoCenters) ) { - pStereo2 = pInChIMobH->StereoIsotopic; - } else - if ( pInChIMobH->Stereo && - (pInChIMobH->Stereo->nNumberOfStereoBonds + - pInChIMobH->Stereo->nNumberOfStereoCenters) ) { - pStereo2 = pInChIMobH->Stereo; - } - } - INCHI_HEAPCHK - - num_stereo_bonds = num_stereo_bonds2 = 0; - num_stereo_centers = num_stereo_centers2 = 0; - /* -- have already been done in the initialization -- - iDeletedH = 0; - nNumDeletedH = 0; - */ - if ( pStereo || pStereo2 ) { - /* count implicit H needed for parities and reallocate at[]; set at[n_vertex].at_type=1 for these atoms */ - int len1 = pStereo? pStereo->nNumberOfStereoCenters : 0; - int len2 = pStereo2? pStereo2->nNumberOfStereoCenters : 0; - int i2, diff, diff2; - for ( i = i2 = 0; i < len1 || i2 < len2; ) { - if ( i < len1 && i2 < len2 ) { - diff = (int)pStereo->nNumber[i] - (int)pStereo2->nNumber[i2]; - if ( diff <= 0 ) { - n_vertex = pStereo->nNumber[i]-1; - i ++; - i2 += !diff; - } else { - n_vertex = pStereo2->nNumber[i2]-1; - num_stereo_centers2 ++; - i2 ++; - } - } else - if ( i < len1 ) { - n_vertex = pStereo->nNumber[i]-1; - i ++; - } else { - n_vertex = pStereo2->nNumber[i2]-1; - num_stereo_centers2 ++; - i2 ++; - } - /* find whether it is an allene */ - if ( at[n_vertex].valence == 2 && - at[n_vertex].num_H == 0 && - bCanAtomBeMiddleAllene(at[n_vertex].elname, 0, 0) && - at[jv = at[n_vertex].neighbor[0]].valence + at[jv].num_H == 3 && - bCanAtomBeTerminalAllene(at[jv].elname, 0, 0) && - at[jn = at[n_vertex].neighbor[1]].valence + at[jn].num_H == 3 && - bCanAtomBeTerminalAllene(at[jn].elname, 0, 0) ) { - /* allene */ - if ( !at[jv].at_type && at[jv].num_H ) { - nNumDeletedH += at[jv].num_H; - at[jv].at_type ++; /* H should be added as an explicit H */ - } - if ( !at[jn].at_type && at[jn].num_H ) { - nNumDeletedH += at[jn].num_H; - at[jn].at_type ++; /* H should be added as an explicit H */ - } - } else { - /* stereogenic atom - sp3 */ - if ( !at[n_vertex].at_type && at[n_vertex].num_H ) { - nNumDeletedH += at[n_vertex].num_H; - at[n_vertex].at_type ++; /* H should be added as an explicit H */ - } - } - } - INCHI_HEAPCHK - len1 = pStereo? pStereo->nNumberOfStereoBonds : 0; - len2 = pStereo2? pStereo2->nNumberOfStereoBonds : 0; - for ( i = i2 = 0; i < len1 || i2 < len2; ) { - if ( i < len1 && i2 < len2 ) { - diff = (int)pStereo->nBondAtom1[i] - (int)pStereo2->nBondAtom1[i2]; - diff2 = (int)pStereo->nBondAtom2[i] - (int)pStereo2->nBondAtom2[i2]; - if ( diff < 0 || diff == 0 && diff2 <= 0) { - n_vertex = pStereo->nBondAtom1[i]-1; - n_neigh = pStereo->nBondAtom2[i]-1; - i ++; - i2 += !diff && !diff2; - } else { - n_vertex = pStereo2->nBondAtom1[i2]-1; - n_neigh = pStereo2->nBondAtom2[i2]-1; - num_stereo_bonds2 ++; - i2 ++; - } - } else - if ( i < len1 ) { - n_vertex = pStereo->nBondAtom1[i]-1; - n_neigh = pStereo->nBondAtom2[i]-1; - i ++; - } else { - n_vertex = pStereo2->nBondAtom1[i2]-1; - n_neigh = pStereo2->nBondAtom2[i2]-1; - num_stereo_bonds2 ++; - i2 ++; - } - if ( !is_in_the_list( at[n_vertex].neighbor, (AT_NUMB)n_neigh, at[n_vertex].valence ) ) { - /* must be a cumulene */ - if ( !bFindCumuleneChain( at, (AT_NUMB)n_vertex, (AT_NUMB)n_neigh, nCumulene, MAX_CUMULENE_LEN+1 ) ) { - ret = RI_ERR_SYNTAX; /* not a cumulene */ - goto exit_function; - } - } - if ( !at[n_vertex].at_type && at[n_vertex].num_H ) { - nNumDeletedH += at[n_vertex].num_H; - at[n_vertex].at_type ++; /* H should be added as an explicit H */ - } - if ( !at[n_neigh].at_type && at[n_neigh].num_H ) { - nNumDeletedH += at[n_neigh].num_H; - at[n_neigh].at_type ++; /* H should be added as an explicit H */ - } - } - INCHI_HEAPCHK - if ( nNumDeletedH ) { - /* add explicit H */ - inp_ATOM *at2 = (inp_ATOM *)inchi_calloc( num_atoms + nNumDeletedH, sizeof(at2[0]) ); - if ( !at2 ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - pStruct->num_deleted_H = nNumDeletedH; - memcpy( at2, at, num_atoms * sizeof(at2[0]) ); - inchi_free( at ); - pStruct->at = at = at2; - /* fill out deleted H atom info */ - for ( i = num_atoms; i < num_atoms + nNumDeletedH; i ++ ) { - strcpy( at[i].elname, "H" ); - at[i].el_number = EL_NUMBER_H; - at[i].orig_at_number = iAtNoOffset + i+1; - at[i].orig_compt_at_numb = i + 1; - at[i].component = iComponent + 1; - } - /* connect deleted H */ - for( i = 0; i < num_atoms; i ++ ) { - if ( at[i].at_type == 1 ) { - if ( 0 > (ret = AddExplicitDeletedH( at, i, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) { - goto exit_function; - } - } - } - } - INCHI_HEAPCHK - } - - if ( pStereo ) { - /* mark stereo centers, they have already been connected the added explicit H, if any */ - int bInvertedParity = (pStereo->nCompInv2Abs == -1); - for ( i = 0; i < pStereo->nNumberOfStereoCenters; i ++ ) { - n_vertex = pStereo->nNumber[i]-1; - parity = pStereo->t_parity[i]; - if ( bInvertedParity ) { - parity = (parity == AB_PARITY_EVEN)? AB_PARITY_ODD : (parity == AB_PARITY_ODD)? AB_PARITY_EVEN : parity; - } - /* find whether it is allene */ - if ( at[n_vertex].valence == 2 && - at[n_vertex].num_H == 0 && - bCanAtomBeMiddleAllene(at[n_vertex].elname, 0, 0) && - /* allene has exactly 2 double bonds */ - (jv = at[n_vertex].neighbor[0], at[jv].valence + at[jv].num_H == 3) && - bCanAtomBeTerminalAllene(at[jv].elname, 0, 0) && - (jn = at[n_vertex].neighbor[1], at[jn].valence + at[jn].num_H == 3) && - bCanAtomBeTerminalAllene(at[jn].elname, 0, 0) ) { - /* allene: add explicit H if implicit H are present */ - /* iDeletedH = current number of already added explicit H */ - /* idelH1 = index in at[] of the explicit H added to atom jv */ - if ( at[jv].num_H ) { - if ( 0 > (ret = AddExplicitDeletedH( at, jv, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) { - goto exit_function; - } - } else { - /* index of the stereo atom neighbor */ - idelH1 = at[jv].neighbor[at[jv].neighbor[0]==n_vertex]; - } - if ( at[jn].num_H ) { - /* iDeletedH = current number of already added explicit H */ - /* idelH2 = index of the explicit H added to atom jn */ - if ( 0 > (ret = AddExplicitDeletedH( at, jn, num_atoms, &iDeletedH, &idelH2, nNumDeletedH, pStereo2 != NULL ))) { - goto exit_function; - } - } else { - idelH2 = at[jn].neighbor[at[jn].neighbor[0]==n_vertex]; - } - /* allene: set bond types to double */ - /* - if ( 0 > (ret = set_bond_type( at, (AT_NUMB)n_vertex, (AT_NUMB)jv, BOND_TYPE_DOUBLE ) ) || - 0 > (ret = set_bond_type( at, (AT_NUMB)n_vertex, (AT_NUMB)jn, BOND_TYPE_DOUBLE ) ) ) { - goto exit_function; - } - */ - /* allene: make 0D parity */ - ret = set_cumulene_0D_parity( at, st, num_atoms, idelH1, jv, jn, idelH2, parity, 2 ); - if ( ret < 0 ) { - goto exit_function; - } - } else { - /* stereogenic sp3 atom */ - if ( at[n_vertex].num_H ) { - if ( 0 > (ret = AddExplicitDeletedH( at, n_vertex, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) { - goto exit_function; - } - } - ret = set_atom_0D_parity( at, st, num_atoms, nNumDeletedH, n_vertex, parity ); - if ( ret < 0 ) { - goto exit_function; - } - num_stereo_centers ++; - } - if ( ret < 0 ) { - goto exit_function; - } - } - INCHI_HEAPCHK - /* mark stereobonds */ - for ( i = 0; i < pStereo->nNumberOfStereoBonds; i ++ ) { - jv = pStereo->nBondAtom1[i]-1; - jn = pStereo->nBondAtom2[i]-1; - parity = pStereo->b_parity[i]; - if ( !is_in_the_list( at[jv].neighbor, (AT_NUMB)jn, at[jv].valence ) ) { - /* must be a cumulene */ - if ( !bFindCumuleneChain( at, (AT_NUMB)jv, (AT_NUMB)jn, nCumulene, MAX_CUMULENE_LEN+1 ) ) { - return RI_ERR_SYNTAX; /* not a cumulene */ - } - len = MAX_CUMULENE_LEN+1; - } else { - /* a regular double or alt bond */ - nCumulene[0] = jv; - nCumulene[1] = jn; - len = 1; /* cumulene length is number of bonds, not number of atoms */ - } - /* cumulene or double bond: add explicit H if implicit H are present */ - if ( at[jv].num_H ) { - if ( 0 > (ret = AddExplicitDeletedH( at, jv, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) { - goto exit_function; - } - } else { - /* double bond neighbor that has the smallest canonical number; it is either 0th or 1st */ - idelH1 = at[jv].neighbor[at[jv].neighbor[0]==nCumulene[1]]; - } - if ( at[jn].num_H ) { - if ( 0 > (ret = AddExplicitDeletedH( at, jn, num_atoms, &iDeletedH, &idelH2, nNumDeletedH, pStereo2 != NULL ))) { - goto exit_function; - } - } else { - idelH2 = at[jn].neighbor[at[jn].neighbor[0]==nCumulene[len-1]]; - } - if ( 0 > (ret = set_cumulene_0D_parity( at, st, num_atoms, idelH1, jv, jn, idelH2, parity, len )) ) { - goto exit_function; - } - } - INCHI_HEAPCHK - } - /* allocate memory for Mobile-H-only stereo */ - if ( num_stereo_centers2 + num_stereo_bonds2 ) { - if ( !(st = (inp_ATOM_STEREO *)inchi_calloc( num_atoms, sizeof(st[0])))) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - CopyAt2St( at, st, num_atoms ); - } - pStruct->st = st; - if ( num_stereo_centers2 ) { - /* In case of Fixed-H */ - /* mark additional Mobile-H stereo centers, they have already been connected the added explicit H, if any */ - int bInvertedParity = (pStereo2->nCompInv2Abs == -1); - for ( i = 0; i < pStereo2->nNumberOfStereoCenters; i ++ ) { - n_vertex = pStereo2->nNumber[i]-1; - parity = pStereo2->t_parity[i]; - if ( at[n_vertex].p_parity ) { - continue; /* the parity has already been set for Fixed-H */ - } - if ( bInvertedParity ) { - parity = (parity == AB_PARITY_EVEN)? AB_PARITY_ODD : (parity == AB_PARITY_ODD)? AB_PARITY_EVEN : parity; - } - /* find whether it is allene */ - if ( at[n_vertex].valence == 2 && - at[n_vertex].num_H == 0 && - bCanAtomBeMiddleAllene(at[n_vertex].elname, 0, 0) && - /* allene has exactly 2 double bonds */ - (jv = at[n_vertex].neighbor[0], at[jv].valence + at[jv].num_H == 3) && - bCanAtomBeTerminalAllene(at[jv].elname, 0, 0) && - (jn = at[n_vertex].neighbor[1], at[jn].valence + at[jn].num_H == 3) && - bCanAtomBeTerminalAllene(at[jn].elname, 0, 0) ) { - /* allene: add explicit H if implicit H are present */ - /* iDeletedH = current number of already added explicit H */ - /* idelH1 = index in at[] of the explicit H added to atom jv */ - if ( at[jv].num_H ) { - if ( 0 > (ret = AddExplicitDeletedH( at, jv, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) { - goto exit_function; - } - } else { - /* index of the stereo atom neighbor */ - idelH1 = at[jv].neighbor[at[jv].neighbor[0]==n_vertex]; - } - if ( at[jn].num_H ) { - /* iDeletedH = current number of already added explicit H */ - /* idelH2 = index of the explicit H added to atom jn */ - if ( 0 > (ret = AddExplicitDeletedH( at, jn, num_atoms, &iDeletedH, &idelH2, nNumDeletedH, pStereo2 != NULL ))) { - goto exit_function; - } - } else { - idelH2 = at[jn].neighbor[at[jn].neighbor[0]==n_vertex]; - } - /* allene: set bond types to double */ - /* - if ( 0 > (ret = set_bond_type( at, (AT_NUMB)n_vertex, (AT_NUMB)jv, BOND_TYPE_DOUBLE ) ) || - 0 > (ret = set_bond_type( at, (AT_NUMB)n_vertex, (AT_NUMB)jn, BOND_TYPE_DOUBLE ) ) ) { - goto exit_function; - } - */ - /* allene: make 0D parity */ - ret = set_cumulene_0D_parity( at, st, num_atoms, idelH1, jv, jn, idelH2, parity, 2 ); - if ( ret < 0 ) { - goto exit_function; - } - } else { - /* stereogenic sp3 atom */ - if ( at[n_vertex].num_H ) { - if ( 0 > (ret = AddExplicitDeletedH( at, n_vertex, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) { - goto exit_function; - } - } - ret = set_atom_0D_parity( at, st, num_atoms, nNumDeletedH, n_vertex, parity ); - if ( ret < 0 ) { - goto exit_function; - } - num_stereo_centers ++; - } - if ( ret < 0 ) { - goto exit_function; - } - } - } - if ( num_stereo_bonds2 ) { - /* In case of Fixed-H */ - /* mark additional Mobile-H stereobonds, they have already been connected the added explicit H, if any */ - for ( i = 0; i < pStereo2->nNumberOfStereoBonds; i ++ ) { - jv = pStereo2->nBondAtom1[i]-1; - jn = pStereo2->nBondAtom2[i]-1; - parity = pStereo2->b_parity[i]; - if ( !is_in_the_list( at[jv].neighbor, (AT_NUMB)jn, at[jv].valence ) ) { - /* must be a cumulene */ - if ( !bFindCumuleneChain( at, (AT_NUMB)jv, (AT_NUMB)jn, nCumulene, MAX_CUMULENE_LEN+1 ) ) { - return RI_ERR_SYNTAX; /* not a cumulene */ - } - len = MAX_CUMULENE_LEN+1; - } else { - /* a regular double or alt bond */ - nCumulene[0] = jv; - nCumulene[1] = jn; - len = 1; /* cumulene length is number of bonds, not number of atoms */ - } - /* cumulene or double bond: add explicit H if implicit H are present */ - if ( at[jv].num_H ) { - if ( 0 > (ret = AddExplicitDeletedH( at, jv, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) { - goto exit_function; - } - } else { - /* double bond neighbor that has the smallest canonical number */ - idelH1 = at[jv].neighbor[at[jv].neighbor[0]==nCumulene[1]]; - } - if ( at[jn].num_H ) { - if ( 0 > (ret = AddExplicitDeletedH( at, jn, num_atoms, &iDeletedH, &idelH2, nNumDeletedH, pStereo2 != NULL ))) { - goto exit_function; - } - } else { - idelH2 = at[jn].neighbor[at[jn].neighbor[0]==nCumulene[len-1]]; - } - if ( 0 > (ret = set_cumulene_0D_parity( at, st, num_atoms, idelH1, jv, jn, idelH2, parity, len )) ) { - goto exit_function; - } - } - - } - - - ret = num_atoms; - -exit_function: - return ret; -} -/*************************************************************/ -int SetStereoBondTypeFor0DParity( inp_ATOM *at, int i1, int m1 ) -{ - AT_NUMB nCumulene[MAX_CUMULENE_LEN+2]; - int j, n1, n2, k1, m2, ret, nLenCumulene = 0, bond_type; - k1 = at[i1].sb_ord[m1]; - n1 = i1; - nCumulene[nLenCumulene ++] = n1; - do { - n2 = at[n1].neighbor[k1]; /* next atom */ - nCumulene[nLenCumulene ++] = n2; - for (m2 = 0; m2 < MAX_NUM_STEREO_BONDS && at[n2].sb_parity[m2]; m2 ++ ) { - if ( n1 == at[n2].neighbor[(int)at[n2].sb_ord[m2]] ) { - /* found the endatom */ - goto found; - } - } - if ( at[n2].num_H || at[n2].valence != 2 || at[n2].endpoint ) { - break; /* not a middle cumulene */ - } - k1 = (at[n2].neighbor[0] == n1); - n1 = n2; - } while ( at[n1].valence == 2 && !at[n1].num_H && nLenCumulene < MAX_CUMULENE_LEN+2 && - bCanAtomBeMiddleAllene( at[n1].elname, at[n1].charge, at[n1].radical ) ); - return RI_ERR_SYNTAX; /* failed */ - -found: - if ( nLenCumulene == 2 ) { - bond_type = BOND_TYPE_STEREO; /* double bond or alternating bond */ - } else { - bond_type = BOND_TYPE_DOUBLE; /* cumulene or allene */ - } - - for ( j = 1; j < nLenCumulene; j ++ ) { - /* if bond_type = BOND_TYPE_DOUBLE then increments at->cham_bonds_valence: */ - /* at->cham_bonds_valence += BOND_TYPE_DOUBLE-BOND_TYPE_SINGLE */ - if ( 0 > (ret = set_bond_type( at, (AT_NUMB)nCumulene[j-1], (AT_NUMB)nCumulene[j], bond_type ) ) ) { - return RI_ERR_PROGR; /* failed */ - } - } - return nLenCumulene; -} -/******************************************************************************************************/ -int SetStereoBondTypesFrom0DStereo( StrFromINChI *pStruct, INChI *pInChI) -{ - INChI_Stereo *pStereo; - inp_ATOM *at = pStruct->at; - int num_atoms = pStruct->num_atoms; - int i, j, num_stereo_bonds, ret; - - if ( pInChI->StereoIsotopic && - (pInChI->StereoIsotopic->nNumberOfStereoBonds + - pInChI->StereoIsotopic->nNumberOfStereoCenters) ) { - pStereo = pInChI->StereoIsotopic; - } else - if ( pInChI->Stereo && - (pInChI->Stereo->nNumberOfStereoBonds + - pInChI->Stereo->nNumberOfStereoCenters) ) { - pStereo = pInChI->Stereo; - } else { - pStereo = NULL; - } - - /************************ set bond types separately from stereo *******************/ - if ( pStereo ) { - num_stereo_bonds = 0; - for ( i = 0; i < num_atoms; i ++ ) { - /* set BOND_TYPE_DOUBLE in allenes and cumulenes */ - /* set BOND_TYPE_STEREO in double bond stereo */ - for ( j = 0; j < MAX_NUM_STEREO_BONDS && at[i].sb_parity[j]; j ++ ) { - num_stereo_bonds ++; - if ( 0 > (ret = SetStereoBondTypeFor0DParity( at, i, j ) ) ) { - goto exit_function; - } - } - } - if ( num_stereo_bonds ) { - int num_bond_type_stereo; - int num_bond_type_altern; - AT_NUMB neigh; - /* replace adjacent BOND_TYPE_STEREO with BOND_TYPE_ALTERN */ - for ( i = 0; i < num_atoms; i ++ ) { - num_bond_type_stereo = 0; - num_bond_type_altern = 0; - for ( j = 0; j < at[i].valence; j ++ ) { - num_bond_type_stereo += ( at[i].bond_type[j] == BOND_TYPE_STEREO ); - num_bond_type_altern += ( at[i].bond_type[j] == BOND_TYPE_ALTERN ); - } - if ( num_bond_type_stereo + num_bond_type_altern > 1 && num_bond_type_stereo ) { - for ( j = 0; j < at[i].valence; j ++ ) { - if ( at[i].bond_type[j] == BOND_TYPE_STEREO ) { - neigh = at[i].neighbor[j]; - /* does not change at[i].chem_bond_valence in case of BOND_TYPE_ALTERN */ - if ( 0 > (ret = set_bond_type( at, (AT_NUMB)i, neigh, BOND_TYPE_ALTERN ) ) ) { - goto exit_function; - } - } - } - } - /* at this point only isolated stereo bonds have type BOND_TYPE_STEREO */ - } - /* increment at[i].chem_bonds_valence if at[i] has an altern. bond */ - /* replace BOND_TYPE_STEREO with BOND_TYPE_DOUBLE and increment */ - /* chem_bonds_valence of the adjacent atoms */ - for ( i = 0; i < num_atoms; i ++ ) { - num_bond_type_stereo = 0; - num_bond_type_altern = 0; - for ( j = 0; j < at[i].valence; j ++ ) { - num_bond_type_stereo += ( at[i].bond_type[j] == BOND_TYPE_STEREO ); - num_bond_type_altern += ( at[i].bond_type[j] == BOND_TYPE_ALTERN ); - } - if ( !num_bond_type_stereo && num_bond_type_altern ) { - /* an atom has only BOND_TYPE_ALTERN => adjacent BOND_TYPE_ALTERN case */ - at[i].chem_bonds_valence += 1; - } else - if ( num_bond_type_stereo == 1 ) { - /* isolated BOND_TYPE_STEREO => replace with BOND_TYPE_DOUBLE */ - for ( j = 0; j < at[i].valence; j ++ ) { - if ( at[i].bond_type[j] == BOND_TYPE_STEREO ) { - neigh = at[i].neighbor[j]; - /* replacing BOND_TYPE_STEREO with BOND_TYPE_DOUBLE */ - /* does not change at->chem_bonds_valence */ - if ( 0 > (ret = set_bond_type( at, (AT_NUMB)i, neigh, BOND_TYPE_DOUBLE ) ) ) { - goto exit_function; - } - at[i].chem_bonds_valence ++; - at[(int)neigh].chem_bonds_valence ++; - } - } - } else - if ( num_bond_type_stereo + num_bond_type_altern ) { - /* an atom still has both BOND_TYPE_STEREO and BOND_TYPE_ALTERN */ - ret = RI_ERR_PROGR; - goto exit_function; - } - } - INCHI_HEAPCHK - } - } - ret = 0; /* success */ -exit_function: - return ret; -} - -/******************************************************************************************************/ -int CopyBnsToAtom( StrFromINChI *pStruct, BN_STRUCT *pBNS, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int bAllowZeroBondOrder ) -{ - int i, j, atom_charge, left_charge, charge, ret = 0, v1, nMinorder; - int num_at = pStruct->num_atoms; - inp_ATOM *at = pStruct->at; - ICHICONST SRM *pSrm = pStruct->pSrm; - BNS_VERTEX *pv; - BNS_EDGE *pe; - int chem_bonds_valence, bond_order; - - atom_charge = left_charge = 0; - for ( i = 0; i < num_at; i ++ ) { - pv = pBNS->vert + i; - /* bonds */ - chem_bonds_valence = 0; - for ( j = 0; j < at[i].valence; j ++ ) { - pe = pBNS->edge + pv->iedge[j]; - BondFlowMaxcapMinorder( at, pVA, pSrm, i, j, NULL, &nMinorder, NULL ); - bond_order = pe->flow + nMinorder; - if ( !bAllowZeroBondOrder && !bond_order ) { - bond_order = 1; - } - chem_bonds_valence += bond_order; - at[i].bond_type[j] = bond_order; /* BOND_MARK_HIGHLIGHT */ - } - at[i].chem_bonds_valence = chem_bonds_valence; - /* charges (both may be present resulting in zero) */ - at[i].charge = pVA[i].cInitCharge; - if ( pVA[i].nCMinusGroupEdge ) { - pe = pBNS->edge + pVA[i].nCMinusGroupEdge - 1; - if ( charge = pe->flow ) { - at[i].charge -= charge; - atom_charge -= charge; - } - } - if ( pVA[i].nCPlusGroupEdge ) { - pe = pBNS->edge + pVA[i].nCPlusGroupEdge - 1; - if ( charge = pe->cap - pe->flow ) { - at[i].charge += charge; - atom_charge += charge; - } - } - if ( pv->st_edge.cap > pv->st_edge.flow ) { - at[i].radical = RADICAL_SINGLET + (pv->st_edge.cap - pv->st_edge.flow); - } - } - /* find charge excess */ - for ( i = num_at; i < pBNS->num_vertices; i ++ ) { - pv = pBNS->vert + i; - if ( charge = pv->st_edge.cap - pv->st_edge.flow ) { - if ( IS_BNS_VT_C_OR_CSUPER_GR(pv->type) ) { - left_charge -= charge; - } else - if ( IS_BNS_VT_YVCONNECTOR(pv->type) ) { - left_charge += charge; - } - } - } - /* tautomeric H and (-) */ - for ( i = 0; i < pBNS->num_t_groups; i ++ ) { - /* tautomeric groups are first non-atom vertices; - order of them is same as in pTCGroups->pTCG[] */ - int num_H = pTCGroups->pTCG[i].tg_num_H; - int num_Minus = pTCGroups->pTCG[i].tg_num_Minus; - int bMinusFirst = (pTCGroups->pTCG[i].tg_RestoreFlags & TGRF_MINUS_FIRST); - int num_at_add; - Vertex vMinus = NO_VERTEX; - pv = pBNS->vert + num_at + i; /* t-group vertex */ - if ( !(pv->type & BNS_VERT_TYPE_TGROUP) ) { - return RI_ERR_PROGR; - } - if ( pTCGroups->pTCG[i].tg_set_Minus > 0 && num_Minus > 0 ) { - vMinus = pTCGroups->pTCG[i].tg_set_Minus-1; - num_Minus --; - } - - if ( bMinusFirst ) { - for ( j = 0; j < pv->num_adj_edges; j ++ ) { - pe = pBNS->edge + pv->iedge[j]; - v1 = pe->neighbor1; - num_at_add = pe->flow; - if ( v1 == vMinus ) { - if ( num_at_add ) { - at[v1].charge = -1; /* no checking at[v1].charge == 0 for now ??? */ - num_at_add --; /* no checking num_at_add > 0 for now ??? */ - } else { - num_Minus ++; /* error ??? */ - } - vMinus = NO_VERTEX; - } - if ( num_at_add > 0 ) { - /* atom has tautomeric attachment; do not allow =N(-) */ - if ( num_Minus && !at[v1].charge && - at[v1].valence == at[v1].chem_bonds_valence ) { - at[v1].charge --; - num_at_add --; - num_Minus --; - } - if ( num_at_add > 0 ) { - at[v1].num_H += num_at_add; - num_H -= num_at_add; - num_at_add = 0; - } - } - at[v1].endpoint = i+1; - } - if ( (num_H+num_Minus != pv->st_edge.cap - pv->st_edge.flow) && (num_H || num_Minus || vMinus != NO_VERTEX) ) { - return RI_ERR_PROGR; - } - } else { - for ( j = pv->num_adj_edges-1; 0 <= j; j -- ) { - pe = pBNS->edge + pv->iedge[j]; - v1 = pe->neighbor1; - num_at_add = pe->flow; - if ( v1 == vMinus ) { - if ( num_at_add ) { - at[v1].charge = -1; /* no checking at[v1].charge == 0 for now ??? */ - num_at_add --; /* no checking num_at_add > 0 for now ??? */ - } else { - num_Minus ++; /* error ??? */ - } - vMinus = NO_VERTEX; - } - if ( num_at_add > 0 ) { - /* atom has tautomeric attachment; do not allow =N(-) */ - if ( num_Minus && !at[v1].charge && - at[v1].valence == at[v1].chem_bonds_valence ) { - at[v1].charge --; - num_at_add --; - num_Minus --; - } - if ( num_at_add > 0 ) { - at[v1].num_H += num_at_add; - num_H -= num_at_add; - num_at_add = 0; - } - } - at[v1].endpoint = i+1; - } - if ( (num_H+num_Minus != pv->st_edge.cap - pv->st_edge.flow) && (num_H || num_Minus || vMinus != NO_VERTEX) ) { - return RI_ERR_PROGR; - } - } - } - - return ret; -} -/******************************************************************************************************/ -int CheckBnsConsistency( StrFromINChI *pStruct, BN_STRUCT *pBNS, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int bNoRad ) -{ - int nOutput = 0; -#ifndef TARGET_API_LIB -#if ( bRELEASE_VERSION == 0 ) - char s[128]; - int i, j, atom_charge, left_charge, charge, excess_charge, ret = 0; - int v1, v2, flow, tot_st_flow, tot_st_cap, num_electrons, nNumMetalAtoms; - int num_at = pStruct->num_atoms; - inp_ATOM *at = pStruct->at; - BNS_VERTEX *pv; - BNS_EDGE *pe; -#ifdef _DEBUG - int bDebugOutput = 0; - bNoRad = 1; - bDebugOutput = 1; -#endif - /* count electrons and metals */ - num_electrons = -pTCGroups->total_charge; - nNumMetalAtoms = 0; - for ( i = 0; i < pTCGroups->num_tgroups; i ++ ) { - num_electrons += pTCGroups->pTCG[i].tg_num_H; - } - for ( i = 0; i < num_at; i ++ ) { - num_electrons += at[i].el_number + at[i].num_H; - nNumMetalAtoms += pVA[i].cMetal; - } - /* create output string */ - sprintf( s, "%d:%d%sM%02dv%da%de%db%d* ", - bNoRad, pTCGroups->iComponent+1, num_electrons%2?"O":"E", nNumMetalAtoms, - pBNS->num_vertices, num_at, pBNS->num_edges, pBNS->num_bonds ); - - - tot_st_flow = tot_st_cap = 0; - atom_charge = left_charge = 0; - if ( pBNS->num_atoms != num_at ) { - fprintf( stdout, "\n%sNum. atoms discrepancy: %d(BNS) vs. %d(at) ", s, pBNS->num_atoms, num_at); - nOutput ++; - } - /* check edges */ -#ifdef _DEBUG - if ( bDebugOutput && bNoRad ) { - fprintf( stderr, "\n\n------begin------------------------------------------------\n" ); - fprintf( stderr, "\n\fedge cap flow v1 v2\n\n" ); - /* xxxx xxxx/xxxx xxxx/xxxx xxxx xxxx */ - } -#endif - for ( i = 0; i < pBNS->num_edges; i ++ ) { - pe = pBNS->edge + i; - v1 = pe->neighbor1; - v2 = v1 ^ pe->neighbor12; - if ( pe->cap < pe->flow || pe->flow < 0 ) { - fprintf( stdout, "\n%sedge %d (%d-%d) has cap=%d flow=%d ", s, i, v1, v2, pe->cap, pe->flow ); - nOutput ++; - } -#ifdef _DEBUG - if ( bDebugOutput && bNoRad ) { - /* xxxx xxxx/xxxx xxxx/xxxx xxxx xxxx */ - fprintf( stderr, "%4d %4d/%-4d %4d/%-4d %4d %4d\n", i, pe->cap, pe->cap0, pe->flow, pe->flow0, v1, v2 ); - } -#endif - } - - - /* check vertices */ -#ifdef _DEBUG - if ( bDebugOutput && bNoRad ) { - fprintf( stderr, "\n\fvert st-cap st-flow type iedge : neigh\n\n" ); - /* xxxx xxxx/xxxx xxxx/xxxx 0xXXX xxxx : xxx */ - } -#endif - for ( i = 0; i < pBNS->num_vertices; i ++ ) { - pv = pBNS->vert + i; -#ifdef _DEBUG - if ( bDebugOutput && bNoRad ) { - /* xxxx xxxx/xxxx xxxx/xxxx 0xXXX xxxx : xxx */ - int j; - const char *s; - char sAtom[6]; - switch( pv->type ) { - case BNS_VERT_TYPE_ATOM: - sprintf( sAtom, "At %-2.2s", i < num_at? at[i].elname : "??" ); - s = sAtom; - break; - case BNS_VERT_TYPE_ATOM | BNS_VERT_TYPE_ENDPOINT: - s = "Endpt"; - break; - case BNS_VT_C_POS: - s = "(+) "; - break; - case BNS_VT_C_NEG: - s = "(-) "; - break; - case BNS_VT_C_POS_C: - s = "(+C) "; - break; - case BNS_VT_C_NEG_C: - s = "(-C) "; - break; - case BNS_VT_C_POS_M: - s = "(+M) "; - break; - case BNS_VT_C_NEG_M: - s = "(-M) "; - break; - case BNS_VT_C_POS_ALL: - s = "(+)Sg"; - break; - case BNS_VT_C_NEG_ALL: - s = "(-)Sg"; - break; - case BNS_VT_M_GROUP: - s = "M-grp"; - break; - case BNS_VERT_TYPE__AUX | BNS_VERT_TYPE_TEMP: - s = "ChStr"; - break; - case BNS_VERT_TYPE__AUX: - s = "Yconn"; - break; - case BNS_VERT_TYPE_TGROUP: - s = "T-grp"; - break; - default: - s = "Unkn."; - break; - } - fprintf( stderr, "%4d %4d/%-4d %4d/%-4d 0x%03X %5s", - i, pv->st_edge.cap, pv->st_edge.cap0, pv->st_edge.flow, pv->st_edge.flow0, - pv->type, s ); - for ( j = 0; j < pv->num_adj_edges; j ++ ) { - fprintf( stderr, " %2d", pv->iedge[j] ); - } - fprintf( stderr, ":" ); - for ( j = 0; j < pv->num_adj_edges; j ++ ) { - pe = pBNS->edge + pv->iedge[j]; - fprintf( stderr, " %2d", pe->neighbor12 ^ i ); - } - fprintf( stderr, "\n" ); - } -#endif - tot_st_flow += pv->st_edge.flow; - tot_st_cap += pv->st_edge.cap; - if ( pv->num_adj_edges > pv->max_adj_edges ) { - fprintf( stdout, "\n%s%s %d type 0x%X \"%s\" num_edges=%d > max=%d ", s, - i < num_at? "atom":"vertex", i, - pv->type, at[i].elname, pv->num_adj_edges, pv->max_adj_edges ); - nOutput ++; - } - if ( i < num_at ) { - /* charge on atoms */ - charge = pVA[i].cInitCharge; - if ( pVA[i].nCMinusGroupEdge ) { - pe = pBNS->edge + pVA[i].nCMinusGroupEdge - 1; - if ( pe->flow > 0 ) { - charge -= pe->flow; - } - } - if ( pVA[i].nCPlusGroupEdge ) { - pe = pBNS->edge + pVA[i].nCPlusGroupEdge - 1; - if ( pe->cap > pe->flow ) { - charge += pe->cap - pe->flow; - } - } - if ( bNoRad && pv->st_edge.flow != pv->st_edge.cap ) { - fprintf( stdout, "\n%s%s %d: type 0x%X \"%s\" unexpected st_cap=%d st_flow=%d ", s, - i < num_at? "atom":"vertex", i, - pv->type, at[i].elname, pv->st_edge.cap, pv->st_edge.flow ); - nOutput ++; - } else - if ( bNoRad && charge && !strcmp(at[i].elname, "C") ) { - /* ignore carbonyls */ - if ( i == 0 && num_at == 2 && !strcmp(at[1].elname, "O") && - !at[0].num_H && !at[1].num_H && !pTCGroups->total_charge) { - ; /* C(-)#O(+) structure */ - } else { - fprintf( stdout, "\n%s%s %d: type 0x%X \"%s\" charge=%d ", s, - i < num_at? "atom":"vertex", i, - pv->type, at[i].elname, charge ); - nOutput ++; - } - } - atom_charge += charge; - } else - if ( (charge = pv->st_edge.cap - pv->st_edge.flow) > 0 ) { - /* excess charge */ - if ( !bNoRad && IS_BNS_VT_C_OR_CSUPER_GR(pv->type) ) { - left_charge -= charge; - } else - if ( !bNoRad && IS_BNS_VT_YVCONNECTOR(pv->type) ) { - left_charge += charge; - } else - if ( !bNoRad && IS_BNS_VT_M_GR(pv->type) && - 0 <= (j=pTCGroups->nGroup[TCG_MeFlower3]) && - i == pTCGroups->pTCG[j].nVertexNumber ) { - ; /* additional "radical" on metal flower */ - } else - if ( !(pv->type & BNS_VERT_TYPE_TGROUP) || bNoRad ) { - /* t-groups before running BFS should have st_cap > st_flow */ - fprintf( stdout, "\n%s%s %d: type 0x%X unexpected st_cap=%d st_flow=%d ", s, - i < num_at? "atom":"vertex", i, - pv->type, pv->st_edge.cap, pv->st_edge.flow); - nOutput ++; - } - } - if ( pv->st_edge.cap < pv->st_edge.flow || pv->st_edge.flow < 0 ) { - fprintf( stdout, "\n%s%s %d: type 0x%X \"%s\" st_cap=%d st_flow=%d ", s, - i < num_at? "atom":"vertex", i, - pv->type, i < num_at? at[i].elname:"", pv->st_edge.cap, pv->st_edge.flow ); - nOutput ++; - } - /* check edge_flow vs. st_flow consistency */ - for( j = 0, flow = 0; j < pv->num_adj_edges; j ++ ) { - pe = pBNS->edge + pv->iedge[j]; - flow += pe->flow; - } - if ( flow != pv->st_edge.flow ) { - fprintf( stdout, "\n%s%s %d: type 0x%X \"%s\" st_flow=%d edge_flow=%d ", s, - i < num_at? "atom":"vertex", i, - pv->type, i < num_at? at[i].elname:"", pv->st_edge.flow, flow ); - nOutput ++; - } - } -#ifdef _DEBUG - if ( bDebugOutput && bNoRad ) { - fprintf( stderr, "\n------end--------------------------------------------------\n" ); - } -#endif - /* - if ( num_electrons %= 2 ) { - fprintf( stdout, "\n%d*Odd number of electrons (%d atoms) ", bNoRad, num_at ); - nOutput ++; - } - */ - /* tautomeric groups charge */ - for ( i = 0, charge = 0; i < pTCGroups->num_tgroups; i ++ ) { - charge -= pTCGroups->pTCG[i].tg_num_Minus; - } - /* compare */ - if ( charge != pTCGroups->tgroup_charge ) { - fprintf( stdout, "\n%sCounted t-group charge=%d while %d was saved ", s, - charge, pTCGroups->tgroup_charge); - nOutput ++; - } - /* add other charges */ - charge += atom_charge + left_charge; - excess_charge = pTCGroups->total_charge - pTCGroups->added_charge - pTCGroups->tgroup_charge; - if ( charge != pTCGroups->total_charge && excess_charge != pTCGroups->total_charge - charge ) { - fprintf( stdout, "\n%sCounted total charge=%d while %d was saved; excess charge=%d ", s, - charge, pTCGroups->total_charge, excess_charge ); - nOutput ++; - } - if ( tot_st_cap != pBNS->tot_st_cap || tot_st_flow != pBNS->tot_st_flow ) { - fprintf( stdout, "\n%sCounted/saved total st_flow=%d/%d st_cap=%d/%d ", s, - tot_st_flow, pBNS->tot_st_flow, tot_st_cap, pBNS->tot_st_cap ); - nOutput ++; - } - if ( nOutput ) { - fprintf( stdout, "\n" ); - } -#endif -#endif - return nOutput; -} - -/******************************************************************************************************/ -int AddExplicitDeletedH( inp_ATOM *at, int jv, int num_at, int *iDeletedH, int *iH, int nNumDeletedH, int bTwoStereo ) -{ - inp_ATOM *cur_H, *cur_at = at+jv; - int tot_num_iso_H = NUM_ISO_H(cur_at, 0); - int num_H = cur_at->num_H; - int iso_H = 0; - S_CHAR num_iso_H[NUM_H_ISOTOPES]; - int i; - - if ( !at[jv].at_type ) { - return RI_ERR_PROGR; - } - - if ( at[jv].at_type > 1 ) { - /* explicit hydrogens have already been added; find them */ - for ( i = 0; i < *iDeletedH; i ++ ) { - if ( at[num_at + i].neighbor[0] == jv ) { - *iH = num_at + i; /* return the first found H, it has the smallest canonical pseudo rank */ - return 0; - } - } - return RI_ERR_PROGR; - } - /* add all explicit H disconnected from at[jv] in order H, 1H, D, T */ - *iH = *iDeletedH + num_at; /* num_H includes all H, both isotopic and normal */ - for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { - num_iso_H[i] = at[jv].num_iso_H[i]; - } - for ( ; num_H && (*iDeletedH) < nNumDeletedH; (*iDeletedH) ++ ) { - cur_H = at + num_at + (*iDeletedH); /* first available empty atom will be this explicit H */ - cur_H->neighbor[cur_H->valence] = jv; /* connect this new atom H to the real atom */ - cur_H->bond_type[cur_H->valence] = BOND_TYPE_SINGLE; - cur_H->valence ++; - if ( num_H > tot_num_iso_H ) { - num_H --; - if ( num_H != tot_num_iso_H ) { - /* may happen when Mobile-H stereo included in Fixed-H processing */ - if ( bTwoStereo ) { - continue; - } else { - return RI_ERR_SYNTAX; /* two identical H neighbors of a stereo atom/bond */ - } - } - } else { - while ( iso_H < NUM_H_ISOTOPES && !num_iso_H[iso_H] ) - iso_H ++; - if ( iso_H < NUM_H_ISOTOPES ) { - cur_H->iso_atw_diff = iso_H + 1; /* isotopic shift + 1 */ - num_H --; - tot_num_iso_H --; - num_iso_H[iso_H] --; - if ( num_iso_H[iso_H] ) { - return RI_ERR_SYNTAX; /* two identical isotopic H neighbors of a stereo atom/bond */ - } - } else { - return RI_ERR_SYNTAX; /* not enough isotopic H */ - } - } - } - if ( num_H ) { - return RI_ERR_SYNTAX; - } - at[jv].at_type ++; /* at[jv].at_type==2 => explicit hydrogens have already been added */ - return 0; /* success */ -} -/******************************************************************************************************/ -int bFindCumuleneChain( inp_ATOM *at, AT_NUMB i1, AT_NUMB i2, AT_NUMB nCumulene[], int nMaxLen ) -/* nMaxLen = number of bonds in cumulene = 3 = MAX_CUMULENE_LEN+1 */ -/* nCumulene[nMaxLen+1] will contain cumulene chain >i1=x=y=i2< in this order */ -{ - int i, len, iat, nat; - nCumulene[0] = i1; - for ( i = 0; i < at[i1].valence; i ++ ) { - len = 0; - iat = i1; /* current */ - nat = at[i1].neighbor[i]; /* next */ - if ( len+1 == nMaxLen ) { - if ( nat == i2 ) { - nCumulene[++len] = nat; - return 1; /* success */ - } - continue; /* check next at[i1] neighbor */ - } - while ( at[nat].valence == 2 && - at[nat].num_H == 0 && - bCanAtomBeMiddleAllene(at[nat].elname, 0, 0) ) { - nCumulene[++len] = nat; - nat = at[nat].neighbor[at[nat].neighbor[0]==iat]; /* new next */ - if ( len+1 == nMaxLen ) { - if ( nat == i2 ) { - nCumulene[++len] = nat; - return 1; /* success */ - } - break; /* check next at[i1] neighbor */ - } - iat = nCumulene[len]; /* new current */ - } - } - return 0; /* failed */ -} -/******************************************************************************************************/ -int set_bond_type( inp_ATOM *at, AT_NUMB i1, AT_NUMB i2, int bType ) -{ - AT_NUMB *p1 = is_in_the_list( at[i1].neighbor, i2, at[i1].valence ); - AT_NUMB *p2 = is_in_the_list( at[i2].neighbor, i1, at[i2].valence ); - if ( p1 && p2 ) { - int j1 = p1 - at[i1].neighbor; - int j2 = p2 - at[i2].neighbor; - int bTypePrev = at[i1].bond_type[j1]; - at[i1].bond_type[j1] = bType; - at[i2].bond_type[j2] = bType; - if ( bTypePrev && bTypePrev <= BOND_TYPE_TRIPLE && - bType && bType <= BOND_TYPE_TRIPLE ) { - at[i1].chem_bonds_valence += bType - bTypePrev; - at[i2].chem_bonds_valence += bType - bTypePrev; - } - return 0; - } - return RI_ERR_SYNTAX; -} -/******************************************************************************************************/ -int set_cumulene_0D_parity( inp_ATOM *at, inp_ATOM_STEREO *st, int num_at, int idelH1, int i1, int i2, int idelH2, int parity, int len ) -{ - AT_NUMB nCumulene[MAX_CUMULENE_LEN+2]; - AT_NUMB *p1, *p2; - int m1, m2, parity1, parity2, sb_ord_m1, sb_ord_m2, k1, k2, num_neigh1, num_neigh2; - /* the following types must exactly match types in inp_ATOM and inp_ATOM_STEREO */ - S_CHAR *sb_ord1, *sn_ord1, *sb_parity1; - S_CHAR *sb_ord2, *sn_ord2, *sb_parity2; - AT_NUMB *sn_orig_at_num1; - AT_NUMB *sn_orig_at_num2; - - - if ( !bFindCumuleneChain( at, (AT_NUMB)i1, (AT_NUMB)i2, nCumulene, len ) ) { - return RI_ERR_SYNTAX; /* not an allene */ - } - /* stereo bond neighbors: index of a stereo bond in its end-atom adjacency lists */ - if ( (p1 = is_in_the_list( at[i1].neighbor, nCumulene[1], at[i1].valence )) && - (p2 = is_in_the_list( at[i2].neighbor, nCumulene[len-1], at[i2].valence )) ) { - sb_ord_m1 = p1 - at[i1].neighbor; /* indes of stereobond in the atom's adjacency list */ - sb_ord_m2 = p2 - at[i2].neighbor; - } else { - return RI_ERR_PROGR; - } - num_neigh1 = at[i1].valence + at[i1].num_H; - num_neigh2 = at[i2].valence + at[i2].num_H; - - if ( num_neigh1 < MIN_NUM_STEREO_BOND_NEIGH || num_neigh1 > MAX_NUM_STEREO_BOND_NEIGH || - num_neigh2 < MIN_NUM_STEREO_BOND_NEIGH || num_neigh2 > MAX_NUM_STEREO_BOND_NEIGH ) { - return RI_ERR_SYNTAX; - } - - - sb_ord1 = st? st[i1].sb_ord : at[i1].sb_ord; - sb_ord2 = st? st[i2].sb_ord : at[i2].sb_ord; - sb_parity1 = st? st[i1].sb_parity : at[i1].sb_parity; - sb_parity2 = st? st[i2].sb_parity : at[i2].sb_parity; - - /* find the first unoccupied locations in the stereobond 0D descriptor lists; check whether the stereo has already been set */ - for( m1 = k1 = 0; m1 < MAX_NUM_STEREO_BONDS && sb_parity1[m1] && !(k1 = sb_ord1[m1] == sb_ord_m1); m1 ++ ) - ; - for( m2 = k2 = 0; m2 < MAX_NUM_STEREO_BONDS && sb_parity2[m2] && !(k2 = sb_ord2[m2] == sb_ord_m2); m2 ++ ) - ; - if ( m1 == MAX_NUM_STEREO_BONDS || m2 == MAX_NUM_STEREO_BONDS ) { - return RI_ERR_SYNTAX; - } - if ( k1 && k2 ) { - return 0; /* the stereo descriptor of this bond/allene/cumulene has already been set */ - } - if ( k1 || k2 ) { - return RI_ERR_SYNTAX; /* only half of a bond was set */ - } - - sn_ord1 = st? st[i1].sn_ord : at[i1].sn_ord; - sn_ord2 = st? st[i2].sn_ord : at[i2].sn_ord; - sn_orig_at_num1 = st? st[i1].sn_orig_at_num : at[i1].sn_orig_at_num; - sn_orig_at_num2 = st? st[i2].sn_orig_at_num : at[i2].sn_orig_at_num; - - /* stereo bond neighbors connection index */ - sb_ord1[m1] = sb_ord_m1; - sb_ord2[m2] = sb_ord_m2; - /* stereo bond end atom neighbors */ - sn_orig_at_num1[m1] = at[idelH1].orig_at_number; - if ( idelH1 < num_at ) { - if ( p1 = is_in_the_list( at[i1].neighbor, (AT_NUMB)idelH1, at[i1].valence ) ) { - sn_ord1[m1] = p1 - at[i1].neighbor; - } else { - return RI_ERR_PROGR; - } - } else { - sn_ord1[m1] = -1; - } - - sn_orig_at_num2[m2] = at[idelH2].orig_at_number; - if ( idelH2 < num_at ) { - if ( p2 = is_in_the_list( at[i2].neighbor, (AT_NUMB)idelH2, at[i2].valence ) ) { - sn_ord2[m2] = p2 - at[i2].neighbor; - } else { - return RI_ERR_PROGR; - } - } else { - sn_ord2[m2] = -1; - } - if ( ATOM_PARITY_WELL_DEF(parity) ) { - /* special case: 2 bonds to sb atom => inverse parity because */ - /* InChI parity refers to the lone pair as a neighbor */ - int num_inv = (num_neigh1 == MIN_NUM_STEREO_BOND_NEIGH) + (num_neigh2 == MIN_NUM_STEREO_BOND_NEIGH); - if ( num_inv % 2 ) { - parity = (parity == AB_PARITY_EVEN)? AB_PARITY_ODD : AB_PARITY_EVEN; - } - parity1 = AB_PARITY_EVEN; - parity2 = (parity == AB_PARITY_EVEN)? AB_PARITY_EVEN : AB_PARITY_ODD; - } else { - parity1 = parity2 = parity; - } - sb_parity1[m1] = parity1; - sb_parity2[m2] = parity2; - - return 0; -} -/******************************************************************************************************/ -int set_atom_0D_parity( inp_ATOM *at, inp_ATOM_STEREO *st, int num_at, int num_deleted_H, int i1, int parity ) -{ - int m1=0, m2, i, j, tot_num_neigh; - /* the following types must exactly match types in inp_ATOM and inp_ATOM_STEREO */ - /* Given parity from InChI, the order of stereo center neighbors is: */ - /* 1. The stereocenter itself if the total number of neighbors is 3 (not 4) */ - /* 2. Explicit H: non-isotopic, isotopic in order ofascending atomic mass */ - /* Explicit H have already been sorted in this order */ - /* 3. Normal neighboring atoms, atom numbers (=canonical numbers from InChI - 1) in ascending order */ - /* Normal neighboring atoms have already been sorted in this order */ - S_CHAR *p_parity; - AT_NUMB *p_orig_at_num; - - if ( !st || !at[i1].p_parity ) { - m1 = 0; - p_parity = st? &st[i1].p_parity : &at[i1].p_parity; - p_orig_at_num = st? st[i1].p_orig_at_num : at[i1].p_orig_at_num; - - tot_num_neigh = at[i1].valence + at[i1].num_H; - if ( tot_num_neigh == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { - /* only 3 neighbors: the atom itself is the first neighbor */ - p_orig_at_num[m1 ++] = at[i1].orig_at_number; - } else - if ( tot_num_neigh != MAX_NUM_STEREO_ATOM_NEIGH ) { - return RI_ERR_PROGR; /* wrong number of members */ - } - m2 = m1 + (MAX_NUM_STEREO_ATOM_NEIGH - at[i1].valence); - /* stereoneighbors: deleted explicit atoms H first, in order of increasing isotopic mass */ - if ( at[i1].num_H ) { - for ( j = 0; m1 < m2 && j < num_deleted_H; j ++ ) { - if ( at[j + num_at].neighbor[0] == i1 ) { - p_orig_at_num[m1 ++] = at[j + num_at].orig_at_number; - } - } - } - if ( m1 + at[i1].valence != MAX_NUM_STEREO_ATOM_NEIGH ) { - return RI_ERR_PROGR; /* wrong number of members */ - } - /* stereoneighbors: other than explicit H atoms */ - for ( i = 0; i < at[i1].valence; i ++ ) { - m2 = at[i1].neighbor[i]; - p_orig_at_num[m1 ++] = at[m2].orig_at_number; - } - *p_parity = parity; - } - - return 0; -} -#if ( BNS_RAD_SEARCH == 1 ) -/******************************************************************************************************/ -int MoveRadToAtomsAddCharges( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int forbidden_mask ) -{ - int nNumRad, ret = 0, ret2; - int i, j, k, num_rad_not_atom, num_moved=0, num_candidates= 0, extra_charge=0, added_charge, delta; - BNS_EDGE *pEdge; - BNS_VERTEX *pv; - Vertex v1, v2; - S_SHORT *pnRad = NULL, *pnDelta = NULL; - CC_CAND *pCand = NULL; - int cnBits, bAtomRadRemoved = 0; - - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - - for ( i = pBNS->num_atoms, num_rad_not_atom=0; i < pBNS->num_vertices; i ++ ) { - num_rad_not_atom += pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow; - } - if ( !num_rad_not_atom ) { - goto exit_function; - } - /****************************************************/ - /* */ - /* Move radicals from ChargeStruct to atoms */ - /* */ - /****************************************************/ - - /* allocate memory to keep track of moved radicals */ - pnRad = (S_SHORT *) inchi_malloc(pBNS->num_vertices * sizeof(pnRad[0])); - pnDelta = (S_SHORT *)inchi_calloc(pBNS->num_atoms, sizeof(pnDelta[0])); - if ( !pnRad || !pnDelta ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - for ( i = 0; i < pBNS->num_vertices; i ++ ) { - pnRad[i] = pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow; - } - while( 1 ) { - /* remove radicals from atoms */ - for ( i = 0; i < pBNS->num_atoms; i ++ ) { - pnDelta[i] = pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow; - pBNS->vert[i].st_edge.cap -= pnDelta[i]; - bAtomRadRemoved += (0 != pnDelta[i]); - } - ret = SetRadEndpoints( pBNS, pBD, RAD_SRCH_FROM_FICT ); - if ( !ret ) { - break; - } - if ( ret < 0 ) { - goto exit_function; - } - nNumRad = ret; - for ( i = 0; i < nNumRad; i ++ ) { - pEdge = pBNS->edge + pBD->RadEdges[i]; - v1 = pEdge->neighbor1; - v2 = pEdge->neighbor12 ^ v1; - pBNS->vert[v1].st_edge.flow -= pEdge->flow; - pBNS->vert[v2].st_edge.flow -= pEdge->flow; - pBNS->tot_st_flow -= 2*pEdge->flow; - pEdge->flow = 0; - pEdge->forbidden |= forbidden_mask; - pBNS->edge_forbidden_mask |= forbidden_mask; - } - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret < 0 ) { - goto exit_function; - } else { - num_moved += ret; - } - RemoveRadEndpoints( pBNS, pBD, NULL ); - if ( ret == 0 ) { - break; /* could not move more radicals */ - } - if ( bAtomRadRemoved ) { - /* restore radicals to atoms */ - for ( i = 0; i < pBNS->num_atoms; i ++ ) { - pBNS->vert[i].st_edge.cap += pnDelta[i]; - } - bAtomRadRemoved = 0; - } - } - if ( bAtomRadRemoved ) { - /* restore radicals to atoms */ - for ( i = 0; i < pBNS->num_atoms; i ++ ) { - pBNS->vert[i].st_edge.cap += pnDelta[i]; - } - bAtomRadRemoved = 0; - } - pBNS->edge_forbidden_mask &= ~forbidden_mask; - - - /****************************************************/ - /* */ - /* Fix the charges */ - /* */ - /****************************************************/ - if ( num_moved ) { - /* find reqired charge */ - extra_charge = 0; - for ( i = pBNS->num_atoms, pv=pBNS->vert+i; i < pBNS->num_vertices; i ++, pv++ ) { - if ( delta = pv->st_edge.cap - pv->st_edge.flow ) { - if ( IS_BNS_VT_C_OR_CSUPER_GR(pv->type) ) { - extra_charge -= delta; - } else - if ( BNS_VERT_TYPE__AUX == pv->type ) { - extra_charge += delta; - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - } - } - if ( !extra_charge ) { - goto exit_function; - } - /* find differences */ - num_candidates = 0; - for ( i = 0; i < pBNS->num_vertices; i ++ ) { - pnRad[i] = (pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow) - pnRad[i]; - if ( pnRad[i] > 0 && i < pBNS->num_atoms && !pVA[i].nTautGroupEdge ) { - num_candidates ++; - } - } - } - if ( num_candidates > 0 ) { - pCand = (CC_CAND *)inchi_calloc( num_candidates, sizeof(pCand[0]) ); - if ( !pCand ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - /* create atom */ - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - - for ( i = 0, j = 0; i < pBNS->num_vertices; i ++ ) { - if ( pnRad[i] > 0 && i < pBNS->num_atoms && !pVA[i].nTautGroupEdge ) { - pCand[j].iat = i; - pCand[j].num_bonds = at2[i].valence; - pCand[j].chem_valence = at2[i].chem_bonds_valence; - pCand[j].cMetal = pVA[i].cMetal; - pCand[j].cNumBondsToMetal = pVA[i].cNumBondsToMetal; - pCand[j].cNumValenceElectrons = pVA[i].cNumValenceElectrons; - pCand[j].cPeriodicRowNumber = pVA[i].cPeriodicRowNumber; - pCand[j].el_number = at2[i].el_number; - cnBits = (pVA[i].cnListIndex > 0)? cnList[pVA[i].cnListIndex-1].bits : 0; - while ( cnBits > 0 ) { - pCand[j].cNumChargeStates ++; - cnBits >>= cn_bits_shift; - } - j ++; - } - } - if ( j > 1 ) { - qsort( pCand, j, sizeof(pCand[0]), comp_cc_cand ); - } - added_charge = 0; - - for ( k = 0; k < j; k ++ ) { - int rest_of_charge = extra_charge - added_charge; - int charge_per_left_atom = (abs(rest_of_charge) + j-k - 1)/(j-k); - int this_atom_add_charge = rest_of_charge > 0? charge_per_left_atom : -charge_per_left_atom; - pVA[pCand[k].iat].cInitCharge += this_atom_add_charge; - added_charge += this_atom_add_charge; - if ( this_atom_add_charge ) { - for ( i = pBNS->num_vertices-1, pv = pBNS->vert + i; this_atom_add_charge && pBNS->num_atoms <= i; i --, pv -- ) { - if ( delta = pv->st_edge.cap - pv->st_edge.flow ) { - if ( this_atom_add_charge < 0 && IS_BNS_VT_C_OR_CSUPER_GR(pv->type) ) { - if ( delta + this_atom_add_charge > 0 ) { - delta = -this_atom_add_charge; - } - pv->st_edge.cap -= delta; - pBNS->tot_st_cap -= delta; - this_atom_add_charge += delta; - } else - if ( this_atom_add_charge > 0 && BNS_VERT_TYPE__AUX == pv->type ) { - if ( delta > this_atom_add_charge ) { - delta = this_atom_add_charge; - } - pv->st_edge.cap -= delta; - pBNS->tot_st_cap -= delta; - this_atom_add_charge -= delta; - } - } - } - } - } - } - -exit_function: - if ( pnRad ) { - inchi_free( pnRad ); - } - if ( pnDelta ) { - inchi_free( pnDelta ); - } - if ( pCand ) { - inchi_free( pCand ); - } - return ret; -} -#endif -/**************************************************************************************************/ -typedef struct tagMobileHGroups { - AT_NUMB group_number; - AT_NUMB atom_number; - AT_NUMB atom_type_pVA; - S_CHAR ineigh; - S_CHAR bond_type; - S_CHAR forbidden; - /* S_CHAR el_type;*/ - S_CHAR endpoint_valence; - S_CHAR num_bonds; - S_CHAR bonds_valence; - S_CHAR num_bonds_non_metal; - S_CHAR bonds_valence_non_metal; - -} MOBILE_GR; - -typedef struct tagMobileGroupList { - AT_NUMB group_number; - AT_NUMB num; -} MGROUPS; -/**************************************************************************************************/ -int AdjustTgroupsToForbiddenEdges2( BN_STRUCT *pBNS, inp_ATOM *at, VAL_AT *pVA, - int num_atoms, int forbidden_mask ) -{ - int i, j, k; - int centerpoint_type, neigh_type; - int num_changes; - int num_donors, num_acceptors, num_donor_endpoints, num_acceptor_endpoints; - int neigh, tg_number, num_eql_mobile_gr, num_dif_mobile_gr, bond_type, has_mobile_H, has_mobile; - int num_forbidden, ind_forbidden, forbidden, num_N, num_O, num_P, num_S, num_OSt; - int val, delta_val, delta_met, num_bonds_non_metal, bonds_valence_non_metal; - int num_bonds, bonds_valence; - int inv_forbidden_mask = ~forbidden_mask; - MOBILE_GR MobileGr[MAXVAL]; - int num_endpoints; - MGROUPS MGroups[MAXVAL]; - int num_mgroups, num_diff_t_groups; - BNS_EDGE *e, *e1, *e2, *ev, *ev1, *ev2; - BNS_VERTEX *pv1, *pv2; - num_changes = 0; - /* search for possible centerpoints */ - for ( i = 0; i < num_atoms; i ++ ) { - - if ( at[i].chem_bonds_valence == at[i].valence || at[i].num_H || - at[i].endpoint || at[i].charge || at[i].radical || - !is_centerpoint_elem(at[i].el_number) || - !(centerpoint_type = get_pVA_atom_type( pVA, at, i, 0 )) || - 2 > (delta_val = at[i].chem_bonds_valence - (val = get_el_valence(at[i].el_number, 0, 0))) || - 2 > (delta_met = (bonds_valence_non_metal = nNoMetalBondsValence(at, i)) - val ) - ) { - continue; - } - - num_donors = num_acceptors = num_donor_endpoints = num_acceptor_endpoints = 0; - num_mgroups = num_endpoints = num_diff_t_groups = 0; - has_mobile = has_mobile_H = num_eql_mobile_gr = num_dif_mobile_gr = tg_number = 0; - ind_forbidden = -1; - num_forbidden = 0; - num_N = num_O = num_P = num_S = num_OSt = 0; - num_bonds_non_metal = nNoMetalNumBonds(at, i); - bonds_valence = at[i].chem_bonds_valence; - num_bonds = at[i].valence; - - for ( j = 0; j < at[i].valence; j ++ ) { - /* collect neighbors info */ - neigh = at[i].neighbor[j]; - val = get_endpoint_valence( at[neigh].el_number ); - forbidden = pBNS->edge[(int)pBNS->vert[i].iedge[j]].forbidden; - bond_type = (at[i].bond_type[j] & BOND_TYPE_MASK); - neigh_type = get_pVA_atom_type( pVA, at, neigh, bond_type); - if ( !forbidden && !at[neigh].endpoint ) { - /* save forbidden bonds */ - if ( is_el_a_metal(at[neigh].el_number) ) { - continue; - } - switch( bond_type ) { - case BOND_TYPE_SINGLE: - if ( !at[neigh].num_H && at[neigh].charge != -1 ) { - continue; /* not a donor */ - } - break; - case BOND_TYPE_DOUBLE: - if ( !neigh_type ) { - continue; - } - break; - default: - continue; - } - } - - MobileGr[num_endpoints].atom_number = neigh; - MobileGr[num_endpoints].ineigh = j; - MobileGr[num_endpoints].bond_type = bond_type; - MobileGr[num_endpoints].group_number = at[neigh].endpoint; - MobileGr[num_endpoints].endpoint_valence = val; - MobileGr[num_endpoints].forbidden = forbidden; - MobileGr[num_endpoints].atom_type_pVA = neigh_type; - MobileGr[num_endpoints].num_bonds = at[neigh].valence; - MobileGr[num_endpoints].bonds_valence = at[neigh].chem_bonds_valence; - MobileGr[num_endpoints].num_bonds_non_metal = nNoMetalNumBonds(at, neigh); - MobileGr[num_endpoints].bonds_valence_non_metal = nNoMetalBondsValence( at, neigh ); - - if ( forbidden & forbidden_mask ) { - num_forbidden ++; - ind_forbidden = num_endpoints; - } - num_O += 0 != (neigh_type & EL_TYPE_O) && at[neigh].valence == 1; /* ignore -O- */ - num_N += 0 != (neigh_type & EL_TYPE_N) && - !(at[neigh].valence == 3 && at[neigh].chem_bonds_valence == 3); /* ignore -N< */ - num_S += 0 != (neigh_type & EL_TYPE_S) && at[neigh].valence == 1; /* ignore -S- */ - num_P += 0 != (neigh_type & EL_TYPE_P) && - !(at[neigh].valence == 3 && at[neigh].chem_bonds_valence == 3); /* ignore -P< */ - num_OSt += 0 != (neigh_type & EL_TYPE_OSt); - num_acceptors += (bond_type == BOND_TYPE_DOUBLE) && (neigh_type & EL_TYPE_PT); - num_donors += (bond_type == BOND_TYPE_SINGLE) && (neigh_type & EL_TYPE_PT) && - (at[neigh].num_H || at[neigh].charge==-1 || at[neigh].endpoint); - if ( at[neigh].endpoint ) { - num_acceptor_endpoints += (bond_type == BOND_TYPE_DOUBLE); - num_donor_endpoints += (bond_type == BOND_TYPE_SINGLE); - if ( !tg_number ) { - tg_number = at[neigh].endpoint; - num_eql_mobile_gr = 1; - } else - if ( tg_number == at[neigh].endpoint ) { - num_eql_mobile_gr ++; - } else { - num_dif_mobile_gr ++; - } - } else - if ( bond_type == BOND_TYPE_SINGLE && val ) { - if ( at[neigh].endpoint ) { - has_mobile_H |= 1; - has_mobile |= 1; - } else { - has_mobile_H |= (0 != at[neigh].num_H); - has_mobile |= (0 != at[neigh].num_H) || (at[neigh].charge == -1); - } - } - num_endpoints ++; - - if ( at[neigh].endpoint || (neigh_type & EL_TYPE_PT) ) { - for ( k = 0; k < num_mgroups; k ++ ) { - if ( MGroups[k].group_number == at[neigh].endpoint ) { - MGroups[k].num ++; - break; - } - } - if ( k == num_mgroups ) { - MGroups[k].group_number = at[neigh].endpoint; - MGroups[k].num = 1; - num_mgroups ++; - num_diff_t_groups += (0 != at[neigh].endpoint); - } - } - } - if ( !num_acceptors || !num_donors || /* num_acceptors > 2 ||*/ - num_eql_mobile_gr == num_endpoints && !num_forbidden || - !tg_number && !has_mobile_H ) { - continue; /* nothing to do */ - } - -/* case_5_1: */ - /***************** determine the case ************************/ - if ( 3 == num_bonds_non_metal && - 4 == bonds_valence_non_metal && - (centerpoint_type == EL_TYPE_C) && - 2 == num_O && 1 == num_N+num_S && num_OSt && - 1 == num_forbidden && 3 == num_eql_mobile_gr ) { - /******************************************************** - *** InChI Tech. Man., Table 5, case 1 *** - ******************************************************** - 2 - OH OH X = N, S, Se, Te - / / f = fixed bond - e / / tg = Mobile-H vertex - HX---C --> X===C - ev2|| f \\ ev1 f \ - || \\ \ - tg------O OH - 1 - Problem: - XH, O, and O belong to the same Mobile-H group. - Fixed bond prevents the correct structure restoration: - H cannot migrate from X to O because HX-N bond is fixed - Solution: - Move H from X to allow XH-C bond change - (this unfixes the bond, see SetForbiddenEdges(...) ) - *********************************************************/ - int jXH = -1, jO1 = -1, jO2 = -1, n = 0; - for ( j = 0; j < num_endpoints; j ++ ) { - if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_S)) && - (MobileGr[j].forbidden == forbidden_mask) && - MobileGr[j].bond_type == BOND_TYPE_SINGLE && - jXH < 0 ) { - jXH = j; - n ++; - } else - if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_O && - MobileGr[j].num_bonds_non_metal == 1 && - !MobileGr[j].forbidden ) { - if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && jO1 < 0 ) { - jO1 = j; - n ++; - } else - if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && jO2 < 0 ) { - jO2 = j; - n ++; - } - } - } - if ( n != 3 ) { - goto case_5_2; - } - /* XH-C edge */ - e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jXH].ineigh]; - /* C=O edge */ - ev1 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jO1].ineigh]; - /* XH-tg edge */ - ev2 = pBNS->edge + pVA[MobileGr[jXH].atom_number].nTautGroupEdge - 1; - - if ( !ev1->flow || !ev2->flow ) { - goto case_5_2; - } - - /* do not remove forbidden edge bit */ - e->flow ++; - ev1->flow --; - ev2->flow --; - pBNS->vert[ev1->neighbor12 ^ i].st_edge.flow --; - pBNS->vert[ev2->neighbor12 ^ ev2->neighbor1].st_edge.flow --; - pBNS->tot_st_flow -= 2; - num_changes ++; - continue; - } -case_5_2: - /*********************************************************************/ - if ( 3 == num_bonds_non_metal && - 5 == bonds_valence_non_metal && - (centerpoint_type == EL_TYPE_N) && - 2 == num_O && 1 == num_N+num_S && - 1 == num_forbidden && 3 == num_eql_mobile_gr ) { - /******************************************************** - *** InChI Tech. Man., Table 5, case 2 *** - ******************************************************** - - O OH X = N, S, Se, Te - // / f = fixed bond - e // / tg = Mobile-H vertex - HX---N --> X===N - ev2|| f \\ ev1 f \\ - || \\ \\ - tg------O O - - Problem: - XH, O, and O belong to the same Mobile-H group. - Fixed bond prevents the correct structure restoration: - H cannot migrate from X to O because HX-N bond is fixed - Solution: - Move H from X to allow XH-N bond change - (this unfixes the bond, see SetForbiddenEdges(...) ) - *********************************************************/ - int jXH = -1, jO1 = -1, jO2 = -1, n = 0; - for ( j = 0; j < num_endpoints; j ++ ) { - if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_S)) && - (MobileGr[j].forbidden == forbidden_mask) && - MobileGr[j].bond_type == BOND_TYPE_SINGLE && - jXH < 0 ) { - jXH = j; - n ++; - } else - if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_O && - MobileGr[j].bond_type == BOND_TYPE_DOUBLE && - MobileGr[j].num_bonds_non_metal == 1 && - !MobileGr[j].forbidden ) { - if ( jO1 < 0 ) { - jO1 = j; - n ++; - } else - if ( jO2 < 0 ) { - jO2 = j; - n ++; - } - } - } - if ( n != 3 ) { - goto case_5_4; - } - /* XH-N edge */ - e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jXH].ineigh]; - /* N=O edge */ - ev1 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jO1].ineigh]; - /* XH-tg edge */ - ev2 = pBNS->edge + pVA[MobileGr[jXH].atom_number].nTautGroupEdge - 1; - - if ( !ev1->flow || !ev2->flow ) { - goto case_5_4; - } - /* do not remove forbidden edge bit */ - e->flow ++; - ev1->flow --; - ev2->flow --; - pBNS->vert[ev1->neighbor12 ^ i].st_edge.flow --; /* first =O vertex */ - pBNS->vert[ev2->neighbor12 ^ ev2->neighbor1].st_edge.flow --; /* taut group vertex tg */ - pBNS->tot_st_flow -= 2; - num_changes ++; - continue; - } -case_5_4: - /*********************************************************************/ - if ( 3 == num_bonds_non_metal && - 5 == bonds_valence_non_metal && - (centerpoint_type & (EL_TYPE_N | EL_TYPE_P)) && - 1 == num_O+num_S && 0 < num_N && 2 == (num_N + num_P) && - 1 == num_forbidden && num_O+num_S+num_N == num_eql_mobile_gr ) { - /******************************************************** - *** InChI Tech. Man., Table 5, case 4 *** - ******************************************************** - O = O, S, Se, Te - X X X = N, P, As - // // f = fixed bond - e // ev2 // tg = Mobile-H vertex - O===N --> HO---N - || f \ ev1 f \\ - || \ \\ - tg------NH N - - Problem: - O, NH, and possibly X belong to the same Mobile-H group. - Fixed bond prevents the correct structure restoration: - H cannot migrate from NH to O because O=N bond is fixed - Solution: - Move H from NH to O to allow O=N bond change - (this unfixes the bond, see fix_special_bonds(...) ) - *********************************************************/ - int jO = -1, jNH = -1, jX = -1, n = 0; - for ( j = 0; j < num_endpoints; j ++ ) { - if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) && - MobileGr[j].forbidden == forbidden_mask && - MobileGr[j].bond_type == BOND_TYPE_DOUBLE && - MobileGr[j].num_bonds_non_metal == 1 && - jO < 0 ) { - jO = j; - n ++; - } else - if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_P)) && - !MobileGr[j].forbidden ) { - if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && - (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N && jNH < 0 ) { - jNH = j; - n ++; - } else - if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && jX < 0 ) { - jX = j; - n ++; - } - } - } - if ( n != 3 ) { - goto case_5_6; - } - /* O=N edge */ - e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jO].ineigh]; - /* N-NH edge */ - ev1 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jNH].ineigh]; - /* N=X edge */ - ev2 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jX].ineigh]; - - if ( !e->flow ) { - goto case_5_6; - } - /* do not remove forbidden edge bit */ - e->flow --; - ev1->flow ++; - pBNS->vert[e->neighbor12 ^ i].st_edge.flow --; - pBNS->vert[ev1->neighbor12 ^ i].st_edge.flow --; - pBNS->tot_st_flow -= 2; - num_changes ++; - continue; - } -case_5_6: - /********* InChI Tech.Man. Table 5, case 6 **************/ - if ( 2 == delta_met && 4 == num_bonds_non_metal && - 5 == bonds_valence_non_metal && - 1 == num_forbidden && 1 < num_eql_mobile_gr && - !num_dif_mobile_gr && - (centerpoint_type & (EL_TYPE_N | EL_TYPE_P)) && - 1 <= num_N && 2 <= num_N+num_O+num_S && - 1 == num_acceptor_endpoints && 0 < num_donor_endpoints ) { - int jN = -1, njFix = 0, jFix[4], n = 0; - /* centerpoint is N, P, As, Sb - - input output - ----- ------ - end - po- - int - 2 - - X ZH X Z Z=N,O,S,Se,Te [terminal endpoint] - \ | \ || - \| f f \|| - Y---N===N--- Y---N---NH--- - e f - cen end no bond - ter po- fixed - po- int - int 1 tautomerism O==N--NH is allowed - - Problem: OH and =N- belong to a Mobile-H group, but - forbidden edge e does not allow them to be - tautomeric in the restored structure. - - Solution: - - 1. Decrement flow in edge e - 2. Decrement st_edge flow in N and N connected by e - 3. Fix all single order bonds to not terminal tautomeric N around N(centerpoint) - 4. Run BNS to establist new flow distribution - */ - /* fixed bond */ - for ( j = 0; j < num_endpoints; j ++ ) { - neigh = MobileGr[j].atom_number; - if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && - (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N && - MobileGr[j].num_bonds_non_metal == 2 && - MobileGr[j].bonds_valence_non_metal == 3 && - at[neigh].endpoint && - !at[neigh].num_H && !at[neigh].charge && - !at[neigh].radical && - (MobileGr[j].forbidden & forbidden_mask) && jN < 0 ) { - jN = j; - n ++; - } else - if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && - at[neigh].endpoint ) { - if ( MobileGr[j].num_bonds > 1 ) { - jFix[njFix ++] = j; - } - n ++; - } - } - - if ( jN < 0 || n < 2 || 1 + njFix == n ) { - goto case_5_7; /* nothing to do */ - } - - e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jN].ineigh]; /* fixed edge */ - if ( !e->flow ) { - goto case_5_7; - } - e->flow --; - pBNS->vert[i].st_edge.flow --; - pBNS->vert[e->neighbor12 ^ i].st_edge.flow --; - pBNS->tot_st_flow -= 2; - - for ( j = 0; j < njFix; j ++ ) { - /* edges to fix */ - ev = pBNS->edge + pBNS->vert[i].iedge[(int)MobileGr[jFix[j]].ineigh]; - ev->forbidden |= forbidden_mask; - } - num_changes ++; - continue; - } -case_5_7: - /*********************************************************************/ - if ( 3 == num_bonds_non_metal && - 4 == bonds_valence_non_metal && - (centerpoint_type == EL_TYPE_S) && - 2 == num_O+num_S && 1 == num_OSt && 1 == num_N && - 1 == num_forbidden && 3 == num_eql_mobile_gr && - MobileGr[ind_forbidden].bond_type == BOND_TYPE_SINGLE ) { - /******************************************************** - *** InChI Tech. Man., Table 5, case 7 *** - ******************************************************** - O = O, S, Se, Te - OH OH S = S, Se, Te - / / f = fixed bond - e /ev2 f / tg = Mobile-H vertex - HN---S --> N===S X = N or non-endpoint; - || f \\ \ - ev2|| \\ev1 \ - tg------O OH - N, O, O - Problem: ======= - O, NH, OH belong to the same Mobile-H group. - Fixed bond prevents the correct structure restoration: - H cannot migrate from NH to O because HN-S bond is fixed - Solution: - Move H from NH to =O to allow HN=S bond change by making a 2nd terminal -OH - (this unfixes the bond, see fix_special_bonds(...) ) - *********************************************************/ - int jO = -1, jNH = -1, jOH = -1, n = 0; - for ( j = 0; j < num_endpoints; j ++ ) { - if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N && - MobileGr[j].forbidden == forbidden_mask && - MobileGr[j].bond_type == BOND_TYPE_SINGLE && - MobileGr[j].num_bonds_non_metal <= 2 && - jNH < 0 ) { - jNH = j; - n ++; - } else - if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) && - !MobileGr[j].forbidden && - MobileGr[j].num_bonds_non_metal == 1 ) { - if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && - jO < 0 ) { - jO = j; - n ++; - } else - if ( jOH < 0 ) { - jOH = j; - n ++; - } - } - } - if ( n != 3 ) { - goto case_5_9a; - } - /* NH-S edge */ - e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jNH].ineigh]; - /* S=O edge */ - ev1 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jO].ineigh]; - /* XH-tg edge */ - ev2 = pBNS->edge + pVA[MobileGr[jNH].atom_number].nTautGroupEdge - 1; - - if ( !ev1->flow || !ev2->flow ) { - goto case_5_9a; - } - - /* do not remove forbidden edge bit */ - e->flow ++; - ev1->flow --; - ev2->flow --; - pBNS->vert[ev1->neighbor12 ^ i].st_edge.flow --; /* first =O vertex */ - pBNS->vert[ev2->neighbor12 ^ ev2->neighbor1].st_edge.flow --; /* taut group vertex tg */ - pBNS->tot_st_flow -= 2; - num_changes ++; - continue; - } -case_5_9a: - /*********************************************************************/ - if ( 3 == num_bonds_non_metal && - 4 == bonds_valence_non_metal && - (centerpoint_type == EL_TYPE_S) && - 1 == num_O+num_S && !num_OSt && 1 <= num_N && - 1 == num_forbidden && - num_O+num_S+num_N == num_eql_mobile_gr && - MobileGr[ind_forbidden].bond_type == BOND_TYPE_SINGLE ) { - /******************************************************** - *** InChI Tech. Man., Table 5, case 9a *** - ******************************************************** - O = O, S, Se, Te - X X S = S, Se, Te - / / f = fixed bond - / / tg = Mobile-H vertex - HN---S --> N===S X = N or non-endpoint; - || \\ e \ -X is not -O(terminal) - || f\\ f\ - tg------O OH - N, N, O or N, O - Problem: ================ - O, NH belong to the same Mobile-H group. - Fixed bond prevents the correct structure restoration: - H cannot migrate from NH to O because O=S bond is fixed - Solution: - Move H from NH to =O to allow O=S bond change by making a terminal -OH - (this unfixes the bond, see fix_special_bonds(...) ) - *********************************************************/ - int jO = -1, jNH = -1, jX = -1, n = 0; - for ( j = 0; j < num_endpoints; j ++ ) { - if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) && - MobileGr[j].forbidden == forbidden_mask && - MobileGr[j].bond_type == BOND_TYPE_DOUBLE && - MobileGr[j].num_bonds_non_metal == 1 && - jO < 0 ) { - jO = j; - n ++; - } else - if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N && - !MobileGr[j].forbidden && - MobileGr[j].bond_type == BOND_TYPE_SINGLE && - jNH < 0 ) { - jNH = j; - n ++; - } else - if ( jX < 0 ) { - jX = j; - n ++; - } - } - if ( jO < 0 || jNH < 0 ) { - goto case_5_8b_to_9b; - } - - e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[ind_forbidden].ineigh]; - if ( !e->flow ) { - goto case_5_8b_to_9b; - } - e->flow --; - pBNS->vert[e->neighbor1].st_edge.flow --; - pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --; - pBNS->tot_st_flow -= 2; - num_changes ++; - continue; - } -case_5_8b_to_9b: /* #1 */ - /*********************************************************************/ - if ( 3 == num_bonds_non_metal && - 4 == bonds_valence_non_metal && - (centerpoint_type == EL_TYPE_S) && - 0 == num_O+num_S && 2 == num_N && 0 == num_P && !num_OSt && - 1 == num_forbidden && - 1 == num_eql_mobile_gr && 0 == num_dif_mobile_gr && - 1 == num_donor_endpoints && 0 == num_acceptor_endpoints && - 1 == num_donors && 1 == num_acceptors && - MobileGr[ind_forbidden].bond_type == BOND_TYPE_SINGLE ) { - /******************************************************** - *** InChI Tech. Man., Table 5, case 8b->9b *** - ******************************************************** - ---> O = O, S, Se, Te - X X S = S, Se, Te - \ f/ \ f/ f = fixed bond - \ ev / C=====Z \ / C-----ZH tg = Mobile-H vertex - N===S | | N===S || || X = is N not an endpoint; - not \ | | \ || || -X is not terminal -O,-S,-Se,-Te or - an \ | e | \|| e || any N, P, As - endpoint NH=====tg N------tg - is an f N, N, X, fixed single - endpoint ===================== - - Problem: - N is not a Mobile-H endpoint, NH is a Mobile-H endpoint - Unfixed bond N==S prevents the correct structure restoration: - H can migrate from NH to N because N=S bond is not fixed - Solution: - Move H from NH to =Z to make N=S bond fixed (Table 5, case 9) - (this unfixes the bond, see fix_special_bonds(...) ) - *********************************************************/ - int jN = -1, jNH = -1, jX = -1, n = 0; - for ( j = 0; j < num_endpoints; j ++ ) { - if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N && - !(MobileGr[j].forbidden & forbidden_mask) ) { - if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && - !at[MobileGr[j].atom_number].endpoint && - jN < 0 ) { - jN = j; - n ++; - } else - if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && - MobileGr[j].num_bonds == 2 && - MobileGr[j].bonds_valence == 2 && - at[MobileGr[j].atom_number].endpoint && - jNH < 0 ) { - jNH = j; - n ++; - } - } else - if ( !((MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_P)) || - (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) && - MobileGr[j].num_bonds > 1 ) && - (MobileGr[j].forbidden & forbidden_mask) && - MobileGr[j].bond_type == BOND_TYPE_SINGLE && - jX < 0 ) { - jX = j; - n ++; - } - } - if ( n != 3 ) { - goto case_5_8c_to_9c; - } - - e = pBNS->edge + pVA[MobileGr[jNH].atom_number].nTautGroupEdge - 1; - if ( !e->flow ) { - goto case_5_8c_to_9c; /* should not happen ??? */ - } - e->flow --; - pBNS->vert[e->neighbor1].st_edge.flow --; - pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --; - pBNS->tot_st_flow -= 2; - e->forbidden |= forbidden_mask; - num_changes ++; - continue; - } -case_5_8c_to_9c: /* #2 */ - /*********************************************************************/ - if ( 3 == num_bonds_non_metal && - 4 == bonds_valence_non_metal && - (centerpoint_type == EL_TYPE_S) && - 0 == num_O+num_S && 3 == num_N && 0 == num_P && - 1 == num_forbidden && - 3 == num_eql_mobile_gr && 0 == num_dif_mobile_gr && - 2 == num_donor_endpoints && 1 == num_acceptor_endpoints && - 2 == num_donors && 1 == num_acceptors && - MobileGr[ind_forbidden].bond_type == BOND_TYPE_SINGLE ) { - /******************************************************** - *** InChI Tech. Man., Table 5, case 8c->9c *** - ******************************************************** - is an endpoint ---> O = O, S, Se, Te - NH2(X) NH2(X) S = S, Se, Te - \ / pv1 pv2 \ / f = fixed bond - \ 1 / C-----ZH \ / C=====Z tg = Mobile-H vertex - N===S || || N===S | | X = is N not an endpoint; - is an \f||ev1 ||ev2 \f | | -X is not terminal -O,-S,-Se,-Te or - endpoint \|| e || \ | e | any N, P, As - 2 N------tg NH=====tg C is a centerpoint of a t-group - is an f N, N, X, fixed single - endpoint ===================== - - Problem: - N is not a Mobile-H endpoint, NH is a Mobile-H endpoint - Unfixed bond N==S prevents the correct structure restoration: - H can migrate from NH to N because N=S bond is not fixed - Solution: - Move H from NH to =Z to make N=S bond fixed (Table 5, case 9) - (this unfixes the bond, see fix_special_bonds(...) ) - *********************************************************/ - int jN1 = -1, jN2 = -1, jX = -1, n = 0; - EdgeIndex ie, ie1, ie2; - for ( j = 0; j < num_endpoints; j ++ ) { - if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N && - !(MobileGr[j].forbidden & forbidden_mask) ) { - if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && - at[MobileGr[j].atom_number].endpoint && - jN1 < 0 ) { - jN1 = j; - n ++; - } else - if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && - MobileGr[j].num_bonds == 2 && - MobileGr[j].bonds_valence == 3 && - MobileGr[j].forbidden == forbidden_mask && - at[MobileGr[j].atom_number].endpoint && - jN2 < 0 ) { - jN2 = j; - n ++; - } else - if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && - MobileGr[j].num_bonds <= 2 && - MobileGr[j].bonds_valence <= 3 && - at[MobileGr[j].atom_number].endpoint && - jX < 0 ) { - jX = j; - n ++; - } - } - } - if ( n != 3 ) { - goto case_5_9b_to_8b; - } - - e = pBNS->edge + pVA[MobileGr[jN2].atom_number].nTautGroupEdge - 1; - if ( e->flow ) { - goto case_5_9b_to_8b; /* should not happen ??? */ - } - pv1 = pBNS->vert + e->neighbor1; /* must be jN2 */ - pv2 = pBNS->vert + (e->neighbor1 ^ e->neighbor12); - ie = e - pBNS->edge; - ie1 = ie2 = -1; - for ( j = 0; j < pv1->num_adj_edges; j ++ ) { - ev1 = pBNS->edge + pv1->iedge[j]; - if ( ev1->flow && !ev1->forbidden ) { - ie1 = ev1 - pBNS->edge; - pv1 = pBNS->vert + (ev1->neighbor12 ^ (pv1 - pBNS->vert)); - break; - } - } - for ( j = 0; j < pv2->num_adj_edges; j ++ ) { - ev2 = pBNS->edge + pv2->iedge[j]; - if ( ev2->flow && !ev2->forbidden ) { - ie2 = ev2 - pBNS->edge; - pv2 = pBNS->vert + (ev2->neighbor12 ^ (pv2 - pBNS->vert)); - break; - } - } - if ( ie1 < 0 || ie2 < 0 ) { - goto case_5_9b_to_8b; - } - e->flow ++; - e->forbidden |= forbidden_mask; - ev1->flow --; - ev2->flow --; - pv1->st_edge.flow --; - pv2->st_edge.flow --; - pBNS->tot_st_flow -= 2; - num_changes ++; - continue; - } -case_5_9b_to_8b: /* #3 */ - /*********************************************************************/ - if ( 3 == num_bonds_non_metal && - 4 == bonds_valence_non_metal && - (centerpoint_type == EL_TYPE_S) && - 0 == num_O+num_S && 2 == num_N && 0 == num_P && - 1 == num_forbidden && - 2 == num_eql_mobile_gr && 0 == num_dif_mobile_gr && - 1 == num_donor_endpoints && 1 == num_acceptor_endpoints && - 1 == num_donors && 1 == num_acceptors && - MobileGr[ind_forbidden].bond_type == BOND_TYPE_DOUBLE ) { - /******************************************************** - *** InChI Tech. Man., Table 5, case 9b->8b *** - ******************************************************** - ---> O = O, S, Se, Te - X is an X S = S, Se, Te - \ / endpoint \ / f = fixed bond - \ 1ev / C-----ZH \ / C=====Z tg = Mobile-H vertex - N===S || || N===S | | X = is N not an endpoint; - is an f \ || || f \ | | -X is not terminal -O,-S,-Se,-Te or - endpoint 2\|| e || \ | e | any N, P, As - N------tg NH=====tg - is an f N, N, X, fixed double - endpoint ===================== - - Problem: - N1 and N2 are Mobile-H endpoints and belong to the same Mobile-H group. - Fixed bond N1==S prevents the correct structure restoration: - H cannot migrate ZH->N2->N1 because N1=S bond is fixed - Solution: - Move H from ZH to N2 to make N1=S bond unfixed and fix S-X bond (Table 5, case 8) - (see fix_special_bonds(...) for details ) - *********************************************************/ - int jN1 = -1, jN2 = -1, jX = -1, n = 0; - EdgeIndex ie, ie1, ie2; - for ( j = 0; j < num_endpoints; j ++ ) { - if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N ) { - if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && - at[MobileGr[j].atom_number].endpoint && - (MobileGr[j].forbidden == forbidden_mask) && - jN1 < 0 ) { - jN1 = j; - n ++; - } else - if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && - MobileGr[j].num_bonds == 2 && - MobileGr[j].bonds_valence == 3 && - at[MobileGr[j].atom_number].endpoint && - !(MobileGr[j].forbidden & forbidden_mask) && - jN2 < 0 ) { - jN2 = j; - n ++; - } - } else - if ( !((MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_P)) || - (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) && - MobileGr[j].num_bonds > 1 ) && - !(MobileGr[j].forbidden & forbidden_mask) && - MobileGr[j].bond_type == BOND_TYPE_SINGLE && - jX < 0 ) { - jX = j; - n ++; - } - } - if ( jN1 < 0 || jN2 < 0 ) { - goto case_5_9c_to_8c; - } - - ev = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jN1].ineigh]; - e = pBNS->edge + pVA[MobileGr[jN2].atom_number].nTautGroupEdge - 1; - if ( e->flow ) { - goto case_5_9c_to_8c; /* should not happen ??? */ - } - pv1 = pBNS->vert + e->neighbor1; /* must be jN2 */ - pv2 = pBNS->vert + (e->neighbor1 ^ e->neighbor12); - ie = e - pBNS->edge; - ie1 = ie2 = -1; - ev->forbidden &= inv_forbidden_mask; - for ( j = 0; j < pv1->num_adj_edges; j ++ ) { - ev1 = pBNS->edge + pv1->iedge[j]; - if ( ev1->flow && !ev1->forbidden ) { - ie1 = ev1 - pBNS->edge; - pv1 = pBNS->vert + (ev1->neighbor12 ^ (pv1 - pBNS->vert)); - break; - } - } - for ( j = 0; j < pv2->num_adj_edges; j ++ ) { - ev2 = pBNS->edge + pv2->iedge[j]; - if ( ev2->flow && !ev2->forbidden ) { - ie2 = ev2 - pBNS->edge; - pv2 = pBNS->vert + (ev2->neighbor12 ^ (pv2 - pBNS->vert)); - break; - } - } - if ( ie1 < 0 || ie2 < 0 ) { - ev->forbidden |= forbidden_mask; /* failed; restore the forbidden bit */ - goto case_5_9c_to_8c; - } - e->flow ++; - e->forbidden |= forbidden_mask; - ev1->flow --; - ev2->flow --; - pv1->st_edge.flow --; - pv2->st_edge.flow --; - pBNS->tot_st_flow -= 2; - num_changes ++; - continue; - } -case_5_9c_to_8c: /* #4 */ - /*********************************************************************/ - if ( 3 == num_bonds_non_metal && - 4 == bonds_valence_non_metal && - (centerpoint_type == EL_TYPE_S) && - 0 == num_O+num_S && 3 == num_N && 0 == num_P && - 0 == num_forbidden && - 2 == num_diff_t_groups && 2 == num_mgroups && /* all neighbors belong to 2 t-groups */ - 3 == num_eql_mobile_gr + num_dif_mobile_gr && /* all 3 neighbors belong to t-groups */ - 2 == num_donor_endpoints && 1 == num_acceptor_endpoints && - 2 == num_donors && 1 == num_acceptors ) { - /******************************************************** - *** InChI Tech. Man., Table 5, case 8c->9c *** - ******************************************************** - is an endpoint ---> O = O, S, Se, Te - tg1 NH2(X) NH2(X) S = S, Se, Te - \ / pv1 pv2 \ / f = fixed bond - \(1) / C=====Z \ / C-----ZH tg = Mobile-H vertex - N===S | | N===S || || X = is N not an endpoint; - is an \ |ev1 | ev2 \ || || -X is not terminal -O,-S,-Se,-Te or - endpoint \ | e | \|| e || any N, P, As - tg1 (2)NH=====tg N------tg C is a centerpoint of a t-group - is an f N, N, X, fixed single - endpoint ===================== - tg2 - Problem: - N (1) and NH2 are Mobile-H group 1 endpoiints, NH (2) is a Mobile-H group 2 endpoint - Unfixed bonds N==S--NH(2) allows the two Mobile H groups to merge - hence prevents the correct structure restoration: - H can migrate from NH (2) to N (1) because S-NH(1) bond is not fixed - Solution: - Move H from NH(2) to =Z to make S-NH(2) bond fixed (Table 5, case 8c) - (this unfixes the bond, see fix_special_bonds(...) ) - *********************************************************/ - int jN1 = -1, jN2 = -1, jX = -1, n = 0; - /* find t-group that is represented by only one neighbor */ - for ( j = 0, k = 0; j < num_mgroups; j ++ ) { - if ( 1 == MGroups[k].num && MGroups[k].group_number ) { - k = MGroups[k].group_number; - break; - } - } - if ( !k ) { - goto case_5_9c_to_9d; - } - for ( j = 0; j < num_endpoints; j ++ ) { - if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N ) { - if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && - at[MobileGr[j].atom_number].endpoint && - at[MobileGr[j].atom_number].endpoint != k && - jN1 < 0 ) { - jN1 = j; - n ++; - } else - if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && - MobileGr[j].num_bonds == 2 && - MobileGr[j].bonds_valence == 2 && - at[MobileGr[j].atom_number].endpoint == k && - jN2 < 0 ) { - jN2 = j; - n ++; - } else - if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && - MobileGr[j].num_bonds <= 2 && - MobileGr[j].bonds_valence <= 3 && - at[MobileGr[j].atom_number].endpoint && - at[MobileGr[j].atom_number].endpoint != k && - jX < 0 ) { - jX = j; - n ++; - } - } - } - if ( n != 3 ) { - goto case_5_9c_to_9d; - } - - e = pBNS->edge + pVA[MobileGr[jN2].atom_number].nTautGroupEdge - 1; - if ( !e->flow ) { - goto case_5_9c_to_9d; /* should not happen ??? */ - } - e->flow --; - pBNS->vert[e->neighbor1].st_edge.flow --; - pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --; - pBNS->tot_st_flow -= 2; - e->forbidden |= forbidden_mask; - num_changes ++; - continue; - } -case_5_9c_to_9d: /* #6 */ - /*********************************************************************/ - if ( 3 == num_bonds_non_metal && - 4 == bonds_valence_non_metal && - (centerpoint_type == EL_TYPE_S) && - 0 == num_O+num_S && 3 == num_N && 0 == num_P && - 0 == num_forbidden && - 3 == num_mgroups && 2 == num_diff_t_groups && - 2 == num_donor_endpoints && 0 == num_acceptor_endpoints && - 2 == num_donors && 1 == num_acceptors ) { - /******************************************************** - *** InChI Tech. Man., Table 5, case 9b->8b *** - ******************************************************** - 3(X) ---> e2| O = O, S, Se, Te - NH---is an N====== S = S, Se, Te - \ / endpoint \ / f = fixed bond - \ 1 / C=====Z \ f / C-----Z tg = Mobile-H vertex - N===S | | N===S || || X = is N not an endpoint; - is an ev \ | | ev \ || || -X is not terminal -O,-S,-Se,-Te or - endpoint 2\ | e1 | \|| e1 || any N, P, As - NH=====tg N------tg - is an f N, N, X, fixed double - endpoint ===================== - - Problem: - N1, N2, and N3 are Mobile-H endpoints and belong to the same Mobile-H group. - Fixed bond N1==S prevents the correct structure restoration: - H cannot migrate N3->N2->N1 because N1=S bond is fixed - Solution: - Move mobile H to N2 and N3 to make N1=S bond unfixed (Table 5, case 9c) - (see fix_special_bonds(...) for details ) - *********************************************************/ - int jN1 = -1, jN2 = -1, jX = -1, n = 0; - for ( j = 0; j < num_endpoints; j ++ ) { - if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N ) { - if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && - !at[MobileGr[j].atom_number].endpoint && - !(MobileGr[j].forbidden & forbidden_mask) && - jN1 < 0 ) { - jN1 = j; - n ++; - } else - if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && - MobileGr[j].num_bonds == 2 && - MobileGr[j].bonds_valence <= 3 && - at[MobileGr[j].atom_number].endpoint && - !(MobileGr[j].forbidden & forbidden_mask) ) { - if ( jN2 < 0 ) { - jN2 = j; - n ++; - } else - if ( jX < 0 ) { - jX = j; - n ++; - } - } - } - } - if ( n != 3 ) { - goto case_end; - } - ev = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jN1].ineigh]; - if ( !e->flow ) { - goto case_end; - } - e1 = pBNS->edge + pVA[MobileGr[jN2].atom_number].nTautGroupEdge - 1; - if ( !e1->flow ) { - goto case_end; /* should not happen ??? */ - } - e2 = pBNS->edge + pVA[MobileGr[jX].atom_number].nTautGroupEdge - 1; - if ( !e2->flow ) { - goto case_end; /* should not happen ??? */ - } - /* take care of edge e1 */ - e = e1; - e->flow --; - pBNS->vert[e->neighbor1].st_edge.flow --; - pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --; - pBNS->tot_st_flow -= 2; - e->forbidden |= forbidden_mask; - num_changes ++; - /* take care of edge e2 */ - e = e2; - e->flow --; - pBNS->vert[e->neighbor1].st_edge.flow --; - pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --; - pBNS->tot_st_flow -= 2; - e->forbidden |= forbidden_mask; - num_changes ++; - /* take care of edge ev: do not let it change */ - ev->forbidden |= forbidden_mask; - continue; - } -case_end:; - } -/*exit_function:*/ - return num_changes; -} -/******************************************************************************************************/ -/* Replace ambiguous neutral (+)edge->flow=0, (-)edge->flow=1 with (+)edge->flow=1, (-)edge->flow=0 */ -/******************************************************************************************************/ -int RearrangePlusMinusEdgesFlow( BN_STRUCT *pBNS, BN_DATA *pBD, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, int forbidden_edge_mask ) -{ - int ret, ePlus, eMinus; - EDGE_LIST NewlyFixedEdges; - BNS_EDGE *pPlus, *pMinus; - int i, k1, k2, num_found, num_tot, delta, v1, v2; - - ret = 0; - - AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR ); - for ( i = 0, num_found = num_tot = 0; i < pBNS->num_atoms; i ++ ) { - eMinus = pVA[i].nCMinusGroupEdge - 1; - ePlus = pVA[i].nCPlusGroupEdge - 1; - num_tot += (eMinus >= 0) + (ePlus >= 0); - if ( eMinus >= 0 && ePlus >= 0 ) { - pPlus = pBNS->edge + ePlus; - pMinus = pBNS->edge + eMinus; - if ( (k1=pMinus->flow) > 0 && (k2=pPlus->cap-pPlus->flow) > 0 ) { - num_found ++; - } - } - } - if ( !num_found ) { - goto exit_function; - } - if ( ret = AllocEdgeList( &NewlyFixedEdges, num_tot + pBNS->num_bonds ) ) { - goto exit_function; - } - - for ( i = 0, num_found = num_tot = 0; i < pBNS->num_atoms; i ++ ) { - eMinus = pVA[i].nCMinusGroupEdge - 1; - ePlus = pVA[i].nCPlusGroupEdge - 1; - num_tot += (eMinus >= 0) + (ePlus >= 0); - if ( eMinus >= 0 && ePlus >= 0 ) { - pPlus = pBNS->edge + ePlus; - pMinus = pBNS->edge + eMinus; - if ( (k1=pMinus->flow) > 0 && (k2=pPlus->cap - pPlus->flow) > 0 ) { - /* rearrange */ - v1 = pMinus->neighbor1; - v2 = pMinus->neighbor12 ^ v1; - delta = inchi_min(k1,k2); - pMinus->flow -= delta; - pBNS->vert[v1].st_edge.flow -= delta; - pBNS->vert[v2].st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - } - /* fix charges */ - pPlus->forbidden |= forbidden_edge_mask; - pMinus->forbidden |= forbidden_edge_mask; - if ( (ret = AddToEdgeList( &NewlyFixedEdges, eMinus, 0 )) || - (ret = AddToEdgeList( &NewlyFixedEdges, ePlus, 0 ))) { - goto exit_function; - } - } else - if ( eMinus >= 0 ) { - /* fix charges */ - pMinus = pBNS->edge + eMinus; - pMinus->forbidden |= forbidden_edge_mask; - if ( ret = AddToEdgeList( &NewlyFixedEdges, eMinus, 0 )) { - goto exit_function; - } - } else - if ( ePlus >= 0 ) { - /* fix charges */ - pPlus = pBNS->edge + ePlus; - pPlus->forbidden |= forbidden_edge_mask; - if ( ret = AddToEdgeList( &NewlyFixedEdges, ePlus, 0 )) { - goto exit_function; - } - } - } - for ( i = 0; i < pBNS->num_bonds; i ++ ) { - pBNS->edge[i].forbidden |= forbidden_edge_mask; - if ( ret = AddToEdgeList( &NewlyFixedEdges, i, 0 )) { - goto exit_function; - } - } - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask ); - if ( ret < 0 ) { - goto exit_function; - } - -exit_function: - AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE ); - return ret; -} -/******************************************************************************************************/ -int IncrementZeroOrderBondsToHeteroat( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, - VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, - int forbidden_edge_mask) -{ -#define FIX_BOND_ADD_ALLOC 128 - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - BNS_EDGE *pe, *peZero, *peNeighMeigh = NULL, *peMeFlower; - BNS_VERTEX *pMeFlower = NULL, *pNeigh = NULL, *pNeighNeigh=NULL; - - int i, j, k, ret2, ret, bFixedCarbonCharges, num_changes, bSuccess; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - Vertex vMeFlower0, vNeigh, vNeighMeigh = NO_VERTEX; - - EDGE_LIST CarbonChargeEdges; - EDGE_LIST NewlyFixedEdges; - - ret = 0; - num_changes = 0; - - bFixedCarbonCharges = 0; - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR ); - - if ( !pTCGroups->num_metal_atoms || - 0 > (k=pTCGroups->nGroup[TCG_MeFlower0]) || - 0 > (vMeFlower0 = pTCGroups->pTCG[k].nVertexNumber)) { - goto exit_function; - } - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - for ( i = 0; i < num_at; i ++ ) { - if ( !pVA[i].cMetal || pVA[i].nMetalGroupEdge <= 0 ) { - continue; - } - peMeFlower = pBNS->edge + pVA[i].nMetalGroupEdge-1; - if ( vMeFlower0 != (peMeFlower->neighbor12 ^ i) ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - pMeFlower = pBNS->vert + vMeFlower0; - - for ( j = 0; j < at2[i].valence; j ++ ) { - if ( !peMeFlower->flow ) { - break; /* cannot do anything */ - } - if ( !(at2[i].bond_type[j] & BOND_TYPE_MASK) ) { - /* found a zero order bond */ - if ( !bFixedCarbonCharges ) { - /* do not let carbon atoms get charged */ - if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { - goto exit_function; - } - bFixedCarbonCharges ++; - } - peZero = pBNS->edge + pBNS->vert[i].iedge[j]; - if ( peZero->flow ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - /* fix other edges */ - for ( k = 0; k < at2[i].valence; k ++ ) { - pe = pBNS->edge + pBNS->vert[i].iedge[k]; - if ( pe->flow == 1 && !(pe->forbidden & forbidden_edge_mask) ) { - if ( ret = AddToEdgeList( &NewlyFixedEdges, pe - pBNS->edge, FIX_BOND_ADD_ALLOC )) { - goto exit_function; - } - pe->forbidden |= forbidden_edge_mask; - } - } - /* do not create =N(+)= in a ring or #O(+) terminal */ - for ( k = 0; k < num_at; k ++ ) { - if ( !pVA[k].cMetal && pVA[k].cNumValenceElectrons == 5 && - at2[k].valence == 2 && !at2[k].num_H && pVA[k].cMinRingSize <= 6 && - pVA[k].nCPlusGroupEdge > 0 && - (pe=pBNS->edge + pVA[k].nCPlusGroupEdge-1)->flow==1 && - !(pe->forbidden & forbidden_edge_mask)) { - - if ( ret = AddToEdgeList( &NewlyFixedEdges, pe - pBNS->edge, FIX_BOND_ADD_ALLOC )) { - goto exit_function; - } - pe->forbidden |= forbidden_edge_mask; - } - } - - /* metal's neighbor connected by a zero-order bond */ - pNeigh = pBNS->vert + (vNeigh = at2[i].neighbor[j]); - /*for ( k = 0; k < pNeigh->num_adj_edges; k ++ )*/ - for ( k = pNeigh->num_adj_edges-1; 0 <= k; k -- ) - { - peNeighMeigh = pBNS->edge + pNeigh->iedge[k]; - if ( !peNeighMeigh->flow ) { - continue; - } - vNeighMeigh = peNeighMeigh->neighbor12 ^ vNeigh; - if ( vNeighMeigh != i && vNeighMeigh != vMeFlower0 ) { - /* metal neighbor's neighbor connected by a not-zero-order bond */ - pNeighNeigh = pBNS->vert + vNeighMeigh; - break; /* found */ - } - } - if ( k < 0 ) { - continue; /* neighbor not found */ - } - peZero->flow ++; - peZero->forbidden |= forbidden_edge_mask; - peMeFlower->flow --; - peNeighMeigh->flow --; - pMeFlower->st_edge.flow --; - pNeighNeigh->st_edge.flow --; - pBNS->tot_st_flow -= 2; - /* test */ - bSuccess = 0; - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == vMeFlower0 && vPathStart == vNeighMeigh || - vPathEnd == vNeighMeigh && vPathStart == vMeFlower0) && abs(nDeltaCharge) <= 2 ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - (*pnNumRunBNS) ++; - *pnTotalDelta += ret; - num_changes ++; - bSuccess = ret; - } - if ( ret = AddToEdgeList( &NewlyFixedEdges, peZero - pBNS->edge, FIX_BOND_ADD_ALLOC )) { - goto exit_function; - } - } else { - peZero->flow --; - peZero->forbidden &= inv_forbidden_edge_mask; - peMeFlower->flow ++; - peNeighMeigh->flow ++; - pMeFlower->st_edge.flow ++; - pNeighNeigh->st_edge.flow ++; - pBNS->tot_st_flow += 2; - } - RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask ); - NewlyFixedEdges.num_edges = 0; - if ( bSuccess ) { - /* update at2[] */ - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - } - } - } - } - ret = num_changes; - -exit_function: - RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask ); - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE ); - return ret; -} -/*********************************************************************** - NH2 NH2 - \ \ - C==S(+)- => C(+)-S- where NH2 are not tautomeric - / / - NH2 NH2 -************************************************************************/ -int MovePlusFromS2DiaminoCarbon( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, - VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ - int i, j, k, ret, ret2, cur_success; - int delta; - EdgeIndex ePlusS, ePlusC, eMinusC, e; - BNS_VERTEX *pvS, *pvC, *pv1, *pv2; - BNS_EDGE *pePlusS, *pePlusC, *pe1, *pe2, *peCN[3], *peSC, *pe; - Vertex vC, vN; - - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - - Vertex vPathStart, vPathEnd, v1, v2; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - - EDGE_LIST AllChargeEdges; - - ret = 0; - cur_success = 0; - - AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - /* find (NH2)C=S(+) */ - for ( i = 0; i < num_at; i ++ ) { - if ( !pVA[i].cMetal && pVA[i].cNumValenceElectrons == 6 && - at2[i].valence == 2 && - (pvS = pBNS->vert+i)->st_edge.cap == pvS->st_edge.flow && - 0 <= (ePlusS = pVA[i].nCPlusGroupEdge-1) && !(pePlusS=pBNS->edge+ePlusS)->flow && /* S(+) */ - (pe1=pBNS->edge + pvS->iedge[0])->flow + - (pe2=pBNS->edge + pvS->iedge[1])->flow == 1 /* -S(+)= */ && - pVA[vC = (peSC=pe1->flow? pe1 : pe2)->neighbor12 ^ i].cNumValenceElectrons == 4 && - at2[vC].valence == 3 && - 0 <= (ePlusC=pVA[vC].nCPlusGroupEdge-1) && (pePlusC=pBNS->edge+ePlusC)->flow && - !(0 <= (eMinusC=pVA[vC].nCMinusGroupEdge-1) && pBNS->edge[eMinusC].flow ) ) { - /* found >C=S(+)- */ - pvC = pBNS->vert + vC; - for ( j = k = 0; j < at[vC].valence; j ++ ) { - if ( peSC != (peCN[k] = pBNS->edge + pvC->iedge[j]) && !peCN[k]->flow ) { - k ++; /* a single bond from C */ - } - } - if ( k != 2 ) { - continue; - } - for ( j = 0; j < k; j ++ ) { - vN = peCN[j]->neighbor12 ^ vC; - if ( pVA[vN].cNumValenceElectrons != 5 || - pBNS->vert[vN].st_edge.cap != pBNS->vert[vN].st_edge.flow || - at2[vN].num_H != 2 || - at2[vN].endpoint || (pStruct->endpoint && pStruct->endpoint[vN]) ) { - break; /* does not fit the pattern */ - } - } - if ( j != k ) { - continue; - } - /* fix all charges */ - if ( !AllChargeEdges.num_edges ) { - for ( j = 0; j < num_at; j ++ ) { - if ( 0 <= (e = pVA[j].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && - (ret = AddToEdgeList( &AllChargeEdges, e, 2*num_at )) ) { - goto exit_function; - } - if ( 0 <= (e = pVA[j].nCMinusGroupEdge-1) && !pBNS->edge[e].forbidden && - (ret = AddToEdgeList( &AllChargeEdges, e, 2*num_at )) ) { - goto exit_function; - } - } - } - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - pePlusS->forbidden &= ~forbidden_edge_mask; - pe = pePlusC; - if ( !pe->flow ) - continue; - delta = 1; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { - /* Remover (+)charge from S => nDeltaCharge == -1 */ - /* Flow change on pe (+)charge edge (atom S) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - (*pnNumRunBNS) ++; - cur_success ++; - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - } -exit_function: - AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); - return ret; -} -/******************************************************************************************************/ -int EliminateChargeSeparationOnHeteroatoms( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, - VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, - int forbidden_edge_mask, int forbidden_stereo_edge_mask) - /********* Avoid charge separation on heteroatoms ******************/ -{ - int i, j, k, ret, ret2, num_pos, num_neg, num_min=0, bFixedCarbonCharges; - int vPlusSuper; /* (+)super vertex */ - int ePlusSuper; /* edge from vPlusSuper to (+/-) */ - int vPlusMinus; /* (+/-) vertex */ - int nDeltaPlus1, nDeltaMinus1, delta; - BNS_VERTEX *pvPlusSuper, *pvPlusMinus; - BNS_EDGE *pEdge; - - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - - EDGE_LIST FixedLargeRingStereoEdges, CarbonChargeEdges; - - ret = 0; - - AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); - bFixedCarbonCharges = 0; - - if ( forbidden_stereo_edge_mask ) { - for ( i = 0; i < num_at; i ++ ) { - for ( j = 0; j < at2[i].valence; j ++ ) { - if ( pBNS->edge[k = pBNS->vert[i].iedge[j]].forbidden == forbidden_stereo_edge_mask ) { - int nMinRingSize = is_bond_in_Nmax_memb_ring( at2, i, j, pStruct->pbfsq->q, - pStruct->pbfsq->nAtomLevel, - pStruct->pbfsq->cSource, 99 /* max ring size */ ); - if ( 0 < nMinRingSize && (ret = AddToEdgeList( &FixedLargeRingStereoEdges, k, 64 ))) { - goto exit_function; - } - } - } - } - if ( !FixedLargeRingStereoEdges.num_edges ) { - goto exit_function; - } else { - /* allow stereobonds in rings change */ - RemoveForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask ); - } - } - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - /* count charges */ - num_pos = num_neg = 0; - for ( i = 0; i < num_at; i ++ ) { - if ( !pVA[i].cMetal && !at2[i].radical ) { - num_pos += ( at2[i].charge > 0 ); - num_neg += ( at2[i].charge < 0 ); - } - } - num_min = inchi_min( num_pos, num_neg ); - - - if ( num_min && - (k = pTCGroups->nGroup[TCG_Plus]) >= 0 && - (ePlusSuper = pTCGroups->pTCG[k].nForwardEdge) > 0 && - (vPlusSuper = pTCGroups->pTCG[k].nVertexNumber) >= num_at && - !(pEdge=pBNS->edge + ePlusSuper)->forbidden ) { - - vPlusMinus = pEdge->neighbor12 ^ vPlusSuper; - pvPlusSuper = pBNS->vert + vPlusSuper; - pvPlusMinus = pBNS->vert + vPlusMinus; - num_min = inchi_min( num_min, pEdge->flow ); - nDeltaPlus1 = pvPlusSuper->st_edge.cap - pvPlusSuper->st_edge.flow; - nDeltaMinus1 = pvPlusMinus->st_edge.cap - pvPlusMinus->st_edge.flow; - if ( num_min && (!nDeltaPlus1 && !nDeltaMinus1 ) ) { - if ( !bFixedCarbonCharges ) { /* 02-02-2006 */ - /* do not let carbon atoms get charged */ - if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { - goto exit_function; - } - bFixedCarbonCharges ++; - } - delta = 1; - pEdge->forbidden |= forbidden_edge_mask; - pBNS->edge_forbidden_mask |= forbidden_edge_mask; - for ( i = 0; i < num_min; i += delta ) { - /* cancel 1 pair of charges at a time */ - /* an attempt to cancel all at once may */ - /* convert a pair of N(IV)(+) into a pair of N(V) neutral with total charge reduced by 2 */ - pEdge->flow -= delta; - pvPlusSuper->st_edge.flow -= delta; - pvPlusMinus->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - /* test for charhe cancellation */ - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret == 1 && (vPathEnd == vPlusSuper && vPathStart == vPlusMinus || - vPathEnd == vPlusMinus && vPathStart == vPlusSuper) && nDeltaCharge < 0 ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else - if ( ret == 1 ) { - *pnTotalDelta += ret; - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - } else { - pEdge->flow += delta; - pvPlusSuper->st_edge.flow += delta; - pvPlusMinus->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - break; - } - } - num_min -= i; /* how many pairs of charges left */ - pEdge->forbidden &= inv_forbidden_edge_mask; - } - nDeltaPlus1 = pvPlusSuper->st_edge.cap - pvPlusSuper->st_edge.flow; - nDeltaMinus1 = pvPlusMinus->st_edge.cap - pvPlusMinus->st_edge.flow; - if ( num_min > 1 && (!nDeltaPlus1 && !nDeltaMinus1 ) ) { - delta = 2; - pEdge->forbidden |= forbidden_edge_mask; - pBNS->edge_forbidden_mask |= forbidden_edge_mask; - for ( i = 0; i < num_min; i += delta ) { - /* cancel 2 pairs of opposite charges at a time */ - /* 1. test cancellation of a pair of (+) charges */ - pvPlusSuper->st_edge.cap += delta; - pBNS->tot_st_cap += delta; - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret < 0 ) { - goto exit_function; - } - pvPlusSuper->st_edge.cap -= delta; - pBNS->tot_st_cap -= delta; - if ( ret != 1 || (vPathEnd != vPlusSuper || vPathStart != vPlusSuper) || nDeltaCharge >= 0 ) { - break; - } - /* 2. test cancellation of a pair of (-) charges */ - pvPlusMinus->st_edge.cap += delta; - pBNS->tot_st_cap += delta; - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret < 0 ) { - goto exit_function; - } - pvPlusMinus->st_edge.cap -= delta; - pBNS->tot_st_cap -= delta; - if ( ret != 1 || (vPathEnd != vPlusMinus || vPathStart != vPlusMinus) || nDeltaCharge >= 0 ) { - break; - } - /* 3. Actually cancel the pair of charges */ - pEdge->flow -= delta; - pvPlusSuper->st_edge.flow -= delta; - pvPlusMinus->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else - if ( ret == 2 ) { - *pnTotalDelta += ret; - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - } - num_min -= i; /* how many pairs of charges left */ - pEdge->forbidden &= inv_forbidden_edge_mask; - } - } - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at; -exit_function: - if ( bFixedCarbonCharges ) { - RemoveForbiddenEdgeMask(pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); - } - if ( forbidden_stereo_edge_mask && FixedLargeRingStereoEdges.num_edges ) { - SetForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask ); - } - AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_FREE ); - - return ret < 0? ret : num_min; -} -#if (MOVE_CHARGES_FROM_HETEREO_TO_METAL == 1 ) -/********************** not used *************************************************************************/ -int MoveChargeFromHeteroatomsToMetals( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, - VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, - int forbidden_edge_mask) - /********* Avoid charge separation on heteroatoms ******************/ -{ - int i, k, ret, ret2, num_pos, num_neg, num_min; - int vPlusSuper, vMinusSuper; /* (+), (-) super vertices */ - int ePlusSuper, eMinusSuper; /* edges from vPlusSuper or vMinusSuper to (+/-) */ - int vPlMn; /* (+/-) vertex */ - int vPlusHeteroat, vMinusHeteroat; /* (+), (-) heteroatom vertices */ - int ePlusHeteroat, eMinusHeteroat; /* edges from (+) or (-) heteroatom vertex to super (+) or (-) */ - int vPlusCarbons, vMinusCarbons; /* (+), (-) carbons vertices */ - int ePlusCarbons, eMinusCarbons; /* edges from (+), (-) carbons vertices to super (+) or (-) */ - int vPlusMetals, vMinusMetals; /* (+), (-) carbons vertices */ - int ePlusMetals, eMinusMetals; /* edges from (+), (-) carbons vertices to super (+) or (-) */ - int eMinusHeteroToSuper; /* edge (-)vHetero-[eMinusHeteroat]-Y-[eMinusHeteroToSuper]-(-)vPlusSuper */ - int v1, v2; - int nDeltaPlus1, nDeltaMinus1, delta; - BNS_VERTEX *pvPlusSuper, *pvMinusSuper, *pvPlMn; - BNS_VERTEX *pvPlusHeteroat, *pvMinusHeteroat, *pvPlusCarbons, *pvMinusCarbons; - BNS_VERTEX *pvPlusMetals, *pvMinusMetals; - BNS_EDGE *pEdgePlusHeteroat, *pEdgeMinusHeteroat, *pEdgeMinusHeteroToSuper; - BNS_EDGE *pEdgePlusCarbons, *pEdgeMinusCarbons, *pEdgePlusMetals, *pEdgeMinusMetals; - BNS_EDGE *pEdgePlusSuper, *pEdgeMinusSuper; - - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - - - ret = 0; - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - /* (+) */ - pEdgePlusSuper = NULL; - pvPlusSuper = NULL; - if ( (k = pTCGroups->nGroup[TCG_Plus]) >= 0 && - (ePlusSuper = pTCGroups->pTCG[k].nForwardEdge) > 0 && - (vPlusSuper = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { - pEdgePlusSuper = pBNS->edge + ePlusSuper; - pvPlusSuper = pBNS->vert + vPlusSuper; - } - pEdgePlusCarbons = NULL; - pvPlusCarbons = NULL; - if ( (k = pTCGroups->nGroup[TCG_Plus_C0] ) > 0 && - (ePlusCarbons = pTCGroups->pTCG[k].nForwardEdge) > 0 && - (vPlusCarbons = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { - pEdgePlusCarbons = pBNS->edge + ePlusCarbons; - pvPlusCarbons = pBNS->vert + vPlusCarbons; - } - pEdgePlusHeteroat = NULL; - pvPlusHeteroat = NULL; - if ( (k = pTCGroups->nGroup[TCG_Plus0] ) > 0 && - (ePlusHeteroat = pTCGroups->pTCG[k].nForwardEdge) > 0 && - (vPlusHeteroat = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { - pEdgePlusHeteroat = pBNS->edge + ePlusHeteroat; - pvPlusHeteroat = pBNS->vert + vPlusHeteroat; - } - pEdgePlusMetals = NULL; - pvPlusMetals = NULL; - if ( (k = pTCGroups->nGroup[TCG_Plus_M0] ) > 0 && - (ePlusMetals = pTCGroups->pTCG[k].nForwardEdge) > 0 && - (vPlusMetals = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { - pEdgePlusMetals = pBNS->edge + ePlusMetals; - pvPlusMetals = pBNS->vert + vPlusMetals; - } - /* (-) */ - pEdgeMinusSuper = NULL; - pvMinusSuper = NULL; - if ( (k = pTCGroups->nGroup[TCG_Minus]) >= 0 && - (eMinusSuper = pTCGroups->pTCG[k].nForwardEdge) > 0 && - (vMinusSuper = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { - pEdgeMinusSuper = pBNS->edge + eMinusSuper; - pvMinusSuper = pBNS->vert + vMinusSuper; - } - pEdgeMinusCarbons = NULL; - pvMinusCarbons = NULL; - if ( (k = pTCGroups->nGroup[TCG_Minus_C0] ) > 0 && - (eMinusCarbons = pTCGroups->pTCG[k].nForwardEdge) > 0 && - (vMinusCarbons = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { - pEdgeMinusCarbons = pBNS->edge + eMinusCarbons; - pvMinusCarbons = pBNS->vert + vMinusCarbons; - } - pEdgeMinusHeteroat = NULL; - pvMinusHeteroat = NULL; - pEdgeMinusHeteroToSuper = NULL; - if ( (k = pTCGroups->nGroup[TCG_Minus0] ) > 0 && - (eMinusHeteroat = pTCGroups->pTCG[k].nForwardEdge) > 0 && - (vMinusHeteroat = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { - BNS_VERTEX *pvYMinusHetero; - BNS_EDGE *pe; - int vYMinusHetero; - pEdgeMinusHeteroat = pBNS->edge + eMinusHeteroat; - pvMinusHeteroat = pBNS->vert + vMinusHeteroat; - /* next edge toward (-)super */ - if ( pvMinusSuper ) { - vYMinusHetero = pEdgeMinusHeteroat->neighbor12 ^ vMinusHeteroat; - pvYMinusHetero = pBNS->vert + vYMinusHetero; - for ( i = 0; i < pvYMinusHetero->num_adj_edges; i ++ ) { - pe = pBNS->edge + pvYMinusHetero->iedge[i]; - if ( (pe->neighbor12 ^ vYMinusHetero) == vMinusSuper ) { - pEdgeMinusHeteroToSuper = pe; - eMinusHeteroToSuper = pe - pBNS->edge; - break; - } - } - } - } - pEdgeMinusMetals = NULL; - pvMinusMetals = NULL; - if ( (k = pTCGroups->nGroup[TCG_Minus_M0] ) > 0 && - (eMinusMetals = pTCGroups->pTCG[k].nForwardEdge) > 0 && - (vMinusMetals = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { - pEdgeMinusMetals = pBNS->edge + eMinusMetals; - pvMinusMetals = pBNS->vert + vMinusMetals; - } - /* (+/-) */ - pvPlMn = NULL; - if ( pEdgePlusSuper ) { - vPlMn = pEdgePlusSuper->neighbor12 ^ vPlusSuper; - pvPlMn = pBNS->vert + vPlMn; - } else - if ( pEdgeMinusSuper ) { - vPlMn = pEdgeMinusSuper->neighbor12 ^ vMinusSuper; - pvPlMn = pBNS->vert + vPlMn; - } - num_pos = num_neg = 0; - /***************************************************************/ - /* Positive Charges */ - /***************************************************************/ - if ( pEdgePlusHeteroat && pEdgePlusMetals ) { - /* count charges */ - for ( i = 0; i < num_at; i ++ ) { - if ( !at2[i].radical && - at2[i].charge > 0 && - (k = pVA[i].nCPlusGroupEdge-1) >= 0 ) { - v1 = pBNS->edge[k].neighbor1; - v2 = pBNS->edge[k].neighbor1 ^ pBNS->edge[k].neighbor12; - if ( v1 == vPlusHeteroat || v2 == vPlusHeteroat ) { - num_pos ++; - } - } - } - /* attempt to move (+) from heteroatoms to metal atoms */ - num_min = inchi_min( num_pos, pEdgePlusHeteroat->flow ); - - nDeltaPlus1 = pvPlusSuper->st_edge.cap - pvPlusSuper->st_edge.flow; - nDeltaMinus1 = pvPlMn->st_edge.cap - pvPlMn->st_edge.flow; - if ( num_min && !nDeltaPlus1 && !nDeltaMinus1 ) { - if ( pEdgePlusSuper ) { - pEdgePlusSuper->forbidden |= forbidden_edge_mask; - } - if ( pEdgeMinusSuper ) { - pEdgeMinusSuper->forbidden |= forbidden_edge_mask; - } - if ( pEdgePlusCarbons ) { - pEdgePlusCarbons->forbidden |= forbidden_edge_mask; - } - if ( pEdgeMinusCarbons ) { - pEdgeMinusCarbons->forbidden |= forbidden_edge_mask; - } - if ( pEdgePlusHeteroat ) { - pEdgePlusHeteroat->forbidden |= forbidden_edge_mask; - } - if ( pEdgeMinusHeteroat ) { - pEdgeMinusHeteroat->forbidden |= forbidden_edge_mask; - } - delta = 1; - for ( i = 0; i < num_min; i += delta ) { - v1 = pEdgePlusHeteroat->neighbor1; - v2 = pEdgePlusHeteroat->neighbor12 ^ v1; - pEdgePlusHeteroat->flow -= delta; - pBNS->vert[v1].st_edge.flow -= delta; - pBNS->vert[v2].st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - /* test for charhe cancellation */ - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else - if ( ret == 1 ) { - *pnTotalDelta += ret; - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - } else { - pEdgePlusHeteroat->flow += delta; - pBNS->vert[v1].st_edge.flow += delta; - pBNS->vert[v2].st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - break; - } - } - if ( pEdgePlusSuper ) { - pEdgePlusSuper->forbidden &= inv_forbidden_edge_mask; - } - if ( pEdgeMinusSuper ) { - pEdgeMinusSuper->forbidden &= inv_forbidden_edge_mask; - } - if ( pEdgePlusCarbons ) { - pEdgePlusCarbons->forbidden &= inv_forbidden_edge_mask; - } - if ( pEdgeMinusCarbons ) { - pEdgeMinusCarbons->forbidden &= inv_forbidden_edge_mask; - } - if ( pEdgePlusHeteroat ) { - pEdgePlusHeteroat->forbidden &= inv_forbidden_edge_mask; - } - if ( pEdgeMinusHeteroat ) { - pEdgeMinusHeteroat->forbidden &= inv_forbidden_edge_mask; - } - } - } - /***************************************************************/ - /* Negative Charges */ - /***************************************************************/ - if ( pEdgeMinusHeteroToSuper && pEdgeMinusMetals ) { - /* count charges */ - for ( i = 0; i < num_at; i ++ ) { - if ( !at2[i].radical && - at2[i].charge < 0 && - (k = pVA[i].nCMinusGroupEdge-1) >= 0 ) { - v1 = pBNS->edge[k].neighbor1; - v2 = pBNS->edge[k].neighbor1 ^ pBNS->edge[k].neighbor12; - if ( v1 == vMinusHeteroat || v2 == vMinusHeteroat ) { - num_neg ++; - } - } - } - if ( num_neg ) { - /* attempt to move (+) from heteroatoms to metal atoms */ - num_min = inchi_min( num_neg, pEdgeMinusHeteroToSuper->flow ); - } - - nDeltaPlus1 = pvPlusSuper->st_edge.cap - pvPlusSuper->st_edge.flow; - nDeltaMinus1 = pvPlMn->st_edge.cap - pvPlMn->st_edge.flow; - if ( num_min && !nDeltaPlus1 && !nDeltaMinus1 ) { - if ( pEdgePlusSuper ) { - pEdgePlusSuper->forbidden |= forbidden_edge_mask; - } - if ( pEdgeMinusSuper ) { - pEdgeMinusSuper->forbidden |= forbidden_edge_mask; - } - if ( pEdgePlusCarbons ) { - pEdgePlusCarbons->forbidden |= forbidden_edge_mask; - } - if ( pEdgeMinusCarbons ) { - pEdgeMinusCarbons->forbidden |= forbidden_edge_mask; - } - if ( pEdgePlusHeteroat ) { - pEdgePlusHeteroat->forbidden |= forbidden_edge_mask; - } - if ( pEdgeMinusHeteroToSuper ) { - pEdgeMinusHeteroToSuper->forbidden |= forbidden_edge_mask; - } - delta = 1; - for ( i = 0; i < num_min; i += delta ) { - v1 = pEdgeMinusHeteroToSuper->neighbor1; - v2 = pEdgeMinusHeteroToSuper->neighbor12 ^ v1; - pEdgeMinusHeteroToSuper->flow -= delta; - pBNS->vert[v1].st_edge.flow -= delta; - pBNS->vert[v2].st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - /* test for charhe cancellation */ - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else - if ( ret == 1 ) { - *pnTotalDelta += ret; - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - } else { - pEdgeMinusHeteroToSuper->flow += delta; - pBNS->vert[v1].st_edge.flow += delta; - pBNS->vert[v2].st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - break; - } - } - if ( pEdgePlusSuper ) { - pEdgePlusSuper->forbidden &= inv_forbidden_edge_mask; - } - if ( pEdgeMinusSuper ) { - pEdgeMinusSuper->forbidden &= inv_forbidden_edge_mask; - } - if ( pEdgePlusCarbons ) { - pEdgePlusCarbons->forbidden &= inv_forbidden_edge_mask; - } - if ( pEdgeMinusCarbons ) { - pEdgeMinusCarbons->forbidden &= inv_forbidden_edge_mask; - } - if ( pEdgePlusHeteroat ) { - pEdgePlusHeteroat->forbidden &= inv_forbidden_edge_mask; - } - if ( pEdgeMinusHeteroToSuper ) { - pEdgeMinusHeteroToSuper->forbidden &= inv_forbidden_edge_mask; - } - } - } -exit_function: - return ret; -} -#endif -/******************************************************************************************************/ -int RestoreCyanoGroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - BNS_EDGE *pe; - - int i, j, ret2, ret; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - Vertex v1, v2; - - EDGE_LIST CarbonChargeEdges; - - ret = 0; - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - - for ( i = 0; i < num_at && 0 <= ret; i ++ ) { - if ( at2[i].valence == 1 && - at2[i].num_H == 0 && - at2[i].chem_bonds_valence == 2 && - at2[i].charge == -1 && - at2[i].radical == 0 && - pVA[i].cNumValenceElectrons == 5 && /* terminal N(-)=, P, As, Sb, Bi */ - pVA[i].nCMinusGroupEdge > 0 && - pVA[i].nTautGroupEdge == 0 && - at2[j=at2[i].neighbor[0]].valence == 2 && - at2[j].num_H == 0 && - at2[j].chem_bonds_valence == 4 && - at2[j].charge == 0 && - at2[j].radical == 0 && - pVA[j].cNumValenceElectrons == 4 && /* C or Si or Ge or Sn or Pb */ - pVA[i].cnListIndex > 0 && - cnList[pVA[i].cnListIndex-1].bits == cn_bits_MN ) { - /* found N(-)=C= */ - pe = pBNS->edge + (pVA[i].nCMinusGroupEdge-1); /* N#N(+) triple bond edge */ - - if ( !pe->flow ) { - continue; /* wrong atom ??? Strange... */ - } - v1 = pe->neighbor1; - v2 = pe->neighbor12 ^ v1; - pe->flow --; - pBNS->vert[v1].st_edge.flow --; - pBNS->vert[v2].st_edge.flow --; - pBNS->tot_st_flow -= 2; - pe->forbidden |= forbidden_edge_mask; - - /* do not let carbon atoms get charged */ - if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { - goto exit_function; - } - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - *pnTotalDelta += ret; - } else { - pe->flow ++; - pBNS->vert[v1].st_edge.flow ++; - pBNS->vert[v2].st_edge.flow ++; - pBNS->tot_st_flow += 2; - } - RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - - pe->forbidden &= inv_forbidden_edge_mask; /* unmask the edges */ - } - } - -exit_function: - - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); - return ret; -} -/******************************************************************************************************/ -int RestoreIsoCyanoGroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ -#define INC_EDGE_LIST 16 - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms, num_failed, num_success; - BNS_EDGE *pe; - Vertex v1, v2; - - int i, j, ret2, ret, bIsCarbon; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - EdgeIndex eNMinusEdge, eNPlusEdge, eNPlusEdge1, eN34Edge; - EdgeIndex eNFlowerEdge1; - - EDGE_LIST CarbonChargeEdges, AllChargeEdges, IsoCyanoCarbonChargeEdges; - - ret = 0; - num_failed = num_success = 0; - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); /* carbon charge edges */ - AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); /* heteroatom charge edges */ - AllocEdgeList( &IsoCyanoCarbonChargeEdges, EDGE_LIST_CLEAR ); /* C in C(+)#N(+) charge edges */ - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - /* 1st attempt: take care of C(+)#N(+)- => C(-)#N(+)- and remove 2 negative charges */ - /* This would produce nDeltaCharge = 2 */ - AllocEdgeList( &CarbonChargeEdges, 2*num_at ); - for ( i = 0; i < num_at && 0 <= ret; i ++ ) { - /* accumulate edges for subsequent fixing them */ - bIsCarbon = (pVA[i].cNumValenceElectrons == 4 && pVA[i].cPeriodicRowNumber == 1); - eNFlowerEdge1 = NO_VERTEX; - if ( (eNMinusEdge = pVA[i].nCMinusGroupEdge - 1)>= 0 && - !pBNS->edge[eNMinusEdge].forbidden ) { - if ( bIsCarbon ) { - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - } else - if ( !pVA[i].cMetal && !at2[i].endpoint && at2[i].charge != -1 ) { - if ( ret = AddToEdgeList( &AllChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - } - } - if ( (eNPlusEdge = pVA[i].nCPlusGroupEdge - 1)>= 0 && - !pBNS->edge[eNPlusEdge].forbidden ) { - if ( bIsCarbon ) { - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - } else - if ( !pVA[i].cMetal && !at2[i].endpoint ) { - if ( ret = AddToEdgeList( &AllChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( pVA[i].cNumValenceElectrons == 5 && - NO_VERTEX != (eNFlowerEdge1 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge )) && - !pBNS->edge[eNFlowerEdge1].flow ) { - if ( ret = AddToEdgeList( &AllChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - } - } - } - if ( bIsCarbon && - 0 <= eNMinusEdge && - 0 <= eNPlusEdge && - at2[i].valence == 1 && - at2[i].num_H == 0 && - at2[i].radical == 0 && - !pBNS->edge[eNMinusEdge].forbidden && - pBNS->edge[eNMinusEdge].flow == 0 && - !pBNS->edge[eNPlusEdge].forbidden && - pBNS->edge[eNPlusEdge].flow == 0 && /* found terminal C(+) */ - - at2[j=at2[i].neighbor[0]].valence == 2 && - at2[j].num_H == 0 && - at2[j].radical == 0 && - pVA[j].cNumValenceElectrons == 5 && - (eNPlusEdge1 = pVA[j].nCPlusGroupEdge - 1)>= 0 && - pBNS->edge[eNPlusEdge].flow == 0 ) { /* -N(+)- */ - -#ifdef NEVER /* I have not found a good reason to do this yet */ - /* fix (+) charge on -N(+)- as much as C charges are fixed */ - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - /* fix floer edge to prevent N(V) ??? */ - if ( NO_VERTEX != (eNFlowerEdge1 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge1 )) && - !pBNS->edge[eNFlowerEdge1].flow ) { - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - } -#endif - /* - Carbon(+) Carbon(-) - ChargeStruct: ChargeStruct: - - 5(+C) 5(+C) - / // - 6(-C) 4 6(-C) 4 - \ // \\ / - 3 3 - | | - 2 2 - || || - -C1- -C1- - | | - - 3-6 is (-) Charge Edge; 4-5 is (+) Charge Edge - - To convert the left pattern to the right one: - - We need to release these charge edges and decrement - edge 3-4 flow to change charge from (+) to (-) - - */ - - /* find vertices 4 and 5 */ - v1 = pBNS->edge[eNPlusEdge].neighbor1; /* one of two vertices incident with edge 4-5 */ - v2 = pBNS->edge[eNPlusEdge].neighbor12 ^ v1; - if ( IS_BNS_VT_C_GR(pBNS->vert[v1].type) ) { - /* v1 is 5(+C) */ - Vertex tmp = v1; - v1 = v2; - v2 = tmp; - } - /* v1 should be 4, v2 - 5(+C) */ - if ( !IS_BNS_VT_CHRG_STRUCT(pBNS->vert[v1].type) || pBNS->vert[v1].num_adj_edges != 2 ) { - continue; /* mismatch */ - } - /* find edge 3-4 */ - eN34Edge = pBNS->vert[v1].iedge[pBNS->vert[v1].iedge[0] == eNPlusEdge]; - if ( pBNS->edge[eN34Edge].forbidden || !pBNS->edge[eN34Edge].flow ) { - continue; - } - /* save 3 edges: 6-3, 4-5, and 3-4 in this order */ - if ( ret = AddToEdgeList( &IsoCyanoCarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &IsoCyanoCarbonChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &IsoCyanoCarbonChargeEdges, eN34Edge, INC_EDGE_LIST ) ) { - goto exit_function; - } - } - } - /* 1st attempt: move (-) charges from heteroatoms to C(+) */ - SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &IsoCyanoCarbonChargeEdges, forbidden_edge_mask ); - for ( i = IsoCyanoCarbonChargeEdges.num_edges-3; 0 <= i; i -= 3 ) { - eNMinusEdge = IsoCyanoCarbonChargeEdges.pnEdges[i]; - eNPlusEdge = IsoCyanoCarbonChargeEdges.pnEdges[i+1]; - eN34Edge = IsoCyanoCarbonChargeEdges.pnEdges[i+2]; - - pe = pBNS->edge + eN34Edge; - pe->forbidden |= forbidden_edge_mask; - if ( !pe->flow ) { - continue; /* already done */ - } - - v1 = pe->neighbor1; - v2 = pe->neighbor12 ^ v1; - pe->flow --; - pBNS->vert[v1].st_edge.flow --; - pBNS->vert[v2].st_edge.flow --; - pBNS->tot_st_flow -= 2; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= -2 ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - *pnTotalDelta += ret; - num_success ++; - } else { - pe->flow ++; - pBNS->vert[v1].st_edge.flow ++; - pBNS->vert[v2].st_edge.flow ++; - pBNS->tot_st_flow += 2; - pe->forbidden &= inv_forbidden_edge_mask; - num_failed ++; - } - } - if ( num_failed ) { - /* relax conditions: allow all heteroatoms to change charge */ - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - for ( i = IsoCyanoCarbonChargeEdges.num_edges-3; 0 <= i; i -= 3 ) { - eNMinusEdge = IsoCyanoCarbonChargeEdges.pnEdges[i]; - eNPlusEdge = IsoCyanoCarbonChargeEdges.pnEdges[i+1]; - eN34Edge = IsoCyanoCarbonChargeEdges.pnEdges[i+2]; - - pe = pBNS->edge + eN34Edge; - pe->forbidden |= forbidden_edge_mask; - if ( !pe->flow ) { - continue; /* already done */ - } - - v1 = pe->neighbor1; - v2 = pe->neighbor12 ^ v1; - pe->flow --; - pBNS->vert[v1].st_edge.flow --; - pBNS->vert[v2].st_edge.flow --; - pBNS->tot_st_flow -= 2; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= 2 ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - *pnTotalDelta += ret; - num_success ++; - } else { - pe->flow ++; - pBNS->vert[v1].st_edge.flow ++; - pBNS->vert[v2].st_edge.flow ++; - pBNS->tot_st_flow += 2; - pe->forbidden &= inv_forbidden_edge_mask; /* let it change if it wants */ - num_failed ++; - } - } - } - RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &IsoCyanoCarbonChargeEdges, forbidden_edge_mask ); - - -exit_function: - - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &IsoCyanoCarbonChargeEdges, EDGE_LIST_FREE ); - return ret; -#undef INC_EDGE_LIST -} - -/******************************************************************************************************/ -int FixMetal_Nminus_Ominus( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ -#define INC_EDGE_LIST 16 - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - int num_failed, num_success, n, nDeltaChargeMax, nMetalCharge; - BNS_EDGE *pe; - Vertex v1, v2; - - int i, j, k, ret2, ret; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - EdgeIndex e, eNMinusEdge, eNMinusEdge1, eNMinusEdge2, eNPlusEdge2; - - EDGE_LIST AllChargeEdges; - - ret = 0; - num_failed = num_success = 0; - AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - /* attepmt #1 N#N(+)-N => N(-)=N(+)=N */ - for ( i = 0; i < num_at && 0 <= ret; i ++ ) { - if ( at2[i].valence == 1 && - at2[i].num_H == 0 && - at2[i].radical == 0 && - pVA[i].cNumValenceElectrons == 6 && /* terminal -O */ - (eNMinusEdge = pVA[i].nCMinusGroupEdge - 1)>= 0 && pBNS->edge[eNMinusEdge].flow == 1 && - !pBNS->edge[eNMinusEdge].forbidden && /* terminal O(-) */ - - at2[j=at2[i].neighbor[0]].valence == 2 && - at2[j].num_H == 0 && - at2[j].radical == 0 && - pVA[j].cNumValenceElectrons == 5 && - (eNMinusEdge1 = pVA[j].nCMinusGroupEdge - 1)>= 0 && pBNS->edge[eNMinusEdge1].flow == 1 && - !pBNS->edge[eNMinusEdge1].forbidden && - - pVA[k=at2[j].neighbor[at2[j].neighbor[0]==i]].cMetal && - - (eNMinusEdge2 = pVA[k].nCMinusGroupEdge - 1)>= 0 && - !pBNS->edge[eNMinusEdge2].forbidden && - (eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1)>= 0 && - !pBNS->edge[eNPlusEdge2].forbidden ) { - - /* found M(q)-N(-)-O(-); convert to M(q-2)-N=O */ - - /* find all charge edges to fix */ - if ( 0 == AllChargeEdges.num_edges ) { - for ( n = 0; n < num_at; n ++ ) { - if ( (e = pVA[n].nCMinusGroupEdge - 1)>= 0 && - !pBNS->edge[e].forbidden ) { - if ( ret = AddToEdgeList( &AllChargeEdges, e, num_at ) ) { - goto exit_function; - } - } - if ( (e = pVA[n].nCPlusGroupEdge - 1)>= 0 && - !pBNS->edge[e].forbidden ) { - if ( ret = AddToEdgeList( &AllChargeEdges, e, num_at ) ) { - goto exit_function; - } - if ( pVA[n].cNumValenceElectrons == 6 && - NO_VERTEX != (e = GetChargeFlowerUpperEdge( pBNS, pVA, e )) && - pBNS->edge[e].flow == 0 ) { - if ( ret = AddToEdgeList( &AllChargeEdges, e, num_at ) ) { - goto exit_function; - } - } - } - } - } - - nMetalCharge = (pBNS->edge[eNPlusEdge2].cap - pBNS->edge[eNPlusEdge2].flow) - - pBNS->edge[eNMinusEdge2].flow; - if ( nMetalCharge == 0 ) { - /* change on O is invisible; charge from N(-) goes, charge comes to Metal */ - nDeltaChargeMax = 0; - } else - if ( nMetalCharge == 2 ) { - /* charges on Metal and N disappear */ - nDeltaChargeMax = -2; - } else { - /* charge from N disappears */ - nDeltaChargeMax = -1; - } - - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - pBNS->edge[eNMinusEdge1].forbidden &= inv_forbidden_edge_mask; - pBNS->edge[eNMinusEdge2].forbidden &= inv_forbidden_edge_mask; - pBNS->edge[eNPlusEdge2].forbidden &= inv_forbidden_edge_mask; - - pe = pBNS->edge + eNMinusEdge; /* must be already fixed as a charge edge */ - - v1 = pe->neighbor1; - v2 = pe->neighbor12 ^ v1; - pe->flow --; - pBNS->vert[v1].st_edge.flow --; - pBNS->vert[v2].st_edge.flow --; - pBNS->tot_st_flow -= 2; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) /*&& nDeltaCharge == nDeltaChargeMax*/ ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - *pnTotalDelta += ret; - num_success ++; - } else { - pe->flow ++; - pBNS->vert[v1].st_edge.flow ++; - pBNS->vert[v2].st_edge.flow ++; - pBNS->tot_st_flow += 2; - num_failed ++; - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - } - ret = num_success; - -exit_function: - - AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); - - return ret; -#undef INC_EDGE_LIST -} -/******************************************************************************************************/ -int RestoreNNNgroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ -#define INC_EDGE_LIST 16 - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms, num_failed, num_success, n, nDeltaChargeMax; - BNS_EDGE *pe; - Vertex v1, v2; - - int i, j, k, ret2, ret; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - EdgeIndex eNMinusEdge, eNPlusEdge, eNMinusEdge1, eNPlusEdge1, eNMinusEdge2, eNPlusEdge2; - EdgeIndex eNFlowerEdge1, eNFlowerEdge2; - - EDGE_LIST CarbonChargeEdges, AllChargeEdges, NNNChargeEdges, CurNNNChargeEdges, AllNNNTermAtoms, AllNIIIChargeEdges; - - ret = 0; - num_failed = num_success = 0; - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &NNNChargeEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &CurNNNChargeEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &AllNNNTermAtoms, EDGE_LIST_CLEAR ); - AllocEdgeList( &AllNIIIChargeEdges, EDGE_LIST_CLEAR ); - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - /* attepmt #1 N#N(+)-N => N(-)=N(+)=N: naive approach; expecting tp move (-) from some other atom */ - for ( i = 0; i < num_at && 0 <= ret; i ++ ) { - if ( at2[i].valence == 1 && - at2[i].num_H == 0 && - at2[i].chem_bonds_valence == 3 && - at2[i].charge == 0 && - at2[i].radical == 0 && - pVA[i].cNumValenceElectrons == 5 && /* terminal N# */ - (eNMinusEdge = pVA[i].nCMinusGroupEdge - 1)>= 0 && pBNS->edge[eNMinusEdge].flow == 0 && - !pBNS->edge[eNMinusEdge].forbidden && - at2[j=at2[i].neighbor[0]].valence == 2 && - at2[j].num_H == 0 && - at2[j].chem_bonds_valence == 4 && - at2[j].charge == 1 && - at2[j].radical == 0 && - pVA[j].cNumValenceElectrons == 5 && - (eNPlusEdge = pVA[j].nCPlusGroupEdge - 1)>= 0 && pBNS->edge[eNPlusEdge].flow == 0 && - !pBNS->edge[eNPlusEdge].forbidden && - at2[k=at2[j].neighbor[at2[j].neighbor[0]==i]].valence == 2 && - at2[k].num_H == 0 && - at2[k].chem_bonds_valence == 3 && - pVA[k].cNumValenceElectrons == 5 && - (eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1)>= 0 && pBNS->edge[eNPlusEdge2].flow == 1 && - !pBNS->edge[eNPlusEdge2].forbidden && - pVA[i].cnListIndex > 0 && - cnList[pVA[i].cnListIndex-1].bits == cn_bits_MN ) { - /* found N#N(+)-N~ where the last N (at2[k]) may be charged */ - pe = pBNS->edge + pBNS->vert[i].iedge[0]; /* N#N(+) triple bond edge */ - - v1 = pe->neighbor1; - v2 = pe->neighbor12 ^ v1; - pe->flow --; - pBNS->vert[v1].st_edge.flow --; - pBNS->vert[v2].st_edge.flow --; - pBNS->tot_st_flow -= 2; - - pe->forbidden |= forbidden_edge_mask; - pBNS->edge[eNPlusEdge].forbidden |= forbidden_edge_mask; - pBNS->edge[eNPlusEdge2].forbidden |= forbidden_edge_mask; - - - if ( !CarbonChargeEdges.num_edges ) { - /* do not let carbon atoms get charged */ - AllocEdgeList( &CarbonChargeEdges, INC_EDGE_LIST ); - if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { - goto exit_function; - } - } else { - SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - } - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= 0 ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - *pnTotalDelta += ret; - num_success ++; - /* fix charges on N(-)=N(+)=N- */ - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - } else { - pe->flow ++; - pBNS->vert[v1].st_edge.flow ++; - pBNS->vert[v2].st_edge.flow ++; - pBNS->tot_st_flow += 2; - num_failed ++; - } - RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - - pe->forbidden &= inv_forbidden_edge_mask; - pBNS->edge[eNPlusEdge].forbidden &= inv_forbidden_edge_mask; - pBNS->edge[eNPlusEdge2].forbidden &= inv_forbidden_edge_mask; - } - } - - /* 2nd attempt: take care of N#N(+)-N=-...=N-N(-) */ - /* This would produce nDeltaCharge >= 2 */ - - AllChargeEdges.num_edges = 0; - AllNNNTermAtoms.num_edges = 0; - NNNChargeEdges.num_edges = 0; - AllNIIIChargeEdges.num_edges = 0; - - for ( i = 0; i < num_at && 0 <= ret; i ++ ) { - if ( (eNMinusEdge = pVA[i].nCMinusGroupEdge - 1)>= 0 && - !pBNS->edge[eNMinusEdge].forbidden ) { - if ( ret = AddToEdgeList( &AllChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - } else { - eNMinusEdge = -1; - } - if ( (eNPlusEdge = pVA[i].nCPlusGroupEdge - 1)>= 0 && - !pBNS->edge[eNPlusEdge].forbidden ) { - if ( ret = AddToEdgeList( &AllChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( pVA[i].cNumValenceElectrons == 5 && at2[i].valence == 3 && at2[i].chem_bonds_valence == 3) { - if ( ret = AddToEdgeList( &AllNIIIChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - } - /* N flower edge */ - if ( pVA[i].cNumValenceElectrons == 5 && pVA[i].cPeriodicRowNumber == 1 && - NO_VERTEX != (eNFlowerEdge1 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge )) && - pBNS->edge[eNFlowerEdge1].flow == 0 && - ( ret = AddToEdgeList( &AllChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) ) { - goto exit_function; - } - } else { - eNPlusEdge = -1; - } - - if ( 0 <= eNMinusEdge && - at2[i].valence == 1 && - at2[i].num_H == 0 && - at2[i].radical == 0 && - pVA[i].cNumValenceElectrons == 5 && /* terminal N# */ - - at2[j=at2[i].neighbor[0]].valence == 2 && - at2[j].num_H == 0 && - at2[j].radical == 0 && - pVA[j].cNumValenceElectrons == 5 && - (eNMinusEdge1 = pVA[j].nCMinusGroupEdge - 1)>= 0 && - (eNPlusEdge1 = pVA[j].nCPlusGroupEdge - 1)>= 0 && - !pBNS->edge[eNMinusEdge1].forbidden && - !pBNS->edge[eNPlusEdge1].forbidden && - - at2[k=at2[j].neighbor[at2[j].neighbor[0]==i]].valence == 2 && - at2[k].num_H == 0 && - at2[k].radical == 0 && - pVA[k].cNumValenceElectrons == 5 && - (eNMinusEdge2 = pVA[k].nCMinusGroupEdge - 1)>= 0 && - (eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1)>= 0 && - !pBNS->edge[eNMinusEdge2].forbidden && - !pBNS->edge[eNPlusEdge2].forbidden && - - pVA[i].cnListIndex > 0 && - cnList[pVA[i].cnListIndex-1].bits == cn_bits_MN ) { - /* found N#N(+)-N~ or N(-)=N-N= where the last N (at2[k]) may be charged */ - - /* 1. N(-)=N(+)=N- */ - if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ - pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { - continue; /* already good */ - } - /* accumulate terminal atoms of all other NNN */ - if ( ret = AddToEdgeList( &AllNNNTermAtoms, i, INC_EDGE_LIST ) ) { - goto exit_function; - } - /* 2. N#N(+)-N= */ - if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ - pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { - /* unfix (-) edge on terminal N# */ - if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - continue; - } - /* 3. N(-)=N-N= */ - if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 1 && /* N */ - pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { - /* unfix (+) edge on middle N */ - if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - continue; - } - /* 4. N#N(+)-N(-)- */ - if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ - pBNS->edge[eNMinusEdge2].flow == 1 && pBNS->edge[eNPlusEdge2].flow == 1 /* N(-) */ ) { - /* unfix (-) edge on the 1st and 3rd N */ - if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) { - goto exit_function; - } - continue; - } - /* 5. N#N(+)-N(+)# */ - if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ - pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 0 /* N(+) */ ) { - /* unfix (-) edge on the 1st and (+) edge on the 3rd N */ - if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) { - goto exit_function; - } - continue; - } - } - } - /* try to fix each NNN */ - for ( n = AllNNNTermAtoms.num_edges-1; 0 <= n; n -- ) { - i = AllNNNTermAtoms.pnEdges[n]; - eNMinusEdge = pVA[i].nCMinusGroupEdge - 1; - /*eNPlusEdge = pVA[i].nCPlusGroupEdge - 1;*/ - j=at2[i].neighbor[0]; - eNMinusEdge1 = pVA[j].nCMinusGroupEdge - 1; - eNPlusEdge1 = pVA[j].nCPlusGroupEdge - 1; - k=at2[j].neighbor[at2[j].neighbor[0]==i]; - eNMinusEdge2 = pVA[k].nCMinusGroupEdge - 1; - eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1; - /*SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );*/ - /* 1. N(-)=N(+)=N- */ - if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ - pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { - - RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge ); - RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge1 ); - RemoveFromEdgeListByValue( &NNNChargeEdges, eNPlusEdge1 ); - RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge2 ); - RemoveFromEdgeListByValue( &NNNChargeEdges, eNPlusEdge2 ); - - pe = NULL; - } else /* 2. N#N(+)-N= */ - if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ - pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { - /* decrement triple bond on terminal N# */ - pe = pBNS->edge + pBNS->vert[i].iedge[0]; - } else - /* 3. N(-)=N-N= */ - if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 1 && /* N */ - pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { - /* decrement flow on (+) charge edge of the middle =N- */ - pe = pBNS->edge + eNPlusEdge1; - } else - /* 4. N#N(+)-N(-)- */ - if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ - pBNS->edge[eNMinusEdge2].flow == 1 && pBNS->edge[eNPlusEdge2].flow == 1 /* N(-) */ ) { - /* decrement triple bond on terminal N# */ - pe = pBNS->edge + pBNS->vert[i].iedge[0]; - } else - /* 5. N#N(+)-N(+)# */ - if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ - pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 0 /* N(+) */ ) { - /* decrement triple bond on terminal N# */ - pe = pBNS->edge + pBNS->vert[i].iedge[0]; - } else { - pe = NULL; /* unknown case */ - } - if ( pe ) { - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &NNNChargeEdges, forbidden_edge_mask ); - - v1 = pe->neighbor1; - v2 = pe->neighbor12 ^ v1; - pe->flow --; - pBNS->vert[v1].st_edge.flow --; - pBNS->vert[v2].st_edge.flow --; - pBNS->tot_st_flow -= 2; - - pe->forbidden |= forbidden_edge_mask; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) /*&& nDeltaCharge <= 2*/ ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - *pnTotalDelta += ret; - num_success ++; - /* fix charges on N(-)=N(+)=N- */ - RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge ); - RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge1 ); - RemoveFromEdgeListByValue( &NNNChargeEdges, eNPlusEdge1 ); - RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge2 ); - RemoveFromEdgeListByValue( &NNNChargeEdges, eNPlusEdge2 ); - } else { - pe->flow ++; - pBNS->vert[v1].st_edge.flow ++; - pBNS->vert[v2].st_edge.flow ++; - pBNS->tot_st_flow += 2; - num_failed ++; - } - pe->forbidden &= inv_forbidden_edge_mask; - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - } - - /* 3rd attempt */ - - /* - AllChargeEdges.num_edges = 0; - AllNNNTermAtoms.num_edges = 0; - NNNChargeEdges.num_edges = 0; - */ - for ( i = 0; i < num_at && 0 <= ret; i ++ ) { - - eNMinusEdge = pVA[i].nCMinusGroupEdge - 1; - /*eNPlusEdge = pVA[i].nCPlusGroupEdge - 1;*/ - - if ( 0 <= eNMinusEdge && - at2[i].valence == 1 && - at2[i].num_H == 0 && - at2[i].radical == 0 && - pVA[i].cNumValenceElectrons == 5 && /* terminal N# */ - - at2[j=at2[i].neighbor[0]].valence == 2 && - at2[j].num_H == 0 && - at2[j].radical == 0 && - pVA[j].cNumValenceElectrons == 5 && - (eNMinusEdge1 = pVA[j].nCMinusGroupEdge - 1)>= 0 && - (eNPlusEdge1 = pVA[j].nCPlusGroupEdge - 1)>= 0 && - !pBNS->edge[eNMinusEdge1].forbidden && - !pBNS->edge[eNPlusEdge1].forbidden && - - at2[k=at2[j].neighbor[at2[j].neighbor[0]==i]].valence == 2 && - at2[k].num_H == 0 && - at2[k].radical == 0 && - pVA[k].cNumValenceElectrons == 5 && - (eNMinusEdge2 = pVA[k].nCMinusGroupEdge - 1)>= 0 && - (eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1)>= 0 && - !pBNS->edge[eNMinusEdge2].forbidden && - !pBNS->edge[eNPlusEdge2].forbidden && - - pVA[i].cnListIndex > 0 && - cnList[pVA[i].cnListIndex-1].bits == cn_bits_MN ) { - - /* found N#N(+)-N~ or N(-)=N-N= where the last N (at2[k]) may be charged */ - NNNChargeEdges.num_edges = 0; - - eNFlowerEdge1 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge1 ); - eNFlowerEdge2 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge2 ); - - /* 1. N(-)=N(+)=N- */ - if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ - pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { - /* fix charges on N(-)=N(+)=N- */ - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) { - goto exit_function; - } - continue; /* already good */ - } - /* 2. N#N(+)-N= */ - if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ - pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { - /* unfix (-) edge on terminal N# */ - if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) { - goto exit_function; - } - pe = pBNS->edge + pBNS->vert[i].iedge[0]; - nDeltaChargeMax = 0; - nDeltaChargeMax = (num_failed && !num_success && pStruct->nNumRemovedProtonsMobHInChI > 0)? 2 : 0; - } else - /* 3. N(-)=N-N= */ - if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 1 && /* N */ - pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { - /* unfix (+) edge on middle N */ - if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( NO_VERTEX != eNFlowerEdge1 && - ( ret = AddToEdgeList( &NNNChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) ) { - goto exit_function; - } - /* decrement flow on (+) charge edge of the middle =N- */ - pe = pBNS->edge + eNPlusEdge1; - nDeltaChargeMax = 2; - } else - /* 4. N#N(+)-N(-)- */ - if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ - pBNS->edge[eNMinusEdge2].flow == 1 && pBNS->edge[eNPlusEdge2].flow == 1 /* N(-) */ ) { - /* unfix (-) edge on the 1st and 3rd N */ - if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) { - goto exit_function; - } - /* decrement triple bond on terminal N# */ - pe = pBNS->edge + pBNS->vert[i].iedge[0]; - nDeltaChargeMax = 0; - } else - /* 5. N#N(+)-N(+)# */ - if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ - pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ - pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 0 /* N(+) */ ) { - /* unfix (-) edge on the 1st and (+) edge on the 3rd N */ - if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) { - goto exit_function; - } - /* decrement triple bond on terminal N# */ - pe = pBNS->edge + pBNS->vert[i].iedge[0]; - nDeltaChargeMax = 0; - } else { - continue; - } - - if ( NO_VERTEX != eNFlowerEdge1 && !pBNS->edge[eNFlowerEdge1].flow ) { - if ( ret = AddToEdgeList( &NNNChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - } - if ( NO_VERTEX != eNFlowerEdge2 && !pBNS->edge[eNFlowerEdge2].flow ) { - if ( ret = AddToEdgeList( &NNNChargeEdges, eNFlowerEdge2, INC_EDGE_LIST ) ) { - goto exit_function; - } - } - - v1 = pe->neighbor1; - v2 = pe->neighbor12 ^ v1; - pe->flow --; - pBNS->vert[v1].st_edge.flow --; - pBNS->vert[v2].st_edge.flow --; - pBNS->tot_st_flow -= 2; - - pe->forbidden |= forbidden_edge_mask; - - if ( !CarbonChargeEdges.num_edges ) { - /* do not let carbon atoms get charged */ - AllocEdgeList( &CarbonChargeEdges, INC_EDGE_LIST ); - if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { - goto exit_function; - } - } else { - SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - } - SetForbiddenEdgeMask( pBNS, &NNNChargeEdges, forbidden_edge_mask ); - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= nDeltaChargeMax ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - *pnTotalDelta += ret; - num_success ++; - /* fix charges on N(-)=N(+)=N- */ - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) { - goto exit_function; - } - } else { - pe->flow ++; - pBNS->vert[v1].st_edge.flow ++; - pBNS->vert[v2].st_edge.flow ++; - pBNS->tot_st_flow += 2; - num_failed ++; - } - RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &NNNChargeEdges, forbidden_edge_mask ); - pe->forbidden &= inv_forbidden_edge_mask; - /*pBNS->edge[eNPlusEdge].forbidden &= inv_forbidden_edge_mask;*/ /* BC: array index out of range */ - } - } - RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - - -exit_function: - - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &NNNChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &CurNNNChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &AllNNNTermAtoms, EDGE_LIST_FREE ); - AllocEdgeList( &AllNIIIChargeEdges, EDGE_LIST_FREE ); - return ret; -#undef INC_EDGE_LIST -} -/******************************************************************************************************/ -int EliminateNitrogen5Val3Bonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ - int i, j, k, bForbiddenCarbonCharges, ret2, ret; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - EDGE_LIST CarbonChargeEdges; - - ret = 0; - bForbiddenCarbonCharges = 0; - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - - /* forbid creation of other N(V) atoms */ - /* fix single bonds to metals */ - for ( i = 0; i < num_at; i ++ ) { - if ( pVA[i].cNumValenceElectrons == 5 && - 0 <= (k = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 )) && - 1 == pBNS->edge[k].flow) { - pBNS->edge[k].forbidden |= forbidden_edge_mask; - } else - if ( pVA[i].cMetal ) { - for ( j = 0; j < at2[i].valence; j ++ ) { - if ( BOND_TYPE_SINGLE == (at2[i].bond_type[j] & BOND_TYPE_MASK) ) { - pBNS->edge[pBNS->vert[i].iedge[j]].forbidden |= forbidden_edge_mask; - } - } - } - } - - /*------------------------------------------------------------------------------ - (+) single line => flow = 0 (+)-(Y)=(+)super - 01 // double line => flow = 1 fix-> 01 // <-- fix - 1 --- 0 1 === 0 - \\ // edge eij connects vertices i < j: \ / 02 - 12 2 02 <--- edge number: e02 connects vertices v0 12 2(..) <- double 'radical' - | v0 and v2 | - =N= vertex N has number i =N= - | | - --------------------------------------------------------------------------------*/ - for ( i = 0; i < num_at; i ++ ) { - if ( pVA[i].cNumValenceElectrons == 5 && at2[i].valence == 3 && - at2[i].chem_bonds_valence == 5 && !at2[i].charge && !at2[i].radical && - !(at2[i].endpoint || pStruct->endpoint && pStruct->endpoint[i]) && pVA[i].cnListIndex > 0 && - cnList[pVA[i].cnListIndex-1].bits == cn_bits_NPN && - pVA[i].nCPlusGroupEdge > 0 ) { - - Vertex v, v0 = NO_VERTEX, v1 = NO_VERTEX, v2 = NO_VERTEX; - EdgeIndex iePlus, ie, ie12 = NO_VERTEX, ie02, ie01; - BNS_VERTEX *pv0, *pv1, *pv2 = NULL; - BNS_EDGE *pePlus, *pe, *pe12 = NULL, *pe02 = NULL, *pe01 = NULL; - Vertex vPathStart, vPathEnd; - int nPathLen; - int nDeltaH, nDeltaCharge, nNumVisitedAtoms; - - iePlus = pVA[i].nCPlusGroupEdge - 1; - pePlus = pBNS->edge + iePlus; - - v0 = IS_BNS_VT_C_GR( pBNS->vert[pePlus->neighbor1].type )? - (pePlus->neighbor1 ^ pePlus->neighbor12) : pePlus->neighbor1; - pv0 = pBNS->vert + v0; - for ( j = 0; j < pv0->num_adj_edges; j ++ ) { - ie = pv0->iedge[j]; - if ( ie == iePlus ) { - continue; - } - pe = pBNS->edge + ie; - if ( pe->flow == 1 && v2 == NO_VERTEX ) { - /* 0 - 2, edge 02 */ - v2 = pe->neighbor12 ^ v0; - pv2 = pBNS->vert + v2; - ie02 = ie; - pe02 = pe; - } else - if ( pe->flow == 0 && v1 == NO_VERTEX ) { - /* 0 - 1, edge 01 */ - v1 = pe->neighbor12 ^ v0; - pv1 = pBNS->vert + v2; - ie01 = ie; - pe01 = pe; - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - } - if ( v1 == NO_VERTEX || v2 == NO_VERTEX ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - for ( j = 0; j < pv2->num_adj_edges; j ++ ) { - ie = pv2->iedge[j]; - pe = pBNS->edge + ie; - v = pe->neighbor12 ^ v2; - if ( v == v0 || v == i ) { - continue; - } else - if ( v == v1 && pe->flow == 1 ) { - /* 1 - 2, edge 12 */ - ie12 = ie; - pe12 = pe; - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - } - if ( ie12 == NO_VERTEX ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - /* rearrange cap and flow, forbid 2 edges */ - pe01->flow = 1; - pe12->flow = 0; - pe02->flow = 0; - pv2->st_edge.flow -= 2; - pBNS->tot_st_flow -= 2; - pePlus->forbidden |= forbidden_edge_mask; - pe01->forbidden |= forbidden_edge_mask; - - if ( !bForbiddenCarbonCharges ) { - if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { - goto exit_function; - } - bForbiddenCarbonCharges = 1; - } - - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret == 1 && vPathEnd == v2 && vPathStart == v2 && nDeltaCharge <= (pVA[i].cNumBondsToMetal? 2:0) ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - } else { - pe01->flow = 0; - pe12->flow = 1; - pe02->flow = 1; - pv2->st_edge.flow += 2; - pBNS->tot_st_flow += 2; - } - pePlus->forbidden &= inv_forbidden_edge_mask; - pe01->forbidden &= inv_forbidden_edge_mask; - - if ( ret < 0 ) { - goto exit_function; - } else - if ( ret ) { - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - } - } - } -exit_function: - /* allow creation of other N(V) atoms */ - for ( i = 0; i < num_at; i ++ ) { - if ( pVA[i].cNumValenceElectrons == 5 && - 0 <= (k = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 )) && - 1 == pBNS->edge[k].flow && (pBNS->edge[k].forbidden & forbidden_edge_mask) ) { - pBNS->edge[k].forbidden &= inv_forbidden_edge_mask; - } else - if ( pVA[i].cMetal ) { - for ( j = 0; j < at2[i].valence; j ++ ) { - if ( BOND_TYPE_SINGLE == (at2[i].bond_type[j] & BOND_TYPE_MASK) ) { - pBNS->edge[pBNS->vert[i].iedge[j]].forbidden &= inv_forbidden_edge_mask; - } - } - } - } - RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); - return ret; -} -/******************************************************************************************************/ -int Convert_SIV_to_SVI(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ - int i, j, k, neigh, bForbiddenCarbonCharges, nFlowerEdge, delta, ret2, ret; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - EDGE_LIST CarbonChargeEdges, FlowerEdgesList; - - ret = 0; - bForbiddenCarbonCharges = 0; - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &FlowerEdgesList, EDGE_LIST_CLEAR ); - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - - /* forbid creation of other S(IV) atoms */ - /* fix single bonds to metals and (N(IV), flow=1), (S(IV), flow=0) */ - for ( i = 0; i < num_at; i ++ ) { - if ( (pVA[i].cNumValenceElectrons == 5 /* N(IV)*/ || pVA[i].cNumValenceElectrons == 6 /* S(VI)*/) && - 0 <= (k = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 )) && - !pBNS->edge[k].forbidden && - 6 == pVA[i].cNumValenceElectrons + pBNS->edge[k].flow ) { - - pBNS->edge[k].forbidden |= forbidden_edge_mask; - if ( ret = AddToEdgeList( &FlowerEdgesList, k, 64 )) { - goto exit_function; - } - } else - if ( pVA[i].cMetal ) { - for ( j = 0; j < at2[i].valence; j ++ ) { - if ( BOND_TYPE_SINGLE == (at2[i].bond_type[j] & BOND_TYPE_MASK) ) { - - pBNS->edge[k=pBNS->vert[i].iedge[j]].forbidden |= forbidden_edge_mask; - if ( ret = AddToEdgeList( &FlowerEdgesList, k, 64 )) { - goto exit_function; - } - } - } - } else - /* fix bonds to neighbors of S(IV) if they are not O,S,Se,Te with 2 or more bonds */ - /* exactly same if(..) as below */ - if ( pVA[i].cNumValenceElectrons == 6 && at2[i].valence == 4 && - at2[i].chem_bonds_valence == 4 && !at2[i].charge && !at2[i].radical && - !at2[i].endpoint && pVA[i].cnListIndex > 0 && - cnList[pVA[i].cnListIndex-1].bits == cn_bits_NPN && - 0 <= (nFlowerEdge = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 ) ) && - pBNS->edge[nFlowerEdge].flow > 0 ) { - - for ( j = 0; j < at2[i].valence; j ++ ) { - neigh = at2[i].neighbor[j]; - if ( pVA[neigh].cNumValenceElectrons != 6 && at2[neigh].valence > 1 ) { - k = pBNS->vert[i].iedge[j]; - if ( !pBNS->edge[k].forbidden ) { - if ( ret = AddToEdgeList( &FlowerEdgesList, k, 64 )) { - goto exit_function; - } - pBNS->edge[k].forbidden |= forbidden_edge_mask; - } - } - } - } - } - /*------------------------------------------------------------------------------ - example: struct #301, - | | disconnected porphyrin with four -SO3(-) - -S- => =S= - | | - - -------------------------------------------------------------------------------*/ - - /*------------------------------------------------------------------------------- - found: super(+)=(Y) super(+)=(Y) - \ \ - (+) single line => flow = 0 (+) (+) - 01 // double line => flow = 1 fix-> 01 // 01 // - 1 === 0 triple line => flow = 2 (.)1 --- 0(.) ---> 1 --- 0 - \ / edge eij connects vertices i 0 && - cnList[pVA[i].cnListIndex-1].bits == cn_bits_NPN && - /* 01 is nFlowerEdge */ - 0 <= (nFlowerEdge = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 ) ) && - pBNS->edge[nFlowerEdge].flow > 0 ) { - - Vertex v1 = NO_VERTEX, v2 = NO_VERTEX; - BNS_VERTEX *pv1, *pv2; - BNS_EDGE *pe; - Vertex vPathStart, vPathEnd; - int nPathLen; - int nDeltaH, nDeltaCharge, nNumVisitedAtoms; - - if ( !bForbiddenCarbonCharges ) { - if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { - goto exit_function; - } - bForbiddenCarbonCharges = 1; - } - - delta = 1; - pe = pBNS->edge + nFlowerEdge; /* edge 01 */ - pv1 = pBNS->vert + (v1 = pe->neighbor1); /* vertex 0 */ - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); /* vertex 1 */ - - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret == 1 && - (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && - nDeltaCharge <= (pVA[i].cNumBondsToMetal? 2:0) ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - } else { - pe->forbidden &= inv_forbidden_edge_mask; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - if ( ret < 0 ) { - goto exit_function; - } else - if ( ret ) { - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - /* store the fixed edge to unfix it upon exit */ - if ( ret = AddToEdgeList( &FlowerEdgesList, nFlowerEdge, 64 )) { - goto exit_function; - } - } - } - } -exit_function: - RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); - RemoveForbiddenEdgeMask( pBNS, &FlowerEdgesList, forbidden_edge_mask ); - AllocEdgeList( &FlowerEdgesList, EDGE_LIST_FREE ); - return ret; -} -/****************************************************************************************************** - - - =N(+)=O =N-O(-) - => - M(q) M(q+2) - -*******************************************************************************************************/ -int PlusFromDB_N_DB_O_to_Metal(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ - int i, j, k, n, bForbiddenCarbonCharges, delta, ret2, ret, num_NO, num_M; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - EDGE_LIST CarbonChargeEdges, NO_ChargeEdgeList, NO_EdgeList; - - Vertex v1, v2; - BNS_VERTEX *pv1, *pv2; - BNS_EDGE *pe; - Vertex vPathStart, vPathEnd; - int nPathLen; - int nDeltaH, nDeltaCharge, nNumVisitedAtoms; - - - if ( !pTCGroups->num_metal_atoms ) - return 0; - - ret = 0; - bForbiddenCarbonCharges = 0; - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); /* all charges */ - AllocEdgeList( &NO_ChargeEdgeList, EDGE_LIST_CLEAR ); /* charges to be changed */ - AllocEdgeList( &NO_EdgeList, EDGE_LIST_CLEAR ); /* N(+)=O edges */ - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - num_NO = num_M = 0; - /* forbid creation of other S(IV) atoms */ - /* fix single bonds to metals and (N(IV), flow=1), (S(IV), flow=0) */ - for ( i = 0; i < num_at; i ++ ) { - if ( !pVA[i].cMetal ) { - if ( (k = pVA[i].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { - if ( ret = AddToEdgeList( &CarbonChargeEdges, k, 64 ) ) { - goto exit_function; - } - } - if ( (k = pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { - if ( ret = AddToEdgeList( &CarbonChargeEdges, k, 64 ) ) { - goto exit_function; - } - } - } else { - num_M ++; - } - /* - if ( pVA[i].cMetal ) { - if ( (k = pVA[i].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { - if ( ret = AddToEdgeList( &NO_ChargeEdgeList, k, 64 ) ) { - goto exit_function; - } - } - if ( (k = pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { - if ( ret = AddToEdgeList( &NO_ChargeEdgeList, k, 64 ) ) { - goto exit_function; - } - } - } else - */ - if ( !pVA[i].cMetal && - pVA[i].cNumValenceElectrons == 6 && - at2[i].charge == 0 && !at2[i].num_H && - 1 == at2[i].valence && 2 == at2[i].chem_bonds_valence && - pVA[j=at2[i].neighbor[0]].cNumValenceElectrons == 5 && - at2[j].charge == 1 && !at2[j].num_H && - 2 == at2[j].valence && 4 == at2[j].chem_bonds_valence ) { - /* found =N(+)=O */ - if ( (k = pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden /* O */ && - (n = pVA[j].nCPlusGroupEdge -1) >= 0 && !pBNS->edge[j].forbidden /* N */ ) { - if ( (ret = AddToEdgeList( &NO_ChargeEdgeList, k, 64 ) ) || - (ret = AddToEdgeList( &NO_ChargeEdgeList, n, 64 ) ) ) { - goto exit_function; - } - k = pBNS->vert[i].iedge[0]; /* N(+)=O bond */ - if ( !pBNS->edge[k].forbidden ) { - if ( ret = AddToEdgeList( &NO_EdgeList, k, 64 ) ) { - goto exit_function; - } - num_NO ++; - } - } - } - } - if ( num_M && num_NO ) { - SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - SetForbiddenEdgeMask( pBNS, &NO_EdgeList, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &NO_ChargeEdgeList, forbidden_edge_mask ); - /* now only N(+), O(-) and metal charges are allowed to change */ - for ( i = 0; i < NO_EdgeList.num_edges; i ++ ) { - k = NO_EdgeList.pnEdges[i]; - delta = 1; - pe = pBNS->edge + k; /* edge N(+)=O */ - pv1 = pBNS->vert + (v1 = pe->neighbor1); /* vertex 0 */ - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); /* vertex 1 */ - - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret == 1 && - (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && - nDeltaCharge == 0 ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - } else { - pe->forbidden &= inv_forbidden_edge_mask; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - if ( ret < 0 ) { - goto exit_function; - } - } - } -exit_function: - RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &NO_EdgeList, forbidden_edge_mask ); - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &NO_EdgeList, EDGE_LIST_FREE ); - AllocEdgeList( &NO_ChargeEdgeList, EDGE_LIST_FREE ); - return ret; -} -/******************************************************************************************************/ -int MoveMobileHToAvoidFixedBonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ - int ret2, ret; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int nNumFixedEdges, nNumAdjEdges; - - ret = 0; - - if ( pTCGroups->num_tgroups ) { - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } -#if ( FIND_RING_SYSTEMS == 1 ) - ret2 = MarkRingSystemsInp( at2, num_at, 0 ); - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } -#endif - /* --- forbidden edges --- */ - ret2 = SetForbiddenEdges( pBNS, at2, num_at, forbidden_edge_mask ); - if ( ret2 < 0 ) { - ret2 = -(ret + 1); - } - nNumFixedEdges = ret2; - ret = AdjustTgroupsToForbiddenEdges2( pBNS, at2, pVA, num_at, forbidden_edge_mask ); - nNumAdjEdges = ret; - if ( ret ) { - pBNS->edge_forbidden_mask |= forbidden_edge_mask; - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else { - *pnTotalDelta += ret; - } - } - if ( nNumFixedEdges || nNumAdjEdges ) { - /* removes this edge mask from ALL edges */ - RemoveForbiddenBondFlowBits( pBNS, forbidden_edge_mask ); - } - } - -exit_function: - - return ret; -} -/******************************************************************************************************/ -/* Find and eliminate cases when Mobile H endpoint has radical on it (typical for wrong P(VI)(=O)3OH */ -int RemoveRadFromMobileHEndpoint(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ - int i, num_fixes, tot_num_fixes = 0; - - int ret2, ret; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - - int itg, j, k, n, m; - Vertex vtg1, endpoint0=NO_VERTEX, endpoint1, endpoint2, centerpoint; - Vertex centerpoint_found=NO_VERTEX; - BNS_VERTEX *ptg1, *pEndp0=NULL, *pEndp1, *pEndp2, *pCentp, *pCentp_found, *pEndp2_found=NULL; - BNS_EDGE *etg0=NULL, *etg1, *etg2, *ecp0, *ecp1, *ecp2; - BNS_EDGE *etg1_found=NULL, *ecp0_found=NULL, *ecp1_found=NULL, *ecp2_found=NULL; - int tgroup_number, num_endpoints; - - ret = 0; - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - while ( pBNS->tot_st_cap > pBNS->tot_st_flow && pTCGroups->num_tgroups ) { - num_fixes = 0; - for ( itg = 0; itg < pTCGroups->num_tgroups; itg ++ ) { - pCentp_found=NULL; - tgroup_number = pTCGroups->pTCG[itg].ord_num; - vtg1 = pTCGroups->pTCG[itg].nVertexNumber; /* taut group vertex index */ - ptg1 = pBNS->vert + vtg1; /* taut group vertex */ - num_endpoints = pTCGroups->pTCG[itg].num_edges; - for ( i = 0; i < num_endpoints; i ++ ) { - etg0 = pBNS->edge + ptg1->iedge[i]; /* edge from t-group to endpoint */ - endpoint0 = etg0->neighbor12 ^ vtg1; /* taut endpoint vertex index */ - pEndp0 = pBNS->vert + endpoint0; /* taut endpoint vertex (possible location of mobile H */ - if ( pEndp0->st_edge.cap > pEndp0->st_edge.flow ) { - /* radical endpoint1 has been detected */ - /* find a 1-3 centerpoint that has two or more endpoints */ - /* connected to the t-group vertex by edges with flow>0 and */ - /* to the centerpoint by edges with flow = 0 */ - /* after that: (1) increment etg1 flow to eliminate radical */ - /* (2) increment flow on one of the two other edges to the t-group */ - /* (3) increment st_cap on the found centerpoint */ - /* (4) rerun the BNS and re-create the structure */ - break; - } - } - if ( i == num_endpoints ) { - continue; - } - if ( i < num_endpoints ) { - /* tautomeric endpoint found; traverse its t-group edges */ - for ( j = 0; j < num_endpoints; j ++ ) { - if ( i == j ) { - continue; /* avoid the already found radical endpoint */ - } - etg1 = pBNS->edge + ptg1->iedge[j]; /* another edge from t-group to another endpoinr */ - endpoint1 = etg1->neighbor12 ^ vtg1; /* another endpoint vertex index */ - pEndp1 = pBNS->vert + endpoint1; /* another endpoint vertex */ - if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) { - continue; /* one more radical-endpoint! What is going on here??? */ - } - if ( !etg1->flow ) { - continue; /* avoid enpoints that do not have an attachment */ - } - if ( !(pEndp1->type & BNS_VERT_TYPE_ENDPOINT) ) { - continue; /* should not happen */ - } - /* traverse endpoint1 edges to find a single bond connecting it to the centerpoint */ - for ( k = 0; k < at2[endpoint1].valence; k ++ ) { - ecp1 = pBNS->edge + pEndp1->iedge[k]; - if ( ecp1->flow ) { - continue; - } - centerpoint = ecp1->neighbor12 ^ endpoint1; - pCentp = pBNS->vert + centerpoint; - /* traverse centerpoint edges to find a single bond to the 2nd endpoint */ - for ( n = 0; n < at2[centerpoint].valence; n ++ ) { - ecp2 = pBNS->edge + pCentp->iedge[n]; - if ( ecp2->flow ) { - continue; - } - endpoint2 = ecp2->neighbor12 ^ centerpoint; - if ( endpoint2 <= endpoint1 || !pVA[endpoint2].nTautGroupEdge ) { - continue; /* don't go back: neighbors are in order of ascending ord. numbers */ - } - pEndp2 = pBNS->vert + endpoint2; - if ( !(pEndp2->type & BNS_VERT_TYPE_ENDPOINT) ) { - continue; - } - etg2 = pBNS->edge + pVA[endpoint2].nTautGroupEdge - 1; - if ( !etg2->flow || (etg2->neighbor12 ^ endpoint2) != vtg1 ) { - continue; - } - /* we have found the path: - Endp1 Endp1 - etg1 // \ ecp1 etg1 / \\ ecp1 - etg0 // \ etg0 / \\ - Endp0-----tg1 Centp --> Endp0=====tg1 Centp - ^ \\ / \\ / - radical | etg2 \\ / ecp2 etg2 \\ / ecp2 - Endp2 Endp2 - */ - - /* compare centerpoints */ - if ( !pCentp_found || - /* try to avoid carbons */ - (pVA[centerpoint].cNumValenceElectrons != 4 || - pVA[centerpoint].cPeriodicRowNumber != 1) && - pVA[centerpoint_found].cNumValenceElectrons == 4 && - pVA[centerpoint_found].cPeriodicRowNumber == 1 || - /* try a better non-carbon */ - (pVA[centerpoint].cNumValenceElectrons != 4 || - pVA[centerpoint].cPeriodicRowNumber != 1 ) && - (at[centerpoint].valence > at[centerpoint_found].valence || - at[centerpoint].valence == at[centerpoint_found].valence && - at[centerpoint].el_number > at[centerpoint_found].el_number) ) { - - pCentp_found = pCentp; - etg1_found = etg1; - ecp1_found = ecp1; - centerpoint_found = centerpoint; - break; - } - } - } - } - } - if ( pCentp_found ) { - /* ---- (1) */ - etg0->flow ++; - pEndp0->st_edge.flow ++; - /* ---- (2) */ - etg1_found->flow --; - /* ---- (3) */ - ecp1_found->flow ++; - /* ---- (4) */ - pCentp_found->st_edge.flow ++; - pCentp_found->st_edge.cap ++; - - pBNS->tot_st_flow += 2; - pBNS->tot_st_cap += 1; - pCentp_found = NULL; - num_fixes ++; - tot_num_fixes ++; /* #1 Mob-H */ - continue; - } - - /* 2nd attempt: increment flow in centerpoint---radical_endpint edge */ - if ( i < num_endpoints ) { - /* tautomeric endpoint found; traverse its t-group edges */ - for ( j = 0; j < num_endpoints; j ++ ) { - if ( i == j ) { - continue; /* avoid the found radical endpoint */ - } - etg1 = pBNS->edge + ptg1->iedge[j]; - endpoint1 = etg1->neighbor12 ^ vtg1; - pEndp1 = pBNS->vert + endpoint1; /* another endpoint */ - if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) { - continue; /* one more radical-endpoint! What is going on here??? */ - } - if ( !etg1->flow ) { - continue; /* avoid enpoints that do not have an attachment */ - } - if ( !(pEndp1->type & BNS_VERT_TYPE_ENDPOINT) ) { - continue; /* should not happen */ - } - /* traverse endpoint1 edges to find the edge connecting it to the centerpoint */ - for ( k = 0; k < at2[endpoint1].valence; k ++ ) { - ecp1 = pBNS->edge + pEndp1->iedge[k]; - if ( ecp1->flow ) { - continue; - } - centerpoint = ecp1->neighbor12 ^ endpoint1; - pCentp = pBNS->vert + centerpoint; - if ( pCentp->type & BNS_VERT_TYPE_ENDPOINT ) { - continue; /* do not set another endpoint's valence = an unusual value */ - } - - /* traverse centerpoint edges to find edge connecting it to the endpoint0 */ - ecp2 = NULL; - pEndp2 = NULL; - for ( n = 0; n < at2[centerpoint].valence; n ++ ) { - ecp0 = pBNS->edge + pCentp->iedge[n]; - if ( ecp0->flow ) { - endpoint2 = ecp0->neighbor12 ^ centerpoint; - if ( (pBNS->vert[endpoint2].type & BNS_VERT_TYPE_ENDPOINT) ) { - continue; /* ignore endpoint2 if it is tautomeric endpoint */ - } - /* check whether ecp0 is stereogenic: if it is then we cannot decrement its flow */ - for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[centerpoint].sb_parity[m]; m ++ ) { - if ( at[centerpoint].sb_ord[m] == n ) { - endpoint2 = NO_VERTEX; - break; - } - } - if ( endpoint2 == NO_VERTEX ) { - continue; - } - pEndp2 = pBNS->vert + endpoint2; /* found */ - ecp2 = ecp0; - break; - } - } - for ( n = 0; n < at[centerpoint].valence; n ++ ) { - ecp0 = pBNS->edge + pCentp->iedge[n]; - if ( ecp0->flow ) { - continue; - } - if ( endpoint0 != (ecp0->neighbor12 ^ centerpoint) ) { - continue; - } - /* Found: - Endp2(not endpoint) Endp2(not radical) - || | - ||ecp2 |ecp2 - ecp0 || ecp1 ecp0 | ecp1 - Endp0----Centp----Endp1 Endp0====Centp----Endp1 - ^ \ / --> \ / - radical | \ / \ / - \ / \ / - etg0 \ / etg1 etg0 \ / etg1 - \ / \ / - tg1 tg1 - - */ - - /* compare centerpoints */ - if ( !pCentp_found || - /* try to avoid carbons */ - (pVA[centerpoint].cNumValenceElectrons != 4 || - pVA[centerpoint].cPeriodicRowNumber != 1) && - pVA[centerpoint_found].cNumValenceElectrons == 4 && - pVA[centerpoint_found].cPeriodicRowNumber == 1 || - /* try a better non-carbon */ - (pVA[centerpoint].cNumValenceElectrons != 4 || - pVA[centerpoint].cPeriodicRowNumber != 1 ) && - (at[centerpoint].valence > at[centerpoint_found].valence || - at[centerpoint].valence == at[centerpoint_found].valence && - at[centerpoint].el_number > at[centerpoint_found].el_number) ) { - - pCentp_found = pCentp; - etg1_found = etg1; - ecp0_found = ecp0; - centerpoint_found = centerpoint; - pEndp2_found = pEndp2; - ecp2_found = ecp2; - break; - } - } - } - } - } - if ( pCentp_found ) { - ecp0_found->flow ++; - if ( ecp0_found->cap < ecp0_found->flow ) { - ecp0_found->cap = ecp0_found->flow; - } - pEndp0->st_edge.flow ++; - if ( pEndp2_found && ecp2_found ) { - ecp2_found->flow --; - pEndp2_found->st_edge.flow --; - } else { - /* Endp2 not found */ - pCentp_found->st_edge.flow ++; - pCentp_found->st_edge.cap ++; - pBNS->tot_st_flow += 2; /* radical elimination */ - pBNS->tot_st_cap += 1; - } - - pCentp_found = NULL; - num_fixes ++; - tot_num_fixes ++; /* #2 Mob-H */ - continue; - } - /* 3rd attempt: find =C= and move radical to it */ - if ( i < num_endpoints ) { - int jj, delta, bNotFixed = 1; - Vertex vPathStart, vPathEnd, v1, v2; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - for ( jj = 0; jj < num_at && bNotFixed; jj ++ ) { - if ( at2[i].endpoint ) { - continue; - } - if ( 2 == at2[jj].valence && pBNS->vert[jj].st_edge.cap == pBNS->vert[jj].st_edge.flow && - 4 == pVA[jj].cNumValenceElectrons && - !(ecp0 = pBNS->edge + pBNS->vert[jj].iedge[0])->forbidden && - !(ecp1 = pBNS->edge + pBNS->vert[jj].iedge[1])->forbidden && - 1 == ecp0->flow && 1 == ecp1->flow && - !at2[(int)at2[i].neighbor[0]].sb_parity[0] && - !at2[(int)at2[i].neighbor[1]].sb_parity[0] ) { - /* found =C=; make a radical and try to cancel the two radicals */ - k = ecp0->neighbor12 ^ jj; - if ( at2[k].endpoint ) { - ecp0 = ecp1; - k = ecp0->neighbor12 ^ jj; - if ( at2[k].endpoint ) { - continue; - } - } - delta = 1; - /* decrement C valence */ - pBNS->vert[jj].st_edge.flow -= delta; - pBNS->vert[jj].st_edge.cap -= delta; - /* decrement bond order */ - ecp0->flow -= delta; - /* reflect the changes in at2[k] to make it a radical */ - pBNS->vert[k].st_edge.flow -= delta; - pBNS->tot_st_cap -= delta; - pBNS->tot_st_flow -= 2*delta; - - v1 = endpoint0; - v2 = k; - - ret2 = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret2 == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { - ret2 = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret2 > 0 ) { - num_fixes ++; - tot_num_fixes ++; /* #3 Mob-H */ - pBNS->vert[jj].st_edge.cap += delta; /* create radical on =C- */ - pBNS->tot_st_cap += delta; - pCentp_found = NULL; - bNotFixed = 0; /* exit from the cycle */ - break; - } - } else { - /* failed */ - pBNS->vert[jj].st_edge.flow += delta; - pBNS->vert[jj].st_edge.cap += delta; - /* decrement bond order */ - ecp0->flow += delta; - /* reflect the changes in at2[k] to make it a radical */ - pBNS->vert[k].st_edge.flow += delta; - pBNS->tot_st_cap += delta; - pBNS->tot_st_flow += 2*delta; - } - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - } - } - } - } - if ( !num_fixes ) { - break; - } - } - ret = tot_num_fixes; -exit_function: - pStruct->at = at; - memcpy( at2, at, len_at*sizeof(at2[0])); - return ret; -} -/******************************************************************************************************/ -/* Find and eliminate cases when Mobile H endpoint has radical on it (typical for wrong P(VI)(=O)3OH */ -int RemoveRadFromMobileHEndpointFixH(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ -#define IS_C(x) (NO_VERTEX != x && pVA[x].cNumValenceElectrons == 4 && pVA[x].cPeriodicRowNumber == 1) - int i, num_fixes, tot_num_fixes = 0; - - int ret2, ret; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - EDGE_LIST ChargeEdgeList, BondEdgeList; - int itg, j, k, n, m, num_endp; - Vertex endpoint0=NO_VERTEX, endpoint1, endpoint2=NO_VERTEX, centerpoint; - Vertex centerpoint_found=NO_VERTEX, endpoint2_found=NO_VERTEX; - BNS_VERTEX *pEndp0=NULL, *pEndp1, *pEndp2, *pCentp, *pCentp_found, *pEndp2_found=NULL; - BNS_EDGE *ecp0, *ecp1, *ecp2, *ecp0_found=NULL, *ecp1_found=NULL, *ecp2_found=NULL; - int tgroup_number, num_endpoints; - - ret = 0; - - if ( pStruct->iMobileH != TAUT_NON ) - return ret; - - AllocEdgeList( &ChargeEdgeList, EDGE_LIST_CLEAR); - AllocEdgeList( &BondEdgeList, EDGE_LIST_CLEAR); - - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - while ( pBNS->tot_st_cap > pBNS->tot_st_flow && pStruct->ti.num_t_groups ) { - int iEndpoint = 0; - num_fixes = 0; - for ( itg = 0; itg < pStruct->ti.num_t_groups; iEndpoint += num_endpoints, itg ++ ) { - pCentp_found=NULL; - tgroup_number = pStruct->ti.t_group[itg].nGroupNumber; - num_endpoints = pStruct->ti.t_group[itg].nNumEndpoints; - for ( i = 0; i < num_endpoints; i ++ ) { - endpoint0 = pStruct->ti.nEndpointAtomNumber[iEndpoint+i]; - pEndp0 = pBNS->vert + endpoint0; /* taut endpoint vertex (possible location of mobile H */ - if ( pEndp0->st_edge.cap > pEndp0->st_edge.flow ) { - /* radical endpoint1 has been detected */ - /* find a 1-3 centerpoint that has two or more endpoints */ - /* connected to the t-group vertex by edges with flow>0 and */ - /* to the centerpoint by edges with flow = 0 */ - /* after that: (1) increment etg1 flow to eliminate radical */ - /* (2) increment flow on one of the two other edges to the t-group */ - /* (3) increment st_cap on the found centerpoint */ - /* (4) rerun the BNS and re-create the structure */ - break; - } - } - - /* 2nd attempt: increment flow in centerpoint---radical_endpoint edge */ - pCentp_found = NULL; - if ( i < num_endpoints ) { - /* tautomeric endpoint found; traverse its t-group edges */ - for ( j = 0; j < num_endpoints; j ++ ) { - if ( i == j ) { - continue; /* avoid the found radical endpoint */ - } - endpoint1 = pStruct->ti.nEndpointAtomNumber[iEndpoint+j]; - pEndp1 = pBNS->vert + endpoint1; /* another endpoint */ - - if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) { - continue; /* one more radical-endpoint! What is going on here??? */ - } - if ( !at2[endpoint1].num_H && at2[endpoint1].charge != -1 ) { - continue; /* avoid enpoints that do not have an attachment */ - } - if ( !pStruct->endpoint[endpoint1] ) { - continue; /* should not happen */ - } - /* traverse endpoint1 edges to find the edge connecting it to the centerpoint */ - for ( k = 0; k < pEndp1->num_adj_edges; k ++ ) { - ecp1 = pBNS->edge + pEndp1->iedge[k]; - if ( ecp1->flow ) { - continue; - } - centerpoint = ecp1->neighbor12 ^ endpoint1; - if ( centerpoint >= pBNS->num_atoms ) { - break; /* no more edges to atoms */ - } - pCentp = pBNS->vert + centerpoint; - if ( pStruct->endpoint[centerpoint] ) { - continue; /* do not set another endpoint's valence = an unusual value */ - } - /* traverse centerpoint edges to find edge connecting it to the endpoint0 */ - /* 1. Find a double bond to an endpoint */ - ecp2 = NULL; - pEndp2 = NULL; - for ( n = 0, num_endp = 0; n < at2[centerpoint].valence; n ++ ) { - ecp0 = pBNS->edge + pCentp->iedge[n]; - if ( ecp0->flow ) { - endpoint2 = ecp0->neighbor12 ^ centerpoint; - if ( pStruct->endpoint[endpoint2] /* ??? */ ) { - continue; - } - /* check whether ecp0 is stereogenic: if it is then we cannot decrement its flow */ - for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[centerpoint].sb_parity[m]; m ++ ) { - if ( at[centerpoint].sb_ord[m] == n ) { - endpoint2 = NO_VERTEX; - break; - } - } - if ( endpoint2 == NO_VERTEX ) { - continue; - } - pEndp2 = pBNS->vert + endpoint2; - ecp2 = ecp0; - break; - } - } - if ( !ecp2 ) { - continue; - } - /* 2. Find a single bond to an endpoint0 */ - for ( n = 0, num_endp = 0; n < at2[centerpoint].valence; n ++ ) { - ecp0 = pBNS->edge + pCentp->iedge[n]; - if ( ecp0->flow ) { - continue; - } - if ( endpoint0 != (ecp0->neighbor12 ^ centerpoint) ) { - continue; - } - /* Found: - Endp2 Endp2(not radical) - || | - ||ecp2 |ecp2 - ecp0 || ecp1 ecp0 | ecp1 - Endp0----Centp----Endp1 Endp0====Centp----Endp1 - ^ \ / --> \ / - radical | \ / \ / - \ / \ / - etg0 \ / etg1 etg0 \ / etg1 - \ / \ / - tg1 tg1 - - */ - - /* compare centerpoints */ - if ( !pCentp_found || - /* try to avoid carbons */ - (pVA[centerpoint].cNumValenceElectrons != 4 || - pVA[centerpoint].cPeriodicRowNumber != 1) && - pVA[centerpoint_found].cNumValenceElectrons == 4 && - pVA[centerpoint_found].cPeriodicRowNumber == 1 || - /* try a better non-carbon */ - (pVA[centerpoint].cNumValenceElectrons != 4 || - pVA[centerpoint].cPeriodicRowNumber != 1 ) && - (at[centerpoint].valence > at[centerpoint_found].valence || - at[centerpoint].valence == at[centerpoint_found].valence && - at[centerpoint].el_number > at[centerpoint_found].el_number) ) { - - pCentp_found = pCentp; - ecp0_found = ecp0; - centerpoint_found = centerpoint; - pEndp2_found = pEndp2; - ecp2_found = ecp2; - break; - } - } - } - } - } - /* decrement st_flow, st_cap on Endp2; decrement flow on ecp2; decrement st_flow on Centp */ - /* result: radicals on Endp0 and Centp => run BNS */ - if ( pCentp_found ) { - /* make ecp0 a double bond, make ecp2 a single bond, remove radical */ - ecp0_found->flow ++; - if ( ecp0_found->cap < ecp0_found->flow ) { - ecp0_found->cap = ecp0_found->flow; - } - pEndp0->st_edge.flow ++; - if ( pEndp2_found && ecp2_found ) { - ecp2_found->flow --; - pEndp2_found->st_edge.flow --; - } else { - /* Endp2 not found: only make ecp0 a double bond */ - pCentp_found->st_edge.flow ++; - pCentp_found->st_edge.cap ++; - pBNS->tot_st_flow += 2; /* radical elimination */ - pBNS->tot_st_cap += 1; - } - - pCentp_found = NULL; - num_fixes ++; /* #2 */ - tot_num_fixes ++; - continue; - } - /* 1st attempt */ - pCentp_found = NULL; - if ( i < num_endpoints ) { - /* tautomeric endpoint found; traverse its t-group edges */ - for ( j = 0; j < num_endpoints; j ++ ) { - if ( i == j ) { - continue; /* avoid the found radical endpoint */ - } - endpoint1 = pStruct->ti.nEndpointAtomNumber[iEndpoint+j]; - pEndp1 = pBNS->vert + endpoint1; /* another endpoint */ - if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) { - continue; /* one more radical-endpoint! What is going on here??? */ - } - if ( !at2[endpoint1].num_H && at2[endpoint1].charge != -1 ) { - continue; /* avoid enpoints that do not have an attachment */ - } - if ( !pStruct->endpoint[endpoint1] ) { - continue; /* should not happen */ - } - /* traverse endpoint1 edges to find the edge connecting it to the centerpoint */ - for ( k = 0; k < pEndp1->num_adj_edges; k ++ ) { - ecp1 = pBNS->edge + pEndp1->iedge[k]; - if ( ecp1->flow ) { - continue; - } - centerpoint = ecp1->neighbor12 ^ endpoint1; - if ( centerpoint >= pBNS->num_atoms ) { - break; - } - pCentp = pBNS->vert + centerpoint; - /* traverse centerpoint edges to find the 2nd endpoint */ - for ( n = 0, num_endp = 0; n < pCentp->num_adj_edges; n ++ ) { - ecp2 = pBNS->edge + pCentp->iedge[n]; - if ( ecp2->flow ) { - continue; - } - endpoint2 = ecp2->neighbor12 ^ centerpoint; - if ( endpoint2 >= pBNS->num_atoms ) { - break; - } - if ( !pStruct->endpoint[endpoint2] ) { - continue; - } - pEndp2 = pBNS->vert + endpoint2; - - if ( at2[endpoint2].num_H || at2[endpoint1].charge == -1 ) { - continue; - } - - /* we have found the path: - - Endp1 has no attachments, Endp2 has. - - Endp1 Endp1 - etg1 // \ ecp1 etg1 / \\ ecp1 - etg0 // \ etg0 / \\ - Endp0-----tg1 Centp --> Endp0=====tg1 Centp - ^ \\ / \\ / - radical | etg2 \\ / ecp2 etg2 \\ / ecp2 - Endp2 Endp2 - */ - - /* compare centerpoints */ - if ( !pCentp_found || - /* try to avoid carbons */ - (pVA[centerpoint].cNumValenceElectrons != 4 || - pVA[centerpoint].cPeriodicRowNumber != 1) && - pVA[centerpoint_found].cNumValenceElectrons == 4 && - pVA[centerpoint_found].cPeriodicRowNumber == 1 || - /* try a better non-carbon */ - (pVA[centerpoint].cNumValenceElectrons != 4 || - pVA[centerpoint].cPeriodicRowNumber != 1 ) && - (at[centerpoint].valence > at[centerpoint_found].valence || - at[centerpoint].valence == at[centerpoint_found].valence && - at[centerpoint].el_number > at[centerpoint_found].el_number) ) { - - pCentp_found = pCentp; - ecp1_found = ecp1; - centerpoint_found = centerpoint; - break; - } - } - } - } - } - if ( pCentp_found ) { - /* create a new radical at the centerpoint and try to cancel them */ - int delta = 1, ret3; - Vertex vPathStart, vPathEnd, v1, v2; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - - pCentp_found->st_edge.cap += delta; - pBNS->tot_st_cap += delta; - - v1 = pCentp_found - pBNS->vert; - v2 = pEndp0 - pBNS->vert; - ret3 = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret3 == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge % 2 == 0 ) { - ret3 = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret3 > 0 ) { - num_fixes ++; - tot_num_fixes ++; /* #1 */ - pCentp_found = NULL; - continue; - } - } else { - pCentp_found->st_edge.cap -= delta; - pBNS->tot_st_cap -= delta; - } - if ( ret3 < 0 ) { - ret = ret3; - goto exit_function; - } - } - /*---------------------------------------------------------------------------------------- - 3rd attempt: add radical to keep - ============== u,f=>unfixed, fixed edges (N electrons)%2 - (-) (-) (-) | - e0/ \\ e1 => u/ \\u => // \ v - / \\ ecp1 ecp2 / \\ u f // \ - C--X* Y(-)--C==Z C--X* Y(-)--C*--Z --X(-) Y===C---Z* - rad. endp not rad. endp rad not rad. endp not - Endp0 Endp1 endp endp to endp endp endp - Endp2 cancel - - Note: endpoints X and Y may belong to different t-groups - ----------------------------------------------------------------------------------------*/ - pCentp_found = NULL; - if ( i < num_endpoints ) { - int e0, e1; - if ( (e0=pVA[endpoint0].nCMinusGroupEdge-1)<0 || pBNS->edge[e0].forbidden ) { - continue; /* no negative charge on Endp0 is possible */ - } - /* a radical-tautomeric endpoint found; traverse all endpoints */ - for ( j = 0; j < pStruct->ti.nNumEndpoints; j ++ ) { - if ( iEndpoint+i == j ) { - continue; /* avoid the found radical endpoint */ - } - - endpoint1 = pStruct->ti.nEndpointAtomNumber[j]; - pEndp1 = pBNS->vert + endpoint1; /* another endpoint */ - - if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) { - continue; /* one more radical-endpoint! What is going on here??? */ - } - if ( ((e1=pVA[endpoint1].nCMinusGroupEdge-1)<0 || !pBNS->edge[e1].flow) || pBNS->edge[e1].forbidden ) { - continue; /* no negative charge on Endp1 */ - } - if ( !pStruct->endpoint[endpoint1] ) { - continue; /* should not happen */ - } - /* traverse endpoint1 edges to find the edge connecting it to the centerpoint */ - for ( k = 0; k < pEndp1->num_adj_edges; k ++ ) { - ecp1 = pBNS->edge + pEndp1->iedge[k]; /* e1C */ - if ( ecp1->flow || ecp1->forbidden ) { - continue; - } - centerpoint = ecp1->neighbor12 ^ endpoint1; - if ( centerpoint >= pBNS->num_atoms ) { - break; /* no more edges to atoms */ - } - pCentp = pBNS->vert + centerpoint; - if ( pStruct->endpoint[centerpoint] ) { - continue; /* do not set another endpoint's valence = an unusual value */ - } - /* traverse centerpoint edges to find edge connecting it to the endpoint0 */ - /* 1. Find a double bond to a not endpoint */ - ecp2 = NULL; - pEndp2 = NULL; - for ( n = 0, num_endp = 0; n < pCentp->num_adj_edges; n ++ ) { - ecp0 = pBNS->edge + pCentp->iedge[n]; - if ( ecp0->flow && !ecp0->forbidden ) { - endpoint2 = ecp0->neighbor12 ^ centerpoint; - if ( endpoint2 >= pBNS->num_atoms || pStruct->endpoint[endpoint2] ) { - continue; - } - /* check whether ecp0 is stereogenic: if it is then we cannot decrement its flow */ - for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[centerpoint].sb_parity[m]; m ++ ) { - if ( at[centerpoint].sb_ord[m] == n ) { - endpoint2 = NO_VERTEX; - break; - } - } - if ( endpoint2 == NO_VERTEX ) { - continue; - } - pEndp2 = pBNS->vert + endpoint2; - ecp2 = ecp0; /* e2C */ - break; - } - } - if ( !ecp2 ) - continue; - /* compare centerpoints */ - if ( !pCentp_found || - /* try to find carbons */ - !IS_C(endpoint2_found) && IS_C(endpoint2) || - IS_C(endpoint2_found) && IS_C(endpoint2) && - !IS_C(centerpoint_found) && IS_C(centerpoint) ) { - - pCentp_found = pCentp; - centerpoint_found = centerpoint; - endpoint2_found = endpoint2; - ecp2_found = ecp2; - ecp1_found = ecp1; - ecp0_found = pBNS->edge + e0; - break; - } - } - } - } - /* decrement st_flow, st_cap on Endp2; decrement flow on ecp2; decrement st_flow on Centp */ - /* result: radicals on Endp0 and Centp => run BNS */ - if ( pCentp_found ) { - Vertex vPathStart, vPathEnd, v1, v2; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - int delta; - - Vertex vEndp0 = ecp0_found->neighbor1; - Vertex vEndp1 = ecp1_found->neighbor12 ^ centerpoint_found; - Vertex vEndp2 = ecp2_found->neighbor12 ^ centerpoint_found; - BNS_EDGE *pe0 = ecp0_found; - BNS_EDGE *pe1 = pBNS->edge + (pVA[vEndp1].nCMinusGroupEdge - 1); - pEndp1 = pBNS->vert + vEndp1; - pEndp2 = pBNS->vert + vEndp2; - pCentp = pCentp_found; - if ( !ChargeEdgeList.num_alloc ) { - for ( n = 0; n < pStruct->num_atoms; n ++ ) { - if ( (k = pVA[n].nCMinusGroupEdge)>= 0 && !pBNS->edge[k].forbidden && - (ret = AddToEdgeList( &ChargeEdgeList, k, pStruct->num_atoms ) ) ) { - goto exit_function; - } - if ( (k = pVA[n].nCPlusGroupEdge)>= 0 && !pBNS->edge[k].forbidden && - (ret = AddToEdgeList( &ChargeEdgeList, k, pStruct->num_atoms ) ) ) { - goto exit_function; - } - } - } - if ( !BondEdgeList.num_alloc ) { - for ( n = 0; n < pBNS->num_bonds; n ++ ) { - if ( (ret = AddToEdgeList( &BondEdgeList, n, pBNS->num_bonds ) ) ) { - goto exit_function; - } - } - } - /* fix all bonds and charges */ - SetForbiddenEdgeMask( pBNS, &ChargeEdgeList, forbidden_edge_mask ); - SetForbiddenEdgeMask( pBNS, &BondEdgeList, forbidden_edge_mask ); - /* prepare flow for testing */ - delta = 1; - ecp2_found->flow -= delta; - pCentp->st_edge.flow -= delta; - pEndp2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - /* unfix edges to be changed */ - pe0->forbidden &= inv_forbidden_edge_mask; - pe1->forbidden &= inv_forbidden_edge_mask; - ecp1_found->forbidden &= inv_forbidden_edge_mask; - - pBNS->tot_st_cap += delta; - - v1 = vEndp0; - v2 = centerpoint_found; - ret2 = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret2 == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { - ret2 = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret2 > 0 ) { - num_fixes ++; - tot_num_fixes ++; /* #3 */ - pCentp_found = NULL; - } - } else { - /* roll back */ - ecp2_found->flow += delta; - pCentp->st_edge.flow += delta; - pEndp2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - RemoveForbiddenEdgeMask( pBNS, &ChargeEdgeList, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &BondEdgeList, forbidden_edge_mask ); - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - if ( !pCentp_found ) - continue; - } - - } - if ( !num_fixes ) { - break; - } - } - -/************ again ***********************************************************/ - while ( pBNS->tot_st_cap > pBNS->tot_st_flow && pStruct->ti.num_t_groups ) { - int iEndpoint = 0; - num_fixes = 0; - for ( itg = 0; itg < pStruct->ti.num_t_groups; iEndpoint += num_endpoints, itg ++ ) { - pCentp_found=NULL; - tgroup_number = pStruct->ti.t_group[itg].nGroupNumber; - num_endpoints = pStruct->ti.t_group[itg].nNumEndpoints; - for ( i = 0; i < num_endpoints; i ++ ) { - endpoint0 = pStruct->ti.nEndpointAtomNumber[iEndpoint+i]; - pEndp0 = pBNS->vert + endpoint0; /* taut endpoint vertex (possible location of mobile H */ - if ( pEndp0->st_edge.cap > pEndp0->st_edge.flow ) { - /* radical endpoint1 has been detected */ - /* find a 1-3 centerpoint that has two or more endpoints */ - /* connected to the t-group vertex by edges with flow>0 and */ - /* to the centerpoint by edges with flow = 0 */ - /* after that: (1) increment etg1 flow to eliminate radical */ - /* (2) increment flow on one of the two other edges to the t-group */ - /* (3) increment st_cap on the found centerpoint */ - /* (4) rerun the BNS and re-create the structure */ - break; - } - } - /* 4th attempt */ - if ( i < num_endpoints ) { - /* tautomeric endpoint found; traverse its t-group edges */ - pEndp2_found = NULL; - for ( j = 0; j < pEndp0->num_adj_edges; j ++ ) { - ecp0 = pBNS->edge + pEndp0->iedge[j]; - centerpoint = ecp0->neighbor12 ^ endpoint0; - if ( centerpoint >= pBNS->num_atoms || ecp0->flow || pStruct->endpoint[centerpoint] ) { - continue; /* ignore non-single bonds, orig. InChI endpoints, and fictitious atoms */ - } - pCentp = pBNS->vert + centerpoint; - for ( k = 0; k < pCentp->num_adj_edges; k ++ ) { - ecp1 = pBNS->edge + pCentp->iedge[k]; - endpoint1 = ecp1->neighbor12 ^ centerpoint; - if ( endpoint1 >= pBNS->num_atoms || !ecp1->flow || pStruct->endpoint[endpoint1] ) { - continue; /* ignore single bonds, orig. InChI endpoints, and fictitious atoms */ - } - pEndp1 = pBNS->vert + endpoint1; - if ( endpoint1 == endpoint0 || pEndp1->st_edge.cap != pEndp1->st_edge.flow ) { - continue; /* ignore radicals */ - } - if ( !pEndp2_found || - /* try to find carbons */ - !IS_C(endpoint2_found) && IS_C(endpoint1) || - IS_C(endpoint2_found) && IS_C(endpoint1) && - !IS_C(centerpoint_found) && IS_C(centerpoint) ) { - pEndp2_found = pEndp1; - pCentp_found = pCentp; - endpoint2_found = endpoint1; - centerpoint_found = centerpoint; - ecp1_found = ecp0; - ecp2_found = ecp1; - } - } - } - if ( pEndp2_found ) { - /* move radical from pEndp0 to pEndp2 */ - pEndp0->st_edge.flow ++; - ecp1_found->flow ++; - ecp2_found->flow --; - pEndp2_found->st_edge.flow --; - pEndp2_found = NULL; - pCentp_found = NULL; - num_fixes ++; /* #4 */ - tot_num_fixes ++; - continue; - } - } - } - if ( !num_fixes ) { - break; - } - } - - - ret = tot_num_fixes; -exit_function: - AllocEdgeList( &ChargeEdgeList, EDGE_LIST_FREE); - AllocEdgeList( &BondEdgeList, EDGE_LIST_FREE); - - pStruct->at = at; - memcpy( at2, at, len_at*sizeof(at2[0])); - return ret; -#undef IS_C -} -/************************************************************************************************/ -/* move (+) charges to >N- and other centerpoints */ -int MoveChargeToMakeCenerpoints(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ - int i, j, neigh, num_endpoints, tg_group=0, num_success; - int ret2, ret, delta; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - - /* for RunBnsTestOnce */ - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - - BNS_EDGE *pEdgePlus, *pEdgeMinus; - Vertex v1p, v2p, v1m, v2m; - BNS_VERTEX *pv1p, *pv2p, *pv1m, *pv2m; - - ret = 0; - num_success = 0; - /* to simplify, prepare new at[] from pBNS */ - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - - for ( i = 0; i < num_at; i ++ ) { - if ( pVA[i].cNumValenceElectrons != 4 && /* not C, Si, Ge */ - !pVA[i].cMetal && !pVA[i].nTautGroupEdge && - !at2[i].num_H && at2[i].valence >= 3 && - at2[i].valence == at2[i].chem_bonds_valence && - !at2[i].charge && pVA[i].nCPlusGroupEdge > 0 && - is_centerpoint_elem( at2[i].el_number ) ) { - for ( j = 0, num_endpoints = 0; j < at2[i].valence; j ++ ) { - neigh = at2[i].neighbor[j]; - if ( at2[neigh].endpoint ) { - if ( !num_endpoints ) { - tg_group = at2[neigh].endpoint; - } else - if ( tg_group != at2[neigh].endpoint ) { - break; /* not a centerpoint */ - } - num_endpoints ++; - } - } - if ( j == at2[i].valence && num_endpoints > 1 ) { - /* found possible centerpoint */ - pEdgePlus = pBNS->edge + (pVA[i].nCPlusGroupEdge-1); - pEdgeMinus = (pVA[i].nCMinusGroupEdge > 0)? pBNS->edge + (pVA[i].nCMinusGroupEdge-1) : NULL; - if ( pEdgePlus->flow + (pEdgeMinus? pEdgeMinus->flow : 0) != 1 ) { - continue; - } - v1p = pEdgePlus->neighbor1; - v2p = pEdgePlus->neighbor12 ^ v1p; - pv1p = pBNS->vert + v1p; - pv2p = pBNS->vert + v2p; - if ( pEdgeMinus ) { - v1m = pEdgeMinus->neighbor1; - v2m = pEdgeMinus->neighbor12 ^ v1m; - pv1m = pBNS->vert + v1m; - pv2m = pBNS->vert + v2m; - } else { - v1m = NO_VERTEX; - v2m = NO_VERTEX; - pv1m = NULL; - pv2m = NULL; - } - ret = 0; - /* set new flow to run BNS Search */ - if ( delta = pEdgePlus->flow ) { - /* positive charge <=> flow=0 on (=) edge */ - pEdgePlus->flow -= delta; - pv1p->st_edge.flow -= delta; - pv2p->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - pEdgePlus->forbidden |= forbidden_edge_mask; - if ( pEdgeMinus ) { - pEdgeMinus->forbidden |= forbidden_edge_mask; - } - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret == 1 && (vPathEnd == v1p && vPathStart == v2p || - vPathEnd == v2p && vPathStart == v1p) && - nDeltaCharge == -1 /* charge moving to this atom disappers*/ ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else - if ( ret == 1 ) { - *pnTotalDelta += ret; - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - } else { - ret = 0; - pEdgePlus->flow += delta; - pv1p->st_edge.flow += delta; - pv2p->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - pEdgePlus->forbidden &= inv_forbidden_edge_mask; - if ( pEdgeMinus ) { - pEdgeMinus->forbidden &= inv_forbidden_edge_mask; - } - } else - if ( pEdgeMinus && (delta == pEdgeMinus->flow) && pEdgePlus->flow == 0 ) { - /* positive charge <=> flow=0 on (=) edge and flow=0 on (-) edge */ - pEdgeMinus->flow -= delta; - pv1m->st_edge.flow -= delta; - pv2m->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - pEdgePlus->forbidden |= forbidden_edge_mask; - pEdgeMinus->forbidden |= forbidden_edge_mask; - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret == 1 && (vPathEnd == v1m && vPathStart == v2m || - vPathEnd == v2m && vPathStart == v1m) && - nDeltaCharge == -1 /* charge moving to this atom disappers*/ ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else - if ( ret == 1 ) { - *pnTotalDelta += ret; - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - } else { - ret = 0; - pEdgeMinus->flow += delta; - pv1m->st_edge.flow += delta; - pv2m->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - pEdgePlus->forbidden &= inv_forbidden_edge_mask; - pEdgeMinus->forbidden &= inv_forbidden_edge_mask; - } - if ( ret ) { - num_success ++; - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - } - } - } - } - ret = num_success; -exit_function: - return ret; -} -#endif +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include + +/* */ +/*#define CHECK_WIN32_VC_HEAP*/ + +#include "mode.h" + +#if ( READ_INCHI_STRING == 1 ) + +#include "ichister.h" +#include "ichirvrs.h" + + + +/****************************************************************************/ +void CopyAt2St( inp_ATOM *at, inp_ATOM_STEREO * st, int num_atoms ) +{ + int i; + for ( i = 0; i < num_atoms; i ++ ) + { + if ( at[i].p_parity ) + { + memcpy( st[i].p_orig_at_num, at[i].p_orig_at_num, sizeof(st[0].p_orig_at_num) ); + st[i].p_parity = at[i].p_parity; + } + if ( at[i].sb_parity[0] ) + { + memcpy( st[i].sb_ord, at[i].sb_ord, sizeof(st[0].sb_ord) ); + memcpy( st[i].sb_parity, at[i].sb_parity, sizeof(st[0].sb_parity) ); + memcpy( st[i].sn_ord, at[i].sn_ord, sizeof(st[0].sn_ord) ); + memcpy( st[i].sn_orig_at_num, at[i].sn_orig_at_num, sizeof(st[0].sn_orig_at_num) ); + } + } +} + + +/****************************************************************************/ +void CopySt2At( inp_ATOM *at, inp_ATOM_STEREO * st, int num_atoms ) +{ + int i; + if ( !st ) + { + return; + } + + for ( i = 0; i < num_atoms; i ++ ) + { + if ( st[i].p_parity ) + { + memcpy( at[i].p_orig_at_num, st[i].p_orig_at_num, sizeof(at[0].p_orig_at_num) ); + at[i].p_parity = st[i].p_parity; + } + if ( st[i].sb_parity[0] ) + { + memcpy( at[i].sb_ord, st[i].sb_ord, sizeof(st[0].sb_ord) ); + memcpy( at[i].sb_parity, st[i].sb_parity, sizeof(at[0].sb_parity) ); + memcpy( at[i].sn_ord, st[i].sn_ord, sizeof(at[0].sn_ord) ); + memcpy( at[i].sn_orig_at_num, st[i].sn_orig_at_num, sizeof(at[0].sn_orig_at_num) ); + } + } +} + + +/****************************************************************************/ +int RestoreAtomConnectionsSetStereo( StrFromINChI *pStruct, int iComponent, int iAtNoOffset, INChI *pInChI, INChI *pInChIMobH) +{ + inp_ATOM *at = NULL; + inp_ATOM_STEREO * st = NULL; + int num_atoms, i, jv, jn, n_vertex, n_neigh, num_H, parity; + int nNumDeletedH=0, iDeletedH=0, idelH1, idelH2, ret = 0, len; + int num_stereo_bonds, num_stereo_centers, num_stereo_bonds2, num_stereo_centers2; + INChI_Stereo *pStereo = NULL, *pStereo2 = NULL; + AT_NUMB nCumulene[MAX_CUMULENE_LEN+2]; + + num_atoms = pInChI->nNumberOfAtoms; + if ( num_atoms <= 0 ) + { + return 0; + } + + INCHI_HEAPCHK + + /* atoms */ + pStruct->at = at = (inp_ATOM *) inchi_calloc ( num_atoms, sizeof(pStruct->at[0]) ); + if ( !at ) + { + ret = RI_ERR_ALLOC; + goto exit_function; + } + + pStruct->num_atoms = num_atoms; + + /* charge */ + pStruct->charge = pInChI->nTotalCharge; + + /* elements, terminal atoms H */ + for ( i = 0; i < num_atoms; i ++ ) + { + at[i].el_number = pInChI->nAtom[i]; + if ( -1==get_element_chemical_symbol(UCINT pInChI->nAtom[i], at[i].elname ) ) + { + ret = RI_ERR_PROGR; + goto exit_function; + } + at[i].orig_at_number = iAtNoOffset + i+1; + at[i].orig_compt_at_numb = i + 1; + at[i].component = iComponent + 1; + num_H = pInChI->nNum_H[i]; + /* --- pInChI->nNum_H_fixed[i] was added to pInChI->nNum_H[i] --- + if ( pInChI->nNum_H_fixed ) { + num_H += pInChI->nNum_H_fixed[i]; + } + */ + at[i].num_H = num_H; + } + + INCHI_HEAPCHK + + /* connections */ + for ( i = 1, n_vertex = pInChI->nConnTable[0]-1; i < pInChI->lenConnTable; i ++ ) + { + if ( (n_neigh = pInChI->nConnTable[i]-1) < n_vertex ) + { + /* vertex - neighbor connection */ + jv = at[n_vertex].valence ++; + at[n_vertex].neighbor[jv] = n_neigh; + at[n_vertex].bond_type[jv] = BOND_TYPE_SINGLE; + at[n_vertex].chem_bonds_valence += at[n_vertex].bond_type[jv]; + /* neighbor - vertex connection */ + jn = at[n_neigh].valence ++; + at[n_neigh].neighbor[jn] = n_vertex; + at[n_neigh].bond_type[jn] = BOND_TYPE_SINGLE; + at[n_neigh].chem_bonds_valence += at[n_neigh].bond_type[jn]; + } + else if ( (n_vertex = n_neigh) >= num_atoms ) + { + ret = RI_ERR_PROGR; + goto exit_function; + } + } + + INCHI_HEAPCHK + + /* isotopic atoms */ + if ( pInChI->IsotopicAtom && pInChI->nNumberOfIsotopicAtoms ) + { + for ( i = 0; i < pInChI->nNumberOfIsotopicAtoms; i ++ ) + { + n_vertex = pInChI->IsotopicAtom[i].nAtomNumber-1; + at[n_vertex].iso_atw_diff = (char)pInChI->IsotopicAtom[i].nIsoDifference; + at[n_vertex].num_iso_H[0] = (char)pInChI->IsotopicAtom[i].nNum_H; + at[n_vertex].num_iso_H[1] = (char)pInChI->IsotopicAtom[i].nNum_D; + at[n_vertex].num_iso_H[2] = (char)pInChI->IsotopicAtom[i].nNum_T; + } + pStruct->bIsotopic |= 1; + } + + INCHI_HEAPCHK + + /* tautomeric groups */ + if ( ret = GetTgroupInfoFromInChI( &pStruct->ti, at, NULL, pInChI ) ) + { + goto exit_function; + } + + /* coordinates: data from unused members: pInChI->IsotopicTGroup and InChI->nNumberOfIsotopicTGroups */ + if ( pInChI->IsotopicTGroup && !pInChI->nNumberOfIsotopicTGroups ) + { + pStruct->pXYZ = (XYZ_COORD *) pInChI->IsotopicTGroup; + pInChI->IsotopicTGroup = NULL; + } + + /* stereo */ + if ( pInChI->StereoIsotopic && + (pInChI->StereoIsotopic->nNumberOfStereoBonds + + pInChI->StereoIsotopic->nNumberOfStereoCenters) ) + { + pStereo = pInChI->StereoIsotopic; + } + else if ( pInChI->Stereo && + (pInChI->Stereo->nNumberOfStereoBonds + + pInChI->Stereo->nNumberOfStereoCenters) ) + { + pStereo = pInChI->Stereo; + } + else + { + pStereo = NULL; + } + + /* stereo2: Mobile-H in addition to Fixed-H*/ + pStereo2 = NULL; + if ( pInChIMobH && pInChIMobH->nNumberOfAtoms ) + { + if ( pInChIMobH->StereoIsotopic && + (pInChIMobH->StereoIsotopic->nNumberOfStereoBonds + + pInChIMobH->StereoIsotopic->nNumberOfStereoCenters) ) + { + pStereo2 = pInChIMobH->StereoIsotopic; + } + else + if ( pInChIMobH->Stereo && + (pInChIMobH->Stereo->nNumberOfStereoBonds + + pInChIMobH->Stereo->nNumberOfStereoCenters) ) + { + pStereo2 = pInChIMobH->Stereo; + } + } + + INCHI_HEAPCHK + + num_stereo_bonds = num_stereo_bonds2 = 0; + num_stereo_centers = num_stereo_centers2 = 0; + /* -- have already been done in the initialization -- + iDeletedH = 0; + nNumDeletedH = 0; + */ + if ( pStereo || pStereo2 ) + { + /* count implicit H needed for parities and reallocate at[]; set at[n_vertex].at_type=1 for these atoms */ + int len1 = pStereo? pStereo->nNumberOfStereoCenters : 0; + int len2 = pStereo2? pStereo2->nNumberOfStereoCenters : 0; + int i2, diff, diff2; + for ( i = i2 = 0; i < len1 || i2 < len2; ) + { + if ( i < len1 && i2 < len2 ) + { + diff = (int)pStereo->nNumber[i] - (int)pStereo2->nNumber[i2]; + if ( diff <= 0 ) { + n_vertex = pStereo->nNumber[i]-1; + i ++; + i2 += !diff; + } + else + { + n_vertex = pStereo2->nNumber[i2]-1; + num_stereo_centers2 ++; + i2 ++; + } + } + else if ( i < len1 ) + { + n_vertex = pStereo->nNumber[i]-1; + i ++; + } + else + { + n_vertex = pStereo2->nNumber[i2]-1; + num_stereo_centers2 ++; + i2 ++; + } + + /* find whether it is an allene */ + if ( at[n_vertex].valence == 2 && + at[n_vertex].num_H == 0 && + bCanAtomBeMiddleAllene(at[n_vertex].elname, 0, 0) && + at[jv = at[n_vertex].neighbor[0]].valence + at[jv].num_H == 3 && + bCanAtomBeTerminalAllene(at[jv].elname, 0, 0) && + at[jn = at[n_vertex].neighbor[1]].valence + at[jn].num_H == 3 && + bCanAtomBeTerminalAllene(at[jn].elname, 0, 0) ) + { + /* allene */ + if ( !at[jv].at_type && at[jv].num_H ) + { + nNumDeletedH += at[jv].num_H; + at[jv].at_type ++; /* H should be added as an explicit H */ + } + + if ( !at[jn].at_type && at[jn].num_H ) + { + nNumDeletedH += at[jn].num_H; + at[jn].at_type ++; /* H should be added as an explicit H */ + } + } + else + { + /* stereogenic atom - sp3 */ + if ( !at[n_vertex].at_type && at[n_vertex].num_H ) + { + nNumDeletedH += at[n_vertex].num_H; + at[n_vertex].at_type ++; /* H should be added as an explicit H */ + } + } + } + + INCHI_HEAPCHK + + len1 = pStereo? pStereo->nNumberOfStereoBonds : 0; + len2 = pStereo2? pStereo2->nNumberOfStereoBonds : 0; + for ( i = i2 = 0; i < len1 || i2 < len2; ) + { + if ( i < len1 && i2 < len2 ) + { + diff = (int)pStereo->nBondAtom1[i] - (int)pStereo2->nBondAtom1[i2]; + diff2 = (int)pStereo->nBondAtom2[i] - (int)pStereo2->nBondAtom2[i2]; + if ( diff < 0 || diff == 0 && diff2 <= 0) + { + n_vertex = pStereo->nBondAtom1[i]-1; + n_neigh = pStereo->nBondAtom2[i]-1; + i ++; + i2 += !diff && !diff2; + } + else + { + n_vertex = pStereo2->nBondAtom1[i2]-1; + n_neigh = pStereo2->nBondAtom2[i2]-1; + num_stereo_bonds2 ++; + i2 ++; + } + } + else if ( i < len1 ) + { + n_vertex = pStereo->nBondAtom1[i]-1; + n_neigh = pStereo->nBondAtom2[i]-1; + i ++; + } + else + { + n_vertex = pStereo2->nBondAtom1[i2]-1; + n_neigh = pStereo2->nBondAtom2[i2]-1; + num_stereo_bonds2 ++; + i2 ++; + } + + if ( !is_in_the_list( at[n_vertex].neighbor, + (AT_NUMB)n_neigh, at[n_vertex].valence ) ) + { + /* must be a cumulene */ + if ( !bFindCumuleneChain( at, (AT_NUMB)n_vertex, (AT_NUMB)n_neigh, + nCumulene, MAX_CUMULENE_LEN+1 ) ) + { + ret = RI_ERR_SYNTAX; /* not a cumulene */ + goto exit_function; + } + } + + if ( !at[n_vertex].at_type && at[n_vertex].num_H ) + { + nNumDeletedH += at[n_vertex].num_H; + at[n_vertex].at_type ++; /* H should be added as an explicit H */ + } + + if ( !at[n_neigh].at_type && at[n_neigh].num_H ) + { + nNumDeletedH += at[n_neigh].num_H; + at[n_neigh].at_type ++; /* H should be added as an explicit H */ + } + } + + INCHI_HEAPCHK + + if ( nNumDeletedH ) + { + /* add explicit H */ + inp_ATOM *at2 = (inp_ATOM *)inchi_calloc( num_atoms + nNumDeletedH, sizeof(at2[0]) ); + if ( !at2 ) + { + ret = RI_ERR_ALLOC; + goto exit_function; + } + pStruct->num_deleted_H = nNumDeletedH; + memcpy( at2, at, num_atoms * sizeof(at2[0]) ); + inchi_free( at ); + pStruct->at = at = at2; + /* fill out deleted H atom info */ + for ( i = num_atoms; i < num_atoms + nNumDeletedH; i ++ ) + { + strcpy( at[i].elname, "H" ); + at[i].el_number = EL_NUMBER_H; + at[i].orig_at_number = iAtNoOffset + i+1; + at[i].orig_compt_at_numb = i + 1; + at[i].component = iComponent + 1; + } + + /* connect deleted H */ + for( i = 0; i < num_atoms; i ++ ) + { + if ( at[i].at_type == 1 ) + { + if ( 0 > (ret = AddExplicitDeletedH( at, i, num_atoms, + &iDeletedH, &idelH1, + nNumDeletedH, pStereo2 != NULL ))) + { + goto exit_function; + } + } + } + } + + INCHI_HEAPCHK + } + + if ( pStereo ) + { + /* mark stereo centers, they have already been connected the added explicit H, if any */ + int bInvertedParity = (pStereo->nCompInv2Abs == -1); + for ( i = 0; i < pStereo->nNumberOfStereoCenters; i ++ ) + { + n_vertex = pStereo->nNumber[i]-1; + parity = pStereo->t_parity[i]; + if ( bInvertedParity ) + { + parity = (parity == AB_PARITY_EVEN)? AB_PARITY_ODD : (parity == AB_PARITY_ODD)? AB_PARITY_EVEN : parity; + } + /* find whether it is allene */ + if ( at[n_vertex].valence == 2 && + at[n_vertex].num_H == 0 && + bCanAtomBeMiddleAllene(at[n_vertex].elname, 0, 0) && + /* allene has exactly 2 double bonds */ + (jv = at[n_vertex].neighbor[0], at[jv].valence + at[jv].num_H == 3) && + bCanAtomBeTerminalAllene(at[jv].elname, 0, 0) && + (jn = at[n_vertex].neighbor[1], at[jn].valence + at[jn].num_H == 3) && + bCanAtomBeTerminalAllene(at[jn].elname, 0, 0) ) { + /* allene: add explicit H if implicit H are present */ + /* iDeletedH = current number of already added explicit H */ + /* idelH1 = index in at[] of the explicit H added to atom jv */ + if ( at[jv].num_H ) + { + if ( 0 > (ret = AddExplicitDeletedH( at, jv, num_atoms, &iDeletedH, + &idelH1, nNumDeletedH, + pStereo2 != NULL ))) + { + goto exit_function; + } + } + else + { + /* index of the stereo atom neighbor */ + idelH1 = at[jv].neighbor[at[jv].neighbor[0]==n_vertex]; + } + + if ( at[jn].num_H ) + { + /* iDeletedH = current number of already added explicit H */ + /* idelH2 = index of the explicit H added to atom jn */ + if ( 0 > (ret = AddExplicitDeletedH( at, jn, num_atoms, &iDeletedH, &idelH2, nNumDeletedH, pStereo2 != NULL ))) { + goto exit_function; + } + } + else + { + idelH2 = at[jn].neighbor[at[jn].neighbor[0]==n_vertex]; + } + /* allene: set bond types to double */ + /* + if ( 0 > (ret = set_bond_type( at, (AT_NUMB)n_vertex, (AT_NUMB)jv, BOND_TYPE_DOUBLE ) ) || + 0 > (ret = set_bond_type( at, (AT_NUMB)n_vertex, (AT_NUMB)jn, BOND_TYPE_DOUBLE ) ) ) { + goto exit_function; + } + */ + /* allene: make 0D parity */ + ret = set_cumulene_0D_parity( at, st, num_atoms, idelH1, jv, jn, idelH2, parity, 2 ); + if ( ret < 0 ) + { + goto exit_function; + } + } + else + { + /* stereogenic sp3 atom */ + if ( at[n_vertex].num_H ) + { + if ( 0 > (ret = AddExplicitDeletedH( at, n_vertex, num_atoms, + &iDeletedH, &idelH1, nNumDeletedH, + pStereo2 != NULL ))) + { + goto exit_function; + } + } + + ret = set_atom_0D_parity( at, st, num_atoms, nNumDeletedH, n_vertex, parity ); + + if ( ret < 0 ) + { + goto exit_function; + } + num_stereo_centers ++; + } + + if ( ret < 0 ) + { + goto exit_function; + } + } + + INCHI_HEAPCHK + + /* mark stereobonds */ + for ( i = 0; i < pStereo->nNumberOfStereoBonds; i ++ ) + { + jv = pStereo->nBondAtom1[i]-1; + jn = pStereo->nBondAtom2[i]-1; + parity = pStereo->b_parity[i]; + if ( !is_in_the_list( at[jv].neighbor, (AT_NUMB)jn, at[jv].valence ) ) + { + /* must be a cumulene */ + if ( !bFindCumuleneChain( at, (AT_NUMB)jv, (AT_NUMB)jn, nCumulene, MAX_CUMULENE_LEN+1 ) ) + { + return RI_ERR_SYNTAX; /* not a cumulene */ + } + len = MAX_CUMULENE_LEN+1; + } + else + { + /* a regular double or alt bond */ + nCumulene[0] = jv; + nCumulene[1] = jn; + len = 1; /* cumulene length is number of bonds, not number of atoms */ + } + + /* cumulene or double bond: add explicit H if implicit H are present */ + if ( at[jv].num_H ) + { + if ( 0 > (ret = AddExplicitDeletedH( at, jv, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) + { + goto exit_function; + } + } + else + { + /* double bond neighbor that has the smallest canonical number; it is either 0th or 1st */ + idelH1 = at[jv].neighbor[at[jv].neighbor[0]==nCumulene[1]]; + } + if ( at[jn].num_H ) + { + if ( 0 > (ret = AddExplicitDeletedH( at, jn, num_atoms, &iDeletedH, &idelH2, nNumDeletedH, pStereo2 != NULL ))) + { + goto exit_function; + } + } + else + { + idelH2 = at[jn].neighbor[at[jn].neighbor[0]==nCumulene[len-1]]; + } + if ( 0 > (ret = set_cumulene_0D_parity( at, st, num_atoms, idelH1, jv, jn, idelH2, parity, len )) ) + { + goto exit_function; + } + } + + INCHI_HEAPCHK + } + + /* allocate memory for Mobile-H-only stereo */ + if ( num_stereo_centers2 + num_stereo_bonds2 ) + { + if ( !(st = (inp_ATOM_STEREO *)inchi_calloc( num_atoms, sizeof(st[0])))) + { + ret = RI_ERR_ALLOC; + goto exit_function; + } + CopyAt2St( at, st, num_atoms ); + } + + pStruct->st = st; + if ( num_stereo_centers2 ) + { + /* In case of Fixed-H */ + /* mark additional Mobile-H stereo centers, they have already been connected the added explicit H, if any */ + int bInvertedParity = (pStereo2->nCompInv2Abs == -1); + for ( i = 0; i < pStereo2->nNumberOfStereoCenters; i ++ ) + { + n_vertex = pStereo2->nNumber[i]-1; + parity = pStereo2->t_parity[i]; + if ( at[n_vertex].p_parity ) + { + continue; /* the parity has already been set for Fixed-H */ + } + if ( bInvertedParity ) + { + parity = (parity == AB_PARITY_EVEN)? AB_PARITY_ODD : (parity == AB_PARITY_ODD)? AB_PARITY_EVEN : parity; + } + /* find whether it is allene */ + if ( at[n_vertex].valence == 2 && + at[n_vertex].num_H == 0 && + bCanAtomBeMiddleAllene(at[n_vertex].elname, 0, 0) && + /* allene has exactly 2 double bonds */ + (jv = at[n_vertex].neighbor[0], at[jv].valence + at[jv].num_H == 3) && + bCanAtomBeTerminalAllene(at[jv].elname, 0, 0) && + (jn = at[n_vertex].neighbor[1], at[jn].valence + at[jn].num_H == 3) && + bCanAtomBeTerminalAllene(at[jn].elname, 0, 0) ) + { + /* allene: add explicit H if implicit H are present */ + /* iDeletedH = current number of already added explicit H */ + /* idelH1 = index in at[] of the explicit H added to atom jv */ + if ( at[jv].num_H ) + { + if ( 0 > (ret = AddExplicitDeletedH( at, jv, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) + { + goto exit_function; + } + } + else + { + /* index of the stereo atom neighbor */ + idelH1 = at[jv].neighbor[at[jv].neighbor[0]==n_vertex]; + } + if ( at[jn].num_H ) + { + /* iDeletedH = current number of already added explicit H */ + /* idelH2 = index of the explicit H added to atom jn */ + if ( 0 > (ret = AddExplicitDeletedH( at, jn, num_atoms, &iDeletedH, &idelH2, nNumDeletedH, pStereo2 != NULL ))) + { + goto exit_function; + } + } + else + { + idelH2 = at[jn].neighbor[at[jn].neighbor[0]==n_vertex]; + } + /* allene: set bond types to double */ + /* + if ( 0 > (ret = set_bond_type( at, (AT_NUMB)n_vertex, (AT_NUMB)jv, BOND_TYPE_DOUBLE ) ) || + 0 > (ret = set_bond_type( at, (AT_NUMB)n_vertex, (AT_NUMB)jn, BOND_TYPE_DOUBLE ) ) ) { + goto exit_function; + } + */ + /* allene: make 0D parity */ + ret = set_cumulene_0D_parity( at, st, num_atoms, idelH1, jv, jn, idelH2, parity, 2 ); + if ( ret < 0 ) + { + goto exit_function; + } + } + else + { + /* stereogenic sp3 atom */ + if ( at[n_vertex].num_H ) + { + if ( 0 > (ret = AddExplicitDeletedH( at, n_vertex, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) + { + goto exit_function; + } + } + ret = set_atom_0D_parity( at, st, num_atoms, nNumDeletedH, n_vertex, parity ); + if ( ret < 0 ) + { + goto exit_function; + } + num_stereo_centers ++; + } + if ( ret < 0 ) + { + goto exit_function; + } + } + } + + if ( num_stereo_bonds2 ) + { + /* In case of Fixed-H */ + /* mark additional Mobile-H stereobonds, they have already been connected the added explicit H, if any */ + for ( i = 0; i < pStereo2->nNumberOfStereoBonds; i ++ ) + { + jv = pStereo2->nBondAtom1[i]-1; + jn = pStereo2->nBondAtom2[i]-1; + parity = pStereo2->b_parity[i]; + if ( !is_in_the_list( at[jv].neighbor, (AT_NUMB)jn, at[jv].valence ) ) + { + /* must be a cumulene */ + if ( !bFindCumuleneChain( at, (AT_NUMB)jv, (AT_NUMB)jn, nCumulene, MAX_CUMULENE_LEN+1 ) ) + { + return RI_ERR_SYNTAX; /* not a cumulene */ + } + len = MAX_CUMULENE_LEN+1; + } + else + { + /* a regular double or alt bond */ + nCumulene[0] = jv; + nCumulene[1] = jn; + len = 1; /* cumulene length is number of bonds, not number of atoms */ + } + /* cumulene or double bond: add explicit H if implicit H are present */ + if ( at[jv].num_H ) + { + if ( 0 > (ret = AddExplicitDeletedH( at, jv, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) + { + goto exit_function; + } + } + else + { + /* double bond neighbor that has the smallest canonical number */ + idelH1 = at[jv].neighbor[at[jv].neighbor[0]==nCumulene[1]]; + } + if ( at[jn].num_H ) + { + if ( 0 > (ret = AddExplicitDeletedH( at, jn, num_atoms, &iDeletedH, &idelH2, nNumDeletedH, pStereo2 != NULL ))) + { + goto exit_function; + } + } + else + { + idelH2 = at[jn].neighbor[at[jn].neighbor[0]==nCumulene[len-1]]; + } + if ( 0 > (ret = set_cumulene_0D_parity( at, st, num_atoms, idelH1, jv, jn, idelH2, parity, len )) ) + { + goto exit_function; + } + } + } + + + ret = num_atoms; + +exit_function: + return ret; +} + + +/*************************************************************/ +int SetStereoBondTypeFor0DParity( inp_ATOM *at, int i1, int m1 ) +{ + AT_NUMB nCumulene[MAX_CUMULENE_LEN+2]; + int j, n1, n2, k1, m2, ret, nLenCumulene = 0, bond_type; + k1 = at[i1].sb_ord[m1]; + n1 = i1; + nCumulene[nLenCumulene ++] = n1; + do { + n2 = at[n1].neighbor[k1]; /* next atom */ + nCumulene[nLenCumulene ++] = n2; + for (m2 = 0; m2 < MAX_NUM_STEREO_BONDS && at[n2].sb_parity[m2]; m2 ++ ) { + if ( n1 == at[n2].neighbor[(int)at[n2].sb_ord[m2]] ) { + /* found the endatom */ + goto found; + } + } + if ( at[n2].num_H || at[n2].valence != 2 || at[n2].endpoint ) { + break; /* not a middle cumulene */ + } + k1 = (at[n2].neighbor[0] == n1); + n1 = n2; + } while ( at[n1].valence == 2 && !at[n1].num_H && nLenCumulene < MAX_CUMULENE_LEN+2 && + bCanAtomBeMiddleAllene( at[n1].elname, at[n1].charge, at[n1].radical ) ); + return RI_ERR_SYNTAX; /* failed */ + +found: + if ( nLenCumulene == 2 ) { + bond_type = BOND_TYPE_STEREO; /* double bond or alternating bond */ + } else { + bond_type = BOND_TYPE_DOUBLE; /* cumulene or allene */ + } + + for ( j = 1; j < nLenCumulene; j ++ ) { + /* if bond_type = BOND_TYPE_DOUBLE then increments at->cham_bonds_valence: */ + /* at->cham_bonds_valence += BOND_TYPE_DOUBLE-BOND_TYPE_SINGLE */ + if ( 0 > (ret = set_bond_type( at, (AT_NUMB)nCumulene[j-1], (AT_NUMB)nCumulene[j], bond_type ) ) ) { + return RI_ERR_PROGR; /* failed */ + } + } + return nLenCumulene; +} +/****************************************************************************/ +int SetStereoBondTypesFrom0DStereo( StrFromINChI *pStruct, INChI *pInChI) +{ + INChI_Stereo *pStereo; + inp_ATOM *at = pStruct->at; + int num_atoms = pStruct->num_atoms; + int i, j, num_stereo_bonds, ret; + + if ( pInChI->StereoIsotopic && + (pInChI->StereoIsotopic->nNumberOfStereoBonds + + pInChI->StereoIsotopic->nNumberOfStereoCenters) ) { + pStereo = pInChI->StereoIsotopic; + } else + if ( pInChI->Stereo && + (pInChI->Stereo->nNumberOfStereoBonds + + pInChI->Stereo->nNumberOfStereoCenters) ) { + pStereo = pInChI->Stereo; + } else { + pStereo = NULL; + } + + /************************ set bond types separately from stereo *******************/ + if ( pStereo ) { + num_stereo_bonds = 0; + for ( i = 0; i < num_atoms; i ++ ) { + /* set BOND_TYPE_DOUBLE in allenes and cumulenes */ + /* set BOND_TYPE_STEREO in double bond stereo */ + for ( j = 0; j < MAX_NUM_STEREO_BONDS && at[i].sb_parity[j]; j ++ ) { + num_stereo_bonds ++; + if ( 0 > (ret = SetStereoBondTypeFor0DParity( at, i, j ) ) ) { + goto exit_function; + } + } + } + if ( num_stereo_bonds ) { + int num_bond_type_stereo; + int num_bond_type_altern; + AT_NUMB neigh; + /* replace adjacent BOND_TYPE_STEREO with BOND_TYPE_ALTERN */ + for ( i = 0; i < num_atoms; i ++ ) { + num_bond_type_stereo = 0; + num_bond_type_altern = 0; + for ( j = 0; j < at[i].valence; j ++ ) { + num_bond_type_stereo += ( at[i].bond_type[j] == BOND_TYPE_STEREO ); + num_bond_type_altern += ( at[i].bond_type[j] == BOND_TYPE_ALTERN ); + } + if ( num_bond_type_stereo + num_bond_type_altern > 1 && num_bond_type_stereo ) { + for ( j = 0; j < at[i].valence; j ++ ) { + if ( at[i].bond_type[j] == BOND_TYPE_STEREO ) { + neigh = at[i].neighbor[j]; + /* does not change at[i].chem_bond_valence in case of BOND_TYPE_ALTERN */ + if ( 0 > (ret = set_bond_type( at, (AT_NUMB)i, neigh, BOND_TYPE_ALTERN ) ) ) { + goto exit_function; + } + } + } + } + /* at this point only isolated stereo bonds have type BOND_TYPE_STEREO */ + } + /* increment at[i].chem_bonds_valence if at[i] has an altern. bond */ + /* replace BOND_TYPE_STEREO with BOND_TYPE_DOUBLE and increment */ + /* chem_bonds_valence of the adjacent atoms */ + for ( i = 0; i < num_atoms; i ++ ) { + num_bond_type_stereo = 0; + num_bond_type_altern = 0; + for ( j = 0; j < at[i].valence; j ++ ) { + num_bond_type_stereo += ( at[i].bond_type[j] == BOND_TYPE_STEREO ); + num_bond_type_altern += ( at[i].bond_type[j] == BOND_TYPE_ALTERN ); + } + if ( !num_bond_type_stereo && num_bond_type_altern ) { + /* an atom has only BOND_TYPE_ALTERN => adjacent BOND_TYPE_ALTERN case */ + at[i].chem_bonds_valence += 1; + } else + if ( num_bond_type_stereo == 1 ) { + /* isolated BOND_TYPE_STEREO => replace with BOND_TYPE_DOUBLE */ + for ( j = 0; j < at[i].valence; j ++ ) { + if ( at[i].bond_type[j] == BOND_TYPE_STEREO ) { + neigh = at[i].neighbor[j]; + /* replacing BOND_TYPE_STEREO with BOND_TYPE_DOUBLE */ + /* does not change at->chem_bonds_valence */ + if ( 0 > (ret = set_bond_type( at, (AT_NUMB)i, neigh, BOND_TYPE_DOUBLE ) ) ) { + goto exit_function; + } + at[i].chem_bonds_valence ++; + at[(int)neigh].chem_bonds_valence ++; + } + } + } else + if ( num_bond_type_stereo + num_bond_type_altern ) { + /* an atom still has both BOND_TYPE_STEREO and BOND_TYPE_ALTERN */ + ret = RI_ERR_PROGR; + goto exit_function; + } + } + INCHI_HEAPCHK + } + } + ret = 0; /* success */ +exit_function: + return ret; +} + +/****************************************************************************/ +int CopyBnsToAtom( StrFromINChI *pStruct, BN_STRUCT *pBNS, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int bAllowZeroBondOrder ) +{ + int i, j, atom_charge, left_charge, charge, ret = 0, v1, nMinorder; + int num_at = pStruct->num_atoms; + inp_ATOM *at = pStruct->at; + ICHICONST SRM *pSrm = pStruct->pSrm; + BNS_VERTEX *pv; + BNS_EDGE *pe; + int chem_bonds_valence, bond_order; + + atom_charge = left_charge = 0; + for ( i = 0; i < num_at; i ++ ) { + pv = pBNS->vert + i; + /* bonds */ + chem_bonds_valence = 0; + for ( j = 0; j < at[i].valence; j ++ ) { + pe = pBNS->edge + pv->iedge[j]; + BondFlowMaxcapMinorder( at, pVA, pSrm, i, j, NULL, &nMinorder, NULL ); + bond_order = pe->flow + nMinorder; + if ( !bAllowZeroBondOrder && !bond_order ) { + bond_order = 1; + } + chem_bonds_valence += bond_order; + at[i].bond_type[j] = bond_order; /* BOND_MARK_HIGHLIGHT */ + } + at[i].chem_bonds_valence = chem_bonds_valence; + /* charges (both may be present resulting in zero) */ + at[i].charge = pVA[i].cInitCharge; + if ( pVA[i].nCMinusGroupEdge ) { + pe = pBNS->edge + pVA[i].nCMinusGroupEdge - 1; + if ( charge = pe->flow ) { + at[i].charge -= charge; + atom_charge -= charge; + } + } + if ( pVA[i].nCPlusGroupEdge ) { + pe = pBNS->edge + pVA[i].nCPlusGroupEdge - 1; + if ( charge = pe->cap - pe->flow ) { + at[i].charge += charge; + atom_charge += charge; + } + } + if ( pv->st_edge.cap > pv->st_edge.flow ) { + at[i].radical = RADICAL_SINGLET + (pv->st_edge.cap - pv->st_edge.flow); + } + } + /* find charge excess */ + for ( i = num_at; i < pBNS->num_vertices; i ++ ) { + pv = pBNS->vert + i; + if ( charge = pv->st_edge.cap - pv->st_edge.flow ) { + if ( IS_BNS_VT_C_OR_CSUPER_GR(pv->type) ) { + left_charge -= charge; + } else + if ( IS_BNS_VT_YVCONNECTOR(pv->type) ) { + left_charge += charge; + } + } + } + /* tautomeric H and (-) */ + for ( i = 0; i < pBNS->num_t_groups; i ++ ) { + /* tautomeric groups are first non-atom vertices; + order of them is same as in pTCGroups->pTCG[] */ + int num_H = pTCGroups->pTCG[i].tg_num_H; + int num_Minus = pTCGroups->pTCG[i].tg_num_Minus; + int bMinusFirst = (pTCGroups->pTCG[i].tg_RestoreFlags & TGRF_MINUS_FIRST); + int num_at_add; + Vertex vMinus = NO_VERTEX; + pv = pBNS->vert + num_at + i; /* t-group vertex */ + if ( !(pv->type & BNS_VERT_TYPE_TGROUP) ) { + return RI_ERR_PROGR; + } + if ( pTCGroups->pTCG[i].tg_set_Minus > 0 && num_Minus > 0 ) { + vMinus = pTCGroups->pTCG[i].tg_set_Minus-1; + num_Minus --; + } + + if ( bMinusFirst ) { + for ( j = 0; j < pv->num_adj_edges; j ++ ) { + pe = pBNS->edge + pv->iedge[j]; + v1 = pe->neighbor1; + num_at_add = pe->flow; + if ( v1 == vMinus ) { + if ( num_at_add ) { + at[v1].charge = -1; /* no checking at[v1].charge == 0 for now ??? */ + num_at_add --; /* no checking num_at_add > 0 for now ??? */ + } else { + num_Minus ++; /* error ??? */ + } + vMinus = NO_VERTEX; + } + if ( num_at_add > 0 ) { + /* atom has tautomeric attachment; do not allow =N(-) */ + if ( num_Minus && !at[v1].charge && + at[v1].valence == at[v1].chem_bonds_valence ) { + at[v1].charge --; + num_at_add --; + num_Minus --; + } + if ( num_at_add > 0 ) { + at[v1].num_H += num_at_add; + num_H -= num_at_add; + num_at_add = 0; + } + } + at[v1].endpoint = i+1; + } + if ( (num_H+num_Minus != pv->st_edge.cap - pv->st_edge.flow) && (num_H || num_Minus || vMinus != NO_VERTEX) ) { + return RI_ERR_PROGR; + } + } else { + for ( j = pv->num_adj_edges-1; 0 <= j; j -- ) { + pe = pBNS->edge + pv->iedge[j]; + v1 = pe->neighbor1; + num_at_add = pe->flow; + if ( v1 == vMinus ) { + if ( num_at_add ) { + at[v1].charge = -1; /* no checking at[v1].charge == 0 for now ??? */ + num_at_add --; /* no checking num_at_add > 0 for now ??? */ + } else { + num_Minus ++; /* error ??? */ + } + vMinus = NO_VERTEX; + } + if ( num_at_add > 0 ) { + /* atom has tautomeric attachment; do not allow =N(-) */ + if ( num_Minus && !at[v1].charge && + at[v1].valence == at[v1].chem_bonds_valence ) { + at[v1].charge --; + num_at_add --; + num_Minus --; + } + if ( num_at_add > 0 ) { + at[v1].num_H += num_at_add; + num_H -= num_at_add; + num_at_add = 0; + } + } + at[v1].endpoint = i+1; + } + if ( (num_H+num_Minus != pv->st_edge.cap - pv->st_edge.flow) && (num_H || num_Minus || vMinus != NO_VERTEX) ) { + return RI_ERR_PROGR; + } + } + } + + return ret; +} +/****************************************************************************/ +int CheckBnsConsistency( StrFromINChI *pStruct, BN_STRUCT *pBNS, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int bNoRad ) +{ + int nOutput = 0; +#ifndef TARGET_API_LIB +#if ( bRELEASE_VERSION == 0 ) + char s[128]; + int i, j, atom_charge, left_charge, charge, excess_charge, ret = 0; + int v1, v2, flow, tot_st_flow, tot_st_cap, num_electrons, nNumMetalAtoms; + int num_at = pStruct->num_atoms; + inp_ATOM *at = pStruct->at; + BNS_VERTEX *pv; + BNS_EDGE *pe; +#ifdef _DEBUG + int bDebugOutput = 0; + bNoRad = 1; + bDebugOutput = 1; +#endif + /* count electrons and metals */ + num_electrons = -pTCGroups->total_charge; + nNumMetalAtoms = 0; + for ( i = 0; i < pTCGroups->num_tgroups; i ++ ) { + num_electrons += pTCGroups->pTCG[i].tg_num_H; + } + for ( i = 0; i < num_at; i ++ ) { + num_electrons += at[i].el_number + at[i].num_H; + nNumMetalAtoms += pVA[i].cMetal; + } + /* create output string */ + sprintf( s, "%d:%d%sM%02dv%da%de%db%d* ", + bNoRad, pTCGroups->iComponent+1, num_electrons%2?"O":"E", nNumMetalAtoms, + pBNS->num_vertices, num_at, pBNS->num_edges, pBNS->num_bonds ); + + + tot_st_flow = tot_st_cap = 0; + atom_charge = left_charge = 0; + if ( pBNS->num_atoms != num_at ) { + fprintf( stdout, "\n%sNum. atoms discrepancy: %d(BNS) vs. %d(at) ", s, pBNS->num_atoms, num_at); + nOutput ++; + } + /* check edges */ +#ifdef _DEBUG + if ( bDebugOutput && bNoRad ) { + fprintf( stderr, "\n\n------begin------------------------------------------------\n" ); + fprintf( stderr, "\n\fedge cap flow v1 v2\n\n" ); + /* xxxx xxxx/xxxx xxxx/xxxx xxxx xxxx */ + } +#endif + for ( i = 0; i < pBNS->num_edges; i ++ ) { + pe = pBNS->edge + i; + v1 = pe->neighbor1; + v2 = v1 ^ pe->neighbor12; + if ( pe->cap < pe->flow || pe->flow < 0 ) { + fprintf( stdout, "\n%sedge %d (%d-%d) has cap=%d flow=%d ", s, i, v1, v2, pe->cap, pe->flow ); + nOutput ++; + } +#ifdef _DEBUG + if ( bDebugOutput && bNoRad ) { + /* xxxx xxxx/xxxx xxxx/xxxx xxxx xxxx */ + fprintf( stderr, "%4d %4d/%-4d %4d/%-4d %4d %4d\n", i, pe->cap, pe->cap0, pe->flow, pe->flow0, v1, v2 ); + } +#endif + } + + + /* check vertices */ +#ifdef _DEBUG + if ( bDebugOutput && bNoRad ) { + fprintf( stderr, "\n\fvert st-cap st-flow type iedge : neigh\n\n" ); + /* xxxx xxxx/xxxx xxxx/xxxx 0xXXX xxxx : xxx */ + } +#endif + for ( i = 0; i < pBNS->num_vertices; i ++ ) { + pv = pBNS->vert + i; +#ifdef _DEBUG + if ( bDebugOutput && bNoRad ) { + /* xxxx xxxx/xxxx xxxx/xxxx 0xXXX xxxx : xxx */ + int j; + const char *s; + char sAtom[6]; + switch( pv->type ) { + case BNS_VERT_TYPE_ATOM: + sprintf( sAtom, "At %-2.2s", i < num_at? at[i].elname : "??" ); + s = sAtom; + break; + case BNS_VERT_TYPE_ATOM | BNS_VERT_TYPE_ENDPOINT: + s = "Endpt"; + break; + case BNS_VT_C_POS: + s = "(+) "; + break; + case BNS_VT_C_NEG: + s = "(-) "; + break; + case BNS_VT_C_POS_C: + s = "(+C) "; + break; + case BNS_VT_C_NEG_C: + s = "(-C) "; + break; + case BNS_VT_C_POS_M: + s = "(+M) "; + break; + case BNS_VT_C_NEG_M: + s = "(-M) "; + break; + case BNS_VT_C_POS_ALL: + s = "(+)Sg"; + break; + case BNS_VT_C_NEG_ALL: + s = "(-)Sg"; + break; + case BNS_VT_M_GROUP: + s = "M-grp"; + break; + case BNS_VERT_TYPE__AUX | BNS_VERT_TYPE_TEMP: + s = "ChStr"; + break; + case BNS_VERT_TYPE__AUX: + s = "Yconn"; + break; + case BNS_VERT_TYPE_TGROUP: + s = "T-grp"; + break; + default: + s = "Unkn."; + break; + } + fprintf( stderr, "%4d %4d/%-4d %4d/%-4d 0x%03X %5s", + i, pv->st_edge.cap, pv->st_edge.cap0, pv->st_edge.flow, pv->st_edge.flow0, + pv->type, s ); + for ( j = 0; j < pv->num_adj_edges; j ++ ) { + fprintf( stderr, " %2d", pv->iedge[j] ); + } + fprintf( stderr, ":" ); + for ( j = 0; j < pv->num_adj_edges; j ++ ) { + pe = pBNS->edge + pv->iedge[j]; + fprintf( stderr, " %2d", pe->neighbor12 ^ i ); + } + fprintf( stderr, "\n" ); + } +#endif + tot_st_flow += pv->st_edge.flow; + tot_st_cap += pv->st_edge.cap; + if ( pv->num_adj_edges > pv->max_adj_edges ) { + fprintf( stdout, "\n%s%s %d type 0x%X \"%s\" num_edges=%d > max=%d ", s, + i < num_at? "atom":"vertex", i, + pv->type, at[i].elname, pv->num_adj_edges, pv->max_adj_edges ); + nOutput ++; + } + if ( i < num_at ) { + /* charge on atoms */ + charge = pVA[i].cInitCharge; + if ( pVA[i].nCMinusGroupEdge ) { + pe = pBNS->edge + pVA[i].nCMinusGroupEdge - 1; + if ( pe->flow > 0 ) { + charge -= pe->flow; + } + } + if ( pVA[i].nCPlusGroupEdge ) { + pe = pBNS->edge + pVA[i].nCPlusGroupEdge - 1; + if ( pe->cap > pe->flow ) { + charge += pe->cap - pe->flow; + } + } + if ( bNoRad && pv->st_edge.flow != pv->st_edge.cap ) { + fprintf( stdout, "\n%s%s %d: type 0x%X \"%s\" unexpected st_cap=%d st_flow=%d ", s, + i < num_at? "atom":"vertex", i, + pv->type, at[i].elname, pv->st_edge.cap, pv->st_edge.flow ); + nOutput ++; + } else + if ( bNoRad && charge && !strcmp(at[i].elname, "C") ) { + /* ignore carbonyls */ + if ( i == 0 && num_at == 2 && !strcmp(at[1].elname, "O") && + !at[0].num_H && !at[1].num_H && !pTCGroups->total_charge) { + ; /* C(-)#O(+) structure */ + } else { + fprintf( stdout, "\n%s%s %d: type 0x%X \"%s\" charge=%d ", s, + i < num_at? "atom":"vertex", i, + pv->type, at[i].elname, charge ); + nOutput ++; + } + } + atom_charge += charge; + } else + if ( (charge = pv->st_edge.cap - pv->st_edge.flow) > 0 ) { + /* excess charge */ + if ( !bNoRad && IS_BNS_VT_C_OR_CSUPER_GR(pv->type) ) { + left_charge -= charge; + } else + if ( !bNoRad && IS_BNS_VT_YVCONNECTOR(pv->type) ) { + left_charge += charge; + } else + if ( !bNoRad && IS_BNS_VT_M_GR(pv->type) && + 0 <= (j=pTCGroups->nGroup[TCG_MeFlower3]) && + i == pTCGroups->pTCG[j].nVertexNumber ) { + ; /* additional "radical" on metal flower */ + } else + if ( !(pv->type & BNS_VERT_TYPE_TGROUP) || bNoRad ) { + /* t-groups before running BFS should have st_cap > st_flow */ + fprintf( stdout, "\n%s%s %d: type 0x%X unexpected st_cap=%d st_flow=%d ", s, + i < num_at? "atom":"vertex", i, + pv->type, pv->st_edge.cap, pv->st_edge.flow); + nOutput ++; + } + } + if ( pv->st_edge.cap < pv->st_edge.flow || pv->st_edge.flow < 0 ) { + fprintf( stdout, "\n%s%s %d: type 0x%X \"%s\" st_cap=%d st_flow=%d ", s, + i < num_at? "atom":"vertex", i, + pv->type, i < num_at? at[i].elname:"", pv->st_edge.cap, pv->st_edge.flow ); + nOutput ++; + } + /* check edge_flow vs. st_flow consistency */ + for( j = 0, flow = 0; j < pv->num_adj_edges; j ++ ) { + pe = pBNS->edge + pv->iedge[j]; + flow += pe->flow; + } + if ( flow != pv->st_edge.flow ) { + fprintf( stdout, "\n%s%s %d: type 0x%X \"%s\" st_flow=%d edge_flow=%d ", s, + i < num_at? "atom":"vertex", i, + pv->type, i < num_at? at[i].elname:"", pv->st_edge.flow, flow ); + nOutput ++; + } + } +#ifdef _DEBUG + if ( bDebugOutput && bNoRad ) { + fprintf( stderr, "\n------end--------------------------------------------------\n" ); + } +#endif + /* + if ( num_electrons %= 2 ) { + fprintf( stdout, "\n%d*Odd number of electrons (%d atoms) ", bNoRad, num_at ); + nOutput ++; + } + */ + /* tautomeric groups charge */ + for ( i = 0, charge = 0; i < pTCGroups->num_tgroups; i ++ ) { + charge -= pTCGroups->pTCG[i].tg_num_Minus; + } + /* compare */ + if ( charge != pTCGroups->tgroup_charge ) { + fprintf( stdout, "\n%sCounted t-group charge=%d while %d was saved ", s, + charge, pTCGroups->tgroup_charge); + nOutput ++; + } + /* add other charges */ + charge += atom_charge + left_charge; + excess_charge = pTCGroups->total_charge - pTCGroups->added_charge - pTCGroups->tgroup_charge; + if ( charge != pTCGroups->total_charge && excess_charge != pTCGroups->total_charge - charge ) { + fprintf( stdout, "\n%sCounted total charge=%d while %d was saved; excess charge=%d ", s, + charge, pTCGroups->total_charge, excess_charge ); + nOutput ++; + } + if ( tot_st_cap != pBNS->tot_st_cap || tot_st_flow != pBNS->tot_st_flow ) { + fprintf( stdout, "\n%sCounted/saved total st_flow=%d/%d st_cap=%d/%d ", s, + tot_st_flow, pBNS->tot_st_flow, tot_st_cap, pBNS->tot_st_cap ); + nOutput ++; + } + if ( nOutput ) { + fprintf( stdout, "\n" ); + } +#endif +#endif + return nOutput; +} + +/****************************************************************************/ +int AddExplicitDeletedH( inp_ATOM *at, int jv, int num_at, int *iDeletedH, int *iH, int nNumDeletedH, int bTwoStereo ) +{ + inp_ATOM *cur_H, *cur_at = at+jv; + int tot_num_iso_H = NUM_ISO_H(cur_at, 0); + int num_H = cur_at->num_H; + int iso_H = 0; + S_CHAR num_iso_H[NUM_H_ISOTOPES]; + int i; + + if ( !at[jv].at_type ) { + return RI_ERR_PROGR; + } + + if ( at[jv].at_type > 1 ) { + /* explicit hydrogens have already been added; find them */ + for ( i = 0; i < *iDeletedH; i ++ ) { + if ( at[num_at + i].neighbor[0] == jv ) { + *iH = num_at + i; /* return the first found H, it has the smallest canonical pseudo rank */ + return 0; + } + } + return RI_ERR_PROGR; + } + /* add all explicit H disconnected from at[jv] in order H, 1H, D, T */ + *iH = *iDeletedH + num_at; /* num_H includes all H, both isotopic and normal */ + for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { + num_iso_H[i] = at[jv].num_iso_H[i]; + } + for ( ; num_H && (*iDeletedH) < nNumDeletedH; (*iDeletedH) ++ ) { + cur_H = at + num_at + (*iDeletedH); /* first available empty atom will be this explicit H */ + cur_H->neighbor[cur_H->valence] = jv; /* connect this new atom H to the real atom */ + cur_H->bond_type[cur_H->valence] = BOND_TYPE_SINGLE; + cur_H->valence ++; + if ( num_H > tot_num_iso_H ) { + num_H --; + if ( num_H != tot_num_iso_H ) { + /* may happen when Mobile-H stereo included in Fixed-H processing */ + if ( bTwoStereo ) { + continue; + } else { + return RI_ERR_SYNTAX; /* two identical H neighbors of a stereo atom/bond */ + } + } + } else { + while ( iso_H < NUM_H_ISOTOPES && !num_iso_H[iso_H] ) + iso_H ++; + if ( iso_H < NUM_H_ISOTOPES ) { + cur_H->iso_atw_diff = iso_H + 1; /* isotopic shift + 1 */ + num_H --; + tot_num_iso_H --; + num_iso_H[iso_H] --; + if ( num_iso_H[iso_H] ) { + return RI_ERR_SYNTAX; /* two identical isotopic H neighbors of a stereo atom/bond */ + } + } else { + return RI_ERR_SYNTAX; /* not enough isotopic H */ + } + } + } + if ( num_H ) { + return RI_ERR_SYNTAX; + } + at[jv].at_type ++; /* at[jv].at_type==2 => explicit hydrogens have already been added */ + return 0; /* success */ +} +/****************************************************************************/ +int bFindCumuleneChain( inp_ATOM *at, AT_NUMB i1, AT_NUMB i2, AT_NUMB nCumulene[], int nMaxLen ) +/* nMaxLen = number of bonds in cumulene = 3 = MAX_CUMULENE_LEN+1 */ +/* nCumulene[nMaxLen+1] will contain cumulene chain >i1=x=y=i2< in this order */ +{ + int i, len, iat, nat; + nCumulene[0] = i1; + for ( i = 0; i < at[i1].valence; i ++ ) { + len = 0; + iat = i1; /* current */ + nat = at[i1].neighbor[i]; /* next */ + if ( len+1 == nMaxLen ) { + if ( nat == i2 ) { + nCumulene[++len] = nat; + return 1; /* success */ + } + continue; /* check next at[i1] neighbor */ + } + while ( at[nat].valence == 2 && + at[nat].num_H == 0 && + bCanAtomBeMiddleAllene(at[nat].elname, 0, 0) ) { + nCumulene[++len] = nat; + nat = at[nat].neighbor[at[nat].neighbor[0]==iat]; /* new next */ + if ( len+1 == nMaxLen ) { + if ( nat == i2 ) { + nCumulene[++len] = nat; + return 1; /* success */ + } + break; /* check next at[i1] neighbor */ + } + iat = nCumulene[len]; /* new current */ + } + } + return 0; /* failed */ +} +/****************************************************************************/ +int set_bond_type( inp_ATOM *at, AT_NUMB i1, AT_NUMB i2, int bType ) +{ + AT_NUMB *p1 = is_in_the_list( at[i1].neighbor, i2, at[i1].valence ); + AT_NUMB *p2 = is_in_the_list( at[i2].neighbor, i1, at[i2].valence ); + if ( p1 && p2 ) { + int j1 = (int) (p1 - at[i1].neighbor); + int j2 = (int) (p2 - at[i2].neighbor); + int bTypePrev = at[i1].bond_type[j1]; + at[i1].bond_type[j1] = bType; + at[i2].bond_type[j2] = bType; + if ( bTypePrev && bTypePrev <= BOND_TYPE_TRIPLE && + bType && bType <= BOND_TYPE_TRIPLE ) { + at[i1].chem_bonds_valence += bType - bTypePrev; + at[i2].chem_bonds_valence += bType - bTypePrev; + } + return 0; + } + return RI_ERR_SYNTAX; +} +/****************************************************************************/ +int set_cumulene_0D_parity( inp_ATOM *at, inp_ATOM_STEREO *st, int num_at, int idelH1, int i1, int i2, int idelH2, int parity, int len ) +{ + AT_NUMB nCumulene[MAX_CUMULENE_LEN+2]; + AT_NUMB *p1, *p2; + int m1, m2, parity1, parity2, sb_ord_m1, sb_ord_m2, k1, k2, num_neigh1, num_neigh2; + /* the following types must exactly match types in inp_ATOM and inp_ATOM_STEREO */ + S_CHAR *sb_ord1, *sn_ord1, *sb_parity1; + S_CHAR *sb_ord2, *sn_ord2, *sb_parity2; + AT_NUMB *sn_orig_at_num1; + AT_NUMB *sn_orig_at_num2; + + + if ( !bFindCumuleneChain( at, (AT_NUMB)i1, (AT_NUMB)i2, nCumulene, len ) ) { + return RI_ERR_SYNTAX; /* not an allene */ + } + /* stereo bond neighbors: index of a stereo bond in its end-atom adjacency lists */ + if ( (p1 = is_in_the_list( at[i1].neighbor, nCumulene[1], at[i1].valence )) && + (p2 = is_in_the_list( at[i2].neighbor, nCumulene[len-1], at[i2].valence )) ) { + sb_ord_m1 = (int) (p1 - at[i1].neighbor); /* indes of stereobond in the atom's adjacency list */ + sb_ord_m2 = (int) (p2 - at[i2].neighbor); + } else { + return RI_ERR_PROGR; + } + num_neigh1 = at[i1].valence + at[i1].num_H; + num_neigh2 = at[i2].valence + at[i2].num_H; + + if ( num_neigh1 < MIN_NUM_STEREO_BOND_NEIGH || num_neigh1 > MAX_NUM_STEREO_BOND_NEIGH || + num_neigh2 < MIN_NUM_STEREO_BOND_NEIGH || num_neigh2 > MAX_NUM_STEREO_BOND_NEIGH ) { + return RI_ERR_SYNTAX; + } + + + sb_ord1 = st? st[i1].sb_ord : at[i1].sb_ord; + sb_ord2 = st? st[i2].sb_ord : at[i2].sb_ord; + sb_parity1 = st? st[i1].sb_parity : at[i1].sb_parity; + sb_parity2 = st? st[i2].sb_parity : at[i2].sb_parity; + + /* find the first unoccupied locations in the stereobond 0D descriptor lists; check whether the stereo has already been set */ + for( m1 = k1 = 0; m1 < MAX_NUM_STEREO_BONDS && sb_parity1[m1] && !(k1 = sb_ord1[m1] == sb_ord_m1); m1 ++ ) + ; + for( m2 = k2 = 0; m2 < MAX_NUM_STEREO_BONDS && sb_parity2[m2] && !(k2 = sb_ord2[m2] == sb_ord_m2); m2 ++ ) + ; + if ( m1 == MAX_NUM_STEREO_BONDS || m2 == MAX_NUM_STEREO_BONDS ) { + return RI_ERR_SYNTAX; + } + if ( k1 && k2 ) { + return 0; /* the stereo descriptor of this bond/allene/cumulene has already been set */ + } + if ( k1 || k2 ) { + return RI_ERR_SYNTAX; /* only half of a bond was set */ + } + + sn_ord1 = st? st[i1].sn_ord : at[i1].sn_ord; + sn_ord2 = st? st[i2].sn_ord : at[i2].sn_ord; + sn_orig_at_num1 = st? st[i1].sn_orig_at_num : at[i1].sn_orig_at_num; + sn_orig_at_num2 = st? st[i2].sn_orig_at_num : at[i2].sn_orig_at_num; + + /* stereo bond neighbors connection index */ + sb_ord1[m1] = sb_ord_m1; + sb_ord2[m2] = sb_ord_m2; + /* stereo bond end atom neighbors */ + sn_orig_at_num1[m1] = at[idelH1].orig_at_number; + if ( idelH1 < num_at ) { + if ( p1 = is_in_the_list( at[i1].neighbor, (AT_NUMB)idelH1, at[i1].valence ) ) { + sn_ord1[m1] = (int) (p1 - at[i1].neighbor); + } else { + return RI_ERR_PROGR; + } + } else { + sn_ord1[m1] = -1; + } + + sn_orig_at_num2[m2] = at[idelH2].orig_at_number; + if ( idelH2 < num_at ) { + if ( p2 = is_in_the_list( at[i2].neighbor, (AT_NUMB)idelH2, at[i2].valence ) ) { + sn_ord2[m2] = (int) (p2 - at[i2].neighbor); + } else { + return RI_ERR_PROGR; + } + } else { + sn_ord2[m2] = -1; + } + if ( ATOM_PARITY_WELL_DEF(parity) ) { + /* special case: 2 bonds to sb atom => inverse parity because */ + /* InChI parity refers to the lone pair as a neighbor */ + int num_inv = (num_neigh1 == MIN_NUM_STEREO_BOND_NEIGH) + (num_neigh2 == MIN_NUM_STEREO_BOND_NEIGH); + if ( num_inv % 2 ) { + parity = (parity == AB_PARITY_EVEN)? AB_PARITY_ODD : AB_PARITY_EVEN; + } + parity1 = AB_PARITY_EVEN; + parity2 = (parity == AB_PARITY_EVEN)? AB_PARITY_EVEN : AB_PARITY_ODD; + } else { + parity1 = parity2 = parity; + } + sb_parity1[m1] = parity1; + sb_parity2[m2] = parity2; + + return 0; +} +/****************************************************************************/ +int set_atom_0D_parity( inp_ATOM *at, inp_ATOM_STEREO *st, int num_at, int num_deleted_H, int i1, int parity ) +{ + int m1=0, m2, i, j, tot_num_neigh; + /* the following types must exactly match types in inp_ATOM and inp_ATOM_STEREO */ + /* Given parity from InChI, the order of stereo center neighbors is: */ + /* 1. The stereocenter itself if the total number of neighbors is 3 (not 4) */ + /* 2. Explicit H: non-isotopic, isotopic in order ofascending atomic mass */ + /* Explicit H have already been sorted in this order */ + /* 3. Normal neighboring atoms, atom numbers (=canonical numbers from InChI - 1) in ascending order */ + /* Normal neighboring atoms have already been sorted in this order */ + S_CHAR *p_parity; + AT_NUMB *p_orig_at_num; + + if ( !st || !at[i1].p_parity ) { + m1 = 0; + p_parity = st? &st[i1].p_parity : &at[i1].p_parity; + p_orig_at_num = st? st[i1].p_orig_at_num : at[i1].p_orig_at_num; + + tot_num_neigh = at[i1].valence + at[i1].num_H; + if ( tot_num_neigh == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { + /* only 3 neighbors: the atom itself is the first neighbor */ + p_orig_at_num[m1 ++] = at[i1].orig_at_number; + } else + if ( tot_num_neigh != MAX_NUM_STEREO_ATOM_NEIGH ) { + return RI_ERR_PROGR; /* wrong number of members */ + } + m2 = m1 + (MAX_NUM_STEREO_ATOM_NEIGH - at[i1].valence); + /* stereoneighbors: deleted explicit atoms H first, in order of increasing isotopic mass */ + if ( at[i1].num_H ) { + for ( j = 0; m1 < m2 && j < num_deleted_H; j ++ ) { + if ( at[j + num_at].neighbor[0] == i1 ) { + p_orig_at_num[m1 ++] = at[j + num_at].orig_at_number; + } + } + } + if ( m1 + at[i1].valence != MAX_NUM_STEREO_ATOM_NEIGH ) { + return RI_ERR_PROGR; /* wrong number of members */ + } + /* stereoneighbors: other than explicit H atoms */ + for ( i = 0; i < at[i1].valence; i ++ ) { + m2 = at[i1].neighbor[i]; + p_orig_at_num[m1 ++] = at[m2].orig_at_number; + } + *p_parity = parity; + } + + return 0; +} +#if ( BNS_RAD_SEARCH == 1 ) +/****************************************************************************/ +int MoveRadToAtomsAddCharges( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int forbidden_mask ) +{ + int nNumRad, ret = 0, ret2; + int i, j, k, num_rad_not_atom, num_moved=0, num_candidates= 0, extra_charge=0, added_charge, delta; + BNS_EDGE *pEdge; + BNS_VERTEX *pv; + Vertex v1, v2; + S_SHORT *pnRad = NULL, *pnDelta = NULL; + CC_CAND *pCand = NULL; + int cnBits, bAtomRadRemoved = 0; + + int num_at = pStruct->num_atoms; + int num_deleted_H = pStruct->num_deleted_H; + int len_at = num_at + num_deleted_H; + + for ( i = pBNS->num_atoms, num_rad_not_atom=0; i < pBNS->num_vertices; i ++ ) { + num_rad_not_atom += pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow; + } + if ( !num_rad_not_atom ) { + goto exit_function; + } + /****************************************************/ + /* */ + /* Move radicals from ChargeStruct to atoms */ + /* */ + /****************************************************/ + + /* allocate memory to keep track of moved radicals */ + pnRad = (S_SHORT *) inchi_malloc(pBNS->num_vertices * sizeof(pnRad[0])); + pnDelta = (S_SHORT *)inchi_calloc(pBNS->num_atoms, sizeof(pnDelta[0])); + if ( !pnRad || !pnDelta ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + for ( i = 0; i < pBNS->num_vertices; i ++ ) { + pnRad[i] = pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow; + } + while( 1 ) { + /* remove radicals from atoms */ + for ( i = 0; i < pBNS->num_atoms; i ++ ) { + pnDelta[i] = pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow; + pBNS->vert[i].st_edge.cap -= pnDelta[i]; + bAtomRadRemoved += (0 != pnDelta[i]); + } + ret = SetRadEndpoints( pBNS, pBD, RAD_SRCH_FROM_FICT ); + if ( !ret ) { + break; + } + if ( ret < 0 ) { + goto exit_function; + } + nNumRad = ret; + for ( i = 0; i < nNumRad; i ++ ) { + pEdge = pBNS->edge + pBD->RadEdges[i]; + v1 = pEdge->neighbor1; + v2 = pEdge->neighbor12 ^ v1; + pBNS->vert[v1].st_edge.flow -= pEdge->flow; + pBNS->vert[v2].st_edge.flow -= pEdge->flow; + pBNS->tot_st_flow -= 2*pEdge->flow; + pEdge->flow = 0; + pEdge->forbidden |= forbidden_mask; + pBNS->edge_forbidden_mask |= forbidden_mask; + } + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret < 0 ) { + goto exit_function; + } else { + num_moved += ret; + } + RemoveRadEndpoints( pBNS, pBD, NULL ); + if ( ret == 0 ) { + break; /* could not move more radicals */ + } + if ( bAtomRadRemoved ) { + /* restore radicals to atoms */ + for ( i = 0; i < pBNS->num_atoms; i ++ ) { + pBNS->vert[i].st_edge.cap += pnDelta[i]; + } + bAtomRadRemoved = 0; + } + } + if ( bAtomRadRemoved ) { + /* restore radicals to atoms */ + for ( i = 0; i < pBNS->num_atoms; i ++ ) { + pBNS->vert[i].st_edge.cap += pnDelta[i]; + } + bAtomRadRemoved = 0; + } + pBNS->edge_forbidden_mask &= ~forbidden_mask; + + + /****************************************************/ + /* */ + /* Fix the charges */ + /* */ + /****************************************************/ + if ( num_moved ) { + /* find reqired charge */ + extra_charge = 0; + for ( i = pBNS->num_atoms, pv=pBNS->vert+i; i < pBNS->num_vertices; i ++, pv++ ) { + if ( delta = pv->st_edge.cap - pv->st_edge.flow ) { + if ( IS_BNS_VT_C_OR_CSUPER_GR(pv->type) ) { + extra_charge -= delta; + } else + if ( BNS_VERT_TYPE__AUX == pv->type ) { + extra_charge += delta; + } else { + ret = RI_ERR_PROGR; + goto exit_function; + } + } + } + if ( !extra_charge ) { + goto exit_function; + } + /* find differences */ + num_candidates = 0; + for ( i = 0; i < pBNS->num_vertices; i ++ ) { + pnRad[i] = (pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow) - pnRad[i]; + if ( pnRad[i] > 0 && i < pBNS->num_atoms && !pVA[i].nTautGroupEdge ) { + num_candidates ++; + } + } + } + if ( num_candidates > 0 ) { + pCand = (CC_CAND *)inchi_calloc( num_candidates, sizeof(pCand[0]) ); + if ( !pCand ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + /* create atom */ + memcpy( at2, at, len_at*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + pStruct->at = at; + if ( ret2 < 0 ) { + ret = ret2; + goto exit_function; + } + + for ( i = 0, j = 0; i < pBNS->num_vertices; i ++ ) { + if ( pnRad[i] > 0 && i < pBNS->num_atoms && !pVA[i].nTautGroupEdge ) { + pCand[j].iat = i; + pCand[j].num_bonds = at2[i].valence; + pCand[j].chem_valence = at2[i].chem_bonds_valence; + pCand[j].cMetal = pVA[i].cMetal; + pCand[j].cNumBondsToMetal = pVA[i].cNumBondsToMetal; + pCand[j].cNumValenceElectrons = pVA[i].cNumValenceElectrons; + pCand[j].cPeriodicRowNumber = pVA[i].cPeriodicRowNumber; + pCand[j].el_number = at2[i].el_number; + cnBits = (pVA[i].cnListIndex > 0)? cnList[pVA[i].cnListIndex-1].bits : 0; + while ( cnBits > 0 ) { + pCand[j].cNumChargeStates ++; + cnBits >>= cn_bits_shift; + } + j ++; + } + } + if ( j > 1 ) { + qsort( pCand, j, sizeof(pCand[0]), comp_cc_cand ); + } + added_charge = 0; + + for ( k = 0; k < j; k ++ ) { + int rest_of_charge = extra_charge - added_charge; + int charge_per_left_atom = (abs(rest_of_charge) + j-k - 1)/(j-k); + int this_atom_add_charge = rest_of_charge > 0? charge_per_left_atom : -charge_per_left_atom; + pVA[pCand[k].iat].cInitCharge += this_atom_add_charge; + added_charge += this_atom_add_charge; + if ( this_atom_add_charge ) { + for ( i = pBNS->num_vertices-1, pv = pBNS->vert + i; this_atom_add_charge && pBNS->num_atoms <= i; i --, pv -- ) { + if ( delta = pv->st_edge.cap - pv->st_edge.flow ) { + if ( this_atom_add_charge < 0 && IS_BNS_VT_C_OR_CSUPER_GR(pv->type) ) { + if ( delta + this_atom_add_charge > 0 ) { + delta = -this_atom_add_charge; + } + pv->st_edge.cap -= delta; + pBNS->tot_st_cap -= delta; + this_atom_add_charge += delta; + } else + if ( this_atom_add_charge > 0 && BNS_VERT_TYPE__AUX == pv->type ) { + if ( delta > this_atom_add_charge ) { + delta = this_atom_add_charge; + } + pv->st_edge.cap -= delta; + pBNS->tot_st_cap -= delta; + this_atom_add_charge -= delta; + } + } + } + } + } + } + +exit_function: + if ( pnRad ) { + inchi_free( pnRad ); + } + if ( pnDelta ) { + inchi_free( pnDelta ); + } + if ( pCand ) { + inchi_free( pCand ); + } + return ret; +} +#endif +/**************************************************************************************************/ +typedef struct tagMobileHGroups { + AT_NUMB group_number; + AT_NUMB atom_number; + AT_NUMB atom_type_pVA; + S_CHAR ineigh; + S_CHAR bond_type; + S_CHAR forbidden; + /* S_CHAR el_type;*/ + S_CHAR endpoint_valence; + S_CHAR num_bonds; + S_CHAR bonds_valence; + S_CHAR num_bonds_non_metal; + S_CHAR bonds_valence_non_metal; +} MOBILE_GR; + +typedef struct tagMobileGroupList { + AT_NUMB group_number; + AT_NUMB num; +} MGROUPS; +/**************************************************************************************************/ +int AdjustTgroupsToForbiddenEdges2( BN_STRUCT *pBNS, inp_ATOM *at, VAL_AT *pVA, + int num_atoms, int forbidden_mask ) +{ + int i, j, k; + int centerpoint_type, neigh_type; + int num_changes; + int num_donors, num_acceptors, num_donor_endpoints, num_acceptor_endpoints; + int neigh, tg_number, num_eql_mobile_gr, num_dif_mobile_gr, bond_type, has_mobile_H, has_mobile; + int num_forbidden, ind_forbidden, forbidden, num_N, num_O, num_P, num_S, num_OSt; + int val, delta_val, delta_met, num_bonds_non_metal, bonds_valence_non_metal; + int num_bonds, bonds_valence; + int inv_forbidden_mask = ~forbidden_mask; + MOBILE_GR MobileGr[MAXVAL]; + int num_endpoints; + MGROUPS MGroups[MAXVAL]; + int num_mgroups, num_diff_t_groups; + BNS_EDGE *e, *e1, *e2, *ev, *ev1, *ev2; + BNS_VERTEX *pv1, *pv2; + num_changes = 0; + /* search for possible centerpoints */ + for ( i = 0; i < num_atoms; i ++ ) { + + if ( at[i].chem_bonds_valence == at[i].valence || at[i].num_H || + at[i].endpoint || at[i].charge || at[i].radical || + !is_centerpoint_elem(at[i].el_number) || + !(centerpoint_type = get_pVA_atom_type( pVA, at, i, 0 )) || + 2 > (delta_val = at[i].chem_bonds_valence - (val = get_el_valence(at[i].el_number, 0, 0))) || + 2 > (delta_met = (bonds_valence_non_metal = nNoMetalBondsValence(at, i)) - val ) + ) { + continue; + } + + num_donors = num_acceptors = num_donor_endpoints = num_acceptor_endpoints = 0; + num_mgroups = num_endpoints = num_diff_t_groups = 0; + has_mobile = has_mobile_H = num_eql_mobile_gr = num_dif_mobile_gr = tg_number = 0; + ind_forbidden = -1; + num_forbidden = 0; + num_N = num_O = num_P = num_S = num_OSt = 0; + num_bonds_non_metal = nNoMetalNumBonds(at, i); + bonds_valence = at[i].chem_bonds_valence; + num_bonds = at[i].valence; + + for ( j = 0; j < at[i].valence; j ++ ) { + /* collect neighbors info */ + neigh = at[i].neighbor[j]; + val = get_endpoint_valence( at[neigh].el_number ); + forbidden = pBNS->edge[(int)pBNS->vert[i].iedge[j]].forbidden; + bond_type = (at[i].bond_type[j] & BOND_TYPE_MASK); + neigh_type = get_pVA_atom_type( pVA, at, neigh, bond_type); + if ( !forbidden && !at[neigh].endpoint ) { + /* save forbidden bonds */ + if ( is_el_a_metal(at[neigh].el_number) ) { + continue; + } + switch( bond_type ) { + case BOND_TYPE_SINGLE: + if ( !at[neigh].num_H && at[neigh].charge != -1 ) { + continue; /* not a donor */ + } + break; + case BOND_TYPE_DOUBLE: + if ( !neigh_type ) { + continue; + } + break; + default: + continue; + } + } + + MobileGr[num_endpoints].atom_number = neigh; + MobileGr[num_endpoints].ineigh = j; + MobileGr[num_endpoints].bond_type = bond_type; + MobileGr[num_endpoints].group_number = at[neigh].endpoint; + MobileGr[num_endpoints].endpoint_valence = val; + MobileGr[num_endpoints].forbidden = forbidden; + MobileGr[num_endpoints].atom_type_pVA = neigh_type; + MobileGr[num_endpoints].num_bonds = at[neigh].valence; + MobileGr[num_endpoints].bonds_valence = at[neigh].chem_bonds_valence; + MobileGr[num_endpoints].num_bonds_non_metal = nNoMetalNumBonds(at, neigh); + MobileGr[num_endpoints].bonds_valence_non_metal = nNoMetalBondsValence( at, neigh ); + + if ( forbidden & forbidden_mask ) { + num_forbidden ++; + ind_forbidden = num_endpoints; + } + num_O += 0 != (neigh_type & EL_TYPE_O) && at[neigh].valence == 1; /* ignore -O- */ + num_N += 0 != (neigh_type & EL_TYPE_N) && + !(at[neigh].valence == 3 && at[neigh].chem_bonds_valence == 3); /* ignore -N< */ + num_S += 0 != (neigh_type & EL_TYPE_S) && at[neigh].valence == 1; /* ignore -S- */ + num_P += 0 != (neigh_type & EL_TYPE_P) && + !(at[neigh].valence == 3 && at[neigh].chem_bonds_valence == 3); /* ignore -P< */ + num_OSt += 0 != (neigh_type & EL_TYPE_OSt); + num_acceptors += (bond_type == BOND_TYPE_DOUBLE) && (neigh_type & EL_TYPE_PT); + num_donors += (bond_type == BOND_TYPE_SINGLE) && (neigh_type & EL_TYPE_PT) && + (at[neigh].num_H || at[neigh].charge==-1 || at[neigh].endpoint); + if ( at[neigh].endpoint ) { + num_acceptor_endpoints += (bond_type == BOND_TYPE_DOUBLE); + num_donor_endpoints += (bond_type == BOND_TYPE_SINGLE); + if ( !tg_number ) { + tg_number = at[neigh].endpoint; + num_eql_mobile_gr = 1; + } else + if ( tg_number == at[neigh].endpoint ) { + num_eql_mobile_gr ++; + } else { + num_dif_mobile_gr ++; + } + } else + if ( bond_type == BOND_TYPE_SINGLE && val ) { + if ( at[neigh].endpoint ) { + has_mobile_H |= 1; + has_mobile |= 1; + } else { + has_mobile_H |= (0 != at[neigh].num_H); + has_mobile |= (0 != at[neigh].num_H) || (at[neigh].charge == -1); + } + } + num_endpoints ++; + + if ( at[neigh].endpoint || (neigh_type & EL_TYPE_PT) ) { + for ( k = 0; k < num_mgroups; k ++ ) { + if ( MGroups[k].group_number == at[neigh].endpoint ) { + MGroups[k].num ++; + break; + } + } + if ( k == num_mgroups ) { + MGroups[k].group_number = at[neigh].endpoint; + MGroups[k].num = 1; + num_mgroups ++; + num_diff_t_groups += (0 != at[neigh].endpoint); + } + } + } + if ( !num_acceptors || !num_donors || /* num_acceptors > 2 ||*/ + num_eql_mobile_gr == num_endpoints && !num_forbidden || + !tg_number && !has_mobile_H ) { + continue; /* nothing to do */ + } + +/* case_5_1: */ + /***************** determine the case ************************/ + if ( 3 == num_bonds_non_metal && + 4 == bonds_valence_non_metal && + (centerpoint_type == EL_TYPE_C) && + 2 == num_O && 1 == num_N+num_S && num_OSt && + 1 == num_forbidden && 3 == num_eql_mobile_gr ) { + /******************************************************** + *** InChI Tech. Man., Table 5, case 1 *** + ******************************************************** + 2 + OH OH X = N, S, Se, Te + / / f = fixed bond + e / / tg = Mobile-H vertex + HX---C --> X===C + ev2|| f \\ ev1 f \ + || \\ \ + tg------O OH + 1 + Problem: + XH, O, and O belong to the same Mobile-H group. + Fixed bond prevents the correct structure restoration: + H cannot migrate from X to O because HX-N bond is fixed + Solution: + Move H from X to allow XH-C bond change + (this unfixes the bond, see SetForbiddenEdges(...) ) + *********************************************************/ + int jXH = -1, jO1 = -1, jO2 = -1, n = 0; + for ( j = 0; j < num_endpoints; j ++ ) { + if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_S)) && + (MobileGr[j].forbidden == forbidden_mask) && + MobileGr[j].bond_type == BOND_TYPE_SINGLE && + jXH < 0 ) { + jXH = j; + n ++; + } else + if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_O && + MobileGr[j].num_bonds_non_metal == 1 && + !MobileGr[j].forbidden ) { + if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && jO1 < 0 ) { + jO1 = j; + n ++; + } else + if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && jO2 < 0 ) { + jO2 = j; + n ++; + } + } + } + if ( n != 3 ) { + goto case_5_2; + } + /* XH-C edge */ + e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jXH].ineigh]; + /* C=O edge */ + ev1 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jO1].ineigh]; + /* XH-tg edge */ + ev2 = pBNS->edge + pVA[MobileGr[jXH].atom_number].nTautGroupEdge - 1; + + if ( !ev1->flow || !ev2->flow ) { + goto case_5_2; + } + + /* do not remove forbidden edge bit */ + e->flow ++; + ev1->flow --; + ev2->flow --; + pBNS->vert[ev1->neighbor12 ^ i].st_edge.flow --; + pBNS->vert[ev2->neighbor12 ^ ev2->neighbor1].st_edge.flow --; + pBNS->tot_st_flow -= 2; + num_changes ++; + continue; + } +case_5_2: + /*********************************************************************/ + if ( 3 == num_bonds_non_metal && + 5 == bonds_valence_non_metal && + (centerpoint_type == EL_TYPE_N) && + 2 == num_O && 1 == num_N+num_S && + 1 == num_forbidden && 3 == num_eql_mobile_gr ) { + /******************************************************** + *** InChI Tech. Man., Table 5, case 2 *** + ******************************************************** + + O OH X = N, S, Se, Te + // / f = fixed bond + e // / tg = Mobile-H vertex + HX---N --> X===N + ev2|| f \\ ev1 f \\ + || \\ \\ + tg------O O + + Problem: + XH, O, and O belong to the same Mobile-H group. + Fixed bond prevents the correct structure restoration: + H cannot migrate from X to O because HX-N bond is fixed + Solution: + Move H from X to allow XH-N bond change + (this unfixes the bond, see SetForbiddenEdges(...) ) + *********************************************************/ + int jXH = -1, jO1 = -1, jO2 = -1, n = 0; + for ( j = 0; j < num_endpoints; j ++ ) { + if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_S)) && + (MobileGr[j].forbidden == forbidden_mask) && + MobileGr[j].bond_type == BOND_TYPE_SINGLE && + jXH < 0 ) { + jXH = j; + n ++; + } else + if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_O && + MobileGr[j].bond_type == BOND_TYPE_DOUBLE && + MobileGr[j].num_bonds_non_metal == 1 && + !MobileGr[j].forbidden ) { + if ( jO1 < 0 ) { + jO1 = j; + n ++; + } else + if ( jO2 < 0 ) { + jO2 = j; + n ++; + } + } + } + if ( n != 3 ) { + goto case_5_4; + } + /* XH-N edge */ + e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jXH].ineigh]; + /* N=O edge */ + ev1 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jO1].ineigh]; + /* XH-tg edge */ + ev2 = pBNS->edge + pVA[MobileGr[jXH].atom_number].nTautGroupEdge - 1; + + if ( !ev1->flow || !ev2->flow ) { + goto case_5_4; + } + /* do not remove forbidden edge bit */ + e->flow ++; + ev1->flow --; + ev2->flow --; + pBNS->vert[ev1->neighbor12 ^ i].st_edge.flow --; /* first =O vertex */ + pBNS->vert[ev2->neighbor12 ^ ev2->neighbor1].st_edge.flow --; /* taut group vertex tg */ + pBNS->tot_st_flow -= 2; + num_changes ++; + continue; + } +case_5_4: + /*********************************************************************/ + if ( 3 == num_bonds_non_metal && + 5 == bonds_valence_non_metal && + (centerpoint_type & (EL_TYPE_N | EL_TYPE_P)) && + 1 == num_O+num_S && 0 < num_N && 2 == (num_N + num_P) && + 1 == num_forbidden && num_O+num_S+num_N == num_eql_mobile_gr ) { + /******************************************************** + *** InChI Tech. Man., Table 5, case 4 *** + ******************************************************** + O = O, S, Se, Te + X X X = N, P, As + // // f = fixed bond + e // ev2 // tg = Mobile-H vertex + O===N --> HO---N + || f \ ev1 f \\ + || \ \\ + tg------NH N + + Problem: + O, NH, and possibly X belong to the same Mobile-H group. + Fixed bond prevents the correct structure restoration: + H cannot migrate from NH to O because O=N bond is fixed + Solution: + Move H from NH to O to allow O=N bond change + (this unfixes the bond, see fix_special_bonds(...) ) + *********************************************************/ + int jO = -1, jNH = -1, jX = -1, n = 0; + for ( j = 0; j < num_endpoints; j ++ ) { + if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) && + MobileGr[j].forbidden == forbidden_mask && + MobileGr[j].bond_type == BOND_TYPE_DOUBLE && + MobileGr[j].num_bonds_non_metal == 1 && + jO < 0 ) { + jO = j; + n ++; + } else + if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_P)) && + !MobileGr[j].forbidden ) { + if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && + (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N && jNH < 0 ) { + jNH = j; + n ++; + } else + if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && jX < 0 ) { + jX = j; + n ++; + } + } + } + if ( n != 3 ) { + goto case_5_6; + } + /* O=N edge */ + e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jO].ineigh]; + /* N-NH edge */ + ev1 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jNH].ineigh]; + /* N=X edge */ + ev2 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jX].ineigh]; + + if ( !e->flow ) { + goto case_5_6; + } + /* do not remove forbidden edge bit */ + e->flow --; + ev1->flow ++; + pBNS->vert[e->neighbor12 ^ i].st_edge.flow --; + pBNS->vert[ev1->neighbor12 ^ i].st_edge.flow --; + pBNS->tot_st_flow -= 2; + num_changes ++; + continue; + } +case_5_6: + /********* InChI Tech.Man. Table 5, case 6 **************/ + if ( 2 == delta_met && 4 == num_bonds_non_metal && + 5 == bonds_valence_non_metal && + 1 == num_forbidden && 1 < num_eql_mobile_gr && + !num_dif_mobile_gr && + (centerpoint_type & (EL_TYPE_N | EL_TYPE_P)) && + 1 <= num_N && 2 <= num_N+num_O+num_S && + 1 == num_acceptor_endpoints && 0 < num_donor_endpoints ) { + int jN = -1, njFix = 0, jFix[4], n = 0; + /* centerpoint is N, P, As, Sb + + input output + ----- ------ + end + po- + int + 2 + + X ZH X Z Z=N,O,S,Se,Te [terminal endpoint] + \ | \ || + \| f f \|| + Y---N===N--- Y---N---NH--- + e f + cen end no bond + ter po- fixed + po- int + int 1 tautomerism O==N--NH is allowed + + Problem: OH and =N- belong to a Mobile-H group, but + forbidden edge e does not allow them to be + tautomeric in the restored structure. + + Solution: + + 1. Decrement flow in edge e + 2. Decrement st_edge flow in N and N connected by e + 3. Fix all single order bonds to not terminal tautomeric N around N(centerpoint) + 4. Run BNS to establist new flow distribution + */ + /* fixed bond */ + for ( j = 0; j < num_endpoints; j ++ ) { + neigh = MobileGr[j].atom_number; + if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && + (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N && + MobileGr[j].num_bonds_non_metal == 2 && + MobileGr[j].bonds_valence_non_metal == 3 && + at[neigh].endpoint && + !at[neigh].num_H && !at[neigh].charge && + !at[neigh].radical && + (MobileGr[j].forbidden & forbidden_mask) && jN < 0 ) { + jN = j; + n ++; + } else + if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && + at[neigh].endpoint ) { + if ( MobileGr[j].num_bonds > 1 ) { + jFix[njFix ++] = j; + } + n ++; + } + } + + if ( jN < 0 || n < 2 || 1 + njFix == n ) { + goto case_5_7; /* nothing to do */ + } + + e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jN].ineigh]; /* fixed edge */ + if ( !e->flow ) { + goto case_5_7; + } + e->flow --; + pBNS->vert[i].st_edge.flow --; + pBNS->vert[e->neighbor12 ^ i].st_edge.flow --; + pBNS->tot_st_flow -= 2; + + for ( j = 0; j < njFix; j ++ ) { + /* edges to fix */ + ev = pBNS->edge + pBNS->vert[i].iedge[(int)MobileGr[jFix[j]].ineigh]; + ev->forbidden |= forbidden_mask; + } + num_changes ++; + continue; + } +case_5_7: + /*********************************************************************/ + if ( 3 == num_bonds_non_metal && + 4 == bonds_valence_non_metal && + (centerpoint_type == EL_TYPE_S) && + 2 == num_O+num_S && 1 == num_OSt && 1 == num_N && + 1 == num_forbidden && 3 == num_eql_mobile_gr && + MobileGr[ind_forbidden].bond_type == BOND_TYPE_SINGLE ) { + /******************************************************** + *** InChI Tech. Man., Table 5, case 7 *** + ******************************************************** + O = O, S, Se, Te + OH OH S = S, Se, Te + / / f = fixed bond + e /ev2 f / tg = Mobile-H vertex + HN---S --> N===S X = N or non-endpoint; + || f \\ \ + ev2|| \\ev1 \ + tg------O OH + N, O, O + Problem: ======= + O, NH, OH belong to the same Mobile-H group. + Fixed bond prevents the correct structure restoration: + H cannot migrate from NH to O because HN-S bond is fixed + Solution: + Move H from NH to =O to allow HN=S bond change by making a 2nd terminal -OH + (this unfixes the bond, see fix_special_bonds(...) ) + *********************************************************/ + int jO = -1, jNH = -1, jOH = -1, n = 0; + for ( j = 0; j < num_endpoints; j ++ ) { + if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N && + MobileGr[j].forbidden == forbidden_mask && + MobileGr[j].bond_type == BOND_TYPE_SINGLE && + MobileGr[j].num_bonds_non_metal <= 2 && + jNH < 0 ) { + jNH = j; + n ++; + } else + if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) && + !MobileGr[j].forbidden && + MobileGr[j].num_bonds_non_metal == 1 ) { + if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && + jO < 0 ) { + jO = j; + n ++; + } else + if ( jOH < 0 ) { + jOH = j; + n ++; + } + } + } + if ( n != 3 ) { + goto case_5_9a; + } + /* NH-S edge */ + e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jNH].ineigh]; + /* S=O edge */ + ev1 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jO].ineigh]; + /* XH-tg edge */ + ev2 = pBNS->edge + pVA[MobileGr[jNH].atom_number].nTautGroupEdge - 1; + + if ( !ev1->flow || !ev2->flow ) { + goto case_5_9a; + } + + /* do not remove forbidden edge bit */ + e->flow ++; + ev1->flow --; + ev2->flow --; + pBNS->vert[ev1->neighbor12 ^ i].st_edge.flow --; /* first =O vertex */ + pBNS->vert[ev2->neighbor12 ^ ev2->neighbor1].st_edge.flow --; /* taut group vertex tg */ + pBNS->tot_st_flow -= 2; + num_changes ++; + continue; + } +case_5_9a: + /*********************************************************************/ + if ( 3 == num_bonds_non_metal && + 4 == bonds_valence_non_metal && + (centerpoint_type == EL_TYPE_S) && + 1 == num_O+num_S && !num_OSt && 1 <= num_N && + 1 == num_forbidden && + num_O+num_S+num_N == num_eql_mobile_gr && + MobileGr[ind_forbidden].bond_type == BOND_TYPE_SINGLE ) { + /******************************************************** + *** InChI Tech. Man., Table 5, case 9a *** + ******************************************************** + O = O, S, Se, Te + X X S = S, Se, Te + / / f = fixed bond + / / tg = Mobile-H vertex + HN---S --> N===S X = N or non-endpoint; + || \\ e \ -X is not -O(terminal) + || f\\ f\ + tg------O OH + N, N, O or N, O + Problem: ================ + O, NH belong to the same Mobile-H group. + Fixed bond prevents the correct structure restoration: + H cannot migrate from NH to O because O=S bond is fixed + Solution: + Move H from NH to =O to allow O=S bond change by making a terminal -OH + (this unfixes the bond, see fix_special_bonds(...) ) + *********************************************************/ + int jO = -1, jNH = -1, jX = -1, n = 0; + for ( j = 0; j < num_endpoints; j ++ ) { + if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) && + MobileGr[j].forbidden == forbidden_mask && + MobileGr[j].bond_type == BOND_TYPE_DOUBLE && + MobileGr[j].num_bonds_non_metal == 1 && + jO < 0 ) { + jO = j; + n ++; + } else + if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N && + !MobileGr[j].forbidden && + MobileGr[j].bond_type == BOND_TYPE_SINGLE && + jNH < 0 ) { + jNH = j; + n ++; + } else + if ( jX < 0 ) { + jX = j; + n ++; + } + } + if ( jO < 0 || jNH < 0 ) { + goto case_5_8b_to_9b; + } + + e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[ind_forbidden].ineigh]; + if ( !e->flow ) { + goto case_5_8b_to_9b; + } + e->flow --; + pBNS->vert[e->neighbor1].st_edge.flow --; + pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --; + pBNS->tot_st_flow -= 2; + num_changes ++; + continue; + } +case_5_8b_to_9b: /* #1 */ + /*********************************************************************/ + if ( 3 == num_bonds_non_metal && + 4 == bonds_valence_non_metal && + (centerpoint_type == EL_TYPE_S) && + 0 == num_O+num_S && 2 == num_N && 0 == num_P && !num_OSt && + 1 == num_forbidden && + 1 == num_eql_mobile_gr && 0 == num_dif_mobile_gr && + 1 == num_donor_endpoints && 0 == num_acceptor_endpoints && + 1 == num_donors && 1 == num_acceptors && + MobileGr[ind_forbidden].bond_type == BOND_TYPE_SINGLE ) { + /******************************************************** + *** InChI Tech. Man., Table 5, case 8b->9b *** + ******************************************************** + ---> O = O, S, Se, Te + X X S = S, Se, Te + \ f/ \ f/ f = fixed bond + \ ev / C=====Z \ / C-----ZH tg = Mobile-H vertex + N===S | | N===S || || X = is N not an endpoint; + not \ | | \ || || -X is not terminal -O,-S,-Se,-Te or + an \ | e | \|| e || any N, P, As + endpoint NH=====tg N------tg + is an f N, N, X, fixed single + endpoint ===================== + + Problem: + N is not a Mobile-H endpoint, NH is a Mobile-H endpoint + Unfixed bond N==S prevents the correct structure restoration: + H can migrate from NH to N because N=S bond is not fixed + Solution: + Move H from NH to =Z to make N=S bond fixed (Table 5, case 9) + (this unfixes the bond, see fix_special_bonds(...) ) + *********************************************************/ + int jN = -1, jNH = -1, jX = -1, n = 0; + for ( j = 0; j < num_endpoints; j ++ ) { + if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N && + !(MobileGr[j].forbidden & forbidden_mask) ) { + if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && + !at[MobileGr[j].atom_number].endpoint && + jN < 0 ) { + jN = j; + n ++; + } else + if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && + MobileGr[j].num_bonds == 2 && + MobileGr[j].bonds_valence == 2 && + at[MobileGr[j].atom_number].endpoint && + jNH < 0 ) { + jNH = j; + n ++; + } + } else + if ( !((MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_P)) || + (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) && + MobileGr[j].num_bonds > 1 ) && + (MobileGr[j].forbidden & forbidden_mask) && + MobileGr[j].bond_type == BOND_TYPE_SINGLE && + jX < 0 ) { + jX = j; + n ++; + } + } + if ( n != 3 ) { + goto case_5_8c_to_9c; + } + + e = pBNS->edge + pVA[MobileGr[jNH].atom_number].nTautGroupEdge - 1; + if ( !e->flow ) { + goto case_5_8c_to_9c; /* should not happen ??? */ + } + e->flow --; + pBNS->vert[e->neighbor1].st_edge.flow --; + pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --; + pBNS->tot_st_flow -= 2; + e->forbidden |= forbidden_mask; + num_changes ++; + continue; + } +case_5_8c_to_9c: /* #2 */ + /*********************************************************************/ + if ( 3 == num_bonds_non_metal && + 4 == bonds_valence_non_metal && + (centerpoint_type == EL_TYPE_S) && + 0 == num_O+num_S && 3 == num_N && 0 == num_P && + 1 == num_forbidden && + 3 == num_eql_mobile_gr && 0 == num_dif_mobile_gr && + 2 == num_donor_endpoints && 1 == num_acceptor_endpoints && + 2 == num_donors && 1 == num_acceptors && + MobileGr[ind_forbidden].bond_type == BOND_TYPE_SINGLE ) { + /******************************************************** + *** InChI Tech. Man., Table 5, case 8c->9c *** + ******************************************************** + is an endpoint ---> O = O, S, Se, Te + NH2(X) NH2(X) S = S, Se, Te + \ / pv1 pv2 \ / f = fixed bond + \ 1 / C-----ZH \ / C=====Z tg = Mobile-H vertex + N===S || || N===S | | X = is N not an endpoint; + is an \f||ev1 ||ev2 \f | | -X is not terminal -O,-S,-Se,-Te or + endpoint \|| e || \ | e | any N, P, As + 2 N------tg NH=====tg C is a centerpoint of a t-group + is an f N, N, X, fixed single + endpoint ===================== + + Problem: + N is not a Mobile-H endpoint, NH is a Mobile-H endpoint + Unfixed bond N==S prevents the correct structure restoration: + H can migrate from NH to N because N=S bond is not fixed + Solution: + Move H from NH to =Z to make N=S bond fixed (Table 5, case 9) + (this unfixes the bond, see fix_special_bonds(...) ) + *********************************************************/ + int jN1 = -1, jN2 = -1, jX = -1, n = 0; + EdgeIndex ie, ie1, ie2; + for ( j = 0; j < num_endpoints; j ++ ) { + if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N && + !(MobileGr[j].forbidden & forbidden_mask) ) { + if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && + at[MobileGr[j].atom_number].endpoint && + jN1 < 0 ) { + jN1 = j; + n ++; + } else + if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && + MobileGr[j].num_bonds == 2 && + MobileGr[j].bonds_valence == 3 && + MobileGr[j].forbidden == forbidden_mask && + at[MobileGr[j].atom_number].endpoint && + jN2 < 0 ) { + jN2 = j; + n ++; + } else + if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && + MobileGr[j].num_bonds <= 2 && + MobileGr[j].bonds_valence <= 3 && + at[MobileGr[j].atom_number].endpoint && + jX < 0 ) { + jX = j; + n ++; + } + } + } + if ( n != 3 ) { + goto case_5_9b_to_8b; + } + + e = pBNS->edge + pVA[MobileGr[jN2].atom_number].nTautGroupEdge - 1; + if ( e->flow ) { + goto case_5_9b_to_8b; /* should not happen ??? */ + } + pv1 = pBNS->vert + e->neighbor1; /* must be jN2 */ + pv2 = pBNS->vert + (e->neighbor1 ^ e->neighbor12); + ie = (int) (e - pBNS->edge); + ie1 = ie2 = -1; + for ( j = 0; j < pv1->num_adj_edges; j ++ ) { + ev1 = pBNS->edge + pv1->iedge[j]; + if ( ev1->flow && !ev1->forbidden ) { + ie1 = (int) (ev1 - pBNS->edge); + pv1 = pBNS->vert + (ev1->neighbor12 ^ (pv1 - pBNS->vert)); + break; + } + } + for ( j = 0; j < pv2->num_adj_edges; j ++ ) { + ev2 = pBNS->edge + pv2->iedge[j]; + if ( ev2->flow && !ev2->forbidden ) { + ie2 = (int) (ev2 - pBNS->edge); + pv2 = pBNS->vert + (ev2->neighbor12 ^ (pv2 - pBNS->vert)); + break; + } + } + if ( ie1 < 0 || ie2 < 0 ) { + goto case_5_9b_to_8b; + } + e->flow ++; + e->forbidden |= forbidden_mask; + ev1->flow --; + ev2->flow --; + pv1->st_edge.flow --; + pv2->st_edge.flow --; + pBNS->tot_st_flow -= 2; + num_changes ++; + continue; + } +case_5_9b_to_8b: /* #3 */ + /*********************************************************************/ + if ( 3 == num_bonds_non_metal && + 4 == bonds_valence_non_metal && + (centerpoint_type == EL_TYPE_S) && + 0 == num_O+num_S && 2 == num_N && 0 == num_P && + 1 == num_forbidden && + 2 == num_eql_mobile_gr && 0 == num_dif_mobile_gr && + 1 == num_donor_endpoints && 1 == num_acceptor_endpoints && + 1 == num_donors && 1 == num_acceptors && + MobileGr[ind_forbidden].bond_type == BOND_TYPE_DOUBLE ) { + /******************************************************** + *** InChI Tech. Man., Table 5, case 9b->8b *** + ******************************************************** + ---> O = O, S, Se, Te + X is an X S = S, Se, Te + \ / endpoint \ / f = fixed bond + \ 1ev / C-----ZH \ / C=====Z tg = Mobile-H vertex + N===S || || N===S | | X = is N not an endpoint; + is an f \ || || f \ | | -X is not terminal -O,-S,-Se,-Te or + endpoint 2\|| e || \ | e | any N, P, As + N------tg NH=====tg + is an f N, N, X, fixed double + endpoint ===================== + + Problem: + N1 and N2 are Mobile-H endpoints and belong to the same Mobile-H group. + Fixed bond N1==S prevents the correct structure restoration: + H cannot migrate ZH->N2->N1 because N1=S bond is fixed + Solution: + Move H from ZH to N2 to make N1=S bond unfixed and fix S-X bond (Table 5, case 8) + (see fix_special_bonds(...) for details ) + *********************************************************/ + int jN1 = -1, jN2 = -1, jX = -1, n = 0; + EdgeIndex ie, ie1, ie2; + for ( j = 0; j < num_endpoints; j ++ ) { + if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N ) { + if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && + at[MobileGr[j].atom_number].endpoint && + (MobileGr[j].forbidden == forbidden_mask) && + jN1 < 0 ) { + jN1 = j; + n ++; + } else + if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && + MobileGr[j].num_bonds == 2 && + MobileGr[j].bonds_valence == 3 && + at[MobileGr[j].atom_number].endpoint && + !(MobileGr[j].forbidden & forbidden_mask) && + jN2 < 0 ) { + jN2 = j; + n ++; + } + } else + if ( !((MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_P)) || + (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) && + MobileGr[j].num_bonds > 1 ) && + !(MobileGr[j].forbidden & forbidden_mask) && + MobileGr[j].bond_type == BOND_TYPE_SINGLE && + jX < 0 ) { + jX = j; + n ++; + } + } + if ( jN1 < 0 || jN2 < 0 ) { + goto case_5_9c_to_8c; + } + + ev = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jN1].ineigh]; + e = pBNS->edge + pVA[MobileGr[jN2].atom_number].nTautGroupEdge - 1; + if ( e->flow ) { + goto case_5_9c_to_8c; /* should not happen ??? */ + } + pv1 = pBNS->vert + e->neighbor1; /* must be jN2 */ + pv2 = pBNS->vert + (e->neighbor1 ^ e->neighbor12); + ie = (int) (e - pBNS->edge); + ie1 = ie2 = -1; + ev->forbidden &= inv_forbidden_mask; + for ( j = 0; j < pv1->num_adj_edges; j ++ ) { + ev1 = pBNS->edge + pv1->iedge[j]; + if ( ev1->flow && !ev1->forbidden ) { + ie1 = (int) (ev1 - pBNS->edge); + pv1 = pBNS->vert + (ev1->neighbor12 ^ (pv1 - pBNS->vert)); + break; + } + } + for ( j = 0; j < pv2->num_adj_edges; j ++ ) { + ev2 = pBNS->edge + pv2->iedge[j]; + if ( ev2->flow && !ev2->forbidden ) { + ie2 = (int) (ev2 - pBNS->edge); + pv2 = pBNS->vert + (ev2->neighbor12 ^ (pv2 - pBNS->vert)); + break; + } + } + if ( ie1 < 0 || ie2 < 0 ) { + ev->forbidden |= forbidden_mask; /* failed; restore the forbidden bit */ + goto case_5_9c_to_8c; + } + e->flow ++; + e->forbidden |= forbidden_mask; + ev1->flow --; + ev2->flow --; + pv1->st_edge.flow --; + pv2->st_edge.flow --; + pBNS->tot_st_flow -= 2; + num_changes ++; + continue; + } +case_5_9c_to_8c: /* #4 */ + /*********************************************************************/ + if ( 3 == num_bonds_non_metal && + 4 == bonds_valence_non_metal && + (centerpoint_type == EL_TYPE_S) && + 0 == num_O+num_S && 3 == num_N && 0 == num_P && + 0 == num_forbidden && + 2 == num_diff_t_groups && 2 == num_mgroups && /* all neighbors belong to 2 t-groups */ + 3 == num_eql_mobile_gr + num_dif_mobile_gr && /* all 3 neighbors belong to t-groups */ + 2 == num_donor_endpoints && 1 == num_acceptor_endpoints && + 2 == num_donors && 1 == num_acceptors ) { + /******************************************************** + *** InChI Tech. Man., Table 5, case 8c->9c *** + ******************************************************** + is an endpoint ---> O = O, S, Se, Te + tg1 NH2(X) NH2(X) S = S, Se, Te + \ / pv1 pv2 \ / f = fixed bond + \(1) / C=====Z \ / C-----ZH tg = Mobile-H vertex + N===S | | N===S || || X = is N not an endpoint; + is an \ |ev1 | ev2 \ || || -X is not terminal -O,-S,-Se,-Te or + endpoint \ | e | \|| e || any N, P, As + tg1 (2)NH=====tg N------tg C is a centerpoint of a t-group + is an f N, N, X, fixed single + endpoint ===================== + tg2 + Problem: + N (1) and NH2 are Mobile-H group 1 endpoiints, NH (2) is a Mobile-H group 2 endpoint + Unfixed bonds N==S--NH(2) allows the two Mobile H groups to merge + hence prevents the correct structure restoration: + H can migrate from NH (2) to N (1) because S-NH(1) bond is not fixed + Solution: + Move H from NH(2) to =Z to make S-NH(2) bond fixed (Table 5, case 8c) + (this unfixes the bond, see fix_special_bonds(...) ) + *********************************************************/ + int jN1 = -1, jN2 = -1, jX = -1, n = 0; + /* find t-group that is represented by only one neighbor */ + for ( j = 0, k = 0; j < num_mgroups; j ++ ) { + if ( 1 == MGroups[k].num && MGroups[k].group_number ) { + k = MGroups[k].group_number; + break; + } + } + if ( !k ) { + goto case_5_9c_to_9d; + } + for ( j = 0; j < num_endpoints; j ++ ) { + if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N ) { + if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && + at[MobileGr[j].atom_number].endpoint && + at[MobileGr[j].atom_number].endpoint != k && + jN1 < 0 ) { + jN1 = j; + n ++; + } else + if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && + MobileGr[j].num_bonds == 2 && + MobileGr[j].bonds_valence == 2 && + at[MobileGr[j].atom_number].endpoint == k && + jN2 < 0 ) { + jN2 = j; + n ++; + } else + if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && + MobileGr[j].num_bonds <= 2 && + MobileGr[j].bonds_valence <= 3 && + at[MobileGr[j].atom_number].endpoint && + at[MobileGr[j].atom_number].endpoint != k && + jX < 0 ) { + jX = j; + n ++; + } + } + } + if ( n != 3 ) { + goto case_5_9c_to_9d; + } + + e = pBNS->edge + pVA[MobileGr[jN2].atom_number].nTautGroupEdge - 1; + if ( !e->flow ) { + goto case_5_9c_to_9d; /* should not happen ??? */ + } + e->flow --; + pBNS->vert[e->neighbor1].st_edge.flow --; + pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --; + pBNS->tot_st_flow -= 2; + e->forbidden |= forbidden_mask; + num_changes ++; + continue; + } +case_5_9c_to_9d: /* #6 */ + /*********************************************************************/ + if ( 3 == num_bonds_non_metal && + 4 == bonds_valence_non_metal && + (centerpoint_type == EL_TYPE_S) && + 0 == num_O+num_S && 3 == num_N && 0 == num_P && + 0 == num_forbidden && + 3 == num_mgroups && 2 == num_diff_t_groups && + 2 == num_donor_endpoints && 0 == num_acceptor_endpoints && + 2 == num_donors && 1 == num_acceptors ) { + /******************************************************** + *** InChI Tech. Man., Table 5, case 9b->8b *** + ******************************************************** + 3(X) ---> e2| O = O, S, Se, Te + NH---is an N====== S = S, Se, Te + \ / endpoint \ / f = fixed bond + \ 1 / C=====Z \ f / C-----Z tg = Mobile-H vertex + N===S | | N===S || || X = is N not an endpoint; + is an ev \ | | ev \ || || -X is not terminal -O,-S,-Se,-Te or + endpoint 2\ | e1 | \|| e1 || any N, P, As + NH=====tg N------tg + is an f N, N, X, fixed double + endpoint ===================== + + Problem: + N1, N2, and N3 are Mobile-H endpoints and belong to the same Mobile-H group. + Fixed bond N1==S prevents the correct structure restoration: + H cannot migrate N3->N2->N1 because N1=S bond is fixed + Solution: + Move mobile H to N2 and N3 to make N1=S bond unfixed (Table 5, case 9c) + (see fix_special_bonds(...) for details ) + *********************************************************/ + int jN1 = -1, jN2 = -1, jX = -1, n = 0; + for ( j = 0; j < num_endpoints; j ++ ) { + if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N ) { + if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && + !at[MobileGr[j].atom_number].endpoint && + !(MobileGr[j].forbidden & forbidden_mask) && + jN1 < 0 ) { + jN1 = j; + n ++; + } else + if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && + MobileGr[j].num_bonds == 2 && + MobileGr[j].bonds_valence <= 3 && + at[MobileGr[j].atom_number].endpoint && + !(MobileGr[j].forbidden & forbidden_mask) ) { + if ( jN2 < 0 ) { + jN2 = j; + n ++; + } else + if ( jX < 0 ) { + jX = j; + n ++; + } + } + } + } + if ( n != 3 ) { + goto case_end; + } + ev = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jN1].ineigh]; + if ( !e->flow ) { /*@@@ ??? SHOULD BE ev ??? */ + goto case_end; + } + e1 = pBNS->edge + pVA[MobileGr[jN2].atom_number].nTautGroupEdge - 1; + if ( !e1->flow ) { + goto case_end; /* should not happen ??? */ + } + e2 = pBNS->edge + pVA[MobileGr[jX].atom_number].nTautGroupEdge - 1; + if ( !e2->flow ) { + goto case_end; /* should not happen ??? */ + } + /* take care of edge e1 */ + e = e1; + e->flow --; + pBNS->vert[e->neighbor1].st_edge.flow --; + pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --; + pBNS->tot_st_flow -= 2; + e->forbidden |= forbidden_mask; + num_changes ++; + /* take care of edge e2 */ + e = e2; + e->flow --; + pBNS->vert[e->neighbor1].st_edge.flow --; + pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --; + pBNS->tot_st_flow -= 2; + e->forbidden |= forbidden_mask; + num_changes ++; + /* take care of edge ev: do not let it change */ + ev->forbidden |= forbidden_mask; + continue; + } +case_end:; + } +/*exit_function:*/ + return num_changes; +} +/****************************************************************************/ +/* Replace ambiguous neutral (+)edge->flow=0, (-)edge->flow=1 with (+)edge->flow=1, (-)edge->flow=0 */ +/****************************************************************************/ +int RearrangePlusMinusEdgesFlow( BN_STRUCT *pBNS, BN_DATA *pBD, VAL_AT *pVA, + ALL_TC_GROUPS *pTCGroups, int forbidden_edge_mask ) +{ + int ret, ePlus, eMinus; + EDGE_LIST NewlyFixedEdges; + BNS_EDGE *pPlus, *pMinus; + int i, k1, k2, num_found, num_tot, delta, v1, v2; + + ret = 0; + + AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR ); + for ( i = 0, num_found = num_tot = 0; i < pBNS->num_atoms; i ++ ) { + eMinus = pVA[i].nCMinusGroupEdge - 1; + ePlus = pVA[i].nCPlusGroupEdge - 1; + num_tot += (eMinus >= 0) + (ePlus >= 0); + if ( eMinus >= 0 && ePlus >= 0 ) { + pPlus = pBNS->edge + ePlus; + pMinus = pBNS->edge + eMinus; + if ( (k1=pMinus->flow) > 0 && (k2=pPlus->cap-pPlus->flow) > 0 ) { + num_found ++; + } + } + } + if ( !num_found ) { + goto exit_function; + } + if ( ret = AllocEdgeList( &NewlyFixedEdges, num_tot + pBNS->num_bonds ) ) { + goto exit_function; + } + + for ( i = 0, num_found = num_tot = 0; i < pBNS->num_atoms; i ++ ) { + eMinus = pVA[i].nCMinusGroupEdge - 1; + ePlus = pVA[i].nCPlusGroupEdge - 1; + num_tot += (eMinus >= 0) + (ePlus >= 0); + if ( eMinus >= 0 && ePlus >= 0 ) { + pPlus = pBNS->edge + ePlus; + pMinus = pBNS->edge + eMinus; + if ( (k1=pMinus->flow) > 0 && (k2=pPlus->cap - pPlus->flow) > 0 ) { + /* rearrange */ + v1 = pMinus->neighbor1; + v2 = pMinus->neighbor12 ^ v1; + delta = inchi_min(k1,k2); + pMinus->flow -= delta; + pBNS->vert[v1].st_edge.flow -= delta; + pBNS->vert[v2].st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + } + /* fix charges */ + pPlus->forbidden |= forbidden_edge_mask; + pMinus->forbidden |= forbidden_edge_mask; + if ( (ret = AddToEdgeList( &NewlyFixedEdges, eMinus, 0 )) || + (ret = AddToEdgeList( &NewlyFixedEdges, ePlus, 0 ))) { + goto exit_function; + } + } else + if ( eMinus >= 0 ) { + /* fix charges */ + pMinus = pBNS->edge + eMinus; + pMinus->forbidden |= forbidden_edge_mask; + if ( ret = AddToEdgeList( &NewlyFixedEdges, eMinus, 0 )) { + goto exit_function; + } + } else + if ( ePlus >= 0 ) { + /* fix charges */ + pPlus = pBNS->edge + ePlus; + pPlus->forbidden |= forbidden_edge_mask; + if ( ret = AddToEdgeList( &NewlyFixedEdges, ePlus, 0 )) { + goto exit_function; + } + } + } + for ( i = 0; i < pBNS->num_bonds; i ++ ) { + pBNS->edge[i].forbidden |= forbidden_edge_mask; + if ( ret = AddToEdgeList( &NewlyFixedEdges, i, 0 )) { + goto exit_function; + } + } + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask ); + if ( ret < 0 ) { + goto exit_function; + } + +exit_function: + AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE ); + return ret; +} +/****************************************************************************/ +int IncrementZeroOrderBondsToHeteroat( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, + VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, + int forbidden_edge_mask) +{ +#define FIX_BOND_ADD_ALLOC 128 + Vertex vPathStart, vPathEnd; + int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; + BNS_EDGE *pe, *peZero, *peNeighMeigh = NULL, *peMeFlower; + BNS_VERTEX *pMeFlower = NULL, *pNeigh = NULL, *pNeighNeigh=NULL; + + int i, j, k, ret2, ret, bFixedCarbonCharges, num_changes, bSuccess; + int num_at = pStruct->num_atoms; + int num_deleted_H = pStruct->num_deleted_H; + int len_at = num_at + num_deleted_H; + int inv_forbidden_edge_mask = ~forbidden_edge_mask; + Vertex vMeFlower0, vNeigh, vNeighMeigh = NO_VERTEX; + + EDGE_LIST CarbonChargeEdges; + EDGE_LIST NewlyFixedEdges; + + ret = 0; + num_changes = 0; + + bFixedCarbonCharges = 0; + AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR ); + + if ( !pTCGroups->num_metal_atoms || + 0 > (k=pTCGroups->nGroup[TCG_MeFlower0]) || + 0 > (vMeFlower0 = pTCGroups->pTCG[k].nVertexNumber)) { + goto exit_function; + } + + memcpy( at2, at, len_at*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + pStruct->at = at; + if ( ret2 < 0 ) { + ret = ret2; + goto exit_function; + } + for ( i = 0; i < num_at; i ++ ) { + if ( !pVA[i].cMetal || pVA[i].nMetalGroupEdge <= 0 ) { + continue; + } + peMeFlower = pBNS->edge + pVA[i].nMetalGroupEdge-1; + if ( vMeFlower0 != (peMeFlower->neighbor12 ^ i) ) { + ret = RI_ERR_PROGR; + goto exit_function; + } + pMeFlower = pBNS->vert + vMeFlower0; + + for ( j = 0; j < at2[i].valence; j ++ ) { + if ( !peMeFlower->flow ) { + break; /* cannot do anything */ + } + if ( !(at2[i].bond_type[j] & BOND_TYPE_MASK) ) { + /* found a zero order bond */ + if ( !bFixedCarbonCharges ) { + /* do not let carbon atoms get charged */ + if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { + goto exit_function; + } + bFixedCarbonCharges ++; + } + peZero = pBNS->edge + pBNS->vert[i].iedge[j]; + if ( peZero->flow ) { + ret = RI_ERR_PROGR; + goto exit_function; + } + /* fix other edges */ + for ( k = 0; k < at2[i].valence; k ++ ) { + pe = pBNS->edge + pBNS->vert[i].iedge[k]; + if ( pe->flow == 1 && !(pe->forbidden & forbidden_edge_mask) ) { + if ( ret = AddToEdgeList( &NewlyFixedEdges, (int) (pe - pBNS->edge), FIX_BOND_ADD_ALLOC )) + { + goto exit_function; + } + pe->forbidden |= forbidden_edge_mask; + } + } + /* do not create =N(+)= in a ring or #O(+) terminal */ + for ( k = 0; k < num_at; k ++ ) { + if ( !pVA[k].cMetal && pVA[k].cNumValenceElectrons == 5 && + at2[k].valence == 2 && !at2[k].num_H && pVA[k].cMinRingSize <= 6 && + pVA[k].nCPlusGroupEdge > 0 && + (pe=pBNS->edge + pVA[k].nCPlusGroupEdge-1)->flow==1 && + !(pe->forbidden & forbidden_edge_mask)) { + + if ( ret = AddToEdgeList( &NewlyFixedEdges, (int) (pe - pBNS->edge), FIX_BOND_ADD_ALLOC )) + { + goto exit_function; + } + pe->forbidden |= forbidden_edge_mask; + } + } + + /* metal's neighbor connected by a zero-order bond */ + pNeigh = pBNS->vert + (vNeigh = at2[i].neighbor[j]); + /*for ( k = 0; k < pNeigh->num_adj_edges; k ++ )*/ + for ( k = pNeigh->num_adj_edges-1; 0 <= k; k -- ) + { + peNeighMeigh = pBNS->edge + pNeigh->iedge[k]; + if ( !peNeighMeigh->flow ) { + continue; + } + vNeighMeigh = peNeighMeigh->neighbor12 ^ vNeigh; + if ( vNeighMeigh != i && vNeighMeigh != vMeFlower0 ) { + /* metal neighbor's neighbor connected by a not-zero-order bond */ + pNeighNeigh = pBNS->vert + vNeighMeigh; + break; /* found */ + } + } + if ( k < 0 ) { + continue; /* neighbor not found */ + } + peZero->flow ++; + peZero->forbidden |= forbidden_edge_mask; + peMeFlower->flow --; + peNeighMeigh->flow --; + pMeFlower->st_edge.flow --; + pNeighNeigh->st_edge.flow --; + pBNS->tot_st_flow -= 2; + /* test */ + bSuccess = 0; + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == vMeFlower0 && vPathStart == vNeighMeigh || + vPathEnd == vNeighMeigh && vPathStart == vMeFlower0) && abs(nDeltaCharge) <= 2 ) { + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + (*pnNumRunBNS) ++; + *pnTotalDelta += ret; + num_changes ++; + bSuccess = ret; + } + if ( ret = AddToEdgeList( &NewlyFixedEdges, (int) (peZero - pBNS->edge), FIX_BOND_ADD_ALLOC )) + { + goto exit_function; + } + } else { + peZero->flow --; + peZero->forbidden &= inv_forbidden_edge_mask; + peMeFlower->flow ++; + peNeighMeigh->flow ++; + pMeFlower->st_edge.flow ++; + pNeighNeigh->st_edge.flow ++; + pBNS->tot_st_flow += 2; + } + RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask ); + NewlyFixedEdges.num_edges = 0; + if ( bSuccess ) { + /* update at2[] */ + memcpy( at2, at, len_at*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + pStruct->at = at; + if ( ret2 < 0 ) { + ret = ret2; + goto exit_function; + } + } + } + } + } + ret = num_changes; + +exit_function: + RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask ); + AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); + AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE ); + return ret; +} +/*********************************************************************** + NH2 NH2 + \ \ + C==S(+)- => C(+)-S- where NH2 are not tautomeric + / / + NH2 NH2 +************************************************************************/ +int MovePlusFromS2DiaminoCarbon( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, + VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) +{ + int i, j, k, ret, ret2, cur_success; + int delta; + EdgeIndex ePlusS, ePlusC, eMinusC, e; + BNS_VERTEX *pvS, *pvC, *pv1, *pv2; + BNS_EDGE *pePlusS, *pePlusC, *pe1, *pe2, *peCN[3], *peSC, *pe; + Vertex vC, vN; + + int num_at = pStruct->num_atoms; + int num_deleted_H = pStruct->num_deleted_H; + int len_at = num_at + num_deleted_H; + + Vertex vPathStart, vPathEnd, v1, v2; + int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; + + EDGE_LIST AllChargeEdges; + + ret = 0; + cur_success = 0; + + AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); + + memcpy( at2, at, len_at*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + pStruct->at = at; + if ( ret2 < 0 ) { + ret = ret2; + goto exit_function; + } + /* find (NH2)C=S(+) */ + for ( i = 0; i < num_at; i ++ ) { + if ( !pVA[i].cMetal && pVA[i].cNumValenceElectrons == 6 && + at2[i].valence == 2 && + (pvS = pBNS->vert+i)->st_edge.cap == pvS->st_edge.flow && + 0 <= (ePlusS = pVA[i].nCPlusGroupEdge-1) && !(pePlusS=pBNS->edge+ePlusS)->flow && /* S(+) */ + (pe1=pBNS->edge + pvS->iedge[0])->flow + + (pe2=pBNS->edge + pvS->iedge[1])->flow == 1 /* -S(+)= */ && + pVA[vC = (peSC=pe1->flow? pe1 : pe2)->neighbor12 ^ i].cNumValenceElectrons == 4 && + at2[vC].valence == 3 && + 0 <= (ePlusC=pVA[vC].nCPlusGroupEdge-1) && (pePlusC=pBNS->edge+ePlusC)->flow && + !(0 <= (eMinusC=pVA[vC].nCMinusGroupEdge-1) && pBNS->edge[eMinusC].flow ) ) { + /* found >C=S(+)- */ + pvC = pBNS->vert + vC; + for ( j = k = 0; j < at[vC].valence; j ++ ) { + if ( peSC != (peCN[k] = pBNS->edge + pvC->iedge[j]) && !peCN[k]->flow ) { + k ++; /* a single bond from C */ + } + } + if ( k != 2 ) { + continue; + } + for ( j = 0; j < k; j ++ ) { + vN = peCN[j]->neighbor12 ^ vC; + if ( pVA[vN].cNumValenceElectrons != 5 || + pBNS->vert[vN].st_edge.cap != pBNS->vert[vN].st_edge.flow || + at2[vN].num_H != 2 || + at2[vN].endpoint || (pStruct->endpoint && pStruct->endpoint[vN]) ) { + break; /* does not fit the pattern */ + } + } + if ( j != k ) { + continue; + } + /* fix all charges */ + if ( !AllChargeEdges.num_edges ) { + for ( j = 0; j < num_at; j ++ ) { + if ( 0 <= (e = pVA[j].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && + (ret = AddToEdgeList( &AllChargeEdges, e, 2*num_at )) ) { + goto exit_function; + } + if ( 0 <= (e = pVA[j].nCMinusGroupEdge-1) && !pBNS->edge[e].forbidden && + (ret = AddToEdgeList( &AllChargeEdges, e, 2*num_at )) ) { + goto exit_function; + } + } + } + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + pePlusS->forbidden &= ~forbidden_edge_mask; + pe = pePlusC; + if ( !pe->flow ) + continue; + delta = 1; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { + /* Remover (+)charge from S => nDeltaCharge == -1 */ + /* Flow change on pe (+)charge edge (atom S) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + (*pnNumRunBNS) ++; + cur_success ++; + } + } else { + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } + } +exit_function: + AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); + return ret; +} +/****************************************************************************/ +int EliminateChargeSeparationOnHeteroatoms( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, + VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, + int forbidden_edge_mask, int forbidden_stereo_edge_mask) + /********* Avoid charge separation on heteroatoms ******************/ +{ + int i, j, k, ret, ret2, num_pos, num_neg, num_min=0, bFixedCarbonCharges; + int vPlusSuper; /* (+)super vertex */ + int ePlusSuper; /* edge from vPlusSuper to (+/-) */ + int vPlusMinus; /* (+/-) vertex */ + int nDeltaPlus1, nDeltaMinus1, delta; + BNS_VERTEX *pvPlusSuper, *pvPlusMinus; + BNS_EDGE *pEdge; + + int num_at = pStruct->num_atoms; + int num_deleted_H = pStruct->num_deleted_H; + int len_at = num_at + num_deleted_H; + int inv_forbidden_edge_mask = ~forbidden_edge_mask; + + Vertex vPathStart, vPathEnd; + int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; + + EDGE_LIST FixedLargeRingStereoEdges, CarbonChargeEdges; + + ret = 0; + + AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); + bFixedCarbonCharges = 0; + + if ( forbidden_stereo_edge_mask ) { + for ( i = 0; i < num_at; i ++ ) { + for ( j = 0; j < at2[i].valence; j ++ ) { + if ( pBNS->edge[k = pBNS->vert[i].iedge[j]].forbidden == forbidden_stereo_edge_mask ) { + int nMinRingSize = is_bond_in_Nmax_memb_ring( at2, i, j, pStruct->pbfsq->q, + pStruct->pbfsq->nAtomLevel, + pStruct->pbfsq->cSource, 99 /* max ring size */ ); + if ( 0 < nMinRingSize && (ret = AddToEdgeList( &FixedLargeRingStereoEdges, k, 64 ))) { + goto exit_function; + } + } + } + } + if ( !FixedLargeRingStereoEdges.num_edges ) { + goto exit_function; + } else { + /* allow stereobonds in rings change */ + RemoveForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask ); + } + } + + memcpy( at2, at, len_at*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + pStruct->at = at; + if ( ret2 < 0 ) { + ret = ret2; + goto exit_function; + } + /* count charges */ + num_pos = num_neg = 0; + for ( i = 0; i < num_at; i ++ ) { + if ( !pVA[i].cMetal && !at2[i].radical ) { + num_pos += ( at2[i].charge > 0 ); + num_neg += ( at2[i].charge < 0 ); + } + } + num_min = inchi_min( num_pos, num_neg ); + + + if ( num_min && + (k = pTCGroups->nGroup[TCG_Plus]) >= 0 && + (ePlusSuper = pTCGroups->pTCG[k].nForwardEdge) > 0 && + (vPlusSuper = pTCGroups->pTCG[k].nVertexNumber) >= num_at && + !(pEdge=pBNS->edge + ePlusSuper)->forbidden ) { + + vPlusMinus = pEdge->neighbor12 ^ vPlusSuper; + pvPlusSuper = pBNS->vert + vPlusSuper; + pvPlusMinus = pBNS->vert + vPlusMinus; + num_min = inchi_min( num_min, pEdge->flow ); + nDeltaPlus1 = pvPlusSuper->st_edge.cap - pvPlusSuper->st_edge.flow; + nDeltaMinus1 = pvPlusMinus->st_edge.cap - pvPlusMinus->st_edge.flow; + if ( num_min && (!nDeltaPlus1 && !nDeltaMinus1 ) ) { + if ( !bFixedCarbonCharges ) { /* 02-02-2006 */ + /* do not let carbon atoms get charged */ + if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { + goto exit_function; + } + bFixedCarbonCharges ++; + } + delta = 1; + pEdge->forbidden |= forbidden_edge_mask; + pBNS->edge_forbidden_mask |= forbidden_edge_mask; + for ( i = 0; i < num_min; i += delta ) { + /* cancel 1 pair of charges at a time */ + /* an attempt to cancel all at once may */ + /* convert a pair of N(IV)(+) into a pair of N(V) neutral with total charge reduced by 2 */ + pEdge->flow -= delta; + pvPlusSuper->st_edge.flow -= delta; + pvPlusMinus->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + /* test for charhe cancellation */ + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + if ( ret < 0 ) { + goto exit_function; + } + if ( ret == 1 && (vPathEnd == vPlusSuper && vPathStart == vPlusMinus || + vPathEnd == vPlusMinus && vPathStart == vPlusSuper) && nDeltaCharge < 0 ) { + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + (*pnNumRunBNS) ++; + if ( ret < 0 ) { + goto exit_function; + } else + if ( ret == 1 ) { + *pnTotalDelta += ret; + } else { + ret = RI_ERR_PROGR; + goto exit_function; + } + } else { + pEdge->flow += delta; + pvPlusSuper->st_edge.flow += delta; + pvPlusMinus->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + break; + } + } + num_min -= i; /* how many pairs of charges left */ + pEdge->forbidden &= inv_forbidden_edge_mask; + } + nDeltaPlus1 = pvPlusSuper->st_edge.cap - pvPlusSuper->st_edge.flow; + nDeltaMinus1 = pvPlusMinus->st_edge.cap - pvPlusMinus->st_edge.flow; + if ( num_min > 1 && (!nDeltaPlus1 && !nDeltaMinus1 ) ) { + delta = 2; + pEdge->forbidden |= forbidden_edge_mask; + pBNS->edge_forbidden_mask |= forbidden_edge_mask; + for ( i = 0; i < num_min; i += delta ) { + /* cancel 2 pairs of opposite charges at a time */ + /* 1. test cancellation of a pair of (+) charges */ + pvPlusSuper->st_edge.cap += delta; + pBNS->tot_st_cap += delta; + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + if ( ret < 0 ) { + goto exit_function; + } + pvPlusSuper->st_edge.cap -= delta; + pBNS->tot_st_cap -= delta; + if ( ret != 1 || (vPathEnd != vPlusSuper || vPathStart != vPlusSuper) || nDeltaCharge >= 0 ) { + break; + } + /* 2. test cancellation of a pair of (-) charges */ + pvPlusMinus->st_edge.cap += delta; + pBNS->tot_st_cap += delta; + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + if ( ret < 0 ) { + goto exit_function; + } + pvPlusMinus->st_edge.cap -= delta; + pBNS->tot_st_cap -= delta; + if ( ret != 1 || (vPathEnd != vPlusMinus || vPathStart != vPlusMinus) || nDeltaCharge >= 0 ) { + break; + } + /* 3. Actually cancel the pair of charges */ + pEdge->flow -= delta; + pvPlusSuper->st_edge.flow -= delta; + pvPlusMinus->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + (*pnNumRunBNS) ++; + if ( ret < 0 ) { + goto exit_function; + } else + if ( ret == 2 ) { + *pnTotalDelta += ret; + } else { + ret = RI_ERR_PROGR; + goto exit_function; + } + } + num_min -= i; /* how many pairs of charges left */ + pEdge->forbidden &= inv_forbidden_edge_mask; + } + } + memcpy( at2, at, len_at*sizeof(at2[0])); + pStruct->at = at; +exit_function: + if ( bFixedCarbonCharges ) { + RemoveForbiddenEdgeMask(pBNS, &CarbonChargeEdges, forbidden_edge_mask ); + AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); + } + if ( forbidden_stereo_edge_mask && FixedLargeRingStereoEdges.num_edges ) { + SetForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask ); + } + AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_FREE ); + + return ret < 0? ret : num_min; +} +#if (MOVE_CHARGES_FROM_HETEREO_TO_METAL == 1 ) +/********************** not used *************************************************************************/ +int MoveChargeFromHeteroatomsToMetals( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, + VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, + int forbidden_edge_mask) + /********* Avoid charge separation on heteroatoms ******************/ +{ + int i, k, ret, ret2, num_pos, num_neg, num_min; + int vPlusSuper, vMinusSuper; /* (+), (-) super vertices */ + int ePlusSuper, eMinusSuper; /* edges from vPlusSuper or vMinusSuper to (+/-) */ + int vPlMn; /* (+/-) vertex */ + int vPlusHeteroat, vMinusHeteroat; /* (+), (-) heteroatom vertices */ + int ePlusHeteroat, eMinusHeteroat; /* edges from (+) or (-) heteroatom vertex to super (+) or (-) */ + int vPlusCarbons, vMinusCarbons; /* (+), (-) carbons vertices */ + int ePlusCarbons, eMinusCarbons; /* edges from (+), (-) carbons vertices to super (+) or (-) */ + int vPlusMetals, vMinusMetals; /* (+), (-) carbons vertices */ + int ePlusMetals, eMinusMetals; /* edges from (+), (-) carbons vertices to super (+) or (-) */ + int eMinusHeteroToSuper; /* edge (-)vHetero-[eMinusHeteroat]-Y-[eMinusHeteroToSuper]-(-)vPlusSuper */ + int v1, v2; + int nDeltaPlus1, nDeltaMinus1, delta; + BNS_VERTEX *pvPlusSuper, *pvMinusSuper, *pvPlMn; + BNS_VERTEX *pvPlusHeteroat, *pvMinusHeteroat, *pvPlusCarbons, *pvMinusCarbons; + BNS_VERTEX *pvPlusMetals, *pvMinusMetals; + BNS_EDGE *pEdgePlusHeteroat, *pEdgeMinusHeteroat, *pEdgeMinusHeteroToSuper; + BNS_EDGE *pEdgePlusCarbons, *pEdgeMinusCarbons, *pEdgePlusMetals, *pEdgeMinusMetals; + BNS_EDGE *pEdgePlusSuper, *pEdgeMinusSuper; + + int num_at = pStruct->num_atoms; + int num_deleted_H = pStruct->num_deleted_H; + int len_at = num_at + num_deleted_H; + int inv_forbidden_edge_mask = ~forbidden_edge_mask; + + Vertex vPathStart, vPathEnd; + int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; + + + ret = 0; + + memcpy( at2, at, len_at*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + pStruct->at = at; + if ( ret2 < 0 ) { + ret = ret2; + goto exit_function; + } + /* (+) */ + pEdgePlusSuper = NULL; + pvPlusSuper = NULL; + if ( (k = pTCGroups->nGroup[TCG_Plus]) >= 0 && + (ePlusSuper = pTCGroups->pTCG[k].nForwardEdge) > 0 && + (vPlusSuper = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { + pEdgePlusSuper = pBNS->edge + ePlusSuper; + pvPlusSuper = pBNS->vert + vPlusSuper; + } + pEdgePlusCarbons = NULL; + pvPlusCarbons = NULL; + if ( (k = pTCGroups->nGroup[TCG_Plus_C0] ) > 0 && + (ePlusCarbons = pTCGroups->pTCG[k].nForwardEdge) > 0 && + (vPlusCarbons = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { + pEdgePlusCarbons = pBNS->edge + ePlusCarbons; + pvPlusCarbons = pBNS->vert + vPlusCarbons; + } + pEdgePlusHeteroat = NULL; + pvPlusHeteroat = NULL; + if ( (k = pTCGroups->nGroup[TCG_Plus0] ) > 0 && + (ePlusHeteroat = pTCGroups->pTCG[k].nForwardEdge) > 0 && + (vPlusHeteroat = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { + pEdgePlusHeteroat = pBNS->edge + ePlusHeteroat; + pvPlusHeteroat = pBNS->vert + vPlusHeteroat; + } + pEdgePlusMetals = NULL; + pvPlusMetals = NULL; + if ( (k = pTCGroups->nGroup[TCG_Plus_M0] ) > 0 && + (ePlusMetals = pTCGroups->pTCG[k].nForwardEdge) > 0 && + (vPlusMetals = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { + pEdgePlusMetals = pBNS->edge + ePlusMetals; + pvPlusMetals = pBNS->vert + vPlusMetals; + } + /* (-) */ + pEdgeMinusSuper = NULL; + pvMinusSuper = NULL; + if ( (k = pTCGroups->nGroup[TCG_Minus]) >= 0 && + (eMinusSuper = pTCGroups->pTCG[k].nForwardEdge) > 0 && + (vMinusSuper = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { + pEdgeMinusSuper = pBNS->edge + eMinusSuper; + pvMinusSuper = pBNS->vert + vMinusSuper; + } + pEdgeMinusCarbons = NULL; + pvMinusCarbons = NULL; + if ( (k = pTCGroups->nGroup[TCG_Minus_C0] ) > 0 && + (eMinusCarbons = pTCGroups->pTCG[k].nForwardEdge) > 0 && + (vMinusCarbons = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { + pEdgeMinusCarbons = pBNS->edge + eMinusCarbons; + pvMinusCarbons = pBNS->vert + vMinusCarbons; + } + pEdgeMinusHeteroat = NULL; + pvMinusHeteroat = NULL; + pEdgeMinusHeteroToSuper = NULL; + if ( (k = pTCGroups->nGroup[TCG_Minus0] ) > 0 && + (eMinusHeteroat = pTCGroups->pTCG[k].nForwardEdge) > 0 && + (vMinusHeteroat = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { + BNS_VERTEX *pvYMinusHetero; + BNS_EDGE *pe; + int vYMinusHetero; + pEdgeMinusHeteroat = pBNS->edge + eMinusHeteroat; + pvMinusHeteroat = pBNS->vert + vMinusHeteroat; + /* next edge toward (-)super */ + if ( pvMinusSuper ) { + vYMinusHetero = pEdgeMinusHeteroat->neighbor12 ^ vMinusHeteroat; + pvYMinusHetero = pBNS->vert + vYMinusHetero; + for ( i = 0; i < pvYMinusHetero->num_adj_edges; i ++ ) { + pe = pBNS->edge + pvYMinusHetero->iedge[i]; + if ( (pe->neighbor12 ^ vYMinusHetero) == vMinusSuper ) { + pEdgeMinusHeteroToSuper = pe; + eMinusHeteroToSuper = pe - pBNS->edge; + break; + } + } + } + } + pEdgeMinusMetals = NULL; + pvMinusMetals = NULL; + if ( (k = pTCGroups->nGroup[TCG_Minus_M0] ) > 0 && + (eMinusMetals = pTCGroups->pTCG[k].nForwardEdge) > 0 && + (vMinusMetals = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) { + pEdgeMinusMetals = pBNS->edge + eMinusMetals; + pvMinusMetals = pBNS->vert + vMinusMetals; + } + /* (+/-) */ + pvPlMn = NULL; + if ( pEdgePlusSuper ) { + vPlMn = pEdgePlusSuper->neighbor12 ^ vPlusSuper; + pvPlMn = pBNS->vert + vPlMn; + } else + if ( pEdgeMinusSuper ) { + vPlMn = pEdgeMinusSuper->neighbor12 ^ vMinusSuper; + pvPlMn = pBNS->vert + vPlMn; + } + num_pos = num_neg = 0; + /***************************************************************/ + /* Positive Charges */ + /***************************************************************/ + if ( pEdgePlusHeteroat && pEdgePlusMetals ) { + /* count charges */ + for ( i = 0; i < num_at; i ++ ) { + if ( !at2[i].radical && + at2[i].charge > 0 && + (k = pVA[i].nCPlusGroupEdge-1) >= 0 ) { + v1 = pBNS->edge[k].neighbor1; + v2 = pBNS->edge[k].neighbor1 ^ pBNS->edge[k].neighbor12; + if ( v1 == vPlusHeteroat || v2 == vPlusHeteroat ) { + num_pos ++; + } + } + } + /* attempt to move (+) from heteroatoms to metal atoms */ + num_min = inchi_min( num_pos, pEdgePlusHeteroat->flow ); + + nDeltaPlus1 = pvPlusSuper->st_edge.cap - pvPlusSuper->st_edge.flow; + nDeltaMinus1 = pvPlMn->st_edge.cap - pvPlMn->st_edge.flow; + if ( num_min && !nDeltaPlus1 && !nDeltaMinus1 ) { + if ( pEdgePlusSuper ) { + pEdgePlusSuper->forbidden |= forbidden_edge_mask; + } + if ( pEdgeMinusSuper ) { + pEdgeMinusSuper->forbidden |= forbidden_edge_mask; + } + if ( pEdgePlusCarbons ) { + pEdgePlusCarbons->forbidden |= forbidden_edge_mask; + } + if ( pEdgeMinusCarbons ) { + pEdgeMinusCarbons->forbidden |= forbidden_edge_mask; + } + if ( pEdgePlusHeteroat ) { + pEdgePlusHeteroat->forbidden |= forbidden_edge_mask; + } + if ( pEdgeMinusHeteroat ) { + pEdgeMinusHeteroat->forbidden |= forbidden_edge_mask; + } + delta = 1; + for ( i = 0; i < num_min; i += delta ) { + v1 = pEdgePlusHeteroat->neighbor1; + v2 = pEdgePlusHeteroat->neighbor12 ^ v1; + pEdgePlusHeteroat->flow -= delta; + pBNS->vert[v1].st_edge.flow -= delta; + pBNS->vert[v2].st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + /* test for charhe cancellation */ + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + if ( ret < 0 ) { + goto exit_function; + } + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + (*pnNumRunBNS) ++; + if ( ret < 0 ) { + goto exit_function; + } else + if ( ret == 1 ) { + *pnTotalDelta += ret; + } else { + ret = RI_ERR_PROGR; + goto exit_function; + } + } else { + pEdgePlusHeteroat->flow += delta; + pBNS->vert[v1].st_edge.flow += delta; + pBNS->vert[v2].st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + break; + } + } + if ( pEdgePlusSuper ) { + pEdgePlusSuper->forbidden &= inv_forbidden_edge_mask; + } + if ( pEdgeMinusSuper ) { + pEdgeMinusSuper->forbidden &= inv_forbidden_edge_mask; + } + if ( pEdgePlusCarbons ) { + pEdgePlusCarbons->forbidden &= inv_forbidden_edge_mask; + } + if ( pEdgeMinusCarbons ) { + pEdgeMinusCarbons->forbidden &= inv_forbidden_edge_mask; + } + if ( pEdgePlusHeteroat ) { + pEdgePlusHeteroat->forbidden &= inv_forbidden_edge_mask; + } + if ( pEdgeMinusHeteroat ) { + pEdgeMinusHeteroat->forbidden &= inv_forbidden_edge_mask; + } + } + } + /***************************************************************/ + /* Negative Charges */ + /***************************************************************/ + if ( pEdgeMinusHeteroToSuper && pEdgeMinusMetals ) { + /* count charges */ + for ( i = 0; i < num_at; i ++ ) { + if ( !at2[i].radical && + at2[i].charge < 0 && + (k = pVA[i].nCMinusGroupEdge-1) >= 0 ) { + v1 = pBNS->edge[k].neighbor1; + v2 = pBNS->edge[k].neighbor1 ^ pBNS->edge[k].neighbor12; + if ( v1 == vMinusHeteroat || v2 == vMinusHeteroat ) { + num_neg ++; + } + } + } + if ( num_neg ) { + /* attempt to move (+) from heteroatoms to metal atoms */ + num_min = inchi_min( num_neg, pEdgeMinusHeteroToSuper->flow ); + } + + nDeltaPlus1 = pvPlusSuper->st_edge.cap - pvPlusSuper->st_edge.flow; + nDeltaMinus1 = pvPlMn->st_edge.cap - pvPlMn->st_edge.flow; + if ( num_min && !nDeltaPlus1 && !nDeltaMinus1 ) { + if ( pEdgePlusSuper ) { + pEdgePlusSuper->forbidden |= forbidden_edge_mask; + } + if ( pEdgeMinusSuper ) { + pEdgeMinusSuper->forbidden |= forbidden_edge_mask; + } + if ( pEdgePlusCarbons ) { + pEdgePlusCarbons->forbidden |= forbidden_edge_mask; + } + if ( pEdgeMinusCarbons ) { + pEdgeMinusCarbons->forbidden |= forbidden_edge_mask; + } + if ( pEdgePlusHeteroat ) { + pEdgePlusHeteroat->forbidden |= forbidden_edge_mask; + } + if ( pEdgeMinusHeteroToSuper ) { + pEdgeMinusHeteroToSuper->forbidden |= forbidden_edge_mask; + } + delta = 1; + for ( i = 0; i < num_min; i += delta ) { + v1 = pEdgeMinusHeteroToSuper->neighbor1; + v2 = pEdgeMinusHeteroToSuper->neighbor12 ^ v1; + pEdgeMinusHeteroToSuper->flow -= delta; + pBNS->vert[v1].st_edge.flow -= delta; + pBNS->vert[v2].st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + /* test for charhe cancellation */ + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + if ( ret < 0 ) { + goto exit_function; + } + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + (*pnNumRunBNS) ++; + if ( ret < 0 ) { + goto exit_function; + } else + if ( ret == 1 ) { + *pnTotalDelta += ret; + } else { + ret = RI_ERR_PROGR; + goto exit_function; + } + } else { + pEdgeMinusHeteroToSuper->flow += delta; + pBNS->vert[v1].st_edge.flow += delta; + pBNS->vert[v2].st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + break; + } + } + if ( pEdgePlusSuper ) { + pEdgePlusSuper->forbidden &= inv_forbidden_edge_mask; + } + if ( pEdgeMinusSuper ) { + pEdgeMinusSuper->forbidden &= inv_forbidden_edge_mask; + } + if ( pEdgePlusCarbons ) { + pEdgePlusCarbons->forbidden &= inv_forbidden_edge_mask; + } + if ( pEdgeMinusCarbons ) { + pEdgeMinusCarbons->forbidden &= inv_forbidden_edge_mask; + } + if ( pEdgePlusHeteroat ) { + pEdgePlusHeteroat->forbidden &= inv_forbidden_edge_mask; + } + if ( pEdgeMinusHeteroToSuper ) { + pEdgeMinusHeteroToSuper->forbidden &= inv_forbidden_edge_mask; + } + } + } +exit_function: + return ret; +} +#endif +/****************************************************************************/ +int RestoreCyanoGroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) +{ + Vertex vPathStart, vPathEnd; + int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; + BNS_EDGE *pe; + + int i, j, ret2, ret; + int num_at = pStruct->num_atoms; + int num_deleted_H = pStruct->num_deleted_H; + int len_at = num_at + num_deleted_H; + int inv_forbidden_edge_mask = ~forbidden_edge_mask; + Vertex v1, v2; + + EDGE_LIST CarbonChargeEdges; + + ret = 0; + AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); + + memcpy( at2, at, len_at*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + pStruct->at = at; + if ( ret2 < 0 ) { + ret = ret2; + goto exit_function; + } + + for ( i = 0; i < num_at && 0 <= ret; i ++ ) { + if ( at2[i].valence == 1 && + at2[i].num_H == 0 && + at2[i].chem_bonds_valence == 2 && + at2[i].charge == -1 && + at2[i].radical == 0 && + pVA[i].cNumValenceElectrons == 5 && /* terminal N(-)=, P, As, Sb, Bi */ + pVA[i].nCMinusGroupEdge > 0 && + pVA[i].nTautGroupEdge == 0 && + at2[j=at2[i].neighbor[0]].valence == 2 && + at2[j].num_H == 0 && + at2[j].chem_bonds_valence == 4 && + at2[j].charge == 0 && + at2[j].radical == 0 && + pVA[j].cNumValenceElectrons == 4 && /* C or Si or Ge or Sn or Pb */ + pVA[i].cnListIndex > 0 && + cnList[pVA[i].cnListIndex-1].bits == cn_bits_MN ) { + /* found N(-)=C= */ + pe = pBNS->edge + (pVA[i].nCMinusGroupEdge-1); /* N#N(+) triple bond edge */ + + if ( !pe->flow ) { + continue; /* wrong atom ??? Strange... */ + } + v1 = pe->neighbor1; + v2 = pe->neighbor12 ^ v1; + pe->flow --; + pBNS->vert[v1].st_edge.flow --; + pBNS->vert[v2].st_edge.flow --; + pBNS->tot_st_flow -= 2; + pe->forbidden |= forbidden_edge_mask; + + /* do not let carbon atoms get charged */ + if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { + goto exit_function; + } + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + (*pnNumRunBNS) ++; + *pnTotalDelta += ret; + } else { + pe->flow ++; + pBNS->vert[v1].st_edge.flow ++; + pBNS->vert[v2].st_edge.flow ++; + pBNS->tot_st_flow += 2; + } + RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); + + pe->forbidden &= inv_forbidden_edge_mask; /* unmask the edges */ + } + } + +exit_function: + + AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); + return ret; +} +/****************************************************************************/ +int RestoreIsoCyanoGroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) +{ +#define INC_EDGE_LIST 16 + Vertex vPathStart, vPathEnd; + int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms, num_failed, num_success; + BNS_EDGE *pe; + Vertex v1, v2; + + int i, j, ret2, ret, bIsCarbon; + int num_at = pStruct->num_atoms; + int num_deleted_H = pStruct->num_deleted_H; + int len_at = num_at + num_deleted_H; + int inv_forbidden_edge_mask = ~forbidden_edge_mask; + EdgeIndex eNMinusEdge, eNPlusEdge, eNPlusEdge1, eN34Edge; + EdgeIndex eNFlowerEdge1; + + EDGE_LIST CarbonChargeEdges, AllChargeEdges, IsoCyanoCarbonChargeEdges; + + ret = 0; + num_failed = num_success = 0; + AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); /* carbon charge edges */ + AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); /* heteroatom charge edges */ + AllocEdgeList( &IsoCyanoCarbonChargeEdges, EDGE_LIST_CLEAR ); /* C in C(+)#N(+) charge edges */ + + memcpy( at2, at, len_at*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + pStruct->at = at; + if ( ret2 < 0 ) { + ret = ret2; + goto exit_function; + } + /* 1st attempt: take care of C(+)#N(+)- => C(-)#N(+)- and remove 2 negative charges */ + /* This would produce nDeltaCharge = 2 */ + AllocEdgeList( &CarbonChargeEdges, 2*num_at ); + for ( i = 0; i < num_at && 0 <= ret; i ++ ) { + /* accumulate edges for subsequent fixing them */ + bIsCarbon = (pVA[i].cNumValenceElectrons == 4 && pVA[i].cPeriodicRowNumber == 1); + eNFlowerEdge1 = NO_VERTEX; + if ( (eNMinusEdge = pVA[i].nCMinusGroupEdge - 1)>= 0 && + !pBNS->edge[eNMinusEdge].forbidden ) { + if ( bIsCarbon ) { + if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { + goto exit_function; + } + } else + if ( !pVA[i].cMetal && !at2[i].endpoint && at2[i].charge != -1 ) { + if ( ret = AddToEdgeList( &AllChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { + goto exit_function; + } + } + } + if ( (eNPlusEdge = pVA[i].nCPlusGroupEdge - 1)>= 0 && + !pBNS->edge[eNPlusEdge].forbidden ) { + if ( bIsCarbon ) { + if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) { + goto exit_function; + } + } else + if ( !pVA[i].cMetal && !at2[i].endpoint ) { + if ( ret = AddToEdgeList( &AllChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) { + goto exit_function; + } + if ( pVA[i].cNumValenceElectrons == 5 && + NO_VERTEX != (eNFlowerEdge1 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge )) && + !pBNS->edge[eNFlowerEdge1].flow ) { + if ( ret = AddToEdgeList( &AllChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) { + goto exit_function; + } + } + } + } + if ( bIsCarbon && + 0 <= eNMinusEdge && + 0 <= eNPlusEdge && + at2[i].valence == 1 && + at2[i].num_H == 0 && + at2[i].radical == 0 && + !pBNS->edge[eNMinusEdge].forbidden && + pBNS->edge[eNMinusEdge].flow == 0 && + !pBNS->edge[eNPlusEdge].forbidden && + pBNS->edge[eNPlusEdge].flow == 0 && /* found terminal C(+) */ + + at2[j=at2[i].neighbor[0]].valence == 2 && + at2[j].num_H == 0 && + at2[j].radical == 0 && + pVA[j].cNumValenceElectrons == 5 && + (eNPlusEdge1 = pVA[j].nCPlusGroupEdge - 1)>= 0 && + pBNS->edge[eNPlusEdge].flow == 0 ) { /* -N(+)- */ + +#ifdef NEVER /* I have not found a good reason to do this yet */ + /* fix (+) charge on -N(+)- as much as C charges are fixed */ + if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { + goto exit_function; + } + /* fix floer edge to prevent N(V) ??? */ + if ( NO_VERTEX != (eNFlowerEdge1 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge1 )) && + !pBNS->edge[eNFlowerEdge1].flow ) { + if ( ret = AddToEdgeList( &CarbonChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) { + goto exit_function; + } + } +#endif + /* + Carbon(+) Carbon(-) + ChargeStruct: ChargeStruct: + + 5(+C) 5(+C) + / // + 6(-C) 4 6(-C) 4 + \ // \\ / + 3 3 + | | + 2 2 + || || + -C1- -C1- + | | + + 3-6 is (-) Charge Edge; 4-5 is (+) Charge Edge + + To convert the left pattern to the right one: + + We need to release these charge edges and decrement + edge 3-4 flow to change charge from (+) to (-) + + */ + + /* find vertices 4 and 5 */ + v1 = pBNS->edge[eNPlusEdge].neighbor1; /* one of two vertices incident with edge 4-5 */ + v2 = pBNS->edge[eNPlusEdge].neighbor12 ^ v1; + if ( IS_BNS_VT_C_GR(pBNS->vert[v1].type) ) { + /* v1 is 5(+C) */ + Vertex tmp = v1; + v1 = v2; + v2 = tmp; + } + /* v1 should be 4, v2 - 5(+C) */ + if ( !IS_BNS_VT_CHRG_STRUCT(pBNS->vert[v1].type) || pBNS->vert[v1].num_adj_edges != 2 ) { + continue; /* mismatch */ + } + /* find edge 3-4 */ + eN34Edge = pBNS->vert[v1].iedge[pBNS->vert[v1].iedge[0] == eNPlusEdge]; + if ( pBNS->edge[eN34Edge].forbidden || !pBNS->edge[eN34Edge].flow ) { + continue; + } + /* save 3 edges: 6-3, 4-5, and 3-4 in this order */ + if ( ret = AddToEdgeList( &IsoCyanoCarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &IsoCyanoCarbonChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &IsoCyanoCarbonChargeEdges, eN34Edge, INC_EDGE_LIST ) ) { + goto exit_function; + } + } + } + /* 1st attempt: move (-) charges from heteroatoms to C(+) */ + SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &IsoCyanoCarbonChargeEdges, forbidden_edge_mask ); + for ( i = IsoCyanoCarbonChargeEdges.num_edges-3; 0 <= i; i -= 3 ) { + eNMinusEdge = IsoCyanoCarbonChargeEdges.pnEdges[i]; + eNPlusEdge = IsoCyanoCarbonChargeEdges.pnEdges[i+1]; + eN34Edge = IsoCyanoCarbonChargeEdges.pnEdges[i+2]; + + pe = pBNS->edge + eN34Edge; + pe->forbidden |= forbidden_edge_mask; + if ( !pe->flow ) { + continue; /* already done */ + } + + v1 = pe->neighbor1; + v2 = pe->neighbor12 ^ v1; + pe->flow --; + pBNS->vert[v1].st_edge.flow --; + pBNS->vert[v2].st_edge.flow --; + pBNS->tot_st_flow -= 2; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= -2 ) { + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + (*pnNumRunBNS) ++; + *pnTotalDelta += ret; + num_success ++; + } else { + pe->flow ++; + pBNS->vert[v1].st_edge.flow ++; + pBNS->vert[v2].st_edge.flow ++; + pBNS->tot_st_flow += 2; + pe->forbidden &= inv_forbidden_edge_mask; + num_failed ++; + } + } + if ( num_failed ) { + /* relax conditions: allow all heteroatoms to change charge */ + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + for ( i = IsoCyanoCarbonChargeEdges.num_edges-3; 0 <= i; i -= 3 ) { + eNMinusEdge = IsoCyanoCarbonChargeEdges.pnEdges[i]; + eNPlusEdge = IsoCyanoCarbonChargeEdges.pnEdges[i+1]; + eN34Edge = IsoCyanoCarbonChargeEdges.pnEdges[i+2]; + + pe = pBNS->edge + eN34Edge; + pe->forbidden |= forbidden_edge_mask; + if ( !pe->flow ) { + continue; /* already done */ + } + + v1 = pe->neighbor1; + v2 = pe->neighbor12 ^ v1; + pe->flow --; + pBNS->vert[v1].st_edge.flow --; + pBNS->vert[v2].st_edge.flow --; + pBNS->tot_st_flow -= 2; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= 2 ) { + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + (*pnNumRunBNS) ++; + *pnTotalDelta += ret; + num_success ++; + } else { + pe->flow ++; + pBNS->vert[v1].st_edge.flow ++; + pBNS->vert[v2].st_edge.flow ++; + pBNS->tot_st_flow += 2; + pe->forbidden &= inv_forbidden_edge_mask; /* let it change if it wants */ + num_failed ++; + } + } + } + RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &IsoCyanoCarbonChargeEdges, forbidden_edge_mask ); + + +exit_function: + + AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); + AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); + AllocEdgeList( &IsoCyanoCarbonChargeEdges, EDGE_LIST_FREE ); + return ret; +#undef INC_EDGE_LIST +} + +/****************************************************************************/ +int FixMetal_Nminus_Ominus( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) +{ +#define INC_EDGE_LIST 16 + Vertex vPathStart, vPathEnd; + int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; + int num_failed, num_success, n, nDeltaChargeMax, nMetalCharge; + BNS_EDGE *pe; + Vertex v1, v2; + + int i, j, k, ret2, ret; + int num_at = pStruct->num_atoms; + int num_deleted_H = pStruct->num_deleted_H; + int len_at = num_at + num_deleted_H; + int inv_forbidden_edge_mask = ~forbidden_edge_mask; + EdgeIndex e, eNMinusEdge, eNMinusEdge1, eNMinusEdge2, eNPlusEdge2; + + EDGE_LIST AllChargeEdges; + + ret = 0; + num_failed = num_success = 0; + AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); + + memcpy( at2, at, len_at*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + pStruct->at = at; + if ( ret2 < 0 ) { + ret = ret2; + goto exit_function; + } + /* attepmt #1 N#N(+)-N => N(-)=N(+)=N */ + for ( i = 0; i < num_at && 0 <= ret; i ++ ) { + if ( at2[i].valence == 1 && + at2[i].num_H == 0 && + at2[i].radical == 0 && + pVA[i].cNumValenceElectrons == 6 && /* terminal -O */ + (eNMinusEdge = pVA[i].nCMinusGroupEdge - 1)>= 0 && pBNS->edge[eNMinusEdge].flow == 1 && + !pBNS->edge[eNMinusEdge].forbidden && /* terminal O(-) */ + + at2[j=at2[i].neighbor[0]].valence == 2 && + at2[j].num_H == 0 && + at2[j].radical == 0 && + pVA[j].cNumValenceElectrons == 5 && + (eNMinusEdge1 = pVA[j].nCMinusGroupEdge - 1)>= 0 && pBNS->edge[eNMinusEdge1].flow == 1 && + !pBNS->edge[eNMinusEdge1].forbidden && + + pVA[k=at2[j].neighbor[at2[j].neighbor[0]==i]].cMetal && + + (eNMinusEdge2 = pVA[k].nCMinusGroupEdge - 1)>= 0 && + !pBNS->edge[eNMinusEdge2].forbidden && + (eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1)>= 0 && + !pBNS->edge[eNPlusEdge2].forbidden ) { + + /* found M(q)-N(-)-O(-); convert to M(q-2)-N=O */ + + /* find all charge edges to fix */ + if ( 0 == AllChargeEdges.num_edges ) { + for ( n = 0; n < num_at; n ++ ) { + if ( (e = pVA[n].nCMinusGroupEdge - 1)>= 0 && + !pBNS->edge[e].forbidden ) { + if ( ret = AddToEdgeList( &AllChargeEdges, e, num_at ) ) { + goto exit_function; + } + } + if ( (e = pVA[n].nCPlusGroupEdge - 1)>= 0 && + !pBNS->edge[e].forbidden ) { + if ( ret = AddToEdgeList( &AllChargeEdges, e, num_at ) ) { + goto exit_function; + } + if ( pVA[n].cNumValenceElectrons == 6 && + NO_VERTEX != (e = GetChargeFlowerUpperEdge( pBNS, pVA, e )) && + pBNS->edge[e].flow == 0 ) { + if ( ret = AddToEdgeList( &AllChargeEdges, e, num_at ) ) { + goto exit_function; + } + } + } + } + } + + nMetalCharge = (pBNS->edge[eNPlusEdge2].cap - pBNS->edge[eNPlusEdge2].flow) + - pBNS->edge[eNMinusEdge2].flow; + if ( nMetalCharge == 0 ) { + /* change on O is invisible; charge from N(-) goes, charge comes to Metal */ + nDeltaChargeMax = 0; + } else + if ( nMetalCharge == 2 ) { + /* charges on Metal and N disappear */ + nDeltaChargeMax = -2; + } else { + /* charge from N disappears */ + nDeltaChargeMax = -1; + } + + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + pBNS->edge[eNMinusEdge1].forbidden &= inv_forbidden_edge_mask; + pBNS->edge[eNMinusEdge2].forbidden &= inv_forbidden_edge_mask; + pBNS->edge[eNPlusEdge2].forbidden &= inv_forbidden_edge_mask; + + pe = pBNS->edge + eNMinusEdge; /* must be already fixed as a charge edge */ + + v1 = pe->neighbor1; + v2 = pe->neighbor12 ^ v1; + pe->flow --; + pBNS->vert[v1].st_edge.flow --; + pBNS->vert[v2].st_edge.flow --; + pBNS->tot_st_flow -= 2; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) /*&& nDeltaCharge == nDeltaChargeMax*/ ) { + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + (*pnNumRunBNS) ++; + *pnTotalDelta += ret; + num_success ++; + } else { + pe->flow ++; + pBNS->vert[v1].st_edge.flow ++; + pBNS->vert[v2].st_edge.flow ++; + pBNS->tot_st_flow += 2; + num_failed ++; + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } + } + ret = num_success; + +exit_function: + + AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); + + return ret; +#undef INC_EDGE_LIST +} +/****************************************************************************/ +int RestoreNNNgroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) +{ +#define INC_EDGE_LIST 16 + Vertex vPathStart, vPathEnd; + int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms, num_failed, num_success, n, nDeltaChargeMax; + BNS_EDGE *pe; + Vertex v1, v2; + + int i, j, k, ret2, ret; + int num_at = pStruct->num_atoms; + int num_deleted_H = pStruct->num_deleted_H; + int len_at = num_at + num_deleted_H; + int inv_forbidden_edge_mask = ~forbidden_edge_mask; + EdgeIndex eNMinusEdge, eNPlusEdge, eNMinusEdge1, eNPlusEdge1, eNMinusEdge2, eNPlusEdge2; + EdgeIndex eNFlowerEdge1, eNFlowerEdge2; + + EDGE_LIST CarbonChargeEdges, AllChargeEdges, NNNChargeEdges, CurNNNChargeEdges, AllNNNTermAtoms, AllNIIIChargeEdges; + + ret = 0; + num_failed = num_success = 0; + AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &NNNChargeEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &CurNNNChargeEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &AllNNNTermAtoms, EDGE_LIST_CLEAR ); + AllocEdgeList( &AllNIIIChargeEdges, EDGE_LIST_CLEAR ); + + memcpy( at2, at, len_at*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + pStruct->at = at; + if ( ret2 < 0 ) { + ret = ret2; + goto exit_function; + } + /* attepmt #1 N#N(+)-N => N(-)=N(+)=N: naive approach; expecting tp move (-) from some other atom */ + for ( i = 0; i < num_at && 0 <= ret; i ++ ) { + if ( at2[i].valence == 1 && + at2[i].num_H == 0 && + at2[i].chem_bonds_valence == 3 && + at2[i].charge == 0 && + at2[i].radical == 0 && + pVA[i].cNumValenceElectrons == 5 && /* terminal N# */ + (eNMinusEdge = pVA[i].nCMinusGroupEdge - 1)>= 0 && pBNS->edge[eNMinusEdge].flow == 0 && + !pBNS->edge[eNMinusEdge].forbidden && + at2[j=at2[i].neighbor[0]].valence == 2 && + at2[j].num_H == 0 && + at2[j].chem_bonds_valence == 4 && + at2[j].charge == 1 && + at2[j].radical == 0 && + pVA[j].cNumValenceElectrons == 5 && + (eNPlusEdge = pVA[j].nCPlusGroupEdge - 1)>= 0 && pBNS->edge[eNPlusEdge].flow == 0 && + !pBNS->edge[eNPlusEdge].forbidden && + at2[k=at2[j].neighbor[at2[j].neighbor[0]==i]].valence == 2 && + at2[k].num_H == 0 && + at2[k].chem_bonds_valence == 3 && + pVA[k].cNumValenceElectrons == 5 && + (eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1)>= 0 && pBNS->edge[eNPlusEdge2].flow == 1 && + !pBNS->edge[eNPlusEdge2].forbidden && + pVA[i].cnListIndex > 0 && + cnList[pVA[i].cnListIndex-1].bits == cn_bits_MN ) { + /* found N#N(+)-N~ where the last N (at2[k]) may be charged */ + pe = pBNS->edge + pBNS->vert[i].iedge[0]; /* N#N(+) triple bond edge */ + + v1 = pe->neighbor1; + v2 = pe->neighbor12 ^ v1; + pe->flow --; + pBNS->vert[v1].st_edge.flow --; + pBNS->vert[v2].st_edge.flow --; + pBNS->tot_st_flow -= 2; + + pe->forbidden |= forbidden_edge_mask; + pBNS->edge[eNPlusEdge].forbidden |= forbidden_edge_mask; + pBNS->edge[eNPlusEdge2].forbidden |= forbidden_edge_mask; + + + if ( !CarbonChargeEdges.num_edges ) { + /* do not let carbon atoms get charged */ + AllocEdgeList( &CarbonChargeEdges, INC_EDGE_LIST ); + if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { + goto exit_function; + } + } else { + SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); + } + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= 0 ) { + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + (*pnNumRunBNS) ++; + *pnTotalDelta += ret; + num_success ++; + /* fix charges on N(-)=N(+)=N- */ + if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) { + goto exit_function; + } + } else { + pe->flow ++; + pBNS->vert[v1].st_edge.flow ++; + pBNS->vert[v2].st_edge.flow ++; + pBNS->tot_st_flow += 2; + num_failed ++; + } + RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); + + pe->forbidden &= inv_forbidden_edge_mask; + pBNS->edge[eNPlusEdge].forbidden &= inv_forbidden_edge_mask; + pBNS->edge[eNPlusEdge2].forbidden &= inv_forbidden_edge_mask; + } + } + + /* 2nd attempt: take care of N#N(+)-N=-...=N-N(-) */ + /* This would produce nDeltaCharge >= 2 */ + + AllChargeEdges.num_edges = 0; + AllNNNTermAtoms.num_edges = 0; + NNNChargeEdges.num_edges = 0; + AllNIIIChargeEdges.num_edges = 0; + + for ( i = 0; i < num_at && 0 <= ret; i ++ ) { + if ( (eNMinusEdge = pVA[i].nCMinusGroupEdge - 1)>= 0 && + !pBNS->edge[eNMinusEdge].forbidden ) { + if ( ret = AddToEdgeList( &AllChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { + goto exit_function; + } + } else { + eNMinusEdge = -1; + } + if ( (eNPlusEdge = pVA[i].nCPlusGroupEdge - 1)>= 0 && + !pBNS->edge[eNPlusEdge].forbidden ) { + if ( ret = AddToEdgeList( &AllChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) { + goto exit_function; + } + if ( pVA[i].cNumValenceElectrons == 5 && at2[i].valence == 3 && at2[i].chem_bonds_valence == 3) { + if ( ret = AddToEdgeList( &AllNIIIChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) { + goto exit_function; + } + } + /* N flower edge */ + if ( pVA[i].cNumValenceElectrons == 5 && pVA[i].cPeriodicRowNumber == 1 && + NO_VERTEX != (eNFlowerEdge1 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge )) && + pBNS->edge[eNFlowerEdge1].flow == 0 && + ( ret = AddToEdgeList( &AllChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) ) { + goto exit_function; + } + } else { + eNPlusEdge = -1; + } + + if ( 0 <= eNMinusEdge && + at2[i].valence == 1 && + at2[i].num_H == 0 && + at2[i].radical == 0 && + pVA[i].cNumValenceElectrons == 5 && /* terminal N# */ + + at2[j=at2[i].neighbor[0]].valence == 2 && + at2[j].num_H == 0 && + at2[j].radical == 0 && + pVA[j].cNumValenceElectrons == 5 && + (eNMinusEdge1 = pVA[j].nCMinusGroupEdge - 1)>= 0 && + (eNPlusEdge1 = pVA[j].nCPlusGroupEdge - 1)>= 0 && + !pBNS->edge[eNMinusEdge1].forbidden && + !pBNS->edge[eNPlusEdge1].forbidden && + + at2[k=at2[j].neighbor[at2[j].neighbor[0]==i]].valence == 2 && + at2[k].num_H == 0 && + at2[k].radical == 0 && + pVA[k].cNumValenceElectrons == 5 && + (eNMinusEdge2 = pVA[k].nCMinusGroupEdge - 1)>= 0 && + (eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1)>= 0 && + !pBNS->edge[eNMinusEdge2].forbidden && + !pBNS->edge[eNPlusEdge2].forbidden && + + pVA[i].cnListIndex > 0 && + cnList[pVA[i].cnListIndex-1].bits == cn_bits_MN ) { + /* found N#N(+)-N~ or N(-)=N-N= where the last N (at2[k]) may be charged */ + + /* 1. N(-)=N(+)=N- */ + if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */ + pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ + pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { + continue; /* already good */ + } + /* accumulate terminal atoms of all other NNN */ + if ( ret = AddToEdgeList( &AllNNNTermAtoms, i, INC_EDGE_LIST ) ) { + goto exit_function; + } + /* 2. N#N(+)-N= */ + if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ + pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ + pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { + /* unfix (-) edge on terminal N# */ + if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { + goto exit_function; + } + continue; + } + /* 3. N(-)=N-N= */ + if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */ + pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 1 && /* N */ + pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { + /* unfix (+) edge on middle N */ + if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { + goto exit_function; + } + continue; + } + /* 4. N#N(+)-N(-)- */ + if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ + pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ + pBNS->edge[eNMinusEdge2].flow == 1 && pBNS->edge[eNPlusEdge2].flow == 1 /* N(-) */ ) { + /* unfix (-) edge on the 1st and 3rd N */ + if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) { + goto exit_function; + } + continue; + } + /* 5. N#N(+)-N(+)# */ + if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ + pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ + pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 0 /* N(+) */ ) { + /* unfix (-) edge on the 1st and (+) edge on the 3rd N */ + if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) { + goto exit_function; + } + continue; + } + } + } + /* try to fix each NNN */ + for ( n = AllNNNTermAtoms.num_edges-1; 0 <= n; n -- ) { + i = AllNNNTermAtoms.pnEdges[n]; + eNMinusEdge = pVA[i].nCMinusGroupEdge - 1; + /*eNPlusEdge = pVA[i].nCPlusGroupEdge - 1;*/ + j=at2[i].neighbor[0]; + eNMinusEdge1 = pVA[j].nCMinusGroupEdge - 1; + eNPlusEdge1 = pVA[j].nCPlusGroupEdge - 1; + k=at2[j].neighbor[at2[j].neighbor[0]==i]; + eNMinusEdge2 = pVA[k].nCMinusGroupEdge - 1; + eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1; + /*SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );*/ + /* 1. N(-)=N(+)=N- */ + if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */ + pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ + pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { + + RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge ); + RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge1 ); + RemoveFromEdgeListByValue( &NNNChargeEdges, eNPlusEdge1 ); + RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge2 ); + RemoveFromEdgeListByValue( &NNNChargeEdges, eNPlusEdge2 ); + + pe = NULL; + } else /* 2. N#N(+)-N= */ + if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ + pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ + pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { + /* decrement triple bond on terminal N# */ + pe = pBNS->edge + pBNS->vert[i].iedge[0]; + } else + /* 3. N(-)=N-N= */ + if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */ + pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 1 && /* N */ + pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { + /* decrement flow on (+) charge edge of the middle =N- */ + pe = pBNS->edge + eNPlusEdge1; + } else + /* 4. N#N(+)-N(-)- */ + if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ + pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ + pBNS->edge[eNMinusEdge2].flow == 1 && pBNS->edge[eNPlusEdge2].flow == 1 /* N(-) */ ) { + /* decrement triple bond on terminal N# */ + pe = pBNS->edge + pBNS->vert[i].iedge[0]; + } else + /* 5. N#N(+)-N(+)# */ + if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ + pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ + pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 0 /* N(+) */ ) { + /* decrement triple bond on terminal N# */ + pe = pBNS->edge + pBNS->vert[i].iedge[0]; + } else { + pe = NULL; /* unknown case */ + } + if ( pe ) { + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &NNNChargeEdges, forbidden_edge_mask ); + + v1 = pe->neighbor1; + v2 = pe->neighbor12 ^ v1; + pe->flow --; + pBNS->vert[v1].st_edge.flow --; + pBNS->vert[v2].st_edge.flow --; + pBNS->tot_st_flow -= 2; + + pe->forbidden |= forbidden_edge_mask; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) /*&& nDeltaCharge <= 2*/ ) { + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + (*pnNumRunBNS) ++; + *pnTotalDelta += ret; + num_success ++; + /* fix charges on N(-)=N(+)=N- */ + RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge ); + RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge1 ); + RemoveFromEdgeListByValue( &NNNChargeEdges, eNPlusEdge1 ); + RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge2 ); + RemoveFromEdgeListByValue( &NNNChargeEdges, eNPlusEdge2 ); + } else { + pe->flow ++; + pBNS->vert[v1].st_edge.flow ++; + pBNS->vert[v2].st_edge.flow ++; + pBNS->tot_st_flow += 2; + num_failed ++; + } + pe->forbidden &= inv_forbidden_edge_mask; + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } + } + + /* 3rd attempt */ + + /* + AllChargeEdges.num_edges = 0; + AllNNNTermAtoms.num_edges = 0; + NNNChargeEdges.num_edges = 0; + */ + for ( i = 0; i < num_at && 0 <= ret; i ++ ) { + + eNMinusEdge = pVA[i].nCMinusGroupEdge - 1; + /*eNPlusEdge = pVA[i].nCPlusGroupEdge - 1;*/ + + if ( 0 <= eNMinusEdge && + at2[i].valence == 1 && + at2[i].num_H == 0 && + at2[i].radical == 0 && + pVA[i].cNumValenceElectrons == 5 && /* terminal N# */ + + at2[j=at2[i].neighbor[0]].valence == 2 && + at2[j].num_H == 0 && + at2[j].radical == 0 && + pVA[j].cNumValenceElectrons == 5 && + (eNMinusEdge1 = pVA[j].nCMinusGroupEdge - 1)>= 0 && + (eNPlusEdge1 = pVA[j].nCPlusGroupEdge - 1)>= 0 && + !pBNS->edge[eNMinusEdge1].forbidden && + !pBNS->edge[eNPlusEdge1].forbidden && + + at2[k=at2[j].neighbor[at2[j].neighbor[0]==i]].valence == 2 && + at2[k].num_H == 0 && + at2[k].radical == 0 && + pVA[k].cNumValenceElectrons == 5 && + (eNMinusEdge2 = pVA[k].nCMinusGroupEdge - 1)>= 0 && + (eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1)>= 0 && + !pBNS->edge[eNMinusEdge2].forbidden && + !pBNS->edge[eNPlusEdge2].forbidden && + + pVA[i].cnListIndex > 0 && + cnList[pVA[i].cnListIndex-1].bits == cn_bits_MN ) { + + /* found N#N(+)-N~ or N(-)=N-N= where the last N (at2[k]) may be charged */ + NNNChargeEdges.num_edges = 0; + + eNFlowerEdge1 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge1 ); + eNFlowerEdge2 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge2 ); + + /* 1. N(-)=N(+)=N- */ + if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */ + pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ + pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { + /* fix charges on N(-)=N(+)=N- */ + if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) { + goto exit_function; + } + continue; /* already good */ + } + /* 2. N#N(+)-N= */ + if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ + pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ + pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { + /* unfix (-) edge on terminal N# */ + if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) { + goto exit_function; + } + pe = pBNS->edge + pBNS->vert[i].iedge[0]; + nDeltaChargeMax = 0; + nDeltaChargeMax = (num_failed && !num_success && pStruct->nNumRemovedProtonsMobHInChI > 0)? 2 : 0; + } else + /* 3. N(-)=N-N= */ + if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */ + pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 1 && /* N */ + pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) { + /* unfix (+) edge on middle N */ + if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) { + goto exit_function; + } + if ( NO_VERTEX != eNFlowerEdge1 && + ( ret = AddToEdgeList( &NNNChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) ) { + goto exit_function; + } + /* decrement flow on (+) charge edge of the middle =N- */ + pe = pBNS->edge + eNPlusEdge1; + nDeltaChargeMax = 2; + } else + /* 4. N#N(+)-N(-)- */ + if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ + pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ + pBNS->edge[eNMinusEdge2].flow == 1 && pBNS->edge[eNPlusEdge2].flow == 1 /* N(-) */ ) { + /* unfix (-) edge on the 1st and 3rd N */ + if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) { + goto exit_function; + } + /* decrement triple bond on terminal N# */ + pe = pBNS->edge + pBNS->vert[i].iedge[0]; + nDeltaChargeMax = 0; + } else + /* 5. N#N(+)-N(+)# */ + if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */ + pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */ + pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 0 /* N(+) */ ) { + /* unfix (-) edge on the 1st and (+) edge on the 3rd N */ + if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) { + goto exit_function; + } + /* decrement triple bond on terminal N# */ + pe = pBNS->edge + pBNS->vert[i].iedge[0]; + nDeltaChargeMax = 0; + } else { + continue; + } + + if ( NO_VERTEX != eNFlowerEdge1 && !pBNS->edge[eNFlowerEdge1].flow ) { + if ( ret = AddToEdgeList( &NNNChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) { + goto exit_function; + } + } + if ( NO_VERTEX != eNFlowerEdge2 && !pBNS->edge[eNFlowerEdge2].flow ) { + if ( ret = AddToEdgeList( &NNNChargeEdges, eNFlowerEdge2, INC_EDGE_LIST ) ) { + goto exit_function; + } + } + + v1 = pe->neighbor1; + v2 = pe->neighbor12 ^ v1; + pe->flow --; + pBNS->vert[v1].st_edge.flow --; + pBNS->vert[v2].st_edge.flow --; + pBNS->tot_st_flow -= 2; + + pe->forbidden |= forbidden_edge_mask; + + if ( !CarbonChargeEdges.num_edges ) { + /* do not let carbon atoms get charged */ + AllocEdgeList( &CarbonChargeEdges, INC_EDGE_LIST ); + if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { + goto exit_function; + } + } else { + SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); + } + SetForbiddenEdgeMask( pBNS, &NNNChargeEdges, forbidden_edge_mask ); + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= nDeltaChargeMax ) { + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + (*pnNumRunBNS) ++; + *pnTotalDelta += ret; + num_success ++; + /* fix charges on N(-)=N(+)=N- */ + if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) { + goto exit_function; + } + } else { + pe->flow ++; + pBNS->vert[v1].st_edge.flow ++; + pBNS->vert[v2].st_edge.flow ++; + pBNS->tot_st_flow += 2; + num_failed ++; + } + RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &NNNChargeEdges, forbidden_edge_mask ); + pe->forbidden &= inv_forbidden_edge_mask; + /*pBNS->edge[eNPlusEdge].forbidden &= inv_forbidden_edge_mask;*/ /* BC: array index out of range */ + } + } + RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); + + +exit_function: + + AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); + AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); + AllocEdgeList( &NNNChargeEdges, EDGE_LIST_FREE ); + AllocEdgeList( &CurNNNChargeEdges, EDGE_LIST_FREE ); + AllocEdgeList( &AllNNNTermAtoms, EDGE_LIST_FREE ); + AllocEdgeList( &AllNIIIChargeEdges, EDGE_LIST_FREE ); + return ret; +#undef INC_EDGE_LIST +} +/****************************************************************************/ +int EliminateNitrogen5Val3Bonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) +{ + int i, j, k, bForbiddenCarbonCharges, ret2, ret; + int num_at = pStruct->num_atoms; + int num_deleted_H = pStruct->num_deleted_H; + int len_at = num_at + num_deleted_H; + int inv_forbidden_edge_mask = ~forbidden_edge_mask; + EDGE_LIST CarbonChargeEdges; + + ret = 0; + bForbiddenCarbonCharges = 0; + AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); + + memcpy( at2, at, len_at*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + if ( ret2 < 0 ) { + ret = ret2; + goto exit_function; + } + + /* forbid creation of other N(V) atoms */ + /* fix single bonds to metals */ + for ( i = 0; i < num_at; i ++ ) { + if ( pVA[i].cNumValenceElectrons == 5 && + 0 <= (k = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 )) && + 1 == pBNS->edge[k].flow) { + pBNS->edge[k].forbidden |= forbidden_edge_mask; + } else + if ( pVA[i].cMetal ) { + for ( j = 0; j < at2[i].valence; j ++ ) { + if ( BOND_TYPE_SINGLE == (at2[i].bond_type[j] & BOND_TYPE_MASK) ) { + pBNS->edge[pBNS->vert[i].iedge[j]].forbidden |= forbidden_edge_mask; + } + } + } + } + + /*------------------------------------------------------------------------------ + (+) single line => flow = 0 (+)-(Y)=(+)super + 01 // double line => flow = 1 fix-> 01 // <-- fix + 1 --- 0 1 === 0 + \\ // edge eij connects vertices i < j: \ / 02 + 12 2 02 <--- edge number: e02 connects vertices v0 12 2(..) <- double 'radical' + | v0 and v2 | + =N= vertex N has number i =N= + | | + --------------------------------------------------------------------------------*/ + for ( i = 0; i < num_at; i ++ ) { + if ( pVA[i].cNumValenceElectrons == 5 && at2[i].valence == 3 && + at2[i].chem_bonds_valence == 5 && !at2[i].charge && !at2[i].radical && + !(at2[i].endpoint || pStruct->endpoint && pStruct->endpoint[i]) && pVA[i].cnListIndex > 0 && + cnList[pVA[i].cnListIndex-1].bits == cn_bits_NPN && + pVA[i].nCPlusGroupEdge > 0 ) { + + Vertex v, v0 = NO_VERTEX, v1 = NO_VERTEX, v2 = NO_VERTEX; + EdgeIndex iePlus, ie, ie12 = NO_VERTEX, ie02, ie01; + BNS_VERTEX *pv0, *pv1, *pv2 = NULL; + BNS_EDGE *pePlus, *pe, *pe12 = NULL, *pe02 = NULL, *pe01 = NULL; + Vertex vPathStart, vPathEnd; + int nPathLen; + int nDeltaH, nDeltaCharge, nNumVisitedAtoms; + + iePlus = pVA[i].nCPlusGroupEdge - 1; + pePlus = pBNS->edge + iePlus; + + v0 = IS_BNS_VT_C_GR( pBNS->vert[pePlus->neighbor1].type )? + (pePlus->neighbor1 ^ pePlus->neighbor12) : pePlus->neighbor1; + pv0 = pBNS->vert + v0; + for ( j = 0; j < pv0->num_adj_edges; j ++ ) { + ie = pv0->iedge[j]; + if ( ie == iePlus ) { + continue; + } + pe = pBNS->edge + ie; + if ( pe->flow == 1 && v2 == NO_VERTEX ) { + /* 0 - 2, edge 02 */ + v2 = pe->neighbor12 ^ v0; + pv2 = pBNS->vert + v2; + ie02 = ie; + pe02 = pe; + } else + if ( pe->flow == 0 && v1 == NO_VERTEX ) { + /* 0 - 1, edge 01 */ + v1 = pe->neighbor12 ^ v0; + pv1 = pBNS->vert + v2; + ie01 = ie; + pe01 = pe; + } else { + ret = RI_ERR_PROGR; + goto exit_function; + } + } + if ( v1 == NO_VERTEX || v2 == NO_VERTEX ) { + ret = RI_ERR_PROGR; + goto exit_function; + } + for ( j = 0; j < pv2->num_adj_edges; j ++ ) { + ie = pv2->iedge[j]; + pe = pBNS->edge + ie; + v = pe->neighbor12 ^ v2; + if ( v == v0 || v == i ) { + continue; + } else + if ( v == v1 && pe->flow == 1 ) { + /* 1 - 2, edge 12 */ + ie12 = ie; + pe12 = pe; + } else { + ret = RI_ERR_PROGR; + goto exit_function; + } + } + if ( ie12 == NO_VERTEX ) { + ret = RI_ERR_PROGR; + goto exit_function; + } + /* rearrange cap and flow, forbid 2 edges */ + pe01->flow = 1; + pe12->flow = 0; + pe02->flow = 0; + pv2->st_edge.flow -= 2; + pBNS->tot_st_flow -= 2; + pePlus->forbidden |= forbidden_edge_mask; + pe01->forbidden |= forbidden_edge_mask; + + if ( !bForbiddenCarbonCharges ) { + if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { + goto exit_function; + } + bForbiddenCarbonCharges = 1; + } + + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + if ( ret == 1 && vPathEnd == v2 && vPathStart == v2 && nDeltaCharge <= (pVA[i].cNumBondsToMetal? 2:0) ) { + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + } else { + pe01->flow = 0; + pe12->flow = 1; + pe02->flow = 1; + pv2->st_edge.flow += 2; + pBNS->tot_st_flow += 2; + } + pePlus->forbidden &= inv_forbidden_edge_mask; + pe01->forbidden &= inv_forbidden_edge_mask; + + if ( ret < 0 ) { + goto exit_function; + } else + if ( ret ) { + memcpy( at2, at, len_at*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + if ( ret2 < 0 ) { + ret = ret2; + goto exit_function; + } + } + } + } +exit_function: + /* allow creation of other N(V) atoms */ + for ( i = 0; i < num_at; i ++ ) { + if ( pVA[i].cNumValenceElectrons == 5 && + 0 <= (k = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 )) && + 1 == pBNS->edge[k].flow && (pBNS->edge[k].forbidden & forbidden_edge_mask) ) { + pBNS->edge[k].forbidden &= inv_forbidden_edge_mask; + } else + if ( pVA[i].cMetal ) { + for ( j = 0; j < at2[i].valence; j ++ ) { + if ( BOND_TYPE_SINGLE == (at2[i].bond_type[j] & BOND_TYPE_MASK) ) { + pBNS->edge[pBNS->vert[i].iedge[j]].forbidden &= inv_forbidden_edge_mask; + } + } + } + } + RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); + AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); + return ret; +} +/****************************************************************************/ +int Convert_SIV_to_SVI(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) +{ + int i, j, k, neigh, bForbiddenCarbonCharges, nFlowerEdge, delta, ret2, ret; + int num_at = pStruct->num_atoms; + int num_deleted_H = pStruct->num_deleted_H; + int len_at = num_at + num_deleted_H; + int inv_forbidden_edge_mask = ~forbidden_edge_mask; + EDGE_LIST CarbonChargeEdges, FlowerEdgesList; + + ret = 0; + bForbiddenCarbonCharges = 0; + AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &FlowerEdgesList, EDGE_LIST_CLEAR ); + + memcpy( at2, at, len_at*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + if ( ret2 < 0 ) { + ret = ret2; + goto exit_function; + } + + /* forbid creation of other S(IV) atoms */ + /* fix single bonds to metals and (N(IV), flow=1), (S(IV), flow=0) */ + for ( i = 0; i < num_at; i ++ ) { + if ( (pVA[i].cNumValenceElectrons == 5 /* N(IV)*/ || pVA[i].cNumValenceElectrons == 6 /* S(VI)*/) && + 0 <= (k = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 )) && + !pBNS->edge[k].forbidden && + 6 == pVA[i].cNumValenceElectrons + pBNS->edge[k].flow ) { + + pBNS->edge[k].forbidden |= forbidden_edge_mask; + if ( ret = AddToEdgeList( &FlowerEdgesList, k, 64 )) { + goto exit_function; + } + } else + if ( pVA[i].cMetal ) { + for ( j = 0; j < at2[i].valence; j ++ ) { + if ( BOND_TYPE_SINGLE == (at2[i].bond_type[j] & BOND_TYPE_MASK) ) { + + pBNS->edge[k=pBNS->vert[i].iedge[j]].forbidden |= forbidden_edge_mask; + if ( ret = AddToEdgeList( &FlowerEdgesList, k, 64 )) { + goto exit_function; + } + } + } + } else + /* fix bonds to neighbors of S(IV) if they are not O,S,Se,Te with 2 or more bonds */ + /* exactly same if(..) as below */ + if ( pVA[i].cNumValenceElectrons == 6 && at2[i].valence == 4 && + at2[i].chem_bonds_valence == 4 && !at2[i].charge && !at2[i].radical && + !at2[i].endpoint && pVA[i].cnListIndex > 0 && + cnList[pVA[i].cnListIndex-1].bits == cn_bits_NPN && + 0 <= (nFlowerEdge = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 ) ) && + pBNS->edge[nFlowerEdge].flow > 0 ) { + + for ( j = 0; j < at2[i].valence; j ++ ) { + neigh = at2[i].neighbor[j]; + if ( pVA[neigh].cNumValenceElectrons != 6 && at2[neigh].valence > 1 ) { + k = pBNS->vert[i].iedge[j]; + if ( !pBNS->edge[k].forbidden ) { + if ( ret = AddToEdgeList( &FlowerEdgesList, k, 64 )) { + goto exit_function; + } + pBNS->edge[k].forbidden |= forbidden_edge_mask; + } + } + } + } + } + /*------------------------------------------------------------------------------ + example: struct #301, + | | disconnected porphyrin with four -SO3(-) + -S- => =S= + | | + + -------------------------------------------------------------------------------*/ + + /*------------------------------------------------------------------------------- + found: super(+)=(Y) super(+)=(Y) + \ \ + (+) single line => flow = 0 (+) (+) + 01 // double line => flow = 1 fix-> 01 // 01 // + 1 === 0 triple line => flow = 2 (.)1 --- 0(.) ---> 1 --- 0 + \ / edge eij connects vertices i 0 && + cnList[pVA[i].cnListIndex-1].bits == cn_bits_NPN && + /* 01 is nFlowerEdge */ + 0 <= (nFlowerEdge = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 ) ) && + pBNS->edge[nFlowerEdge].flow > 0 ) { + + Vertex v1 = NO_VERTEX, v2 = NO_VERTEX; + BNS_VERTEX *pv1, *pv2; + BNS_EDGE *pe; + Vertex vPathStart, vPathEnd; + int nPathLen; + int nDeltaH, nDeltaCharge, nNumVisitedAtoms; + + if ( !bForbiddenCarbonCharges ) { + if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { + goto exit_function; + } + bForbiddenCarbonCharges = 1; + } + + delta = 1; + pe = pBNS->edge + nFlowerEdge; /* edge 01 */ + pv1 = pBNS->vert + (v1 = pe->neighbor1); /* vertex 0 */ + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); /* vertex 1 */ + + pe->forbidden |= forbidden_edge_mask; + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + if ( ret == 1 && + (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && + nDeltaCharge <= (pVA[i].cNumBondsToMetal? 2:0) ) { + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + } else { + pe->forbidden &= inv_forbidden_edge_mask; + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + if ( ret < 0 ) { + goto exit_function; + } else + if ( ret ) { + memcpy( at2, at, len_at*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + if ( ret2 < 0 ) { + ret = ret2; + goto exit_function; + } + /* store the fixed edge to unfix it upon exit */ + if ( ret = AddToEdgeList( &FlowerEdgesList, nFlowerEdge, 64 )) { + goto exit_function; + } + } + } + } +exit_function: + RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); + AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); + RemoveForbiddenEdgeMask( pBNS, &FlowerEdgesList, forbidden_edge_mask ); + AllocEdgeList( &FlowerEdgesList, EDGE_LIST_FREE ); + return ret; +} +/****************************************************************************************************** + + + =N(+)=O =N-O(-) + => + M(q) M(q+2) + +*******************************************************************************************************/ +int PlusFromDB_N_DB_O_to_Metal(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) +{ + int i, j, k, n, bForbiddenCarbonCharges, delta, ret2, ret, num_NO, num_M; + int num_at = pStruct->num_atoms; + int num_deleted_H = pStruct->num_deleted_H; + int len_at = num_at + num_deleted_H; + int inv_forbidden_edge_mask = ~forbidden_edge_mask; + EDGE_LIST CarbonChargeEdges, NO_ChargeEdgeList, NO_EdgeList; + + Vertex v1, v2; + BNS_VERTEX *pv1, *pv2; + BNS_EDGE *pe; + Vertex vPathStart, vPathEnd; + int nPathLen; + int nDeltaH, nDeltaCharge, nNumVisitedAtoms; + + + if ( !pTCGroups->num_metal_atoms ) + return 0; + + ret = 0; + bForbiddenCarbonCharges = 0; + AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); /* all charges */ + AllocEdgeList( &NO_ChargeEdgeList, EDGE_LIST_CLEAR ); /* charges to be changed */ + AllocEdgeList( &NO_EdgeList, EDGE_LIST_CLEAR ); /* N(+)=O edges */ + + memcpy( at2, at, len_at*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + if ( ret2 < 0 ) { + ret = ret2; + goto exit_function; + } + num_NO = num_M = 0; + /* forbid creation of other S(IV) atoms */ + /* fix single bonds to metals and (N(IV), flow=1), (S(IV), flow=0) */ + for ( i = 0; i < num_at; i ++ ) { + if ( !pVA[i].cMetal ) { + if ( (k = pVA[i].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { + if ( ret = AddToEdgeList( &CarbonChargeEdges, k, 64 ) ) { + goto exit_function; + } + } + if ( (k = pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { + if ( ret = AddToEdgeList( &CarbonChargeEdges, k, 64 ) ) { + goto exit_function; + } + } + } else { + num_M ++; + } + /* + if ( pVA[i].cMetal ) { + if ( (k = pVA[i].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { + if ( ret = AddToEdgeList( &NO_ChargeEdgeList, k, 64 ) ) { + goto exit_function; + } + } + if ( (k = pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { + if ( ret = AddToEdgeList( &NO_ChargeEdgeList, k, 64 ) ) { + goto exit_function; + } + } + } else + */ + if ( !pVA[i].cMetal && + pVA[i].cNumValenceElectrons == 6 && + at2[i].charge == 0 && !at2[i].num_H && + 1 == at2[i].valence && 2 == at2[i].chem_bonds_valence && + pVA[j=at2[i].neighbor[0]].cNumValenceElectrons == 5 && + at2[j].charge == 1 && !at2[j].num_H && + 2 == at2[j].valence && 4 == at2[j].chem_bonds_valence ) { + /* found =N(+)=O */ + if ( (k = pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden /* O */ && + (n = pVA[j].nCPlusGroupEdge -1) >= 0 && !pBNS->edge[j].forbidden /* N */ ) { + if ( (ret = AddToEdgeList( &NO_ChargeEdgeList, k, 64 ) ) || + (ret = AddToEdgeList( &NO_ChargeEdgeList, n, 64 ) ) ) { + goto exit_function; + } + k = pBNS->vert[i].iedge[0]; /* N(+)=O bond */ + if ( !pBNS->edge[k].forbidden ) { + if ( ret = AddToEdgeList( &NO_EdgeList, k, 64 ) ) { + goto exit_function; + } + num_NO ++; + } + } + } + } + if ( num_M && num_NO ) { + SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); + SetForbiddenEdgeMask( pBNS, &NO_EdgeList, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &NO_ChargeEdgeList, forbidden_edge_mask ); + /* now only N(+), O(-) and metal charges are allowed to change */ + for ( i = 0; i < NO_EdgeList.num_edges; i ++ ) { + k = NO_EdgeList.pnEdges[i]; + delta = 1; + pe = pBNS->edge + k; /* edge N(+)=O */ + pv1 = pBNS->vert + (v1 = pe->neighbor1); /* vertex 0 */ + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); /* vertex 1 */ + + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + if ( ret == 1 && + (vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) && + nDeltaCharge == 0 ) { + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + } else { + pe->forbidden &= inv_forbidden_edge_mask; + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + if ( ret < 0 ) { + goto exit_function; + } + } + } +exit_function: + RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &NO_EdgeList, forbidden_edge_mask ); + AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); + AllocEdgeList( &NO_EdgeList, EDGE_LIST_FREE ); + AllocEdgeList( &NO_ChargeEdgeList, EDGE_LIST_FREE ); + return ret; +} +/****************************************************************************/ +int MoveMobileHToAvoidFixedBonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) +{ + int ret2, ret; + int num_at = pStruct->num_atoms; + int num_deleted_H = pStruct->num_deleted_H; + int len_at = num_at + num_deleted_H; + int nNumFixedEdges, nNumAdjEdges; + + ret = 0; + + if ( pTCGroups->num_tgroups ) { + memcpy( at2, at, len_at*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + pStruct->at = at; + if ( ret2 < 0 ) { + ret = ret2; + goto exit_function; + } +#if ( FIND_RING_SYSTEMS == 1 ) + ret2 = MarkRingSystemsInp( at2, num_at, 0 ); + if ( ret2 < 0 ) { + ret = ret2; + goto exit_function; + } +#endif + /* --- forbidden edges --- */ + ret2 = SetForbiddenEdges( pBNS, at2, num_at, forbidden_edge_mask, 0, NULL ); + + if ( ret2 < 0 ) { + ret2 = -(ret + 1); + } + nNumFixedEdges = ret2; + ret = AdjustTgroupsToForbiddenEdges2( pBNS, at2, pVA, num_at, forbidden_edge_mask ); + nNumAdjEdges = ret; + if ( ret ) { + pBNS->edge_forbidden_mask |= forbidden_edge_mask; + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + (*pnNumRunBNS) ++; + if ( ret < 0 ) { + goto exit_function; + } else { + *pnTotalDelta += ret; + } + } + if ( nNumFixedEdges || nNumAdjEdges ) { + /* removes this edge mask from ALL edges */ + RemoveForbiddenBondFlowBits( pBNS, forbidden_edge_mask ); + } + } + +exit_function: + + return ret; +} +/****************************************************************************/ +/* Find and eliminate cases when Mobile H endpoint has radical on it (typical for wrong P(VI)(=O)3OH */ +int RemoveRadFromMobileHEndpoint(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) +{ + int i, num_fixes, tot_num_fixes = 0; + + int ret2, ret; + int num_at = pStruct->num_atoms; + int num_deleted_H = pStruct->num_deleted_H; + int len_at = num_at + num_deleted_H; + + int itg, j, k, n, m; + Vertex vtg1, endpoint0=NO_VERTEX, endpoint1, endpoint2, centerpoint; + Vertex centerpoint_found=NO_VERTEX; + BNS_VERTEX *ptg1, *pEndp0=NULL, *pEndp1, *pEndp2, *pCentp, *pCentp_found, *pEndp2_found=NULL; + BNS_EDGE *etg0=NULL, *etg1, *etg2, *ecp0, *ecp1, *ecp2; + BNS_EDGE *etg1_found=NULL, *ecp0_found=NULL, *ecp1_found=NULL, *ecp2_found=NULL; + int tgroup_number, num_endpoints; + + ret = 0; + + memcpy( at2, at, len_at*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + if ( ret2 < 0 ) { + ret = ret2; + goto exit_function; + } + while ( pBNS->tot_st_cap > pBNS->tot_st_flow && pTCGroups->num_tgroups ) { + num_fixes = 0; + for ( itg = 0; itg < pTCGroups->num_tgroups; itg ++ ) { + pCentp_found=NULL; + tgroup_number = pTCGroups->pTCG[itg].ord_num; + vtg1 = pTCGroups->pTCG[itg].nVertexNumber; /* taut group vertex index */ + ptg1 = pBNS->vert + vtg1; /* taut group vertex */ + num_endpoints = pTCGroups->pTCG[itg].num_edges; + for ( i = 0; i < num_endpoints; i ++ ) { + etg0 = pBNS->edge + ptg1->iedge[i]; /* edge from t-group to endpoint */ + endpoint0 = etg0->neighbor12 ^ vtg1; /* taut endpoint vertex index */ + pEndp0 = pBNS->vert + endpoint0; /* taut endpoint vertex (possible location of mobile H */ + if ( pEndp0->st_edge.cap > pEndp0->st_edge.flow ) { + /* radical endpoint1 has been detected */ + /* find a 1-3 centerpoint that has two or more endpoints */ + /* connected to the t-group vertex by edges with flow>0 and */ + /* to the centerpoint by edges with flow = 0 */ + /* after that: (1) increment etg1 flow to eliminate radical */ + /* (2) increment flow on one of the two other edges to the t-group */ + /* (3) increment st_cap on the found centerpoint */ + /* (4) rerun the BNS and re-create the structure */ + break; + } + } + if ( i == num_endpoints ) { + continue; + } + if ( i < num_endpoints ) { + /* tautomeric endpoint found; traverse its t-group edges */ + for ( j = 0; j < num_endpoints; j ++ ) { + if ( i == j ) { + continue; /* avoid the already found radical endpoint */ + } + etg1 = pBNS->edge + ptg1->iedge[j]; /* another edge from t-group to another endpoinr */ + endpoint1 = etg1->neighbor12 ^ vtg1; /* another endpoint vertex index */ + pEndp1 = pBNS->vert + endpoint1; /* another endpoint vertex */ + if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) { + continue; /* one more radical-endpoint! What is going on here??? */ + } + if ( !etg1->flow ) { + continue; /* avoid enpoints that do not have an attachment */ + } + if ( !(pEndp1->type & BNS_VERT_TYPE_ENDPOINT) ) { + continue; /* should not happen */ + } + /* traverse endpoint1 edges to find a single bond connecting it to the centerpoint */ + for ( k = 0; k < at2[endpoint1].valence; k ++ ) { + ecp1 = pBNS->edge + pEndp1->iedge[k]; + if ( ecp1->flow ) { + continue; + } + centerpoint = ecp1->neighbor12 ^ endpoint1; + pCentp = pBNS->vert + centerpoint; + /* traverse centerpoint edges to find a single bond to the 2nd endpoint */ + for ( n = 0; n < at2[centerpoint].valence; n ++ ) { + ecp2 = pBNS->edge + pCentp->iedge[n]; + if ( ecp2->flow ) { + continue; + } + endpoint2 = ecp2->neighbor12 ^ centerpoint; + if ( endpoint2 <= endpoint1 || !pVA[endpoint2].nTautGroupEdge ) { + continue; /* don't go back: neighbors are in order of ascending ord. numbers */ + } + pEndp2 = pBNS->vert + endpoint2; + if ( !(pEndp2->type & BNS_VERT_TYPE_ENDPOINT) ) { + continue; + } + etg2 = pBNS->edge + pVA[endpoint2].nTautGroupEdge - 1; + if ( !etg2->flow || (etg2->neighbor12 ^ endpoint2) != vtg1 ) { + continue; + } + /* we have found the path: + Endp1 Endp1 + etg1 // \ ecp1 etg1 / \\ ecp1 + etg0 // \ etg0 / \\ + Endp0-----tg1 Centp --> Endp0=====tg1 Centp + ^ \\ / \\ / + radical | etg2 \\ / ecp2 etg2 \\ / ecp2 + Endp2 Endp2 + */ + + /* compare centerpoints */ + if ( !pCentp_found || + /* try to avoid carbons */ + (pVA[centerpoint].cNumValenceElectrons != 4 || + pVA[centerpoint].cPeriodicRowNumber != 1) && + pVA[centerpoint_found].cNumValenceElectrons == 4 && + pVA[centerpoint_found].cPeriodicRowNumber == 1 || + /* try a better non-carbon */ + (pVA[centerpoint].cNumValenceElectrons != 4 || + pVA[centerpoint].cPeriodicRowNumber != 1 ) && + (at[centerpoint].valence > at[centerpoint_found].valence || + at[centerpoint].valence == at[centerpoint_found].valence && + at[centerpoint].el_number > at[centerpoint_found].el_number) ) { + + pCentp_found = pCentp; + etg1_found = etg1; + ecp1_found = ecp1; + centerpoint_found = centerpoint; + break; + } + } + } + } + } + if ( pCentp_found ) { + /* ---- (1) */ + etg0->flow ++; + pEndp0->st_edge.flow ++; + /* ---- (2) */ + etg1_found->flow --; + /* ---- (3) */ + ecp1_found->flow ++; + /* ---- (4) */ + pCentp_found->st_edge.flow ++; + pCentp_found->st_edge.cap ++; + + pBNS->tot_st_flow += 2; + pBNS->tot_st_cap += 1; + pCentp_found = NULL; + num_fixes ++; + tot_num_fixes ++; /* #1 Mob-H */ + continue; + } + + /* 2nd attempt: increment flow in centerpoint---radical_endpint edge */ + if ( i < num_endpoints ) { + /* tautomeric endpoint found; traverse its t-group edges */ + for ( j = 0; j < num_endpoints; j ++ ) { + if ( i == j ) { + continue; /* avoid the found radical endpoint */ + } + etg1 = pBNS->edge + ptg1->iedge[j]; + endpoint1 = etg1->neighbor12 ^ vtg1; + pEndp1 = pBNS->vert + endpoint1; /* another endpoint */ + if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) { + continue; /* one more radical-endpoint! What is going on here??? */ + } + if ( !etg1->flow ) { + continue; /* avoid enpoints that do not have an attachment */ + } + if ( !(pEndp1->type & BNS_VERT_TYPE_ENDPOINT) ) { + continue; /* should not happen */ + } + /* traverse endpoint1 edges to find the edge connecting it to the centerpoint */ + for ( k = 0; k < at2[endpoint1].valence; k ++ ) { + ecp1 = pBNS->edge + pEndp1->iedge[k]; + if ( ecp1->flow ) { + continue; + } + centerpoint = ecp1->neighbor12 ^ endpoint1; + pCentp = pBNS->vert + centerpoint; + if ( pCentp->type & BNS_VERT_TYPE_ENDPOINT ) { + continue; /* do not set another endpoint's valence = an unusual value */ + } + + /* traverse centerpoint edges to find edge connecting it to the endpoint0 */ + ecp2 = NULL; + pEndp2 = NULL; + for ( n = 0; n < at2[centerpoint].valence; n ++ ) { + ecp0 = pBNS->edge + pCentp->iedge[n]; + if ( ecp0->flow ) { + endpoint2 = ecp0->neighbor12 ^ centerpoint; + if ( (pBNS->vert[endpoint2].type & BNS_VERT_TYPE_ENDPOINT) ) { + continue; /* ignore endpoint2 if it is tautomeric endpoint */ + } + /* check whether ecp0 is stereogenic: if it is then we cannot decrement its flow */ + for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[centerpoint].sb_parity[m]; m ++ ) { + if ( at[centerpoint].sb_ord[m] == n ) { + endpoint2 = NO_VERTEX; + break; + } + } + if ( endpoint2 == NO_VERTEX ) { + continue; + } + pEndp2 = pBNS->vert + endpoint2; /* found */ + ecp2 = ecp0; + break; + } + } + for ( n = 0; n < at[centerpoint].valence; n ++ ) { + ecp0 = pBNS->edge + pCentp->iedge[n]; + if ( ecp0->flow ) { + continue; + } + if ( endpoint0 != (ecp0->neighbor12 ^ centerpoint) ) { + continue; + } + /* Found: + Endp2(not endpoint) Endp2(not radical) + || | + ||ecp2 |ecp2 + ecp0 || ecp1 ecp0 | ecp1 + Endp0----Centp----Endp1 Endp0====Centp----Endp1 + ^ \ / --> \ / + radical | \ / \ / + \ / \ / + etg0 \ / etg1 etg0 \ / etg1 + \ / \ / + tg1 tg1 + + */ + + /* compare centerpoints */ + if ( !pCentp_found || + /* try to avoid carbons */ + (pVA[centerpoint].cNumValenceElectrons != 4 || + pVA[centerpoint].cPeriodicRowNumber != 1) && + pVA[centerpoint_found].cNumValenceElectrons == 4 && + pVA[centerpoint_found].cPeriodicRowNumber == 1 || + /* try a better non-carbon */ + (pVA[centerpoint].cNumValenceElectrons != 4 || + pVA[centerpoint].cPeriodicRowNumber != 1 ) && + (at[centerpoint].valence > at[centerpoint_found].valence || + at[centerpoint].valence == at[centerpoint_found].valence && + at[centerpoint].el_number > at[centerpoint_found].el_number) ) { + + pCentp_found = pCentp; + etg1_found = etg1; + ecp0_found = ecp0; + centerpoint_found = centerpoint; + pEndp2_found = pEndp2; + ecp2_found = ecp2; + break; + } + } + } + } + } + if ( pCentp_found ) { + ecp0_found->flow ++; + if ( ecp0_found->cap < ecp0_found->flow ) { + ecp0_found->cap = ecp0_found->flow; + } + pEndp0->st_edge.flow ++; + if ( pEndp2_found && ecp2_found ) { + ecp2_found->flow --; + pEndp2_found->st_edge.flow --; + } else { + /* Endp2 not found */ + pCentp_found->st_edge.flow ++; + pCentp_found->st_edge.cap ++; + pBNS->tot_st_flow += 2; /* radical elimination */ + pBNS->tot_st_cap += 1; + } + + pCentp_found = NULL; + num_fixes ++; + tot_num_fixes ++; /* #2 Mob-H */ + continue; + } + /* 3rd attempt: find =C= and move radical to it */ + if ( i < num_endpoints ) { + int jj, delta, bNotFixed = 1; + Vertex vPathStart, vPathEnd, v1, v2; + int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; + for ( jj = 0; jj < num_at && bNotFixed; jj ++ ) { + if ( at2[i].endpoint ) { + continue; + } + if ( 2 == at2[jj].valence && pBNS->vert[jj].st_edge.cap == pBNS->vert[jj].st_edge.flow && + 4 == pVA[jj].cNumValenceElectrons && + !(ecp0 = pBNS->edge + pBNS->vert[jj].iedge[0])->forbidden && + !(ecp1 = pBNS->edge + pBNS->vert[jj].iedge[1])->forbidden && + 1 == ecp0->flow && 1 == ecp1->flow && + !at2[(int)at2[i].neighbor[0]].sb_parity[0] && + !at2[(int)at2[i].neighbor[1]].sb_parity[0] ) { + /* found =C=; make a radical and try to cancel the two radicals */ + k = ecp0->neighbor12 ^ jj; + if ( at2[k].endpoint ) { + ecp0 = ecp1; + k = ecp0->neighbor12 ^ jj; + if ( at2[k].endpoint ) { + continue; + } + } + delta = 1; + /* decrement C valence */ + pBNS->vert[jj].st_edge.flow -= delta; + pBNS->vert[jj].st_edge.cap -= delta; + /* decrement bond order */ + ecp0->flow -= delta; + /* reflect the changes in at2[k] to make it a radical */ + pBNS->vert[k].st_edge.flow -= delta; + pBNS->tot_st_cap -= delta; + pBNS->tot_st_flow -= 2*delta; + + v1 = endpoint0; + v2 = k; + + ret2 = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret2 == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { + ret2 = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret2 > 0 ) { + num_fixes ++; + tot_num_fixes ++; /* #3 Mob-H */ + pBNS->vert[jj].st_edge.cap += delta; /* create radical on =C- */ + pBNS->tot_st_cap += delta; + pCentp_found = NULL; + bNotFixed = 0; /* exit from the cycle */ + break; + } + } else { + /* failed */ + pBNS->vert[jj].st_edge.flow += delta; + pBNS->vert[jj].st_edge.cap += delta; + /* decrement bond order */ + ecp0->flow += delta; + /* reflect the changes in at2[k] to make it a radical */ + pBNS->vert[k].st_edge.flow += delta; + pBNS->tot_st_cap += delta; + pBNS->tot_st_flow += 2*delta; + } + if ( ret2 < 0 ) { + ret = ret2; + goto exit_function; + } + } + } + } + } + if ( !num_fixes ) { + break; + } + } + ret = tot_num_fixes; +exit_function: + pStruct->at = at; + memcpy( at2, at, len_at*sizeof(at2[0])); + return ret; +} +/****************************************************************************/ +/* Find and eliminate cases when Mobile H endpoint has radical on it (typical for wrong P(VI)(=O)3OH */ +int RemoveRadFromMobileHEndpointFixH(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) +{ +#define IS_C(x) (NO_VERTEX != x && pVA[x].cNumValenceElectrons == 4 && pVA[x].cPeriodicRowNumber == 1) + int i, num_fixes, tot_num_fixes = 0; + + int ret2, ret; + int num_at = pStruct->num_atoms; + int num_deleted_H = pStruct->num_deleted_H; + int len_at = num_at + num_deleted_H; + int inv_forbidden_edge_mask = ~forbidden_edge_mask; + EDGE_LIST ChargeEdgeList, BondEdgeList; + int itg, j, k, n, m, num_endp; + Vertex endpoint0=NO_VERTEX, endpoint1, endpoint2=NO_VERTEX, centerpoint; + Vertex centerpoint_found=NO_VERTEX, endpoint2_found=NO_VERTEX; + BNS_VERTEX *pEndp0=NULL, *pEndp1, *pEndp2, *pCentp, *pCentp_found, *pEndp2_found=NULL; + BNS_EDGE *ecp0, *ecp1, *ecp2, *ecp0_found=NULL, *ecp1_found=NULL, *ecp2_found=NULL; + int tgroup_number, num_endpoints; + + ret = 0; + + if ( pStruct->iMobileH != TAUT_NON ) + return ret; + + AllocEdgeList( &ChargeEdgeList, EDGE_LIST_CLEAR); + AllocEdgeList( &BondEdgeList, EDGE_LIST_CLEAR); + + memcpy( at2, at, len_at*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + if ( ret2 < 0 ) { + ret = ret2; + goto exit_function; + } + while ( pBNS->tot_st_cap > pBNS->tot_st_flow && pStruct->ti.num_t_groups ) { + int iEndpoint = 0; + num_fixes = 0; + for ( itg = 0; itg < pStruct->ti.num_t_groups; iEndpoint += num_endpoints, itg ++ ) { + pCentp_found=NULL; + tgroup_number = pStruct->ti.t_group[itg].nGroupNumber; + num_endpoints = pStruct->ti.t_group[itg].nNumEndpoints; + for ( i = 0; i < num_endpoints; i ++ ) { + endpoint0 = pStruct->ti.nEndpointAtomNumber[iEndpoint+i]; + pEndp0 = pBNS->vert + endpoint0; /* taut endpoint vertex (possible location of mobile H */ + if ( pEndp0->st_edge.cap > pEndp0->st_edge.flow ) { + /* radical endpoint1 has been detected */ + /* find a 1-3 centerpoint that has two or more endpoints */ + /* connected to the t-group vertex by edges with flow>0 and */ + /* to the centerpoint by edges with flow = 0 */ + /* after that: (1) increment etg1 flow to eliminate radical */ + /* (2) increment flow on one of the two other edges to the t-group */ + /* (3) increment st_cap on the found centerpoint */ + /* (4) rerun the BNS and re-create the structure */ + break; + } + } + + /* 2nd attempt: increment flow in centerpoint---radical_endpoint edge */ + pCentp_found = NULL; + if ( i < num_endpoints ) { + /* tautomeric endpoint found; traverse its t-group edges */ + for ( j = 0; j < num_endpoints; j ++ ) { + if ( i == j ) { + continue; /* avoid the found radical endpoint */ + } + endpoint1 = pStruct->ti.nEndpointAtomNumber[iEndpoint+j]; + pEndp1 = pBNS->vert + endpoint1; /* another endpoint */ + + if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) { + continue; /* one more radical-endpoint! What is going on here??? */ + } + if ( !at2[endpoint1].num_H && at2[endpoint1].charge != -1 ) { + continue; /* avoid enpoints that do not have an attachment */ + } + if ( !pStruct->endpoint[endpoint1] ) { + continue; /* should not happen */ + } + /* traverse endpoint1 edges to find the edge connecting it to the centerpoint */ + for ( k = 0; k < pEndp1->num_adj_edges; k ++ ) { + ecp1 = pBNS->edge + pEndp1->iedge[k]; + if ( ecp1->flow ) { + continue; + } + centerpoint = ecp1->neighbor12 ^ endpoint1; + if ( centerpoint >= pBNS->num_atoms ) { + break; /* no more edges to atoms */ + } + pCentp = pBNS->vert + centerpoint; + if ( pStruct->endpoint[centerpoint] ) { + continue; /* do not set another endpoint's valence = an unusual value */ + } + /* traverse centerpoint edges to find edge connecting it to the endpoint0 */ + /* 1. Find a double bond to an endpoint */ + ecp2 = NULL; + pEndp2 = NULL; + for ( n = 0, num_endp = 0; n < at2[centerpoint].valence; n ++ ) { + ecp0 = pBNS->edge + pCentp->iedge[n]; + if ( ecp0->flow ) { + endpoint2 = ecp0->neighbor12 ^ centerpoint; + if ( pStruct->endpoint[endpoint2] /* ??? */ ) { + continue; + } + /* check whether ecp0 is stereogenic: if it is then we cannot decrement its flow */ + for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[centerpoint].sb_parity[m]; m ++ ) { + if ( at[centerpoint].sb_ord[m] == n ) { + endpoint2 = NO_VERTEX; + break; + } + } + if ( endpoint2 == NO_VERTEX ) { + continue; + } + pEndp2 = pBNS->vert + endpoint2; + ecp2 = ecp0; + break; + } + } + if ( !ecp2 ) { + continue; + } + /* 2. Find a single bond to an endpoint0 */ + for ( n = 0, num_endp = 0; n < at2[centerpoint].valence; n ++ ) { + ecp0 = pBNS->edge + pCentp->iedge[n]; + if ( ecp0->flow ) { + continue; + } + if ( endpoint0 != (ecp0->neighbor12 ^ centerpoint) ) { + continue; + } + /* Found: + Endp2 Endp2(not radical) + || | + ||ecp2 |ecp2 + ecp0 || ecp1 ecp0 | ecp1 + Endp0----Centp----Endp1 Endp0====Centp----Endp1 + ^ \ / --> \ / + radical | \ / \ / + \ / \ / + etg0 \ / etg1 etg0 \ / etg1 + \ / \ / + tg1 tg1 + + */ + + /* compare centerpoints */ + if ( !pCentp_found || + /* try to avoid carbons */ + (pVA[centerpoint].cNumValenceElectrons != 4 || + pVA[centerpoint].cPeriodicRowNumber != 1) && + pVA[centerpoint_found].cNumValenceElectrons == 4 && + pVA[centerpoint_found].cPeriodicRowNumber == 1 || + /* try a better non-carbon */ + (pVA[centerpoint].cNumValenceElectrons != 4 || + pVA[centerpoint].cPeriodicRowNumber != 1 ) && + (at[centerpoint].valence > at[centerpoint_found].valence || + at[centerpoint].valence == at[centerpoint_found].valence && + at[centerpoint].el_number > at[centerpoint_found].el_number) ) { + + pCentp_found = pCentp; + ecp0_found = ecp0; + centerpoint_found = centerpoint; + pEndp2_found = pEndp2; + ecp2_found = ecp2; + break; + } + } + } + } + } + /* decrement st_flow, st_cap on Endp2; decrement flow on ecp2; decrement st_flow on Centp */ + /* result: radicals on Endp0 and Centp => run BNS */ + if ( pCentp_found ) { + /* make ecp0 a double bond, make ecp2 a single bond, remove radical */ + ecp0_found->flow ++; + if ( ecp0_found->cap < ecp0_found->flow ) { + ecp0_found->cap = ecp0_found->flow; + } + pEndp0->st_edge.flow ++; + if ( pEndp2_found && ecp2_found ) { + ecp2_found->flow --; + pEndp2_found->st_edge.flow --; + } else { + /* Endp2 not found: only make ecp0 a double bond */ + pCentp_found->st_edge.flow ++; + pCentp_found->st_edge.cap ++; + pBNS->tot_st_flow += 2; /* radical elimination */ + pBNS->tot_st_cap += 1; + } + + pCentp_found = NULL; + num_fixes ++; /* #2 */ + tot_num_fixes ++; + continue; + } + /* 1st attempt */ + pCentp_found = NULL; + if ( i < num_endpoints ) { + /* tautomeric endpoint found; traverse its t-group edges */ + for ( j = 0; j < num_endpoints; j ++ ) { + if ( i == j ) { + continue; /* avoid the found radical endpoint */ + } + endpoint1 = pStruct->ti.nEndpointAtomNumber[iEndpoint+j]; + pEndp1 = pBNS->vert + endpoint1; /* another endpoint */ + if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) { + continue; /* one more radical-endpoint! What is going on here??? */ + } + if ( !at2[endpoint1].num_H && at2[endpoint1].charge != -1 ) { + continue; /* avoid enpoints that do not have an attachment */ + } + if ( !pStruct->endpoint[endpoint1] ) { + continue; /* should not happen */ + } + /* traverse endpoint1 edges to find the edge connecting it to the centerpoint */ + for ( k = 0; k < pEndp1->num_adj_edges; k ++ ) { + ecp1 = pBNS->edge + pEndp1->iedge[k]; + if ( ecp1->flow ) { + continue; + } + centerpoint = ecp1->neighbor12 ^ endpoint1; + if ( centerpoint >= pBNS->num_atoms ) { + break; + } + pCentp = pBNS->vert + centerpoint; + /* traverse centerpoint edges to find the 2nd endpoint */ + for ( n = 0, num_endp = 0; n < pCentp->num_adj_edges; n ++ ) { + ecp2 = pBNS->edge + pCentp->iedge[n]; + if ( ecp2->flow ) { + continue; + } + endpoint2 = ecp2->neighbor12 ^ centerpoint; + if ( endpoint2 >= pBNS->num_atoms ) { + break; + } + if ( !pStruct->endpoint[endpoint2] ) { + continue; + } + pEndp2 = pBNS->vert + endpoint2; + + if ( at2[endpoint2].num_H || at2[endpoint1].charge == -1 ) { + continue; + } + + /* we have found the path: + + Endp1 has no attachments, Endp2 has. + + Endp1 Endp1 + etg1 // \ ecp1 etg1 / \\ ecp1 + etg0 // \ etg0 / \\ + Endp0-----tg1 Centp --> Endp0=====tg1 Centp + ^ \\ / \\ / + radical | etg2 \\ / ecp2 etg2 \\ / ecp2 + Endp2 Endp2 + */ + + /* compare centerpoints */ + if ( !pCentp_found || + /* try to avoid carbons */ + (pVA[centerpoint].cNumValenceElectrons != 4 || + pVA[centerpoint].cPeriodicRowNumber != 1) && + pVA[centerpoint_found].cNumValenceElectrons == 4 && + pVA[centerpoint_found].cPeriodicRowNumber == 1 || + /* try a better non-carbon */ + (pVA[centerpoint].cNumValenceElectrons != 4 || + pVA[centerpoint].cPeriodicRowNumber != 1 ) && + (at[centerpoint].valence > at[centerpoint_found].valence || + at[centerpoint].valence == at[centerpoint_found].valence && + at[centerpoint].el_number > at[centerpoint_found].el_number) ) { + + pCentp_found = pCentp; + ecp1_found = ecp1; + centerpoint_found = centerpoint; + break; + } + } + } + } + } + if ( pCentp_found ) { + /* create a new radical at the centerpoint and try to cancel them */ + int delta = 1, ret3; + Vertex vPathStart, vPathEnd, v1, v2; + int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; + + pCentp_found->st_edge.cap += delta; + pBNS->tot_st_cap += delta; + + v1 = (int) (pCentp_found - pBNS->vert); + v2 = (int) (pEndp0 - pBNS->vert); + + ret3 = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret3 == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge % 2 == 0 ) { + ret3 = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret3 > 0 ) { + num_fixes ++; + tot_num_fixes ++; /* #1 */ + pCentp_found = NULL; + continue; + } + } else { + pCentp_found->st_edge.cap -= delta; + pBNS->tot_st_cap -= delta; + } + if ( ret3 < 0 ) { + ret = ret3; + goto exit_function; + } + } + /*---------------------------------------------------------------------------------------- + 3rd attempt: add radical to keep + ============== u,f=>unfixed, fixed edges (N electrons)%2 + (-) (-) (-) | + e0/ \\ e1 => u/ \\u => // \ v + / \\ ecp1 ecp2 / \\ u f // \ + C--X* Y(-)--C==Z C--X* Y(-)--C*--Z --X(-) Y===C---Z* + rad. endp not rad. endp rad not rad. endp not + Endp0 Endp1 endp endp to endp endp endp + Endp2 cancel + + Note: endpoints X and Y may belong to different t-groups + ----------------------------------------------------------------------------------------*/ + pCentp_found = NULL; + if ( i < num_endpoints ) { + int e0, e1; + if ( (e0=pVA[endpoint0].nCMinusGroupEdge-1)<0 || pBNS->edge[e0].forbidden ) { + continue; /* no negative charge on Endp0 is possible */ + } + /* a radical-tautomeric endpoint found; traverse all endpoints */ + for ( j = 0; j < pStruct->ti.nNumEndpoints; j ++ ) { + if ( iEndpoint+i == j ) { + continue; /* avoid the found radical endpoint */ + } + + endpoint1 = pStruct->ti.nEndpointAtomNumber[j]; + pEndp1 = pBNS->vert + endpoint1; /* another endpoint */ + + if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) { + continue; /* one more radical-endpoint! What is going on here??? */ + } + if ( ((e1=pVA[endpoint1].nCMinusGroupEdge-1)<0 || !pBNS->edge[e1].flow) || pBNS->edge[e1].forbidden ) { + continue; /* no negative charge on Endp1 */ + } + if ( !pStruct->endpoint[endpoint1] ) { + continue; /* should not happen */ + } + /* traverse endpoint1 edges to find the edge connecting it to the centerpoint */ + for ( k = 0; k < pEndp1->num_adj_edges; k ++ ) { + ecp1 = pBNS->edge + pEndp1->iedge[k]; /* e1C */ + if ( ecp1->flow || ecp1->forbidden ) { + continue; + } + centerpoint = ecp1->neighbor12 ^ endpoint1; + if ( centerpoint >= pBNS->num_atoms ) { + break; /* no more edges to atoms */ + } + pCentp = pBNS->vert + centerpoint; + if ( pStruct->endpoint[centerpoint] ) { + continue; /* do not set another endpoint's valence = an unusual value */ + } + /* traverse centerpoint edges to find edge connecting it to the endpoint0 */ + /* 1. Find a double bond to a not endpoint */ + ecp2 = NULL; + pEndp2 = NULL; + for ( n = 0, num_endp = 0; n < pCentp->num_adj_edges; n ++ ) { + ecp0 = pBNS->edge + pCentp->iedge[n]; + if ( ecp0->flow && !ecp0->forbidden ) { + endpoint2 = ecp0->neighbor12 ^ centerpoint; + if ( endpoint2 >= pBNS->num_atoms || pStruct->endpoint[endpoint2] ) { + continue; + } + /* check whether ecp0 is stereogenic: if it is then we cannot decrement its flow */ + for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[centerpoint].sb_parity[m]; m ++ ) { + if ( at[centerpoint].sb_ord[m] == n ) { + endpoint2 = NO_VERTEX; + break; + } + } + if ( endpoint2 == NO_VERTEX ) { + continue; + } + pEndp2 = pBNS->vert + endpoint2; + ecp2 = ecp0; /* e2C */ + break; + } + } + if ( !ecp2 ) + continue; + /* compare centerpoints */ + if ( !pCentp_found || + /* try to find carbons */ + !IS_C(endpoint2_found) && IS_C(endpoint2) || + IS_C(endpoint2_found) && IS_C(endpoint2) && + !IS_C(centerpoint_found) && IS_C(centerpoint) ) { + + pCentp_found = pCentp; + centerpoint_found = centerpoint; + endpoint2_found = endpoint2; + ecp2_found = ecp2; + ecp1_found = ecp1; + ecp0_found = pBNS->edge + e0; + break; + } + } + } + } + /* decrement st_flow, st_cap on Endp2; decrement flow on ecp2; decrement st_flow on Centp */ + /* result: radicals on Endp0 and Centp => run BNS */ + if ( pCentp_found ) { + Vertex vPathStart, vPathEnd, v1, v2; + int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; + int delta; + + Vertex vEndp0 = ecp0_found->neighbor1; + Vertex vEndp1 = ecp1_found->neighbor12 ^ centerpoint_found; + Vertex vEndp2 = ecp2_found->neighbor12 ^ centerpoint_found; + BNS_EDGE *pe0 = ecp0_found; + BNS_EDGE *pe1 = pBNS->edge + (pVA[vEndp1].nCMinusGroupEdge - 1); + pEndp1 = pBNS->vert + vEndp1; + pEndp2 = pBNS->vert + vEndp2; + pCentp = pCentp_found; + if ( !ChargeEdgeList.num_alloc ) { + for ( n = 0; n < pStruct->num_atoms; n ++ ) { + if ( (k = pVA[n].nCMinusGroupEdge)>= 0 && !pBNS->edge[k].forbidden && + (ret = AddToEdgeList( &ChargeEdgeList, k, pStruct->num_atoms ) ) ) { + goto exit_function; + } + if ( (k = pVA[n].nCPlusGroupEdge)>= 0 && !pBNS->edge[k].forbidden && + (ret = AddToEdgeList( &ChargeEdgeList, k, pStruct->num_atoms ) ) ) { + goto exit_function; + } + } + } + if ( !BondEdgeList.num_alloc ) { + for ( n = 0; n < pBNS->num_bonds; n ++ ) { + if ( (ret = AddToEdgeList( &BondEdgeList, n, pBNS->num_bonds ) ) ) { + goto exit_function; + } + } + } + /* fix all bonds and charges */ + SetForbiddenEdgeMask( pBNS, &ChargeEdgeList, forbidden_edge_mask ); + SetForbiddenEdgeMask( pBNS, &BondEdgeList, forbidden_edge_mask ); + /* prepare flow for testing */ + delta = 1; + ecp2_found->flow -= delta; + pCentp->st_edge.flow -= delta; + pEndp2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + /* unfix edges to be changed */ + pe0->forbidden &= inv_forbidden_edge_mask; + pe1->forbidden &= inv_forbidden_edge_mask; + ecp1_found->forbidden &= inv_forbidden_edge_mask; + + pBNS->tot_st_cap += delta; + + v1 = vEndp0; + v2 = centerpoint_found; + ret2 = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret2 == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { + ret2 = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret2 > 0 ) { + num_fixes ++; + tot_num_fixes ++; /* #3 */ + pCentp_found = NULL; + } + } else { + /* roll back */ + ecp2_found->flow += delta; + pCentp->st_edge.flow += delta; + pEndp2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + RemoveForbiddenEdgeMask( pBNS, &ChargeEdgeList, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &BondEdgeList, forbidden_edge_mask ); + if ( ret2 < 0 ) { + ret = ret2; + goto exit_function; + } + if ( !pCentp_found ) + continue; + } + } + if ( !num_fixes ) { + break; + } + } + +/************ again ***********************************************************/ + while ( pBNS->tot_st_cap > pBNS->tot_st_flow && pStruct->ti.num_t_groups ) { + int iEndpoint = 0; + num_fixes = 0; + for ( itg = 0; itg < pStruct->ti.num_t_groups; iEndpoint += num_endpoints, itg ++ ) { + pCentp_found=NULL; + tgroup_number = pStruct->ti.t_group[itg].nGroupNumber; + num_endpoints = pStruct->ti.t_group[itg].nNumEndpoints; + for ( i = 0; i < num_endpoints; i ++ ) { + endpoint0 = pStruct->ti.nEndpointAtomNumber[iEndpoint+i]; + pEndp0 = pBNS->vert + endpoint0; /* taut endpoint vertex (possible location of mobile H */ + if ( pEndp0->st_edge.cap > pEndp0->st_edge.flow ) { + /* radical endpoint1 has been detected */ + /* find a 1-3 centerpoint that has two or more endpoints */ + /* connected to the t-group vertex by edges with flow>0 and */ + /* to the centerpoint by edges with flow = 0 */ + /* after that: (1) increment etg1 flow to eliminate radical */ + /* (2) increment flow on one of the two other edges to the t-group */ + /* (3) increment st_cap on the found centerpoint */ + /* (4) rerun the BNS and re-create the structure */ + break; + } + } + /* 4th attempt */ + if ( i < num_endpoints ) { + /* tautomeric endpoint found; traverse its t-group edges */ + pEndp2_found = NULL; + for ( j = 0; j < pEndp0->num_adj_edges; j ++ ) { + ecp0 = pBNS->edge + pEndp0->iedge[j]; + centerpoint = ecp0->neighbor12 ^ endpoint0; + if ( centerpoint >= pBNS->num_atoms || ecp0->flow || pStruct->endpoint[centerpoint] ) { + continue; /* ignore non-single bonds, orig. InChI endpoints, and fictitious atoms */ + } + pCentp = pBNS->vert + centerpoint; + for ( k = 0; k < pCentp->num_adj_edges; k ++ ) { + ecp1 = pBNS->edge + pCentp->iedge[k]; + endpoint1 = ecp1->neighbor12 ^ centerpoint; + if ( endpoint1 >= pBNS->num_atoms || !ecp1->flow || pStruct->endpoint[endpoint1] ) { + continue; /* ignore single bonds, orig. InChI endpoints, and fictitious atoms */ + } + pEndp1 = pBNS->vert + endpoint1; + if ( endpoint1 == endpoint0 || pEndp1->st_edge.cap != pEndp1->st_edge.flow ) { + continue; /* ignore radicals */ + } + if ( !pEndp2_found || + /* try to find carbons */ + !IS_C(endpoint2_found) && IS_C(endpoint1) || + IS_C(endpoint2_found) && IS_C(endpoint1) && + !IS_C(centerpoint_found) && IS_C(centerpoint) ) { + pEndp2_found = pEndp1; + pCentp_found = pCentp; + endpoint2_found = endpoint1; + centerpoint_found = centerpoint; + ecp1_found = ecp0; + ecp2_found = ecp1; + } + } + } + if ( pEndp2_found ) { + /* move radical from pEndp0 to pEndp2 */ + pEndp0->st_edge.flow ++; + ecp1_found->flow ++; + ecp2_found->flow --; + pEndp2_found->st_edge.flow --; + pEndp2_found = NULL; + pCentp_found = NULL; + num_fixes ++; /* #4 */ + tot_num_fixes ++; + continue; + } + } + } + if ( !num_fixes ) + { + break; + } + } + + + ret = tot_num_fixes; + +exit_function: + AllocEdgeList( &ChargeEdgeList, EDGE_LIST_FREE); + AllocEdgeList( &BondEdgeList, EDGE_LIST_FREE); + + pStruct->at = at; + memcpy( at2, at, len_at*sizeof(at2[0])); + return ret; + +#undef IS_C +} + + +/************************************************************************************************/ +/* move (+) charges to >N- and other centerpoints */ +int MoveChargeToMakeCenerpoints(BN_STRUCT *pBNS, + BN_DATA *pBD, + StrFromINChI *pStruct, + inp_ATOM *at, + inp_ATOM *at2, + VAL_AT *pVA, + ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, + int *pnTotalDelta, + int forbidden_edge_mask) +{ + int i, j, neigh, num_endpoints, tg_group=0, num_success; + int ret2, ret, delta; + int num_at = pStruct->num_atoms; + int num_deleted_H = pStruct->num_deleted_H; + int len_at = num_at + num_deleted_H; + int inv_forbidden_edge_mask = ~forbidden_edge_mask; + + /* for RunBnsTestOnce */ + Vertex vPathStart, vPathEnd; + int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; + + BNS_EDGE *pEdgePlus, *pEdgeMinus; + Vertex v1p, v2p, v1m, v2m; + BNS_VERTEX *pv1p, *pv2p, *pv1m, *pv2m; + + ret = 0; + num_success = 0; + + /* to simplify, prepare new at[] from pBNS */ + memcpy( at2, at, len_at*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + pStruct->at = at; + if ( ret2 < 0 ) + { + ret = ret2; + goto exit_function; + } + + for ( i = 0; i < num_at; i ++ ) + { + if ( pVA[i].cNumValenceElectrons != 4 && /* not C, Si, Ge */ + !pVA[i].cMetal && !pVA[i].nTautGroupEdge && + !at2[i].num_H && at2[i].valence >= 3 && + at2[i].valence == at2[i].chem_bonds_valence && + !at2[i].charge && pVA[i].nCPlusGroupEdge > 0 && + is_centerpoint_elem( at2[i].el_number ) ) { + for ( j = 0, num_endpoints = 0; j < at2[i].valence; j ++ ) + { + neigh = at2[i].neighbor[j]; + if ( at2[neigh].endpoint ) + { + if ( !num_endpoints ) + { + tg_group = at2[neigh].endpoint; + } + else if ( tg_group != at2[neigh].endpoint ) + { + break; /* not a centerpoint */ + } + num_endpoints ++; + } + } + + if ( j == at2[i].valence && num_endpoints > 1 ) + { + /* found possible centerpoint */ + pEdgePlus = pBNS->edge + (pVA[i].nCPlusGroupEdge-1); + pEdgeMinus = (pVA[i].nCMinusGroupEdge > 0)? pBNS->edge + (pVA[i].nCMinusGroupEdge-1) : NULL; + if ( pEdgePlus->flow + (pEdgeMinus? pEdgeMinus->flow : 0) != 1 ) + { + continue; + } + v1p = pEdgePlus->neighbor1; + v2p = pEdgePlus->neighbor12 ^ v1p; + pv1p = pBNS->vert + v1p; + pv2p = pBNS->vert + v2p; + if ( pEdgeMinus ) + { + v1m = pEdgeMinus->neighbor1; + v2m = pEdgeMinus->neighbor12 ^ v1m; + pv1m = pBNS->vert + v1m; + pv2m = pBNS->vert + v2m; + } + else + { + v1m = NO_VERTEX; + v2m = NO_VERTEX; + pv1m = NULL; + pv2m = NULL; + } + ret = 0; + + /* set new flow to run BNS Search */ + if ( delta = pEdgePlus->flow ) + { + /* positive charge <=> flow=0 on (=) edge */ + pEdgePlus->flow -= delta; + pv1p->st_edge.flow -= delta; + pv2p->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + pEdgePlus->forbidden |= forbidden_edge_mask; + if ( pEdgeMinus ) + { + pEdgeMinus->forbidden |= forbidden_edge_mask; + } + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret < 0 ) + { + goto exit_function; + } + + if ( ret == 1 && (vPathEnd == v1p && vPathStart == v2p || + vPathEnd == v2p && vPathStart == v1p) && + nDeltaCharge == -1 /* charge moving to this atom disappers*/ ) + { + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + (*pnNumRunBNS) ++; + if ( ret < 0 ) + { + goto exit_function; + } + else if ( ret == 1 ) + { + *pnTotalDelta += ret; + } + else + { + ret = RI_ERR_PROGR; + goto exit_function; + } + } + else + { + ret = 0; + pEdgePlus->flow += delta; + pv1p->st_edge.flow += delta; + pv2p->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + pEdgePlus->forbidden &= inv_forbidden_edge_mask; + if ( pEdgeMinus ) + { + pEdgeMinus->forbidden &= inv_forbidden_edge_mask; + } + } + else + if ( pEdgeMinus && (delta == pEdgeMinus->flow) && pEdgePlus->flow == 0 ) + { + /* positive charge <=> flow=0 on (=) edge and flow=0 on (-) edge */ + pEdgeMinus->flow -= delta; + pv1m->st_edge.flow -= delta; + pv2m->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + pEdgePlus->forbidden |= forbidden_edge_mask; + pEdgeMinus->forbidden |= forbidden_edge_mask; + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + if ( ret < 0 ) + { + goto exit_function; + } + if ( ret == 1 && (vPathEnd == v1m && vPathStart == v2m || + vPathEnd == v2m && vPathStart == v1m) && + nDeltaCharge == -1 /* charge moving to this atom disappers*/ ) + { + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + (*pnNumRunBNS) ++; + if ( ret < 0 ) + { + goto exit_function; + } + else if ( ret == 1 ) + { + *pnTotalDelta += ret; + } + else + { + ret = RI_ERR_PROGR; + goto exit_function; + } + } + else + { + ret = 0; + pEdgeMinus->flow += delta; + pv1m->st_edge.flow += delta; + pv2m->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + pEdgePlus->forbidden &= inv_forbidden_edge_mask; + pEdgeMinus->forbidden &= inv_forbidden_edge_mask; + } + if ( ret ) + { + num_success ++; + memcpy( at2, at, len_at*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + pStruct->at = at; + if ( ret2 < 0 ) + { + ret = ret2; + goto exit_function; + } + } + } + } + } + + + ret = num_success; + +exit_function: + return ret; +} + +#endif diff --git a/INCHI-1-SRC/INCHI/common/ichirvr3.c b/INCHI-1-SRC/INCHI_BASE/src/ichirvr3.c similarity index 95% rename from INCHI-1-SRC/INCHI/common/ichirvr3.c rename to INCHI-1-SRC/INCHI_BASE/src/ichirvr3.c index 2c61302..0379753 100644 --- a/INCHI-1-SRC/INCHI/common/ichirvr3.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichirvr3.c @@ -1,5530 +1,5528 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - -/*#define CHECK_WIN32_VC_HEAP*/ -#include "mode.h" - -#if ( READ_INCHI_STRING == 1 ) - -#include "ichi.h" -#include "ichitime.h" - -#include "inpdef.h" -#include "ichimain.h" -#include "ichierr.h" -#include "incomdef.h" -#include "ichiring.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "util.h" - -#include "ichicomp.h" -#include "ichister.h" - -#include "ichi_bns.h" - -#include "strutil.h" - -#include "ichirvrs.h" - -#define INC_ADD_EDGE 64 - -/* local types */ - -/* types for TgDiffHChgFH */ -#define fNumRPosChgH 0 /* number of positive charges on endpoints that have H in at2[] */ -#define fNumRPosChgU 1 /* number of positive charges on endpoints that have no H in at2[] */ -#define fNumRNegChgO 2 /* number of negative charges on O endpoints */ -#define fNumRNegChgN 3 /* number of negative charges on N endpoints */ -#define fNumRNeutrlH 4 /* number of neutral endp that have H in at2[] */ - -#define fNumNPosChgH 5 /* number of positive charges on endpoints that have H in atf[] */ -#define fNumNPosChgU 6 /* number of positive charges on endpoints that have no H in atf[] */ -#define fNumNNegChgO 7 /* number of negative charges on O endpoints */ -#define fNumNNegChgN 8 /* number of negative charges on N endpoints */ -#define fNumNNeutrlH 9 /* number of neutral endp that have H in atf[] */ - -#define fNumAllChgT 10 /* total number of fNum... */ - -typedef struct tagTgDiffHChgFH { - short itg; /* t-group index; endpoint = itg+1 */ - short nNumHInchi; /* number of H in t-group from orig. InChI */ - short nNumHRevrs; /* number of H in at2[] */ - short nNumHNorml; /* number of H in Normalized atfMobile_H_Revrs[] */ - short nNumMInchi; /* number of (-) in InChI */ - short nNumMRevrs; /* number of (-) in at2[] */ - short nNumMNorml; /* number of (-) in atf[] */ - short nNumPRevrs; /* number of (+) in at2[] */ - short nNumPNorml; /* number of (+) in Normalized atfMobile_H_Revrs[] */ - short n[fNumAllChgT]; /* all numbers */ - short i[fNumAllChgT]; /* all indices */ -} TgDiffHChgFH; - -/* local prototypes */ -static int FillTgDiffHChgFH( TgDiffHChgFH tdhc[], int max_tdhc, inp_ATOM at2[], inp_ATOM atf[], - AT_NUMB *nCanon2AtnoRevrs, VAL_AT *pVA, T_GROUP_INFO *ti, EDGE_LIST *pAtomIndList ); - - -/************************************************************/ -int bHas_N_V( inp_ATOM *at2, int num_atoms ) -{ - static U_CHAR el_number_N; - int i, num_found = 0; - if ( !el_number_N ) { - el_number_N = get_periodic_table_number( "N" ); - } - for ( i = 0; i < num_atoms; i ++ ) { - if ( at2[i].el_number == el_number_N && !at2[i].charge && - !at2[i].num_H && !at2[i].radical && - at2[i].chem_bonds_valence == 5 && - (at2[i].valence==3) ) { - num_found ++; - } - } - return num_found; -} -/*************************************************************************************/ -int FillTgDiffHChgFH( TgDiffHChgFH tdhc[], int max_tdhc, inp_ATOM at2[], - inp_ATOM atf[], AT_NUMB *nCanon2AtnoRevrs, VAL_AT *pVA, - T_GROUP_INFO *ti, EDGE_LIST *pAtomIndList ) -{ - - int i, j, iat, itg, itg_prev, num, itg_out, bOverflow; - EDGE_LIST IndList; /* type, itg */ - TgDiffHChgFH cur_tdhc; - AT_NUMB *pEndp0; - inp_ATOM *at2i, *atfi; - int typeR, typeN, type, ret = 0, nCurIndListLen; - - AllocEdgeList( &IndList, EDGE_LIST_CLEAR ); - pAtomIndList->num_edges = 0; - itg_out = 0; - bOverflow = 0; - memset( tdhc, 0, max_tdhc * sizeof(tdhc[0]) ); - - for ( itg = 0; itg < ti->num_t_groups; itg ++ ) { - memset( &cur_tdhc, 0, sizeof(cur_tdhc) ); - - cur_tdhc.itg = itg; - cur_tdhc.nNumHInchi = ti->t_group[itg].num[0] - ti->t_group[itg].num[1]; - cur_tdhc.nNumMInchi = ti->t_group[itg].num[1]; - - pEndp0 = ti->nEndpointAtomNumber + ti->t_group[itg].nFirstEndpointAtNoPos; - nCurIndListLen = IndList.num_edges; - for ( j = 0; j < ti->t_group[itg].nNumEndpoints; j ++ ) { - i = pEndp0[j]; - iat = nCanon2AtnoRevrs[i]; - - at2i = at2 + iat; - atfi = atf + iat; - - typeR = typeN = -1; - if ( at2i->charge == 1 ) { - if ( at2i->num_H ) { - typeR = fNumRPosChgH; - } else { - typeR = fNumRPosChgU; - } - cur_tdhc.nNumPRevrs ++; - } else - if ( at2i->charge == -1 ) { - if ( pVA[iat].cNumValenceElectrons == 6) { - typeR = fNumRNegChgO; - } else - if ( pVA[iat].cNumValenceElectrons == 5) { - typeR = fNumRNegChgN; - } - cur_tdhc.nNumMRevrs ++; - } else - if ( at2i->num_H && at2i->valence == at2i->chem_bonds_valence ) { - typeR = fNumRNeutrlH; - } - cur_tdhc.nNumHRevrs += at2i->num_H; - - if ( atfi->charge == 1 ) { - if ( atfi->num_H ) { - typeN = fNumNPosChgH; - } else { - typeN = fNumNPosChgU; - } - cur_tdhc.nNumPNorml ++; - } else - if ( atfi->charge == -1 ) { - if ( pVA[iat].cNumValenceElectrons == 6) { - typeN = fNumNNegChgO; - } else - if ( pVA[iat].cNumValenceElectrons == 5) { - typeN = fNumNNegChgN; - } - cur_tdhc.nNumMNorml ++; - } else - if ( atfi->num_H && atfi->valence == atfi->chem_bonds_valence ) { - typeN = fNumNNeutrlH; - } - cur_tdhc.nNumHNorml += atfi->num_H; - if ( at2[iat].charge < 0 || 0 < pVA[iat].nCPlusGroupEdge ) { - if ( typeR >= 0 && ( - (ret = AddToEdgeList( &IndList, typeR, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &IndList, itg, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &IndList, iat, INC_ADD_EDGE )) ) ) { - goto exit_function; - } - if ( typeN >= 0 && ( - (ret = AddToEdgeList( &IndList, typeN, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &IndList, itg, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &IndList, iat, INC_ADD_EDGE )) ) ) { - goto exit_function; - } - } - - } - if ( cur_tdhc.nNumHNorml == cur_tdhc.nNumHInchi && - cur_tdhc.nNumMNorml == cur_tdhc.nNumMInchi ) { - IndList.num_edges = nCurIndListLen; /* t-group seems to be correct */ - continue; - } - if ( itg_out < max_tdhc ) { - tdhc[itg_out ++] = cur_tdhc; - } else { - bOverflow |= 1; - IndList.num_edges = nCurIndListLen; - break; - } - } - /* fill out atom index list */ - if ( itg_out ) { - itg_prev = IndList.pnEdges[1]; /* the 1st saved t-group number */ - for ( type = 0; type < fNumAllChgT; type ++ ) { - j = 0; - for ( i = 0; i < itg_out; i ++ ) { - num = 0; - itg = tdhc[i].itg; - tdhc[i].i[type] = -999; /* empty */ - while( IndList.pnEdges[j+1] == itg ) { - if ( IndList.pnEdges[j] == type ) { - if ( !num ++ ) { - tdhc[i].i[type] = pAtomIndList->num_edges; - } - if ( ret = AddToEdgeList( pAtomIndList, IndList.pnEdges[j+2], INC_ADD_EDGE )) { - goto exit_function; - } - } - j += 3; - } - tdhc[i].n[type] = num; - } - } - } - ret = itg_out; -exit_function: - AllocEdgeList( &IndList, EDGE_LIST_FREE ); - return ret; - -/* -#undef fNumRPosChgH -#undef fNumRPosChgU -#undef fNumRNegChgO -#undef fNumRNegChgN - -#undef fNumNPosChgH -#undef fNumNPosChgU -#undef fNumNNegChgO -#undef fNumNNegChgN - -#undef fNumAllChgT -*/ -} - -/***********************************************************************************************/ -int FixFixedHRestoredStructure(ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, - StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, T_GROUP_INFO **ppt_group_info, inp_ATOM **ppat_norm, - inp_ATOM **ppat_prep, INChI *pInChI[], long num_inp, int bHasSomeFixedH, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask) -{ - /*--------- process extra or missing Fixed-H on non-tautomeric atoms ------*/ - /* at2 should be the most recently restored atom, Fixed-H */ - int i, j, k, delta, num_try, tot_succes, cur_success, ret = 0, bAllowedNFlowerEdges=0, num_zero_ret; - CMP2FHINCHI c2i; - CMP2FHINCHI *pc2i = &c2i; - - EDGE_LIST AllChargeEdges, CurrEdges, SFlowerEdges, NFlowerEdges, OtherNFlowerEdges, FixedLargeRingStereoEdges; - EDGE_LIST AllBondEdges; - - EdgeIndex e; - BNS_EDGE *pe; - Vertex v1, v2; - BNS_VERTEX *pv1, *pv2; - - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - - int nNumRunBNS = 0, forbidden_edge_mask_inv = ~forbidden_edge_mask; - - INCHI_HEAPCHK - - AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &CurrEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &NFlowerEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &SFlowerEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &OtherNFlowerEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &AllBondEdges, EDGE_LIST_CLEAR ); - - tot_succes = 0; - - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - - for ( i = 0; i < pStruct->num_atoms; i ++ ) { - if ( (e=pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - (ret = AddToEdgeList( &AllChargeEdges, e, INC_ADD_EDGE )) ) { - goto exit_function; - } - if ( (e=pVA[i].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - if ( ret = AddToEdgeList( &AllChargeEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - - /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ - if ( pVA[i].cNumValenceElectrons == 5 && !pVA[i].cMetal && /* N, P, As */ - NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e ))) { - - if ( pBNS->edge[j].forbidden ) { - continue; - } - - if ( pBNS->edge[j].flow ) { - if ( ret = AddToEdgeList( &AllChargeEdges, j, INC_ADD_EDGE ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NFlowerEdges, j, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else { - if ( ret = AddToEdgeList( &OtherNFlowerEdges, j, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } else - /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ - if ( pVA[i].cNumValenceElectrons == 6 && !pVA[i].cMetal && /* N, P, As */ - NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e ))) { - - if ( pBNS->edge[j].forbidden ) { - continue; - } - - if ( pBNS->edge[j].flow ) { - if ( ret = AddToEdgeList( &SFlowerEdges, j, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - - } - for ( j = 0; j < at2[i].valence; j ++ ) { - k = at2[i].neighbor[j]; - if ( k < i && !pBNS->edge[e=pBNS->vert[i].iedge[j]].forbidden ) { - if ( ret = AddToEdgeList( &AllBondEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - } - if ( forbidden_stereo_edge_mask ) { - for ( i = 0; i < pStruct->num_atoms; i ++ ) { - for ( j = 0; j < at2[i].valence; j ++ ) { - if ( pBNS->edge[k = pBNS->vert[i].iedge[j]].forbidden == forbidden_stereo_edge_mask ) { - int nMinRingSize = is_bond_in_Nmax_memb_ring( at2, i, j, pStruct->pbfsq->q, - pStruct->pbfsq->nAtomLevel, - pStruct->pbfsq->cSource, 99 /* max ring size */ ); - if ( 0 < nMinRingSize && (ret = AddToEdgeList( &FixedLargeRingStereoEdges, k, INC_ADD_EDGE ))) { - goto exit_function; - } - } - } - } - } - - INCHI_HEAPCHK - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - INCHI_HEAPCHK - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - - INCHI_HEAPCHK - - if ( !pc2i->bHasDifference || - !pc2i->len_c2at && pc2i->nNumTgRevrs == pc2i->nNumTgInChI && - pc2i->nNumEndpRevrs == pc2i->nNumRemHInChI && - pc2i->nNumEndpRevrs == pc2i->nNumEndpInChI && - !pc2i->nNumTgDiffMinus && !pc2i->nNumTgDiffH ) { - goto exit_function; /* nothing to do */ - } - - /*goto exit_function;*/ /* debug only*/ - - if ( pc2i->len_c2at >= 2 ) { - /*----------------------------------------------------*/ - /* case 01: restored: O=AB-O(-) original: (-)O-AB=O */ - /* FixH: 0 -1 -1 0 */ - /* MobH: 0 1 1 0 */ - /* non-taut non-taut */ - /* O = O, S, Se; charged atoms O are not tautomeric */ - /* Solution: move (-) from B-O(-) to O=A */ - /*----------------------------------------------------*/ - int num_DB_O = 0, num_SB_O_Minus = 0, iat; - short iat_DB_O[MAX_DIFF_FIXH], iat_SB_O_Minus[MAX_DIFF_FIXH]; - cur_success = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( pc2i->c2at[i].nValElectr == 6 /* && !pc2i->c2at[i].endptInChI -- mod#1*/ && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - if ( /* orig. InChI info: */ - num_SB_O_Minus < MAX_DIFF_FIXH && - pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI == 0 && - /* reversed structure info: */ - pc2i->c2at[i].nFixHRevrs == -1 && pc2i->c2at[i].nMobHRevrs == 1 && - pc2i->c2at[i].nAtChargeRevrs == -1 && !at2[iat].num_H && /* at2 is Fixed-H */ - at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 1 ) { - iat_SB_O_Minus[num_SB_O_Minus ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else - if ( /* orig. InChI info: */ - num_DB_O < MAX_DIFF_FIXH && - pc2i->c2at[i].nFixHInChI == -1 && pc2i->c2at[i].nMobHInChI == 1 && - /* reversed structure info: */ - pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && - pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && - at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 ) { - iat_DB_O[num_DB_O ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - } - if ( num_try = inchi_min( num_SB_O_Minus, num_DB_O ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_SB_O_Minus && cur_success < num_try; i ++ ) { - iat = iat_SB_O_Minus[i]; - pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; /* remove (-) from AB-O(-) */ - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Added (-)charge to O=AB => nDeltaCharge == -1 */ - /* Flow change on pe (-)charge edge (atom B-O(-)) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 01 */ - } - } else { - pe->forbidden &= forbidden_edge_mask_inv; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - CurrEdges.num_edges = 0; /* clear current edge list */ - } - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pc2i->len_c2at >= 1 ) { - /*--------------------------------------------------------------*/ - /* case 02: restored: -O(+)=AB-NH2 original: -O-AB=NH2(+) */ - /* FixH: 0 0 0 1 */ - /* MobH: 0 2 0 1 */ - /* O = P, As, Sb, O, S, Se, F, Cl, Br, I; not taut. in InChI */ - /* N = N, O, S, Se, Te; has H; tautomeric or not tautomeric */ - /* Solution: move (+) from O(+) to NH2 */ - /*--------------------------------------------------------------*/ - int num_DB_O_Plus = 0, num_SB_NH = 0, iat; - short iat_DB_O_Plus[MAX_DIFF_FIXH], iat_SB_NH[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : NULL; - cur_success = 0; - num_zero_ret = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( /* orig. InChI info: =NH2(+), =OH(+) */ - num_SB_NH < MAX_DIFF_FIXH && - (pc2i->c2at[i].nValElectr == 5 && pc2i->c2at[i].nPeriodNum == 1 || - pc2i->c2at[i].nValElectr == 6 ) /* N, O, S, Se, Te */ && - /*!pc2i->c2at[i].endptInChI &&*/ /* <=== relaxation */ - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pc2i->c2at[i].nFixHInChI>0 /*== 1 --modification#2*/ && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ - /* reversed structure info: */ - pc2i->c2at[i].nFixHRevrs == 0 && /* pc2i->c2at[i].nMobHRevrs == 0 &&*/ - pc2i->c2at[i].nAtChargeRevrs == 0 && at2[iat].num_H && - at2[iat].valence == at2[iat].chem_bonds_valence ) { - iat_SB_NH[num_SB_NH ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( /* in restored atom: charge=+1, no H, has double bond, P, As, O, S, Se, Te, F, Cl, Br, I */ - num_DB_O_Plus < MAX_DIFF_FIXH && - at2[iat].charge == 1 && !at2[iat].num_H && - at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - (pVA[iat].cNumValenceElectrons == 6 || pVA[iat].cNumValenceElectrons == 7 || - pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber > 1) && - /* in orig.InChI: not an endpoint, has no H */ - !pStruct->endpoint[i] && - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i] ) && - /* has (+) edge */ - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - iat_DB_O_Plus[num_DB_O_Plus ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( num_try = inchi_min( num_DB_O_Plus, num_SB_NH ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; -repeat_02_allow_NV: - for ( i = 0; i < num_SB_NH && cur_success < num_try; i ++ ) { - iat = iat_SB_NH[i]; - pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { - /* Removed charge from O(+) => nDeltaCharge == -1 */ - /* Flow change on pe (+)charge edge (atom NH2) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 02 */ - } - } else { - num_zero_ret += !ret; - pe->forbidden &= forbidden_edge_mask_inv; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - if ( num_zero_ret == num_try && !bAllowedNFlowerEdges && NFlowerEdges.num_edges ) { - RemoveForbiddenEdgeMask( pBNS, &NFlowerEdges, forbidden_edge_mask ); - bAllowedNFlowerEdges = 1; - goto repeat_02_allow_NV; - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - bAllowedNFlowerEdges = 0; - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pc2i->len_c2at >= 1 && pc2i->nNumTgRevrs == 1 && - (pc2i->nNumEndpRevrs > pc2i->nNumEndpInChI || pc2i->nNumTgInChI > 1) /* ADP in Revrs */ ) { - /*--------------------------------------------------------------*/ - /* case 03: restored: -N(-)-AB=O original: -N=AB-O(-) */ - /* FixH: 0 0 0 -1 */ - /* MobH: 0 0 0 1 */ - /* O = O, S, Se; N = N; */ - /* restored atoms are tautomeric; original atoms are not taut. */ - /* restored struct has 1 t-group; original has less endpoints */ - /* and possibly >1 t-groups */ - /* Solution: move (-) from N(-) to =O */ - /* these atoms are tautomeric in restored structure */ - /*--------------------------------------------------------------*/ - int num_SB_N_Minus = 0, num_DB_O = 0, iat; - short iat_SB_N_Minus[MAX_DIFF_FIXH], iat_DB_O[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - /* - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - */ - cur_success = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( /* orig. InChI info: -O(-) */ - num_DB_O < MAX_DIFF_FIXH && - pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ && - !pc2i->c2at[i].endptInChI && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pc2i->c2at[i].nFixHInChI == -1 && pc2i->c2at[i].nMobHInChI == 1 && - /* reversed structure info: */ - pc2i->c2at[i].endptRevrs && - pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && - pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && - at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 ) { - iat_DB_O[num_DB_O ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( /* in restored atom N: charge=-1, no H, has no double bond, endpoint */ - num_SB_N_Minus < MAX_DIFF_FIXH && - at2[iat].charge == -1 && /*!at2[iat].num_H &&*/ - at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && - at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint && - /* in orig.InChI: not an endpoint, has no H */ - /* !pStruct->endpoint[i] && */ - /* - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i] ) && - */ - /* has (-) edge */ - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - iat_SB_N_Minus[num_SB_N_Minus ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( num_try = inchi_min( num_SB_N_Minus, num_DB_O ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_SB_N_Minus && cur_success < num_try; i ++ ) { - iat = iat_SB_N_Minus[i]; - pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; /* 2006-03-03: changed from CPlusGroupEdge */ - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Added (-) charge to =O => nDeltaCharge == 1 */ - /* Flow change on pe (-)charge edge (atom -N(-)-) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 03 */ - } - } else { - pe->forbidden &= forbidden_edge_mask_inv; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pc2i->nNumTgRevrs == 1 && /* pc2i->nNumRemHInChI < 0 &&*/ - (pc2i->nNumEndpRevrs > pc2i->nNumEndpInChI || pc2i->nNumTgInChI > 1) /* ADP in Revrs */ ) { - /*--------------------------------------------------------------*/ - /* case 03a:restored: -N(-)-AB=O original: -N=AB-O(-) */ - /* FixH: 0 0 0 0 */ - /* MobH: 0 0 0 0 */ - /* O = O, S, Se; N = N; taut */ - /* restored atoms are tautomeric; original atom is; N may be. */ - /* restored struct has 1 t-group; original has less endpoints */ - /* and possibly >1 t-groups */ - /* Solution: move (-) from N(-) to =O */ - /* these atoms are tautomeric in restored structure */ - /*--------------------------------------------------------------*/ - int num_SB_N_Minus = 0, num_DB_O = 0, iat; - short iat_SB_N_Minus[MAX_DIFF_FIXH], iat_DB_O[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - S_CHAR *pnMobHInChI = (pInChI[1] && pInChI[1]->nNum_H)? pInChI[1]->nNum_H : - (pInChI[0] && pInChI[0]->nNum_H)? pInChI[0]->nNum_H : NULL; - S_CHAR *pnFixHInChI = pStruct->fixed_H; - - cur_success = 0; - CurrEdges.num_edges = 0; - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( /* in restored atom N: charge=-1, no H, has no double bond, endpoint */ - num_SB_N_Minus < MAX_DIFF_FIXH && - at2[iat].charge == -1 && /*!at2[iat].num_H &&*/ - at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && - at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint && - /* in orig.InChI: may be an endpoint, has no H */ - /* has (-) edge */ - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - iat_SB_N_Minus[num_SB_N_Minus ++] = iat; - } else - if ( num_DB_O < MAX_DIFF_FIXH && - at2[iat].charge == 0 && /*!at2[iat].num_H &&*/ - at2[iat].valence+1 == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - pVA[iat].cNumValenceElectrons == 6 && - at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint && /* endpoint in Reconstructed */ - (pStruct->endpoint[i] || /* endpoint or H(+) acceptor in original */ - pnMobHInChI && pnMobHInChI[i] == 1 && pnFixHInChI && pnFixHInChI[i] == -1 ) && - /* has (-) edge */ - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - iat_DB_O[num_DB_O ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( num_try = inchi_min( num_SB_N_Minus, num_DB_O ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - /* allow charge transfer to all found =O */ - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_SB_N_Minus && cur_success < num_try; i ++ ) { - iat = iat_SB_N_Minus[i]; - pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Added (-) charge to =O => nDeltaCharge == 1 */ - /* Flow change on pe (-)charge edge (atom -N(-)-) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 03a */ - } - } else { - pe->forbidden &= forbidden_edge_mask_inv; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pc2i->len_c2at >= 1 && pc2i->nNumTgInChI == 1 && /* ADP in InChI */ - (pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1) ) { - /*--------------------------------------------------------------*/ - /* case 04: restored: OH(+)=AB-O- OH- orig. HO-AB=O(+)- OH- */ - /* FixH: 1 0 0 1 0 1 */ - /* MobH: 0 0 1 0 0 0 */ - /* non-taut. taut taut */ - /* ADP: one t-group or more endpoints */ - /* O(+) = N, P, As, As, O, S, Se; OH = N, O, S, Se, Te */ - /* Solution: move (+) from O(+) to NH2 */ - /*--------------------------------------------------------------*/ - int num_SB_Neutr = 0, num_DB_Charged = 0, iat; - short iat_SB_Neutr[MAX_DIFF_FIXH], iat_DB_Charged[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - cur_success = 0; - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( /* in restored atom: charge=+1, has H, has double bond, N, O, S, Se, Te */ - num_DB_Charged < MAX_DIFF_FIXH && - at2[iat].charge == 1 && at2[iat].num_H && - at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - (pVA[iat].cNumValenceElectrons == 6 || - pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1) && - /* in orig.InChI: an endpoint, has fixed-H */ - pStruct->endpoint[i] && - (pStruct->fixed_H && pStruct->fixed_H[i]) && - /*!(nMobHInChI && nMobHInChI[i] ) &&*/ - /* has (+) edge */ - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - - iat_DB_Charged[num_DB_Charged ++] = iat; - - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else - if ( /* in restored atom: charge=0, has no H, has no double bond, N, P, O, S, Se, Te */ - num_SB_Neutr < MAX_DIFF_FIXH && - at2[iat].charge == 0 && !at2[iat].num_H && - at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - (pVA[iat].cNumValenceElectrons == 6 || - pVA[iat].cNumValenceElectrons == 5 ) && - /* in orig.InChI: an endpoint, has fixed-H */ - /* pStruct->endpoint[i] && */ - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i] ) && - /* has (+) edge */ - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && - 0 == pBNS->edge[e].forbidden ) { - - iat_SB_Neutr[num_SB_Neutr ++] = iat; - - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( num_try = inchi_min( num_SB_Neutr, num_DB_Charged ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_SB_Neutr && cur_success < num_try; i ++ ) { - iat = iat_SB_Neutr[i]; - pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { - /* Removed charge from O(+) => nDeltaCharge == -1 */ - /* Flow change on pe (+)charge edge (atom NH2) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 04 */ - } - } else { - pe->forbidden &= forbidden_edge_mask_inv; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pc2i->len_c2at > 1 ) { - /*--------------------------------------------------------------*/ - /* case 05: restored: O=AB-NH original:(-)O-AB=NH(+) */ - /* FixH: 0 0 -1 1 */ - /* MobH: 0 1 1 0 */ - /* O = O, S, Se; N = N, O, S, Se, Te; all atoms not tautomeric */ - /* Solution: Separate charges */ - /*--------------------------------------------------------------*/ - int num_DB_O = 0, num_SB_NH = 0, iat; - short iat_DB_O[MAX_DIFF_FIXH], iat_SB_NH[MAX_DIFF_FIXH]; - cur_success = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( /* orig. InChI info: =NH2(+), =OH(+) */ - num_SB_NH < MAX_DIFF_FIXH && - (pc2i->c2at[i].nValElectr == 5 && pc2i->c2at[i].nPeriodNum == 1 || - pc2i->c2at[i].nValElectr == 6 ) /* N, O, S, Se, Te */ && - !pc2i->c2at[i].endptInChI && - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pc2i->c2at[i].nFixHInChI == 1 && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ - /* reversed structure info: */ - pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs && - pc2i->c2at[i].nAtChargeRevrs == 0 && at2[iat].num_H && - !pc2i->c2at[i].endptRevrs && - at2[iat].valence == at2[iat].chem_bonds_valence ) { - iat_SB_NH[num_SB_NH ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else - if ( /* orig. InChI info: -O(-) */ - num_DB_O < MAX_DIFF_FIXH && - (pc2i->c2at[i].nValElectr == 6 ) /* O, S, Se, Te */ && - !pc2i->c2at[i].endptInChI && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pc2i->c2at[i].nFixHInChI == -1 && pc2i->c2at[i].nMobHInChI == 1 && - /* reversed structure info: */ - pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && - pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && - !pc2i->c2at[i].endptRevrs && - at2[iat].valence + 1 == at2[iat].chem_bonds_valence ) { - iat_DB_O[num_DB_O ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( num_try = inchi_min( num_DB_O, num_SB_NH ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_SB_NH && cur_success < num_try; i ++ ) { - iat = iat_SB_NH[i]; - pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Added charge to =O => nDeltaCharge == 1 */ - /* Flow change on pe (+)charge edge (atom NH2) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 05 */ - } - } else { - pe->forbidden &= forbidden_edge_mask_inv; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pStruct->fixed_H && pStruct->endpoint && pc2i->nChargeFixHInChI > 0 && pc2i->nChargeFixHInChI > pc2i->nChargeMobHInChI ) { - /*----------------------------------------------------------*/ - /* case 06c: restored -NH- or -NH(+) orig: -NH- */ - /* Fixed-H 1 1 0 */ - /* Mobile-H 0 0 1 */ - /* not tautomeric not tautomeric */ - /* has adjacent (+) */ - /* charges */ - /* Solution: move (+) charges to the -NH- unless it already*/ - /* N = N, O, S, Se, Te */ - /* has (+) charge blocked by adjacent (+) */ - /*----------------------------------------------------------*/ - int iat; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - /* - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - inp_ATOM *atfMobile_H_Revrs = pStruct->pOne_norm_data[TAUT_YES] && - pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds? - pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds : NULL; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : NULL; - */ - EDGE_LIST CurChargeEdges; - EdgeIndex e2; - cur_success = 0; - AllocEdgeList( &CurChargeEdges, EDGE_LIST_CLEAR ); - CurrEdges.num_edges = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - /* atoms -NH- from which H(+) were removed by the Normalization in orig. InChI */ - iat = pc2i->c2at[i].atomNumber; - if ( (pc2i->c2at[i].nValElectr == 6 || - pc2i->c2at[i].nValElectr == 5 && pc2i->c2at[i].nPeriodNum == 1) && - !pc2i->c2at[i].endptInChI && - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - if ( /* orig. InChI info: -NH- */ - pc2i->c2at[i].nFixHInChI == 1 && pc2i->c2at[i].nMobHInChI == 0 && - /* reversed structure info: */ - pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 1 && /* was not removed */ - /*pc2i->c2at[i].nAtChargeRevrs == 0 &&*/ at2[iat].num_H && /* at2 is Fixed-H */ - at2[iat].valence == at2[iat].chem_bonds_valence ) { - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - } - for ( i = 0; i < pStruct->num_atoms; i ++ ) { - /* find adjacent charged atoms */ - iat = nCanon2AtnoRevrs[i]; - if ( pStruct->endpoint[i] || at2[iat].charge != 1 || at2[iat].radical || pVA[iat].cMetal ) { - continue; - } - if ( 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && !pBNS->edge[e].flow && pVA[iat].cNumValenceElectrons >= 5 ) { - /* positively charged atom */ - for ( j = 0; j < at2[iat].valence; j ++ ) { - if ( at2[k=(int)at2[iat].neighbor[j]].charge == 1 && !pVA[k].cMetal && - 0 <= (e2=pVA[k].nCPlusGroupEdge-1) && !pBNS->edge[e2].forbidden && !pBNS->edge[e2].flow) { - if ( 0 > FindInEdgeList( &CurrEdges, e ) && - 0 > FindInEdgeList( &CurChargeEdges, e ) && - ( ret = AddToEdgeList( &CurChargeEdges, e, INC_ADD_EDGE ) ) ) { - goto exit_case_06c; - } - if ( 0 > FindInEdgeList( &CurrEdges, e2 ) && - 0 > FindInEdgeList( &CurChargeEdges, e2 ) && - ( ret = AddToEdgeList( &CurChargeEdges, e2, INC_ADD_EDGE ) ) ) { - goto exit_case_06c; - } - } - } - } - } - if ( num_try = inchi_min( CurrEdges.num_edges, CurChargeEdges.num_edges ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurChargeEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < CurrEdges.num_edges && cur_success < num_try; i ++ ) { - e = CurrEdges.pnEdges[i]; - pe = pBNS->edge + e; /* (+)charge edge of -NH- or -OH */ - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->flow -= delta; /* add (+) to -NHm */ - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { - /* Removed (+)charge from -NH- => nDeltaCharge == -1 */ - /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 06c */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } -exit_case_06c: - CurrEdges.num_edges = 0; /* clear current edge list */ - AllocEdgeList( &CurChargeEdges, EDGE_LIST_FREE ); - if ( ret < 0 ) { - goto exit_function; - } - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pc2i->len_c2at >= 2 ) { - /*------------------------------------------------------------*/ - /* case 06d: restored: XH(+)=-AB-NH orig.: XH-=AB=NH(+) */ - /* FixH: 1 1 0 0 1 1 */ - /* MobH: 0 taut 1 1 taut 0 */ - /* */ - /* */ - /* N = N, O, S, Se; atoms N are not tautomeric in orig InChI */ - /* X = N, O, S, Se, Te, F, Cl, Br, I; atom X is non-taut */ - /* Solution: move (+) from X to NH */ - /*------------------------------------------------------------*/ - int iat; - /* - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - inp_ATOM *atfMobile_H_Revrs = pStruct->pOne_norm_data[TAUT_YES] && - pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds? - pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds : - pStruct->pOne_norm_data[TAUT_NON]->at; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - */ - EDGE_LIST CurChargeEdges; - cur_success = 0; - AllocEdgeList( &CurChargeEdges, EDGE_LIST_CLEAR ); - CurrEdges.num_edges = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - /* XH(+) */ - if ( /* reconstructed: non-taut and (+) */ - (pc2i->c2at[i].nMobHRevrs+1 == pc2i->c2at[i].nFixHRevrs && - pc2i->c2at[i].nFixHRevrs > 0 && !pc2i->c2at[i].endptRevrs && - pc2i->c2at[i].nAtChargeRevrs == 1 && - /* original InChI: non-taut & has H or an endpoint, has Fixed H */ - (!pc2i->c2at[i].nFixHInChI && pc2i->c2at[i].nMobHInChI == pc2i->c2at[i].nFixHRevrs || - pc2i->c2at[i].nFixHInChI == pc2i->c2at[i].nFixHRevrs && pc2i->c2at[i].endptInChI )) && - 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && !pBNS->edge[e].flow) { - - if (ret = AddToEdgeList( &CurChargeEdges, e, INC_ADD_EDGE )) { - goto exit_case_06d; - } - } else - /* -NH- */ - if ( /* original InChI: has H and is not an endpoint */ - (pc2i->c2at[i].nMobHInChI+1 == pc2i->c2at[i].nFixHInChI && - pc2i->c2at[i].nFixHInChI > 0 && !pc2i->c2at[i].endptInChI && - pc2i->c2at[i].nAtChargeRevrs == 0 && - /* reconstructed InChI: non-taut & has H or an endpoint, has Fixed H */ - (!pc2i->c2at[i].nFixHRevrs && pc2i->c2at[i].nMobHRevrs == pc2i->c2at[i].nFixHInChI || - pc2i->c2at[i].nFixHRevrs == pc2i->c2at[i].nFixHInChI && pc2i->c2at[i].endptRevrs )) && - 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && - pBNS->edge[e].flow) { - - if (ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE )) { - goto exit_case_06d; - } - } - } - if ( num_try = inchi_min( CurrEdges.num_edges, CurChargeEdges.num_edges ) ) { - /* detected; attempt to fix */ - int bSFlowerEdgesMayBeForbidden = (SFlowerEdges.num_edges > 0); - int bSFlowerEdgesIsForbidden; - for ( bSFlowerEdgesIsForbidden = bSFlowerEdgesMayBeForbidden; - 0 <= bSFlowerEdgesIsForbidden; bSFlowerEdgesIsForbidden -- ) { - if ( bSFlowerEdgesIsForbidden ) { - /* on the 1st pass disallow -S(+)= => =S=, allow only -S(+)= => -S- */ - SetForbiddenEdgeMask( pBNS, &SFlowerEdges, forbidden_edge_mask ); - } - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurChargeEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < CurrEdges.num_edges && cur_success < num_try; i ++ ) { - e = CurrEdges.pnEdges[i]; - pe = pBNS->edge + e; /* (+)charge edge of -NH- or -OH */ - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->flow -= delta; /* add (+) to -NHm */ - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { - /* Removed (+)charge from -NH- => nDeltaCharge == -1 */ - /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 06d */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &SFlowerEdges, forbidden_edge_mask ); - } - - } -exit_case_06d: - CurrEdges.num_edges = 0; /* clear current edge list */ - AllocEdgeList( &CurChargeEdges, EDGE_LIST_FREE ); - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - - if ( pc2i->len_c2at >= 2 ) { - /*--------------------------------------------------------*/ - /* case 06: restored: NHn(+)=AB-NHm orig.: NHn-AB=NHm(+) */ - /* FixH: 1 0 0 1 */ - /* MobH: n-1 m n m-1 */ - /* N = N, O, S, Se; atoms N are not tautomeric */ - /* Solution: move (+) from NHn(+) to NHn */ - /*--------------------------------------------------------*/ - int num_DB_NHn_Plus = 0, num_SB_NHm_Neutr = 0, iat; - short iat_DB_NHn_Plus[MAX_DIFF_FIXH], iat_SB_NHm_Neutr[MAX_DIFF_FIXH]; - cur_success = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( (pc2i->c2at[i].nValElectr == 6 || - pc2i->c2at[i].nValElectr == 5 && pc2i->c2at[i].nPeriodNum == 1) && - !pc2i->c2at[i].endptInChI && - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - if ( /* orig. InChI info: NHm */ - num_SB_NHm_Neutr < MAX_DIFF_FIXH && - pc2i->c2at[i].nFixHInChI == 1 && /*pc2i->c2at[i].nMobHInChI == 0 &&*/ - /* reversed structure info: */ - pc2i->c2at[i].nFixHRevrs == 0 && /*pc2i->c2at[i].nMobHRevrs == 1 &&*/ - pc2i->c2at[i].nAtChargeRevrs == 0 && at2[iat].num_H && /* at2 is Fixed-H */ - at2[iat].valence == at2[iat].chem_bonds_valence ) { - iat_SB_NHm_Neutr[num_SB_NHm_Neutr ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else - if ( /* orig. InChI info: */ - num_DB_NHn_Plus < MAX_DIFF_FIXH && - pc2i->c2at[i].nFixHInChI == 0 && /*pc2i->c2at[i].nMobHInChI &&*/ - /* reversed structure info: */ - pc2i->c2at[i].nFixHRevrs == 1 && /*pc2i->c2at[i].nMobHRevrs == 0 &&*/ - pc2i->c2at[i].nAtChargeRevrs == 1 && at2[iat].num_H && - at2[iat].valence < at2[iat].chem_bonds_valence ) { - iat_DB_NHn_Plus[num_DB_NHn_Plus ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - } - if ( num_try = inchi_min( num_SB_NHm_Neutr, num_DB_NHn_Plus ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_SB_NHm_Neutr && cur_success < num_try; i ++ ) { - iat = iat_SB_NHm_Neutr[i]; - pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; /* add (+) to -NHm */ - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { - /* Removed (+)charge from -NHn => nDeltaCharge == -1 */ - /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 06 */ - } - } else { - pe->forbidden &= forbidden_edge_mask_inv; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( (pc2i->nNumTgInChI > pc2i->nNumTgRevrs && pc2i->nNumTgRevrs == 1 || - pc2i->nNumEndpInChI < pc2i->nNumEndpRevrs ) && - pStruct->nNumRemovedProtonsMobHInChI == pStruct->One_ti.tni.nNumRemovedProtons && - pStruct->fixed_H && pStruct->endpoint && pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds ) { - /*----------------------------------------------------------*/ - /* case 06a: restored: N'(+)=-AB-NH orig.: N'-=AB=NH(+) */ - /* FixH: 0 1 0 1 */ - /* MobH: 0 0 0 0 */ - /* single t-group multiple t-groups */ - /* N = N, O, S, Se; atoms N are not tautomeric */ - /* N' = N atom N' is not tautomeric */ - /* Solution: move (+) from N' to NH */ - /*----------------------------------------------------------*/ - int iat; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - /* - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - */ - inp_ATOM *atfMobile_H_Revrs = pStruct->pOne_norm_data[TAUT_YES] && - pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds? - pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds : NULL; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - EDGE_LIST CurChargeEdges; - cur_success = 0; - AllocEdgeList( &CurChargeEdges, EDGE_LIST_CLEAR ); - CurrEdges.num_edges = 0; - for ( i = 0; i < pStruct->num_atoms; i ++ ) { - iat = nCanon2AtnoRevrs[i]; - if ( pStruct->endpoint[i] ) { - continue; - } - /* -NH-, -OH */ - if ( pStruct->fixed_H[i] && !nMobHInChI[i] && - at2[iat].charge == 0 && at2[iat].radical == 0 && - 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && - (ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ))) { - goto exit_case_06a; - } else - /* >N(+)= */ - if ( at2[iat].charge == 1 && !at2[iat].num_H && - pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && - atfMobile_H_Revrs && atfMobile_H_Revrs[iat].charge == 0 && - 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && !pBNS->edge[e].flow && - (ret = AddToEdgeList( &CurChargeEdges, e, INC_ADD_EDGE ))) { - goto exit_case_06a; - } - } - if ( num_try = inchi_min( CurrEdges.num_edges, CurChargeEdges.num_edges ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurChargeEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < CurrEdges.num_edges && cur_success < num_try; i ++ ) { - e = CurrEdges.pnEdges[i]; - pe = pBNS->edge + e; /* (+)charge edge of -NH- or -OH */ - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->flow -= delta; /* add (+) to -NHm */ - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { - /* Removed (+)charge from -NH- => nDeltaCharge == -1 */ - /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 06a */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } -exit_case_06a: - CurrEdges.num_edges = 0; /* clear current edge list */ - AllocEdgeList( &CurChargeEdges, EDGE_LIST_FREE ); - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - if ( (pc2i->nNumTgInChI > pc2i->nNumTgRevrs && pc2i->nNumTgRevrs == 1 || - pc2i->nNumEndpInChI < pc2i->nNumEndpRevrs ) && - (pStruct->nNumRemovedProtonsMobHInChI == pStruct->One_ti.tni.nNumRemovedProtons || - pStruct->nNumRemovedProtonsMobHInChI > pStruct->One_ti.tni.nNumRemovedProtons ) && - pStruct->fixed_H && pStruct->endpoint && pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds ) { - /*----------------------------------------------------------*/ - /* case 06b: restored: X(+)=-AB-NH orig.: X-=AB=NH(+) */ - /* FixH: 0 1 1 0 1 */ - /* MobH: 0 0 t 0 0 */ - /* single t-group multiple t-groups */ - /* or no t-groupd */ - /* N = N, O, S, Se; atoms N are not tautomeric */ - /* X = O, S, Se, Te, F, Cl, Br, I; atom X is not tautomeric*/ - /* Solution: move (+) from X to NH */ - /*----------------------------------------------------------*/ - int iat; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - /* - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - */ - inp_ATOM *atfMobile_H_Revrs = pStruct->pOne_norm_data[TAUT_YES] && - pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds? - pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds : NULL; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - EDGE_LIST CurChargeEdges; - cur_success = 0; - AllocEdgeList( &CurChargeEdges, EDGE_LIST_CLEAR ); - CurrEdges.num_edges = 0; - for ( i = 0; i < pStruct->num_atoms; i ++ ) { - iat = nCanon2AtnoRevrs[i]; - if ( pStruct->endpoint[i] ) { - continue; - } - /* -NH-, -OH */ - if ( pStruct->fixed_H[i] && !nMobHInChI[i] && - at2[iat].charge == 0 && at2[iat].radical == 0 && - 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && - (ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ))) { - goto exit_case_06b; - } else - /* X(+)= */ - if ( at2[iat].charge == 1 && !at2[iat].num_H && - (pVA[iat].cNumValenceElectrons == 6 || pVA[iat].cPeriodicRowNumber == 7) && - atfMobile_H_Revrs && atfMobile_H_Revrs[iat].charge == 1 && - 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && !pBNS->edge[e].flow && - (ret = AddToEdgeList( &CurChargeEdges, e, INC_ADD_EDGE ))) { - goto exit_case_06b; - } - } - if ( num_try = inchi_min( CurrEdges.num_edges, CurChargeEdges.num_edges ) ) { - /* detected; attempt to fix */ - int bSFlowerEdgesMayBeForbidden = (SFlowerEdges.num_edges > 0); - int bSFlowerEdgesIsForbidden; - for ( bSFlowerEdgesIsForbidden = bSFlowerEdgesMayBeForbidden; - 0 <= bSFlowerEdgesIsForbidden; bSFlowerEdgesIsForbidden -- ) { - if ( bSFlowerEdgesIsForbidden ) { - /* on the 1st pass disallow -S(+)= => =S=, allow only -S(+)= => -S- */ - SetForbiddenEdgeMask( pBNS, &SFlowerEdges, forbidden_edge_mask ); - } - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurChargeEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < CurrEdges.num_edges && cur_success < num_try; i ++ ) { - e = CurrEdges.pnEdges[i]; - pe = pBNS->edge + e; /* (+)charge edge of -NH- or -OH */ - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->flow -= delta; /* add (+) to -NHm */ - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { - /* Removed (+)charge from -NH- => nDeltaCharge == -1 */ - /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 06b */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &SFlowerEdges, forbidden_edge_mask ); - } - - } -exit_case_06b: - CurrEdges.num_edges = 0; /* clear current edge list */ - AllocEdgeList( &CurChargeEdges, EDGE_LIST_FREE ); - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - - - if ( pc2i->nNumTgInChI > 1 && - (pStruct->nNumRemovedProtonsMobHInChI > 0 || pStruct->ti.tni.nNumRemovedProtons > 0 ) && - pStruct->fixed_H && pStruct->endpoint && - pStruct->pOne_norm_data[TAUT_YES] && pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds ) { - /*----------------------------------------------------------*/ - /* case 06e:restored: XHn(+)=-AB-YHm orig.: XHn-=AB=YHm(+) */ - /* FixH: 1 0 1 1 */ - /* MobH: 0 1 t t */ - /* non-taut atoms multiple t-groups */ - /* */ - /* 1. orig. t-group has more H on its endpoints counted */ - /* in atf and has no (+) on endpoint that has H */ - /* 2. orig. t-group has less H on its endpoints counted */ - /* in atf and has (+) on endpoint that has H */ - /* in reconstructed struct and less H in atf */ - /* Solution: move (+) from (2) to atom in (1) that has H */ - /* */ - /* tg1 reconstr: XHn and more H than in orig t-group */ - /* atf: XHn */ - /* tg2 reconstr: XHm(+) and less H than in */ - /* atf: XH(m-1) orig in t-group */ - /* */ - /* N = N, O, S, Se; atoms N are not tautomeric */ - /* X = O, S, Se, Te, F, Cl, Br, I; atom X is not tautomeric*/ - /* Solution: move (+) from X to NH */ - /*----------------------------------------------------------*/ - - int iat, nNumWrongTg, jjoffs, jj, nNum2RemovePlus, nNum2AddPlus, nNum2MovePlus; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - /* - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - */ - inp_ATOM *atfMobile_H_Revrs = pStruct->pOne_norm_data[TAUT_YES] && - pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds? - pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds : - pStruct->pOne_norm_data[TAUT_YES] && - pStruct->pOne_norm_data[TAUT_YES]->at? - pStruct->pOne_norm_data[TAUT_YES]->at : NULL; - /* - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - */ - EDGE_LIST CurChargeEdges /* source of (+)*/, EndpList; - TgDiffHChgFH tdhc[MAX_DIFF_FIXH]; - BNS_VERTEX *pv1n, *pv2n; - BNS_EDGE *pe1n, *pe2n; - Vertex v1n, v2n; - - cur_success = 0; - AllocEdgeList( &CurChargeEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &EndpList, EDGE_LIST_CLEAR ); - CurrEdges.num_edges = 0; /* receptors of (+) */ - if ( !atfMobile_H_Revrs ) { - goto exit_case_06e; - } - nNumWrongTg = FillTgDiffHChgFH( tdhc, MAX_DIFF_FIXH, at2, atfMobile_H_Revrs, - nCanon2AtnoRevrs, pVA, &pStruct->ti, &EndpList ); - if ( nNumWrongTg < 1 ) { - goto exit_case_06e; /* for now only transfer (+) from one Mobile-H group to another */ - } - nNum2RemovePlus = nNum2AddPlus = nNum2MovePlus = 0; - for ( i = 0; i < nNumWrongTg; i ++ ) { - /* detect t-group that has extra (+) on H */ - if ( tdhc[i].nNumHInchi > tdhc[i].nNumHNorml && - tdhc[i].nNumPRevrs > tdhc[i].nNumPNorml && tdhc[i].n[fNumRPosChgH] ) { - /* count how many (+) to remove */ - /* store XH(+) atom numbers */ - int nNumNeeded = inchi_min( tdhc[i].nNumHInchi-tdhc[i].nNumHNorml, tdhc[i].n[fNumRPosChgH]); - nNum2RemovePlus += nNumNeeded; - jjoffs = tdhc[i].i[ fNumRPosChgH ]; - for ( jj = 0; jj < tdhc[i].n[fNumRPosChgH]; jj ++ ) { - iat = EndpList.pnEdges[ jjoffs + jj ]; - e = pVA[iat].nCPlusGroupEdge-1; - if ( ret = AddToEdgeList( &CurChargeEdges, e, INC_ADD_EDGE ) ) { - goto exit_case_06e; - } - } - } else - /* detect t-group that needs (+) on XH to reduce number of H */ - if ( tdhc[i].nNumHInchi < tdhc[i].nNumHNorml && tdhc[i].n[fNumRNeutrlH] ) { - /* store XH atom numbers */ - int nNumNeeded = inchi_min( tdhc[i].nNumHNorml-tdhc[i].nNumHInchi, tdhc[i].n[fNumRNeutrlH]); - nNum2AddPlus += nNumNeeded; - jjoffs = tdhc[i].i[ fNumRNeutrlH ]; - for ( jj = 0; jj < tdhc[i].n[fNumRNeutrlH]; jj ++ ) { - iat = EndpList.pnEdges[ jjoffs + jj ]; - e = pVA[iat].nCPlusGroupEdge-1; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_case_06e; - } - } - } - } - nNum2MovePlus = inchi_min( nNum2RemovePlus, nNum2AddPlus ); - if ( CurrEdges.num_edges > 0 && CurChargeEdges.num_edges > 0 ) { - for ( i = 0; 0 < nNum2MovePlus && i < nNumWrongTg; i ++ ) { - /* detect t-group that has extra (+) on H */ - if ( tdhc[i].nNumHInchi > tdhc[i].nNumHNorml && - tdhc[i].nNumPRevrs > tdhc[i].nNumPNorml && tdhc[i].n[fNumRPosChgH] ) { - int nNum2Remove = tdhc[i].nNumHInchi - tdhc[i].nNumHNorml; - if ( nNum2Remove < tdhc[i].n[fNumRPosChgH] ) { - nNum2Remove = tdhc[i].n[fNumRPosChgH]; - } - /* store XH(+) atom numbers */ - jjoffs = tdhc[i].i[ fNumRPosChgH ]; - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - for ( jj = 0; 0 < nNum2MovePlus && 0 < nNum2Remove && jj < tdhc[i].n[fNumRPosChgH]; jj ++ ) { - iat = EndpList.pnEdges[ jjoffs + jj ]; - e = pVA[iat].nCPlusGroupEdge-1; - pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; - if ( pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - for ( j = pv1->num_adj_edges-1; 0 <= j; j -- ) { - pe1n = pBNS->edge + pv1->iedge[j]; - if ( pe1n->flow && !pe1n->forbidden ) { - pv1n = pBNS->vert + (v1n = pe1n->neighbor12 ^ v1); - break; - } - } - if ( j < 0 ) - continue; /* not found */ - - for ( j = pv2->num_adj_edges-2; 0 <= j; j -- ) { - pe2n = pBNS->edge + pv2->iedge[j]; - if ( pe2n->flow && !pe2n->forbidden ) { - pv2n = pBNS->vert + (v2n = pe2n->neighbor12 ^ v2); - break; - } - } - if ( j < 0 ) - continue; /* not found */ - delta = 1; - pe->flow += delta; - pe1n->flow -= delta; - pe2n->flow -= delta; - pv1n->st_edge.flow -= delta; - pv2n->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1n && vPathStart == v2n || - vPathEnd == v2n && vPathStart == v1n) && - (nDeltaCharge == 0 || nDeltaCharge == 1) ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - nNum2Remove --; - nNum2MovePlus --; - cur_success ++; /* 06e */ - } - } else { - pe->flow -= delta; - pe1n->flow += delta; - pe2n->flow += delta; - pv1n->st_edge.flow += delta; - pv2n->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_case_06e; - } - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - } - } -exit_case_06e: - CurrEdges.num_edges = 0; /* clear current edge list */ - AllocEdgeList( &CurChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &EndpList, EDGE_LIST_FREE ); - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - - - if ( pc2i->len_c2at >= 1 ) { - /*--------------------------------------------------------------*/ - /* case 07: restored: O(-)-AB=O original: O=AB-O(-) */ - /* FixH: 0 0 0 -1 */ - /* MobH: 0 0 0 1 */ - /* taut (non-taut) (taut) non-taut */ - /* taut (taut) (non-taut) non-taut */ - /* O = O, S, Se, Te */ - /* Solution: move (-) from O(-)-AB to AB=O */ - /*--------------------------------------------------------------*/ - int num_SB_O_Minus = 0, num_DB_O_Neutr = 0, iat; - short iat_SB_O_Minus[MAX_DIFF_FIXH], iat_DB_O_Neutr[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - cur_success = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( /* orig. InChI info: -O(-), non-taut */ - num_DB_O_Neutr < MAX_DIFF_FIXH && - pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ && - !pc2i->c2at[i].endptInChI && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pc2i->c2at[i].nFixHInChI == -1 && pc2i->c2at[i].nMobHInChI == 1 && - /* reversed structure info: */ - pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && - pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && - at2[iat].valence < at2[iat].chem_bonds_valence ) { - iat_DB_O_Neutr[num_DB_O_Neutr ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( /* in restored atom: charge=-1, no H, has single bond, O, S, Se, Te */ - num_SB_O_Minus < MAX_DIFF_FIXH && - at2[iat].charge == -1 && !at2[iat].num_H && - at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - pVA[iat].cNumValenceElectrons == 6 && - at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint && - /* in orig.InChI: not an endpoint, has no H */ - /*pStruct->endpoint[i] && -- modificatuion#1 */ - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i] ) && - /* has (-) edge */ - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - iat_SB_O_Minus[num_SB_O_Minus ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( num_try = inchi_min( num_SB_O_Minus, num_DB_O_Neutr ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_SB_O_Minus && cur_success < num_try; i ++ ) { - iat = iat_SB_O_Minus[i]; - pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Moved (-) charge to AB=O => nDeltaCharge == 1 */ - /* Flow change on pe (-)charge edge (O(-)-AB) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 07 */ - } - } else { - pe->forbidden &= forbidden_edge_mask_inv; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pc2i->len_c2at >= 1 ) { - /*--------------------------------------------------------------*/ - /* case 07a: restored: O(-)-N(V)B=O original: O=N(V)B-O(-) */ - /* FixH: 0 0 0 -1 */ - /* MobH: 0 0 0 1 */ - /* non-taut (non-taut) non-taut non-taut */ - /* non-taut (taut) non-taut non-taut */ - /* O = O, S, Se, Te */ - /* Solution: move (-) from O(-)-AB to AB=O */ - /*--------------------------------------------------------------*/ - int num_SB_O_Minus = 0, num_DB_O_Neutr = 0, iat, iN; - short iat_SB_O_Minus[MAX_DIFF_FIXH], iat_DB_O_Neutr[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - cur_success = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( /* orig. InChI info: -O(-), non-taut */ - num_DB_O_Neutr < MAX_DIFF_FIXH && - pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ && - !pc2i->c2at[i].endptInChI && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pc2i->c2at[i].nFixHInChI == -1 && pc2i->c2at[i].nMobHInChI == 1 && - /* reversed structure info: */ - pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && - pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && - at2[iat].valence < at2[iat].chem_bonds_valence ) { - iat_DB_O_Neutr[num_DB_O_Neutr ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( /* in restored atom: charge=-1, no H, has single bond, O, S, Se, Te */ - num_SB_O_Minus < MAX_DIFF_FIXH && - at2[iat].charge == -1 && !at2[iat].num_H && - at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - pVA[iat].cNumValenceElectrons == 6 && - /*at_Mobile_H_Revrs && !at_Mobile_H_Revrs[iat].endpoint &&*/ - /* in orig.InChI: not an endpoint, has no H */ - !pStruct->endpoint[i] && - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i] ) && - /* has N(V) neighbor */ - 1 == at2[iat].valence && at2[iN=at2[iat].neighbor[0]].chem_bonds_valence==5 && - !at2[iN].charge && pVA[iN].cNumValenceElectrons == 5 && - /* has (-) edge */ - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - iat_SB_O_Minus[num_SB_O_Minus ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( num_try = inchi_min( num_SB_O_Minus, num_DB_O_Neutr ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_SB_O_Minus && cur_success < num_try; i ++ ) { - iat = iat_SB_O_Minus[i]; - pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Moved (-) charge to AB=O => nDeltaCharge == 1 */ - /* Flow change on pe (-)charge edge (O(-)-AB) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 07 */ - } - } else { - pe->forbidden &= forbidden_edge_mask_inv; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - if ( /*(pc2i->len_c2at >= 1 || pc2i->nNumRemHRevrs) &&*/ pc2i->nNumTgInChI == 1 && /* ADP in InChI */ - (pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1) ) { - /*----------------------------------------------------------------*/ - /* case 08: restored: O(-)-AB=N- OH- orig. O=AB-N(-)- OH- */ - /* FixH: 1 0 0 0 0 1 */ - /* MobH: 0 0 1 0 0 0 */ - /* may be taut or not non-taut taut taut taut */ - /* ADP: one t-group or more endpoints */ - /* O(-) = S, Se, Te; N = N; */ - /* Solution: move (-) from O(-) to =N-; avoid stereogenic DB on N */ - /*----------------------------------------------------------------*/ - int num_DB_N_Neutr = 0, num_SB_O_Minus = 0, iat; - short iat_DB_N_Neutr[MAX_DIFF_FIXH], iat_SB_O_Minus[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - cur_success = 0; - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( /* in restored atom: charge=-1, has no H, has single bond, O, S, Se, Te */ - num_SB_O_Minus < MAX_DIFF_FIXH && - at2[iat].charge == -1 && !at2[iat].num_H && - at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - pVA[iat].cNumValenceElectrons == 6 && - /* in orig.InChI: an endpoint, may have fixed-H */ - pStruct->endpoint[i] && - /*!(pStruct->fixed_H && pStruct->fixed_H[i]) &&*/ - !(nMobHInChI && nMobHInChI[i] ) && - /* has (-) edge */ - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - - iat_SB_O_Minus[num_SB_O_Minus ++] = iat; - - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else - if ( /* in restored atom: charge=0, has no H, has double non-stereogenic bond, N */ - num_DB_N_Neutr < MAX_DIFF_FIXH && - at2[iat].charge == 0 && !at2[iat].num_H && !at2[iat].sb_parity[0] && - at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && - /* in orig.InChI: an endpoint, has no fixed-H */ - pStruct->endpoint[i] && - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i] ) && - /* has (-) edge */ - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && - 0 == pBNS->edge[e].forbidden ) { - - iat_DB_N_Neutr[num_DB_N_Neutr ++] = iat; - - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( num_try = inchi_min( num_DB_N_Neutr, num_SB_O_Minus ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - /* allow stereobonds in rings change */ - if ( forbidden_stereo_edge_mask ) - RemoveForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask ); - - delta = 1; - for ( i = 0; i < num_SB_O_Minus && cur_success < num_try; i ++ ) { - iat = iat_SB_O_Minus[i]; - pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Moved (-) charge to =N- => nDeltaCharge == 1 */ - /* Flow change on pe (-)charge edge (atom (-)O-) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 08 */ - } - } else { - pe->forbidden &= forbidden_edge_mask_inv; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - if ( forbidden_stereo_edge_mask ) - SetForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pc2i->len_c2at >= 2 ) { - /*--------------------------------------------------------*/ - /* case 09: restored: NH2(+)=C--NH2 orig.: NH2-C(+)-NH2 */ - /* FixH: 2 | 2 0 | 0 */ - /* MobH: 0 0 2 2 */ - /* N = N, taut taut non-taut non-taut*/ - /* Solution: move (+) from NH2(+) to C */ - /*--------------------------------------------------------*/ - int iat; - cur_success = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( (pc2i->c2at[i].nValElectr == 5 && pc2i->c2at[i].nPeriodNum == 1) && - /* orig. InChI info: */ - !pc2i->c2at[i].endptInChI && - pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI && - /* reversed structure info: */ - pc2i->c2at[i].endptRevrs && - pc2i->c2at[i].nFixHRevrs && !pc2i->c2at[i].nMobHRevrs && - pc2i->c2at[i].nAtChargeRevrs == 1 && - at2[iat].valence + 1 == at2[iat].chem_bonds_valence && - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - EdgeIndex eNC = NO_VERTEX, eCPlusC; - int iNH2, iatC, iatNH2, icNH2; - /* found NH2(+)=; locate =C< and find whether it has -NH2 neighbor */ - for ( j = 0; j < at2[iat].valence; j ++ ) { - if ( at2[iat].bond_type[j] == BOND_TYPE_DOUBLE ) - break; - } - if ( j == at2[iat].valence ) - continue; - eNC = pBNS->vert[iat].iedge[j]; /* edge NH2(+)=C */ - iatC = at2[iat].neighbor[j]; - if ( pVA[iatC].cNumValenceElectrons != 4 || pVA[iatC].cMetal || at2[iatC].charge || - at2[iatC].valence != 3 || at2[iatC].valence+1 != at2[iatC].chem_bonds_valence || - (eCPlusC=pVA[iatC].nCPlusGroupEdge-1) < 0 || pBNS->edge[eCPlusC].forbidden) - continue; - for ( j = 0; j < at2[iatC].valence; j ++ ) { - iatNH2 = at2[iatC].neighbor[j]; - if ( iatNH2 == iat || pVA[iatNH2].cNumValenceElectrons != 5 || - pVA[iatNH2].cPeriodicRowNumber != 1 || !at2[iatNH2].num_H || at2[iatNH2].charge) - continue; - icNH2 = pStruct->nAtno2Canon[0][iatNH2]; - for ( iNH2 = 0; iNH2 < pc2i->len_c2at; iNH2 ++ ) { - if ( iatNH2 == pc2i->c2at[iNH2].atomNumber ) - break; - } - if ( iNH2 == pc2i->len_c2at ) - continue; - - if ( (pc2i->c2at[iNH2].nValElectr == 5 && pc2i->c2at[iNH2].nPeriodNum == 1) && - /* orig. InChI info: */ - !pc2i->c2at[iNH2].endptInChI && - pc2i->c2at[iNH2].nFixHInChI == 0 && pc2i->c2at[iNH2].nMobHInChI && - /* reversed structure info: */ - pc2i->c2at[iNH2].endptRevrs && - pc2i->c2at[iNH2].nFixHRevrs && !pc2i->c2at[iNH2].nMobHRevrs && - pc2i->c2at[iNH2].nAtChargeRevrs == 0 && - at2[iatNH2].valence == at2[iatNH2].chem_bonds_valence ) { - /* we have found NH2(+)=, =C<, and bond between them */ - - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &CurrEdges, eCPlusC, INC_ADD_EDGE ) ) { - goto exit_function; - } - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - - pe = pBNS->edge + eNC; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; /* add (+) to -NHm */ - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { - /* Removed (+)charge from -NHn => nDeltaCharge == -1 */ - /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 09 */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - pe->forbidden &= forbidden_edge_mask_inv; - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - CurrEdges.num_edges = 0; /* clear current edge list */ - break; - } - } - } - } - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - - if ( pc2i->len_c2at >= 2 ) { - /*--------------------------------------------------------*/ - /* case 10: restored: NH2-X(+)-NH- orig.: NH2(+)=X-NH- */ - /* FixH: 0 0 2 1 */ - /* MobH: 2 1 0 0 */ - /* N = N,O,S,Se,Te non-taut non-taut taut taut */ - /* Solution: move (+) from X(+) to NH2 or NH */ - /*--------------------------------------------------------*/ - int iat; - cur_success = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - if ( pc2i->c2at[i].nValue ) - continue; - iat = pc2i->c2at[i].atomNumber; - if ( (pc2i->c2at[i].nValElectr == 6 || - pc2i->c2at[i].nValElectr == 5 && pc2i->c2at[i].nPeriodNum == 1) && - /* orig. InChI info: */ - pc2i->c2at[i].endptInChI && - pc2i->c2at[i].nFixHInChI && !pc2i->c2at[i].nMobHInChI && - /* reversed structure info: */ - !pc2i->c2at[i].endptRevrs && - !pc2i->c2at[i].nFixHRevrs && pc2i->c2at[i].nMobHRevrs && - pc2i->c2at[i].nAtChargeRevrs == 0 && - at2[iat].valence == at2[iat].chem_bonds_valence && - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - - EdgeIndex eCPlusC, eCPlusNH2, bContinue=1; - int iNH2, iatC, iatNH2, icNH2, j1, j2; - BNS_EDGE *pe_iat, *pe_iNH2; - /* found NH2- locate -X(+) and find whether it has another -NH2 neighbor */ - for ( j1 = 0; j1 < at2[iat].valence && bContinue; j1 ++ ) { - if ( at2[iat].bond_type[j1] == BOND_TYPE_SINGLE && - at2[iatC = at2[iat].neighbor[j1]].charge == 1 && - (4 <= pVA[iatC].cNumValenceElectrons && pVA[iatC].cNumValenceElectrons <= 6) && - at2[iatC].valence == at2[iatC].chem_bonds_valence && - (eCPlusC=pVA[iatC].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[eCPlusC].forbidden) { - /* found a candidate for X; find another NH2 */ - for ( j2 = 0; j2 < at2[iatC].valence && bContinue; j2 ++ ) { - if ( at2[iatC].bond_type[j2] == BOND_TYPE_SINGLE && - iat != (iatNH2 = at2[iatC].neighbor[j2]) && - at2[iatNH2].charge == 0 && at2[iatNH2].num_H && - (pVA[iatNH2].cNumValenceElectrons==5 || pVA[iatNH2].cNumValenceElectrons==6) && - at2[iatNH2].valence == at2[iatNH2].chem_bonds_valence && - (eCPlusNH2=pVA[iatNH2].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[eCPlusNH2].forbidden) { - for ( iNH2 = 0; iNH2 < pc2i->len_c2at; iNH2 ++ ) { - if ( iatNH2 != pc2i->c2at[iNH2].atomNumber || pc2i->c2at[iNH2].nValue ) - continue; - /* check the second -NH */ - icNH2 = pStruct->nAtno2Canon[0][iatNH2]; /* canon number -1 */ - if ( /* orig. InChI info: */ - pc2i->c2at[iNH2].endptInChI && - pc2i->c2at[iNH2].nFixHInChI && !pc2i->c2at[iNH2].nMobHInChI && - /* reversed structure info: */ - !pc2i->c2at[iNH2].endptRevrs && - !pc2i->c2at[iNH2].nFixHRevrs && pc2i->c2at[iNH2].nMobHRevrs && - pc2i->c2at[iNH2].nAtChargeRevrs == 0 ) { - /* we have found NH-X(+)-NH; remove charge from X(+) */ - pe_iat = pBNS->edge + pBNS->vert[iat].iedge[j1]; - pe_iNH2 = pBNS->edge + pBNS->vert[iatC].iedge[j2]; - /* pick up one of -NH to move (+) to it */ - if ( !pe_iat->forbidden && pBNS->edge[e].flow ) { - pe = pBNS->edge + e; - } else - if ( !pe_iNH2->forbidden && pBNS->edge[eCPlusNH2].flow ) { - pe = pBNS->edge + eCPlusNH2; - } else { - continue; /* none of the two -X(+)- bonds may be changed */ - } - if ( ret = AddToEdgeList( &CurrEdges, eCPlusC, INC_ADD_EDGE ) ) { - goto exit_function; - } - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - /*pe->forbidden |= forbidden_edge_mask;*/ - pe->flow -= delta; /* add (+) to -NHm */ - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { - /* Removed (+)charge from -NHn => nDeltaCharge == -1 */ - /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 10 */ - bContinue = 0; - pc2i->c2at[i].nValue = 1; /* mark as used */ - pc2i->c2at[iNH2].nValue = 1; /* mark as used */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - - /*pe->forbidden &= forbidden_edge_mask_inv;*/ - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - CurrEdges.num_edges = 0; /* clear current edge list */ - break; - } - } /* iNH2: pc2i->c2at[iNH2] cycle */ - } - } /* j2: iatC neighbors cycle */ - } - } /* j1: iat neighbors cycle */ - } - } /* i: pc2i->c2at[i] cycle */ - if ( cur_success ) { - /* - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - pc2i->c2at[i].nValue = 0; - } - */ - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( /*pc2i->len_c2at >= 1 &&*/ pc2i->nNumTgInChI == 1 && /* ADP in InChI */ - (pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1) ) { - /*--------------------------------------------------------------*/ - /* case 11: restored: NH(+)=AB-N< OH- orig. NH-AB=N(+)< OH- */ - /* FixH: 0 0 0 1 0 1 */ - /* MobH: 1 0 1 0 0 0 */ - /* non-taut. taut taut */ - /* ADP: one t-group or more endpoints */ - /* NH(+)= => N, O, S, Se; -N< => N */ - /* Solution: move (+) from NH(+) to -N< */ - /*--------------------------------------------------------------*/ - int num_SB_Neutr = 0, num_DB_Charged = 0, iat; - short iat_SB_Neutr[MAX_DIFF_FIXH], iat_DB_Charged[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - cur_success = 0; - /* search for NH(+)= */ - /* search for -N< */ - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( /* in restored atom: charge=0, has no H, has no double bond, N only */ - num_DB_Charged < MAX_DIFF_FIXH && - at2[iat].charge == 1 && at2[iat].num_H && - at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - (pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 || - pVA[iat].cNumValenceElectrons == 6 ) && - /* in orig.InChI: an endpoint, has fixed-H */ - /*pStruct->endpoint[i] &&*/ - (pStruct->fixed_H && pStruct->fixed_H[i]) && - /*!(nMobHInChI && nMobHInChI[i] ) &&*/ - /* has (+) edge */ - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && 0 == pBNS->edge[e].forbidden ) { - - iat_DB_Charged[num_DB_Charged ++] = iat; - /* - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - */ - } else - if ( /* in restored atom: charge=0, has no H, has no double bond, N only */ - num_SB_Neutr < MAX_DIFF_FIXH && - at2[iat].charge == 0 && !at2[iat].num_H && - at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - (pVA[iat].cNumValenceElectrons == 5 && - pVA[iat].cPeriodicRowNumber == 1 ) && - /* in orig.InChI: an endpoint, has fixed-H */ - /*pStruct->endpoint[i] &&*/ - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i] ) && - /* has (+) edge */ - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && 0 == pBNS->edge[e].forbidden ) { - - iat_SB_Neutr[num_SB_Neutr ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( num_try = inchi_min( num_SB_Neutr, num_DB_Charged ) ) { - /* detected; attempt to fix */ - BNS_VERTEX *pv1n, *pv2n; - BNS_EDGE *pe1n, *pe2n; - Vertex v1n, v2n; - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_DB_Charged && cur_success < num_try; i ++ ) { - iat = iat_DB_Charged[i]; - pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; - if ( pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - for ( j = pv1->num_adj_edges-1; 0 <= j; j -- ) { - pe1n = pBNS->edge + pv1->iedge[j]; - if ( pe1n->flow && !pe1n->forbidden ) { - pv1n = pBNS->vert + (v1n = pe1n->neighbor12 ^ v1); - break; - } - } - if ( j < 0 ) - continue; /* not found */ - - for ( j = pv2->num_adj_edges-2; 0 <= j; j -- ) { - pe2n = pBNS->edge + pv2->iedge[j]; - if ( pe2n->flow && !pe2n->forbidden ) { - pv2n = pBNS->vert + (v2n = pe2n->neighbor12 ^ v2); - break; - } - } - if ( j < 0 ) - continue; /* not found */ - - pe->flow += delta; - pe1n->flow -= delta; - pe2n->flow -= delta; - pv1n->st_edge.flow -= delta; - pv2n->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1n && vPathStart == v2n || - vPathEnd == v2n && vPathStart == v1n) && - (nDeltaCharge == 0 || nDeltaCharge == 1) ) { - /* before setting flows the structure could be: - [NH+ neigh, v1n]=e1n=[NH+,v1]-pe-[+,v2]=e2n=[another at or its chargeStruct] - or - - [NH+ or ChStr, v1n]=pe1n=[NH+ or ChStr, v1]-pe-[+,v2]=pe2n=[at2 or ChStr, v2n] - ^ ^ ^ - NH+(+)edge | N (+) edge: only - | these are not forbidden - | - hetero (+) vertex - - After setting flows (* mark radicals, =pe= is forbidden): - - *[NH+ or ChStr, v1n]-pe1n-[NH+ or ChStr, v1]=pe=[+,v2]-pe2n-[at2 or ChStr, v2n]* - ^ ^ ^ - NH+(+)edge | N (+) edge: only - | these are not forbidden - | - hetero (+) vertex - - Flow in - pe1n and pe2n will or will not change, depending on the structure. - - Consider what happens if pe2n changes. It may only increment. - If pe2n flow increments then another (+)edge flow dectrements. If - [at2 or ChStr, v2n] is at2 then at2 charge would change from (+) to 0, - and another N charge would change from 0 to (+), giving tot. change of - number of charges (-1)+(+1)=0. However, if [at2 or ChStr, v2n] is - ChargeStruct then at2 will not be on the alt path and only the creation - of another (+) will be detected. - */ - /* Removed charge from O(+) => nDeltaCharge == -1 */ - /* Flow change on pe (+)charge edge (atom NH2) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 11 */ - } - } else { - pe->flow -= delta; - pe1n->flow += delta; - pe2n->flow += delta; - pv1n->st_edge.flow += delta; - pv2n->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pc2i->len_c2at >= 1 && pc2i->nNumTgInChI == 1 && - pc2i->nNumRemHInChI >= -1 && /* 2006-03-03 */ - (pc2i->nNumEndpInChI > pc2i->nNumEndpRevrs || pc2i->nNumTgRevrs > 1) /* ADP in InChI */ ) { - /*--------------------------------------------------------------*/ - /* case 12: restored: O=AB-N< original: (-)O-AB=N(+)< */ - /* FixH: 0 0 0 0 */ - /* MobH: 0 0 0 0 */ - /* non-taut taut */ - /* O = O, S, Se, N; N = N; */ - /* restored atom O is not tautomeric; original atom O is taut. */ - /* original struct has 1 t-group; restored has less endpoints */ - /* and/or possibly >1 t-groups */ - /* Solution: separate charges between O= and -N< */ - /* allow moving charge to N(V) to make it N(IV)(+) */ - /*--------------------------------------------------------------*/ - int bOnly_N_V = 1; - cur_success = 0; - while( 1 ) { - int num_SB_N_Neutr = 0, num_DB_O = 0, iat, num_N_V=0, bN_V; - short iat_SB_N_Neutr[MAX_DIFF_FIXH], iat_DB_O[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - cur_success = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( /* orig. InChI info: -O(-) */ - num_DB_O < MAX_DIFF_FIXH && - (pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ || - pc2i->c2at[i].nValElectr == 5 && - pc2i->c2at[i].nPeriodNum == 1 /* N */ ) && - pc2i->c2at[i].endptInChI && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI == 0 && - /* reversed structure info: */ - !pc2i->c2at[i].endptRevrs && - pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && - pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && - ((pc2i->c2at[i].nValElectr == 6)? - (at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2): - (pc2i->c2at[i].nValElectr == 5)? - (at2[iat].valence == 2 && at2[iat].chem_bonds_valence == 3): - 0)) { - - iat_DB_O[num_DB_O ++] = iat; - /* - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - */ - } - } - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - bN_V = 0; - if ( /* in restored atom N: charge=0, no H, has no double bond, not an endpoint */ - num_SB_N_Neutr < MAX_DIFF_FIXH && - at2[iat].charge == 0 && !at2[iat].num_H && - (at2[iat].valence == at2[iat].chem_bonds_valence || - (bN_V = at2[iat].valence+2 == at2[iat].chem_bonds_valence)) && - !pVA[iat].cMetal && - pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && - !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint) && - /* in orig.InChI: not an endpoint, has no H */ - !pStruct->endpoint[i] && - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i]) && - /* has (+) edge */ - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - - if ( bOnly_N_V && bN_V && - NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e )) && - !pBNS->edge[j].forbidden && !pBNS->edge[j].flow ) { - if ( !num_N_V ) { - /* switch to N(V) only mode */ - CurrEdges.num_edges = 0; - num_SB_N_Neutr = 0; - } - iat_SB_N_Neutr[num_SB_N_Neutr ++] = iat; - num_N_V ++; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &CurrEdges, j, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else - if ( !num_N_V ) { - iat_SB_N_Neutr[num_SB_N_Neutr ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - /* in addition, permit N(V)=>N(IV)(+) change by allowing charge flower edge change flow */ - if ( bN_V && NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e )) && - !pBNS->edge[j].forbidden && !pBNS->edge[j].flow ) { - if ( ret = AddToEdgeList( &CurrEdges, j, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - } - } - if ( num_try = inchi_min( num_SB_N_Neutr, num_DB_O ) ) { - /* detected; attempt to fix */ - BNS_EDGE *pe_CMinus; - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_DB_O && cur_success < num_try; i ++ ) { - iat = iat_DB_O[i]; - pe_CMinus = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; - pe_CMinus->forbidden &= forbidden_edge_mask_inv; - - pe = pBNS->edge + pBNS->vert[iat].iedge[0]; /* double bond O=...*/ - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; /* change bond O=X to O(rad)-X(rad) */ - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 2 ) { - /* Added (-) charge to =O and (+) charge to N => nDeltaCharge == 2 */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 12 */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - pe->forbidden &= forbidden_edge_mask_inv; /* allow changes to O=X bond */ - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - break; - } else - if ( bOnly_N_V ) { - bOnly_N_V = 0; - } else { - break; - } - } - } - - if ( pc2i->nNumTgDiffMinus /*|| pc2i->nNumTgDiffH */ /* no ADP in InChI needed */ ) { - /*--------------------------------------------------------------*/ - /* | | */ - /* case 13: restored: O=AB=N= original: (-)O-AB-N(+)= */ - /* FixH: 0 0 0 0 */ - /* MobH: 0 0 0 0 */ - /* non-taut taut non-taut */ - /* O = O, S, Se, N; N = N, P, ... */ - /* t-group in original has same num. endpoints */ - /* same num_H and less (-) than in the restored structure */ - /* original atom O is tautomeric, N is not taut in both */ - /* original struct has 1 t-group; restored has less endpoints */ - /* and/or possibly >1 t-groups */ - /* Solution: separate charges between O= and -N< */ - /* allow moving charge to N(V) to make it N(IV)(+) */ - /*--------------------------------------------------------------*/ - int itg; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - - S_CHAR *num_Fixed_H_Revrs = pStruct->pOneINChI[0]->nNum_H_fixed? pStruct->pOneINChI[0]->nNum_H_fixed : NULL; - S_CHAR *pnMobHRevrs = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNum_H)? - pStruct->pOneINChI[1]->nNum_H : - (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)? - pStruct->pOneINChI[0]->nNum_H : NULL; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - cur_success = 0; - /* find whether this may help */ - for ( itg = 0; itg < pStruct->ti.num_t_groups && itg < pStruct->One_ti.num_t_groups; itg ++ ) { - if ( pStruct->ti.t_group[itg].nNumEndpoints == pStruct->One_ti.t_group[itg].nNumEndpoints && - pStruct->ti.t_group[itg].num[0] - pStruct->ti.t_group[itg].num[1] == - pStruct->One_ti.t_group[itg].num[0] - pStruct->One_ti.t_group[itg].num[1] && - pStruct->ti.t_group[itg].num[1] > pStruct->One_ti.t_group[itg].num[1]) { - /* restored InChI t-group has more (-) and same number of H */ - - int num_SB_N_Neutr = 0, num_DB_O = 0, iat; - short iat_SB_N_Neutr[MAX_DIFF_FIXH], iat_DB_O[MAX_DIFF_FIXH]; - cur_success = 0; - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( /* orig. InChI info: -O(-) */ - num_DB_O < MAX_DIFF_FIXH && - (pVA[i].cNumValenceElectrons == 6 /* O, S, Se, Te */ ) && - pStruct->endpoint[i] == itg+1 && - (e=pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i]) && - /* reversed structure info: */ - /*!pc2i->c2at[i].endptRevrs &&*/ - !(num_Fixed_H_Revrs && num_Fixed_H_Revrs[iat]) && - !(pnMobHRevrs && pnMobHRevrs[iat]) && - at2[iat].charge == 0 && at2[iat].num_H == 0 && - at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 ) { - - iat_DB_O[num_DB_O ++] = iat; - /* - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - */ - } else - if ( /* in restored atom N: charge=0, no H, has no double bond, not an endpoint */ - num_SB_N_Neutr < MAX_DIFF_FIXH && - at2[iat].charge == 0 && !at2[iat].num_H && - /*at2[iat].valence == at2[iat].chem_bonds_valence ||*/ - (at2[iat].valence==4 && at2[iat].chem_bonds_valence==5) && - !pVA[iat].cMetal && - pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber >= 1 && - !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint) && - /* in orig.InChI: not an endpoint, has no H */ - !pStruct->endpoint[i] && - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i]) && - /* has (+) edge */ - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - - iat_SB_N_Neutr[num_SB_N_Neutr ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( num_try = inchi_min( num_SB_N_Neutr, num_DB_O ) ) { - /* detected; attempt to fix */ - BNS_EDGE *pe_CMinus; - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_DB_O && cur_success < num_try; i ++ ) { - iat = iat_DB_O[i]; - pe_CMinus = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; - pe_CMinus->forbidden &= forbidden_edge_mask_inv; - - pe = pBNS->edge + pBNS->vert[iat].iedge[0]; /* double bond O=...*/ - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; /* change bond O=X to O(rad)-X(rad) */ - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 2 ) { - /* Added (-) charge to =O and (+) charge to N => nDeltaCharge == 2 */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 13 */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - pe->forbidden &= forbidden_edge_mask_inv; /* allow changes to O=X bond */ - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - break; - }/* else - if ( bOnly_N_V ) { - bOnly_N_V = 0; - } - */ - break; - } - } - } - - if ( (pc2i->nNumTgInChI <= 1 && - pc2i->nNumRemHInChI > pc2i->nNumRemHRevrs || pc2i->len_c2at) && - bHas_N_V( at2, pStruct->num_atoms) ) { - /*-----------------------------------------------------------------*/ - /* | | */ - /* case 14: restored:-N=AB=N=CD-XH original: (-)N-AB-N(+)=CD-XH */ - /* FixH: 0 0 0/1 0 1 */ - /* MobH: 0 0 1/0 0 0 */ - /* non-taut n/t any non any */ - /* taut */ - /* X = O, S, Se, N; N = N */ - /* t-group in original may have more (-) than in restored */ - /* same num_H and less (-) than in the restored structure */ - /* atom N(V)/N(IV)(+) is not taut in both */ - /* The following transformation should be possible: */ - /* | | */ - /* N=AB=N=CD-XH -> (-)N-AB-N-CD=XH(+) */ - /* This allows ADP to remove H(+) from -XH */ - /* As the result, the original structure has 0 or 1 t-group */ - /* Solution: separate charges between -N(III)= and N(V) */ - /*-----------------------------------------------------------------*/ - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - int num_N_V = 0, iat, i1, i2, i3, e1Flower, e1Plus, e2Plus, e2Minus, e3Plus; - int max_success = pc2i->nNumRemHInChI - pc2i->nNumRemHRevrs; - short iat_N_V_Array[MAX_DIFF_FIXH]; - EDGE_LIST iat_X_List, iat_N_III_List; - AllocEdgeList( &iat_X_List, EDGE_LIST_CLEAR ); - AllocEdgeList( &iat_N_III_List, EDGE_LIST_CLEAR ); - cur_success = 0; - ret = 0; - for ( i = 0; i < pStruct->num_atoms; i ++ ) { - iat = nCanon2AtnoRevrs[i]; - /* search for N(V), 3 bonds */ - if ( /* restored structure */ - num_N_V < MAX_DIFF_FIXH && - at2[iat].chem_bonds_valence == 5 && at2[iat].valence == 3 && - !at2[iat].charge && !at2[iat].radical && - pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && - !( at_Mobile_H_Revrs && at_Mobile_H_Revrs[i].endpoint ) && - !at2[iat].num_H && - (e = pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pBNS->edge[e].flow /* no charge */ && - NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e )) && !pBNS->edge[j].forbidden && - !pBNS->edge[j].flow /* neutral, valence=5 */ && - /* orig. InChI */ - !pStruct->endpoint[i] && - !(nMobHInChI && nMobHInChI[i]) && !pStruct->fixed_H[i] ) { - iat_N_V_Array[num_N_V ++] = iat; - } else - /* search for -N= */ - if ( /* restored structure */ - at2[iat].chem_bonds_valence == 3 && at2[iat].valence == 2 && - !at2[iat].charge && !at2[iat].radical && - pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && - !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[i].endpoint ) && - !at2[iat].num_H && - (e = pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - !pBNS->edge[e].flow /* no charge */ && - /* orig. InChI */ - /*!pStruct->endpoint[i] &&*/ - !(nMobHInChI && nMobHInChI[i]) && !pStruct->fixed_H[i] ) { - - if ( ret = AddToEdgeList( &iat_N_III_List, iat, 32 ) ) { - goto exit_case_14; - } - } else - /* search for -OH -NH-, -NH2 */ - if ( /* restored structure */ - at2[iat].chem_bonds_valence == at2[iat].valence && - !at2[iat].charge && !at2[iat].radical && - (pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 || - pVA[iat].cNumValenceElectrons == 6 ) && - at2[iat].num_H && - (e = pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pBNS->edge[e].flow /* no charge */ && - /* orig. InChI */ - !(nMobHInChI && nMobHInChI[i]) && pStruct->fixed_H[i] ) { - - if ( ret = AddToEdgeList( &iat_X_List, iat, 32 ) ) { - goto exit_case_14; - } - } - } - if ( !max_success ) { - max_success = inchi_min( num_N_V, iat_N_III_List.num_edges ); - max_success = inchi_min( max_success, iat_X_List.num_edges ); - } - if ( num_N_V && iat_N_III_List.num_edges && iat_X_List.num_edges ) { - for ( i1 = 0; i1 < num_N_V && cur_success < max_success; i1 ++ ) { - int iat_N_V = iat_N_V_Array[i1]; - if ( NO_VERTEX == iat_N_V || - 0 >= (e1Plus = pVA[iat_N_V].nCPlusGroupEdge-1) || - NO_VERTEX == (e1Flower = GetChargeFlowerUpperEdge( pBNS, pVA, e1Plus )) || - 1 != pBNS->edge[e1Plus].flow || - 0 != pBNS->edge[e1Flower].flow ) { - continue; - } - for ( i2 = iat_N_III_List.num_edges-1; 0 <= i2 && cur_success < max_success; i2 -- ) { - int iat_N_III = iat_N_III_List.pnEdges[i2]; - if ( NO_VERTEX == iat_N_III || - 0 >= (e2Minus = pVA[iat_N_III].nCMinusGroupEdge-1) || - 0 >= (e2Plus = pVA[iat_N_III].nCPlusGroupEdge-1) || - 0 != pBNS->edge[e2Minus].flow || - 1 != pBNS->edge[e2Plus].flow ) { - /* do not consider this atom anymore */ - iat_N_III_List.pnEdges[i2] = NO_VERTEX; - continue; - } - for ( i3 = iat_X_List.num_edges-1; 0 <= i3 && cur_success < max_success; i3 -- ) { - int iat_X = iat_X_List.pnEdges[i3]; - BNS_VERTEX *pv1n, *pv2n; - BNS_EDGE *pe1n, *pe2n, *pe1Plus, *pe2Minus, *pe3Plus; - Vertex v1n, v2n; - ret = 0; - if ( NO_VERTEX == iat_X || - 0 >= (e3Plus = pVA[iat_X].nCPlusGroupEdge-1) || - 1 != pBNS->edge[e3Plus].flow ) { - /* do not consider this atom anymore */ - iat_X_List.pnEdges[i3] = NO_VERTEX; - continue; - } - /* all is ready to check whether the following applies: - forbid changes of all charges and N,P,... flowers - allow to change edges: e2Minus, e3Plus - Increment flow in e1Flower - The result should be: increase in number of charges by 2 - */ - pe1Plus = pBNS->edge + e1Plus; /* N(V) positive charge edge */ - pe2Minus = pBNS->edge + e2Minus; /* =N- negative charge edge */ - pe3Plus = pBNS->edge + e3Plus; /* -XH positive charge edge */ - pe = pBNS->edge + e1Flower; /* N(V) flower edge */ - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - for ( j = pv1->num_adj_edges-1; 0 <= j; j -- ) { - pe1n = pBNS->edge + pv1->iedge[j]; - if ( pe1n->flow && !pe1n->forbidden && pe1n != pe1Plus ) { - pv1n = pBNS->vert + (v1n = pe1n->neighbor12 ^ v1); - break; - } - } - if ( j < 0 ) - continue; /* not found -- should not happen */ - for ( j = pv2->num_adj_edges-1; 0 <= j; j -- ) { /* was -2; changed 2006-2-28 12:35pm*/ - pe2n = pBNS->edge + pv2->iedge[j]; - if ( pe2n->flow && !pe2n->forbidden && pe2n != pe1Plus ) { - pv2n = pBNS->vert + (v2n = pe2n->neighbor12 ^ v2); - break; - } - } - if ( j < 0 ) - continue; /* not found -- should not happen */ - delta = 1; - pe->flow += delta; - pe1n->flow -= delta; - pe2n->flow -= delta; - pv1n->st_edge.flow -= delta; - pv2n->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - SetForbiddenEdgeMask( pBNS, &OtherNFlowerEdges, forbidden_edge_mask ); - - /* allow two charges to change */ - pe2Minus->forbidden &= forbidden_edge_mask_inv; - pe3Plus->forbidden &= forbidden_edge_mask_inv; - /* test #1 */ - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - INCHI_HEAPCHK - if ( ret < 0 ) { - goto exit_case_14; - } else - if ( ret == 1 && (vPathEnd == v1n && vPathStart == v2n || - vPathEnd == v2n && vPathStart == v1n) && - nDeltaCharge == 2 ) { - ; /* success */ - } else { - ret = 0; - } - /* restore BNS */ - pe2Minus->forbidden |= forbidden_edge_mask; - pe3Plus->forbidden |= forbidden_edge_mask; - pe->flow -= delta; - pe1n->flow += delta; - pe2n->flow += delta; - pv1n->st_edge.flow += delta; - pv2n->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - if ( ret == 1 ) { - /* test #2: check if charge separation is possible */ - pe->flow += delta; - pe1n->flow -= delta; - pe2n->flow -= delta; - pv1n->st_edge.flow -= delta; - pv2n->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - /* allow two charges (N(V) and N(III)) to change */ - pe2Minus->forbidden &= forbidden_edge_mask_inv; - pe1Plus->forbidden &= forbidden_edge_mask_inv; - /* test #2 */ - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret == 1 && (vPathEnd == v1n && vPathStart == v2n || - vPathEnd == v2n && vPathStart == v1n) && - nDeltaCharge == 2 ) { - /* success; actually change charges */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 14 */ - } - } - if ( ret <= 0 ) { - /* failed: restore BNS flow */ - pe->flow -= delta; - pe1n->flow += delta; - pe2n->flow += delta; - pv1n->st_edge.flow += delta; - pv2n->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &OtherNFlowerEdges, forbidden_edge_mask ); - if ( ret > 0 ) { - /* do not repeat for the same atoms */ - iat_N_V_Array[i1] = NO_VERTEX; - iat_N_III_List.pnEdges[i2] = NO_VERTEX; - iat_X_List.pnEdges[i3] = NO_VERTEX; - } - if ( ret < 0 ) { - goto exit_case_14; - } - if ( ret > 0 ) { - break; - } - } /* i3 cycle */ - if ( ret > 0 ) { - break; - } - } /* i2 cycle */ - } - } -exit_case_14: - AllocEdgeList( &iat_X_List, EDGE_LIST_FREE ); - AllocEdgeList( &iat_N_III_List, EDGE_LIST_FREE ); - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &OtherNFlowerEdges, forbidden_edge_mask ); - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( ret < 0 ) { - goto exit_function; - } - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - - if ( pc2i->nNumTgMRevrs > pc2i->nNumTgMInChI || - pc2i->nNumRemHRevrs < pc2i->nNumRemHInChI || - pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || - pc2i->nNumTgInChI <= 1 && pc2i->nNumTgRevrs > pc2i->nNumTgInChI ) { - /*--------------------------------------------------------------*/ - /* case 15: restored: -(+)O=AB-N< orig: -O-AB=N(+)< */ - /* (a) restored t-groups have more (-) than in original InChI */ - /* (b) Mobile-H charge: restored > original InChI *and* */ - /* removed H: restored < original InChI */ - /* (c) restored t-groups have less endpnoits than in orig InChI */ - /* O = O, S, Se, Te; N = N */ - /* Solution: move (+) from -O(+)= to -N< */ - /*--------------------------------------------------------------*/ - int num_SB_Neutr = 0, num_DB_Charged = 0, iat; - short iat_SB_Neutr[MAX_DIFF_FIXH], iat_DB_Charged[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - cur_success = 0; - /* search for -O(+)= */ - /* search for -N< */ - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( /* -O(+)= in restored atom: charge=1, has no H, a double bond */ - num_DB_Charged < MAX_DIFF_FIXH && - at2[iat].charge == 1 && !at2[iat].num_H && - at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - (pVA[iat].cNumValenceElectrons == 6 ) && - /* in orig.InChI: an endpoint, has fixed-H */ - /*pStruct->endpoint[i] &&*/ - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i] ) && - /* has (+) edge */ - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && 0 == pBNS->edge[e].forbidden ) { - - iat_DB_Charged[num_DB_Charged ++] = iat; - /* - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - */ - } else - if ( /* -N< in restored atom: charge=0, has no H, has no double bond, N only */ - num_SB_Neutr < MAX_DIFF_FIXH && - at2[iat].charge == 0 && !at2[iat].num_H && - at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - (pVA[iat].cNumValenceElectrons == 5 && - pVA[iat].cPeriodicRowNumber == 1 ) && - /* in orig.InChI: an endpoint, has fixed-H */ - /*pStruct->endpoint[i] &&*/ - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i] ) && - /* has (+) edge */ - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && 0 == pBNS->edge[e].forbidden ) { - - iat_SB_Neutr[num_SB_Neutr ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( num_try = inchi_min( num_SB_Neutr, num_DB_Charged ) ) { - /* detected; attempt to fix */ - BNS_VERTEX *pv1n, *pv2n; - BNS_EDGE *pe1n, *pe2n; - Vertex v1n, v2n; - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_DB_Charged && cur_success < num_try; i ++ ) { - iat = iat_DB_Charged[i]; - pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; - if ( pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - for ( j = pv1->num_adj_edges-1; 0 <= j; j -- ) { - pe1n = pBNS->edge + pv1->iedge[j]; - if ( pe1n->flow && !pe1n->forbidden ) { - pv1n = pBNS->vert + (v1n = pe1n->neighbor12 ^ v1); - break; - } - } - if ( j < 0 ) - continue; /* not found */ - - for ( j = pv2->num_adj_edges-1; 0 <= j; j -- ) { /* was -2; changed 2006-2-28 12:35pm*/ - pe2n = pBNS->edge + pv2->iedge[j]; - if ( pe2n->flow && !pe2n->forbidden ) { - pv2n = pBNS->vert + (v2n = pe2n->neighbor12 ^ v2); - break; - } - } - if ( j < 0 ) - continue; /* not found */ - - pe->flow += delta; - pe1n->flow -= delta; - pe2n->flow -= delta; - pv1n->st_edge.flow -= delta; - pv2n->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1n && vPathStart == v2n || - vPathEnd == v2n && vPathStart == v1n) && - (nDeltaCharge == 0 || nDeltaCharge == 1) ) { - /* Moved charge from O(+) to -N< => nDeltaCharge == 1 or 0 if pe2n = -N< charge edge */ - /* Flow change on pe (+)charge edge (atom NH2) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 15 */ - } - } else { - pe->flow -= delta; - pe1n->flow += delta; - pe2n->flow += delta; - pv1n->st_edge.flow += delta; - pv2n->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pc2i->nNumTgDiffMinus ) { - /*----------------------------------------------------------------*/ - /* case 16: restored: O=X-NH(-) orig.: O(-)-X=NH */ - /* t-group: (H,-) (2H) */ - /* O(-) = S, Se, Te; N = N; */ - /* Solution: move (-) from O(-) to -NH(-) */ - /*----------------------------------------------------------------*/ - int num_SB_N_Minus = 0, num_DB_O_Neutr = 0, iat, itg; - short iat_SB_N_Minus[MAX_DIFF_FIXH], iat_DB_O_Neutr[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - cur_success = 0; - for ( itg = 0; itg < pStruct->ti.num_t_groups && itg < pStruct->One_ti.num_t_groups; itg ++ ) { - if ( pStruct->ti.t_group[itg].nNumEndpoints != pStruct->One_ti.t_group[itg].nNumEndpoints || - pStruct->ti.t_group[itg].num[1] >= pStruct->One_ti.t_group[itg].num[1] ) { - continue; - } - CurrEdges.num_edges = num_SB_N_Minus = num_DB_O_Neutr = 0; - cur_success = 0; - for ( j = 0, k = pStruct->One_ti.t_group[itg].nFirstEndpointAtNoPos; - j < pStruct->One_ti.t_group[itg].nNumEndpoints; j ++ ) { - i = pStruct->One_ti.nEndpointAtomNumber[k+j]; /* canonical number in restored struct. */ - iat = nCanon2AtnoRevrs[i]; - if ( /* in restored atom: charge=0, has no H, has double bond, O, S, Se, Te */ - num_DB_O_Neutr < MAX_DIFF_FIXH && - at2[iat].charge == 0 && !at2[iat].num_H && - at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - pVA[iat].cNumValenceElectrons == 6 && - /* in orig.InChI: an endpoint, may have fixed-H */ - pStruct->endpoint[i] && - /*!(pStruct->fixed_H && pStruct->fixed_H[i]) &&*/ - !(nMobHInChI && nMobHInChI[i] ) && - /* has (-) edge */ - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - - iat_DB_O_Neutr[num_DB_O_Neutr ++] = iat; - - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - - } else - if ( /* in restored atom: charge=-1, has H, has double bond, N */ - num_SB_N_Minus < MAX_DIFF_FIXH && - at2[iat].charge == -1 && at2[iat].num_H && - at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && - /* in orig.InChI: an endpoint, has no fixed-H */ - pStruct->endpoint[i] && - (pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i] ) && - /* has (-) edge */ - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && - 0 == pBNS->edge[e].forbidden ) { - - iat_SB_N_Minus[num_SB_N_Minus ++] = iat; - /* - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - */ - } - } - if ( num_try = inchi_min( num_SB_N_Minus, num_DB_O_Neutr ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - /* allow stereobonds in rings change */ - /* - if ( forbidden_stereo_edge_mask ) - RemoveForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask ); - */ - delta = 1; - for ( i = 0; i < num_SB_N_Minus && cur_success < num_try; i ++ ) { - iat = iat_SB_N_Minus[i]; - pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - /*pe->forbidden |= forbidden_edge_mask;*/ - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Moved (-) charge to =O => nDeltaCharge == 1 */ - /* Flow change on pe (-)charge edge (atom -NH(-)) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 16 */ - } - } else { - pe->forbidden &= forbidden_edge_mask_inv; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - /* - if ( forbidden_stereo_edge_mask ) - SetForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask ); - */ - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - } - - if ( pc2i->nNumRemHInChI < pc2i->nNumRemHRevrs ) { - /*--------------------------------------------------------------*/ - /* case 17: restored: OH(+)=AB-O- orig. HO-AB=O(+)- */ - /* number of removed H: n+m n */ - /* OH(+) = N, O, S, Se; -O- = P,As,O,S,Se,Te,F,Cl,Br,I */ - /* Solution: move (+) from OH(+) to -O- */ - /*--------------------------------------------------------------*/ - int num_SB_Neutr = 0, num_DB_Charged = 0, iat; - short iat_SB_Neutr[MAX_DIFF_FIXH], iat_DB_Charged[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - cur_success = 0; - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( /* in restored atom: charge=+1, has H, has double bond, N, O, S, Se, Te */ - num_DB_Charged < MAX_DIFF_FIXH && - at2[iat].charge == 1 && at2[iat].num_H && - at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - (pVA[iat].cNumValenceElectrons == 6 || - pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1) && - /* has (+) edge */ - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - - iat_DB_Charged[num_DB_Charged ++] = iat; - /* - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - */ - } else - if ( /* in restored atom: charge=0, has no H, has no double bond, N, P, O, S, Se, Te */ - num_SB_Neutr < MAX_DIFF_FIXH && - at2[iat].charge == 0 && !at2[iat].num_H && - at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - (pVA[iat].cNumValenceElectrons == 6 || pVA[iat].cNumValenceElectrons == 7 || - pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber > 1 ) && - /* in orig.InChI: not an endpoint */ - !pStruct->endpoint[i] && - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - !(nMobHInChI && nMobHInChI[i] ) && - /* has (+) edge */ - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && - 0 == pBNS->edge[e].forbidden ) { - - iat_SB_Neutr[num_SB_Neutr ++] = iat; - - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( num_try = inchi_min( num_SB_Neutr, num_DB_Charged ) ) { - BNS_VERTEX *pv1n, *pv2n; - BNS_EDGE *pe1n, *pe2n; - Vertex v1n, v2n; - - num_try = inchi_min( num_try, pc2i->nNumRemHRevrs-pc2i->nNumRemHInChI); - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_DB_Charged && cur_success < num_try; i ++ ) { - iat = iat_DB_Charged[i]; - pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; - if ( pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - for ( j = pv1->num_adj_edges-1; 0 <= j; j -- ) { - pe1n = pBNS->edge + pv1->iedge[j]; - if ( pe1n->flow && !pe1n->forbidden ) { - pv1n = pBNS->vert + (v1n = pe1n->neighbor12 ^ v1); - break; - } - } - if ( j < 0 ) - continue; /* not found */ - - for ( j = pv2->num_adj_edges-1; 0 <= j; j -- ) { /* was -2; changed 2006-2-28 12:35pm*/ - pe2n = pBNS->edge + pv2->iedge[j]; - if ( pe2n->flow && !pe2n->forbidden ) { - pv2n = pBNS->vert + (v2n = pe2n->neighbor12 ^ v2); - break; - } - } - if ( j < 0 ) - continue; /* not found */ - - pe->flow += delta; - pe1n->flow -= delta; - pe2n->flow -= delta; - pv1n->st_edge.flow -= delta; - pv2n->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1n && vPathStart == v2n || - vPathEnd == v2n && vPathStart == v1n) && - (nDeltaCharge == 0 || nDeltaCharge == 1) ) { - /* Moved charge from OH(+) to -O- => nDeltaCharge == 1 or 0 if pe2n = -O- charge edge */ - /* Flow change on pe (+)charge edge (atom OH(+)) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 17 */ - } - } else { - pe->flow -= delta; - pe1n->flow += delta; - pe2n->flow += delta; - pv1n->st_edge.flow += delta; - pv2n->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( (pc2i->nNumTgInChI && pStruct->endpoint && - pc2i->nNumTgMInChI > pc2i->nNumTgMRevrs && pc2i->nNumEndpInChI > pc2i->nNumEndpRevrs ) ) { - /*-----------------------------------------------------------------*/ - /* */ - /* case 18: restored:-N=AB-X -(-)N-AB-X(+) */ - /* FixH: 0 0 0 0 */ - /* MobH: 0 0 0 0 */ - /* non non taut non */ - /* taut taut taut */ - /* X = any heteroatom N=N */ - /* t-group in original has (Hn,-m) in the restored: (Hn,-m+1) */ - /* same num_H and more (-) than in the restored structure */ - /* atom X is not taut in both */ - /* Solution: separate charges between -N(III)= and X */ - /*-----------------------------------------------------------------*/ - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - int iat, e1, itg, max_success; - CurrEdges.num_edges = 0; - cur_success = 0; - ret = 0; - /* search for -N= */ - for ( itg = 0; itg < pStruct->ti.num_t_groups && itg < pStruct->One_ti.num_t_groups; itg ++ ) { - if ( pStruct->ti.t_group[itg].nNumEndpoints <= pStruct->One_ti.t_group[itg].nNumEndpoints || - pStruct->ti.t_group[itg].num[1] <= pStruct->One_ti.t_group[itg].num[1] ) { - continue; - } - CurrEdges.num_edges = 0; - cur_success = 0; - for ( j = 0, k = pStruct->ti.t_group[itg].nFirstEndpointAtNoPos; - j < pStruct->ti.t_group[itg].nNumEndpoints; j ++ ) { - i = pStruct->ti.nEndpointAtomNumber[k+j]; /* canonical number in restored struct. */ - iat = nCanon2AtnoRevrs[i]; - if ( !pStruct->endpoint[i] || !at_Mobile_H_Revrs || at_Mobile_H_Revrs[iat].endpoint || - pVA[i].cNumValenceElectrons != 5 || pVA[i].cPeriodicRowNumber != 1 || - 2 != at2[iat].valence || at2[iat].num_H || at2[iat].radical || - 0 <= (e1=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e1].flow || - 0 > (e=pVA[iat].nCMinusGroupEdge-1) || pBNS->edge[e].forbidden || pBNS->edge[e].flow ) { - continue; - } - /* found -N= */ - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( !(max_success = CurrEdges.num_edges) ) { - goto exit_case_18; - } - /* search for X */ - for ( i = 0; i < pStruct->num_atoms && cur_success < max_success; i ++ ) { - iat = nCanon2AtnoRevrs[i]; - if ( pStruct->endpoint[i] || !pVA[i].cNumValenceElectrons || pVA[i].cNumValenceElectrons == 4 || - at2[iat].num_H || at2[iat].radical || - 0 <= (e1=pVA[iat].nCMinusGroupEdge-1) && !pBNS->edge[e1].flow || - 0 > (e=pVA[iat].nCPlusGroupEdge-1) || pBNS->edge[e].forbidden || pBNS->edge[e].flow != 1 ) { - continue; - } - /* try to move the charge */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - SetForbiddenEdgeMask( pBNS, &OtherNFlowerEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - - pe = pBNS->edge + e; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - delta = 1; - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Created (-) charge on -N= => nDeltaCharge == 1 */ - /* Flow change on pe (+)charge edge (atom X) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 18 */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } -exit_case_18: - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &OtherNFlowerEdges, forbidden_edge_mask ); - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( ret < 0 ) { - goto exit_function; - } - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - if ( pc2i->len_c2at >= 1 ) { - /*--------------------------------------------------------------*/ - /* case 19 restored: M--OH original: M(-)==OH(+) */ - /* FixH: metal 0 1 */ - /* MobH: 1 0 */ - /* O = O, S, Se, Te; not taut. in InChI */ - /* In restored structure has H; tautomeric or not tautomeric */ - /* Solution: move (+) from -OH to M; charhe on M may vary */ - /*--------------------------------------------------------------*/ - int iat; - EdgeIndex eOHPlus, eMPlus, eMMinus, eOMBond; - BNS_EDGE *peOHPlus, *peMPlus, *peMMinus, *peOMBond; - int iatMetal, ChargeOnMetal, DeltaChargeExpected; - cur_success = 0; - num_zero_ret = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( /* orig. InChI info: =NH2(+), =OH(+) */ - (pc2i->c2at[i].nValElectr == 6 ) /* N, O, S, Se, Te */ && - /*!pc2i->c2at[i].endptInChI &&*/ /* <=== relaxation */ - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && - pc2i->c2at[i].nFixHInChI == 1 && pc2i->c2at[i].nMobHInChI == 0 && - /* reversed structure info: */ - pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 1 && - pc2i->c2at[i].nAtChargeRevrs == 0 && at2[iat].num_H && - at2[iat].valence == 1 && - at2[iat].valence == at2[iat].chem_bonds_valence && - /* metal atom */ - pVA[iatMetal=at2[iat].neighbor[0]].cMetal && - (eMPlus=pVA[iatMetal].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[eMPlus].forbidden && - (eMMinus=pVA[iatMetal].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[eMMinus].forbidden && - !pBNS->edge[eOMBond=pBNS->vert[iat].iedge[0]].forbidden - ) { - - /* -OH charge edges */ - if ( ret = AddToEdgeList( &CurrEdges, iat, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( CurrEdges.num_edges ) { - /* detected; fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - SetForbiddenEdgeMask( pBNS, &NFlowerEdges, forbidden_edge_mask ); - SetForbiddenEdgeMask( pBNS, &AllBondEdges, forbidden_edge_mask ); - for ( i = 0; i < CurrEdges.num_edges; i ++ ) { - /* v1 is -OH, v2 is adjacent to it Metal */ - iat = CurrEdges.pnEdges[i]; - iatMetal = at2[iat].neighbor[0]; - peOHPlus = pBNS->edge + (eOHPlus = pVA[iat].nCPlusGroupEdge-1); - peMPlus = pBNS->edge + (eMPlus = pVA[iatMetal].nCPlusGroupEdge-1); - peMMinus = pBNS->edge + (eMMinus = pVA[iatMetal].nCMinusGroupEdge-1); - peOMBond = pBNS->edge + (eOMBond =pBNS->vert[iat].iedge[0]); - /* remove forbidden edge masks */ - peMPlus->forbidden &= forbidden_edge_mask_inv; - peMMinus->forbidden &= forbidden_edge_mask_inv; - peOMBond->forbidden &= forbidden_edge_mask_inv; - - ChargeOnMetal = (peMPlus->cap - peMPlus->flow) - peMMinus->flow; - if ( 1 == ChargeOnMetal ) { - /* We are going to subtract 1 from the charge on Metal */ - /* Added (+)charge to -OH is not known to RunBnsTestOnce() */ - DeltaChargeExpected = -1; /* charge will become = 0 */ - } else - if ( 0 == ChargeOnMetal ) { - DeltaChargeExpected = 1; /* charge on Metal will be created */ - } else { - DeltaChargeExpected = 0; - } - - delta = 1; - pe = peOHPlus; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->flow -= delta; /* remove (-) from AB-O(-) */ - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == DeltaChargeExpected ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 19 */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - /* set forbidden edge masks back */ - peMPlus->forbidden |= forbidden_edge_mask; - peMMinus->forbidden |= forbidden_edge_mask; - peOMBond->forbidden |= forbidden_edge_mask; - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &NFlowerEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &AllBondEdges, forbidden_edge_mask ); - - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - } - if ( pc2i->len_c2at > 1 && pc2i->nNumTgRevrs && pc2i->nNumTgInChI) { - /*--------------------------------------------------------------*/ - /* case 20: restored: O(-)-AB=N- original: O=AB-N(-)- */ - /* FixH: 0 0 0 -1 */ - /* MobH: 0 0 0 1 */ - /* taut non-taut non-taut taut */ - /* or taut no H */ - /* no H */ - /* O = O, S, Se; N = N, O, S, Se, Te; */ - /* restored atoms are taut/non-taut; original are opposite. */ - /* Solution: move (-) from O(-) to =N- */ - /*--------------------------------------------------------------*/ - int num_SB_O_Minus = 0, num_DB_N = 0, iat; - short iat_SB_O_Minus[MAX_DIFF_FIXH], iat_DB_N[MAX_DIFF_FIXH]; - - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - /* - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - */ - cur_success = 0; - CurrEdges.num_edges = 0; /* clear current edge list */ - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( /* orig. InChI info: =O or -N= */ - num_DB_N < MAX_DIFF_FIXH && - pc2i->c2at[i].endptInChI && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pBNS->edge[e].flow == 0 && - pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI == 0 && - /* if more than 1 t-group are in orig. InChI then do not move (-) to N */ - (pc2i->nNumTgInChI == 1 || pc2i->c2at[i].nValElectr == 6) && - /* reversed structure info: */ - !pc2i->c2at[i].endptRevrs && - pc2i->c2at[i].nFixHRevrs == 0 && /*pc2i->c2at[i].nMobHRevrs == 0 &&*/ - pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && - at2[iat].valence + 1 == at2[iat].chem_bonds_valence ) { - iat_DB_N[num_DB_N ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else - if ( /* orig. InChI info: -O(-) */ - num_SB_O_Minus < MAX_DIFF_FIXH && - !pc2i->c2at[i].endptInChI && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pBNS->edge[e].flow == 1 && - pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI == 0 && - pc2i->c2at[i].nValElectr == 6 && - /* reversed structure info: */ - pc2i->c2at[i].endptRevrs && - pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && - pc2i->c2at[i].nAtChargeRevrs == -1 && !at2[iat].num_H && - at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 1 ) { - iat_SB_O_Minus[num_SB_O_Minus ++] = iat; - } - } - if ( !num_DB_N ) { - /* search among N that are tautomeric in both cases */ - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - if ( !pStruct->endpoint[i] ) { - continue; - } - iat = nCanon2AtnoRevrs[i]; - if ( /* in restored atom O: charge=-1, no H, has no double bond, endpoint */ - num_DB_N < MAX_DIFF_FIXH && - at2[iat].charge == 0 && !at2[iat].num_H && - at2[iat].valence + 1 == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - /* in orig.InChI: an endpoint, has no H */ - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - /*!(nMobHInChI && nMobHInChI[i] ) &&*/ - /* has (-) edge */ - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - !pBNS->edge[e].flow ) { - - iat_DB_N[num_DB_N ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - } - if ( num_try = inchi_min( num_SB_O_Minus, num_DB_N ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_SB_O_Minus && cur_success < num_try; i ++ ) { - iat = iat_SB_O_Minus[i]; - pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Added (-) charge to =N- => nDeltaCharge == 1 */ - /* Flow change on pe (-)charge edge (atom -O(-)) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 20 */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - if ( pc2i->len_c2at && pc2i->nNumTgRevrs && pc2i->nNumTgHInChI && pStruct->endpoint ) { - /*--------------------------------------------------------------*/ - /* O(-) O */ - /* | || */ - /* case 21: restored: R=S=O original: R-S=O */ - /* | | */ - /* O(-) O(-) */ - /* All O are taut R is not taut */ - /* */ - /* In addition, another atom O that should have been tautomeric */ - /* or has H(+) added in Mobile-H layer is not like that */ - /* O = O, S, Se; S=S, Se, Te */ - /* Solution: move (-) from O(-) to =O */ - /* these atoms are tautomeric in restored structure */ - /*--------------------------------------------------------------*/ - int num_SB_O_Minus = 0, num_DB_O = 0, iat, iS; - short iat_SB_O_Minus[MAX_DIFF_FIXH], iat_Central[MAX_DIFF_FIXH], iat_DB_O[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - /* - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - */ - CurrEdges.num_edges = 0; /* clear current edge list */ - cur_success = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( /* orig. InChI info: =O */ - num_DB_O < MAX_DIFF_FIXH && - pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ && - (pc2i->c2at[i].endptInChI || pc2i->c2at[i].nMobHInChI) && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pc2i->c2at[i].nFixHInChI == 0 && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ - /* reversed structure info: */ - !(pc2i->c2at[i].endptRevrs || pc2i->c2at[i].nMobHRevrs) && - pc2i->c2at[i].nFixHRevrs == 0 && - pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && - at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 ) { - iat_DB_O[num_DB_O ++] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - for ( i = 0; num_DB_O && i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - if ( !pStruct->endpoint[i] ) { - continue; - } - iat = nCanon2AtnoRevrs[i]; - if ( /* in restored atom O: charge=-1, no H, has no double bond, endpoint */ - num_SB_O_Minus < MAX_DIFF_FIXH && - at2[iat].charge == -1 && !at2[iat].num_H && - at2[iat].valence == 1 && at2[iat].chem_bonds_valence && !pVA[iat].cMetal && - pVA[iat].cNumValenceElectrons == 6 && - (at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint) && - /* in orig.InChI: an endpoint, has no H */ - !(pStruct->fixed_H && pStruct->fixed_H[i]) && - /*!(nMobHInChI && nMobHInChI[i] ) &&*/ - /* has (-) edge */ - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pBNS->edge[e].flow ) { - int nNumTautSB = 0, nNumTautDB = 0, nNumOtherDB = 0, nNumOtherSB = 0, nNumOthers = 0, nNumNegEndp = 0; - /* traverse neighbors of the centerpoint iS */ - iS = at2[i].neighbor[0]; - for ( j = 0; j < num_SB_O_Minus; j ++ ) { - if ( iat_Central[j] == iS ) - break; - } - if ( j < num_SB_O_Minus ) { - continue; /* have already been there */ - } - for ( j = 0; j < at[iS].valence; j ++ ) { - int bond_type = at2[iS].bond_type[j]; - k = at2[iS].neighbor[j]; - if ( k == i ) { - continue; - } - if ( pStruct->endpoint[k] == pStruct->endpoint[i] ) { - nNumTautSB += ( bond_type == BOND_TYPE_SINGLE ); - nNumTautDB += ( bond_type == BOND_TYPE_DOUBLE ); - } else - if ( bond_type == BOND_TYPE_DOUBLE ) { - nNumOtherDB ++; - } else - if ( bond_type == BOND_TYPE_SINGLE ) { - nNumOtherSB ++; - } else { - nNumOthers ++; - } - if ( at2[k].endpoint == at2[i].endpoint && at2[k].valence == 1 && - at2[k].charge == -1 && pVA[k].cNumValenceElectrons == 6 ) { - nNumNegEndp ++; - } - } - if ( !nNumTautSB ) { - continue; - } - if ( !( nNumOtherDB && nNumTautDB ) ) { - continue; /* ignore */ - } - - iat_SB_O_Minus[num_SB_O_Minus] = iat; - iat_Central[num_SB_O_Minus ++] = iS; - } - } - if ( num_try = inchi_min( num_SB_O_Minus, num_DB_O ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_SB_O_Minus && cur_success < num_try; i ++ ) { - iat = iat_SB_O_Minus[i]; - pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Added (-) charge to =O => nDeltaCharge == 1 */ - /* Flow change on pe (-)charge edge (atom -N(-)-) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 21 */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pc2i->len_c2at && pc2i->nNumTgRevrs && pc2i->nNumEndpInChI < pc2i->nNumEndpRevrs ) { - /*--------------------------------------------------------------*/ - /* O O */ - /* || || */ - /* case 21a:restored: R=S-R' =X original: R-S-R' -X(-) */ - /* | || */ - /* O(-) O(-) */ - /* All O and X are taut O and X are not taut */ - /* it is possible that X is R */ - /* */ - /* O = O, S, Se; S=S, Se, Te; X = N, O, S, Se, Te */ - /* Solution: move (-) from O(-) to =X */ - /* these atoms are tautomeric in restored structure */ - /*--------------------------------------------------------------*/ - int iat, iS; - /* - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - */ - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - /* - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - */ - EDGE_LIST OtherSO, CentralS, SOMinus, MinusAcceptord; - CurrEdges.num_edges = 0; /* clear current edge list */ - AllocEdgeList( &OtherSO, EDGE_LIST_CLEAR ); - AllocEdgeList( &CentralS, EDGE_LIST_CLEAR ); - AllocEdgeList( &SOMinus, EDGE_LIST_CLEAR ); - AllocEdgeList( &MinusAcceptord, EDGE_LIST_CLEAR ); - cur_success = 0; - if ( !at_Mobile_H_Revrs ) { - goto exit_case_21a; - } - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( /* orig. InChI info: -X(-) */ - /*num_DB_O < MAX_DIFF_FIXH &&*/ - /*pc2i->c2at[i].nValElectr == 6 */ /* O, S, Se, Te */ - !pc2i->c2at[i].endptInChI && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pc2i->c2at[i].nFixHInChI == 0 && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ - /* reversed structure info: */ - (pc2i->c2at[i].endptRevrs || pc2i->c2at[i].nMobHRevrs) && - pc2i->c2at[i].nFixHRevrs == 0 && - /*pc2i->c2at[i].nAtChargeRevrs == 0 &&*/ !at2[iat].num_H ) { - if ( pVA[iat].cNumValenceElectrons == 6 && at2[iat].charge == -1 && - pBNS->edge[e].flow && - at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 1 && - pVA[iS=(int)at2[iat].neighbor[0]].cNumValenceElectrons == 6 && pVA[iS].cPeriodicRowNumber > 1 && - at2[iS].valence >= 4 ) { - /* a candidate for S in -SO2- */ - int nNumTautSB = 0, nNumTautDB = 0, nNumOtherDB = 0, nNumOtherSB = 0; - int nNumOthers = 0, nNumNegEndp = 0, nNumEndpO = 0; - /* check whether we have already found it */ - if ( 0 <= FindInEdgeList( &CentralS, iS ) ) { - continue; - } - for ( j = 0; j < at[iS].valence; j ++ ) { - int bond_type = at2[iS].bond_type[j]; - k = at2[iS].neighbor[j]; - if ( k == iat ) { - continue; - } - if ( pc2i->c2at[i].endptRevrs == at_Mobile_H_Revrs[k].endpoint && !at2[k].endpoint ) { - nNumTautSB += ( bond_type == BOND_TYPE_SINGLE ); - nNumTautDB += ( bond_type == BOND_TYPE_DOUBLE ); - nNumEndpO += (pVA[k].cNumValenceElectrons == 6 && at2[k].valence == 1); - } else - if ( bond_type == BOND_TYPE_DOUBLE ) { - nNumOtherDB ++; - } else - if ( bond_type == BOND_TYPE_SINGLE ) { - nNumOtherSB ++; - } else { - nNumOthers ++; - } - if ( at2[k].endpoint == at2[i].endpoint && at2[k].valence == 1 && - at2[k].charge == -1 && pVA[k].cNumValenceElectrons == 6 ) { - nNumNegEndp ++; - } - } - if ( !nNumEndpO ) { - continue; - } - if ( nNumTautSB + nNumTautDB + nNumOtherDB <= nNumEndpO ) { - continue; /* ignore */ - } - /* collect double bond taut =O */ - for ( j = 0; j < at[iS].valence; j ++ ) { - int bond_type = at2[iS].bond_type[j]; - k = at2[iS].neighbor[j]; - if ( pc2i->c2at[i].endptRevrs == at_Mobile_H_Revrs[k].endpoint && - !at2[k].endpoint && pVA[k].cNumValenceElectrons == 6 && at2[k].valence == 1 && - 0 <= (e=pVA[k].nCMinusGroupEdge-1) && !pBNS->edge[e].forbidden ) { - if ( bond_type == BOND_TYPE_DOUBLE && !at2[k].charge && !pBNS->edge[e].flow) { - /* charges to be unchanged */ - if ( ret = AddToEdgeList( &OtherSO, e, INC_ADD_EDGE ) ) { - goto exit_case_21a; - } - } else - if ( bond_type == BOND_TYPE_SINGLE && at2[k].charge == -1 && pBNS->edge[e].flow ) { - /* charges to be removed */ - if ( ret = AddToEdgeList( &SOMinus, e, INC_ADD_EDGE ) ) { - goto exit_case_21a; - } - } - } - } - if ( ret = AddToEdgeList( &CentralS, iS, INC_ADD_EDGE ) ) { - goto exit_case_21a; - } - } else - if ( at2[iat].charge == 0 && !pBNS->edge[e].flow && - at2[iat].valence + 1 == at2[iat].chem_bonds_valence ) { - /* changeable charges */ - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - } - /* remove unchangeable from changeable */ - for ( i = 0; i < OtherSO.num_edges; i ++ ) { - RemoveFromEdgeListByValue( &CurrEdges, OtherSO.pnEdges[i] ); - } - - if ( num_try = inchi_min( SOMinus.num_edges, CurrEdges.num_edges ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < SOMinus.num_edges && cur_success < num_try; i ++ ) { - pe = pBNS->edge + SOMinus.pnEdges[i]; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - /*pe->forbidden |= forbidden_edge_mask;*/ - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Added (-) charge to =O => nDeltaCharge == 1 */ - /* Flow change on pe (-)charge edge (atom -N(-)-) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 21a */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } -exit_case_21a: - CurrEdges.num_edges = 0; /* clear current edge list */ - AllocEdgeList( &OtherSO, EDGE_LIST_FREE ); - AllocEdgeList( &CentralS, EDGE_LIST_FREE ); - AllocEdgeList( &SOMinus, EDGE_LIST_FREE ); - AllocEdgeList( &MinusAcceptord, EDGE_LIST_FREE ); - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pc2i->len_c2at ) { - /*------------------------------------------------------------------*/ - /* case 22: restored: N(-)=N(+)=C...=O orig: N#N-N=...-O(-) */ - /* im InChI -O(-) may have H(+) added by Normalization */ - /* or may be tautomeric */ - /* Solution: move (-) from N(-) to =O */ - /* */ - /*------------------------------------------------------------------*/ - int num_DB_O = 0, iat; - short iat_DB_O[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - int iN2, iC; - BNS_EDGE *peDB_O_Minus; - /* - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - */ - CurrEdges.num_edges = 0; /* clear current edge list */ - cur_success = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( /* orig. InChI info: =O */ - num_DB_O < MAX_DIFF_FIXH && - pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ && - (pc2i->c2at[i].endptInChI || pc2i->c2at[i].nMobHInChI) && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pc2i->c2at[i].nFixHInChI == 0 && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ - /* reversed structure info: */ - !(pc2i->c2at[i].endptRevrs || pc2i->c2at[i].nMobHRevrs) && - pc2i->c2at[i].nFixHRevrs == 0 && - pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && - at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 ) { - iat_DB_O[num_DB_O ++] = iat; - } - } - for ( i = 0; num_DB_O && i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( /* in restored atom O: charge=-1, no H, has no double bond, endpoint */ - at2[iat].charge == -1 && !at2[iat].num_H && - at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 && !pVA[iat].cMetal && - pVA[iat].cNumValenceElectrons == 5 && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pBNS->edge[e].flow && - !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint) && - pVA[iN2=at2[iat].neighbor[0]].cNumValenceElectrons == 5 && - at2[iat].bond_type[0] == BOND_TYPE_DOUBLE && - at2[iN2].charge == 1 && at2[iN2].valence == 2 && at2[iN2].chem_bonds_valence == 4 && - pVA[iC=at2[iN2].neighbor[at2[iN2].neighbor[0]==iN2]].cNumValenceElectrons == 4 ) { - - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( num_try = inchi_min( CurrEdges.num_edges, num_DB_O ) ) { - /* detected; attempt to fix */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - delta = 1; - for ( i = 0; i < num_DB_O && cur_success < num_try; i ++ ) { - iat = iat_DB_O[i]; - - peDB_O_Minus = pBNS->edge + (pVA[iat].nCMinusGroupEdge-1); - pe = pBNS->edge + pBNS->vert[iat].iedge[0]; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - peDB_O_Minus->forbidden &= forbidden_edge_mask_inv; - - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { - /* Added (-) charge to =O and removed from =N(-) => nDeltaCharge == 0 */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 22 */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - INCHI_HEAPCHK - pe->forbidden &= forbidden_edge_mask_inv; - peDB_O_Minus->forbidden |= forbidden_edge_mask; - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - if ( pc2i->len_c2at && pc2i->nNumTgInChI == 1 ) { - /*------------------------------------------------------------------*/ - /* case 23: -NO2 are to be tautomeric but they are not AND */ - /* InChI has a SINGLE tautomeric group */ - /* */ - /* (-)O (-)O */ - /* Solution: convert \ \ */ - /* N-X=...-Z(-) => N(+)=X- ...=Z */ - /* // / */ - /* O (-)O */ - /* */ - /* O O */ - /* or \\ \\ */ - /* N-X=...-Z(-) => N=X- ...=Z */ - /* // / */ - /* O (-)O */ - /* */ - /* */ - /* (a) move (-) from other tautomeric atom to O in O=N-X */ - /* or from other atom that has to be tautomeric */ - /* but is not */ - /* (b) create (+) [ion pair creation] on N as in */ - /* */ - /* OH OH */ - /* / / */ - /* -C=N => =C-N(+) */ - /* \\ \\ */ - /* O O */ - /* */ - /*------------------------------------------------------------------*/ - int num_DB_O = 0, iat; - short iat_DB_O[MAX_DIFF_FIXH], iat_NO2[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - /* - inp_ATOM *atfMobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at_fixed_bonds)? - pStruct->pOne_norm_data[1]->at_fixed_bonds : NULL; - */ - S_CHAR *num_Fixed_H_Revrs = pStruct->pOneINChI[0]->nNum_H_fixed? pStruct->pOneINChI[0]->nNum_H_fixed : NULL; - S_CHAR *pnMobHRevrs = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNum_H)? - pStruct->pOneINChI[1]->nNum_H : - (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)? - pStruct->pOneINChI[0]->nNum_H : NULL; - int iN, one_success; - BNS_EDGE *peDB_O_Minus; - int neigh, nNumO, nNumOthers; -#define CHG_SET_NOOH 0 -#define CHG_SET_WRONG_TAUT 1 -#define CHG_SET_TAUT 2 -#define CHG_LAST_SET 2 /* the last index in trying */ -#define CHG_SET_O_FIXED 3 -#define CHG_SET_NUM 4 - EDGE_LIST ChangeableEdges[CHG_SET_NUM]; - memset( ChangeableEdges, 0, sizeof(ChangeableEdges) ); - /* equivalent to AllocEdgeList( &EdgeList, EDGE_LIST_CLEAR ); */ - /* - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - */ - CurrEdges.num_edges = 0; /* clear current edge list */ - cur_success = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( /* orig. InChI info: taut in orig. InChI =O located in -NO2 that is not taut in Reconstructed InChI */ - num_DB_O < MAX_DIFF_FIXH && - pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ && - (pc2i->c2at[i].endptInChI /*|| pc2i->c2at[i].nMobHInChI*/) && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pc2i->c2at[i].nFixHInChI == 0 && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ - /* reversed structure info: */ - !(pc2i->c2at[i].endptRevrs /*|| pc2i->c2at[i].nMobHRevrs*/) && - pc2i->c2at[i].nFixHRevrs == 0 && - pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && - at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 && - /* find whether it belongs to NO2 */ - pVA[iN=at2[iat].neighbor[0]].cNumValenceElectrons == 5 && - at2[iN].valence == 3 && (at2[iN].charge == 0 || at2[iN].charge == 1) && - at2[iN].chem_bonds_valence == 5 - at2[iN].charge ) { - /* find the second O */ - nNumO = nNumOthers = 0; - for ( k = 0; k < at2[iN].valence; k ++ ) { - neigh = at2[iN].neighbor[k]; - if ( neigh == iat ) { - continue; - } - if ( pVA[neigh].cNumValenceElectrons == 6 && - pStruct->endpoint[neigh] && - !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[neigh].endpoint) && - at2[neigh].valence == 1 && at2[neigh].num_H == 0 && - at2[neigh].radical == 0 && (at2[neigh].charge == 0 || at2[neigh].charge == -1) && - at2[neigh].chem_bonds_valence - at2[neigh].charge == 2) { - nNumO ++; - } else - if ( at2[iN].bond_type[k] == BOND_TYPE_SINGLE && - at2[neigh].valence > 1 && - at2[neigh].valence < at2[neigh].chem_bonds_valence ) { - nNumOthers ++; - } - } - if ( nNumO != 1 || nNumOthers != 1 ) { - continue; - } - for ( k = 0; k < num_DB_O; k ++ ) { - if ( iat_NO2[k] == iN ) { - break; - } - } - if ( k == num_DB_O ) { - iat_NO2[num_DB_O] = iN; - iat_DB_O[num_DB_O ++] = iat; - } - /* save the edge to avoid interference */ - if ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e, INC_ADD_EDGE ) ) { - goto exit_case_23; - } - } - } - if ( num_DB_O ) { - /* 1. search for =N(=O)-OH; assume =N(+)(-O(-))(-OH) does not happen */ - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - /* find O=N(V) */ - iat = nCanon2AtnoRevrs[i]; - if ( !pStruct->endpoint[i] || pVA[i].cNumValenceElectrons != 6 || - at2[iat].valence != 1 || at2[iat].charge || - 0 > (e = pVA[iat].nCMinusGroupEdge-1) || - at2[iat].num_H + at2[iat].chem_bonds_valence != 2 || - pVA[iN=at2[iat].neighbor[0]].cNumValenceElectrons != 5 || - 0 > (e = pVA[iN].nCPlusGroupEdge-1) || - pBNS->edge[e].forbidden || !pBNS->edge[e].flow || - at2[iN].charge || at2[iN].valence != 3 || at2[iN].chem_bonds_valence != 5) { - continue; - } - /* find the second O, -OH */ - nNumO = nNumOthers = 0; - for ( k = 0; k < at2[iN].valence; k ++ ) { - neigh = at2[iN].neighbor[k]; - if ( neigh == iat ) { - continue; - } - if ( pVA[neigh].cNumValenceElectrons == 6 && - pStruct->endpoint[neigh] && - at2[neigh].valence == 1 && at2[neigh].num_H == 1 && - at2[neigh].radical == 0 && (at2[neigh].charge == 0 ) ) { - nNumO ++; - } else - if ( at2[iN].bond_type[k] == BOND_TYPE_DOUBLE && - at2[neigh].valence >= 2 && - at2[neigh].valence < at2[neigh].chem_bonds_valence ) { - nNumOthers ++; - } - } - if ( nNumO != 1 || nNumOthers != 1 ) { - continue; - } - /* save edges to be changed */ - if ( (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NOOH], e, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e, INC_ADD_EDGE ))) { - goto exit_case_23; - } - if ( NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e )) && - (( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NOOH], j, INC_ADD_EDGE ) ) || - ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e, INC_ADD_EDGE ) ))) { - goto exit_case_23; - } - } - /* 2. search for (-) atoms that are tautomeric but should not be */ - /* or that got H from Normalization but they shouldn't */ - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( at2[iat].charge == -1 && - !pStruct->endpoint[i] && - (at_Mobile_H_Revrs && - (at_Mobile_H_Revrs[i].endpoint || at2[iat].num_H < at_Mobile_H_Revrs[i].num_H )) ) { - - if ( 0 <= (e = pVA[iat].nCMinusGroupEdge-1) && - 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e ) && - !pBNS->edge[e].forbidden && pBNS->edge[e].flow && - ( - ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_WRONG_TAUT], e, INC_ADD_EDGE ) ) || - ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e, INC_ADD_EDGE ) ) - ) ) { - goto exit_case_23; - } - } else - /* negatively charged atom in Reconstructed structure got H(+) from Normalization */ - /* and is not tautomeric; in the original structure it is tautomeric */ - if ( at2[iat].charge == -1 && - pStruct->endpoint[i] && - !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[i].endpoint) && - (num_Fixed_H_Revrs && num_Fixed_H_Revrs[i] == -1) && - (pnMobHRevrs && pnMobHRevrs[i] == 1) && - pStruct->fixed_H[i] == 0 ) { - - if ( 0 <= (e = pVA[iat].nCMinusGroupEdge-1) && - 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e ) && - !pBNS->edge[e].forbidden && pBNS->edge[e].flow && - ( - ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_WRONG_TAUT], e, INC_ADD_EDGE ) ) || - ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e, INC_ADD_EDGE ) ) - ) ) { - goto exit_case_23; - } - } - } - /* 3. Search for (-) atoms that are tautomeric */ - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - iat = nCanon2AtnoRevrs[i]; - if ( pStruct->endpoint[i] && - (at_Mobile_H_Revrs && at_Mobile_H_Revrs[i].endpoint) && - at2[iat].charge == -1 - /*&& pVA[i].cNumValenceElectrons == 6*/ ) { - if ( 0 <= (e = pVA[iat].nCMinusGroupEdge-1) && - !pBNS->edge[e].forbidden && pBNS->edge[e].flow && - 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e ) && - ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_TAUT], e, INC_ADD_EDGE ) ) ) { - goto exit_case_23; - } - } - } - /* ------- finally, try to move charges from O=N --------------*/ - for ( i = 0; i < num_DB_O; i ++ ) { - int nDeltaChargeExpected; - one_success = 0; - delta = 1; - iat = iat_DB_O[i]; - peDB_O_Minus = pBNS->edge + (pVA[iat].nCMinusGroupEdge-1); - pe = pBNS->edge + pBNS->vert[iat].iedge[0]; - - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - for ( k = 0; !one_success && k <= CHG_LAST_SET; k ++ ) { - if ( !ChangeableEdges[k].num_edges ) { - continue; - } - nDeltaChargeExpected = (k==CHG_SET_NOOH)? 2 : 0; - - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &ChangeableEdges[k], forbidden_edge_mask ); - /* allow (-) charge to move to N=O */ - peDB_O_Minus->forbidden &= forbidden_edge_mask_inv; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && - nDeltaCharge == nDeltaChargeExpected ) { - /* Move (-) charge to =O and remove it an endpoint => nDeltaCharge == 0 */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - one_success ++; /* 23 */ - } - } - INCHI_HEAPCHK - } - cur_success += one_success; - - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - pe->forbidden &= forbidden_edge_mask_inv; - - if ( !one_success ) { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - } - } -exit_case_23: - for ( i = 0; i < CHG_SET_NUM; i ++ ) { - AllocEdgeList( &ChangeableEdges[i], EDGE_LIST_FREE ); - } - - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } -#undef CHG_SET_NOOH -#undef CHG_SET_WRONG_TAUT -#undef CHG_SET_TAUT -#undef CHG_LAST_SET -#undef CHG_SET_O_FIXED -#undef CHG_SET_NUM - } - - if ( pc2i->len_c2at && pc2i->nNumTgInChI == 1 ) { - /*------------------------------------------------------------------*/ - /* case 24: InChI norm. -N(-)-N(+)(IV) => -N=N(V) prevents tauto- */ - /* merism on -N(-)- in case of ADP */ - /* */ - /* Solution: convert N(V)=N- ...=X -> N(IV)(+)-N=...-X(-)*/ - /* N(IV)(+)-N(-)-...=X */ - /* */ - /* Orig InChI taut taut, 1 t-group only(ADP?) */ - /* Reconstructed struct non-taut possibly not taut */ - /* */ - /* Details: 1a. store next to N(V) (+)edge its flower edge */ - /* 1b. store next to N(-) edge NO_VERTEX */ - /* 2. Release (-) edges of other missing endpoints or */ - /* all endpoints if no other is missing */ - /* 3. Decrement flow on (+) edge */ - /* if flower edge is stored then expect DeltaCharge=2*/ - /* otherwise DeltaCharge = 0 */ - /*------------------------------------------------------------------*/ - int iat; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - inp_ATOM *atf = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at_fixed_bonds)? - pStruct->pOne_norm_data[1]->at_fixed_bonds : NULL; - int iN, one_success; - EdgeIndex ef, e1; - BNS_EDGE *pef; -#define CHG_SET_MISSED_TAUT 0 -#define CHG_SET_OTHER_TAUT_O 1 -#define CHG_SET_OTHER_TAUT_N 2 -#define CHG_LAST_SET 2 /* the last index in trying */ -#define CHG_SET_NN 3 -#define CHG_SET_AVOID 4 -#define CHG_SET_NUM 5 - EDGE_LIST ChangeableEdges[CHG_SET_NUM]; - memset( ChangeableEdges, 0, sizeof(ChangeableEdges) ); - /* equivalent to AllocEdgeList( &EdgeList, EDGE_LIST_CLEAR ); */ - /* - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - */ - CurrEdges.num_edges = 0; /* clear current edge list */ - cur_success = 0; - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( /* orig. InChI info: -N=N(V) */ - pc2i->c2at[i].nValElectr == 5 /* N or P */ && - (pc2i->c2at[i].endptInChI /* only N */) && - (e1=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e1].forbidden && - pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI == 0 && - /* reversed structure info: */ - !pc2i->c2at[i].endptRevrs && - pc2i->c2at[i].nFixHRevrs == 0 && - pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && - at2[iat].valence == 2 && at2[iat].chem_bonds_valence == 3 && - /* find whether -N= has =N(V) neighbor; Note: operator comma: (A,B) returns B */ - (iN = at2[iat].neighbor[at2[iat].bond_type[0] != BOND_TYPE_DOUBLE], - pVA[iN].cNumValenceElectrons == 5) && - at2[iN].chem_bonds_valence == 5 && - at2[iN].charge == 0 && !at2[iN].num_H && !at2[iN].radical && - 0 <= (e=pVA[iN].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && - 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e )) { - - ef = GetChargeFlowerUpperEdge( pBNS, pVA, e ); /* == NO_VERTEX if N(V) has 4 bonds */ - if ( (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NN], e, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NN], ef, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NN], 1, INC_ADD_EDGE )) || /* expected nDeltaCharge */ - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e1, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], ef, INC_ADD_EDGE ))) { - goto exit_case_24; - } - /* mark -N= so that (-) will not be moved to it */ - if ( 0 <= (e = pVA[iat].nCMinusGroupEdge) && !pBNS->edge[e].forbidden && - 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) && - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE ))) { - goto exit_case_24; - } - } else - if ( /* orig. InChI info: -N(-)N(IV)(+) */ - atf && - pc2i->c2at[i].nValElectr == 5 /* N or P */ && - pc2i->c2at[i].endptInChI /* only N */ && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI == 0 && - /* reversed structure info: */ - !pc2i->c2at[i].endptRevrs && - pc2i->c2at[i].nFixHRevrs == 0 && - pc2i->c2at[i].nAtChargeRevrs == -1 && !at2[iat].num_H && - at2[iat].valence == 2 && at2[iat].chem_bonds_valence == 2 && - atf[iat].valence == 2 && atf[iat].chem_bonds_valence == 3 && - /* find whether -N= has =N(V) neighbor; Note: operator comma: (A,B) returns B */ - (iN=atf[iat].neighbor[atf[iat].bond_type[0] != BOND_TYPE_DOUBLE], - pVA[iN].cNumValenceElectrons == 5) && - at2[iN].charge == 1 && /* double bond neighbor */ - at2[iN].chem_bonds_valence == 4 && - atf[iN].charge == 0 && - atf[iN].chem_bonds_valence == 5 && /* InChI normalization created N(V)=N- out of N(IV)(+)-N(-)- */ - !at2[iN].num_H && !at2[iN].radical && - 0 <= (e=pVA[iat].nCMinusGroupEdge-1) && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && - 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) ) { - /* save (-) edge */ - if ( (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NN], e, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NN], NO_VERTEX, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NN], 1, INC_ADD_EDGE )) || /* expected nDeltaCharge */ - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE ))) { - goto exit_case_24; - } - } - } - if ( !ChangeableEdges[CHG_SET_NN].num_edges ) { - goto exit_case_24; - } - /* Collect all relevant tautomeric atoms */ - for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ - if ( !pStruct->endpoint[i] ) { - continue; - } - iat = nCanon2AtnoRevrs[i]; - if ( at2[iat].charge || at2[iat].radical || at2[iat].valence == at2[iat].chem_bonds_valence ) { - continue; /* cannot be an acceptor of (-) */ - } - if ( 0 > (e=pVA[iat].nCMinusGroupEdge-1) || pBNS->edge[e].forbidden || pBNS->edge[e].flow ) { - continue; - } - if ( 0 <= FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) ) { - continue; /* has already been used */ - } - /* missing endpoint */ - if ( !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint) ) { - if ( 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) && ( - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_MISSED_TAUT], e, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE )))) { - goto exit_case_24; - } - } else - /* endpoint O */ - if ( pVA[iat].cNumValenceElectrons == 6 ) { - if ( 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) && ( - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_OTHER_TAUT_O], e, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE )))){ - goto exit_case_24; - } - } else - /* endpoint N */ - if ( pVA[iat].cNumValenceElectrons == 5 ) { - if ( 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) && ( - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_OTHER_TAUT_N], e, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE )))){ - goto exit_case_24; - } - } - } - /* ------- finally, try to move charges from -N(-)-N(+) or to N(V) --------------*/ - for ( i = 0; i < ChangeableEdges[CHG_SET_NN].num_edges; i += 3 ) { - int nDeltaChargeExpected; - one_success = 0; - delta = 1; - pe = pBNS->edge + ChangeableEdges[CHG_SET_NN].pnEdges[i]; - pef = (NO_VERTEX != ChangeableEdges[CHG_SET_NN].pnEdges[i+1])? - pBNS->edge + ChangeableEdges[CHG_SET_NN].pnEdges[i+1] : NULL; - nDeltaChargeExpected = ChangeableEdges[CHG_SET_NN].pnEdges[i+2]; - - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - for ( k = 0; !one_success && k <= CHG_LAST_SET; k ++ ) { - if ( !ChangeableEdges[k].num_edges ) { - continue; - } - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &ChangeableEdges[k], forbidden_edge_mask ); - /* allow change of N(V) flower edge */ - if ( pef ) { - pef->forbidden &= forbidden_edge_mask_inv; - } - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && - nDeltaCharge == nDeltaChargeExpected ) { - /* Move (-) charge to =O and remove it an endpoint => nDeltaCharge == 0 */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - one_success ++; /* 24 */ - } - } - INCHI_HEAPCHK - } - cur_success += one_success; - - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - - if ( !one_success ) { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - } -exit_case_24: - for ( i = 0; i < CHG_SET_NUM; i ++ ) { - AllocEdgeList( &ChangeableEdges[i], EDGE_LIST_FREE ); - } - - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } -#undef CHG_SET_NN -#undef CHG_SET_MISSED_TAUT -#undef CHG_SET_OTHER_TAUT_O -#undef CHG_SET_OTHER_TAUT_N -#undef CHG_LAST_SET -#undef CHG_SET_AVOID -#undef CHG_SET_NUM - } - - /* pStruct->nNumRemovedProtonsMobHInChI == pc2i->nNumRemHInChI */ - - if ( pc2i->len_c2at && pc2i->nNumTgInChI == 1 && - pc2i->nNumRemHRevrs > pc2i->nNumRemHInChI && 0 > pc2i->nNumRemHInChI && - (pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || - pc2i->nNumTgRevrs > pc2i->nNumTgInChI ) ) { - /*------------------------------------------------------------------*/ - /* case 25: Restored InChI does not have 2 or more added protons */ - /* possibly taut. endpoints are missing */ - /* has -N(-O(-))-O(-) group(s) */ - /* Original InChI has only one t-group */ - /* */ - /* Solution: convert -N(-O(-))-O(-) -> -N(+)(=O)-O(-) */ - /* and direct 2(-) to the missing taut atoms*/ - /* at first attempt try to move (-) to N only */ - /* */ - /*------------------------------------------------------------------*/ - int iat; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - AT_NUMB *nAtno2CanonRevrs = pStruct->nAtno2Canon[0]; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - /* - inp_ATOM *atf = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at_fixed_bonds)? - pStruct->pOne_norm_data[1]->at_fixed_bonds : NULL; - */ - int iN, neigh, one_success; - EdgeIndex e1, bFirst; - BNS_EDGE *pef; -#define CHG_SET_MISSED_TAUT_1 0 -#define CHG_SET_MISSED_TAUT_ALL 1 -#define CHG_SET_OTHER_TAUT_1 2 -#define CHG_SET_OTHER_TAUT_ALL 3 -#define CHG_LAST_SET 3 /* the last index in trying */ -#define CHG_SET_NO_IN_NO2M2 4 -#define CHG_SET_AVOID 5 -#define CHG_SET_NUM 6 - EDGE_LIST ChangeableEdges[CHG_SET_NUM]; - memset( ChangeableEdges, 0, sizeof(ChangeableEdges) ); - /* equivalent to AllocEdgeList( &EdgeList, EDGE_LIST_CLEAR ); */ - /* - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - */ - CurrEdges.num_edges = 0; /* clear current edge list */ - cur_success = 0; - /* find all -N(-O(-))-O(-) */ - for ( i = 0; i < pStruct->num_atoms; i ++ ) { - iat = nCanon2AtnoRevrs[i]; - if ( pStruct->endpoint[i] ) { - if ( 0 > (e=pVA[iat].nCMinusGroupEdge-1) || pBNS->edge[e].forbidden || - 0 <= FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) ) { - continue; - } - bFirst = ( pVA[iat].cNumValenceElectrons == 5 && pc2i->nNumTgInChI == 1 || - pVA[iat].cNumValenceElectrons == 6 && pc2i->nNumTgInChI != 1 ); - /* many or no t-groups -> try O only first */ - /* single t-group -> try only N first */ - if ( !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[i].endpoint) ) { - /* missed tautomeric endpoint */ - if ( bFirst && - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_MISSED_TAUT_1], e, INC_ADD_EDGE ))) { - goto exit_case_25; - } - if (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_MISSED_TAUT_ALL], e, INC_ADD_EDGE )) { - goto exit_case_25; - } - } - if ( bFirst && - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_OTHER_TAUT_1], e, INC_ADD_EDGE ))) { - goto exit_case_25; - } - if (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_OTHER_TAUT_ALL], e, INC_ADD_EDGE )) { - goto exit_case_25; - } - if (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE )) { - goto exit_case_25; - } - } else - if ( at2[iat].valence == 1 && at2[iat].charge == -1 && - pVA[iat].cNumValenceElectrons == 6 && - pVA[iN=at2[iat].neighbor[0]].cNumValenceElectrons == 5 && /* -O(-) */ - !pStruct->endpoint[nAtno2CanonRevrs[iN]] && - at2[iN].valence == 3 && at2[iN].chem_bonds_valence == 3 && - !at2[iN].charge && !at2[iN].radical && - 0 <= (e=pVA[iN].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && - pBNS->edge[e].flow && /* NPlus edge */ - 0 <= (e1 = pVA[iat].nCMinusGroupEdge-1) && !pBNS->edge[e1].forbidden && - pBNS->edge[e1].flow && /* OMinus edge */ - 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) && - 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e1 )) { - /* found >N-O(-) */ - int nNumO = 0, nNumOthers = 0; - for ( k = 0; k < at2[iN].valence; k ++ ) { - neigh = at2[iN].neighbor[k]; - if ( neigh == iat ) { - continue; - } - if ( pVA[neigh].cNumValenceElectrons == 6 && - !pStruct->endpoint[neigh] && - at2[neigh].valence == 1 && at2[neigh].num_H == 0 && - at2[neigh].radical == 0 && at2[neigh].charge == -1 && - at2[neigh].chem_bonds_valence == 1 ) { - nNumO ++; - } else - if ( at2[iN].bond_type[k] == BOND_TYPE_SINGLE && - at2[neigh].valence > 1 && - at2[neigh].valence < at2[neigh].chem_bonds_valence ) { - nNumOthers ++; - } - } - if ( nNumO != 1 && nNumOthers != 1 ) { - continue; - } - /* save charge edges: NPlus first, OMinus second */ - if ( (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NO_IN_NO2M2], e, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NO_IN_NO2M2], e1, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE )) || - (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e1, INC_ADD_EDGE ))) { - goto exit_case_25; - } - } - } - if ( !ChangeableEdges[CHG_SET_NO_IN_NO2M2].num_edges || - !ChangeableEdges[CHG_SET_OTHER_TAUT_ALL].num_edges ) { - goto exit_case_25; - } - /* ------- finally, try to move charges from -NO2(2-) or to tautomeric endpoints ----*/ - for ( i = 0; i < ChangeableEdges[CHG_SET_NO_IN_NO2M2].num_edges; i += 2 ) { - int nDeltaChargeExpected = 3; - /* change flow on O(-) to make it neutral; 3 new charges will be created: - N(+), and two (-) on InChI endpoints - alternatively, if we change flow on N to make N(+) then O(-) will - be nutralized (-1 charge) and two (-) charges on taut. endpoints will be - created (+2); the total change in this case would be (-1)+(+2) = +1 - */ - one_success = 0; - delta = 1; - pe = pBNS->edge + ChangeableEdges[CHG_SET_NO_IN_NO2M2].pnEdges[i+1]; /* O(-) edge */ - pef = pBNS->edge + ChangeableEdges[CHG_SET_NO_IN_NO2M2].pnEdges[i]; /* >N- (+) edge */ - - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - for ( k = 0; !one_success && k <= CHG_LAST_SET; k ++ ) { - if ( !ChangeableEdges[k].num_edges ) { - continue; - } - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &ChangeableEdges[k], forbidden_edge_mask ); - /* allow change of N(V) flower edge */ - pef->forbidden &= forbidden_edge_mask_inv; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && - nDeltaCharge == nDeltaChargeExpected ) { - /* Move (-) charge to =O and remove it an endpoint => nDeltaCharge == 0 */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - one_success ++; /* 24 */ - } - } - INCHI_HEAPCHK - } - cur_success += one_success; - - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - - if ( !one_success ) { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - } -exit_case_25: - for ( i = 0; i < CHG_SET_NUM; i ++ ) { - AllocEdgeList( &ChangeableEdges[i], EDGE_LIST_FREE ); - } - - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } -#undef CHG_SET_NN -#undef CHG_SET_MISSED_TAUT -#undef CHG_SET_OTHER_TAUT_O -#undef CHG_SET_OTHER_TAUT_N -#undef CHG_LAST_SET -#undef CHG_SET_AVOID -#undef CHG_SET_NUM - } - - -exit_function: - AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &CurrEdges, EDGE_LIST_FREE ); - AllocEdgeList( &NFlowerEdges, EDGE_LIST_FREE ); - AllocEdgeList( &SFlowerEdges, EDGE_LIST_FREE ); - AllocEdgeList( &OtherNFlowerEdges, EDGE_LIST_FREE ); - AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_FREE ); - AllocEdgeList( &AllBondEdges, EDGE_LIST_FREE ); - return ret < 0? ret : (pc2i->bHasDifference && tot_succes); -} -#endif +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include + +/*#define CHECK_WIN32_VC_HEAP*/ +#include "mode.h" +#include "ichicant.h" +#include "ichitime.h" +#include "ichirvrs.h" + + +#if ( READ_INCHI_STRING == 1 ) + +#define INC_ADD_EDGE 64 + +/* local types */ + +/* types for TgDiffHChgFH */ +#define fNumRPosChgH 0 /* number of positive charges on endpoints that have H in at2[] */ +#define fNumRPosChgU 1 /* number of positive charges on endpoints that have no H in at2[] */ +#define fNumRNegChgO 2 /* number of negative charges on O endpoints */ +#define fNumRNegChgN 3 /* number of negative charges on N endpoints */ +#define fNumRNeutrlH 4 /* number of neutral endp that have H in at2[] */ + +#define fNumNPosChgH 5 /* number of positive charges on endpoints that have H in atf[] */ +#define fNumNPosChgU 6 /* number of positive charges on endpoints that have no H in atf[] */ +#define fNumNNegChgO 7 /* number of negative charges on O endpoints */ +#define fNumNNegChgN 8 /* number of negative charges on N endpoints */ +#define fNumNNeutrlH 9 /* number of neutral endp that have H in atf[] */ + +#define fNumAllChgT 10 /* total number of fNum... */ + +typedef struct tagTgDiffHChgFH { + short itg; /* t-group index; endpoint = itg+1 */ + short nNumHInchi; /* number of H in t-group from orig. InChI */ + short nNumHRevrs; /* number of H in at2[] */ + short nNumHNorml; /* number of H in Normalized atfMobile_H_Revrs[] */ + short nNumMInchi; /* number of (-) in InChI */ + short nNumMRevrs; /* number of (-) in at2[] */ + short nNumMNorml; /* number of (-) in atf[] */ + short nNumPRevrs; /* number of (+) in at2[] */ + short nNumPNorml; /* number of (+) in Normalized atfMobile_H_Revrs[] */ + short n[fNumAllChgT]; /* all numbers */ + short i[fNumAllChgT]; /* all indices */ +} TgDiffHChgFH; + +/* local prototypes */ +static int FillTgDiffHChgFH( TgDiffHChgFH tdhc[], int max_tdhc, inp_ATOM at2[], inp_ATOM atf[], + AT_NUMB *nCanon2AtnoRevrs, VAL_AT *pVA, T_GROUP_INFO *ti, EDGE_LIST *pAtomIndList ); + + +/************************************************************/ +int bHas_N_V( inp_ATOM *at2, int num_atoms ) +{ + static U_CHAR el_number_N; + int i, num_found = 0; + if ( !el_number_N ) { + el_number_N = get_periodic_table_number( "N" ); + } + for ( i = 0; i < num_atoms; i ++ ) { + if ( at2[i].el_number == el_number_N && !at2[i].charge && + !at2[i].num_H && !at2[i].radical && + at2[i].chem_bonds_valence == 5 && + (at2[i].valence==3) ) { + num_found ++; + } + } + return num_found; +} +/*************************************************************************************/ +int FillTgDiffHChgFH( TgDiffHChgFH tdhc[], int max_tdhc, inp_ATOM at2[], + inp_ATOM atf[], AT_NUMB *nCanon2AtnoRevrs, VAL_AT *pVA, + T_GROUP_INFO *ti, EDGE_LIST *pAtomIndList ) +{ + + int i, j, iat, itg, itg_prev, num, itg_out, bOverflow; + EDGE_LIST IndList; /* type, itg */ + TgDiffHChgFH cur_tdhc; + AT_NUMB *pEndp0; + inp_ATOM *at2i, *atfi; + int typeR, typeN, type, ret = 0, nCurIndListLen; + + AllocEdgeList( &IndList, EDGE_LIST_CLEAR ); + pAtomIndList->num_edges = 0; + itg_out = 0; + bOverflow = 0; + memset( tdhc, 0, max_tdhc * sizeof(tdhc[0]) ); + + for ( itg = 0; itg < ti->num_t_groups; itg ++ ) { + memset( &cur_tdhc, 0, sizeof(cur_tdhc) ); + + cur_tdhc.itg = itg; + cur_tdhc.nNumHInchi = ti->t_group[itg].num[0] - ti->t_group[itg].num[1]; + cur_tdhc.nNumMInchi = ti->t_group[itg].num[1]; + + pEndp0 = ti->nEndpointAtomNumber + ti->t_group[itg].nFirstEndpointAtNoPos; + nCurIndListLen = IndList.num_edges; + for ( j = 0; j < ti->t_group[itg].nNumEndpoints; j ++ ) { + i = pEndp0[j]; + iat = nCanon2AtnoRevrs[i]; + + at2i = at2 + iat; + atfi = atf + iat; + + typeR = typeN = -1; + if ( at2i->charge == 1 ) { + if ( at2i->num_H ) { + typeR = fNumRPosChgH; + } else { + typeR = fNumRPosChgU; + } + cur_tdhc.nNumPRevrs ++; + } else + if ( at2i->charge == -1 ) { + if ( pVA[iat].cNumValenceElectrons == 6) { + typeR = fNumRNegChgO; + } else + if ( pVA[iat].cNumValenceElectrons == 5) { + typeR = fNumRNegChgN; + } + cur_tdhc.nNumMRevrs ++; + } else + if ( at2i->num_H && at2i->valence == at2i->chem_bonds_valence ) { + typeR = fNumRNeutrlH; + } + cur_tdhc.nNumHRevrs += at2i->num_H; + + if ( atfi->charge == 1 ) { + if ( atfi->num_H ) { + typeN = fNumNPosChgH; + } else { + typeN = fNumNPosChgU; + } + cur_tdhc.nNumPNorml ++; + } else + if ( atfi->charge == -1 ) { + if ( pVA[iat].cNumValenceElectrons == 6) { + typeN = fNumNNegChgO; + } else + if ( pVA[iat].cNumValenceElectrons == 5) { + typeN = fNumNNegChgN; + } + cur_tdhc.nNumMNorml ++; + } else + if ( atfi->num_H && atfi->valence == atfi->chem_bonds_valence ) { + typeN = fNumNNeutrlH; + } + cur_tdhc.nNumHNorml += atfi->num_H; + if ( at2[iat].charge < 0 || 0 < pVA[iat].nCPlusGroupEdge ) { + if ( typeR >= 0 && ( + (ret = AddToEdgeList( &IndList, typeR, INC_ADD_EDGE )) || + (ret = AddToEdgeList( &IndList, itg, INC_ADD_EDGE )) || + (ret = AddToEdgeList( &IndList, iat, INC_ADD_EDGE )) ) ) { + goto exit_function; + } + if ( typeN >= 0 && ( + (ret = AddToEdgeList( &IndList, typeN, INC_ADD_EDGE )) || + (ret = AddToEdgeList( &IndList, itg, INC_ADD_EDGE )) || + (ret = AddToEdgeList( &IndList, iat, INC_ADD_EDGE )) ) ) { + goto exit_function; + } + } + } + if ( cur_tdhc.nNumHNorml == cur_tdhc.nNumHInchi && + cur_tdhc.nNumMNorml == cur_tdhc.nNumMInchi ) { + IndList.num_edges = nCurIndListLen; /* t-group seems to be correct */ + continue; + } + if ( itg_out < max_tdhc ) { + tdhc[itg_out ++] = cur_tdhc; + } else { + bOverflow |= 1; + IndList.num_edges = nCurIndListLen; + break; + } + } + /* fill out atom index list */ + if ( itg_out ) { + itg_prev = IndList.pnEdges[1]; /* the 1st saved t-group number */ + for ( type = 0; type < fNumAllChgT; type ++ ) { + j = 0; + for ( i = 0; i < itg_out; i ++ ) { + num = 0; + itg = tdhc[i].itg; + tdhc[i].i[type] = -999; /* empty */ + while( IndList.pnEdges[j+1] == itg ) { + if ( IndList.pnEdges[j] == type ) { + if ( !num ++ ) { + tdhc[i].i[type] = pAtomIndList->num_edges; + } + if ( ret = AddToEdgeList( pAtomIndList, IndList.pnEdges[j+2], INC_ADD_EDGE )) { + goto exit_function; + } + } + j += 3; + } + tdhc[i].n[type] = num; + } + } + } + ret = itg_out; +exit_function: + AllocEdgeList( &IndList, EDGE_LIST_FREE ); + return ret; + +/* +#undef fNumRPosChgH +#undef fNumRPosChgU +#undef fNumRNegChgO +#undef fNumRNegChgN + +#undef fNumNPosChgH +#undef fNumNPosChgU +#undef fNumNNegChgO +#undef fNumNNegChgN + +#undef fNumAllChgT +*/ +} + +/***********************************************************************************************/ +int FixFixedHRestoredStructure( CANON_GLOBALS *pCG, + INCHI_CLOCK *ic, + ICHICONST INPUT_PARMS *ip, + STRUCT_DATA *sd, + BN_STRUCT *pBNS, + BN_DATA *pBD, + StrFromINChI *pStruct, + inp_ATOM *at, + inp_ATOM *at2, + inp_ATOM *at3, + VAL_AT *pVA, + ALL_TC_GROUPS *pTCGroups, + T_GROUP_INFO **ppt_group_info, + inp_ATOM **ppat_norm, + inp_ATOM **ppat_prep, + INChI *pInChI[], + long num_inp, + int bHasSomeFixedH, + int *pnNumRunBNS, + int *pnTotalDelta, + int forbidden_edge_mask, + int forbidden_stereo_edge_mask) +{ + /*--------- process extra or missing Fixed-H on non-tautomeric atoms ------*/ + /* at2 should be the most recently restored atom, Fixed-H */ + int i, j, k, delta, num_try, tot_succes, cur_success, ret = 0, bAllowedNFlowerEdges=0, num_zero_ret; + CMP2FHINCHI c2i; + CMP2FHINCHI *pc2i = &c2i; + + EDGE_LIST AllChargeEdges, CurrEdges, SFlowerEdges, NFlowerEdges, OtherNFlowerEdges, FixedLargeRingStereoEdges; + EDGE_LIST AllBondEdges; + + EdgeIndex e; + BNS_EDGE *pe; + Vertex v1, v2; + BNS_VERTEX *pv1, *pv2; + + Vertex vPathStart, vPathEnd; + int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; + + int nNumRunBNS = 0, forbidden_edge_mask_inv = ~forbidden_edge_mask; + + INCHI_HEAPCHK + + AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &CurrEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &NFlowerEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &SFlowerEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &OtherNFlowerEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &AllBondEdges, EDGE_LIST_CLEAR ); + + tot_succes = 0; + + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + + for ( i = 0; i < pStruct->num_atoms; i ++ ) { + if ( (e=pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + (ret = AddToEdgeList( &AllChargeEdges, e, INC_ADD_EDGE )) ) { + goto exit_function; + } + if ( (e=pVA[i].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { + if ( ret = AddToEdgeList( &AllChargeEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + + /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ + if ( pVA[i].cNumValenceElectrons == 5 && !pVA[i].cMetal && /* N, P, As */ + NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e ))) { + + if ( pBNS->edge[j].forbidden ) { + continue; + } + + if ( pBNS->edge[j].flow ) { + if ( ret = AddToEdgeList( &AllChargeEdges, j, INC_ADD_EDGE ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &NFlowerEdges, j, INC_ADD_EDGE ) ) { + goto exit_function; + } + } else { + if ( ret = AddToEdgeList( &OtherNFlowerEdges, j, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } else + /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ + if ( pVA[i].cNumValenceElectrons == 6 && !pVA[i].cMetal && /* N, P, As */ + NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e ))) { + + if ( pBNS->edge[j].forbidden ) { + continue; + } + + if ( pBNS->edge[j].flow ) { + if ( ret = AddToEdgeList( &SFlowerEdges, j, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + } + for ( j = 0; j < at2[i].valence; j ++ ) { + k = at2[i].neighbor[j]; + if ( k < i && !pBNS->edge[e=pBNS->vert[i].iedge[j]].forbidden ) { + if ( ret = AddToEdgeList( &AllBondEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + } + if ( forbidden_stereo_edge_mask ) { + for ( i = 0; i < pStruct->num_atoms; i ++ ) { + for ( j = 0; j < at2[i].valence; j ++ ) { + if ( pBNS->edge[k = pBNS->vert[i].iedge[j]].forbidden == forbidden_stereo_edge_mask ) { + int nMinRingSize = is_bond_in_Nmax_memb_ring( at2, i, j, pStruct->pbfsq->q, + pStruct->pbfsq->nAtomLevel, + pStruct->pbfsq->cSource, 99 /* max ring size */ ); + if ( 0 < nMinRingSize && (ret = AddToEdgeList( &FixedLargeRingStereoEdges, k, INC_ADD_EDGE ))) { + goto exit_function; + } + } + } + } + } + + INCHI_HEAPCHK + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + INCHI_HEAPCHK + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + + INCHI_HEAPCHK + + if ( !pc2i->bHasDifference || + !pc2i->len_c2at && pc2i->nNumTgRevrs == pc2i->nNumTgInChI && + pc2i->nNumEndpRevrs == pc2i->nNumRemHInChI && + pc2i->nNumEndpRevrs == pc2i->nNumEndpInChI && + !pc2i->nNumTgDiffMinus && !pc2i->nNumTgDiffH ) { + goto exit_function; /* nothing to do */ + } + + /*goto exit_function;*/ /* debug only*/ + + if ( pc2i->len_c2at >= 2 ) { + /*----------------------------------------------------*/ + /* case 01: restored: O=AB-O(-) original: (-)O-AB=O */ + /* FixH: 0 -1 -1 0 */ + /* MobH: 0 1 1 0 */ + /* non-taut non-taut */ + /* O = O, S, Se; charged atoms O are not tautomeric */ + /* Solution: move (-) from B-O(-) to O=A */ + /*----------------------------------------------------*/ + int num_DB_O = 0, num_SB_O_Minus = 0, iat; + short iat_DB_O[MAX_DIFF_FIXH], iat_SB_O_Minus[MAX_DIFF_FIXH]; + cur_success = 0; + for ( i = 0; i < pc2i->len_c2at; i ++ ) { + iat = pc2i->c2at[i].atomNumber; + if ( pc2i->c2at[i].nValElectr == 6 /* && !pc2i->c2at[i].endptInChI -- mod#1*/ && + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { + if ( /* orig. InChI info: */ + num_SB_O_Minus < MAX_DIFF_FIXH && + pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI == 0 && + /* reversed structure info: */ + pc2i->c2at[i].nFixHRevrs == -1 && pc2i->c2at[i].nMobHRevrs == 1 && + pc2i->c2at[i].nAtChargeRevrs == -1 && !at2[iat].num_H && /* at2 is Fixed-H */ + at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 1 ) { + iat_SB_O_Minus[num_SB_O_Minus ++] = iat; + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } else + if ( /* orig. InChI info: */ + num_DB_O < MAX_DIFF_FIXH && + pc2i->c2at[i].nFixHInChI == -1 && pc2i->c2at[i].nMobHInChI == 1 && + /* reversed structure info: */ + pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && + pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && + at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 ) { + iat_DB_O[num_DB_O ++] = iat; + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + } + if ( num_try = inchi_min( num_SB_O_Minus, num_DB_O ) ) { + /* detected; attempt to fix */ + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + delta = 1; + for ( i = 0; i < num_SB_O_Minus && cur_success < num_try; i ++ ) { + iat = iat_SB_O_Minus[i]; + pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->forbidden |= forbidden_edge_mask; + pe->flow -= delta; /* remove (-) from AB-O(-) */ + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { + /* Added (-)charge to O=AB => nDeltaCharge == -1 */ + /* Flow change on pe (-)charge edge (atom B-O(-)) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 01 */ + } + } else { + pe->forbidden &= forbidden_edge_mask_inv; + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + CurrEdges.num_edges = 0; /* clear current edge list */ + } + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, + ip, sd, pBNS, pStruct, + at, at2, at3, pVA, + pTCGroups, + ppt_group_info, ppat_norm, + ppat_prep ) ) ) + { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + + if ( pc2i->len_c2at >= 1 ) { + /*--------------------------------------------------------------*/ + /* case 02: restored: -O(+)=AB-NH2 original: -O-AB=NH2(+) */ + /* FixH: 0 0 0 1 */ + /* MobH: 0 2 0 1 */ + /* O = P, As, Sb, O, S, Se, F, Cl, Br, I; not taut. in InChI */ + /* N = N, O, S, Se, Te; has H; tautomeric or not tautomeric */ + /* Solution: move (+) from O(+) to NH2 */ + /*--------------------------------------------------------------*/ + int num_DB_O_Plus = 0, num_SB_NH = 0, iat; + short iat_DB_O_Plus[MAX_DIFF_FIXH], iat_SB_NH[MAX_DIFF_FIXH]; + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : NULL; + cur_success = 0; + num_zero_ret = 0; + for ( i = 0; i < pc2i->len_c2at; i ++ ) { + iat = pc2i->c2at[i].atomNumber; + if ( /* orig. InChI info: =NH2(+), =OH(+) */ + num_SB_NH < MAX_DIFF_FIXH && + (pc2i->c2at[i].nValElectr == 5 && pc2i->c2at[i].nPeriodNum == 1 || + pc2i->c2at[i].nValElectr == 6 ) /* N, O, S, Se, Te */ && + /*!pc2i->c2at[i].endptInChI &&*/ /* <=== relaxation */ + (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + pc2i->c2at[i].nFixHInChI>0 /*== 1 --modification#2*/ && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ + /* reversed structure info: */ + pc2i->c2at[i].nFixHRevrs == 0 && /* pc2i->c2at[i].nMobHRevrs == 0 &&*/ + pc2i->c2at[i].nAtChargeRevrs == 0 && at2[iat].num_H && + at2[iat].valence == at2[iat].chem_bonds_valence ) { + iat_SB_NH[num_SB_NH ++] = iat; + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ + iat = nCanon2AtnoRevrs[i]; + if ( /* in restored atom: charge=+1, no H, has double bond, P, As, O, S, Se, Te, F, Cl, Br, I */ + num_DB_O_Plus < MAX_DIFF_FIXH && + at2[iat].charge == 1 && !at2[iat].num_H && + at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && + (pVA[iat].cNumValenceElectrons == 6 || pVA[iat].cNumValenceElectrons == 7 || + pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber > 1) && + /* in orig.InChI: not an endpoint, has no H */ + !pStruct->endpoint[i] && + !(pStruct->fixed_H && pStruct->fixed_H[i]) && + !(nMobHInChI && nMobHInChI[i] ) && + /* has (+) edge */ + (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { + iat_DB_O_Plus[num_DB_O_Plus ++] = iat; + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + if ( num_try = inchi_min( num_DB_O_Plus, num_SB_NH ) ) { + /* detected; attempt to fix */ + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + delta = 1; +repeat_02_allow_NV: + for ( i = 0; i < num_SB_NH && cur_success < num_try; i ++ ) { + iat = iat_SB_NH[i]; + pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->forbidden |= forbidden_edge_mask; + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { + /* Removed charge from O(+) => nDeltaCharge == -1 */ + /* Flow change on pe (+)charge edge (atom NH2) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 02 */ + } + } else { + num_zero_ret += !ret; + pe->forbidden &= forbidden_edge_mask_inv; + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + } + if ( num_zero_ret == num_try && !bAllowedNFlowerEdges && NFlowerEdges.num_edges ) { + RemoveForbiddenEdgeMask( pBNS, &NFlowerEdges, forbidden_edge_mask ); + bAllowedNFlowerEdges = 1; + goto repeat_02_allow_NV; + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + bAllowedNFlowerEdges = 0; + } + CurrEdges.num_edges = 0; /* clear current edge list */ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + + if ( pc2i->len_c2at >= 1 && pc2i->nNumTgRevrs == 1 && + (pc2i->nNumEndpRevrs > pc2i->nNumEndpInChI || pc2i->nNumTgInChI > 1) /* ADP in Revrs */ ) { + /*--------------------------------------------------------------*/ + /* case 03: restored: -N(-)-AB=O original: -N=AB-O(-) */ + /* FixH: 0 0 0 -1 */ + /* MobH: 0 0 0 1 */ + /* O = O, S, Se; N = N; */ + /* restored atoms are tautomeric; original atoms are not taut. */ + /* restored struct has 1 t-group; original has less endpoints */ + /* and possibly >1 t-groups */ + /* Solution: move (-) from N(-) to =O */ + /* these atoms are tautomeric in restored structure */ + /*--------------------------------------------------------------*/ + int num_SB_N_Minus = 0, num_DB_O = 0, iat; + short iat_SB_N_Minus[MAX_DIFF_FIXH], iat_DB_O[MAX_DIFF_FIXH]; + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && + pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; + /* + S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; + */ + cur_success = 0; + for ( i = 0; i < pc2i->len_c2at; i ++ ) { + iat = pc2i->c2at[i].atomNumber; + if ( /* orig. InChI info: -O(-) */ + num_DB_O < MAX_DIFF_FIXH && + pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ && + !pc2i->c2at[i].endptInChI && + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + pc2i->c2at[i].nFixHInChI == -1 && pc2i->c2at[i].nMobHInChI == 1 && + /* reversed structure info: */ + pc2i->c2at[i].endptRevrs && + pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && + pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && + at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 ) { + iat_DB_O[num_DB_O ++] = iat; + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ + iat = nCanon2AtnoRevrs[i]; + if ( /* in restored atom N: charge=-1, no H, has no double bond, endpoint */ + num_SB_N_Minus < MAX_DIFF_FIXH && + at2[iat].charge == -1 && /*!at2[iat].num_H &&*/ + at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && + pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && + at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint && + /* in orig.InChI: not an endpoint, has no H */ + /* !pStruct->endpoint[i] && */ + /* + !(pStruct->fixed_H && pStruct->fixed_H[i]) && + !(nMobHInChI && nMobHInChI[i] ) && + */ + /* has (-) edge */ + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { + iat_SB_N_Minus[num_SB_N_Minus ++] = iat; + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + if ( num_try = inchi_min( num_SB_N_Minus, num_DB_O ) ) { + /* detected; attempt to fix */ + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + delta = 1; + for ( i = 0; i < num_SB_N_Minus && cur_success < num_try; i ++ ) { + iat = iat_SB_N_Minus[i]; + pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; /* 2006-03-03: changed from CPlusGroupEdge */ + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->forbidden |= forbidden_edge_mask; + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { + /* Added (-) charge to =O => nDeltaCharge == 1 */ + /* Flow change on pe (-)charge edge (atom -N(-)-) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 03 */ + } + } else { + pe->forbidden &= forbidden_edge_mask_inv; + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } + CurrEdges.num_edges = 0; /* clear current edge list */ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + + if ( pc2i->nNumTgRevrs == 1 && /* pc2i->nNumRemHInChI < 0 &&*/ + (pc2i->nNumEndpRevrs > pc2i->nNumEndpInChI || pc2i->nNumTgInChI > 1) /* ADP in Revrs */ ) { + /*--------------------------------------------------------------*/ + /* case 03a:restored: -N(-)-AB=O original: -N=AB-O(-) */ + /* FixH: 0 0 0 0 */ + /* MobH: 0 0 0 0 */ + /* O = O, S, Se; N = N; taut */ + /* restored atoms are tautomeric; original atom is; N may be. */ + /* restored struct has 1 t-group; original has less endpoints */ + /* and possibly >1 t-groups */ + /* Solution: move (-) from N(-) to =O */ + /* these atoms are tautomeric in restored structure */ + /*--------------------------------------------------------------*/ + int num_SB_N_Minus = 0, num_DB_O = 0, iat; + short iat_SB_N_Minus[MAX_DIFF_FIXH], iat_DB_O[MAX_DIFF_FIXH]; + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && + pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; + S_CHAR *pnMobHInChI = (pInChI[1] && pInChI[1]->nNum_H)? pInChI[1]->nNum_H : + (pInChI[0] && pInChI[0]->nNum_H)? pInChI[0]->nNum_H : NULL; + S_CHAR *pnFixHInChI = pStruct->fixed_H; + + cur_success = 0; + CurrEdges.num_edges = 0; + for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ + iat = nCanon2AtnoRevrs[i]; + if ( /* in restored atom N: charge=-1, no H, has no double bond, endpoint */ + num_SB_N_Minus < MAX_DIFF_FIXH && + at2[iat].charge == -1 && /*!at2[iat].num_H &&*/ + at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && + pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && + at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint && + /* in orig.InChI: may be an endpoint, has no H */ + /* has (-) edge */ + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { + iat_SB_N_Minus[num_SB_N_Minus ++] = iat; + } else + if ( num_DB_O < MAX_DIFF_FIXH && + at2[iat].charge == 0 && /*!at2[iat].num_H &&*/ + at2[iat].valence+1 == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && + pVA[iat].cNumValenceElectrons == 6 && + at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint && /* endpoint in Reconstructed */ + (pStruct->endpoint[i] || /* endpoint or H(+) acceptor in original */ + pnMobHInChI && pnMobHInChI[i] == 1 && pnFixHInChI && pnFixHInChI[i] == -1 ) && + /* has (-) edge */ + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { + iat_DB_O[num_DB_O ++] = iat; + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + if ( num_try = inchi_min( num_SB_N_Minus, num_DB_O ) ) { + /* detected; attempt to fix */ + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + /* allow charge transfer to all found =O */ + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + delta = 1; + for ( i = 0; i < num_SB_N_Minus && cur_success < num_try; i ++ ) { + iat = iat_SB_N_Minus[i]; + pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->forbidden |= forbidden_edge_mask; + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { + /* Added (-) charge to =O => nDeltaCharge == 1 */ + /* Flow change on pe (-)charge edge (atom -N(-)-) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 03a */ + } + } else { + pe->forbidden &= forbidden_edge_mask_inv; + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } + CurrEdges.num_edges = 0; /* clear current edge list */ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + + if ( pc2i->len_c2at >= 1 && pc2i->nNumTgInChI == 1 && /* ADP in InChI */ + (pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1) ) { + /*--------------------------------------------------------------*/ + /* case 04: restored: OH(+)=AB-O- OH- orig. HO-AB=O(+)- OH- */ + /* FixH: 1 0 0 1 0 1 */ + /* MobH: 0 0 1 0 0 0 */ + /* non-taut. taut taut */ + /* ADP: one t-group or more endpoints */ + /* O(+) = N, P, As, As, O, S, Se; OH = N, O, S, Se, Te */ + /* Solution: move (+) from O(+) to NH2 */ + /*--------------------------------------------------------------*/ + int num_SB_Neutr = 0, num_DB_Charged = 0, iat; + short iat_SB_Neutr[MAX_DIFF_FIXH], iat_DB_Charged[MAX_DIFF_FIXH]; + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; + cur_success = 0; + for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ + iat = nCanon2AtnoRevrs[i]; + if ( /* in restored atom: charge=+1, has H, has double bond, N, O, S, Se, Te */ + num_DB_Charged < MAX_DIFF_FIXH && + at2[iat].charge == 1 && at2[iat].num_H && + at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && + (pVA[iat].cNumValenceElectrons == 6 || + pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1) && + /* in orig.InChI: an endpoint, has fixed-H */ + pStruct->endpoint[i] && + (pStruct->fixed_H && pStruct->fixed_H[i]) && + /*!(nMobHInChI && nMobHInChI[i] ) &&*/ + /* has (+) edge */ + (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { + + iat_DB_Charged[num_DB_Charged ++] = iat; + + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } else + if ( /* in restored atom: charge=0, has no H, has no double bond, N, P, O, S, Se, Te */ + num_SB_Neutr < MAX_DIFF_FIXH && + at2[iat].charge == 0 && !at2[iat].num_H && + at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && + (pVA[iat].cNumValenceElectrons == 6 || + pVA[iat].cNumValenceElectrons == 5 ) && + /* in orig.InChI: an endpoint, has fixed-H */ + /* pStruct->endpoint[i] && */ + !(pStruct->fixed_H && pStruct->fixed_H[i]) && + !(nMobHInChI && nMobHInChI[i] ) && + /* has (+) edge */ + (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && + 0 == pBNS->edge[e].forbidden ) { + + iat_SB_Neutr[num_SB_Neutr ++] = iat; + + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + if ( num_try = inchi_min( num_SB_Neutr, num_DB_Charged ) ) { + /* detected; attempt to fix */ + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + delta = 1; + for ( i = 0; i < num_SB_Neutr && cur_success < num_try; i ++ ) { + iat = iat_SB_Neutr[i]; + pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->forbidden |= forbidden_edge_mask; + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { + /* Removed charge from O(+) => nDeltaCharge == -1 */ + /* Flow change on pe (+)charge edge (atom NH2) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 04 */ + } + } else { + pe->forbidden &= forbidden_edge_mask_inv; + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } + CurrEdges.num_edges = 0; /* clear current edge list */ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + + if ( pc2i->len_c2at > 1 ) { + /*--------------------------------------------------------------*/ + /* case 05: restored: O=AB-NH original:(-)O-AB=NH(+) */ + /* FixH: 0 0 -1 1 */ + /* MobH: 0 1 1 0 */ + /* O = O, S, Se; N = N, O, S, Se, Te; all atoms not tautomeric */ + /* Solution: Separate charges */ + /*--------------------------------------------------------------*/ + int num_DB_O = 0, num_SB_NH = 0, iat; + short iat_DB_O[MAX_DIFF_FIXH], iat_SB_NH[MAX_DIFF_FIXH]; + cur_success = 0; + for ( i = 0; i < pc2i->len_c2at; i ++ ) { + iat = pc2i->c2at[i].atomNumber; + if ( /* orig. InChI info: =NH2(+), =OH(+) */ + num_SB_NH < MAX_DIFF_FIXH && + (pc2i->c2at[i].nValElectr == 5 && pc2i->c2at[i].nPeriodNum == 1 || + pc2i->c2at[i].nValElectr == 6 ) /* N, O, S, Se, Te */ && + !pc2i->c2at[i].endptInChI && + (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + pc2i->c2at[i].nFixHInChI == 1 && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ + /* reversed structure info: */ + pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs && + pc2i->c2at[i].nAtChargeRevrs == 0 && at2[iat].num_H && + !pc2i->c2at[i].endptRevrs && + at2[iat].valence == at2[iat].chem_bonds_valence ) { + iat_SB_NH[num_SB_NH ++] = iat; + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } else + if ( /* orig. InChI info: -O(-) */ + num_DB_O < MAX_DIFF_FIXH && + (pc2i->c2at[i].nValElectr == 6 ) /* O, S, Se, Te */ && + !pc2i->c2at[i].endptInChI && + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + pc2i->c2at[i].nFixHInChI == -1 && pc2i->c2at[i].nMobHInChI == 1 && + /* reversed structure info: */ + pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && + pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && + !pc2i->c2at[i].endptRevrs && + at2[iat].valence + 1 == at2[iat].chem_bonds_valence ) { + iat_DB_O[num_DB_O ++] = iat; + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + if ( num_try = inchi_min( num_DB_O, num_SB_NH ) ) { + /* detected; attempt to fix */ + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + delta = 1; + for ( i = 0; i < num_SB_NH && cur_success < num_try; i ++ ) { + iat = iat_SB_NH[i]; + pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->forbidden |= forbidden_edge_mask; + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { + /* Added charge to =O => nDeltaCharge == 1 */ + /* Flow change on pe (+)charge edge (atom NH2) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 05 */ + } + } else { + pe->forbidden &= forbidden_edge_mask_inv; + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } + CurrEdges.num_edges = 0; /* clear current edge list */ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + + if ( pStruct->fixed_H && pStruct->endpoint && pc2i->nChargeFixHInChI > 0 && pc2i->nChargeFixHInChI > pc2i->nChargeMobHInChI ) { + /*----------------------------------------------------------*/ + /* case 06c: restored -NH- or -NH(+) orig: -NH- */ + /* Fixed-H 1 1 0 */ + /* Mobile-H 0 0 1 */ + /* not tautomeric not tautomeric */ + /* has adjacent (+) */ + /* charges */ + /* Solution: move (+) charges to the -NH- unless it already*/ + /* N = N, O, S, Se, Te */ + /* has (+) charge blocked by adjacent (+) */ + /*----------------------------------------------------------*/ + int iat; + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + /* + inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && + pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; + inp_ATOM *atfMobile_H_Revrs = pStruct->pOne_norm_data[TAUT_YES] && + pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds? + pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds : NULL; + S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : NULL; + */ + EDGE_LIST CurChargeEdges; + EdgeIndex e2; + cur_success = 0; + AllocEdgeList( &CurChargeEdges, EDGE_LIST_CLEAR ); + CurrEdges.num_edges = 0; + for ( i = 0; i < pc2i->len_c2at; i ++ ) { + /* atoms -NH- from which H(+) were removed by the Normalization in orig. InChI */ + iat = pc2i->c2at[i].atomNumber; + if ( (pc2i->c2at[i].nValElectr == 6 || + pc2i->c2at[i].nValElectr == 5 && pc2i->c2at[i].nPeriodNum == 1) && + !pc2i->c2at[i].endptInChI && + (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { + if ( /* orig. InChI info: -NH- */ + pc2i->c2at[i].nFixHInChI == 1 && pc2i->c2at[i].nMobHInChI == 0 && + /* reversed structure info: */ + pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 1 && /* was not removed */ + /*pc2i->c2at[i].nAtChargeRevrs == 0 &&*/ at2[iat].num_H && /* at2 is Fixed-H */ + at2[iat].valence == at2[iat].chem_bonds_valence ) { + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + } + for ( i = 0; i < pStruct->num_atoms; i ++ ) { + /* find adjacent charged atoms */ + iat = nCanon2AtnoRevrs[i]; + if ( pStruct->endpoint[i] || at2[iat].charge != 1 || at2[iat].radical || pVA[iat].cMetal ) { + continue; + } + if ( 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && !pBNS->edge[e].flow && pVA[iat].cNumValenceElectrons >= 5 ) { + /* positively charged atom */ + for ( j = 0; j < at2[iat].valence; j ++ ) { + if ( at2[k=(int)at2[iat].neighbor[j]].charge == 1 && !pVA[k].cMetal && + 0 <= (e2=pVA[k].nCPlusGroupEdge-1) && !pBNS->edge[e2].forbidden && !pBNS->edge[e2].flow) { + if ( 0 > FindInEdgeList( &CurrEdges, e ) && + 0 > FindInEdgeList( &CurChargeEdges, e ) && + ( ret = AddToEdgeList( &CurChargeEdges, e, INC_ADD_EDGE ) ) ) { + goto exit_case_06c; + } + if ( 0 > FindInEdgeList( &CurrEdges, e2 ) && + 0 > FindInEdgeList( &CurChargeEdges, e2 ) && + ( ret = AddToEdgeList( &CurChargeEdges, e2, INC_ADD_EDGE ) ) ) { + goto exit_case_06c; + } + } + } + } + } + if ( num_try = inchi_min( CurrEdges.num_edges, CurChargeEdges.num_edges ) ) { + /* detected; attempt to fix */ + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurChargeEdges, forbidden_edge_mask ); + delta = 1; + for ( i = 0; i < CurrEdges.num_edges && cur_success < num_try; i ++ ) { + e = CurrEdges.pnEdges[i]; + pe = pBNS->edge + e; /* (+)charge edge of -NH- or -OH */ + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->flow -= delta; /* add (+) to -NHm */ + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { + /* Removed (+)charge from -NH- => nDeltaCharge == -1 */ + /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 06c */ + } + } else { + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } +exit_case_06c: + CurrEdges.num_edges = 0; /* clear current edge list */ + AllocEdgeList( &CurChargeEdges, EDGE_LIST_FREE ); + if ( ret < 0 ) { + goto exit_function; + } + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + + if ( pc2i->len_c2at >= 2 ) { + /*------------------------------------------------------------*/ + /* case 06d: restored: XH(+)=-AB-NH orig.: XH-=AB=NH(+) */ + /* FixH: 1 1 0 0 1 1 */ + /* MobH: 0 taut 1 1 taut 0 */ + /* */ + /* */ + /* N = N, O, S, Se; atoms N are not tautomeric in orig InChI */ + /* X = N, O, S, Se, Te, F, Cl, Br, I; atom X is non-taut */ + /* Solution: move (+) from X to NH */ + /*------------------------------------------------------------*/ + int iat; + /* + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && + pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; + inp_ATOM *atfMobile_H_Revrs = pStruct->pOne_norm_data[TAUT_YES] && + pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds? + pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds : + pStruct->pOne_norm_data[TAUT_NON]->at; + S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; + */ + EDGE_LIST CurChargeEdges; + cur_success = 0; + AllocEdgeList( &CurChargeEdges, EDGE_LIST_CLEAR ); + CurrEdges.num_edges = 0; + for ( i = 0; i < pc2i->len_c2at; i ++ ) { + iat = pc2i->c2at[i].atomNumber; + /* XH(+) */ + if ( /* reconstructed: non-taut and (+) */ + (pc2i->c2at[i].nMobHRevrs+1 == pc2i->c2at[i].nFixHRevrs && + pc2i->c2at[i].nFixHRevrs > 0 && !pc2i->c2at[i].endptRevrs && + pc2i->c2at[i].nAtChargeRevrs == 1 && + /* original InChI: non-taut & has H or an endpoint, has Fixed H */ + (!pc2i->c2at[i].nFixHInChI && pc2i->c2at[i].nMobHInChI == pc2i->c2at[i].nFixHRevrs || + pc2i->c2at[i].nFixHInChI == pc2i->c2at[i].nFixHRevrs && pc2i->c2at[i].endptInChI )) && + 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && !pBNS->edge[e].flow) { + + if (ret = AddToEdgeList( &CurChargeEdges, e, INC_ADD_EDGE )) { + goto exit_case_06d; + } + } else + /* -NH- */ + if ( /* original InChI: has H and is not an endpoint */ + (pc2i->c2at[i].nMobHInChI+1 == pc2i->c2at[i].nFixHInChI && + pc2i->c2at[i].nFixHInChI > 0 && !pc2i->c2at[i].endptInChI && + pc2i->c2at[i].nAtChargeRevrs == 0 && + /* reconstructed InChI: non-taut & has H or an endpoint, has Fixed H */ + (!pc2i->c2at[i].nFixHRevrs && pc2i->c2at[i].nMobHRevrs == pc2i->c2at[i].nFixHInChI || + pc2i->c2at[i].nFixHRevrs == pc2i->c2at[i].nFixHInChI && pc2i->c2at[i].endptRevrs )) && + 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && + pBNS->edge[e].flow) { + + if (ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE )) { + goto exit_case_06d; + } + } + } + if ( num_try = inchi_min( CurrEdges.num_edges, CurChargeEdges.num_edges ) ) { + /* detected; attempt to fix */ + int bSFlowerEdgesMayBeForbidden = (SFlowerEdges.num_edges > 0); + int bSFlowerEdgesIsForbidden; + for ( bSFlowerEdgesIsForbidden = bSFlowerEdgesMayBeForbidden; + 0 <= bSFlowerEdgesIsForbidden; bSFlowerEdgesIsForbidden -- ) { + if ( bSFlowerEdgesIsForbidden ) { + /* on the 1st pass disallow -S(+)= => =S=, allow only -S(+)= => -S- */ + SetForbiddenEdgeMask( pBNS, &SFlowerEdges, forbidden_edge_mask ); + } + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurChargeEdges, forbidden_edge_mask ); + delta = 1; + for ( i = 0; i < CurrEdges.num_edges && cur_success < num_try; i ++ ) { + e = CurrEdges.pnEdges[i]; + pe = pBNS->edge + e; /* (+)charge edge of -NH- or -OH */ + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->flow -= delta; /* add (+) to -NHm */ + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { + /* Removed (+)charge from -NH- => nDeltaCharge == -1 */ + /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 06d */ + } + } else { + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &SFlowerEdges, forbidden_edge_mask ); + } + } +exit_case_06d: + CurrEdges.num_edges = 0; /* clear current edge list */ + AllocEdgeList( &CurChargeEdges, EDGE_LIST_FREE ); + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + + + if ( pc2i->len_c2at >= 2 ) { + /*--------------------------------------------------------*/ + /* case 06: restored: NHn(+)=AB-NHm orig.: NHn-AB=NHm(+) */ + /* FixH: 1 0 0 1 */ + /* MobH: n-1 m n m-1 */ + /* N = N, O, S, Se; atoms N are not tautomeric */ + /* Solution: move (+) from NHn(+) to NHn */ + /*--------------------------------------------------------*/ + int num_DB_NHn_Plus = 0, num_SB_NHm_Neutr = 0, iat; + short iat_DB_NHn_Plus[MAX_DIFF_FIXH], iat_SB_NHm_Neutr[MAX_DIFF_FIXH]; + cur_success = 0; + for ( i = 0; i < pc2i->len_c2at; i ++ ) { + iat = pc2i->c2at[i].atomNumber; + if ( (pc2i->c2at[i].nValElectr == 6 || + pc2i->c2at[i].nValElectr == 5 && pc2i->c2at[i].nPeriodNum == 1) && + !pc2i->c2at[i].endptInChI && + (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { + if ( /* orig. InChI info: NHm */ + num_SB_NHm_Neutr < MAX_DIFF_FIXH && + pc2i->c2at[i].nFixHInChI == 1 && /*pc2i->c2at[i].nMobHInChI == 0 &&*/ + /* reversed structure info: */ + pc2i->c2at[i].nFixHRevrs == 0 && /*pc2i->c2at[i].nMobHRevrs == 1 &&*/ + pc2i->c2at[i].nAtChargeRevrs == 0 && at2[iat].num_H && /* at2 is Fixed-H */ + at2[iat].valence == at2[iat].chem_bonds_valence ) { + iat_SB_NHm_Neutr[num_SB_NHm_Neutr ++] = iat; + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } else + if ( /* orig. InChI info: */ + num_DB_NHn_Plus < MAX_DIFF_FIXH && + pc2i->c2at[i].nFixHInChI == 0 && /*pc2i->c2at[i].nMobHInChI &&*/ + /* reversed structure info: */ + pc2i->c2at[i].nFixHRevrs == 1 && /*pc2i->c2at[i].nMobHRevrs == 0 &&*/ + pc2i->c2at[i].nAtChargeRevrs == 1 && at2[iat].num_H && + at2[iat].valence < at2[iat].chem_bonds_valence ) { + iat_DB_NHn_Plus[num_DB_NHn_Plus ++] = iat; + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + } + if ( num_try = inchi_min( num_SB_NHm_Neutr, num_DB_NHn_Plus ) ) { + /* detected; attempt to fix */ + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + delta = 1; + for ( i = 0; i < num_SB_NHm_Neutr && cur_success < num_try; i ++ ) { + iat = iat_SB_NHm_Neutr[i]; + pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->forbidden |= forbidden_edge_mask; + pe->flow -= delta; /* add (+) to -NHm */ + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { + /* Removed (+)charge from -NHn => nDeltaCharge == -1 */ + /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 06 */ + } + } else { + pe->forbidden &= forbidden_edge_mask_inv; + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } + CurrEdges.num_edges = 0; /* clear current edge list */ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + + if ( (pc2i->nNumTgInChI > pc2i->nNumTgRevrs && pc2i->nNumTgRevrs == 1 || + pc2i->nNumEndpInChI < pc2i->nNumEndpRevrs ) && + pStruct->nNumRemovedProtonsMobHInChI == pStruct->One_ti.tni.nNumRemovedProtons && + pStruct->fixed_H && pStruct->endpoint && pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds ) { + /*----------------------------------------------------------*/ + /* case 06a: restored: N'(+)=-AB-NH orig.: N'-=AB=NH(+) */ + /* FixH: 0 1 0 1 */ + /* MobH: 0 0 0 0 */ + /* single t-group multiple t-groups */ + /* N = N, O, S, Se; atoms N are not tautomeric */ + /* N' = N atom N' is not tautomeric */ + /* Solution: move (+) from N' to NH */ + /*----------------------------------------------------------*/ + int iat; + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + /* + inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && + pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; + */ + inp_ATOM *atfMobile_H_Revrs = pStruct->pOne_norm_data[TAUT_YES] && + pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds? + pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds : NULL; + S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; + EDGE_LIST CurChargeEdges; + cur_success = 0; + AllocEdgeList( &CurChargeEdges, EDGE_LIST_CLEAR ); + CurrEdges.num_edges = 0; + for ( i = 0; i < pStruct->num_atoms; i ++ ) { + iat = nCanon2AtnoRevrs[i]; + if ( pStruct->endpoint[i] ) { + continue; + } + /* -NH-, -OH */ + if ( pStruct->fixed_H[i] && !nMobHInChI[i] && + at2[iat].charge == 0 && at2[iat].radical == 0 && + 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && + (ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ))) { + goto exit_case_06a; + } else + /* >N(+)= */ + if ( at2[iat].charge == 1 && !at2[iat].num_H && + pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && + atfMobile_H_Revrs && atfMobile_H_Revrs[iat].charge == 0 && + 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && !pBNS->edge[e].flow && + (ret = AddToEdgeList( &CurChargeEdges, e, INC_ADD_EDGE ))) { + goto exit_case_06a; + } + } + if ( num_try = inchi_min( CurrEdges.num_edges, CurChargeEdges.num_edges ) ) { + /* detected; attempt to fix */ + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurChargeEdges, forbidden_edge_mask ); + delta = 1; + for ( i = 0; i < CurrEdges.num_edges && cur_success < num_try; i ++ ) { + e = CurrEdges.pnEdges[i]; + pe = pBNS->edge + e; /* (+)charge edge of -NH- or -OH */ + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->flow -= delta; /* add (+) to -NHm */ + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { + /* Removed (+)charge from -NH- => nDeltaCharge == -1 */ + /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 06a */ + } + } else { + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } +exit_case_06a: + CurrEdges.num_edges = 0; /* clear current edge list */ + AllocEdgeList( &CurChargeEdges, EDGE_LIST_FREE ); + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + if ( (pc2i->nNumTgInChI > pc2i->nNumTgRevrs && pc2i->nNumTgRevrs == 1 || + pc2i->nNumEndpInChI < pc2i->nNumEndpRevrs ) && + (pStruct->nNumRemovedProtonsMobHInChI == pStruct->One_ti.tni.nNumRemovedProtons || + pStruct->nNumRemovedProtonsMobHInChI > pStruct->One_ti.tni.nNumRemovedProtons ) && + pStruct->fixed_H && pStruct->endpoint && pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds ) { + /*----------------------------------------------------------*/ + /* case 06b: restored: X(+)=-AB-NH orig.: X-=AB=NH(+) */ + /* FixH: 0 1 1 0 1 */ + /* MobH: 0 0 t 0 0 */ + /* single t-group multiple t-groups */ + /* or no t-groupd */ + /* N = N, O, S, Se; atoms N are not tautomeric */ + /* X = O, S, Se, Te, F, Cl, Br, I; atom X is not tautomeric*/ + /* Solution: move (+) from X to NH */ + /*----------------------------------------------------------*/ + int iat; + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + /* + inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && + pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; + */ + inp_ATOM *atfMobile_H_Revrs = pStruct->pOne_norm_data[TAUT_YES] && + pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds? + pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds : NULL; + S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; + EDGE_LIST CurChargeEdges; + cur_success = 0; + AllocEdgeList( &CurChargeEdges, EDGE_LIST_CLEAR ); + CurrEdges.num_edges = 0; + for ( i = 0; i < pStruct->num_atoms; i ++ ) { + iat = nCanon2AtnoRevrs[i]; + if ( pStruct->endpoint[i] ) { + continue; + } + /* -NH-, -OH */ + if ( pStruct->fixed_H[i] && !nMobHInChI[i] && + at2[iat].charge == 0 && at2[iat].radical == 0 && + 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && + (ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ))) { + goto exit_case_06b; + } else + /* X(+)= */ + if ( at2[iat].charge == 1 && !at2[iat].num_H && + (pVA[iat].cNumValenceElectrons == 6 || pVA[iat].cPeriodicRowNumber == 7) && + atfMobile_H_Revrs && atfMobile_H_Revrs[iat].charge == 1 && + 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && !pBNS->edge[e].flow && + (ret = AddToEdgeList( &CurChargeEdges, e, INC_ADD_EDGE ))) { + goto exit_case_06b; + } + } + if ( num_try = inchi_min( CurrEdges.num_edges, CurChargeEdges.num_edges ) ) { + /* detected; attempt to fix */ + int bSFlowerEdgesMayBeForbidden = (SFlowerEdges.num_edges > 0); + int bSFlowerEdgesIsForbidden; + for ( bSFlowerEdgesIsForbidden = bSFlowerEdgesMayBeForbidden; + 0 <= bSFlowerEdgesIsForbidden; bSFlowerEdgesIsForbidden -- ) { + if ( bSFlowerEdgesIsForbidden ) { + /* on the 1st pass disallow -S(+)= => =S=, allow only -S(+)= => -S- */ + SetForbiddenEdgeMask( pBNS, &SFlowerEdges, forbidden_edge_mask ); + } + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurChargeEdges, forbidden_edge_mask ); + delta = 1; + for ( i = 0; i < CurrEdges.num_edges && cur_success < num_try; i ++ ) { + e = CurrEdges.pnEdges[i]; + pe = pBNS->edge + e; /* (+)charge edge of -NH- or -OH */ + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->flow -= delta; /* add (+) to -NHm */ + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { + /* Removed (+)charge from -NH- => nDeltaCharge == -1 */ + /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 06b */ + } + } else { + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &SFlowerEdges, forbidden_edge_mask ); + } + } +exit_case_06b: + CurrEdges.num_edges = 0; /* clear current edge list */ + AllocEdgeList( &CurChargeEdges, EDGE_LIST_FREE ); + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + + + + if ( pc2i->nNumTgInChI > 1 && + (pStruct->nNumRemovedProtonsMobHInChI > 0 || pStruct->ti.tni.nNumRemovedProtons > 0 ) && + pStruct->fixed_H && pStruct->endpoint && + pStruct->pOne_norm_data[TAUT_YES] && pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds ) { + /*----------------------------------------------------------*/ + /* case 06e:restored: XHn(+)=-AB-YHm orig.: XHn-=AB=YHm(+) */ + /* FixH: 1 0 1 1 */ + /* MobH: 0 1 t t */ + /* non-taut atoms multiple t-groups */ + /* */ + /* 1. orig. t-group has more H on its endpoints counted */ + /* in atf and has no (+) on endpoint that has H */ + /* 2. orig. t-group has less H on its endpoints counted */ + /* in atf and has (+) on endpoint that has H */ + /* in reconstructed struct and less H in atf */ + /* Solution: move (+) from (2) to atom in (1) that has H */ + /* */ + /* tg1 reconstr: XHn and more H than in orig t-group */ + /* atf: XHn */ + /* tg2 reconstr: XHm(+) and less H than in */ + /* atf: XH(m-1) orig in t-group */ + /* */ + /* N = N, O, S, Se; atoms N are not tautomeric */ + /* X = O, S, Se, Te, F, Cl, Br, I; atom X is not tautomeric*/ + /* Solution: move (+) from X to NH */ + /*----------------------------------------------------------*/ + + int iat, nNumWrongTg, jjoffs, jj, nNum2RemovePlus, nNum2AddPlus, nNum2MovePlus; + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + /* + inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && + pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; + */ + inp_ATOM *atfMobile_H_Revrs = pStruct->pOne_norm_data[TAUT_YES] && + pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds? + pStruct->pOne_norm_data[TAUT_YES]->at_fixed_bonds : + pStruct->pOne_norm_data[TAUT_YES] && + pStruct->pOne_norm_data[TAUT_YES]->at? + pStruct->pOne_norm_data[TAUT_YES]->at : NULL; + /* + S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; + */ + EDGE_LIST CurChargeEdges /* source of (+)*/, EndpList; + TgDiffHChgFH tdhc[MAX_DIFF_FIXH]; + BNS_VERTEX *pv1n, *pv2n; + BNS_EDGE *pe1n, *pe2n; + Vertex v1n, v2n; + + cur_success = 0; + AllocEdgeList( &CurChargeEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &EndpList, EDGE_LIST_CLEAR ); + CurrEdges.num_edges = 0; /* receptors of (+) */ + if ( !atfMobile_H_Revrs ) { + goto exit_case_06e; + } + nNumWrongTg = FillTgDiffHChgFH( tdhc, MAX_DIFF_FIXH, at2, atfMobile_H_Revrs, + nCanon2AtnoRevrs, pVA, &pStruct->ti, &EndpList ); + if ( nNumWrongTg < 1 ) { + goto exit_case_06e; /* for now only transfer (+) from one Mobile-H group to another */ + } + nNum2RemovePlus = nNum2AddPlus = nNum2MovePlus = 0; + for ( i = 0; i < nNumWrongTg; i ++ ) { + /* detect t-group that has extra (+) on H */ + if ( tdhc[i].nNumHInchi > tdhc[i].nNumHNorml && + tdhc[i].nNumPRevrs > tdhc[i].nNumPNorml && tdhc[i].n[fNumRPosChgH] ) { + /* count how many (+) to remove */ + /* store XH(+) atom numbers */ + int nNumNeeded = inchi_min( tdhc[i].nNumHInchi-tdhc[i].nNumHNorml, tdhc[i].n[fNumRPosChgH]); + nNum2RemovePlus += nNumNeeded; + jjoffs = tdhc[i].i[ fNumRPosChgH ]; + for ( jj = 0; jj < tdhc[i].n[fNumRPosChgH]; jj ++ ) { + iat = EndpList.pnEdges[ jjoffs + jj ]; + e = pVA[iat].nCPlusGroupEdge-1; + if ( ret = AddToEdgeList( &CurChargeEdges, e, INC_ADD_EDGE ) ) { + goto exit_case_06e; + } + } + } else + /* detect t-group that needs (+) on XH to reduce number of H */ + if ( tdhc[i].nNumHInchi < tdhc[i].nNumHNorml && tdhc[i].n[fNumRNeutrlH] ) { + /* store XH atom numbers */ + int nNumNeeded = inchi_min( tdhc[i].nNumHNorml-tdhc[i].nNumHInchi, tdhc[i].n[fNumRNeutrlH]); + nNum2AddPlus += nNumNeeded; + jjoffs = tdhc[i].i[ fNumRNeutrlH ]; + for ( jj = 0; jj < tdhc[i].n[fNumRNeutrlH]; jj ++ ) { + iat = EndpList.pnEdges[ jjoffs + jj ]; + e = pVA[iat].nCPlusGroupEdge-1; + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_case_06e; + } + } + } + } + nNum2MovePlus = inchi_min( nNum2RemovePlus, nNum2AddPlus ); + if ( CurrEdges.num_edges > 0 && CurChargeEdges.num_edges > 0 ) { + for ( i = 0; 0 < nNum2MovePlus && i < nNumWrongTg; i ++ ) { + /* detect t-group that has extra (+) on H */ + if ( tdhc[i].nNumHInchi > tdhc[i].nNumHNorml && + tdhc[i].nNumPRevrs > tdhc[i].nNumPNorml && tdhc[i].n[fNumRPosChgH] ) { + int nNum2Remove = tdhc[i].nNumHInchi - tdhc[i].nNumHNorml; + if ( nNum2Remove < tdhc[i].n[fNumRPosChgH] ) { + nNum2Remove = tdhc[i].n[fNumRPosChgH]; + } + /* store XH(+) atom numbers */ + jjoffs = tdhc[i].i[ fNumRPosChgH ]; + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + for ( jj = 0; 0 < nNum2MovePlus && 0 < nNum2Remove && jj < tdhc[i].n[fNumRPosChgH]; jj ++ ) { + iat = EndpList.pnEdges[ jjoffs + jj ]; + e = pVA[iat].nCPlusGroupEdge-1; + pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; + if ( pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + for ( j = pv1->num_adj_edges-1; 0 <= j; j -- ) { + pe1n = pBNS->edge + pv1->iedge[j]; + if ( pe1n->flow && !pe1n->forbidden ) { + pv1n = pBNS->vert + (v1n = pe1n->neighbor12 ^ v1); + break; + } + } + if ( j < 0 ) + continue; /* not found */ + + for ( j = pv2->num_adj_edges-2; 0 <= j; j -- ) { + pe2n = pBNS->edge + pv2->iedge[j]; + if ( pe2n->flow && !pe2n->forbidden ) { + pv2n = pBNS->vert + (v2n = pe2n->neighbor12 ^ v2); + break; + } + } + if ( j < 0 ) + continue; /* not found */ + delta = 1; + pe->flow += delta; + pe1n->flow -= delta; + pe2n->flow -= delta; + pv1n->st_edge.flow -= delta; + pv2n->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1n && vPathStart == v2n || + vPathEnd == v2n && vPathStart == v1n) && + (nDeltaCharge == 0 || nDeltaCharge == 1) ) { + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + nNum2Remove --; + nNum2MovePlus --; + cur_success ++; /* 06e */ + } + } else { + pe->flow -= delta; + pe1n->flow += delta; + pe2n->flow += delta; + pv1n->st_edge.flow += delta; + pv2n->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_case_06e; + } + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } + } + } +exit_case_06e: + CurrEdges.num_edges = 0; /* clear current edge list */ + AllocEdgeList( &CurChargeEdges, EDGE_LIST_FREE ); + AllocEdgeList( &EndpList, EDGE_LIST_FREE ); + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + + + + if ( pc2i->len_c2at >= 1 ) { + /*--------------------------------------------------------------*/ + /* case 07: restored: O(-)-AB=O original: O=AB-O(-) */ + /* FixH: 0 0 0 -1 */ + /* MobH: 0 0 0 1 */ + /* taut (non-taut) (taut) non-taut */ + /* taut (taut) (non-taut) non-taut */ + /* O = O, S, Se, Te */ + /* Solution: move (-) from O(-)-AB to AB=O */ + /*--------------------------------------------------------------*/ + int num_SB_O_Minus = 0, num_DB_O_Neutr = 0, iat; + short iat_SB_O_Minus[MAX_DIFF_FIXH], iat_DB_O_Neutr[MAX_DIFF_FIXH]; + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && + pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; + S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; + cur_success = 0; + for ( i = 0; i < pc2i->len_c2at; i ++ ) { + iat = pc2i->c2at[i].atomNumber; + if ( /* orig. InChI info: -O(-), non-taut */ + num_DB_O_Neutr < MAX_DIFF_FIXH && + pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ && + !pc2i->c2at[i].endptInChI && + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + pc2i->c2at[i].nFixHInChI == -1 && pc2i->c2at[i].nMobHInChI == 1 && + /* reversed structure info: */ + pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && + pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && + at2[iat].valence < at2[iat].chem_bonds_valence ) { + iat_DB_O_Neutr[num_DB_O_Neutr ++] = iat; + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ + iat = nCanon2AtnoRevrs[i]; + if ( /* in restored atom: charge=-1, no H, has single bond, O, S, Se, Te */ + num_SB_O_Minus < MAX_DIFF_FIXH && + at2[iat].charge == -1 && !at2[iat].num_H && + at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && + pVA[iat].cNumValenceElectrons == 6 && + at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint && + /* in orig.InChI: not an endpoint, has no H */ + /*pStruct->endpoint[i] && -- modificatuion#1 */ + !(pStruct->fixed_H && pStruct->fixed_H[i]) && + !(nMobHInChI && nMobHInChI[i] ) && + /* has (-) edge */ + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { + iat_SB_O_Minus[num_SB_O_Minus ++] = iat; + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + if ( num_try = inchi_min( num_SB_O_Minus, num_DB_O_Neutr ) ) { + /* detected; attempt to fix */ + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + delta = 1; + for ( i = 0; i < num_SB_O_Minus && cur_success < num_try; i ++ ) { + iat = iat_SB_O_Minus[i]; + pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->forbidden |= forbidden_edge_mask; + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { + /* Moved (-) charge to AB=O => nDeltaCharge == 1 */ + /* Flow change on pe (-)charge edge (O(-)-AB) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 07 */ + } + } else { + pe->forbidden &= forbidden_edge_mask_inv; + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } + CurrEdges.num_edges = 0; /* clear current edge list */ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + + if ( pc2i->len_c2at >= 1 ) { + /*--------------------------------------------------------------*/ + /* case 07a: restored: O(-)-N(V)B=O original: O=N(V)B-O(-) */ + /* FixH: 0 0 0 -1 */ + /* MobH: 0 0 0 1 */ + /* non-taut (non-taut) non-taut non-taut */ + /* non-taut (taut) non-taut non-taut */ + /* O = O, S, Se, Te */ + /* Solution: move (-) from O(-)-AB to AB=O */ + /*--------------------------------------------------------------*/ + int num_SB_O_Minus = 0, num_DB_O_Neutr = 0, iat, iN; + short iat_SB_O_Minus[MAX_DIFF_FIXH], iat_DB_O_Neutr[MAX_DIFF_FIXH]; + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; + cur_success = 0; + for ( i = 0; i < pc2i->len_c2at; i ++ ) { + iat = pc2i->c2at[i].atomNumber; + if ( /* orig. InChI info: -O(-), non-taut */ + num_DB_O_Neutr < MAX_DIFF_FIXH && + pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ && + !pc2i->c2at[i].endptInChI && + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + pc2i->c2at[i].nFixHInChI == -1 && pc2i->c2at[i].nMobHInChI == 1 && + /* reversed structure info: */ + pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && + pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && + at2[iat].valence < at2[iat].chem_bonds_valence ) { + iat_DB_O_Neutr[num_DB_O_Neutr ++] = iat; + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ + iat = nCanon2AtnoRevrs[i]; + if ( /* in restored atom: charge=-1, no H, has single bond, O, S, Se, Te */ + num_SB_O_Minus < MAX_DIFF_FIXH && + at2[iat].charge == -1 && !at2[iat].num_H && + at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && + pVA[iat].cNumValenceElectrons == 6 && + /*at_Mobile_H_Revrs && !at_Mobile_H_Revrs[iat].endpoint &&*/ + /* in orig.InChI: not an endpoint, has no H */ + !pStruct->endpoint[i] && + !(pStruct->fixed_H && pStruct->fixed_H[i]) && + !(nMobHInChI && nMobHInChI[i] ) && + /* has N(V) neighbor */ + 1 == at2[iat].valence && at2[iN=at2[iat].neighbor[0]].chem_bonds_valence==5 && + !at2[iN].charge && pVA[iN].cNumValenceElectrons == 5 && + /* has (-) edge */ + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { + iat_SB_O_Minus[num_SB_O_Minus ++] = iat; + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + if ( num_try = inchi_min( num_SB_O_Minus, num_DB_O_Neutr ) ) { + /* detected; attempt to fix */ + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + delta = 1; + for ( i = 0; i < num_SB_O_Minus && cur_success < num_try; i ++ ) { + iat = iat_SB_O_Minus[i]; + pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->forbidden |= forbidden_edge_mask; + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { + /* Moved (-) charge to AB=O => nDeltaCharge == 1 */ + /* Flow change on pe (-)charge edge (O(-)-AB) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 07 */ + } + } else { + pe->forbidden &= forbidden_edge_mask_inv; + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } + CurrEdges.num_edges = 0; /* clear current edge list */ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + if ( /*(pc2i->len_c2at >= 1 || pc2i->nNumRemHRevrs) &&*/ pc2i->nNumTgInChI == 1 && /* ADP in InChI */ + (pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1) ) { + /*----------------------------------------------------------------*/ + /* case 08: restored: O(-)-AB=N- OH- orig. O=AB-N(-)- OH- */ + /* FixH: 1 0 0 0 0 1 */ + /* MobH: 0 0 1 0 0 0 */ + /* may be taut or not non-taut taut taut taut */ + /* ADP: one t-group or more endpoints */ + /* O(-) = S, Se, Te; N = N; */ + /* Solution: move (-) from O(-) to =N-; avoid stereogenic DB on N */ + /*----------------------------------------------------------------*/ + int num_DB_N_Neutr = 0, num_SB_O_Minus = 0, iat; + short iat_DB_N_Neutr[MAX_DIFF_FIXH], iat_SB_O_Minus[MAX_DIFF_FIXH]; + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; + cur_success = 0; + for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ + iat = nCanon2AtnoRevrs[i]; + if ( /* in restored atom: charge=-1, has no H, has single bond, O, S, Se, Te */ + num_SB_O_Minus < MAX_DIFF_FIXH && + at2[iat].charge == -1 && !at2[iat].num_H && + at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && + pVA[iat].cNumValenceElectrons == 6 && + /* in orig.InChI: an endpoint, may have fixed-H */ + pStruct->endpoint[i] && + /*!(pStruct->fixed_H && pStruct->fixed_H[i]) &&*/ + !(nMobHInChI && nMobHInChI[i] ) && + /* has (-) edge */ + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { + + iat_SB_O_Minus[num_SB_O_Minus ++] = iat; + + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } else + if ( /* in restored atom: charge=0, has no H, has double non-stereogenic bond, N */ + num_DB_N_Neutr < MAX_DIFF_FIXH && + at2[iat].charge == 0 && !at2[iat].num_H && !at2[iat].sb_parity[0] && + at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && + pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && + /* in orig.InChI: an endpoint, has no fixed-H */ + pStruct->endpoint[i] && + !(pStruct->fixed_H && pStruct->fixed_H[i]) && + !(nMobHInChI && nMobHInChI[i] ) && + /* has (-) edge */ + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && + 0 == pBNS->edge[e].forbidden ) { + + iat_DB_N_Neutr[num_DB_N_Neutr ++] = iat; + + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + if ( num_try = inchi_min( num_DB_N_Neutr, num_SB_O_Minus ) ) { + /* detected; attempt to fix */ + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + /* allow stereobonds in rings change */ + if ( forbidden_stereo_edge_mask ) + RemoveForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask ); + + delta = 1; + for ( i = 0; i < num_SB_O_Minus && cur_success < num_try; i ++ ) { + iat = iat_SB_O_Minus[i]; + pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->forbidden |= forbidden_edge_mask; + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { + /* Moved (-) charge to =N- => nDeltaCharge == 1 */ + /* Flow change on pe (-)charge edge (atom (-)O-) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 08 */ + } + } else { + pe->forbidden &= forbidden_edge_mask_inv; + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + if ( forbidden_stereo_edge_mask ) + SetForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask ); + } + CurrEdges.num_edges = 0; /* clear current edge list */ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + + if ( pc2i->len_c2at >= 2 ) { + /*--------------------------------------------------------*/ + /* case 09: restored: NH2(+)=C--NH2 orig.: NH2-C(+)-NH2 */ + /* FixH: 2 | 2 0 | 0 */ + /* MobH: 0 0 2 2 */ + /* N = N, taut taut non-taut non-taut*/ + /* Solution: move (+) from NH2(+) to C */ + /*--------------------------------------------------------*/ + int iat; + cur_success = 0; + for ( i = 0; i < pc2i->len_c2at; i ++ ) { + iat = pc2i->c2at[i].atomNumber; + if ( (pc2i->c2at[i].nValElectr == 5 && pc2i->c2at[i].nPeriodNum == 1) && + /* orig. InChI info: */ + !pc2i->c2at[i].endptInChI && + pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI && + /* reversed structure info: */ + pc2i->c2at[i].endptRevrs && + pc2i->c2at[i].nFixHRevrs && !pc2i->c2at[i].nMobHRevrs && + pc2i->c2at[i].nAtChargeRevrs == 1 && + at2[iat].valence + 1 == at2[iat].chem_bonds_valence && + (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { + EdgeIndex eNC = NO_VERTEX, eCPlusC; + int iNH2, iatC, iatNH2, icNH2; + /* found NH2(+)=; locate =C< and find whether it has -NH2 neighbor */ + for ( j = 0; j < at2[iat].valence; j ++ ) { + if ( at2[iat].bond_type[j] == BOND_TYPE_DOUBLE ) + break; + } + if ( j == at2[iat].valence ) + continue; + eNC = pBNS->vert[iat].iedge[j]; /* edge NH2(+)=C */ + iatC = at2[iat].neighbor[j]; + if ( pVA[iatC].cNumValenceElectrons != 4 || pVA[iatC].cMetal || at2[iatC].charge || + at2[iatC].valence != 3 || at2[iatC].valence+1 != at2[iatC].chem_bonds_valence || + (eCPlusC=pVA[iatC].nCPlusGroupEdge-1) < 0 || pBNS->edge[eCPlusC].forbidden) + continue; + for ( j = 0; j < at2[iatC].valence; j ++ ) { + iatNH2 = at2[iatC].neighbor[j]; + if ( iatNH2 == iat || pVA[iatNH2].cNumValenceElectrons != 5 || + pVA[iatNH2].cPeriodicRowNumber != 1 || !at2[iatNH2].num_H || at2[iatNH2].charge) + continue; + icNH2 = pStruct->nAtno2Canon[0][iatNH2]; + for ( iNH2 = 0; iNH2 < pc2i->len_c2at; iNH2 ++ ) { + if ( iatNH2 == pc2i->c2at[iNH2].atomNumber ) + break; + } + if ( iNH2 == pc2i->len_c2at ) + continue; + + if ( (pc2i->c2at[iNH2].nValElectr == 5 && pc2i->c2at[iNH2].nPeriodNum == 1) && + /* orig. InChI info: */ + !pc2i->c2at[iNH2].endptInChI && + pc2i->c2at[iNH2].nFixHInChI == 0 && pc2i->c2at[iNH2].nMobHInChI && + /* reversed structure info: */ + pc2i->c2at[iNH2].endptRevrs && + pc2i->c2at[iNH2].nFixHRevrs && !pc2i->c2at[iNH2].nMobHRevrs && + pc2i->c2at[iNH2].nAtChargeRevrs == 0 && + at2[iatNH2].valence == at2[iatNH2].chem_bonds_valence ) { + /* we have found NH2(+)=, =C<, and bond between them */ + + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &CurrEdges, eCPlusC, INC_ADD_EDGE ) ) { + goto exit_function; + } + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + delta = 1; + + pe = pBNS->edge + eNC; + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->forbidden |= forbidden_edge_mask; + pe->flow -= delta; /* add (+) to -NHm */ + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { + /* Removed (+)charge from -NHn => nDeltaCharge == -1 */ + /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 09 */ + } + } else { + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + pe->forbidden &= forbidden_edge_mask_inv; + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + CurrEdges.num_edges = 0; /* clear current edge list */ + break; + } + } + } + } + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + + + if ( pc2i->len_c2at >= 2 ) { + /*--------------------------------------------------------*/ + /* case 10: restored: NH2-X(+)-NH- orig.: NH2(+)=X-NH- */ + /* FixH: 0 0 2 1 */ + /* MobH: 2 1 0 0 */ + /* N = N,O,S,Se,Te non-taut non-taut taut taut */ + /* Solution: move (+) from X(+) to NH2 or NH */ + /*--------------------------------------------------------*/ + int iat; + cur_success = 0; + for ( i = 0; i < pc2i->len_c2at; i ++ ) { + if ( pc2i->c2at[i].nValue ) + continue; + iat = pc2i->c2at[i].atomNumber; + if ( (pc2i->c2at[i].nValElectr == 6 || + pc2i->c2at[i].nValElectr == 5 && pc2i->c2at[i].nPeriodNum == 1) && + /* orig. InChI info: */ + pc2i->c2at[i].endptInChI && + pc2i->c2at[i].nFixHInChI && !pc2i->c2at[i].nMobHInChI && + /* reversed structure info: */ + !pc2i->c2at[i].endptRevrs && + !pc2i->c2at[i].nFixHRevrs && pc2i->c2at[i].nMobHRevrs && + pc2i->c2at[i].nAtChargeRevrs == 0 && + at2[iat].valence == at2[iat].chem_bonds_valence && + (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { + + EdgeIndex eCPlusC, eCPlusNH2, bContinue=1; + int iNH2, iatC, iatNH2, icNH2, j1, j2; + BNS_EDGE *pe_iat, *pe_iNH2; + /* found NH2- locate -X(+) and find whether it has another -NH2 neighbor */ + for ( j1 = 0; j1 < at2[iat].valence && bContinue; j1 ++ ) { + if ( at2[iat].bond_type[j1] == BOND_TYPE_SINGLE && + at2[iatC = at2[iat].neighbor[j1]].charge == 1 && + (4 <= pVA[iatC].cNumValenceElectrons && pVA[iatC].cNumValenceElectrons <= 6) && + at2[iatC].valence == at2[iatC].chem_bonds_valence && + (eCPlusC=pVA[iatC].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[eCPlusC].forbidden) { + /* found a candidate for X; find another NH2 */ + for ( j2 = 0; j2 < at2[iatC].valence && bContinue; j2 ++ ) { + if ( at2[iatC].bond_type[j2] == BOND_TYPE_SINGLE && + iat != (iatNH2 = at2[iatC].neighbor[j2]) && + at2[iatNH2].charge == 0 && at2[iatNH2].num_H && + (pVA[iatNH2].cNumValenceElectrons==5 || pVA[iatNH2].cNumValenceElectrons==6) && + at2[iatNH2].valence == at2[iatNH2].chem_bonds_valence && + (eCPlusNH2=pVA[iatNH2].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[eCPlusNH2].forbidden) { + for ( iNH2 = 0; iNH2 < pc2i->len_c2at; iNH2 ++ ) { + if ( iatNH2 != pc2i->c2at[iNH2].atomNumber || pc2i->c2at[iNH2].nValue ) + continue; + /* check the second -NH */ + icNH2 = pStruct->nAtno2Canon[0][iatNH2]; /* canon number -1 */ + if ( /* orig. InChI info: */ + pc2i->c2at[iNH2].endptInChI && + pc2i->c2at[iNH2].nFixHInChI && !pc2i->c2at[iNH2].nMobHInChI && + /* reversed structure info: */ + !pc2i->c2at[iNH2].endptRevrs && + !pc2i->c2at[iNH2].nFixHRevrs && pc2i->c2at[iNH2].nMobHRevrs && + pc2i->c2at[iNH2].nAtChargeRevrs == 0 ) { + /* we have found NH-X(+)-NH; remove charge from X(+) */ + pe_iat = pBNS->edge + pBNS->vert[iat].iedge[j1]; + pe_iNH2 = pBNS->edge + pBNS->vert[iatC].iedge[j2]; + /* pick up one of -NH to move (+) to it */ + if ( !pe_iat->forbidden && pBNS->edge[e].flow ) { + pe = pBNS->edge + e; + } else + if ( !pe_iNH2->forbidden && pBNS->edge[eCPlusNH2].flow ) { + pe = pBNS->edge + eCPlusNH2; + } else { + continue; /* none of the two -X(+)- bonds may be changed */ + } + if ( ret = AddToEdgeList( &CurrEdges, eCPlusC, INC_ADD_EDGE ) ) { + goto exit_function; + } + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + delta = 1; + + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + /*pe->forbidden |= forbidden_edge_mask;*/ + pe->flow -= delta; /* add (+) to -NHm */ + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) { + /* Removed (+)charge from -NHn => nDeltaCharge == -1 */ + /* Flow change on pe (+)charge edge (atom NHm(+)) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 10 */ + bContinue = 0; + pc2i->c2at[i].nValue = 1; /* mark as used */ + pc2i->c2at[iNH2].nValue = 1; /* mark as used */ + } + } else { + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + + /*pe->forbidden &= forbidden_edge_mask_inv;*/ + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + CurrEdges.num_edges = 0; /* clear current edge list */ + break; + } + } /* iNH2: pc2i->c2at[iNH2] cycle */ + } + } /* j2: iatC neighbors cycle */ + } + } /* j1: iat neighbors cycle */ + } + } /* i: pc2i->c2at[i] cycle */ + if ( cur_success ) { + /* + for ( i = 0; i < pc2i->len_c2at; i ++ ) { + pc2i->c2at[i].nValue = 0; + } + */ + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + + if ( /*pc2i->len_c2at >= 1 &&*/ pc2i->nNumTgInChI == 1 && /* ADP in InChI */ + (pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1) ) { + /*--------------------------------------------------------------*/ + /* case 11: restored: NH(+)=AB-N< OH- orig. NH-AB=N(+)< OH- */ + /* FixH: 0 0 0 1 0 1 */ + /* MobH: 1 0 1 0 0 0 */ + /* non-taut. taut taut */ + /* ADP: one t-group or more endpoints */ + /* NH(+)= => N, O, S, Se; -N< => N */ + /* Solution: move (+) from NH(+) to -N< */ + /*--------------------------------------------------------------*/ + int num_SB_Neutr = 0, num_DB_Charged = 0, iat; + short iat_SB_Neutr[MAX_DIFF_FIXH], iat_DB_Charged[MAX_DIFF_FIXH]; + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; + cur_success = 0; + /* search for NH(+)= */ + /* search for -N< */ + for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ + iat = nCanon2AtnoRevrs[i]; + if ( /* in restored atom: charge=0, has no H, has no double bond, N only */ + num_DB_Charged < MAX_DIFF_FIXH && + at2[iat].charge == 1 && at2[iat].num_H && + at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && + (pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 || + pVA[iat].cNumValenceElectrons == 6 ) && + /* in orig.InChI: an endpoint, has fixed-H */ + /*pStruct->endpoint[i] &&*/ + (pStruct->fixed_H && pStruct->fixed_H[i]) && + /*!(nMobHInChI && nMobHInChI[i] ) &&*/ + /* has (+) edge */ + (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && 0 == pBNS->edge[e].forbidden ) { + + iat_DB_Charged[num_DB_Charged ++] = iat; + /* + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + */ + } else + if ( /* in restored atom: charge=0, has no H, has no double bond, N only */ + num_SB_Neutr < MAX_DIFF_FIXH && + at2[iat].charge == 0 && !at2[iat].num_H && + at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && + (pVA[iat].cNumValenceElectrons == 5 && + pVA[iat].cPeriodicRowNumber == 1 ) && + /* in orig.InChI: an endpoint, has fixed-H */ + /*pStruct->endpoint[i] &&*/ + !(pStruct->fixed_H && pStruct->fixed_H[i]) && + !(nMobHInChI && nMobHInChI[i] ) && + /* has (+) edge */ + (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && 0 == pBNS->edge[e].forbidden ) { + + iat_SB_Neutr[num_SB_Neutr ++] = iat; + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + if ( num_try = inchi_min( num_SB_Neutr, num_DB_Charged ) ) { + /* detected; attempt to fix */ + BNS_VERTEX *pv1n, *pv2n; + BNS_EDGE *pe1n, *pe2n; + Vertex v1n, v2n; + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + delta = 1; + for ( i = 0; i < num_DB_Charged && cur_success < num_try; i ++ ) { + iat = iat_DB_Charged[i]; + pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; + if ( pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + for ( j = pv1->num_adj_edges-1; 0 <= j; j -- ) { + pe1n = pBNS->edge + pv1->iedge[j]; + if ( pe1n->flow && !pe1n->forbidden ) { + pv1n = pBNS->vert + (v1n = pe1n->neighbor12 ^ v1); + break; + } + } + if ( j < 0 ) + continue; /* not found */ + + for ( j = pv2->num_adj_edges-2; 0 <= j; j -- ) { + pe2n = pBNS->edge + pv2->iedge[j]; + if ( pe2n->flow && !pe2n->forbidden ) { + pv2n = pBNS->vert + (v2n = pe2n->neighbor12 ^ v2); + break; + } + } + if ( j < 0 ) + continue; /* not found */ + + pe->flow += delta; + pe1n->flow -= delta; + pe2n->flow -= delta; + pv1n->st_edge.flow -= delta; + pv2n->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1n && vPathStart == v2n || + vPathEnd == v2n && vPathStart == v1n) && + (nDeltaCharge == 0 || nDeltaCharge == 1) ) { + /* before setting flows the structure could be: + [NH+ neigh, v1n]=e1n=[NH+,v1]-pe-[+,v2]=e2n=[another at or its chargeStruct] + or + + [NH+ or ChStr, v1n]=pe1n=[NH+ or ChStr, v1]-pe-[+,v2]=pe2n=[at2 or ChStr, v2n] + ^ ^ ^ + NH+(+)edge | N (+) edge: only + | these are not forbidden + | + hetero (+) vertex + + After setting flows (* mark radicals, =pe= is forbidden): + + *[NH+ or ChStr, v1n]-pe1n-[NH+ or ChStr, v1]=pe=[+,v2]-pe2n-[at2 or ChStr, v2n]* + ^ ^ ^ + NH+(+)edge | N (+) edge: only + | these are not forbidden + | + hetero (+) vertex + + Flow in + pe1n and pe2n will or will not change, depending on the structure. + + Consider what happens if pe2n changes. It may only increment. + If pe2n flow increments then another (+)edge flow dectrements. If + [at2 or ChStr, v2n] is at2 then at2 charge would change from (+) to 0, + and another N charge would change from 0 to (+), giving tot. change of + number of charges (-1)+(+1)=0. However, if [at2 or ChStr, v2n] is + ChargeStruct then at2 will not be on the alt path and only the creation + of another (+) will be detected. + */ + /* Removed charge from O(+) => nDeltaCharge == -1 */ + /* Flow change on pe (+)charge edge (atom NH2) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 11 */ + } + } else { + pe->flow -= delta; + pe1n->flow += delta; + pe2n->flow += delta; + pv1n->st_edge.flow += delta; + pv2n->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } + CurrEdges.num_edges = 0; /* clear current edge list */ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + + if ( pc2i->len_c2at >= 1 && pc2i->nNumTgInChI == 1 && + pc2i->nNumRemHInChI >= -1 && /* 2006-03-03 */ + (pc2i->nNumEndpInChI > pc2i->nNumEndpRevrs || pc2i->nNumTgRevrs > 1) /* ADP in InChI */ ) { + /*--------------------------------------------------------------*/ + /* case 12: restored: O=AB-N< original: (-)O-AB=N(+)< */ + /* FixH: 0 0 0 0 */ + /* MobH: 0 0 0 0 */ + /* non-taut taut */ + /* O = O, S, Se, N; N = N; */ + /* restored atom O is not tautomeric; original atom O is taut. */ + /* original struct has 1 t-group; restored has less endpoints */ + /* and/or possibly >1 t-groups */ + /* Solution: separate charges between O= and -N< */ + /* allow moving charge to N(V) to make it N(IV)(+) */ + /*--------------------------------------------------------------*/ + int bOnly_N_V = 1; + cur_success = 0; + while( 1 ) { + int num_SB_N_Neutr = 0, num_DB_O = 0, iat, num_N_V=0, bN_V; + short iat_SB_N_Neutr[MAX_DIFF_FIXH], iat_DB_O[MAX_DIFF_FIXH]; + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && + pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; + S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; + cur_success = 0; + for ( i = 0; i < pc2i->len_c2at; i ++ ) { + iat = pc2i->c2at[i].atomNumber; + if ( /* orig. InChI info: -O(-) */ + num_DB_O < MAX_DIFF_FIXH && + (pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ || + pc2i->c2at[i].nValElectr == 5 && + pc2i->c2at[i].nPeriodNum == 1 /* N */ ) && + pc2i->c2at[i].endptInChI && + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI == 0 && + /* reversed structure info: */ + !pc2i->c2at[i].endptRevrs && + pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && + pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && + ((pc2i->c2at[i].nValElectr == 6)? + (at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2): + (pc2i->c2at[i].nValElectr == 5)? + (at2[iat].valence == 2 && at2[iat].chem_bonds_valence == 3): + 0)) { + + iat_DB_O[num_DB_O ++] = iat; + /* + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + */ + } + } + for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ + iat = nCanon2AtnoRevrs[i]; + bN_V = 0; + if ( /* in restored atom N: charge=0, no H, has no double bond, not an endpoint */ + num_SB_N_Neutr < MAX_DIFF_FIXH && + at2[iat].charge == 0 && !at2[iat].num_H && + (at2[iat].valence == at2[iat].chem_bonds_valence || + (bN_V = at2[iat].valence+2 == at2[iat].chem_bonds_valence)) && + !pVA[iat].cMetal && + pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && + !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint) && + /* in orig.InChI: not an endpoint, has no H */ + !pStruct->endpoint[i] && + !(pStruct->fixed_H && pStruct->fixed_H[i]) && + !(nMobHInChI && nMobHInChI[i]) && + /* has (+) edge */ + (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { + + if ( bOnly_N_V && bN_V && + NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e )) && + !pBNS->edge[j].forbidden && !pBNS->edge[j].flow ) { + if ( !num_N_V ) { + /* switch to N(V) only mode */ + CurrEdges.num_edges = 0; + num_SB_N_Neutr = 0; + } + iat_SB_N_Neutr[num_SB_N_Neutr ++] = iat; + num_N_V ++; + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &CurrEdges, j, INC_ADD_EDGE ) ) { + goto exit_function; + } + } else + if ( !num_N_V ) { + iat_SB_N_Neutr[num_SB_N_Neutr ++] = iat; + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + /* in addition, permit N(V)=>N(IV)(+) change by allowing charge flower edge change flow */ + if ( bN_V && NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e )) && + !pBNS->edge[j].forbidden && !pBNS->edge[j].flow ) { + if ( ret = AddToEdgeList( &CurrEdges, j, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + } + } + if ( num_try = inchi_min( num_SB_N_Neutr, num_DB_O ) ) { + /* detected; attempt to fix */ + BNS_EDGE *pe_CMinus; + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + delta = 1; + for ( i = 0; i < num_DB_O && cur_success < num_try; i ++ ) { + iat = iat_DB_O[i]; + pe_CMinus = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; + pe_CMinus->forbidden &= forbidden_edge_mask_inv; + + pe = pBNS->edge + pBNS->vert[iat].iedge[0]; /* double bond O=...*/ + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->forbidden |= forbidden_edge_mask; /* change bond O=X to O(rad)-X(rad) */ + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 2 ) { + /* Added (-) charge to =O and (+) charge to N => nDeltaCharge == 2 */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 12 */ + } + } else { + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + pe->forbidden &= forbidden_edge_mask_inv; /* allow changes to O=X bond */ + INCHI_HEAPCHK + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } + CurrEdges.num_edges = 0; /* clear current edge list */ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + break; + } else + if ( bOnly_N_V ) { + bOnly_N_V = 0; + } else { + break; + } + } + } + + if ( pc2i->nNumTgDiffMinus /*|| pc2i->nNumTgDiffH */ /* no ADP in InChI needed */ ) { + /*--------------------------------------------------------------*/ + /* | | */ + /* case 13: restored: O=AB=N= original: (-)O-AB-N(+)= */ + /* FixH: 0 0 0 0 */ + /* MobH: 0 0 0 0 */ + /* non-taut taut non-taut */ + /* O = O, S, Se, N; N = N, P, ... */ + /* t-group in original has same num. endpoints */ + /* same num_H and less (-) than in the restored structure */ + /* original atom O is tautomeric, N is not taut in both */ + /* original struct has 1 t-group; restored has less endpoints */ + /* and/or possibly >1 t-groups */ + /* Solution: separate charges between O= and -N< */ + /* allow moving charge to N(V) to make it N(IV)(+) */ + /*--------------------------------------------------------------*/ + int itg; + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && + pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; + + S_CHAR *num_Fixed_H_Revrs = pStruct->pOneINChI[0]->nNum_H_fixed? pStruct->pOneINChI[0]->nNum_H_fixed : NULL; + S_CHAR *pnMobHRevrs = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNum_H)? + pStruct->pOneINChI[1]->nNum_H : + (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)? + pStruct->pOneINChI[0]->nNum_H : NULL; + S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; + cur_success = 0; + /* find whether this may help */ + for ( itg = 0; itg < pStruct->ti.num_t_groups && itg < pStruct->One_ti.num_t_groups; itg ++ ) { + if ( pStruct->ti.t_group[itg].nNumEndpoints == pStruct->One_ti.t_group[itg].nNumEndpoints && + pStruct->ti.t_group[itg].num[0] - pStruct->ti.t_group[itg].num[1] == + pStruct->One_ti.t_group[itg].num[0] - pStruct->One_ti.t_group[itg].num[1] && + pStruct->ti.t_group[itg].num[1] > pStruct->One_ti.t_group[itg].num[1]) { + /* restored InChI t-group has more (-) and same number of H */ + + int num_SB_N_Neutr = 0, num_DB_O = 0, iat; + short iat_SB_N_Neutr[MAX_DIFF_FIXH], iat_DB_O[MAX_DIFF_FIXH]; + cur_success = 0; + for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ + iat = nCanon2AtnoRevrs[i]; + if ( /* orig. InChI info: -O(-) */ + num_DB_O < MAX_DIFF_FIXH && + (pVA[i].cNumValenceElectrons == 6 /* O, S, Se, Te */ ) && + pStruct->endpoint[i] == itg+1 && + (e=pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + !(pStruct->fixed_H && pStruct->fixed_H[i]) && + !(nMobHInChI && nMobHInChI[i]) && + /* reversed structure info: */ + /*!pc2i->c2at[i].endptRevrs &&*/ + !(num_Fixed_H_Revrs && num_Fixed_H_Revrs[iat]) && + !(pnMobHRevrs && pnMobHRevrs[iat]) && + at2[iat].charge == 0 && at2[iat].num_H == 0 && + at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 ) { + + iat_DB_O[num_DB_O ++] = iat; + /* + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + */ + } else + if ( /* in restored atom N: charge=0, no H, has no double bond, not an endpoint */ + num_SB_N_Neutr < MAX_DIFF_FIXH && + at2[iat].charge == 0 && !at2[iat].num_H && + /*at2[iat].valence == at2[iat].chem_bonds_valence ||*/ + (at2[iat].valence==4 && at2[iat].chem_bonds_valence==5) && + !pVA[iat].cMetal && + pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber >= 1 && + !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint) && + /* in orig.InChI: not an endpoint, has no H */ + !pStruct->endpoint[i] && + !(pStruct->fixed_H && pStruct->fixed_H[i]) && + !(nMobHInChI && nMobHInChI[i]) && + /* has (+) edge */ + (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { + + iat_SB_N_Neutr[num_SB_N_Neutr ++] = iat; + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + if ( num_try = inchi_min( num_SB_N_Neutr, num_DB_O ) ) { + /* detected; attempt to fix */ + BNS_EDGE *pe_CMinus; + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + delta = 1; + for ( i = 0; i < num_DB_O && cur_success < num_try; i ++ ) { + iat = iat_DB_O[i]; + pe_CMinus = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; + pe_CMinus->forbidden &= forbidden_edge_mask_inv; + + pe = pBNS->edge + pBNS->vert[iat].iedge[0]; /* double bond O=...*/ + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->forbidden |= forbidden_edge_mask; /* change bond O=X to O(rad)-X(rad) */ + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 2 ) { + /* Added (-) charge to =O and (+) charge to N => nDeltaCharge == 2 */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 13 */ + } + } else { + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + pe->forbidden &= forbidden_edge_mask_inv; /* allow changes to O=X bond */ + INCHI_HEAPCHK + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } + CurrEdges.num_edges = 0; /* clear current edge list */ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + break; + }/* else + if ( bOnly_N_V ) { + bOnly_N_V = 0; + } + */ + break; + } + } + } + + if ( (pc2i->nNumTgInChI <= 1 && + pc2i->nNumRemHInChI > pc2i->nNumRemHRevrs || pc2i->len_c2at) && + bHas_N_V( at2, pStruct->num_atoms) ) { + /*-----------------------------------------------------------------*/ + /* | | */ + /* case 14: restored:-N=AB=N=CD-XH original: (-)N-AB-N(+)=CD-XH */ + /* FixH: 0 0 0/1 0 1 */ + /* MobH: 0 0 1/0 0 0 */ + /* non-taut n/t any non any */ + /* taut */ + /* X = O, S, Se, N; N = N */ + /* t-group in original may have more (-) than in restored */ + /* same num_H and less (-) than in the restored structure */ + /* atom N(V)/N(IV)(+) is not taut in both */ + /* The following transformation should be possible: */ + /* | | */ + /* N=AB=N=CD-XH -> (-)N-AB-N-CD=XH(+) */ + /* This allows ADP to remove H(+) from -XH */ + /* As the result, the original structure has 0 or 1 t-group */ + /* Solution: separate charges between -N(III)= and N(V) */ + /*-----------------------------------------------------------------*/ + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && + pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; + + S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; + int num_N_V = 0, iat, i1, i2, i3, e1Flower, e1Plus, e2Plus, e2Minus, e3Plus; + int max_success = pc2i->nNumRemHInChI - pc2i->nNumRemHRevrs; + short iat_N_V_Array[MAX_DIFF_FIXH]; + EDGE_LIST iat_X_List, iat_N_III_List; + AllocEdgeList( &iat_X_List, EDGE_LIST_CLEAR ); + AllocEdgeList( &iat_N_III_List, EDGE_LIST_CLEAR ); + cur_success = 0; + ret = 0; + for ( i = 0; i < pStruct->num_atoms; i ++ ) { + iat = nCanon2AtnoRevrs[i]; + /* search for N(V), 3 bonds */ + if ( /* restored structure */ + num_N_V < MAX_DIFF_FIXH && + at2[iat].chem_bonds_valence == 5 && at2[iat].valence == 3 && + !at2[iat].charge && !at2[iat].radical && + pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && + !( at_Mobile_H_Revrs && at_Mobile_H_Revrs[i].endpoint ) && + !at2[iat].num_H && + (e = pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + pBNS->edge[e].flow /* no charge */ && + NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e )) && !pBNS->edge[j].forbidden && + !pBNS->edge[j].flow /* neutral, valence=5 */ && + /* orig. InChI */ + !pStruct->endpoint[i] && + !(nMobHInChI && nMobHInChI[i]) && !pStruct->fixed_H[i] ) { + iat_N_V_Array[num_N_V ++] = iat; + } else + /* search for -N= */ + if ( /* restored structure */ + at2[iat].chem_bonds_valence == 3 && at2[iat].valence == 2 && + !at2[iat].charge && !at2[iat].radical && + pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && + !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[i].endpoint ) && + !at2[iat].num_H && + (e = pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + !pBNS->edge[e].flow /* no charge */ && + /* orig. InChI */ + /*!pStruct->endpoint[i] &&*/ + !(nMobHInChI && nMobHInChI[i]) && !pStruct->fixed_H[i] ) { + + if ( ret = AddToEdgeList( &iat_N_III_List, iat, 32 ) ) { + goto exit_case_14; + } + } else + /* search for -OH -NH-, -NH2 */ + if ( /* restored structure */ + at2[iat].chem_bonds_valence == at2[iat].valence && + !at2[iat].charge && !at2[iat].radical && + (pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 || + pVA[iat].cNumValenceElectrons == 6 ) && + at2[iat].num_H && + (e = pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + pBNS->edge[e].flow /* no charge */ && + /* orig. InChI */ + !(nMobHInChI && nMobHInChI[i]) && pStruct->fixed_H[i] ) { + + if ( ret = AddToEdgeList( &iat_X_List, iat, 32 ) ) { + goto exit_case_14; + } + } + } + if ( !max_success ) { + max_success = inchi_min( num_N_V, iat_N_III_List.num_edges ); + max_success = inchi_min( max_success, iat_X_List.num_edges ); + } + if ( num_N_V && iat_N_III_List.num_edges && iat_X_List.num_edges ) { + for ( i1 = 0; i1 < num_N_V && cur_success < max_success; i1 ++ ) { + int iat_N_V = iat_N_V_Array[i1]; + if ( NO_VERTEX == iat_N_V || + 0 >= (e1Plus = pVA[iat_N_V].nCPlusGroupEdge-1) || + NO_VERTEX == (e1Flower = GetChargeFlowerUpperEdge( pBNS, pVA, e1Plus )) || + 1 != pBNS->edge[e1Plus].flow || + 0 != pBNS->edge[e1Flower].flow ) { + continue; + } + for ( i2 = iat_N_III_List.num_edges-1; 0 <= i2 && cur_success < max_success; i2 -- ) { + int iat_N_III = iat_N_III_List.pnEdges[i2]; + if ( NO_VERTEX == iat_N_III || + 0 >= (e2Minus = pVA[iat_N_III].nCMinusGroupEdge-1) || + 0 >= (e2Plus = pVA[iat_N_III].nCPlusGroupEdge-1) || + 0 != pBNS->edge[e2Minus].flow || + 1 != pBNS->edge[e2Plus].flow ) { + /* do not consider this atom anymore */ + iat_N_III_List.pnEdges[i2] = NO_VERTEX; + continue; + } + for ( i3 = iat_X_List.num_edges-1; 0 <= i3 && cur_success < max_success; i3 -- ) { + int iat_X = iat_X_List.pnEdges[i3]; + BNS_VERTEX *pv1n, *pv2n; + BNS_EDGE *pe1n, *pe2n, *pe1Plus, *pe2Minus, *pe3Plus; + Vertex v1n, v2n; + ret = 0; + if ( NO_VERTEX == iat_X || + 0 >= (e3Plus = pVA[iat_X].nCPlusGroupEdge-1) || + 1 != pBNS->edge[e3Plus].flow ) { + /* do not consider this atom anymore */ + iat_X_List.pnEdges[i3] = NO_VERTEX; + continue; + } + /* all is ready to check whether the following applies: + forbid changes of all charges and N,P,... flowers + allow to change edges: e2Minus, e3Plus + Increment flow in e1Flower + The result should be: increase in number of charges by 2 + */ + pe1Plus = pBNS->edge + e1Plus; /* N(V) positive charge edge */ + pe2Minus = pBNS->edge + e2Minus; /* =N- negative charge edge */ + pe3Plus = pBNS->edge + e3Plus; /* -XH positive charge edge */ + pe = pBNS->edge + e1Flower; /* N(V) flower edge */ + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + for ( j = pv1->num_adj_edges-1; 0 <= j; j -- ) { + pe1n = pBNS->edge + pv1->iedge[j]; + if ( pe1n->flow && !pe1n->forbidden && pe1n != pe1Plus ) { + pv1n = pBNS->vert + (v1n = pe1n->neighbor12 ^ v1); + break; + } + } + if ( j < 0 ) + continue; /* not found -- should not happen */ + for ( j = pv2->num_adj_edges-1; 0 <= j; j -- ) { /* was -2; changed 2006-2-28 12:35pm*/ + pe2n = pBNS->edge + pv2->iedge[j]; + if ( pe2n->flow && !pe2n->forbidden && pe2n != pe1Plus ) { + pv2n = pBNS->vert + (v2n = pe2n->neighbor12 ^ v2); + break; + } + } + if ( j < 0 ) + continue; /* not found -- should not happen */ + delta = 1; + pe->flow += delta; + pe1n->flow -= delta; + pe2n->flow -= delta; + pv1n->st_edge.flow -= delta; + pv2n->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + SetForbiddenEdgeMask( pBNS, &OtherNFlowerEdges, forbidden_edge_mask ); + + /* allow two charges to change */ + pe2Minus->forbidden &= forbidden_edge_mask_inv; + pe3Plus->forbidden &= forbidden_edge_mask_inv; + /* test #1 */ + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + INCHI_HEAPCHK + if ( ret < 0 ) { + goto exit_case_14; + } else + if ( ret == 1 && (vPathEnd == v1n && vPathStart == v2n || + vPathEnd == v2n && vPathStart == v1n) && + nDeltaCharge == 2 ) { + ; /* success */ + } else { + ret = 0; + } + /* restore BNS */ + pe2Minus->forbidden |= forbidden_edge_mask; + pe3Plus->forbidden |= forbidden_edge_mask; + pe->flow -= delta; + pe1n->flow += delta; + pe2n->flow += delta; + pv1n->st_edge.flow += delta; + pv2n->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + if ( ret == 1 ) { + /* test #2: check if charge separation is possible */ + pe->flow += delta; + pe1n->flow -= delta; + pe2n->flow -= delta; + pv1n->st_edge.flow -= delta; + pv2n->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + /* allow two charges (N(V) and N(III)) to change */ + pe2Minus->forbidden &= forbidden_edge_mask_inv; + pe1Plus->forbidden &= forbidden_edge_mask_inv; + /* test #2 */ + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + if ( ret == 1 && (vPathEnd == v1n && vPathStart == v2n || + vPathEnd == v2n && vPathStart == v1n) && + nDeltaCharge == 2 ) { + /* success; actually change charges */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 14 */ + } + } + if ( ret <= 0 ) { + /* failed: restore BNS flow */ + pe->flow -= delta; + pe1n->flow += delta; + pe2n->flow += delta; + pv1n->st_edge.flow += delta; + pv2n->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &OtherNFlowerEdges, forbidden_edge_mask ); + if ( ret > 0 ) { + /* do not repeat for the same atoms */ + iat_N_V_Array[i1] = NO_VERTEX; + iat_N_III_List.pnEdges[i2] = NO_VERTEX; + iat_X_List.pnEdges[i3] = NO_VERTEX; + } + if ( ret < 0 ) { + goto exit_case_14; + } + if ( ret > 0 ) { + break; + } + } /* i3 cycle */ + if ( ret > 0 ) { + break; + } + } /* i2 cycle */ + } + } +exit_case_14: + AllocEdgeList( &iat_X_List, EDGE_LIST_FREE ); + AllocEdgeList( &iat_N_III_List, EDGE_LIST_FREE ); + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &OtherNFlowerEdges, forbidden_edge_mask ); + CurrEdges.num_edges = 0; /* clear current edge list */ + if ( ret < 0 ) { + goto exit_function; + } + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + + + if ( pc2i->nNumTgMRevrs > pc2i->nNumTgMInChI || + pc2i->nNumRemHRevrs < pc2i->nNumRemHInChI || + pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || + pc2i->nNumTgInChI <= 1 && pc2i->nNumTgRevrs > pc2i->nNumTgInChI ) { + /*--------------------------------------------------------------*/ + /* case 15: restored: -(+)O=AB-N< orig: -O-AB=N(+)< */ + /* (a) restored t-groups have more (-) than in original InChI */ + /* (b) Mobile-H charge: restored > original InChI *and* */ + /* removed H: restored < original InChI */ + /* (c) restored t-groups have less endpnoits than in orig InChI */ + /* O = O, S, Se, Te; N = N */ + /* Solution: move (+) from -O(+)= to -N< */ + /*--------------------------------------------------------------*/ + int num_SB_Neutr = 0, num_DB_Charged = 0, iat; + short iat_SB_Neutr[MAX_DIFF_FIXH], iat_DB_Charged[MAX_DIFF_FIXH]; + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; + cur_success = 0; + /* search for -O(+)= */ + /* search for -N< */ + for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ + iat = nCanon2AtnoRevrs[i]; + if ( /* -O(+)= in restored atom: charge=1, has no H, a double bond */ + num_DB_Charged < MAX_DIFF_FIXH && + at2[iat].charge == 1 && !at2[iat].num_H && + at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && + (pVA[iat].cNumValenceElectrons == 6 ) && + /* in orig.InChI: an endpoint, has fixed-H */ + /*pStruct->endpoint[i] &&*/ + !(pStruct->fixed_H && pStruct->fixed_H[i]) && + !(nMobHInChI && nMobHInChI[i] ) && + /* has (+) edge */ + (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && 0 == pBNS->edge[e].forbidden ) { + + iat_DB_Charged[num_DB_Charged ++] = iat; + /* + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + */ + } else + if ( /* -N< in restored atom: charge=0, has no H, has no double bond, N only */ + num_SB_Neutr < MAX_DIFF_FIXH && + at2[iat].charge == 0 && !at2[iat].num_H && + at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && + (pVA[iat].cNumValenceElectrons == 5 && + pVA[iat].cPeriodicRowNumber == 1 ) && + /* in orig.InChI: an endpoint, has fixed-H */ + /*pStruct->endpoint[i] &&*/ + !(pStruct->fixed_H && pStruct->fixed_H[i]) && + !(nMobHInChI && nMobHInChI[i] ) && + /* has (+) edge */ + (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && 0 == pBNS->edge[e].forbidden ) { + + iat_SB_Neutr[num_SB_Neutr ++] = iat; + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + if ( num_try = inchi_min( num_SB_Neutr, num_DB_Charged ) ) { + /* detected; attempt to fix */ + BNS_VERTEX *pv1n, *pv2n; + BNS_EDGE *pe1n, *pe2n; + Vertex v1n, v2n; + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + delta = 1; + for ( i = 0; i < num_DB_Charged && cur_success < num_try; i ++ ) { + iat = iat_DB_Charged[i]; + pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; + if ( pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + for ( j = pv1->num_adj_edges-1; 0 <= j; j -- ) { + pe1n = pBNS->edge + pv1->iedge[j]; + if ( pe1n->flow && !pe1n->forbidden ) { + pv1n = pBNS->vert + (v1n = pe1n->neighbor12 ^ v1); + break; + } + } + if ( j < 0 ) + continue; /* not found */ + + for ( j = pv2->num_adj_edges-1; 0 <= j; j -- ) { /* was -2; changed 2006-2-28 12:35pm*/ + pe2n = pBNS->edge + pv2->iedge[j]; + if ( pe2n->flow && !pe2n->forbidden ) { + pv2n = pBNS->vert + (v2n = pe2n->neighbor12 ^ v2); + break; + } + } + if ( j < 0 ) + continue; /* not found */ + + pe->flow += delta; + pe1n->flow -= delta; + pe2n->flow -= delta; + pv1n->st_edge.flow -= delta; + pv2n->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1n && vPathStart == v2n || + vPathEnd == v2n && vPathStart == v1n) && + (nDeltaCharge == 0 || nDeltaCharge == 1) ) { + /* Moved charge from O(+) to -N< => nDeltaCharge == 1 or 0 if pe2n = -N< charge edge */ + /* Flow change on pe (+)charge edge (atom NH2) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 15 */ + } + } else { + pe->flow -= delta; + pe1n->flow += delta; + pe2n->flow += delta; + pv1n->st_edge.flow += delta; + pv2n->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } + CurrEdges.num_edges = 0; /* clear current edge list */ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + + if ( pc2i->nNumTgDiffMinus ) { + /*----------------------------------------------------------------*/ + /* case 16: restored: O=X-NH(-) orig.: O(-)-X=NH */ + /* t-group: (H,-) (2H) */ + /* O(-) = S, Se, Te; N = N; */ + /* Solution: move (-) from O(-) to -NH(-) */ + /*----------------------------------------------------------------*/ + int num_SB_N_Minus = 0, num_DB_O_Neutr = 0, iat, itg; + short iat_SB_N_Minus[MAX_DIFF_FIXH], iat_DB_O_Neutr[MAX_DIFF_FIXH]; + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; + cur_success = 0; + for ( itg = 0; itg < pStruct->ti.num_t_groups && itg < pStruct->One_ti.num_t_groups; itg ++ ) { + if ( pStruct->ti.t_group[itg].nNumEndpoints != pStruct->One_ti.t_group[itg].nNumEndpoints || + pStruct->ti.t_group[itg].num[1] >= pStruct->One_ti.t_group[itg].num[1] ) { + continue; + } + CurrEdges.num_edges = num_SB_N_Minus = num_DB_O_Neutr = 0; + cur_success = 0; + for ( j = 0, k = pStruct->One_ti.t_group[itg].nFirstEndpointAtNoPos; + j < pStruct->One_ti.t_group[itg].nNumEndpoints; j ++ ) { + i = pStruct->One_ti.nEndpointAtomNumber[k+j]; /* canonical number in restored struct. */ + iat = nCanon2AtnoRevrs[i]; + if ( /* in restored atom: charge=0, has no H, has double bond, O, S, Se, Te */ + num_DB_O_Neutr < MAX_DIFF_FIXH && + at2[iat].charge == 0 && !at2[iat].num_H && + at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && + pVA[iat].cNumValenceElectrons == 6 && + /* in orig.InChI: an endpoint, may have fixed-H */ + pStruct->endpoint[i] && + /*!(pStruct->fixed_H && pStruct->fixed_H[i]) &&*/ + !(nMobHInChI && nMobHInChI[i] ) && + /* has (-) edge */ + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { + + iat_DB_O_Neutr[num_DB_O_Neutr ++] = iat; + + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } else + if ( /* in restored atom: charge=-1, has H, has double bond, N */ + num_SB_N_Minus < MAX_DIFF_FIXH && + at2[iat].charge == -1 && at2[iat].num_H && + at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && + pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1 && + /* in orig.InChI: an endpoint, has no fixed-H */ + pStruct->endpoint[i] && + (pStruct->fixed_H && pStruct->fixed_H[i]) && + !(nMobHInChI && nMobHInChI[i] ) && + /* has (-) edge */ + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && + 0 == pBNS->edge[e].forbidden ) { + + iat_SB_N_Minus[num_SB_N_Minus ++] = iat; + /* + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + */ + } + } + if ( num_try = inchi_min( num_SB_N_Minus, num_DB_O_Neutr ) ) { + /* detected; attempt to fix */ + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + /* allow stereobonds in rings change */ + /* + if ( forbidden_stereo_edge_mask ) + RemoveForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask ); + */ + delta = 1; + for ( i = 0; i < num_SB_N_Minus && cur_success < num_try; i ++ ) { + iat = iat_SB_N_Minus[i]; + pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + /*pe->forbidden |= forbidden_edge_mask;*/ + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { + /* Moved (-) charge to =O => nDeltaCharge == 1 */ + /* Flow change on pe (-)charge edge (atom -NH(-)) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 16 */ + } + } else { + pe->forbidden &= forbidden_edge_mask_inv; + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + /* + if ( forbidden_stereo_edge_mask ) + SetForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask ); + */ + } + CurrEdges.num_edges = 0; /* clear current edge list */ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + } + + if ( pc2i->nNumRemHInChI < pc2i->nNumRemHRevrs ) { + /*--------------------------------------------------------------*/ + /* case 17: restored: OH(+)=AB-O- orig. HO-AB=O(+)- */ + /* number of removed H: n+m n */ + /* OH(+) = N, O, S, Se; -O- = P,As,O,S,Se,Te,F,Cl,Br,I */ + /* Solution: move (+) from OH(+) to -O- */ + /*--------------------------------------------------------------*/ + int num_SB_Neutr = 0, num_DB_Charged = 0, iat; + short iat_SB_Neutr[MAX_DIFF_FIXH], iat_DB_Charged[MAX_DIFF_FIXH]; + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; + cur_success = 0; + for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ + iat = nCanon2AtnoRevrs[i]; + if ( /* in restored atom: charge=+1, has H, has double bond, N, O, S, Se, Te */ + num_DB_Charged < MAX_DIFF_FIXH && + at2[iat].charge == 1 && at2[iat].num_H && + at2[iat].valence < at2[iat].chem_bonds_valence && !pVA[iat].cMetal && + (pVA[iat].cNumValenceElectrons == 6 || + pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1) && + /* has (+) edge */ + (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { + + iat_DB_Charged[num_DB_Charged ++] = iat; + /* + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + */ + } else + if ( /* in restored atom: charge=0, has no H, has no double bond, N, P, O, S, Se, Te */ + num_SB_Neutr < MAX_DIFF_FIXH && + at2[iat].charge == 0 && !at2[iat].num_H && + at2[iat].valence == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && + (pVA[iat].cNumValenceElectrons == 6 || pVA[iat].cNumValenceElectrons == 7 || + pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber > 1 ) && + /* in orig.InChI: not an endpoint */ + !pStruct->endpoint[i] && + !(pStruct->fixed_H && pStruct->fixed_H[i]) && + !(nMobHInChI && nMobHInChI[i] ) && + /* has (+) edge */ + (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && + 0 == pBNS->edge[e].forbidden ) { + + iat_SB_Neutr[num_SB_Neutr ++] = iat; + + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + if ( num_try = inchi_min( num_SB_Neutr, num_DB_Charged ) ) { + BNS_VERTEX *pv1n, *pv2n; + BNS_EDGE *pe1n, *pe2n; + Vertex v1n, v2n; + + num_try = inchi_min( num_try, pc2i->nNumRemHRevrs-pc2i->nNumRemHInChI); + /* detected; attempt to fix */ + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + delta = 1; + for ( i = 0; i < num_DB_Charged && cur_success < num_try; i ++ ) { + iat = iat_DB_Charged[i]; + pe = pBNS->edge + pVA[iat].nCPlusGroupEdge-1; + if ( pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + for ( j = pv1->num_adj_edges-1; 0 <= j; j -- ) { + pe1n = pBNS->edge + pv1->iedge[j]; + if ( pe1n->flow && !pe1n->forbidden ) { + pv1n = pBNS->vert + (v1n = pe1n->neighbor12 ^ v1); + break; + } + } + if ( j < 0 ) + continue; /* not found */ + + for ( j = pv2->num_adj_edges-1; 0 <= j; j -- ) { /* was -2; changed 2006-2-28 12:35pm*/ + pe2n = pBNS->edge + pv2->iedge[j]; + if ( pe2n->flow && !pe2n->forbidden ) { + pv2n = pBNS->vert + (v2n = pe2n->neighbor12 ^ v2); + break; + } + } + if ( j < 0 ) + continue; /* not found */ + + pe->flow += delta; + pe1n->flow -= delta; + pe2n->flow -= delta; + pv1n->st_edge.flow -= delta; + pv2n->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1n && vPathStart == v2n || + vPathEnd == v2n && vPathStart == v1n) && + (nDeltaCharge == 0 || nDeltaCharge == 1) ) { + /* Moved charge from OH(+) to -O- => nDeltaCharge == 1 or 0 if pe2n = -O- charge edge */ + /* Flow change on pe (+)charge edge (atom OH(+)) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 17 */ + } + } else { + pe->flow -= delta; + pe1n->flow += delta; + pe2n->flow += delta; + pv1n->st_edge.flow += delta; + pv2n->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } + CurrEdges.num_edges = 0; /* clear current edge list */ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + + if ( (pc2i->nNumTgInChI && pStruct->endpoint && + pc2i->nNumTgMInChI > pc2i->nNumTgMRevrs && pc2i->nNumEndpInChI > pc2i->nNumEndpRevrs ) ) { + /*-----------------------------------------------------------------*/ + /* */ + /* case 18: restored:-N=AB-X -(-)N-AB-X(+) */ + /* FixH: 0 0 0 0 */ + /* MobH: 0 0 0 0 */ + /* non non taut non */ + /* taut taut taut */ + /* X = any heteroatom N=N */ + /* t-group in original has (Hn,-m) in the restored: (Hn,-m+1) */ + /* same num_H and more (-) than in the restored structure */ + /* atom X is not taut in both */ + /* Solution: separate charges between -N(III)= and X */ + /*-----------------------------------------------------------------*/ + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && + pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; + int iat, e1, itg, max_success; + CurrEdges.num_edges = 0; + cur_success = 0; + ret = 0; + /* search for -N= */ + for ( itg = 0; itg < pStruct->ti.num_t_groups && itg < pStruct->One_ti.num_t_groups; itg ++ ) { + if ( pStruct->ti.t_group[itg].nNumEndpoints <= pStruct->One_ti.t_group[itg].nNumEndpoints || + pStruct->ti.t_group[itg].num[1] <= pStruct->One_ti.t_group[itg].num[1] ) { + continue; + } + CurrEdges.num_edges = 0; + cur_success = 0; + for ( j = 0, k = pStruct->ti.t_group[itg].nFirstEndpointAtNoPos; + j < pStruct->ti.t_group[itg].nNumEndpoints; j ++ ) { + i = pStruct->ti.nEndpointAtomNumber[k+j]; /* canonical number in restored struct. */ + iat = nCanon2AtnoRevrs[i]; + if ( !pStruct->endpoint[i] || !at_Mobile_H_Revrs || at_Mobile_H_Revrs[iat].endpoint || + pVA[i].cNumValenceElectrons != 5 || pVA[i].cPeriodicRowNumber != 1 || + 2 != at2[iat].valence || at2[iat].num_H || at2[iat].radical || + 0 <= (e1=pVA[iat].nCPlusGroupEdge-1) && !pBNS->edge[e1].flow || + 0 > (e=pVA[iat].nCMinusGroupEdge-1) || pBNS->edge[e].forbidden || pBNS->edge[e].flow ) { + continue; + } + /* found -N= */ + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + if ( !(max_success = CurrEdges.num_edges) ) { + goto exit_case_18; + } + /* search for X */ + for ( i = 0; i < pStruct->num_atoms && cur_success < max_success; i ++ ) { + iat = nCanon2AtnoRevrs[i]; + if ( pStruct->endpoint[i] || !pVA[i].cNumValenceElectrons || pVA[i].cNumValenceElectrons == 4 || + at2[iat].num_H || at2[iat].radical || + 0 <= (e1=pVA[iat].nCMinusGroupEdge-1) && !pBNS->edge[e1].flow || + 0 > (e=pVA[iat].nCPlusGroupEdge-1) || pBNS->edge[e].forbidden || pBNS->edge[e].flow != 1 ) { + continue; + } + /* try to move the charge */ + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + SetForbiddenEdgeMask( pBNS, &OtherNFlowerEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + + pe = pBNS->edge + e; + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + delta = 1; + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { + /* Created (-) charge on -N= => nDeltaCharge == 1 */ + /* Flow change on pe (+)charge edge (atom X) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 18 */ + } + } else { + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + } +exit_case_18: + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &OtherNFlowerEdges, forbidden_edge_mask ); + CurrEdges.num_edges = 0; /* clear current edge list */ + if ( ret < 0 ) { + goto exit_function; + } + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + if ( pc2i->len_c2at >= 1 ) { + /*--------------------------------------------------------------*/ + /* case 19 restored: M--OH original: M(-)==OH(+) */ + /* FixH: metal 0 1 */ + /* MobH: 1 0 */ + /* O = O, S, Se, Te; not taut. in InChI */ + /* In restored structure has H; tautomeric or not tautomeric */ + /* Solution: move (+) from -OH to M; charhe on M may vary */ + /*--------------------------------------------------------------*/ + int iat; + EdgeIndex eOHPlus, eMPlus, eMMinus, eOMBond; + BNS_EDGE *peOHPlus, *peMPlus, *peMMinus, *peOMBond; + int iatMetal, ChargeOnMetal, DeltaChargeExpected; + cur_success = 0; + num_zero_ret = 0; + for ( i = 0; i < pc2i->len_c2at; i ++ ) { + iat = pc2i->c2at[i].atomNumber; + if ( /* orig. InChI info: =NH2(+), =OH(+) */ + (pc2i->c2at[i].nValElectr == 6 ) /* N, O, S, Se, Te */ && + /*!pc2i->c2at[i].endptInChI &&*/ /* <=== relaxation */ + (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && + pc2i->c2at[i].nFixHInChI == 1 && pc2i->c2at[i].nMobHInChI == 0 && + /* reversed structure info: */ + pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 1 && + pc2i->c2at[i].nAtChargeRevrs == 0 && at2[iat].num_H && + at2[iat].valence == 1 && + at2[iat].valence == at2[iat].chem_bonds_valence && + /* metal atom */ + pVA[iatMetal=at2[iat].neighbor[0]].cMetal && + (eMPlus=pVA[iatMetal].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[eMPlus].forbidden && + (eMMinus=pVA[iatMetal].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[eMMinus].forbidden && + !pBNS->edge[eOMBond=pBNS->vert[iat].iedge[0]].forbidden + ) { + + /* -OH charge edges */ + if ( ret = AddToEdgeList( &CurrEdges, iat, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + if ( CurrEdges.num_edges ) { + /* detected; fix */ + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + SetForbiddenEdgeMask( pBNS, &NFlowerEdges, forbidden_edge_mask ); + SetForbiddenEdgeMask( pBNS, &AllBondEdges, forbidden_edge_mask ); + for ( i = 0; i < CurrEdges.num_edges; i ++ ) { + /* v1 is -OH, v2 is adjacent to it Metal */ + iat = CurrEdges.pnEdges[i]; + iatMetal = at2[iat].neighbor[0]; + peOHPlus = pBNS->edge + (eOHPlus = pVA[iat].nCPlusGroupEdge-1); + peMPlus = pBNS->edge + (eMPlus = pVA[iatMetal].nCPlusGroupEdge-1); + peMMinus = pBNS->edge + (eMMinus = pVA[iatMetal].nCMinusGroupEdge-1); + peOMBond = pBNS->edge + (eOMBond =pBNS->vert[iat].iedge[0]); + /* remove forbidden edge masks */ + peMPlus->forbidden &= forbidden_edge_mask_inv; + peMMinus->forbidden &= forbidden_edge_mask_inv; + peOMBond->forbidden &= forbidden_edge_mask_inv; + + ChargeOnMetal = (peMPlus->cap - peMPlus->flow) - peMMinus->flow; + if ( 1 == ChargeOnMetal ) { + /* We are going to subtract 1 from the charge on Metal */ + /* Added (+)charge to -OH is not known to RunBnsTestOnce() */ + DeltaChargeExpected = -1; /* charge will become = 0 */ + } else + if ( 0 == ChargeOnMetal ) { + DeltaChargeExpected = 1; /* charge on Metal will be created */ + } else { + DeltaChargeExpected = 0; + } + + delta = 1; + pe = peOHPlus; + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->flow -= delta; /* remove (-) from AB-O(-) */ + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == DeltaChargeExpected ) { + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 19 */ + } + } else { + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + /* set forbidden edge masks back */ + peMPlus->forbidden |= forbidden_edge_mask; + peMMinus->forbidden |= forbidden_edge_mask; + peOMBond->forbidden |= forbidden_edge_mask; + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &NFlowerEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &AllBondEdges, forbidden_edge_mask ); + + CurrEdges.num_edges = 0; /* clear current edge list */ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + } + if ( pc2i->len_c2at > 1 && pc2i->nNumTgRevrs && pc2i->nNumTgInChI) { + /*--------------------------------------------------------------*/ + /* case 20: restored: O(-)-AB=N- original: O=AB-N(-)- */ + /* FixH: 0 0 0 -1 */ + /* MobH: 0 0 0 1 */ + /* taut non-taut non-taut taut */ + /* or taut no H */ + /* no H */ + /* O = O, S, Se; N = N, O, S, Se, Te; */ + /* restored atoms are taut/non-taut; original are opposite. */ + /* Solution: move (-) from O(-) to =N- */ + /*--------------------------------------------------------------*/ + int num_SB_O_Minus = 0, num_DB_N = 0, iat; + short iat_SB_O_Minus[MAX_DIFF_FIXH], iat_DB_N[MAX_DIFF_FIXH]; + + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + /* + inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && + pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; + S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; + */ + cur_success = 0; + CurrEdges.num_edges = 0; /* clear current edge list */ + for ( i = 0; i < pc2i->len_c2at; i ++ ) { + iat = pc2i->c2at[i].atomNumber; + if ( /* orig. InChI info: =O or -N= */ + num_DB_N < MAX_DIFF_FIXH && + pc2i->c2at[i].endptInChI && + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + pBNS->edge[e].flow == 0 && + pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI == 0 && + /* if more than 1 t-group are in orig. InChI then do not move (-) to N */ + (pc2i->nNumTgInChI == 1 || pc2i->c2at[i].nValElectr == 6) && + /* reversed structure info: */ + !pc2i->c2at[i].endptRevrs && + pc2i->c2at[i].nFixHRevrs == 0 && /*pc2i->c2at[i].nMobHRevrs == 0 &&*/ + pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && + at2[iat].valence + 1 == at2[iat].chem_bonds_valence ) { + iat_DB_N[num_DB_N ++] = iat; + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } else + if ( /* orig. InChI info: -O(-) */ + num_SB_O_Minus < MAX_DIFF_FIXH && + !pc2i->c2at[i].endptInChI && + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + pBNS->edge[e].flow == 1 && + pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI == 0 && + pc2i->c2at[i].nValElectr == 6 && + /* reversed structure info: */ + pc2i->c2at[i].endptRevrs && + pc2i->c2at[i].nFixHRevrs == 0 && pc2i->c2at[i].nMobHRevrs == 0 && + pc2i->c2at[i].nAtChargeRevrs == -1 && !at2[iat].num_H && + at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 1 ) { + iat_SB_O_Minus[num_SB_O_Minus ++] = iat; + } + } + if ( !num_DB_N ) { + /* search among N that are tautomeric in both cases */ + for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ + if ( !pStruct->endpoint[i] ) { + continue; + } + iat = nCanon2AtnoRevrs[i]; + if ( /* in restored atom O: charge=-1, no H, has no double bond, endpoint */ + num_DB_N < MAX_DIFF_FIXH && + at2[iat].charge == 0 && !at2[iat].num_H && + at2[iat].valence + 1 == at2[iat].chem_bonds_valence && !pVA[iat].cMetal && + /* in orig.InChI: an endpoint, has no H */ + !(pStruct->fixed_H && pStruct->fixed_H[i]) && + /*!(nMobHInChI && nMobHInChI[i] ) &&*/ + /* has (-) edge */ + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + !pBNS->edge[e].flow ) { + + iat_DB_N[num_DB_N ++] = iat; + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + } + if ( num_try = inchi_min( num_SB_O_Minus, num_DB_N ) ) { + /* detected; attempt to fix */ + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + delta = 1; + for ( i = 0; i < num_SB_O_Minus && cur_success < num_try; i ++ ) { + iat = iat_SB_O_Minus[i]; + pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { + /* Added (-) charge to =N- => nDeltaCharge == 1 */ + /* Flow change on pe (-)charge edge (atom -O(-)) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 20 */ + } + } else { + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } + CurrEdges.num_edges = 0; /* clear current edge list */ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + if ( pc2i->len_c2at && pc2i->nNumTgRevrs && pc2i->nNumTgHInChI && pStruct->endpoint ) { + /*--------------------------------------------------------------*/ + /* O(-) O */ + /* | || */ + /* case 21: restored: R=S=O original: R-S=O */ + /* | | */ + /* O(-) O(-) */ + /* All O are taut R is not taut */ + /* */ + /* In addition, another atom O that should have been tautomeric */ + /* or has H(+) added in Mobile-H layer is not like that */ + /* O = O, S, Se; S=S, Se, Te */ + /* Solution: move (-) from O(-) to =O */ + /* these atoms are tautomeric in restored structure */ + /*--------------------------------------------------------------*/ + int num_SB_O_Minus = 0, num_DB_O = 0, iat, iS; + short iat_SB_O_Minus[MAX_DIFF_FIXH], iat_Central[MAX_DIFF_FIXH], iat_DB_O[MAX_DIFF_FIXH]; + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && + pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; + /* + S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; + */ + CurrEdges.num_edges = 0; /* clear current edge list */ + cur_success = 0; + for ( i = 0; i < pc2i->len_c2at; i ++ ) { + iat = pc2i->c2at[i].atomNumber; + if ( /* orig. InChI info: =O */ + num_DB_O < MAX_DIFF_FIXH && + pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ && + (pc2i->c2at[i].endptInChI || pc2i->c2at[i].nMobHInChI) && + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + pc2i->c2at[i].nFixHInChI == 0 && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ + /* reversed structure info: */ + !(pc2i->c2at[i].endptRevrs || pc2i->c2at[i].nMobHRevrs) && + pc2i->c2at[i].nFixHRevrs == 0 && + pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && + at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 ) { + iat_DB_O[num_DB_O ++] = iat; + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + for ( i = 0; num_DB_O && i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ + if ( !pStruct->endpoint[i] ) { + continue; + } + iat = nCanon2AtnoRevrs[i]; + if ( /* in restored atom O: charge=-1, no H, has no double bond, endpoint */ + num_SB_O_Minus < MAX_DIFF_FIXH && + at2[iat].charge == -1 && !at2[iat].num_H && + at2[iat].valence == 1 && at2[iat].chem_bonds_valence && !pVA[iat].cMetal && + pVA[iat].cNumValenceElectrons == 6 && + (at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint) && + /* in orig.InChI: an endpoint, has no H */ + !(pStruct->fixed_H && pStruct->fixed_H[i]) && + /*!(nMobHInChI && nMobHInChI[i] ) &&*/ + /* has (-) edge */ + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + pBNS->edge[e].flow ) { + int nNumTautSB = 0, nNumTautDB = 0, nNumOtherDB = 0, nNumOtherSB = 0, nNumOthers = 0, nNumNegEndp = 0; + /* traverse neighbors of the centerpoint iS */ + iS = at2[i].neighbor[0]; + for ( j = 0; j < num_SB_O_Minus; j ++ ) { + if ( iat_Central[j] == iS ) + break; + } + if ( j < num_SB_O_Minus ) { + continue; /* have already been there */ + } + for ( j = 0; j < at[iS].valence; j ++ ) { + int bond_type = at2[iS].bond_type[j]; + k = at2[iS].neighbor[j]; + if ( k == i ) { + continue; + } + if ( pStruct->endpoint[k] == pStruct->endpoint[i] ) { + nNumTautSB += ( bond_type == BOND_TYPE_SINGLE ); + nNumTautDB += ( bond_type == BOND_TYPE_DOUBLE ); + } else + if ( bond_type == BOND_TYPE_DOUBLE ) { + nNumOtherDB ++; + } else + if ( bond_type == BOND_TYPE_SINGLE ) { + nNumOtherSB ++; + } else { + nNumOthers ++; + } + if ( at2[k].endpoint == at2[i].endpoint && at2[k].valence == 1 && + at2[k].charge == -1 && pVA[k].cNumValenceElectrons == 6 ) { + nNumNegEndp ++; + } + } + if ( !nNumTautSB ) { + continue; + } + if ( !( nNumOtherDB && nNumTautDB ) ) { + continue; /* ignore */ + } + + iat_SB_O_Minus[num_SB_O_Minus] = iat; + iat_Central[num_SB_O_Minus ++] = iS; + } + } + if ( num_try = inchi_min( num_SB_O_Minus, num_DB_O ) ) { + /* detected; attempt to fix */ + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + delta = 1; + for ( i = 0; i < num_SB_O_Minus && cur_success < num_try; i ++ ) { + iat = iat_SB_O_Minus[i]; + pe = pBNS->edge + pVA[iat].nCMinusGroupEdge-1; + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->forbidden |= forbidden_edge_mask; + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { + /* Added (-) charge to =O => nDeltaCharge == 1 */ + /* Flow change on pe (-)charge edge (atom -N(-)-) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 21 */ + } + } else { + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } + CurrEdges.num_edges = 0; /* clear current edge list */ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + + if ( pc2i->len_c2at && pc2i->nNumTgRevrs && pc2i->nNumEndpInChI < pc2i->nNumEndpRevrs ) { + /*--------------------------------------------------------------*/ + /* O O */ + /* || || */ + /* case 21a:restored: R=S-R' =X original: R-S-R' -X(-) */ + /* | || */ + /* O(-) O(-) */ + /* All O and X are taut O and X are not taut */ + /* it is possible that X is R */ + /* */ + /* O = O, S, Se; S=S, Se, Te; X = N, O, S, Se, Te */ + /* Solution: move (-) from O(-) to =X */ + /* these atoms are tautomeric in restored structure */ + /*--------------------------------------------------------------*/ + int iat, iS; + /* + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + */ + inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && + pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; + /* + S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; + */ + EDGE_LIST OtherSO, CentralS, SOMinus, MinusAcceptord; + CurrEdges.num_edges = 0; /* clear current edge list */ + AllocEdgeList( &OtherSO, EDGE_LIST_CLEAR ); + AllocEdgeList( &CentralS, EDGE_LIST_CLEAR ); + AllocEdgeList( &SOMinus, EDGE_LIST_CLEAR ); + AllocEdgeList( &MinusAcceptord, EDGE_LIST_CLEAR ); + cur_success = 0; + if ( !at_Mobile_H_Revrs ) { + goto exit_case_21a; + } + for ( i = 0; i < pc2i->len_c2at; i ++ ) { + iat = pc2i->c2at[i].atomNumber; + if ( /* orig. InChI info: -X(-) */ + /*num_DB_O < MAX_DIFF_FIXH &&*/ + /*pc2i->c2at[i].nValElectr == 6 */ /* O, S, Se, Te */ + !pc2i->c2at[i].endptInChI && + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + pc2i->c2at[i].nFixHInChI == 0 && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ + /* reversed structure info: */ + (pc2i->c2at[i].endptRevrs || pc2i->c2at[i].nMobHRevrs) && + pc2i->c2at[i].nFixHRevrs == 0 && + /*pc2i->c2at[i].nAtChargeRevrs == 0 &&*/ !at2[iat].num_H ) { + if ( pVA[iat].cNumValenceElectrons == 6 && at2[iat].charge == -1 && + pBNS->edge[e].flow && + at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 1 && + pVA[iS=(int)at2[iat].neighbor[0]].cNumValenceElectrons == 6 && pVA[iS].cPeriodicRowNumber > 1 && + at2[iS].valence >= 4 ) { + /* a candidate for S in -SO2- */ + int nNumTautSB = 0, nNumTautDB = 0, nNumOtherDB = 0, nNumOtherSB = 0; + int nNumOthers = 0, nNumNegEndp = 0, nNumEndpO = 0; + /* check whether we have already found it */ + if ( 0 <= FindInEdgeList( &CentralS, iS ) ) { + continue; + } + for ( j = 0; j < at[iS].valence; j ++ ) { + int bond_type = at2[iS].bond_type[j]; + k = at2[iS].neighbor[j]; + if ( k == iat ) { + continue; + } + if ( pc2i->c2at[i].endptRevrs == at_Mobile_H_Revrs[k].endpoint && !at2[k].endpoint ) { + nNumTautSB += ( bond_type == BOND_TYPE_SINGLE ); + nNumTautDB += ( bond_type == BOND_TYPE_DOUBLE ); + nNumEndpO += (pVA[k].cNumValenceElectrons == 6 && at2[k].valence == 1); + } else + if ( bond_type == BOND_TYPE_DOUBLE ) { + nNumOtherDB ++; + } else + if ( bond_type == BOND_TYPE_SINGLE ) { + nNumOtherSB ++; + } else { + nNumOthers ++; + } + if ( at2[k].endpoint == at2[i].endpoint && at2[k].valence == 1 && + at2[k].charge == -1 && pVA[k].cNumValenceElectrons == 6 ) { + nNumNegEndp ++; + } + } + if ( !nNumEndpO ) { + continue; + } + if ( nNumTautSB + nNumTautDB + nNumOtherDB <= nNumEndpO ) { + continue; /* ignore */ + } + /* collect double bond taut =O */ + for ( j = 0; j < at[iS].valence; j ++ ) { + int bond_type = at2[iS].bond_type[j]; + k = at2[iS].neighbor[j]; + if ( pc2i->c2at[i].endptRevrs == at_Mobile_H_Revrs[k].endpoint && + !at2[k].endpoint && pVA[k].cNumValenceElectrons == 6 && at2[k].valence == 1 && + 0 <= (e=pVA[k].nCMinusGroupEdge-1) && !pBNS->edge[e].forbidden ) { + if ( bond_type == BOND_TYPE_DOUBLE && !at2[k].charge && !pBNS->edge[e].flow) { + /* charges to be unchanged */ + if ( ret = AddToEdgeList( &OtherSO, e, INC_ADD_EDGE ) ) { + goto exit_case_21a; + } + } else + if ( bond_type == BOND_TYPE_SINGLE && at2[k].charge == -1 && pBNS->edge[e].flow ) { + /* charges to be removed */ + if ( ret = AddToEdgeList( &SOMinus, e, INC_ADD_EDGE ) ) { + goto exit_case_21a; + } + } + } + } + if ( ret = AddToEdgeList( &CentralS, iS, INC_ADD_EDGE ) ) { + goto exit_case_21a; + } + } else + if ( at2[iat].charge == 0 && !pBNS->edge[e].flow && + at2[iat].valence + 1 == at2[iat].chem_bonds_valence ) { + /* changeable charges */ + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + } + /* remove unchangeable from changeable */ + for ( i = 0; i < OtherSO.num_edges; i ++ ) { + RemoveFromEdgeListByValue( &CurrEdges, OtherSO.pnEdges[i] ); + } + + if ( num_try = inchi_min( SOMinus.num_edges, CurrEdges.num_edges ) ) { + /* detected; attempt to fix */ + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + delta = 1; + for ( i = 0; i < SOMinus.num_edges && cur_success < num_try; i ++ ) { + pe = pBNS->edge + SOMinus.pnEdges[i]; + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + /*pe->forbidden |= forbidden_edge_mask;*/ + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { + /* Added (-) charge to =O => nDeltaCharge == 1 */ + /* Flow change on pe (-)charge edge (atom -N(-)-) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 21a */ + } + } else { + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } +exit_case_21a: + CurrEdges.num_edges = 0; /* clear current edge list */ + AllocEdgeList( &OtherSO, EDGE_LIST_FREE ); + AllocEdgeList( &CentralS, EDGE_LIST_FREE ); + AllocEdgeList( &SOMinus, EDGE_LIST_FREE ); + AllocEdgeList( &MinusAcceptord, EDGE_LIST_FREE ); + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + + if ( pc2i->len_c2at ) { + /*------------------------------------------------------------------*/ + /* case 22: restored: N(-)=N(+)=C...=O orig: N#N-N=...-O(-) */ + /* im InChI -O(-) may have H(+) added by Normalization */ + /* or may be tautomeric */ + /* Solution: move (-) from N(-) to =O */ + /* */ + /*------------------------------------------------------------------*/ + int num_DB_O = 0, iat; + short iat_DB_O[MAX_DIFF_FIXH]; + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && + pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; + int iN2, iC; + BNS_EDGE *peDB_O_Minus; + /* + S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; + */ + CurrEdges.num_edges = 0; /* clear current edge list */ + cur_success = 0; + for ( i = 0; i < pc2i->len_c2at; i ++ ) { + iat = pc2i->c2at[i].atomNumber; + if ( /* orig. InChI info: =O */ + num_DB_O < MAX_DIFF_FIXH && + pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ && + (pc2i->c2at[i].endptInChI || pc2i->c2at[i].nMobHInChI) && + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + pc2i->c2at[i].nFixHInChI == 0 && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ + /* reversed structure info: */ + !(pc2i->c2at[i].endptRevrs || pc2i->c2at[i].nMobHRevrs) && + pc2i->c2at[i].nFixHRevrs == 0 && + pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && + at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 ) { + iat_DB_O[num_DB_O ++] = iat; + } + } + for ( i = 0; num_DB_O && i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ + iat = nCanon2AtnoRevrs[i]; + if ( /* in restored atom O: charge=-1, no H, has no double bond, endpoint */ + at2[iat].charge == -1 && !at2[iat].num_H && + at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 && !pVA[iat].cMetal && + pVA[iat].cNumValenceElectrons == 5 && + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + pBNS->edge[e].flow && + !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint) && + pVA[iN2=at2[iat].neighbor[0]].cNumValenceElectrons == 5 && + at2[iat].bond_type[0] == BOND_TYPE_DOUBLE && + at2[iN2].charge == 1 && at2[iN2].valence == 2 && at2[iN2].chem_bonds_valence == 4 && + pVA[iC=at2[iN2].neighbor[at2[iN2].neighbor[0]==iN2]].cNumValenceElectrons == 4 ) { + + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + if ( num_try = inchi_min( CurrEdges.num_edges, num_DB_O ) ) { + /* detected; attempt to fix */ + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + delta = 1; + for ( i = 0; i < num_DB_O && cur_success < num_try; i ++ ) { + iat = iat_DB_O[i]; + + peDB_O_Minus = pBNS->edge + (pVA[iat].nCMinusGroupEdge-1); + pe = pBNS->edge + pBNS->vert[iat].iedge[0]; + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->forbidden |= forbidden_edge_mask; + peDB_O_Minus->forbidden &= forbidden_edge_mask_inv; + + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { + /* Added (-) charge to =O and removed from =N(-) => nDeltaCharge == 0 */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 22 */ + } + } else { + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + INCHI_HEAPCHK + pe->forbidden &= forbidden_edge_mask_inv; + peDB_O_Minus->forbidden |= forbidden_edge_mask; + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } + CurrEdges.num_edges = 0; /* clear current edge list */ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + if ( pc2i->len_c2at && pc2i->nNumTgInChI == 1 ) { + /*------------------------------------------------------------------*/ + /* case 23: -NO2 are to be tautomeric but they are not AND */ + /* InChI has a SINGLE tautomeric group */ + /* */ + /* (-)O (-)O */ + /* Solution: convert \ \ */ + /* N-X=...-Z(-) => N(+)=X- ...=Z */ + /* // / */ + /* O (-)O */ + /* */ + /* O O */ + /* or \\ \\ */ + /* N-X=...-Z(-) => N=X- ...=Z */ + /* // / */ + /* O (-)O */ + /* */ + /* */ + /* (a) move (-) from other tautomeric atom to O in O=N-X */ + /* or from other atom that has to be tautomeric */ + /* but is not */ + /* (b) create (+) [ion pair creation] on N as in */ + /* */ + /* OH OH */ + /* / / */ + /* -C=N => =C-N(+) */ + /* \\ \\ */ + /* O O */ + /* */ + /*------------------------------------------------------------------*/ + int num_DB_O = 0, iat; + short iat_DB_O[MAX_DIFF_FIXH], iat_NO2[MAX_DIFF_FIXH]; + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && + pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; + /* + inp_ATOM *atfMobile_H_Revrs = (pStruct->pOne_norm_data[1] && + pStruct->pOne_norm_data[1]->at_fixed_bonds)? + pStruct->pOne_norm_data[1]->at_fixed_bonds : NULL; + */ + S_CHAR *num_Fixed_H_Revrs = pStruct->pOneINChI[0]->nNum_H_fixed? pStruct->pOneINChI[0]->nNum_H_fixed : NULL; + S_CHAR *pnMobHRevrs = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNum_H)? + pStruct->pOneINChI[1]->nNum_H : + (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)? + pStruct->pOneINChI[0]->nNum_H : NULL; + int iN, one_success; + BNS_EDGE *peDB_O_Minus; + int neigh, nNumO, nNumOthers; +#define CHG_SET_NOOH 0 +#define CHG_SET_WRONG_TAUT 1 +#define CHG_SET_TAUT 2 +#define CHG_LAST_SET 2 /* the last index in trying */ +#define CHG_SET_O_FIXED 3 +#define CHG_SET_NUM 4 + EDGE_LIST ChangeableEdges[CHG_SET_NUM]; + memset( ChangeableEdges, 0, sizeof(ChangeableEdges) ); + /* equivalent to AllocEdgeList( &EdgeList, EDGE_LIST_CLEAR ); */ + /* + S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; + */ + CurrEdges.num_edges = 0; /* clear current edge list */ + cur_success = 0; + for ( i = 0; i < pc2i->len_c2at; i ++ ) { + iat = pc2i->c2at[i].atomNumber; + if ( /* orig. InChI info: taut in orig. InChI =O located in -NO2 that is not taut in Reconstructed InChI */ + num_DB_O < MAX_DIFF_FIXH && + pc2i->c2at[i].nValElectr == 6 /* O, S, Se, Te */ && + (pc2i->c2at[i].endptInChI /*|| pc2i->c2at[i].nMobHInChI*/) && + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + pc2i->c2at[i].nFixHInChI == 0 && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ + /* reversed structure info: */ + !(pc2i->c2at[i].endptRevrs /*|| pc2i->c2at[i].nMobHRevrs*/) && + pc2i->c2at[i].nFixHRevrs == 0 && + pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && + at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 && + /* find whether it belongs to NO2 */ + pVA[iN=at2[iat].neighbor[0]].cNumValenceElectrons == 5 && + at2[iN].valence == 3 && (at2[iN].charge == 0 || at2[iN].charge == 1) && + at2[iN].chem_bonds_valence == 5 - at2[iN].charge ) { + /* find the second O */ + nNumO = nNumOthers = 0; + for ( k = 0; k < at2[iN].valence; k ++ ) { + neigh = at2[iN].neighbor[k]; + if ( neigh == iat ) { + continue; + } + if ( pVA[neigh].cNumValenceElectrons == 6 && + pStruct->endpoint[neigh] && + !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[neigh].endpoint) && + at2[neigh].valence == 1 && at2[neigh].num_H == 0 && + at2[neigh].radical == 0 && (at2[neigh].charge == 0 || at2[neigh].charge == -1) && + at2[neigh].chem_bonds_valence - at2[neigh].charge == 2) { + nNumO ++; + } else + if ( at2[iN].bond_type[k] == BOND_TYPE_SINGLE && + at2[neigh].valence > 1 && + at2[neigh].valence < at2[neigh].chem_bonds_valence ) { + nNumOthers ++; + } + } + if ( nNumO != 1 || nNumOthers != 1 ) { + continue; + } + for ( k = 0; k < num_DB_O; k ++ ) { + if ( iat_NO2[k] == iN ) { + break; + } + } + if ( k == num_DB_O ) { + iat_NO2[num_DB_O] = iN; + iat_DB_O[num_DB_O ++] = iat; + } + /* save the edge to avoid interference */ + if ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e, INC_ADD_EDGE ) ) { + goto exit_case_23; + } + } + } + if ( num_DB_O ) { + /* 1. search for =N(=O)-OH; assume =N(+)(-O(-))(-OH) does not happen */ + for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ + /* find O=N(V) */ + iat = nCanon2AtnoRevrs[i]; + if ( !pStruct->endpoint[i] || pVA[i].cNumValenceElectrons != 6 || + at2[iat].valence != 1 || at2[iat].charge || + 0 > (e = pVA[iat].nCMinusGroupEdge-1) || + at2[iat].num_H + at2[iat].chem_bonds_valence != 2 || + pVA[iN=at2[iat].neighbor[0]].cNumValenceElectrons != 5 || + 0 > (e = pVA[iN].nCPlusGroupEdge-1) || + pBNS->edge[e].forbidden || !pBNS->edge[e].flow || + at2[iN].charge || at2[iN].valence != 3 || at2[iN].chem_bonds_valence != 5) { + continue; + } + /* find the second O, -OH */ + nNumO = nNumOthers = 0; + for ( k = 0; k < at2[iN].valence; k ++ ) { + neigh = at2[iN].neighbor[k]; + if ( neigh == iat ) { + continue; + } + if ( pVA[neigh].cNumValenceElectrons == 6 && + pStruct->endpoint[neigh] && + at2[neigh].valence == 1 && at2[neigh].num_H == 1 && + at2[neigh].radical == 0 && (at2[neigh].charge == 0 ) ) { + nNumO ++; + } else + if ( at2[iN].bond_type[k] == BOND_TYPE_DOUBLE && + at2[neigh].valence >= 2 && + at2[neigh].valence < at2[neigh].chem_bonds_valence ) { + nNumOthers ++; + } + } + if ( nNumO != 1 || nNumOthers != 1 ) { + continue; + } + /* save edges to be changed */ + if ( (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NOOH], e, INC_ADD_EDGE )) || + (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e, INC_ADD_EDGE ))) { + goto exit_case_23; + } + if ( NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e )) && + (( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NOOH], j, INC_ADD_EDGE ) ) || + ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e, INC_ADD_EDGE ) ))) { + goto exit_case_23; + } + } + /* 2. search for (-) atoms that are tautomeric but should not be */ + /* or that got H from Normalization but they shouldn't */ + for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ + iat = nCanon2AtnoRevrs[i]; + if ( at2[iat].charge == -1 && + !pStruct->endpoint[i] && + (at_Mobile_H_Revrs && + (at_Mobile_H_Revrs[i].endpoint || at2[iat].num_H < at_Mobile_H_Revrs[i].num_H )) ) { + + if ( 0 <= (e = pVA[iat].nCMinusGroupEdge-1) && + 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e ) && + !pBNS->edge[e].forbidden && pBNS->edge[e].flow && + ( + ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_WRONG_TAUT], e, INC_ADD_EDGE ) ) || + ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e, INC_ADD_EDGE ) ) + ) ) { + goto exit_case_23; + } + } else + /* negatively charged atom in Reconstructed structure got H(+) from Normalization */ + /* and is not tautomeric; in the original structure it is tautomeric */ + if ( at2[iat].charge == -1 && + pStruct->endpoint[i] && + !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[i].endpoint) && + (num_Fixed_H_Revrs && num_Fixed_H_Revrs[i] == -1) && + (pnMobHRevrs && pnMobHRevrs[i] == 1) && + pStruct->fixed_H[i] == 0 ) { + + if ( 0 <= (e = pVA[iat].nCMinusGroupEdge-1) && + 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e ) && + !pBNS->edge[e].forbidden && pBNS->edge[e].flow && + ( + ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_WRONG_TAUT], e, INC_ADD_EDGE ) ) || + ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e, INC_ADD_EDGE ) ) + ) ) { + goto exit_case_23; + } + } + } + /* 3. Search for (-) atoms that are tautomeric */ + for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ + iat = nCanon2AtnoRevrs[i]; + if ( pStruct->endpoint[i] && + (at_Mobile_H_Revrs && at_Mobile_H_Revrs[i].endpoint) && + at2[iat].charge == -1 + /*&& pVA[i].cNumValenceElectrons == 6*/ ) { + if ( 0 <= (e = pVA[iat].nCMinusGroupEdge-1) && + !pBNS->edge[e].forbidden && pBNS->edge[e].flow && + 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e ) && + ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_TAUT], e, INC_ADD_EDGE ) ) ) { + goto exit_case_23; + } + } + } + /* ------- finally, try to move charges from O=N --------------*/ + for ( i = 0; i < num_DB_O; i ++ ) { + int nDeltaChargeExpected; + one_success = 0; + delta = 1; + iat = iat_DB_O[i]; + peDB_O_Minus = pBNS->edge + (pVA[iat].nCMinusGroupEdge-1); + pe = pBNS->edge + pBNS->vert[iat].iedge[0]; + + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->forbidden |= forbidden_edge_mask; + + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + for ( k = 0; !one_success && k <= CHG_LAST_SET; k ++ ) { + if ( !ChangeableEdges[k].num_edges ) { + continue; + } + nDeltaChargeExpected = (k==CHG_SET_NOOH)? 2 : 0; + + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &ChangeableEdges[k], forbidden_edge_mask ); + /* allow (-) charge to move to N=O */ + peDB_O_Minus->forbidden &= forbidden_edge_mask_inv; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && + nDeltaCharge == nDeltaChargeExpected ) { + /* Move (-) charge to =O and remove it an endpoint => nDeltaCharge == 0 */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + one_success ++; /* 23 */ + } + } + INCHI_HEAPCHK + } + cur_success += one_success; + + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + pe->forbidden &= forbidden_edge_mask_inv; + + if ( !one_success ) { + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + } + } +exit_case_23: + for ( i = 0; i < CHG_SET_NUM; i ++ ) { + AllocEdgeList( &ChangeableEdges[i], EDGE_LIST_FREE ); + } + + CurrEdges.num_edges = 0; /* clear current edge list */ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } +#undef CHG_SET_NOOH +#undef CHG_SET_WRONG_TAUT +#undef CHG_SET_TAUT +#undef CHG_LAST_SET +#undef CHG_SET_O_FIXED +#undef CHG_SET_NUM + } + + if ( pc2i->len_c2at && pc2i->nNumTgInChI == 1 ) { + /*------------------------------------------------------------------*/ + /* case 24: InChI norm. -N(-)-N(+)(IV) => -N=N(V) prevents tauto- */ + /* merism on -N(-)- in case of ADP */ + /* */ + /* Solution: convert N(V)=N- ...=X -> N(IV)(+)-N=...-X(-)*/ + /* N(IV)(+)-N(-)-...=X */ + /* */ + /* Orig InChI taut taut, 1 t-group only(ADP?) */ + /* Reconstructed struct non-taut possibly not taut */ + /* */ + /* Details: 1a. store next to N(V) (+)edge its flower edge */ + /* 1b. store next to N(-) edge NO_VERTEX */ + /* 2. Release (-) edges of other missing endpoints or */ + /* all endpoints if no other is missing */ + /* 3. Decrement flow on (+) edge */ + /* if flower edge is stored then expect DeltaCharge=2*/ + /* otherwise DeltaCharge = 0 */ + /*------------------------------------------------------------------*/ + int iat; + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && + pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; + inp_ATOM *atf = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at_fixed_bonds)? + pStruct->pOne_norm_data[1]->at_fixed_bonds : NULL; + int iN, one_success; + EdgeIndex ef, e1; + BNS_EDGE *pef; +#define CHG_SET_MISSED_TAUT 0 +#define CHG_SET_OTHER_TAUT_O 1 +#define CHG_SET_OTHER_TAUT_N 2 +#define CHG_LAST_SET 2 /* the last index in trying */ +#define CHG_SET_NN 3 +#define CHG_SET_AVOID 4 +#define CHG_SET_NUM 5 + EDGE_LIST ChangeableEdges[CHG_SET_NUM]; + memset( ChangeableEdges, 0, sizeof(ChangeableEdges) ); + /* equivalent to AllocEdgeList( &EdgeList, EDGE_LIST_CLEAR ); */ + /* + S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; + */ + CurrEdges.num_edges = 0; /* clear current edge list */ + cur_success = 0; + for ( i = 0; i < pc2i->len_c2at; i ++ ) { + iat = pc2i->c2at[i].atomNumber; + if ( /* orig. InChI info: -N=N(V) */ + pc2i->c2at[i].nValElectr == 5 /* N or P */ && + (pc2i->c2at[i].endptInChI /* only N */) && + (e1=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e1].forbidden && + pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI == 0 && + /* reversed structure info: */ + !pc2i->c2at[i].endptRevrs && + pc2i->c2at[i].nFixHRevrs == 0 && + pc2i->c2at[i].nAtChargeRevrs == 0 && !at2[iat].num_H && + at2[iat].valence == 2 && at2[iat].chem_bonds_valence == 3 && + /* find whether -N= has =N(V) neighbor; Note: operator comma: (A,B) returns B */ + (iN = at2[iat].neighbor[at2[iat].bond_type[0] != BOND_TYPE_DOUBLE], + pVA[iN].cNumValenceElectrons == 5) && + at2[iN].chem_bonds_valence == 5 && + at2[iN].charge == 0 && !at2[iN].num_H && !at2[iN].radical && + 0 <= (e=pVA[iN].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && + 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e )) { + + ef = GetChargeFlowerUpperEdge( pBNS, pVA, e ); /* == NO_VERTEX if N(V) has 4 bonds */ + if ( (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NN], e, INC_ADD_EDGE )) || + (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NN], ef, INC_ADD_EDGE )) || + (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NN], 1, INC_ADD_EDGE )) || /* expected nDeltaCharge */ + (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e1, INC_ADD_EDGE )) || + (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE )) || + (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], ef, INC_ADD_EDGE ))) { + goto exit_case_24; + } + /* mark -N= so that (-) will not be moved to it */ + if ( 0 <= (e = pVA[iat].nCMinusGroupEdge) && !pBNS->edge[e].forbidden && + 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) && + (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE ))) { + goto exit_case_24; + } + } else + if ( /* orig. InChI info: -N(-)N(IV)(+) */ + atf && + pc2i->c2at[i].nValElectr == 5 /* N or P */ && + pc2i->c2at[i].endptInChI /* only N */ && + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + pc2i->c2at[i].nFixHInChI == 0 && pc2i->c2at[i].nMobHInChI == 0 && + /* reversed structure info: */ + !pc2i->c2at[i].endptRevrs && + pc2i->c2at[i].nFixHRevrs == 0 && + pc2i->c2at[i].nAtChargeRevrs == -1 && !at2[iat].num_H && + at2[iat].valence == 2 && at2[iat].chem_bonds_valence == 2 && + atf[iat].valence == 2 && atf[iat].chem_bonds_valence == 3 && + /* find whether -N= has =N(V) neighbor; Note: operator comma: (A,B) returns B */ + (iN=atf[iat].neighbor[atf[iat].bond_type[0] != BOND_TYPE_DOUBLE], + pVA[iN].cNumValenceElectrons == 5) && + at2[iN].charge == 1 && /* double bond neighbor */ + at2[iN].chem_bonds_valence == 4 && + atf[iN].charge == 0 && + atf[iN].chem_bonds_valence == 5 && /* InChI normalization created N(V)=N- out of N(IV)(+)-N(-)- */ + !at2[iN].num_H && !at2[iN].radical && + 0 <= (e=pVA[iat].nCMinusGroupEdge-1) && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && + 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) ) { + /* save (-) edge */ + if ( (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NN], e, INC_ADD_EDGE )) || + (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NN], NO_VERTEX, INC_ADD_EDGE )) || + (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NN], 1, INC_ADD_EDGE )) || /* expected nDeltaCharge */ + (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE ))) { + goto exit_case_24; + } + } + } + if ( !ChangeableEdges[CHG_SET_NN].num_edges ) { + goto exit_case_24; + } + /* Collect all relevant tautomeric atoms */ + for ( i = 0; i < pStruct->num_atoms; i ++ ) { /* i = canonical number - 1 */ + if ( !pStruct->endpoint[i] ) { + continue; + } + iat = nCanon2AtnoRevrs[i]; + if ( at2[iat].charge || at2[iat].radical || at2[iat].valence == at2[iat].chem_bonds_valence ) { + continue; /* cannot be an acceptor of (-) */ + } + if ( 0 > (e=pVA[iat].nCMinusGroupEdge-1) || pBNS->edge[e].forbidden || pBNS->edge[e].flow ) { + continue; + } + if ( 0 <= FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) ) { + continue; /* has already been used */ + } + /* missing endpoint */ + if ( !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint) ) { + if ( 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) && ( + (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_MISSED_TAUT], e, INC_ADD_EDGE )) || + (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE )))) { + goto exit_case_24; + } + } else + /* endpoint O */ + if ( pVA[iat].cNumValenceElectrons == 6 ) { + if ( 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) && ( + (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_OTHER_TAUT_O], e, INC_ADD_EDGE )) || + (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE )))){ + goto exit_case_24; + } + } else + /* endpoint N */ + if ( pVA[iat].cNumValenceElectrons == 5 ) { + if ( 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) && ( + (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_OTHER_TAUT_N], e, INC_ADD_EDGE )) || + (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE )))){ + goto exit_case_24; + } + } + } + /* ------- finally, try to move charges from -N(-)-N(+) or to N(V) --------------*/ + for ( i = 0; i < ChangeableEdges[CHG_SET_NN].num_edges; i += 3 ) { + int nDeltaChargeExpected; + one_success = 0; + delta = 1; + pe = pBNS->edge + ChangeableEdges[CHG_SET_NN].pnEdges[i]; + pef = (NO_VERTEX != ChangeableEdges[CHG_SET_NN].pnEdges[i+1])? + pBNS->edge + ChangeableEdges[CHG_SET_NN].pnEdges[i+1] : NULL; + nDeltaChargeExpected = ChangeableEdges[CHG_SET_NN].pnEdges[i+2]; + + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + for ( k = 0; !one_success && k <= CHG_LAST_SET; k ++ ) { + if ( !ChangeableEdges[k].num_edges ) { + continue; + } + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &ChangeableEdges[k], forbidden_edge_mask ); + /* allow change of N(V) flower edge */ + if ( pef ) { + pef->forbidden &= forbidden_edge_mask_inv; + } + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && + nDeltaCharge == nDeltaChargeExpected ) { + /* Move (-) charge to =O and remove it an endpoint => nDeltaCharge == 0 */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + one_success ++; /* 24 */ + } + } + INCHI_HEAPCHK + } + cur_success += one_success; + + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + + if ( !one_success ) { + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + } +exit_case_24: + for ( i = 0; i < CHG_SET_NUM; i ++ ) { + AllocEdgeList( &ChangeableEdges[i], EDGE_LIST_FREE ); + } + + CurrEdges.num_edges = 0; /* clear current edge list */ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } +#undef CHG_SET_NN +#undef CHG_SET_MISSED_TAUT +#undef CHG_SET_OTHER_TAUT_O +#undef CHG_SET_OTHER_TAUT_N +#undef CHG_LAST_SET +#undef CHG_SET_AVOID +#undef CHG_SET_NUM + } + + /* pStruct->nNumRemovedProtonsMobHInChI == pc2i->nNumRemHInChI */ + + if ( pc2i->len_c2at && pc2i->nNumTgInChI == 1 && + pc2i->nNumRemHRevrs > pc2i->nNumRemHInChI && 0 > pc2i->nNumRemHInChI && + (pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || + pc2i->nNumTgRevrs > pc2i->nNumTgInChI ) ) { + /*------------------------------------------------------------------*/ + /* case 25: Restored InChI does not have 2 or more added protons */ + /* possibly taut. endpoints are missing */ + /* has -N(-O(-))-O(-) group(s) */ + /* Original InChI has only one t-group */ + /* */ + /* Solution: convert -N(-O(-))-O(-) -> -N(+)(=O)-O(-) */ + /* and direct 2(-) to the missing taut atoms*/ + /* at first attempt try to move (-) to N only */ + /* */ + /*------------------------------------------------------------------*/ + int iat; + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + AT_NUMB *nAtno2CanonRevrs = pStruct->nAtno2Canon[0]; + inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && + pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; + /* + inp_ATOM *atf = (pStruct->pOne_norm_data[1] && pStruct->pOne_norm_data[1]->at_fixed_bonds)? + pStruct->pOne_norm_data[1]->at_fixed_bonds : NULL; + */ + int iN, neigh, one_success; + EdgeIndex e1, bFirst; + BNS_EDGE *pef; +#define CHG_SET_MISSED_TAUT_1 0 +#define CHG_SET_MISSED_TAUT_ALL 1 +#define CHG_SET_OTHER_TAUT_1 2 +#define CHG_SET_OTHER_TAUT_ALL 3 +#define CHG_LAST_SET 3 /* the last index in trying */ +#define CHG_SET_NO_IN_NO2M2 4 +#define CHG_SET_AVOID 5 +#define CHG_SET_NUM 6 + EDGE_LIST ChangeableEdges[CHG_SET_NUM]; + memset( ChangeableEdges, 0, sizeof(ChangeableEdges) ); + /* equivalent to AllocEdgeList( &EdgeList, EDGE_LIST_CLEAR ); */ + /* + S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; + */ + CurrEdges.num_edges = 0; /* clear current edge list */ + cur_success = 0; + /* find all -N(-O(-))-O(-) */ + for ( i = 0; i < pStruct->num_atoms; i ++ ) { + iat = nCanon2AtnoRevrs[i]; + if ( pStruct->endpoint[i] ) { + if ( 0 > (e=pVA[iat].nCMinusGroupEdge-1) || pBNS->edge[e].forbidden || + 0 <= FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) ) { + continue; + } + bFirst = ( pVA[iat].cNumValenceElectrons == 5 && pc2i->nNumTgInChI == 1 || + pVA[iat].cNumValenceElectrons == 6 && pc2i->nNumTgInChI != 1 ); + /* many or no t-groups -> try O only first */ + /* single t-group -> try only N first */ + if ( !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[i].endpoint) ) { + /* missed tautomeric endpoint */ + if ( bFirst && + (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_MISSED_TAUT_1], e, INC_ADD_EDGE ))) { + goto exit_case_25; + } + if (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_MISSED_TAUT_ALL], e, INC_ADD_EDGE )) { + goto exit_case_25; + } + } + if ( bFirst && + (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_OTHER_TAUT_1], e, INC_ADD_EDGE ))) { + goto exit_case_25; + } + if (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_OTHER_TAUT_ALL], e, INC_ADD_EDGE )) { + goto exit_case_25; + } + if (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE )) { + goto exit_case_25; + } + } else + if ( at2[iat].valence == 1 && at2[iat].charge == -1 && + pVA[iat].cNumValenceElectrons == 6 && + pVA[iN=at2[iat].neighbor[0]].cNumValenceElectrons == 5 && /* -O(-) */ + !pStruct->endpoint[nAtno2CanonRevrs[iN]] && + at2[iN].valence == 3 && at2[iN].chem_bonds_valence == 3 && + !at2[iN].charge && !at2[iN].radical && + 0 <= (e=pVA[iN].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden && + pBNS->edge[e].flow && /* NPlus edge */ + 0 <= (e1 = pVA[iat].nCMinusGroupEdge-1) && !pBNS->edge[e1].forbidden && + pBNS->edge[e1].flow && /* OMinus edge */ + 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e ) && + 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_AVOID], e1 )) { + /* found >N-O(-) */ + int nNumO = 0, nNumOthers = 0; + for ( k = 0; k < at2[iN].valence; k ++ ) { + neigh = at2[iN].neighbor[k]; + if ( neigh == iat ) { + continue; + } + if ( pVA[neigh].cNumValenceElectrons == 6 && + !pStruct->endpoint[neigh] && + at2[neigh].valence == 1 && at2[neigh].num_H == 0 && + at2[neigh].radical == 0 && at2[neigh].charge == -1 && + at2[neigh].chem_bonds_valence == 1 ) { + nNumO ++; + } else + if ( at2[iN].bond_type[k] == BOND_TYPE_SINGLE && + at2[neigh].valence > 1 && + at2[neigh].valence < at2[neigh].chem_bonds_valence ) { + nNumOthers ++; + } + } + if ( nNumO != 1 && nNumOthers != 1 ) { + continue; + } + /* save charge edges: NPlus first, OMinus second */ + if ( (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NO_IN_NO2M2], e, INC_ADD_EDGE )) || + (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_NO_IN_NO2M2], e1, INC_ADD_EDGE )) || + (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e, INC_ADD_EDGE )) || + (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_AVOID], e1, INC_ADD_EDGE ))) { + goto exit_case_25; + } + } + } + if ( !ChangeableEdges[CHG_SET_NO_IN_NO2M2].num_edges || + !ChangeableEdges[CHG_SET_OTHER_TAUT_ALL].num_edges ) { + goto exit_case_25; + } + /* ------- finally, try to move charges from -NO2(2-) or to tautomeric endpoints ----*/ + for ( i = 0; i < ChangeableEdges[CHG_SET_NO_IN_NO2M2].num_edges; i += 2 ) { + int nDeltaChargeExpected = 3; + /* change flow on O(-) to make it neutral; 3 new charges will be created: + N(+), and two (-) on InChI endpoints + alternatively, if we change flow on N to make N(+) then O(-) will + be nutralized (-1 charge) and two (-) charges on taut. endpoints will be + created (+2); the total change in this case would be (-1)+(+2) = +1 + */ + one_success = 0; + delta = 1; + pe = pBNS->edge + ChangeableEdges[CHG_SET_NO_IN_NO2M2].pnEdges[i+1]; /* O(-) edge */ + pef = pBNS->edge + ChangeableEdges[CHG_SET_NO_IN_NO2M2].pnEdges[i]; /* >N- (+) edge */ + + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + for ( k = 0; !one_success && k <= CHG_LAST_SET; k ++ ) { + if ( !ChangeableEdges[k].num_edges ) { + continue; + } + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &ChangeableEdges[k], forbidden_edge_mask ); + /* allow change of N(V) flower edge */ + pef->forbidden &= forbidden_edge_mask_inv; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && + nDeltaCharge == nDeltaChargeExpected ) { + /* Move (-) charge to =O and remove it an endpoint => nDeltaCharge == 0 */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + one_success ++; /* 24 */ + } + } + INCHI_HEAPCHK + } + cur_success += one_success; + + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + + if ( !one_success ) { + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + } +exit_case_25: + for ( i = 0; i < CHG_SET_NUM; i ++ ) { + AllocEdgeList( &ChangeableEdges[i], EDGE_LIST_FREE ); + } + + CurrEdges.num_edges = 0; /* clear current edge list */ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2FHINCHI( pStruct, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } +#undef CHG_SET_NN +#undef CHG_SET_MISSED_TAUT +#undef CHG_SET_OTHER_TAUT_O +#undef CHG_SET_OTHER_TAUT_N +#undef CHG_LAST_SET +#undef CHG_SET_AVOID +#undef CHG_SET_NUM + } + + +exit_function: + AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); + AllocEdgeList( &CurrEdges, EDGE_LIST_FREE ); + AllocEdgeList( &NFlowerEdges, EDGE_LIST_FREE ); + AllocEdgeList( &SFlowerEdges, EDGE_LIST_FREE ); + AllocEdgeList( &OtherNFlowerEdges, EDGE_LIST_FREE ); + AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_FREE ); + AllocEdgeList( &AllBondEdges, EDGE_LIST_FREE ); + return ret < 0? ret : (pc2i->bHasDifference && tot_succes); +} + + +#endif diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichirvr4.c b/INCHI-1-SRC/INCHI_BASE/src/ichirvr4.c similarity index 93% rename from INCHI-1-SRC/INCHI_API/inchi_dll/ichirvr4.c rename to INCHI-1-SRC/INCHI_BASE/src/ichirvr4.c index bdb7256..11eaa64 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichirvr4.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichirvr4.c @@ -1,3218 +1,3241 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - -/*^^^ */ -/*#define CHECK_WIN32_VC_HEAP*/ -#include "mode.h" - -#if ( READ_INCHI_STRING == 1 ) - -#include "ichi.h" -#include "ichitime.h" - -#include "inpdef.h" -#include "ichimain.h" -#include "ichierr.h" -#include "incomdef.h" -#include "ichiring.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "util.h" - -#include "ichicomp.h" -#include "ichister.h" - -#include "ichi_bns.h" - -#include "strutil.h" - -#include "ichirvrs.h" -/*^^^ */ - -/********************** Forbid carbon charge edges ***********************************/ -int ForbidCarbonChargeEdges( BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups, EDGE_LIST *pCarbonChargeEdges, int forbidden_edge_mask ) -{ -#define MAX_NUM_CARBON_CHARGE_EDGES 2 - int nType, i, k, ret; - BNS_EDGE *pEdge; - if ( ret = AllocEdgeList( pCarbonChargeEdges, MAX_NUM_CARBON_CHARGE_EDGES ) ) { - goto exit_function; - } - pCarbonChargeEdges->num_edges = 0; - for ( i = 0; i < MAX_NUM_CARBON_CHARGE_EDGES; i ++ ) { - switch( i ) { - case 0: - nType = TCG_Plus_C0; - break; - case 1: - nType = TCG_Minus_C0; - break; - default: - ret = RI_ERR_PROGR; - goto exit_function; - } - if ( (k = pTCGroups->nGroup[nType]) >= 0 ) { - k = pTCGroups->pTCG[k].nForwardEdge; - if ( k > 0 ) { - pEdge = pBNS->edge + k; - if ( !(pEdge->forbidden & forbidden_edge_mask) ) { - pEdge->forbidden |= forbidden_edge_mask; - if ( ret = AddToEdgeList( pCarbonChargeEdges, k, 0 ) ) { - goto exit_function; - } - } - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - } - } - ret = pCarbonChargeEdges->num_edges; -exit_function: - return ret; -#undef MAX_NUM_CARBON_CHARGE_EDGES -} -/******************************************************************************************************/ -int ForbidNintrogenPlus2BondsInSmallRings( BN_STRUCT *pBNS, inp_ATOM *at, int num_at, - VAL_AT *pVA, int min_ring_size, ALL_TC_GROUPS *pTCGroups, - EDGE_LIST *pNplus2BondsEdges, int forbidden_edge_mask ) -{ - int i, j, ret; - BNS_EDGE *e; - - ret = 0; - /* --- forbid edges that allow to make =N(+)= or #N(+)- in small ring */ - for ( i = 0; i < num_at; i ++ ) { - if ( at[i].valence == 2 && - !at[i].num_H && !at[i].endpoint && - pVA[i].cNumValenceElectrons == 5 && - pVA[i].cPeriodicRowNumber == 1 && - !pVA[i].cMaxFlowToMetal && pVA[i].nCPlusGroupEdge > 0 && - pVA[i].cnListIndex > 0 && cnList[pVA[i].cnListIndex-1].bits == cn_bits_MNP && - pVA[i].cMinRingSize && pVA[i].cMinRingSize <= min_ring_size ) { - - e = pBNS->edge + (j = pVA[i].nCPlusGroupEdge - 1); - if ( !(e->forbidden & forbidden_edge_mask) ) { - e->forbidden |= forbidden_edge_mask; - if ( ret = AddToEdgeList( pNplus2BondsEdges, j, 128 ) ) { - goto exit_function; - } - } - } - } - ret = 0; -exit_function: - return ret; -} - -/************************************************************************************************* -Problem: Formula in InChI from the reversed structure has less H than in the input InChI -Solutions: - -(a) | | - -B(-)-NH-=..-=N(+)< => -B(-)-NH(+)=-..=-N< (H is not removed from the ion pair) - | | - - | | -(b) >N(+)=-=...-=N-NH => >N-=-...=-N(+)-NH (charge from onium cannot be moved to remove H+) - | | -*************************************************************************************************/ -int FixLessHydrogenInFormula( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, - inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ) -{ - int iBPlus=NO_VERTEX, iNV=NO_VERTEX, iNH = NO_VERTEX, neigh; - EDGE_LIST NewlyFixedEdges; - int ret, i, j; - int num_at = pStruct->num_atoms; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - /* for RunBnsTestOnce */ - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - - AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR ); - if ( ret = AllocEdgeList( &NewlyFixedEdges, 2*num_at ) ) { - goto exit_function; - } - for ( i = 0; i < num_at; i ++ ) { - if ( (j = pVA[i].nCMinusGroupEdge-1) >= 0 ) { - if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) { - goto exit_function; - } - pBNS->edge[j].forbidden |= forbidden_edge_mask; - } - if ( (j = pVA[i].nCPlusGroupEdge-1) >= 0 ) { - if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) { - goto exit_function; - } - pBNS->edge[j].forbidden |= forbidden_edge_mask; - } - } - /* extra H has been removed; check non-tautomeric atoms */ - for ( i = 0; i < num_at; i ++ ) { - if ( !at2[i].endpoint && !pVA[i].cMetal && - pVA[i].cNumValenceElectrons == 5 && pVA[i].cPeriodicRowNumber == 1 && - at2[i].num_H == atf[i].num_H + 1) { - /* H was removed from N */ - iNH = i; - break; - } - } - if ( 0 <= iNH && iNH < num_at ) { - /* check neighbors for | | - (a) -B(+)- or (b) =N- - | | - */ - for ( j = 0; j < at2[i].valence; j ++ ) { - neigh = at2[iNH].neighbor[j]; - if ( at2[neigh].valence == 4 ) { - if ( at2[neigh].charge == -1 && at2[neigh].chem_bonds_valence == 4 && - !at2[neigh].radical && !at[neigh].num_H ) { - iBPlus = neigh; - } - } - } - } - if ( 0 <= iNH && iNH < num_at ) { - int bond_type_at2; - int bond_type_atf; - int num_bonds_in_path = 0; - int delta = -1, nxt = iNH, prv = NO_VERTEX, nxt_is_NPlus; - /* the changed bond to the dehydrogenated atom H should have greater order */ - /* delta = (new bond order in atf[]) - (restored bond order in at2[]) */ - nxt_is_NPlus = 0; - do { - i = nxt; - nxt = NO_VERTEX; - delta = -delta; - for ( j = 0; j < at2[i].valence; j ++ ) { - bond_type_at2 = at2[i].bond_type[j] & BOND_TYPE_MASK; /* restored bond */ - bond_type_atf = atf[i].bond_type[j] & BOND_TYPE_MASK; /* normalized bond */ - nxt_is_NPlus = 0; - if ( (bond_type_atf - bond_type_at2 == delta || bond_type_atf == BOND_ALT12NS) && - BOND_TYPE_SINGLE <= bond_type_at2 + delta && bond_type_at2 + delta <= BOND_TYPE_TRIPLE && - !at2[(int)at2[i].neighbor[j]].cFlags ) { - prv = i; - nxt = at2[i].neighbor[j]; - nxt_is_NPlus = at2[nxt].charge == 1 && atf[nxt].charge == 0 && - pVA[nxt].cNumValenceElectrons == 5 && pVA[nxt].cPeriodicRowNumber == 1; - at2[i].cFlags |= 1; /* avoid cycling */ - num_bonds_in_path ++; - if ( delta == -1 && at2[prv].valence == 4 && at2[prv].chem_bonds_valence == 5 && - !at2[prv].charge && !at2[prv].radical && pVA[prv].cNumValenceElectrons == 5 && - pVA[prv].nCPlusGroupEdge > 0 ) { - iNV = prv; - } - if ( at2[nxt].charge != atf[nxt].charge ) { - if ( (at2[nxt].charge == 1 || atf[nxt].charge == 1) && - pVA[nxt].nCPlusGroupEdge > 0 ) { - pBNS->edge[pVA[nxt].nCPlusGroupEdge-1].forbidden &= inv_forbidden_edge_mask; - } - if ( (at2[nxt].charge == -1 || atf[nxt].charge == -1) && - pVA[nxt].nCMinusGroupEdge > 0 ) { - pBNS->edge[pVA[nxt].nCMinusGroupEdge-1].forbidden &= inv_forbidden_edge_mask; - } - } - break; /* found */ - } - } - } while ( nxt >= 0 && !( nxt_is_NPlus && delta == -1 ) ); - for ( i = 0; i < num_at; i ++ ) { - at2[i].cFlags = 0; - } - if ( nxt >= 0 && nxt_is_NPlus && delta == -1 ) { - /* a simple alt path from NH-= to =N(+) has been found */ - if ( iBPlus || iNV ) { - /* move (+) charge from N(+) to iNV or, if iBPlus, then to iNH */ - if ( iNV >= 0 && (j = pVA[iNV].nCPlusGroupEdge-1) > 0 && pBNS->edge[j].flow > 0 || - iNH >= 0 && (j = pVA[iNH].nCPlusGroupEdge-1) > 0 && pBNS->edge[j].flow > 0 ) { - int ieFlower; - BNS_EDGE *pe = pBNS->edge + j, *peFlower = NULL; - Vertex v1 = pe->neighbor1; - Vertex v2 = v1 ^ pe->neighbor12; - BNS_VERTEX *pv1 = pBNS->vert + v1; - BNS_VERTEX *pv2 = pBNS->vert + v2; - - delta = 1; - /* prevent conversion of >N(+)= into N(V) neutral */ - ieFlower = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[nxt].nCPlusGroupEdge-1 ); - if ( ieFlower >= 0 ) { - peFlower = pBNS->edge + ieFlower; - if ( peFlower->flow == delta ) { - peFlower->forbidden |= forbidden_edge_mask; - if ( ret = AddToEdgeList( &NewlyFixedEdges, ieFlower, 0 )) { - goto exit_function; - } - } - } - pe->forbidden |= forbidden_edge_mask; - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && - nDeltaCharge <= 0 /* charge moving to this atom disappers*/ ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else - if ( ret == 1 ) { - *pnTotalDelta += ret; - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - } else { - ret = 0; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - - } - - } - } - } -exit_function: - /* remove bond fixation */ - RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask ); - AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE ); - return ret; -} -/*********************************************************************************************** - - - X=Y-O(-) => X(-)-Y=O - - -************************************************************************************************/ -int FixMoreHydrogenInFormula( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, - inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ) -{ - int iNH = NO_VERTEX, neigh, neigh2; - EDGE_LIST NewlyFixedEdges; - int ret, i, j, k, k2, delta; - int num_at = pStruct->num_atoms; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - Vertex v1, v2; - /* for RunBnsTestOnce */ - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - BNS_EDGE *pe, *pe2; - - AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR ); - if ( ret = AllocEdgeList( &NewlyFixedEdges, 2*num_at ) ) { - goto exit_function; - } - /* fix all charges */ - for ( i = 0; i < num_at; i ++ ) { - if ( (j = pVA[i].nCMinusGroupEdge-1) >= 0 ) { - if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) { - goto exit_function; - } - pBNS->edge[j].forbidden |= forbidden_edge_mask; - } - if ( (j = pVA[i].nCPlusGroupEdge-1) >= 0 ) { - if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) { - goto exit_function; - } - pBNS->edge[j].forbidden |= forbidden_edge_mask; - } - } - - /* H(+) has been added to -O(-); check non-tautomeric atoms */ - for ( i = 0; i < num_at; i ++ ) { - if ( !(pStruct->bMobileH? at2[i].endpoint : pStruct->endpoint[i]) && !pVA[i].cMetal && - at2[i].num_H + 1 == atf[i].num_H && /* normalization added H ??? What would happen in Fixed-H case?*/ - (k = pVA[i].nCMinusGroupEdge-1) >= 0 && - pBNS->edge[k].flow == 1 && /* atom had (-) charge before preprocessing */ - at2[i].charge == -1 && atf[i].charge == 0 && /* and has no charge after preprocessing */ - at2[i].valence == 1 && at2[i].chem_bonds_valence == 1 && /* connected by a single bond */ - pVA[i].cNumValenceElectrons == 6 && /* atom is O, S, Se, Te */ - at2[neigh=at2[i].neighbor[0]].chem_bonds_valence > at2[neigh].valence - /* atom's single neighbor has multiple bond(s)*/ - ) { - /* H(+) was added to O in Y=X-O(-), where X is the only neighbor of O, X=neigh, Y=neigh2 */ - iNH = i; - for ( j = 0; j < at2[neigh].valence; j ++ ) { - neigh2 = at2[neigh].neighbor[j]; - if ( neigh2 != iNH && !at2[neigh2].endpoint && - !pBNS->edge[(int)pBNS->vert[neigh].iedge[j]].forbidden && - 4 <= pVA[neigh2].cNumValenceElectrons && - pVA[neigh2].cNumValenceElectrons <= 5 && /* neig2 is C or N */ - (k2 = pVA[neigh2].nCMinusGroupEdge-1) >= 0 && - !pBNS->edge[k2].flow /* negative charge may be moved to neigh2 */ ) { - break; - } - } - if ( j < at2[neigh].valence ) { - delta = 1; - pe = pBNS->edge + k; /* -O(-) negative charge edge; flow = 1 */ - pe2 = pBNS->edge + k2; /* X charge edge; flow = 0 */ - v1 = pe->neighbor1; - v2 = pe->neighbor12 ^ v1; - pe->flow -= delta; - pBNS->vert[v1].st_edge.flow -= delta; - pBNS->vert[v2].st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - pe2->forbidden &= inv_forbidden_edge_mask; /* allow the charge to move */ - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && - nDeltaCharge <= 1 ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else - if ( ret ) { - *pnTotalDelta += ret; - } else { - ret = RI_ERR_PROGR; - } - break; - } else { - /* the attempt has failed; restore the flow */ - ret = 0; - pe->flow += delta; - pBNS->vert[v1].st_edge.flow += delta; - pBNS->vert[v2].st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - } - } - } -exit_function: - /* remove bond fixation */ - RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask ); - AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE ); - return ret; -} -#if ( FIX_ADD_PROTON_FOR_ADP == 1 ) -/******************************************************************************************************/ -int FixAddProtonForADP( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, - inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, ICR *picr, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ) -{ - int iBPlus=NO_VERTEX, iNV=NO_VERTEX, iNH = NO_VERTEX, neigh, neigh2; - EDGE_LIST NewlyFixedEdges; - int ret, i, j, k, k2, delta; - int num_at = pStruct->num_atoms; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - Vertex v1, v2; - /* for RunBnsTestOnce */ - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - BNS_EDGE *pe, *pe2; - - ret = 0; - /* - AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR ); - - for ( i = 0; i < num_at; i ++ ) { - if ( at2[i].radical == RADICAL_DOUBLET && at2[i].endpoint ) { - pStruct->bExtract |= EXTRACT_STRUCT_NUMBER; - ret = 1; - break; - } - } - */ - return ret; -} -#endif -/****************************************************************************************************** - OH OH - / / - -NH => -NH(+) to eliminate false tautomerism. S(IV) or N(V) or P(V) may be a centerpoint - \\ \ - O O(-) -*******************************************************************************************************/ -int FixRemoveExtraTautEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, - inp_ATOM *at2, inp_ATOM *atf, inp_ATOM *atn, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, ICR *picr, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ) -{ - EDGE_LIST NewlyFixedEdges; - int ret, i, j, k, delta, centerpoint, endpoint1, endpoint2; - int num_at = pStruct->num_atoms; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - Vertex v1, v2; - /* for RunBnsTestOnce */ - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - BNS_EDGE *pe, *pe2; - - ret = 0; - - AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR ); - if ( ret = AllocEdgeList( &NewlyFixedEdges, 2*num_at ) ) { - goto exit_function; - } - /* fix all charges */ - for ( i = 0; i < num_at; i ++ ) { - if ( (j = pVA[i].nCMinusGroupEdge-1) >= 0 ) { - if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) { - goto exit_function; - } - pBNS->edge[j].forbidden |= forbidden_edge_mask; - } - if ( (j = pVA[i].nCPlusGroupEdge-1) >= 0 ) { - if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) { - goto exit_function; - } - pBNS->edge[j].forbidden |= forbidden_edge_mask; - } - } - - for ( i = 0; i < picr->num_endp_in1_only; i ++ ) { - endpoint1 = picr->endp_in1_only[i]-1; - if ( at2[endpoint1].valence == at2[endpoint1].chem_bonds_valence || - pVA[endpoint1].nCMinusGroupEdge <= 0 ) { - continue; - } - /* find centerpoint */ - for ( j = 0; j < at2[endpoint1].valence; j ++ ) { - if ( BOND_TYPE_DOUBLE == ( BOND_TYPE_MASK & at2[endpoint1].bond_type[j] ) ) { - centerpoint = at2[endpoint1].neighbor[j]; - if ( at2[centerpoint].charge || pVA[centerpoint].nCPlusGroupEdge <= 0 || - !is_centerpoint_elem( at2[centerpoint].el_number ) ) { - continue; - } - /* -- the centerpoint as depicted has no ChargeStruct flower --- - m = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[centerpoint].nCPlusGroupEdge-1 ); - if ( m < 0 || pBNS->edge[m].flow ) { - continue; - } - */ - /* find 2nd endpoint */ - for ( k = 0; k < at2[centerpoint].valence; k ++ ) { - if ( BOND_TYPE_SINGLE != ( BOND_TYPE_MASK & at2[centerpoint].bond_type[k] ) ) { - continue; - } - endpoint2 = at2[centerpoint].neighbor[k]; - if ( !at2[endpoint2].endpoint && atn[endpoint2].endpoint ) { - break; - } - } - if ( k == at2[centerpoint].valence ) { - continue; - } - /* the centerpoint and two extra endpoints have been found */ - pe = pBNS->edge + pVA[centerpoint].nCPlusGroupEdge - 1; - if ( !pe->flow ) { - continue; - } - pe2 = pBNS->edge + pVA[endpoint1].nCMinusGroupEdge - 1; - if ( pe2->flow ) { - continue; - } - delta = 1; - v1 = pe->neighbor1; - v2 = pe->neighbor12 ^ v1; - pe->flow -= delta; - pBNS->vert[v1].st_edge.flow -= delta; - pBNS->vert[v2].st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - pe2->forbidden &= inv_forbidden_edge_mask; /* allow the charge to move */ - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && - nDeltaCharge <= 1 ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else - if ( ret ) { - *pnTotalDelta += ret; - } else { - ret = RI_ERR_PROGR; - } - goto exit_function; - } else { - ret = 0; - pe->flow += delta; - pBNS->vert[v1].st_edge.flow += delta; - pBNS->vert[v2].st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - pe2->forbidden |= forbidden_edge_mask; - } - } - } - } - -exit_function: - /* remove bond fixation */ - RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask ); - AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE ); - return ret; -} -/*************************************************************************/ -int FillOutExtraFixedHDataRestr( StrFromINChI *pStruct ) -{ - int i, j, k, len, ret = 0; - AT_NUMB *pNum; - for ( i = 0; i < TAUT_NUM; i ++ ) { - if ( pStruct->pOneINChI_Aux[i] ) { - pNum = (pStruct->pOneINChI_Aux[i]->nIsotopicOrigAtNosInCanonOrd && - pStruct->pOneINChI_Aux[i]->nIsotopicOrigAtNosInCanonOrd[0])? - pStruct->pOneINChI_Aux[i]->nIsotopicOrigAtNosInCanonOrd: - (pStruct->pOneINChI_Aux[i]->nOrigAtNosInCanonOrd && - pStruct->pOneINChI_Aux[i]->nOrigAtNosInCanonOrd[0])? - pStruct->pOneINChI_Aux[i]->nOrigAtNosInCanonOrd : NULL; - } else { - pNum = NULL; - } - if ( pNum ) { - len = pStruct->num_atoms * sizeof(pStruct->nCanon2Atno[0][0]); - if ( !pStruct->nCanon2Atno[i] && - !(pStruct->nCanon2Atno[i] = (AT_NUMB *) inchi_malloc( len )) || - !pStruct->nAtno2Canon[i] && - !(pStruct->nAtno2Canon[i] = (AT_NUMB *) inchi_malloc( len ))) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - - INCHI_HEAPCHK - - memcpy( pStruct->nCanon2Atno[i], pNum, len ); /* ??? the next for(...) fills it out */ - - INCHI_HEAPCHK - - for ( j = 0; j < pStruct->num_atoms; j ++ ) { - k = pNum[j]-1; /* atom number */ - pStruct->nCanon2Atno[i][j] = (AT_NUMB)k; - pStruct->nAtno2Canon[i][k] = (AT_NUMB)j; - INCHI_HEAPCHK - } - } else - if ( !i ) { - ret = RI_ERR_PROGR; - goto exit_function; - } else { - if ( pStruct->nCanon2Atno[i] ) { - inchi_free( pStruct->nCanon2Atno[i] ); - pStruct->nCanon2Atno[i] = NULL; - } - INCHI_HEAPCHK - if ( pStruct->nAtno2Canon[i] ) { - inchi_free( pStruct->nAtno2Canon[i] ); - pStruct->nAtno2Canon[i] = NULL; - } - INCHI_HEAPCHK - } - } - -exit_function: - return ret; -} -/*************************************************************************/ -int FillOutExtraFixedHDataInChI( StrFromINChI *pStruct, INChI *pInChI[] ) -{ - int ret = 0; - /*--- allocate memory for Mobile/Fixed-H data from the input InChI ---*/ - if ( NULL == pStruct->endpoint ) { - pStruct->endpoint = (AT_NUMB *)inchi_calloc(pStruct->num_atoms, sizeof(pStruct->endpoint[0])); - } else { - memset( pStruct->endpoint, 0, pStruct->num_atoms * sizeof(pStruct->endpoint[0] ) ); - } - if ( NULL == pStruct->fixed_H ) { - pStruct->fixed_H = (S_CHAR *) inchi_malloc(pStruct->num_atoms * sizeof(pStruct->fixed_H[0])); - } - if ( !pStruct->endpoint || !pStruct->fixed_H ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - /*--- fill out Mobile/Fixed-H data from the input InChI ---*/ - GetTgroupInfoFromInChI( &pStruct->ti, NULL, pStruct->endpoint, pInChI[1] ); - if ( pInChI[0]->nNum_H_fixed ) { - memcpy( pStruct->fixed_H, pInChI[0]->nNum_H_fixed, pStruct->num_atoms * sizeof(pStruct->fixed_H[0]) ); - } else { - memset( pStruct->fixed_H, 0, pStruct->num_atoms * sizeof(pStruct->fixed_H[0]) ); - } - -exit_function: - return ret; -} -/***********************************************************************************************/ -int FillOutCMP2FHINCHI( StrFromINChI *pStruct, inp_ATOM *at2, VAL_AT *pVA, INChI *pInChI[], CMP2FHINCHI *pc2i ) -{ - int ret = 0, i, j; - int bFixHRevrsExists = pInChI[1] && pInChI[1]->nNumberOfAtoms > 0 && !pInChI[1]->bDeleted; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && - pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; - S_CHAR *num_Fixed_H_Revrs = pStruct->pOneINChI[0]->nNum_H_fixed? pStruct->pOneINChI[0]->nNum_H_fixed : NULL; - /* atom number in structure that produced original InChI is atom number in all inp_ATOM *atoms */ - /* atom number in structure that produced restored InChI is in nAtomRevrs[]: */ - AT_NUMB *nAtno2CanonRevrs = pStruct->nAtno2Canon[0]; - S_CHAR *pnMobHInChI = (pInChI[1] && pInChI[1]->nNum_H)? pInChI[1]->nNum_H : - (pInChI[0] && pInChI[0]->nNum_H)? pInChI[0]->nNum_H : NULL; - S_CHAR *pnMobHRevrs = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNum_H)? - pStruct->pOneINChI[1]->nNum_H : - (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)? - pStruct->pOneINChI[0]->nNum_H : NULL; - int nNumTgHInChI, nNumTgMInChI, nNumTgHRevrs, nNumTgMRevrs; - memset( pc2i, 0, sizeof(*pc2i) ); - pc2i->nNumTgInChI = pStruct->ti.num_t_groups; - pc2i->nNumTgRevrs = pStruct->One_ti.num_t_groups; - pc2i->bHasDifference |= pc2i->nNumTgInChI != pc2i->nNumTgRevrs; - - pc2i->nNumRemHInChI = pStruct->nNumRemovedProtonsMobHInChI; - pc2i->nNumRemHRevrs = pStruct->One_ti.tni.nNumRemovedProtons; - pc2i->bHasDifference |= pc2i->nNumRemHInChI != pc2i->nNumRemHRevrs; - - pc2i->bFixedHLayerExistsRevrs = bFixHRevrsExists; - pc2i->bHasDifference |= !bFixHRevrsExists; - - for ( i = 0; i < pStruct->ti.num_t_groups && i < pStruct->One_ti.num_t_groups; i ++ ) { - nNumTgHInChI = pStruct->ti.t_group[i].num[0] - pStruct->ti.t_group[i].num[1]; - nNumTgMInChI = pStruct->ti.t_group[i].num[1]; - nNumTgHRevrs = pStruct->One_ti.t_group[i].num[0] - pStruct->One_ti.t_group[i].num[1]; - nNumTgMRevrs = pStruct->One_ti.t_group[i].num[1]; - - pc2i->bHasDifference |= nNumTgHInChI != nNumTgHRevrs; - pc2i->bHasDifference |= nNumTgMInChI != nNumTgMRevrs; - - if ( pStruct->ti.t_group[i].nNumEndpoints == - pStruct->One_ti.t_group[i].nNumEndpoints ) { - - if ( nNumTgHInChI != nNumTgHRevrs ) { - pc2i->nNumTgDiffH ++; - } - if ( nNumTgMInChI != nNumTgMRevrs ) { - pc2i->nNumTgDiffMinus ++; - } - } - pc2i->bHasDifference |= pStruct->ti.t_group[i].nNumEndpoints != - pStruct->One_ti.t_group[i].nNumEndpoints; - - pc2i->nNumTgHInChI += nNumTgHInChI; - pc2i->nNumTgMInChI += nNumTgMInChI; - pc2i->nNumTgHRevrs += nNumTgHRevrs; - pc2i->nNumTgMRevrs += nNumTgMRevrs; - - } - for ( ; i < pStruct->ti.num_t_groups; i ++ ) { - nNumTgHInChI = pStruct->ti.t_group[i].num[0] - pStruct->ti.t_group[i].num[1]; - nNumTgMInChI = pStruct->ti.t_group[i].num[1]; - pc2i->nNumTgHInChI += nNumTgHInChI; - pc2i->nNumTgMInChI += nNumTgMInChI; - pc2i->bHasDifference |= 1; - } - for ( ; i < pStruct->One_ti.num_t_groups; i ++ ) { - nNumTgHRevrs = pStruct->One_ti.t_group[i].num[0] - pStruct->One_ti.t_group[i].num[1]; - nNumTgMRevrs = pStruct->One_ti.t_group[i].num[1]; - pc2i->nNumTgHRevrs += nNumTgHRevrs; - pc2i->nNumTgMRevrs += nNumTgMRevrs; - pc2i->bHasDifference |= 1; - - } - for ( i = j = 0; i < pStruct->num_atoms; i ++ ) { - /* i = original InChI canonical number - 1 */ - /* k = atom number from InChI created out of restored Fixed-H structure */ - int iCanonRevrs = nAtno2CanonRevrs[i]; - int endptInChI = pStruct->endpoint[i]; /* endpoint in InChI */ - int endptRevrs = at_Mobile_H_Revrs? at_Mobile_H_Revrs[i].endpoint : 0; - int nFixHInChI = pStruct->fixed_H[i]; - int nFixHRevrs = num_Fixed_H_Revrs? num_Fixed_H_Revrs[iCanonRevrs]:0; - int nMobHInChI = pnMobHInChI? pnMobHInChI[i]:0; - int nMobHRevrs = pnMobHRevrs? pnMobHRevrs[iCanonRevrs]:0; - if ( /*(!endptInChI || !endptRevrs) &&*/ (nFixHInChI != nFixHRevrs ) || - (!endptInChI != !endptRevrs) || nMobHInChI != nMobHRevrs ) { - /* in InChI or reversed InChI atom[i] is not tautomeric */ - /* and number of fixed-H on the atom[i] differs */ - if ( j >= MAX_DIFF_FIXH ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - pc2i->c2at[j].endptInChI = endptInChI; - pc2i->c2at[j].endptRevrs = endptRevrs; - pc2i->bHasDifference |= !endptInChI != !endptRevrs; - pc2i->c2at[j].atomNumber = i; - pc2i->c2at[j].nValElectr = pVA[i].cNumValenceElectrons; - pc2i->c2at[j].nPeriodNum = pVA[i].cPeriodicRowNumber; - pc2i->c2at[j].nFixHInChI = nFixHInChI; - pc2i->c2at[j].nFixHRevrs = nFixHRevrs; - pc2i->bHasDifference |= nFixHInChI != nFixHRevrs; - pc2i->c2at[j].nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H[i] : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H[i] : 0; - pc2i->c2at[j].nMobHRevrs = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNum_H)? - pStruct->pOneINChI[1]->nNum_H[iCanonRevrs] : - (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)? - pStruct->pOneINChI[0]->nNum_H[iCanonRevrs] : 0; - pc2i->nNumDiffMobH += (nMobHInChI != nMobHRevrs && !endptRevrs && !endptInChI); - pc2i->bHasDifference |= nMobHInChI != nMobHRevrs; - pc2i->c2at[j].nNumHRevrs = at2[i].num_H; - pc2i->c2at[j].nAtChargeRevrs = at2[i].charge; - j ++; - } - pc2i->nNumEndpInChI += (endptInChI != 0); - pc2i->nNumEndpRevrs += (endptRevrs != 0); - - if ( !pVA[i].cMetal ) { - pc2i->nChargeFixHRevrsNonMetal += at2[i].charge; - pc2i->nChargeMobHRevrsNonMetal += at_Mobile_H_Revrs? at_Mobile_H_Revrs[i].charge : 0; - } - - /*pStruct->bExtract |= EXTRACT_STRUCT_NUMBER;*/ - } - pc2i->nChargeFixHInChI = pInChI[0]? pInChI[0]->nTotalCharge : 0; - pc2i->nChargeMobHInChI = pInChI[1]? pInChI[1]->nTotalCharge : 0; - - pc2i->nChargeMobHRevrs = pStruct->pOneINChI[1]? pStruct->pOneINChI[1]->nTotalCharge : - pStruct->pOneINChI[0]? pStruct->pOneINChI[0]->nTotalCharge : 0; - pc2i->nChargeFixHRevrs = pStruct->pOneINChI[0]? pStruct->pOneINChI[0]->nTotalCharge : 0; - - pc2i->bHasDifference |= pc2i->nChargeFixHInChI != pc2i->nChargeFixHRevrs; - pc2i->bHasDifference |= pc2i->nChargeMobHInChI != pc2i->nChargeMobHRevrs; - -exit_function: - pc2i->len_c2at = j; - - return ret; -} -/***********************************************************************************************/ -int FillOutCMP2MHINCHI( StrFromINChI *pStruct, ALL_TC_GROUPS *pTCGroups, inp_ATOM *at2, - VAL_AT *pVA, INChI *pInChI[], CMP2MHINCHI *pc2i ) -{ - int ret = 0, i, j, iat; - int bFixHRevrsExists = pInChI[1] && pInChI[1]->nNumberOfAtoms > 0 && !pInChI[1]->bDeleted; - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[0] && - pStruct->pOne_norm_data[0]->at)? pStruct->pOne_norm_data[0]->at : NULL; - /* atom number in structure that produced original InChI is atom number in all inp_ATOM *atoms */ - /* atom number in structure that produced restored InChI is in nAtomRevrs[]: */ - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - AT_NUMB *nAtno2CanonRevrs = pStruct->nAtno2Canon[0]; - S_CHAR *pnMobHInChI = (pInChI[0] && pInChI[0]->nNum_H)? pInChI[0]->nNum_H : NULL; - S_CHAR *pnMobHRevrs = (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)? - pStruct->pOneINChI[0]->nNum_H : NULL; - int nNumTgHInChI, nNumTgMInChI, nNumTgHRevrs, nNumTgMRevrs; - memset( pc2i, 0, sizeof(*pc2i) ); - pc2i->nNumTgInChI = pStruct->ti.num_t_groups; - pc2i->nNumTgRevrs = pStruct->One_ti.num_t_groups; - pc2i->bHasDifference |= pc2i->nNumTgInChI != pc2i->nNumTgRevrs; - - pc2i->nNumRemHInChI = pStruct->nNumRemovedProtonsMobHInChI; - pc2i->nNumRemHRevrs = pStruct->One_ti.tni.nNumRemovedProtons; - /*pc2i->bHasDifference |= pc2i->nNumRemHInChI != pc2i->nNumRemHRevrs;*/ - - pc2i->bFixedHLayerExistsRevrs = bFixHRevrsExists; - /*pc2i->bHasDifference |= !bFixHRevrsExists;*/ - - for ( i = 0; i < pStruct->ti.num_t_groups; i ++ ) { - int jFst = pStruct->ti.t_group[i].nFirstEndpointAtNoPos; - int jNum = pStruct->ti.t_group[i].nNumEndpoints; - int is_N, is_O; - for ( j = 0; j < jNum; j ++ ) { - iat = pStruct->ti.nEndpointAtomNumber[jFst + j]; - is_N = pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1; - is_O = pVA[iat].cNumValenceElectrons == 6; - if ( is_N + is_O != 1 ) { - return RI_ERR_SYNTAX; - } - pc2i->nNumTgNInChI += is_N; - pc2i->nNumTgOInChI += is_O; - if ( at2[iat].chem_bonds_valence == at2[iat].valence ) { - /* donor */ - if ( is_N ) { - /* N */ - pc2i->nNumTgNHInChI += at2[iat].charge == 0 && at2[iat].num_H == 1; - pc2i->nNumTgNH2InChI += at2[iat].charge == 0 && at2[iat].num_H == 2; - pc2i->nNumTgNMinusInChI += at2[iat].charge == -1 && at2[iat].num_H == 0; - pc2i->nNumTgNHMinusInChI += at2[iat].charge == -1 && at2[iat].num_H == 1; - } else { - /* O, S, Se, Te */ - pc2i->nNumTgOHInChI += at2[iat].charge == 0 && at2[iat].num_H == 1; - pc2i->nNumTgOMinusInChI += at2[iat].charge == -1 && at2[iat].num_H == 0; - } - } else - if ( at2[iat].chem_bonds_valence == at2[iat].valence+1 ) { - /* donor */ - if ( is_N ) { - /* N */ - pc2i->nNumTgDBNHInChI += at2[iat].charge == 0 && at2[iat].num_H == 1; - pc2i->nNumTgDBNMinusInChI += at2[iat].charge == -1 && at2[iat].num_H == 0; - pc2i->nNumTgDBNInChI += at2[iat].charge == 0 && at2[iat].num_H == 0; - } else { - /* O, S, Se, Te */ - pc2i->nNumTgDBOInChI += at2[iat].charge == 0 && at2[iat].num_H == 0; - } - } - } - } - for ( i = 0; i < pStruct->One_ti.num_t_groups; i ++ ) { - int jFst = pStruct->One_ti.t_group[i].nFirstEndpointAtNoPos; - int jNum = pStruct->One_ti.t_group[i].nNumEndpoints; - int is_N, is_O; - for ( j = 0; j < jNum; j ++ ) { - iat = nCanon2AtnoRevrs[(int)pStruct->One_ti.nEndpointAtomNumber[jFst + j]]; - is_N = pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1; - is_O = pVA[iat].cNumValenceElectrons == 6; - if ( is_N + is_O != 1 ) { - return RI_ERR_PROGR; - } - pc2i->nNumTgNRevrs += is_N; - pc2i->nNumTgORevrs += is_O; - if ( at2[iat].chem_bonds_valence == at2[iat].valence ) { - /* donor */ - if ( is_N ) { - /* N */ - pc2i->nNumTgNHRevrs += at2[iat].charge == 0 && at2[iat].num_H == 1; - pc2i->nNumTgNH2Revrs += at2[iat].charge == 0 && at2[iat].num_H == 2; - pc2i->nNumTgNMinusRevrs += at2[iat].charge == -1 && at2[iat].num_H == 0; - pc2i->nNumTgNHMinusRevrs += at2[iat].charge == -1 && at2[iat].num_H == 1; - } else { - /* O, S, Se, Te */ - pc2i->nNumTgOHRevrs += at2[iat].charge == 0 && at2[iat].num_H == 1; - pc2i->nNumTgOMinusRevrs += at2[iat].charge == -1 && at2[iat].num_H == 0; - } - } else - if ( at2[iat].chem_bonds_valence == at2[iat].valence+1 ) { - /* donor */ - if ( is_N ) { - /* N */ - pc2i->nNumTgDBNHRevrs += at2[iat].charge == 0 && at2[iat].num_H == 1; - pc2i->nNumTgDBNMinusRevrs += at2[iat].charge == -1 && at2[iat].num_H == 0; - pc2i->nNumTgDBNRevrs += at2[iat].charge == 0 && at2[iat].num_H == 0; - } else { - /* O, S, Se, Te */ - pc2i->nNumTgDBORevrs += at2[iat].charge == 0 && at2[iat].num_H == 0; - } - } - } - } - - for ( i = 0; i < pStruct->ti.num_t_groups && i < pStruct->One_ti.num_t_groups; i ++ ) { - nNumTgHInChI = pStruct->ti.t_group[i].num[0] - pStruct->ti.t_group[i].num[1]; - nNumTgMInChI = pStruct->ti.t_group[i].num[1]; - nNumTgHRevrs = pStruct->One_ti.t_group[i].num[0] - pStruct->One_ti.t_group[i].num[1]; - nNumTgMRevrs = pStruct->One_ti.t_group[i].num[1]; - - pc2i->bHasDifference |= nNumTgHInChI != nNumTgHRevrs; - pc2i->bHasDifference |= nNumTgMInChI != nNumTgMRevrs; - - if ( pStruct->ti.t_group[i].nNumEndpoints == - pStruct->One_ti.t_group[i].nNumEndpoints ) { - - if ( nNumTgHInChI != nNumTgHRevrs ) { - pc2i->nNumTgDiffH ++; - } - if ( nNumTgMInChI != nNumTgMRevrs ) { - pc2i->nNumTgDiffMinus ++; - } - } - pc2i->bHasDifference |= pStruct->ti.t_group[i].nNumEndpoints != - pStruct->One_ti.t_group[i].nNumEndpoints; - - pc2i->nNumTgHInChI += nNumTgHInChI; - pc2i->nNumTgMInChI += nNumTgMInChI; - pc2i->nNumTgHRevrs += nNumTgHRevrs; - pc2i->nNumTgMRevrs += nNumTgMRevrs; - - } - for ( ; i < pStruct->ti.num_t_groups; i ++ ) { - nNumTgHInChI = pStruct->ti.t_group[i].num[0] - pStruct->ti.t_group[i].num[1]; - nNumTgMInChI = pStruct->ti.t_group[i].num[1]; - pc2i->nNumTgHInChI += nNumTgHInChI; - pc2i->nNumTgMInChI += nNumTgMInChI; - pc2i->bHasDifference |= 1; - } - for ( ; i < pStruct->One_ti.num_t_groups; i ++ ) { - nNumTgHRevrs = pStruct->One_ti.t_group[i].num[0] - pStruct->One_ti.t_group[i].num[1]; - nNumTgMRevrs = pStruct->One_ti.t_group[i].num[1]; - pc2i->nNumTgHRevrs += nNumTgHRevrs; - pc2i->nNumTgMRevrs += nNumTgMRevrs; - pc2i->bHasDifference |= 1; - - } - for ( i = j = 0; i < pStruct->num_atoms; i ++ ) { - /* i = original InChI canonical number - 1 */ - /* k = atom number from InChI created out of restored Fixed-H structure */ - int iCanonRevrs = nAtno2CanonRevrs[i]; - int endptInChI = at2[i].endpoint; /* endpoint in InChI */ - int endptRevrs = at_Mobile_H_Revrs? at_Mobile_H_Revrs[i].endpoint : 0; - int nMobHInChI = pnMobHInChI? pnMobHInChI[i]:0; - int nMobHRevrs = pnMobHRevrs? pnMobHRevrs[iCanonRevrs]:0; - if ( (!endptInChI != !endptRevrs) || nMobHInChI != nMobHRevrs ) { - /* in InChI or reversed InChI atom[i] is not tautomeric */ - /* and number of fixed-H on the atom[i] differs */ - if ( j >= MAX_DIFF_FIXH ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - pc2i->c2at[j].endptInChI = endptInChI; - pc2i->c2at[j].endptRevrs = endptRevrs; - pc2i->bHasDifference |= !endptInChI != !endptRevrs; - pc2i->c2at[j].atomNumber = i; - pc2i->c2at[j].nValElectr = pVA[i].cNumValenceElectrons; - pc2i->c2at[j].nPeriodNum = pVA[i].cPeriodicRowNumber; - pc2i->c2at[j].nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H[i] : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H[i] : 0; - pc2i->c2at[j].nMobHRevrs = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNum_H)? - pStruct->pOneINChI[1]->nNum_H[iCanonRevrs] : - (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)? - pStruct->pOneINChI[0]->nNum_H[iCanonRevrs] : 0; - - pc2i->nNumDiffMobH += (nMobHInChI != nMobHRevrs && !endptRevrs && !endptInChI); - pc2i->bHasDifference |= (nMobHInChI != nMobHRevrs); - pc2i->c2at[j].nNumHRevrs = at2[i].num_H; - pc2i->c2at[j].nAtChargeRevrs = at2[i].charge; - j ++; - } - pc2i->nNumEndpInChI += (endptInChI != 0); - pc2i->nNumEndpRevrs += (endptRevrs != 0); - - if ( !pVA[i].cMetal ) { - pc2i->nChargeMobHRevrsNonMetal += (at_Mobile_H_Revrs && !at_Mobile_H_Revrs[i].endpoint)? at_Mobile_H_Revrs[i].charge : 0; - } - - - /*pStruct->bExtract |= EXTRACT_STRUCT_NUMBER;*/ - } - pc2i->nChargeMobHRevrsNonMetal += pTCGroups->tgroup_charge; - - pc2i->nChargeMobHInChI = pInChI[0]? pInChI[0]->nTotalCharge : 0; - - pc2i->nChargeMobHRevrs = pStruct->pOneINChI[0]? pStruct->pOneINChI[0]->nTotalCharge : 0; - - pc2i->bHasDifference |= pc2i->nChargeMobHInChI != pc2i->nChargeMobHRevrs; - -exit_function: - pc2i->len_c2at = j; - - return ret; -} -/******************************************************************************************************/ -int NormalizeAndCompare(ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, - StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, INChI *pInChI[], long num_inp, int bHasSomeFixedH, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask) -{ - int i; - int err; - ICR icr, icr2; - int num_norm_endpoints, num_endpoints, num_norm_t_groups, num_mobile, num_norm_mobile, ret = 0; -#if ( bRELEASE_VERSION == 0 ) -#ifndef TARGET_API_LIB - const char *szCurHdr = (ip->pSdfValue && ip->pSdfValue[0])? ip->pSdfValue : "???"; - int iComponent = pTCGroups->iComponent; -#endif -#endif - T_GROUP_INFO *t_group_info = NULL; - inp_ATOM *at_norm = NULL; /* normalized */ - inp_ATOM *at_prep = NULL; /* preprocessed */ - INCHI_MODE cmpInChI, cmpInChI2; - int nDeltaPrev, nDeltaCur; - int iOrigInChI, iRevrInChI; - - - /***********************************************************/ - /* normalize and create one component InChI */ - /***********************************************************/ - ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - &t_group_info, &at_norm, &at_prep ); - if ( ret < 0 ) { -#if ( bRELEASE_VERSION == 0 ) -#ifndef TARGET_API_LIB - fprintf( stdout, "\nERROR in MakeOneInchi-1: %ld %s Comp:%d %c%c Err:%d\n", num_inp, - szCurHdr? szCurHdr: "???", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', ret); -#endif -#endif - goto exit_function; - } - if ( pStruct->bMobileH == TAUT_NON ) { - /* these indexes are used to compare Mobile-H InChI */ - iOrigInChI = (pInChI[1] && pInChI[1]->nNumberOfAtoms && !pInChI[1]->bDeleted)? 1 : 0; - iRevrInChI = (pStruct->pOneINChI[1] &&pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted)? 1 : 0; - } else { - iOrigInChI = 0; - iRevrInChI = 0; - } - - /************************************************************/ - /* compare */ - /************************************************************/ - if ( pStruct->iMobileH == TAUT_NON && (ret = FillOutExtraFixedHDataRestr( pStruct )) ) { - goto exit_function; - } - cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *a2*/, &icr, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - /********** InChI from restored structure has LESS hydrogen atoms ******************************/ - if ( (cmpInChI & IDIF_LESS_H) && at_prep && 0 < (nDeltaCur = icr.tot_num_H2 - icr.tot_num_H1) ) { - do { - ret = FixLessHydrogenInFormula( pBNS, pBD, pStruct, at, at2, at_prep, pVA, pTCGroups, - pnNumRunBNS, pnTotalDelta, forbidden_edge_mask ); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret ) { - /* Probably success. The changes are in pBNS. Create new InChI out of the new restored structure */ - ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - &t_group_info, &at_norm, &at_prep ); - if ( ret < 0 ) { -#if ( bRELEASE_VERSION == 0 ) -#ifndef TARGET_API_LIB - fprintf( stdout, "\nERROR in MakeOneInchi-2: %ld %s Comp:%d %c%c Err:%d\n", num_inp, - szCurHdr? szCurHdr: "???", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', ret); -#endif -#endif - goto exit_function; - } - /* compare new InChI to the original InChI */ - if ( pStruct->bMobileH == TAUT_NON ) { - iRevrInChI = (pStruct->pOneINChI[1] &&pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted)? 1 : 0; - } else { - iRevrInChI = 0; - } - if ( pStruct->iMobileH == TAUT_NON && (ret = FillOutExtraFixedHDataRestr( pStruct )) ) { - goto exit_function; - } - cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL, &icr, &err ); - nDeltaPrev = nDeltaCur; - nDeltaCur = icr.tot_num_H2 - icr.tot_num_H1; - } else { - break; - } - } while( (cmpInChI & IDIF_LESS_H) && at_prep && nDeltaCur && nDeltaCur < nDeltaPrev ); - } - /********** InChI from restored structure has MORE hydrogen atoms ******************************/ - if ( (cmpInChI & IDIF_MORE_H) && at_prep && 0 < (nDeltaCur = icr.tot_num_H1 - icr.tot_num_H2) ) { - do { - ret = FixMoreHydrogenInFormula( pBNS, pBD, pStruct, at, at2, at_prep, pVA, pTCGroups, - pnNumRunBNS, pnTotalDelta, forbidden_edge_mask ); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret ) { - /* Probably success. The changes are in pBNS. Create new InChI out of the new restored structure */ - ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - &t_group_info, &at_norm, &at_prep ); - if ( ret < 0 ) { -#if ( bRELEASE_VERSION == 0 ) -#ifndef TARGET_API_LIB - fprintf( stdout, "\nERROR in MakeOneInchi-3: %ld %s Comp:%d %c%c Err:%d\n", num_inp, - szCurHdr? szCurHdr: "???", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', ret); -#endif -#endif - goto exit_function; - } - /* compare new InChI to the original InChI */ - if ( pStruct->bMobileH == TAUT_NON ) { - iRevrInChI = (pStruct->pOneINChI[1] &&pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted)? 1 : 0; - } else { - iRevrInChI = 0; - } - if ( pStruct->iMobileH == TAUT_NON && (ret = FillOutExtraFixedHDataRestr( pStruct )) ) { - goto exit_function; - } - cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL, &icr, &err ); - nDeltaPrev = nDeltaCur; - nDeltaCur = icr.tot_num_H1 - icr.tot_num_H2; - } else { - break; - } - } while( (cmpInChI & IDIF_MORE_H) && at_prep && nDeltaCur && nDeltaCur < nDeltaPrev ); - } - /***************** Fix non-taut atoms normalized to tautomeric endpoints ***********************/ - if ( (cmpInChI & IDIF_EXTRA_TG_ENDP) && at_norm && 0 < (nDeltaCur = icr.num_endp_in1_only) ) { - do { - ret = FixRemoveExtraTautEndpoints( pBNS, pBD, pStruct, at, at2, at_prep, at_norm, pVA, pTCGroups, &icr, - pnNumRunBNS, pnTotalDelta, forbidden_edge_mask ); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret ) { - /* Probably success. The changes are in pBNS. Create new InChI out of the new restored structure */ - ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - &t_group_info, &at_norm, &at_prep ); - if ( ret < 0 ) { -#if ( bRELEASE_VERSION == 0 ) -#ifndef TARGET_API_LIB - fprintf( stdout, "\nERROR in MakeOneInchi-4: %ld %s Comp:%d %c%c Err:%d\n", num_inp, - szCurHdr? szCurHdr: "???", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', ret); -#endif -#endif - goto exit_function; - } - /* compare new InChI to the original InChI */ - if ( pStruct->bMobileH == TAUT_NON ) { - iRevrInChI = (pStruct->pOneINChI[1] &&pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted)? 1 : 0; - } else { - iRevrInChI = 0; - } - if ( pStruct->iMobileH == TAUT_NON && (ret = FillOutExtraFixedHDataRestr( pStruct )) ) { - goto exit_function; - } - cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL, &icr, &err ); - nDeltaPrev = nDeltaCur; - nDeltaCur = icr.num_endp_in1_only; - } else { - break; - } - } while( (cmpInChI & IDIF_EXTRA_TG_ENDP) && at_norm && nDeltaCur && nDeltaCur < nDeltaPrev ); - } - /************************ case of Fixed-H ******************************************************/ - - if ( pStruct->bMobileH == TAUT_NON ) { - int num_tries = 0; - do { - if ( 0 > (ret = FixFixedHRestoredStructure(ip, sd, pBNS, pBD, pStruct, at, at2, at3, pVA, pTCGroups, - &t_group_info, &at_norm, &at_prep, pInChI, - num_inp, bHasSomeFixedH, pnNumRunBNS, pnTotalDelta, forbidden_edge_mask, - forbidden_stereo_edge_mask) ) ) { - goto exit_function; - } - } while( num_tries ++ < 2 && ret > 0 ); - } - /************************ case of Fixed-H ******************************************************/ - if ( pStruct->bMobileH == TAUT_YES ) { - if ( 0 > (ret = FixMobileHRestoredStructure(ip, sd, pBNS, pBD, pStruct, at, at2, at3, pVA, pTCGroups, - &t_group_info, &at_norm, &at_prep, pInChI, - num_inp, bHasSomeFixedH, pnNumRunBNS, pnTotalDelta, forbidden_edge_mask, - forbidden_stereo_edge_mask) ) ) { - goto exit_function; - } - } - /**********************************************************************************************/ - /* stereo */ - cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *a2*/, &icr, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - cmpInChI2 = 0; - memset ( &icr2, 0, sizeof(icr2) ); - if ( iRevrInChI || iOrigInChI ) { - /* additional mobile-H compare in case of Fixed-H */ - cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *a2*/, &icr2, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - } - ret = FixRestoredStructureStereo( cmpInChI, &icr, cmpInChI2, &icr2, - ip, sd, pBNS, pBD, pStruct, at, at2, at3, pVA, pTCGroups, - &t_group_info, &at_norm, &at_prep, pInChI, - num_inp, pnNumRunBNS, pnTotalDelta, forbidden_edge_mask, - forbidden_stereo_edge_mask); - - if ( ret < 0 ) { - goto exit_function; - } -#if ( FIX_ADD_PROTON_FOR_ADP == 1 ) - /************************ check and fix ADP by adding a proton (dummy) *************************/ - if ( cmpInChI && pTCGroups->num_tgroups && pBNS->tot_st_cap > pBNS->tot_st_flow ) { - ret = FixAddProtonForADP( pBNS, pBD, pStruct, at, at2, at_prep, pVA, pTCGroups, &icr, - pnNumRunBNS, pnTotalDelta, forbidden_edge_mask ); - if ( ret < 0 ) { - goto exit_function; - } - } -#endif - /* moved to MakeOneInChIOutOfStrFromINChI(): - pStruct->nNumRemovedProtons = (pStruct->iMobileH == TAUT_YES)? pStruct->One_ti.tni.nNumRemovedProtons : 0; - */ - - /* count endpoints */ - num_endpoints = 0; - num_norm_endpoints = 0; - num_norm_t_groups = 0; - num_mobile = 0; - num_norm_mobile = 0; - at_norm = pStruct->pOne_norm_data[0]->at; - for ( i = 0; i < pTCGroups->num_tgroups; i ++ ) { - num_endpoints += pTCGroups->pTCG[i].num_edges; - num_mobile += pTCGroups->pTCG[i].tg_num_H + pTCGroups->pTCG[i].tg_num_Minus; - } - - if ( t_group_info ) { - /* after canonicalization, t_group_info->t_group[i].num[0] = number of H */ - /* t_group_info->t_group[i].num[1] = number of (-) */ - for ( i = 0; i < t_group_info->num_t_groups; i ++ ) { - if ( t_group_info->t_group[i].num[0] ) { - num_norm_t_groups ++; - num_norm_endpoints += t_group_info->t_group[i].nNumEndpoints; - num_norm_mobile += t_group_info->t_group[i].num[0]+t_group_info->t_group[i].num[1]; - } - } - } -#if ( bRELEASE_VERSION == 0 ) -#ifndef TARGET_API_LIB - if ( num_norm_t_groups != pTCGroups->num_tgroups || num_norm_endpoints != num_endpoints ) { - /* need aggressive (de)protonation */ - /* pStruct->bExtract |= EXTRACT_STRUCT_NUMBER; */ - fprintf( stdout, "NORMCOMP: %s comp=%d %c%c: InChI/NormRvrs NumTg=%d/%d NumEndp=%d/%d\n", - (*ip).pSdfValue, (*pTCGroups).iComponent, - pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', - pTCGroups->num_tgroups, num_norm_t_groups, - num_endpoints, num_norm_endpoints ); - - } -#endif -#endif - -exit_function: - - for( i = 0; i < TAUT_NUM; i ++ ) { - Free_INChI( &pStruct->pOneINChI[i] ); - Free_INChI_Aux( &pStruct->pOneINChI_Aux[i] ); - FreeInpAtomData( pStruct->pOne_norm_data[i] ); - if ( pStruct->pOne_norm_data[i] ) { - inchi_free( pStruct->pOne_norm_data[i] ); - pStruct->pOne_norm_data[i] = NULL; - } - } - free_t_group_info( &pStruct->One_ti ); - return ret; -} -/******************************************************************************************************/ -/* Find A=X< where all bonds to X except A=X are marked as stereogenic; temporary allow stereobonds */ -/* change and make A=X bonds single */ -int CheckAndRefixStereobonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ - int forbidden_edge_stereo = BNS_EDGE_FORBIDDEN_MASK; - int inv_forbidden_edge_stereo = ~forbidden_edge_stereo; - - int i, k, ne, j1, j2, num_wrong, num_fixed; - int ret2, retBNS, ret; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - EDGE_LIST FixedEdges, WrongEdges, CarbonChargeEdges; - - BNS_EDGE *pEdge; - Vertex v1, v2; - BNS_VERTEX *pv1, *pv2; - - ret = 0; - - /* to simplify, prepare new at[] from pBNS */ - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - return ret; - } - - num_wrong = 0; - /* find wrong double bonds */ - for ( i = 0; i < num_at; i ++ ) { - if ( at2[i].valence == 3 && - at2[i].chem_bonds_valence - at2[i].valence == 1 && - at2[i].sb_parity[0] && at2[i].sb_parity[1] && !at2[i].sb_parity[2] && - (at2[i].bond_type[j1=(int)at2[i].sb_ord[0]] & BOND_TYPE_MASK) == BOND_TYPE_SINGLE && - (at2[i].bond_type[j2=(int)at2[i].sb_ord[1]] & BOND_TYPE_MASK) == BOND_TYPE_SINGLE && - j1 != j2 ) { - - num_wrong ++; - } - } - if ( !num_wrong ) { - return 0; - } - num_fixed = 0; - for ( i = 0; i < pBNS->num_bonds; i ++ ) { - pEdge = pBNS->edge + i; - if ( pEdge->forbidden & forbidden_edge_stereo ) { - num_fixed ++; - } - } - - /* there may be no fixed stereo bonds at all, see #87607 */ - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &FixedEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &WrongEdges, EDGE_LIST_CLEAR ); - - /* do not goto exit_function before reaching this point: EdgeLists have not been initiated */ - - if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { - goto exit_function; - } - if ( (ret = AllocEdgeList( &FixedEdges, num_fixed )) || - (ret = AllocEdgeList( &WrongEdges, num_wrong )) ) { - goto exit_function; - } - /* collect wrong double bonds and set flow=0 */ - for ( i = 0; i < num_at && WrongEdges.num_edges < num_wrong; i ++ ) { - if ( at2[i].valence == 3 && - at2[i].chem_bonds_valence - at2[i].valence == 1 && - at2[i].sb_parity[0] && at2[i].sb_parity[1] && !at2[i].sb_parity[2] && - (at2[i].bond_type[j1=(int)at2[i].sb_ord[0]] & BOND_TYPE_MASK) == BOND_TYPE_SINGLE && - (at2[i].bond_type[j2=(int)at2[i].sb_ord[1]] & BOND_TYPE_MASK) == BOND_TYPE_SINGLE && - j1 != j2 ) { - switch ( j1 + j2 ) { - case 1: /* 0, 1 */ - k = 2; - break; - case 2: /* 0, 2 */ - k = 1; - break; - case 3: /* 1, 2 */ - k = 0; - break; - default: - ret = RI_ERR_PROGR; - goto exit_function; - } - ne = pBNS->vert[i].iedge[k]; - pEdge = pBNS->edge + ne; - v1 = pEdge->neighbor1; - v2 = pEdge->neighbor12 ^ v1; - pv1 = pBNS->vert + v1; - pv2 = pBNS->vert + v2; - - if ( !pEdge->flow ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - pEdge->flow --; - pEdge->forbidden |= forbidden_edge_mask; - pv1->st_edge.flow --; - pv2->st_edge.flow --; - pBNS->tot_st_flow -= 2; - if ( ret = AddToEdgeList( &WrongEdges, ne, 0 )) { - goto exit_function; - } - } - } - /* remove forbidden mark from stereo bonds (unfix stereo bonds) */ - for ( i = 0; i < pBNS->num_bonds && FixedEdges.num_edges < num_fixed; i ++ ) { - pEdge = pBNS->edge + i; - if ( pEdge->forbidden & forbidden_edge_stereo ) { - pEdge->forbidden &= inv_forbidden_edge_stereo; - FixedEdges.pnEdges[FixedEdges.num_edges ++] = i; - } - } - /* Run BNS to move charges and rearrange bond orders */ - retBNS = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( retBNS < 0 ) { - goto exit_function; - } else - if ( retBNS > 0 ) { - *pnTotalDelta += retBNS; - } - /* remove forbidden_edge_mask and set forbidden_edge_stereo */ - RemoveForbiddenEdgeMask( pBNS, &WrongEdges, forbidden_edge_mask ); - /* allow carbon charges to change */ - RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); - /* fix previously unfixed stereo bonds */ - SetForbiddenEdgeMask( pBNS, &FixedEdges, forbidden_edge_stereo ); - /* Run BNS again in case not all edge flows are maximal */ - ret2 = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret2 < 0 ) { - goto exit_function; - } else - if ( ret2 > 0 ) { - *pnTotalDelta += retBNS; - } - ret = retBNS; - -exit_function: - - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &FixedEdges, EDGE_LIST_FREE ); - AllocEdgeList( &WrongEdges, EDGE_LIST_FREE ); - - return ret; -} -/******************************************************************************************************/ -/* Find and eliminate false Mobile-H groups: Cl(=O)3(-O(-)) => Cl(-)(=O)4 */ -int MoveChargeToRemoveCenerpoints(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ - int i, j, neigh, num_endpoints, num_success; - int num_donors, num_acceptors, bond_type, num_donors_O, num_acceptors_O, is_centerpoint_N, num_known_endpoints, num_wrong_neigh; - int ret2, ret_forbid_edges, ret, delta; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int forbidden_edge_test = BNS_EDGE_FORBIDDEN_TEST; - int bPossiblyIgnore = pStruct->charge >= 0 && (!pTCGroups->num_tgroups || pStruct->iMobileH == TAUT_NON && pStruct->ti.num_t_groups); - S_CHAR MobileChargeNeigh[MAXVAL], DoubleBondAcceptors[MAXVAL], DoubleBondNotONeigh[MAXVAL]; - int numMobileChargeNeigh, numDoubleBondAcceptors, numDoubleBondNotONeigh, numOtherDoubleBondOAcceptors=0; - EDGE_LIST ChargeListAllExcept_DB_O; - - - BNS_EDGE *pEdgeMinus, *pe; - Vertex v1m, v2m; - BNS_VERTEX *pv1m, *pv2m; - ret = 0; - num_success = 0; - - /* count O(+)H, N(+)H */ - - /* - if ( pStruct->charge >= 0 && (!pTCGroups->num_tgroups || pStruct->iMobileH == TAUT_NON && pStruct->ti.num_t_groups) ) { - goto exit_function; - } - */ - if ( ret = AllocEdgeList( &ChargeListAllExcept_DB_O, EDGE_LIST_CLEAR ) ) { - goto exit_function; - } - - - /* to simplify, prepare new at[] from pBNS */ - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } -#if ( FIND_RING_SYSTEMS == 1 ) - ret2 = MarkRingSystemsInp( at2, num_at, 0 ); - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } -#endif - /* mark bonds that cannot be tautomeric; do not forget to remove the marks later */ - ret_forbid_edges = SetForbiddenEdges( pBNS, at2, num_at, forbidden_edge_test ); - if ( ret_forbid_edges < 0 ) { - ret = ret_forbid_edges; - goto exit_function; - } - - for ( i = 0; i < num_at; i ++ ) { - if ( pVA[i].cNumValenceElectrons != 4 && /* not C, Si, Ge */ - !(pVA[i].nTautGroupEdge || pStruct->iMobileH == TAUT_NON && pStruct->endpoint && pStruct->endpoint[i] ) && - !at2[i].num_H && !at2[i].charge && at2[i].valence >= 2 && - at2[i].valence < at2[i].chem_bonds_valence && - is_centerpoint_elem( at2[i].el_number ) ) { - - is_centerpoint_N = (pVA[i].cNumValenceElectrons == 5 && (pVA[i].cPeriodicRowNumber == 1 || pVA[i].cMetal)); - /* look at the neighbors */ - numMobileChargeNeigh = numDoubleBondAcceptors = numDoubleBondNotONeigh = num_donors = num_acceptors = 0; - num_donors_O = num_acceptors_O = 0; - num_known_endpoints = num_wrong_neigh = 0; - for ( j = 0, num_endpoints = 0; j < at2[i].valence; j ++ ) { - neigh = at2[i].neighbor[j]; - if ( (at2[neigh].endpoint || pStruct->iMobileH == TAUT_NON && pStruct->endpoint && pStruct->endpoint[neigh]) || at2[neigh].charge > 0 ) { - num_known_endpoints ++; - continue; - } - if ( pBNS->edge[pBNS->vert[i].iedge[j]].forbidden & forbidden_edge_test ) { - continue; - } - bond_type = at2[i].bond_type[j] & BOND_TYPE_MASK; - if ( bond_type > BOND_TYPE_DOUBLE ) { - num_wrong_neigh ++; - continue; - } - if ( at2[neigh].num_H && bond_type == BOND_TYPE_SINGLE ) { - break; /* not this case */ - } - if ( at2[neigh].chem_bonds_valence - at2[neigh].charge - != get_endpoint_valence( at2[neigh].el_number ) ) { - if ( bond_type == BOND_TYPE_DOUBLE && pVA[neigh].cNumValenceElectrons != 6 ) { - DoubleBondNotONeigh[numDoubleBondNotONeigh ++] = j; - } - continue; - } - if ( at2[neigh].charge == -1 && bond_type == BOND_TYPE_SINGLE && - (pVA[neigh].nCMinusGroupEdge < 1 || pBNS->edge[pVA[neigh].nCMinusGroupEdge-1].flow != 1) ) { - break; - } - switch( bond_type ) { - case BOND_TYPE_SINGLE: - if ( at2[neigh].charge != -1 || pVA[neigh].nCMinusGroupEdge <= 0 ) { - num_wrong_neigh ++; - continue; - } - num_donors ++; - num_donors_O += (pVA[neigh].cNumValenceElectrons == 6 && pVA[neigh].cPeriodicRowNumber <= 4); - MobileChargeNeigh[numMobileChargeNeigh ++] = j; - break; - case BOND_TYPE_DOUBLE: - if ( at2[neigh].charge ) { - num_wrong_neigh ++; - continue; - } - DoubleBondAcceptors[numDoubleBondAcceptors ++] = j; - num_acceptors ++; - num_acceptors_O += (pVA[neigh].cNumValenceElectrons == 6 && pVA[neigh].cPeriodicRowNumber <= 4); - } - } - if ( j != at2[i].valence || !num_donors || !num_acceptors ) { - continue; - } - /* special case NOn(-) */ - if ( is_centerpoint_N && (num_donors == num_donors_O) && (num_acceptors == num_acceptors_O) ) { - continue; - } - if ( pStruct->iMobileH == TAUT_NON && num_donors == numDoubleBondNotONeigh ) { - /* fix all charges except on =O */ - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - int k, e, num_MovedCharges = 0; - - if ( !ChargeListAllExcept_DB_O.num_edges ) { - numOtherDoubleBondOAcceptors = 0; - for ( k = 0; k < num_at; k ++ ) { - if ( 1 == at2[k].valence && pBNS->edge[pBNS->vert[k].iedge[0]].flow && - !pBNS->edge[pBNS->vert[k].iedge[0]].forbidden && - !((e=pVA[k].nCMinusGroupEdge-1) >= 0 && pBNS->edge[e].flow) && - !((e=pVA[k].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].flow) && - /* 0 == at2[k].charge && */ - pVA[k].cNumValenceElectrons == 6 && !pVA[k].cMetal && - pStruct->endpoint && pStruct->endpoint[k] || - pStruct->fixed_H && pStruct->fixed_H[k] ) { - numOtherDoubleBondOAcceptors ++; /* do not fix this minus edge */ - } else - if ( (e=pVA[k].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].flow && - !pBNS->edge[e].forbidden && - ( ret = AddToEdgeList( &ChargeListAllExcept_DB_O, e, 64 )) ) { - goto exit_function; - } - if ( (e=pVA[k].nCPlusGroupEdge-1) >= 0 && - !pBNS->edge[e].forbidden && - ( ret = AddToEdgeList( &ChargeListAllExcept_DB_O, e, 64 )) ) { - goto exit_function; - } - } - } - /* fix double bonds to non-O neighbors connected by double bonds; - we will try to make these bons single */ - for ( k = 0; k < numDoubleBondNotONeigh; k ++ ) { - e = pBNS->vert[i].iedge[(int)DoubleBondNotONeigh[k]]; - if ( !pBNS->edge[e].forbidden && - (ret = AddToEdgeList( &ChargeListAllExcept_DB_O, e, 64 ))) { - goto exit_function; - } - } - /* attempt to make DoubleBondNotONeigh[] single */ - SetForbiddenEdgeMask( pBNS, &ChargeListAllExcept_DB_O, forbidden_edge_mask); - for ( k = 0; k < numDoubleBondNotONeigh && num_MovedCharges < numMobileChargeNeigh; k ++ ) { - pe = pBNS->edge + pBNS->vert[i].iedge[(int)DoubleBondNotONeigh[k]]; - delta = 1; - if ( pe->flow != delta ) - continue; - pv1m = pBNS->vert + (v1m = pe->neighbor1); - pv2m = pBNS->vert + (v2m = pe->neighbor12 ^ v1m); - pv1m->st_edge.flow -= delta; - pv2m->st_edge.flow -= delta; - pe->flow -= delta; - pBNS->tot_st_flow -= 2*delta; - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret == 1 && (vPathEnd == v1m && vPathStart == v2m || - vPathEnd == v2m && vPathStart == v1m) && - nDeltaCharge == 0 /* (-) moving from one to another atom*/ ) { - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else - if ( ret == 1 ) { - *pnTotalDelta += ret; - num_MovedCharges ++; - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - } else { - ret = 0; - pv1m->st_edge.flow += delta; - pv2m->st_edge.flow += delta; - pe->flow += delta; - pBNS->tot_st_flow += 2*delta; - } - } - RemoveForbiddenEdgeMask( pBNS, &ChargeListAllExcept_DB_O, forbidden_edge_mask); - } else - if ( !bPossiblyIgnore || !num_known_endpoints && !num_wrong_neigh && (num_acceptors_O + num_donors_O) >=3 ) { - /* remove negative charges from the neighbors */ - pBNS->vert[i].st_edge.cap += num_donors; /* enough to make all bonds to donors double */ - pBNS->tot_st_cap += num_donors; - pVA[i].cInitCharge -= num_donors; /* work no matter what are known charge/valence */ - for ( j = 0; j < numMobileChargeNeigh; j ++ ) { - neigh = at2[i].neighbor[ (int)MobileChargeNeigh[j] ]; - pEdgeMinus = pBNS->edge + (pVA[neigh].nCMinusGroupEdge-1); - v1m = pEdgeMinus->neighbor1; - v2m = pEdgeMinus->neighbor12 ^ v1m; - pv1m = pBNS->vert + v1m; - pv2m = pBNS->vert + v2m; - delta = pEdgeMinus->flow; - pv1m->st_edge.flow -= delta; - pv2m->st_edge.flow -= delta; - if ( IS_BNS_VT_C_GR( pv1m->type ) ) { - /* irreversible change to ChargeStruct */ - pv1m->st_edge.cap -= delta; - } else - if ( IS_BNS_VT_C_GR( pv2m->type ) ) { - /* irreversible change to ChargeStruct */ - pv2m->st_edge.cap -= delta; - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - pBNS->tot_st_cap -= delta; - pBNS->tot_st_flow -= 2*delta; - pEdgeMinus->flow -= delta; - } - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else - if ( ret == num_donors ) { - *pnTotalDelta += ret; - num_success ++; - /*pStruct->bExtract |= EXTRACT_STRUCT_NUMBER;*/ - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - } - } - } - if ( ret_forbid_edges ) { - /* remove the marks */ - RemoveForbiddenBondFlowBits( pBNS, forbidden_edge_test ); - } - ret = num_success; -exit_function: - AllocEdgeList( &ChargeListAllExcept_DB_O, EDGE_LIST_FREE ); - return ret; -} -/******************************************************************************************************/ -/* Find and eliminate cases when Mobile H endpoint has radical on it (typical for wrong P(VI)(=O)3OH */ -int MakeSingleBondsMetal2ChargedHeteroat(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ - int i; - - int ret2, ret, pass; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - - int j, k; - int cur_num_edges; - BNS_EDGE *e; - Vertex v1, v2; - - EdgeIndex *pFixedEdges; - int nNumEdgesToFix; - - ret = 0; - - /* to simplify, prepare new at[] from pBNS */ - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - - pFixedEdges = NULL; - - nNumEdgesToFix = 0; /* cpunt nNumEdgesToFix only when pass==0 */ - cur_num_edges = 0; /* count cur_num_edges only when pass==1; at the end they must be equal */ - for ( pass = 0; pass < 2; pass ++ ) { - if ( pass ) { - /* 2nd pass: allocate edge storage */ - if ( !nNumEdgesToFix ) { - break; /* nothing to do */ - } - pFixedEdges = (EdgeIndex *) inchi_malloc(nNumEdgesToFix * sizeof( pFixedEdges[0] ) ); - if ( !pFixedEdges ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - } - for ( i = 0; i < num_at; i ++ ) { - int neigh; - if ( pVA[i].cMetal ) { - for ( j = 0; j < at2[i].valence; j ++ ) { - neigh = at2[i].neighbor[j]; - if ( pVA[neigh].cNumValenceElectrons == 4 && - pVA[neigh].cPeriodicRowNumber == 1 ) { - continue; /* ignore carbon */ - } - if ( at2[i].bond_type[j] > BOND_TYPE_SINGLE && at2[neigh].charge && - !pVA[neigh].cMetal && pVA[neigh].cnListIndex > 0 ) { - int cnBits = at2[neigh].charge > 0? MAKE_CN_BITS(cn_bits_N, cn_bits_P, 0, 0) : - MAKE_CN_BITS(cn_bits_N, cn_bits_M, 0, 0); - int atBits = cnList[pVA[neigh].cnListIndex-1].bits; - for ( k = 0; k < MAX_NUM_CN_BITS-1; k ++, atBits >>= cn_bits_shift ) { /* ??? */ - if ( (atBits & cnBits) == cnBits ) { - break; - } - } - if ( k == MAX_NUM_CN_BITS-1 ) { - continue; - } - if ( pass == 0 ) { - nNumEdgesToFix ++; - } else { - pFixedEdges[ cur_num_edges ++ ] = pBNS->vert[i].iedge[j]; - } - } - } - } - } - } - - /* restore the initial structures */ - memcpy( at2, at, (num_at + num_deleted_H)*sizeof(at2[0])); - - if ( nNumEdgesToFix && pFixedEdges ) { - if ( nNumEdgesToFix != cur_num_edges ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - /* change edge flow, fix the edges, and run BNS */ - for ( i = 0; i < nNumEdgesToFix; i ++ ) { - e = pBNS->edge + pFixedEdges[i]; - v1 = e->neighbor1; - v2 = e->neighbor12 ^ v1; - e->flow --; - e->forbidden |= forbidden_edge_mask; - pBNS->vert[v1].st_edge.flow --; - pBNS->vert[v2].st_edge.flow --; - pBNS->tot_st_flow -= 2; - (*pnTotalDelta) -= 2; - } - /* Run BNS allowing to change any charges */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else { - (*pnTotalDelta) += ret; - } - /* unfix the edges */ - for ( i = 0; i < nNumEdgesToFix; i ++ ) { - e = pBNS->edge + pFixedEdges[i]; - e->forbidden &= inv_forbidden_edge_mask; - } - if ( ret < 2 * nNumEdgesToFix ) { - /* not all fixes succeeded */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - (*pnNumRunBNS) ++; - if ( ret < 0 ) { - goto exit_function; - } else { - (*pnTotalDelta) += ret; - } - } - } - if ( pFixedEdges ) { - inchi_free( pFixedEdges ); - pFixedEdges = NULL; - } - - -exit_function: - return ret; -} -/**************************************************************************/ -/* In Reconnected structure change 'salt bonds' to 'coordination bonds */ -/* for example, M-O-C= -> M(+)-O(-)-C= */ -/* Defect: instead of NH2-C=O(+)-M it will restore NH2(+)=C-O(-)-M(+) */ -/* However, in this release metal-organic compounds do not get much care */ -int SaltBondsToCoordBonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) -{ - int i; - - int ret2, ret, cur_success; - int num_at = pStruct->num_atoms; - int num_edges = pBNS->num_bonds + 2 * pBNS->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - int len_at = num_at + num_deleted_H; - int inv_forbidden_edge_mask = ~forbidden_edge_mask; - EDGE_LIST AllChargeEdges; - - int j, k, n; - BNS_EDGE *pe, *pePlusMetal, *peMinusO; - BNS_VERTEX *pv1, *pv2, *pvO, *pvM; - Vertex v1, v2, vPlusMinus; - - EdgeIndex ie, iePlusMetal, ieMinusO; - - Vertex vPathStart, vPathEnd; - int delta, nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - - ret = 0; - cur_success = 0; - AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); - - if ( pStruct->iInchiRec == INCHI_BAS || !pStruct->pSrm->bMetalAddFlower || pStruct->pSrm->nMetalMinBondOrder ) { - goto exit_function; - } - - /* to simplify, prepare new at[] from pBNS */ - memcpy( at2, at, len_at*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; - if ( ret2 < 0 ) { - ret = ret2; - goto exit_function; - } - for ( i = 0; i < num_at; i ++ ) { - if ( bIsMetalSalt( at2, i ) ) { - if ( !AllChargeEdges.num_edges ) { - /*--------- one-time action: fix all bonds, charges, taut. group edges ------------*/ - for ( j = 0; j < num_at; j ++ ) { - /* all bonds */ - for ( k = 0; k < at2[j].valence; k ++ ) { - n = at2[j].neighbor[k]; - if ( n < j && !pBNS->edge[ie = pBNS->vert[j].iedge[k]].forbidden && - ( ret = AddToEdgeList( &AllChargeEdges, ie, num_edges ) ) ) { - goto exit_function; - } - } - /* charge edges */ - if ( (ie=pVA[j].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[ie].forbidden && - (ret = AddToEdgeList( &AllChargeEdges, ie, num_edges ) ) ) { - goto exit_function; - } - if ( (ie=pVA[j].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[ie].forbidden && - ( ret = AddToEdgeList( &AllChargeEdges, ie, num_edges ) ) ) { - goto exit_function; - } - } - /* taut group edges */ - for ( j = 0; j < pTCGroups->num_tgroups; j ++ ) { - pv1 = pBNS->vert + (v1=pTCGroups->pTCG[j].nVertexNumber); /* t-group vertex */ - for ( k = 0; k < pv1->num_adj_edges; k ++ ) { - /* ie, pe - tautomeric atom edge; pv2 - endpoint vertex */ - /* Note: pe, pv2, v1 are not used here; they are to show how to traverse t-group */ - pv2 = pBNS->vert + (pe = pBNS->edge + (ie=pv1->iedge[k]))->neighbor1; - if ( ret = AddToEdgeList( &AllChargeEdges, ie, num_edges ) ) { - goto exit_function; - } - } - } - /*---------------------------------------------------------------*/ - } - /* replace all single bonds to neutral neighbors with zero-order bonds - allow neighbor charge change to (-1) and metal atom charge increment +1 */ - for ( k = 0; k < at2[i].valence; k ++ ) { - n = at2[i].neighbor[k]; - pe = pBNS->edge + pBNS->vert[i].iedge[k]; - if ( at2[n].charge || at2[i].bond_type[k] != BOND_TYPE_SINGLE ) { - continue; - } - iePlusMetal = pVA[i].nCPlusGroupEdge-1; - ieMinusO = pVA[n].nCMinusGroupEdge-1; - - if ( pe->flow != 1 || pe->forbidden || iePlusMetal < 0 ) { - continue; - } - pePlusMetal = pBNS->edge + iePlusMetal; - if ( pePlusMetal->flow <= 0 ) { - continue; /* to add (+) to metal this flow must be decremented */ - } - if ( ieMinusO >= 0 ) { - /* usually does not happen */ - peMinusO = pBNS->edge + ieMinusO; - - if ( peMinusO->flow || pePlusMetal->forbidden || peMinusO->forbidden ) { - continue; - } - - /* decrement bond order to 0 */ - delta = 1; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - pePlusMetal->forbidden &= inv_forbidden_edge_mask; - peMinusO->forbidden &= inv_forbidden_edge_mask; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) /*&& nDeltaCharge > 0*/ ) { - /* (+)charge was just moved, no change in number of charges */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - (*pnNumRunBNS) ++; - cur_success ++; /* 01 */ - } - } else { - pe->flow += delta; /* roll back */ - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } else - if ( NO_VERTEX != (vPlusMinus = GetPlusMinusVertex( pBNS, pTCGroups, 1, 1 ) ) ) { - /* manually add (-) charge to O and (+) charge to metal */ - /* decrement bond order to 0 */ - /*---------------------------------------------------------------------------*/ - /* */ - /* (+/-)* (+/-) Result: */ - /* | || */ - /* | || - Added (+) to M */ - /* (+)super (+)super - Incremented bond M-O */ - /* || | */ - /* || => | To make this attachment H, */ - /* (Y) (Y) increment */ - /* | || pTCGroups->pTCG[itg].tg_num_H */ - /* | || */ - /* (+)metal (+)hetero Technical details: */ - /* \\ \ increase capacities of */ - /* M M(+) edges to (+/-) otherwise */ - /* | || flow may not be able to */ - /* -O* -O-O increase */ - /* */ - /* After that change M=O bond order from 2 to 0 */ - /*---------------------------------------------------------------------------*/ - int i1, j1, k1; - delta = 1; - pvO = pBNS->vert + n; - pvM = pBNS->vert + i; - /* Increment st_edge.cap on (+/-) vertex */ - pBNS->vert[vPlusMinus].st_edge.cap += delta; - /* Increment st_edge.cap on O */ - pvO->st_edge.cap += delta; - /* increment cap on M-O edge */ - pe->cap += delta; - /* total cap count */ - pBNS->tot_st_cap += 2*delta; - - v1 = vPlusMinus; - v2 = n; /* atom O */ - - /* increase capacities of edges to Y */ - for ( i1 = 0; i1 < pBNS->vert[vPlusMinus].num_adj_edges; i1 ++ ) { - j1 = pBNS->edge[pBNS->vert[vPlusMinus].iedge[i1]].neighbor12 ^ vPlusMinus; - for ( k1 = 0; k1 < pBNS->vert[j1].num_adj_edges; k1 ++ ) { - pBNS->edge[pBNS->vert[j1].iedge[k1]].cap += delta; - } - } - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - pePlusMetal->forbidden &= inv_forbidden_edge_mask; - pe->forbidden &= inv_forbidden_edge_mask; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - cur_success = 0; - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) /*&& nDeltaCharge == 1*/ ) { - /* Added (+)charge to -N< => nDeltaCharge == 1 */ - /* Flow change on pe (-)charge edge (atom B-O(-)) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - (*pnNumRunBNS) ++; - cur_success ++; /* 01 */ - } - } - if ( cur_success ) { - /* set bond M=O order = 0 */ - if ( pe->flow != 2*delta ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - /* reduce pe bond order by 2*delta */ - pe->flow -= 2*delta; - pvO->st_edge.cap -= 2*delta; - pvO->st_edge.flow -= 2*delta; - pvM->st_edge.flow -= 2*delta; - pvM->st_edge.cap -= 2*delta; - pBNS->tot_st_cap -= 3*delta; - pBNS->tot_st_flow -= 4*delta; - /* fix M-O bond order to zero */ - pe->cap -= 2*delta; - /* add fixed (-) charge to O */ - pVA[n].cInitCharge -= delta; - } else { - /* failed */ - pBNS->vert[vPlusMinus].st_edge.cap -= delta; - pvO->st_edge.cap -= delta; - /*pTCGroups->pTCG[itg].edges_cap -= delta;*/ /* ???bug??? - commented out 2006-03-22 */ - pBNS->tot_st_cap -= 2*delta; - /* decrease capacities of edges to Y */ - for ( i1 = 0; i1 < pBNS->vert[vPlusMinus].num_adj_edges; i1 ++ ) { - j1 = pBNS->edge[pBNS->vert[vPlusMinus].iedge[i1]].neighbor12 ^ vPlusMinus; - for ( k1 = 0; k1 < pBNS->vert[j1].num_adj_edges; k1 ++ ) { - pBNS->edge[pBNS->vert[j1].iedge[k1]].cap -= delta; - } - } - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - } - } - } - -exit_function: - AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); - return ret; -} -#if ( KEEP_METAL_EDGE_FLOW == 1 ) -/******************************************************************************************************/ -int ForbidMetalCarbonEdges( BN_STRUCT *pBNS, inp_ATOM *at, int num_at, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, EDGE_LIST *pMetalCarbonEdges, int forbidden_edge_mask ) -{ - - int i, j, neigh, nNumEdgeMetalCarbon = 0, pass = 0, ret = 0; - BNS_VERTEX *pVert, *pNeigh; - BNS_EDGE *pEdge; - - /* count carbon-metal edges */ - - if ( pTCGroups->num_metal_atoms ) { -fill_ForbiddenEdgesMetalCarbon: - for ( i = 0; i < num_at; i ++ ) { - if ( pVA[i].cMetal && pVA[i].cNumBondsToMetal ) { - pVert = pBNS->vert + i; - for ( j = 0; j < pVert->num_adj_edges; j ++ ) { - pEdge = pBNS->edge + pVert->iedge[j]; - neigh = pEdge->neighbor12 ^ i; - pNeigh = pBNS->vert + neigh; - if ( !IS_BNS_VT_ATOM(pNeigh->type) ) - continue; - if ( at[neigh].endpoint ) - continue; - if ( pVA[neigh].cNumValenceElectrons == 4 && pVA[neigh].cPeriodicRowNumber == 1 && - pNeigh->st_edge.cap >= at[neigh].valence+1 ) { - if ( pass ) { - if ( ret = AddToEdgeList( pMetalCarbonEdges, pVert->iedge[j], 0 ) ) { - goto exit_function; - } - pEdge->forbidden |= forbidden_edge_mask; - } else { - nNumEdgeMetalCarbon ++; - } - } - - } - } - } - if ( !pass && nNumEdgeMetalCarbon ) { - if ( ret = AllocEdgeList( pMetalCarbonEdges, nNumEdgeMetalCarbon ) ) { - goto exit_function; - } - pass ++; - goto fill_ForbiddenEdgesMetalCarbon; - } - } -exit_function: - return ret; -} -#endif -/******************************************************************************************************/ -int RunBnsRestore1( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, - StrFromINChI *pStruct, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, INChI *pInChI[], - long num_inp, int bHasSomeFixedH ) -{ - int nNumRunBNS = 0; - - EDGE_LIST CarbonChargeEdges, MetalCarbonEdges, Nplus2BondsEdges; - - int nTotalDelta = 0, ret = 0, tot_num_fixes; - inp_ATOM *at = pStruct->at; - inp_ATOM *at2 = NULL; /* restored structure */ - inp_ATOM *at3 = NULL; /* structure for calculating one InChI */ - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; -#ifdef _DEBUG - int ret2; -#endif - -#if ( KEEP_METAL_EDGE_FLOW == 1 ) - BNS_VERTEX *pVert, *pNeigh; - int j, neigh; -#endif - - /* Edge lista initialization */ - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &MetalCarbonEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &Nplus2BondsEdges, EDGE_LIST_CLEAR ); - - if ( pStruct->iMobileH == TAUT_NON && - ( ret = FillOutExtraFixedHDataInChI( pStruct, pInChI ) ) ) { - goto exit_function; - } - - if ( !at2 && !(at2 = (inp_ATOM *) inchi_malloc((num_at + num_deleted_H)*sizeof(at2[0]))) || - !at3 && !(at3 = (inp_ATOM *) inchi_malloc((num_at + num_deleted_H)*sizeof(at3[0])))) { - return RI_ERR_ALLOC; - } - - if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, BNS_EDGE_FORBIDDEN_TEMP ))) { - goto exit_function; - } - -#if ( KEEP_METAL_EDGE_FLOW == 1 ) - /* count edges of -C(IV)< carbons connected to metals */ - if ( 0 > (ret = ForbidMetalCarbonEdges( pBNS, at, num_at, pVA, pTCGroups, &MetalCarbonEdges, BNS_EDGE_FORBIDDEN_TEMP ))) { - goto exit_function; - } -#endif - if ( 0 > (ret = ForbidNintrogenPlus2BondsInSmallRings( pBNS, at, num_at, pVA, 6, - pTCGroups, &Nplus2BondsEdges, BNS_EDGE_FORBIDDEN_TEMP ) ) ) { - goto exit_function; - } - - /*********** Run BNS #1: no charge on carbons and =N= ***************/ - if ( Nplus2BondsEdges.num_edges ) { - /* Run BNS leaving carbon charges unchanged */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - nNumRunBNS ++; - if ( ret < 0 ) { - goto exit_function; - } else { - nTotalDelta += ret; - } - RemoveForbiddenEdgeMask( pBNS, &Nplus2BondsEdges, BNS_EDGE_FORBIDDEN_TEMP ); - AllocEdgeList( &Nplus2BondsEdges, EDGE_LIST_FREE ); - } -#ifdef _DEBUG - /* debug only */ - memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; -#endif - /*************************** extend min ring size to 8 ****************************/ - if ( 0 > (ret = ForbidNintrogenPlus2BondsInSmallRings( pBNS, at, num_at, pVA, 8, - pTCGroups, &Nplus2BondsEdges, BNS_EDGE_FORBIDDEN_TEMP ) ) ) { - goto exit_function; - } - if ( Nplus2BondsEdges.num_edges ) { - /* Run BNS leaving carbon charges unchanged */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - nNumRunBNS ++; - if ( ret < 0 ) { - goto exit_function; - } else { - nTotalDelta += ret; - } - RemoveForbiddenEdgeMask( pBNS, &Nplus2BondsEdges, BNS_EDGE_FORBIDDEN_TEMP ); - AllocEdgeList( &Nplus2BondsEdges, EDGE_LIST_FREE ); - } -#ifdef _DEBUG - /* debug only */ - memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; -#endif - /*******************************************************************/ - if ( CarbonChargeEdges.num_edges > 0 ) { - /* Run BNS leaving carbon charges unchanged */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - nNumRunBNS ++; - if ( ret < 0 ) { - goto exit_function; - } else { - nTotalDelta += ret; - } - RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, BNS_EDGE_FORBIDDEN_TEMP ); - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); - } -#ifdef _DEBUG - /* debug only */ - memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; -#endif - /*******************************************************************/ - if ( MetalCarbonEdges.num_edges > 0 ) { - /* Run BNS leaving carbon charges unchanged */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - nNumRunBNS ++; - if ( ret < 0 ) { - goto exit_function; - } else { - nTotalDelta += ret; - } - RemoveForbiddenEdgeMask( pBNS, &MetalCarbonEdges, BNS_EDGE_FORBIDDEN_TEMP ); - AllocEdgeList( &MetalCarbonEdges, EDGE_LIST_FREE ); - } - /*******************************************************************/ - /* Run BNS allowing to change any charges */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - nNumRunBNS ++; - if ( ret < 0 ) { - goto exit_function; - } else { - nTotalDelta += ret; - } -#ifdef _DEBUG - /* debug only */ - memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; -#endif - -#if ( BNS_RAD_SEARCH == 1 ) - /******************************************************************/ - /* move unfulfilled 'radicals' from ChargeStruct to atoms */ - /* and set change charges of affected atoms to fit total charge */ - ret = MoveRadToAtomsAddCharges( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, BNS_EDGE_FORBIDDEN_TEMP ); - if ( ret < 0 ) { - goto exit_function; - } -#endif - /**************************************************************/ - /**************************************************************/ - /***** fix restore inconsistencies *****/ - /**************************************************************/ - /**************************************************************/ -#ifdef _DEBUG - /* debug only */ - memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; -#endif - - /* rearrange (+) and (-) edges flow so that there is no (+)flow=0 and (-)flow=1 */ - ret = RearrangePlusMinusEdgesFlow( pBNS, pBD, pVA, pTCGroups, BNS_EDGE_FORBIDDEN_TEMP ); - if ( ret < 0 ) { - goto exit_function; - } - - /*****************************************************************/ - /* Increment zero order metal bonds to heteroatoms */ - /*****************************************************************/ - ret = IncrementZeroOrderBondsToHeteroat( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - -#ifdef _DEBUG - /* debug only */ - memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); - pStruct->at = at2; - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at = at; -#endif - -#if (MOVE_CHARGES_FROM_HETEREO_TO_METAL == 1 ) - /*****************************************************************/ - /* move charges from heteroatoms to metal atoms */ - /*****************************************************************/ - ret = MoveChargeFromHeteroatomsToMetals( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } -#endif - /*********************************************************************** - NH2 NH2 - \ \ - C==S(+)- => C(+)-S- where NH2 are not tautomeric - / / - NH2 NH2 - ************************************************************************/ - ret = MovePlusFromS2DiaminoCarbon( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - /*****************************************************************/ - /* Avoid charge separation on heteroatoms */ - /*****************************************************************/ - ret = EliminateChargeSeparationOnHeteroatoms( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP, 0); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret ) { - /*charge separation remains; allow changes of stereobonds in a ring and try again */ - ret = EliminateChargeSeparationOnHeteroatoms( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP, - BNS_EDGE_FORBIDDEN_MASK); - if ( ret < 0 ) { - goto exit_function; - } - } - /*****************************************************************/ - /* convert N#N(+)-N= into N(-)=N(+)=N- */ - /*****************************************************************/ - ret = RestoreNNNgroup( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - /*****************************************************************/ - /* convert Metal(q)-N(-)-O(-) Metal(q-2)-N=O (local change) */ - /*****************************************************************/ - ret = FixMetal_Nminus_Ominus( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - /*****************************************************************/ - /* convert N(-)=C= into N#C- - */ - /*****************************************************************/ - ret = RestoreCyanoGroup( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - /*****************************************************************/ - /* convert C(+)#N(+)- into C(-)#N(+)- */ - /*****************************************************************/ - ret = RestoreIsoCyanoGroup( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - /*****************************************************************/ - /* eliminate =N(V)= if possible */ - /* | */ - /*****************************************************************/ - ret = EliminateNitrogen5Val3Bonds(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - - /*****************************************************************/ - /* | | */ - /* convert -S- to =S= if possible */ - /* | | */ - /*****************************************************************/ - ret = Convert_SIV_to_SVI(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - - /*****************************************************************/ - /* =N(+)=O =N-O(-) */ - /* convert => if possible */ - /* Metal(q) Metal(q+2) */ - /*****************************************************************/ - ret = PlusFromDB_N_DB_O_to_Metal(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - - /*****************************************************************/ - /* forbidden edges prevents required in InChI tautomerism */ - /* incorrectly restored mobile H mix separate tautomeric groups */ - /* because an edge may not become forbidden */ - /* note: removes this 'forbidden_edge' bit from ALL edges */ - /*****************************************************************/ - ret = MoveMobileHToAvoidFixedBonds( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - - if ( ret < 0 ) { - goto exit_function; - } - /**************************************************************************/ - /* 2. Mobile H endpoint has radical on it (typical for wrong P(VI)(=O)3OH */ - tot_num_fixes = 0; - if ( pStruct->iMobileH==TAUT_NON ) { - ret = RemoveRadFromMobileHEndpointFixH( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - } else { - ret = RemoveRadFromMobileHEndpoint( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - } - if ( ret < 0 ) { - goto exit_function; - } - tot_num_fixes += ret; - /**************************************************************/ - /* make bonds between a charged heteroatom and a metal single */ - ret = MakeSingleBondsMetal2ChargedHeteroat(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - /**************************************************************/ - /* move (+) charges to >N- and other centerpoints */ - ret = MoveChargeToMakeCenerpoints(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - - /**************************************************************************/ - /* Find and eliminate false Mobile-H groups: Cl(=O)3(-O(-)) => Cl(-)(=O)4 */ - ret = MoveChargeToRemoveCenerpoints(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - /**************************************************************************/ - /* Find A=X< where all bonds to X except A=X are marked as stereogenic */ - /* make bonds A=X single */ - ret = CheckAndRefixStereobonds(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - /**************************************************************************/ - /* In Reconnected structure change 'salt bonds' to 'coordination bonds */ - /* for example, M-O-C= -> M(+)-O(-)-C= */ - /* Defect: instead of NH2-C=O(+)-M it will restore NH2(+)=C-O(-)-M(+) */ - /* However, in this release metal-organic compounds do not get much care */ - ret = SaltBondsToCoordBonds(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); - if ( ret < 0 ) { - goto exit_function; - } - /**************************************************************************/ - /* Normalize the structure and compare t-groups and stereobonds */ - ret = NormalizeAndCompare(ip, sd, pBNS, pBD, pStruct, at, at2, at3, pVA, pTCGroups, pInChI, num_inp, bHasSomeFixedH, - &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP, BNS_EDGE_FORBIDDEN_MASK); - if ( ret < 0 ) { - goto exit_function; - } - /**************************************************************************/ - /* Create InChI out of the restored structure */ - - - /*ret = nTotalDelta;*/ - -exit_function: - pStruct->at = at; - pStruct->at2 = at2; - at2 = NULL; - AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &MetalCarbonEdges, EDGE_LIST_FREE ); - AllocEdgeList( &Nplus2BondsEdges, EDGE_LIST_FREE ); - if ( at2 ) { - inchi_free( at2 ); - } - if ( at3 ) { - inchi_free( at3 ); - } - - return ret; -} - -/******************************************************************************************************/ -int RestoreAtomMakeBNS( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, StrFromINChI *pStruct, int iComponent, - int iAtNoOffset, INChI *pInChI[], const char *szCurHdr, long num_inp, int bHasSomeFixedH ) -{ - int i, j, ret = 0, ret2; - /*int nDelta, nTotalDelta;*/ - VAL_AT *pVA = NULL; - VAL_AT va1; - int num_at = pStruct->num_atoms; - inp_ATOM *at = pStruct->at; - ALL_TC_GROUPS TCGroups; - ALL_TC_GROUPS *pTCGroups = &TCGroups; - int nAddEdges2eachAtom = 2, nAddVertices = 0; - - BFS_Q bfsq; - - /* BNS creation */ - BN_STRUCT *pBNS = NULL; - BN_DATA *pBD = NULL; - int nNum_changed_bonds = 0; - int bTreatMoreAtomsAsMetals = 0, bSecondPassNewMetals=0; - int nMaxAddAtoms = 2, nMaxAddEdges = 2, max_altp = BN_MAX_ALTP; - - memset( pTCGroups, 0, sizeof(pTCGroups[0]) ); - for ( i = 0; i < NUM_TCGROUP_TYPES; i ++ ) { - pTCGroups->nGroup[i] = TCG_None; /* unassigned */ - } - pTCGroups->iComponent = iComponent; - pTCGroups->iAtNoOffset = iAtNoOffset; - - if ( num_at == 1 ) { - /* single atom -- no bonds to restore */ - inp_ATOM *at2 = (inp_ATOM *) inchi_malloc(sizeof(at2[0])*(pStruct->num_atoms+pStruct->num_deleted_H)); - inp_ATOM *at3 = (inp_ATOM *) inchi_malloc(sizeof(at3[0])*(pStruct->num_atoms+pStruct->num_deleted_H)); - pStruct->at2 = at2; - at[0].charge = pInChI[0]->nTotalCharge; - if ( at2 ) { - memcpy( at2, at, sizeof(at2[0])*(pStruct->num_atoms+pStruct->num_deleted_H)); - } - if ( !at2 || !at3 ) { - if ( at3 ) inchi_free( at3 ); - return RI_ERR_ALLOC; - } - ret = MakeOneInChIOutOfStrFromINChI( ip, sd, pStruct, pStruct->at2, at3, pTCGroups ); - /* clean up */ - for( i = 0; i < TAUT_NUM; i ++ ) { - Free_INChI( &pStruct->pOneINChI[i] ); - Free_INChI_Aux( &pStruct->pOneINChI_Aux[i] ); - FreeInpAtomData( pStruct->pOne_norm_data[i] ); - if ( pStruct->pOne_norm_data[i] ) { - inchi_free( pStruct->pOne_norm_data[i] ); - pStruct->pOne_norm_data[i] = NULL; - } - } - free_t_group_info( &pStruct->One_ti ); - inchi_free( at3 ); - - return ret; - } - - AllocBfsQueue( &bfsq, BFS_Q_CLEAR, 0 ); - if ( !(pVA = (VAL_AT *) inchi_calloc( num_at, sizeof( pVA[0] ) ) ) ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - pStruct->pVA = pVA; - memset( &va1, 0, sizeof(va1) ); - pTCGroups->total_charge = pInChI[0]->nTotalCharge; - if ( 0 > ( ret = AllocBfsQueue( &bfsq, num_at, 0 /* min ring size undefined */ ) ) ) { - goto exit_function; - } - pStruct->pbfsq = &bfsq; - - if ( pStruct->iMobileH == TAUT_NON && pInChI[1] && pInChI[1]->nNumberOfAtoms > 1 && - ( ret = FillOutpStructEndpointFromInChI( pInChI[1], &pStruct->endpoint )) ) { - goto exit_function; - } - - /* mark metal atoms; find min ring sizes for atoms that have 2 bonds */ - for ( i = 0; i < num_at; i ++ ) { - pVA[i].cNumValenceElectrons = get_sp_element_type( at[i].el_number, &j ); - pVA[i].cPeriodicRowNumber = j; - pVA[i].cPeriodicNumber = at[i].el_number; - pVA[i].cNumValenceElectrons --; /* = -1 d- and f- metals, 0 for H, 1 for Na, 2 for Mg,.. = (ATYPE_Xx-1) */ - - if ( is_el_a_metal( at[i].el_number ) ) { - if ( pStruct->pSrm->bStereoRemovesMetalFlag ) { - /* treat metal as non-metal if it is stereogenic or has a stereobond */ - pVA[i].cMetal = !( at[i].p_parity || at[i].sb_parity[0] ); - } else { - pVA[i].cMetal = 1; - } - } - if ( at[i].valence == 2 && !at[i].num_H ) { - pVA[i].cMinRingSize = is_bond_in_Nmax_memb_ring( at, i, 0, bfsq.q, bfsq.nAtomLevel, - bfsq.cSource, 99 /* max ring size */ ); - } else { - pVA[i].cMinRingSize = 0; - } - } - /* AllocBfsQueue( &bfsq, BFS_Q_FREE, 0 ); */ - -repeat_for_new_metals: - /* set valences for the first time; find ChargeValence structures for each atom */ - for ( i = 0; i < num_at; i ++ ) { - /* get additional fictitious atoms information */ - pVA[i].cInitFreeValences = 0; - ret = GetAtomRestoreInfo( at, i, pVA, pStruct->pSrm, pStruct->bMobileH, pStruct->endpoint ); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret == TREAT_ATOM_AS_METAL && !bSecondPassNewMetals && !pVA[i].cMetal ) { - if ( pStruct->pSrm->bStereoRemovesMetalFlag ) { - /* treat metal as non-metal if it is stereogenic or has a stereobond */ - pVA[i].cMetal = !( at[i].p_parity || at[i].sb_parity[0] ); - } else { - pVA[i].cMetal = 1; - } - if ( pVA[i].cMetal ) { - bTreatMoreAtomsAsMetals ++; - } - } - pTCGroups->charge_on_atoms += pVA[i].cInitCharge; - } - if ( bTreatMoreAtomsAsMetals && !bSecondPassNewMetals ) { - for ( i = 0; i < num_at; i ++ ) { - /* clear all members of pVA[i] except two */ - pTCGroups->charge_on_atoms -= pVA[i].cInitCharge; - va1.cMetal = pVA[i].cMetal; - va1.cMinRingSize = pVA[i].cMinRingSize; - va1.cNumValenceElectrons = pVA[i].cNumValenceElectrons; - va1.cPeriodicRowNumber = pVA[i].cPeriodicRowNumber; - va1.cPeriodicNumber = pVA[i].cPeriodicNumber; - pVA[i] = va1; - } - bSecondPassNewMetals = 1; - goto repeat_for_new_metals; - } - - /* count atoms, bonds, additional edges and vertices in ChargeValence structures and t-groups */ - ret = nCountBnsSizes( at, num_at, nAddEdges2eachAtom, nAddVertices, &pStruct->ti, - pVA, pStruct->pSrm, pTCGroups ); - if ( ret < 0 ) { - goto exit_function; - } - - /* find and count groups; add counts of all other vertices to be created */ - ret = nAddSuperCGroups( pTCGroups ); - if ( ret < 0 ) { - goto exit_function; - } - - /* create the BNS and fill it with all real atoms */ - pBNS = AllocateAndInitTCGBnStruct( pStruct, pVA, pTCGroups, - nMaxAddAtoms, nMaxAddEdges, max_altp, &nNum_changed_bonds ); - if ( !pBNS ) { - ret = BNS_OUT_OF_RAM; - goto exit_function; - } - /* add t-groups to the BNS */ - ret = AddTGroups2TCGBnStruct( pBNS, pStruct, pVA, pTCGroups, nMaxAddEdges ); - if ( ret < 0 ) { - goto exit_function; - } - - /* add c-groups to the BNS; adjust charges */ - ret = AddCGroups2TCGBnStruct( pBNS, pStruct, pVA, pTCGroups, nMaxAddEdges ); - if ( ret < 0 ) { - goto exit_function; - } - - /* allocate BNData */ - pBD = AllocateAndInitBnData( pBNS->max_vertices + pBNS->max_vertices/2 ); - if ( !pBD ) { - ret = BNS_OUT_OF_RAM; - goto exit_function; - } - CheckBnsConsistency( pStruct, pBNS, pVA, pTCGroups, 0 ); - - /* restore bonds & charges */ - ret = RunBnsRestore1( ip, sd, pBNS, pBD, pStruct, pVA, pTCGroups, pInChI, num_inp, bHasSomeFixedH ); - if ( ret < 0 ) { - goto exit_function; - } - - ret = CheckBnsConsistency( pStruct, pBNS, pVA, pTCGroups, 1 ); -#if ( bRELEASE_VERSION == 0 ) -#ifndef TARGET_API_LIB - if ( ret ) { - fprintf( stdout, "Msg for: %ld %s comp=%d %c%c\n", num_inp, (szCurHdr && szCurHdr[0])? szCurHdr : "", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F' ); - } - if ( pStruct->iMobileH == TAUT_YES && pStruct->nNumRemovedProtons ) { - fprintf( stdout, "REMOVED_PROTONS%+d %ld %s\n", pStruct->nNumRemovedProtons, num_inp, (szCurHdr && szCurHdr[0])? szCurHdr : "" ); - /*pStruct->bExtract |= EXTRACT_STRUCT_NUMBER;*/ - } - if ( pStruct->bExtract & EXTRACT_STRUCT_NUMBER ) { - fprintf( stdout, "EXTRACT: %ld: %s\n", num_inp, (szCurHdr && szCurHdr[0])? szCurHdr : "" ); - } -#endif -#endif - { /* create the final structure in pStruct->at2 */ - inp_ATOM *at_tmp = pStruct->at; - pStruct->at = pStruct->at2; - memcpy( pStruct->at, at_tmp, sizeof(pStruct->at[0])*(pStruct->num_atoms + pStruct->num_deleted_H) ); - ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); - pStruct->at2 = pStruct->at; - pStruct->at = at_tmp; - if ( ret2 < 0 ) { - ret = ret2; - } - } - -exit_function: - - pStruct->pbfsq = NULL; - AllocBfsQueue( &bfsq, BFS_Q_FREE, 0 ); - - pBD = DeAllocateBnData( pBD ); - pBNS = DeAllocateBnStruct( pBNS ); - /* - if ( pVA ) inchi_free( pVA ); - */ - if ( pTCGroups->pTCG ) inchi_free( pTCGroups->pTCG ); - - return ret; -} -/******************************************************************************************************/ -int OneInChI2Atom( ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd, const char *szCurHdr, long num_inp, - StrFromINChI *pStruct, int iComponent, int iAtNoOffset, int bHasSomeFixedH, INChI *pInChI[]) -{ - int ret; - INPUT_PARMS *ip, ip_loc; - - ip_loc = *ip_inp; - ip = &ip_loc; - - sd->pStrErrStruct[0] = '\0'; - ret = RestoreAtomConnectionsSetStereo( pStruct, iComponent, iAtNoOffset, pInChI[0], pInChI[1]); - if ( ret < 0 ) { - goto exit_function; - } - ret = SetStereoBondTypesFrom0DStereo( pStruct, pInChI[0]); - if ( ret < 0 ) { - goto exit_function; - } - ret = ReconcileAllCmlBondParities( pStruct->at, pStruct->num_atoms, 0 ); - if ( ret < 0 ) { - goto exit_function; - } - /* main InChI restore function */ - ret = RestoreAtomMakeBNS( ip, sd, pStruct, iComponent, iAtNoOffset, pInChI, szCurHdr, num_inp, bHasSomeFixedH ); - -#ifndef COMPILE_ANSI_ONLY - if ( (pStruct->num_inp_actual>0? pStruct->num_inp_actual : num_inp) >= ip->first_struct_number && - ( (/*ret > 0 &&*/ ip->bDisplayIfRestoreWarnings ) && pStruct->pXYZ ) ) { - inchiTime ulTStart; - InchiTimeGet( &ulTStart ); - DisplayRestoredComponent( pStruct, iComponent, iAtNoOffset, pInChI[0], szCurHdr ); - sd->ulStructTime -= InchiTimeElapsed( &ulTStart ); /* subtract display time */ - } -#endif - if ( ret < 0 ) { - goto exit_function; - } - if ( (pStruct->num_inp_actual? pStruct->num_inp_actual: num_inp) >= ip->first_struct_number && ret >= 0 ) { - /* remove t-group markings and increment zero-order bonds, - otherwise MakeInChIOutOfStrFromINChI2() woild fail */ - /* --- moved to MakeInChIOutOfStrFromINChI2 --- - IncrZeroBondsAndClearEndpts(pStruct->at2, pStruct->num_atoms, iComponent+1); - CopySt2At( pStruct->at2, pStruct->st, pStruct->num_atoms ); - */ - /* include all restored structure features in pStruct->at2 */ - /* make full InChI out of pStruct->at2, pStruct->num_atoms */ - /***************************************************************************************/ - /* !!! pStruct->One_InChI etc. were removed at the exit from NormalizeAndCompare() !!! */ - /***************************************************************************************/ - if ( bHasSomeFixedH && pStruct->iInchiRec == INCHI_REC && pStruct->iMobileH == TAUT_YES && - !pStruct->bFixedHExists && !(ip->nMode & REQ_MODE_BASIC) ) { - /* reconnected components without Fixed-H layer may produce 'tautomeric' fragments like Cl(-) */ - ip->nMode |= REQ_MODE_BASIC; - } - ret = MakeInChIOutOfStrFromINChI2( ip, sd, pStruct, iComponent, iAtNoOffset, num_inp ); - if ( ret >= 0 ) { - ; - } -#if ( bRELEASE_VERSION == 0 ) -#ifndef TARGET_API_LIB - else { - fprintf( stdout, "\nERROR in MakeInChI-1: %ld %s Comp:%d %c%c Err:%d\n", num_inp, - szCurHdr? szCurHdr: "???", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', ret); - } -#endif -#endif - } - - -exit_function: - return ret; -} -/********************************************************************************************/ -int MakeProtonComponent( StrFromINChI *pStruct, int iComponent, int num_prot ) -{ - inp_ATOM *at = NULL; - int i; - - if ( num_prot <= 0 ) { - return 0; - } - /* allocate */ - pStruct->at = (inp_ATOM *) inchi_calloc( num_prot, sizeof(pStruct->at[0]) ); - pStruct->at2 = (inp_ATOM *) inchi_calloc( num_prot, sizeof(pStruct->at2[0]) ); - if ( !pStruct->at || !pStruct->at2 ) { - return 0; - } - /* create protons */ - at = pStruct->at; - /* fill out proton atom info */ - for ( i = 0; i < num_prot; i ++ ) { - strcpy( at[i].elname, "H" ); - at[i].el_number = EL_NUMBER_H; - at[i].orig_at_number = i+1; - /* - at[i].orig_compt_at_numb = i + 1; - at[i].component = i + 1; - */ - at[i].charge = 1; - } - memcpy( pStruct->at2, at, num_prot * sizeof(pStruct->at2[0]) ); - pStruct->bDeleted = 0; - pStruct->num_atoms = num_prot; - pStruct->bMobileH = TAUT_YES; - pStruct->iMobileH = TAUT_YES; - return num_prot; -} -/********************************************************************************************/ -int AddRemProtonsInRestrStruct( ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd, long num_inp, - int bHasSomeFixedH, - StrFromINChI *pStruct, int num_components, - StrFromINChI *pStructR, int num_componentsR, - NUM_H *nProtonsToBeRemovedByNormFromRevrs, int *recmet_change_balance ) -{ /* on entry and exit, all at[i].num_H do not include isotopic H and explicit terminal H are connected */ - int iComp, q, ret = 0; - int num_atoms, tot_num_at, num_deleted_H, num_tg, num_changed, num_deleted_components; - inp_ATOM *at; - INPUT_PARMS *ip, ip_loc; - int num_prot = *nProtonsToBeRemovedByNormFromRevrs; - int delta_recmet_prot, num_prot_prev, bAccumulateChanges=0, nNumProtAddedByRevrs; - INChI_Aux *pINChI_Aux; - INCHI_MODE bNormalizationFlags; - int nChargeRevrs, nChargeInChI; - - if ( !num_prot ) { - return 0; - } - delta_recmet_prot = 0; - num_changed = 0; - num_deleted_components = 0; - ip_loc = *ip_inp; - ip = &ip_loc; - /*---------------------------------------------------------------------------------- - nLink < 0 && num_componentsR > 0 => This is a Disconnected structure component; it is - same as already processed reconnected one - Do no preicess it - - nLink > 0 && num_componentsR > 0 => This is a Disconnected structure component; - (should not happen) It it is a result of (nLink-1)th Reconeected - component disconnection (NOT IMPLEMENTED YET) - - nLink = 0 => Process this component. It is either a reconnected - component, or a result of a disconnection (for now) - - nLink > 0 && num_componentsR = 0 => This is a Reconnected component that is same as - a disconnected one that will not be processed. - Process and save charge delta. - -----------------------------------------------------------------------------------*/ - - for ( iComp = 0; iComp < num_components && num_prot; iComp ++ ) { - bAccumulateChanges = 0; - if ( pStruct[iComp].nLink < 0 && num_componentsR > 0 ) { - /* check */ - q = -(pStruct[iComp].nLink+1); - if ( !pStructR || !num_componentsR || q >= num_componentsR || pStructR[q].nLink != (iComp+1) ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - continue; /* Disconnected structure component has already been processed as a Reconnected one */ - } - - at = pStruct[iComp].at2; - num_atoms = pStruct[iComp].num_atoms; - tot_num_at = pStruct[iComp].num_atoms+(num_deleted_H=pStruct[iComp].num_deleted_H); - bAccumulateChanges = ( pStruct[iComp].nLink > 0 && !num_componentsR ); - nChargeRevrs = pStruct[iComp].nChargeRevrs; - nChargeInChI = pStruct[iComp].nChargeInChI; - num_deleted_components += (0 != pStruct[iComp].bDeleted); - if ( !at || !num_atoms ) { - continue; - } - /* find whether it is a reconnected structure */ - q = bRevInchiComponentExists( pStruct+iComp, INCHI_REC, TAUT_YES, 0 )? INCHI_REC : INCHI_BAS; - /* - q = pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC] && - pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC][0][TAUT_YES] && - pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC][0][TAUT_YES]->nNumberOfAtoms? INCHI_REC : INCHI_BAS; - */ - pINChI_Aux = pStruct[iComp].RevInChI.pINChI_Aux[q][0][TAUT_YES]; /* 0 = 1st component in RevInChI */ - /*nNumProtAddedByRevrs = pINChI_Aux->nNumRemovedProtons;*/ - nNumProtAddedByRevrs = -pStruct[iComp].nNumRemovedProtonsByRevrs; - bNormalizationFlags = pINChI_Aux->bNormalizationFlags; - num_tg = pINChI_Aux->nNumberOfTGroups; - - - /* disconnect all explicit H and add the number of implicit iso H and all explicit terminal H to the number of implicit H */ - if ( 0 > ( ret = DisconnectedConnectedH( at, num_atoms, num_deleted_H ) ) ) { - goto exit_function; - } - num_prot_prev = num_prot; - ret = AddRemoveProtonsRestr( at, num_atoms, &num_prot, nNumProtAddedByRevrs, - bNormalizationFlags, num_tg, nChargeRevrs, nChargeInChI ); - - pStruct[iComp].bPostProcessed = ret; - num_changed += (ret > 0); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret > 0 ) { - /* recalculate InChI; it will reconnect at */ - StrFromINChI *pStruct1 = pStruct + iComp; - INCHI_MODE nMode = ip->nMode; - FreeAllINChIArrays( pStruct1->RevInChI.pINChI, - pStruct1->RevInChI.pINChI_Aux, - pStruct1->RevInChI.num_components ); - - if ( bHasSomeFixedH && pStruct1->iInchiRec == INCHI_REC && pStruct1->iMobileH == TAUT_YES && - !pStruct1->bFixedHExists && !(ip->nMode & REQ_MODE_BASIC) ) { - /* reconnected components without Fixed-H layer may produce 'tautomeric' fragments like Cl(-) */ - ip->nMode |= REQ_MODE_BASIC; - } - /* calls ConnectDisconnectedH(...): subtracts number of implicit iso H from implicit H */ - ret = MakeInChIOutOfStrFromINChI2( ip, sd, pStruct1, 0, 0, num_inp ); - ip->nMode = nMode; - if ( ret < 0 ) { - goto exit_function; - } - } else { - /* reconnect disconnected terminal H and subtracts number of implicit iso H from implicit H */ - if ( 0 > ( ret = ConnectDisconnectedH( at, num_atoms, num_deleted_H ) ) ) { - goto exit_function; - } - } - if ( bAccumulateChanges && recmet_change_balance ) { - /* processed Reconnected layer component that is also present in Disconnected layer */ - delta_recmet_prot += num_prot - num_prot_prev; - } - } - - iComp = num_components-1; - if ( !bHasSomeFixedH && num_prot > 0 && 1 == num_deleted_components && iComp >= 0 && pStruct[iComp].bDeleted ) { - /* add bare protons to the deleted Mobile-H component; undelete the component */ - num_prot_prev = num_prot; - if ( !MakeProtonComponent( pStruct+iComp, iComp, num_prot ) ) { - goto exit_function; - } else { - /* recalculate InChI; it will reconnect at */ - StrFromINChI *pStruct1 = pStruct + iComp; - INCHI_MODE nMode = ip->nMode; - num_changed ++; - num_prot = 0; - FreeAllINChIArrays( pStruct1->RevInChI.pINChI, - pStruct1->RevInChI.pINChI_Aux, - pStruct1->RevInChI.num_components ); - - if ( bHasSomeFixedH && pStruct1->iInchiRec == INCHI_REC && pStruct1->iMobileH == TAUT_YES && - !pStruct1->bFixedHExists && !(ip->nMode & REQ_MODE_BASIC) ) { - /* reconnected components without Fixed-H layer may produce 'tautomeric' fragments like Cl(-) */ - ip->nMode |= REQ_MODE_BASIC; - } - /* Although MakeInChIOutOfStrFromINChI2() calls ConnectDisconnectedH(...) */ - /* to subtracts number of implicit iso H from implicit H */ - /* this CANNOT have any effect on the deleted H component */ - ret = MakeInChIOutOfStrFromINChI2( ip, sd, pStruct1, 0, 0, num_inp ); - ip->nMode = nMode; - if ( ret < 0 ) { - goto exit_function; - } - if ( bAccumulateChanges && recmet_change_balance ) { - /* processed Reconnected layer component that is also present in Disconnected layer */ - delta_recmet_prot += num_prot - num_prot_prev; - } - } - } - *nProtonsToBeRemovedByNormFromRevrs = num_prot; - if ( recmet_change_balance ) { - *recmet_change_balance = delta_recmet_prot; - } - -exit_function: - return ret < 0? ret : num_changed; -} -/**********************************************************************************/ -int AddRemIsoProtonsInRestrStruct( ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd, long num_inp, int bHasSomeFixedH, - StrFromINChI *pStruct, int num_components, - StrFromINChI *pStructR, int num_componentsR, - NUM_H pProtonBalance[], NUM_H recmet_change_balance[] ) -{ /* on entry and exit, all at[i].num_H do not include isotopic H and explicit terminal H are connected */ - int iComp, q, k, ret = 0, bNotEmpty; - int num_atoms, tot_num_at, num_deleted_H, num_tg, num_changed; - inp_ATOM *at; - NUM_H num_prot[NUM_H_ISOTOPES], delta_recmet_prot[NUM_H_ISOTOPES], num_prot_prev[NUM_H_ISOTOPES]; - int bAccumulateChanges; - INChI_Aux *pINChI_Aux; - INChI *pINChI; - INCHI_MODE bNormalizationFlags; - INPUT_PARMS *ip, ip_loc; - - ip_loc = *ip_inp; - ip = &ip_loc; - - memcpy( num_prot, pProtonBalance, sizeof(num_prot) ); - for ( bNotEmpty=0, k = 0; k < NUM_H_ISOTOPES; k ++ ) { - bNotEmpty |= num_prot[k]; - } - if ( !bNotEmpty ) { - return 0; - } - memset ( delta_recmet_prot, 0, sizeof(delta_recmet_prot)); - num_changed = 0; - /*---------------------------------------------------------------------------------- - nLink < 0 && num_componentsR > 0 => This is a Disconnected structure component; it is - same as already processed reconnected one - Do no preicess it - - nLink > 0 && num_componentsR > 0 => This is a Disconnected structure component; - (should not happen) It it is a result of (nLink-1)th Reconeected - component disconnection (NOT IMPLEMENTED YET) - - nLink = 0 => Process this component. It is either a reconnected - component, or a result of a disconnection (for now) - - nLink > 0 && num_componentsR = 0 => This is a Reconnected component that is same as - a disconnected one that will not be processed. - Process and save charge delta. - -----------------------------------------------------------------------------------*/ - - for ( iComp = 0; iComp < num_components && num_prot; iComp ++ ) { - bAccumulateChanges = 0; - if ( pStruct[iComp].nLink < 0 && num_componentsR > 0 ) { - /* check */ - q = -(pStruct[iComp].nLink+1); - if ( !pStructR || !num_componentsR || q >= num_componentsR || pStructR[q].nLink != (iComp+1) ) { - ret = RI_ERR_PROGR; - goto exit_function; - } - continue; /* Disconnected structure component has already been processed as a Reconnected one */ - } - - at = pStruct[iComp].at2; - num_atoms = pStruct[iComp].num_atoms; - tot_num_at = pStruct[iComp].num_atoms+(num_deleted_H=pStruct[iComp].num_deleted_H); - bAccumulateChanges = ( pStruct[iComp].nLink > 0 && !num_componentsR ); - - if ( !at || !num_atoms ) { - continue; - } - /* find whether it is a reconnected structure */ - q = pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC] && - pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC][0][TAUT_YES] && - pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC][0][TAUT_YES]->nNumberOfAtoms? INCHI_REC : INCHI_BAS; - - pINChI_Aux = pStruct[iComp].RevInChI.pINChI_Aux[q][0][TAUT_YES]; /* 0 = 1st component in RevInChI */ - pINChI = pStruct[iComp].RevInChI.pINChI[q][0][TAUT_YES]; /* 0 = 1st component in RevInChI */ - bNormalizationFlags = pINChI_Aux->bNormalizationFlags; - num_tg = pINChI_Aux->nNumberOfTGroups; - memcpy( num_prot_prev, num_prot, sizeof(num_prot_prev) ); - - /* pass CONNECTED explicit H to AddRemoveIsoProtonsRestr() for isotopic H addition */ - ret = AddRemoveIsoProtonsRestr( at, num_atoms, num_prot, num_tg ); - - pStruct[iComp].bPostProcessed |= ret; - num_changed += (ret > 0); - if ( ret < 0 ) { - goto exit_function; - } - if ( ret > 0 ) { - StrFromINChI *pStruct1 = pStruct+iComp; - INCHI_MODE nMode = ip->nMode; - /* recalculate InChI; MakeInChIOutOfStrFromINChI2() will reconnect explicit H */ - /* disconnect all explicit H and add the number of implicit iso H and all explicit terminal H to the number of implicit H */ - if ( 0 > ( ret = DisconnectedConnectedH( at, num_atoms, num_deleted_H ) ) ) { - goto exit_function; - } - FreeAllINChIArrays( pStruct1->RevInChI.pINChI, - pStruct1->RevInChI.pINChI_Aux, - pStruct1->RevInChI.num_components ); - if ( bHasSomeFixedH && pStruct1->iInchiRec == INCHI_REC && pStruct1->iMobileH == TAUT_YES && - !pStruct1->bFixedHExists && !(ip->nMode & REQ_MODE_BASIC) ) { - /* reconnected components without Fixed-H layer may produce 'tautomeric' fragments like Cl(-) */ - ip->nMode |= REQ_MODE_BASIC; - } - /* input: disconnected explicit H, output: connected explicit H */ - ret = MakeInChIOutOfStrFromINChI2( ip, sd, pStruct1, 0, 0, num_inp ); - ip->nMode = nMode; - if ( ret < 0 ) { - goto exit_function; - } - } - /* the following was commented out 2007-08-28 by DT. Reason: it's a bug since H must be already connected */ - /* else { - if ( 0 > ( ret = ConnectDisconnectedH( at, num_atoms, num_deleted_H ) ) ) { - goto exit_function; - } - } */ - if ( bAccumulateChanges ) { - /* processed Reconnected layer component that is also present in Disconnected layer */ - for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) { - delta_recmet_prot[k] += num_prot[k] - num_prot_prev[k]; - } - } - } - - memcpy ( pProtonBalance, num_prot, sizeof(num_prot) ); - if ( recmet_change_balance ) { - memcpy ( recmet_change_balance, delta_recmet_prot, sizeof(delta_recmet_prot) ); - } -exit_function: - return ret < 0? ret : num_changed; -} - -#endif +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include + +/*#define CHECK_WIN32_VC_HEAP*/ + +#include "mode.h" + +#if ( READ_INCHI_STRING == 1 ) +#include "ichitime.h" +#include "ichirvrs.h" +#include "ichicant.h" + +/********************** Forbid carbon charge edges ***********************************/ +int ForbidCarbonChargeEdges( BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups, EDGE_LIST *pCarbonChargeEdges, int forbidden_edge_mask ) +{ +#define MAX_NUM_CARBON_CHARGE_EDGES 2 + int nType, i, k, ret; + BNS_EDGE *pEdge; + if ( ret = AllocEdgeList( pCarbonChargeEdges, MAX_NUM_CARBON_CHARGE_EDGES ) ) { + goto exit_function; + } + pCarbonChargeEdges->num_edges = 0; + for ( i = 0; i < MAX_NUM_CARBON_CHARGE_EDGES; i ++ ) { + switch( i ) { + case 0: + nType = TCG_Plus_C0; + break; + case 1: + nType = TCG_Minus_C0; + break; + default: + ret = RI_ERR_PROGR; + goto exit_function; + } + if ( (k = pTCGroups->nGroup[nType]) >= 0 ) { + k = pTCGroups->pTCG[k].nForwardEdge; + if ( k > 0 ) { + pEdge = pBNS->edge + k; + if ( !(pEdge->forbidden & forbidden_edge_mask) ) { + pEdge->forbidden |= forbidden_edge_mask; + if ( ret = AddToEdgeList( pCarbonChargeEdges, k, 0 ) ) { + goto exit_function; + } + } + } else { + ret = RI_ERR_PROGR; + goto exit_function; + } + } + } + ret = pCarbonChargeEdges->num_edges; +exit_function: + return ret; +#undef MAX_NUM_CARBON_CHARGE_EDGES +} + +/****************************************************************************/ +int ForbidNintrogenPlus2BondsInSmallRings( BN_STRUCT *pBNS, inp_ATOM *at, int num_at, + VAL_AT *pVA, int min_ring_size, ALL_TC_GROUPS *pTCGroups, + EDGE_LIST *pNplus2BondsEdges, int forbidden_edge_mask ) +{ + int i, j, ret; + BNS_EDGE *e; + + ret = 0; + /* --- forbid edges that allow to make =N(+)= or #N(+)- in small ring */ + for ( i = 0; i < num_at; i ++ ) { + if ( at[i].valence == 2 && + !at[i].num_H && !at[i].endpoint && + pVA[i].cNumValenceElectrons == 5 && + pVA[i].cPeriodicRowNumber == 1 && + !pVA[i].cMaxFlowToMetal && pVA[i].nCPlusGroupEdge > 0 && + pVA[i].cnListIndex > 0 && cnList[pVA[i].cnListIndex-1].bits == cn_bits_MNP && + pVA[i].cMinRingSize && pVA[i].cMinRingSize <= min_ring_size ) { + + e = pBNS->edge + (j = pVA[i].nCPlusGroupEdge - 1); + if ( !(e->forbidden & forbidden_edge_mask) ) { + e->forbidden |= forbidden_edge_mask; + if ( ret = AddToEdgeList( pNplus2BondsEdges, j, 128 ) ) { + goto exit_function; + } + } + } + } + ret = 0; +exit_function: + return ret; +} + +/************************************************************************************************* +Problem: Formula in InChI from the reversed structure has less H than in the input InChI +Solutions: + +(a) | | + -B(-)-NH-=..-=N(+)< => -B(-)-NH(+)=-..=-N< (H is not removed from the ion pair) + | | + + | | +(b) >N(+)=-=...-=N-NH => >N-=-...=-N(+)-NH (charge from onium cannot be moved to remove H+) + | | +*************************************************************************************************/ +int FixLessHydrogenInFormula( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, + inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ) +{ + int iBPlus=NO_VERTEX, iNV=NO_VERTEX, iNH = NO_VERTEX, neigh; + EDGE_LIST NewlyFixedEdges; + int ret, i, j; + int num_at = pStruct->num_atoms; + int inv_forbidden_edge_mask = ~forbidden_edge_mask; + /* for RunBnsTestOnce */ + Vertex vPathStart, vPathEnd; + int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; + + AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR ); + if ( ret = AllocEdgeList( &NewlyFixedEdges, 2*num_at ) ) { + goto exit_function; + } + for ( i = 0; i < num_at; i ++ ) { + if ( (j = pVA[i].nCMinusGroupEdge-1) >= 0 ) { + if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) { + goto exit_function; + } + pBNS->edge[j].forbidden |= forbidden_edge_mask; + } + if ( (j = pVA[i].nCPlusGroupEdge-1) >= 0 ) { + if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) { + goto exit_function; + } + pBNS->edge[j].forbidden |= forbidden_edge_mask; + } + } + /* extra H has been removed; check non-tautomeric atoms */ + for ( i = 0; i < num_at; i ++ ) { + if ( !at2[i].endpoint && !pVA[i].cMetal && + pVA[i].cNumValenceElectrons == 5 && pVA[i].cPeriodicRowNumber == 1 && + at2[i].num_H == atf[i].num_H + 1) { + /* H was removed from N */ + iNH = i; + break; + } + } + if ( 0 <= iNH && iNH < num_at ) { + /* check neighbors for | | + (a) -B(+)- or (b) =N- + | | + */ + for ( j = 0; j < at2[i].valence; j ++ ) { + neigh = at2[iNH].neighbor[j]; + if ( at2[neigh].valence == 4 ) { + if ( at2[neigh].charge == -1 && at2[neigh].chem_bonds_valence == 4 && + !at2[neigh].radical && !at[neigh].num_H ) { + iBPlus = neigh; + } + } + } + } + if ( 0 <= iNH && iNH < num_at ) { + int bond_type_at2; + int bond_type_atf; + int num_bonds_in_path = 0; + int delta = -1, nxt = iNH, prv = NO_VERTEX, nxt_is_NPlus; + /* the changed bond to the dehydrogenated atom H should have greater order */ + /* delta = (new bond order in atf[]) - (restored bond order in at2[]) */ + nxt_is_NPlus = 0; + do { + i = nxt; + nxt = NO_VERTEX; + delta = -delta; + for ( j = 0; j < at2[i].valence; j ++ ) { + bond_type_at2 = at2[i].bond_type[j] & BOND_TYPE_MASK; /* restored bond */ + bond_type_atf = atf[i].bond_type[j] & BOND_TYPE_MASK; /* normalized bond */ + nxt_is_NPlus = 0; + if ( (bond_type_atf - bond_type_at2 == delta || bond_type_atf == BOND_ALT12NS) && + BOND_TYPE_SINGLE <= bond_type_at2 + delta && bond_type_at2 + delta <= BOND_TYPE_TRIPLE && + !at2[(int)at2[i].neighbor[j]].cFlags ) { + prv = i; + nxt = at2[i].neighbor[j]; + nxt_is_NPlus = at2[nxt].charge == 1 && atf[nxt].charge == 0 && + pVA[nxt].cNumValenceElectrons == 5 && pVA[nxt].cPeriodicRowNumber == 1; + at2[i].cFlags |= 1; /* avoid cycling */ + num_bonds_in_path ++; + if ( delta == -1 && at2[prv].valence == 4 && at2[prv].chem_bonds_valence == 5 && + !at2[prv].charge && !at2[prv].radical && pVA[prv].cNumValenceElectrons == 5 && + pVA[prv].nCPlusGroupEdge > 0 ) { + iNV = prv; + } + if ( at2[nxt].charge != atf[nxt].charge ) { + if ( (at2[nxt].charge == 1 || atf[nxt].charge == 1) && + pVA[nxt].nCPlusGroupEdge > 0 ) { + pBNS->edge[pVA[nxt].nCPlusGroupEdge-1].forbidden &= inv_forbidden_edge_mask; + } + if ( (at2[nxt].charge == -1 || atf[nxt].charge == -1) && + pVA[nxt].nCMinusGroupEdge > 0 ) { + pBNS->edge[pVA[nxt].nCMinusGroupEdge-1].forbidden &= inv_forbidden_edge_mask; + } + } + break; /* found */ + } + } + } while ( nxt >= 0 && !( nxt_is_NPlus && delta == -1 ) ); + for ( i = 0; i < num_at; i ++ ) { + at2[i].cFlags = 0; + } + if ( nxt >= 0 && nxt_is_NPlus && delta == -1 ) { + /* a simple alt path from NH-= to =N(+) has been found */ + if ( iBPlus || iNV ) { + /* move (+) charge from N(+) to iNV or, if iBPlus, then to iNH */ + if ( iNV >= 0 && (j = pVA[iNV].nCPlusGroupEdge-1) > 0 && pBNS->edge[j].flow > 0 || + iNH >= 0 && (j = pVA[iNH].nCPlusGroupEdge-1) > 0 && pBNS->edge[j].flow > 0 ) { + int ieFlower; + BNS_EDGE *pe = pBNS->edge + j, *peFlower = NULL; + Vertex v1 = pe->neighbor1; + Vertex v2 = v1 ^ pe->neighbor12; + BNS_VERTEX *pv1 = pBNS->vert + v1; + BNS_VERTEX *pv2 = pBNS->vert + v2; + + delta = 1; + /* prevent conversion of >N(+)= into N(V) neutral */ + ieFlower = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[nxt].nCPlusGroupEdge-1 ); + if ( ieFlower >= 0 ) { + peFlower = pBNS->edge + ieFlower; + if ( peFlower->flow == delta ) { + peFlower->forbidden |= forbidden_edge_mask; + if ( ret = AddToEdgeList( &NewlyFixedEdges, ieFlower, 0 )) { + goto exit_function; + } + } + } + pe->forbidden |= forbidden_edge_mask; + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + if ( ret < 0 ) { + goto exit_function; + } + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && + nDeltaCharge <= 0 /* charge moving to this atom disappers*/ ) { + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + (*pnNumRunBNS) ++; + if ( ret < 0 ) { + goto exit_function; + } else + if ( ret == 1 ) { + *pnTotalDelta += ret; + } else { + ret = RI_ERR_PROGR; + goto exit_function; + } + } else { + ret = 0; + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + } + } + } + } +exit_function: + /* remove bond fixation */ + RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask ); + AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE ); + return ret; +} + +/*********************************************************************************************** + + + X=Y-O(-) => X(-)-Y=O + + +************************************************************************************************/ +int FixMoreHydrogenInFormula( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, + inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ) +{ + int iNH = NO_VERTEX, neigh, neigh2; + EDGE_LIST NewlyFixedEdges; + int ret, i, j, k, k2=0, delta; + int num_at = pStruct->num_atoms; + int inv_forbidden_edge_mask = ~forbidden_edge_mask; + Vertex v1, v2; + /* for RunBnsTestOnce */ + Vertex vPathStart, vPathEnd; + int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; + BNS_EDGE *pe, *pe2; + + AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR ); + if ( ret = AllocEdgeList( &NewlyFixedEdges, 2*num_at ) ) { + goto exit_function; + } + /* fix all charges */ + for ( i = 0; i < num_at; i ++ ) { + if ( (j = pVA[i].nCMinusGroupEdge-1) >= 0 ) { + if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) { + goto exit_function; + } + pBNS->edge[j].forbidden |= forbidden_edge_mask; + } + if ( (j = pVA[i].nCPlusGroupEdge-1) >= 0 ) { + if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) { + goto exit_function; + } + pBNS->edge[j].forbidden |= forbidden_edge_mask; + } + } + + /* H(+) has been added to -O(-); check non-tautomeric atoms */ + for ( i = 0; i < num_at; i ++ ) { + if ( !(pStruct->bMobileH? at2[i].endpoint : pStruct->endpoint[i]) && !pVA[i].cMetal && + at2[i].num_H + 1 == atf[i].num_H && /* normalization added H ??? What would happen in Fixed-H case?*/ + (k = pVA[i].nCMinusGroupEdge-1) >= 0 && + pBNS->edge[k].flow == 1 && /* atom had (-) charge before preprocessing */ + at2[i].charge == -1 && atf[i].charge == 0 && /* and has no charge after preprocessing */ + at2[i].valence == 1 && at2[i].chem_bonds_valence == 1 && /* connected by a single bond */ + pVA[i].cNumValenceElectrons == 6 && /* atom is O, S, Se, Te */ + at2[neigh=at2[i].neighbor[0]].chem_bonds_valence > at2[neigh].valence + /* atom's single neighbor has multiple bond(s)*/ + ) { + /* H(+) was added to O in Y=X-O(-), where X is the only neighbor of O, X=neigh, Y=neigh2 */ + iNH = i; + for ( j = 0; j < at2[neigh].valence; j ++ ) { + neigh2 = at2[neigh].neighbor[j]; + if ( neigh2 != iNH && !at2[neigh2].endpoint && + !pBNS->edge[(int)pBNS->vert[neigh].iedge[j]].forbidden && + 4 <= pVA[neigh2].cNumValenceElectrons && + pVA[neigh2].cNumValenceElectrons <= 5 && /* neig2 is C or N */ + (k2 = pVA[neigh2].nCMinusGroupEdge-1) >= 0 && + !pBNS->edge[k2].flow /* negative charge may be moved to neigh2 */ ) { + break; + } + } + if ( j < at2[neigh].valence ) { + delta = 1; + pe = pBNS->edge + k; /* -O(-) negative charge edge; flow = 1 */ + pe2 = pBNS->edge + k2; /* X charge edge; flow = 0 */ + v1 = pe->neighbor1; + v2 = pe->neighbor12 ^ v1; + pe->flow -= delta; + pBNS->vert[v1].st_edge.flow -= delta; + pBNS->vert[v2].st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + pe2->forbidden &= inv_forbidden_edge_mask; /* allow the charge to move */ + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + if ( ret < 0 ) { + goto exit_function; + } + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && + nDeltaCharge <= 1 ) { + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + (*pnNumRunBNS) ++; + if ( ret < 0 ) { + goto exit_function; + } else + if ( ret ) { + *pnTotalDelta += ret; + } else { + ret = RI_ERR_PROGR; + } + break; + } else { + /* the attempt has failed; restore the flow */ + ret = 0; + pe->flow += delta; + pBNS->vert[v1].st_edge.flow += delta; + pBNS->vert[v2].st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + } + } + } +exit_function: + /* remove bond fixation */ + RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask ); + AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE ); + return ret; +} +#if ( FIX_ADD_PROTON_FOR_ADP == 1 ) +/****************************************************************************/ +int FixAddProtonForADP( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, + inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, ICR *picr, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ) +{ + int iBPlus=NO_VERTEX, iNV=NO_VERTEX, iNH = NO_VERTEX, neigh, neigh2; + EDGE_LIST NewlyFixedEdges; + int ret, i, j, k, k2, delta; + int num_at = pStruct->num_atoms; + int inv_forbidden_edge_mask = ~forbidden_edge_mask; + Vertex v1, v2; + /* for RunBnsTestOnce */ + Vertex vPathStart, vPathEnd; + int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; + BNS_EDGE *pe, *pe2; + + ret = 0; + /* + AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR ); + + for ( i = 0; i < num_at; i ++ ) { + if ( at2[i].radical == RADICAL_DOUBLET && at2[i].endpoint ) { + pStruct->bExtract |= EXTRACT_STRUCT_NUMBER; + ret = 1; + break; + } + } + */ + return ret; +} +#endif +/****************************************************************************************************** + OH OH + / / + -NH => -NH(+) to eliminate false tautomerism. S(IV) or N(V) or P(V) may be a centerpoint + \\ \ + O O(-) +*******************************************************************************************************/ +int FixRemoveExtraTautEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, + inp_ATOM *at2, inp_ATOM *atf, inp_ATOM *atn, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, ICR *picr, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ) +{ + EDGE_LIST NewlyFixedEdges; + int ret, i, j, k, delta, centerpoint, endpoint1, endpoint2; + int num_at = pStruct->num_atoms; + int inv_forbidden_edge_mask = ~forbidden_edge_mask; + Vertex v1, v2; + /* for RunBnsTestOnce */ + Vertex vPathStart, vPathEnd; + int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; + BNS_EDGE *pe, *pe2; + + ret = 0; + + AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR ); + if ( ret = AllocEdgeList( &NewlyFixedEdges, 2*num_at ) ) { + goto exit_function; + } + /* fix all charges */ + for ( i = 0; i < num_at; i ++ ) { + if ( (j = pVA[i].nCMinusGroupEdge-1) >= 0 ) { + if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) { + goto exit_function; + } + pBNS->edge[j].forbidden |= forbidden_edge_mask; + } + if ( (j = pVA[i].nCPlusGroupEdge-1) >= 0 ) { + if ( ret = AddToEdgeList( &NewlyFixedEdges, j, 0 )) { + goto exit_function; + } + pBNS->edge[j].forbidden |= forbidden_edge_mask; + } + } + + for ( i = 0; i < picr->num_endp_in1_only; i ++ ) { + endpoint1 = picr->endp_in1_only[i]-1; + if ( at2[endpoint1].valence == at2[endpoint1].chem_bonds_valence || + pVA[endpoint1].nCMinusGroupEdge <= 0 ) { + continue; + } + /* find centerpoint */ + for ( j = 0; j < at2[endpoint1].valence; j ++ ) { + if ( BOND_TYPE_DOUBLE == ( BOND_TYPE_MASK & at2[endpoint1].bond_type[j] ) ) { + centerpoint = at2[endpoint1].neighbor[j]; + if ( at2[centerpoint].charge || pVA[centerpoint].nCPlusGroupEdge <= 0 || + !is_centerpoint_elem( at2[centerpoint].el_number ) ) { + continue; + } + /* -- the centerpoint as depicted has no ChargeStruct flower --- + m = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[centerpoint].nCPlusGroupEdge-1 ); + if ( m < 0 || pBNS->edge[m].flow ) { + continue; + } + */ + /* find 2nd endpoint */ + for ( k = 0; k < at2[centerpoint].valence; k ++ ) { + if ( BOND_TYPE_SINGLE != ( BOND_TYPE_MASK & at2[centerpoint].bond_type[k] ) ) { + continue; + } + endpoint2 = at2[centerpoint].neighbor[k]; + if ( !at2[endpoint2].endpoint && atn[endpoint2].endpoint ) { + break; + } + } + if ( k == at2[centerpoint].valence ) { + continue; + } + /* the centerpoint and two extra endpoints have been found */ + pe = pBNS->edge + pVA[centerpoint].nCPlusGroupEdge - 1; + if ( !pe->flow ) { + continue; + } + pe2 = pBNS->edge + pVA[endpoint1].nCMinusGroupEdge - 1; + if ( pe2->flow ) { + continue; + } + delta = 1; + v1 = pe->neighbor1; + v2 = pe->neighbor12 ^ v1; + pe->flow -= delta; + pBNS->vert[v1].st_edge.flow -= delta; + pBNS->vert[v2].st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + pe2->forbidden &= inv_forbidden_edge_mask; /* allow the charge to move */ + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + if ( ret < 0 ) { + goto exit_function; + } + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && + nDeltaCharge <= 1 ) { + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + (*pnNumRunBNS) ++; + if ( ret < 0 ) { + goto exit_function; + } else + if ( ret ) { + *pnTotalDelta += ret; + } else { + ret = RI_ERR_PROGR; + } + goto exit_function; + } else { + ret = 0; + pe->flow += delta; + pBNS->vert[v1].st_edge.flow += delta; + pBNS->vert[v2].st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + pe2->forbidden |= forbidden_edge_mask; + } + } + } + } + +exit_function: + /* remove bond fixation */ + RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask ); + AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE ); + return ret; +} + +/*************************************************************************/ +int FillOutExtraFixedHDataRestr( StrFromINChI *pStruct ) +{ + int i, j, k, len, ret = 0; + AT_NUMB *pNum; + for ( i = 0; i < TAUT_NUM; i ++ ) { + if ( pStruct->pOneINChI_Aux[i] ) { + pNum = (pStruct->pOneINChI_Aux[i]->nIsotopicOrigAtNosInCanonOrd && + pStruct->pOneINChI_Aux[i]->nIsotopicOrigAtNosInCanonOrd[0])? + pStruct->pOneINChI_Aux[i]->nIsotopicOrigAtNosInCanonOrd: + (pStruct->pOneINChI_Aux[i]->nOrigAtNosInCanonOrd && + pStruct->pOneINChI_Aux[i]->nOrigAtNosInCanonOrd[0])? + pStruct->pOneINChI_Aux[i]->nOrigAtNosInCanonOrd : NULL; + } else { + pNum = NULL; + } + if ( pNum ) { + len = pStruct->num_atoms * sizeof(pStruct->nCanon2Atno[0][0]); + if ( !pStruct->nCanon2Atno[i] && + !(pStruct->nCanon2Atno[i] = (AT_NUMB *) inchi_malloc( len )) || + !pStruct->nAtno2Canon[i] && + !(pStruct->nAtno2Canon[i] = (AT_NUMB *) inchi_malloc( len ))) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + + INCHI_HEAPCHK + + memcpy( pStruct->nCanon2Atno[i], pNum, len ); /* ??? the next for(...) fills it out */ + + INCHI_HEAPCHK + + for ( j = 0; j < pStruct->num_atoms; j ++ ) { + k = pNum[j]-1; /* atom number */ + pStruct->nCanon2Atno[i][j] = (AT_NUMB)k; + pStruct->nAtno2Canon[i][k] = (AT_NUMB)j; + INCHI_HEAPCHK + } + } else + if ( !i ) { + ret = RI_ERR_PROGR; + goto exit_function; + } else { + if ( pStruct->nCanon2Atno[i] ) { + inchi_free( pStruct->nCanon2Atno[i] ); + pStruct->nCanon2Atno[i] = NULL; + } + INCHI_HEAPCHK + if ( pStruct->nAtno2Canon[i] ) { + inchi_free( pStruct->nAtno2Canon[i] ); + pStruct->nAtno2Canon[i] = NULL; + } + INCHI_HEAPCHK + } + } + +exit_function: + return ret; +} + +/*************************************************************************/ +int FillOutExtraFixedHDataInChI( StrFromINChI *pStruct, INChI *pInChI[] ) +{ + int ret = 0; + /*--- allocate memory for Mobile/Fixed-H data from the input InChI ---*/ + if ( NULL == pStruct->endpoint ) { + pStruct->endpoint = (AT_NUMB *)inchi_calloc(pStruct->num_atoms, sizeof(pStruct->endpoint[0])); + } else { + memset( pStruct->endpoint, 0, pStruct->num_atoms * sizeof(pStruct->endpoint[0] ) ); + } + if ( NULL == pStruct->fixed_H ) { + pStruct->fixed_H = (S_CHAR *) inchi_malloc(pStruct->num_atoms * sizeof(pStruct->fixed_H[0])); + } + if ( !pStruct->endpoint || !pStruct->fixed_H ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + /*--- fill out Mobile/Fixed-H data from the input InChI ---*/ + GetTgroupInfoFromInChI( &pStruct->ti, NULL, pStruct->endpoint, pInChI[1] ); + if ( pInChI[0]->nNum_H_fixed ) { + memcpy( pStruct->fixed_H, pInChI[0]->nNum_H_fixed, pStruct->num_atoms * sizeof(pStruct->fixed_H[0]) ); + } else { + memset( pStruct->fixed_H, 0, pStruct->num_atoms * sizeof(pStruct->fixed_H[0]) ); + } + +exit_function: + return ret; +} + +/***********************************************************************************************/ +int FillOutCMP2FHINCHI( StrFromINChI *pStruct, inp_ATOM *at2, VAL_AT *pVA, INChI *pInChI[], CMP2FHINCHI *pc2i ) +{ + int ret = 0, i, j; + int bFixHRevrsExists = pInChI[1] && pInChI[1]->nNumberOfAtoms > 0 && !pInChI[1]->bDeleted; + inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[1] && + pStruct->pOne_norm_data[1]->at)? pStruct->pOne_norm_data[1]->at : NULL; + S_CHAR *num_Fixed_H_Revrs = pStruct->pOneINChI[0]->nNum_H_fixed? pStruct->pOneINChI[0]->nNum_H_fixed : NULL; + /* atom number in structure that produced original InChI is atom number in all inp_ATOM *atoms */ + /* atom number in structure that produced restored InChI is in nAtomRevrs[]: */ + AT_NUMB *nAtno2CanonRevrs = pStruct->nAtno2Canon[0]; + S_CHAR *pnMobHInChI = (pInChI[1] && pInChI[1]->nNum_H)? pInChI[1]->nNum_H : + (pInChI[0] && pInChI[0]->nNum_H)? pInChI[0]->nNum_H : NULL; + S_CHAR *pnMobHRevrs = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNum_H)? + pStruct->pOneINChI[1]->nNum_H : + (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)? + pStruct->pOneINChI[0]->nNum_H : NULL; + int nNumTgHInChI, nNumTgMInChI, nNumTgHRevrs, nNumTgMRevrs; + memset( pc2i, 0, sizeof(*pc2i) ); + pc2i->nNumTgInChI = pStruct->ti.num_t_groups; + pc2i->nNumTgRevrs = pStruct->One_ti.num_t_groups; + pc2i->bHasDifference |= pc2i->nNumTgInChI != pc2i->nNumTgRevrs; + + pc2i->nNumRemHInChI = pStruct->nNumRemovedProtonsMobHInChI; + pc2i->nNumRemHRevrs = pStruct->One_ti.tni.nNumRemovedProtons; + pc2i->bHasDifference |= pc2i->nNumRemHInChI != pc2i->nNumRemHRevrs; + + pc2i->bFixedHLayerExistsRevrs = bFixHRevrsExists; + pc2i->bHasDifference |= !bFixHRevrsExists; + + for ( i = 0; i < pStruct->ti.num_t_groups && i < pStruct->One_ti.num_t_groups; i ++ ) { + nNumTgHInChI = pStruct->ti.t_group[i].num[0] - pStruct->ti.t_group[i].num[1]; + nNumTgMInChI = pStruct->ti.t_group[i].num[1]; + nNumTgHRevrs = pStruct->One_ti.t_group[i].num[0] - pStruct->One_ti.t_group[i].num[1]; + nNumTgMRevrs = pStruct->One_ti.t_group[i].num[1]; + + pc2i->bHasDifference |= nNumTgHInChI != nNumTgHRevrs; + pc2i->bHasDifference |= nNumTgMInChI != nNumTgMRevrs; + + if ( pStruct->ti.t_group[i].nNumEndpoints == + pStruct->One_ti.t_group[i].nNumEndpoints ) { + + if ( nNumTgHInChI != nNumTgHRevrs ) { + pc2i->nNumTgDiffH ++; + } + if ( nNumTgMInChI != nNumTgMRevrs ) { + pc2i->nNumTgDiffMinus ++; + } + } + pc2i->bHasDifference |= pStruct->ti.t_group[i].nNumEndpoints != + pStruct->One_ti.t_group[i].nNumEndpoints; + + pc2i->nNumTgHInChI += nNumTgHInChI; + pc2i->nNumTgMInChI += nNumTgMInChI; + pc2i->nNumTgHRevrs += nNumTgHRevrs; + pc2i->nNumTgMRevrs += nNumTgMRevrs; + } + for ( ; i < pStruct->ti.num_t_groups; i ++ ) { + nNumTgHInChI = pStruct->ti.t_group[i].num[0] - pStruct->ti.t_group[i].num[1]; + nNumTgMInChI = pStruct->ti.t_group[i].num[1]; + pc2i->nNumTgHInChI += nNumTgHInChI; + pc2i->nNumTgMInChI += nNumTgMInChI; + pc2i->bHasDifference |= 1; + } + for ( ; i < pStruct->One_ti.num_t_groups; i ++ ) { + nNumTgHRevrs = pStruct->One_ti.t_group[i].num[0] - pStruct->One_ti.t_group[i].num[1]; + nNumTgMRevrs = pStruct->One_ti.t_group[i].num[1]; + pc2i->nNumTgHRevrs += nNumTgHRevrs; + pc2i->nNumTgMRevrs += nNumTgMRevrs; + pc2i->bHasDifference |= 1; + } + for ( i = j = 0; i < pStruct->num_atoms; i ++ ) { + /* i = original InChI canonical number - 1 */ + /* k = atom number from InChI created out of restored Fixed-H structure */ + int iCanonRevrs = nAtno2CanonRevrs[i]; + int endptInChI = pStruct->endpoint[i]; /* endpoint in InChI */ + int endptRevrs = at_Mobile_H_Revrs? at_Mobile_H_Revrs[i].endpoint : 0; + int nFixHInChI = pStruct->fixed_H[i]; + int nFixHRevrs = num_Fixed_H_Revrs? num_Fixed_H_Revrs[iCanonRevrs]:0; + int nMobHInChI = pnMobHInChI? pnMobHInChI[i]:0; + int nMobHRevrs = pnMobHRevrs? pnMobHRevrs[iCanonRevrs]:0; + if ( /*(!endptInChI || !endptRevrs) &&*/ (nFixHInChI != nFixHRevrs ) || + (!endptInChI != !endptRevrs) || nMobHInChI != nMobHRevrs ) { + /* in InChI or reversed InChI atom[i] is not tautomeric */ + /* and number of fixed-H on the atom[i] differs */ + if ( j >= MAX_DIFF_FIXH ) { + ret = RI_ERR_PROGR; + goto exit_function; + } + pc2i->c2at[j].endptInChI = endptInChI; + pc2i->c2at[j].endptRevrs = endptRevrs; + pc2i->bHasDifference |= !endptInChI != !endptRevrs; + pc2i->c2at[j].atomNumber = i; + pc2i->c2at[j].nValElectr = pVA[i].cNumValenceElectrons; + pc2i->c2at[j].nPeriodNum = pVA[i].cPeriodicRowNumber; + pc2i->c2at[j].nFixHInChI = nFixHInChI; + pc2i->c2at[j].nFixHRevrs = nFixHRevrs; + pc2i->bHasDifference |= nFixHInChI != nFixHRevrs; + pc2i->c2at[j].nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H[i] : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H[i] : 0; + pc2i->c2at[j].nMobHRevrs = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNum_H)? + pStruct->pOneINChI[1]->nNum_H[iCanonRevrs] : + (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)? + pStruct->pOneINChI[0]->nNum_H[iCanonRevrs] : 0; + pc2i->nNumDiffMobH += (nMobHInChI != nMobHRevrs && !endptRevrs && !endptInChI); + pc2i->bHasDifference |= nMobHInChI != nMobHRevrs; + pc2i->c2at[j].nNumHRevrs = at2[i].num_H; + pc2i->c2at[j].nAtChargeRevrs = at2[i].charge; + j ++; + } + pc2i->nNumEndpInChI += (endptInChI != 0); + pc2i->nNumEndpRevrs += (endptRevrs != 0); + + if ( !pVA[i].cMetal ) { + pc2i->nChargeFixHRevrsNonMetal += at2[i].charge; + pc2i->nChargeMobHRevrsNonMetal += at_Mobile_H_Revrs? at_Mobile_H_Revrs[i].charge : 0; + } + + /*pStruct->bExtract |= EXTRACT_STRUCT_NUMBER;*/ + } + pc2i->nChargeFixHInChI = pInChI[0]? pInChI[0]->nTotalCharge : 0; + pc2i->nChargeMobHInChI = pInChI[1]? pInChI[1]->nTotalCharge : 0; + + pc2i->nChargeMobHRevrs = pStruct->pOneINChI[1]? pStruct->pOneINChI[1]->nTotalCharge : + pStruct->pOneINChI[0]? pStruct->pOneINChI[0]->nTotalCharge : 0; + pc2i->nChargeFixHRevrs = pStruct->pOneINChI[0]? pStruct->pOneINChI[0]->nTotalCharge : 0; + + pc2i->bHasDifference |= pc2i->nChargeFixHInChI != pc2i->nChargeFixHRevrs; + pc2i->bHasDifference |= pc2i->nChargeMobHInChI != pc2i->nChargeMobHRevrs; + +exit_function: + pc2i->len_c2at = j; + + return ret; +} + +/***********************************************************************************************/ +int FillOutCMP2MHINCHI( StrFromINChI *pStruct, ALL_TC_GROUPS *pTCGroups, inp_ATOM *at2, + VAL_AT *pVA, INChI *pInChI[], CMP2MHINCHI *pc2i ) +{ + int ret = 0, i, j, iat; + int bFixHRevrsExists = pInChI[1] && pInChI[1]->nNumberOfAtoms > 0 && !pInChI[1]->bDeleted; + inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[0] && + pStruct->pOne_norm_data[0]->at)? pStruct->pOne_norm_data[0]->at : NULL; + /* atom number in structure that produced original InChI is atom number in all inp_ATOM *atoms */ + /* atom number in structure that produced restored InChI is in nAtomRevrs[]: */ + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + AT_NUMB *nAtno2CanonRevrs = pStruct->nAtno2Canon[0]; + S_CHAR *pnMobHInChI = (pInChI[0] && pInChI[0]->nNum_H)? pInChI[0]->nNum_H : NULL; + S_CHAR *pnMobHRevrs = (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)? + pStruct->pOneINChI[0]->nNum_H : NULL; + int nNumTgHInChI, nNumTgMInChI, nNumTgHRevrs, nNumTgMRevrs; + memset( pc2i, 0, sizeof(*pc2i) ); + pc2i->nNumTgInChI = pStruct->ti.num_t_groups; + pc2i->nNumTgRevrs = pStruct->One_ti.num_t_groups; + pc2i->bHasDifference |= pc2i->nNumTgInChI != pc2i->nNumTgRevrs; + + pc2i->nNumRemHInChI = pStruct->nNumRemovedProtonsMobHInChI; + pc2i->nNumRemHRevrs = pStruct->One_ti.tni.nNumRemovedProtons; + /*pc2i->bHasDifference |= pc2i->nNumRemHInChI != pc2i->nNumRemHRevrs;*/ + + pc2i->bFixedHLayerExistsRevrs = bFixHRevrsExists; + /*pc2i->bHasDifference |= !bFixHRevrsExists;*/ + + for ( i = 0; i < pStruct->ti.num_t_groups; i ++ ) { + int jFst = pStruct->ti.t_group[i].nFirstEndpointAtNoPos; + int jNum = pStruct->ti.t_group[i].nNumEndpoints; + int is_N, is_O; + for ( j = 0; j < jNum; j ++ ) { + iat = pStruct->ti.nEndpointAtomNumber[jFst + j]; + is_N = pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1; + is_O = pVA[iat].cNumValenceElectrons == 6; + if ( is_N + is_O != 1 ) { + return RI_ERR_SYNTAX; + } + pc2i->nNumTgNInChI += is_N; + pc2i->nNumTgOInChI += is_O; + if ( at2[iat].chem_bonds_valence == at2[iat].valence ) { + /* donor */ + if ( is_N ) { + /* N */ + pc2i->nNumTgNHInChI += at2[iat].charge == 0 && at2[iat].num_H == 1; + pc2i->nNumTgNH2InChI += at2[iat].charge == 0 && at2[iat].num_H == 2; + pc2i->nNumTgNMinusInChI += at2[iat].charge == -1 && at2[iat].num_H == 0; + pc2i->nNumTgNHMinusInChI += at2[iat].charge == -1 && at2[iat].num_H == 1; + } else { + /* O, S, Se, Te */ + pc2i->nNumTgOHInChI += at2[iat].charge == 0 && at2[iat].num_H == 1; + pc2i->nNumTgOMinusInChI += at2[iat].charge == -1 && at2[iat].num_H == 0; + } + } else + if ( at2[iat].chem_bonds_valence == at2[iat].valence+1 ) { + /* donor */ + if ( is_N ) { + /* N */ + pc2i->nNumTgDBNHInChI += at2[iat].charge == 0 && at2[iat].num_H == 1; + pc2i->nNumTgDBNMinusInChI += at2[iat].charge == -1 && at2[iat].num_H == 0; + pc2i->nNumTgDBNInChI += at2[iat].charge == 0 && at2[iat].num_H == 0; + } else { + /* O, S, Se, Te */ + pc2i->nNumTgDBOInChI += at2[iat].charge == 0 && at2[iat].num_H == 0; + } + } + } + } + for ( i = 0; i < pStruct->One_ti.num_t_groups; i ++ ) { + int jFst = pStruct->One_ti.t_group[i].nFirstEndpointAtNoPos; + int jNum = pStruct->One_ti.t_group[i].nNumEndpoints; + int is_N, is_O; + for ( j = 0; j < jNum; j ++ ) { + iat = nCanon2AtnoRevrs[(int)pStruct->One_ti.nEndpointAtomNumber[jFst + j]]; + is_N = pVA[iat].cNumValenceElectrons == 5 && pVA[iat].cPeriodicRowNumber == 1; + is_O = pVA[iat].cNumValenceElectrons == 6; + if ( is_N + is_O != 1 ) { + return RI_ERR_PROGR; + } + pc2i->nNumTgNRevrs += is_N; + pc2i->nNumTgORevrs += is_O; + if ( at2[iat].chem_bonds_valence == at2[iat].valence ) { + /* donor */ + if ( is_N ) { + /* N */ + pc2i->nNumTgNHRevrs += at2[iat].charge == 0 && at2[iat].num_H == 1; + pc2i->nNumTgNH2Revrs += at2[iat].charge == 0 && at2[iat].num_H == 2; + pc2i->nNumTgNMinusRevrs += at2[iat].charge == -1 && at2[iat].num_H == 0; + pc2i->nNumTgNHMinusRevrs += at2[iat].charge == -1 && at2[iat].num_H == 1; + } else { + /* O, S, Se, Te */ + pc2i->nNumTgOHRevrs += at2[iat].charge == 0 && at2[iat].num_H == 1; + pc2i->nNumTgOMinusRevrs += at2[iat].charge == -1 && at2[iat].num_H == 0; + } + } else + if ( at2[iat].chem_bonds_valence == at2[iat].valence+1 ) { + /* donor */ + if ( is_N ) { + /* N */ + pc2i->nNumTgDBNHRevrs += at2[iat].charge == 0 && at2[iat].num_H == 1; + pc2i->nNumTgDBNMinusRevrs += at2[iat].charge == -1 && at2[iat].num_H == 0; + pc2i->nNumTgDBNRevrs += at2[iat].charge == 0 && at2[iat].num_H == 0; + } else { + /* O, S, Se, Te */ + pc2i->nNumTgDBORevrs += at2[iat].charge == 0 && at2[iat].num_H == 0; + } + } + } + } + + for ( i = 0; i < pStruct->ti.num_t_groups && i < pStruct->One_ti.num_t_groups; i ++ ) { + nNumTgHInChI = pStruct->ti.t_group[i].num[0] - pStruct->ti.t_group[i].num[1]; + nNumTgMInChI = pStruct->ti.t_group[i].num[1]; + nNumTgHRevrs = pStruct->One_ti.t_group[i].num[0] - pStruct->One_ti.t_group[i].num[1]; + nNumTgMRevrs = pStruct->One_ti.t_group[i].num[1]; + + pc2i->bHasDifference |= nNumTgHInChI != nNumTgHRevrs; + pc2i->bHasDifference |= nNumTgMInChI != nNumTgMRevrs; + + if ( pStruct->ti.t_group[i].nNumEndpoints == + pStruct->One_ti.t_group[i].nNumEndpoints ) { + + if ( nNumTgHInChI != nNumTgHRevrs ) { + pc2i->nNumTgDiffH ++; + } + if ( nNumTgMInChI != nNumTgMRevrs ) { + pc2i->nNumTgDiffMinus ++; + } + } + pc2i->bHasDifference |= pStruct->ti.t_group[i].nNumEndpoints != + pStruct->One_ti.t_group[i].nNumEndpoints; + + pc2i->nNumTgHInChI += nNumTgHInChI; + pc2i->nNumTgMInChI += nNumTgMInChI; + pc2i->nNumTgHRevrs += nNumTgHRevrs; + pc2i->nNumTgMRevrs += nNumTgMRevrs; + } + for ( ; i < pStruct->ti.num_t_groups; i ++ ) { + nNumTgHInChI = pStruct->ti.t_group[i].num[0] - pStruct->ti.t_group[i].num[1]; + nNumTgMInChI = pStruct->ti.t_group[i].num[1]; + pc2i->nNumTgHInChI += nNumTgHInChI; + pc2i->nNumTgMInChI += nNumTgMInChI; + pc2i->bHasDifference |= 1; + } + for ( ; i < pStruct->One_ti.num_t_groups; i ++ ) { + nNumTgHRevrs = pStruct->One_ti.t_group[i].num[0] - pStruct->One_ti.t_group[i].num[1]; + nNumTgMRevrs = pStruct->One_ti.t_group[i].num[1]; + pc2i->nNumTgHRevrs += nNumTgHRevrs; + pc2i->nNumTgMRevrs += nNumTgMRevrs; + pc2i->bHasDifference |= 1; + } + for ( i = j = 0; i < pStruct->num_atoms; i ++ ) { + /* i = original InChI canonical number - 1 */ + /* k = atom number from InChI created out of restored Fixed-H structure */ + int iCanonRevrs = nAtno2CanonRevrs[i]; + int endptInChI = at2[i].endpoint; /* endpoint in InChI */ + int endptRevrs = at_Mobile_H_Revrs? at_Mobile_H_Revrs[i].endpoint : 0; + int nMobHInChI = pnMobHInChI? pnMobHInChI[i]:0; + int nMobHRevrs = pnMobHRevrs? pnMobHRevrs[iCanonRevrs]:0; + if ( (!endptInChI != !endptRevrs) || nMobHInChI != nMobHRevrs ) { + /* in InChI or reversed InChI atom[i] is not tautomeric */ + /* and number of fixed-H on the atom[i] differs */ + if ( j >= MAX_DIFF_FIXH ) { + ret = RI_ERR_PROGR; + goto exit_function; + } + pc2i->c2at[j].endptInChI = endptInChI; + pc2i->c2at[j].endptRevrs = endptRevrs; + pc2i->bHasDifference |= !endptInChI != !endptRevrs; + pc2i->c2at[j].atomNumber = i; + pc2i->c2at[j].nValElectr = pVA[i].cNumValenceElectrons; + pc2i->c2at[j].nPeriodNum = pVA[i].cPeriodicRowNumber; + pc2i->c2at[j].nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H[i] : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H[i] : 0; + pc2i->c2at[j].nMobHRevrs = (pStruct->pOneINChI[1] && pStruct->pOneINChI[1]->nNum_H)? + pStruct->pOneINChI[1]->nNum_H[iCanonRevrs] : + (pStruct->pOneINChI[0] && pStruct->pOneINChI[0]->nNum_H)? + pStruct->pOneINChI[0]->nNum_H[iCanonRevrs] : 0; + + pc2i->nNumDiffMobH += (nMobHInChI != nMobHRevrs && !endptRevrs && !endptInChI); + pc2i->bHasDifference |= (nMobHInChI != nMobHRevrs); + pc2i->c2at[j].nNumHRevrs = at2[i].num_H; + pc2i->c2at[j].nAtChargeRevrs = at2[i].charge; + j ++; + } + pc2i->nNumEndpInChI += (endptInChI != 0); + pc2i->nNumEndpRevrs += (endptRevrs != 0); + + if ( !pVA[i].cMetal ) { + pc2i->nChargeMobHRevrsNonMetal += (at_Mobile_H_Revrs && !at_Mobile_H_Revrs[i].endpoint)? at_Mobile_H_Revrs[i].charge : 0; + } + + + /*pStruct->bExtract |= EXTRACT_STRUCT_NUMBER;*/ + } + pc2i->nChargeMobHRevrsNonMetal += pTCGroups->tgroup_charge; + + pc2i->nChargeMobHInChI = pInChI[0]? pInChI[0]->nTotalCharge : 0; + + pc2i->nChargeMobHRevrs = pStruct->pOneINChI[0]? pStruct->pOneINChI[0]->nTotalCharge : 0; + + pc2i->bHasDifference |= pc2i->nChargeMobHInChI != pc2i->nChargeMobHRevrs; + +exit_function: + pc2i->len_c2at = j; + + return ret; +} + + +/****************************************************************************/ +int NormalizeAndCompare( CANON_GLOBALS *pCG, INCHI_CLOCK *ic, + ICHICONST INPUT_PARMS *ip, + STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, + StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, + ALL_TC_GROUPS *pTCGroups, INChI *pInChI[], + long num_inp, int bHasSomeFixedH, + int *pnNumRunBNS, int *pnTotalDelta, + int forbidden_edge_mask, int forbidden_stereo_edge_mask) +{ + int i; + int err; + ICR icr, icr2; + int num_norm_endpoints, num_endpoints, num_norm_t_groups, num_mobile, num_norm_mobile, ret = 0; +#if ( bRELEASE_VERSION == 0 ) +#ifndef TARGET_API_LIB + const char *szCurHdr = (ip->pSdfValue && ip->pSdfValue[0])? ip->pSdfValue : "???"; + int iComponent = pTCGroups->iComponent; +#endif +#endif + T_GROUP_INFO *t_group_info = NULL; + inp_ATOM *at_norm = NULL; /* normalized */ + inp_ATOM *at_prep = NULL; /* preprocessed */ + INCHI_MODE cmpInChI, cmpInChI2; + int nDeltaPrev, nDeltaCur; + int iOrigInChI, iRevrInChI; + + + /***********************************************************/ + /* normalize and create one component InChI */ + /***********************************************************/ + ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + &t_group_info, &at_norm, &at_prep ); + if ( ret < 0 ) { +#if ( bRELEASE_VERSION == 0 ) +#ifndef TARGET_API_LIB + fprintf( stdout, "\nERROR in MakeOneInchi-1: %ld %s Comp:%d %c%c Err:%d\n", num_inp, + szCurHdr? szCurHdr: "???", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', ret); +#endif +#endif + goto exit_function; + } + if ( pStruct->bMobileH == TAUT_NON ) { + /* these indexes are used to compare Mobile-H InChI */ + iOrigInChI = (pInChI[1] && pInChI[1]->nNumberOfAtoms && !pInChI[1]->bDeleted)? 1 : 0; + iRevrInChI = (pStruct->pOneINChI[1] &&pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted)? 1 : 0; + } else { + iOrigInChI = 0; + iRevrInChI = 0; + } + + /************************************************************/ + /* compare */ + /************************************************************/ + if ( pStruct->iMobileH == TAUT_NON && (ret = FillOutExtraFixedHDataRestr( pStruct )) ) { + goto exit_function; + } + cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *a2*/, &icr, &err ); + if ( cmpInChI & IDIF_PROBLEM ) { + ret = RI_ERR_PROGR; /* severe restore problem */ + goto exit_function; + } + if ( err ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + /********** InChI from restored structure has LESS hydrogen atoms ******************************/ + if ( (cmpInChI & IDIF_LESS_H) && at_prep && 0 < (nDeltaCur = icr.tot_num_H2 - icr.tot_num_H1) ) { + do { + ret = FixLessHydrogenInFormula( pBNS, pBD, pStruct, at, at2, at_prep, pVA, pTCGroups, + pnNumRunBNS, pnTotalDelta, forbidden_edge_mask ); + if ( ret < 0 ) { + goto exit_function; + } + if ( ret ) { + /* Probably success. The changes are in pBNS. Create new InChI out of the new restored structure */ + ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + &t_group_info, &at_norm, &at_prep ); + if ( ret < 0 ) { +#if ( bRELEASE_VERSION == 0 ) +#ifndef TARGET_API_LIB + fprintf( stdout, "\nERROR in MakeOneInchi-2: %ld %s Comp:%d %c%c Err:%d\n", num_inp, + szCurHdr? szCurHdr: "???", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', ret); +#endif +#endif + goto exit_function; + } + /* compare new InChI to the original InChI */ + if ( pStruct->bMobileH == TAUT_NON ) { + iRevrInChI = (pStruct->pOneINChI[1] &&pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted)? 1 : 0; + } else { + iRevrInChI = 0; + } + if ( pStruct->iMobileH == TAUT_NON && (ret = FillOutExtraFixedHDataRestr( pStruct )) ) { + goto exit_function; + } + cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL, &icr, &err ); + nDeltaPrev = nDeltaCur; + nDeltaCur = icr.tot_num_H2 - icr.tot_num_H1; + } else { + break; + } + } while( (cmpInChI & IDIF_LESS_H) && at_prep && nDeltaCur && nDeltaCur < nDeltaPrev ); + } + /********** InChI from restored structure has MORE hydrogen atoms ******************************/ + if ( (cmpInChI & IDIF_MORE_H) && at_prep && 0 < (nDeltaCur = icr.tot_num_H1 - icr.tot_num_H2) ) { + do { + ret = FixMoreHydrogenInFormula( pBNS, pBD, pStruct, at, at2, at_prep, pVA, pTCGroups, + pnNumRunBNS, pnTotalDelta, forbidden_edge_mask ); + if ( ret < 0 ) { + goto exit_function; + } + if ( ret ) { + /* Probably success. The changes are in pBNS. Create new InChI out of the new restored structure */ + ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + &t_group_info, &at_norm, &at_prep ); + if ( ret < 0 ) { +#if ( bRELEASE_VERSION == 0 ) +#ifndef TARGET_API_LIB + fprintf( stdout, "\nERROR in MakeOneInchi-3: %ld %s Comp:%d %c%c Err:%d\n", num_inp, + szCurHdr? szCurHdr: "???", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', ret); +#endif +#endif + goto exit_function; + } + /* compare new InChI to the original InChI */ + if ( pStruct->bMobileH == TAUT_NON ) { + iRevrInChI = (pStruct->pOneINChI[1] &&pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted)? 1 : 0; + } else { + iRevrInChI = 0; + } + if ( pStruct->iMobileH == TAUT_NON && (ret = FillOutExtraFixedHDataRestr( pStruct )) ) { + goto exit_function; + } + cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL, &icr, &err ); + nDeltaPrev = nDeltaCur; + nDeltaCur = icr.tot_num_H1 - icr.tot_num_H2; + } else { + break; + } + } while( (cmpInChI & IDIF_MORE_H) && at_prep && nDeltaCur && nDeltaCur < nDeltaPrev ); + } + /***************** Fix non-taut atoms normalized to tautomeric endpoints ***********************/ + if ( (cmpInChI & IDIF_EXTRA_TG_ENDP) && at_norm && 0 < (nDeltaCur = icr.num_endp_in1_only) ) { + do { + ret = FixRemoveExtraTautEndpoints( pBNS, pBD, pStruct, at, at2, at_prep, at_norm, pVA, pTCGroups, &icr, + pnNumRunBNS, pnTotalDelta, forbidden_edge_mask ); + if ( ret < 0 ) { + goto exit_function; + } + if ( ret ) { + /* Probably success. The changes are in pBNS. Create new InChI out of the new restored structure */ + ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + &t_group_info, &at_norm, &at_prep ); + if ( ret < 0 ) { +#if ( bRELEASE_VERSION == 0 ) +#ifndef TARGET_API_LIB + fprintf( stdout, "\nERROR in MakeOneInchi-4: %ld %s Comp:%d %c%c Err:%d\n", num_inp, + szCurHdr? szCurHdr: "???", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', ret); +#endif +#endif + goto exit_function; + } + /* compare new InChI to the original InChI */ + if ( pStruct->bMobileH == TAUT_NON ) { + iRevrInChI = (pStruct->pOneINChI[1] &&pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted)? 1 : 0; + } else { + iRevrInChI = 0; + } + if ( pStruct->iMobileH == TAUT_NON && (ret = FillOutExtraFixedHDataRestr( pStruct )) ) { + goto exit_function; + } + cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL, &icr, &err ); + nDeltaPrev = nDeltaCur; + nDeltaCur = icr.num_endp_in1_only; + } else { + break; + } + } while( (cmpInChI & IDIF_EXTRA_TG_ENDP) && at_norm && nDeltaCur && nDeltaCur < nDeltaPrev ); + } + /************************ case of Fixed-H ******************************************************/ + + if ( pStruct->bMobileH == TAUT_NON ) { + int num_tries = 0; + do { + if ( 0 > (ret = FixFixedHRestoredStructure( pCG, ic, ip, sd, pBNS, pBD, pStruct, at, at2, at3, pVA, pTCGroups, + &t_group_info, &at_norm, &at_prep, pInChI, + num_inp, bHasSomeFixedH, pnNumRunBNS, pnTotalDelta, forbidden_edge_mask, + forbidden_stereo_edge_mask) ) ) { + goto exit_function; + } + } while( num_tries ++ < 2 && ret > 0 ); + } + /************************ case of Fixed-H ******************************************************/ + if ( pStruct->bMobileH == TAUT_YES ) { + if ( 0 > (ret = FixMobileHRestoredStructure( pCG, ic, ip, sd, pBNS, pBD, pStruct, at, at2, at3, pVA, pTCGroups, + &t_group_info, &at_norm, &at_prep, pInChI, + num_inp, bHasSomeFixedH, pnNumRunBNS, pnTotalDelta, forbidden_edge_mask, + forbidden_stereo_edge_mask) ) ) { + goto exit_function; + } + } + /**********************************************************************************************/ + /* stereo */ + cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *a2*/, &icr, &err ); + if ( cmpInChI & IDIF_PROBLEM ) { + ret = RI_ERR_PROGR; /* severe restore problem */ + goto exit_function; + } + if ( err ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + cmpInChI2 = 0; + memset ( &icr2, 0, sizeof(icr2) ); + if ( iRevrInChI || iOrigInChI ) { + /* additional mobile-H compare in case of Fixed-H */ + cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *a2*/, &icr2, &err ); + if ( cmpInChI & IDIF_PROBLEM ) { + ret = RI_ERR_PROGR; /* severe restore problem */ + goto exit_function; + } + if ( err ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + } + ret = FixRestoredStructureStereo( pCG, ic, + cmpInChI, &icr, cmpInChI2, &icr2, + ip, sd, pBNS, pBD, pStruct, at, at2, at3, pVA, pTCGroups, + &t_group_info, &at_norm, &at_prep, pInChI, + num_inp, pnNumRunBNS, pnTotalDelta, forbidden_edge_mask, + forbidden_stereo_edge_mask); + + if ( ret < 0 ) { + goto exit_function; + } +#if ( FIX_ADD_PROTON_FOR_ADP == 1 ) + /************************ check and fix ADP by adding a proton (dummy) *************************/ + if ( cmpInChI && pTCGroups->num_tgroups && pBNS->tot_st_cap > pBNS->tot_st_flow ) { + ret = FixAddProtonForADP( pBNS, pBD, pStruct, at, at2, at_prep, pVA, pTCGroups, &icr, + pnNumRunBNS, pnTotalDelta, forbidden_edge_mask ); + if ( ret < 0 ) { + goto exit_function; + } + } +#endif + /* moved to MakeOneInChIOutOfStrFromINChI(): + pStruct->nNumRemovedProtons = (pStruct->iMobileH == TAUT_YES)? pStruct->One_ti.tni.nNumRemovedProtons : 0; + */ + + /* count endpoints */ + num_endpoints = 0; + num_norm_endpoints = 0; + num_norm_t_groups = 0; + num_mobile = 0; + num_norm_mobile = 0; + at_norm = pStruct->pOne_norm_data[0]->at; + for ( i = 0; i < pTCGroups->num_tgroups; i ++ ) { + num_endpoints += pTCGroups->pTCG[i].num_edges; + num_mobile += pTCGroups->pTCG[i].tg_num_H + pTCGroups->pTCG[i].tg_num_Minus; + } + + if ( t_group_info ) { + /* after canonicalization, t_group_info->t_group[i].num[0] = number of H */ + /* t_group_info->t_group[i].num[1] = number of (-) */ + for ( i = 0; i < t_group_info->num_t_groups; i ++ ) { + if ( t_group_info->t_group[i].num[0] ) { + num_norm_t_groups ++; + num_norm_endpoints += t_group_info->t_group[i].nNumEndpoints; + num_norm_mobile += t_group_info->t_group[i].num[0]+t_group_info->t_group[i].num[1]; + } + } + } +#if ( bRELEASE_VERSION == 0 ) +#ifndef TARGET_API_LIB + if ( num_norm_t_groups != pTCGroups->num_tgroups || num_norm_endpoints != num_endpoints ) { + /* need aggressive (de)protonation */ + /* pStruct->bExtract |= EXTRACT_STRUCT_NUMBER; */ + fprintf( stdout, "NORMCOMP: %s comp=%d %c%c: InChI/NormRvrs NumTg=%d/%d NumEndp=%d/%d\n", + (*ip).pSdfValue, (*pTCGroups).iComponent, + pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', + pTCGroups->num_tgroups, num_norm_t_groups, + num_endpoints, num_norm_endpoints ); + } +#endif +#endif + +exit_function: + + for( i = 0; i < TAUT_NUM; i ++ ) { + Free_INChI( &pStruct->pOneINChI[i] ); + Free_INChI_Aux( &pStruct->pOneINChI_Aux[i] ); + FreeInpAtomData( pStruct->pOne_norm_data[i] ); + if ( pStruct->pOne_norm_data[i] ) { + inchi_free( pStruct->pOne_norm_data[i] ); + pStruct->pOne_norm_data[i] = NULL; + } + } + free_t_group_info( &pStruct->One_ti ); + return ret; +} + + +/****************************************************************************/ +/* Find A=X< where all bonds to X except A=X are marked as stereogenic; temporary allow stereobonds */ +/* change and make A=X bonds single */ +int CheckAndRefixStereobonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) +{ + int forbidden_edge_stereo = BNS_EDGE_FORBIDDEN_MASK; + int inv_forbidden_edge_stereo = ~forbidden_edge_stereo; + + int i, k, ne, j1, j2, num_wrong, num_fixed; + int ret2, retBNS, ret; + int num_at = pStruct->num_atoms; + int num_deleted_H = pStruct->num_deleted_H; + int len_at = num_at + num_deleted_H; + EDGE_LIST FixedEdges, WrongEdges, CarbonChargeEdges; + + BNS_EDGE *pEdge; + Vertex v1, v2; + BNS_VERTEX *pv1, *pv2; + + ret = 0; + + /* to simplify, prepare new at[] from pBNS */ + memcpy( at2, at, len_at*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + pStruct->at = at; + if ( ret2 < 0 ) { + return ret; + } + + num_wrong = 0; + /* find wrong double bonds */ + for ( i = 0; i < num_at; i ++ ) { + if ( at2[i].valence == 3 && + at2[i].chem_bonds_valence - at2[i].valence == 1 && + at2[i].sb_parity[0] && at2[i].sb_parity[1] && !at2[i].sb_parity[2] && + (at2[i].bond_type[j1=(int)at2[i].sb_ord[0]] & BOND_TYPE_MASK) == BOND_TYPE_SINGLE && + (at2[i].bond_type[j2=(int)at2[i].sb_ord[1]] & BOND_TYPE_MASK) == BOND_TYPE_SINGLE && + j1 != j2 ) { + + num_wrong ++; + } + } + if ( !num_wrong ) { + return 0; + } + num_fixed = 0; + for ( i = 0; i < pBNS->num_bonds; i ++ ) { + pEdge = pBNS->edge + i; + if ( pEdge->forbidden & forbidden_edge_stereo ) { + num_fixed ++; + } + } + + /* there may be no fixed stereo bonds at all, see #87607 */ + AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &FixedEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &WrongEdges, EDGE_LIST_CLEAR ); + + /* do not goto exit_function before reaching this point: EdgeLists have not been initiated */ + + if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) { + goto exit_function; + } + if ( (ret = AllocEdgeList( &FixedEdges, num_fixed )) || + (ret = AllocEdgeList( &WrongEdges, num_wrong )) ) { + goto exit_function; + } + /* collect wrong double bonds and set flow=0 */ + for ( i = 0; i < num_at && WrongEdges.num_edges < num_wrong; i ++ ) { + if ( at2[i].valence == 3 && + at2[i].chem_bonds_valence - at2[i].valence == 1 && + at2[i].sb_parity[0] && at2[i].sb_parity[1] && !at2[i].sb_parity[2] && + (at2[i].bond_type[j1=(int)at2[i].sb_ord[0]] & BOND_TYPE_MASK) == BOND_TYPE_SINGLE && + (at2[i].bond_type[j2=(int)at2[i].sb_ord[1]] & BOND_TYPE_MASK) == BOND_TYPE_SINGLE && + j1 != j2 ) { + switch ( j1 + j2 ) { + case 1: /* 0, 1 */ + k = 2; + break; + case 2: /* 0, 2 */ + k = 1; + break; + case 3: /* 1, 2 */ + k = 0; + break; + default: + ret = RI_ERR_PROGR; + goto exit_function; + } + ne = pBNS->vert[i].iedge[k]; + pEdge = pBNS->edge + ne; + v1 = pEdge->neighbor1; + v2 = pEdge->neighbor12 ^ v1; + pv1 = pBNS->vert + v1; + pv2 = pBNS->vert + v2; + + if ( !pEdge->flow ) { + ret = RI_ERR_PROGR; + goto exit_function; + } + pEdge->flow --; + pEdge->forbidden |= forbidden_edge_mask; + pv1->st_edge.flow --; + pv2->st_edge.flow --; + pBNS->tot_st_flow -= 2; + if ( ret = AddToEdgeList( &WrongEdges, ne, 0 )) { + goto exit_function; + } + } + } + /* remove forbidden mark from stereo bonds (unfix stereo bonds) */ + for ( i = 0; i < pBNS->num_bonds && FixedEdges.num_edges < num_fixed; i ++ ) { + pEdge = pBNS->edge + i; + if ( pEdge->forbidden & forbidden_edge_stereo ) { + pEdge->forbidden &= inv_forbidden_edge_stereo; + FixedEdges.pnEdges[FixedEdges.num_edges ++] = i; + } + } + /* Run BNS to move charges and rearrange bond orders */ + retBNS = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + (*pnNumRunBNS) ++; + if ( retBNS < 0 ) { + goto exit_function; + } else + if ( retBNS > 0 ) { + *pnTotalDelta += retBNS; + } + /* remove forbidden_edge_mask and set forbidden_edge_stereo */ + RemoveForbiddenEdgeMask( pBNS, &WrongEdges, forbidden_edge_mask ); + /* allow carbon charges to change */ + RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask ); + /* fix previously unfixed stereo bonds */ + SetForbiddenEdgeMask( pBNS, &FixedEdges, forbidden_edge_stereo ); + /* Run BNS again in case not all edge flows are maximal */ + ret2 = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + (*pnNumRunBNS) ++; + if ( ret2 < 0 ) { + goto exit_function; + } else + if ( ret2 > 0 ) { + *pnTotalDelta += retBNS; + } + ret = retBNS; + +exit_function: + + AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); + AllocEdgeList( &FixedEdges, EDGE_LIST_FREE ); + AllocEdgeList( &WrongEdges, EDGE_LIST_FREE ); + + return ret; +} + + +/****************************************************************************/ +/* Find and eliminate false Mobile-H groups: Cl(=O)3(-O(-)) => Cl(-)(=O)4 */ +int MoveChargeToRemoveCenerpoints(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) +{ + int i, j, neigh, num_endpoints, num_success; + int num_donors, num_acceptors, bond_type, num_donors_O, num_acceptors_O, is_centerpoint_N, num_known_endpoints, num_wrong_neigh; + int ret2, ret_forbid_edges, ret, delta; + int num_at = pStruct->num_atoms; + int num_deleted_H = pStruct->num_deleted_H; + int len_at = num_at + num_deleted_H; + int forbidden_edge_test = BNS_EDGE_FORBIDDEN_TEST; + int bPossiblyIgnore = pStruct->charge >= 0 && (!pTCGroups->num_tgroups || pStruct->iMobileH == TAUT_NON && pStruct->ti.num_t_groups); + S_CHAR MobileChargeNeigh[MAXVAL], DoubleBondAcceptors[MAXVAL], DoubleBondNotONeigh[MAXVAL]; + int numMobileChargeNeigh, numDoubleBondAcceptors, numDoubleBondNotONeigh, numOtherDoubleBondOAcceptors=0; + EDGE_LIST ChargeListAllExcept_DB_O; + + + BNS_EDGE *pEdgeMinus, *pe; + Vertex v1m, v2m; + BNS_VERTEX *pv1m, *pv2m; + ret = 0; + num_success = 0; + + /* count O(+)H, N(+)H */ + + /* + if ( pStruct->charge >= 0 && (!pTCGroups->num_tgroups || pStruct->iMobileH == TAUT_NON && pStruct->ti.num_t_groups) ) { + goto exit_function; + } + */ + if ( ret = AllocEdgeList( &ChargeListAllExcept_DB_O, EDGE_LIST_CLEAR ) ) { + goto exit_function; + } + + + /* to simplify, prepare new at[] from pBNS */ + memcpy( at2, at, len_at*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + pStruct->at = at; + if ( ret2 < 0 ) { + ret = ret2; + goto exit_function; + } +#if ( FIND_RING_SYSTEMS == 1 ) + ret2 = MarkRingSystemsInp( at2, num_at, 0 ); + if ( ret2 < 0 ) { + ret = ret2; + goto exit_function; + } +#endif + /* mark bonds that cannot be tautomeric; do not forget to remove the marks later */ + ret_forbid_edges = SetForbiddenEdges( pBNS, at2, num_at, forbidden_edge_test, 0, NULL ); + if ( ret_forbid_edges < 0 ) { + ret = ret_forbid_edges; + goto exit_function; + } + + for ( i = 0; i < num_at; i ++ ) { + if ( pVA[i].cNumValenceElectrons != 4 && /* not C, Si, Ge */ + !(pVA[i].nTautGroupEdge || pStruct->iMobileH == TAUT_NON && pStruct->endpoint && pStruct->endpoint[i] ) && + !at2[i].num_H && !at2[i].charge && at2[i].valence >= 2 && + at2[i].valence < at2[i].chem_bonds_valence && + is_centerpoint_elem( at2[i].el_number ) ) { + + is_centerpoint_N = (pVA[i].cNumValenceElectrons == 5 && (pVA[i].cPeriodicRowNumber == 1 || pVA[i].cMetal)); + /* look at the neighbors */ + numMobileChargeNeigh = numDoubleBondAcceptors = numDoubleBondNotONeigh = num_donors = num_acceptors = 0; + num_donors_O = num_acceptors_O = 0; + num_known_endpoints = num_wrong_neigh = 0; + for ( j = 0, num_endpoints = 0; j < at2[i].valence; j ++ ) { + neigh = at2[i].neighbor[j]; + if ( (at2[neigh].endpoint || pStruct->iMobileH == TAUT_NON && pStruct->endpoint && pStruct->endpoint[neigh]) || at2[neigh].charge > 0 ) { + num_known_endpoints ++; + continue; + } + if ( pBNS->edge[pBNS->vert[i].iedge[j]].forbidden & forbidden_edge_test ) { + continue; + } + bond_type = at2[i].bond_type[j] & BOND_TYPE_MASK; + if ( bond_type > BOND_TYPE_DOUBLE ) { + num_wrong_neigh ++; + continue; + } + if ( at2[neigh].num_H && bond_type == BOND_TYPE_SINGLE ) { + break; /* not this case */ + } + if ( at2[neigh].chem_bonds_valence - at2[neigh].charge + != get_endpoint_valence( at2[neigh].el_number ) ) { + if ( bond_type == BOND_TYPE_DOUBLE && pVA[neigh].cNumValenceElectrons != 6 ) { + DoubleBondNotONeigh[numDoubleBondNotONeigh ++] = j; + } + continue; + } + if ( at2[neigh].charge == -1 && bond_type == BOND_TYPE_SINGLE && + (pVA[neigh].nCMinusGroupEdge < 1 || pBNS->edge[pVA[neigh].nCMinusGroupEdge-1].flow != 1) ) { + break; + } + switch( bond_type ) { + case BOND_TYPE_SINGLE: + if ( at2[neigh].charge != -1 || pVA[neigh].nCMinusGroupEdge <= 0 ) { + num_wrong_neigh ++; + continue; + } + num_donors ++; + num_donors_O += (pVA[neigh].cNumValenceElectrons == 6 && pVA[neigh].cPeriodicRowNumber <= 4); + MobileChargeNeigh[numMobileChargeNeigh ++] = j; + break; + case BOND_TYPE_DOUBLE: + if ( at2[neigh].charge ) { + num_wrong_neigh ++; + continue; + } + DoubleBondAcceptors[numDoubleBondAcceptors ++] = j; + num_acceptors ++; + num_acceptors_O += (pVA[neigh].cNumValenceElectrons == 6 && pVA[neigh].cPeriodicRowNumber <= 4); + } + } + if ( j != at2[i].valence || !num_donors || !num_acceptors ) { + continue; + } + /* special case NOn(-) */ + if ( is_centerpoint_N && (num_donors == num_donors_O) && (num_acceptors == num_acceptors_O) ) { + continue; + } + if ( pStruct->iMobileH == TAUT_NON && num_donors == numDoubleBondNotONeigh ) { + /* fix all charges except on =O */ + Vertex vPathStart, vPathEnd; + int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; + int k, e, num_MovedCharges = 0; + + if ( !ChargeListAllExcept_DB_O.num_edges ) { + numOtherDoubleBondOAcceptors = 0; + for ( k = 0; k < num_at; k ++ ) { + if ( 1 == at2[k].valence && pBNS->edge[pBNS->vert[k].iedge[0]].flow && + !pBNS->edge[pBNS->vert[k].iedge[0]].forbidden && + !((e=pVA[k].nCMinusGroupEdge-1) >= 0 && pBNS->edge[e].flow) && + !((e=pVA[k].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].flow) && + /* 0 == at2[k].charge && */ + pVA[k].cNumValenceElectrons == 6 && !pVA[k].cMetal && + pStruct->endpoint && pStruct->endpoint[k] || + pStruct->fixed_H && pStruct->fixed_H[k] ) { + numOtherDoubleBondOAcceptors ++; /* do not fix this minus edge */ + } else + if ( (e=pVA[k].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].flow && + !pBNS->edge[e].forbidden && + ( ret = AddToEdgeList( &ChargeListAllExcept_DB_O, e, 64 )) ) { + goto exit_function; + } + if ( (e=pVA[k].nCPlusGroupEdge-1) >= 0 && + !pBNS->edge[e].forbidden && + ( ret = AddToEdgeList( &ChargeListAllExcept_DB_O, e, 64 )) ) { + goto exit_function; + } + } + } + /* fix double bonds to non-O neighbors connected by double bonds; + we will try to make these bons single */ + for ( k = 0; k < numDoubleBondNotONeigh; k ++ ) { + e = pBNS->vert[i].iedge[(int)DoubleBondNotONeigh[k]]; + if ( !pBNS->edge[e].forbidden && + (ret = AddToEdgeList( &ChargeListAllExcept_DB_O, e, 64 ))) { + goto exit_function; + } + } + /* attempt to make DoubleBondNotONeigh[] single */ + SetForbiddenEdgeMask( pBNS, &ChargeListAllExcept_DB_O, forbidden_edge_mask); + for ( k = 0; k < numDoubleBondNotONeigh && num_MovedCharges < numMobileChargeNeigh; k ++ ) { + pe = pBNS->edge + pBNS->vert[i].iedge[(int)DoubleBondNotONeigh[k]]; + delta = 1; + if ( pe->flow != delta ) + continue; + pv1m = pBNS->vert + (v1m = pe->neighbor1); + pv2m = pBNS->vert + (v2m = pe->neighbor12 ^ v1m); + pv1m->st_edge.flow -= delta; + pv2m->st_edge.flow -= delta; + pe->flow -= delta; + pBNS->tot_st_flow -= 2*delta; + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + if ( ret < 0 ) { + goto exit_function; + } + if ( ret == 1 && (vPathEnd == v1m && vPathStart == v2m || + vPathEnd == v2m && vPathStart == v1m) && + nDeltaCharge == 0 /* (-) moving from one to another atom*/ ) { + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + (*pnNumRunBNS) ++; + if ( ret < 0 ) { + goto exit_function; + } else + if ( ret == 1 ) { + *pnTotalDelta += ret; + num_MovedCharges ++; + } else { + ret = RI_ERR_PROGR; + goto exit_function; + } + } else { + ret = 0; + pv1m->st_edge.flow += delta; + pv2m->st_edge.flow += delta; + pe->flow += delta; + pBNS->tot_st_flow += 2*delta; + } + } + RemoveForbiddenEdgeMask( pBNS, &ChargeListAllExcept_DB_O, forbidden_edge_mask); + } else + if ( !bPossiblyIgnore || !num_known_endpoints && !num_wrong_neigh && (num_acceptors_O + num_donors_O) >=3 ) { + /* remove negative charges from the neighbors */ + pBNS->vert[i].st_edge.cap += num_donors; /* enough to make all bonds to donors double */ + pBNS->tot_st_cap += num_donors; + pVA[i].cInitCharge -= num_donors; /* work no matter what are known charge/valence */ + for ( j = 0; j < numMobileChargeNeigh; j ++ ) { + neigh = at2[i].neighbor[ (int)MobileChargeNeigh[j] ]; + pEdgeMinus = pBNS->edge + (pVA[neigh].nCMinusGroupEdge-1); + v1m = pEdgeMinus->neighbor1; + v2m = pEdgeMinus->neighbor12 ^ v1m; + pv1m = pBNS->vert + v1m; + pv2m = pBNS->vert + v2m; + delta = pEdgeMinus->flow; + pv1m->st_edge.flow -= delta; + pv2m->st_edge.flow -= delta; + if ( IS_BNS_VT_C_GR( pv1m->type ) ) { + /* irreversible change to ChargeStruct */ + pv1m->st_edge.cap -= delta; + } else + if ( IS_BNS_VT_C_GR( pv2m->type ) ) { + /* irreversible change to ChargeStruct */ + pv2m->st_edge.cap -= delta; + } else { + ret = RI_ERR_PROGR; + goto exit_function; + } + pBNS->tot_st_cap -= delta; + pBNS->tot_st_flow -= 2*delta; + pEdgeMinus->flow -= delta; + } + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + (*pnNumRunBNS) ++; + if ( ret < 0 ) { + goto exit_function; + } else + if ( ret == num_donors ) { + *pnTotalDelta += ret; + num_success ++; + /*pStruct->bExtract |= EXTRACT_STRUCT_NUMBER;*/ + } else { + ret = RI_ERR_PROGR; + goto exit_function; + } + } + } + } + if ( ret_forbid_edges ) { + /* remove the marks */ + RemoveForbiddenBondFlowBits( pBNS, forbidden_edge_test ); + } + ret = num_success; +exit_function: + AllocEdgeList( &ChargeListAllExcept_DB_O, EDGE_LIST_FREE ); + return ret; +} + + +/****************************************************************************/ +/* Find and eliminate cases when Mobile H endpoint has radical on it (typical for wrong P(VI)(=O)3OH */ +int MakeSingleBondsMetal2ChargedHeteroat(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) +{ + int i; + + int ret2, ret, pass; + int num_at = pStruct->num_atoms; + int num_deleted_H = pStruct->num_deleted_H; + int len_at = num_at + num_deleted_H; + int inv_forbidden_edge_mask = ~forbidden_edge_mask; + + int j, k; + int cur_num_edges; + BNS_EDGE *e; + Vertex v1, v2; + + EdgeIndex *pFixedEdges; + int nNumEdgesToFix; + + ret = 0; + + /* to simplify, prepare new at[] from pBNS */ + memcpy( at2, at, len_at*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + pStruct->at = at; + if ( ret2 < 0 ) { + ret = ret2; + goto exit_function; + } + + pFixedEdges = NULL; + + nNumEdgesToFix = 0; /* cpunt nNumEdgesToFix only when pass==0 */ + cur_num_edges = 0; /* count cur_num_edges only when pass==1; at the end they must be equal */ + for ( pass = 0; pass < 2; pass ++ ) { + if ( pass ) { + /* 2nd pass: allocate edge storage */ + if ( !nNumEdgesToFix ) { + break; /* nothing to do */ + } + pFixedEdges = (EdgeIndex *) inchi_malloc(nNumEdgesToFix * sizeof( pFixedEdges[0] ) ); + if ( !pFixedEdges ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + } + for ( i = 0; i < num_at; i ++ ) { + int neigh; + if ( pVA[i].cMetal ) { + for ( j = 0; j < at2[i].valence; j ++ ) { + neigh = at2[i].neighbor[j]; + if ( pVA[neigh].cNumValenceElectrons == 4 && + pVA[neigh].cPeriodicRowNumber == 1 ) { + continue; /* ignore carbon */ + } + if ( at2[i].bond_type[j] > BOND_TYPE_SINGLE && at2[neigh].charge && + !pVA[neigh].cMetal && pVA[neigh].cnListIndex > 0 ) { + int cnBits = at2[neigh].charge > 0? MAKE_CN_BITS(cn_bits_N, cn_bits_P, 0, 0) : + MAKE_CN_BITS(cn_bits_N, cn_bits_M, 0, 0); + int atBits = cnList[pVA[neigh].cnListIndex-1].bits; + for ( k = 0; k < MAX_NUM_CN_BITS-1; k ++, atBits >>= cn_bits_shift ) { /* ??? */ + if ( (atBits & cnBits) == cnBits ) { + break; + } + } + if ( k == MAX_NUM_CN_BITS-1 ) { + continue; + } + if ( pass == 0 ) { + nNumEdgesToFix ++; + } else { + pFixedEdges[ cur_num_edges ++ ] = pBNS->vert[i].iedge[j]; + } + } + } + } + } + } + + /* restore the initial structures */ + memcpy( at2, at, (num_at + num_deleted_H)*sizeof(at2[0])); + + if ( nNumEdgesToFix && pFixedEdges ) { + if ( nNumEdgesToFix != cur_num_edges ) { + ret = RI_ERR_PROGR; + goto exit_function; + } + /* change edge flow, fix the edges, and run BNS */ + for ( i = 0; i < nNumEdgesToFix; i ++ ) { + e = pBNS->edge + pFixedEdges[i]; + v1 = e->neighbor1; + v2 = e->neighbor12 ^ v1; + e->flow --; + e->forbidden |= forbidden_edge_mask; + pBNS->vert[v1].st_edge.flow --; + pBNS->vert[v2].st_edge.flow --; + pBNS->tot_st_flow -= 2; + (*pnTotalDelta) -= 2; + } + /* Run BNS allowing to change any charges */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + (*pnNumRunBNS) ++; + if ( ret < 0 ) { + goto exit_function; + } else { + (*pnTotalDelta) += ret; + } + /* unfix the edges */ + for ( i = 0; i < nNumEdgesToFix; i ++ ) { + e = pBNS->edge + pFixedEdges[i]; + e->forbidden &= inv_forbidden_edge_mask; + } + if ( ret < 2 * nNumEdgesToFix ) { + /* not all fixes succeeded */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + (*pnNumRunBNS) ++; + if ( ret < 0 ) { + goto exit_function; + } else { + (*pnTotalDelta) += ret; + } + } + } + if ( pFixedEdges ) { + inchi_free( pFixedEdges ); + pFixedEdges = NULL; + } + + +exit_function: + return ret; +} + + +/**************************************************************************/ +/* In Reconnected structure change 'salt bonds' to 'coordination bonds */ +/* for example, M-O-C= -> M(+)-O(-)-C= */ +/* Defect: instead of NH2-C=O(+)-M it will restore NH2(+)=C-O(-)-M(+) */ +/* However, in this release metal-organic compounds do not get much care */ +int SaltBondsToCoordBonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask) +{ + int i; + + int ret2, ret, cur_success; + int num_at = pStruct->num_atoms; + int num_edges = pBNS->num_bonds + 2 * pBNS->num_atoms; + int num_deleted_H = pStruct->num_deleted_H; + int len_at = num_at + num_deleted_H; + int inv_forbidden_edge_mask = ~forbidden_edge_mask; + EDGE_LIST AllChargeEdges; + + int j, k, n; + BNS_EDGE *pe, *pePlusMetal, *peMinusO; + BNS_VERTEX *pv1, *pv2, *pvO, *pvM; + Vertex v1, v2, vPlusMinus; + + EdgeIndex ie, iePlusMetal, ieMinusO; + + Vertex vPathStart, vPathEnd; + int delta, nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; + + ret = 0; + cur_success = 0; + AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); + + if ( pStruct->iInchiRec == INCHI_BAS || !pStruct->pSrm->bMetalAddFlower || pStruct->pSrm->nMetalMinBondOrder ) { + goto exit_function; + } + + /* to simplify, prepare new at[] from pBNS */ + memcpy( at2, at, len_at*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + pStruct->at = at; + if ( ret2 < 0 ) { + ret = ret2; + goto exit_function; + } + for ( i = 0; i < num_at; i ++ ) { + if ( bIsMetalSalt( at2, i ) ) { + if ( !AllChargeEdges.num_edges ) { + /*--------- one-time action: fix all bonds, charges, taut. group edges ------------*/ + for ( j = 0; j < num_at; j ++ ) { + /* all bonds */ + for ( k = 0; k < at2[j].valence; k ++ ) { + n = at2[j].neighbor[k]; + if ( n < j && !pBNS->edge[ie = pBNS->vert[j].iedge[k]].forbidden && + ( ret = AddToEdgeList( &AllChargeEdges, ie, num_edges ) ) ) { + goto exit_function; + } + } + /* charge edges */ + if ( (ie=pVA[j].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[ie].forbidden && + (ret = AddToEdgeList( &AllChargeEdges, ie, num_edges ) ) ) { + goto exit_function; + } + if ( (ie=pVA[j].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[ie].forbidden && + ( ret = AddToEdgeList( &AllChargeEdges, ie, num_edges ) ) ) { + goto exit_function; + } + } + /* taut group edges */ + for ( j = 0; j < pTCGroups->num_tgroups; j ++ ) { + pv1 = pBNS->vert + (v1=pTCGroups->pTCG[j].nVertexNumber); /* t-group vertex */ + for ( k = 0; k < pv1->num_adj_edges; k ++ ) { + /* ie, pe - tautomeric atom edge; pv2 - endpoint vertex */ + /* Note: pe, pv2, v1 are not used here; they are to show how to traverse t-group */ + pv2 = pBNS->vert + (pe = pBNS->edge + (ie=pv1->iedge[k]))->neighbor1; + if ( ret = AddToEdgeList( &AllChargeEdges, ie, num_edges ) ) { + goto exit_function; + } + } + } + /*---------------------------------------------------------------*/ + } + /* replace all single bonds to neutral neighbors with zero-order bonds + allow neighbor charge change to (-1) and metal atom charge increment +1 */ + for ( k = 0; k < at2[i].valence; k ++ ) { + n = at2[i].neighbor[k]; + pe = pBNS->edge + pBNS->vert[i].iedge[k]; + if ( at2[n].charge || at2[i].bond_type[k] != BOND_TYPE_SINGLE ) { + continue; + } + iePlusMetal = pVA[i].nCPlusGroupEdge-1; + ieMinusO = pVA[n].nCMinusGroupEdge-1; + + if ( pe->flow != 1 || pe->forbidden || iePlusMetal < 0 ) { + continue; + } + pePlusMetal = pBNS->edge + iePlusMetal; + if ( pePlusMetal->flow <= 0 ) { + continue; /* to add (+) to metal this flow must be decremented */ + } + if ( ieMinusO >= 0 ) { + /* usually does not happen */ + peMinusO = pBNS->edge + ieMinusO; + + if ( peMinusO->flow || pePlusMetal->forbidden || peMinusO->forbidden ) { + continue; + } + + /* decrement bond order to 0 */ + delta = 1; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + pePlusMetal->forbidden &= inv_forbidden_edge_mask; + peMinusO->forbidden &= inv_forbidden_edge_mask; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) /*&& nDeltaCharge > 0*/ ) { + /* (+)charge was just moved, no change in number of charges */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + (*pnNumRunBNS) ++; + cur_success ++; /* 01 */ + } + } else { + pe->flow += delta; /* roll back */ + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } else + if ( NO_VERTEX != (vPlusMinus = GetPlusMinusVertex( pBNS, pTCGroups, 1, 1 ) ) ) { + /* manually add (-) charge to O and (+) charge to metal */ + /* decrement bond order to 0 */ + /*---------------------------------------------------------------------------*/ + /* */ + /* (+/-)* (+/-) Result: */ + /* | || */ + /* | || - Added (+) to M */ + /* (+)super (+)super - Incremented bond M-O */ + /* || | */ + /* || => | To make this attachment H, */ + /* (Y) (Y) increment */ + /* | || pTCGroups->pTCG[itg].tg_num_H */ + /* | || */ + /* (+)metal (+)hetero Technical details: */ + /* \\ \ increase capacities of */ + /* M M(+) edges to (+/-) otherwise */ + /* | || flow may not be able to */ + /* -O* -O-O increase */ + /* */ + /* After that change M=O bond order from 2 to 0 */ + /*---------------------------------------------------------------------------*/ + int i1, j1, k1; + delta = 1; + pvO = pBNS->vert + n; + pvM = pBNS->vert + i; + /* Increment st_edge.cap on (+/-) vertex */ + pBNS->vert[vPlusMinus].st_edge.cap += delta; + /* Increment st_edge.cap on O */ + pvO->st_edge.cap += delta; + /* increment cap on M-O edge */ + pe->cap += delta; + /* total cap count */ + pBNS->tot_st_cap += 2*delta; + + v1 = vPlusMinus; + v2 = n; /* atom O */ + + /* increase capacities of edges to Y */ + for ( i1 = 0; i1 < pBNS->vert[vPlusMinus].num_adj_edges; i1 ++ ) { + j1 = pBNS->edge[pBNS->vert[vPlusMinus].iedge[i1]].neighbor12 ^ vPlusMinus; + for ( k1 = 0; k1 < pBNS->vert[j1].num_adj_edges; k1 ++ ) { + pBNS->edge[pBNS->vert[j1].iedge[k1]].cap += delta; + } + } + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + pePlusMetal->forbidden &= inv_forbidden_edge_mask; + pe->forbidden &= inv_forbidden_edge_mask; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + cur_success = 0; + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) /*&& nDeltaCharge == 1*/ ) { + /* Added (+)charge to -N< => nDeltaCharge == 1 */ + /* Flow change on pe (-)charge edge (atom B-O(-)) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + (*pnNumRunBNS) ++; + cur_success ++; /* 01 */ + } + } + if ( cur_success ) { + /* set bond M=O order = 0 */ + if ( pe->flow != 2*delta ) { + ret = RI_ERR_PROGR; + goto exit_function; + } + /* reduce pe bond order by 2*delta */ + pe->flow -= 2*delta; + pvO->st_edge.cap -= 2*delta; + pvO->st_edge.flow -= 2*delta; + pvM->st_edge.flow -= 2*delta; + pvM->st_edge.cap -= 2*delta; + pBNS->tot_st_cap -= 3*delta; + pBNS->tot_st_flow -= 4*delta; + /* fix M-O bond order to zero */ + pe->cap -= 2*delta; + /* add fixed (-) charge to O */ + pVA[n].cInitCharge -= delta; + } else { + /* failed */ + pBNS->vert[vPlusMinus].st_edge.cap -= delta; + pvO->st_edge.cap -= delta; + /*pTCGroups->pTCG[itg].edges_cap -= delta;*/ /* ???bug??? - commented out 2006-03-22 */ + pBNS->tot_st_cap -= 2*delta; + /* decrease capacities of edges to Y */ + for ( i1 = 0; i1 < pBNS->vert[vPlusMinus].num_adj_edges; i1 ++ ) { + j1 = pBNS->edge[pBNS->vert[vPlusMinus].iedge[i1]].neighbor12 ^ vPlusMinus; + for ( k1 = 0; k1 < pBNS->vert[j1].num_adj_edges; k1 ++ ) { + pBNS->edge[pBNS->vert[j1].iedge[k1]].cap -= delta; + } + } + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } + } + } + } + +exit_function: + AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); + return ret; +} +#if ( KEEP_METAL_EDGE_FLOW == 1 ) +/****************************************************************************/ +int ForbidMetalCarbonEdges( BN_STRUCT *pBNS, inp_ATOM *at, int num_at, VAL_AT *pVA, + ALL_TC_GROUPS *pTCGroups, EDGE_LIST *pMetalCarbonEdges, int forbidden_edge_mask ) +{ + + int i, j, neigh, nNumEdgeMetalCarbon = 0, pass = 0, ret = 0; + BNS_VERTEX *pVert, *pNeigh; + BNS_EDGE *pEdge; + + /* count carbon-metal edges */ + + if ( pTCGroups->num_metal_atoms ) { +fill_ForbiddenEdgesMetalCarbon: + for ( i = 0; i < num_at; i ++ ) { + if ( pVA[i].cMetal && pVA[i].cNumBondsToMetal ) { + pVert = pBNS->vert + i; + for ( j = 0; j < pVert->num_adj_edges; j ++ ) { + pEdge = pBNS->edge + pVert->iedge[j]; + neigh = pEdge->neighbor12 ^ i; + pNeigh = pBNS->vert + neigh; + if ( !IS_BNS_VT_ATOM(pNeigh->type) ) + continue; + if ( at[neigh].endpoint ) + continue; + if ( pVA[neigh].cNumValenceElectrons == 4 && pVA[neigh].cPeriodicRowNumber == 1 && + pNeigh->st_edge.cap >= at[neigh].valence+1 ) { + if ( pass ) { + if ( ret = AddToEdgeList( pMetalCarbonEdges, pVert->iedge[j], 0 ) ) { + goto exit_function; + } + pEdge->forbidden |= forbidden_edge_mask; + } else { + nNumEdgeMetalCarbon ++; + } + } + } + } + } + if ( !pass && nNumEdgeMetalCarbon ) { + if ( ret = AllocEdgeList( pMetalCarbonEdges, nNumEdgeMetalCarbon ) ) { + goto exit_function; + } + pass ++; + goto fill_ForbiddenEdgesMetalCarbon; + } + } +exit_function: + return ret; +} + + + +#endif + + + +/****************************************************************************/ +int RunBnsRestore1( CANON_GLOBALS *pCG, INCHI_CLOCK *ic, ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, + StrFromINChI *pStruct, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, INChI *pInChI[], + long num_inp, int bHasSomeFixedH ) +{ + int nNumRunBNS = 0; + + EDGE_LIST CarbonChargeEdges, MetalCarbonEdges, Nplus2BondsEdges; + + int nTotalDelta = 0, ret = 0, tot_num_fixes; + inp_ATOM *at = pStruct->at; + inp_ATOM *at2 = NULL; /* restored structure */ + inp_ATOM *at3 = NULL; /* structure for calculating one InChI */ + int num_at = pStruct->num_atoms; + int num_deleted_H = pStruct->num_deleted_H; +#ifdef _DEBUG + int ret2; +#endif + +#if ( KEEP_METAL_EDGE_FLOW == 1 ) + BNS_VERTEX *pVert, *pNeigh; + int j, neigh; +#endif + + /* Edge lists initialization */ + AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &MetalCarbonEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &Nplus2BondsEdges, EDGE_LIST_CLEAR ); + + if ( pStruct->iMobileH == TAUT_NON && + ( ret = FillOutExtraFixedHDataInChI( pStruct, pInChI ) ) ) { + goto exit_function; + } + + if ( !at2 && !(at2 = (inp_ATOM *) inchi_malloc((num_at + num_deleted_H)*sizeof(at2[0]))) || + !at3 && !(at3 = (inp_ATOM *) inchi_malloc((num_at + num_deleted_H)*sizeof(at3[0])))) { + return RI_ERR_ALLOC; + } + + if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, BNS_EDGE_FORBIDDEN_TEMP ))) { + goto exit_function; + } + +#if ( KEEP_METAL_EDGE_FLOW == 1 ) + /* count edges of -C(IV)< carbons connected to metals */ + if ( 0 > (ret = ForbidMetalCarbonEdges( pBNS, at, num_at, pVA, pTCGroups, &MetalCarbonEdges, BNS_EDGE_FORBIDDEN_TEMP ))) { + goto exit_function; + } +#endif + if ( 0 > (ret = ForbidNintrogenPlus2BondsInSmallRings( pBNS, at, num_at, pVA, 6, + pTCGroups, &Nplus2BondsEdges, BNS_EDGE_FORBIDDEN_TEMP ) ) ) { + goto exit_function; + } + + /*********** Run BNS #1: no charge on carbons and =N= ***************/ + if ( Nplus2BondsEdges.num_edges ) { + /* Run BNS leaving carbon charges unchanged */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + nNumRunBNS ++; + if ( ret < 0 ) { + goto exit_function; + } else { + nTotalDelta += ret; + } + RemoveForbiddenEdgeMask( pBNS, &Nplus2BondsEdges, BNS_EDGE_FORBIDDEN_TEMP ); + AllocEdgeList( &Nplus2BondsEdges, EDGE_LIST_FREE ); + } +#ifdef _DEBUG + /* debug only */ + memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + pStruct->at = at; +#endif + /*************************** extend min ring size to 8 ****************************/ + if ( 0 > (ret = ForbidNintrogenPlus2BondsInSmallRings( pBNS, at, num_at, pVA, 8, + pTCGroups, &Nplus2BondsEdges, BNS_EDGE_FORBIDDEN_TEMP ) ) ) { + goto exit_function; + } + if ( Nplus2BondsEdges.num_edges ) { + /* Run BNS leaving carbon charges unchanged */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + nNumRunBNS ++; + if ( ret < 0 ) { + goto exit_function; + } else { + nTotalDelta += ret; + } + RemoveForbiddenEdgeMask( pBNS, &Nplus2BondsEdges, BNS_EDGE_FORBIDDEN_TEMP ); + AllocEdgeList( &Nplus2BondsEdges, EDGE_LIST_FREE ); + } +#ifdef _DEBUG + /* debug only */ + memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + pStruct->at = at; +#endif + /*******************************************************************/ + if ( CarbonChargeEdges.num_edges > 0 ) { + /* Run BNS leaving carbon charges unchanged */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + nNumRunBNS ++; + if ( ret < 0 ) { + goto exit_function; + } else { + nTotalDelta += ret; + } + RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, BNS_EDGE_FORBIDDEN_TEMP ); + AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); + } +#ifdef _DEBUG + /* debug only */ + memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + pStruct->at = at; +#endif + /*******************************************************************/ + if ( MetalCarbonEdges.num_edges > 0 ) { + /* Run BNS leaving carbon charges unchanged */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + nNumRunBNS ++; + if ( ret < 0 ) { + goto exit_function; + } else { + nTotalDelta += ret; + } + RemoveForbiddenEdgeMask( pBNS, &MetalCarbonEdges, BNS_EDGE_FORBIDDEN_TEMP ); + AllocEdgeList( &MetalCarbonEdges, EDGE_LIST_FREE ); + } + /*******************************************************************/ + /* Run BNS allowing to change any charges */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + nNumRunBNS ++; + if ( ret < 0 ) { + goto exit_function; + } else { + nTotalDelta += ret; + } +#ifdef _DEBUG + /* debug only */ + memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + pStruct->at = at; +#endif + +#if ( BNS_RAD_SEARCH == 1 ) + /****************************************************************************/ + /* move unfulfilled 'radicals' from ChargeStruct to atoms */ + /* and set change charges of affected atoms to fit total charge */ + ret = MoveRadToAtomsAddCharges( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, BNS_EDGE_FORBIDDEN_TEMP ); + if ( ret < 0 ) { + goto exit_function; + } +#endif + /**************************************************************/ + /**************************************************************/ + /***** fix restore inconsistencies *****/ + /**************************************************************/ + /**************************************************************/ +#ifdef _DEBUG + /* debug only */ + memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + pStruct->at = at; +#endif + + /* rearrange (+) and (-) edges flow so that there is no (+)flow=0 and (-)flow=1 */ + ret = RearrangePlusMinusEdgesFlow( pBNS, pBD, pVA, pTCGroups, BNS_EDGE_FORBIDDEN_TEMP ); + if ( ret < 0 ) { + goto exit_function; + } + + /*****************************************************************/ + /* Increment zero order metal bonds to heteroatoms */ + /*****************************************************************/ + ret = IncrementZeroOrderBondsToHeteroat( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, + &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); + if ( ret < 0 ) { + goto exit_function; + } + +#ifdef _DEBUG + /* debug only */ + memcpy( at2, at, (pStruct->num_atoms + pStruct->num_deleted_H)*sizeof(at2[0])); + pStruct->at = at2; + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + pStruct->at = at; +#endif + +#if (MOVE_CHARGES_FROM_HETEREO_TO_METAL == 1 ) + /*****************************************************************/ + /* move charges from heteroatoms to metal atoms */ + /*****************************************************************/ + ret = MoveChargeFromHeteroatomsToMetals( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, + &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); + if ( ret < 0 ) { + goto exit_function; + } +#endif + /*********************************************************************** + NH2 NH2 + \ \ + C==S(+)- => C(+)-S- where NH2 are not tautomeric + / / + NH2 NH2 + ************************************************************************/ + ret = MovePlusFromS2DiaminoCarbon( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, + &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); + if ( ret < 0 ) { + goto exit_function; + } + /*****************************************************************/ + /* Avoid charge separation on heteroatoms */ + /*****************************************************************/ + ret = EliminateChargeSeparationOnHeteroatoms( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, + &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP, 0); + if ( ret < 0 ) { + goto exit_function; + } + if ( ret ) { + /*charge separation remains; allow changes of stereobonds in a ring and try again */ + ret = EliminateChargeSeparationOnHeteroatoms( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, + &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP, + BNS_EDGE_FORBIDDEN_MASK); + if ( ret < 0 ) { + goto exit_function; + } + } + /*****************************************************************/ + /* convert N#N(+)-N= into N(-)=N(+)=N- */ + /*****************************************************************/ + ret = RestoreNNNgroup( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, + &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); + if ( ret < 0 ) { + goto exit_function; + } + /*****************************************************************/ + /* convert Metal(q)-N(-)-O(-) Metal(q-2)-N=O (local change) */ + /*****************************************************************/ + ret = FixMetal_Nminus_Ominus( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, + &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); + if ( ret < 0 ) { + goto exit_function; + } + /*****************************************************************/ + /* convert N(-)=C= into N#C- - */ + /*****************************************************************/ + ret = RestoreCyanoGroup( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, + &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); + if ( ret < 0 ) { + goto exit_function; + } + /*****************************************************************/ + /* convert C(+)#N(+)- into C(-)#N(+)- */ + /*****************************************************************/ + ret = RestoreIsoCyanoGroup( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, + &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); + if ( ret < 0 ) { + goto exit_function; + } + /*****************************************************************/ + /* eliminate =N(V)= if possible */ + /* | */ + /*****************************************************************/ + ret = EliminateNitrogen5Val3Bonds(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, + &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); + if ( ret < 0 ) { + goto exit_function; + } + + /*****************************************************************/ + /* | | */ + /* convert -S- to =S= if possible */ + /* | | */ + /*****************************************************************/ + ret = Convert_SIV_to_SVI(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, + &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); + if ( ret < 0 ) { + goto exit_function; + } + + /*****************************************************************/ + /* =N(+)=O =N-O(-) */ + /* convert => if possible */ + /* Metal(q) Metal(q+2) */ + /*****************************************************************/ + ret = PlusFromDB_N_DB_O_to_Metal(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, + &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); + if ( ret < 0 ) { + goto exit_function; + } + + /*****************************************************************/ + /* forbidden edges prevents required in InChI tautomerism */ + /* incorrectly restored mobile H mix separate tautomeric groups */ + /* because an edge may not become forbidden */ + /* note: removes this 'forbidden_edge' bit from ALL edges */ + /*****************************************************************/ + ret = MoveMobileHToAvoidFixedBonds( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, + &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); + + if ( ret < 0 ) { + goto exit_function; + } + /**************************************************************************/ + /* 2. Mobile H endpoint has radical on it (typical for wrong P(VI)(=O)3OH */ + tot_num_fixes = 0; + if ( pStruct->iMobileH==TAUT_NON ) { + ret = RemoveRadFromMobileHEndpointFixH( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, + &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); + } else { + ret = RemoveRadFromMobileHEndpoint( pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, + &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); + } + if ( ret < 0 ) { + goto exit_function; + } + tot_num_fixes += ret; + /**************************************************************/ + /* make bonds between a charged heteroatom and a metal single */ + ret = MakeSingleBondsMetal2ChargedHeteroat(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, + &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); + if ( ret < 0 ) { + goto exit_function; + } + /**************************************************************/ + /* move (+) charges to >N- and other centerpoints */ + ret = MoveChargeToMakeCenerpoints(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, + &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); + if ( ret < 0 ) { + goto exit_function; + } + + /**************************************************************************/ + /* Find and eliminate false Mobile-H groups: Cl(=O)3(-O(-)) => Cl(-)(=O)4 */ + ret = MoveChargeToRemoveCenerpoints(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, + &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); + if ( ret < 0 ) { + goto exit_function; + } + /**************************************************************************/ + /* Find A=X< where all bonds to X except A=X are marked as stereogenic */ + /* make bonds A=X single */ + ret = CheckAndRefixStereobonds(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, + &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); + if ( ret < 0 ) { + goto exit_function; + } + /**************************************************************************/ + /* In Reconnected structure change 'salt bonds' to 'coordination bonds */ + /* for example, M-O-C= -> M(+)-O(-)-C= */ + /* Defect: instead of NH2-C=O(+)-M it will restore NH2(+)=C-O(-)-M(+) */ + /* However, in this release metal-organic compounds do not get much care */ + ret = SaltBondsToCoordBonds(pBNS, pBD, pStruct, at, at2, pVA, pTCGroups, + &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP); + if ( ret < 0 ) { + goto exit_function; + } + /**************************************************************************/ + /* Normalize the structure and compare t-groups and stereobonds */ + ret = NormalizeAndCompare( pCG, ic, ip, sd, pBNS, pBD, pStruct, at, at2, at3, pVA, pTCGroups, pInChI, num_inp, bHasSomeFixedH, + &nNumRunBNS, &nTotalDelta, BNS_EDGE_FORBIDDEN_TEMP, BNS_EDGE_FORBIDDEN_MASK); + if ( ret < 0 ) { + goto exit_function; + } + /**************************************************************************/ + /* Create InChI out of the restored structure */ + + + /*ret = nTotalDelta;*/ + +exit_function: + pStruct->at = at; + pStruct->at2 = at2; + at2 = NULL; + AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE ); + AllocEdgeList( &MetalCarbonEdges, EDGE_LIST_FREE ); + AllocEdgeList( &Nplus2BondsEdges, EDGE_LIST_FREE ); + if ( at2 ) { + inchi_free( at2 ); + } + if ( at3 ) { + inchi_free( at3 ); + } + + return ret; +} + +/****************************************************************************/ +int RestoreAtomMakeBNS( INCHI_CLOCK *ic, CANON_GLOBALS *pCG, + ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, + StrFromINChI *pStruct, int iComponent, + int iAtNoOffset, INChI *pInChI[], + const char *szCurHdr, long num_inp, + int bHasSomeFixedH ) +{ + int i, j, ret = 0, ret2; + /*int nDelta, nTotalDelta;*/ + VAL_AT *pVA = NULL; + VAL_AT va1; + int num_at = pStruct->num_atoms; + inp_ATOM *at = pStruct->at; + ALL_TC_GROUPS TCGroups; + ALL_TC_GROUPS *pTCGroups = &TCGroups; + int nAddEdges2eachAtom = 2, nAddVertices = 0; + + BFS_Q bfsq; + + /* BNS creation */ + BN_STRUCT *pBNS = NULL; + BN_DATA *pBD = NULL; + int nNum_changed_bonds = 0; + int bTreatMoreAtomsAsMetals = 0, bSecondPassNewMetals=0; + int nMaxAddAtoms = 2, nMaxAddEdges = 2, max_altp = BN_MAX_ALTP; + + memset( pTCGroups, 0, sizeof(pTCGroups[0]) ); + for ( i = 0; i < NUM_TCGROUP_TYPES; i ++ ) { + pTCGroups->nGroup[i] = TCG_None; /* unassigned */ + } + pTCGroups->iComponent = iComponent; + pTCGroups->iAtNoOffset = iAtNoOffset; + + if ( num_at == 1 ) { + /* single atom -- no bonds to restore */ + inp_ATOM *at2 = (inp_ATOM *) inchi_malloc(sizeof(at2[0])*(pStruct->num_atoms+pStruct->num_deleted_H)); + inp_ATOM *at3 = (inp_ATOM *) inchi_malloc(sizeof(at3[0])*(pStruct->num_atoms+pStruct->num_deleted_H)); + pStruct->at2 = at2; + at[0].charge = pInChI[0]->nTotalCharge; + if ( at2 ) { + memcpy( at2, at, sizeof(at2[0])*(pStruct->num_atoms+pStruct->num_deleted_H)); + } + if ( !at2 || !at3 ) { + if ( at3 ) inchi_free( at3 ); + return RI_ERR_ALLOC; + } + ret = MakeOneInChIOutOfStrFromINChI( pCG, ic, ip, sd, pStruct, pStruct->at2, at3, pTCGroups ); + /* clean up */ + for( i = 0; i < TAUT_NUM; i ++ ) { + Free_INChI( &pStruct->pOneINChI[i] ); + Free_INChI_Aux( &pStruct->pOneINChI_Aux[i] ); + FreeInpAtomData( pStruct->pOne_norm_data[i] ); + if ( pStruct->pOne_norm_data[i] ) { + inchi_free( pStruct->pOne_norm_data[i] ); + pStruct->pOne_norm_data[i] = NULL; + } + } + free_t_group_info( &pStruct->One_ti ); + inchi_free( at3 ); + + return ret; + } + + AllocBfsQueue( &bfsq, BFS_Q_CLEAR, 0 ); + if ( !(pVA = (VAL_AT *) inchi_calloc( num_at, sizeof( pVA[0] ) ) ) ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + pStruct->pVA = pVA; + memset( &va1, 0, sizeof(va1) ); + pTCGroups->total_charge = pInChI[0]->nTotalCharge; + if ( 0 > ( ret = AllocBfsQueue( &bfsq, num_at, 0 /* min ring size undefined */ ) ) ) { + goto exit_function; + } + pStruct->pbfsq = &bfsq; + + if ( pStruct->iMobileH == TAUT_NON && pInChI[1] && pInChI[1]->nNumberOfAtoms > 1 && + ( ret = FillOutpStructEndpointFromInChI( pInChI[1], &pStruct->endpoint )) ) { + goto exit_function; + } + + /* mark metal atoms; find min ring sizes for atoms that have 2 bonds */ + for ( i = 0; i < num_at; i ++ ) { + pVA[i].cNumValenceElectrons = get_sp_element_type( at[i].el_number, &j ); + pVA[i].cPeriodicRowNumber = j; + pVA[i].cPeriodicNumber = at[i].el_number; + pVA[i].cNumValenceElectrons --; /* = -1 d- and f- metals, 0 for H, 1 for Na, 2 for Mg,.. = (ATYPE_Xx-1) */ + + if ( is_el_a_metal( at[i].el_number ) ) { + if ( pStruct->pSrm->bStereoRemovesMetalFlag ) { + /* treat metal as non-metal if it is stereogenic or has a stereobond */ + pVA[i].cMetal = !( at[i].p_parity || at[i].sb_parity[0] ); + } else { + pVA[i].cMetal = 1; + } + } + if ( at[i].valence == 2 && !at[i].num_H ) { + pVA[i].cMinRingSize = is_bond_in_Nmax_memb_ring( at, i, 0, bfsq.q, bfsq.nAtomLevel, + bfsq.cSource, 99 /* max ring size */ ); + } else { + pVA[i].cMinRingSize = 0; + } + } + /* AllocBfsQueue( &bfsq, BFS_Q_FREE, 0 ); */ + +repeat_for_new_metals: + /* set valences for the first time; find ChargeValence structures for each atom */ + for ( i = 0; i < num_at; i ++ ) { + /* get additional fictitious atoms information */ + pVA[i].cInitFreeValences = 0; + + ret = GetAtomRestoreInfo( pCG, at, i, pVA, pStruct->pSrm, pStruct->bMobileH, pStruct->endpoint ); + + if ( ret < 0 ) { + goto exit_function; + } + if ( ret == TREAT_ATOM_AS_METAL && !bSecondPassNewMetals && !pVA[i].cMetal ) { + if ( pStruct->pSrm->bStereoRemovesMetalFlag ) { + /* treat metal as non-metal if it is stereogenic or has a stereobond */ + pVA[i].cMetal = !( at[i].p_parity || at[i].sb_parity[0] ); + } else { + pVA[i].cMetal = 1; + } + if ( pVA[i].cMetal ) { + bTreatMoreAtomsAsMetals ++; + } + } + pTCGroups->charge_on_atoms += pVA[i].cInitCharge; + } + if ( bTreatMoreAtomsAsMetals && !bSecondPassNewMetals ) { + for ( i = 0; i < num_at; i ++ ) { + /* clear all members of pVA[i] except two */ + pTCGroups->charge_on_atoms -= pVA[i].cInitCharge; + va1.cMetal = pVA[i].cMetal; + va1.cMinRingSize = pVA[i].cMinRingSize; + va1.cNumValenceElectrons = pVA[i].cNumValenceElectrons; + va1.cPeriodicRowNumber = pVA[i].cPeriodicRowNumber; + va1.cPeriodicNumber = pVA[i].cPeriodicNumber; + pVA[i] = va1; + } + bSecondPassNewMetals = 1; + goto repeat_for_new_metals; + } + + /* count atoms, bonds, additional edges and vertices in ChargeValence structures and t-groups */ + ret = nCountBnsSizes( at, num_at, nAddEdges2eachAtom, nAddVertices, &pStruct->ti, + pVA, pStruct->pSrm, pTCGroups ); + if ( ret < 0 ) { + goto exit_function; + } + + /* find and count groups; add counts of all other vertices to be created */ + ret = nAddSuperCGroups( pTCGroups ); + if ( ret < 0 ) { + goto exit_function; + } + + /* create the BNS and fill it with all real atoms */ + pBNS = AllocateAndInitTCGBnStruct( pStruct, pVA, pTCGroups, + nMaxAddAtoms, nMaxAddEdges, max_altp, &nNum_changed_bonds ); + if ( !pBNS ) { + ret = BNS_OUT_OF_RAM; + goto exit_function; + } + /* add t-groups to the BNS */ + ret = AddTGroups2TCGBnStruct( pBNS, pStruct, pVA, pTCGroups, nMaxAddEdges ); + if ( ret < 0 ) { + goto exit_function; + } + + /* add c-groups to the BNS; adjust charges */ + ret = AddCGroups2TCGBnStruct( pBNS, pStruct, pVA, pTCGroups, nMaxAddEdges ); + if ( ret < 0 ) { + goto exit_function; + } + + pBNS->ulTimeOutTime = NULL; /* TODO: Correct this. ulTimeOutTime;*/ /* v. 1.05 */ + pBNS->ic = ic; /* v. 1.05 */ + + /* allocate BNData */ + pBD = AllocateAndInitBnData( pBNS->max_vertices + pBNS->max_vertices/2 ); + if ( !pBD ) { + ret = BNS_OUT_OF_RAM; + goto exit_function; + } + CheckBnsConsistency( pStruct, pBNS, pVA, pTCGroups, 0 ); + + /* restore bonds & charges */ + ret = RunBnsRestore1( pCG, ic, ip, sd, pBNS, pBD, pStruct, pVA, pTCGroups, pInChI, num_inp, bHasSomeFixedH ); + if ( ret < 0 ) { + goto exit_function; + } + + ret = CheckBnsConsistency( pStruct, pBNS, pVA, pTCGroups, 1 ); +#if ( bRELEASE_VERSION == 0 ) +#ifndef TARGET_API_LIB + if ( ret ) { + fprintf( stdout, "Msg for: %ld %s comp=%d %c%c\n", num_inp, (szCurHdr && szCurHdr[0])? szCurHdr : "", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F' ); + } + if ( pStruct->iMobileH == TAUT_YES && pStruct->nNumRemovedProtons ) { + fprintf( stdout, "REMOVED_PROTONS%+d %ld %s\n", pStruct->nNumRemovedProtons, num_inp, (szCurHdr && szCurHdr[0])? szCurHdr : "" ); + /*pStruct->bExtract |= EXTRACT_STRUCT_NUMBER;*/ + } + if ( pStruct->bExtract & EXTRACT_STRUCT_NUMBER ) { + fprintf( stdout, "EXTRACT: %ld: %s\n", num_inp, (szCurHdr && szCurHdr[0])? szCurHdr : "" ); + } +#endif +#endif + { /* create the final structure in pStruct->at2 */ + inp_ATOM *at_tmp = pStruct->at; + pStruct->at = pStruct->at2; + memcpy( pStruct->at, at_tmp, sizeof(pStruct->at[0])*(pStruct->num_atoms + pStruct->num_deleted_H) ); + ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 ); + pStruct->at2 = pStruct->at; + pStruct->at = at_tmp; + if ( ret2 < 0 ) { + ret = ret2; + } + } + +exit_function: + + pStruct->pbfsq = NULL; + AllocBfsQueue( &bfsq, BFS_Q_FREE, 0 ); + + pBD = DeAllocateBnData( pBD ); + pBNS = DeAllocateBnStruct( pBNS ); + /* + if ( pVA ) inchi_free( pVA ); + */ + if ( pTCGroups->pTCG ) inchi_free( pTCGroups->pTCG ); + + return ret; +} + + +/****************************************************************************/ +int OneInChI2Atom( INCHI_CLOCK *ic, CANON_GLOBALS *pCG, ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd, const char *szCurHdr, long num_inp, + StrFromINChI *pStruct, int iComponent, int iAtNoOffset, int bHasSomeFixedH, INChI *pInChI[]) +{ + int ret; + INPUT_PARMS *ip, ip_loc; + + ip_loc = *ip_inp; + ip = &ip_loc; + + sd->pStrErrStruct[0] = '\0'; + ret = RestoreAtomConnectionsSetStereo( pStruct, iComponent, iAtNoOffset, pInChI[0], pInChI[1]); + if ( ret < 0 ) { + goto exit_function; + } + ret = SetStereoBondTypesFrom0DStereo( pStruct, pInChI[0]); + if ( ret < 0 ) { + goto exit_function; + } + ret = ReconcileAllCmlBondParities( pStruct->at, pStruct->num_atoms, 0 ); + if ( ret < 0 ) { + goto exit_function; + } + + /* main InChI restore function */ + ret = RestoreAtomMakeBNS( ic, pCG, ip, sd, pStruct, iComponent, iAtNoOffset, pInChI, szCurHdr, num_inp, bHasSomeFixedH ); + +#ifndef COMPILE_ANSI_ONLY + if ( (pStruct->num_inp_actual>0? pStruct->num_inp_actual : num_inp) >= ip->first_struct_number && + ( (/*ret > 0 &&*/ ip->bDisplayIfRestoreWarnings ) && pStruct->pXYZ ) ) { + inchiTime ulTStart; + InchiTimeGet( &ulTStart ); + DisplayRestoredComponent( pCG, pStruct, iComponent, iAtNoOffset, pInChI[0], szCurHdr ); + sd->ulStructTime -= InchiTimeElapsed( ic, &ulTStart ); /* subtract display time */ + } +#endif + + if ( ret < 0 ) { + goto exit_function; + } + if ( (pStruct->num_inp_actual? pStruct->num_inp_actual: num_inp) >= ip->first_struct_number && ret >= 0 ) { + /* remove t-group markings and increment zero-order bonds, + otherwise MakeInChIOutOfStrFromINChI2() woild fail */ + /* --- moved to MakeInChIOutOfStrFromINChI2 --- + IncrZeroBondsAndClearEndpts(pStruct->at2, pStruct->num_atoms, iComponent+1); + CopySt2At( pStruct->at2, pStruct->st, pStruct->num_atoms ); + */ + /* include all restored structure features in pStruct->at2 */ + /* make full InChI out of pStruct->at2, pStruct->num_atoms */ + /***************************************************************************************/ + /* !!! pStruct->One_InChI etc. were removed at the exit from NormalizeAndCompare() !!! */ + /***************************************************************************************/ + if ( bHasSomeFixedH && pStruct->iInchiRec == INCHI_REC && pStruct->iMobileH == TAUT_YES && + !pStruct->bFixedHExists && !(ip->nMode & REQ_MODE_BASIC) ) { + /* reconnected components without Fixed-H layer may produce 'tautomeric' fragments like Cl(-) */ + ip->nMode |= REQ_MODE_BASIC; + } + + ret = MakeInChIOutOfStrFromINChI2( ic, pCG, ip, sd, pStruct, iComponent, iAtNoOffset, num_inp ); + + if ( ret >= 0 ) { + ; + } +#if ( bRELEASE_VERSION == 0 ) +#ifndef TARGET_API_LIB + else { + fprintf( stdout, "\nERROR in MakeInChI-1: %ld %s Comp:%d %c%c Err:%d\n", num_inp, + szCurHdr? szCurHdr: "???", iComponent, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F', ret); + } +#endif +#endif + } + + +exit_function: + return ret; +} + + +/********************************************************************************************/ +int MakeProtonComponent( StrFromINChI *pStruct, int iComponent, int num_prot ) +{ + inp_ATOM *at = NULL; + int i; + + if ( num_prot <= 0 ) { + return 0; + } + /* allocate */ + pStruct->at = (inp_ATOM *) inchi_calloc( num_prot, sizeof(pStruct->at[0]) ); + pStruct->at2 = (inp_ATOM *) inchi_calloc( num_prot, sizeof(pStruct->at2[0]) ); + if ( !pStruct->at || !pStruct->at2 ) { + return 0; + } + /* create protons */ + at = pStruct->at; + /* fill out proton atom info */ + for ( i = 0; i < num_prot; i ++ ) { + strcpy( at[i].elname, "H" ); + at[i].el_number = EL_NUMBER_H; + at[i].orig_at_number = i+1; + /* + at[i].orig_compt_at_numb = i + 1; + at[i].component = i + 1; + */ + at[i].charge = 1; + } + memcpy( pStruct->at2, at, num_prot * sizeof(pStruct->at2[0]) ); + pStruct->bDeleted = 0; + pStruct->num_atoms = num_prot; + pStruct->bMobileH = TAUT_YES; + pStruct->iMobileH = TAUT_YES; + return num_prot; +} + + +/********************************************************************************************/ +int AddRemProtonsInRestrStruct( INCHI_CLOCK *ic, CANON_GLOBALS *pCG, ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd, long num_inp, + int bHasSomeFixedH, + StrFromINChI *pStruct, int num_components, + StrFromINChI *pStructR, int num_componentsR, + NUM_H *nProtonsToBeRemovedByNormFromRevrs, int *recmet_change_balance ) +{ + /* on entry and exit, all at[i].num_H do not include isotopic H and explicit terminal H are connected */ + int iComp, q, ret = 0; + int num_atoms, tot_num_at, num_deleted_H, num_tg, num_changed, num_deleted_components; + inp_ATOM *at; + INPUT_PARMS *ip, ip_loc; + int num_prot = *nProtonsToBeRemovedByNormFromRevrs; + int delta_recmet_prot, num_prot_prev, bAccumulateChanges=0, nNumProtAddedByRevrs; + INChI_Aux *pINChI_Aux; + INCHI_MODE bNormalizationFlags; + int nChargeRevrs, nChargeInChI; + + if ( !num_prot ) { + return 0; + } + delta_recmet_prot = 0; + num_changed = 0; + num_deleted_components = 0; + ip_loc = *ip_inp; + ip = &ip_loc; + /*---------------------------------------------------------------------------------- + nLink < 0 && num_componentsR > 0 => This is a Disconnected structure component; it is + same as already processed reconnected one + Do no preicess it + + nLink > 0 && num_componentsR > 0 => This is a Disconnected structure component; + (should not happen) It it is a result of (nLink-1)th Reconeected + component disconnection (NOT IMPLEMENTED YET) + + nLink = 0 => Process this component. It is either a reconnected + component, or a result of a disconnection (for now) + + nLink > 0 && num_componentsR = 0 => This is a Reconnected component that is same as + a disconnected one that will not be processed. + Process and save charge delta. + -----------------------------------------------------------------------------------*/ + + for ( iComp = 0; iComp < num_components && num_prot; iComp ++ ) { + bAccumulateChanges = 0; + if ( pStruct[iComp].nLink < 0 && num_componentsR > 0 ) { + /* check */ + q = -(pStruct[iComp].nLink+1); + if ( !pStructR || !num_componentsR || q >= num_componentsR || pStructR[q].nLink != (iComp+1) ) { + ret = RI_ERR_PROGR; + goto exit_function; + } + continue; /* Disconnected structure component has already been processed as a Reconnected one */ + } + + at = pStruct[iComp].at2; + num_atoms = pStruct[iComp].num_atoms; + tot_num_at = pStruct[iComp].num_atoms+(num_deleted_H=pStruct[iComp].num_deleted_H); + bAccumulateChanges = ( pStruct[iComp].nLink > 0 && !num_componentsR ); + nChargeRevrs = pStruct[iComp].nChargeRevrs; + nChargeInChI = pStruct[iComp].nChargeInChI; + num_deleted_components += (0 != pStruct[iComp].bDeleted); + if ( !at || !num_atoms ) { + continue; + } + /* find whether it is a reconnected structure */ + q = bRevInchiComponentExists( pStruct+iComp, INCHI_REC, TAUT_YES, 0 )? INCHI_REC : INCHI_BAS; + /* + q = pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC] && + pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC][0][TAUT_YES] && + pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC][0][TAUT_YES]->nNumberOfAtoms? INCHI_REC : INCHI_BAS; + */ + pINChI_Aux = pStruct[iComp].RevInChI.pINChI_Aux[q][0][TAUT_YES]; /* 0 = 1st component in RevInChI */ + /*nNumProtAddedByRevrs = pINChI_Aux->nNumRemovedProtons;*/ + nNumProtAddedByRevrs = -pStruct[iComp].nNumRemovedProtonsByRevrs; + bNormalizationFlags = pINChI_Aux->bNormalizationFlags; + num_tg = pINChI_Aux->nNumberOfTGroups; + + + /* disconnect all explicit H and add the number of implicit iso H and all explicit terminal H to the number of implicit H */ + if ( 0 > ( ret = DisconnectedConnectedH( at, num_atoms, num_deleted_H ) ) ) { + goto exit_function; + } + num_prot_prev = num_prot; + ret = AddRemoveProtonsRestr( at, num_atoms, &num_prot, nNumProtAddedByRevrs, + bNormalizationFlags, num_tg, nChargeRevrs, nChargeInChI ); + + pStruct[iComp].bPostProcessed = ret; + num_changed += (ret > 0); + if ( ret < 0 ) { + goto exit_function; + } + if ( ret > 0 ) { + /* recalculate InChI; it will reconnect at */ + StrFromINChI *pStruct1 = pStruct + iComp; + INCHI_MODE nMode = ip->nMode; + FreeAllINChIArrays( pStruct1->RevInChI.pINChI, + pStruct1->RevInChI.pINChI_Aux, + pStruct1->RevInChI.num_components ); + + if ( bHasSomeFixedH && pStruct1->iInchiRec == INCHI_REC && pStruct1->iMobileH == TAUT_YES && + !pStruct1->bFixedHExists && !(ip->nMode & REQ_MODE_BASIC) ) { + /* reconnected components without Fixed-H layer may produce 'tautomeric' fragments like Cl(-) */ + ip->nMode |= REQ_MODE_BASIC; + } + /* calls ConnectDisconnectedH(...): subtracts number of implicit iso H from implicit H */ + + ret = MakeInChIOutOfStrFromINChI2( ic, pCG, ip, sd, pStruct1, 0, 0, num_inp ); + + ip->nMode = nMode; + if ( ret < 0 ) { + goto exit_function; + } + } else { + /* reconnect disconnected terminal H and subtracts number of implicit iso H from implicit H */ + if ( 0 > ( ret = ConnectDisconnectedH( at, num_atoms, num_deleted_H ) ) ) { + goto exit_function; + } + } + if ( bAccumulateChanges && recmet_change_balance ) { + /* processed Reconnected layer component that is also present in Disconnected layer */ + delta_recmet_prot += num_prot - num_prot_prev; + } + } + + iComp = num_components-1; + if ( !bHasSomeFixedH && num_prot > 0 && 1 == num_deleted_components && iComp >= 0 && pStruct[iComp].bDeleted ) { + /* add bare protons to the deleted Mobile-H component; undelete the component */ + num_prot_prev = num_prot; + if ( !MakeProtonComponent( pStruct+iComp, iComp, num_prot ) ) { + goto exit_function; + } else { + /* recalculate InChI; it will reconnect at */ + StrFromINChI *pStruct1 = pStruct + iComp; + INCHI_MODE nMode = ip->nMode; + num_changed ++; + num_prot = 0; + FreeAllINChIArrays( pStruct1->RevInChI.pINChI, + pStruct1->RevInChI.pINChI_Aux, + pStruct1->RevInChI.num_components ); + + if ( bHasSomeFixedH && pStruct1->iInchiRec == INCHI_REC && pStruct1->iMobileH == TAUT_YES && + !pStruct1->bFixedHExists && !(ip->nMode & REQ_MODE_BASIC) ) { + /* reconnected components without Fixed-H layer may produce 'tautomeric' fragments like Cl(-) */ + ip->nMode |= REQ_MODE_BASIC; + } + /* Although MakeInChIOutOfStrFromINChI2() calls ConnectDisconnectedH(...) */ + /* to subtracts number of implicit iso H from implicit H */ + /* this CANNOT have any effect on the deleted H component */ + + ret = MakeInChIOutOfStrFromINChI2( ic, pCG, ip, sd, pStruct1, 0, 0, num_inp ); + + ip->nMode = nMode; + if ( ret < 0 ) { + goto exit_function; + } + if ( bAccumulateChanges && recmet_change_balance ) { + /* processed Reconnected layer component that is also present in Disconnected layer */ + delta_recmet_prot += num_prot - num_prot_prev; + } + } + } + *nProtonsToBeRemovedByNormFromRevrs = num_prot; + if ( recmet_change_balance ) { + *recmet_change_balance = delta_recmet_prot; + } + +exit_function: + return ret < 0? ret : num_changed; +} + + +/**********************************************************************************/ +int AddRemIsoProtonsInRestrStruct( INCHI_CLOCK *ic, CANON_GLOBALS *pCG, ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd, long num_inp, int bHasSomeFixedH, + StrFromINChI *pStruct, int num_components, + StrFromINChI *pStructR, int num_componentsR, + NUM_H pProtonBalance[], NUM_H recmet_change_balance[] ) +{ /* on entry and exit, all at[i].num_H do not include isotopic H and explicit terminal H are connected */ + int iComp, q, k, ret = 0, bNotEmpty; + int num_atoms, tot_num_at, num_deleted_H, num_tg, num_changed; + inp_ATOM *at; + NUM_H num_prot[NUM_H_ISOTOPES], delta_recmet_prot[NUM_H_ISOTOPES], num_prot_prev[NUM_H_ISOTOPES]; + int bAccumulateChanges; + INChI_Aux *pINChI_Aux; + INChI *pINChI; + INCHI_MODE bNormalizationFlags; + INPUT_PARMS *ip, ip_loc; + + ip_loc = *ip_inp; + ip = &ip_loc; + + memcpy( num_prot, pProtonBalance, sizeof(num_prot) ); + for ( bNotEmpty=0, k = 0; k < NUM_H_ISOTOPES; k ++ ) { + bNotEmpty |= num_prot[k]; + } + if ( !bNotEmpty ) { + return 0; + } + memset ( delta_recmet_prot, 0, sizeof(delta_recmet_prot)); + num_changed = 0; + /*---------------------------------------------------------------------------------- + nLink < 0 && num_componentsR > 0 => This is a Disconnected structure component; it is + same as already processed reconnected one + Do no preicess it + + nLink > 0 && num_componentsR > 0 => This is a Disconnected structure component; + (should not happen) It it is a result of (nLink-1)th Reconeected + component disconnection (NOT IMPLEMENTED YET) + + nLink = 0 => Process this component. It is either a reconnected + component, or a result of a disconnection (for now) + + nLink > 0 && num_componentsR = 0 => This is a Reconnected component that is same as + a disconnected one that will not be processed. + Process and save charge delta. + -----------------------------------------------------------------------------------*/ + + for ( iComp = 0; iComp < num_components && num_prot; iComp ++ ) { + bAccumulateChanges = 0; + if ( pStruct[iComp].nLink < 0 && num_componentsR > 0 ) { + /* check */ + q = -(pStruct[iComp].nLink+1); + if ( !pStructR || !num_componentsR || q >= num_componentsR || pStructR[q].nLink != (iComp+1) ) { + ret = RI_ERR_PROGR; + goto exit_function; + } + continue; /* Disconnected structure component has already been processed as a Reconnected one */ + } + + at = pStruct[iComp].at2; + num_atoms = pStruct[iComp].num_atoms; + tot_num_at = pStruct[iComp].num_atoms+(num_deleted_H=pStruct[iComp].num_deleted_H); + bAccumulateChanges = ( pStruct[iComp].nLink > 0 && !num_componentsR ); + + if ( !at || !num_atoms ) { + continue; + } + /* find whether it is a reconnected structure */ + q = pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC] && + pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC][0][TAUT_YES] && + pStruct[iComp].RevInChI.pINChI_Aux[INCHI_REC][0][TAUT_YES]->nNumberOfAtoms? INCHI_REC : INCHI_BAS; + + pINChI_Aux = pStruct[iComp].RevInChI.pINChI_Aux[q][0][TAUT_YES]; /* 0 = 1st component in RevInChI */ + pINChI = pStruct[iComp].RevInChI.pINChI[q][0][TAUT_YES]; /* 0 = 1st component in RevInChI */ + bNormalizationFlags = pINChI_Aux->bNormalizationFlags; + num_tg = pINChI_Aux->nNumberOfTGroups; + memcpy( num_prot_prev, num_prot, sizeof(num_prot_prev) ); + + /* pass CONNECTED explicit H to AddRemoveIsoProtonsRestr() for isotopic H addition */ + ret = AddRemoveIsoProtonsRestr( at, num_atoms, num_prot, num_tg ); + + pStruct[iComp].bPostProcessed |= ret; + num_changed += (ret > 0); + if ( ret < 0 ) { + goto exit_function; + } + if ( ret > 0 ) { + StrFromINChI *pStruct1 = pStruct+iComp; + INCHI_MODE nMode = ip->nMode; + /* recalculate InChI; MakeInChIOutOfStrFromINChI2() will reconnect explicit H */ + /* disconnect all explicit H and add the number of implicit iso H and all explicit terminal H to the number of implicit H */ + if ( 0 > ( ret = DisconnectedConnectedH( at, num_atoms, num_deleted_H ) ) ) { + goto exit_function; + } + FreeAllINChIArrays( pStruct1->RevInChI.pINChI, + pStruct1->RevInChI.pINChI_Aux, + pStruct1->RevInChI.num_components ); + if ( bHasSomeFixedH && pStruct1->iInchiRec == INCHI_REC && pStruct1->iMobileH == TAUT_YES && + !pStruct1->bFixedHExists && !(ip->nMode & REQ_MODE_BASIC) ) { + /* reconnected components without Fixed-H layer may produce 'tautomeric' fragments like Cl(-) */ + ip->nMode |= REQ_MODE_BASIC; + } + /* input: disconnected explicit H, output: connected explicit H */ + ret = MakeInChIOutOfStrFromINChI2( ic, pCG, ip, sd, pStruct1, 0, 0, num_inp ); + ip->nMode = nMode; + if ( ret < 0 ) { + goto exit_function; + } + } + /* the following was commented out 2007-08-28 by DT. Reason: it's a bug since H must be already connected */ + /* else { + if ( 0 > ( ret = ConnectDisconnectedH( at, num_atoms, num_deleted_H ) ) ) { + goto exit_function; + } + } */ + if ( bAccumulateChanges ) { + /* processed Reconnected layer component that is also present in Disconnected layer */ + for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) { + delta_recmet_prot[k] += num_prot[k] - num_prot_prev[k]; + } + } + } + + memcpy ( pProtonBalance, num_prot, sizeof(num_prot) ); + if ( recmet_change_balance ) { + memcpy ( recmet_change_balance, delta_recmet_prot, sizeof(delta_recmet_prot) ); + } +exit_function: + return ret < 0? ret : num_changed; +} + +#endif diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichirvr5.c b/INCHI-1-SRC/INCHI_BASE/src/ichirvr5.c similarity index 94% rename from INCHI-1-SRC/INCHI_API/inchi_dll/ichirvr5.c rename to INCHI-1-SRC/INCHI_BASE/src/ichirvr5.c index 2c922ce..95da3f8 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichirvr5.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichirvr5.c @@ -1,1197 +1,1181 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - -/*#define CHECK_WIN32_VC_HEAP*/ -#include "mode.h" - -#if ( READ_INCHI_STRING == 1 ) - -#include "ichi.h" -#include "ichitime.h" - -#include "inpdef.h" -#include "ichimain.h" -#include "ichierr.h" -#include "incomdef.h" -#include "ichiring.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "util.h" - -#include "ichicomp.h" -#include "ichister.h" - -#include "ichi_bns.h" - -#include "strutil.h" - -#include "ichirvrs.h" - - -#define INC_ADD_EDGE 64 -/***********************************************************************************************/ -int GetPlusMinusVertex( BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups, int bCheckForbiddenPlus, int bCheckForbiddenMinus ) -{ - int k, ePlusSuper, eMinusSuper, vPlusSuper, vMinusSuper, vPlusMinus1 = NO_VERTEX, vPlusMinus2 = NO_VERTEX; - BNS_EDGE *pEdge; - if ( (k = pTCGroups->nGroup[TCG_Plus]) >= 0 && - (ePlusSuper = pTCGroups->pTCG[k].nForwardEdge) > 0 && - (vPlusSuper = pTCGroups->pTCG[k].nVertexNumber) >= pBNS->num_atoms && - !((pEdge=pBNS->edge + ePlusSuper)->forbidden && bCheckForbiddenPlus) ) { - - vPlusMinus1 = pEdge->neighbor12 ^ vPlusSuper; - } - if ( (k = pTCGroups->nGroup[TCG_Minus]) >= 0 && - (eMinusSuper = pTCGroups->pTCG[k].nForwardEdge) > 0 && - (vMinusSuper = pTCGroups->pTCG[k].nVertexNumber) >= pBNS->num_atoms && - !((pEdge=pBNS->edge + eMinusSuper)->forbidden && bCheckForbiddenMinus) ) { - - vPlusMinus2 = pEdge->neighbor12 ^ eMinusSuper; - } - if ( bCheckForbiddenPlus && NO_VERTEX == vPlusMinus1 || - bCheckForbiddenMinus && NO_VERTEX == vPlusMinus2 ) { - return NO_VERTEX; - } - return (NO_VERTEX != vPlusMinus1)? vPlusMinus1 : vPlusMinus2; -} -/***********************************************************************************************/ -int bIsUnsatCarbonInASmallRing( inp_ATOM *at2, VAL_AT *pVA, int iat, BFS_Q *pbfsq, int min_ring_size ) -{ - int j, nCurRingSize, nMinRingSize; - if ( min_ring_size < 5 ) { - /* =C= in a small ring */ - if ( at2[iat].valence == 2 && - pVA[iat].cMinRingSize <= 5 && - at2[iat].chem_bonds_valence == 4 ) { - return 1; - } - } else { - if ( at2[iat].valence == 2 && - pVA[iat].cMinRingSize && - pVA[iat].cMinRingSize <= min_ring_size && - at2[iat].chem_bonds_valence == 3 ) { - return 1; - } - nCurRingSize = nMinRingSize = min_ring_size+1; - if ( (at2[iat].valence == 2 || at2[iat].valence == 3) && - at2[iat].chem_bonds_valence == at2[iat].valence+1 ) { - for ( j = 0; j < at2[iat].valence; j ++ ) { - nCurRingSize = is_bond_in_Nmax_memb_ring( at2, iat, j, pbfsq->q, - pbfsq->nAtomLevel, - pbfsq->cSource, (AT_RANK)nMinRingSize /* max ring size */ ); - if ( 0 < nCurRingSize && nCurRingSize < nMinRingSize ) { - nMinRingSize = nCurRingSize; - } - } - return (0 <= nCurRingSize)? (nMinRingSize <= min_ring_size) : nCurRingSize; - } - } - return 0; -} -/***********************************************************************************************/ -int FixMobileHRestoredStructure(ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, - StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, T_GROUP_INFO **ppt_group_info, inp_ATOM **ppat_norm, - inp_ATOM **ppat_prep, INChI *pInChI[], long num_inp, int bHasSomeFixedH, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask) -{ - /*--------- process extra or missing Fixed-H on non-tautomeric atoms ------*/ - /* at2 should be the most recently restored atom, Fixed-H */ - int i, j, k, iat, delta, tot_succes, cur_success, ret = 0; - CMP2MHINCHI c2i; - CMP2MHINCHI *pc2i = &c2i; - - EDGE_LIST AllChargeEdges, CurrEdges, CurrEdges2, CurrEdges3, TautEdges, NFlowerEdges, OtherNFlowerEdges, FixedLargeRingStereoEdges; - EDGE_LIST *pEdgeList = NULL; - - EdgeIndex e; - BNS_EDGE *pe; - Vertex v1, v2, vPlusMinus; - BNS_VERTEX *pv1, *pv2; - - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - - int nNumRunBNS = 0, forbidden_edge_mask_inv = ~forbidden_edge_mask; - - INCHI_HEAPCHK - - AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &CurrEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &NFlowerEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &CurrEdges2, EDGE_LIST_CLEAR ); - AllocEdgeList( &CurrEdges3, EDGE_LIST_CLEAR ); - AllocEdgeList( &OtherNFlowerEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &TautEdges, EDGE_LIST_CLEAR ); - - tot_succes = 0; - - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - /* taut group edges */ - for ( i = 0; i < pTCGroups->num_tgroups; i ++ ) { - pv1 = pBNS->vert + (v1=pTCGroups->pTCG[i].nVertexNumber); /* t-group vertex */ - for ( j = 0; j < pv1->num_adj_edges; j ++ ) { - /* e, pe - tautomeric atom edge; pv2 - endpoint vertex */ - /* Note: pe, pv2, v1 are not used here; they are to show how to traverse t-group */ - pv2 = pBNS->vert + (pe = pBNS->edge + (e=pv1->iedge[j]))->neighbor1; - if ( ret = AddToEdgeList( &TautEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - /* charge and flower edges */ - for ( i = 0; i < pStruct->num_atoms; i ++ ) { - if ( (e=pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - (ret = AddToEdgeList( &AllChargeEdges, e, INC_ADD_EDGE )) ) { - goto exit_function; - } - if ( (e=pVA[i].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { - if ( ret = AddToEdgeList( &AllChargeEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - - /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ - if ( pVA[i].cNumValenceElectrons == 5 && !pVA[i].cMetal && /* N, P, As */ - NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e ))) { - - if ( !pBNS->edge[j].forbidden && pBNS->edge[j].flow ) { - if ( ret = AddToEdgeList( &AllChargeEdges, j, INC_ADD_EDGE ) ) { - goto exit_function; - } - if ( ret = AddToEdgeList( &NFlowerEdges, j, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else { - if ( ret = AddToEdgeList( &OtherNFlowerEdges, j, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - - } - } - if ( forbidden_stereo_edge_mask ) { - for ( i = 0; i < pStruct->num_atoms; i ++ ) { - for ( j = 0; j < at2[i].valence; j ++ ) { - if ( pBNS->edge[k = pBNS->vert[i].iedge[j]].forbidden == forbidden_stereo_edge_mask ) { - int nMinRingSize = is_bond_in_Nmax_memb_ring( at2, i, j, pStruct->pbfsq->q, - pStruct->pbfsq->nAtomLevel, - pStruct->pbfsq->cSource, 99 /* max ring size */ ); - if ( 0 < nMinRingSize && (ret = AddToEdgeList( &FixedLargeRingStereoEdges, k, INC_ADD_EDGE ))) { - goto exit_function; - } - } - } - } - } - - INCHI_HEAPCHK - - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - INCHI_HEAPCHK - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - - INCHI_HEAPCHK - - - - - if ( pc2i->nNumTgInChI == 1 && ( pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1 ) && - pc2i->nNumTgDBNMinusRevrs + pc2i->nNumTgNHMinusRevrs == 0 && pc2i->nNumTgOMinusInChI && - !(pTCGroups->pTCG[0].tg_RestoreFlags & TGRF_MINUS_FIRST) ) { - /*----------------------------------------------------*/ - /* case 01: restored has -O(-) and does not have N(-) */ - /* endpoints defined by the original InChI */ - /* restored has single taut. group or more */ - /* tautomeric endpoints. */ - /* Solution: move (-) from endp. -O(-) to endpoints N */ - /*----------------------------------------------------*/ - pTCGroups->pTCG[0].tg_RestoreFlags |= TGRF_MINUS_FIRST; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { - goto exit_function; /* no fixed-H found */ - } - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - if ( pc2i->nNumTgInChI == 1 && ( pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1 ) && - pc2i->nNumTgDBNMinusRevrs + pc2i->nNumTgNHMinusRevrs == 0 && pc2i->nNumTgOMinusInChI == 0 ) { - /*-------------------------------------------------------*/ - /* case 02: restored has no -O(-) and does not have N(-) */ - /* restored has single taut. group or more */ - /* tautomeric endpoints. */ - /* Solution: >N-AB=N- => >N(+)=AB-NH- (add H(+)) */ - /* Solution: >N-AB=NH => >N(+)=AB-NH2 (add H(+)) */ - /* SB_N_III DB_N_III */ - /*-------------------------------------------------------*/ - int iat_SB_N_III[MAX_DIFF_MOBH], iat_DB_N_III[MAX_DIFF_MOBH]; - int num_SB_N_III = 0, num_DB_N_III = 0, k1, k2; - CurrEdges.num_edges = 0; - cur_success = 0; - for ( i = 0; i < pStruct->num_atoms; i ++ ) { - iat = i; - if ( pVA[iat].cNumValenceElectrons == 5 && pVA[i].cPeriodicRowNumber == 1 && - !at2[iat].endpoint && !at2[iat].charge && !at2[iat].radical ) { - if ( num_DB_N_III < MAX_DIFF_MOBH && !at2[iat].num_H && - at2[iat].valence == 2 && - at2[iat].chem_bonds_valence == 3 && - !at2[iat].sb_parity[0] && /* do not eliminate stereobonds */ - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pBNS->edge[e].cap && !pBNS->edge[e].flow ) { - /* -N= */ - iat_DB_N_III[ num_DB_N_III ++ ] = iat; - } else - if ( num_DB_N_III < MAX_DIFF_MOBH && 1 == at2[iat].num_H && - at2[iat].valence == 1 && - at2[iat].chem_bonds_valence == 2 && - !at2[iat].sb_parity[0] && /* do not eliminate stereobonds */ - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pBNS->edge[e].cap && !pBNS->edge[e].flow ) { - /* -N= */ - iat_DB_N_III[ num_DB_N_III ++ ] = iat; - } else - if ( num_SB_N_III < MAX_DIFF_MOBH && !at2[iat].num_H && - at2[iat].valence == 3 && - at2[iat].chem_bonds_valence == 3 && - (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - pBNS->edge[e].cap && pBNS->edge[e].flow) { - /* -N< */ - iat_SB_N_III[ num_SB_N_III ++ ] = iat; - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - } - if ( num_DB_N_III && num_SB_N_III ) { - EdgeIndex ieMinus; - BNS_EDGE *peMinus; - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - for ( i = 0; i < num_DB_N_III && !cur_success; i ++ ) { - iat = iat_DB_N_III[ i ]; - e = pBNS->edge[k1=pBNS->vert[iat].iedge[0]].flow? k1 : - pBNS->edge[k2=pBNS->vert[iat].iedge[1]].flow? k2 : NO_VERTEX; - if ( e == NO_VERTEX ) { - continue; /* should not happen */ - } - ieMinus = pVA[iat].nCMinusGroupEdge-1; - peMinus = pBNS->edge + ieMinus; - pe = pBNS->edge + e; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; /* fix double bond */ - peMinus->forbidden &= forbidden_edge_mask_inv; /* allow negative charge */ - delta = 1; - pe->flow -= delta; /* remove (-) from AB-O(-) */ - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 2 ) { - /* Added (-)charge -N= and (+) to -N< => nDeltaCharge == 2 */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 01 */ - - /* eliminate (-) charge and add H */ - pv1 = pBNS->vert + (v1 = peMinus->neighbor1); /* atom */ - pv2 = pBNS->vert + (v2 = peMinus->neighbor12 ^ v1);/* (=) vertex */ - /* effectively eliminate (-) edge by setting its cap=flow= 0 */ - peMinus->cap --; - peMinus->flow --; - pv1->st_edge.cap --; - pv1->st_edge.flow --; - pv2->st_edge.cap --; - pv2->st_edge.flow --; - pBNS->tot_st_flow -= 2; - pBNS->tot_st_cap -= 2; - /* add H */ - pStruct->at[iat].num_H ++; - /* register total charge increase */ - pTCGroups->total_charge ++; - pStruct->nNumRemovedProtonsByRevrs -= 1; - } - } else { - pe->forbidden &= forbidden_edge_mask_inv; - peMinus->forbidden |= forbidden_edge_mask; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - CurrEdges.num_edges = 0; /* clear current edge list */ - - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - } - if ( pc2i->nNumTgInChI == 1 && ( pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1 ) && /* ADP */ - pc2i->nNumTgMInChI == 0 && pc2i->nNumTgNInChI && pc2i->nNumTgOInChI ) { - /*-------------------------------------------------------*/ - /* case 03: restored has N and O endpoints, no (-) endp */ - /* case 04: original has single taut. group or more */ - /* tautomeric endpoints. */ - /* Solution: 1. Move taut attachment from O to N */ - /* Solution: 2. Replace the attachment with (-) */ - /* SB_N_III DB_N_III */ - /*-------------------------------------------------------*/ - /* - int iat_SB_N_III[MAX_DIFF_MOBH], iat_DB_N_III[MAX_DIFF_MOBH]; - int num_SB_N_III = 0, num_DB_N_III = 0, k1, k2, - */ - int itg, j1, j2, bAction = 0; - BNS_VERTEX *pTg, *pvEndp, *pvEndp2, *pvCent; - Vertex vEndp, vEndp2, vCent; - BNS_EDGE *peTg, *peTg2, *peCent1, *peCent2; - EdgeIndex eTg, eTg2; - - CurrEdges.num_edges = 0; - CurrEdges2.num_edges = 0; - cur_success = 0; - - /* 1st attempt: -NH-=O => -N(-)-=O or -N=-OH => -N(-)-=O */ - for ( itg = 0; itg < pTCGroups->num_tgroups && !cur_success; itg ++ ) { - pTg = pBNS->vert + pTCGroups->pTCG[itg].nVertexNumber; - for ( i = 0; i < pTg->num_adj_edges && !cur_success; i ++ ) { - pvEndp = pBNS->vert + (vEndp = (peTg = pBNS->edge + (eTg=pTg->iedge[i]))->neighbor1); - eTg2 = -1; - if ( pVA[vEndp].cNumValenceElectrons == 6 && peTg->cap ) { - /* endpoint -OH or =O found; search for a possible centerpoint */ - for ( j1 = 0; j1 < at2[vEndp].valence && eTg2 < 0; j1 ++ ) { - peCent1 = pBNS->edge + pvEndp->iedge[j1]; /* edge from O to a centerpoint */ - pvCent = pBNS->vert + (vCent = peCent1->neighbor12 ^ vEndp); /* centerpoint */ - if ( at2[vCent].endpoint || !peCent1->cap || - peCent1->flow + (peTg->cap == peTg->flow) != 1 ) { - continue; - } - /* search for another endpoint, N, around vCent */ - for ( j2 = 0; j2 < at2[vCent].valence; j2 ++ ) { - peCent2 = pBNS->edge + pvCent->iedge[j2]; - pvEndp2 = pBNS->vert + (vEndp2 = peCent2->neighbor12 ^ vCent); - if ( !peCent2->cap || peCent2->flow+peCent1->flow != 1 || - at2[vEndp2].endpoint != itg+1 || - pVA[vEndp2].cNumValenceElectrons != 5 || - 0 > (j=pVA[vEndp2].nTautGroupEdge-1) || - (peTg2 = pBNS->edge + j)->forbidden || - peCent2->flow + (peTg2->cap == peTg2->flow) != 1 ) { - continue; - } - eTg2 = j; - break; /* found OH-C=N- or O=C-NH- */ - } - } - } - if ( eTg2 >= 0 ) { - /*-------------------------------------------- - tg tg - eTg //\ eTg2 eTg / \\eTg2 - // \ / \\ - vEndp HO--C==N vEndp2 --> vEndp O==C--NH vEndp2 - ^ ^ ^ ^ ^ ^ - eCent1 | eCent2 eCent1 | eCent2 - vCent vCent - - additional action: -OH-C=N- => O=C-NH- - -------------------------------------------*/ - if ( 0 == peTg->cap - peTg->flow && 1 == peTg2->cap - peTg2->flow && - 0 == peCent1->flow && 1 == peCent2->flow ) { - peTg->flow --; /* 03 prepare */ - peTg2->flow ++; - peCent2->flow --; - peCent1->flow ++; - bAction |= 1; /* switched H position */ - } - if ( 1 == peTg->cap - peTg->flow && 0 == peTg2->cap - peTg2->flow && - 1 == peCent1->flow && 0 == peCent2->flow ) { - /* replace -NH- with -N(-)- */ - pTCGroups->pTCG[itg].tg_num_H --; - pTCGroups->pTCG[itg].tg_num_Minus ++; - pTCGroups->pTCG[itg].tg_RestoreFlags |= TGRF_MINUS_FIRST; - pTCGroups->pTCG[itg].tg_set_Minus = vEndp2+1; - pStruct->ti.t_group[itg].num[1] ++; /* increment number of (-), keep number of taut attachments */ - pTCGroups->total_charge --; - pTCGroups->tgroup_charge --; - pStruct->nNumRemovedProtonsByRevrs += 1; - bAction |= 2; /* single NH (at2[vEndp2]) replaced with N(-) */ - cur_success ++; /* 03/04 */ - } - } - } - } - - if ( 0 == pc2i->nNumTgNHInChI+ pc2i->nNumTgNH2InChI && pc2i->nNumTgOHInChI && !cur_success ) { - /* transfer an attachement to N */ - for ( itg = 0; itg < pTCGroups->num_tgroups; itg ++ ) { - pTg = pBNS->vert + pTCGroups->pTCG[itg].nVertexNumber; - for ( i = 0; i < pTg->num_adj_edges; i ++ ) { - pvEndp = pBNS->vert + (vEndp = (peTg = pBNS->edge + (eTg=pTg->iedge[i]))->neighbor1); - if ( pVA[vEndp].cNumValenceElectrons == 6 && - at2[vEndp].valence == at2[vEndp].chem_bonds_valence && - peTg->flow && peTg->flow == peTg->cap ) { - /* endpoint -OH found; save the tautomeric group edge */ - if ( ret = AddToEdgeList( &CurrEdges, eTg, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else - if ( pVA[vEndp].cNumValenceElectrons == 5 && - pVA[vEndp].cPeriodicRowNumber == 1 && - at2[vEndp].valence + 1 == at2[vEndp].chem_bonds_valence && - peTg->cap && peTg->flow + 1 == peTg->cap ) { - /* endpoint -N= or =NH found, check for -N=-OH */ - e = -1; - for ( j1 = 0; j1 < at2[vEndp].valence && e < 0; j1 ++ ) { - peCent1 = pBNS->edge + pvEndp->iedge[j1]; - if ( peCent1->flow == 1 ) { - /* double bond */ - pvCent = pBNS->vert + (vCent = peCent1->neighbor12 ^ vEndp); - if ( at2[vCent].endpoint ) - continue; - for ( j2 = 0; j2 < at2[vCent].valence; j2 ++ ) { - peCent2 = pBNS->edge + pvCent->iedge[j2]; - pvEndp2 = pBNS->vert + (vEndp2 = peCent2->neighbor12 ^ vCent); - if ( peCent2->flow || at2[vEndp2].endpoint != itg+1 || - pVA[vEndp2].cNumValenceElectrons != 6 || - 0 >= (e=pVA[vEndp2].nTautGroupEdge-1) || - pBNS->edge[e].forbidden || !pBNS->edge[e].flow ) { - e = -1; - continue; - } - /*********************/ - /* found -N=X-OH */ - /* vEndp ^ vEndp2 */ - /* vCent */ - /*********************/ - /* save this -OH taut edge */ - if ( ret = AddToEdgeList( &CurrEdges2, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - break; - } - } - } - if ( e < 0 && (ret = AddToEdgeList( &CurrEdges, eTg, INC_ADD_EDGE )) ) { - goto exit_function; - } - } - } - } - /* rearrange the flows */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - SetForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - SetForbiddenEdgeMask( pBNS, &CurrEdges2, forbidden_edge_mask ); - pEdgeList = CurrEdges2.num_edges? &CurrEdges2 : CurrEdges.num_edges? &CurrEdges : NULL; - - for ( i = 0; pEdgeList && i < pEdgeList->num_edges && !cur_success; i ++ ) { - pe = pBNS->edge + pEdgeList->pnEdges[i]; /* pe->flow = 1 <=> -OH */ - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); /* -OH atom */ - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); /* t-group vertex */ - /* locate the t-group */ - for ( itg = 0; itg < pTCGroups->num_tgroups; itg ++ ) { - if ( v2 == pTCGroups->pTCG[itg].nVertexNumber ) { - break; - } - } - if ( itg == pTCGroups->num_tgroups ) { - /* tgroup not found -- should not happen */ - continue; - } - - delta = 1; - pe->flow -= delta; /* add one attachment to */ - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 2 ) { - /* Added (-)charge -N= and (+) to -N< => nDeltaCharge == 2 */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 03 */ - /* replace -NH- with -N(-)- */ - pTCGroups->pTCG[itg].tg_num_H --; - pTCGroups->pTCG[itg].tg_num_Minus ++; - pTCGroups->pTCG[itg].tg_RestoreFlags |= TGRF_MINUS_FIRST; - pStruct->ti.t_group[itg].num[1] ++; - pTCGroups->total_charge --; - pTCGroups->tgroup_charge --; - pStruct->nNumRemovedProtonsByRevrs += 1; - bAction |= 4; /* H in the 1st available NH was replaced with (-) */ - } - } else { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - } else - if ( pc2i->nNumTgNHInChI+ pc2i->nNumTgNH2InChI && pc2i->nNumTgOInChI && !cur_success ) { - /* change an attachement to N from H to (-) */ - for ( itg = 0; itg < pTCGroups->num_tgroups && !cur_success; itg ++ ) { - pTg = pBNS->vert + pTCGroups->pTCG[itg].nVertexNumber; - for ( i = 0; i < pTg->num_adj_edges && !cur_success; i ++ ) { - pvEndp2 = pBNS->vert + (vEndp2 = (peTg = pBNS->edge + pTg->iedge[i])->neighbor1); - if ( pVA[vEndp2].cNumValenceElectrons == 5 && pVA[vEndp2].cPeriodicRowNumber == 1 && - at2[vEndp2].valence == at2[vEndp2].chem_bonds_valence && - peTg->flow && peTg->flow == peTg->cap ) { - /* endpoint -NHn found; change its charge */ - cur_success ++; /* 04 */ - /* replace -NH- with -N(-)- */ - pTCGroups->pTCG[itg].tg_num_H --; - pTCGroups->pTCG[itg].tg_num_Minus ++; - pTCGroups->pTCG[itg].tg_RestoreFlags |= TGRF_MINUS_FIRST; - pTCGroups->pTCG[itg].tg_set_Minus = vEndp2 + 1; - pStruct->ti.t_group[itg].num[1] ++; - pTCGroups->total_charge --; - pTCGroups->tgroup_charge --; - pStruct->nNumRemovedProtonsByRevrs += 1; - bAction |= 8; /* manually set (-) charge to NH atom, vEndp2 */ - } - } - } - } - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( pStruct->One_ti.num_t_groups == 1 && pStruct->One_ti.t_group[0].num[1] ) { - /* this method did not work: no alt path from N(-) to =O */ - itg = 0; - if ( bAction & (8 | 2 ) ) { - /* roll back NH -> N(-) replacement; H move from OH to N is not undone */ - pTCGroups->pTCG[itg].tg_num_H ++; - pTCGroups->pTCG[itg].tg_num_Minus --; - pTCGroups->pTCG[itg].tg_RestoreFlags &= ~TGRF_MINUS_FIRST; - pTCGroups->pTCG[itg].tg_set_Minus = 0; - pStruct->ti.t_group[itg].num[1] --; - pTCGroups->total_charge ++; - pTCGroups->tgroup_charge ++; - pStruct->nNumRemovedProtonsByRevrs -= 1; - cur_success --; - } else - if ( bAction & 4 ) { - pTCGroups->pTCG[itg].tg_num_H ++; - pTCGroups->pTCG[itg].tg_num_Minus --; - pTCGroups->pTCG[itg].tg_RestoreFlags &= ~TGRF_MINUS_FIRST; - pStruct->ti.t_group[itg].num[1] --; - pTCGroups->total_charge ++; - pTCGroups->tgroup_charge ++; - pStruct->nNumRemovedProtonsByRevrs -= 1; - cur_success --; - } else { - ret = RI_ERR_PROGR; - goto exit_function; - } - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - } - - if ( pc2i->nNumTgInChI == 1 && ( pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1 ) && /* ADP */ - pc2i->nNumTgMInChI == 0 && (pc2i->nNumTgNInChI || pc2i->nNumTgOInChI) && - NO_VERTEX != (vPlusMinus = GetPlusMinusVertex( pBNS, pTCGroups, 1, 1 )) ) { - /*---------------------------------------------------------------------------*/ - /* case 05: restored has N endpoints, no (-) endpoints */ - /* original has single taut. group or more */ - /* tautomeric endpoints. */ - /* Solution: Find -N< and allow (+) charge change */ - /* Fix all charges and taut attachments exept */ - /* =N- and =O (taut. endpoints) */ - /* Increment st_edge.cap on (+/-) vertex => add (+) charge to -N< */ - /* Increment tot. charge in other places */ - /* Increment t-group st_edge.cap */ - /* Run BNS */ - /* */ - /* (+/-)* (+/-) Result: */ - /* | || */ - /* | || - Added (+) to -N< */ - /* (+)super (+)super - Added attachment point to O */ - /* || | */ - /* || => | To make this attachment H, */ - /* (Y) (Y) increment */ - /* | || pTCGroups->pTCG[itg].tg_num_H */ - /* | || */ - /* (+)hetero (+)hetero Technical details: */ - /* \\ \ increase capacities of */ - /* N N(+) edges to (+/-) otherwise */ - /* | || flow may not be able to */ - /* *(t)--O=R. (t)==O-R. increase */ - /* */ - /* */ - /*---------------------------------------------------------------------------*/ - int itg; - BNS_VERTEX *pTg, *pvEndp; - Vertex vEndp, vTg; - BNS_EDGE *peTg; - EdgeIndex eTg; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - - CurrEdges.num_edges = 0; - CurrEdges2.num_edges = 0; - cur_success = 0; - /* find -N< and non-taut =N- or =O */ - for ( i = 0; i < pStruct->num_atoms; i ++ ) { - iat = nCanon2AtnoRevrs[i]; - /* -N< */ - if ( !at2[iat].endpoint && !at2[iat].charge && !at2[iat].radical && !at2[iat].num_H && - pVA[i].cNumValenceElectrons == 5 && pVA[i].cPeriodicRowNumber == 1 && - 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && pBNS->edge[e].flow && !pBNS->edge[e].forbidden) { - - if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( !CurrEdges.num_edges ) { - goto exit_case_05; - } - /* find taut -N= and =O */ - for ( itg = 0; itg < pTCGroups->num_tgroups && !cur_success; itg ++ ) { - CurrEdges2.num_edges = 0; - pTg = pBNS->vert + (vTg = pTCGroups->pTCG[itg].nVertexNumber); - for ( i = 0; i < pTg->num_adj_edges; i ++ ) { - pvEndp = pBNS->vert + (vEndp = (peTg = pBNS->edge + (eTg=pTg->iedge[i]))->neighbor1); - if ( at2[vEndp].charge || at2[vEndp].radical || peTg->cap - peTg->flow != 1 ) { - continue; - } - /* t-group edges to -N= and =O */ - if ( ret = AddToEdgeList( &CurrEdges2, eTg, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - if ( !CurrEdges2.num_edges ) { - goto exit_case_05; - } - /* fix all charge edges except -N< and all taut. edges except =O and =N- */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - SetForbiddenEdgeMask( pBNS, &TautEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &CurrEdges2, forbidden_edge_mask ); - delta = 1; - /* Increment st_edge.cap on (+/-) vertex */ - pBNS->vert[vPlusMinus].st_edge.cap += delta; - /* Increment st_edge.cap on t-group */ - pTg->st_edge.cap += delta; - /* total cap count */ - pBNS->tot_st_cap += 2*delta; - - v1 = vPlusMinus; - v2 = vTg; - - /* increase capacities of edges to Y */ - for ( i = 0; i < pBNS->vert[vPlusMinus].num_adj_edges; i ++ ) { - j = pBNS->edge[pBNS->vert[vPlusMinus].iedge[i]].neighbor12 ^ vPlusMinus; - for ( k = 0; k < pBNS->vert[j].num_adj_edges; k ++ ) { - pBNS->edge[pBNS->vert[j].iedge[k]].cap += delta; - } - } - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Added (+)charge to -N< => nDeltaCharge == 1 */ - /* Flow change on pe (-)charge edge (atom B-O(-)) is not known to RunBnsTestOnce()) */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 01 */ - /* update bookkeeping */ - pTCGroups->total_charge += delta; - pTCGroups->pTCG[itg].edges_cap += delta; - pTCGroups->pTCG[itg].tg_num_H += delta; - pStruct->nNumRemovedProtonsByRevrs -= delta; - } - } else { - pBNS->vert[vPlusMinus].st_edge.cap -= delta; - pTg->st_edge.cap -= delta; - /*pTCGroups->pTCG[itg].edges_cap -= delta;*/ /* ???bug??? - commented out 2006-03-22 */ - pBNS->tot_st_cap -= 2*delta; - /* decrease capacities of edges to Y */ - for ( i = 0; i < pBNS->vert[vPlusMinus].num_adj_edges; i ++ ) { - j = pBNS->edge[pBNS->vert[vPlusMinus].iedge[i]].neighbor12 ^ vPlusMinus; - for ( k = 0; k < pBNS->vert[j].num_adj_edges; k ++ ) { - pBNS->edge[pBNS->vert[j].iedge[k]].cap -= delta; - } - } - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &TautEdges, forbidden_edge_mask ); - } - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } - -exit_case_05:; - } - - while ( pc2i->nNumDiffMobH && pc2i->nChargeMobHRevrs > pc2i->nChargeMobHInChI ) { - /*----------------------------------------------------*/ - /* case 06: restored has extra H attached to -O(-) */ - /* while the chrge should be on C, most pro- */ - /* bably in a small ring.ut. group or more */ - /* tautomeric endpoints. */ - /* Solution: move (-) from O to C */ - /*----------------------------------------------------*/ - int iO, mode; - EdgeIndex e2; - BNS_EDGE *pe2; - cur_success = 0; - for ( i = 0; !cur_success && i < pc2i->len_c2at; i ++ ) { - - if ( pc2i->c2at[i].nMobHRevrs == pc2i->c2at[i].nMobHInChI + 1 && - pc2i->c2at[i].nNumHRevrs == pc2i->c2at[i].nMobHInChI && - !pc2i->c2at[i].endptInChI && !pc2i->c2at[i].endptRevrs && - at2[iO = pc2i->c2at[i].atomNumber].charge == -1 && - 0 <= (e=pVA[iO].nCMinusGroupEdge-1) && (pe=pBNS->edge+e)->flow ) { - - /* try suitable atoms C */ - /* first look for =C= in a small ring */ - - for( mode = 4; !cur_success && mode <= 8; mode ++ ) { - - if ( mode == 8 ) - mode = 99; - - for ( iat = 0; !cur_success && iat < pStruct->num_atoms; iat ++ ) { - - if ( !at2[iat].charge && !at2[iat].radical && - pVA[iat].cNumValenceElectrons == 4 && - 0 <= (e2=pVA[iat].nCMinusGroupEdge-1) && !(pe2=pBNS->edge+e2)->flow && - 0 < bIsUnsatCarbonInASmallRing( at2, pVA, iat, pStruct->pbfsq, mode ) ) { - - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - /* allow negative charge on the chosen carbon */ - pe2->forbidden &= forbidden_edge_mask_inv; - - delta = 1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { - /* Added (-)charge to unsaturated C => nDeltaCharge == 2 */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - cur_success ++; /* 01 */ - tot_succes += cur_success; - } - } else { - pe->forbidden |= forbidden_edge_mask; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } - } - } - } - } - if ( cur_success ) { - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } else { - break; - } - } - if ( pc2i->len_c2at && pc2i->nChargeMobHRevrs > pc2i->nChargeMobHInChI ) { - /*------------------------------------------------------------------*/ - /* case 07: -NO2 are to be tautomeric but they are not AND */ - /* InChI has a SINGLE tautomeric group */ - /* */ - /* (-)O (-)O */ - /* Solution: convert \ \ */ - /* N-X=...-Z(-) => N(+)=X- ...=Z */ - /* // / */ - /* O (-)O */ - /* */ - /* O O */ - /* or \\ \\ */ - /* N-X=...-Z(-) => N=X- ...=Z */ - /* // / */ - /* O (-)O */ - /* */ - /* */ - /* (a) move (-) from other tautomeric atom to O in O=N-X */ - /* or from other atom that has to be tautomeric */ - /* but is not */ - /* (b) create (+) [ion pair creation] on N as in */ - /* */ - /* OH OH */ - /* / / */ - /* -C=N => =C-N(+) */ - /* \\ \\ */ - /* O O */ - /* */ - /*------------------------------------------------------------------*/ - int num_DB_O = 0; - short iat_DB_O[MAX_DIFF_FIXH], iat_NO2[MAX_DIFF_FIXH]; - AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; - /* - AT_NUMB *nAtno2CanonRevrs = pStruct->nAtno2Canon[0]; - */ - inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[0] && - pStruct->pOne_norm_data[0]->at)? pStruct->pOne_norm_data[0]->at : NULL; - - int iN, one_success; - BNS_EDGE *peDB_O_Minus; - int neigh, nNumO, nNumOthers; -#define CHG_SET_WRONG_TAUT_N 0 -#define CHG_SET_WRONG_TAUT_O 1 -#define CHG_SET_WRONG_TAUT_ALL 2 -#define CHG_LAST_SET 2 /* the last index in trying */ -#define CHG_SET_O_FIXED 3 -#define CHG_SET_NUM 4 - EDGE_LIST ChangeableEdges[CHG_SET_NUM]; - memset( ChangeableEdges, 0, sizeof(ChangeableEdges) ); - /* equivalent to AllocEdgeList( &EdgeList, EDGE_LIST_CLEAR ); */ - /* - S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : - pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; - */ - CurrEdges.num_edges = 0; /* clear current edge list */ - cur_success = 0; - for ( i = 0; i < pStruct->num_atoms; i ++ ) { - iat = nCanon2AtnoRevrs[i]; - if ( /* orig. InChI info: taut in orig. InChI =O located in -NO2 that is not taut in Reconstructed InChI */ - num_DB_O < MAX_DIFF_FIXH && - pVA[iat].cNumValenceElectrons == 6 /* O, S, Se, Te */ && - (!at2[iat].endpoint /*|| pc2i->c2at[i].nMobHInChI*/) && - (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && - at2[iat].num_H == 0 && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ - /* reversed structure info: */ - !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint) /*|| pc2i->c2at[i].nMobHRevrs*/ && - !at2[iat].charge && - at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 && - /* find whether it belongs to NO2 */ - pVA[iN=at2[iat].neighbor[0]].cNumValenceElectrons == 5 && - at2[iN].valence == 3 && (at2[iN].charge == 0 || at2[iN].charge == 1) && - at2[iN].chem_bonds_valence == 5 - at2[iN].charge ) { - /* find the second O */ - nNumO = nNumOthers = 0; - for ( k = 0; k < at2[iN].valence; k ++ ) { - neigh = at2[iN].neighbor[k]; - if ( neigh == iat ) { - continue; - } - if ( pVA[neigh].cNumValenceElectrons == 6 && - !at2[neigh].endpoint && - !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[neigh].endpoint) && - at2[neigh].valence == 1 && at2[neigh].num_H == 0 && - at2[neigh].radical == 0 && (at2[neigh].charge == 0 || at2[neigh].charge == -1) && - at2[neigh].chem_bonds_valence - at2[neigh].charge == 2) { - nNumO ++; - } else - if ( at2[iN].bond_type[k] == BOND_TYPE_SINGLE && - at2[neigh].valence > 1 && - at2[neigh].valence < at2[neigh].chem_bonds_valence ) { - nNumOthers ++; - } - } - if ( nNumO != 1 || nNumOthers != 1 ) { - continue; - } - for ( k = 0; k < num_DB_O; k ++ ) { - if ( iat_NO2[k] == iN ) { - break; - } - } - if ( k == num_DB_O ) { - iat_NO2[num_DB_O] = iN; - iat_DB_O[num_DB_O ++] = iat; - } - /* save the =O (-)-edge to avoid interference */ - if ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e, INC_ADD_EDGE ) ) { - goto exit_case_07; - } - } - } - if ( num_DB_O ) { - /* search for falsely tautomeric negatively charged atoms N and O */ - for ( i = 0; i < pc2i->len_c2at; i ++ ) { - iat = pc2i->c2at[i].atomNumber; - if ( pc2i->c2at[i].endptRevrs && !pc2i->c2at[i].endptInChI && - pc2i->c2at[i].nAtChargeRevrs == - 1 && - 0 <= (e=pVA[iat].nCMinusGroupEdge-1) && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && - 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e ) ) { - if ( pc2i->c2at[i].nValElectr == 6 ) { - if (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_WRONG_TAUT_O], e, INC_ADD_EDGE ) ) { - goto exit_case_07; - } - } else - if ( pc2i->c2at[i].nValElectr == 5 ) { - if (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_WRONG_TAUT_N], e, INC_ADD_EDGE ) ) { - goto exit_case_07; - } - } - if (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_WRONG_TAUT_ALL], e, INC_ADD_EDGE ) ) { - goto exit_case_07; - } - } - } - /* ------- finally, try to move charges from O=N --------------*/ - for ( i = 0; i < num_DB_O; i ++ ) { - int nDeltaChargeExpected; - one_success = 0; - delta = 1; - iat = iat_DB_O[i]; - peDB_O_Minus = pBNS->edge + (pVA[iat].nCMinusGroupEdge-1); - pe = pBNS->edge + pBNS->vert[iat].iedge[0]; - - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; - - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - for ( k = 0; !one_success && k <= CHG_LAST_SET; k ++ ) { - if ( !ChangeableEdges[k].num_edges ) { - continue; - } - nDeltaChargeExpected = 0; - - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - RemoveForbiddenEdgeMask( pBNS, &ChangeableEdges[k], forbidden_edge_mask ); - /* allow (-) charge to move to N=O */ - peDB_O_Minus->forbidden &= forbidden_edge_mask_inv; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && - nDeltaCharge == nDeltaChargeExpected ) { - /* Move (-) charge to =O and remove it an endpoint => nDeltaCharge == 0 */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - nNumRunBNS ++; - one_success ++; /* 07 */ - } - } - INCHI_HEAPCHK - } - cur_success += one_success; - - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - pe->forbidden &= forbidden_edge_mask_inv; - - if ( !one_success ) { - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - } - } -exit_case_07: - for ( i = 0; i < CHG_SET_NUM; i ++ ) { - AllocEdgeList( &ChangeableEdges[i], EDGE_LIST_FREE ); - } - - CurrEdges.num_edges = 0; /* clear current edge list */ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - if ( !pc2i->bHasDifference ) { - goto exit_function; /* nothing to do */ - } - } -#undef CHG_SET_NOOH -#undef CHG_SET_WRONG_TAUT -#undef CHG_SET_TAUT -#undef CHG_LAST_SET -#undef CHG_SET_O_FIXED -#undef CHG_SET_NUM - } - - - -exit_function: - AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &CurrEdges, EDGE_LIST_FREE ); - AllocEdgeList( &CurrEdges2, EDGE_LIST_FREE ); - AllocEdgeList( &CurrEdges3, EDGE_LIST_FREE ); - AllocEdgeList( &NFlowerEdges, EDGE_LIST_FREE ); - AllocEdgeList( &OtherNFlowerEdges, EDGE_LIST_FREE ); - AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_FREE ); - AllocEdgeList( &TautEdges, EDGE_LIST_FREE ); - - - return ret; -} - -#endif +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include + +/*#define CHECK_WIN32_VC_HEAP*/ + +#include "mode.h" + +#if ( READ_INCHI_STRING == 1 ) + +#include "ichitime.h" +#include "ichicant.h" +#include "ichirvrs.h" + +#define INC_ADD_EDGE 64 + +/***********************************************************************************************/ +int GetPlusMinusVertex( BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups, int bCheckForbiddenPlus, int bCheckForbiddenMinus ) +{ + int k, ePlusSuper, eMinusSuper, vPlusSuper, vMinusSuper, vPlusMinus1 = NO_VERTEX, vPlusMinus2 = NO_VERTEX; + BNS_EDGE *pEdge; + if ( (k = pTCGroups->nGroup[TCG_Plus]) >= 0 && + (ePlusSuper = pTCGroups->pTCG[k].nForwardEdge) > 0 && + (vPlusSuper = pTCGroups->pTCG[k].nVertexNumber) >= pBNS->num_atoms && + !((pEdge=pBNS->edge + ePlusSuper)->forbidden && bCheckForbiddenPlus) ) { + + vPlusMinus1 = pEdge->neighbor12 ^ vPlusSuper; + } + if ( (k = pTCGroups->nGroup[TCG_Minus]) >= 0 && + (eMinusSuper = pTCGroups->pTCG[k].nForwardEdge) > 0 && + (vMinusSuper = pTCGroups->pTCG[k].nVertexNumber) >= pBNS->num_atoms && + !((pEdge=pBNS->edge + eMinusSuper)->forbidden && bCheckForbiddenMinus) ) { + + vPlusMinus2 = pEdge->neighbor12 ^ eMinusSuper; + } + if ( bCheckForbiddenPlus && NO_VERTEX == vPlusMinus1 || + bCheckForbiddenMinus && NO_VERTEX == vPlusMinus2 ) { + return NO_VERTEX; + } + return (NO_VERTEX != vPlusMinus1)? vPlusMinus1 : vPlusMinus2; +} + + +/***********************************************************************************************/ +int bIsUnsatCarbonInASmallRing( inp_ATOM *at2, VAL_AT *pVA, int iat, BFS_Q *pbfsq, int min_ring_size ) +{ + int j, nCurRingSize, nMinRingSize; + if ( min_ring_size < 5 ) { + /* =C= in a small ring */ + if ( at2[iat].valence == 2 && + pVA[iat].cMinRingSize <= 5 && + at2[iat].chem_bonds_valence == 4 ) { + return 1; + } + } else { + if ( at2[iat].valence == 2 && + pVA[iat].cMinRingSize && + pVA[iat].cMinRingSize <= min_ring_size && + at2[iat].chem_bonds_valence == 3 ) { + return 1; + } + nCurRingSize = nMinRingSize = min_ring_size+1; + if ( (at2[iat].valence == 2 || at2[iat].valence == 3) && + at2[iat].chem_bonds_valence == at2[iat].valence+1 ) { + for ( j = 0; j < at2[iat].valence; j ++ ) { + nCurRingSize = is_bond_in_Nmax_memb_ring( at2, iat, j, pbfsq->q, + pbfsq->nAtomLevel, + pbfsq->cSource, (AT_RANK)nMinRingSize /* max ring size */ ); + if ( 0 < nCurRingSize && nCurRingSize < nMinRingSize ) { + nMinRingSize = nCurRingSize; + } + } + return (0 <= nCurRingSize)? (nMinRingSize <= min_ring_size) : nCurRingSize; + } + } + return 0; +} + + +/***********************************************************************************************/ +int FixMobileHRestoredStructure( CANON_GLOBALS *pCG, INCHI_CLOCK *ic, ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, + StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, + ALL_TC_GROUPS *pTCGroups, T_GROUP_INFO **ppt_group_info, inp_ATOM **ppat_norm, + inp_ATOM **ppat_prep, INChI *pInChI[], long num_inp, int bHasSomeFixedH, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask) +{ + /*--------- process extra or missing Fixed-H on non-tautomeric atoms ------*/ + /* at2 should be the most recently restored atom, Fixed-H */ + int i, j, k, iat, delta, tot_succes, cur_success, ret = 0; + CMP2MHINCHI c2i; + CMP2MHINCHI *pc2i = &c2i; + + EDGE_LIST AllChargeEdges, CurrEdges, CurrEdges2, CurrEdges3, TautEdges, NFlowerEdges, OtherNFlowerEdges, FixedLargeRingStereoEdges; + EDGE_LIST *pEdgeList = NULL; + + EdgeIndex e; + BNS_EDGE *pe; + Vertex v1, v2, vPlusMinus; + BNS_VERTEX *pv1, *pv2; + + Vertex vPathStart, vPathEnd; + int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; + + int nNumRunBNS = 0, forbidden_edge_mask_inv = ~forbidden_edge_mask; + + INCHI_HEAPCHK + + AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &CurrEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &NFlowerEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &CurrEdges2, EDGE_LIST_CLEAR ); + AllocEdgeList( &CurrEdges3, EDGE_LIST_CLEAR ); + AllocEdgeList( &OtherNFlowerEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &TautEdges, EDGE_LIST_CLEAR ); + + tot_succes = 0; + + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + /* taut group edges */ + for ( i = 0; i < pTCGroups->num_tgroups; i ++ ) { + pv1 = pBNS->vert + (v1=pTCGroups->pTCG[i].nVertexNumber); /* t-group vertex */ + for ( j = 0; j < pv1->num_adj_edges; j ++ ) { + /* e, pe - tautomeric atom edge; pv2 - endpoint vertex */ + /* Note: pe, pv2, v1 are not used here; they are to show how to traverse t-group */ + pv2 = pBNS->vert + (pe = pBNS->edge + (e=pv1->iedge[j]))->neighbor1; + if ( ret = AddToEdgeList( &TautEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + /* charge and flower edges */ + for ( i = 0; i < pStruct->num_atoms; i ++ ) { + if ( (e=pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + (ret = AddToEdgeList( &AllChargeEdges, e, INC_ADD_EDGE )) ) { + goto exit_function; + } + if ( (e=pVA[i].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden ) { + if ( ret = AddToEdgeList( &AllChargeEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + + /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ + if ( pVA[i].cNumValenceElectrons == 5 && !pVA[i].cMetal && /* N, P, As */ + NO_VERTEX != (j = GetChargeFlowerUpperEdge( pBNS, pVA, e ))) { + + if ( !pBNS->edge[j].forbidden && pBNS->edge[j].flow ) { + if ( ret = AddToEdgeList( &AllChargeEdges, j, INC_ADD_EDGE ) ) { + goto exit_function; + } + if ( ret = AddToEdgeList( &NFlowerEdges, j, INC_ADD_EDGE ) ) { + goto exit_function; + } + } else { + if ( ret = AddToEdgeList( &OtherNFlowerEdges, j, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + } + } + if ( forbidden_stereo_edge_mask ) { + for ( i = 0; i < pStruct->num_atoms; i ++ ) { + for ( j = 0; j < at2[i].valence; j ++ ) { + if ( pBNS->edge[k = pBNS->vert[i].iedge[j]].forbidden == forbidden_stereo_edge_mask ) { + int nMinRingSize = is_bond_in_Nmax_memb_ring( at2, i, j, pStruct->pbfsq->q, + pStruct->pbfsq->nAtomLevel, + pStruct->pbfsq->cSource, 99 /* max ring size */ ); + if ( 0 < nMinRingSize && (ret = AddToEdgeList( &FixedLargeRingStereoEdges, k, INC_ADD_EDGE ))) { + goto exit_function; + } + } + } + } + } + + INCHI_HEAPCHK + + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + INCHI_HEAPCHK + if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + + INCHI_HEAPCHK + + + + + if ( pc2i->nNumTgInChI == 1 && ( pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1 ) && + pc2i->nNumTgDBNMinusRevrs + pc2i->nNumTgNHMinusRevrs == 0 && pc2i->nNumTgOMinusInChI && + !(pTCGroups->pTCG[0].tg_RestoreFlags & TGRF_MINUS_FIRST) ) { + /*----------------------------------------------------*/ + /* case 01: restored has -O(-) and does not have N(-) */ + /* endpoints defined by the original InChI */ + /* restored has single taut. group or more */ + /* tautomeric endpoints. */ + /* Solution: move (-) from endp. -O(-) to endpoints N */ + /*----------------------------------------------------*/ + pTCGroups->pTCG[0].tg_RestoreFlags |= TGRF_MINUS_FIRST; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( !pInChI[0]->nNum_H_fixed && !pStruct->pOneINChI[0]->nNum_H_fixed ) { + goto exit_function; /* no fixed-H found */ + } + if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + if ( pc2i->nNumTgInChI == 1 && ( pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1 ) && + pc2i->nNumTgDBNMinusRevrs + pc2i->nNumTgNHMinusRevrs == 0 && pc2i->nNumTgOMinusInChI == 0 ) { + /*-------------------------------------------------------*/ + /* case 02: restored has no -O(-) and does not have N(-) */ + /* restored has single taut. group or more */ + /* tautomeric endpoints. */ + /* Solution: >N-AB=N- => >N(+)=AB-NH- (add H(+)) */ + /* Solution: >N-AB=NH => >N(+)=AB-NH2 (add H(+)) */ + /* SB_N_III DB_N_III */ + /*-------------------------------------------------------*/ + int iat_SB_N_III[MAX_DIFF_MOBH], iat_DB_N_III[MAX_DIFF_MOBH]; + int num_SB_N_III = 0, num_DB_N_III = 0, k1, k2; + CurrEdges.num_edges = 0; + cur_success = 0; + for ( i = 0; i < pStruct->num_atoms; i ++ ) { + iat = i; + if ( pVA[iat].cNumValenceElectrons == 5 && pVA[i].cPeriodicRowNumber == 1 && + !at2[iat].endpoint && !at2[iat].charge && !at2[iat].radical ) { + if ( num_DB_N_III < MAX_DIFF_MOBH && !at2[iat].num_H && + at2[iat].valence == 2 && + at2[iat].chem_bonds_valence == 3 && + !at2[iat].sb_parity[0] && /* do not eliminate stereobonds */ + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + pBNS->edge[e].cap && !pBNS->edge[e].flow ) { + /* -N= */ + iat_DB_N_III[ num_DB_N_III ++ ] = iat; + } else + if ( num_DB_N_III < MAX_DIFF_MOBH && 1 == at2[iat].num_H && + at2[iat].valence == 1 && + at2[iat].chem_bonds_valence == 2 && + !at2[iat].sb_parity[0] && /* do not eliminate stereobonds */ + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + pBNS->edge[e].cap && !pBNS->edge[e].flow ) { + /* -N= */ + iat_DB_N_III[ num_DB_N_III ++ ] = iat; + } else + if ( num_SB_N_III < MAX_DIFF_MOBH && !at2[iat].num_H && + at2[iat].valence == 3 && + at2[iat].chem_bonds_valence == 3 && + (e=pVA[iat].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + pBNS->edge[e].cap && pBNS->edge[e].flow) { + /* -N< */ + iat_SB_N_III[ num_SB_N_III ++ ] = iat; + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + } + if ( num_DB_N_III && num_SB_N_III ) { + EdgeIndex ieMinus; + BNS_EDGE *peMinus; + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + for ( i = 0; i < num_DB_N_III && !cur_success; i ++ ) { + iat = iat_DB_N_III[ i ]; + e = pBNS->edge[k1=pBNS->vert[iat].iedge[0]].flow? k1 : + pBNS->edge[k2=pBNS->vert[iat].iedge[1]].flow? k2 : NO_VERTEX; + if ( e == NO_VERTEX ) { + continue; /* should not happen */ + } + ieMinus = pVA[iat].nCMinusGroupEdge-1; + peMinus = pBNS->edge + ieMinus; + pe = pBNS->edge + e; + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->forbidden |= forbidden_edge_mask; /* fix double bond */ + peMinus->forbidden &= forbidden_edge_mask_inv; /* allow negative charge */ + delta = 1; + pe->flow -= delta; /* remove (-) from AB-O(-) */ + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 2 ) { + /* Added (-)charge -N= and (+) to -N< => nDeltaCharge == 2 */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 01 */ + + /* eliminate (-) charge and add H */ + pv1 = pBNS->vert + (v1 = peMinus->neighbor1); /* atom */ + pv2 = pBNS->vert + (v2 = peMinus->neighbor12 ^ v1);/* (=) vertex */ + /* effectively eliminate (-) edge by setting its cap=flow= 0 */ + peMinus->cap --; + peMinus->flow --; + pv1->st_edge.cap --; + pv1->st_edge.flow --; + pv2->st_edge.cap --; + pv2->st_edge.flow --; + pBNS->tot_st_flow -= 2; + pBNS->tot_st_cap -= 2; + /* add H */ + pStruct->at[iat].num_H ++; + /* register total charge increase */ + pTCGroups->total_charge ++; + pStruct->nNumRemovedProtonsByRevrs -= 1; + } + } else { + pe->forbidden &= forbidden_edge_mask_inv; + peMinus->forbidden |= forbidden_edge_mask; + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + CurrEdges.num_edges = 0; /* clear current edge list */ + + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + } + if ( pc2i->nNumTgInChI == 1 && ( pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1 ) && /* ADP */ + pc2i->nNumTgMInChI == 0 && pc2i->nNumTgNInChI && pc2i->nNumTgOInChI ) { + /*-------------------------------------------------------*/ + /* case 03: restored has N and O endpoints, no (-) endp */ + /* case 04: original has single taut. group or more */ + /* tautomeric endpoints. */ + /* Solution: 1. Move taut attachment from O to N */ + /* Solution: 2. Replace the attachment with (-) */ + /* SB_N_III DB_N_III */ + /*-------------------------------------------------------*/ + /* + int iat_SB_N_III[MAX_DIFF_MOBH], iat_DB_N_III[MAX_DIFF_MOBH]; + int num_SB_N_III = 0, num_DB_N_III = 0, k1, k2, + */ + int itg, j1, j2, bAction = 0; + BNS_VERTEX *pTg, *pvEndp, *pvEndp2, *pvCent; + Vertex vEndp, vEndp2, vCent; + BNS_EDGE *peTg, *peTg2, *peCent1, *peCent2; + EdgeIndex eTg, eTg2; + + CurrEdges.num_edges = 0; + CurrEdges2.num_edges = 0; + cur_success = 0; + + /* 1st attempt: -NH-=O => -N(-)-=O or -N=-OH => -N(-)-=O */ + for ( itg = 0; itg < pTCGroups->num_tgroups && !cur_success; itg ++ ) { + pTg = pBNS->vert + pTCGroups->pTCG[itg].nVertexNumber; + for ( i = 0; i < pTg->num_adj_edges && !cur_success; i ++ ) { + pvEndp = pBNS->vert + (vEndp = (peTg = pBNS->edge + (eTg=pTg->iedge[i]))->neighbor1); + eTg2 = -1; + if ( pVA[vEndp].cNumValenceElectrons == 6 && peTg->cap ) { + /* endpoint -OH or =O found; search for a possible centerpoint */ + for ( j1 = 0; j1 < at2[vEndp].valence && eTg2 < 0; j1 ++ ) { + peCent1 = pBNS->edge + pvEndp->iedge[j1]; /* edge from O to a centerpoint */ + pvCent = pBNS->vert + (vCent = peCent1->neighbor12 ^ vEndp); /* centerpoint */ + if ( at2[vCent].endpoint || !peCent1->cap || + peCent1->flow + (peTg->cap == peTg->flow) != 1 ) { + continue; + } + /* search for another endpoint, N, around vCent */ + for ( j2 = 0; j2 < at2[vCent].valence; j2 ++ ) { + peCent2 = pBNS->edge + pvCent->iedge[j2]; + pvEndp2 = pBNS->vert + (vEndp2 = peCent2->neighbor12 ^ vCent); + if ( !peCent2->cap || peCent2->flow+peCent1->flow != 1 || + at2[vEndp2].endpoint != itg+1 || + pVA[vEndp2].cNumValenceElectrons != 5 || + 0 > (j=pVA[vEndp2].nTautGroupEdge-1) || + (peTg2 = pBNS->edge + j)->forbidden || + peCent2->flow + (peTg2->cap == peTg2->flow) != 1 ) { + continue; + } + eTg2 = j; + break; /* found OH-C=N- or O=C-NH- */ + } + } + } + if ( eTg2 >= 0 ) { + /*-------------------------------------------- + tg tg + eTg //\ eTg2 eTg / \\eTg2 + // \ / \\ + vEndp HO--C==N vEndp2 --> vEndp O==C--NH vEndp2 + ^ ^ ^ ^ ^ ^ + eCent1 | eCent2 eCent1 | eCent2 + vCent vCent + + additional action: -OH-C=N- => O=C-NH- + -------------------------------------------*/ + if ( 0 == peTg->cap - peTg->flow && 1 == peTg2->cap - peTg2->flow && + 0 == peCent1->flow && 1 == peCent2->flow ) { + peTg->flow --; /* 03 prepare */ + peTg2->flow ++; + peCent2->flow --; + peCent1->flow ++; + bAction |= 1; /* switched H position */ + } + if ( 1 == peTg->cap - peTg->flow && 0 == peTg2->cap - peTg2->flow && + 1 == peCent1->flow && 0 == peCent2->flow ) { + /* replace -NH- with -N(-)- */ + pTCGroups->pTCG[itg].tg_num_H --; + pTCGroups->pTCG[itg].tg_num_Minus ++; + pTCGroups->pTCG[itg].tg_RestoreFlags |= TGRF_MINUS_FIRST; + pTCGroups->pTCG[itg].tg_set_Minus = vEndp2+1; + pStruct->ti.t_group[itg].num[1] ++; /* increment number of (-), keep number of taut attachments */ + pTCGroups->total_charge --; + pTCGroups->tgroup_charge --; + pStruct->nNumRemovedProtonsByRevrs += 1; + bAction |= 2; /* single NH (at2[vEndp2]) replaced with N(-) */ + cur_success ++; /* 03/04 */ + } + } + } + } + + if ( 0 == pc2i->nNumTgNHInChI+ pc2i->nNumTgNH2InChI && pc2i->nNumTgOHInChI && !cur_success ) { + /* transfer an attachement to N */ + for ( itg = 0; itg < pTCGroups->num_tgroups; itg ++ ) { + pTg = pBNS->vert + pTCGroups->pTCG[itg].nVertexNumber; + for ( i = 0; i < pTg->num_adj_edges; i ++ ) { + pvEndp = pBNS->vert + (vEndp = (peTg = pBNS->edge + (eTg=pTg->iedge[i]))->neighbor1); + if ( pVA[vEndp].cNumValenceElectrons == 6 && + at2[vEndp].valence == at2[vEndp].chem_bonds_valence && + peTg->flow && peTg->flow == peTg->cap ) { + /* endpoint -OH found; save the tautomeric group edge */ + if ( ret = AddToEdgeList( &CurrEdges, eTg, INC_ADD_EDGE ) ) { + goto exit_function; + } + } else + if ( pVA[vEndp].cNumValenceElectrons == 5 && + pVA[vEndp].cPeriodicRowNumber == 1 && + at2[vEndp].valence + 1 == at2[vEndp].chem_bonds_valence && + peTg->cap && peTg->flow + 1 == peTg->cap ) { + /* endpoint -N= or =NH found, check for -N=-OH */ + e = -1; + for ( j1 = 0; j1 < at2[vEndp].valence && e < 0; j1 ++ ) { + peCent1 = pBNS->edge + pvEndp->iedge[j1]; + if ( peCent1->flow == 1 ) { + /* double bond */ + pvCent = pBNS->vert + (vCent = peCent1->neighbor12 ^ vEndp); + if ( at2[vCent].endpoint ) + continue; + for ( j2 = 0; j2 < at2[vCent].valence; j2 ++ ) { + peCent2 = pBNS->edge + pvCent->iedge[j2]; + pvEndp2 = pBNS->vert + (vEndp2 = peCent2->neighbor12 ^ vCent); + if ( peCent2->flow || at2[vEndp2].endpoint != itg+1 || + pVA[vEndp2].cNumValenceElectrons != 6 || + 0 >= (e=pVA[vEndp2].nTautGroupEdge-1) || + pBNS->edge[e].forbidden || !pBNS->edge[e].flow ) { + e = -1; + continue; + } + /*********************/ + /* found -N=X-OH */ + /* vEndp ^ vEndp2 */ + /* vCent */ + /*********************/ + /* save this -OH taut edge */ + if ( ret = AddToEdgeList( &CurrEdges2, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + break; + } + } + } + if ( e < 0 && (ret = AddToEdgeList( &CurrEdges, eTg, INC_ADD_EDGE )) ) { + goto exit_function; + } + } + } + } + /* rearrange the flows */ + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + SetForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + SetForbiddenEdgeMask( pBNS, &CurrEdges2, forbidden_edge_mask ); + pEdgeList = CurrEdges2.num_edges? &CurrEdges2 : CurrEdges.num_edges? &CurrEdges : NULL; + + for ( i = 0; pEdgeList && i < pEdgeList->num_edges && !cur_success; i ++ ) { + pe = pBNS->edge + pEdgeList->pnEdges[i]; /* pe->flow = 1 <=> -OH */ + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); /* -OH atom */ + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); /* t-group vertex */ + /* locate the t-group */ + for ( itg = 0; itg < pTCGroups->num_tgroups; itg ++ ) { + if ( v2 == pTCGroups->pTCG[itg].nVertexNumber ) { + break; + } + } + if ( itg == pTCGroups->num_tgroups ) { + /* tgroup not found -- should not happen */ + continue; + } + + delta = 1; + pe->flow -= delta; /* add one attachment to */ + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 2 ) { + /* Added (-)charge -N= and (+) to -N< => nDeltaCharge == 2 */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 03 */ + /* replace -NH- with -N(-)- */ + pTCGroups->pTCG[itg].tg_num_H --; + pTCGroups->pTCG[itg].tg_num_Minus ++; + pTCGroups->pTCG[itg].tg_RestoreFlags |= TGRF_MINUS_FIRST; + pStruct->ti.t_group[itg].num[1] ++; + pTCGroups->total_charge --; + pTCGroups->tgroup_charge --; + pStruct->nNumRemovedProtonsByRevrs += 1; + bAction |= 4; /* H in the 1st available NH was replaced with (-) */ + } + } else { + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + } else + if ( pc2i->nNumTgNHInChI+ pc2i->nNumTgNH2InChI && pc2i->nNumTgOInChI && !cur_success ) { + /* change an attachement to N from H to (-) */ + for ( itg = 0; itg < pTCGroups->num_tgroups && !cur_success; itg ++ ) { + pTg = pBNS->vert + pTCGroups->pTCG[itg].nVertexNumber; + for ( i = 0; i < pTg->num_adj_edges && !cur_success; i ++ ) { + pvEndp2 = pBNS->vert + (vEndp2 = (peTg = pBNS->edge + pTg->iedge[i])->neighbor1); + if ( pVA[vEndp2].cNumValenceElectrons == 5 && pVA[vEndp2].cPeriodicRowNumber == 1 && + at2[vEndp2].valence == at2[vEndp2].chem_bonds_valence && + peTg->flow && peTg->flow == peTg->cap ) { + /* endpoint -NHn found; change its charge */ + cur_success ++; /* 04 */ + /* replace -NH- with -N(-)- */ + pTCGroups->pTCG[itg].tg_num_H --; + pTCGroups->pTCG[itg].tg_num_Minus ++; + pTCGroups->pTCG[itg].tg_RestoreFlags |= TGRF_MINUS_FIRST; + pTCGroups->pTCG[itg].tg_set_Minus = vEndp2 + 1; + pStruct->ti.t_group[itg].num[1] ++; + pTCGroups->total_charge --; + pTCGroups->tgroup_charge --; + pStruct->nNumRemovedProtonsByRevrs += 1; + bAction |= 8; /* manually set (-) charge to NH atom, vEndp2 */ + } + } + } + } + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( pStruct->One_ti.num_t_groups == 1 && pStruct->One_ti.t_group[0].num[1] ) { + /* this method did not work: no alt path from N(-) to =O */ + itg = 0; + if ( bAction & (8 | 2 ) ) { + /* roll back NH -> N(-) replacement; H move from OH to N is not undone */ + pTCGroups->pTCG[itg].tg_num_H ++; + pTCGroups->pTCG[itg].tg_num_Minus --; + pTCGroups->pTCG[itg].tg_RestoreFlags &= ~TGRF_MINUS_FIRST; + pTCGroups->pTCG[itg].tg_set_Minus = 0; + pStruct->ti.t_group[itg].num[1] --; + pTCGroups->total_charge ++; + pTCGroups->tgroup_charge ++; + pStruct->nNumRemovedProtonsByRevrs -= 1; + cur_success --; + } else + if ( bAction & 4 ) { + pTCGroups->pTCG[itg].tg_num_H ++; + pTCGroups->pTCG[itg].tg_num_Minus --; + pTCGroups->pTCG[itg].tg_RestoreFlags &= ~TGRF_MINUS_FIRST; + pStruct->ti.t_group[itg].num[1] --; + pTCGroups->total_charge ++; + pTCGroups->tgroup_charge ++; + pStruct->nNumRemovedProtonsByRevrs -= 1; + cur_success --; + } else { + ret = RI_ERR_PROGR; + goto exit_function; + } + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + } + + if ( pc2i->nNumTgInChI == 1 && ( pc2i->nNumEndpRevrs < pc2i->nNumEndpInChI || pc2i->nNumTgRevrs > 1 ) && /* ADP */ + pc2i->nNumTgMInChI == 0 && (pc2i->nNumTgNInChI || pc2i->nNumTgOInChI) && + NO_VERTEX != (vPlusMinus = GetPlusMinusVertex( pBNS, pTCGroups, 1, 1 )) ) { + /*---------------------------------------------------------------------------*/ + /* case 05: restored has N endpoints, no (-) endpoints */ + /* original has single taut. group or more */ + /* tautomeric endpoints. */ + /* Solution: Find -N< and allow (+) charge change */ + /* Fix all charges and taut attachments exept */ + /* =N- and =O (taut. endpoints) */ + /* Increment st_edge.cap on (+/-) vertex => add (+) charge to -N< */ + /* Increment tot. charge in other places */ + /* Increment t-group st_edge.cap */ + /* Run BNS */ + /* */ + /* (+/-)* (+/-) Result: */ + /* | || */ + /* | || - Added (+) to -N< */ + /* (+)super (+)super - Added attachment point to O */ + /* || | */ + /* || => | To make this attachment H, */ + /* (Y) (Y) increment */ + /* | || pTCGroups->pTCG[itg].tg_num_H */ + /* | || */ + /* (+)hetero (+)hetero Technical details: */ + /* \\ \ increase capacities of */ + /* N N(+) edges to (+/-) otherwise */ + /* | || flow may not be able to */ + /* *(t)--O=R. (t)==O-R. increase */ + /* */ + /* */ + /*---------------------------------------------------------------------------*/ + int itg; + BNS_VERTEX *pTg, *pvEndp; + Vertex vEndp, vTg; + BNS_EDGE *peTg; + EdgeIndex eTg; + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + + CurrEdges.num_edges = 0; + CurrEdges2.num_edges = 0; + cur_success = 0; + /* find -N< and non-taut =N- or =O */ + for ( i = 0; i < pStruct->num_atoms; i ++ ) { + iat = nCanon2AtnoRevrs[i]; + /* -N< */ + if ( !at2[iat].endpoint && !at2[iat].charge && !at2[iat].radical && !at2[iat].num_H && + pVA[i].cNumValenceElectrons == 5 && pVA[i].cPeriodicRowNumber == 1 && + 0 <= (e=pVA[iat].nCPlusGroupEdge-1) && pBNS->edge[e].flow && !pBNS->edge[e].forbidden) { + + if ( ret = AddToEdgeList( &CurrEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + if ( !CurrEdges.num_edges ) { + goto exit_case_05; + } + /* find taut -N= and =O */ + for ( itg = 0; itg < pTCGroups->num_tgroups && !cur_success; itg ++ ) { + CurrEdges2.num_edges = 0; + pTg = pBNS->vert + (vTg = pTCGroups->pTCG[itg].nVertexNumber); + for ( i = 0; i < pTg->num_adj_edges; i ++ ) { + pvEndp = pBNS->vert + (vEndp = (peTg = pBNS->edge + (eTg=pTg->iedge[i]))->neighbor1); + if ( at2[vEndp].charge || at2[vEndp].radical || peTg->cap - peTg->flow != 1 ) { + continue; + } + /* t-group edges to -N= and =O */ + if ( ret = AddToEdgeList( &CurrEdges2, eTg, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + if ( !CurrEdges2.num_edges ) { + goto exit_case_05; + } + /* fix all charge edges except -N< and all taut. edges except =O and =N- */ + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + SetForbiddenEdgeMask( pBNS, &TautEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &CurrEdges2, forbidden_edge_mask ); + delta = 1; + /* Increment st_edge.cap on (+/-) vertex */ + pBNS->vert[vPlusMinus].st_edge.cap += delta; + /* Increment st_edge.cap on t-group */ + pTg->st_edge.cap += delta; + /* total cap count */ + pBNS->tot_st_cap += 2*delta; + + v1 = vPlusMinus; + v2 = vTg; + + /* increase capacities of edges to Y */ + for ( i = 0; i < pBNS->vert[vPlusMinus].num_adj_edges; i ++ ) { + j = pBNS->edge[pBNS->vert[vPlusMinus].iedge[i]].neighbor12 ^ vPlusMinus; + for ( k = 0; k < pBNS->vert[j].num_adj_edges; k ++ ) { + pBNS->edge[pBNS->vert[j].iedge[k]].cap += delta; + } + } + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { + /* Added (+)charge to -N< => nDeltaCharge == 1 */ + /* Flow change on pe (-)charge edge (atom B-O(-)) is not known to RunBnsTestOnce()) */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 01 */ + /* update bookkeeping */ + pTCGroups->total_charge += delta; + pTCGroups->pTCG[itg].edges_cap += delta; + pTCGroups->pTCG[itg].tg_num_H += delta; + pStruct->nNumRemovedProtonsByRevrs -= delta; + } + } else { + pBNS->vert[vPlusMinus].st_edge.cap -= delta; + pTg->st_edge.cap -= delta; + /*pTCGroups->pTCG[itg].edges_cap -= delta;*/ /* ???bug??? - commented out 2006-03-22 */ + pBNS->tot_st_cap -= 2*delta; + /* decrease capacities of edges to Y */ + for ( i = 0; i < pBNS->vert[vPlusMinus].num_adj_edges; i ++ ) { + j = pBNS->edge[pBNS->vert[vPlusMinus].iedge[i]].neighbor12 ^ vPlusMinus; + for ( k = 0; k < pBNS->vert[j].num_adj_edges; k ++ ) { + pBNS->edge[pBNS->vert[j].iedge[k]].cap -= delta; + } + } + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &TautEdges, forbidden_edge_mask ); + } + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } + +exit_case_05:; + } + + while ( pc2i->nNumDiffMobH && pc2i->nChargeMobHRevrs > pc2i->nChargeMobHInChI ) { + /*----------------------------------------------------*/ + /* case 06: restored has extra H attached to -O(-) */ + /* while the chrge should be on C, most pro- */ + /* bably in a small ring.ut. group or more */ + /* tautomeric endpoints. */ + /* Solution: move (-) from O to C */ + /*----------------------------------------------------*/ + int iO, mode; + EdgeIndex e2; + BNS_EDGE *pe2; + cur_success = 0; + for ( i = 0; !cur_success && i < pc2i->len_c2at; i ++ ) { + + if ( pc2i->c2at[i].nMobHRevrs == pc2i->c2at[i].nMobHInChI + 1 && + pc2i->c2at[i].nNumHRevrs == pc2i->c2at[i].nMobHInChI && + !pc2i->c2at[i].endptInChI && !pc2i->c2at[i].endptRevrs && + at2[iO = pc2i->c2at[i].atomNumber].charge == -1 && + 0 <= (e=pVA[iO].nCMinusGroupEdge-1) && (pe=pBNS->edge+e)->flow ) { + + /* try suitable atoms C */ + /* first look for =C= in a small ring */ + + for( mode = 4; !cur_success && mode <= 8; mode ++ ) { + + if ( mode == 8 ) + mode = 99; + + for ( iat = 0; !cur_success && iat < pStruct->num_atoms; iat ++ ) { + + if ( !at2[iat].charge && !at2[iat].radical && + pVA[iat].cNumValenceElectrons == 4 && + 0 <= (e2=pVA[iat].nCMinusGroupEdge-1) && !(pe2=pBNS->edge+e2)->flow && + 0 < bIsUnsatCarbonInASmallRing( at2, pVA, iat, pStruct->pbfsq, mode ) ) { + + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + /* allow negative charge on the chosen carbon */ + pe2->forbidden &= forbidden_edge_mask_inv; + + delta = 1; + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) { + /* Added (-)charge to unsaturated C => nDeltaCharge == 2 */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + cur_success ++; /* 01 */ + tot_succes += cur_success; + } + } else { + pe->forbidden |= forbidden_edge_mask; + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } + } + } + } + } + if ( cur_success ) { + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } else { + break; + } + } + if ( pc2i->len_c2at && pc2i->nChargeMobHRevrs > pc2i->nChargeMobHInChI ) { + /*------------------------------------------------------------------*/ + /* case 07: -NO2 are to be tautomeric but they are not AND */ + /* InChI has a SINGLE tautomeric group */ + /* */ + /* (-)O (-)O */ + /* Solution: convert \ \ */ + /* N-X=...-Z(-) => N(+)=X- ...=Z */ + /* // / */ + /* O (-)O */ + /* */ + /* O O */ + /* or \\ \\ */ + /* N-X=...-Z(-) => N=X- ...=Z */ + /* // / */ + /* O (-)O */ + /* */ + /* */ + /* (a) move (-) from other tautomeric atom to O in O=N-X */ + /* or from other atom that has to be tautomeric */ + /* but is not */ + /* (b) create (+) [ion pair creation] on N as in */ + /* */ + /* OH OH */ + /* / / */ + /* -C=N => =C-N(+) */ + /* \\ \\ */ + /* O O */ + /* */ + /*------------------------------------------------------------------*/ + int num_DB_O = 0; + short iat_DB_O[MAX_DIFF_FIXH], iat_NO2[MAX_DIFF_FIXH]; + AT_NUMB *nCanon2AtnoRevrs = pStruct->nCanon2Atno[0]; + /* + AT_NUMB *nAtno2CanonRevrs = pStruct->nAtno2Canon[0]; + */ + inp_ATOM *at_Mobile_H_Revrs = (pStruct->pOne_norm_data[0] && + pStruct->pOne_norm_data[0]->at)? pStruct->pOne_norm_data[0]->at : NULL; + + int iN, one_success; + BNS_EDGE *peDB_O_Minus; + int neigh, nNumO, nNumOthers; +#define CHG_SET_WRONG_TAUT_N 0 +#define CHG_SET_WRONG_TAUT_O 1 +#define CHG_SET_WRONG_TAUT_ALL 2 +#define CHG_LAST_SET 2 /* the last index in trying */ +#define CHG_SET_O_FIXED 3 +#define CHG_SET_NUM 4 + EDGE_LIST ChangeableEdges[CHG_SET_NUM]; + memset( ChangeableEdges, 0, sizeof(ChangeableEdges) ); + /* equivalent to AllocEdgeList( &EdgeList, EDGE_LIST_CLEAR ); */ + /* + S_CHAR *nMobHInChI = pInChI[1] && pInChI[1]->nNum_H? pInChI[1]->nNum_H : + pInChI[0] && pInChI[0]->nNum_H? pInChI[0]->nNum_H : 0; + */ + CurrEdges.num_edges = 0; /* clear current edge list */ + cur_success = 0; + for ( i = 0; i < pStruct->num_atoms; i ++ ) { + iat = nCanon2AtnoRevrs[i]; + if ( /* orig. InChI info: taut in orig. InChI =O located in -NO2 that is not taut in Reconstructed InChI */ + num_DB_O < MAX_DIFF_FIXH && + pVA[iat].cNumValenceElectrons == 6 /* O, S, Se, Te */ && + (!at2[iat].endpoint /*|| pc2i->c2at[i].nMobHInChI*/) && + (e=pVA[iat].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[e].forbidden && + at2[iat].num_H == 0 && /*pc2i->c2at[i].nMobHInChI == 1 &&*/ + /* reversed structure info: */ + !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[iat].endpoint) /*|| pc2i->c2at[i].nMobHRevrs*/ && + !at2[iat].charge && + at2[iat].valence == 1 && at2[iat].chem_bonds_valence == 2 && + /* find whether it belongs to NO2 */ + pVA[iN=at2[iat].neighbor[0]].cNumValenceElectrons == 5 && + at2[iN].valence == 3 && (at2[iN].charge == 0 || at2[iN].charge == 1) && + at2[iN].chem_bonds_valence == 5 - at2[iN].charge ) { + /* find the second O */ + nNumO = nNumOthers = 0; + for ( k = 0; k < at2[iN].valence; k ++ ) { + neigh = at2[iN].neighbor[k]; + if ( neigh == iat ) { + continue; + } + if ( pVA[neigh].cNumValenceElectrons == 6 && + !at2[neigh].endpoint && + !(at_Mobile_H_Revrs && at_Mobile_H_Revrs[neigh].endpoint) && + at2[neigh].valence == 1 && at2[neigh].num_H == 0 && + at2[neigh].radical == 0 && (at2[neigh].charge == 0 || at2[neigh].charge == -1) && + at2[neigh].chem_bonds_valence - at2[neigh].charge == 2) { + nNumO ++; + } else + if ( at2[iN].bond_type[k] == BOND_TYPE_SINGLE && + at2[neigh].valence > 1 && + at2[neigh].valence < at2[neigh].chem_bonds_valence ) { + nNumOthers ++; + } + } + if ( nNumO != 1 || nNumOthers != 1 ) { + continue; + } + for ( k = 0; k < num_DB_O; k ++ ) { + if ( iat_NO2[k] == iN ) { + break; + } + } + if ( k == num_DB_O ) { + iat_NO2[num_DB_O] = iN; + iat_DB_O[num_DB_O ++] = iat; + } + /* save the =O (-)-edge to avoid interference */ + if ( ret = AddToEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e, INC_ADD_EDGE ) ) { + goto exit_case_07; + } + } + } + if ( num_DB_O ) { + /* search for falsely tautomeric negatively charged atoms N and O */ + for ( i = 0; i < pc2i->len_c2at; i ++ ) { + iat = pc2i->c2at[i].atomNumber; + if ( pc2i->c2at[i].endptRevrs && !pc2i->c2at[i].endptInChI && + pc2i->c2at[i].nAtChargeRevrs == - 1 && + 0 <= (e=pVA[iat].nCMinusGroupEdge-1) && !pBNS->edge[e].forbidden && pBNS->edge[e].flow && + 0 > FindInEdgeList( &ChangeableEdges[CHG_SET_O_FIXED], e ) ) { + if ( pc2i->c2at[i].nValElectr == 6 ) { + if (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_WRONG_TAUT_O], e, INC_ADD_EDGE ) ) { + goto exit_case_07; + } + } else + if ( pc2i->c2at[i].nValElectr == 5 ) { + if (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_WRONG_TAUT_N], e, INC_ADD_EDGE ) ) { + goto exit_case_07; + } + } + if (ret = AddToEdgeList( &ChangeableEdges[CHG_SET_WRONG_TAUT_ALL], e, INC_ADD_EDGE ) ) { + goto exit_case_07; + } + } + } + /* ------- finally, try to move charges from O=N --------------*/ + for ( i = 0; i < num_DB_O; i ++ ) { + int nDeltaChargeExpected; + one_success = 0; + delta = 1; + iat = iat_DB_O[i]; + peDB_O_Minus = pBNS->edge + (pVA[iat].nCMinusGroupEdge-1); + pe = pBNS->edge + pBNS->vert[iat].iedge[0]; + + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->forbidden |= forbidden_edge_mask; + + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + for ( k = 0; !one_success && k <= CHG_LAST_SET; k ++ ) { + if ( !ChangeableEdges[k].num_edges ) { + continue; + } + nDeltaChargeExpected = 0; + + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + RemoveForbiddenEdgeMask( pBNS, &ChangeableEdges[k], forbidden_edge_mask ); + /* allow (-) charge to move to N=O */ + peDB_O_Minus->forbidden &= forbidden_edge_mask_inv; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && + nDeltaCharge == nDeltaChargeExpected ) { + /* Move (-) charge to =O and remove it an endpoint => nDeltaCharge == 0 */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + nNumRunBNS ++; + one_success ++; /* 07 */ + } + } + INCHI_HEAPCHK + } + cur_success += one_success; + + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + pe->forbidden &= forbidden_edge_mask_inv; + + if ( !one_success ) { + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + } + } +exit_case_07: + for ( i = 0; i < CHG_SET_NUM; i ++ ) { + AllocEdgeList( &ChangeableEdges[i], EDGE_LIST_FREE ); + } + + CurrEdges.num_edges = 0; /* clear current edge list */ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + if ( !pc2i->bHasDifference ) { + goto exit_function; /* nothing to do */ + } + } +#undef CHG_SET_NOOH +#undef CHG_SET_WRONG_TAUT +#undef CHG_SET_TAUT +#undef CHG_LAST_SET +#undef CHG_SET_O_FIXED +#undef CHG_SET_NUM + } + + + +exit_function: + AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); + AllocEdgeList( &CurrEdges, EDGE_LIST_FREE ); + AllocEdgeList( &CurrEdges2, EDGE_LIST_FREE ); + AllocEdgeList( &CurrEdges3, EDGE_LIST_FREE ); + AllocEdgeList( &NFlowerEdges, EDGE_LIST_FREE ); + AllocEdgeList( &OtherNFlowerEdges, EDGE_LIST_FREE ); + AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_FREE ); + AllocEdgeList( &TautEdges, EDGE_LIST_FREE ); + + + return ret; +} + + +#endif diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichirvr6.c b/INCHI-1-SRC/INCHI_BASE/src/ichirvr6.c similarity index 93% rename from INCHI-1-SRC/INCHI_API/inchi_dll/ichirvr6.c rename to INCHI-1-SRC/INCHI_BASE/src/ichirvr6.c index 35d1e54..5063ff3 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichirvr6.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichirvr6.c @@ -1,1318 +1,1293 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - -/*#define CHECK_WIN32_VC_HEAP*/ -#include "mode.h" - -#if ( READ_INCHI_STRING == 1 ) - -#include "ichi.h" -#include "ichitime.h" - -#include "inpdef.h" -#include "ichimain.h" -#include "ichierr.h" -#include "incomdef.h" -#include "ichiring.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "util.h" - -#include "ichicomp.h" -#include "ichister.h" - -#include "ichi_bns.h" - -#include "strutil.h" - -#include "ichirvrs.h" - - -#define INC_ADD_EDGE 64 -/***********************************************************************************************/ -int FixRestoredStructureStereo( INCHI_MODE cmpInChI, ICR *icr, INCHI_MODE cmpInChI2, ICR *icr2, - ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, - StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, T_GROUP_INFO **ppt_group_info, inp_ATOM **ppat_norm, - inp_ATOM **ppat_prep, INChI *pInChI[], long num_inp, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask) -{ - /*--------- process extra or missing Fixed-H on non-tautomeric atoms ------*/ - /* at2 should be the most recently restored atom, Fixed-H */ - int i, j, k, delta, tot_succes, max_success, cur_success, ret = 0; - int err, iOrigInChI, iRevrInChI; - int j12, v1, v2, e, vRad; - BNS_VERTEX *pv1, *pv2, *pvRad; - BNS_EDGE *pe, *peRad; - EDGE_LIST AllChargeEdges, CurrEdges, NFlowerEdges, OtherNFlowerEdges, FixedStereoEdges, AllRadList; - EDGE_LIST TautMinusEdges[2]; /* 0 -> O & O(+), 1=> N & N(+) */ - - Vertex vPathStart, vPathEnd; - int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; - INChI_Stereo *pStereoInChI, *pStereo2InChI, *pStereoRevrs, *pStereo2Revrs; - - /* Stereo */ - - /* currently being processed layer */ - pStereoInChI = (pInChI[0]->StereoIsotopic && - pInChI[0]->StereoIsotopic->nNumberOfStereoBonds + - pInChI[0]->StereoIsotopic->nNumberOfStereoCenters)? - pInChI[0]->StereoIsotopic : pInChI[0]->Stereo; - - /* mobile-H layer in case of Fixed-H */ - pStereo2InChI = (pStruct->bMobileH == TAUT_YES || !pInChI[1] || - !pInChI[1]->nNumberOfAtoms || pInChI[1]->bDeleted)? - NULL: - (pInChI[1]->StereoIsotopic && - pInChI[1]->StereoIsotopic->nNumberOfStereoBonds + - pInChI[1]->StereoIsotopic->nNumberOfStereoCenters)? - pInChI[1]->StereoIsotopic : - pInChI[1]->Stereo; - - /* currently being processed layer */ - pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; - - /* mobile-H layer in case of Fixed-H */ - pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || - !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? - NULL: - (pStruct->pOneINChI[1]->StereoIsotopic && - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[1]->StereoIsotopic : - pStruct->pOneINChI[1]->Stereo; - - INCHI_HEAPCHK - - AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &CurrEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &NFlowerEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &OtherNFlowerEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &FixedStereoEdges, EDGE_LIST_CLEAR ); - AllocEdgeList( &AllRadList, EDGE_LIST_CLEAR ); - - AllocEdgeList( TautMinusEdges+0, EDGE_LIST_CLEAR ); - AllocEdgeList( TautMinusEdges+1, EDGE_LIST_CLEAR ); - - cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - - cmpInChI2 = 0; - - if ( pStruct->bMobileH == TAUT_NON ) { - /* these indexes are used to compare Mobile-H InChI */ - iOrigInChI = (pInChI[1] && pInChI[1]->nNumberOfAtoms && !pInChI[1]->bDeleted)? 1 : 0; - iRevrInChI = (pStruct->pOneINChI[1] &&pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted)? 1 : 0; - } else { - iOrigInChI = 0; - iRevrInChI = 0; - } - - memset ( icr2, 0, sizeof(*icr2) ); - if ( iRevrInChI || iOrigInChI ) { - /* additional mobile-H compare in case of Fixed-H */ - cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - } - - - if ( !(cmpInChI & IDIFF_SB) && !(cmpInChI2 & IDIFF_SB) ) { - goto exit_function; - } - /* need to temporarily remove fixing of stereogenic bonds */ - for ( i = 0; i < pStruct->num_atoms; i ++ ) { - pv1 = pBNS->vert + i; - for ( j = 0; j < at2[i].valence; j ++ ) { - pe = pBNS->edge + (e=pv1->iedge[j]); - if ( j == pe->neighbor1 ) { - /* do not store same bond 2 times */ - if ( (pe->forbidden & forbidden_stereo_edge_mask) && - (ret = AddToEdgeList( &FixedStereoEdges, e, INC_ADD_EDGE ) ) ) { - goto exit_function; - } - } - } - } - - - tot_succes = 0; - cur_success = 0; - if ( (cmpInChI & IDIF_SB_MISS) && (!cmpInChI2 || (cmpInChI2 & IDIF_SB_MISS)) && - 0 < (max_success = pBNS->tot_st_cap - pBNS->tot_st_flow) ) { - /*----------------------------------------------------*/ - /* case 01: extra stereogenic bond, radical present */ - /* X=N-O* => X=N=O and eliminate radical */ - /*----------------------------------------------------*/ - int aN; - BNS_VERTEX *pvO, *pvN; - BNS_EDGE *peNO; - - RemoveForbiddenEdgeMask( pBNS, &FixedStereoEdges, forbidden_stereo_edge_mask ); - - for ( i = 0; i < icr->num_sb_in2_only && cur_success < max_success; i ++ ) { - j12 = icr->sb_in2_only[i]; - pv1 = pBNS->vert + (v1 = pStereoInChI->nBondAtom1[j12]-1); - pv2 = pBNS->vert + (v2 = pStereoInChI->nBondAtom2[j12]-1); - for ( k = 0; k < at2[v1].valence; k ++ ) { - pe = pBNS->edge + (e = pv1->iedge[k]); - if ( v2 == (pe->neighbor12 ^ v1) ) - break; /* the edge has been found */ - } - if ( k == at2[v1].valence ) { - ret = RI_ERR_SYNTAX; - goto exit_function; - } - /* check v1 */ - pv1->st_edge.cap --; - pv1->st_edge.flow --; - pv2->st_edge.flow --; - pe->flow --; /* new radical on v2 */ - vRad = NO_VERTEX; - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - pv1->st_edge.cap ++; - pv1->st_edge.flow ++; - pv2->st_edge.flow ++; - pe->flow ++; /* remove new radical on v2 */ - - if ( ret == 1 /*&& !nDeltaH*/ && !nDeltaCharge && (v2 == vPathStart || v2 == vPathEnd) ) { - vRad = (v2 == vPathStart)? vPathEnd : vPathStart; - } else { - pv2->st_edge.cap --; - pv2->st_edge.flow --; - pv1->st_edge.flow --; - pe->flow --; /* new radical on v1 */ - vRad = NO_VERTEX; - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - pv2->st_edge.cap ++; - pv2->st_edge.flow ++; - pv1->st_edge.flow ++; - pe->flow ++; /* remove new radical on v1 */ - if ( ret == 1 /*&& !nDeltaH*/ && !nDeltaCharge && (v1 == vPathStart || v1 == vPathEnd) ) { - vRad = (v1 == vPathStart)? vPathEnd : vPathStart; - } - } - if ( vRad == NO_VERTEX ) { - continue; /* radical did not affect this bond */ - } - pvRad = pBNS->vert + vRad; - /* detect =N-O* */ - if ( pVA[vRad].cNumValenceElectrons == 6 && at2[vRad].valence == 1 && - (peRad = pBNS->edge + pvRad->iedge[0])->flow == 0 && - pVA[aN = peRad->neighbor12 ^ vRad].cNumValenceElectrons == 5 && - at2[aN].valence == 2 ) { - /*------------------------------------------------------------ - Fix Metal disconnection/normalization inconsistency : - disconnected restored - R=N(+)-M R=N--M R=N + M R=N + M - | -> || -> || -> | - O(-) O O O* <- radical - - The correct R=N + M(+) - disconnection | - would be this: O(-) - --------------------------------------------------------------*/ - pvN = pBNS->vert + aN; - pvO = pvRad; - peNO = peRad; - - /* N-O* => N=O */ - peNO->flow ++; - pvO->st_edge.flow ++; - pvN->st_edge.cap ++; - pvN->st_edge.flow ++; - pBNS->tot_st_cap += 1; - pBNS->tot_st_flow += 2; - cur_success ++; - } else { - /* all other radicals that affect stereo */ - delta = pvRad->st_edge.cap - pvRad->st_edge.flow; - pvRad->st_edge.cap -= delta; - pBNS->tot_st_cap -= delta; - } - } -/*exit_case_01:*/ - SetForbiddenEdgeMask( pBNS, &FixedStereoEdges, forbidden_stereo_edge_mask ); - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - /* - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - */ - cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - cmpInChI2 = 0; - memset ( icr2, 0, sizeof(*icr2) ); - if ( iRevrInChI || iOrigInChI ) { - /* additional mobile-H compare in case of Fixed-H */ - cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - } - - pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; - - - pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || - !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? - NULL: - (pStruct->pOneINChI[1]->StereoIsotopic && - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[1]->StereoIsotopic : - pStruct->pOneINChI[1]->Stereo; - - } - } - - cur_success = 0; - if ( !(cmpInChI & IDIF_SB_MISS) && (cmpInChI2 & IDIF_SB_MISS) && - icr2->num_sb_in2_only && - 0 < (max_success = pBNS->tot_st_cap - pBNS->tot_st_flow) ) { - /*----------------------------------------------------*/ - /* case 02: missing stereogenic bond in Mobile-H only */ - /* X=N-O* => X=N=O and eliminate radical */ - /*----------------------------------------------------*/ - int retC, ret2C, retS, ret2S; - INCHI_MODE cmpInChI_Prev, cmpInChI2_Prev; - ICR icr_Prev, icr2_Prev; - - /* blind attepmt */ - icr_Prev = *icr; - icr2_Prev = *icr2; - cmpInChI_Prev = cmpInChI; - cmpInChI2_Prev = cmpInChI2; - for ( i = AllRadList.num_edges = 0; i < pStruct->num_atoms; i ++ ) { - if ( pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow == 1 && - (ret = AddToEdgeList( &AllRadList, i, INC_ADD_EDGE ) ) ) { - goto exit_function; - } - } - for ( i = 0; i < AllRadList.num_edges; i ++ ) { - j = AllRadList.pnEdges[i]; - pBNS->vert[j].st_edge.cap -= 1; - pBNS->tot_st_cap -= 1; - } - /*-------------------------------------------------*/ - /* re-create InChI and see whether it looks better */ - /*-------------------------------------------------*/ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - /* - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - */ - cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - cmpInChI2 = 0; - memset ( icr2, 0, sizeof(*icr2) ); - if ( iRevrInChI || iOrigInChI ) { - /* additional mobile-H compare in case of Fixed-H */ - cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - } - retC = CompareIcr( icr, &icr_Prev, NULL, NULL, IDIFF_CONSTIT ); - retS = CompareIcr( icr, &icr_Prev, NULL, NULL, IDIFF_STEREO ); - ret2C = CompareIcr( icr2, &icr2_Prev, NULL, NULL, IDIFF_CONSTIT ); - ret2S = CompareIcr( icr2, &icr2_Prev, NULL, NULL, IDIFF_STEREO ); - - if ( 0 >= retC && - 0 >= retS && - 0 >= ret2C && - 0 > ret2S ) { - ; /* accept */ - } else { - /* reject */ - for ( i = 0; i < AllRadList.num_edges; i ++ ) { - j = AllRadList.pnEdges[i]; - pBNS->vert[j].st_edge.cap += 1; - pBNS->tot_st_cap += 1; - } - - /*-------------------------------------------------*/ - /* re-create InChI-- return to previous state */ - /*-------------------------------------------------*/ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - /* - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - */ - cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - cmpInChI2 = 0; - memset ( icr2, 0, sizeof(*icr2) ); - if ( iRevrInChI || iOrigInChI ) { - /* additional mobile-H compare in case of Fixed-H */ - cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - } - pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; - - - pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || - !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? - NULL: - (pStruct->pOneINChI[1]->StereoIsotopic && - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[1]->StereoIsotopic : - pStruct->pOneINChI[1]->Stereo; - } -/*exit_case_02:;*/ - } - - cur_success = 0; - if ( pStruct->bMobileH == TAUT_NON && (cmpInChI & IDIF_SB_EXTRA_UNDF) && - pStruct->endpoint ) { - /*------------------------------------------------------*/ - /* case 03: extra stereogenic bond in Fixed-H only */ - /* in Mobile-H this bond is not stereogenic. */ - /* Since this bond parity is not known, it is UNDEFINED */ - /*------------------------------------------------------*/ - int bDone, num_endpoints; - - TautMinusEdges[0].num_edges = 0; - TautMinusEdges[1].num_edges = 0; - AllChargeEdges.num_edges = 0; - /* in1 => in restored structure; in2 => in original InChI */ - for ( i = 0; i < icr->num_sb_undef_in1_only; i ++ ) { - j12 = icr->sb_undef_in1_only[i]; - pv1 = pBNS->vert + (v1 = pStereoRevrs->nBondAtom1[j12]-1); - pv2 = pBNS->vert + (v2 = pStereoRevrs->nBondAtom2[j12]-1); - - if ( pStereo2Revrs ) { - /* reject if it is extra in Mobile-H also */ - if ( icr2->num_sb_undef_in1_only ) { - for ( j = 0; j < icr2->num_sb_undef_in1_only; j ++ ) { - k = icr2->sb_undef_in1_only[j]; - if ( v1 == pStereo2Revrs->nBondAtom1[k] && - v2 == pStereo2Revrs->nBondAtom2[k] ) { - break; - } - } - if ( j < icr->num_sb_in1_only ) { - continue; /* extra stereobond in Mobile H also */ - } - } - } - /* reject if it is a stereobond in Mobile-H also */ - if ( pStereo2InChI && pStereo2InChI->nNumberOfStereoBonds ) { - for ( j = 0; j < pStereo2InChI->nNumberOfStereoBonds; j ++ ) { - if ( v1 == pStereo2InChI->nBondAtom1[j] && - v2 == pStereo2InChI->nBondAtom1[j] ) { - break; - } - } - if ( j < pStereo2InChI->nNumberOfStereoBonds ) { - continue; /* ignore this extra stereo bond: it is in Mobile-H */ - } - } - /* find the edge between v1 and v2 */ - for ( k = 0; k < at2[v1].valence; k ++ ) { - pe = pBNS->edge + (e = pv1->iedge[k]); - if ( v2 == (pe->neighbor12 ^ v1) ) - break; /* the edge has been found */ - } - if ( k == at2[v1].valence ) { - ret = RI_ERR_SYNTAX; - goto exit_function; - } - /* Fix all charges except negative charges on tautomeric endpoints */ - if ( !AllChargeEdges.num_edges && !TautMinusEdges[0].num_edges && !TautMinusEdges[1].num_edges ) { - for ( j = 0; j < pStruct->num_atoms; j ++ ) { - if ( (k=pVA[j].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { - if ( !pStruct->endpoint[j] ) { - if (ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else - if ( pVA[j].cNumValenceElectrons == 6 ) { - /* O */ - if (ret = AddToEdgeList( TautMinusEdges+0, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else { - /* N */ - if (ret = AddToEdgeList( TautMinusEdges+1, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - if ( (k=pVA[j].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { - if ( ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ - if ( pVA[j].cNumValenceElectrons == 5 && !pVA[j].cMetal && /* N, P, As */ - NO_VERTEX != (k = GetChargeFlowerUpperEdge( pBNS, pVA, k ))) { - - if ( !pBNS->edge[j].forbidden && pBNS->edge[k].flow ) { - if ( ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - } - } - } - if ( !pe->flow ) - continue; - /* fix all charges except tautomeric; first allow only O, then only N, finally both N and O */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - for ( k = 1, bDone = 0; k < 4 && !bDone; k ++ ) { - /* fix tautomeric charges */ - num_endpoints = (TautMinusEdges+0)->num_edges + (TautMinusEdges+1)->num_edges; - if ( k == 2 ) { - /* fix charges on O */ - SetForbiddenEdgeMask( pBNS, TautMinusEdges+0, forbidden_edge_mask ); - num_endpoints -= (TautMinusEdges+0)->num_edges; - } - if ( k == 1 ) { - SetForbiddenEdgeMask( pBNS, TautMinusEdges+1, forbidden_edge_mask ); - num_endpoints -= (TautMinusEdges+1)->num_edges; - } - if ( num_endpoints >= 2 ) { - delta = 1; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; /* fix stereobond */ - pe->flow -= delta; /* decrement stereobond order */ - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { - /* Negative charge has been moved, no change in number of charges */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - (*pnNumRunBNS) ++; - cur_success ++; /* 01 */ - bDone = 1; - } - } else { - pe->forbidden &= ~forbidden_edge_mask; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - } - /* unfix tautomeric charges */ - if ( k == 2 ) - RemoveForbiddenEdgeMask( pBNS, TautMinusEdges+0, forbidden_edge_mask ); - if ( k == 1 ) - RemoveForbiddenEdgeMask( pBNS, TautMinusEdges+1, forbidden_edge_mask ); - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } -/*exit_case_03:*/ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - /* - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - */ - cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - cmpInChI2 = 0; - memset ( icr2, 0, sizeof(*icr2) ); - if ( iRevrInChI || iOrigInChI ) { - /* additional mobile-H compare in case of Fixed-H */ - cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - } - pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; - - - pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || - !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? - NULL: - (pStruct->pOneINChI[1]->StereoIsotopic && - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[1]->StereoIsotopic : - pStruct->pOneINChI[1]->Stereo; - - } - } - - cur_success = 0; - if ( (cmpInChI & IDIF_SB_EXTRA_UNDF) ) { - /*------------------------------------------------------*/ - /* case 04: extra stereogenic bond */ - /* Since this bond parity is not known, it is UNDEFINED */ - /*------------------------------------------------------*/ - int bDone, num_endpoints; - - TautMinusEdges[0].num_edges = 0; - TautMinusEdges[1].num_edges = 0; - AllChargeEdges.num_edges = 0; - /* in1 => in restored structure; in2 => in original InChI */ - for ( i = 0; i < icr->num_sb_undef_in1_only; i ++ ) { - j12 = icr->sb_undef_in1_only[i]; - pv1 = pBNS->vert + (v1 = pStereoRevrs->nBondAtom1[j12]-1); - pv2 = pBNS->vert + (v2 = pStereoRevrs->nBondAtom2[j12]-1); - - /* find the edge between v1 and v2 */ - for ( k = 0; k < at2[v1].valence; k ++ ) { - pe = pBNS->edge + (e = pv1->iedge[k]); - if ( v2 == (pe->neighbor12 ^ v1) ) - break; /* the edge has been found */ - } - if ( k == at2[v1].valence ) { - ret = RI_ERR_SYNTAX; - goto exit_function; - } - if ( pStereo2Revrs ) { - /* reject if it is not extra in Mobile-H also */ - if ( icr2->num_sb_undef_in1_only ) { - for ( j = 0; j < icr2->num_sb_undef_in1_only; j ++ ) { - k = icr2->sb_undef_in1_only[j]; - if ( v1 == pStereo2Revrs->nBondAtom1[k] && - v2 == pStereo2Revrs->nBondAtom2[k] ) { - break; - } - } - if ( j == icr->num_sb_in1_only ) { - continue; /* extra stereobond only in Fixed-H, not in Mobile H also */ - } - } - } - - /* Fix all charges except negative charges on tautomeric endpoints */ - if ( !AllChargeEdges.num_edges && !TautMinusEdges[0].num_edges && !TautMinusEdges[1].num_edges ) { - for ( j = 0; j < pStruct->num_atoms; j ++ ) { - if ( (k=pVA[j].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { - if (ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - if ( (k=pVA[j].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { - int bMayBeUnfixed = !at2[j].num_H && !(pStruct->endpoint && pStruct->endpoint[j]); - if ( bMayBeUnfixed && pVA[j].cNumValenceElectrons == 6 || - pVA[j].cNumValenceElectrons == 5 && pVA[j].cPeriodicRowNumber > 1 ) { - /* O & P */ - if (ret = AddToEdgeList( TautMinusEdges+0, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else - if ( bMayBeUnfixed && - pVA[j].cNumValenceElectrons == 5 && pVA[j].cPeriodicRowNumber == 1 ) { - /* N */ - if (ret = AddToEdgeList( TautMinusEdges+1, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } else { - if ( ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ - if ( pVA[j].cNumValenceElectrons == 5 && !pVA[j].cMetal && /* N, P, As */ - NO_VERTEX != (k = GetChargeFlowerUpperEdge( pBNS, pVA, k ))) { - if ( !pBNS->edge[j].forbidden && pBNS->edge[k].flow ) { - if ( ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - } - } - } - if ( !pe->flow ) - continue; - /* fix all charges except tautomeric; first allow only O, then only N, finally both N and O */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - for ( k = 1, bDone = 0; k < 4 && !bDone; k ++ ) { - /* fix positive charges on heteroatoms */ - num_endpoints = (TautMinusEdges+0)->num_edges + (TautMinusEdges+1)->num_edges; - if ( k == 2 ) { - /* fix charges on O */ - SetForbiddenEdgeMask( pBNS, TautMinusEdges+0, forbidden_edge_mask ); - num_endpoints -= (TautMinusEdges+0)->num_edges; - } - if ( k == 1 ) { - /* fix charges on N */ - SetForbiddenEdgeMask( pBNS, TautMinusEdges+1, forbidden_edge_mask ); - num_endpoints -= (TautMinusEdges+1)->num_edges; - } - if ( num_endpoints >= 2 ) { - delta = 1; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - pe->forbidden |= forbidden_edge_mask; /* fix stereobond */ - pe->flow -= delta; /* decrement stereobond order */ - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { - /* Negative charge has been moved, no change in number of charges */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - (*pnNumRunBNS) ++; - cur_success ++; /* 01 */ - bDone = 1; - } - } else { - pe->forbidden &= ~forbidden_edge_mask; - pe->flow += delta; - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - } - /* unfix tautomeric charges */ - if ( k == 2 ) - RemoveForbiddenEdgeMask( pBNS, TautMinusEdges+0, forbidden_edge_mask ); - if ( k == 1 ) - RemoveForbiddenEdgeMask( pBNS, TautMinusEdges+1, forbidden_edge_mask ); - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); - } -/*exit_case_04:*/ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - /* - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - */ - cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - cmpInChI2 = 0; - memset ( icr2, 0, sizeof(*icr2) ); - if ( iRevrInChI || iOrigInChI ) { - /* additional mobile-H compare in case of Fixed-H */ - cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - } - pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; - - - pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || - !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? - NULL: - (pStruct->pOneINChI[1]->StereoIsotopic && - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[1]->StereoIsotopic : - pStruct->pOneINChI[1]->Stereo; - - } - } - - cur_success = 0; - if ( pStruct->bMobileH == TAUT_YES && - (cmpInChI & IDIF_SB_EXTRA_UNDF && - !pStruct->ti.num_t_groups) - /*pStruct->bMobileH == TAUT_NON && (cmpInChI2 & IDIF_SB_EXTRA_UNDF)*/) { - /*----------------------------------------------------------*/ - /* case 05: extra stereogenic bond on =NH2(+), (B, Mobile-H)*/ - /* H H */ - /* original: N(+)=-N< -> N--==N/ */ - /* (A) H */ - /* double bond is marked as */ - /* not stereogenic due to */ - /* its change during proton */ - /* removal => No Stereo bond */ - /* (=NH may be tautomeric) */ - /* */ - /* H H */ - /* original: N=-N(+)< -> N--==N/ */ - /* (B) H */ - /* double bond was not */ - /* changed during proton */ - /* In Fixed-H this bond removal => Undef Stereo */ - /* may not be stereogenic (=NH is not tautomeric) */ - /* (a) due to (+) movement */ - /* (b) due to symmetry (2H), even if isotopic */ - /* */ - /* Fixed-H: move (+) to or from NH2 for Undef or No stereo */ - /* respectively */ - /* Mobile-H: Add H(+) to =NH and move the charge to =N- */ - /* to eliminate Undef stereo */ - /* Move charge from N to -NH2 to create */ - /* Undef Stereo */ - /* Since this bond parity is not known, it is UNDEFINED */ - /* */ - /* Solution: Add H(+) to =NH and move charge to -N= */ - /* */ - /*----------------------------------------------------------*/ - int aN, aC, i1, i2, vPlusMinus; - AllChargeEdges.num_edges = 0; - /* in1 => in restored structure; in2 => in original InChI */ - for ( i = 0; i < icr->num_sb_undef_in1_only; i ++ ) { - j12 = icr->sb_undef_in1_only[i]; - pv1 = pBNS->vert + (v1 = pStereoRevrs->nBondAtom1[j12]-1); - pv2 = pBNS->vert + (v2 = pStereoRevrs->nBondAtom2[j12]-1); - /* indicators of -NH: */ - i1 = at2[v1].valence == 1 && at2[v1].num_H == 1 && !at2[v1].endpoint && - pVA[v1].cNumValenceElectrons == 5 && pVA[v1].cPeriodicRowNumber == 1; - i2 = at2[v2].valence == 1 && at2[v2].num_H == 1 && !at2[v2].endpoint && - pVA[v2].cNumValenceElectrons == 5 && pVA[v2].cPeriodicRowNumber == 1; - if ( !i1 && !i2 || i1 && i2 ) { - continue; - } - /* find the edge between v1 and v2 */ - for ( k = 0; k < at2[v1].valence; k ++ ) { - pe = pBNS->edge + (e = pv1->iedge[k]); - if ( v2 == (pe->neighbor12 ^ v1) ) - break; /* the edge has been found */ - } - if ( k == at2[v1].valence ) { - ret = RI_ERR_SYNTAX; - goto exit_function; - } - if ( pe->flow != 1 ) { - continue; /* already charged */ - } - aN = i1? v1 : v2; /* -NH atom */ - aC = i1? v2 : v1; /* neighbor */ - /* Replace =NH with -NH2 - Create such a charge on some -N< that may be moved to NH2 to remove H(+): - transformation: - from: HN=C-=-N=(+vert)-Y=(+super)-(+/-) - to: 2HN-C*-=-N=(+vert)-Y=(+super)-(+/-)* - Run BNS to obtain: - 2HN-C=-=N(+)-(+vert)=Y-(+super)=(+/-) - */ - vPlusMinus = GetPlusMinusVertex( pBNS, pTCGroups, 1, 0 ); - if ( NO_VERTEX == vPlusMinus ) { - break; /* cannot do anything */ - } - /* increase edges to -Y-(+/-)-Y- capacities */ - delta = 1; - for ( i1 = 0; i1 < pBNS->vert[vPlusMinus].num_adj_edges; i1 ++ ) { - i2 = pBNS->edge[pBNS->vert[vPlusMinus].iedge[i1]].neighbor12 ^ vPlusMinus; - for ( k = 0; k < pBNS->vert[i2].num_adj_edges; k ++ ) { - pBNS->edge[pBNS->vert[i2].iedge[k]].cap += delta; - } - } - /* Fix all charges except (+) on -N< */ - if ( !AllChargeEdges.num_edges ) { - for ( j = 0; j < pStruct->num_atoms; j ++ ) { - if ( (k=pVA[j].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { - if (ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - if ( (k=pVA[j].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { - if ( pVA[j].cNumValenceElectrons == 5 && pVA[j].cPeriodicRowNumber == 1 && - !at2[j].num_H && at2[j].valence == 3 && - !(at2[j].endpoint || pStruct->endpoint && pStruct->endpoint[j]) ) { - ; /* do not fix -N< or =N(+)< */ - } else { - /* all others */ - if (ret = AddToEdgeList( TautMinusEdges+0, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ - if ( pVA[j].cNumValenceElectrons == 5 && !pVA[j].cMetal && /* N, P, As */ - NO_VERTEX != (k = GetChargeFlowerUpperEdge( pBNS, pVA, k ))) { - if ( !pBNS->edge[j].forbidden && pBNS->edge[k].flow ) { - if ( ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - } - } - } - /* Make bond to =NH single, add radical to aC */ - pe->flow -= delta; /* make single bond */ - pBNS->vert[aN].st_edge.flow -= delta; - pBNS->vert[aN].st_edge.cap -= delta; /* avoid radical on N */ - pBNS->vert[aC].st_edge.flow -= delta; /* create radical on C */ - pBNS->vert[vPlusMinus].st_edge.cap += delta; /* create radical on (+/-) */ - pBNS->tot_st_flow -= 2*delta; - /* fix C-NH bond */ - if ( ret = AddToEdgeList( &AllChargeEdges, e, INC_ADD_EDGE ) ) { - goto exit_function; - } - /* pBNS->tot_st_cap is unchanged */ - /* find all aC edges except pe to fix them */ - /* 2. Check whether it would work and do if it would */ - SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );/* fix aC edges */ - pe->cap ++; - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == vPlusMinus && vPathStart == aC || - vPathEnd == aC && vPathStart == vPlusMinus) && nDeltaCharge == 1 ) { - /* Negative charge has been moved, no change in number of charges */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - (*pnNumRunBNS) ++; - /* 3. Add H to -NH and register increaded charge */ - pStruct->at[aN].num_H ++; - pTCGroups->total_charge ++; - cur_success ++; /* 01 */ - } - } else { - pe->flow += delta; /* make single bond */ - pBNS->vert[aN].st_edge.flow += delta; - pBNS->vert[aN].st_edge.cap += delta; /* avoid radical on N */ - pBNS->vert[aC].st_edge.flow += delta; /* create radical on C */ - pBNS->vert[vPlusMinus].st_edge.cap -= delta; /* create radical on (+/-) */ - pBNS->tot_st_flow += 2*delta; - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );/* fix aC edges */ - AllChargeEdges.num_edges --; /* remove pe from the list */ - CurrEdges.num_edges = 0; - continue; /* should not happen */ - } - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );/* fix aC edges */ - AllChargeEdges.num_edges --; /* remove pe from the list */ - CurrEdges.num_edges = 0; - } -/*exit_case_05:*/ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - /* - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - */ - cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - cmpInChI2 = 0; - memset ( icr2, 0, sizeof(*icr2) ); - if ( iRevrInChI || iOrigInChI ) { - /* additional mobile-H compare in case of Fixed-H */ - cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - } - pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; - - - pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || - !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? - NULL: - (pStruct->pOneINChI[1]->StereoIsotopic && - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[1]->StereoIsotopic : - pStruct->pOneINChI[1]->Stereo; - - } - } - - cur_success = 0; - if ( pStruct->bMobileH == TAUT_NON && pStereo2Revrs /* added check 2006-04-05 */ && - (cmpInChI2 & IDIF_SB_EXTRA_UNDF && - !pStruct->ti.num_t_groups) - /*pStruct->bMobileH == TAUT_NON && (cmpInChI2 & IDIF_SB_EXTRA_UNDF)*/) { - /*----------------------------------------------------------*/ - /* case 06: extra stereogenic bond on =NH2(+), (B, Fixed-H) */ - /* H H =========== */ - /* original: N(+)=-N< -> N--==N(+)< */ - /* (A) H H */ - /* double bond in Mobile-H */ - /* layer has Undef stereo */ - /* */ - /* */ - /* Fixed-H: move (+) to or from NH2 for Undef or No stereo */ - /* respectively */ - /* Mobile-H: Add H(+) to =NH and move the charge to =N- */ - /* to eliminate Undef stereo */ - /* Move charge from N to -NH2 to create */ - /* Undef Stereo */ - /* Since this bond parity is not known, it is UNDEFINED */ - /* */ - /* Solution: Move (+) from -NH2(+) to othe -N< */ - /* */ - /*----------------------------------------------------------*/ - int aN, aC, i1, i2, ePlus; - BNS_EDGE *pePlus; - AllChargeEdges.num_edges = 0; - /* in1 => in restored structure; in2 => in original InChI */ - for ( i = 0; i < icr2->num_sb_undef_in1_only; i ++ ) { - j12 = icr2->sb_undef_in1_only[i]; - pv1 = pBNS->vert + (v1 = pStereo2Revrs->nBondAtom1[j12]-1); - pv2 = pBNS->vert + (v2 = pStereo2Revrs->nBondAtom2[j12]-1); - /* indicators of -NH: */ - i1 = at2[v1].valence == 1 && at2[v1].num_H == 2 && !at2[v1].endpoint && - pVA[v1].cNumValenceElectrons == 5 && pVA[v1].cPeriodicRowNumber == 1; - i2 = at2[v2].valence == 1 && at2[v2].num_H == 2 && !at2[v2].endpoint && - pVA[v2].cNumValenceElectrons == 5 && pVA[v2].cPeriodicRowNumber == 1; - if ( !i1 && !i2 || i1 && i2 ) { - continue; - } - /* find the edge between v1 and v2 */ - for ( k = 0; k < at2[v1].valence; k ++ ) { - pe = pBNS->edge + (e = pv1->iedge[k]); - if ( v2 == (pe->neighbor12 ^ v1) ) - break; /* the edge has been found */ - } - if ( k == at2[v1].valence ) { - ret = RI_ERR_SYNTAX; - goto exit_function; - } - if ( pe->flow != 1 ) { - continue; /* already charged */ - } - aN = i1? v1 : v2; /* -NH atom */ - aC = i1? v2 : v1; /* neighbor */ - if ( 0 > (ePlus = pVA[aN].nCPlusGroupEdge-1) || - (pePlus = pBNS->edge + ePlus)->flow || /* must be (+) charged */ - pePlus->forbidden ) { - continue; - } - /* Move (+) from =NH2(+) to some other -N< - */ - /* Fix all charges except (+) on -N< */ - if ( !AllChargeEdges.num_edges ) { - for ( j = 0; j < pStruct->num_atoms; j ++ ) { - if ( (k=pVA[j].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { - if (ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - if ( (k=pVA[j].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { - if ( pVA[j].cNumValenceElectrons == 5 && pVA[j].cPeriodicRowNumber == 1 && - !at2[j].num_H && at2[j].valence == 3 && - !(at2[j].endpoint || pStruct->endpoint && pStruct->endpoint[j]) ) { - ; /* do not fix -N< or =N(+)< */ - } else { - /* all others */ - if (ret = AddToEdgeList( TautMinusEdges+0, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ - if ( pVA[j].cNumValenceElectrons == 5 && !pVA[j].cMetal && /* N, P, As */ - NO_VERTEX != (k = GetChargeFlowerUpperEdge( pBNS, pVA, k ))) { - if ( !pBNS->edge[j].forbidden && pBNS->edge[k].flow ) { - if ( ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { - goto exit_function; - } - } - } - } - } - } - /* pePlus edge is already fixed; unfix it */ - /* To decrement (+) on =NH2(+) decrement its double bond order */ - delta = 1; - if ( !pe->flow ) - continue; - pv1 = pBNS->vert + (v1 = pe->neighbor1); - pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); - - delta = 1; - pe->flow -= delta; - pv1->st_edge.flow -= delta; - pv2->st_edge.flow -= delta; - pBNS->tot_st_flow -= 2*delta; - - pe->forbidden |= forbidden_edge_mask; - pePlus->forbidden &= ~forbidden_edge_mask; - - ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, - &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); - - if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || - vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { - /* (+)charge was just moved, no change in number of charges */ - ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); - if ( ret > 0 ) { - (*pnNumRunBNS) ++; - cur_success ++; /* 01 */ - } - } else { - pe->flow += delta; /* roll back */ - pv1->st_edge.flow += delta; - pv2->st_edge.flow += delta; - pBNS->tot_st_flow += 2*delta; - } - pe->forbidden &= ~forbidden_edge_mask; - RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );/* fix aC edges */ - } -/*exit_case_06:*/ - if ( cur_success ) { - tot_succes += cur_success; - /* recalculate InChI from the structure */ - if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, - ppt_group_info, ppat_norm, ppat_prep ) ) ) { - goto exit_function; - } - if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { - goto exit_function; - } - /* - if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { - goto exit_function; - } - */ - cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr2, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - cmpInChI2 = 0; - memset ( icr2, 0, sizeof(*icr2) ); - if ( iRevrInChI || iOrigInChI ) { - /* additional mobile-H compare in case of Fixed-H */ - cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); - if ( cmpInChI & IDIF_PROBLEM ) { - ret = RI_ERR_PROGR; /* severe restore problem */ - goto exit_function; - } - if ( err ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - } - pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; - - - pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || - !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? - NULL: - (pStruct->pOneINChI[1]->StereoIsotopic && - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + - pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? - pStruct->pOneINChI[1]->StereoIsotopic : - pStruct->pOneINChI[1]->Stereo; - - } - } - - -exit_function: - SetForbiddenEdgeMask( pBNS, &FixedStereoEdges, forbidden_stereo_edge_mask ); - AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); - AllocEdgeList( &CurrEdges, EDGE_LIST_FREE ); - AllocEdgeList( &NFlowerEdges, EDGE_LIST_FREE ); - AllocEdgeList( &OtherNFlowerEdges, EDGE_LIST_FREE ); - AllocEdgeList( &FixedStereoEdges, EDGE_LIST_FREE ); - AllocEdgeList( &AllRadList, EDGE_LIST_FREE ); /* eliminate memory leak */ - AllocEdgeList( TautMinusEdges+0, EDGE_LIST_FREE ); - AllocEdgeList( TautMinusEdges+1, EDGE_LIST_FREE ); - - return ret; -} -#endif +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include + +/*#define CHECK_WIN32_VC_HEAP*/ + +#include "mode.h" + +#if ( READ_INCHI_STRING == 1 ) + +#include "ichitime.h" +#include "ichicant.h" +#include "ichirvrs.h" + + +#define INC_ADD_EDGE 64 +/***********************************************************************************************/ +int FixRestoredStructureStereo( struct tagCANON_GLOBALS *pCG, INCHI_CLOCK *ic, INCHI_MODE cmpInChI, ICR *icr, INCHI_MODE cmpInChI2, ICR *icr2, + ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, + StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, + ALL_TC_GROUPS *pTCGroups, T_GROUP_INFO **ppt_group_info, inp_ATOM **ppat_norm, + inp_ATOM **ppat_prep, INChI *pInChI[], long num_inp, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask) +{ + /*--------- process extra or missing Fixed-H on non-tautomeric atoms ------*/ + /* at2 should be the most recently restored atom, Fixed-H */ + int i, j, k, delta, tot_succes, max_success, cur_success, ret = 0; + int err, iOrigInChI, iRevrInChI; + int j12, v1, v2, e, vRad; + BNS_VERTEX *pv1, *pv2, *pvRad; + BNS_EDGE *pe, *peRad; + EDGE_LIST AllChargeEdges, CurrEdges, NFlowerEdges, OtherNFlowerEdges, FixedStereoEdges, AllRadList; + EDGE_LIST TautMinusEdges[2]; /* 0 -> O & O(+), 1=> N & N(+) */ + + Vertex vPathStart, vPathEnd; + int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms; + INChI_Stereo *pStereoInChI, *pStereo2InChI, *pStereoRevrs, *pStereo2Revrs; + + /* Stereo */ + + /* currently being processed layer */ + pStereoInChI = (pInChI[0]->StereoIsotopic && + pInChI[0]->StereoIsotopic->nNumberOfStereoBonds + + pInChI[0]->StereoIsotopic->nNumberOfStereoCenters)? + pInChI[0]->StereoIsotopic : pInChI[0]->Stereo; + + /* mobile-H layer in case of Fixed-H */ + pStereo2InChI = (pStruct->bMobileH == TAUT_YES || !pInChI[1] || + !pInChI[1]->nNumberOfAtoms || pInChI[1]->bDeleted)? + NULL: + (pInChI[1]->StereoIsotopic && + pInChI[1]->StereoIsotopic->nNumberOfStereoBonds + + pInChI[1]->StereoIsotopic->nNumberOfStereoCenters)? + pInChI[1]->StereoIsotopic : + pInChI[1]->Stereo; + + /* currently being processed layer */ + pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && + pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + + pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? + pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; + + /* mobile-H layer in case of Fixed-H */ + pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || + !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? + NULL: + (pStruct->pOneINChI[1]->StereoIsotopic && + pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + + pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? + pStruct->pOneINChI[1]->StereoIsotopic : + pStruct->pOneINChI[1]->Stereo; + + INCHI_HEAPCHK + + AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &CurrEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &NFlowerEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &OtherNFlowerEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &FixedStereoEdges, EDGE_LIST_CLEAR ); + AllocEdgeList( &AllRadList, EDGE_LIST_CLEAR ); + + AllocEdgeList( TautMinusEdges+0, EDGE_LIST_CLEAR ); + AllocEdgeList( TautMinusEdges+1, EDGE_LIST_CLEAR ); + + cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); + if ( cmpInChI & IDIF_PROBLEM ) { + ret = RI_ERR_PROGR; /* severe restore problem */ + goto exit_function; + } + if ( err ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + + cmpInChI2 = 0; + + if ( pStruct->bMobileH == TAUT_NON ) { + /* these indexes are used to compare Mobile-H InChI */ + iOrigInChI = (pInChI[1] && pInChI[1]->nNumberOfAtoms && !pInChI[1]->bDeleted)? 1 : 0; + iRevrInChI = (pStruct->pOneINChI[1] &&pStruct->pOneINChI[1]->nNumberOfAtoms && !pStruct->pOneINChI[1]->bDeleted)? 1 : 0; + } else { + iOrigInChI = 0; + iRevrInChI = 0; + } + + memset ( icr2, 0, sizeof(*icr2) ); + if ( iRevrInChI || iOrigInChI ) { + /* additional mobile-H compare in case of Fixed-H */ + cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); + if ( cmpInChI & IDIF_PROBLEM ) { + ret = RI_ERR_PROGR; /* severe restore problem */ + goto exit_function; + } + if ( err ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + } + + + if ( !(cmpInChI & IDIFF_SB) && !(cmpInChI2 & IDIFF_SB) ) { + goto exit_function; + } + /* need to temporarily remove fixing of stereogenic bonds */ + for ( i = 0; i < pStruct->num_atoms; i ++ ) { + pv1 = pBNS->vert + i; + for ( j = 0; j < at2[i].valence; j ++ ) { + pe = pBNS->edge + (e=pv1->iedge[j]); + if ( j == pe->neighbor1 ) { + /* do not store same bond 2 times */ + if ( (pe->forbidden & forbidden_stereo_edge_mask) && + (ret = AddToEdgeList( &FixedStereoEdges, e, INC_ADD_EDGE ) ) ) { + goto exit_function; + } + } + } + } + + + tot_succes = 0; + cur_success = 0; + if ( (cmpInChI & IDIF_SB_MISS) && (!cmpInChI2 || (cmpInChI2 & IDIF_SB_MISS)) && + 0 < (max_success = pBNS->tot_st_cap - pBNS->tot_st_flow) ) { + /*----------------------------------------------------*/ + /* case 01: extra stereogenic bond, radical present */ + /* X=N-O* => X=N=O and eliminate radical */ + /*----------------------------------------------------*/ + int aN; + BNS_VERTEX *pvO, *pvN; + BNS_EDGE *peNO; + + RemoveForbiddenEdgeMask( pBNS, &FixedStereoEdges, forbidden_stereo_edge_mask ); + + for ( i = 0; i < icr->num_sb_in2_only && cur_success < max_success; i ++ ) { + j12 = icr->sb_in2_only[i]; + pv1 = pBNS->vert + (v1 = pStereoInChI->nBondAtom1[j12]-1); + pv2 = pBNS->vert + (v2 = pStereoInChI->nBondAtom2[j12]-1); + for ( k = 0; k < at2[v1].valence; k ++ ) { + pe = pBNS->edge + (e = pv1->iedge[k]); + if ( v2 == (pe->neighbor12 ^ v1) ) + break; /* the edge has been found */ + } + if ( k == at2[v1].valence ) { + ret = RI_ERR_SYNTAX; + goto exit_function; + } + /* check v1 */ + pv1->st_edge.cap --; + pv1->st_edge.flow --; + pv2->st_edge.flow --; + pe->flow --; /* new radical on v2 */ + vRad = NO_VERTEX; + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + pv1->st_edge.cap ++; + pv1->st_edge.flow ++; + pv2->st_edge.flow ++; + pe->flow ++; /* remove new radical on v2 */ + + if ( ret == 1 /*&& !nDeltaH*/ && !nDeltaCharge && (v2 == vPathStart || v2 == vPathEnd) ) { + vRad = (v2 == vPathStart)? vPathEnd : vPathStart; + } else { + pv2->st_edge.cap --; + pv2->st_edge.flow --; + pv1->st_edge.flow --; + pe->flow --; /* new radical on v1 */ + vRad = NO_VERTEX; + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + pv2->st_edge.cap ++; + pv2->st_edge.flow ++; + pv1->st_edge.flow ++; + pe->flow ++; /* remove new radical on v1 */ + if ( ret == 1 /*&& !nDeltaH*/ && !nDeltaCharge && (v1 == vPathStart || v1 == vPathEnd) ) { + vRad = (v1 == vPathStart)? vPathEnd : vPathStart; + } + } + if ( vRad == NO_VERTEX ) { + continue; /* radical did not affect this bond */ + } + pvRad = pBNS->vert + vRad; + /* detect =N-O* */ + if ( pVA[vRad].cNumValenceElectrons == 6 && at2[vRad].valence == 1 && + (peRad = pBNS->edge + pvRad->iedge[0])->flow == 0 && + pVA[aN = peRad->neighbor12 ^ vRad].cNumValenceElectrons == 5 && + at2[aN].valence == 2 ) { + /*------------------------------------------------------------ + Fix Metal disconnection/normalization inconsistency : + disconnected restored + R=N(+)-M R=N--M R=N + M R=N + M + | -> || -> || -> | + O(-) O O O* <- radical + + The correct R=N + M(+) + disconnection | + would be this: O(-) + --------------------------------------------------------------*/ + pvN = pBNS->vert + aN; + pvO = pvRad; + peNO = peRad; + + /* N-O* => N=O */ + peNO->flow ++; + pvO->st_edge.flow ++; + pvN->st_edge.cap ++; + pvN->st_edge.flow ++; + pBNS->tot_st_cap += 1; + pBNS->tot_st_flow += 2; + cur_success ++; + } else { + /* all other radicals that affect stereo */ + delta = pvRad->st_edge.cap - pvRad->st_edge.flow; + pvRad->st_edge.cap -= delta; + pBNS->tot_st_cap -= delta; + } + } +/*exit_case_01:*/ + SetForbiddenEdgeMask( pBNS, &FixedStereoEdges, forbidden_stereo_edge_mask ); + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + /* + if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + */ + cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); + if ( cmpInChI & IDIF_PROBLEM ) { + ret = RI_ERR_PROGR; /* severe restore problem */ + goto exit_function; + } + if ( err ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + cmpInChI2 = 0; + memset ( icr2, 0, sizeof(*icr2) ); + if ( iRevrInChI || iOrigInChI ) { + /* additional mobile-H compare in case of Fixed-H */ + cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); + if ( cmpInChI & IDIF_PROBLEM ) { + ret = RI_ERR_PROGR; /* severe restore problem */ + goto exit_function; + } + if ( err ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + } + + pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && + pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + + pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? + pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; + + + pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || + !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? + NULL: + (pStruct->pOneINChI[1]->StereoIsotopic && + pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + + pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? + pStruct->pOneINChI[1]->StereoIsotopic : + pStruct->pOneINChI[1]->Stereo; + } + } + + cur_success = 0; + if ( !(cmpInChI & IDIF_SB_MISS) && (cmpInChI2 & IDIF_SB_MISS) && + icr2->num_sb_in2_only && + 0 < (max_success = pBNS->tot_st_cap - pBNS->tot_st_flow) ) { + /*----------------------------------------------------*/ + /* case 02: missing stereogenic bond in Mobile-H only */ + /* X=N-O* => X=N=O and eliminate radical */ + /*----------------------------------------------------*/ + int retC, ret2C, retS, ret2S; + INCHI_MODE cmpInChI_Prev, cmpInChI2_Prev; + ICR icr_Prev, icr2_Prev; + + /* blind attepmt */ + icr_Prev = *icr; + icr2_Prev = *icr2; + cmpInChI_Prev = cmpInChI; + cmpInChI2_Prev = cmpInChI2; + for ( i = AllRadList.num_edges = 0; i < pStruct->num_atoms; i ++ ) { + if ( pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow == 1 && + (ret = AddToEdgeList( &AllRadList, i, INC_ADD_EDGE ) ) ) { + goto exit_function; + } + } + for ( i = 0; i < AllRadList.num_edges; i ++ ) { + j = AllRadList.pnEdges[i]; + pBNS->vert[j].st_edge.cap -= 1; + pBNS->tot_st_cap -= 1; + } + /*-------------------------------------------------*/ + /* re-create InChI and see whether it looks better */ + /*-------------------------------------------------*/ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + /* + if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + */ + cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); + if ( cmpInChI & IDIF_PROBLEM ) { + ret = RI_ERR_PROGR; /* severe restore problem */ + goto exit_function; + } + if ( err ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + cmpInChI2 = 0; + memset ( icr2, 0, sizeof(*icr2) ); + if ( iRevrInChI || iOrigInChI ) { + /* additional mobile-H compare in case of Fixed-H */ + cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); + if ( cmpInChI & IDIF_PROBLEM ) { + ret = RI_ERR_PROGR; /* severe restore problem */ + goto exit_function; + } + if ( err ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + } + retC = CompareIcr( icr, &icr_Prev, NULL, NULL, IDIFF_CONSTIT ); + retS = CompareIcr( icr, &icr_Prev, NULL, NULL, IDIFF_STEREO ); + ret2C = CompareIcr( icr2, &icr2_Prev, NULL, NULL, IDIFF_CONSTIT ); + ret2S = CompareIcr( icr2, &icr2_Prev, NULL, NULL, IDIFF_STEREO ); + + if ( 0 >= retC && + 0 >= retS && + 0 >= ret2C && + 0 > ret2S ) { + ; /* accept */ + } else { + /* reject */ + for ( i = 0; i < AllRadList.num_edges; i ++ ) { + j = AllRadList.pnEdges[i]; + pBNS->vert[j].st_edge.cap += 1; + pBNS->tot_st_cap += 1; + } + + /*-------------------------------------------------*/ + /* re-create InChI-- return to previous state */ + /*-------------------------------------------------*/ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + /* + if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + */ + cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); + if ( cmpInChI & IDIF_PROBLEM ) { + ret = RI_ERR_PROGR; /* severe restore problem */ + goto exit_function; + } + if ( err ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + cmpInChI2 = 0; + memset ( icr2, 0, sizeof(*icr2) ); + if ( iRevrInChI || iOrigInChI ) { + /* additional mobile-H compare in case of Fixed-H */ + cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); + if ( cmpInChI & IDIF_PROBLEM ) { + ret = RI_ERR_PROGR; /* severe restore problem */ + goto exit_function; + } + if ( err ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + } + pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && + pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + + pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? + pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; + + + pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || + !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? + NULL: + (pStruct->pOneINChI[1]->StereoIsotopic && + pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + + pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? + pStruct->pOneINChI[1]->StereoIsotopic : + pStruct->pOneINChI[1]->Stereo; + } +/*exit_case_02:;*/ + } + + cur_success = 0; + if ( pStruct->bMobileH == TAUT_NON && (cmpInChI & IDIF_SB_EXTRA_UNDF) && + pStruct->endpoint ) { + /*------------------------------------------------------*/ + /* case 03: extra stereogenic bond in Fixed-H only */ + /* in Mobile-H this bond is not stereogenic. */ + /* Since this bond parity is not known, it is UNDEFINED */ + /*------------------------------------------------------*/ + int bDone, num_endpoints; + + TautMinusEdges[0].num_edges = 0; + TautMinusEdges[1].num_edges = 0; + AllChargeEdges.num_edges = 0; + /* in1 => in restored structure; in2 => in original InChI */ + for ( i = 0; i < icr->num_sb_undef_in1_only; i ++ ) { + j12 = icr->sb_undef_in1_only[i]; + pv1 = pBNS->vert + (v1 = pStereoRevrs->nBondAtom1[j12]-1); + pv2 = pBNS->vert + (v2 = pStereoRevrs->nBondAtom2[j12]-1); + + if ( pStereo2Revrs ) { + /* reject if it is extra in Mobile-H also */ + if ( icr2->num_sb_undef_in1_only ) { + for ( j = 0; j < icr2->num_sb_undef_in1_only; j ++ ) { + k = icr2->sb_undef_in1_only[j]; + if ( v1 == pStereo2Revrs->nBondAtom1[k] && + v2 == pStereo2Revrs->nBondAtom2[k] ) { + break; + } + } + if ( j < icr->num_sb_in1_only ) { + continue; /* extra stereobond in Mobile H also */ + } + } + } + /* reject if it is a stereobond in Mobile-H also */ + if ( pStereo2InChI && pStereo2InChI->nNumberOfStereoBonds ) { + for ( j = 0; j < pStereo2InChI->nNumberOfStereoBonds; j ++ ) { + if ( v1 == pStereo2InChI->nBondAtom1[j] && + v2 == pStereo2InChI->nBondAtom1[j] ) { + break; + } + } + if ( j < pStereo2InChI->nNumberOfStereoBonds ) { + continue; /* ignore this extra stereo bond: it is in Mobile-H */ + } + } + /* find the edge between v1 and v2 */ + for ( k = 0; k < at2[v1].valence; k ++ ) { + pe = pBNS->edge + (e = pv1->iedge[k]); + if ( v2 == (pe->neighbor12 ^ v1) ) + break; /* the edge has been found */ + } + if ( k == at2[v1].valence ) { + ret = RI_ERR_SYNTAX; + goto exit_function; + } + /* Fix all charges except negative charges on tautomeric endpoints */ + if ( !AllChargeEdges.num_edges && !TautMinusEdges[0].num_edges && !TautMinusEdges[1].num_edges ) { + for ( j = 0; j < pStruct->num_atoms; j ++ ) { + if ( (k=pVA[j].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { + if ( !pStruct->endpoint[j] ) { + if (ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { + goto exit_function; + } + } else + if ( pVA[j].cNumValenceElectrons == 6 ) { + /* O */ + if (ret = AddToEdgeList( TautMinusEdges+0, k, INC_ADD_EDGE ) ) { + goto exit_function; + } + } else { + /* N */ + if (ret = AddToEdgeList( TautMinusEdges+1, k, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + if ( (k=pVA[j].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { + if ( ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { + goto exit_function; + } + /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ + if ( pVA[j].cNumValenceElectrons == 5 && !pVA[j].cMetal && /* N, P, As */ + NO_VERTEX != (k = GetChargeFlowerUpperEdge( pBNS, pVA, k ))) { + + if ( !pBNS->edge[j].forbidden && pBNS->edge[k].flow ) { + if ( ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + } + } + } + if ( !pe->flow ) + continue; + /* fix all charges except tautomeric; first allow only O, then only N, finally both N and O */ + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + for ( k = 1, bDone = 0; k < 4 && !bDone; k ++ ) { + /* fix tautomeric charges */ + num_endpoints = (TautMinusEdges+0)->num_edges + (TautMinusEdges+1)->num_edges; + if ( k == 2 ) { + /* fix charges on O */ + SetForbiddenEdgeMask( pBNS, TautMinusEdges+0, forbidden_edge_mask ); + num_endpoints -= (TautMinusEdges+0)->num_edges; + } + if ( k == 1 ) { + SetForbiddenEdgeMask( pBNS, TautMinusEdges+1, forbidden_edge_mask ); + num_endpoints -= (TautMinusEdges+1)->num_edges; + } + if ( num_endpoints >= 2 ) { + delta = 1; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->forbidden |= forbidden_edge_mask; /* fix stereobond */ + pe->flow -= delta; /* decrement stereobond order */ + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { + /* Negative charge has been moved, no change in number of charges */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + (*pnNumRunBNS) ++; + cur_success ++; /* 01 */ + bDone = 1; + } + } else { + pe->forbidden &= ~forbidden_edge_mask; + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + } + /* unfix tautomeric charges */ + if ( k == 2 ) + RemoveForbiddenEdgeMask( pBNS, TautMinusEdges+0, forbidden_edge_mask ); + if ( k == 1 ) + RemoveForbiddenEdgeMask( pBNS, TautMinusEdges+1, forbidden_edge_mask ); + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } +/*exit_case_03:*/ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + /* + if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + */ + cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); + if ( cmpInChI & IDIF_PROBLEM ) { + ret = RI_ERR_PROGR; /* severe restore problem */ + goto exit_function; + } + if ( err ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + cmpInChI2 = 0; + memset ( icr2, 0, sizeof(*icr2) ); + if ( iRevrInChI || iOrigInChI ) { + /* additional mobile-H compare in case of Fixed-H */ + cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); + if ( cmpInChI & IDIF_PROBLEM ) { + ret = RI_ERR_PROGR; /* severe restore problem */ + goto exit_function; + } + if ( err ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + } + pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && + pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + + pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? + pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; + + + pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || + !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? + NULL: + (pStruct->pOneINChI[1]->StereoIsotopic && + pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + + pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? + pStruct->pOneINChI[1]->StereoIsotopic : + pStruct->pOneINChI[1]->Stereo; + } + } + + cur_success = 0; + if ( (cmpInChI & IDIF_SB_EXTRA_UNDF) ) { + /*------------------------------------------------------*/ + /* case 04: extra stereogenic bond */ + /* Since this bond parity is not known, it is UNDEFINED */ + /*------------------------------------------------------*/ + int bDone, num_endpoints; + + TautMinusEdges[0].num_edges = 0; + TautMinusEdges[1].num_edges = 0; + AllChargeEdges.num_edges = 0; + /* in1 => in restored structure; in2 => in original InChI */ + for ( i = 0; i < icr->num_sb_undef_in1_only; i ++ ) { + j12 = icr->sb_undef_in1_only[i]; + pv1 = pBNS->vert + (v1 = pStereoRevrs->nBondAtom1[j12]-1); + pv2 = pBNS->vert + (v2 = pStereoRevrs->nBondAtom2[j12]-1); + + /* find the edge between v1 and v2 */ + for ( k = 0; k < at2[v1].valence; k ++ ) { + pe = pBNS->edge + (e = pv1->iedge[k]); + if ( v2 == (pe->neighbor12 ^ v1) ) + break; /* the edge has been found */ + } + if ( k == at2[v1].valence ) { + ret = RI_ERR_SYNTAX; + goto exit_function; + } + if ( pStereo2Revrs ) { + /* reject if it is not extra in Mobile-H also */ + if ( icr2->num_sb_undef_in1_only ) { + for ( j = 0; j < icr2->num_sb_undef_in1_only; j ++ ) { + k = icr2->sb_undef_in1_only[j]; + if ( v1 == pStereo2Revrs->nBondAtom1[k] && + v2 == pStereo2Revrs->nBondAtom2[k] ) { + break; + } + } + if ( j == icr->num_sb_in1_only ) { + continue; /* extra stereobond only in Fixed-H, not in Mobile H also */ + } + } + } + + /* Fix all charges except negative charges on tautomeric endpoints */ + if ( !AllChargeEdges.num_edges && !TautMinusEdges[0].num_edges && !TautMinusEdges[1].num_edges ) { + for ( j = 0; j < pStruct->num_atoms; j ++ ) { + if ( (k=pVA[j].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { + if (ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + if ( (k=pVA[j].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { + int bMayBeUnfixed = !at2[j].num_H && !(pStruct->endpoint && pStruct->endpoint[j]); + if ( bMayBeUnfixed && pVA[j].cNumValenceElectrons == 6 || + pVA[j].cNumValenceElectrons == 5 && pVA[j].cPeriodicRowNumber > 1 ) { + /* O & P */ + if (ret = AddToEdgeList( TautMinusEdges+0, k, INC_ADD_EDGE ) ) { + goto exit_function; + } + } else + if ( bMayBeUnfixed && + pVA[j].cNumValenceElectrons == 5 && pVA[j].cPeriodicRowNumber == 1 ) { + /* N */ + if (ret = AddToEdgeList( TautMinusEdges+1, k, INC_ADD_EDGE ) ) { + goto exit_function; + } + } else { + if ( ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ + if ( pVA[j].cNumValenceElectrons == 5 && !pVA[j].cMetal && /* N, P, As */ + NO_VERTEX != (k = GetChargeFlowerUpperEdge( pBNS, pVA, k ))) { + if ( !pBNS->edge[j].forbidden && pBNS->edge[k].flow ) { + if ( ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + } + } + } + if ( !pe->flow ) + continue; + /* fix all charges except tautomeric; first allow only O, then only N, finally both N and O */ + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + for ( k = 1, bDone = 0; k < 4 && !bDone; k ++ ) { + /* fix positive charges on heteroatoms */ + num_endpoints = (TautMinusEdges+0)->num_edges + (TautMinusEdges+1)->num_edges; + if ( k == 2 ) { + /* fix charges on O */ + SetForbiddenEdgeMask( pBNS, TautMinusEdges+0, forbidden_edge_mask ); + num_endpoints -= (TautMinusEdges+0)->num_edges; + } + if ( k == 1 ) { + /* fix charges on N */ + SetForbiddenEdgeMask( pBNS, TautMinusEdges+1, forbidden_edge_mask ); + num_endpoints -= (TautMinusEdges+1)->num_edges; + } + if ( num_endpoints >= 2 ) { + delta = 1; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + pe->forbidden |= forbidden_edge_mask; /* fix stereobond */ + pe->flow -= delta; /* decrement stereobond order */ + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { + /* Negative charge has been moved, no change in number of charges */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + (*pnNumRunBNS) ++; + cur_success ++; /* 01 */ + bDone = 1; + } + } else { + pe->forbidden &= ~forbidden_edge_mask; + pe->flow += delta; + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + } + /* unfix tautomeric charges */ + if ( k == 2 ) + RemoveForbiddenEdgeMask( pBNS, TautMinusEdges+0, forbidden_edge_mask ); + if ( k == 1 ) + RemoveForbiddenEdgeMask( pBNS, TautMinusEdges+1, forbidden_edge_mask ); + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask ); + } +/*exit_case_04:*/ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + /* + if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + */ + cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); + if ( cmpInChI & IDIF_PROBLEM ) { + ret = RI_ERR_PROGR; /* severe restore problem */ + goto exit_function; + } + if ( err ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + cmpInChI2 = 0; + memset ( icr2, 0, sizeof(*icr2) ); + if ( iRevrInChI || iOrigInChI ) { + /* additional mobile-H compare in case of Fixed-H */ + cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); + if ( cmpInChI & IDIF_PROBLEM ) { + ret = RI_ERR_PROGR; /* severe restore problem */ + goto exit_function; + } + if ( err ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + } + pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && + pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + + pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? + pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; + + + pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || + !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? + NULL: + (pStruct->pOneINChI[1]->StereoIsotopic && + pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + + pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? + pStruct->pOneINChI[1]->StereoIsotopic : + pStruct->pOneINChI[1]->Stereo; + } + } + + cur_success = 0; + if ( pStruct->bMobileH == TAUT_YES && + (cmpInChI & IDIF_SB_EXTRA_UNDF && + !pStruct->ti.num_t_groups) + /*pStruct->bMobileH == TAUT_NON && (cmpInChI2 & IDIF_SB_EXTRA_UNDF)*/) { + /*----------------------------------------------------------*/ + /* case 05: extra stereogenic bond on =NH2(+), (B, Mobile-H)*/ + /* H H */ + /* original: N(+)=-N< -> N--==N/ */ + /* (A) H */ + /* double bond is marked as */ + /* not stereogenic due to */ + /* its change during proton */ + /* removal => No Stereo bond */ + /* (=NH may be tautomeric) */ + /* */ + /* H H */ + /* original: N=-N(+)< -> N--==N/ */ + /* (B) H */ + /* double bond was not */ + /* changed during proton */ + /* In Fixed-H this bond removal => Undef Stereo */ + /* may not be stereogenic (=NH is not tautomeric) */ + /* (a) due to (+) movement */ + /* (b) due to symmetry (2H), even if isotopic */ + /* */ + /* Fixed-H: move (+) to or from NH2 for Undef or No stereo */ + /* respectively */ + /* Mobile-H: Add H(+) to =NH and move the charge to =N- */ + /* to eliminate Undef stereo */ + /* Move charge from N to -NH2 to create */ + /* Undef Stereo */ + /* Since this bond parity is not known, it is UNDEFINED */ + /* */ + /* Solution: Add H(+) to =NH and move charge to -N= */ + /* */ + /*----------------------------------------------------------*/ + int aN, aC, i1, i2, vPlusMinus; + AllChargeEdges.num_edges = 0; + /* in1 => in restored structure; in2 => in original InChI */ + for ( i = 0; i < icr->num_sb_undef_in1_only; i ++ ) { + j12 = icr->sb_undef_in1_only[i]; + pv1 = pBNS->vert + (v1 = pStereoRevrs->nBondAtom1[j12]-1); + pv2 = pBNS->vert + (v2 = pStereoRevrs->nBondAtom2[j12]-1); + /* indicators of -NH: */ + i1 = at2[v1].valence == 1 && at2[v1].num_H == 1 && !at2[v1].endpoint && + pVA[v1].cNumValenceElectrons == 5 && pVA[v1].cPeriodicRowNumber == 1; + i2 = at2[v2].valence == 1 && at2[v2].num_H == 1 && !at2[v2].endpoint && + pVA[v2].cNumValenceElectrons == 5 && pVA[v2].cPeriodicRowNumber == 1; + if ( !i1 && !i2 || i1 && i2 ) { + continue; + } + /* find the edge between v1 and v2 */ + for ( k = 0; k < at2[v1].valence; k ++ ) { + pe = pBNS->edge + (e = pv1->iedge[k]); + if ( v2 == (pe->neighbor12 ^ v1) ) + break; /* the edge has been found */ + } + if ( k == at2[v1].valence ) { + ret = RI_ERR_SYNTAX; + goto exit_function; + } + if ( pe->flow != 1 ) { + continue; /* already charged */ + } + aN = i1? v1 : v2; /* -NH atom */ + aC = i1? v2 : v1; /* neighbor */ + /* Replace =NH with -NH2 + Create such a charge on some -N< that may be moved to NH2 to remove H(+): + transformation: + from: HN=C-=-N=(+vert)-Y=(+super)-(+/-) + to: 2HN-C*-=-N=(+vert)-Y=(+super)-(+/-)* + Run BNS to obtain: + 2HN-C=-=N(+)-(+vert)=Y-(+super)=(+/-) + */ + vPlusMinus = GetPlusMinusVertex( pBNS, pTCGroups, 1, 0 ); + if ( NO_VERTEX == vPlusMinus ) { + break; /* cannot do anything */ + } + /* increase edges to -Y-(+/-)-Y- capacities */ + delta = 1; + for ( i1 = 0; i1 < pBNS->vert[vPlusMinus].num_adj_edges; i1 ++ ) { + i2 = pBNS->edge[pBNS->vert[vPlusMinus].iedge[i1]].neighbor12 ^ vPlusMinus; + for ( k = 0; k < pBNS->vert[i2].num_adj_edges; k ++ ) { + pBNS->edge[pBNS->vert[i2].iedge[k]].cap += delta; + } + } + /* Fix all charges except (+) on -N< */ + if ( !AllChargeEdges.num_edges ) { + for ( j = 0; j < pStruct->num_atoms; j ++ ) { + if ( (k=pVA[j].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { + if (ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + if ( (k=pVA[j].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { + if ( pVA[j].cNumValenceElectrons == 5 && pVA[j].cPeriodicRowNumber == 1 && + !at2[j].num_H && at2[j].valence == 3 && + !(at2[j].endpoint || pStruct->endpoint && pStruct->endpoint[j]) ) { + ; /* do not fix -N< or =N(+)< */ + } else { + /* all others */ + if (ret = AddToEdgeList( TautMinusEdges+0, k, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ + if ( pVA[j].cNumValenceElectrons == 5 && !pVA[j].cMetal && /* N, P, As */ + NO_VERTEX != (k = GetChargeFlowerUpperEdge( pBNS, pVA, k ))) { + if ( !pBNS->edge[j].forbidden && pBNS->edge[k].flow ) { + if ( ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + } + } + } + /* Make bond to =NH single, add radical to aC */ + pe->flow -= delta; /* make single bond */ + pBNS->vert[aN].st_edge.flow -= delta; + pBNS->vert[aN].st_edge.cap -= delta; /* avoid radical on N */ + pBNS->vert[aC].st_edge.flow -= delta; /* create radical on C */ + pBNS->vert[vPlusMinus].st_edge.cap += delta; /* create radical on (+/-) */ + pBNS->tot_st_flow -= 2*delta; + /* fix C-NH bond */ + if ( ret = AddToEdgeList( &AllChargeEdges, e, INC_ADD_EDGE ) ) { + goto exit_function; + } + /* pBNS->tot_st_cap is unchanged */ + /* find all aC edges except pe to fix them */ + /* 2. Check whether it would work and do if it would */ + SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );/* fix aC edges */ + pe->cap ++; + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == vPlusMinus && vPathStart == aC || + vPathEnd == aC && vPathStart == vPlusMinus) && nDeltaCharge == 1 ) { + /* Negative charge has been moved, no change in number of charges */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + (*pnNumRunBNS) ++; + /* 3. Add H to -NH and register increaded charge */ + pStruct->at[aN].num_H ++; + pTCGroups->total_charge ++; + cur_success ++; /* 01 */ + } + } else { + pe->flow += delta; /* make single bond */ + pBNS->vert[aN].st_edge.flow += delta; + pBNS->vert[aN].st_edge.cap += delta; /* avoid radical on N */ + pBNS->vert[aC].st_edge.flow += delta; /* create radical on C */ + pBNS->vert[vPlusMinus].st_edge.cap -= delta; /* create radical on (+/-) */ + pBNS->tot_st_flow += 2*delta; + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );/* fix aC edges */ + AllChargeEdges.num_edges --; /* remove pe from the list */ + CurrEdges.num_edges = 0; + continue; /* should not happen */ + } + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );/* fix aC edges */ + AllChargeEdges.num_edges --; /* remove pe from the list */ + CurrEdges.num_edges = 0; + } +/*exit_case_05:*/ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + /* + if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + */ + cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr, &err ); + if ( cmpInChI & IDIF_PROBLEM ) { + ret = RI_ERR_PROGR; /* severe restore problem */ + goto exit_function; + } + if ( err ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + cmpInChI2 = 0; + memset ( icr2, 0, sizeof(*icr2) ); + if ( iRevrInChI || iOrigInChI ) { + /* additional mobile-H compare in case of Fixed-H */ + cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); + if ( cmpInChI & IDIF_PROBLEM ) { + ret = RI_ERR_PROGR; /* severe restore problem */ + goto exit_function; + } + if ( err ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + } + pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && + pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + + pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? + pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; + + + pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || + !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? + NULL: + (pStruct->pOneINChI[1]->StereoIsotopic && + pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + + pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? + pStruct->pOneINChI[1]->StereoIsotopic : + pStruct->pOneINChI[1]->Stereo; + } + } + + cur_success = 0; + if ( pStruct->bMobileH == TAUT_NON && pStereo2Revrs /* added check 2006-04-05 */ && + (cmpInChI2 & IDIF_SB_EXTRA_UNDF && + !pStruct->ti.num_t_groups) + /*pStruct->bMobileH == TAUT_NON && (cmpInChI2 & IDIF_SB_EXTRA_UNDF)*/) { + /*----------------------------------------------------------*/ + /* case 06: extra stereogenic bond on =NH2(+), (B, Fixed-H) */ + /* H H =========== */ + /* original: N(+)=-N< -> N--==N(+)< */ + /* (A) H H */ + /* double bond in Mobile-H */ + /* layer has Undef stereo */ + /* */ + /* */ + /* Fixed-H: move (+) to or from NH2 for Undef or No stereo */ + /* respectively */ + /* Mobile-H: Add H(+) to =NH and move the charge to =N- */ + /* to eliminate Undef stereo */ + /* Move charge from N to -NH2 to create */ + /* Undef Stereo */ + /* Since this bond parity is not known, it is UNDEFINED */ + /* */ + /* Solution: Move (+) from -NH2(+) to othe -N< */ + /* */ + /*----------------------------------------------------------*/ + int aN, aC, i1, i2, ePlus; + BNS_EDGE *pePlus; + AllChargeEdges.num_edges = 0; + /* in1 => in restored structure; in2 => in original InChI */ + for ( i = 0; i < icr2->num_sb_undef_in1_only; i ++ ) { + j12 = icr2->sb_undef_in1_only[i]; + pv1 = pBNS->vert + (v1 = pStereo2Revrs->nBondAtom1[j12]-1); + pv2 = pBNS->vert + (v2 = pStereo2Revrs->nBondAtom2[j12]-1); + /* indicators of -NH: */ + i1 = at2[v1].valence == 1 && at2[v1].num_H == 2 && !at2[v1].endpoint && + pVA[v1].cNumValenceElectrons == 5 && pVA[v1].cPeriodicRowNumber == 1; + i2 = at2[v2].valence == 1 && at2[v2].num_H == 2 && !at2[v2].endpoint && + pVA[v2].cNumValenceElectrons == 5 && pVA[v2].cPeriodicRowNumber == 1; + if ( !i1 && !i2 || i1 && i2 ) { + continue; + } + /* find the edge between v1 and v2 */ + for ( k = 0; k < at2[v1].valence; k ++ ) { + pe = pBNS->edge + (e = pv1->iedge[k]); + if ( v2 == (pe->neighbor12 ^ v1) ) + break; /* the edge has been found */ + } + if ( k == at2[v1].valence ) { + ret = RI_ERR_SYNTAX; + goto exit_function; + } + if ( pe->flow != 1 ) { + continue; /* already charged */ + } + aN = i1? v1 : v2; /* -NH atom */ + aC = i1? v2 : v1; /* neighbor */ + if ( 0 > (ePlus = pVA[aN].nCPlusGroupEdge-1) || + (pePlus = pBNS->edge + ePlus)->flow || /* must be (+) charged */ + pePlus->forbidden ) { + continue; + } + /* Move (+) from =NH2(+) to some other -N< + */ + /* Fix all charges except (+) on -N< */ + if ( !AllChargeEdges.num_edges ) { + for ( j = 0; j < pStruct->num_atoms; j ++ ) { + if ( (k=pVA[j].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { + if (ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + if ( (k=pVA[j].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) { + if ( pVA[j].cNumValenceElectrons == 5 && pVA[j].cPeriodicRowNumber == 1 && + !at2[j].num_H && at2[j].valence == 3 && + !(at2[j].endpoint || pStruct->endpoint && pStruct->endpoint[j]) ) { + ; /* do not fix -N< or =N(+)< */ + } else { + /* all others */ + if (ret = AddToEdgeList( TautMinusEdges+0, k, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + /* in addition, disallow N(V) creation by forbidding charge flower edge that has flow=1 */ + if ( pVA[j].cNumValenceElectrons == 5 && !pVA[j].cMetal && /* N, P, As */ + NO_VERTEX != (k = GetChargeFlowerUpperEdge( pBNS, pVA, k ))) { + if ( !pBNS->edge[j].forbidden && pBNS->edge[k].flow ) { + if ( ret = AddToEdgeList( &AllChargeEdges, k, INC_ADD_EDGE ) ) { + goto exit_function; + } + } + } + } + } + } + /* pePlus edge is already fixed; unfix it */ + /* To decrement (+) on =NH2(+) decrement its double bond order */ + delta = 1; + if ( !pe->flow ) + continue; + pv1 = pBNS->vert + (v1 = pe->neighbor1); + pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); + + delta = 1; + pe->flow -= delta; + pv1->st_edge.flow -= delta; + pv2->st_edge.flow -= delta; + pBNS->tot_st_flow -= 2*delta; + + pe->forbidden |= forbidden_edge_mask; + pePlus->forbidden &= ~forbidden_edge_mask; + + ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen, + &nDeltaH, &nDeltaCharge, &nNumVisitedAtoms ); + + if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 || + vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) { + /* (+)charge was just moved, no change in number of charges */ + ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups ); + if ( ret > 0 ) { + (*pnNumRunBNS) ++; + cur_success ++; /* 01 */ + } + } else { + pe->flow += delta; /* roll back */ + pv1->st_edge.flow += delta; + pv2->st_edge.flow += delta; + pBNS->tot_st_flow += 2*delta; + } + pe->forbidden &= ~forbidden_edge_mask; + RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );/* fix aC edges */ + } +/*exit_case_06:*/ + if ( cur_success ) { + tot_succes += cur_success; + /* recalculate InChI from the structure */ + if ( 0 > (ret = MakeOneInChIOutOfStrFromINChI2( pCG, ic, ip, sd, pBNS, pStruct, at, at2, at3, pVA, pTCGroups, + ppt_group_info, ppat_norm, ppat_prep ) ) ) { + goto exit_function; + } + if ( ret = FillOutExtraFixedHDataRestr( pStruct ) ) { + goto exit_function; + } + /* + if ( ret = FillOutCMP2MHINCHI( pStruct, pTCGroups, at2, pVA, pInChI, pc2i ) ) { + goto exit_function; + } + */ + cmpInChI = CompareReversedINChI2( pStruct->pOneINChI[0], pInChI[0], pStruct->pOneINChI_Aux[0], NULL /*INChI_Aux *v2*/, icr2, &err ); + if ( cmpInChI & IDIF_PROBLEM ) { + ret = RI_ERR_PROGR; /* severe restore problem */ + goto exit_function; + } + if ( err ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + cmpInChI2 = 0; + memset ( icr2, 0, sizeof(*icr2) ); + if ( iRevrInChI || iOrigInChI ) { + /* additional mobile-H compare in case of Fixed-H */ + cmpInChI2 = CompareReversedINChI2( pStruct->pOneINChI[iRevrInChI], pInChI[iOrigInChI], pStruct->pOneINChI_Aux[iRevrInChI], NULL /*INChI_Aux *v2*/, icr2, &err ); + if ( cmpInChI & IDIF_PROBLEM ) { + ret = RI_ERR_PROGR; /* severe restore problem */ + goto exit_function; + } + if ( err ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + } + pStereoRevrs = (pStruct->pOneINChI[0]->StereoIsotopic && + pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoBonds + + pStruct->pOneINChI[0]->StereoIsotopic->nNumberOfStereoCenters)? + pStruct->pOneINChI[0]->StereoIsotopic : pStruct->pOneINChI[0]->Stereo; + + + pStereo2Revrs = (pStruct->bMobileH == TAUT_YES || !pStruct->pOneINChI[1] || + !pStruct->pOneINChI[1]->nNumberOfAtoms || pStruct->pOneINChI[1]->bDeleted)? + NULL: + (pStruct->pOneINChI[1]->StereoIsotopic && + pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoBonds + + pStruct->pOneINChI[1]->StereoIsotopic->nNumberOfStereoCenters)? + pStruct->pOneINChI[1]->StereoIsotopic : + pStruct->pOneINChI[1]->Stereo; + } + } + + +exit_function: + SetForbiddenEdgeMask( pBNS, &FixedStereoEdges, forbidden_stereo_edge_mask ); + AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE ); + AllocEdgeList( &CurrEdges, EDGE_LIST_FREE ); + AllocEdgeList( &NFlowerEdges, EDGE_LIST_FREE ); + AllocEdgeList( &OtherNFlowerEdges, EDGE_LIST_FREE ); + AllocEdgeList( &FixedStereoEdges, EDGE_LIST_FREE ); + AllocEdgeList( &AllRadList, EDGE_LIST_FREE ); /* eliminate memory leak */ + AllocEdgeList( TautMinusEdges+0, EDGE_LIST_FREE ); + AllocEdgeList( TautMinusEdges+1, EDGE_LIST_FREE ); + + return ret; +} +#endif diff --git a/INCHI-1-SRC/INCHI/common/ichirvr7.c b/INCHI-1-SRC/INCHI_BASE/src/ichirvr7.c similarity index 89% rename from INCHI-1-SRC/INCHI/common/ichirvr7.c rename to INCHI-1-SRC/INCHI_BASE/src/ichirvr7.c index 42d71d1..375fe09 100644 --- a/INCHI-1-SRC/INCHI/common/ichirvr7.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichirvr7.c @@ -1,2352 +1,2530 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include - -/*^^^ */ -/* #define CHECK_WIN32_VC_HEAP */ -#include "mode.h" - -#if ( READ_INCHI_STRING == 1 ) - -#include "ichicomp.h" -#include "ichi.h" -#include "ichitime.h" -#include "ichierr.h" -#include "util.h" -#include "strutil.h" - -/* reverse InChI */ -#include "ichimain.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichister.h" -#include "strutil.h" -#include "ichisize.h" -#include "ichiring.h" -#include "ichinorm.h" - -#include "ichirvrs.h" -#include "inchicmp.h" - -/******************************************************************************************************/ -int InChI2Atom( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, const char *szCurHdr, long num_inp, - StrFromINChI *pStruct, int iComponent, int iAtNoOffset, int bI2A_Flag, int bHasSomeFixedH, InpInChI *OneInput) -{ - int iINChI = (bI2A_Flag & I2A_FLAG_RECMET)? INCHI_REC : INCHI_BAS; - int bMobileH = (bI2A_Flag & I2A_FLAG_FIXEDH)? TAUT_NON : TAUT_YES; - INChI *pInChI[TAUT_NUM]; - int ret = 0; - - memset( pInChI, 0, sizeof(pInChI) ); - /* disconnected or reconnected */ - if ( iINChI == INCHI_REC ) { - if ( !OneInput->nNumComponents[iINChI][TAUT_YES] ) { - iINChI = INCHI_BAS; - } - } - if ( iComponent >= OneInput->nNumComponents[iINChI][TAUT_YES] ) { - return 0; /* component does not exist */ - } - /* mobile or fixed H */ - pStruct->bFixedHExists = 0; - if ( bMobileH == TAUT_NON ) { - if ( !OneInput->nNumComponents[iINChI][bMobileH] ) { - /* only one InChI exists (no mobile H) */ - bMobileH = TAUT_YES; - } - } - - if ( iComponent >= OneInput->nNumComponents[iINChI][bMobileH] ) { - return 0; /* component does not exist */ - } - /* pointer to the InChI that is going to be reversed */ - pInChI[0] = &OneInput->pInpInChI[iINChI][bMobileH][iComponent]; - pStruct->bMobileH = bMobileH; - pStruct->iINCHI = iINChI; - /* deleted component only in case Mobile-H and compound contains only protons */ - if ( pInChI[0]->bDeleted ) { - return 0; /* deleted component, presumably H(+) */ - } - - if ( bMobileH == TAUT_NON && OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons ) { - pStruct->nNumRemovedProtonsMobHInChI = - OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons[iComponent].nNumRemovedProtons; - } - - if ( bMobileH == TAUT_NON || bMobileH == TAUT_YES && OneInput->pInpInChI[iINChI][TAUT_NON] && - OneInput->pInpInChI[iINChI][TAUT_NON][iComponent].nNumberOfAtoms > 0 && - !OneInput->pInpInChI[iINChI][TAUT_NON][iComponent].bDeleted ) { - pStruct->bFixedHExists = 1; - } - if ( bMobileH == TAUT_NON && iComponent < OneInput->nNumComponents[iINChI][TAUT_YES] && - OneInput->pInpInChI[iINChI][TAUT_YES] && - OneInput->pInpInChI[iINChI][TAUT_YES][iComponent].nNumberOfAtoms > 0 && - !OneInput->pInpInChI[iINChI][TAUT_YES][iComponent].bDeleted ) { - /* pointer to the Mobile-H InChI if we are reversing Fixed-H InChI */ - pInChI[1] = &OneInput->pInpInChI[iINChI][TAUT_YES][iComponent]; - } - pStruct->num_inp_actual = OneInput->num_inp; - ret = OneInChI2Atom( ip, sd, szCurHdr, num_inp, pStruct, iComponent, iAtNoOffset, bHasSomeFixedH, pInChI); - return ret; /* same interpretation as in ProcessOneStructure ??? */ -} - -/*******************************************************************/ -void RemoveFixHInChIIdentical2MobH( InpInChI *pOneInput ) -{ - int iInchiRec, cur_num_comp, k; - /* eliminate Fixed-H InChI that are exactly came as the corresponding Mobile-H structures */ - for ( iInchiRec = 0; iInchiRec < INCHI_NUM; iInchiRec ++ ) { - cur_num_comp = inchi_min(pOneInput->nNumComponents[iInchiRec][TAUT_YES], - pOneInput->nNumComponents[iInchiRec][TAUT_NON]); - for ( k = 0; k < cur_num_comp; k ++ ) { - if ( !CompareReversedINChI( pOneInput->pInpInChI[iInchiRec][TAUT_YES]+k, - pOneInput->pInpInChI[iInchiRec][TAUT_NON]+k, NULL, NULL ) ) { - Free_INChI_Members( pOneInput->pInpInChI[iInchiRec][TAUT_NON]+k ); - memset( pOneInput->pInpInChI[iInchiRec][TAUT_NON]+k, 0, sizeof(pOneInput->pInpInChI[0][0][0]) ); - } - } - } -} -/*******************************************************************/ -int MarkDisconectedIdenticalToReconnected ( InpInChI *pOneInput ) -{ - /* mark Disconnected InChI components that are exactly came as Reconnected ones */ - /* Disconnected will have a negative number of the reconnected component */ - /* Reconnected will have a positive number of the disconnected component */ - int k1, k2, num_marked = 0; - for ( k1 = 0; k1 < inchi_max(pOneInput->nNumComponents[INCHI_BAS][TAUT_YES], - pOneInput->nNumComponents[INCHI_BAS][TAUT_NON]); k1 ++ ) { - for ( k2 = 0; k2 < inchi_max(pOneInput->nNumComponents[INCHI_REC][TAUT_YES], - pOneInput->nNumComponents[INCHI_REC][TAUT_NON]); k2 ++ ) { - int eqM = ( k1 < pOneInput->nNumComponents[INCHI_BAS][TAUT_YES] && - k2 < pOneInput->nNumComponents[INCHI_REC][TAUT_YES] && - !pOneInput->pInpInChI[INCHI_REC][TAUT_YES][k2].nLink && /* already linked */ - !pOneInput->pInpInChI[INCHI_BAS][TAUT_YES][k1].bDeleted && - pOneInput->pInpInChI[INCHI_BAS][TAUT_YES][k1].nNumberOfAtoms && - pOneInput->pInpInChI[INCHI_BAS][TAUT_YES][k1].nNumberOfAtoms == - pOneInput->pInpInChI[INCHI_REC][TAUT_YES][k2].nNumberOfAtoms && - !pOneInput->pInpInChI[INCHI_REC][TAUT_YES][k2].bDeleted && - !CompareReversedINChI( pOneInput->pInpInChI[INCHI_REC][TAUT_YES]+k2, - pOneInput->pInpInChI[INCHI_BAS][TAUT_YES]+k1, - NULL, NULL )); - int isF1 = (k1 < pOneInput->nNumComponents[INCHI_BAS][TAUT_NON] && - 0 == pOneInput->pInpInChI[INCHI_BAS][TAUT_NON][k1].bDeleted && - 0 < pOneInput->pInpInChI[INCHI_BAS][TAUT_NON][k1].nNumberOfAtoms ); - int isF2 = (k2 < pOneInput->nNumComponents[INCHI_REC][TAUT_NON] && - 0 == pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].bDeleted && - 0 < pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].nNumberOfAtoms ); - int eqF = isF1 && isF2 && - !pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].nLink && - pOneInput->pInpInChI[INCHI_BAS][TAUT_NON][k1].nNumberOfAtoms == - pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].nNumberOfAtoms && - !CompareReversedINChI( pOneInput->pInpInChI[INCHI_REC][TAUT_NON]+k2, - pOneInput->pInpInChI[INCHI_BAS][TAUT_NON]+k1, - NULL, NULL ); - if ( eqM && (!isF1 && !isF2 || eqF ) ) { - pOneInput->pInpInChI[INCHI_BAS][TAUT_YES][k1].nLink = -(k2+1); - pOneInput->pInpInChI[INCHI_REC][TAUT_YES][k2].nLink = (k1+1); - if ( eqF ) { - pOneInput->pInpInChI[INCHI_BAS][TAUT_NON][k1].nLink = -(k2+1); - pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].nLink = (k1+1); - } - num_marked ++; - break; /* equal InChI has been deleted from the disconnected layer, get next k1 */ - } - } - } - return num_marked; - -} -/**************************************************************/ -void SetUpSrm( SRM *pSrm ) -{ - /* structure restore parms !!!!! */ - memset( pSrm, 0, sizeof(pSrm[0]) ); - pSrm->bFixStereoBonds = FIX_STEREO_BOND_ORDER; - pSrm->nMetal2EndpointMinBondOrder = 1; - pSrm->nMetal2EndpointInitEdgeFlow = 0; - if ( METAL_FREE_CHARGE_VAL == 1 ) { - pSrm->bMetalAddFlower = 1; - /* the next 3 parameters: */ - /* 0, 0, 0 => all bonds 0, no init radical on metal */ - /* 0, 0, 1 => all bonds 0, init radical on metal */ - /* 0, 1, 0 => wrong */ - /* 0, 1, 1 => all bonds 1, no init radical on metal */ - /* 1, 0, 1 => min bond order 1, all bonds to metal have order 1 */ - /* 1, 1, 0 => wrong */ - /* 1, 1, 1 => wrong */ - pSrm->nMetalMinBondOrder = 0; - pSrm->nMetalInitEdgeFlow = 1; - pSrm->nMetalInitBondOrder = 1; - pSrm->bStereoRemovesMetalFlag = pSrm->bFixStereoBonds; - pSrm->nMetalFlowerParam_D = 16; - pSrm->nMetalMaxCharge_D = 16; - } else { - pSrm->bMetalAddFlower = 0; - pSrm->nMetalMinBondOrder = 1; - pSrm->nMetalInitEdgeFlow = 0; - pSrm->nMetalInitBondOrder = 1; - pSrm->bStereoRemovesMetalFlag = pSrm->bFixStereoBonds; - pSrm->nMetalFlowerParam_D = 16; - pSrm->nMetalMaxCharge_D = 0; - } - /* - pSrm->nMetalInitBondOrder = pSrm->nMetalMinBondOrder - + pSrm->nMetalInitEdgeFlow; - */ - pSrm->nMetal2EndpointInitBondOrder = pSrm->nMetal2EndpointMinBondOrder - + pSrm->nMetal2EndpointInitEdgeFlow; - -} -/**************************************************************************************/ -int MergeStructureComponents( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, char *szCurHdr, - ICHICONST SRM *pSrm, int bReqNonTaut, StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], - InpInChI *pOneInput ) -{ - int iInchiRec, iMobileH, iAlternH, num_components, tot_just_atoms, tot_removed_H, tot_atoms, cur_nA, cur_nH; - int k, i, j, ret, iCurAtomOffs, iNxtAtomOffs, iCurDelHOffs, iNxtDelHOffs, len, len2, iShiftH, icomp; - int *nAtomOffs=NULL, *nDelHOffs=NULL; - StrFromINChI *pStruct1; - inp_ATOM *at=NULL, *a; - - ret = 0; - pOneInput->num_atoms = 0; - /* select highest detail level */ - if ( num_components = pOneInput->nNumComponents[INCHI_REC][TAUT_NON] ) { - iInchiRec = INCHI_REC; - iMobileH = TAUT_NON; - } else - if ( num_components = pOneInput->nNumComponents[INCHI_REC][TAUT_YES] ) { - iInchiRec = INCHI_REC; - iMobileH = TAUT_YES; - } else - if ( num_components = pOneInput->nNumComponents[INCHI_BAS][TAUT_NON] ) { - iInchiRec = INCHI_BAS; - iMobileH = TAUT_NON; - } else - if ( num_components = pOneInput->nNumComponents[INCHI_BAS][TAUT_YES] ) { - iInchiRec = INCHI_BAS; - iMobileH = TAUT_YES; - } else { - return 0; /* no components available */ - } - - nAtomOffs = (int*) inchi_malloc((num_components+1) * sizeof(nAtomOffs[0])); - nDelHOffs = (int*) inchi_malloc((num_components+1) * sizeof(nDelHOffs[0])); - if ( !nAtomOffs || !nDelHOffs ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - /* count number of atoms and removed H */ - tot_just_atoms = tot_removed_H = tot_atoms = 0; - iAlternH = (iMobileH==TAUT_NON && pOneInput->nNumComponents[iInchiRec][TAUT_YES])? TAUT_YES : -1; - nAtomOffs[0] = nDelHOffs[0] = 0; - for ( k = 0; k < num_components; k ++ ) { - pStruct1 = pStruct[iInchiRec][iMobileH][k].num_atoms? pStruct[iInchiRec][iMobileH]+k : - iAlternH>=0 && - pStruct[iInchiRec][iAlternH][k].num_atoms? pStruct[iInchiRec][iAlternH]+k : NULL; - if ( !pStruct1 || !pStruct1->at2 || !pStruct1->num_atoms || pStruct1->bDeleted ) { - cur_nA = cur_nH = 0; - } else { - cur_nA = pStruct1->num_atoms; - cur_nH = pStruct1->num_deleted_H; - } - nAtomOffs[k+1] = nAtomOffs[k] + cur_nA; - nDelHOffs[k+1] = nDelHOffs[k] + cur_nH; - } - tot_just_atoms = nAtomOffs[num_components]; - /* shift all H to the end */ - for ( k = 0; k <= num_components; k ++ ) { - nDelHOffs[k] += tot_just_atoms; - } - tot_atoms = nDelHOffs[num_components]; - - /* merge atoms together: 1. Allocate */ - if ( NULL == (at = (inp_ATOM *) inchi_malloc( (tot_atoms+1) * sizeof(at[0]) ) ) ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - if ( !tot_atoms ) { - ret = 0; - goto exit_function; /* empty structure */ - } - /* merge atoms together: 2. Copy */ - for ( k = 0; k < num_components; k ++ ) { - pStruct1 = pStruct[iInchiRec][iMobileH][k].num_atoms? pStruct[iInchiRec][iMobileH]+k : - iAlternH>=0 && - pStruct[iInchiRec][iAlternH][k].num_atoms? pStruct[iInchiRec][iAlternH]+k : NULL; - if ( len = nAtomOffs[k+1] - nAtomOffs[k] ) { - memcpy( at + nAtomOffs[k], pStruct1->at2, len * sizeof(at[0]) ); - if ( len2 = nDelHOffs[k+1] - nDelHOffs[k] ) { - memcpy( at + nDelHOffs[k], pStruct1->at2+len, len2 * sizeof(at[0]) ); - } - } - } - /* merge atoms together: 3. Update atom numbers */ - icomp = 0; - for ( k = 0; k < num_components; k ++ ) { - iCurAtomOffs = nAtomOffs[k]; - iNxtAtomOffs = nAtomOffs[k+1]; - iCurDelHOffs = nDelHOffs[k]; - iNxtDelHOffs = nDelHOffs[k+1]; - len = nAtomOffs[k+1] - nAtomOffs[k]; /* number of atoms in a component excluding explicit H */ - iShiftH = iCurDelHOffs - len; - if ( !len ) { - continue; - } - icomp ++; /* current component number */ - /* update atoms */ - for ( i = iCurAtomOffs; i < iNxtAtomOffs; i ++ ) { - - a = at+i; - - a->endpoint = 0; - a->bAmbiguousStereo = 0; - a->at_type = 0; - a->bCutVertex = 0; - a->bUsed0DParity = 0; - a->cFlags = 0; - a->nBlockSystem = 0; - a->nNumAtInRingSystem = 0; - a->nRingSystem = 0; - - for ( j = 0; j < a->valence; j ++ ) { - if ( a->neighbor[j] < len ) { - a->neighbor[j] += iCurAtomOffs; /* atom */ - } else { - a->neighbor[j] += iShiftH; /* explicit H */ - } - } - a->orig_at_number += iCurAtomOffs; - a->component = icomp; - if ( a->p_parity ) { - for ( j = 0; j < MAX_NUM_STEREO_ATOM_NEIGH; j ++ ) { - if ( a->p_orig_at_num[j] <= len ) { - /* originally, orig_at_num = atom_index+1, therefore <= instead of < */ - a->p_orig_at_num[j] += iCurAtomOffs; - } else { - a->p_orig_at_num[j] += iShiftH; - } - } - } - for ( j = 0; j < MAX_NUM_STEREO_BONDS && a->sb_parity[j]; j ++ ) { - if ( a->sn_orig_at_num[j] <= len ) { - /* originally, orig_at_num = atom_index+1, therefore <= instead of < */ - a->sn_orig_at_num[j] += iCurAtomOffs; - } else { - a->sn_orig_at_num[j] += iShiftH; - } - } - } - /* update fixed-H */ - for ( i = iCurDelHOffs; i < iNxtDelHOffs; i ++ ) { - a = at+i; - a->neighbor[0] += iCurAtomOffs; - a->orig_at_number += iShiftH; - - } - } - /* save the results */ - pOneInput->atom = at; - pOneInput->num_atoms = tot_atoms; - at = NULL; - -exit_function: - if ( at ) inchi_free( at ); /* in case of failure */ - if ( nAtomOffs ) inchi_free( nAtomOffs ); - if ( nDelHOffs ) inchi_free( nDelHOffs ); - return ret; -} -#ifndef COMPILE_ANSI_ONLY -static PER_DRAW_PARMS pdp; -/******************************************************************************************************/ -int DisplayAllRestoredComponents( inp_ATOM *at, int num_at, const char *szCurHdr ) -{ - int ret; - char szTitle[512]; - DRAW_PARMS dp; - TBL_DRAW_PARMS tdp; - if ( num_at <= 0 ) { - return 0; - } - memset( &dp, 0, sizeof(dp)); - memset( &tdp, 0, sizeof(tdp) ); - //memset( &pdp, 0, sizeof(pdp) ); - dp.sdp.tdp = &tdp; - dp.pdp = &pdp; - dp.sdp.nFontSize = -9; - sprintf( szTitle, "All Components of Restored %s Structure", szCurHdr? szCurHdr : "(No structure name)"); - ret = DisplayStructure( at, num_at, 0 /* nNumDeletedH*/, 0 /*bAdd_DT_to_num_H*/, - 0 /*nNumRemovedProtons*/, NULL /*NUM_H *nNumRemovedProtonsIsotopic*/, - 1 /*int bIsotopic*/, 0 /*bTautomeric*/, - NULL /* pINChI */, NULL /* INChI_Aux **cur_INChI_Aux*/, - 0 /*bAbcNumbers*/, &dp, 0 /*INCHI_MODE nMode*/, szTitle ); - return 0; -} -/******************************************************************************************************/ -int DisplayOneRestoredComponent( StrFromINChI *pStruct, inp_ATOM *at, - int iComponent, int nNumComponents, int bMobileH, - const char *szCurHdr ) -{ - int ret, k; - int num_at = pStruct->num_atoms; - XYZ_COORD *pxyz = pStruct->pXYZ; - char szTitle[512]; - DRAW_PARMS dp; - TBL_DRAW_PARMS tdp; - int iInchiRec = pStruct->iInchiRec; - int iMobileH = pStruct->iMobileH; - INChI **pInChI = NULL; - INChI_Aux **pAux = NULL; - int nNumRemovedProtons = pAux? pAux[iMobileH]->nNumRemovedProtons : 0; - NUM_H *nNumRemovedProtonsIsotopic = pAux? pAux[iMobileH]->nNumRemovedIsotopicH : NULL; - - - if ( num_at <= 0 || !pxyz ) { - return 0; - } - if ( iInchiRec && !pStruct->RevInChI.pINChI_Aux[iInchiRec][0] ) { - iInchiRec = 0; - } - k = iMobileH; - if ( !bRevInchiComponentExists( pStruct, iInchiRec, k, 0 ) ) { - k = ALT_TAUT(k); - } - pInChI = pStruct->RevInChI.pINChI[iInchiRec][0]; - pAux = pStruct->RevInChI.pINChI_Aux[iInchiRec][0]; - - - memset( &dp, 0, sizeof(dp)); - memset( &tdp, 0, sizeof(tdp) ); - //memset( &pdp, 0, sizeof(pdp) ); - dp.sdp.tdp = &tdp; - dp.pdp = &pdp; - dp.sdp.nFontSize = -9; - sprintf( szTitle, "Restored %s Component %d of %d %c%c", - szCurHdr? szCurHdr : "(No structure name)", iComponent+1, nNumComponents, - pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F' ); - ret = DisplayStructure( at, num_at, 0 /* nNumDeletedH*/, 0 /*bAdd_DT_to_num_H*/, - nNumRemovedProtons, /*NULL*/ nNumRemovedProtonsIsotopic, - 1 /*int bIsotopic*/, k, - pInChI, pAux, - 0 /*bAbcNumbers*/, &dp, 0 /*INCHI_MODE nMode*/, szTitle ); - return 0; -} -/******************************************************************************************************/ -int DisplayRestoredComponent( StrFromINChI *pStruct, int iComponent, int iAtNoOffset, INChI *pInChI, const char *szCurHdr ) -{ - int i, ret; - int num_at = pStruct->num_atoms; - int num_deleted_H = pStruct->num_deleted_H; - inp_ATOM *atom = pStruct->at2; - XYZ_COORD *pxyz = pStruct->pXYZ; - inp_ATOM *at = NULL; - char szTitle[512]; - DRAW_PARMS dp; - TBL_DRAW_PARMS tdp; - if ( !atom || num_at <= 0 || !pxyz ) { - return 0; - } - at = (inp_ATOM *)inchi_calloc( num_at + num_deleted_H, sizeof(at[0]) ); - if ( !at ) { - return RI_ERR_ALLOC; - } - memcpy( at, atom, (num_at + num_deleted_H) * sizeof(at[0]) ); - for ( i = 0; i < num_at; i ++ ) { - at[i].x = pxyz[i].xyz[0]; - at[i].y = pxyz[i].xyz[1]; - at[i].z = pxyz[i].xyz[2]; - } - memset( &dp, 0, sizeof(dp)); - memset( &tdp, 0, sizeof(tdp) ); - //memset( &pdp, 0, sizeof(pdp) ); - dp.sdp.tdp = &tdp; - dp.pdp = &pdp; - dp.sdp.nFontSize = -9; - sprintf( szTitle, "DBG Restored %s Component %d %c%c", szCurHdr? szCurHdr : "(No structure name)", iComponent+1, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F' ); - ret = DisplayStructure( at, num_at, 0 /* nNumDeletedH*/, 0 /*bAdd_DT_to_num_H*/, - 0 /*nNumRemovedProtons*/, NULL /*NUM_H *nNumRemovedProtonsIsotopic*/, - 1 /*int bIsotopic*/, 0 /*bTautomeric*/, - &pInChI, NULL /* INChI_Aux **cur_INChI_Aux*/, - 0 /*bAbcNumbers*/, &dp, 0 /*INCHI_MODE nMode*/, szTitle ); - inchi_free( at ); - return 0; -} -/**************************************************************************************/ -int DisplayStructureComponents( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, char *szCurHdr, - ICHICONST SRM *pSrm, int bReqNonTaut, StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], - InpInChI *pOneInput ) -{ - int iInchiRec, iMobileH, iCurMobH, iAlternH, num_components, tot_just_atoms, tot_removed_H, tot_atoms, cur_nA, cur_nH; - int k, i, j, ret, iCurAtomOffs, iNxtAtomOffs, iCurDelHOffs, iNxtDelHOffs, len, len2, iShiftH, icomp; - int *nAtomOffs=NULL, *nDelHOffs=NULL, bNoCoord=0, iNewCoord=0, nNewCoord=0; - double x_max=-1.0e16, x_min = 1.0e16, y_max=-1.0e16, y_min=1.0e16, delta = 0.0; - StrFromINChI *pStruct1; - inp_ATOM *at=NULL, *a; - - if (!ip->bDisplayCompositeResults && !ip->bDisplay ) { - return 0; - } - - ret = 0; - pOneInput->num_atoms = 0; - /* select highest detail level */ - if ( num_components = pOneInput->nNumComponents[INCHI_REC][TAUT_NON] ) { - iInchiRec = INCHI_REC; - iMobileH = TAUT_NON; - } else - if ( num_components = pOneInput->nNumComponents[INCHI_REC][TAUT_YES] ) { - iInchiRec = INCHI_REC; - iMobileH = TAUT_YES; - } else - if ( num_components = pOneInput->nNumComponents[INCHI_BAS][TAUT_NON] ) { - iInchiRec = INCHI_BAS; - iMobileH = TAUT_NON; - } else - if ( num_components = pOneInput->nNumComponents[INCHI_BAS][TAUT_YES] ) { - iInchiRec = INCHI_BAS; - iMobileH = TAUT_YES; - } else { - return 0; /* no components available */ - } - for ( k = 0; k < num_components; k ++ ) { - if ( pStruct[iInchiRec][iMobileH][k].bDeleted ) - break; - } - num_components = k; - - nAtomOffs = (int*) inchi_malloc((num_components+1) * sizeof(nAtomOffs[0])); - nDelHOffs = (int*) inchi_malloc((num_components+1) * sizeof(nDelHOffs[0])); - if ( !nAtomOffs || !nDelHOffs ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - /* count number of atoms and removed H */ - tot_just_atoms = tot_removed_H = tot_atoms = 0; - iAlternH = (iMobileH==TAUT_NON && pOneInput->nNumComponents[iInchiRec][TAUT_YES])? TAUT_YES : -1; - nAtomOffs[0] = nDelHOffs[0] = 0; - for ( k = 0; k < num_components; k ++ ) { - pStruct1 = pStruct[iInchiRec][iMobileH][k].num_atoms? pStruct[iInchiRec][iMobileH]+k : - iAlternH>=0 && - pStruct[iInchiRec][iAlternH][k].num_atoms? pStruct[iInchiRec][iAlternH]+k : NULL; - if ( !pStruct1 || !pStruct1->at2 || !pStruct1->num_atoms ) { - cur_nA = cur_nH = 0; - } else { - cur_nA = pStruct1->num_atoms; - cur_nH = pStruct1->num_deleted_H; - if ( cur_nA && !pStruct1->pXYZ ) { - if ( !k ) { - ret = 0; /* no coordinates available */ - goto exit_function; - } else { - bNoCoord ++; - } - } - } - nAtomOffs[k+1] = nAtomOffs[k] + cur_nA; - nDelHOffs[k+1] = nDelHOffs[k] + cur_nH; - } - tot_just_atoms = nAtomOffs[num_components]; - /* shift all H to the end */ - for ( k = 0; k <= num_components; k ++ ) { - nDelHOffs[k] += tot_just_atoms; - } - tot_atoms = nDelHOffs[num_components]; - - /* merge atoms together: 1. Allocate */ - if ( NULL == (at = (inp_ATOM *) inchi_malloc( (tot_atoms+1) * sizeof(at[0]) ) ) ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - if ( !tot_atoms ) { - ret = 0; - goto exit_function; /* empty structure */ - } - /* merge atoms together: 2. Copy */ - for ( k = 0; k < num_components; k ++ ) { - pStruct1 = pStruct[iInchiRec][iMobileH][k].num_atoms? pStruct[iInchiRec][iCurMobH=iMobileH]+k : - iAlternH>=0 && - pStruct[iInchiRec][iAlternH][k].num_atoms? pStruct[iInchiRec][iCurMobH=iAlternH]+k : NULL; - if ( len = nAtomOffs[k+1] - nAtomOffs[k] ) { - XYZ_COORD *pxyz = pStruct1->pXYZ; - len2 = nDelHOffs[k+1] - nDelHOffs[k]; /* do not separate H from the atom: we will not need them */ - iCurAtomOffs = nAtomOffs[k]; - a = at + iCurAtomOffs; - memcpy( a, pStruct1->at2, (len+len2) * sizeof(at[0]) ); - DisconnectedConnectedH( a, len, len2 ); - if ( pxyz ) { - for ( i = 0; i < len; i ++ ) { - a[i].x = pxyz[i].xyz[0]; - x_max = inchi_max( x_max, pxyz[i].xyz[0] ); - x_min = inchi_min( x_min, pxyz[i].xyz[0] ); - a[i].y = pxyz[i].xyz[1]; - y_max = inchi_max( y_max, pxyz[i].xyz[1] ); - y_min = inchi_min( y_min, pxyz[i].xyz[1] ); - a[i].z = pxyz[i].xyz[2]; - nNewCoord ++; - } - } else { - if ( !iNewCoord ) { - if ( !nNewCoord ) { - ret = 0; - goto exit_function; /* empty structure */ - } - delta = inchi_max(x_max - x_min, y_max - y_min); - if ( delta == 0.0 ) { - delta = 0.5 * (x_max+x_min); - if ( delta == 0.0 ) - delta = 1.0; - } else { - delta /= sqrt( (double)(nNewCoord+1) ); - } - } - for ( i = 0; i < len; i ++ ) { - a[i].x = x_max + delta; - a[i].y = y_max - iNewCoord * delta; - a[i].z = 0.0; - iNewCoord ++; - } - if ( pStruct1->pXYZ = (XYZ_COORD *)inchi_calloc(len, sizeof(pStruct1->pXYZ[0]) ) ) { - - for ( i = 0; i < len; i ++ ) { - pStruct1->pXYZ[i].xyz[0] = a[i].x; - pStruct1->pXYZ[i].xyz[1] = a[i].y; - pStruct1->pXYZ[i].xyz[2] = 0.0; - } - } - } - if ( ip->bDisplay || ip->bDisplayCompositeResults && 1 == num_components ) { - DisplayOneRestoredComponent( pStruct1, a, k, num_components, iCurMobH, szCurHdr ); - } - if ( !pxyz && pStruct1->pXYZ ) { - inchi_free( pStruct1->pXYZ ); - pStruct1->pXYZ = NULL; - } - } - } - /* merge atoms together: 3. Update atom numbers */ - icomp = 0; - if ( ip->bDisplayCompositeResults && num_components > 1 ) { - for ( k = 0; k < num_components; k ++ ) { - /* display each restored component if requested */ - iCurAtomOffs = nAtomOffs[k]; - iNxtAtomOffs = nAtomOffs[k+1]; - iCurDelHOffs = nDelHOffs[k]; - iNxtDelHOffs = nDelHOffs[k+1]; - len = nAtomOffs[k+1] - nAtomOffs[k]; /* number of atoms in a component excluding explicit H */ - iShiftH = iCurDelHOffs - len; - if ( !len ) { - continue; - } - icomp ++; /* current component number */ - /* update atoms */ - for ( i = iCurAtomOffs; i < iNxtAtomOffs; i ++ ) { - a = at+i; - for ( j = 0; j < a->valence; j ++ ) { - if ( a->neighbor[j] < len ) { - a->neighbor[j] += iCurAtomOffs; /* atom */ - } else { - ret = RI_ERR_PROGR; /* explicit H */ - goto exit_function; - } - } - a->orig_at_number += iCurAtomOffs; - } - } - tot_atoms = nAtomOffs[num_components]; - DisplayAllRestoredComponents( at, tot_atoms, szCurHdr ); - - } - -exit_function: - if ( at ) inchi_free( at ); /* in case of failure */ - if ( nAtomOffs ) inchi_free( nAtomOffs ); - if ( nDelHOffs ) inchi_free( nDelHOffs ); - return ret; -} -#endif -/**************************************************************************************/ -int AllInchiToStructure( ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd_inp, long num_inp, char *szCurHdr, - ICHICONST SRM *pSrm, int bHasSomeFixedH, StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], - InpInChI *pOneInput ) -{ - int iInchiRec, iMobileH, cur_num_comp, bCurI2A_Flag, k, ret, num_err; - INPUT_PARMS *ip, ip_loc; - STRUCT_DATA *sd, sd_loc; - long ulProcessingTime = 0; - inchiTime ulTStart; - - InchiTimeGet( &ulTStart ); - ip = &ip_loc; - *ip = *ip_inp; - sd = &sd_loc; - memset( sd, 0, sizeof(*sd)); - sd->ulStructTime = sd_inp->ulStructTime; - ret = 0; - num_err = 0; - for ( iInchiRec = 0; iInchiRec < INCHI_NUM; iInchiRec ++ ) { /* Disconnected/Connected */ - for ( iMobileH = 0; iMobileH < TAUT_NUM; iMobileH ++ ) { /* Mobile/Fixed H */ - cur_num_comp = pOneInput->nNumComponents[iInchiRec][iMobileH]; - if ( !cur_num_comp ) { - continue; - } - /* allocate memory for all existing components */ - pStruct[iInchiRec][iMobileH] = (StrFromINChI *)inchi_calloc( cur_num_comp, sizeof(pStruct[0][0][0])); - if ( !pStruct[iInchiRec][iMobileH] ) { - ret = RI_ERR_ALLOC; - goto exit_error; - } - /* set conversion mode */ - bCurI2A_Flag = (iMobileH? 0: I2A_FLAG_FIXEDH) | (iInchiRec? I2A_FLAG_RECMET : 0); - if ( iMobileH ) { - ip->nMode &= ~REQ_MODE_BASIC; - } else { - ip->nMode |= REQ_MODE_BASIC; - } - /* InChI --> structure conversion for all components except duplicated */ - for ( k = 0; k < cur_num_comp; k ++ ) { /* components */ - if ( !iMobileH && !pOneInput->pInpInChI[iInchiRec][iMobileH][k].nNumberOfAtoms || - pOneInput->pInpInChI[iInchiRec][iMobileH][k].bDeleted || - pOneInput->pInpInChI[iInchiRec][iMobileH][k].nLink < 0 ) { - - pStruct[iInchiRec][iMobileH][k].nLink = pOneInput->pInpInChI[iInchiRec][iMobileH][k].nLink; - pStruct[iInchiRec][iMobileH][k].bDeleted = pOneInput->pInpInChI[iInchiRec][iMobileH][k].bDeleted; - continue; /* do not create a structure out of an unavailable - Fixed-H InChI or out of the one present in Reconnected layer */ -#ifdef NEVER /* a wrong attempt to process deleted components here */ - if ( pStruct[iInchiRec][iMobileH][k].nLink = pOneInput->pInpInChI[iInchiRec][iMobileH][k].nLink ) { - continue; /* do not create a structure out of an unavailable - Fixed-H InChI or out of the one present in Reconnected layer */ - } else - if ( iMobileH && pOneInput->pInpInChI[iInchiRec][iMobileH][k].nNumberOfAtoms && - pOneInput->pInpInChI[iInchiRec][iMobileH][k].bDeleted && - pOneInput->pInpInChI[iInchiRec][iMobileH][0].bDeleted ) { - /* all components are protons */ - ; - } else { - continue; - } -#endif - } - if ( bHasSomeFixedH && iMobileH && k < pOneInput->nNumComponents[iInchiRec][TAUT_NON] && - pOneInput->pInpInChI[iInchiRec][TAUT_NON][k].nNumberOfAtoms ) { - continue; /* do not process Mobile-H if Fixed-H is requested and exists */ - } - pStruct[iInchiRec][iMobileH][k].pSrm = pSrm; - pStruct[iInchiRec][iMobileH][k].iInchiRec = iInchiRec; - pStruct[iInchiRec][iMobileH][k].iMobileH = iMobileH; - - /****************************************************/ - /* */ - /* Convert InChI of one component into a Structure */ - /* */ - /****************************************************/ - - ret = InChI2Atom( ip, sd, szCurHdr, num_inp, pStruct[iInchiRec][iMobileH]+k, k, - 0 /* AtNoOffset*/, bCurI2A_Flag, bHasSomeFixedH, pOneInput ); - pStruct[iInchiRec][iMobileH][k].nLink = pOneInput->pInpInChI[iInchiRec][iMobileH][k].nLink; - if ( ret < 0 ) { -#if ( bRELEASE_VERSION != 1 ) -#ifndef TARGET_API_LIB - /* !!! Conversion Error -- Ignore for now !!! */ - fprintf( stdout, "%ld %s Conversion failed: %d, %c%c comp %d\n", - num_inp, szCurHdr? szCurHdr : "Struct", ret, iInchiRec? 'R':'D', iMobileH? 'M':'F', k+1); -#endif -#endif - if ( ret == CT_USER_QUIT_ERR ) { - goto exit_error; - } - pStruct[iInchiRec][iMobileH][k].nError = ret; - ret = 0; /* force to ignore the errors for now !!!! */ - num_err ++; - } - } - } - } -exit_error: - ulProcessingTime += InchiTimeElapsed( &ulTStart ); - sd->ulStructTime += ulProcessingTime; - return ret<0? ret : num_err; -} -/**************************************************************************************/ -int AddProtonAndIsoHBalanceToMobHStruct( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, - long num_inp, int bHasSomeFixedH, char *szCurHdr, - StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], InpInChI *pOneInput) -{ - COMPONENT_REM_PROTONS nToBeRemovedByNormFromRevrs[INCHI_NUM]; - int nRemovedByNormFromRevrs[INCHI_NUM]; - int nRemovedByRevrs[INCHI_NUM]; - - int nDeltaFromDisconnected = 0, nRemovedProtonsByNormFromRevrs, nRemovedProtonsByRevrs, num_changes = 0; - NUM_H nIsoDeltaFromDisconnected[NUM_H_ISOTOPES]; - int iInchiRec, i, k, k1, ret = 0; - int nChargeInChI, nChargeRevrs; - - if ( bHasSomeFixedH ) { - return 0; /* 2005-03-01 */ - } - - /* num protons removed by InChI Normalization from the original structure */ - for ( i = 0; i < INCHI_NUM; i ++ ) { - nToBeRemovedByNormFromRevrs[i].nNumRemovedProtons = pOneInput->nNumProtons[i][TAUT_YES].nNumRemovedProtons; - for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) { - nToBeRemovedByNormFromRevrs[i].nNumRemovedIsotopicH[k] = pOneInput->nNumProtons[i][TAUT_YES].nNumRemovedIsotopicH[k]; - } - } - /* accumulate here num. protons removed by the normalization from the reversed structure */ - nRemovedByNormFromRevrs[INCHI_BAS] = - nRemovedByNormFromRevrs[INCHI_REC] = 0; - nRemovedByRevrs[INCHI_REC] = - nRemovedByRevrs[INCHI_BAS] = 0; - /* protons added/removed by InChI Normalization to/from Restored Structure might have been added by StructureRestore */ - for ( iInchiRec = 0; iInchiRec < INCHI_NUM; iInchiRec ++ ) { - for ( k = 0; k < pOneInput->nNumComponents[iInchiRec][TAUT_YES]; k ++ ) { - if ( !bInpInchiComponentExists( pOneInput, iInchiRec, TAUT_YES, k ) ) { - continue; - } - nRemovedProtonsByNormFromRevrs = 0; /* Num protons removed from the Restored Structure by InChI Normalization */ - nRemovedProtonsByRevrs = 0; /* Num protons removed by the Reconstruction from the Restored Structure */ - if ( iInchiRec == INCHI_REC || iInchiRec == INCHI_BAS && (k1=pStruct[iInchiRec][TAUT_YES][k].nLink) >= 0 ) { - - REV_INCHI *pRevInChI = &pStruct[iInchiRec][TAUT_YES][k].RevInChI; - INChI_Aux **pINChI_Aux2 = pRevInChI->pINChI_Aux[iInchiRec][0]; /* component 0*/ - INChI **pINChI_Revr = pRevInChI->pINChI[iInchiRec][0]; - INChI *pINChI_Orig = pOneInput->pInpInChI[iInchiRec][TAUT_YES]+k; - nChargeRevrs = pINChI_Revr? pINChI_Revr[TAUT_YES]->nTotalCharge : NO_VALUE_INT; - nChargeInChI = pINChI_Orig->nTotalCharge; - if ( pINChI_Aux2 ) { - nRemovedProtonsByNormFromRevrs = pINChI_Aux2[TAUT_YES]->nNumRemovedProtons; - } - nRemovedProtonsByRevrs = pStruct[iInchiRec][TAUT_YES][k].nNumRemovedProtonsByRevrs; - pStruct[iInchiRec][TAUT_YES][k].nChargeRevrs = nChargeRevrs; - pStruct[iInchiRec][TAUT_YES][k].nChargeInChI = nChargeInChI; - } else - if ( 0 <= ( k1 = -(1+pStruct[iInchiRec][TAUT_YES][k].nLink) ) ) { - REV_INCHI *pRevInChI = &pStruct[INCHI_REC][TAUT_YES][k1].RevInChI; - INChI_Aux **pINChI_Aux2 = pRevInChI->pINChI_Aux[INCHI_BAS][0]; /* component 0 */ - INChI **pINChI_Revr = pRevInChI->pINChI[INCHI_BAS][0]; - INChI *pINChI_Orig = pOneInput->pInpInChI[INCHI_REC][TAUT_YES]+k1; - nChargeRevrs = pINChI_Revr? pINChI_Revr[TAUT_YES]->nTotalCharge : NO_VALUE_INT; - nChargeInChI = pINChI_Orig->nTotalCharge; - if ( pINChI_Aux2 ) { - nRemovedProtonsByNormFromRevrs = pINChI_Aux2[TAUT_YES]->nNumRemovedProtons; - } - /* this component cannot be disconnected because it is same as in reconnected layer */ - nRemovedProtonsByRevrs = pStruct[INCHI_REC][TAUT_YES][k1].nNumRemovedProtonsByRevrs; - pStruct[iInchiRec][TAUT_YES][k1].nChargeRevrs = nChargeRevrs; - pStruct[iInchiRec][TAUT_YES][k1].nChargeInChI = nChargeInChI; - } - /* how many protons (to be removed by InChI Normalization) to add = - (proton balance in InChI} - - {number of protons known to be removed by InChI Normalization from Reconstructed structure} */ - nToBeRemovedByNormFromRevrs[iInchiRec].nNumRemovedProtons -= nRemovedProtonsByNormFromRevrs; - nRemovedByNormFromRevrs[iInchiRec] += nRemovedProtonsByNormFromRevrs; - nRemovedByRevrs[iInchiRec] += nRemovedProtonsByRevrs; - pStruct[iInchiRec][TAUT_YES][k].nRemovedProtonsByNormFromRevrs = nRemovedProtonsByNormFromRevrs; - } - } - - /* Since fixed-H layer is missing we need to add proton balance to the components */ - memset( nIsoDeltaFromDisconnected, 0, sizeof(nIsoDeltaFromDisconnected) ); - for ( iInchiRec = INCHI_REC; INCHI_BAS <= iInchiRec; iInchiRec -- ) { - /* - if ( !pOneInput->nNumComponents[iInchiRec][TAUT_NON] && - pOneInput->nNumComponents[iInchiRec][TAUT_YES] ) { - */ - int bHasRecMobH = (iInchiRec==INCHI_BAS && pOneInput->nNumComponents[INCHI_REC][TAUT_YES]); - /* bHasRecMobH means all components that could not be disconnected are in reconnected part */ - if ( iInchiRec==INCHI_BAS ) { - /* second pass: common structures have been changed */ - nToBeRemovedByNormFromRevrs[INCHI_BAS].nNumRemovedProtons += nDeltaFromDisconnected; - } - /* after proton removal InChI is recalculated */ - - ret = AddRemProtonsInRestrStruct( ip, sd, num_inp, bHasSomeFixedH, pStruct[iInchiRec][TAUT_YES], - pOneInput->nNumComponents[iInchiRec][TAUT_YES], - bHasRecMobH? pStruct[INCHI_REC][TAUT_YES] : NULL, - bHasRecMobH? pOneInput->nNumComponents[INCHI_REC][TAUT_YES]:0, - &nToBeRemovedByNormFromRevrs[iInchiRec].nNumRemovedProtons, - (iInchiRec==INCHI_REC)?&nDeltaFromDisconnected : NULL); - if ( ret < 0 ) { - goto exit_function; - } - num_changes += ret; - /* - } - */ - } - /* if fixed-H layer is missing then we need to add isotopic exchangeable proton balance to the components */ - for ( iInchiRec = INCHI_REC; INCHI_BAS <= iInchiRec; iInchiRec -- ) { - /* - if ( !pOneInput->nNumComponents[iInchiRec][TAUT_NON] && - pOneInput->nNumComponents[iInchiRec][TAUT_YES] ) { - */ - int bHasRecMobH = (iInchiRec==INCHI_BAS && pOneInput->nNumComponents[INCHI_REC][TAUT_YES]); - /* bHasRecMobH means all components that could not be disconnected are in reconnected part */ - if ( iInchiRec==INCHI_BAS ) { - /* second pass: common structures have been changed */ - for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) { - nToBeRemovedByNormFromRevrs[INCHI_BAS].nNumRemovedIsotopicH[k] += nIsoDeltaFromDisconnected[k]; - } - } - /* after proton removal InChI is recalculated */ - ret = AddRemIsoProtonsInRestrStruct( ip, sd, num_inp, bHasSomeFixedH, pStruct[iInchiRec][TAUT_YES], - pOneInput->nNumComponents[iInchiRec][TAUT_YES], - bHasRecMobH? pStruct[INCHI_REC][TAUT_YES] : NULL, - bHasRecMobH? pOneInput->nNumComponents[INCHI_REC][TAUT_YES]:0, - nToBeRemovedByNormFromRevrs[iInchiRec].nNumRemovedIsotopicH, - (iInchiRec==INCHI_REC)?nIsoDeltaFromDisconnected : NULL); - if ( ret < 0 ) { - goto exit_function; - } - num_changes += ret; - /* - } - */ - } - -exit_function: - return ret; -} - -/*************************************************************/ -void FreeStrFromINChI( StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], int nNumComponents[INCHI_NUM][TAUT_NUM] ) -{ - int iInchiRec, iMobileH, cur_num_comp, k, j; - StrFromINChI *pStruct1; - for ( iInchiRec = 0; iInchiRec < INCHI_NUM; iInchiRec ++ ) { - for ( iMobileH = 0; iMobileH < TAUT_NUM; iMobileH ++ ) { - cur_num_comp = nNumComponents[iInchiRec][iMobileH]; - if ( !cur_num_comp || !(pStruct1=pStruct[iInchiRec][iMobileH]) ) { - continue; - } - for ( k = 0; k < cur_num_comp; k ++ ) { - if ( pStruct1[k].at ) { - inchi_free(pStruct1[k].at); - } - if ( pStruct1[k].at2 ) { - inchi_free(pStruct1[k].at2); - } - if ( pStruct1[k].st ) { - inchi_free(pStruct1[k].st); - } - if ( pStruct1[k].pVA ) { - inchi_free(pStruct1[k].pVA); - } - /* - if ( pStruct1[k].ti.t_group ) { - inchi_free( pStruct1[k].ti.t_group ); - } - */ - if ( pStruct1[k].pXYZ ) { - inchi_free(pStruct1[k].pXYZ); - } - /*==== begin ====*/ - free_t_group_info( &pStruct1[k].ti ); - if ( pStruct1[k].endpoint ) { - inchi_free(pStruct1[k].endpoint); - } - if ( pStruct1[k].fixed_H ) { - inchi_free(pStruct1[k].fixed_H); - } - for ( j = 0; j < TAUT_NUM; j ++ ) { - if ( pStruct1[k].nAtno2Canon[j] ) - inchi_free( pStruct1[k].nAtno2Canon[j] ); - if ( pStruct1[k].nCanon2Atno[j] ) - inchi_free( pStruct1[k].nCanon2Atno[j] ); - } - /*===== end ======*/ - /* free INChI memory */ - FreeAllINChIArrays( pStruct1[k].RevInChI.pINChI, - pStruct1[k].RevInChI.pINChI_Aux, - pStruct1[k].RevInChI.num_components ); -#ifdef NEVER - /* don't do that: these are just pointers to OneInput structure members */ - Free_INChI( &pStruct1[k].pINChI ); - Free_INChI_Aux( &pStruct1[k].pINChI_Aux ); - if ( pStruct1[k].inp_norm_data ) { - FreeInpAtomData( pStruct1[k].inp_norm_data ); - inchi_free( pStruct1[k].inp_norm_data ); - } -#endif - } - inchi_free(pStruct[iInchiRec][iMobileH]); - pStruct[iInchiRec][iMobileH] = NULL; - } - } -} -/********************************************************************/ -void FreeInpInChI( InpInChI *pOneInput ) -{ - int iINChI, k, j; - for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) { - for ( j = 0; j < TAUT_NUM; j ++ ) { - if ( pOneInput->pInpInChI[iINChI][j] ) { - for ( k = 0; k < pOneInput->nNumComponents[iINChI][j]; k ++ ) { - Free_INChI_Members( &pOneInput->pInpInChI[iINChI][j][k] ); - } - inchi_free(pOneInput->pInpInChI[iINChI][j]); - pOneInput->pInpInChI[iINChI][j] = NULL; - } - if ( pOneInput->nNumProtons[iINChI][j].pNumProtons ) { - inchi_free( pOneInput->nNumProtons[iINChI][j].pNumProtons ); - pOneInput->nNumProtons[iINChI][j].pNumProtons = NULL; - } - } - } - if ( pOneInput->atom ) inchi_free(pOneInput->atom); - memset( pOneInput, 0, sizeof(*pOneInput) ); -} - -/***********************************************************************************************/ -int CompareAllOrigInchiToRevInChI(StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], InpInChI *pOneInput, int bReqNonTaut, - long num_inp, char *szCurHdr) -{ - int i, iInchiRec, iMobileH, iMobileHpStruct, num_components, iComponent, ret=0; - COMPONENT_REM_PROTONS nCurRemovedProtons, nNumRemovedProtons; - INChI *pInChI[TAUT_NUM]; - INCHI_MODE CompareInchiFlags[TAUT_NUM]; - memset( pOneInput->CompareInchiFlags[0], 0, sizeof(pOneInput->CompareInchiFlags[0]) ); - memset( &nNumRemovedProtons, 0, sizeof(nNumRemovedProtons) ); - - /* do we have reconnected InChI ?*/ - iInchiRec = INCHI_REC; - iMobileH = TAUT_NON; - if ( !pOneInput->nNumComponents[iInchiRec][TAUT_YES] && !pOneInput->nNumComponents[iInchiRec][TAUT_NON] ) { - iInchiRec = INCHI_BAS; - } - /* do we have Mobile or Fixed-H ? */ - if ( !pOneInput->nNumComponents[iInchiRec][TAUT_NON] || !bReqNonTaut ) { - iMobileH = TAUT_YES; /* index for pOneInput */ - } - /* if a restored structure has Fixed-H InChI then its mobile-H restored InChI is in Fixed-H pStruct */ - num_components = pOneInput->nNumComponents[iInchiRec][iMobileH]; - for ( iComponent = 0; iComponent < num_components; iComponent ++ ) { - int bMobileH = iMobileH; - pInChI[0] = pInChI[1] = NULL; - if ( pOneInput->pInpInChI[iInchiRec][bMobileH][iComponent].nNumberOfAtoms && - !pOneInput->pInpInChI[iInchiRec][bMobileH][iComponent].bDeleted ) { - /* the requested InChI layer exists */ - pInChI[0] = &pOneInput->pInpInChI[iInchiRec][bMobileH][iComponent]; - if ( bMobileH == TAUT_NON ) { - pInChI[1] = &pOneInput->pInpInChI[iInchiRec][TAUT_YES][iComponent]; - } - } else - if ( bMobileH == TAUT_NON && - pOneInput->pInpInChI[iInchiRec][TAUT_YES][iComponent].nNumberOfAtoms && - !pOneInput->pInpInChI[iInchiRec][TAUT_YES][iComponent].bDeleted ) { - /* the requested Fixed-H InChI layer does not exist; however, the Mobile-H does exist */ - bMobileH = TAUT_YES; /* only Mobile-H is available */ - pInChI[0] = &pOneInput->pInpInChI[iInchiRec][bMobileH][iComponent]; - } - memset( CompareInchiFlags, 0, sizeof(CompareInchiFlags) ); - memset( &nCurRemovedProtons, 0, sizeof(nCurRemovedProtons) ); - iMobileHpStruct = -#if ( bRELEASE_VERSION == 0 ) -#ifndef TARGET_API_LIB - /* legacy: reproduce old output */ - OldPrintCompareOneOrigInchiToRevInChI(pStruct[iInchiRec][bMobileH]+iComponent, pInChI, bMobileH, - iComponent, num_inp, szCurHdr); -#endif -#endif - /* one component comparison result bits */ - ret = CompareOneOrigInchiToRevInChI( pStruct[iInchiRec][bMobileH]+iComponent, pInChI, bMobileH, iComponent, - num_inp, szCurHdr, &nCurRemovedProtons, CompareInchiFlags); - if ( ret >= 0 ) { - /* no errors encountered -> accumulate removed protons from individual Mobile-H layers of components */ - nNumRemovedProtons.nNumRemovedProtons += nCurRemovedProtons.nNumRemovedProtons; - for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { - nNumRemovedProtons.nNumRemovedIsotopicH[i] += nCurRemovedProtons.nNumRemovedIsotopicH[i]; - } - /* accumulate compare bits */ - for ( i = 0; i < TAUT_NUM; i ++ ) { - pOneInput->CompareInchiFlags[0][i] |= CompareInchiFlags[i]; - } - } else { - goto exit_function; - } - } - if ( iMobileH == TAUT_YES ) { - if ( pOneInput->nNumProtons[iInchiRec][iMobileH].pNumProtons ) { - ret = RI_ERR_PROGR; /* in Mobile-H case proton balances are split between compoments */ - } else { - /* num removed protons in orig. InChI num removed protons in restored InChi */ - if ( nNumRemovedProtons.nNumRemovedProtons != pOneInput->nNumProtons[iInchiRec][iMobileH].nNumRemovedProtons ) { - /* restored structure InChI has less or more removed protons */ - pOneInput->CompareInchiFlags[0][TAUT_YES] |= INCHIDIFF_MOBH_PROTONS; -#if ( bRELEASE_VERSION == 0 ) - /* debug output only */ - { - int num_H_AddedByRevrs = pOneInput->nNumProtons[iInchiRec][iMobileH].nNumRemovedProtons - - nNumRemovedProtons.nNumRemovedProtons; - fprintf( stdout, "COMPARE_INCHI: %ld: %s %cM: Proton balance (Diff: %d, RevrsRem=%d)\n", - num_inp, szCurHdr? szCurHdr : "Struct", iInchiRec? 'R':'D', - pOneInput->nNumProtons[iInchiRec][iMobileH].nNumRemovedProtons,num_H_AddedByRevrs); - } -#endif - } - for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { - if ( nNumRemovedProtons.nNumRemovedIsotopicH[i] != pOneInput->nNumProtons[iInchiRec][TAUT_YES].nNumRemovedIsotopicH[i] ) { - pOneInput->CompareInchiFlags[0][TAUT_YES] |= INCHIDIFF_MOB_ISO_H; -#if ( bRELEASE_VERSION == 0 ) - /* debug output only */ - { - int num_H_AddedByRevrs = pOneInput->nNumProtons[iInchiRec][TAUT_YES].nNumRemovedIsotopicH[i] - - nNumRemovedProtons.nNumRemovedIsotopicH[i]; - fprintf( stdout, "COMPARE_INCHI: %ld: %s %cM: Iso Xchg %dH balance (Diff: %d, RevrsRem=%d)\n", - num_inp, szCurHdr? szCurHdr : "Struct", iInchiRec? 'R':'D', i+1, - pOneInput->nNumProtons[iInchiRec][TAUT_YES].nNumRemovedIsotopicH[i],num_H_AddedByRevrs); - } -#endif - } - } - } - } - -exit_function: - return ret; -} -/***********************************************************************************************/ -int CompareAllDisconnectedOrigInchiToRevInChI(StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], - InpInChI *pOneInput, int bHasSomeFixedH, - long num_inp, char *szCurHdr) -{ - int i, k, m, n, iInChI, iMobileH, bMobileH, ifk; - int num_components_D, num_components_R; - int nNumCompHaveSeparateProtons_D, nNumCompHaveSeparateProtons_R; - int num_fragments_D, num_fragments_R, num_fragments_DR, num_fragments, iComponent, ret; - int ifInChI, ifMobileH, bfMobileH, nLink; - COMPONENT_REM_PROTONS nNumRemovedProtons_D; /* removed from the disconnected layer of the Input InChI */ - COMPONENT_REM_PROTONS nNumRemovedProtons_D_all; /* if only totals are avalable */ - COMPONENT_REM_PROTONS nNumRemovedProtons_R; /* removed from disconnected layer of the reconstructed struct */ - COMPONENT_REM_PROTONS nNumRemovedProtons_R_all; - INCHI_MODE CompareInchiFlags[TAUT_NUM]; - StrFromINChI *pStruct1; - INChI_Aux *pINChI_Aux; - INCHI_SORT *pINChISort1 = NULL; /* from reversed structure */ - INCHI_SORT *pINChISort2 = NULL; /* original input InChI */ - int nNumNonTaut1=0, nNumNonTaut2=0; - - ret = 0; - memset( pOneInput->CompareInchiFlags[1], 0, sizeof(pOneInput->CompareInchiFlags[1]) ); - - /* count components that are not subject to disconnection */ - if ( !pOneInput->nNumComponents[INCHI_REC][TAUT_YES] && - !pOneInput->nNumComponents[INCHI_REC][TAUT_NON] ) { - return 0; /* nothing to do */ - } - - memset( &nNumRemovedProtons_D, 0, sizeof(nNumRemovedProtons_D) ); - memset( &nNumRemovedProtons_R, 0, sizeof(nNumRemovedProtons_R) ); - memset( &nNumRemovedProtons_D_all, 0, sizeof(nNumRemovedProtons_D_all) ); - memset( &nNumRemovedProtons_R_all, 0, sizeof(nNumRemovedProtons_R_all) ); - memset( CompareInchiFlags, 0, sizeof(CompareInchiFlags) ); - - num_components_D = inchi_max( pOneInput->nNumComponents[INCHI_BAS][TAUT_YES], - pOneInput->nNumComponents[INCHI_BAS][TAUT_NON] ); - num_components_R = inchi_max( pOneInput->nNumComponents[INCHI_REC][TAUT_YES], - pOneInput->nNumComponents[INCHI_REC][TAUT_NON] ); - /***********************************************************************************************/ - /* InpInChI: count fragments -- disconnected components that do not match reconnected */ - /* Accumulate removed H and isotopic H from ALL Fixed-H disconnected components except deleted */ - /* This segment collects info from the original InChI */ - /***********************************************************************************************/ - /*---- Original InChI ----*/ - num_fragments_D = 0; - iInChI = INCHI_BAS; - iMobileH = bHasSomeFixedH? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES; - nNumCompHaveSeparateProtons_D = 0; - - /* in case of Mobile-H components here are the proton totals from the original InChI disconn. layer */ - nNumRemovedProtons_D.nNumRemovedProtons = pOneInput->nNumProtons[iInChI][TAUT_YES].nNumRemovedProtons; - memcpy( nNumRemovedProtons_D.nNumRemovedIsotopicH, - pOneInput->nNumProtons[iInChI][TAUT_YES].nNumRemovedIsotopicH, - sizeof(nNumRemovedProtons_D.nNumRemovedIsotopicH) ); /* total for the disconnected layer */ - - for ( k = 0; k < num_components_D; k ++ ) { - bMobileH = iMobileH; - if ( !bInpInchiComponentExists( pOneInput, iInChI, bMobileH, k ) ) { - if ( bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) ) { - bMobileH = TAUT_YES; - } else { - continue; /* component is missing ??? */ - } - } - if ( 0 > (nLink = pOneInput->pInpInChI[iInChI][bMobileH][k].nLink) ) { - /* component in Disconnected layer is linked to the identical one in the Reconnected layer */ - if ( pOneInput->nNumProtons[INCHI_REC][TAUT_YES].pNumProtons ) { - nNumCompHaveSeparateProtons_D ++; - nLink = -(1+nLink); - nNumRemovedProtons_D.nNumRemovedProtons += pOneInput->nNumProtons[INCHI_REC][TAUT_YES].pNumProtons[nLink].nNumRemovedProtons; - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - nNumRemovedProtons_D.nNumRemovedIsotopicH[m] += pOneInput->nNumProtons[INCHI_REC][TAUT_YES].pNumProtons[nLink].nNumRemovedIsotopicH[m]; - } - } - continue; /* same as reconnected */ - } - /* component in the reconnected layer that was disconnected */ - nNumNonTaut2 += (bMobileH == TAUT_NON); - if ( pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons ) { - nNumCompHaveSeparateProtons_D ++; - nNumRemovedProtons_D.nNumRemovedProtons += pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons[k].nNumRemovedProtons; - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - nNumRemovedProtons_D.nNumRemovedIsotopicH[m] += pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH[m]; - } - } - num_fragments_D ++; /* number of disconnected fragments from original reconnected structure */ - } - /* in case of Mobile-H components here are the proton totals from the original InChI */ - /* - nNumRemovedProtons_D_all.nNumRemovedProtons = pOneInput->nNumProtons[iInChI][TAUT_YES].nNumRemovedProtons; - memcpy( nNumRemovedProtons_D_all.nNumRemovedIsotopicH, - pOneInput->nNumProtons[iInChI][TAUT_YES].nNumRemovedIsotopicH, - sizeof(nNumRemovedProtons_D_all.nNumRemovedIsotopicH) ); - - */ - /****************************************************************************************************/ - /* count fragments in reconstructed reconnected structure */ - /* accumulate removed H and isotopic H from ALL reconstructed reconnected components except deleted */ - /* This segment collects info from the reconstructed structure InChI */ - /****************************************************************************************************/ - /*---- InChI from the reconstructed reconnected structure ----*/ - num_fragments_R = 0; - iInChI = INCHI_REC; - iMobileH = bHasSomeFixedH? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES; - nNumCompHaveSeparateProtons_R = 0; - for ( k = 0; k < num_components_R; k ++ ) { - bMobileH = iMobileH; - if ( !bInpInchiComponentExists( pOneInput, iInChI, bMobileH, k ) ) { - if ( bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) ) { - bMobileH = TAUT_YES; - } else { - continue; /* component is missing ??? (Deleted proton in Mobile-H layer) */ - } - } - if ( 0 < pOneInput->pInpInChI[iInChI][bMobileH][k].nLink ) { - /* this reconstructed reconnected component was NOT DISCONNECTED */ - /* same component is in the disconnected layer, it has no metal atoms or is an isolated metal atom */ - pStruct1 = pStruct[iInChI][bMobileH]+k; - ifMobileH = TAUT_YES; /* Mobile-H Aux_Info contains number removed protons */ - ifInChI = INCHI_BAS; /* this component cannot be reconnected */ - ifk = 0; /* 0th component since it is InChI of a single component */ - /* The statement in the following line is *WRONG*, component number mixed with bMobileH: */ - /* in RevInchi, when only Mobile-H is present then its only non-NULL InChI has index 0==TAUT_NON */ - if ( bRevInchiComponentExists( pStruct1, ifInChI, ifMobileH, ifk ) ) { - /* count protons */ - pINChI_Aux = pStruct1->RevInChI.pINChI_Aux[ifInChI][ifk][ifMobileH]; - if ( pINChI_Aux ) { - nNumRemovedProtons_R.nNumRemovedProtons += pINChI_Aux->nNumRemovedProtons; - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - nNumRemovedProtons_R.nNumRemovedIsotopicH[m] += pINChI_Aux->nNumRemovedIsotopicH[m]; - } - } - } - nNumCompHaveSeparateProtons_R += bRevInchiComponentExists( pStruct1, ifInChI, ALT_TAUT(ifMobileH), ifk ); - continue; /* same as disconnected, has no metal atoms */ - } - /* this reconstructed reconnected component WAS DISCONNECTED; check its fragments */ - /* it does not have same component in the disconnected layer */ - pStruct1 = pStruct[iInChI][bMobileH]+k; - num_fragments = pStruct1->RevInChI.num_components[INCHI_BAS]; - ifInChI = INCHI_BAS; /* disconnected layer */ - ifMobileH = bHasSomeFixedH? TAUT_NON : TAUT_YES; - for ( ifk = 0; ifk < num_fragments; ifk ++ ) { - bfMobileH = ifMobileH; - if ( !bRevInchiComponentExists( pStruct1, ifInChI, bfMobileH, ifk ) ) { - if ( bRevInchiComponentExists( pStruct1, ifInChI, TAUT_YES, ifk ) ) { - bfMobileH = TAUT_YES; - } else { - continue; /* fragment does not exist ??? */ - } - } - nNumNonTaut1 += (bfMobileH == TAUT_NON); - nNumCompHaveSeparateProtons_R += (bfMobileH == TAUT_NON); - /* count protons from fragments made by metal disconnection */ - pINChI_Aux = pStruct1->RevInChI.pINChI_Aux[ifInChI][ifk][TAUT_YES]; - if ( pINChI_Aux ) { - nNumRemovedProtons_R.nNumRemovedProtons += pINChI_Aux->nNumRemovedProtons; - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - nNumRemovedProtons_R.nNumRemovedIsotopicH[m] += pINChI_Aux->nNumRemovedIsotopicH[m]; - } - } - num_fragments_R ++; /* number of disconnected fragments from reconstructed reconnected structure */ - } - } - /*---------------- special treatment of the last reconstructed component -----------------*/ - /*---------------- this may contain separate protons added by the reconstruction ---------*/ - k = num_components_R - 1; - pStruct1 = pStruct[iInChI][iMobileH]+k; - if ( iMobileH == TAUT_YES && !bHasSomeFixedH && - bInpInchiComponentDeleted( pOneInput, iInChI, iMobileH, k ) && - (num_fragments = pStruct1->RevInChI.num_components[INCHI_BAS]) ) { - - ifInChI = INCHI_BAS; /* disconnected layer */ - ifMobileH = TAUT_YES; - for ( ifk = 0; ifk < num_fragments; ifk ++ ) { - bfMobileH = ifMobileH; - if ( !bRevInchiComponentDeleted( pStruct1, ifInChI, bfMobileH, ifk ) ) { - continue; /* fragment does exist ??? Should not happen */ - } - /* - nNumNonTaut1 += (bfMobileH == TAUT_NON); - nNumCompHaveSeparateProtons_R += (bfMobileH == TAUT_NON); - */ - /* count protons from fragments made by metal disconnection */ - pINChI_Aux = pStruct1->RevInChI.pINChI_Aux[ifInChI][ifk][TAUT_YES]; - if ( pINChI_Aux ) { - nNumRemovedProtons_R.nNumRemovedProtons += pINChI_Aux->nNumRemovedProtons; - for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { - nNumRemovedProtons_R.nNumRemovedIsotopicH[m] += pINChI_Aux->nNumRemovedIsotopicH[m]; - } - } - /*num_fragments_R ++;*/ /* number of disconnected fragments from reconstructed reconnected structure */ - } - } - - - - num_fragments_DR = inchi_max( num_fragments_D, num_fragments_R ); - /* in case of correct reconstruction, num_fragments_D, num_fragments_R */ - - if ( !num_fragments_DR ) { - return 0; /* no component was disconnected */ - } - if ( num_fragments_D != num_fragments_R ) { - for ( i = 0; i < TAUT_NUM; i ++ ) { - if ( pOneInput->nNumComponents[INCHI_BAS][i] ) { - pOneInput->CompareInchiFlags[1][i] |= INCHIDIFF_PROBLEM; - } - } - return 1; /* severe error */ - } - - - pINChISort1 = (INCHI_SORT *)inchi_calloc(num_fragments_DR, sizeof(pINChISort1[0])); - pINChISort2 = (INCHI_SORT *)inchi_calloc(num_fragments_DR, sizeof(pINChISort2[0])); - if ( !pINChISort1 || !pINChISort2 ) { - ret = RI_ERR_ALLOC; - goto exit_function; - } - - /* accumulate original InChI of fragments -- disconnected components that do not match reconnected */ - iInChI = INCHI_BAS; - iMobileH = bHasSomeFixedH? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES; - for ( k = n = 0; k < num_components_D; k ++ ) { - bMobileH = iMobileH; - if ( !bInpInchiComponentExists( pOneInput, iInChI, bMobileH, k ) ) { - if ( bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) ) { - bMobileH = TAUT_YES; - } else { - continue; /* component is missing ??? (Deleted proton in Mobile-H layer) */ - } - } - if ( 0 > pOneInput->pInpInChI[iInChI][bMobileH][k].nLink ) { - continue; /* same as reconnected */ - } - /* the component exists in disconnected layer of the orig. InChI only: it is a fragment */ - pINChISort2[n].pINChI[bMobileH] = pOneInput->pInpInChI[iInChI][bMobileH] + k; - if ( bMobileH == TAUT_NON && - (bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) || - bInpInchiComponentDeleted( pOneInput, iInChI, TAUT_YES, k ) ) ) { - pINChISort2[n].pINChI[TAUT_YES] = pOneInput->pInpInChI[iInChI][TAUT_YES] + k; - } - /* the last sort key is a number of removed protons */ - pINChISort2[n].ord_number = pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons? - pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons[k].nNumRemovedProtons : 0; - pINChISort2[n].n1 = k; /* orig. InChI disconnected layer component number */ - pINChISort2[n].n2 = -1; /* no fragment index */ - n ++; - } - - /* accumulate fragments from the reconstructed structure */ - iInChI = INCHI_REC; - iMobileH = bHasSomeFixedH? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES; - for ( k = n = 0; k < num_components_R; k ++ ) { - bMobileH = iMobileH; - if ( !bInpInchiComponentExists( pOneInput, iInChI, bMobileH, k ) ) { - if ( bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) ) { - bMobileH = TAUT_YES; - } else { - continue; /* component is missing ??? (Deleted proton in Mobile-H layer) */ - } - } - /* the reconstructed structure */ - if ( 0 < pOneInput->pInpInChI[iInChI][bMobileH][k].nLink ) { - continue; /* same as disconnected, has no metal atoms */ - } - /* this reconstructed structure was disconnected */ - pStruct1 = pStruct[iInChI][bMobileH]+k; - num_fragments = pStruct1->RevInChI.num_components[INCHI_BAS]; - ifInChI = INCHI_BAS; - ifMobileH = bHasSomeFixedH? TAUT_NON : TAUT_YES; - for ( i = 0; i < num_fragments; i ++ ) { - bfMobileH = ifMobileH; - if ( !bRevInchiComponentExists( pStruct1, ifInChI, bfMobileH, i ) ) { - if ( bRevInchiComponentExists( pStruct1, ifInChI, TAUT_YES, i ) ) { - bfMobileH = TAUT_YES; - } else { - continue; /* component is missing ??? */ - } - } - pINChISort1[n].pINChI[bfMobileH] = pStruct1->RevInChI.pINChI[ifInChI][i][bfMobileH]; - if ( bfMobileH == TAUT_NON /*&& bRevInchiComponentExists( pStruct1, ifInChI, TAUT_YES, i )*/ ) { - pINChISort1[n].pINChI[TAUT_YES] = pStruct1->RevInChI.pINChI[ifInChI][i][TAUT_YES]; - /* remove Fixed-H InChI if is is identical to Mobile-H */ - /* do it exactly same way the identical components were removed from InpInChI */ - if ( !CompareReversedINChI( pINChISort1[n].pINChI[bfMobileH], - pINChISort1[n].pINChI[TAUT_YES], NULL, NULL ) ) { - pINChISort1[n].pINChI[bfMobileH] = NULL; /* remove Fixed-H layer */ - } else { - pINChISort1[n].ord_number = pStruct1->RevInChI.pINChI_Aux[ifInChI][i][TAUT_YES]->nNumRemovedProtons; - } - } - - pINChISort1[n].n1 = k; /* reconstructed reconnected structure component index */ - pINChISort1[n].n2 = i; /* index of a fragment made out of this component */ - n ++; - } - } - - /* sort fragment InChI before comparing them */ - qsort( pINChISort1, num_fragments_D, sizeof(pINChISort1[0]), CompINChITaut2 ); - qsort( pINChISort2, num_fragments_R, sizeof(pINChISort2[0]), CompINChITaut2 ); - - /* compare fragments -- components present in disconnected layer only */ - for ( iComponent = 0; iComponent < num_fragments_DR; iComponent ++ ) { - INChI *pInChI1[TAUT_NUM]; /* from reversed structure */ - INChI *pInChI2[TAUT_NUM]; /* original input InChI */ - for ( i = 0; i < TAUT_NUM; i ++ ) { - pInChI1[i] = pINChISort1[iComponent].pINChI[i]; - pInChI2[i] = pINChISort2[iComponent].pINChI[i]; - } - CompareTwoPairsOfInChI( pInChI1, pInChI2, !bHasSomeFixedH, CompareInchiFlags ); - } - - if ( /*nNumNonTaut1 && nNumNonTaut2 &&*/ bHasSomeFixedH ) { - if ( nNumCompHaveSeparateProtons_D || nNumCompHaveSeparateProtons_R ) { - /* for each component, compare number removed protons */ - /* comparison does not make sense if Disconnected Fixed-H layer is not present */ - for ( iComponent = 0; iComponent < num_fragments_DR; iComponent ++ ) { - NUM_H nNumRemovedIsotopicH1[NUM_H_ISOTOPES]; - NUM_H nNumRemovedIsotopicH2[NUM_H_ISOTOPES]; - - memset( nNumRemovedIsotopicH1, 0, sizeof(nNumRemovedIsotopicH1) ); - memset( nNumRemovedIsotopicH2, 0, sizeof(nNumRemovedIsotopicH2) ); - /* compare removed protons */ - if ( pINChISort1[iComponent].ord_number != pINChISort2[iComponent].ord_number ) { - CompareInchiFlags[TAUT_YES] |= INCHIDIFF_MOBH_PROTONS; /* diff number of removed protons */ - } - /* also compare removed isotopic atoms H */ - k = pINChISort2[iComponent].n1; /* input InChI, OneInput */ - if ( pOneInput->nNumProtons[INCHI_BAS][TAUT_YES].pNumProtons ) { - memcpy( nNumRemovedIsotopicH2, - pOneInput->nNumProtons[INCHI_BAS][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH, - sizeof( nNumRemovedIsotopicH2 ) ); - } - /* get fragments of reconstructed structure removed protons info */ - k = pINChISort1[iComponent].n1; /* restored component number */ - i = pINChISort1[iComponent].n2; /* subcomponent number */ - iInChI = INCHI_REC; - iMobileH = bHasSomeFixedH? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES; - bMobileH = iMobileH; - if ( !bInpInchiComponentExists( pOneInput, iInChI, bMobileH, k ) ) { - if ( bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) ) { - bMobileH = TAUT_YES; - } else { - goto compare_iso_H; - } - } - if ( pOneInput->pInpInChI[iInChI][bMobileH][k].nLink ) { - continue; - /* - ret = RI_ERR_PROGR; - goto exit_function; - */ - } - pStruct1 = pStruct[iInChI][bMobileH]+k; - num_fragments = pStruct1->RevInChI.num_components[INCHI_BAS]; - ifInChI = INCHI_BAS; - ifMobileH = bHasSomeFixedH? TAUT_NON : TAUT_YES; - if ( i < num_fragments ) { - bfMobileH = ifMobileH; - if ( !bRevInchiComponentExists( pStruct1, ifInChI, bfMobileH, i ) ) { - if ( bRevInchiComponentExists( pStruct1, ifInChI, TAUT_YES, i ) ) { - bfMobileH = TAUT_YES; - } else { - goto compare_iso_H; - } - } - memcpy( nNumRemovedIsotopicH1, - pStruct1->RevInChI.pINChI_Aux[ifInChI][i][TAUT_YES]->nNumRemovedIsotopicH, - sizeof( nNumRemovedIsotopicH1 ) ); - } -compare_iso_H: - if ( memcmp( nNumRemovedIsotopicH1, nNumRemovedIsotopicH2, sizeof( nNumRemovedIsotopicH1 ) ) ) { - CompareInchiFlags[TAUT_YES] |= INCHIDIFF_REM_ISO_H; - } - } - } - } else - /*if ( !nNumNonTaut1 && !nNumNonTaut2 || !bHasSomeFixedH )*/ { - /* compare totals for removed protons and isotopic H */ - if ( pOneInput->nNumProtons[INCHI_BAS][TAUT_YES].nNumRemovedProtons != - nNumRemovedProtons_R.nNumRemovedProtons ) { - CompareInchiFlags[TAUT_YES] |= INCHIDIFF_MOBH_PROTONS; - } - if ( memcmp( pOneInput->nNumProtons[INCHI_BAS][TAUT_YES].nNumRemovedIsotopicH, - nNumRemovedProtons_R.nNumRemovedIsotopicH, - sizeof( nNumRemovedProtons_R.nNumRemovedIsotopicH ) ) ) { - CompareInchiFlags[TAUT_YES] |= INCHIDIFF_REM_ISO_H; - } - } - - if ( !nNumNonTaut1 == !nNumNonTaut2 ) { - ; /* difference if(nNumNonTaut1 != nNumNonTaut2) will be caught in InChI comparison */ - } else - if ( nNumNonTaut1 ) { - /* reconstructed has Fixed-H while the original has not: extra Fixed-H layer */ - CompareInchiFlags[TAUT_YES] |= INCHIDIFF_WRONG_TAUT; - } else { - /* the original InChI has Fixed-H while the reconstructed one has not: missing Fixed-H layer */ - CompareInchiFlags[TAUT_YES] |= INCHIDIFF_NO_TAUT; - } - for ( i = 0; i < TAUT_NUM; i ++ ) { - pOneInput->CompareInchiFlags[1][i] |= CompareInchiFlags[i]; - } - - /* compare totals */ - if ( nNumRemovedProtons_R.nNumRemovedProtons != nNumRemovedProtons_D.nNumRemovedProtons ) { - CompareInchiFlags[TAUT_YES] |= INCHIDIFF_MOBH_PROTONS; /* diff number of removed protons */ - } - if ( memcmp( nNumRemovedProtons_R.nNumRemovedIsotopicH, - nNumRemovedProtons_D.nNumRemovedIsotopicH, - sizeof( nNumRemovedProtons_D.nNumRemovedIsotopicH ) ) ) { - CompareInchiFlags[TAUT_YES] |= INCHIDIFF_REM_ISO_H; - } - -exit_function: - - if ( pINChISort1 ) inchi_free( pINChISort1 ); - if ( pINChISort2 ) inchi_free( pINChISort2 ); - - return ret; -} -/******************************************************************************************************/ -int CompareTwoPairsOfInChI( INChI *pInChI1[TAUT_NUM], INChI *pInChI2[TAUT_NUM], - int bMobileH, INCHI_MODE CompareInchiFlags[] ) -{ - int iMobileH, err=0; - INCHI_MODE cmp; - for ( iMobileH = 0; iMobileH < TAUT_NUM; iMobileH ++ ) { - if ( !pInChI1[iMobileH] != !pInChI2[iMobileH] ) { - if ( iMobileH == TAUT_NON && - pInChI1[TAUT_YES] && pInChI1[TAUT_YES] ) { - CompareInchiFlags[iMobileH] |= INCHIDIFF_COMP_HLAYER; - } else { - CompareInchiFlags[iMobileH] |= INCHIDIFF_COMP_NUMBER; - } - continue; - } - if ( pInChI1[iMobileH] && pInChI2[iMobileH] ) { - cmp = CompareReversedINChI3( pInChI1[iMobileH], pInChI2[iMobileH], NULL, NULL, &err ); - if ( cmp ) { - CompareInchiFlags[iMobileH] |= cmp; - } - } - } - return err; -} -/******************************************************************************************************/ -int CompareOneOrigInchiToRevInChI(StrFromINChI *pStruct, INChI *pInChI[TAUT_NUM], int bMobileH, int iComponent, - long num_inp, char *szCurHdr, - COMPONENT_REM_PROTONS *nCurRemovedProtons, INCHI_MODE CompareInchiFlags[]) -{ - int ret = pStruct->RevInChI.nRetVal, err=0; - INCHI_MODE cmp; - if ( ret == _IS_OKAY || ret == _IS_WARNING ) { - /* ignore bMobileH for now */ - int i, i0, b /* created type */, b0 /* requested type*/, j, k; - /* pINChI[iINCHI][iComponent][bTaut] */ - /* i0 = requested Rec/Disconnected: 1/0 */ - /* i = what InChI creaded out of the restored structure */ - /* b0 = requested Mobile/Fixed-H: 1/0 */ - /* b = what InChI creaded out of the restored structure */ - i = i0 = pStruct->iINCHI; - b = b0 = pStruct->iMobileH; - if ( i == INCHI_REC && !pStruct->RevInChI.num_components[i] ) { - i = INCHI_BAS; - } - if ( b == TAUT_NON && (!pStruct->RevInChI.pINChI[i] || - !pStruct->RevInChI.pINChI[i][0][b] || - !pStruct->RevInChI.pINChI[i][0][b]->nNumberOfAtoms ) ) { - b = TAUT_YES; - } - if ( pStruct->bDeleted && (!pInChI[0] || pInChI[0]->bDeleted ) ) { - return 0; - } - - if ( pStruct->RevInChI.num_components[i] > 1 && - !pStruct->RevInChI.pINChI[i][1][b]->bDeleted || - pStruct->RevInChI.num_components[i] < 1 ) { - CompareInchiFlags[bMobileH] |= INCHIDIFF_COMP_NUMBER; - } - if ( b != b0 || b != bMobileH || b0 != bMobileH || i > i0 ) { - /* do not print messages about TAUT_YES instead of TAUT_NON */ - CompareInchiFlags[bMobileH] |= INCHIDIFF_COMP_HLAYER; - } - - if ( pStruct->RevInChI.num_components[i] ) { - /* compare InChI from restored structure; '0' in [i][0][b] is the first component */ - if ( b == TAUT_YES && pStruct->RevInChI.pINChI[i][0][b]->bDeleted && (!pInChI[0] || pInChI[0]->bDeleted ) ) { - /* the 1st component is made out of proton(s) and the input component is missing or also a proton */ - cmp = 0; - } else { - cmp = CompareReversedINChI3( pStruct->RevInChI.pINChI[i][0][b], pInChI[0], NULL, NULL, &err ); - if ( cmp ) { - CompareInchiFlags[bMobileH] |= cmp; - } - } - if ( b == b0 && b == TAUT_NON ) { - if ( pStruct->RevInChI.pINChI[i][0][TAUT_YES] && - !pStruct->RevInChI.pINChI[i][0][TAUT_YES]->bDeleted || - pInChI[1] && !pInChI[1]->bDeleted ) { - - /* in addition to fixed-H also compare mobile-H InChI */ - cmp = CompareReversedINChI3( pStruct->RevInChI.pINChI[i][0][TAUT_YES], pInChI[1], NULL, NULL, &err ); - if ( cmp ) { - CompareInchiFlags[TAUT_YES] |= cmp; - } - } - /* compare removed H */ - if ( pStruct->nNumRemovedProtonsMobHInChI != pStruct->RevInChI.pINChI_Aux[i][0][TAUT_YES]->nNumRemovedProtons ) { - CompareInchiFlags[TAUT_YES] |= INCHIDIFF_MOBH_PROTONS; - } - } - memset( nCurRemovedProtons, 0, sizeof(*nCurRemovedProtons) ); - for ( k = 0; k < pStruct->RevInChI.num_components[i]; k ++ ) { - if ( !k || pStruct->RevInChI.pINChI[i][k][TAUT_YES]->bDeleted ) { - /* get removed protons from the 1st component; add othere only if they are deleted protons */ - nCurRemovedProtons->nNumRemovedProtons += pStruct->RevInChI.pINChI_Aux[i][k][TAUT_YES]->nNumRemovedProtons; - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { - nCurRemovedProtons->nNumRemovedIsotopicH[j] += pStruct->RevInChI.pINChI_Aux[i][k][TAUT_YES]->nNumRemovedIsotopicH[j]; - } - } - } - } - } else { - CompareInchiFlags[bMobileH] |= INCHIDIFF_STR2INCHI_ERR; - } - return err; -} -/*************************************************************************************/ -INCHI_MODE CompareReversedStereoINChI3( INChI_Stereo *s1/* InChI from reversed struct */, INChI_Stereo *s2 /* input InChI */, ICR *picr) -{ - int ret = 0; - int j1, j2, num_eq, num_dif, num_extra_undf, num_miss_undf, num_in1_only, num_in2_only; - int bAddSb = !(picr->num_sb_undef_in1_only + picr->num_sb_in1_only + picr->num_sb_in2_only); - int bAddSc = !(picr->num_sc_undef_in1_only + picr->num_sc_in1_only + picr->num_sc_in2_only); - - int nNumSc1 = s1? s1->nNumberOfStereoCenters : 0; - int nNumSc2 = s2? s2->nNumberOfStereoCenters : 0; - int nNumSb1 = s1? s1->nNumberOfStereoBonds : 0; - int nNumSb2 = s2? s2->nNumberOfStereoBonds : 0; - - if ( (nNumSc1 || nNumSc1) && - ( nNumSc1 != nNumSc2 || - memcmp( s1->nNumber, s2->nNumber, nNumSc1*sizeof(s1->nNumber[0] ) ) || - memcmp( s1->t_parity, s2->t_parity, nNumSc1*sizeof(s1->t_parity[0]) ) ) ) { - - num_eq = num_dif = num_extra_undf = num_miss_undf = num_in1_only = num_in2_only = 0; - for ( j1 = j2 = 0; j1 < nNumSc1 && j2 < nNumSc2; ) { - if ( s1->nNumber[j1] == s2->nNumber[j2] ) { - if ( s1->t_parity[j1] == s2->t_parity[j2] ) { - num_eq ++; - } else { - num_dif ++; - } - j1 ++; - j2 ++; - } else - if ( s1->nNumber[j1] < s2->nNumber[j2] ) { - num_in1_only ++; - if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { - num_extra_undf ++; - } - if ( bAddSc ) { - if ( picr->num_sc_in1_only < ICR_MAX_SC_IN1_ONLY ) - picr->sc_in1_only[picr->num_sc_in1_only ++] = j1; - if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { - if ( picr->num_sc_undef_in1_only < ICR_MAX_SC_UNDF ) - picr->sc_undef_in1_only[picr->num_sc_undef_in1_only ++] = j1; - } - } - j1 ++; - } else { - num_in2_only ++; - if ( s2->t_parity[j2] == AB_PARITY_UNDF ) { - num_miss_undf ++; - } - if ( bAddSc ) { - if ( picr->num_sc_in2_only < ICR_MAX_SC_IN2_ONLY ) - picr->sc_in2_only[picr->num_sc_in2_only ++] = j2; - if ( s2->t_parity[j2] == AB_PARITY_UNDF ) { - if ( picr->num_sc_undef_in2_only < ICR_MAX_SC_UNDF ) - picr->sc_undef_in2_only[picr->num_sc_undef_in2_only ++] = j1; - } - } - j2 ++; - } - } - while ( j1 < nNumSc1 ) { - if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { - num_extra_undf ++; - } - num_in1_only ++; - if ( bAddSc ) { - if ( picr->num_sc_in1_only < ICR_MAX_SC_IN1_ONLY ) - picr->sc_in1_only[picr->num_sc_in1_only ++] = j1; - if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { - if ( picr->num_sc_undef_in1_only < ICR_MAX_SC_UNDF ) - picr->sc_undef_in1_only[picr->num_sc_undef_in1_only ++] = j1; - } - } - j1 ++; - } - while ( j2 < nNumSc2 ) { - if ( s2->t_parity[j2] == AB_PARITY_UNDF ) { - num_miss_undf ++; - } - num_in2_only ++; - if ( bAddSc ) { - if ( picr->num_sc_in2_only < ICR_MAX_SC_IN2_ONLY ) - picr->sc_in2_only[picr->num_sc_in2_only ++] = j2; - } - j2 ++; - } - if ( num_dif ) { - ret |= INCHIDIFF_SC_PARITY; - } - if ( num_in1_only ) { - if ( num_extra_undf ) { - ret |= INCHIDIFF_SC_EXTRA_UNDF; - } - if ( num_in1_only != num_extra_undf ) { - ret |= INCHIDIFF_SC_EXTRA; - } - } - if ( num_in2_only ) { - if ( num_miss_undf ) { - ret |= INCHIDIFF_SC_MISS_UNDF; - } - if ( num_in2_only != num_miss_undf ) { - ret |= INCHIDIFF_SC_MISS; - } - } - } - if ( s1 && s2 && (s2->nCompInv2Abs != 2) && s1->nCompInv2Abs != s2->nCompInv2Abs && s1->nCompInv2Abs && s2->nCompInv2Abs ) { - ret |= INCHIDIFF_SC_INV; /* 2007-07-13 DT: added (s2->nCompInv2Abs != 2) to fix bug reoprted by Yerin on 2007/02/28 */ - /* Bug description: falsely reported "Stereo centers/allenes: Falsely inverted" for /S2 or /S3 */ - } - - if ( (nNumSb1 || nNumSb2 ) && - (nNumSb1 != nNumSb2 || - memcmp( s1->nBondAtom1, s2->nBondAtom1, nNumSb1*sizeof(s1->nBondAtom1[0]) ) || - memcmp( s1->nBondAtom2, s2->nBondAtom2, nNumSb1*sizeof(s1->nBondAtom2[0]) ) || - memcmp( s1->b_parity, s2->b_parity, nNumSb1*sizeof(s1->b_parity[0]) ) ) ) { - - num_eq = num_dif = num_extra_undf = num_miss_undf = num_in1_only = num_in2_only = 0; - for ( j1 = j2 = 0; j1 < nNumSb1 && j2 < nNumSb2; ) { - if ( s1->nBondAtom1[j1] == s2->nBondAtom1[j2] && - s1->nBondAtom2[j1] == s2->nBondAtom2[j2] ) { - if ( s1->b_parity[j1] == s2->b_parity[j2] ) { - num_eq ++; - } else { - num_dif ++; - } - j1 ++; - j2 ++; - } else - if ( s1->nBondAtom1[j1] < s2->nBondAtom1[j2] || - s1->nBondAtom1[j1] == s2->nBondAtom1[j2] && s1->nBondAtom2[j1] < s2->nBondAtom2[j2]) { - num_in1_only ++; - if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { - num_extra_undf ++; - } - if ( bAddSb ) { - if ( picr->num_sb_in1_only < ICR_MAX_SB_IN1_ONLY ) - picr->sb_in1_only[picr->num_sb_in1_only ++] = j1; - if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { - if ( picr->num_sb_undef_in1_only < ICR_MAX_SB_UNDF ) - picr->sb_undef_in1_only[picr->num_sb_undef_in1_only ++] = j1; - } - } - j1 ++; - } else { - num_in2_only ++; - if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { - num_miss_undf ++; - } - if ( bAddSb ) { - if ( picr->num_sb_in2_only < ICR_MAX_SB_IN2_ONLY ) - picr->sb_in2_only[picr->num_sb_in2_only ++] = j2; - if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { - if ( picr->num_sb_undef_in2_only < ICR_MAX_SB_UNDF ) - picr->sb_undef_in2_only[picr->num_sb_undef_in2_only ++] = j1; - } - } - j2 ++; - } - } - while ( j1 < nNumSb1 ) { - num_in1_only ++; - if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { - num_extra_undf ++; - } - if ( bAddSb ) { - if ( picr->num_sb_in1_only < ICR_MAX_SB_IN1_ONLY ) - picr->sb_in1_only[picr->num_sb_in1_only ++] = j1; - if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { - if ( picr->num_sb_undef_in1_only < ICR_MAX_SB_UNDF ) - picr->sb_undef_in1_only[picr->num_sb_undef_in1_only ++] = j1; - } - } - j1 ++; - } - while ( j2 < nNumSb2 ) { - num_in2_only ++; - if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { - num_miss_undf ++; - } - if ( bAddSb ) { - if ( picr->num_sb_in2_only < ICR_MAX_SB_IN2_ONLY ) - picr->sb_in2_only[picr->num_sb_in2_only ++] = j2; - if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { - if ( picr->num_sb_undef_in2_only < ICR_MAX_SB_UNDF ) - picr->sb_undef_in2_only[picr->num_sb_undef_in2_only ++] = j1; - } - } - j2 ++; - } - if ( num_dif ) { - ret |= INCHIDIFF_SB_PARITY; - } - if ( num_in1_only ) { - if ( num_extra_undf ) { - ret |= INCHIDIFF_SB_EXTRA_UNDF; - } - if ( num_in1_only != num_extra_undf ) { - ret |= INCHIDIFF_SB_EXTRA; - } - } - if ( num_in2_only ) { - if ( num_miss_undf ) { - ret |= INCHIDIFF_SB_MISS_UNDF; - } - if ( num_in2_only != num_miss_undf ) { - ret |= INCHIDIFF_SB_MISS; - } - } - } - - return ret; -} -/*********************************************************************************************************/ -INCHI_MODE CompareReversedINChI3( INChI *i1 /* InChI from reversed struct */, INChI *i2 /* input InChI */, - INChI_Aux *a1, INChI_Aux *a2, int *err ) -{ - INCHI_MODE ret = 0; - INChI_Stereo *Stereo1=NULL, *Stereo2=NULL; - int n1, n2, m, j, j1, j2, ret2, num_H1, num_H2; - ICR icr; - ICR *picr = &icr; - - *err = 0; - - memset( picr, 0, sizeof(*picr) ); - - if ( i1 == NULL && i2 == NULL ) - return 0; - if ( (i1 == NULL) ^ (i2 == NULL) ) { - ret |= INCHIDIFF_PROBLEM; /* one InChI exists while another doesn't */ - goto exit_function; - } - - if ( i1->nErrorCode == i2->nErrorCode ) { - if ( i1->nErrorCode ) { - ret |= INCHIDIFF_PROBLEM; /* both InChI have same error codes */ - goto exit_function; - } - } else { - ret |= INCHIDIFF_PROBLEM; /* at least one InChI has an error code */ - goto exit_function; - } - - if ( i1->nNumberOfAtoms != i2->nNumberOfAtoms ) { - ret |= INCHIDIFF_NUM_AT; - goto exit_function; - } - if ( i1->nNumberOfAtoms > 0 ) { - if ( memcmp( i1->nAtom, i2->nAtom, i1->nNumberOfAtoms*sizeof(i1->nAtom[0]) ) ) { - ret |= INCHIDIFF_ATOMS; - goto exit_function; - } - /* INCHIDIFF_NON_TAUT_H, INCHIDIFF_MORE_FH, INCHIDIFF_LESS_FH */ - if ( memcmp( i1->nNum_H, i2->nNum_H, i1->nNumberOfAtoms*sizeof(i1->nNum_H[0]) ) ) { - ret |= INCHIDIFF_POSITION_H; - for ( j1 = 0; j1 < i1->nNumberOfAtoms; j1 ++ ) { - if ( i1->nNum_H[j1] != i2->nNum_H[j1] && picr->num_diff_pos_H < ICR_MAX_DIFF_FIXED_H ) { - picr->diff_pos_H_at[picr->num_diff_pos_H] = j1; - picr->diff_pos_H_nH[picr->num_diff_pos_H] = i1->nNum_H[j1] - i2->nNum_H[j1]; - picr->num_diff_pos_H ++; - } - } - } - /* fixed H */ - if ( i1->nNum_H_fixed || i2->nNum_H_fixed ) { - int bHasFixedH1 = 0, bHasFixedH2 = 0, i; - if ( i1->nNum_H_fixed ) { - for ( i = 0; i < i1->nNumberOfAtoms; i ++ ) { - if ( i1->nNum_H_fixed[i] ) { - bHasFixedH1 ++; - } - } - } - if ( i2->nNum_H_fixed ) { - for ( i = 0; i < i2->nNumberOfAtoms; i ++ ) { - if ( i2->nNum_H_fixed[i] ) { - bHasFixedH2 ++; - } - } - } - if ( bHasFixedH1 && !bHasFixedH2 ) { - for ( i = j = 0; i < i1->nNumberOfAtoms; i ++ ) { - if ( i1->nNum_H_fixed[i] ) { - if ( j < ICR_MAX_DIFF_FIXED_H ) { - picr->fixed_H_at1_more[j] = i; - picr->fixed_H_nH1_more[j] = i1->nNum_H_fixed[i]; - j ++; - } - } - } - picr->num_fixed_H1_more = j; - ret |= INCHIDIFF_MORE_FH; /* Extra Fixed-H */ - } else - if ( !bHasFixedH1 && bHasFixedH2 ) { - for ( i = j = 0; i < i2->nNumberOfAtoms; i ++ ) { - if ( i2->nNum_H_fixed[i] ) { - if ( j < ICR_MAX_DIFF_FIXED_H ) { - picr->fixed_H_at2_more[j] = i; - picr->fixed_H_nH2_more[j] = i2->nNum_H_fixed[i]; - j ++; - } - } - } - picr->num_fixed_H2_more = j; - ret |= INCHIDIFF_LESS_FH; /* Missed Fixed-H */ - } else - if ( bHasFixedH1 && bHasFixedH2 && - memcmp( i1->nNum_H_fixed, i2->nNum_H_fixed, i1->nNumberOfAtoms*sizeof(i1->nNum_H_fixed[0]) ) ) { - for ( i = j1 = j2 = 0; i < i1->nNumberOfAtoms; i ++ ) { - if ( i1->nNum_H_fixed[i] > i2->nNum_H_fixed[i] ) { - if ( j1 < ICR_MAX_DIFF_FIXED_H ) { - picr->fixed_H_at1_more[j1] = i; - picr->fixed_H_nH1_more[j1] = i1->nNum_H_fixed[i] - i2->nNum_H_fixed[i]; - j1 ++; - } - } else - if ( i1->nNum_H_fixed[i] < i2->nNum_H_fixed[i] ) { - if ( j2 < ICR_MAX_DIFF_FIXED_H ) { - picr->fixed_H_at2_more[j2] = i; - picr->fixed_H_nH2_more[j2] = i2->nNum_H_fixed[i] - i1->nNum_H_fixed[i]; - j2 ++; - } - } - } - ret |= (j1? INCHIDIFF_MORE_FH:0) | (j2? INCHIDIFF_LESS_FH:0); - picr->num_fixed_H1_more = j1; - picr->num_fixed_H2_more = j2; - } - } - } - /* compare formulas and H */ - num_H1 = 0; - num_H2 = 0; - ret2 = CompareHillFormulasNoH( i1->szHillFormula, i2->szHillFormula, &num_H1, &num_H2 ); - picr->tot_num_H1 = num_H1; - picr->tot_num_H2 = num_H2; - if ( ret2 ) { - ret |= INCHIDIFF_NUM_EL; - goto exit_function; - } - if ( num_H1 > num_H2 ) { - ret |= INCHIDIFF_MORE_H; - } - if ( num_H1 < num_H2 ) { - ret |= INCHIDIFF_LESS_H; - } - - if ( i1->lenConnTable != i2->lenConnTable ) { - ret |= INCHIDIFF_CON_LEN; - goto exit_function; - } else - if ( i1->lenConnTable > 0 && memcmp( i1->nConnTable, i2->nConnTable, i1->lenConnTable*sizeof(i1->nConnTable[0]) ) ) { - ret |= INCHIDIFF_CON_TBL; - goto exit_function; - } - /* output special cases: different number of t-groups, different sizes of t-groups, different endpoints */ - /* in isotopic or deprotonated cases i1->lenTautomer == 1 && i1->nTautomer[0] = 0 */ -/* - if ( i1->lenTautomer != i2->lenTautomer && (i1->lenTautomer > 1 || i2->lenTautomer > 1) ) { - ret |= INCHIDIFF_TAUT_LEN; - } -*/ - /* compare number of t-groups */ - n1 = i1->lenTautomer? i1->nTautomer[0] : 0; - n2 = i2->lenTautomer? i2->nTautomer[0] : 0; - if ( !n1 && n2 ) { - ret |= INCHIDIFF_NO_TAUT; - } else - if ( n1 && !n2 ) { - ret |= INCHIDIFF_WRONG_TAUT; - } else - if ( n1 == 1 && n2 > 1 ) { - ret |= INCHIDIFF_SINGLE_TG; - } else - if ( n1 > 1 && n2 == 1 ) { - ret |= INCHIDIFF_MULTIPLE_TG; - } else - if ( n1 != n2 ) { - ret |= INCHIDIFF_NUM_TG; - } - if ( n1 || n2 ) { - /* number of endpoints */ - int num1 = 0, num2 = 0, num_M1=0, num_M2=0; - int len, num_eq, num_in1_only, num_in2_only; - AT_NUMB *pe1 = (AT_NUMB *) inchi_malloc( (i1->lenTautomer+1) * sizeof(pe1[0]) ); - AT_NUMB *pe2 = (AT_NUMB *) inchi_malloc( (i2->lenTautomer+1) * sizeof(pe2[0]) ); - num_H1 = num_H2=0; - /* collect endpoints, H, (-) */ - if ( !pe1 || !pe2 ) { - if ( pe1 ) inchi_free( pe1 ); - if ( pe2 ) inchi_free( pe2 ); - *err = RI_ERR_ALLOC; /* allocation error */ - goto exit_function; - } - for ( m = 1; m < i1->lenTautomer; m += len ) { - len = i1->nTautomer[m ++]; - num_H1 += i1->nTautomer[m]; - num_M1 += i1->nTautomer[m+1]; - for ( j = 2; j < len; j ++ ) { - pe1[num1 ++] = i1->nTautomer[m + j]; - } - } - for ( m = 1; m < i2->lenTautomer; m += len ) { - len = i2->nTautomer[m ++]; - num_H2 += i2->nTautomer[m]; - num_M2 += i2->nTautomer[m+1]; - for ( j = 2; j < len; j ++ ) { - pe2[num2 ++] = i2->nTautomer[m + j]; - } - } - picr->num_taut_H1 = num_H1; - picr->num_taut_H2 = num_H2; - picr->num_taut_M1 = num_M1; - picr->num_taut_M2 = num_M2; - /* sort endpoints */ - insertions_sort_AT_NUMB( pe1, num1 ); - insertions_sort_AT_NUMB( pe2, num2 ); - /* compare */ - /* - if ( num1 < num2 ) { - ret |= INCHIDIFF_LESS_TG_ENDP; - } else - if ( num1 > num2 ) { - ret |= INCHIDIFF_MORE_TG_ENDP; - } - */ - /* compare all */ - num_eq = num_in1_only = num_in2_only = 0; - for ( j1 = j2 = 0; j1 < num1 && j2 < num2; ) { - if( pe1[j1] == pe2[j2] ) { - j1 ++; - j2 ++; - num_eq ++; - } else - if ( pe1[j1] < pe2[j1] ) { - if ( picr->num_endp_in1_only < ICR_MAX_ENDP_IN1_ONLY ) { - picr->endp_in1_only[picr->num_endp_in1_only ++] = pe1[j1]; - } - j1 ++; - num_in1_only ++; - } else { - if ( picr->num_endp_in2_only < ICR_MAX_ENDP_IN2_ONLY ) { - picr->endp_in2_only[picr->num_endp_in2_only ++] = pe2[j2]; - } - j2 ++; - num_in2_only ++; - } - } - while ( j1 < num1 ) { - if ( picr->num_endp_in1_only < ICR_MAX_ENDP_IN1_ONLY ) { - picr->endp_in1_only[picr->num_endp_in1_only ++] = pe1[j1]; - } - j1 ++; - num_in1_only ++; - } - while ( j2 < num2 ) { - if ( picr->num_endp_in2_only < ICR_MAX_ENDP_IN2_ONLY ) { - picr->endp_in2_only[picr->num_endp_in2_only ++] = pe2[j2]; - } - j2 ++; - num_in2_only ++; - } - if ( num_in1_only ) { - ret |= INCHIDIFF_EXTRA_TG_ENDP; - } - if ( num_in2_only ) { - ret |= INCHIDIFF_MISS_TG_ENDP; - } - if ( !num_in1_only && !num_in2_only && num_eq ) { - ; /* same t-groups endpoints */ - } else { - ret |= INCHIDIFF_DIFF_TG_ENDP; - } - inchi_free( pe1 ); - inchi_free( pe2 ); - - } - - if ( (i1->lenTautomer > 1 && i2->lenTautomer > 1) && - ( i1->lenTautomer != i2->lenTautomer || - memcmp( i1->nTautomer, i2->nTautomer, i1->lenTautomer*sizeof(i1->nTautomer[0]) ) ) ) - ret |= INCHIDIFF_TG; - - if ( i1->nNumberOfIsotopicAtoms != i2->nNumberOfIsotopicAtoms ) { - ret |= INCHIDIFF_NUM_ISO_AT; - } else - if ( i1->nNumberOfIsotopicAtoms > 0 && memcmp( i1->IsotopicAtom, i2->IsotopicAtom, i1->nNumberOfIsotopicAtoms*sizeof(i1->IsotopicAtom[0]) ) ) - ret |= INCHIDIFF_ISO_AT; - if ( i1->nTotalCharge != i2->nTotalCharge ) - ret |= INCHIDIFF_CHARGE; - if ( a1 && a1->nNumRemovedProtons && (!a2 || a2->nNumRemovedProtons != a1->nNumRemovedProtons) ) { - ret |= INCHIDIFF_REM_PROT; - } - if ( a1 && (!a2 || - a2->nNumRemovedIsotopicH[0] != a1->nNumRemovedIsotopicH[0] || - a2->nNumRemovedIsotopicH[1] != a1->nNumRemovedIsotopicH[1] || - a2->nNumRemovedIsotopicH[2] != a1->nNumRemovedIsotopicH[2]) ) { - ret |= INCHIDIFF_REM_ISO_H; - } - -/* - if ( i1->nPossibleLocationsOfIsotopicH && i2->nPossibleLocationsOfIsotopicH ) { - if ( i1->nPossibleLocationsOfIsotopicH[0] != i2->nPossibleLocationsOfIsotopicH[0] || - memcmp(i1->nPossibleLocationsOfIsotopicH, i2->nPossibleLocationsOfIsotopicH, - sizeof(i1->nPossibleLocationsOfIsotopicH[0])*i1->nPossibleLocationsOfIsotopicH[0]) ) - return 18; - } else - if ( !i1->nPossibleLocationsOfIsotopicH != !i2->nPossibleLocationsOfIsotopicH ) { - return 19; - } -*/ - if ( i1->StereoIsotopic && - i1->StereoIsotopic->nNumberOfStereoBonds + i1->StereoIsotopic->nNumberOfStereoCenters ) { - Stereo1 = i1->StereoIsotopic; - } else { - Stereo1 = i1->Stereo; - } - if ( i2->StereoIsotopic && - i2->StereoIsotopic->nNumberOfStereoBonds + i2->StereoIsotopic->nNumberOfStereoCenters ) { - Stereo2 = i2->StereoIsotopic; - } else { - Stereo2 = i2->Stereo; - } - ret |= CompareReversedStereoINChI3( Stereo1, Stereo2, picr ); - -exit_function: - - picr->flags = ret; - - return ret; -} -/* message group names */ -CMP_INCHI_MSG_GROUP CompareInchiMsgsGroup[] = { -{IDGRP_ERR, " Error:"}, -{IDGRP_H, " Hydrogens:"}, -{IDGRP_MOB_GRP, " Mobile-H groups:"}, -{IDGRP_ISO_AT, " Isotopic:"}, -{IDGRP_CHARGE, " Charge(s):"}, -{IDGRP_PROTONS, " Proton balance:"}, -{IDGRP_ISO_H, " Exchangeable isotopic H:"}, -{IDGRP_SC, " Stereo centers/allenes:"}, -{IDGRP_SB, " Stereobonds/cumulenes:"}, -{IDGRP_HLAYER, " Fixed-H layer:"}, -{IDGRP_COMP, " Number of components:"}, -{IDGRP_CONV_ERR," Conversion encountered:"}, -{IDGRP_ZERO, ""} -}; -/* messages */ -CMP_INCHI_MSG CompareInchiMsgs[] = { -{INCHIDIFF_PROBLEM ,IDGRP_ERR, " Wrong result" }, /*0x00000001, severe: at least one InChI does not exist */ -{INCHIDIFF_POSITION_H ,IDGRP_H, " Locations or number" }, /*0x00000002, difference in non-taut {Mobile-H} or all H {Fixed-H} location/number */ -{INCHIDIFF_MORE_FH ,IDGRP_H, " Fixed-H" }, /*0x00000004, extra fixed H */ -{INCHIDIFF_LESS_FH ,IDGRP_H, " Fixed-H" }, /*0x00000004, missing fixed H */ -{INCHIDIFF_MORE_H ,IDGRP_H, " Number" }, /*0x00000008, formulas differ in number of H */ -{INCHIDIFF_LESS_H ,IDGRP_H, " Number" }, /*0x00000008, formulas differ in number of H */ -{INCHIDIFF_NO_TAUT ,IDGRP_MOB_GRP, " Missing" }, /*0x00000010, restored structure has no taut groups while the original InChI has some */ -{INCHIDIFF_WRONG_TAUT ,IDGRP_MOB_GRP, " Falsely present" }, /*0x00000020, restored has tautomerism while the original does not have it */ -{INCHIDIFF_SINGLE_TG ,IDGRP_MOB_GRP, " One instead of multiple" }, /*0x00000040, restored has 1 taut. group while the original InChI has multiple tg */ -{INCHIDIFF_MULTIPLE_TG ,IDGRP_MOB_GRP, " Multiple instead of one" }, /*0x00000080, restored has multiple tg while the original InChI has only one tg */ -{INCHIDIFF_EXTRA_TG_ENDP,IDGRP_MOB_GRP, " Attachment points" }, /*0x00000100, extra tautomeric endpoint{s} in restored structure */ -{INCHIDIFF_MISS_TG_ENDP ,IDGRP_MOB_GRP, " Attachment points" }, /*0x00000100, one or more tg endpoint is not in the restored structure */ -{INCHIDIFF_DIFF_TG_ENDP ,IDGRP_MOB_GRP, " Attachment points" }, /*0x00000100, lists of tg endpoints are different */ -{INCHIDIFF_NUM_TG ,IDGRP_MOB_GRP, " Number" }, /*0x00000200, different number of tautomeric groups */ -{INCHIDIFF_TG ,IDGRP_MOB_GRP, " Do not match" }, /*0x00000200, different tautomeric groups */ -{INCHIDIFF_NUM_ISO_AT ,IDGRP_ISO_AT, " Atoms do not match" }, /*0x00000400, ?severe: restored struct. has different number of isotopic atoms */ -{INCHIDIFF_ISO_AT ,IDGRP_ISO_AT, " Atoms do not match" }, /*0x00000400, ?severe: restored struct. has different locations/isotopes of isotopic atoms */ -{INCHIDIFF_REM_ISO_H ,IDGRP_ISO_H, " Does not match for a component" }, /*0x00000800, isotopic H removed */ -{INCHIDIFF_MOB_ISO_H ,IDGRP_ISO_H, " Do not match" }, /*0x00001000, different number of mobile exchangeable isotopic H */ -{INCHIDIFF_CHARGE ,IDGRP_CHARGE, " Do not match" }, /*0x00002000, restored structure has different charge */ -{INCHIDIFF_REM_PROT ,IDGRP_PROTONS, " Does not match for a component" }, /*0x00004000, proton{s} removed/added from the restored structure */ -{INCHIDIFF_MOBH_PROTONS ,IDGRP_PROTONS, " Does not match" }, /*0x00008000, different proton balance */ -{INCHIDIFF_SC_INV ,IDGRP_SC, " Falsely inverted" }, /*0x00010000, restores structure has different inversion stereocenter mark */ -{INCHIDIFF_SC_PARITY ,IDGRP_SC, " Wrong parity" }, /*0x00020000, restored structure has stereoatoms or allenes with different parity */ -{INCHIDIFF_SC_EXTRA_UNDF,IDGRP_SC, " Extra undefined" }, /*0x00040000, restored structure has extra undefined stereocenter{s} */ -{INCHIDIFF_SC_EXTRA ,IDGRP_SC, " Extra known" }, /*0x00080000, restored structure has extra stereocenter{s} */ -{INCHIDIFF_SC_MISS_UNDF ,IDGRP_SC, " Missing undefined" }, /*0x00100000, restored structure has not some undefined stereocenter{s} */ -{INCHIDIFF_SC_MISS ,IDGRP_SC, " Missing known" }, /*0x00200000, restored structure has not some stereocenters that are not undefined */ -{INCHIDIFF_SB_PARITY ,IDGRP_SB, " Wrong parity" }, /*0x00400000, restored structure has stereobonds or cumulenes with different parity */ -{INCHIDIFF_SB_EXTRA_UNDF,IDGRP_SB, " Extra undefined" }, /*0x00800000, restored structure has extra undefined stereobond{s} */ -{INCHIDIFF_SB_EXTRA ,IDGRP_SB, " Missing known" }, /*0x01000000, restored structure has extra stereobond{s} */ -{INCHIDIFF_SB_MISS_UNDF ,IDGRP_SB, " Missing undefined" }, /*0x02000000, restored structure has not some undefined stereocenters */ -{INCHIDIFF_SB_MISS ,IDGRP_SB, " Missing known" }, /*0x04000000, restored structure has not some stereobonds that are not undefined */ -{INCHIDIFF_COMP_HLAYER ,IDGRP_HLAYER, " Missing or extra" }, /*0x08000000, Restored component has Mobile-H layer instead of both Mobile-H & Fixed-H or both instead of one */ -{INCHIDIFF_COMP_NUMBER ,IDGRP_COMP, " Does not match" }, /*0x10000000, wrong number of components */ -{INCHIDIFF_STR2INCHI_ERR,IDGRP_CONV_ERR," Error" }, /*0x20000000 Restored structure to InChI conversion error */ -{INCHIDIFF_ZERO ,IDGRP_ZERO, "" } -}; - -/*************************************************************************/ -int AddOneMsg( char *szMsg, int used_len, int tot_len, const char *szAddMsg, const char *szDelim ) -{ - const char ellip[] = "..."; - int len = strlen( szAddMsg ); - int len_delim = (used_len && szDelim)? strlen(szDelim) : 0; - int len_to_copy; - if ( len + len_delim + used_len < tot_len ) { - if ( len_delim ) { - strcpy( szMsg+used_len, szDelim ); - used_len += len_delim; - } - strcpy( szMsg+used_len, szAddMsg ); - used_len += len; - } else - if ( (len_to_copy = (tot_len - used_len - len_delim - (int)sizeof(ellip))) > 10 ) { - if ( len_delim ) { - strcpy( szMsg+used_len, szDelim ); - used_len += len_delim; - } - strncpy( szMsg+used_len, szAddMsg, len_to_copy ); - used_len += len_to_copy; - strcpy( szMsg+used_len, ellip ); - used_len += sizeof( ellip ) - 1; - } - return used_len; -} -/*************************************************************************/ -int FillOutCompareMessage( char *szMsg, int nLenMsg, INCHI_MODE bits[] ) -{ - int bMobileH, k, n, len = strlen( szMsg ); - int iPrevGrpIdx, iCurGrpIdx, bFound; - INCHI_MODE bit; - static const char *hdr = " Problems/mismatches:"; - char szOneMsg[256]; - if ( bits[TAUT_YES] || bits[TAUT_NON] ) { - if ( !strstr( szMsg, hdr ) ) { - len = AddOneMsg( szMsg, len, nLenMsg, hdr, NULL ); - } - for ( bMobileH = TAUT_YES; 0 <= bMobileH; bMobileH -- ) { - if ( bits[bMobileH] ) { - strcpy( szOneMsg, bMobileH==TAUT_YES? " Mobile-H(" : " Fixed-H(" ); - len = AddOneMsg( szMsg, len, nLenMsg, szOneMsg, NULL ); - } - bit = 1; - iPrevGrpIdx = -1; - do { - if ( bit & bits[bMobileH] ) { - /* search for the message */ - bFound = 0; - for ( k = 0; CompareInchiMsgs[k].nBit != INCHIDIFF_ZERO && !bFound; k ++ ) { - if ( bit & (INCHI_MODE)CompareInchiMsgs[k].nBit ) { - /* message found */ - for ( n = 0; CompareInchiMsgsGroup[n].nGroupID != IDGRP_ZERO; n ++ ) { - if ( CompareInchiMsgsGroup[n].nGroupID == CompareInchiMsgs[k].nGroupID ) { - iCurGrpIdx = n; - if ( iCurGrpIdx != iPrevGrpIdx ) { - if ( iPrevGrpIdx >= 0 ) { - len = AddOneMsg( szMsg, len, nLenMsg, ";", NULL ); - } - len = AddOneMsg( szMsg, len, nLenMsg, CompareInchiMsgsGroup[iCurGrpIdx].szGroupName, NULL ); - } - len = AddOneMsg( szMsg, len, nLenMsg, CompareInchiMsgs[k].szMsg, iCurGrpIdx == iPrevGrpIdx? ",":NULL ); - iPrevGrpIdx = iCurGrpIdx; - bFound = 1; - break; - } - } - } - } - } - bit <<= 1; - } while ( bit ); - if ( bits[bMobileH] ) { - len = AddOneMsg( szMsg, len, nLenMsg, ")", NULL ); - } - } - } - return len; -} - -#endif +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include +#include + +/* */ + +/* #define CHECK_WIN32_VC_HEAP */ + +#include "mode.h" + +#if ( READ_INCHI_STRING == 1 ) + +#include "ichitime.h" + +/* reverse InChI */ + +#include "ichitime.h" +#include "ichicant.h" +#include "ichirvrs.h" +#include "inchicmp.h" + +#ifndef COMPILE_ANSI_ONLY +#ifndef TARGET_LIB_FOR_WINCHI +#include "inchi_gui.h" +#endif +#endif + + + + +/****************************************************************************/ +int InChI2Atom( INCHI_CLOCK *ic, CANON_GLOBALS *pCG, + ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, + const char *szCurHdr, long num_inp, + StrFromINChI *pStruct, + int iComponent, int iAtNoOffset, int bI2A_Flag, + int bHasSomeFixedH, InpInChI *OneInput) +{ + int ret = 0; + + int iINChI = (bI2A_Flag & I2A_FLAG_RECMET)? INCHI_REC : INCHI_BAS; + int bMobileH = (bI2A_Flag & I2A_FLAG_FIXEDH)? TAUT_NON : TAUT_YES; + + INChI *pInChI[TAUT_NUM]; + + + + memset( pInChI, 0, sizeof(pInChI) ); + + + /* disconnected or reconnected */ + + if ( iINChI == INCHI_REC ) + { + if ( !OneInput->nNumComponents[iINChI][TAUT_YES] ) + { + iINChI = INCHI_BAS; + } + } + if ( iComponent >= OneInput->nNumComponents[iINChI][TAUT_YES] ) + { + return 0; /* component does not exist */ + } + + + /* mobile or fixed H */ + pStruct->bFixedHExists = 0; + if ( bMobileH == TAUT_NON ) + { + if ( !OneInput->nNumComponents[iINChI][bMobileH] ) + { + /* only one InChI exists (no mobile H) */ + bMobileH = TAUT_YES; + } + } + if ( iComponent >= OneInput->nNumComponents[iINChI][bMobileH] ) + { + return 0; /* component does not exist */ + } + + /* pointer to the InChI that is going to be reversed */ + pInChI[0] = &OneInput->pInpInChI[iINChI][bMobileH][iComponent]; + pStruct->bMobileH = bMobileH; + pStruct->iINCHI = iINChI; + + /* deleted component only in case Mobile-H and compound contains only protons */ + if ( pInChI[0]->bDeleted ) + { + return 0; /* deleted component, presumably H(+) */ + } + + if ( bMobileH == TAUT_NON && OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons ) + { + pStruct->nNumRemovedProtonsMobHInChI = + OneInput->nNumProtons[iINChI][TAUT_YES].pNumProtons[iComponent].nNumRemovedProtons; + } + + if ( bMobileH == TAUT_NON || + bMobileH == TAUT_YES && + OneInput->pInpInChI[iINChI][TAUT_NON] && + OneInput->pInpInChI[iINChI][TAUT_NON][iComponent].nNumberOfAtoms > 0 && + !OneInput->pInpInChI[iINChI][TAUT_NON][iComponent].bDeleted + ) + { + pStruct->bFixedHExists = 1; + } + + if ( bMobileH == TAUT_NON && + iComponent < OneInput->nNumComponents[iINChI][TAUT_YES] && + OneInput->pInpInChI[iINChI][TAUT_YES] && + OneInput->pInpInChI[iINChI][TAUT_YES][iComponent].nNumberOfAtoms > 0 && + !OneInput->pInpInChI[iINChI][TAUT_YES][iComponent].bDeleted + ) + { + /* pointer to the Mobile-H InChI if we are reversing Fixed-H InChI */ + pInChI[1] = &OneInput->pInpInChI[iINChI][TAUT_YES][iComponent]; + } + + pStruct->num_inp_actual = OneInput->num_inp; + + + ret = OneInChI2Atom( ic, pCG, + ip, sd, szCurHdr, num_inp, pStruct, iComponent, + iAtNoOffset, bHasSomeFixedH, pInChI); + + + return ret; /* same interpretation as in ProcessOneStructure ??? */ +} + + +/*******************************************************************/ +void RemoveFixHInChIIdentical2MobH( InpInChI *pOneInput ) +{ + int iInchiRec, cur_num_comp, k; + + /* eliminate Fixed-H InChI that are exactly came as the corresponding Mobile-H structures */ + for ( iInchiRec = 0; iInchiRec < INCHI_NUM; iInchiRec ++ ) + { + cur_num_comp = inchi_min(pOneInput->nNumComponents[iInchiRec][TAUT_YES], + pOneInput->nNumComponents[iInchiRec][TAUT_NON]); + for ( k = 0; k < cur_num_comp; k ++ ) + { + if ( !CompareReversedINChI( pOneInput->pInpInChI[iInchiRec][TAUT_YES]+k, + pOneInput->pInpInChI[iInchiRec][TAUT_NON]+k, NULL, NULL ) ) { + Free_INChI_Members( pOneInput->pInpInChI[iInchiRec][TAUT_NON]+k ); + memset( pOneInput->pInpInChI[iInchiRec][TAUT_NON]+k, 0, sizeof(pOneInput->pInpInChI[0][0][0]) ); + } + } + } +} + + +/*******************************************************************/ +/* mark Disconnected InChI components that are exactly came as Reconnected ones */ +/* Disconnected will have a negative number of the reconnected component */ +/* Reconnected will have a positive number of the disconnected component */ +int MarkDisconectedIdenticalToReconnected ( InpInChI *pOneInput ) +{ + int k1, k2, num_marked = 0; + int k1max = inchi_max( pOneInput->nNumComponents[INCHI_BAS][TAUT_YES], + pOneInput->nNumComponents[INCHI_BAS][TAUT_NON]); + + for ( k1 = 0; k1 < k1max; k1 ++ ) + { + int k2max = inchi_max( pOneInput->nNumComponents[INCHI_REC][TAUT_YES], + pOneInput->nNumComponents[INCHI_REC][TAUT_NON]); + + for ( k2 = 0; k2 < k2max; k2 ++ ) + { + int eqM = k1 < pOneInput->nNumComponents[INCHI_BAS][TAUT_YES] + && + k2 < pOneInput->nNumComponents[INCHI_REC][TAUT_YES] + && + !pOneInput->pInpInChI[INCHI_REC][TAUT_YES][k2].nLink /* already linked */ + && + !pOneInput->pInpInChI[INCHI_BAS][TAUT_YES][k1].bDeleted + && + pOneInput->pInpInChI[INCHI_BAS][TAUT_YES][k1].nNumberOfAtoms + && + pOneInput->pInpInChI[INCHI_BAS][TAUT_YES][k1].nNumberOfAtoms== + pOneInput->pInpInChI[INCHI_REC][TAUT_YES][k2].nNumberOfAtoms + && + !pOneInput->pInpInChI[INCHI_REC][TAUT_YES][k2].bDeleted + && + !CompareReversedINChI( pOneInput->pInpInChI[INCHI_REC][TAUT_YES]+k2, + pOneInput->pInpInChI[INCHI_BAS][TAUT_YES]+k1, + NULL, NULL ) + ; + + int isF1 = k1 < pOneInput->nNumComponents[INCHI_BAS][TAUT_NON] + && + 0 == pOneInput->pInpInChI[INCHI_BAS][TAUT_NON][k1].bDeleted + && + 0 < pOneInput->pInpInChI[INCHI_BAS][TAUT_NON][k1].nNumberOfAtoms + ; + + int isF2 = k2 < pOneInput->nNumComponents[INCHI_REC][TAUT_NON] + && + 0 == pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].bDeleted + && + 0 < pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].nNumberOfAtoms + ; + + int eqF = isF1 + && + isF2 + && + !pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].nLink + && + pOneInput->pInpInChI[INCHI_BAS][TAUT_NON][k1].nNumberOfAtoms== + pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].nNumberOfAtoms + && + !CompareReversedINChI( pOneInput->pInpInChI[INCHI_REC][TAUT_NON]+k2, + pOneInput->pInpInChI[INCHI_BAS][TAUT_NON]+k1, + NULL, NULL ) + ; + + if ( eqM && ( !isF1 && !isF2 || eqF ) ) + { + pOneInput->pInpInChI[INCHI_BAS][TAUT_YES][k1].nLink = -(k2+1); + pOneInput->pInpInChI[INCHI_REC][TAUT_YES][k2].nLink = (k1+1); + if ( eqF ) + { + pOneInput->pInpInChI[INCHI_BAS][TAUT_NON][k1].nLink = -(k2+1); + pOneInput->pInpInChI[INCHI_REC][TAUT_NON][k2].nLink = (k1+1); + } + num_marked ++; + break; + /* equal InChI has been deleted from the disconnected layer, get next k1 */ + } + } + } + + return num_marked; +} + + +/**************************************************************/ +void SetUpSrm( SRM *pSrm ) +{ + /* structure restore parms !!!!! */ + memset( pSrm, 0, sizeof(pSrm[0]) ); + pSrm->bFixStereoBonds = FIX_STEREO_BOND_ORDER; + pSrm->nMetal2EndpointMinBondOrder = 1; + pSrm->nMetal2EndpointInitEdgeFlow = 0; + + if ( METAL_FREE_CHARGE_VAL == 1 ) { + pSrm->bMetalAddFlower = 1; + /* the next 3 parameters: */ + /* 0, 0, 0 => all bonds 0, no init radical on metal */ + /* 0, 0, 1 => all bonds 0, init radical on metal */ + /* 0, 1, 0 => wrong */ + /* 0, 1, 1 => all bonds 1, no init radical on metal */ + /* 1, 0, 1 => min bond order 1, all bonds to metal have order 1 */ + /* 1, 1, 0 => wrong */ + /* 1, 1, 1 => wrong */ + pSrm->nMetalMinBondOrder = 0; + pSrm->nMetalInitEdgeFlow = 1; + pSrm->nMetalInitBondOrder = 1; + pSrm->bStereoRemovesMetalFlag = pSrm->bFixStereoBonds; + pSrm->nMetalFlowerParam_D = 16; + pSrm->nMetalMaxCharge_D = 16; + } + else + { + pSrm->bMetalAddFlower = 0; + pSrm->nMetalMinBondOrder = 1; + pSrm->nMetalInitEdgeFlow = 0; + pSrm->nMetalInitBondOrder = 1; + pSrm->bStereoRemovesMetalFlag = pSrm->bFixStereoBonds; + pSrm->nMetalFlowerParam_D = 16; + pSrm->nMetalMaxCharge_D = 0; + } + pSrm->nMetal2EndpointInitBondOrder = pSrm->nMetal2EndpointMinBondOrder + + pSrm->nMetal2EndpointInitEdgeFlow; +} + + +/**************************************************************************************/ +int MergeStructureComponents( ICHICONST INPUT_PARMS *ip, + STRUCT_DATA *sd, + long num_inp, + char *szCurHdr, + ICHICONST SRM *pSrm, + int bReqNonTaut, + StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], + InpInChI *pOneInput ) +{ + int iInchiRec, iMobileH, iAlternH, num_components, tot_just_atoms, tot_removed_H, tot_atoms, cur_nA, cur_nH; + int k, i, j, ret, iCurAtomOffs, iNxtAtomOffs, iCurDelHOffs, iNxtDelHOffs, len, len2, iShiftH, icomp; + int *nAtomOffs=NULL, *nDelHOffs=NULL; + StrFromINChI *pStruct1; + inp_ATOM *at=NULL, *a; + + ret = 0; + pOneInput->num_atoms = 0; + /* select highest detail level */ + if ( num_components = pOneInput->nNumComponents[INCHI_REC][TAUT_NON] ) { + iInchiRec = INCHI_REC; + iMobileH = TAUT_NON; + } else + if ( num_components = pOneInput->nNumComponents[INCHI_REC][TAUT_YES] ) { + iInchiRec = INCHI_REC; + iMobileH = TAUT_YES; + } else + if ( num_components = pOneInput->nNumComponents[INCHI_BAS][TAUT_NON] ) { + iInchiRec = INCHI_BAS; + iMobileH = TAUT_NON; + } else + if ( num_components = pOneInput->nNumComponents[INCHI_BAS][TAUT_YES] ) { + iInchiRec = INCHI_BAS; + iMobileH = TAUT_YES; + } else { + return 0; /* no components available */ + } + + nAtomOffs = (int*) inchi_malloc((num_components+1) * sizeof(nAtomOffs[0])); + nDelHOffs = (int*) inchi_malloc((num_components+1) * sizeof(nDelHOffs[0])); + if ( !nAtomOffs || !nDelHOffs ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + /* count number of atoms and removed H */ + tot_just_atoms = tot_removed_H = tot_atoms = 0; + iAlternH = (iMobileH==TAUT_NON && pOneInput->nNumComponents[iInchiRec][TAUT_YES])? TAUT_YES : -1; + nAtomOffs[0] = nDelHOffs[0] = 0; + for ( k = 0; k < num_components; k ++ ) { + pStruct1 = pStruct[iInchiRec][iMobileH][k].num_atoms? pStruct[iInchiRec][iMobileH]+k : + iAlternH>=0 && + pStruct[iInchiRec][iAlternH][k].num_atoms? pStruct[iInchiRec][iAlternH]+k : NULL; + if ( !pStruct1 || !pStruct1->at2 || !pStruct1->num_atoms || pStruct1->bDeleted ) { + cur_nA = cur_nH = 0; + } else { + cur_nA = pStruct1->num_atoms; + cur_nH = pStruct1->num_deleted_H; + } + nAtomOffs[k+1] = nAtomOffs[k] + cur_nA; + nDelHOffs[k+1] = nDelHOffs[k] + cur_nH; + } + tot_just_atoms = nAtomOffs[num_components]; + /* shift all H to the end */ + for ( k = 0; k <= num_components; k ++ ) { + nDelHOffs[k] += tot_just_atoms; + } + tot_atoms = nDelHOffs[num_components]; + + /* merge atoms together: 1. Allocate */ + if ( NULL == (at = (inp_ATOM *) inchi_malloc( (tot_atoms+1) * sizeof(at[0]) ) ) ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + if ( !tot_atoms ) { + ret = 0; + goto exit_function; /* empty structure */ + } + /* merge atoms together: 2. Copy */ + for ( k = 0; k < num_components; k ++ ) { + pStruct1 = pStruct[iInchiRec][iMobileH][k].num_atoms? pStruct[iInchiRec][iMobileH]+k : + iAlternH>=0 && + pStruct[iInchiRec][iAlternH][k].num_atoms? pStruct[iInchiRec][iAlternH]+k : NULL; + if ( len = nAtomOffs[k+1] - nAtomOffs[k] ) { + memcpy( at + nAtomOffs[k], pStruct1->at2, len * sizeof(at[0]) ); + if ( len2 = nDelHOffs[k+1] - nDelHOffs[k] ) { + memcpy( at + nDelHOffs[k], pStruct1->at2+len, len2 * sizeof(at[0]) ); + } + } + } + /* merge atoms together: 3. Update atom numbers */ + icomp = 0; + for ( k = 0; k < num_components; k ++ ) { + iCurAtomOffs = nAtomOffs[k]; + iNxtAtomOffs = nAtomOffs[k+1]; + iCurDelHOffs = nDelHOffs[k]; + iNxtDelHOffs = nDelHOffs[k+1]; + len = nAtomOffs[k+1] - nAtomOffs[k]; /* number of atoms in a component excluding explicit H */ + iShiftH = iCurDelHOffs - len; + if ( !len ) { + continue; + } + icomp ++; /* current component number */ + /* update atoms */ + for ( i = iCurAtomOffs; i < iNxtAtomOffs; i ++ ) { + + a = at+i; + + a->endpoint = 0; + a->bAmbiguousStereo = 0; + a->at_type = 0; + a->bCutVertex = 0; + a->bUsed0DParity = 0; + a->cFlags = 0; + a->nBlockSystem = 0; + a->nNumAtInRingSystem = 0; + a->nRingSystem = 0; + + for ( j = 0; j < a->valence; j ++ ) { + if ( a->neighbor[j] < len ) { + a->neighbor[j] += iCurAtomOffs; /* atom */ + } else { + a->neighbor[j] += iShiftH; /* explicit H */ + } + } + a->orig_at_number += iCurAtomOffs; + a->component = icomp; + if ( a->p_parity ) { + for ( j = 0; j < MAX_NUM_STEREO_ATOM_NEIGH; j ++ ) { + if ( a->p_orig_at_num[j] <= len ) { + /* originally, orig_at_num = atom_index+1, therefore <= instead of < */ + a->p_orig_at_num[j] += iCurAtomOffs; + } else { + a->p_orig_at_num[j] += iShiftH; + } + } + } + for ( j = 0; j < MAX_NUM_STEREO_BONDS && a->sb_parity[j]; j ++ ) { + if ( a->sn_orig_at_num[j] <= len ) { + /* originally, orig_at_num = atom_index+1, therefore <= instead of < */ + a->sn_orig_at_num[j] += iCurAtomOffs; + } else { + a->sn_orig_at_num[j] += iShiftH; + } + } + } + /* update fixed-H */ + for ( i = iCurDelHOffs; i < iNxtDelHOffs; i ++ ) { + a = at+i; + a->neighbor[0] += iCurAtomOffs; + a->orig_at_number += iShiftH; + } + } + /* save the results */ + pOneInput->atom = at; + pOneInput->num_atoms = tot_atoms; + at = NULL; + +exit_function: + if ( at ) inchi_free( at ); /* in case of failure */ + if ( nAtomOffs ) inchi_free( nAtomOffs ); + if ( nDelHOffs ) inchi_free( nDelHOffs ); + return ret; +} + + + +#ifndef COMPILE_ANSI_ONLY + + + +static PER_DRAW_PARMS pdp; + + + +/****************************************************************************/ +int DisplayAllRestoredComponents( struct tagCANON_GLOBALS *pCG, inp_ATOM *at, int num_at, const char *szCurHdr ) +{ + int ret; + char szTitle[512]; + DRAW_PARMS dp; + TBL_DRAW_PARMS tdp; + if ( num_at <= 0 ) { + return 0; + } + memset( &dp, 0, sizeof(dp)); + memset( &tdp, 0, sizeof(tdp) ); + //memset( &pdp, 0, sizeof(pdp) ); + dp.sdp.tdp = &tdp; + dp.pdp = &pdp; + dp.sdp.nFontSize = -9; + sprintf( szTitle, "All Components of Restored %s Structure", szCurHdr? szCurHdr : "(No structure name)"); + + ret = DisplayStructure( pCG, at, num_at, 0 /* nNumDeletedH*/, 0 /*bAdd_DT_to_num_H*/, + 0 /*nNumRemovedProtons*/, NULL /*NUM_H *nNumRemovedProtonsIsotopic*/, + 1 /*int bIsotopic*/, 0 /*bTautomeric*/, + NULL /* pINChI */, NULL /* INChI_Aux **cur_INChI_Aux*/, + 0 /*bAbcNumbers*/, &dp, 0 /*INCHI_MODE nMode*/, szTitle ); + return 0; +} + + +/****************************************************************************/ +int DisplayOneRestoredComponent( struct tagCANON_GLOBALS *pCG, StrFromINChI *pStruct, inp_ATOM *at, + int iComponent, int nNumComponents, int bMobileH, + const char *szCurHdr ) +{ + int ret, k; + int num_at = pStruct->num_atoms; + XYZ_COORD *pxyz = pStruct->pXYZ; + char szTitle[512]; + DRAW_PARMS dp; + TBL_DRAW_PARMS tdp; + int iInchiRec = pStruct->iInchiRec; + int iMobileH = pStruct->iMobileH; + INChI **pInChI = NULL; + INChI_Aux **pAux = NULL; + int nNumRemovedProtons = pAux? pAux[iMobileH]->nNumRemovedProtons : 0; + NUM_H *nNumRemovedProtonsIsotopic = pAux? pAux[iMobileH]->nNumRemovedIsotopicH : NULL; + + + if ( num_at <= 0 || !pxyz ) { + return 0; + } + if ( iInchiRec && !pStruct->RevInChI.pINChI_Aux[iInchiRec][0] ) { + iInchiRec = 0; + } + k = iMobileH; + if ( !bRevInchiComponentExists( pStruct, iInchiRec, k, 0 ) ) { + k = ALT_TAUT(k); + } + pInChI = pStruct->RevInChI.pINChI[iInchiRec][0]; + pAux = pStruct->RevInChI.pINChI_Aux[iInchiRec][0]; + + + memset( &dp, 0, sizeof(dp)); + memset( &tdp, 0, sizeof(tdp) ); + //memset( &pdp, 0, sizeof(pdp) ); + dp.sdp.tdp = &tdp; + dp.pdp = &pdp; + dp.sdp.nFontSize = -9; + sprintf( szTitle, "Restored %s Component %d of %d %c%c", + szCurHdr? szCurHdr : "(No structure name)", iComponent+1, nNumComponents, + pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F' ); + ret = DisplayStructure( pCG, at, num_at, 0 /* nNumDeletedH*/, 0 /*bAdd_DT_to_num_H*/, + nNumRemovedProtons, /*NULL*/ nNumRemovedProtonsIsotopic, + 1 /*int bIsotopic*/, k, + pInChI, pAux, + 0 /*bAbcNumbers*/, &dp, 0 /*INCHI_MODE nMode*/, szTitle ); + return 0; +} + + +/****************************************************************************/ +/******************************************************************************************************/ +int DisplayRestoredComponent( struct tagCANON_GLOBALS *pCG, StrFromINChI *pStruct, int iComponent, int iAtNoOffset, INChI *pInChI, const char *szCurHdr ) +{ + int i, ret; + int num_at = pStruct->num_atoms; + int num_deleted_H = pStruct->num_deleted_H; + inp_ATOM *atom = pStruct->at2; + XYZ_COORD *pxyz = pStruct->pXYZ; + inp_ATOM *at = NULL; + char szTitle[512]; + DRAW_PARMS dp; + TBL_DRAW_PARMS tdp; + if ( !atom || num_at <= 0 || !pxyz ) { + return 0; + } + at = (inp_ATOM *)inchi_calloc( num_at + num_deleted_H, sizeof(at[0]) ); + if ( !at ) { + return RI_ERR_ALLOC; + } + memcpy( at, atom, (num_at + num_deleted_H) * sizeof(at[0]) ); + for ( i = 0; i < num_at; i ++ ) { + at[i].x = pxyz[i].xyz[0]; + at[i].y = pxyz[i].xyz[1]; + at[i].z = pxyz[i].xyz[2]; + } + memset( &dp, 0, sizeof(dp)); + memset( &tdp, 0, sizeof(tdp) ); + //memset( &pdp, 0, sizeof(pdp) ); + dp.sdp.tdp = &tdp; + dp.pdp = &pdp; + dp.sdp.nFontSize = -9; + sprintf( szTitle, "DBG Restored %s Component %d %c%c", szCurHdr? szCurHdr : "(No structure name)", iComponent+1, pStruct->iInchiRec? 'R':'D', pStruct->iMobileH?'M':'F' ); + ret = DisplayStructure( pCG, at, num_at, 0 /* nNumDeletedH*/, 0 /*bAdd_DT_to_num_H*/, + 0 /*nNumRemovedProtons*/, NULL /*NUM_H *nNumRemovedProtonsIsotopic*/, + 1 /*int bIsotopic*/, 0 /*bTautomeric*/, + &pInChI, NULL /* INChI_Aux **cur_INChI_Aux*/, + 0 /*bAbcNumbers*/, &dp, 0 /*INCHI_MODE nMode*/, szTitle ); + inchi_free( at ); + return 0; +} +/**************************************************************************************/ +int DisplayStructureComponents( struct tagCANON_GLOBALS *pCG, ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, char *szCurHdr, + ICHICONST SRM *pSrm, int bReqNonTaut, StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], + InpInChI *pOneInput ) +{ + int iInchiRec, iMobileH, iCurMobH, iAlternH, num_components, tot_just_atoms, tot_removed_H, tot_atoms, cur_nA, cur_nH; + int k, i, j, ret, iCurAtomOffs, iNxtAtomOffs, iCurDelHOffs, iNxtDelHOffs, len, len2, iShiftH, icomp; + int *nAtomOffs=NULL, *nDelHOffs=NULL, bNoCoord=0, iNewCoord=0, nNewCoord=0; + double x_max=-1.0e16, x_min = 1.0e16, y_max=-1.0e16, y_min=1.0e16, delta = 0.0; + StrFromINChI *pStruct1; + inp_ATOM *at=NULL, *a; + + if (!ip->bDisplayCompositeResults && !ip->bDisplay ) { + return 0; + } + + ret = 0; + pOneInput->num_atoms = 0; + /* select highest detail level */ + if ( num_components = pOneInput->nNumComponents[INCHI_REC][TAUT_NON] ) { + iInchiRec = INCHI_REC; + iMobileH = TAUT_NON; + } else + if ( num_components = pOneInput->nNumComponents[INCHI_REC][TAUT_YES] ) { + iInchiRec = INCHI_REC; + iMobileH = TAUT_YES; + } else + if ( num_components = pOneInput->nNumComponents[INCHI_BAS][TAUT_NON] ) { + iInchiRec = INCHI_BAS; + iMobileH = TAUT_NON; + } else + if ( num_components = pOneInput->nNumComponents[INCHI_BAS][TAUT_YES] ) { + iInchiRec = INCHI_BAS; + iMobileH = TAUT_YES; + } else { + return 0; /* no components available */ + } + for ( k = 0; k < num_components; k ++ ) { + if ( pStruct[iInchiRec][iMobileH][k].bDeleted ) + break; + } + num_components = k; + + nAtomOffs = (int*) inchi_malloc((num_components+1) * sizeof(nAtomOffs[0])); + nDelHOffs = (int*) inchi_malloc((num_components+1) * sizeof(nDelHOffs[0])); + if ( !nAtomOffs || !nDelHOffs ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + /* count number of atoms and removed H */ + tot_just_atoms = tot_removed_H = tot_atoms = 0; + iAlternH = (iMobileH==TAUT_NON && pOneInput->nNumComponents[iInchiRec][TAUT_YES])? TAUT_YES : -1; + nAtomOffs[0] = nDelHOffs[0] = 0; + for ( k = 0; k < num_components; k ++ ) { + pStruct1 = pStruct[iInchiRec][iMobileH][k].num_atoms? pStruct[iInchiRec][iMobileH]+k : + iAlternH>=0 && + pStruct[iInchiRec][iAlternH][k].num_atoms? pStruct[iInchiRec][iAlternH]+k : NULL; + if ( !pStruct1 || !pStruct1->at2 || !pStruct1->num_atoms ) { + cur_nA = cur_nH = 0; + } else { + cur_nA = pStruct1->num_atoms; + cur_nH = pStruct1->num_deleted_H; + if ( cur_nA && !pStruct1->pXYZ ) { + if ( !k ) { + ret = 0; /* no coordinates available */ + goto exit_function; + } else { + bNoCoord ++; + } + } + } + nAtomOffs[k+1] = nAtomOffs[k] + cur_nA; + nDelHOffs[k+1] = nDelHOffs[k] + cur_nH; + } + tot_just_atoms = nAtomOffs[num_components]; + /* shift all H to the end */ + for ( k = 0; k <= num_components; k ++ ) { + nDelHOffs[k] += tot_just_atoms; + } + tot_atoms = nDelHOffs[num_components]; + + /* merge atoms together: 1. Allocate */ + if ( NULL == (at = (inp_ATOM *) inchi_malloc( (tot_atoms+1) * sizeof(at[0]) ) ) ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + if ( !tot_atoms ) { + ret = 0; + goto exit_function; /* empty structure */ + } + /* merge atoms together: 2. Copy */ + for ( k = 0; k < num_components; k ++ ) { + pStruct1 = pStruct[iInchiRec][iMobileH][k].num_atoms? pStruct[iInchiRec][iCurMobH=iMobileH]+k : + iAlternH>=0 && + pStruct[iInchiRec][iAlternH][k].num_atoms? pStruct[iInchiRec][iCurMobH=iAlternH]+k : NULL; + if ( len = nAtomOffs[k+1] - nAtomOffs[k] ) { + XYZ_COORD *pxyz = pStruct1->pXYZ; + len2 = nDelHOffs[k+1] - nDelHOffs[k]; /* do not separate H from the atom: we will not need them */ + iCurAtomOffs = nAtomOffs[k]; + a = at + iCurAtomOffs; + memcpy( a, pStruct1->at2, (len+len2) * sizeof(at[0]) ); + DisconnectedConnectedH( a, len, len2 ); + if ( pxyz ) { + for ( i = 0; i < len; i ++ ) { + a[i].x = pxyz[i].xyz[0]; + x_max = inchi_max( x_max, pxyz[i].xyz[0] ); + x_min = inchi_min( x_min, pxyz[i].xyz[0] ); + a[i].y = pxyz[i].xyz[1]; + y_max = inchi_max( y_max, pxyz[i].xyz[1] ); + y_min = inchi_min( y_min, pxyz[i].xyz[1] ); + a[i].z = pxyz[i].xyz[2]; + nNewCoord ++; + } + } else { + if ( !iNewCoord ) { + if ( !nNewCoord ) { + ret = 0; + goto exit_function; /* empty structure */ + } + delta = inchi_max(x_max - x_min, y_max - y_min); + if ( delta == 0.0 ) { + delta = 0.5 * (x_max+x_min); + if ( delta == 0.0 ) + delta = 1.0; + } else { + delta /= sqrt( (double)(nNewCoord+1) ); + } + } + for ( i = 0; i < len; i ++ ) { + a[i].x = x_max + delta; + a[i].y = y_max - iNewCoord * delta; + a[i].z = 0.0; + iNewCoord ++; + } + if ( pStruct1->pXYZ = (XYZ_COORD *)inchi_calloc(len, sizeof(pStruct1->pXYZ[0]) ) ) { + + for ( i = 0; i < len; i ++ ) { + pStruct1->pXYZ[i].xyz[0] = a[i].x; + pStruct1->pXYZ[i].xyz[1] = a[i].y; + pStruct1->pXYZ[i].xyz[2] = 0.0; + } + } + } + if ( ip->bDisplay || ip->bDisplayCompositeResults && 1 == num_components ) { + DisplayOneRestoredComponent( pCG, pStruct1, a, k, num_components, iCurMobH, szCurHdr ); + } + if ( !pxyz && pStruct1->pXYZ ) { + inchi_free( pStruct1->pXYZ ); + pStruct1->pXYZ = NULL; + } + } + } + /* merge atoms together: 3. Update atom numbers */ + icomp = 0; + if ( ip->bDisplayCompositeResults && num_components > 1 ) { + for ( k = 0; k < num_components; k ++ ) { + /* display each restored component if requested */ + iCurAtomOffs = nAtomOffs[k]; + iNxtAtomOffs = nAtomOffs[k+1]; + iCurDelHOffs = nDelHOffs[k]; + iNxtDelHOffs = nDelHOffs[k+1]; + len = nAtomOffs[k+1] - nAtomOffs[k]; /* number of atoms in a component excluding explicit H */ + iShiftH = iCurDelHOffs - len; + if ( !len ) { + continue; + } + icomp ++; /* current component number */ + /* update atoms */ + for ( i = iCurAtomOffs; i < iNxtAtomOffs; i ++ ) { + a = at+i; + for ( j = 0; j < a->valence; j ++ ) { + if ( a->neighbor[j] < len ) { + a->neighbor[j] += iCurAtomOffs; /* atom */ + } else { + ret = RI_ERR_PROGR; /* explicit H */ + goto exit_function; + } + } + a->orig_at_number += iCurAtomOffs; + } + } + tot_atoms = nAtomOffs[num_components]; + DisplayAllRestoredComponents( pCG, at, tot_atoms, szCurHdr ); + } + +exit_function: + if ( at ) inchi_free( at ); /* in case of failure */ + if ( nAtomOffs ) inchi_free( nAtomOffs ); + if ( nDelHOffs ) inchi_free( nDelHOffs ); + return ret; +} +#endif + + + +/**************************************************************************************/ +int AllInchiToStructure( INCHI_CLOCK *ic, CANON_GLOBALS *pCG, + ICHICONST INPUT_PARMS *ip_inp, + STRUCT_DATA *sd_inp, + long num_inp, char *szCurHdr, + ICHICONST SRM *pSrm, int bHasSomeFixedH, + StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], + InpInChI *pOneInput ) +{ + int iInchiRec, iMobileH, cur_num_comp, bCurI2A_Flag, k, ret, num_err; + INPUT_PARMS *ip, ip_loc; + STRUCT_DATA *sd, sd_loc; + long ulProcessingTime = 0; + inchiTime ulTStart; + + InchiTimeGet( &ulTStart ); + ip = &ip_loc; + *ip = *ip_inp; + sd = &sd_loc; + memset( sd, 0, sizeof(*sd)); + sd->ulStructTime = sd_inp->ulStructTime; + ret = 0; + num_err = 0; + for ( iInchiRec = 0; iInchiRec < INCHI_NUM; iInchiRec ++ ) { /* Disconnected/Connected */ + for ( iMobileH = 0; iMobileH < TAUT_NUM; iMobileH ++ ) { /* Mobile/Fixed H */ + cur_num_comp = pOneInput->nNumComponents[iInchiRec][iMobileH]; + if ( !cur_num_comp ) { + continue; + } + /* allocate memory for all existing components */ + pStruct[iInchiRec][iMobileH] = (StrFromINChI *)inchi_calloc( cur_num_comp, sizeof(pStruct[0][0][0])); + if ( !pStruct[iInchiRec][iMobileH] ) { + ret = RI_ERR_ALLOC; + goto exit_error; + } + /* set conversion mode */ + bCurI2A_Flag = (iMobileH? 0: I2A_FLAG_FIXEDH) | (iInchiRec? I2A_FLAG_RECMET : 0); + if ( iMobileH ) { + ip->nMode &= ~REQ_MODE_BASIC; + } else { + ip->nMode |= REQ_MODE_BASIC; + } + /* InChI --> structure conversion for all components except duplicated */ + for ( k = 0; k < cur_num_comp; k ++ ) { /* components */ + if ( !iMobileH && !pOneInput->pInpInChI[iInchiRec][iMobileH][k].nNumberOfAtoms || + pOneInput->pInpInChI[iInchiRec][iMobileH][k].bDeleted || + pOneInput->pInpInChI[iInchiRec][iMobileH][k].nLink < 0 ) { + + pStruct[iInchiRec][iMobileH][k].nLink = pOneInput->pInpInChI[iInchiRec][iMobileH][k].nLink; + pStruct[iInchiRec][iMobileH][k].bDeleted = pOneInput->pInpInChI[iInchiRec][iMobileH][k].bDeleted; + continue; /* do not create a structure out of an unavailable + Fixed-H InChI or out of the one present in Reconnected layer */ +#ifdef NEVER /* a wrong attempt to process deleted components here */ + if ( pStruct[iInchiRec][iMobileH][k].nLink = pOneInput->pInpInChI[iInchiRec][iMobileH][k].nLink ) { + continue; /* do not create a structure out of an unavailable + Fixed-H InChI or out of the one present in Reconnected layer */ + } else + if ( iMobileH && pOneInput->pInpInChI[iInchiRec][iMobileH][k].nNumberOfAtoms && + pOneInput->pInpInChI[iInchiRec][iMobileH][k].bDeleted && + pOneInput->pInpInChI[iInchiRec][iMobileH][0].bDeleted ) { + /* all components are protons */ + ; + } else { + continue; + } +#endif + } + if ( bHasSomeFixedH && iMobileH && k < pOneInput->nNumComponents[iInchiRec][TAUT_NON] && + pOneInput->pInpInChI[iInchiRec][TAUT_NON][k].nNumberOfAtoms ) { + continue; /* do not process Mobile-H if Fixed-H is requested and exists */ + } + pStruct[iInchiRec][iMobileH][k].pSrm = pSrm; + pStruct[iInchiRec][iMobileH][k].iInchiRec = iInchiRec; + pStruct[iInchiRec][iMobileH][k].iMobileH = iMobileH; + + /****************************************************/ + /* */ + /* Convert InChI of one component into a Structure */ + /* */ + /****************************************************/ + + ret = InChI2Atom( ic, pCG, ip, sd, szCurHdr, num_inp, pStruct[iInchiRec][iMobileH]+k, k, + 0 /* AtNoOffset*/, bCurI2A_Flag, bHasSomeFixedH, pOneInput ); + pStruct[iInchiRec][iMobileH][k].nLink = pOneInput->pInpInChI[iInchiRec][iMobileH][k].nLink; + if ( ret < 0 ) { +#if ( bRELEASE_VERSION != 1 ) +#ifndef TARGET_API_LIB + /* !!! Conversion Error -- Ignore for now !!! */ + fprintf( stdout, "%ld %s Conversion failed: %d, %c%c comp %d\n", + num_inp, szCurHdr? szCurHdr : "Struct", ret, iInchiRec? 'R':'D', iMobileH? 'M':'F', k+1); +#endif +#endif + if ( ret == CT_USER_QUIT_ERR ) { + goto exit_error; + } + pStruct[iInchiRec][iMobileH][k].nError = ret; + ret = 0; /* force to ignore the errors for now !!!! */ + num_err ++; + } + } + } + } +exit_error: + ulProcessingTime += InchiTimeElapsed( ic, &ulTStart ); + sd->ulStructTime += ulProcessingTime; + return ret<0? ret : num_err; +} + + +/**************************************************************************************/ +int AddProtonAndIsoHBalanceToMobHStruct( INCHI_CLOCK *ic, CANON_GLOBALS *pCG, + ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, + long num_inp, int bHasSomeFixedH, + char *szCurHdr, + StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], + InpInChI *pOneInput) +{ + COMPONENT_REM_PROTONS nToBeRemovedByNormFromRevrs[INCHI_NUM]; + int nRemovedByNormFromRevrs[INCHI_NUM]; + int nRemovedByRevrs[INCHI_NUM]; + + int nDeltaFromDisconnected = 0, nRemovedProtonsByNormFromRevrs, nRemovedProtonsByRevrs, num_changes = 0; + NUM_H nIsoDeltaFromDisconnected[NUM_H_ISOTOPES]; + int iInchiRec, i, k, k1, ret = 0; + int nChargeInChI, nChargeRevrs; + + if ( bHasSomeFixedH ) { + return 0; /* 2005-03-01 */ + } + + /* num protons removed by InChI Normalization from the original structure */ + for ( i = 0; i < INCHI_NUM; i ++ ) { + nToBeRemovedByNormFromRevrs[i].nNumRemovedProtons = pOneInput->nNumProtons[i][TAUT_YES].nNumRemovedProtons; + for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) { + nToBeRemovedByNormFromRevrs[i].nNumRemovedIsotopicH[k] = pOneInput->nNumProtons[i][TAUT_YES].nNumRemovedIsotopicH[k]; + } + } + /* accumulate here num. protons removed by the normalization from the reversed structure */ + nRemovedByNormFromRevrs[INCHI_BAS] = + nRemovedByNormFromRevrs[INCHI_REC] = 0; + nRemovedByRevrs[INCHI_REC] = + nRemovedByRevrs[INCHI_BAS] = 0; + /* protons added/removed by InChI Normalization to/from Restored Structure might have been added by StructureRestore */ + for ( iInchiRec = 0; iInchiRec < INCHI_NUM; iInchiRec ++ ) { + for ( k = 0; k < pOneInput->nNumComponents[iInchiRec][TAUT_YES]; k ++ ) { + if ( !bInpInchiComponentExists( pOneInput, iInchiRec, TAUT_YES, k ) ) { + continue; + } + nRemovedProtonsByNormFromRevrs = 0; /* Num protons removed from the Restored Structure by InChI Normalization */ + nRemovedProtonsByRevrs = 0; /* Num protons removed by the Reconstruction from the Restored Structure */ + if ( iInchiRec == INCHI_REC || iInchiRec == INCHI_BAS && (k1=pStruct[iInchiRec][TAUT_YES][k].nLink) >= 0 ) { + + REV_INCHI *pRevInChI = &pStruct[iInchiRec][TAUT_YES][k].RevInChI; + INChI_Aux **pINChI_Aux2 = pRevInChI->pINChI_Aux[iInchiRec][0]; /* component 0*/ + INChI **pINChI_Revr = pRevInChI->pINChI[iInchiRec][0]; + INChI *pINChI_Orig = pOneInput->pInpInChI[iInchiRec][TAUT_YES]+k; + nChargeRevrs = pINChI_Revr? pINChI_Revr[TAUT_YES]->nTotalCharge : NO_VALUE_INT; + nChargeInChI = pINChI_Orig->nTotalCharge; + if ( pINChI_Aux2 ) { + nRemovedProtonsByNormFromRevrs = pINChI_Aux2[TAUT_YES]->nNumRemovedProtons; + } + nRemovedProtonsByRevrs = pStruct[iInchiRec][TAUT_YES][k].nNumRemovedProtonsByRevrs; + pStruct[iInchiRec][TAUT_YES][k].nChargeRevrs = nChargeRevrs; + pStruct[iInchiRec][TAUT_YES][k].nChargeInChI = nChargeInChI; + } else + if ( 0 <= ( k1 = -(1+pStruct[iInchiRec][TAUT_YES][k].nLink) ) ) { + REV_INCHI *pRevInChI = &pStruct[INCHI_REC][TAUT_YES][k1].RevInChI; + INChI_Aux **pINChI_Aux2 = pRevInChI->pINChI_Aux[INCHI_BAS][0]; /* component 0 */ + INChI **pINChI_Revr = pRevInChI->pINChI[INCHI_BAS][0]; + INChI *pINChI_Orig = pOneInput->pInpInChI[INCHI_REC][TAUT_YES]+k1; + nChargeRevrs = pINChI_Revr? pINChI_Revr[TAUT_YES]->nTotalCharge : NO_VALUE_INT; + nChargeInChI = pINChI_Orig->nTotalCharge; + if ( pINChI_Aux2 ) { + nRemovedProtonsByNormFromRevrs = pINChI_Aux2[TAUT_YES]->nNumRemovedProtons; + } + /* this component cannot be disconnected because it is same as in reconnected layer */ + nRemovedProtonsByRevrs = pStruct[INCHI_REC][TAUT_YES][k1].nNumRemovedProtonsByRevrs; + pStruct[iInchiRec][TAUT_YES][k1].nChargeRevrs = nChargeRevrs; + pStruct[iInchiRec][TAUT_YES][k1].nChargeInChI = nChargeInChI; + } + /* how many protons (to be removed by InChI Normalization) to add = + (proton balance in InChI} - + {number of protons known to be removed by InChI Normalization from Reconstructed structure} */ + nToBeRemovedByNormFromRevrs[iInchiRec].nNumRemovedProtons -= nRemovedProtonsByNormFromRevrs; + nRemovedByNormFromRevrs[iInchiRec] += nRemovedProtonsByNormFromRevrs; + nRemovedByRevrs[iInchiRec] += nRemovedProtonsByRevrs; + pStruct[iInchiRec][TAUT_YES][k].nRemovedProtonsByNormFromRevrs = nRemovedProtonsByNormFromRevrs; + } + } + + /* Since fixed-H layer is missing we need to add proton balance to the components */ + memset( nIsoDeltaFromDisconnected, 0, sizeof(nIsoDeltaFromDisconnected) ); + for ( iInchiRec = INCHI_REC; INCHI_BAS <= iInchiRec; iInchiRec -- ) { + /* + if ( !pOneInput->nNumComponents[iInchiRec][TAUT_NON] && + pOneInput->nNumComponents[iInchiRec][TAUT_YES] ) { + */ + int bHasRecMobH = (iInchiRec==INCHI_BAS && pOneInput->nNumComponents[INCHI_REC][TAUT_YES]); + /* bHasRecMobH means all components that could not be disconnected are in reconnected part */ + if ( iInchiRec==INCHI_BAS ) { + /* second pass: common structures have been changed */ + nToBeRemovedByNormFromRevrs[INCHI_BAS].nNumRemovedProtons += nDeltaFromDisconnected; + } + /* after proton removal InChI is recalculated */ + + ret = AddRemProtonsInRestrStruct( ic, pCG, ip, sd, num_inp, bHasSomeFixedH, pStruct[iInchiRec][TAUT_YES], + pOneInput->nNumComponents[iInchiRec][TAUT_YES], + bHasRecMobH? pStruct[INCHI_REC][TAUT_YES] : NULL, + bHasRecMobH? pOneInput->nNumComponents[INCHI_REC][TAUT_YES]:0, + &nToBeRemovedByNormFromRevrs[iInchiRec].nNumRemovedProtons, + (iInchiRec==INCHI_REC)?&nDeltaFromDisconnected : NULL); + if ( ret < 0 ) { + goto exit_function; + } + num_changes += ret; + /* + } + */ + } + /* if fixed-H layer is missing then we need to add isotopic exchangeable proton balance to the components */ + for ( iInchiRec = INCHI_REC; INCHI_BAS <= iInchiRec; iInchiRec -- ) { + /* + if ( !pOneInput->nNumComponents[iInchiRec][TAUT_NON] && + pOneInput->nNumComponents[iInchiRec][TAUT_YES] ) { + */ + int bHasRecMobH = (iInchiRec==INCHI_BAS && pOneInput->nNumComponents[INCHI_REC][TAUT_YES]); + /* bHasRecMobH means all components that could not be disconnected are in reconnected part */ + if ( iInchiRec==INCHI_BAS ) { + /* second pass: common structures have been changed */ + for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) { + nToBeRemovedByNormFromRevrs[INCHI_BAS].nNumRemovedIsotopicH[k] += nIsoDeltaFromDisconnected[k]; + } + } + /* after proton removal InChI is recalculated */ + ret = AddRemIsoProtonsInRestrStruct( ic, pCG, ip, sd, num_inp, bHasSomeFixedH, pStruct[iInchiRec][TAUT_YES], + pOneInput->nNumComponents[iInchiRec][TAUT_YES], + bHasRecMobH? pStruct[INCHI_REC][TAUT_YES] : NULL, + bHasRecMobH? pOneInput->nNumComponents[INCHI_REC][TAUT_YES]:0, + nToBeRemovedByNormFromRevrs[iInchiRec].nNumRemovedIsotopicH, + (iInchiRec==INCHI_REC)?nIsoDeltaFromDisconnected : NULL); + + if ( ret < 0 ) { + goto exit_function; + } + num_changes += ret; + /* + } + */ + } + +exit_function: + return ret; +} + + +/*************************************************************/ +void FreeStrFromINChI( StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], + int nNumComponents[INCHI_NUM][TAUT_NUM] ) +{ + int iInchiRec, iMobileH, cur_num_comp, k, j; + StrFromINChI *pStruct1; + for ( iInchiRec = 0; iInchiRec < INCHI_NUM; iInchiRec ++ ) { + for ( iMobileH = 0; iMobileH < TAUT_NUM; iMobileH ++ ) { + cur_num_comp = nNumComponents[iInchiRec][iMobileH]; + if ( !cur_num_comp || !(pStruct1=pStruct[iInchiRec][iMobileH]) ) { + continue; + } + for ( k = 0; k < cur_num_comp; k ++ ) { + if ( pStruct1[k].at ) { + inchi_free(pStruct1[k].at); + } + if ( pStruct1[k].at2 ) { + inchi_free(pStruct1[k].at2); + } + if ( pStruct1[k].st ) { + inchi_free(pStruct1[k].st); + } + if ( pStruct1[k].pVA ) { + inchi_free(pStruct1[k].pVA); + } + /* + if ( pStruct1[k].ti.t_group ) { + inchi_free( pStruct1[k].ti.t_group ); + } + */ + if ( pStruct1[k].pXYZ ) { + inchi_free(pStruct1[k].pXYZ); + } + /*==== begin ====*/ + free_t_group_info( &pStruct1[k].ti ); + if ( pStruct1[k].endpoint ) { + inchi_free(pStruct1[k].endpoint); + } + if ( pStruct1[k].fixed_H ) { + inchi_free(pStruct1[k].fixed_H); + } + for ( j = 0; j < TAUT_NUM; j ++ ) { + if ( pStruct1[k].nAtno2Canon[j] ) + inchi_free( pStruct1[k].nAtno2Canon[j] ); + if ( pStruct1[k].nCanon2Atno[j] ) + inchi_free( pStruct1[k].nCanon2Atno[j] ); + } + /*===== end ======*/ + /* free INChI memory */ + FreeAllINChIArrays( pStruct1[k].RevInChI.pINChI, + pStruct1[k].RevInChI.pINChI_Aux, + pStruct1[k].RevInChI.num_components ); +#ifdef NEVER + /* don't do that: these are just pointers to OneInput structure members */ + Free_INChI( &pStruct1[k].pINChI ); + Free_INChI_Aux( &pStruct1[k].pINChI_Aux ); + if ( pStruct1[k].inp_norm_data ) { + FreeInpAtomData( pStruct1[k].inp_norm_data ); + inchi_free( pStruct1[k].inp_norm_data ); + } +#endif + } + inchi_free(pStruct[iInchiRec][iMobileH]); + pStruct[iInchiRec][iMobileH] = NULL; + } + } +} + + +/********************************************************************/ +void FreeInpInChI( InpInChI *pOneInput ) +{ + int iINChI, k, j; + for ( iINChI = 0; iINChI < INCHI_NUM; iINChI ++ ) { + for ( j = 0; j < TAUT_NUM; j ++ ) { + if ( pOneInput->pInpInChI[iINChI][j] ) { + for ( k = 0; k < pOneInput->nNumComponents[iINChI][j]; k ++ ) { + Free_INChI_Members( &pOneInput->pInpInChI[iINChI][j][k] ); + } + inchi_free(pOneInput->pInpInChI[iINChI][j]); + pOneInput->pInpInChI[iINChI][j] = NULL; + } + if ( pOneInput->nNumProtons[iINChI][j].pNumProtons ) { + inchi_free( pOneInput->nNumProtons[iINChI][j].pNumProtons ); + pOneInput->nNumProtons[iINChI][j].pNumProtons = NULL; + } + } + } + if ( pOneInput->atom ) inchi_free(pOneInput->atom); + + FreeExtOrigAtData ( pOneInput->polymer, pOneInput->v3000 ); + + memset( pOneInput, 0, sizeof(*pOneInput) ); +} + + +/***********************************************************************************************/ +int CompareAllOrigInchiToRevInChI( StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], + InpInChI *pOneInput, + int bReqNonTaut, + long num_inp, + char *szCurHdr) +{ + int i, iInchiRec, iMobileH, iMobileHpStruct, num_components, iComponent, ret=0; + COMPONENT_REM_PROTONS nCurRemovedProtons, nNumRemovedProtons; + INChI *pInChI[TAUT_NUM]; + INCHI_MODE CompareInchiFlags[TAUT_NUM]; + memset( pOneInput->CompareInchiFlags[0], 0, sizeof(pOneInput->CompareInchiFlags[0]) ); + memset( &nNumRemovedProtons, 0, sizeof(nNumRemovedProtons) ); + + /* do we have reconnected InChI ?*/ + iInchiRec = INCHI_REC; + iMobileH = TAUT_NON; + if ( !pOneInput->nNumComponents[iInchiRec][TAUT_YES] && !pOneInput->nNumComponents[iInchiRec][TAUT_NON] ) { + iInchiRec = INCHI_BAS; + } + /* do we have Mobile or Fixed-H ? */ + if ( !pOneInput->nNumComponents[iInchiRec][TAUT_NON] || !bReqNonTaut ) { + iMobileH = TAUT_YES; /* index for pOneInput */ + } + /* if a restored structure has Fixed-H InChI then its mobile-H restored InChI is in Fixed-H pStruct */ + num_components = pOneInput->nNumComponents[iInchiRec][iMobileH]; + for ( iComponent = 0; iComponent < num_components; iComponent ++ ) { + int bMobileH = iMobileH; + pInChI[0] = pInChI[1] = NULL; + if ( pOneInput->pInpInChI[iInchiRec][bMobileH][iComponent].nNumberOfAtoms && + !pOneInput->pInpInChI[iInchiRec][bMobileH][iComponent].bDeleted ) { + /* the requested InChI layer exists */ + pInChI[0] = &pOneInput->pInpInChI[iInchiRec][bMobileH][iComponent]; + if ( bMobileH == TAUT_NON ) { + pInChI[1] = &pOneInput->pInpInChI[iInchiRec][TAUT_YES][iComponent]; + } + } else + if ( bMobileH == TAUT_NON && + pOneInput->pInpInChI[iInchiRec][TAUT_YES][iComponent].nNumberOfAtoms && + !pOneInput->pInpInChI[iInchiRec][TAUT_YES][iComponent].bDeleted ) { + /* the requested Fixed-H InChI layer does not exist; however, the Mobile-H does exist */ + bMobileH = TAUT_YES; /* only Mobile-H is available */ + pInChI[0] = &pOneInput->pInpInChI[iInchiRec][bMobileH][iComponent]; + } + memset( CompareInchiFlags, 0, sizeof(CompareInchiFlags) ); + memset( &nCurRemovedProtons, 0, sizeof(nCurRemovedProtons) ); + iMobileHpStruct = +#if ( bRELEASE_VERSION == 0 ) +#ifndef TARGET_API_LIB + /* legacy: reproduce old output */ + OldPrintCompareOneOrigInchiToRevInChI(pStruct[iInchiRec][bMobileH]+iComponent, pInChI, bMobileH, + iComponent, num_inp, szCurHdr); +#endif +#endif + /* one component comparison result bits */ + ret = CompareOneOrigInchiToRevInChI( pStruct[iInchiRec][bMobileH]+iComponent, pInChI, bMobileH, iComponent, + num_inp, szCurHdr, &nCurRemovedProtons, CompareInchiFlags); + if ( ret >= 0 ) { + /* no errors encountered -> accumulate removed protons from individual Mobile-H layers of components */ + nNumRemovedProtons.nNumRemovedProtons += nCurRemovedProtons.nNumRemovedProtons; + for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { + nNumRemovedProtons.nNumRemovedIsotopicH[i] += nCurRemovedProtons.nNumRemovedIsotopicH[i]; + } + /* accumulate compare bits */ + for ( i = 0; i < TAUT_NUM; i ++ ) { + pOneInput->CompareInchiFlags[0][i] |= CompareInchiFlags[i]; + } + } else { + goto exit_function; + } + } + if ( iMobileH == TAUT_YES ) { + if ( pOneInput->nNumProtons[iInchiRec][iMobileH].pNumProtons ) { + ret = RI_ERR_PROGR; /* in Mobile-H case proton balances are split between compoments */ + } else { + /* num removed protons in orig. InChI num removed protons in restored InChi */ + if ( nNumRemovedProtons.nNumRemovedProtons != pOneInput->nNumProtons[iInchiRec][iMobileH].nNumRemovedProtons ) { + /* restored structure InChI has less or more removed protons */ + pOneInput->CompareInchiFlags[0][TAUT_YES] |= INCHIDIFF_MOBH_PROTONS; +#if ( bRELEASE_VERSION == 0 ) + /* debug output only */ + { + int num_H_AddedByRevrs = pOneInput->nNumProtons[iInchiRec][iMobileH].nNumRemovedProtons + - nNumRemovedProtons.nNumRemovedProtons; + fprintf( stdout, "COMPARE_INCHI: %ld: %s %cM: Proton balance (Diff: %d, RevrsRem=%d)\n", + num_inp, szCurHdr? szCurHdr : "Struct", iInchiRec? 'R':'D', + pOneInput->nNumProtons[iInchiRec][iMobileH].nNumRemovedProtons,num_H_AddedByRevrs); + } +#endif + } + for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { + if ( nNumRemovedProtons.nNumRemovedIsotopicH[i] != pOneInput->nNumProtons[iInchiRec][TAUT_YES].nNumRemovedIsotopicH[i] ) { + pOneInput->CompareInchiFlags[0][TAUT_YES] |= INCHIDIFF_MOB_ISO_H; +#if ( bRELEASE_VERSION == 0 ) + /* debug output only */ + { + int num_H_AddedByRevrs = pOneInput->nNumProtons[iInchiRec][TAUT_YES].nNumRemovedIsotopicH[i] + - nNumRemovedProtons.nNumRemovedIsotopicH[i]; + fprintf( stdout, "COMPARE_INCHI: %ld: %s %cM: Iso Xchg %dH balance (Diff: %d, RevrsRem=%d)\n", + num_inp, szCurHdr? szCurHdr : "Struct", iInchiRec? 'R':'D', i+1, + pOneInput->nNumProtons[iInchiRec][TAUT_YES].nNumRemovedIsotopicH[i],num_H_AddedByRevrs); + } +#endif + } + } + } + } + +exit_function: + return ret; +} + + +/***********************************************************************************************/ +int CompareAllDisconnectedOrigInchiToRevInChI( StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], + InpInChI *pOneInput, + int bHasSomeFixedH, + long num_inp, + char *szCurHdr) +{ + int i, k, m, n, iInChI, iMobileH, bMobileH, ifk; + int num_components_D, num_components_R; + int nNumCompHaveSeparateProtons_D, nNumCompHaveSeparateProtons_R; + int num_fragments_D, num_fragments_R, num_fragments_DR, num_fragments, iComponent, ret; + int ifInChI, ifMobileH, bfMobileH, nLink; + COMPONENT_REM_PROTONS nNumRemovedProtons_D; /* removed from the disconnected layer of the Input InChI */ + COMPONENT_REM_PROTONS nNumRemovedProtons_D_all; /* if only totals are avalable */ + COMPONENT_REM_PROTONS nNumRemovedProtons_R; /* removed from disconnected layer of the reconstructed struct */ + COMPONENT_REM_PROTONS nNumRemovedProtons_R_all; + INCHI_MODE CompareInchiFlags[TAUT_NUM]; + StrFromINChI *pStruct1; + INChI_Aux *pINChI_Aux; + INCHI_SORT *pINChISort1 = NULL; /* from reversed structure */ + INCHI_SORT *pINChISort2 = NULL; /* original input InChI */ + int nNumNonTaut1=0, nNumNonTaut2=0; + + ret = 0; + memset( pOneInput->CompareInchiFlags[1], 0, sizeof(pOneInput->CompareInchiFlags[1]) ); + + /* count components that are not subject to disconnection */ + if ( !pOneInput->nNumComponents[INCHI_REC][TAUT_YES] && + !pOneInput->nNumComponents[INCHI_REC][TAUT_NON] ) { + return 0; /* nothing to do */ + } + + memset( &nNumRemovedProtons_D, 0, sizeof(nNumRemovedProtons_D) ); + memset( &nNumRemovedProtons_R, 0, sizeof(nNumRemovedProtons_R) ); + memset( &nNumRemovedProtons_D_all, 0, sizeof(nNumRemovedProtons_D_all) ); + memset( &nNumRemovedProtons_R_all, 0, sizeof(nNumRemovedProtons_R_all) ); + memset( CompareInchiFlags, 0, sizeof(CompareInchiFlags) ); + + num_components_D = inchi_max( pOneInput->nNumComponents[INCHI_BAS][TAUT_YES], + pOneInput->nNumComponents[INCHI_BAS][TAUT_NON] ); + num_components_R = inchi_max( pOneInput->nNumComponents[INCHI_REC][TAUT_YES], + pOneInput->nNumComponents[INCHI_REC][TAUT_NON] ); + /***********************************************************************************************/ + /* InpInChI: count fragments -- disconnected components that do not match reconnected */ + /* Accumulate removed H and isotopic H from ALL Fixed-H disconnected components except deleted */ + /* This segment collects info from the original InChI */ + /***********************************************************************************************/ + /*---- Original InChI ----*/ + num_fragments_D = 0; + iInChI = INCHI_BAS; + iMobileH = bHasSomeFixedH? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES; + nNumCompHaveSeparateProtons_D = 0; + + /* in case of Mobile-H components here are the proton totals from the original InChI disconn. layer */ + nNumRemovedProtons_D.nNumRemovedProtons = pOneInput->nNumProtons[iInChI][TAUT_YES].nNumRemovedProtons; + memcpy( nNumRemovedProtons_D.nNumRemovedIsotopicH, + pOneInput->nNumProtons[iInChI][TAUT_YES].nNumRemovedIsotopicH, + sizeof(nNumRemovedProtons_D.nNumRemovedIsotopicH) ); /* total for the disconnected layer */ + + for ( k = 0; k < num_components_D; k ++ ) { + bMobileH = iMobileH; + if ( !bInpInchiComponentExists( pOneInput, iInChI, bMobileH, k ) ) { + if ( bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) ) { + bMobileH = TAUT_YES; + } else { + continue; /* component is missing ??? */ + } + } + if ( 0 > (nLink = pOneInput->pInpInChI[iInChI][bMobileH][k].nLink) ) { + /* component in Disconnected layer is linked to the identical one in the Reconnected layer */ + if ( pOneInput->nNumProtons[INCHI_REC][TAUT_YES].pNumProtons ) { + nNumCompHaveSeparateProtons_D ++; + nLink = -(1+nLink); + nNumRemovedProtons_D.nNumRemovedProtons += pOneInput->nNumProtons[INCHI_REC][TAUT_YES].pNumProtons[nLink].nNumRemovedProtons; + for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { + nNumRemovedProtons_D.nNumRemovedIsotopicH[m] += pOneInput->nNumProtons[INCHI_REC][TAUT_YES].pNumProtons[nLink].nNumRemovedIsotopicH[m]; + } + } + continue; /* same as reconnected */ + } + /* component in the reconnected layer that was disconnected */ + nNumNonTaut2 += (bMobileH == TAUT_NON); + if ( pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons ) { + nNumCompHaveSeparateProtons_D ++; + nNumRemovedProtons_D.nNumRemovedProtons += pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons[k].nNumRemovedProtons; + for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { + nNumRemovedProtons_D.nNumRemovedIsotopicH[m] += pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH[m]; + } + } + num_fragments_D ++; /* number of disconnected fragments from original reconnected structure */ + } + /* in case of Mobile-H components here are the proton totals from the original InChI */ + /* + nNumRemovedProtons_D_all.nNumRemovedProtons = pOneInput->nNumProtons[iInChI][TAUT_YES].nNumRemovedProtons; + memcpy( nNumRemovedProtons_D_all.nNumRemovedIsotopicH, + pOneInput->nNumProtons[iInChI][TAUT_YES].nNumRemovedIsotopicH, + sizeof(nNumRemovedProtons_D_all.nNumRemovedIsotopicH) ); + + */ + /****************************************************************************************************/ + /* count fragments in reconstructed reconnected structure */ + /* accumulate removed H and isotopic H from ALL reconstructed reconnected components except deleted */ + /* This segment collects info from the reconstructed structure InChI */ + /****************************************************************************************************/ + /*---- InChI from the reconstructed reconnected structure ----*/ + num_fragments_R = 0; + iInChI = INCHI_REC; + iMobileH = bHasSomeFixedH? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES; + nNumCompHaveSeparateProtons_R = 0; + for ( k = 0; k < num_components_R; k ++ ) { + bMobileH = iMobileH; + if ( !bInpInchiComponentExists( pOneInput, iInChI, bMobileH, k ) ) { + if ( bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) ) { + bMobileH = TAUT_YES; + } else { + continue; /* component is missing ??? (Deleted proton in Mobile-H layer) */ + } + } + if ( 0 < pOneInput->pInpInChI[iInChI][bMobileH][k].nLink ) { + /* this reconstructed reconnected component was NOT DISCONNECTED */ + /* same component is in the disconnected layer, it has no metal atoms or is an isolated metal atom */ + pStruct1 = pStruct[iInChI][bMobileH]+k; + ifMobileH = TAUT_YES; /* Mobile-H Aux_Info contains number removed protons */ + ifInChI = INCHI_BAS; /* this component cannot be reconnected */ + ifk = 0; /* 0th component since it is InChI of a single component */ + /* The statement in the following line is *WRONG*, component number mixed with bMobileH: */ + /* in RevInchi, when only Mobile-H is present then its only non-NULL InChI has index 0==TAUT_NON */ + if ( bRevInchiComponentExists( pStruct1, ifInChI, ifMobileH, ifk ) ) { + /* count protons */ + pINChI_Aux = pStruct1->RevInChI.pINChI_Aux[ifInChI][ifk][ifMobileH]; + if ( pINChI_Aux ) { + nNumRemovedProtons_R.nNumRemovedProtons += pINChI_Aux->nNumRemovedProtons; + for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { + nNumRemovedProtons_R.nNumRemovedIsotopicH[m] += pINChI_Aux->nNumRemovedIsotopicH[m]; + } + } + } + nNumCompHaveSeparateProtons_R += bRevInchiComponentExists( pStruct1, ifInChI, ALT_TAUT(ifMobileH), ifk ); + continue; /* same as disconnected, has no metal atoms */ + } + /* this reconstructed reconnected component WAS DISCONNECTED; check its fragments */ + /* it does not have same component in the disconnected layer */ + pStruct1 = pStruct[iInChI][bMobileH]+k; + num_fragments = pStruct1->RevInChI.num_components[INCHI_BAS]; + ifInChI = INCHI_BAS; /* disconnected layer */ + ifMobileH = bHasSomeFixedH? TAUT_NON : TAUT_YES; + for ( ifk = 0; ifk < num_fragments; ifk ++ ) { + bfMobileH = ifMobileH; + if ( !bRevInchiComponentExists( pStruct1, ifInChI, bfMobileH, ifk ) ) { + if ( bRevInchiComponentExists( pStruct1, ifInChI, TAUT_YES, ifk ) ) { + bfMobileH = TAUT_YES; + } else { + continue; /* fragment does not exist ??? */ + } + } + nNumNonTaut1 += (bfMobileH == TAUT_NON); + nNumCompHaveSeparateProtons_R += (bfMobileH == TAUT_NON); + /* count protons from fragments made by metal disconnection */ + pINChI_Aux = pStruct1->RevInChI.pINChI_Aux[ifInChI][ifk][TAUT_YES]; + if ( pINChI_Aux ) { + nNumRemovedProtons_R.nNumRemovedProtons += pINChI_Aux->nNumRemovedProtons; + for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { + nNumRemovedProtons_R.nNumRemovedIsotopicH[m] += pINChI_Aux->nNumRemovedIsotopicH[m]; + } + } + num_fragments_R ++; /* number of disconnected fragments from reconstructed reconnected structure */ + } + } + /*---------------- special treatment of the last reconstructed component -----------------*/ + /*---------------- this may contain separate protons added by the reconstruction ---------*/ + k = num_components_R - 1; + pStruct1 = pStruct[iInChI][iMobileH]+k; + if ( iMobileH == TAUT_YES && !bHasSomeFixedH && + bInpInchiComponentDeleted( pOneInput, iInChI, iMobileH, k ) && + (num_fragments = pStruct1->RevInChI.num_components[INCHI_BAS]) ) { + + ifInChI = INCHI_BAS; /* disconnected layer */ + ifMobileH = TAUT_YES; + for ( ifk = 0; ifk < num_fragments; ifk ++ ) { + bfMobileH = ifMobileH; + if ( !bRevInchiComponentDeleted( pStruct1, ifInChI, bfMobileH, ifk ) ) { + continue; /* fragment does exist ??? Should not happen */ + } + /* + nNumNonTaut1 += (bfMobileH == TAUT_NON); + nNumCompHaveSeparateProtons_R += (bfMobileH == TAUT_NON); + */ + /* count protons from fragments made by metal disconnection */ + pINChI_Aux = pStruct1->RevInChI.pINChI_Aux[ifInChI][ifk][TAUT_YES]; + if ( pINChI_Aux ) { + nNumRemovedProtons_R.nNumRemovedProtons += pINChI_Aux->nNumRemovedProtons; + for ( m = 0; m < NUM_H_ISOTOPES; m ++ ) { + nNumRemovedProtons_R.nNumRemovedIsotopicH[m] += pINChI_Aux->nNumRemovedIsotopicH[m]; + } + } + /*num_fragments_R ++;*/ /* number of disconnected fragments from reconstructed reconnected structure */ + } + } + + + + num_fragments_DR = inchi_max( num_fragments_D, num_fragments_R ); + /* in case of correct reconstruction, num_fragments_D, num_fragments_R */ + + if ( !num_fragments_DR ) { + return 0; /* no component was disconnected */ + } + if ( num_fragments_D != num_fragments_R ) { + for ( i = 0; i < TAUT_NUM; i ++ ) { + if ( pOneInput->nNumComponents[INCHI_BAS][i] ) { + pOneInput->CompareInchiFlags[1][i] |= INCHIDIFF_PROBLEM; + } + } + return 1; /* severe error */ + } + + + pINChISort1 = (INCHI_SORT *)inchi_calloc(num_fragments_DR, sizeof(pINChISort1[0])); + pINChISort2 = (INCHI_SORT *)inchi_calloc(num_fragments_DR, sizeof(pINChISort2[0])); + if ( !pINChISort1 || !pINChISort2 ) { + ret = RI_ERR_ALLOC; + goto exit_function; + } + + /* accumulate original InChI of fragments -- disconnected components that do not match reconnected */ + iInChI = INCHI_BAS; + iMobileH = bHasSomeFixedH? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES; + for ( k = n = 0; k < num_components_D; k ++ ) { + bMobileH = iMobileH; + if ( !bInpInchiComponentExists( pOneInput, iInChI, bMobileH, k ) ) { + if ( bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) ) { + bMobileH = TAUT_YES; + } else { + continue; /* component is missing ??? (Deleted proton in Mobile-H layer) */ + } + } + if ( 0 > pOneInput->pInpInChI[iInChI][bMobileH][k].nLink ) { + continue; /* same as reconnected */ + } + /* the component exists in disconnected layer of the orig. InChI only: it is a fragment */ + pINChISort2[n].pINChI[bMobileH] = pOneInput->pInpInChI[iInChI][bMobileH] + k; + if ( bMobileH == TAUT_NON && + (bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) || + bInpInchiComponentDeleted( pOneInput, iInChI, TAUT_YES, k ) ) ) { + pINChISort2[n].pINChI[TAUT_YES] = pOneInput->pInpInChI[iInChI][TAUT_YES] + k; + } + /* the last sort key is a number of removed protons */ + pINChISort2[n].ord_number = pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons? + pOneInput->nNumProtons[iInChI][TAUT_YES].pNumProtons[k].nNumRemovedProtons : 0; + pINChISort2[n].n1 = k; /* orig. InChI disconnected layer component number */ + pINChISort2[n].n2 = -1; /* no fragment index */ + n ++; + } + + /* accumulate fragments from the reconstructed structure */ + iInChI = INCHI_REC; + iMobileH = bHasSomeFixedH? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES; + for ( k = n = 0; k < num_components_R; k ++ ) { + bMobileH = iMobileH; + if ( !bInpInchiComponentExists( pOneInput, iInChI, bMobileH, k ) ) { + if ( bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) ) { + bMobileH = TAUT_YES; + } else { + continue; /* component is missing ??? (Deleted proton in Mobile-H layer) */ + } + } + /* the reconstructed structure */ + if ( 0 < pOneInput->pInpInChI[iInChI][bMobileH][k].nLink ) { + continue; /* same as disconnected, has no metal atoms */ + } + /* this reconstructed structure was disconnected */ + pStruct1 = pStruct[iInChI][bMobileH]+k; + num_fragments = pStruct1->RevInChI.num_components[INCHI_BAS]; + ifInChI = INCHI_BAS; + ifMobileH = bHasSomeFixedH? TAUT_NON : TAUT_YES; + for ( i = 0; i < num_fragments; i ++ ) { + bfMobileH = ifMobileH; + if ( !bRevInchiComponentExists( pStruct1, ifInChI, bfMobileH, i ) ) { + if ( bRevInchiComponentExists( pStruct1, ifInChI, TAUT_YES, i ) ) { + bfMobileH = TAUT_YES; + } else { + continue; /* component is missing ??? */ + } + } + pINChISort1[n].pINChI[bfMobileH] = pStruct1->RevInChI.pINChI[ifInChI][i][bfMobileH]; + if ( bfMobileH == TAUT_NON /*&& bRevInchiComponentExists( pStruct1, ifInChI, TAUT_YES, i )*/ ) { + pINChISort1[n].pINChI[TAUT_YES] = pStruct1->RevInChI.pINChI[ifInChI][i][TAUT_YES]; + /* remove Fixed-H InChI if is is identical to Mobile-H */ + /* do it exactly same way the identical components were removed from InpInChI */ + if ( !CompareReversedINChI( pINChISort1[n].pINChI[bfMobileH], + pINChISort1[n].pINChI[TAUT_YES], NULL, NULL ) ) { + pINChISort1[n].pINChI[bfMobileH] = NULL; /* remove Fixed-H layer */ + } else { + pINChISort1[n].ord_number = pStruct1->RevInChI.pINChI_Aux[ifInChI][i][TAUT_YES]->nNumRemovedProtons; + } + } + + pINChISort1[n].n1 = k; /* reconstructed reconnected structure component index */ + pINChISort1[n].n2 = i; /* index of a fragment made out of this component */ + n ++; + } + } + + /* sort fragment InChI before comparing them */ + qsort( pINChISort1, num_fragments_D, sizeof(pINChISort1[0]), CompINChITaut2 ); + qsort( pINChISort2, num_fragments_R, sizeof(pINChISort2[0]), CompINChITaut2 ); + + /* compare fragments -- components present in disconnected layer only */ + for ( iComponent = 0; iComponent < num_fragments_DR; iComponent ++ ) { + INChI *pInChI1[TAUT_NUM]; /* from reversed structure */ + INChI *pInChI2[TAUT_NUM]; /* original input InChI */ + for ( i = 0; i < TAUT_NUM; i ++ ) { + pInChI1[i] = pINChISort1[iComponent].pINChI[i]; + pInChI2[i] = pINChISort2[iComponent].pINChI[i]; + } + CompareTwoPairsOfInChI( pInChI1, pInChI2, !bHasSomeFixedH, CompareInchiFlags ); + } + + if ( /*nNumNonTaut1 && nNumNonTaut2 &&*/ bHasSomeFixedH ) { + if ( nNumCompHaveSeparateProtons_D || nNumCompHaveSeparateProtons_R ) { + /* for each component, compare number removed protons */ + /* comparison does not make sense if Disconnected Fixed-H layer is not present */ + for ( iComponent = 0; iComponent < num_fragments_DR; iComponent ++ ) { + NUM_H nNumRemovedIsotopicH1[NUM_H_ISOTOPES]; + NUM_H nNumRemovedIsotopicH2[NUM_H_ISOTOPES]; + + memset( nNumRemovedIsotopicH1, 0, sizeof(nNumRemovedIsotopicH1) ); + memset( nNumRemovedIsotopicH2, 0, sizeof(nNumRemovedIsotopicH2) ); + /* compare removed protons */ + if ( pINChISort1[iComponent].ord_number != pINChISort2[iComponent].ord_number ) { + CompareInchiFlags[TAUT_YES] |= INCHIDIFF_MOBH_PROTONS; /* diff number of removed protons */ + } + /* also compare removed isotopic atoms H */ + k = pINChISort2[iComponent].n1; /* input InChI, OneInput */ + if ( pOneInput->nNumProtons[INCHI_BAS][TAUT_YES].pNumProtons ) { + memcpy( nNumRemovedIsotopicH2, + pOneInput->nNumProtons[INCHI_BAS][TAUT_YES].pNumProtons[k].nNumRemovedIsotopicH, + sizeof( nNumRemovedIsotopicH2 ) ); + } + /* get fragments of reconstructed structure removed protons info */ + k = pINChISort1[iComponent].n1; /* restored component number */ + i = pINChISort1[iComponent].n2; /* subcomponent number */ + iInChI = INCHI_REC; + iMobileH = bHasSomeFixedH? !pOneInput->nNumComponents[iInChI][TAUT_NON] : TAUT_YES; + bMobileH = iMobileH; + if ( !bInpInchiComponentExists( pOneInput, iInChI, bMobileH, k ) ) { + if ( bInpInchiComponentExists( pOneInput, iInChI, TAUT_YES, k ) ) { + bMobileH = TAUT_YES; + } else { + goto compare_iso_H; + } + } + if ( pOneInput->pInpInChI[iInChI][bMobileH][k].nLink ) { + continue; + /* + ret = RI_ERR_PROGR; + goto exit_function; + */ + } + pStruct1 = pStruct[iInChI][bMobileH]+k; + num_fragments = pStruct1->RevInChI.num_components[INCHI_BAS]; + ifInChI = INCHI_BAS; + ifMobileH = bHasSomeFixedH? TAUT_NON : TAUT_YES; + if ( i < num_fragments ) { + bfMobileH = ifMobileH; + if ( !bRevInchiComponentExists( pStruct1, ifInChI, bfMobileH, i ) ) { + if ( bRevInchiComponentExists( pStruct1, ifInChI, TAUT_YES, i ) ) { + bfMobileH = TAUT_YES; + } else { + goto compare_iso_H; + } + } + memcpy( nNumRemovedIsotopicH1, + pStruct1->RevInChI.pINChI_Aux[ifInChI][i][TAUT_YES]->nNumRemovedIsotopicH, + sizeof( nNumRemovedIsotopicH1 ) ); + } +compare_iso_H: + if ( memcmp( nNumRemovedIsotopicH1, nNumRemovedIsotopicH2, sizeof( nNumRemovedIsotopicH1 ) ) ) { + CompareInchiFlags[TAUT_YES] |= INCHIDIFF_REM_ISO_H; + } + } + } + } else + /*if ( !nNumNonTaut1 && !nNumNonTaut2 || !bHasSomeFixedH )*/ { + /* compare totals for removed protons and isotopic H */ + if ( pOneInput->nNumProtons[INCHI_BAS][TAUT_YES].nNumRemovedProtons != + nNumRemovedProtons_R.nNumRemovedProtons ) { + CompareInchiFlags[TAUT_YES] |= INCHIDIFF_MOBH_PROTONS; + } + if ( memcmp( pOneInput->nNumProtons[INCHI_BAS][TAUT_YES].nNumRemovedIsotopicH, + nNumRemovedProtons_R.nNumRemovedIsotopicH, + sizeof( nNumRemovedProtons_R.nNumRemovedIsotopicH ) ) ) { + CompareInchiFlags[TAUT_YES] |= INCHIDIFF_REM_ISO_H; + } + } + + if ( !nNumNonTaut1 == !nNumNonTaut2 ) { + ; /* difference if(nNumNonTaut1 != nNumNonTaut2) will be caught in InChI comparison */ + } else + if ( nNumNonTaut1 ) { + /* reconstructed has Fixed-H while the original has not: extra Fixed-H layer */ + CompareInchiFlags[TAUT_YES] |= INCHIDIFF_WRONG_TAUT; + } else { + /* the original InChI has Fixed-H while the reconstructed one has not: missing Fixed-H layer */ + CompareInchiFlags[TAUT_YES] |= INCHIDIFF_NO_TAUT; + } + for ( i = 0; i < TAUT_NUM; i ++ ) { + pOneInput->CompareInchiFlags[1][i] |= CompareInchiFlags[i]; + } + + /* compare totals */ + if ( nNumRemovedProtons_R.nNumRemovedProtons != nNumRemovedProtons_D.nNumRemovedProtons ) { + CompareInchiFlags[TAUT_YES] |= INCHIDIFF_MOBH_PROTONS; /* diff number of removed protons */ + } + if ( memcmp( nNumRemovedProtons_R.nNumRemovedIsotopicH, + nNumRemovedProtons_D.nNumRemovedIsotopicH, + sizeof( nNumRemovedProtons_D.nNumRemovedIsotopicH ) ) ) { + CompareInchiFlags[TAUT_YES] |= INCHIDIFF_REM_ISO_H; + } + +exit_function: + + if ( pINChISort1 ) inchi_free( pINChISort1 ); + if ( pINChISort2 ) inchi_free( pINChISort2 ); + + return ret; +} + + +/****************************************************************************/ +int CompareTwoPairsOfInChI( INChI *pInChI1[TAUT_NUM], + INChI *pInChI2[TAUT_NUM], + int bMobileH, + INCHI_MODE CompareInchiFlags[] ) +{ + int iMobileH, err=0; + INCHI_MODE cmp; + for ( iMobileH = 0; iMobileH < TAUT_NUM; iMobileH ++ ) { + if ( !pInChI1[iMobileH] != !pInChI2[iMobileH] ) { + if ( iMobileH == TAUT_NON && + pInChI1[TAUT_YES] && pInChI1[TAUT_YES] ) { + CompareInchiFlags[iMobileH] |= INCHIDIFF_COMP_HLAYER; + } else { + CompareInchiFlags[iMobileH] |= INCHIDIFF_COMP_NUMBER; + } + continue; + } + if ( pInChI1[iMobileH] && pInChI2[iMobileH] ) { + cmp = CompareReversedINChI3( pInChI1[iMobileH], pInChI2[iMobileH], NULL, NULL, &err ); + if ( cmp ) { + CompareInchiFlags[iMobileH] |= cmp; + } + } + } + return err; +} + + +/****************************************************************************/ +int CompareOneOrigInchiToRevInChI( StrFromINChI *pStruct, + INChI *pInChI[TAUT_NUM], + int bMobileH, + int iComponent, + long num_inp, + char *szCurHdr, + COMPONENT_REM_PROTONS *nCurRemovedProtons, + INCHI_MODE CompareInchiFlags[]) +{ + int ret = pStruct->RevInChI.nRetVal, err=0; + INCHI_MODE cmp; + if ( ret == _IS_OKAY || ret == _IS_WARNING ) { + /* ignore bMobileH for now */ + int i, i0, b /* created type */, b0 /* requested type*/, j, k; + /* pINChI[iINCHI][iComponent][bTaut] */ + /* i0 = requested Rec/Disconnected: 1/0 */ + /* i = what InChI creaded out of the restored structure */ + /* b0 = requested Mobile/Fixed-H: 1/0 */ + /* b = what InChI creaded out of the restored structure */ + i = i0 = pStruct->iINCHI; + b = b0 = pStruct->iMobileH; + if ( i == INCHI_REC && !pStruct->RevInChI.num_components[i] ) { + i = INCHI_BAS; + } + if ( b == TAUT_NON && (!pStruct->RevInChI.pINChI[i] || + !pStruct->RevInChI.pINChI[i][0][b] || + !pStruct->RevInChI.pINChI[i][0][b]->nNumberOfAtoms ) ) { + b = TAUT_YES; + } + if ( pStruct->bDeleted && (!pInChI[0] || pInChI[0]->bDeleted ) ) { + return 0; + } + + if ( pStruct->RevInChI.num_components[i] > 1 && + !pStruct->RevInChI.pINChI[i][1][b]->bDeleted || + pStruct->RevInChI.num_components[i] < 1 ) { + CompareInchiFlags[bMobileH] |= INCHIDIFF_COMP_NUMBER; + } + if ( b != b0 || b != bMobileH || b0 != bMobileH || i > i0 ) { + /* do not print messages about TAUT_YES instead of TAUT_NON */ + CompareInchiFlags[bMobileH] |= INCHIDIFF_COMP_HLAYER; + } + + if ( pStruct->RevInChI.num_components[i] ) { + /* compare InChI from restored structure; '0' in [i][0][b] is the first component */ + if ( b == TAUT_YES && pStruct->RevInChI.pINChI[i][0][b]->bDeleted && (!pInChI[0] || pInChI[0]->bDeleted ) ) { + /* the 1st component is made out of proton(s) and the input component is missing or also a proton */ + cmp = 0; + } else { + cmp = CompareReversedINChI3( pStruct->RevInChI.pINChI[i][0][b], pInChI[0], NULL, NULL, &err ); + if ( cmp ) { + CompareInchiFlags[bMobileH] |= cmp; + } + } + if ( b == b0 && b == TAUT_NON ) { + if ( pStruct->RevInChI.pINChI[i][0][TAUT_YES] && + !pStruct->RevInChI.pINChI[i][0][TAUT_YES]->bDeleted || + pInChI[1] && !pInChI[1]->bDeleted ) { + + /* in addition to fixed-H also compare mobile-H InChI */ + cmp = CompareReversedINChI3( pStruct->RevInChI.pINChI[i][0][TAUT_YES], pInChI[1], NULL, NULL, &err ); + if ( cmp ) { + CompareInchiFlags[TAUT_YES] |= cmp; + } + } + /* compare removed H */ + if ( pStruct->nNumRemovedProtonsMobHInChI != pStruct->RevInChI.pINChI_Aux[i][0][TAUT_YES]->nNumRemovedProtons ) { + CompareInchiFlags[TAUT_YES] |= INCHIDIFF_MOBH_PROTONS; + } + } + memset( nCurRemovedProtons, 0, sizeof(*nCurRemovedProtons) ); + for ( k = 0; k < pStruct->RevInChI.num_components[i]; k ++ ) { + if ( !k || pStruct->RevInChI.pINChI[i][k][TAUT_YES]->bDeleted ) { + /* get removed protons from the 1st component; add othere only if they are deleted protons */ + nCurRemovedProtons->nNumRemovedProtons += pStruct->RevInChI.pINChI_Aux[i][k][TAUT_YES]->nNumRemovedProtons; + for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { + nCurRemovedProtons->nNumRemovedIsotopicH[j] += pStruct->RevInChI.pINChI_Aux[i][k][TAUT_YES]->nNumRemovedIsotopicH[j]; + } + } + } + } + } else { + CompareInchiFlags[bMobileH] |= INCHIDIFF_STR2INCHI_ERR; + } + return err; +} + + +/*************************************************************************************/ +INCHI_MODE CompareReversedStereoINChI3( INChI_Stereo *s1 /* InChI from reversed struct */, + INChI_Stereo *s2 /* input InChI */, + ICR *picr) +{ + int ret = 0; + int j1, j2, num_eq, num_dif, num_extra_undf, num_miss_undf, num_in1_only, num_in2_only; + int bAddSb = !(picr->num_sb_undef_in1_only + picr->num_sb_in1_only + picr->num_sb_in2_only); + int bAddSc = !(picr->num_sc_undef_in1_only + picr->num_sc_in1_only + picr->num_sc_in2_only); + + int nNumSc1 = s1? s1->nNumberOfStereoCenters : 0; + int nNumSc2 = s2? s2->nNumberOfStereoCenters : 0; + int nNumSb1 = s1? s1->nNumberOfStereoBonds : 0; + int nNumSb2 = s2? s2->nNumberOfStereoBonds : 0; + + if ( (nNumSc1 || nNumSc1) && + ( nNumSc1 != nNumSc2 || + memcmp( s1->nNumber, s2->nNumber, nNumSc1*sizeof(s1->nNumber[0] ) ) || + memcmp( s1->t_parity, s2->t_parity, nNumSc1*sizeof(s1->t_parity[0]) ) ) ) { + + num_eq = num_dif = num_extra_undf = num_miss_undf = num_in1_only = num_in2_only = 0; + for ( j1 = j2 = 0; j1 < nNumSc1 && j2 < nNumSc2; ) { + if ( s1->nNumber[j1] == s2->nNumber[j2] ) { + if ( s1->t_parity[j1] == s2->t_parity[j2] ) { + num_eq ++; + } else { + num_dif ++; + } + j1 ++; + j2 ++; + } else + if ( s1->nNumber[j1] < s2->nNumber[j2] ) { + num_in1_only ++; + if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { + num_extra_undf ++; + } + if ( bAddSc ) { + if ( picr->num_sc_in1_only < ICR_MAX_SC_IN1_ONLY ) + picr->sc_in1_only[picr->num_sc_in1_only ++] = j1; + if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { + if ( picr->num_sc_undef_in1_only < ICR_MAX_SC_UNDF ) + picr->sc_undef_in1_only[picr->num_sc_undef_in1_only ++] = j1; + } + } + j1 ++; + } else { + num_in2_only ++; + if ( s2->t_parity[j2] == AB_PARITY_UNDF ) { + num_miss_undf ++; + } + if ( bAddSc ) { + if ( picr->num_sc_in2_only < ICR_MAX_SC_IN2_ONLY ) + picr->sc_in2_only[picr->num_sc_in2_only ++] = j2; + if ( s2->t_parity[j2] == AB_PARITY_UNDF ) { + if ( picr->num_sc_undef_in2_only < ICR_MAX_SC_UNDF ) + picr->sc_undef_in2_only[picr->num_sc_undef_in2_only ++] = j1; + } + } + j2 ++; + } + } + while ( j1 < nNumSc1 ) { + if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { + num_extra_undf ++; + } + num_in1_only ++; + if ( bAddSc ) { + if ( picr->num_sc_in1_only < ICR_MAX_SC_IN1_ONLY ) + picr->sc_in1_only[picr->num_sc_in1_only ++] = j1; + if ( s1->t_parity[j1] == AB_PARITY_UNDF ) { + if ( picr->num_sc_undef_in1_only < ICR_MAX_SC_UNDF ) + picr->sc_undef_in1_only[picr->num_sc_undef_in1_only ++] = j1; + } + } + j1 ++; + } + while ( j2 < nNumSc2 ) { + if ( s2->t_parity[j2] == AB_PARITY_UNDF ) { + num_miss_undf ++; + } + num_in2_only ++; + if ( bAddSc ) { + if ( picr->num_sc_in2_only < ICR_MAX_SC_IN2_ONLY ) + picr->sc_in2_only[picr->num_sc_in2_only ++] = j2; + } + j2 ++; + } + if ( num_dif ) { + ret |= INCHIDIFF_SC_PARITY; + } + if ( num_in1_only ) { + if ( num_extra_undf ) { + ret |= INCHIDIFF_SC_EXTRA_UNDF; + } + if ( num_in1_only != num_extra_undf ) { + ret |= INCHIDIFF_SC_EXTRA; + } + } + if ( num_in2_only ) { + if ( num_miss_undf ) { + ret |= INCHIDIFF_SC_MISS_UNDF; + } + if ( num_in2_only != num_miss_undf ) { + ret |= INCHIDIFF_SC_MISS; + } + } + } + if ( s1 && s2 && (s2->nCompInv2Abs != 2) && s1->nCompInv2Abs != s2->nCompInv2Abs && s1->nCompInv2Abs && s2->nCompInv2Abs ) { + ret |= INCHIDIFF_SC_INV; /* 2007-07-13 DT: added (s2->nCompInv2Abs != 2) to fix bug reoprted by Yerin on 2007/02/28 */ + /* Bug description: falsely reported "Stereo centers/allenes: Falsely inverted" for /S2 or /S3 */ + } + + if ( (nNumSb1 || nNumSb2 ) && + (nNumSb1 != nNumSb2 || + memcmp( s1->nBondAtom1, s2->nBondAtom1, nNumSb1*sizeof(s1->nBondAtom1[0]) ) || + memcmp( s1->nBondAtom2, s2->nBondAtom2, nNumSb1*sizeof(s1->nBondAtom2[0]) ) || + memcmp( s1->b_parity, s2->b_parity, nNumSb1*sizeof(s1->b_parity[0]) ) ) ) { + + num_eq = num_dif = num_extra_undf = num_miss_undf = num_in1_only = num_in2_only = 0; + for ( j1 = j2 = 0; j1 < nNumSb1 && j2 < nNumSb2; ) { + if ( s1->nBondAtom1[j1] == s2->nBondAtom1[j2] && + s1->nBondAtom2[j1] == s2->nBondAtom2[j2] ) { + if ( s1->b_parity[j1] == s2->b_parity[j2] ) { + num_eq ++; + } else { + num_dif ++; + } + j1 ++; + j2 ++; + } else + if ( s1->nBondAtom1[j1] < s2->nBondAtom1[j2] || + s1->nBondAtom1[j1] == s2->nBondAtom1[j2] && s1->nBondAtom2[j1] < s2->nBondAtom2[j2]) { + num_in1_only ++; + if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { + num_extra_undf ++; + } + if ( bAddSb ) { + if ( picr->num_sb_in1_only < ICR_MAX_SB_IN1_ONLY ) + picr->sb_in1_only[picr->num_sb_in1_only ++] = j1; + if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { + if ( picr->num_sb_undef_in1_only < ICR_MAX_SB_UNDF ) + picr->sb_undef_in1_only[picr->num_sb_undef_in1_only ++] = j1; + } + } + j1 ++; + } else { + num_in2_only ++; + if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { + num_miss_undf ++; + } + if ( bAddSb ) { + if ( picr->num_sb_in2_only < ICR_MAX_SB_IN2_ONLY ) + picr->sb_in2_only[picr->num_sb_in2_only ++] = j2; + if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { + if ( picr->num_sb_undef_in2_only < ICR_MAX_SB_UNDF ) + picr->sb_undef_in2_only[picr->num_sb_undef_in2_only ++] = j1; + } + } + j2 ++; + } + } + while ( j1 < nNumSb1 ) { + num_in1_only ++; + if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { + num_extra_undf ++; + } + if ( bAddSb ) { + if ( picr->num_sb_in1_only < ICR_MAX_SB_IN1_ONLY ) + picr->sb_in1_only[picr->num_sb_in1_only ++] = j1; + if ( s1->b_parity[j1] == AB_PARITY_UNDF ) { + if ( picr->num_sb_undef_in1_only < ICR_MAX_SB_UNDF ) + picr->sb_undef_in1_only[picr->num_sb_undef_in1_only ++] = j1; + } + } + j1 ++; + } + while ( j2 < nNumSb2 ) { + num_in2_only ++; + if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { + num_miss_undf ++; + } + if ( bAddSb ) { + if ( picr->num_sb_in2_only < ICR_MAX_SB_IN2_ONLY ) + picr->sb_in2_only[picr->num_sb_in2_only ++] = j2; + if ( s2->b_parity[j2] == AB_PARITY_UNDF ) { + if ( picr->num_sb_undef_in2_only < ICR_MAX_SB_UNDF ) + picr->sb_undef_in2_only[picr->num_sb_undef_in2_only ++] = j1; + } + } + j2 ++; + } + if ( num_dif ) { + ret |= INCHIDIFF_SB_PARITY; + } + if ( num_in1_only ) { + if ( num_extra_undf ) { + ret |= INCHIDIFF_SB_EXTRA_UNDF; + } + if ( num_in1_only != num_extra_undf ) { + ret |= INCHIDIFF_SB_EXTRA; + } + } + if ( num_in2_only ) { + if ( num_miss_undf ) { + ret |= INCHIDIFF_SB_MISS_UNDF; + } + if ( num_in2_only != num_miss_undf ) { + ret |= INCHIDIFF_SB_MISS; + } + } + } + + return ret; +} + + +/*********************************************************************************************************/ +INCHI_MODE CompareReversedINChI3( INChI *i1 /* InChI from reversed struct */, + INChI *i2 /* input InChI */, + INChI_Aux *a1, + INChI_Aux *a2, + int *err ) +{ + INCHI_MODE ret = 0; + INChI_Stereo *Stereo1=NULL, *Stereo2=NULL; + int n1, n2, m, j, j1, j2, ret2, num_H1, num_H2; + ICR icr; + ICR *picr = &icr; + + *err = 0; + + memset( picr, 0, sizeof(*picr) ); + + if ( i1 == NULL && i2 == NULL ) + return 0; + if ( (i1 == NULL) ^ (i2 == NULL) ) { + ret |= INCHIDIFF_PROBLEM; /* one InChI exists while another doesn't */ + goto exit_function; + } + + if ( i1->nErrorCode == i2->nErrorCode ) { + if ( i1->nErrorCode ) { + ret |= INCHIDIFF_PROBLEM; /* both InChI have same error codes */ + goto exit_function; + } + } else { + ret |= INCHIDIFF_PROBLEM; /* at least one InChI has an error code */ + goto exit_function; + } + + if ( i1->nNumberOfAtoms != i2->nNumberOfAtoms ) { + ret |= INCHIDIFF_NUM_AT; + goto exit_function; + } + if ( i1->nNumberOfAtoms > 0 ) { + if ( memcmp( i1->nAtom, i2->nAtom, i1->nNumberOfAtoms*sizeof(i1->nAtom[0]) ) ) { + ret |= INCHIDIFF_ATOMS; + goto exit_function; + } + /* INCHIDIFF_NON_TAUT_H, INCHIDIFF_MORE_FH, INCHIDIFF_LESS_FH */ + if ( memcmp( i1->nNum_H, i2->nNum_H, i1->nNumberOfAtoms*sizeof(i1->nNum_H[0]) ) ) { + ret |= INCHIDIFF_POSITION_H; + for ( j1 = 0; j1 < i1->nNumberOfAtoms; j1 ++ ) { + if ( i1->nNum_H[j1] != i2->nNum_H[j1] && picr->num_diff_pos_H < ICR_MAX_DIFF_FIXED_H ) { + picr->diff_pos_H_at[picr->num_diff_pos_H] = j1; + picr->diff_pos_H_nH[picr->num_diff_pos_H] = i1->nNum_H[j1] - i2->nNum_H[j1]; + picr->num_diff_pos_H ++; + } + } + } + /* fixed H */ + if ( i1->nNum_H_fixed || i2->nNum_H_fixed ) { + int bHasFixedH1 = 0, bHasFixedH2 = 0, i; + if ( i1->nNum_H_fixed ) { + for ( i = 0; i < i1->nNumberOfAtoms; i ++ ) { + if ( i1->nNum_H_fixed[i] ) { + bHasFixedH1 ++; + } + } + } + if ( i2->nNum_H_fixed ) { + for ( i = 0; i < i2->nNumberOfAtoms; i ++ ) { + if ( i2->nNum_H_fixed[i] ) { + bHasFixedH2 ++; + } + } + } + if ( bHasFixedH1 && !bHasFixedH2 ) { + for ( i = j = 0; i < i1->nNumberOfAtoms; i ++ ) { + if ( i1->nNum_H_fixed[i] ) { + if ( j < ICR_MAX_DIFF_FIXED_H ) { + picr->fixed_H_at1_more[j] = i; + picr->fixed_H_nH1_more[j] = i1->nNum_H_fixed[i]; + j ++; + } + } + } + picr->num_fixed_H1_more = j; + ret |= INCHIDIFF_MORE_FH; /* Extra Fixed-H */ + } else + if ( !bHasFixedH1 && bHasFixedH2 ) { + for ( i = j = 0; i < i2->nNumberOfAtoms; i ++ ) { + if ( i2->nNum_H_fixed[i] ) { + if ( j < ICR_MAX_DIFF_FIXED_H ) { + picr->fixed_H_at2_more[j] = i; + picr->fixed_H_nH2_more[j] = i2->nNum_H_fixed[i]; + j ++; + } + } + } + picr->num_fixed_H2_more = j; + ret |= INCHIDIFF_LESS_FH; /* Missed Fixed-H */ + } else + if ( bHasFixedH1 && bHasFixedH2 && + memcmp( i1->nNum_H_fixed, i2->nNum_H_fixed, i1->nNumberOfAtoms*sizeof(i1->nNum_H_fixed[0]) ) ) { + for ( i = j1 = j2 = 0; i < i1->nNumberOfAtoms; i ++ ) { + if ( i1->nNum_H_fixed[i] > i2->nNum_H_fixed[i] ) { + if ( j1 < ICR_MAX_DIFF_FIXED_H ) { + picr->fixed_H_at1_more[j1] = i; + picr->fixed_H_nH1_more[j1] = i1->nNum_H_fixed[i] - i2->nNum_H_fixed[i]; + j1 ++; + } + } else + if ( i1->nNum_H_fixed[i] < i2->nNum_H_fixed[i] ) { + if ( j2 < ICR_MAX_DIFF_FIXED_H ) { + picr->fixed_H_at2_more[j2] = i; + picr->fixed_H_nH2_more[j2] = i2->nNum_H_fixed[i] - i1->nNum_H_fixed[i]; + j2 ++; + } + } + } + ret |= (j1? INCHIDIFF_MORE_FH:0) | (j2? INCHIDIFF_LESS_FH:0); + picr->num_fixed_H1_more = j1; + picr->num_fixed_H2_more = j2; + } + } + } + /* compare formulas and H */ + num_H1 = 0; + num_H2 = 0; + ret2 = CompareHillFormulasNoH( i1->szHillFormula, i2->szHillFormula, &num_H1, &num_H2 ); + picr->tot_num_H1 = num_H1; + picr->tot_num_H2 = num_H2; + if ( ret2 ) { + ret |= INCHIDIFF_NUM_EL; + goto exit_function; + } + if ( num_H1 > num_H2 ) { + ret |= INCHIDIFF_MORE_H; + } + if ( num_H1 < num_H2 ) { + ret |= INCHIDIFF_LESS_H; + } + + if ( i1->lenConnTable != i2->lenConnTable ) { + ret |= INCHIDIFF_CON_LEN; + goto exit_function; + } else + if ( i1->lenConnTable > 0 && memcmp( i1->nConnTable, i2->nConnTable, i1->lenConnTable*sizeof(i1->nConnTable[0]) ) ) { + ret |= INCHIDIFF_CON_TBL; + goto exit_function; + } + /* output special cases: different number of t-groups, different sizes of t-groups, different endpoints */ + /* in isotopic or deprotonated cases i1->lenTautomer == 1 && i1->nTautomer[0] = 0 */ +/* + if ( i1->lenTautomer != i2->lenTautomer && (i1->lenTautomer > 1 || i2->lenTautomer > 1) ) { + ret |= INCHIDIFF_TAUT_LEN; + } +*/ + /* compare number of t-groups */ + n1 = i1->lenTautomer? i1->nTautomer[0] : 0; + n2 = i2->lenTautomer? i2->nTautomer[0] : 0; + if ( !n1 && n2 ) { + ret |= INCHIDIFF_NO_TAUT; + } else + if ( n1 && !n2 ) { + ret |= INCHIDIFF_WRONG_TAUT; + } else + if ( n1 == 1 && n2 > 1 ) { + ret |= INCHIDIFF_SINGLE_TG; + } else + if ( n1 > 1 && n2 == 1 ) { + ret |= INCHIDIFF_MULTIPLE_TG; + } else + if ( n1 != n2 ) { + ret |= INCHIDIFF_NUM_TG; + } + if ( n1 || n2 ) { + /* number of endpoints */ + int num1 = 0, num2 = 0, num_M1=0, num_M2=0; + int len, num_eq, num_in1_only, num_in2_only; + AT_NUMB *pe1 = (AT_NUMB *) inchi_malloc( (i1->lenTautomer+1) * sizeof(pe1[0]) ); + AT_NUMB *pe2 = (AT_NUMB *) inchi_malloc( (i2->lenTautomer+1) * sizeof(pe2[0]) ); + num_H1 = num_H2=0; + /* collect endpoints, H, (-) */ + if ( !pe1 || !pe2 ) { + if ( pe1 ) inchi_free( pe1 ); + if ( pe2 ) inchi_free( pe2 ); + *err = RI_ERR_ALLOC; /* allocation error */ + goto exit_function; + } + for ( m = 1; m < i1->lenTautomer; m += len ) { + len = i1->nTautomer[m ++]; + num_H1 += i1->nTautomer[m]; + num_M1 += i1->nTautomer[m+1]; + for ( j = 2; j < len; j ++ ) { + pe1[num1 ++] = i1->nTautomer[m + j]; + } + } + for ( m = 1; m < i2->lenTautomer; m += len ) { + len = i2->nTautomer[m ++]; + num_H2 += i2->nTautomer[m]; + num_M2 += i2->nTautomer[m+1]; + for ( j = 2; j < len; j ++ ) { + pe2[num2 ++] = i2->nTautomer[m + j]; + } + } + picr->num_taut_H1 = num_H1; + picr->num_taut_H2 = num_H2; + picr->num_taut_M1 = num_M1; + picr->num_taut_M2 = num_M2; + /* sort endpoints */ + insertions_sort_AT_NUMB( pe1, num1 ); + insertions_sort_AT_NUMB( pe2, num2 ); + /* compare */ + /* + if ( num1 < num2 ) { + ret |= INCHIDIFF_LESS_TG_ENDP; + } else + if ( num1 > num2 ) { + ret |= INCHIDIFF_MORE_TG_ENDP; + } + */ + /* compare all */ + num_eq = num_in1_only = num_in2_only = 0; + for ( j1 = j2 = 0; j1 < num1 && j2 < num2; ) { + if( pe1[j1] == pe2[j2] ) { + j1 ++; + j2 ++; + num_eq ++; + } else + if ( pe1[j1] < pe2[j1] ) { + if ( picr->num_endp_in1_only < ICR_MAX_ENDP_IN1_ONLY ) { + picr->endp_in1_only[picr->num_endp_in1_only ++] = pe1[j1]; + } + j1 ++; + num_in1_only ++; + } else { + if ( picr->num_endp_in2_only < ICR_MAX_ENDP_IN2_ONLY ) { + picr->endp_in2_only[picr->num_endp_in2_only ++] = pe2[j2]; + } + j2 ++; + num_in2_only ++; + } + } + while ( j1 < num1 ) { + if ( picr->num_endp_in1_only < ICR_MAX_ENDP_IN1_ONLY ) { + picr->endp_in1_only[picr->num_endp_in1_only ++] = pe1[j1]; + } + j1 ++; + num_in1_only ++; + } + while ( j2 < num2 ) { + if ( picr->num_endp_in2_only < ICR_MAX_ENDP_IN2_ONLY ) { + picr->endp_in2_only[picr->num_endp_in2_only ++] = pe2[j2]; + } + j2 ++; + num_in2_only ++; + } + if ( num_in1_only ) { + ret |= INCHIDIFF_EXTRA_TG_ENDP; + } + if ( num_in2_only ) { + ret |= INCHIDIFF_MISS_TG_ENDP; + } + if ( !num_in1_only && !num_in2_only && num_eq ) { + ; /* same t-groups endpoints */ + } else { + ret |= INCHIDIFF_DIFF_TG_ENDP; + } + inchi_free( pe1 ); + inchi_free( pe2 ); + } + + if ( (i1->lenTautomer > 1 && i2->lenTautomer > 1) && + ( i1->lenTautomer != i2->lenTautomer || + memcmp( i1->nTautomer, i2->nTautomer, i1->lenTautomer*sizeof(i1->nTautomer[0]) ) ) ) + ret |= INCHIDIFF_TG; + + if ( i1->nNumberOfIsotopicAtoms != i2->nNumberOfIsotopicAtoms ) { + ret |= INCHIDIFF_NUM_ISO_AT; + } else + if ( i1->nNumberOfIsotopicAtoms > 0 && memcmp( i1->IsotopicAtom, i2->IsotopicAtom, i1->nNumberOfIsotopicAtoms*sizeof(i1->IsotopicAtom[0]) ) ) + ret |= INCHIDIFF_ISO_AT; + if ( i1->nTotalCharge != i2->nTotalCharge ) + ret |= INCHIDIFF_CHARGE; + if ( a1 && a1->nNumRemovedProtons && (!a2 || a2->nNumRemovedProtons != a1->nNumRemovedProtons) ) { + ret |= INCHIDIFF_REM_PROT; + } + if ( a1 && (!a2 || + a2->nNumRemovedIsotopicH[0] != a1->nNumRemovedIsotopicH[0] || + a2->nNumRemovedIsotopicH[1] != a1->nNumRemovedIsotopicH[1] || + a2->nNumRemovedIsotopicH[2] != a1->nNumRemovedIsotopicH[2]) ) { + ret |= INCHIDIFF_REM_ISO_H; + } + +/* + if ( i1->nPossibleLocationsOfIsotopicH && i2->nPossibleLocationsOfIsotopicH ) { + if ( i1->nPossibleLocationsOfIsotopicH[0] != i2->nPossibleLocationsOfIsotopicH[0] || + memcmp(i1->nPossibleLocationsOfIsotopicH, i2->nPossibleLocationsOfIsotopicH, + sizeof(i1->nPossibleLocationsOfIsotopicH[0])*i1->nPossibleLocationsOfIsotopicH[0]) ) + return 18; + } else + if ( !i1->nPossibleLocationsOfIsotopicH != !i2->nPossibleLocationsOfIsotopicH ) { + return 19; + } +*/ + if ( i1->StereoIsotopic && + i1->StereoIsotopic->nNumberOfStereoBonds + i1->StereoIsotopic->nNumberOfStereoCenters ) { + Stereo1 = i1->StereoIsotopic; + } else { + Stereo1 = i1->Stereo; + } + if ( i2->StereoIsotopic && + i2->StereoIsotopic->nNumberOfStereoBonds + i2->StereoIsotopic->nNumberOfStereoCenters ) { + Stereo2 = i2->StereoIsotopic; + } else { + Stereo2 = i2->Stereo; + } + ret |= CompareReversedStereoINChI3( Stereo1, Stereo2, picr ); + +exit_function: + picr->flags = ret; + + return ret; +} + + +/* message group names */ +const CMP_INCHI_MSG_GROUP CompareInchiMsgsGroup[] = { +{IDGRP_ERR, " Error:"}, +{IDGRP_H, " Hydrogens:"}, +{IDGRP_MOB_GRP, " Mobile-H groups:"}, +{IDGRP_ISO_AT, " Isotopic:"}, +{IDGRP_CHARGE, " Charge(s):"}, +{IDGRP_PROTONS, " Proton balance:"}, +{IDGRP_ISO_H, " Exchangeable isotopic H:"}, +{IDGRP_SC, " Stereo centers/allenes:"}, +{IDGRP_SB, " Stereobonds/cumulenes:"}, +{IDGRP_HLAYER, " Fixed-H layer:"}, +{IDGRP_COMP, " Number of components:"}, +{IDGRP_CONV_ERR," Conversion encountered:"}, +{IDGRP_ZERO, ""} +}; + + + +/* messages */ +const CMP_INCHI_MSG CompareInchiMsgs[] = { +{INCHIDIFF_PROBLEM ,IDGRP_ERR, " Wrong result" }, /*0x00000001, severe: at least one InChI does not exist */ +{INCHIDIFF_POSITION_H ,IDGRP_H, " Locations or number" }, /*0x00000002, difference in non-taut {Mobile-H} or all H {Fixed-H} location/number */ +{INCHIDIFF_MORE_FH ,IDGRP_H, " Fixed-H" }, /*0x00000004, extra fixed H */ +{INCHIDIFF_LESS_FH ,IDGRP_H, " Fixed-H" }, /*0x00000004, missing fixed H */ +{INCHIDIFF_MORE_H ,IDGRP_H, " Number" }, /*0x00000008, formulas differ in number of H */ +{INCHIDIFF_LESS_H ,IDGRP_H, " Number" }, /*0x00000008, formulas differ in number of H */ +{INCHIDIFF_NO_TAUT ,IDGRP_MOB_GRP, " Missing" }, /*0x00000010, restored structure has no taut groups while the original InChI has some */ +{INCHIDIFF_WRONG_TAUT ,IDGRP_MOB_GRP, " Falsely present" }, /*0x00000020, restored has tautomerism while the original does not have it */ +{INCHIDIFF_SINGLE_TG ,IDGRP_MOB_GRP, " One instead of multiple" }, /*0x00000040, restored has 1 taut. group while the original InChI has multiple tg */ +{INCHIDIFF_MULTIPLE_TG ,IDGRP_MOB_GRP, " Multiple instead of one" }, /*0x00000080, restored has multiple tg while the original InChI has only one tg */ +{INCHIDIFF_EXTRA_TG_ENDP,IDGRP_MOB_GRP, " Attachment points" }, /*0x00000100, extra tautomeric endpoint{s} in restored structure */ +{INCHIDIFF_MISS_TG_ENDP ,IDGRP_MOB_GRP, " Attachment points" }, /*0x00000100, one or more tg endpoint is not in the restored structure */ +{INCHIDIFF_DIFF_TG_ENDP ,IDGRP_MOB_GRP, " Attachment points" }, /*0x00000100, lists of tg endpoints are different */ +{INCHIDIFF_NUM_TG ,IDGRP_MOB_GRP, " Number" }, /*0x00000200, different number of tautomeric groups */ +{INCHIDIFF_TG ,IDGRP_MOB_GRP, " Do not match" }, /*0x00000200, different tautomeric groups */ +{INCHIDIFF_NUM_ISO_AT ,IDGRP_ISO_AT, " Atoms do not match" }, /*0x00000400, ?severe: restored struct. has different number of isotopic atoms */ +{INCHIDIFF_ISO_AT ,IDGRP_ISO_AT, " Atoms do not match" }, /*0x00000400, ?severe: restored struct. has different locations/isotopes of isotopic atoms */ +{INCHIDIFF_REM_ISO_H ,IDGRP_ISO_H, " Does not match for a component" }, /*0x00000800, isotopic H removed */ +{INCHIDIFF_MOB_ISO_H ,IDGRP_ISO_H, " Do not match" }, /*0x00001000, different number of mobile exchangeable isotopic H */ +{INCHIDIFF_CHARGE ,IDGRP_CHARGE, " Do not match" }, /*0x00002000, restored structure has different charge */ +{INCHIDIFF_REM_PROT ,IDGRP_PROTONS, " Does not match for a component" }, /*0x00004000, proton{s} removed/added from the restored structure */ +{INCHIDIFF_MOBH_PROTONS ,IDGRP_PROTONS, " Does not match" }, /*0x00008000, different proton balance */ +{INCHIDIFF_SC_INV ,IDGRP_SC, " Falsely inverted" }, /*0x00010000, restores structure has different inversion stereocenter mark */ +{INCHIDIFF_SC_PARITY ,IDGRP_SC, " Wrong parity" }, /*0x00020000, restored structure has stereoatoms or allenes with different parity */ +{INCHIDIFF_SC_EXTRA_UNDF,IDGRP_SC, " Extra undefined" }, /*0x00040000, restored structure has extra undefined stereocenter{s} */ +{INCHIDIFF_SC_EXTRA ,IDGRP_SC, " Extra known" }, /*0x00080000, restored structure has extra stereocenter{s} */ +{INCHIDIFF_SC_MISS_UNDF ,IDGRP_SC, " Missing undefined" }, /*0x00100000, restored structure has not some undefined stereocenter{s} */ +{INCHIDIFF_SC_MISS ,IDGRP_SC, " Missing known" }, /*0x00200000, restored structure has not some stereocenters that are not undefined */ +{INCHIDIFF_SB_PARITY ,IDGRP_SB, " Wrong parity" }, /*0x00400000, restored structure has stereobonds or cumulenes with different parity */ +{INCHIDIFF_SB_EXTRA_UNDF,IDGRP_SB, " Extra undefined" }, /*0x00800000, restored structure has extra undefined stereobond{s} */ +{INCHIDIFF_SB_EXTRA ,IDGRP_SB, " Missing known" }, /*0x01000000, restored structure has extra stereobond{s} */ +{INCHIDIFF_SB_MISS_UNDF ,IDGRP_SB, " Missing undefined" }, /*0x02000000, restored structure has not some undefined stereocenters */ +{INCHIDIFF_SB_MISS ,IDGRP_SB, " Missing known" }, /*0x04000000, restored structure has not some stereobonds that are not undefined */ +{INCHIDIFF_COMP_HLAYER ,IDGRP_HLAYER, " Missing or extra" }, /*0x08000000, Restored component has Mobile-H layer instead of both Mobile-H & Fixed-H or both instead of one */ +{INCHIDIFF_COMP_NUMBER ,IDGRP_COMP, " Does not match" }, /*0x10000000, wrong number of components */ +{INCHIDIFF_STR2INCHI_ERR,IDGRP_CONV_ERR," Error" }, /*0x20000000 Restored structure to InChI conversion error */ +{INCHIDIFF_ZERO ,IDGRP_ZERO, "" } +}; + + + + +/*************************************************************************/ +int AddOneMsg( char *szMsg, + int used_len, + int tot_len, + const char *szAddMsg, + const char *szDelim ) +{ + const char ellip[] = "..."; + int len = (int) strlen(szAddMsg ); + int len_delim = (used_len && szDelim)? strlen(szDelim) : 0; + int len_to_copy; + if ( len + len_delim + used_len < tot_len ) { + if ( len_delim ) { + strcpy( szMsg+used_len, szDelim ); + used_len += len_delim; + } + strcpy( szMsg+used_len, szAddMsg ); + used_len += len; + } else + if ( (len_to_copy = (tot_len - used_len - len_delim - (int)sizeof(ellip))) > 10 ) { + if ( len_delim ) { + strcpy( szMsg+used_len, szDelim ); + used_len += len_delim; + } + strncpy( szMsg+used_len, szAddMsg, len_to_copy ); + used_len += len_to_copy; + strcpy( szMsg+used_len, ellip ); + used_len += sizeof( ellip ) - 1; + } + return used_len; +} + + +/*************************************************************************/ +int FillOutCompareMessage( char *szMsg, int nLenMsg, INCHI_MODE bits[]) +{ + int bMobileH, k, n, len = (int) strlen( szMsg ); + int iPrevGrpIdx, iCurGrpIdx, bFound; + INCHI_MODE bit; + static const char *hdr = " Problems/mismatches:"; + char szOneMsg[256]; + + int mismatch = 0; + + if ( bits[TAUT_YES] || bits[TAUT_NON] ) + { + + mismatch = -1; + + if ( !strstr( szMsg, hdr ) ) + { + len = AddOneMsg( szMsg, len, nLenMsg, hdr, NULL ); + } + + for ( bMobileH = TAUT_YES; 0 <= bMobileH; bMobileH -- ) + /* bMobileH = TAUT_YES, TAUT_NON */ + { + if ( bits[bMobileH] ) + { + strcpy( szOneMsg, bMobileH==TAUT_YES? " Mobile-H(" : " Fixed-H(" ); + len = AddOneMsg( szMsg, len, nLenMsg, szOneMsg, NULL ); + } + + bit = 1; + iPrevGrpIdx = -1; + do + { + if ( bit & bits[bMobileH] ) + { + /* search for the message */ + bFound = 0; + for ( k = 0; CompareInchiMsgs[k].nBit != INCHIDIFF_ZERO && !bFound; k ++ ) + { + if ( bit & (INCHI_MODE)CompareInchiMsgs[k].nBit ) + { + /* message found */ + for ( n = 0; CompareInchiMsgsGroup[n].nGroupID != IDGRP_ZERO; n ++ ) + { + if ( CompareInchiMsgsGroup[n].nGroupID == CompareInchiMsgs[k].nGroupID ) + { + iCurGrpIdx = n; + if ( iCurGrpIdx != iPrevGrpIdx ) + { + if ( iPrevGrpIdx >= 0 ) + { + len = AddOneMsg( szMsg, len, nLenMsg, ";", NULL ); + } + len = AddOneMsg( szMsg, len, nLenMsg, CompareInchiMsgsGroup[iCurGrpIdx].szGroupName, NULL ); + } + len = AddOneMsg( szMsg, len, nLenMsg, CompareInchiMsgs[k].szMsg, iCurGrpIdx == iPrevGrpIdx? ",":NULL ); + iPrevGrpIdx = iCurGrpIdx; + bFound = 1; + break; + } + } + } + } + } + bit <<= 1; + } while ( bit ); + + if ( bits[bMobileH] ) + { + len = AddOneMsg( szMsg, len, nLenMsg, ")", NULL ); + } + } + } + + return mismatch; /*len; */ +} + + +#endif diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichirvrs.h b/INCHI-1-SRC/INCHI_BASE/src/ichirvrs.h similarity index 74% rename from INCHI-1-SRC/INCHI_API/inchi_dll/ichirvrs.h rename to INCHI-1-SRC/INCHI_BASE/src/ichirvrs.h index 1bee0c4..c89f164 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichirvrs.h +++ b/INCHI-1-SRC/INCHI_BASE/src/ichirvrs.h @@ -1,916 +1,1108 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __ICHIRVRS_H__ -#define __ICHIRVRS_H__ - -#define ICHICONST const - -#define RI_ERR_ALLOC (-1) -#define RI_ERR_SYNTAX (-2) -#define RI_ERR_PROGR (-3) -#define RI_ERR_EOL (-4) -#define RI_ERR_EOF (0) - - -#define NO_VALUE_INT 9999 -#define NOT_READ_INT 9998 /* has not been read yet */ - -#define VALUE_OCTET 8 /* number of electrons in a full shell */ - -#define INC_EDGE_LIST_DEFAULT 64 - -typedef struct tagXYZCoord { - double xyz[3]; -} XYZ_COORD; - -typedef struct tagStructRestoreMode { - int bMetalAddFlower; /* 1 => allow adjustable metal valence and charge; 0=> use std charge/valence */ - /* the following three apply only if bMetalAddFlower = 1 */ - int nMetalMinBondOrder; /* edge_flow=f means bond order=cMetalMinBondOrder+f */ - int nMetalInitEdgeFlow; /* one bond contribution to metal's (st-cap - st-flow) = */ - /* (nMetalInitBondOrder-nMetalMinBondOrder) - nMetalInitEdgeFlow */ - int nMetalInitBondOrder; /* >= nMetalMinBondOrder + nMetalInitEdgeFlow */ - /* same for metal-endpoint bonds */ - int nMetal2EndpointMinBondOrder; - int nMetal2EndpointInitBondOrder; - int nMetal2EndpointInitEdgeFlow; - int nMetalFlowerParam_D; /* additional edge capacity for canceling radicals */ - int nMetalMaxCharge_D; /* cap and/or flow for metal charge group */ - int bStereoRemovesMetalFlag; /* 1=> treat stereogenic atoms and atoms connected by a stereo bond as non-metals */ - - int bFixStereoBonds; /* 1=> forbid stereogenic double bonds from changing */ -} SRM; - -typedef struct tagReversedInChI { - PINChI2 *pINChI[INCHI_NUM]; - PINChI_Aux2 *pINChI_Aux[INCHI_NUM]; - int num_components[INCHI_NUM]; - int nRetVal; -} REV_INCHI; - -/**************************************/ -#define BFS_Q_CLEAR (-1) -#define BFS_Q_FREE (-2) -typedef struct tagBfsQueue { - QUEUE *q; - AT_RANK *nAtomLevel; - S_CHAR *cSource; - int num_at; - AT_RANK min_ring_size; /* 8 => detect 7-member and smaller rings */ -} BFS_Q; -/**************************************/ - -#define EXTRACT_STRUCT_NUMBER 1 - -/* additional Mobile-H parities to be added to Fixed-H parities */ -/* This allows to set parities that exist in Mobile-H layer only */ -typedef struct tagInpAtomAddParities { - /* cml 0D parities */ - S_CHAR bUsed0DParity; /* bit=1 => stereobond; bit=2 => stereocenter */ - /* cml tetrahedral parity */ - S_CHAR p_parity; - AT_NUMB p_orig_at_num[MAX_NUM_STEREO_ATOM_NEIGH]; - /* cml bond parities */ - S_CHAR sb_ord[MAX_NUM_STEREO_BONDS]; /* stereo bond/neighbor ordering number, starts from 0 */ - /* neighbors on both sides of stereobond have same sign=> trans/T/E, diff. signs => cis/C/Z */ - S_CHAR sn_ord[MAX_NUM_STEREO_BONDS]; /* ord. num. of the neighbor adjacent to the SB; starts from 0; - -1 means removed explicit H */ - /* neighbors on both sides of stereobond have same parity => trans/T/E/2, diff. parities => cis/C/Z/1 */ - S_CHAR sb_parity[MAX_NUM_STEREO_BONDS]; - AT_NUMB sn_orig_at_num[MAX_NUM_STEREO_BONDS]; /* orig. at number of sn_ord[] neighbors */ -} inp_ATOM_STEREO; - -#define FIX_STEREO_BOND_ORDER 0 /* 1=> fix stereobonds; treat metal as non-metal if it is stereogenic or has a stereobond */ - -#define METAL_FREE_CHARGE_VAL 1 /* 1=> allow free changing charges/valences of metals; initial bond order=0 or 1 */ -#define ALLOW_METAL_BOND_ZERO 1 /* 1=> allow zero flow (bobd order) to metals */ - -#if ( ALLOW_METAL_BOND_ZERO == 1 ) -/* INIT_METAL_BOND_ZERO=1 => INIT_METAL_BOND_FLOW=0 */ -#define INIT_METAL_BOND_ZERO 0 /* 1=> initialize zero order bond to metals */ -#define INIT_METAL_BOND_FLOW 1 /* 1=> init flow=1, 0 => init. flow = 0 */ -#else -#define INIT_METAL_BOND_ZERO 0 -#define INIT_METAL_BOND_FLOW 0 /* always 0 */ -#endif - -#define I2A_FLAG_FIXEDH 0x0001 -#define I2A_FLAG_RECMET 0x0002 - -#define EL_NUMBER_H 1 - - -#define ATYPE_H 1 -#define ATYPE_Na 2 -#define ATYPE_Mg 3 -#define ATYPE_B 4 -#define ATYPE_C 5 -#define ATYPE_N 6 -#define ATYPE_O 7 -#define ATYPE_Cl 8 - - -/* first bonds valence for charge = c is cValence[0][c+VAL_BASE]; VAL_MIN_CHARGE <= c <= VAL_MAX_CHARGE */ -/* second bonds valence for charge = c is cValence[1][c+VAL_BASE]; VAL_MIN_CHARGE <= c <= VAL_MAX_CHARGE */ -/* total number of valences is 2 = VAL_NUMBER */ -/* neutral bond valence orders are cValence[0][VAL_NEUTR_ORDER], cValence[1][VAL_NEUTR_ORDER] */ -#define VAL_BASE ( 1) -#define VAL_MIN_CHARGE (-1) -#define VAL_MAX_CHARGE ( 1) -#define VAL_NUMBER ( 2) -#define VAL_NEUTR_ORDER (VAL_MAX_CHARGE-VAL_MIN_CHARGE+1) -#define VAL_LENGTH (VAL_NEUTR_ORDER+1) -#define VAL_NEGAT_CHARGE 0 -#define VAL_NEUTR_CHARGE 1 -#define VAL_POSIT_CHARGE 2 - -typedef struct tagAtomIonPrperies { - /* char cValence[VAL_NUMBER][VAL_LENGTH]; */ /* ordering numbers of minimal valence, 0-based */ - char cDoNotAddH; /* InChI does not add H to this element */ - char cMetal; /* the element is a metal */ - char cNumBondsToMetal; /* number of bonds to metal */ - char cInitFlowToMetal; /* sum of init flow to metal atoms */ - char cInitValenceToMetal; /* sum of init adjusted bond orders to metal atoms */ - char cInitOrigValenceToMetal; /* sum of init bond orders to metal atoms */ - char cMaxFlowToMetal; /* max total edge flow to metal atoms */ - char cInitFreeValences; /* number of 'dots' to connect; charges are marked separately */ - S_CHAR cInitCharge; /* initial charge on the atom (not included in ChargeStruct */ - char cNumValenceElectrons; - char cPeriodicRowNumber; - char cMinRingSize; /* min ring size for atoms that have 2 bonds only */ - U_CHAR cPeriodicNumber; /* number in Periodic Table of elements */ - S_CHAR cnListIndex; /* (index in the cnList) + 1; 0 => none */ - int nCMinusGroupEdge; /* (index of the edge to the atom's (-) group) + 1 */ - int nCPlusGroupEdge; /* (index of the edge to the atom's (+) group) + 1 */ - int nMetalGroupEdge; /* index of the edge to the atom's M-group + 1 */ - int nTautGroupEdge; /* index of the edge from the atom to the t-group + 1 */ -} VAL_AT; - - -/******************************************************************************************************/ -#define INI_NUM_TCGROUPS 16 -#define INC_NUM_TCGROUPS 16 - -typedef enum tagTgRestoreFlags { - TGRF_MINUS_FIRST = 1 -} TGRF; -typedef struct tagTCGroup { - int type; /* group type */ - int ord_num; /* ordering number within the type, typically t-group number */ - int num_edges; - /* charge group specific */ - int st_cap; - int st_flow; - int edges_cap; - int edges_flow; - int nVertexNumber; /* group vertex number; 0 = unassigned */ - int nForwardEdge; /* edge index: from c-group to central Y-connecting vertex - or from supergroup to (+/-) vertex; 0 => unassigned */ - int nBackwardEdge; /* edge index: from central Y-connecting vertex - to supergroup; 0 => unassigned */ - /* tautomeric group specific */ - short tg_num_H; /* number of H in a tautomeric group */ - short tg_num_Minus; /* negative charge on t-group */ - Vertex tg_set_Minus; /* the vertex+1 that has to have (-) */ - short tg_RestoreFlags; /* Set (-) to first memberst of a t-group (usually, N) */ -} TC_GROUP; - -typedef enum tagTCGroupTypes { - TCG_None = -1, /* so far only ord=0 is used */ - /* group type ord */ - TCG_Plus0 = 0, /* BNS_VT_C_POS 0 */ - TCG_Plus1, /* BNS_VT_C_POS 1 */ - TCG_Minus0, /* BNS_VT_C_NEG 0 */ - TCG_Minus1, /* BNS_VT_C_NEG 1 */ - TCG_Plus_C0, /* BNS_VT_C_POS_C 0 */ - TCG_Plus_C1, /* BNS_VT_C_POS_C 1 */ - TCG_Minus_C0, /* BNS_VT_C_NEG_C 0 */ - TCG_Minus_C1, /* BNS_VT_C_NEG_C 1 */ - TCG_Plus_M0, /* BNS_VT_C_POS_M 0 */ - TCG_Plus_M1, /* BNS_VT_C_POS_M 1 */ - TCG_Minus_M0, /* BNS_VT_C_NEG_M 0 */ - TCG_Minus_M1, /* BNS_VT_C_NEG_M 1 */ - TCG_MeFlower0, /* BNS_VT_M_GROUP 0 */ /* base */ - TCG_MeFlower1, /* BNS_VT_M_GROUP 1 */ - TCG_MeFlower2, /* BNS_VT_M_GROUP 2 */ - TCG_MeFlower3, /* BNS_VT_M_GROUP 3 */ - - TCG_Plus, /* BNS_VT_C_POS_ALL 0 */ - TCG_Minus, /* BNS_VT_C_NEG_ALL 0 */ - - NUM_TCGROUP_TYPES /* number of group types */ -}TCGR_TYPE; - -typedef struct tagAllTCGroups { - TC_GROUP *pTCG; - int num_tc_groups; /* number of charge groups and metal-flower vertices */ - int max_tc_groups; /* number of allocated of pTCG[] elements */ - int nGroup[NUM_TCGROUP_TYPES]; /* tagTCGroupTypes */ - int nVertices; /* total number of vertices */ - int nEdges; /* total number of edges */ - int nAddIedges; /* additional increase of number of iedges for edge switching to another group */ - int num_atoms; /* number of atoms */ - int num_bonds; /* number of bonds */ - int num_tgroups; /* number t-groups */ - int num_tgroup_edges; /* number of edges to t-groups */ - /* charges */ - int tgroup_charge; /* total charge of all t-groups */ - int charge_on_atoms; /* charge permanently sitting on atoms */ - int added_charge; /* charge added to the c-groups */ - int total_charge; /* total charge of the component */ - int total_electrons; /* total number of electrons on all atoms */ - int total_electrons_metals; /* total number of electrons on unbonded metals */ - - int num_metal_atoms; /* number of metal atoms */ - int num_metal_bonds; /* number of atom-metal bonds */ - /* excess_charge = total_charge - added_charge - tgroup_charge: add to metals etc. */ - - int nEdge4charge; /* edge used to add charges; neighbor1=supercharge, another = (+/-) vertex */ - int nEdgePlus; /* edge to (+) supergroup; 0 means none */ - int nEdgeMinus; /* edge to (-) supergroup; 0 means none */ - int iComponent; /* component number */ - int iAtNoOffset; /* first atom number -- always 0 for now */ -} ALL_TC_GROUPS; - -/**************************************/ -#define EDGE_LIST_CLEAR (-1) -#define EDGE_LIST_FREE (-2) - -typedef struct tagEdgeList { - int num_alloc; - int num_edges; - EdgeIndex *pnEdges; -} EDGE_LIST; -/**************************************/ - -#define BOND_MARK_STEREO 0x10 -#define BOND_TYPE_STEREO (BOND_TYPE_SINGLE | BOND_MARK_STEREO) - -/* local */ -#define RESET_EDGE_FORBIDDEN_MASK 0 - -#define TREAT_ATOM_AS_METAL 99 - - -/************************************************************************************/ -typedef struct tagChargeValence { - int nValence; - int nCharge; - int nValenceOrderingNumber; -} CHARGE_VAL; - -#define MY_CONST const -/*************************************************************************************/ -typedef struct tagChargeChangeCandidate { - Vertex iat; - char num_bonds; - char chem_valence; - char cMetal; - char cNumBondsToMetal; - char cNumValenceElectrons; - char cPeriodicRowNumber; - char cNumChargeStates; - U_CHAR el_number; -} CC_CAND; - -typedef struct tagOneComponentRemovedAndExchangeableH { - NUM_H nNumRemovedProtons; - NUM_H nNumRemovedIsotopicH[NUM_H_ISOTOPES]; /* isotopic H that may be exchanged and considered - randomly distributed, including removed protons */ -} COMPONENT_REM_PROTONS; - -typedef struct tagRemovedAndExchangeableH { - /* totals for Mobile-H layer */ - NUM_H nNumRemovedProtons; - NUM_H nNumRemovedIsotopicH[NUM_H_ISOTOPES]; /* isotopic H that may be exchanged and considered - randomly distributed, including removed protons */ - /* for individual components from comparing Fixed-H vs Mobile-H formulas; NULL if not available */ - COMPONENT_REM_PROTONS *pNumProtons; -} REM_PROTONS; - -typedef struct tagInputInChI { - INChI *pInpInChI[INCHI_NUM][TAUT_NUM]; - int nNumComponents[INCHI_NUM][TAUT_NUM]; - REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM]; - int s[INCHI_NUM][TAUT_NUM][2]; /* s[0=non-iso, 1=iso] = 0,1,2,3 <= regular /s; -1=> "/s" (empty) */ - long num_inp; - inp_ATOM *atom; /* the whole restored structure made out of all components */ - int num_atoms; /* number of atoms including explicit H */ - int num_explicit_H; /* number of explicit H in the atom */ - INCHI_MODE CompareInchiFlags[INCHI_NUM][TAUT_NUM]; -} InpInChI; - -typedef struct tagStructFromInChI { - /* InChI component -> Structure result */ - inp_ATOM *at; /* length = num_atoms + num_deleted_H, zero pint struct for BNS->struct conversion */ - inp_ATOM_STEREO *st; /* additional stereo that exists only in Mobile-H layer */ - inp_ATOM *at2; /* length = num_atoms + num_deleted_H, the conversion result */ - - /* information from InChI only */ - T_GROUP_INFO ti; /* from original InChI[0] if Mobile-H from the beginning or later from InChI[1] if Fixed-H */ - AT_NUMB *endpoint; /* from original InChI[1] in case of Fixed-H only */ - S_CHAR *fixed_H; /* from original InChI[0] in case of Fixed-H only */ - XYZ_COORD *pXYZ; - int num_atoms; - int num_deleted_H; /* if requested and Fixed-H InChI is available */ - int nNumRemovedProtonsMobHInChI; /* number of protons removed from Mobile-H struct in original InChI */ - S_CHAR charge; - char bIsotopic; - - /* InChI -> Structure conversion parms and intermediate data */ - BN_STRUCT *pBNS; - BN_DATA *pBD; - ICHICONST SRM *pSrm; - - /* InChI layer to reverse */ - char bMobileH; - char iINCHI; - char bFixedHExists; /* fixed-H InChI exists or not */ - - /* InChI -> Struct component -> Full InChI result (both disconnected and connected if exist) */ - REV_INCHI RevInChI; - int nRemovedProtonsByNormFromRevrs; /* number of H(+) removed by normalization after - Struct Restore and before Add/Remove Protons */ - int nNumRemovedProtonsByRevrs; /* number of H(+) removed by the reconstruction, - before Add/Remove Protons, only from TAUT_YES */ - - int bExtract; /* for debugging */ - - /* single component InChI calculation */ - INChI *pOneINChI[TAUT_NUM]; /* InChI of restored structure */ - INChI_Aux *pOneINChI_Aux[TAUT_NUM]; - INP_ATOM_DATA *pOne_norm_data[TAUT_NUM]; /* normalized restored structure */ - S_CHAR *pOne_fixed_H; /* !!! from normalized restored structure in case of Fixed-H only */ - T_GROUP_INFO One_ti; /* t-groups of normalized canonicalized restored structure */ - int nOneINChI_bMobileH; /* type of restored structure InChI */ - int nNumRemovedProtons; /* =0 for Fixed-H, = num. removed protons in case of Mobile-H InChI */ - /* in case of Fixed-H processing see pStruct->One_ti.tni.nNumRemovedProtons */ - AT_NUMB *nAtno2Canon[TAUT_NUM]; /* nAtno2Canon[restored_at_no][*] = (atom canon number in restored struct)-1*/ - AT_NUMB *nCanon2Atno[TAUT_NUM]; /* nCanon2Atno[(atom canon number in restored struct)-1][*] = restored_at_no; */ - - int nError; - /* other parms */ - char iInchiRec; /* index in the original InChI array */ - char iMobileH; /* index in the original InChI array */ - char bDeleted; /* InChI component marked as Deleted, means a proton in Mobile-H layer */ - /* struct. ordering number "Structure: nnn" if present */ - long num_inp_actual; - - /* utility data */ - BFS_Q *pbfsq; - VAL_AT *pVA; - - int nLink; /* same as in INChI */ - int bPostProcessed; /* recalculate after add/remove protons */ - - /* TAUT_YES layer charges */ - int nChargeRevrs; /* component charge of the reconstructed structure, TAUT_YES layer */ - int nChargeInChI; /* component charge from the original InChI, TAUT_YES layer */ -} StrFromINChI; - - -#define EL_TYPE_O 0x0001 -#define EL_TYPE_S 0x0002 -#define EL_TYPE_N 0x0004 -#define EL_TYPE_P 0x0008 -#define EL_TYPE_C 0x0010 -#define EL_TYPE_X 0x0020 /* any not metal */ -#define EL_TYPE_MASK 0x003f -#define EL_TYPE_OSt 0x0100 /* terminal -OH, -O(-), -SH, -S(-), ... from fix_special_bonds(...) */ -#define EL_TYPE_PT 0x0200 /* may be a tautomeric endpoint */ - -/* the atom to which the node is attached has number 1; added atoms have numbers 2,3,... */ -#define MAX_CN_VAL 3 -typedef struct tagVertCapFlow { - S_SHORT type; - S_CHAR cap; - S_CHAR flow; - S_CHAR valence; -} VCF; -typedef struct tagEdgeCapFlow { - S_SHORT neigh; - S_CHAR cap; - S_CHAR bForbiddenEdge; - S_CHAR flow; -} ECF; -typedef struct tagChargeNodes { - VCF v; - ECF e[MAX_CN_VAL]; -} C_NODE; - - -#define cn_bits_N 1 /* Neutral: charge = 0 */ -#define cn_bits_P 2 /* Plus 1: charge = +1 */ -#define cn_bits_M 4 /* Minus 1: charge = -1 */ -#define cn_bits_shift 3 -#define MAX_NUM_CN_BITS 4 -#define MAKE_CN_BITS(A, B, C, D ) (( (( ((D) << cn_bits_shift | (C)) << cn_bits_shift ) | (B)) << cn_bits_shift ) | (A)) - -#define cn_bits_PNPN MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_P, cn_bits_N) -#define cn_bits_NPNP MAKE_CN_BITS(cn_bits_N, cn_bits_P, cn_bits_N, cn_bits_P) -#define cn_bits_NPN MAKE_CN_BITS(cn_bits_N, cn_bits_P, cn_bits_N, 0) -#define cn_bits_PNP MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_P, 0) -#define cn_bits_MNP MAKE_CN_BITS(cn_bits_M, cn_bits_N, cn_bits_P, 0) -#define cn_bits_PNM MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_M, 0) -#define cn_bits_EN MAKE_CN_BITS(cn_bits_P | cn_bits_M, cn_bits_N, 0, 0) -#define cn_bits_NMN MAKE_CN_BITS(cn_bits_N, cn_bits_M, cn_bits_N, 0) -#define cn_bits_NE MAKE_CN_BITS(cn_bits_N, cn_bits_P | cn_bits_M, 0, 0) -#define cn_bits_NEN MAKE_CN_BITS(cn_bits_N, cn_bits_M | cn_bits_N, cn_bits_N, 0) -#define cn_bits_NP MAKE_CN_BITS(cn_bits_N, cn_bits_P, 0, 0) -#define cn_bits_PN MAKE_CN_BITS(cn_bits_P, cn_bits_N, 0, 0) -#define cn_bits_NM MAKE_CN_BITS(cn_bits_N, cn_bits_M, 0, 0) -#define cn_bits_MN MAKE_CN_BITS(cn_bits_M, cn_bits_N, 0, 0) -#define cn_bits_P_ MAKE_CN_BITS(cn_bits_P, 0, 0, 0) -#define cn_bits_M_ MAKE_CN_BITS(cn_bits_M, 0, 0, 0) -#define cn_bits_N_ MAKE_CN_BITS(cn_bits_N, 0, 0, 0) -#define cn_bits_Me (-1) - -#define cnListIndexMe (17) /* index of {cnMe, cn_bits_Me,... } element of cnList[] */ - -extern int cnListNumEl; /* number of elements in cnList[] */ - -typedef struct tagChargeNodeList { - MY_CONST C_NODE *pCN; - int bits; - int nInitialCharge; - int len; -} CN_LIST; - -extern MY_CONST CN_LIST cnList[]; - -/************************ fixed H comparison ******************************************************/ -#define MAX_DIFF_FIXH 256 -#define MAX_DIFF_MOBH 256 -typedef struct tagAtomsCmpTwoFixedH { - AT_NUMB endptInChI; - AT_NUMB endptRevrs; - AT_NUMB atomNumber; - U_CHAR nValElectr; - U_CHAR nPeriodNum; - S_CHAR nFixHInChI; - S_CHAR nFixHRevrs; - S_CHAR nMobHInChI; - S_CHAR nMobHRevrs; - S_CHAR nNumHRevrs; - S_CHAR nAtChargeRevrs; - S_CHAR nValue; /* flag(s) */ -} CMP2FHATOMS; - -typedef struct tagStructCmpTwoFixedH { - CMP2FHATOMS c2at[MAX_DIFF_FIXH]; - short len_c2at; - short nNumRemHInChI; - short nNumRemHRevrs; - short nNumTgInChI; - short nNumTgRevrs; - short nNumEndpInChI; - short nNumEndpRevrs; - short nNumTgDiffMinus; /* number of would-be-identical t-groups that have different number of (-) */ - short nNumTgDiffH; /* number of would-be-identical t-groups that have different number of H */ - short nNumTgMInChI; /* number of (-) in orig. InChI t-groups */ - short nNumTgHInChI; /* number of H in orig. InChI t-groups */ - short nNumTgMRevrs; /* number of (-) in reversed structure t-groups */ - short nNumTgHRevrs; /* number of H in reversed structure t-groups */ - S_CHAR nChargeFixHInChI; - S_CHAR nChargeMobHInChI; - S_CHAR nChargeFixHRevrs; - S_CHAR nChargeMobHRevrs; - S_CHAR nChargeFixHRevrsNonMetal; /* charge does not include charges on metals */ - S_CHAR nChargeMobHRevrsNonMetal; /* charge does not include charges on metals */ - char bFixedHLayerExistsRevrs; - char bHasDifference; - U_CHAR nNumDiffMobH; - -} CMP2FHINCHI; - -/************************ Mobile H comparison *********************************************/ -typedef struct tagAtomsCmpTwoMobileH { - AT_NUMB endptInChI; - AT_NUMB endptRevrs; - AT_NUMB atomNumber; - U_CHAR nValElectr; - U_CHAR nPeriodNum; - S_CHAR nMobHInChI; /* number of H on the atom in the orig. InChI */ - S_CHAR nMobHRevrs; /* number of H on the atom in InChI from the reconstructed structure */ - S_CHAR nNumHRevrs; /* number of H on the atom in the being reconstructed structure */ - S_CHAR nAtChargeRevrs; - S_CHAR nValue; /* flag(s) */ -} CMP2MHATOMS; - -typedef struct tagStructCmpTwoMobileH { - CMP2MHATOMS c2at[MAX_DIFF_FIXH]; - short len_c2at; - short nNumRemHInChI; - short nNumRemHRevrs; - short nNumTgInChI; - short nNumTgRevrs; - short nNumEndpInChI; - short nNumEndpRevrs; - short nNumTgDiffMinus; /* number of would-be-identical t-groups that have different number of (-) */ - short nNumTgDiffH; /* number of would-be-identical t-groups that have different number of H */ - - short nNumTgMInChI; /* number of (-) in orig. InChI t-groups */ - short nNumTgHInChI; /* number of H in orig. InChI t-groups */ - short nNumTgOInChI; /* number of tautomeric O,S,Se in orig. InChI t-groups */ - short nNumTgNInChI; /* number of tautomeric N in orig. InChI t-groups */ - - short nNumTgMRevrs; /* number of (-) in reversed structure t-groups */ - short nNumTgHRevrs; /* number of H in reversed structure t-groups */ - short nNumTgORevrs; /* number of tautomeric O,S,Se in reversed structure t-groups */ - short nNumTgNRevrs; /* number of tautomeric N in reversed structure t-groups */ - - short nNumTgOMinusRevrs; /* number of -O(-) on endpoints found in restored structure */ - short nNumTgOHRevrs; /* number of -OH on endpoints found in restored structure */ - short nNumTgDBORevrs; /* number of =O on endpoints found in restored structure */ - short nNumTgNMinusRevrs; /* number of -N(-)- on endpoints found in restored structure */ - short nNumTgNHMinusRevrs; /* number of -NH(-) on endpoints found in restored structure */ - short nNumTgNHRevrs; /* number of -NH- on endpoints found in restored structure */ - short nNumTgNH2Revrs; /* number of -NH2 on endpoints found in restored structure */ - short nNumTgDBNHRevrs; /* number of =NH on endpoints found in restored structure */ - short nNumTgDBNMinusRevrs; /* number of =N(-) on endpoints found in restored structure */ - short nNumTgDBNRevrs; /* number of =N- on endpoints found in restored structure */ - - short nNumTgOMinusInChI; /* number of -O(-) on endpoints according to original InChI */ - short nNumTgOHInChI; /* number of -OH on endpoints according to original InChI */ - short nNumTgDBOInChI; /* number of =O on endpoints according to original InChI */ - short nNumTgNMinusInChI; /* number of -N(-)- on endpoints according to original InChI */ - short nNumTgNHMinusInChI; /* number of -NH(-) on endpoints according to original InChI */ - short nNumTgNHInChI; /* number of -NH- on endpoints according to original InChI */ - short nNumTgNH2InChI; /* number of -NH2 on endpoints according to original InChI */ - short nNumTgDBNHInChI; /* number of =NH on endpoints according to original InChI */ - short nNumTgDBNMinusInChI; /* number of =N(-) on endpoints according to original InChI */ - short nNumTgDBNInChI; /* number of =N- on endpoints according to original InChI */ - - S_CHAR nChargeMobHInChI; - S_CHAR nChargeMobHRevrs; - S_CHAR nChargeMobHRevrsNonMetal; /* charge does not include charges on metals; later add ion pairs rejection */ - char bFixedHLayerExistsRevrs; - char bHasDifference; - U_CHAR nNumDiffMobH; -} CMP2MHINCHI; - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -int OneInChI2Atom( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, const char *szCurHdr, long num_inp, - StrFromINChI *pStruct, int iComponent, int iAtNoOffset, int bHasSomeFixedH, INChI *pInChI[]); -int get_sp_element_type( int nPeriodicNumber, int *nRow ); -int get_bonds_valences( int nPeriodicNum, int bonds_valence, int num_H, VAL_AT *pVA ); - - -/* local prototypes */ -int AddExplicitDeletedH( inp_ATOM *at, int jv, int num_at, int *iDeletedH, int *iH, int nNumDeletedH, int bTwoStereo ); -int bFindCumuleneChain( inp_ATOM *at, AT_NUMB i1, AT_NUMB i2, AT_NUMB nCumulene[], int nMaxLen ); -int set_bond_type( inp_ATOM *at, AT_NUMB i1, AT_NUMB i2, int bType ); -int set_cumulene_0D_parity( inp_ATOM *at, inp_ATOM_STEREO *st, int num_at, int idelH1, int i1, int i2, int idelH2, int parity, int len ); -int set_atom_0D_parity( inp_ATOM *at, inp_ATOM_STEREO *st, int num_at, int num_deleted_H, int i1, int parity ); -int GetTgroupInfoFromInChI( T_GROUP_INFO *ti, inp_ATOM *at, AT_NUMB *endpoint, INChI *pInChI ); -int FillOutpStructEndpointFromInChI( INChI *pInChI, AT_NUMB **pEndpoint ); -int SetStereoBondTypeFor0DParity( inp_ATOM *at, int i1, int m1 ); -int SetStereoBondTypesFrom0DStereo( StrFromINChI *pStruct, INChI *pInChI); -void CopyAt2St( inp_ATOM *at, inp_ATOM_STEREO * st, int num_atoms ); -void CopySt2At( inp_ATOM *at, inp_ATOM_STEREO * st, int num_atoms ); -int RestoreAtomConnectionsSetStereo( StrFromINChI *pStruct, int iComponent, int iAtNoOffset, INChI *pInChI, INChI *pInChIMobH); -int RestoreAtomMakeBNS( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, StrFromINChI *pStruct, int iComponent, - int iAtNoOffset, INChI *pInChI[], const char *szCurHdr, long num_inp, int bHasSomeFixedH ); -int nAddSuperCGroups( ALL_TC_GROUPS *pTCGroups ); - -int AddCGroups2TCGBnStruct( BN_STRUCT *pBNS, StrFromINChI *pStruct, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, int nMaxAddEdges ); -int AddTGroups2TCGBnStruct( BN_STRUCT *pBNS, StrFromINChI *pStruct, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, int nMaxAddEdges ); -BN_STRUCT* AllocateAndInitTCGBnStruct( StrFromINChI *pStruct, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, - int nMaxAddAtoms, int nMaxAddEdges, - int max_altp, int *pNum_changed_bonds ); -int nCountBnsSizes( inp_ATOM *at, int num_at, int nAddEdges2eachAtom, int nAddVertices, - T_GROUP_INFO *ti, VAL_AT *pVA, ICHICONST SRM *pSrm, ALL_TC_GROUPS *pTCGroups ); -int GetAtomRestoreInfo( inp_ATOM *atom, int iat, VAL_AT *pVArray, ICHICONST SRM *pSrm, int bMobileH, AT_NUMB *endpoint ); -int AddEdgeFlow( int edge_cap, int edge_flow, BNS_EDGE *e01, BNS_VERTEX *pv0 /*src*/, - BNS_VERTEX *pv1/*dest*/, int *tot_st_cap, int *tot_st_flow ); -void SetEdgeCapFlow( BNS_EDGE *e, int edge_cap, int edge_flow ); - -void AddStCapFlow( BNS_VERTEX *vert_ficpoint, int *tot_st_flow, int *tot_st_cap, int cap, int flow ); -void SetStCapFlow( BNS_VERTEX *vert_ficpoint, int *tot_st_flow, int *tot_st_cap, int cap, int flow ); - -int ConnectSuperCGroup( int nTCG_Plus, int nAddGroups[], int num_add, - int *pcur_num_vertices, int *pcur_num_edges, - int *tot_st_cap, int *tot_st_flow, - BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups ); -int ConnectTwoVertices( BNS_VERTEX *p1, BNS_VERTEX *p2, BNS_EDGE *e, BN_STRUCT *pBNS, int bClearEdge ); - -int nTautEndpointEdgeCap( inp_ATOM *at, VAL_AT *pVA, int i ); - -/* -int GetAtomBondFlow( inp_ATOM *atom, VAL_AT *pVA, ICHICONST SRM *pSrm, int iat, int ineigh ); -void GetAtomStCapFlow( inp_ATOM *atom, VAL_AT *pVA, ICHICONST SRM *pSrm, int iat, int *pCap, int *pFlow ); -int GetAtomToMCGroupInitEdgeCapFlow( EdgeFlow *nEdgeCap, EdgeFlow *nEdgeFlow, ICHICONST SRM *pSrm, inp_ATOM *at, VAL_AT *pVA, int iat ); -void GetAtomToMetalInitEdgeCapFlow( EdgeFlow *nEdgeCap, EdgeFlow *nEdgeFlow ); -*/ -int AtomStcapStflow( inp_ATOM *atom, VAL_AT *pVA, ICHICONST SRM *pSrm, int iat, int *pnStcap, int *pnStflow, - EdgeFlow *pnMGroupEdgeCap, EdgeFlow *pnMGroupEdgeFlow ); -int BondFlowMaxcapMinorder( inp_ATOM *atom, VAL_AT *pVA, ICHICONST SRM *pSrm, int iat, int ineigh, - int *pnMaxcap, int *pnMinorder, int *pbNeedsFlower ); - - - -int clean_charge_val( CHARGE_VAL *pChargeVal, int len, inp_ATOM *atom, VAL_AT *pVA, int iat, int bIsMetal, int bMobileH, AT_NUMB *endpoint ); -int ReallocTCGroups( ALL_TC_GROUPS *pTCGroups, int nAdd ); -int RegisterTCGroup( ALL_TC_GROUPS *pTCGroups, int nGroupType, int nGroupOrdNum, - int nVertexCap, int nVertexFlow, int nEdgeCap, int nEdgeFlow, int nNumEdges); -int CopyBnsToAtom( StrFromINChI *pStruct, BN_STRUCT *pBNS, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int bAllowZeroBondOrder ); -int CheckBnsConsistency( StrFromINChI *pStruct, BN_STRUCT *pBNS, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int bNoRad ); - -int RunBnsRestore1( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, INChI *pInChI[], long num_inp, int bHasSomeFixedH); -int RunBnsRestoreOnce( BN_STRUCT *pBNS, BN_DATA *pBD, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups ); -int nNumEdgesToCnVertex( MY_CONST C_NODE *pCN, int len, int v ); -int ConnectMetalFlower( int *pcur_num_vertices, int *pcur_num_edges, - int *tot_st_cap, int *tot_st_flow, ICHICONST SRM *pSrm, - BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups ); -int AddRadicalToMetal( int *tot_st_cap, int *tot_st_flow, ICHICONST SRM *pSrm, BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups ); -int bMayBeACationInMobileHLayer( inp_ATOM *at, VAL_AT *pVA, int iat, int bMobileH ); - -int MakeOneInChIOutOfStrFromINChI( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, StrFromINChI *pStruct, inp_ATOM *at2, inp_ATOM *at3, ALL_TC_GROUPS *pTCGroups ); -void IncrZeroBondsAndClearEndpts(inp_ATOM *at, int num_at, int iComponent); -void IncrZeroBonds(inp_ATOM *at, int num_at, int iComponent ); -void ClearEndpts(inp_ATOM *at, int num_at ); -int DisplayRestoredComponent( StrFromINChI *pStruct, int iComponent, int iAtNoOffset, INChI *pInChI, const char *szCurHdr ); -int cmp_charge_val( const void *a1, const void *a2 ); - -int EvaluateChargeChanges( BN_STRUCT *pBNS, VAL_AT *pVA, int *pnDeltaH, int *pnDeltaCharge, int *pnNumVisitedAtoms ); -int RunBnsTestOnce( BN_STRUCT *pBNS, BN_DATA *pBD, VAL_AT *pVA, Vertex *pvFirst, Vertex *pvLast, - int *pPathLen, int *pnDeltaH, int *pnDeltaCharge, int *pnNumVisitedAtoms ); -int comp_cc_cand( const void *a1, const void *a2 ); -int MoveRadToAtomsAddCharges( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int forbidden_mask ); -void RemoveForbiddenBondFlowBits( BN_STRUCT *pBNS, int forbidden_edge_mask_int ); -int PlusFromDB_N_DB_O_to_Metal(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int AdjustTgroupsToForbiddenEdges2( BN_STRUCT *pBNS, inp_ATOM *at, VAL_AT *pVA, int num_atoms, int forbidden_mask ); -int AllocEdgeList( EDGE_LIST *pEdges, int nLen ); -int AddToEdgeList( EDGE_LIST *pEdges, int iedge, int nAddLen ); -int RemoveFromEdgeListByIndex( EDGE_LIST *pEdges, int index ); -int RemoveFromEdgeListByValue( EDGE_LIST *pEdges, int iedge ); -int FindInEdgeList( EDGE_LIST *pEdges, int iedge ); -int AllocBfsQueue( BFS_Q *pQ, int num_at, int min_ring_size ); -void RemoveForbiddenEdgeMask( BN_STRUCT *pBNS, EDGE_LIST *pEdges, int forbidden_edge_mask ); -void SetForbiddenEdgeMask( BN_STRUCT *pBNS, EDGE_LIST *pEdges, int forbidden_edge_mask ); -int ForbidCarbonChargeEdges( BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups, EDGE_LIST *pCarbonChargeEdges, int forbidden_edge_mask ); -int ForbidMetalCarbonEdges( BN_STRUCT *pBNS, inp_ATOM *at, int num_at, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, EDGE_LIST *pMetalCarbonEdges, int forbidden_edge_mask ); -int ForbidNintrogenPlus2BondsInSmallRings( BN_STRUCT *pBNS, inp_ATOM *at, int num_at, - VAL_AT *pVA, int min_ring_size, ALL_TC_GROUPS *pTCGroups, - EDGE_LIST *pNplus2BondsEdges, int forbidden_edge_mask ); -int RearrangePlusMinusEdgesFlow( BN_STRUCT *pBNS, BN_DATA *pBD, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, int forbidden_edge_mask ); -int IncrementZeroOrderBondsToHeteroat( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, - VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, - int forbidden_edge_mask); -int MoveChargeFromHeteroatomsToMetals( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, - VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, - int forbidden_edge_mask); -int EliminateChargeSeparationOnHeteroatoms( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, - VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, - int forbidden_edge_mask, int forbidden_stereo_edge_mask); -int MovePlusFromS2DiaminoCarbon( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, - VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int RestoreCyanoGroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int RestoreIsoCyanoGroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int RestoreNNNgroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int FixMetal_Nminus_Ominus( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int EliminateNitrogen5Val3Bonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int Convert_SIV_to_SVI(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int MoveMobileHToAvoidFixedBonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int RemoveRadFromMobileHEndpoint(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int RemoveRadFromMobileHEndpointFixH(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int MoveChargeToMakeCenerpoints(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int CheckAndRefixStereobonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int MoveChargeToRemoveCenerpoints(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int MakeSingleBondsMetal2ChargedHeteroat(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int SaltBondsToCoordBonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); -int FixLessHydrogenInFormula( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, - inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ); -int FixMoreHydrogenInFormula( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, - inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ); -int FixAddProtonForADP( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, - inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, ICR *picr, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ); -int ConnectDisconnectedH( inp_ATOM *at, int num_atoms, int num_deleted_H ); -int DisconnectedConnectedH( inp_ATOM *at, int num_atoms, int num_deleted_H ); -int MakeInChIOutOfStrFromINChI2( ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd_inp, StrFromINChI *pStruct, - int iComponent, int iAtNoOffset, long num_inp ); -int GetChargeFlowerUpperEdge( BN_STRUCT *pBNS, VAL_AT *pVA, int nChargeEdge ); -int get_pVA_atom_type( VAL_AT *pVA, inp_ATOM *at, int iat, int bond_type ); - -int NormalizeAndCompare(ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, - StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, INChI *pInChI[], long num_inp, int bHasSomeFixedH, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask); -/* call InChI normalization only */ -int NormalizeStructure( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, - StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, - VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - inp_ATOM *at_norm, inp_ATOM *at_fixed_bonds_out, T_GROUP_INFO *t_group_info ); -/* create one InChI */ -int MakeOneInChIOutOfStrFromINChI2( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, StrFromINChI *pStruct, - inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, - T_GROUP_INFO **t_group_info, inp_ATOM **at_norm, inp_ATOM **at_prep ); -/* fixed-H */ -int FillOutExtraFixedHDataRestr( StrFromINChI *pStruct ); -int FillOutExtraFixedHDataInChI( StrFromINChI *pStruct, INChI *pInChI[] ); -int FixFixedHRestoredStructure(ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, - StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, T_GROUP_INFO **ti, inp_ATOM **at_norm, inp_ATOM **at_prep, - INChI *pInChI[], long num_inp, int bHasSomeFixedH, int *pnNumRunBNS, int *pnTotalDelta, - int forbidden_edge_mask, int forbidden_stereo_edge_mask); -int FixRemoveExtraTautEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, - inp_ATOM *at2, inp_ATOM *atf, inp_ATOM *atn, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, ICR *picr, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ); -int FillOutCMP2FHINCHI( StrFromINChI *pStruct, inp_ATOM *at2, VAL_AT *pVA, INChI *pInChI[], CMP2FHINCHI *pc2i ); -int FillOutCMP2MHINCHI( StrFromINChI *pStruct, ALL_TC_GROUPS *pTCGroups, inp_ATOM *at2, - VAL_AT *pVA, INChI *pInChI[], CMP2MHINCHI *pc2i ); - -int bHas_N_V( inp_ATOM *at2, int num_atoms ); - -int GetPlusMinusVertex( BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups, int bCheckForbiddenPlus, int bCheckForbiddenMinus ); -int FixMobileHRestoredStructure(ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, - StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, T_GROUP_INFO **ppt_group_info, inp_ATOM **ppat_norm, - inp_ATOM **ppat_prep, INChI *pInChI[], long num_inp, int bHasSomeFixedH, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask); - -int FixRestoredStructureStereo( INCHI_MODE cmpInChI, ICR *icr, INCHI_MODE cmpInChI2, ICR *icr2, - ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, - StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, - ALL_TC_GROUPS *pTCGroups, T_GROUP_INFO **ppt_group_info, inp_ATOM **ppat_norm, - inp_ATOM **ppat_prep, INChI *pInChI[], long num_inp, - int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask); - -int AddRemProtonsInRestrStruct( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, - int bHasSomeFixedH, - StrFromINChI *pStruct, int num_components, - StrFromINChI *pStructR, int num_componentsR, - NUM_H *pProtonBalance, int *recmet_change_balance ); -int AllInchiToStructure( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, char *szCurHdr, - ICHICONST SRM *pSrm, int bReqNonTaut, StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], - InpInChI *pOneInput ); -int AddProtonAndIsoHBalanceToMobHStruct( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, - long num_inp, int bHasSomeFixedH, char *szCurHdr, - StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], InpInChI *pOneInput); -int InChI2Atom( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, const char *szCurHdr, long num_inp, - StrFromINChI *pStruct, int iComponent, int iAtNoOffset, int bI2A_Flag, int bHasSomeFixedH, InpInChI *pOneInput); - -int MarkDisconectedIdenticalToReconnected ( InpInChI *pOneInput ); -void RemoveFixHInChIIdentical2MobH( InpInChI *pOneInput ); -void SetUpSrm( SRM *pSrm ); -void FreeInpInChI( InpInChI *pOneInput ); -void FreeStrFromINChI( StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], int nNumComponents[INCHI_NUM][TAUT_NUM] ); -int OldPrintCompareOneOrigInchiToRevInChI(StrFromINChI *pStruct, INChI *pInChI[TAUT_NUM], int bMobileH, - int iComponent, long num_inp, char *szCurHdr); -int CompareOneOrigInchiToRevInChI(StrFromINChI *pStruct, INChI *pInChI[TAUT_NUM], int bMobileH, int iComponent, - long num_inp, char *szCurHdr, - COMPONENT_REM_PROTONS *nCurRemovedProtons, INCHI_MODE CompareInchiFlags[]); -int CompareTwoPairsOfInChI( INChI *pInChI1[TAUT_NUM], INChI *pInChI2[TAUT_NUM], - int bMobileH, INCHI_MODE CompareInchiFlags[] ); -INCHI_MODE CompareReversedINChI3( INChI *i1 /* InChI from reversed struct */, INChI *i2 /* input InChI */, - INChI_Aux *a1, INChI_Aux *a2, int *err ); -INCHI_MODE CompareReversedStereoINChI3( INChI_Stereo *s1/* InChI from reversed struct */, INChI_Stereo *s2 /* input InChI */, ICR *picr); -int CompareAllOrigInchiToRevInChI(StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], InpInChI *pOneInput, int bReqNonTaut, - long num_inp, char *szCurHdr); -int CompareAllDisconnectedOrigInchiToRevInChI(StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], - InpInChI *pOneInput, int bHasSomeFixedH, - long num_inp, char *szCurHdr); -int insertions_sort_AT_NUMB( AT_NUMB *base, int num ); - -int AddRemIsoProtonsInRestrStruct( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, int bHasSomeFixedH, - StrFromINChI *pStruct, int num_components, - StrFromINChI *pStructR, int num_componentsR, - NUM_H pProtonBalance[], NUM_H recmet_change_balance[] ); -int OutputInChIOutOfStrFromINChI(ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd_inp, - long num_inp, int bINChIOutputOptions, - INCHI_IOSTREAM *pout, INCHI_IOSTREAM *plog, - InpInChI *pOneInput, int bHasSomeFixedH, - unsigned char save_opt_bits); - -int MergeStructureComponents( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, char *szCurHdr, - ICHICONST SRM *pSrm, int bReqNonTaut, StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], - InpInChI *pOneInput ); -int AddOneMsg( char *szMsg, int used_len, int tot_len, const char *szAddMsg, const char *szDelim ); -int FillOutCompareMessage( char *szMsg, int nLenMsg, INCHI_MODE bits[] ); -void clear_t_group_info( T_GROUP_INFO *ti ); -int bInpInchiComponentExists( InpInChI *pOneInput, int iINCHI, int bMobileH, int k ); -int bInpInchiComponentDeleted( InpInChI *pOneInput, int iInChI, int bMobileH, int k ); -int bRevInchiComponentExists( StrFromINChI *pStruct, int iInChI, int bMobileH, int k ); -int bRevInchiComponentDeleted( StrFromINChI *pStruct, int iInChI, int bMobileH, int k ); -int DetectInpInchiCreationOptions ( InpInChI *pOneInput, int *bHasReconnected, int *bHasMetal, - int *bHasFixedH, int *sFlag, int *bTautFlag ); -int DisplayStructureComponents( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, char *szCurHdr, - ICHICONST SRM *pSrm, int bReqNonTaut, StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], - InpInChI *pOneInput ); -int DisplayOneRestoredComponent( StrFromINChI *pStruct, inp_ATOM *at, - int iComponent, int nNumComponents, int bMobileH, - const char *szCurHdr ); -int DisplayAllRestoredComponents( inp_ATOM *at, int num_at, const char *szCurHdr ); -int CountStereoTypes( INChI *pInChI, int *num_known_SB, int *num_known_SC, - int *num_unk_und_SB, int *num_unk_und_SC, - int *num_SC_PIII, int *num_SC_AsIII); -int GetNumNeighborsFromInchi( INChI *pInChI, AT_NUMB nAtNumber ); -int bIsUnsatCarbonInASmallRing( inp_ATOM *at, VAL_AT *pVA, int iat, BFS_Q *pbfsq, int min_ring_size ); - -int MakeProtonComponent( StrFromINChI *pStruct, int iComponent, int num_prot ); - -/* extra configurarion */ -#define KEEP_METAL_EDGE_FLOW 0 /* counterexample: mdb0-1738.sdf.txt */ -#define MOVE_CHARGES_FROM_HETEREO_TO_METAL 0 /* disabled */ -#define FIX_ADD_PROTON_FOR_ADP 0 /* not used */ - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _ICHIRVRS_H_ +#define _ICHIRVRS_H_ + +#include "ichimain.h" +#include "ichiring.h" + +#define ICHICONST const + +#define RI_ERR_ALLOC (-1) +#define RI_ERR_SYNTAX (-2) +#define RI_ERR_PROGR (-3) +#define RI_ERR_EOL (-4) +#define RI_ERR_EOF (0) + +#define RI_ERR_MISMATCH (-9) + +#define NO_VALUE_INT 9999 +#define NOT_READ_INT 9998 /* has not been read yet */ + +#define VALUE_OCTET 8 /* number of electrons in a full shell */ + +#define INC_EDGE_LIST_DEFAULT 64 + +typedef struct tagXYZCoord { + double xyz[3]; +} XYZ_COORD; + +typedef struct tagStructRestoreMode { + int bMetalAddFlower; /* 1 => allow adjustable metal valence and charge; 0=> use std charge/valence */ + /* the following three apply only if bMetalAddFlower = 1 */ + int nMetalMinBondOrder; /* edge_flow=f means bond order=cMetalMinBondOrder+f */ + int nMetalInitEdgeFlow; /* one bond contribution to metal's (st-cap - st-flow) = */ + /* (nMetalInitBondOrder-nMetalMinBondOrder) - nMetalInitEdgeFlow */ + int nMetalInitBondOrder; /* >= nMetalMinBondOrder + nMetalInitEdgeFlow */ + /* same for metal-endpoint bonds */ + int nMetal2EndpointMinBondOrder; + int nMetal2EndpointInitBondOrder; + int nMetal2EndpointInitEdgeFlow; + int nMetalFlowerParam_D; /* additional edge capacity for canceling radicals */ + int nMetalMaxCharge_D; /* cap and/or flow for metal charge group */ + int bStereoRemovesMetalFlag; /* 1=> treat stereogenic atoms and atoms connected by a stereo bond as non-metals */ + + int bFixStereoBonds; /* 1=> forbid stereogenic double bonds from changing */ +} SRM; + +typedef struct tagReversedInChI { + PINChI2 *pINChI[INCHI_NUM]; + PINChI_Aux2 *pINChI_Aux[INCHI_NUM]; + int num_components[INCHI_NUM]; + int nRetVal; +} REV_INCHI; + +/**************************************/ +#define BFS_Q_CLEAR (-1) +#define BFS_Q_FREE (-2) +typedef struct tagBfsQueue { + QUEUE *q; + AT_RANK *nAtomLevel; + S_CHAR *cSource; + int num_at; + AT_RANK min_ring_size; /* 8 => detect 7-member and smaller rings */ +} BFS_Q; +/**************************************/ + +#define EXTRACT_STRUCT_NUMBER 1 + +/* additional Mobile-H parities to be added to Fixed-H parities */ +/* This allows to set parities that exist in Mobile-H layer only */ +typedef struct tagInpAtomAddParities { + /* cml 0D parities */ + S_CHAR bUsed0DParity; /* bit=1 => stereobond; bit=2 => stereocenter */ + /* cml tetrahedral parity */ + S_CHAR p_parity; + AT_NUMB p_orig_at_num[MAX_NUM_STEREO_ATOM_NEIGH]; + /* cml bond parities */ + S_CHAR sb_ord[MAX_NUM_STEREO_BONDS]; /* stereo bond/neighbor ordering number, starts from 0 */ + /* neighbors on both sides of stereobond have same sign=> trans/T/E, diff. signs => cis/C/Z */ + S_CHAR sn_ord[MAX_NUM_STEREO_BONDS]; /* ord. num. of the neighbor adjacent to the SB; starts from 0; + -1 means removed explicit H */ + /* neighbors on both sides of stereobond have same parity => trans/T/E/2, diff. parities => cis/C/Z/1 */ + S_CHAR sb_parity[MAX_NUM_STEREO_BONDS]; + AT_NUMB sn_orig_at_num[MAX_NUM_STEREO_BONDS]; /* orig. at number of sn_ord[] neighbors */ +} inp_ATOM_STEREO; + +#define FIX_STEREO_BOND_ORDER 0 /* 1=> fix stereobonds; treat metal as non-metal if it is stereogenic or has a stereobond */ + +#define METAL_FREE_CHARGE_VAL 1 /* 1=> allow free changing charges/valences of metals; initial bond order=0 or 1 */ +#define ALLOW_METAL_BOND_ZERO 1 /* 1=> allow zero flow (bobd order) to metals */ + +#if ( ALLOW_METAL_BOND_ZERO == 1 ) +/* INIT_METAL_BOND_ZERO=1 => INIT_METAL_BOND_FLOW=0 */ +#define INIT_METAL_BOND_ZERO 0 /* 1=> initialize zero order bond to metals */ +#define INIT_METAL_BOND_FLOW 1 /* 1=> init flow=1, 0 => init. flow = 0 */ +#else +#define INIT_METAL_BOND_ZERO 0 +#define INIT_METAL_BOND_FLOW 0 /* always 0 */ +#endif + +#define I2A_FLAG_FIXEDH 0x0001 +#define I2A_FLAG_RECMET 0x0002 + +#define EL_NUMBER_H 1 + + +#define ATYPE_H 1 +#define ATYPE_Na 2 +#define ATYPE_Mg 3 +#define ATYPE_B 4 +#define ATYPE_C 5 +#define ATYPE_N 6 +#define ATYPE_O 7 +#define ATYPE_Cl 8 + + +/* first bonds valence for charge = c is cValence[0][c+VAL_BASE]; VAL_MIN_CHARGE <= c <= VAL_MAX_CHARGE */ +/* second bonds valence for charge = c is cValence[1][c+VAL_BASE]; VAL_MIN_CHARGE <= c <= VAL_MAX_CHARGE */ +/* total number of valences is 2 = VAL_NUMBER */ +/* neutral bond valence orders are cValence[0][VAL_NEUTR_ORDER], cValence[1][VAL_NEUTR_ORDER] */ +#define VAL_BASE ( 1) +#define VAL_MIN_CHARGE (-1) +#define VAL_MAX_CHARGE ( 1) +#define VAL_NUMBER ( 2) +#define VAL_NEUTR_ORDER (VAL_MAX_CHARGE-VAL_MIN_CHARGE+1) +#define VAL_LENGTH (VAL_NEUTR_ORDER+1) +#define VAL_NEGAT_CHARGE 0 +#define VAL_NEUTR_CHARGE 1 +#define VAL_POSIT_CHARGE 2 + +typedef struct tagAtomIonPrperies { + /* char cValence[VAL_NUMBER][VAL_LENGTH]; */ /* ordering numbers of minimal valence, 0-based */ + char cDoNotAddH; /* InChI does not add H to this element */ + char cMetal; /* the element is a metal */ + char cNumBondsToMetal; /* number of bonds to metal */ + char cInitFlowToMetal; /* sum of init flow to metal atoms */ + char cInitValenceToMetal; /* sum of init adjusted bond orders to metal atoms */ + char cInitOrigValenceToMetal; /* sum of init bond orders to metal atoms */ + char cMaxFlowToMetal; /* max total edge flow to metal atoms */ + char cInitFreeValences; /* number of 'dots' to connect; charges are marked separately */ + S_CHAR cInitCharge; /* initial charge on the atom (not included in ChargeStruct */ + char cNumValenceElectrons; + char cPeriodicRowNumber; + char cMinRingSize; /* min ring size for atoms that have 2 bonds only */ + U_CHAR cPeriodicNumber; /* number in Periodic Table of elements */ + S_CHAR cnListIndex; /* (index in the cnList) + 1; 0 => none */ + int nCMinusGroupEdge; /* (index of the edge to the atom's (-) group) + 1 */ + int nCPlusGroupEdge; /* (index of the edge to the atom's (+) group) + 1 */ + int nMetalGroupEdge; /* index of the edge to the atom's M-group + 1 */ + int nTautGroupEdge; /* index of the edge from the atom to the t-group + 1 */ +} VAL_AT; + + +/****************************************************************************/ +#define INI_NUM_TCGROUPS 16 +#define INC_NUM_TCGROUPS 16 + +typedef enum tagTgRestoreFlags { + TGRF_MINUS_FIRST = 1 +} TGRF; +typedef struct tagTCGroup { + int type; /* group type */ + int ord_num; /* ordering number within the type, typically t-group number */ + int num_edges; + /* charge group specific */ + int st_cap; + int st_flow; + int edges_cap; + int edges_flow; + int nVertexNumber; /* group vertex number; 0 = unassigned */ + int nForwardEdge; /* edge index: from c-group to central Y-connecting vertex + or from supergroup to (+/-) vertex; 0 => unassigned */ + int nBackwardEdge; /* edge index: from central Y-connecting vertex + to supergroup; 0 => unassigned */ + /* tautomeric group specific */ + short tg_num_H; /* number of H in a tautomeric group */ + short tg_num_Minus; /* negative charge on t-group */ + Vertex tg_set_Minus; /* the vertex+1 that has to have (-) */ + short tg_RestoreFlags; /* Set (-) to first memberst of a t-group (usually, N) */ +} TC_GROUP; + +typedef enum tagTCGroupTypes { + TCG_None = -1, /* so far only ord=0 is used */ + /* group type ord */ + TCG_Plus0 = 0, /* BNS_VT_C_POS 0 */ + TCG_Plus1, /* BNS_VT_C_POS 1 */ + TCG_Minus0, /* BNS_VT_C_NEG 0 */ + TCG_Minus1, /* BNS_VT_C_NEG 1 */ + TCG_Plus_C0, /* BNS_VT_C_POS_C 0 */ + TCG_Plus_C1, /* BNS_VT_C_POS_C 1 */ + TCG_Minus_C0, /* BNS_VT_C_NEG_C 0 */ + TCG_Minus_C1, /* BNS_VT_C_NEG_C 1 */ + TCG_Plus_M0, /* BNS_VT_C_POS_M 0 */ + TCG_Plus_M1, /* BNS_VT_C_POS_M 1 */ + TCG_Minus_M0, /* BNS_VT_C_NEG_M 0 */ + TCG_Minus_M1, /* BNS_VT_C_NEG_M 1 */ + TCG_MeFlower0, /* BNS_VT_M_GROUP 0 */ /* base */ + TCG_MeFlower1, /* BNS_VT_M_GROUP 1 */ + TCG_MeFlower2, /* BNS_VT_M_GROUP 2 */ + TCG_MeFlower3, /* BNS_VT_M_GROUP 3 */ + + TCG_Plus, /* BNS_VT_C_POS_ALL 0 */ + TCG_Minus, /* BNS_VT_C_NEG_ALL 0 */ + + NUM_TCGROUP_TYPES /* number of group types */ +}TCGR_TYPE; + +typedef struct tagAllTCGroups { + TC_GROUP *pTCG; + int num_tc_groups; /* number of charge groups and metal-flower vertices */ + int max_tc_groups; /* number of allocated of pTCG[] elements */ + int nGroup[NUM_TCGROUP_TYPES]; /* tagTCGroupTypes */ + int nVertices; /* total number of vertices */ + int nEdges; /* total number of edges */ + int nAddIedges; /* additional increase of number of iedges for edge switching to another group */ + int num_atoms; /* number of atoms */ + int num_bonds; /* number of bonds */ + int num_tgroups; /* number t-groups */ + int num_tgroup_edges; /* number of edges to t-groups */ + /* charges */ + int tgroup_charge; /* total charge of all t-groups */ + int charge_on_atoms; /* charge permanently sitting on atoms */ + int added_charge; /* charge added to the c-groups */ + int total_charge; /* total charge of the component */ + int total_electrons; /* total number of electrons on all atoms */ + int total_electrons_metals; /* total number of electrons on unbonded metals */ + + int num_metal_atoms; /* number of metal atoms */ + int num_metal_bonds; /* number of atom-metal bonds */ + /* excess_charge = total_charge - added_charge - tgroup_charge: add to metals etc. */ + + int nEdge4charge; /* edge used to add charges; neighbor1=supercharge, another = (+/-) vertex */ + int nEdgePlus; /* edge to (+) supergroup; 0 means none */ + int nEdgeMinus; /* edge to (-) supergroup; 0 means none */ + int iComponent; /* component number */ + int iAtNoOffset; /* first atom number -- always 0 for now */ +} ALL_TC_GROUPS; + +/**************************************/ +#define EDGE_LIST_CLEAR (-1) +#define EDGE_LIST_FREE (-2) + +typedef struct tagEdgeList { + int num_alloc; + int num_edges; + EdgeIndex *pnEdges; +} EDGE_LIST; +/**************************************/ + +#define BOND_MARK_STEREO 0x10 +#define BOND_TYPE_STEREO (BOND_TYPE_SINGLE | BOND_MARK_STEREO) + +/* local */ +#define RESET_EDGE_FORBIDDEN_MASK 0 + +#define TREAT_ATOM_AS_METAL 99 + + +/************************************************************************************/ +typedef struct tagChargeValence { + int nValence; + int nCharge; + int nValenceOrderingNumber; +} CHARGE_VAL; + +#define MY_CONST const +/*************************************************************************************/ +typedef struct tagChargeChangeCandidate { + Vertex iat; + char num_bonds; + char chem_valence; + char cMetal; + char cNumBondsToMetal; + char cNumValenceElectrons; + char cPeriodicRowNumber; + char cNumChargeStates; + U_CHAR el_number; +} CC_CAND; + +typedef struct tagOneComponentRemovedAndExchangeableH { + NUM_H nNumRemovedProtons; + NUM_H nNumRemovedIsotopicH[NUM_H_ISOTOPES]; /* isotopic H that may be exchanged and considered + randomly distributed, including removed protons */ +} COMPONENT_REM_PROTONS; + +typedef struct tagRemovedAndExchangeableH { + /* totals for Mobile-H layer */ + NUM_H nNumRemovedProtons; + NUM_H nNumRemovedIsotopicH[NUM_H_ISOTOPES]; /* isotopic H that may be exchanged and considered + randomly distributed, including removed protons */ + /* for individual components from comparing Fixed-H vs Mobile-H formulas; NULL if not available */ + COMPONENT_REM_PROTONS *pNumProtons; +} REM_PROTONS; + +typedef struct tagInputInChI { + INChI *pInpInChI[INCHI_NUM][TAUT_NUM]; + int nNumComponents[INCHI_NUM][TAUT_NUM]; + REM_PROTONS nNumProtons[INCHI_NUM][TAUT_NUM]; + int s[INCHI_NUM][TAUT_NUM][2]; + /* s[0=non-iso, 1=iso] = 0,1,2,3 <= regular /s; -1=> "/s" (empty) */ + long num_inp; + inp_ATOM *atom; /* the whole restored structure made out of all components */ + int num_atoms; /* number of atoms including explicit H */ + int num_explicit_H; /* number of explicit H in the atom */ + INCHI_MODE CompareInchiFlags[INCHI_NUM][TAUT_NUM]; + /* v. 1.05 extensions */ + OrigAtDataPolymer *polymer; + OrigAtDataV3000 *v3000; +} InpInChI; + +typedef struct tagStructFromInChI { + /* InChI component -> Structure result */ + inp_ATOM *at; /* length = num_atoms + num_deleted_H, zero pint struct for BNS->struct conversion */ + inp_ATOM_STEREO *st; /* additional stereo that exists only in Mobile-H layer */ + inp_ATOM *at2; /* length = num_atoms + num_deleted_H, the conversion result */ + + /* information from InChI only */ + T_GROUP_INFO ti; /* from original InChI[0] if Mobile-H from the beginning or later from InChI[1] if Fixed-H */ + AT_NUMB *endpoint; /* from original InChI[1] in case of Fixed-H only */ + S_CHAR *fixed_H; /* from original InChI[0] in case of Fixed-H only */ + XYZ_COORD *pXYZ; + int num_atoms; + int num_deleted_H; /* if requested and Fixed-H InChI is available */ + int nNumRemovedProtonsMobHInChI; /* number of protons removed from Mobile-H struct in original InChI */ + S_CHAR charge; + char bIsotopic; + + /* InChI -> Structure conversion parms and intermediate data */ + BN_STRUCT *pBNS; + BN_DATA *pBD; + ICHICONST SRM *pSrm; + + /* InChI layer to reverse */ + char bMobileH; + char iINCHI; + char bFixedHExists; /* fixed-H InChI exists or not */ + + /* InChI -> Struct component -> Full InChI result (both disconnected and connected if exist) */ + REV_INCHI RevInChI; + int nRemovedProtonsByNormFromRevrs; /* number of H(+) removed by normalization after + Struct Restore and before Add/Remove Protons */ + int nNumRemovedProtonsByRevrs; /* number of H(+) removed by the reconstruction, + before Add/Remove Protons, only from TAUT_YES */ + + int bExtract; /* for debugging */ + + /* single component InChI calculation */ + INChI *pOneINChI[TAUT_NUM]; /* InChI of restored structure */ + INChI_Aux *pOneINChI_Aux[TAUT_NUM]; + INP_ATOM_DATA *pOne_norm_data[TAUT_NUM]; /* normalized restored structure */ + S_CHAR *pOne_fixed_H; /* !!! from normalized restored structure in case of Fixed-H only */ + T_GROUP_INFO One_ti; /* t-groups of normalized canonicalized restored structure */ + int nOneINChI_bMobileH; /* type of restored structure InChI */ + int nNumRemovedProtons; /* =0 for Fixed-H, = num. removed protons in case of Mobile-H InChI */ + /* in case of Fixed-H processing see pStruct->One_ti.tni.nNumRemovedProtons */ + AT_NUMB *nAtno2Canon[TAUT_NUM]; /* nAtno2Canon[restored_at_no][*] = (atom canon number in restored struct)-1*/ + AT_NUMB *nCanon2Atno[TAUT_NUM]; /* nCanon2Atno[(atom canon number in restored struct)-1][*] = restored_at_no; */ + + int nError; + /* other parms */ + char iInchiRec; /* index in the original InChI array */ + char iMobileH; /* index in the original InChI array */ + char bDeleted; /* InChI component marked as Deleted, means a proton in Mobile-H layer */ + /* struct. ordering number "Structure: nnn" if present */ + long num_inp_actual; + + /* utility data */ + BFS_Q *pbfsq; + VAL_AT *pVA; + + int nLink; /* same as in INChI */ + int bPostProcessed; /* recalculate after add/remove protons */ + + /* TAUT_YES layer charges */ + int nChargeRevrs; /* component charge of the reconstructed structure, TAUT_YES layer */ + int nChargeInChI; /* component charge from the original InChI, TAUT_YES layer */ +} StrFromINChI; + + +#define EL_TYPE_O 0x0001 +#define EL_TYPE_S 0x0002 +#define EL_TYPE_N 0x0004 +#define EL_TYPE_P 0x0008 +#define EL_TYPE_C 0x0010 +#define EL_TYPE_X 0x0020 /* any not metal */ +#define EL_TYPE_MASK 0x003f +#define EL_TYPE_OSt 0x0100 /* terminal -OH, -O(-), -SH, -S(-), ... from fix_special_bonds(...) */ +#define EL_TYPE_PT 0x0200 /* may be a tautomeric endpoint */ + +/* the atom to which the node is attached has number 1; added atoms have numbers 2,3,... */ +#define MAX_CN_VAL 3 +typedef struct tagVertCapFlow { + S_SHORT type; + S_CHAR cap; + S_CHAR flow; + S_CHAR valence; +} VCF; +typedef struct tagEdgeCapFlow { + S_SHORT neigh; + S_CHAR cap; + S_CHAR bForbiddenEdge; + S_CHAR flow; +} ECF; +typedef struct tagChargeNodes { + VCF v; + ECF e[MAX_CN_VAL]; +} C_NODE; + + +#define cn_bits_N 1 /* Neutral: charge = 0 */ +#define cn_bits_P 2 /* Plus 1: charge = +1 */ +#define cn_bits_M 4 /* Minus 1: charge = -1 */ +#define cn_bits_shift 3 +#define MAX_NUM_CN_BITS 4 +#define MAKE_CN_BITS(A, B, C, D ) (( (( ((D) << cn_bits_shift | (C)) << cn_bits_shift ) | (B)) << cn_bits_shift ) | (A)) + +#define cn_bits_PNPN MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_P, cn_bits_N) +#define cn_bits_NPNP MAKE_CN_BITS(cn_bits_N, cn_bits_P, cn_bits_N, cn_bits_P) +#define cn_bits_NPN MAKE_CN_BITS(cn_bits_N, cn_bits_P, cn_bits_N, 0) +#define cn_bits_PNP MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_P, 0) +#define cn_bits_MNP MAKE_CN_BITS(cn_bits_M, cn_bits_N, cn_bits_P, 0) +#define cn_bits_PNM MAKE_CN_BITS(cn_bits_P, cn_bits_N, cn_bits_M, 0) +#define cn_bits_EN MAKE_CN_BITS(cn_bits_P | cn_bits_M, cn_bits_N, 0, 0) +#define cn_bits_NMN MAKE_CN_BITS(cn_bits_N, cn_bits_M, cn_bits_N, 0) +#define cn_bits_NE MAKE_CN_BITS(cn_bits_N, cn_bits_P | cn_bits_M, 0, 0) +#define cn_bits_NEN MAKE_CN_BITS(cn_bits_N, cn_bits_M | cn_bits_N, cn_bits_N, 0) +#define cn_bits_NP MAKE_CN_BITS(cn_bits_N, cn_bits_P, 0, 0) +#define cn_bits_PN MAKE_CN_BITS(cn_bits_P, cn_bits_N, 0, 0) +#define cn_bits_NM MAKE_CN_BITS(cn_bits_N, cn_bits_M, 0, 0) +#define cn_bits_MN MAKE_CN_BITS(cn_bits_M, cn_bits_N, 0, 0) +#define cn_bits_P_ MAKE_CN_BITS(cn_bits_P, 0, 0, 0) +#define cn_bits_M_ MAKE_CN_BITS(cn_bits_M, 0, 0, 0) +#define cn_bits_N_ MAKE_CN_BITS(cn_bits_N, 0, 0, 0) +#define cn_bits_Me (-1) + +#define cnListIndexMe (17) /* index of {cnMe, cn_bits_Me,... } element of cnList[] */ + +extern const int cnListNumEl; /* number of elements in cnList[] */ + +typedef struct tagChargeNodeList { + MY_CONST C_NODE *pCN; + int bits; + int nInitialCharge; + int len; +} CN_LIST; + +extern MY_CONST CN_LIST cnList[]; + +/************************ fixed H comparison ******************************************************/ +#define MAX_DIFF_FIXH 256 +#define MAX_DIFF_MOBH 256 +typedef struct tagAtomsCmpTwoFixedH { + AT_NUMB endptInChI; + AT_NUMB endptRevrs; + AT_NUMB atomNumber; + U_CHAR nValElectr; + U_CHAR nPeriodNum; + S_CHAR nFixHInChI; + S_CHAR nFixHRevrs; + S_CHAR nMobHInChI; + S_CHAR nMobHRevrs; + S_CHAR nNumHRevrs; + S_CHAR nAtChargeRevrs; + S_CHAR nValue; /* flag(s) */ +} CMP2FHATOMS; + +typedef struct tagStructCmpTwoFixedH { + CMP2FHATOMS c2at[MAX_DIFF_FIXH]; + short len_c2at; + short nNumRemHInChI; + short nNumRemHRevrs; + short nNumTgInChI; + short nNumTgRevrs; + short nNumEndpInChI; + short nNumEndpRevrs; + short nNumTgDiffMinus; /* number of would-be-identical t-groups that have different number of (-) */ + short nNumTgDiffH; /* number of would-be-identical t-groups that have different number of H */ + short nNumTgMInChI; /* number of (-) in orig. InChI t-groups */ + short nNumTgHInChI; /* number of H in orig. InChI t-groups */ + short nNumTgMRevrs; /* number of (-) in reversed structure t-groups */ + short nNumTgHRevrs; /* number of H in reversed structure t-groups */ + S_CHAR nChargeFixHInChI; + S_CHAR nChargeMobHInChI; + S_CHAR nChargeFixHRevrs; + S_CHAR nChargeMobHRevrs; + S_CHAR nChargeFixHRevrsNonMetal; /* charge does not include charges on metals */ + S_CHAR nChargeMobHRevrsNonMetal; /* charge does not include charges on metals */ + char bFixedHLayerExistsRevrs; + char bHasDifference; + U_CHAR nNumDiffMobH; +} CMP2FHINCHI; + +/************************ Mobile H comparison *********************************************/ +typedef struct tagAtomsCmpTwoMobileH { + AT_NUMB endptInChI; + AT_NUMB endptRevrs; + AT_NUMB atomNumber; + U_CHAR nValElectr; + U_CHAR nPeriodNum; + S_CHAR nMobHInChI; /* number of H on the atom in the orig. InChI */ + S_CHAR nMobHRevrs; /* number of H on the atom in InChI from the reconstructed structure */ + S_CHAR nNumHRevrs; /* number of H on the atom in the being reconstructed structure */ + S_CHAR nAtChargeRevrs; + S_CHAR nValue; /* flag(s) */ +} CMP2MHATOMS; + +typedef struct tagStructCmpTwoMobileH { + CMP2MHATOMS c2at[MAX_DIFF_FIXH]; + short len_c2at; + short nNumRemHInChI; + short nNumRemHRevrs; + short nNumTgInChI; + short nNumTgRevrs; + short nNumEndpInChI; + short nNumEndpRevrs; + short nNumTgDiffMinus; /* number of would-be-identical t-groups that have different number of (-) */ + short nNumTgDiffH; /* number of would-be-identical t-groups that have different number of H */ + + short nNumTgMInChI; /* number of (-) in orig. InChI t-groups */ + short nNumTgHInChI; /* number of H in orig. InChI t-groups */ + short nNumTgOInChI; /* number of tautomeric O,S,Se in orig. InChI t-groups */ + short nNumTgNInChI; /* number of tautomeric N in orig. InChI t-groups */ + + short nNumTgMRevrs; /* number of (-) in reversed structure t-groups */ + short nNumTgHRevrs; /* number of H in reversed structure t-groups */ + short nNumTgORevrs; /* number of tautomeric O,S,Se in reversed structure t-groups */ + short nNumTgNRevrs; /* number of tautomeric N in reversed structure t-groups */ + + short nNumTgOMinusRevrs; /* number of -O(-) on endpoints found in restored structure */ + short nNumTgOHRevrs; /* number of -OH on endpoints found in restored structure */ + short nNumTgDBORevrs; /* number of =O on endpoints found in restored structure */ + short nNumTgNMinusRevrs; /* number of -N(-)- on endpoints found in restored structure */ + short nNumTgNHMinusRevrs; /* number of -NH(-) on endpoints found in restored structure */ + short nNumTgNHRevrs; /* number of -NH- on endpoints found in restored structure */ + short nNumTgNH2Revrs; /* number of -NH2 on endpoints found in restored structure */ + short nNumTgDBNHRevrs; /* number of =NH on endpoints found in restored structure */ + short nNumTgDBNMinusRevrs; /* number of =N(-) on endpoints found in restored structure */ + short nNumTgDBNRevrs; /* number of =N- on endpoints found in restored structure */ + + short nNumTgOMinusInChI; /* number of -O(-) on endpoints according to original InChI */ + short nNumTgOHInChI; /* number of -OH on endpoints according to original InChI */ + short nNumTgDBOInChI; /* number of =O on endpoints according to original InChI */ + short nNumTgNMinusInChI; /* number of -N(-)- on endpoints according to original InChI */ + short nNumTgNHMinusInChI; /* number of -NH(-) on endpoints according to original InChI */ + short nNumTgNHInChI; /* number of -NH- on endpoints according to original InChI */ + short nNumTgNH2InChI; /* number of -NH2 on endpoints according to original InChI */ + short nNumTgDBNHInChI; /* number of =NH on endpoints according to original InChI */ + short nNumTgDBNMinusInChI; /* number of =N(-) on endpoints according to original InChI */ + short nNumTgDBNInChI; /* number of =N- on endpoints according to original InChI */ + + S_CHAR nChargeMobHInChI; + S_CHAR nChargeMobHRevrs; + S_CHAR nChargeMobHRevrsNonMetal; /* charge does not include charges on metals; later add ion pairs rejection */ + char bFixedHLayerExistsRevrs; + char bHasDifference; + U_CHAR nNumDiffMobH; +} CMP2MHINCHI; + + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + + + +struct tagCANON_GLOBALS; +struct tagINCHI_CLOCK; +int OneInChI2Atom( struct tagINCHI_CLOCK *ic, + struct tagCANON_GLOBALS *pCG, + ICHICONST INPUT_PARMS *ip, + STRUCT_DATA *sd, + const char *szCurHdr, + long num_inp, + StrFromINChI *pStruct, + int iComponent, + int iAtNoOffset, + int bHasSomeFixedH, + INChI *pInChI[]); + +int get_sp_element_type( int nPeriodicNumber, int *nRow ); + +int get_bonds_valences( int nPeriodicNum, + int bonds_valence, + int num_H, + VAL_AT *pVA ); + + +/* local prototypes */ +int AddExplicitDeletedH( inp_ATOM *at, + int jv, + int num_at, + int *iDeletedH, + int *iH, + int nNumDeletedH, + int bTwoStereo ); +int bFindCumuleneChain( inp_ATOM *at, + AT_NUMB i1, + AT_NUMB i2, + AT_NUMB nCumulene[], + int nMaxLen ); +int set_bond_type( inp_ATOM *at, + AT_NUMB i1, + AT_NUMB i2, + int bType ); +int set_cumulene_0D_parity( inp_ATOM *at, + inp_ATOM_STEREO *st, + int num_at, + int idelH1, + int i1, + int i2, + int idelH2, + int parity, + int len ); +int set_atom_0D_parity( inp_ATOM *at, + inp_ATOM_STEREO *st, + int num_at, + int num_deleted_H, + int i1, + int parity ); +int GetTgroupInfoFromInChI( T_GROUP_INFO *ti, + inp_ATOM *at, + AT_NUMB *endpoint, + INChI *pInChI ); +int FillOutpStructEndpointFromInChI( INChI *pInChI, AT_NUMB **pEndpoint ); +int SetStereoBondTypeFor0DParity( inp_ATOM *at, int i1, int m1 ); +int SetStereoBondTypesFrom0DStereo( StrFromINChI *pStruct, INChI *pInChI); +void CopyAt2St( inp_ATOM *at, inp_ATOM_STEREO * st, int num_atoms ); +void CopySt2At( inp_ATOM *at, inp_ATOM_STEREO * st, int num_atoms ); +int RestoreAtomConnectionsSetStereo( StrFromINChI *pStruct, + int iComponent, + int iAtNoOffset, + INChI *pInChI, + INChI *pInChIMobH); + +int RestoreAtomMakeBNS( struct tagINCHI_CLOCK *ic, + struct tagCANON_GLOBALS *pCG, + ICHICONST INPUT_PARMS *ip, + STRUCT_DATA *sd, + StrFromINChI *pStruct, + int iComponent, + int iAtNoOffset, + INChI *pInChI[], + const char *szCurHdr, + long num_inp, + int bHasSomeFixedH ); + +int nAddSuperCGroups( ALL_TC_GROUPS *pTCGroups ); + +int AddCGroups2TCGBnStruct( BN_STRUCT *pBNS, + StrFromINChI *pStruct, + VAL_AT *pVA, + ALL_TC_GROUPS *pTCGroups, + int nMaxAddEdges ); +int AddTGroups2TCGBnStruct( BN_STRUCT *pBNS, + StrFromINChI *pStruct, + VAL_AT *pVA, + ALL_TC_GROUPS *pTCGroups, + int nMaxAddEdges ); +BN_STRUCT* AllocateAndInitTCGBnStruct( StrFromINChI *pStruct, + VAL_AT *pVA, + ALL_TC_GROUPS *pTCGroups, + int nMaxAddAtoms, + int nMaxAddEdges, + int max_altp, + int *pNum_changed_bonds ); +int nCountBnsSizes( inp_ATOM *at, + int num_at, + int nAddEdges2eachAtom, + int nAddVertices, + T_GROUP_INFO *ti, + VAL_AT *pVA, + ICHICONST SRM *pSrm, + ALL_TC_GROUPS *pTCGroups ); + +int GetAtomRestoreInfo( struct tagCANON_GLOBALS *pCG, + inp_ATOM *atom, int iat, VAL_AT *pVArray, + ICHICONST SRM *pSrm, int bMobileH, AT_NUMB *endpoint ); + +int AddEdgeFlow( int edge_cap, + int edge_flow, + BNS_EDGE *e01, + BNS_VERTEX *pv0 /*src*/, + BNS_VERTEX *pv1/*dest*/, + int *tot_st_cap, + int *tot_st_flow ); + +void SetEdgeCapFlow( BNS_EDGE *e, int edge_cap, int edge_flow ); + +void AddStCapFlow( BNS_VERTEX *vert_ficpoint, + int *tot_st_flow, + int *tot_st_cap, + int cap, + int flow ); +void SetStCapFlow( BNS_VERTEX *vert_ficpoint, + int *tot_st_flow, + int *tot_st_cap, + int cap, + int flow ); + +int ConnectSuperCGroup( int nTCG_Plus, + int nAddGroups[], + int num_add, + int *pcur_num_vertices, + int *pcur_num_edges, + int *tot_st_cap, + int *tot_st_flow, + BN_STRUCT *pBNS, + ALL_TC_GROUPS *pTCGroups ); +int ConnectTwoVertices( BNS_VERTEX *p1, + BNS_VERTEX *p2, + BNS_EDGE *e, + BN_STRUCT *pBNS, + int bClearEdge ); + +int nTautEndpointEdgeCap( inp_ATOM *at, VAL_AT *pVA, int i ); + +/* +int GetAtomBondFlow( inp_ATOM *atom, VAL_AT *pVA, ICHICONST SRM *pSrm, int iat, int ineigh ); +void GetAtomStCapFlow( inp_ATOM *atom, VAL_AT *pVA, ICHICONST SRM *pSrm, int iat, int *pCap, int *pFlow ); +int GetAtomToMCGroupInitEdgeCapFlow( EdgeFlow *nEdgeCap, EdgeFlow *nEdgeFlow, ICHICONST SRM *pSrm, inp_ATOM *at, VAL_AT *pVA, int iat ); +void GetAtomToMetalInitEdgeCapFlow( EdgeFlow *nEdgeCap, EdgeFlow *nEdgeFlow ); +*/ + +int AtomStcapStflow( inp_ATOM *atom, + VAL_AT *pVA, + ICHICONST SRM *pSrm, + int iat, + int *pnStcap, + int *pnStflow, + EdgeFlow *pnMGroupEdgeCap, + EdgeFlow *pnMGroupEdgeFlow ); +int BondFlowMaxcapMinorder( inp_ATOM *atom, + VAL_AT *pVA, + ICHICONST SRM *pSrm, + int iat, + int ineigh, + int *pnMaxcap, + int *pnMinorder, + int *pbNeedsFlower ); + + +int clean_charge_val( struct tagCANON_GLOBALS *pCG, + CHARGE_VAL *pChargeVal, int len, + inp_ATOM *atom, VAL_AT *pVA, int iat, + int bIsMetal, int bMobileH, AT_NUMB *endpoint ); + +int ReallocTCGroups( ALL_TC_GROUPS *pTCGroups, int nAdd ); +int RegisterTCGroup( ALL_TC_GROUPS *pTCGroups, int nGroupType, int nGroupOrdNum, + int nVertexCap, int nVertexFlow, int nEdgeCap, int nEdgeFlow, int nNumEdges); +int CopyBnsToAtom( StrFromINChI *pStruct, BN_STRUCT *pBNS, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int bAllowZeroBondOrder ); +int CheckBnsConsistency( StrFromINChI *pStruct, BN_STRUCT *pBNS, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int bNoRad ); + +int RunBnsRestore1( struct tagCANON_GLOBALS *pCG, + struct tagINCHI_CLOCK *ic, ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, INChI *pInChI[], long num_inp, int bHasSomeFixedH); + +int RunBnsRestoreOnce( BN_STRUCT *pBNS, BN_DATA *pBD, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups ); +int nNumEdgesToCnVertex( MY_CONST C_NODE *pCN, int len, int v ); +int ConnectMetalFlower( int *pcur_num_vertices, int *pcur_num_edges, + int *tot_st_cap, int *tot_st_flow, ICHICONST SRM *pSrm, + BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups ); +int AddRadicalToMetal( int *tot_st_cap, int *tot_st_flow, ICHICONST SRM *pSrm, BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups ); +int bMayBeACationInMobileHLayer( inp_ATOM *at, VAL_AT *pVA, int iat, int bMobileH ); + +int MakeOneInChIOutOfStrFromINChI( struct tagCANON_GLOBALS *pCG, struct tagINCHI_CLOCK *ic, ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, StrFromINChI *pStruct, inp_ATOM *at2, inp_ATOM *at3, ALL_TC_GROUPS *pTCGroups ); +void IncrZeroBondsAndClearEndpts(inp_ATOM *at, int num_at, int iComponent); +void IncrZeroBonds(inp_ATOM *at, int num_at, int iComponent ); +void ClearEndpts(inp_ATOM *at, int num_at ); +int DisplayRestoredComponent( struct tagCANON_GLOBALS *pCG, StrFromINChI *pStruct, int iComponent, int iAtNoOffset, INChI *pInChI, const char *szCurHdr ); +int cmp_charge_val( const void *a1, const void *a2, void * ); + +int EvaluateChargeChanges( BN_STRUCT *pBNS, VAL_AT *pVA, int *pnDeltaH, int *pnDeltaCharge, int *pnNumVisitedAtoms ); +int RunBnsTestOnce( BN_STRUCT *pBNS, BN_DATA *pBD, VAL_AT *pVA, Vertex *pvFirst, Vertex *pvLast, + int *pPathLen, int *pnDeltaH, int *pnDeltaCharge, int *pnNumVisitedAtoms ); +int comp_cc_cand( const void *a1, const void *a2 ); +int MoveRadToAtomsAddCharges( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int forbidden_mask ); +void RemoveForbiddenBondFlowBits( BN_STRUCT *pBNS, int forbidden_edge_mask_int ); + +int PlusFromDB_N_DB_O_to_Metal( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); + +int AdjustTgroupsToForbiddenEdges2( BN_STRUCT *pBNS, inp_ATOM *at, VAL_AT *pVA, int num_atoms, int forbidden_mask ); +int AllocEdgeList( EDGE_LIST *pEdges, int nLen ); +int AddToEdgeList( EDGE_LIST *pEdges, int iedge, int nAddLen ); +int RemoveFromEdgeListByIndex( EDGE_LIST *pEdges, int index ); +int RemoveFromEdgeListByValue( EDGE_LIST *pEdges, int iedge ); +int FindInEdgeList( EDGE_LIST *pEdges, int iedge ); +int AllocBfsQueue( BFS_Q *pQ, int num_at, int min_ring_size ); +void RemoveForbiddenEdgeMask( BN_STRUCT *pBNS, EDGE_LIST *pEdges, int forbidden_edge_mask ); +void SetForbiddenEdgeMask( BN_STRUCT *pBNS, EDGE_LIST *pEdges, int forbidden_edge_mask ); +int ForbidCarbonChargeEdges( BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups, EDGE_LIST *pCarbonChargeEdges, int forbidden_edge_mask ); +int ForbidMetalCarbonEdges( BN_STRUCT *pBNS, inp_ATOM *at, int num_at, VAL_AT *pVA, + ALL_TC_GROUPS *pTCGroups, EDGE_LIST *pMetalCarbonEdges, int forbidden_edge_mask ); +int ForbidNintrogenPlus2BondsInSmallRings( BN_STRUCT *pBNS, inp_ATOM *at, int num_at, + VAL_AT *pVA, int min_ring_size, ALL_TC_GROUPS *pTCGroups, + EDGE_LIST *pNplus2BondsEdges, int forbidden_edge_mask ); +int RearrangePlusMinusEdgesFlow( BN_STRUCT *pBNS, BN_DATA *pBD, VAL_AT *pVA, + ALL_TC_GROUPS *pTCGroups, int forbidden_edge_mask ); +int IncrementZeroOrderBondsToHeteroat( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, + VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, + int forbidden_edge_mask); +int MoveChargeFromHeteroatomsToMetals( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, + VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, + int forbidden_edge_mask); +int EliminateChargeSeparationOnHeteroatoms( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, + VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, + int forbidden_edge_mask, int forbidden_stereo_edge_mask); +int MovePlusFromS2DiaminoCarbon( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, + VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); +int RestoreCyanoGroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); +int RestoreIsoCyanoGroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); +int RestoreNNNgroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); +int FixMetal_Nminus_Ominus( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); +int EliminateNitrogen5Val3Bonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); +int Convert_SIV_to_SVI(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); +int MoveMobileHToAvoidFixedBonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); +int RemoveRadFromMobileHEndpoint(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); +int RemoveRadFromMobileHEndpointFixH(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); +int MoveChargeToMakeCenerpoints(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); +int CheckAndRefixStereobonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); +int MoveChargeToRemoveCenerpoints(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); +int MakeSingleBondsMetal2ChargedHeteroat(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); +int SaltBondsToCoordBonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask); +int FixLessHydrogenInFormula( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, + inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ); +int FixMoreHydrogenInFormula( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, + inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ); +int FixAddProtonForADP( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, + inp_ATOM *at2, inp_ATOM *atf, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, ICR *picr, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ); +int ConnectDisconnectedH( inp_ATOM *at, int num_atoms, int num_deleted_H ); +int DisconnectedConnectedH( inp_ATOM *at, int num_atoms, int num_deleted_H ); + +int MakeInChIOutOfStrFromINChI2( struct tagINCHI_CLOCK *ic, struct tagCANON_GLOBALS *pCG, ICHICONST INPUT_PARMS *ip_inp, STRUCT_DATA *sd_inp, StrFromINChI *pStruct, + int iComponent, int iAtNoOffset, long num_inp ); + +int GetChargeFlowerUpperEdge( BN_STRUCT *pBNS, VAL_AT *pVA, int nChargeEdge ); +int get_pVA_atom_type( VAL_AT *pVA, inp_ATOM *at, int iat, int bond_type ); + +int NormalizeAndCompare(struct tagCANON_GLOBALS *pCG, struct tagINCHI_CLOCK *ic, ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, + StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, + ALL_TC_GROUPS *pTCGroups, INChI *pInChI[], long num_inp, int bHasSomeFixedH, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask); + +/* call InChI normalization only */ +int NormalizeStructure( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, + StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, + VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + inp_ATOM *at_norm, inp_ATOM *at_fixed_bonds_out, T_GROUP_INFO *t_group_info ); +/* create one InChI */ +int MakeOneInChIOutOfStrFromINChI2( struct tagCANON_GLOBALS *pCG, struct tagINCHI_CLOCK *ic, ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, + T_GROUP_INFO **t_group_info, inp_ATOM **at_norm, inp_ATOM **at_prep ); + +/* fixed-H */ +int FillOutExtraFixedHDataRestr( StrFromINChI *pStruct ); +int FillOutExtraFixedHDataInChI( StrFromINChI *pStruct, INChI *pInChI[] ); + +int FixFixedHRestoredStructure( struct tagCANON_GLOBALS *pCG, + struct tagINCHI_CLOCK *ic, + ICHICONST INPUT_PARMS *ip, + STRUCT_DATA *sd, + BN_STRUCT *pBNS, BN_DATA *pBD, + StrFromINChI *pStruct, + inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, + VAL_AT *pVA, + ALL_TC_GROUPS *pTCGroups, T_GROUP_INFO **ti, + inp_ATOM **at_norm, inp_ATOM **at_prep, + INChI *pInChI[], long num_inp, + int bHasSomeFixedH, int *pnNumRunBNS, + int *pnTotalDelta, + int forbidden_edge_mask, + int forbidden_stereo_edge_mask); + +int FixRemoveExtraTautEndpoints( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct, inp_ATOM *at, + inp_ATOM *at2, inp_ATOM *atf, inp_ATOM *atn, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, ICR *picr, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask ); +int FillOutCMP2FHINCHI( StrFromINChI *pStruct, inp_ATOM *at2, VAL_AT *pVA, INChI *pInChI[], CMP2FHINCHI *pc2i ); +int FillOutCMP2MHINCHI( StrFromINChI *pStruct, ALL_TC_GROUPS *pTCGroups, inp_ATOM *at2, + VAL_AT *pVA, INChI *pInChI[], CMP2MHINCHI *pc2i ); + +int bHas_N_V( inp_ATOM *at2, int num_atoms ); + +int GetPlusMinusVertex( BN_STRUCT *pBNS, ALL_TC_GROUPS *pTCGroups, int bCheckForbiddenPlus, int bCheckForbiddenMinus ); + +int FixMobileHRestoredStructure( struct tagCANON_GLOBALS *pCG, + struct tagINCHI_CLOCK *ic, + ICHICONST INPUT_PARMS *ip, + STRUCT_DATA *sd, + BN_STRUCT *pBNS, + BN_DATA *pBD, + StrFromINChI *pStruct, + inp_ATOM *at, + inp_ATOM *at2, + inp_ATOM *at3, + VAL_AT *pVA, + ALL_TC_GROUPS *pTCGroups, + T_GROUP_INFO **ppt_group_info, + inp_ATOM **ppat_norm, + inp_ATOM **ppat_prep, + INChI *pInChI[], + long num_inp, + int bHasSomeFixedH, + int *pnNumRunBNS, + int *pnTotalDelta, + int forbidden_edge_mask, + int forbidden_stereo_edge_mask); + +int FixRestoredStructureStereo( struct tagCANON_GLOBALS *pCG, struct tagINCHI_CLOCK *ic, INCHI_MODE cmpInChI, ICR *icr, INCHI_MODE cmpInChI2, ICR *icr2, + ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, BN_STRUCT *pBNS, BN_DATA *pBD, + StrFromINChI *pStruct, inp_ATOM *at, inp_ATOM *at2, inp_ATOM *at3, VAL_AT *pVA, + ALL_TC_GROUPS *pTCGroups, T_GROUP_INFO **ppt_group_info, inp_ATOM **ppat_norm, + inp_ATOM **ppat_prep, INChI *pInChI[], long num_inp, + int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask, int forbidden_stereo_edge_mask); + +int AddRemProtonsInRestrStruct( struct tagINCHI_CLOCK *ic, struct tagCANON_GLOBALS *pCG, ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, + int bHasSomeFixedH, + StrFromINChI *pStruct, int num_components, + StrFromINChI *pStructR, int num_componentsR, + NUM_H *pProtonBalance, int *recmet_change_balance ); +int AllInchiToStructure( struct tagINCHI_CLOCK *ic, struct tagCANON_GLOBALS *pCG, ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, char *szCurHdr, + ICHICONST SRM *pSrm, int bReqNonTaut, StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], + InpInChI *pOneInput ); +int AddProtonAndIsoHBalanceToMobHStruct( struct tagINCHI_CLOCK *ic, + struct tagCANON_GLOBALS *pCG, + ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, + long num_inp, int bHasSomeFixedH, + char *szCurHdr, + StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], + InpInChI *pOneInput); +int InChI2Atom( struct tagINCHI_CLOCK *ic, struct tagCANON_GLOBALS *pCG, + ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, + const char *szCurHdr, long num_inp, + StrFromINChI *pStruct, int iComponent, + int iAtNoOffset, int bI2A_Flag, int bHasSomeFixedH, + InpInChI *pOneInput); + + +int MarkDisconectedIdenticalToReconnected ( InpInChI *pOneInput ); +void RemoveFixHInChIIdentical2MobH( InpInChI *pOneInput ); +void SetUpSrm( SRM *pSrm ); +void FreeInputInChI2Struct( InpInChI *pOneInput ); +void FreeStrFromINChI( StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], int nNumComponents[INCHI_NUM][TAUT_NUM] ); +int OldPrintCompareOneOrigInchiToRevInChI(StrFromINChI *pStruct, INChI *pInChI[TAUT_NUM], int bMobileH, + int iComponent, long num_inp, char *szCurHdr); +int CompareOneOrigInchiToRevInChI(StrFromINChI *pStruct, INChI *pInChI[TAUT_NUM], int bMobileH, int iComponent, + long num_inp, char *szCurHdr, + COMPONENT_REM_PROTONS *nCurRemovedProtons, INCHI_MODE CompareInchiFlags[]); +int CompareTwoPairsOfInChI( INChI *pInChI1[TAUT_NUM], INChI *pInChI2[TAUT_NUM], + int bMobileH, INCHI_MODE CompareInchiFlags[] ); +INCHI_MODE CompareReversedINChI3( INChI *i1 /* InChI from reversed struct */, INChI *i2 /* input InChI */, + INChI_Aux *a1, INChI_Aux *a2, int *err ); +INCHI_MODE CompareReversedStereoINChI3( INChI_Stereo *s1/* InChI from reversed struct */, INChI_Stereo *s2 /* input InChI */, ICR *picr); +int CompareAllOrigInchiToRevInChI(StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], InpInChI *pOneInput, int bReqNonTaut, + long num_inp, char *szCurHdr); +int CompareAllDisconnectedOrigInchiToRevInChI(StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], + InpInChI *pOneInput, int bHasSomeFixedH, + long num_inp, char *szCurHdr); +int insertions_sort_AT_NUMB( AT_NUMB *base, int num ); + +int AddRemIsoProtonsInRestrStruct( struct tagINCHI_CLOCK *ic, + struct tagCANON_GLOBALS *pCG, + ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, + long num_inp, int bHasSomeFixedH, + StrFromINChI *pStruct, int num_components, + StrFromINChI *pStructR, int num_componentsR, + NUM_H pProtonBalance[], + NUM_H recmet_change_balance[] ); + +int OutputInChIOutOfStrFromINChI( struct tagINCHI_CLOCK *ic, + struct tagCANON_GLOBALS *pCG, + ICHICONST INPUT_PARMS *ip_inp, + STRUCT_DATA *sd_inp, + long num_inp, int bINChIOutputOptions, + INCHI_IOSTREAM *pout, INCHI_IOSTREAM *plog, + InpInChI *pOneInput, int bHasSomeFixedH, + unsigned char save_opt_bits); + +int MergeStructureComponents( ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, long num_inp, char *szCurHdr, + ICHICONST SRM *pSrm, int bReqNonTaut, StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], + InpInChI *pOneInput ); +int AddOneMsg( char *szMsg, int used_len, int tot_len, const char *szAddMsg, const char *szDelim ); +int FillOutCompareMessage( char *szMsg, int nLenMsg, INCHI_MODE bits[] ); +void clear_t_group_info( T_GROUP_INFO *ti ); +int bInpInchiComponentExists( InpInChI *pOneInput, int iINCHI, int bMobileH, int k ); +int bInpInchiComponentDeleted( InpInChI *pOneInput, int iInChI, int bMobileH, int k ); +int bRevInchiComponentExists( StrFromINChI *pStruct, int iInChI, int bMobileH, int k ); +int bRevInchiComponentDeleted( StrFromINChI *pStruct, int iInChI, int bMobileH, int k ); +int DetectInpInchiCreationOptions ( InpInChI *pOneInput, int *bHasReconnected, int *bHasMetal, + int *bHasFixedH, int *sFlag, int *bTautFlag ); + +int DisplayStructureComponents( struct tagCANON_GLOBALS *pCG, + ICHICONST INPUT_PARMS *ip, STRUCT_DATA *sd, + long num_inp, char *szCurHdr, + ICHICONST SRM *pSrm, + int bReqNonTaut, + StrFromINChI *pStruct[INCHI_NUM][TAUT_NUM], + InpInChI *pOneInput ); +int DisplayOneRestoredComponent( struct tagCANON_GLOBALS *pCG, + StrFromINChI *pStruct, inp_ATOM *at, + int iComponent, int nNumComponents, int bMobileH, + const char *szCurHdr ); +int DisplayAllRestoredComponents( struct tagCANON_GLOBALS *pCG, + inp_ATOM *at, int num_at, const char *szCurHdr ); + +int CountStereoTypes( INChI *pInChI, int *num_known_SB, int *num_known_SC, + int *num_unk_und_SB, int *num_unk_und_SC, + int *num_SC_PIII, int *num_SC_AsIII); +int GetNumNeighborsFromInchi( INChI *pInChI, AT_NUMB nAtNumber ); +int bIsUnsatCarbonInASmallRing( inp_ATOM *at, VAL_AT *pVA, int iat, BFS_Q *pbfsq, int min_ring_size ); + +int MakeProtonComponent( StrFromINChI *pStruct, int iComponent, int num_prot ); + +void FreeInpInChI( InpInChI *pOneInput ); + + +/* extra configurarion */ +#define KEEP_METAL_EDGE_FLOW 0 /* counterexample: mdb0-1738.sdf.txt */ +#define MOVE_CHARGES_FROM_HETEREO_TO_METAL 0 /* disabled */ +#define FIX_ADD_PROTON_FOR_ADP 0 /* not used */ + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + + +#endif /* _ICHIRVRS_H_ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichisize.h b/INCHI-1-SRC/INCHI_BASE/src/ichisize.h similarity index 55% rename from INCHI-1-SRC/INCHI_API/inchi_dll/ichisize.h rename to INCHI-1-SRC/INCHI_BASE/src/ichisize.h index d6f3ab4..8eeedda 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichisize.h +++ b/INCHI-1-SRC/INCHI_BASE/src/ichisize.h @@ -1,66 +1,65 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef ___INCHISIZE_H__ -#define ___INCHISIZE_H__ - -typedef unsigned short AT_NUMB; -typedef unsigned short AT_RANK; -#define AT_RANK_MASK ((AT_RANK)~0) - -typedef signed short NUM_H; -#define MAX_ATOMS 1024 - - -#define CHAR_MASK 0xFF - - -typedef AT_RANK *pAT_RANK; -typedef pAT_RANK *ppAT_RANK; - -typedef unsigned long INCHI_MODE; - -#define LEN_COORD 10 -#define NUM_COORD 3 -typedef char MOL_COORD[LEN_COORD*NUM_COORD + NUM_COORD-1]; /*copied 30 bytes from MOLfile */ - - -#endif /* ___INCHISIZE_H__ */ - +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _ICHISIZE_H_ +#define _ICHISIZE_H_ + +typedef unsigned short AT_NUMB; +typedef unsigned short AT_RANK; +#define AT_RANK_MASK ((AT_RANK)~0) + +typedef signed short NUM_H; +#define MAX_ATOMS 32766 +#define NORMALLY_ALLOWED_INP_MAX_ATOMS 1024 + + +#define CHAR_MASK 0xFF + + +typedef AT_RANK *pAT_RANK; +typedef pAT_RANK *ppAT_RANK; + +typedef unsigned long INCHI_MODE; + +#define LEN_COORD 10 +#define NUM_COORD 3 +typedef char MOL_COORD[LEN_COORD*NUM_COORD + NUM_COORD-1]; /*copied 30 bytes from MOLfile */ + + +#endif /* _ICHISIZE_H_ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichisort.c b/INCHI-1-SRC/INCHI_BASE/src/ichisort.c similarity index 59% rename from INCHI-1-SRC/INCHI_API/inchi_dll/ichisort.c rename to INCHI-1-SRC/INCHI_BASE/src/ichisort.c index 0daa175..a534f73 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichisort.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichisort.c @@ -1,572 +1,828 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - -#include "mode.h" - -#include "incomdef.h" -#include "extr_ct.h" -#include "ichitaut.h" -#include "ichicant.h" -#include "ichicomn.h" - -#include "ichicomp.h" - -#define RET_MAX 32767 - -/**********************************************************************************/ -void inchi_swap ( char *a, char *b, size_t width ) -{ - char tmp; - if ( a != b ) - while ( width-- ) { - tmp = *a; - *a++ = *b; - *b++ = tmp; - } -} -/**********************************************************************************/ -/* Sort by insertions */ -int insertions_sort( void *base, size_t num, size_t width, int ( *compare )(const void *e1, const void *e2 ) ) -{ - char *i, *j, *pk = (char*)base; - int num_trans = 0; - size_t k; - for( k=1; k < num; k++, pk += width ) { - /*for( i = pk, j = pk + width; j > (char*)base && (*compare)(i,j) > 0; j=i, i -= width )*/ - for( i = j = pk + width; j > (char*)base && (i -= width,(*compare)(i,j)) > 0; j=i ) /* changed to keep BoundsChecker happy 2007-09-24 DT */ - { - inchi_swap( i, j, width ); - num_trans ++; - } - } - return num_trans; -} -/**********************************************************************************/ -/* Sort by insertions */ -int insertions_sort_AT_NUMBERS( AT_NUMB *base, int num, int ( *compare )(const void *e1, const void *e2 ) ) -{ - AT_NUMB *i, *j, *pk, tmp; - int k, num_trans = 0; - for( k=1, pk = base; k < num; k++, pk ++ ) { - for( j = (i = pk) + 1, tmp = *j; j > base && (*compare)(i,&tmp) > 0; j=i, i -- ) { - *j = *i; - num_trans ++; - } - *j = tmp; - } - return num_trans; -} -/**********************************************************************************/ -/* Sort neighbors according to ranks in ascending order */ -void insertions_sort_NeighList_AT_NUMBERS( NEIGH_LIST base, AT_RANK *nRank ) -{ - AT_NUMB *i, *j, *pk, tmp; - AT_RANK rj; /* optimization */ - int k, num = (int)*base++; - for( k=1, pk = base; k < num; k++, pk ++ ) { - for( j = (i = pk) + 1, rj=nRank[(int)*j]; j > base && nRank[(int)*i] > rj; j=i, i -- ) { - tmp = *i; - *i = *j; - *j = tmp; - } - } -} -/**********************************************************************************/ -/* Sort neighbors according to ranks in ascending order */ -int insertions_sort_AT_RANK( AT_RANK *base, int num ) -{ - AT_RANK *i, *j, *pk, tmp; - int k, num_trans = 0; - for( k=1, pk = base; k < num; k++, pk ++ ) { - for( j = (i = pk) + 1, tmp = *j; j > base && *i > tmp; j=i, i -- ) { - *j = *i; - num_trans ++; - } - *j = tmp; - } - return num_trans; -} -/**********************************************************************************/ -/* Sort neighbors according to ranks in ascending order */ -int insertions_sort_NeighList_AT_NUMBERS3( NEIGH_LIST base, AT_RANK *nRank ) -{ - AT_NUMB *i, *j, *pk, tmp; - AT_RANK rj; - int k, n, num = (int)*base++; - for( k=1, pk = base, n=0; k < num; k++, pk ++ ) { - for( j = (i = pk) + 1, rj=nRank[(int)(tmp=*j)]; j > base && nRank[(int)*i] > rj; j=i, i -- ) { - *j = *i; - n ++; - } - *j = tmp; - } - return n; -} - -/**********************************************************************************/ -/* Sort neighbors according to symm. ranks (primary key) and canon. ranks (secondary key), in descending order */ -void insertions_sort_NeighListBySymmAndCanonRank( NEIGH_LIST base, const AT_RANK *nSymmRank, const AT_RANK *nCanonRank ) -{ - AT_NUMB *i, *j, *pk, tmp; - int diff; - int k, num = (int)*base++; - for( k=1, pk = base; k < num; k++, pk ++ ) { - for( j = (i = pk) + 1; j > base && /* always j > i */ - ( 0 > (diff = (int)nSymmRank[(int)*i] - (int)nSymmRank[(int)*j]) || - !diff && nCanonRank[(int)*i] < nCanonRank[(int)*j]); j=i, i -- ) { - tmp = *i; - *i = *j; - *j = tmp; - } - } -} - -/********************************************************************************************* - * - * Comparison functions - * - *********************************************************************************************/ -int CompNeighborsAT_NUMBER( const void* a1, const void* a2) -{ -#ifdef CT_NEIGH_INCREASE - return (int)pn_RankForSort[pNeighborsForSort[(int)*(const AT_NUMB*)a1]] - - (int)pn_RankForSort[pNeighborsForSort[(int)*(const AT_NUMB*)a2]]; -#else - return (int)pn_RankForSort[pNeighborsForSort[(int)*(const AT_NUMB*)a2]] - - (int)pn_RankForSort[pNeighborsForSort[(int)*(const AT_NUMB*)a1]]; -#endif -} - -/**********************************************************************************/ -int comp_AT_RANK( const void* a1, const void* a2) -{ - return (int)*(const AT_RANK*)a1 - (int)*(const AT_RANK*)a2; -} - -/**********************************************************************************/ -/* Compare for sorting Ranks only */ -int CompRank(const void* a1, const void* a2 ) -{ - int ret = (int)pn_RankForSort[(int)*(const AT_RANK*)a1] - - (int)pn_RankForSort[(int)*(const AT_RANK*)a2]; - return ret; -} -/**********************************************************************************/ -int CompRanksOrd( const void* a1, const void* a2 ) -{ - int ret; - ret = (int)pn_RankForSort[(int)*(const AT_RANK*)a1] - - (int)pn_RankForSort[(int)*(const AT_RANK*)a2]; - if ( !ret ) - ret = (int)*(const AT_RANK*)a1 - (int)*(const AT_RANK*)a2; - return ret; -} -/**********************************************************************************/ -int CompAtomInvariants2Only( const void* a1, const void* a2 ) -{ - const ATOM_INVARIANT2 *pAI1 = pAtomInvariant2ForSort + (int)*(const AT_RANK*)a1; - const ATOM_INVARIANT2 *pAI2 = pAtomInvariant2ForSort + (int)*(const AT_RANK*)a2; - int i; - for ( i = 0; i < AT_INV_BREAK1; i ++ ) { - if ( pAI1->val[i] == pAI2->val[i] ) - continue; - return (int)pAI1->val[i] - (int)pAI2->val[i]; - } - if ( pAI1->iso_sort_key != pAI2->iso_sort_key ) { - return ( pAI1->iso_sort_key > pAI2->iso_sort_key )? 1 : -1; - } - for ( ; i < AT_INV_LENGTH; i ++ ) { - if ( pAI1->val[i] != pAI2->val[i] ) - continue; - return (int)pAI1->val[i] - (int)pAI2->val[i]; - } - if ( pAI1->iso_aux_key != pAI2->iso_aux_key ) { - return ( pAI1->iso_aux_key > pAI2->iso_aux_key )? 1 : -1; - } - return 0; -} -/**********************************************************************************/ -int CompAtomInvariants2( const void* a1, const void* a2 ) -{ - /* Warning: the following line may be compiler implementation dependent */ - int ret = CompAtomInvariants2Only( a1, a2 ); - if ( !ret ) - ret = (int)*(const AT_RANK*)a1 - (int)*(const AT_RANK*)a2; - return ret; -} -/**********************************************************************************/ -/* Compare two elements lexicographically */ -int CompChemElemLex( const void *a1, const void *a2 ) -{ - return memcmp( a1, a2, 2); -} -/**********************************************************************************/ -/* lexicographic compare */ -int CompareNeighListLex( NEIGH_LIST pp1, NEIGH_LIST pp2, const AT_RANK *nRank) -{ - int len1 = (int)*pp1++; - int len2 = (int)*pp2++; - int len = inchi_min( len1, len2 ); - int diff = 0; - while ( len -- > 0 && !( diff = (int)nRank[*pp1++] - (int)nRank[*pp2++] ) ) - ; - return diff? diff : (len1 - len2); - -} -/**********************************************************************************/ -/* lexicographic compare */ -int CompareNeighListLexUpToMaxRank( NEIGH_LIST pp1, NEIGH_LIST pp2, const AT_RANK *nRank, AT_RANK nMaxAtNeighRank ) -{ - int len1 = (int)*pp1++; - int len2 = (int)*pp2++; - int diff = 0; - int len; - while( 0 < len1 && nRank[pp1[len1-1]] > nMaxAtNeighRank ) { - len1 --; - } - while( 0 < len2 && nRank[pp2[len2-1]] > nMaxAtNeighRank ) { - len2 --; - } - len = inchi_min( len1, len2 ); - while ( len -- > 0 && !( diff = (int)nRank[*pp1++] - (int)nRank[*pp2++] ) ) - ; - return diff? diff : (len1 - len2); - -} -/**********************************************************************************/ -int compare_NeighLists( const NEIGH_LIST *op1, const NEIGH_LIST *op2 ) -{ - return CompareNeighListLex( *op1, *op2, pn_RankForSort); -} -/**********************************************************************************/ -int CompNeighListRanks( const void* a1, const void* a2 ) -{ - int ret; - ret = (int)pn_RankForSort[*((const AT_RANK*)a1)] - - (int)pn_RankForSort[*((const AT_RANK*)a2)]; - if ( !ret ) - ret = compare_NeighLists( pNeighList_RankForSort + *((const AT_RANK*)a1), - pNeighList_RankForSort + *((const AT_RANK*)a2) ); - return ret; -} -/**********************************************************************************/ -int CompNeighLists( const void* a1, const void* a2 ) -{ - int ret; - ret = compare_NeighLists( pNeighList_RankForSort + *((const AT_RANK*)a1), - pNeighList_RankForSort + *((const AT_RANK*)a2) ); - return ret; -} -/**********************************************************************************/ -int CompNeighListsUpToMaxRank( const void* a1, const void* a2 ) -{ - int ret; - ret = CompareNeighListLexUpToMaxRank( pNeighList_RankForSort[*((const AT_RANK*)a1)], - pNeighList_RankForSort[*((const AT_RANK*)a2)], - pn_RankForSort, nMaxAtNeighRankForSort ); - return ret; -} -/**********************************************************************************/ -int CompNeighListRanksOrd( const void* a1, const void* a2 ) -{ - int ret = CompNeighListRanks( a1, a2 ); - if ( !ret ) - ret = (int)*((const AT_RANK*)a1) - (int)*((const AT_RANK*)a2); /* keep original order if identical */ - return ret; -} -/**********************************************************************************/ -int CompRanksInvOrd( const void* a1, const void* a2 ) -{ - return (int)*(const AT_RANK*)a2 - (int)*(const AT_RANK*)a1; -} -/**********************************************************************************/ -int CompNeighborsRanksCountEql( const void* a1, const void* a2 ) -{ -#ifdef CT_NEIGH_INCREASE - int ret = (int)pn_RankForSort[(int)*(const AT_RANK*)a1] - - (int)pn_RankForSort[(int)*(const AT_RANK*)a2]; -#else - int ret = (int)pn_RankForSort[(int)*(const AT_RANK*)a2] - - (int)pn_RankForSort[(int)*(const AT_RANK*)a1]; -#endif - nNumCompNeighborsRanksCountEql += !ret; - return ret; -} -/**************************************************************************************** - * - * In this neighbor list the (vertex number) = (canonical number) - 1 - * Since LinearCT is sorted so that parents are in ascending order - * and all neighbors of a parent are smaller than the parent and are - * in ascending order, the neighbors in the NEIGH_LIST are automatically - * sorted in ascending order - */ -NEIGH_LIST *CreateNeighListFromLinearCT( AT_NUMB *LinearCT, int nLenCT, int num_atoms ) -{ - /* atom numbers in LinearCT are canonical numbers - * order: parent[i] > neigh[i][0] < neigh[i][1]... neigh[i+1][0] < ... - * parent[i] < parent[i+1] - */ - int i, j; - S_CHAR *valence = NULL; - NEIGH_LIST *pp = NULL; - AT_NUMB *pAtList = NULL; - AT_RANK n_vertex, n_neigh; - int err = 1, num_bonds; - int length, start; - if ( (int)LinearCT[0] > num_atoms ) { - goto exit_function; - } - if ( !(valence = (S_CHAR*)inchi_calloc( num_atoms+1, sizeof(valence[0]) ) ) ) { - goto exit_function; - } - for ( i = 1, num_bonds = 0, n_vertex = LinearCT[0]; i < nLenCT; i ++ ) { - if ( (n_neigh = LinearCT[i]) < n_vertex ) { - valence[n_neigh] ++; - valence[n_vertex] ++; - num_bonds += 2; - } else - if ( (int)(n_vertex = n_neigh) > num_atoms ) { - goto exit_function; - } - } - if ( (int)n_vertex != num_atoms ) { - goto exit_function; - } - length = num_bonds + num_atoms + 1; - if ( pp = (NEIGH_LIST *) inchi_calloc((num_atoms+1), sizeof(NEIGH_LIST)) ) { - if ( pAtList = (AT_NUMB *) inchi_malloc( length*sizeof(*pAtList) ) ) { - /* create empty connection table */ - for ( i = 1, length = 0; i <= num_atoms; i ++ ) { - start = length; - length += (valence[i]+1); - pp[i-1] = pAtList + start; - pp[i-1][0] = 0; - } - /* fill out the CT */ - for ( i = 1, n_vertex = LinearCT[0]-1; i < nLenCT; i ++ ) { - if ( (n_neigh = LinearCT[i]-1) < n_vertex ) { - /* vertex - neighbor connection */ - j = (int)(++pp[(int)n_vertex][0]); - pp[(int)n_vertex][j] = n_neigh; - /* neighbor - vertex connection */ - j = (int)(++pp[(int)n_neigh][0]); - pp[(int)n_neigh][j] = n_vertex; - - } else - if ( (int)(n_vertex = n_neigh) >= num_atoms ) { - goto exit_function; - } - } - err = 0; - } - } -exit_function: - if ( valence ) { - inchi_free( valence ); - } - if ( err ) { - if ( pAtList ) - inchi_free( pAtList ); - if ( pp ) { - inchi_free( pp ); - pp = NULL; - } - } - return pp; -} - -/*********************************************************************************** - * NEIGH_LIST pp[] is an array of pointers to the lists of neighboring atoms numbers - * The first number in each list is a number of neighbors. - * In case of bDoubleBondSquare != 0 neighbors connected by the double bond appear 2 times - * The first element pp[0] is a pointer to be deallocated to free all the lists. - */ -NEIGH_LIST *CreateNeighList( int num_atoms, int num_at_tg, sp_ATOM* at, - int bDoubleBondSquare, T_GROUP_INFO *t_group_info ) -{ - /* +1 to add NULL termination */ - NEIGH_LIST *pp = (NEIGH_LIST *) inchi_calloc((num_at_tg+1), sizeof(NEIGH_LIST)); - T_GROUP *t_group = NULL; - AT_NUMB *nEndpointAtomNumber = NULL; - int num_t_groups = 0; - int nFirstEndpointAtNoPos; - - AT_NUMB *pAtList = NULL; - int length, start, val, i, j; - if ( pp ) { - if ( num_at_tg > num_atoms ) { - t_group = t_group_info->t_group; - num_t_groups = t_group_info->num_t_groups; - nEndpointAtomNumber = t_group_info->nEndpointAtomNumber; - } - - if ( !bDoubleBondSquare ) { - for ( i = 0, length = 0; i < num_atoms; i ++ ) { - length += (int)at[i].valence + (num_t_groups && at[i].endpoint); - } - length += num_atoms; - for ( i = 0; i < num_t_groups; i ++ ) { - length += (int)t_group[i].nNumEndpoints; - } - length += num_t_groups; - - } else { - for ( i = 0, length = 0; i < num_atoms; i ++ ) { - val = (int)at[i].valence; - for ( j = 0; j < val; j ++ ) { - length += 1 + (bDoubleBondSquare && BOND_DOUBLE == at[i].bond_type[j]); - } - length += (num_t_groups && at[i].endpoint); - } - length += num_atoms; - for ( i = 0; i < num_t_groups; i ++ ) { - length += (int)t_group[i].nNumEndpoints; - } - length += num_t_groups; - } - length ++; /* +1 to save number of neighbors */ - if ( pAtList = (AT_NUMB *) inchi_malloc( length*sizeof(*pAtList) ) ) { - if ( !bDoubleBondSquare ) { - for ( i = 0, length = 0; i < num_atoms; i ++ ) { - val = at[i].valence; - start = length ++; - for ( j = 0; j < val; j ++ ) { - pAtList[length ++] = at[i].neighbor[j]; - } - /* add endpoint */ - if (num_t_groups && at[i].endpoint) { - pAtList[length ++] = num_atoms + (int)at[i].endpoint - 1; - } - pAtList[start] = length - start - 1; /* number of neighbors before the list of neighbors */ - pp[i] = pAtList + start; /* pointer to the */ - } - - } else { - for ( i = 0, length = 0; i < num_atoms; i ++ ) { - val = at[i].valence; - start = length ++; - for ( j = 0; j < val; j ++ ) { - pAtList[length ++] = at[i].neighbor[j]; - if ( bDoubleBondSquare && BOND_DOUBLE == at[i].bond_type[j] ) { - pAtList[length ++] = at[i].neighbor[j]; /* a list of neighbor orig. numbers */ - } - } - /* add endpoint */ - if (num_t_groups && at[i].endpoint) { - pAtList[length ++] = num_atoms + (int)at[i].endpoint - 1; - } - pAtList[start] = length - start - 1; /* number of neighbors before the list of neighbors */ - pp[i] = pAtList + start; /* pointer to the */ - } - } - - /* add t-groups */ - for ( i = 0; i < num_t_groups; i ++ ) { - val = (int)t_group[i].nNumEndpoints; - start = length ++; - nFirstEndpointAtNoPos = (int)t_group[i].nFirstEndpointAtNoPos; - for ( j = 0; j < val; j ++ ) { - pAtList[length ++] = nEndpointAtomNumber[nFirstEndpointAtNoPos+j]; - } - pAtList[start] = length - start - 1; /* number of neighbors before the list of neighbors */ - pp[num_atoms+i] = pAtList + start; /* pointer to the */ - } - } else { - inchi_free ( pp ); - return NULL; - } - } - return pp; -} -/**********************************************************************************/ -void FreeNeighList( NEIGH_LIST *pp ) -{ - if ( pp ) { - if ( pp[0] ) { - inchi_free( pp[0] ); - } - inchi_free( pp ); - } -} - -/**********************************************************************************/ -int BreakAllTies( int num_atoms, int num_max, AT_RANK **pRankStack, - NEIGH_LIST *NeighList, AT_RANK *nTempRank, CANON_STAT *pCS) -{ - int i, nRet = -1, nNumRanks=1 /* value does not matter*/; - - AT_RANK *nPrevRank = *pRankStack ++; - AT_RANK *nPrevAtomNumber = *pRankStack ++; - - AT_RANK *nNewRank = NULL; - AT_RANK *nNewAtomNumber = NULL; - - if ( !pRankStack[0] ) { - pRankStack[0] = (AT_RANK *) inchi_malloc(num_max*sizeof(*nNewRank)); - } - if ( !pRankStack[1] ) { - pRankStack[1] = (AT_RANK *) inchi_malloc(num_max*sizeof(*nNewAtomNumber)); - } - if ( !pRankStack[0] || !pRankStack[1] ) - return CT_OUT_OF_RAM; /* */ - nNewRank = pRankStack[0]; - nNewAtomNumber = pRankStack[1]; - - if ( nNewRank && nNewAtomNumber ) { - memcpy( nNewAtomNumber, nPrevAtomNumber, num_atoms*sizeof(nNewAtomNumber[0])); - memcpy( nNewRank, nPrevRank, num_atoms*sizeof(nNewRank[0])); - - for ( i = 1, nRet=0; i < num_atoms; i ++ ) { /* 12-12-2001: replaced Prev... with New... */ - if ( nNewRank[(int)nNewAtomNumber[i-1]] == nNewRank[(int)nNewAtomNumber[i]] ) { - nNewRank[nNewAtomNumber[i-1]] = (AT_RANK)i; - nNumRanks = DifferentiateRanks2( num_atoms, NeighList, - nNumRanks, nNewRank, nTempRank, - nNewAtomNumber, &pCS->lNumNeighListIter, 1 ); - pCS->lNumBreakTies ++; - nRet ++; - } - } - } - return nRet; -} +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include + +#include "mode.h" +#include "ichicomn.h" +#include "ichicant.h" + + +#if 0 +#define RET_MAX 32767 +#define CUTOFF 8 /* testing shows that this is good value */ +#endif + +/* Note: the theoretical number of stack entries required is +no more than 1 + log2(num). But we switch to insertion +sort for CUTOFF elements or less, so we really only need +1 + log2(num) - log2(CUTOFF) stack entries. For a CUTOFF +of 8, that means we need no more than 30 stack entries for +32 bit platforms, and 62 for 64-bit platforms. */ +#define STKSIZ (8*sizeof(void*) - 2) + + +/* + inchi's qsort +*/ +void inchi_qsort( void *pParam, + void *base, + size_t num, + size_t width, + int (*comp)(const void *, const void *, void *)) +{ + char *lo, *hi; /* ends of sub-array currently sorting */ + char *mid; /* points to middle of subarray */ + char *loguy, *higuy; /* traveling pointers for partition step */ + size_t size; /* size of the sub-array */ + char *lostk[STKSIZ], *histk[STKSIZ]; + int stkptr; /* stack for saving sub-array to be processed */ + + if (num < 2) + return; /* nothing to do */ + + stkptr = 0; /* initialize stack */ + + lo = (char *)base; + hi = (char *)base + width * (num-1); /* initialize limits */ + + /* this entry point is for pseudo-recursion calling: setting + lo and hi and jumping to here is like recursion, but stkptr is + preserved, locals aren't, so we preserve stuff on the stack */ +recurse: + + size = (hi - lo) / width + 1; /* number of el's to sort */ + + /* First we pick a partitioning element. The efficiency of the + algorithm demands that we find one that is approximately the median + of the values, but also that we select one fast. We choose the + median of the first, middle, and last elements, to avoid bad + performance in the face of already sorted data, or data that is made + up of multiple sorted runs appended together. Testing shows that a + median-of-three algorithm provides better performance than simply + picking the middle element for the latter case. */ + + mid = lo + (size / 2) * width; /* find middle element */ + + /* Sort the first, middle, last elements into order */ + if (comp(lo, mid, pParam) > 0) { + inchi_swap(lo, mid, width); + } + if (comp(lo, hi, pParam) > 0) { + inchi_swap(lo, hi, width); + } + if (comp(mid, hi, pParam) > 0) { + inchi_swap(mid, hi, width); + } + + /* We now wish to partition the array into three pieces, one consisting + of elements <= partition element, one of elements equal to the + partition element, and one of elements > than it. This is done + below; comments indicate conditions established at every step. */ + + loguy = lo; + higuy = hi; + + /* Note that higuy decreases and loguy increases on every iteration, + so loop must terminate. */ + for (;;) { + /* lo <= loguy < hi, lo < higuy <= hi, + A[i] <= A[mid] for lo <= i <= loguy, + A[i] > A[mid] for higuy <= i < hi, + A[hi] >= A[mid] */ + + /* The doubled loop is to avoid calling comp(mid,mid), since some + existing comparison funcs don't work when passed the same + value for both pointers. */ + + if (mid > loguy) { + do { + loguy += width; + } while (loguy < mid && comp(loguy, mid, pParam) <= 0); + } + if (mid <= loguy) { + do { + loguy += width; + } while (loguy <= hi && comp(loguy, mid, pParam) <= 0); + } + + /* lo < loguy <= hi+1, A[i] <= A[mid] for lo <= i < loguy, + either loguy > hi or A[loguy] > A[mid] */ + + do { + higuy -= width; + } while (higuy > mid && comp(higuy, mid, pParam) > 0); + + /* lo <= higuy < hi, A[i] > A[mid] for higuy < i < hi, + either higuy == lo or A[higuy] <= A[mid] */ + + if (higuy < loguy) + break; + + /* if loguy > hi or higuy == lo, then we would have exited, so + A[loguy] > A[mid], A[higuy] <= A[mid], + loguy <= hi, higuy > lo */ + + inchi_swap(loguy, higuy, width); + + /* If the partition element was moved, follow it. Only need + to check for mid == higuy, since before the swap, + A[loguy] > A[mid] implies loguy != mid. */ + + if (mid == higuy) + mid = loguy; + + /* A[loguy] <= A[mid], A[higuy] > A[mid]; so condition at top + of loop is re-established */ + } + + /* A[i] <= A[mid] for lo <= i < loguy, + A[i] > A[mid] for higuy < i < hi, + A[hi] >= A[mid] + higuy < loguy + implying: + higuy == loguy-1 + or higuy == hi - 1, loguy == hi + 1, A[hi] == A[mid] */ + + /* Find adjacent elements equal to the partition element. The + doubled loop is to avoid calling comp(mid,mid), since some + existing comparison funcs don't work when passed the same value + for both pointers. */ + + higuy += width; + if (mid < higuy) { + do { + higuy -= width; + } while (higuy > mid && comp(higuy, mid, pParam) == 0); + } + if (mid >= higuy) { + do { + higuy -= width; + } while (higuy > lo && comp(higuy, mid, pParam) == 0); + } + + /* OK, now we have the following: + higuy < loguy + lo <= higuy <= hi + A[i] <= A[mid] for lo <= i <= higuy + A[i] == A[mid] for higuy < i < loguy + A[i] > A[mid] for loguy <= i < hi + A[hi] >= A[mid] */ + + /* We've finished the partition, now we want to sort the subarrays + [lo, higuy] and [loguy, hi]. + We do the smaller one first to minimize stack usage. + We only sort arrays of length 2 or more.*/ + + if ( higuy - lo >= hi - loguy ) { + if (lo < higuy) { + lostk[stkptr] = lo; + histk[stkptr] = higuy; + ++stkptr; + } /* save big recursion for later */ + + if (loguy < hi) { + lo = loguy; + goto recurse; /* do small recursion */ + } + } + else { + if (loguy < hi) { + lostk[stkptr] = loguy; + histk[stkptr] = hi; + ++stkptr; /* save big recursion for later */ + } + + if (lo < higuy) { + hi = higuy; + goto recurse; /* do small recursion */ + } + } + + /* We have sorted the array, except for any pending sorts on the stack. + Check if there are any, and do them. */ + + --stkptr; + if (stkptr >= 0) { + lo = lostk[stkptr]; + hi = histk[stkptr]; + goto recurse; /* pop subarray from stack */ + } + else + return; /* all subarrays done */ +} + + + +/**********************************************************************************/ +void inchi_swap ( char *a, char *b, size_t width ) +{ + char tmp; + if ( a != b ) + while ( width-- ) { + tmp = *a; + *a++ = *b; + *b++ = tmp; + } +} + + +/**********************************************************************************/ +/* Sort by insertions */ +int insertions_sort( void *pCG, + void *base, + size_t num, size_t width, + int ( *compare )(const void *e1, const void *e2, void * ) ) +{ + char *i, *j, *pk = (char*)base; + int num_trans = 0; + size_t k; + for( k=1; k < num; k++, pk += width ) { + /*for( i = pk, j = pk + width; j > (char*)base && (*compare)(i,j) > 0; j=i, i -= width )*/ + for( i = j = pk + width; + j > (char*)base && (i -= width,(*compare)(i,j, pCG)) > 0; + j=i ) /* changed to keep BoundsChecker happy 2007-09-24 DT */ + { + inchi_swap( i, j, width ); + num_trans ++; + } + } + return num_trans; +} + + +/**********************************************************************************/ +/* Sort by insertions */ +int insertions_sort_AT_NUMBERS( void *pCG, AT_NUMB *base, int num, int ( *compare )(const void *e1, const void *e2, void * ) ) +{ + AT_NUMB *i, *j, *pk, tmp; + int k, num_trans = 0; + for( k=1, pk = base; k < num; k++, pk ++ ) { + for( j = (i = pk) + 1, tmp = *j; j > base && (*compare)(i,&tmp, pCG) > 0; j=i, i -- ) { + *j = *i; + num_trans ++; + } + *j = tmp; + } + return num_trans; +} + + +/**********************************************************************************/ +/* Sort neighbors according to ranks in ascending order */ +void insertions_sort_NeighList_AT_NUMBERS( NEIGH_LIST base, AT_RANK *nRank ) +{ + AT_NUMB *i, *j, *pk, tmp; + AT_RANK rj; /* optimization */ + int k, num = (int)*base++; + for( k=1, pk = base; k < num; k++, pk ++ ) { + for( j = (i = pk) + 1, rj=nRank[(int)*j]; j > base && nRank[(int)*i] > rj; j=i, i -- ) { + tmp = *i; + *i = *j; + *j = tmp; + } + } +} + + +/**********************************************************************************/ +/* Sort neighbors according to ranks in ascending order */ +int insertions_sort_AT_RANK( AT_RANK *base, int num ) +{ + AT_RANK *i, *j, *pk, tmp; + int k, num_trans = 0; + for( k=1, pk = base; k < num; k++, pk ++ ) { + for( j = (i = pk) + 1, tmp = *j; j > base && *i > tmp; j=i, i -- ) { + *j = *i; + num_trans ++; + } + *j = tmp; + } + return num_trans; +} + + +/**********************************************************************************/ +/* Sort neighbors according to ranks in ascending order */ +int insertions_sort_NeighList_AT_NUMBERS3( NEIGH_LIST base, AT_RANK *nRank ) +{ + AT_NUMB *i, *j, *pk, tmp; + AT_RANK rj; + int k, n, num = (int)*base++; + for( k=1, pk = base, n=0; k < num; k++, pk ++ ) { + for( j = (i = pk) + 1, rj=nRank[(int)(tmp=*j)]; j > base && nRank[(int)*i] > rj; j=i, i -- ) { + *j = *i; + n ++; + } + *j = tmp; + } + return n; +} + +/**********************************************************************************/ +/* Sort neighbors according to symm. ranks (primary key) and canon. ranks (secondary key), in descending order */ +void insertions_sort_NeighListBySymmAndCanonRank( NEIGH_LIST base, const AT_RANK *nSymmRank, const AT_RANK *nCanonRank ) +{ + AT_NUMB *i, *j, *pk, tmp; + int diff; + int k, num = (int)*base++; + for( k=1, pk = base; k < num; k++, pk ++ ) { + for( j = (i = pk) + 1; j > base && /* always j > i */ + ( 0 > (diff = (int)nSymmRank[(int)*i] - (int)nSymmRank[(int)*j]) || + !diff && nCanonRank[(int)*i] < nCanonRank[(int)*j]); j=i, i -- ) { + tmp = *i; + *i = *j; + *j = tmp; + } + } +} + +/********************************************************************************************* + * + * Comparison functions + * + *********************************************************************************************/ + +int CompNeighborsAT_NUMBER( const void* a1, const void* a2, void *p) +{ + CANON_GLOBALS *pCG=(CANON_GLOBALS *) p; +#ifdef CT_NEIGH_INCREASE + return (int)pCG->m_pn_RankForSort[pCG->m_pNeighborsForSort[(int)*(const AT_NUMB*)a1]] - + (int)pCG->m_pn_RankForSort[pCG->m_pNeighborsForSort[(int)*(const AT_NUMB*)a2]]; +#else + return (int)((CANON_GLOBALS *) pCG)->m_pn_RankForSort[pNeighborsForSort[(int)*(const AT_NUMB*)a2]] - + (int)((CANON_GLOBALS *) pCG)->m_pn_RankForSort[pNeighborsForSort[(int)*(const AT_NUMB*)a1]]; +#endif +} + +/**********************************************************************************/ +int comp_AT_RANK( const void* a1, const void* a2, void *p) +{ + return (int)*(const AT_RANK*)a1 - (int)*(const AT_RANK*)a2; +} + +/**********************************************************************************/ +/* Compare for sorting Ranks only */ +int CompRank(const void* a1, const void* a2, void *p) +{ + CANON_GLOBALS *pCG=(CANON_GLOBALS *) p; + int ret = (int)pCG->m_pn_RankForSort[(int)*(const AT_RANK*)a1] - + (int)pCG->m_pn_RankForSort[(int)*(const AT_RANK*)a2]; + return ret; +} +/**********************************************************************************/ +int CompRanksOrd( const void* a1, const void* a2, void *p) +{ + int ret; + CANON_GLOBALS *pCG=(CANON_GLOBALS *) p; + ret = (int)pCG->m_pn_RankForSort[(int)*(const AT_RANK*)a1] - + (int)pCG->m_pn_RankForSort[(int)*(const AT_RANK*)a2]; + if ( !ret ) + ret = (int)*(const AT_RANK*)a1 - (int)*(const AT_RANK*)a2; + return ret; +} +/**********************************************************************************/ +int CompAtomInvariants2Only( const void* a1, const void* a2, void *p ) +{ + CANON_GLOBALS *pCG=(CANON_GLOBALS *) p; + const ATOM_INVARIANT2 *pAI1 = pCG->m_pAtomInvariant2ForSort + (int)*(const AT_RANK*)a1; + const ATOM_INVARIANT2 *pAI2 = pCG->m_pAtomInvariant2ForSort + (int)*(const AT_RANK*)a2; + int i; + for ( i = 0; i < AT_INV_BREAK1; i ++ ) { + if ( pAI1->val[i] == pAI2->val[i] ) + continue; + return (int)pAI1->val[i] - (int)pAI2->val[i]; + } + if ( pAI1->iso_sort_key != pAI2->iso_sort_key ) { + return ( pAI1->iso_sort_key > pAI2->iso_sort_key )? 1 : -1; + } + for ( ; i < AT_INV_LENGTH; i ++ ) { + if ( pAI1->val[i] != pAI2->val[i] ) + continue; + return (int)pAI1->val[i] - (int)pAI2->val[i]; + } + if ( pAI1->iso_aux_key != pAI2->iso_aux_key ) { + return ( pAI1->iso_aux_key > pAI2->iso_aux_key )? 1 : -1; + } + return 0; +} +/**********************************************************************************/ +int CompAtomInvariants2( const void* a1, const void* a2, void *p) +{ + /* Warning: the following line may be compiler implementation dependent */ + int ret = CompAtomInvariants2Only( a1, a2, p ); + if ( !ret ) + ret = (int)*(const AT_RANK*)a1 - (int)*(const AT_RANK*)a2; + return ret; +} + + +/**********************************************************************************/ +/* Compare two elements lexicographically */ +int CompChemElemLex( const void *a1, const void *a2 ) +{ + return memcmp( a1, a2, 2); +} + + +/**********************************************************************************/ +/* lexicographic compare */ +int CompareNeighListLex( NEIGH_LIST pp1, NEIGH_LIST pp2, const AT_RANK *nRank) +{ + int len1 = (int)*pp1++; + int len2 = (int)*pp2++; + int len = inchi_min( len1, len2 ); + int diff = 0; + while ( len -- > 0 && !( diff = (int)nRank[*pp1++] - (int)nRank[*pp2++] ) ) + ; + return diff? diff : (len1 - len2); +} + + +/**********************************************************************************/ +/* lexicographic compare */ +int CompareNeighListLexUpToMaxRank( NEIGH_LIST pp1, NEIGH_LIST pp2, const AT_RANK *nRank, AT_RANK nMaxAtNeighRank ) +{ + int len1 = (int)*pp1++; + int len2 = (int)*pp2++; + int diff = 0; + int len; + while( 0 < len1 && nRank[pp1[len1-1]] > nMaxAtNeighRank ) { + len1 --; + } + while( 0 < len2 && nRank[pp2[len2-1]] > nMaxAtNeighRank ) { + len2 --; + } + len = inchi_min( len1, len2 ); + while ( len -- > 0 && !( diff = (int)nRank[*pp1++] - (int)nRank[*pp2++] ) ) + ; + return diff? diff : (len1 - len2); +} + + +/**********************************************************************************/ +int compare_NeighLists( const NEIGH_LIST *op1, const NEIGH_LIST *op2, void *p ) +{ + CANON_GLOBALS *pCG=(CANON_GLOBALS *) p; + return CompareNeighListLex( *op1, *op2, pCG->m_pn_RankForSort); +} +/**********************************************************************************/ +int CompNeighListRanks( const void* a1, const void* a2, void *p ) +{ + CANON_GLOBALS *pCG=(CANON_GLOBALS *) p; + int ret; + ret = (int)pCG->m_pn_RankForSort[*((const AT_RANK*)a1)] - + (int)pCG->m_pn_RankForSort[*((const AT_RANK*)a2)]; + if ( !ret ) + ret = compare_NeighLists( pCG->m_pNeighList_RankForSort + *((const AT_RANK*)a1), + pCG->m_pNeighList_RankForSort + *((const AT_RANK*)a2), p ); + return ret; +} +/**********************************************************************************/ +int CompNeighLists( const void* a1, const void* a2, void *p ) +{ + CANON_GLOBALS *pCG=(CANON_GLOBALS *) p; + int ret; + ret = compare_NeighLists( pCG->m_pNeighList_RankForSort + *((const AT_RANK*)a1), + pCG->m_pNeighList_RankForSort + *((const AT_RANK*)a2), p ); + return ret; +} +/**********************************************************************************/ +int CompNeighListsUpToMaxRank( const void* a1, const void* a2, void *p ) +{ + CANON_GLOBALS *pCG=(CANON_GLOBALS *) p; + int ret; + ret = CompareNeighListLexUpToMaxRank( pCG->m_pNeighList_RankForSort[*((const AT_RANK*)a1)], + pCG->m_pNeighList_RankForSort[*((const AT_RANK*)a2)], + pCG->m_pn_RankForSort, pCG->m_nMaxAtNeighRankForSort ); + return ret; +} +/**********************************************************************************/ +int CompNeighListRanksOrd( const void* a1, const void* a2, void *p ) +{ + int ret = CompNeighListRanks( a1, a2, p ); + if ( !ret ) + ret = (int)*((const AT_RANK*)a1) - (int)*((const AT_RANK*)a2); /* keep original order if identical */ + return ret; +} +/**********************************************************************************/ +int CompRanksInvOrd( const void* a1, const void* a2, void *p) +{ + return (int)*(const AT_RANK*)a2 - (int)*(const AT_RANK*)a1; +} +/**********************************************************************************/ +int CompNeighborsRanksCountEql( const void* a1, const void* a2, void *p ) +{ + CANON_GLOBALS *pCG=(CANON_GLOBALS *) p; +#ifdef CT_NEIGH_INCREASE + int ret = (int)pCG->m_pn_RankForSort[(int)*(const AT_RANK*)a1] - + (int)pCG->m_pn_RankForSort[(int)*(const AT_RANK*)a2]; +#else + int ret = (int)pCG->m_pn_RankForSort[(int)*(const AT_RANK*)a2] - + (int)pCG->m_pn_RankForSort[(int)*(const AT_RANK*)a1]; +#endif + pCG->m_nNumCompNeighborsRanksCountEql += !ret; + return ret; +} + + +/**************************************************************************************** + * + * In this neighbor list the (vertex number) = (canonical number) - 1 + * Since LinearCT is sorted so that parents are in ascending order + * and all neighbors of a parent are smaller than the parent and are + * in ascending order, the neighbors in the NEIGH_LIST are automatically + * sorted in ascending order + */ +NEIGH_LIST *CreateNeighListFromLinearCT( AT_NUMB *LinearCT, int nLenCT, int num_atoms ) +{ + /* atom numbers in LinearCT are canonical numbers + * order: parent[i] > neigh[i][0] < neigh[i][1]... neigh[i+1][0] < ... + * parent[i] < parent[i+1] + */ + int i, j; + S_CHAR *valence = NULL; + NEIGH_LIST *pp = NULL; + AT_NUMB *pAtList = NULL; + AT_RANK n_vertex, n_neigh; + int err = 1, num_bonds; + int length, start; + if ( (int)LinearCT[0] > num_atoms ) { + goto exit_function; + } + if ( !(valence = (S_CHAR*)inchi_calloc( num_atoms+1, sizeof(valence[0]) ) ) ) { + goto exit_function; + } + for ( i = 1, num_bonds = 0, n_vertex = LinearCT[0]; i < nLenCT; i ++ ) { + if ( (n_neigh = LinearCT[i]) < n_vertex ) { + valence[n_neigh] ++; + valence[n_vertex] ++; + num_bonds += 2; + } else + if ( (int)(n_vertex = n_neigh) > num_atoms ) { + goto exit_function; + } + } + if ( (int)n_vertex != num_atoms ) { + goto exit_function; + } + length = num_bonds + num_atoms + 1; + if ( pp = (NEIGH_LIST *) inchi_calloc((num_atoms+1), sizeof(NEIGH_LIST)) ) { + if ( pAtList = (AT_NUMB *) inchi_malloc( length*sizeof(*pAtList) ) ) { + /* create empty connection table */ + for ( i = 1, length = 0; i <= num_atoms; i ++ ) { + start = length; + length += (valence[i]+1); + pp[i-1] = pAtList + start; + pp[i-1][0] = 0; + } + /* fill out the CT */ + for ( i = 1, n_vertex = LinearCT[0]-1; i < nLenCT; i ++ ) { + if ( (n_neigh = LinearCT[i]-1) < n_vertex ) { + /* vertex - neighbor connection */ + j = (int)(++pp[(int)n_vertex][0]); + pp[(int)n_vertex][j] = n_neigh; + /* neighbor - vertex connection */ + j = (int)(++pp[(int)n_neigh][0]); + pp[(int)n_neigh][j] = n_vertex; + } else + if ( (int)(n_vertex = n_neigh) >= num_atoms ) { + goto exit_function; + } + } + err = 0; + } + } +exit_function: + if ( valence ) { + inchi_free( valence ); + } + if ( err ) { + if ( pAtList ) + inchi_free( pAtList ); + if ( pp ) { + inchi_free( pp ); + pp = NULL; + } + } + return pp; +} + +/*********************************************************************************** + * NEIGH_LIST pp[] is an array of pointers to the lists of neighboring atoms numbers + * The first number in each list is a number of neighbors. + * In case of bDoubleBondSquare != 0 neighbors connected by the double bond appear 2 times + * The first element pp[0] is a pointer to be deallocated to free all the lists. + */ +NEIGH_LIST *CreateNeighList( int num_atoms, int num_at_tg, sp_ATOM* at, + int bDoubleBondSquare, T_GROUP_INFO *t_group_info ) +{ + /* +1 to add NULL termination */ + NEIGH_LIST *pp = (NEIGH_LIST *) inchi_calloc((num_at_tg+1), sizeof(NEIGH_LIST)); + T_GROUP *t_group = NULL; + AT_NUMB *nEndpointAtomNumber = NULL; + int num_t_groups = 0; + int nFirstEndpointAtNoPos; + + AT_NUMB *pAtList = NULL; + int length, start, val, i, j; + if ( pp ) { + if ( num_at_tg > num_atoms ) { + t_group = t_group_info->t_group; + num_t_groups = t_group_info->num_t_groups; + nEndpointAtomNumber = t_group_info->nEndpointAtomNumber; + } + + if ( !bDoubleBondSquare ) { + for ( i = 0, length = 0; i < num_atoms; i ++ ) { + length += (int)at[i].valence + (num_t_groups && at[i].endpoint); + } + length += num_atoms; + for ( i = 0; i < num_t_groups; i ++ ) { + length += (int)t_group[i].nNumEndpoints; + } + length += num_t_groups; + } else { + for ( i = 0, length = 0; i < num_atoms; i ++ ) { + val = (int)at[i].valence; + for ( j = 0; j < val; j ++ ) { + length += 1 + (bDoubleBondSquare && BOND_DOUBLE == at[i].bond_type[j]); + } + length += (num_t_groups && at[i].endpoint); + } + length += num_atoms; + for ( i = 0; i < num_t_groups; i ++ ) { + length += (int)t_group[i].nNumEndpoints; + } + length += num_t_groups; + } + length ++; /* +1 to save number of neighbors */ + if ( pAtList = (AT_NUMB *) inchi_malloc( length*sizeof(*pAtList) ) ) { + if ( !bDoubleBondSquare ) { + for ( i = 0, length = 0; i < num_atoms; i ++ ) { + val = at[i].valence; + start = length ++; + for ( j = 0; j < val; j ++ ) { + pAtList[length ++] = at[i].neighbor[j]; + } + /* add endpoint */ + if (num_t_groups && at[i].endpoint) { + pAtList[length ++] = num_atoms + (int)at[i].endpoint - 1; + } + pAtList[start] = length - start - 1; /* number of neighbors before the list of neighbors */ + pp[i] = pAtList + start; /* pointer to the */ + } + } else { + for ( i = 0, length = 0; i < num_atoms; i ++ ) { + val = at[i].valence; + start = length ++; + for ( j = 0; j < val; j ++ ) { + pAtList[length ++] = at[i].neighbor[j]; + if ( bDoubleBondSquare && BOND_DOUBLE == at[i].bond_type[j] ) { + pAtList[length ++] = at[i].neighbor[j]; /* a list of neighbor orig. numbers */ + } + } + /* add endpoint */ + if (num_t_groups && at[i].endpoint) { + pAtList[length ++] = num_atoms + (int)at[i].endpoint - 1; + } + pAtList[start] = length - start - 1; /* number of neighbors before the list of neighbors */ + pp[i] = pAtList + start; /* pointer to the */ + } + } + + /* add t-groups */ + for ( i = 0; i < num_t_groups; i ++ ) { + val = (int)t_group[i].nNumEndpoints; + start = length ++; + nFirstEndpointAtNoPos = (int)t_group[i].nFirstEndpointAtNoPos; + for ( j = 0; j < val; j ++ ) { + pAtList[length ++] = nEndpointAtomNumber[nFirstEndpointAtNoPos+j]; + } + pAtList[start] = length - start - 1; /* number of neighbors before the list of neighbors */ + pp[num_atoms+i] = pAtList + start; /* pointer to the */ + } + } else { + inchi_free ( pp ); + return NULL; + } + } + return pp; +} + + +/**********************************************************************************/ +void FreeNeighList( NEIGH_LIST *pp ) +{ + if ( pp ) { + if ( pp[0] ) { + inchi_free( pp[0] ); + } + inchi_free( pp ); + } +} + +/**********************************************************************************/ +int BreakAllTies( CANON_GLOBALS *pCG, + int num_atoms, + int num_max, + AT_RANK **pRankStack, + NEIGH_LIST *NeighList, + AT_RANK *nTempRank, + CANON_STAT *pCS) +{ + int i, nRet = -1, nNumRanks=1 /* value does not matter*/; + + AT_RANK *nPrevRank = *pRankStack ++; + AT_RANK *nPrevAtomNumber = *pRankStack ++; + + AT_RANK *nNewRank = NULL; + AT_RANK *nNewAtomNumber = NULL; + + if ( !pRankStack[0] ) { + pRankStack[0] = (AT_RANK *) inchi_malloc(num_max*sizeof(*nNewRank)); + } + if ( !pRankStack[1] ) { + pRankStack[1] = (AT_RANK *) inchi_malloc(num_max*sizeof(*nNewAtomNumber)); + } + if ( !pRankStack[0] || !pRankStack[1] ) + return CT_OUT_OF_RAM; /* */ + nNewRank = pRankStack[0]; + nNewAtomNumber = pRankStack[1]; + + if ( nNewRank && nNewAtomNumber ) { + memcpy( nNewAtomNumber, nPrevAtomNumber, num_atoms*sizeof(nNewAtomNumber[0])); + memcpy( nNewRank, nPrevRank, num_atoms*sizeof(nNewRank[0])); + + for ( i = 1, nRet=0; i < num_atoms; i ++ ) { /* 12-12-2001: replaced Prev... with New... */ + if ( nNewRank[(int)nNewAtomNumber[i-1]] == nNewRank[(int)nNewAtomNumber[i]] ) { + nNewRank[nNewAtomNumber[i-1]] = (AT_RANK)i; + nNumRanks = DifferentiateRanks2( pCG, num_atoms, NeighList, + nNumRanks, nNewRank, nTempRank, + nNewAtomNumber, + &pCS->lNumNeighListIter, 1 ); + pCS->lNumBreakTies ++; + nRet ++; + } + } + } + + return nRet; +} + + +/* + int insertions sort +*/ +int * iisort( int *list, int num ) +{ +int i; + for (i = 1; i < num; i++) + { + int tmp = list[i]; + int j = i - 1; + while ( j >= 0 && list[j] > tmp ) + { + list[j+1] = list[j]; + j--; + } + list[j+1] = tmp; + } + return list; +} diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ichister.c b/INCHI-1-SRC/INCHI_BASE/src/ichister.c similarity index 91% rename from INCHI-1-SRC/INCHI_API/inchi_dll/ichister.c rename to INCHI-1-SRC/INCHI_BASE/src/ichister.c index 654963a..65bac58 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ichister.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichister.c @@ -1,3870 +1,3959 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include - -#include "mode.h" - -#include "ichierr.h" -#include "inpdef.h" -#include "extr_ct.h" -#include "ichister.h" -#include "ichiring.h" -#include "ichi.h" - -#include "ichicomp.h" -#include "util.h" - -#define ZTYPE_DOWN (-1) /* should be equal to -ZTYPE_UP */ -#define ZTYPE_NONE 0 -#define ZTYPE_UP 1 /* should be equal to -ZTYPE_DOWN */ -#define ZTYPE_3D 3 -#define ZTYPE_EITHER 9999 - -/* criteria for ill-defined */ -#define MIN_ANGLE 0.10 /* 5.73 degrees */ -#define MIN_SINE 0.03 /* min edge/plane angle in case the tetrahedra has significantly different edge length */ -#define MIN_ANGLE_DBOND 0.087156 /* 5 degrees = max angle considered as too small for unambiguous double bond stereo */ -#define MIN_SINE_OUTSIDE 0.06 /* min edge/plane angle to determine whether the central atom is outside of the tetrahedra */ -#define MIN_SINE_SQUARE 0.125 /* min edge/plane angle in case the tetrahedra is somewhat close to a parallelogram */ -#define MIN_SINE_EDGE 0.167 /* min sine/(min.edge) ratio to avoid undefined in case of long edges */ -#define MIN_LEN_STRAIGHT 1.900 /* min length of two normalized to 1 bonds in a straight line */ -#define MAX_SINE 0.70710678118654752440084436210485 /* 1/sqrt(2)=sin(pi/4) */ -#define MIN_BOND_LEN 0.000001 -#define ZERO_LENGTH MIN_BOND_LEN -#define ZERO_FLOAT 1.0e-12 -#define BOND_PARITY_UNDEFINED 64 -#if ( STEREO_CENTER_BONDS_NORM == 1 ) -#define MPY_SINE 1.00 /* was 3.0 */ -#define MAX_EDGE_RATIO 2.50 /* max max/min edge ratio for a tetrahedra close to a parallelogram */ -#else -#define MPY_SINE 3.00 -#define MAX_EDGE_RATIO 6.00 /* max max/min edge ratio for a tetrahedra close to a parallelogram */ -#endif -/* local prototypes */ -static int save_a_stereo_bond( int z_prod, int result_action, - int at1, int ord1, AT_NUMB *stereo_bond_neighbor1, S_CHAR *stereo_bond_ord1, S_CHAR *stereo_bond_z_prod1, S_CHAR *stereo_bond_parity1, - int at2, int ord2, AT_NUMB *stereo_bond_neighbor2, S_CHAR *stereo_bond_ord2, S_CHAR *stereo_bond_z_prod2, S_CHAR *stereo_bond_parity2 ); -static double get_z_coord( inp_ATOM* at, int cur_atom, int neigh_no, int *nType,int bPointedEdgeStereo ); -static double len3( const double c[] ); -static double len2( const double c[] ); -static double* diff3( const double a[], const double b[], double result[] ); -static double* add3( const double a[], const double b[], double result[] ); -static double* mult3( const double a[], double b, double result[] ); -static double* copy3( const double a[], double result[] ); -static double* change_sign3( const double a[], double result[] ); -static double dot_prod3( const double a[], const double b[] ); -static int dot_prodchar3( const S_CHAR a[], const S_CHAR b[] ); -static double* cross_prod3( const double a[], const double b[], double result[] ); -static double triple_prod( double a[], double b[], double c[], double *sine_value ); -static double triple_prod_and_min_abs_sine(double at_coord[][3], double *min_sine); -static int are_3_vect_in_one_plane( double at_coord[][3], double min_sine); -static int triple_prod_char( inp_ATOM *at, int at_1, int i_next_at_1, S_CHAR *z_dir1, - int at_2, int i_next_at_2, S_CHAR *z_dir2 ); - -static int CompDble( const void *a1, const void *a2 ); -static int Get2DTetrahedralAmbiguity( double at_coord[][3], int bAddExplicitNeighbor, int bFix2DstereoBorderCase ); -static double triple_prod_and_min_abs_sine2(double at_coord[][3], double central_at_coord[], int bAddedExplicitNeighbor, double *min_sine, int *bAmbiguous); -static int are_4at_in_one_plane( double at_coord[][3], double min_sine); -static int bInpAtomHasRequirdNeigh ( inp_ATOM *at, int cur_at, int RequirdNeighType, int NumDbleBonds ); -static int bIsSuitableHeteroInpAtom( inp_ATOM *at ); -static int bIsOxide( inp_ATOM *at, int cur_at ); -static int half_stereo_bond_parity( inp_ATOM *at, int cur_at, inp_ATOM *at_removed_H, int num_removed_H, S_CHAR *z_dir, - int bPointedEdgeStereo, int vABParityUnknown ); -static int get_allowed_stereo_bond_type( int bond_type ); -static int can_be_a_stereo_bond_with_isotopic_H( inp_ATOM *at, int cur_at, INCHI_MODE nMode ); -static int half_stereo_bond_action( int nParity, int bUnknown, int bIsotopic, int vABParityUnknown ); -static int set_stereo_bonds_parity( sp_ATOM *out_at, inp_ATOM *at, int at_1, inp_ATOM *at_removed_H, int num_removed_H, - INCHI_MODE nMode, QUEUE *q, AT_RANK *nAtomLevel, - S_CHAR *cSource, AT_RANK min_sb_ring_size, - int bPointedEdgeStereo, int vABParityUnknown ); -static int can_be_a_stereo_atom_with_isotopic_H( inp_ATOM *at, int cur_at, int bPointedEdgeStereo ); -static int set_stereo_atom_parity( sp_ATOM *out_at, inp_ATOM *at, int cur_at, inp_ATOM *at_removed_H, int num_removed_H, - int bPointedEdgeStereo, int vABParityUnknown ); -/* -int set_stereo_parity( inp_ATOM* at, sp_ATOM* at_output, int num_at, int num_removed_H, - int *nMaxNumStereoAtoms, int *nMaxNumStereoBonds, INCHI_MODE nMode, int bPointedEdgeStereo, vABParityUnknown ); -int get_opposite_sb_atom( inp_ATOM *at, int cur_atom, int icur2nxt, int *pnxt_atom, int *pinxt2cur, int *pinxt_sb_parity_ord ); -*/ -int ReconcileCmlIncidentBondParities( inp_ATOM *at, int cur_atom, int prev_atom, S_CHAR *visited, int bDisconnected ); -int comp_AT_NUMB( const void* a1, const void* a2); -int GetHalfStereobond0DParity( inp_ATOM *at, int cur_at, AT_NUMB nSbNeighOrigAtNumb[], int nNumExplictAttachments, int bond_parity, int nFlag ); -int GetStereocenter0DParity( inp_ATOM *at, int cur_at, int j1, AT_NUMB nSbNeighOrigAtNumb[], int nFlag ); -int GetSbNeighOrigAtNumb( inp_ATOM *at, int cur_at, inp_ATOM *at_removed_H, int num_removed_H, AT_NUMB nSbNeighOrigAtNumb[]); -int FixSb0DParities( inp_ATOM *at, /* inp_ATOM *at_removed_H, int num_removed_H,*/ int chain_length, - int at_1, int i_next_at_1, S_CHAR z_dir1[], - int at_2, int i_next_at_2, S_CHAR z_dir2[], - int *pparity1, int *pparity2 ); - -/******************************************************************/ - - -static double *pDoubleForSort; - -/**********************************************************************************/ -int comp_AT_NUMB( const void* a1, const void* a2) -{ - return (int)*(const AT_NUMB*)a1 - (int)*(const AT_NUMB*)a2; -} -/******************************************************************/ -double get_z_coord( inp_ATOM* at, int cur_atom, int neigh_no, int *nType, int bPointedEdgeStereo ) -{ - int stereo_value = at[cur_atom].bond_stereo[neigh_no]; - int stereo_type = abs( stereo_value ); - int neigh = (int)at[cur_atom].neighbor[neigh_no]; - double z = at[neigh].z - at[cur_atom].z; - int bFlat; - - if ( bFlat = (fabs(z) < ZERO_LENGTH) ) { - int i; - for ( i = 0; i < at[cur_atom].valence; i ++ ) { - if ( fabs(at[cur_atom].z - at[(int)at[cur_atom].neighbor[i]].z) > ZERO_LENGTH ) { - bFlat = 0; - break; - } - } - } - - if ( bFlat ) { - if ( !bPointedEdgeStereo || bPointedEdgeStereo * stereo_value >= 0 ) { - /* bPointedEdgeStereo > 0: define stereo from pointed end of the stereo bond only */ - /* bPointedEdgeStereo < 0: define stereo from wide end of the stereo bond only (case of removed H) */ - switch( stereo_type ) { - /* 1=Up (solid triangle), 6=Down (Dashed triangle), 4=Either (zigzag triangle) */ - case 0: /* No stereo */ - *nType = ZTYPE_NONE; - break; - case STEREO_SNGL_UP: /* 1= Up */ - *nType = ZTYPE_UP; - break; - case STEREO_SNGL_EITHER: /* 4 = Either */ - *nType = ZTYPE_EITHER; - break; - case STEREO_SNGL_DOWN: /* 6 = Down */ - *nType = ZTYPE_DOWN; - break; - default: - *nType = ZTYPE_NONE; /* ignore unexpected values */ - } - if ( stereo_value < 0 && (*nType == ZTYPE_DOWN || *nType == ZTYPE_UP) ) - *nType = -*nType; - } else { - *nType = ZTYPE_NONE; /* no stereo */ - } - } else - if ( stereo_type == STEREO_SNGL_EITHER && - ( !bPointedEdgeStereo || bPointedEdgeStereo * stereo_value >= 0 ) ) { - *nType = ZTYPE_EITHER; - } else { - *nType = ZTYPE_3D; - } - return z; -} -/******************************************************************/ -double len3( const double c[] ) -{ - return sqrt( c[0]*c[0] + c[1]*c[1] + c[2]*c[2] ); -} -/******************************************************************/ -double len2( const double c[] ) -{ - return sqrt( c[0]*c[0] + c[1]*c[1] ); -} -/******************************************************************/ -double* diff3( const double a[], const double b[], double result[] ) -{ - - result[0] = a[0] - b[0]; - result[1] = a[1] - b[1]; - result[2] = a[2] - b[2]; - - return result; -} -/******************************************************************/ -double* add3( const double a[], const double b[], double result[] ) -{ - result[0] = a[0] + b[0]; - result[1] = a[1] + b[1]; - result[2] = a[2] + b[2]; - - return result; -} -/******************************************************************/ -double* mult3( const double a[], double b, double result[] ) -{ - result[0] = a[0] * b; - result[1] = a[1] * b; - result[2] = a[2] * b; - - return result; -} -/*************************************************************/ -double* copy3( const double a[], double result[] ) -{ - result[0] = a[0]; - result[1] = a[1]; - result[2] = a[2]; - - return result; -} -/*************************************************************/ -double* change_sign3( const double a[], double result[] ) -{ - result[0] = -a[0]; - result[1] = -a[1]; - result[2] = -a[2]; - - return result; -} -/*************************************************************/ -double dot_prod3( const double a[], const double b[] ) -{ - return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; -} -/*************************************************************/ -int dot_prodchar3( const S_CHAR a[], const S_CHAR b[] ) -{ - int prod = ((int)a[0]*(int)b[0] + (int)a[1]*(int)b[1] + (int)a[2]*(int)b[2])/100; - if ( prod > 100 ) - prod = 100; - else - if ( prod < -100 ) - prod = -100; - return prod; -} -/*************************************************************/ -double* cross_prod3( const double a[], const double b[], double result[] ) -{ - double tmp[3]; - - tmp[0] = (a[1]*b[2]-a[2]*b[1]); - tmp[1] = -(a[0]*b[2]-a[2]*b[0]); - tmp[2] = (a[0]*b[1]-a[1]*b[0]); - - result[0] = tmp[0]; - result[1] = tmp[1]; - result[2] = tmp[2]; - - return result; -} -/*************************************************************/ -double triple_prod( double a[], double b[], double c[], double *sine_value ) -{ - double ab[3], dot_prod_ab_c, abs_c, abs_ab; - cross_prod3( a, b, ab ); - /* ab[0] = (a[1]*b[2]-a[2]*b[1]); */ - /* ab[1] = -(a[0]*b[2]-a[2]*b[0]); */ - /* ab[2] = (a[0]*b[1]-a[1]*b[0]); */ - dot_prod_ab_c = dot_prod3( ab, c ); - /* dot_prod_ab_c = ab[0]*c[0] + ab[1]*c[1] + ab[2]*c[2]; */ - if ( sine_value ) { - abs_c = len3( c ); - /* abs_c = sqrt( c[0]*c[0] + c[1]*c[1] + c[2]*c[2] ); */ - abs_ab = len3( ab ); - /* abs_ab = sqrt( ab[0]*ab[0] + ab[1]*ab[1] + ab[2]*ab[2] ); */ - - if ( abs_c > 1.e-7 /* otherwise c has zero length */ && abs_ab > 1.e-7 /* otherwise a is parallel to b*/ ) { - *sine_value = MPY_SINE * dot_prod_ab_c / ( abs_c * abs_ab); - /* *sine_value = dot_prod_ab_c / ( abs_c * abs_ab); */ - } else { - *sine_value = 0.0; - } - } - return dot_prod_ab_c; -} -/*************************************************************/ -int CompDble( const void *a1, const void *a2 ) -{ - double diff = pDoubleForSort[*(const int*)a1] - pDoubleForSort[*(const int*)a2]; - if ( diff > 0.0 ) - return 1; - if ( diff < 0.0 ) - return -1; - return 0; -} -/*************************************************************/ -#define T2D_OKAY 1 -#define T2D_WARN 2 -#define T2D_UNDF 4 -int Get2DTetrahedralAmbiguity( double at_coord[][3], int bAddExplicitNeighbor, int bFix2DstereoBorderCase ) -{ -/* const double one_pi = 2.0*atan2(1.0 , 0.0 ); */ -const double one_pi = 3.14159265358979323846; /* M_PI */ -const double two_pi = 2.0*one_pi; -const double dAngleAndPiMaxDiff = 2.0*atan2(1.0, sqrt(7.0)); /* min sine between 2 InPlane bonds */ -int nBondType[MAX_NUM_STEREO_ATOM_NEIGH], nBondOrder[MAX_NUM_STEREO_ATOM_NEIGH]; -double dBondDirection[MAX_NUM_STEREO_ATOM_NEIGH]; -volatile double dAngle, dAlpha, dLimit, dBisector; - /* 2010-02-10 added 'volatile': workaround ensuring proper behavior for gcc 32-bit */ - /* cml-enabled compiles at >=O1 for SID484922 and alike (both lin&win had problems) */ -int nNumNeigh = MAX_NUM_STEREO_ATOM_NEIGH - (bAddExplicitNeighbor != 0); -int i, num_Up, num_Dn, bPrev_Up, cur_len_Up, cur_first_Up, len_Up, first_Up; -int ret=0; - - for ( i = 0, num_Up = num_Dn = 0; i < nNumNeigh; i ++ ) - { - dAngle = atan2( at_coord[i][1], at_coord[i][0] ); /* range from -pi to +pi */ - if ( dAngle < 0.0 ) { - dAngle += two_pi; - } - dBondDirection[i] = dAngle; - nBondType[i] = (at_coord[i][2] > 0.0)? 1 : (at_coord[i][2] < 0.0)? -1 : 0; /* z-coord sign */ - if ( nBondType[i] > 0 ) { - num_Up ++; - } else - if ( nBondType[i] < 0 ) { - num_Dn ++; - } - nBondOrder[i] = i; - } - if ( num_Up < num_Dn ) { - for ( i = 0; i < nNumNeigh; i ++ ) { - nBondType[i] = -nBondType[i]; - } - inchi_swap( (char*)&num_Dn, (char*)&num_Up, sizeof(num_Dn) ); - } - if ( !num_Up ) { - return T2D_UNDF; - } - - /* sort according to the bond orientations */ - pDoubleForSort = dBondDirection; - insertions_sort( nBondOrder, (unsigned) nNumNeigh, sizeof(nBondOrder[0]), CompDble ); - - /* find the longest contiguous sequence of Up bonds */ - if ( num_Up == nNumNeigh ) { - /* all bonds are Up */ - len_Up = cur_len_Up = nNumNeigh; /* added cur_len_Up initialization 1/8/2002 */ - first_Up = 0; - } else { - /* at least one bond is not Up */ - cur_len_Up = len_Up = bPrev_Up = 0; - /* prev. cycle header version --- - for ( i = 0; 1; i ++ ) { - if ( i >= nNumNeigh && !bPrev_Up ) { - break; - } - ----------} */ - /* look at all bonds and continue (circle therough the beginning) as long as the current bond is Up */ - for ( i = 0; i < nNumNeigh || bPrev_Up; i ++ ) { - if ( nBondType[nBondOrder[i % nNumNeigh]] > 0 ) { - if ( bPrev_Up ) { - cur_len_Up ++; /* uncrement number of Up bonds in current contiguous sequence of them */ - } else { - bPrev_Up = 1; /* start new contiguous sequence of Up bonds */ - cur_len_Up = 1; - cur_first_Up = i % nNumNeigh; - } - } else - if ( bPrev_Up ) { /* end of contiguous sequence of Up bonds */ - if ( cur_len_Up > len_Up ) { - first_Up = cur_first_Up; /* store the sequence because it is longer than the ptrvious one */ - len_Up = cur_len_Up; - } - bPrev_Up = 0; - } - } - } -#if ( FIX_2D_STEREO_BORDER_CASE == 1 ) - /* check if the bonds with ordering numbers first_Up+len_Up and first_Up+len_Up+1 */ - /* have identical angles. In this case switch their order to enlarge the Up sequence */ -#define ZERO_ANGLE 0.000001 - if ( nNumNeigh - len_Up >= 2 ) { - int next1, next2; - for ( i = 1; i < nNumNeigh - len_Up; i ++ ) { - next2 = (first_Up+len_Up + i) % nNumNeigh; /* the 2nd after Up sequence */ - if ( nBondType[nBondOrder[next2]] > 0 ) { - next1 = (first_Up+len_Up) % nNumNeigh; /* the 1st after Up sequence */ - dAngle = dBondDirection[nBondOrder[next1]] - dBondDirection[nBondOrder[next2]]; - if ( fabs(dAngle) < ZERO_ANGLE ) { - inchi_swap( (char*)&nBondOrder[next1], (char*)&nBondOrder[next2], sizeof(nBondOrder[0]) ); - len_Up ++; - break; - } - } - } - } - /* check whether the not-Up bond (located before the found first-Up) has */ - /* same angle as the Up bond that precedes this not-Up bond */ - if ( nNumNeigh - len_Up >= 2 ) { - int next1, next2; - for ( i = 1; i < nNumNeigh - len_Up; i ++ ) { - next2 = (first_Up+nNumNeigh - i - 1 ) % nNumNeigh; /* the 2nd before Up sequence */ - if ( nBondType[nBondOrder[next2]] > 0 ) { - next1 = (first_Up+nNumNeigh-1) % nNumNeigh; /* the 1st before Up sequence */ - dAngle = dBondDirection[nBondOrder[next1]] - dBondDirection[nBondOrder[next2]]; - if ( fabs(dAngle) < ZERO_ANGLE ) { - inchi_swap( (char*)&nBondOrder[next1], (char*)&nBondOrder[next2], sizeof(nBondOrder[0]) ); - first_Up = next1; - len_Up ++; - break; - } - } - } - } -#else - if ( bFix2DstereoBorderCase ) { - /* check if the bonds with ordering numbers first_Up+len_Up and first_Up+len_Up+1 */ - /* have identical angles. In this case switch their order to enlarge the Up sequence */ -#define ZERO_ANGLE 0.000001 - if ( nNumNeigh - len_Up >= 2 ) { - int next1, next2; - for ( i = 1; i < nNumNeigh - len_Up; i ++ ) { - next2 = (first_Up+len_Up + i) % nNumNeigh; /* the 2nd after Up sequence */ - if ( nBondType[nBondOrder[next2]] > 0 ) { - next1 = (first_Up+len_Up) % nNumNeigh; /* the 1st after Up sequence */ - dAngle = dBondDirection[nBondOrder[next1]] - dBondDirection[nBondOrder[next2]]; - if ( fabs(dAngle) < ZERO_ANGLE ) { - inchi_swap( (char*)&nBondOrder[next1], (char*)&nBondOrder[next2], sizeof(nBondOrder[0]) ); - len_Up ++; - break; - } - } - } - } - /* check whether the not-Up bond (located before the found first-Up) has */ - /* same angle as the Up bond that precedes this not-Up bond */ - if ( nNumNeigh - len_Up >= 2 ) { - int next1, next2; - for ( i = 1; i < nNumNeigh - len_Up; i ++ ) { - next2 = (first_Up+nNumNeigh - i - 1 ) % nNumNeigh; /* the 2nd before Up sequence */ - if ( nBondType[nBondOrder[next2]] > 0 ) { - next1 = (first_Up+nNumNeigh-1) % nNumNeigh; /* the 1st before Up sequence */ - dAngle = dBondDirection[nBondOrder[next1]] - dBondDirection[nBondOrder[next2]]; - if ( fabs(dAngle) < ZERO_ANGLE ) { - inchi_swap( (char*)&nBondOrder[next1], (char*)&nBondOrder[next2], sizeof(nBondOrder[0]) ); - first_Up = next1; - len_Up ++; - break; - } - } - } - } - } -#endif - /* Turn all the bonds around the center so that */ - /* the 1st Up bond has zero radian direction */ - dAlpha = dBondDirection[nBondOrder[first_Up]]; - for ( i = 0; i < nNumNeigh; i ++ ) { - if ( i == nBondOrder[first_Up] ) { - dBondDirection[i] = 0.0; - } else { - dAngle = dBondDirection[i] - dAlpha; - if ( dAngle < 0.0 ) { - dAngle += two_pi; - } - dBondDirection[i] = dAngle; - } - } - - /******************************************************** - * Process particular cases - ********************************************************/ - - - if ( nNumNeigh == 3 ) /************************ 3 bonds ************************/ - { - - switch( num_Up ) - { - - - - case 0: /* 0 Up */ - return T2D_UNDF; - - - - - case 1: /* 1 Up */ - if ( num_Dn ) - { -#ifdef _DEBUG - if ( num_Dn != 1 ) /* debug only */ - return -1; -#endif - ret = (T2D_UNDF | T2D_WARN); - } - else - { - dAngle = dBondDirection[nBondOrder[(first_Up + 2) % nNumNeigh]] - - dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; - - if ( dAngle < 0.0 ) - dAngle += two_pi; - if ( dAngle - one_pi < -MIN_ANGLE || dAngle - one_pi > MIN_ANGLE ) - { - ret = T2D_OKAY; - } - else - { - ret = (T2D_UNDF | T2D_WARN); - } - } - break; - - - - - case 2: /* 2 Up */ - if ( num_Dn ) - { - dAlpha = dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]] - - dBondDirection[nBondOrder[(first_Up ) % nNumNeigh]]; - - if ( dAlpha < 0.0 ) - dAlpha += two_pi; - - if ( dAlpha > one_pi - MIN_ANGLE ) - { - ret = T2D_OKAY; - } - else if ( dAlpha < two_pi / 3.0 - MIN_ANGLE ) - { - ret = (T2D_UNDF | T2D_WARN); - } - else - { - /* angle between 2 Up bonds is between 120 and 180 degrees */ - /* direction of the (Alpha angle bisector) + 180 degrees */ - dBisector = dBondDirection[nBondOrder[(first_Up ) % nNumNeigh]]; - dBisector+= dBondDirection[nBondOrder[(first_Up + 1 ) % nNumNeigh]]; - dBisector/= 2.0; - dBisector-= one_pi; - if ( dBisector < 0.0 ) - { - dBisector += two_pi; - } - if ( dAlpha < two_pi / 3.0 + MIN_ANGLE ) - { - /* dAlpha is inside ( 2pi/3 - eps, 2pi/3 + eps ) interval */ - dLimit = MIN_ANGLE * 3.0 / 2.0; - } - else - { - dLimit = dAlpha * 3.0 / 2.0 - one_pi; - } - - dAngle = dBondDirection[nBondOrder[(first_Up + 2 ) % nNumNeigh]]; - - if ( dBisector - dAngle < -dLimit || - dBisector - dAngle > dLimit ) - { - ret = (T2D_UNDF | T2D_WARN); - } - else - { - ret = T2D_OKAY; - } - } - } /* if ( num_Dn ) */ - else - { - ret = T2D_OKAY; - } - break; - - - - case 3: /* 3 Up */ - ret = T2D_OKAY; - break; - - - default:/* other Up */ - return -1; - - } /* eof switch( num_Up ) at nNumNeigh == 3 */ - - } - - - else if ( nNumNeigh == 4) /******************************* 4 bonds ********************/ - { - switch( num_Up ) - { - - case 0: /* 0 Up */ - return T2D_UNDF; - - - case 1: /* 1 Up */ - if ( num_Dn ) - { - if ( nBondType[nBondOrder[(first_Up + 2) % nNumNeigh]] < 0 ) - { - /* - * Up, In Plane, Dn, In Plane. Undefined if angle between - * two In Plane bonds is wuthin pi +/- 2*arcsine(1/sqrt(8)) interval - * That is, 138.5 to 221.4 degrees; for certainty the interval is - * increased by 5.7 degrees at each end to - * 134.8 to 227.1 degrees - */ - dAngle = dBondDirection[nBondOrder[(first_Up + 3) % nNumNeigh]] - - dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; - if ( dAngle < 0.0 ) { - dAngle += two_pi; - } - if ( fabs( dAngle - one_pi ) < dAngleAndPiMaxDiff + MIN_ANGLE ) { - ret = (T2D_UNDF | T2D_WARN); - } - else - { - ret = T2D_OKAY; - } - } - else - { - ret = T2D_OKAY; - } -#ifdef _DEBUG - if ( num_Dn != 1 ) /* debug only */ - return -1; -#endif - } - else - { - ret = T2D_OKAY; - dAngle = dBondDirection[nBondOrder[(first_Up + 3) % nNumNeigh]] - - dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; - if ( dAngle < 0.0 ) - { - dAngle += two_pi; - } - if ( dAngle < one_pi - MIN_ANGLE ) - { - ret |= T2D_WARN; - } - } - break; - - - case 2: /* 2 Up */ -#if ( FIX_2D_STEREO_BORDER_CASE == 1 ) - if ( len_Up == 1 ) - { - ret = T2D_OKAY; - } - else - { - dAngle = dBondDirection[nBondOrder[(first_Up + 3) % nNumNeigh]] - - dBondDirection[nBondOrder[(first_Up + 0) % nNumNeigh]]; - dAngle = fabs(two_pi - dAngle); - dAlpha = dBondDirection[nBondOrder[(first_Up + 2) % nNumNeigh]] - - dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; - dAlpha = fabs(dAlpha); - if ( dAngle < 2.0 * ZERO_ANGLE && dAlpha > MIN_ANGLE || - dAlpha < 2.0 * ZERO_ANGLE && dAngle > MIN_ANGLE ) - { - ret = (T2D_OKAY | T2D_WARN); - } - else - { - ret = (T2D_UNDF | T2D_WARN); - } - } -#else - if ( bFix2DstereoBorderCase ) - { - /* bug fix */ - if ( len_Up == 1 ) - { - ret = T2D_OKAY; - } - else - { - dAngle = dBondDirection[nBondOrder[(first_Up + 3) % nNumNeigh]] - - dBondDirection[nBondOrder[(first_Up + 0) % nNumNeigh]]; - dAngle = fabs(two_pi - dAngle); - dAlpha = dBondDirection[nBondOrder[(first_Up + 2) % nNumNeigh]] - - dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; - dAlpha = fabs(dAlpha); - if ( dAngle < 2.0 * ZERO_ANGLE && dAlpha > MIN_ANGLE || - dAlpha < 2.0 * ZERO_ANGLE && dAngle > MIN_ANGLE ) - { - ret = (T2D_OKAY | T2D_WARN); - } - else - { - ret = (T2D_UNDF | T2D_WARN); - } - } - } - else - { - /* original InChI v. 1 bug */ - if ( cur_len_Up == 1 ) - { - ret = T2D_OKAY; - } - else - { - ret = (T2D_UNDF | T2D_WARN); - } - } -#endif - break; - - - case 3: /* 3 Up */ - ret = T2D_OKAY; - dAngle = dBondDirection[nBondOrder[(first_Up + 2) % nNumNeigh]] - - dBondDirection[nBondOrder[(first_Up + 0) % nNumNeigh]]; - if ( dAngle < 0.0 ) - { - dAngle += two_pi; - } - if ( dAngle < one_pi - MIN_ANGLE ) - { - ret |= T2D_WARN; - } - break; - - case 4: /* 4 Up */ - ret = (T2D_UNDF | T2D_WARN); - break; - - default:/* other Up */ - return -1; /* program error */ - - } /* eof switch( num_Up ) at nNumNeigh == 4 */ - - if ( ret == T2D_OKAY ) - { - /* check whether all bonds are inside a less than 180 degrees sector */ - for ( i = 0; i < nNumNeigh; i ++ ) - { - dAngle = dBondDirection[nBondOrder[(i + nNumNeigh - 1) % nNumNeigh]] - - dBondDirection[nBondOrder[ i % nNumNeigh]]; - if ( dAngle < 0.0 ) - { - dAngle += two_pi; - } - if ( dAngle < one_pi - MIN_ANGLE ) - { - ret |= T2D_WARN; - break; - } - } - } - - } /* eof nNumNeigh == 4 */ - - else /*************************** number of bonds != 3 or 4 ******************/ - { - - return -1; /* error */ - } - - - return ret; - -} - -/*************************************************************/ -double triple_prod_and_min_abs_sine2(double at_coord[][3], double central_at_coord[], int bAddedExplicitNeighbor, double *min_sine, int *bAmbiguous) -{ - double min_sine_value=9999.0, sine_value, min_edge_len, max_edge_len, min_edge_len_NoExplNeigh, max_edge_len_NoExplNeigh; - double s0, s1, s2, s3, e01, e02, e03, e12, e13, e23, tmp[3], e[3][3]; - double prod, ret, central_prod[4]; - int bLongEdges; - - if ( !min_sine ) { - return triple_prod( at_coord[0], at_coord[1], at_coord[2], NULL ); - } - - ret = triple_prod( at_coord[0], at_coord[1], at_coord[2], &sine_value ); - sine_value = MPY_SINE * fabs( sine_value ); - - diff3( at_coord[1], at_coord[0], e[2] ); - diff3( at_coord[0], at_coord[2], e[1] ); - diff3( at_coord[2], at_coord[1], e[0] ); - - /* lengths of the 6 edges of the tetrahedra */ - e03 = len3( at_coord[0] ); /* 1 */ - e13 = len3( at_coord[1] ); - e23 = len3( at_coord[2] ); /* includes added neighbor if bAddedExplicitNeighbor*/ - e02 = len3( e[1] ); /* includes added neighbor if bAddedExplicitNeighbor*/ - e12 = len3( e[0] ); /* includes added neighbor if bAddedExplicitNeighbor*/ - e01 = len3( e[2] ); - - /* min & max edge length */ - max_edge_len = - min_edge_len = e03; - - if ( min_edge_len > e13 ) - min_edge_len = e13; - if ( min_edge_len > e01 ) - min_edge_len = e01; - min_edge_len_NoExplNeigh = min_edge_len; - - if ( min_edge_len > e23 ) - min_edge_len = e23; - if ( min_edge_len > e02 ) - min_edge_len = e02; - if ( min_edge_len > e12 ) - min_edge_len = e12; - - if ( max_edge_len < e13 ) - max_edge_len = e13; - if ( max_edge_len < e01 ) - max_edge_len = e01; - max_edge_len_NoExplNeigh = max_edge_len; - - if ( max_edge_len < e23 ) - max_edge_len = e23; - if ( max_edge_len < e02 ) - max_edge_len = e02; - if ( max_edge_len < e12 ) - max_edge_len = e12; - - if ( !bAddedExplicitNeighbor ) { - min_edge_len_NoExplNeigh = min_edge_len; - max_edge_len_NoExplNeigh = max_edge_len; - } - - bLongEdges = bAddedExplicitNeighbor? - ( max_edge_len_NoExplNeigh < MAX_EDGE_RATIO * min_edge_len_NoExplNeigh ) : - ( max_edge_len < MAX_EDGE_RATIO * min_edge_len ); - - if ( sine_value > MIN_SINE && ( min_sine || bAmbiguous ) ) { - if ( min_sine ) { - prod = fabs( ret ); - /* tetrahedra height = volume(prod) / area of a plane(cross_prod) */ - /* (instead of a tetrahedra calculate parallelogram/parallelepiped area/volume) */ - - /* 4 heights from each of the 4 vertices to the opposite plane */ - s0 = prod / len3( cross_prod3( at_coord[1], at_coord[2], tmp ) ); - s1 = prod / len3( cross_prod3( at_coord[0], at_coord[2], tmp ) ); - s2 = prod / len3( cross_prod3( at_coord[0], at_coord[1], tmp ) ); - s3 = prod / len3( cross_prod3( e[0], e[1], tmp ) ); - /* abs. value of a sine of an angle between each tetrahedra edge and plane */ - /* sine = height / edge length */ - if ( (sine_value = s0/e01) < min_sine_value ) - min_sine_value = sine_value; - if ( (sine_value = s0/e02) < min_sine_value ) - min_sine_value = sine_value; - if ( (sine_value = s0/e03) < min_sine_value ) - min_sine_value = sine_value; - - if ( (sine_value = s1/e01) < min_sine_value ) - min_sine_value = sine_value; - if ( (sine_value = s1/e12) < min_sine_value ) - min_sine_value = sine_value; - if ( (sine_value = s1/e13) < min_sine_value ) - min_sine_value = sine_value; - - if ( (sine_value = s2/e02) < min_sine_value ) - min_sine_value = sine_value; - if ( (sine_value = s2/e12) < min_sine_value ) - min_sine_value = sine_value; - if ( (sine_value = s2/e23) < min_sine_value ) - min_sine_value = sine_value; - - if ( (sine_value = s3/e03) < min_sine_value ) - min_sine_value = sine_value; - if ( (sine_value = s3/e13) < min_sine_value ) - min_sine_value = sine_value; - if ( (sine_value = s3/e23) < min_sine_value ) - min_sine_value = sine_value; - /* actually use triple sine */ - *min_sine = sine_value = MPY_SINE * min_sine_value; - } - - if ( bAmbiguous && sine_value >= MIN_SINE ) { - /* check whether the central atom is outside the tetrahedra (0,0,0), at_coord[0,1,2] */ - /* compare the tetrahedra volume and the volume of a tetrahedra having central_at_coord[] vertex */ - int i; - diff3( central_at_coord, at_coord[0], tmp ); - central_prod[0] = triple_prod( at_coord[0], at_coord[1], central_at_coord, NULL ); - central_prod[1] = triple_prod( at_coord[1], at_coord[2], central_at_coord, NULL ); - central_prod[2] = triple_prod( at_coord[2], at_coord[0], central_at_coord, NULL ); - central_prod[3] = triple_prod( e[2], e[1], tmp, NULL ); - for ( i = 0; i <= 3; i ++ ) { - if ( central_prod[i] / ret < -MIN_SINE_OUTSIDE ) { - *bAmbiguous |= AMBIGUOUS_STEREO; - break; - } - } - } -#if ( STEREO_CENTER_BONDS_NORM == 1 ) - - if ( bLongEdges && !bAddedExplicitNeighbor && max_edge_len >= MIN_LEN_STRAIGHT ) { - /* possible planar tetragon */ - if ( sine_value < MIN_SINE_SQUARE ) { - *min_sine = MIN_SINE / 2.0; /* force parity to be undefined */ - if ( bAmbiguous && !*bAmbiguous ) { - *bAmbiguous |= AMBIGUOUS_STEREO; - } - } - } - - if ( bLongEdges && sine_value < MIN_SINE_SQUARE && sine_value < MIN_SINE_EDGE * min_edge_len_NoExplNeigh ) { - *min_sine = MIN_SINE / 2.0; /* force parity to be undefined */ - if ( bAmbiguous && !*bAmbiguous ) { - *bAmbiguous |= AMBIGUOUS_STEREO; - } - } -#endif - - } else - if ( min_sine ) { - *min_sine = sine_value; - } - - return ret; -} -/*************************************************************/ -double triple_prod_and_min_abs_sine(double at_coord[][3], double *min_sine) -{ - double min_sine_value=9999.0, sine_value; - double prod=0.0; - - if ( !min_sine ) { - return triple_prod( at_coord[0], at_coord[1], at_coord[2], NULL ); - } - - prod = triple_prod( at_coord[0], at_coord[1], at_coord[2], &sine_value ); - sine_value = fabs( sine_value ); - min_sine_value = inchi_min( min_sine_value, sine_value ); - - prod = triple_prod( at_coord[1], at_coord[2], at_coord[0], &sine_value ); - sine_value = fabs( sine_value ); - min_sine_value = inchi_min( min_sine_value, sine_value ); - - prod = triple_prod( at_coord[2], at_coord[0], at_coord[1], &sine_value ); - sine_value = fabs( sine_value ); - min_sine_value = inchi_min( min_sine_value, sine_value ); - - *min_sine = min_sine_value; - - return prod; -} -/*************************************************************/ -/* Find if point (0,0,0)a and 3 atoms are in one plane */ -int are_3_vect_in_one_plane( double at_coord[][3], double min_sine) -{ - double actual_min_sine; - double prod; - prod = triple_prod_and_min_abs_sine( at_coord, &actual_min_sine); - return actual_min_sine <= min_sine; -} -/*************************************************************/ -/* Find if 4 atoms are in one plane */ -int are_4at_in_one_plane( double at_coord[][3], double min_sine) -{ - double actual_min_sine, min_actual_min_sine; - double coord[3][3], prod; - int i, k, j; - for ( k = 0; k < 4; k ++ ) { /* cycle added 4004-08-15 */ - for ( i = j = 0; i < 4; i ++ ) { - if ( i != k ) { - diff3( at_coord[i], at_coord[k], coord[j] ); - j ++; - } - } - prod = triple_prod_and_min_abs_sine( coord, &actual_min_sine); - if ( !k || actual_min_sine < min_actual_min_sine ) { - min_actual_min_sine = actual_min_sine; - } - } - return min_actual_min_sine <= min_sine; -} -/*************************************************************/ -int triple_prod_char( inp_ATOM *at, int at_1, int i_next_at_1, S_CHAR *z_dir1, - int at_2, int i_next_at_2, S_CHAR *z_dir2 ) -{ - inp_ATOM *at1, *at2; - double pnt[3][3], len; - int i; - int ret = 0; - - at1 = at + at_1; - at2 = at + at[at_1].neighbor[i_next_at_1]; - - pnt[0][0] = at2->x - at1->x; - pnt[0][1] = at2->y - at1->y; - pnt[0][2] = at2->z - at1->z; - - at2 = at + at_2; - at1 = at + at[at_2].neighbor[i_next_at_2]; - - pnt[1][0] = at2->x - at1->x; - pnt[1][1] = at2->y - at1->y; - pnt[1][2] = at2->z - at1->z; -/* - * resultant pnt vector directions: - * - * pnt[0] pnt[1] - * - * [at_1]---->[...] [...]---->[at_2] - * - * - * add3 below: (pnt[0] + pnt[1]) -> pnt[1] - */ - add3( pnt[0], pnt[1], pnt[1] ); - - - - for ( i = 0; i < 3; i ++ ) { - pnt[0][i] = (double)z_dir1[i]; - pnt[2][i] = (double)z_dir2[i]; - } - for ( i = 0; i < 3; i ++ ) { - len = len3( pnt[i] ); - if ( len < MIN_BOND_LEN ) { - if ( i == 1 && (at[at_1].bUsed0DParity || at[at_2].bUsed0DParity) ) { - pnt[i][0] = 0.0; - pnt[i][1] = 1.0; - pnt[i][2] = 0.0; - len = 1.0; /* standard at_1-->at_2 vector coordinates in case of 0D allene */ - } else { - goto exit_function; /* too short bond */ - } - } - mult3( pnt[i], 1.0/len, pnt[i] ); - } - len = 100.0*triple_prod(pnt[0], pnt[1], pnt[2], NULL ); -/* - * ^ pnt[0] - * | The orientation on this diagram - * | produces len = -100 - * [at_1]------>[at_2] - * pnt[1] / - * / - * / pnt[2] (up from the plane) - * v - * - * Note: len is invariant upon at_1 <--> at_2 transposition because - * triple product changes sign upon pnt[0]<-->pnt[2] transposition and - * triple product changes sign upon pnt[1]--> -pnt[1] change of direction: - * - * triple_prod(pnt[0], pnt[1], pnt[2], NULL ) = - * triple_prod(pnt[2], -pnt[1], pnt[0], NULL ) - * - */ - - ret = len >= 0.0? (int)(floor(len+0.5)) : -(int)(floor(0.5-len)); - -exit_function: - - return ret; -} - - -/****************************************************************/ - -#if ( NEW_STEREOCENTER_CHECK == 1 ) /* { */ - -/********************************************************************************************/ -int bInpAtomHasRequirdNeigh ( inp_ATOM *at, int cur_at, int RequirdNeighType, int NumDbleBonds ) -{ - /* RequirdNeighType: - reqired neighbor types (bitmap): - 0 => any neighbors - 1 => no terminal hydrogen atom neighbors - 2 => no terminal -X and -XH together (don't care about -X, -XH bond type, charge, radical) - (X = tautomeric endpoint atom) - NumDbleBonds: - if non-zero then allow double, alternating and tautomeric bonds - */ - int i, j, ni, nj, bond_type, num_1s, num_mult, num_other; - - if ( at[cur_at].endpoint ) { /* tautomeric endpoint cannot be a stereo center */ - return 0; - } - - if ( (1 & RequirdNeighType) && at[cur_at].num_H ) { - return 0; - } - - if ( 2 & RequirdNeighType ) { - for ( i = 0; i < at[cur_at].valence; i ++ ) { - ni = (int)at[cur_at].neighbor[i]; - if ( at[ni].valence != 1 || - !get_endpoint_valence( at[ni].el_number ) ) { - continue; - } - for ( j = i+1; j < at[cur_at].valence; j ++ ) { - nj = (int)at[cur_at].neighbor[j]; - if ( at[nj].valence != 1 || - at[ni].el_number != at[nj].el_number || - !get_endpoint_valence( at[nj].el_number ) ) { - continue; - } - /* - * if (at[ni].num_H != at[nj].num_H) then the atoms (neighbors of at[cur_at] - * are tautomeric endpoints and are indistinguishable => cur_at is not stereogenic - * if (at[ni].num_H == at[nj].num_H) then the neighbors are indistinguishable - * and cur_at will be found non-sterogenic later - * get_endpoint_valence() check will not allow the neighbors to be carbons - * Therefore the following "if" is not needed; we may just return 0. - */ - if ( at[ni].num_H != at[nj].num_H && strcmp(at[ni].elname, "C" ) ) { - return 0; /* found -X and -XH neighbors */ - } - } - } - } - - num_1s = num_mult = num_other = 0; - - for ( i = 0; i < at[cur_at].valence; i ++ ) { - bond_type = (at[cur_at].bond_type[i] & ~BOND_MARK_ALL); - switch( bond_type ) { - case BOND_SINGLE: - num_1s ++; - break; - case BOND_DOUBLE: - case BOND_ALTERN: - case BOND_TAUTOM: - case BOND_ALT12NS: - num_mult ++; - break; - default: - num_other ++; - break; - } - } - - if ( num_other ) { - return 0; - } - - if ( NumDbleBonds && NumDbleBonds > num_mult || - !NumDbleBonds && at[cur_at].valence != num_1s ) { - return 0; - } - return 1; -} -/********************************************************************************************/ -int bCanInpAtomBeAStereoCenter( inp_ATOM *at, int cur_at, int bPointedEdgeStereo ) -{ - -/************************************************************************************* - * current version - ************************************************************************************* - * Use #define to split the stereocenter description table into parts - * to make it easier to read - * - * --------- 4 single bonds stereocenters ------- - * 0 1 2 3 4 5 - * - * | | | | | | - * -C- -Si- -Ge- -Sn- >As[+] >B[-] - * | | | | | | - */ -#define SZELEM1 "C\000","Si", "Ge", "Sn", "As", "B\000", -#define CCHARGE1 0, 0, 0, 0, 1, -1, -#define CNUMBONDSANDH1 4, 4, 4, 4, 4, 4, -#define CCHEMVALENCEH1 4, 4, 4, 4, 4, 4, -#define CHAS3MEMBRING1 0, 0, 0, 0, 0, 0, -#define CREQUIRDNEIGH1 0, 0, 0, 0, 3, 0, -/* - * --------------- S, Se stereocenters ---------- - * 6 7 8 9 10 11 12 13 - * - * | | || | | || - * -S= =S= -S[+] >S[+] -Se= =Se= -Se[+] >Se[+] - * | | | | | | | | - */ -#define SZELEM2 "S\000","S\000","S\000","S\000","Se", "Se", "Se", "Se", -#define CCHARGE2 0, 0, 1, 1, 0, 0, 1, 1, -#define CNUMBONDSANDH2 3, 4, 3, 4, 3, 4, 3, 4, -#define CCHEMVALENCEH2 4, 6, 3, 5, 4, 6, 3, 5, -#define CHAS3MEMBRING2 0, 0, 0, 0, 0, 0, 0, 0, -#define CREQUIRDNEIGH2 3, 3, 3, 3, 3, 3, 3, 3, -/* - * ------------------ N, P stereocenters ----------------- - * 14 15 16 17 18 19 20 - * - * Phosphine Arsine - * X---Y - * | | \ / | | \ / \ / - * =N- >N[+] N >P[+] =P- P As - * | | | | | | | - */ -#define SZELEM3 "N\000","N\000","N\000","P\000","P\000","P\000", "As", -#define CCHARGE3 0, 1, 0, 1, 0, 0, 0, -#define CNUMBONDSANDH3 4, 4, 3, 4, 4, 3, 3, -#define CCHEMVALENCEH3 5, 4, 3, 4, 5, 3, 3, -#define CHAS3MEMBRING3 0, 0, 1, 0, 0, 0, 0, -#define CREQUIRDNEIGH3 3, 3, 1, 3, 3, 2, 2, - -#define PHOSPHINE_STEREO 19 /* the number must match Phosphine number in the comments, see above */ -#define ARSINE_STEREO 20 /* the number must match Arsine number in the comments, see above */ - - static char szElem[][3]={ SZELEM1 SZELEM2 SZELEM3 }; - static S_CHAR cCharge[]={ CCHARGE1 CCHARGE2 CCHARGE3 }; - static S_CHAR cNumBondsAndH[]={ CNUMBONDSANDH1 CNUMBONDSANDH2 CNUMBONDSANDH3 }; - static S_CHAR cChemValenceH[]={ CCHEMVALENCEH1 CCHEMVALENCEH2 CCHEMVALENCEH3 }; - static S_CHAR cHas3MembRing[]={ CHAS3MEMBRING1 CHAS3MEMBRING2 CHAS3MEMBRING3 }; - static S_CHAR cRequirdNeigh[]={ CREQUIRDNEIGH1 CREQUIRDNEIGH2 CREQUIRDNEIGH3 }; - - static int n = sizeof(szElem)/sizeof(szElem[0]); - /* reqired neighbor types (bitmap): - 0 => check bonds only - 1 => no terminal hydrogen atom neighbors - 2 => no terminal -X and -XH together (don't care the bond type, charge, radical) - (X = tautomeric endpoint atom) - Note: whenever cChemValenceH[] > cNumBondsAndH[] - the tautomeric and/or alternating bonds - are permitted - - */ - int i, ret = 0; - for ( i = 0; i < n; i++ ) { - if ( !strcmp( at[cur_at].elname, szElem[i]) && - at[cur_at].charge == cCharge[i] && - (!at[cur_at].radical || at[cur_at].radical == 1) && - at[cur_at].valence +at[cur_at].num_H == cNumBondsAndH[i] && - at[cur_at].chem_bonds_valence+at[cur_at].num_H == cChemValenceH[i] && - (cHas3MembRing[i]? is_atom_in_3memb_ring( at, cur_at ) : 1) && - bInpAtomHasRequirdNeigh ( at, cur_at, cRequirdNeigh[i], cChemValenceH[i]-cNumBondsAndH[i]) ) { - ret = cNumBondsAndH[i]; - break; - } - } - - if ( i == PHOSPHINE_STEREO && !(bPointedEdgeStereo & PES_BIT_PHOSPHINE_STEREO) ) - ret = 0; - if ( i == ARSINE_STEREO && !(bPointedEdgeStereo & PES_BIT_ARSINE_STEREO) ) - ret = 0; - return ret; -} - -#else /* } NEW_STEREOCENTER_CHECK { */ - -/********************************************************************************************/ -int bCanAtomBeAStereoCenter( char *elname, S_CHAR charge, S_CHAR radical ) -{ - static const char szElem[][3] = { "C\000", "Si", "Ge", "N\000", "P\000", "As", "B\000" }; - static const S_CHAR cCharge[] = { 0, 0, 0, 1, 1, 1, -1 }; - int i, ret = 0; - for ( i = 0; i < sizeof(szElem)/sizeof(szElem[0]); i++ ) { - if ( !strcmp( elname, szElem[i] ) && (charge == cCharge[i]) ) { - ret = (!radical || radical == RADICAL_SINGLET); - break; - } - } - return ret; -} -#endif /* } NEW_STEREOCENTER_CHECK */ - -/****************************************************************/ -/* used for atoms adjacent to stereogenic bonds only */ -int bAtomHasValence3( char *elname, S_CHAR charge, S_CHAR radical ) -{ - static const char szElem[][3] = { "N\000" }; - static const S_CHAR cCharge[] = { 0, }; - int i, ret = 0; - for ( i = 0; i < (int)(sizeof(szElem)/sizeof(szElem[0])); i++ ) { - if ( !strcmp( elname, szElem[i] ) && (charge == cCharge[i]) ) { - ret = ( !radical || radical == RADICAL_SINGLET ); - break; - } - } - return ret; -} - -/****************************************************************/ -/* used for atoms adjacent to stereogenic bonds only */ -int bCanAtomHaveAStereoBond( char *elname, S_CHAR charge, S_CHAR radical ) -{ - static const char szElem[][3] = { "C\000", "Si", "Ge", "N\000", "N\000" }; - static const S_CHAR cCharge[] = { 0, 0, 0, 0, 1, }; - static const int n = sizeof(szElem)/sizeof(szElem[0]); - int i, ret = 0; - for ( i = 0; i < n; i++ ) { - if ( !strcmp( elname, szElem[i] ) && (charge == cCharge[i]) ) { - ret = (!radical || radical == RADICAL_SINGLET); - break; - } - } - return ret; -} -/****************************************************************/ -/* used for atoms adjacent to stereogenic bonds only */ -int bCanAtomBeMiddleAllene( char *elname, S_CHAR charge, S_CHAR radical ) -{ - static const char szElem[][3] = { "C\000", "Si", "Ge", }; - static const S_CHAR cCharge[] = { 0, 0, 0, }; - static const int n = sizeof(szElem)/sizeof(szElem[0]); - int i, ret = 0; - for ( i = 0; i < n; i++ ) { - if ( !strcmp( elname, szElem[i] ) && (charge == cCharge[i]) ) { - ret = (!radical || radical == RADICAL_SINGLET); - break; - } - } - return ret; -} -/*****************************************************************/ -int bIsSuitableHeteroInpAtom( inp_ATOM *at ) -{ - int val, num_H; - if ( 0 == at->charge && - (!at->radical || RADICAL_SINGLET == at->radical) && - 0 < (val=get_endpoint_valence( at->el_number ) )) { - num_H = at->num_H; - if ( val == at->chem_bonds_valence + num_H ) { - switch( val ) { - case 2: /* O */ - if ( !num_H && 1 == at->valence ) - return 0; /* =O */ - break; /* not found */ - case 3: /* N */ - if ( 1 == at->valence && 1 == num_H || - 2 == at->valence && 0 == num_H ) - return 1; /* =N- or =NH */ - break; /* not found */ - } - } - } - return -1; -} -/****************************************************************/ -int bIsOxide( inp_ATOM *at, int cur_at ) -{ - int i, bond_type; - inp_ATOM *a = at + cur_at, *an; - for ( i = 0; i < a->valence; i ++ ) { - bond_type = (a->bond_type[i] &= ~BOND_MARK_ALL); - if ( bond_type == BOND_DOUBLE ) { - an = at + (int)a->neighbor[i]; - if ( 1 == an->valence && - !an->charge && !an->num_H && !an->radical && - 2 == get_endpoint_valence( an->el_number ) ) { - return 1; - } - } else - if ( bond_type == BOND_TAUTOM || bond_type == BOND_ALT12NS ) { - an = at + (int)a->neighbor[i]; - if ( 1 == an->valence && - 2 == get_endpoint_valence( an->el_number ) ) { - return 1; - } - } - } - return 0; -} -/****************************************************************/ -/* used for atoms adjacent to stereogenic bonds only */ -int bCanAtomBeTerminalAllene( char *elname, S_CHAR charge, S_CHAR radical ) -{ - static const char szElem[][3] = { "C\000", "Si", "Ge", }; - static const S_CHAR cCharge[] = { 0, 0, 0, }; - static const int n = sizeof(szElem)/sizeof(szElem[0]); - int i, ret = 0; - for ( i = 0; i < n; i++ ) { - if ( !strcmp( elname, szElem[i] ) && (charge == cCharge[i]) ) { - ret = (!radical || radical == RADICAL_SINGLET); - break; - } - } - return ret; -} -/************************************************************************/ -int GetHalfStereobond0DParity( inp_ATOM *at, int cur_at, AT_NUMB nSbNeighOrigAtNumb[], - int nNumExplictAttachments, int bond_parity, int nFlag ) -{ - int m, last_parity, cur_parity; - int i, icur2nxt, icur2neigh, cur_order_parity, nxt_at; - AT_NUMB nNextSbAtOrigNumb; - /* find atom parities for all valid streobonds incident to at[cur_at] */ - for ( m = 0, last_parity = 0; m < MAX_NUM_STEREO_BONDS && at[cur_at].sb_parity[m]; m ++ ) { - icur2nxt = icur2neigh = -1; /* ordering number of neighbors in nSbNeighOrigAtNumb[] */ - cur_parity = 0; /* parity for mth stereobond incident to the cur_at */ - if ( 0 <= at[cur_at].sb_ord[m] && at[cur_at].sb_ord[m] < at[cur_at].valence && - 0 <= (nxt_at = at[cur_at].neighbor[(int)at[cur_at].sb_ord[m]]) && - at[nxt_at].valence <= MAX_NUM_STEREO_BONDS && /* make sure it is a valid stereobond */ - (nNextSbAtOrigNumb = at[nxt_at].orig_at_number) ) { - /* since at[cur_at].sn_ord[m] = -1 for explicit H use at[cur_at].sn_orig_at_num[m] */ - for ( i = 0; i < nNumExplictAttachments; i ++ ) { - if ( at[cur_at].sn_orig_at_num[m] == nSbNeighOrigAtNumb[i] ) { - icur2neigh = i; /* neighbor */ - } else - if ( nNextSbAtOrigNumb == nSbNeighOrigAtNumb[i] ) { - icur2nxt = i; /* atom connected by a stereobond */ - } - } - if ( icur2neigh >= 0 && icur2nxt >= 0 ) { - if ( ATOM_PARITY_WELL_DEF(at[cur_at].sb_parity[m]) ) { - /* parity of at[cur_atom] neighbor permutation to reach this order: { next_atom, neigh_atom, ...} */ - cur_order_parity = (icur2nxt + icur2neigh + (icur2nxt > icur2neigh) - 1) % 2; - cur_parity = 2 - (cur_order_parity + at[cur_at].sb_parity[m]) % 2; - } else { - /* unknowm/undef parities do not depend on the neighbor order */ - cur_parity = at[cur_at].sb_parity[m]; - } - } - } else { - continue; - } - /* use a well-known parity if available; if not then use preferably the unknown */ - if ( !last_parity ) { - last_parity = cur_parity; - } else - if ( last_parity != cur_parity && cur_parity ) { - if ( ATOM_PARITY_WELL_DEF(last_parity) ) { - if ( ATOM_PARITY_WELL_DEF(cur_parity) ) { - last_parity = 0; /* error: all well-defined parities should be same */ - break; - } - } else - if ( ATOM_PARITY_WELL_DEF(cur_parity) ) { - /* replace unknown/undefined parity with well-known */ - last_parity = cur_parity; - } else { - /* select min unknown/undefined parity (out of AB_PARITY_UNKN and AB_PARITY_UNDF) */ - last_parity = inchi_min(cur_parity, last_parity); - } - } - } - if ( last_parity ) { - bond_parity = last_parity; - at[cur_at].bUsed0DParity |= nFlag; /* set flag: used stereobond 0D parity */ - } - return bond_parity; -} -/*******************************************************************************************/ -int FixSb0DParities( inp_ATOM *at, /* inp_ATOM *at_removed_H, int num_removed_H,*/ int chain_length, - int at_1, int i_next_at_1, S_CHAR z_dir1[], - int at_2, int i_next_at_2, S_CHAR z_dir2[], - int *pparity1, int *pparity2 ) -{ - int k, parity1, parity2, abs_parity1, abs_parity2; - int j1, j2, parity_sign; - /* - AT_NUMB nSbNeighOrigAtNumb1[MAX_NUM_STEREO_BOND_NEIGH], nSbNeighOrigAtNumb2[MAX_NUM_STEREO_BOND_NEIGH]; - int nNumExplictAttachments1, nNumExplictAttachments2; - */ - parity1 = parity2 = AB_PARITY_NONE; - j1 = j2 = -1; - parity_sign = ( *pparity1 < 0 || *pparity2 < 0 )? -1 : 1; - - abs_parity1 = abs(*pparity1); - abs_parity2 = abs(*pparity2); - - for ( k = 0; k < MAX_NUM_STEREO_BONDS && at[at_1].sb_parity[k]; k ++ ) { - if ( at[at_1].sb_ord[k] == i_next_at_1 ) { - parity1 = at[at_1].sb_parity[k]; - j1 = k; - } - } - for ( k = 0; k < MAX_NUM_STEREO_BONDS && at[at_2].sb_parity[k]; k ++ ) { - if ( at[at_2].sb_ord[k] == i_next_at_2 ) { - parity2 = at[at_2].sb_parity[k]; - j2 = k; - } - } - switch( (j1 >= 0) + 2*(j2 >= 0) ) { - case 0: - /* the bond has no 0D parity */ - *pparity1 = *pparity2 = parity_sign * AB_PARITY_UNDF; - return 0; - case 1: - case 2: - /* 0D parity data error */ - *pparity1 = *pparity2 = AB_PARITY_NONE; - return -1; - case 3: - /* the bond has 0D parity */ - switch ( !(ATOM_PARITY_WELL_DEF( abs_parity1 ) && ATOM_PARITY_WELL_DEF( parity1 )) + - 2 * !(ATOM_PARITY_WELL_DEF( abs_parity2 ) && ATOM_PARITY_WELL_DEF( parity2 )) ) { - case 0: - /* both parities are well-defined; continue */ - break; - case 1: - /* 0D parity not well-defined for at_1 */ - *pparity1 = parity_sign * (ATOM_PARITY_WELL_DEF( parity1 )? abs_parity1 : - ATOM_PARITY_WELL_DEF( abs_parity1 )? parity1 : - inchi_min(abs_parity1, parity1)); - *pparity2 = parity_sign * abs_parity2; - return -1; - case 2: - /* 0D parity not well-defined for at_2 */ - *pparity1 = parity_sign * abs_parity1; - *pparity2 = parity_sign * (ATOM_PARITY_WELL_DEF( parity2 )? abs_parity2 : - ATOM_PARITY_WELL_DEF( abs_parity2 )? parity2 : - inchi_min(abs_parity2, parity2)); - return -1; - case 3: - abs_parity1 = (ATOM_PARITY_WELL_DEF( parity1 )? abs_parity1 : - ATOM_PARITY_WELL_DEF( abs_parity1 )? parity1 : - inchi_min(abs_parity1, parity1)); - abs_parity2 = (ATOM_PARITY_WELL_DEF( parity2 )? abs_parity2 : - ATOM_PARITY_WELL_DEF( abs_parity2 )? parity2 : - inchi_min(abs_parity2, parity2)); - *pparity1 = *pparity2 = parity_sign * inchi_min(abs_parity1, abs_parity2); - /*return (parity1 == parity2)? 0 : -1;*/ - return -1; - } - break; - } - /* we are here if both end-atoms of the bond have well-defined 0D parities */ - /* - nNumExplictAttachments1 = GetSbNeighOrigAtNumb( at, at_1, at_removed_H, num_removed_H, nSbNeighOrigAtNumb1 ); - nNumExplictAttachments2 = GetSbNeighOrigAtNumb( at, at_2, at_removed_H, num_removed_H, nSbNeighOrigAtNumb2 ); - parity1 = GetHalfStereobond0DParity( at, at_1, nSbNeighOrigAtNumb1, nNumExplictAttachments1, *pparity1, 0 ); - parity2 = GetHalfStereobond0DParity( at, at_2, nSbNeighOrigAtNumb2, nNumExplictAttachments2, *pparity2, 0 ); - */ - *pparity1 = parity_sign * abs_parity1; - *pparity2 = parity_sign * abs_parity2; - - if ( chain_length % 2 ) { - /* allene; chain_length = (number of double bonds) - 1 */ - /* - int zer1 = ( !z_dir1[0] && !z_dir1[1] && !z_dir1[2] ); - int zer2 = ( !z_dir2[0] && !z_dir2[1] && !z_dir2[2] ); - */ - int bWrong_z_dir1 = (0 != (at[at_1].bUsed0DParity & FlagSB_0D)); - int bWrong_z_dir2 = (0 != (at[at_2].bUsed0DParity & FlagSB_0D)); - - if ( bWrong_z_dir1 && bWrong_z_dir2 ) { - goto set_default; - } else - if ( bWrong_z_dir1 || bWrong_z_dir2 ) { - double r12[3], zi1[3], zi2[3], abs_r12, abs_zi2; - int at_i1, at_i2, j; - S_CHAR z_dir[3]; - r12[0] = at[at_2].x - at[at_1].x; - r12[1] = at[at_2].y - at[at_1].y; - r12[2] = at[at_2].z - at[at_1].z; - abs_r12 = len3( r12 ); - if ( abs_r12 < MIN_BOND_LEN ) { - goto set_default; - } - /* make r12[] point to the atom with 'good' z_dir[] */ - if ( bWrong_z_dir1 ) { - at_i1 = at_2; /* has good z_dir2[] */ - at_i2 = at_1; /* has bad z_dir1[] */ - zi1[0] = z_dir2[0]; - zi1[1] = z_dir2[1]; - zi1[2] = z_dir2[2]; - mult3( r12, 1.0/abs_r12, r12 ); /* make length = 1 */ - } else { - at_i1 = at_1; /* has good z_dir1[] */ - at_i2 = at_2; /* has bad z_dir2[] */ - zi1[0] = z_dir1[0]; - zi1[1] = z_dir1[1]; - zi1[2] = z_dir1[2]; - mult3( r12, -1.0/abs_r12, r12 ); /* make length = 1 */ - } - cross_prod3( r12, zi1, zi2 ); - abs_zi2 = len3( zi2 ); - mult3( zi2, 100.0/abs_zi2, zi2 ); /* make length = 100 */ - for ( j = 0; j < 3; j ++ ) { - z_dir[j] = (S_CHAR) (zi2[j]>= 0.0? floor(0.5 + zi2[j]) : - -floor(0.5 - zi2[j])); /* abs(z_dir) = 100 */ - } - if ( bWrong_z_dir1 ) { - memcpy( z_dir1, z_dir, sizeof(z_dir) ); - } else { - memcpy( z_dir2, z_dir, sizeof(z_dir) ); - } - } - return 0; - -set_default: - /* z_dir1[] = x-direction; z_dir2[] = z-direction; r12[] = y-direction */ - z_dir1[0] = 100; - z_dir1[1] = z_dir1[2] = 0; - z_dir2[0] = z_dir2[1] = 0; - z_dir2[2] = 100; - } - return 0; -} -/**********************************************************/ -/* without this InChI fails on reconstructed CID=450438 */ -/* (isotopic, Unknown SB adjacent to SB with known parity) */ -/**********************************************************/ -int FixUnkn0DStereoBonds(inp_ATOM *at, int num_at) -{ - int i, m, num=0; - - /* add usual Unknown stereobond descriptors to each Unknown bond */ - for( i = 0; i < num_at; i ++ ) { - for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[i].sb_parity[m]; m ++ ) { - if ( AB_PARITY_UNKN == at[i].sb_parity[m] ) { - at[i].bond_stereo[ (int)at[i].sb_ord[m] ] = STEREO_DBLE_EITHER; - num ++; - } - } - } -#ifdef NEVER - if ( num ) { - int j; - /* how to remove Unknown stereo bond parities */ - for( i = 0; i < num_at; i ++ ) { - for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[i].sb_parity[m]; m ++ ) { - if ( AB_PARITY_UNKN == at[i].sb_parity[m] ) { - for ( j = m+1; j < MAX_NUM_STEREO_BONDS; j ++ ) { - at[i].sb_parity[j-1] = at[i].sb_parity[j]; - at[i].sb_ord[j-1] = at[i].sb_ord[j]; - at[i].sn_ord[j-1] = at[i].sn_ord[j]; - at[i].sn_orig_at_num[j-1] = at[i].sn_orig_at_num[j]; - } - at[i].sb_parity[j-1] = 0; - at[i].sb_ord[j-1] = 0; - at[i].sn_ord[j-1] = 0; - at[i].sn_orig_at_num[j-1] = 0; - } - } - } - } -#endif - return num; -} -/*====================================================================================================== - -half_stereo_bond_parity() General Description: - - A) find projections of 3 bonds on a reasonable plane defined - by a vector z_dir perpendicular to the plane - B) calculate parity - -half_stereo_bond_parity() Detailed Description: - - 1) Find at_coord[] = vectors from the central atoms to its neighbors - 2) If only 2 neighbors are present, then create a reasonable 3rd neighbor - (an implicit H or a fictitious atom in case of =NX) coordinates - 3) Normalize at_coord[] to unit length - 4) Find unit vector pnt[2] perpendicular to the plane containing - at_coord[] arrow ends. - Even though it is not necessary, make z-coordinate of pnt[2] positive. - ** pnt[2] has the new z-axis direction ** - 5) Let pnt[0] = perpendicular to pnt[2] component of at_coord[0]; - Normalize pnt[0] to unit length. - ** pnt[0] has the new x-axis direction ** - 6) Let pnt[1] = pnt[2] x pnt[0] (cross-product); - ** pnt[1] has the new y-axis direction ** - 7) Find at_coord[] in the new xyz-basis and normalize their xy-projections - to a unit length - 8) In the new xy-plane find (counterclockwise) angles: - tmp1 = (from at_coord[0] to at_coord[1]) - tmp2 = (from at_coord[0] to at_coord[2]) - 9) Calculate the parity: if tmp1 < tmp2 then 1 (odd) else 2 (even) - (even: looking from the arrow end of the new z-axis, 0, 1, and 2 neighbors - are in clockwise order) - 10) Calculate z_dir = 100*pnt[2]. - - Note1. If z_dir vectors of atoms located at the opposite ends of a double bond have approximately - opposite directions (that is, their dot-product is negative) then the parity of the - stereogenic bond calculated from half-bond-parities should be inverted - - Note2. In case of a tetrahedral cumulene a triple product (z_dir1, (1->2), z_dir2) is used instead - of the dot-product. (1->2) is a vector from the atom#1 to the atom #2. This triple product - is invariant with respect to the atom numbering because it does not change upon (1,2) - permutation. - - Stereo ambiguity in case of 2 neighbors: - ---------------------------------------- - Undefined: single-double bond angle > pi - arcsin(0.03) = 178.28164199834454285275613218975 degrees - Ambiguous: single-double bond angle > 175 degrees = pi - 0.087156 Rad - - Return values - (cases: I=only in case of isotopic H atoms the neighbors are different, - N=in case of non-isotopic H atoms the neighbors are different) - - -4 = AB_PARITY_UNDF => atom is adjacent to a stereogenic bond, but the geometry is undefined, I - -3 = AB_PARITY_UNKN => atom is adjacent to a stereogenic bond, but the geometry is not known to the iuser, I - -2 =-AB_PARITY_EVEN => parity of an atom adjacent to a stereogenic bond, I - -1 =-AB_PARITY_ODD => parity of an atom adjacent to a stereogenic bond, I - 0 = AB_PARITY_NONE => the atom is not adjacent to a stereogenic bond - 1 = AB_PARITY_ODD => parity of an atom adjacent to a stereogenic bond, N&I - 2 = AB_PARITY_EVEN => parity of an atom adjacent to a stereogenic bond, N&I - 3 = AB_PARITY_UNKN => atom is adjacent to a stereogenic bond, but the geometry is not known to the iuser, N&I - 4 = AB_PARITY_UNDF => atom is adjacent to a stereogenic bond, but the geometry is undefined, N&I - 5 = AB_PARITY_IISO => atom constitutionally equivalent to this atom may be adjacent to a stereogenic bond, I - - -=====================================================================================================*/ - -int half_stereo_bond_parity( inp_ATOM *at, int cur_at, inp_ATOM *at_removed_H, - int num_removed_H, S_CHAR *z_dir, - int bPointedEdgeStereo, int vABParityUnknown ) -{ - double at_coord[MAX_NUM_STEREO_BOND_NEIGH][3], c, s, tmp[3], tmp1, tmp2, min_tmp, max_tmp, z; - double temp[3], pnt[3][3]; - int j, k, p0, p1, p2, next, bValence3=0, num_z, nType, num_either_single, num_either_double; - int nNumExplictAttachments; - int bond_parity = AB_PARITY_UNDF; - int num_H=0, num_iH, num_eH=0, num_nH=0 /* = num_iso_H[0] */; - int num_iso_H[NUM_H_ISOTOPES+1]; - int index_H[5]; /* cannot have more than 4 elements: 1 H, 1 1H, 1 D, 1 T atom(s) */ - /* const double one_pi = 2.0*atan2(1.0 , 0.0 ); */ - const double one_pi = 3.14159265358979323846; /* M_PI */ - const double two_pi = 2.0*one_pi; - int bIgnoreIsotopicH = (0 != (at[cur_at].cFlags & AT_FLAG_ISO_H_POINT)); - AT_NUMB nSbNeighOrigAtNumb[MAX_NUM_STEREO_BOND_NEIGH]; - - - if ( z_dir && !z_dir[0] && !z_dir[1] && !z_dir[2] ) { - z_dir[2]=100; - } - - num_H = at[cur_at].num_H; - if ( num_H > NUM_H_ISOTOPES ) - return 0; /* at least 2 H atoms are isotopically identical */ - - if ( MAX_NUM_STEREO_BOND_NEIGH < at[cur_at].valence + num_H || - MIN_NUM_STEREO_BOND_NEIGH > at[cur_at].valence + num_H ) - return 0; - - if ( !bCanAtomHaveAStereoBond( at[cur_at].elname, at[cur_at].charge, at[cur_at].radical ) ) - return 0; - if ( !bIgnoreIsotopicH ) { - for ( j = 0, num_nH = num_H; j < NUM_H_ISOTOPES; j ++ ) { - if ( (k = (int)at[cur_at].num_iso_H[j]) > 1 ) { - return AB_PARITY_IISO; /* two or more identical isotopic H atoms */ - } - num_nH -= k; - } - } - /* at this point num_nH = number of non-isotopic H atoms */ - if ( num_nH > 1 ) - return AB_PARITY_IISO; /* two or more identical non-isotopic H atoms */ - if ( num_nH < 0 ) - return CT_ISO_H_ERR; /* program error */ /* */ - - /******************************************************************** - * Note. At this point all (implicit and explicit) isotopic - * terminal H neighbors are either different or not present. - ********************************************************************/ - - /* locate explicit hydrogen atoms */ - /* (at_removed_H are sorted in ascending isotopic H mass order, non-isotopic first) */ - memset( num_iso_H, 0, sizeof(num_iso_H) ); - if ( at_removed_H && num_removed_H > 0 ) { - for ( j = 0; j < num_removed_H; j ++ ) { - if ( at_removed_H[j].neighbor[0] == cur_at ) { - k = bIgnoreIsotopicH? 0 : at_removed_H[j].iso_atw_diff; - if ( 0 <= k && k <= NUM_H_ISOTOPES ) { - if ( ++num_iso_H[k] > 1 ) /* num_iso_H[0] = number of non-isotopic H atoms */ - return CT_ISO_H_ERR; /* program error in counting hydrogens */ /* */ - index_H[num_eH++] = j; - } else { - return CT_ISO_H_ERR; /* program error */ /* */ - } - } - } - num_iH = num_H - num_eH; /* number of implicit non-isotopic and isotopic H atoms */ - if ( num_iH > 1 ) { - /* more than one implicit H: cannot reconstruct the geometry */ - bond_parity = -AB_PARITY_UNDF; - goto exit_function; - } - } else { - num_iH = num_H; - } - /* at this point num_iH = number of implicit non-isotopic and isotopic H atoms */ - if ( at[cur_at].valence + num_eH < MIN_NUM_STEREO_BOND_NEIGH ) { - /* =NH or =CHD when no explicit H is present */ - return num_H == 1? AB_PARITY_UNDF : -AB_PARITY_UNDF; - } - - bValence3 = bAtomHasValence3( at[cur_at].elname, at[cur_at].charge, at[cur_at].radical ); - /* - * Can one explicit hydrogen be added to make asymmetric configuration? - * For now we can add 1 H atom in case of an appropriate geometry if: - * (a) one non-isotopic H (even if explicit isotopic H atoms are present), or - * (b) one isotopic or non-isotopic H if NO explicit isotopic or non-isotopic H atom is present - * This makes sense only in case chem. valence = 4. In case of chem. valence = 3, do not check. - */ - if ( at[cur_at].valence + num_eH == MIN_NUM_STEREO_BOND_NEIGH && !bValence3 && - !(/*(a)*/ 1 == num_nH && !num_iso_H[0] || - /*(b)*/ 1 == num_H && !num_eH) - ) { - goto exit_function; - /* return num_H == 1? AB_PARITY_UNDF : -AB_PARITY_UNDF; */ - } - - /* store neighbors coordinates */ - num_z = num_either_single = num_either_double = 0; - for ( k = nNumExplictAttachments = 0; k < 2; k ++ ) { - switch( k ) { - case 0: - for ( j = 0; j < num_eH; j ++, nNumExplictAttachments ++ ) { - next = index_H[j]; - at_coord[nNumExplictAttachments][0] = at_removed_H[next].x - at[cur_at].x; - at_coord[nNumExplictAttachments][1] = at_removed_H[next].y - at[cur_at].y; - nSbNeighOrigAtNumb[nNumExplictAttachments] = at_removed_H[next].orig_at_number; - /* use the fact that (at_removed_H - at) = (number of atoms except removed explicit H) */ - z = -get_z_coord( at, (at_removed_H-at)+next, 0 /*neighbor #*/, &nType, -(bPointedEdgeStereo & PES_BIT_POINT_EDGE_STEREO) ); - switch ( nType ) { - case ZTYPE_EITHER: - num_either_single ++; /* bond in "Either" direction. */ - break; - case ZTYPE_UP: - case ZTYPE_DOWN: - nType = -nType; /* at_removed_H[] contains bonds TO the center, not from */ - z = len2( at_coord[nNumExplictAttachments] ); - /* - z = sqrt( at_coord[nNumExplictAttachments][0]*at_coord[nNumExplictAttachments][0] - + at_coord[nNumExplictAttachments][1]*at_coord[nNumExplictAttachments][1] ); - */ - if ( nType == ZTYPE_DOWN ) - z = -z; - /* no break; here */ - case ZTYPE_3D: - num_z ++; - } - at_coord[nNumExplictAttachments][2] = z; - } - break; - case 1: - for ( j = 0; j < at[cur_at].valence; j ++, nNumExplictAttachments ++ ) { - next = at[cur_at].neighbor[j]; - at_coord[nNumExplictAttachments][0] = at[next].x - at[cur_at].x; - at_coord[nNumExplictAttachments][1] = at[next].y - at[cur_at].y; - nSbNeighOrigAtNumb[nNumExplictAttachments] = at[next].orig_at_number; - - z = get_z_coord( at, cur_at, j /*neighbor #*/, &nType, (bPointedEdgeStereo & PES_BIT_POINT_EDGE_STEREO) ); - switch ( nType ) { - case ZTYPE_EITHER: - num_either_single ++; /* bond in "Either" direction. */ - break; - case ZTYPE_UP: - case ZTYPE_DOWN: - z = len2( at_coord[nNumExplictAttachments] ); - /* - z = sqrt( at_coord[nNumExplictAttachments][0]*at_coord[nNumExplictAttachments][0] - + at_coord[nNumExplictAttachments][1]*at_coord[nNumExplictAttachments][1] ); - */ - if ( nType == ZTYPE_DOWN ) - z = -z; - /* no break; here */ - case ZTYPE_3D: - num_z ++; - } - at_coord[nNumExplictAttachments][2] = z; - } - break; - } - } - - if ( num_either_single ) { - bond_parity = vABParityUnknown /*AB_PARITY_UNKN*/; /* single bond is 'unknown' */ - goto exit_function; - } - - /* nNumExplictAttachments is a total number of attachments, including removed explicit terminal hydrogens */ - if ( nNumExplictAttachments == 2 ) { - /* create coordinates of the implicit hydrogen (or a fictitious atom in case of ==N-X ), */ - /* coord[2][], attached to the cur_at. */ - for ( j = 0; j < 3; j ++ ) { - at_coord[2][j] = - ( at_coord[0][j] + at_coord[1][j] ); - } - nSbNeighOrigAtNumb[nNumExplictAttachments] = 0; /* implicit H or lone pair */ - } - for ( j = 0; j < 3; j ++ ) { - tmp[j] = len3( at_coord[j] ); - } - min_tmp = inchi_min( tmp[0], inchi_min(tmp[1], tmp[2]) ); - max_tmp = inchi_max( tmp[0], inchi_max(tmp[1], tmp[2]) ); - if ( min_tmp < MIN_BOND_LEN || min_tmp < MIN_SINE*max_tmp ) { - /* all bonds or some of bonds are too short */ - if ( at[cur_at].sb_parity[0] ) { - /* use bond psrity; the reconciliation in ReconcileAllCmlBondParities() - * has made all ways to calculate parity produce same result - */ - bond_parity = GetHalfStereobond0DParity( at, cur_at, nSbNeighOrigAtNumb, - nNumExplictAttachments, bond_parity, FlagSB_0D ); - } - - goto exit_function; - } - /* normalize lengths to 1 */ - for ( j = 0; j < 3; j ++ ) { - mult3( at_coord[j], 1.0/tmp[j], at_coord[j] ); - } - - /* find projections of at_coord vector differences on the plane containing their arrowhead ends */ - for ( j = 0; j < 3; j ++ ) { - /* pnt[0..2] = {0-1, 1-2, 2-0} */ - tmp[j] = len3(diff3( at_coord[j], at_coord[(j+1)%3], pnt[j] )); - if ( tmp[j] < MIN_SINE ) { - goto exit_function; /* angle #i-cur_at-#j is too small */ - } - mult3( pnt[j], 1.0/tmp[j], pnt[j] ); /* 2003-10-06 */ - } - /* find pnt[p2], a vector perpendicular to the plane, and its length tmp[p2] */ - /* replace previous pnt[p2], tmp[p2] with new values; the old values do not have any additional */ - /* information because pnt[p0]+pnt[p1]+pnt[p2]=0 */ - /* 10-6-2003: a cross-product of one pair pnt[j], pnt[(j+1)%3] can be very small. Find the larges one */ - tmp1 = len3( cross_prod3( pnt[0], pnt[1], temp ) ); - for (j = 1, k = 0; j < 3; j ++ ) { - tmp2 = len3( cross_prod3( pnt[j], pnt[(j+1)%3], temp ) ); - if ( tmp2 > tmp1 ) { - tmp1 = tmp2; - k = j; - } - } - /* previously p0=0, p1=1, p2=2 */ - p0 = k; - p1 = (k+1)%3; - p2 = (k+2)%3; - tmp[p2] = len3( cross_prod3( pnt[p0], pnt[p1], pnt[p2] ) ); - if ( tmp[p2] < MIN_SINE*tmp[p0]*tmp[p1] ) { - goto exit_function; /* pnt[p0] is almost colinear to pnt[p1] */ - } - /* new basis: pnt[p0], pnt[p1], pnt[p2]; set z-coord sign and make abs(pnt[p2]) = 1 */ - mult3( pnt[p2], (pnt[p2][2]>0.0? 1.0:-1.0)/tmp[p2], pnt[p2] ); /* unit vector in the new z-axis direction */ - - min_tmp = dot_prod3( at_coord[0], pnt[p2] ); /* non-planarity measure (sine): hight of at_coord[] pyramid */ - mult3( pnt[p2], min_tmp, pnt[p0] ); /* vector height of the pyramid, ideally 0 */ - /* find new pnt[p0] = projection of at_coord[p0] on plane orthogonal to pnt[p2] */ - tmp[p0] = len3(diff3( at_coord[0], pnt[p0], pnt[p0] )); - mult3( pnt[p0], 1.0/tmp[p0], pnt[p0] ); /* new x axis basis vector */ - cross_prod3( pnt[p2], pnt[p0], pnt[p1] ); /* new y axis basis vector */ - /* find at_coord in the new basis of {pnt[p0], pnt[p1], pnt[p2]} */ - for ( j = 0; j < 3; j ++ ) { - copy3( at_coord[j], temp ); - for ( k = 0; k < 3; k ++ ) { - at_coord[j][k] = dot_prod3( temp, pnt[(k+p0)%3] ); - } - /* new xy plane projection length */ - tmp[j] = sqrt(at_coord[j][0]*at_coord[j][0] + at_coord[j][1]*at_coord[j][1]); - /* make new xy plane projection length = 1 */ - mult3( at_coord[j], 1.0/tmp[j], at_coord[j] ); - } - - s = fabs( at_coord[1][0]*at_coord[2][1] - at_coord[1][1]*at_coord[2][0] ); /* 1-2 sine */ - c = at_coord[1][0]*at_coord[2][0] + at_coord[1][1]*at_coord[2][1]; /* 1-2 cosine */ - if ( s < MIN_SINE && c > 0.5 ) { - goto exit_function; /* bonds to neigh. 1 and 2 have almost same direction; relative angles are undefined */ - } - c = at_coord[0][0]; /* cosine of the angle between new Ox axis and a bond to the neighbor 0. Should be 1 */ - s = at_coord[0][1]; /* sine. Should be 0 */ - /* turn vectors so that vector #1 (at_coord[0]) becomes {1, 0} */ - for ( j = 0; j < MAX_NUM_STEREO_BOND_NEIGH; j ++ ) { - tmp1 = c*at_coord[j][0] + s*at_coord[j][1]; - tmp2 = -s*at_coord[j][0] + c*at_coord[j][1]; - at_coord[j][0] = tmp1; - at_coord[j][1] = tmp2; - } - /* counterclockwise angles from the direction to neigh 0 to to directions to neighbors 1 and 2: */ - tmp1 = atan2( at_coord[1][1], at_coord[1][0] ); /* range -pi and +pi */ - tmp2 = atan2( at_coord[2][1], at_coord[2][0] ); - if ( tmp1 < 0.0 ) - tmp1 += two_pi; /* range 0 to 2*pi */ - if ( tmp2 < 0.0 ) - tmp2 += two_pi; - /*----------------------------------- - Example - 1 \ case tmp1 < tmp2 - \ parity is odd - \ (counterclockwise) - A------- 0 - / - / - 2 / - - ------------------------------------*/ - bond_parity = 2 - ( tmp1 < tmp2 ); - for ( j = 0; j < 3; j ++ ) { - z_dir[j] = (S_CHAR) (pnt[p2][j]>= 0.0? floor(0.5 + 100.0 * pnt[p2][j]) : - -floor(0.5 - 100.0 * pnt[p2][j])); /* abs(z_dir) = 100 */ - } - /* check for ambiguity */ - if ( nNumExplictAttachments > 2 ) { - min_tmp = inchi_min( tmp1, tmp2 ); - max_tmp = inchi_max( tmp1, tmp2 ); - if ( min_tmp > one_pi-MIN_SINE || max_tmp < one_pi+MIN_SINE || max_tmp-min_tmp > one_pi - MIN_SINE ) { - at[cur_at].bAmbiguousStereo |= AMBIGUOUS_STEREO; - } else /* 3D ambiguity 8-28-2002 */ - if ( fabs(at_coord[0][2]) > MAX_SINE ) { /* all fabs(at_coord[j][2] (j=0..2) must be equal */ - at[cur_at].bAmbiguousStereo |= AMBIGUOUS_STEREO; - } - } else - if ( nNumExplictAttachments == 2 ) { /* 10-6-2003: added */ - min_tmp = fabs(tmp1 - one_pi); - if ( min_tmp < MIN_SINE ) { - bond_parity = AB_PARITY_UNDF; /* consider as undefined 10-6-2003 */ - } else - if ( min_tmp < MIN_ANGLE_DBOND ) { - at[cur_at].bAmbiguousStereo |= AMBIGUOUS_STEREO; - } - } - - - /* for 3 neighbors moving implicit H to the index=0 from index=2 position */ - /* can be done in 2 transpositions and does not change atom's parity */ -exit_function: - if ( num_H > 1 && bond_parity > 0 && !(bond_parity & AB_PARITY_0D) /*&& PARITY_WELL_DEF(bond_parity)*/ ) { - /* - * stereo only if isotopes are counted. Do not inverse - * Examples: sign for this: - * H D - * / / H - * ==C or ==CH / - * \ ==N (bValence3=1) - * D - * two explicit one explicit H isotope (D), - * isotopic H atoms one implicit H - */ - bond_parity = -bond_parity; /* refers to isotopically substituted structure only */ - } - return bond_parity; -} - -/*************************************************************/ -int save_a_stereo_bond( int z_prod, int result_action, - int at1, int ord1, AT_NUMB *stereo_bond_neighbor1, S_CHAR *stereo_bond_ord1, S_CHAR *stereo_bond_z_prod1, S_CHAR *stereo_bond_parity1, - int at2, int ord2, AT_NUMB *stereo_bond_neighbor2, S_CHAR *stereo_bond_ord2, S_CHAR *stereo_bond_z_prod2, S_CHAR *stereo_bond_parity2 ) -{ - int i1, i2; - for ( i1 = 0; i1 < MAX_NUM_STEREO_BONDS && stereo_bond_neighbor1[i1]; i1 ++ ) - ; - for ( i2 = 0; i2 < MAX_NUM_STEREO_BONDS && stereo_bond_neighbor2[i2]; i2 ++ ) - ; - if ( i1 == MAX_NUM_STEREO_BONDS || i2 == MAX_NUM_STEREO_BONDS ) - return 0; - - stereo_bond_parity1[i1] = - stereo_bond_parity2[i2] = result_action; - - stereo_bond_neighbor1[i1] = (AT_NUMB) (at2+1); - stereo_bond_ord1[i1] = (S_CHAR)ord1; - stereo_bond_neighbor2[i2] = (AT_NUMB) (at1+1); - stereo_bond_ord2[i2] = (S_CHAR)ord2; - stereo_bond_z_prod1[i1] = - stereo_bond_z_prod2[i2] = (S_CHAR)z_prod; - return 1; -} -/***************************************************************/ -int get_allowed_stereo_bond_type( int bond_type ) -{ -#if (ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS == 0 ) - if ( (bond_type & ~BOND_MARK_ALL) == BOND_TAUTOM ) - return 0; /* no tautomer bonds allowed */ - else -#endif -#if ( EXCL_ALL_AROM_BOND_PARITY == 1 ) /* { */ - /* a stereo bond cannot belong to an aromatic atom */ - if ( (bond_type &= ~BOND_MARK_ALL) == BOND_ALTERN ) - { - return 0; - } -#else /* } { */ -#if ( ADD_6MEMB_AROM_BOND_PARITY == 1 ) - /* accept any aromatic bond as a stereo bond */ - if ( (bond_type &= ~BOND_MARK_ALL) == BOND_ALTERN ) -#else - /* accept only aromatic bonds in non-6-member rings */ - if ( (bond_type &= ~BOND_MARK_ALL) == BOND_ALTERN ) ) -#endif - { - return BOND_ALTERN; - } -#endif /* } */ - else - /* at this point BOND_MARK_ALL bits have been removed from bond_type */ - if ( bond_type == BOND_DOUBLE || bond_type == BOND_SINGLE ) { - return bond_type; - } -#if (ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS == 1 ) - else - if ( bond_type == BOND_TAUTOM ) { - return BOND_TAUTOM; - } -#endif - - return 0; /* wrong bond type */ -} - -/*************************************************************/ -int can_be_a_stereo_bond_with_isotopic_H( inp_ATOM *at, int cur_at, INCHI_MODE nMode ) -{ - int i, j, next_at, num_stereo_bonds, bFound; - int bond_type, num_2s, num_alt; - int num_2s_next, num_alt_next, num_wrong_bonds_1, num_wrong_bonds_2; -#if ( N_V_STEREOBONDS == 1 ) - int n2sh, num_2s_hetero[2], num_2s_hetero_next[2], next_next_at, type_N, type_N_next; -#endif - if ( MAX_NUM_STEREO_BOND_NEIGH < at[cur_at].valence+at[cur_at].num_H || - MIN_NUM_STEREO_BOND_NEIGH > at[cur_at].valence+at[cur_at].num_H ) - return 0; - if ( !bCanAtomHaveAStereoBond( at[cur_at].elname, at[cur_at].charge, at[cur_at].radical ) ) - return 0; - /* count bonds and find the second atom on the stereo bond */ - num_2s = num_alt = num_wrong_bonds_1 = 0; -#if ( N_V_STEREOBONDS == 1 ) - num_2s_hetero[0] = num_2s_hetero[1] = type_N = 0; - if ( 0 == at[cur_at].num_H && 0 == at[cur_at].charge && 0 == at[cur_at].radical && - 3 == get_endpoint_valence( at[cur_at].el_number ) ) { - if ( 2 == at[cur_at].valence && 3 == at[cur_at].chem_bonds_valence ) { - type_N = 1; - } else - if ( 3 == at[cur_at].valence && 5 == at[cur_at].chem_bonds_valence ) { - type_N = 2; /* unfortunately includes >N# */ - } - } -#endif - for ( i = 0, num_stereo_bonds = 0; i < at[cur_at].valence; i ++ ) { - bFound = 0; - next_at = at[cur_at].neighbor[i]; - bond_type = get_allowed_stereo_bond_type( (int)at[cur_at].bond_type[i] ); - if ( bond_type == BOND_ALTERN ) { - num_alt ++; - if ( cur_at > next_at && !(nMode & CMODE_NO_ALT_SBONDS) ) - bFound = 1; - } else - if ( bond_type == BOND_DOUBLE ) { - num_2s ++; -#if ( N_V_STEREOBONDS == 1 ) - if ( 0 <= (n2sh = bIsSuitableHeteroInpAtom( at + next_at )) ) { - num_2s_hetero[n2sh] ++; /* n2sh=0 -> =N- or =NH; n2sh=1 -> =O */ - } -#endif - if ( cur_at > next_at ) - bFound = 1; - } else - if ( bond_type != BOND_SINGLE && bond_type != BOND_TAUTOM ) { - num_wrong_bonds_1 ++; -#if ( ONE_BAD_SB_NEIGHBOR == 1 ) - if ( num_wrong_bonds_1 > 1 || num_wrong_bonds_1 && 2 >= at[cur_at].valence ) { - return 0; /* wrong bond type */ - } else { - continue; - } -#else - return 0; /* wrong bond type */ -#endif - } - - if ( bFound ) { - /* check "next_at" atom on the opposite side of the bond */ - if ( MAX_NUM_STEREO_BOND_NEIGH < at[next_at].valence+at[next_at].num_H || - MIN_NUM_STEREO_BOND_NEIGH > at[next_at].valence+at[next_at].num_H ) - continue; - if ( !bCanAtomHaveAStereoBond( at[next_at].elname, at[next_at].charge, at[next_at].radical ) ) - continue; - /* next atom neighbors */ - num_2s_next = num_alt_next = num_wrong_bonds_2 = 0; -#if ( N_V_STEREOBONDS == 1 ) - num_2s_hetero_next[0] = num_2s_hetero_next[1] = type_N_next = 0; - if ( 0 == at[next_at].num_H && 0 == at[next_at].charge && 0 == at[next_at].radical && - 3 == get_endpoint_valence( at[next_at].el_number ) ) { - if ( 2 == at[next_at].valence && 3 == at[next_at].chem_bonds_valence ) { - type_N_next = 1; /* -N= */ - } else - if ( 3 == at[next_at].valence && 5 == at[next_at].chem_bonds_valence ) { - type_N_next = 2; /* unfortunately includes >N# */ - } - } -#endif - for ( j = 0; j < at[next_at].valence; j ++ ) { - bond_type = get_allowed_stereo_bond_type( (int)at[next_at].bond_type[j] ); - if ( bond_type == BOND_ALTERN ) - num_alt_next ++; - else - if ( bond_type == BOND_DOUBLE ) { - num_2s_next ++; -#if ( N_V_STEREOBONDS == 1 ) - next_next_at = at[next_at].neighbor[j]; - if ( 0 <= (n2sh = bIsSuitableHeteroInpAtom( at + next_next_at )) ) { - num_2s_hetero_next[n2sh] ++; /* n2sh=0 -> =N- or =NH; n2sh=1 -> =O */ - } -#endif - } else - if ( bond_type != BOND_SINGLE && bond_type != BOND_TAUTOM ) { - num_wrong_bonds_2 ++; -#if ( ONE_BAD_SB_NEIGHBOR == 1 ) - if ( num_wrong_bonds_1 > 1 || num_wrong_bonds_1 && 2 >= at[cur_at].valence ) { - break; /* wrong bond type */ - } else { - continue; - } -#else - break; /* wrong bond type */ -#endif - } - } - /* figure out whether the at[cur_at]--at[next_at] bond may not be stereogenic */ - -#if ( N_V_STEREOBONDS == 1 ) - if ( 3 == (type_N | type_N_next) && - ( 2 == type_N && !bIsOxide( at, cur_at ) || - 2 == type_N_next && !bIsOxide( at, next_at ) ) ) { - bFound = 0; - } else -#endif - if ( j < at[next_at].valence || /* at[next_at] has a wrong bond type*/ - (num_alt_next>0) + (num_2s_next>0) != 1 /* only one type of stereogenic bond permitted */ - ) { - bFound = 0; - } else - if ( 2 < num_2s_next ) { - bFound = 0; - } else - if ( 2 == num_2s_next ) { - if ( 2 == at[next_at].valence ) { - ; /* only one double bond permitted except cumulenes */ -#if ( N_V_STEREOBONDS == 1 ) - } else - if ( 1 == (num_2s_hetero_next[0] | num_2s_hetero_next[1]) && - 3 == at[next_at].valence + at[next_at].num_H && - 5 == at[next_at].chem_bonds_valence + at[next_at].num_H && - 3 == get_endpoint_valence( at[next_at].el_number ) && - (!type_N || bIsOxide( at, next_at )) ) { - ; /* - * found: - * - * \ / \ / \ / - * \ / \ / \ / - * N==C or N==C or N==N - * // \ // \ // \ - * O ^ \ N ^ \ O ^ \ - * | | | - * | | | - * at[next_at] at[next_at] at[next_at] - */ -#endif - } else { - bFound = 0; - } - } - - } - if ( bFound ) { - num_stereo_bonds++; - } - } - - if ( (num_alt>0) + (num_2s>0) != 1 || !num_stereo_bonds ) - return 0; - if ( num_2s > 1 ) { -#if ( N_V_STEREOBONDS == 1 ) - if ( 2 == num_2s && - 1 == (num_2s_hetero[0] | num_2s_hetero[1]) && - 3 == at[cur_at].valence + at[cur_at].num_H && - 5 == at[cur_at].chem_bonds_valence + at[cur_at].num_H && - 3 == get_endpoint_valence( at[cur_at].el_number ) ) { - ; - } else { - return 0; - } -#else - return 0; -#endif - } - - return num_stereo_bonds; -} -/*************************************************************/ -int half_stereo_bond_action( int nParity, int bUnknown, int bIsotopic, int vABParityUnknown ) -{ -#define AB_NEGATIVE 0x10 -#define AB_UNKNOWN 0x20 - int nAction; - - if ( nParity == AB_PARITY_NONE ) - return AB_PARITY_NONE; - - /* Unknown (type 1) in the parity value may come from the 'Either' single bond only */ - /* Treat it as a known single bond geometry and unknown (Either) double bond */ - if ( nParity == vABParityUnknown /*AB_PARITY_UNKN*/ ) - nParity = AB_PARITY_ODD | AB_UNKNOWN; - if ( nParity == -vABParityUnknown /*AB_PARITY_UNKN*/ ) - nParity = AB_PARITY_ODD | AB_UNKNOWN | AB_NEGATIVE; - - /* make positive, replace AB_PARITY_EVEN with AB_PARITY_ODD */ - if ( nParity < 0 ) - nParity = ((nParity == -AB_PARITY_EVEN)? AB_PARITY_ODD : (-nParity)) | AB_NEGATIVE; - else - if (nParity == AB_PARITY_EVEN) - nParity = AB_PARITY_ODD; - - /* Unknown (type 2): was detected in the double bond attribute */ - /* (this 'unknown' came from 'Either' double bond) */ - /* Treat both unknowns in the same way */ - if ( bUnknown ) - nParity |= AB_UNKNOWN; - - if ( bIsotopic ) { - switch ( nParity ) { - case AB_PARITY_ODD: - case AB_PARITY_ODD | AB_NEGATIVE: - nAction = AB_PARITY_CALC; - break; - case AB_PARITY_ODD | AB_UNKNOWN: - case AB_PARITY_UNDF | AB_UNKNOWN: - case AB_PARITY_ODD | AB_UNKNOWN | AB_NEGATIVE: - case AB_PARITY_UNDF | AB_UNKNOWN | AB_NEGATIVE: - nAction = vABParityUnknown /*AB_PARITY_UNKN*/; - break; - case AB_PARITY_IISO: - case AB_PARITY_IISO | AB_UNKNOWN: - nAction = AB_PARITY_NONE; - break; - case AB_PARITY_UNDF: - case AB_PARITY_UNDF | AB_NEGATIVE: - nAction = AB_PARITY_UNDF; - break; - default: - nAction = -1; /* program error */ - } - } else { - /* Non-isotopic */ - switch ( nParity ) { - case AB_PARITY_ODD: - nAction = AB_PARITY_CALC; - break; - case AB_PARITY_ODD | AB_UNKNOWN: - case AB_PARITY_UNDF | AB_UNKNOWN: - nAction = vABParityUnknown /*AB_PARITY_UNKN*/; - break; - /* case AB_PARITY_ODD | AB_UNKNOWN | AB_NEGATIVE: */ - case AB_PARITY_UNDF: - nAction = AB_PARITY_UNDF; - break; - case AB_PARITY_ODD | AB_UNKNOWN | AB_NEGATIVE: - case AB_PARITY_ODD | AB_NEGATIVE: - case AB_PARITY_IISO: - case AB_PARITY_IISO | AB_UNKNOWN: - case AB_PARITY_UNDF | AB_NEGATIVE: - case AB_PARITY_UNDF | AB_UNKNOWN | AB_NEGATIVE: - nAction = AB_PARITY_NONE; - break; - default: - nAction = -1; /* program error */ - } - } - return nAction; -#undef AB_NEGATIVE -#undef AB_UNKNOWN -} -/*************************************************************/ -int set_stereo_bonds_parity( sp_ATOM *out_at, inp_ATOM *at, int at_1, inp_ATOM *at_removed_H, int num_removed_H, - INCHI_MODE nMode, QUEUE *q, AT_RANK *nAtomLevel, S_CHAR *cSource, - AT_RANK min_sb_ring_size, int bPointedEdgeStereo, int vABParityUnknown ) -{ - int j, k, next_at_1, i_next_at_1, i_next_at_2, at_2, next_at_2, num_stereo_bonds, bFound, bAllene; - int bond_type, num_2s_1, num_alt_1; - int num_2s_2, num_alt_2; -#if ( ONE_BAD_SB_NEIGHBOR == 1 ) - int num_wrong_bonds_1, num_wrong_bonds_2; -#endif -#if ( N_V_STEREOBONDS == 1 ) - int n2sh, num_2s_hetero[2], num_2s_hetero_next[2], next_next_at, type_N, type_N_next; -#endif - int num_stored_stereo_bonds, num_stored_isotopic_stereo_bonds; - int chain_length, num_chains, cur_chain_length; - int all_at_2[MAX_NUM_STEREO_BONDS]; - int all_pos_1[MAX_NUM_STEREO_BONDS], all_pos_2[MAX_NUM_STEREO_BONDS]; - S_CHAR all_unkn[MAX_NUM_STEREO_BONDS]; - int /*at_1_parity, at_2_parity,*/ nUnknown, stop=0; - - /* at_1_parity = AB_PARITY_NONE; */ /* do not know */ - /* check valence */ - if ( MAX_NUM_STEREO_BOND_NEIGH < at[at_1].valence+at[at_1].num_H || - MIN_NUM_STEREO_BOND_NEIGH > at[at_1].valence+at[at_1].num_H ) - return 0; - if ( !bCanAtomHaveAStereoBond( at[at_1].elname, at[at_1].charge, at[at_1].radical ) ) - return 0; - if ( at[at_1].c_point ) - return 0; /* rejects atoms that can lose or gain a (positive) charge. 01-24-2003 */ - - /* middle cumulene atoms, for example, =C=, should be ignored here */ - /* only atoms at the ends of cumulene chains are considered. */ - if ( !at[at_1].num_H && 2 == at[at_1].valence && - BOND_DOUBLE == get_allowed_stereo_bond_type( (int)at[at_1].bond_type[0] ) && - BOND_DOUBLE == get_allowed_stereo_bond_type( (int)at[at_1].bond_type[1] ) ) { - return 0; - } - - /* count bonds and find the second atom on the stereo bond */ - num_2s_1 = num_alt_1 = 0; - chain_length = 0; - num_chains = 0; -#if ( ONE_BAD_SB_NEIGHBOR == 1 ) - num_wrong_bonds_1 = 0; -#endif -#if ( N_V_STEREOBONDS == 1 ) - num_2s_hetero[0] = num_2s_hetero[1] = type_N = 0; - if ( 0 == at[at_1].num_H && 0 == at[at_1].charge && 0 == at[at_1].radical && - 3 == get_endpoint_valence( at[at_1].el_number ) ) { - if ( 2 == at[at_1].valence && 3 == at[at_1].chem_bonds_valence ) { - type_N = 1; - } else - if ( 3 == at[at_1].valence && 5 == at[at_1].chem_bonds_valence ) { - type_N = 2; /* unfortunately includes >N# */ - } - } -#endif - for ( i_next_at_1 = 0, num_stereo_bonds = 0; i_next_at_1 < at[at_1].valence; i_next_at_1 ++ ) { - nUnknown = (at[at_1].bond_stereo[i_next_at_1] == STEREO_DBLE_EITHER); - bond_type = get_allowed_stereo_bond_type( (int)at[at_1].bond_type[i_next_at_1] ); - at_2 = -1; /* not found */ - if ( bond_type == BOND_ALTERN || - bond_type == BOND_DOUBLE ) { - next_at_1 = at_2 = at[at_1].neighbor[i_next_at_1]; - next_at_2 = at_1; - } - switch ( bond_type ) { - case BOND_ALTERN: - num_alt_1 ++; -#if ( FIND_RING_SYSTEMS == 1 ) - if ( at[at_1].nRingSystem != at[at_2].nRingSystem ) - continue; /* reject alt. bond connecting different ring systems */ -#endif - if ( (nMode & CMODE_NO_ALT_SBONDS) || - !bCanAtomHaveAStereoBond( at[at_2].elname, at[at_2].charge, at[at_2].radical ) ) { - continue; /* reject non-stereogenic bond to neighbor ord. #i_next_at_1 */ - } - break; - case BOND_DOUBLE: - /* check for cumulene/allene */ - num_2s_1++; - cur_chain_length = 0; - if ( bCanAtomBeTerminalAllene( at[at_1].elname, at[at_1].charge, at[at_1].radical ) ) { - /* - * Example of cumulene - * chain length = 2: >X=C=C=Y< - * | | | | - * 1st cumulene atom= at_1 | | at_2 =last cumlene chain atom - * next to at_1= next_at_1 next_at_2 =previous to at_2 - * - * chain length odd: stereocenter on the middle atom ( 1=> allene ) - * chain length even: "long stereogenic bond" - */ - while ((bAllene = - !at[at_2].num_H && at[at_2].valence == 2 && - BOND_DOUBLE == get_allowed_stereo_bond_type( (int)at[at_2].bond_type[0] ) && - BOND_DOUBLE == get_allowed_stereo_bond_type( (int)at[at_2].bond_type[1] )) && - bCanAtomBeMiddleAllene( at[at_2].elname, at[at_2].charge, at[at_2].radical ) ) { - k = ((int)at[at_2].neighbor[0]==next_at_2); /* opposite neighbor position */ - next_at_2 = at_2; - nUnknown += (at[at_2].bond_stereo[k] == STEREO_DBLE_EITHER); - at_2 = (int)at[at_2].neighbor[k]; - cur_chain_length ++; /* count =C= atoms */ - } - if ( cur_chain_length ) { - num_chains ++; - if ( bAllene /* at the end of the chain atom Y is =Y=, not =Y< or =Y- */ || - !bCanAtomBeTerminalAllene( at[at_2].elname, at[at_2].charge, at[at_2].radical ) ) { - cur_chain_length = 0; - continue; /* ignore: does not fit cumulene description; go to check next at_1 neighbor */ - } - chain_length = cur_chain_length; /* accept a stereogenic cumulele */ - } - } -#if ( N_V_STEREOBONDS == 1 ) - if ( !cur_chain_length && - 0 <= (n2sh = bIsSuitableHeteroInpAtom( at + at_2 )) ) { - num_2s_hetero[n2sh] ++; /* n2sh=0 -> =N- or =NH; n2sh=1 -> =O */ - } -#endif - if ( !cur_chain_length && - !bCanAtomHaveAStereoBond( at[at_2].elname, at[at_2].charge, at[at_2].radical ) ) { - continue; /* reject non-stereogenic bond to neighbor #i_next_at_1 */ - } - - break; - - case BOND_SINGLE: - case BOND_TAUTOM: - continue; /* reject non-stereogenic bond to neighbor #i_next_at_1 */ - default: -#if ( ONE_BAD_SB_NEIGHBOR == 1 ) - num_wrong_bonds_1 ++; - continue; -#else - return 0; /* wrong bond type; */ -#endif - } - - /* check atom at the opposite end of possibly stereogenic bond */ - - bFound = (at_2 >= 0 && at_1 > at_2 ); /* i_next_at_1 = at_1 stereogenic bond neighbor attachment number */ - - if ( bFound ) { - /* check "at_2" atom on the opposite side of the bond or cumulene chain */ - if ( MAX_NUM_STEREO_BOND_NEIGH < at[at_2].valence+at[at_2].num_H || - MIN_NUM_STEREO_BOND_NEIGH > at[at_2].valence+at[at_2].num_H ) - continue; - - /* check at_2 neighbors and bonds */ - num_2s_2 = num_alt_2 = 0; -#if ( N_V_STEREOBONDS == 1 ) - num_2s_hetero_next[0] = num_2s_hetero_next[1] = type_N_next = 0; - if ( 0 == at[at_2].num_H && 0 == at[at_2].charge && 0 == at[at_2].radical && - 3 == get_endpoint_valence( at[at_2].el_number ) ) { - if ( 2 == at[at_2].valence && 3 == at[at_2].chem_bonds_valence ) { - type_N_next = 1; /* -N= */ - } else - if ( 3 == at[at_2].valence && 5 == at[at_2].chem_bonds_valence ) { - type_N_next = 2; /* unfortunately includes >N# */ - } - } -#endif - i_next_at_2 = -1; /* unassigned mark */ -#if ( ONE_BAD_SB_NEIGHBOR == 1 ) - num_wrong_bonds_2 = 0; -#endif - for ( j = 0; j < at[at_2].valence; j ++ ) { - bond_type = get_allowed_stereo_bond_type( (int)at[at_2].bond_type[j] ); - if ( !bond_type ) { -#if ( ONE_BAD_SB_NEIGHBOR == 1 ) - num_wrong_bonds_2 ++; - continue; /* this bond type is not allowed to be adjacent to a stereo bond */ -#else - break; -#endif - } - if ( bond_type == BOND_DOUBLE ) { - num_2s_2 ++; -#if ( N_V_STEREOBONDS == 1 ) - next_next_at = at[at_2].neighbor[j]; - if ( 0 <= (n2sh = bIsSuitableHeteroInpAtom( at + next_next_at )) ) { - num_2s_hetero_next[n2sh] ++; /* n2sh=0 -> =N- or =NH; n2sh=1 -> =O */ - } -#endif - } else { - num_alt_2 += ( bond_type == BOND_ALTERN ); - } - if ( (int)at[at_2].neighbor[j] == next_at_2 ) - i_next_at_2 = j; /* assigned */ - } - if ( -#if ( ONE_BAD_SB_NEIGHBOR == 1 ) - num_wrong_bonds_2 > 1 || num_wrong_bonds_2 && 2 >= at[at_2].valence || -#else - j < at[at_2].valence /* "next" has a wrong bond type*/ || -#endif - (num_alt_2>0) + (num_2s_2>0) != 1 || /* all double XOR all alt bonds only */ - /* num_2s_2 > 1 ||*/ /* only one double bond permitted */ - i_next_at_2 < 0 /* atom next to the opposite atom not found */ ) { - bFound = 0; - } else - if ( at[at_2].c_point ) { - bFound = 0; /* rejects atoms that can lose or gain a (positive) charge. 01-24-2003 */ - } else - if ( num_2s_2 > 2 ) { - bFound = 0; - } else -#if ( N_V_STEREOBONDS == 1 ) - if ( 3 == (type_N | type_N_next) && - ( 2 == type_N && !bIsOxide( at, at_1 ) || - 2 == type_N_next && !bIsOxide( at, at_2 ) ) ) { - bFound = 0; - } else -#endif - if ( 2 == num_2s_2 ) { -#if ( N_V_STEREOBONDS == 1 ) - if ( !chain_length && - 1 == (num_2s_hetero_next[0] | num_2s_hetero_next[1]) && - 3 == at[at_2].valence + at[at_2].num_H && - 5 == at[at_2].chem_bonds_valence + at[at_2].num_H && - 3 == get_endpoint_valence( at[at_2].el_number ) && - (!type_N || bIsOxide( at, at_2 )) ) { - /* - * found: - * - * \ / \ / \ / - * \ / \ / \ / - * N==C or N==C or N==N - * // \ // \ // \ - * O ^ \ N ^ \ O ^ \ - * | | | - * | | | - * at[at_2] at[at_2] at[at_2] - */ - ; - } else { - bFound = 0; - } -#else - bFound = 0; -#endif - } - - - if ( chain_length && num_alt_2 ) - return 0; /* allow no alt bonds in cumulenes */ - } - if ( bFound ) { - all_pos_1[num_stereo_bonds] = i_next_at_1; /* neighbor to at_1 position */ - all_pos_2[num_stereo_bonds] = i_next_at_2; /* neighbor to at_2 position */ - all_at_2[num_stereo_bonds] = at_2; /* at_2 */ - all_unkn[num_stereo_bonds] = nUnknown; /* stereogenic bond has Unknown configuration */ - /* - if ( (at[at_1].bUsed0DParity & 2) || (at[at_2].bUsed0DParity & 2) ) { - for ( k = 0; k < MAX_NUM_STEREO_BONDS && at[at_1].sb_parity[k]; k ++ ) { - if ( at[at_1].sb_neigh[k] == i_next_at_1 ) { - if ( at[at_1].sb_parity[k] == AB_PARITY_UNKN && !nUnknown ) { - all_unkn[num_stereo_bonds] = 1; - } - break; - } - } - } - */ - num_stereo_bonds ++; - } - } - if ( num_chains > 1 ) { - return 0; /* cannot be more than 1 cumulene chain. */ - } -#if ( ONE_BAD_SB_NEIGHBOR == 1 ) - if ( num_wrong_bonds_1 > 1 || num_wrong_bonds_1 && 2 >= at[at_1].valence ) { - return 0; /* wrong bond type */ - } -#endif - /* accept only short chains for now */ - /* chain_length=1: >C=C=C< tetrahedral center, allene */ - /* chain_length=2: >C=C=C=C< stereogenic bond, cumulene */ - if ( chain_length && (num_stereo_bonds != 1 || num_alt_1 || chain_length > MAX_CUMULENE_LEN) ) { - return 0; - } - - /* we need 1 double bond/chain XOR up to 3 arom. bonds */ - /* to have a stereogenic bond */ - if ( (num_alt_1>0) + (num_2s_1>0) != 1 || !num_stereo_bonds /*|| num_2s_1 > 1*/ ) - return 0; - - if ( num_2s_1 > 1 ) { -#if ( N_V_STEREOBONDS == 1 ) - if ( 2 == num_2s_1 && - 2 == type_N && - 1 == (num_2s_hetero[0] | num_2s_hetero[1]) && - 3 == at[at_1].valence + at[at_1].num_H && - 5 == at[at_1].chem_bonds_valence + at[at_1].num_H && - 3 == get_endpoint_valence( at[at_1].el_number ) ) { - ; - } else { - return 0; - } -#else - return 0; -#endif - } - - /* ================== calculate parities ====================== */ - - - /* find possibly stereo bonds and save them */ - num_stored_isotopic_stereo_bonds = 0; - num_stored_stereo_bonds = 0; - for ( k = 0; k < num_stereo_bonds; k ++ ) { - - int cur_parity, next_parity, abs_cur_parity, abs_next_parity, dot_prod_z; - S_CHAR z_dir1[3], z_dir2[3]; /* 3D vectors for half stereo bond parity direction */ - int chain_len_bits = MAKE_BITS_CUMULENE_LEN(chain_length); - int cur_parity_defined, next_parity_defined; - int cur_action, next_action, result_action; - - at_2 = all_at_2[k]; - i_next_at_1 = all_pos_1[k]; - -#if ( MIN_SB_RING_SIZE > 0 ) - if( at[at_1].nRingSystem == at[at_2].nRingSystem ) { - /* check min. ring size only if both double bond/cumulene */ - /* ending atoms belong to the same ring system */ - j = is_bond_in_Nmax_memb_ring( at, at_1, i_next_at_1, q, nAtomLevel, cSource, min_sb_ring_size ); - if ( j > 0 ) { - continue; - } else - if ( j < 0 ) { - return CT_STEREOBOND_ERROR; - } - } -#endif - - i_next_at_2 = all_pos_2[k]; - nUnknown = all_unkn[k]; - memset(z_dir1, 0, sizeof(z_dir1)); - memset(z_dir2, 0, sizeof(z_dir2)); - - /******************************************************************************** - * find atom parities (negative means parity due to H-isotopes only) - * and half stereo bond parity directions z_dir1, z_dir2. - * - * Bond can have unknown or undefined parity or no parity because of: - * 1. Geometry (poorly defined, cannot calculate, for example linear =C-F - * or =CHD with no geometry) -- Undefined parity - * H - * 2. Identical H atoms (no parity in principle, for example =C< ) - * -- No parity H - * - * 3. The user said double bond stereo is unknown - * or at least one of single bonds is in unknown direction - * -- Unknown parity - * - * These 3 cases (see above) are referred below as 1, 2, 3. - * Each of the cases may be present or not (2 possibilities) - * Total number of combination is 2*2*2=8 - * - * Since a case when all 3 are not present is a well-defined parity, - * we do not consider this case here. Then 2*2*2-1=7 cases are left. - * - * If several cases are present, list them below separated by "+". - * For example, 1+2 means (1) undefined geometry and (2) no parity - * is possible because of identical H atoms. - * - * N) Decision table, Non-isotopic, 2*2*2-1=7 cases: - * ================================================= - * none : 2+any: 1+2(e.g.=CH2); 1+2+3; 2; 2+3 AB_PARITY_NONE=0 - * undefined: 1 AB_PARITY_UNDF - * unknown : 1+3; 3 AB_PARITY_UNKN - * - * I) Decision table, Isotopic, 2*2*2-1=7 cases: - * ============================================= - * none : none - * undefined: 1; 1+2; 1+2+3; 2; 2+3 - * unknown : 1+3; 3 - * - * Note: When defining identical atoms H atoms in case 2, - * Isotopic and Non-isotopic cases are different: - * N: do NOT take into account the isotopic composition of H atoms - * I: DO take into account the isotopic composition of H atoms - * (it is assumed that H isotopes are always different) - * - * half_stereo_bond_parity() returns: - * ================================== - * Note: half_stereo_bond_parity() is unaware of case 3. - * - * can't be a half of a stereo bond AB_PARITY_NONE - * 1, isotopic & non-isotopic: AB_PARITY_UNDF - * 1, isotopic only -AB_PARITY_UNDF - * 2, no parity: identical H isotopes AB_PARITY_IISO - * 3, 'Either' single bond(s) AB_PARITY_UNKN ??? - * 3, 'Either' single bond(s), iso H -AB_PARITY_UNKN ??? - * defined parity AB_PARITY_ODD, AB_PARITY_EVEN - * defined parity for isotopic only: -AB_PARITY_ODD, -AB_PARITY_EVEN - * - * Resultant value for the stereo bond parity - * ---+-------------------+-------+--------+----------------+ - * 3? | half_stereo_bond_ | N or I| case 1,| bond parity | - * | parity()= | | 2 or 3 | | - * ---+-------------------+-------+--------+----------------+ - * ( AB_PARITY_ODD/EVEN) => N&I: - => AB_PARITY_CALC (=6, calc.later) - * 3+( AB_PARITY_ODD/EVEN) => N&I: 3 => AB_PARITY_UNKN (=3) - * (-AB_PARITY_ODD/EVEN) => N: 2 => AB_PARITY_NONE (=0) - * (-AB_PARITY_ODD/EVEN) => I: - => AB_PARITY_CALC - * 3+(-AB_PARITY_ODD/EVEN) => N: 2+3 => AB_PARITY_UNDF (=4) - * 3+(-AB_PARITY_ODD/EVEN) => I: 3 => AB_PARITY_UNKN - * ( AB_PARITY_IISO ) => N: 1+2, 2 => AB_PARITY_NONE (=0) - * ( AB_PARITY_IISO ) => I: 1+2, 2 => AB_PARITY_UNDF - * 3+( AB_PARITY_IISO ) => N: 1+2+3,2+3=> AB_PARITY_NONE - * 3+( AB_PARITY_IISO ) => I: 1+2+3,2+3=> AB_PARITY_UNDF - * ( AB_PARITY_UNDF ) => N&I: 1 => AB_PARITY_UNDF - * 3+( AB_PARITY_UNDF ) => N&I: 1+3 => AB_PARITY_UNKN - * (-AB_PARITY_UNDF ) => N: 1+2 => AB_PARITY_NONE - * (-AB_PARITY_UNDF ) => I: 1 => AB_PARITY_UNDF - * 3+(-AB_PARITY_UNDF ) => N: 1+2+3 => AB_PARITY_NONE - * 3+(-AB_PARITY_UNDF ) => I: 1+3 => AB_PARITY_UNKN - * ---+-------------------+-------+--------+----------------+ - - * If bond parity is undefined because abs(dot_prod_z) < MIN_DOT_PROD - * then replace: AB_PARITY_CALC - * with: AB_PARITY_UNDF - * Joining two half_bond_parity() results: - * - * - * atom1 \ atom2 | AB_PARITY_NONE AB_PARITY_UNKN AB_PARITY_UNDF AB_PARITY_CALC - * ----------------+--------------------------------------------------------------- - *0=AB_PARITY_NONE | AB_PARITY_NONE AB_PARITY_NONE AB_PARITY_NONE AB_PARITY_NONE - *3=AB_PARITY_UNKN | AB_PARITY_UNKN AB_PARITY_UNKN AB_PARITY_UNKN - *4=AB_PARITY_UNDF | AB_PARITY_UNDF AB_PARITY_UNDF - *6=AB_PARITY_CALC | AB_PARITY_CALC - * - * that is, take min out of the two - *********************************************************************************/ - - cur_parity = half_stereo_bond_parity( at, at_1, at_removed_H, num_removed_H, - z_dir1, bPointedEdgeStereo, vABParityUnknown ); - next_parity = half_stereo_bond_parity( at, at_2, at_removed_H, num_removed_H, - z_dir2, bPointedEdgeStereo, vABParityUnknown ); - - if ( RETURNED_ERROR(cur_parity) || RETURNED_ERROR(next_parity) ) { - return CT_CALC_STEREO_ERR; - } - if ( (at[at_1].bUsed0DParity & FlagSB_0D) || (at[at_1].bUsed0DParity & FlagSB_0D) ) { - FixSb0DParities( at, /* at_removed_H, num_removed_H,*/ chain_length, - at_1, i_next_at_1, z_dir1, - at_2, i_next_at_2, z_dir2, &cur_parity, &next_parity ); - } - - if ( cur_parity == AB_PARITY_NONE || abs(cur_parity) == AB_PARITY_IISO ) { - continue; - } - if ( next_parity == AB_PARITY_NONE || abs(next_parity) == AB_PARITY_IISO ) { - continue; - } - - cur_action = half_stereo_bond_action( cur_parity, nUnknown, 0, vABParityUnknown ); /* -1 => program error */ - next_action = half_stereo_bond_action( next_parity, nUnknown, 0, vABParityUnknown ); - result_action = inchi_min(cur_action, next_action); - - if ( result_action == -1 ) { - stop = 1; /* program error */ - } - - abs_cur_parity = abs( cur_parity ); - abs_next_parity = abs( next_parity ); - cur_parity_defined = ATOM_PARITY_WELL_DEF(abs_cur_parity); - next_parity_defined = ATOM_PARITY_WELL_DEF(abs_next_parity); - - - if ( cur_parity_defined && next_parity_defined ) { - /* find how the whole bond parity depend on geometry */ - /* if dot_prod_z < 0 then bond_parity := 3-bond_parity */ - /* can be done only for a well-defined geometry */ - /* - dot_prod_z = (chain_len_bits & BIT_CUMULENE_CHI)? - triple_prod_char( at, at_1, i_next_at_1, z_dir1, at_2, i_next_at_2, z_dir2 ) : - dot_prodchar3(z_dir1, z_dir2); - */ - dot_prod_z = (chain_len_bits && BOND_CHAIN_LEN(chain_len_bits)%2)? - triple_prod_char( at, at_1, i_next_at_1, z_dir1, at_2, i_next_at_2, z_dir2 ) : - dot_prodchar3(z_dir1, z_dir2); - - if ( abs(dot_prod_z) < MIN_DOT_PROD ) { - /* The geometry is not well-defined. Eliminate AB_PARITY_CALC */ - result_action = inchi_min( result_action, AB_PARITY_UNDF ); - } - } else { - dot_prod_z = 0; - } - - if ( result_action != AB_PARITY_NONE && result_action != -1 ) { - /* stereo, no isotopes (only positive) */ - if ( cur_parity > 0 && next_parity > 0 ) { - if ( save_a_stereo_bond( dot_prod_z, result_action | chain_len_bits, - at_1, i_next_at_1, out_at[at_1].stereo_bond_neighbor, - out_at[at_1].stereo_bond_ord, out_at[at_1].stereo_bond_z_prod, - out_at[at_1].stereo_bond_parity, - at_2, i_next_at_2, out_at[at_2].stereo_bond_neighbor, - out_at[at_2].stereo_bond_ord, out_at[at_2].stereo_bond_z_prod, - out_at[at_2].stereo_bond_parity) ) { - if ( !out_at[at_1].parity || - cur_parity_defined && !ATOM_PARITY_WELL_DEF(abs(out_at[at_1].parity)) ) { - out_at[at_1].parity = cur_parity; - memcpy( out_at[at_1].z_dir, z_dir1, sizeof(out_at[0].z_dir) ); - } - if ( !out_at[at_2].parity || - next_parity_defined && !ATOM_PARITY_WELL_DEF(abs(out_at[at_2].parity)) ) { - out_at[at_2].parity = next_parity; - memcpy( out_at[at_2].z_dir, z_dir2, sizeof(out_at[0].z_dir) ); - } - out_at[at_1].bAmbiguousStereo |= at[at_1].bAmbiguousStereo; - out_at[at_2].bAmbiguousStereo |= at[at_2].bAmbiguousStereo; - num_stored_stereo_bonds ++; - } - } - } - - /* stereo + isotopic (all non-zero) */ - cur_action = half_stereo_bond_action( cur_parity, nUnknown, 1, vABParityUnknown ); /* -1 => program error */ - next_action = half_stereo_bond_action( next_parity, nUnknown, 1, vABParityUnknown ); - result_action = inchi_min(cur_action, next_action); - cur_parity = abs_cur_parity; - next_parity = abs_next_parity; - if ( result_action != AB_PARITY_NONE && result_action != -1 ) { - /* stero, isotopic */ - if ( cur_parity > 0 && next_parity > 0 ) { - if( save_a_stereo_bond( dot_prod_z, result_action | chain_len_bits, - at_1, i_next_at_1, out_at[at_1].stereo_bond_neighbor2, - out_at[at_1].stereo_bond_ord2, out_at[at_1].stereo_bond_z_prod2, - out_at[at_1].stereo_bond_parity2, - at_2, i_next_at_2, out_at[at_2].stereo_bond_neighbor2, - out_at[at_2].stereo_bond_ord2, out_at[at_2].stereo_bond_z_prod2, - out_at[at_2].stereo_bond_parity2) ) { - if ( !out_at[at_1].parity2 || - cur_parity_defined && !ATOM_PARITY_WELL_DEF(abs(out_at[at_1].parity2)) ) { - out_at[at_1].parity2 = cur_parity /*| chain_len_bits*/; - if ( !out_at[at_1].parity ) { - memcpy( out_at[at_1].z_dir, z_dir1, sizeof(out_at[0].z_dir) ); - } - } - if ( !out_at[at_2].parity2 || /* next line changed from abs(out_at[at_2].parity) 2006-03-05 */ - next_parity_defined && !ATOM_PARITY_WELL_DEF(abs(out_at[at_2].parity2)) ) { - out_at[at_2].parity2 = next_parity /*| chain_len_bits*/; - if ( !out_at[at_2].parity ) { - memcpy( out_at[at_2].z_dir, z_dir2, sizeof(out_at[0].z_dir) ); - } - } - out_at[at_1].bAmbiguousStereo |= at[at_1].bAmbiguousStereo; - out_at[at_2].bAmbiguousStereo |= at[at_2].bAmbiguousStereo; - num_stored_isotopic_stereo_bonds ++; - } - } - } else - if ( result_action == -1 ) { - stop = 1; /* program error? */ - } - - } - if ( stop ) { - return CT_CALC_STEREO_ERR; - } - return /*num_stored_stereo_bonds+*/ num_stored_isotopic_stereo_bonds; -} - - -/*********************************************************************/ -/* if isotopic H, D, T added, can the atom be a stereo center? */ -#if ( NEW_STEREOCENTER_CHECK == 1 ) -/* int bCanInpAtomBeAStereoCenter( inp_ATOM *at, int cur_at ) */ -int can_be_a_stereo_atom_with_isotopic_H( inp_ATOM *at, int cur_at, int bPointedEdgeStereo ) -{ - int nNumNeigh; - if ( (nNumNeigh = bCanInpAtomBeAStereoCenter( at, cur_at, bPointedEdgeStereo )) && - at[cur_at].valence + at[cur_at].num_H == nNumNeigh && - at[cur_at].num_H <= NUM_H_ISOTOPES - ) { - return 1; - } - return 0; -} -#else -int can_be_a_stereo_atom_with_isotopic_H( inp_ATOM *at, int cur_at ) -{ - int j, ret = 0; - if ( bCanAtomBeAStereoCenter( at[cur_at].elname, at[cur_at].charge, at[cur_at].radical ) && - at[cur_at].valence + at[cur_at].num_H == MAX_NUM_STEREO_ATOM_NEIGH && - at[cur_at].num_H < MAX_NUM_STEREO_ATOM_NEIGH - ) { - - for ( j = 0, ret=1; ret && j < at[cur_at].valence; j ++ ) { - if ( (at[cur_at].bond_type[j] & ~BOND_MARK_ALL) != BOND_SINGLE ) { - ret = 0; - } - } - } - return ret; -} -#endif -/***************************************************************/ -int GetStereocenter0DParity( inp_ATOM *at, int cur_at, int j1, AT_NUMB nSbNeighOrigAtNumb[], int nFlag ) -{ - int parity = AB_PARITY_NONE; - if ( at[cur_at].p_parity && (j1 == MAX_NUM_STEREO_ATOM_NEIGH-1 || j1 == MAX_NUM_STEREO_ATOM_NEIGH) ) { - int i, num_trans_inp, num_trans_neigh; - AT_NUMB nInpNeighOrigAtNumb[MAX_NUM_STEREO_ATOM_NEIGH]; - for ( i = 0; i < MAX_NUM_STEREO_ATOM_NEIGH; i ++ ) { - nInpNeighOrigAtNumb[i] = at[cur_at].p_orig_at_num[i]; - if ( nInpNeighOrigAtNumb[i] == at[cur_at].orig_at_number ) { - nInpNeighOrigAtNumb[i] = 0; /* lone pair or explicit H */ - } - } - num_trans_inp = insertions_sort( nInpNeighOrigAtNumb, MAX_NUM_STEREO_ATOM_NEIGH, sizeof(nInpNeighOrigAtNumb[0]), comp_AT_NUMB ); - num_trans_neigh = insertions_sort( nSbNeighOrigAtNumb, j1, sizeof(nSbNeighOrigAtNumb[0]), comp_AT_NUMB ); - if ( j1 == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { - ; /*num_trans_neigh += j1;*/ /* the lone pair or implicit H is implicitly at the top of the list */ - } - if ( !memcmp( nInpNeighOrigAtNumb + MAX_NUM_STEREO_ATOM_NEIGH-j1, nSbNeighOrigAtNumb, j1*sizeof(AT_NUMB) ) ) { - if ( ATOM_PARITY_WELL_DEF(at[cur_at].p_parity) ) { - parity = 2 - (num_trans_inp + num_trans_neigh + at[cur_at].p_parity) % 2; - } else { - parity = at[cur_at].p_parity; - } - at[cur_at].bUsed0DParity |= nFlag; /* 0D parity used for streocenter parity */ - } - } - return parity; -} - -/*************************************************************** - * Get stereo atom parity for the current order of attachments - * The result in at[cur_at].parity is valid for previously removed - * explicit hydrogen atoms, including isotopic ones, that are located in at_removed_H[] - * The return value is a calculated parity. - */ -#define ADD_EXPLICIT_HYDROGEN_NEIGH 1 -#define ADD_EXPLICIT_LONE_PAIR_NEIGH 2 -int set_stereo_atom_parity( sp_ATOM *out_at, inp_ATOM *at, int cur_at, inp_ATOM *at_removed_H, - int num_removed_H, int bPointedEdgeStereo, int vABParityUnknown) -{ - int j, k, next_at, num_z, j1, nType, num_explicit_H, tot_num_iso_H, nMustHaveNumNeigh; - int num_explicit_iso_H[NUM_H_ISOTOPES+1]; /* numbers of removed hydrogen atoms */ - int index_H[MAX_NUM_STEREO_ATOM_NEIGH]; /* cannot have more than 4 elements: 1 H, 1 D, 1 T atom(s) */ - double z, sum_xyz[3], min_sine, triple_product; - double at_coord[MAX_NUM_STEREO_ATOM_NEIGH][3]; - double bond_len_xy[4], rmax=0.0, rmin=0.0; - double at_coord_center[3]; - int parity, bAmbiguous = 0, bAddExplicitNeighbor = 0, b2D = 0, n2DTetrahedralAmbiguity = 0; - int bIgnoreIsotopicH = (0 != (at[cur_at].cFlags & AT_FLAG_ISO_H_POINT)); - AT_NUMB nSbNeighOrigAtNumb[MAX_NUM_STEREO_ATOM_NEIGH]; - - out_at[cur_at].parity = - out_at[cur_at].parity2 = - out_at[cur_at].stereo_atom_parity = - out_at[cur_at].stereo_atom_parity2 = AB_PARITY_NONE; - parity = AB_PARITY_NONE; - - memset(num_explicit_iso_H, 0, sizeof(num_explicit_iso_H)); - num_explicit_H = 0; - -#if ( NEW_STEREOCENTER_CHECK == 1 ) - if ( !(nMustHaveNumNeigh = bCanInpAtomBeAStereoCenter( at, cur_at, bPointedEdgeStereo ) ) || - at[cur_at].num_H > NUM_H_ISOTOPES - ) { - goto exit_function; - } -#else - nMustHaveNumNeigh = MAX_NUM_STEREO_ATOM_NEIGH; - if ( !bCanAtomBeAStereoCenter( at[cur_at].elname, at[cur_at].charge, at[cur_at].radical ) || - at[cur_at].valence + at[cur_at].num_H != nMustHaveNumNeigh || - at[cur_at].num_H > NUM_H_ISOTOPES - ) { - goto exit_function; - } - for ( j = 0; j < at[cur_at].valence; j ++ ) { - if ( (at[cur_at].bond_type[j] & ~BOND_MARK_ALL) != BOND_SINGLE ) { - goto exit_function; - } - } -#endif - - /* numbers of isotopic H atoms */ - for ( j = 0, tot_num_iso_H = 0; j < NUM_H_ISOTOPES; j ++ ) { - if ( at[cur_at].num_iso_H[j] > 1 ) { - goto exit_function; /* two or more identical hydrogen isotopic neighbors */ - } - tot_num_iso_H += at[cur_at].num_iso_H[j]; - } - if ( bIgnoreIsotopicH ) { - tot_num_iso_H = 0; /* isotopic H considered subject to exchange => ignore isotopic */ - } - /* number of non-isotopic H atoms */ - if ( at[cur_at].num_H - tot_num_iso_H > 1 ) { - goto exit_function; /* two or more identical hydrogen non-isotopic neighbors */ - } - - /* count removed explicit terminal hydrogens attached to at[cur_at]. */ - /* the result is num_explicit_H. */ - /* Removed hydrogens are sorted in increasing isotopic shift order */ - if ( at_removed_H && num_removed_H > 0 ) { - for ( j = 0; j < num_removed_H; j ++ ) { - if ( at_removed_H[j].neighbor[0] == cur_at ) { - k = at_removed_H[j].iso_atw_diff; - /* iso_atw_diff values: H=>0, 1H=>1, D=2H=>2, T=3H=>3 */ - if ( k < 0 || k > NUM_H_ISOTOPES || bIgnoreIsotopicH ) - k = 0; /* treat wrong H isotopes as non-isotopic H */ - num_explicit_iso_H[k] ++; - index_H[num_explicit_H++] = j; - } - } - } - - /* coordinates initialization */ - num_z = 0; - sum_xyz[0] = sum_xyz[1] = sum_xyz[2] = 0.0; - - at_coord_center[0] = - at_coord_center[1] = - at_coord_center[2] = 0.0; - - /* fill out stereo center neighbors coordinates */ - /* and obtain the parity from the geometry */ - - for ( k = 0, j1 = 0; k < 2; k ++ ) { - switch( k ) { - - case 0: - /* add coordinates of removed hydrogens */ - for ( j = 0; j < num_explicit_H; j ++, j1 ++ ) { - next_at = index_H[j]; - /* use bond description located at removed_H atom */ - /* minus sign at get_z_coord: at_removed_H[] contains bonds TO at[cur_at], not FROM it. */ - /* Note: &at[(at_removed_H-at)+ next_at] == &at_removed_H[next_at] */ - z = -get_z_coord( at, (at_removed_H-at)+ next_at, 0 /*neighbor #*/, &nType, -(bPointedEdgeStereo & PES_BIT_POINT_EDGE_STEREO) ); - switch ( nType ) { - case ZTYPE_EITHER: - parity = vABParityUnknown /*AB_PARITY_UNKN*/ ; /* no parity: bond in "Either" direction. */ - goto exit_function; - case ZTYPE_UP: - case ZTYPE_DOWN: - nType = -nType; /* at_removed_H[] contains bonds TO the center, not from */ - b2D ++; - /* no break; here */ - case ZTYPE_3D: - num_z ++; - } - - nSbNeighOrigAtNumb[j1] = at_removed_H[next_at].orig_at_number; - at_coord[j1][0] = at_removed_H[next_at].x-at[cur_at].x; - at_coord[j1][1] = at_removed_H[next_at].y-at[cur_at].y; - bond_len_xy[j1] = len2(at_coord[j1]); - /* bond_len_xy[j1] = sqrt(at_coord[j1][0]*at_coord[j1][0]+at_coord[j1][1]*at_coord[j1][1]); */ - at_coord[j1][2] = (nType==ZTYPE_3D? z : - nType==ZTYPE_UP? bond_len_xy[j1] : - nType==ZTYPE_DOWN? -bond_len_xy[j1] : 0.0 ); - } - break; - case 1: - /* add all coordinates of other neighboring atoms */ - for ( j = 0; j < at[cur_at].valence; j ++, j1 ++ ) { - next_at = at[cur_at].neighbor[j]; - z = get_z_coord( at, cur_at, j, &nType, (bPointedEdgeStereo & PES_BIT_POINT_EDGE_STEREO) ); - switch ( nType ) { - case ZTYPE_EITHER: - parity = vABParityUnknown /*AB_PARITY_UNKN*/; /* unknown parity: bond in "Either" direction. */ - goto exit_function; - case ZTYPE_UP: - case ZTYPE_DOWN: - b2D ++; - case ZTYPE_3D: - num_z ++; - } - - nSbNeighOrigAtNumb[j1] = at[next_at].orig_at_number; - at_coord[j1][0] = at[next_at].x-at[cur_at].x; - at_coord[j1][1] = at[next_at].y-at[cur_at].y; - bond_len_xy[j1] = len2(at_coord[j1]); - /* bond_len_xy[j1] = sqrt(at_coord[j1][0]*at_coord[j1][0]+at_coord[j1][1]*at_coord[j1][1]); */ - at_coord[j1][2] = (nType==ZTYPE_3D? z : - nType==ZTYPE_UP? bond_len_xy[j1] : - nType==ZTYPE_DOWN? -bond_len_xy[j1] : 0.0 ); - } - break; - } - } - /* j1 is the number of explicit neighbors (that is, all neighbors except implicit H) */ - - b2D = (b2D == num_z && num_z); /* 1 => two-dimensional */ - - if ( MAX_NUM_STEREO_ATOM_NEIGH != at[cur_at].valence+num_explicit_H && - MAX_NUM_STEREO_ATOM_NEIGH-1 != at[cur_at].valence+num_explicit_H ) { - /* not enough geometry data to find the central atom parity */ - if ( nMustHaveNumNeigh == at[cur_at].valence+at[cur_at].num_H && - at[cur_at].num_H > 1 ) { - /* only isotopic parity is possible; no non-isotopic parity */ - if ( parity == vABParityUnknown /*AB_PARITY_UNKN*/ ) { - parity = -vABParityUnknown /*AB_PARITY_UNKN*/; /* the user marked the center as "unknown" */ - } else { - parity = -AB_PARITY_UNDF; /* not enough geometry; only isotopic parity is possible */ - } - } else { - parity = AB_PARITY_NONE; /* not a stereocenter at all */ - } - goto exit_function; - } - /* make all vector lengths equal to 1; exit if too short. 9-10-2002 */ - for ( j = 0; j < j1; j ++ ) { - z = len3( at_coord[j] ); - if ( z < MIN_BOND_LEN ) { - /* bond length is too small: use 0D parities */ - if ( AB_PARITY_NONE == (parity = GetStereocenter0DParity( at, cur_at, j1, nSbNeighOrigAtNumb, FlagSC_0D )) ) { - parity = AB_PARITY_UNDF; - } - goto exit_function; - } -#if ( STEREO_CENTER_BONDS_NORM == 1 ) - else { - mult3( at_coord[j], 1.0/z, at_coord[j] ); - } -#endif - rmax = j? inchi_max( rmax, z) : z; - rmin = j? inchi_min( rmin, z) : z; - } - if ( rmin / rmax < MIN_SINE ) { - /* bond ratio is too small: use 0D parities */ - if ( AB_PARITY_NONE == (parity = GetStereocenter0DParity( at, cur_at, j1, nSbNeighOrigAtNumb, FlagSC_0D )) ) { - parity = AB_PARITY_UNDF; - } - goto exit_function; - } - for ( j = 0; j < j1; j ++ ) { - add3( sum_xyz, at_coord[j], sum_xyz ); - } - - - - /* here j1 is a number of neighbors including explicit terminal isotopic H */ - /* num_explicit_iso_H[0] = number of explicit non-isotopic hydrogen atom neighbors */ - j = j1; - /* Add Explicit Neighbor */ - if ( j1 == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { - /* add an explicit neighbor if possible */ - if ( nMustHaveNumNeigh == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { - bAddExplicitNeighbor = ADD_EXPLICIT_LONE_PAIR_NEIGH; - } else - if ( nMustHaveNumNeigh == MAX_NUM_STEREO_ATOM_NEIGH ) { - /* check whether an explicit non-isotopic hydrogen can be added */ - /* to an atom that is a stereogenic atom */ - if ( 1 == at[cur_at].num_H - num_explicit_H && /* the atom has only one one implicit hydrogen */ - 1 == at[cur_at].num_H - tot_num_iso_H ) { /* this hydrogen is non-isotopic */ - bAddExplicitNeighbor = ADD_EXPLICIT_HYDROGEN_NEIGH; - } - } - } - - if ( bAddExplicitNeighbor ) { - /*********************************************************** - * May happen only if (j1 == MAX_NUM_STEREO_ATOM_NEIGH-1) - * 3 neighbors only, no H-neighbors. Create and add coordinates of an implicit H - * or a fake 4th neighbor, that is, a lone pair - */ - if ( parity == vABParityUnknown /*AB_PARITY_UNKN*/ ) { - goto exit_function; /* the user insists the parity is unknown and the isotopic */ - /* composition of the neighbors does not contradict */ - } else - if ( num_z == 0 || are_3_vect_in_one_plane(at_coord, MIN_SINE) ) { - /* "hydrogen down" rule is needed to resolve an ambiguity */ - if ( num_z > 0 ) { - bAmbiguous |= AMBIGUOUS_STEREO; - } -#if ( APPLY_IMPLICIT_H_DOWN_RULE == 1 ) /* { */ - /* Although H should be at the top of the list, add it to the bottom. */ - /* This will be taken care of later by inverting parity 1<->2 */ - at_coord[j][0] = 0.0; - at_coord[j][1] = 0.0; -#if ( STEREO_CENTER_BONDS_NORM == 1 ) - at_coord[j][2] = -1.0; -#else - at_coord[j][2] = -(bond_len_xy[0]+bond_len_xy[1]+bond_len_xy[2])/3.0; -#endif -#else /* } APPLY_IMPLICIT_H_DOWN_RULE { */ -#if (ALWAYS_SET_STEREO_PARITY == 1) - parity = AB_PARITY_EVEN; /* suppose atoms are pre-sorted (testing) */ -#else - /* all 3 bonds are in one plain: try to get 0D parities */ - if ( AB_PARITY_NONE == (parity = GetStereocenter0DParity( at, cur_at, j1, nSbNeighOrigAtNumb, FlagSC_0D )) ) { - parity = AB_PARITY_UNDF; - } - /*parity = AB_PARITY_UNDF;*/ /* no parity can be calculated found */ -#endif - goto exit_function; -#endif /* } APPLY_IMPLICIT_H_DOWN_RULE */ - } else { - /* we have enough information to find implicit hydrogen coordinates */ - /* - at_coord[j][0] = -sum_x; - at_coord[j][1] = -sum_y; - at_coord[j][2] = -sum_z; - */ - copy3( sum_xyz, at_coord[j] ); - change_sign3( at_coord[j], at_coord[j] ); - z = len3( at_coord[j] ); -#if ( FIX_STEREO_SCALING_BUG == 1 ) - if ( z > 1.0 ) { - rmax *= z; - } else { - rmin *= z; - } -#else - /* Comparing the original bond lengths to lenghts derived from normalized to 1 */ - /* This bug leads to pronouncing legitimate stereogenic atoms */ - /* connected by 3 bonds "undefined" if in a nicely drawn 2D structure */ - /* bond lengths are about 20 or greater. Reported by Reinhard Dunkel 2005-08-05 */ - if ( bPointedEdgeStereo & PES_BIT_FIX_SP3_BUG ) { - /* coordinate scaling bug fixed here */ - if ( z > 1.0 ) { - rmax *= z; - } else { - rmin *= z; - } - } else { - /* original InChI v.1 bug */ - rmax = inchi_max( rmax, z ); - rmin = inchi_min( rmin, z ); - } -#endif - if ( z < MIN_BOND_LEN || rmin/rmax < MIN_SINE ) { - /* the new 4th bond is too short: try to get 0D parities */ - if ( AB_PARITY_NONE == (parity = GetStereocenter0DParity( at, cur_at, j1, nSbNeighOrigAtNumb, FlagSC_0D )) ) { - parity = AB_PARITY_UNDF; - } - goto exit_function; - } -#if ( STEREO_CENTER_BOND4_NORM == 1 ) - else { - mult3( at_coord[j], 1.0/z, at_coord[j] ); - } -#endif - } - } else - if ( j1 != MAX_NUM_STEREO_ATOM_NEIGH ) { - if ( parity == vABParityUnknown /*AB_PARITY_UNKN*/ ) { - parity = -AB_PARITY_UNDF; /* isotopic composition of H-neighbors contradicts 'unknown' */ - } - goto exit_function; - } else /* j1 == MAX_NUM_STEREO_ATOM_NEIGH */ - if ( num_z == 0 || are_4at_in_one_plane(at_coord, MIN_SINE) ) { - /* all four neighours in xy plane: undefined geometry. */ - if ( num_z > 0 ) { - bAmbiguous |= AMBIGUOUS_STEREO; - } - if ( parity != vABParityUnknown /*AB_PARITY_UNKN*/ ) { -#if (ALWAYS_SET_STEREO_PARITY == 1) - parity = AB_PARITY_EVEN; /* suppose atoms are pre-sorted (testing) */ -#else - /* all 4 bonds are in one plain: try to get 0D parities */ - if ( AB_PARITY_NONE == (parity = GetStereocenter0DParity( at, cur_at, j1, nSbNeighOrigAtNumb, FlagSC_0D )) ) { - parity = AB_PARITY_UNDF; - } else - if ( ATOM_PARITY_WELL_DEF( parity ) ) { - bAmbiguous &= ~AMBIGUOUS_STEREO; /* 0D parity has resolved the ambiguity */ - } -#endif - } - goto exit_function; - } - /*********************************************************** - * At this point we have 4 neighboring atoms. - * check for tetrahedral ambiguity in 2D case - */ - if ( b2D ) - { - - n2DTetrahedralAmbiguity = Get2DTetrahedralAmbiguity( at_coord, bAddExplicitNeighbor, (bPointedEdgeStereo & PES_BIT_FIX_SP3_BUG ) ); - - if ( 0 < n2DTetrahedralAmbiguity ) - { - if ( T2D_WARN & n2DTetrahedralAmbiguity ) { - bAmbiguous |= AMBIGUOUS_STEREO; - } - if ( T2D_UNDF & n2DTetrahedralAmbiguity ) { - if ( parity != vABParityUnknown /*AB_PARITY_UNKN*/ ) { -#if (ALWAYS_SET_STEREO_PARITY == 1) - parity = AB_PARITY_EVEN; /* suppose atoms are pre-sorted (testing) */ -#else - parity = AB_PARITY_UNDF; /* no parity */ -#endif - } - goto exit_function; - } - } else - if ( n2DTetrahedralAmbiguity < 0 ) { - bAmbiguous |= AMBIGUOUS_STEREO_ERROR; /* error */ - parity = AB_PARITY_UNDF; - goto exit_function; - } - } - - /************************************************************/ - /* Move coordinates origin to the neighbor #0 */ - for ( j = 1; j < MAX_NUM_STEREO_ATOM_NEIGH; j ++ ) { - diff3(at_coord[j], at_coord[0], at_coord[j]); - } - diff3(at_coord_center, at_coord[0], at_coord_center); - - /* - for ( k = 0; k < 3; k++ ) { - for ( j = 1; j < MAX_NUM_STEREO_ATOM_NEIGH; j ++ ) { - at_coord[j][k] -= at_coord[0][k]; - } - at_coord_center[k] -= at_coord[0][k]; - } - */ - /******************************************************** - * find the central (cur_at) atom's parity - * (orientation of atoms #1-3 when looking from #0) - ********************************************************/ - triple_product = triple_prod_and_min_abs_sine2(&at_coord[1], at_coord_center, bAddExplicitNeighbor, &min_sine, &bAmbiguous); - /* - * check for tetrahedral ambiguity -- leave it out for now - */ - if ( fabs(triple_product) > ZERO_FLOAT && (min_sine > MIN_SINE || fabs(min_sine) > ZERO_FLOAT && (n2DTetrahedralAmbiguity & T2D_OKAY ) ) ) { - /* Even => sorted in correct order, Odd=>transposed */ - parity = triple_product > 0.0? AB_PARITY_EVEN : AB_PARITY_ODD; - /* if ( num_explicit_H && at[cur_at].removed_H_parity % 2 ) */ - /* odd transposition of the removed implicit H */ - /* out_at[cur_at].parity = 3 - out_at[cur_at].parity; */ - - /* moved; see below */ - /* out_at[cur_at].bAmbiguousStereo |= bAmbiguous; */ - /* at[cur_at].bAmbiguousStereo |= bAmbiguous; */ - - /* for 4 attached atoms, moving the implicit H from index=3 to index=0 */ - /* can be done in odd number (3) transpositions: (23)(12)(01), which inverts the parity */ - if ( j1 == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { - parity = 3 - parity; - } - } else { -#if (ALWAYS_SET_STEREO_PARITY == 1) - parity = AT_PARITY_EVEN; /* suppose atoms are pre-sorted (testing) */ -#else - if ( num_z > 0 ) { - bAmbiguous |= AMBIGUOUS_STEREO; - } - parity = AB_PARITY_UNDF; /* no parity: 4 bonds are in one plane. */ -#endif - } -exit_function: - - if ( parity ) { - out_at[cur_at].bAmbiguousStereo |= bAmbiguous; - at[cur_at].bAmbiguousStereo |= bAmbiguous; - } - - - /* non-isotopic parity */ - if ( at[cur_at].num_H > 1 || parity <= 0 ) - ; /* no non-isotopic parity */ - else - out_at[cur_at].parity = parity; - - /* isotopic parity */ - if ( parity == -AB_PARITY_UNDF || parity == -vABParityUnknown /*AB_PARITY_UNKN*/ ) - parity = -parity; - if ( parity < 0 ) - parity = AB_PARITY_NONE; - out_at[cur_at].parity2 = parity; - - - parity = PARITY_VAL(out_at[cur_at].parity); - out_at[cur_at].stereo_atom_parity = ATOM_PARITY_WELL_DEF( parity )? AB_PARITY_CALC : parity; - parity = PARITY_VAL(out_at[cur_at].parity2); - out_at[cur_at].stereo_atom_parity2 = ATOM_PARITY_WELL_DEF( parity )? AB_PARITY_CALC : parity; - /* - out_at[cur_at].parity2 = out_at[cur_at].parity; // save for stereo + isotopic canon. - if ( out_at[cur_at].parity ) { - if ( num_explicit_H > 1 || j1 == MAX_NUM_STEREO_ATOM_NEIGH-1 && num_explicit_H ) { - // X H X - // for example, >C< or >C-D - // Y D Y - // parity exists for stereo + isotopic atoms canonicalization only - out_at[cur_at].parity = 0; - } - } - // returning 0 means this can be an adjacent to a stereogenic bond atom - */ - - return (int)out_at[cur_at].parity2; - -} -#undef ADD_EXPLICIT_HYDROGEN_NEIGH -#undef ADD_EXPLICIT_LONE_PAIR_NEIGH - -/*************************************************************/ -int set_stereo_parity( inp_ATOM* at, sp_ATOM* at_output, int num_at, int num_removed_H, - int *nMaxNumStereoAtoms, int *nMaxNumStereoBonds, INCHI_MODE nMode, - int bPointedEdgeStereo, int vABParityUnknown ) -{ - int num_3D_stereo_atoms=0; - int num_stereo_bonds=0; /* added to fix allene stereo bug reported for FClC=C=CFCl by Burt Leland - 2009-02-05 DT */ - - int i, is_stereo, num_stereo, max_stereo_atoms=0, max_stereo_bonds=0; - QUEUE *q = NULL; - AT_RANK *nAtomLevel = NULL; - S_CHAR *cSource = NULL; - AT_RANK min_sb_ring_size = 0; - - /********************************************************** - * - * Note: this parity reflects only relative positions of - * the atoms-neighbors and their ordering in the - * lists of neighbors. - * - * To obtain the actual parity, the parity of a number - * of neighbors transpositions (to obtain a sorted - * list of numbers assigned to the atoms) should be - * added. - * - **********************************************************/ - - /********************************************************************************* - - An example of parity=1 for stereogenic center, tetrahedral asymmetric atom - - - - (1) - | - | - [C] | - | - (2)------(0) - / - / - / - / - (3) - - - Notation: (n) is a tetrahedral atom neighbor; n is an index of a neighbor in - the central_at->neighbor[] array : neighbor atom number is central_at->neighbor[n]. - - (0)-(1), (0)-(2), (0)-(3) are lines connecting atom [C] neighbors to neighbor (0) - (0), (1) and (2) are in the plane - (0)-(3) is directed from the plain to the viewer - [C] is somewhere between (0), (1), (2), (3) - Since (1)-(2)-(3) are in a clockwise order when looking from (0), parity is 2, or even; - otherwise parity would be 1, or odd. - - ********************************************************************************** - - Examples of a stereogenic bond. - - Notation: [atom number], (index of a neighbor): - [1] and [2] are atoms connected by the stereogenic bond - numbers in () are indexes of neighbors of [1] or [2]. - (12 x 16)z = z-component of [1]-[2] and [1]-[6] cross-product - - atom [1] atom [2] - [8] [4] prod01 = (12 x 16)z < 0 prod01 = (21 x 24)z < 0 - \ / prod02 = (12 x 18)z > 0 prod02 = (21 x 25)z > 0 - (2) (1) 0 transpositions because 0 transpositions because - \ / double bond is in 0 posit. double bond is in 0 position - [1]==(0)(0)==[2] 0 = (prod01 > prod02) 0 = (prod01 > prod02) - / \ - (1) (2) result: parity = 2, even result: parity=2, even - / \ - [6] [5] - - - - atom [1] atom [2] - [8] [5] prod01 = (12 x 18)z > 0 prod01 = (21 x 24)z > 0 - \ / prod02 = (12 x 16)z < 0 prod02 = (21 x 25)z < 0 - (0) (2) 2 transpositions to move 1 transposition to move - \ / at [2] from 2 to 0 pos. at [1] from 1 to 0 position - [1]==(2)(1)==[2] 1 = (prod01 > prod02) 1 = (prod01 > prod02) - / \ - (1) (0) result: parity = (1+2) result: parity=(1+1) - / \ 2-(1+2)%2 = 1, odd 2-(1+1)%2 = 2, even - [6] [4] - - - *********************************************************************************** - Note: atoms' numbers [1], [2], [4],... are not used to calculate parity at this - point. They will be used for each numbering in the canonicalization. - Note: parity=3 for a stereo atom means entered undefined bond direction - parity=4 for an atom means parity cannot be determined from the given geometry - ***********************************************************************************/ - - if ( !at_output || !at ) { - return -1; - } - - /* clear stereo descriptors */ - - for( i = 0; i < num_at; i ++ ) { - at_output[i].parity = 0; - at_output[i].parity2 = 0; - memset(&at_output[i].stereo_bond_neighbor[0], 0, sizeof(at_output[0].stereo_bond_neighbor) ); - memset(&at_output[i].stereo_bond_neighbor2[0], 0, sizeof(at_output[0].stereo_bond_neighbor2) ); - memset(&at_output[i].stereo_bond_ord[0], 0, sizeof(at_output[0].stereo_bond_ord) ); - memset(&at_output[i].stereo_bond_ord2[0], 0, sizeof(at_output[0].stereo_bond_ord2) ); - memset(&at_output[i].stereo_bond_z_prod[0], 0, sizeof(at_output[0].stereo_bond_z_prod) ); - memset(&at_output[i].stereo_bond_z_prod2[0], 0, sizeof(at_output[0].stereo_bond_z_prod2) ); - memset(&at_output[i].stereo_bond_parity[0], 0, sizeof(at_output[0].stereo_bond_parity) ); - memset(&at_output[i].stereo_bond_parity2[0], 0, sizeof(at_output[0].stereo_bond_parity2) ); - } - /* estimate max numbers of stereo atoms and bonds if isotopic H are added */ - if ( nMaxNumStereoAtoms || nMaxNumStereoBonds ) { - for( i = 0, num_stereo = 0; i < num_at; i ++ ) { - int num; - num = can_be_a_stereo_atom_with_isotopic_H( at, i, bPointedEdgeStereo ); - if ( num ) { - max_stereo_atoms += num; - } else - if ( (num = can_be_a_stereo_bond_with_isotopic_H( at, i, nMode ) ) ) { /* accept cumulenes */ - max_stereo_bonds += num; - } - } - if ( nMaxNumStereoAtoms ) - *nMaxNumStereoAtoms = max_stereo_atoms; - if ( nMaxNumStereoBonds ) - *nMaxNumStereoBonds = max_stereo_bonds; - } - /* calculate stereo descriptors */ -#if ( MIN_SB_RING_SIZE > 0 ) - min_sb_ring_size = (AT_RANK)(((nMode & REQ_MODE_MIN_SB_RING_MASK) >> REQ_MODE_MIN_SB_RING_SHFT) & AT_RANK_MASK); - if ( min_sb_ring_size >= 3 ) { - /* create BFS data structure for finding for each stereo bond its min. ring sizes */ - q = QueueCreate( num_at+1, sizeof(qInt) ); - nAtomLevel = (AT_RANK*)inchi_calloc(sizeof(nAtomLevel[0]),num_at); - cSource = (S_CHAR *)inchi_calloc(sizeof(cSource[0]),num_at); - if ( !q || !cSource || !nAtomLevel ) { - num_3D_stereo_atoms = CT_OUT_OF_RAM; - goto exit_function; - } - } else { - min_sb_ring_size = 2; - } -#endif - /* main cycle: set stereo parities */ - for( i = 0, num_stereo = 0; i < num_at; i ++ ) - { - is_stereo = set_stereo_atom_parity( at_output, at, i, at+num_at, num_removed_H, - bPointedEdgeStereo, vABParityUnknown ) ; - if ( is_stereo ) - { - num_3D_stereo_atoms += ATOM_PARITY_WELL_DEF( is_stereo ); - } - else - { - is_stereo = set_stereo_bonds_parity( at_output, at, i, at+num_at, num_removed_H, nMode, - q, nAtomLevel, cSource, min_sb_ring_size, - bPointedEdgeStereo, vABParityUnknown ); - if ( RETURNED_ERROR( is_stereo ) ) { - num_3D_stereo_atoms = is_stereo; - break; - } - num_stereo_bonds += (is_stereo != 0); /* added to fix bug reported by Burt Leland - 2009-02-05 DT */ - } - num_stereo += (is_stereo != 0); - is_stereo = is_stereo; - } - - /* added to fix bug reported by Burt Leland - 2009-02-05 DT */ - if ( max_stereo_atoms < num_3D_stereo_atoms && nMaxNumStereoAtoms ) - *nMaxNumStereoAtoms = num_3D_stereo_atoms; - if ( max_stereo_bonds < num_stereo_bonds && nMaxNumStereoBonds ) - *nMaxNumStereoBonds = num_stereo_bonds; - - /* - if ( (nMode & REQ_MODE_SC_IGN_ALL_UU ) - REQ_MODE_SC_IGN_ALL_UU - REQ_MODE_SB_IGN_ALL_UU - */ - -#if ( MIN_SB_RING_SIZE > 0 ) - if ( q ) { - q = QueueDelete( q ); - } - if ( nAtomLevel ) - inchi_free( nAtomLevel ); - if ( cSource ) - inchi_free( cSource ); -exit_function: -#endif - - - return num_3D_stereo_atoms; -} -/***************************************************************** - * Functions that disconnect bonds - * - *=== During Preprocessing === - * - * RemoveInpAtBond - * DisconnectMetalSalt (is not aware of bond parities) - * DisconnectAmmoniumSalt - * - *=== Before Normalization === - * - * remove_terminal_HDT - * - *=== During the Normalization === - * - * AddOrRemoveExplOrImplH - * - *****************************************************************/ -int ReconcileAllCmlBondParities( inp_ATOM *at, int num_atoms, int bDisconnected ) -{ - int i, ret = 0; - S_CHAR *visited = (S_CHAR*) inchi_calloc( num_atoms, sizeof(*visited) ); - if ( !visited ) - return -1; /* out of RAM */ - for ( i = 0; i < num_atoms; i ++ ) { - if ( at[i].sb_parity[0] && !visited[i] && !(bDisconnected && is_el_a_metal(at[i].el_number)) ) { - if ( ret = ReconcileCmlIncidentBondParities( at, i, -1, visited, bDisconnected ) ) { - break; /* error */ - } - } - } - inchi_free ( visited ); - return ret; -} -/*****************************************************************/ -int ReconcileCmlIncidentBondParities( inp_ATOM *at, int cur_atom, int prev_atom, S_CHAR *visited, int bDisconnected ) -{ - /* visited = 0 or parity => atom has not been visited - 10 + parity => currently is on the stack + its final parity - 20 + parity => has been visited; is not on the stack anymore + its final parity */ - int i, j, nxt_atom, ret = 0, len; - int icur2nxt, icur2neigh; /* cur atom neighbors */ - int inxt2cur, inxt2neigh; /* next atom neighbors */ - int cur_parity, nxt_parity; - int cur_order_parity, nxt_order_parity, cur_sb_parity, nxt_sb_parity, bCurMask, bNxtMask; - /* !(bDisconnected && is_el_a_metal(at[i].el_number) */ - - if ( at[cur_atom].valence > MAX_NUM_STEREO_BONDS ) - return 0; /* ignore */ - - if ( !at[cur_atom].sb_parity[0] ) - return 1; /* wrong call */ - - if ( visited[cur_atom] >= 10 ) - return 2; /* program error */ - - cur_parity = visited[cur_atom] % 10; - - visited[cur_atom] += 10; - - for ( i = 0; i < MAX_NUM_STEREO_BONDS && at[cur_atom].sb_parity[i]; i ++ ) { - icur2nxt = (int)at[cur_atom].sb_ord[i]; - len = get_opposite_sb_atom( at, cur_atom, icur2nxt, &nxt_atom, &inxt2cur, &j ); - if ( !len ) { - return 4; /* could not find the opposite atom: bond parity data error */ - } - if ( nxt_atom == prev_atom ) - continue; - if ( visited[nxt_atom] >= 20 ) - continue; /* back edge, second visit: ignore */ - if ( at[nxt_atom].valence > MAX_NUM_STEREO_BONDS ) - continue; /* may be treated only after metal disconnection */ - - if ( bDisconnected && (at[cur_atom].sb_parity[i] & SB_PARITY_FLAG) ) { - cur_sb_parity = (at[cur_atom].sb_parity[i] >> SB_PARITY_SHFT); - bCurMask = 3 << SB_PARITY_SHFT; - } else { - cur_sb_parity = (at[cur_atom].sb_parity[i] & SB_PARITY_MASK); - bCurMask = 3; - } - if ( bDisconnected && (at[nxt_atom].sb_parity[j] & SB_PARITY_FLAG) ) { - nxt_sb_parity = (at[nxt_atom].sb_parity[j] >> SB_PARITY_SHFT); - bNxtMask = 3 << SB_PARITY_SHFT; - } else { - nxt_sb_parity = (at[nxt_atom].sb_parity[j] & SB_PARITY_MASK); - bNxtMask = 3; - } - - - - if ( !ATOM_PARITY_WELL_DEF(cur_sb_parity) || - !ATOM_PARITY_WELL_DEF(nxt_sb_parity) ) { - if ( cur_sb_parity == nxt_sb_parity ) { - continue; - /*goto move_forward;*/ /* bypass unknown/undefined */ - } - return 3; /* sb parities do not match: bond parity data error */ - } - - icur2neigh = (int)at[cur_atom].sn_ord[i]; - inxt2neigh = (int)at[nxt_atom].sn_ord[j]; - /* parity of at[cur_atom].neighbor[] premutation to reach this order: { next_atom, neigh_atom, ...} */ - - /* 1. move next_atom from position=icur2nxt to position=0 => - * icur2nxt permutations - * 2. move neigh_atom from position=inxt2neigh+(inxt2cur > inxt2neigh) to position=1 => - * inxt2neigh+(inxt2cur > inxt2neigh)-1 permutations. - * Note if (inxt2cur > inxt2neigh) then move #1 increments neigh_atom position - * Note add 4 because icur2neigh may be negative due to isotopic H removal - */ - cur_order_parity = (4+icur2nxt + icur2neigh + (icur2neigh > icur2nxt)) % 2; - /* same for next atom: */ - /* parity of at[nxt_atom].neighbor[] premutation to reach this order: { cur_atom, neigh_atom, ...} */ - nxt_order_parity = (4+inxt2cur + inxt2neigh + (inxt2neigh > inxt2cur)) % 2; - - nxt_parity = visited[nxt_atom] % 10; - - if ( !cur_parity ) { - cur_parity = 2 - (cur_order_parity + cur_sb_parity) % 2; - visited[cur_atom] += cur_parity; - } else - if ( cur_parity != 2 - (cur_order_parity + cur_sb_parity) % 2 ) { - - /***** reconcile bond parities *****/ - - /* Each bond parity is split into two values located at the end atoms. - For T (trans) the values are (1,1) or (2,2) - For C (cis) the values are (1,2) or (2,1) - The fact that one pair = another with inverted parities, namely - Inv(1,1) = (2,2) and Inv(1,2) = (2,1), allows to - simultaneouly invert parities of the current bond end atoms - (at[cur_atom].sb_parity[i], at[nxt_atom].sb_parity[j]) - so that the final current atom parity cur_parity - calculated later in stereochemical canonicalization for - each stereobond incident with the current atomis same. - Achieving this is called here RECONCILIATION. - If at the closure of an aromatic circuit the parities of - next atom cannot be reconciled with already calculated then - this function returns 5 (error). - */ - - at[cur_atom].sb_parity[i] ^= bCurMask; - at[nxt_atom].sb_parity[j] ^= bNxtMask; - cur_sb_parity ^= 3; - nxt_sb_parity ^= 3; - } - - if ( !nxt_parity ) { - nxt_parity = 2 - (nxt_order_parity + nxt_sb_parity) % 2; - visited[nxt_atom] += nxt_parity; - } else - if ( nxt_parity != 2 - (nxt_order_parity + nxt_sb_parity) % 2 ) { - return 5; /* algorithm does not work for Mebius-like structures */ - } - -/* move_forward: */ - if ( visited[nxt_atom] < 10 ) { - ret = ReconcileCmlIncidentBondParities( at, nxt_atom, cur_atom, visited, bDisconnected ); - if ( ret ) { - break; - } - } - } - visited[cur_atom] += 10; /* all bonds incident to the current atom have - been processed or an error occurred. */ - return ret; -} -/*****************************************************************/ -int get_opposite_sb_atom( inp_ATOM *at, int cur_atom, int icur2nxt, int *pnxt_atom, int *pinxt2cur, int *pinxt_sb_parity_ord ) -{ - AT_NUMB nxt_atom; - int j, len; - - len = 0; - while ( len ++ < 20 ) { /* arbitrarily set cumulene length limit to avoid infinite loop */ - nxt_atom = at[cur_atom].neighbor[icur2nxt]; - for ( j = 0; j < MAX_NUM_STEREO_BONDS && at[nxt_atom].sb_parity[j]; j ++ ) { - if ( cur_atom == at[nxt_atom].neighbor[(int)at[nxt_atom].sb_ord[j]] ) { - /* found the opposite atom */ - *pnxt_atom = nxt_atom; - *pinxt2cur = at[nxt_atom].sb_ord[j]; - *pinxt_sb_parity_ord = j; - return len; - } - } - if ( j ) { - return 0; /* reached atom(s) with stereobond (sb) parity, the opposite atom has not been found */ - } - if ( at[nxt_atom].valence == 2 && 2*BOND_TYPE_DOUBLE == at[nxt_atom].chem_bonds_valence ) { - /* follow cumulene =X= path */ - icur2nxt = (at[nxt_atom].neighbor[0] == cur_atom); - cur_atom = nxt_atom; - } else { - return 0; /* neither atom with a sb parity not middle cumulene could be reached */ - } - } - return 0; /* too long chain of cumulene was found */ -} +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include + +#include "mode.h" +#include "ichister.h" +#include "ichiring.h" +#include "ichi.h" +#include "ichitaut.h" +#include "ichicomp.h" +#include "ichicant.h" +#include "util.h" + +#define ZTYPE_DOWN (-1) /* should be equal to -ZTYPE_UP */ +#define ZTYPE_NONE 0 +#define ZTYPE_UP 1 /* should be equal to -ZTYPE_DOWN */ +#define ZTYPE_3D 3 +#define ZTYPE_EITHER 9999 + +/* criteria for ill-defined */ +#define MIN_ANGLE 0.10 /* 5.73 degrees */ +#define MIN_SINE 0.03 /* min edge/plane angle in case the tetrahedra has significantly different edge length */ +#define MIN_ANGLE_DBOND 0.087156 /* 5 degrees = max angle considered as too small for unambiguous double bond stereo */ +#define MIN_SINE_OUTSIDE 0.06 /* min edge/plane angle to determine whether the central atom is outside of the tetrahedra */ +#define MIN_SINE_SQUARE 0.125 /* min edge/plane angle in case the tetrahedra is somewhat close to a parallelogram */ +#define MIN_SINE_EDGE 0.167 /* min sine/(min.edge) ratio to avoid undefined in case of long edges */ +#define MIN_LEN_STRAIGHT 1.900 /* min length of two normalized to 1 bonds in a straight line */ +#define MAX_SINE 0.70710678118654752440084436210485 /* 1/sqrt(2)=sin(pi/4) */ +#define MIN_BOND_LEN 0.000001 +#define ZERO_LENGTH MIN_BOND_LEN +#define ZERO_FLOAT 1.0e-12 +#define BOND_PARITY_UNDEFINED 64 +#if ( STEREO_CENTER_BONDS_NORM == 1 ) +#define MPY_SINE 1.00 /* was 3.0 */ +#define MAX_EDGE_RATIO 2.50 /* max max/min edge ratio for a tetrahedra close to a parallelogram */ +#else +#define MPY_SINE 3.00 +#define MAX_EDGE_RATIO 6.00 /* max max/min edge ratio for a tetrahedra close to a parallelogram */ +#endif +/* local prototypes */ +static int save_a_stereo_bond( int z_prod, int result_action, + int at1, int ord1, AT_NUMB *stereo_bond_neighbor1, S_CHAR *stereo_bond_ord1, S_CHAR *stereo_bond_z_prod1, S_CHAR *stereo_bond_parity1, + int at2, int ord2, AT_NUMB *stereo_bond_neighbor2, S_CHAR *stereo_bond_ord2, S_CHAR *stereo_bond_z_prod2, S_CHAR *stereo_bond_parity2 ); +static double get_z_coord( inp_ATOM* at, int cur_atom, int neigh_no, int *nType,int bPointedEdgeStereo ); +static double len3( const double c[] ); +static double len2( const double c[] ); +static double* diff3( const double a[], const double b[], double result[] ); +static double* add3( const double a[], const double b[], double result[] ); +static double* mult3( const double a[], double b, double result[] ); +static double* copy3( const double a[], double result[] ); +static double* change_sign3( const double a[], double result[] ); +static double dot_prod3( const double a[], const double b[] ); +static int dot_prodchar3( const S_CHAR a[], const S_CHAR b[] ); +static double* cross_prod3( const double a[], const double b[], double result[] ); +static double triple_prod( double a[], double b[], double c[], double *sine_value ); +static double triple_prod_and_min_abs_sine(double at_coord[][3], double *min_sine); +static int are_3_vect_in_one_plane( double at_coord[][3], double min_sine); +static int triple_prod_char( inp_ATOM *at, int at_1, int i_next_at_1, S_CHAR *z_dir1, + int at_2, int i_next_at_2, S_CHAR *z_dir2 ); + +static int CompDble( const void *a1, const void *a2, void * ); +static int Get2DTetrahedralAmbiguity( CANON_GLOBALS *pCG, double at_coord[][3], int bAddExplicitNeighbor, int bFix2DstereoBorderCase ); +static double triple_prod_and_min_abs_sine2(double at_coord[][3], double central_at_coord[], int bAddedExplicitNeighbor, double *min_sine, int *bAmbiguous); +static int are_4at_in_one_plane( double at_coord[][3], double min_sine); +static int bInpAtomHasRequirdNeigh ( inp_ATOM *at, int cur_at, int RequirdNeighType, int NumDbleBonds ); +static int bIsSuitableHeteroInpAtom( inp_ATOM *at ); +static int bIsOxide( inp_ATOM *at, int cur_at ); +static int half_stereo_bond_parity( inp_ATOM *at, int cur_at, inp_ATOM *at_removed_H, int num_removed_H, S_CHAR *z_dir, + int bPointedEdgeStereo, int vABParityUnknown ); +static int get_allowed_stereo_bond_type( int bond_type ); +static int can_be_a_stereo_bond_with_isotopic_H( inp_ATOM *at, int cur_at, INCHI_MODE nMode ); +static int half_stereo_bond_action( int nParity, int bUnknown, int bIsotopic, int vABParityUnknown ); +static int set_stereo_bonds_parity( sp_ATOM *out_at, inp_ATOM *at, int at_1, inp_ATOM *at_removed_H, int num_removed_H, + INCHI_MODE nMode, QUEUE *q, AT_RANK *nAtomLevel, + S_CHAR *cSource, AT_RANK min_sb_ring_size, + int bPointedEdgeStereo, int vABParityUnknown ); +static int can_be_a_stereo_atom_with_isotopic_H( inp_ATOM *at, int cur_at, int bPointedEdgeStereo ); +static int set_stereo_atom_parity( CANON_GLOBALS *pCG, sp_ATOM *out_at, inp_ATOM *at, int cur_at, inp_ATOM *at_removed_H, int num_removed_H, + int bPointedEdgeStereo, int vABParityUnknown ); +/* +int set_stereo_parity( CANON_GLOBALS *pCG, inp_ATOM* at, sp_ATOM* at_output, int num_at, int num_removed_H, + int *nMaxNumStereoAtoms, int *nMaxNumStereoBonds, INCHI_MODE nMode, int bPointedEdgeStereo, vABParityUnknown ); +int get_opposite_sb_atom( inp_ATOM *at, int cur_atom, int icur2nxt, int *pnxt_atom, int *pinxt2cur, int *pinxt_sb_parity_ord ); +*/ +int ReconcileCmlIncidentBondParities( inp_ATOM *at, int cur_atom, int prev_atom, S_CHAR *visited, int bDisconnected ); +int comp_AT_NUMB( const void* a1, const void* a2, void *); +int GetHalfStereobond0DParity( inp_ATOM *at, int cur_at, AT_NUMB nSbNeighOrigAtNumb[], int nNumExplictAttachments, int bond_parity, int nFlag ); +int GetStereocenter0DParity( CANON_GLOBALS *pCG, inp_ATOM *at, int cur_at, int j1, AT_NUMB nSbNeighOrigAtNumb[], int nFlag ); +int GetSbNeighOrigAtNumb( inp_ATOM *at, int cur_at, inp_ATOM *at_removed_H, int num_removed_H, AT_NUMB nSbNeighOrigAtNumb[]); +int FixSb0DParities( inp_ATOM *at, /* inp_ATOM *at_removed_H, int num_removed_H,*/ int chain_length, + int at_1, int i_next_at_1, S_CHAR z_dir1[], + int at_2, int i_next_at_2, S_CHAR z_dir2[], + int *pparity1, int *pparity2 ); + +/****************************************************************************/ + + +static double *pDoubleForSort; + +/**********************************************************************************/ +int comp_AT_NUMB( const void* a1, const void* a2, void *p) +{ + return (int)*(const AT_NUMB*)a1 - (int)*(const AT_NUMB*)a2; +} + + +/****************************************************************************/ +double get_z_coord( inp_ATOM* at, int cur_atom, int neigh_no, int *nType, int bPointedEdgeStereo ) +{ + int stereo_value = at[cur_atom].bond_stereo[neigh_no]; + int stereo_type = abs( stereo_value ); + int neigh = (int)at[cur_atom].neighbor[neigh_no]; + double z = at[neigh].z - at[cur_atom].z; + int bFlat; + + if ( bFlat = (fabs(z) < ZERO_LENGTH) ) { + int i; + for ( i = 0; i < at[cur_atom].valence; i ++ ) { + if ( fabs(at[cur_atom].z - at[(int)at[cur_atom].neighbor[i]].z) > ZERO_LENGTH ) { + bFlat = 0; + break; + } + } + } + + if ( bFlat ) { + if ( !bPointedEdgeStereo || bPointedEdgeStereo * stereo_value >= 0 ) { + /* bPointedEdgeStereo > 0: define stereo from pointed end of the stereo bond only */ + /* bPointedEdgeStereo < 0: define stereo from wide end of the stereo bond only (case of removed H) */ + switch( stereo_type ) { + /* 1=Up (solid triangle), 6=Down (Dashed triangle), 4=Either (zigzag triangle) */ + case 0: /* No stereo */ + *nType = ZTYPE_NONE; + break; + case STEREO_SNGL_UP: /* 1= Up */ + *nType = ZTYPE_UP; + break; + case STEREO_SNGL_EITHER: /* 4 = Either */ + *nType = ZTYPE_EITHER; + break; + case STEREO_SNGL_DOWN: /* 6 = Down */ + *nType = ZTYPE_DOWN; + break; + default: + *nType = ZTYPE_NONE; /* ignore unexpected values */ + } + if ( stereo_value < 0 && (*nType == ZTYPE_DOWN || *nType == ZTYPE_UP) ) + *nType = -*nType; + } else { + *nType = ZTYPE_NONE; /* no stereo */ + } + } else + if ( stereo_type == STEREO_SNGL_EITHER && + ( !bPointedEdgeStereo || bPointedEdgeStereo * stereo_value >= 0 ) ) { + *nType = ZTYPE_EITHER; + } else { + *nType = ZTYPE_3D; + } + return z; +} + + +/****************************************************************************/ +double len3( const double c[] ) +{ + return sqrt( c[0]*c[0] + c[1]*c[1] + c[2]*c[2] ); +} + + +/****************************************************************************/ +double len2( const double c[] ) +{ + return sqrt( c[0]*c[0] + c[1]*c[1] ); +} + + +/****************************************************************************/ +double* diff3( const double a[], const double b[], double result[] ) +{ + + result[0] = a[0] - b[0]; + result[1] = a[1] - b[1]; + result[2] = a[2] - b[2]; + + return result; +} + + +/****************************************************************************/ +double* add3( const double a[], const double b[], double result[] ) +{ + result[0] = a[0] + b[0]; + result[1] = a[1] + b[1]; + result[2] = a[2] + b[2]; + + return result; +} + + +/****************************************************************************/ +double* mult3( const double a[], double b, double result[] ) +{ + result[0] = a[0] * b; + result[1] = a[1] * b; + result[2] = a[2] * b; + + return result; +} + + +/*************************************************************/ +double* copy3( const double a[], double result[] ) +{ + result[0] = a[0]; + result[1] = a[1]; + result[2] = a[2]; + + return result; +} + + +/*************************************************************/ +double* change_sign3( const double a[], double result[] ) +{ + result[0] = -a[0]; + result[1] = -a[1]; + result[2] = -a[2]; + + return result; +} + + +/*************************************************************/ +double dot_prod3( const double a[], const double b[] ) +{ + return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; +} + + +/*************************************************************/ +int dot_prodchar3( const S_CHAR a[], const S_CHAR b[] ) +{ + int prod = ((int)a[0]*(int)b[0] + (int)a[1]*(int)b[1] + (int)a[2]*(int)b[2])/100; + if ( prod > 100 ) + prod = 100; + else + if ( prod < -100 ) + prod = -100; + return prod; +} + + +/*************************************************************/ +double* cross_prod3( const double a[], const double b[], double result[] ) +{ + double tmp[3]; + + tmp[0] = (a[1]*b[2]-a[2]*b[1]); + tmp[1] = -(a[0]*b[2]-a[2]*b[0]); + tmp[2] = (a[0]*b[1]-a[1]*b[0]); + + result[0] = tmp[0]; + result[1] = tmp[1]; + result[2] = tmp[2]; + + return result; +} + + +/*************************************************************/ +double triple_prod( double a[], double b[], double c[], double *sine_value ) +{ + double ab[3], dot_prod_ab_c, abs_c, abs_ab; + cross_prod3( a, b, ab ); + /* ab[0] = (a[1]*b[2]-a[2]*b[1]); */ + /* ab[1] = -(a[0]*b[2]-a[2]*b[0]); */ + /* ab[2] = (a[0]*b[1]-a[1]*b[0]); */ + dot_prod_ab_c = dot_prod3( ab, c ); + /* dot_prod_ab_c = ab[0]*c[0] + ab[1]*c[1] + ab[2]*c[2]; */ + if ( sine_value ) { + abs_c = len3( c ); + /* abs_c = sqrt( c[0]*c[0] + c[1]*c[1] + c[2]*c[2] ); */ + abs_ab = len3( ab ); + /* abs_ab = sqrt( ab[0]*ab[0] + ab[1]*ab[1] + ab[2]*ab[2] ); */ + + if ( abs_c > 1.e-7 /* otherwise c has zero length */ && abs_ab > 1.e-7 /* otherwise a is parallel to b*/ ) { + *sine_value = MPY_SINE * dot_prod_ab_c / ( abs_c * abs_ab); + /* *sine_value = dot_prod_ab_c / ( abs_c * abs_ab); */ + } else { + *sine_value = 0.0; + } + } + return dot_prod_ab_c; +} + + +/*************************************************************/ +int CompDble( const void *a1, const void *a2, void *p ) +{ + double *pDoubleForSort=(double *) p; + double diff = pDoubleForSort[*(const int*)a1] - pDoubleForSort[*(const int*)a2]; + if ( diff > 0.0 ) + return 1; + if ( diff < 0.0 ) + return -1; + return 0; +} + + +/*************************************************************/ +#define T2D_OKAY 1 +#define T2D_WARN 2 +#define T2D_UNDF 4 + +int Get2DTetrahedralAmbiguity( CANON_GLOBALS *pCG, + double at_coord[][3], + int bAddExplicitNeighbor, + int bFix2DstereoBorderCase ) +{ +/* const double one_pi = 2.0*atan2(1.0 , 0.0 ); */ +const double one_pi = 3.14159265358979323846; /* M_PI */ +const double two_pi = 2.0*one_pi; +const double dAngleAndPiMaxDiff = 2.0*atan2(1.0, sqrt(7.0)); /* min sine between 2 InPlane bonds */ +double *pDoubleForSort; +int nBondType[MAX_NUM_STEREO_ATOM_NEIGH], nBondOrder[MAX_NUM_STEREO_ATOM_NEIGH]; +double dBondDirection[MAX_NUM_STEREO_ATOM_NEIGH]; +volatile double dAngle, dAlpha, dLimit, dBisector; + /* 2010-02-10 added 'volatile': workaround ensuring proper behavior for gcc 32-bit */ + /* cml-enabled compiles at >=O1 for SID484922 and alike (both lin&win had problems) */ +int nNumNeigh = MAX_NUM_STEREO_ATOM_NEIGH - (bAddExplicitNeighbor != 0); +int i, num_Up, num_Dn, bPrev_Up, cur_len_Up, cur_first_Up, len_Up, first_Up; +int ret=0; + + for ( i = 0, num_Up = num_Dn = 0; i < nNumNeigh; i ++ ) + { + dAngle = atan2( at_coord[i][1], at_coord[i][0] ); /* range from -pi to +pi */ + if ( dAngle < 0.0 ) { + dAngle += two_pi; + } + dBondDirection[i] = dAngle; + nBondType[i] = (at_coord[i][2] > 0.0)? 1 : (at_coord[i][2] < 0.0)? -1 : 0; /* z-coord sign */ + if ( nBondType[i] > 0 ) { + num_Up ++; + } else + if ( nBondType[i] < 0 ) { + num_Dn ++; + } + nBondOrder[i] = i; + } + if ( num_Up < num_Dn ) { + for ( i = 0; i < nNumNeigh; i ++ ) { + nBondType[i] = -nBondType[i]; + } + inchi_swap( (char*)&num_Dn, (char*)&num_Up, sizeof(num_Dn) ); + } + if ( !num_Up ) { + return T2D_UNDF; + } + + /* sort according to the bond orientations */ + pDoubleForSort = dBondDirection; + insertions_sort( pDoubleForSort, nBondOrder, nNumNeigh, + sizeof(nBondOrder[0]), CompDble ); + + /* find the longest contiguous sequence of Up bonds */ + if ( num_Up == nNumNeigh ) { + /* all bonds are Up */ + len_Up = cur_len_Up = nNumNeigh; /* added cur_len_Up initialization 1/8/2002 */ + first_Up = 0; + } else { + /* at least one bond is not Up */ + cur_len_Up = len_Up = bPrev_Up = 0; + /* prev. cycle header version --- + for ( i = 0; 1; i ++ ) { + if ( i >= nNumNeigh && !bPrev_Up ) { + break; + } + ----------} */ + /* look at all bonds and continue (circle therough the beginning) as long as the current bond is Up */ + for ( i = 0; i < nNumNeigh || bPrev_Up; i ++ ) { + if ( nBondType[nBondOrder[i % nNumNeigh]] > 0 ) { + if ( bPrev_Up ) { + cur_len_Up ++; /* uncrement number of Up bonds in current contiguous sequence of them */ + } else { + bPrev_Up = 1; /* start new contiguous sequence of Up bonds */ + cur_len_Up = 1; + cur_first_Up = i % nNumNeigh; + } + } else + if ( bPrev_Up ) { /* end of contiguous sequence of Up bonds */ + if ( cur_len_Up > len_Up ) { + first_Up = cur_first_Up; /* store the sequence because it is longer than the ptrvious one */ + len_Up = cur_len_Up; + } + bPrev_Up = 0; + } + } + } +#if ( FIX_2D_STEREO_BORDER_CASE == 1 ) + /* check if the bonds with ordering numbers first_Up+len_Up and first_Up+len_Up+1 */ + /* have identical angles. In this case switch their order to enlarge the Up sequence */ +#define ZERO_ANGLE 0.000001 + if ( nNumNeigh - len_Up >= 2 ) { + int next1, next2; + for ( i = 1; i < nNumNeigh - len_Up; i ++ ) { + next2 = (first_Up+len_Up + i) % nNumNeigh; /* the 2nd after Up sequence */ + if ( nBondType[nBondOrder[next2]] > 0 ) { + next1 = (first_Up+len_Up) % nNumNeigh; /* the 1st after Up sequence */ + dAngle = dBondDirection[nBondOrder[next1]] - dBondDirection[nBondOrder[next2]]; + if ( fabs(dAngle) < ZERO_ANGLE ) { + inchi_swap( (char*)&nBondOrder[next1], (char*)&nBondOrder[next2], sizeof(nBondOrder[0]) ); + len_Up ++; + break; + } + } + } + } + /* check whether the not-Up bond (located before the found first-Up) has */ + /* same angle as the Up bond that precedes this not-Up bond */ + if ( nNumNeigh - len_Up >= 2 ) { + int next1, next2; + for ( i = 1; i < nNumNeigh - len_Up; i ++ ) { + next2 = (first_Up+nNumNeigh - i - 1 ) % nNumNeigh; /* the 2nd before Up sequence */ + if ( nBondType[nBondOrder[next2]] > 0 ) { + next1 = (first_Up+nNumNeigh-1) % nNumNeigh; /* the 1st before Up sequence */ + dAngle = dBondDirection[nBondOrder[next1]] - dBondDirection[nBondOrder[next2]]; + if ( fabs(dAngle) < ZERO_ANGLE ) { + inchi_swap( (char*)&nBondOrder[next1], (char*)&nBondOrder[next2], sizeof(nBondOrder[0]) ); + first_Up = next1; + len_Up ++; + break; + } + } + } + } +#else + if ( bFix2DstereoBorderCase ) { + /* check if the bonds with ordering numbers first_Up+len_Up and first_Up+len_Up+1 */ + /* have identical angles. In this case switch their order to enlarge the Up sequence */ +#define ZERO_ANGLE 0.000001 + if ( nNumNeigh - len_Up >= 2 ) { + int next1, next2; + for ( i = 1; i < nNumNeigh - len_Up; i ++ ) { + next2 = (first_Up+len_Up + i) % nNumNeigh; /* the 2nd after Up sequence */ + if ( nBondType[nBondOrder[next2]] > 0 ) { + next1 = (first_Up+len_Up) % nNumNeigh; /* the 1st after Up sequence */ + dAngle = dBondDirection[nBondOrder[next1]] - dBondDirection[nBondOrder[next2]]; + if ( fabs(dAngle) < ZERO_ANGLE ) { + inchi_swap( (char*)&nBondOrder[next1], (char*)&nBondOrder[next2], sizeof(nBondOrder[0]) ); + len_Up ++; + break; + } + } + } + } + /* check whether the not-Up bond (located before the found first-Up) has */ + /* same angle as the Up bond that precedes this not-Up bond */ + if ( nNumNeigh - len_Up >= 2 ) { + int next1, next2; + for ( i = 1; i < nNumNeigh - len_Up; i ++ ) { + next2 = (first_Up+nNumNeigh - i - 1 ) % nNumNeigh; /* the 2nd before Up sequence */ + if ( nBondType[nBondOrder[next2]] > 0 ) { + next1 = (first_Up+nNumNeigh-1) % nNumNeigh; /* the 1st before Up sequence */ + dAngle = dBondDirection[nBondOrder[next1]] - dBondDirection[nBondOrder[next2]]; + if ( fabs(dAngle) < ZERO_ANGLE ) { + inchi_swap( (char*)&nBondOrder[next1], (char*)&nBondOrder[next2], sizeof(nBondOrder[0]) ); + first_Up = next1; + len_Up ++; + break; + } + } + } + } + } +#endif + /* Turn all the bonds around the center so that */ + /* the 1st Up bond has zero radian direction */ + dAlpha = dBondDirection[nBondOrder[first_Up]]; + for ( i = 0; i < nNumNeigh; i ++ ) { + if ( i == nBondOrder[first_Up] ) { + dBondDirection[i] = 0.0; + } else { + dAngle = dBondDirection[i] - dAlpha; + if ( dAngle < 0.0 ) { + dAngle += two_pi; + } + dBondDirection[i] = dAngle; + } + } + + /******************************************************** + * Process particular cases + ********************************************************/ + + + if ( nNumNeigh == 3 ) /************************ 3 bonds ************************/ + { + + switch( num_Up ) + { + + + + case 0: /* 0 Up */ + return T2D_UNDF; + + + + + case 1: /* 1 Up */ + if ( num_Dn ) + { +#ifdef _DEBUG + if ( num_Dn != 1 ) /* debug only */ + return -1; +#endif + ret = (T2D_UNDF | T2D_WARN); + } + else + { + dAngle = dBondDirection[nBondOrder[(first_Up + 2) % nNumNeigh]] - + dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; + + if ( dAngle < 0.0 ) + dAngle += two_pi; + if ( dAngle - one_pi < -MIN_ANGLE || dAngle - one_pi > MIN_ANGLE ) + { + ret = T2D_OKAY; + } + else + { + ret = (T2D_UNDF | T2D_WARN); + } + } + break; + + + + + case 2: /* 2 Up */ + if ( num_Dn ) + { + dAlpha = dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]] - + dBondDirection[nBondOrder[(first_Up ) % nNumNeigh]]; + + if ( dAlpha < 0.0 ) + dAlpha += two_pi; + + if ( dAlpha > one_pi - MIN_ANGLE ) + { + ret = T2D_OKAY; + } + else if ( dAlpha < two_pi / 3.0 - MIN_ANGLE ) + { + ret = (T2D_UNDF | T2D_WARN); + } + else + { + /* angle between 2 Up bonds is between 120 and 180 degrees */ + /* direction of the (Alpha angle bisector) + 180 degrees */ + dBisector = dBondDirection[nBondOrder[(first_Up ) % nNumNeigh]]; + dBisector+= dBondDirection[nBondOrder[(first_Up + 1 ) % nNumNeigh]]; + dBisector/= 2.0; + dBisector-= one_pi; + if ( dBisector < 0.0 ) + { + dBisector += two_pi; + } + if ( dAlpha < two_pi / 3.0 + MIN_ANGLE ) + { + /* dAlpha is inside ( 2pi/3 - eps, 2pi/3 + eps ) interval */ + dLimit = MIN_ANGLE * 3.0 / 2.0; + } + else + { + dLimit = dAlpha * 3.0 / 2.0 - one_pi; + } + + dAngle = dBondDirection[nBondOrder[(first_Up + 2 ) % nNumNeigh]]; + + if ( dBisector - dAngle < -dLimit || + dBisector - dAngle > dLimit ) + { + ret = (T2D_UNDF | T2D_WARN); + } + else + { + ret = T2D_OKAY; + } + } + } /* if ( num_Dn ) */ + else + { + ret = T2D_OKAY; + } + break; + + + + case 3: /* 3 Up */ + ret = T2D_OKAY; + break; + + + default:/* other Up */ + return -1; + } /* eof switch( num_Up ) at nNumNeigh == 3 */ + } + + + else if ( nNumNeigh == 4) /******************************* 4 bonds ********************/ + { + switch( num_Up ) + { + + case 0: /* 0 Up */ + return T2D_UNDF; + + + case 1: /* 1 Up */ + if ( num_Dn ) + { + if ( nBondType[nBondOrder[(first_Up + 2) % nNumNeigh]] < 0 ) + { + /* + * Up, In Plane, Dn, In Plane. Undefined if angle between + * two In Plane bonds is wuthin pi +/- 2*arcsine(1/sqrt(8)) interval + * That is, 138.5 to 221.4 degrees; for certainty the interval is + * increased by 5.7 degrees at each end to + * 134.8 to 227.1 degrees + */ + dAngle = dBondDirection[nBondOrder[(first_Up + 3) % nNumNeigh]] - + dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; + if ( dAngle < 0.0 ) { + dAngle += two_pi; + } + if ( fabs( dAngle - one_pi ) < dAngleAndPiMaxDiff + MIN_ANGLE ) { + ret = (T2D_UNDF | T2D_WARN); + } + else + { + ret = T2D_OKAY; + } + } + else + { + ret = T2D_OKAY; + } +#ifdef _DEBUG + if ( num_Dn != 1 ) /* debug only */ + return -1; +#endif + } + else + { + ret = T2D_OKAY; + dAngle = dBondDirection[nBondOrder[(first_Up + 3) % nNumNeigh]] - + dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; + if ( dAngle < 0.0 ) + { + dAngle += two_pi; + } + if ( dAngle < one_pi - MIN_ANGLE ) + { + ret |= T2D_WARN; + } + } + break; + + + case 2: /* 2 Up */ +#if ( FIX_2D_STEREO_BORDER_CASE == 1 ) + if ( len_Up == 1 ) + { + ret = T2D_OKAY; + } + else + { + dAngle = dBondDirection[nBondOrder[(first_Up + 3) % nNumNeigh]] - + dBondDirection[nBondOrder[(first_Up + 0) % nNumNeigh]]; + dAngle = fabs(two_pi - dAngle); + dAlpha = dBondDirection[nBondOrder[(first_Up + 2) % nNumNeigh]] - + dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; + dAlpha = fabs(dAlpha); + if ( dAngle < 2.0 * ZERO_ANGLE && dAlpha > MIN_ANGLE || + dAlpha < 2.0 * ZERO_ANGLE && dAngle > MIN_ANGLE ) + { + ret = (T2D_OKAY | T2D_WARN); + } + else + { + ret = (T2D_UNDF | T2D_WARN); + } + } +#else + if ( bFix2DstereoBorderCase ) + { + /* bug fix */ + if ( len_Up == 1 ) + { + ret = T2D_OKAY; + } + else + { + dAngle = dBondDirection[nBondOrder[(first_Up + 3) % nNumNeigh]] - + dBondDirection[nBondOrder[(first_Up + 0) % nNumNeigh]]; + dAngle = fabs(two_pi - dAngle); + dAlpha = dBondDirection[nBondOrder[(first_Up + 2) % nNumNeigh]] - + dBondDirection[nBondOrder[(first_Up + 1) % nNumNeigh]]; + dAlpha = fabs(dAlpha); + if ( dAngle < 2.0 * ZERO_ANGLE && dAlpha > MIN_ANGLE || + dAlpha < 2.0 * ZERO_ANGLE && dAngle > MIN_ANGLE ) + { + ret = (T2D_OKAY | T2D_WARN); + } + else + { + ret = (T2D_UNDF | T2D_WARN); + } + } + } + else + { + /* original InChI v. 1 bug */ + if ( cur_len_Up == 1 ) + { + ret = T2D_OKAY; + } + else + { + ret = (T2D_UNDF | T2D_WARN); + } + } +#endif + break; + + + case 3: /* 3 Up */ + ret = T2D_OKAY; + dAngle = dBondDirection[nBondOrder[(first_Up + 2) % nNumNeigh]] - + dBondDirection[nBondOrder[(first_Up + 0) % nNumNeigh]]; + if ( dAngle < 0.0 ) + { + dAngle += two_pi; + } + if ( dAngle < one_pi - MIN_ANGLE ) + { + ret |= T2D_WARN; + } + break; + + case 4: /* 4 Up */ + ret = (T2D_UNDF | T2D_WARN); + break; + + default:/* other Up */ + return -1; /* program error */ + } /* eof switch( num_Up ) at nNumNeigh == 4 */ + + if ( ret == T2D_OKAY ) + { + /* check whether all bonds are inside a less than 180 degrees sector */ + for ( i = 0; i < nNumNeigh; i ++ ) + { + dAngle = dBondDirection[nBondOrder[(i + nNumNeigh - 1) % nNumNeigh]] - + dBondDirection[nBondOrder[ i % nNumNeigh]]; + if ( dAngle < 0.0 ) + { + dAngle += two_pi; + } + if ( dAngle < one_pi - MIN_ANGLE ) + { + ret |= T2D_WARN; + break; + } + } + } + } /* eof nNumNeigh == 4 */ + + else /*************************** number of bonds != 3 or 4 ******************/ + { + + return -1; /* error */ + } + + + return ret; +} + +/*************************************************************/ +double triple_prod_and_min_abs_sine2(double at_coord[][3], double central_at_coord[], int bAddedExplicitNeighbor, double *min_sine, int *bAmbiguous) +{ + double min_sine_value=9999.0, sine_value, min_edge_len, max_edge_len, min_edge_len_NoExplNeigh, max_edge_len_NoExplNeigh; + double s0, s1, s2, s3, e01, e02, e03, e12, e13, e23, tmp[3], e[3][3]; + double prod, ret, central_prod[4]; + int bLongEdges; + + if ( !min_sine ) { + return triple_prod( at_coord[0], at_coord[1], at_coord[2], NULL ); + } + + ret = triple_prod( at_coord[0], at_coord[1], at_coord[2], &sine_value ); + sine_value = MPY_SINE * fabs( sine_value ); + + diff3( at_coord[1], at_coord[0], e[2] ); + diff3( at_coord[0], at_coord[2], e[1] ); + diff3( at_coord[2], at_coord[1], e[0] ); + + /* lengths of the 6 edges of the tetrahedra */ + e03 = len3( at_coord[0] ); /* 1 */ + e13 = len3( at_coord[1] ); + e23 = len3( at_coord[2] ); /* includes added neighbor if bAddedExplicitNeighbor*/ + e02 = len3( e[1] ); /* includes added neighbor if bAddedExplicitNeighbor*/ + e12 = len3( e[0] ); /* includes added neighbor if bAddedExplicitNeighbor*/ + e01 = len3( e[2] ); + + /* min & max edge length */ + max_edge_len = + min_edge_len = e03; + + if ( min_edge_len > e13 ) + min_edge_len = e13; + if ( min_edge_len > e01 ) + min_edge_len = e01; + min_edge_len_NoExplNeigh = min_edge_len; + + if ( min_edge_len > e23 ) + min_edge_len = e23; + if ( min_edge_len > e02 ) + min_edge_len = e02; + if ( min_edge_len > e12 ) + min_edge_len = e12; + + if ( max_edge_len < e13 ) + max_edge_len = e13; + if ( max_edge_len < e01 ) + max_edge_len = e01; + max_edge_len_NoExplNeigh = max_edge_len; + + if ( max_edge_len < e23 ) + max_edge_len = e23; + if ( max_edge_len < e02 ) + max_edge_len = e02; + if ( max_edge_len < e12 ) + max_edge_len = e12; + + if ( !bAddedExplicitNeighbor ) { + min_edge_len_NoExplNeigh = min_edge_len; + max_edge_len_NoExplNeigh = max_edge_len; + } + + bLongEdges = bAddedExplicitNeighbor? + ( max_edge_len_NoExplNeigh < MAX_EDGE_RATIO * min_edge_len_NoExplNeigh ) : + ( max_edge_len < MAX_EDGE_RATIO * min_edge_len ); + + if ( sine_value > MIN_SINE && ( min_sine || bAmbiguous ) ) { + if ( min_sine ) { + prod = fabs( ret ); + /* tetrahedra height = volume(prod) / area of a plane(cross_prod) */ + /* (instead of a tetrahedra calculate parallelogram/parallelepiped area/volume) */ + + /* 4 heights from each of the 4 vertices to the opposite plane */ + s0 = prod / len3( cross_prod3( at_coord[1], at_coord[2], tmp ) ); + s1 = prod / len3( cross_prod3( at_coord[0], at_coord[2], tmp ) ); + s2 = prod / len3( cross_prod3( at_coord[0], at_coord[1], tmp ) ); + s3 = prod / len3( cross_prod3( e[0], e[1], tmp ) ); + /* abs. value of a sine of an angle between each tetrahedra edge and plane */ + /* sine = height / edge length */ + if ( (sine_value = s0/e01) < min_sine_value ) + min_sine_value = sine_value; + if ( (sine_value = s0/e02) < min_sine_value ) + min_sine_value = sine_value; + if ( (sine_value = s0/e03) < min_sine_value ) + min_sine_value = sine_value; + + if ( (sine_value = s1/e01) < min_sine_value ) + min_sine_value = sine_value; + if ( (sine_value = s1/e12) < min_sine_value ) + min_sine_value = sine_value; + if ( (sine_value = s1/e13) < min_sine_value ) + min_sine_value = sine_value; + + if ( (sine_value = s2/e02) < min_sine_value ) + min_sine_value = sine_value; + if ( (sine_value = s2/e12) < min_sine_value ) + min_sine_value = sine_value; + if ( (sine_value = s2/e23) < min_sine_value ) + min_sine_value = sine_value; + + if ( (sine_value = s3/e03) < min_sine_value ) + min_sine_value = sine_value; + if ( (sine_value = s3/e13) < min_sine_value ) + min_sine_value = sine_value; + if ( (sine_value = s3/e23) < min_sine_value ) + min_sine_value = sine_value; + /* actually use triple sine */ + *min_sine = sine_value = MPY_SINE * min_sine_value; + } + + if ( bAmbiguous && sine_value >= MIN_SINE ) { + /* check whether the central atom is outside the tetrahedra (0,0,0), at_coord[0,1,2] */ + /* compare the tetrahedra volume and the volume of a tetrahedra having central_at_coord[] vertex */ + int i; + diff3( central_at_coord, at_coord[0], tmp ); + central_prod[0] = triple_prod( at_coord[0], at_coord[1], central_at_coord, NULL ); + central_prod[1] = triple_prod( at_coord[1], at_coord[2], central_at_coord, NULL ); + central_prod[2] = triple_prod( at_coord[2], at_coord[0], central_at_coord, NULL ); + central_prod[3] = triple_prod( e[2], e[1], tmp, NULL ); + for ( i = 0; i <= 3; i ++ ) { + if ( central_prod[i] / ret < -MIN_SINE_OUTSIDE ) { + *bAmbiguous |= AMBIGUOUS_STEREO; + break; + } + } + } +#if ( STEREO_CENTER_BONDS_NORM == 1 ) + + if ( bLongEdges && !bAddedExplicitNeighbor && max_edge_len >= MIN_LEN_STRAIGHT ) { + /* possible planar tetragon */ + if ( sine_value < MIN_SINE_SQUARE ) { + *min_sine = MIN_SINE / 2.0; /* force parity to be undefined */ + if ( bAmbiguous && !*bAmbiguous ) { + *bAmbiguous |= AMBIGUOUS_STEREO; + } + } + } + + if ( bLongEdges && sine_value < MIN_SINE_SQUARE && sine_value < MIN_SINE_EDGE * min_edge_len_NoExplNeigh ) { + *min_sine = MIN_SINE / 2.0; /* force parity to be undefined */ + if ( bAmbiguous && !*bAmbiguous ) { + *bAmbiguous |= AMBIGUOUS_STEREO; + } + } +#endif + } else + if ( min_sine ) { + *min_sine = sine_value; + } + + return ret; +} + + +/*************************************************************/ +double triple_prod_and_min_abs_sine(double at_coord[][3], double *min_sine) +{ + double min_sine_value=9999.0, sine_value; + double prod=0.0; + + if ( !min_sine ) { + return triple_prod( at_coord[0], at_coord[1], at_coord[2], NULL ); + } + + prod = triple_prod( at_coord[0], at_coord[1], at_coord[2], &sine_value ); + sine_value = fabs( sine_value ); + min_sine_value = inchi_min( min_sine_value, sine_value ); + + prod = triple_prod( at_coord[1], at_coord[2], at_coord[0], &sine_value ); + sine_value = fabs( sine_value ); + min_sine_value = inchi_min( min_sine_value, sine_value ); + + prod = triple_prod( at_coord[2], at_coord[0], at_coord[1], &sine_value ); + sine_value = fabs( sine_value ); + min_sine_value = inchi_min( min_sine_value, sine_value ); + + *min_sine = min_sine_value; + + return prod; +} + + +/*************************************************************/ +/* Find if point (0,0,0)a and 3 atoms are in one plane */ +int are_3_vect_in_one_plane( double at_coord[][3], double min_sine) +{ + double actual_min_sine; + double prod; + prod = triple_prod_and_min_abs_sine( at_coord, &actual_min_sine); + return actual_min_sine <= min_sine; +} + + +/*************************************************************/ +/* Find if 4 atoms are in one plane */ +int are_4at_in_one_plane( double at_coord[][3], double min_sine) +{ + double actual_min_sine, min_actual_min_sine; + double coord[3][3], prod; + int i, k, j; + for ( k = 0; k < 4; k ++ ) { /* cycle added 4004-08-15 */ + for ( i = j = 0; i < 4; i ++ ) { + if ( i != k ) { + diff3( at_coord[i], at_coord[k], coord[j] ); + j ++; + } + } + prod = triple_prod_and_min_abs_sine( coord, &actual_min_sine); + if ( !k || actual_min_sine < min_actual_min_sine ) { + min_actual_min_sine = actual_min_sine; + } + } + return min_actual_min_sine <= min_sine; +} + + +/*************************************************************/ +int triple_prod_char( inp_ATOM *at, int at_1, int i_next_at_1, S_CHAR *z_dir1, + int at_2, int i_next_at_2, S_CHAR *z_dir2 ) +{ + inp_ATOM *at1, *at2; + double pnt[3][3], len; + int i; + int ret = 0; + + at1 = at + at_1; + at2 = at + at[at_1].neighbor[i_next_at_1]; + + pnt[0][0] = at2->x - at1->x; + pnt[0][1] = at2->y - at1->y; + pnt[0][2] = at2->z - at1->z; + + at2 = at + at_2; + at1 = at + at[at_2].neighbor[i_next_at_2]; + + pnt[1][0] = at2->x - at1->x; + pnt[1][1] = at2->y - at1->y; + pnt[1][2] = at2->z - at1->z; +/* + * resultant pnt vector directions: + * + * pnt[0] pnt[1] + * + * [at_1]---->[...] [...]---->[at_2] + * + * + * add3 below: (pnt[0] + pnt[1]) -> pnt[1] + */ + add3( pnt[0], pnt[1], pnt[1] ); + + + + for ( i = 0; i < 3; i ++ ) { + pnt[0][i] = (double)z_dir1[i]; + pnt[2][i] = (double)z_dir2[i]; + } + for ( i = 0; i < 3; i ++ ) { + len = len3( pnt[i] ); + if ( len < MIN_BOND_LEN ) { + if ( i == 1 && (at[at_1].bUsed0DParity || at[at_2].bUsed0DParity) ) { + pnt[i][0] = 0.0; + pnt[i][1] = 1.0; + pnt[i][2] = 0.0; + len = 1.0; /* standard at_1-->at_2 vector coordinates in case of 0D allene */ + } else { + goto exit_function; /* too short bond */ + } + } + mult3( pnt[i], 1.0/len, pnt[i] ); + } + len = 100.0*triple_prod(pnt[0], pnt[1], pnt[2], NULL ); +/* + * ^ pnt[0] + * | The orientation on this diagram + * | produces len = -100 + * [at_1]------>[at_2] + * pnt[1] / + * / + * / pnt[2] (up from the plane) + * v + * + * Note: len is invariant upon at_1 <--> at_2 transposition because + * triple product changes sign upon pnt[0]<-->pnt[2] transposition and + * triple product changes sign upon pnt[1]--> -pnt[1] change of direction: + * + * triple_prod(pnt[0], pnt[1], pnt[2], NULL ) = + * triple_prod(pnt[2], -pnt[1], pnt[0], NULL ) + * + */ + + ret = len >= 0.0? (int)(floor(len+0.5)) : -(int)(floor(0.5-len)); + +exit_function: + + return ret; +} + + +/****************************************************************/ + +#if ( NEW_STEREOCENTER_CHECK == 1 ) /* { */ + +/********************************************************************************************/ +int bInpAtomHasRequirdNeigh ( inp_ATOM *at, int cur_at, int RequirdNeighType, int NumDbleBonds ) +{ + /* RequirdNeighType: + reqired neighbor types (bitmap): + 0 => any neighbors + 1 => no terminal hydrogen atom neighbors + 2 => no terminal -X and -XH together (don't care about -X, -XH bond type, charge, radical) + (X = tautomeric endpoint atom) + NumDbleBonds: + if non-zero then allow double, alternating and tautomeric bonds + */ + int i, j, ni, nj, bond_type, num_1s, num_mult, num_other; + + if ( at[cur_at].endpoint ) { /* tautomeric endpoint cannot be a stereo center */ + return 0; + } + + if ( (1 & RequirdNeighType) && at[cur_at].num_H ) { + return 0; + } + + if ( 2 & RequirdNeighType ) { + for ( i = 0; i < at[cur_at].valence; i ++ ) { + ni = (int)at[cur_at].neighbor[i]; + if ( at[ni].valence != 1 || + !get_endpoint_valence( at[ni].el_number ) ) { + continue; + } + for ( j = i+1; j < at[cur_at].valence; j ++ ) { + nj = (int)at[cur_at].neighbor[j]; + if ( at[nj].valence != 1 || + at[ni].el_number != at[nj].el_number || + !get_endpoint_valence( at[nj].el_number ) ) { + continue; + } + /* + * if (at[ni].num_H != at[nj].num_H) then the atoms (neighbors of at[cur_at] + * are tautomeric endpoints and are indistinguishable => cur_at is not stereogenic + * if (at[ni].num_H == at[nj].num_H) then the neighbors are indistinguishable + * and cur_at will be found non-sterogenic later + * get_endpoint_valence() check will not allow the neighbors to be carbons + * Therefore the following "if" is not needed; we may just return 0. + */ + if ( at[ni].num_H != at[nj].num_H && strcmp(at[ni].elname, "C" ) ) { + return 0; /* found -X and -XH neighbors */ + } + } + } + } + + num_1s = num_mult = num_other = 0; + + for ( i = 0; i < at[cur_at].valence; i ++ ) { + bond_type = (at[cur_at].bond_type[i] & ~BOND_MARK_ALL); + switch( bond_type ) { + case BOND_SINGLE: + num_1s ++; + break; + case BOND_DOUBLE: + case BOND_ALTERN: + case BOND_TAUTOM: + case BOND_ALT12NS: + num_mult ++; + break; + default: + num_other ++; + break; + } + } + + if ( num_other ) { + return 0; + } + + if ( NumDbleBonds && NumDbleBonds > num_mult || + !NumDbleBonds && at[cur_at].valence != num_1s ) { + return 0; + } + return 1; +} + + +/********************************************************************************************/ +int bCanInpAtomBeAStereoCenter( inp_ATOM *at, int cur_at, int bPointedEdgeStereo ) +{ + +/************************************************************************************* + * current version + ************************************************************************************* + * Use #define to split the stereocenter description table into parts + * to make it easier to read + * + * --------- 4 single bonds stereocenters ------- + * 0 1 2 3 4 5 + * + * | | | | | | + * -C- -Si- -Ge- -Sn- >As[+] >B[-] + * | | | | | | + */ +#define SZELEM1 "C\000","Si", "Ge", "Sn", "As", "B\000", +#define CCHARGE1 0, 0, 0, 0, 1, -1, +#define CNUMBONDSANDH1 4, 4, 4, 4, 4, 4, +#define CCHEMVALENCEH1 4, 4, 4, 4, 4, 4, +#define CHAS3MEMBRING1 0, 0, 0, 0, 0, 0, +#define CREQUIRDNEIGH1 0, 0, 0, 0, 3, 0, +/* + * --------------- S, Se stereocenters ---------- + * 6 7 8 9 10 11 12 13 + * + * | | || | | || + * -S= =S= -S[+] >S[+] -Se= =Se= -Se[+] >Se[+] + * | | | | | | | | + */ +#define SZELEM2 "S\000","S\000","S\000","S\000","Se", "Se", "Se", "Se", +#define CCHARGE2 0, 0, 1, 1, 0, 0, 1, 1, +#define CNUMBONDSANDH2 3, 4, 3, 4, 3, 4, 3, 4, +#define CCHEMVALENCEH2 4, 6, 3, 5, 4, 6, 3, 5, +#define CHAS3MEMBRING2 0, 0, 0, 0, 0, 0, 0, 0, +#define CREQUIRDNEIGH2 3, 3, 3, 3, 3, 3, 3, 3, +/* + * ------------------ N, P stereocenters ----------------- + * 14 15 16 17 18 19 20 + * + * Phosphine Arsine + * X---Y + * | | \ / | | \ / \ / + * =N- >N[+] N >P[+] =P- P As + * | | | | | | | + */ +#define SZELEM3 "N\000","N\000","N\000","P\000","P\000","P\000", "As", +#define CCHARGE3 0, 1, 0, 1, 0, 0, 0, +#define CNUMBONDSANDH3 4, 4, 3, 4, 4, 3, 3, +#define CCHEMVALENCEH3 5, 4, 3, 4, 5, 3, 3, +#define CHAS3MEMBRING3 0, 0, 1, 0, 0, 0, 0, +#define CREQUIRDNEIGH3 3, 3, 1, 3, 3, 2, 2, + +#define PHOSPHINE_STEREO 19 /* the number must match Phosphine number in the comments, see above */ +#define ARSINE_STEREO 20 /* the number must match Arsine number in the comments, see above */ + + static const char szElem[][3]={ SZELEM1 SZELEM2 SZELEM3 }; + static const S_CHAR cCharge[]={ CCHARGE1 CCHARGE2 CCHARGE3 }; + static const S_CHAR cNumBondsAndH[]={ CNUMBONDSANDH1 CNUMBONDSANDH2 CNUMBONDSANDH3 }; + static const S_CHAR cChemValenceH[]={ CCHEMVALENCEH1 CCHEMVALENCEH2 CCHEMVALENCEH3 }; + static const S_CHAR cHas3MembRing[]={ CHAS3MEMBRING1 CHAS3MEMBRING2 CHAS3MEMBRING3 }; + static const S_CHAR cRequirdNeigh[]={ CREQUIRDNEIGH1 CREQUIRDNEIGH2 CREQUIRDNEIGH3 }; + + static const int n = sizeof(szElem)/sizeof(szElem[0]); + /* reqired neighbor types (bitmap): + 0 => check bonds only + 1 => no terminal hydrogen atom neighbors + 2 => no terminal -X and -XH together (don't care the bond type, charge, radical) + (X = tautomeric endpoint atom) + Note: whenever cChemValenceH[] > cNumBondsAndH[] + the tautomeric and/or alternating bonds + are permitted + + */ + int i, ret = 0; + for ( i = 0; i < n; i++ ) { + if ( !strcmp( at[cur_at].elname, szElem[i]) && + at[cur_at].charge == cCharge[i] && + (!at[cur_at].radical || at[cur_at].radical == 1) && + at[cur_at].valence +at[cur_at].num_H == cNumBondsAndH[i] && + at[cur_at].chem_bonds_valence+at[cur_at].num_H == cChemValenceH[i] && + (cHas3MembRing[i]? is_atom_in_3memb_ring( at, cur_at ) : 1) && + bInpAtomHasRequirdNeigh ( at, cur_at, cRequirdNeigh[i], cChemValenceH[i]-cNumBondsAndH[i]) ) { + ret = cNumBondsAndH[i]; + break; + } + } + + if ( i == PHOSPHINE_STEREO && !(bPointedEdgeStereo & PES_BIT_PHOSPHINE_STEREO) ) + ret = 0; + if ( i == ARSINE_STEREO && !(bPointedEdgeStereo & PES_BIT_ARSINE_STEREO) ) + ret = 0; + return ret; +} + +#else /* } NEW_STEREOCENTER_CHECK { */ + +/********************************************************************************************/ +int bCanAtomBeAStereoCenter( char *elname, S_CHAR charge, S_CHAR radical ) +{ + static const char szElem[][3] = { "C\000", "Si", "Ge", "N\000", "P\000", "As", "B\000" }; + static const S_CHAR cCharge[] = { 0, 0, 0, 1, 1, 1, -1 }; + int i, ret = 0; + for ( i = 0; i < sizeof(szElem)/sizeof(szElem[0]); i++ ) { + if ( !strcmp( elname, szElem[i] ) && (charge == cCharge[i]) ) { + ret = (!radical || radical == RADICAL_SINGLET); + break; + } + } + return ret; +} +#endif /* } NEW_STEREOCENTER_CHECK */ + +/****************************************************************/ +/* used for atoms adjacent to stereogenic bonds only */ +int bAtomHasValence3( char *elname, S_CHAR charge, S_CHAR radical ) +{ + static const char szElem[][3] = { "N\000" }; + static const S_CHAR cCharge[] = { 0, }; + int i, ret = 0; + for ( i = 0; i < (int)(sizeof(szElem)/sizeof(szElem[0])); i++ ) { + if ( !strcmp( elname, szElem[i] ) && (charge == cCharge[i]) ) { + ret = ( !radical || radical == RADICAL_SINGLET ); + break; + } + } + return ret; +} + +/****************************************************************/ +/* used for atoms adjacent to stereogenic bonds only */ +int bCanAtomHaveAStereoBond( char *elname, S_CHAR charge, S_CHAR radical ) +{ + static const char szElem[][3] = { "C\000", "Si", "Ge", "N\000", "N\000" }; + static const S_CHAR cCharge[] = { 0, 0, 0, 0, 1, }; + static const int n = sizeof(szElem)/sizeof(szElem[0]); + int i, ret = 0; + for ( i = 0; i < n; i++ ) { + if ( !strcmp( elname, szElem[i] ) && (charge == cCharge[i]) ) { + ret = (!radical || radical == RADICAL_SINGLET); + break; + } + } + return ret; +} + + +/****************************************************************/ +/* used for atoms adjacent to stereogenic bonds only */ +int bCanAtomBeMiddleAllene( char *elname, S_CHAR charge, S_CHAR radical ) +{ + static const char szElem[][3] = { "C\000", "Si", "Ge", }; + static const S_CHAR cCharge[] = { 0, 0, 0, }; + static const int n = sizeof(szElem)/sizeof(szElem[0]); + int i, ret = 0; + for ( i = 0; i < n; i++ ) { + if ( !strcmp( elname, szElem[i] ) && (charge == cCharge[i]) ) { + ret = (!radical || radical == RADICAL_SINGLET); + break; + } + } + return ret; +} + + +/*****************************************************************/ +int bIsSuitableHeteroInpAtom( inp_ATOM *at ) +{ + int val, num_H; + if ( 0 == at->charge && + (!at->radical || RADICAL_SINGLET == at->radical) && + 0 < (val=get_endpoint_valence( at->el_number ) )) { + num_H = at->num_H; + if ( val == at->chem_bonds_valence + num_H ) { + switch( val ) { + case 2: /* O */ + if ( !num_H && 1 == at->valence ) + return 0; /* =O */ + break; /* not found */ + case 3: /* N */ + if ( 1 == at->valence && 1 == num_H || + 2 == at->valence && 0 == num_H ) + return 1; /* =N- or =NH */ + break; /* not found */ + } + } + } + return -1; +} + + +/****************************************************************/ +int bIsOxide( inp_ATOM *at, int cur_at ) +{ + int i, bond_type; + inp_ATOM *a = at + cur_at, *an; + for ( i = 0; i < a->valence; i ++ ) { + bond_type = (a->bond_type[i] &= ~BOND_MARK_ALL); + if ( bond_type == BOND_DOUBLE ) { + an = at + (int)a->neighbor[i]; + if ( 1 == an->valence && + !an->charge && !an->num_H && !an->radical && + 2 == get_endpoint_valence( an->el_number ) ) { + return 1; + } + } else + if ( bond_type == BOND_TAUTOM || bond_type == BOND_ALT12NS ) { + an = at + (int)a->neighbor[i]; + if ( 1 == an->valence && + 2 == get_endpoint_valence( an->el_number ) ) { + return 1; + } + } + } + return 0; +} + + +/****************************************************************/ +/* used for atoms adjacent to stereogenic bonds only */ +int bCanAtomBeTerminalAllene( char *elname, S_CHAR charge, S_CHAR radical ) +{ + static const char szElem[][3] = { "C\000", "Si", "Ge", }; + static const S_CHAR cCharge[] = { 0, 0, 0, }; + static const int n = sizeof(szElem)/sizeof(szElem[0]); + int i, ret = 0; + for ( i = 0; i < n; i++ ) { + if ( !strcmp( elname, szElem[i] ) && (charge == cCharge[i]) ) { + ret = (!radical || radical == RADICAL_SINGLET); + break; + } + } + return ret; +} + + +/************************************************************************/ +int GetHalfStereobond0DParity( inp_ATOM *at, int cur_at, AT_NUMB nSbNeighOrigAtNumb[], + int nNumExplictAttachments, int bond_parity, int nFlag ) +{ + int m, last_parity, cur_parity; + int i, icur2nxt, icur2neigh, cur_order_parity, nxt_at; + AT_NUMB nNextSbAtOrigNumb; + /* find atom parities for all valid streobonds incident to at[cur_at] */ + for ( m = 0, last_parity = 0; m < MAX_NUM_STEREO_BONDS && at[cur_at].sb_parity[m]; m ++ ) { + icur2nxt = icur2neigh = -1; /* ordering number of neighbors in nSbNeighOrigAtNumb[] */ + cur_parity = 0; /* parity for mth stereobond incident to the cur_at */ + if ( 0 <= at[cur_at].sb_ord[m] && at[cur_at].sb_ord[m] < at[cur_at].valence && + 0 <= (nxt_at = at[cur_at].neighbor[(int)at[cur_at].sb_ord[m]]) && + at[nxt_at].valence <= MAX_NUM_STEREO_BONDS && /* make sure it is a valid stereobond */ + (nNextSbAtOrigNumb = at[nxt_at].orig_at_number) ) { + /* since at[cur_at].sn_ord[m] = -1 for explicit H use at[cur_at].sn_orig_at_num[m] */ + for ( i = 0; i < nNumExplictAttachments; i ++ ) { + if ( at[cur_at].sn_orig_at_num[m] == nSbNeighOrigAtNumb[i] ) { + icur2neigh = i; /* neighbor */ + } else + if ( nNextSbAtOrigNumb == nSbNeighOrigAtNumb[i] ) { + icur2nxt = i; /* atom connected by a stereobond */ + } + } + if ( icur2neigh >= 0 && icur2nxt >= 0 ) { + if ( ATOM_PARITY_WELL_DEF(at[cur_at].sb_parity[m]) ) { + /* parity of at[cur_atom] neighbor permutation to reach this order: { next_atom, neigh_atom, ...} */ + cur_order_parity = (icur2nxt + icur2neigh + (icur2nxt > icur2neigh) - 1) % 2; + cur_parity = 2 - (cur_order_parity + at[cur_at].sb_parity[m]) % 2; + } else { + /* unknowm/undef parities do not depend on the neighbor order */ + cur_parity = at[cur_at].sb_parity[m]; + } + } + } else { + continue; + } + /* use a well-known parity if available; if not then use preferably the unknown */ + if ( !last_parity ) { + last_parity = cur_parity; + } else + if ( last_parity != cur_parity && cur_parity ) { + if ( ATOM_PARITY_WELL_DEF(last_parity) ) { + if ( ATOM_PARITY_WELL_DEF(cur_parity) ) { + last_parity = 0; /* error: all well-defined parities should be same */ + break; + } + } else + if ( ATOM_PARITY_WELL_DEF(cur_parity) ) { + /* replace unknown/undefined parity with well-known */ + last_parity = cur_parity; + } else { + /* select min unknown/undefined parity (out of AB_PARITY_UNKN and AB_PARITY_UNDF) */ + last_parity = inchi_min(cur_parity, last_parity); + } + } + } + if ( last_parity ) { + bond_parity = last_parity; + at[cur_at].bUsed0DParity |= nFlag; /* set flag: used stereobond 0D parity */ + } + return bond_parity; +} + + +/*******************************************************************************************/ +int FixSb0DParities( inp_ATOM *at, /* inp_ATOM *at_removed_H, int num_removed_H,*/ int chain_length, + int at_1, int i_next_at_1, S_CHAR z_dir1[], + int at_2, int i_next_at_2, S_CHAR z_dir2[], + int *pparity1, int *pparity2 ) +{ + int k, parity1, parity2, abs_parity1, abs_parity2; + int j1, j2, parity_sign; + /* + AT_NUMB nSbNeighOrigAtNumb1[MAX_NUM_STEREO_BOND_NEIGH], nSbNeighOrigAtNumb2[MAX_NUM_STEREO_BOND_NEIGH]; + int nNumExplictAttachments1, nNumExplictAttachments2; + */ + parity1 = parity2 = AB_PARITY_NONE; + j1 = j2 = -1; + parity_sign = ( *pparity1 < 0 || *pparity2 < 0 )? -1 : 1; + + abs_parity1 = abs(*pparity1); + abs_parity2 = abs(*pparity2); + + for ( k = 0; k < MAX_NUM_STEREO_BONDS && at[at_1].sb_parity[k]; k ++ ) { + if ( at[at_1].sb_ord[k] == i_next_at_1 ) { + parity1 = at[at_1].sb_parity[k]; + j1 = k; + } + } + for ( k = 0; k < MAX_NUM_STEREO_BONDS && at[at_2].sb_parity[k]; k ++ ) { + if ( at[at_2].sb_ord[k] == i_next_at_2 ) { + parity2 = at[at_2].sb_parity[k]; + j2 = k; + } + } + switch( (j1 >= 0) + 2*(j2 >= 0) ) { + case 0: + /* the bond has no 0D parity */ + *pparity1 = *pparity2 = parity_sign * AB_PARITY_UNDF; + return 0; + case 1: + case 2: + /* 0D parity data error */ + *pparity1 = *pparity2 = AB_PARITY_NONE; + return -1; + case 3: + /* the bond has 0D parity */ + switch ( !(ATOM_PARITY_WELL_DEF( abs_parity1 ) && ATOM_PARITY_WELL_DEF( parity1 )) + + 2 * !(ATOM_PARITY_WELL_DEF( abs_parity2 ) && ATOM_PARITY_WELL_DEF( parity2 )) ) { + case 0: + /* both parities are well-defined; continue */ + break; + case 1: + /* 0D parity not well-defined for at_1 */ + *pparity1 = parity_sign * (ATOM_PARITY_WELL_DEF( parity1 )? abs_parity1 : + ATOM_PARITY_WELL_DEF( abs_parity1 )? parity1 : + inchi_min(abs_parity1, parity1)); + *pparity2 = parity_sign * abs_parity2; + return -1; + case 2: + /* 0D parity not well-defined for at_2 */ + *pparity1 = parity_sign * abs_parity1; + *pparity2 = parity_sign * (ATOM_PARITY_WELL_DEF( parity2 )? abs_parity2 : + ATOM_PARITY_WELL_DEF( abs_parity2 )? parity2 : + inchi_min(abs_parity2, parity2)); + return -1; + case 3: + abs_parity1 = (ATOM_PARITY_WELL_DEF( parity1 )? abs_parity1 : + ATOM_PARITY_WELL_DEF( abs_parity1 )? parity1 : + inchi_min(abs_parity1, parity1)); + abs_parity2 = (ATOM_PARITY_WELL_DEF( parity2 )? abs_parity2 : + ATOM_PARITY_WELL_DEF( abs_parity2 )? parity2 : + inchi_min(abs_parity2, parity2)); + *pparity1 = *pparity2 = parity_sign * inchi_min(abs_parity1, abs_parity2); + /*return (parity1 == parity2)? 0 : -1;*/ + return -1; + } + break; + } + /* we are here if both end-atoms of the bond have well-defined 0D parities */ + /* + nNumExplictAttachments1 = GetSbNeighOrigAtNumb( at, at_1, at_removed_H, num_removed_H, nSbNeighOrigAtNumb1 ); + nNumExplictAttachments2 = GetSbNeighOrigAtNumb( at, at_2, at_removed_H, num_removed_H, nSbNeighOrigAtNumb2 ); + parity1 = GetHalfStereobond0DParity( at, at_1, nSbNeighOrigAtNumb1, nNumExplictAttachments1, *pparity1, 0 ); + parity2 = GetHalfStereobond0DParity( at, at_2, nSbNeighOrigAtNumb2, nNumExplictAttachments2, *pparity2, 0 ); + */ + *pparity1 = parity_sign * abs_parity1; + *pparity2 = parity_sign * abs_parity2; + + if ( chain_length % 2 ) { + /* allene; chain_length = (number of double bonds) - 1 */ + /* + int zer1 = ( !z_dir1[0] && !z_dir1[1] && !z_dir1[2] ); + int zer2 = ( !z_dir2[0] && !z_dir2[1] && !z_dir2[2] ); + */ + int bWrong_z_dir1 = (0 != (at[at_1].bUsed0DParity & FlagSB_0D)); + int bWrong_z_dir2 = (0 != (at[at_2].bUsed0DParity & FlagSB_0D)); + + if ( bWrong_z_dir1 && bWrong_z_dir2 ) { + goto set_default; + } else + if ( bWrong_z_dir1 || bWrong_z_dir2 ) { + double r12[3], zi1[3], zi2[3], abs_r12, abs_zi2; + int at_i1, at_i2, j; + S_CHAR z_dir[3]; + r12[0] = at[at_2].x - at[at_1].x; + r12[1] = at[at_2].y - at[at_1].y; + r12[2] = at[at_2].z - at[at_1].z; + abs_r12 = len3( r12 ); + if ( abs_r12 < MIN_BOND_LEN ) { + goto set_default; + } + /* make r12[] point to the atom with 'good' z_dir[] */ + if ( bWrong_z_dir1 ) { + at_i1 = at_2; /* has good z_dir2[] */ + at_i2 = at_1; /* has bad z_dir1[] */ + zi1[0] = z_dir2[0]; + zi1[1] = z_dir2[1]; + zi1[2] = z_dir2[2]; + mult3( r12, 1.0/abs_r12, r12 ); /* make length = 1 */ + } else { + at_i1 = at_1; /* has good z_dir1[] */ + at_i2 = at_2; /* has bad z_dir2[] */ + zi1[0] = z_dir1[0]; + zi1[1] = z_dir1[1]; + zi1[2] = z_dir1[2]; + mult3( r12, -1.0/abs_r12, r12 ); /* make length = 1 */ + } + cross_prod3( r12, zi1, zi2 ); + abs_zi2 = len3( zi2 ); + mult3( zi2, 100.0/abs_zi2, zi2 ); /* make length = 100 */ + for ( j = 0; j < 3; j ++ ) { + z_dir[j] = (S_CHAR) (zi2[j]>= 0.0? floor(0.5 + zi2[j]) : + -floor(0.5 - zi2[j])); /* abs(z_dir) = 100 */ + } + if ( bWrong_z_dir1 ) { + memcpy( z_dir1, z_dir, sizeof(z_dir) ); + } else { + memcpy( z_dir2, z_dir, sizeof(z_dir) ); + } + } + return 0; + +set_default: + /* z_dir1[] = x-direction; z_dir2[] = z-direction; r12[] = y-direction */ + z_dir1[0] = 100; + z_dir1[1] = z_dir1[2] = 0; + z_dir2[0] = z_dir2[1] = 0; + z_dir2[2] = 100; + } + return 0; +} + + +/**********************************************************/ +/* without this InChI fails on reconstructed CID=450438 */ +/* (isotopic, Unknown SB adjacent to SB with known parity) */ +/**********************************************************/ +int FixUnkn0DStereoBonds(inp_ATOM *at, int num_at) +{ + int i, m, num=0; + + /* add usual Unknown stereobond descriptors to each Unknown bond */ + for( i = 0; i < num_at; i ++ ) { + for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[i].sb_parity[m]; m ++ ) { + if ( AB_PARITY_UNKN == at[i].sb_parity[m] ) { + at[i].bond_stereo[ (int)at[i].sb_ord[m] ] = STEREO_DBLE_EITHER; + num ++; + } + } + } +#ifdef NEVER + if ( num ) { + int j; + /* how to remove Unknown stereo bond parities */ + for( i = 0; i < num_at; i ++ ) { + for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[i].sb_parity[m]; m ++ ) { + if ( AB_PARITY_UNKN == at[i].sb_parity[m] ) { + for ( j = m+1; j < MAX_NUM_STEREO_BONDS; j ++ ) { + at[i].sb_parity[j-1] = at[i].sb_parity[j]; + at[i].sb_ord[j-1] = at[i].sb_ord[j]; + at[i].sn_ord[j-1] = at[i].sn_ord[j]; + at[i].sn_orig_at_num[j-1] = at[i].sn_orig_at_num[j]; + } + at[i].sb_parity[j-1] = 0; + at[i].sb_ord[j-1] = 0; + at[i].sn_ord[j-1] = 0; + at[i].sn_orig_at_num[j-1] = 0; + } + } + } + } +#endif + return num; +} + + +/*====================================================================================================== + +half_stereo_bond_parity() General Description: + + A) find projections of 3 bonds on a reasonable plane defined + by a vector z_dir perpendicular to the plane + B) calculate parity + +half_stereo_bond_parity() Detailed Description: + + 1) Find at_coord[] = vectors from the central atoms to its neighbors + 2) If only 2 neighbors are present, then create a reasonable 3rd neighbor + (an implicit H or a fictitious atom in case of =NX) coordinates + 3) Normalize at_coord[] to unit length + 4) Find unit vector pnt[2] perpendicular to the plane containing + at_coord[] arrow ends. + Even though it is not necessary, make z-coordinate of pnt[2] positive. + ** pnt[2] has the new z-axis direction ** + 5) Let pnt[0] = perpendicular to pnt[2] component of at_coord[0]; + Normalize pnt[0] to unit length. + ** pnt[0] has the new x-axis direction ** + 6) Let pnt[1] = pnt[2] x pnt[0] (cross-product); + ** pnt[1] has the new y-axis direction ** + 7) Find at_coord[] in the new xyz-basis and normalize their xy-projections + to a unit length + 8) In the new xy-plane find (counterclockwise) angles: + tmp1 = (from at_coord[0] to at_coord[1]) + tmp2 = (from at_coord[0] to at_coord[2]) + 9) Calculate the parity: if tmp1 < tmp2 then 1 (odd) else 2 (even) + (even: looking from the arrow end of the new z-axis, 0, 1, and 2 neighbors + are in clockwise order) + 10) Calculate z_dir = 100*pnt[2]. + + Note1. If z_dir vectors of atoms located at the opposite ends of a double bond have approximately + opposite directions (that is, their dot-product is negative) then the parity of the + stereogenic bond calculated from half-bond-parities should be inverted + + Note2. In case of a tetrahedral cumulene a triple product (z_dir1, (1->2), z_dir2) is used instead + of the dot-product. (1->2) is a vector from the atom#1 to the atom #2. This triple product + is invariant with respect to the atom numbering because it does not change upon (1,2) + permutation. + + Stereo ambiguity in case of 2 neighbors: + ---------------------------------------- + Undefined: single-double bond angle > pi - arcsin(0.03) = 178.28164199834454285275613218975 degrees + Ambiguous: single-double bond angle > 175 degrees = pi - 0.087156 Rad + + Return values + (cases: I=only in case of isotopic H atoms the neighbors are different, + N=in case of non-isotopic H atoms the neighbors are different) + + -4 = AB_PARITY_UNDF => atom is adjacent to a stereogenic bond, but the geometry is undefined, I + -3 = AB_PARITY_UNKN => atom is adjacent to a stereogenic bond, but the geometry is not known to the iuser, I + -2 =-AB_PARITY_EVEN => parity of an atom adjacent to a stereogenic bond, I + -1 =-AB_PARITY_ODD => parity of an atom adjacent to a stereogenic bond, I + 0 = AB_PARITY_NONE => the atom is not adjacent to a stereogenic bond + 1 = AB_PARITY_ODD => parity of an atom adjacent to a stereogenic bond, N&I + 2 = AB_PARITY_EVEN => parity of an atom adjacent to a stereogenic bond, N&I + 3 = AB_PARITY_UNKN => atom is adjacent to a stereogenic bond, but the geometry is not known to the iuser, N&I + 4 = AB_PARITY_UNDF => atom is adjacent to a stereogenic bond, but the geometry is undefined, N&I + 5 = AB_PARITY_IISO => atom constitutionally equivalent to this atom may be adjacent to a stereogenic bond, I + + +=====================================================================================================*/ + +int half_stereo_bond_parity( inp_ATOM *at, int cur_at, inp_ATOM *at_removed_H, + int num_removed_H, S_CHAR *z_dir, + int bPointedEdgeStereo, int vABParityUnknown ) +{ + double at_coord[MAX_NUM_STEREO_BOND_NEIGH][3], c, s, tmp[3], tmp1, tmp2, min_tmp, max_tmp, z; + double temp[3], pnt[3][3]; + int j, k, p0, p1, p2, next, bValence3=0, num_z, nType, num_either_single, num_either_double; + int nNumExplictAttachments; + int bond_parity = AB_PARITY_UNDF; + int num_H=0, num_iH, num_eH=0, num_nH=0 /* = num_iso_H[0] */; + int num_iso_H[NUM_H_ISOTOPES+1]; + int index_H[5]; /* cannot have more than 4 elements: 1 H, 1 1H, 1 D, 1 T atom(s) */ + /* const double one_pi = 2.0*atan2(1.0 , 0.0 ); */ + const double one_pi = 3.14159265358979323846; /* M_PI */ + const double two_pi = 2.0*one_pi; + int bIgnoreIsotopicH = (0 != (at[cur_at].cFlags & AT_FLAG_ISO_H_POINT)); + AT_NUMB nSbNeighOrigAtNumb[MAX_NUM_STEREO_BOND_NEIGH]; + + + if ( z_dir && !z_dir[0] && !z_dir[1] && !z_dir[2] ) { + z_dir[2]=100; + } + + num_H = at[cur_at].num_H; + if ( num_H > NUM_H_ISOTOPES ) + return 0; /* at least 2 H atoms are isotopically identical */ + + if ( MAX_NUM_STEREO_BOND_NEIGH < at[cur_at].valence + num_H || + MIN_NUM_STEREO_BOND_NEIGH > at[cur_at].valence + num_H ) + return 0; + + if ( !bCanAtomHaveAStereoBond( at[cur_at].elname, at[cur_at].charge, at[cur_at].radical ) ) + return 0; + if ( !bIgnoreIsotopicH ) { + for ( j = 0, num_nH = num_H; j < NUM_H_ISOTOPES; j ++ ) { + if ( (k = (int)at[cur_at].num_iso_H[j]) > 1 ) { + return AB_PARITY_IISO; /* two or more identical isotopic H atoms */ + } + num_nH -= k; + } + } + /* at this point num_nH = number of non-isotopic H atoms */ + if ( num_nH > 1 ) + return AB_PARITY_IISO; /* two or more identical non-isotopic H atoms */ + if ( num_nH < 0 ) + return CT_ISO_H_ERR; /* program error */ /* */ + + /******************************************************************** + * Note. At this point all (implicit and explicit) isotopic + * terminal H neighbors are either different or not present. + ********************************************************************/ + + /* locate explicit hydrogen atoms */ + /* (at_removed_H are sorted in ascending isotopic H mass order, non-isotopic first) */ + memset( num_iso_H, 0, sizeof(num_iso_H) ); + if ( at_removed_H && num_removed_H > 0 ) { + for ( j = 0; j < num_removed_H; j ++ ) { + if ( at_removed_H[j].neighbor[0] == cur_at ) { + k = bIgnoreIsotopicH? 0 : at_removed_H[j].iso_atw_diff; + if ( 0 <= k && k <= NUM_H_ISOTOPES ) { + if ( ++num_iso_H[k] > 1 ) /* num_iso_H[0] = number of non-isotopic H atoms */ + return CT_ISO_H_ERR; /* program error in counting hydrogens */ /* */ + index_H[num_eH++] = j; + } else { + return CT_ISO_H_ERR; /* program error */ /* */ + } + } + } + num_iH = num_H - num_eH; /* number of implicit non-isotopic and isotopic H atoms */ + if ( num_iH > 1 ) { + /* more than one implicit H: cannot reconstruct the geometry */ + bond_parity = -AB_PARITY_UNDF; + goto exit_function; + } + } else { + num_iH = num_H; + } + /* at this point num_iH = number of implicit non-isotopic and isotopic H atoms */ + if ( at[cur_at].valence + num_eH < MIN_NUM_STEREO_BOND_NEIGH ) { + /* =NH or =CHD when no explicit H is present */ + return num_H == 1? AB_PARITY_UNDF : -AB_PARITY_UNDF; + } + + bValence3 = bAtomHasValence3( at[cur_at].elname, at[cur_at].charge, at[cur_at].radical ); + /* + * Can one explicit hydrogen be added to make asymmetric configuration? + * For now we can add 1 H atom in case of an appropriate geometry if: + * (a) one non-isotopic H (even if explicit isotopic H atoms are present), or + * (b) one isotopic or non-isotopic H if NO explicit isotopic or non-isotopic H atom is present + * This makes sense only in case chem. valence = 4. In case of chem. valence = 3, do not check. + */ + if ( at[cur_at].valence + num_eH == MIN_NUM_STEREO_BOND_NEIGH && !bValence3 && + !(/*(a)*/ 1 == num_nH && !num_iso_H[0] || + /*(b)*/ 1 == num_H && !num_eH) + ) { + goto exit_function; + /* return num_H == 1? AB_PARITY_UNDF : -AB_PARITY_UNDF; */ + } + + /* store neighbors coordinates */ + num_z = num_either_single = num_either_double = 0; + for ( k = nNumExplictAttachments = 0; k < 2; k ++ ) { + switch( k ) { + case 0: + for ( j = 0; j < num_eH; j ++, nNumExplictAttachments ++ ) { + next = index_H[j]; + at_coord[nNumExplictAttachments][0] = at_removed_H[next].x - at[cur_at].x; + at_coord[nNumExplictAttachments][1] = at_removed_H[next].y - at[cur_at].y; + nSbNeighOrigAtNumb[nNumExplictAttachments] = at_removed_H[next].orig_at_number; + /* use the fact that (at_removed_H - at) = (number of atoms except removed explicit H) */ + + z = -get_z_coord( at, (int) (at_removed_H-at)+next, + 0 /*neighbor #*/, + &nType, + -(bPointedEdgeStereo & PES_BIT_POINT_EDGE_STEREO) ); + + switch ( nType ) { + case ZTYPE_EITHER: + num_either_single ++; /* bond in "Either" direction. */ + break; + case ZTYPE_UP: + case ZTYPE_DOWN: + nType = -nType; /* at_removed_H[] contains bonds TO the center, not from */ + z = len2( at_coord[nNumExplictAttachments] ); + /* + z = sqrt( at_coord[nNumExplictAttachments][0]*at_coord[nNumExplictAttachments][0] + + at_coord[nNumExplictAttachments][1]*at_coord[nNumExplictAttachments][1] ); + */ + if ( nType == ZTYPE_DOWN ) + z = -z; + /* no break; here */ + case ZTYPE_3D: + num_z ++; + } + at_coord[nNumExplictAttachments][2] = z; + } + break; + case 1: + for ( j = 0; j < at[cur_at].valence; j ++, nNumExplictAttachments ++ ) { + next = at[cur_at].neighbor[j]; + at_coord[nNumExplictAttachments][0] = at[next].x - at[cur_at].x; + at_coord[nNumExplictAttachments][1] = at[next].y - at[cur_at].y; + nSbNeighOrigAtNumb[nNumExplictAttachments] = at[next].orig_at_number; + + z = get_z_coord( at, cur_at, j /*neighbor #*/, &nType, (bPointedEdgeStereo & PES_BIT_POINT_EDGE_STEREO) ); + switch ( nType ) { + case ZTYPE_EITHER: + num_either_single ++; /* bond in "Either" direction. */ + break; + case ZTYPE_UP: + case ZTYPE_DOWN: + z = len2( at_coord[nNumExplictAttachments] ); + /* + z = sqrt( at_coord[nNumExplictAttachments][0]*at_coord[nNumExplictAttachments][0] + + at_coord[nNumExplictAttachments][1]*at_coord[nNumExplictAttachments][1] ); + */ + if ( nType == ZTYPE_DOWN ) + z = -z; + /* no break; here */ + case ZTYPE_3D: + num_z ++; + } + at_coord[nNumExplictAttachments][2] = z; + } + break; + } + } + + if ( num_either_single ) { + bond_parity = vABParityUnknown /*AB_PARITY_UNKN*/; /* single bond is 'unknown' */ + goto exit_function; + } + + /* nNumExplictAttachments is a total number of attachments, including removed explicit terminal hydrogens */ + if ( nNumExplictAttachments == 2 ) { + /* create coordinates of the implicit hydrogen (or a fictitious atom in case of ==N-X ), */ + /* coord[2][], attached to the cur_at. */ + for ( j = 0; j < 3; j ++ ) { + at_coord[2][j] = - ( at_coord[0][j] + at_coord[1][j] ); + } + nSbNeighOrigAtNumb[nNumExplictAttachments] = 0; /* implicit H or lone pair */ + } + for ( j = 0; j < 3; j ++ ) { + tmp[j] = len3( at_coord[j] ); + } + min_tmp = inchi_min( tmp[0], inchi_min(tmp[1], tmp[2]) ); + max_tmp = inchi_max( tmp[0], inchi_max(tmp[1], tmp[2]) ); + if ( min_tmp < MIN_BOND_LEN || min_tmp < MIN_SINE*max_tmp ) { + /* all bonds or some of bonds are too short */ + if ( at[cur_at].sb_parity[0] ) { + /* use bond psrity; the reconciliation in ReconcileAllCmlBondParities() + * has made all ways to calculate parity produce same result + */ + bond_parity = GetHalfStereobond0DParity( at, cur_at, nSbNeighOrigAtNumb, + nNumExplictAttachments, bond_parity, FlagSB_0D ); + } + + goto exit_function; + } + /* normalize lengths to 1 */ + for ( j = 0; j < 3; j ++ ) { + mult3( at_coord[j], 1.0/tmp[j], at_coord[j] ); + } + + /* find projections of at_coord vector differences on the plane containing their arrowhead ends */ + for ( j = 0; j < 3; j ++ ) { + /* pnt[0..2] = {0-1, 1-2, 2-0} */ + tmp[j] = len3(diff3( at_coord[j], at_coord[(j+1)%3], pnt[j] )); + if ( tmp[j] < MIN_SINE ) { + goto exit_function; /* angle #i-cur_at-#j is too small */ + } + mult3( pnt[j], 1.0/tmp[j], pnt[j] ); /* 2003-10-06 */ + } + /* find pnt[p2], a vector perpendicular to the plane, and its length tmp[p2] */ + /* replace previous pnt[p2], tmp[p2] with new values; the old values do not have any additional */ + /* information because pnt[p0]+pnt[p1]+pnt[p2]=0 */ + /* 10-6-2003: a cross-product of one pair pnt[j], pnt[(j+1)%3] can be very small. Find the larges one */ + tmp1 = len3( cross_prod3( pnt[0], pnt[1], temp ) ); + for (j = 1, k = 0; j < 3; j ++ ) { + tmp2 = len3( cross_prod3( pnt[j], pnt[(j+1)%3], temp ) ); + if ( tmp2 > tmp1 ) { + tmp1 = tmp2; + k = j; + } + } + /* previously p0=0, p1=1, p2=2 */ + p0 = k; + p1 = (k+1)%3; + p2 = (k+2)%3; + tmp[p2] = len3( cross_prod3( pnt[p0], pnt[p1], pnt[p2] ) ); + if ( tmp[p2] < MIN_SINE*tmp[p0]*tmp[p1] ) { + goto exit_function; /* pnt[p0] is almost colinear to pnt[p1] */ + } + /* new basis: pnt[p0], pnt[p1], pnt[p2]; set z-coord sign and make abs(pnt[p2]) = 1 */ + mult3( pnt[p2], (pnt[p2][2]>0.0? 1.0:-1.0)/tmp[p2], pnt[p2] ); /* unit vector in the new z-axis direction */ + + min_tmp = dot_prod3( at_coord[0], pnt[p2] ); /* non-planarity measure (sine): hight of at_coord[] pyramid */ + mult3( pnt[p2], min_tmp, pnt[p0] ); /* vector height of the pyramid, ideally 0 */ + /* find new pnt[p0] = projection of at_coord[p0] on plane orthogonal to pnt[p2] */ + tmp[p0] = len3(diff3( at_coord[0], pnt[p0], pnt[p0] )); + mult3( pnt[p0], 1.0/tmp[p0], pnt[p0] ); /* new x axis basis vector */ + cross_prod3( pnt[p2], pnt[p0], pnt[p1] ); /* new y axis basis vector */ + /* find at_coord in the new basis of {pnt[p0], pnt[p1], pnt[p2]} */ + for ( j = 0; j < 3; j ++ ) { + copy3( at_coord[j], temp ); + for ( k = 0; k < 3; k ++ ) { + at_coord[j][k] = dot_prod3( temp, pnt[(k+p0)%3] ); + } + /* new xy plane projection length */ + tmp[j] = sqrt(at_coord[j][0]*at_coord[j][0] + at_coord[j][1]*at_coord[j][1]); + /* make new xy plane projection length = 1 */ + mult3( at_coord[j], 1.0/tmp[j], at_coord[j] ); + } + + s = fabs( at_coord[1][0]*at_coord[2][1] - at_coord[1][1]*at_coord[2][0] ); /* 1-2 sine */ + c = at_coord[1][0]*at_coord[2][0] + at_coord[1][1]*at_coord[2][1]; /* 1-2 cosine */ + if ( s < MIN_SINE && c > 0.5 ) { + goto exit_function; /* bonds to neigh. 1 and 2 have almost same direction; relative angles are undefined */ + } + c = at_coord[0][0]; /* cosine of the angle between new Ox axis and a bond to the neighbor 0. Should be 1 */ + s = at_coord[0][1]; /* sine. Should be 0 */ + /* turn vectors so that vector #1 (at_coord[0]) becomes {1, 0} */ + for ( j = 0; j < MAX_NUM_STEREO_BOND_NEIGH; j ++ ) { + tmp1 = c*at_coord[j][0] + s*at_coord[j][1]; + tmp2 = -s*at_coord[j][0] + c*at_coord[j][1]; + at_coord[j][0] = tmp1; + at_coord[j][1] = tmp2; + } + /* counterclockwise angles from the direction to neigh 0 to to directions to neighbors 1 and 2: */ + tmp1 = atan2( at_coord[1][1], at_coord[1][0] ); /* range -pi and +pi */ + tmp2 = atan2( at_coord[2][1], at_coord[2][0] ); + if ( tmp1 < 0.0 ) + tmp1 += two_pi; /* range 0 to 2*pi */ + if ( tmp2 < 0.0 ) + tmp2 += two_pi; + /*----------------------------------- + Example + 1 \ case tmp1 < tmp2 + \ parity is odd + \ (counterclockwise) + A------- 0 + / + / + 2 / + + ------------------------------------*/ + bond_parity = 2 - ( tmp1 < tmp2 ); + for ( j = 0; j < 3; j ++ ) { + z_dir[j] = (S_CHAR) (pnt[p2][j]>= 0.0? floor(0.5 + 100.0 * pnt[p2][j]) : + -floor(0.5 - 100.0 * pnt[p2][j])); /* abs(z_dir) = 100 */ + } + /* check for ambiguity */ + if ( nNumExplictAttachments > 2 ) { + min_tmp = inchi_min( tmp1, tmp2 ); + max_tmp = inchi_max( tmp1, tmp2 ); + if ( min_tmp > one_pi-MIN_SINE || max_tmp < one_pi+MIN_SINE || max_tmp-min_tmp > one_pi - MIN_SINE ) { + at[cur_at].bAmbiguousStereo |= AMBIGUOUS_STEREO; + } else /* 3D ambiguity 8-28-2002 */ + if ( fabs(at_coord[0][2]) > MAX_SINE ) { /* all fabs(at_coord[j][2] (j=0..2) must be equal */ + at[cur_at].bAmbiguousStereo |= AMBIGUOUS_STEREO; + } + } else + if ( nNumExplictAttachments == 2 ) { /* 10-6-2003: added */ + min_tmp = fabs(tmp1 - one_pi); + if ( min_tmp < MIN_SINE ) { + bond_parity = AB_PARITY_UNDF; /* consider as undefined 10-6-2003 */ + } else + if ( min_tmp < MIN_ANGLE_DBOND ) { + at[cur_at].bAmbiguousStereo |= AMBIGUOUS_STEREO; + } + } + + + /* for 3 neighbors moving implicit H to the index=0 from index=2 position */ + /* can be done in 2 transpositions and does not change atom's parity */ +exit_function: + if ( num_H > 1 && bond_parity > 0 && !(bond_parity & AB_PARITY_0D) /*&& PARITY_WELL_DEF(bond_parity)*/ ) { + /* + * stereo only if isotopes are counted. Do not inverse + * Examples: sign for this: + * H D + * / / H + * ==C or ==CH / + * \ ==N (bValence3=1) + * D + * two explicit one explicit H isotope (D), + * isotopic H atoms one implicit H + */ + bond_parity = -bond_parity; /* refers to isotopically substituted structure only */ + } + return bond_parity; +} + +/*************************************************************/ +int save_a_stereo_bond( int z_prod, int result_action, + int at1, int ord1, AT_NUMB *stereo_bond_neighbor1, S_CHAR *stereo_bond_ord1, S_CHAR *stereo_bond_z_prod1, S_CHAR *stereo_bond_parity1, + int at2, int ord2, AT_NUMB *stereo_bond_neighbor2, S_CHAR *stereo_bond_ord2, S_CHAR *stereo_bond_z_prod2, S_CHAR *stereo_bond_parity2 ) +{ + int i1, i2; + for ( i1 = 0; i1 < MAX_NUM_STEREO_BONDS && stereo_bond_neighbor1[i1]; i1 ++ ) + ; + for ( i2 = 0; i2 < MAX_NUM_STEREO_BONDS && stereo_bond_neighbor2[i2]; i2 ++ ) + ; + if ( i1 == MAX_NUM_STEREO_BONDS || i2 == MAX_NUM_STEREO_BONDS ) + return 0; + + stereo_bond_parity1[i1] = + stereo_bond_parity2[i2] = result_action; + + stereo_bond_neighbor1[i1] = (AT_NUMB) (at2+1); + stereo_bond_ord1[i1] = (S_CHAR)ord1; + stereo_bond_neighbor2[i2] = (AT_NUMB) (at1+1); + stereo_bond_ord2[i2] = (S_CHAR)ord2; + stereo_bond_z_prod1[i1] = + stereo_bond_z_prod2[i2] = (S_CHAR)z_prod; + return 1; +} + + +/***************************************************************/ +int get_allowed_stereo_bond_type( int bond_type ) +{ +#if (ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS == 0 ) + if ( (bond_type & ~BOND_MARK_ALL) == BOND_TAUTOM ) + return 0; /* no tautomer bonds allowed */ + else +#endif +#if ( EXCL_ALL_AROM_BOND_PARITY == 1 ) /* { */ + /* a stereo bond cannot belong to an aromatic atom */ + if ( (bond_type &= ~BOND_MARK_ALL) == BOND_ALTERN ) + { + return 0; + } +#else /* } { */ +#if ( ADD_6MEMB_AROM_BOND_PARITY == 1 ) + /* accept any aromatic bond as a stereo bond */ + if ( (bond_type &= ~BOND_MARK_ALL) == BOND_ALTERN ) +#else + /* accept only aromatic bonds in non-6-member rings */ + if ( (bond_type &= ~BOND_MARK_ALL) == BOND_ALTERN ) ) +#endif + { + return BOND_ALTERN; + } +#endif /* } */ + else + /* at this point BOND_MARK_ALL bits have been removed from bond_type */ + if ( bond_type == BOND_DOUBLE || bond_type == BOND_SINGLE ) { + return bond_type; + } +#if (ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS == 1 ) + else + if ( bond_type == BOND_TAUTOM ) { + return BOND_TAUTOM; + } +#endif + + return 0; /* wrong bond type */ +} + +/*************************************************************/ +int can_be_a_stereo_bond_with_isotopic_H( inp_ATOM *at, int cur_at, INCHI_MODE nMode ) +{ + int i, j, next_at, num_stereo_bonds, bFound; + int bond_type, num_2s, num_alt; + int num_2s_next, num_alt_next, num_wrong_bonds_1, num_wrong_bonds_2; +#if ( N_V_STEREOBONDS == 1 ) + int n2sh, num_2s_hetero[2], num_2s_hetero_next[2], next_next_at, type_N, type_N_next; +#endif + if ( MAX_NUM_STEREO_BOND_NEIGH < at[cur_at].valence+at[cur_at].num_H || + MIN_NUM_STEREO_BOND_NEIGH > at[cur_at].valence+at[cur_at].num_H ) + return 0; + if ( !bCanAtomHaveAStereoBond( at[cur_at].elname, at[cur_at].charge, at[cur_at].radical ) ) + return 0; + /* count bonds and find the second atom on the stereo bond */ + num_2s = num_alt = num_wrong_bonds_1 = 0; +#if ( N_V_STEREOBONDS == 1 ) + num_2s_hetero[0] = num_2s_hetero[1] = type_N = 0; + if ( 0 == at[cur_at].num_H && 0 == at[cur_at].charge && 0 == at[cur_at].radical && + 3 == get_endpoint_valence( at[cur_at].el_number ) ) { + if ( 2 == at[cur_at].valence && 3 == at[cur_at].chem_bonds_valence ) { + type_N = 1; + } else + if ( 3 == at[cur_at].valence && 5 == at[cur_at].chem_bonds_valence ) { + type_N = 2; /* unfortunately includes >N# */ + } + } +#endif + for ( i = 0, num_stereo_bonds = 0; i < at[cur_at].valence; i ++ ) { + bFound = 0; + next_at = at[cur_at].neighbor[i]; + bond_type = get_allowed_stereo_bond_type( (int)at[cur_at].bond_type[i] ); + if ( bond_type == BOND_ALTERN ) { + num_alt ++; + if ( cur_at > next_at && !(nMode & CMODE_NO_ALT_SBONDS) ) + bFound = 1; + } else + if ( bond_type == BOND_DOUBLE ) { + num_2s ++; +#if ( N_V_STEREOBONDS == 1 ) + if ( 0 <= (n2sh = bIsSuitableHeteroInpAtom( at + next_at )) ) { + num_2s_hetero[n2sh] ++; /* n2sh=0 -> =N- or =NH; n2sh=1 -> =O */ + } +#endif + if ( cur_at > next_at ) + bFound = 1; + } else + if ( bond_type != BOND_SINGLE && bond_type != BOND_TAUTOM ) { + num_wrong_bonds_1 ++; +#if ( ONE_BAD_SB_NEIGHBOR == 1 ) + if ( num_wrong_bonds_1 > 1 || num_wrong_bonds_1 && 2 >= at[cur_at].valence ) { + return 0; /* wrong bond type */ + } else { + continue; + } +#else + return 0; /* wrong bond type */ +#endif + } + + if ( bFound ) { + /* check "next_at" atom on the opposite side of the bond */ + if ( MAX_NUM_STEREO_BOND_NEIGH < at[next_at].valence+at[next_at].num_H || + MIN_NUM_STEREO_BOND_NEIGH > at[next_at].valence+at[next_at].num_H ) + continue; + if ( !bCanAtomHaveAStereoBond( at[next_at].elname, at[next_at].charge, at[next_at].radical ) ) + continue; + /* next atom neighbors */ + num_2s_next = num_alt_next = num_wrong_bonds_2 = 0; +#if ( N_V_STEREOBONDS == 1 ) + num_2s_hetero_next[0] = num_2s_hetero_next[1] = type_N_next = 0; + if ( 0 == at[next_at].num_H && 0 == at[next_at].charge && 0 == at[next_at].radical && + 3 == get_endpoint_valence( at[next_at].el_number ) ) { + if ( 2 == at[next_at].valence && 3 == at[next_at].chem_bonds_valence ) { + type_N_next = 1; /* -N= */ + } else + if ( 3 == at[next_at].valence && 5 == at[next_at].chem_bonds_valence ) { + type_N_next = 2; /* unfortunately includes >N# */ + } + } +#endif + for ( j = 0; j < at[next_at].valence; j ++ ) { + bond_type = get_allowed_stereo_bond_type( (int)at[next_at].bond_type[j] ); + if ( bond_type == BOND_ALTERN ) + num_alt_next ++; + else + if ( bond_type == BOND_DOUBLE ) { + num_2s_next ++; +#if ( N_V_STEREOBONDS == 1 ) + next_next_at = at[next_at].neighbor[j]; + if ( 0 <= (n2sh = bIsSuitableHeteroInpAtom( at + next_next_at )) ) { + num_2s_hetero_next[n2sh] ++; /* n2sh=0 -> =N- or =NH; n2sh=1 -> =O */ + } +#endif + } else + if ( bond_type != BOND_SINGLE && bond_type != BOND_TAUTOM ) { + num_wrong_bonds_2 ++; +#if ( ONE_BAD_SB_NEIGHBOR == 1 ) + if ( num_wrong_bonds_1 > 1 || num_wrong_bonds_1 && 2 >= at[cur_at].valence ) { + break; /* wrong bond type */ + } else { + continue; + } +#else + break; /* wrong bond type */ +#endif + } + } + /* figure out whether the at[cur_at]--at[next_at] bond may not be stereogenic */ + +#if ( N_V_STEREOBONDS == 1 ) + if ( 3 == (type_N | type_N_next) && + ( 2 == type_N && !bIsOxide( at, cur_at ) || + 2 == type_N_next && !bIsOxide( at, next_at ) ) ) { + bFound = 0; + } else +#endif + if ( j < at[next_at].valence || /* at[next_at] has a wrong bond type*/ + (num_alt_next>0) + (num_2s_next>0) != 1 /* only one type of stereogenic bond permitted */ + ) { + bFound = 0; + } else + if ( 2 < num_2s_next ) { + bFound = 0; + } else + if ( 2 == num_2s_next ) { + if ( 2 == at[next_at].valence ) { + ; /* only one double bond permitted except cumulenes */ +#if ( N_V_STEREOBONDS == 1 ) + } else + if ( 1 == (num_2s_hetero_next[0] | num_2s_hetero_next[1]) && + 3 == at[next_at].valence + at[next_at].num_H && + 5 == at[next_at].chem_bonds_valence + at[next_at].num_H && + 3 == get_endpoint_valence( at[next_at].el_number ) && + (!type_N || bIsOxide( at, next_at )) ) { + ; /* + * found: + * + * \ / \ / \ / + * \ / \ / \ / + * N==C or N==C or N==N + * // \ // \ // \ + * O ^ \ N ^ \ O ^ \ + * | | | + * | | | + * at[next_at] at[next_at] at[next_at] + */ +#endif + } else { + bFound = 0; + } + } + } + if ( bFound ) { + num_stereo_bonds++; + } + } + + if ( (num_alt>0) + (num_2s>0) != 1 || !num_stereo_bonds ) + return 0; + if ( num_2s > 1 ) { +#if ( N_V_STEREOBONDS == 1 ) + if ( 2 == num_2s && + 1 == (num_2s_hetero[0] | num_2s_hetero[1]) && + 3 == at[cur_at].valence + at[cur_at].num_H && + 5 == at[cur_at].chem_bonds_valence + at[cur_at].num_H && + 3 == get_endpoint_valence( at[cur_at].el_number ) ) { + ; + } else { + return 0; + } +#else + return 0; +#endif + } + + return num_stereo_bonds; +} + + +/*************************************************************/ +int half_stereo_bond_action( int nParity, int bUnknown, int bIsotopic, int vABParityUnknown ) +{ +#define AB_NEGATIVE 0x10 +#define AB_UNKNOWN 0x20 + int nAction; + + if ( nParity == AB_PARITY_NONE ) + return AB_PARITY_NONE; + + /* Unknown (type 1) in the parity value may come from the 'Either' single bond only */ + /* Treat it as a known single bond geometry and unknown (Either) double bond */ + if ( nParity == vABParityUnknown /*AB_PARITY_UNKN*/ ) + nParity = AB_PARITY_ODD | AB_UNKNOWN; + if ( nParity == -vABParityUnknown /*AB_PARITY_UNKN*/ ) + nParity = AB_PARITY_ODD | AB_UNKNOWN | AB_NEGATIVE; + + /* make positive, replace AB_PARITY_EVEN with AB_PARITY_ODD */ + if ( nParity < 0 ) + nParity = ((nParity == -AB_PARITY_EVEN)? AB_PARITY_ODD : (-nParity)) | AB_NEGATIVE; + else + if (nParity == AB_PARITY_EVEN) + nParity = AB_PARITY_ODD; + + /* Unknown (type 2): was detected in the double bond attribute */ + /* (this 'unknown' came from 'Either' double bond) */ + /* Treat both unknowns in the same way */ + if ( bUnknown ) + nParity |= AB_UNKNOWN; + + if ( bIsotopic ) { + switch ( nParity ) { + case AB_PARITY_ODD: + case AB_PARITY_ODD | AB_NEGATIVE: + nAction = AB_PARITY_CALC; + break; + case AB_PARITY_ODD | AB_UNKNOWN: + case AB_PARITY_UNDF | AB_UNKNOWN: + case AB_PARITY_ODD | AB_UNKNOWN | AB_NEGATIVE: + case AB_PARITY_UNDF | AB_UNKNOWN | AB_NEGATIVE: + nAction = vABParityUnknown /*AB_PARITY_UNKN*/; + break; + case AB_PARITY_IISO: + case AB_PARITY_IISO | AB_UNKNOWN: + nAction = AB_PARITY_NONE; + break; + case AB_PARITY_UNDF: + case AB_PARITY_UNDF | AB_NEGATIVE: + nAction = AB_PARITY_UNDF; + break; + default: + nAction = -1; /* program error */ + } + } else { + /* Non-isotopic */ + switch ( nParity ) { + case AB_PARITY_ODD: + nAction = AB_PARITY_CALC; + break; + case AB_PARITY_ODD | AB_UNKNOWN: + case AB_PARITY_UNDF | AB_UNKNOWN: + nAction = vABParityUnknown /*AB_PARITY_UNKN*/; + break; + /* case AB_PARITY_ODD | AB_UNKNOWN | AB_NEGATIVE: */ + case AB_PARITY_UNDF: + nAction = AB_PARITY_UNDF; + break; + case AB_PARITY_ODD | AB_UNKNOWN | AB_NEGATIVE: + case AB_PARITY_ODD | AB_NEGATIVE: + case AB_PARITY_IISO: + case AB_PARITY_IISO | AB_UNKNOWN: + case AB_PARITY_UNDF | AB_NEGATIVE: + case AB_PARITY_UNDF | AB_UNKNOWN | AB_NEGATIVE: + nAction = AB_PARITY_NONE; + break; + default: + nAction = -1; /* program error */ + } + } + return nAction; +#undef AB_NEGATIVE +#undef AB_UNKNOWN +} + + +/*************************************************************/ +int set_stereo_bonds_parity( sp_ATOM *out_at, inp_ATOM *at, int at_1, inp_ATOM *at_removed_H, int num_removed_H, + INCHI_MODE nMode, QUEUE *q, AT_RANK *nAtomLevel, S_CHAR *cSource, + AT_RANK min_sb_ring_size, int bPointedEdgeStereo, int vABParityUnknown ) +{ + int j, k, next_at_1, i_next_at_1, i_next_at_2, at_2, next_at_2, num_stereo_bonds, bFound, bAllene; + int bond_type, num_2s_1, num_alt_1; + int num_2s_2, num_alt_2; +#if ( ONE_BAD_SB_NEIGHBOR == 1 ) + int num_wrong_bonds_1, num_wrong_bonds_2; +#endif +#if ( N_V_STEREOBONDS == 1 ) + int n2sh, num_2s_hetero[2], num_2s_hetero_next[2], next_next_at, type_N, type_N_next; +#endif + int num_stored_stereo_bonds, num_stored_isotopic_stereo_bonds; + int chain_length, num_chains, cur_chain_length; + int all_at_2[MAX_NUM_STEREO_BONDS]; + int all_pos_1[MAX_NUM_STEREO_BONDS], all_pos_2[MAX_NUM_STEREO_BONDS]; + S_CHAR all_unkn[MAX_NUM_STEREO_BONDS]; + int /*at_1_parity, at_2_parity,*/ nUnknown, stop=0; + + /* at_1_parity = AB_PARITY_NONE; */ /* do not know */ + /* check valence */ + if ( MAX_NUM_STEREO_BOND_NEIGH < at[at_1].valence+at[at_1].num_H || + MIN_NUM_STEREO_BOND_NEIGH > at[at_1].valence+at[at_1].num_H ) + return 0; + if ( !bCanAtomHaveAStereoBond( at[at_1].elname, at[at_1].charge, at[at_1].radical ) ) + return 0; + if ( at[at_1].c_point ) + return 0; /* rejects atoms that can lose or gain a (positive) charge. 01-24-2003 */ + + /* middle cumulene atoms, for example, =C=, should be ignored here */ + /* only atoms at the ends of cumulene chains are considered. */ + if ( !at[at_1].num_H && 2 == at[at_1].valence && + BOND_DOUBLE == get_allowed_stereo_bond_type( (int)at[at_1].bond_type[0] ) && + BOND_DOUBLE == get_allowed_stereo_bond_type( (int)at[at_1].bond_type[1] ) ) { + return 0; + } + + /* count bonds and find the second atom on the stereo bond */ + num_2s_1 = num_alt_1 = 0; + chain_length = 0; + num_chains = 0; +#if ( ONE_BAD_SB_NEIGHBOR == 1 ) + num_wrong_bonds_1 = 0; +#endif +#if ( N_V_STEREOBONDS == 1 ) + num_2s_hetero[0] = num_2s_hetero[1] = type_N = 0; + if ( 0 == at[at_1].num_H && 0 == at[at_1].charge && 0 == at[at_1].radical && + 3 == get_endpoint_valence( at[at_1].el_number ) ) { + if ( 2 == at[at_1].valence && 3 == at[at_1].chem_bonds_valence ) { + type_N = 1; + } else + if ( 3 == at[at_1].valence && 5 == at[at_1].chem_bonds_valence ) { + type_N = 2; /* unfortunately includes >N# */ + } + } +#endif + for ( i_next_at_1 = 0, num_stereo_bonds = 0; i_next_at_1 < at[at_1].valence; i_next_at_1 ++ ) { + nUnknown = (at[at_1].bond_stereo[i_next_at_1] == STEREO_DBLE_EITHER); + bond_type = get_allowed_stereo_bond_type( (int)at[at_1].bond_type[i_next_at_1] ); + at_2 = -1; /* not found */ + if ( bond_type == BOND_ALTERN || + bond_type == BOND_DOUBLE ) { + next_at_1 = at_2 = at[at_1].neighbor[i_next_at_1]; + next_at_2 = at_1; + } + switch ( bond_type ) { + case BOND_ALTERN: + num_alt_1 ++; +#if ( FIND_RING_SYSTEMS == 1 ) + if ( at[at_1].nRingSystem != at[at_2].nRingSystem ) + continue; /* reject alt. bond connecting different ring systems */ +#endif + if ( (nMode & CMODE_NO_ALT_SBONDS) || + !bCanAtomHaveAStereoBond( at[at_2].elname, at[at_2].charge, at[at_2].radical ) ) { + continue; /* reject non-stereogenic bond to neighbor ord. #i_next_at_1 */ + } + break; + case BOND_DOUBLE: + /* check for cumulene/allene */ + num_2s_1++; + cur_chain_length = 0; + if ( bCanAtomBeTerminalAllene( at[at_1].elname, at[at_1].charge, at[at_1].radical ) ) { + /* + * Example of cumulene + * chain length = 2: >X=C=C=Y< + * | | | | + * 1st cumulene atom= at_1 | | at_2 =last cumlene chain atom + * next to at_1= next_at_1 next_at_2 =previous to at_2 + * + * chain length odd: stereocenter on the middle atom ( 1=> allene ) + * chain length even: "long stereogenic bond" + */ + while ((bAllene = + !at[at_2].num_H && at[at_2].valence == 2 && + BOND_DOUBLE == get_allowed_stereo_bond_type( (int)at[at_2].bond_type[0] ) && + BOND_DOUBLE == get_allowed_stereo_bond_type( (int)at[at_2].bond_type[1] )) && + bCanAtomBeMiddleAllene( at[at_2].elname, at[at_2].charge, at[at_2].radical ) ) { + k = ((int)at[at_2].neighbor[0]==next_at_2); /* opposite neighbor position */ + next_at_2 = at_2; + nUnknown += (at[at_2].bond_stereo[k] == STEREO_DBLE_EITHER); + at_2 = (int)at[at_2].neighbor[k]; + cur_chain_length ++; /* count =C= atoms */ + } + if ( cur_chain_length ) { + num_chains ++; + if ( bAllene /* at the end of the chain atom Y is =Y=, not =Y< or =Y- */ || + !bCanAtomBeTerminalAllene( at[at_2].elname, at[at_2].charge, at[at_2].radical ) ) { + cur_chain_length = 0; + continue; /* ignore: does not fit cumulene description; go to check next at_1 neighbor */ + } + chain_length = cur_chain_length; /* accept a stereogenic cumulele */ + } + } +#if ( N_V_STEREOBONDS == 1 ) + if ( !cur_chain_length && + 0 <= (n2sh = bIsSuitableHeteroInpAtom( at + at_2 )) ) { + num_2s_hetero[n2sh] ++; /* n2sh=0 -> =N- or =NH; n2sh=1 -> =O */ + } +#endif + if ( !cur_chain_length && + !bCanAtomHaveAStereoBond( at[at_2].elname, at[at_2].charge, at[at_2].radical ) ) { + continue; /* reject non-stereogenic bond to neighbor #i_next_at_1 */ + } + + break; + + case BOND_SINGLE: + case BOND_TAUTOM: + continue; /* reject non-stereogenic bond to neighbor #i_next_at_1 */ + default: +#if ( ONE_BAD_SB_NEIGHBOR == 1 ) + num_wrong_bonds_1 ++; + continue; +#else + return 0; /* wrong bond type; */ +#endif + } + + /* check atom at the opposite end of possibly stereogenic bond */ + + bFound = (at_2 >= 0 && at_1 > at_2 ); /* i_next_at_1 = at_1 stereogenic bond neighbor attachment number */ + + if ( bFound ) { + /* check "at_2" atom on the opposite side of the bond or cumulene chain */ + if ( MAX_NUM_STEREO_BOND_NEIGH < at[at_2].valence+at[at_2].num_H || + MIN_NUM_STEREO_BOND_NEIGH > at[at_2].valence+at[at_2].num_H ) + continue; + + /* check at_2 neighbors and bonds */ + num_2s_2 = num_alt_2 = 0; +#if ( N_V_STEREOBONDS == 1 ) + num_2s_hetero_next[0] = num_2s_hetero_next[1] = type_N_next = 0; + if ( 0 == at[at_2].num_H && 0 == at[at_2].charge && 0 == at[at_2].radical && + 3 == get_endpoint_valence( at[at_2].el_number ) ) { + if ( 2 == at[at_2].valence && 3 == at[at_2].chem_bonds_valence ) { + type_N_next = 1; /* -N= */ + } else + if ( 3 == at[at_2].valence && 5 == at[at_2].chem_bonds_valence ) { + type_N_next = 2; /* unfortunately includes >N# */ + } + } +#endif + i_next_at_2 = -1; /* unassigned mark */ +#if ( ONE_BAD_SB_NEIGHBOR == 1 ) + num_wrong_bonds_2 = 0; +#endif + for ( j = 0; j < at[at_2].valence; j ++ ) { + bond_type = get_allowed_stereo_bond_type( (int)at[at_2].bond_type[j] ); + if ( !bond_type ) { +#if ( ONE_BAD_SB_NEIGHBOR == 1 ) + num_wrong_bonds_2 ++; + continue; /* this bond type is not allowed to be adjacent to a stereo bond */ +#else + break; +#endif + } + if ( bond_type == BOND_DOUBLE ) { + num_2s_2 ++; +#if ( N_V_STEREOBONDS == 1 ) + next_next_at = at[at_2].neighbor[j]; + if ( 0 <= (n2sh = bIsSuitableHeteroInpAtom( at + next_next_at )) ) { + num_2s_hetero_next[n2sh] ++; /* n2sh=0 -> =N- or =NH; n2sh=1 -> =O */ + } +#endif + } else { + num_alt_2 += ( bond_type == BOND_ALTERN ); + } + if ( (int)at[at_2].neighbor[j] == next_at_2 ) + i_next_at_2 = j; /* assigned */ + } + if ( +#if ( ONE_BAD_SB_NEIGHBOR == 1 ) + num_wrong_bonds_2 > 1 || num_wrong_bonds_2 && 2 >= at[at_2].valence || +#else + j < at[at_2].valence /* "next" has a wrong bond type*/ || +#endif + (num_alt_2>0) + (num_2s_2>0) != 1 || /* all double XOR all alt bonds only */ + /* num_2s_2 > 1 ||*/ /* only one double bond permitted */ + i_next_at_2 < 0 /* atom next to the opposite atom not found */ ) { + bFound = 0; + } else + if ( at[at_2].c_point ) { + bFound = 0; /* rejects atoms that can lose or gain a (positive) charge. 01-24-2003 */ + } else + if ( num_2s_2 > 2 ) { + bFound = 0; + } else +#if ( N_V_STEREOBONDS == 1 ) + if ( 3 == (type_N | type_N_next) && + ( 2 == type_N && !bIsOxide( at, at_1 ) || + 2 == type_N_next && !bIsOxide( at, at_2 ) ) ) { + bFound = 0; + } else +#endif + if ( 2 == num_2s_2 ) { +#if ( N_V_STEREOBONDS == 1 ) + if ( !chain_length && + 1 == (num_2s_hetero_next[0] | num_2s_hetero_next[1]) && + 3 == at[at_2].valence + at[at_2].num_H && + 5 == at[at_2].chem_bonds_valence + at[at_2].num_H && + 3 == get_endpoint_valence( at[at_2].el_number ) && + (!type_N || bIsOxide( at, at_2 )) ) { + /* + * found: + * + * \ / \ / \ / + * \ / \ / \ / + * N==C or N==C or N==N + * // \ // \ // \ + * O ^ \ N ^ \ O ^ \ + * | | | + * | | | + * at[at_2] at[at_2] at[at_2] + */ + ; + } else { + bFound = 0; + } +#else + bFound = 0; +#endif + } + + + if ( chain_length && num_alt_2 ) + return 0; /* allow no alt bonds in cumulenes */ + } + if ( bFound ) { + all_pos_1[num_stereo_bonds] = i_next_at_1; /* neighbor to at_1 position */ + all_pos_2[num_stereo_bonds] = i_next_at_2; /* neighbor to at_2 position */ + all_at_2[num_stereo_bonds] = at_2; /* at_2 */ + all_unkn[num_stereo_bonds] = nUnknown; /* stereogenic bond has Unknown configuration */ + /* + if ( (at[at_1].bUsed0DParity & 2) || (at[at_2].bUsed0DParity & 2) ) { + for ( k = 0; k < MAX_NUM_STEREO_BONDS && at[at_1].sb_parity[k]; k ++ ) { + if ( at[at_1].sb_neigh[k] == i_next_at_1 ) { + if ( at[at_1].sb_parity[k] == AB_PARITY_UNKN && !nUnknown ) { + all_unkn[num_stereo_bonds] = 1; + } + break; + } + } + } + */ + num_stereo_bonds ++; + } + } + if ( num_chains > 1 ) { + return 0; /* cannot be more than 1 cumulene chain. */ + } +#if ( ONE_BAD_SB_NEIGHBOR == 1 ) + if ( num_wrong_bonds_1 > 1 || num_wrong_bonds_1 && 2 >= at[at_1].valence ) { + return 0; /* wrong bond type */ + } +#endif + /* accept only short chains for now */ + /* chain_length=1: >C=C=C< tetrahedral center, allene */ + /* chain_length=2: >C=C=C=C< stereogenic bond, cumulene */ + if ( chain_length && (num_stereo_bonds != 1 || num_alt_1 || chain_length > MAX_CUMULENE_LEN) ) { + return 0; + } + + /* we need 1 double bond/chain XOR up to 3 arom. bonds */ + /* to have a stereogenic bond */ + if ( (num_alt_1>0) + (num_2s_1>0) != 1 || !num_stereo_bonds /*|| num_2s_1 > 1*/ ) + return 0; + + if ( num_2s_1 > 1 ) { +#if ( N_V_STEREOBONDS == 1 ) + if ( 2 == num_2s_1 && + 2 == type_N && + 1 == (num_2s_hetero[0] | num_2s_hetero[1]) && + 3 == at[at_1].valence + at[at_1].num_H && + 5 == at[at_1].chem_bonds_valence + at[at_1].num_H && + 3 == get_endpoint_valence( at[at_1].el_number ) ) { + ; + } else { + return 0; + } +#else + return 0; +#endif + } + + /* ================== calculate parities ====================== */ + + + /* find possibly stereo bonds and save them */ + num_stored_isotopic_stereo_bonds = 0; + num_stored_stereo_bonds = 0; + for ( k = 0; k < num_stereo_bonds; k ++ ) { + + int cur_parity, next_parity, abs_cur_parity, abs_next_parity, dot_prod_z; + S_CHAR z_dir1[3], z_dir2[3]; /* 3D vectors for half stereo bond parity direction */ + int chain_len_bits = MAKE_BITS_CUMULENE_LEN(chain_length); + int cur_parity_defined, next_parity_defined; + int cur_action, next_action, result_action; + + at_2 = all_at_2[k]; + i_next_at_1 = all_pos_1[k]; + +#if ( MIN_SB_RING_SIZE > 0 ) + if( at[at_1].nRingSystem == at[at_2].nRingSystem ) { + /* check min. ring size only if both double bond/cumulene */ + /* ending atoms belong to the same ring system */ + j = is_bond_in_Nmax_memb_ring( at, at_1, i_next_at_1, q, nAtomLevel, cSource, min_sb_ring_size ); + if ( j > 0 ) { + continue; + } else + if ( j < 0 ) { + return CT_STEREOBOND_ERROR; + } + } +#endif + + i_next_at_2 = all_pos_2[k]; + nUnknown = all_unkn[k]; + memset(z_dir1, 0, sizeof(z_dir1)); + memset(z_dir2, 0, sizeof(z_dir2)); + + /******************************************************************************** + * find atom parities (negative means parity due to H-isotopes only) + * and half stereo bond parity directions z_dir1, z_dir2. + * + * Bond can have unknown or undefined parity or no parity because of: + * 1. Geometry (poorly defined, cannot calculate, for example linear =C-F + * or =CHD with no geometry) -- Undefined parity + * H + * 2. Identical H atoms (no parity in principle, for example =C< ) + * -- No parity H + * + * 3. The user said double bond stereo is unknown + * or at least one of single bonds is in unknown direction + * -- Unknown parity + * + * These 3 cases (see above) are referred below as 1, 2, 3. + * Each of the cases may be present or not (2 possibilities) + * Total number of combination is 2*2*2=8 + * + * Since a case when all 3 are not present is a well-defined parity, + * we do not consider this case here. Then 2*2*2-1=7 cases are left. + * + * If several cases are present, list them below separated by "+". + * For example, 1+2 means (1) undefined geometry and (2) no parity + * is possible because of identical H atoms. + * + * N) Decision table, Non-isotopic, 2*2*2-1=7 cases: + * ================================================= + * none : 2+any: 1+2(e.g.=CH2); 1+2+3; 2; 2+3 AB_PARITY_NONE=0 + * undefined: 1 AB_PARITY_UNDF + * unknown : 1+3; 3 AB_PARITY_UNKN + * + * I) Decision table, Isotopic, 2*2*2-1=7 cases: + * ============================================= + * none : none + * undefined: 1; 1+2; 1+2+3; 2; 2+3 + * unknown : 1+3; 3 + * + * Note: When defining identical atoms H atoms in case 2, + * Isotopic and Non-isotopic cases are different: + * N: do NOT take into account the isotopic composition of H atoms + * I: DO take into account the isotopic composition of H atoms + * (it is assumed that H isotopes are always different) + * + * half_stereo_bond_parity() returns: + * ================================== + * Note: half_stereo_bond_parity() is unaware of case 3. + * + * can't be a half of a stereo bond AB_PARITY_NONE + * 1, isotopic & non-isotopic: AB_PARITY_UNDF + * 1, isotopic only -AB_PARITY_UNDF + * 2, no parity: identical H isotopes AB_PARITY_IISO + * 3, 'Either' single bond(s) AB_PARITY_UNKN ??? + * 3, 'Either' single bond(s), iso H -AB_PARITY_UNKN ??? + * defined parity AB_PARITY_ODD, AB_PARITY_EVEN + * defined parity for isotopic only: -AB_PARITY_ODD, -AB_PARITY_EVEN + * + * Resultant value for the stereo bond parity + * ---+-------------------+-------+--------+----------------+ + * 3? | half_stereo_bond_ | N or I| case 1,| bond parity | + * | parity()= | | 2 or 3 | | + * ---+-------------------+-------+--------+----------------+ + * ( AB_PARITY_ODD/EVEN) => N&I: - => AB_PARITY_CALC (=6, calc.later) + * 3+( AB_PARITY_ODD/EVEN) => N&I: 3 => AB_PARITY_UNKN (=3) + * (-AB_PARITY_ODD/EVEN) => N: 2 => AB_PARITY_NONE (=0) + * (-AB_PARITY_ODD/EVEN) => I: - => AB_PARITY_CALC + * 3+(-AB_PARITY_ODD/EVEN) => N: 2+3 => AB_PARITY_UNDF (=4) + * 3+(-AB_PARITY_ODD/EVEN) => I: 3 => AB_PARITY_UNKN + * ( AB_PARITY_IISO ) => N: 1+2, 2 => AB_PARITY_NONE (=0) + * ( AB_PARITY_IISO ) => I: 1+2, 2 => AB_PARITY_UNDF + * 3+( AB_PARITY_IISO ) => N: 1+2+3,2+3=> AB_PARITY_NONE + * 3+( AB_PARITY_IISO ) => I: 1+2+3,2+3=> AB_PARITY_UNDF + * ( AB_PARITY_UNDF ) => N&I: 1 => AB_PARITY_UNDF + * 3+( AB_PARITY_UNDF ) => N&I: 1+3 => AB_PARITY_UNKN + * (-AB_PARITY_UNDF ) => N: 1+2 => AB_PARITY_NONE + * (-AB_PARITY_UNDF ) => I: 1 => AB_PARITY_UNDF + * 3+(-AB_PARITY_UNDF ) => N: 1+2+3 => AB_PARITY_NONE + * 3+(-AB_PARITY_UNDF ) => I: 1+3 => AB_PARITY_UNKN + * ---+-------------------+-------+--------+----------------+ + + * If bond parity is undefined because abs(dot_prod_z) < MIN_DOT_PROD + * then replace: AB_PARITY_CALC + * with: AB_PARITY_UNDF + * Joining two half_bond_parity() results: + * + * + * atom1 \ atom2 | AB_PARITY_NONE AB_PARITY_UNKN AB_PARITY_UNDF AB_PARITY_CALC + * ----------------+--------------------------------------------------------------- + *0=AB_PARITY_NONE | AB_PARITY_NONE AB_PARITY_NONE AB_PARITY_NONE AB_PARITY_NONE + *3=AB_PARITY_UNKN | AB_PARITY_UNKN AB_PARITY_UNKN AB_PARITY_UNKN + *4=AB_PARITY_UNDF | AB_PARITY_UNDF AB_PARITY_UNDF + *6=AB_PARITY_CALC | AB_PARITY_CALC + * + * that is, take min out of the two + *********************************************************************************/ + + cur_parity = half_stereo_bond_parity( at, at_1, at_removed_H, num_removed_H, + z_dir1, bPointedEdgeStereo, vABParityUnknown ); + next_parity = half_stereo_bond_parity( at, at_2, at_removed_H, num_removed_H, + z_dir2, bPointedEdgeStereo, vABParityUnknown ); + + if ( RETURNED_ERROR(cur_parity) || RETURNED_ERROR(next_parity) ) { + return CT_CALC_STEREO_ERR; + } + if ( (at[at_1].bUsed0DParity & FlagSB_0D) || (at[at_1].bUsed0DParity & FlagSB_0D) ) { + FixSb0DParities( at, /* at_removed_H, num_removed_H,*/ chain_length, + at_1, i_next_at_1, z_dir1, + at_2, i_next_at_2, z_dir2, &cur_parity, &next_parity ); + } + + if ( cur_parity == AB_PARITY_NONE || abs(cur_parity) == AB_PARITY_IISO ) { + continue; + } + if ( next_parity == AB_PARITY_NONE || abs(next_parity) == AB_PARITY_IISO ) { + continue; + } + + cur_action = half_stereo_bond_action( cur_parity, nUnknown, 0, vABParityUnknown ); /* -1 => program error */ + next_action = half_stereo_bond_action( next_parity, nUnknown, 0, vABParityUnknown ); + result_action = inchi_min(cur_action, next_action); + + if ( result_action == -1 ) { + stop = 1; /* program error */ + } + + abs_cur_parity = abs( cur_parity ); + abs_next_parity = abs( next_parity ); + cur_parity_defined = ATOM_PARITY_WELL_DEF(abs_cur_parity); + next_parity_defined = ATOM_PARITY_WELL_DEF(abs_next_parity); + + + if ( cur_parity_defined && next_parity_defined ) { + /* find how the whole bond parity depend on geometry */ + /* if dot_prod_z < 0 then bond_parity := 3-bond_parity */ + /* can be done only for a well-defined geometry */ + /* + dot_prod_z = (chain_len_bits & BIT_CUMULENE_CHI)? + triple_prod_char( at, at_1, i_next_at_1, z_dir1, at_2, i_next_at_2, z_dir2 ) : + dot_prodchar3(z_dir1, z_dir2); + */ + dot_prod_z = (chain_len_bits && BOND_CHAIN_LEN(chain_len_bits)%2)? + triple_prod_char( at, at_1, i_next_at_1, z_dir1, at_2, i_next_at_2, z_dir2 ) : + dot_prodchar3(z_dir1, z_dir2); + + if ( abs(dot_prod_z) < MIN_DOT_PROD ) { + /* The geometry is not well-defined. Eliminate AB_PARITY_CALC */ + result_action = inchi_min( result_action, AB_PARITY_UNDF ); + } + } else { + dot_prod_z = 0; + } + + if ( result_action != AB_PARITY_NONE && result_action != -1 ) { + /* stereo, no isotopes (only positive) */ + if ( cur_parity > 0 && next_parity > 0 ) { + if ( save_a_stereo_bond( dot_prod_z, result_action | chain_len_bits, + at_1, i_next_at_1, out_at[at_1].stereo_bond_neighbor, + out_at[at_1].stereo_bond_ord, out_at[at_1].stereo_bond_z_prod, + out_at[at_1].stereo_bond_parity, + at_2, i_next_at_2, out_at[at_2].stereo_bond_neighbor, + out_at[at_2].stereo_bond_ord, out_at[at_2].stereo_bond_z_prod, + out_at[at_2].stereo_bond_parity) ) { + if ( !out_at[at_1].parity || + cur_parity_defined && !ATOM_PARITY_WELL_DEF(abs(out_at[at_1].parity)) ) { + out_at[at_1].parity = cur_parity; + memcpy( out_at[at_1].z_dir, z_dir1, sizeof(out_at[0].z_dir) ); + } + if ( !out_at[at_2].parity || + next_parity_defined && !ATOM_PARITY_WELL_DEF(abs(out_at[at_2].parity)) ) { + out_at[at_2].parity = next_parity; + memcpy( out_at[at_2].z_dir, z_dir2, sizeof(out_at[0].z_dir) ); + } + out_at[at_1].bAmbiguousStereo |= at[at_1].bAmbiguousStereo; + out_at[at_2].bAmbiguousStereo |= at[at_2].bAmbiguousStereo; + num_stored_stereo_bonds ++; + } + } + } + + /* stereo + isotopic (all non-zero) */ + cur_action = half_stereo_bond_action( cur_parity, nUnknown, 1, vABParityUnknown ); /* -1 => program error */ + next_action = half_stereo_bond_action( next_parity, nUnknown, 1, vABParityUnknown ); + result_action = inchi_min(cur_action, next_action); + cur_parity = abs_cur_parity; + next_parity = abs_next_parity; + if ( result_action != AB_PARITY_NONE && result_action != -1 ) { + /* stero, isotopic */ + if ( cur_parity > 0 && next_parity > 0 ) { + if( save_a_stereo_bond( dot_prod_z, result_action | chain_len_bits, + at_1, i_next_at_1, out_at[at_1].stereo_bond_neighbor2, + out_at[at_1].stereo_bond_ord2, out_at[at_1].stereo_bond_z_prod2, + out_at[at_1].stereo_bond_parity2, + at_2, i_next_at_2, out_at[at_2].stereo_bond_neighbor2, + out_at[at_2].stereo_bond_ord2, out_at[at_2].stereo_bond_z_prod2, + out_at[at_2].stereo_bond_parity2) ) { + if ( !out_at[at_1].parity2 || + cur_parity_defined && !ATOM_PARITY_WELL_DEF(abs(out_at[at_1].parity2)) ) { + out_at[at_1].parity2 = cur_parity /*| chain_len_bits*/; + if ( !out_at[at_1].parity ) { + memcpy( out_at[at_1].z_dir, z_dir1, sizeof(out_at[0].z_dir) ); + } + } + if ( !out_at[at_2].parity2 || /* next line changed from abs(out_at[at_2].parity) 2006-03-05 */ + next_parity_defined && !ATOM_PARITY_WELL_DEF(abs(out_at[at_2].parity2)) ) { + out_at[at_2].parity2 = next_parity /*| chain_len_bits*/; + if ( !out_at[at_2].parity ) { + memcpy( out_at[at_2].z_dir, z_dir2, sizeof(out_at[0].z_dir) ); + } + } + out_at[at_1].bAmbiguousStereo |= at[at_1].bAmbiguousStereo; + out_at[at_2].bAmbiguousStereo |= at[at_2].bAmbiguousStereo; + num_stored_isotopic_stereo_bonds ++; + } + } + } else + if ( result_action == -1 ) { + stop = 1; /* program error? */ + } + } + if ( stop ) { + return CT_CALC_STEREO_ERR; + } + return /*num_stored_stereo_bonds+*/ num_stored_isotopic_stereo_bonds; +} + + +/*********************************************************************/ +/* if isotopic H, D, T added, can the atom be a stereo center? */ +#if ( NEW_STEREOCENTER_CHECK == 1 ) +/* int bCanInpAtomBeAStereoCenter( inp_ATOM *at, int cur_at ) */ +int can_be_a_stereo_atom_with_isotopic_H( inp_ATOM *at, int cur_at, int bPointedEdgeStereo ) +{ + int nNumNeigh; + if ( (nNumNeigh = bCanInpAtomBeAStereoCenter( at, cur_at, bPointedEdgeStereo )) && + at[cur_at].valence + at[cur_at].num_H == nNumNeigh && + at[cur_at].num_H <= NUM_H_ISOTOPES + ) { + return 1; + } + return 0; +} +#else +int can_be_a_stereo_atom_with_isotopic_H( inp_ATOM *at, int cur_at ) +{ + int j, ret = 0; + if ( bCanAtomBeAStereoCenter( at[cur_at].elname, at[cur_at].charge, at[cur_at].radical ) && + at[cur_at].valence + at[cur_at].num_H == MAX_NUM_STEREO_ATOM_NEIGH && + at[cur_at].num_H < MAX_NUM_STEREO_ATOM_NEIGH + ) { + + for ( j = 0, ret=1; ret && j < at[cur_at].valence; j ++ ) { + if ( (at[cur_at].bond_type[j] & ~BOND_MARK_ALL) != BOND_SINGLE ) { + ret = 0; + } + } + } + return ret; +} +#endif + + +/***************************************************************/ +int GetStereocenter0DParity( CANON_GLOBALS *pCG, + inp_ATOM *at, int cur_at, int j1, + AT_NUMB nSbNeighOrigAtNumb[], int nFlag ) +{ + int parity = AB_PARITY_NONE; + if ( at[cur_at].p_parity && (j1 == MAX_NUM_STEREO_ATOM_NEIGH-1 || j1 == MAX_NUM_STEREO_ATOM_NEIGH) ) { + int i, num_trans_inp, num_trans_neigh; + AT_NUMB nInpNeighOrigAtNumb[MAX_NUM_STEREO_ATOM_NEIGH]; + for ( i = 0; i < MAX_NUM_STEREO_ATOM_NEIGH; i ++ ) { + nInpNeighOrigAtNumb[i] = at[cur_at].p_orig_at_num[i]; + if ( nInpNeighOrigAtNumb[i] == at[cur_at].orig_at_number ) { + nInpNeighOrigAtNumb[i] = 0; /* lone pair or explicit H */ + } + } + + num_trans_inp = insertions_sort( pCG, nInpNeighOrigAtNumb, MAX_NUM_STEREO_ATOM_NEIGH, sizeof(nInpNeighOrigAtNumb[0]), comp_AT_NUMB ); + num_trans_neigh = insertions_sort( pCG, nSbNeighOrigAtNumb, j1, sizeof(nSbNeighOrigAtNumb[0]), comp_AT_NUMB ); + + if ( j1 == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { + ; /*num_trans_neigh += j1;*/ /* the lone pair or implicit H is implicitly at the top of the list */ + } + if ( !memcmp( nInpNeighOrigAtNumb + MAX_NUM_STEREO_ATOM_NEIGH-j1, nSbNeighOrigAtNumb, j1*sizeof(AT_NUMB) ) ) { + if ( ATOM_PARITY_WELL_DEF(at[cur_at].p_parity) ) { + parity = 2 - (num_trans_inp + num_trans_neigh + at[cur_at].p_parity) % 2; + } else { + parity = at[cur_at].p_parity; + } + at[cur_at].bUsed0DParity |= nFlag; /* 0D parity used for streocenter parity */ + } + } + return parity; +} + +/*************************************************************** + * Get stereo atom parity for the current order of attachments + * The result in at[cur_at].parity is valid for previously removed + * explicit hydrogen atoms, including isotopic ones, that are located in at_removed_H[] + * The return value is a calculated parity. + */ + +#define ADD_EXPLICIT_HYDROGEN_NEIGH 1 +#define ADD_EXPLICIT_LONE_PAIR_NEIGH 2 + +int set_stereo_atom_parity( CANON_GLOBALS *pCG, sp_ATOM *out_at, inp_ATOM *at, int cur_at, inp_ATOM *at_removed_H, + int num_removed_H, int bPointedEdgeStereo, int vABParityUnknown) +{ + int j, k, next_at, num_z, j1, nType, num_explicit_H, tot_num_iso_H, nMustHaveNumNeigh; + int num_explicit_iso_H[NUM_H_ISOTOPES+1]; /* numbers of removed hydrogen atoms */ + int index_H[MAX_NUM_STEREO_ATOM_NEIGH]; /* cannot have more than 4 elements: 1 H, 1 D, 1 T atom(s) */ + double z, sum_xyz[3], min_sine, triple_product; + double at_coord[MAX_NUM_STEREO_ATOM_NEIGH][3]; + double bond_len_xy[4], rmax=0.0, rmin=0.0; + double at_coord_center[3]; + int parity, bAmbiguous = 0, bAddExplicitNeighbor = 0, b2D = 0, n2DTetrahedralAmbiguity = 0; + int bIgnoreIsotopicH = (0 != (at[cur_at].cFlags & AT_FLAG_ISO_H_POINT)); + AT_NUMB nSbNeighOrigAtNumb[MAX_NUM_STEREO_ATOM_NEIGH]; + + out_at[cur_at].parity = + out_at[cur_at].parity2 = + out_at[cur_at].stereo_atom_parity = + out_at[cur_at].stereo_atom_parity2 = AB_PARITY_NONE; + parity = AB_PARITY_NONE; + + memset(num_explicit_iso_H, 0, sizeof(num_explicit_iso_H)); + num_explicit_H = 0; + +#if ( NEW_STEREOCENTER_CHECK == 1 ) + if ( !(nMustHaveNumNeigh = bCanInpAtomBeAStereoCenter( at, cur_at, bPointedEdgeStereo ) ) || + at[cur_at].num_H > NUM_H_ISOTOPES + ) { + goto exit_function; + } +#else + nMustHaveNumNeigh = MAX_NUM_STEREO_ATOM_NEIGH; + if ( !bCanAtomBeAStereoCenter( at[cur_at].elname, at[cur_at].charge, at[cur_at].radical ) || + at[cur_at].valence + at[cur_at].num_H != nMustHaveNumNeigh || + at[cur_at].num_H > NUM_H_ISOTOPES + ) { + goto exit_function; + } + for ( j = 0; j < at[cur_at].valence; j ++ ) { + if ( (at[cur_at].bond_type[j] & ~BOND_MARK_ALL) != BOND_SINGLE ) { + goto exit_function; + } + } +#endif + + /* numbers of isotopic H atoms */ + for ( j = 0, tot_num_iso_H = 0; j < NUM_H_ISOTOPES; j ++ ) { + if ( at[cur_at].num_iso_H[j] > 1 ) { + goto exit_function; /* two or more identical hydrogen isotopic neighbors */ + } + tot_num_iso_H += at[cur_at].num_iso_H[j]; + } + if ( bIgnoreIsotopicH ) { + tot_num_iso_H = 0; /* isotopic H considered subject to exchange => ignore isotopic */ + } + /* number of non-isotopic H atoms */ + if ( at[cur_at].num_H - tot_num_iso_H > 1 ) { + goto exit_function; /* two or more identical hydrogen non-isotopic neighbors */ + } + + /* count removed explicit terminal hydrogens attached to at[cur_at]. */ + /* the result is num_explicit_H. */ + /* Removed hydrogens are sorted in increasing isotopic shift order */ + if ( at_removed_H && num_removed_H > 0 ) { + for ( j = 0; j < num_removed_H; j ++ ) { + if ( at_removed_H[j].neighbor[0] == cur_at ) { + k = at_removed_H[j].iso_atw_diff; + /* iso_atw_diff values: H=>0, 1H=>1, D=2H=>2, T=3H=>3 */ + if ( k < 0 || k > NUM_H_ISOTOPES || bIgnoreIsotopicH ) + k = 0; /* treat wrong H isotopes as non-isotopic H */ + num_explicit_iso_H[k] ++; + index_H[num_explicit_H++] = j; + } + } + } + + /* coordinates initialization */ + num_z = 0; + sum_xyz[0] = sum_xyz[1] = sum_xyz[2] = 0.0; + + at_coord_center[0] = + at_coord_center[1] = + at_coord_center[2] = 0.0; + + /* fill out stereo center neighbors coordinates */ + /* and obtain the parity from the geometry */ + + for ( k = 0, j1 = 0; k < 2; k ++ ) { + switch( k ) { + + case 0: + /* add coordinates of removed hydrogens */ + for ( j = 0; j < num_explicit_H; j ++, j1 ++ ) { + next_at = index_H[j]; + /* use bond description located at removed_H atom */ + /* minus sign at get_z_coord: at_removed_H[] contains bonds TO at[cur_at], not FROM it. */ + /* Note: &at[(at_removed_H-at)+ next_at] == &at_removed_H[next_at] */ + z = -get_z_coord( at, (int) (at_removed_H-at)+ next_at, + 0 /*neighbor #*/, + &nType, + -(bPointedEdgeStereo & PES_BIT_POINT_EDGE_STEREO) ); + switch ( nType ) { + case ZTYPE_EITHER: + parity = vABParityUnknown /*AB_PARITY_UNKN*/ ; /* no parity: bond in "Either" direction. */ + goto exit_function; + case ZTYPE_UP: + case ZTYPE_DOWN: + nType = -nType; /* at_removed_H[] contains bonds TO the center, not from */ + b2D ++; + /* no break; here */ + case ZTYPE_3D: + num_z ++; + } + + nSbNeighOrigAtNumb[j1] = at_removed_H[next_at].orig_at_number; + at_coord[j1][0] = at_removed_H[next_at].x-at[cur_at].x; + at_coord[j1][1] = at_removed_H[next_at].y-at[cur_at].y; + bond_len_xy[j1] = len2(at_coord[j1]); + /* bond_len_xy[j1] = sqrt(at_coord[j1][0]*at_coord[j1][0]+at_coord[j1][1]*at_coord[j1][1]); */ + at_coord[j1][2] = (nType==ZTYPE_3D? z : + nType==ZTYPE_UP? bond_len_xy[j1] : + nType==ZTYPE_DOWN? -bond_len_xy[j1] : 0.0 ); + } + break; + case 1: + /* add all coordinates of other neighboring atoms */ + for ( j = 0; j < at[cur_at].valence; j ++, j1 ++ ) { + next_at = at[cur_at].neighbor[j]; + z = get_z_coord( at, cur_at, j, &nType, (bPointedEdgeStereo & PES_BIT_POINT_EDGE_STEREO) ); + switch ( nType ) { + case ZTYPE_EITHER: + parity = vABParityUnknown /*AB_PARITY_UNKN*/; /* unknown parity: bond in "Either" direction. */ + goto exit_function; + case ZTYPE_UP: + case ZTYPE_DOWN: + b2D ++; + case ZTYPE_3D: + num_z ++; + } + + nSbNeighOrigAtNumb[j1] = at[next_at].orig_at_number; + at_coord[j1][0] = at[next_at].x-at[cur_at].x; + at_coord[j1][1] = at[next_at].y-at[cur_at].y; + bond_len_xy[j1] = len2(at_coord[j1]); + /* bond_len_xy[j1] = sqrt(at_coord[j1][0]*at_coord[j1][0]+at_coord[j1][1]*at_coord[j1][1]); */ + at_coord[j1][2] = (nType==ZTYPE_3D? z : + nType==ZTYPE_UP? bond_len_xy[j1] : + nType==ZTYPE_DOWN? -bond_len_xy[j1] : 0.0 ); + } + break; + } + } + /* j1 is the number of explicit neighbors (that is, all neighbors except implicit H) */ + + b2D = (b2D == num_z && num_z); /* 1 => two-dimensional */ + + if ( MAX_NUM_STEREO_ATOM_NEIGH != at[cur_at].valence+num_explicit_H && + MAX_NUM_STEREO_ATOM_NEIGH-1 != at[cur_at].valence+num_explicit_H ) { + /* not enough geometry data to find the central atom parity */ + if ( nMustHaveNumNeigh == at[cur_at].valence+at[cur_at].num_H && + at[cur_at].num_H > 1 ) { + /* only isotopic parity is possible; no non-isotopic parity */ + if ( parity == vABParityUnknown /*AB_PARITY_UNKN*/ ) { + parity = -vABParityUnknown /*AB_PARITY_UNKN*/; /* the user marked the center as "unknown" */ + } else { + parity = -AB_PARITY_UNDF; /* not enough geometry; only isotopic parity is possible */ + } + } else { + parity = AB_PARITY_NONE; /* not a stereocenter at all */ + } + goto exit_function; + } + /* make all vector lengths equal to 1; exit if too short. 9-10-2002 */ + for ( j = 0; j < j1; j ++ ) { + z = len3( at_coord[j] ); + if ( z < MIN_BOND_LEN ) { + /* bond length is too small: use 0D parities */ + if ( AB_PARITY_NONE == (parity = GetStereocenter0DParity( pCG, at, cur_at, + j1, + nSbNeighOrigAtNumb, + FlagSC_0D )) ) + { + parity = AB_PARITY_UNDF; + } + goto exit_function; + } +#if ( STEREO_CENTER_BONDS_NORM == 1 ) + else { + mult3( at_coord[j], 1.0/z, at_coord[j] ); + } +#endif + rmax = j? inchi_max( rmax, z) : z; + rmin = j? inchi_min( rmin, z) : z; + } + if ( rmin / rmax < MIN_SINE ) { + /* bond ratio is too small: use 0D parities */ + if ( AB_PARITY_NONE == (parity = GetStereocenter0DParity( pCG, at, cur_at, j1, + nSbNeighOrigAtNumb, + FlagSC_0D )) ) + { + parity = AB_PARITY_UNDF; + } + goto exit_function; + } + for ( j = 0; j < j1; j ++ ) { + add3( sum_xyz, at_coord[j], sum_xyz ); + } + + + + /* here j1 is a number of neighbors including explicit terminal isotopic H */ + /* num_explicit_iso_H[0] = number of explicit non-isotopic hydrogen atom neighbors */ + j = j1; + /* Add Explicit Neighbor */ + if ( j1 == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { + /* add an explicit neighbor if possible */ + if ( nMustHaveNumNeigh == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { + bAddExplicitNeighbor = ADD_EXPLICIT_LONE_PAIR_NEIGH; + } else + if ( nMustHaveNumNeigh == MAX_NUM_STEREO_ATOM_NEIGH ) { + /* check whether an explicit non-isotopic hydrogen can be added */ + /* to an atom that is a stereogenic atom */ + if ( 1 == at[cur_at].num_H - num_explicit_H && /* the atom has only one one implicit hydrogen */ + 1 == at[cur_at].num_H - tot_num_iso_H ) { /* this hydrogen is non-isotopic */ + bAddExplicitNeighbor = ADD_EXPLICIT_HYDROGEN_NEIGH; + } + } + } + + if ( bAddExplicitNeighbor ) { + /*********************************************************** + * May happen only if (j1 == MAX_NUM_STEREO_ATOM_NEIGH-1) + * 3 neighbors only, no H-neighbors. Create and add coordinates of an implicit H + * or a fake 4th neighbor, that is, a lone pair + */ + if ( parity == vABParityUnknown /*AB_PARITY_UNKN*/ ) { + goto exit_function; /* the user insists the parity is unknown and the isotopic */ + /* composition of the neighbors does not contradict */ + } else + if ( num_z == 0 || are_3_vect_in_one_plane(at_coord, MIN_SINE) ) { + /* "hydrogen down" rule is needed to resolve an ambiguity */ + if ( num_z > 0 ) { + bAmbiguous |= AMBIGUOUS_STEREO; + } +#if ( APPLY_IMPLICIT_H_DOWN_RULE == 1 ) /* { */ + /* Although H should be at the top of the list, add it to the bottom. */ + /* This will be taken care of later by inverting parity 1<->2 */ + at_coord[j][0] = 0.0; + at_coord[j][1] = 0.0; +#if ( STEREO_CENTER_BONDS_NORM == 1 ) + at_coord[j][2] = -1.0; +#else + at_coord[j][2] = -(bond_len_xy[0]+bond_len_xy[1]+bond_len_xy[2])/3.0; +#endif +#else /* } APPLY_IMPLICIT_H_DOWN_RULE { */ +#if (ALWAYS_SET_STEREO_PARITY == 1) + parity = AB_PARITY_EVEN; /* suppose atoms are pre-sorted (testing) */ +#else + /* all 3 bonds are in one plane: try to get 0D parities */ + if ( AB_PARITY_NONE == (parity = GetStereocenter0DParity( pCG, at, cur_at, + j1, + nSbNeighOrigAtNumb, + FlagSC_0D )) ) + { + parity = AB_PARITY_UNDF; + } + /*parity = AB_PARITY_UNDF;*/ /* no parity can be calculated found */ +#endif + goto exit_function; +#endif /* } APPLY_IMPLICIT_H_DOWN_RULE */ + } else { + /* we have enough information to find implicit hydrogen coordinates */ + /* + at_coord[j][0] = -sum_x; + at_coord[j][1] = -sum_y; + at_coord[j][2] = -sum_z; + */ + copy3( sum_xyz, at_coord[j] ); + change_sign3( at_coord[j], at_coord[j] ); + z = len3( at_coord[j] ); +#if ( FIX_STEREO_SCALING_BUG == 1 ) + if ( z > 1.0 ) { + rmax *= z; + } else { + rmin *= z; + } +#else + /* Comparing the original bond lengths to lenghts derived from normalized to 1 */ + /* This bug leads to pronouncing legitimate stereogenic atoms */ + /* connected by 3 bonds "undefined" if in a nicely drawn 2D structure */ + /* bond lengths are about 20 or greater. Reported by Reinhard Dunkel 2005-08-05 */ + if ( bPointedEdgeStereo & PES_BIT_FIX_SP3_BUG ) { + /* coordinate scaling bug fixed here */ + if ( z > 1.0 ) { + rmax *= z; + } else { + rmin *= z; + } + } else { + /* original InChI v.1 bug */ + rmax = inchi_max( rmax, z ); + rmin = inchi_min( rmin, z ); + } +#endif + if ( z < MIN_BOND_LEN || rmin/rmax < MIN_SINE ) { + /* the new 4th bond is too short: try to get 0D parities */ + if ( AB_PARITY_NONE == (parity = GetStereocenter0DParity( pCG, at, cur_at, j1, nSbNeighOrigAtNumb, FlagSC_0D )) ) { + parity = AB_PARITY_UNDF; + } + goto exit_function; + } +#if ( STEREO_CENTER_BOND4_NORM == 1 ) + else { + mult3( at_coord[j], 1.0/z, at_coord[j] ); + } +#endif + } + } else + if ( j1 != MAX_NUM_STEREO_ATOM_NEIGH ) { + if ( parity == vABParityUnknown /*AB_PARITY_UNKN*/ ) { + parity = -AB_PARITY_UNDF; /* isotopic composition of H-neighbors contradicts 'unknown' */ + } + goto exit_function; + } else /* j1 == MAX_NUM_STEREO_ATOM_NEIGH */ + if ( num_z == 0 || are_4at_in_one_plane(at_coord, MIN_SINE) ) { + /* all four neighours in xy plane: undefined geometry. */ + if ( num_z > 0 ) { + bAmbiguous |= AMBIGUOUS_STEREO; + } + if ( parity != vABParityUnknown /*AB_PARITY_UNKN*/ ) { +#if (ALWAYS_SET_STEREO_PARITY == 1) + parity = AB_PARITY_EVEN; /* suppose atoms are pre-sorted (testing) */ +#else + /* all 4 bonds are in one plane: try to get 0D parities */ + if ( AB_PARITY_NONE == (parity = GetStereocenter0DParity( pCG, at, cur_at, j1, nSbNeighOrigAtNumb, FlagSC_0D )) ) { + parity = AB_PARITY_UNDF; + } else + if ( ATOM_PARITY_WELL_DEF( parity ) ) { + bAmbiguous &= ~AMBIGUOUS_STEREO; /* 0D parity has resolved the ambiguity */ + } +#endif + } + goto exit_function; + } + /*********************************************************** + * At this point we have 4 neighboring atoms. + * check for tetrahedral ambiguity in 2D case + */ + if ( b2D ) + { + + n2DTetrahedralAmbiguity = Get2DTetrahedralAmbiguity( + pCG, at_coord, bAddExplicitNeighbor, + (bPointedEdgeStereo & PES_BIT_FIX_SP3_BUG ) ); + + if ( 0 < n2DTetrahedralAmbiguity ) + { + if ( T2D_WARN & n2DTetrahedralAmbiguity ) { + bAmbiguous |= AMBIGUOUS_STEREO; + } + if ( T2D_UNDF & n2DTetrahedralAmbiguity ) { + if ( parity != vABParityUnknown /*AB_PARITY_UNKN*/ ) { +#if (ALWAYS_SET_STEREO_PARITY == 1) + parity = AB_PARITY_EVEN; /* suppose atoms are pre-sorted (testing) */ +#else + parity = AB_PARITY_UNDF; /* no parity */ +#endif + } + goto exit_function; + } + } else + if ( n2DTetrahedralAmbiguity < 0 ) { + bAmbiguous |= AMBIGUOUS_STEREO_ERROR; /* error */ + parity = AB_PARITY_UNDF; + goto exit_function; + } + } + + /************************************************************/ + /* Move coordinates origin to the neighbor #0 */ + for ( j = 1; j < MAX_NUM_STEREO_ATOM_NEIGH; j ++ ) { + diff3(at_coord[j], at_coord[0], at_coord[j]); + } + diff3(at_coord_center, at_coord[0], at_coord_center); + + /* + for ( k = 0; k < 3; k++ ) { + for ( j = 1; j < MAX_NUM_STEREO_ATOM_NEIGH; j ++ ) { + at_coord[j][k] -= at_coord[0][k]; + } + at_coord_center[k] -= at_coord[0][k]; + } + */ + /******************************************************** + * find the central (cur_at) atom's parity + * (orientation of atoms #1-3 when looking from #0) + ********************************************************/ + triple_product = triple_prod_and_min_abs_sine2(&at_coord[1], at_coord_center, bAddExplicitNeighbor, &min_sine, &bAmbiguous); + /* + * check for tetrahedral ambiguity -- leave it out for now + */ + if ( fabs(triple_product) > ZERO_FLOAT && (min_sine > MIN_SINE || fabs(min_sine) > ZERO_FLOAT && (n2DTetrahedralAmbiguity & T2D_OKAY ) ) ) { + /* Even => sorted in correct order, Odd=>transposed */ + parity = triple_product > 0.0? AB_PARITY_EVEN : AB_PARITY_ODD; + /* if ( num_explicit_H && at[cur_at].removed_H_parity % 2 ) */ + /* odd transposition of the removed implicit H */ + /* out_at[cur_at].parity = 3 - out_at[cur_at].parity; */ + + /* moved; see below */ + /* out_at[cur_at].bAmbiguousStereo |= bAmbiguous; */ + /* at[cur_at].bAmbiguousStereo |= bAmbiguous; */ + + /* for 4 attached atoms, moving the implicit H from index=3 to index=0 */ + /* can be done in odd number (3) transpositions: (23)(12)(01), which inverts the parity */ + if ( j1 == MAX_NUM_STEREO_ATOM_NEIGH-1 ) { + parity = 3 - parity; + } + } else { +#if (ALWAYS_SET_STEREO_PARITY == 1) + parity = AT_PARITY_EVEN; /* suppose atoms are pre-sorted (testing) */ +#else + if ( num_z > 0 ) { + bAmbiguous |= AMBIGUOUS_STEREO; + } + parity = AB_PARITY_UNDF; /* no parity: 4 bonds are in one plane. */ +#endif + } +exit_function: + + if ( parity ) { + out_at[cur_at].bAmbiguousStereo |= bAmbiguous; + at[cur_at].bAmbiguousStereo |= bAmbiguous; + } + + + /* non-isotopic parity */ + if ( at[cur_at].num_H > 1 || parity <= 0 ) + ; /* no non-isotopic parity */ + else + out_at[cur_at].parity = parity; + + /* isotopic parity */ + if ( parity == -AB_PARITY_UNDF || parity == -vABParityUnknown /*AB_PARITY_UNKN*/ ) + parity = -parity; + if ( parity < 0 ) + parity = AB_PARITY_NONE; + out_at[cur_at].parity2 = parity; + + + parity = PARITY_VAL(out_at[cur_at].parity); + out_at[cur_at].stereo_atom_parity = ATOM_PARITY_WELL_DEF( parity )? AB_PARITY_CALC : parity; + parity = PARITY_VAL(out_at[cur_at].parity2); + out_at[cur_at].stereo_atom_parity2 = ATOM_PARITY_WELL_DEF( parity )? AB_PARITY_CALC : parity; + /* + out_at[cur_at].parity2 = out_at[cur_at].parity; // save for stereo + isotopic canon. + if ( out_at[cur_at].parity ) { + if ( num_explicit_H > 1 || j1 == MAX_NUM_STEREO_ATOM_NEIGH-1 && num_explicit_H ) { + // X H X + // for example, >C< or >C-D + // Y D Y + // parity exists for stereo + isotopic atoms canonicalization only + out_at[cur_at].parity = 0; + } + } + // returning 0 means this can be an adjacent to a stereogenic bond atom + */ + + return (int)out_at[cur_at].parity2; +} +#undef ADD_EXPLICIT_HYDROGEN_NEIGH +#undef ADD_EXPLICIT_LONE_PAIR_NEIGH + + +/*************************************************************/ +int set_stereo_parity( CANON_GLOBALS *pCG, inp_ATOM* at, sp_ATOM* at_output, int num_at, int num_removed_H, + int *nMaxNumStereoAtoms, int *nMaxNumStereoBonds, INCHI_MODE nMode, + int bPointedEdgeStereo, int vABParityUnknown ) +{ + int num_3D_stereo_atoms=0; + int num_stereo_bonds=0; /* added to fix allene stereo bug reported for FClC=C=CFCl by Burt Leland - 2009-02-05 DT */ + + int i, is_stereo, num_stereo, max_stereo_atoms=0, max_stereo_bonds=0; + QUEUE *q = NULL; + AT_RANK *nAtomLevel = NULL; + S_CHAR *cSource = NULL; + AT_RANK min_sb_ring_size = 0; + + /********************************************************** + * + * Note: this parity reflects only relative positions of + * the atoms-neighbors and their ordering in the + * lists of neighbors. + * + * To obtain the actual parity, the parity of a number + * of neighbors transpositions (to obtain a sorted + * list of numbers assigned to the atoms) should be + * added. + * + **********************************************************/ + + /********************************************************************************* + + An example of parity=1 for stereogenic center, tetrahedral asymmetric atom + + + + (1) + | + | + [C] | + | + (2)------(0) + / + / + / + / + (3) + + + Notation: (n) is a tetrahedral atom neighbor; n is an index of a neighbor in + the central_at->neighbor[] array : neighbor atom number is central_at->neighbor[n]. + + (0)-(1), (0)-(2), (0)-(3) are lines connecting atom [C] neighbors to neighbor (0) + (0), (1) and (2) are in the plane + (0)-(3) is directed from the plain to the viewer + [C] is somewhere between (0), (1), (2), (3) + Since (1)-(2)-(3) are in a clockwise order when looking from (0), parity is 2, or even; + otherwise parity would be 1, or odd. + + ********************************************************************************** + + Examples of a stereogenic bond. + + Notation: [atom number], (index of a neighbor): + [1] and [2] are atoms connected by the stereogenic bond + numbers in () are indexes of neighbors of [1] or [2]. + (12 x 16)z = z-component of [1]-[2] and [1]-[6] cross-product + + atom [1] atom [2] + [8] [4] prod01 = (12 x 16)z < 0 prod01 = (21 x 24)z < 0 + \ / prod02 = (12 x 18)z > 0 prod02 = (21 x 25)z > 0 + (2) (1) 0 transpositions because 0 transpositions because + \ / double bond is in 0 posit. double bond is in 0 position + [1]==(0)(0)==[2] 0 = (prod01 > prod02) 0 = (prod01 > prod02) + / \ + (1) (2) result: parity = 2, even result: parity=2, even + / \ + [6] [5] + + + + atom [1] atom [2] + [8] [5] prod01 = (12 x 18)z > 0 prod01 = (21 x 24)z > 0 + \ / prod02 = (12 x 16)z < 0 prod02 = (21 x 25)z < 0 + (0) (2) 2 transpositions to move 1 transposition to move + \ / at [2] from 2 to 0 pos. at [1] from 1 to 0 position + [1]==(2)(1)==[2] 1 = (prod01 > prod02) 1 = (prod01 > prod02) + / \ + (1) (0) result: parity = (1+2) result: parity=(1+1) + / \ 2-(1+2)%2 = 1, odd 2-(1+1)%2 = 2, even + [6] [4] + + + *********************************************************************************** + Note: atoms' numbers [1], [2], [4],... are not used to calculate parity at this + point. They will be used for each numbering in the canonicalization. + Note: parity=3 for a stereo atom means entered undefined bond direction + parity=4 for an atom means parity cannot be determined from the given geometry + ***********************************************************************************/ + + if ( !at_output || !at ) { + return -1; + } + + /* clear stereo descriptors */ + + for( i = 0; i < num_at; i ++ ) { + at_output[i].parity = 0; + at_output[i].parity2 = 0; + memset(&at_output[i].stereo_bond_neighbor[0], 0, sizeof(at_output[0].stereo_bond_neighbor) ); + memset(&at_output[i].stereo_bond_neighbor2[0], 0, sizeof(at_output[0].stereo_bond_neighbor2) ); + memset(&at_output[i].stereo_bond_ord[0], 0, sizeof(at_output[0].stereo_bond_ord) ); + memset(&at_output[i].stereo_bond_ord2[0], 0, sizeof(at_output[0].stereo_bond_ord2) ); + memset(&at_output[i].stereo_bond_z_prod[0], 0, sizeof(at_output[0].stereo_bond_z_prod) ); + memset(&at_output[i].stereo_bond_z_prod2[0], 0, sizeof(at_output[0].stereo_bond_z_prod2) ); + memset(&at_output[i].stereo_bond_parity[0], 0, sizeof(at_output[0].stereo_bond_parity) ); + memset(&at_output[i].stereo_bond_parity2[0], 0, sizeof(at_output[0].stereo_bond_parity2) ); + } + /* estimate max numbers of stereo atoms and bonds if isotopic H are added */ + if ( nMaxNumStereoAtoms || nMaxNumStereoBonds ) { + for( i = 0, num_stereo = 0; i < num_at; i ++ ) { + int num; + num = can_be_a_stereo_atom_with_isotopic_H( at, i, bPointedEdgeStereo ); + if ( num ) { + max_stereo_atoms += num; + } else + if ( (num = can_be_a_stereo_bond_with_isotopic_H( at, i, nMode ) ) ) { /* accept cumulenes */ + max_stereo_bonds += num; + } + } + if ( nMaxNumStereoAtoms ) + *nMaxNumStereoAtoms = max_stereo_atoms; + if ( nMaxNumStereoBonds ) + *nMaxNumStereoBonds = max_stereo_bonds; + } + /* calculate stereo descriptors */ +#if ( MIN_SB_RING_SIZE > 0 ) + min_sb_ring_size = (AT_RANK)(((nMode & REQ_MODE_MIN_SB_RING_MASK) >> REQ_MODE_MIN_SB_RING_SHFT) & AT_RANK_MASK); + if ( min_sb_ring_size >= 3 ) { + /* create BFS data structure for finding for each stereo bond its min. ring sizes */ + q = QueueCreate( num_at+1, sizeof(qInt) ); + nAtomLevel = (AT_RANK*)inchi_calloc(sizeof(nAtomLevel[0]),num_at); + cSource = (S_CHAR *)inchi_calloc(sizeof(cSource[0]),num_at); + if ( !q || !cSource || !nAtomLevel ) { + num_3D_stereo_atoms = CT_OUT_OF_RAM; + goto exit_function; + } + } else { + min_sb_ring_size = 2; + } +#endif + /* main cycle: set stereo parities */ + for( i = 0, num_stereo = 0; i < num_at; i ++ ) + { + is_stereo = set_stereo_atom_parity( pCG, at_output, at, i, at+num_at, num_removed_H, + bPointedEdgeStereo, vABParityUnknown ) ; + if ( is_stereo ) + { + num_3D_stereo_atoms += ATOM_PARITY_WELL_DEF( is_stereo ); + } + else + { + is_stereo = set_stereo_bonds_parity( at_output, at, i, at+num_at, num_removed_H, nMode, + q, nAtomLevel, cSource, min_sb_ring_size, + bPointedEdgeStereo, vABParityUnknown ); + if ( RETURNED_ERROR( is_stereo ) ) { + num_3D_stereo_atoms = is_stereo; + break; + } + num_stereo_bonds += (is_stereo != 0); /* added to fix bug reported by Burt Leland - 2009-02-05 DT */ + } + num_stereo += (is_stereo != 0); + is_stereo = is_stereo; + } + + /* added to fix bug reported by Burt Leland - 2009-02-05 DT */ + if ( max_stereo_atoms < num_3D_stereo_atoms && nMaxNumStereoAtoms ) + *nMaxNumStereoAtoms = num_3D_stereo_atoms; + if ( max_stereo_bonds < num_stereo_bonds && nMaxNumStereoBonds ) + *nMaxNumStereoBonds = num_stereo_bonds; + + /* + if ( (nMode & REQ_MODE_SC_IGN_ALL_UU ) + REQ_MODE_SC_IGN_ALL_UU + REQ_MODE_SB_IGN_ALL_UU + */ + +#if ( MIN_SB_RING_SIZE > 0 ) + if ( q ) { + q = QueueDelete( q ); + } + if ( nAtomLevel ) + inchi_free( nAtomLevel ); + if ( cSource ) + inchi_free( cSource ); +exit_function: +#endif + + + return num_3D_stereo_atoms; +} + + +/***************************************************************** + * Functions that disconnect bonds + * + *=== During Preprocessing === + * + * RemoveInpAtBond + * DisconnectMetalSalt (is not aware of bond parities) + * DisconnectAmmoniumSalt + * + *=== Before Normalization === + * + * remove_terminal_HDT + * + *=== During the Normalization === + * + * AddOrRemoveExplOrImplH + * + *****************************************************************/ +int ReconcileAllCmlBondParities( inp_ATOM *at, int num_atoms, int bDisconnected ) +{ + int i, ret = 0; + S_CHAR *visited = (S_CHAR*) inchi_calloc( num_atoms, sizeof(*visited) ); + if ( !visited ) + return -1; /* out of RAM */ + for ( i = 0; i < num_atoms; i ++ ) { + if ( at[i].sb_parity[0] && !visited[i] && !(bDisconnected && is_el_a_metal(at[i].el_number)) ) { + if ( ret = ReconcileCmlIncidentBondParities( at, i, -1, visited, bDisconnected ) ) { + break; /* error */ + } + } + } + inchi_free ( visited ); + return ret; +} + + +/*****************************************************************/ +int ReconcileCmlIncidentBondParities( inp_ATOM *at, int cur_atom, int prev_atom, S_CHAR *visited, int bDisconnected ) +{ + /* visited = 0 or parity => atom has not been visited + 10 + parity => currently is on the stack + its final parity + 20 + parity => has been visited; is not on the stack anymore + its final parity */ + int i, j, nxt_atom, ret = 0, len; + int icur2nxt, icur2neigh; /* cur atom neighbors */ + int inxt2cur, inxt2neigh; /* next atom neighbors */ + int cur_parity, nxt_parity; + int cur_order_parity, nxt_order_parity, cur_sb_parity, nxt_sb_parity, bCurMask, bNxtMask; + /* !(bDisconnected && is_el_a_metal(at[i].el_number) */ + + if ( at[cur_atom].valence > MAX_NUM_STEREO_BONDS ) + return 0; /* ignore */ + + if ( !at[cur_atom].sb_parity[0] ) + return 1; /* wrong call */ + + if ( visited[cur_atom] >= 10 ) + return 2; /* program error */ + + cur_parity = visited[cur_atom] % 10; + + visited[cur_atom] += 10; + + for ( i = 0; i < MAX_NUM_STEREO_BONDS && at[cur_atom].sb_parity[i]; i ++ ) { + icur2nxt = (int)at[cur_atom].sb_ord[i]; + len = get_opposite_sb_atom( at, cur_atom, icur2nxt, &nxt_atom, &inxt2cur, &j ); + if ( !len ) { + return 4; /* could not find the opposite atom: bond parity data error */ + } + if ( nxt_atom == prev_atom ) + continue; + if ( visited[nxt_atom] >= 20 ) + continue; /* back edge, second visit: ignore */ + if ( at[nxt_atom].valence > MAX_NUM_STEREO_BONDS ) + continue; /* may be treated only after metal disconnection */ + + if ( bDisconnected && (at[cur_atom].sb_parity[i] & SB_PARITY_FLAG) ) { + cur_sb_parity = (at[cur_atom].sb_parity[i] >> SB_PARITY_SHFT); + bCurMask = 3 << SB_PARITY_SHFT; + } else { + cur_sb_parity = (at[cur_atom].sb_parity[i] & SB_PARITY_MASK); + bCurMask = 3; + } + if ( bDisconnected && (at[nxt_atom].sb_parity[j] & SB_PARITY_FLAG) ) { + nxt_sb_parity = (at[nxt_atom].sb_parity[j] >> SB_PARITY_SHFT); + bNxtMask = 3 << SB_PARITY_SHFT; + } else { + nxt_sb_parity = (at[nxt_atom].sb_parity[j] & SB_PARITY_MASK); + bNxtMask = 3; + } + + + + if ( !ATOM_PARITY_WELL_DEF(cur_sb_parity) || + !ATOM_PARITY_WELL_DEF(nxt_sb_parity) ) { + if ( cur_sb_parity == nxt_sb_parity ) { + continue; + /*goto move_forward;*/ /* bypass unknown/undefined */ + } + return 3; /* sb parities do not match: bond parity data error */ + } + + icur2neigh = (int)at[cur_atom].sn_ord[i]; + inxt2neigh = (int)at[nxt_atom].sn_ord[j]; + /* parity of at[cur_atom].neighbor[] premutation to reach this order: { next_atom, neigh_atom, ...} */ + + /* 1. move next_atom from position=icur2nxt to position=0 => + * icur2nxt permutations + * 2. move neigh_atom from position=inxt2neigh+(inxt2cur > inxt2neigh) to position=1 => + * inxt2neigh+(inxt2cur > inxt2neigh)-1 permutations. + * Note if (inxt2cur > inxt2neigh) then move #1 increments neigh_atom position + * Note add 4 because icur2neigh may be negative due to isotopic H removal + */ + cur_order_parity = (4+icur2nxt + icur2neigh + (icur2neigh > icur2nxt)) % 2; + /* same for next atom: */ + /* parity of at[nxt_atom].neighbor[] premutation to reach this order: { cur_atom, neigh_atom, ...} */ + nxt_order_parity = (4+inxt2cur + inxt2neigh + (inxt2neigh > inxt2cur)) % 2; + + nxt_parity = visited[nxt_atom] % 10; + + if ( !cur_parity ) { + cur_parity = 2 - (cur_order_parity + cur_sb_parity) % 2; + visited[cur_atom] += cur_parity; + } else + if ( cur_parity != 2 - (cur_order_parity + cur_sb_parity) % 2 ) { + + /***** reconcile bond parities *****/ + + /* Each bond parity is split into two values located at the end atoms. + For T (trans) the values are (1,1) or (2,2) + For C (cis) the values are (1,2) or (2,1) + The fact that one pair = another with inverted parities, namely + Inv(1,1) = (2,2) and Inv(1,2) = (2,1), allows to + simultaneouly invert parities of the current bond end atoms + (at[cur_atom].sb_parity[i], at[nxt_atom].sb_parity[j]) + so that the final current atom parity cur_parity + calculated later in stereochemical canonicalization for + each stereobond incident with the current atomis same. + Achieving this is called here RECONCILIATION. + If at the closure of an aromatic circuit the parities of + next atom cannot be reconciled with already calculated then + this function returns 5 (error). + */ + + at[cur_atom].sb_parity[i] ^= bCurMask; + at[nxt_atom].sb_parity[j] ^= bNxtMask; + cur_sb_parity ^= 3; + nxt_sb_parity ^= 3; + } + + if ( !nxt_parity ) { + nxt_parity = 2 - (nxt_order_parity + nxt_sb_parity) % 2; + visited[nxt_atom] += nxt_parity; + } else + if ( nxt_parity != 2 - (nxt_order_parity + nxt_sb_parity) % 2 ) { + return 5; /* algorithm does not work for Mebius-like structures */ + } + +/* move_forward: */ + if ( visited[nxt_atom] < 10 ) { + ret = ReconcileCmlIncidentBondParities( at, nxt_atom, cur_atom, visited, bDisconnected ); + if ( ret ) { + break; + } + } + } + visited[cur_atom] += 10; /* all bonds incident to the current atom have + been processed or an error occurred. */ + return ret; +} + + +/*****************************************************************/ +int get_opposite_sb_atom( inp_ATOM *at, int cur_atom, int icur2nxt, int *pnxt_atom, int *pinxt2cur, int *pinxt_sb_parity_ord ) +{ + AT_NUMB nxt_atom; + int j, len; + + len = 0; + while ( len ++ < 20 ) { /* arbitrarily set cumulene length limit to avoid infinite loop */ + nxt_atom = at[cur_atom].neighbor[icur2nxt]; + for ( j = 0; j < MAX_NUM_STEREO_BONDS && at[nxt_atom].sb_parity[j]; j ++ ) { + if ( cur_atom == at[nxt_atom].neighbor[(int)at[nxt_atom].sb_ord[j]] ) { + /* found the opposite atom */ + *pnxt_atom = nxt_atom; + *pinxt2cur = at[nxt_atom].sb_ord[j]; + *pinxt_sb_parity_ord = j; + return len; + } + } + if ( j ) { + return 0; /* reached atom(s) with stereobond (sb) parity, the opposite atom has not been found */ + } + if ( at[nxt_atom].valence == 2 && 2*BOND_TYPE_DOUBLE == at[nxt_atom].chem_bonds_valence ) { + /* follow cumulene =X= path */ + icur2nxt = (at[nxt_atom].neighbor[0] == cur_atom); + cur_atom = nxt_atom; + } else { + return 0; /* neither atom with a sb parity not middle cumulene could be reached */ + } + } + + return 0; /* too long chain of cumulene was found */ +} diff --git a/INCHI-1-SRC/INCHI/common/ichister.h b/INCHI-1-SRC/INCHI_BASE/src/ichister.h similarity index 62% rename from INCHI-1-SRC/INCHI/common/ichister.h rename to INCHI-1-SRC/INCHI_BASE/src/ichister.h index 12c7a1f..21004e3 100644 --- a/INCHI-1-SRC/INCHI/common/ichister.h +++ b/INCHI-1-SRC/INCHI_BASE/src/ichister.h @@ -1,72 +1,81 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHISTER_H__ -#define __INCHISTER_H__ - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif -int bCanAtomBeAStereoCenter( char *elname, S_CHAR charge, S_CHAR radical ); -int bCanInpAtomBeAStereoCenter( inp_ATOM *at, int cur_at, int bPointedEdgeStereo ); -int bCanAtomHaveAStereoBond( char *elname, S_CHAR charge, S_CHAR radical ); -int bCanAtomBeTerminalAllene( char *elname, S_CHAR charge, S_CHAR radical ); -int bCanAtomBeMiddleAllene( char *elname, S_CHAR charge, S_CHAR radical ); -int bAtomHasValence3( char *elname, S_CHAR charge, S_CHAR radical ); -int set_stereo_parity( inp_ATOM* at, sp_ATOM* at_output, int num_at, int num_removed_H, - int *nMaxNumStereoAtoms, int *nMaxNumStereoBonds, INCHI_MODE nMode, - int bPointedEdgeStereo, int vABParityUnknown ); -int get_opposite_sb_atom( inp_ATOM *at, int cur_atom, int icur2nxt, - int *pnxt_atom, int *pinxt2cur, int *pinxt_sb_parity_ord ); - -#define PES_BIT_POINT_EDGE_STEREO 1 -#define PES_BIT_PHOSPHINE_STEREO 2 -#define PES_BIT_ARSINE_STEREO 4 -#define PES_BIT_FIX_SP3_BUG 8 - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -#endif /* __INCHISTER_H__ */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _ICHISTER_H_ +#define _ICHISTER_H_ + +#include "ichicomn.h" + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif +int bCanAtomBeAStereoCenter( char *elname, S_CHAR charge, S_CHAR radical ); +int bCanInpAtomBeAStereoCenter( inp_ATOM *at, int cur_at, int bPointedEdgeStereo ); +int bCanAtomHaveAStereoBond( char *elname, S_CHAR charge, S_CHAR radical ); +int bCanAtomBeTerminalAllene( char *elname, S_CHAR charge, S_CHAR radical ); +int bCanAtomBeMiddleAllene( char *elname, S_CHAR charge, S_CHAR radical ); +int bAtomHasValence3( char *elname, S_CHAR charge, S_CHAR radical ); + + +struct tagCANON_GLOBALS; +int set_stereo_parity( struct tagCANON_GLOBALS *pCG, + inp_ATOM* at, sp_ATOM* at_output, + int num_at, int num_removed_H, + int *nMaxNumStereoAtoms, int *nMaxNumStereoBonds, + INCHI_MODE nMode, + int bPointedEdgeStereo, int vABParityUnknown ); + +int get_opposite_sb_atom( inp_ATOM *at, int cur_atom, int icur2nxt, + int *pnxt_atom, int *pinxt2cur, int *pinxt_sb_parity_ord ); + +#define PES_BIT_POINT_EDGE_STEREO 1 +#define PES_BIT_PHOSPHINE_STEREO 2 +#define PES_BIT_ARSINE_STEREO 4 +#define PES_BIT_FIX_SP3_BUG 8 + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + + +#endif /* _ICHISTER_H_ */ diff --git a/INCHI-1-SRC/INCHI/common/ichitaut.c b/INCHI-1-SRC/INCHI_BASE/src/ichitaut.c similarity index 88% rename from INCHI-1-SRC/INCHI/common/ichitaut.c rename to INCHI-1-SRC/INCHI_BASE/src/ichitaut.c index ad7e4d5..2979028 100644 --- a/INCHI-1-SRC/INCHI/common/ichitaut.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichitaut.c @@ -1,4567 +1,4556 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include - - -#include "mode.h" - -#include "inpdef.h" -#include "extr_ct.h" -#include "inpdef.h" -#include "ichitaut.h" -#include "ichinorm.h" -#include "ichicant.h" -#include "ichicomn.h" - -#include "ichicomp.h" - -#include "util.h" - -#include "ichi_bns.h" - - -/* Local prototypes */ -int SetTautomericBonds( inp_ATOM *at, int nNumBondPos, T_BONDPOS *BondPos ); -int CompRankTautomer(const void* a1, const void* a2 ); -int RegisterEndPoints( T_GROUP_INFO *t_group_info, /* T_GROUP *t_group, int *pnum_t, int max_num_t,*/ - T_ENDPOINT *EndPoint, int nNumEndPoints, inp_ATOM *at, int num_atoms, C_GROUP_INFO *cgi - , struct BalancedNetworkStructure *pBNS ); -int cmpTGroupNumber( const void *a1, const void *a2 ); -int comp_candidates( const void *a1, const void *a2 ); -int MoveEndpoint( inp_ATOM *at, S_CANDIDATE *s_candidate, AT_NUMB endpoint, AT_NUMB *nTGroupNewNumbers, - AT_NUMB *nTGroupPosition, int nNewTGroupOrd, T_GROUP_INFO *t_group_info); - -int FindAccessibleEndPoints( T_ENDPOINT *EndPoint, int *nNumEndPoints, T_BONDPOS *BondPos, int *nNumBondPos, - struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, - inp_ATOM *at, int num_atoms, C_GROUP_INFO *cgi, int taut_mode ); - -/* Bits for GetChargeType */ - -#define C_SUBTYPE_CHARGED 0 -#define C_SUBTYPE_p_DONOR 1 /* new */ -#define C_SUBTYPE_p_ACCEPT 2 /* new */ -#define C_SUBTYPE_H_ACCEPT 4 -#define C_SUBTYPE_H_DONOR 8 -#define C_SUBTYPE_NEUTRAL 16 - - -/* Internal stack array size */ -#define MAX_STACK_ARRAY_LEN 127 -#define MAX_TGROUP_ARRAY_LEN 127 - -/* local prototypes */ -int GetChargeType( inp_ATOM *atom, int iat, S_CHAR *cChargeSubtype ); -int GetNeutralRepsIfNeeded( AT_NUMB *pri, AT_NUMB *prj, inp_ATOM *at, int num_atoms, T_ENDPOINT *EndPoint, int nNumEndPoints, C_GROUP_INFO *cgi ); -int bCanBeACPoint( inp_ATOM *at, S_CHAR cCharge, S_CHAR cChangeValence, S_CHAR neutral_bonds_valence, - S_CHAR neutral_valence, S_CHAR nEndpointValence, S_CHAR *cChargeSubtype ); -int CmpCCandidates( const void *a1, const void *a2 ); -int RegisterCPoints( C_GROUP *c_group, int *pnum_c, int max_num_c, T_GROUP_INFO *t_group_info, - int point1, int point2, int ctype, inp_ATOM *at, int num_atoms ); -int GetSaltChargeType( inp_ATOM *at, int at_no, T_GROUP_INFO *t_group_info, int *s_subtype ); -int GetOtherSaltChargeType( inp_ATOM *at, int at_no, T_GROUP_INFO *t_group_info, int *s_subtype, int bAccept_O ); -int MergeSaltTautGroupsBlind( inp_ATOM *at, int s_type, int num_atoms, S_GROUP_INFO *s_group_info, int nNumCandidates, - T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, - struct BalancedNetworkStructure *pBNS ); -int ConnectSaltTGroups2SuperTGroup( inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, int nNumCandidates, - T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, - struct BalancedNetworkStructure *pBNS, int *nNewTGroupNumber, int *vertSuperTGroup ); -int bDoNotMergeNonTautAtom(inp_ATOM *at, int at_no); -int GetOtherSaltType( inp_ATOM *at, int at_no, int *s_subtype ); - - -/*****************************************************************************/ -/* Tautomers: Sorting globals */ -AT_RANK *pn_tRankForSort; -/*****************************************************************************/ - - - -/*****************************************************************************/ -int is_centerpoint_elem( U_CHAR el_number ) -{ - static U_CHAR el_numb[12]; - static int len; - int i; - if ( !el_numb[0] && !len ) { - el_numb[len++] = (U_CHAR)get_periodic_table_number( "C" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "N" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "P" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "S" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "I" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "As" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "Sb" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "Se" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "Te" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "Cl" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "Br" ); - } - for ( i = 0; i < len; i ++ ) { - if ( el_numb[i] == el_number ) { - return 1; - } - } - return 0; -} -#if ( KETO_ENOL_TAUT == 1 ) /* post v.1 feature */ - - - -/*****************************************************************************/ -int is_centerpoint_elem_KET( U_CHAR el_number ) -{ - static U_CHAR el_numb[1]; - static int len; - int i; - if ( !el_numb[0] && !len ) { - el_numb[len++] = (U_CHAR)get_periodic_table_number( "C" ); - } - for ( i = 0; i < len; i ++ ) { - if ( el_numb[i] == el_number ) { - return 1; - } - } - return 0; -} -#endif - - - -/*****************************************************************************/ -int is_centerpoint_elem_strict( U_CHAR el_number ) -{ - static U_CHAR el_numb[6]; - static int len; - int i; - if ( !el_numb[0] && !len ) { - el_numb[len++] = (U_CHAR)get_periodic_table_number( "C" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "N" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "P" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "As" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "Sb" ); - } - for ( i = 0; i < len; i ++ ) { - if ( el_numb[i] == el_number ) { - return 1; - } - } - return 0; -} - - - -/*****************************************************************************/ -int get_endpoint_valence( U_CHAR el_number ) -{ - static U_CHAR el_numb[6]; - static int len, len2; - int i; - if ( !el_numb[0] && !len ) { - el_numb[len++] = (U_CHAR)get_periodic_table_number( "O" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "S" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "Se" ); - el_numb[len++] = (U_CHAR)get_periodic_table_number( "Te" ); - len2 = len; - el_numb[len++] = (U_CHAR)get_periodic_table_number( "N" ); - } - for ( i = 0; i < len; i ++ ) { - if ( el_numb[i] == el_number ) { - return i < len2? 2 : 3; - } - } - return 0; -} - - - -/*****************************************************************************/ -#if ( KETO_ENOL_TAUT == 1 ) /* post v.1 feature */ -/*****************************************************************************/ -int get_endpoint_valence_KET( U_CHAR el_number ) -{ - static U_CHAR el_numb[2]; - static int len, len2; - int i; - if ( !el_numb[0] && !len ) { - el_numb[len++] = (U_CHAR)get_periodic_table_number( "O" ); - len2 = len; - el_numb[len++] = (U_CHAR)get_periodic_table_number( "C" ); - } - for ( i = 0; i < len; i ++ ) { - if ( el_numb[i] == el_number ) { - return i < len2? 2 : 4; - } - } - return 0; -} -#endif -/*****************************************************************************/ - - - -/*****************************************************************************/ -int AddAtom2num( AT_RANK num[], inp_ATOM *atom, int at_no, int bSubtract ) -{ /* bSubtract: 0=> add, 1=>subtract, 2=> fill */ - inp_ATOM *at = atom + at_no; - int k; - int nMobile = (at->charge == -1); - if ( bSubtract == 1 ) { - /* 1: subtract */ - num[1] -= nMobile; - nMobile += at->num_H; - num[0] -= nMobile; - for ( k = 0; k < T_NUM_ISOTOPIC; k ++ ) { - /* T (3H isotope) first because it has higher weight */ - num[T_NUM_NO_ISOTOPIC+k] -= at->num_iso_H[NUM_H_ISOTOPES-k-1]; - } - } else { - if ( bSubtract == 2 ) { - /* fill */ - memset( num, 0, (T_NUM_NO_ISOTOPIC + T_NUM_ISOTOPIC)*sizeof(num[0]) ); - } - /* else (0): add */ - num[1] += nMobile; - nMobile += at->num_H; - num[0] += nMobile; - for ( k = 0; k < T_NUM_ISOTOPIC; k ++ ) { - /* T (3H isotope) first because it has higher weight */ - num[T_NUM_NO_ISOTOPIC+k] += at->num_iso_H[NUM_H_ISOTOPES-k-1]; - } - } - return nMobile; -} - - - -/*****************************************************************************/ -void AddAtom2DA( AT_RANK num_DA[], inp_ATOM *atom, int at_no, int bSubtract ) -{ /* bSubtract: 0=> add, 1=>subtract, 2=> fill */ - inp_ATOM *at = atom + at_no; - int nDelta, nAcidic_O; - - if (at->charge < -1 || at->charge == 1 && !at->c_point || at->charge > 1 ) - return; - - nDelta = ( bSubtract == 1 )? -1 : 1; - - /* "Acidic" O, S, Se, Te recognition */ - if ( at->at_type & ATT_ACIDIC_CO ) { - nAcidic_O = nDelta; - } else { - nAcidic_O = 0; - } - - if ( bSubtract == 2 ) { /* 2: fill, otherwise add */ - memset( num_DA, 0, TG_NUM_DA * sizeof(num_DA[0]) ); - } - if ( at->charge <= 0 && at->valence == at->chem_bonds_valence || - /* neutral or negative donor */ - at->charge > 0 && at->valence + 1 == at->chem_bonds_valence - /* positively charged donor */ - ) { - if ( at->charge < 0 ) { - num_DA[TG_Num_dM] += nDelta; - num_DA[TG_Num_dO] += nAcidic_O; - } else - if ( at->num_H ) { - num_DA[TG_Num_dH] += nDelta; - num_DA[TG_Num_dO] += nAcidic_O; - } - } else - if ( at->charge <= 0 && at->valence + 1 == at->chem_bonds_valence || - at->charge > 0 && at->valence + 2 == at->chem_bonds_valence ) { - /* acceptor */ - if ( at->charge < 0 ) { - num_DA[TG_Num_aM] += nDelta; - } else - if ( at->num_H ) { - num_DA[TG_Num_aH] += nDelta; - } else { - num_DA[TG_Num_aO] += nAcidic_O; /* acidic O-acceptor has no H or charge */ - } - } - return; -} - - - -/*****************************************************************************/ -int AddEndPoint( T_ENDPOINT *pEndPoint, inp_ATOM *at, int iat ) -{ - pEndPoint->nAtomNumber = iat; - pEndPoint->nEquNumber = 0; - pEndPoint->nGroupNumber = at[iat].endpoint; - if ( at[iat].endpoint ) { - /* already an endpoint */ - memset( pEndPoint->num, 0, sizeof(pEndPoint->num) ); - } else { - /* not an endpoint yet, make it an endpoint */ - AddAtom2num( pEndPoint->num, at, iat, 2 ); /* fill */ - AddAtom2DA( pEndPoint->num_DA, at, iat, 2 ); - /* - nMobile = pEndPoint->num[1] = (at[iat].charge == -1); - nMobile = pEndPoint->num[0] = at[iat].num_H + nMobile; - for ( k = 0; k < T_NUM_ISOTOPIC; k ++ ) { - pEndPoint->num[T_NUM_NO_ISOTOPIC+k] = at[iat].num_iso_H[NUM_H_ISOTOPES-k-1]; - } - */ - } - return 0; -} - - - -/*****************************************************************************/ -int nGetEndpointInfo( inp_ATOM *atom, int iat, ENDPOINT_INFO *eif ) -{ - int nEndpointValence; - int nMobile; - S_CHAR cChargeSubtype; - - if ( atom[iat].radical && atom[iat].radical != RADICAL_SINGLET ) - return 0; /* a radical */ - if ( !(nEndpointValence = get_endpoint_valence( atom[iat].el_number )) ) - return 0; /* not an endpoint */ - if ( nEndpointValence <= atom[iat].valence ) - return 0; /* not an endpoint, for example >N(+)< or >N< or >O(+)- or >O- or >N- or -O- */ - - if ( atom[iat].charge == -1 || atom[iat].charge == 0 ) { - /* not a positive charge-point */ - if ( nEndpointValence < atom[iat].chem_bonds_valence ) - return 0; /* abnormal valence > standard endpoint valence */ - nMobile = atom[iat].num_H + (atom[iat].charge == -1); - if ( nMobile + atom[iat].chem_bonds_valence != nEndpointValence ) - return 0; /* non-standard endpoint valence */ - switch ( atom[iat].chem_bonds_valence - atom[iat].valence ) { - case 0: - eif->cDonor = 1; - eif->cAcceptor = 0; - break; - case 1: - eif->cDonor = 0; - eif->cAcceptor = 1; - break; - default: - return 0; - } - eif->cMobile = nMobile; - eif->cNeutralBondsValence = nEndpointValence-nMobile; - eif->cMoveableCharge = 0; -#if ( KETO_ENOL_TAUT == 1 ) - eif->cKetoEnolCode = 0; -#endif - return nEndpointValence; - } else - if ( atom[iat].c_point && - 0 <= GetChargeType( atom, iat, &cChargeSubtype ) && - ((int)cChargeSubtype & (C_SUBTYPE_H_ACCEPT|C_SUBTYPE_H_DONOR)) - ) { - /* charge-point */ - if ( cChargeSubtype & C_SUBTYPE_H_ACCEPT ) { - eif->cDonor = 0; - eif->cAcceptor = 1; - } else - if ( cChargeSubtype & C_SUBTYPE_H_DONOR ) { - eif->cDonor = 1; - eif->cAcceptor = 0; - } else { - return 0; - } - eif->cMobile = atom[iat].num_H; - eif->cNeutralBondsValence = nEndpointValence-atom[iat].num_H; - eif->cMoveableCharge = atom[iat].charge; -#if ( KETO_ENOL_TAUT == 1 ) - eif->cKetoEnolCode = 0; -#endif - return nEndpointValence; - } - return 0; -} - - - -/*****************************************************************************/ -#if ( KETO_ENOL_TAUT == 1 ) /* post v.1 feature */ -/*****************************************************************************/ -int nGetEndpointInfo_KET( inp_ATOM *atom, int iat, ENDPOINT_INFO *eif ) -{ - int nEndpointValence; - int nMobile; - S_CHAR cChargeSubtype; - - /* - static U_CHAR el_number_O, el_number_C; - - if ( !el_number_O ) { - el_number_O = (U_CHAR)get_periodic_table_number( "O" ); - el_number_C = (U_CHAR)get_periodic_table_number( "C" ); - } - */ - if ( atom[iat].radical && atom[iat].radical != RADICAL_SINGLET ) - return 0; /* a radical */ - if ( !(nEndpointValence = get_endpoint_valence_KET( atom[iat].el_number )) ) - return 0; /* not an endpoint; only O and C can be an endpoint for keto-enol tautomerism */ - if ( nEndpointValence <= atom[iat].valence ) - return 0; /* not an endpoint, for example >N(+)< or >N< or >O(+)- or >O- or >N- or -O- */ - if ( nEndpointValence == 4 && atom[iat].valence < 2 ) - return 0; /* exclude O==C--CH3 <=> HO--C==CH2 */ - if ( nEndpointValence == 2 && atom[iat].valence > 1 ) - return 0; /* exclude --O--C==CH-- */ - - if ( atom[iat].charge == -1 || atom[iat].charge == 0 ) { - /* not a positive charge-point */ - if ( nEndpointValence < atom[iat].chem_bonds_valence ) - return 0; /* abnormal valence > standard endpoint valence */ - nMobile = atom[iat].num_H + (atom[iat].charge == -1); - if ( nMobile + atom[iat].chem_bonds_valence != nEndpointValence ) - return 0; /* non-standard endpoint valence */ - switch ( atom[iat].chem_bonds_valence - atom[iat].valence ) { - case 0: - eif->cDonor = 1; - eif->cAcceptor = 0; - break; - case 1: - eif->cDonor = 0; - eif->cAcceptor = 1; - break; - default: - return 0; - } - eif->cMobile = nMobile; - eif->cNeutralBondsValence = nEndpointValence-nMobile; - eif->cMoveableCharge = 0; - eif->cKetoEnolCode = (nEndpointValence == 2)? 1 : (nEndpointValence == 4)? 2 : 0; - return nEndpointValence; - } else - if ( atom[iat].c_point && - 0 <= GetChargeType( atom, iat, &cChargeSubtype ) && - ((int)cChargeSubtype & (C_SUBTYPE_H_ACCEPT|C_SUBTYPE_H_DONOR)) - ) { - /* charge-point; currently only O for keto-enol tautomerism */ - if ( cChargeSubtype & C_SUBTYPE_H_ACCEPT ) { - eif->cDonor = 0; - eif->cAcceptor = 1; - } else - if ( cChargeSubtype & C_SUBTYPE_H_DONOR ) { - eif->cDonor = 1; - eif->cAcceptor = 0; - } else { - return 0; - } - eif->cMobile = atom[iat].num_H; - eif->cNeutralBondsValence = nEndpointValence-atom[iat].num_H; - eif->cMoveableCharge = atom[iat].charge; - eif->cKetoEnolCode = (nEndpointValence == 2)? 1 : (nEndpointValence == 4)? 2 : 0; - return nEndpointValence; - } - return 0; -} -#endif -/*****************************************************************************/ - - - -/*****************************************************************************/ -/* RegisterEndPoints ret>0 => new registration happened, */ -/* =0 => no changes, -1 => program error (debug) */ -/*****************************************************************************/ -int RegisterEndPoints( T_GROUP_INFO *t_group_info, - /* T_GROUP *t_group, int *pnum_t, int max_num_t,*/ - T_ENDPOINT *EndPoint, int nNumEndPoints, inp_ATOM *at, int num_atoms, - C_GROUP_INFO *cgi, struct BalancedNetworkStructure *pBNS ) -{ - T_GROUP *t_group = t_group_info->t_group; - int *pnum_t = &t_group_info->num_t_groups; - int max_num_t = t_group_info->max_num_t_groups; - int nNumZeroEqu, nNumNewTGroups; - AT_NUMB group, prev_group, prev_eqnum, nNextGroupNumber, nLeastGroupNumber; - int nNumGroups, num_t, difference; - int i, j, k, ret; - AT_NUMB nNewTgNumberStackArray[MAX_STACK_ARRAY_LEN+1]; - AT_NUMB nGroupNumberStackArray[MAX_STACK_ARRAY_LEN+1]; - AT_NUMB nGroupNewNumberStackArray[MAX_STACK_ARRAY_LEN+1]; - AT_NUMB *nNewTgNumber = nNewTgNumberStackArray; - AT_NUMB *nGroupNumber = nGroupNumberStackArray; - AT_NUMB *nGroupNewNumber = nGroupNewNumberStackArray; - - if ( nNumEndPoints <= 0 ) - return 0; /* nothing to do */ - num_t = *pnum_t; - difference = 0; - nNextGroupNumber = 0; - nNumZeroEqu = 0; - ret = 0; - /* find max group number; increment it to obtain next available group number */ - for ( i = 0; i < num_t; i ++ ) { - if ( nNextGroupNumber < t_group[i].nGroupNumber ) - nNextGroupNumber = t_group[i].nGroupNumber; - } - nNextGroupNumber ++; - - /* find min non-zero group number nLeastGroupNumber; - count zero EndPoint[i].nEquNumber - if all EndPoint[i].nGroupNumber are equal and non-zero then exit: nothing to do. - */ - nLeastGroupNumber = nNextGroupNumber; - prev_group = EndPoint[0].nGroupNumber; - prev_eqnum = EndPoint[0].nEquNumber; - for ( i = j = k = 0; i < nNumEndPoints; i ++ ) { - if ( group = EndPoint[i].nGroupNumber ) { - if ( group < nLeastGroupNumber ) { - nLeastGroupNumber = group; - } - } - j += (prev_group == EndPoint[i].nGroupNumber); /* count endpoints that belong to the 1st group */ - k += (prev_eqnum == EndPoint[i].nEquNumber); /* count endpoints that belongo to a group equivalent to the 1st group */ - nNumZeroEqu += !EndPoint[i].nEquNumber; /* count endpoints that have been processed by FindAccessibleEndPoints() */ - } - if ( j == nNumEndPoints && prev_group && k == nNumEndPoints ) { - /* all endpoints already belong to one t-group; - the last comparison is not needed for now - because EndPoint[i].nEquNumber cannot make - endpont partitioning finer - */ - return 0; - } - - nNumNewTGroups = 0; - - if ( !nNumZeroEqu ) { - /* EndPoint[] has been processed by FindAccessibleEndPoints; - * equal EndPoint[i].nEquNumber mark endpoints belonging to - * the same t-group - * Since now the next available t-group number, nNextGroupNumber, - * is known,replace fict. IDs assigned by FindAccessibleEndPoints - * with correct new t-group numbers. - */ - for ( i = 0; i < nNumEndPoints; i ++ ) { - if ( (group = EndPoint[i].nEquNumber) >= nNextGroupNumber ) { - /* replace fict. IDs assigned by FindAccessibleEndPoints() with new t-group numbers */ - /* these fict. IDs have values = (num_atoms+1), (num_atoms+2),...; they may be non-contiguous */ - for ( j = 0; j < nNumNewTGroups; j ++ ) { - if ( group == nGroupNewNumber[j] ) - break; - } - if ( j == nNumNewTGroups ) { - /* found new fict. ID = group */ - if ( j == MAX_STACK_ARRAY_LEN && nGroupNewNumber == nGroupNewNumberStackArray ) { - /* stack array overflow; allocate more memory than may be needed */ - nGroupNewNumber = (AT_NUMB *) inchi_malloc(nNumEndPoints*sizeof(nGroupNewNumber[0])); - if ( !nGroupNewNumber ) { - ret = -1; - goto exit_function; - } - memcpy( nGroupNewNumber, nGroupNewNumberStackArray, nNumNewTGroups*sizeof(nGroupNewNumber[0])); - } - /* save newly found fict. t-group ID to compare to the next values of EndPoint[].nEquNumber */ - nGroupNewNumber[j] = group; - nNumNewTGroups ++; - } - EndPoint[i].nEquNumber = nNextGroupNumber + j; - } - } /* after this point the values just stored in nGroupNewNumber[] will not - be used. However, the obtained nNumNewTGroups value will be used */ - } else - if ( nNumZeroEqu == nNumEndPoints ) { - /* EndPoint[] has NOT been processed by FindAccessibleEndPoints; - all atoms and t-groups to which endpoints belong should be merged into a single t-group - */ - if ( nLeastGroupNumber == nNextGroupNumber ) { - /* flag to create a new t-group: none of the found - * endpoints belong to an already known t-group - */ - nNumNewTGroups = 1; /* otherwise 0 */ - } - /* All EndPoint[*].nEquNumber are zeroes. All endpoints will - * belong to one new or old t-group; its ID is nLeastGroupNumber. - * Set EndPoint[i].nEquNumber = nLeastGroupNumber; - */ - for ( i = 0; i < nNumEndPoints; i ++ ) { - EndPoint[i].nEquNumber = nLeastGroupNumber; - } - } else { - ret = -1; /* program error: only some of EndPoint[i].nEquNumber are zero */ /* */ - goto exit_function; - } - - if ( nNumNewTGroups ) { - /* create new nNumNewTGroups t-group(s) */ - if ( num_t + nNumNewTGroups > max_num_t ) { - ret = -1; /* found too many t-groups */ /* */ - goto exit_function; - } - /* initialize new t-group(s) */ - memset( t_group + num_t, 0, nNumNewTGroups * sizeof(t_group[0]) ); - for ( i = 0; i < nNumNewTGroups; i ++ ) { - t_group[num_t+i].nGroupNumber = nNextGroupNumber + i; - } - } - - /* At this point: - * EndPoint[i].nGroupNumber == 0 => the endpoint atom does not belong to a t-group yet - * EndPoint[i].nGroupNumber > 0 => current t-group ID of the endpoint atom - * EndPoint[i].nEquNumber --> new ID of a tautomeric group of this endpoint atom - * EndPoint[i].nAtomNumber --> number of the endpoint atom - */ - - nNumGroups = 0; /* counts the groups to be renumbered */ - for ( i = j = 0; i < nNumEndPoints; i ++ ) { - if ( group = EndPoint[i].nGroupNumber ) { - if ( group == EndPoint[i].nEquNumber ) { - continue; /* ignore: the endpoint belongs to the same t-group as before */ - } - /* save information for renumbering of the existing t-groups */ - for ( j = 0; j < nNumGroups; j ++ ) { - if ( group == nGroupNumber[j] ) { - if ( EndPoint[i].nEquNumber != nGroupNewNumber[j] ) { - ret = -1; /* program error */ /* */ - goto exit_function; - } - break; - } - } - if ( j == nNumGroups ) { - /* discovered a new t-group number; store it together with its nEquNumber */ - if ( j == MAX_STACK_ARRAY_LEN ) { - if ( nGroupNewNumber == nGroupNewNumberStackArray ) { - nGroupNewNumber = (AT_NUMB *) inchi_malloc(nNumEndPoints*sizeof(nGroupNewNumber[0])); - if ( !nGroupNewNumber ) { - ret = -1; - goto exit_function; - } - memcpy( nGroupNewNumber, nGroupNewNumberStackArray, nNumGroups*sizeof(nGroupNewNumber[0])); - } - if ( nGroupNumber == nGroupNumberStackArray ) { - nGroupNumber = (AT_NUMB *) inchi_malloc(nNumEndPoints*sizeof(nGroupNumber[0])); - if ( !nGroupNumber ) { - ret = -1; - goto exit_function; - } - memcpy( nGroupNumber, nGroupNumberStackArray, nNumGroups*sizeof(nGroupNumber[0])); - } - } - - nGroupNumber[j] = group; /* old t-group ID */ - nGroupNewNumber[j] = EndPoint[i].nEquNumber; /* new t-group ID */ - nNumGroups ++; - } - } else { - /* add a new endpoint to the newly created or previously existing t-groups */ - group = EndPoint[i].nEquNumber; - if ( group >= nNextGroupNumber ) { - /* get index of a new t-group from equ number */ - j = num_t + group - nNextGroupNumber; /* newly assigned IDs are contiguous */ - } else { - /* old t-group */ - if ( j >= num_t || group != t_group[j].nGroupNumber ) { - /* search only if j is not a needed group index */ - for ( j = 0; j < num_t; j ++ ) { - if ( group == t_group[j].nGroupNumber ) - break; - } - if ( j == num_t ) { - ret = -1; /* program error: t-group not found */ /* */ - goto exit_function; - } - } - } - /* add aton to existing or new t-group */ - t_group[j].nNumEndpoints ++; - for ( k = 0; k < (int)(sizeof(t_group->num)/sizeof(t_group->num[0])); k ++ ) - t_group[j].num[k] += EndPoint[i].num[k]; - for ( k = 0; k < (int)(sizeof(t_group->num_DA)/sizeof(t_group->num_DA[0])); k ++ ) - t_group[j].num_DA[k] += EndPoint[i].num_DA[k]; - /* mark endpoint */ - at[EndPoint[i].nAtomNumber].endpoint = group; - difference ++; - } - } - - difference += nNumGroups; - num_t += nNumNewTGroups; - if ( !difference ) { - ret = 0; /* nothing to do. Not necessarily a program error: happens if all EndPoint[i].nGroupNumber==EndPoint[i].nEquNumber */ - goto exit_function; - } - - if ( nNumGroups ) { - /* prepare for renumbering: find max t-group number */ - for ( i = 0, nNextGroupNumber = 0; i < num_t; i ++ ) { - if ( nNextGroupNumber < t_group[i].nGroupNumber ) { - nNextGroupNumber = t_group[i].nGroupNumber; - } - } - } - /* renumber and merge t-groups */ - for ( i = 0; i < nNumGroups; i ++ ) { - int i1, i2; - AT_NUMB group1 = nGroupNumber[i]; - AT_NUMB group2 = nGroupNewNumber[i]; - /* add group1 to group2, then delete group1. */ - for ( j = 0, i1 = i2 = -1; j < num_t && (i1 < 0 || i2 < 0); j ++ ) { - if ( i1 < 0 && group1 == t_group[j].nGroupNumber ) - i1 = j; - if ( i2 < 0 && group2 == t_group[j].nGroupNumber ) - i2 = j; - } - if ( i1 < 0 || i2 < 0 ) { - ret = -1; /* program error */ /* */ - goto exit_function; - } - /* add t_group[i1] to t_group[i2] and remove t_group[i1] */ - for ( k = 0; k < (int)(sizeof(t_group->num)/sizeof(t_group->num[0])); k ++ ) - t_group[i2].num[k] += t_group[i1].num[k]; - for ( k = 0; k < (int)(sizeof(t_group->num_DA)/sizeof(t_group->num_DA[0])); k ++ ) - t_group[i2].num_DA[k] += t_group[i1].num_DA[k]; - t_group[i2].nNumEndpoints += t_group[i1].nNumEndpoints; - num_t --; - if ( num_t > i1 ) { - memmove( t_group+i1, t_group+i1+1, ( num_t - i1)*sizeof(t_group[0]) ); - } - } - - if ( nNumGroups ) { - /* there are groups to merge */ - if ( nNextGroupNumber >= MAX_STACK_ARRAY_LEN ) { - nNewTgNumber = (AT_NUMB *) inchi_malloc((nNextGroupNumber+1)*sizeof(*nNewTgNumber)); - if ( !nNewTgNumber ) { - ret = -1; - goto exit_function; /* error: out of RAM */ - } - } - memset( nNewTgNumber, 0, (nNextGroupNumber+1)*sizeof(*nNewTgNumber) ); - for ( i = 0; i < num_t; i ++ ) { - nNewTgNumber[t_group[i].nGroupNumber] = i+1; /* new t-group numbers */ - } - for ( j = 0; j < nNumGroups; j ++ ) { - if ( !nNewTgNumber[nGroupNumber[j]] && nNewTgNumber[nGroupNewNumber[j]] ) { - nNewTgNumber[nGroupNumber[j]] = nNewTgNumber[nGroupNewNumber[j]]; - } else { - ret = -1; /* program error: all new numbers must have been marked */ - goto exit_function; - } - } - /* renumber t-groups */ - for ( i = 0; i < num_t; i ++ ) { - t_group[i].nGroupNumber = nNewTgNumber[t_group[i].nGroupNumber]; - } -#if ( bRELEASE_VERSION != 1 ) - /* Check: debug only */ - for ( i = 1; i < num_t; i ++ ) { - if ( 1 != t_group[i].nGroupNumber - t_group[i-1].nGroupNumber ) { - ret = -1; /* debug */ - goto exit_function; - } - } -#endif - /* renumber endpoints */ - for ( i = 0; i < num_atoms; i ++ ) { - if ( group = at[i].endpoint ) { - if ( !(at[i].endpoint = nNewTgNumber[group]) || nNextGroupNumber <= nNewTgNumber[group] ) { - ret = -1; /* program error */ - goto exit_function; - } - } - } - } - if ( nNewTgNumber != nNewTgNumberStackArray ) { - inchi_free( nNewTgNumber ); - nNewTgNumber = nNewTgNumberStackArray; - } - if ( nGroupNumber != nGroupNumberStackArray ) { - inchi_free(nGroupNumber); - nGroupNumber = nGroupNumberStackArray; - } - if ( nGroupNewNumber != nGroupNewNumberStackArray ) { - inchi_free( nGroupNewNumber ); - nGroupNewNumber = nGroupNewNumberStackArray; - } - if ( !t_group_info->tGroupNumber ) { - t_group_info->tGroupNumber = (AT_NUMB *) inchi_malloc(2*max_num_t*sizeof(t_group_info->tGroupNumber[0])); - if ( !t_group_info->tGroupNumber ) { - ret = -1; - goto exit_function; - } - } - /* fill out t-group index 2004-02-27 */ - memset( t_group_info->tGroupNumber, 0, 2*max_num_t*sizeof(t_group_info->tGroupNumber[0]) ); - for ( i = 0; i < num_t; i ++ ) { - if ( t_group[i].nNumEndpoints && t_group[i].nGroupNumber ) - t_group_info->tGroupNumber[t_group[i].nGroupNumber] = i+1; - } - - if ( pBNS && (pBNS->tot_st_cap == pBNS->tot_st_flow || ALWAYS_ADD_TG_ON_THE_FLY) ) { - T_GROUP_INFO tgi; - int ret_bns; - memset( &tgi, 0, sizeof(tgi) ); - tgi.num_t_groups = num_t; - tgi.t_group = t_group; -#if ( KETO_ENOL_TAUT == 1 ) - tgi.bTautFlags |= (t_group_info->bTautFlags & TG_FLAG_KETO_ENOL_TAUT); /* needed in AddTGroups2BnStruct() */ -#endif - /* reinitialize BN Structure */ - ret_bns = ReInitBnStruct( pBNS, at, num_atoms, 0 ); - if ( IS_BNS_ERROR( ret_bns ) ) { - return ret_bns; - } - if ( *pBNS->pbTautFlags & TG_FLAG_MOVE_POS_CHARGES ) { - /* set new charge groups */ - ret_bns = AddCGroups2BnStruct( pBNS, at, num_atoms, cgi ); - if ( IS_BNS_ERROR( ret_bns ) ) { - return ret_bns; - } - } - /* set new tautomeric groups */ - ret_bns = AddTGroups2BnStruct( pBNS, at, num_atoms, &tgi ); - if ( IS_BNS_ERROR( ret_bns ) ) { - return ret_bns; - } - } - - *pnum_t = num_t; - return difference; - -exit_function: - if ( nNewTgNumber != nNewTgNumberStackArray ) { - inchi_free( nNewTgNumber ); - } - if ( nGroupNumber != nGroupNumberStackArray ) { - inchi_free(nGroupNumber); - } - if ( nGroupNewNumber != nGroupNewNumberStackArray ) { - inchi_free( nGroupNewNumber ); - } - return ret; -} - - - -/***************************************************************************** - * Change non-alternating and non-tautomeric bonds - * (that is, single and double bonds) to tautomeric - *****************************************************************************/ -int SetTautomericBonds( inp_ATOM *at, int nNumBondPos, T_BONDPOS *BondPos ) -{ - int k, n; - for ( k = n = 0; k < nNumBondPos; k ++ ) { - int neighbor_index = BondPos[k].neighbor_index; - int center = BondPos[k].nAtomNumber; - int bond_mark = at[center].bond_type[neighbor_index]; - int bond_type = bond_mark & ~BOND_MARK_ALL; - int neighbor; -#if ( REPLACE_ALT_WITH_TAUT == 1 ) - if ( bond_type != BOND_TAUTOM ) -#else - if ( bond_type != BOND_ALTERN && bond_type != BOND_TAUTOM ) -#endif - { - int ii; - /* change bond type to BOND_TAUTOM presering higher bits marks */ - bond_type = (bond_mark & BOND_MARK_ALL) | BOND_TAUTOM; - /* change center-neighbor bond */ - at[center].bond_type[neighbor_index] = bond_type; - neighbor = at[center].neighbor[neighbor_index]; - for ( ii = 0; ii < at[neighbor].valence; ii ++ ) { - if ( at[neighbor].neighbor[ii] == center ) { - /* neighbor-center bond found */ - at[neighbor].bond_type[ii] = bond_type; - break; - } - } - n ++; - } - } - return n; -} - - - -/*****************************************************************************/ -int GetNeutralRepsIfNeeded( AT_NUMB *pri, AT_NUMB *prj, inp_ATOM *at, int num_atoms, - T_ENDPOINT *EndPoint, int nNumEndPoints, C_GROUP_INFO *cgi ) -{ - AT_NUMB ri = *pri; - AT_NUMB rj = *prj; - int i, k; - AT_NUMB c_point, endpoint, r; - - if ( (c_point = at[ri].c_point) && (c_point == at[rj].c_point) && - (at[ri].charge == 1 || at[rj].charge == 1) && cgi && cgi->num_c_groups > 0 ) { - /* at[ri] and at[rj] belong to the same charge group, at least one is charged */ - /* MS VC++ 2005 reports unreachable code here ??? */ - for ( k = 0; k < cgi->num_c_groups; k ++ ) { - if ( cgi->c_group[k].nGroupNumber == c_point ) { - /* cgi->c_group[k] is found to be this charge group */ - if ( cgi->c_group[k].num_CPoints - cgi->c_group[k].num[0] < 2 ) { - /* Only one neutral in the c-group: we will not be able to neutralize both - when looking for the alt path to discover the tautomerism. - Therefore we need to find a neutral t-group representative */ - /* at[rj] */ - if ( endpoint = at[rj].endpoint ) { - for ( i = 0; i < nNumEndPoints; i ++ ) { - if ( (r=EndPoint[i].nAtomNumber) == *prj ) - continue; /* ignore at[*prj] */ - if ( at[r].endpoint != endpoint ) - continue; /* at[r] does not belong to the same t-group as at[*prj]; ignore the atom */ - if ( !at[r].c_point ) { - rj = r; /* found a neutral t-group representative */ - break; - } - if ( at[r].c_point != c_point && c_point == at[rj].c_point ) { - /* replace only once because of (c_point == at[rj].c_point) condition */ - rj = r; - } - } - if ( rj == *prj /*&& at[ri].endpoint*/ ) { - /* !!! "&& at[ri].endpoint": only between 2 t-groups 2004-02-27; - the change disabled due to undiscovered yet possibility of ambiguity*/ - /* no replacement has been found in EndPoint[]; try all atoms in the t-group */ - for ( i = 0; i < num_atoms; i ++ ) { - if ( at[i].endpoint != endpoint ) - continue; - if ( i == (int)*prj ) - continue; - if ( !at[i].c_point ) { - rj = (AT_NUMB)i; /* found neutral t-group representative */ - break; - } - if ( at[i].c_point != c_point && c_point == at[rj].c_point ) { - /* replace only once */ - rj = (AT_NUMB)i; - } - } - } - } - /* at[ri] */ - if ( endpoint = at[ri].endpoint ) { - for ( i = 0; i < nNumEndPoints; i ++ ) { - if ( (r=EndPoint[i].nAtomNumber) == *pri ) - continue; - if ( at[r].endpoint != endpoint ) - continue; - if ( !at[r].c_point ) { - ri = r; /* found neutral t-group representative */ - break; - } - if ( at[r].c_point != c_point && c_point == at[ri].c_point && - at[r].c_point != at[rj].c_point ) { - /* replace only once */ - ri = r; - } - } - if ( ri == *pri && at[rj].endpoint ) { - /* !!! "&& at[rj].endpoint": only between 2 t-groups 2004-02-27; - the change disabled due to undiscovered yet possibility of ambiguity */ - for ( i = 0; i < num_atoms; i ++ ) { - if ( at[i].endpoint != endpoint ) - continue; - if ( i == (int)*pri ) - continue; - if ( !at[i].c_point ) { - ri = (AT_NUMB)i; /* found neutral t-group representative */ - break; - } - if ( at[i].c_point != c_point && c_point == at[ri].c_point && - at[i].c_point != at[rj].c_point) { - /* replace only once */ - ri = (AT_NUMB)i; - } - } - } - } - - } - } - break; - } - *prj = rj; - *pri = ri; - } - return 0; -} - - - -/*****************************************************************************/ -int FindAccessibleEndPoints( T_ENDPOINT *EndPoint, int *nNumEndPoints, T_BONDPOS *BondPos, int *nNumBondPos, - struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD, - inp_ATOM *at, int num_atoms, C_GROUP_INFO *cgi, int taut_mode ) -{ - AT_NUMB nTGroupRepresenative[MAXVAL], nTGroupEqu[MAXVAL], nTGEndPointNo[MAXVAL], ri, rj; - AT_NUMB nCurTGroupNumber, nMaxTGroupNumber, nNumTgroupNumbers, nMaxEquNumber; - int i, j, k, nNumDiffTGroupNumbers = 0, nNumFoundEqu, nErr; - - if ( *nNumEndPoints != *nNumBondPos ) - return 0; - /* collect all group numbers. Fill EndPoint[i].nEquNumber */ - for ( i = 0; i < *nNumEndPoints; i ++ ) { - nCurTGroupNumber = EndPoint[i].nEquNumber = EndPoint[i].nGroupNumber; /* initial equivalence */ - if ( nCurTGroupNumber ) { - /* found endpoint that already belongs to a t-group */ - for ( j = 0; j < nNumDiffTGroupNumbers; j ++ ) { - if ( nTGroupEqu[j] == nCurTGroupNumber ) - break; - } - if ( j == nNumDiffTGroupNumbers ) { - nTGroupRepresenative[nNumDiffTGroupNumbers] = EndPoint[i].nAtomNumber; - nTGroupEqu[nNumDiffTGroupNumbers] = EndPoint[i].nGroupNumber; - nTGEndPointNo[nNumDiffTGroupNumbers] = i; - nNumDiffTGroupNumbers ++; - } - } - } - - - /* check whether each pair belongs to the same t-group and establish the equivalence(s) */ - for ( i = 0, nNumFoundEqu=0; i < nNumDiffTGroupNumbers; i ++ ) { - for ( j = i+1; j < nNumDiffTGroupNumbers; j ++ ) { - ri = nTGroupRepresenative[i]; - rj = nTGroupRepresenative[j]; - /* both at[ri] and at[rj] are known to belong to tautomeric groups */ - GetNeutralRepsIfNeeded( &ri, &rj, at, num_atoms, EndPoint, *nNumEndPoints, cgi ); - nErr = bExistsAnyAltPath( pBNS, pBD, at, num_atoms, ri, rj, taut_mode ); - if ( IS_BNS_ERROR(nErr) ) - return nErr; - if ( 0 == nErr ) - continue; /* alt path between at[ri] and at[rj] not found */ - nCurTGroupNumber = inchi_min( nTGroupEqu[i], nTGroupEqu[j] ); - nMaxTGroupNumber = inchi_max( nTGroupEqu[i], nTGroupEqu[j] ); - for ( k = 0; k < nNumDiffTGroupNumbers; k ++ ) { - if ( nTGroupEqu[k]==nMaxTGroupNumber ) { - nTGroupEqu[k] = nCurTGroupNumber; - nNumFoundEqu ++; - } - } - for ( k = 0; k < *nNumEndPoints; k ++ ) { - if ( EndPoint[k].nEquNumber == nMaxTGroupNumber ) { - EndPoint[k].nEquNumber = nCurTGroupNumber; - } - } - } - } - if ( nNumFoundEqu ) { - /* leave in only non-equivalent representatives */ - for ( i = 1, k = 0; i < nNumDiffTGroupNumbers; i ++ ) { - for ( j = 0; j < i; j ++ ) { - if ( nTGroupEqu[j] == nTGroupEqu[i] ) { - nTGroupEqu[i] = 0; /* i > j; mark equivalent for removal*/ - break; - } - } - } - for ( i = j = 0; i < nNumDiffTGroupNumbers; i ++ ) { - if ( nTGroupEqu[i] ) { - if ( i != j ) { /* remove the marked */ - nTGroupEqu[j] = nTGroupEqu[i]; - nTGroupRepresenative[j] = nTGroupRepresenative[i]; - nTGEndPointNo[j] = nTGEndPointNo[i]; - } - j ++; - } - } - nNumDiffTGroupNumbers = j; /* number of known t-group representatives */ - } - /* collect endpoints that have not been assigned to t-groups */ - for ( i = 0, j = nNumDiffTGroupNumbers; i < *nNumEndPoints; i ++ ) { - if ( EndPoint[i].nEquNumber ) - continue; - nTGroupEqu[j] = 0; - nTGroupRepresenative[j] = EndPoint[i].nAtomNumber; - nTGEndPointNo[j] = i; - j ++; - - } - nNumTgroupNumbers = j; - nMaxEquNumber = num_atoms + 1; /* impossible atom or t-group number */ - - /* check whether each pair belongs to the same group and establish the equivalence(s) */ - for ( i = 0, nNumFoundEqu=0; i < nNumTgroupNumbers; i ++ ) { - for ( j = i+1; j < nNumTgroupNumbers; j ++ ) { - if ( nTGroupEqu[i] != nTGroupEqu[j] && (i>=nNumDiffTGroupNumbers || j>=nNumDiffTGroupNumbers) || - /* equivalence of a t-group and a non-t-group atom */ - !nTGroupEqu[i] && !nTGroupEqu[j] - /* equivalence of two non-t-group atoms */ - ) { - ri = nTGroupRepresenative[i]; - rj = nTGroupRepresenative[j]; - /*------------------------------!!!--------------------------------------------- - Explanation why GetNeutralRepsIfNeeded() may need to be changed 2004-02-27 - The change has been disabled due to undiscovered yet possibility of ambiguity - to search for neutral only among EndPoint[] in case taut-not_taut pairs - - Counterexample: O=C-NH(+)=C-NH2 - 1 2 3 - - Has already been found: 2-3 (+)-charge exchange - 1-2 tautomerism (charge removed to 3) - Now testing: 2-3 tautomerism. If not commented out, - GetNeutralRepsIfNeeded() would replace 2-3 test with 1-3 test because: - o Charge group has only one neutral and both 2 and 3 belong to it, - therefore we cannot neutralize both; search for neutral representative; - o Since 1 and 2 belong to the same t-group and 1 is neutral, - test 1-3 instead of 2-3. - This breaks our condition: - Test tautomeric H movement only between neutral atoms. - -----------------------------------------------------------------------------*/ - GetNeutralRepsIfNeeded( &ri, &rj, at, num_atoms, EndPoint, *nNumEndPoints, cgi ); - - nErr = bExistsAnyAltPath( pBNS, pBD, at, num_atoms, ri, rj, taut_mode ); - if ( IS_BNS_ERROR(nErr) ) - return nErr; - if ( nErr <= 0 ) - continue; - if ( nTGroupEqu[i] && nTGroupEqu[j] ) { - /* found equivalence of two t-groups; at least one of them must be a new one */ - nCurTGroupNumber = inchi_min( nTGroupEqu[i], nTGroupEqu[j] ); - nMaxTGroupNumber = inchi_max( nTGroupEqu[i], nTGroupEqu[j] ); - for ( k = 0; k < nNumTgroupNumbers; k ++ ) { - if ( nTGroupEqu[k]==nMaxTGroupNumber ) { - nTGroupEqu[k] = nCurTGroupNumber; - nNumFoundEqu ++; - } - } - for ( k = 0; k < *nNumEndPoints; k ++ ) { - if ( EndPoint[k].nEquNumber == nMaxTGroupNumber ) { - EndPoint[k].nEquNumber = nCurTGroupNumber; - } - } - } else - if ( nTGroupEqu[i] ) { /* extend existing t-group */ - nTGroupEqu[j] = nTGroupEqu[i]; - EndPoint[nTGEndPointNo[j]].nEquNumber = nTGroupEqu[i]; - - } else - if ( nTGroupEqu[j] ) { /* extend existing t-group */ - nTGroupEqu[i] = nTGroupEqu[j]; - EndPoint[nTGEndPointNo[i]].nEquNumber = nTGroupEqu[j]; - - } else { /* establis a new t-group */ - nTGroupEqu[i] = - nTGroupEqu[j] = nMaxEquNumber; /* assign a fict. ID to establish equivalence */ - EndPoint[nTGEndPointNo[i]].nEquNumber = - EndPoint[nTGEndPointNo[j]].nEquNumber = nMaxEquNumber; - nMaxEquNumber ++; - } - } - } - } - /* eliminate endpoints and bonds that do not belong to t-group(s) - (they have not been found connected by an alt path to any other endpoint) - */ - for ( i = 0, j = 0; i < *nNumEndPoints; i ++ ) { - if ( EndPoint[i].nEquNumber ) { -#if ( IGNORE_SINGLE_ENDPOINTS == 1 ) /* 1-28-2003 */ - for ( k = 0, nNumFoundEqu = 0; k < *nNumEndPoints; k ++ ) { - nNumFoundEqu += (EndPoint[i].nEquNumber == EndPoint[k].nEquNumber); - } - if ( nNumFoundEqu <= 1 ) { /* one time it is equal to itself when i == k above */ - /* if EndPoint[i] is not "equivalent" to any other EndPoint then ignore it */ - continue; - } -#endif - if ( i != j ) { /* save endpoints that are found to be connected to other endpoints by alt paths */ - EndPoint[j] = EndPoint[i]; - BondPos[j] = BondPos[i]; - } - j ++; - } - } - -#if ( IGNORE_SINGLE_ENDPOINTS != 1 ) /* 1-28-2003 */ - /* Do not allow a centerpoint to have only one tautomeric bond */ - /* Hack: we may have only one centerpoint */ - /* BondPos[*].nAtomNumber are centerpoints */ - if ( j == 1 ) { - /* check if there exist other centerpoint neighbors - * connected to it by another tautomeric-bond - */ - for ( i = 0, k = 0; i < at[BondPos[0].nAtomNumber].valence; i ++ ) { - k += ( i != BondPos[0].neighbor_index && - BOND_TAUTOM == (at[BondPos[0].nAtomNumber].bond_type[i] & ~BOND_MARK_ALL)); - } - if ( !k ) { - j = 0; - } - } -#endif - - *nNumEndPoints = *nNumBondPos = j; - return j; - -} - - - -/*****************************************************************************/ -/*#if ( MOVE_CHARGES == 1 ) */ /* { */ -/*****************************************************************************/ - -/**********************************************/ -/* */ -/* definitions for positive ion recognition */ -/* */ -/**********************************************/ - -/*****************************************************************************/ -typedef struct tagChargeType { /* meaning see in bCanBeACPoint() */ - char elname[3]; - S_CHAR charge; - S_CHAR neutral_valence; - S_CHAR neutral_bonds_valence; /* valence of a neutral atom */ - S_CHAR cChangeValence; /* charge increases valence by this value */ - S_CHAR cChargeType; /* different types are treated separately */ - S_CHAR num_bonds; /* added 02-06-2005 */ -} CHARGE_TYPE; - -CHARGE_TYPE CType[] = { - { "N\0", 1, 3, 3, 1, 0, 0 }, - { "P\0", 1, 3, 3, 1, 1, 0 }, -#if ( ADD_MOVEABLE_O_PLUS == 1 ) - { "O\0", 1, 2, 2, 1, 2, 2 }, /* added 02-06-2005 */ - { "S\0", 1, 2, 2, 1, 3, 2 }, /* added 03-18-2005 */ - { "Se", 1, 2, 2, 1, 4, 2 }, /* added 03-18-2005 */ - { "Te", 1, 2, 2, 1, 5, 2 }, /* added 03-18-2005 */ -#endif -}; - -/* bits */ - -#define C_SUBTYPE_CHARGED 0 -#define C_SUBTYPE_p_DONOR 1 /* new */ -#define C_SUBTYPE_p_ACCEPT 2 /* new */ -#define C_SUBTYPE_H_ACCEPT 4 -#define C_SUBTYPE_H_DONOR 8 -#define C_SUBTYPE_NEUTRAL 16 - -/* make sure any C_SUBTYPE_CHARGED_... < any C_SUBTYPE_NEUTRAL_... */ -/* charged */ -#define C_SUBTYPE_CHARGED_NON_TAUT (C_SUBTYPE_CHARGED) -#define C_SUBTYPE_CHARGED_p_DONOR (C_SUBTYPE_CHARGED|C_SUBTYPE_p_DONOR) -#define C_SUBTYPE_CHARGED_H_ACCEPT (C_SUBTYPE_CHARGED|C_SUBTYPE_H_ACCEPT) -#define C_SUBTYPE_CHARGED_H_ACCEPT_p_DONOR (C_SUBTYPE_CHARGED|C_SUBTYPE_H_ACCEPT|C_SUBTYPE_p_DONOR) -#define C_SUBTYPE_CHARGED_H_DONOR (C_SUBTYPE_CHARGED|C_SUBTYPE_H_DONOR |C_SUBTYPE_p_DONOR) -/* neutral */ -#define C_SUBTYPE_NEUTRAL_NON_TAUT (C_SUBTYPE_NEUTRAL) -#define C_SUBTYPE_NEUTRAL_H_ACCEPT (C_SUBTYPE_NEUTRAL|C_SUBTYPE_H_ACCEPT) -#define C_SUBTYPE_NEUTRAL_H_ACCEPT_p_ACCEPT (C_SUBTYPE_NEUTRAL|C_SUBTYPE_H_ACCEPT|C_SUBTYPE_p_ACCEPT) -#define C_SUBTYPE_NEUTRAL_H_DONOR (C_SUBTYPE_NEUTRAL|C_SUBTYPE_H_DONOR) - -#define NUM_C_TYPES (int)(sizeof( CType )/sizeof(CType[0])) -/*****************************************************************************/ - - - -/*****************************************************************************/ -int bCanBeACPoint( inp_ATOM *at, S_CHAR cCharge, S_CHAR cChangeValence, S_CHAR neutral_bonds_valence, - S_CHAR neutral_valence, S_CHAR nEndpointValence, S_CHAR *cChargeSubtype ) -{ - int nChangeValence; - int nNumBonds; - int nBondsValence; - int bNegCharge = (at->charge == -1); /* add fict. bonds to (-) 2004-02-24*/ - if ( at->charge == cCharge && at->valence == at->chem_bonds_valence && at->num_H ) { - /* proton donors candidates >NH(+)-, >NH2(+), -NH3(+), >OH(+), -OH2(+) */ - /* charged, added p-transfer -- 01-28-2004 */ - nChangeValence = at->charge * cChangeValence; /* +1 or -1; currently only +1 */ - nBondsValence = at->chem_bonds_valence + at->num_H; - if ( nBondsValence == neutral_bonds_valence + nChangeValence && nEndpointValence ) { - *cChargeSubtype = C_SUBTYPE_CHARGED_p_DONOR; /* ignore Phosphorus p-donors for now */ - } - return 0; - } else - if ( at->charge == cCharge && at->valence < at->chem_bonds_valence ) { - /* the requirement at->valence < at->chem_bonds_valence rejects - candidates >NH(+)-, >NH2(+), -NH3(+), >N(+)<, >OH(+), -OH2(+), >O(+)- - Moveable charge requires double bonds; these ions have no double bonds - */ - - /* charged */ - nChangeValence = at->charge * cChangeValence; /* +1 or -1; currently only +1 */ - nBondsValence = at->chem_bonds_valence + at->num_H; - nNumBonds = at->valence + at->num_H; - if ( nBondsValence == neutral_bonds_valence + nChangeValence ) { /* known valence */ - if ( nNumBonds == neutral_valence ) { - /* non-tautomeric: >N(+)=, =O(+)- - possibly tautomeric donor: =NH(+)-, =NH2(+), =OH(+) */ - if ( at->valence == neutral_valence || !nEndpointValence ) { - /* non-tautomeric: >N(+)=, =O(+)-; any suitable P+: >P(+)=, =PH(+)-, =PH2(+) */ - *cChargeSubtype = C_SUBTYPE_CHARGED_NON_TAUT; - } else { - /* possibly tautomeric donor: =NH(+)-, =NH2(+), =OH(+) */ - *cChargeSubtype = C_SUBTYPE_CHARGED_H_DONOR; - } - return 1; - } - if ( nNumBonds == neutral_valence - 1 ) { - /* possibly tutomeric acceptor: =N(+)=, #N(+)-, #NH(+), #O(+) */ - if ( nEndpointValence ) { - *cChargeSubtype = at->num_H? C_SUBTYPE_CHARGED_H_ACCEPT_p_DONOR : C_SUBTYPE_CHARGED_H_ACCEPT; - } else { - /* =P(+)=, #P(+)-, #PH(+) */ - *cChargeSubtype = C_SUBTYPE_CHARGED_NON_TAUT; - } - return 1; /* charge type, charged */ - } - } - - } else - if ( at->charge == 0 || bNegCharge ) { - /* neutral atom or anion, all bonds are single */ - nBondsValence = at->chem_bonds_valence + at->num_H + bNegCharge; /* add fict. bonds to (-) 2004-02-24*/ - nNumBonds = at->valence + at->num_H + bNegCharge; /* add fict. bonds to (-) 2004-02-24*/ - if ( nBondsValence == neutral_bonds_valence ) { - if ( nNumBonds == neutral_valence ) { - /* only single bonds: >N-, >NH, -NH2, -O-, -OH, >P- >PH -PH2 */ - /* >N(-), -NH(-), -O(-). >P(-) -PH(-) */ - if ( at->valence == neutral_valence || !nEndpointValence ) { - /* >N-, -O-, any P(3 single bonds): >P- >PH -PH2 */ - *cChargeSubtype = C_SUBTYPE_NEUTRAL_NON_TAUT; - } else - if ( at->valence < neutral_valence /*&& nEndpointValence */ ) { - /* num_H > 0: >NH -NH2 -OH */ - /* num_H = 0: none C_SUBTYPE_NEUTRAL_H_ACCEPT for now */ - *cChargeSubtype = at->num_H? C_SUBTYPE_NEUTRAL_H_DONOR: C_SUBTYPE_NEUTRAL_H_ACCEPT; - } else { - return 0; - } - return 1; /* charge type, neutral */ - } - if ( nNumBonds == neutral_valence - 1 ) { - /* possibly tautomeric acceptor =N-, =NH, =O or non-taut =P-, =PH */ - if ( nEndpointValence ) { - /* =N-, =NH, =O */ - *cChargeSubtype = C_SUBTYPE_NEUTRAL_H_ACCEPT_p_ACCEPT; - } else { - /* =P-, =PH */ - *cChargeSubtype = C_SUBTYPE_NEUTRAL_NON_TAUT; - } - return 1; /* charge type, (+) => neutral */ - } - } - } - return 0; -} - - - -/*****************************************************************************/ -int GetChargeType( inp_ATOM *atom, int iat, S_CHAR *cChargeSubtype ) -{ - int i, n; - S_CHAR nEndpointValence; - inp_ATOM *at = atom + iat; - - *cChargeSubtype = 0; - /* ignore ion pairs and charges != 1 */ - if ( abs(at->charge) == 1 ) { - for ( i = 0; i < at->valence; i ++ ) { - n = at->neighbor[i]; - /* allow negatively charged tautomeric neighbors 2004-02-26 */ - if ( abs(atom[n].charge + at->charge) < abs(atom[n].charge - at->charge) && !atom[n].endpoint ) { - return -1; /* charges have different signs */ - } - } - } else - if ( at->charge ) { - return -1; /* abs(charge) != 1 */ - } - /* find candidates */ - for ( i = 0; i < NUM_C_TYPES; i ++ ) { - if ( !strcmp( at->elname, CType[i].elname ) && - (!CType[i].num_bonds || CType[i].num_bonds==at->valence && at->nNumAtInRingSystem >= 5) ) { - nEndpointValence = (S_CHAR)get_endpoint_valence(at->el_number ); - if ( bCanBeACPoint( at, CType[i].charge, CType[i].cChangeValence, CType[i].neutral_bonds_valence, - CType[i].neutral_valence, nEndpointValence, cChargeSubtype ) ) { - return CType[i].cChargeType; - } - } - } - return -1; -} - - -/*****************************************************************************/ -int CmpCCandidates( const void *a1, const void *a2 ) -{ - const C_CANDIDATE *c1 = (const C_CANDIDATE *)a1; - const C_CANDIDATE *c2 = (const C_CANDIDATE *)a2; - int ret; - if ( ret = (int)c1->type - (int)c2->type ) - return ret; - if ( ret = (int)c1->subtype - (int)c2->subtype ) - return ret; - ret = (int)c1->atnumber - (int)c2->atnumber; - return ret; -} - - -/*****************************************************************************/ -int RegisterCPoints( C_GROUP *c_group, int *pnum_c, int max_num_c, T_GROUP_INFO *t_group_info, - int point1, int point2, int ctype, inp_ATOM *at, int num_atoms ) -{ - int num_c = *pnum_c, i, i1, i2; - AT_NUMB nGroupNumber = 0, nNewGroupNumber; - - - if ( at[point1].c_point == at[point2].c_point ) { - if ( at[point1].c_point ) - return 0; - memset( c_group+num_c, 0, sizeof(c_group[0]) ); - if ( num_c < max_num_c ) { - c_group[num_c].num[0] = CHARGED_CPOINT(at,point1) + CHARGED_CPOINT(at, point2); - c_group[num_c].num_CPoints += 2; - c_group[num_c].cGroupType = ctype; - /* get next available c-group number */ - for ( i = 0; i < num_c; i ++ ) { - if ( nGroupNumber < c_group[i].nGroupNumber ) - nGroupNumber = c_group[i].nGroupNumber; - } - nGroupNumber ++; - c_group[num_c].nGroupNumber = - at[point1].c_point = - at[point2].c_point = nGroupNumber; - *pnum_c = num_c+1; - /* count protons */ - if ( at[point1].num_H ) { - c_group[num_c].num[1] ++; - } else - if ( at[point2].num_H ) { - c_group[num_c].num[1] ++; - } else - if ( (at[point1].endpoint || at[point2].endpoint) && t_group_info && t_group_info->t_group && t_group_info->num_t_groups ) { - /* !!! add later !!! */ - } - - - return 1; - } - return BNS_CPOINT_ERR; /* overflow */ - } - if ( at[point1].c_point > at[point2].c_point ) { - /* make sure at[point1].c_point < at[point2].c_point */ - i = point1; - point1 = point2; - point2 = i; - } - if ( !at[point1].c_point ) { - /* add a new c-endpoint to an existing c-group */ - nGroupNumber = at[point2].c_point; - for ( i = 0; i < num_c; i ++ ) { - if ( nGroupNumber == c_group[i].nGroupNumber ) { - at[point1].c_point = at[point2].c_point; - c_group[i].num_CPoints ++; - c_group[i].num[0] += CHARGED_CPOINT(at,point1); - return 1; - } - } - return BNS_CPOINT_ERR; /* program error: c-group not found */ - } else { - /* merge two c-groups */ - nNewGroupNumber = at[point1].c_point; - nGroupNumber = at[point2].c_point; - for ( i = 0, i1=i2=-1; i < num_c && (i1 < 0 || i2 < 0); i ++ ) { - if ( nNewGroupNumber == c_group[i].nGroupNumber ) { - i1 = i; - continue; - } - if ( nGroupNumber == c_group[i].nGroupNumber ) { - i2 = i; - continue; - } - } - if ( i1 < 0 || i2 < 0 ) { - return BNS_CPOINT_ERR; /* at least one not found */ - } - - c_group[i1].num[0] += c_group[i2].num[0]; - c_group[i1].num_CPoints += c_group[i2].num_CPoints; - num_c --; - if ( num_c > i2 ) { - memmove( c_group+i2, c_group+i2+1, ( num_c - i2)*sizeof(c_group[0]) ); - } - *pnum_c = num_c; - /* renumber c-groups */ - for ( i = 0; i < num_c; i ++ ) { - if ( c_group[i].nGroupNumber > nGroupNumber ) { - c_group[i].nGroupNumber --; - } - } - /* renumber c-points */ - for ( i = 0; i < num_atoms; i ++ ) { - if ( at[i].c_point > nGroupNumber ) { - at[i].c_point --; - } else - if ( at[i].c_point == nGroupNumber ) { - at[i].c_point = nNewGroupNumber; - } - } - return 1; - } -} - - - -/*****************************************************************************/ -int MarkChargeGroups(inp_ATOM *at, int num_atoms, - C_GROUP_INFO *c_group_info, T_GROUP_INFO *t_group_info, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD) -{ - int nNumChanges = 0; - - if ( c_group_info && c_group_info->c_candidate && c_group_info->max_num_candidates > 0 ) { - int i, i1, i2, i3, j, num_tested; - C_CANDIDATE *c_candidate = c_group_info->c_candidate; - int nMaxNumCandidates = c_group_info->max_num_candidates; - int nNumCandidates = c_group_info->num_candidates; - S_CHAR c_type, c_subtype; - int iat1, iat2, ret, nDelta; - - if ( nNumCandidates == -1 ) - { - nNumCandidates = 0; /* 2004-02-26 they could appear after t-group discovery */ - /*return 0;*/ - } - if ( nNumCandidates == 0 ) - { - for ( i = 0, nNumCandidates = 0; i < num_atoms; i ++ ) - { - if ( 0 <= (c_type = GetChargeType( at, i, &c_subtype )) ) - { - if ( nNumCandidates >= nMaxNumCandidates ) - { - return BNS_VERT_EDGE_OVFL; - } - c_candidate[nNumCandidates].atnumber = i; - c_candidate[nNumCandidates].type = c_type; - c_candidate[nNumCandidates].subtype = c_subtype; - nNumCandidates ++; - } - } - if ( nNumCandidates <= 1 ) - { - c_group_info->num_candidates = -1; /* no candidate exists */ - return 0; - } - } - /* sorting keys: (1) atom type (N,P); (2) uncharged=16/charged=0; (3) other; - atom-charged-N .... i1 - ... - atom-charged-N - atom-neutral-N .... i2 - ... - atom-neutral-N - atom-charged-P .... i3 ... i1 - ... - atom-charged-P - atom-neutral-P ........... i2 - ... - atom-neutral-P - end. ........... i3 - */ - qsort(c_candidate, nNumCandidates, sizeof(c_candidate[0]), CmpCCandidates); - - i1 = 0; - num_tested = 0; - nDelta = 0; - - while ( i1 < nNumCandidates ) - { - - /* the the first charged candidate of a new atom type */ - for (; i1 < nNumCandidates && (c_candidate[i1].subtype & C_SUBTYPE_NEUTRAL); i1 ++) - ; - if ( i1 == nNumCandidates ) - break; /* not found */ - - /* bypass other charged candidates of the same atom type */ - for ( i2 = i1+1; i2 < nNumCandidates && - c_candidate[i2].type == c_candidate[i1].type && - !(c_candidate[i2].subtype & C_SUBTYPE_NEUTRAL); i2++ ) - ; - if ( i2 == nNumCandidates ) - break; /* no neutral candidates */ - - /* find next to the last neutral candidate of the same atom type */ - for ( i3 = i2; i3 < nNumCandidates && - c_candidate[i3].type == c_candidate[i1].type; i3 ++ ) - ; - - if ( i3 == i2 ) - { - /* no neutral candidates found */ - if ( i2 < nNumCandidates ) - { - i1 = i3; - continue; /* move to the next atom type */ - } - break; /* nothing more to do */ - } - - /* found charged candidates: i1...i2-1; neutral candidates: i2...i3-1 */ - for ( i = i1; i < i2; i ++ ) - { - iat1 = c_candidate[i].atnumber; - for ( j = i2; j < i3; j ++ ) - { - /* check alt path at[iat1]=-=-...-at[iat2]; at[iat1] is charged, at[iat2] is neutral */ - num_tested ++; - iat2 = c_candidate[j].atnumber; - if ( at[iat1].c_point && at[iat1].c_point == at[iat2].c_point ) - continue; - ret = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, iat1, iat2, ALT_PATH_MODE_CHARGE ); - if ( IS_BNS_ERROR( ret ) ) - { - return ret; - } - if ( ret & 1 ) - { - nDelta = (ret & ~3) >> 2; - nNumChanges += (ret & 2); - ret = RegisterCPoints( c_group_info->c_group, &c_group_info->num_c_groups, - c_group_info->max_num_c_groups, t_group_info, - iat1, iat2, c_candidate[i1].type, at, num_atoms ); - if ( IS_BNS_ERROR( ret ) ) - { - return ret; - } - if ( nDelta ) - { - goto quick_exit; - } - } - } - } - i1 = i3; - } -quick_exit: - if ( c_group_info->num_candidates == 0 ) - { - /* first time: initialize */ - c_group_info->num_candidates = num_tested? nNumCandidates : -1; /* no candidate exists */ - } - - } - return nNumChanges; -} - - - -/*****************************************************************************/ -int GetSaltChargeType(inp_ATOM *at, int at_no, T_GROUP_INFO *t_group_info, int *s_subtype ) -{ - static int el_number_C = 0; - static int el_number_O = 0; - static int el_number_S = 0; - static int el_number_Se = 0; - static int el_number_Te = 0; - -/* - type (returned value): - -1 => ignore - 0 => oxygen - subtype: - 1 = SALT_DONOR_H => has H - 2 = SALT_DONOR_Neg => has (-) charge - 4 = SALT_ACCEPTOR => may be an acceptor of H or (-), but not necessarily - - O-atom should be: - - a terminal atom - - connected to unsaturated, uncharged, non-radical atom C that has chemical valence 4: - H-donors: =CH-OH, =C(-X)-OH - possible H-acceptors: -CH=O, >C=O - H-acceptors are true if O is tautomeric -*/ - int iC, tg, i, type; - /* one-time initialization */ - if ( !el_number_O ) { - el_number_C = get_periodic_table_number( "C" ); - el_number_O = get_periodic_table_number( "O" ); - el_number_S = get_periodic_table_number( "S" ); - el_number_Se = get_periodic_table_number( "Se" ); - el_number_Te = get_periodic_table_number( "Te" ); - } - *s_subtype = 0; /* initialize the output */ - /* check whether it is a candidate */ - if ( at[at_no].valence != 1 || - at[at_no].radical && at[at_no].radical != RADICAL_SINGLET || - at[at_no].charge < -1 || - at[at_no].charge > 0 && !at[at_no].c_point ) { - return -1; - } - - if ( at[at_no].el_number == el_number_O || - at[at_no].el_number == el_number_S || - at[at_no].el_number == el_number_Se || - at[at_no].el_number == el_number_Te ) { - type = 0; /* terminal oxygen atom, needs more to be checked... */ - } else { - type = -1; /* ignore this atom */ - } - - if ( type < 0 || - at[at_no].chem_bonds_valence + at[at_no].num_H != - get_el_valence(at[at_no].el_number, at[at_no].charge, 0) ) { - return -1; /* non-standard valence or not an oxygen */ - } - - iC = at[at_no].neighbor[0]; - -#if ( SALT_WITH_PROTONS == 1 ) - if ( at[iC].el_number != el_number_C || - at[iC].chem_bonds_valence + at[iC].num_H != 4 || /* allow =C(H)-OH or -C(H)=O */ - at[iC].charge || - at[iC].radical && at[iC].radical != RADICAL_SINGLET || - at[iC].valence == at[iC].chem_bonds_valence ) { - return -1; /* oxigen is connected to a wrong atom */ - } -#else - if ( at[iC].el_number != el_number_C || - at[iC].num_H || - at[iC].chem_bonds_valence != 4 || /* allow only no H on C */ - at[iC].charge || - at[iC].radical && at[iC].radical != RADICAL_SINGLET || - at[iC].valence == at[iC].chem_bonds_valence ) { - return -1; /* oxigen is connected to a wrong atom */ - } -#endif - if ( (tg = at[at_no].endpoint) && t_group_info && t_group_info->t_group ) { - /* O-atom is in a tautomeric group */ - for ( i = 0; i < t_group_info->num_t_groups; i ++ ) { - if ( tg == t_group_info->t_group[i].nGroupNumber ) { - /* - t_group_info->t_group[i].num[0] = number of attached H-atoms and negative charges - t_group_info->t_group[i].num[1] = number of attached negative charges - */ - if ( t_group_info->t_group[i].num[0] > t_group_info->t_group[i].num[1] ) { - *s_subtype |= SALT_DONOR_H; /* has H */ - } - if ( t_group_info->t_group[i].num[1] ) { - *s_subtype |= SALT_DONOR_Neg; /* has (-) */ - } - *s_subtype |= SALT_ACCEPTOR; /* there is always an acceptor in a t-group */ - return type; - } - } - return -1; /* error: t-group not found */ - } - /* O is not not in a tautomeric group */ - /* assume valence(O-) < valence(O) < valence(O+) */ - if ( at[at_no].charge == -1 ) { - *s_subtype |= SALT_DONOR_Neg; /* has (-) */ - } - if ( at[at_no].charge <= 0 && at[at_no].num_H ) { - *s_subtype |= SALT_DONOR_H; /* has H */ - } - if ( at[at_no].charge == 0 && at[at_no].chem_bonds_valence == 2 ) { - *s_subtype |= SALT_ACCEPTOR; - } - /* since O cannot be a charge point, the following cannot happen: */ - if ( at[at_no].charge == 1 && at[at_no].c_point && at[at_no].chem_bonds_valence == 2 && at[at_no].num_H ) { - *s_subtype |= SALT_DONOR_H; /* has H */ - } - return type; -} - - - -/*****************************************************************************/ -int bDoNotMergeNonTautAtom(inp_ATOM *at, int at_no) -{ - static int el_number_N = 0; - - if ( !el_number_N ) { - el_number_N = get_periodic_table_number( "N" ); - } - if ( at[at_no].el_number == el_number_N ) - { - return 1; - } - return 0; -} - - - -/*****************************************************************************/ -int GetOtherSaltChargeType( inp_ATOM *at, int at_no, T_GROUP_INFO *t_group_info, int *s_subtype, int bAccept_O ) -{ - /* static int el_number_C = 0; */ - /* static int el_number_N = 0; */ - static int el_number_O = 0; - static int el_number_S = 0; - static int el_number_Se = 0; - static int el_number_Te = 0; - -/* - type (returned value): - -1 => ignore - 1 => not an oxygen - subtype: - 1 = SALT_DONOR_H => has H - 2 = SALT_DONOR_Neg => has (-) charge - 4 = SALT_ACCEPTOR => may be an acceptor of H or (-), but not necessarily - - the atom should be: - - a tautomeric endpoint atom - - connected to possible centerpoint atom - - another description of the atom searched here: - - any possibly tautomeric atom adjacent to a possibly centerpoint - that has at least one double bond (possibly if positively charged); - if eif.cAcceptor then the bond between the atom and the centerpoint must be possibly double - if eif.cAcceptor then the bond must be possibly single - Donors that belong to a t-group are also acceptors - - -*/ - int tg, i, j, type, endpoint_valence, num_centerpoints, bond_type, centerpoint; - ENDPOINT_INFO eif; - /* one-time initialization */ - if ( !el_number_O && !bAccept_O ) { - /* el_number_C = get_periodic_table_number( "C" ); */ - /* el_number_N = get_periodic_table_number( "N" ); */ - el_number_O = get_periodic_table_number( "O" ); - el_number_S = get_periodic_table_number( "S" ); - el_number_Se = get_periodic_table_number( "Se" ); - el_number_Te = get_periodic_table_number( "Te" ); - } - *s_subtype = 0; /* initialize the output */ - if ( !bAccept_O /* only N */ && - (at[at_no].el_number == el_number_O || - at[at_no].el_number == el_number_S || - at[at_no].el_number == el_number_Se || - at[at_no].el_number == el_number_Te ) ) { - return -1; /* we are not looking for oxygen here */ - } - - type = 1; - if ( !(endpoint_valence = nGetEndpointInfo( at, at_no, &eif )) ) { - return -1; /* not a possible endpoint */ - } else { - /* at[at_no] is not not in a tautomeric group; use eif previously filled out by nGetEndpointInfo */ - /* check whether there is adjacent atom-candidate for a centerpoint */ - num_centerpoints = 0; - for ( j = 0; j < at[at_no].valence; j ++ ) { - bond_type = (int)at[at_no].bond_type[j] & BOND_TYPE_MASK; - centerpoint = (int)at[at_no].neighbor[j]; /* a centerpoint candidate */ - if ( ( eif.cAcceptor && (bond_type == BOND_DOUBLE || - bond_type == BOND_ALTERN || /* possibly double */ - bond_type == BOND_ALT12NS || - bond_type == BOND_TAUTOM ) || - eif.cDonor && (bond_type == BOND_SINGLE || - bond_type == BOND_ALTERN || /* possibly single */ - bond_type == BOND_ALT12NS || - bond_type == BOND_TAUTOM ) ) && - (at[centerpoint].chem_bonds_valence > at[centerpoint].valence || - /* check for possible endpoint added 2004-02-24 */ - at[centerpoint].chem_bonds_valence == at[centerpoint].valence && - (at[centerpoint].endpoint || at[centerpoint].c_point) /* tautomerism or charge may increment at[centerpoint].chem_bonds_valence*/ ) && - is_centerpoint_elem( at[centerpoint].el_number ) ) { - num_centerpoints ++; - break; /* at least one possibly centerpoint neighbor has been found */ - } - } - if ( !num_centerpoints ) { - return -1; - } - /* moved here from just after "type = 1;" line 2004-02-26 */ - if ( (tg = at[at_no].endpoint) && t_group_info && t_group_info->t_group ) { - /* atom is in a tautomeric group */ - for ( i = 0; i < t_group_info->num_t_groups; i ++ ) { - if ( tg == t_group_info->t_group[i].nGroupNumber ) { - /* - t_group_info->t_group[i].num[0] = number of attached H-atoms and negative charges - t_group_info->t_group[i].num[1] = number of attached negative charges - */ - if ( t_group_info->t_group[i].num[0] > t_group_info->t_group[i].num[1] ) { - *s_subtype |= SALT_DONOR_H; /* has H */ - } - if ( t_group_info->t_group[i].num[1] ) { - *s_subtype |= SALT_DONOR_Neg; /* has (-) */ - } - *s_subtype |= SALT_ACCEPTOR; /* there is always an acceptor in a t-group */ - return type; - } - } - return -1; /* error: t-group not found */ - } - - if ( eif.cAcceptor ) { - *s_subtype |= SALT_ACCEPTOR; - } - if ( eif.cDonor ) { - if ( at[at_no].charge == -1 ) { - *s_subtype |= SALT_DONOR_Neg; /* has (-) */ - } - if ( at[at_no].num_H ) { - *s_subtype |= SALT_DONOR_H; /* has H */ - } - } - } - return type; -} - - - -/*****************************************************************************/ -int GetOtherSaltType( inp_ATOM *at, int at_no, int *s_subtype ) -{ - static int el_number_C = 0; - /* static int el_number_N = 0; */ - /* static int el_number_O = 0; */ - static int el_number_S = 0; - static int el_number_Se = 0; - static int el_number_Te = 0; - -/* - type (returned value): - -1 => ignore - 2 => found: SH - proton donor -CH2-SH, >CH-SH, >C< S(-) - proton acceptor -CH2-S(-), >CH-S(-), >C< - subtype: - 1 = SALT_DONOR_H => has H - 2 = SALT_DONOR_Neg => has (-) charge - 4 = SALT_ACCEPTOR => may be an acceptor of H or (-), but not necessarily - - non-O-atom should be: - - a tautomeric endpoint atom - - connected to possible middle point atom -*/ - int type, endpoint_valence, bond_type, centerpoint; - ENDPOINT_INFO eif; - - if ( at[at_no].valence != 1 || at[at_no].chem_bonds_valence != 1 || - 1 != (at[at_no].num_H==1) + (at[at_no].charge==-1) ) { - return -1; - } - /* one-time initialization */ - if ( !el_number_S ) { - el_number_C = get_periodic_table_number( "C" ); - /* el_number_N = get_periodic_table_number( "N" ); */ - /* el_number_O = get_periodic_table_number( "O" ); */ - el_number_S = get_periodic_table_number( "S" ); - el_number_Se = get_periodic_table_number( "Se" ); - el_number_Te = get_periodic_table_number( "Te" ); - } - *s_subtype = 0; /* initialize the output */ - if ( !(at[at_no].el_number == el_number_S || - at[at_no].el_number == el_number_Se || - at[at_no].el_number == el_number_Te ) ) { - return -1; /* we are not looking for oxygen here */ - } - - type = 2; /* non-tautomeric p-donor or acceptor: C-SH, C-S(-) */ - - if ( !(endpoint_valence = nGetEndpointInfo( at, at_no, &eif )) || - eif.cMoveableCharge && !at[at_no].c_point || !eif.cDonor || eif.cAcceptor ) { - return -1; /* not a possible -SH or -S(-) */ - } else { - /* at[at_no] is not not in a tautomeric group; use eif previously filled out by nGetEndpointInfo */ - /* check whether there is adjacent atom-candidate for a centerpoint */ - centerpoint = (int)at[at_no].neighbor[0]; - bond_type = (int)at[at_no].bond_type[0] & BOND_TYPE_MASK; - if ( at[centerpoint].el_number != el_number_C || - at[centerpoint].charge || - at[centerpoint].radical && at[centerpoint].radical != RADICAL_SINGLET || - at[centerpoint].valence != at[centerpoint].chem_bonds_valence ) { - return -1; /* not a carbon with all single bonds */ - } - if ( at[at_no].num_H == 1 ) { - *s_subtype |= SALT_p_DONOR; - } else - if ( at[at_no].charge == -1 ) { - *s_subtype |= SALT_p_ACCEPTOR; - } else { - return -1; - } - } - return type; -} - -/********************************************************************************************************/ -/* new version: merge all, check alt paths, then unmerge unreachable O-atoms if any */ -/* Check for oxygen negative charge-H tautomerism (Salts) - allowed long-range tautomerism; more than one H or (-) can be moved, for example: - HO-C=C-O(-) O=C-C=O - / \ / \ - R R R R - | | => | | - R' R' R' R' - \ / \ / - O=C-C=O HO-C=C-O(-) - - To check: - - | | - -add all possible HO-C=, O=C, (-)O-C= (including all containing O t-groups) into one t-group; - -temporarily disconnect one of previously not belonging to any t-group O-atoms from the one t-group; - -find whether there is an alt path allowing H or (-) to migrate - from the temp. disconnected O to any one left in the group. - If the alt path does not exist then the temp. disconnected atom does not - participate in the H/(-) migrartion and it will be unmarked/unmerged. - -*/ - - - -/*****************************************************************************/ -int comp_candidates( const void *a1, const void *a2 ) -{ - const S_CANDIDATE *s1 = (const S_CANDIDATE *)a1; - const S_CANDIDATE *s2 = (const S_CANDIDATE *)a2; - int ret; - if ( s1->type >= 0 /* enabled < */ && s2->type < 0 /* disabled */ ) - return -1; /* enabled goes first */ - if ( s1->type < 0 /* disabled > */ && s2->type >= 0 /* enabled */ ) - return 1; - if ( s1->endpoint && !s2->endpoint ) - return -1; /* tautomeric goes first; only tautomeric may be disabled */ - if ( !s1->endpoint && s2->endpoint ) - return 1; /* tautomeric goes first; only tautomeric may be disabled */ - if ( s1->endpoint && s2->endpoint && (ret = (int)s1->endpoint - (int)s2->endpoint) ) { - return ret; - } - return (int)s1->atnumber - (int)s2->atnumber; -} - - - -/*****************************************************************************/ -int MarkSaltChargeGroups2 ( inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, - T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, - struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD ) -{ -/* BNS_EDGE_FORBIDDEN_TEMP */ -#define ALT_PATH_FOUND (MAX_ATOMS+1) -#define NO_ENDPOINT (MAX_ATOMS+2) /* the two defines must be different */ -#define DISABLE_CANDIDATE 10 -#define cPAIR(a,b) cPair[a+b*nNumLeftCandidates] -#define ACCEPTOR_PAIR 1 -#define DONOR_PAIR 2 - - int nNumChanges = 0, nNumOtherChanges = 0, nNumAcidicChanges = 0, nTotNumChanges = 0; - S_CHAR *cPair = NULL; - T_ENDPOINT *EndPoint = NULL; - if ( s_group_info && s_group_info->s_candidate && s_group_info->max_num_candidates > 0 ) { - int i, j, i1, j1; - S_CANDIDATE *s_candidate = s_group_info->s_candidate; - int nMaxNumCandidates = s_group_info->max_num_candidates; - int nNumCandidates = s_group_info->num_candidates; - int nNumOtherCandidates = s_group_info->num_other_candidates; - int nNumPOnlyCandidates = s_group_info->num_p_only_candidates; - int nNumLeftCandidates = 0; - int nNumMarkedCandidates = 0; - int s_type, s_subtype; - int ret, nDelta; - int bHardAddedRemovedProtons = t_group_info && (t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT); - - int s_subtype_all = 0; - int nDonorPairs, nAcceptorPairs, nCurDonorPairs, nCurAcceptorPairs, bAlreadyTested; -/* - ENDPOINT_INFO eif; -*/ - -#if ( IGNORE_TGROUP_WITHOUT_H == 1 ) - int bTGroupHasNegativeChargesOnly = 1; -#endif - /*return 0;*/ /* debug only */ - - i1 = -1; - - if ( nNumCandidates <= -2 || !t_group_info || !t_group_info->t_group ) { - return 0; - } - /*************************************************************************/ - /* find all candidates including those with differen s_type (other type) */ - /*************************************************************************/ - for ( i = 0, nNumCandidates = nNumOtherCandidates = nNumPOnlyCandidates = 0; i < num_atoms; i ++ ) { - if ( 0 == (s_type = GetSaltChargeType( at, i, t_group_info, &s_subtype )) || - /* -C=O or =C-OH, O = S, Se, Te */ - 1 == (s_type = GetOtherSaltChargeType( at, i, t_group_info, &s_subtype, 1/* bAccept_O*/ )) || - /* =Z-MH or -Z=M, Z = centerpoint, M = endpoint, other than above */ - 2 == (s_type = GetOtherSaltType( at, i, &s_subtype ) ) || - ( bHardAddedRemovedProtons && 4 == (s_type = bIsHardRemHCandidate( at, i, &s_subtype ) ) ) - /* >C-SH, >C-S(-); S=S,Se,Te */ - ) { - - if ( nNumCandidates >= nMaxNumCandidates ) { - return BNS_VERT_EDGE_OVFL; - } - s_candidate[nNumCandidates].atnumber = i; - s_candidate[nNumCandidates].type = s_type; - s_candidate[nNumCandidates].subtype = s_subtype; - s_candidate[nNumCandidates].endpoint = at[i].endpoint; - nNumCandidates ++; - nNumOtherCandidates += (1 == s_type); - s_subtype_all |= s_subtype; - i1 = i; /* save a representative of a tautomeric group */ - } - } - - if ( nNumCandidates <= 1 || /* TG_FLAG_ALLOW_NO_NEGTV_O <=> CHARGED_SALTS_ONLY=0 */ - !(s_subtype_all & SALT_ACCEPTOR) || - (((t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O) || - (t_group_info->bTautFlagsDone & TG_FLAG_FOUND_SALT_CHARGES_DONE) || - (t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT)) ? - !(s_subtype_all & (SALT_DONOR)): - (!(s_subtype_all & SALT_DONOR_Neg) || nNumOtherCandidates == nNumCandidates )) - ) { - s_group_info->num_candidates = 0; /* no candidate exists */ - return 0; - } - if ( !(s_subtype_all & (SALT_DONOR_Neg) ) ) { - t_group_info->bTautFlagsDone |= TG_FLAG_ALLOW_NO_NEGTV_O_DONE; - } - - /************************************************************************************/ - /* Mark redundant candidates so that only one candidate from one t-group is left in */ - /************************************************************************************/ - for ( i = 0; i < nNumCandidates; i ++ ) { - if ( 2 == s_candidate[nNumCandidates].type ) { - s_candidate[i].type -= DISABLE_CANDIDATE; /* disable >C-SH candidates */ - nNumLeftCandidates ++; /* count rejected */ - continue; - } - if ( s_candidate[i].endpoint ) { - for ( j = i-1; 0 <= j; j -- ) { - if ( s_candidate[i].endpoint == s_candidate[j].endpoint ) { - s_candidate[i].type -= DISABLE_CANDIDATE; /* disable subsequent redundant */ - nNumLeftCandidates ++; /* count rejected */ - break; - } - } - } - } - nNumLeftCandidates = nNumCandidates - nNumLeftCandidates; /* subtract num. rejected from the total */ - s_group_info->num_candidates = 0; /* reinit next time */ - /*********************************************************************/ - /* reorder so that all disabled are at the end, tautomeric are first */ - /*********************************************************************/ - qsort ( s_candidate, nNumCandidates, sizeof(s_candidate[0]), comp_candidates ); - cPair = (S_CHAR *)inchi_calloc( nNumLeftCandidates*nNumLeftCandidates, sizeof(cPair[0]) ); - if ( !cPair ) { - /*printf("BNS_OUT_OF_RAM-6\n");*/ - nTotNumChanges = BNS_OUT_OF_RAM; - goto quick_exit; - } - nDonorPairs = nAcceptorPairs = 0; - /**********************************************************************/ - /* Find whether we have at least one donor pair and one acceptor pair */ - /**********************************************************************/ - for ( i = 0; i < nNumLeftCandidates; i ++ ) { - nCurDonorPairs = nCurAcceptorPairs = 0; - for ( j = 0; j <= i; j ++ ) { - if ( i == j && !s_candidate[i].endpoint ) { - continue; /* same non-taut atom. However, success for i==j means * - * that the whole tautomeric group may donate or accept 2H */ - } - /* check for acceptor pair */ - if ( (s_candidate[i].subtype & SALT_ACCEPTOR) && (s_candidate[j].subtype & SALT_ACCEPTOR) && - (ret = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, s_candidate[i].atnumber, - s_candidate[j].atnumber, ALT_PATH_MODE_ADD2H_TST ))) { - if ( IS_BNS_ERROR( ret ) ) { - nTotNumChanges = ret; - goto quick_exit; - } - if ( ret & 1 ) { - nDelta = (ret & ~3) >> 2; - /*nNumChanges += (ret & 2);*/ - if ( nDelta ) { - /* alt path unleashed previously localized radicals and they annihilated */ - nNumChanges = 0; - nTotNumChanges = BNS_RADICAL_ERR; - goto quick_exit; - } - cPAIR(i,j) |= ACCEPTOR_PAIR; /* the result: mark the pair */ - /*cPAIR(j,i) |= ACCEPTOR_PAIR;*/ - } - } - /* check for donor pair */ - if ( (s_candidate[i].subtype & SALT_DONOR) && (s_candidate[j].subtype & SALT_DONOR) && - (ret = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, s_candidate[i].atnumber, - s_candidate[j].atnumber, ALT_PATH_MODE_REM2H_TST ))) { - if ( IS_BNS_ERROR( ret ) ) { - nTotNumChanges = ret; - goto quick_exit; - } - if ( ret & 1 ) { - nDelta = (ret & ~3) >> 2; - /*nNumChanges += (ret & 2);*/ - if ( nDelta ) { - /* alt path unleashed previously localized radicals and they annihilated */ - nNumChanges = 0; - nTotNumChanges = BNS_RADICAL_ERR; - goto quick_exit; - } - cPAIR(i,j) |= DONOR_PAIR; /* the result: mark the pair */ - /*cPAIR(j,i) |= ACCEPTOR_PAIR;*/ - } - } - /* since the results will be used later to change bonds, check only now */ - /* when both results for (i,j) have been obtained. */ - if ( cPAIR(i,j) & ACCEPTOR_PAIR ) { - nCurAcceptorPairs ++; - if ( nDonorPairs ) { - /* find donor pair (i1,j1) such that i!=i1, i!=j1, j!=i1, j!=j1 */ - for ( i1 = 0; i1 < i; i1 ++ ) { - for ( j1 = 0; j1 <= i1; j1 ++ ) { - /* here always j1 < i && i1 < i therefore we do not compare i to i1 or j1 */ - if ( j1 != j && i1 != j && (cPAIR(i1,j1) & DONOR_PAIR) ) { - /* both the donor and the acceptor pairs have been found */ - goto bFound2Pairs; - } - } - } - } - } - if ( cPAIR(i,j) & DONOR_PAIR ) { - nCurDonorPairs ++; - if ( nAcceptorPairs ) { - /* find acceptor pair (i1,j1) such that i!=i1, i!=j1, j!=i1, j!=j1 */ - for ( i1 = 0; i1 < i; i1 ++ ) { - for ( j1 = 0; j1 <= i1; j1 ++ ) { - /* here always j1 < i && i1 < i therefore we do not compare i to i1 or j1 */ - if ( j1 != j && i1 != j && (cPAIR(i1,j1) & ACCEPTOR_PAIR) ) { - /* both the donor and the acceptor pairs have been found */ - goto bFound2Pairs; - } - } - } - } - } - } - nDonorPairs += nCurDonorPairs; - nAcceptorPairs += nCurAcceptorPairs; - } - /* nothing has been found */ - nNumChanges = 0; - inchi_free( cPair ); - cPair = NULL; - goto quick_exit; - - - /* both the donor and the acceptor pairs have been found */ -bFound2Pairs: - /* first, try already found pairs */ - i1 = i; - j1 = j; - - /* Find all possible donor and acceptor pairs */ - nNumMarkedCandidates = 0; - for ( i = 0; i < nNumLeftCandidates; i ++ ) { - nCurDonorPairs = nCurAcceptorPairs = 0; - for ( j = 0; j <= i; j ++ ) { - bAlreadyTested = (i < i1 || i == i1 && j <= j1); - if ( bAlreadyTested && (cPAIR(i,j) & ACCEPTOR_PAIR) || !bAlreadyTested ) { - /* checking for acceptor pair */ - if ( (s_candidate[i].subtype & SALT_ACCEPTOR) && (s_candidate[j].subtype & SALT_ACCEPTOR) && - (ret = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, s_candidate[i].atnumber, - s_candidate[j].atnumber, ALT_PATH_MODE_ADD2H_CHG ))) { - if ( IS_BNS_ERROR( ret ) ) { - nTotNumChanges = ret; - goto quick_exit; - } - if ( ret & 1 ) { - nDelta = (ret & ~3) >> 2; - nNumChanges += (ret & 2); - if ( nDelta ) { - /* alt path unleashed previously localized radicals and they annihilated */ - nNumChanges = 0; - nTotNumChanges = BNS_RADICAL_ERR; - goto quick_exit; - } - cPAIR(i,j) |= ACCEPTOR_PAIR; - /*cPAIR(j,i) |= ACCEPTOR_PAIR;*/ - nCurAcceptorPairs += !bAlreadyTested; - if ( !(s_candidate[i].subtype & SALT_SELECTED) ) { - s_candidate[i].subtype |= SALT_SELECTED; - nNumMarkedCandidates ++; - if ( !s_candidate[i].endpoint && s_candidate[i].type ) { - nNumOtherChanges ++; - } else { - nNumAcidicChanges ++; - } - } - if ( !(s_candidate[j].subtype & SALT_SELECTED) ) { - s_candidate[j].subtype |= SALT_SELECTED; - nNumMarkedCandidates ++; - if ( !s_candidate[j].endpoint && s_candidate[j].type ) { - nNumOtherChanges ++; - } else { - nNumAcidicChanges ++; - } - } - } - } - } - if ( bAlreadyTested && (cPAIR(i,j) & DONOR_PAIR) || !bAlreadyTested ) { - /* checking for donor pair */ - if ( (s_candidate[i].subtype & SALT_DONOR) && (s_candidate[j].subtype & SALT_DONOR) && - (ret = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, s_candidate[i].atnumber, - s_candidate[j].atnumber, ALT_PATH_MODE_REM2H_CHG ))) { - if ( IS_BNS_ERROR( ret ) ) { - nTotNumChanges = ret; - goto quick_exit; - } - if ( ret & 1 ) { - nDelta = (ret & ~3) >> 2; - nNumChanges += (ret & 2); - if ( nDelta ) { - /* alt path unleashed previously localized radicals and they annihilated */ - nNumChanges = 0; - nTotNumChanges = BNS_RADICAL_ERR; - goto quick_exit; - } - cPAIR(i,j) |= DONOR_PAIR; - /*cPAIR(j,i) |= ACCEPTOR_PAIR;*/ - nCurDonorPairs += !bAlreadyTested; - if ( !(s_candidate[i].subtype & SALT_SELECTED) ) { - s_candidate[i].subtype |= SALT_SELECTED; - nNumMarkedCandidates ++; - if ( !s_candidate[i].endpoint && s_candidate[i].type ) { - nNumOtherChanges ++; - } else { - nNumAcidicChanges ++; - } - } - if ( !(s_candidate[j].subtype & SALT_SELECTED) ) { - s_candidate[j].subtype |= SALT_SELECTED; - nNumMarkedCandidates ++; - if ( !s_candidate[j].endpoint && s_candidate[j].type ) { - nNumOtherChanges ++; - } else { - nNumAcidicChanges ++; - } - } - } - } - } - } - nDonorPairs += nCurDonorPairs; - nAcceptorPairs += nCurAcceptorPairs; - } - inchi_free( cPair ); - cPair = NULL; - - if ( nNumMarkedCandidates ) { - EndPoint = (T_ENDPOINT *)inchi_calloc( nNumMarkedCandidates, sizeof(EndPoint[0])); - if ( !EndPoint ) { - /*printf("BNS_OUT_OF_RAM-7\n");*/ - nTotNumChanges = BNS_OUT_OF_RAM; - goto quick_exit; - } - for ( i = 0, j = 0; i < nNumLeftCandidates; i ++ ) { - if ( s_candidate[i].subtype & SALT_SELECTED ) { - s_candidate[i].subtype ^= SALT_SELECTED; /* remove the flag */ - if ( j < nNumMarkedCandidates ) { - i1 = s_candidate[i].atnumber; /* save a representative of the t-group to be created */ - AddEndPoint( EndPoint+j, at, i1 ); - } - j ++; - } - } - if ( j != nNumMarkedCandidates ) { - nTotNumChanges = BNS_PROGRAM_ERR; - goto quick_exit; - } - /* merge all marked atoms and their t-groups into one t-group */ - ret = RegisterEndPoints( t_group_info, EndPoint, nNumMarkedCandidates, at, num_atoms, c_group_info, pBNS ); - if ( ret == -1 ) { - ret = BNS_PROGRAM_ERR; - } - if ( ret < 0 ) { - nTotNumChanges = ret; - goto quick_exit; - } - nTotNumChanges += (ret > 0); - inchi_free( EndPoint ); - EndPoint = NULL; - - if ( nNumMarkedCandidates ) { - for ( i = nNumLeftCandidates; i < nNumCandidates; i ++ ) { - s_candidate[i].type += DISABLE_CANDIDATE; - j1 = s_candidate[i].atnumber; - if ( at[j1].endpoint == at[i1].endpoint ) { - if ( !s_candidate[i].endpoint && s_candidate[i].type ) { - nNumOtherChanges ++; - } else { - nNumAcidicChanges ++; - } - } - } - } else { - for ( i = nNumLeftCandidates; i < nNumCandidates; i ++ ) { - s_candidate[i].type += DISABLE_CANDIDATE; - } - } - - /* find whether the new t-group have any movable H */ - for ( i = 0, bTGroupHasNegativeChargesOnly = 0; i < t_group_info->num_t_groups; i ++ ) { - if ( t_group_info->t_group[i].nGroupNumber == at[i1].endpoint && - t_group_info->t_group[i].num[0] == t_group_info->t_group[i].num[1] ) { - bTGroupHasNegativeChargesOnly = 1; - break; - } - } - } - nTotNumChanges = ( nTotNumChanges > 0); - -#if ( IGNORE_TGROUP_WITHOUT_H == 1 ) - if ( nTotNumChanges && bTGroupHasNegativeChargesOnly ) { - nTotNumChanges = 2; /* means no moveable H has been affected */ - } -#endif - } - -quick_exit: - if ( nNumOtherChanges && nTotNumChanges == 1 ) { - nTotNumChanges = 5; /* not only acidic atoms merged */ - } - if ( cPair ) { - inchi_free( cPair ); - /*cPair = NULL;*/ - } - if ( EndPoint ) { - inchi_free ( EndPoint ); - /*EndPoint = NULL;*/ - } - return nTotNumChanges; /* 0=>no changes, 1=>new salt tautomerism found, 2=>only new charge tautomerism found */ -#undef ALT_PATH_FOUND -#undef NO_ENDPOINT -} -/********************************************************************************************************/ -/* regular one-path version: find alt paths then merge */ -/* Check for oxygen negative charge-H tautomerism (Salts) - allowed long-range tautomerism; only one H or (-) can be moved, for example: - HO-C=X-Y=Z-...-C=O => O=C-X=Y-Z=...=C-OH -*/ - -#if ( SALT_WITH_PROTONS == 1 ) - -#define MAX_LOCAL_TGNUM 0 /* was 32; disable since it has not been used */ - -#if ( MAX_LOCAL_TGNUM > 0 ) -typedef struct tagTGroupData { - S_SHORT nGroupNumber; /* t-group number from t_group_info->t_group->nGroupNumber */ - S_SHORT nGroupIndex; /* TGroupData[nGroupNumber]nGroupIndex = index of t_group in t_group_info */ - S_SHORT nDonorM; /* number of endpoint-donors that have negative charge (Minus) */ - S_SHORT nDonorH; /* number of endpoint-donors that have only H */ - S_SHORT nAccepM; /* number of endpoint-acceptors that have negative charge (Minus) */ - S_SHORT nAccepH; /* number of endpoint-acceptors that have H and no negative charge */ - S_SHORT nAccep0; /* number of endpoint-acceptors that have no H and no negative charge */ - S_SHORT nDonorA; /* number of acidic endpoint-donors */ - S_SHORT nAccepS; /* number of acidic endpoint-acceptors */ -} TGroupData; -#endif - - - -/*****************************************************************************/ -int MarkSaltChargeGroups ( inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, - T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, - struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD ) -{ - - int nNumChanges = 0, nTotNumChanges = 0; - if ( s_group_info && s_group_info->s_candidate && s_group_info->max_num_candidates > 0 ) { - int i, i1, i2, j, j1, j2, jj, ii1, ii2, jj1, jj2, /*k,*/ num_tested; - S_CANDIDATE *s_candidate = s_group_info->s_candidate; - int nMaxNumCandidates = s_group_info->max_num_candidates; - int nNumCandidates = s_group_info->num_candidates; - int nNumOtherCandidates = s_group_info->num_other_candidates; - int nNumPOnlyCandidates = s_group_info->num_p_only_candidates; - int s_type, s_subtype; - int ret, nDelta, /*nMobile,*/ err = 0; - int s_subtype_all = 0; - int nGroupNumber; - T_ENDPOINT EndPoint[2]; -#if ( MAX_LOCAL_TGNUM > 0 ) - TGroupData tgData[MAX_LOCAL_TGNUM]; - TGroupData *ptgData = tgData; -#endif - int cond1=0,cond2a=0,cond2b=0,cond2c=0,cond2=0; - - if ( nNumCandidates <= -1 || !t_group_info || !t_group_info->t_group ) { - return 0; - } - - /* count t-groups */ - for ( i = 0, nGroupNumber = 0; i < t_group_info->num_t_groups; i ++ ) { - if ( nGroupNumber < t_group_info->t_group[i].nGroupNumber ) { - nGroupNumber = t_group_info->t_group[i].nGroupNumber; /* max. t-group number */ - } - } -#if ( MAX_LOCAL_TGNUM > 0 ) - /* prepare memory */ - if ( nGroupNumber >= MAX_LOCAL_TGNUM ) { - if ( !( ptgData = (TGroupData*)inchi_calloc( nGroupNumber+1, sizeof(TGroupData) ) ) ) { - err = BNS_OUT_OF_RAM; - goto quick_exit; - } - } else { - memset( ptgData, 0, sizeof(tgData) ); - } - ptgData[0].nGroupIndex = -1; /* data for non-tautomeric atoms */ - for ( i = 0, nGroupNumber = 0; i < t_group_info->num_t_groups; i ++ ) { - if ( nGroupNumber = t_group_info->t_group[i].nGroupNumber ) { - ptgData[nGroupNumber].nGroupIndex = i; - ptgData[i].nGroupNumber = nGroupNumber; - } - } -#endif - nNumCandidates = 0; /* always recalculate 2004-03-22 */ - num_tested = 0; - - if ( nNumCandidates == 0 ) - { - for ( i = 0, nNumCandidates = nNumOtherCandidates = nNumPOnlyCandidates = 0; i < num_atoms; i ++ ) - { - if ( 0 == (s_type = GetSaltChargeType( at, i, t_group_info, &s_subtype )) || - /* -C=O or =C-OH, O = S, Se, Te */ -#if ( INCL_NON_SALT_CANDIDATATES == 1 ) - 1 == (s_type = GetOtherSaltChargeType( at, i, t_group_info, &s_subtype, 1 )) || - /* =Z-MH or -Z=M, Z = centerpoint, M = endpoint, other than above */ -#endif - 2 == (s_type = GetOtherSaltType( at, i, &s_subtype ) ) - /* >C-SH, >C-S(-); S=S,Se,Te */ - ) - { - - if ( nNumCandidates >= nMaxNumCandidates ) - { - err = BNS_VERT_EDGE_OVFL; - goto quick_exit; - } - s_candidate[nNumCandidates].atnumber = i; - s_candidate[nNumCandidates].type = s_type; - s_candidate[nNumCandidates].subtype = s_subtype; - s_candidate[nNumCandidates].endpoint = at[i].endpoint; - nNumCandidates ++; - nNumOtherCandidates += (1 == s_type); - nNumPOnlyCandidates += (2 == s_type); - s_subtype_all |= s_subtype; - /*i1 = i;*/ /* save a representative of a tautomeric group */ - } - } /* for */ - - /* changes: TG_FLAG_ALLOW_NO_NEGTV_O replaced CHARGED_SALTS_ONLY==0 */ -#if 0 - if ( nNumCandidates <= 1 || - !(s_subtype_all & SALT_ACCEPTOR) || - (((t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O)|| - (t_group_info->bTautFlagsDone & TG_FLAG_FOUND_SALT_CHARGES_DONE) || - (t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT)) ? - !(s_subtype_all & (SALT_DONOR_Neg | SALT_DONOR_H)): - (!(s_subtype_all & SALT_DONOR_Neg) || nNumOtherCandidates==nNumCandidates)) - ) { -#endif - cond1 = s_subtype_all & SALT_ACCEPTOR; - cond2a = t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O; - cond2b = t_group_info->bTautFlagsDone & TG_FLAG_FOUND_SALT_CHARGES_DONE; - cond2c = t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT; - if ( cond2a || cond2b|| cond2c ) - cond2 = !(s_subtype_all & (SALT_DONOR_Neg | SALT_DONOR_H)); - else - cond2 = !(s_subtype_all & SALT_DONOR_Neg) || nNumOtherCandidates==nNumCandidates; - if ( nNumCandidates <= 1 || !cond1 || cond2 - /*( - ( cond2a || cond2b || cond2c ) - ? !(s_subtype_all & (SALT_DONOR_Neg | SALT_DONOR_H)) - : ( !(s_subtype_all & SALT_DONOR_Neg) || nNumOtherCandidates==nNumCandidates) ) */ - ) - { - s_group_info->num_candidates = -1; /* no candidate exists */ - goto quick_exit; - } - if ( !(s_subtype_all & (SALT_DONOR_Neg) ) ) { - t_group_info->bTautFlagsDone |= TG_FLAG_ALLOW_NO_NEGTV_O_DONE; - } - } else { - for ( i = 0; i < nNumCandidates; i ++ ) { - i1 = s_candidate[i].atnumber; - if ( 0 <= (s_type = GetSaltChargeType( at, i1, t_group_info, &s_subtype )) -#if ( INCL_NON_SALT_CANDIDATATES == 1 ) - || 0 < (s_type = GetOtherSaltChargeType( at, i1, t_group_info, &s_subtype, 1 /* bAccept_O*/ )) -#endif - ) { - s_candidate[nNumCandidates].type = s_type; - s_candidate[nNumCandidates].subtype = s_subtype; - s_candidate[nNumCandidates].endpoint = at[i1].endpoint; - } - } - } - /* Look for alt paths connecting: - SALT_DONOR_Neg to SALT_ACCEPTOR : long distance migration of negative charges - SALT_DONOR_H to SALT_ACCEPTOR : long distance migration of H-atoms - */ - do { - nNumChanges = 0; - for ( i1 = 0; i1 < nNumCandidates; i1 ++ ) { - j1 = s_candidate[i1].atnumber; - for ( i2 = i1+1; i2 < nNumCandidates; i2 ++ ) { - /* prev. approach: do not test if both candidates are not "salt-type". Disabled 2004-03-18 - if ( s_candidate[i1].type && s_candidate[i2].type ) - continue; - */ - j2 = s_candidate[i2].atnumber; - if ( at[j1].endpoint && at[j1].endpoint == at[j2].endpoint ) { - continue; - } - for ( j = 0; j < 2; j ++ ) { - if ( j ) { - ii1 = i2; /* candidate 1 (donor) ordering number */ - ii2 = i1; /* candidate 2 (acceptor) ordering number */ - jj1 = j2; /* candidate 1 (donor) atom number */ - jj2 = j1; /* candidate 2 (acceptor) atom number */ - } else { /* transposition */ - ii1 = i1; /* candidate 1 (donor) ordering number */ - ii2 = i2; /* candidate 2 (acceptor) ordering number */ - jj1 = j1; /* candidate 1 (donor) atom number */ - jj2 = j2; /* candidate 2 (acceptor) atom number */ - } - - if ( ( s_candidate[ii1].subtype & (SALT_DONOR_Neg | SALT_DONOR_H) ) && - ( s_candidate[ii2].subtype & SALT_ACCEPTOR ) ) - { - ret = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, jj2, jj1, ALT_PATH_MODE_4_SALT ); - num_tested ++; - if ( IS_BNS_ERROR( ret ) ) - { - err = ret; - goto quick_exit; - } - if ( ret & 1 ) - { - nDelta = (ret & ~3) >> 2; - nNumChanges += (ret & 2); - for ( i = 0; i < 2; i ++ ) - { - jj = i? jj2 : jj1; - AddEndPoint( EndPoint+i, at, jj ); - } - /* add/merge taut groups and reinit pBNS in the fly */ - ret = RegisterEndPoints( t_group_info, - EndPoint, 2, at, num_atoms, c_group_info, pBNS ); - if ( ret == -1 ) - { - ret = BNS_PROGRAM_ERR; - } - if ( ret < 0 ) - { - err = ret; - goto quick_exit; - } - if ( nDelta ) - { - err = BNS_RADICAL_ERR; - goto quick_exit; - } - nNumChanges += (ret > 0); - break; /* avoid redundant repetition */ - } - } - } - } - } - nTotNumChanges += nNumChanges; - } while ( num_tested && nNumChanges ); - -quick_exit: - if ( !err ) { - nTotNumChanges += nNumChanges; /* nNumChanges != 0 only in case of 'goto quick_exit' */ - if ( s_group_info->num_candidates == 0 ) { - /* first time: initialize */ - s_group_info->num_candidates = num_tested? nNumCandidates : -1; /* no candidate exists */ - } - } else { - nTotNumChanges = err; - } -#if ( MAX_LOCAL_TGNUM > 0 ) - if ( ptgData != tgData ) { - inchi_free( ptgData ); - } -#endif - } - return nTotNumChanges; -} -#else - -/*****************************************************************************/ -int MarkSaltChargeGroups ( inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, - T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, - struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD ) -{ - - int nNumChanges = 0, nTotNumChanges = 0; - if ( s_group_info && s_group_info->s_candidate && s_group_info->max_num_candidates > 0 ) { - int i, i1, i2, j, j1, j2, jj, ii1, ii2, jj1, jj2, k, num_tested; - S_CANDIDATE *s_candidate = s_group_info->s_candidate; - int nMaxNumCandidates = s_group_info->max_num_candidates; - int nNumCandidates = s_group_info->num_candidates; - int nNumOtherCandidates = s_group_info->num_other_candidates; - int s_type, s_subtype; - int ret, nDelta, nMobile; - int s_subtype_all = 0; - T_ENDPOINT EndPoint[2]; - - if ( nNumCandidates <= -1 || !t_group_info || !t_group_info->t_group ) { - return 0; - } else - if ( nNumCandidates == 0 ) { - for ( i = 0, nNumCandidates = nNumOtherCandidates = 0; i < num_atoms; i ++ ) { - if ( 0 <= (s_type = GetSaltChargeType( at, i, t_group_info, &s_subtype )) ) { - if ( nNumCandidates >= nMaxNumCandidates ) { - return BNS_VERT_EDGE_OVFL; - } - s_candidate[nNumCandidates].atnumber = i; - s_candidate[nNumCandidates].type = s_type; - s_candidate[nNumCandidates].subtype = s_subtype; - s_candidate[nNumCandidates].endpoint = at[i].endpoint; - nNumCandidates ++; - s_subtype_all |= s_subtype; - /*i1 = i;*/ /* save a representative of a tautomeric group */ - } -#if ( INCL_NON_SALT_CANDIDATATES == 1 ) - else /* new */ - if ( 0 < (s_type = GetOtherSaltChargeType( at, i, t_group_info, &s_subtype, 1 /* bAccept_O*/ )) ) { - if ( nNumCandidates >= nMaxNumCandidates ) { - return BNS_VERT_EDGE_OVFL; - } - s_candidate[nNumCandidates].atnumber = i; - s_candidate[nNumCandidates].type = s_type; - s_candidate[nNumCandidates].subtype = s_subtype; - s_candidate[nNumCandidates].endpoint = at[i].endpoint; - nNumCandidates ++; - nNumOtherCandidates ++; - s_subtype_all |= s_subtype; - } -#endif - } - - /* changes: TG_FLAG_ALLOW_NO_NEGTV_O replaced CHARGED_SALTS_ONLY==0 */ - if ( nNumCandidates <= 1 || nNumOtherCandidates == nNumCandidates || - ((t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O) ? - !(s_subtype_all & (SALT_DONOR_Neg | SALT_DONOR_H)): - !(s_subtype_all & SALT_DONOR_Neg)) || - !(s_subtype_all & SALT_ACCEPTOR)) { - s_group_info->num_candidates = -1; /* no candidate exists */ - return 0; - } - if ( !(s_subtype_all & (SALT_DONOR_Neg) ) ) { - t_group_info->bTautFlagsDone |= TG_FLAG_ALLOW_NO_NEGTV_O_DONE; - } - } else { - for ( i = 0; i < nNumCandidates; i ++ ) { - i1 = s_candidate[i].atnumber; - if ( 0 <= (s_type = GetSaltChargeType( at, i1, t_group_info, &s_subtype )) -#if ( INCL_NON_SALT_CANDIDATATES == 1 ) - || 0 < (s_type = GetOtherSaltChargeType( at, i1, t_group_info, &s_subtype, 1 /* bAccept_O*/ )) -#endif - ) { - s_candidate[nNumCandidates].type = s_type; - s_candidate[nNumCandidates].subtype = s_subtype; - s_candidate[nNumCandidates].endpoint = at[i1].endpoint; - } - } - } - /* Look for alt paths connecting: - SALT_DONOR_Neg to SALT_ACCEPTOR : long distance migration of negative charges - SALT_DONOR_H to SALT_ACCEPTOR : long distance migration of H-atoms - */ - num_tested = 0; - do { - nNumChanges = 0; - for ( i1 = 0; i1 < nNumCandidates; i1 ++ ) { - j1 = s_candidate[i1].atnumber; - for ( i2 = i1+1; i2 < nNumCandidates; i2 ++ ) { - if ( s_candidate[i1].type && s_candidate[i2].type ) - continue; /* both candidates are not "salt-type" */ - j2 = s_candidate[i2].atnumber; - if ( at[j1].endpoint && at[j1].endpoint == at[j2].endpoint ) { - continue; - } - for ( j = 0; j < 2; j ++ ) { - if ( j ) { - ii1 = i2; /* candidate 1 (donor) ordering number */ - ii2 = i1; /* candidate 2 (acceptor) ordering number */ - jj1 = j2; /* candidate 1 (donor) atom number */ - jj2 = j1; /* candidate 2 (acceptor) atom number */ - } else { /* transposition */ - ii1 = i1; /* candidate 1 (donor) ordering number */ - ii2 = i2; /* candidate 2 (acceptor) ordering number */ - jj1 = j1; /* candidate 1 (donor) atom number */ - jj2 = j2; /* candidate 2 (acceptor) atom number */ - } - - if ( ( s_candidate[ii1].subtype & (SALT_DONOR_Neg | SALT_DONOR_H) ) && - ( s_candidate[ii2].subtype & SALT_ACCEPTOR ) ) { - ret = bExistsAltPath( pBNS, pBD, NULL, at, num_atoms, jj2, jj1, ALT_PATH_MODE_4_SALT ); - num_tested ++; - if ( IS_BNS_ERROR( ret ) ) { - return ret; - } - if ( ret & 1 ) { - nDelta = (ret & ~3) >> 2; - nNumChanges += (ret & 2); - for ( i = 0; i < 2; i ++ ) { - jj = i? jj2 : jj1; - EndPoint[i].nAtomNumber = jj; - EndPoint[i].nEquNumber = 0; - EndPoint[i].nGroupNumber = at[jj].endpoint; - if ( at[jj].endpoint ) { - memset( EndPoint[i].num, 0, sizeof(EndPoint[i].num) ); - } else { - AddAtom2num( EndPoint[i].num, at, jj, 2 ); /* fill out */ - AddAtom2DA( EndPoint[i].num_DA, at, jj, 2 ); - /* - nMobile = EndPoint[i].num[1] = (at[jj].charge == -1); - nMobile = EndPoint[i].num[0] = at[jj].num_H + nMobile; - for ( k = 0; k < T_NUM_ISOTOPIC; k ++ ) { - EndPoint[i].num[T_NUM_NO_ISOTOPIC+k] = at[jj].num_iso_H[NUM_H_ISOTOPES-k-1]; - } - */ - } - } - /* add/merge taut groups and reinit pBNS */ - ret = RegisterEndPoints( t_group_info, - EndPoint, 2, at, num_atoms, c_group_info, pBNS ); - if ( ret < 0 ) { - return ret; - } - nNumChanges += (ret > 0); - if ( nDelta ) { - goto quick_exit; - } - break; /* avoid redundant repetition */ - } - } - } - } - } - nTotNumChanges += nNumChanges; - } while ( num_tested && nNumChanges ); - -quick_exit: - nTotNumChanges += nNumChanges; /* nNumChanges != 0 only in case of 'goto quick_exit' */ - if ( s_group_info->num_candidates == 0 ) { - /* first time: initialize */ - s_group_info->num_candidates = num_tested? nNumCandidates : -1; /* no candidate exists */ - } - - } - return nTotNumChanges; -} -#endif - - - -/*****************************************************************************/ -int MergeSaltTautGroups( inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, - T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, - struct BalancedNetworkStructure *pBNS ) -{ - /* count candidates to be connected: exclude pure donors that do not belong to any t-group */ - AT_NUMB nCurTGroupNumber; - int i, j, /*k,*/ ret, iat, /*nMobile,*/ nMinNumEndpoints; - int s_subtype_all, s_subtype_taut; - int nMaxNumCandidates, nNumCandidates, nNumCandidates2; - T_ENDPOINT EndPointStackArray[MAX_STACK_ARRAY_LEN]; /* will be reallocated if too short */ - T_ENDPOINT *EndPoint = EndPointStackArray; - - - if ( !s_group_info || !s_group_info->s_candidate || /*s_group_info->num_candidates <= 0 ||*/ - !t_group_info || !t_group_info->t_group || !c_group_info ) { - return 0; - } - nMinNumEndpoints = 0; - nMaxNumCandidates = s_group_info->max_num_candidates; - nCurTGroupNumber = MAX_ATOMS; /* impossible t-group number */ - s_subtype_all = s_subtype_taut = 0; - /* collect tautomeric acidic O and previously non-tautomeric C-OH, C-SH, C-O(-), C-S(-) */ - /* find whether previously found tautomeric atoms have both mobile H and (-) */ - if ( 1 || (s_group_info->num_candidates < 0) ) { - /* can be only -O(-) and -OH */ - int s_type, s_subtype; - S_CANDIDATE *s_candidate = s_group_info->s_candidate; - for ( i = 0, nNumCandidates = nNumCandidates2 = 0; i < num_atoms; i ++ ) { - s_subtype = 0; - if ( 0 == (s_type = GetSaltChargeType( at, i, t_group_info, &s_subtype )) || - /* -C=O or =C-OH, O = S, Se, Te */ - - /*(t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT) &&*/ - 1 == (s_type = GetOtherSaltChargeType( at, i, t_group_info, &s_subtype, 1/* bAccept_O*/ )) || - /* =Z-MH or -Z=M, Z = centerpoint, M = endpoint, other than above. M may be N */ - - 2 == (s_type = GetOtherSaltType( at, i, &s_subtype )) || - /* >C-SH, >C-S(-); S=S,Se,Te */ - - /* other proton donor or acceptor */ - bHasAcidicHydrogen( at, i) && ((s_type=3), (s_subtype = SALT_p_DONOR)) || - bHasAcidicMinus( at, i) && ((s_type=3), (s_subtype = SALT_p_ACCEPTOR)) - ) { - - if ( nNumCandidates >= nMaxNumCandidates ) { - return BNS_VERT_EDGE_OVFL; - } - if ( at[i].endpoint ) { - s_subtype_taut |= s_subtype; - } else - if ( bDoNotMergeNonTautAtom(at, i) ) { - continue; /* ignore non-tautomeric N */ - } - if ( !( s_subtype & SALT_DONOR_ALL ) || - (s_subtype & SALT_ACCEPTOR) && !at[i].endpoint ) { - continue; /* do not include non-taut acceptors like -C=O */ - } - s_candidate[nNumCandidates].atnumber = i; - s_candidate[nNumCandidates].type = s_type; - s_candidate[nNumCandidates].subtype = s_subtype; - s_candidate[nNumCandidates].endpoint = at[i].endpoint; - nNumCandidates ++; - s_subtype_all |= s_subtype; - } - } - /* - Forced merging occurs upon: - =========================== - (t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O) or - (t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT) - - - Allow forced merging in cases: - {t-groups} (H, (-)} {H, (-), t-groups} - - - Normal salt merging in cases: - (H, (-)} {H, (-), t-groups}, - - Cannot merge H into t-groups if no (-) is present - */ - - - if ( (t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O) || - (t_group_info->bTautFlagsDone & TG_FLAG_FOUND_SALT_CHARGES_DONE) || - (t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT) ) { - /* force merge even though no negative charges are present */ - if ( nNumCandidates <= 1 || - (!(s_subtype_all & SALT_DONOR_Neg2) || !(s_subtype_all & SALT_DONOR_H2)) && - !t_group_info->num_t_groups ) { - s_group_info->num_candidates = -1; /* no candidate exists */ - return 0; - } - } else { - /* normal salt mode: merge if both -XH and -X(-) are present */ - if ( nNumCandidates <= 1 || - (!(s_subtype_all & SALT_DONOR_Neg2) || !(s_subtype_all & SALT_DONOR_H2)) ) { - s_group_info->num_candidates = -1; /* no candidate exists */ - return 0; - } - } - /* -- old code -- - if ( nNumCandidates <= 1 || - (((t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O) || - (t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT)) ? - !(s_subtype_all & SALT_DONOR_ALL): - !(s_subtype_all & SALT_DONOR_Neg2) - ) - ) { - s_group_info->num_candidates = -1; - return 0; - } - */ - if ( !(s_subtype_all & (SALT_DONOR_Neg2) ) ) { - t_group_info->bTautFlagsDone |= TG_FLAG_ALLOW_NO_NEGTV_O_DONE; - } - s_group_info->num_candidates = nNumCandidates; - } - - for ( i = 0; i < s_group_info->num_candidates; i ++ ) { - iat = s_group_info->s_candidate[i].atnumber; - if ( (s_group_info->s_candidate[i].subtype & SALT_ACCEPTOR) && !at[iat].endpoint ) { - continue; /* should not happen */ - } - s_subtype_all |= s_group_info->s_candidate[i].subtype; - if ( at[iat].endpoint != nCurTGroupNumber || !at[iat].endpoint ) { - nMinNumEndpoints ++; - } - nCurTGroupNumber = (int)at[iat].endpoint; - } - if ( nMinNumEndpoints <= 1 ) { - return 0; /* too few endpoints */ - } - - /* make sure we have enough memory */ - if ( nMinNumEndpoints > MAX_STACK_ARRAY_LEN ) { - if ( !(EndPoint = (T_ENDPOINT *)inchi_calloc( nMinNumEndpoints, sizeof(EndPoint[0]) ) ) ) { - /*printf("BNS_OUT_OF_RAM-8\n");*/ - return BNS_OUT_OF_RAM; - } - } - - nCurTGroupNumber = MAX_ATOMS; /* impossible t-group number */ - for ( i = j = 0; i < s_group_info->num_candidates; i ++ ) { - iat = s_group_info->s_candidate[i].atnumber; - if ( s_group_info->s_candidate[i].subtype == SALT_ACCEPTOR && !at[iat].endpoint ) { - continue; - } - if ( at[iat].endpoint != nCurTGroupNumber || !at[iat].endpoint ) { - AddEndPoint( EndPoint+j, at, iat ); - j ++; - } - nCurTGroupNumber = (int)at[iat].endpoint; - } - - ret = RegisterEndPoints( t_group_info, - EndPoint, j, at, num_atoms, c_group_info, pBNS ); - if ( ret == -1 ) { - ret = BNS_PROGRAM_ERR; - } - - if ( EndPoint != EndPointStackArray ) { - inchi_free( EndPoint ); - } - - return ret; -} - - - -/*****************************************************************************/ -int MakeIsotopicHGroup(inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, - T_GROUP_INFO *t_group_info) -{ - /* all tautomeric atoms and all possible H+ donors and acceptors that have H */ - int i, j, k, n, bHasH, tg, nError=0; - int s_subtype_all, s_subtype_taut; - int nMaxNumCandidates, nNumCandidates, nNumNonTautCandidates; - - - if ( !s_group_info || !s_group_info->s_candidate || /*s_group_info->num_candidates <= 0 ||*/ - !t_group_info || !t_group_info->t_group ) { - return 0; - } - nMaxNumCandidates = s_group_info->max_num_candidates; - s_subtype_all = s_subtype_taut = 0; - memset( t_group_info->num_iso_H, 0, sizeof(t_group_info->num_iso_H) ); - if ( 1 || (s_group_info->num_candidates < 0) ) { - int s_type, s_subtype; - S_CANDIDATE *s_candidate = s_group_info->s_candidate; - for ( i = 0, nNumCandidates = nNumNonTautCandidates = 0; i < num_atoms; i ++ ) { - s_subtype = 0; - s_type = 0; - if ( at[i].endpoint ) { - if ( (tg = t_group_info->tGroupNumber[at[i].endpoint]) && - at[i].endpoint == t_group_info->t_group[tg-=1].nGroupNumber ) { - bHasH = (int)t_group_info->t_group[tg].num[0] - (int)t_group_info->t_group[tg].num[1]; - } else { - nError = BNS_PROGRAM_ERR; - break; - } - } else { - bHasH = (int)at[i].num_H; - } - if ( bHasH && at[i].endpoint || /* tautomeric atoms */ - - /* non-tautomeric heteroatoms that - (a) have H and - (b) may be donors of H - therefore may exchange isotopic-non-isotopic H */ - bHasH && - (0 == (s_type = GetSaltChargeType( at, i, t_group_info, &s_subtype )) || - /* -C=O or =C-OH, O = S, Se, Te */ - - /*(t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT) &&*/ - 1 == (s_type = GetOtherSaltChargeType( at, i, t_group_info, &s_subtype, 1/* bAccept_O*/ )) || - /* =Z-MH or -Z=M, Z = centerpoint, M = endpoint, other than above. M may be N */ - - 2 == (s_type = GetOtherSaltType( at, i, &s_subtype )) || - /* >C-SH, >C-S(-); S=S,Se,Te */ - - /* other proton donor or acceptor */ - bHasAcidicHydrogen( at, i) && ((s_type=3), (s_subtype = SALT_p_DONOR)) || - bHasAcidicMinus( at, i) && ((s_type=3), (s_subtype = SALT_p_ACCEPTOR)) || - bHasOtherExchangableH (at, i) && ((s_type=3), (s_subtype = SALT_DONOR_H)) ) - - ) { - - if ( nNumCandidates >= nMaxNumCandidates ) { - return BNS_VERT_EDGE_OVFL; - } - s_candidate[nNumCandidates].atnumber = i; - s_candidate[nNumCandidates].type = s_type; - s_candidate[nNumCandidates].subtype = s_subtype; - s_candidate[nNumCandidates].endpoint = at[i].endpoint; - nNumCandidates ++; - nNumNonTautCandidates += !at[i].endpoint; - s_subtype_all |= s_subtype; - } - } - if ( nError ) { - return nError; - } - if ( nNumCandidates > 0 ) { - t_group_info->nIsotopicEndpointAtomNumber = (AT_NUMB *)inchi_calloc( nNumNonTautCandidates+1, sizeof(t_group_info->nIsotopicEndpointAtomNumber[0])); - t_group_info->nIsotopicEndpointAtomNumber[0] = nNumNonTautCandidates; - for ( i = 0, n = 1; i < nNumCandidates; i ++ ) { - k = s_candidate[i].atnumber; - if ( !at[k].endpoint ) { - t_group_info->nIsotopicEndpointAtomNumber[n++] = k; - } - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { - t_group_info->num_iso_H[j] += at[k].num_iso_H[j]; - } - at[k].cFlags |= AT_FLAG_ISO_H_POINT; - } - t_group_info->nNumIsotopicEndpoints = nNumNonTautCandidates+1; - } - } - return nNumCandidates; -} - -/*#else*/ /* } DISCONNECT_SALTS == 0 */ - -/********************************************************************************** - Charges and tautomeric endpoints (N only) - ********************************************************************************** - - H = number of possibly moveable hydrogen atoms - C = possibly moveable positive charge - - - = single bond - = = double bond - # = triple bond - -+-----------------------------------------------------------------------------+ -|ca-| H | edges to t- | 1 bond | 2 bonds | 3 bonds *) | -|se | C | and c-groups | (valence) | (valence) | (valence) | -| # | | (edges flow) | | | | -+---|------+---------------+----------------+----------------+----------------| -| 1 | H=0 | -- (1) | =NH (3) | =N- (3) | >N- (3) | -| | C=0 | == | | | | -+---|------+---------------+----------------+----------------+----------------| -| 2 | H=1 | == (2) | -NH2 (3) | -NH- (3) | none | -| | C=0 | == | | | | -+---|------+---------------+----------------+----------------+----------------| -| 3 | H=0 | -- (0) | #NH(+) (4) | =N(+)= (4) +)| >N(+)= (4) | -| | C=1 | -- | (prohibited | | | -| | | | by edge cap) | | | -+---|------+---------------+----------------+----------------+----------------| -| 4 | H=1 | == (1) | =NH2(+) (4) +)| =NH(+)- (4) +)| >NH(+)- (4) | -| | C=1 | -- | | | | -+---+-------------------------------------------------------------------------+ - - *) Cannot be a tautomeric endpoint - - +) The three charged types of atoms [=N(+)=, =NH(+)-, =NH2(+)] should be - checked for possible H-tautomerism. Other types in the marked by *) - column should not be checked as long as H(+) exchange is not considered - tautomeric. - - Other possibilities: -NH3(+) >NH2(+) >N(+)< cannot be H-tautomeric endpoints. - - Case #1 (H=0, C=0) and #4 (H=1,C=0) is indistinguishable from the - viewpoint of edges flow and capacities except for flow from N to (+) vertex. - - Without taking precautions H(+) can be transferred - - from =NH2(+) to =NH, - from =NH(+)- to =N-, - from >NH(+)- to >N- - - or to any other appropriate atom that has a lone electron pair and bonds - will not change. In this case no bond must be marked as tautomeric. - - For this reason before attempting to transfer H from one endpoint to - another the charges on the two atoms should be set to zero by - forcing zero flow from each of atoms to the (+)-vertices if the - atoms belong to a c-group. - - **********************************************************************************/ - - -/***********************************************************************************/ -/* MarkTautomerGroups: do not identify positively charged N as endpoints for now */ -/***********************************************************************************/ -int MarkTautomerGroups( inp_ATOM *at, int num_atoms, T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info - , struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD ) -{ - int i, j, k, m, endpoint_valence, centerpoint, endpoint, bond_type, nMobile, num_changes=0, tot_changes=0; - T_ENDPOINT EndPoint[MAXVAL]; - T_BONDPOS BondPos[MAXVAL]; - AT_NUMB nGroupNumber; - int bDiffGroups; - int nNumEndPoints, nNumBondPos, nNumPossibleMobile; - int bTautBond, bNonTautBond, bAltBond; - int nNumDonor, nNumAcceptor, bPossiblyEndpoint; - T_GROUP *t_group; - int *pnum_t, max_num_t, bIgnoreIsotopic; - ENDPOINT_INFO eif1, eif2; - int nErr = 0; -#define ALLOWED_EDGE(PBNS, IAT,IBOND) ( !(PBNS) || !(PBNS)->edge || !(PBNS)->vert || !(PBNS)->edge[(PBNS)->vert[IAT].iedge[IBOND]].forbidden) -#define ACTUAL_ORDER(PBNS, IAT,IBOND, BTYPE) ( ((PBNS) && (PBNS)->edge && (PBNS)->vert &&\ - ((BTYPE)==BOND_ALT_123 || (BTYPE)==BOND_ALT_13 || (BTYPE)==BOND_ALT_23))? (PBNS)->edge[(PBNS)->vert[IAT].iedge[IBOND]].flow+BOND_TYPE_SINGLE:(BTYPE)) - - - if ( !t_group_info || !(t_group_info->bTautFlags & TG_FLAG_TEST_TAUT__ATOMS) ) - return 0; - /* initial t_group allocation */ - if ( !t_group_info->t_group && !t_group_info->max_num_t_groups ) { - INCHI_MODE bTautFlags = t_group_info->bTautFlags; /* save initial setting */ - INCHI_MODE bTautFlagsDone = t_group_info->bTautFlagsDone; /* save previous findings, if any */ - TNI tni = t_group_info->tni; - AT_NUMB *tGroupNumber = t_group_info->tGroupNumber; - bIgnoreIsotopic = t_group_info->bIgnoreIsotopic; - memset( t_group_info, 0, sizeof(*t_group_info) ); - t_group_info->bIgnoreIsotopic = bIgnoreIsotopic; /* restore initial setting */ - t_group_info->bTautFlags = bTautFlags; - t_group_info->bTautFlagsDone = bTautFlagsDone; - t_group_info->tni = tni; - t_group_info->tGroupNumber = tGroupNumber; - t_group_info->max_num_t_groups = num_atoms/2+1; /* upper limit */ - if (!(t_group_info->t_group = (T_GROUP*)inchi_calloc(t_group_info->max_num_t_groups, sizeof(t_group[0])))) { - return (t_group_info->max_num_t_groups = -1); /* failed, out of RAM */ - } - } - /* check if t_group_info exists */ - if ( !t_group_info->t_group || !t_group_info->max_num_t_groups ) - return 0; - - if ( 0 > t_group_info->max_num_t_groups ) - return t_group_info->max_num_t_groups; - - pnum_t = &t_group_info->num_t_groups; /* number of found tautomer endpoint groups */ - t_group = t_group_info->t_group; - max_num_t = t_group_info->max_num_t_groups; - bIgnoreIsotopic = t_group_info->bIgnoreIsotopic; - /* 1-3 tautomers */ - for ( i = 0; i < num_atoms; i ++ ) { - /* find possible endpoint Z = at[i] */ - if ( endpoint_valence = nGetEndpointInfo( at, i, &eif1 ) ) { - /* 1st endpoint candidate found. Find centerpoint candidate */ - for ( j = 0; j < at[i].valence; j ++ ) { - bond_type = (int)at[i].bond_type[j] & ~BOND_MARK_ALL; -#if ( FIX_BOND23_IN_TAUT == 1 ) - bond_type = ACTUAL_ORDER(pBNS,i,j,bond_type); -#endif - centerpoint = (int)at[i].neighbor[j]; /* a centerpoint candidate */ - if ( (bond_type == BOND_DOUBLE || - bond_type == BOND_ALTERN || - bond_type == BOND_ALT12NS || - bond_type == BOND_TAUTOM) && is_centerpoint_elem( at[centerpoint].el_number ) - && ALLOWED_EDGE(pBNS, i, j) - ) { - /* test a centerpoint candidate. */ - /* find all endpoints including at[i] and store them into EndPoint[] */ - nNumPossibleMobile = 0; - nGroupNumber = (AT_NUMB)num_atoms; /* greater than any tautomeric group number */ - bDiffGroups = -1; /* ignore the first difference */ - nNumDonor = nNumAcceptor = 0; - for ( k = 0, nNumEndPoints = 0, nNumBondPos = 0; k < at[centerpoint].valence; k ++ ) { - endpoint = at[centerpoint].neighbor[k]; /* endpoint candidate */ - bond_type = (int)at[centerpoint].bond_type[k] & ~BOND_MARK_ALL; -#if ( FIX_BOND23_IN_TAUT == 1 ) - bond_type = ACTUAL_ORDER(pBNS,centerpoint,k,bond_type); -#endif - bTautBond = - bNonTautBond = - bAltBond = - bPossiblyEndpoint = 0; - if ( !ALLOWED_EDGE(pBNS, centerpoint, k) ) { - continue; - } else - if ( bond_type == BOND_ALTERN || bond_type == BOND_ALT12NS || bond_type == BOND_TAUTOM ) { - bTautBond = 1; -#if ( REPLACE_ALT_WITH_TAUT == 1 ) - bAltBond = (bond_type == BOND_ALTERN || bond_type == BOND_ALT12NS); -#endif - } else - if ( bond_type == BOND_SINGLE || bond_type == BOND_DOUBLE ) - bNonTautBond = 1; - else - continue; - - if ( !(endpoint_valence = nGetEndpointInfo( at, endpoint, &eif1 )) ) - continue; /* not an endpoint element or can't have mobile groups */ - /* save information about the found possible tautomeric endpoint */ - /* 2 = T_NUM_NO_ISOTOPIC non-isotopic values */ - nMobile = - AddAtom2num( EndPoint[nNumEndPoints].num, at, endpoint, 2 ); /* fill out */ - AddAtom2DA( EndPoint[nNumEndPoints].num_DA, at, endpoint, 2 ); - /* --- why is isitopic info missing ? -- see below - nMobile = EndPoint[nNumEndPoints].num[1] = (at[endpoint].charge == -1); - nMobile = EndPoint[nNumEndPoints].num[0] = at[endpoint].num_H + nMobile; - */ - if ( bNonTautBond ) { - m = (bond_type == BOND_SINGLE && (nMobile || at[endpoint].endpoint)); - nNumDonor += m; - bPossiblyEndpoint += m; - m = (bond_type == BOND_DOUBLE ); - nNumAcceptor += m; - bPossiblyEndpoint += m; - } else { - /* tautomeric or alternating bond */ - m = (0 != at[endpoint].endpoint || eif1.cDonor ); - nNumDonor += m; - bPossiblyEndpoint += m; - m = ( at[endpoint].endpoint || - eif1.cNeutralBondsValence > at[endpoint].valence ); - nNumAcceptor += m; - bPossiblyEndpoint += m; - } - if ( !bPossiblyEndpoint ) - continue; - EndPoint[nNumEndPoints].nGroupNumber = at[endpoint].endpoint; /* =0 if it is an endpoint for the 1st time */ - EndPoint[nNumEndPoints].nEquNumber = 0; - EndPoint[nNumEndPoints].nAtomNumber = (AT_NUMB)endpoint; - if ( nGroupNumber != at[endpoint].endpoint ) { - bDiffGroups ++; - nGroupNumber = at[endpoint].endpoint; - } - - /* save positions of all, not only possibly tautomeric bonds */ -#if ( REPLACE_ALT_WITH_TAUT != 1 ) - if ( bNonTautBond || bAltBond ) { -#endif - BondPos[nNumBondPos].nAtomNumber = (AT_NUMB)centerpoint; - BondPos[nNumBondPos].neighbor_index = (AT_NUMB)k; /* bond ordering number; used to change bonds to tautomeric only */ - nNumBondPos ++; -#if ( REPLACE_ALT_WITH_TAUT != 1 ) - } -#endif - /* mobile group is possible if (a) the endpoint has a mobile group or */ - /* (b) the centerpoint is adjacent to another endpoint */ - nNumPossibleMobile += (nMobile>0 || at[endpoint].endpoint); - nNumEndPoints ++; - } - if ( nNumEndPoints > 1 && nNumPossibleMobile && nNumDonor && nNumAcceptor ) { - /* - * a tautomeric group has been found - * - * at this point: - * nGroupNumber = 0 if all endpoints belong to a newly discovered tautomeric group - * bDiffGroups > 0 if at least 2 tautomeric groups are to be merged (one of them can be new) - * case (nGroupNumber != 0 && bDiffGroups = 0 ) ignored because all endpoints belong to the same known t-group - * case (nGroupNumber != 0 && bDiffGroups < 0 ) cannot happen - */ - - nErr=FindAccessibleEndPoints( EndPoint, &nNumEndPoints, BondPos, &nNumBondPos, - pBNS, pBD, at, num_atoms, c_group_info, ALT_PATH_MODE_TAUTOM ); - if ( IS_BNS_ERROR(nErr) ) { - return nErr; - } - nErr = 0; - - if ( nNumEndPoints > 0 ) { - if ( !nGroupNumber || bDiffGroups > 0 ) { - num_changes = RegisterEndPoints( t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS ); - if ( num_changes == -1 ) { - nErr = CT_TAUCOUNT_ERR; - } - if ( num_changes < 0 ) { - nErr = num_changes; - } - if ( nErr ) - goto exit_function; - tot_changes += (num_changes>0); - } - if ( nNumBondPos > 0 ) { - /* some of the bonds have not been marked as tautomeric yet */ - num_changes = SetTautomericBonds( at, nNumBondPos, BondPos ); - tot_changes += (num_changes>0); - } - } - } - } - } - } - } -#if ( KETO_ENOL_TAUT == 1 ) /***** post v.1 feature *****/ - if ( t_group_info->bTautFlags & TG_FLAG_KETO_ENOL_TAUT ) { - /* 1,3 keto-enol tautomerism */ - for ( i = 0; i < num_atoms; i ++ ) { - /* find possible endpoint Z = at[i] */ - if ( endpoint_valence = nGetEndpointInfo_KET( at, i, &eif1 ) ) { - /* 1st endpoint candidate found. Find centerpoint candidate */ - for ( j = 0; j < at[i].valence; j ++ ) { - bond_type = (int)at[i].bond_type[j] & ~BOND_MARK_ALL; -#if ( FIX_BOND23_IN_TAUT == 1 ) - bond_type = ACTUAL_ORDER(pBNS,i,j,bond_type); -#endif - centerpoint = (int)at[i].neighbor[j]; /* a centerpoint candidate */ - if ( (bond_type == BOND_DOUBLE || - bond_type == BOND_ALTERN || - bond_type == BOND_ALT12NS || - bond_type == BOND_TAUTOM) && - is_centerpoint_elem_KET( at[centerpoint].el_number ) && - !at[centerpoint].charge && !at[centerpoint].radical && - /* only normal carbon is allowed */ - 4 == at[centerpoint].chem_bonds_valence + at[centerpoint].num_H - && ALLOWED_EDGE(pBNS, i, j) - ) { - int num_O = 0; - int num_C = 0; - /* test a centerpoint candidate. */ - /* find all endpoints including at[i] and store them into EndPoint[] */ - nNumPossibleMobile = 0; - nGroupNumber = (AT_NUMB)num_atoms; /* greater than any tautomeric group number */ - bDiffGroups = -1; /* ignore the first difference */ - nNumDonor = nNumAcceptor = 0; - for ( k = 0, nNumEndPoints = 0, nNumBondPos = 0; k < at[centerpoint].valence; k ++ ) { - endpoint = at[centerpoint].neighbor[k]; /* endpoint candidate */ - bond_type = (int)at[centerpoint].bond_type[k] & ~BOND_MARK_ALL; -#if ( FIX_BOND23_IN_TAUT == 1 ) - bond_type = ACTUAL_ORDER(pBNS,centerpoint,k,bond_type); -#endif - bTautBond = - bNonTautBond = - bAltBond = - bPossiblyEndpoint = 0; - if ( !ALLOWED_EDGE(pBNS, centerpoint, k) ) { - continue; - } else - if ( bond_type == BOND_ALTERN || bond_type == BOND_ALT12NS || bond_type == BOND_TAUTOM ) { - bTautBond = 1; -#if ( REPLACE_ALT_WITH_TAUT == 1 ) - bAltBond = (bond_type == BOND_ALTERN || bond_type == BOND_ALT12NS); -#endif - } else - if ( bond_type == BOND_SINGLE || bond_type == BOND_DOUBLE ) - bNonTautBond = 1; - else - continue; - - if ( !(endpoint_valence = nGetEndpointInfo_KET( at, endpoint, &eif2 )) ) { - continue; - } - /* - if ( 3 != eif1.cKetoEnolCode + eif2.cKetoEnolCode && endpoint != i ) - continue; - */ - /* save information about the found possible tautomeric endpoint */ - /* 2 = T_NUM_NO_ISOTOPIC non-isotopic values */ - nMobile = - AddAtom2num( EndPoint[nNumEndPoints].num, at, endpoint, 2 ); /* fill out */ - AddAtom2DA( EndPoint[nNumEndPoints].num_DA, at, endpoint, 2 ); - /* --- why is isitopic info missing ? -- see below - nMobile = EndPoint[nNumEndPoints].num[1] = (at[endpoint].charge == -1); - nMobile = EndPoint[nNumEndPoints].num[0] = at[endpoint].num_H + nMobile; - */ - if ( bNonTautBond ) { - m = (bond_type == BOND_SINGLE && (nMobile || at[endpoint].endpoint)); - nNumDonor += m; - bPossiblyEndpoint += m; - m = (bond_type == BOND_DOUBLE ); - nNumAcceptor += m; - bPossiblyEndpoint += m; - } else { - /* tautomeric or alternating bond */ - m = (0 != at[endpoint].endpoint || eif1.cDonor ); - nNumDonor += m; - bPossiblyEndpoint += m; - m = ( at[endpoint].endpoint || - eif1.cNeutralBondsValence > at[endpoint].valence ); - nNumAcceptor += m; - bPossiblyEndpoint += m; - } - if ( !bPossiblyEndpoint ) - continue; - - num_O += (endpoint_valence == 2); - num_C += (endpoint_valence == 4); - - EndPoint[nNumEndPoints].nGroupNumber = at[endpoint].endpoint; /* =0 if it is an endpoint for the 1st time */ - EndPoint[nNumEndPoints].nEquNumber = 0; - EndPoint[nNumEndPoints].nAtomNumber = (AT_NUMB)endpoint; - if ( nGroupNumber != at[endpoint].endpoint ) { - bDiffGroups ++; - nGroupNumber = at[endpoint].endpoint; - } - - /* save positions of all, not only possibly tautomeric bonds */ -#if ( REPLACE_ALT_WITH_TAUT != 1 ) - if ( bNonTautBond || bAltBond ) { -#endif - BondPos[nNumBondPos].nAtomNumber = (AT_NUMB)centerpoint; - BondPos[nNumBondPos].neighbor_index = (AT_NUMB)k; /* bond ordering number; used to change bonds to tautomeric only */ - nNumBondPos ++; -#if ( REPLACE_ALT_WITH_TAUT != 1 ) - } -#endif - /* mobile group is possible if (a) the endpoint has a mobile group or */ - /* (b) the centerpoint is adjacent to another endpoint */ - nNumPossibleMobile += (nMobile>0 || at[endpoint].endpoint); - nNumEndPoints ++; - } - if ( nNumEndPoints > 1 && nNumPossibleMobile && nNumDonor && nNumAcceptor && num_O==1 && num_C ) { - /* - * a tautomeric group has been found - * - * at this point: - * nGroupNumber = 0 if all endpoints belong to a newly discovered tautomeric group - * bDiffGroups > 0 if at least 2 tautomeric groups are to be merged (one of them can be new) - * case (nGroupNumber != 0 && bDiffGroups = 0 ) ignored because all endpoints belong to the same known t-group - * case (nGroupNumber != 0 && bDiffGroups < 0 ) cannot happen - */ - - nErr=FindAccessibleEndPoints( EndPoint, &nNumEndPoints, BondPos, &nNumBondPos, - pBNS, pBD, at, num_atoms, c_group_info, ALT_PATH_MODE_TAUTOM_KET ); - if ( IS_BNS_ERROR(nErr) ) { - return nErr; - } - nErr = 0; - - if ( nNumEndPoints > 0 ) { - if ( !nGroupNumber || bDiffGroups > 0 ) { - num_changes = RegisterEndPoints( t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS ); - if ( num_changes == -1 ) { - nErr = CT_TAUCOUNT_ERR; - } - if ( num_changes < 0 ) { - nErr = num_changes; - } - if ( nErr ) - goto exit_function; - tot_changes += (num_changes>0); - } - if ( nNumBondPos > 0 ) { - /* some of the bonds have not been marked as tautomeric yet */ - num_changes = SetTautomericBonds( at, nNumBondPos, BondPos ); - tot_changes += (num_changes>0); - } - } - } - } - } - } - } - } -#endif /* KETO_ENOL_TAUT */ - -#if ( TAUT_OTHER == 1 ) /* { */ - if ( !tot_changes ) { -#define MAX_ALT_PATH_LEN 8 - int nMaxLenDfsPath = MAX_ALT_PATH_LEN; - int i1, i2; - AT_RANK *nDfsPathPos = (AT_RANK *)inchi_calloc( num_atoms, sizeof(nDfsPathPos[0]) ); - DFS_PATH DfsPath[MAX_ALT_PATH_LEN]; - int ret; - if ( !nDfsPathPos || !DfsPath ) { - tot_changes = CT_OUT_OF_RAM; /* */ - goto free_memory; - } -#if ( TAUT_15_NON_RING == 1 ) /***** post v.1 feature *****/ - if ( t_group_info->bTautFlags & TG_FLAG_1_5_TAUT ) { - /* 1,5 tautomerism; one of the endpoints should no be on a ring */ - /* - O OH O - || | || - A--pos- A--pos- A--pos- - / sib- // sib- ? / sib- - C ly C ly CH ly - \\ a <--> \ a <--> \ a - B--ring B--ring B--ring - | || || - NH N N - - Note: few recent modifications now allow the terminal N be in a ring, too - */ - for ( i1 = 0; i1 < num_atoms; i1 ++ ) { - /* find possible endpoint Z = at[i1] */ - if ( !(endpoint_valence = nGetEndpointInfo( at, i1, &eif1 ) ) /*|| - at[i1].nNumAtInRingSystem > 1*/ ) { - continue; /* not a possibly endpoint */ - } - - if ( 1 ) { - nNumEndPoints = 0; - nNumBondPos = 0; - - ret = nGet15TautInAltPath( at, i1, nDfsPathPos, - DfsPath, nMaxLenDfsPath, - EndPoint, sizeof(EndPoint)/sizeof(EndPoint[0]), - BondPos, sizeof(BondPos)/sizeof(BondPos[0]), - &nNumEndPoints, &nNumBondPos, - pBNS, pBD, num_atoms); - if ( ret > 0 ) { - if ( nNumEndPoints ) { - num_changes = RegisterEndPoints( t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS); - if ( num_changes == -1 ) { - nErr = CT_TAUCOUNT_ERR; - } - if ( num_changes < 0 ) { - nErr = num_changes; - } - if ( nErr ) - goto free_memory; - tot_changes += (num_changes > 0); - } - if ( nNumBondPos ) { - tot_changes += ( 0 < SetTautomericBonds( at, nNumBondPos, BondPos ) ); - } - } else - if ( IS_BNS_ERROR( ret ) ) { - nErr = ret; - goto free_memory; - } - } - } - } -#endif -#if ( TAUT_4PYRIDINOL_RINGS == 1 ) - /* 6-member rings */ - /* - O OH OH - || | | - / \ // \ / \\ - || || <--> | || <--> || | - \ / \\ / \ // - NH N N - */ - for ( i1 = 0; i1 < num_atoms; i1 ++ ) { - /* find possible endpoint Z = at[i1] */ - if ( 3 != (endpoint_valence = nGetEndpointInfo( at, i1, &eif1 ) ) || - 2 != at[i1].valence ) { - continue; /* not a nitrogen atom or a wrong valence */ - } - - if ( at[i1].nNumAtInRingSystem >= 6 ) { - nNumEndPoints = 0; - nNumBondPos = 0; - - ret = nGet15TautIn6MembAltRing( at, i1, nDfsPathPos, - DfsPath, nMaxLenDfsPath, - EndPoint, sizeof(EndPoint)/sizeof(EndPoint[0]), - BondPos, sizeof(BondPos)/sizeof(BondPos[0]), - &nNumEndPoints, &nNumBondPos, - pBNS, pBD, num_atoms); - if ( ret > 0 ) { - if ( nNumEndPoints ) { - num_changes = RegisterEndPoints( t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS); - if ( num_changes == -1 ) { - nErr = CT_TAUCOUNT_ERR; - } - if ( num_changes < 0 ) { - nErr = num_changes; - } - if ( nErr ) - goto free_memory; - tot_changes += (num_changes > 0); - } - if ( nNumBondPos ) { - tot_changes += ( 0 < SetTautomericBonds( at, nNumBondPos, BondPos ) ); - } - } else - if ( IS_BNS_ERROR( ret ) ) { - nErr = ret; - goto free_memory; - } - } - } -#endif /* TAUT_4PYRIDINOL_RINGS */ -#if ( TAUT_PYRAZOLE_RINGS == 1 ) - /* 5-member rings: - - Z Z - / \\ // \ - X Y <--> X Y - \\ / \ // - N--NH HN--N - - ^ ^ - search for these NH - */ - /* 5-member rings (pyrazole derivatives): look for the neighboring N */ - for ( i1 = 0; i1 < num_atoms; i1 ++ ) { - if ( 2 == at[i1].valence && - at[i1].nNumAtInRingSystem >= 5 && - 3 == (endpoint_valence = nGetEndpointInfo( at, i1, &eif1 )) - ) { - nMobile = at[i1].num_H + (at[i1].charge == -1); - for ( j = 0; j < at[i1].valence; j ++ ) { - int nMobile2, endpoint_valence2; - i2 = at[i1].neighbor[j]; - - /* may be important */ - if ( i2 >= i1 ) - continue; /* do not try same pair 2 times */ - - if ( at[i2].nRingSystem != at[i1].nRingSystem ) - continue; - - bond_type = (at[i1].bond_type[j] & ~BOND_MARK_ALL); - if ( bond_type != BOND_SINGLE && - bond_type != BOND_TAUTOM && - bond_type != BOND_ALT12NS && - bond_type != BOND_ALTERN || /* added 1-15-2002 */ - 2 != at[i2].valence || - 3 != (endpoint_valence2 = nGetEndpointInfo( at, i2, &eif2 ) ) ) { - continue; /* not a nitrogen atom or a wrong valence or not a single bond */ - } - nMobile2 = at[i2].num_H + (at[i2].charge == -1); /* number of mobile groups */ -#if ( TAUT_IGNORE_EQL_ENDPOINTS == 1 ) - if ( at[i1].endpoint && at[i1].endpoint == at[i2].endpoint ) - continue; /* atoms already belong to the same t-group */ -#endif - if ( !at[i1].endpoint && !at[i2].endpoint && 1!=nMobile + nMobile2 ) - continue; - - ret = nGet12TautIn5MembAltRing( at, i1, j, nDfsPathPos, - DfsPath, nMaxLenDfsPath, - EndPoint, sizeof(EndPoint)/sizeof(EndPoint[0]), - BondPos, sizeof(BondPos)/sizeof(BondPos[0]), - &nNumEndPoints, &nNumBondPos - , pBNS, pBD, num_atoms); - if ( ret > 0 ) { - if ( nNumEndPoints ) { - num_changes = RegisterEndPoints( t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS); - if ( num_changes == -1 ) { - nErr = CT_TAUCOUNT_ERR; - } - if ( num_changes < 0 ) { - nErr = num_changes; - } - if ( nErr ) - goto free_memory; - tot_changes += (num_changes > 0); - } - if ( nNumBondPos ) { - tot_changes += ( 0 < SetTautomericBonds( at, nNumBondPos, BondPos ) ); - } - } else - if ( IS_BNS_ERROR( ret ) ) { - nErr = ret; - goto free_memory; - } - } - } - } -#endif /* TAUT_PYRAZOLE_RINGS */ -#if ( TAUT_TROPOLONE_7 == 1 || TAUT_TROPOLONE_5 == 1 ) /* { */ - /******************************************************** - * A B - * | || - * 7-member rings (tropolones): look for M=Q--R--ZH, - * ^ ^ ^ ^ - * endpoint1 i1 i2 endpoint2 - * where A-Q-R=B belong to a 7-member alt. (except Q-R bond) ring: ..=A-(Q-R)=B-.. - * Bond Q-R should be single or tautomeric or alternating - * M=Q and R-ZH should be chain (non-ring) bonds - * Same for 5-member rings - */ - for ( i1 = 0; i1 < num_atoms; i1 ++ ) { - if ( at[i1].nNumAtInRingSystem >= -#if ( TAUT_TROPOLONE_5 == 1 ) - 5 -#else - 7 -#endif - && - bIsCenterPointStrict( at, i1 ) && -#if ( TAUT_RINGS_ATTACH_CHAIN == 1 ) - at[i1].bCutVertex && -#endif - at[i1].valence == 3 && !at[i1].endpoint ) { - int nMobile1, endpoint1, endpoint1_valence, bond_type1; - int nMobile2, endpoint2, endpoint2_valence, bond_type2; - for ( j = 0; j < at[i1].valence; j ++ ) { - i2 = at[i1].neighbor[j]; - /* - // may be important - if ( i2 > i1 ) - continue; // do not try same pair 2 times - */ - if ( at[i2].nRingSystem != at[i1].nRingSystem || - !bIsCenterPointStrict( at, i2 ) || -#if ( TAUT_RINGS_ATTACH_CHAIN == 1 ) - !at[i2].bCutVertex || -#endif - at[i2].valence != 3 || at[i2].endpoint ) - continue; - bond_type = (at[i1].bond_type[j] & ~BOND_MARK_ALL); - if ( bond_type != BOND_SINGLE && - bond_type != BOND_TAUTOM && - bond_type != BOND_ALT12NS && - bond_type != BOND_ALTERN ) { - continue; /* not a single bond between Q-R */ - } - /* find endpoints */ - for ( k = 0; k < at[i1].valence; k ++ ) { - endpoint1 = at[i1].neighbor[k]; - if ( endpoint1 == i2 ) - continue; /* j == k */ - if ( !(endpoint1_valence = nGetEndpointInfo( at, endpoint1, &eif1 ) ) ) - continue; /* not an endpoint1 element or can't have mobile groups */ -#if ( TAUT_RINGS_ATTACH_CHAIN == 1 ) - if ( at[endpoint1].nRingSystem == at[i1].nRingSystem ) - continue; -#endif - nMobile1 = at[endpoint1].num_H + (at[endpoint1].charge == -1); /* number of mobile groups */ - if ( nMobile1 + at[endpoint1].chem_bonds_valence != endpoint1_valence ) - continue; /* abnormal endpoint1 valence; ignore. */ - bond_type1 = (at[i1].bond_type[k] & ~BOND_MARK_ALL); - - if ( bond_type1 != BOND_SINGLE && - bond_type1 != BOND_DOUBLE && - bond_type1 != BOND_TAUTOM && - bond_type1 != BOND_ALT12NS && - bond_type1 != BOND_ALTERN ) - continue; - - for ( m = 0; m < at[i2].valence; m ++ ) { - endpoint2 = at[i2].neighbor[m]; - if ( endpoint2 == i1 ) - continue; - if ( !(endpoint2_valence = nGetEndpointInfo( at, endpoint2, &eif2 )) ) - continue; /* not an endpoint2 element or can't have mobile groups */ -#if ( TAUT_RINGS_ATTACH_CHAIN == 1 ) - if ( at[endpoint2].nRingSystem == at[i2].nRingSystem ) - continue; -#endif - nMobile2 = at[endpoint2].num_H + (at[endpoint2].charge == -1); /* number of mobile groups */ - bond_type2 = (at[i2].bond_type[m] & ~BOND_MARK_ALL); - - if ( bond_type2 != BOND_SINGLE && - bond_type2 != BOND_DOUBLE && - bond_type2 != BOND_TAUTOM && - bond_type2 != BOND_ALT12NS && - bond_type2 != BOND_ALTERN ) - continue; - - /* final test for possible tautomerism */ - nMobile = 0; - - if ( ALLOWED_EDGE(pBNS, i1, k) && ALLOWED_EDGE(pBNS, i2, m) ) { - - /* can mobile group move from 1 to 2? */ - nMobile += (at[endpoint1].endpoint || nMobile1) && /* from endpoint1 */ - (bond_type1 != BOND_DOUBLE) && - - (at[endpoint2].endpoint || /* to endpoint2 */ - eif2.cNeutralBondsValence > at[endpoint2].valence ) && - (bond_type2 != BOND_SINGLE); - - - /* can mobile group move from 2 to 1? */ - nMobile += (at[endpoint2].endpoint || nMobile2) && /* from endpoint2 */ - (bond_type2 != BOND_DOUBLE) && /*changed from BOND_SINGLE 2004-02-26 */ - - (at[endpoint1].endpoint || /* to endpoint1 */ - eif1.cNeutralBondsValence > at[endpoint1].valence ) && - (bond_type1 != BOND_SINGLE); - } - if ( !nMobile ) - continue; - - if ( bond_type1 == bond_type2 && - (bond_type1 == BOND_SINGLE || bond_type1 == BOND_DOUBLE) ) - continue; - /* -- old -- - if ( !at[endpoint1].endpoint && !at[endpoint2].endpoint && 1 != nMobile1 + nMobile2 ) - continue; - */ - /* -- new -- - - if ( !at[endpoint1].endpoint && !at[endpoint2].endpoint ) { - if ( !(bond_type1 == BOND_SINGLE || bond_type1 == BOND_DOUBLE) || - !(bond_type2 == BOND_SINGLE || bond_type2 == BOND_DOUBLE) ) { - // at this point bond_type1 != bond_type2 - continue; - } - if ( bond_type1 == BOND_SINGLE && !nMobile1 || - bond_type2 == BOND_SINGLE && !nMobile2 || - 0 == nMobile1 + nMobile2 ) { - continue; - } - } - */ -#if ( TAUT_TROPOLONE_7 == 1 ) - if ( at[i1].nNumAtInRingSystem >= 7 ) { - ret = nGet14TautIn7MembAltRing( at, i1, j, k, m, nDfsPathPos, - DfsPath, nMaxLenDfsPath, - EndPoint, sizeof(EndPoint)/sizeof(EndPoint[0]), - BondPos, sizeof(BondPos)/sizeof(BondPos[0]), - &nNumEndPoints, &nNumBondPos, - pBNS, pBD, num_atoms); - if ( ret > 0 ) { - if ( nNumEndPoints ) { - num_changes = RegisterEndPoints( t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS); - if ( num_changes == -1 ) { - nErr = CT_TAUCOUNT_ERR; - } - if ( num_changes < 0 ) { - nErr = num_changes; - } - if ( nErr ) - goto free_memory; - tot_changes += (num_changes > 0); - } - if ( nNumBondPos ) { - tot_changes += ( 0 < SetTautomericBonds( at, nNumBondPos, BondPos ) ); - } - } else - if ( IS_BNS_ERROR( ret ) ) { - nErr = ret; - goto free_memory; - } - } -#endif - -#if ( TAUT_TROPOLONE_5 == 1 ) - if ( at[i1].nNumAtInRingSystem >= 5 ) { - ret = nGet14TautIn5MembAltRing( at, i1, j, k, m, nDfsPathPos, - DfsPath, nMaxLenDfsPath, - EndPoint, sizeof(EndPoint)/sizeof(EndPoint[0]), - BondPos, sizeof(BondPos)/sizeof(BondPos[0]), - &nNumEndPoints, &nNumBondPos, - pBNS, pBD, num_atoms); - if ( ret > 0 ) { - if ( nNumEndPoints ) { - num_changes = RegisterEndPoints( t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS); - if ( num_changes == -1 ) { - nErr = CT_TAUCOUNT_ERR; - } - if ( num_changes < 0 ) { - nErr = num_changes; - } - if ( nErr ) - goto free_memory; - tot_changes += (num_changes > 0); - } - if ( nNumBondPos ) { - tot_changes += ( 0 < SetTautomericBonds( at, nNumBondPos, BondPos ) ); - } - } else - if ( IS_BNS_ERROR( ret ) ) { - nErr = ret; - goto free_memory; - } - } -#endif - } - } - } - } - } -#endif /* } TAUT_TROPOLONE */ -free_memory: - if ( nDfsPathPos ) { - inchi_free( nDfsPathPos ); - } -#undef MAX_ALT_PATH_LEN - } -#endif /* } FIND_RING_SYSTEMS */ -exit_function: - return nErr < 0? nErr : tot_changes; -} - - - -/*****************************************************************************/ -int free_t_group_info( T_GROUP_INFO *t_group_info ) -{ - if ( t_group_info ) { - if ( t_group_info->t_group ) { - inchi_free( t_group_info->t_group ); - } - if ( t_group_info->nEndpointAtomNumber ) { - inchi_free( t_group_info->nEndpointAtomNumber ); - } - if ( t_group_info->tGroupNumber ) { - inchi_free( t_group_info->tGroupNumber ); - } - if ( t_group_info->nIsotopicEndpointAtomNumber ) { - inchi_free( t_group_info->nIsotopicEndpointAtomNumber ); - } - memset( t_group_info, 0, sizeof(*t_group_info)); - } - return 0; -} - - - -/*****************************************************************************/ -int make_a_copy_of_t_group_info( T_GROUP_INFO *t_group_info, T_GROUP_INFO *t_group_info_orig ) -{ - int err = 0, len; - free_t_group_info( t_group_info ); - if ( t_group_info_orig && t_group_info ) { - if ( (len=t_group_info_orig->max_num_t_groups) > 0 ) { - if (t_group_info->t_group = - (T_GROUP*) inchi_malloc( len * sizeof(t_group_info->t_group[0]))) { - memcpy(t_group_info->t_group, - t_group_info_orig->t_group, - len * sizeof(t_group_info->t_group[0])); - } else { - err ++; - } - } - if ( (len = t_group_info_orig->nNumEndpoints) > 0 ) { - if (t_group_info->nEndpointAtomNumber = - (AT_NUMB*) inchi_malloc( len * sizeof(t_group_info->nEndpointAtomNumber[0]))) { - memcpy(t_group_info->nEndpointAtomNumber, - t_group_info_orig->nEndpointAtomNumber, - len * sizeof(t_group_info->nEndpointAtomNumber[0])); - } else { - err ++; - } - } - if ( (len = t_group_info_orig->num_t_groups) > 0 ) { - if (t_group_info->tGroupNumber = - (AT_NUMB*) inchi_malloc( len * TGSO_TOTAL_LEN * sizeof(t_group_info->tGroupNumber[0]))) { - memcpy(t_group_info->tGroupNumber, - t_group_info_orig->tGroupNumber, - len * TGSO_TOTAL_LEN * sizeof(t_group_info->tGroupNumber[0])); - } else { - err ++; - } - } - if ( (len = t_group_info_orig->nNumIsotopicEndpoints) > 0 ) { - if (t_group_info->nIsotopicEndpointAtomNumber = - (AT_NUMB*) inchi_malloc( len * sizeof(t_group_info->nIsotopicEndpointAtomNumber[0]))) { - memcpy(t_group_info->nIsotopicEndpointAtomNumber, - t_group_info_orig->nIsotopicEndpointAtomNumber, - len * sizeof(t_group_info->nIsotopicEndpointAtomNumber[0])); - } else { - err ++; - } - } - if ( !err ) { - t_group_info->nNumEndpoints = t_group_info_orig->nNumEndpoints; - t_group_info->num_t_groups = t_group_info_orig->num_t_groups; - t_group_info->max_num_t_groups = t_group_info_orig->max_num_t_groups; - t_group_info->bIgnoreIsotopic = t_group_info_orig->bIgnoreIsotopic; - t_group_info->nNumIsotopicEndpoints = t_group_info_orig->nNumIsotopicEndpoints; - t_group_info->tni = t_group_info_orig->tni; - /* - t_group_info->nNumRemovedExplicitH = t_group_info_orig->nNumRemovedExplicitH; - t_group_info->nNumRemovedProtons = t_group_info_orig->nNumRemovedProtons; - t_group_info->bNormalizationFlags = t_group_info_orig->bNormalizationFlags; - */ - /* - t_group_info->bHardAddedRemovedProtons = t_group_info_orig->bHardAddedRemovedProtons; - t_group_info->bSimpleAddedRemovedProtons = t_group_info_orig->bSimpleAddedRemovedProtons; - t_group_info->nNumCanceledCharges = t_group_info_orig->nNumCanceledCharges; - */ - } - t_group_info->bTautFlags = t_group_info_orig->bTautFlags; - t_group_info->bTautFlagsDone = t_group_info_orig->bTautFlagsDone; - } - return err; -} - - - -/*****************************************************************************/ -/* Set tautomer group isotopic sort keys */ -/*****************************************************************************/ -int set_tautomer_iso_sort_keys( T_GROUP_INFO *t_group_info ) -{ - T_GROUP *t_group; - T_GROUP_ISOWT Mult = 1; - int i, j, num_t_groups, num_iso_t_groups = 0; - if ( !t_group_info || !(t_group = t_group_info->t_group) || - 0 >= (num_t_groups = t_group_info->num_t_groups) || t_group_info->nNumIsotopicEndpoints ) - return 0; - for ( i = 0; i < num_t_groups; i ++ ) { - t_group[i].iWeight = 0; - j = T_NUM_ISOTOPIC - 1; - Mult = 1; - do { - t_group[i].iWeight += Mult * (T_GROUP_ISOWT)t_group[i].num[T_NUM_NO_ISOTOPIC+j]; - } while ( --j >= 0 && (Mult *= T_GROUP_ISOWT_MULT) ); - num_iso_t_groups += (t_group[i].iWeight != 0); - } - return num_iso_t_groups; -} - -/****************************************************************************** - * - * Fill t_group_info with information necessary to fill out tautomer part - * of the linear connection table record. - * Note: on input, t_group_info should contain information created by MarkTautomerGroups() - * No previous t_group_info adjustment due to throwing out disconnected parts of - * the chemical structure is needed. - * - * Note2: throws out t_groups containing negative charges only (IGNORE_TGROUP_WITHOUT_H==1) - * (leave their tautomeric bonds unchanged) - * Note3: removes negative charges from other tautomeric groups - * and adjust counts of mobile atoms if permitted (REMOVE_TGROUP_CHARGE==1) - */ -int CountTautomerGroups( sp_ATOM *at, int num_atoms, T_GROUP_INFO *t_group_info ) -{ - int i, j, ret = 0, nNumEndpoints, max_t_group, num_groups_noH; - - AT_NUMB nGroupNumber, nNewGroupNumber, *nCurrEndpointAtNoPos = NULL; - - T_GROUP *t_group; - int num_t; - /* int bIgnoreIsotopic, max_num_t; */ - AT_NUMB *nTautomerGroupNumber = NULL; - AT_NUMB *nEndpointAtomNumber = NULL; - AT_NUMB *tGroupNumber = NULL; - - if ( !t_group_info || !t_group_info->t_group || 0 >= t_group_info->max_num_t_groups ) { - return 0; /* empty t-groups */ - } - num_t = t_group_info->num_t_groups; - t_group = t_group_info->t_group; - /* - max_num_t = t_group_info->max_num_t_groups; - bIgnoreIsotopic = t_group_info->bIgnoreIsotopic; - */ - num_groups_noH = 0; - - /* the following 2 arrays are to be rebuilt here */ - if ( t_group_info->nEndpointAtomNumber ) { - inchi_free ( t_group_info->nEndpointAtomNumber ); - t_group_info->nEndpointAtomNumber = NULL; - } - if ( t_group_info->tGroupNumber ) { - inchi_free ( t_group_info->tGroupNumber ); - t_group_info->tGroupNumber = NULL; - } - /* find max_t_group */ - for ( i = 0, max_t_group = 0; i < t_group_info->num_t_groups; i ++ ) { - if ( max_t_group < t_group[i].nGroupNumber ) - max_t_group = t_group[i].nGroupNumber; - } - /* allocate memory for temp storage of numbers of endpoints */ - if ( max_t_group && - !(nTautomerGroupNumber = (AT_NUMB*) inchi_calloc( max_t_group+1, sizeof(nTautomerGroupNumber[0]) ) /*temp*/ ) ) { - goto err_exit_function; /* program error: out of RAM */ /* */ - } - - /* count endpoints for each tautomer group */ - for ( i = 0, nNumEndpoints = 0; i < num_atoms; i ++ ) { - if ( (j = at[i].endpoint) == 0 ) - continue; - if ( j > max_t_group ) /* debug only */ - goto err_exit_function; /* program error */ /* */ - nTautomerGroupNumber[j] ++; - nNumEndpoints ++; - } - - if ( !nNumEndpoints ) { - goto exit_function; /* not a tautomer */ - } - - /* allocate temporary array */ - if ( !(nEndpointAtomNumber = (AT_NUMB*) inchi_calloc( nNumEndpoints, sizeof(nEndpointAtomNumber[0]) ) ) || - !(nCurrEndpointAtNoPos = (AT_NUMB*) inchi_calloc( num_t, sizeof(nCurrEndpointAtNoPos[0]) ) /*temp*/ ) ) { - goto err_exit_function; /* program error: out of RAM */ /* */ - } - /* - * Remove missing endpoints from t_group. Since only one - * disconnected part is processed, some endpoints groups may have disappeared. - * Mark t_groups containing charges only for subsequent removal - */ - for ( i = 0, nNewGroupNumber = 0; i < num_t; /*i ++*/ ) { - int bNoH = 0, nNumH; - nGroupNumber = t_group[i].nGroupNumber; - for ( j = 1, nNumH = t_group[i].num[0]; j < T_NUM_NO_ISOTOPIC; j ++ ) { - nNumH -= (int)t_group[i].num[j]; - } - if ( t_group[i].nNumEndpoints != nTautomerGroupNumber[(int)nGroupNumber] -#if ( IGNORE_TGROUP_WITHOUT_H == 1 ) - || (bNoH = (t_group[i].num[0]==t_group[i].num[1])) /* only for (H,-) t-groups; (+) t-groups are not removed */ -#endif - ) { - if ( !nTautomerGroupNumber[(int)nGroupNumber] || bNoH ) { - /* the group belongs to another disconnected part of the structure or has only charges */ - /* Remove the group */ - num_t --; - if ( i < num_t ) - memmove( t_group+i, t_group+i+1, (num_t-i)*sizeof(t_group[0]) ); - if ( bNoH ) { - /* group contains no mobile hydrogen atoms, only charges. Prepare to remove it. */ - nTautomerGroupNumber[(int)nGroupNumber] = 0; - num_groups_noH ++; - } - /*i --;*/ - } else { - /* different number of endpoints */ - goto err_exit_function; /* program error */ /* */ - } - } else { - /* renumber t_group and prepare to renumber at[i].endpoint */ - nTautomerGroupNumber[(int)nGroupNumber] = - t_group[i].nGroupNumber = ++nNewGroupNumber; /* = i+1 */ - /* get first group atom orig. number position in the nEndpointAtomNumber[] */ - /* and in the tautomer endpoint canon numbers part of the connection table */ - t_group[i].nFirstEndpointAtNoPos = nCurrEndpointAtNoPos[i] = - i? (t_group[i-1].nFirstEndpointAtNoPos+t_group[i-1].nNumEndpoints) : 0; - t_group[i].num[0] = nNumH; -#if ( REMOVE_TGROUP_CHARGE == 1 ) - t_group[i].num[1] = 0; /* remove only (-) charges */ -#endif - /* -- wrong condition. Disabled. - if ( t_group[i].nGroupNumber != i + 1 ) { // for debug only - goto err_exit_function; // program error - } - */ - i ++; - } - } - if ( num_t != nNewGroupNumber ) { /* for debug only */ - goto err_exit_function; /* program error */ /* */ - } - - /* check if any tautomer group was left */ - if ( !nNewGroupNumber ) { - if ( !num_groups_noH ) - goto err_exit_function; /* program error: not a tautomer */ /* */ - else - goto exit_function; - } - /* - * an array for tautomer group sorting later, at the time of storing Connection Table - * Later the sorting consists out of 2 steps: - * 1) Sort t_group[i].nNumEndpoints endpoint atom ranks within each endpoint group - * starting from t_group[i].nFirstEndpointAtNoPos; i = 0..t_group_info->num_t_groups-1 - * 2) Sort the groups indexes t_group_info->tGroupNumber[] - */ - if ( !(tGroupNumber= - (AT_NUMB*)inchi_calloc(nNewGroupNumber*TGSO_TOTAL_LEN, sizeof(tGroupNumber[0])))) { - goto err_exit_function; /* out of RAM */ - } - for ( i = 0; i < nNewGroupNumber; i ++ ) { - tGroupNumber[i] = (AT_NUMB)i; /* initialization: original t_group number = (at[i]->endpoint-1) */ - } - /* - * renumber endpoint atoms and save their orig. atom - * numbers for filling out the tautomer part of the LinearCT. - * nCurrEndpointAtNoPos[j] is an index of the atom number in the nEndpointAtomNumber[] - */ - for ( i = 0; i < num_atoms; i ++ ) { - if ( j = (int)at[i].endpoint ) { - j = (int)(at[i].endpoint = nTautomerGroupNumber[j])-1; /* new t_group number */ - if ( j >= 0 ) { /* j=-1 in case of no mobile hydrogen atoms (charges only), group being removed */ - if ( nCurrEndpointAtNoPos[j] >= /* debug only */ - t_group[j].nFirstEndpointAtNoPos+t_group[j].nNumEndpoints ) { - goto err_exit_function; /* program error */ /* */ - } - nEndpointAtomNumber[(int)nCurrEndpointAtNoPos[j] ++] = (AT_NUMB)i; - } else { - nNumEndpoints --; /* endpoint has been removed */ - } - } - } - t_group_info->num_t_groups = nNewGroupNumber; - t_group_info->nNumEndpoints = nNumEndpoints; - t_group_info->nEndpointAtomNumber = nEndpointAtomNumber; - t_group_info->tGroupNumber = tGroupNumber; /* only the 1st segment filled */ - inchi_free ( nTautomerGroupNumber ); - inchi_free ( nCurrEndpointAtNoPos ); - return nNumEndpoints + T_GROUP_HDR_LEN * nNewGroupNumber + 1; /* nLenLinearCTTautomer */ - -err_exit_function: - ret = CT_TAUCOUNT_ERR; -exit_function: - /* release allocated memory; set "no tautomeric group" */ - if ( nEndpointAtomNumber ) - inchi_free ( nEndpointAtomNumber ); - if ( nTautomerGroupNumber ) - inchi_free ( nTautomerGroupNumber ); - if ( tGroupNumber ) - inchi_free ( tGroupNumber ); - if ( nCurrEndpointAtNoPos ) - inchi_free ( nCurrEndpointAtNoPos ); - t_group_info->nNumEndpoints = 0; - t_group_info->num_t_groups = 0; - if ( !ret && ((t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) || - t_group_info->nNumIsotopicEndpoints>1 && (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE))) ) { - ret = 1; /* only protons have been (re)moved or neitralization happened */ - } - return ret; -} -#if ( READ_INCHI_STRING == 1 ) -#if ( INCLUDE_NORMALIZATION_ENTRY_POINT == 1 ) -/*****************************************************************************/ -int CountTautomerGroupsInpAt( inp_ATOM *at, int num_atoms, T_GROUP_INFO *t_group_info ) -{ - int i, j, ret = 0, nNumEndpoints, max_t_group, num_groups_noH; - - AT_NUMB nGroupNumber, nNewGroupNumber, *nCurrEndpointAtNoPos = NULL; - - T_GROUP *t_group; - int num_t; - /* int bIgnoreIsotopic, max_num_t; */ - AT_NUMB *nTautomerGroupNumber = NULL; - AT_NUMB *nEndpointAtomNumber = NULL; - AT_NUMB *tGroupNumber = NULL; - - if ( !t_group_info || !t_group_info->t_group || 0 >= t_group_info->max_num_t_groups ) { - return 0; /* empty t-groups */ - } - num_t = t_group_info->num_t_groups; - t_group = t_group_info->t_group; - /* - max_num_t = t_group_info->max_num_t_groups; - bIgnoreIsotopic = t_group_info->bIgnoreIsotopic; - */ - num_groups_noH = 0; - - /* the following 2 arrays are to be rebuilt here */ - if ( t_group_info->nEndpointAtomNumber ) { - inchi_free ( t_group_info->nEndpointAtomNumber ); - t_group_info->nEndpointAtomNumber = NULL; - } - if ( t_group_info->tGroupNumber ) { - inchi_free ( t_group_info->tGroupNumber ); - t_group_info->tGroupNumber = NULL; - } - /* find max_t_group */ - for ( i = 0, max_t_group = 0; i < t_group_info->num_t_groups; i ++ ) { - if ( max_t_group < t_group[i].nGroupNumber ) - max_t_group = t_group[i].nGroupNumber; - } - /* allocate memory for temp storage of numbers of endpoints */ - if ( max_t_group && - !(nTautomerGroupNumber = (AT_NUMB*) inchi_calloc( max_t_group+1, sizeof(nTautomerGroupNumber[0]) ) /*temp*/ ) ) { - goto err_exit_function; /* program error: out of RAM */ /* */ - } - - /* count endpoints for each tautomer group */ - for ( i = 0, nNumEndpoints = 0; i < num_atoms; i ++ ) { - if ( (j = at[i].endpoint) == 0 ) - continue; - if ( j > max_t_group ) /* debug only */ - goto err_exit_function; /* program error */ /* */ - nTautomerGroupNumber[j] ++; - nNumEndpoints ++; - } - - if ( !nNumEndpoints ) { - goto exit_function; /* not a tautomer */ - } - - /* allocate temporary array */ - if ( !(nEndpointAtomNumber = (AT_NUMB*) inchi_calloc( nNumEndpoints, sizeof(nEndpointAtomNumber[0]) ) ) || - !(nCurrEndpointAtNoPos = (AT_NUMB*) inchi_calloc( num_t, sizeof(nCurrEndpointAtNoPos[0]) ) /*temp*/ ) ) { - goto err_exit_function; /* program error: out of RAM */ /* */ - } - /* - * Remove missing endpoints from t_group. Since only one - * disconnected part is processed, some endpoints groups may have disappeared. - * Mark t_groups containing charges only for subsequent removal - */ - for ( i = 0, nNewGroupNumber = 0; i < num_t; /*i ++*/ ) { - int bNoH = 0, nNumH; - nGroupNumber = t_group[i].nGroupNumber; - for ( j = 1, nNumH = t_group[i].num[0]; j < T_NUM_NO_ISOTOPIC; j ++ ) { - nNumH -= (int)t_group[i].num[j]; - } - if ( t_group[i].nNumEndpoints != nTautomerGroupNumber[(int)nGroupNumber] -#if ( IGNORE_TGROUP_WITHOUT_H == 1 ) - || (bNoH = (t_group[i].num[0]==t_group[i].num[1])) /* only for (H,-) t-groups; (+) t-groups are not removed */ -#endif - ) { - if ( !nTautomerGroupNumber[(int)nGroupNumber] || bNoH ) { - /* the group belongs to another disconnected part of the structure or has only charges */ - /* Remove the group */ - num_t --; - if ( i < num_t ) - memmove( t_group+i, t_group+i+1, (num_t-i)*sizeof(t_group[0]) ); - if ( bNoH ) { - /* group contains no mobile hydrogen atoms, only charges. Prepare to remove it. */ - nTautomerGroupNumber[(int)nGroupNumber] = 0; - num_groups_noH ++; - } - /*i --;*/ - } else { - /* different number of endpoints */ - goto err_exit_function; /* program error */ /* */ - } - } else { - /* renumber t_group and prepare to renumber at[i].endpoint */ - nTautomerGroupNumber[(int)nGroupNumber] = - t_group[i].nGroupNumber = ++nNewGroupNumber; /* = i+1 */ - /* get first group atom orig. number position in the nEndpointAtomNumber[] */ - /* and in the tautomer endpoint canon numbers part of the connection table */ - t_group[i].nFirstEndpointAtNoPos = nCurrEndpointAtNoPos[i] = - i? (t_group[i-1].nFirstEndpointAtNoPos+t_group[i-1].nNumEndpoints) : 0; - t_group[i].num[0] = nNumH; -#if ( REMOVE_TGROUP_CHARGE == 1 ) - t_group[i].num[1] = 0; /* remove only (-) charges */ -#endif - /* -- wrong condition. Disabled. - if ( t_group[i].nGroupNumber != i + 1 ) { // for debug only - goto err_exit_function; // program error - } - */ - i ++; - } - } - if ( num_t != nNewGroupNumber ) { /* for debug only */ - goto err_exit_function; /* program error */ /* */ - } - - /* check if any tautomer group was left */ - if ( !nNewGroupNumber ) { - if ( !num_groups_noH ) - goto err_exit_function; /* program error: not a tautomer */ /* */ - else - goto exit_function; - } - /* - * an array for tautomer group sorting later, at the time of storing Connection Table - * Later the sorting consists out of 2 steps: - * 1) Sort t_group[i].nNumEndpoints endpoint atom ranks within each endpoint group - * starting from t_group[i].nFirstEndpointAtNoPos; i = 0..t_group_info->num_t_groups-1 - * 2) Sort the groups indexes t_group_info->tGroupNumber[] - */ - if ( !(tGroupNumber= - (AT_NUMB*)inchi_calloc(nNewGroupNumber*TGSO_TOTAL_LEN, sizeof(tGroupNumber[0])))) { - goto err_exit_function; /* out of RAM */ - } - for ( i = 0; i < nNewGroupNumber; i ++ ) { - tGroupNumber[i] = (AT_NUMB)i; /* initialization: original t_group number = (at[i]->endpoint-1) */ - } - /* - * renumber endpoint atoms and save their orig. atom - * numbers for filling out the tautomer part of the LinearCT. - * nCurrEndpointAtNoPos[j] is an index of the atom number in the nEndpointAtomNumber[] - */ - for ( i = 0; i < num_atoms; i ++ ) { - if ( j = (int)at[i].endpoint ) { - j = (int)(at[i].endpoint = nTautomerGroupNumber[j])-1; /* new t_group number */ - if ( j >= 0 ) { /* j=-1 in case of no mobile hydrogen atoms (charges only), group being removed */ - if ( nCurrEndpointAtNoPos[j] >= /* debug only */ - t_group[j].nFirstEndpointAtNoPos+t_group[j].nNumEndpoints ) { - goto err_exit_function; /* program error */ /* */ - } - nEndpointAtomNumber[(int)nCurrEndpointAtNoPos[j] ++] = (AT_NUMB)i; - } else { - nNumEndpoints --; /* endpoint has been removed */ - } - } - } - t_group_info->num_t_groups = nNewGroupNumber; - t_group_info->nNumEndpoints = nNumEndpoints; - t_group_info->nEndpointAtomNumber = nEndpointAtomNumber; - t_group_info->tGroupNumber = tGroupNumber; /* only the 1st segment filled */ - inchi_free ( nTautomerGroupNumber ); - inchi_free ( nCurrEndpointAtNoPos ); - return nNumEndpoints + T_GROUP_HDR_LEN * nNewGroupNumber + 1; /* nLenLinearCTTautomer */ - -err_exit_function: - ret = CT_TAUCOUNT_ERR; -exit_function: - /* release allocated memory; set "no tautomeric group" */ - if ( nEndpointAtomNumber ) - inchi_free ( nEndpointAtomNumber ); - if ( nTautomerGroupNumber ) - inchi_free ( nTautomerGroupNumber ); - if ( tGroupNumber ) - inchi_free ( tGroupNumber ); - if ( nCurrEndpointAtNoPos ) - inchi_free ( nCurrEndpointAtNoPos ); - t_group_info->nNumEndpoints = 0; - t_group_info->num_t_groups = 0; - if ( !ret && ((t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) || - t_group_info->nNumIsotopicEndpoints>1 && (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE))) ) { - ret = 1; /* only protons have been (re)moved or neitralization happened */ - } - return ret; -} -#endif -#endif - - - -/***************************************************************************** - * tautomers: Compare for sorting - *****************************************************************************/ -/* Compare for sorting Ranks only */ -/* Globals: pn_tRankForSort */ -int CompRankTautomer(const void* a1, const void* a2 ) -{ - int ret = (int)pn_tRankForSort[(int)(*(const AT_RANK*)a1)] - - (int)pn_tRankForSort[(int)(*(const AT_RANK*)a2)]; - return ret; -} - - - -/*****************************************************************************/ -int SortTautomerGroupsAndEndpoints( T_GROUP_INFO *t_group_info, int num_atoms, int num_at_tg, AT_RANK *nRank ) -{ - int i, nFirstEndpointAtNoPos, nNumEndpoints; - AT_NUMB *nEndpointAtomNumber; - int num_t_groups = num_at_tg - num_atoms; - T_GROUP *t_group = NULL; - /* check if sorting is required */ - - if ( num_t_groups <= 0 || t_group_info->nNumEndpoints < 2 ) { - return 0; /* no tautomer data */ - } - t_group = t_group_info->t_group; - /* sort endpoints within the groups */ - for ( i = 0; i < num_t_groups; i ++ ) { - if ( t_group[i].nNumEndpoints < 2 ) - continue; /* program error; should not happen */ /* */ - /* set globals for sorting */ - nFirstEndpointAtNoPos = t_group[i].nFirstEndpointAtNoPos; - nNumEndpoints = t_group[i].nNumEndpoints; - if ( nNumEndpoints + nFirstEndpointAtNoPos > t_group_info->nNumEndpoints ) { /* for debug only */ - return CT_TAUCOUNT_ERR; /* program error */ /* */ - } - nEndpointAtomNumber = t_group_info->nEndpointAtomNumber+(int)nFirstEndpointAtNoPos; - pn_tRankForSort = nRank; - insertions_sort( nEndpointAtomNumber, nNumEndpoints, sizeof(nEndpointAtomNumber[0]), CompRankTautomer); - } - /* sort the tautomeric groups according to their ranks only - (that is, ignoring the isotopic composition of the mobile groups and ranks of the endpoints) */ - if ( t_group_info->num_t_groups > 1 ) { - /* set globals for sorting */ - /* a hack: the ranks of all tautomeric groups are */ - /* located at nRank[num_atoms..num_at_tg-1] */ - pn_tRankForSort = nRank+num_atoms; - /* sort */ - /* ordering numbers to sort : t_group_info->tGroupNumber; */ - insertions_sort( t_group_info->tGroupNumber, num_t_groups, - sizeof(t_group_info->tGroupNumber[0]), CompRankTautomer); - } - return t_group_info->num_t_groups; -} +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include + +#include "mode.h" + +#include "ichitaut.h" +#include "ichicomn.h" +#include "ichitime.h" +#include "ichicant.h" +#include "util.h" + +/* Local prototypes */ + +int SetTautomericBonds( inp_ATOM *at, int nNumBondPos, T_BONDPOS *BondPos ); +int CompRankTautomer( const void* a1, const void* a2, void * ); +int RegisterEndPoints( CANON_GLOBALS *pCG, + T_GROUP_INFO *t_group_info, + /* T_GROUP *t_group, int *pnum_t, int max_num_t,*/ + T_ENDPOINT *EndPoint, + int nNumEndPoints, + inp_ATOM *at, + int num_atoms, + C_GROUP_INFO *cgi, + struct BalancedNetworkStructure *pBNS ); +int cmpTGroupNumber( const void *a1, const void *a2 ); +int comp_candidates( const void *a1, const void *a2 ); +int MoveEndpoint( inp_ATOM *at, + S_CANDIDATE *s_candidate, + AT_NUMB endpoint, + AT_NUMB *nTGroupNewNumbers, + AT_NUMB *nTGroupPosition, + int nNewTGroupOrd, + T_GROUP_INFO *t_group_info); +int FindAccessibleEndPoints( CANON_GLOBALS *pCG, + T_ENDPOINT *EndPoint, + int *nNumEndPoints, + T_BONDPOS *BondPos, + int *nNumBondPos, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD, + inp_ATOM *at, + int num_atoms, + C_GROUP_INFO *cgi, + int taut_mode ); +int GetChargeType( inp_ATOM *atom, int iat, S_CHAR *cChargeSubtype ); +int GetNeutralRepsIfNeeded( AT_NUMB *pri, + AT_NUMB *prj, + inp_ATOM *at, + int num_atoms, + T_ENDPOINT *EndPoint, + int nNumEndPoints, + C_GROUP_INFO *cgi ); +int bCanBeACPoint( inp_ATOM *at, + S_CHAR cCharge, + S_CHAR cChangeValence, + S_CHAR neutral_bonds_valence, + S_CHAR neutral_valence, + S_CHAR nEndpointValence, S_CHAR *cChargeSubtype ); +int CmpCCandidates( const void *a1, const void *a2 ); +int RegisterCPoints( C_GROUP *c_group, + int *pnum_c, + int max_num_c, + T_GROUP_INFO *t_group_info, + int point1, + int point2, + int ctype, + inp_ATOM *at, + int num_atoms ); +int GetSaltChargeType( inp_ATOM *at, + int at_no, + T_GROUP_INFO *t_group_info, + int *s_subtype ); +int GetOtherSaltChargeType( inp_ATOM *at, + int at_no, + T_GROUP_INFO *t_group_info, + int *s_subtype, + int bAccept_O ); +int MergeSaltTautGroupsBlind( inp_ATOM *at, + int s_type, int num_atoms, + S_GROUP_INFO *s_group_info, + int nNumCandidates, + T_GROUP_INFO *t_group_info, + C_GROUP_INFO *c_group_info, + struct BalancedNetworkStructure *pBNS ); +int ConnectSaltTGroups2SuperTGroup( inp_ATOM *at, + int num_atoms, + S_GROUP_INFO *s_group_info, + int nNumCandidates, + T_GROUP_INFO *t_group_info, + C_GROUP_INFO *c_group_info, + struct BalancedNetworkStructure *pBNS, + int *nNewTGroupNumber, int *vertSuperTGroup ); +int bDoNotMergeNonTautAtom( inp_ATOM *at, int at_no ); +int GetOtherSaltType( inp_ATOM *at, int at_no, int *s_subtype ); + +/* Bits for GetChargeType */ +#define C_SUBTYPE_CHARGED 0 +#define C_SUBTYPE_p_DONOR 1 /* new */ +#define C_SUBTYPE_p_ACCEPT 2 /* new */ +#define C_SUBTYPE_H_ACCEPT 4 +#define C_SUBTYPE_H_DONOR 8 +#define C_SUBTYPE_NEUTRAL 16 + +/* Internal stack array size */ +#define MAX_STACK_ARRAY_LEN 127 +#define MAX_TGROUP_ARRAY_LEN 127 + + +/* + is_centerpoint... functions +*/ + + +int is_centerpoint_elem( U_CHAR el_number ) +{ + static U_CHAR el_numb[12]; + static int len; + int len2; + int i; + if (!len) + { + len2=0; + el_numb[len2++] = (U_CHAR)get_periodic_table_number( "C" ); + el_numb[len2++] = (U_CHAR)get_periodic_table_number( "N" ); + el_numb[len2++] = (U_CHAR)get_periodic_table_number( "P" ); + el_numb[len2++] = (U_CHAR)get_periodic_table_number( "S" ); + el_numb[len2++] = (U_CHAR)get_periodic_table_number( "I" ); + el_numb[len2++] = (U_CHAR)get_periodic_table_number( "As" ); + el_numb[len2++] = (U_CHAR)get_periodic_table_number( "Sb" ); + el_numb[len2++] = (U_CHAR)get_periodic_table_number( "Se" ); + el_numb[len2++] = (U_CHAR)get_periodic_table_number( "Te" ); + el_numb[len2++] = (U_CHAR)get_periodic_table_number( "Cl" ); + el_numb[len2++] = (U_CHAR)get_periodic_table_number( "Br" ); + len=len2; + } + for ( i = 0; i < len; i ++ ) { + if ( el_numb[i] == el_number ) { + return 1; + } + } + return 0; +} + + +#if ( KETO_ENOL_TAUT == 1 ) /* post v.1 feature */ + + +int is_centerpoint_elem_KET( U_CHAR el_number ) +{ + static U_CHAR el_numb[1]; + static int len; + int i; + if ( !el_numb[0] && !len ) { + el_numb[len++] = (U_CHAR)get_periodic_table_number( "C" ); + } + for ( i = 0; i < len; i ++ ) { + if ( el_numb[i] == el_number ) { + return 1; + } + } + return 0; +} +#endif + + +int is_centerpoint_elem_strict( U_CHAR el_number ) +{ + static U_CHAR el_numb[6]; + static int len; + int len2; + int i; + if (!len) + { + len2=0; + el_numb[len2++] = (U_CHAR)get_periodic_table_number( "C" ); + el_numb[len2++] = (U_CHAR)get_periodic_table_number( "N" ); + el_numb[len2++] = (U_CHAR)get_periodic_table_number( "P" ); + el_numb[len2++] = (U_CHAR)get_periodic_table_number( "As" ); + el_numb[len2++] = (U_CHAR)get_periodic_table_number( "Sb" ); + len=len2; + } + for ( i = 0; i < len; i ++ ) { + if ( el_numb[i] == el_number ) { + return 1; + } + } + return 0; +} + + +/* + AddAtom2... +*/ + + +int AddAtom2num( AT_RANK num[], inp_ATOM *atom, int at_no, int bSubtract ) +{ /* bSubtract: 0=> add, 1=>subtract, 2=> fill */ + inp_ATOM *at = atom + at_no; + int k; + int nMobile = (at->charge == -1); + if ( bSubtract == 1 ) { + /* 1: subtract */ + num[1] -= nMobile; + nMobile += at->num_H; + num[0] -= nMobile; + for ( k = 0; k < T_NUM_ISOTOPIC; k ++ ) { + /* T (3H isotope) first because it has higher weight */ + num[T_NUM_NO_ISOTOPIC+k] -= at->num_iso_H[NUM_H_ISOTOPES-k-1]; + } + } else { + if ( bSubtract == 2 ) { + /* fill */ + memset( num, 0, (T_NUM_NO_ISOTOPIC + T_NUM_ISOTOPIC)*sizeof(num[0]) ); + } + /* else (0): add */ + num[1] += nMobile; + nMobile += at->num_H; + num[0] += nMobile; + for ( k = 0; k < T_NUM_ISOTOPIC; k ++ ) { + /* T (3H isotope) first because it has higher weight */ + num[T_NUM_NO_ISOTOPIC+k] += at->num_iso_H[NUM_H_ISOTOPES-k-1]; + } + } + return nMobile; +} + + +void AddAtom2DA( AT_RANK num_DA[], inp_ATOM *atom, int at_no, int bSubtract ) +{ /* bSubtract: 0=> add, 1=>subtract, 2=> fill */ + inp_ATOM *at = atom + at_no; + int nDelta, nAcidic_O; + + if (at->charge < -1 || at->charge == 1 && !at->c_point || at->charge > 1 ) + return; + + nDelta = ( bSubtract == 1 )? -1 : 1; + + /* "Acidic" O, S, Se, Te recognition */ + if ( at->at_type & ATT_ACIDIC_CO ) { + nAcidic_O = nDelta; + } else { + nAcidic_O = 0; + } + + if ( bSubtract == 2 ) { /* 2: fill, otherwise add */ + memset( num_DA, 0, TG_NUM_DA * sizeof(num_DA[0]) ); + } + if ( at->charge <= 0 && at->valence == at->chem_bonds_valence || + /* neutral or negative donor */ + at->charge > 0 && at->valence + 1 == at->chem_bonds_valence + /* positively charged donor */ + ) { + if ( at->charge < 0 ) { + num_DA[TG_Num_dM] += nDelta; + num_DA[TG_Num_dO] += nAcidic_O; + } else + if ( at->num_H ) { + num_DA[TG_Num_dH] += nDelta; + num_DA[TG_Num_dO] += nAcidic_O; + } + } else + if ( at->charge <= 0 && at->valence + 1 == at->chem_bonds_valence || + at->charge > 0 && at->valence + 2 == at->chem_bonds_valence ) { + /* acceptor */ + if ( at->charge < 0 ) { + num_DA[TG_Num_aM] += nDelta; + } else + if ( at->num_H ) { + num_DA[TG_Num_aH] += nDelta; + } else { + num_DA[TG_Num_aO] += nAcidic_O; /* acidic O-acceptor has no H or charge */ + } + } + return; +} + + +/* + +*/ + + +int AddEndPoint( T_ENDPOINT *pEndPoint, inp_ATOM *at, int iat ) +{ + pEndPoint->nAtomNumber = iat; + pEndPoint->nEquNumber = 0; + pEndPoint->nGroupNumber = at[iat].endpoint; + if ( at[iat].endpoint ) { + /* already an endpoint */ + memset( pEndPoint->num, 0, sizeof(pEndPoint->num) ); + } else { + /* not an endpoint yet, make it an endpoint */ + AddAtom2num( pEndPoint->num, at, iat, 2 ); /* fill */ + AddAtom2DA( pEndPoint->num_DA, at, iat, 2 ); + /* + nMobile = pEndPoint->num[1] = (at[iat].charge == -1); + nMobile = pEndPoint->num[0] = at[iat].num_H + nMobile; + for ( k = 0; k < T_NUM_ISOTOPIC; k ++ ) { + pEndPoint->num[T_NUM_NO_ISOTOPIC+k] = at[iat].num_iso_H[NUM_H_ISOTOPES-k-1]; + } + */ + } + return 0; +} + + +int nGetEndpointInfo( inp_ATOM *atom, int iat, ENDPOINT_INFO *eif ) +{ + int nEndpointValence; + int nMobile; + S_CHAR cChargeSubtype; + + if ( atom[iat].radical && atom[iat].radical != RADICAL_SINGLET ) + return 0; /* a radical */ + if ( !(nEndpointValence = get_endpoint_valence( atom[iat].el_number )) ) + return 0; /* not an endpoint */ + if ( nEndpointValence <= atom[iat].valence ) + return 0; /* not an endpoint, for example >N(+)< or >N< or >O(+)- or >O- or >N- or -O- */ + + if ( atom[iat].charge == -1 || atom[iat].charge == 0 ) { + /* not a positive charge-point */ + if ( nEndpointValence < atom[iat].chem_bonds_valence ) + return 0; /* abnormal valence > standard endpoint valence */ + nMobile = atom[iat].num_H + (atom[iat].charge == -1); + if ( nMobile + atom[iat].chem_bonds_valence != nEndpointValence ) + return 0; /* non-standard endpoint valence */ + switch ( atom[iat].chem_bonds_valence - atom[iat].valence ) { + case 0: + eif->cDonor = 1; + eif->cAcceptor = 0; + break; + case 1: + eif->cDonor = 0; + eif->cAcceptor = 1; + break; + default: + return 0; + } + eif->cMobile = nMobile; + eif->cNeutralBondsValence = nEndpointValence-nMobile; + eif->cMoveableCharge = 0; +#if ( KETO_ENOL_TAUT == 1 ) + eif->cKetoEnolCode = 0; +#endif + return nEndpointValence; + } else + if ( atom[iat].c_point && + 0 <= GetChargeType( atom, iat, &cChargeSubtype ) && + ((int)cChargeSubtype & (C_SUBTYPE_H_ACCEPT|C_SUBTYPE_H_DONOR)) + ) { + /* charge-point */ + if ( cChargeSubtype & C_SUBTYPE_H_ACCEPT ) { + eif->cDonor = 0; + eif->cAcceptor = 1; + } else + if ( cChargeSubtype & C_SUBTYPE_H_DONOR ) { + eif->cDonor = 1; + eif->cAcceptor = 0; + } else { + return 0; + } + eif->cMobile = atom[iat].num_H; + eif->cNeutralBondsValence = nEndpointValence-atom[iat].num_H; + eif->cMoveableCharge = atom[iat].charge; +#if ( KETO_ENOL_TAUT == 1 ) + eif->cKetoEnolCode = 0; +#endif + return nEndpointValence; + } + return 0; +} + + +/*****************************************************************************/ +#if ( KETO_ENOL_TAUT == 1 ) /* post v.1 feature */ +int nGetEndpointInfo_KET( inp_ATOM *atom, + int iat, + ENDPOINT_INFO *eif ) +{ + int nEndpointValence; + int nMobile; + S_CHAR cChargeSubtype; + + if ( atom[iat].radical && atom[iat].radical != RADICAL_SINGLET ) + return 0; /* a radical */ + if ( !(nEndpointValence = get_endpoint_valence_KET( atom[iat].el_number )) ) + return 0; /* not an endpoint; only O and C can be an endpoint for keto-enol tautomerism */ + if ( nEndpointValence <= atom[iat].valence ) + return 0; /* not an endpoint, for example >N(+)< or >N< or >O(+)- or >O- or >N- or -O- */ + if ( nEndpointValence == 4 && atom[iat].valence < 2 ) + return 0; /* exclude O==C--CH3 <=> HO--C==CH2 */ + if ( nEndpointValence == 2 && atom[iat].valence > 1 ) + return 0; /* exclude --O--C==CH-- */ + + if ( atom[iat].charge == -1 || atom[iat].charge == 0 ) { + /* not a positive charge-point */ + if ( nEndpointValence < atom[iat].chem_bonds_valence ) + return 0; /* abnormal valence > standard endpoint valence */ + nMobile = atom[iat].num_H + (atom[iat].charge == -1); + if ( nMobile + atom[iat].chem_bonds_valence != nEndpointValence ) + return 0; /* non-standard endpoint valence */ + switch ( atom[iat].chem_bonds_valence - atom[iat].valence ) { + case 0: + eif->cDonor = 1; + eif->cAcceptor = 0; + break; + case 1: + eif->cDonor = 0; + eif->cAcceptor = 1; + break; + default: + return 0; + } + eif->cMobile = nMobile; + eif->cNeutralBondsValence = nEndpointValence-nMobile; + eif->cMoveableCharge = 0; + eif->cKetoEnolCode = (nEndpointValence == 2)? 1 : (nEndpointValence == 4)? 2 : 0; + return nEndpointValence; + } else + if ( atom[iat].c_point && + 0 <= GetChargeType( atom, iat, &cChargeSubtype ) && + ((int)cChargeSubtype & (C_SUBTYPE_H_ACCEPT|C_SUBTYPE_H_DONOR)) + ) { + /* charge-point; currently only O for keto-enol tautomerism */ + if ( cChargeSubtype & C_SUBTYPE_H_ACCEPT ) { + eif->cDonor = 0; + eif->cAcceptor = 1; + } else + if ( cChargeSubtype & C_SUBTYPE_H_DONOR ) { + eif->cDonor = 1; + eif->cAcceptor = 0; + } else { + return 0; + } + eif->cMobile = atom[iat].num_H; + eif->cNeutralBondsValence = nEndpointValence-atom[iat].num_H; + eif->cMoveableCharge = atom[iat].charge; + eif->cKetoEnolCode = (nEndpointValence == 2)? 1 : (nEndpointValence == 4)? 2 : 0; + return nEndpointValence; + } + return 0; +} +#endif +/*****************************************************************************/ + + +/* + RegisterEndPoints + + Returns >0 => new registration happened, + =0 => no changes, + -1 => program error (debug) +*/ +int RegisterEndPoints( CANON_GLOBALS *pCG, + T_GROUP_INFO *t_group_info, + /* T_GROUP *t_group, int *pnum_t, int max_num_t,*/ + T_ENDPOINT *EndPoint, + int nNumEndPoints, + inp_ATOM *at, + int num_atoms, + C_GROUP_INFO *cgi, + struct BalancedNetworkStructure *pBNS ) +{ + T_GROUP *t_group = t_group_info->t_group; + int *pnum_t = &t_group_info->num_t_groups; + int max_num_t = t_group_info->max_num_t_groups; + int nNumZeroEqu, nNumNewTGroups; + AT_NUMB group, prev_group, prev_eqnum, nNextGroupNumber, nLeastGroupNumber; + int nNumGroups, num_t, difference; + int i, j, k, ret; + AT_NUMB nNewTgNumberStackArray[MAX_STACK_ARRAY_LEN+1]; + AT_NUMB nGroupNumberStackArray[MAX_STACK_ARRAY_LEN+1]; + AT_NUMB nGroupNewNumberStackArray[MAX_STACK_ARRAY_LEN+1]; + AT_NUMB *nNewTgNumber = nNewTgNumberStackArray; + AT_NUMB *nGroupNumber = nGroupNumberStackArray; + AT_NUMB *nGroupNewNumber = nGroupNewNumberStackArray; + + if ( nNumEndPoints <= 0 ) + return 0; /* nothing to do */ + num_t = *pnum_t; + difference = 0; + nNextGroupNumber = 0; + nNumZeroEqu = 0; + ret = 0; + /* find max group number; increment it to obtain next available group number */ + for ( i = 0; i < num_t; i ++ ) { + if ( nNextGroupNumber < t_group[i].nGroupNumber ) + nNextGroupNumber = t_group[i].nGroupNumber; + } + nNextGroupNumber ++; + + /* find min non-zero group number nLeastGroupNumber; + count zero EndPoint[i].nEquNumber + if all EndPoint[i].nGroupNumber are equal and non-zero then exit: nothing to do. + */ + nLeastGroupNumber = nNextGroupNumber; + prev_group = EndPoint[0].nGroupNumber; + prev_eqnum = EndPoint[0].nEquNumber; + for ( i = j = k = 0; i < nNumEndPoints; i ++ ) { + if ( group = EndPoint[i].nGroupNumber ) { + if ( group < nLeastGroupNumber ) { + nLeastGroupNumber = group; + } + } + j += (prev_group == EndPoint[i].nGroupNumber); /* count endpoints that belong to the 1st group */ + k += (prev_eqnum == EndPoint[i].nEquNumber); /* count endpoints that belongo to a group equivalent to the 1st group */ + nNumZeroEqu += !EndPoint[i].nEquNumber; /* count endpoints that have been processed by FindAccessibleEndPoints() */ + } + if ( j == nNumEndPoints && prev_group && k == nNumEndPoints ) { + /* all endpoints already belong to one t-group; + the last comparison is not needed for now + because EndPoint[i].nEquNumber cannot make + endpont partitioning finer + */ + return 0; + } + + nNumNewTGroups = 0; + + if ( !nNumZeroEqu ) { + /* EndPoint[] has been processed by FindAccessibleEndPoints; + * equal EndPoint[i].nEquNumber mark endpoints belonging to + * the same t-group + * Since now the next available t-group number, nNextGroupNumber, + * is known,replace fict. IDs assigned by FindAccessibleEndPoints + * with correct new t-group numbers. + */ + for ( i = 0; i < nNumEndPoints; i ++ ) { + if ( (group = EndPoint[i].nEquNumber) >= nNextGroupNumber ) { + /* replace fict. IDs assigned by FindAccessibleEndPoints() with new t-group numbers */ + /* these fict. IDs have values = (num_atoms+1), (num_atoms+2),...; they may be non-contiguous */ + for ( j = 0; j < nNumNewTGroups; j ++ ) { + if ( group == nGroupNewNumber[j] ) + break; + } + if ( j == nNumNewTGroups ) { + /* found new fict. ID = group */ + if ( j == MAX_STACK_ARRAY_LEN && nGroupNewNumber == nGroupNewNumberStackArray ) { + /* stack array overflow; allocate more memory than may be needed */ + nGroupNewNumber = (AT_NUMB *) inchi_malloc(nNumEndPoints*sizeof(nGroupNewNumber[0])); + if ( !nGroupNewNumber ) { + ret = -1; + goto exit_function; + } + memcpy( nGroupNewNumber, nGroupNewNumberStackArray, nNumNewTGroups*sizeof(nGroupNewNumber[0])); + } + /* save newly found fict. t-group ID to compare to the next values of EndPoint[].nEquNumber */ + nGroupNewNumber[j] = group; + nNumNewTGroups ++; + } + EndPoint[i].nEquNumber = nNextGroupNumber + j; + } + } /* after this point the values just stored in nGroupNewNumber[] will not + be used. However, the obtained nNumNewTGroups value will be used */ + } else + if ( nNumZeroEqu == nNumEndPoints ) { + /* EndPoint[] has NOT been processed by FindAccessibleEndPoints; + all atoms and t-groups to which endpoints belong should be merged into a single t-group + */ + if ( nLeastGroupNumber == nNextGroupNumber ) { + /* flag to create a new t-group: none of the found + * endpoints belong to an already known t-group + */ + nNumNewTGroups = 1; /* otherwise 0 */ + } + /* All EndPoint[*].nEquNumber are zeroes. All endpoints will + * belong to one new or old t-group; its ID is nLeastGroupNumber. + * Set EndPoint[i].nEquNumber = nLeastGroupNumber; + */ + for ( i = 0; i < nNumEndPoints; i ++ ) { + EndPoint[i].nEquNumber = nLeastGroupNumber; + } + } else { + ret = -1; /* program error: only some of EndPoint[i].nEquNumber are zero */ /* */ + goto exit_function; + } + + if ( nNumNewTGroups ) { + /* create new nNumNewTGroups t-group(s) */ + if ( num_t + nNumNewTGroups > max_num_t ) { + ret = -1; /* found too many t-groups */ /* */ + goto exit_function; + } + /* initialize new t-group(s) */ + memset( t_group + num_t, 0, nNumNewTGroups * sizeof(t_group[0]) ); + for ( i = 0; i < nNumNewTGroups; i ++ ) { + t_group[num_t+i].nGroupNumber = nNextGroupNumber + i; + } + } + + /* At this point: + * EndPoint[i].nGroupNumber == 0 => the endpoint atom does not belong to a t-group yet + * EndPoint[i].nGroupNumber > 0 => current t-group ID of the endpoint atom + * EndPoint[i].nEquNumber --> new ID of a tautomeric group of this endpoint atom + * EndPoint[i].nAtomNumber --> number of the endpoint atom + */ + + nNumGroups = 0; /* counts the groups to be renumbered */ + for ( i = j = 0; i < nNumEndPoints; i ++ ) { + if ( group = EndPoint[i].nGroupNumber ) { + if ( group == EndPoint[i].nEquNumber ) { + continue; /* ignore: the endpoint belongs to the same t-group as before */ + } + /* save information for renumbering of the existing t-groups */ + for ( j = 0; j < nNumGroups; j ++ ) { + if ( group == nGroupNumber[j] ) { + if ( EndPoint[i].nEquNumber != nGroupNewNumber[j] ) { + ret = -1; /* program error */ /* */ + goto exit_function; + } + break; + } + } + if ( j == nNumGroups ) { + /* discovered a new t-group number; store it together with its nEquNumber */ + if ( j == MAX_STACK_ARRAY_LEN ) { + if ( nGroupNewNumber == nGroupNewNumberStackArray ) { + nGroupNewNumber = (AT_NUMB *) inchi_malloc(nNumEndPoints*sizeof(nGroupNewNumber[0])); + if ( !nGroupNewNumber ) { + ret = -1; + goto exit_function; + } + memcpy( nGroupNewNumber, nGroupNewNumberStackArray, nNumGroups*sizeof(nGroupNewNumber[0])); + } + if ( nGroupNumber == nGroupNumberStackArray ) { + nGroupNumber = (AT_NUMB *) inchi_malloc(nNumEndPoints*sizeof(nGroupNumber[0])); + if ( !nGroupNumber ) { + ret = -1; + goto exit_function; + } + memcpy( nGroupNumber, nGroupNumberStackArray, nNumGroups*sizeof(nGroupNumber[0])); + } + } + + nGroupNumber[j] = group; /* old t-group ID */ + nGroupNewNumber[j] = EndPoint[i].nEquNumber; /* new t-group ID */ + nNumGroups ++; + } + } else { + /* add a new endpoint to the newly created or previously existing t-groups */ + group = EndPoint[i].nEquNumber; + if ( group >= nNextGroupNumber ) { + /* get index of a new t-group from equ number */ + j = num_t + group - nNextGroupNumber; /* newly assigned IDs are contiguous */ + } else { + /* old t-group */ + if ( j >= num_t || group != t_group[j].nGroupNumber ) { + /* search only if j is not a needed group index */ + for ( j = 0; j < num_t; j ++ ) { + if ( group == t_group[j].nGroupNumber ) + break; + } + if ( j == num_t ) { + ret = -1; /* program error: t-group not found */ /* */ + goto exit_function; + } + } + } + /* add aton to existing or new t-group */ + t_group[j].nNumEndpoints ++; + for ( k = 0; k < (int)(sizeof(t_group->num)/sizeof(t_group->num[0])); k ++ ) + t_group[j].num[k] += EndPoint[i].num[k]; + for ( k = 0; k < (int)(sizeof(t_group->num_DA)/sizeof(t_group->num_DA[0])); k ++ ) + t_group[j].num_DA[k] += EndPoint[i].num_DA[k]; + /* mark endpoint */ + at[EndPoint[i].nAtomNumber].endpoint = group; + difference ++; + } + } + + difference += nNumGroups; + num_t += nNumNewTGroups; + if ( !difference ) { + ret = 0; /* nothing to do. Not necessarily a program error: happens if all EndPoint[i].nGroupNumber==EndPoint[i].nEquNumber */ + goto exit_function; + } + + if ( nNumGroups ) { + /* prepare for renumbering: find max t-group number */ + for ( i = 0, nNextGroupNumber = 0; i < num_t; i ++ ) { + if ( nNextGroupNumber < t_group[i].nGroupNumber ) { + nNextGroupNumber = t_group[i].nGroupNumber; + } + } + } + /* renumber and merge t-groups */ + for ( i = 0; i < nNumGroups; i ++ ) { + int i1, i2; + AT_NUMB group1 = nGroupNumber[i]; + AT_NUMB group2 = nGroupNewNumber[i]; + /* add group1 to group2, then delete group1. */ + for ( j = 0, i1 = i2 = -1; j < num_t && (i1 < 0 || i2 < 0); j ++ ) { + if ( i1 < 0 && group1 == t_group[j].nGroupNumber ) + i1 = j; + if ( i2 < 0 && group2 == t_group[j].nGroupNumber ) + i2 = j; + } + if ( i1 < 0 || i2 < 0 ) { + ret = -1; /* program error */ /* */ + goto exit_function; + } + /* add t_group[i1] to t_group[i2] and remove t_group[i1] */ + for ( k = 0; k < (int)(sizeof(t_group->num)/sizeof(t_group->num[0])); k ++ ) + t_group[i2].num[k] += t_group[i1].num[k]; + for ( k = 0; k < (int)(sizeof(t_group->num_DA)/sizeof(t_group->num_DA[0])); k ++ ) + t_group[i2].num_DA[k] += t_group[i1].num_DA[k]; + t_group[i2].nNumEndpoints += t_group[i1].nNumEndpoints; + num_t --; + if ( num_t > i1 ) { + memmove( t_group+i1, t_group+i1+1, ( num_t - i1)*sizeof(t_group[0]) ); + } + } + + if ( nNumGroups ) { + /* there are groups to merge */ + if ( nNextGroupNumber >= MAX_STACK_ARRAY_LEN ) { + nNewTgNumber = (AT_NUMB *) inchi_malloc((nNextGroupNumber+1)*sizeof(*nNewTgNumber)); + if ( !nNewTgNumber ) { + ret = -1; + goto exit_function; /* error: out of RAM */ + } + } + memset( nNewTgNumber, 0, (nNextGroupNumber+1)*sizeof(*nNewTgNumber) ); + for ( i = 0; i < num_t; i ++ ) { + nNewTgNumber[t_group[i].nGroupNumber] = i+1; /* new t-group numbers */ + } + for ( j = 0; j < nNumGroups; j ++ ) { + if ( !nNewTgNumber[nGroupNumber[j]] && nNewTgNumber[nGroupNewNumber[j]] ) { + nNewTgNumber[nGroupNumber[j]] = nNewTgNumber[nGroupNewNumber[j]]; + } else { + ret = -1; /* program error: all new numbers must have been marked */ + goto exit_function; + } + } + /* renumber t-groups */ + for ( i = 0; i < num_t; i ++ ) { + t_group[i].nGroupNumber = nNewTgNumber[t_group[i].nGroupNumber]; + } +#if ( bRELEASE_VERSION != 1 ) + /* Check: debug only */ + for ( i = 1; i < num_t; i ++ ) { + if ( 1 != t_group[i].nGroupNumber - t_group[i-1].nGroupNumber ) { + ret = -1; /* debug */ + goto exit_function; + } + } +#endif + /* renumber endpoints */ + for ( i = 0; i < num_atoms; i ++ ) { + if ( group = at[i].endpoint ) { + if ( !(at[i].endpoint = nNewTgNumber[group]) || nNextGroupNumber <= nNewTgNumber[group] ) { + ret = -1; /* program error */ + goto exit_function; + } + } + } + } + if ( nNewTgNumber != nNewTgNumberStackArray ) { + inchi_free( nNewTgNumber ); + nNewTgNumber = nNewTgNumberStackArray; + } + if ( nGroupNumber != nGroupNumberStackArray ) { + inchi_free(nGroupNumber); + nGroupNumber = nGroupNumberStackArray; + } + if ( nGroupNewNumber != nGroupNewNumberStackArray ) { + inchi_free( nGroupNewNumber ); + nGroupNewNumber = nGroupNewNumberStackArray; + } + if ( !t_group_info->tGroupNumber ) { + t_group_info->tGroupNumber = (AT_NUMB *) inchi_malloc(2*max_num_t*sizeof(t_group_info->tGroupNumber[0])); + if ( !t_group_info->tGroupNumber ) { + ret = -1; + goto exit_function; + } + } + /* fill out t-group index 2004-02-27 */ + memset( t_group_info->tGroupNumber, 0, 2*max_num_t*sizeof(t_group_info->tGroupNumber[0]) ); + for ( i = 0; i < num_t; i ++ ) { + if ( t_group[i].nNumEndpoints && t_group[i].nGroupNumber ) + t_group_info->tGroupNumber[t_group[i].nGroupNumber] = i+1; + } + + if ( pBNS && (pBNS->tot_st_cap == pBNS->tot_st_flow || ALWAYS_ADD_TG_ON_THE_FLY) ) { + T_GROUP_INFO tgi; + int ret_bns; + memset( &tgi, 0, sizeof(tgi) ); + tgi.num_t_groups = num_t; + tgi.t_group = t_group; +#if ( KETO_ENOL_TAUT == 1 ) + tgi.bTautFlags |= (t_group_info->bTautFlags & TG_FLAG_KETO_ENOL_TAUT); /* needed in AddTGroups2BnStruct() */ +#endif + /* reinitialize BN Structure */ + ret_bns = ReInitBnStruct( pBNS, at, num_atoms, 0 ); + if ( IS_BNS_ERROR( ret_bns ) ) { + return ret_bns; + } + if ( *pBNS->pbTautFlags & TG_FLAG_MOVE_POS_CHARGES ) { + /* set new charge groups */ + ret_bns = AddCGroups2BnStruct( pCG, pBNS, at, num_atoms, cgi ); + if ( IS_BNS_ERROR( ret_bns ) ) { + return ret_bns; + } + } + /* set new tautomeric groups */ + ret_bns = AddTGroups2BnStruct( pCG, pBNS, at, num_atoms, &tgi ); + if ( IS_BNS_ERROR( ret_bns ) ) { + return ret_bns; + } + } + + *pnum_t = num_t; + return difference; + +exit_function: + if ( nNewTgNumber != nNewTgNumberStackArray ) { + inchi_free( nNewTgNumber ); + } + if ( nGroupNumber != nGroupNumberStackArray ) { + inchi_free(nGroupNumber); + } + if ( nGroupNewNumber != nGroupNewNumberStackArray ) { + inchi_free( nGroupNewNumber ); + } + return ret; +} + + +/* Change non-alternating and non-tautomeric bonds + (that is, single and double bonds) to tautomeric */ +int SetTautomericBonds( inp_ATOM *at, + int nNumBondPos, + T_BONDPOS *BondPos ) +{ + int k, n; + for ( k = n = 0; k < nNumBondPos; k ++ ) { + int neighbor_index = BondPos[k].neighbor_index; + int center = BondPos[k].nAtomNumber; + int bond_mark = at[center].bond_type[neighbor_index]; + int bond_type = bond_mark & ~BOND_MARK_ALL; + int neighbor; +#if ( REPLACE_ALT_WITH_TAUT == 1 ) + if ( bond_type != BOND_TAUTOM ) +#else + if ( bond_type != BOND_ALTERN && bond_type != BOND_TAUTOM ) +#endif + { + int ii; + /* change bond type to BOND_TAUTOM presering higher bits marks */ + bond_type = (bond_mark & BOND_MARK_ALL) | BOND_TAUTOM; + /* change center-neighbor bond */ + at[center].bond_type[neighbor_index] = bond_type; + neighbor = at[center].neighbor[neighbor_index]; + for ( ii = 0; ii < at[neighbor].valence; ii ++ ) { + if ( at[neighbor].neighbor[ii] == center ) { + /* neighbor-center bond found */ + at[neighbor].bond_type[ii] = bond_type; + break; + } + } + n ++; + } + } + return n; +} + + +int GetNeutralRepsIfNeeded( AT_NUMB *pri, + AT_NUMB *prj, + inp_ATOM *at, + int num_atoms, + T_ENDPOINT *EndPoint, + int nNumEndPoints, + C_GROUP_INFO *cgi ) +{ + AT_NUMB ri = *pri; + AT_NUMB rj = *prj; + int i, k; + AT_NUMB c_point, endpoint, r; + + if ( (c_point = at[ri].c_point) && + ( at[rj].c_point==c_point ) && + (at[ri].charge == 1 || at[rj].charge == 1) && + cgi && + cgi->num_c_groups > 0 ) + { + /* at[ri] and at[rj] belong to the same charge group, at least one is charged */ + /* MS VC++ 2005 reports unreachable code here ??? */ + for ( k = 0; k < cgi->num_c_groups; k++ ) + { + if ( cgi->c_group[k].nGroupNumber == c_point ) + { + /* cgi->c_group[k] is found to be this charge group */ + if ( cgi->c_group[k].num_CPoints - cgi->c_group[k].num[0] < 2 ) + { + /* Only one neutral in the c-group: we will not be able to neutralize both + when looking for the alt path to discover the tautomerism. + Therefore we need to find a neutral t-group representative */ + /* at[rj] */ + if ( endpoint = at[rj].endpoint ) { + for ( i = 0; i < nNumEndPoints; i ++ ) { + if ( (r=EndPoint[i].nAtomNumber) == *prj ) + continue; /* ignore at[*prj] */ + if ( at[r].endpoint != endpoint ) + continue; /* at[r] does not belong to the same t-group as at[*prj]; ignore the atom */ + if ( !at[r].c_point ) { + rj = r; /* found a neutral t-group representative */ + break; + } + if ( at[r].c_point != c_point && c_point == at[rj].c_point ) { + /* replace only once because of (c_point == at[rj].c_point) condition */ + rj = r; + } + } + if ( rj == *prj /*&& at[ri].endpoint*/ ) { + /* !!! "&& at[ri].endpoint": only between 2 t-groups 2004-02-27; + the change disabled due to undiscovered yet possibility of ambiguity*/ + /* no replacement has been found in EndPoint[]; try all atoms in the t-group */ + for ( i = 0; i < num_atoms; i ++ ) { + if ( at[i].endpoint != endpoint ) + continue; + if ( i == (int)*prj ) + continue; + if ( !at[i].c_point ) { + rj = (AT_NUMB)i; /* found neutral t-group representative */ + break; + } + if ( at[i].c_point != c_point && c_point == at[rj].c_point ) { + /* replace only once */ + rj = (AT_NUMB)i; + } + } + } + } + /* at[ri] */ + if ( endpoint = at[ri].endpoint ) { + for ( i = 0; i < nNumEndPoints; i ++ ) { + if ( (r=EndPoint[i].nAtomNumber) == *pri ) + continue; + if ( at[r].endpoint != endpoint ) + continue; + if ( !at[r].c_point ) { + ri = r; /* found neutral t-group representative */ + break; + } + if ( at[r].c_point != c_point && c_point == at[ri].c_point && + at[r].c_point != at[rj].c_point ) { + /* replace only once */ + ri = r; + } + } + if ( ri == *pri && at[rj].endpoint ) { + /* !!! "&& at[rj].endpoint": only between 2 t-groups 2004-02-27; + the change disabled due to undiscovered yet possibility of ambiguity */ + for ( i = 0; i < num_atoms; i ++ ) { + if ( at[i].endpoint != endpoint ) + continue; + if ( i == (int)*pri ) + continue; + if ( !at[i].c_point ) { + ri = (AT_NUMB)i; /* found neutral t-group representative */ + break; + } + if ( at[i].c_point != c_point && c_point == at[ri].c_point && + at[i].c_point != at[rj].c_point) { + /* replace only once */ + ri = (AT_NUMB)i; + } + } + } + } + } + } + break; + } + *prj = rj; + *pri = ri; + } + return 0; +} + + +/*****************************************************************************/ +int FindAccessibleEndPoints( CANON_GLOBALS *pCG, + T_ENDPOINT *EndPoint, + int *nNumEndPoints, + T_BONDPOS *BondPos, + int *nNumBondPos, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD, + inp_ATOM *at, + int num_atoms, + C_GROUP_INFO *cgi, + int taut_mode ) +{ + AT_NUMB nTGroupRepresenative[MAXVAL], nTGroupEqu[MAXVAL], nTGEndPointNo[MAXVAL], ri, rj; + AT_NUMB nCurTGroupNumber, nMaxTGroupNumber, nNumTgroupNumbers, nMaxEquNumber; + int i, j, k, nNumDiffTGroupNumbers = 0, nNumFoundEqu, nErr; + + if ( *nNumEndPoints != *nNumBondPos ) + return 0; + /* collect all group numbers. Fill EndPoint[i].nEquNumber */ + for ( i = 0; i < *nNumEndPoints; i ++ ) { + nCurTGroupNumber = EndPoint[i].nEquNumber = EndPoint[i].nGroupNumber; /* initial equivalence */ + if ( nCurTGroupNumber ) { + /* found endpoint that already belongs to a t-group */ + for ( j = 0; j < nNumDiffTGroupNumbers; j ++ ) { + if ( nTGroupEqu[j] == nCurTGroupNumber ) + break; + } + if ( j == nNumDiffTGroupNumbers ) { + nTGroupRepresenative[nNumDiffTGroupNumbers] = EndPoint[i].nAtomNumber; + nTGroupEqu[nNumDiffTGroupNumbers] = EndPoint[i].nGroupNumber; + nTGEndPointNo[nNumDiffTGroupNumbers] = i; + nNumDiffTGroupNumbers ++; + } + } + } + + + /* check whether each pair belongs to the same t-group and establish the equivalence(s) */ + for ( i = 0, nNumFoundEqu=0; i < nNumDiffTGroupNumbers; i ++ ) { + for ( j = i+1; j < nNumDiffTGroupNumbers; j ++ ) { + ri = nTGroupRepresenative[i]; + rj = nTGroupRepresenative[j]; + /* both at[ri] and at[rj] are known to belong to tautomeric groups */ + GetNeutralRepsIfNeeded( &ri, &rj, at, num_atoms, EndPoint, *nNumEndPoints, cgi ); + nErr = bExistsAnyAltPath( pCG, pBNS, pBD, at, num_atoms, ri, rj, taut_mode ); + if ( IS_BNS_ERROR(nErr) ) + return nErr; + if ( 0 == nErr ) + continue; /* alt path between at[ri] and at[rj] not found */ + nCurTGroupNumber = inchi_min( nTGroupEqu[i], nTGroupEqu[j] ); + nMaxTGroupNumber = inchi_max( nTGroupEqu[i], nTGroupEqu[j] ); + for ( k = 0; k < nNumDiffTGroupNumbers; k ++ ) { + if ( nTGroupEqu[k]==nMaxTGroupNumber ) { + nTGroupEqu[k] = nCurTGroupNumber; + nNumFoundEqu ++; + } + } + for ( k = 0; k < *nNumEndPoints; k ++ ) { + if ( EndPoint[k].nEquNumber == nMaxTGroupNumber ) { + EndPoint[k].nEquNumber = nCurTGroupNumber; + } + } + } + } + if ( nNumFoundEqu ) { + /* leave in only non-equivalent representatives */ + for ( i = 1, k = 0; i < nNumDiffTGroupNumbers; i ++ ) { + for ( j = 0; j < i; j ++ ) { + if ( nTGroupEqu[j] == nTGroupEqu[i] ) { + nTGroupEqu[i] = 0; /* i > j; mark equivalent for removal*/ + break; + } + } + } + for ( i = j = 0; i < nNumDiffTGroupNumbers; i ++ ) { + if ( nTGroupEqu[i] ) { + if ( i != j ) { /* remove the marked */ + nTGroupEqu[j] = nTGroupEqu[i]; + nTGroupRepresenative[j] = nTGroupRepresenative[i]; + nTGEndPointNo[j] = nTGEndPointNo[i]; + } + j ++; + } + } + nNumDiffTGroupNumbers = j; /* number of known t-group representatives */ + } + /* collect endpoints that have not been assigned to t-groups */ + for ( i = 0, j = nNumDiffTGroupNumbers; i < *nNumEndPoints; i ++ ) { + if ( EndPoint[i].nEquNumber ) + continue; + nTGroupEqu[j] = 0; + nTGroupRepresenative[j] = EndPoint[i].nAtomNumber; + nTGEndPointNo[j] = i; + j ++; + } + nNumTgroupNumbers = j; + nMaxEquNumber = num_atoms + 1; /* impossible atom or t-group number */ + + /* check whether each pair belongs to the same group and establish the equivalence(s) */ + for ( i = 0, nNumFoundEqu=0; i < nNumTgroupNumbers; i ++ ) { + for ( j = i+1; j < nNumTgroupNumbers; j ++ ) { + if ( nTGroupEqu[i] != nTGroupEqu[j] && (i>=nNumDiffTGroupNumbers || j>=nNumDiffTGroupNumbers) || + /* equivalence of a t-group and a non-t-group atom */ + !nTGroupEqu[i] && !nTGroupEqu[j] + /* equivalence of two non-t-group atoms */ + ) { + ri = nTGroupRepresenative[i]; + rj = nTGroupRepresenative[j]; + /*------------------------------!!!--------------------------------------------- + Explanation why GetNeutralRepsIfNeeded() may need to be changed 2004-02-27 + The change has been disabled due to undiscovered yet possibility of ambiguity + to search for neutral only among EndPoint[] in case taut-not_taut pairs + + Counterexample: O=C-NH(+)=C-NH2 + 1 2 3 + + Has already been found: 2-3 (+)-charge exchange + 1-2 tautomerism (charge removed to 3) + Now testing: 2-3 tautomerism. If not commented out, + GetNeutralRepsIfNeeded() would replace 2-3 test with 1-3 test because: + o Charge group has only one neutral and both 2 and 3 belong to it, + therefore we cannot neutralize both; search for neutral representative; + o Since 1 and 2 belong to the same t-group and 1 is neutral, + test 1-3 instead of 2-3. + This breaks our condition: + Test tautomeric H movement only between neutral atoms. + -----------------------------------------------------------------------------*/ + GetNeutralRepsIfNeeded( &ri, &rj, at, num_atoms, EndPoint, *nNumEndPoints, cgi ); + + nErr = bExistsAnyAltPath( pCG, pBNS, pBD, at, num_atoms, ri, rj, taut_mode ); + if ( IS_BNS_ERROR(nErr) ) + return nErr; + if ( nErr <= 0 ) + continue; + if ( nTGroupEqu[i] && nTGroupEqu[j] ) { + /* found equivalence of two t-groups; at least one of them must be a new one */ + nCurTGroupNumber = inchi_min( nTGroupEqu[i], nTGroupEqu[j] ); + nMaxTGroupNumber = inchi_max( nTGroupEqu[i], nTGroupEqu[j] ); + for ( k = 0; k < nNumTgroupNumbers; k ++ ) { + if ( nTGroupEqu[k]==nMaxTGroupNumber ) { + nTGroupEqu[k] = nCurTGroupNumber; + nNumFoundEqu ++; + } + } + for ( k = 0; k < *nNumEndPoints; k ++ ) { + if ( EndPoint[k].nEquNumber == nMaxTGroupNumber ) { + EndPoint[k].nEquNumber = nCurTGroupNumber; + } + } + } else + if ( nTGroupEqu[i] ) { /* extend existing t-group */ + nTGroupEqu[j] = nTGroupEqu[i]; + EndPoint[nTGEndPointNo[j]].nEquNumber = nTGroupEqu[i]; + } else + if ( nTGroupEqu[j] ) { /* extend existing t-group */ + nTGroupEqu[i] = nTGroupEqu[j]; + EndPoint[nTGEndPointNo[i]].nEquNumber = nTGroupEqu[j]; + } else { /* establis a new t-group */ + nTGroupEqu[i] = + nTGroupEqu[j] = nMaxEquNumber; /* assign a fict. ID to establish equivalence */ + EndPoint[nTGEndPointNo[i]].nEquNumber = + EndPoint[nTGEndPointNo[j]].nEquNumber = nMaxEquNumber; + nMaxEquNumber ++; + } + } + } + } + /* eliminate endpoints and bonds that do not belong to t-group(s) + (they have not been found connected by an alt path to any other endpoint) + */ + for ( i = 0, j = 0; i < *nNumEndPoints; i ++ ) { + if ( EndPoint[i].nEquNumber ) { +#if ( IGNORE_SINGLE_ENDPOINTS == 1 ) /* 1-28-2003 */ + for ( k = 0, nNumFoundEqu = 0; k < *nNumEndPoints; k ++ ) { + nNumFoundEqu += (EndPoint[i].nEquNumber == EndPoint[k].nEquNumber); + } + if ( nNumFoundEqu <= 1 ) { /* one time it is equal to itself when i == k above */ + /* if EndPoint[i] is not "equivalent" to any other EndPoint then ignore it */ + continue; + } +#endif + if ( i != j ) { /* save endpoints that are found to be connected to other endpoints by alt paths */ + EndPoint[j] = EndPoint[i]; + BondPos[j] = BondPos[i]; + } + j ++; + } + } + +#if ( IGNORE_SINGLE_ENDPOINTS != 1 ) /* 1-28-2003 */ + /* Do not allow a centerpoint to have only one tautomeric bond */ + /* Hack: we may have only one centerpoint */ + /* BondPos[*].nAtomNumber are centerpoints */ + if ( j == 1 ) { + /* check if there exist other centerpoint neighbors + * connected to it by another tautomeric-bond + */ + for ( i = 0, k = 0; i < at[BondPos[0].nAtomNumber].valence; i ++ ) { + k += ( i != BondPos[0].neighbor_index && + BOND_TAUTOM == (at[BondPos[0].nAtomNumber].bond_type[i] & ~BOND_MARK_ALL)); + } + if ( !k ) { + j = 0; + } + } +#endif + + *nNumEndPoints = *nNumBondPos = j; + return j; +} + + +/*****************************************************************************/ +/*#if ( MOVE_CHARGES == 1 ) */ /* { */ +/*****************************************************************************/ + +/**********************************************/ +/* */ +/* definitions for positive ion recognition */ +/* */ +/**********************************************/ + +/*****************************************************************************/ +typedef struct tagChargeType { /* meaning see in bCanBeACPoint() */ + char elname[3]; + S_CHAR charge; + S_CHAR neutral_valence; + S_CHAR neutral_bonds_valence; /* valence of a neutral atom */ + S_CHAR cChangeValence; /* charge increases valence by this value */ + S_CHAR cChargeType; /* different types are treated separately */ + S_CHAR num_bonds; /* added 02-06-2005 */ +} CHARGE_TYPE; + +const CHARGE_TYPE CType[] = { + { "N\0", 1, 3, 3, 1, 0, 0 }, + { "P\0", 1, 3, 3, 1, 1, 0 }, +#if ( ADD_MOVEABLE_O_PLUS == 1 ) + { "O\0", 1, 2, 2, 1, 2, 2 }, /* added 02-06-2005 */ + { "S\0", 1, 2, 2, 1, 3, 2 }, /* added 03-18-2005 */ + { "Se", 1, 2, 2, 1, 4, 2 }, /* added 03-18-2005 */ + { "Te", 1, 2, 2, 1, 5, 2 }, /* added 03-18-2005 */ +#endif +}; + +/* bits */ + +#define C_SUBTYPE_CHARGED 0 +#define C_SUBTYPE_p_DONOR 1 /* new */ +#define C_SUBTYPE_p_ACCEPT 2 /* new */ +#define C_SUBTYPE_H_ACCEPT 4 +#define C_SUBTYPE_H_DONOR 8 +#define C_SUBTYPE_NEUTRAL 16 + +/* make sure any C_SUBTYPE_CHARGED_... < any C_SUBTYPE_NEUTRAL_... */ +/* charged */ +#define C_SUBTYPE_CHARGED_NON_TAUT (C_SUBTYPE_CHARGED) +#define C_SUBTYPE_CHARGED_p_DONOR (C_SUBTYPE_CHARGED|C_SUBTYPE_p_DONOR) +#define C_SUBTYPE_CHARGED_H_ACCEPT (C_SUBTYPE_CHARGED|C_SUBTYPE_H_ACCEPT) +#define C_SUBTYPE_CHARGED_H_ACCEPT_p_DONOR (C_SUBTYPE_CHARGED|C_SUBTYPE_H_ACCEPT|C_SUBTYPE_p_DONOR) +#define C_SUBTYPE_CHARGED_H_DONOR (C_SUBTYPE_CHARGED|C_SUBTYPE_H_DONOR |C_SUBTYPE_p_DONOR) +/* neutral */ +#define C_SUBTYPE_NEUTRAL_NON_TAUT (C_SUBTYPE_NEUTRAL) +#define C_SUBTYPE_NEUTRAL_H_ACCEPT (C_SUBTYPE_NEUTRAL|C_SUBTYPE_H_ACCEPT) +#define C_SUBTYPE_NEUTRAL_H_ACCEPT_p_ACCEPT (C_SUBTYPE_NEUTRAL|C_SUBTYPE_H_ACCEPT|C_SUBTYPE_p_ACCEPT) +#define C_SUBTYPE_NEUTRAL_H_DONOR (C_SUBTYPE_NEUTRAL|C_SUBTYPE_H_DONOR) + +#define NUM_C_TYPES (int)(sizeof( CType )/sizeof(CType[0])) +/*****************************************************************************/ + + +int bCanBeACPoint( inp_ATOM *at, S_CHAR cCharge, S_CHAR cChangeValence, S_CHAR neutral_bonds_valence, + S_CHAR neutral_valence, S_CHAR nEndpointValence, S_CHAR *cChargeSubtype ) +{ + int nChangeValence; + int nNumBonds; + int nBondsValence; + int bNegCharge = (at->charge == -1); /* add fict. bonds to (-) 2004-02-24*/ + if ( at->charge == cCharge && at->valence == at->chem_bonds_valence && at->num_H ) { + /* proton donors candidates >NH(+)-, >NH2(+), -NH3(+), >OH(+), -OH2(+) */ + /* charged, added p-transfer -- 01-28-2004 */ + nChangeValence = at->charge * cChangeValence; /* +1 or -1; currently only +1 */ + nBondsValence = at->chem_bonds_valence + at->num_H; + if ( nBondsValence == neutral_bonds_valence + nChangeValence && nEndpointValence ) { + *cChargeSubtype = C_SUBTYPE_CHARGED_p_DONOR; /* ignore Phosphorus p-donors for now */ + } + return 0; + } else + if ( at->charge == cCharge && at->valence < at->chem_bonds_valence ) { + /* the requirement at->valence < at->chem_bonds_valence rejects + candidates >NH(+)-, >NH2(+), -NH3(+), >N(+)<, >OH(+), -OH2(+), >O(+)- + Moveable charge requires double bonds; these ions have no double bonds + */ + + /* charged */ + nChangeValence = at->charge * cChangeValence; /* +1 or -1; currently only +1 */ + nBondsValence = at->chem_bonds_valence + at->num_H; + nNumBonds = at->valence + at->num_H; + if ( nBondsValence == neutral_bonds_valence + nChangeValence ) { /* known valence */ + if ( nNumBonds == neutral_valence ) { + /* non-tautomeric: >N(+)=, =O(+)- + possibly tautomeric donor: =NH(+)-, =NH2(+), =OH(+) */ + if ( at->valence == neutral_valence || !nEndpointValence ) { + /* non-tautomeric: >N(+)=, =O(+)-; any suitable P+: >P(+)=, =PH(+)-, =PH2(+) */ + *cChargeSubtype = C_SUBTYPE_CHARGED_NON_TAUT; + } else { + /* possibly tautomeric donor: =NH(+)-, =NH2(+), =OH(+) */ + *cChargeSubtype = C_SUBTYPE_CHARGED_H_DONOR; + } + return 1; + } + if ( nNumBonds == neutral_valence - 1 ) { + /* possibly tutomeric acceptor: =N(+)=, #N(+)-, #NH(+), #O(+) */ + if ( nEndpointValence ) { + *cChargeSubtype = at->num_H? C_SUBTYPE_CHARGED_H_ACCEPT_p_DONOR : C_SUBTYPE_CHARGED_H_ACCEPT; + } else { + /* =P(+)=, #P(+)-, #PH(+) */ + *cChargeSubtype = C_SUBTYPE_CHARGED_NON_TAUT; + } + return 1; /* charge type, charged */ + } + } + } else + if ( at->charge == 0 || bNegCharge ) { + /* neutral atom or anion, all bonds are single */ + nBondsValence = at->chem_bonds_valence + at->num_H + bNegCharge; /* add fict. bonds to (-) 2004-02-24*/ + nNumBonds = at->valence + at->num_H + bNegCharge; /* add fict. bonds to (-) 2004-02-24*/ + if ( nBondsValence == neutral_bonds_valence ) { + if ( nNumBonds == neutral_valence ) { + /* only single bonds: >N-, >NH, -NH2, -O-, -OH, >P- >PH -PH2 */ + /* >N(-), -NH(-), -O(-). >P(-) -PH(-) */ + if ( at->valence == neutral_valence || !nEndpointValence ) { + /* >N-, -O-, any P(3 single bonds): >P- >PH -PH2 */ + *cChargeSubtype = C_SUBTYPE_NEUTRAL_NON_TAUT; + } else + if ( at->valence < neutral_valence /*&& nEndpointValence */ ) { + /* num_H > 0: >NH -NH2 -OH */ + /* num_H = 0: none C_SUBTYPE_NEUTRAL_H_ACCEPT for now */ + *cChargeSubtype = at->num_H? C_SUBTYPE_NEUTRAL_H_DONOR: C_SUBTYPE_NEUTRAL_H_ACCEPT; + } else { + return 0; + } + return 1; /* charge type, neutral */ + } + if ( nNumBonds == neutral_valence - 1 ) { + /* possibly tautomeric acceptor =N-, =NH, =O or non-taut =P-, =PH */ + if ( nEndpointValence ) { + /* =N-, =NH, =O */ + *cChargeSubtype = C_SUBTYPE_NEUTRAL_H_ACCEPT_p_ACCEPT; + } else { + /* =P-, =PH */ + *cChargeSubtype = C_SUBTYPE_NEUTRAL_NON_TAUT; + } + return 1; /* charge type, (+) => neutral */ + } + } + } + return 0; +} + + +int GetChargeType( inp_ATOM *atom, int iat, S_CHAR *cChargeSubtype ) +{ + int i, n; + S_CHAR nEndpointValence; + inp_ATOM *at = atom + iat; + + *cChargeSubtype = 0; + /* ignore ion pairs and charges != 1 */ + if ( abs(at->charge) == 1 ) { + for ( i = 0; i < at->valence; i ++ ) { + n = at->neighbor[i]; + /* allow negatively charged tautomeric neighbors 2004-02-26 */ + if ( abs(atom[n].charge + at->charge) < abs(atom[n].charge - at->charge) && !atom[n].endpoint ) { + return -1; /* charges have different signs */ + } + } + } else + if ( at->charge ) { + return -1; /* abs(charge) != 1 */ + } + /* find candidates */ + for ( i = 0; i < NUM_C_TYPES; i ++ ) { + if ( !strcmp( at->elname, CType[i].elname ) && + (!CType[i].num_bonds || CType[i].num_bonds==at->valence && at->nNumAtInRingSystem >= 5) ) { + nEndpointValence = (S_CHAR)get_endpoint_valence(at->el_number ); + if ( bCanBeACPoint( at, CType[i].charge, CType[i].cChangeValence, CType[i].neutral_bonds_valence, + CType[i].neutral_valence, nEndpointValence, cChargeSubtype ) ) { + return CType[i].cChargeType; + } + } + } + return -1; +} + + +int CmpCCandidates( const void *a1, const void *a2 ) +{ + const C_CANDIDATE *c1 = (const C_CANDIDATE *)a1; + const C_CANDIDATE *c2 = (const C_CANDIDATE *)a2; + int ret; + if ( ret = (int)c1->type - (int)c2->type ) + return ret; + if ( ret = (int)c1->subtype - (int)c2->subtype ) + return ret; + ret = (int)c1->atnumber - (int)c2->atnumber; + return ret; +} + + +int RegisterCPoints( C_GROUP *c_group, int *pnum_c, int max_num_c, T_GROUP_INFO *t_group_info, + int point1, int point2, int ctype, inp_ATOM *at, int num_atoms ) +{ + int num_c = *pnum_c, i, i1, i2; + AT_NUMB nGroupNumber = 0, nNewGroupNumber; + + + if ( at[point1].c_point == at[point2].c_point ) { + if ( at[point1].c_point ) + return 0; + memset( c_group+num_c, 0, sizeof(c_group[0]) ); + if ( num_c < max_num_c ) { + c_group[num_c].num[0] = CHARGED_CPOINT(at,point1) + CHARGED_CPOINT(at, point2); + c_group[num_c].num_CPoints += 2; + c_group[num_c].cGroupType = ctype; + /* get next available c-group number */ + for ( i = 0; i < num_c; i ++ ) { + if ( nGroupNumber < c_group[i].nGroupNumber ) + nGroupNumber = c_group[i].nGroupNumber; + } + nGroupNumber ++; + c_group[num_c].nGroupNumber = + at[point1].c_point = + at[point2].c_point = nGroupNumber; + *pnum_c = num_c+1; + /* count protons */ + if ( at[point1].num_H ) { + c_group[num_c].num[1] ++; + } else + if ( at[point2].num_H ) { + c_group[num_c].num[1] ++; + } else + if ( (at[point1].endpoint || at[point2].endpoint) && t_group_info && t_group_info->t_group && t_group_info->num_t_groups ) { + /* !!! add later !!! */ + } + + + return 1; + } + return BNS_CPOINT_ERR; /* overflow */ + } + if ( at[point1].c_point > at[point2].c_point ) { + /* make sure at[point1].c_point < at[point2].c_point */ + i = point1; + point1 = point2; + point2 = i; + } + if ( !at[point1].c_point ) { + /* add a new c-endpoint to an existing c-group */ + nGroupNumber = at[point2].c_point; + for ( i = 0; i < num_c; i ++ ) { + if ( nGroupNumber == c_group[i].nGroupNumber ) { + at[point1].c_point = at[point2].c_point; + c_group[i].num_CPoints ++; + c_group[i].num[0] += CHARGED_CPOINT(at,point1); + return 1; + } + } + return BNS_CPOINT_ERR; /* program error: c-group not found */ + } else { + /* merge two c-groups */ + nNewGroupNumber = at[point1].c_point; + nGroupNumber = at[point2].c_point; + for ( i = 0, i1=i2=-1; i < num_c && (i1 < 0 || i2 < 0); i ++ ) { + if ( nNewGroupNumber == c_group[i].nGroupNumber ) { + i1 = i; + continue; + } + if ( nGroupNumber == c_group[i].nGroupNumber ) { + i2 = i; + continue; + } + } + if ( i1 < 0 || i2 < 0 ) { + return BNS_CPOINT_ERR; /* at least one not found */ + } + + c_group[i1].num[0] += c_group[i2].num[0]; + c_group[i1].num_CPoints += c_group[i2].num_CPoints; + num_c --; + if ( num_c > i2 ) { + memmove( c_group+i2, c_group+i2+1, ( num_c - i2)*sizeof(c_group[0]) ); + } + *pnum_c = num_c; + /* renumber c-groups */ + for ( i = 0; i < num_c; i ++ ) { + if ( c_group[i].nGroupNumber > nGroupNumber ) { + c_group[i].nGroupNumber --; + } + } + /* renumber c-points */ + for ( i = 0; i < num_atoms; i ++ ) { + if ( at[i].c_point > nGroupNumber ) { + at[i].c_point --; + } else + if ( at[i].c_point == nGroupNumber ) { + at[i].c_point = nNewGroupNumber; + } + } + return 1; + } +} + + +int MarkChargeGroups( struct tagCANON_GLOBALS *pCG, + inp_ATOM *at, int num_atoms, + C_GROUP_INFO *c_group_info, T_GROUP_INFO *t_group_info, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD) +{ + int nNumChanges = 0; + + if ( c_group_info && c_group_info->c_candidate && c_group_info->max_num_candidates > 0 ) { + int i, i1, i2, i3, j, num_tested; + C_CANDIDATE *c_candidate = c_group_info->c_candidate; + int nMaxNumCandidates = c_group_info->max_num_candidates; + int nNumCandidates = c_group_info->num_candidates; + S_CHAR c_type, c_subtype; + int iat1, iat2, ret, nDelta; + + if ( nNumCandidates == -1 ) + { + nNumCandidates = 0; /* 2004-02-26 they could appear after t-group discovery */ + /*return 0;*/ + } + if ( nNumCandidates == 0 ) + { + for ( i = 0, nNumCandidates = 0; i < num_atoms; i ++ ) + { + if ( 0 <= (c_type = GetChargeType( at, i, &c_subtype )) ) + { + if ( nNumCandidates >= nMaxNumCandidates ) + { + return BNS_VERT_EDGE_OVFL; + } + c_candidate[nNumCandidates].atnumber = i; + c_candidate[nNumCandidates].type = c_type; + c_candidate[nNumCandidates].subtype = c_subtype; + nNumCandidates ++; + } + } + if ( nNumCandidates <= 1 ) + { + c_group_info->num_candidates = -1; /* no candidate exists */ + return 0; + } + } + /* sorting keys: (1) atom type (N,P); (2) uncharged=16/charged=0; (3) other; + atom-charged-N .... i1 + ... + atom-charged-N + atom-neutral-N .... i2 + ... + atom-neutral-N + atom-charged-P .... i3 ... i1 + ... + atom-charged-P + atom-neutral-P ........... i2 + ... + atom-neutral-P + end. ........... i3 + */ + qsort(c_candidate, nNumCandidates, sizeof(c_candidate[0]), CmpCCandidates); + + i1 = 0; + num_tested = 0; + nDelta = 0; + + while ( i1 < nNumCandidates ) + { + + /* the the first charged candidate of a new atom type */ + for (; i1 < nNumCandidates && (c_candidate[i1].subtype & C_SUBTYPE_NEUTRAL); i1 ++) + ; + if ( i1 == nNumCandidates ) + break; /* not found */ + + /* bypass other charged candidates of the same atom type */ + for ( i2 = i1+1; i2 < nNumCandidates && + c_candidate[i2].type == c_candidate[i1].type && + !(c_candidate[i2].subtype & C_SUBTYPE_NEUTRAL); i2++ ) + ; + if ( i2 == nNumCandidates ) + break; /* no neutral candidates */ + + /* find next to the last neutral candidate of the same atom type */ + for ( i3 = i2; i3 < nNumCandidates && + c_candidate[i3].type == c_candidate[i1].type; i3 ++ ) + ; + + if ( i3 == i2 ) + { + /* no neutral candidates found */ + if ( i2 < nNumCandidates ) + { + i1 = i3; + continue; /* move to the next atom type */ + } + break; /* nothing more to do */ + } + + /* found charged candidates: i1...i2-1; neutral candidates: i2...i3-1 */ + for ( i = i1; i < i2; i ++ ) + { + iat1 = c_candidate[i].atnumber; + for ( j = i2; j < i3; j ++ ) + { + /* check alt path at[iat1]=-=-...-at[iat2]; at[iat1] is charged, at[iat2] is neutral */ + num_tested ++; + iat2 = c_candidate[j].atnumber; + if ( at[iat1].c_point && at[iat1].c_point == at[iat2].c_point ) + continue; + + ret = bExistsAltPath( pCG, pBNS, pBD, NULL, at, num_atoms, iat1, iat2, ALT_PATH_MODE_CHARGE ); + + if ( IS_BNS_ERROR( ret ) ) + { + return ret; + } + if ( ret & 1 ) + { + nDelta = (ret & ~3) >> 2; + nNumChanges += (ret & 2); + ret = RegisterCPoints( c_group_info->c_group, &c_group_info->num_c_groups, + c_group_info->max_num_c_groups, t_group_info, + iat1, iat2, c_candidate[i1].type, at, num_atoms ); + if ( IS_BNS_ERROR( ret ) ) + { + return ret; + } + if ( nDelta ) + { + goto quick_exit; + } + } + } + } + i1 = i3; + } +quick_exit: + if ( c_group_info->num_candidates == 0 ) + { + /* first time: initialize */ + c_group_info->num_candidates = num_tested? nNumCandidates : -1; /* no candidate exists */ + } + } + return nNumChanges; +} + + +int GetSaltChargeType(inp_ATOM *at, int at_no, T_GROUP_INFO *t_group_info, int *s_subtype ) +{ + static int el_number_C = 0; + static int el_number_O = 0; + static int el_number_S = 0; + static int el_number_Se = 0; + static int el_number_Te = 0; + +/* + type (returned value): + -1 => ignore + 0 => oxygen + subtype: + 1 = SALT_DONOR_H => has H + 2 = SALT_DONOR_Neg => has (-) charge + 4 = SALT_ACCEPTOR => may be an acceptor of H or (-), but not necessarily + + O-atom should be: + - a terminal atom + - connected to unsaturated, uncharged, non-radical atom C that has chemical valence 4: + H-donors: =CH-OH, =C(-X)-OH + possible H-acceptors: -CH=O, >C=O + H-acceptors are true if O is tautomeric +*/ + int iC, tg, i, type; + /* one-time initialization */ + if ( !el_number_O ) { + el_number_C = get_periodic_table_number( "C" ); + el_number_O = get_periodic_table_number( "O" ); + el_number_S = get_periodic_table_number( "S" ); + el_number_Se = get_periodic_table_number( "Se" ); + el_number_Te = get_periodic_table_number( "Te" ); + } + *s_subtype = 0; /* initialize the output */ + /* check whether it is a candidate */ + if ( at[at_no].valence != 1 || + at[at_no].radical && at[at_no].radical != RADICAL_SINGLET || + at[at_no].charge < -1 || + at[at_no].charge > 0 && !at[at_no].c_point ) { + return -1; + } + + if ( at[at_no].el_number == el_number_O || + at[at_no].el_number == el_number_S || + at[at_no].el_number == el_number_Se || + at[at_no].el_number == el_number_Te ) { + type = 0; /* terminal oxygen atom, needs more to be checked... */ + } else { + type = -1; /* ignore this atom */ + } + + if ( type < 0 || + at[at_no].chem_bonds_valence + at[at_no].num_H != + get_el_valence(at[at_no].el_number, at[at_no].charge, 0) ) { + return -1; /* non-standard valence or not an oxygen */ + } + + iC = at[at_no].neighbor[0]; + +#if ( SALT_WITH_PROTONS == 1 ) + if ( at[iC].el_number != el_number_C || + at[iC].chem_bonds_valence + at[iC].num_H != 4 || /* allow =C(H)-OH or -C(H)=O */ + at[iC].charge || + at[iC].radical && at[iC].radical != RADICAL_SINGLET || + at[iC].valence == at[iC].chem_bonds_valence ) { + return -1; /* oxigen is connected to a wrong atom */ + } +#else + if ( at[iC].el_number != el_number_C || + at[iC].num_H || + at[iC].chem_bonds_valence != 4 || /* allow only no H on C */ + at[iC].charge || + at[iC].radical && at[iC].radical != RADICAL_SINGLET || + at[iC].valence == at[iC].chem_bonds_valence ) { + return -1; /* oxigen is connected to a wrong atom */ + } +#endif + if ( (tg = at[at_no].endpoint) && t_group_info && t_group_info->t_group ) { + /* O-atom is in a tautomeric group */ + for ( i = 0; i < t_group_info->num_t_groups; i ++ ) { + if ( tg == t_group_info->t_group[i].nGroupNumber ) { + /* + t_group_info->t_group[i].num[0] = number of attached H-atoms and negative charges + t_group_info->t_group[i].num[1] = number of attached negative charges + */ + if ( t_group_info->t_group[i].num[0] > t_group_info->t_group[i].num[1] ) { + *s_subtype |= SALT_DONOR_H; /* has H */ + } + if ( t_group_info->t_group[i].num[1] ) { + *s_subtype |= SALT_DONOR_Neg; /* has (-) */ + } + *s_subtype |= SALT_ACCEPTOR; /* there is always an acceptor in a t-group */ + return type; + } + } + return -1; /* error: t-group not found */ + } + /* O is not not in a tautomeric group */ + /* assume valence(O-) < valence(O) < valence(O+) */ + if ( at[at_no].charge == -1 ) { + *s_subtype |= SALT_DONOR_Neg; /* has (-) */ + } + if ( at[at_no].charge <= 0 && at[at_no].num_H ) { + *s_subtype |= SALT_DONOR_H; /* has H */ + } + if ( at[at_no].charge == 0 && at[at_no].chem_bonds_valence == 2 ) { + *s_subtype |= SALT_ACCEPTOR; + } + /* since O cannot be a charge point, the following cannot happen: */ + if ( at[at_no].charge == 1 && at[at_no].c_point && at[at_no].chem_bonds_valence == 2 && at[at_no].num_H ) { + *s_subtype |= SALT_DONOR_H; /* has H */ + } + return type; +} + + +int bDoNotMergeNonTautAtom(inp_ATOM *at, int at_no) +{ + static int el_number_N = 0; + + if ( !el_number_N ) { + el_number_N = get_periodic_table_number( "N" ); + } + if ( at[at_no].el_number == el_number_N ) + { + return 1; + } + return 0; +} + + +int GetOtherSaltChargeType( inp_ATOM *at, int at_no, T_GROUP_INFO *t_group_info, int *s_subtype, int bAccept_O ) +{ + /* static int el_number_C = 0; */ + /* static int el_number_N = 0; */ + static int el_number_O = 0; + static int el_number_S = 0; + static int el_number_Se = 0; + static int el_number_Te = 0; + +/* + type (returned value): + -1 => ignore + 1 => not an oxygen + subtype: + 1 = SALT_DONOR_H => has H + 2 = SALT_DONOR_Neg => has (-) charge + 4 = SALT_ACCEPTOR => may be an acceptor of H or (-), but not necessarily + + the atom should be: + - a tautomeric endpoint atom + - connected to possible centerpoint atom + + another description of the atom searched here: + + any possibly tautomeric atom adjacent to a possibly centerpoint + that has at least one double bond (possibly if positively charged); + if eif.cAcceptor then the bond between the atom and the centerpoint must be possibly double + if eif.cAcceptor then the bond must be possibly single + Donors that belong to a t-group are also acceptors + + +*/ + int tg, i, j, type, endpoint_valence, num_centerpoints, bond_type, centerpoint; + ENDPOINT_INFO eif; + /* one-time initialization */ + if ( !el_number_O && !bAccept_O ) { + /* el_number_C = get_periodic_table_number( "C" ); */ + /* el_number_N = get_periodic_table_number( "N" ); */ + el_number_O = get_periodic_table_number( "O" ); + el_number_S = get_periodic_table_number( "S" ); + el_number_Se = get_periodic_table_number( "Se" ); + el_number_Te = get_periodic_table_number( "Te" ); + } + *s_subtype = 0; /* initialize the output */ + if ( !bAccept_O /* only N */ && + (at[at_no].el_number == el_number_O || + at[at_no].el_number == el_number_S || + at[at_no].el_number == el_number_Se || + at[at_no].el_number == el_number_Te ) ) { + return -1; /* we are not looking for oxygen here */ + } + + type = 1; + if ( !(endpoint_valence = nGetEndpointInfo( at, at_no, &eif )) ) { + return -1; /* not a possible endpoint */ + } else { + /* at[at_no] is not not in a tautomeric group; use eif previously filled out by nGetEndpointInfo */ + /* check whether there is adjacent atom-candidate for a centerpoint */ + num_centerpoints = 0; + for ( j = 0; j < at[at_no].valence; j ++ ) { + bond_type = (int)at[at_no].bond_type[j] & BOND_TYPE_MASK; + centerpoint = (int)at[at_no].neighbor[j]; /* a centerpoint candidate */ + if ( ( eif.cAcceptor && (bond_type == BOND_DOUBLE || + bond_type == BOND_ALTERN || /* possibly double */ + bond_type == BOND_ALT12NS || + bond_type == BOND_TAUTOM ) || + eif.cDonor && (bond_type == BOND_SINGLE || + bond_type == BOND_ALTERN || /* possibly single */ + bond_type == BOND_ALT12NS || + bond_type == BOND_TAUTOM ) ) && + (at[centerpoint].chem_bonds_valence > at[centerpoint].valence || + /* check for possible endpoint added 2004-02-24 */ + at[centerpoint].chem_bonds_valence == at[centerpoint].valence && + (at[centerpoint].endpoint || at[centerpoint].c_point) /* tautomerism or charge may increment at[centerpoint].chem_bonds_valence*/ ) && + is_centerpoint_elem( at[centerpoint].el_number ) ) { + num_centerpoints ++; + break; /* at least one possibly centerpoint neighbor has been found */ + } + } + if ( !num_centerpoints ) { + return -1; + } + /* moved here from just after "type = 1;" line 2004-02-26 */ + if ( (tg = at[at_no].endpoint) && t_group_info && t_group_info->t_group ) { + /* atom is in a tautomeric group */ + for ( i = 0; i < t_group_info->num_t_groups; i ++ ) { + if ( tg == t_group_info->t_group[i].nGroupNumber ) { + /* + t_group_info->t_group[i].num[0] = number of attached H-atoms and negative charges + t_group_info->t_group[i].num[1] = number of attached negative charges + */ + if ( t_group_info->t_group[i].num[0] > t_group_info->t_group[i].num[1] ) { + *s_subtype |= SALT_DONOR_H; /* has H */ + } + if ( t_group_info->t_group[i].num[1] ) { + *s_subtype |= SALT_DONOR_Neg; /* has (-) */ + } + *s_subtype |= SALT_ACCEPTOR; /* there is always an acceptor in a t-group */ + return type; + } + } + return -1; /* error: t-group not found */ + } + + if ( eif.cAcceptor ) { + *s_subtype |= SALT_ACCEPTOR; + } + if ( eif.cDonor ) { + if ( at[at_no].charge == -1 ) { + *s_subtype |= SALT_DONOR_Neg; /* has (-) */ + } + if ( at[at_no].num_H ) { + *s_subtype |= SALT_DONOR_H; /* has H */ + } + } + } + return type; +} + + +int GetOtherSaltType( inp_ATOM *at, int at_no, int *s_subtype ) +{ + static int el_number_C = 0; + /* static int el_number_N = 0; */ + /* static int el_number_O = 0; */ + static int el_number_S = 0; + static int el_number_Se = 0; + static int el_number_Te = 0; + +/* + type (returned value): + -1 => ignore + 2 => found: SH + proton donor -CH2-SH, >CH-SH, >C< S(-) + proton acceptor -CH2-S(-), >CH-S(-), >C< + subtype: + 1 = SALT_DONOR_H => has H + 2 = SALT_DONOR_Neg => has (-) charge + 4 = SALT_ACCEPTOR => may be an acceptor of H or (-), but not necessarily + + non-O-atom should be: + - a tautomeric endpoint atom + - connected to possible middle point atom +*/ + int type, endpoint_valence, bond_type, centerpoint; + ENDPOINT_INFO eif; + + if ( at[at_no].valence != 1 || at[at_no].chem_bonds_valence != 1 || + 1 != (at[at_no].num_H==1) + (at[at_no].charge==-1) ) { + return -1; + } + /* one-time initialization */ + if ( !el_number_S ) { + el_number_C = get_periodic_table_number( "C" ); + /* el_number_N = get_periodic_table_number( "N" ); */ + /* el_number_O = get_periodic_table_number( "O" ); */ + el_number_S = get_periodic_table_number( "S" ); + el_number_Se = get_periodic_table_number( "Se" ); + el_number_Te = get_periodic_table_number( "Te" ); + } + *s_subtype = 0; /* initialize the output */ + if ( !(at[at_no].el_number == el_number_S || + at[at_no].el_number == el_number_Se || + at[at_no].el_number == el_number_Te ) ) { + return -1; /* we are not looking for oxygen here */ + } + + type = 2; /* non-tautomeric p-donor or acceptor: C-SH, C-S(-) */ + + if ( !(endpoint_valence = nGetEndpointInfo( at, at_no, &eif )) || + eif.cMoveableCharge && !at[at_no].c_point || !eif.cDonor || eif.cAcceptor ) { + return -1; /* not a possible -SH or -S(-) */ + } else { + /* at[at_no] is not not in a tautomeric group; use eif previously filled out by nGetEndpointInfo */ + /* check whether there is adjacent atom-candidate for a centerpoint */ + centerpoint = (int)at[at_no].neighbor[0]; + bond_type = (int)at[at_no].bond_type[0] & BOND_TYPE_MASK; + if ( at[centerpoint].el_number != el_number_C || + at[centerpoint].charge || + at[centerpoint].radical && at[centerpoint].radical != RADICAL_SINGLET || + at[centerpoint].valence != at[centerpoint].chem_bonds_valence ) { + return -1; /* not a carbon with all single bonds */ + } + if ( at[at_no].num_H == 1 ) { + *s_subtype |= SALT_p_DONOR; + } else + if ( at[at_no].charge == -1 ) { + *s_subtype |= SALT_p_ACCEPTOR; + } else { + return -1; + } + } + return type; +} + +/****************************************************************************/ +/* new version: merge all, check alt paths, then unmerge unreachable O-atoms if any */ +/* Check for oxygen negative charge-H tautomerism (Salts) + allowed long-range tautomerism; more than one H or (-) can be moved, for example: + HO-C=C-O(-) O=C-C=O + / \ / \ + R R R R + | | => | | + R' R' R' R' + \ / \ / + O=C-C=O HO-C=C-O(-) + + To check: + + | | + -add all possible HO-C=, O=C, (-)O-C= (including all containing O t-groups) into one t-group; + -temporarily disconnect one of previously not belonging to any t-group O-atoms from the one t-group; + -find whether there is an alt path allowing H or (-) to migrate + from the temp. disconnected O to any one left in the group. + If the alt path does not exist then the temp. disconnected atom does not + participate in the H/(-) migrartion and it will be unmarked/unmerged. + +*/ + + + +/*****************************************************************************/ +int comp_candidates( const void *a1, const void *a2 ) +{ + const S_CANDIDATE *s1 = (const S_CANDIDATE *)a1; + const S_CANDIDATE *s2 = (const S_CANDIDATE *)a2; + int ret; + if ( s1->type >= 0 /* enabled < */ && s2->type < 0 /* disabled */ ) + return -1; /* enabled goes first */ + if ( s1->type < 0 /* disabled > */ && s2->type >= 0 /* enabled */ ) + return 1; + if ( s1->endpoint && !s2->endpoint ) + return -1; /* tautomeric goes first; only tautomeric may be disabled */ + if ( !s1->endpoint && s2->endpoint ) + return 1; /* tautomeric goes first; only tautomeric may be disabled */ + if ( s1->endpoint && s2->endpoint && (ret = (int)s1->endpoint - (int)s2->endpoint) ) { + return ret; + } + return (int)s1->atnumber - (int)s2->atnumber; +} + + +int MarkSaltChargeGroups2( CANON_GLOBALS *pCG, inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, + T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, + struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD ) +{ +/* BNS_EDGE_FORBIDDEN_TEMP */ +#define ALT_PATH_FOUND (MAX_ATOMS+1) +#define NO_ENDPOINT (MAX_ATOMS+2) /* the two defines must be different */ +#define DISABLE_CANDIDATE 10 +#define cPAIR(a,b) cPair[a+b*nNumLeftCandidates] +#define ACCEPTOR_PAIR 1 +#define DONOR_PAIR 2 + + int nNumChanges = 0, nNumOtherChanges = 0, nNumAcidicChanges = 0, nTotNumChanges = 0; + S_CHAR *cPair = NULL; + T_ENDPOINT *EndPoint = NULL; + if ( s_group_info && s_group_info->s_candidate && s_group_info->max_num_candidates > 0 ) { + int i, j, i1, j1; + S_CANDIDATE *s_candidate = s_group_info->s_candidate; + int nMaxNumCandidates = s_group_info->max_num_candidates; + int nNumCandidates = s_group_info->num_candidates; + int nNumOtherCandidates = s_group_info->num_other_candidates; + int nNumPOnlyCandidates = s_group_info->num_p_only_candidates; + int nNumLeftCandidates = 0; + int nNumMarkedCandidates = 0; + int s_type, s_subtype; + int ret, nDelta; + int bHardAddedRemovedProtons = t_group_info && (t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT); + + int s_subtype_all = 0; + int nDonorPairs, nAcceptorPairs, nCurDonorPairs, nCurAcceptorPairs, bAlreadyTested; +/* + ENDPOINT_INFO eif; +*/ + +#if ( IGNORE_TGROUP_WITHOUT_H == 1 ) + int bTGroupHasNegativeChargesOnly = 1; +#endif + /*return 0;*/ /* debug only */ + + i1 = -1; + + if ( nNumCandidates <= -2 || !t_group_info || !t_group_info->t_group ) { + return 0; + } + /*************************************************************************/ + /* find all candidates including those with differen s_type (other type) */ + /*************************************************************************/ + for ( i = 0, nNumCandidates = nNumOtherCandidates = nNumPOnlyCandidates = 0; i < num_atoms; i ++ ) { + if ( 0 == (s_type = GetSaltChargeType( at, i, t_group_info, &s_subtype )) || + /* -C=O or =C-OH, O = S, Se, Te */ + 1 == (s_type = GetOtherSaltChargeType( at, i, t_group_info, &s_subtype, 1/* bAccept_O*/ )) || + /* =Z-MH or -Z=M, Z = centerpoint, M = endpoint, other than above */ + 2 == (s_type = GetOtherSaltType( at, i, &s_subtype ) ) || + ( bHardAddedRemovedProtons && 4 == (s_type = bIsHardRemHCandidate( at, i, &s_subtype ) ) ) + /* >C-SH, >C-S(-); S=S,Se,Te */ + ) { + + if ( nNumCandidates >= nMaxNumCandidates ) { + return BNS_VERT_EDGE_OVFL; + } + s_candidate[nNumCandidates].atnumber = i; + s_candidate[nNumCandidates].type = s_type; + s_candidate[nNumCandidates].subtype = s_subtype; + s_candidate[nNumCandidates].endpoint = at[i].endpoint; + nNumCandidates ++; + nNumOtherCandidates += (1 == s_type); + s_subtype_all |= s_subtype; + i1 = i; /* save a representative of a tautomeric group */ + } + } + + if ( nNumCandidates <= 1 || /* TG_FLAG_ALLOW_NO_NEGTV_O <=> CHARGED_SALTS_ONLY=0 */ + !(s_subtype_all & SALT_ACCEPTOR) || + (((t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O) || + (t_group_info->bTautFlagsDone & TG_FLAG_FOUND_SALT_CHARGES_DONE) || + (t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT)) ? + !(s_subtype_all & (SALT_DONOR)): + (!(s_subtype_all & SALT_DONOR_Neg) || nNumOtherCandidates == nNumCandidates )) + ) { + s_group_info->num_candidates = 0; /* no candidate exists */ + return 0; + } + if ( !(s_subtype_all & (SALT_DONOR_Neg) ) ) { + t_group_info->bTautFlagsDone |= TG_FLAG_ALLOW_NO_NEGTV_O_DONE; + } + + /************************************************************************************/ + /* Mark redundant candidates so that only one candidate from one t-group is left in */ + /************************************************************************************/ + for ( i = 0; i < nNumCandidates; i ++ ) { + if ( 2 == s_candidate[nNumCandidates].type ) { + s_candidate[i].type -= DISABLE_CANDIDATE; /* disable >C-SH candidates */ + nNumLeftCandidates ++; /* count rejected */ + continue; + } + if ( s_candidate[i].endpoint ) { + for ( j = i-1; 0 <= j; j -- ) { + if ( s_candidate[i].endpoint == s_candidate[j].endpoint ) { + s_candidate[i].type -= DISABLE_CANDIDATE; /* disable subsequent redundant */ + nNumLeftCandidates ++; /* count rejected */ + break; + } + } + } + } + nNumLeftCandidates = nNumCandidates - nNumLeftCandidates; /* subtract num. rejected from the total */ + s_group_info->num_candidates = 0; /* reinit next time */ + /*********************************************************************/ + /* reorder so that all disabled are at the end, tautomeric are first */ + /*********************************************************************/ + qsort ( s_candidate, nNumCandidates, sizeof(s_candidate[0]), comp_candidates ); + cPair = (S_CHAR *)inchi_calloc( nNumLeftCandidates*nNumLeftCandidates, sizeof(cPair[0]) ); + if ( !cPair ) { + /*printf("BNS_OUT_OF_RAM-6\n");*/ + nTotNumChanges = BNS_OUT_OF_RAM; + goto quick_exit; + } + nDonorPairs = nAcceptorPairs = 0; + /**********************************************************************/ + /* Find whether we have at least one donor pair and one acceptor pair */ + /**********************************************************************/ + for ( i = 0; i < nNumLeftCandidates; i ++ ) { + nCurDonorPairs = nCurAcceptorPairs = 0; + for ( j = 0; j <= i; j ++ ) { + if ( i == j && !s_candidate[i].endpoint ) { + continue; /* same non-taut atom. However, success for i==j means * + * that the whole tautomeric group may donate or accept 2H */ + } + /* check for acceptor pair */ + if ( (s_candidate[i].subtype & SALT_ACCEPTOR) && (s_candidate[j].subtype & SALT_ACCEPTOR) && + (ret = bExistsAltPath( pCG, pBNS, pBD, NULL, at, num_atoms, s_candidate[i].atnumber, + s_candidate[j].atnumber, ALT_PATH_MODE_ADD2H_TST ))) + { + if ( IS_BNS_ERROR( ret ) ) { + nTotNumChanges = ret; + goto quick_exit; + } + if ( ret & 1 ) { + nDelta = (ret & ~3) >> 2; + /*nNumChanges += (ret & 2);*/ + if ( nDelta ) { + /* alt path unleashed previously localized radicals and they annihilated */ + nNumChanges = 0; + nTotNumChanges = BNS_RADICAL_ERR; + goto quick_exit; + } + cPAIR(i,j) |= ACCEPTOR_PAIR; /* the result: mark the pair */ + /*cPAIR(j,i) |= ACCEPTOR_PAIR;*/ + } + } + /* check for donor pair */ + if ( (s_candidate[i].subtype & SALT_DONOR) && (s_candidate[j].subtype & SALT_DONOR) && + (ret = bExistsAltPath( pCG, pBNS, pBD, NULL, at, num_atoms, s_candidate[i].atnumber, + s_candidate[j].atnumber, ALT_PATH_MODE_REM2H_TST ))) + { + if ( IS_BNS_ERROR( ret ) ) { + nTotNumChanges = ret; + goto quick_exit; + } + if ( ret & 1 ) { + nDelta = (ret & ~3) >> 2; + /*nNumChanges += (ret & 2);*/ + if ( nDelta ) { + /* alt path unleashed previously localized radicals and they annihilated */ + nNumChanges = 0; + nTotNumChanges = BNS_RADICAL_ERR; + goto quick_exit; + } + cPAIR(i,j) |= DONOR_PAIR; /* the result: mark the pair */ + /*cPAIR(j,i) |= ACCEPTOR_PAIR;*/ + } + } + /* since the results will be used later to change bonds, check only now */ + /* when both results for (i,j) have been obtained. */ + if ( cPAIR(i,j) & ACCEPTOR_PAIR ) { + nCurAcceptorPairs ++; + if ( nDonorPairs ) { + /* find donor pair (i1,j1) such that i!=i1, i!=j1, j!=i1, j!=j1 */ + for ( i1 = 0; i1 < i; i1 ++ ) { + for ( j1 = 0; j1 <= i1; j1 ++ ) { + /* here always j1 < i && i1 < i therefore we do not compare i to i1 or j1 */ + if ( j1 != j && i1 != j && (cPAIR(i1,j1) & DONOR_PAIR) ) { + /* both the donor and the acceptor pairs have been found */ + goto bFound2Pairs; + } + } + } + } + } + if ( cPAIR(i,j) & DONOR_PAIR ) { + nCurDonorPairs ++; + if ( nAcceptorPairs ) { + /* find acceptor pair (i1,j1) such that i!=i1, i!=j1, j!=i1, j!=j1 */ + for ( i1 = 0; i1 < i; i1 ++ ) { + for ( j1 = 0; j1 <= i1; j1 ++ ) { + /* here always j1 < i && i1 < i therefore we do not compare i to i1 or j1 */ + if ( j1 != j && i1 != j && (cPAIR(i1,j1) & ACCEPTOR_PAIR) ) { + /* both the donor and the acceptor pairs have been found */ + goto bFound2Pairs; + } + } + } + } + } + } + nDonorPairs += nCurDonorPairs; + nAcceptorPairs += nCurAcceptorPairs; + } + /* nothing has been found */ + nNumChanges = 0; + inchi_free( cPair ); + cPair = NULL; + goto quick_exit; + + + /* both the donor and the acceptor pairs have been found */ +bFound2Pairs: + /* first, try already found pairs */ + i1 = i; + j1 = j; + + /* Find all possible donor and acceptor pairs */ + nNumMarkedCandidates = 0; + for ( i = 0; i < nNumLeftCandidates; i ++ ) { + nCurDonorPairs = nCurAcceptorPairs = 0; + for ( j = 0; j <= i; j ++ ) { + bAlreadyTested = (i < i1 || i == i1 && j <= j1); + if ( bAlreadyTested && (cPAIR(i,j) & ACCEPTOR_PAIR) || !bAlreadyTested ) { + /* checking for acceptor pair */ + if ( (s_candidate[i].subtype & SALT_ACCEPTOR) && (s_candidate[j].subtype & SALT_ACCEPTOR) && + (ret = bExistsAltPath( pCG, pBNS, pBD, NULL, at, num_atoms, s_candidate[i].atnumber, + s_candidate[j].atnumber, ALT_PATH_MODE_ADD2H_CHG ))) + { + if ( IS_BNS_ERROR( ret ) ) + { + nTotNumChanges = ret; + goto quick_exit; + } + if ( ret & 1 ) { + nDelta = (ret & ~3) >> 2; + nNumChanges += (ret & 2); + if ( nDelta ) { + /* alt path unleashed previously localized radicals and they annihilated */ + nNumChanges = 0; + nTotNumChanges = BNS_RADICAL_ERR; + goto quick_exit; + } + cPAIR(i,j) |= ACCEPTOR_PAIR; + /*cPAIR(j,i) |= ACCEPTOR_PAIR;*/ + nCurAcceptorPairs += !bAlreadyTested; + if ( !(s_candidate[i].subtype & SALT_SELECTED) ) { + s_candidate[i].subtype |= SALT_SELECTED; + nNumMarkedCandidates ++; + if ( !s_candidate[i].endpoint && s_candidate[i].type ) { + nNumOtherChanges ++; + } else { + nNumAcidicChanges ++; + } + } + if ( !(s_candidate[j].subtype & SALT_SELECTED) ) { + s_candidate[j].subtype |= SALT_SELECTED; + nNumMarkedCandidates ++; + if ( !s_candidate[j].endpoint && s_candidate[j].type ) { + nNumOtherChanges ++; + } else { + nNumAcidicChanges ++; + } + } + } + } + } + if ( bAlreadyTested && (cPAIR(i,j) & DONOR_PAIR) || !bAlreadyTested ) { + /* checking for donor pair */ + if ( (s_candidate[i].subtype & SALT_DONOR) && (s_candidate[j].subtype & SALT_DONOR) && + (ret = bExistsAltPath( pCG, pBNS, pBD, NULL, at, num_atoms, s_candidate[i].atnumber, + s_candidate[j].atnumber, ALT_PATH_MODE_REM2H_CHG ))) + { + if ( IS_BNS_ERROR( ret ) ) { + nTotNumChanges = ret; + goto quick_exit; + } + if ( ret & 1 ) { + nDelta = (ret & ~3) >> 2; + nNumChanges += (ret & 2); + if ( nDelta ) { + /* alt path unleashed previously localized radicals and they annihilated */ + nNumChanges = 0; + nTotNumChanges = BNS_RADICAL_ERR; + goto quick_exit; + } + cPAIR(i,j) |= DONOR_PAIR; + /*cPAIR(j,i) |= ACCEPTOR_PAIR;*/ + nCurDonorPairs += !bAlreadyTested; + if ( !(s_candidate[i].subtype & SALT_SELECTED) ) { + s_candidate[i].subtype |= SALT_SELECTED; + nNumMarkedCandidates ++; + if ( !s_candidate[i].endpoint && s_candidate[i].type ) { + nNumOtherChanges ++; + } else { + nNumAcidicChanges ++; + } + } + if ( !(s_candidate[j].subtype & SALT_SELECTED) ) { + s_candidate[j].subtype |= SALT_SELECTED; + nNumMarkedCandidates ++; + if ( !s_candidate[j].endpoint && s_candidate[j].type ) { + nNumOtherChanges ++; + } else { + nNumAcidicChanges ++; + } + } + } + } + } + } + nDonorPairs += nCurDonorPairs; + nAcceptorPairs += nCurAcceptorPairs; + } + inchi_free( cPair ); + cPair = NULL; + + if ( nNumMarkedCandidates ) { + EndPoint = (T_ENDPOINT *)inchi_calloc( nNumMarkedCandidates, sizeof(EndPoint[0])); + if ( !EndPoint ) { + /*printf("BNS_OUT_OF_RAM-7\n");*/ + nTotNumChanges = BNS_OUT_OF_RAM; + goto quick_exit; + } + for ( i = 0, j = 0; i < nNumLeftCandidates; i ++ ) { + if ( s_candidate[i].subtype & SALT_SELECTED ) { + s_candidate[i].subtype ^= SALT_SELECTED; /* remove the flag */ + if ( j < nNumMarkedCandidates ) { + i1 = s_candidate[i].atnumber; /* save a representative of the t-group to be created */ + AddEndPoint( EndPoint+j, at, i1 ); + } + j ++; + } + } + if ( j != nNumMarkedCandidates ) { + nTotNumChanges = BNS_PROGRAM_ERR; + goto quick_exit; + } + + /* merge all marked atoms and their t-groups into one t-group */ + ret = RegisterEndPoints( pCG, t_group_info, EndPoint, nNumMarkedCandidates, at, num_atoms, c_group_info, pBNS ); + + if ( ret == -1 ) { + ret = BNS_PROGRAM_ERR; + } + if ( ret < 0 ) { + nTotNumChanges = ret; + goto quick_exit; + } + nTotNumChanges += (ret > 0); + inchi_free( EndPoint ); + EndPoint = NULL; + + if ( nNumMarkedCandidates ) { + for ( i = nNumLeftCandidates; i < nNumCandidates; i ++ ) { + s_candidate[i].type += DISABLE_CANDIDATE; + j1 = s_candidate[i].atnumber; + if ( at[j1].endpoint == at[i1].endpoint ) { + if ( !s_candidate[i].endpoint && s_candidate[i].type ) { + nNumOtherChanges ++; + } else { + nNumAcidicChanges ++; + } + } + } + } else { + for ( i = nNumLeftCandidates; i < nNumCandidates; i ++ ) { + s_candidate[i].type += DISABLE_CANDIDATE; + } + } + + /* find whether the new t-group have any movable H */ + for ( i = 0, bTGroupHasNegativeChargesOnly = 0; i < t_group_info->num_t_groups; i ++ ) { + if ( t_group_info->t_group[i].nGroupNumber == at[i1].endpoint && + t_group_info->t_group[i].num[0] == t_group_info->t_group[i].num[1] ) { + bTGroupHasNegativeChargesOnly = 1; + break; + } + } + } + nTotNumChanges = ( nTotNumChanges > 0); + +#if ( IGNORE_TGROUP_WITHOUT_H == 1 ) + if ( nTotNumChanges && bTGroupHasNegativeChargesOnly ) { + nTotNumChanges = 2; /* means no moveable H has been affected */ + } +#endif + } + +quick_exit: + if ( nNumOtherChanges && nTotNumChanges == 1 ) { + nTotNumChanges = 5; /* not only acidic atoms merged */ + } + if ( cPair ) { + inchi_free( cPair ); + /*cPair = NULL;*/ + } + if ( EndPoint ) { + inchi_free ( EndPoint ); + /*EndPoint = NULL;*/ + } + return nTotNumChanges; /* 0=>no changes, 1=>new salt tautomerism found, 2=>only new charge tautomerism found */ +#undef ALT_PATH_FOUND +#undef NO_ENDPOINT +} +/****************************************************************************/ +/* regular one-path version: find alt paths then merge */ +/* Check for oxygen negative charge-H tautomerism (Salts) + allowed long-range tautomerism; only one H or (-) can be moved, for example: + HO-C=X-Y=Z-...-C=O => O=C-X=Y-Z=...=C-OH +*/ + +#if ( SALT_WITH_PROTONS == 1 ) + +#define MAX_LOCAL_TGNUM 0 /* was 32; disable since it has not been used */ + +#if ( MAX_LOCAL_TGNUM > 0 ) +typedef struct tagTGroupData { + S_SHORT nGroupNumber; /* t-group number from t_group_info->t_group->nGroupNumber */ + S_SHORT nGroupIndex; /* TGroupData[nGroupNumber]nGroupIndex = index of t_group in t_group_info */ + S_SHORT nDonorM; /* number of endpoint-donors that have negative charge (Minus) */ + S_SHORT nDonorH; /* number of endpoint-donors that have only H */ + S_SHORT nAccepM; /* number of endpoint-acceptors that have negative charge (Minus) */ + S_SHORT nAccepH; /* number of endpoint-acceptors that have H and no negative charge */ + S_SHORT nAccep0; /* number of endpoint-acceptors that have no H and no negative charge */ + S_SHORT nDonorA; /* number of acidic endpoint-donors */ + S_SHORT nAccepS; /* number of acidic endpoint-acceptors */ +} TGroupData; +#endif + + +int MarkSaltChargeGroups ( CANON_GLOBALS *pCG, inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, + T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, + struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD ) +{ + + int nNumChanges = 0, nTotNumChanges = 0; + if ( s_group_info && s_group_info->s_candidate && s_group_info->max_num_candidates > 0 ) { + int i, i1, i2, j, j1, j2, jj, ii1, ii2, jj1, jj2, /*k,*/ num_tested; + S_CANDIDATE *s_candidate = s_group_info->s_candidate; + int nMaxNumCandidates = s_group_info->max_num_candidates; + int nNumCandidates = s_group_info->num_candidates; + int nNumOtherCandidates = s_group_info->num_other_candidates; + int nNumPOnlyCandidates = s_group_info->num_p_only_candidates; + int s_type, s_subtype; + int ret, nDelta, /*nMobile,*/ err = 0; + int s_subtype_all = 0; + int nGroupNumber; + T_ENDPOINT EndPoint[2]; +#if ( MAX_LOCAL_TGNUM > 0 ) + TGroupData tgData[MAX_LOCAL_TGNUM]; + TGroupData *ptgData = tgData; +#endif + int cond1=0,cond2a=0,cond2b=0,cond2c=0,cond2=0; + + if ( nNumCandidates <= -1 || !t_group_info || !t_group_info->t_group ) { + return 0; + } + + /* count t-groups */ + for ( i = 0, nGroupNumber = 0; i < t_group_info->num_t_groups; i ++ ) { + if ( nGroupNumber < t_group_info->t_group[i].nGroupNumber ) { + nGroupNumber = t_group_info->t_group[i].nGroupNumber; /* max. t-group number */ + } + } +#if ( MAX_LOCAL_TGNUM > 0 ) + /* prepare memory */ + if ( nGroupNumber >= MAX_LOCAL_TGNUM ) { + if ( !( ptgData = (TGroupData*)inchi_calloc( nGroupNumber+1, sizeof(TGroupData) ) ) ) { + err = BNS_OUT_OF_RAM; + goto quick_exit; + } + } else { + memset( ptgData, 0, sizeof(tgData) ); + } + ptgData[0].nGroupIndex = -1; /* data for non-tautomeric atoms */ + for ( i = 0, nGroupNumber = 0; i < t_group_info->num_t_groups; i ++ ) { + if ( nGroupNumber = t_group_info->t_group[i].nGroupNumber ) { + ptgData[nGroupNumber].nGroupIndex = i; + ptgData[i].nGroupNumber = nGroupNumber; + } + } +#endif + nNumCandidates = 0; /* always recalculate 2004-03-22 */ + num_tested = 0; + + if ( nNumCandidates == 0 ) + { + for ( i = 0, nNumCandidates = nNumOtherCandidates = nNumPOnlyCandidates = 0; i < num_atoms; i ++ ) + { + if ( 0 == (s_type = GetSaltChargeType( at, i, t_group_info, &s_subtype )) || + /* -C=O or =C-OH, O = S, Se, Te */ +#if ( INCL_NON_SALT_CANDIDATATES == 1 ) + 1 == (s_type = GetOtherSaltChargeType( at, i, t_group_info, &s_subtype, 1 )) || + /* =Z-MH or -Z=M, Z = centerpoint, M = endpoint, other than above */ +#endif + 2 == (s_type = GetOtherSaltType( at, i, &s_subtype ) ) + /* >C-SH, >C-S(-); S=S,Se,Te */ + ) + { + + if ( nNumCandidates >= nMaxNumCandidates ) + { + err = BNS_VERT_EDGE_OVFL; + goto quick_exit; + } + s_candidate[nNumCandidates].atnumber = i; + s_candidate[nNumCandidates].type = s_type; + s_candidate[nNumCandidates].subtype = s_subtype; + s_candidate[nNumCandidates].endpoint = at[i].endpoint; + nNumCandidates ++; + nNumOtherCandidates += (1 == s_type); + nNumPOnlyCandidates += (2 == s_type); + s_subtype_all |= s_subtype; + /*i1 = i;*/ /* save a representative of a tautomeric group */ + } + } /* for */ + + /* changes: TG_FLAG_ALLOW_NO_NEGTV_O replaced CHARGED_SALTS_ONLY==0 */ + cond1 = s_subtype_all & SALT_ACCEPTOR; + cond2a = t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O; + cond2b = t_group_info->bTautFlagsDone & TG_FLAG_FOUND_SALT_CHARGES_DONE; + cond2c = t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT; + if ( cond2a || cond2b|| cond2c ) + cond2 = !(s_subtype_all & (SALT_DONOR_Neg | SALT_DONOR_H)); + else + cond2 = !(s_subtype_all & SALT_DONOR_Neg) || nNumOtherCandidates==nNumCandidates; + if ( nNumCandidates <= 1 || !cond1 || cond2 + /*( + ( cond2a || cond2b || cond2c ) + ? !(s_subtype_all & (SALT_DONOR_Neg | SALT_DONOR_H)) + : ( !(s_subtype_all & SALT_DONOR_Neg) || nNumOtherCandidates==nNumCandidates) ) */ + ) + { + s_group_info->num_candidates = -1; /* no candidate exists */ + goto quick_exit; + } + if ( !(s_subtype_all & (SALT_DONOR_Neg) ) ) { + t_group_info->bTautFlagsDone |= TG_FLAG_ALLOW_NO_NEGTV_O_DONE; + } + } else { + for ( i = 0; i < nNumCandidates; i ++ ) { + i1 = s_candidate[i].atnumber; + if ( 0 <= (s_type = GetSaltChargeType( at, i1, t_group_info, &s_subtype )) +#if ( INCL_NON_SALT_CANDIDATATES == 1 ) + || 0 < (s_type = GetOtherSaltChargeType( at, i1, t_group_info, &s_subtype, 1 /* bAccept_O*/ )) +#endif + ) { + s_candidate[nNumCandidates].type = s_type; + s_candidate[nNumCandidates].subtype = s_subtype; + s_candidate[nNumCandidates].endpoint = at[i1].endpoint; + } + } + } + /* Look for alt paths connecting: + SALT_DONOR_Neg to SALT_ACCEPTOR : long distance migration of negative charges + SALT_DONOR_H to SALT_ACCEPTOR : long distance migration of H-atoms + */ + do { + nNumChanges = 0; + for ( i1 = 0; i1 < nNumCandidates; i1 ++ ) { + j1 = s_candidate[i1].atnumber; + for ( i2 = i1+1; i2 < nNumCandidates; i2 ++ ) { + /* prev. approach: do not test if both candidates are not "salt-type". Disabled 2004-03-18 + if ( s_candidate[i1].type && s_candidate[i2].type ) + continue; + */ + j2 = s_candidate[i2].atnumber; + if ( at[j1].endpoint && at[j1].endpoint == at[j2].endpoint ) { + continue; + } + for ( j = 0; j < 2; j ++ ) { + if ( j ) { + ii1 = i2; /* candidate 1 (donor) ordering number */ + ii2 = i1; /* candidate 2 (acceptor) ordering number */ + jj1 = j2; /* candidate 1 (donor) atom number */ + jj2 = j1; /* candidate 2 (acceptor) atom number */ + } else { /* transposition */ + ii1 = i1; /* candidate 1 (donor) ordering number */ + ii2 = i2; /* candidate 2 (acceptor) ordering number */ + jj1 = j1; /* candidate 1 (donor) atom number */ + jj2 = j2; /* candidate 2 (acceptor) atom number */ + } + + if ( ( s_candidate[ii1].subtype & (SALT_DONOR_Neg | SALT_DONOR_H) ) && + ( s_candidate[ii2].subtype & SALT_ACCEPTOR ) ) + { +/****printf("\nChkpt @ %-s:%-d ", __FILE__,__LINE__); fflush(stdout);*/ + ret = bExistsAltPath( pCG, pBNS, pBD, NULL, at, num_atoms, jj2, jj1, ALT_PATH_MODE_4_SALT ); + num_tested ++; + if ( IS_BNS_ERROR( ret ) ) + { + err = ret; + goto quick_exit; + } + + if ( ret & 1 ) + { + nDelta = (ret & ~3) >> 2; + nNumChanges += (ret & 2); + for ( i = 0; i < 2; i ++ ) + { + jj = i? jj2 : jj1; + AddEndPoint( EndPoint+i, at, jj ); + } + + /* add/merge taut groups and reinit pBNS in the fly */ + ret = RegisterEndPoints( pCG, t_group_info, + EndPoint, 2, at, num_atoms, c_group_info, pBNS ); + if ( ret == -1 ) + { + ret = BNS_PROGRAM_ERR; + } + if ( ret < 0 ) + { + err = ret; + goto quick_exit; + } + if ( nDelta ) + { + err = BNS_RADICAL_ERR; + goto quick_exit; + } + nNumChanges += (ret > 0); + break; /* avoid redundant repetition */ + } + } + } + } + } + nTotNumChanges += nNumChanges; + } while ( num_tested && nNumChanges ); + +quick_exit: + if ( !err ) { + nTotNumChanges += nNumChanges; /* nNumChanges != 0 only in case of 'goto quick_exit' */ + if ( s_group_info->num_candidates == 0 ) { + /* first time: initialize */ + s_group_info->num_candidates = num_tested? nNumCandidates : -1; /* no candidate exists */ + } + } else { + nTotNumChanges = err; + } +#if ( MAX_LOCAL_TGNUM > 0 ) + if ( ptgData != tgData ) { + inchi_free( ptgData ); + } +#endif + } + return nTotNumChanges; +} + + +#else +/********************************************************************************************************/ +int MarkSaltChargeGroups ( CANON_GLOBALS *pCG, inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, + T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, + struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD ) +{ + + int nNumChanges = 0, nTotNumChanges = 0; + if ( s_group_info && s_group_info->s_candidate && s_group_info->max_num_candidates > 0 ) { + int i, i1, i2, j, j1, j2, jj, ii1, ii2, jj1, jj2, k, num_tested; + S_CANDIDATE *s_candidate = s_group_info->s_candidate; + int nMaxNumCandidates = s_group_info->max_num_candidates; + int nNumCandidates = s_group_info->num_candidates; + int nNumOtherCandidates = s_group_info->num_other_candidates; + int s_type, s_subtype; + int ret, nDelta, nMobile; + int s_subtype_all = 0; + T_ENDPOINT EndPoint[2]; + + if ( nNumCandidates <= -1 || !t_group_info || !t_group_info->t_group ) { + return 0; + } else + if ( nNumCandidates == 0 ) { + for ( i = 0, nNumCandidates = nNumOtherCandidates = 0; i < num_atoms; i ++ ) { + if ( 0 <= (s_type = GetSaltChargeType( at, i, t_group_info, &s_subtype )) ) { + if ( nNumCandidates >= nMaxNumCandidates ) { + return BNS_VERT_EDGE_OVFL; + } + s_candidate[nNumCandidates].atnumber = i; + s_candidate[nNumCandidates].type = s_type; + s_candidate[nNumCandidates].subtype = s_subtype; + s_candidate[nNumCandidates].endpoint = at[i].endpoint; + nNumCandidates ++; + s_subtype_all |= s_subtype; + /*i1 = i;*/ /* save a representative of a tautomeric group */ + } +#if ( INCL_NON_SALT_CANDIDATATES == 1 ) + else /* new */ + if ( 0 < (s_type = GetOtherSaltChargeType( at, i, t_group_info, &s_subtype, 1 /* bAccept_O*/ )) ) { + if ( nNumCandidates >= nMaxNumCandidates ) { + return BNS_VERT_EDGE_OVFL; + } + s_candidate[nNumCandidates].atnumber = i; + s_candidate[nNumCandidates].type = s_type; + s_candidate[nNumCandidates].subtype = s_subtype; + s_candidate[nNumCandidates].endpoint = at[i].endpoint; + nNumCandidates ++; + nNumOtherCandidates ++; + s_subtype_all |= s_subtype; + } +#endif + } + + /* changes: TG_FLAG_ALLOW_NO_NEGTV_O replaced CHARGED_SALTS_ONLY==0 */ + if ( nNumCandidates <= 1 || nNumOtherCandidates == nNumCandidates || + ((t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O) ? + !(s_subtype_all & (SALT_DONOR_Neg | SALT_DONOR_H)): + !(s_subtype_all & SALT_DONOR_Neg)) || + !(s_subtype_all & SALT_ACCEPTOR)) { + s_group_info->num_candidates = -1; /* no candidate exists */ + return 0; + } + if ( !(s_subtype_all & (SALT_DONOR_Neg) ) ) { + t_group_info->bTautFlagsDone |= TG_FLAG_ALLOW_NO_NEGTV_O_DONE; + } + } else { + for ( i = 0; i < nNumCandidates; i ++ ) { + i1 = s_candidate[i].atnumber; + if ( 0 <= (s_type = GetSaltChargeType( at, i1, t_group_info, &s_subtype )) +#if ( INCL_NON_SALT_CANDIDATATES == 1 ) + || 0 < (s_type = GetOtherSaltChargeType( at, i1, t_group_info, &s_subtype, 1 /* bAccept_O*/ )) +#endif + ) { + s_candidate[nNumCandidates].type = s_type; + s_candidate[nNumCandidates].subtype = s_subtype; + s_candidate[nNumCandidates].endpoint = at[i1].endpoint; + } + } + } + /* Look for alt paths connecting: + SALT_DONOR_Neg to SALT_ACCEPTOR : long distance migration of negative charges + SALT_DONOR_H to SALT_ACCEPTOR : long distance migration of H-atoms + */ + num_tested = 0; + do { + nNumChanges = 0; + for ( i1 = 0; i1 < nNumCandidates; i1 ++ ) { + j1 = s_candidate[i1].atnumber; + for ( i2 = i1+1; i2 < nNumCandidates; i2 ++ ) { + if ( s_candidate[i1].type && s_candidate[i2].type ) + continue; /* both candidates are not "salt-type" */ + j2 = s_candidate[i2].atnumber; + if ( at[j1].endpoint && at[j1].endpoint == at[j2].endpoint ) { + continue; + } + for ( j = 0; j < 2; j ++ ) { + if ( j ) { + ii1 = i2; /* candidate 1 (donor) ordering number */ + ii2 = i1; /* candidate 2 (acceptor) ordering number */ + jj1 = j2; /* candidate 1 (donor) atom number */ + jj2 = j1; /* candidate 2 (acceptor) atom number */ + } else { /* transposition */ + ii1 = i1; /* candidate 1 (donor) ordering number */ + ii2 = i2; /* candidate 2 (acceptor) ordering number */ + jj1 = j1; /* candidate 1 (donor) atom number */ + jj2 = j2; /* candidate 2 (acceptor) atom number */ + } + + if ( ( s_candidate[ii1].subtype & (SALT_DONOR_Neg | SALT_DONOR_H) ) && + ( s_candidate[ii2].subtype & SALT_ACCEPTOR ) ) { + ret = bExistsAltPath( pCG, pBNS, pBD, NULL, at, num_atoms, jj2, jj1, ALT_PATH_MODE_4_SALT ); + num_tested ++; + if ( IS_BNS_ERROR( ret ) ) { + return ret; + } + if ( ret & 1 ) { + nDelta = (ret & ~3) >> 2; + nNumChanges += (ret & 2); + for ( i = 0; i < 2; i ++ ) { + jj = i? jj2 : jj1; + EndPoint[i].nAtomNumber = jj; + EndPoint[i].nEquNumber = 0; + EndPoint[i].nGroupNumber = at[jj].endpoint; + if ( at[jj].endpoint ) { + memset( EndPoint[i].num, 0, sizeof(EndPoint[i].num) ); + } else { + AddAtom2num( EndPoint[i].num, at, jj, 2 ); /* fill out */ + AddAtom2DA( EndPoint[i].num_DA, at, jj, 2 ); + /* + nMobile = EndPoint[i].num[1] = (at[jj].charge == -1); + nMobile = EndPoint[i].num[0] = at[jj].num_H + nMobile; + for ( k = 0; k < T_NUM_ISOTOPIC; k ++ ) { + EndPoint[i].num[T_NUM_NO_ISOTOPIC+k] = at[jj].num_iso_H[NUM_H_ISOTOPES-k-1]; + } + */ + } + } + /* add/merge taut groups and reinit pBNS */ + ret = RegisterEndPoints( pCG, t_group_info, + EndPoint, 2, at, num_atoms, c_group_info, pBNS ); + if ( ret < 0 ) { + return ret; + } + nNumChanges += (ret > 0); + if ( nDelta ) { + goto quick_exit; + } + break; /* avoid redundant repetition */ + } + } + } + } + } + nTotNumChanges += nNumChanges; + } while ( num_tested && nNumChanges ); + +quick_exit: + nTotNumChanges += nNumChanges; /* nNumChanges != 0 only in case of 'goto quick_exit' */ + if ( s_group_info->num_candidates == 0 ) { + /* first time: initialize */ + s_group_info->num_candidates = num_tested? nNumCandidates : -1; /* no candidate exists */ + } + } + return nTotNumChanges; +} +#endif + + +int MergeSaltTautGroups( CANON_GLOBALS *pCG, inp_ATOM *at, int num_atoms, + S_GROUP_INFO *s_group_info, + T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info, + struct BalancedNetworkStructure *pBNS ) +{ + /* count candidates to be connected: exclude pure donors that do not belong to any t-group */ + AT_NUMB nCurTGroupNumber; + int i, j, /*k,*/ ret, iat, /*nMobile,*/ nMinNumEndpoints; + int s_subtype_all, s_subtype_taut; + int nMaxNumCandidates, nNumCandidates, nNumCandidates2; + T_ENDPOINT EndPointStackArray[MAX_STACK_ARRAY_LEN]; /* will be reallocated if too short */ + T_ENDPOINT *EndPoint = EndPointStackArray; + + + if ( !s_group_info || !s_group_info->s_candidate || /*s_group_info->num_candidates <= 0 ||*/ + !t_group_info || !t_group_info->t_group || !c_group_info ) { + return 0; + } + nMinNumEndpoints = 0; + nMaxNumCandidates = s_group_info->max_num_candidates; + nCurTGroupNumber = MAX_ATOMS; /* impossible t-group number */ + s_subtype_all = s_subtype_taut = 0; + /* collect tautomeric acidic O and previously non-tautomeric C-OH, C-SH, C-O(-), C-S(-) */ + /* find whether previously found tautomeric atoms have both mobile H and (-) */ + if ( 1 || (s_group_info->num_candidates < 0) ) { + /* can be only -O(-) and -OH */ + int s_type, s_subtype; + S_CANDIDATE *s_candidate = s_group_info->s_candidate; + for ( i = 0, nNumCandidates = nNumCandidates2 = 0; i < num_atoms; i ++ ) { + s_subtype = 0; + if ( 0 == (s_type = GetSaltChargeType( at, i, t_group_info, &s_subtype )) || + /* -C=O or =C-OH, O = S, Se, Te */ + + /*(t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT) &&*/ + 1 == (s_type = GetOtherSaltChargeType( at, i, t_group_info, &s_subtype, 1/* bAccept_O*/ )) || + /* =Z-MH or -Z=M, Z = centerpoint, M = endpoint, other than above. M may be N */ + + 2 == (s_type = GetOtherSaltType( at, i, &s_subtype )) || + /* >C-SH, >C-S(-); S=S,Se,Te */ + + /* other proton donor or acceptor */ + bHasAcidicHydrogen( at, i) && ((s_type=3), (s_subtype = SALT_p_DONOR)) || + bHasAcidicMinus( at, i) && ((s_type=3), (s_subtype = SALT_p_ACCEPTOR)) + ) { + + if ( nNumCandidates >= nMaxNumCandidates ) { + return BNS_VERT_EDGE_OVFL; + } + if ( at[i].endpoint ) { + s_subtype_taut |= s_subtype; + } else + if ( bDoNotMergeNonTautAtom(at, i) ) { + continue; /* ignore non-tautomeric N */ + } + if ( !( s_subtype & SALT_DONOR_ALL ) || + (s_subtype & SALT_ACCEPTOR) && !at[i].endpoint ) { + continue; /* do not include non-taut acceptors like -C=O */ + } + s_candidate[nNumCandidates].atnumber = i; + s_candidate[nNumCandidates].type = s_type; + s_candidate[nNumCandidates].subtype = s_subtype; + s_candidate[nNumCandidates].endpoint = at[i].endpoint; + nNumCandidates ++; + s_subtype_all |= s_subtype; + } + } + /* + Forced merging occurs upon: + =========================== + (t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O) or + (t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT) + + + Allow forced merging in cases: + {t-groups} (H, (-)} {H, (-), t-groups} + + + Normal salt merging in cases: + (H, (-)} {H, (-), t-groups}, + + Cannot merge H into t-groups if no (-) is present + */ + + + if ( (t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O) || + (t_group_info->bTautFlagsDone & TG_FLAG_FOUND_SALT_CHARGES_DONE) || + (t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT) ) { + /* force merge even though no negative charges are present */ + if ( nNumCandidates <= 1 || + (!(s_subtype_all & SALT_DONOR_Neg2) || !(s_subtype_all & SALT_DONOR_H2)) && + !t_group_info->num_t_groups ) { + s_group_info->num_candidates = -1; /* no candidate exists */ + return 0; + } + } else { + /* normal salt mode: merge if both -XH and -X(-) are present */ + if ( nNumCandidates <= 1 || + (!(s_subtype_all & SALT_DONOR_Neg2) || !(s_subtype_all & SALT_DONOR_H2)) ) { + s_group_info->num_candidates = -1; /* no candidate exists */ + return 0; + } + } + /* -- old code -- + if ( nNumCandidates <= 1 || + (((t_group_info->bTautFlags & TG_FLAG_ALLOW_NO_NEGTV_O) || + (t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT)) ? + !(s_subtype_all & SALT_DONOR_ALL): + !(s_subtype_all & SALT_DONOR_Neg2) + ) + ) { + s_group_info->num_candidates = -1; + return 0; + } + */ + if ( !(s_subtype_all & (SALT_DONOR_Neg2) ) ) { + t_group_info->bTautFlagsDone |= TG_FLAG_ALLOW_NO_NEGTV_O_DONE; + } + s_group_info->num_candidates = nNumCandidates; + } + + for ( i = 0; i < s_group_info->num_candidates; i ++ ) { + iat = s_group_info->s_candidate[i].atnumber; + if ( (s_group_info->s_candidate[i].subtype & SALT_ACCEPTOR) && !at[iat].endpoint ) { + continue; /* should not happen */ + } + s_subtype_all |= s_group_info->s_candidate[i].subtype; + if ( at[iat].endpoint != nCurTGroupNumber || !at[iat].endpoint ) { + nMinNumEndpoints ++; + } + nCurTGroupNumber = (int)at[iat].endpoint; + } + if ( nMinNumEndpoints <= 1 ) { + return 0; /* too few endpoints */ + } + + /* make sure we have enough memory */ + if ( nMinNumEndpoints > MAX_STACK_ARRAY_LEN ) { + if ( !(EndPoint = (T_ENDPOINT *)inchi_calloc( nMinNumEndpoints, sizeof(EndPoint[0]) ) ) ) { + /*printf("BNS_OUT_OF_RAM-8\n");*/ + return BNS_OUT_OF_RAM; + } + } + + nCurTGroupNumber = MAX_ATOMS; /* impossible t-group number */ + for ( i = j = 0; i < s_group_info->num_candidates; i ++ ) { + iat = s_group_info->s_candidate[i].atnumber; + if ( s_group_info->s_candidate[i].subtype == SALT_ACCEPTOR && !at[iat].endpoint ) { + continue; + } + if ( at[iat].endpoint != nCurTGroupNumber || !at[iat].endpoint ) { + AddEndPoint( EndPoint+j, at, iat ); + j ++; + } + nCurTGroupNumber = (int)at[iat].endpoint; + } + + ret = RegisterEndPoints( pCG, t_group_info, + EndPoint, j, at, num_atoms, c_group_info, pBNS ); + + if ( ret == -1 ) { + ret = BNS_PROGRAM_ERR; + } + + if ( EndPoint != EndPointStackArray ) { + inchi_free( EndPoint ); + } + + return ret; +} + + +int MakeIsotopicHGroup(inp_ATOM *at, int num_atoms, S_GROUP_INFO *s_group_info, + T_GROUP_INFO *t_group_info) +{ + /* all tautomeric atoms and all possible H+ donors and acceptors that have H */ + int i, j, k, n, bHasH, tg, nError=0; + int s_subtype_all, s_subtype_taut; + int nMaxNumCandidates, nNumCandidates, nNumNonTautCandidates; + + + if ( !s_group_info || !s_group_info->s_candidate || /*s_group_info->num_candidates <= 0 ||*/ + !t_group_info || !t_group_info->t_group ) { + return 0; + } + nMaxNumCandidates = s_group_info->max_num_candidates; + s_subtype_all = s_subtype_taut = 0; + memset( t_group_info->num_iso_H, 0, sizeof(t_group_info->num_iso_H) ); + if ( 1 || (s_group_info->num_candidates < 0) ) { + int s_type, s_subtype; + S_CANDIDATE *s_candidate = s_group_info->s_candidate; + for ( i = 0, nNumCandidates = nNumNonTautCandidates = 0; i < num_atoms; i ++ ) { + s_subtype = 0; + s_type = 0; + if ( at[i].endpoint ) { + if ( (tg = t_group_info->tGroupNumber[at[i].endpoint]) && + at[i].endpoint == t_group_info->t_group[tg-=1].nGroupNumber ) { + bHasH = (int)t_group_info->t_group[tg].num[0] - (int)t_group_info->t_group[tg].num[1]; + } else { + nError = BNS_PROGRAM_ERR; + break; + } + } else { + bHasH = (int)at[i].num_H; + } + if ( bHasH && at[i].endpoint || /* tautomeric atoms */ + + /* non-tautomeric heteroatoms that + (a) have H and + (b) may be donors of H + therefore may exchange isotopic-non-isotopic H */ + bHasH && + (0 == (s_type = GetSaltChargeType( at, i, t_group_info, &s_subtype )) || + /* -C=O or =C-OH, O = S, Se, Te */ + + /*(t_group_info->tni.bNormalizationFlags & FLAG_FORCE_SALT_TAUT) &&*/ + 1 == (s_type = GetOtherSaltChargeType( at, i, t_group_info, &s_subtype, 1/* bAccept_O*/ )) || + /* =Z-MH or -Z=M, Z = centerpoint, M = endpoint, other than above. M may be N */ + + 2 == (s_type = GetOtherSaltType( at, i, &s_subtype )) || + /* >C-SH, >C-S(-); S=S,Se,Te */ + + /* other proton donor or acceptor */ + bHasAcidicHydrogen( at, i) && ((s_type=3), (s_subtype = SALT_p_DONOR)) || + bHasAcidicMinus( at, i) && ((s_type=3), (s_subtype = SALT_p_ACCEPTOR)) || + bHasOtherExchangableH (at, i) && ((s_type=3), (s_subtype = SALT_DONOR_H)) ) + + ) { + + if ( nNumCandidates >= nMaxNumCandidates ) { + return BNS_VERT_EDGE_OVFL; + } + s_candidate[nNumCandidates].atnumber = i; + s_candidate[nNumCandidates].type = s_type; + s_candidate[nNumCandidates].subtype = s_subtype; + s_candidate[nNumCandidates].endpoint = at[i].endpoint; + nNumCandidates ++; + nNumNonTautCandidates += !at[i].endpoint; + s_subtype_all |= s_subtype; + } + } + if ( nError ) { + return nError; + } + if ( nNumCandidates > 0 ) { + t_group_info->nIsotopicEndpointAtomNumber = (AT_NUMB *)inchi_calloc( nNumNonTautCandidates+1, sizeof(t_group_info->nIsotopicEndpointAtomNumber[0])); + t_group_info->nIsotopicEndpointAtomNumber[0] = nNumNonTautCandidates; + for ( i = 0, n = 1; i < nNumCandidates; i ++ ) { + k = s_candidate[i].atnumber; + if ( !at[k].endpoint ) { + t_group_info->nIsotopicEndpointAtomNumber[n++] = k; + } + for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { + t_group_info->num_iso_H[j] += at[k].num_iso_H[j]; + } + at[k].cFlags |= AT_FLAG_ISO_H_POINT; + } + t_group_info->nNumIsotopicEndpoints = nNumNonTautCandidates+1; + } + } + return nNumCandidates; +} + + +/*#else*/ /* } DISCONNECT_SALTS == 0 */ + + +/********************************************************************************** + Charges and tautomeric endpoints (N only) + ********************************************************************************** + + H = number of possibly moveable hydrogen atoms + C = possibly moveable positive charge + + - = single bond + = = double bond + # = triple bond + ++-----------------------------------------------------------------------------+ +|ca-| H | edges to t- | 1 bond | 2 bonds | 3 bonds *) | +|se | C | and c-groups | (valence) | (valence) | (valence) | +| # | | (edges flow) | | | | ++---|------+---------------+----------------+----------------+----------------| +| 1 | H=0 | -- (1) | =NH (3) | =N- (3) | >N- (3) | +| | C=0 | == | | | | ++---|------+---------------+----------------+----------------+----------------| +| 2 | H=1 | == (2) | -NH2 (3) | -NH- (3) | none | +| | C=0 | == | | | | ++---|------+---------------+----------------+----------------+----------------| +| 3 | H=0 | -- (0) | #NH(+) (4) | =N(+)= (4) +)| >N(+)= (4) | +| | C=1 | -- | (prohibited | | | +| | | | by edge cap) | | | ++---|------+---------------+----------------+----------------+----------------| +| 4 | H=1 | == (1) | =NH2(+) (4) +)| =NH(+)- (4) +)| >NH(+)- (4) | +| | C=1 | -- | | | | ++---+-------------------------------------------------------------------------+ + + *) Cannot be a tautomeric endpoint + + +) The three charged types of atoms [=N(+)=, =NH(+)-, =NH2(+)] should be + checked for possible H-tautomerism. Other types in the marked by *) + column should not be checked as long as H(+) exchange is not considered + tautomeric. + + Other possibilities: -NH3(+) >NH2(+) >N(+)< cannot be H-tautomeric endpoints. + + Case #1 (H=0, C=0) and #4 (H=1,C=0) is indistinguishable from the + viewpoint of edges flow and capacities except for flow from N to (+) vertex. + + Without taking precautions H(+) can be transferred + + from =NH2(+) to =NH, + from =NH(+)- to =N-, + from >NH(+)- to >N- + + or to any other appropriate atom that has a lone electron pair and bonds + will not change. In this case no bond must be marked as tautomeric. + + For this reason before attempting to transfer H from one endpoint to + another the charges on the two atoms should be set to zero by + forcing zero flow from each of atoms to the (+)-vertices if the + atoms belong to a c-group. + + **********************************************************************************/ + + +/* + Mark Tautomer Groups: + do not identify positively charged N as endpoints for now +*/ +int MarkTautomerGroups( CANON_GLOBALS *pCG, inp_ATOM *at, int num_atoms, T_GROUP_INFO *t_group_info, C_GROUP_INFO *c_group_info + , struct BalancedNetworkStructure *pBNS, struct BalancedNetworkData *pBD ) +{ + int i, j, k, m, endpoint_valence, centerpoint, endpoint, bond_type, nMobile, num_changes=0, tot_changes=0; + T_ENDPOINT EndPoint[MAXVAL]; + T_BONDPOS BondPos[MAXVAL]; + AT_NUMB nGroupNumber; + int bDiffGroups; + int nNumEndPoints, nNumBondPos, nNumPossibleMobile; + int bTautBond, bNonTautBond, bAltBond; + int nNumDonor, nNumAcceptor, bPossiblyEndpoint; + T_GROUP *t_group; + int *pnum_t, max_num_t, bIgnoreIsotopic; + ENDPOINT_INFO eif1, eif2; + int nErr = 0; +#define ALLOWED_EDGE(PBNS, IAT,IBOND) ( !(PBNS) || !(PBNS)->edge || !(PBNS)->vert || !(PBNS)->edge[(PBNS)->vert[IAT].iedge[IBOND]].forbidden) +#define ACTUAL_ORDER(PBNS, IAT,IBOND, BTYPE) ( ((PBNS) && (PBNS)->edge && (PBNS)->vert &&\ + ((BTYPE)==BOND_ALT_123 || (BTYPE)==BOND_ALT_13 || (BTYPE)==BOND_ALT_23))? (PBNS)->edge[(PBNS)->vert[IAT].iedge[IBOND]].flow+BOND_TYPE_SINGLE:(BTYPE)) + + + if ( !t_group_info || !(t_group_info->bTautFlags & TG_FLAG_TEST_TAUT__ATOMS) ) + return 0; + /* initial t_group allocation */ + if ( !t_group_info->t_group && !t_group_info->max_num_t_groups ) { + INCHI_MODE bTautFlags = t_group_info->bTautFlags; /* save initial setting */ + INCHI_MODE bTautFlagsDone = t_group_info->bTautFlagsDone; /* save previous findings, if any */ + TNI tni = t_group_info->tni; + AT_NUMB *tGroupNumber = t_group_info->tGroupNumber; + bIgnoreIsotopic = t_group_info->bIgnoreIsotopic; + memset( t_group_info, 0, sizeof(*t_group_info) ); + t_group_info->bIgnoreIsotopic = bIgnoreIsotopic; /* restore initial setting */ + t_group_info->bTautFlags = bTautFlags; + t_group_info->bTautFlagsDone = bTautFlagsDone; + t_group_info->tni = tni; + t_group_info->tGroupNumber = tGroupNumber; + t_group_info->max_num_t_groups = num_atoms/2+1; /* upper limit */ + if (!(t_group_info->t_group = (T_GROUP*)inchi_calloc(t_group_info->max_num_t_groups, sizeof(t_group[0])))) { + return (t_group_info->max_num_t_groups = -1); /* failed, out of RAM */ + } + } + /* check if t_group_info exists */ + if ( !t_group_info->t_group || !t_group_info->max_num_t_groups ) + return 0; + + if ( 0 > t_group_info->max_num_t_groups ) + return t_group_info->max_num_t_groups; + + pnum_t = &t_group_info->num_t_groups; /* number of found tautomer endpoint groups */ + t_group = t_group_info->t_group; + max_num_t = t_group_info->max_num_t_groups; + bIgnoreIsotopic = t_group_info->bIgnoreIsotopic; + /* 1-3 tautomers */ + for ( i = 0; i < num_atoms; i ++ ) { + /* find possible endpoint Z = at[i] */ + if ( endpoint_valence = nGetEndpointInfo( at, i, &eif1 ) ) { + /* 1st endpoint candidate found. Find centerpoint candidate */ + for ( j = 0; j < at[i].valence; j ++ ) { + bond_type = (int)at[i].bond_type[j] & ~BOND_MARK_ALL; +#if ( FIX_BOND23_IN_TAUT == 1 ) + bond_type = ACTUAL_ORDER(pBNS,i,j,bond_type); +#endif + centerpoint = (int)at[i].neighbor[j]; /* a centerpoint candidate */ + if ( (bond_type == BOND_DOUBLE || + bond_type == BOND_ALTERN || + bond_type == BOND_ALT12NS || + bond_type == BOND_TAUTOM) && is_centerpoint_elem( at[centerpoint].el_number ) + && ALLOWED_EDGE(pBNS, i, j) + ) { + /* test a centerpoint candidate. */ + /* find all endpoints including at[i] and store them into EndPoint[] */ + nNumPossibleMobile = 0; + nGroupNumber = (AT_NUMB)num_atoms; /* greater than any tautomeric group number */ + bDiffGroups = -1; /* ignore the first difference */ + nNumDonor = nNumAcceptor = 0; + for ( k = 0, nNumEndPoints = 0, nNumBondPos = 0; k < at[centerpoint].valence; k ++ ) { + endpoint = at[centerpoint].neighbor[k]; /* endpoint candidate */ + bond_type = (int)at[centerpoint].bond_type[k] & ~BOND_MARK_ALL; +#if ( FIX_BOND23_IN_TAUT == 1 ) + bond_type = ACTUAL_ORDER(pBNS,centerpoint,k,bond_type); +#endif + bTautBond = + bNonTautBond = + bAltBond = + bPossiblyEndpoint = 0; + if ( !ALLOWED_EDGE(pBNS, centerpoint, k) ) { + continue; + } else + if ( bond_type == BOND_ALTERN || bond_type == BOND_ALT12NS || bond_type == BOND_TAUTOM ) { + bTautBond = 1; +#if ( REPLACE_ALT_WITH_TAUT == 1 ) + bAltBond = (bond_type == BOND_ALTERN || bond_type == BOND_ALT12NS); +#endif + } else + if ( bond_type == BOND_SINGLE || bond_type == BOND_DOUBLE ) + bNonTautBond = 1; + else + continue; + + if ( !(endpoint_valence = nGetEndpointInfo( at, endpoint, &eif1 )) ) + continue; /* not an endpoint element or can't have mobile groups */ + /* save information about the found possible tautomeric endpoint */ + /* 2 = T_NUM_NO_ISOTOPIC non-isotopic values */ + nMobile = + AddAtom2num( EndPoint[nNumEndPoints].num, at, endpoint, 2 ); /* fill out */ + AddAtom2DA( EndPoint[nNumEndPoints].num_DA, at, endpoint, 2 ); + /* --- why is isitopic info missing ? -- see below + nMobile = EndPoint[nNumEndPoints].num[1] = (at[endpoint].charge == -1); + nMobile = EndPoint[nNumEndPoints].num[0] = at[endpoint].num_H + nMobile; + */ + if ( bNonTautBond ) { + m = (bond_type == BOND_SINGLE && (nMobile || at[endpoint].endpoint)); + nNumDonor += m; + bPossiblyEndpoint += m; + m = (bond_type == BOND_DOUBLE ); + nNumAcceptor += m; + bPossiblyEndpoint += m; + } else { + /* tautomeric or alternating bond */ + m = (0 != at[endpoint].endpoint || eif1.cDonor ); + nNumDonor += m; + bPossiblyEndpoint += m; + m = ( at[endpoint].endpoint || + eif1.cNeutralBondsValence > at[endpoint].valence ); + nNumAcceptor += m; + bPossiblyEndpoint += m; + } + if ( !bPossiblyEndpoint ) + continue; + EndPoint[nNumEndPoints].nGroupNumber = at[endpoint].endpoint; /* =0 if it is an endpoint for the 1st time */ + EndPoint[nNumEndPoints].nEquNumber = 0; + EndPoint[nNumEndPoints].nAtomNumber = (AT_NUMB)endpoint; + if ( nGroupNumber != at[endpoint].endpoint ) { + bDiffGroups ++; + nGroupNumber = at[endpoint].endpoint; + } + + /* save positions of all, not only possibly tautomeric bonds */ +#if ( REPLACE_ALT_WITH_TAUT != 1 ) + if ( bNonTautBond || bAltBond ) { +#endif + BondPos[nNumBondPos].nAtomNumber = (AT_NUMB)centerpoint; + BondPos[nNumBondPos].neighbor_index = (AT_NUMB)k; /* bond ordering number; used to change bonds to tautomeric only */ + nNumBondPos ++; +#if ( REPLACE_ALT_WITH_TAUT != 1 ) + } +#endif + /* mobile group is possible if (a) the endpoint has a mobile group or */ + /* (b) the centerpoint is adjacent to another endpoint */ + nNumPossibleMobile += (nMobile>0 || at[endpoint].endpoint); + nNumEndPoints ++; + } + if ( nNumEndPoints > 1 && nNumPossibleMobile && nNumDonor && nNumAcceptor ) { + /* + * a tautomeric group has been found + * + * at this point: + * nGroupNumber = 0 if all endpoints belong to a newly discovered tautomeric group + * bDiffGroups > 0 if at least 2 tautomeric groups are to be merged (one of them can be new) + * case (nGroupNumber != 0 && bDiffGroups = 0 ) ignored because all endpoints belong to the same known t-group + * case (nGroupNumber != 0 && bDiffGroups < 0 ) cannot happen + */ + + nErr = FindAccessibleEndPoints( pCG, + EndPoint, &nNumEndPoints, + BondPos, &nNumBondPos, + pBNS, pBD, at, + num_atoms, c_group_info, + ALT_PATH_MODE_TAUTOM ); + + if ( IS_BNS_ERROR(nErr) ) { + return nErr; + } + nErr = 0; + + if ( nNumEndPoints > 0 ) { + if ( !nGroupNumber || bDiffGroups > 0 ) { + + num_changes = RegisterEndPoints( pCG, t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS ); + if ( num_changes == -1 ) { + nErr = CT_TAUCOUNT_ERR; + } + if ( num_changes < 0 ) { + nErr = num_changes; + } + if ( nErr ) + goto exit_function; + tot_changes += (num_changes>0); + } + if ( nNumBondPos > 0 ) { + /* some of the bonds have not been marked as tautomeric yet */ + num_changes = SetTautomericBonds( at, nNumBondPos, BondPos ); + tot_changes += (num_changes>0); + } + } + } + } + } + } + } +#if ( KETO_ENOL_TAUT == 1 ) /***** post v.1 feature *****/ + if ( t_group_info->bTautFlags & TG_FLAG_KETO_ENOL_TAUT ) { + /* 1,3 keto-enol tautomerism */ + for ( i = 0; i < num_atoms; i ++ ) { + /* find possible endpoint Z = at[i] */ + if ( endpoint_valence = nGetEndpointInfo_KET( at, i, &eif1 ) ) { + /* 1st endpoint candidate found. Find centerpoint candidate */ + for ( j = 0; j < at[i].valence; j ++ ) { + bond_type = (int)at[i].bond_type[j] & ~BOND_MARK_ALL; +#if ( FIX_BOND23_IN_TAUT == 1 ) + bond_type = ACTUAL_ORDER(pBNS,i,j,bond_type); +#endif + centerpoint = (int)at[i].neighbor[j]; /* a centerpoint candidate */ + if ( (bond_type == BOND_DOUBLE || + bond_type == BOND_ALTERN || + bond_type == BOND_ALT12NS || + bond_type == BOND_TAUTOM) && + is_centerpoint_elem_KET( at[centerpoint].el_number ) && + !at[centerpoint].charge && !at[centerpoint].radical && + /* only normal carbon is allowed */ + 4 == at[centerpoint].chem_bonds_valence + at[centerpoint].num_H + && ALLOWED_EDGE(pBNS, i, j) + ) { + int num_O = 0; + int num_C = 0; + /* test a centerpoint candidate. */ + /* find all endpoints including at[i] and store them into EndPoint[] */ + nNumPossibleMobile = 0; + nGroupNumber = (AT_NUMB)num_atoms; /* greater than any tautomeric group number */ + bDiffGroups = -1; /* ignore the first difference */ + nNumDonor = nNumAcceptor = 0; + for ( k = 0, nNumEndPoints = 0, nNumBondPos = 0; k < at[centerpoint].valence; k ++ ) { + endpoint = at[centerpoint].neighbor[k]; /* endpoint candidate */ + bond_type = (int)at[centerpoint].bond_type[k] & ~BOND_MARK_ALL; +#if ( FIX_BOND23_IN_TAUT == 1 ) + bond_type = ACTUAL_ORDER(pBNS,centerpoint,k,bond_type); +#endif + bTautBond = + bNonTautBond = + bAltBond = + bPossiblyEndpoint = 0; + if ( !ALLOWED_EDGE(pBNS, centerpoint, k) ) { + continue; + } else + if ( bond_type == BOND_ALTERN || bond_type == BOND_ALT12NS || bond_type == BOND_TAUTOM ) { + bTautBond = 1; +#if ( REPLACE_ALT_WITH_TAUT == 1 ) + bAltBond = (bond_type == BOND_ALTERN || bond_type == BOND_ALT12NS); +#endif + } else + if ( bond_type == BOND_SINGLE || bond_type == BOND_DOUBLE ) + bNonTautBond = 1; + else + continue; + + if ( !(endpoint_valence = nGetEndpointInfo_KET( at, endpoint, &eif2 )) ) { + continue; + } + /* + if ( 3 != eif1.cKetoEnolCode + eif2.cKetoEnolCode && endpoint != i ) + continue; + */ + /* save information about the found possible tautomeric endpoint */ + /* 2 = T_NUM_NO_ISOTOPIC non-isotopic values */ + nMobile = + AddAtom2num( EndPoint[nNumEndPoints].num, at, endpoint, 2 ); /* fill out */ + AddAtom2DA( EndPoint[nNumEndPoints].num_DA, at, endpoint, 2 ); + /* --- why is isitopic info missing ? -- see below + nMobile = EndPoint[nNumEndPoints].num[1] = (at[endpoint].charge == -1); + nMobile = EndPoint[nNumEndPoints].num[0] = at[endpoint].num_H + nMobile; + */ + if ( bNonTautBond ) { + m = (bond_type == BOND_SINGLE && (nMobile || at[endpoint].endpoint)); + nNumDonor += m; + bPossiblyEndpoint += m; + m = (bond_type == BOND_DOUBLE ); + nNumAcceptor += m; + bPossiblyEndpoint += m; + } else { + /* tautomeric or alternating bond */ + m = (0 != at[endpoint].endpoint || eif1.cDonor ); + nNumDonor += m; + bPossiblyEndpoint += m; + m = ( at[endpoint].endpoint || + eif1.cNeutralBondsValence > at[endpoint].valence ); + nNumAcceptor += m; + bPossiblyEndpoint += m; + } + if ( !bPossiblyEndpoint ) + continue; + + num_O += (endpoint_valence == 2); + num_C += (endpoint_valence == 4); + + EndPoint[nNumEndPoints].nGroupNumber = at[endpoint].endpoint; /* =0 if it is an endpoint for the 1st time */ + EndPoint[nNumEndPoints].nEquNumber = 0; + EndPoint[nNumEndPoints].nAtomNumber = (AT_NUMB)endpoint; + if ( nGroupNumber != at[endpoint].endpoint ) { + bDiffGroups ++; + nGroupNumber = at[endpoint].endpoint; + } + + /* save positions of all, not only possibly tautomeric bonds */ +#if ( REPLACE_ALT_WITH_TAUT != 1 ) + if ( bNonTautBond || bAltBond ) { +#endif + BondPos[nNumBondPos].nAtomNumber = (AT_NUMB)centerpoint; + BondPos[nNumBondPos].neighbor_index = (AT_NUMB)k; /* bond ordering number; used to change bonds to tautomeric only */ + nNumBondPos ++; +#if ( REPLACE_ALT_WITH_TAUT != 1 ) + } +#endif + /* mobile group is possible if (a) the endpoint has a mobile group or */ + /* (b) the centerpoint is adjacent to another endpoint */ + nNumPossibleMobile += (nMobile>0 || at[endpoint].endpoint); + nNumEndPoints ++; + } + if ( nNumEndPoints > 1 && nNumPossibleMobile && nNumDonor && nNumAcceptor && num_O==1 && num_C ) { + /* + * a tautomeric group has been found + * + * at this point: + * nGroupNumber = 0 if all endpoints belong to a newly discovered tautomeric group + * bDiffGroups > 0 if at least 2 tautomeric groups are to be merged (one of them can be new) + * case (nGroupNumber != 0 && bDiffGroups = 0 ) ignored because all endpoints belong to the same known t-group + * case (nGroupNumber != 0 && bDiffGroups < 0 ) cannot happen + */ + + nErr = FindAccessibleEndPoints( pCG, EndPoint, &nNumEndPoints, BondPos, &nNumBondPos, + pBNS, pBD, at, num_atoms, c_group_info, ALT_PATH_MODE_TAUTOM_KET ); + + if ( IS_BNS_ERROR(nErr) ) { + return nErr; + } + nErr = 0; + + if ( nNumEndPoints > 0 ) { + if ( !nGroupNumber || bDiffGroups > 0 ) { + + num_changes = RegisterEndPoints( pCG, t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS ); + if ( num_changes == -1 ) { + nErr = CT_TAUCOUNT_ERR; + } + if ( num_changes < 0 ) { + nErr = num_changes; + } + if ( nErr ) + goto exit_function; + tot_changes += (num_changes>0); + } + if ( nNumBondPos > 0 ) { + /* some of the bonds have not been marked as tautomeric yet */ + num_changes = SetTautomericBonds( at, nNumBondPos, BondPos ); + tot_changes += (num_changes>0); + } + } + } + } + } + } + } + } +#endif /* KETO_ENOL_TAUT */ + +#if ( TAUT_OTHER == 1 ) /* { */ + if ( !tot_changes ) { +#define MAX_ALT_PATH_LEN 8 + int nMaxLenDfsPath = MAX_ALT_PATH_LEN; + int i1, i2; + AT_RANK *nDfsPathPos = (AT_RANK *)inchi_calloc( num_atoms, sizeof(nDfsPathPos[0]) ); + DFS_PATH DfsPath[MAX_ALT_PATH_LEN]; + int ret; + if ( !nDfsPathPos || !DfsPath ) { + tot_changes = CT_OUT_OF_RAM; /* */ + goto free_memory; + } +#if ( TAUT_15_NON_RING == 1 ) /***** post v.1 feature *****/ + if ( t_group_info->bTautFlags & TG_FLAG_1_5_TAUT ) { + /* 1,5 tautomerism; one of the endpoints should no be on a ring */ + /* + O OH O + || | || + A--pos- A--pos- A--pos- + / sib- // sib- ? / sib- + C ly C ly CH ly + \\ a <--> \ a <--> \ a + B--ring B--ring B--ring + | || || + NH N N + + Note: few recent modifications now allow the terminal N be in a ring, too + */ + for ( i1 = 0; i1 < num_atoms; i1 ++ ) { + /* find possible endpoint Z = at[i1] */ + if ( !(endpoint_valence = nGetEndpointInfo( at, i1, &eif1 ) ) /*|| + at[i1].nNumAtInRingSystem > 1*/ ) { + continue; /* not a possibly endpoint */ + } + + if ( 1 ) { + nNumEndPoints = 0; + nNumBondPos = 0; + + ret = nGet15TautInAltPath( pCG, at, i1, nDfsPathPos, + DfsPath, nMaxLenDfsPath, + EndPoint, sizeof(EndPoint)/sizeof(EndPoint[0]), + BondPos, sizeof(BondPos)/sizeof(BondPos[0]), + &nNumEndPoints, &nNumBondPos, + pBNS, pBD, num_atoms); + + if ( ret > 0 ) { + if ( nNumEndPoints ) { + num_changes = RegisterEndPoints( pCG, t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS); + if ( num_changes == -1 ) { + nErr = CT_TAUCOUNT_ERR; + } + if ( num_changes < 0 ) { + nErr = num_changes; + } + if ( nErr ) + goto free_memory; + tot_changes += (num_changes > 0); + } + if ( nNumBondPos ) { + tot_changes += ( 0 < SetTautomericBonds( at, nNumBondPos, BondPos ) ); + } + } else + if ( IS_BNS_ERROR( ret ) ) { + nErr = ret; + goto free_memory; + } + } + } + } +#endif +#if ( TAUT_4PYRIDINOL_RINGS == 1 ) + /* 6-member rings */ + /* + O OH OH + || | | + / \ // \ / \\ + || || <--> | || <--> || | + \ / \\ / \ // + NH N N + */ + for ( i1 = 0; i1 < num_atoms; i1 ++ ) { + /* find possible endpoint Z = at[i1] */ + if ( 3 != (endpoint_valence = nGetEndpointInfo( at, i1, &eif1 ) ) || + 2 != at[i1].valence ) { + continue; /* not a nitrogen atom or a wrong valence */ + } + + if ( at[i1].nNumAtInRingSystem >= 6 ) { + nNumEndPoints = 0; + nNumBondPos = 0; + + ret = nGet15TautIn6MembAltRing( pCG, at, i1, nDfsPathPos, + DfsPath, nMaxLenDfsPath, + EndPoint, sizeof(EndPoint)/sizeof(EndPoint[0]), + BondPos, sizeof(BondPos)/sizeof(BondPos[0]), + &nNumEndPoints, &nNumBondPos, + pBNS, pBD, num_atoms); + if ( ret > 0 ) { + if ( nNumEndPoints ) { + num_changes = RegisterEndPoints( pCG, t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS); + if ( num_changes == -1 ) { + nErr = CT_TAUCOUNT_ERR; + } + if ( num_changes < 0 ) { + nErr = num_changes; + } + if ( nErr ) + goto free_memory; + tot_changes += (num_changes > 0); + } + if ( nNumBondPos ) { + tot_changes += ( 0 < SetTautomericBonds( at, nNumBondPos, BondPos ) ); + } + } else + if ( IS_BNS_ERROR( ret ) ) { + nErr = ret; + goto free_memory; + } + } + } +#endif /* TAUT_4PYRIDINOL_RINGS */ +#if ( TAUT_PYRAZOLE_RINGS == 1 ) + /* 5-member rings: + + Z Z + / \\ // \ + X Y <--> X Y + \\ / \ // + N--NH HN--N + + ^ ^ + search for these NH + */ + /* 5-member rings (pyrazole derivatives): look for the neighboring N */ + for ( i1 = 0; i1 < num_atoms; i1 ++ ) { + if ( 2 == at[i1].valence && + at[i1].nNumAtInRingSystem >= 5 && + 3 == (endpoint_valence = nGetEndpointInfo( at, i1, &eif1 )) + ) { + nMobile = at[i1].num_H + (at[i1].charge == -1); + for ( j = 0; j < at[i1].valence; j ++ ) { + int nMobile2, endpoint_valence2; + i2 = at[i1].neighbor[j]; + + /* may be important */ + if ( i2 >= i1 ) + continue; /* do not try same pair 2 times */ + + if ( at[i2].nRingSystem != at[i1].nRingSystem ) + continue; + + bond_type = (at[i1].bond_type[j] & ~BOND_MARK_ALL); + if ( bond_type != BOND_SINGLE && + bond_type != BOND_TAUTOM && + bond_type != BOND_ALT12NS && + bond_type != BOND_ALTERN || /* added 1-15-2002 */ + 2 != at[i2].valence || + 3 != (endpoint_valence2 = nGetEndpointInfo( at, i2, &eif2 ) ) ) { + continue; /* not a nitrogen atom or a wrong valence or not a single bond */ + } + nMobile2 = at[i2].num_H + (at[i2].charge == -1); /* number of mobile groups */ +#if ( TAUT_IGNORE_EQL_ENDPOINTS == 1 ) + if ( at[i1].endpoint && at[i1].endpoint == at[i2].endpoint ) + continue; /* atoms already belong to the same t-group */ +#endif + if ( !at[i1].endpoint && !at[i2].endpoint && 1!=nMobile + nMobile2 ) + continue; + + ret = nGet12TautIn5MembAltRing( pCG, at, i1, j, nDfsPathPos, + DfsPath, nMaxLenDfsPath, + EndPoint, sizeof(EndPoint)/sizeof(EndPoint[0]), + BondPos, sizeof(BondPos)/sizeof(BondPos[0]), + &nNumEndPoints, &nNumBondPos + , pBNS, pBD, num_atoms); + if ( ret > 0 ) { + if ( nNumEndPoints ) { + num_changes = RegisterEndPoints( pCG, t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS); + if ( num_changes == -1 ) { + nErr = CT_TAUCOUNT_ERR; + } + if ( num_changes < 0 ) { + nErr = num_changes; + } + if ( nErr ) + goto free_memory; + tot_changes += (num_changes > 0); + } + if ( nNumBondPos ) { + tot_changes += ( 0 < SetTautomericBonds( at, nNumBondPos, BondPos ) ); + } + } else + if ( IS_BNS_ERROR( ret ) ) { + nErr = ret; + goto free_memory; + } + } + } + } +#endif /* TAUT_PYRAZOLE_RINGS */ +#if ( TAUT_TROPOLONE_7 == 1 || TAUT_TROPOLONE_5 == 1 ) /* { */ + /******************************************************** + * A B + * | || + * 7-member rings (tropolones): look for M=Q--R--ZH, + * ^ ^ ^ ^ + * endpoint1 i1 i2 endpoint2 + * where A-Q-R=B belong to a 7-member alt. (except Q-R bond) ring: ..=A-(Q-R)=B-.. + * Bond Q-R should be single or tautomeric or alternating + * M=Q and R-ZH should be chain (non-ring) bonds + * Same for 5-member rings + */ + for ( i1 = 0; i1 < num_atoms; i1 ++ ) { + if ( at[i1].nNumAtInRingSystem >= +#if ( TAUT_TROPOLONE_5 == 1 ) + 5 +#else + 7 +#endif + && + bIsCenterPointStrict( at, i1 ) && +#if ( TAUT_RINGS_ATTACH_CHAIN == 1 ) + at[i1].bCutVertex && +#endif + at[i1].valence == 3 && !at[i1].endpoint ) { + int nMobile1, endpoint1, endpoint1_valence, bond_type1; + int nMobile2, endpoint2, endpoint2_valence, bond_type2; + for ( j = 0; j < at[i1].valence; j ++ ) { + i2 = at[i1].neighbor[j]; + /* + // may be important + if ( i2 > i1 ) + continue; // do not try same pair 2 times + */ + if ( at[i2].nRingSystem != at[i1].nRingSystem || + !bIsCenterPointStrict( at, i2 ) || +#if ( TAUT_RINGS_ATTACH_CHAIN == 1 ) + !at[i2].bCutVertex || +#endif + at[i2].valence != 3 || at[i2].endpoint ) + continue; + bond_type = (at[i1].bond_type[j] & ~BOND_MARK_ALL); + if ( bond_type != BOND_SINGLE && + bond_type != BOND_TAUTOM && + bond_type != BOND_ALT12NS && + bond_type != BOND_ALTERN ) { + continue; /* not a single bond between Q-R */ + } + /* find endpoints */ + for ( k = 0; k < at[i1].valence; k ++ ) { + endpoint1 = at[i1].neighbor[k]; + if ( endpoint1 == i2 ) + continue; /* j == k */ + if ( !(endpoint1_valence = nGetEndpointInfo( at, endpoint1, &eif1 ) ) ) + continue; /* not an endpoint1 element or can't have mobile groups */ +#if ( TAUT_RINGS_ATTACH_CHAIN == 1 ) + if ( at[endpoint1].nRingSystem == at[i1].nRingSystem ) + continue; +#endif + nMobile1 = at[endpoint1].num_H + (at[endpoint1].charge == -1); /* number of mobile groups */ + if ( nMobile1 + at[endpoint1].chem_bonds_valence != endpoint1_valence ) + continue; /* abnormal endpoint1 valence; ignore. */ + bond_type1 = (at[i1].bond_type[k] & ~BOND_MARK_ALL); + + if ( bond_type1 != BOND_SINGLE && + bond_type1 != BOND_DOUBLE && + bond_type1 != BOND_TAUTOM && + bond_type1 != BOND_ALT12NS && + bond_type1 != BOND_ALTERN ) + continue; + + for ( m = 0; m < at[i2].valence; m ++ ) { + endpoint2 = at[i2].neighbor[m]; + if ( endpoint2 == i1 ) + continue; + if ( !(endpoint2_valence = nGetEndpointInfo( at, endpoint2, &eif2 )) ) + continue; /* not an endpoint2 element or can't have mobile groups */ +#if ( TAUT_RINGS_ATTACH_CHAIN == 1 ) + if ( at[endpoint2].nRingSystem == at[i2].nRingSystem ) + continue; +#endif + nMobile2 = at[endpoint2].num_H + (at[endpoint2].charge == -1); /* number of mobile groups */ + bond_type2 = (at[i2].bond_type[m] & ~BOND_MARK_ALL); + + if ( bond_type2 != BOND_SINGLE && + bond_type2 != BOND_DOUBLE && + bond_type2 != BOND_TAUTOM && + bond_type2 != BOND_ALT12NS && + bond_type2 != BOND_ALTERN ) + continue; + + /* final test for possible tautomerism */ + nMobile = 0; + + if ( ALLOWED_EDGE(pBNS, i1, k) && ALLOWED_EDGE(pBNS, i2, m) ) { + + /* can mobile group move from 1 to 2? */ + nMobile += (at[endpoint1].endpoint || nMobile1) && /* from endpoint1 */ + (bond_type1 != BOND_DOUBLE) && + + (at[endpoint2].endpoint || /* to endpoint2 */ + eif2.cNeutralBondsValence > at[endpoint2].valence ) && + (bond_type2 != BOND_SINGLE); + + + /* can mobile group move from 2 to 1? */ + nMobile += (at[endpoint2].endpoint || nMobile2) && /* from endpoint2 */ + (bond_type2 != BOND_DOUBLE) && /*changed from BOND_SINGLE 2004-02-26 */ + + (at[endpoint1].endpoint || /* to endpoint1 */ + eif1.cNeutralBondsValence > at[endpoint1].valence ) && + (bond_type1 != BOND_SINGLE); + } + if ( !nMobile ) + continue; + + if ( bond_type1 == bond_type2 && + (bond_type1 == BOND_SINGLE || bond_type1 == BOND_DOUBLE) ) + continue; + /* -- old -- + if ( !at[endpoint1].endpoint && !at[endpoint2].endpoint && 1 != nMobile1 + nMobile2 ) + continue; + */ + /* -- new -- + + if ( !at[endpoint1].endpoint && !at[endpoint2].endpoint ) { + if ( !(bond_type1 == BOND_SINGLE || bond_type1 == BOND_DOUBLE) || + !(bond_type2 == BOND_SINGLE || bond_type2 == BOND_DOUBLE) ) { + // at this point bond_type1 != bond_type2 + continue; + } + if ( bond_type1 == BOND_SINGLE && !nMobile1 || + bond_type2 == BOND_SINGLE && !nMobile2 || + 0 == nMobile1 + nMobile2 ) { + continue; + } + } + */ +#if ( TAUT_TROPOLONE_7 == 1 ) + if ( at[i1].nNumAtInRingSystem >= 7 ) { + ret = nGet14TautIn7MembAltRing( pCG, at, i1, j, k, m, nDfsPathPos, + DfsPath, nMaxLenDfsPath, + EndPoint, sizeof(EndPoint)/sizeof(EndPoint[0]), + BondPos, sizeof(BondPos)/sizeof(BondPos[0]), + &nNumEndPoints, &nNumBondPos, + pBNS, pBD, num_atoms); + if ( ret > 0 ) { + if ( nNumEndPoints ) { + num_changes = RegisterEndPoints( pCG, t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS); + if ( num_changes == -1 ) { + nErr = CT_TAUCOUNT_ERR; + } + if ( num_changes < 0 ) { + nErr = num_changes; + } + if ( nErr ) + goto free_memory; + tot_changes += (num_changes > 0); + } + if ( nNumBondPos ) { + tot_changes += ( 0 < SetTautomericBonds( at, nNumBondPos, BondPos ) ); + } + } else + if ( IS_BNS_ERROR( ret ) ) { + nErr = ret; + goto free_memory; + } + } +#endif + +#if ( TAUT_TROPOLONE_5 == 1 ) + if ( at[i1].nNumAtInRingSystem >= 5 ) { + ret = nGet14TautIn5MembAltRing( pCG, at, i1, j, k, m, nDfsPathPos, + DfsPath, nMaxLenDfsPath, + EndPoint, sizeof(EndPoint)/sizeof(EndPoint[0]), + BondPos, sizeof(BondPos)/sizeof(BondPos[0]), + &nNumEndPoints, &nNumBondPos, + pBNS, pBD, num_atoms); + if ( ret > 0 ) { + if ( nNumEndPoints ) { + num_changes = RegisterEndPoints( pCG, t_group_info, EndPoint, nNumEndPoints, at, num_atoms, c_group_info, pBNS); + if ( num_changes == -1 ) { + nErr = CT_TAUCOUNT_ERR; + } + if ( num_changes < 0 ) { + nErr = num_changes; + } + if ( nErr ) + goto free_memory; + tot_changes += (num_changes > 0); + } + if ( nNumBondPos ) { + tot_changes += ( 0 < SetTautomericBonds( at, nNumBondPos, BondPos ) ); + } + } else + if ( IS_BNS_ERROR( ret ) ) { + nErr = ret; + goto free_memory; + } + } +#endif + } + } + } + } + } +#endif /* } TAUT_TROPOLONE */ +free_memory: + if ( nDfsPathPos ) { + inchi_free( nDfsPathPos ); + } +#undef MAX_ALT_PATH_LEN + } +#endif /* } FIND_RING_SYSTEMS */ +exit_function: + return nErr < 0? nErr : tot_changes; +} + + +int free_t_group_info( T_GROUP_INFO *t_group_info ) +{ + if ( t_group_info ) { + if ( t_group_info->t_group ) { + inchi_free( t_group_info->t_group ); + } + if ( t_group_info->nEndpointAtomNumber ) { + inchi_free( t_group_info->nEndpointAtomNumber ); + } + if ( t_group_info->tGroupNumber ) { + inchi_free( t_group_info->tGroupNumber ); + } + if ( t_group_info->nIsotopicEndpointAtomNumber ) { + inchi_free( t_group_info->nIsotopicEndpointAtomNumber ); + } + memset( t_group_info, 0, sizeof(*t_group_info)); + } + return 0; +} + + +int make_a_copy_of_t_group_info( T_GROUP_INFO *t_group_info, T_GROUP_INFO *t_group_info_orig ) +{ + int err = 0, len; + free_t_group_info( t_group_info ); + if ( t_group_info_orig && t_group_info ) { + if ( (len=t_group_info_orig->max_num_t_groups) > 0 ) { + if (t_group_info->t_group = + (T_GROUP*) inchi_malloc( len * sizeof(t_group_info->t_group[0]))) { + memcpy(t_group_info->t_group, + t_group_info_orig->t_group, + len * sizeof(t_group_info->t_group[0])); + } else { + err ++; + } + } + if ( (len = t_group_info_orig->nNumEndpoints) > 0 ) { + if (t_group_info->nEndpointAtomNumber = + (AT_NUMB*) inchi_malloc( len * sizeof(t_group_info->nEndpointAtomNumber[0]))) { + memcpy(t_group_info->nEndpointAtomNumber, + t_group_info_orig->nEndpointAtomNumber, + len * sizeof(t_group_info->nEndpointAtomNumber[0])); + } else { + err ++; + } + } + if ( (len = t_group_info_orig->num_t_groups) > 0 ) { + if (t_group_info->tGroupNumber = + (AT_NUMB*) inchi_malloc( len * TGSO_TOTAL_LEN * sizeof(t_group_info->tGroupNumber[0]))) { + memcpy(t_group_info->tGroupNumber, + t_group_info_orig->tGroupNumber, + len * TGSO_TOTAL_LEN * sizeof(t_group_info->tGroupNumber[0])); + } else { + err ++; + } + } + if ( (len = t_group_info_orig->nNumIsotopicEndpoints) > 0 ) { + if (t_group_info->nIsotopicEndpointAtomNumber = + (AT_NUMB*) inchi_malloc( len * sizeof(t_group_info->nIsotopicEndpointAtomNumber[0]))) { + memcpy(t_group_info->nIsotopicEndpointAtomNumber, + t_group_info_orig->nIsotopicEndpointAtomNumber, + len * sizeof(t_group_info->nIsotopicEndpointAtomNumber[0])); + } else { + err ++; + } + } + if ( !err ) { + t_group_info->nNumEndpoints = t_group_info_orig->nNumEndpoints; + t_group_info->num_t_groups = t_group_info_orig->num_t_groups; + t_group_info->max_num_t_groups = t_group_info_orig->max_num_t_groups; + t_group_info->bIgnoreIsotopic = t_group_info_orig->bIgnoreIsotopic; + t_group_info->nNumIsotopicEndpoints = t_group_info_orig->nNumIsotopicEndpoints; + t_group_info->tni = t_group_info_orig->tni; + /* + t_group_info->nNumRemovedExplicitH = t_group_info_orig->nNumRemovedExplicitH; + t_group_info->nNumRemovedProtons = t_group_info_orig->nNumRemovedProtons; + t_group_info->bNormalizationFlags = t_group_info_orig->bNormalizationFlags; + */ + /* + t_group_info->bHardAddedRemovedProtons = t_group_info_orig->bHardAddedRemovedProtons; + t_group_info->bSimpleAddedRemovedProtons = t_group_info_orig->bSimpleAddedRemovedProtons; + t_group_info->nNumCanceledCharges = t_group_info_orig->nNumCanceledCharges; + */ + } + t_group_info->bTautFlags = t_group_info_orig->bTautFlags; + t_group_info->bTautFlagsDone = t_group_info_orig->bTautFlagsDone; + } + return err; +} + + +/* Set tautomer group isotopic sort keys */ +int set_tautomer_iso_sort_keys( T_GROUP_INFO *t_group_info ) +{ + T_GROUP *t_group; + T_GROUP_ISOWT Mult = 1; + int i, j, num_t_groups, num_iso_t_groups = 0; + if ( !t_group_info || !(t_group = t_group_info->t_group) || + 0 >= (num_t_groups = t_group_info->num_t_groups) || t_group_info->nNumIsotopicEndpoints ) + return 0; + for ( i = 0; i < num_t_groups; i ++ ) { + t_group[i].iWeight = 0; + j = T_NUM_ISOTOPIC - 1; + Mult = 1; + do { + t_group[i].iWeight += Mult * (T_GROUP_ISOWT)t_group[i].num[T_NUM_NO_ISOTOPIC+j]; + } while ( --j >= 0 && (Mult *= T_GROUP_ISOWT_MULT) ); + num_iso_t_groups += (t_group[i].iWeight != 0); + } + return num_iso_t_groups; +} + + +/* + Fill t_group_info with information necessary to fill out tautomer part + of the linear connection table record. + + Note: on input, t_group_info should contain information created by MarkTautomerGroups() + No previous t_group_info adjustment due to throwing out disconnected parts of + the chemical structure is needed. + + Note2: throws out t_groups containing negative charges only (IGNORE_TGROUP_WITHOUT_H==1) + (leave their tautomeric bonds unchanged) + Note3: removes negative charges from other tautomeric groups + and adjust counts of mobile atoms if permitted (REMOVE_TGROUP_CHARGE==1) + */ +int CountTautomerGroups( sp_ATOM *at, int num_atoms, T_GROUP_INFO *t_group_info ) +{ + int i, j, ret = 0, nNumEndpoints, max_t_group, num_groups_noH; + + AT_NUMB nGroupNumber, nNewGroupNumber, *nCurrEndpointAtNoPos = NULL; + + T_GROUP *t_group; + int num_t; + /* int bIgnoreIsotopic, max_num_t; */ + AT_NUMB *nTautomerGroupNumber = NULL; + AT_NUMB *nEndpointAtomNumber = NULL; + AT_NUMB *tGroupNumber = NULL; + + if ( !t_group_info || !t_group_info->t_group || 0 >= t_group_info->max_num_t_groups ) { + return 0; /* empty t-groups */ + } + num_t = t_group_info->num_t_groups; + t_group = t_group_info->t_group; + /* + max_num_t = t_group_info->max_num_t_groups; + bIgnoreIsotopic = t_group_info->bIgnoreIsotopic; + */ + num_groups_noH = 0; + + /* the following 2 arrays are to be rebuilt here */ + if ( t_group_info->nEndpointAtomNumber ) { + inchi_free ( t_group_info->nEndpointAtomNumber ); + t_group_info->nEndpointAtomNumber = NULL; + } + if ( t_group_info->tGroupNumber ) { + inchi_free ( t_group_info->tGroupNumber ); + t_group_info->tGroupNumber = NULL; + } + /* find max_t_group */ + for ( i = 0, max_t_group = 0; i < t_group_info->num_t_groups; i ++ ) { + if ( max_t_group < t_group[i].nGroupNumber ) + max_t_group = t_group[i].nGroupNumber; + } + /* allocate memory for temp storage of numbers of endpoints */ + if ( max_t_group && + !(nTautomerGroupNumber = (AT_NUMB*) inchi_calloc( max_t_group+1, sizeof(nTautomerGroupNumber[0]) ) /*temp*/ ) ) { + goto err_exit_function; /* program error: out of RAM */ /* */ + } + + /* count endpoints for each tautomer group */ + for ( i = 0, nNumEndpoints = 0; i < num_atoms; i ++ ) { + if ( (j = at[i].endpoint) == 0 ) + continue; + if ( j > max_t_group ) /* debug only */ + goto err_exit_function; /* program error */ /* */ + nTautomerGroupNumber[j] ++; + nNumEndpoints ++; + } + + if ( !nNumEndpoints ) { + goto exit_function; /* not a tautomer */ + } + + /* allocate temporary array */ + if ( !(nEndpointAtomNumber = (AT_NUMB*) inchi_calloc( nNumEndpoints, sizeof(nEndpointAtomNumber[0]) ) ) || + !(nCurrEndpointAtNoPos = (AT_NUMB*) inchi_calloc( num_t, sizeof(nCurrEndpointAtNoPos[0]) ) /*temp*/ ) ) { + goto err_exit_function; /* program error: out of RAM */ /* */ + } + /* + * Remove missing endpoints from t_group. Since only one + * disconnected part is processed, some endpoints groups may have disappeared. + * Mark t_groups containing charges only for subsequent removal + */ + for ( i = 0, nNewGroupNumber = 0; i < num_t; /*i ++*/ ) { + int bNoH = 0, nNumH; + nGroupNumber = t_group[i].nGroupNumber; + for ( j = 1, nNumH = t_group[i].num[0]; j < T_NUM_NO_ISOTOPIC; j ++ ) { + nNumH -= (int)t_group[i].num[j]; + } + if ( t_group[i].nNumEndpoints != nTautomerGroupNumber[(int)nGroupNumber] +#if ( IGNORE_TGROUP_WITHOUT_H == 1 ) + || (bNoH = (t_group[i].num[0]==t_group[i].num[1])) /* only for (H,-) t-groups; (+) t-groups are not removed */ +#endif + ) { + if ( !nTautomerGroupNumber[(int)nGroupNumber] || bNoH ) { + /* the group belongs to another disconnected part of the structure or has only charges */ + /* Remove the group */ + num_t --; + if ( i < num_t ) + memmove( t_group+i, t_group+i+1, (num_t-i)*sizeof(t_group[0]) ); + if ( bNoH ) { + /* group contains no mobile hydrogen atoms, only charges. Prepare to remove it. */ + nTautomerGroupNumber[(int)nGroupNumber] = 0; + num_groups_noH ++; + } + /*i --;*/ + } else { + /* different number of endpoints */ + goto err_exit_function; /* program error */ /* */ + } + } else { + /* renumber t_group and prepare to renumber at[i].endpoint */ + nTautomerGroupNumber[(int)nGroupNumber] = + t_group[i].nGroupNumber = ++nNewGroupNumber; /* = i+1 */ + /* get first group atom orig. number position in the nEndpointAtomNumber[] */ + /* and in the tautomer endpoint canon numbers part of the connection table */ + t_group[i].nFirstEndpointAtNoPos = nCurrEndpointAtNoPos[i] = + i? (t_group[i-1].nFirstEndpointAtNoPos+t_group[i-1].nNumEndpoints) : 0; + t_group[i].num[0] = nNumH; +#if ( REMOVE_TGROUP_CHARGE == 1 ) + t_group[i].num[1] = 0; /* remove only (-) charges */ +#endif + /* -- wrong condition. Disabled. + if ( t_group[i].nGroupNumber != i + 1 ) { // for debug only + goto err_exit_function; // program error + } + */ + i ++; + } + } + if ( num_t != nNewGroupNumber ) { /* for debug only */ + goto err_exit_function; /* program error */ /* */ + } + + /* check if any tautomer group was left */ + if ( !nNewGroupNumber ) { + if ( !num_groups_noH ) + goto err_exit_function; /* program error: not a tautomer */ /* */ + else + goto exit_function; + } + /* + * an array for tautomer group sorting later, at the time of storing Connection Table + * Later the sorting consists out of 2 steps: + * 1) Sort t_group[i].nNumEndpoints endpoint atom ranks within each endpoint group + * starting from t_group[i].nFirstEndpointAtNoPos; i = 0..t_group_info->num_t_groups-1 + * 2) Sort the groups indexes t_group_info->tGroupNumber[] + */ + if ( !(tGroupNumber= + (AT_NUMB*)inchi_calloc(nNewGroupNumber*TGSO_TOTAL_LEN, sizeof(tGroupNumber[0])))) { + goto err_exit_function; /* out of RAM */ + } + for ( i = 0; i < nNewGroupNumber; i ++ ) { + tGroupNumber[i] = (AT_NUMB)i; /* initialization: original t_group number = (at[i]->endpoint-1) */ + } + /* + * renumber endpoint atoms and save their orig. atom + * numbers for filling out the tautomer part of the LinearCT. + * nCurrEndpointAtNoPos[j] is an index of the atom number in the nEndpointAtomNumber[] + */ + for ( i = 0; i < num_atoms; i ++ ) { + if ( j = (int)at[i].endpoint ) { + j = (int)(at[i].endpoint = nTautomerGroupNumber[j])-1; /* new t_group number */ + if ( j >= 0 ) { /* j=-1 in case of no mobile hydrogen atoms (charges only), group being removed */ + if ( nCurrEndpointAtNoPos[j] >= /* debug only */ + t_group[j].nFirstEndpointAtNoPos+t_group[j].nNumEndpoints ) { + goto err_exit_function; /* program error */ /* */ + } + nEndpointAtomNumber[(int)nCurrEndpointAtNoPos[j] ++] = (AT_NUMB)i; + } else { + nNumEndpoints --; /* endpoint has been removed */ + } + } + } + t_group_info->num_t_groups = nNewGroupNumber; + t_group_info->nNumEndpoints = nNumEndpoints; + t_group_info->nEndpointAtomNumber = nEndpointAtomNumber; + t_group_info->tGroupNumber = tGroupNumber; /* only the 1st segment filled */ + inchi_free ( nTautomerGroupNumber ); + inchi_free ( nCurrEndpointAtNoPos ); + return nNumEndpoints + T_GROUP_HDR_LEN * nNewGroupNumber + 1; /* nLenLinearCTTautomer */ + +err_exit_function: + ret = CT_TAUCOUNT_ERR; +exit_function: + /* release allocated memory; set "no tautomeric group" */ + if ( nEndpointAtomNumber ) + inchi_free ( nEndpointAtomNumber ); + if ( nTautomerGroupNumber ) + inchi_free ( nTautomerGroupNumber ); + if ( tGroupNumber ) + inchi_free ( tGroupNumber ); + if ( nCurrEndpointAtNoPos ) + inchi_free ( nCurrEndpointAtNoPos ); + t_group_info->nNumEndpoints = 0; + t_group_info->num_t_groups = 0; + if ( !ret && ((t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) || + t_group_info->nNumIsotopicEndpoints>1 && (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE))) ) { + ret = 1; /* only protons have been (re)moved or neitralization happened */ + } + return ret; +} + + +#if ( READ_INCHI_STRING == 1 ) +#if ( INCLUDE_NORMALIZATION_ENTRY_POINT == 1 ) +/*****************************************************************************/ +int CountTautomerGroupsInpAt( inp_ATOM *at, int num_atoms, T_GROUP_INFO *t_group_info ) +{ + int i, j, ret = 0, nNumEndpoints, max_t_group, num_groups_noH; + + AT_NUMB nGroupNumber, nNewGroupNumber, *nCurrEndpointAtNoPos = NULL; + + T_GROUP *t_group; + int num_t; + /* int bIgnoreIsotopic, max_num_t; */ + AT_NUMB *nTautomerGroupNumber = NULL; + AT_NUMB *nEndpointAtomNumber = NULL; + AT_NUMB *tGroupNumber = NULL; + + if ( !t_group_info || !t_group_info->t_group || 0 >= t_group_info->max_num_t_groups ) { + return 0; /* empty t-groups */ + } + num_t = t_group_info->num_t_groups; + t_group = t_group_info->t_group; + /* + max_num_t = t_group_info->max_num_t_groups; + bIgnoreIsotopic = t_group_info->bIgnoreIsotopic; + */ + num_groups_noH = 0; + + /* the following 2 arrays are to be rebuilt here */ + if ( t_group_info->nEndpointAtomNumber ) { + inchi_free ( t_group_info->nEndpointAtomNumber ); + t_group_info->nEndpointAtomNumber = NULL; + } + if ( t_group_info->tGroupNumber ) { + inchi_free ( t_group_info->tGroupNumber ); + t_group_info->tGroupNumber = NULL; + } + /* find max_t_group */ + for ( i = 0, max_t_group = 0; i < t_group_info->num_t_groups; i ++ ) { + if ( max_t_group < t_group[i].nGroupNumber ) + max_t_group = t_group[i].nGroupNumber; + } + /* allocate memory for temp storage of numbers of endpoints */ + if ( max_t_group && + !(nTautomerGroupNumber = (AT_NUMB*) inchi_calloc( max_t_group+1, sizeof(nTautomerGroupNumber[0]) ) /*temp*/ ) ) { + goto err_exit_function; /* program error: out of RAM */ /* */ + } + + /* count endpoints for each tautomer group */ + for ( i = 0, nNumEndpoints = 0; i < num_atoms; i ++ ) { + if ( (j = at[i].endpoint) == 0 ) + continue; + if ( j > max_t_group ) /* debug only */ + goto err_exit_function; /* program error */ /* */ + nTautomerGroupNumber[j] ++; + nNumEndpoints ++; + } + + if ( !nNumEndpoints ) { + goto exit_function; /* not a tautomer */ + } + + /* allocate temporary array */ + if ( !(nEndpointAtomNumber = (AT_NUMB*) inchi_calloc( nNumEndpoints, sizeof(nEndpointAtomNumber[0]) ) ) || + !(nCurrEndpointAtNoPos = (AT_NUMB*) inchi_calloc( num_t, sizeof(nCurrEndpointAtNoPos[0]) ) /*temp*/ ) ) { + goto err_exit_function; /* program error: out of RAM */ /* */ + } + /* + * Remove missing endpoints from t_group. Since only one + * disconnected part is processed, some endpoints groups may have disappeared. + * Mark t_groups containing charges only for subsequent removal + */ + for ( i = 0, nNewGroupNumber = 0; i < num_t; /*i ++*/ ) { + int bNoH = 0, nNumH; + nGroupNumber = t_group[i].nGroupNumber; + for ( j = 1, nNumH = t_group[i].num[0]; j < T_NUM_NO_ISOTOPIC; j ++ ) { + nNumH -= (int)t_group[i].num[j]; + } + if ( t_group[i].nNumEndpoints != nTautomerGroupNumber[(int)nGroupNumber] +#if ( IGNORE_TGROUP_WITHOUT_H == 1 ) + || (bNoH = (t_group[i].num[0]==t_group[i].num[1])) /* only for (H,-) t-groups; (+) t-groups are not removed */ +#endif + ) { + if ( !nTautomerGroupNumber[(int)nGroupNumber] || bNoH ) { + /* the group belongs to another disconnected part of the structure or has only charges */ + /* Remove the group */ + num_t --; + if ( i < num_t ) + memmove( t_group+i, t_group+i+1, (num_t-i)*sizeof(t_group[0]) ); + if ( bNoH ) { + /* group contains no mobile hydrogen atoms, only charges. Prepare to remove it. */ + nTautomerGroupNumber[(int)nGroupNumber] = 0; + num_groups_noH ++; + } + /*i --;*/ + } else { + /* different number of endpoints */ + goto err_exit_function; /* program error */ /* */ + } + } else { + /* renumber t_group and prepare to renumber at[i].endpoint */ + nTautomerGroupNumber[(int)nGroupNumber] = + t_group[i].nGroupNumber = ++nNewGroupNumber; /* = i+1 */ + /* get first group atom orig. number position in the nEndpointAtomNumber[] */ + /* and in the tautomer endpoint canon numbers part of the connection table */ + t_group[i].nFirstEndpointAtNoPos = nCurrEndpointAtNoPos[i] = + i? (t_group[i-1].nFirstEndpointAtNoPos+t_group[i-1].nNumEndpoints) : 0; + t_group[i].num[0] = nNumH; +#if ( REMOVE_TGROUP_CHARGE == 1 ) + t_group[i].num[1] = 0; /* remove only (-) charges */ +#endif + /* -- wrong condition. Disabled. + if ( t_group[i].nGroupNumber != i + 1 ) { // for debug only + goto err_exit_function; // program error + } + */ + i ++; + } + } + if ( num_t != nNewGroupNumber ) { /* for debug only */ + goto err_exit_function; /* program error */ /* */ + } + + /* check if any tautomer group was left */ + if ( !nNewGroupNumber ) { + if ( !num_groups_noH ) + goto err_exit_function; /* program error: not a tautomer */ /* */ + else + goto exit_function; + } + /* + * an array for tautomer group sorting later, at the time of storing Connection Table + * Later the sorting consists out of 2 steps: + * 1) Sort t_group[i].nNumEndpoints endpoint atom ranks within each endpoint group + * starting from t_group[i].nFirstEndpointAtNoPos; i = 0..t_group_info->num_t_groups-1 + * 2) Sort the groups indexes t_group_info->tGroupNumber[] + */ + if ( !(tGroupNumber= + (AT_NUMB*)inchi_calloc(nNewGroupNumber*TGSO_TOTAL_LEN, sizeof(tGroupNumber[0])))) { + goto err_exit_function; /* out of RAM */ + } + for ( i = 0; i < nNewGroupNumber; i ++ ) { + tGroupNumber[i] = (AT_NUMB)i; /* initialization: original t_group number = (at[i]->endpoint-1) */ + } + /* + * renumber endpoint atoms and save their orig. atom + * numbers for filling out the tautomer part of the LinearCT. + * nCurrEndpointAtNoPos[j] is an index of the atom number in the nEndpointAtomNumber[] + */ + for ( i = 0; i < num_atoms; i ++ ) { + if ( j = (int)at[i].endpoint ) { + j = (int)(at[i].endpoint = nTautomerGroupNumber[j])-1; /* new t_group number */ + if ( j >= 0 ) { /* j=-1 in case of no mobile hydrogen atoms (charges only), group being removed */ + if ( nCurrEndpointAtNoPos[j] >= /* debug only */ + t_group[j].nFirstEndpointAtNoPos+t_group[j].nNumEndpoints ) { + goto err_exit_function; /* program error */ /* */ + } + nEndpointAtomNumber[(int)nCurrEndpointAtNoPos[j] ++] = (AT_NUMB)i; + } else { + nNumEndpoints --; /* endpoint has been removed */ + } + } + } + t_group_info->num_t_groups = nNewGroupNumber; + t_group_info->nNumEndpoints = nNumEndpoints; + t_group_info->nEndpointAtomNumber = nEndpointAtomNumber; + t_group_info->tGroupNumber = tGroupNumber; /* only the 1st segment filled */ + inchi_free ( nTautomerGroupNumber ); + inchi_free ( nCurrEndpointAtNoPos ); + return nNumEndpoints + T_GROUP_HDR_LEN * nNewGroupNumber + 1; /* nLenLinearCTTautomer */ + +err_exit_function: + ret = CT_TAUCOUNT_ERR; +exit_function: + /* release allocated memory; set "no tautomeric group" */ + if ( nEndpointAtomNumber ) + inchi_free ( nEndpointAtomNumber ); + if ( nTautomerGroupNumber ) + inchi_free ( nTautomerGroupNumber ); + if ( tGroupNumber ) + inchi_free ( tGroupNumber ); + if ( nCurrEndpointAtNoPos ) + inchi_free ( nCurrEndpointAtNoPos ); + t_group_info->nNumEndpoints = 0; + t_group_info->num_t_groups = 0; + if ( !ret && ((t_group_info->tni.bNormalizationFlags & FLAG_NORM_CONSIDER_TAUT) || + t_group_info->nNumIsotopicEndpoints>1 && (t_group_info->bTautFlagsDone & (TG_FLAG_FOUND_ISOTOPIC_H_DONE | TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE))) ) { + ret = 1; /* only protons have been (re)moved or neitralization happened */ + } + return ret; +} +#endif +#endif + + +/* + Tautomers: Compare for sorting + + Compare for sorting Ranks only + Globals: pn_tRankForSort +*/ +int CompRankTautomer(const void* a1, const void* a2, void *p ) +{ + AT_RANK *pn_tRankForSort=(AT_RANK *) p; + int ret = (int)pn_tRankForSort[(int)(*(const AT_RANK*)a1)] - + (int)pn_tRankForSort[(int)(*(const AT_RANK*)a2)]; + return ret; +} + + +int SortTautomerGroupsAndEndpoints( CANON_GLOBALS *pCG, T_GROUP_INFO *t_group_info, int num_atoms, int num_at_tg, AT_RANK *nRank ) +{ + int i, nFirstEndpointAtNoPos, nNumEndpoints; + AT_RANK *pn_tRankForSort; + AT_NUMB *nEndpointAtomNumber; + int num_t_groups = num_at_tg - num_atoms; + T_GROUP *t_group = NULL; + /* check if sorting is required */ + + if ( num_t_groups <= 0 || t_group_info->nNumEndpoints < 2 ) { + return 0; /* no tautomer data */ + } + t_group = t_group_info->t_group; + /* sort endpoints within the groups */ + for ( i = 0; i < num_t_groups; i ++ ) { + if ( t_group[i].nNumEndpoints < 2 ) + continue; /* program error; should not happen */ /* */ + /* set globals for sorting */ + nFirstEndpointAtNoPos = t_group[i].nFirstEndpointAtNoPos; + nNumEndpoints = t_group[i].nNumEndpoints; + if ( nNumEndpoints + nFirstEndpointAtNoPos > t_group_info->nNumEndpoints ) { /* for debug only */ + return CT_TAUCOUNT_ERR; /* program error */ /* */ + } + nEndpointAtomNumber = t_group_info->nEndpointAtomNumber+(int)nFirstEndpointAtNoPos; + pn_tRankForSort = nRank; + insertions_sort( pn_tRankForSort, nEndpointAtomNumber, nNumEndpoints, sizeof(nEndpointAtomNumber[0]), CompRankTautomer); + } + /* sort the tautomeric groups according to their ranks only + (that is, ignoring the isotopic composition of the mobile groups and ranks of the endpoints) */ + if ( t_group_info->num_t_groups > 1 ) { + /* set globals for sorting */ + /* a hack: the ranks of all tautomeric groups are */ + /* located at nRank[num_atoms..num_at_tg-1] */ + pn_tRankForSort = nRank+num_atoms; + /* sort */ + /* ordering numbers to sort : t_group_info->tGroupNumber; */ + insertions_sort( pn_tRankForSort, t_group_info->tGroupNumber, num_t_groups, + sizeof(t_group_info->tGroupNumber[0]), CompRankTautomer); + } + + return t_group_info->num_t_groups; +} diff --git a/INCHI-1-SRC/INCHI/common/ichitaut.h b/INCHI-1-SRC/INCHI_BASE/src/ichitaut.h similarity index 76% rename from INCHI-1-SRC/INCHI/common/ichitaut.h rename to INCHI-1-SRC/INCHI_BASE/src/ichitaut.h index 3199022..5025a88 100644 --- a/INCHI-1-SRC/INCHI/common/ichitaut.h +++ b/INCHI-1-SRC/INCHI_BASE/src/ichitaut.h @@ -1,444 +1,486 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHITAUT_H__ -#define __INCHITAUT_H__ - -#include "inpdef.h" -#include "ichi_bns.h" - - -/******************************************************* - --- Header of tautomers groups --- - --- Each entry is AT_TAUTOMER_HDR type --- - number of tautomer groups (nNumTautGroups) - index of the first tautomer group (#1) - ... - index of the last tautomer group (#nNumTautGroups) - --- end of the Header of tautomers groups description --- - - --- One endpoint group description --- - --- Each entry has AT_TAUTOMER type members --- - - number of endpoints (0=end of list) - number of mobile groups, including number of negative charges (=num(H)+num(-)) - number of negative charges - number of 1H atoms - number of 2H (deuterium) atoms - number of 3H (tritium) atoms - - atom rank #1 (ascending order) - ... - atom rank #endpoints - --- end of the endpoint group description ---- - - ---------------------------------------------- - Note: - In the following Linear CT Tautomer descriptions - we assume the tautomeric groups and the endpoints - within them have been properly sorted - - --------- Linear CT Tautomer description ----- - - -- fixed length part, non-isotopic -- - number of endpoints = t_group->nNumEndpoints - number of mobile atoms = t_group->num[0] - ... - number of negative charges = t_group->num[T_NUM_NO_ISOTOPIC-1] - -- fixed length part, isotopic -- - number of T (3H) = t_group->num[T_NUM_NO_ISOTOPIC] - ... - number of 1H = t_group->num[T_NUM_NO_ISOTOPIC+T_NUM_ISOTOPIC-1] - -- variable length part -- - rank of the first endpoint = nRank[t_group_info->nEndpointAtomNumber[t_group->nFirstEndpointAtNoPos]]; - ... - rank of the last endpoint = nRank[t_group_info->nEndpointAtomNumber[t_group->nFirstEndpointAtNoPos+t_group->nNumEndpoints-1]]; - - --------- Linear CT Isotopic Tautomer description ----- - - number of T (3H) = t_group->num[T_NUM_NO_ISOTOPIC] - ... - number of 1H = t_group->num[T_NUM_NO_ISOTOPIC+T_NUM_ISOTOPIC-1] - t-group ordering number in the Linear CT Tautomer, starts from 1 - -***************************************************************/ - - -#define T_NUM_NO_ISOTOPIC 2 -#define T_NUM_ISOTOPIC NUM_H_ISOTOPES /* was 2, now 3 */ - -#define T_GROUP_HDR_LEN (1+T_NUM_NO_ISOTOPIC /*+T_NUM_ISOTOPIC*/) /* LinearCTTautomer */ - -typedef AT_NUMB AT_TAUTOMER; /* LinearCTTautomer */ - -typedef AT_ISO_SORT_KEY T_GROUP_ISOWT; /* must hold value up to T_GROUP_ISOWT_MULT^3-1 */ - /* similar to AT_ISO_SORT_KEY */ -/* = num_1H + T_GROUP_ISOWT_MULT*(num_D + T_GROUP_ISOWT_MULT*num_T) */ - - -#define T_GROUP_ISOWT_MULT 1024 /* (max. number of identical isotopic hydrogens in a taut. group) + 1 */ - /* changed from 256U 9-12-2003 */ - /* (similar to AT_ISO_SORT_KEY_MULT ) */ -/* note: (long)T_GROUP_ISOWT should always be positive (have zero sign bit) */ - -typedef struct tagIsotopicTautomerGroup { - AT_NUMB tgroup_num; /* ordering number of a tautomer group with isotopes > 0 */ - /* - union { - struct { - AT_NUMB num_T; - AT_NUMB num_D; - AT_NUMB num_1H; - }; - AT_NUMB num[T_NUM_ISOTOPIC]; - }; - */ - AT_NUMB num[T_NUM_ISOTOPIC]; /* inverted order: num_T, num_D, num_1H */ -} AT_ISO_TGROUP; - -typedef enum tagTG_NumDA { /* 2004-02-26 */ - TG_Num_dH, /* number of H donors that have only H (all single bonds) */ - TG_Num_dM, /* number of H donors that have (-) (all single bonds) */ - TG_Num_aH, /* number of H acceptors that have H and no (-) (+a double bond) */ - TG_Num_aM, /* number of H acceptors that have (-) and possibly H (+ one double bond) */ - TG_Num_dO, /* number of H donors =C-OH or =C-O(-) */ - TG_Num_aO, /* number of H acceptors -C=O */ - TG_NUM_DA /* number of elements in an array */ -} TGNUMDA; - -typedef struct tagTautomerGroup { - /* - union { - struct { - // T_NUM_NO_ISOTOPIC = 2 elements: - AT_RANK num_Mobile; // Num_H+num_D+num_T+num_NegCharges - AT_RANK num_NegCharges; - // T_NUM_ISOTOPIC = 3 elements - AT_RANK num_T; // here the isotopic part (num+T_NUM_NO_ISOTOPIC) starts - AT_RANK num_D; - AT_RANK num_1H; - }; - AT_RANK num[T_NUM_NO_ISOTOPIC+T_NUM_ISOTOPIC]; // same size and meaning as num[] in T_ENDPOINT - }; - */ - AT_RANK num[T_NUM_NO_ISOTOPIC+T_NUM_ISOTOPIC]; /* same size and meaning as num[] in T_ENDPOINT */ - /* isotopic inv. order: num_T, num_D, num_1H */ - AT_RANK num_DA[TG_NUM_DA]; - T_GROUP_ISOWT iWeight; /* isotopic "weight" = T_GROUP_ISOWT_MULT*(T_GROUP_ISOWT_MULT*num_T + num_D)+num_1H; */ - AT_NUMB nGroupNumber; /* positive tautomer group ID = atom->endpoint */ - AT_NUMB nNumEndpoints; /* number of the atom numbers in T_GROUP_INFO::nEndpointAtomNumber[] */ - AT_NUMB nFirstEndpointAtNoPos; /* the first index of the atom number in T_GROUP_INFO::nEndpointAtomNumber[] */ -} T_GROUP; - -/* offsets/num_t_groups within T_GROUP_INFO::tGroupNumber */ -#define TGSO_CURR_ORDER 0 /* tGroupNumber: current sorting order */ -#define TGSO_SYMM_RANK 1 /* tSymmRank: symmetry ranks (no isotopes) = min. ordering number > 0. */ -#define TGSO_SYMM_IORDER 2 /* tiGroupNumber: isotopic symmetry rank sorting order */ -#define TGSO_SYMM_IRANK 3 /* tiSymmRank: isotopic symmetry ranks */ -#define TGSO_TOTAL_LEN 4 - -/***************************************************/ -/* flags for t_group_info->tni.bNormalizationFlags */ -/***************************************************/ -#define FLAG_PROTON_NPO_SIMPLE_REMOVED 0x0001 -#define FLAG_PROTON_NP_HARD_REMOVED 0x0002 -#define FLAG_PROTON_AC_SIMPLE_ADDED 0x0004 -#define FLAG_PROTON_AC_SIMPLE_REMOVED 0x0008 -#define FLAG_PROTON_AC_HARD_REMOVED 0x0010 -#define FLAG_PROTON_AC_HARD_ADDED 0x0020 -#define FLAG_PROTON_CHARGE_CANCEL 0x0040 -#define FLAG_PROTON_SINGLE_REMOVED 0x0080 - -/* signifies tautomeric structure even though no t-group discovered */ -#define FLAG_NORM_CONSIDER_TAUT ( FLAG_PROTON_NPO_SIMPLE_REMOVED | \ - FLAG_PROTON_NP_HARD_REMOVED | \ - FLAG_PROTON_AC_SIMPLE_ADDED | \ - FLAG_PROTON_AC_SIMPLE_REMOVED | \ - FLAG_PROTON_AC_HARD_REMOVED | \ - FLAG_PROTON_AC_HARD_ADDED | \ - FLAG_PROTON_SINGLE_REMOVED | \ - FLAG_PROTON_CHARGE_CANCEL ) - -#if ( FIX_N_MINUS_NORN_BUG == 1 ) -#define FLAG_FORCE_SALT_TAUT ( FLAG_PROTON_NP_HARD_REMOVED | \ - FLAG_PROTON_AC_HARD_REMOVED | \ - FLAG_PROTON_AC_HARD_ADDED | \ - FLAG_PROTON_CHARGE_CANCEL ) -#else -/* force salt tautomerism exploration */ -#define FLAG_FORCE_SALT_TAUT ( FLAG_PROTON_NP_HARD_REMOVED | \ - FLAG_PROTON_AC_HARD_REMOVED | \ - FLAG_PROTON_AC_HARD_ADDED ) -#endif - -typedef struct tagTautomerNormInfo { - NUM_H nNumRemovedExplicitH; /* keeps track of explicit H */ - NUM_H nNumRemovedProtons; - NUM_H nNumRemovedProtonsIsotopic[NUM_H_ISOTOPES]; - INCHI_MODE bNormalizationFlags; -} TNI; - -/***************************************************/ -/* t_group_info definition */ -/***************************************************/ -typedef struct tagTautomerGroupsInfo { - T_GROUP *t_group; /* max_num_t_groups elements */ - AT_NUMB *nEndpointAtomNumber; /* nNumEndpoints elements; also see comments to T_GROUP */ - AT_NUMB *tGroupNumber; - int nNumEndpoints; - int num_t_groups; - int max_num_t_groups; - int bIgnoreIsotopic; - - AT_NUMB *nIsotopicEndpointAtomNumber; /* [0]: number of the following atoms; [1...]: non-tautomeric atoms that may have isotopic H */ - int nNumIsotopicEndpoints; /* allocated length of nIsotopicEndpointAtomNumber */ - NUM_H num_iso_H[NUM_H_ISOTOPES]; /* isotopic H on tautomeric atoms and those in nIsotopicEndpointAtomNumber */ - - TNI tni; - - INCHI_MODE bTautFlags; - INCHI_MODE bTautFlagsDone; -} T_GROUP_INFO; - -#define CANON_FLAG_NO_H_RECANON 0x0001 /* iOther: second canonicalization of the no H structure */ -#define CANON_FLAG_NO_TAUT_H_DIFF 0x0002 /* iOther: NoTautH eq. partition differs from NoH */ -#define CANON_FLAG_ISO_ONLY_NON_TAUT_DIFF 0x0004 /* iOther: eq. partition in isotopic only non-taut differs from non-isotopic */ -#define CANON_FLAG_ISO_TAUT_DIFF 0x0008 /* iBase: isotopic eq. partition in isotopic taut differs from non-isotopic taut */ -#define CANON_FLAG_ISO_FIXED_H_DIFF 0x0010 /* iOther: isotopic eq. partition in fixed H non-taut differs from non-isotopic fixed H */ - -/* Note: rank of tautomer atom #i = Rank[nEndpointAtomNumber[i]] */ -/* for each tautomer atom group (t_group) t_group.nFirstEndpointAtNoPos */ -/* is the first index of the atom number in nEndpointAtomNumber[] */ - -typedef struct tagTautomerEndpoint { - /* - union { - struct { - AT_RANK num_Mobile; // Num_H+num_D+num_T+num_NegCharges - AT_RANK num_NegCharges; - AT_RANK num_T; - AT_RANK num_D; - }; - AT_RANK num[T_NUM_NO_ISOTOPIC+T_NUM_ISOTOPIC]; // same size and meaning as num[] in T_GROUP - }; - */ - AT_RANK num[T_NUM_NO_ISOTOPIC+T_NUM_ISOTOPIC]; /* same size and meaning as num[] in T_GROUP */ - AT_RANK num_DA[TG_NUM_DA]; - AT_NUMB nGroupNumber; - AT_NUMB nEquNumber; /* same for endpoints connected by alt paths */ - AT_NUMB nAtomNumber; - /*AT_NUMB neighbor_index; */ -} T_ENDPOINT; - -typedef struct tagTautomerBondLocation { - AT_NUMB nAtomNumber; - AT_NUMB neighbor_index; -} T_BONDPOS; - -typedef struct tagEndpointInfo { - S_CHAR cMoveableCharge; - S_CHAR cNeutralBondsValence; - S_CHAR cMobile; - S_CHAR cDonor; - S_CHAR cAcceptor; - S_CHAR cKetoEnolCode; /* 1 => carbon, 2 => oxygen */ /* post v.1 feature */ -} ENDPOINT_INFO; - - -/* positive charge group (extended onium) */ - -#define CHARGED_CPOINT(X,i) ((X)[i].charge==1) - -typedef struct tagChargeCandidate { - AT_NUMB atnumber; - S_CHAR type; - S_CHAR subtype; -} C_CANDIDATE; - -typedef struct tagChargeGroup { - AT_RANK num[2]; /* [0]: number of (+), [1]: number atoms that have H, including H accessible through tautomerism */ - AT_RANK num_CPoints; - AT_NUMB nGroupNumber; - U_CHAR cGroupType; -} C_GROUP; - -typedef struct tagChargeGroupsInfo { - C_GROUP *c_group; - int num_c_groups; - int max_num_c_groups; - - C_CANDIDATE *c_candidate; - int max_num_candidates; - int num_candidates; /* 0=>unimitialized, -1=>no candidates found */ -} C_GROUP_INFO; - -/* salts */ -typedef struct tagSaltChargeCandidate { - AT_NUMB atnumber; - S_CHAR type; - S_CHAR subtype; - AT_NUMB endpoint; /* MAX_ATOMS+1 => found alt path to the candidate */ -} S_CANDIDATE; - -typedef struct tagSaltGroupInfo { - S_CANDIDATE *s_candidate; - int max_num_candidates; - int num_candidates; /* 0=>unimitialized, -1=>no candidates found */ - int num_other_candidates; /* num. non-"acidic O" candidates */ - int num_p_only_candidates; /* num. non-tautomeric p-donor/acceptor candidates like -CH2-SH */ -} S_GROUP_INFO; - -/********************* ATOM_SIZES *******************************/ -/* sizes of a component */ -typedef struct tagAtomSizes { - /* for tautomeric and non-tautomeric structures */ - int nMaxNumStereoAtoms; /* max. number of stereo atoms in isotopic case */ - int nMaxNumStereoBonds; /* max. number of stereo bonds in isotopic case */ - int num_isotopic_atoms; /* includes atoms that have isotopic tautomeric H */ - int nLenCT; - int nLenBonds; - int nLenIsotopic; - int nLenCTAtOnly; - int nLenLinearCTStereoDble; /* max. number of stereo bonds in non-isotopic case */ - int nLenLinearCTStereoCarb; /* max. number of stereo atoms in non-isotopic case */ - /* int bHasIsotopicAtoms; */ - int bMayHaveStereo; - - int bIgnoreIsotopic; - - /* tautomeric structure only; zeroes in non-tautomeric */ - int nLenLinearCTTautomer; - int nLenLinearCTIsotopicTautomer; - int bHasIsotopicTautGroups; - int nLenIsotopicEndpoints; - -} ATOM_SIZES; - - -typedef struct tagDfsPath { - AT_RANK at_no; - /*AT_RANK nDfsLevel;*/ - U_CHAR bond_type; - S_CHAR bond_pos; -} DFS_PATH; - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -int is_centerpoint_elem( U_CHAR el_number ); -int is_centerpoint_elem_strict( U_CHAR el_number ); -#if ( KETO_ENOL_TAUT == 1 ) -int is_centerpoint_elem_KET( U_CHAR el_number ); -#endif -int bIsCenterPointStrict( inp_ATOM *atom, int iat ); - -int nGetEndpointInfo( inp_ATOM *atom, int iat, ENDPOINT_INFO *eif ); -#if ( KETO_ENOL_TAUT == 1 ) -int nGetEndpointInfo_KET( inp_ATOM *atom, int iat, ENDPOINT_INFO *eif ); -#endif -void AddAtom2DA( AT_RANK num_DA[], inp_ATOM *atom, int at_no, int bSubtract ); -int AddAtom2num( AT_RANK num[], inp_ATOM *atom, int at_no, int bSubtract ); -int AddEndPoint( T_ENDPOINT *pEndPoint, inp_ATOM *at, int iat ); -int bHasAcidicHydrogen( inp_ATOM *at, int i ); -int bHasOtherExchangableH ( inp_ATOM *at, int i ); -int bHasAcidicMinus( inp_ATOM *at, int i ); - - -int nGet15TautIn6MembAltRing( inp_ATOM *atom, int nStartAtom, AT_RANK *nBfsTreePos, - DFS_PATH *DfsPath, int nMaxLenBfsTree, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ); - -int nGet12TautIn5MembAltRing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, - AT_RANK *nBfsTreePos, DFS_PATH *DfsPath, int nMaxLenBfsTree, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ); -int nGet14TautIn7MembAltRing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, - int nStartAtomNeighborEndpoint, int nStartAtomNeighborNeighborEndpoint, - AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, int nMaxLenDfsPath, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ); -int nGet14TautIn5MembAltRing( inp_ATOM *atom, int nStartAtom, int nStartAtomNeighbor, - int nStartAtomNeighborEndpoint, int nStartAtomNeighborNeighborEndpoint, - AT_RANK *nDfsPathPos, DFS_PATH *DfsPath, int nMaxLenDfsPath, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ); - -int nGet15TautInAltPath( inp_ATOM *atom, int nStartAtom, AT_RANK *nDfsPathPos, - DFS_PATH *DfsPath, int nMaxLenDfsPath, - T_ENDPOINT *EndPoint, int nMaxNumEndPoint, - T_BONDPOS *BondPos, int nMaxNumBondPos, - int *pnNumEndPoint, int *pnNumBondPos, - struct BalancedNetworkStructure *pBNS, - struct BalancedNetworkData *pBD, int num_atoms ); - - -#if ( RING2CHAIN == 1 ) -int Ring2Chain( ORIG_ATOM_DATA *orig_inp_data ); -#endif - -#if ( UNDERIVATIZE == 1 ) -int underivatize( ORIG_ATOM_DATA *orig_inp_data ); -#endif - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -#endif /* __INCHITAUT_H__ */ - +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _ICHITAUT_H_ +#define _ICHITAUT_H_ + +#include "ichi_bns.h" +#include "extr_ct.h" + +/******************************************************* + --- Header of tautomers groups --- + --- Each entry is AT_TAUTOMER_HDR type --- + number of tautomer groups (nNumTautGroups) + index of the first tautomer group (#1) + ... + index of the last tautomer group (#nNumTautGroups) + --- end of the Header of tautomers groups description --- + + --- One endpoint group description --- + --- Each entry has AT_TAUTOMER type members --- + + number of endpoints (0=end of list) + number of mobile groups, including number of negative charges (=num(H)+num(-)) + number of negative charges + number of 1H atoms + number of 2H (deuterium) atoms + number of 3H (tritium) atoms + + atom rank #1 (ascending order) + ... + atom rank #endpoints + --- end of the endpoint group description ---- + + ---------------------------------------------- + Note: + In the following Linear CT Tautomer descriptions + we assume the tautomeric groups and the endpoints + within them have been properly sorted + + --------- Linear CT Tautomer description ----- + + -- fixed length part, non-isotopic -- + number of endpoints = t_group->nNumEndpoints + number of mobile atoms = t_group->num[0] + ... + number of negative charges = t_group->num[T_NUM_NO_ISOTOPIC-1] + -- fixed length part, isotopic -- + number of T (3H) = t_group->num[T_NUM_NO_ISOTOPIC] + ... + number of 1H = t_group->num[T_NUM_NO_ISOTOPIC+T_NUM_ISOTOPIC-1] + -- variable length part -- + rank of the first endpoint = nRank[t_group_info->nEndpointAtomNumber[t_group->nFirstEndpointAtNoPos]]; + ... + rank of the last endpoint = nRank[t_group_info->nEndpointAtomNumber[t_group->nFirstEndpointAtNoPos+t_group->nNumEndpoints-1]]; + + --------- Linear CT Isotopic Tautomer description ----- + + number of T (3H) = t_group->num[T_NUM_NO_ISOTOPIC] + ... + number of 1H = t_group->num[T_NUM_NO_ISOTOPIC+T_NUM_ISOTOPIC-1] + t-group ordering number in the Linear CT Tautomer, starts from 1 + +***************************************************************/ + + +#define T_NUM_NO_ISOTOPIC 2 +#define T_NUM_ISOTOPIC NUM_H_ISOTOPES /* was 2, now 3 */ + +#define T_GROUP_HDR_LEN (1+T_NUM_NO_ISOTOPIC /*+T_NUM_ISOTOPIC*/) /* LinearCTTautomer */ + +typedef AT_NUMB AT_TAUTOMER; /* LinearCTTautomer */ + +typedef AT_ISO_SORT_KEY T_GROUP_ISOWT; /* must hold value up to T_GROUP_ISOWT_MULT^3-1 */ + /* similar to AT_ISO_SORT_KEY */ +/* = num_1H + T_GROUP_ISOWT_MULT*(num_D + T_GROUP_ISOWT_MULT*num_T) */ + + +#define T_GROUP_ISOWT_MULT 1024 /* (max. number of identical isotopic hydrogens in a taut. group) + 1 */ + /* changed from 256U 9-12-2003 */ + /* (similar to AT_ISO_SORT_KEY_MULT ) */ +/* note: (long)T_GROUP_ISOWT should always be positive (have zero sign bit) */ + +typedef struct tagIsotopicTautomerGroup { + AT_NUMB tgroup_num; /* ordering number of a tautomer group with isotopes > 0 */ + /* + union { + struct { + AT_NUMB num_T; + AT_NUMB num_D; + AT_NUMB num_1H; + }; + AT_NUMB num[T_NUM_ISOTOPIC]; + }; + */ + AT_NUMB num[T_NUM_ISOTOPIC]; /* inverted order: num_T, num_D, num_1H */ +} AT_ISO_TGROUP; + +typedef enum tagTG_NumDA { /* 2004-02-26 */ + TG_Num_dH, /* number of H donors that have only H (all single bonds) */ + TG_Num_dM, /* number of H donors that have (-) (all single bonds) */ + TG_Num_aH, /* number of H acceptors that have H and no (-) (+a double bond) */ + TG_Num_aM, /* number of H acceptors that have (-) and possibly H (+ one double bond) */ + TG_Num_dO, /* number of H donors =C-OH or =C-O(-) */ + TG_Num_aO, /* number of H acceptors -C=O */ + TG_NUM_DA /* number of elements in an array */ +} TGNUMDA; + +typedef struct tagTautomerGroup { + /* + union { + struct { + // T_NUM_NO_ISOTOPIC = 2 elements: + AT_RANK num_Mobile; // Num_H+num_D+num_T+num_NegCharges + AT_RANK num_NegCharges; + // T_NUM_ISOTOPIC = 3 elements + AT_RANK num_T; // here the isotopic part (num+T_NUM_NO_ISOTOPIC) starts + AT_RANK num_D; + AT_RANK num_1H; + }; + AT_RANK num[T_NUM_NO_ISOTOPIC+T_NUM_ISOTOPIC]; // same size and meaning as num[] in T_ENDPOINT + }; + */ + AT_RANK num[T_NUM_NO_ISOTOPIC+T_NUM_ISOTOPIC]; /* same size and meaning as num[] in T_ENDPOINT */ + /* isotopic inv. order: num_T, num_D, num_1H */ + AT_RANK num_DA[TG_NUM_DA]; + T_GROUP_ISOWT iWeight; /* isotopic "weight" = T_GROUP_ISOWT_MULT*(T_GROUP_ISOWT_MULT*num_T + num_D)+num_1H; */ + AT_NUMB nGroupNumber; /* positive tautomer group ID = atom->endpoint */ + AT_NUMB nNumEndpoints; /* number of the atom numbers in T_GROUP_INFO::nEndpointAtomNumber[] */ + AT_NUMB nFirstEndpointAtNoPos; /* the first index of the atom number in T_GROUP_INFO::nEndpointAtomNumber[] */ +} T_GROUP; + +/* offsets/num_t_groups within T_GROUP_INFO::tGroupNumber */ +#define TGSO_CURR_ORDER 0 /* tGroupNumber: current sorting order */ +#define TGSO_SYMM_RANK 1 /* tSymmRank: symmetry ranks (no isotopes) = min. ordering number > 0. */ +#define TGSO_SYMM_IORDER 2 /* tiGroupNumber: isotopic symmetry rank sorting order */ +#define TGSO_SYMM_IRANK 3 /* tiSymmRank: isotopic symmetry ranks */ +#define TGSO_TOTAL_LEN 4 + +/***************************************************/ +/* flags for t_group_info->tni.bNormalizationFlags */ +/***************************************************/ +#define FLAG_PROTON_NPO_SIMPLE_REMOVED 0x0001 +#define FLAG_PROTON_NP_HARD_REMOVED 0x0002 +#define FLAG_PROTON_AC_SIMPLE_ADDED 0x0004 +#define FLAG_PROTON_AC_SIMPLE_REMOVED 0x0008 +#define FLAG_PROTON_AC_HARD_REMOVED 0x0010 +#define FLAG_PROTON_AC_HARD_ADDED 0x0020 +#define FLAG_PROTON_CHARGE_CANCEL 0x0040 +#define FLAG_PROTON_SINGLE_REMOVED 0x0080 + +/* signifies tautomeric structure even though no t-group discovered */ +#define FLAG_NORM_CONSIDER_TAUT ( FLAG_PROTON_NPO_SIMPLE_REMOVED | \ + FLAG_PROTON_NP_HARD_REMOVED | \ + FLAG_PROTON_AC_SIMPLE_ADDED | \ + FLAG_PROTON_AC_SIMPLE_REMOVED | \ + FLAG_PROTON_AC_HARD_REMOVED | \ + FLAG_PROTON_AC_HARD_ADDED | \ + FLAG_PROTON_SINGLE_REMOVED | \ + FLAG_PROTON_CHARGE_CANCEL ) + +#if ( FIX_N_MINUS_NORN_BUG == 1 ) +#define FLAG_FORCE_SALT_TAUT ( FLAG_PROTON_NP_HARD_REMOVED | \ + FLAG_PROTON_AC_HARD_REMOVED | \ + FLAG_PROTON_AC_HARD_ADDED | \ + FLAG_PROTON_CHARGE_CANCEL ) +#else +/* force salt tautomerism exploration */ +#define FLAG_FORCE_SALT_TAUT ( FLAG_PROTON_NP_HARD_REMOVED | \ + FLAG_PROTON_AC_HARD_REMOVED | \ + FLAG_PROTON_AC_HARD_ADDED ) +#endif + +typedef struct tagTautomerNormInfo { + NUM_H nNumRemovedExplicitH; /* keeps track of explicit H */ + NUM_H nNumRemovedProtons; + NUM_H nNumRemovedProtonsIsotopic[NUM_H_ISOTOPES]; + INCHI_MODE bNormalizationFlags; +} TNI; + +/***************************************************/ +/* t_group_info definition */ +/***************************************************/ +typedef struct tagTautomerGroupsInfo { + T_GROUP *t_group; /* max_num_t_groups elements */ + AT_NUMB *nEndpointAtomNumber; /* nNumEndpoints elements; also see comments to T_GROUP */ + AT_NUMB *tGroupNumber; + int nNumEndpoints; + int num_t_groups; + int max_num_t_groups; + int bIgnoreIsotopic; + + AT_NUMB *nIsotopicEndpointAtomNumber; /* [0]: number of the following atoms; [1...]: non-tautomeric atoms that may have isotopic H */ + int nNumIsotopicEndpoints; /* allocated length of nIsotopicEndpointAtomNumber */ + NUM_H num_iso_H[NUM_H_ISOTOPES]; /* isotopic H on tautomeric atoms and those in nIsotopicEndpointAtomNumber */ + + TNI tni; + + INCHI_MODE bTautFlags; + INCHI_MODE bTautFlagsDone; +} T_GROUP_INFO; + +#define CANON_FLAG_NO_H_RECANON 0x0001 /* iOther: second canonicalization of the no H structure */ +#define CANON_FLAG_NO_TAUT_H_DIFF 0x0002 /* iOther: NoTautH eq. partition differs from NoH */ +#define CANON_FLAG_ISO_ONLY_NON_TAUT_DIFF 0x0004 /* iOther: eq. partition in isotopic only non-taut differs from non-isotopic */ +#define CANON_FLAG_ISO_TAUT_DIFF 0x0008 /* iBase: isotopic eq. partition in isotopic taut differs from non-isotopic taut */ +#define CANON_FLAG_ISO_FIXED_H_DIFF 0x0010 /* iOther: isotopic eq. partition in fixed H non-taut differs from non-isotopic fixed H */ + +/* Note: rank of tautomer atom #i = Rank[nEndpointAtomNumber[i]] */ +/* for each tautomer atom group (t_group) t_group.nFirstEndpointAtNoPos */ +/* is the first index of the atom number in nEndpointAtomNumber[] */ + +typedef struct tagTautomerEndpoint { + /* + union { + struct { + AT_RANK num_Mobile; // Num_H+num_D+num_T+num_NegCharges + AT_RANK num_NegCharges; + AT_RANK num_T; + AT_RANK num_D; + }; + AT_RANK num[T_NUM_NO_ISOTOPIC+T_NUM_ISOTOPIC]; // same size and meaning as num[] in T_GROUP + }; + */ + AT_RANK num[T_NUM_NO_ISOTOPIC+T_NUM_ISOTOPIC]; /* same size and meaning as num[] in T_GROUP */ + AT_RANK num_DA[TG_NUM_DA]; + AT_NUMB nGroupNumber; + AT_NUMB nEquNumber; /* same for endpoints connected by alt paths */ + AT_NUMB nAtomNumber; + /*AT_NUMB neighbor_index; */ +} T_ENDPOINT; + +typedef struct tagTautomerBondLocation { + AT_NUMB nAtomNumber; + AT_NUMB neighbor_index; +} T_BONDPOS; + +typedef struct tagEndpointInfo { + S_CHAR cMoveableCharge; + S_CHAR cNeutralBondsValence; + S_CHAR cMobile; + S_CHAR cDonor; + S_CHAR cAcceptor; + S_CHAR cKetoEnolCode; /* 1 => carbon, 2 => oxygen */ /* post v.1 feature */ +} ENDPOINT_INFO; + + +/* positive charge group (extended onium) */ + +#define CHARGED_CPOINT(X,i) ((X)[i].charge==1) + +typedef struct tagChargeCandidate { + AT_NUMB atnumber; + S_CHAR type; + S_CHAR subtype; +} C_CANDIDATE; + +typedef struct tagChargeGroup { + AT_RANK num[2]; /* [0]: number of (+), [1]: number atoms that have H, including H accessible through tautomerism */ + AT_RANK num_CPoints; + AT_NUMB nGroupNumber; + U_CHAR cGroupType; +} C_GROUP; + +typedef struct tagChargeGroupsInfo { + C_GROUP *c_group; + int num_c_groups; + int max_num_c_groups; + + C_CANDIDATE *c_candidate; + int max_num_candidates; + int num_candidates; /* 0=>unimitialized, -1=>no candidates found */ +} C_GROUP_INFO; + +/* salts */ +typedef struct tagSaltChargeCandidate { + AT_NUMB atnumber; + S_CHAR type; + S_CHAR subtype; + AT_NUMB endpoint; /* MAX_ATOMS+1 => found alt path to the candidate */ +} S_CANDIDATE; + +typedef struct tagSaltGroupInfo { + S_CANDIDATE *s_candidate; + int max_num_candidates; + int num_candidates; /* 0=>unimitialized, -1=>no candidates found */ + int num_other_candidates; /* num. non-"acidic O" candidates */ + int num_p_only_candidates; /* num. non-tautomeric p-donor/acceptor candidates like -CH2-SH */ +} S_GROUP_INFO; + +/********************* ATOM_SIZES *******************************/ +/* sizes of a component */ +typedef struct tagAtomSizes { + /* for tautomeric and non-tautomeric structures */ + int nMaxNumStereoAtoms; /* max. number of stereo atoms in isotopic case */ + int nMaxNumStereoBonds; /* max. number of stereo bonds in isotopic case */ + int num_isotopic_atoms; /* includes atoms that have isotopic tautomeric H */ + int nLenCT; + int nLenBonds; + int nLenIsotopic; + int nLenCTAtOnly; + int nLenLinearCTStereoDble; /* max. number of stereo bonds in non-isotopic case */ + int nLenLinearCTStereoCarb; /* max. number of stereo atoms in non-isotopic case */ + /* int bHasIsotopicAtoms; */ + int bMayHaveStereo; + + int bIgnoreIsotopic; + + /* tautomeric structure only; zeroes in non-tautomeric */ + int nLenLinearCTTautomer; + int nLenLinearCTIsotopicTautomer; + int bHasIsotopicTautGroups; + int nLenIsotopicEndpoints; +} ATOM_SIZES; + + +typedef struct tagDfsPath { + AT_RANK at_no; + /*AT_RANK nDfsLevel;*/ + U_CHAR bond_type; + S_CHAR bond_pos; +} DFS_PATH; + + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + +int is_centerpoint_elem( U_CHAR el_number ); +int is_centerpoint_elem_strict( U_CHAR el_number ); +#if ( KETO_ENOL_TAUT == 1 ) +int is_centerpoint_elem_KET( U_CHAR el_number ); +#endif +int bIsCenterPointStrict( inp_ATOM *atom, int iat ); + +int nGetEndpointInfo( inp_ATOM *atom, int iat, ENDPOINT_INFO *eif ); +#if ( KETO_ENOL_TAUT == 1 ) +int nGetEndpointInfo_KET( inp_ATOM *atom, int iat, ENDPOINT_INFO *eif ); +#endif +void AddAtom2DA( AT_RANK num_DA[], inp_ATOM *atom, int at_no, int bSubtract ); +int AddAtom2num( AT_RANK num[], inp_ATOM *atom, int at_no, int bSubtract ); +int AddEndPoint( T_ENDPOINT *pEndPoint, inp_ATOM *at, int iat ); +int bHasAcidicHydrogen( inp_ATOM *at, int i ); +int bHasOtherExchangableH ( inp_ATOM *at, int i ); +int bHasAcidicMinus( inp_ATOM *at, int i ); + + +int nGet15TautIn6MembAltRing( struct tagCANON_GLOBALS *pCG, + inp_ATOM *atom, + int nStartAtom, + AT_RANK *nBfsTreePos, + DFS_PATH *DfsPath, + int nMaxLenBfsTree, + T_ENDPOINT *EndPoint, + int nMaxNumEndPoint, + T_BONDPOS *BondPos, + int nMaxNumBondPos, + int *pnNumEndPoint, + int *pnNumBondPos, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD, + int num_atoms ); + +int nGet12TautIn5MembAltRing( struct tagCANON_GLOBALS *pCG, + inp_ATOM *atom, int nStartAtom, + int nStartAtomNeighbor, + AT_RANK *nBfsTreePos, + DFS_PATH *DfsPath, + int nMaxLenBfsTree, + T_ENDPOINT *EndPoint, + int nMaxNumEndPoint, + T_BONDPOS *BondPos, + int nMaxNumBondPos, + int *pnNumEndPoint, + int *pnNumBondPos, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD, + int num_atoms ); + +int nGet14TautIn7MembAltRing( struct tagCANON_GLOBALS *pCG, + inp_ATOM *atom, + int nStartAtom, + int nStartAtomNeighbor, + int nStartAtomNeighborEndpoint, + int nStartAtomNeighborNeighborEndpoint, + AT_RANK *nDfsPathPos, + DFS_PATH *DfsPath, + int nMaxLenDfsPath, + T_ENDPOINT *EndPoint, + int nMaxNumEndPoint, + T_BONDPOS *BondPos, + int nMaxNumBondPos, + int *pnNumEndPoint, + int *pnNumBondPos, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD, + int num_atoms ); + +int nGet14TautIn5MembAltRing( struct tagCANON_GLOBALS *pCG, + inp_ATOM *atom, + int nStartAtom, + int nStartAtomNeighbor, + int nStartAtomNeighborEndpoint, + int nStartAtomNeighborNeighborEndpoint, + AT_RANK *nDfsPathPos, + DFS_PATH *DfsPath, + int nMaxLenDfsPath, + T_ENDPOINT *EndPoint, + int nMaxNumEndPoint, + T_BONDPOS *BondPos, + int nMaxNumBondPos, + int *pnNumEndPoint, + int *pnNumBondPos, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD, + int num_atoms ); + +int nGet15TautInAltPath( struct tagCANON_GLOBALS *pCG, + inp_ATOM *atom, + int nStartAtom, AT_RANK *nDfsPathPos, + DFS_PATH *DfsPath, + int nMaxLenDfsPath, + T_ENDPOINT *EndPoint, + int nMaxNumEndPoint, + T_BONDPOS *BondPos, + int nMaxNumBondPos, + int *pnNumEndPoint, + int *pnNumBondPos, + struct BalancedNetworkStructure *pBNS, + struct BalancedNetworkData *pBD, + int num_atoms ); + + +#if ( RING2CHAIN == 1 ) +int Ring2Chain( ORIG_ATOM_DATA *orig_inp_data ); +#endif + +#if ( UNDERIVATIZE == 1 ) +int underivatize( ORIG_ATOM_DATA *orig_inp_data ); +#endif + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + + +#endif /* _ICHITAUT_H_ */ diff --git a/INCHI-1-SRC/INCHI_BASE/src/ichitime.h b/INCHI-1-SRC/INCHI_BASE/src/ichitime.h new file mode 100644 index 0000000..364b866 --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/ichitime.h @@ -0,0 +1,104 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef __ICHITIME_H__ +#define __ICHITIME_H__ + +#ifdef COMPILE_ANSI_ONLY + +#ifdef __FreeBSD__ +#include +#endif + +/* get times() */ +#ifdef INCHI_USETIMES +#include +#endif + +/*#include */ + +#include + +typedef struct tagInchiTime { + clock_t clockTime; +} inchiTime; + +#else + +/* Win32 _ftime(): */ +#include + +typedef struct tagInchiTime { + unsigned long clockTime; /* Time in seconds since midnight (00:00:00), January 1, 1970; + signed long overflow expected in 2038 */ + long millitime; /* milliseconds */ +} inchiTime; + +#endif + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + + +typedef struct tagINCHI_CLOCK +{ + clock_t m_MaxPositiveClock; + clock_t m_MinNegativeClock; + clock_t m_HalfMaxPositiveClock; + clock_t m_HalfMinNegativeClock; +} INCHI_CLOCK; + +void InchiTimeGet( inchiTime *TickEnd ); + +long InchiTimeMsecDiff(INCHI_CLOCK *ic, inchiTime *TickEnd, inchiTime *TickStart ); +void InchiTimeAddMsec(INCHI_CLOCK *ic, inchiTime *TickEnd, unsigned long nNumMsec ); +int bInchiTimeIsOver(INCHI_CLOCK *ic, inchiTime *TickEnd ); +long InchiTimeElapsed(INCHI_CLOCK *ic, inchiTime *TickStart ); + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + + +#endif /* __ICHITIME_H__ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ikey_base26.c b/INCHI-1-SRC/INCHI_BASE/src/ikey_base26.c similarity index 92% rename from INCHI-1-SRC/INCHI_API/inchi_dll/ikey_base26.c rename to INCHI-1-SRC/INCHI_BASE/src/ikey_base26.c index b30b3db..7aa1b4f 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ikey_base26.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ikey_base26.c @@ -1,1366 +1,1347 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - InChIKey: procedures for base-26 encoding - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - -#ifdef _MSC_VER -#if _MSC_VER > 1000 -#pragma warning( disable : 4996 ) -#endif -#endif - -#include -#include -#include -#include "ikey_base26.h" - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - As the 2^14 (16384) is very close to 26^3 (17576), a triplet of uppercase - letters A..Z encodes 14 bits with good efficiency. - For speed, we just tabulate triplets below. - - We should throw away 17576-16384= 1192 triplets. - These are 676 triplets starting from 'E', the most frequent letter in English - texts (the other 516 are those started at 'T' , "TAA" to "TTV"). - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -static char t26[][4] = -{ -"AAA","AAB","AAC","AAD","AAE","AAF","AAG","AAH","AAI","AAJ","AAK","AAL","AAM","AAN","AAO","AAP", -"AAQ","AAR","AAS","AAT","AAU","AAV","AAW","AAX","AAY","AAZ","ABA","ABB","ABC","ABD","ABE","ABF", -"ABG","ABH","ABI","ABJ","ABK","ABL","ABM","ABN","ABO","ABP","ABQ","ABR","ABS","ABT","ABU","ABV", -"ABW","ABX","ABY","ABZ","ACA","ACB","ACC","ACD","ACE","ACF","ACG","ACH","ACI","ACJ","ACK","ACL", -"ACM","ACN","ACO","ACP","ACQ","ACR","ACS","ACT","ACU","ACV","ACW","ACX","ACY","ACZ","ADA","ADB", -"ADC","ADD","ADE","ADF","ADG","ADH","ADI","ADJ","ADK","ADL","ADM","ADN","ADO","ADP","ADQ","ADR", -"ADS","ADT","ADU","ADV","ADW","ADX","ADY","ADZ","AEA","AEB","AEC","AED","AEE","AEF","AEG","AEH", -"AEI","AEJ","AEK","AEL","AEM","AEN","AEO","AEP","AEQ","AER","AES","AET","AEU","AEV","AEW","AEX", -"AEY","AEZ","AFA","AFB","AFC","AFD","AFE","AFF","AFG","AFH","AFI","AFJ","AFK","AFL","AFM","AFN", -"AFO","AFP","AFQ","AFR","AFS","AFT","AFU","AFV","AFW","AFX","AFY","AFZ","AGA","AGB","AGC","AGD", -"AGE","AGF","AGG","AGH","AGI","AGJ","AGK","AGL","AGM","AGN","AGO","AGP","AGQ","AGR","AGS","AGT", -"AGU","AGV","AGW","AGX","AGY","AGZ","AHA","AHB","AHC","AHD","AHE","AHF","AHG","AHH","AHI","AHJ", -"AHK","AHL","AHM","AHN","AHO","AHP","AHQ","AHR","AHS","AHT","AHU","AHV","AHW","AHX","AHY","AHZ", -"AIA","AIB","AIC","AID","AIE","AIF","AIG","AIH","AII","AIJ","AIK","AIL","AIM","AIN","AIO","AIP", -"AIQ","AIR","AIS","AIT","AIU","AIV","AIW","AIX","AIY","AIZ","AJA","AJB","AJC","AJD","AJE","AJF", -"AJG","AJH","AJI","AJJ","AJK","AJL","AJM","AJN","AJO","AJP","AJQ","AJR","AJS","AJT","AJU","AJV", -"AJW","AJX","AJY","AJZ","AKA","AKB","AKC","AKD","AKE","AKF","AKG","AKH","AKI","AKJ","AKK","AKL", -"AKM","AKN","AKO","AKP","AKQ","AKR","AKS","AKT","AKU","AKV","AKW","AKX","AKY","AKZ","ALA","ALB", -"ALC","ALD","ALE","ALF","ALG","ALH","ALI","ALJ","ALK","ALL","ALM","ALN","ALO","ALP","ALQ","ALR", -"ALS","ALT","ALU","ALV","ALW","ALX","ALY","ALZ","AMA","AMB","AMC","AMD","AME","AMF","AMG","AMH", -"AMI","AMJ","AMK","AML","AMM","AMN","AMO","AMP","AMQ","AMR","AMS","AMT","AMU","AMV","AMW","AMX", -"AMY","AMZ","ANA","ANB","ANC","AND","ANE","ANF","ANG","ANH","ANI","ANJ","ANK","ANL","ANM","ANN", -"ANO","ANP","ANQ","ANR","ANS","ANT","ANU","ANV","ANW","ANX","ANY","ANZ","AOA","AOB","AOC","AOD", -"AOE","AOF","AOG","AOH","AOI","AOJ","AOK","AOL","AOM","AON","AOO","AOP","AOQ","AOR","AOS","AOT", -"AOU","AOV","AOW","AOX","AOY","AOZ","APA","APB","APC","APD","APE","APF","APG","APH","API","APJ", -"APK","APL","APM","APN","APO","APP","APQ","APR","APS","APT","APU","APV","APW","APX","APY","APZ", -"AQA","AQB","AQC","AQD","AQE","AQF","AQG","AQH","AQI","AQJ","AQK","AQL","AQM","AQN","AQO","AQP", -"AQQ","AQR","AQS","AQT","AQU","AQV","AQW","AQX","AQY","AQZ","ARA","ARB","ARC","ARD","ARE","ARF", -"ARG","ARH","ARI","ARJ","ARK","ARL","ARM","ARN","ARO","ARP","ARQ","ARR","ARS","ART","ARU","ARV", -"ARW","ARX","ARY","ARZ","ASA","ASB","ASC","ASD","ASE","ASF","ASG","ASH","ASI","ASJ","ASK","ASL", -"ASM","ASN","ASO","ASP","ASQ","ASR","ASS","AST","ASU","ASV","ASW","ASX","ASY","ASZ","ATA","ATB", -"ATC","ATD","ATE","ATF","ATG","ATH","ATI","ATJ","ATK","ATL","ATM","ATN","ATO","ATP","ATQ","ATR", -"ATS","ATT","ATU","ATV","ATW","ATX","ATY","ATZ","AUA","AUB","AUC","AUD","AUE","AUF","AUG","AUH", -"AUI","AUJ","AUK","AUL","AUM","AUN","AUO","AUP","AUQ","AUR","AUS","AUT","AUU","AUV","AUW","AUX", -"AUY","AUZ","AVA","AVB","AVC","AVD","AVE","AVF","AVG","AVH","AVI","AVJ","AVK","AVL","AVM","AVN", -"AVO","AVP","AVQ","AVR","AVS","AVT","AVU","AVV","AVW","AVX","AVY","AVZ","AWA","AWB","AWC","AWD", -"AWE","AWF","AWG","AWH","AWI","AWJ","AWK","AWL","AWM","AWN","AWO","AWP","AWQ","AWR","AWS","AWT", -"AWU","AWV","AWW","AWX","AWY","AWZ","AXA","AXB","AXC","AXD","AXE","AXF","AXG","AXH","AXI","AXJ", -"AXK","AXL","AXM","AXN","AXO","AXP","AXQ","AXR","AXS","AXT","AXU","AXV","AXW","AXX","AXY","AXZ", -"AYA","AYB","AYC","AYD","AYE","AYF","AYG","AYH","AYI","AYJ","AYK","AYL","AYM","AYN","AYO","AYP", -"AYQ","AYR","AYS","AYT","AYU","AYV","AYW","AYX","AYY","AYZ","AZA","AZB","AZC","AZD","AZE","AZF", -"AZG","AZH","AZI","AZJ","AZK","AZL","AZM","AZN","AZO","AZP","AZQ","AZR","AZS","AZT","AZU","AZV", -"AZW","AZX","AZY","AZZ","BAA","BAB","BAC","BAD","BAE","BAF","BAG","BAH","BAI","BAJ","BAK","BAL", -"BAM","BAN","BAO","BAP","BAQ","BAR","BAS","BAT","BAU","BAV","BAW","BAX","BAY","BAZ","BBA","BBB", -"BBC","BBD","BBE","BBF","BBG","BBH","BBI","BBJ","BBK","BBL","BBM","BBN","BBO","BBP","BBQ","BBR", -"BBS","BBT","BBU","BBV","BBW","BBX","BBY","BBZ","BCA","BCB","BCC","BCD","BCE","BCF","BCG","BCH", -"BCI","BCJ","BCK","BCL","BCM","BCN","BCO","BCP","BCQ","BCR","BCS","BCT","BCU","BCV","BCW","BCX", -"BCY","BCZ","BDA","BDB","BDC","BDD","BDE","BDF","BDG","BDH","BDI","BDJ","BDK","BDL","BDM","BDN", -"BDO","BDP","BDQ","BDR","BDS","BDT","BDU","BDV","BDW","BDX","BDY","BDZ","BEA","BEB","BEC","BED", -"BEE","BEF","BEG","BEH","BEI","BEJ","BEK","BEL","BEM","BEN","BEO","BEP","BEQ","BER","BES","BET", -"BEU","BEV","BEW","BEX","BEY","BEZ","BFA","BFB","BFC","BFD","BFE","BFF","BFG","BFH","BFI","BFJ", -"BFK","BFL","BFM","BFN","BFO","BFP","BFQ","BFR","BFS","BFT","BFU","BFV","BFW","BFX","BFY","BFZ", -"BGA","BGB","BGC","BGD","BGE","BGF","BGG","BGH","BGI","BGJ","BGK","BGL","BGM","BGN","BGO","BGP", -"BGQ","BGR","BGS","BGT","BGU","BGV","BGW","BGX","BGY","BGZ","BHA","BHB","BHC","BHD","BHE","BHF", -"BHG","BHH","BHI","BHJ","BHK","BHL","BHM","BHN","BHO","BHP","BHQ","BHR","BHS","BHT","BHU","BHV", -"BHW","BHX","BHY","BHZ","BIA","BIB","BIC","BID","BIE","BIF","BIG","BIH","BII","BIJ","BIK","BIL", -"BIM","BIN","BIO","BIP","BIQ","BIR","BIS","BIT","BIU","BIV","BIW","BIX","BIY","BIZ","BJA","BJB", -"BJC","BJD","BJE","BJF","BJG","BJH","BJI","BJJ","BJK","BJL","BJM","BJN","BJO","BJP","BJQ","BJR", -"BJS","BJT","BJU","BJV","BJW","BJX","BJY","BJZ","BKA","BKB","BKC","BKD","BKE","BKF","BKG","BKH", -"BKI","BKJ","BKK","BKL","BKM","BKN","BKO","BKP","BKQ","BKR","BKS","BKT","BKU","BKV","BKW","BKX", -"BKY","BKZ","BLA","BLB","BLC","BLD","BLE","BLF","BLG","BLH","BLI","BLJ","BLK","BLL","BLM","BLN", -"BLO","BLP","BLQ","BLR","BLS","BLT","BLU","BLV","BLW","BLX","BLY","BLZ","BMA","BMB","BMC","BMD", -"BME","BMF","BMG","BMH","BMI","BMJ","BMK","BML","BMM","BMN","BMO","BMP","BMQ","BMR","BMS","BMT", -"BMU","BMV","BMW","BMX","BMY","BMZ","BNA","BNB","BNC","BND","BNE","BNF","BNG","BNH","BNI","BNJ", -"BNK","BNL","BNM","BNN","BNO","BNP","BNQ","BNR","BNS","BNT","BNU","BNV","BNW","BNX","BNY","BNZ", -"BOA","BOB","BOC","BOD","BOE","BOF","BOG","BOH","BOI","BOJ","BOK","BOL","BOM","BON","BOO","BOP", -"BOQ","BOR","BOS","BOT","BOU","BOV","BOW","BOX","BOY","BOZ","BPA","BPB","BPC","BPD","BPE","BPF", -"BPG","BPH","BPI","BPJ","BPK","BPL","BPM","BPN","BPO","BPP","BPQ","BPR","BPS","BPT","BPU","BPV", -"BPW","BPX","BPY","BPZ","BQA","BQB","BQC","BQD","BQE","BQF","BQG","BQH","BQI","BQJ","BQK","BQL", -"BQM","BQN","BQO","BQP","BQQ","BQR","BQS","BQT","BQU","BQV","BQW","BQX","BQY","BQZ","BRA","BRB", -"BRC","BRD","BRE","BRF","BRG","BRH","BRI","BRJ","BRK","BRL","BRM","BRN","BRO","BRP","BRQ","BRR", -"BRS","BRT","BRU","BRV","BRW","BRX","BRY","BRZ","BSA","BSB","BSC","BSD","BSE","BSF","BSG","BSH", -"BSI","BSJ","BSK","BSL","BSM","BSN","BSO","BSP","BSQ","BSR","BSS","BST","BSU","BSV","BSW","BSX", -"BSY","BSZ","BTA","BTB","BTC","BTD","BTE","BTF","BTG","BTH","BTI","BTJ","BTK","BTL","BTM","BTN", -"BTO","BTP","BTQ","BTR","BTS","BTT","BTU","BTV","BTW","BTX","BTY","BTZ","BUA","BUB","BUC","BUD", -"BUE","BUF","BUG","BUH","BUI","BUJ","BUK","BUL","BUM","BUN","BUO","BUP","BUQ","BUR","BUS","BUT", -"BUU","BUV","BUW","BUX","BUY","BUZ","BVA","BVB","BVC","BVD","BVE","BVF","BVG","BVH","BVI","BVJ", -"BVK","BVL","BVM","BVN","BVO","BVP","BVQ","BVR","BVS","BVT","BVU","BVV","BVW","BVX","BVY","BVZ", -"BWA","BWB","BWC","BWD","BWE","BWF","BWG","BWH","BWI","BWJ","BWK","BWL","BWM","BWN","BWO","BWP", -"BWQ","BWR","BWS","BWT","BWU","BWV","BWW","BWX","BWY","BWZ","BXA","BXB","BXC","BXD","BXE","BXF", -"BXG","BXH","BXI","BXJ","BXK","BXL","BXM","BXN","BXO","BXP","BXQ","BXR","BXS","BXT","BXU","BXV", -"BXW","BXX","BXY","BXZ","BYA","BYB","BYC","BYD","BYE","BYF","BYG","BYH","BYI","BYJ","BYK","BYL", -"BYM","BYN","BYO","BYP","BYQ","BYR","BYS","BYT","BYU","BYV","BYW","BYX","BYY","BYZ","BZA","BZB", -"BZC","BZD","BZE","BZF","BZG","BZH","BZI","BZJ","BZK","BZL","BZM","BZN","BZO","BZP","BZQ","BZR", -"BZS","BZT","BZU","BZV","BZW","BZX","BZY","BZZ","CAA","CAB","CAC","CAD","CAE","CAF","CAG","CAH", -"CAI","CAJ","CAK","CAL","CAM","CAN","CAO","CAP","CAQ","CAR","CAS","CAT","CAU","CAV","CAW","CAX", -"CAY","CAZ","CBA","CBB","CBC","CBD","CBE","CBF","CBG","CBH","CBI","CBJ","CBK","CBL","CBM","CBN", -"CBO","CBP","CBQ","CBR","CBS","CBT","CBU","CBV","CBW","CBX","CBY","CBZ","CCA","CCB","CCC","CCD", -"CCE","CCF","CCG","CCH","CCI","CCJ","CCK","CCL","CCM","CCN","CCO","CCP","CCQ","CCR","CCS","CCT", -"CCU","CCV","CCW","CCX","CCY","CCZ","CDA","CDB","CDC","CDD","CDE","CDF","CDG","CDH","CDI","CDJ", -"CDK","CDL","CDM","CDN","CDO","CDP","CDQ","CDR","CDS","CDT","CDU","CDV","CDW","CDX","CDY","CDZ", -"CEA","CEB","CEC","CED","CEE","CEF","CEG","CEH","CEI","CEJ","CEK","CEL","CEM","CEN","CEO","CEP", -"CEQ","CER","CES","CET","CEU","CEV","CEW","CEX","CEY","CEZ","CFA","CFB","CFC","CFD","CFE","CFF", -"CFG","CFH","CFI","CFJ","CFK","CFL","CFM","CFN","CFO","CFP","CFQ","CFR","CFS","CFT","CFU","CFV", -"CFW","CFX","CFY","CFZ","CGA","CGB","CGC","CGD","CGE","CGF","CGG","CGH","CGI","CGJ","CGK","CGL", -"CGM","CGN","CGO","CGP","CGQ","CGR","CGS","CGT","CGU","CGV","CGW","CGX","CGY","CGZ","CHA","CHB", -"CHC","CHD","CHE","CHF","CHG","CHH","CHI","CHJ","CHK","CHL","CHM","CHN","CHO","CHP","CHQ","CHR", -"CHS","CHT","CHU","CHV","CHW","CHX","CHY","CHZ","CIA","CIB","CIC","CID","CIE","CIF","CIG","CIH", -"CII","CIJ","CIK","CIL","CIM","CIN","CIO","CIP","CIQ","CIR","CIS","CIT","CIU","CIV","CIW","CIX", -"CIY","CIZ","CJA","CJB","CJC","CJD","CJE","CJF","CJG","CJH","CJI","CJJ","CJK","CJL","CJM","CJN", -"CJO","CJP","CJQ","CJR","CJS","CJT","CJU","CJV","CJW","CJX","CJY","CJZ","CKA","CKB","CKC","CKD", -"CKE","CKF","CKG","CKH","CKI","CKJ","CKK","CKL","CKM","CKN","CKO","CKP","CKQ","CKR","CKS","CKT", -"CKU","CKV","CKW","CKX","CKY","CKZ","CLA","CLB","CLC","CLD","CLE","CLF","CLG","CLH","CLI","CLJ", -"CLK","CLL","CLM","CLN","CLO","CLP","CLQ","CLR","CLS","CLT","CLU","CLV","CLW","CLX","CLY","CLZ", -"CMA","CMB","CMC","CMD","CME","CMF","CMG","CMH","CMI","CMJ","CMK","CML","CMM","CMN","CMO","CMP", -"CMQ","CMR","CMS","CMT","CMU","CMV","CMW","CMX","CMY","CMZ","CNA","CNB","CNC","CND","CNE","CNF", -"CNG","CNH","CNI","CNJ","CNK","CNL","CNM","CNN","CNO","CNP","CNQ","CNR","CNS","CNT","CNU","CNV", -"CNW","CNX","CNY","CNZ","COA","COB","COC","COD","COE","COF","COG","COH","COI","COJ","COK","COL", -"COM","CON","COO","COP","COQ","COR","COS","COT","COU","COV","COW","COX","COY","COZ","CPA","CPB", -"CPC","CPD","CPE","CPF","CPG","CPH","CPI","CPJ","CPK","CPL","CPM","CPN","CPO","CPP","CPQ","CPR", -"CPS","CPT","CPU","CPV","CPW","CPX","CPY","CPZ","CQA","CQB","CQC","CQD","CQE","CQF","CQG","CQH", -"CQI","CQJ","CQK","CQL","CQM","CQN","CQO","CQP","CQQ","CQR","CQS","CQT","CQU","CQV","CQW","CQX", -"CQY","CQZ","CRA","CRB","CRC","CRD","CRE","CRF","CRG","CRH","CRI","CRJ","CRK","CRL","CRM","CRN", -"CRO","CRP","CRQ","CRR","CRS","CRT","CRU","CRV","CRW","CRX","CRY","CRZ","CSA","CSB","CSC","CSD", -"CSE","CSF","CSG","CSH","CSI","CSJ","CSK","CSL","CSM","CSN","CSO","CSP","CSQ","CSR","CSS","CST", -"CSU","CSV","CSW","CSX","CSY","CSZ","CTA","CTB","CTC","CTD","CTE","CTF","CTG","CTH","CTI","CTJ", -"CTK","CTL","CTM","CTN","CTO","CTP","CTQ","CTR","CTS","CTT","CTU","CTV","CTW","CTX","CTY","CTZ", -"CUA","CUB","CUC","CUD","CUE","CUF","CUG","CUH","CUI","CUJ","CUK","CUL","CUM","CUN","CUO","CUP", -"CUQ","CUR","CUS","CUT","CUU","CUV","CUW","CUX","CUY","CUZ","CVA","CVB","CVC","CVD","CVE","CVF", -"CVG","CVH","CVI","CVJ","CVK","CVL","CVM","CVN","CVO","CVP","CVQ","CVR","CVS","CVT","CVU","CVV", -"CVW","CVX","CVY","CVZ","CWA","CWB","CWC","CWD","CWE","CWF","CWG","CWH","CWI","CWJ","CWK","CWL", -"CWM","CWN","CWO","CWP","CWQ","CWR","CWS","CWT","CWU","CWV","CWW","CWX","CWY","CWZ","CXA","CXB", -"CXC","CXD","CXE","CXF","CXG","CXH","CXI","CXJ","CXK","CXL","CXM","CXN","CXO","CXP","CXQ","CXR", -"CXS","CXT","CXU","CXV","CXW","CXX","CXY","CXZ","CYA","CYB","CYC","CYD","CYE","CYF","CYG","CYH", -"CYI","CYJ","CYK","CYL","CYM","CYN","CYO","CYP","CYQ","CYR","CYS","CYT","CYU","CYV","CYW","CYX", -"CYY","CYZ","CZA","CZB","CZC","CZD","CZE","CZF","CZG","CZH","CZI","CZJ","CZK","CZL","CZM","CZN", -"CZO","CZP","CZQ","CZR","CZS","CZT","CZU","CZV","CZW","CZX","CZY","CZZ","DAA","DAB","DAC","DAD", -"DAE","DAF","DAG","DAH","DAI","DAJ","DAK","DAL","DAM","DAN","DAO","DAP","DAQ","DAR","DAS","DAT", -"DAU","DAV","DAW","DAX","DAY","DAZ","DBA","DBB","DBC","DBD","DBE","DBF","DBG","DBH","DBI","DBJ", -"DBK","DBL","DBM","DBN","DBO","DBP","DBQ","DBR","DBS","DBT","DBU","DBV","DBW","DBX","DBY","DBZ", -"DCA","DCB","DCC","DCD","DCE","DCF","DCG","DCH","DCI","DCJ","DCK","DCL","DCM","DCN","DCO","DCP", -"DCQ","DCR","DCS","DCT","DCU","DCV","DCW","DCX","DCY","DCZ","DDA","DDB","DDC","DDD","DDE","DDF", -"DDG","DDH","DDI","DDJ","DDK","DDL","DDM","DDN","DDO","DDP","DDQ","DDR","DDS","DDT","DDU","DDV", -"DDW","DDX","DDY","DDZ","DEA","DEB","DEC","DED","DEE","DEF","DEG","DEH","DEI","DEJ","DEK","DEL", -"DEM","DEN","DEO","DEP","DEQ","DER","DES","DET","DEU","DEV","DEW","DEX","DEY","DEZ","DFA","DFB", -"DFC","DFD","DFE","DFF","DFG","DFH","DFI","DFJ","DFK","DFL","DFM","DFN","DFO","DFP","DFQ","DFR", -"DFS","DFT","DFU","DFV","DFW","DFX","DFY","DFZ","DGA","DGB","DGC","DGD","DGE","DGF","DGG","DGH", -"DGI","DGJ","DGK","DGL","DGM","DGN","DGO","DGP","DGQ","DGR","DGS","DGT","DGU","DGV","DGW","DGX", -"DGY","DGZ","DHA","DHB","DHC","DHD","DHE","DHF","DHG","DHH","DHI","DHJ","DHK","DHL","DHM","DHN", -"DHO","DHP","DHQ","DHR","DHS","DHT","DHU","DHV","DHW","DHX","DHY","DHZ","DIA","DIB","DIC","DID", -"DIE","DIF","DIG","DIH","DII","DIJ","DIK","DIL","DIM","DIN","DIO","DIP","DIQ","DIR","DIS","DIT", -"DIU","DIV","DIW","DIX","DIY","DIZ","DJA","DJB","DJC","DJD","DJE","DJF","DJG","DJH","DJI","DJJ", -"DJK","DJL","DJM","DJN","DJO","DJP","DJQ","DJR","DJS","DJT","DJU","DJV","DJW","DJX","DJY","DJZ", -"DKA","DKB","DKC","DKD","DKE","DKF","DKG","DKH","DKI","DKJ","DKK","DKL","DKM","DKN","DKO","DKP", -"DKQ","DKR","DKS","DKT","DKU","DKV","DKW","DKX","DKY","DKZ","DLA","DLB","DLC","DLD","DLE","DLF", -"DLG","DLH","DLI","DLJ","DLK","DLL","DLM","DLN","DLO","DLP","DLQ","DLR","DLS","DLT","DLU","DLV", -"DLW","DLX","DLY","DLZ","DMA","DMB","DMC","DMD","DME","DMF","DMG","DMH","DMI","DMJ","DMK","DML", -"DMM","DMN","DMO","DMP","DMQ","DMR","DMS","DMT","DMU","DMV","DMW","DMX","DMY","DMZ","DNA","DNB", -"DNC","DND","DNE","DNF","DNG","DNH","DNI","DNJ","DNK","DNL","DNM","DNN","DNO","DNP","DNQ","DNR", -"DNS","DNT","DNU","DNV","DNW","DNX","DNY","DNZ","DOA","DOB","DOC","DOD","DOE","DOF","DOG","DOH", -"DOI","DOJ","DOK","DOL","DOM","DON","DOO","DOP","DOQ","DOR","DOS","DOT","DOU","DOV","DOW","DOX", -"DOY","DOZ","DPA","DPB","DPC","DPD","DPE","DPF","DPG","DPH","DPI","DPJ","DPK","DPL","DPM","DPN", -"DPO","DPP","DPQ","DPR","DPS","DPT","DPU","DPV","DPW","DPX","DPY","DPZ","DQA","DQB","DQC","DQD", -"DQE","DQF","DQG","DQH","DQI","DQJ","DQK","DQL","DQM","DQN","DQO","DQP","DQQ","DQR","DQS","DQT", -"DQU","DQV","DQW","DQX","DQY","DQZ","DRA","DRB","DRC","DRD","DRE","DRF","DRG","DRH","DRI","DRJ", -"DRK","DRL","DRM","DRN","DRO","DRP","DRQ","DRR","DRS","DRT","DRU","DRV","DRW","DRX","DRY","DRZ", -"DSA","DSB","DSC","DSD","DSE","DSF","DSG","DSH","DSI","DSJ","DSK","DSL","DSM","DSN","DSO","DSP", -"DSQ","DSR","DSS","DST","DSU","DSV","DSW","DSX","DSY","DSZ","DTA","DTB","DTC","DTD","DTE","DTF", -"DTG","DTH","DTI","DTJ","DTK","DTL","DTM","DTN","DTO","DTP","DTQ","DTR","DTS","DTT","DTU","DTV", -"DTW","DTX","DTY","DTZ","DUA","DUB","DUC","DUD","DUE","DUF","DUG","DUH","DUI","DUJ","DUK","DUL", -"DUM","DUN","DUO","DUP","DUQ","DUR","DUS","DUT","DUU","DUV","DUW","DUX","DUY","DUZ","DVA","DVB", -"DVC","DVD","DVE","DVF","DVG","DVH","DVI","DVJ","DVK","DVL","DVM","DVN","DVO","DVP","DVQ","DVR", -"DVS","DVT","DVU","DVV","DVW","DVX","DVY","DVZ","DWA","DWB","DWC","DWD","DWE","DWF","DWG","DWH", -"DWI","DWJ","DWK","DWL","DWM","DWN","DWO","DWP","DWQ","DWR","DWS","DWT","DWU","DWV","DWW","DWX", -"DWY","DWZ","DXA","DXB","DXC","DXD","DXE","DXF","DXG","DXH","DXI","DXJ","DXK","DXL","DXM","DXN", -"DXO","DXP","DXQ","DXR","DXS","DXT","DXU","DXV","DXW","DXX","DXY","DXZ","DYA","DYB","DYC","DYD", -"DYE","DYF","DYG","DYH","DYI","DYJ","DYK","DYL","DYM","DYN","DYO","DYP","DYQ","DYR","DYS","DYT", -"DYU","DYV","DYW","DYX","DYY","DYZ","DZA","DZB","DZC","DZD","DZE","DZF","DZG","DZH","DZI","DZJ", -"DZK","DZL","DZM","DZN","DZO","DZP","DZQ","DZR","DZS","DZT","DZU","DZV","DZW","DZX","DZY","DZZ", - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - E-starteds are intentionally omitted -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -"FAA","FAB","FAC","FAD","FAE","FAF","FAG","FAH","FAI","FAJ","FAK","FAL", -"FAM","FAN","FAO","FAP","FAQ","FAR","FAS","FAT","FAU","FAV","FAW","FAX","FAY","FAZ","FBA","FBB", -"FBC","FBD","FBE","FBF","FBG","FBH","FBI","FBJ","FBK","FBL","FBM","FBN","FBO","FBP","FBQ","FBR", -"FBS","FBT","FBU","FBV","FBW","FBX","FBY","FBZ","FCA","FCB","FCC","FCD","FCE","FCF","FCG","FCH", -"FCI","FCJ","FCK","FCL","FCM","FCN","FCO","FCP","FCQ","FCR","FCS","FCT","FCU","FCV","FCW","FCX", -"FCY","FCZ","FDA","FDB","FDC","FDD","FDE","FDF","FDG","FDH","FDI","FDJ","FDK","FDL","FDM","FDN", -"FDO","FDP","FDQ","FDR","FDS","FDT","FDU","FDV","FDW","FDX","FDY","FDZ","FEA","FEB","FEC","FED", -"FEE","FEF","FEG","FEH","FEI","FEJ","FEK","FEL","FEM","FEN","FEO","FEP","FEQ","FER","FES","FET", -"FEU","FEV","FEW","FEX","FEY","FEZ","FFA","FFB","FFC","FFD","FFE","FFF","FFG","FFH","FFI","FFJ", -"FFK","FFL","FFM","FFN","FFO","FFP","FFQ","FFR","FFS","FFT","FFU","FFV","FFW","FFX","FFY","FFZ", -"FGA","FGB","FGC","FGD","FGE","FGF","FGG","FGH","FGI","FGJ","FGK","FGL","FGM","FGN","FGO","FGP", -"FGQ","FGR","FGS","FGT","FGU","FGV","FGW","FGX","FGY","FGZ","FHA","FHB","FHC","FHD","FHE","FHF", -"FHG","FHH","FHI","FHJ","FHK","FHL","FHM","FHN","FHO","FHP","FHQ","FHR","FHS","FHT","FHU","FHV", -"FHW","FHX","FHY","FHZ","FIA","FIB","FIC","FID","FIE","FIF","FIG","FIH","FII","FIJ","FIK","FIL", -"FIM","FIN","FIO","FIP","FIQ","FIR","FIS","FIT","FIU","FIV","FIW","FIX","FIY","FIZ","FJA","FJB", -"FJC","FJD","FJE","FJF","FJG","FJH","FJI","FJJ","FJK","FJL","FJM","FJN","FJO","FJP","FJQ","FJR", -"FJS","FJT","FJU","FJV","FJW","FJX","FJY","FJZ","FKA","FKB","FKC","FKD","FKE","FKF","FKG","FKH", -"FKI","FKJ","FKK","FKL","FKM","FKN","FKO","FKP","FKQ","FKR","FKS","FKT","FKU","FKV","FKW","FKX", -"FKY","FKZ","FLA","FLB","FLC","FLD","FLE","FLF","FLG","FLH","FLI","FLJ","FLK","FLL","FLM","FLN", -"FLO","FLP","FLQ","FLR","FLS","FLT","FLU","FLV","FLW","FLX","FLY","FLZ","FMA","FMB","FMC","FMD", -"FME","FMF","FMG","FMH","FMI","FMJ","FMK","FML","FMM","FMN","FMO","FMP","FMQ","FMR","FMS","FMT", -"FMU","FMV","FMW","FMX","FMY","FMZ","FNA","FNB","FNC","FND","FNE","FNF","FNG","FNH","FNI","FNJ", -"FNK","FNL","FNM","FNN","FNO","FNP","FNQ","FNR","FNS","FNT","FNU","FNV","FNW","FNX","FNY","FNZ", -"FOA","FOB","FOC","FOD","FOE","FOF","FOG","FOH","FOI","FOJ","FOK","FOL","FOM","FON","FOO","FOP", -"FOQ","FOR","FOS","FOT","FOU","FOV","FOW","FOX","FOY","FOZ","FPA","FPB","FPC","FPD","FPE","FPF", -"FPG","FPH","FPI","FPJ","FPK","FPL","FPM","FPN","FPO","FPP","FPQ","FPR","FPS","FPT","FPU","FPV", -"FPW","FPX","FPY","FPZ","FQA","FQB","FQC","FQD","FQE","FQF","FQG","FQH","FQI","FQJ","FQK","FQL", -"FQM","FQN","FQO","FQP","FQQ","FQR","FQS","FQT","FQU","FQV","FQW","FQX","FQY","FQZ","FRA","FRB", -"FRC","FRD","FRE","FRF","FRG","FRH","FRI","FRJ","FRK","FRL","FRM","FRN","FRO","FRP","FRQ","FRR", -"FRS","FRT","FRU","FRV","FRW","FRX","FRY","FRZ","FSA","FSB","FSC","FSD","FSE","FSF","FSG","FSH", -"FSI","FSJ","FSK","FSL","FSM","FSN","FSO","FSP","FSQ","FSR","FSS","FST","FSU","FSV","FSW","FSX", -"FSY","FSZ","FTA","FTB","FTC","FTD","FTE","FTF","FTG","FTH","FTI","FTJ","FTK","FTL","FTM","FTN", -"FTO","FTP","FTQ","FTR","FTS","FTT","FTU","FTV","FTW","FTX","FTY","FTZ","FUA","FUB","FUC","FUD", -"FUE","FUF","FUG","FUH","FUI","FUJ","FUK","FUL","FUM","FUN","FUO","FUP","FUQ","FUR","FUS","FUT", -"FUU","FUV","FUW","FUX","FUY","FUZ","FVA","FVB","FVC","FVD","FVE","FVF","FVG","FVH","FVI","FVJ", -"FVK","FVL","FVM","FVN","FVO","FVP","FVQ","FVR","FVS","FVT","FVU","FVV","FVW","FVX","FVY","FVZ", -"FWA","FWB","FWC","FWD","FWE","FWF","FWG","FWH","FWI","FWJ","FWK","FWL","FWM","FWN","FWO","FWP", -"FWQ","FWR","FWS","FWT","FWU","FWV","FWW","FWX","FWY","FWZ","FXA","FXB","FXC","FXD","FXE","FXF", -"FXG","FXH","FXI","FXJ","FXK","FXL","FXM","FXN","FXO","FXP","FXQ","FXR","FXS","FXT","FXU","FXV", -"FXW","FXX","FXY","FXZ","FYA","FYB","FYC","FYD","FYE","FYF","FYG","FYH","FYI","FYJ","FYK","FYL", -"FYM","FYN","FYO","FYP","FYQ","FYR","FYS","FYT","FYU","FYV","FYW","FYX","FYY","FYZ","FZA","FZB", -"FZC","FZD","FZE","FZF","FZG","FZH","FZI","FZJ","FZK","FZL","FZM","FZN","FZO","FZP","FZQ","FZR", -"FZS","FZT","FZU","FZV","FZW","FZX","FZY","FZZ","GAA","GAB","GAC","GAD","GAE","GAF","GAG","GAH", -"GAI","GAJ","GAK","GAL","GAM","GAN","GAO","GAP","GAQ","GAR","GAS","GAT","GAU","GAV","GAW","GAX", -"GAY","GAZ","GBA","GBB","GBC","GBD","GBE","GBF","GBG","GBH","GBI","GBJ","GBK","GBL","GBM","GBN", -"GBO","GBP","GBQ","GBR","GBS","GBT","GBU","GBV","GBW","GBX","GBY","GBZ","GCA","GCB","GCC","GCD", -"GCE","GCF","GCG","GCH","GCI","GCJ","GCK","GCL","GCM","GCN","GCO","GCP","GCQ","GCR","GCS","GCT", -"GCU","GCV","GCW","GCX","GCY","GCZ","GDA","GDB","GDC","GDD","GDE","GDF","GDG","GDH","GDI","GDJ", -"GDK","GDL","GDM","GDN","GDO","GDP","GDQ","GDR","GDS","GDT","GDU","GDV","GDW","GDX","GDY","GDZ", -"GEA","GEB","GEC","GED","GEE","GEF","GEG","GEH","GEI","GEJ","GEK","GEL","GEM","GEN","GEO","GEP", -"GEQ","GER","GES","GET","GEU","GEV","GEW","GEX","GEY","GEZ","GFA","GFB","GFC","GFD","GFE","GFF", -"GFG","GFH","GFI","GFJ","GFK","GFL","GFM","GFN","GFO","GFP","GFQ","GFR","GFS","GFT","GFU","GFV", -"GFW","GFX","GFY","GFZ","GGA","GGB","GGC","GGD","GGE","GGF","GGG","GGH","GGI","GGJ","GGK","GGL", -"GGM","GGN","GGO","GGP","GGQ","GGR","GGS","GGT","GGU","GGV","GGW","GGX","GGY","GGZ","GHA","GHB", -"GHC","GHD","GHE","GHF","GHG","GHH","GHI","GHJ","GHK","GHL","GHM","GHN","GHO","GHP","GHQ","GHR", -"GHS","GHT","GHU","GHV","GHW","GHX","GHY","GHZ","GIA","GIB","GIC","GID","GIE","GIF","GIG","GIH", -"GII","GIJ","GIK","GIL","GIM","GIN","GIO","GIP","GIQ","GIR","GIS","GIT","GIU","GIV","GIW","GIX", -"GIY","GIZ","GJA","GJB","GJC","GJD","GJE","GJF","GJG","GJH","GJI","GJJ","GJK","GJL","GJM","GJN", -"GJO","GJP","GJQ","GJR","GJS","GJT","GJU","GJV","GJW","GJX","GJY","GJZ","GKA","GKB","GKC","GKD", -"GKE","GKF","GKG","GKH","GKI","GKJ","GKK","GKL","GKM","GKN","GKO","GKP","GKQ","GKR","GKS","GKT", -"GKU","GKV","GKW","GKX","GKY","GKZ","GLA","GLB","GLC","GLD","GLE","GLF","GLG","GLH","GLI","GLJ", -"GLK","GLL","GLM","GLN","GLO","GLP","GLQ","GLR","GLS","GLT","GLU","GLV","GLW","GLX","GLY","GLZ", -"GMA","GMB","GMC","GMD","GME","GMF","GMG","GMH","GMI","GMJ","GMK","GML","GMM","GMN","GMO","GMP", -"GMQ","GMR","GMS","GMT","GMU","GMV","GMW","GMX","GMY","GMZ","GNA","GNB","GNC","GND","GNE","GNF", -"GNG","GNH","GNI","GNJ","GNK","GNL","GNM","GNN","GNO","GNP","GNQ","GNR","GNS","GNT","GNU","GNV", -"GNW","GNX","GNY","GNZ","GOA","GOB","GOC","GOD","GOE","GOF","GOG","GOH","GOI","GOJ","GOK","GOL", -"GOM","GON","GOO","GOP","GOQ","GOR","GOS","GOT","GOU","GOV","GOW","GOX","GOY","GOZ","GPA","GPB", -"GPC","GPD","GPE","GPF","GPG","GPH","GPI","GPJ","GPK","GPL","GPM","GPN","GPO","GPP","GPQ","GPR", -"GPS","GPT","GPU","GPV","GPW","GPX","GPY","GPZ","GQA","GQB","GQC","GQD","GQE","GQF","GQG","GQH", -"GQI","GQJ","GQK","GQL","GQM","GQN","GQO","GQP","GQQ","GQR","GQS","GQT","GQU","GQV","GQW","GQX", -"GQY","GQZ","GRA","GRB","GRC","GRD","GRE","GRF","GRG","GRH","GRI","GRJ","GRK","GRL","GRM","GRN", -"GRO","GRP","GRQ","GRR","GRS","GRT","GRU","GRV","GRW","GRX","GRY","GRZ","GSA","GSB","GSC","GSD", -"GSE","GSF","GSG","GSH","GSI","GSJ","GSK","GSL","GSM","GSN","GSO","GSP","GSQ","GSR","GSS","GST", -"GSU","GSV","GSW","GSX","GSY","GSZ","GTA","GTB","GTC","GTD","GTE","GTF","GTG","GTH","GTI","GTJ", -"GTK","GTL","GTM","GTN","GTO","GTP","GTQ","GTR","GTS","GTT","GTU","GTV","GTW","GTX","GTY","GTZ", -"GUA","GUB","GUC","GUD","GUE","GUF","GUG","GUH","GUI","GUJ","GUK","GUL","GUM","GUN","GUO","GUP", -"GUQ","GUR","GUS","GUT","GUU","GUV","GUW","GUX","GUY","GUZ","GVA","GVB","GVC","GVD","GVE","GVF", -"GVG","GVH","GVI","GVJ","GVK","GVL","GVM","GVN","GVO","GVP","GVQ","GVR","GVS","GVT","GVU","GVV", -"GVW","GVX","GVY","GVZ","GWA","GWB","GWC","GWD","GWE","GWF","GWG","GWH","GWI","GWJ","GWK","GWL", -"GWM","GWN","GWO","GWP","GWQ","GWR","GWS","GWT","GWU","GWV","GWW","GWX","GWY","GWZ","GXA","GXB", -"GXC","GXD","GXE","GXF","GXG","GXH","GXI","GXJ","GXK","GXL","GXM","GXN","GXO","GXP","GXQ","GXR", -"GXS","GXT","GXU","GXV","GXW","GXX","GXY","GXZ","GYA","GYB","GYC","GYD","GYE","GYF","GYG","GYH", -"GYI","GYJ","GYK","GYL","GYM","GYN","GYO","GYP","GYQ","GYR","GYS","GYT","GYU","GYV","GYW","GYX", -"GYY","GYZ","GZA","GZB","GZC","GZD","GZE","GZF","GZG","GZH","GZI","GZJ","GZK","GZL","GZM","GZN", -"GZO","GZP","GZQ","GZR","GZS","GZT","GZU","GZV","GZW","GZX","GZY","GZZ","HAA","HAB","HAC","HAD", -"HAE","HAF","HAG","HAH","HAI","HAJ","HAK","HAL","HAM","HAN","HAO","HAP","HAQ","HAR","HAS","HAT", -"HAU","HAV","HAW","HAX","HAY","HAZ","HBA","HBB","HBC","HBD","HBE","HBF","HBG","HBH","HBI","HBJ", -"HBK","HBL","HBM","HBN","HBO","HBP","HBQ","HBR","HBS","HBT","HBU","HBV","HBW","HBX","HBY","HBZ", -"HCA","HCB","HCC","HCD","HCE","HCF","HCG","HCH","HCI","HCJ","HCK","HCL","HCM","HCN","HCO","HCP", -"HCQ","HCR","HCS","HCT","HCU","HCV","HCW","HCX","HCY","HCZ","HDA","HDB","HDC","HDD","HDE","HDF", -"HDG","HDH","HDI","HDJ","HDK","HDL","HDM","HDN","HDO","HDP","HDQ","HDR","HDS","HDT","HDU","HDV", -"HDW","HDX","HDY","HDZ","HEA","HEB","HEC","HED","HEE","HEF","HEG","HEH","HEI","HEJ","HEK","HEL", -"HEM","HEN","HEO","HEP","HEQ","HER","HES","HET","HEU","HEV","HEW","HEX","HEY","HEZ","HFA","HFB", -"HFC","HFD","HFE","HFF","HFG","HFH","HFI","HFJ","HFK","HFL","HFM","HFN","HFO","HFP","HFQ","HFR", -"HFS","HFT","HFU","HFV","HFW","HFX","HFY","HFZ","HGA","HGB","HGC","HGD","HGE","HGF","HGG","HGH", -"HGI","HGJ","HGK","HGL","HGM","HGN","HGO","HGP","HGQ","HGR","HGS","HGT","HGU","HGV","HGW","HGX", -"HGY","HGZ","HHA","HHB","HHC","HHD","HHE","HHF","HHG","HHH","HHI","HHJ","HHK","HHL","HHM","HHN", -"HHO","HHP","HHQ","HHR","HHS","HHT","HHU","HHV","HHW","HHX","HHY","HHZ","HIA","HIB","HIC","HID", -"HIE","HIF","HIG","HIH","HII","HIJ","HIK","HIL","HIM","HIN","HIO","HIP","HIQ","HIR","HIS","HIT", -"HIU","HIV","HIW","HIX","HIY","HIZ","HJA","HJB","HJC","HJD","HJE","HJF","HJG","HJH","HJI","HJJ", -"HJK","HJL","HJM","HJN","HJO","HJP","HJQ","HJR","HJS","HJT","HJU","HJV","HJW","HJX","HJY","HJZ", -"HKA","HKB","HKC","HKD","HKE","HKF","HKG","HKH","HKI","HKJ","HKK","HKL","HKM","HKN","HKO","HKP", -"HKQ","HKR","HKS","HKT","HKU","HKV","HKW","HKX","HKY","HKZ","HLA","HLB","HLC","HLD","HLE","HLF", -"HLG","HLH","HLI","HLJ","HLK","HLL","HLM","HLN","HLO","HLP","HLQ","HLR","HLS","HLT","HLU","HLV", -"HLW","HLX","HLY","HLZ","HMA","HMB","HMC","HMD","HME","HMF","HMG","HMH","HMI","HMJ","HMK","HML", -"HMM","HMN","HMO","HMP","HMQ","HMR","HMS","HMT","HMU","HMV","HMW","HMX","HMY","HMZ","HNA","HNB", -"HNC","HND","HNE","HNF","HNG","HNH","HNI","HNJ","HNK","HNL","HNM","HNN","HNO","HNP","HNQ","HNR", -"HNS","HNT","HNU","HNV","HNW","HNX","HNY","HNZ","HOA","HOB","HOC","HOD","HOE","HOF","HOG","HOH", -"HOI","HOJ","HOK","HOL","HOM","HON","HOO","HOP","HOQ","HOR","HOS","HOT","HOU","HOV","HOW","HOX", -"HOY","HOZ","HPA","HPB","HPC","HPD","HPE","HPF","HPG","HPH","HPI","HPJ","HPK","HPL","HPM","HPN", -"HPO","HPP","HPQ","HPR","HPS","HPT","HPU","HPV","HPW","HPX","HPY","HPZ","HQA","HQB","HQC","HQD", -"HQE","HQF","HQG","HQH","HQI","HQJ","HQK","HQL","HQM","HQN","HQO","HQP","HQQ","HQR","HQS","HQT", -"HQU","HQV","HQW","HQX","HQY","HQZ","HRA","HRB","HRC","HRD","HRE","HRF","HRG","HRH","HRI","HRJ", -"HRK","HRL","HRM","HRN","HRO","HRP","HRQ","HRR","HRS","HRT","HRU","HRV","HRW","HRX","HRY","HRZ", -"HSA","HSB","HSC","HSD","HSE","HSF","HSG","HSH","HSI","HSJ","HSK","HSL","HSM","HSN","HSO","HSP", -"HSQ","HSR","HSS","HST","HSU","HSV","HSW","HSX","HSY","HSZ","HTA","HTB","HTC","HTD","HTE","HTF", -"HTG","HTH","HTI","HTJ","HTK","HTL","HTM","HTN","HTO","HTP","HTQ","HTR","HTS","HTT","HTU","HTV", -"HTW","HTX","HTY","HTZ","HUA","HUB","HUC","HUD","HUE","HUF","HUG","HUH","HUI","HUJ","HUK","HUL", -"HUM","HUN","HUO","HUP","HUQ","HUR","HUS","HUT","HUU","HUV","HUW","HUX","HUY","HUZ","HVA","HVB", -"HVC","HVD","HVE","HVF","HVG","HVH","HVI","HVJ","HVK","HVL","HVM","HVN","HVO","HVP","HVQ","HVR", -"HVS","HVT","HVU","HVV","HVW","HVX","HVY","HVZ","HWA","HWB","HWC","HWD","HWE","HWF","HWG","HWH", -"HWI","HWJ","HWK","HWL","HWM","HWN","HWO","HWP","HWQ","HWR","HWS","HWT","HWU","HWV","HWW","HWX", -"HWY","HWZ","HXA","HXB","HXC","HXD","HXE","HXF","HXG","HXH","HXI","HXJ","HXK","HXL","HXM","HXN", -"HXO","HXP","HXQ","HXR","HXS","HXT","HXU","HXV","HXW","HXX","HXY","HXZ","HYA","HYB","HYC","HYD", -"HYE","HYF","HYG","HYH","HYI","HYJ","HYK","HYL","HYM","HYN","HYO","HYP","HYQ","HYR","HYS","HYT", -"HYU","HYV","HYW","HYX","HYY","HYZ","HZA","HZB","HZC","HZD","HZE","HZF","HZG","HZH","HZI","HZJ", -"HZK","HZL","HZM","HZN","HZO","HZP","HZQ","HZR","HZS","HZT","HZU","HZV","HZW","HZX","HZY","HZZ", -"IAA","IAB","IAC","IAD","IAE","IAF","IAG","IAH","IAI","IAJ","IAK","IAL","IAM","IAN","IAO","IAP", -"IAQ","IAR","IAS","IAT","IAU","IAV","IAW","IAX","IAY","IAZ","IBA","IBB","IBC","IBD","IBE","IBF", -"IBG","IBH","IBI","IBJ","IBK","IBL","IBM","IBN","IBO","IBP","IBQ","IBR","IBS","IBT","IBU","IBV", -"IBW","IBX","IBY","IBZ","ICA","ICB","ICC","ICD","ICE","ICF","ICG","ICH","ICI","ICJ","ICK","ICL", -"ICM","ICN","ICO","ICP","ICQ","ICR","ICS","ICT","ICU","ICV","ICW","ICX","ICY","ICZ","IDA","IDB", -"IDC","IDD","IDE","IDF","IDG","IDH","IDI","IDJ","IDK","IDL","IDM","IDN","IDO","IDP","IDQ","IDR", -"IDS","IDT","IDU","IDV","IDW","IDX","IDY","IDZ","IEA","IEB","IEC","IED","IEE","IEF","IEG","IEH", -"IEI","IEJ","IEK","IEL","IEM","IEN","IEO","IEP","IEQ","IER","IES","IET","IEU","IEV","IEW","IEX", -"IEY","IEZ","IFA","IFB","IFC","IFD","IFE","IFF","IFG","IFH","IFI","IFJ","IFK","IFL","IFM","IFN", -"IFO","IFP","IFQ","IFR","IFS","IFT","IFU","IFV","IFW","IFX","IFY","IFZ","IGA","IGB","IGC","IGD", -"IGE","IGF","IGG","IGH","IGI","IGJ","IGK","IGL","IGM","IGN","IGO","IGP","IGQ","IGR","IGS","IGT", -"IGU","IGV","IGW","IGX","IGY","IGZ","IHA","IHB","IHC","IHD","IHE","IHF","IHG","IHH","IHI","IHJ", -"IHK","IHL","IHM","IHN","IHO","IHP","IHQ","IHR","IHS","IHT","IHU","IHV","IHW","IHX","IHY","IHZ", -"IIA","IIB","IIC","IID","IIE","IIF","IIG","IIH","III","IIJ","IIK","IIL","IIM","IIN","IIO","IIP", -"IIQ","IIR","IIS","IIT","IIU","IIV","IIW","IIX","IIY","IIZ","IJA","IJB","IJC","IJD","IJE","IJF", -"IJG","IJH","IJI","IJJ","IJK","IJL","IJM","IJN","IJO","IJP","IJQ","IJR","IJS","IJT","IJU","IJV", -"IJW","IJX","IJY","IJZ","IKA","IKB","IKC","IKD","IKE","IKF","IKG","IKH","IKI","IKJ","IKK","IKL", -"IKM","IKN","IKO","IKP","IKQ","IKR","IKS","IKT","IKU","IKV","IKW","IKX","IKY","IKZ","ILA","ILB", -"ILC","ILD","ILE","ILF","ILG","ILH","ILI","ILJ","ILK","ILL","ILM","ILN","ILO","ILP","ILQ","ILR", -"ILS","ILT","ILU","ILV","ILW","ILX","ILY","ILZ","IMA","IMB","IMC","IMD","IME","IMF","IMG","IMH", -"IMI","IMJ","IMK","IML","IMM","IMN","IMO","IMP","IMQ","IMR","IMS","IMT","IMU","IMV","IMW","IMX", -"IMY","IMZ","INA","INB","INC","IND","INE","INF","ING","INH","INI","INJ","INK","INL","INM","INN", -"INO","INP","INQ","INR","INS","INT","INU","INV","INW","INX","INY","INZ","IOA","IOB","IOC","IOD", -"IOE","IOF","IOG","IOH","IOI","IOJ","IOK","IOL","IOM","ION","IOO","IOP","IOQ","IOR","IOS","IOT", -"IOU","IOV","IOW","IOX","IOY","IOZ","IPA","IPB","IPC","IPD","IPE","IPF","IPG","IPH","IPI","IPJ", -"IPK","IPL","IPM","IPN","IPO","IPP","IPQ","IPR","IPS","IPT","IPU","IPV","IPW","IPX","IPY","IPZ", -"IQA","IQB","IQC","IQD","IQE","IQF","IQG","IQH","IQI","IQJ","IQK","IQL","IQM","IQN","IQO","IQP", -"IQQ","IQR","IQS","IQT","IQU","IQV","IQW","IQX","IQY","IQZ","IRA","IRB","IRC","IRD","IRE","IRF", -"IRG","IRH","IRI","IRJ","IRK","IRL","IRM","IRN","IRO","IRP","IRQ","IRR","IRS","IRT","IRU","IRV", -"IRW","IRX","IRY","IRZ","ISA","ISB","ISC","ISD","ISE","ISF","ISG","ISH","ISI","ISJ","ISK","ISL", -"ISM","ISN","ISO","ISP","ISQ","ISR","ISS","IST","ISU","ISV","ISW","ISX","ISY","ISZ","ITA","ITB", -"ITC","ITD","ITE","ITF","ITG","ITH","ITI","ITJ","ITK","ITL","ITM","ITN","ITO","ITP","ITQ","ITR", -"ITS","ITT","ITU","ITV","ITW","ITX","ITY","ITZ","IUA","IUB","IUC","IUD","IUE","IUF","IUG","IUH", -"IUI","IUJ","IUK","IUL","IUM","IUN","IUO","IUP","IUQ","IUR","IUS","IUT","IUU","IUV","IUW","IUX", -"IUY","IUZ","IVA","IVB","IVC","IVD","IVE","IVF","IVG","IVH","IVI","IVJ","IVK","IVL","IVM","IVN", -"IVO","IVP","IVQ","IVR","IVS","IVT","IVU","IVV","IVW","IVX","IVY","IVZ","IWA","IWB","IWC","IWD", -"IWE","IWF","IWG","IWH","IWI","IWJ","IWK","IWL","IWM","IWN","IWO","IWP","IWQ","IWR","IWS","IWT", -"IWU","IWV","IWW","IWX","IWY","IWZ","IXA","IXB","IXC","IXD","IXE","IXF","IXG","IXH","IXI","IXJ", -"IXK","IXL","IXM","IXN","IXO","IXP","IXQ","IXR","IXS","IXT","IXU","IXV","IXW","IXX","IXY","IXZ", -"IYA","IYB","IYC","IYD","IYE","IYF","IYG","IYH","IYI","IYJ","IYK","IYL","IYM","IYN","IYO","IYP", -"IYQ","IYR","IYS","IYT","IYU","IYV","IYW","IYX","IYY","IYZ","IZA","IZB","IZC","IZD","IZE","IZF", -"IZG","IZH","IZI","IZJ","IZK","IZL","IZM","IZN","IZO","IZP","IZQ","IZR","IZS","IZT","IZU","IZV", -"IZW","IZX","IZY","IZZ","JAA","JAB","JAC","JAD","JAE","JAF","JAG","JAH","JAI","JAJ","JAK","JAL", -"JAM","JAN","JAO","JAP","JAQ","JAR","JAS","JAT","JAU","JAV","JAW","JAX","JAY","JAZ","JBA","JBB", -"JBC","JBD","JBE","JBF","JBG","JBH","JBI","JBJ","JBK","JBL","JBM","JBN","JBO","JBP","JBQ","JBR", -"JBS","JBT","JBU","JBV","JBW","JBX","JBY","JBZ","JCA","JCB","JCC","JCD","JCE","JCF","JCG","JCH", -"JCI","JCJ","JCK","JCL","JCM","JCN","JCO","JCP","JCQ","JCR","JCS","JCT","JCU","JCV","JCW","JCX", -"JCY","JCZ","JDA","JDB","JDC","JDD","JDE","JDF","JDG","JDH","JDI","JDJ","JDK","JDL","JDM","JDN", -"JDO","JDP","JDQ","JDR","JDS","JDT","JDU","JDV","JDW","JDX","JDY","JDZ","JEA","JEB","JEC","JED", -"JEE","JEF","JEG","JEH","JEI","JEJ","JEK","JEL","JEM","JEN","JEO","JEP","JEQ","JER","JES","JET", -"JEU","JEV","JEW","JEX","JEY","JEZ","JFA","JFB","JFC","JFD","JFE","JFF","JFG","JFH","JFI","JFJ", -"JFK","JFL","JFM","JFN","JFO","JFP","JFQ","JFR","JFS","JFT","JFU","JFV","JFW","JFX","JFY","JFZ", -"JGA","JGB","JGC","JGD","JGE","JGF","JGG","JGH","JGI","JGJ","JGK","JGL","JGM","JGN","JGO","JGP", -"JGQ","JGR","JGS","JGT","JGU","JGV","JGW","JGX","JGY","JGZ","JHA","JHB","JHC","JHD","JHE","JHF", -"JHG","JHH","JHI","JHJ","JHK","JHL","JHM","JHN","JHO","JHP","JHQ","JHR","JHS","JHT","JHU","JHV", -"JHW","JHX","JHY","JHZ","JIA","JIB","JIC","JID","JIE","JIF","JIG","JIH","JII","JIJ","JIK","JIL", -"JIM","JIN","JIO","JIP","JIQ","JIR","JIS","JIT","JIU","JIV","JIW","JIX","JIY","JIZ","JJA","JJB", -"JJC","JJD","JJE","JJF","JJG","JJH","JJI","JJJ","JJK","JJL","JJM","JJN","JJO","JJP","JJQ","JJR", -"JJS","JJT","JJU","JJV","JJW","JJX","JJY","JJZ","JKA","JKB","JKC","JKD","JKE","JKF","JKG","JKH", -"JKI","JKJ","JKK","JKL","JKM","JKN","JKO","JKP","JKQ","JKR","JKS","JKT","JKU","JKV","JKW","JKX", -"JKY","JKZ","JLA","JLB","JLC","JLD","JLE","JLF","JLG","JLH","JLI","JLJ","JLK","JLL","JLM","JLN", -"JLO","JLP","JLQ","JLR","JLS","JLT","JLU","JLV","JLW","JLX","JLY","JLZ","JMA","JMB","JMC","JMD", -"JME","JMF","JMG","JMH","JMI","JMJ","JMK","JML","JMM","JMN","JMO","JMP","JMQ","JMR","JMS","JMT", -"JMU","JMV","JMW","JMX","JMY","JMZ","JNA","JNB","JNC","JND","JNE","JNF","JNG","JNH","JNI","JNJ", -"JNK","JNL","JNM","JNN","JNO","JNP","JNQ","JNR","JNS","JNT","JNU","JNV","JNW","JNX","JNY","JNZ", -"JOA","JOB","JOC","JOD","JOE","JOF","JOG","JOH","JOI","JOJ","JOK","JOL","JOM","JON","JOO","JOP", -"JOQ","JOR","JOS","JOT","JOU","JOV","JOW","JOX","JOY","JOZ","JPA","JPB","JPC","JPD","JPE","JPF", -"JPG","JPH","JPI","JPJ","JPK","JPL","JPM","JPN","JPO","JPP","JPQ","JPR","JPS","JPT","JPU","JPV", -"JPW","JPX","JPY","JPZ","JQA","JQB","JQC","JQD","JQE","JQF","JQG","JQH","JQI","JQJ","JQK","JQL", -"JQM","JQN","JQO","JQP","JQQ","JQR","JQS","JQT","JQU","JQV","JQW","JQX","JQY","JQZ","JRA","JRB", -"JRC","JRD","JRE","JRF","JRG","JRH","JRI","JRJ","JRK","JRL","JRM","JRN","JRO","JRP","JRQ","JRR", -"JRS","JRT","JRU","JRV","JRW","JRX","JRY","JRZ","JSA","JSB","JSC","JSD","JSE","JSF","JSG","JSH", -"JSI","JSJ","JSK","JSL","JSM","JSN","JSO","JSP","JSQ","JSR","JSS","JST","JSU","JSV","JSW","JSX", -"JSY","JSZ","JTA","JTB","JTC","JTD","JTE","JTF","JTG","JTH","JTI","JTJ","JTK","JTL","JTM","JTN", -"JTO","JTP","JTQ","JTR","JTS","JTT","JTU","JTV","JTW","JTX","JTY","JTZ","JUA","JUB","JUC","JUD", -"JUE","JUF","JUG","JUH","JUI","JUJ","JUK","JUL","JUM","JUN","JUO","JUP","JUQ","JUR","JUS","JUT", -"JUU","JUV","JUW","JUX","JUY","JUZ","JVA","JVB","JVC","JVD","JVE","JVF","JVG","JVH","JVI","JVJ", -"JVK","JVL","JVM","JVN","JVO","JVP","JVQ","JVR","JVS","JVT","JVU","JVV","JVW","JVX","JVY","JVZ", -"JWA","JWB","JWC","JWD","JWE","JWF","JWG","JWH","JWI","JWJ","JWK","JWL","JWM","JWN","JWO","JWP", -"JWQ","JWR","JWS","JWT","JWU","JWV","JWW","JWX","JWY","JWZ","JXA","JXB","JXC","JXD","JXE","JXF", -"JXG","JXH","JXI","JXJ","JXK","JXL","JXM","JXN","JXO","JXP","JXQ","JXR","JXS","JXT","JXU","JXV", -"JXW","JXX","JXY","JXZ","JYA","JYB","JYC","JYD","JYE","JYF","JYG","JYH","JYI","JYJ","JYK","JYL", -"JYM","JYN","JYO","JYP","JYQ","JYR","JYS","JYT","JYU","JYV","JYW","JYX","JYY","JYZ","JZA","JZB", -"JZC","JZD","JZE","JZF","JZG","JZH","JZI","JZJ","JZK","JZL","JZM","JZN","JZO","JZP","JZQ","JZR", -"JZS","JZT","JZU","JZV","JZW","JZX","JZY","JZZ","KAA","KAB","KAC","KAD","KAE","KAF","KAG","KAH", -"KAI","KAJ","KAK","KAL","KAM","KAN","KAO","KAP","KAQ","KAR","KAS","KAT","KAU","KAV","KAW","KAX", -"KAY","KAZ","KBA","KBB","KBC","KBD","KBE","KBF","KBG","KBH","KBI","KBJ","KBK","KBL","KBM","KBN", -"KBO","KBP","KBQ","KBR","KBS","KBT","KBU","KBV","KBW","KBX","KBY","KBZ","KCA","KCB","KCC","KCD", -"KCE","KCF","KCG","KCH","KCI","KCJ","KCK","KCL","KCM","KCN","KCO","KCP","KCQ","KCR","KCS","KCT", -"KCU","KCV","KCW","KCX","KCY","KCZ","KDA","KDB","KDC","KDD","KDE","KDF","KDG","KDH","KDI","KDJ", -"KDK","KDL","KDM","KDN","KDO","KDP","KDQ","KDR","KDS","KDT","KDU","KDV","KDW","KDX","KDY","KDZ", -"KEA","KEB","KEC","KED","KEE","KEF","KEG","KEH","KEI","KEJ","KEK","KEL","KEM","KEN","KEO","KEP", -"KEQ","KER","KES","KET","KEU","KEV","KEW","KEX","KEY","KEZ","KFA","KFB","KFC","KFD","KFE","KFF", -"KFG","KFH","KFI","KFJ","KFK","KFL","KFM","KFN","KFO","KFP","KFQ","KFR","KFS","KFT","KFU","KFV", -"KFW","KFX","KFY","KFZ","KGA","KGB","KGC","KGD","KGE","KGF","KGG","KGH","KGI","KGJ","KGK","KGL", -"KGM","KGN","KGO","KGP","KGQ","KGR","KGS","KGT","KGU","KGV","KGW","KGX","KGY","KGZ","KHA","KHB", -"KHC","KHD","KHE","KHF","KHG","KHH","KHI","KHJ","KHK","KHL","KHM","KHN","KHO","KHP","KHQ","KHR", -"KHS","KHT","KHU","KHV","KHW","KHX","KHY","KHZ","KIA","KIB","KIC","KID","KIE","KIF","KIG","KIH", -"KII","KIJ","KIK","KIL","KIM","KIN","KIO","KIP","KIQ","KIR","KIS","KIT","KIU","KIV","KIW","KIX", -"KIY","KIZ","KJA","KJB","KJC","KJD","KJE","KJF","KJG","KJH","KJI","KJJ","KJK","KJL","KJM","KJN", -"KJO","KJP","KJQ","KJR","KJS","KJT","KJU","KJV","KJW","KJX","KJY","KJZ","KKA","KKB","KKC","KKD", -"KKE","KKF","KKG","KKH","KKI","KKJ","KKK","KKL","KKM","KKN","KKO","KKP","KKQ","KKR","KKS","KKT", -"KKU","KKV","KKW","KKX","KKY","KKZ","KLA","KLB","KLC","KLD","KLE","KLF","KLG","KLH","KLI","KLJ", -"KLK","KLL","KLM","KLN","KLO","KLP","KLQ","KLR","KLS","KLT","KLU","KLV","KLW","KLX","KLY","KLZ", -"KMA","KMB","KMC","KMD","KME","KMF","KMG","KMH","KMI","KMJ","KMK","KML","KMM","KMN","KMO","KMP", -"KMQ","KMR","KMS","KMT","KMU","KMV","KMW","KMX","KMY","KMZ","KNA","KNB","KNC","KND","KNE","KNF", -"KNG","KNH","KNI","KNJ","KNK","KNL","KNM","KNN","KNO","KNP","KNQ","KNR","KNS","KNT","KNU","KNV", -"KNW","KNX","KNY","KNZ","KOA","KOB","KOC","KOD","KOE","KOF","KOG","KOH","KOI","KOJ","KOK","KOL", -"KOM","KON","KOO","KOP","KOQ","KOR","KOS","KOT","KOU","KOV","KOW","KOX","KOY","KOZ","KPA","KPB", -"KPC","KPD","KPE","KPF","KPG","KPH","KPI","KPJ","KPK","KPL","KPM","KPN","KPO","KPP","KPQ","KPR", -"KPS","KPT","KPU","KPV","KPW","KPX","KPY","KPZ","KQA","KQB","KQC","KQD","KQE","KQF","KQG","KQH", -"KQI","KQJ","KQK","KQL","KQM","KQN","KQO","KQP","KQQ","KQR","KQS","KQT","KQU","KQV","KQW","KQX", -"KQY","KQZ","KRA","KRB","KRC","KRD","KRE","KRF","KRG","KRH","KRI","KRJ","KRK","KRL","KRM","KRN", -"KRO","KRP","KRQ","KRR","KRS","KRT","KRU","KRV","KRW","KRX","KRY","KRZ","KSA","KSB","KSC","KSD", -"KSE","KSF","KSG","KSH","KSI","KSJ","KSK","KSL","KSM","KSN","KSO","KSP","KSQ","KSR","KSS","KST", -"KSU","KSV","KSW","KSX","KSY","KSZ","KTA","KTB","KTC","KTD","KTE","KTF","KTG","KTH","KTI","KTJ", -"KTK","KTL","KTM","KTN","KTO","KTP","KTQ","KTR","KTS","KTT","KTU","KTV","KTW","KTX","KTY","KTZ", -"KUA","KUB","KUC","KUD","KUE","KUF","KUG","KUH","KUI","KUJ","KUK","KUL","KUM","KUN","KUO","KUP", -"KUQ","KUR","KUS","KUT","KUU","KUV","KUW","KUX","KUY","KUZ","KVA","KVB","KVC","KVD","KVE","KVF", -"KVG","KVH","KVI","KVJ","KVK","KVL","KVM","KVN","KVO","KVP","KVQ","KVR","KVS","KVT","KVU","KVV", -"KVW","KVX","KVY","KVZ","KWA","KWB","KWC","KWD","KWE","KWF","KWG","KWH","KWI","KWJ","KWK","KWL", -"KWM","KWN","KWO","KWP","KWQ","KWR","KWS","KWT","KWU","KWV","KWW","KWX","KWY","KWZ","KXA","KXB", -"KXC","KXD","KXE","KXF","KXG","KXH","KXI","KXJ","KXK","KXL","KXM","KXN","KXO","KXP","KXQ","KXR", -"KXS","KXT","KXU","KXV","KXW","KXX","KXY","KXZ","KYA","KYB","KYC","KYD","KYE","KYF","KYG","KYH", -"KYI","KYJ","KYK","KYL","KYM","KYN","KYO","KYP","KYQ","KYR","KYS","KYT","KYU","KYV","KYW","KYX", -"KYY","KYZ","KZA","KZB","KZC","KZD","KZE","KZF","KZG","KZH","KZI","KZJ","KZK","KZL","KZM","KZN", -"KZO","KZP","KZQ","KZR","KZS","KZT","KZU","KZV","KZW","KZX","KZY","KZZ","LAA","LAB","LAC","LAD", -"LAE","LAF","LAG","LAH","LAI","LAJ","LAK","LAL","LAM","LAN","LAO","LAP","LAQ","LAR","LAS","LAT", -"LAU","LAV","LAW","LAX","LAY","LAZ","LBA","LBB","LBC","LBD","LBE","LBF","LBG","LBH","LBI","LBJ", -"LBK","LBL","LBM","LBN","LBO","LBP","LBQ","LBR","LBS","LBT","LBU","LBV","LBW","LBX","LBY","LBZ", -"LCA","LCB","LCC","LCD","LCE","LCF","LCG","LCH","LCI","LCJ","LCK","LCL","LCM","LCN","LCO","LCP", -"LCQ","LCR","LCS","LCT","LCU","LCV","LCW","LCX","LCY","LCZ","LDA","LDB","LDC","LDD","LDE","LDF", -"LDG","LDH","LDI","LDJ","LDK","LDL","LDM","LDN","LDO","LDP","LDQ","LDR","LDS","LDT","LDU","LDV", -"LDW","LDX","LDY","LDZ","LEA","LEB","LEC","LED","LEE","LEF","LEG","LEH","LEI","LEJ","LEK","LEL", -"LEM","LEN","LEO","LEP","LEQ","LER","LES","LET","LEU","LEV","LEW","LEX","LEY","LEZ","LFA","LFB", -"LFC","LFD","LFE","LFF","LFG","LFH","LFI","LFJ","LFK","LFL","LFM","LFN","LFO","LFP","LFQ","LFR", -"LFS","LFT","LFU","LFV","LFW","LFX","LFY","LFZ","LGA","LGB","LGC","LGD","LGE","LGF","LGG","LGH", -"LGI","LGJ","LGK","LGL","LGM","LGN","LGO","LGP","LGQ","LGR","LGS","LGT","LGU","LGV","LGW","LGX", -"LGY","LGZ","LHA","LHB","LHC","LHD","LHE","LHF","LHG","LHH","LHI","LHJ","LHK","LHL","LHM","LHN", -"LHO","LHP","LHQ","LHR","LHS","LHT","LHU","LHV","LHW","LHX","LHY","LHZ","LIA","LIB","LIC","LID", -"LIE","LIF","LIG","LIH","LII","LIJ","LIK","LIL","LIM","LIN","LIO","LIP","LIQ","LIR","LIS","LIT", -"LIU","LIV","LIW","LIX","LIY","LIZ","LJA","LJB","LJC","LJD","LJE","LJF","LJG","LJH","LJI","LJJ", -"LJK","LJL","LJM","LJN","LJO","LJP","LJQ","LJR","LJS","LJT","LJU","LJV","LJW","LJX","LJY","LJZ", -"LKA","LKB","LKC","LKD","LKE","LKF","LKG","LKH","LKI","LKJ","LKK","LKL","LKM","LKN","LKO","LKP", -"LKQ","LKR","LKS","LKT","LKU","LKV","LKW","LKX","LKY","LKZ","LLA","LLB","LLC","LLD","LLE","LLF", -"LLG","LLH","LLI","LLJ","LLK","LLL","LLM","LLN","LLO","LLP","LLQ","LLR","LLS","LLT","LLU","LLV", -"LLW","LLX","LLY","LLZ","LMA","LMB","LMC","LMD","LME","LMF","LMG","LMH","LMI","LMJ","LMK","LML", -"LMM","LMN","LMO","LMP","LMQ","LMR","LMS","LMT","LMU","LMV","LMW","LMX","LMY","LMZ","LNA","LNB", -"LNC","LND","LNE","LNF","LNG","LNH","LNI","LNJ","LNK","LNL","LNM","LNN","LNO","LNP","LNQ","LNR", -"LNS","LNT","LNU","LNV","LNW","LNX","LNY","LNZ","LOA","LOB","LOC","LOD","LOE","LOF","LOG","LOH", -"LOI","LOJ","LOK","LOL","LOM","LON","LOO","LOP","LOQ","LOR","LOS","LOT","LOU","LOV","LOW","LOX", -"LOY","LOZ","LPA","LPB","LPC","LPD","LPE","LPF","LPG","LPH","LPI","LPJ","LPK","LPL","LPM","LPN", -"LPO","LPP","LPQ","LPR","LPS","LPT","LPU","LPV","LPW","LPX","LPY","LPZ","LQA","LQB","LQC","LQD", -"LQE","LQF","LQG","LQH","LQI","LQJ","LQK","LQL","LQM","LQN","LQO","LQP","LQQ","LQR","LQS","LQT", -"LQU","LQV","LQW","LQX","LQY","LQZ","LRA","LRB","LRC","LRD","LRE","LRF","LRG","LRH","LRI","LRJ", -"LRK","LRL","LRM","LRN","LRO","LRP","LRQ","LRR","LRS","LRT","LRU","LRV","LRW","LRX","LRY","LRZ", -"LSA","LSB","LSC","LSD","LSE","LSF","LSG","LSH","LSI","LSJ","LSK","LSL","LSM","LSN","LSO","LSP", -"LSQ","LSR","LSS","LST","LSU","LSV","LSW","LSX","LSY","LSZ","LTA","LTB","LTC","LTD","LTE","LTF", -"LTG","LTH","LTI","LTJ","LTK","LTL","LTM","LTN","LTO","LTP","LTQ","LTR","LTS","LTT","LTU","LTV", -"LTW","LTX","LTY","LTZ","LUA","LUB","LUC","LUD","LUE","LUF","LUG","LUH","LUI","LUJ","LUK","LUL", -"LUM","LUN","LUO","LUP","LUQ","LUR","LUS","LUT","LUU","LUV","LUW","LUX","LUY","LUZ","LVA","LVB", -"LVC","LVD","LVE","LVF","LVG","LVH","LVI","LVJ","LVK","LVL","LVM","LVN","LVO","LVP","LVQ","LVR", -"LVS","LVT","LVU","LVV","LVW","LVX","LVY","LVZ","LWA","LWB","LWC","LWD","LWE","LWF","LWG","LWH", -"LWI","LWJ","LWK","LWL","LWM","LWN","LWO","LWP","LWQ","LWR","LWS","LWT","LWU","LWV","LWW","LWX", -"LWY","LWZ","LXA","LXB","LXC","LXD","LXE","LXF","LXG","LXH","LXI","LXJ","LXK","LXL","LXM","LXN", -"LXO","LXP","LXQ","LXR","LXS","LXT","LXU","LXV","LXW","LXX","LXY","LXZ","LYA","LYB","LYC","LYD", -"LYE","LYF","LYG","LYH","LYI","LYJ","LYK","LYL","LYM","LYN","LYO","LYP","LYQ","LYR","LYS","LYT", -"LYU","LYV","LYW","LYX","LYY","LYZ","LZA","LZB","LZC","LZD","LZE","LZF","LZG","LZH","LZI","LZJ", -"LZK","LZL","LZM","LZN","LZO","LZP","LZQ","LZR","LZS","LZT","LZU","LZV","LZW","LZX","LZY","LZZ", -"MAA","MAB","MAC","MAD","MAE","MAF","MAG","MAH","MAI","MAJ","MAK","MAL","MAM","MAN","MAO","MAP", -"MAQ","MAR","MAS","MAT","MAU","MAV","MAW","MAX","MAY","MAZ","MBA","MBB","MBC","MBD","MBE","MBF", -"MBG","MBH","MBI","MBJ","MBK","MBL","MBM","MBN","MBO","MBP","MBQ","MBR","MBS","MBT","MBU","MBV", -"MBW","MBX","MBY","MBZ","MCA","MCB","MCC","MCD","MCE","MCF","MCG","MCH","MCI","MCJ","MCK","MCL", -"MCM","MCN","MCO","MCP","MCQ","MCR","MCS","MCT","MCU","MCV","MCW","MCX","MCY","MCZ","MDA","MDB", -"MDC","MDD","MDE","MDF","MDG","MDH","MDI","MDJ","MDK","MDL","MDM","MDN","MDO","MDP","MDQ","MDR", -"MDS","MDT","MDU","MDV","MDW","MDX","MDY","MDZ","MEA","MEB","MEC","MED","MEE","MEF","MEG","MEH", -"MEI","MEJ","MEK","MEL","MEM","MEN","MEO","MEP","MEQ","MER","MES","MET","MEU","MEV","MEW","MEX", -"MEY","MEZ","MFA","MFB","MFC","MFD","MFE","MFF","MFG","MFH","MFI","MFJ","MFK","MFL","MFM","MFN", -"MFO","MFP","MFQ","MFR","MFS","MFT","MFU","MFV","MFW","MFX","MFY","MFZ","MGA","MGB","MGC","MGD", -"MGE","MGF","MGG","MGH","MGI","MGJ","MGK","MGL","MGM","MGN","MGO","MGP","MGQ","MGR","MGS","MGT", -"MGU","MGV","MGW","MGX","MGY","MGZ","MHA","MHB","MHC","MHD","MHE","MHF","MHG","MHH","MHI","MHJ", -"MHK","MHL","MHM","MHN","MHO","MHP","MHQ","MHR","MHS","MHT","MHU","MHV","MHW","MHX","MHY","MHZ", -"MIA","MIB","MIC","MID","MIE","MIF","MIG","MIH","MII","MIJ","MIK","MIL","MIM","MIN","MIO","MIP", -"MIQ","MIR","MIS","MIT","MIU","MIV","MIW","MIX","MIY","MIZ","MJA","MJB","MJC","MJD","MJE","MJF", -"MJG","MJH","MJI","MJJ","MJK","MJL","MJM","MJN","MJO","MJP","MJQ","MJR","MJS","MJT","MJU","MJV", -"MJW","MJX","MJY","MJZ","MKA","MKB","MKC","MKD","MKE","MKF","MKG","MKH","MKI","MKJ","MKK","MKL", -"MKM","MKN","MKO","MKP","MKQ","MKR","MKS","MKT","MKU","MKV","MKW","MKX","MKY","MKZ","MLA","MLB", -"MLC","MLD","MLE","MLF","MLG","MLH","MLI","MLJ","MLK","MLL","MLM","MLN","MLO","MLP","MLQ","MLR", -"MLS","MLT","MLU","MLV","MLW","MLX","MLY","MLZ","MMA","MMB","MMC","MMD","MME","MMF","MMG","MMH", -"MMI","MMJ","MMK","MML","MMM","MMN","MMO","MMP","MMQ","MMR","MMS","MMT","MMU","MMV","MMW","MMX", -"MMY","MMZ","MNA","MNB","MNC","MND","MNE","MNF","MNG","MNH","MNI","MNJ","MNK","MNL","MNM","MNN", -"MNO","MNP","MNQ","MNR","MNS","MNT","MNU","MNV","MNW","MNX","MNY","MNZ","MOA","MOB","MOC","MOD", -"MOE","MOF","MOG","MOH","MOI","MOJ","MOK","MOL","MOM","MON","MOO","MOP","MOQ","MOR","MOS","MOT", -"MOU","MOV","MOW","MOX","MOY","MOZ","MPA","MPB","MPC","MPD","MPE","MPF","MPG","MPH","MPI","MPJ", -"MPK","MPL","MPM","MPN","MPO","MPP","MPQ","MPR","MPS","MPT","MPU","MPV","MPW","MPX","MPY","MPZ", -"MQA","MQB","MQC","MQD","MQE","MQF","MQG","MQH","MQI","MQJ","MQK","MQL","MQM","MQN","MQO","MQP", -"MQQ","MQR","MQS","MQT","MQU","MQV","MQW","MQX","MQY","MQZ","MRA","MRB","MRC","MRD","MRE","MRF", -"MRG","MRH","MRI","MRJ","MRK","MRL","MRM","MRN","MRO","MRP","MRQ","MRR","MRS","MRT","MRU","MRV", -"MRW","MRX","MRY","MRZ","MSA","MSB","MSC","MSD","MSE","MSF","MSG","MSH","MSI","MSJ","MSK","MSL", -"MSM","MSN","MSO","MSP","MSQ","MSR","MSS","MST","MSU","MSV","MSW","MSX","MSY","MSZ","MTA","MTB", -"MTC","MTD","MTE","MTF","MTG","MTH","MTI","MTJ","MTK","MTL","MTM","MTN","MTO","MTP","MTQ","MTR", -"MTS","MTT","MTU","MTV","MTW","MTX","MTY","MTZ","MUA","MUB","MUC","MUD","MUE","MUF","MUG","MUH", -"MUI","MUJ","MUK","MUL","MUM","MUN","MUO","MUP","MUQ","MUR","MUS","MUT","MUU","MUV","MUW","MUX", -"MUY","MUZ","MVA","MVB","MVC","MVD","MVE","MVF","MVG","MVH","MVI","MVJ","MVK","MVL","MVM","MVN", -"MVO","MVP","MVQ","MVR","MVS","MVT","MVU","MVV","MVW","MVX","MVY","MVZ","MWA","MWB","MWC","MWD", -"MWE","MWF","MWG","MWH","MWI","MWJ","MWK","MWL","MWM","MWN","MWO","MWP","MWQ","MWR","MWS","MWT", -"MWU","MWV","MWW","MWX","MWY","MWZ","MXA","MXB","MXC","MXD","MXE","MXF","MXG","MXH","MXI","MXJ", -"MXK","MXL","MXM","MXN","MXO","MXP","MXQ","MXR","MXS","MXT","MXU","MXV","MXW","MXX","MXY","MXZ", -"MYA","MYB","MYC","MYD","MYE","MYF","MYG","MYH","MYI","MYJ","MYK","MYL","MYM","MYN","MYO","MYP", -"MYQ","MYR","MYS","MYT","MYU","MYV","MYW","MYX","MYY","MYZ","MZA","MZB","MZC","MZD","MZE","MZF", -"MZG","MZH","MZI","MZJ","MZK","MZL","MZM","MZN","MZO","MZP","MZQ","MZR","MZS","MZT","MZU","MZV", -"MZW","MZX","MZY","MZZ","NAA","NAB","NAC","NAD","NAE","NAF","NAG","NAH","NAI","NAJ","NAK","NAL", -"NAM","NAN","NAO","NAP","NAQ","NAR","NAS","NAT","NAU","NAV","NAW","NAX","NAY","NAZ","NBA","NBB", -"NBC","NBD","NBE","NBF","NBG","NBH","NBI","NBJ","NBK","NBL","NBM","NBN","NBO","NBP","NBQ","NBR", -"NBS","NBT","NBU","NBV","NBW","NBX","NBY","NBZ","NCA","NCB","NCC","NCD","NCE","NCF","NCG","NCH", -"NCI","NCJ","NCK","NCL","NCM","NCN","NCO","NCP","NCQ","NCR","NCS","NCT","NCU","NCV","NCW","NCX", -"NCY","NCZ","NDA","NDB","NDC","NDD","NDE","NDF","NDG","NDH","NDI","NDJ","NDK","NDL","NDM","NDN", -"NDO","NDP","NDQ","NDR","NDS","NDT","NDU","NDV","NDW","NDX","NDY","NDZ","NEA","NEB","NEC","NED", -"NEE","NEF","NEG","NEH","NEI","NEJ","NEK","NEL","NEM","NEN","NEO","NEP","NEQ","NER","NES","NET", -"NEU","NEV","NEW","NEX","NEY","NEZ","NFA","NFB","NFC","NFD","NFE","NFF","NFG","NFH","NFI","NFJ", -"NFK","NFL","NFM","NFN","NFO","NFP","NFQ","NFR","NFS","NFT","NFU","NFV","NFW","NFX","NFY","NFZ", -"NGA","NGB","NGC","NGD","NGE","NGF","NGG","NGH","NGI","NGJ","NGK","NGL","NGM","NGN","NGO","NGP", -"NGQ","NGR","NGS","NGT","NGU","NGV","NGW","NGX","NGY","NGZ","NHA","NHB","NHC","NHD","NHE","NHF", -"NHG","NHH","NHI","NHJ","NHK","NHL","NHM","NHN","NHO","NHP","NHQ","NHR","NHS","NHT","NHU","NHV", -"NHW","NHX","NHY","NHZ","NIA","NIB","NIC","NID","NIE","NIF","NIG","NIH","NII","NIJ","NIK","NIL", -"NIM","NIN","NIO","NIP","NIQ","NIR","NIS","NIT","NIU","NIV","NIW","NIX","NIY","NIZ","NJA","NJB", -"NJC","NJD","NJE","NJF","NJG","NJH","NJI","NJJ","NJK","NJL","NJM","NJN","NJO","NJP","NJQ","NJR", -"NJS","NJT","NJU","NJV","NJW","NJX","NJY","NJZ","NKA","NKB","NKC","NKD","NKE","NKF","NKG","NKH", -"NKI","NKJ","NKK","NKL","NKM","NKN","NKO","NKP","NKQ","NKR","NKS","NKT","NKU","NKV","NKW","NKX", -"NKY","NKZ","NLA","NLB","NLC","NLD","NLE","NLF","NLG","NLH","NLI","NLJ","NLK","NLL","NLM","NLN", -"NLO","NLP","NLQ","NLR","NLS","NLT","NLU","NLV","NLW","NLX","NLY","NLZ","NMA","NMB","NMC","NMD", -"NME","NMF","NMG","NMH","NMI","NMJ","NMK","NML","NMM","NMN","NMO","NMP","NMQ","NMR","NMS","NMT", -"NMU","NMV","NMW","NMX","NMY","NMZ","NNA","NNB","NNC","NND","NNE","NNF","NNG","NNH","NNI","NNJ", -"NNK","NNL","NNM","NNN","NNO","NNP","NNQ","NNR","NNS","NNT","NNU","NNV","NNW","NNX","NNY","NNZ", -"NOA","NOB","NOC","NOD","NOE","NOF","NOG","NOH","NOI","NOJ","NOK","NOL","NOM","NON","NOO","NOP", -"NOQ","NOR","NOS","NOT","NOU","NOV","NOW","NOX","NOY","NOZ","NPA","NPB","NPC","NPD","NPE","NPF", -"NPG","NPH","NPI","NPJ","NPK","NPL","NPM","NPN","NPO","NPP","NPQ","NPR","NPS","NPT","NPU","NPV", -"NPW","NPX","NPY","NPZ","NQA","NQB","NQC","NQD","NQE","NQF","NQG","NQH","NQI","NQJ","NQK","NQL", -"NQM","NQN","NQO","NQP","NQQ","NQR","NQS","NQT","NQU","NQV","NQW","NQX","NQY","NQZ","NRA","NRB", -"NRC","NRD","NRE","NRF","NRG","NRH","NRI","NRJ","NRK","NRL","NRM","NRN","NRO","NRP","NRQ","NRR", -"NRS","NRT","NRU","NRV","NRW","NRX","NRY","NRZ","NSA","NSB","NSC","NSD","NSE","NSF","NSG","NSH", -"NSI","NSJ","NSK","NSL","NSM","NSN","NSO","NSP","NSQ","NSR","NSS","NST","NSU","NSV","NSW","NSX", -"NSY","NSZ","NTA","NTB","NTC","NTD","NTE","NTF","NTG","NTH","NTI","NTJ","NTK","NTL","NTM","NTN", -"NTO","NTP","NTQ","NTR","NTS","NTT","NTU","NTV","NTW","NTX","NTY","NTZ","NUA","NUB","NUC","NUD", -"NUE","NUF","NUG","NUH","NUI","NUJ","NUK","NUL","NUM","NUN","NUO","NUP","NUQ","NUR","NUS","NUT", -"NUU","NUV","NUW","NUX","NUY","NUZ","NVA","NVB","NVC","NVD","NVE","NVF","NVG","NVH","NVI","NVJ", -"NVK","NVL","NVM","NVN","NVO","NVP","NVQ","NVR","NVS","NVT","NVU","NVV","NVW","NVX","NVY","NVZ", -"NWA","NWB","NWC","NWD","NWE","NWF","NWG","NWH","NWI","NWJ","NWK","NWL","NWM","NWN","NWO","NWP", -"NWQ","NWR","NWS","NWT","NWU","NWV","NWW","NWX","NWY","NWZ","NXA","NXB","NXC","NXD","NXE","NXF", -"NXG","NXH","NXI","NXJ","NXK","NXL","NXM","NXN","NXO","NXP","NXQ","NXR","NXS","NXT","NXU","NXV", -"NXW","NXX","NXY","NXZ","NYA","NYB","NYC","NYD","NYE","NYF","NYG","NYH","NYI","NYJ","NYK","NYL", -"NYM","NYN","NYO","NYP","NYQ","NYR","NYS","NYT","NYU","NYV","NYW","NYX","NYY","NYZ","NZA","NZB", -"NZC","NZD","NZE","NZF","NZG","NZH","NZI","NZJ","NZK","NZL","NZM","NZN","NZO","NZP","NZQ","NZR", -"NZS","NZT","NZU","NZV","NZW","NZX","NZY","NZZ","OAA","OAB","OAC","OAD","OAE","OAF","OAG","OAH", -"OAI","OAJ","OAK","OAL","OAM","OAN","OAO","OAP","OAQ","OAR","OAS","OAT","OAU","OAV","OAW","OAX", -"OAY","OAZ","OBA","OBB","OBC","OBD","OBE","OBF","OBG","OBH","OBI","OBJ","OBK","OBL","OBM","OBN", -"OBO","OBP","OBQ","OBR","OBS","OBT","OBU","OBV","OBW","OBX","OBY","OBZ","OCA","OCB","OCC","OCD", -"OCE","OCF","OCG","OCH","OCI","OCJ","OCK","OCL","OCM","OCN","OCO","OCP","OCQ","OCR","OCS","OCT", -"OCU","OCV","OCW","OCX","OCY","OCZ","ODA","ODB","ODC","ODD","ODE","ODF","ODG","ODH","ODI","ODJ", -"ODK","ODL","ODM","ODN","ODO","ODP","ODQ","ODR","ODS","ODT","ODU","ODV","ODW","ODX","ODY","ODZ", -"OEA","OEB","OEC","OED","OEE","OEF","OEG","OEH","OEI","OEJ","OEK","OEL","OEM","OEN","OEO","OEP", -"OEQ","OER","OES","OET","OEU","OEV","OEW","OEX","OEY","OEZ","OFA","OFB","OFC","OFD","OFE","OFF", -"OFG","OFH","OFI","OFJ","OFK","OFL","OFM","OFN","OFO","OFP","OFQ","OFR","OFS","OFT","OFU","OFV", -"OFW","OFX","OFY","OFZ","OGA","OGB","OGC","OGD","OGE","OGF","OGG","OGH","OGI","OGJ","OGK","OGL", -"OGM","OGN","OGO","OGP","OGQ","OGR","OGS","OGT","OGU","OGV","OGW","OGX","OGY","OGZ","OHA","OHB", -"OHC","OHD","OHE","OHF","OHG","OHH","OHI","OHJ","OHK","OHL","OHM","OHN","OHO","OHP","OHQ","OHR", -"OHS","OHT","OHU","OHV","OHW","OHX","OHY","OHZ","OIA","OIB","OIC","OID","OIE","OIF","OIG","OIH", -"OII","OIJ","OIK","OIL","OIM","OIN","OIO","OIP","OIQ","OIR","OIS","OIT","OIU","OIV","OIW","OIX", -"OIY","OIZ","OJA","OJB","OJC","OJD","OJE","OJF","OJG","OJH","OJI","OJJ","OJK","OJL","OJM","OJN", -"OJO","OJP","OJQ","OJR","OJS","OJT","OJU","OJV","OJW","OJX","OJY","OJZ","OKA","OKB","OKC","OKD", -"OKE","OKF","OKG","OKH","OKI","OKJ","OKK","OKL","OKM","OKN","OKO","OKP","OKQ","OKR","OKS","OKT", -"OKU","OKV","OKW","OKX","OKY","OKZ","OLA","OLB","OLC","OLD","OLE","OLF","OLG","OLH","OLI","OLJ", -"OLK","OLL","OLM","OLN","OLO","OLP","OLQ","OLR","OLS","OLT","OLU","OLV","OLW","OLX","OLY","OLZ", -"OMA","OMB","OMC","OMD","OME","OMF","OMG","OMH","OMI","OMJ","OMK","OML","OMM","OMN","OMO","OMP", -"OMQ","OMR","OMS","OMT","OMU","OMV","OMW","OMX","OMY","OMZ","ONA","ONB","ONC","OND","ONE","ONF", -"ONG","ONH","ONI","ONJ","ONK","ONL","ONM","ONN","ONO","ONP","ONQ","ONR","ONS","ONT","ONU","ONV", -"ONW","ONX","ONY","ONZ","OOA","OOB","OOC","OOD","OOE","OOF","OOG","OOH","OOI","OOJ","OOK","OOL", -"OOM","OON","OOO","OOP","OOQ","OOR","OOS","OOT","OOU","OOV","OOW","OOX","OOY","OOZ","OPA","OPB", -"OPC","OPD","OPE","OPF","OPG","OPH","OPI","OPJ","OPK","OPL","OPM","OPN","OPO","OPP","OPQ","OPR", -"OPS","OPT","OPU","OPV","OPW","OPX","OPY","OPZ","OQA","OQB","OQC","OQD","OQE","OQF","OQG","OQH", -"OQI","OQJ","OQK","OQL","OQM","OQN","OQO","OQP","OQQ","OQR","OQS","OQT","OQU","OQV","OQW","OQX", -"OQY","OQZ","ORA","ORB","ORC","ORD","ORE","ORF","ORG","ORH","ORI","ORJ","ORK","ORL","ORM","ORN", -"ORO","ORP","ORQ","ORR","ORS","ORT","ORU","ORV","ORW","ORX","ORY","ORZ","OSA","OSB","OSC","OSD", -"OSE","OSF","OSG","OSH","OSI","OSJ","OSK","OSL","OSM","OSN","OSO","OSP","OSQ","OSR","OSS","OST", -"OSU","OSV","OSW","OSX","OSY","OSZ","OTA","OTB","OTC","OTD","OTE","OTF","OTG","OTH","OTI","OTJ", -"OTK","OTL","OTM","OTN","OTO","OTP","OTQ","OTR","OTS","OTT","OTU","OTV","OTW","OTX","OTY","OTZ", -"OUA","OUB","OUC","OUD","OUE","OUF","OUG","OUH","OUI","OUJ","OUK","OUL","OUM","OUN","OUO","OUP", -"OUQ","OUR","OUS","OUT","OUU","OUV","OUW","OUX","OUY","OUZ","OVA","OVB","OVC","OVD","OVE","OVF", -"OVG","OVH","OVI","OVJ","OVK","OVL","OVM","OVN","OVO","OVP","OVQ","OVR","OVS","OVT","OVU","OVV", -"OVW","OVX","OVY","OVZ","OWA","OWB","OWC","OWD","OWE","OWF","OWG","OWH","OWI","OWJ","OWK","OWL", -"OWM","OWN","OWO","OWP","OWQ","OWR","OWS","OWT","OWU","OWV","OWW","OWX","OWY","OWZ","OXA","OXB", -"OXC","OXD","OXE","OXF","OXG","OXH","OXI","OXJ","OXK","OXL","OXM","OXN","OXO","OXP","OXQ","OXR", -"OXS","OXT","OXU","OXV","OXW","OXX","OXY","OXZ","OYA","OYB","OYC","OYD","OYE","OYF","OYG","OYH", -"OYI","OYJ","OYK","OYL","OYM","OYN","OYO","OYP","OYQ","OYR","OYS","OYT","OYU","OYV","OYW","OYX", -"OYY","OYZ","OZA","OZB","OZC","OZD","OZE","OZF","OZG","OZH","OZI","OZJ","OZK","OZL","OZM","OZN", -"OZO","OZP","OZQ","OZR","OZS","OZT","OZU","OZV","OZW","OZX","OZY","OZZ","PAA","PAB","PAC","PAD", -"PAE","PAF","PAG","PAH","PAI","PAJ","PAK","PAL","PAM","PAN","PAO","PAP","PAQ","PAR","PAS","PAT", -"PAU","PAV","PAW","PAX","PAY","PAZ","PBA","PBB","PBC","PBD","PBE","PBF","PBG","PBH","PBI","PBJ", -"PBK","PBL","PBM","PBN","PBO","PBP","PBQ","PBR","PBS","PBT","PBU","PBV","PBW","PBX","PBY","PBZ", -"PCA","PCB","PCC","PCD","PCE","PCF","PCG","PCH","PCI","PCJ","PCK","PCL","PCM","PCN","PCO","PCP", -"PCQ","PCR","PCS","PCT","PCU","PCV","PCW","PCX","PCY","PCZ","PDA","PDB","PDC","PDD","PDE","PDF", -"PDG","PDH","PDI","PDJ","PDK","PDL","PDM","PDN","PDO","PDP","PDQ","PDR","PDS","PDT","PDU","PDV", -"PDW","PDX","PDY","PDZ","PEA","PEB","PEC","PED","PEE","PEF","PEG","PEH","PEI","PEJ","PEK","PEL", -"PEM","PEN","PEO","PEP","PEQ","PER","PES","PET","PEU","PEV","PEW","PEX","PEY","PEZ","PFA","PFB", -"PFC","PFD","PFE","PFF","PFG","PFH","PFI","PFJ","PFK","PFL","PFM","PFN","PFO","PFP","PFQ","PFR", -"PFS","PFT","PFU","PFV","PFW","PFX","PFY","PFZ","PGA","PGB","PGC","PGD","PGE","PGF","PGG","PGH", -"PGI","PGJ","PGK","PGL","PGM","PGN","PGO","PGP","PGQ","PGR","PGS","PGT","PGU","PGV","PGW","PGX", -"PGY","PGZ","PHA","PHB","PHC","PHD","PHE","PHF","PHG","PHH","PHI","PHJ","PHK","PHL","PHM","PHN", -"PHO","PHP","PHQ","PHR","PHS","PHT","PHU","PHV","PHW","PHX","PHY","PHZ","PIA","PIB","PIC","PID", -"PIE","PIF","PIG","PIH","PII","PIJ","PIK","PIL","PIM","PIN","PIO","PIP","PIQ","PIR","PIS","PIT", -"PIU","PIV","PIW","PIX","PIY","PIZ","PJA","PJB","PJC","PJD","PJE","PJF","PJG","PJH","PJI","PJJ", -"PJK","PJL","PJM","PJN","PJO","PJP","PJQ","PJR","PJS","PJT","PJU","PJV","PJW","PJX","PJY","PJZ", -"PKA","PKB","PKC","PKD","PKE","PKF","PKG","PKH","PKI","PKJ","PKK","PKL","PKM","PKN","PKO","PKP", -"PKQ","PKR","PKS","PKT","PKU","PKV","PKW","PKX","PKY","PKZ","PLA","PLB","PLC","PLD","PLE","PLF", -"PLG","PLH","PLI","PLJ","PLK","PLL","PLM","PLN","PLO","PLP","PLQ","PLR","PLS","PLT","PLU","PLV", -"PLW","PLX","PLY","PLZ","PMA","PMB","PMC","PMD","PME","PMF","PMG","PMH","PMI","PMJ","PMK","PML", -"PMM","PMN","PMO","PMP","PMQ","PMR","PMS","PMT","PMU","PMV","PMW","PMX","PMY","PMZ","PNA","PNB", -"PNC","PND","PNE","PNF","PNG","PNH","PNI","PNJ","PNK","PNL","PNM","PNN","PNO","PNP","PNQ","PNR", -"PNS","PNT","PNU","PNV","PNW","PNX","PNY","PNZ","POA","POB","POC","POD","POE","POF","POG","POH", -"POI","POJ","POK","POL","POM","PON","POO","POP","POQ","POR","POS","POT","POU","POV","POW","POX", -"POY","POZ","PPA","PPB","PPC","PPD","PPE","PPF","PPG","PPH","PPI","PPJ","PPK","PPL","PPM","PPN", -"PPO","PPP","PPQ","PPR","PPS","PPT","PPU","PPV","PPW","PPX","PPY","PPZ","PQA","PQB","PQC","PQD", -"PQE","PQF","PQG","PQH","PQI","PQJ","PQK","PQL","PQM","PQN","PQO","PQP","PQQ","PQR","PQS","PQT", -"PQU","PQV","PQW","PQX","PQY","PQZ","PRA","PRB","PRC","PRD","PRE","PRF","PRG","PRH","PRI","PRJ", -"PRK","PRL","PRM","PRN","PRO","PRP","PRQ","PRR","PRS","PRT","PRU","PRV","PRW","PRX","PRY","PRZ", -"PSA","PSB","PSC","PSD","PSE","PSF","PSG","PSH","PSI","PSJ","PSK","PSL","PSM","PSN","PSO","PSP", -"PSQ","PSR","PSS","PST","PSU","PSV","PSW","PSX","PSY","PSZ","PTA","PTB","PTC","PTD","PTE","PTF", -"PTG","PTH","PTI","PTJ","PTK","PTL","PTM","PTN","PTO","PTP","PTQ","PTR","PTS","PTT","PTU","PTV", -"PTW","PTX","PTY","PTZ","PUA","PUB","PUC","PUD","PUE","PUF","PUG","PUH","PUI","PUJ","PUK","PUL", -"PUM","PUN","PUO","PUP","PUQ","PUR","PUS","PUT","PUU","PUV","PUW","PUX","PUY","PUZ","PVA","PVB", -"PVC","PVD","PVE","PVF","PVG","PVH","PVI","PVJ","PVK","PVL","PVM","PVN","PVO","PVP","PVQ","PVR", -"PVS","PVT","PVU","PVV","PVW","PVX","PVY","PVZ","PWA","PWB","PWC","PWD","PWE","PWF","PWG","PWH", -"PWI","PWJ","PWK","PWL","PWM","PWN","PWO","PWP","PWQ","PWR","PWS","PWT","PWU","PWV","PWW","PWX", -"PWY","PWZ","PXA","PXB","PXC","PXD","PXE","PXF","PXG","PXH","PXI","PXJ","PXK","PXL","PXM","PXN", -"PXO","PXP","PXQ","PXR","PXS","PXT","PXU","PXV","PXW","PXX","PXY","PXZ","PYA","PYB","PYC","PYD", -"PYE","PYF","PYG","PYH","PYI","PYJ","PYK","PYL","PYM","PYN","PYO","PYP","PYQ","PYR","PYS","PYT", -"PYU","PYV","PYW","PYX","PYY","PYZ","PZA","PZB","PZC","PZD","PZE","PZF","PZG","PZH","PZI","PZJ", -"PZK","PZL","PZM","PZN","PZO","PZP","PZQ","PZR","PZS","PZT","PZU","PZV","PZW","PZX","PZY","PZZ", -"QAA","QAB","QAC","QAD","QAE","QAF","QAG","QAH","QAI","QAJ","QAK","QAL","QAM","QAN","QAO","QAP", -"QAQ","QAR","QAS","QAT","QAU","QAV","QAW","QAX","QAY","QAZ","QBA","QBB","QBC","QBD","QBE","QBF", -"QBG","QBH","QBI","QBJ","QBK","QBL","QBM","QBN","QBO","QBP","QBQ","QBR","QBS","QBT","QBU","QBV", -"QBW","QBX","QBY","QBZ","QCA","QCB","QCC","QCD","QCE","QCF","QCG","QCH","QCI","QCJ","QCK","QCL", -"QCM","QCN","QCO","QCP","QCQ","QCR","QCS","QCT","QCU","QCV","QCW","QCX","QCY","QCZ","QDA","QDB", -"QDC","QDD","QDE","QDF","QDG","QDH","QDI","QDJ","QDK","QDL","QDM","QDN","QDO","QDP","QDQ","QDR", -"QDS","QDT","QDU","QDV","QDW","QDX","QDY","QDZ","QEA","QEB","QEC","QED","QEE","QEF","QEG","QEH", -"QEI","QEJ","QEK","QEL","QEM","QEN","QEO","QEP","QEQ","QER","QES","QET","QEU","QEV","QEW","QEX", -"QEY","QEZ","QFA","QFB","QFC","QFD","QFE","QFF","QFG","QFH","QFI","QFJ","QFK","QFL","QFM","QFN", -"QFO","QFP","QFQ","QFR","QFS","QFT","QFU","QFV","QFW","QFX","QFY","QFZ","QGA","QGB","QGC","QGD", -"QGE","QGF","QGG","QGH","QGI","QGJ","QGK","QGL","QGM","QGN","QGO","QGP","QGQ","QGR","QGS","QGT", -"QGU","QGV","QGW","QGX","QGY","QGZ","QHA","QHB","QHC","QHD","QHE","QHF","QHG","QHH","QHI","QHJ", -"QHK","QHL","QHM","QHN","QHO","QHP","QHQ","QHR","QHS","QHT","QHU","QHV","QHW","QHX","QHY","QHZ", -"QIA","QIB","QIC","QID","QIE","QIF","QIG","QIH","QII","QIJ","QIK","QIL","QIM","QIN","QIO","QIP", -"QIQ","QIR","QIS","QIT","QIU","QIV","QIW","QIX","QIY","QIZ","QJA","QJB","QJC","QJD","QJE","QJF", -"QJG","QJH","QJI","QJJ","QJK","QJL","QJM","QJN","QJO","QJP","QJQ","QJR","QJS","QJT","QJU","QJV", -"QJW","QJX","QJY","QJZ","QKA","QKB","QKC","QKD","QKE","QKF","QKG","QKH","QKI","QKJ","QKK","QKL", -"QKM","QKN","QKO","QKP","QKQ","QKR","QKS","QKT","QKU","QKV","QKW","QKX","QKY","QKZ","QLA","QLB", -"QLC","QLD","QLE","QLF","QLG","QLH","QLI","QLJ","QLK","QLL","QLM","QLN","QLO","QLP","QLQ","QLR", -"QLS","QLT","QLU","QLV","QLW","QLX","QLY","QLZ","QMA","QMB","QMC","QMD","QME","QMF","QMG","QMH", -"QMI","QMJ","QMK","QML","QMM","QMN","QMO","QMP","QMQ","QMR","QMS","QMT","QMU","QMV","QMW","QMX", -"QMY","QMZ","QNA","QNB","QNC","QND","QNE","QNF","QNG","QNH","QNI","QNJ","QNK","QNL","QNM","QNN", -"QNO","QNP","QNQ","QNR","QNS","QNT","QNU","QNV","QNW","QNX","QNY","QNZ","QOA","QOB","QOC","QOD", -"QOE","QOF","QOG","QOH","QOI","QOJ","QOK","QOL","QOM","QON","QOO","QOP","QOQ","QOR","QOS","QOT", -"QOU","QOV","QOW","QOX","QOY","QOZ","QPA","QPB","QPC","QPD","QPE","QPF","QPG","QPH","QPI","QPJ", -"QPK","QPL","QPM","QPN","QPO","QPP","QPQ","QPR","QPS","QPT","QPU","QPV","QPW","QPX","QPY","QPZ", -"QQA","QQB","QQC","QQD","QQE","QQF","QQG","QQH","QQI","QQJ","QQK","QQL","QQM","QQN","QQO","QQP", -"QQQ","QQR","QQS","QQT","QQU","QQV","QQW","QQX","QQY","QQZ","QRA","QRB","QRC","QRD","QRE","QRF", -"QRG","QRH","QRI","QRJ","QRK","QRL","QRM","QRN","QRO","QRP","QRQ","QRR","QRS","QRT","QRU","QRV", -"QRW","QRX","QRY","QRZ","QSA","QSB","QSC","QSD","QSE","QSF","QSG","QSH","QSI","QSJ","QSK","QSL", -"QSM","QSN","QSO","QSP","QSQ","QSR","QSS","QST","QSU","QSV","QSW","QSX","QSY","QSZ","QTA","QTB", -"QTC","QTD","QTE","QTF","QTG","QTH","QTI","QTJ","QTK","QTL","QTM","QTN","QTO","QTP","QTQ","QTR", -"QTS","QTT","QTU","QTV","QTW","QTX","QTY","QTZ","QUA","QUB","QUC","QUD","QUE","QUF","QUG","QUH", -"QUI","QUJ","QUK","QUL","QUM","QUN","QUO","QUP","QUQ","QUR","QUS","QUT","QUU","QUV","QUW","QUX", -"QUY","QUZ","QVA","QVB","QVC","QVD","QVE","QVF","QVG","QVH","QVI","QVJ","QVK","QVL","QVM","QVN", -"QVO","QVP","QVQ","QVR","QVS","QVT","QVU","QVV","QVW","QVX","QVY","QVZ","QWA","QWB","QWC","QWD", -"QWE","QWF","QWG","QWH","QWI","QWJ","QWK","QWL","QWM","QWN","QWO","QWP","QWQ","QWR","QWS","QWT", -"QWU","QWV","QWW","QWX","QWY","QWZ","QXA","QXB","QXC","QXD","QXE","QXF","QXG","QXH","QXI","QXJ", -"QXK","QXL","QXM","QXN","QXO","QXP","QXQ","QXR","QXS","QXT","QXU","QXV","QXW","QXX","QXY","QXZ", -"QYA","QYB","QYC","QYD","QYE","QYF","QYG","QYH","QYI","QYJ","QYK","QYL","QYM","QYN","QYO","QYP", -"QYQ","QYR","QYS","QYT","QYU","QYV","QYW","QYX","QYY","QYZ","QZA","QZB","QZC","QZD","QZE","QZF", -"QZG","QZH","QZI","QZJ","QZK","QZL","QZM","QZN","QZO","QZP","QZQ","QZR","QZS","QZT","QZU","QZV", -"QZW","QZX","QZY","QZZ","RAA","RAB","RAC","RAD","RAE","RAF","RAG","RAH","RAI","RAJ","RAK","RAL", -"RAM","RAN","RAO","RAP","RAQ","RAR","RAS","RAT","RAU","RAV","RAW","RAX","RAY","RAZ","RBA","RBB", -"RBC","RBD","RBE","RBF","RBG","RBH","RBI","RBJ","RBK","RBL","RBM","RBN","RBO","RBP","RBQ","RBR", -"RBS","RBT","RBU","RBV","RBW","RBX","RBY","RBZ","RCA","RCB","RCC","RCD","RCE","RCF","RCG","RCH", -"RCI","RCJ","RCK","RCL","RCM","RCN","RCO","RCP","RCQ","RCR","RCS","RCT","RCU","RCV","RCW","RCX", -"RCY","RCZ","RDA","RDB","RDC","RDD","RDE","RDF","RDG","RDH","RDI","RDJ","RDK","RDL","RDM","RDN", -"RDO","RDP","RDQ","RDR","RDS","RDT","RDU","RDV","RDW","RDX","RDY","RDZ","REA","REB","REC","RED", -"REE","REF","REG","REH","REI","REJ","REK","REL","REM","REN","REO","REP","REQ","RER","RES","RET", -"REU","REV","REW","REX","REY","REZ","RFA","RFB","RFC","RFD","RFE","RFF","RFG","RFH","RFI","RFJ", -"RFK","RFL","RFM","RFN","RFO","RFP","RFQ","RFR","RFS","RFT","RFU","RFV","RFW","RFX","RFY","RFZ", -"RGA","RGB","RGC","RGD","RGE","RGF","RGG","RGH","RGI","RGJ","RGK","RGL","RGM","RGN","RGO","RGP", -"RGQ","RGR","RGS","RGT","RGU","RGV","RGW","RGX","RGY","RGZ","RHA","RHB","RHC","RHD","RHE","RHF", -"RHG","RHH","RHI","RHJ","RHK","RHL","RHM","RHN","RHO","RHP","RHQ","RHR","RHS","RHT","RHU","RHV", -"RHW","RHX","RHY","RHZ","RIA","RIB","RIC","RID","RIE","RIF","RIG","RIH","RII","RIJ","RIK","RIL", -"RIM","RIN","RIO","RIP","RIQ","RIR","RIS","RIT","RIU","RIV","RIW","RIX","RIY","RIZ","RJA","RJB", -"RJC","RJD","RJE","RJF","RJG","RJH","RJI","RJJ","RJK","RJL","RJM","RJN","RJO","RJP","RJQ","RJR", -"RJS","RJT","RJU","RJV","RJW","RJX","RJY","RJZ","RKA","RKB","RKC","RKD","RKE","RKF","RKG","RKH", -"RKI","RKJ","RKK","RKL","RKM","RKN","RKO","RKP","RKQ","RKR","RKS","RKT","RKU","RKV","RKW","RKX", -"RKY","RKZ","RLA","RLB","RLC","RLD","RLE","RLF","RLG","RLH","RLI","RLJ","RLK","RLL","RLM","RLN", -"RLO","RLP","RLQ","RLR","RLS","RLT","RLU","RLV","RLW","RLX","RLY","RLZ","RMA","RMB","RMC","RMD", -"RME","RMF","RMG","RMH","RMI","RMJ","RMK","RML","RMM","RMN","RMO","RMP","RMQ","RMR","RMS","RMT", -"RMU","RMV","RMW","RMX","RMY","RMZ","RNA","RNB","RNC","RND","RNE","RNF","RNG","RNH","RNI","RNJ", -"RNK","RNL","RNM","RNN","RNO","RNP","RNQ","RNR","RNS","RNT","RNU","RNV","RNW","RNX","RNY","RNZ", -"ROA","ROB","ROC","ROD","ROE","ROF","ROG","ROH","ROI","ROJ","ROK","ROL","ROM","RON","ROO","ROP", -"ROQ","ROR","ROS","ROT","ROU","ROV","ROW","ROX","ROY","ROZ","RPA","RPB","RPC","RPD","RPE","RPF", -"RPG","RPH","RPI","RPJ","RPK","RPL","RPM","RPN","RPO","RPP","RPQ","RPR","RPS","RPT","RPU","RPV", -"RPW","RPX","RPY","RPZ","RQA","RQB","RQC","RQD","RQE","RQF","RQG","RQH","RQI","RQJ","RQK","RQL", -"RQM","RQN","RQO","RQP","RQQ","RQR","RQS","RQT","RQU","RQV","RQW","RQX","RQY","RQZ","RRA","RRB", -"RRC","RRD","RRE","RRF","RRG","RRH","RRI","RRJ","RRK","RRL","RRM","RRN","RRO","RRP","RRQ","RRR", -"RRS","RRT","RRU","RRV","RRW","RRX","RRY","RRZ","RSA","RSB","RSC","RSD","RSE","RSF","RSG","RSH", -"RSI","RSJ","RSK","RSL","RSM","RSN","RSO","RSP","RSQ","RSR","RSS","RST","RSU","RSV","RSW","RSX", -"RSY","RSZ","RTA","RTB","RTC","RTD","RTE","RTF","RTG","RTH","RTI","RTJ","RTK","RTL","RTM","RTN", -"RTO","RTP","RTQ","RTR","RTS","RTT","RTU","RTV","RTW","RTX","RTY","RTZ","RUA","RUB","RUC","RUD", -"RUE","RUF","RUG","RUH","RUI","RUJ","RUK","RUL","RUM","RUN","RUO","RUP","RUQ","RUR","RUS","RUT", -"RUU","RUV","RUW","RUX","RUY","RUZ","RVA","RVB","RVC","RVD","RVE","RVF","RVG","RVH","RVI","RVJ", -"RVK","RVL","RVM","RVN","RVO","RVP","RVQ","RVR","RVS","RVT","RVU","RVV","RVW","RVX","RVY","RVZ", -"RWA","RWB","RWC","RWD","RWE","RWF","RWG","RWH","RWI","RWJ","RWK","RWL","RWM","RWN","RWO","RWP", -"RWQ","RWR","RWS","RWT","RWU","RWV","RWW","RWX","RWY","RWZ","RXA","RXB","RXC","RXD","RXE","RXF", -"RXG","RXH","RXI","RXJ","RXK","RXL","RXM","RXN","RXO","RXP","RXQ","RXR","RXS","RXT","RXU","RXV", -"RXW","RXX","RXY","RXZ","RYA","RYB","RYC","RYD","RYE","RYF","RYG","RYH","RYI","RYJ","RYK","RYL", -"RYM","RYN","RYO","RYP","RYQ","RYR","RYS","RYT","RYU","RYV","RYW","RYX","RYY","RYZ","RZA","RZB", -"RZC","RZD","RZE","RZF","RZG","RZH","RZI","RZJ","RZK","RZL","RZM","RZN","RZO","RZP","RZQ","RZR", -"RZS","RZT","RZU","RZV","RZW","RZX","RZY","RZZ","SAA","SAB","SAC","SAD","SAE","SAF","SAG","SAH", -"SAI","SAJ","SAK","SAL","SAM","SAN","SAO","SAP","SAQ","SAR","SAS","SAT","SAU","SAV","SAW","SAX", -"SAY","SAZ","SBA","SBB","SBC","SBD","SBE","SBF","SBG","SBH","SBI","SBJ","SBK","SBL","SBM","SBN", -"SBO","SBP","SBQ","SBR","SBS","SBT","SBU","SBV","SBW","SBX","SBY","SBZ","SCA","SCB","SCC","SCD", -"SCE","SCF","SCG","SCH","SCI","SCJ","SCK","SCL","SCM","SCN","SCO","SCP","SCQ","SCR","SCS","SCT", -"SCU","SCV","SCW","SCX","SCY","SCZ","SDA","SDB","SDC","SDD","SDE","SDF","SDG","SDH","SDI","SDJ", -"SDK","SDL","SDM","SDN","SDO","SDP","SDQ","SDR","SDS","SDT","SDU","SDV","SDW","SDX","SDY","SDZ", -"SEA","SEB","SEC","SED","SEE","SEF","SEG","SEH","SEI","SEJ","SEK","SEL","SEM","SEN","SEO","SEP", -"SEQ","SER","SES","SET","SEU","SEV","SEW","SEX","SEY","SEZ","SFA","SFB","SFC","SFD","SFE","SFF", -"SFG","SFH","SFI","SFJ","SFK","SFL","SFM","SFN","SFO","SFP","SFQ","SFR","SFS","SFT","SFU","SFV", -"SFW","SFX","SFY","SFZ","SGA","SGB","SGC","SGD","SGE","SGF","SGG","SGH","SGI","SGJ","SGK","SGL", -"SGM","SGN","SGO","SGP","SGQ","SGR","SGS","SGT","SGU","SGV","SGW","SGX","SGY","SGZ","SHA","SHB", -"SHC","SHD","SHE","SHF","SHG","SHH","SHI","SHJ","SHK","SHL","SHM","SHN","SHO","SHP","SHQ","SHR", -"SHS","SHT","SHU","SHV","SHW","SHX","SHY","SHZ","SIA","SIB","SIC","SID","SIE","SIF","SIG","SIH", -"SII","SIJ","SIK","SIL","SIM","SIN","SIO","SIP","SIQ","SIR","SIS","SIT","SIU","SIV","SIW","SIX", -"SIY","SIZ","SJA","SJB","SJC","SJD","SJE","SJF","SJG","SJH","SJI","SJJ","SJK","SJL","SJM","SJN", -"SJO","SJP","SJQ","SJR","SJS","SJT","SJU","SJV","SJW","SJX","SJY","SJZ","SKA","SKB","SKC","SKD", -"SKE","SKF","SKG","SKH","SKI","SKJ","SKK","SKL","SKM","SKN","SKO","SKP","SKQ","SKR","SKS","SKT", -"SKU","SKV","SKW","SKX","SKY","SKZ","SLA","SLB","SLC","SLD","SLE","SLF","SLG","SLH","SLI","SLJ", -"SLK","SLL","SLM","SLN","SLO","SLP","SLQ","SLR","SLS","SLT","SLU","SLV","SLW","SLX","SLY","SLZ", -"SMA","SMB","SMC","SMD","SME","SMF","SMG","SMH","SMI","SMJ","SMK","SML","SMM","SMN","SMO","SMP", -"SMQ","SMR","SMS","SMT","SMU","SMV","SMW","SMX","SMY","SMZ","SNA","SNB","SNC","SND","SNE","SNF", -"SNG","SNH","SNI","SNJ","SNK","SNL","SNM","SNN","SNO","SNP","SNQ","SNR","SNS","SNT","SNU","SNV", -"SNW","SNX","SNY","SNZ","SOA","SOB","SOC","SOD","SOE","SOF","SOG","SOH","SOI","SOJ","SOK","SOL", -"SOM","SON","SOO","SOP","SOQ","SOR","SOS","SOT","SOU","SOV","SOW","SOX","SOY","SOZ","SPA","SPB", -"SPC","SPD","SPE","SPF","SPG","SPH","SPI","SPJ","SPK","SPL","SPM","SPN","SPO","SPP","SPQ","SPR", -"SPS","SPT","SPU","SPV","SPW","SPX","SPY","SPZ","SQA","SQB","SQC","SQD","SQE","SQF","SQG","SQH", -"SQI","SQJ","SQK","SQL","SQM","SQN","SQO","SQP","SQQ","SQR","SQS","SQT","SQU","SQV","SQW","SQX", -"SQY","SQZ","SRA","SRB","SRC","SRD","SRE","SRF","SRG","SRH","SRI","SRJ","SRK","SRL","SRM","SRN", -"SRO","SRP","SRQ","SRR","SRS","SRT","SRU","SRV","SRW","SRX","SRY","SRZ","SSA","SSB","SSC","SSD", -"SSE","SSF","SSG","SSH","SSI","SSJ","SSK","SSL","SSM","SSN","SSO","SSP","SSQ","SSR","SSS","SST", -"SSU","SSV","SSW","SSX","SSY","SSZ","STA","STB","STC","STD","STE","STF","STG","STH","STI","STJ", -"STK","STL","STM","STN","STO","STP","STQ","STR","STS","STT","STU","STV","STW","STX","STY","STZ", -"SUA","SUB","SUC","SUD","SUE","SUF","SUG","SUH","SUI","SUJ","SUK","SUL","SUM","SUN","SUO","SUP", -"SUQ","SUR","SUS","SUT","SUU","SUV","SUW","SUX","SUY","SUZ","SVA","SVB","SVC","SVD","SVE","SVF", -"SVG","SVH","SVI","SVJ","SVK","SVL","SVM","SVN","SVO","SVP","SVQ","SVR","SVS","SVT","SVU","SVV", -"SVW","SVX","SVY","SVZ","SWA","SWB","SWC","SWD","SWE","SWF","SWG","SWH","SWI","SWJ","SWK","SWL", -"SWM","SWN","SWO","SWP","SWQ","SWR","SWS","SWT","SWU","SWV","SWW","SWX","SWY","SWZ","SXA","SXB", -"SXC","SXD","SXE","SXF","SXG","SXH","SXI","SXJ","SXK","SXL","SXM","SXN","SXO","SXP","SXQ","SXR", -"SXS","SXT","SXU","SXV","SXW","SXX","SXY","SXZ","SYA","SYB","SYC","SYD","SYE","SYF","SYG","SYH", -"SYI","SYJ","SYK","SYL","SYM","SYN","SYO","SYP","SYQ","SYR","SYS","SYT","SYU","SYV","SYW","SYX", -"SYY","SYZ","SZA","SZB","SZC","SZD","SZE","SZF","SZG","SZH","SZI","SZJ","SZK","SZL","SZM","SZN", -"SZO","SZP","SZQ","SZR","SZS","SZT","SZU","SZV","SZW","SZX","SZY","SZZ", -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - TAA to TTV - 516 triplets - intentionally omitted - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -"TTW","TTX","TTY","TTZ","TUA","TUB","TUC","TUD","TUE","TUF","TUG","TUH","TUI","TUJ","TUK","TUL", -"TUM","TUN","TUO","TUP","TUQ","TUR","TUS","TUT","TUU","TUV","TUW","TUX","TUY","TUZ","TVA","TVB", -"TVC","TVD","TVE","TVF","TVG","TVH","TVI","TVJ","TVK","TVL","TVM","TVN","TVO","TVP","TVQ","TVR", -"TVS","TVT","TVU","TVV","TVW","TVX","TVY","TVZ","TWA","TWB","TWC","TWD","TWE","TWF","TWG","TWH", -"TWI","TWJ","TWK","TWL","TWM","TWN","TWO","TWP","TWQ","TWR","TWS","TWT","TWU","TWV","TWW","TWX", -"TWY","TWZ","TXA","TXB","TXC","TXD","TXE","TXF","TXG","TXH","TXI","TXJ","TXK","TXL","TXM","TXN", -"TXO","TXP","TXQ","TXR","TXS","TXT","TXU","TXV","TXW","TXX","TXY","TXZ","TYA","TYB","TYC","TYD", -"TYE","TYF","TYG","TYH","TYI","TYJ","TYK","TYL","TYM","TYN","TYO","TYP","TYQ","TYR","TYS","TYT", -"TYU","TYV","TYW","TYX","TYY","TYZ","TZA","TZB","TZC","TZD","TZE","TZF","TZG","TZH","TZI","TZJ", -"TZK","TZL","TZM","TZN","TZO","TZP","TZQ","TZR","TZS","TZT","TZU","TZV","TZW","TZX","TZY","TZZ", -"UAA","UAB","UAC","UAD","UAE","UAF","UAG","UAH","UAI","UAJ","UAK","UAL","UAM","UAN","UAO","UAP", -"UAQ","UAR","UAS","UAT","UAU","UAV","UAW","UAX","UAY","UAZ","UBA","UBB","UBC","UBD","UBE","UBF", -"UBG","UBH","UBI","UBJ","UBK","UBL","UBM","UBN","UBO","UBP","UBQ","UBR","UBS","UBT","UBU","UBV", -"UBW","UBX","UBY","UBZ","UCA","UCB","UCC","UCD","UCE","UCF","UCG","UCH","UCI","UCJ","UCK","UCL", -"UCM","UCN","UCO","UCP","UCQ","UCR","UCS","UCT","UCU","UCV","UCW","UCX","UCY","UCZ","UDA","UDB", -"UDC","UDD","UDE","UDF","UDG","UDH","UDI","UDJ","UDK","UDL","UDM","UDN","UDO","UDP","UDQ","UDR", -"UDS","UDT","UDU","UDV","UDW","UDX","UDY","UDZ","UEA","UEB","UEC","UED","UEE","UEF","UEG","UEH", -"UEI","UEJ","UEK","UEL","UEM","UEN","UEO","UEP","UEQ","UER","UES","UET","UEU","UEV","UEW","UEX", -"UEY","UEZ","UFA","UFB","UFC","UFD","UFE","UFF","UFG","UFH","UFI","UFJ","UFK","UFL","UFM","UFN", -"UFO","UFP","UFQ","UFR","UFS","UFT","UFU","UFV","UFW","UFX","UFY","UFZ","UGA","UGB","UGC","UGD", -"UGE","UGF","UGG","UGH","UGI","UGJ","UGK","UGL","UGM","UGN","UGO","UGP","UGQ","UGR","UGS","UGT", -"UGU","UGV","UGW","UGX","UGY","UGZ","UHA","UHB","UHC","UHD","UHE","UHF","UHG","UHH","UHI","UHJ", -"UHK","UHL","UHM","UHN","UHO","UHP","UHQ","UHR","UHS","UHT","UHU","UHV","UHW","UHX","UHY","UHZ", -"UIA","UIB","UIC","UID","UIE","UIF","UIG","UIH","UII","UIJ","UIK","UIL","UIM","UIN","UIO","UIP", -"UIQ","UIR","UIS","UIT","UIU","UIV","UIW","UIX","UIY","UIZ","UJA","UJB","UJC","UJD","UJE","UJF", -"UJG","UJH","UJI","UJJ","UJK","UJL","UJM","UJN","UJO","UJP","UJQ","UJR","UJS","UJT","UJU","UJV", -"UJW","UJX","UJY","UJZ","UKA","UKB","UKC","UKD","UKE","UKF","UKG","UKH","UKI","UKJ","UKK","UKL", -"UKM","UKN","UKO","UKP","UKQ","UKR","UKS","UKT","UKU","UKV","UKW","UKX","UKY","UKZ","ULA","ULB", -"ULC","ULD","ULE","ULF","ULG","ULH","ULI","ULJ","ULK","ULL","ULM","ULN","ULO","ULP","ULQ","ULR", -"ULS","ULT","ULU","ULV","ULW","ULX","ULY","ULZ","UMA","UMB","UMC","UMD","UME","UMF","UMG","UMH", -"UMI","UMJ","UMK","UML","UMM","UMN","UMO","UMP","UMQ","UMR","UMS","UMT","UMU","UMV","UMW","UMX", -"UMY","UMZ","UNA","UNB","UNC","UND","UNE","UNF","UNG","UNH","UNI","UNJ","UNK","UNL","UNM","UNN", -"UNO","UNP","UNQ","UNR","UNS","UNT","UNU","UNV","UNW","UNX","UNY","UNZ","UOA","UOB","UOC","UOD", -"UOE","UOF","UOG","UOH","UOI","UOJ","UOK","UOL","UOM","UON","UOO","UOP","UOQ","UOR","UOS","UOT", -"UOU","UOV","UOW","UOX","UOY","UOZ","UPA","UPB","UPC","UPD","UPE","UPF","UPG","UPH","UPI","UPJ", -"UPK","UPL","UPM","UPN","UPO","UPP","UPQ","UPR","UPS","UPT","UPU","UPV","UPW","UPX","UPY","UPZ", -"UQA","UQB","UQC","UQD","UQE","UQF","UQG","UQH","UQI","UQJ","UQK","UQL","UQM","UQN","UQO","UQP", -"UQQ","UQR","UQS","UQT","UQU","UQV","UQW","UQX","UQY","UQZ","URA","URB","URC","URD","URE","URF", -"URG","URH","URI","URJ","URK","URL","URM","URN","URO","URP","URQ","URR","URS","URT","URU","URV", -"URW","URX","URY","URZ","USA","USB","USC","USD","USE","USF","USG","USH","USI","USJ","USK","USL", -"USM","USN","USO","USP","USQ","USR","USS","UST","USU","USV","USW","USX","USY","USZ","UTA","UTB", -"UTC","UTD","UTE","UTF","UTG","UTH","UTI","UTJ","UTK","UTL","UTM","UTN","UTO","UTP","UTQ","UTR", -"UTS","UTT","UTU","UTV","UTW","UTX","UTY","UTZ","UUA","UUB","UUC","UUD","UUE","UUF","UUG","UUH", -"UUI","UUJ","UUK","UUL","UUM","UUN","UUO","UUP","UUQ","UUR","UUS","UUT","UUU","UUV","UUW","UUX", -"UUY","UUZ","UVA","UVB","UVC","UVD","UVE","UVF","UVG","UVH","UVI","UVJ","UVK","UVL","UVM","UVN", -"UVO","UVP","UVQ","UVR","UVS","UVT","UVU","UVV","UVW","UVX","UVY","UVZ","UWA","UWB","UWC","UWD", -"UWE","UWF","UWG","UWH","UWI","UWJ","UWK","UWL","UWM","UWN","UWO","UWP","UWQ","UWR","UWS","UWT", -"UWU","UWV","UWW","UWX","UWY","UWZ","UXA","UXB","UXC","UXD","UXE","UXF","UXG","UXH","UXI","UXJ", -"UXK","UXL","UXM","UXN","UXO","UXP","UXQ","UXR","UXS","UXT","UXU","UXV","UXW","UXX","UXY","UXZ", -"UYA","UYB","UYC","UYD","UYE","UYF","UYG","UYH","UYI","UYJ","UYK","UYL","UYM","UYN","UYO","UYP", -"UYQ","UYR","UYS","UYT","UYU","UYV","UYW","UYX","UYY","UYZ","UZA","UZB","UZC","UZD","UZE","UZF", -"UZG","UZH","UZI","UZJ","UZK","UZL","UZM","UZN","UZO","UZP","UZQ","UZR","UZS","UZT","UZU","UZV", -"UZW","UZX","UZY","UZZ","VAA","VAB","VAC","VAD","VAE","VAF","VAG","VAH","VAI","VAJ","VAK","VAL", -"VAM","VAN","VAO","VAP","VAQ","VAR","VAS","VAT","VAU","VAV","VAW","VAX","VAY","VAZ","VBA","VBB", -"VBC","VBD","VBE","VBF","VBG","VBH","VBI","VBJ","VBK","VBL","VBM","VBN","VBO","VBP","VBQ","VBR", -"VBS","VBT","VBU","VBV","VBW","VBX","VBY","VBZ","VCA","VCB","VCC","VCD","VCE","VCF","VCG","VCH", -"VCI","VCJ","VCK","VCL","VCM","VCN","VCO","VCP","VCQ","VCR","VCS","VCT","VCU","VCV","VCW","VCX", -"VCY","VCZ","VDA","VDB","VDC","VDD","VDE","VDF","VDG","VDH","VDI","VDJ","VDK","VDL","VDM","VDN", -"VDO","VDP","VDQ","VDR","VDS","VDT","VDU","VDV","VDW","VDX","VDY","VDZ","VEA","VEB","VEC","VED", -"VEE","VEF","VEG","VEH","VEI","VEJ","VEK","VEL","VEM","VEN","VEO","VEP","VEQ","VER","VES","VET", -"VEU","VEV","VEW","VEX","VEY","VEZ","VFA","VFB","VFC","VFD","VFE","VFF","VFG","VFH","VFI","VFJ", -"VFK","VFL","VFM","VFN","VFO","VFP","VFQ","VFR","VFS","VFT","VFU","VFV","VFW","VFX","VFY","VFZ", -"VGA","VGB","VGC","VGD","VGE","VGF","VGG","VGH","VGI","VGJ","VGK","VGL","VGM","VGN","VGO","VGP", -"VGQ","VGR","VGS","VGT","VGU","VGV","VGW","VGX","VGY","VGZ","VHA","VHB","VHC","VHD","VHE","VHF", -"VHG","VHH","VHI","VHJ","VHK","VHL","VHM","VHN","VHO","VHP","VHQ","VHR","VHS","VHT","VHU","VHV", -"VHW","VHX","VHY","VHZ","VIA","VIB","VIC","VID","VIE","VIF","VIG","VIH","VII","VIJ","VIK","VIL", -"VIM","VIN","VIO","VIP","VIQ","VIR","VIS","VIT","VIU","VIV","VIW","VIX","VIY","VIZ","VJA","VJB", -"VJC","VJD","VJE","VJF","VJG","VJH","VJI","VJJ","VJK","VJL","VJM","VJN","VJO","VJP","VJQ","VJR", -"VJS","VJT","VJU","VJV","VJW","VJX","VJY","VJZ","VKA","VKB","VKC","VKD","VKE","VKF","VKG","VKH", -"VKI","VKJ","VKK","VKL","VKM","VKN","VKO","VKP","VKQ","VKR","VKS","VKT","VKU","VKV","VKW","VKX", -"VKY","VKZ","VLA","VLB","VLC","VLD","VLE","VLF","VLG","VLH","VLI","VLJ","VLK","VLL","VLM","VLN", -"VLO","VLP","VLQ","VLR","VLS","VLT","VLU","VLV","VLW","VLX","VLY","VLZ","VMA","VMB","VMC","VMD", -"VME","VMF","VMG","VMH","VMI","VMJ","VMK","VML","VMM","VMN","VMO","VMP","VMQ","VMR","VMS","VMT", -"VMU","VMV","VMW","VMX","VMY","VMZ","VNA","VNB","VNC","VND","VNE","VNF","VNG","VNH","VNI","VNJ", -"VNK","VNL","VNM","VNN","VNO","VNP","VNQ","VNR","VNS","VNT","VNU","VNV","VNW","VNX","VNY","VNZ", -"VOA","VOB","VOC","VOD","VOE","VOF","VOG","VOH","VOI","VOJ","VOK","VOL","VOM","VON","VOO","VOP", -"VOQ","VOR","VOS","VOT","VOU","VOV","VOW","VOX","VOY","VOZ","VPA","VPB","VPC","VPD","VPE","VPF", -"VPG","VPH","VPI","VPJ","VPK","VPL","VPM","VPN","VPO","VPP","VPQ","VPR","VPS","VPT","VPU","VPV", -"VPW","VPX","VPY","VPZ","VQA","VQB","VQC","VQD","VQE","VQF","VQG","VQH","VQI","VQJ","VQK","VQL", -"VQM","VQN","VQO","VQP","VQQ","VQR","VQS","VQT","VQU","VQV","VQW","VQX","VQY","VQZ","VRA","VRB", -"VRC","VRD","VRE","VRF","VRG","VRH","VRI","VRJ","VRK","VRL","VRM","VRN","VRO","VRP","VRQ","VRR", -"VRS","VRT","VRU","VRV","VRW","VRX","VRY","VRZ","VSA","VSB","VSC","VSD","VSE","VSF","VSG","VSH", -"VSI","VSJ","VSK","VSL","VSM","VSN","VSO","VSP","VSQ","VSR","VSS","VST","VSU","VSV","VSW","VSX", -"VSY","VSZ","VTA","VTB","VTC","VTD","VTE","VTF","VTG","VTH","VTI","VTJ","VTK","VTL","VTM","VTN", -"VTO","VTP","VTQ","VTR","VTS","VTT","VTU","VTV","VTW","VTX","VTY","VTZ","VUA","VUB","VUC","VUD", -"VUE","VUF","VUG","VUH","VUI","VUJ","VUK","VUL","VUM","VUN","VUO","VUP","VUQ","VUR","VUS","VUT", -"VUU","VUV","VUW","VUX","VUY","VUZ","VVA","VVB","VVC","VVD","VVE","VVF","VVG","VVH","VVI","VVJ", -"VVK","VVL","VVM","VVN","VVO","VVP","VVQ","VVR","VVS","VVT","VVU","VVV","VVW","VVX","VVY","VVZ", -"VWA","VWB","VWC","VWD","VWE","VWF","VWG","VWH","VWI","VWJ","VWK","VWL","VWM","VWN","VWO","VWP", -"VWQ","VWR","VWS","VWT","VWU","VWV","VWW","VWX","VWY","VWZ","VXA","VXB","VXC","VXD","VXE","VXF", -"VXG","VXH","VXI","VXJ","VXK","VXL","VXM","VXN","VXO","VXP","VXQ","VXR","VXS","VXT","VXU","VXV", -"VXW","VXX","VXY","VXZ","VYA","VYB","VYC","VYD","VYE","VYF","VYG","VYH","VYI","VYJ","VYK","VYL", -"VYM","VYN","VYO","VYP","VYQ","VYR","VYS","VYT","VYU","VYV","VYW","VYX","VYY","VYZ","VZA","VZB", -"VZC","VZD","VZE","VZF","VZG","VZH","VZI","VZJ","VZK","VZL","VZM","VZN","VZO","VZP","VZQ","VZR", -"VZS","VZT","VZU","VZV","VZW","VZX","VZY","VZZ","WAA","WAB","WAC","WAD","WAE","WAF","WAG","WAH", -"WAI","WAJ","WAK","WAL","WAM","WAN","WAO","WAP","WAQ","WAR","WAS","WAT","WAU","WAV","WAW","WAX", -"WAY","WAZ","WBA","WBB","WBC","WBD","WBE","WBF","WBG","WBH","WBI","WBJ","WBK","WBL","WBM","WBN", -"WBO","WBP","WBQ","WBR","WBS","WBT","WBU","WBV","WBW","WBX","WBY","WBZ","WCA","WCB","WCC","WCD", -"WCE","WCF","WCG","WCH","WCI","WCJ","WCK","WCL","WCM","WCN","WCO","WCP","WCQ","WCR","WCS","WCT", -"WCU","WCV","WCW","WCX","WCY","WCZ","WDA","WDB","WDC","WDD","WDE","WDF","WDG","WDH","WDI","WDJ", -"WDK","WDL","WDM","WDN","WDO","WDP","WDQ","WDR","WDS","WDT","WDU","WDV","WDW","WDX","WDY","WDZ", -"WEA","WEB","WEC","WED","WEE","WEF","WEG","WEH","WEI","WEJ","WEK","WEL","WEM","WEN","WEO","WEP", -"WEQ","WER","WES","WET","WEU","WEV","WEW","WEX","WEY","WEZ","WFA","WFB","WFC","WFD","WFE","WFF", -"WFG","WFH","WFI","WFJ","WFK","WFL","WFM","WFN","WFO","WFP","WFQ","WFR","WFS","WFT","WFU","WFV", -"WFW","WFX","WFY","WFZ","WGA","WGB","WGC","WGD","WGE","WGF","WGG","WGH","WGI","WGJ","WGK","WGL", -"WGM","WGN","WGO","WGP","WGQ","WGR","WGS","WGT","WGU","WGV","WGW","WGX","WGY","WGZ","WHA","WHB", -"WHC","WHD","WHE","WHF","WHG","WHH","WHI","WHJ","WHK","WHL","WHM","WHN","WHO","WHP","WHQ","WHR", -"WHS","WHT","WHU","WHV","WHW","WHX","WHY","WHZ","WIA","WIB","WIC","WID","WIE","WIF","WIG","WIH", -"WII","WIJ","WIK","WIL","WIM","WIN","WIO","WIP","WIQ","WIR","WIS","WIT","WIU","WIV","WIW","WIX", -"WIY","WIZ","WJA","WJB","WJC","WJD","WJE","WJF","WJG","WJH","WJI","WJJ","WJK","WJL","WJM","WJN", -"WJO","WJP","WJQ","WJR","WJS","WJT","WJU","WJV","WJW","WJX","WJY","WJZ","WKA","WKB","WKC","WKD", -"WKE","WKF","WKG","WKH","WKI","WKJ","WKK","WKL","WKM","WKN","WKO","WKP","WKQ","WKR","WKS","WKT", -"WKU","WKV","WKW","WKX","WKY","WKZ","WLA","WLB","WLC","WLD","WLE","WLF","WLG","WLH","WLI","WLJ", -"WLK","WLL","WLM","WLN","WLO","WLP","WLQ","WLR","WLS","WLT","WLU","WLV","WLW","WLX","WLY","WLZ", -"WMA","WMB","WMC","WMD","WME","WMF","WMG","WMH","WMI","WMJ","WMK","WML","WMM","WMN","WMO","WMP", -"WMQ","WMR","WMS","WMT","WMU","WMV","WMW","WMX","WMY","WMZ","WNA","WNB","WNC","WND","WNE","WNF", -"WNG","WNH","WNI","WNJ","WNK","WNL","WNM","WNN","WNO","WNP","WNQ","WNR","WNS","WNT","WNU","WNV", -"WNW","WNX","WNY","WNZ","WOA","WOB","WOC","WOD","WOE","WOF","WOG","WOH","WOI","WOJ","WOK","WOL", -"WOM","WON","WOO","WOP","WOQ","WOR","WOS","WOT","WOU","WOV","WOW","WOX","WOY","WOZ","WPA","WPB", -"WPC","WPD","WPE","WPF","WPG","WPH","WPI","WPJ","WPK","WPL","WPM","WPN","WPO","WPP","WPQ","WPR", -"WPS","WPT","WPU","WPV","WPW","WPX","WPY","WPZ","WQA","WQB","WQC","WQD","WQE","WQF","WQG","WQH", -"WQI","WQJ","WQK","WQL","WQM","WQN","WQO","WQP","WQQ","WQR","WQS","WQT","WQU","WQV","WQW","WQX", -"WQY","WQZ","WRA","WRB","WRC","WRD","WRE","WRF","WRG","WRH","WRI","WRJ","WRK","WRL","WRM","WRN", -"WRO","WRP","WRQ","WRR","WRS","WRT","WRU","WRV","WRW","WRX","WRY","WRZ","WSA","WSB","WSC","WSD", -"WSE","WSF","WSG","WSH","WSI","WSJ","WSK","WSL","WSM","WSN","WSO","WSP","WSQ","WSR","WSS","WST", -"WSU","WSV","WSW","WSX","WSY","WSZ","WTA","WTB","WTC","WTD","WTE","WTF","WTG","WTH","WTI","WTJ", -"WTK","WTL","WTM","WTN","WTO","WTP","WTQ","WTR","WTS","WTT","WTU","WTV","WTW","WTX","WTY","WTZ", -"WUA","WUB","WUC","WUD","WUE","WUF","WUG","WUH","WUI","WUJ","WUK","WUL","WUM","WUN","WUO","WUP", -"WUQ","WUR","WUS","WUT","WUU","WUV","WUW","WUX","WUY","WUZ","WVA","WVB","WVC","WVD","WVE","WVF", -"WVG","WVH","WVI","WVJ","WVK","WVL","WVM","WVN","WVO","WVP","WVQ","WVR","WVS","WVT","WVU","WVV", -"WVW","WVX","WVY","WVZ","WWA","WWB","WWC","WWD","WWE","WWF","WWG","WWH","WWI","WWJ","WWK","WWL", -"WWM","WWN","WWO","WWP","WWQ","WWR","WWS","WWT","WWU","WWV","WWW","WWX","WWY","WWZ","WXA","WXB", -"WXC","WXD","WXE","WXF","WXG","WXH","WXI","WXJ","WXK","WXL","WXM","WXN","WXO","WXP","WXQ","WXR", -"WXS","WXT","WXU","WXV","WXW","WXX","WXY","WXZ","WYA","WYB","WYC","WYD","WYE","WYF","WYG","WYH", -"WYI","WYJ","WYK","WYL","WYM","WYN","WYO","WYP","WYQ","WYR","WYS","WYT","WYU","WYV","WYW","WYX", -"WYY","WYZ","WZA","WZB","WZC","WZD","WZE","WZF","WZG","WZH","WZI","WZJ","WZK","WZL","WZM","WZN", -"WZO","WZP","WZQ","WZR","WZS","WZT","WZU","WZV","WZW","WZX","WZY","WZZ","XAA","XAB","XAC","XAD", -"XAE","XAF","XAG","XAH","XAI","XAJ","XAK","XAL","XAM","XAN","XAO","XAP","XAQ","XAR","XAS","XAT", -"XAU","XAV","XAW","XAX","XAY","XAZ","XBA","XBB","XBC","XBD","XBE","XBF","XBG","XBH","XBI","XBJ", -"XBK","XBL","XBM","XBN","XBO","XBP","XBQ","XBR","XBS","XBT","XBU","XBV","XBW","XBX","XBY","XBZ", -"XCA","XCB","XCC","XCD","XCE","XCF","XCG","XCH","XCI","XCJ","XCK","XCL","XCM","XCN","XCO","XCP", -"XCQ","XCR","XCS","XCT","XCU","XCV","XCW","XCX","XCY","XCZ","XDA","XDB","XDC","XDD","XDE","XDF", -"XDG","XDH","XDI","XDJ","XDK","XDL","XDM","XDN","XDO","XDP","XDQ","XDR","XDS","XDT","XDU","XDV", -"XDW","XDX","XDY","XDZ","XEA","XEB","XEC","XED","XEE","XEF","XEG","XEH","XEI","XEJ","XEK","XEL", -"XEM","XEN","XEO","XEP","XEQ","XER","XES","XET","XEU","XEV","XEW","XEX","XEY","XEZ","XFA","XFB", -"XFC","XFD","XFE","XFF","XFG","XFH","XFI","XFJ","XFK","XFL","XFM","XFN","XFO","XFP","XFQ","XFR", -"XFS","XFT","XFU","XFV","XFW","XFX","XFY","XFZ","XGA","XGB","XGC","XGD","XGE","XGF","XGG","XGH", -"XGI","XGJ","XGK","XGL","XGM","XGN","XGO","XGP","XGQ","XGR","XGS","XGT","XGU","XGV","XGW","XGX", -"XGY","XGZ","XHA","XHB","XHC","XHD","XHE","XHF","XHG","XHH","XHI","XHJ","XHK","XHL","XHM","XHN", -"XHO","XHP","XHQ","XHR","XHS","XHT","XHU","XHV","XHW","XHX","XHY","XHZ","XIA","XIB","XIC","XID", -"XIE","XIF","XIG","XIH","XII","XIJ","XIK","XIL","XIM","XIN","XIO","XIP","XIQ","XIR","XIS","XIT", -"XIU","XIV","XIW","XIX","XIY","XIZ","XJA","XJB","XJC","XJD","XJE","XJF","XJG","XJH","XJI","XJJ", -"XJK","XJL","XJM","XJN","XJO","XJP","XJQ","XJR","XJS","XJT","XJU","XJV","XJW","XJX","XJY","XJZ", -"XKA","XKB","XKC","XKD","XKE","XKF","XKG","XKH","XKI","XKJ","XKK","XKL","XKM","XKN","XKO","XKP", -"XKQ","XKR","XKS","XKT","XKU","XKV","XKW","XKX","XKY","XKZ","XLA","XLB","XLC","XLD","XLE","XLF", -"XLG","XLH","XLI","XLJ","XLK","XLL","XLM","XLN","XLO","XLP","XLQ","XLR","XLS","XLT","XLU","XLV", -"XLW","XLX","XLY","XLZ","XMA","XMB","XMC","XMD","XME","XMF","XMG","XMH","XMI","XMJ","XMK","XML", -"XMM","XMN","XMO","XMP","XMQ","XMR","XMS","XMT","XMU","XMV","XMW","XMX","XMY","XMZ","XNA","XNB", -"XNC","XND","XNE","XNF","XNG","XNH","XNI","XNJ","XNK","XNL","XNM","XNN","XNO","XNP","XNQ","XNR", -"XNS","XNT","XNU","XNV","XNW","XNX","XNY","XNZ","XOA","XOB","XOC","XOD","XOE","XOF","XOG","XOH", -"XOI","XOJ","XOK","XOL","XOM","XON","XOO","XOP","XOQ","XOR","XOS","XOT","XOU","XOV","XOW","XOX", -"XOY","XOZ","XPA","XPB","XPC","XPD","XPE","XPF","XPG","XPH","XPI","XPJ","XPK","XPL","XPM","XPN", -"XPO","XPP","XPQ","XPR","XPS","XPT","XPU","XPV","XPW","XPX","XPY","XPZ","XQA","XQB","XQC","XQD", -"XQE","XQF","XQG","XQH","XQI","XQJ","XQK","XQL","XQM","XQN","XQO","XQP","XQQ","XQR","XQS","XQT", -"XQU","XQV","XQW","XQX","XQY","XQZ","XRA","XRB","XRC","XRD","XRE","XRF","XRG","XRH","XRI","XRJ", -"XRK","XRL","XRM","XRN","XRO","XRP","XRQ","XRR","XRS","XRT","XRU","XRV","XRW","XRX","XRY","XRZ", -"XSA","XSB","XSC","XSD","XSE","XSF","XSG","XSH","XSI","XSJ","XSK","XSL","XSM","XSN","XSO","XSP", -"XSQ","XSR","XSS","XST","XSU","XSV","XSW","XSX","XSY","XSZ","XTA","XTB","XTC","XTD","XTE","XTF", -"XTG","XTH","XTI","XTJ","XTK","XTL","XTM","XTN","XTO","XTP","XTQ","XTR","XTS","XTT","XTU","XTV", -"XTW","XTX","XTY","XTZ","XUA","XUB","XUC","XUD","XUE","XUF","XUG","XUH","XUI","XUJ","XUK","XUL", -"XUM","XUN","XUO","XUP","XUQ","XUR","XUS","XUT","XUU","XUV","XUW","XUX","XUY","XUZ","XVA","XVB", -"XVC","XVD","XVE","XVF","XVG","XVH","XVI","XVJ","XVK","XVL","XVM","XVN","XVO","XVP","XVQ","XVR", -"XVS","XVT","XVU","XVV","XVW","XVX","XVY","XVZ","XWA","XWB","XWC","XWD","XWE","XWF","XWG","XWH", -"XWI","XWJ","XWK","XWL","XWM","XWN","XWO","XWP","XWQ","XWR","XWS","XWT","XWU","XWV","XWW","XWX", -"XWY","XWZ","XXA","XXB","XXC","XXD","XXE","XXF","XXG","XXH","XXI","XXJ","XXK","XXL","XXM","XXN", -"XXO","XXP","XXQ","XXR","XXS","XXT","XXU","XXV","XXW","XXX","XXY","XXZ","XYA","XYB","XYC","XYD", -"XYE","XYF","XYG","XYH","XYI","XYJ","XYK","XYL","XYM","XYN","XYO","XYP","XYQ","XYR","XYS","XYT", -"XYU","XYV","XYW","XYX","XYY","XYZ","XZA","XZB","XZC","XZD","XZE","XZF","XZG","XZH","XZI","XZJ", -"XZK","XZL","XZM","XZN","XZO","XZP","XZQ","XZR","XZS","XZT","XZU","XZV","XZW","XZX","XZY","XZZ", - -"YAA","YAB","YAC","YAD", -"YAE","YAF","YAG","YAH","YAI","YAJ","YAK","YAL","YAM","YAN","YAO","YAP","YAQ","YAR","YAS","YAT", -"YAU","YAV","YAW","YAX","YAY","YAZ","YBA","YBB","YBC","YBD","YBE","YBF","YBG","YBH","YBI","YBJ", -"YBK","YBL","YBM","YBN","YBO","YBP","YBQ","YBR","YBS","YBT","YBU","YBV","YBW","YBX","YBY","YBZ", -"YCA","YCB","YCC","YCD","YCE","YCF","YCG","YCH","YCI","YCJ","YCK","YCL","YCM","YCN","YCO","YCP", -"YCQ","YCR","YCS","YCT","YCU","YCV","YCW","YCX","YCY","YCZ","YDA","YDB","YDC","YDD","YDE","YDF", -"YDG","YDH","YDI","YDJ","YDK","YDL","YDM","YDN","YDO","YDP","YDQ","YDR","YDS","YDT","YDU","YDV", -"YDW","YDX","YDY","YDZ","YEA","YEB","YEC","YED","YEE","YEF","YEG","YEH","YEI","YEJ","YEK","YEL", -"YEM","YEN","YEO","YEP","YEQ","YER","YES","YET","YEU","YEV","YEW","YEX","YEY","YEZ","YFA","YFB", -"YFC","YFD","YFE","YFF","YFG","YFH","YFI","YFJ","YFK","YFL","YFM","YFN","YFO","YFP","YFQ","YFR", -"YFS","YFT","YFU","YFV","YFW","YFX","YFY","YFZ","YGA","YGB","YGC","YGD","YGE","YGF","YGG","YGH", -"YGI","YGJ","YGK","YGL","YGM","YGN","YGO","YGP","YGQ","YGR","YGS","YGT","YGU","YGV","YGW","YGX", -"YGY","YGZ","YHA","YHB","YHC","YHD","YHE","YHF","YHG","YHH","YHI","YHJ","YHK","YHL","YHM","YHN", -"YHO","YHP","YHQ","YHR","YHS","YHT","YHU","YHV","YHW","YHX","YHY","YHZ","YIA","YIB","YIC","YID", -"YIE","YIF","YIG","YIH","YII","YIJ","YIK","YIL","YIM","YIN","YIO","YIP","YIQ","YIR","YIS","YIT", -"YIU","YIV","YIW","YIX","YIY","YIZ","YJA","YJB","YJC","YJD","YJE","YJF","YJG","YJH","YJI","YJJ", -"YJK","YJL","YJM","YJN","YJO","YJP","YJQ","YJR","YJS","YJT","YJU","YJV","YJW","YJX","YJY","YJZ", -"YKA","YKB","YKC","YKD","YKE","YKF","YKG","YKH","YKI","YKJ","YKK","YKL","YKM","YKN","YKO","YKP", -"YKQ","YKR","YKS","YKT","YKU","YKV","YKW","YKX","YKY","YKZ","YLA","YLB","YLC","YLD","YLE","YLF", -"YLG","YLH","YLI","YLJ","YLK","YLL","YLM","YLN","YLO","YLP","YLQ","YLR","YLS","YLT","YLU","YLV", -"YLW","YLX","YLY","YLZ","YMA","YMB","YMC","YMD","YME","YMF","YMG","YMH","YMI","YMJ","YMK","YML", -"YMM","YMN","YMO","YMP","YMQ","YMR","YMS","YMT","YMU","YMV","YMW","YMX","YMY","YMZ","YNA","YNB", -"YNC","YND","YNE","YNF","YNG","YNH","YNI","YNJ","YNK","YNL","YNM","YNN","YNO","YNP","YNQ","YNR", -"YNS","YNT","YNU","YNV","YNW","YNX","YNY","YNZ","YOA","YOB","YOC","YOD","YOE","YOF","YOG","YOH", -"YOI","YOJ","YOK","YOL","YOM","YON","YOO","YOP","YOQ","YOR","YOS","YOT","YOU","YOV","YOW","YOX", -"YOY","YOZ","YPA","YPB","YPC","YPD","YPE","YPF","YPG","YPH","YPI","YPJ","YPK","YPL","YPM","YPN", -"YPO","YPP","YPQ","YPR","YPS","YPT","YPU","YPV","YPW","YPX","YPY","YPZ","YQA","YQB","YQC","YQD", -"YQE","YQF","YQG","YQH","YQI","YQJ","YQK","YQL","YQM","YQN","YQO","YQP","YQQ","YQR","YQS","YQT", -"YQU","YQV","YQW","YQX","YQY","YQZ","YRA","YRB","YRC","YRD","YRE","YRF","YRG","YRH","YRI","YRJ", -"YRK","YRL","YRM","YRN","YRO","YRP","YRQ","YRR","YRS","YRT","YRU","YRV","YRW","YRX","YRY","YRZ", -"YSA","YSB","YSC","YSD","YSE","YSF","YSG","YSH","YSI","YSJ","YSK","YSL","YSM","YSN","YSO","YSP", -"YSQ","YSR","YSS","YST","YSU","YSV","YSW","YSX","YSY","YSZ","YTA","YTB","YTC","YTD","YTE","YTF", -"YTG","YTH","YTI","YTJ","YTK","YTL","YTM","YTN","YTO","YTP","YTQ","YTR","YTS","YTT","YTU","YTV", -"YTW","YTX","YTY","YTZ","YUA","YUB","YUC","YUD","YUE","YUF","YUG","YUH","YUI","YUJ","YUK","YUL", -"YUM","YUN","YUO","YUP","YUQ","YUR","YUS","YUT","YUU","YUV","YUW","YUX","YUY","YUZ","YVA","YVB", -"YVC","YVD","YVE","YVF","YVG","YVH","YVI","YVJ","YVK","YVL","YVM","YVN","YVO","YVP","YVQ","YVR", -"YVS","YVT","YVU","YVV","YVW","YVX","YVY","YVZ","YWA","YWB","YWC","YWD","YWE","YWF","YWG","YWH", -"YWI","YWJ","YWK","YWL","YWM","YWN","YWO","YWP","YWQ","YWR","YWS","YWT","YWU","YWV","YWW","YWX", -"YWY","YWZ","YXA","YXB","YXC","YXD","YXE","YXF","YXG","YXH","YXI","YXJ","YXK","YXL","YXM","YXN", -"YXO","YXP","YXQ","YXR","YXS","YXT","YXU","YXV","YXW","YXX","YXY","YXZ","YYA","YYB","YYC","YYD", -"YYE","YYF","YYG","YYH","YYI","YYJ","YYK","YYL","YYM","YYN","YYO","YYP","YYQ","YYR","YYS","YYT", -"YYU","YYV","YYW","YYX","YYY","YYZ","YZA","YZB","YZC","YZD","YZE","YZF","YZG","YZH","YZI","YZJ", -"YZK","YZL","YZM","YZN","YZO","YZP","YZQ","YZR","YZS","YZT","YZU","YZV","YZW","YZX","YZY","YZZ", - -"ZAA","ZAB","ZAC","ZAD", -"ZAE","ZAF","ZAG","ZAH","ZAI","ZAJ","ZAK","ZAL","ZAM","ZAN","ZAO","ZAP","ZAQ","ZAR","ZAS","ZAT", -"ZAU","ZAV","ZAW","ZAX","ZAY","ZAZ","ZBA","ZBB","ZBC","ZBD","ZBE","ZBF","ZBG","ZBH","ZBI","ZBJ", -"ZBK","ZBL","ZBM","ZBN","ZBO","ZBP","ZBQ","ZBR","ZBS","ZBT","ZBU","ZBV","ZBW","ZBX","ZBY","ZBZ", -"ZCA","ZCB","ZCC","ZCD","ZCE","ZCF","ZCG","ZCH","ZCI","ZCJ","ZCK","ZCL","ZCM","ZCN","ZCO","ZCP", -"ZCQ","ZCR","ZCS","ZCT","ZCU","ZCV","ZCW","ZCX","ZCY","ZCZ","ZDA","ZDB","ZDC","ZDD","ZDE","ZDF", -"ZDG","ZDH","ZDI","ZDJ","ZDK","ZDL","ZDM","ZDN","ZDO","ZDP","ZDQ","ZDR","ZDS","ZDT","ZDU","ZDV", -"ZDW","ZDX","ZDY","ZDZ","ZEA","ZEB","ZEC","ZED","ZEE","ZEF","ZEG","ZEH","ZEI","ZEJ","ZEK","ZEL", -"ZEM","ZEN","ZEO","ZEP","ZEQ","ZER","ZES","ZET","ZEU","ZEV","ZEW","ZEX","ZEY","ZEZ","ZFA","ZFB", -"ZFC","ZFD","ZFE","ZFF","ZFG","ZFH","ZFI","ZFJ","ZFK","ZFL","ZFM","ZFN","ZFO","ZFP","ZFQ","ZFR", -"ZFS","ZFT","ZFU","ZFV","ZFW","ZFX","ZFY","ZFZ","ZGA","ZGB","ZGC","ZGD","ZGE","ZGF","ZGG","ZGH", -"ZGI","ZGJ","ZGK","ZGL","ZGM","ZGN","ZGO","ZGP","ZGQ","ZGR","ZGS","ZGT","ZGU","ZGV","ZGW","ZGX", -"ZGY","ZGZ","ZHA","ZHB","ZHC","ZHD","ZHE","ZHF","ZHG","ZHH","ZHI","ZHJ","ZHK","ZHL","ZHM","ZHN", -"ZHO","ZHP","ZHQ","ZHR","ZHS","ZHT","ZHU","ZHV","ZHW","ZHX","ZHY","ZHZ","ZIA","ZIB","ZIC","ZID", -"ZIE","ZIF","ZIG","ZIH","ZII","ZIJ","ZIK","ZIL","ZIM","ZIN","ZIO","ZIP","ZIQ","ZIR","ZIS","ZIT", -"ZIU","ZIV","ZIW","ZIX","ZIY","ZIZ","ZJA","ZJB","ZJC","ZJD","ZJE","ZJF","ZJG","ZJH","ZJI","ZJJ", -"ZJK","ZJL","ZJM","ZJN","ZJO","ZJP","ZJQ","ZJR","ZJS","ZJT","ZJU","ZJV","ZJW","ZJX","ZJY","ZJZ", -"ZKA","ZKB","ZKC","ZKD","ZKE","ZKF","ZKG","ZKH","ZKI","ZKJ","ZKK","ZKL","ZKM","ZKN","ZKO","ZKP", -"ZKQ","ZKR","ZKS","ZKT","ZKU","ZKV","ZKW","ZKX","ZKY","ZKZ","ZLA","ZLB","ZLC","ZLD","ZLE","ZLF", -"ZLG","ZLH","ZLI","ZLJ","ZLK","ZLL","ZLM","ZLN","ZLO","ZLP","ZLQ","ZLR","ZLS","ZLT","ZLU","ZLV", -"ZLW","ZLX","ZLY","ZLZ","ZMA","ZMB","ZMC","ZMD","ZME","ZMF","ZMG","ZMH","ZMI","ZMJ","ZMK","ZML", -"ZMM","ZMN","ZMO","ZMP","ZMQ","ZMR","ZMS","ZMT","ZMU","ZMV","ZMW","ZMX","ZMY","ZMZ","ZNA","ZNB", -"ZNC","ZND","ZNE","ZNF","ZNG","ZNH","ZNI","ZNJ","ZNK","ZNL","ZNM","ZNN","ZNO","ZNP","ZNQ","ZNR", -"ZNS","ZNT","ZNU","ZNV","ZNW","ZNX","ZNY","ZNZ","ZOA","ZOB","ZOC","ZOD","ZOE","ZOF","ZOG","ZOH", -"ZOI","ZOJ","ZOK","ZOL","ZOM","ZON","ZOO","ZOP","ZOQ","ZOR","ZOS","ZOT","ZOU","ZOV","ZOW","ZOX", -"ZOY","ZOZ","ZPA","ZPB","ZPC","ZPD","ZPE","ZPF","ZPG","ZPH","ZPI","ZPJ","ZPK","ZPL","ZPM","ZPN", -"ZPO","ZPP","ZPQ","ZPR","ZPS","ZPT","ZPU","ZPV","ZPW","ZPX","ZPY","ZPZ","ZQA","ZQB","ZQC","ZQD", -"ZQE","ZQF","ZQG","ZQH","ZQI","ZQJ","ZQK","ZQL","ZQM","ZQN","ZQO","ZQP","ZQQ","ZQR","ZQS","ZQT", -"ZQU","ZQV","ZQW","ZQX","ZQY","ZQZ","ZRA","ZRB","ZRC","ZRD","ZRE","ZRF","ZRG","ZRH","ZRI","ZRJ", -"ZRK","ZRL","ZRM","ZRN","ZRO","ZRP","ZRQ","ZRR","ZRS","ZRT","ZRU","ZRV","ZRW","ZRX","ZRY","ZRZ", -"ZSA","ZSB","ZSC","ZSD","ZSE","ZSF","ZSG","ZSH","ZSI","ZSJ","ZSK","ZSL","ZSM","ZSN","ZSO","ZSP", -"ZSQ","ZSR","ZSS","ZST","ZSU","ZSV","ZSW","ZSX","ZSY","ZSZ","ZTA","ZTB","ZTC","ZTD","ZTE","ZTF", -"ZTG","ZTH","ZTI","ZTJ","ZTK","ZTL","ZTM","ZTN","ZTO","ZTP","ZTQ","ZTR","ZTS","ZTT","ZTU","ZTV", -"ZTW","ZTX","ZTY","ZTZ","ZUA","ZUB","ZUC","ZUD","ZUE","ZUF","ZUG","ZUH","ZUI","ZUJ","ZUK","ZUL", -"ZUM","ZUN","ZUO","ZUP","ZUQ","ZUR","ZUS","ZUT","ZUU","ZUV","ZUW","ZUX","ZUY","ZUZ","ZVA","ZVB", -"ZVC","ZVD","ZVE","ZVF","ZVG","ZVH","ZVI","ZVJ","ZVK","ZVL","ZVM","ZVN","ZVO","ZVP","ZVQ","ZVR", -"ZVS","ZVT","ZVU","ZVV","ZVW","ZVX","ZVY","ZVZ","ZWA","ZWB","ZWC","ZWD","ZWE","ZWF","ZWG","ZWH", -"ZWI","ZWJ","ZWK","ZWL","ZWM","ZWN","ZWO","ZWP","ZWQ","ZWR","ZWS","ZWT","ZWU","ZWV","ZWW","ZWX", -"ZWY","ZWZ","ZXA","ZXB","ZXC","ZXD","ZXE","ZXF","ZXG","ZXH","ZXI","ZXJ","ZXK","ZXL","ZXM","ZXN", -"ZXO","ZXP","ZXQ","ZXR","ZXS","ZXT","ZXU","ZXV","ZXW","ZXX","ZXY","ZXZ","ZYA","ZYB","ZYC","ZYD", -"ZYE","ZYF","ZYG","ZYH","ZYI","ZYJ","ZYK","ZYL","ZYM","ZYN","ZYO","ZYP","ZYQ","ZYR","ZYS","ZYT", -"ZYU","ZYV","ZYW","ZYX","ZYY","ZYZ","ZZA","ZZB","ZZC","ZZD","ZZE","ZZF","ZZG","ZZH","ZZI","ZZJ", -"ZZK","ZZL","ZZM","ZZN","ZZO","ZZP","ZZQ","ZZR","ZZS","ZZT","ZZU","ZZV","ZZW","ZZX","ZZY","ZZZ" -}; - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -static char d26[][3] = -{ -"AA","AB","AC","AD","AE","AF","AG","AH","AI","AJ","AK","AL","AM","AN","AO","AP", -"AQ","AR","AS","AT","AU","AV","AW","AX","AY","AZ","BA","BB","BC","BD","BE","BF", -"BG","BH","BI","BJ","BK","BL","BM","BN","BO","BP","BQ","BR","BS","BT","BU","BV", -"BW","BX","BY","BZ","CA","CB","CC","CD","CE","CF","CG","CH","CI","CJ","CK","CL", -"CM","CN","CO","CP","CQ","CR","CS","CT","CU","CV","CW","CX","CY","CZ","DA","DB", -"DC","DD","DE","DF","DG","DH","DI","DJ","DK","DL","DM","DN","DO","DP","DQ","DR", -"DS","DT","DU","DV","DW","DX","DY","DZ","EA","EB","EC","ED","EE","EF","EG","EH", -"EI","EJ","EK","EL","EM","EN","EO","EP","EQ","ER","ES","ET","EU","EV","EW","EX", -"EY","EZ","FA","FB","FC","FD","FE","FF","FG","FH","FI","FJ","FK","FL","FM","FN", -"FO","FP","FQ","FR","FS","FT","FU","FV","FW","FX","FY","FZ","GA","GB","GC","GD", -"GE","GF","GG","GH","GI","GJ","GK","GL","GM","GN","GO","GP","GQ","GR","GS","GT", -"GU","GV","GW","GX","GY","GZ","HA","HB","HC","HD","HE","HF","HG","HH","HI","HJ", -"HK","HL","HM","HN","HO","HP","HQ","HR","HS","HT","HU","HV","HW","HX","HY","HZ", -"IA","IB","IC","ID","IE","IF","IG","IH","II","IJ","IK","IL","IM","IN","IO","IP", -"IQ","IR","IS","IT","IU","IV","IW","IX","IY","IZ","JA","JB","JC","JD","JE","JF", -"JG","JH","JI","JJ","JK","JL","JM","JN","JO","JP","JQ","JR","JS","JT","JU","JV", -"JW","JX","JY","JZ","KA","KB","KC","KD","KE","KF","KG","KH","KI","KJ","KK","KL", -"KM","KN","KO","KP","KQ","KR","KS","KT","KU","KV","KW","KX","KY","KZ","LA","LB", -"LC","LD","LE","LF","LG","LH","LI","LJ","LK","LL","LM","LN","LO","LP","LQ","LR", -"LS","LT","LU","LV","LW","LX","LY","LZ","MA","MB","MC","MD","ME","MF","MG","MH", -"MI","MJ","MK","ML","MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV","MW","MX", -"MY","MZ","NA","NB","NC","ND","NE","NF","NG","NH","NI","NJ","NK","NL","NM","NN", -"NO","NP","NQ","NR","NS","NT","NU","NV","NW","NX","NY","NZ","OA","OB","OC","OD", -"OE","OF","OG","OH","OI","OJ","OK","OL","OM","ON","OO","OP","OQ","OR","OS","OT", -"OU","OV","OW","OX","OY","OZ","PA","PB","PC","PD","PE","PF","PG","PH","PI","PJ", -"PK","PL","PM","PN","PO","PP","PQ","PR","PS","PT","PU","PV","PW","PX","PY","PZ", -"QA","QB","QC","QD","QE","QF","QG","QH","QI","QJ","QK","QL","QM","QN","QO","QP", -"QQ","QR","QS","QT","QU","QV","QW","QX","QY","QZ","RA","RB","RC","RD","RE","RF", -"RG","RH","RI","RJ","RK","RL","RM","RN","RO","RP","RQ","RR","RS","RT","RU","RV", -"RW","RX","RY","RZ","SA","SB","SC","SD","SE","SF","SG","SH","SI","SJ","SK","SL", -"SM","SN","SO","SP","SQ","SR","SS","ST","SU","SV","SW","SX","SY","SZ","TA","TB", -"TC","TD","TE","TF","TG","TH","TI","TJ","TK","TL","TM","TN","TO","TP","TQ","TR", -"TS","TT","TU","TV","TW","TX","TY","TZ","UA","UB","UC","UD","UE","UF","UG","UH", -"UI","UJ","UK","UL","UM","UN","UO","UP","UQ","UR","US","UT","UU","UV","UW","UX", -"UY","UZ","VA","VB","VC","VD","VE","VF","VG","VH","VI","VJ","VK","VL","VM","VN", -"VO","VP","VQ","VR","VS","VT","VU","VV","VW","VX","VY","VZ","WA","WB","WC","WD", -"WE","WF","WG","WH","WI","WJ","WK","WL","WM","WN","WO","WP","WQ","WR","WS","WT", -"WU","WV","WW","WX","WY","WZ","XA","XB","XC","XD","XE","XF","XG","XH","XI","XJ", -"XK","XL","XM","XN","XO","XP","XQ","XR","XS","XT","XU","XV","XW","XX","XY","XZ", -"YA","YB","YC","YD","YE","YF","YG","YH","YI","YJ","YK","YL","YM","YN","YO","YP", -"YQ","YR","YS","YT","YU","YV","YW","YX","YY","YZ","ZA","ZB","ZC","ZD","ZE","ZF", -"ZG","ZH","ZI","ZJ","ZK","ZL","ZM","ZN","ZO","ZP","ZQ","ZR","ZS","ZT","ZU","ZV", -"ZW","ZX","ZY","ZZ" -}; - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Also tabulate 26 base-26 chars. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -static const char *c26 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; /* added const 2007-09-26 DT */ - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Weight scheme for check character . - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -#define N_UNIQUE_WEIGHTS 12 -static int weights_for_checksum[N_UNIQUE_WEIGHTS] = { 1,3,5,7,9,11,15,17,19,21,23,25 }; - /*^^^ co-primes with 26 which are < 26 */ - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get a character representing 1st 14-bit triplet (bits 0..13 of contiguous array of octets) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -const char* base26_triplet_1(const unsigned char *a) -{ -UINT32 b0, b1,h; - b0 = (UINT32) a[0]; /* 1111 1111 */ -#ifndef FIX_BASE26_ENC_BUG - b1 = (UINT32) ( a[1] & 0x3f ); /* 0011 1111 */ - h = (UINT32) ( b0 | b1 << 8 ); -#else - b1 = (UINT32) ( a[1] & 0xfc ); /* 1111 1100 */ - h = (UINT32) ( b0 << 8 | b1 ) >> 2; -#endif - return t26[h]; -} - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get a character representing 2nd 14-bit triplet (bits 14..27) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -const char* base26_triplet_2(const unsigned char *a) -{ -UINT32 b0, b1, b2, h; -#ifndef FIX_BASE26_ENC_BUG - b0 = (UINT32) ( a[1] & 0xc0); /* 1100 0000 */ - b1 = (UINT32) ( a[2] ); /* 1111 1111 */ - b2 = (UINT32) ( a[3] & 0x0f ); /* 0000 1111 */ - h = (UINT32)( b0 | b1 << 8 | b2 << 16 ) >> 6 ; -#else - b0 = (UINT32) ( a[1] & 0x03); /* 0000 0011 */ - b1 = (UINT32) ( a[2] ); /* 1111 1111 */ - b2 = (UINT32) ( a[3] & 0xf0 ); /* 1111 0000 */ - h = (UINT32)( b0 << 16 | b1 << 8 | b2 ) >> 4 ; -#endif - return t26[h]; -} - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get a character representing 3rd 14-bit triplet (bits 28..41) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -const char* base26_triplet_3(const unsigned char *a) -{ -UINT32 b0, b1, b2, h; -#ifndef FIX_BASE26_ENC_BUG - b0 = (UINT32) ( a[3] & 0xf0); /* 1111 0000 */ - b1 = (UINT32) ( a[4] ); /* 1111 1111 */ - b2 = (UINT32) ( a[5] & 0x03 ); /* 0000 0011 */ - h = (UINT32) ( b0 | b1 << 8 | b2 << 16 ) >> 4 ; -#else - b0 = (UINT32) ( a[3] & 0x0f); /* 0000 1111 */ - b1 = (UINT32) ( a[4] ); /* 1111 1111 */ - b2 = (UINT32) ( a[5] & 0xc0 ); /* 1100 0000 */ - h = (UINT32) ( b0 << 16 | b1 << 8 | b2 ) >> 6 ; -#endif - return t26[h]; -} - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get a character representing 4th 14-bit triplet (bits 42..55) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -const char* base26_triplet_4(const unsigned char *a) -{ -UINT32 b0, b1, h; -#ifndef FIX_BASE26_ENC_BUG - b0 = (UINT32) ( a[5] & 0xfc); /* 1111 1100 */ - b1 = (UINT32) ( a[6] ); /* 1111 1111 */ - h = (UINT32) ( b0 | b1 << 8 ) >> 2 ; -#else - b0 = (UINT32) ( a[5] & 0x3f); /* 0011 1111 */ - b1 = (UINT32) ( a[6] ); /* 1111 1111 */ - h = (UINT32) ( b0 << 8 | b1 ) ; -#endif - return t26[h]; -} - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Tail dublets -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - a4 a3 a2 a1 a0 - 28-36: 0001 1111 1111 0000 0000 0000 0000 0000 0000 0000 - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get dublet (bits 28..36) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -const char* base26_dublet_for_bits_28_to_36(unsigned char *a) -{ -UINT32 b0, b1, h; -#ifndef FIX_BASE26_ENC_BUG - b0 = (UINT32) ( a[3] & 0xf0); /* 1111 0000 */ - b1 = (UINT32) ( a[4] & 0x1f ); /* 0001 1111 */ - h = (UINT32)( b0 | b1 << 8 ) >> 4 ; -#else - b0 = (UINT32) ( a[3] & 0x0f); /* 0000 1111 */ - b1 = (UINT32) ( a[4] & 0xf8 ); /* 1111 1000 */ - h = (UINT32)( b0 << 8 | b1 ) >> 3 ; -#endif - return d26[h]; -} - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - a9 a8 a7 - 56-64: 0000 0000 0000 0001 1111 1111 - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get dublet (bits 56..64) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -const char* base26_dublet_for_bits_56_to_64(unsigned char *a) -{ -UINT32 b0, b1, h; -#ifndef FIX_BASE26_ENC_BUG - b0 = (UINT32) ( a[7] ); /* 1111 1111 */ - b1 = (UINT32) ( a[8] & 0x01 ); /* 0000 0001 */ - h = (UINT32)( b0 | b1 << 8 ); -#else - b0 = (UINT32) ( a[7] ); /* 1111 1111 */ - b1 = (UINT32) ( a[8] & 0x80 ); /* 1000 0000 */ - h = (UINT32)( b0 << 8 | b1 ) >> 7; -#endif - return d26[h]; -} - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Calculate check character A..Z for the string. -NB: ignore delimiter dashes. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -char base26_checksum(const char *str) -{ -size_t slen, j, jj=0, checksum = 0; -char c; - - slen = strlen(str); - - for (j=0; j < slen; j++) - { - c = str[j]; - if (c=='-') - continue; - - checksum+= weights_for_checksum[jj]*c; - - jj++; - if (jj > N_UNIQUE_WEIGHTS - 1) - jj = 0; - - } - return c26[checksum%26]; -} - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get hash extension in hexadecimal representation for the major block. -Len(extension) = 256 - 65 = 191 bit. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void get_xtra_hash_major_hex(const unsigned char *a, char* szXtra) -{ -unsigned char c; -int i, j, start_byte=8; -#ifndef FIX_BASE26_ENC_BUG - c = a[start_byte] & 0xfe ; /* 1111 1110 */ -#else - c = a[start_byte] & 0x7f ; /* 0111 1111 */ -#endif - j = sprintf(szXtra,"%02x", c); - for( i = start_byte+1; i < 32; i++ ) - j+= sprintf(szXtra+j,"%02x", a[i]); -} - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get hash extension in hexadecimal representation for the minor block. -Len(extension) = 256 - 37 = 219 bit. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void get_xtra_hash_minor_hex(const unsigned char *a, char* szXtra) -{ -unsigned char c; -int i, j, start_byte=4; -#ifndef FIX_BASE26_ENC_BUG - c = a[start_byte] & 0xe0 ; /* 1110 0000 */ -#else - c = a[start_byte] & 0x07 ; /* 0000 0111 */ -#endif - j = sprintf(szXtra,"%02x", c); - for( i = start_byte+1; i < 32; i++ ) - j+= sprintf(szXtra+j,"%02x", a[i]); -} +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +/* + InChIKey: procedures for base-26 encoding +*/ + + +#ifdef _MSC_VER +#if _MSC_VER > 1000 +#pragma warning( disable : 4996 ) +#endif +#endif + +#include +#include +#include "ikey_base26.h" + +/* + Triplets + + As the 2^14 (16384) is very close to 26^3 (17576), a triplet of uppercase + letters A..Z encodes 14 bits with good efficiency. + For speed, we just tabulate triplets below. + + We should throw away 17576-16384= 1192 triplets. + These are 676 triplets starting from 'E', the most frequent letter in English + texts (the other 516 are those started at 'T' , "TAA" to "TTV"). +*/ + +static const char t26[][4] = +{ +"AAA","AAB","AAC","AAD","AAE","AAF","AAG","AAH","AAI","AAJ","AAK","AAL","AAM","AAN","AAO","AAP", +"AAQ","AAR","AAS","AAT","AAU","AAV","AAW","AAX","AAY","AAZ","ABA","ABB","ABC","ABD","ABE","ABF", +"ABG","ABH","ABI","ABJ","ABK","ABL","ABM","ABN","ABO","ABP","ABQ","ABR","ABS","ABT","ABU","ABV", +"ABW","ABX","ABY","ABZ","ACA","ACB","ACC","ACD","ACE","ACF","ACG","ACH","ACI","ACJ","ACK","ACL", +"ACM","ACN","ACO","ACP","ACQ","ACR","ACS","ACT","ACU","ACV","ACW","ACX","ACY","ACZ","ADA","ADB", +"ADC","ADD","ADE","ADF","ADG","ADH","ADI","ADJ","ADK","ADL","ADM","ADN","ADO","ADP","ADQ","ADR", +"ADS","ADT","ADU","ADV","ADW","ADX","ADY","ADZ","AEA","AEB","AEC","AED","AEE","AEF","AEG","AEH", +"AEI","AEJ","AEK","AEL","AEM","AEN","AEO","AEP","AEQ","AER","AES","AET","AEU","AEV","AEW","AEX", +"AEY","AEZ","AFA","AFB","AFC","AFD","AFE","AFF","AFG","AFH","AFI","AFJ","AFK","AFL","AFM","AFN", +"AFO","AFP","AFQ","AFR","AFS","AFT","AFU","AFV","AFW","AFX","AFY","AFZ","AGA","AGB","AGC","AGD", +"AGE","AGF","AGG","AGH","AGI","AGJ","AGK","AGL","AGM","AGN","AGO","AGP","AGQ","AGR","AGS","AGT", +"AGU","AGV","AGW","AGX","AGY","AGZ","AHA","AHB","AHC","AHD","AHE","AHF","AHG","AHH","AHI","AHJ", +"AHK","AHL","AHM","AHN","AHO","AHP","AHQ","AHR","AHS","AHT","AHU","AHV","AHW","AHX","AHY","AHZ", +"AIA","AIB","AIC","AID","AIE","AIF","AIG","AIH","AII","AIJ","AIK","AIL","AIM","AIN","AIO","AIP", +"AIQ","AIR","AIS","AIT","AIU","AIV","AIW","AIX","AIY","AIZ","AJA","AJB","AJC","AJD","AJE","AJF", +"AJG","AJH","AJI","AJJ","AJK","AJL","AJM","AJN","AJO","AJP","AJQ","AJR","AJS","AJT","AJU","AJV", +"AJW","AJX","AJY","AJZ","AKA","AKB","AKC","AKD","AKE","AKF","AKG","AKH","AKI","AKJ","AKK","AKL", +"AKM","AKN","AKO","AKP","AKQ","AKR","AKS","AKT","AKU","AKV","AKW","AKX","AKY","AKZ","ALA","ALB", +"ALC","ALD","ALE","ALF","ALG","ALH","ALI","ALJ","ALK","ALL","ALM","ALN","ALO","ALP","ALQ","ALR", +"ALS","ALT","ALU","ALV","ALW","ALX","ALY","ALZ","AMA","AMB","AMC","AMD","AME","AMF","AMG","AMH", +"AMI","AMJ","AMK","AML","AMM","AMN","AMO","AMP","AMQ","AMR","AMS","AMT","AMU","AMV","AMW","AMX", +"AMY","AMZ","ANA","ANB","ANC","AND","ANE","ANF","ANG","ANH","ANI","ANJ","ANK","ANL","ANM","ANN", +"ANO","ANP","ANQ","ANR","ANS","ANT","ANU","ANV","ANW","ANX","ANY","ANZ","AOA","AOB","AOC","AOD", +"AOE","AOF","AOG","AOH","AOI","AOJ","AOK","AOL","AOM","AON","AOO","AOP","AOQ","AOR","AOS","AOT", +"AOU","AOV","AOW","AOX","AOY","AOZ","APA","APB","APC","APD","APE","APF","APG","APH","API","APJ", +"APK","APL","APM","APN","APO","APP","APQ","APR","APS","APT","APU","APV","APW","APX","APY","APZ", +"AQA","AQB","AQC","AQD","AQE","AQF","AQG","AQH","AQI","AQJ","AQK","AQL","AQM","AQN","AQO","AQP", +"AQQ","AQR","AQS","AQT","AQU","AQV","AQW","AQX","AQY","AQZ","ARA","ARB","ARC","ARD","ARE","ARF", +"ARG","ARH","ARI","ARJ","ARK","ARL","ARM","ARN","ARO","ARP","ARQ","ARR","ARS","ART","ARU","ARV", +"ARW","ARX","ARY","ARZ","ASA","ASB","ASC","ASD","ASE","ASF","ASG","ASH","ASI","ASJ","ASK","ASL", +"ASM","ASN","ASO","ASP","ASQ","ASR","ASS","AST","ASU","ASV","ASW","ASX","ASY","ASZ","ATA","ATB", +"ATC","ATD","ATE","ATF","ATG","ATH","ATI","ATJ","ATK","ATL","ATM","ATN","ATO","ATP","ATQ","ATR", +"ATS","ATT","ATU","ATV","ATW","ATX","ATY","ATZ","AUA","AUB","AUC","AUD","AUE","AUF","AUG","AUH", +"AUI","AUJ","AUK","AUL","AUM","AUN","AUO","AUP","AUQ","AUR","AUS","AUT","AUU","AUV","AUW","AUX", +"AUY","AUZ","AVA","AVB","AVC","AVD","AVE","AVF","AVG","AVH","AVI","AVJ","AVK","AVL","AVM","AVN", +"AVO","AVP","AVQ","AVR","AVS","AVT","AVU","AVV","AVW","AVX","AVY","AVZ","AWA","AWB","AWC","AWD", +"AWE","AWF","AWG","AWH","AWI","AWJ","AWK","AWL","AWM","AWN","AWO","AWP","AWQ","AWR","AWS","AWT", +"AWU","AWV","AWW","AWX","AWY","AWZ","AXA","AXB","AXC","AXD","AXE","AXF","AXG","AXH","AXI","AXJ", +"AXK","AXL","AXM","AXN","AXO","AXP","AXQ","AXR","AXS","AXT","AXU","AXV","AXW","AXX","AXY","AXZ", +"AYA","AYB","AYC","AYD","AYE","AYF","AYG","AYH","AYI","AYJ","AYK","AYL","AYM","AYN","AYO","AYP", +"AYQ","AYR","AYS","AYT","AYU","AYV","AYW","AYX","AYY","AYZ","AZA","AZB","AZC","AZD","AZE","AZF", +"AZG","AZH","AZI","AZJ","AZK","AZL","AZM","AZN","AZO","AZP","AZQ","AZR","AZS","AZT","AZU","AZV", +"AZW","AZX","AZY","AZZ","BAA","BAB","BAC","BAD","BAE","BAF","BAG","BAH","BAI","BAJ","BAK","BAL", +"BAM","BAN","BAO","BAP","BAQ","BAR","BAS","BAT","BAU","BAV","BAW","BAX","BAY","BAZ","BBA","BBB", +"BBC","BBD","BBE","BBF","BBG","BBH","BBI","BBJ","BBK","BBL","BBM","BBN","BBO","BBP","BBQ","BBR", +"BBS","BBT","BBU","BBV","BBW","BBX","BBY","BBZ","BCA","BCB","BCC","BCD","BCE","BCF","BCG","BCH", +"BCI","BCJ","BCK","BCL","BCM","BCN","BCO","BCP","BCQ","BCR","BCS","BCT","BCU","BCV","BCW","BCX", +"BCY","BCZ","BDA","BDB","BDC","BDD","BDE","BDF","BDG","BDH","BDI","BDJ","BDK","BDL","BDM","BDN", +"BDO","BDP","BDQ","BDR","BDS","BDT","BDU","BDV","BDW","BDX","BDY","BDZ","BEA","BEB","BEC","BED", +"BEE","BEF","BEG","BEH","BEI","BEJ","BEK","BEL","BEM","BEN","BEO","BEP","BEQ","BER","BES","BET", +"BEU","BEV","BEW","BEX","BEY","BEZ","BFA","BFB","BFC","BFD","BFE","BFF","BFG","BFH","BFI","BFJ", +"BFK","BFL","BFM","BFN","BFO","BFP","BFQ","BFR","BFS","BFT","BFU","BFV","BFW","BFX","BFY","BFZ", +"BGA","BGB","BGC","BGD","BGE","BGF","BGG","BGH","BGI","BGJ","BGK","BGL","BGM","BGN","BGO","BGP", +"BGQ","BGR","BGS","BGT","BGU","BGV","BGW","BGX","BGY","BGZ","BHA","BHB","BHC","BHD","BHE","BHF", +"BHG","BHH","BHI","BHJ","BHK","BHL","BHM","BHN","BHO","BHP","BHQ","BHR","BHS","BHT","BHU","BHV", +"BHW","BHX","BHY","BHZ","BIA","BIB","BIC","BID","BIE","BIF","BIG","BIH","BII","BIJ","BIK","BIL", +"BIM","BIN","BIO","BIP","BIQ","BIR","BIS","BIT","BIU","BIV","BIW","BIX","BIY","BIZ","BJA","BJB", +"BJC","BJD","BJE","BJF","BJG","BJH","BJI","BJJ","BJK","BJL","BJM","BJN","BJO","BJP","BJQ","BJR", +"BJS","BJT","BJU","BJV","BJW","BJX","BJY","BJZ","BKA","BKB","BKC","BKD","BKE","BKF","BKG","BKH", +"BKI","BKJ","BKK","BKL","BKM","BKN","BKO","BKP","BKQ","BKR","BKS","BKT","BKU","BKV","BKW","BKX", +"BKY","BKZ","BLA","BLB","BLC","BLD","BLE","BLF","BLG","BLH","BLI","BLJ","BLK","BLL","BLM","BLN", +"BLO","BLP","BLQ","BLR","BLS","BLT","BLU","BLV","BLW","BLX","BLY","BLZ","BMA","BMB","BMC","BMD", +"BME","BMF","BMG","BMH","BMI","BMJ","BMK","BML","BMM","BMN","BMO","BMP","BMQ","BMR","BMS","BMT", +"BMU","BMV","BMW","BMX","BMY","BMZ","BNA","BNB","BNC","BND","BNE","BNF","BNG","BNH","BNI","BNJ", +"BNK","BNL","BNM","BNN","BNO","BNP","BNQ","BNR","BNS","BNT","BNU","BNV","BNW","BNX","BNY","BNZ", +"BOA","BOB","BOC","BOD","BOE","BOF","BOG","BOH","BOI","BOJ","BOK","BOL","BOM","BON","BOO","BOP", +"BOQ","BOR","BOS","BOT","BOU","BOV","BOW","BOX","BOY","BOZ","BPA","BPB","BPC","BPD","BPE","BPF", +"BPG","BPH","BPI","BPJ","BPK","BPL","BPM","BPN","BPO","BPP","BPQ","BPR","BPS","BPT","BPU","BPV", +"BPW","BPX","BPY","BPZ","BQA","BQB","BQC","BQD","BQE","BQF","BQG","BQH","BQI","BQJ","BQK","BQL", +"BQM","BQN","BQO","BQP","BQQ","BQR","BQS","BQT","BQU","BQV","BQW","BQX","BQY","BQZ","BRA","BRB", +"BRC","BRD","BRE","BRF","BRG","BRH","BRI","BRJ","BRK","BRL","BRM","BRN","BRO","BRP","BRQ","BRR", +"BRS","BRT","BRU","BRV","BRW","BRX","BRY","BRZ","BSA","BSB","BSC","BSD","BSE","BSF","BSG","BSH", +"BSI","BSJ","BSK","BSL","BSM","BSN","BSO","BSP","BSQ","BSR","BSS","BST","BSU","BSV","BSW","BSX", +"BSY","BSZ","BTA","BTB","BTC","BTD","BTE","BTF","BTG","BTH","BTI","BTJ","BTK","BTL","BTM","BTN", +"BTO","BTP","BTQ","BTR","BTS","BTT","BTU","BTV","BTW","BTX","BTY","BTZ","BUA","BUB","BUC","BUD", +"BUE","BUF","BUG","BUH","BUI","BUJ","BUK","BUL","BUM","BUN","BUO","BUP","BUQ","BUR","BUS","BUT", +"BUU","BUV","BUW","BUX","BUY","BUZ","BVA","BVB","BVC","BVD","BVE","BVF","BVG","BVH","BVI","BVJ", +"BVK","BVL","BVM","BVN","BVO","BVP","BVQ","BVR","BVS","BVT","BVU","BVV","BVW","BVX","BVY","BVZ", +"BWA","BWB","BWC","BWD","BWE","BWF","BWG","BWH","BWI","BWJ","BWK","BWL","BWM","BWN","BWO","BWP", +"BWQ","BWR","BWS","BWT","BWU","BWV","BWW","BWX","BWY","BWZ","BXA","BXB","BXC","BXD","BXE","BXF", +"BXG","BXH","BXI","BXJ","BXK","BXL","BXM","BXN","BXO","BXP","BXQ","BXR","BXS","BXT","BXU","BXV", +"BXW","BXX","BXY","BXZ","BYA","BYB","BYC","BYD","BYE","BYF","BYG","BYH","BYI","BYJ","BYK","BYL", +"BYM","BYN","BYO","BYP","BYQ","BYR","BYS","BYT","BYU","BYV","BYW","BYX","BYY","BYZ","BZA","BZB", +"BZC","BZD","BZE","BZF","BZG","BZH","BZI","BZJ","BZK","BZL","BZM","BZN","BZO","BZP","BZQ","BZR", +"BZS","BZT","BZU","BZV","BZW","BZX","BZY","BZZ","CAA","CAB","CAC","CAD","CAE","CAF","CAG","CAH", +"CAI","CAJ","CAK","CAL","CAM","CAN","CAO","CAP","CAQ","CAR","CAS","CAT","CAU","CAV","CAW","CAX", +"CAY","CAZ","CBA","CBB","CBC","CBD","CBE","CBF","CBG","CBH","CBI","CBJ","CBK","CBL","CBM","CBN", +"CBO","CBP","CBQ","CBR","CBS","CBT","CBU","CBV","CBW","CBX","CBY","CBZ","CCA","CCB","CCC","CCD", +"CCE","CCF","CCG","CCH","CCI","CCJ","CCK","CCL","CCM","CCN","CCO","CCP","CCQ","CCR","CCS","CCT", +"CCU","CCV","CCW","CCX","CCY","CCZ","CDA","CDB","CDC","CDD","CDE","CDF","CDG","CDH","CDI","CDJ", +"CDK","CDL","CDM","CDN","CDO","CDP","CDQ","CDR","CDS","CDT","CDU","CDV","CDW","CDX","CDY","CDZ", +"CEA","CEB","CEC","CED","CEE","CEF","CEG","CEH","CEI","CEJ","CEK","CEL","CEM","CEN","CEO","CEP", +"CEQ","CER","CES","CET","CEU","CEV","CEW","CEX","CEY","CEZ","CFA","CFB","CFC","CFD","CFE","CFF", +"CFG","CFH","CFI","CFJ","CFK","CFL","CFM","CFN","CFO","CFP","CFQ","CFR","CFS","CFT","CFU","CFV", +"CFW","CFX","CFY","CFZ","CGA","CGB","CGC","CGD","CGE","CGF","CGG","CGH","CGI","CGJ","CGK","CGL", +"CGM","CGN","CGO","CGP","CGQ","CGR","CGS","CGT","CGU","CGV","CGW","CGX","CGY","CGZ","CHA","CHB", +"CHC","CHD","CHE","CHF","CHG","CHH","CHI","CHJ","CHK","CHL","CHM","CHN","CHO","CHP","CHQ","CHR", +"CHS","CHT","CHU","CHV","CHW","CHX","CHY","CHZ","CIA","CIB","CIC","CID","CIE","CIF","CIG","CIH", +"CII","CIJ","CIK","CIL","CIM","CIN","CIO","CIP","CIQ","CIR","CIS","CIT","CIU","CIV","CIW","CIX", +"CIY","CIZ","CJA","CJB","CJC","CJD","CJE","CJF","CJG","CJH","CJI","CJJ","CJK","CJL","CJM","CJN", +"CJO","CJP","CJQ","CJR","CJS","CJT","CJU","CJV","CJW","CJX","CJY","CJZ","CKA","CKB","CKC","CKD", +"CKE","CKF","CKG","CKH","CKI","CKJ","CKK","CKL","CKM","CKN","CKO","CKP","CKQ","CKR","CKS","CKT", +"CKU","CKV","CKW","CKX","CKY","CKZ","CLA","CLB","CLC","CLD","CLE","CLF","CLG","CLH","CLI","CLJ", +"CLK","CLL","CLM","CLN","CLO","CLP","CLQ","CLR","CLS","CLT","CLU","CLV","CLW","CLX","CLY","CLZ", +"CMA","CMB","CMC","CMD","CME","CMF","CMG","CMH","CMI","CMJ","CMK","CML","CMM","CMN","CMO","CMP", +"CMQ","CMR","CMS","CMT","CMU","CMV","CMW","CMX","CMY","CMZ","CNA","CNB","CNC","CND","CNE","CNF", +"CNG","CNH","CNI","CNJ","CNK","CNL","CNM","CNN","CNO","CNP","CNQ","CNR","CNS","CNT","CNU","CNV", +"CNW","CNX","CNY","CNZ","COA","COB","COC","COD","COE","COF","COG","COH","COI","COJ","COK","COL", +"COM","CON","COO","COP","COQ","COR","COS","COT","COU","COV","COW","COX","COY","COZ","CPA","CPB", +"CPC","CPD","CPE","CPF","CPG","CPH","CPI","CPJ","CPK","CPL","CPM","CPN","CPO","CPP","CPQ","CPR", +"CPS","CPT","CPU","CPV","CPW","CPX","CPY","CPZ","CQA","CQB","CQC","CQD","CQE","CQF","CQG","CQH", +"CQI","CQJ","CQK","CQL","CQM","CQN","CQO","CQP","CQQ","CQR","CQS","CQT","CQU","CQV","CQW","CQX", +"CQY","CQZ","CRA","CRB","CRC","CRD","CRE","CRF","CRG","CRH","CRI","CRJ","CRK","CRL","CRM","CRN", +"CRO","CRP","CRQ","CRR","CRS","CRT","CRU","CRV","CRW","CRX","CRY","CRZ","CSA","CSB","CSC","CSD", +"CSE","CSF","CSG","CSH","CSI","CSJ","CSK","CSL","CSM","CSN","CSO","CSP","CSQ","CSR","CSS","CST", +"CSU","CSV","CSW","CSX","CSY","CSZ","CTA","CTB","CTC","CTD","CTE","CTF","CTG","CTH","CTI","CTJ", +"CTK","CTL","CTM","CTN","CTO","CTP","CTQ","CTR","CTS","CTT","CTU","CTV","CTW","CTX","CTY","CTZ", +"CUA","CUB","CUC","CUD","CUE","CUF","CUG","CUH","CUI","CUJ","CUK","CUL","CUM","CUN","CUO","CUP", +"CUQ","CUR","CUS","CUT","CUU","CUV","CUW","CUX","CUY","CUZ","CVA","CVB","CVC","CVD","CVE","CVF", +"CVG","CVH","CVI","CVJ","CVK","CVL","CVM","CVN","CVO","CVP","CVQ","CVR","CVS","CVT","CVU","CVV", +"CVW","CVX","CVY","CVZ","CWA","CWB","CWC","CWD","CWE","CWF","CWG","CWH","CWI","CWJ","CWK","CWL", +"CWM","CWN","CWO","CWP","CWQ","CWR","CWS","CWT","CWU","CWV","CWW","CWX","CWY","CWZ","CXA","CXB", +"CXC","CXD","CXE","CXF","CXG","CXH","CXI","CXJ","CXK","CXL","CXM","CXN","CXO","CXP","CXQ","CXR", +"CXS","CXT","CXU","CXV","CXW","CXX","CXY","CXZ","CYA","CYB","CYC","CYD","CYE","CYF","CYG","CYH", +"CYI","CYJ","CYK","CYL","CYM","CYN","CYO","CYP","CYQ","CYR","CYS","CYT","CYU","CYV","CYW","CYX", +"CYY","CYZ","CZA","CZB","CZC","CZD","CZE","CZF","CZG","CZH","CZI","CZJ","CZK","CZL","CZM","CZN", +"CZO","CZP","CZQ","CZR","CZS","CZT","CZU","CZV","CZW","CZX","CZY","CZZ","DAA","DAB","DAC","DAD", +"DAE","DAF","DAG","DAH","DAI","DAJ","DAK","DAL","DAM","DAN","DAO","DAP","DAQ","DAR","DAS","DAT", +"DAU","DAV","DAW","DAX","DAY","DAZ","DBA","DBB","DBC","DBD","DBE","DBF","DBG","DBH","DBI","DBJ", +"DBK","DBL","DBM","DBN","DBO","DBP","DBQ","DBR","DBS","DBT","DBU","DBV","DBW","DBX","DBY","DBZ", +"DCA","DCB","DCC","DCD","DCE","DCF","DCG","DCH","DCI","DCJ","DCK","DCL","DCM","DCN","DCO","DCP", +"DCQ","DCR","DCS","DCT","DCU","DCV","DCW","DCX","DCY","DCZ","DDA","DDB","DDC","DDD","DDE","DDF", +"DDG","DDH","DDI","DDJ","DDK","DDL","DDM","DDN","DDO","DDP","DDQ","DDR","DDS","DDT","DDU","DDV", +"DDW","DDX","DDY","DDZ","DEA","DEB","DEC","DED","DEE","DEF","DEG","DEH","DEI","DEJ","DEK","DEL", +"DEM","DEN","DEO","DEP","DEQ","DER","DES","DET","DEU","DEV","DEW","DEX","DEY","DEZ","DFA","DFB", +"DFC","DFD","DFE","DFF","DFG","DFH","DFI","DFJ","DFK","DFL","DFM","DFN","DFO","DFP","DFQ","DFR", +"DFS","DFT","DFU","DFV","DFW","DFX","DFY","DFZ","DGA","DGB","DGC","DGD","DGE","DGF","DGG","DGH", +"DGI","DGJ","DGK","DGL","DGM","DGN","DGO","DGP","DGQ","DGR","DGS","DGT","DGU","DGV","DGW","DGX", +"DGY","DGZ","DHA","DHB","DHC","DHD","DHE","DHF","DHG","DHH","DHI","DHJ","DHK","DHL","DHM","DHN", +"DHO","DHP","DHQ","DHR","DHS","DHT","DHU","DHV","DHW","DHX","DHY","DHZ","DIA","DIB","DIC","DID", +"DIE","DIF","DIG","DIH","DII","DIJ","DIK","DIL","DIM","DIN","DIO","DIP","DIQ","DIR","DIS","DIT", +"DIU","DIV","DIW","DIX","DIY","DIZ","DJA","DJB","DJC","DJD","DJE","DJF","DJG","DJH","DJI","DJJ", +"DJK","DJL","DJM","DJN","DJO","DJP","DJQ","DJR","DJS","DJT","DJU","DJV","DJW","DJX","DJY","DJZ", +"DKA","DKB","DKC","DKD","DKE","DKF","DKG","DKH","DKI","DKJ","DKK","DKL","DKM","DKN","DKO","DKP", +"DKQ","DKR","DKS","DKT","DKU","DKV","DKW","DKX","DKY","DKZ","DLA","DLB","DLC","DLD","DLE","DLF", +"DLG","DLH","DLI","DLJ","DLK","DLL","DLM","DLN","DLO","DLP","DLQ","DLR","DLS","DLT","DLU","DLV", +"DLW","DLX","DLY","DLZ","DMA","DMB","DMC","DMD","DME","DMF","DMG","DMH","DMI","DMJ","DMK","DML", +"DMM","DMN","DMO","DMP","DMQ","DMR","DMS","DMT","DMU","DMV","DMW","DMX","DMY","DMZ","DNA","DNB", +"DNC","DND","DNE","DNF","DNG","DNH","DNI","DNJ","DNK","DNL","DNM","DNN","DNO","DNP","DNQ","DNR", +"DNS","DNT","DNU","DNV","DNW","DNX","DNY","DNZ","DOA","DOB","DOC","DOD","DOE","DOF","DOG","DOH", +"DOI","DOJ","DOK","DOL","DOM","DON","DOO","DOP","DOQ","DOR","DOS","DOT","DOU","DOV","DOW","DOX", +"DOY","DOZ","DPA","DPB","DPC","DPD","DPE","DPF","DPG","DPH","DPI","DPJ","DPK","DPL","DPM","DPN", +"DPO","DPP","DPQ","DPR","DPS","DPT","DPU","DPV","DPW","DPX","DPY","DPZ","DQA","DQB","DQC","DQD", +"DQE","DQF","DQG","DQH","DQI","DQJ","DQK","DQL","DQM","DQN","DQO","DQP","DQQ","DQR","DQS","DQT", +"DQU","DQV","DQW","DQX","DQY","DQZ","DRA","DRB","DRC","DRD","DRE","DRF","DRG","DRH","DRI","DRJ", +"DRK","DRL","DRM","DRN","DRO","DRP","DRQ","DRR","DRS","DRT","DRU","DRV","DRW","DRX","DRY","DRZ", +"DSA","DSB","DSC","DSD","DSE","DSF","DSG","DSH","DSI","DSJ","DSK","DSL","DSM","DSN","DSO","DSP", +"DSQ","DSR","DSS","DST","DSU","DSV","DSW","DSX","DSY","DSZ","DTA","DTB","DTC","DTD","DTE","DTF", +"DTG","DTH","DTI","DTJ","DTK","DTL","DTM","DTN","DTO","DTP","DTQ","DTR","DTS","DTT","DTU","DTV", +"DTW","DTX","DTY","DTZ","DUA","DUB","DUC","DUD","DUE","DUF","DUG","DUH","DUI","DUJ","DUK","DUL", +"DUM","DUN","DUO","DUP","DUQ","DUR","DUS","DUT","DUU","DUV","DUW","DUX","DUY","DUZ","DVA","DVB", +"DVC","DVD","DVE","DVF","DVG","DVH","DVI","DVJ","DVK","DVL","DVM","DVN","DVO","DVP","DVQ","DVR", +"DVS","DVT","DVU","DVV","DVW","DVX","DVY","DVZ","DWA","DWB","DWC","DWD","DWE","DWF","DWG","DWH", +"DWI","DWJ","DWK","DWL","DWM","DWN","DWO","DWP","DWQ","DWR","DWS","DWT","DWU","DWV","DWW","DWX", +"DWY","DWZ","DXA","DXB","DXC","DXD","DXE","DXF","DXG","DXH","DXI","DXJ","DXK","DXL","DXM","DXN", +"DXO","DXP","DXQ","DXR","DXS","DXT","DXU","DXV","DXW","DXX","DXY","DXZ","DYA","DYB","DYC","DYD", +"DYE","DYF","DYG","DYH","DYI","DYJ","DYK","DYL","DYM","DYN","DYO","DYP","DYQ","DYR","DYS","DYT", +"DYU","DYV","DYW","DYX","DYY","DYZ","DZA","DZB","DZC","DZD","DZE","DZF","DZG","DZH","DZI","DZJ", +"DZK","DZL","DZM","DZN","DZO","DZP","DZQ","DZR","DZS","DZT","DZU","DZV","DZW","DZX","DZY","DZZ", +/* + E-started's intentionally omitted +*/ +"FAA","FAB","FAC","FAD","FAE","FAF","FAG","FAH","FAI","FAJ","FAK","FAL", +"FAM","FAN","FAO","FAP","FAQ","FAR","FAS","FAT","FAU","FAV","FAW","FAX","FAY","FAZ","FBA","FBB", +"FBC","FBD","FBE","FBF","FBG","FBH","FBI","FBJ","FBK","FBL","FBM","FBN","FBO","FBP","FBQ","FBR", +"FBS","FBT","FBU","FBV","FBW","FBX","FBY","FBZ","FCA","FCB","FCC","FCD","FCE","FCF","FCG","FCH", +"FCI","FCJ","FCK","FCL","FCM","FCN","FCO","FCP","FCQ","FCR","FCS","FCT","FCU","FCV","FCW","FCX", +"FCY","FCZ","FDA","FDB","FDC","FDD","FDE","FDF","FDG","FDH","FDI","FDJ","FDK","FDL","FDM","FDN", +"FDO","FDP","FDQ","FDR","FDS","FDT","FDU","FDV","FDW","FDX","FDY","FDZ","FEA","FEB","FEC","FED", +"FEE","FEF","FEG","FEH","FEI","FEJ","FEK","FEL","FEM","FEN","FEO","FEP","FEQ","FER","FES","FET", +"FEU","FEV","FEW","FEX","FEY","FEZ","FFA","FFB","FFC","FFD","FFE","FFF","FFG","FFH","FFI","FFJ", +"FFK","FFL","FFM","FFN","FFO","FFP","FFQ","FFR","FFS","FFT","FFU","FFV","FFW","FFX","FFY","FFZ", +"FGA","FGB","FGC","FGD","FGE","FGF","FGG","FGH","FGI","FGJ","FGK","FGL","FGM","FGN","FGO","FGP", +"FGQ","FGR","FGS","FGT","FGU","FGV","FGW","FGX","FGY","FGZ","FHA","FHB","FHC","FHD","FHE","FHF", +"FHG","FHH","FHI","FHJ","FHK","FHL","FHM","FHN","FHO","FHP","FHQ","FHR","FHS","FHT","FHU","FHV", +"FHW","FHX","FHY","FHZ","FIA","FIB","FIC","FID","FIE","FIF","FIG","FIH","FII","FIJ","FIK","FIL", +"FIM","FIN","FIO","FIP","FIQ","FIR","FIS","FIT","FIU","FIV","FIW","FIX","FIY","FIZ","FJA","FJB", +"FJC","FJD","FJE","FJF","FJG","FJH","FJI","FJJ","FJK","FJL","FJM","FJN","FJO","FJP","FJQ","FJR", +"FJS","FJT","FJU","FJV","FJW","FJX","FJY","FJZ","FKA","FKB","FKC","FKD","FKE","FKF","FKG","FKH", +"FKI","FKJ","FKK","FKL","FKM","FKN","FKO","FKP","FKQ","FKR","FKS","FKT","FKU","FKV","FKW","FKX", +"FKY","FKZ","FLA","FLB","FLC","FLD","FLE","FLF","FLG","FLH","FLI","FLJ","FLK","FLL","FLM","FLN", +"FLO","FLP","FLQ","FLR","FLS","FLT","FLU","FLV","FLW","FLX","FLY","FLZ","FMA","FMB","FMC","FMD", +"FME","FMF","FMG","FMH","FMI","FMJ","FMK","FML","FMM","FMN","FMO","FMP","FMQ","FMR","FMS","FMT", +"FMU","FMV","FMW","FMX","FMY","FMZ","FNA","FNB","FNC","FND","FNE","FNF","FNG","FNH","FNI","FNJ", +"FNK","FNL","FNM","FNN","FNO","FNP","FNQ","FNR","FNS","FNT","FNU","FNV","FNW","FNX","FNY","FNZ", +"FOA","FOB","FOC","FOD","FOE","FOF","FOG","FOH","FOI","FOJ","FOK","FOL","FOM","FON","FOO","FOP", +"FOQ","FOR","FOS","FOT","FOU","FOV","FOW","FOX","FOY","FOZ","FPA","FPB","FPC","FPD","FPE","FPF", +"FPG","FPH","FPI","FPJ","FPK","FPL","FPM","FPN","FPO","FPP","FPQ","FPR","FPS","FPT","FPU","FPV", +"FPW","FPX","FPY","FPZ","FQA","FQB","FQC","FQD","FQE","FQF","FQG","FQH","FQI","FQJ","FQK","FQL", +"FQM","FQN","FQO","FQP","FQQ","FQR","FQS","FQT","FQU","FQV","FQW","FQX","FQY","FQZ","FRA","FRB", +"FRC","FRD","FRE","FRF","FRG","FRH","FRI","FRJ","FRK","FRL","FRM","FRN","FRO","FRP","FRQ","FRR", +"FRS","FRT","FRU","FRV","FRW","FRX","FRY","FRZ","FSA","FSB","FSC","FSD","FSE","FSF","FSG","FSH", +"FSI","FSJ","FSK","FSL","FSM","FSN","FSO","FSP","FSQ","FSR","FSS","FST","FSU","FSV","FSW","FSX", +"FSY","FSZ","FTA","FTB","FTC","FTD","FTE","FTF","FTG","FTH","FTI","FTJ","FTK","FTL","FTM","FTN", +"FTO","FTP","FTQ","FTR","FTS","FTT","FTU","FTV","FTW","FTX","FTY","FTZ","FUA","FUB","FUC","FUD", +"FUE","FUF","FUG","FUH","FUI","FUJ","FUK","FUL","FUM","FUN","FUO","FUP","FUQ","FUR","FUS","FUT", +"FUU","FUV","FUW","FUX","FUY","FUZ","FVA","FVB","FVC","FVD","FVE","FVF","FVG","FVH","FVI","FVJ", +"FVK","FVL","FVM","FVN","FVO","FVP","FVQ","FVR","FVS","FVT","FVU","FVV","FVW","FVX","FVY","FVZ", +"FWA","FWB","FWC","FWD","FWE","FWF","FWG","FWH","FWI","FWJ","FWK","FWL","FWM","FWN","FWO","FWP", +"FWQ","FWR","FWS","FWT","FWU","FWV","FWW","FWX","FWY","FWZ","FXA","FXB","FXC","FXD","FXE","FXF", +"FXG","FXH","FXI","FXJ","FXK","FXL","FXM","FXN","FXO","FXP","FXQ","FXR","FXS","FXT","FXU","FXV", +"FXW","FXX","FXY","FXZ","FYA","FYB","FYC","FYD","FYE","FYF","FYG","FYH","FYI","FYJ","FYK","FYL", +"FYM","FYN","FYO","FYP","FYQ","FYR","FYS","FYT","FYU","FYV","FYW","FYX","FYY","FYZ","FZA","FZB", +"FZC","FZD","FZE","FZF","FZG","FZH","FZI","FZJ","FZK","FZL","FZM","FZN","FZO","FZP","FZQ","FZR", +"FZS","FZT","FZU","FZV","FZW","FZX","FZY","FZZ","GAA","GAB","GAC","GAD","GAE","GAF","GAG","GAH", +"GAI","GAJ","GAK","GAL","GAM","GAN","GAO","GAP","GAQ","GAR","GAS","GAT","GAU","GAV","GAW","GAX", +"GAY","GAZ","GBA","GBB","GBC","GBD","GBE","GBF","GBG","GBH","GBI","GBJ","GBK","GBL","GBM","GBN", +"GBO","GBP","GBQ","GBR","GBS","GBT","GBU","GBV","GBW","GBX","GBY","GBZ","GCA","GCB","GCC","GCD", +"GCE","GCF","GCG","GCH","GCI","GCJ","GCK","GCL","GCM","GCN","GCO","GCP","GCQ","GCR","GCS","GCT", +"GCU","GCV","GCW","GCX","GCY","GCZ","GDA","GDB","GDC","GDD","GDE","GDF","GDG","GDH","GDI","GDJ", +"GDK","GDL","GDM","GDN","GDO","GDP","GDQ","GDR","GDS","GDT","GDU","GDV","GDW","GDX","GDY","GDZ", +"GEA","GEB","GEC","GED","GEE","GEF","GEG","GEH","GEI","GEJ","GEK","GEL","GEM","GEN","GEO","GEP", +"GEQ","GER","GES","GET","GEU","GEV","GEW","GEX","GEY","GEZ","GFA","GFB","GFC","GFD","GFE","GFF", +"GFG","GFH","GFI","GFJ","GFK","GFL","GFM","GFN","GFO","GFP","GFQ","GFR","GFS","GFT","GFU","GFV", +"GFW","GFX","GFY","GFZ","GGA","GGB","GGC","GGD","GGE","GGF","GGG","GGH","GGI","GGJ","GGK","GGL", +"GGM","GGN","GGO","GGP","GGQ","GGR","GGS","GGT","GGU","GGV","GGW","GGX","GGY","GGZ","GHA","GHB", +"GHC","GHD","GHE","GHF","GHG","GHH","GHI","GHJ","GHK","GHL","GHM","GHN","GHO","GHP","GHQ","GHR", +"GHS","GHT","GHU","GHV","GHW","GHX","GHY","GHZ","GIA","GIB","GIC","GID","GIE","GIF","GIG","GIH", +"GII","GIJ","GIK","GIL","GIM","GIN","GIO","GIP","GIQ","GIR","GIS","GIT","GIU","GIV","GIW","GIX", +"GIY","GIZ","GJA","GJB","GJC","GJD","GJE","GJF","GJG","GJH","GJI","GJJ","GJK","GJL","GJM","GJN", +"GJO","GJP","GJQ","GJR","GJS","GJT","GJU","GJV","GJW","GJX","GJY","GJZ","GKA","GKB","GKC","GKD", +"GKE","GKF","GKG","GKH","GKI","GKJ","GKK","GKL","GKM","GKN","GKO","GKP","GKQ","GKR","GKS","GKT", +"GKU","GKV","GKW","GKX","GKY","GKZ","GLA","GLB","GLC","GLD","GLE","GLF","GLG","GLH","GLI","GLJ", +"GLK","GLL","GLM","GLN","GLO","GLP","GLQ","GLR","GLS","GLT","GLU","GLV","GLW","GLX","GLY","GLZ", +"GMA","GMB","GMC","GMD","GME","GMF","GMG","GMH","GMI","GMJ","GMK","GML","GMM","GMN","GMO","GMP", +"GMQ","GMR","GMS","GMT","GMU","GMV","GMW","GMX","GMY","GMZ","GNA","GNB","GNC","GND","GNE","GNF", +"GNG","GNH","GNI","GNJ","GNK","GNL","GNM","GNN","GNO","GNP","GNQ","GNR","GNS","GNT","GNU","GNV", +"GNW","GNX","GNY","GNZ","GOA","GOB","GOC","GOD","GOE","GOF","GOG","GOH","GOI","GOJ","GOK","GOL", +"GOM","GON","GOO","GOP","GOQ","GOR","GOS","GOT","GOU","GOV","GOW","GOX","GOY","GOZ","GPA","GPB", +"GPC","GPD","GPE","GPF","GPG","GPH","GPI","GPJ","GPK","GPL","GPM","GPN","GPO","GPP","GPQ","GPR", +"GPS","GPT","GPU","GPV","GPW","GPX","GPY","GPZ","GQA","GQB","GQC","GQD","GQE","GQF","GQG","GQH", +"GQI","GQJ","GQK","GQL","GQM","GQN","GQO","GQP","GQQ","GQR","GQS","GQT","GQU","GQV","GQW","GQX", +"GQY","GQZ","GRA","GRB","GRC","GRD","GRE","GRF","GRG","GRH","GRI","GRJ","GRK","GRL","GRM","GRN", +"GRO","GRP","GRQ","GRR","GRS","GRT","GRU","GRV","GRW","GRX","GRY","GRZ","GSA","GSB","GSC","GSD", +"GSE","GSF","GSG","GSH","GSI","GSJ","GSK","GSL","GSM","GSN","GSO","GSP","GSQ","GSR","GSS","GST", +"GSU","GSV","GSW","GSX","GSY","GSZ","GTA","GTB","GTC","GTD","GTE","GTF","GTG","GTH","GTI","GTJ", +"GTK","GTL","GTM","GTN","GTO","GTP","GTQ","GTR","GTS","GTT","GTU","GTV","GTW","GTX","GTY","GTZ", +"GUA","GUB","GUC","GUD","GUE","GUF","GUG","GUH","GUI","GUJ","GUK","GUL","GUM","GUN","GUO","GUP", +"GUQ","GUR","GUS","GUT","GUU","GUV","GUW","GUX","GUY","GUZ","GVA","GVB","GVC","GVD","GVE","GVF", +"GVG","GVH","GVI","GVJ","GVK","GVL","GVM","GVN","GVO","GVP","GVQ","GVR","GVS","GVT","GVU","GVV", +"GVW","GVX","GVY","GVZ","GWA","GWB","GWC","GWD","GWE","GWF","GWG","GWH","GWI","GWJ","GWK","GWL", +"GWM","GWN","GWO","GWP","GWQ","GWR","GWS","GWT","GWU","GWV","GWW","GWX","GWY","GWZ","GXA","GXB", +"GXC","GXD","GXE","GXF","GXG","GXH","GXI","GXJ","GXK","GXL","GXM","GXN","GXO","GXP","GXQ","GXR", +"GXS","GXT","GXU","GXV","GXW","GXX","GXY","GXZ","GYA","GYB","GYC","GYD","GYE","GYF","GYG","GYH", +"GYI","GYJ","GYK","GYL","GYM","GYN","GYO","GYP","GYQ","GYR","GYS","GYT","GYU","GYV","GYW","GYX", +"GYY","GYZ","GZA","GZB","GZC","GZD","GZE","GZF","GZG","GZH","GZI","GZJ","GZK","GZL","GZM","GZN", +"GZO","GZP","GZQ","GZR","GZS","GZT","GZU","GZV","GZW","GZX","GZY","GZZ","HAA","HAB","HAC","HAD", +"HAE","HAF","HAG","HAH","HAI","HAJ","HAK","HAL","HAM","HAN","HAO","HAP","HAQ","HAR","HAS","HAT", +"HAU","HAV","HAW","HAX","HAY","HAZ","HBA","HBB","HBC","HBD","HBE","HBF","HBG","HBH","HBI","HBJ", +"HBK","HBL","HBM","HBN","HBO","HBP","HBQ","HBR","HBS","HBT","HBU","HBV","HBW","HBX","HBY","HBZ", +"HCA","HCB","HCC","HCD","HCE","HCF","HCG","HCH","HCI","HCJ","HCK","HCL","HCM","HCN","HCO","HCP", +"HCQ","HCR","HCS","HCT","HCU","HCV","HCW","HCX","HCY","HCZ","HDA","HDB","HDC","HDD","HDE","HDF", +"HDG","HDH","HDI","HDJ","HDK","HDL","HDM","HDN","HDO","HDP","HDQ","HDR","HDS","HDT","HDU","HDV", +"HDW","HDX","HDY","HDZ","HEA","HEB","HEC","HED","HEE","HEF","HEG","HEH","HEI","HEJ","HEK","HEL", +"HEM","HEN","HEO","HEP","HEQ","HER","HES","HET","HEU","HEV","HEW","HEX","HEY","HEZ","HFA","HFB", +"HFC","HFD","HFE","HFF","HFG","HFH","HFI","HFJ","HFK","HFL","HFM","HFN","HFO","HFP","HFQ","HFR", +"HFS","HFT","HFU","HFV","HFW","HFX","HFY","HFZ","HGA","HGB","HGC","HGD","HGE","HGF","HGG","HGH", +"HGI","HGJ","HGK","HGL","HGM","HGN","HGO","HGP","HGQ","HGR","HGS","HGT","HGU","HGV","HGW","HGX", +"HGY","HGZ","HHA","HHB","HHC","HHD","HHE","HHF","HHG","HHH","HHI","HHJ","HHK","HHL","HHM","HHN", +"HHO","HHP","HHQ","HHR","HHS","HHT","HHU","HHV","HHW","HHX","HHY","HHZ","HIA","HIB","HIC","HID", +"HIE","HIF","HIG","HIH","HII","HIJ","HIK","HIL","HIM","HIN","HIO","HIP","HIQ","HIR","HIS","HIT", +"HIU","HIV","HIW","HIX","HIY","HIZ","HJA","HJB","HJC","HJD","HJE","HJF","HJG","HJH","HJI","HJJ", +"HJK","HJL","HJM","HJN","HJO","HJP","HJQ","HJR","HJS","HJT","HJU","HJV","HJW","HJX","HJY","HJZ", +"HKA","HKB","HKC","HKD","HKE","HKF","HKG","HKH","HKI","HKJ","HKK","HKL","HKM","HKN","HKO","HKP", +"HKQ","HKR","HKS","HKT","HKU","HKV","HKW","HKX","HKY","HKZ","HLA","HLB","HLC","HLD","HLE","HLF", +"HLG","HLH","HLI","HLJ","HLK","HLL","HLM","HLN","HLO","HLP","HLQ","HLR","HLS","HLT","HLU","HLV", +"HLW","HLX","HLY","HLZ","HMA","HMB","HMC","HMD","HME","HMF","HMG","HMH","HMI","HMJ","HMK","HML", +"HMM","HMN","HMO","HMP","HMQ","HMR","HMS","HMT","HMU","HMV","HMW","HMX","HMY","HMZ","HNA","HNB", +"HNC","HND","HNE","HNF","HNG","HNH","HNI","HNJ","HNK","HNL","HNM","HNN","HNO","HNP","HNQ","HNR", +"HNS","HNT","HNU","HNV","HNW","HNX","HNY","HNZ","HOA","HOB","HOC","HOD","HOE","HOF","HOG","HOH", +"HOI","HOJ","HOK","HOL","HOM","HON","HOO","HOP","HOQ","HOR","HOS","HOT","HOU","HOV","HOW","HOX", +"HOY","HOZ","HPA","HPB","HPC","HPD","HPE","HPF","HPG","HPH","HPI","HPJ","HPK","HPL","HPM","HPN", +"HPO","HPP","HPQ","HPR","HPS","HPT","HPU","HPV","HPW","HPX","HPY","HPZ","HQA","HQB","HQC","HQD", +"HQE","HQF","HQG","HQH","HQI","HQJ","HQK","HQL","HQM","HQN","HQO","HQP","HQQ","HQR","HQS","HQT", +"HQU","HQV","HQW","HQX","HQY","HQZ","HRA","HRB","HRC","HRD","HRE","HRF","HRG","HRH","HRI","HRJ", +"HRK","HRL","HRM","HRN","HRO","HRP","HRQ","HRR","HRS","HRT","HRU","HRV","HRW","HRX","HRY","HRZ", +"HSA","HSB","HSC","HSD","HSE","HSF","HSG","HSH","HSI","HSJ","HSK","HSL","HSM","HSN","HSO","HSP", +"HSQ","HSR","HSS","HST","HSU","HSV","HSW","HSX","HSY","HSZ","HTA","HTB","HTC","HTD","HTE","HTF", +"HTG","HTH","HTI","HTJ","HTK","HTL","HTM","HTN","HTO","HTP","HTQ","HTR","HTS","HTT","HTU","HTV", +"HTW","HTX","HTY","HTZ","HUA","HUB","HUC","HUD","HUE","HUF","HUG","HUH","HUI","HUJ","HUK","HUL", +"HUM","HUN","HUO","HUP","HUQ","HUR","HUS","HUT","HUU","HUV","HUW","HUX","HUY","HUZ","HVA","HVB", +"HVC","HVD","HVE","HVF","HVG","HVH","HVI","HVJ","HVK","HVL","HVM","HVN","HVO","HVP","HVQ","HVR", +"HVS","HVT","HVU","HVV","HVW","HVX","HVY","HVZ","HWA","HWB","HWC","HWD","HWE","HWF","HWG","HWH", +"HWI","HWJ","HWK","HWL","HWM","HWN","HWO","HWP","HWQ","HWR","HWS","HWT","HWU","HWV","HWW","HWX", +"HWY","HWZ","HXA","HXB","HXC","HXD","HXE","HXF","HXG","HXH","HXI","HXJ","HXK","HXL","HXM","HXN", +"HXO","HXP","HXQ","HXR","HXS","HXT","HXU","HXV","HXW","HXX","HXY","HXZ","HYA","HYB","HYC","HYD", +"HYE","HYF","HYG","HYH","HYI","HYJ","HYK","HYL","HYM","HYN","HYO","HYP","HYQ","HYR","HYS","HYT", +"HYU","HYV","HYW","HYX","HYY","HYZ","HZA","HZB","HZC","HZD","HZE","HZF","HZG","HZH","HZI","HZJ", +"HZK","HZL","HZM","HZN","HZO","HZP","HZQ","HZR","HZS","HZT","HZU","HZV","HZW","HZX","HZY","HZZ", +"IAA","IAB","IAC","IAD","IAE","IAF","IAG","IAH","IAI","IAJ","IAK","IAL","IAM","IAN","IAO","IAP", +"IAQ","IAR","IAS","IAT","IAU","IAV","IAW","IAX","IAY","IAZ","IBA","IBB","IBC","IBD","IBE","IBF", +"IBG","IBH","IBI","IBJ","IBK","IBL","IBM","IBN","IBO","IBP","IBQ","IBR","IBS","IBT","IBU","IBV", +"IBW","IBX","IBY","IBZ","ICA","ICB","ICC","ICD","ICE","ICF","ICG","ICH","ICI","ICJ","ICK","ICL", +"ICM","ICN","ICO","ICP","ICQ","ICR","ICS","ICT","ICU","ICV","ICW","ICX","ICY","ICZ","IDA","IDB", +"IDC","IDD","IDE","IDF","IDG","IDH","IDI","IDJ","IDK","IDL","IDM","IDN","IDO","IDP","IDQ","IDR", +"IDS","IDT","IDU","IDV","IDW","IDX","IDY","IDZ","IEA","IEB","IEC","IED","IEE","IEF","IEG","IEH", +"IEI","IEJ","IEK","IEL","IEM","IEN","IEO","IEP","IEQ","IER","IES","IET","IEU","IEV","IEW","IEX", +"IEY","IEZ","IFA","IFB","IFC","IFD","IFE","IFF","IFG","IFH","IFI","IFJ","IFK","IFL","IFM","IFN", +"IFO","IFP","IFQ","IFR","IFS","IFT","IFU","IFV","IFW","IFX","IFY","IFZ","IGA","IGB","IGC","IGD", +"IGE","IGF","IGG","IGH","IGI","IGJ","IGK","IGL","IGM","IGN","IGO","IGP","IGQ","IGR","IGS","IGT", +"IGU","IGV","IGW","IGX","IGY","IGZ","IHA","IHB","IHC","IHD","IHE","IHF","IHG","IHH","IHI","IHJ", +"IHK","IHL","IHM","IHN","IHO","IHP","IHQ","IHR","IHS","IHT","IHU","IHV","IHW","IHX","IHY","IHZ", +"IIA","IIB","IIC","IID","IIE","IIF","IIG","IIH","III","IIJ","IIK","IIL","IIM","IIN","IIO","IIP", +"IIQ","IIR","IIS","IIT","IIU","IIV","IIW","IIX","IIY","IIZ","IJA","IJB","IJC","IJD","IJE","IJF", +"IJG","IJH","IJI","IJJ","IJK","IJL","IJM","IJN","IJO","IJP","IJQ","IJR","IJS","IJT","IJU","IJV", +"IJW","IJX","IJY","IJZ","IKA","IKB","IKC","IKD","IKE","IKF","IKG","IKH","IKI","IKJ","IKK","IKL", +"IKM","IKN","IKO","IKP","IKQ","IKR","IKS","IKT","IKU","IKV","IKW","IKX","IKY","IKZ","ILA","ILB", +"ILC","ILD","ILE","ILF","ILG","ILH","ILI","ILJ","ILK","ILL","ILM","ILN","ILO","ILP","ILQ","ILR", +"ILS","ILT","ILU","ILV","ILW","ILX","ILY","ILZ","IMA","IMB","IMC","IMD","IME","IMF","IMG","IMH", +"IMI","IMJ","IMK","IML","IMM","IMN","IMO","IMP","IMQ","IMR","IMS","IMT","IMU","IMV","IMW","IMX", +"IMY","IMZ","INA","INB","INC","IND","INE","INF","ING","INH","INI","INJ","INK","INL","INM","INN", +"INO","INP","INQ","INR","INS","INT","INU","INV","INW","INX","INY","INZ","IOA","IOB","IOC","IOD", +"IOE","IOF","IOG","IOH","IOI","IOJ","IOK","IOL","IOM","ION","IOO","IOP","IOQ","IOR","IOS","IOT", +"IOU","IOV","IOW","IOX","IOY","IOZ","IPA","IPB","IPC","IPD","IPE","IPF","IPG","IPH","IPI","IPJ", +"IPK","IPL","IPM","IPN","IPO","IPP","IPQ","IPR","IPS","IPT","IPU","IPV","IPW","IPX","IPY","IPZ", +"IQA","IQB","IQC","IQD","IQE","IQF","IQG","IQH","IQI","IQJ","IQK","IQL","IQM","IQN","IQO","IQP", +"IQQ","IQR","IQS","IQT","IQU","IQV","IQW","IQX","IQY","IQZ","IRA","IRB","IRC","IRD","IRE","IRF", +"IRG","IRH","IRI","IRJ","IRK","IRL","IRM","IRN","IRO","IRP","IRQ","IRR","IRS","IRT","IRU","IRV", +"IRW","IRX","IRY","IRZ","ISA","ISB","ISC","ISD","ISE","ISF","ISG","ISH","ISI","ISJ","ISK","ISL", +"ISM","ISN","ISO","ISP","ISQ","ISR","ISS","IST","ISU","ISV","ISW","ISX","ISY","ISZ","ITA","ITB", +"ITC","ITD","ITE","ITF","ITG","ITH","ITI","ITJ","ITK","ITL","ITM","ITN","ITO","ITP","ITQ","ITR", +"ITS","ITT","ITU","ITV","ITW","ITX","ITY","ITZ","IUA","IUB","IUC","IUD","IUE","IUF","IUG","IUH", +"IUI","IUJ","IUK","IUL","IUM","IUN","IUO","IUP","IUQ","IUR","IUS","IUT","IUU","IUV","IUW","IUX", +"IUY","IUZ","IVA","IVB","IVC","IVD","IVE","IVF","IVG","IVH","IVI","IVJ","IVK","IVL","IVM","IVN", +"IVO","IVP","IVQ","IVR","IVS","IVT","IVU","IVV","IVW","IVX","IVY","IVZ","IWA","IWB","IWC","IWD", +"IWE","IWF","IWG","IWH","IWI","IWJ","IWK","IWL","IWM","IWN","IWO","IWP","IWQ","IWR","IWS","IWT", +"IWU","IWV","IWW","IWX","IWY","IWZ","IXA","IXB","IXC","IXD","IXE","IXF","IXG","IXH","IXI","IXJ", +"IXK","IXL","IXM","IXN","IXO","IXP","IXQ","IXR","IXS","IXT","IXU","IXV","IXW","IXX","IXY","IXZ", +"IYA","IYB","IYC","IYD","IYE","IYF","IYG","IYH","IYI","IYJ","IYK","IYL","IYM","IYN","IYO","IYP", +"IYQ","IYR","IYS","IYT","IYU","IYV","IYW","IYX","IYY","IYZ","IZA","IZB","IZC","IZD","IZE","IZF", +"IZG","IZH","IZI","IZJ","IZK","IZL","IZM","IZN","IZO","IZP","IZQ","IZR","IZS","IZT","IZU","IZV", +"IZW","IZX","IZY","IZZ","JAA","JAB","JAC","JAD","JAE","JAF","JAG","JAH","JAI","JAJ","JAK","JAL", +"JAM","JAN","JAO","JAP","JAQ","JAR","JAS","JAT","JAU","JAV","JAW","JAX","JAY","JAZ","JBA","JBB", +"JBC","JBD","JBE","JBF","JBG","JBH","JBI","JBJ","JBK","JBL","JBM","JBN","JBO","JBP","JBQ","JBR", +"JBS","JBT","JBU","JBV","JBW","JBX","JBY","JBZ","JCA","JCB","JCC","JCD","JCE","JCF","JCG","JCH", +"JCI","JCJ","JCK","JCL","JCM","JCN","JCO","JCP","JCQ","JCR","JCS","JCT","JCU","JCV","JCW","JCX", +"JCY","JCZ","JDA","JDB","JDC","JDD","JDE","JDF","JDG","JDH","JDI","JDJ","JDK","JDL","JDM","JDN", +"JDO","JDP","JDQ","JDR","JDS","JDT","JDU","JDV","JDW","JDX","JDY","JDZ","JEA","JEB","JEC","JED", +"JEE","JEF","JEG","JEH","JEI","JEJ","JEK","JEL","JEM","JEN","JEO","JEP","JEQ","JER","JES","JET", +"JEU","JEV","JEW","JEX","JEY","JEZ","JFA","JFB","JFC","JFD","JFE","JFF","JFG","JFH","JFI","JFJ", +"JFK","JFL","JFM","JFN","JFO","JFP","JFQ","JFR","JFS","JFT","JFU","JFV","JFW","JFX","JFY","JFZ", +"JGA","JGB","JGC","JGD","JGE","JGF","JGG","JGH","JGI","JGJ","JGK","JGL","JGM","JGN","JGO","JGP", +"JGQ","JGR","JGS","JGT","JGU","JGV","JGW","JGX","JGY","JGZ","JHA","JHB","JHC","JHD","JHE","JHF", +"JHG","JHH","JHI","JHJ","JHK","JHL","JHM","JHN","JHO","JHP","JHQ","JHR","JHS","JHT","JHU","JHV", +"JHW","JHX","JHY","JHZ","JIA","JIB","JIC","JID","JIE","JIF","JIG","JIH","JII","JIJ","JIK","JIL", +"JIM","JIN","JIO","JIP","JIQ","JIR","JIS","JIT","JIU","JIV","JIW","JIX","JIY","JIZ","JJA","JJB", +"JJC","JJD","JJE","JJF","JJG","JJH","JJI","JJJ","JJK","JJL","JJM","JJN","JJO","JJP","JJQ","JJR", +"JJS","JJT","JJU","JJV","JJW","JJX","JJY","JJZ","JKA","JKB","JKC","JKD","JKE","JKF","JKG","JKH", +"JKI","JKJ","JKK","JKL","JKM","JKN","JKO","JKP","JKQ","JKR","JKS","JKT","JKU","JKV","JKW","JKX", +"JKY","JKZ","JLA","JLB","JLC","JLD","JLE","JLF","JLG","JLH","JLI","JLJ","JLK","JLL","JLM","JLN", +"JLO","JLP","JLQ","JLR","JLS","JLT","JLU","JLV","JLW","JLX","JLY","JLZ","JMA","JMB","JMC","JMD", +"JME","JMF","JMG","JMH","JMI","JMJ","JMK","JML","JMM","JMN","JMO","JMP","JMQ","JMR","JMS","JMT", +"JMU","JMV","JMW","JMX","JMY","JMZ","JNA","JNB","JNC","JND","JNE","JNF","JNG","JNH","JNI","JNJ", +"JNK","JNL","JNM","JNN","JNO","JNP","JNQ","JNR","JNS","JNT","JNU","JNV","JNW","JNX","JNY","JNZ", +"JOA","JOB","JOC","JOD","JOE","JOF","JOG","JOH","JOI","JOJ","JOK","JOL","JOM","JON","JOO","JOP", +"JOQ","JOR","JOS","JOT","JOU","JOV","JOW","JOX","JOY","JOZ","JPA","JPB","JPC","JPD","JPE","JPF", +"JPG","JPH","JPI","JPJ","JPK","JPL","JPM","JPN","JPO","JPP","JPQ","JPR","JPS","JPT","JPU","JPV", +"JPW","JPX","JPY","JPZ","JQA","JQB","JQC","JQD","JQE","JQF","JQG","JQH","JQI","JQJ","JQK","JQL", +"JQM","JQN","JQO","JQP","JQQ","JQR","JQS","JQT","JQU","JQV","JQW","JQX","JQY","JQZ","JRA","JRB", +"JRC","JRD","JRE","JRF","JRG","JRH","JRI","JRJ","JRK","JRL","JRM","JRN","JRO","JRP","JRQ","JRR", +"JRS","JRT","JRU","JRV","JRW","JRX","JRY","JRZ","JSA","JSB","JSC","JSD","JSE","JSF","JSG","JSH", +"JSI","JSJ","JSK","JSL","JSM","JSN","JSO","JSP","JSQ","JSR","JSS","JST","JSU","JSV","JSW","JSX", +"JSY","JSZ","JTA","JTB","JTC","JTD","JTE","JTF","JTG","JTH","JTI","JTJ","JTK","JTL","JTM","JTN", +"JTO","JTP","JTQ","JTR","JTS","JTT","JTU","JTV","JTW","JTX","JTY","JTZ","JUA","JUB","JUC","JUD", +"JUE","JUF","JUG","JUH","JUI","JUJ","JUK","JUL","JUM","JUN","JUO","JUP","JUQ","JUR","JUS","JUT", +"JUU","JUV","JUW","JUX","JUY","JUZ","JVA","JVB","JVC","JVD","JVE","JVF","JVG","JVH","JVI","JVJ", +"JVK","JVL","JVM","JVN","JVO","JVP","JVQ","JVR","JVS","JVT","JVU","JVV","JVW","JVX","JVY","JVZ", +"JWA","JWB","JWC","JWD","JWE","JWF","JWG","JWH","JWI","JWJ","JWK","JWL","JWM","JWN","JWO","JWP", +"JWQ","JWR","JWS","JWT","JWU","JWV","JWW","JWX","JWY","JWZ","JXA","JXB","JXC","JXD","JXE","JXF", +"JXG","JXH","JXI","JXJ","JXK","JXL","JXM","JXN","JXO","JXP","JXQ","JXR","JXS","JXT","JXU","JXV", +"JXW","JXX","JXY","JXZ","JYA","JYB","JYC","JYD","JYE","JYF","JYG","JYH","JYI","JYJ","JYK","JYL", +"JYM","JYN","JYO","JYP","JYQ","JYR","JYS","JYT","JYU","JYV","JYW","JYX","JYY","JYZ","JZA","JZB", +"JZC","JZD","JZE","JZF","JZG","JZH","JZI","JZJ","JZK","JZL","JZM","JZN","JZO","JZP","JZQ","JZR", +"JZS","JZT","JZU","JZV","JZW","JZX","JZY","JZZ","KAA","KAB","KAC","KAD","KAE","KAF","KAG","KAH", +"KAI","KAJ","KAK","KAL","KAM","KAN","KAO","KAP","KAQ","KAR","KAS","KAT","KAU","KAV","KAW","KAX", +"KAY","KAZ","KBA","KBB","KBC","KBD","KBE","KBF","KBG","KBH","KBI","KBJ","KBK","KBL","KBM","KBN", +"KBO","KBP","KBQ","KBR","KBS","KBT","KBU","KBV","KBW","KBX","KBY","KBZ","KCA","KCB","KCC","KCD", +"KCE","KCF","KCG","KCH","KCI","KCJ","KCK","KCL","KCM","KCN","KCO","KCP","KCQ","KCR","KCS","KCT", +"KCU","KCV","KCW","KCX","KCY","KCZ","KDA","KDB","KDC","KDD","KDE","KDF","KDG","KDH","KDI","KDJ", +"KDK","KDL","KDM","KDN","KDO","KDP","KDQ","KDR","KDS","KDT","KDU","KDV","KDW","KDX","KDY","KDZ", +"KEA","KEB","KEC","KED","KEE","KEF","KEG","KEH","KEI","KEJ","KEK","KEL","KEM","KEN","KEO","KEP", +"KEQ","KER","KES","KET","KEU","KEV","KEW","KEX","KEY","KEZ","KFA","KFB","KFC","KFD","KFE","KFF", +"KFG","KFH","KFI","KFJ","KFK","KFL","KFM","KFN","KFO","KFP","KFQ","KFR","KFS","KFT","KFU","KFV", +"KFW","KFX","KFY","KFZ","KGA","KGB","KGC","KGD","KGE","KGF","KGG","KGH","KGI","KGJ","KGK","KGL", +"KGM","KGN","KGO","KGP","KGQ","KGR","KGS","KGT","KGU","KGV","KGW","KGX","KGY","KGZ","KHA","KHB", +"KHC","KHD","KHE","KHF","KHG","KHH","KHI","KHJ","KHK","KHL","KHM","KHN","KHO","KHP","KHQ","KHR", +"KHS","KHT","KHU","KHV","KHW","KHX","KHY","KHZ","KIA","KIB","KIC","KID","KIE","KIF","KIG","KIH", +"KII","KIJ","KIK","KIL","KIM","KIN","KIO","KIP","KIQ","KIR","KIS","KIT","KIU","KIV","KIW","KIX", +"KIY","KIZ","KJA","KJB","KJC","KJD","KJE","KJF","KJG","KJH","KJI","KJJ","KJK","KJL","KJM","KJN", +"KJO","KJP","KJQ","KJR","KJS","KJT","KJU","KJV","KJW","KJX","KJY","KJZ","KKA","KKB","KKC","KKD", +"KKE","KKF","KKG","KKH","KKI","KKJ","KKK","KKL","KKM","KKN","KKO","KKP","KKQ","KKR","KKS","KKT", +"KKU","KKV","KKW","KKX","KKY","KKZ","KLA","KLB","KLC","KLD","KLE","KLF","KLG","KLH","KLI","KLJ", +"KLK","KLL","KLM","KLN","KLO","KLP","KLQ","KLR","KLS","KLT","KLU","KLV","KLW","KLX","KLY","KLZ", +"KMA","KMB","KMC","KMD","KME","KMF","KMG","KMH","KMI","KMJ","KMK","KML","KMM","KMN","KMO","KMP", +"KMQ","KMR","KMS","KMT","KMU","KMV","KMW","KMX","KMY","KMZ","KNA","KNB","KNC","KND","KNE","KNF", +"KNG","KNH","KNI","KNJ","KNK","KNL","KNM","KNN","KNO","KNP","KNQ","KNR","KNS","KNT","KNU","KNV", +"KNW","KNX","KNY","KNZ","KOA","KOB","KOC","KOD","KOE","KOF","KOG","KOH","KOI","KOJ","KOK","KOL", +"KOM","KON","KOO","KOP","KOQ","KOR","KOS","KOT","KOU","KOV","KOW","KOX","KOY","KOZ","KPA","KPB", +"KPC","KPD","KPE","KPF","KPG","KPH","KPI","KPJ","KPK","KPL","KPM","KPN","KPO","KPP","KPQ","KPR", +"KPS","KPT","KPU","KPV","KPW","KPX","KPY","KPZ","KQA","KQB","KQC","KQD","KQE","KQF","KQG","KQH", +"KQI","KQJ","KQK","KQL","KQM","KQN","KQO","KQP","KQQ","KQR","KQS","KQT","KQU","KQV","KQW","KQX", +"KQY","KQZ","KRA","KRB","KRC","KRD","KRE","KRF","KRG","KRH","KRI","KRJ","KRK","KRL","KRM","KRN", +"KRO","KRP","KRQ","KRR","KRS","KRT","KRU","KRV","KRW","KRX","KRY","KRZ","KSA","KSB","KSC","KSD", +"KSE","KSF","KSG","KSH","KSI","KSJ","KSK","KSL","KSM","KSN","KSO","KSP","KSQ","KSR","KSS","KST", +"KSU","KSV","KSW","KSX","KSY","KSZ","KTA","KTB","KTC","KTD","KTE","KTF","KTG","KTH","KTI","KTJ", +"KTK","KTL","KTM","KTN","KTO","KTP","KTQ","KTR","KTS","KTT","KTU","KTV","KTW","KTX","KTY","KTZ", +"KUA","KUB","KUC","KUD","KUE","KUF","KUG","KUH","KUI","KUJ","KUK","KUL","KUM","KUN","KUO","KUP", +"KUQ","KUR","KUS","KUT","KUU","KUV","KUW","KUX","KUY","KUZ","KVA","KVB","KVC","KVD","KVE","KVF", +"KVG","KVH","KVI","KVJ","KVK","KVL","KVM","KVN","KVO","KVP","KVQ","KVR","KVS","KVT","KVU","KVV", +"KVW","KVX","KVY","KVZ","KWA","KWB","KWC","KWD","KWE","KWF","KWG","KWH","KWI","KWJ","KWK","KWL", +"KWM","KWN","KWO","KWP","KWQ","KWR","KWS","KWT","KWU","KWV","KWW","KWX","KWY","KWZ","KXA","KXB", +"KXC","KXD","KXE","KXF","KXG","KXH","KXI","KXJ","KXK","KXL","KXM","KXN","KXO","KXP","KXQ","KXR", +"KXS","KXT","KXU","KXV","KXW","KXX","KXY","KXZ","KYA","KYB","KYC","KYD","KYE","KYF","KYG","KYH", +"KYI","KYJ","KYK","KYL","KYM","KYN","KYO","KYP","KYQ","KYR","KYS","KYT","KYU","KYV","KYW","KYX", +"KYY","KYZ","KZA","KZB","KZC","KZD","KZE","KZF","KZG","KZH","KZI","KZJ","KZK","KZL","KZM","KZN", +"KZO","KZP","KZQ","KZR","KZS","KZT","KZU","KZV","KZW","KZX","KZY","KZZ","LAA","LAB","LAC","LAD", +"LAE","LAF","LAG","LAH","LAI","LAJ","LAK","LAL","LAM","LAN","LAO","LAP","LAQ","LAR","LAS","LAT", +"LAU","LAV","LAW","LAX","LAY","LAZ","LBA","LBB","LBC","LBD","LBE","LBF","LBG","LBH","LBI","LBJ", +"LBK","LBL","LBM","LBN","LBO","LBP","LBQ","LBR","LBS","LBT","LBU","LBV","LBW","LBX","LBY","LBZ", +"LCA","LCB","LCC","LCD","LCE","LCF","LCG","LCH","LCI","LCJ","LCK","LCL","LCM","LCN","LCO","LCP", +"LCQ","LCR","LCS","LCT","LCU","LCV","LCW","LCX","LCY","LCZ","LDA","LDB","LDC","LDD","LDE","LDF", +"LDG","LDH","LDI","LDJ","LDK","LDL","LDM","LDN","LDO","LDP","LDQ","LDR","LDS","LDT","LDU","LDV", +"LDW","LDX","LDY","LDZ","LEA","LEB","LEC","LED","LEE","LEF","LEG","LEH","LEI","LEJ","LEK","LEL", +"LEM","LEN","LEO","LEP","LEQ","LER","LES","LET","LEU","LEV","LEW","LEX","LEY","LEZ","LFA","LFB", +"LFC","LFD","LFE","LFF","LFG","LFH","LFI","LFJ","LFK","LFL","LFM","LFN","LFO","LFP","LFQ","LFR", +"LFS","LFT","LFU","LFV","LFW","LFX","LFY","LFZ","LGA","LGB","LGC","LGD","LGE","LGF","LGG","LGH", +"LGI","LGJ","LGK","LGL","LGM","LGN","LGO","LGP","LGQ","LGR","LGS","LGT","LGU","LGV","LGW","LGX", +"LGY","LGZ","LHA","LHB","LHC","LHD","LHE","LHF","LHG","LHH","LHI","LHJ","LHK","LHL","LHM","LHN", +"LHO","LHP","LHQ","LHR","LHS","LHT","LHU","LHV","LHW","LHX","LHY","LHZ","LIA","LIB","LIC","LID", +"LIE","LIF","LIG","LIH","LII","LIJ","LIK","LIL","LIM","LIN","LIO","LIP","LIQ","LIR","LIS","LIT", +"LIU","LIV","LIW","LIX","LIY","LIZ","LJA","LJB","LJC","LJD","LJE","LJF","LJG","LJH","LJI","LJJ", +"LJK","LJL","LJM","LJN","LJO","LJP","LJQ","LJR","LJS","LJT","LJU","LJV","LJW","LJX","LJY","LJZ", +"LKA","LKB","LKC","LKD","LKE","LKF","LKG","LKH","LKI","LKJ","LKK","LKL","LKM","LKN","LKO","LKP", +"LKQ","LKR","LKS","LKT","LKU","LKV","LKW","LKX","LKY","LKZ","LLA","LLB","LLC","LLD","LLE","LLF", +"LLG","LLH","LLI","LLJ","LLK","LLL","LLM","LLN","LLO","LLP","LLQ","LLR","LLS","LLT","LLU","LLV", +"LLW","LLX","LLY","LLZ","LMA","LMB","LMC","LMD","LME","LMF","LMG","LMH","LMI","LMJ","LMK","LML", +"LMM","LMN","LMO","LMP","LMQ","LMR","LMS","LMT","LMU","LMV","LMW","LMX","LMY","LMZ","LNA","LNB", +"LNC","LND","LNE","LNF","LNG","LNH","LNI","LNJ","LNK","LNL","LNM","LNN","LNO","LNP","LNQ","LNR", +"LNS","LNT","LNU","LNV","LNW","LNX","LNY","LNZ","LOA","LOB","LOC","LOD","LOE","LOF","LOG","LOH", +"LOI","LOJ","LOK","LOL","LOM","LON","LOO","LOP","LOQ","LOR","LOS","LOT","LOU","LOV","LOW","LOX", +"LOY","LOZ","LPA","LPB","LPC","LPD","LPE","LPF","LPG","LPH","LPI","LPJ","LPK","LPL","LPM","LPN", +"LPO","LPP","LPQ","LPR","LPS","LPT","LPU","LPV","LPW","LPX","LPY","LPZ","LQA","LQB","LQC","LQD", +"LQE","LQF","LQG","LQH","LQI","LQJ","LQK","LQL","LQM","LQN","LQO","LQP","LQQ","LQR","LQS","LQT", +"LQU","LQV","LQW","LQX","LQY","LQZ","LRA","LRB","LRC","LRD","LRE","LRF","LRG","LRH","LRI","LRJ", +"LRK","LRL","LRM","LRN","LRO","LRP","LRQ","LRR","LRS","LRT","LRU","LRV","LRW","LRX","LRY","LRZ", +"LSA","LSB","LSC","LSD","LSE","LSF","LSG","LSH","LSI","LSJ","LSK","LSL","LSM","LSN","LSO","LSP", +"LSQ","LSR","LSS","LST","LSU","LSV","LSW","LSX","LSY","LSZ","LTA","LTB","LTC","LTD","LTE","LTF", +"LTG","LTH","LTI","LTJ","LTK","LTL","LTM","LTN","LTO","LTP","LTQ","LTR","LTS","LTT","LTU","LTV", +"LTW","LTX","LTY","LTZ","LUA","LUB","LUC","LUD","LUE","LUF","LUG","LUH","LUI","LUJ","LUK","LUL", +"LUM","LUN","LUO","LUP","LUQ","LUR","LUS","LUT","LUU","LUV","LUW","LUX","LUY","LUZ","LVA","LVB", +"LVC","LVD","LVE","LVF","LVG","LVH","LVI","LVJ","LVK","LVL","LVM","LVN","LVO","LVP","LVQ","LVR", +"LVS","LVT","LVU","LVV","LVW","LVX","LVY","LVZ","LWA","LWB","LWC","LWD","LWE","LWF","LWG","LWH", +"LWI","LWJ","LWK","LWL","LWM","LWN","LWO","LWP","LWQ","LWR","LWS","LWT","LWU","LWV","LWW","LWX", +"LWY","LWZ","LXA","LXB","LXC","LXD","LXE","LXF","LXG","LXH","LXI","LXJ","LXK","LXL","LXM","LXN", +"LXO","LXP","LXQ","LXR","LXS","LXT","LXU","LXV","LXW","LXX","LXY","LXZ","LYA","LYB","LYC","LYD", +"LYE","LYF","LYG","LYH","LYI","LYJ","LYK","LYL","LYM","LYN","LYO","LYP","LYQ","LYR","LYS","LYT", +"LYU","LYV","LYW","LYX","LYY","LYZ","LZA","LZB","LZC","LZD","LZE","LZF","LZG","LZH","LZI","LZJ", +"LZK","LZL","LZM","LZN","LZO","LZP","LZQ","LZR","LZS","LZT","LZU","LZV","LZW","LZX","LZY","LZZ", +"MAA","MAB","MAC","MAD","MAE","MAF","MAG","MAH","MAI","MAJ","MAK","MAL","MAM","MAN","MAO","MAP", +"MAQ","MAR","MAS","MAT","MAU","MAV","MAW","MAX","MAY","MAZ","MBA","MBB","MBC","MBD","MBE","MBF", +"MBG","MBH","MBI","MBJ","MBK","MBL","MBM","MBN","MBO","MBP","MBQ","MBR","MBS","MBT","MBU","MBV", +"MBW","MBX","MBY","MBZ","MCA","MCB","MCC","MCD","MCE","MCF","MCG","MCH","MCI","MCJ","MCK","MCL", +"MCM","MCN","MCO","MCP","MCQ","MCR","MCS","MCT","MCU","MCV","MCW","MCX","MCY","MCZ","MDA","MDB", +"MDC","MDD","MDE","MDF","MDG","MDH","MDI","MDJ","MDK","MDL","MDM","MDN","MDO","MDP","MDQ","MDR", +"MDS","MDT","MDU","MDV","MDW","MDX","MDY","MDZ","MEA","MEB","MEC","MED","MEE","MEF","MEG","MEH", +"MEI","MEJ","MEK","MEL","MEM","MEN","MEO","MEP","MEQ","MER","MES","MET","MEU","MEV","MEW","MEX", +"MEY","MEZ","MFA","MFB","MFC","MFD","MFE","MFF","MFG","MFH","MFI","MFJ","MFK","MFL","MFM","MFN", +"MFO","MFP","MFQ","MFR","MFS","MFT","MFU","MFV","MFW","MFX","MFY","MFZ","MGA","MGB","MGC","MGD", +"MGE","MGF","MGG","MGH","MGI","MGJ","MGK","MGL","MGM","MGN","MGO","MGP","MGQ","MGR","MGS","MGT", +"MGU","MGV","MGW","MGX","MGY","MGZ","MHA","MHB","MHC","MHD","MHE","MHF","MHG","MHH","MHI","MHJ", +"MHK","MHL","MHM","MHN","MHO","MHP","MHQ","MHR","MHS","MHT","MHU","MHV","MHW","MHX","MHY","MHZ", +"MIA","MIB","MIC","MID","MIE","MIF","MIG","MIH","MII","MIJ","MIK","MIL","MIM","MIN","MIO","MIP", +"MIQ","MIR","MIS","MIT","MIU","MIV","MIW","MIX","MIY","MIZ","MJA","MJB","MJC","MJD","MJE","MJF", +"MJG","MJH","MJI","MJJ","MJK","MJL","MJM","MJN","MJO","MJP","MJQ","MJR","MJS","MJT","MJU","MJV", +"MJW","MJX","MJY","MJZ","MKA","MKB","MKC","MKD","MKE","MKF","MKG","MKH","MKI","MKJ","MKK","MKL", +"MKM","MKN","MKO","MKP","MKQ","MKR","MKS","MKT","MKU","MKV","MKW","MKX","MKY","MKZ","MLA","MLB", +"MLC","MLD","MLE","MLF","MLG","MLH","MLI","MLJ","MLK","MLL","MLM","MLN","MLO","MLP","MLQ","MLR", +"MLS","MLT","MLU","MLV","MLW","MLX","MLY","MLZ","MMA","MMB","MMC","MMD","MME","MMF","MMG","MMH", +"MMI","MMJ","MMK","MML","MMM","MMN","MMO","MMP","MMQ","MMR","MMS","MMT","MMU","MMV","MMW","MMX", +"MMY","MMZ","MNA","MNB","MNC","MND","MNE","MNF","MNG","MNH","MNI","MNJ","MNK","MNL","MNM","MNN", +"MNO","MNP","MNQ","MNR","MNS","MNT","MNU","MNV","MNW","MNX","MNY","MNZ","MOA","MOB","MOC","MOD", +"MOE","MOF","MOG","MOH","MOI","MOJ","MOK","MOL","MOM","MON","MOO","MOP","MOQ","MOR","MOS","MOT", +"MOU","MOV","MOW","MOX","MOY","MOZ","MPA","MPB","MPC","MPD","MPE","MPF","MPG","MPH","MPI","MPJ", +"MPK","MPL","MPM","MPN","MPO","MPP","MPQ","MPR","MPS","MPT","MPU","MPV","MPW","MPX","MPY","MPZ", +"MQA","MQB","MQC","MQD","MQE","MQF","MQG","MQH","MQI","MQJ","MQK","MQL","MQM","MQN","MQO","MQP", +"MQQ","MQR","MQS","MQT","MQU","MQV","MQW","MQX","MQY","MQZ","MRA","MRB","MRC","MRD","MRE","MRF", +"MRG","MRH","MRI","MRJ","MRK","MRL","MRM","MRN","MRO","MRP","MRQ","MRR","MRS","MRT","MRU","MRV", +"MRW","MRX","MRY","MRZ","MSA","MSB","MSC","MSD","MSE","MSF","MSG","MSH","MSI","MSJ","MSK","MSL", +"MSM","MSN","MSO","MSP","MSQ","MSR","MSS","MST","MSU","MSV","MSW","MSX","MSY","MSZ","MTA","MTB", +"MTC","MTD","MTE","MTF","MTG","MTH","MTI","MTJ","MTK","MTL","MTM","MTN","MTO","MTP","MTQ","MTR", +"MTS","MTT","MTU","MTV","MTW","MTX","MTY","MTZ","MUA","MUB","MUC","MUD","MUE","MUF","MUG","MUH", +"MUI","MUJ","MUK","MUL","MUM","MUN","MUO","MUP","MUQ","MUR","MUS","MUT","MUU","MUV","MUW","MUX", +"MUY","MUZ","MVA","MVB","MVC","MVD","MVE","MVF","MVG","MVH","MVI","MVJ","MVK","MVL","MVM","MVN", +"MVO","MVP","MVQ","MVR","MVS","MVT","MVU","MVV","MVW","MVX","MVY","MVZ","MWA","MWB","MWC","MWD", +"MWE","MWF","MWG","MWH","MWI","MWJ","MWK","MWL","MWM","MWN","MWO","MWP","MWQ","MWR","MWS","MWT", +"MWU","MWV","MWW","MWX","MWY","MWZ","MXA","MXB","MXC","MXD","MXE","MXF","MXG","MXH","MXI","MXJ", +"MXK","MXL","MXM","MXN","MXO","MXP","MXQ","MXR","MXS","MXT","MXU","MXV","MXW","MXX","MXY","MXZ", +"MYA","MYB","MYC","MYD","MYE","MYF","MYG","MYH","MYI","MYJ","MYK","MYL","MYM","MYN","MYO","MYP", +"MYQ","MYR","MYS","MYT","MYU","MYV","MYW","MYX","MYY","MYZ","MZA","MZB","MZC","MZD","MZE","MZF", +"MZG","MZH","MZI","MZJ","MZK","MZL","MZM","MZN","MZO","MZP","MZQ","MZR","MZS","MZT","MZU","MZV", +"MZW","MZX","MZY","MZZ","NAA","NAB","NAC","NAD","NAE","NAF","NAG","NAH","NAI","NAJ","NAK","NAL", +"NAM","NAN","NAO","NAP","NAQ","NAR","NAS","NAT","NAU","NAV","NAW","NAX","NAY","NAZ","NBA","NBB", +"NBC","NBD","NBE","NBF","NBG","NBH","NBI","NBJ","NBK","NBL","NBM","NBN","NBO","NBP","NBQ","NBR", +"NBS","NBT","NBU","NBV","NBW","NBX","NBY","NBZ","NCA","NCB","NCC","NCD","NCE","NCF","NCG","NCH", +"NCI","NCJ","NCK","NCL","NCM","NCN","NCO","NCP","NCQ","NCR","NCS","NCT","NCU","NCV","NCW","NCX", +"NCY","NCZ","NDA","NDB","NDC","NDD","NDE","NDF","NDG","NDH","NDI","NDJ","NDK","NDL","NDM","NDN", +"NDO","NDP","NDQ","NDR","NDS","NDT","NDU","NDV","NDW","NDX","NDY","NDZ","NEA","NEB","NEC","NED", +"NEE","NEF","NEG","NEH","NEI","NEJ","NEK","NEL","NEM","NEN","NEO","NEP","NEQ","NER","NES","NET", +"NEU","NEV","NEW","NEX","NEY","NEZ","NFA","NFB","NFC","NFD","NFE","NFF","NFG","NFH","NFI","NFJ", +"NFK","NFL","NFM","NFN","NFO","NFP","NFQ","NFR","NFS","NFT","NFU","NFV","NFW","NFX","NFY","NFZ", +"NGA","NGB","NGC","NGD","NGE","NGF","NGG","NGH","NGI","NGJ","NGK","NGL","NGM","NGN","NGO","NGP", +"NGQ","NGR","NGS","NGT","NGU","NGV","NGW","NGX","NGY","NGZ","NHA","NHB","NHC","NHD","NHE","NHF", +"NHG","NHH","NHI","NHJ","NHK","NHL","NHM","NHN","NHO","NHP","NHQ","NHR","NHS","NHT","NHU","NHV", +"NHW","NHX","NHY","NHZ","NIA","NIB","NIC","NID","NIE","NIF","NIG","NIH","NII","NIJ","NIK","NIL", +"NIM","NIN","NIO","NIP","NIQ","NIR","NIS","NIT","NIU","NIV","NIW","NIX","NIY","NIZ","NJA","NJB", +"NJC","NJD","NJE","NJF","NJG","NJH","NJI","NJJ","NJK","NJL","NJM","NJN","NJO","NJP","NJQ","NJR", +"NJS","NJT","NJU","NJV","NJW","NJX","NJY","NJZ","NKA","NKB","NKC","NKD","NKE","NKF","NKG","NKH", +"NKI","NKJ","NKK","NKL","NKM","NKN","NKO","NKP","NKQ","NKR","NKS","NKT","NKU","NKV","NKW","NKX", +"NKY","NKZ","NLA","NLB","NLC","NLD","NLE","NLF","NLG","NLH","NLI","NLJ","NLK","NLL","NLM","NLN", +"NLO","NLP","NLQ","NLR","NLS","NLT","NLU","NLV","NLW","NLX","NLY","NLZ","NMA","NMB","NMC","NMD", +"NME","NMF","NMG","NMH","NMI","NMJ","NMK","NML","NMM","NMN","NMO","NMP","NMQ","NMR","NMS","NMT", +"NMU","NMV","NMW","NMX","NMY","NMZ","NNA","NNB","NNC","NND","NNE","NNF","NNG","NNH","NNI","NNJ", +"NNK","NNL","NNM","NNN","NNO","NNP","NNQ","NNR","NNS","NNT","NNU","NNV","NNW","NNX","NNY","NNZ", +"NOA","NOB","NOC","NOD","NOE","NOF","NOG","NOH","NOI","NOJ","NOK","NOL","NOM","NON","NOO","NOP", +"NOQ","NOR","NOS","NOT","NOU","NOV","NOW","NOX","NOY","NOZ","NPA","NPB","NPC","NPD","NPE","NPF", +"NPG","NPH","NPI","NPJ","NPK","NPL","NPM","NPN","NPO","NPP","NPQ","NPR","NPS","NPT","NPU","NPV", +"NPW","NPX","NPY","NPZ","NQA","NQB","NQC","NQD","NQE","NQF","NQG","NQH","NQI","NQJ","NQK","NQL", +"NQM","NQN","NQO","NQP","NQQ","NQR","NQS","NQT","NQU","NQV","NQW","NQX","NQY","NQZ","NRA","NRB", +"NRC","NRD","NRE","NRF","NRG","NRH","NRI","NRJ","NRK","NRL","NRM","NRN","NRO","NRP","NRQ","NRR", +"NRS","NRT","NRU","NRV","NRW","NRX","NRY","NRZ","NSA","NSB","NSC","NSD","NSE","NSF","NSG","NSH", +"NSI","NSJ","NSK","NSL","NSM","NSN","NSO","NSP","NSQ","NSR","NSS","NST","NSU","NSV","NSW","NSX", +"NSY","NSZ","NTA","NTB","NTC","NTD","NTE","NTF","NTG","NTH","NTI","NTJ","NTK","NTL","NTM","NTN", +"NTO","NTP","NTQ","NTR","NTS","NTT","NTU","NTV","NTW","NTX","NTY","NTZ","NUA","NUB","NUC","NUD", +"NUE","NUF","NUG","NUH","NUI","NUJ","NUK","NUL","NUM","NUN","NUO","NUP","NUQ","NUR","NUS","NUT", +"NUU","NUV","NUW","NUX","NUY","NUZ","NVA","NVB","NVC","NVD","NVE","NVF","NVG","NVH","NVI","NVJ", +"NVK","NVL","NVM","NVN","NVO","NVP","NVQ","NVR","NVS","NVT","NVU","NVV","NVW","NVX","NVY","NVZ", +"NWA","NWB","NWC","NWD","NWE","NWF","NWG","NWH","NWI","NWJ","NWK","NWL","NWM","NWN","NWO","NWP", +"NWQ","NWR","NWS","NWT","NWU","NWV","NWW","NWX","NWY","NWZ","NXA","NXB","NXC","NXD","NXE","NXF", +"NXG","NXH","NXI","NXJ","NXK","NXL","NXM","NXN","NXO","NXP","NXQ","NXR","NXS","NXT","NXU","NXV", +"NXW","NXX","NXY","NXZ","NYA","NYB","NYC","NYD","NYE","NYF","NYG","NYH","NYI","NYJ","NYK","NYL", +"NYM","NYN","NYO","NYP","NYQ","NYR","NYS","NYT","NYU","NYV","NYW","NYX","NYY","NYZ","NZA","NZB", +"NZC","NZD","NZE","NZF","NZG","NZH","NZI","NZJ","NZK","NZL","NZM","NZN","NZO","NZP","NZQ","NZR", +"NZS","NZT","NZU","NZV","NZW","NZX","NZY","NZZ","OAA","OAB","OAC","OAD","OAE","OAF","OAG","OAH", +"OAI","OAJ","OAK","OAL","OAM","OAN","OAO","OAP","OAQ","OAR","OAS","OAT","OAU","OAV","OAW","OAX", +"OAY","OAZ","OBA","OBB","OBC","OBD","OBE","OBF","OBG","OBH","OBI","OBJ","OBK","OBL","OBM","OBN", +"OBO","OBP","OBQ","OBR","OBS","OBT","OBU","OBV","OBW","OBX","OBY","OBZ","OCA","OCB","OCC","OCD", +"OCE","OCF","OCG","OCH","OCI","OCJ","OCK","OCL","OCM","OCN","OCO","OCP","OCQ","OCR","OCS","OCT", +"OCU","OCV","OCW","OCX","OCY","OCZ","ODA","ODB","ODC","ODD","ODE","ODF","ODG","ODH","ODI","ODJ", +"ODK","ODL","ODM","ODN","ODO","ODP","ODQ","ODR","ODS","ODT","ODU","ODV","ODW","ODX","ODY","ODZ", +"OEA","OEB","OEC","OED","OEE","OEF","OEG","OEH","OEI","OEJ","OEK","OEL","OEM","OEN","OEO","OEP", +"OEQ","OER","OES","OET","OEU","OEV","OEW","OEX","OEY","OEZ","OFA","OFB","OFC","OFD","OFE","OFF", +"OFG","OFH","OFI","OFJ","OFK","OFL","OFM","OFN","OFO","OFP","OFQ","OFR","OFS","OFT","OFU","OFV", +"OFW","OFX","OFY","OFZ","OGA","OGB","OGC","OGD","OGE","OGF","OGG","OGH","OGI","OGJ","OGK","OGL", +"OGM","OGN","OGO","OGP","OGQ","OGR","OGS","OGT","OGU","OGV","OGW","OGX","OGY","OGZ","OHA","OHB", +"OHC","OHD","OHE","OHF","OHG","OHH","OHI","OHJ","OHK","OHL","OHM","OHN","OHO","OHP","OHQ","OHR", +"OHS","OHT","OHU","OHV","OHW","OHX","OHY","OHZ","OIA","OIB","OIC","OID","OIE","OIF","OIG","OIH", +"OII","OIJ","OIK","OIL","OIM","OIN","OIO","OIP","OIQ","OIR","OIS","OIT","OIU","OIV","OIW","OIX", +"OIY","OIZ","OJA","OJB","OJC","OJD","OJE","OJF","OJG","OJH","OJI","OJJ","OJK","OJL","OJM","OJN", +"OJO","OJP","OJQ","OJR","OJS","OJT","OJU","OJV","OJW","OJX","OJY","OJZ","OKA","OKB","OKC","OKD", +"OKE","OKF","OKG","OKH","OKI","OKJ","OKK","OKL","OKM","OKN","OKO","OKP","OKQ","OKR","OKS","OKT", +"OKU","OKV","OKW","OKX","OKY","OKZ","OLA","OLB","OLC","OLD","OLE","OLF","OLG","OLH","OLI","OLJ", +"OLK","OLL","OLM","OLN","OLO","OLP","OLQ","OLR","OLS","OLT","OLU","OLV","OLW","OLX","OLY","OLZ", +"OMA","OMB","OMC","OMD","OME","OMF","OMG","OMH","OMI","OMJ","OMK","OML","OMM","OMN","OMO","OMP", +"OMQ","OMR","OMS","OMT","OMU","OMV","OMW","OMX","OMY","OMZ","ONA","ONB","ONC","OND","ONE","ONF", +"ONG","ONH","ONI","ONJ","ONK","ONL","ONM","ONN","ONO","ONP","ONQ","ONR","ONS","ONT","ONU","ONV", +"ONW","ONX","ONY","ONZ","OOA","OOB","OOC","OOD","OOE","OOF","OOG","OOH","OOI","OOJ","OOK","OOL", +"OOM","OON","OOO","OOP","OOQ","OOR","OOS","OOT","OOU","OOV","OOW","OOX","OOY","OOZ","OPA","OPB", +"OPC","OPD","OPE","OPF","OPG","OPH","OPI","OPJ","OPK","OPL","OPM","OPN","OPO","OPP","OPQ","OPR", +"OPS","OPT","OPU","OPV","OPW","OPX","OPY","OPZ","OQA","OQB","OQC","OQD","OQE","OQF","OQG","OQH", +"OQI","OQJ","OQK","OQL","OQM","OQN","OQO","OQP","OQQ","OQR","OQS","OQT","OQU","OQV","OQW","OQX", +"OQY","OQZ","ORA","ORB","ORC","ORD","ORE","ORF","ORG","ORH","ORI","ORJ","ORK","ORL","ORM","ORN", +"ORO","ORP","ORQ","ORR","ORS","ORT","ORU","ORV","ORW","ORX","ORY","ORZ","OSA","OSB","OSC","OSD", +"OSE","OSF","OSG","OSH","OSI","OSJ","OSK","OSL","OSM","OSN","OSO","OSP","OSQ","OSR","OSS","OST", +"OSU","OSV","OSW","OSX","OSY","OSZ","OTA","OTB","OTC","OTD","OTE","OTF","OTG","OTH","OTI","OTJ", +"OTK","OTL","OTM","OTN","OTO","OTP","OTQ","OTR","OTS","OTT","OTU","OTV","OTW","OTX","OTY","OTZ", +"OUA","OUB","OUC","OUD","OUE","OUF","OUG","OUH","OUI","OUJ","OUK","OUL","OUM","OUN","OUO","OUP", +"OUQ","OUR","OUS","OUT","OUU","OUV","OUW","OUX","OUY","OUZ","OVA","OVB","OVC","OVD","OVE","OVF", +"OVG","OVH","OVI","OVJ","OVK","OVL","OVM","OVN","OVO","OVP","OVQ","OVR","OVS","OVT","OVU","OVV", +"OVW","OVX","OVY","OVZ","OWA","OWB","OWC","OWD","OWE","OWF","OWG","OWH","OWI","OWJ","OWK","OWL", +"OWM","OWN","OWO","OWP","OWQ","OWR","OWS","OWT","OWU","OWV","OWW","OWX","OWY","OWZ","OXA","OXB", +"OXC","OXD","OXE","OXF","OXG","OXH","OXI","OXJ","OXK","OXL","OXM","OXN","OXO","OXP","OXQ","OXR", +"OXS","OXT","OXU","OXV","OXW","OXX","OXY","OXZ","OYA","OYB","OYC","OYD","OYE","OYF","OYG","OYH", +"OYI","OYJ","OYK","OYL","OYM","OYN","OYO","OYP","OYQ","OYR","OYS","OYT","OYU","OYV","OYW","OYX", +"OYY","OYZ","OZA","OZB","OZC","OZD","OZE","OZF","OZG","OZH","OZI","OZJ","OZK","OZL","OZM","OZN", +"OZO","OZP","OZQ","OZR","OZS","OZT","OZU","OZV","OZW","OZX","OZY","OZZ","PAA","PAB","PAC","PAD", +"PAE","PAF","PAG","PAH","PAI","PAJ","PAK","PAL","PAM","PAN","PAO","PAP","PAQ","PAR","PAS","PAT", +"PAU","PAV","PAW","PAX","PAY","PAZ","PBA","PBB","PBC","PBD","PBE","PBF","PBG","PBH","PBI","PBJ", +"PBK","PBL","PBM","PBN","PBO","PBP","PBQ","PBR","PBS","PBT","PBU","PBV","PBW","PBX","PBY","PBZ", +"PCA","PCB","PCC","PCD","PCE","PCF","PCG","PCH","PCI","PCJ","PCK","PCL","PCM","PCN","PCO","PCP", +"PCQ","PCR","PCS","PCT","PCU","PCV","PCW","PCX","PCY","PCZ","PDA","PDB","PDC","PDD","PDE","PDF", +"PDG","PDH","PDI","PDJ","PDK","PDL","PDM","PDN","PDO","PDP","PDQ","PDR","PDS","PDT","PDU","PDV", +"PDW","PDX","PDY","PDZ","PEA","PEB","PEC","PED","PEE","PEF","PEG","PEH","PEI","PEJ","PEK","PEL", +"PEM","PEN","PEO","PEP","PEQ","PER","PES","PET","PEU","PEV","PEW","PEX","PEY","PEZ","PFA","PFB", +"PFC","PFD","PFE","PFF","PFG","PFH","PFI","PFJ","PFK","PFL","PFM","PFN","PFO","PFP","PFQ","PFR", +"PFS","PFT","PFU","PFV","PFW","PFX","PFY","PFZ","PGA","PGB","PGC","PGD","PGE","PGF","PGG","PGH", +"PGI","PGJ","PGK","PGL","PGM","PGN","PGO","PGP","PGQ","PGR","PGS","PGT","PGU","PGV","PGW","PGX", +"PGY","PGZ","PHA","PHB","PHC","PHD","PHE","PHF","PHG","PHH","PHI","PHJ","PHK","PHL","PHM","PHN", +"PHO","PHP","PHQ","PHR","PHS","PHT","PHU","PHV","PHW","PHX","PHY","PHZ","PIA","PIB","PIC","PID", +"PIE","PIF","PIG","PIH","PII","PIJ","PIK","PIL","PIM","PIN","PIO","PIP","PIQ","PIR","PIS","PIT", +"PIU","PIV","PIW","PIX","PIY","PIZ","PJA","PJB","PJC","PJD","PJE","PJF","PJG","PJH","PJI","PJJ", +"PJK","PJL","PJM","PJN","PJO","PJP","PJQ","PJR","PJS","PJT","PJU","PJV","PJW","PJX","PJY","PJZ", +"PKA","PKB","PKC","PKD","PKE","PKF","PKG","PKH","PKI","PKJ","PKK","PKL","PKM","PKN","PKO","PKP", +"PKQ","PKR","PKS","PKT","PKU","PKV","PKW","PKX","PKY","PKZ","PLA","PLB","PLC","PLD","PLE","PLF", +"PLG","PLH","PLI","PLJ","PLK","PLL","PLM","PLN","PLO","PLP","PLQ","PLR","PLS","PLT","PLU","PLV", +"PLW","PLX","PLY","PLZ","PMA","PMB","PMC","PMD","PME","PMF","PMG","PMH","PMI","PMJ","PMK","PML", +"PMM","PMN","PMO","PMP","PMQ","PMR","PMS","PMT","PMU","PMV","PMW","PMX","PMY","PMZ","PNA","PNB", +"PNC","PND","PNE","PNF","PNG","PNH","PNI","PNJ","PNK","PNL","PNM","PNN","PNO","PNP","PNQ","PNR", +"PNS","PNT","PNU","PNV","PNW","PNX","PNY","PNZ","POA","POB","POC","POD","POE","POF","POG","POH", +"POI","POJ","POK","POL","POM","PON","POO","POP","POQ","POR","POS","POT","POU","POV","POW","POX", +"POY","POZ","PPA","PPB","PPC","PPD","PPE","PPF","PPG","PPH","PPI","PPJ","PPK","PPL","PPM","PPN", +"PPO","PPP","PPQ","PPR","PPS","PPT","PPU","PPV","PPW","PPX","PPY","PPZ","PQA","PQB","PQC","PQD", +"PQE","PQF","PQG","PQH","PQI","PQJ","PQK","PQL","PQM","PQN","PQO","PQP","PQQ","PQR","PQS","PQT", +"PQU","PQV","PQW","PQX","PQY","PQZ","PRA","PRB","PRC","PRD","PRE","PRF","PRG","PRH","PRI","PRJ", +"PRK","PRL","PRM","PRN","PRO","PRP","PRQ","PRR","PRS","PRT","PRU","PRV","PRW","PRX","PRY","PRZ", +"PSA","PSB","PSC","PSD","PSE","PSF","PSG","PSH","PSI","PSJ","PSK","PSL","PSM","PSN","PSO","PSP", +"PSQ","PSR","PSS","PST","PSU","PSV","PSW","PSX","PSY","PSZ","PTA","PTB","PTC","PTD","PTE","PTF", +"PTG","PTH","PTI","PTJ","PTK","PTL","PTM","PTN","PTO","PTP","PTQ","PTR","PTS","PTT","PTU","PTV", +"PTW","PTX","PTY","PTZ","PUA","PUB","PUC","PUD","PUE","PUF","PUG","PUH","PUI","PUJ","PUK","PUL", +"PUM","PUN","PUO","PUP","PUQ","PUR","PUS","PUT","PUU","PUV","PUW","PUX","PUY","PUZ","PVA","PVB", +"PVC","PVD","PVE","PVF","PVG","PVH","PVI","PVJ","PVK","PVL","PVM","PVN","PVO","PVP","PVQ","PVR", +"PVS","PVT","PVU","PVV","PVW","PVX","PVY","PVZ","PWA","PWB","PWC","PWD","PWE","PWF","PWG","PWH", +"PWI","PWJ","PWK","PWL","PWM","PWN","PWO","PWP","PWQ","PWR","PWS","PWT","PWU","PWV","PWW","PWX", +"PWY","PWZ","PXA","PXB","PXC","PXD","PXE","PXF","PXG","PXH","PXI","PXJ","PXK","PXL","PXM","PXN", +"PXO","PXP","PXQ","PXR","PXS","PXT","PXU","PXV","PXW","PXX","PXY","PXZ","PYA","PYB","PYC","PYD", +"PYE","PYF","PYG","PYH","PYI","PYJ","PYK","PYL","PYM","PYN","PYO","PYP","PYQ","PYR","PYS","PYT", +"PYU","PYV","PYW","PYX","PYY","PYZ","PZA","PZB","PZC","PZD","PZE","PZF","PZG","PZH","PZI","PZJ", +"PZK","PZL","PZM","PZN","PZO","PZP","PZQ","PZR","PZS","PZT","PZU","PZV","PZW","PZX","PZY","PZZ", +"QAA","QAB","QAC","QAD","QAE","QAF","QAG","QAH","QAI","QAJ","QAK","QAL","QAM","QAN","QAO","QAP", +"QAQ","QAR","QAS","QAT","QAU","QAV","QAW","QAX","QAY","QAZ","QBA","QBB","QBC","QBD","QBE","QBF", +"QBG","QBH","QBI","QBJ","QBK","QBL","QBM","QBN","QBO","QBP","QBQ","QBR","QBS","QBT","QBU","QBV", +"QBW","QBX","QBY","QBZ","QCA","QCB","QCC","QCD","QCE","QCF","QCG","QCH","QCI","QCJ","QCK","QCL", +"QCM","QCN","QCO","QCP","QCQ","QCR","QCS","QCT","QCU","QCV","QCW","QCX","QCY","QCZ","QDA","QDB", +"QDC","QDD","QDE","QDF","QDG","QDH","QDI","QDJ","QDK","QDL","QDM","QDN","QDO","QDP","QDQ","QDR", +"QDS","QDT","QDU","QDV","QDW","QDX","QDY","QDZ","QEA","QEB","QEC","QED","QEE","QEF","QEG","QEH", +"QEI","QEJ","QEK","QEL","QEM","QEN","QEO","QEP","QEQ","QER","QES","QET","QEU","QEV","QEW","QEX", +"QEY","QEZ","QFA","QFB","QFC","QFD","QFE","QFF","QFG","QFH","QFI","QFJ","QFK","QFL","QFM","QFN", +"QFO","QFP","QFQ","QFR","QFS","QFT","QFU","QFV","QFW","QFX","QFY","QFZ","QGA","QGB","QGC","QGD", +"QGE","QGF","QGG","QGH","QGI","QGJ","QGK","QGL","QGM","QGN","QGO","QGP","QGQ","QGR","QGS","QGT", +"QGU","QGV","QGW","QGX","QGY","QGZ","QHA","QHB","QHC","QHD","QHE","QHF","QHG","QHH","QHI","QHJ", +"QHK","QHL","QHM","QHN","QHO","QHP","QHQ","QHR","QHS","QHT","QHU","QHV","QHW","QHX","QHY","QHZ", +"QIA","QIB","QIC","QID","QIE","QIF","QIG","QIH","QII","QIJ","QIK","QIL","QIM","QIN","QIO","QIP", +"QIQ","QIR","QIS","QIT","QIU","QIV","QIW","QIX","QIY","QIZ","QJA","QJB","QJC","QJD","QJE","QJF", +"QJG","QJH","QJI","QJJ","QJK","QJL","QJM","QJN","QJO","QJP","QJQ","QJR","QJS","QJT","QJU","QJV", +"QJW","QJX","QJY","QJZ","QKA","QKB","QKC","QKD","QKE","QKF","QKG","QKH","QKI","QKJ","QKK","QKL", +"QKM","QKN","QKO","QKP","QKQ","QKR","QKS","QKT","QKU","QKV","QKW","QKX","QKY","QKZ","QLA","QLB", +"QLC","QLD","QLE","QLF","QLG","QLH","QLI","QLJ","QLK","QLL","QLM","QLN","QLO","QLP","QLQ","QLR", +"QLS","QLT","QLU","QLV","QLW","QLX","QLY","QLZ","QMA","QMB","QMC","QMD","QME","QMF","QMG","QMH", +"QMI","QMJ","QMK","QML","QMM","QMN","QMO","QMP","QMQ","QMR","QMS","QMT","QMU","QMV","QMW","QMX", +"QMY","QMZ","QNA","QNB","QNC","QND","QNE","QNF","QNG","QNH","QNI","QNJ","QNK","QNL","QNM","QNN", +"QNO","QNP","QNQ","QNR","QNS","QNT","QNU","QNV","QNW","QNX","QNY","QNZ","QOA","QOB","QOC","QOD", +"QOE","QOF","QOG","QOH","QOI","QOJ","QOK","QOL","QOM","QON","QOO","QOP","QOQ","QOR","QOS","QOT", +"QOU","QOV","QOW","QOX","QOY","QOZ","QPA","QPB","QPC","QPD","QPE","QPF","QPG","QPH","QPI","QPJ", +"QPK","QPL","QPM","QPN","QPO","QPP","QPQ","QPR","QPS","QPT","QPU","QPV","QPW","QPX","QPY","QPZ", +"QQA","QQB","QQC","QQD","QQE","QQF","QQG","QQH","QQI","QQJ","QQK","QQL","QQM","QQN","QQO","QQP", +"QQQ","QQR","QQS","QQT","QQU","QQV","QQW","QQX","QQY","QQZ","QRA","QRB","QRC","QRD","QRE","QRF", +"QRG","QRH","QRI","QRJ","QRK","QRL","QRM","QRN","QRO","QRP","QRQ","QRR","QRS","QRT","QRU","QRV", +"QRW","QRX","QRY","QRZ","QSA","QSB","QSC","QSD","QSE","QSF","QSG","QSH","QSI","QSJ","QSK","QSL", +"QSM","QSN","QSO","QSP","QSQ","QSR","QSS","QST","QSU","QSV","QSW","QSX","QSY","QSZ","QTA","QTB", +"QTC","QTD","QTE","QTF","QTG","QTH","QTI","QTJ","QTK","QTL","QTM","QTN","QTO","QTP","QTQ","QTR", +"QTS","QTT","QTU","QTV","QTW","QTX","QTY","QTZ","QUA","QUB","QUC","QUD","QUE","QUF","QUG","QUH", +"QUI","QUJ","QUK","QUL","QUM","QUN","QUO","QUP","QUQ","QUR","QUS","QUT","QUU","QUV","QUW","QUX", +"QUY","QUZ","QVA","QVB","QVC","QVD","QVE","QVF","QVG","QVH","QVI","QVJ","QVK","QVL","QVM","QVN", +"QVO","QVP","QVQ","QVR","QVS","QVT","QVU","QVV","QVW","QVX","QVY","QVZ","QWA","QWB","QWC","QWD", +"QWE","QWF","QWG","QWH","QWI","QWJ","QWK","QWL","QWM","QWN","QWO","QWP","QWQ","QWR","QWS","QWT", +"QWU","QWV","QWW","QWX","QWY","QWZ","QXA","QXB","QXC","QXD","QXE","QXF","QXG","QXH","QXI","QXJ", +"QXK","QXL","QXM","QXN","QXO","QXP","QXQ","QXR","QXS","QXT","QXU","QXV","QXW","QXX","QXY","QXZ", +"QYA","QYB","QYC","QYD","QYE","QYF","QYG","QYH","QYI","QYJ","QYK","QYL","QYM","QYN","QYO","QYP", +"QYQ","QYR","QYS","QYT","QYU","QYV","QYW","QYX","QYY","QYZ","QZA","QZB","QZC","QZD","QZE","QZF", +"QZG","QZH","QZI","QZJ","QZK","QZL","QZM","QZN","QZO","QZP","QZQ","QZR","QZS","QZT","QZU","QZV", +"QZW","QZX","QZY","QZZ","RAA","RAB","RAC","RAD","RAE","RAF","RAG","RAH","RAI","RAJ","RAK","RAL", +"RAM","RAN","RAO","RAP","RAQ","RAR","RAS","RAT","RAU","RAV","RAW","RAX","RAY","RAZ","RBA","RBB", +"RBC","RBD","RBE","RBF","RBG","RBH","RBI","RBJ","RBK","RBL","RBM","RBN","RBO","RBP","RBQ","RBR", +"RBS","RBT","RBU","RBV","RBW","RBX","RBY","RBZ","RCA","RCB","RCC","RCD","RCE","RCF","RCG","RCH", +"RCI","RCJ","RCK","RCL","RCM","RCN","RCO","RCP","RCQ","RCR","RCS","RCT","RCU","RCV","RCW","RCX", +"RCY","RCZ","RDA","RDB","RDC","RDD","RDE","RDF","RDG","RDH","RDI","RDJ","RDK","RDL","RDM","RDN", +"RDO","RDP","RDQ","RDR","RDS","RDT","RDU","RDV","RDW","RDX","RDY","RDZ","REA","REB","REC","RED", +"REE","REF","REG","REH","REI","REJ","REK","REL","REM","REN","REO","REP","REQ","RER","RES","RET", +"REU","REV","REW","REX","REY","REZ","RFA","RFB","RFC","RFD","RFE","RFF","RFG","RFH","RFI","RFJ", +"RFK","RFL","RFM","RFN","RFO","RFP","RFQ","RFR","RFS","RFT","RFU","RFV","RFW","RFX","RFY","RFZ", +"RGA","RGB","RGC","RGD","RGE","RGF","RGG","RGH","RGI","RGJ","RGK","RGL","RGM","RGN","RGO","RGP", +"RGQ","RGR","RGS","RGT","RGU","RGV","RGW","RGX","RGY","RGZ","RHA","RHB","RHC","RHD","RHE","RHF", +"RHG","RHH","RHI","RHJ","RHK","RHL","RHM","RHN","RHO","RHP","RHQ","RHR","RHS","RHT","RHU","RHV", +"RHW","RHX","RHY","RHZ","RIA","RIB","RIC","RID","RIE","RIF","RIG","RIH","RII","RIJ","RIK","RIL", +"RIM","RIN","RIO","RIP","RIQ","RIR","RIS","RIT","RIU","RIV","RIW","RIX","RIY","RIZ","RJA","RJB", +"RJC","RJD","RJE","RJF","RJG","RJH","RJI","RJJ","RJK","RJL","RJM","RJN","RJO","RJP","RJQ","RJR", +"RJS","RJT","RJU","RJV","RJW","RJX","RJY","RJZ","RKA","RKB","RKC","RKD","RKE","RKF","RKG","RKH", +"RKI","RKJ","RKK","RKL","RKM","RKN","RKO","RKP","RKQ","RKR","RKS","RKT","RKU","RKV","RKW","RKX", +"RKY","RKZ","RLA","RLB","RLC","RLD","RLE","RLF","RLG","RLH","RLI","RLJ","RLK","RLL","RLM","RLN", +"RLO","RLP","RLQ","RLR","RLS","RLT","RLU","RLV","RLW","RLX","RLY","RLZ","RMA","RMB","RMC","RMD", +"RME","RMF","RMG","RMH","RMI","RMJ","RMK","RML","RMM","RMN","RMO","RMP","RMQ","RMR","RMS","RMT", +"RMU","RMV","RMW","RMX","RMY","RMZ","RNA","RNB","RNC","RND","RNE","RNF","RNG","RNH","RNI","RNJ", +"RNK","RNL","RNM","RNN","RNO","RNP","RNQ","RNR","RNS","RNT","RNU","RNV","RNW","RNX","RNY","RNZ", +"ROA","ROB","ROC","ROD","ROE","ROF","ROG","ROH","ROI","ROJ","ROK","ROL","ROM","RON","ROO","ROP", +"ROQ","ROR","ROS","ROT","ROU","ROV","ROW","ROX","ROY","ROZ","RPA","RPB","RPC","RPD","RPE","RPF", +"RPG","RPH","RPI","RPJ","RPK","RPL","RPM","RPN","RPO","RPP","RPQ","RPR","RPS","RPT","RPU","RPV", +"RPW","RPX","RPY","RPZ","RQA","RQB","RQC","RQD","RQE","RQF","RQG","RQH","RQI","RQJ","RQK","RQL", +"RQM","RQN","RQO","RQP","RQQ","RQR","RQS","RQT","RQU","RQV","RQW","RQX","RQY","RQZ","RRA","RRB", +"RRC","RRD","RRE","RRF","RRG","RRH","RRI","RRJ","RRK","RRL","RRM","RRN","RRO","RRP","RRQ","RRR", +"RRS","RRT","RRU","RRV","RRW","RRX","RRY","RRZ","RSA","RSB","RSC","RSD","RSE","RSF","RSG","RSH", +"RSI","RSJ","RSK","RSL","RSM","RSN","RSO","RSP","RSQ","RSR","RSS","RST","RSU","RSV","RSW","RSX", +"RSY","RSZ","RTA","RTB","RTC","RTD","RTE","RTF","RTG","RTH","RTI","RTJ","RTK","RTL","RTM","RTN", +"RTO","RTP","RTQ","RTR","RTS","RTT","RTU","RTV","RTW","RTX","RTY","RTZ","RUA","RUB","RUC","RUD", +"RUE","RUF","RUG","RUH","RUI","RUJ","RUK","RUL","RUM","RUN","RUO","RUP","RUQ","RUR","RUS","RUT", +"RUU","RUV","RUW","RUX","RUY","RUZ","RVA","RVB","RVC","RVD","RVE","RVF","RVG","RVH","RVI","RVJ", +"RVK","RVL","RVM","RVN","RVO","RVP","RVQ","RVR","RVS","RVT","RVU","RVV","RVW","RVX","RVY","RVZ", +"RWA","RWB","RWC","RWD","RWE","RWF","RWG","RWH","RWI","RWJ","RWK","RWL","RWM","RWN","RWO","RWP", +"RWQ","RWR","RWS","RWT","RWU","RWV","RWW","RWX","RWY","RWZ","RXA","RXB","RXC","RXD","RXE","RXF", +"RXG","RXH","RXI","RXJ","RXK","RXL","RXM","RXN","RXO","RXP","RXQ","RXR","RXS","RXT","RXU","RXV", +"RXW","RXX","RXY","RXZ","RYA","RYB","RYC","RYD","RYE","RYF","RYG","RYH","RYI","RYJ","RYK","RYL", +"RYM","RYN","RYO","RYP","RYQ","RYR","RYS","RYT","RYU","RYV","RYW","RYX","RYY","RYZ","RZA","RZB", +"RZC","RZD","RZE","RZF","RZG","RZH","RZI","RZJ","RZK","RZL","RZM","RZN","RZO","RZP","RZQ","RZR", +"RZS","RZT","RZU","RZV","RZW","RZX","RZY","RZZ","SAA","SAB","SAC","SAD","SAE","SAF","SAG","SAH", +"SAI","SAJ","SAK","SAL","SAM","SAN","SAO","SAP","SAQ","SAR","SAS","SAT","SAU","SAV","SAW","SAX", +"SAY","SAZ","SBA","SBB","SBC","SBD","SBE","SBF","SBG","SBH","SBI","SBJ","SBK","SBL","SBM","SBN", +"SBO","SBP","SBQ","SBR","SBS","SBT","SBU","SBV","SBW","SBX","SBY","SBZ","SCA","SCB","SCC","SCD", +"SCE","SCF","SCG","SCH","SCI","SCJ","SCK","SCL","SCM","SCN","SCO","SCP","SCQ","SCR","SCS","SCT", +"SCU","SCV","SCW","SCX","SCY","SCZ","SDA","SDB","SDC","SDD","SDE","SDF","SDG","SDH","SDI","SDJ", +"SDK","SDL","SDM","SDN","SDO","SDP","SDQ","SDR","SDS","SDT","SDU","SDV","SDW","SDX","SDY","SDZ", +"SEA","SEB","SEC","SED","SEE","SEF","SEG","SEH","SEI","SEJ","SEK","SEL","SEM","SEN","SEO","SEP", +"SEQ","SER","SES","SET","SEU","SEV","SEW","SEX","SEY","SEZ","SFA","SFB","SFC","SFD","SFE","SFF", +"SFG","SFH","SFI","SFJ","SFK","SFL","SFM","SFN","SFO","SFP","SFQ","SFR","SFS","SFT","SFU","SFV", +"SFW","SFX","SFY","SFZ","SGA","SGB","SGC","SGD","SGE","SGF","SGG","SGH","SGI","SGJ","SGK","SGL", +"SGM","SGN","SGO","SGP","SGQ","SGR","SGS","SGT","SGU","SGV","SGW","SGX","SGY","SGZ","SHA","SHB", +"SHC","SHD","SHE","SHF","SHG","SHH","SHI","SHJ","SHK","SHL","SHM","SHN","SHO","SHP","SHQ","SHR", +"SHS","SHT","SHU","SHV","SHW","SHX","SHY","SHZ","SIA","SIB","SIC","SID","SIE","SIF","SIG","SIH", +"SII","SIJ","SIK","SIL","SIM","SIN","SIO","SIP","SIQ","SIR","SIS","SIT","SIU","SIV","SIW","SIX", +"SIY","SIZ","SJA","SJB","SJC","SJD","SJE","SJF","SJG","SJH","SJI","SJJ","SJK","SJL","SJM","SJN", +"SJO","SJP","SJQ","SJR","SJS","SJT","SJU","SJV","SJW","SJX","SJY","SJZ","SKA","SKB","SKC","SKD", +"SKE","SKF","SKG","SKH","SKI","SKJ","SKK","SKL","SKM","SKN","SKO","SKP","SKQ","SKR","SKS","SKT", +"SKU","SKV","SKW","SKX","SKY","SKZ","SLA","SLB","SLC","SLD","SLE","SLF","SLG","SLH","SLI","SLJ", +"SLK","SLL","SLM","SLN","SLO","SLP","SLQ","SLR","SLS","SLT","SLU","SLV","SLW","SLX","SLY","SLZ", +"SMA","SMB","SMC","SMD","SME","SMF","SMG","SMH","SMI","SMJ","SMK","SML","SMM","SMN","SMO","SMP", +"SMQ","SMR","SMS","SMT","SMU","SMV","SMW","SMX","SMY","SMZ","SNA","SNB","SNC","SND","SNE","SNF", +"SNG","SNH","SNI","SNJ","SNK","SNL","SNM","SNN","SNO","SNP","SNQ","SNR","SNS","SNT","SNU","SNV", +"SNW","SNX","SNY","SNZ","SOA","SOB","SOC","SOD","SOE","SOF","SOG","SOH","SOI","SOJ","SOK","SOL", +"SOM","SON","SOO","SOP","SOQ","SOR","SOS","SOT","SOU","SOV","SOW","SOX","SOY","SOZ","SPA","SPB", +"SPC","SPD","SPE","SPF","SPG","SPH","SPI","SPJ","SPK","SPL","SPM","SPN","SPO","SPP","SPQ","SPR", +"SPS","SPT","SPU","SPV","SPW","SPX","SPY","SPZ","SQA","SQB","SQC","SQD","SQE","SQF","SQG","SQH", +"SQI","SQJ","SQK","SQL","SQM","SQN","SQO","SQP","SQQ","SQR","SQS","SQT","SQU","SQV","SQW","SQX", +"SQY","SQZ","SRA","SRB","SRC","SRD","SRE","SRF","SRG","SRH","SRI","SRJ","SRK","SRL","SRM","SRN", +"SRO","SRP","SRQ","SRR","SRS","SRT","SRU","SRV","SRW","SRX","SRY","SRZ","SSA","SSB","SSC","SSD", +"SSE","SSF","SSG","SSH","SSI","SSJ","SSK","SSL","SSM","SSN","SSO","SSP","SSQ","SSR","SSS","SST", +"SSU","SSV","SSW","SSX","SSY","SSZ","STA","STB","STC","STD","STE","STF","STG","STH","STI","STJ", +"STK","STL","STM","STN","STO","STP","STQ","STR","STS","STT","STU","STV","STW","STX","STY","STZ", +"SUA","SUB","SUC","SUD","SUE","SUF","SUG","SUH","SUI","SUJ","SUK","SUL","SUM","SUN","SUO","SUP", +"SUQ","SUR","SUS","SUT","SUU","SUV","SUW","SUX","SUY","SUZ","SVA","SVB","SVC","SVD","SVE","SVF", +"SVG","SVH","SVI","SVJ","SVK","SVL","SVM","SVN","SVO","SVP","SVQ","SVR","SVS","SVT","SVU","SVV", +"SVW","SVX","SVY","SVZ","SWA","SWB","SWC","SWD","SWE","SWF","SWG","SWH","SWI","SWJ","SWK","SWL", +"SWM","SWN","SWO","SWP","SWQ","SWR","SWS","SWT","SWU","SWV","SWW","SWX","SWY","SWZ","SXA","SXB", +"SXC","SXD","SXE","SXF","SXG","SXH","SXI","SXJ","SXK","SXL","SXM","SXN","SXO","SXP","SXQ","SXR", +"SXS","SXT","SXU","SXV","SXW","SXX","SXY","SXZ","SYA","SYB","SYC","SYD","SYE","SYF","SYG","SYH", +"SYI","SYJ","SYK","SYL","SYM","SYN","SYO","SYP","SYQ","SYR","SYS","SYT","SYU","SYV","SYW","SYX", +"SYY","SYZ","SZA","SZB","SZC","SZD","SZE","SZF","SZG","SZH","SZI","SZJ","SZK","SZL","SZM","SZN", +"SZO","SZP","SZQ","SZR","SZS","SZT","SZU","SZV","SZW","SZX","SZY","SZZ", +/* + TAA to TTV - 516 triplets - intentionally omitted +*/ +"TTW","TTX","TTY","TTZ","TUA","TUB","TUC","TUD","TUE","TUF","TUG","TUH","TUI","TUJ","TUK","TUL", +"TUM","TUN","TUO","TUP","TUQ","TUR","TUS","TUT","TUU","TUV","TUW","TUX","TUY","TUZ","TVA","TVB", +"TVC","TVD","TVE","TVF","TVG","TVH","TVI","TVJ","TVK","TVL","TVM","TVN","TVO","TVP","TVQ","TVR", +"TVS","TVT","TVU","TVV","TVW","TVX","TVY","TVZ","TWA","TWB","TWC","TWD","TWE","TWF","TWG","TWH", +"TWI","TWJ","TWK","TWL","TWM","TWN","TWO","TWP","TWQ","TWR","TWS","TWT","TWU","TWV","TWW","TWX", +"TWY","TWZ","TXA","TXB","TXC","TXD","TXE","TXF","TXG","TXH","TXI","TXJ","TXK","TXL","TXM","TXN", +"TXO","TXP","TXQ","TXR","TXS","TXT","TXU","TXV","TXW","TXX","TXY","TXZ","TYA","TYB","TYC","TYD", +"TYE","TYF","TYG","TYH","TYI","TYJ","TYK","TYL","TYM","TYN","TYO","TYP","TYQ","TYR","TYS","TYT", +"TYU","TYV","TYW","TYX","TYY","TYZ","TZA","TZB","TZC","TZD","TZE","TZF","TZG","TZH","TZI","TZJ", +"TZK","TZL","TZM","TZN","TZO","TZP","TZQ","TZR","TZS","TZT","TZU","TZV","TZW","TZX","TZY","TZZ", +"UAA","UAB","UAC","UAD","UAE","UAF","UAG","UAH","UAI","UAJ","UAK","UAL","UAM","UAN","UAO","UAP", +"UAQ","UAR","UAS","UAT","UAU","UAV","UAW","UAX","UAY","UAZ","UBA","UBB","UBC","UBD","UBE","UBF", +"UBG","UBH","UBI","UBJ","UBK","UBL","UBM","UBN","UBO","UBP","UBQ","UBR","UBS","UBT","UBU","UBV", +"UBW","UBX","UBY","UBZ","UCA","UCB","UCC","UCD","UCE","UCF","UCG","UCH","UCI","UCJ","UCK","UCL", +"UCM","UCN","UCO","UCP","UCQ","UCR","UCS","UCT","UCU","UCV","UCW","UCX","UCY","UCZ","UDA","UDB", +"UDC","UDD","UDE","UDF","UDG","UDH","UDI","UDJ","UDK","UDL","UDM","UDN","UDO","UDP","UDQ","UDR", +"UDS","UDT","UDU","UDV","UDW","UDX","UDY","UDZ","UEA","UEB","UEC","UED","UEE","UEF","UEG","UEH", +"UEI","UEJ","UEK","UEL","UEM","UEN","UEO","UEP","UEQ","UER","UES","UET","UEU","UEV","UEW","UEX", +"UEY","UEZ","UFA","UFB","UFC","UFD","UFE","UFF","UFG","UFH","UFI","UFJ","UFK","UFL","UFM","UFN", +"UFO","UFP","UFQ","UFR","UFS","UFT","UFU","UFV","UFW","UFX","UFY","UFZ","UGA","UGB","UGC","UGD", +"UGE","UGF","UGG","UGH","UGI","UGJ","UGK","UGL","UGM","UGN","UGO","UGP","UGQ","UGR","UGS","UGT", +"UGU","UGV","UGW","UGX","UGY","UGZ","UHA","UHB","UHC","UHD","UHE","UHF","UHG","UHH","UHI","UHJ", +"UHK","UHL","UHM","UHN","UHO","UHP","UHQ","UHR","UHS","UHT","UHU","UHV","UHW","UHX","UHY","UHZ", +"UIA","UIB","UIC","UID","UIE","UIF","UIG","UIH","UII","UIJ","UIK","UIL","UIM","UIN","UIO","UIP", +"UIQ","UIR","UIS","UIT","UIU","UIV","UIW","UIX","UIY","UIZ","UJA","UJB","UJC","UJD","UJE","UJF", +"UJG","UJH","UJI","UJJ","UJK","UJL","UJM","UJN","UJO","UJP","UJQ","UJR","UJS","UJT","UJU","UJV", +"UJW","UJX","UJY","UJZ","UKA","UKB","UKC","UKD","UKE","UKF","UKG","UKH","UKI","UKJ","UKK","UKL", +"UKM","UKN","UKO","UKP","UKQ","UKR","UKS","UKT","UKU","UKV","UKW","UKX","UKY","UKZ","ULA","ULB", +"ULC","ULD","ULE","ULF","ULG","ULH","ULI","ULJ","ULK","ULL","ULM","ULN","ULO","ULP","ULQ","ULR", +"ULS","ULT","ULU","ULV","ULW","ULX","ULY","ULZ","UMA","UMB","UMC","UMD","UME","UMF","UMG","UMH", +"UMI","UMJ","UMK","UML","UMM","UMN","UMO","UMP","UMQ","UMR","UMS","UMT","UMU","UMV","UMW","UMX", +"UMY","UMZ","UNA","UNB","UNC","UND","UNE","UNF","UNG","UNH","UNI","UNJ","UNK","UNL","UNM","UNN", +"UNO","UNP","UNQ","UNR","UNS","UNT","UNU","UNV","UNW","UNX","UNY","UNZ","UOA","UOB","UOC","UOD", +"UOE","UOF","UOG","UOH","UOI","UOJ","UOK","UOL","UOM","UON","UOO","UOP","UOQ","UOR","UOS","UOT", +"UOU","UOV","UOW","UOX","UOY","UOZ","UPA","UPB","UPC","UPD","UPE","UPF","UPG","UPH","UPI","UPJ", +"UPK","UPL","UPM","UPN","UPO","UPP","UPQ","UPR","UPS","UPT","UPU","UPV","UPW","UPX","UPY","UPZ", +"UQA","UQB","UQC","UQD","UQE","UQF","UQG","UQH","UQI","UQJ","UQK","UQL","UQM","UQN","UQO","UQP", +"UQQ","UQR","UQS","UQT","UQU","UQV","UQW","UQX","UQY","UQZ","URA","URB","URC","URD","URE","URF", +"URG","URH","URI","URJ","URK","URL","URM","URN","URO","URP","URQ","URR","URS","URT","URU","URV", +"URW","URX","URY","URZ","USA","USB","USC","USD","USE","USF","USG","USH","USI","USJ","USK","USL", +"USM","USN","USO","USP","USQ","USR","USS","UST","USU","USV","USW","USX","USY","USZ","UTA","UTB", +"UTC","UTD","UTE","UTF","UTG","UTH","UTI","UTJ","UTK","UTL","UTM","UTN","UTO","UTP","UTQ","UTR", +"UTS","UTT","UTU","UTV","UTW","UTX","UTY","UTZ","UUA","UUB","UUC","UUD","UUE","UUF","UUG","UUH", +"UUI","UUJ","UUK","UUL","UUM","UUN","UUO","UUP","UUQ","UUR","UUS","UUT","UUU","UUV","UUW","UUX", +"UUY","UUZ","UVA","UVB","UVC","UVD","UVE","UVF","UVG","UVH","UVI","UVJ","UVK","UVL","UVM","UVN", +"UVO","UVP","UVQ","UVR","UVS","UVT","UVU","UVV","UVW","UVX","UVY","UVZ","UWA","UWB","UWC","UWD", +"UWE","UWF","UWG","UWH","UWI","UWJ","UWK","UWL","UWM","UWN","UWO","UWP","UWQ","UWR","UWS","UWT", +"UWU","UWV","UWW","UWX","UWY","UWZ","UXA","UXB","UXC","UXD","UXE","UXF","UXG","UXH","UXI","UXJ", +"UXK","UXL","UXM","UXN","UXO","UXP","UXQ","UXR","UXS","UXT","UXU","UXV","UXW","UXX","UXY","UXZ", +"UYA","UYB","UYC","UYD","UYE","UYF","UYG","UYH","UYI","UYJ","UYK","UYL","UYM","UYN","UYO","UYP", +"UYQ","UYR","UYS","UYT","UYU","UYV","UYW","UYX","UYY","UYZ","UZA","UZB","UZC","UZD","UZE","UZF", +"UZG","UZH","UZI","UZJ","UZK","UZL","UZM","UZN","UZO","UZP","UZQ","UZR","UZS","UZT","UZU","UZV", +"UZW","UZX","UZY","UZZ","VAA","VAB","VAC","VAD","VAE","VAF","VAG","VAH","VAI","VAJ","VAK","VAL", +"VAM","VAN","VAO","VAP","VAQ","VAR","VAS","VAT","VAU","VAV","VAW","VAX","VAY","VAZ","VBA","VBB", +"VBC","VBD","VBE","VBF","VBG","VBH","VBI","VBJ","VBK","VBL","VBM","VBN","VBO","VBP","VBQ","VBR", +"VBS","VBT","VBU","VBV","VBW","VBX","VBY","VBZ","VCA","VCB","VCC","VCD","VCE","VCF","VCG","VCH", +"VCI","VCJ","VCK","VCL","VCM","VCN","VCO","VCP","VCQ","VCR","VCS","VCT","VCU","VCV","VCW","VCX", +"VCY","VCZ","VDA","VDB","VDC","VDD","VDE","VDF","VDG","VDH","VDI","VDJ","VDK","VDL","VDM","VDN", +"VDO","VDP","VDQ","VDR","VDS","VDT","VDU","VDV","VDW","VDX","VDY","VDZ","VEA","VEB","VEC","VED", +"VEE","VEF","VEG","VEH","VEI","VEJ","VEK","VEL","VEM","VEN","VEO","VEP","VEQ","VER","VES","VET", +"VEU","VEV","VEW","VEX","VEY","VEZ","VFA","VFB","VFC","VFD","VFE","VFF","VFG","VFH","VFI","VFJ", +"VFK","VFL","VFM","VFN","VFO","VFP","VFQ","VFR","VFS","VFT","VFU","VFV","VFW","VFX","VFY","VFZ", +"VGA","VGB","VGC","VGD","VGE","VGF","VGG","VGH","VGI","VGJ","VGK","VGL","VGM","VGN","VGO","VGP", +"VGQ","VGR","VGS","VGT","VGU","VGV","VGW","VGX","VGY","VGZ","VHA","VHB","VHC","VHD","VHE","VHF", +"VHG","VHH","VHI","VHJ","VHK","VHL","VHM","VHN","VHO","VHP","VHQ","VHR","VHS","VHT","VHU","VHV", +"VHW","VHX","VHY","VHZ","VIA","VIB","VIC","VID","VIE","VIF","VIG","VIH","VII","VIJ","VIK","VIL", +"VIM","VIN","VIO","VIP","VIQ","VIR","VIS","VIT","VIU","VIV","VIW","VIX","VIY","VIZ","VJA","VJB", +"VJC","VJD","VJE","VJF","VJG","VJH","VJI","VJJ","VJK","VJL","VJM","VJN","VJO","VJP","VJQ","VJR", +"VJS","VJT","VJU","VJV","VJW","VJX","VJY","VJZ","VKA","VKB","VKC","VKD","VKE","VKF","VKG","VKH", +"VKI","VKJ","VKK","VKL","VKM","VKN","VKO","VKP","VKQ","VKR","VKS","VKT","VKU","VKV","VKW","VKX", +"VKY","VKZ","VLA","VLB","VLC","VLD","VLE","VLF","VLG","VLH","VLI","VLJ","VLK","VLL","VLM","VLN", +"VLO","VLP","VLQ","VLR","VLS","VLT","VLU","VLV","VLW","VLX","VLY","VLZ","VMA","VMB","VMC","VMD", +"VME","VMF","VMG","VMH","VMI","VMJ","VMK","VML","VMM","VMN","VMO","VMP","VMQ","VMR","VMS","VMT", +"VMU","VMV","VMW","VMX","VMY","VMZ","VNA","VNB","VNC","VND","VNE","VNF","VNG","VNH","VNI","VNJ", +"VNK","VNL","VNM","VNN","VNO","VNP","VNQ","VNR","VNS","VNT","VNU","VNV","VNW","VNX","VNY","VNZ", +"VOA","VOB","VOC","VOD","VOE","VOF","VOG","VOH","VOI","VOJ","VOK","VOL","VOM","VON","VOO","VOP", +"VOQ","VOR","VOS","VOT","VOU","VOV","VOW","VOX","VOY","VOZ","VPA","VPB","VPC","VPD","VPE","VPF", +"VPG","VPH","VPI","VPJ","VPK","VPL","VPM","VPN","VPO","VPP","VPQ","VPR","VPS","VPT","VPU","VPV", +"VPW","VPX","VPY","VPZ","VQA","VQB","VQC","VQD","VQE","VQF","VQG","VQH","VQI","VQJ","VQK","VQL", +"VQM","VQN","VQO","VQP","VQQ","VQR","VQS","VQT","VQU","VQV","VQW","VQX","VQY","VQZ","VRA","VRB", +"VRC","VRD","VRE","VRF","VRG","VRH","VRI","VRJ","VRK","VRL","VRM","VRN","VRO","VRP","VRQ","VRR", +"VRS","VRT","VRU","VRV","VRW","VRX","VRY","VRZ","VSA","VSB","VSC","VSD","VSE","VSF","VSG","VSH", +"VSI","VSJ","VSK","VSL","VSM","VSN","VSO","VSP","VSQ","VSR","VSS","VST","VSU","VSV","VSW","VSX", +"VSY","VSZ","VTA","VTB","VTC","VTD","VTE","VTF","VTG","VTH","VTI","VTJ","VTK","VTL","VTM","VTN", +"VTO","VTP","VTQ","VTR","VTS","VTT","VTU","VTV","VTW","VTX","VTY","VTZ","VUA","VUB","VUC","VUD", +"VUE","VUF","VUG","VUH","VUI","VUJ","VUK","VUL","VUM","VUN","VUO","VUP","VUQ","VUR","VUS","VUT", +"VUU","VUV","VUW","VUX","VUY","VUZ","VVA","VVB","VVC","VVD","VVE","VVF","VVG","VVH","VVI","VVJ", +"VVK","VVL","VVM","VVN","VVO","VVP","VVQ","VVR","VVS","VVT","VVU","VVV","VVW","VVX","VVY","VVZ", +"VWA","VWB","VWC","VWD","VWE","VWF","VWG","VWH","VWI","VWJ","VWK","VWL","VWM","VWN","VWO","VWP", +"VWQ","VWR","VWS","VWT","VWU","VWV","VWW","VWX","VWY","VWZ","VXA","VXB","VXC","VXD","VXE","VXF", +"VXG","VXH","VXI","VXJ","VXK","VXL","VXM","VXN","VXO","VXP","VXQ","VXR","VXS","VXT","VXU","VXV", +"VXW","VXX","VXY","VXZ","VYA","VYB","VYC","VYD","VYE","VYF","VYG","VYH","VYI","VYJ","VYK","VYL", +"VYM","VYN","VYO","VYP","VYQ","VYR","VYS","VYT","VYU","VYV","VYW","VYX","VYY","VYZ","VZA","VZB", +"VZC","VZD","VZE","VZF","VZG","VZH","VZI","VZJ","VZK","VZL","VZM","VZN","VZO","VZP","VZQ","VZR", +"VZS","VZT","VZU","VZV","VZW","VZX","VZY","VZZ","WAA","WAB","WAC","WAD","WAE","WAF","WAG","WAH", +"WAI","WAJ","WAK","WAL","WAM","WAN","WAO","WAP","WAQ","WAR","WAS","WAT","WAU","WAV","WAW","WAX", +"WAY","WAZ","WBA","WBB","WBC","WBD","WBE","WBF","WBG","WBH","WBI","WBJ","WBK","WBL","WBM","WBN", +"WBO","WBP","WBQ","WBR","WBS","WBT","WBU","WBV","WBW","WBX","WBY","WBZ","WCA","WCB","WCC","WCD", +"WCE","WCF","WCG","WCH","WCI","WCJ","WCK","WCL","WCM","WCN","WCO","WCP","WCQ","WCR","WCS","WCT", +"WCU","WCV","WCW","WCX","WCY","WCZ","WDA","WDB","WDC","WDD","WDE","WDF","WDG","WDH","WDI","WDJ", +"WDK","WDL","WDM","WDN","WDO","WDP","WDQ","WDR","WDS","WDT","WDU","WDV","WDW","WDX","WDY","WDZ", +"WEA","WEB","WEC","WED","WEE","WEF","WEG","WEH","WEI","WEJ","WEK","WEL","WEM","WEN","WEO","WEP", +"WEQ","WER","WES","WET","WEU","WEV","WEW","WEX","WEY","WEZ","WFA","WFB","WFC","WFD","WFE","WFF", +"WFG","WFH","WFI","WFJ","WFK","WFL","WFM","WFN","WFO","WFP","WFQ","WFR","WFS","WFT","WFU","WFV", +"WFW","WFX","WFY","WFZ","WGA","WGB","WGC","WGD","WGE","WGF","WGG","WGH","WGI","WGJ","WGK","WGL", +"WGM","WGN","WGO","WGP","WGQ","WGR","WGS","WGT","WGU","WGV","WGW","WGX","WGY","WGZ","WHA","WHB", +"WHC","WHD","WHE","WHF","WHG","WHH","WHI","WHJ","WHK","WHL","WHM","WHN","WHO","WHP","WHQ","WHR", +"WHS","WHT","WHU","WHV","WHW","WHX","WHY","WHZ","WIA","WIB","WIC","WID","WIE","WIF","WIG","WIH", +"WII","WIJ","WIK","WIL","WIM","WIN","WIO","WIP","WIQ","WIR","WIS","WIT","WIU","WIV","WIW","WIX", +"WIY","WIZ","WJA","WJB","WJC","WJD","WJE","WJF","WJG","WJH","WJI","WJJ","WJK","WJL","WJM","WJN", +"WJO","WJP","WJQ","WJR","WJS","WJT","WJU","WJV","WJW","WJX","WJY","WJZ","WKA","WKB","WKC","WKD", +"WKE","WKF","WKG","WKH","WKI","WKJ","WKK","WKL","WKM","WKN","WKO","WKP","WKQ","WKR","WKS","WKT", +"WKU","WKV","WKW","WKX","WKY","WKZ","WLA","WLB","WLC","WLD","WLE","WLF","WLG","WLH","WLI","WLJ", +"WLK","WLL","WLM","WLN","WLO","WLP","WLQ","WLR","WLS","WLT","WLU","WLV","WLW","WLX","WLY","WLZ", +"WMA","WMB","WMC","WMD","WME","WMF","WMG","WMH","WMI","WMJ","WMK","WML","WMM","WMN","WMO","WMP", +"WMQ","WMR","WMS","WMT","WMU","WMV","WMW","WMX","WMY","WMZ","WNA","WNB","WNC","WND","WNE","WNF", +"WNG","WNH","WNI","WNJ","WNK","WNL","WNM","WNN","WNO","WNP","WNQ","WNR","WNS","WNT","WNU","WNV", +"WNW","WNX","WNY","WNZ","WOA","WOB","WOC","WOD","WOE","WOF","WOG","WOH","WOI","WOJ","WOK","WOL", +"WOM","WON","WOO","WOP","WOQ","WOR","WOS","WOT","WOU","WOV","WOW","WOX","WOY","WOZ","WPA","WPB", +"WPC","WPD","WPE","WPF","WPG","WPH","WPI","WPJ","WPK","WPL","WPM","WPN","WPO","WPP","WPQ","WPR", +"WPS","WPT","WPU","WPV","WPW","WPX","WPY","WPZ","WQA","WQB","WQC","WQD","WQE","WQF","WQG","WQH", +"WQI","WQJ","WQK","WQL","WQM","WQN","WQO","WQP","WQQ","WQR","WQS","WQT","WQU","WQV","WQW","WQX", +"WQY","WQZ","WRA","WRB","WRC","WRD","WRE","WRF","WRG","WRH","WRI","WRJ","WRK","WRL","WRM","WRN", +"WRO","WRP","WRQ","WRR","WRS","WRT","WRU","WRV","WRW","WRX","WRY","WRZ","WSA","WSB","WSC","WSD", +"WSE","WSF","WSG","WSH","WSI","WSJ","WSK","WSL","WSM","WSN","WSO","WSP","WSQ","WSR","WSS","WST", +"WSU","WSV","WSW","WSX","WSY","WSZ","WTA","WTB","WTC","WTD","WTE","WTF","WTG","WTH","WTI","WTJ", +"WTK","WTL","WTM","WTN","WTO","WTP","WTQ","WTR","WTS","WTT","WTU","WTV","WTW","WTX","WTY","WTZ", +"WUA","WUB","WUC","WUD","WUE","WUF","WUG","WUH","WUI","WUJ","WUK","WUL","WUM","WUN","WUO","WUP", +"WUQ","WUR","WUS","WUT","WUU","WUV","WUW","WUX","WUY","WUZ","WVA","WVB","WVC","WVD","WVE","WVF", +"WVG","WVH","WVI","WVJ","WVK","WVL","WVM","WVN","WVO","WVP","WVQ","WVR","WVS","WVT","WVU","WVV", +"WVW","WVX","WVY","WVZ","WWA","WWB","WWC","WWD","WWE","WWF","WWG","WWH","WWI","WWJ","WWK","WWL", +"WWM","WWN","WWO","WWP","WWQ","WWR","WWS","WWT","WWU","WWV","WWW","WWX","WWY","WWZ","WXA","WXB", +"WXC","WXD","WXE","WXF","WXG","WXH","WXI","WXJ","WXK","WXL","WXM","WXN","WXO","WXP","WXQ","WXR", +"WXS","WXT","WXU","WXV","WXW","WXX","WXY","WXZ","WYA","WYB","WYC","WYD","WYE","WYF","WYG","WYH", +"WYI","WYJ","WYK","WYL","WYM","WYN","WYO","WYP","WYQ","WYR","WYS","WYT","WYU","WYV","WYW","WYX", +"WYY","WYZ","WZA","WZB","WZC","WZD","WZE","WZF","WZG","WZH","WZI","WZJ","WZK","WZL","WZM","WZN", +"WZO","WZP","WZQ","WZR","WZS","WZT","WZU","WZV","WZW","WZX","WZY","WZZ","XAA","XAB","XAC","XAD", +"XAE","XAF","XAG","XAH","XAI","XAJ","XAK","XAL","XAM","XAN","XAO","XAP","XAQ","XAR","XAS","XAT", +"XAU","XAV","XAW","XAX","XAY","XAZ","XBA","XBB","XBC","XBD","XBE","XBF","XBG","XBH","XBI","XBJ", +"XBK","XBL","XBM","XBN","XBO","XBP","XBQ","XBR","XBS","XBT","XBU","XBV","XBW","XBX","XBY","XBZ", +"XCA","XCB","XCC","XCD","XCE","XCF","XCG","XCH","XCI","XCJ","XCK","XCL","XCM","XCN","XCO","XCP", +"XCQ","XCR","XCS","XCT","XCU","XCV","XCW","XCX","XCY","XCZ","XDA","XDB","XDC","XDD","XDE","XDF", +"XDG","XDH","XDI","XDJ","XDK","XDL","XDM","XDN","XDO","XDP","XDQ","XDR","XDS","XDT","XDU","XDV", +"XDW","XDX","XDY","XDZ","XEA","XEB","XEC","XED","XEE","XEF","XEG","XEH","XEI","XEJ","XEK","XEL", +"XEM","XEN","XEO","XEP","XEQ","XER","XES","XET","XEU","XEV","XEW","XEX","XEY","XEZ","XFA","XFB", +"XFC","XFD","XFE","XFF","XFG","XFH","XFI","XFJ","XFK","XFL","XFM","XFN","XFO","XFP","XFQ","XFR", +"XFS","XFT","XFU","XFV","XFW","XFX","XFY","XFZ","XGA","XGB","XGC","XGD","XGE","XGF","XGG","XGH", +"XGI","XGJ","XGK","XGL","XGM","XGN","XGO","XGP","XGQ","XGR","XGS","XGT","XGU","XGV","XGW","XGX", +"XGY","XGZ","XHA","XHB","XHC","XHD","XHE","XHF","XHG","XHH","XHI","XHJ","XHK","XHL","XHM","XHN", +"XHO","XHP","XHQ","XHR","XHS","XHT","XHU","XHV","XHW","XHX","XHY","XHZ","XIA","XIB","XIC","XID", +"XIE","XIF","XIG","XIH","XII","XIJ","XIK","XIL","XIM","XIN","XIO","XIP","XIQ","XIR","XIS","XIT", +"XIU","XIV","XIW","XIX","XIY","XIZ","XJA","XJB","XJC","XJD","XJE","XJF","XJG","XJH","XJI","XJJ", +"XJK","XJL","XJM","XJN","XJO","XJP","XJQ","XJR","XJS","XJT","XJU","XJV","XJW","XJX","XJY","XJZ", +"XKA","XKB","XKC","XKD","XKE","XKF","XKG","XKH","XKI","XKJ","XKK","XKL","XKM","XKN","XKO","XKP", +"XKQ","XKR","XKS","XKT","XKU","XKV","XKW","XKX","XKY","XKZ","XLA","XLB","XLC","XLD","XLE","XLF", +"XLG","XLH","XLI","XLJ","XLK","XLL","XLM","XLN","XLO","XLP","XLQ","XLR","XLS","XLT","XLU","XLV", +"XLW","XLX","XLY","XLZ","XMA","XMB","XMC","XMD","XME","XMF","XMG","XMH","XMI","XMJ","XMK","XML", +"XMM","XMN","XMO","XMP","XMQ","XMR","XMS","XMT","XMU","XMV","XMW","XMX","XMY","XMZ","XNA","XNB", +"XNC","XND","XNE","XNF","XNG","XNH","XNI","XNJ","XNK","XNL","XNM","XNN","XNO","XNP","XNQ","XNR", +"XNS","XNT","XNU","XNV","XNW","XNX","XNY","XNZ","XOA","XOB","XOC","XOD","XOE","XOF","XOG","XOH", +"XOI","XOJ","XOK","XOL","XOM","XON","XOO","XOP","XOQ","XOR","XOS","XOT","XOU","XOV","XOW","XOX", +"XOY","XOZ","XPA","XPB","XPC","XPD","XPE","XPF","XPG","XPH","XPI","XPJ","XPK","XPL","XPM","XPN", +"XPO","XPP","XPQ","XPR","XPS","XPT","XPU","XPV","XPW","XPX","XPY","XPZ","XQA","XQB","XQC","XQD", +"XQE","XQF","XQG","XQH","XQI","XQJ","XQK","XQL","XQM","XQN","XQO","XQP","XQQ","XQR","XQS","XQT", +"XQU","XQV","XQW","XQX","XQY","XQZ","XRA","XRB","XRC","XRD","XRE","XRF","XRG","XRH","XRI","XRJ", +"XRK","XRL","XRM","XRN","XRO","XRP","XRQ","XRR","XRS","XRT","XRU","XRV","XRW","XRX","XRY","XRZ", +"XSA","XSB","XSC","XSD","XSE","XSF","XSG","XSH","XSI","XSJ","XSK","XSL","XSM","XSN","XSO","XSP", +"XSQ","XSR","XSS","XST","XSU","XSV","XSW","XSX","XSY","XSZ","XTA","XTB","XTC","XTD","XTE","XTF", +"XTG","XTH","XTI","XTJ","XTK","XTL","XTM","XTN","XTO","XTP","XTQ","XTR","XTS","XTT","XTU","XTV", +"XTW","XTX","XTY","XTZ","XUA","XUB","XUC","XUD","XUE","XUF","XUG","XUH","XUI","XUJ","XUK","XUL", +"XUM","XUN","XUO","XUP","XUQ","XUR","XUS","XUT","XUU","XUV","XUW","XUX","XUY","XUZ","XVA","XVB", +"XVC","XVD","XVE","XVF","XVG","XVH","XVI","XVJ","XVK","XVL","XVM","XVN","XVO","XVP","XVQ","XVR", +"XVS","XVT","XVU","XVV","XVW","XVX","XVY","XVZ","XWA","XWB","XWC","XWD","XWE","XWF","XWG","XWH", +"XWI","XWJ","XWK","XWL","XWM","XWN","XWO","XWP","XWQ","XWR","XWS","XWT","XWU","XWV","XWW","XWX", +"XWY","XWZ","XXA","XXB","XXC","XXD","XXE","XXF","XXG","XXH","XXI","XXJ","XXK","XXL","XXM","XXN", +"XXO","XXP","XXQ","XXR","XXS","XXT","XXU","XXV","XXW","XXX","XXY","XXZ","XYA","XYB","XYC","XYD", +"XYE","XYF","XYG","XYH","XYI","XYJ","XYK","XYL","XYM","XYN","XYO","XYP","XYQ","XYR","XYS","XYT", +"XYU","XYV","XYW","XYX","XYY","XYZ","XZA","XZB","XZC","XZD","XZE","XZF","XZG","XZH","XZI","XZJ", +"XZK","XZL","XZM","XZN","XZO","XZP","XZQ","XZR","XZS","XZT","XZU","XZV","XZW","XZX","XZY","XZZ", + +"YAA","YAB","YAC","YAD", +"YAE","YAF","YAG","YAH","YAI","YAJ","YAK","YAL","YAM","YAN","YAO","YAP","YAQ","YAR","YAS","YAT", +"YAU","YAV","YAW","YAX","YAY","YAZ","YBA","YBB","YBC","YBD","YBE","YBF","YBG","YBH","YBI","YBJ", +"YBK","YBL","YBM","YBN","YBO","YBP","YBQ","YBR","YBS","YBT","YBU","YBV","YBW","YBX","YBY","YBZ", +"YCA","YCB","YCC","YCD","YCE","YCF","YCG","YCH","YCI","YCJ","YCK","YCL","YCM","YCN","YCO","YCP", +"YCQ","YCR","YCS","YCT","YCU","YCV","YCW","YCX","YCY","YCZ","YDA","YDB","YDC","YDD","YDE","YDF", +"YDG","YDH","YDI","YDJ","YDK","YDL","YDM","YDN","YDO","YDP","YDQ","YDR","YDS","YDT","YDU","YDV", +"YDW","YDX","YDY","YDZ","YEA","YEB","YEC","YED","YEE","YEF","YEG","YEH","YEI","YEJ","YEK","YEL", +"YEM","YEN","YEO","YEP","YEQ","YER","YES","YET","YEU","YEV","YEW","YEX","YEY","YEZ","YFA","YFB", +"YFC","YFD","YFE","YFF","YFG","YFH","YFI","YFJ","YFK","YFL","YFM","YFN","YFO","YFP","YFQ","YFR", +"YFS","YFT","YFU","YFV","YFW","YFX","YFY","YFZ","YGA","YGB","YGC","YGD","YGE","YGF","YGG","YGH", +"YGI","YGJ","YGK","YGL","YGM","YGN","YGO","YGP","YGQ","YGR","YGS","YGT","YGU","YGV","YGW","YGX", +"YGY","YGZ","YHA","YHB","YHC","YHD","YHE","YHF","YHG","YHH","YHI","YHJ","YHK","YHL","YHM","YHN", +"YHO","YHP","YHQ","YHR","YHS","YHT","YHU","YHV","YHW","YHX","YHY","YHZ","YIA","YIB","YIC","YID", +"YIE","YIF","YIG","YIH","YII","YIJ","YIK","YIL","YIM","YIN","YIO","YIP","YIQ","YIR","YIS","YIT", +"YIU","YIV","YIW","YIX","YIY","YIZ","YJA","YJB","YJC","YJD","YJE","YJF","YJG","YJH","YJI","YJJ", +"YJK","YJL","YJM","YJN","YJO","YJP","YJQ","YJR","YJS","YJT","YJU","YJV","YJW","YJX","YJY","YJZ", +"YKA","YKB","YKC","YKD","YKE","YKF","YKG","YKH","YKI","YKJ","YKK","YKL","YKM","YKN","YKO","YKP", +"YKQ","YKR","YKS","YKT","YKU","YKV","YKW","YKX","YKY","YKZ","YLA","YLB","YLC","YLD","YLE","YLF", +"YLG","YLH","YLI","YLJ","YLK","YLL","YLM","YLN","YLO","YLP","YLQ","YLR","YLS","YLT","YLU","YLV", +"YLW","YLX","YLY","YLZ","YMA","YMB","YMC","YMD","YME","YMF","YMG","YMH","YMI","YMJ","YMK","YML", +"YMM","YMN","YMO","YMP","YMQ","YMR","YMS","YMT","YMU","YMV","YMW","YMX","YMY","YMZ","YNA","YNB", +"YNC","YND","YNE","YNF","YNG","YNH","YNI","YNJ","YNK","YNL","YNM","YNN","YNO","YNP","YNQ","YNR", +"YNS","YNT","YNU","YNV","YNW","YNX","YNY","YNZ","YOA","YOB","YOC","YOD","YOE","YOF","YOG","YOH", +"YOI","YOJ","YOK","YOL","YOM","YON","YOO","YOP","YOQ","YOR","YOS","YOT","YOU","YOV","YOW","YOX", +"YOY","YOZ","YPA","YPB","YPC","YPD","YPE","YPF","YPG","YPH","YPI","YPJ","YPK","YPL","YPM","YPN", +"YPO","YPP","YPQ","YPR","YPS","YPT","YPU","YPV","YPW","YPX","YPY","YPZ","YQA","YQB","YQC","YQD", +"YQE","YQF","YQG","YQH","YQI","YQJ","YQK","YQL","YQM","YQN","YQO","YQP","YQQ","YQR","YQS","YQT", +"YQU","YQV","YQW","YQX","YQY","YQZ","YRA","YRB","YRC","YRD","YRE","YRF","YRG","YRH","YRI","YRJ", +"YRK","YRL","YRM","YRN","YRO","YRP","YRQ","YRR","YRS","YRT","YRU","YRV","YRW","YRX","YRY","YRZ", +"YSA","YSB","YSC","YSD","YSE","YSF","YSG","YSH","YSI","YSJ","YSK","YSL","YSM","YSN","YSO","YSP", +"YSQ","YSR","YSS","YST","YSU","YSV","YSW","YSX","YSY","YSZ","YTA","YTB","YTC","YTD","YTE","YTF", +"YTG","YTH","YTI","YTJ","YTK","YTL","YTM","YTN","YTO","YTP","YTQ","YTR","YTS","YTT","YTU","YTV", +"YTW","YTX","YTY","YTZ","YUA","YUB","YUC","YUD","YUE","YUF","YUG","YUH","YUI","YUJ","YUK","YUL", +"YUM","YUN","YUO","YUP","YUQ","YUR","YUS","YUT","YUU","YUV","YUW","YUX","YUY","YUZ","YVA","YVB", +"YVC","YVD","YVE","YVF","YVG","YVH","YVI","YVJ","YVK","YVL","YVM","YVN","YVO","YVP","YVQ","YVR", +"YVS","YVT","YVU","YVV","YVW","YVX","YVY","YVZ","YWA","YWB","YWC","YWD","YWE","YWF","YWG","YWH", +"YWI","YWJ","YWK","YWL","YWM","YWN","YWO","YWP","YWQ","YWR","YWS","YWT","YWU","YWV","YWW","YWX", +"YWY","YWZ","YXA","YXB","YXC","YXD","YXE","YXF","YXG","YXH","YXI","YXJ","YXK","YXL","YXM","YXN", +"YXO","YXP","YXQ","YXR","YXS","YXT","YXU","YXV","YXW","YXX","YXY","YXZ","YYA","YYB","YYC","YYD", +"YYE","YYF","YYG","YYH","YYI","YYJ","YYK","YYL","YYM","YYN","YYO","YYP","YYQ","YYR","YYS","YYT", +"YYU","YYV","YYW","YYX","YYY","YYZ","YZA","YZB","YZC","YZD","YZE","YZF","YZG","YZH","YZI","YZJ", +"YZK","YZL","YZM","YZN","YZO","YZP","YZQ","YZR","YZS","YZT","YZU","YZV","YZW","YZX","YZY","YZZ", + +"ZAA","ZAB","ZAC","ZAD", +"ZAE","ZAF","ZAG","ZAH","ZAI","ZAJ","ZAK","ZAL","ZAM","ZAN","ZAO","ZAP","ZAQ","ZAR","ZAS","ZAT", +"ZAU","ZAV","ZAW","ZAX","ZAY","ZAZ","ZBA","ZBB","ZBC","ZBD","ZBE","ZBF","ZBG","ZBH","ZBI","ZBJ", +"ZBK","ZBL","ZBM","ZBN","ZBO","ZBP","ZBQ","ZBR","ZBS","ZBT","ZBU","ZBV","ZBW","ZBX","ZBY","ZBZ", +"ZCA","ZCB","ZCC","ZCD","ZCE","ZCF","ZCG","ZCH","ZCI","ZCJ","ZCK","ZCL","ZCM","ZCN","ZCO","ZCP", +"ZCQ","ZCR","ZCS","ZCT","ZCU","ZCV","ZCW","ZCX","ZCY","ZCZ","ZDA","ZDB","ZDC","ZDD","ZDE","ZDF", +"ZDG","ZDH","ZDI","ZDJ","ZDK","ZDL","ZDM","ZDN","ZDO","ZDP","ZDQ","ZDR","ZDS","ZDT","ZDU","ZDV", +"ZDW","ZDX","ZDY","ZDZ","ZEA","ZEB","ZEC","ZED","ZEE","ZEF","ZEG","ZEH","ZEI","ZEJ","ZEK","ZEL", +"ZEM","ZEN","ZEO","ZEP","ZEQ","ZER","ZES","ZET","ZEU","ZEV","ZEW","ZEX","ZEY","ZEZ","ZFA","ZFB", +"ZFC","ZFD","ZFE","ZFF","ZFG","ZFH","ZFI","ZFJ","ZFK","ZFL","ZFM","ZFN","ZFO","ZFP","ZFQ","ZFR", +"ZFS","ZFT","ZFU","ZFV","ZFW","ZFX","ZFY","ZFZ","ZGA","ZGB","ZGC","ZGD","ZGE","ZGF","ZGG","ZGH", +"ZGI","ZGJ","ZGK","ZGL","ZGM","ZGN","ZGO","ZGP","ZGQ","ZGR","ZGS","ZGT","ZGU","ZGV","ZGW","ZGX", +"ZGY","ZGZ","ZHA","ZHB","ZHC","ZHD","ZHE","ZHF","ZHG","ZHH","ZHI","ZHJ","ZHK","ZHL","ZHM","ZHN", +"ZHO","ZHP","ZHQ","ZHR","ZHS","ZHT","ZHU","ZHV","ZHW","ZHX","ZHY","ZHZ","ZIA","ZIB","ZIC","ZID", +"ZIE","ZIF","ZIG","ZIH","ZII","ZIJ","ZIK","ZIL","ZIM","ZIN","ZIO","ZIP","ZIQ","ZIR","ZIS","ZIT", +"ZIU","ZIV","ZIW","ZIX","ZIY","ZIZ","ZJA","ZJB","ZJC","ZJD","ZJE","ZJF","ZJG","ZJH","ZJI","ZJJ", +"ZJK","ZJL","ZJM","ZJN","ZJO","ZJP","ZJQ","ZJR","ZJS","ZJT","ZJU","ZJV","ZJW","ZJX","ZJY","ZJZ", +"ZKA","ZKB","ZKC","ZKD","ZKE","ZKF","ZKG","ZKH","ZKI","ZKJ","ZKK","ZKL","ZKM","ZKN","ZKO","ZKP", +"ZKQ","ZKR","ZKS","ZKT","ZKU","ZKV","ZKW","ZKX","ZKY","ZKZ","ZLA","ZLB","ZLC","ZLD","ZLE","ZLF", +"ZLG","ZLH","ZLI","ZLJ","ZLK","ZLL","ZLM","ZLN","ZLO","ZLP","ZLQ","ZLR","ZLS","ZLT","ZLU","ZLV", +"ZLW","ZLX","ZLY","ZLZ","ZMA","ZMB","ZMC","ZMD","ZME","ZMF","ZMG","ZMH","ZMI","ZMJ","ZMK","ZML", +"ZMM","ZMN","ZMO","ZMP","ZMQ","ZMR","ZMS","ZMT","ZMU","ZMV","ZMW","ZMX","ZMY","ZMZ","ZNA","ZNB", +"ZNC","ZND","ZNE","ZNF","ZNG","ZNH","ZNI","ZNJ","ZNK","ZNL","ZNM","ZNN","ZNO","ZNP","ZNQ","ZNR", +"ZNS","ZNT","ZNU","ZNV","ZNW","ZNX","ZNY","ZNZ","ZOA","ZOB","ZOC","ZOD","ZOE","ZOF","ZOG","ZOH", +"ZOI","ZOJ","ZOK","ZOL","ZOM","ZON","ZOO","ZOP","ZOQ","ZOR","ZOS","ZOT","ZOU","ZOV","ZOW","ZOX", +"ZOY","ZOZ","ZPA","ZPB","ZPC","ZPD","ZPE","ZPF","ZPG","ZPH","ZPI","ZPJ","ZPK","ZPL","ZPM","ZPN", +"ZPO","ZPP","ZPQ","ZPR","ZPS","ZPT","ZPU","ZPV","ZPW","ZPX","ZPY","ZPZ","ZQA","ZQB","ZQC","ZQD", +"ZQE","ZQF","ZQG","ZQH","ZQI","ZQJ","ZQK","ZQL","ZQM","ZQN","ZQO","ZQP","ZQQ","ZQR","ZQS","ZQT", +"ZQU","ZQV","ZQW","ZQX","ZQY","ZQZ","ZRA","ZRB","ZRC","ZRD","ZRE","ZRF","ZRG","ZRH","ZRI","ZRJ", +"ZRK","ZRL","ZRM","ZRN","ZRO","ZRP","ZRQ","ZRR","ZRS","ZRT","ZRU","ZRV","ZRW","ZRX","ZRY","ZRZ", +"ZSA","ZSB","ZSC","ZSD","ZSE","ZSF","ZSG","ZSH","ZSI","ZSJ","ZSK","ZSL","ZSM","ZSN","ZSO","ZSP", +"ZSQ","ZSR","ZSS","ZST","ZSU","ZSV","ZSW","ZSX","ZSY","ZSZ","ZTA","ZTB","ZTC","ZTD","ZTE","ZTF", +"ZTG","ZTH","ZTI","ZTJ","ZTK","ZTL","ZTM","ZTN","ZTO","ZTP","ZTQ","ZTR","ZTS","ZTT","ZTU","ZTV", +"ZTW","ZTX","ZTY","ZTZ","ZUA","ZUB","ZUC","ZUD","ZUE","ZUF","ZUG","ZUH","ZUI","ZUJ","ZUK","ZUL", +"ZUM","ZUN","ZUO","ZUP","ZUQ","ZUR","ZUS","ZUT","ZUU","ZUV","ZUW","ZUX","ZUY","ZUZ","ZVA","ZVB", +"ZVC","ZVD","ZVE","ZVF","ZVG","ZVH","ZVI","ZVJ","ZVK","ZVL","ZVM","ZVN","ZVO","ZVP","ZVQ","ZVR", +"ZVS","ZVT","ZVU","ZVV","ZVW","ZVX","ZVY","ZVZ","ZWA","ZWB","ZWC","ZWD","ZWE","ZWF","ZWG","ZWH", +"ZWI","ZWJ","ZWK","ZWL","ZWM","ZWN","ZWO","ZWP","ZWQ","ZWR","ZWS","ZWT","ZWU","ZWV","ZWW","ZWX", +"ZWY","ZWZ","ZXA","ZXB","ZXC","ZXD","ZXE","ZXF","ZXG","ZXH","ZXI","ZXJ","ZXK","ZXL","ZXM","ZXN", +"ZXO","ZXP","ZXQ","ZXR","ZXS","ZXT","ZXU","ZXV","ZXW","ZXX","ZXY","ZXZ","ZYA","ZYB","ZYC","ZYD", +"ZYE","ZYF","ZYG","ZYH","ZYI","ZYJ","ZYK","ZYL","ZYM","ZYN","ZYO","ZYP","ZYQ","ZYR","ZYS","ZYT", +"ZYU","ZYV","ZYW","ZYX","ZYY","ZYZ","ZZA","ZZB","ZZC","ZZD","ZZE","ZZF","ZZG","ZZH","ZZI","ZZJ", +"ZZK","ZZL","ZZM","ZZN","ZZO","ZZP","ZZQ","ZZR","ZZS","ZZT","ZZU","ZZV","ZZW","ZZX","ZZY","ZZZ" +}; + +/* + Doublets +*/ + +static const char d26[][3] = +{ +"AA","AB","AC","AD","AE","AF","AG","AH","AI","AJ","AK","AL","AM","AN","AO","AP", +"AQ","AR","AS","AT","AU","AV","AW","AX","AY","AZ","BA","BB","BC","BD","BE","BF", +"BG","BH","BI","BJ","BK","BL","BM","BN","BO","BP","BQ","BR","BS","BT","BU","BV", +"BW","BX","BY","BZ","CA","CB","CC","CD","CE","CF","CG","CH","CI","CJ","CK","CL", +"CM","CN","CO","CP","CQ","CR","CS","CT","CU","CV","CW","CX","CY","CZ","DA","DB", +"DC","DD","DE","DF","DG","DH","DI","DJ","DK","DL","DM","DN","DO","DP","DQ","DR", +"DS","DT","DU","DV","DW","DX","DY","DZ","EA","EB","EC","ED","EE","EF","EG","EH", +"EI","EJ","EK","EL","EM","EN","EO","EP","EQ","ER","ES","ET","EU","EV","EW","EX", +"EY","EZ","FA","FB","FC","FD","FE","FF","FG","FH","FI","FJ","FK","FL","FM","FN", +"FO","FP","FQ","FR","FS","FT","FU","FV","FW","FX","FY","FZ","GA","GB","GC","GD", +"GE","GF","GG","GH","GI","GJ","GK","GL","GM","GN","GO","GP","GQ","GR","GS","GT", +"GU","GV","GW","GX","GY","GZ","HA","HB","HC","HD","HE","HF","HG","HH","HI","HJ", +"HK","HL","HM","HN","HO","HP","HQ","HR","HS","HT","HU","HV","HW","HX","HY","HZ", +"IA","IB","IC","ID","IE","IF","IG","IH","II","IJ","IK","IL","IM","IN","IO","IP", +"IQ","IR","IS","IT","IU","IV","IW","IX","IY","IZ","JA","JB","JC","JD","JE","JF", +"JG","JH","JI","JJ","JK","JL","JM","JN","JO","JP","JQ","JR","JS","JT","JU","JV", +"JW","JX","JY","JZ","KA","KB","KC","KD","KE","KF","KG","KH","KI","KJ","KK","KL", +"KM","KN","KO","KP","KQ","KR","KS","KT","KU","KV","KW","KX","KY","KZ","LA","LB", +"LC","LD","LE","LF","LG","LH","LI","LJ","LK","LL","LM","LN","LO","LP","LQ","LR", +"LS","LT","LU","LV","LW","LX","LY","LZ","MA","MB","MC","MD","ME","MF","MG","MH", +"MI","MJ","MK","ML","MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV","MW","MX", +"MY","MZ","NA","NB","NC","ND","NE","NF","NG","NH","NI","NJ","NK","NL","NM","NN", +"NO","NP","NQ","NR","NS","NT","NU","NV","NW","NX","NY","NZ","OA","OB","OC","OD", +"OE","OF","OG","OH","OI","OJ","OK","OL","OM","ON","OO","OP","OQ","OR","OS","OT", +"OU","OV","OW","OX","OY","OZ","PA","PB","PC","PD","PE","PF","PG","PH","PI","PJ", +"PK","PL","PM","PN","PO","PP","PQ","PR","PS","PT","PU","PV","PW","PX","PY","PZ", +"QA","QB","QC","QD","QE","QF","QG","QH","QI","QJ","QK","QL","QM","QN","QO","QP", +"QQ","QR","QS","QT","QU","QV","QW","QX","QY","QZ","RA","RB","RC","RD","RE","RF", +"RG","RH","RI","RJ","RK","RL","RM","RN","RO","RP","RQ","RR","RS","RT","RU","RV", +"RW","RX","RY","RZ","SA","SB","SC","SD","SE","SF","SG","SH","SI","SJ","SK","SL", +"SM","SN","SO","SP","SQ","SR","SS","ST","SU","SV","SW","SX","SY","SZ","TA","TB", +"TC","TD","TE","TF","TG","TH","TI","TJ","TK","TL","TM","TN","TO","TP","TQ","TR", +"TS","TT","TU","TV","TW","TX","TY","TZ","UA","UB","UC","UD","UE","UF","UG","UH", +"UI","UJ","UK","UL","UM","UN","UO","UP","UQ","UR","US","UT","UU","UV","UW","UX", +"UY","UZ","VA","VB","VC","VD","VE","VF","VG","VH","VI","VJ","VK","VL","VM","VN", +"VO","VP","VQ","VR","VS","VT","VU","VV","VW","VX","VY","VZ","WA","WB","WC","WD", +"WE","WF","WG","WH","WI","WJ","WK","WL","WM","WN","WO","WP","WQ","WR","WS","WT", +"WU","WV","WW","WX","WY","WZ","XA","XB","XC","XD","XE","XF","XG","XH","XI","XJ", +"XK","XL","XM","XN","XO","XP","XQ","XR","XS","XT","XU","XV","XW","XX","XY","XZ", +"YA","YB","YC","YD","YE","YF","YG","YH","YI","YJ","YK","YL","YM","YN","YO","YP", +"YQ","YR","YS","YT","YU","YV","YW","YX","YY","YZ","ZA","ZB","ZC","ZD","ZE","ZF", +"ZG","ZH","ZI","ZJ","ZK","ZL","ZM","ZN","ZO","ZP","ZQ","ZR","ZS","ZT","ZU","ZV", +"ZW","ZX","ZY","ZZ" +}; + +/* + Also tabulate 26 base-26 chars. +*/ + +static const char* const c26 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +/* Weight scheme for check character */ +#define N_UNIQUE_WEIGHTS 12 +static const int weights_for_checksum[N_UNIQUE_WEIGHTS] = + { 1,3,5,7,9,11,15,17,19,21,23,25 }; + /* co-primes with 26 which are < 26 */ + + +/* Get a character representing 1st 14-bit triplet + (bits 0..13 of contiguous array of octets) */ +const char* base26_triplet_1(const unsigned char *a) +{ +UINT32 b0, b1,h; + b0 = (UINT32) a[0]; /* 1111 1111 */ +#ifndef FIX_BASE26_ENC_BUG + b1 = (UINT32) ( a[1] & 0x3f ); /* 0011 1111 */ + h = (UINT32) ( b0 | b1 << 8 ); +#else + b1 = (UINT32) ( a[1] & 0xfc ); /* 1111 1100 */ + h = (UINT32) ( b0 << 8 | b1 ) >> 2; +#endif + return t26[h]; +} + + +/* Get a character representing 2nd 14-bit triplet (bits 14..27) */ +const char* base26_triplet_2(const unsigned char *a) +{ +UINT32 b0, b1, b2, h; +#ifndef FIX_BASE26_ENC_BUG + b0 = (UINT32) ( a[1] & 0xc0); /* 1100 0000 */ + b1 = (UINT32) ( a[2] ); /* 1111 1111 */ + b2 = (UINT32) ( a[3] & 0x0f ); /* 0000 1111 */ + h = (UINT32)( b0 | b1 << 8 | b2 << 16 ) >> 6 ; +#else + b0 = (UINT32) ( a[1] & 0x03); /* 0000 0011 */ + b1 = (UINT32) ( a[2] ); /* 1111 1111 */ + b2 = (UINT32) ( a[3] & 0xf0 ); /* 1111 0000 */ + h = (UINT32)( b0 << 16 | b1 << 8 | b2 ) >> 4 ; +#endif + return t26[h]; +} + + +/* Get a character representing 3rd 14-bit triplet (bits 28..41) */ +const char* base26_triplet_3(const unsigned char *a) +{ +UINT32 b0, b1, b2, h; +#ifndef FIX_BASE26_ENC_BUG + b0 = (UINT32) ( a[3] & 0xf0); /* 1111 0000 */ + b1 = (UINT32) ( a[4] ); /* 1111 1111 */ + b2 = (UINT32) ( a[5] & 0x03 ); /* 0000 0011 */ + h = (UINT32) ( b0 | b1 << 8 | b2 << 16 ) >> 4 ; +#else + b0 = (UINT32) ( a[3] & 0x0f); /* 0000 1111 */ + b1 = (UINT32) ( a[4] ); /* 1111 1111 */ + b2 = (UINT32) ( a[5] & 0xc0 ); /* 1100 0000 */ + h = (UINT32) ( b0 << 16 | b1 << 8 | b2 ) >> 6 ; +#endif + return t26[h]; +} + + +/* Get a character representing 4th 14-bit triplet (bits 42..55) */ +const char* base26_triplet_4(const unsigned char *a) +{ +UINT32 b0, b1, h; +#ifndef FIX_BASE26_ENC_BUG + b0 = (UINT32) ( a[5] & 0xfc); /* 1111 1100 */ + b1 = (UINT32) ( a[6] ); /* 1111 1111 */ + h = (UINT32) ( b0 | b1 << 8 ) >> 2 ; +#else + b0 = (UINT32) ( a[5] & 0x3f); /* 0011 1111 */ + b1 = (UINT32) ( a[6] ); /* 1111 1111 */ + h = (UINT32) ( b0 << 8 | b1 ) ; +#endif + return t26[h]; +} + + +/* + Tail dublets +*/ + +/* + a4 a3 a2 a1 a0 + 28-36: 0001 1111 1111 0000 0000 0000 0000 0000 0000 0000 +*/ + + +/* Get dublet (bits 28..36) */ +const char* base26_dublet_for_bits_28_to_36(unsigned char *a) +{ +UINT32 b0, b1, h; +#ifndef FIX_BASE26_ENC_BUG + b0 = (UINT32) ( a[3] & 0xf0); /* 1111 0000 */ + b1 = (UINT32) ( a[4] & 0x1f ); /* 0001 1111 */ + h = (UINT32)( b0 | b1 << 8 ) >> 4 ; +#else + b0 = (UINT32) ( a[3] & 0x0f); /* 0000 1111 */ + b1 = (UINT32) ( a[4] & 0xf8 ); /* 1111 1000 */ + h = (UINT32)( b0 << 8 | b1 ) >> 3 ; +#endif + return d26[h]; +} + + +/* + a9 a8 a7 + 56-64: 0000 0000 0000 0001 1111 1111 +*/ + + +/* Get dublet (bits 56..64) */ +const char* base26_dublet_for_bits_56_to_64(unsigned char *a) +{ +UINT32 b0, b1, h; +#ifndef FIX_BASE26_ENC_BUG + b0 = (UINT32) ( a[7] ); /* 1111 1111 */ + b1 = (UINT32) ( a[8] & 0x01 ); /* 0000 0001 */ + h = (UINT32)( b0 | b1 << 8 ); +#else + b0 = (UINT32) ( a[7] ); /* 1111 1111 */ + b1 = (UINT32) ( a[8] & 0x80 ); /* 1000 0000 */ + h = (UINT32)( b0 << 8 | b1 ) >> 7; +#endif + return d26[h]; +} + + +/* Calculate check character A..Z for the string. + NB: ignore delimiter dashes. */ +const char base26_checksum(const char *str) +{ +size_t slen, j, jj=0, checksum = 0; +char c; + + slen = strlen(str); + + for (j=0; j < slen; j++) + { + c = str[j]; + if (c=='-') + continue; + + checksum+= weights_for_checksum[jj]*c; + + jj++; + if (jj > N_UNIQUE_WEIGHTS - 1) + jj = 0; + } + return c26[checksum%26]; +} + + +/* Get hash extension in hexadecimal representation for the major block. + Len(extension) = 256 - 65 = 191 bit. */ +void get_xtra_hash_major_hex(const unsigned char *a, char* szXtra) +{ +unsigned char c; +int i, j, start_byte=8; +#ifndef FIX_BASE26_ENC_BUG + c = a[start_byte] & 0xfe ; /* 1111 1110 */ +#else + c = a[start_byte] & 0x7f ; /* 0111 1111 */ +#endif + j = sprintf(szXtra,"%02x", c); + for( i = start_byte+1; i < 32; i++ ) + j+= sprintf(szXtra+j,"%02x", a[i]); +} + + +/* Get hash extension in hexadecimal representation for the minor block. + Len(extension) = 256 - 37 = 219 bit. */ +void get_xtra_hash_minor_hex(const unsigned char *a, char* szXtra) +{ +unsigned char c; +int i, j, start_byte=4; +#ifndef FIX_BASE26_ENC_BUG + c = a[start_byte] & 0xe0 ; /* 1110 0000 */ +#else + c = a[start_byte] & 0x07 ; /* 0000 0111 */ +#endif + j = sprintf(szXtra,"%02x", c); + for( i = start_byte+1; i < 32; i++ ) + j+= sprintf(szXtra+j,"%02x", a[i]); +} diff --git a/INCHI-1-SRC/INCHI_BASE/src/ikey_base26.h b/INCHI-1-SRC/INCHI_BASE/src/ikey_base26.h new file mode 100644 index 0000000..55c2761 --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/ikey_base26.h @@ -0,0 +1,96 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _IKEY_BASE26_H_ +#define _IKEY_BASE26_H_ + +/* + Base-26 encoding procedures. + + 'Base26' characters here are considered to be uppercase English + letters 'A..Z' +*/ + + +/* Uncomment the next line to fix base-26 encoding bug */ +/*#define FIX_BASE26_ENC_BUG 1*/ + +typedef unsigned int UINT32; +typedef unsigned short int UINT16; + +#ifdef __cplusplus +extern "C" { +#endif + +/* Get a character representing 1st 14-bit triplet + (bits 0..13 of contiguous array of octets) */ +const char* base26_triplet_1(const unsigned char *a); +/* Get a character representing 2nd 14-bit triplet (bits 14..27) */ +const char* base26_triplet_2(const unsigned char *a); +/* Get a character representing 3rd 14-bit triplet (bits 28..41) */ +const char* base26_triplet_3(const unsigned char *a); +/* Get a character representing 4th 14-bit triplet (bits 42..55) */ +const char* base26_triplet_4(const unsigned char *a); + +/* + Tail dublets +*/ + +/* Get dublet (bits 28..36) */ +const char* base26_dublet_for_bits_28_to_36(unsigned char *a); +/* Get dublet (bits 56..64) */ +const char* base26_dublet_for_bits_56_to_64(unsigned char *a); +/* Calculate check character for the string. */ +const char base26_checksum(const char *str); +/* Get hash extension in hexadecimal representation for the major block. + Len(extension) = 256 - 65 = 191 bit. */ +void get_xtra_hash_major_hex(const unsigned char *a, char* szXtra); +/* Get hash extension in hexadecimal representation for the minor block. + Len(extension) = 256 - 37 = 219 bit. */ +void get_xtra_hash_minor_hex(const unsigned char *a, char* szXtra); + +/* Used instead of isupper() to avoid locale interference. */ +#define isbase26(_c) ( ((unsigned)(_c) >= 'A') && ((unsigned)(_c) <= 'Z') ) + +#ifdef __cplusplus +} +#endif + + +#endif /* _IKEY_BASE26_H_ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/ikey_dll.c b/INCHI-1-SRC/INCHI_BASE/src/ikey_dll.c similarity index 55% rename from INCHI-1-SRC/INCHI_API/inchi_dll/ikey_dll.c rename to INCHI-1-SRC/INCHI_BASE/src/ikey_dll.c index 5be961f..bc4d00f 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/ikey_dll.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ikey_dll.c @@ -1,567 +1,573 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - InChIKey: calculation of hash for InChI string - - Uses truncated SHA-256 function. - SHA-256 implementation: Copyright (C) Brainspark B.V., - see files sha2.c, sha2.h. - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -#ifdef _MSC_VER -#if _MSC_VER > 1000 -#pragma warning( disable : 4996 ) -#endif -#endif - -#include -#include -#include -#include -#include - -#include "sha2.h" -#include "ikey_base26.h" - -#include "ichisize.h" -#include "mode.h" -#include "inchi_api.h" - -#include "util.h" - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Local options -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -#define INCHIKEY_DEBUG 0 /* 2 */ - -#define INCHIKEY_FLAG_OK 0 -#define INCHIKEY_NOT_VALID_FLAG 1 - -enum { MINOUTLENGTH=256 }; - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Local functions -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -void fprint_digest(FILE* fw, const char *header, unsigned char *a); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - EXPORTED FUNCTIONS -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStdINCHIKeyFromStdINCHI(const char* szINCHISource, - char* szINCHIKey) -{ - if ( strlen(szINCHISource) < LEN_INCHI_STRING_PREFIX+3 ) - return INCHIKEY_INVALID_STD_INCHI; - if (szINCHISource[LEN_INCHI_STRING_PREFIX+1]!='S') - return INCHIKEY_INVALID_STD_INCHI; - return GetINCHIKeyFromINCHI(szINCHISource, 0, 0, szINCHIKey, (char *) NULL, (char *) NULL); -} - -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetINCHIKeyFromINCHI(const char* szINCHISource, - const int xtra1, - const int xtra2, - char* szINCHIKey, - char* szXtra1, - char* szXtra2) -{ -int ret = INCHIKEY_OK; -int ret1 = INCHIKEY_OK; -int cn; -size_t slen, i, j, jproto=0, ncp, pos_slash1=0; -char *str = NULL, *smajor = NULL, *sminor = NULL, - *sproto=NULL, - *stmp = NULL, tmp[MINOUTLENGTH]; -unsigned char - digest_major[32], digest_minor[32]; - -char flagstd = 'S', /* standard key */ - flagnonstd = 'N', /* non-standard key */ - flagver = 'A', /* InChI v. 1 */ - flagproto = 'N'; /* no [de]protonization , by default */ -int nprotons; -/* -Protonization encoding: -N 0 -O +1 P +2 Q +3 R +4 S +5 T +6 U +7 V +8 W +9 X +10 Y +11 Z +12 -M -1 L-2 K -3 J -4 I -5 H -6 G -7 F -8 E -9 D -10 C -11 B -12 -A < -12 or > +12 -*/ -static const char *pplus = "OPQRSTUVWXYZ"; -static const char *pminus = "MLKJIHGFEDCB"; - -int bStdFormat = 0; -size_t bytelen = 32; - - - /* Check if input is a valid InChI string */ - - /* .. non-empty */ - if (szINCHISource==NULL) - return INCHIKEY_EMPTY_INPUT; - - slen = strlen(szINCHISource); - - /* .. has valid prefix */ - if (slen standard InChIKey */ - bStdFormat = 1; - pos_slash1++; - } - - /* .. has trailing slash in the right place */ - if (szINCHISource[pos_slash1]!='/') - return INCHIKEY_INVALID_INCHI_PREFIX; - - /* .. the rest of source string contains at least one a..Z0.9 or slash */ - /* TODO: improve/add full string check */ - if (!isalnum(szINCHISource[pos_slash1+1] ) && - ( szINCHISource[pos_slash1+1]!='/' ) ) - return INCHIKEY_INVALID_INCHI; - - - /*^^^ Ok. Will use a local copy of the source. */ - - extract_inchi_substring(&str, szINCHISource, slen); - if (NULL==str) - { - ret = INCHIKEY_NOT_ENOUGH_MEMORY; - goto fin; - } - slen = strlen(str); - - - /*^^^ Make buffers. */ - smajor = (char*) inchi_calloc( slen+1, sizeof(char)); - if (NULL==smajor) - { ret = INCHIKEY_NOT_ENOUGH_MEMORY; goto fin; } - sminor = (char*) inchi_calloc( 2*slen + 2, sizeof(char)); /* we may double the length ... */ - if (NULL==sminor) - { ret = INCHIKEY_NOT_ENOUGH_MEMORY; goto fin; } - stmp = (char*) inchi_calloc( slen+1, sizeof(char)); - if (NULL==stmp) - { ret = INCHIKEY_NOT_ENOUGH_MEMORY; goto fin; } - sproto = (char*) inchi_calloc( slen+1, sizeof(char)); - if (NULL==sproto) - { ret = INCHIKEY_NOT_ENOUGH_MEMORY; goto fin; } - - - szINCHIKey[0] = '\0'; - - - - /*^^^ Extract the major block. */ - - smajor[0] = '\0'; - for (j = pos_slash1 + 1; j < slen-1; j++) - { - if (str[j]=='/') - { - cn = str[j+1]; - switch (cn) - { - /* anything allowed from a major part */ - case 'c': case 'h': case 'q': continue; - - /* "/p"; protons now go to to special string, not to minor hash */ - case 'p': jproto = j; - continue; - - /* "/f", "/r" : may not occur in stdInChI */ - case 'f': case 'r': if ( bStdFormat ) - { - ret = INCHIKEY_INVALID_STD_INCHI; - goto fin; - } - break; - - /* anything allowed from a minor part */ - default: break; - } - break; - } - } - j++; - if (j==slen) - j++; - else - j--; - - if (jproto) - ncp = jproto - pos_slash1 - 1; - else - ncp = j - pos_slash1 - 1; - - - /*^^^ Trim 'InChI=1[S]/' */ - memcpy(smajor,str+pos_slash1+1, ncp*sizeof(str[0])); - smajor[ncp]='\0'; - - - /* Treat protonization */ - if (jproto) - { - /* 2009-01-07 fix bug/typo: assigned incorrect length to the protonation segment of - /* source string ( was sproto[ncp]='\0'; should be sproto[lenproto]='\0'; ) */ - int lenproto = j - (int) jproto; - if (lenproto<3) - { - /* empty "/p", should not occur */ - ret = INCHIKEY_INVALID_INCHI; - goto fin; - } - - memcpy(sproto,str+pos_slash1+ncp+1, lenproto*sizeof(str[0])); - sproto[lenproto]='\0'; - - nprotons = strtol( sproto+2, NULL, 10 ); - - if (nprotons > 0) - { - if (nprotons > 12) flagproto = 'A'; - else flagproto = pplus[nprotons-1]; - } - else if (nprotons < 0) - { - if (nprotons < -12) flagproto = 'A'; - else flagproto = pminus[-nprotons-1]; - } - else - { - /* should never occur */ - ret = INCHIKEY_INVALID_STD_INCHI; - goto fin; - } - } - - - - /*^^^ Extract the minor block. */ - - if (j != slen+1) /*^^^ check that something exists at right.*/ - { - ncp = slen-j; - memcpy(sminor,str+j, (ncp)*sizeof(str[0])); - sminor[ncp]='\0'; - } - else - sminor[0]='\0'; - - -#if INCHIKEY_DEBUG - fprintf(stdout,"Source: {%-s}\n",str); - fprintf(stdout,"SMajor: {%-s}\n",smajor); - fprintf(stdout,"SMinor: {%-s}\n",sminor); - fprintf(stdout,"SProto: {%-s}\n",sproto); -#endif - - - - /*^^^ Compute and compose the InChIKey string. */ - - - /*^^^ Major hash sub-string. */ - for( i = 0; i < 32; i++ ) - digest_major[i] = 0; - sha2_csum( (unsigned char *) smajor, (int) strlen(smajor), digest_major ); - - sprintf(tmp,"%-.3s%-.3s%-.3s%-.3s%-.2s", - base26_triplet_1(digest_major), base26_triplet_2(digest_major), - base26_triplet_3(digest_major), base26_triplet_4(digest_major), - base26_dublet_for_bits_56_to_64(digest_major)); - strcat(szINCHIKey, tmp); -#if (INCHIKEY_DEBUG>1) - fprint_digest(stderr, "Major hash, full SHA-256",digest_major); -#endif - - - - /*^^^ Minor hash sub-string. */ - for( i = 0; i < 32; i++ ) - digest_minor[i] = 0; - slen = strlen(sminor); - if ((slen>0)&&(slen<255)) - { - strcpy(stmp, sminor); - strcpy(sminor+slen,stmp); - } - sha2_csum( (unsigned char *) sminor, (int) strlen(sminor), digest_minor ); -#if (INCHIKEY_DEBUG>1) - fprint_digest(stderr, "Minor hash, full SHA-256",digest_minor); -#endif - - strcat(szINCHIKey, "-"); - sprintf(tmp,"%-.3s%-.3s%-.2s", - base26_triplet_1(digest_minor), - base26_triplet_2(digest_minor), - base26_dublet_for_bits_28_to_36(digest_minor)); - strcat(szINCHIKey, tmp); - - - /* Append a standard/non-standard flag */ - slen = strlen(szINCHIKey); - if ( bStdFormat ) - szINCHIKey[slen] = flagstd; - else - szINCHIKey[slen] = flagnonstd; - - /*^^^ Append InChI v.1 flag */ - szINCHIKey[slen+1] = flagver; - - /*^^^ Append dash */ - szINCHIKey[slen+2] = '-'; - - /*^^^ Append protonization flag */ - szINCHIKey[slen+3] = flagproto; - szINCHIKey[slen+4] = '\0'; - - -#if INCHIKEY_DEBUG - fprintf(stdout,"szINCHIKey: {%-s}\n",szINCHIKey); -#endif - - /* Hash extensions */ - if ( xtra1 && szXtra1 ) - { - get_xtra_hash_major_hex(digest_major, szXtra1); -#if INCHIKEY_DEBUG - fprintf(stderr,"XHash1=%-s\n",szXtra1); - fprintf(stderr,"j=%-d\n",j); -#endif - } - if ( xtra2 && szXtra2 ) - { - get_xtra_hash_minor_hex(digest_minor, szXtra2); -#if INCHIKEY_DEBUG - fprintf(stderr,"XHash2=%-s\n",szXtra2); - fprintf(stderr,"j=%-d\n",j); -#endif - } - - -fin:if (NULL!=str) inchi_free(str); - if (NULL!=smajor) inchi_free(smajor); - if (NULL!=sminor) inchi_free(sminor); - if (NULL!=stmp) inchi_free(stmp); - if (NULL!=sproto) inchi_free(sproto); - - if ( (ret==INCHIKEY_OK) && (ret1!=INCHIKEY_OK) ) - ret = ret1; - return ret; - -} - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Check if the string represents valid InChIKey. -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL CheckINCHIKey(const char *szINCHIKey) -{ -size_t slen, j; - - - slen = strlen(szINCHIKey); - - - /*^^^ Proper length is 27 */ - if (slen != 27) - return INCHIKEY_INVALID_LENGTH; - - /*^^^ Should have dash in 14-th position */ - if (szINCHIKey[14] !='-') - return INCHIKEY_INVALID_LAYOUT; - /*^^^ Should have dash in 25-th position */ - if (szINCHIKey[25] !='-') - return INCHIKEY_INVALID_LAYOUT; - - /*^^^ All other should be uppercase */ - for (j = 0; j < 14; j++) - if ( !isbase26(szINCHIKey[j]) ) - return INCHIKEY_INVALID_LAYOUT; /* first block */ - for (j = 15; j < 25; j++) - if ( !isbase26(szINCHIKey[j]) ) - return INCHIKEY_INVALID_LAYOUT; /* second block */ - if ( !isbase26(szINCHIKey[26]) ) - return INCHIKEY_INVALID_LAYOUT; /* (de)protonation flag */ - - - /*^^^ No 'E' may appear in 0,3,6,and 9 positions of the 1st block ... */ - for (j=0; j <10; j+=3) - if (szINCHIKey[j]=='E') - return INCHIKEY_INVALID_LAYOUT; - /*^^^ ... and 0 and 3 pos. of the second block. */ - for (j=15; j <19; j+=3) - if (szINCHIKey[j]=='E') - return INCHIKEY_INVALID_LAYOUT; - - /*^^^ Check for version (only 1 allowed) */ - if (szINCHIKey[24] !='A') - return INCHIKEY_INVALID_VERSION; - - /*^^^ Check for standard-ness */ - if (szINCHIKey[23] == 'S') - return INCHIKEY_VALID_STANDARD; - else if (szINCHIKey[23] == 'N') - return INCHIKEY_VALID_NON_STANDARD; - else - return INCHIKEY_INVALID_LAYOUT; -} - - - - - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -void fprint_digest(FILE* fw, const char *header, unsigned char *a) -{ -size_t i, bytelen = 32; - fprintf(fw,"%s\n", header); - for( i = 0; i < bytelen; i++ ) - fprintf(fw,"%02x ", a[i]); - fprintf(fw,"\n" ); -} - - - -/********************************************************************/ - -#if ( defined( _WIN32 ) && defined( _MSC_VER ) && _MSC_VER >= 800 && defined(_USRDLL) && defined(BUILD_LINK_AS_DLL) ) - /* Win32 & MS VC ++, compile and link as a DLL */ -/*********************************************************/ -/* C calling conventions export from Win32 dll */ -/*********************************************************/ -/* prototypes */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -int cdecl_CheckINCHIKey(const char *szINCHIKey,const int strict); -int cdecl_GetINCHIKeyFromINCHI(const char* szINCHISource, - const int xtra1,const int xtra2, - char* szINCHIKey, char* szXtra1, char* szXtra2); -int cdecl_GetStdINCHIKeyFromStdINCHI(const char* szINCHISource, char* szINCHIKey); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -/* implementation */ -/* libinchi.def provides export without cdecl_ prefixes */ - -/********************************************************/ -int cdecl_GetStdINCHIKeyFromStdINCHI(const char* szINCHISource, char* szINCHIKey) -{ - return GetStdINCHIKeyFromStdINCHI(szINCHISource, szINCHIKey); -} -/********************************************************/ -int cdecl_CheckINCHIKey(const char *szINCHIKey,const int strict) -{ - return CheckINCHIKey(szINCHIKey); -} -/********************************************************/ -int cdecl_GetINCHIKeyFromINCHI(const char* szINCHISource, - const int xtra1,const int xtra2, - char* szINCHIKey, char* szXtra1, char* szXtra2) -{ - return GetINCHIKeyFromINCHI(szINCHISource, xtra1, xtra2, szINCHIKey, szXtra1, szXtra2); -} -#endif - - - -#if ( defined(__GNUC__) && __GNUC__ >= 3 && defined(__MINGW32__) && defined(_WIN32) ) -#include -/*********************************************************/ -/* Pacal calling conventions export from Win32 dll */ -/*********************************************************/ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif -/* prototypes */ -int PASCAL pasc_CheckINCHIKey(const char *szINCHIKey); -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -/* implementation */ -/* libinchi.def provides export without PASCAL pasc_ prefixes */ -/********************************************************/ -int PASCAL pasc_CheckINCHIKey(const char *szINCHIKey) -{ - return CheckINCHIKey(szINCHIKey); -} -#endif - +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +/* + InChIKey: calculation of hash for InChI string + + Uses truncated SHA-256 function. + SHA-256 implementation: Copyright (C) Brainspark B.V., + see files sha2.c, sha2.h. +*/ + +#ifdef _MSC_VER +#if _MSC_VER > 1000 +#pragma warning( disable : 4996 ) +#endif +#endif + + +#include +#include +#include + +#include "sha2.h" +#include "ikey_base26.h" + +#include "mode.h" +#include "inchi_api.h" +#include "util.h" + +/* Local options */ + +#define INCHIKEY_DEBUG 0 /* 2 */ + +#define INCHIKEY_FLAG_OK 0 +#define INCHIKEY_NOT_VALID_FLAG 1 + +enum { MINOUTLENGTH=256 }; + + + +/* Local functions */ + +void fprint_digest(FILE* fw, const char *header, unsigned char *a); + + +/* + EXPORTED FUNCTIONS +*/ + +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStdINCHIKeyFromStdINCHI(const char* szINCHISource, + char* szINCHIKey) +{ + if ( strlen(szINCHISource) < LEN_INCHI_STRING_PREFIX+3 ) + return INCHIKEY_INVALID_STD_INCHI; + if (szINCHISource[LEN_INCHI_STRING_PREFIX+1]!='S') + return INCHIKEY_INVALID_STD_INCHI; + return GetINCHIKeyFromINCHI(szINCHISource, 0, 0, szINCHIKey, (char *) NULL, (char *) NULL); +} + + +EXPIMP_TEMPLATE INCHI_API +int INCHI_DECL GetINCHIKeyFromINCHI( const char* szINCHISource, + const int xtra1, const int xtra2, + char* szINCHIKey, + char* szXtra1, char* szXtra2) +{ +int ret = INCHIKEY_OK; +int ret1 = INCHIKEY_OK; +int cn; +size_t slen, i, j, jproto=0, ncp, pos_slash1=0; +char *str = NULL, *smajor = NULL, *sminor = NULL, + *sproto=NULL, *stmp = NULL, tmp[MINOUTLENGTH]; +unsigned char + digest_major[32], digest_minor[32]; + +char flagstd = 'S', /* standard key */ + flagnonstd = 'N', /* non-standard key */ + flagexptl = 'B', /* experimental ('beta') key */ + flagver = 'A', /* InChI v. 1 */ + flagproto = 'N'; /* no [de]protonization , by default */ +int nprotons; +/* +Protonization encoding: +N 0 +O +1 P +2 Q +3 R +4 S +5 T +6 U +7 V +8 W +9 X +10 Y +11 Z +12 +M -1 L-2 K -3 J -4 I -5 H -6 G -7 F -8 E -9 D -10 C -11 B -12 +A < -12 or > +12 +*/ +static const char *pplus = "OPQRSTUVWXYZ"; +static const char *pminus = "MLKJIHGFEDCB"; +int is_stdinchi = 0; /* 0 - non-standard, + 1 standard + -1 experimental ('beta') */ + + /* Check if input is a valid InChI string */ + + /* .. non-empty */ + if (szINCHISource==NULL) + return INCHIKEY_EMPTY_INPUT; + + slen = strlen(szINCHISource); + + /* .. has valid prefix */ + if (slen standard InChIKey */ + is_stdinchi = 1; + pos_slash1++; + } + else if (szINCHISource[pos_slash1]=='B') + { + /* v. 1.05 Experimental ('beta') InChI ==> corresponding InChIKey */ + is_stdinchi = -1; + pos_slash1++; + } + + /* .. has trailing slash in the right place */ + if (szINCHISource[pos_slash1]!='/') + return INCHIKEY_INVALID_INCHI_PREFIX; + + /* .. the rest of source string contains at least one a..Z0.9 or slash */ + /* TODO: improve/add full string check */ + if (!isalnum(szINCHISource[pos_slash1+1]) ) + { + int valid_empty_inchi = szINCHISource[pos_slash1+1]=='/'; + int allowed_and_valid_err_inchi = 0; + if ( !valid_empty_inchi ) + { +#ifdef ERR_INCHI_STRING_ALLOWED + allowed_and_valid_err_inchi = szINCHISource[pos_slash1+1]=='?'; +#endif + if ( !allowed_and_valid_err_inchi ) + return INCHIKEY_INVALID_INCHI; + } + } + + /* Ok. Will use a local copy of the source. */ + + extract_inchi_substring(&str, szINCHISource, slen); + if (NULL==str) + { + ret = INCHIKEY_NOT_ENOUGH_MEMORY; + goto fin; + } + slen = strlen(str); + + + /* Make buffers. */ + smajor = (char*) inchi_calloc( slen+1, sizeof(char)); + if (NULL==smajor) + { ret = INCHIKEY_NOT_ENOUGH_MEMORY; goto fin; } + sminor = (char*) inchi_calloc( 2*slen + 2, sizeof(char)); /* we may double the length ... */ + if (NULL==sminor) + { ret = INCHIKEY_NOT_ENOUGH_MEMORY; goto fin; } + stmp = (char*) inchi_calloc( slen+1, sizeof(char)); + if (NULL==stmp) + { ret = INCHIKEY_NOT_ENOUGH_MEMORY; goto fin; } + sproto = (char*) inchi_calloc( slen+1, sizeof(char)); + if (NULL==sproto) + { ret = INCHIKEY_NOT_ENOUGH_MEMORY; goto fin; } + + + szINCHIKey[0] = '\0'; + + + + /* Extract the major block */ + smajor[0] = '\0'; + for (j = pos_slash1 + 1; j < slen-1; j++) + { + if (str[j]=='/') + { + cn = str[j+1]; + switch (cn) + { + /* anything allowed from a major part */ + case 'c': + case 'h': + case 'q': continue; + + /* "/p"; protons now go to to special string, not to minor hash */ + case 'p': + jproto = j; + continue; + + /* "/f", "/r" : may not occur in stdInChI */ + case 'f': + case 'r': + if ( is_stdinchi ) + { + ret = INCHIKEY_INVALID_STD_INCHI; + goto fin; + } + break; + + /* anything allowed from a minor part */ + default: + break; + } + break; + } + } + j++; + if (j==slen) + j++; + else + j--; + + if (jproto) + ncp = jproto - pos_slash1 - 1; + else + ncp = j - pos_slash1 - 1; + + + /* Trim 'InChI=1[S]/' */ + memcpy(smajor,str+pos_slash1+1, ncp*sizeof(str[0])); + smajor[ncp]='\0'; + + + /* Treat protonization */ + if (jproto) + { + /* 2009-01-07 fix bug/typo: assigned incorrect length to the protonation segment of + /* source string ( was sproto[ncp]='\0'; should be sproto[lenproto]='\0'; ) */ + int lenproto = j - (int) jproto; + if (lenproto<3) + { + /* empty "/p", should not occur */ + ret = INCHIKEY_INVALID_INCHI; + goto fin; + } + + memcpy(sproto,str+pos_slash1+ncp+1, lenproto*sizeof(str[0])); + sproto[lenproto]='\0'; + + nprotons = strtol( sproto+2, NULL, 10 ); + + if (nprotons > 0) + { + if (nprotons > 12) flagproto = 'A'; + else flagproto = pplus[nprotons-1]; + } + else if (nprotons < 0) + { + if (nprotons < -12) flagproto = 'A'; + else flagproto = pminus[-nprotons-1]; + } + else + { + /* should never occur */ + ret = INCHIKEY_INVALID_STD_INCHI; + goto fin; + } + } + + + + /* Extract the minor block. */ + + if (j != slen+1) /* check that something exists at right.*/ + { + ncp = slen-j; + memcpy(sminor,str+j, (ncp)*sizeof(str[0])); + sminor[ncp]='\0'; + } + else + sminor[0]='\0'; + + +#if INCHIKEY_DEBUG + ITRACE_("Source: {%-s}\n",str); + ITRACE_("SMajor: {%-s}\n",smajor); + ITRACE_("SMinor: {%-s}\n",sminor); + ITRACE_("SProto: {%-s}\n",sproto); +#endif + + + + /* Compute and compose the InChIKey string. */ + + + /* Major hash sub-string. */ + for( i = 0; i < 32; i++ ) + digest_major[i] = 0; + sha2_csum( (unsigned char *) smajor, (int) strlen(smajor), digest_major ); + + sprintf(tmp,"%-.3s%-.3s%-.3s%-.3s%-.2s", + base26_triplet_1(digest_major), base26_triplet_2(digest_major), + base26_triplet_3(digest_major), base26_triplet_4(digest_major), + base26_dublet_for_bits_56_to_64(digest_major)); + strcat(szINCHIKey, tmp); +#if (INCHIKEY_DEBUG>1) + fprint_digest(stderr, "Major hash, full SHA-256",digest_major); +#endif + + + + /* Minor hash sub-string. */ + for( i = 0; i < 32; i++ ) + digest_minor[i] = 0; + slen = strlen(sminor); + if ((slen>0)&&(slen<255)) + { + strcpy(stmp, sminor); + strcpy(sminor+slen,stmp); + } + sha2_csum( (unsigned char *) sminor, (int) strlen(sminor), digest_minor ); +#if (INCHIKEY_DEBUG>1) + fprint_digest(stderr, "Minor hash, full SHA-256",digest_minor); +#endif + + strcat(szINCHIKey, "-"); + sprintf(tmp,"%-.3s%-.3s%-.2s", + base26_triplet_1(digest_minor), + base26_triplet_2(digest_minor), + base26_dublet_for_bits_28_to_36(digest_minor)); + strcat(szINCHIKey, tmp); + + + /* Append a standard/non-standard flag */ + slen = strlen(szINCHIKey); + if ( is_stdinchi==1 ) + szINCHIKey[slen] = flagstd; + else if ( is_stdinchi==-1 ) + szINCHIKey[slen] = flagexptl; + else + szINCHIKey[slen] = flagnonstd; + + /* Append InChI v.1 flag */ + szINCHIKey[slen+1] = flagver; + + /* Append dash */ + szINCHIKey[slen+2] = '-'; + + /* Append protonization flag */ + szINCHIKey[slen+3] = flagproto; + szINCHIKey[slen+4] = '\0'; + + +#if INCHIKEY_DEBUG + ITRACE_("szINCHIKey: {%-s}\n",szINCHIKey); +#endif + + /* Hash extensions */ + if ( xtra1 && szXtra1 ) + { + get_xtra_hash_major_hex(digest_major, szXtra1); +#if INCHIKEY_DEBUG + fprintf(stderr,"XHash1=%-s\n",szXtra1); + fprintf(stderr,"j=%-d\n",j); +#endif + } + if ( xtra2 && szXtra2 ) + { + get_xtra_hash_minor_hex(digest_minor, szXtra2); +#if INCHIKEY_DEBUG + fprintf(stderr,"XHash2=%-s\n",szXtra2); + fprintf(stderr,"j=%-d\n",j); +#endif + } + + +fin:if (NULL!=str) inchi_free(str); + if (NULL!=smajor) inchi_free(smajor); + if (NULL!=sminor) inchi_free(sminor); + if (NULL!=stmp) inchi_free(stmp); + if (NULL!=sproto) inchi_free(sproto); + + if ( (ret==INCHIKEY_OK) && (ret1!=INCHIKEY_OK) ) + ret = ret1; + return ret; +} + + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Check if the string represents valid InChIKey. +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL CheckINCHIKey(const char *szINCHIKey) +{ +size_t slen, j; + + + slen = strlen(szINCHIKey); + + + /* Proper length is 27 */ + if (slen != 27) + return INCHIKEY_INVALID_LENGTH; + + /* Should have dash in 14-th position */ + if (szINCHIKey[14] !='-') + return INCHIKEY_INVALID_LAYOUT; + /* Should have dash in 25-th position */ + if (szINCHIKey[25] !='-') + return INCHIKEY_INVALID_LAYOUT; + + /* All other should be uppercase */ + for (j = 0; j < 14; j++) + if ( !isbase26(szINCHIKey[j]) ) + return INCHIKEY_INVALID_LAYOUT; /* first block */ + for (j = 15; j < 25; j++) + if ( !isbase26(szINCHIKey[j]) ) + return INCHIKEY_INVALID_LAYOUT; /* second block */ + if ( !isbase26(szINCHIKey[26]) ) + return INCHIKEY_INVALID_LAYOUT; /* (de)protonation flag */ + + + /* No 'E' may appear in 0,3,6,and 9 positions of the 1st block ... */ + for (j=0; j <10; j+=3) + if (szINCHIKey[j]=='E') + return INCHIKEY_INVALID_LAYOUT; + /* ... and 0 and 3 pos. of the second block. */ + for (j=15; j <19; j+=3) + if (szINCHIKey[j]=='E') + return INCHIKEY_INVALID_LAYOUT; + + /* Check for version (only 1 allowed) */ + if (szINCHIKey[24] !='A') + return INCHIKEY_INVALID_VERSION; + + /* Check for standard-ness */ + if (szINCHIKey[23] == 'S') + return INCHIKEY_VALID_STANDARD; + else if (szINCHIKey[23] == 'N') + return INCHIKEY_VALID_NON_STANDARD; + else + return INCHIKEY_INVALID_LAYOUT; +} + + + + + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +void fprint_digest(FILE* fw, const char *header, unsigned char *a) +{ +size_t i, bytelen = 32; + fprintf(fw,"%s\n", header); + for( i = 0; i < bytelen; i++ ) + fprintf(fw,"%02x ", a[i]); + fprintf(fw,"\n" ); +} + + +/********************************************************************/ + +#if ( defined( _WIN32 ) && defined( _MSC_VER ) && _MSC_VER >= 800 && defined(_USRDLL) && defined(BUILD_LINK_AS_DLL) ) + /* Win32 & MS VC ++, compile and link as a DLL */ +/*********************************************************/ +/* C calling conventions export from Win32 dll */ +/*********************************************************/ +/* prototypes */ +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + +int cdecl_CheckINCHIKey(const char *szINCHIKey,const int strict); +int cdecl_GetINCHIKeyFromINCHI(const char* szINCHISource, + const int xtra1,const int xtra2, + char* szINCHIKey, char* szXtra1, char* szXtra2); +int cdecl_GetStdINCHIKeyFromStdINCHI(const char* szINCHISource, char* szINCHIKey); + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + +/* implementation */ +/* libinchi.def provides export without cdecl_ prefixes */ + +/********************************************************/ +int cdecl_GetStdINCHIKeyFromStdINCHI(const char* szINCHISource, char* szINCHIKey) +{ + return GetStdINCHIKeyFromStdINCHI(szINCHISource, szINCHIKey); +} +/********************************************************/ +int cdecl_CheckINCHIKey(const char *szINCHIKey,const int strict) +{ + return CheckINCHIKey(szINCHIKey); +} +/********************************************************/ +int cdecl_GetINCHIKeyFromINCHI(const char* szINCHISource, + const int xtra1,const int xtra2, + char* szINCHIKey, char* szXtra1, char* szXtra2) +{ + return GetINCHIKeyFromINCHI(szINCHISource, xtra1, xtra2, szINCHIKey, szXtra1, szXtra2); +} +#endif + + + +#if ( defined(__GNUC__) && __GNUC__ >= 3 && defined(__MINGW32__) && defined(_WIN32) ) +#include +/*********************************************************/ +/* Pacal calling conventions export from Win32 dll */ +/*********************************************************/ +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif +/* prototypes */ +int PASCAL pasc_CheckINCHIKey(const char *szINCHIKey); +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + +/* implementation */ +/* libinchi.def provides export without PASCAL pasc_ prefixes */ +/********************************************************/ +int PASCAL pasc_CheckINCHIKey(const char *szINCHIKey) +{ + return CheckINCHIKey(szINCHIKey); +} + +#endif diff --git a/INCHI-1-SRC/INCHI_API/inchi_main/inchi_api.h b/INCHI-1-SRC/INCHI_BASE/src/inchi_api.h similarity index 73% rename from INCHI-1-SRC/INCHI_API/inchi_main/inchi_api.h rename to INCHI-1-SRC/INCHI_BASE/src/inchi_api.h index e10a1e2..5a1df65 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_main/inchi_api.h +++ b/INCHI-1-SRC/INCHI_BASE/src/inchi_api.h @@ -1,1349 +1,1428 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INHCH_API_H__ -#define __INHCH_API_H__ - - - - -/*^^^ Post-1.02b fix - thanks to David Foss */ -#ifndef FIND_RING_SYSTEMS -#define FIND_RING_SYSTEMS 1 -#endif -#ifndef FIND_RINS_SYSTEMS_DISTANCES -#define FIND_RINS_SYSTEMS_DISTANCES 0 -#endif - - -/* radical definitions */ -typedef enum tagINCHIRadical { - INCHI_RADICAL_NONE = 0, - INCHI_RADICAL_SINGLET = 1, - INCHI_RADICAL_DOUBLET = 2, - INCHI_RADICAL_TRIPLET = 3 -} inchi_Radical; - -/* bond type definitions */ -typedef enum tagINCHIBondType { - INCHI_BOND_TYPE_NONE = 0, - INCHI_BOND_TYPE_SINGLE = 1, - INCHI_BOND_TYPE_DOUBLE = 2, - INCHI_BOND_TYPE_TRIPLE = 3, - INCHI_BOND_TYPE_ALTERN = 4 /* avoid by all means */ -} inchi_BondType; -/* 2D stereo definitions */ -typedef enum tagINCHIBondStereo2D { - /* stereocenter-related; positive: the sharp end points to this atom */ - INCHI_BOND_STEREO_NONE = 0, - INCHI_BOND_STEREO_SINGLE_1UP = 1, - INCHI_BOND_STEREO_SINGLE_1EITHER = 4, - INCHI_BOND_STEREO_SINGLE_1DOWN = 6, - /* stereocenter-related; negative: the sharp end points to the opposite atom */ - INCHI_BOND_STEREO_SINGLE_2UP = -1, - INCHI_BOND_STEREO_SINGLE_2EITHER = -4, - INCHI_BOND_STEREO_SINGLE_2DOWN = -6, - /* stereobond-related */ - INCHI_BOND_STEREO_DOUBLE_EITHER = 3 /* unknown stereobond geometry */ -} inchi_BondStereo2D; - -/************************************************************************* - * Notes on using INCHI_BOND_STEREO_SINGLE_* from inchi_BondStereo2D * - * * - * These stereo markings are used by InChI to characterize a stereogenic * - * atom if and only if all neighbors of this atom have same z-coordinate * - * as this atom (that is, in case of 2D fragment). * - * The only exception is INCHI_BOND_STEREO_SINGLE_?EITHER marking which * - * always assigns to the atom an "unknown" parity (u). * - * * - * Note the behavior which is default for InChI software v.1.04/03/02std * - * (at -NEWPSOFF option is not supplied) 2D stereo interpretation: * - * only bonds that have sharp end pointing to the stereogenic atom are * - * considered as being out of plane and only sharp ends of * - * INCHI_BOND_STEREO_SINGLE_?EITHER bonds are considered to determine * - * whether the stereochemistry is unknown. * - *************************************************************************/ - -/* sizes definitions */ -#define MAXVAL 20 /* max number of bonds per atom */ -#define ATOM_EL_LEN 6 /* length of ASCIIZ element symbol field */ -#define NUM_H_ISOTOPES 3 /* number of hydrogen isotopes: protium, D, T */ -#define ISOTOPIC_SHIFT_FLAG 10000 /* add to isotopic mass if isotopic_mass = */ - /* (isotopic mass - average atomic mass) */ -#define ISOTOPIC_SHIFT_MAX 100 /* max abs(isotopic mass - average atomic mass) */ - -#ifndef INCHI_US_CHAR_DEF -typedef signed char S_CHAR; -typedef unsigned char U_CHAR; -#define INCHI_US_CHAR_DEF -#endif - -#ifndef INCHI_US_SHORT_DEF -typedef signed short S_SHORT; -typedef unsigned short U_SHORT; -#define INCHI_US_SHORT_DEF -#endif - -typedef S_SHORT AT_NUM; /* atom number; starts from 0 */ - -/************************************************* - * - * - * A T O M S a n d C O N N E C T I V I T Y - * - * - *************************************************/ - -typedef struct tagInchiAtom { - /* atom coordinates */ - double x; - double y; - double z; - /* connectivity */ - AT_NUM neighbor[MAXVAL]; /* adjacency list: ordering numbers of */ - /* the adjacent atoms, >= 0 */ - S_CHAR bond_type[MAXVAL]; /* inchi_BondType */ - /* 2D stereo */ - S_CHAR bond_stereo[MAXVAL]; /* inchi_BondStereo2D; negative if the */ - /* sharp end points to opposite atom */ - /* other atom properties */ - char elname[ATOM_EL_LEN]; /* zero-terminated chemical element name:*/ - /* "H", "Si", etc. */ - AT_NUM num_bonds; /* number of neighbors, bond types and bond*/ - /* stereo in the adjacency list */ - S_CHAR num_iso_H[NUM_H_ISOTOPES+1]; /* implicit hydrogen atoms */ - /* [0]: number of implicit non-isotopic H - (exception: num_iso_H[0]=-1 means INCHI - adds implicit H automatically), - [1]: number of implicit isotopic 1H (protium), - [2]: number of implicit 2H (deuterium), - [3]: number of implicit 3H (tritium) */ - AT_NUM isotopic_mass; /* 0 => non-isotopic; isotopic mass or */ - /* ISOTOPIC_SHIFT_FLAG + mass - (average atomic mass) */ - S_CHAR radical; /* inchi_Radical */ - S_CHAR charge; /* positive or negative; 0 => no charge */ -}inchi_Atom; - -/******************************************************************* - * Notes: 1. Atom ordering numbers (i, k, and atom[i].neighbor[j] below) - * start from zero; max. ordering number is (num_atoms-1). - * 2. inchi_Atom atom[i] is connected to the atom[atom[i].neighbor[j]] - * by a bond that has type atom[i].bond_type[j] and 2D stereo type - * atom[i].bond_stereo[j] (in case of no stereo - * atom[i].bond_stereo[j] = INCHI_BOND_STEREO_NONE) - * Index j is in the range 0 <= j <= (atom[i].num_bonds-1) - * 3. Any connection (represented by atom[i].neighbor[j], - * atom[i].bond_type[j], and atom[i].bond_stereo[j]) - * should be present in one or both adjacency list: - * if k = atom[i].neighbor[j] then i may or may not be present in - * atom[k].neighbor[] list. For example, the adjacency lists may be - * populated with only such neighbors that atom[i].neighbor[j] < i - * All elements of an adjacency list must be different, that is, - * a bond must be specified in an adjacency list only once. - * 4. in Molfiles usually - * (number of implicit H) = Valence - SUM(bond_type[]) - * 5. Seemingly illogical order of the inchi_Atom members was - * chosen in an attempt to avoid alignment problems when - * accessing inchi_Atom from unrelated to C programming - * languages such as Visual Basic. - *******************************************************************/ - -/******************************************************************* - 0D Stereo Parity and Type definitions - ******************************************************************* - Note: - ===== - o Below #A is the ordering number of atom A, starting from 0 - o See parity values corresponding to 'o', 'e', and 'u' in - inchi_StereoParity0D definition below) - - ============================================= - stereogenic bond >A=B< or cumulene >A=C=C=B< - ============================================= - - neighbor[4] : {#X,#A,#B,#Y} in this order - X central_atom : NO_ATOM - \ X Y type : INCHI_StereoType_DoubleBond - A==B \ / - \ A==B - Y - - parity= 'e' parity= 'o' unknown parity = 'u' - - Limitations: - ============ - o Atoms A and B in cumulenes MUST be connected by a chain of double bonds; - atoms A and B in a stereogenic 'double bond' may be connected by a double, - single, or alternating bond. - o One atom may belong to up to 3 stereogenic bonds (i.g. in a fused - aromatic structure). - o Multiple stereogenic bonds incident to any given atom should - either all except possibly one have (possibly different) defined - parities ('o' or 'e') or should all have an unknown parity 'u'. - - Note on parities of alternating stereobonds - =========================================== - D--E - In large rings (see Fig. 1, all // \\ - atoms are C) all alternating bonds B--C F--G - are treated as stereogenic. // \\ - To avoid "undefined" bond parities A H - for bonds BC, DE, FG, HI, JK, LM, AN \ / - it is recommended to mark them with N==M J==I - parities. \ / - L==K Fig. 1 - Such a marking will make - the stereochemical layer unambiguous - and it will be different from the B--C F--G - stereochemical layer of the second // \\ // \\ - structure (Fig. 2). A D--E H - \ / - N==M J==I - By default, double and alternating \ / - bonds in 8-member and greater rings L==K Fig. 2 - are treated by InChI as stereogenic. - - - ============================================= - tetrahedral atom - ============================================= - - 4 neighbors - - X neighbor[4] : {#W, #X, #Y, #Z} - | central_atom: #A - W--A--Y type : INCHI_StereoType_Tetrahedral - | - Z - parity: if (X,Y,Z) are clockwize when seen from W then parity is 'e' otherwise 'o' - Example (see AXYZW above): if W is above the plane XYZ then parity = 'e' - - 3 neighbors - - Y Y neighbor[4] : {#A, #X, #Y, #Z} - / / central_atom: #A - X--A (e.g. O=S ) type : INCHI_StereoType_Tetrahedral - \ \ - Z Z - - parity: if (X,Y,Z) are clockwize when seen from A then parity is 'e', - otherwise 'o' - unknown parity = 'u' - Example (see AXYZ above): if A is above the plane XYZ then parity = 'e' - This approach may be used also in case of an implicit H attached to A. - - ============================================= - allene - ============================================= - - X Y neighbor[4] : {#X,#A,#B,#Y} - \ / central_atom : #C - A=C=B type : INCHI_StereoType_Allene - - Y X - | | - when seen from A along A=C=B: X-A Y-A - - parity: 'e' 'o' - - parity: if A, B, Y are clockwise when seen from X then parity is 'e', - otherwise 'o' - unknown parity = 'u' - Example (see XACBY above): if X on the diagram is above the plane ABY - then parity is 'o' - - Limitations - =========== - o Atoms A and B in allenes MUST be connected by a chain of double bonds; - - ============================================== - Note. Correspondence to CML 0D stereo parities - ============================================== - a list of 4 atoms corresponds to CML atomRefs4 - - tetrahedral atom - ================ - CML atomParity > 0 <=> INCHI_PARITY_EVEN - CML atomParity < 0 <=> INCHI_PARITY_ODD - - | 1 1 1 1 | where xW is x-coordinate of - | xW xX xY xZ | atom W, etc. (xyz is a - CML atomParity = determinant | yW yX yY yZ | 'right-handed' Cartesian - | zW zX xY zZ | coordinate system) - - allene (not yet defined in CML) - =============================== - the parity corresponds to the sign of the following determinant - in exactly same way as for tetrahedral atoms: - - | 1 1 1 1 | where bonds and neighbor[4] array are - | xX xA xB xY | same as defined above for allenes - | yX yA yB yY | Obviously, the parity is same for - | zX zA xB zY | {#X,#A,#B,#Y} and {#Y,#B,#A,#X} - because of the even number of column permutations. - - stereogenic double bond and (not yet defined in CML) cumulenes - ============================================================== - CML 'C' (cis) <=> INCHI_PARITY_ODD - CML 'T' (trans) <=> INCHI_PARITY_EVEN - - - How InChI uses 0D parities - ========================== - - 1. 0D parities are used if all atom coordinates are zeroes. - - In addition to that: - - 2. 0D parities are used for Stereobonds, Allenes, or Cumulenes if: - - 2a. A bond to the end-atom is shorter than MIN_BOND_LEN=0.000001 - 2b. A ratio of two bond lengths to the end-atom is smaller than MIN_SINE=0.03 - 2c. In case of a linear fragment X-A=B end-atom A is treated as satisfying 2a-b - - 0D parities are used if 2a or 2b or 2c applies to one or both end-atoms. - - 3. 0D parities are used for Tetrahedral Atoms if at least one of 3a-c is true: - - 3a. One of bonds to the central atom is shorter than MIN_BOND_LEN=0.000001 - 3b. A ratio of two bond lengths to the central atom is smaller than MIN_SINE=0.03 - 3c. The four neighbors are almost in one plane or the central atom and - its only 3 explicit neighbors are almost in one plane - - Notes on 0D parities and 'undefined' stereogenic elements - ========================================================= - - If 0D parity is to be used according to 1-3 but CH3 CH3 - has not been provided then the corresponding \ / - stereogenic element is considered 'undefined'. C=CH - / - For example, if in the structure (Fig. 3) H - the explicit H has been moved so that it Fig. 3 - has same coordinates as atom >C= (that is, - the length of the bond H-C became zero) - then the double bond is assigned 'undefined' CH3 CH3 - parity which by default is omitted from the \ / - Identifier. CH=CH - - However, the structure on Fig. 4 will have double Fig. 4 - bond parity 'o' and its parity in the Identifier is (-). - - Notes on 0D parities in structures containing metals - ==================================================== - Since InChI disconnects bonds to metals the 0D parities upon the - disconnection may change in several different ways: - - 1) previously non-stereogenic bond may become stereogenic: - - \ / \ / - CH==CH disconnection CH==CH - \ / ======> - M M - - before the disconnection: after the disconnection: - atoms C have valence=5 and the double bond may become - the double bond is not stereogenic - recognized as stereogenic - - 2) previously stereogenic bond may become non-stereogenic: - - M M(+) - \ / / - N==C disconnection (-)N==C - \ ======> \ - - 3) Oddball structures, usually resulting from projecting 3D - structures on the plane, may contain fragment like that - depicted on Fig. 5: - - M A M A - |\ / B / B - | X / disconnection / / - |/ \ / ======> / / - C===C C===C - Fig. 5 - (X stands for bond intersection) - - A-C=C-B parity is A-C=C-B parity is - trans (e) cis (o) or undefined - because the bond because C valence = 3, - orientation is same not 4. - as on Fig, 6 below: - - A M - \ / Removal of M from the structure - C===C on Fig. 5 changes the geometry from trans - / \ to cis. - M' B Removal of M and M' from the structure - Fig. 6 on Fig. 6 does not change the A-C=C-B - geometry: it is trans. - - To resolve the problem InChI API accepts the second parity - corresponding to the metal-disconnected structure. - To store both bond parities use left shift by 3 bits: - - inchi_Stereo0D::parity = ParityOfConnected | (ParityOfDisconnected<<3) - - In case when only disconnected structure parity exists set - ParityOfConnected = INCHI_PARITY_UNDEFINED. - This is the only case when INCHI_PARITY_UNDEFINED parity - may be fed to the InChI. - - In cases when the bond parity in a disconnected structure exists and - differs from the parity in the connected structure the atoms A and B - should be non-metals. - -****************************************************************************/ - -#define NO_ATOM (-1) /* non-existent (central) atom */ - -/* 0D parity types */ -typedef enum tagINCHIStereoType0D { - INCHI_StereoType_None = 0, - INCHI_StereoType_DoubleBond = 1, - INCHI_StereoType_Tetrahedral = 2, - INCHI_StereoType_Allene = 3 -} inchi_StereoType0D; - -/* 0D parities */ -typedef enum tagINCHIStereoParity0D { - INCHI_PARITY_NONE = 0, - INCHI_PARITY_ODD = 1, /* 'o' */ - INCHI_PARITY_EVEN = 2, /* 'e' */ - INCHI_PARITY_UNKNOWN = 3, /* 'u' */ /* (see also readinch.c) - used in: Extract0DParities, INChITo_Atom */ - INCHI_PARITY_UNDEFINED = 4 /* '?' -- should not be used; however, see Note above */ -} inchi_StereoParity0D; - - -/************************************************* - * - * - * 0D - S T E R E O (if no coordinates given) - * - * - *************************************************/ - - -typedef struct tagINCHIStereo0D { - AT_NUM neighbor[4]; /* 4 atoms always */ - AT_NUM central_atom; /* central tetrahedral atom or a central */ - /* atom of allene; otherwise NO_ATOM */ - S_CHAR type; /* inchi_StereoType0D */ - S_CHAR parity; /* inchi_StereoParity0D: may be a combination of two parities: */ - /* ParityOfConnected | (ParityOfDisconnected << 3), see Note above */ -}inchi_Stereo0D; - - - - -/************************************************* - * - * - * I N C h I D L L I n p u t - * - * - *************************************************/ - - -/* Structure -> InChI, GetINCHI() / GetStdINCHI() */ -typedef struct tagINCHI_Input { - /* the caller is responsible for the data allocation and deallocation */ - inchi_Atom *atom; /* array of num_atoms elements */ - inchi_Stereo0D *stereo0D; /* array of num_stereo0D 0D stereo elements or NULL */ - char *szOptions; /* InChI options: space-delimited; each is preceded by */ - /* '/' or '-' depending on OS and compiler */ - AT_NUM num_atoms; /* number of atoms in the structure < 1024 */ - AT_NUM num_stereo0D; /* number of 0D stereo elements */ -}inchi_Input; - -/* InChI -> Structure, GetStructFromINCHI()/GetStructFromStdINCHI() */ -typedef struct tagINCHI_InputINCHI { - /* the caller is responsible for the data allocation and deallocation */ - char *szInChI; /* InChI ASCIIZ string to be converted to a strucure */ - char *szOptions; /* InChI options: space-delimited; each is preceded by */ - /* '/' or '-' depending on OS and compiler */ -} inchi_InputINCHI; - - -/************************************************* - * - * - * I N C h I D L L O u t p u t - * - * - *************************************************/ - -/* Structure -> InChI */ -typedef struct tagINCHI_Output { - /* zero-terminated C-strings allocated by GetStdINCHI() */ - /* to deallocate all of them call FreeStdINCHI() (see below) */ - char *szInChI; /* InChI ASCIIZ string */ - char *szAuxInfo; /* Aux info ASCIIZ string */ - char *szMessage; /* Error/warning ASCIIZ message */ - char *szLog; /* log-file ASCIIZ string, contains a human-readable list */ - /* of recognized options and possibly an Error/warning message */ -} inchi_Output; - -/* InChI -> Structure */ -typedef struct tagINCHI_OutputStruct { - /* 4 pointers are allocated by GetStructFromINCHI()/GetStructFromStdINCHI() */ - /* to deallocate all of them call FreeStructFromStdINCHI()/FreeStructFromStdINCHI() */ - inchi_Atom *atom; /* array of num_atoms elements */ - inchi_Stereo0D *stereo0D; /* array of num_stereo0D 0D stereo elements or NULL */ - AT_NUM num_atoms; /* number of atoms in the structure < 1024 */ - AT_NUM num_stereo0D; /* number of 0D stereo elements */ - char *szMessage; /* Error/warning ASCIIZ message */ - char *szLog; /* log-file ASCIIZ string, contains a human-readable list */ - /* of recognized options and possibly an Error/warning message */ - unsigned long WarningFlags[2][2]; /* warnings, see INCHIDIFF in inchicmp.h */ - /* [x][y]: x=0 => Reconnected if present in InChI otherwise Disconnected/Normal - x=1 => Disconnected layer if Reconnected layer is present - y=1 => Main layer or Mobile-H - y=0 => Fixed-H layer - */ -}inchi_OutputStruct; - - - - -/************************************************* - * - * - * I N C h I D L L I n t e r f a c e - * - * - *************************************************/ - -#if (defined( _WIN32 ) && defined( _MSC_VER ) && defined(BUILD_LINK_AS_DLL) ) - /* Win32 & MS VC ++, compile and link as a DLL */ - #ifdef _USRDLL - /* InChI library dll */ - #define INCHI_API __declspec(dllexport) - #define EXPIMP_TEMPLATE - #define INCHI_DECL __stdcall - #else - /* calling the InChI dll program */ - #define INCHI_API __declspec(dllimport) - #define EXPIMP_TEMPLATE extern - #define INCHI_DECL __stdcall - #endif -#else - /* create a statically linked InChI library or link to an executable */ - #define INCHI_API - #define EXPIMP_TEMPLATE - #define INCHI_DECL -#endif - -/*^^^ Return codes for - GetINCHI - GetStdINCHI - Get_inchi_Input_FromAuxInfo - Get_std_inchi_Input_FromAuxInfo - GetStructFromINCHI - GetStructFromStdINCHI -*/ -typedef enum tagRetValGetINCHI { - inchi_Ret_SKIP = -2, /* not used in InChI library */ - inchi_Ret_EOF = -1, /* no structural data has been provided */ - inchi_Ret_OKAY = 0, /* Success; no errors or warnings */ - inchi_Ret_WARNING = 1, /* Success; warning(s) issued */ - inchi_Ret_ERROR = 2, /* Error: no InChI has been created */ - inchi_Ret_FATAL = 3, /* Severe error: no InChI has been created (typically, memory allocation failure) */ - inchi_Ret_UNKNOWN = 4, /* Unknown program error */ - inchi_Ret_BUSY = 5 /* Previuos call to InChI has not returned yet */ - -} RetValGetINCHI; - -/*^^^ Return codes for CheckINCHI */ -typedef enum tagRetValCheckINCHI -{ - INCHI_VALID_STANDARD = 0, - INCHI_VALID_NON_STANDARD = -1, - INCHI_INVALID_PREFIX = 1, - INCHI_INVALID_VERSION = 2, - INCHI_INVALID_LAYOUT = 3, - INCHI_FAIL_I2I = 4 - -} RetValCheckINCHI; - - - -/* to compile all InChI code as a C++ code #define COMPILE_ALL_CPP */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - -/*^^^ InChI PREFIX */ -#define INCHI_STRING_PREFIX "InChI=" -#define LEN_INCHI_STRING_PREFIX 6 - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Format: - - Standard InChI starts with: InChI=1S/ - Non-standard one with: InChI=1/ - Empty std InChI: InChI=1S// - Empty InChI: InChI=1// - AuxInfo=1// - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - - - -/* EXPORTED FUNCTIONS */ - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -GetINCHI / GetStdINCHI - - - inchi_Input is created by the user; strings in inchi_Output are allocated and deallocated by InChI - inchi_Output does not need to be initilized out to zeroes; see FreeNCHI()/FreeSTDINCHI() on how to deallocate it - - - Valid options for GetINCHI: - (use - instead of / for O.S. other than MS Windows) - - Structure perception (compatible with stdInChI) - /NEWPSOFF /DoNotAddH /SNon - Stereo interpretation (lead to generation of non-standard InChI) - /SRel /SRac /SUCF /ChiralFlagON /ChiralFlagOFF - InChI creation options (lead to generation of non-standard InChI) - /SUU /SLUUD /FixedH /RecMet /KET /15T - - - GetINCHI produces standard InChI if no InChI creation/stereo modification options - are specified. Inveresely, if any of SUU/SLUUD/RecMet/FixedH/Ket/15T/SRel/SRac/SUCF - options are specified, generated InChI will be non-standard one. - - - GetStdINCHI produces standard InChI only. - The valid structure perception options are: - /NEWPSOFF /DoNotAddH /SNon - - - Other options are: - /AuxNone Omit auxiliary information (default: Include) - /Wnumber Set time-out per structure in seconds; W0 means unlimited - In InChI library the default value is unlimited - /OutputSDF Output SDfile instead of InChI - /WarnOnEmptyStructure - Warn and produce empty InChI for empty structure - /SaveOpt Save custom InChI creation options (non-standard InChI) - - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetINCHI( inchi_Input *inp, inchi_Output *out ); -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStdINCHI( inchi_Input *inp, inchi_Output *out ); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -FreeINCHI / FreeStdINCHI - - should be called to deallocate char* pointers - obtained from each GetINCHI /GetStdINCHI call - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeINCHI ( inchi_Output *out ); -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeStdINCHI ( inchi_Output *out ); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -GetStringLength - - helper: get string length - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStringLength( char *p ); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -GetStructFromINCHI / GetStructFromStdINCHI - - inchi_Inputinchi_InputINCHI is created by the user; pointers in inchi_OutputStruct are allocated and deallocated by InChI - inchi_OutputStruct does not need to be initilized out to zeroes; see FreeStructFromStdINCHI() on how to deallocate it - Option /Inchi2Struct is not needed for GetStructFromINCHI()/GetStructFromStdINCHI() - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStructFromINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ); -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStructFromStdINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -FreeStructFromINCHI / FreeStructFromStdINCHI - - should be called to deallocate pointers obtained from each - GetStructFromStdINCHI / GetStructFromINCHI - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeStructFromINCHI( inchi_OutputStruct *out ); -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeStructFromStdINCHI( inchi_OutputStruct *out ); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -GetINCHIfromINCHI - - GetINCHIfromINCHI does same as -InChI2InChI option: converts InChI into InChI for validation purposes - It may also be used to filter out specific layers. For instance, /Snon would remove stereochemical layer - Omitting /FixedH and/or /RecMet would remove Fixed-H or Reconnected layers - To keep all InChI layers use options string "/FixedH /RecMet"; option /InChI2InChI is not needed - inchi_InputINCHI is created by the user; strings in inchi_Output are allocated and deallocated by InChI - inchi_Output does not need to be initilized out to zeroes; see FreeINCHI() on how to deallocate it - - Note: there is no explicit tool to conversion from/to standard InChI - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetINCHIfromINCHI( inchi_InputINCHI *inpInChI, inchi_Output *out ); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -/***************************************************************** - * - * - * C o n v e r s i o n: InChI AuxInfo string => inchi_Input - * - * - *****************************************************************/ - -#ifndef STR_ERR_LEN -#define STR_ERR_LEN 256 -#endif - -typedef struct tagInchiInpData { - inchi_Input *pInp; /* a pointer to pInp that has all items 0 or NULL */ - int bChiral; /* 1 => the structure was marked as chiral, 2=> not chiral, 0=> not marked */ - char szErrMsg[STR_ERR_LEN]; -} InchiInpData; - -/* to compile all InChI code as a C++ code #define COMPILE_ALL_CPP */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Get_inchi_Input_FromAuxInfo / Get_std_inchi_Input_FromAuxInfo - -Input: - szInchiAuxInfo: contains ASCIIZ string of InChI output for a single - structure or only the AuxInfo line - bDoNotAddH: if 0 then InChI will be allowed to add implicit H - bDiffUnkUndfStereo - if not 0, use different labels for unknown and undefined stereo - pInchiInp: should have a valid pointer pInchiInp->pInp to an empty - (all members = 0) inchi_Input structure - -Output: - pInchiInp: The following members of pInp may be filled during the call: - atom, num_atoms, stereo0D, num_stereo0D - Return value: see RetValGetINCHI - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL Get_inchi_Input_FromAuxInfo( - char *szInchiAuxInfo, - int bDoNotAddH, - int bDiffUnkUndfStereo, - InchiInpData *pInchiInp ); -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL Get_std_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, - int bDoNotAddH, - InchiInpData *pInchiInp ); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Free_inchi_Input / Free_std_inchi_Input - - To deallocate and write zeroes into the changed members of pInchiInp->pInp call - Free_std_inchi_Input( inchi_Input *pInp ) - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL Free_inchi_Input( inchi_Input *pInp ); -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL Free_std_inchi_Input( inchi_Input *pInp ); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -CheckINCHI - -Check if the string represents valid InChI/standard InChI. -Input: - szINCHI source InChI - strict if 0, just briefly check for proper layout (prefix, version, etc.) - The result may not be strict. - If not 0, try to perform InChI2InChI conversion and - returns success if a resulting InChI string exactly match source. - The result may be 'false alarm' due to imperfectness of conversion. -Returns: - success/errors codes - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL CheckINCHI(const char *szINCHI, const int strict); - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - InChIKey API - - - InChIKey description - - - - The InChIKey is a character signature based on a hash code of the InChI string. - Standard InChIKey is produced out of standard InChI. - Non-standard InChIKey is produced out of non-standard InChI. - - AAAAAAAAAAAAAA-BBBBBBBBCD-P - - - InChIKey layout is as follows: - - AAAAAAAAAAAAAA - First block (14 letters) - Encodes molecular skeleton (connectivity) - - BBBBBBBB - Second block (8 letters) - Encodes tautomers, stereochemistry, isotopomers, reconnected layer - C - 'S' for standard - 'N' for non-standard - D - InChI version ('A' for 1) - P - (de)protonation flag - Protonization encoding: - N 0 - O +1 P +2 Q +3 R +4 S +5 T +6 U +7 V +8 W +9 X +10 Y +11 Z +12 - M -1 L-2 K -3 J -4 I -5 H -6 G -7 F -8 E -9 D -10 C -11 B -12 - A < -12 or > +12 - - - All symbols except delimiter (dash, that is, minus) are uppercase English - letters representing a "base 26" encoding. - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - -/*^^^ Return codes for key generation procedure */ -#define INCHIKEY_OK 0 -#define INCHIKEY_UNKNOWN_ERROR 1 -#define INCHIKEY_EMPTY_INPUT 2 -#define INCHIKEY_INVALID_INCHI_PREFIX 3 -#define INCHIKEY_NOT_ENOUGH_MEMORY 4 -#define INCHIKEY_INVALID_INCHI 20 -#define INCHIKEY_INVALID_STD_INCHI 21 - - -/*^^^ Return codes for CheckINCHIKey */ -typedef enum tagRetValGetINCHIKey -{ - INCHIKEY_VALID_STANDARD = 0, - INCHIKEY_VALID_NON_STANDARD = -1, - INCHIKEY_INVALID_LENGTH = 1, - INCHIKEY_INVALID_LAYOUT = 2, - INCHIKEY_INVALID_VERSION = 3 -} RetValCheckINCHIKeyv; - - - -/* EXPORTED FUNCTIONS */ - - - -/* To compile all InChI code as a C++ code #define COMPILE_ALL_CPP */ - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -GetINCHIKeyFromINCHI - -Calculate InChIKey by InChI string. - -Input: - szINCHISource - source InChI string - xtra1 - =1 calculate hash extension (up to 256 bits; 1st block) - xtra2 - =1 calculate hash extension (up to 256 bits; 2nd block) - -Output: - szINCHIKey - InChIKey string - The user-supplied buffer szINCHIKey should be at least 28 bytes long. - szXtra1 - hash extension (up to 256 bits; 1st block) string - Caller should allocate space for 64 characters + trailing NULL - szXtra2 - hash extension (up to 256 bits; 2nd block) string - Caller should allocate space for 64 characters + trailing NULL - -Returns: - success/errors codes - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetINCHIKeyFromINCHI(const char* szINCHISource, - const int xtra1, - const int xtra2, - char* szINCHIKey, - char* szXtra1, - char* szXtra2); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -GetStdINCHIKeyFromStdINCHI - - "Standard" counterpart - - For compatibility with v. 1.02std, no extra hash calculation is allowed. - To calculate extra hash(es), use GetINCHIKeyFromINCHI with stdInChI as input. - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStdINCHIKeyFromStdINCHI(const char* szINCHISource, - char* szINCHIKey); - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -CheckINCHIKey - -Check if the string represents valid InChIKey. -Input: - szINCHIKey - source InChIKey string -Returns: - success/errors codes - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL CheckINCHIKey(const char *szINCHIKey); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - Modularized InChI generation API - - - - Note. Functions with STDINCHIGEN prefix are - retained for compatibility with v. 1.02std - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - - - -/*^^^ Data structures holding intermediate (normalization) results */ - -#ifndef MAX_NUM_STEREO_ATOM_NEIGH -#define MAX_NUM_STEREO_ATOM_NEIGH 4 -#endif -#ifndef MAX_NUM_STEREO_BONDS -#define MAX_NUM_STEREO_BONDS 3 -#endif -#ifndef INCHI_NUM -#define INCHI_NUM 2 /* = array size; member indexes: */ -#endif - - -typedef unsigned short AT_NUMBR; -typedef signed short NUM_HS; -typedef unsigned long INCHI_MODES; - -typedef struct tagNormAtom -{ - char elname[ATOM_EL_LEN]; /* chem. element name */ - U_CHAR el_number; /* number of the element in the Periodic Table */ - AT_NUMBR neighbor[MAXVAL]; /* positions (from 0) of the neighbors in the NORM_ATOM array */ - AT_NUMBR orig_at_number; /* original atom number, starts from 1 */ - AT_NUMBR orig_compt_at_numb; /* atom number within a component before terminal H removal */ - S_CHAR bond_stereo[MAXVAL]; /* 1=Up,4=Either,6=Down (this atom is at the pointing wedge) - negative => on the opposite side of the wedge; 3=Either double bond */ - U_CHAR bond_type[MAXVAL]; /* 1=single, 2=double, 3=triple, 4=1/2 (bond order is 1 or 2) */ - /* 5=1/2/3, 6=1/3, 7=2/3, 8=tautomeric, 9=1/2 non-stereogenic */ - - S_CHAR valence; /* number of bonds = number of neighbors not greater than MAXVAL */ - S_CHAR chem_bonds_valence; /* sum of bond types (1,2,3); type 4 needs special treatment */ - S_CHAR num_H; /* number of adjacent implicit hydrogen atoms including D and T */ - S_CHAR num_iso_H[NUM_H_ISOTOPES];/* number of adjacent implicit 1H(protium), 2H(D), 3H(T) < 16 */ - S_CHAR iso_atw_diff; /* =0 => natural isotopic abundances */ - /* >0 => (isotopic mass) - (rounded average atomic mass) + 1 */ - /* <0 => (isotopic mass) - (rounded average atomic mass) */ - S_CHAR charge; /* charge */ - S_CHAR radical; /* RADICAL_SINGLET, RADICAL_DOUBLET, or RADICAL_TRIPLET */ - S_CHAR bAmbiguousStereo; /* flag of detected stereo ambiguity */ - S_CHAR cFlags; /* AT_FLAG_ISO_H_POINT: atom may have exchangeable isotopic H */ - AT_NUMBR at_type; /* ATT_NONE, ATT_ACIDIC, etc. See InChI normalization code */ - AT_NUMBR component; /* number of the structure component > 0 */ - AT_NUMBR endpoint; /* id of a tautomeric group */ - AT_NUMBR c_point; /* id of a positive charge group */ - double x; /* x coordinate */ - double y; /* y coordinate */ - double z; /* x coordinate */ - /*--------- 0D parities ----------*/ - S_CHAR bUsed0DParity; /* bit=1 => stereobond; bit=2 => stereocenter */ - /*----- tetrahedral stereo parity */ - S_CHAR p_parity; /* tetrahedral (sp3) cml parity */ - AT_NUMBR p_orig_at_num[MAX_NUM_STEREO_ATOM_NEIGH]; /* orig_at_number of each neighbor > 0; 0=> no neighbor */ - /*----- stereo bond (SB) parities */ - S_CHAR sb_ord[MAX_NUM_STEREO_BONDS]; /* neighbor[] index of another end of this SB, starts from 0 */ - S_CHAR sn_ord[MAX_NUM_STEREO_BONDS]; /* neighbor[] index of a bond that is not this SB; starts from 0; - -1 means the neighbor is a removed explicit H */ - /* atoms on both ends of a stereobond have same parity => trans/T/E/2, diff. parities => cis/C/Z/1 */ - S_CHAR sb_parity[MAX_NUM_STEREO_BONDS]; /* parities of stereobonds (sp2) incident to this atom */ - AT_NUMBR sn_orig_at_num[MAX_NUM_STEREO_BONDS]; /* orig_at_number of sn_ord[] neighbor > 0 */ - -#if ( FIND_RING_SYSTEMS == 1 ) - S_CHAR bCutVertex; /* is the atom a cut-vertex or not */ - AT_NUMBR nRingSystem; /* starts from 1; number of a ring system */ - AT_NUMBR nNumAtInRingSystem; /* number of atoms in a ring system to which this at belongs */ - AT_NUMBR nBlockSystem; /* ambiguous if the atom is a cut-vertex: better apply this to bonds */ - -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) - AT_NUMBR nDistanceFromTerminal; /* not used */ -#endif - -#endif -} NORM_ATOM; - - - -typedef struct tagNormAtomData -{ - NORM_ATOM *at; /* atom list */ - NORM_ATOM *at_fixed_bonds; /* atom list with added or removed protons only */ - int num_at; /* number of atoms except removed terminal H */ - int num_removed_H; /* number of removed H; at[] has (num_at+num_removed_H) elements */ - int num_bonds; - int num_isotopic; /* number of isotopic atoms */ - int bExists; /* for internal use */ - int bDeleted; /* for internal use */ - int bHasIsotopicLayer; - int bTautomeric; - int bTautPreprocessed; /* for internal use */ - int nNumRemovedProtons; - NUM_HS nNumRemovedProtonsIsotopic[NUM_H_ISOTOPES]; - /* isotopic composition of removed protons, not included in num_iso_H[] */ - NUM_HS num_iso_H[NUM_H_ISOTOPES]; - /* isotopic H on tautomeric atoms and those - in nIsotopicEndpointAtomNumber */ - INCHI_MODES bTautFlags; /* for internal use */ - INCHI_MODES bTautFlagsDone; /* for internal use */ - INCHI_MODES bNormalizationFlags;/* for internal use */ -} NORM_ATOMS; - - -typedef struct tagINCHIGEN_DATA -{ - - char pStrErrStruct[STR_ERR_LEN]; /* intermediate log (warning/error report) */ - int num_components[INCHI_NUM]; /* number of allocated INChI, INChI_Aux data structures */ - /* index=0 => disconnected, 1 => reconnected structure */ - - /*^^^ The results of normalization stage */ - /*^^^ for each member of pair disconnected/reconnected structures: */ - NORM_ATOMS *NormAtomsNontaut[INCHI_NUM]; - NORM_ATOMS *NormAtomsTaut[INCHI_NUM]; - -} INCHIGEN_DATA; - - -/*^^^ InChI Generator Handle */ - -typedef void* INCHIGEN_HANDLE; - - - - -/* EXPORTED FUNCTIONS */ - - - -/* to compile all InChI code as a C++ code #define COMPILE_ALL_CPP */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -INCHIGEN_Create / STDINCHIGEN_Create - - InChI Generator: create generator - Returns handle of generator object or NULL on failure - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API -INCHIGEN_HANDLE INCHI_DECL INCHIGEN_Create(void); -EXPIMP_TEMPLATE INCHI_API -INCHIGEN_HANDLE INCHI_DECL STDINCHIGEN_Create(void); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -INCHIGEN_Setup / STDINCHIGEN_Setup - - InChI Generator: setup - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL INCHIGEN_Setup(INCHIGEN_HANDLE HGen, - INCHIGEN_DATA * pGenData, - inchi_Input * pInp); -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL STDINCHIGEN_Setup(INCHIGEN_HANDLE HGen, - INCHIGEN_DATA * pGenData, - inchi_Input * pInp); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -INCHIGEN_DoNormalization / STDINCHIGEN_DoNormalization - - InChI Generator: structure normalization stage - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL INCHIGEN_DoNormalization(INCHIGEN_HANDLE HGen, - INCHIGEN_DATA * pGenData); -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL STDINCHIGEN_DoNormalization(INCHIGEN_HANDLE HGen, - INCHIGEN_DATA * pGenData); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -INCHIGEN_DoCanonicalization / STDINCHIGEN_DoCanonicalization - - InChI Generator: structure canonicalization stage - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL INCHIGEN_DoCanonicalization - (INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData); -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL STDINCHIGEN_DoCanonicalization - (INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -INCHIGEN_DoSerialization / STDINCHIGEN_DoSerialization - - InChI Generator: InChI serialization stage - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL INCHIGEN_DoSerialization(INCHIGEN_HANDLE HGen, - INCHIGEN_DATA * pGenData, - inchi_Output * pResults); -EXPIMP_TEMPLATE INCHI_API int INCHI_DECL STDINCHIGEN_DoSerialization(INCHIGEN_HANDLE HGen, - INCHIGEN_DATA * pGenData, - inchi_Output * pResults); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -INCHIGEN_DoSerialization / STDINCHIGEN_DoSerialization - - InChI Generator: reset stage (use before get next structure) - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API -void INCHI_DECL INCHIGEN_Reset(INCHIGEN_HANDLE HGen, - INCHIGEN_DATA * pGenData, - inchi_Output * pResults); -EXPIMP_TEMPLATE INCHI_API -void INCHI_DECL STDINCHIGEN_Reset(INCHIGEN_HANDLE HGen, - INCHIGEN_DATA * pGenData, - inchi_Output * pResults); - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -INCHIGEN_DoSerialization / STDINCHIGEN_DoSerialization - - InChI Generator: destroy generator - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL INCHIGEN_Destroy(INCHIGEN_HANDLE HGen); -EXPIMP_TEMPLATE INCHI_API void INCHI_DECL STDINCHIGEN_Destroy(INCHIGEN_HANDLE HGen); - - - - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Prototypes for C calling conventions: - - int GetINCHI( inchi_Input *inp, inchi_Output *out ); - int GetStdINCHI( inchi_Input *inp, inchi_Output *out ); - void FreeINCHI( inchi_Output *out ); - void FreeStdINCHI( inchi_Output *out ); - int GetStringLength( char *p ); - int Get_inchi_Input_FromAuxInfo - ( char *szInchiAuxInfo, int bDoNotAddH, int bDiffUnkUndfStereo, InchiInpData *pInchiInp ); - int Get_std_inchi_Input_FromAuxInfo - ( char *szInchiAuxInfo, int bDoNotAddH, int bDiffUnkUndfStereo,InchiInpData *pInchiInp ); - void Free_inchi_Input( inchi_Input *pInp ); - void Free_std_inchi_Input( inchi_Input *pInp ); - int GetStructFromINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ); - int GetStructFromStdINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ); - void FreeStructFromStdINCHI( inchi_OutputStruct *out ); - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Win32 Dumpbin export information - - ordinal hint RVA name -cdecl - 1 0 000B7EAB CheckINCHI - 2 1 000B7221 CheckINCHIKey - 3 2 000B7C62 FreeINCHI - 4 3 000B7B04 FreeStdINCHI - 5 4 000B72B7 FreeStructFromINCHI - 6 5 000B7BC2 FreeStructFromStdINCHI - 7 6 000B7E33 Free_inchi_Input - 8 7 000B7C58 Free_std_inchi_Input - 9 8 000B727B GetINCHI - 10 9 000B75B4 GetINCHIKeyFromINCHI - 11 A 000B757D GetINCHIfromINCHI - 12 B 000B8211 GetStdINCHI - 13 C 000B7F0A GetStdINCHIKeyFromStdINCHI - 14 D 000B77CB GetStringLength - 15 E 000B7CA3 GetStructFromINCHI - 16 F 000B778A GetStructFromStdINCHI - 17 10 000B7DAC Get_inchi_Input_FromAuxInfo - 18 11 000B7D6B Get_std_inchi_Input_FromAuxInfo - 19 12 000B7D6B INCHIGEN_Create - 20 13 000B7F2D INCHIGEN_Destroy - 21 14 000B7F23 INCHIGEN_DoCanonicalization - 22 15 000B7F23 INCHIGEN_DoNormalization - 23 16 000B714A INCHIGEN_DoSerialization - 24 17 000B7FCD INCHIGEN_Reset - 25 18 000B7FCD INCHIGEN_Setup - 26 19 000B7EA6 STDINCHIGEN_Create - 27 1A 000B7EA6 STDINCHIGEN_Destroy - 28 1B 000B711D STDINCHIGEN_DoCanonicalization - 29 1C 000B7073 STDINCHIGEN_DoNormalization - 30 1D 000B7FC3 STDINCHIGEN_DoSerialization - 31 1E 000B7668 STDINCHIGEN_Reset - 32 1F 000B7438 STDINCHIGEN_Setup -__stdcall or PASCAL - 33 20 000B7DFC _CheckINCHI@8 - 34 21 000B7802 _CheckINCHIKey@4 - 35 22 000B7F73 _FreeINCHI@4 - 36 23 000B7F82 _FreeStdINCHI@4 - 37 24 000B75E1 _FreeStructFromINCHI@4 - 38 25 000B7B81 _FreeStructFromStdINCHI@4 - 39 26 000B7B86 _Free_inchi_Input@4 - 40 27 000B7A96 _Free_std_inchi_Input@4 - 41 28 000B7B5E _GetINCHI@8 - 42 29 000B7285 _GetINCHIKeyFromINCHI@24 - 43 2A 000B758C _GetINCHIfromINCHI@8 - 44 2B 000B7CDA _GetStdINCHI@8 - 45 2C 000B7979 _GetStdINCHIKeyFromStdINCHI@8 - 46 2D 000B7BA4 _GetStringLength@4 - 47 2E 000B70A5 _GetStructFromINCHI@8 - 48 2F 000B79B0 _GetStructFromStdINCHI@8 - 49 30 000B8022 _Get_inchi_Input_FromAuxInfo@16 - 50 31 000B76E0 _Get_std_inchi_Input_FromAuxInfo@12 - 51 32 000B7230 _INCHIGEN_Create@0 - 52 33 000B760E _INCHIGEN_Destroy@4 - 53 34 000B7087 _INCHIGEN_DoCanonicalization@8 - 54 35 000B70B4 _INCHIGEN_DoNormalization@8 - 55 36 000B72D5 _INCHIGEN_DoSerialization@12 - 56 37 000B7FE1 _INCHIGEN_Reset@12 - 57 38 000B7163 _INCHIGEN_Setup@12 - 58 39 000B7159 _STDINCHIGEN_Create@0 - 59 3A 000B78A7 _STDINCHIGEN_Destroy@4 - 60 3B 000B72F3 _STDINCHIGEN_DoCanonicalization@8 - 61 3C 000B737A _STDINCHIGEN_DoNormalization@8 - 62 3D 000B7B72 _STDINCHIGEN_DoSerialization@12 - 63 3E 000B7654 _STDINCHIGEN_Reset@12 - 64 3F 000B75FF _STDINCHIGEN_Setup@12 - - - Note. Currently there is no callback function for aborting, progress, etc. - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - -#endif /* __INHCH_API_H__ */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _INHCH_API_H_ +#define _INHCH_API_H_ + + +#ifndef FIND_RING_SYSTEMS +#define FIND_RING_SYSTEMS 1 +#endif +#ifndef FIND_RINS_SYSTEMS_DISTANCES +#define FIND_RINS_SYSTEMS_DISTANCES 0 +#endif + + +#ifndef FIX_DOCANON_RETCODE_RESET_BUG +#define FIX_DOCANON_RETCODE_RESET_BUG 1 +#endif + +/* radical definitions */ +typedef enum tagINCHIRadical { + INCHI_RADICAL_NONE = 0, + INCHI_RADICAL_SINGLET = 1, + INCHI_RADICAL_DOUBLET = 2, + INCHI_RADICAL_TRIPLET = 3 +} inchi_Radical; + +/* bond type definitions */ +typedef enum tagINCHIBondType { + INCHI_BOND_TYPE_NONE = 0, + INCHI_BOND_TYPE_SINGLE = 1, + INCHI_BOND_TYPE_DOUBLE = 2, + INCHI_BOND_TYPE_TRIPLE = 3, + INCHI_BOND_TYPE_ALTERN = 4 /* avoid by all means */ +} inchi_BondType; +typedef enum tagINCHIBondStereo2D { + /* stereocenter-related; positive: the sharp end points to this atom */ + INCHI_BOND_STEREO_NONE = 0, + INCHI_BOND_STEREO_SINGLE_1UP = 1, + INCHI_BOND_STEREO_SINGLE_1EITHER = 4, + INCHI_BOND_STEREO_SINGLE_1DOWN = 6, + /* stereocenter-related; negative: the sharp end points to the opposite atom */ + INCHI_BOND_STEREO_SINGLE_2UP = -1, + INCHI_BOND_STEREO_SINGLE_2EITHER = -4, + INCHI_BOND_STEREO_SINGLE_2DOWN = -6, + /* stereobond-related */ + INCHI_BOND_STEREO_DOUBLE_EITHER = 3 /* unknown stereobond geometry */ +} inchi_BondStereo2D; +/************************************************************************* + * Notes on using INCHI_BOND_STEREO_SINGLE_* from inchi_BondStereo2D * + * * + * These stereo markings are used by InChI to characterize a stereogenic * + * atom if and only if all neighbors of this atom have same z-coordinate * + * as this atom (that is, in case of 2D fragment). * + * The only exception is INCHI_BOND_STEREO_SINGLE_?EITHER marking which * + * always assigns to the atom an "unknown" parity (u). * + * * + * Note the behavior which is default for InChI software v.1.04/03/02std * + * (at -NEWPSOFF option is not supplied) 2D stereo interpretation: * + * only bonds that have sharp end pointing to the stereogenic atom are * + * considered as being out of plane and only sharp ends of * + * INCHI_BOND_STEREO_SINGLE_?EITHER bonds are considered to determine * + * whether the stereochemistry is unknown. * + *************************************************************************/ + +/* sizes definitions */ +#define MAXVAL 20 /* max number of bonds per atom */ +#define ATOM_EL_LEN 6 /* length of ASCIIZ element symbol field */ +#define NUM_H_ISOTOPES 3 /* number of hydrogen isotopes: protium, D, T */ +#define ISOTOPIC_SHIFT_FLAG 10000 /* add to isotopic mass if isotopic_mass = */ + /* (isotopic mass - average atomic mass) */ +#define ISOTOPIC_SHIFT_MAX 100 /* max abs(isotopic mass - average atomic mass) */ + +#ifndef INCHI_US_CHAR_DEF +typedef signed char S_CHAR; +typedef unsigned char U_CHAR; +#define INCHI_US_CHAR_DEF +#endif + +#ifndef INCHI_US_SHORT_DEF +typedef signed short S_SHORT; +typedef unsigned short U_SHORT; +#define INCHI_US_SHORT_DEF +#endif + +typedef S_SHORT AT_NUM; /* atom number; starts from 0 */ + +/************************************************* + * + * + * A T O M S a n d C O N N E C T I V I T Y + * + * + *************************************************/ + +typedef struct tagInchiAtom { + /* atom coordinates */ + double x; + double y; + double z; + /* connectivity */ + AT_NUM neighbor[MAXVAL]; /* adjacency list: ordering numbers of */ + /* the adjacent atoms, >= 0 */ + S_CHAR bond_type[MAXVAL]; /* inchi_BondType */ + /* 2D stereo */ + S_CHAR bond_stereo[MAXVAL]; /* inchi_BondStereo2D; negative if the */ + /* sharp end points to opposite atom */ + /* other atom properties */ + char elname[ATOM_EL_LEN]; /* zero-terminated chemical element name:*/ + /* "H", "Si", etc. */ + AT_NUM num_bonds; /* number of neighbors, bond types and bond*/ + /* stereo in the adjacency list */ + S_CHAR num_iso_H[NUM_H_ISOTOPES+1]; /* implicit hydrogen atoms */ + /* [0]: number of implicit non-isotopic H + (exception: num_iso_H[0]=-1 means INCHI + adds implicit H automatically), + [1]: number of implicit isotopic 1H (protium), + [2]: number of implicit 2H (deuterium), + [3]: number of implicit 3H (tritium) */ + AT_NUM isotopic_mass; /* 0 => non-isotopic; isotopic mass or */ + /* ISOTOPIC_SHIFT_FLAG + mass - (average atomic mass) */ + S_CHAR radical; /* inchi_Radical */ + S_CHAR charge; /* positive or negative; 0 => no charge */ +}inchi_Atom; + +/******************************************************************* + * Notes: 1. Atom ordering numbers (i, k, and atom[i].neighbor[j] below) + * start from zero; max. ordering number is (num_atoms-1). + * 2. inchi_Atom atom[i] is connected to the atom[atom[i].neighbor[j]] + * by a bond that has type atom[i].bond_type[j] and 2D stereo type + * atom[i].bond_stereo[j] (in case of no stereo + * atom[i].bond_stereo[j] = INCHI_BOND_STEREO_NONE) + * Index j is in the range 0 <= j <= (atom[i].num_bonds-1) + * 3. Any connection (represented by atom[i].neighbor[j], + * atom[i].bond_type[j], and atom[i].bond_stereo[j]) + * should be present in one or both adjacency list: + * if k = atom[i].neighbor[j] then i may or may not be present in + * atom[k].neighbor[] list. For example, the adjacency lists may be + * populated with only such neighbors that atom[i].neighbor[j] < i + * All elements of an adjacency list must be different, that is, + * a bond must be specified in an adjacency list only once. + * 4. in Molfiles usually + * (number of implicit H) = Valence - SUM(bond_type[]) + * 5. Seemingly illogical order of the inchi_Atom members was + * chosen in an attempt to avoid alignment problems when + * accessing inchi_Atom from unrelated to C programming + * languages such as Visual Basic. + *******************************************************************/ + +/******************************************************************* + 0D Stereo Parity and Type definitions + ******************************************************************* + Note: + ===== + o Below #A is the ordering number of atom A, starting from 0 + o See parity values corresponding to 'o', 'e', and 'u' in + inchi_StereoParity0D definition below) + + ============================================= + stereogenic bond >A=B< or cumulene >A=C=C=B< + ============================================= + + neighbor[4] : {#X,#A,#B,#Y} in this order + X central_atom : NO_ATOM + \ X Y type : INCHI_StereoType_DoubleBond + A==B \ / + \ A==B + Y + + parity= 'e' parity= 'o' unknown parity = 'u' + + Limitations: + ============ + o Atoms A and B in cumulenes MUST be connected by a chain of double bonds; + atoms A and B in a stereogenic 'double bond' may be connected by a double, + single, or alternating bond. + o One atom may belong to up to 3 stereogenic bonds (i.g. in a fused + aromatic structure). + o Multiple stereogenic bonds incident to any given atom should + either all except possibly one have (possibly different) defined + parities ('o' or 'e') or should all have an unknown parity 'u'. + + Note on parities of alternating stereobonds + =========================================== + D--E + In large rings (see Fig. 1, all // \\ + atoms are C) all alternating bonds B--C F--G + are treated as stereogenic. // \\ + To avoid "undefined" bond parities A H + for bonds BC, DE, FG, HI, JK, LM, AN \ / + it is recommended to mark them with N==M J==I + parities. \ / + L==K Fig. 1 + Such a marking will make + the stereochemical layer unambiguous + and it will be different from the B--C F--G + stereochemical layer of the second // \\ // \\ + structure (Fig. 2). A D--E H + \ / + N==M J==I + By default, double and alternating \ / + bonds in 8-member and greater rings L==K Fig. 2 + are treated by InChI as stereogenic. + + + ============================================= + tetrahedral atom + ============================================= + + 4 neighbors + + X neighbor[4] : {#W, #X, #Y, #Z} + | central_atom: #A + W--A--Y type : INCHI_StereoType_Tetrahedral + | + Z + parity: if (X,Y,Z) are clockwize when seen from W then parity is 'e' otherwise 'o' + Example (see AXYZW above): if W is above the plane XYZ then parity = 'e' + + 3 neighbors + + Y Y neighbor[4] : {#A, #X, #Y, #Z} + / / central_atom: #A + X--A (e.g. O=S ) type : INCHI_StereoType_Tetrahedral + \ \ + Z Z + + parity: if (X,Y,Z) are clockwize when seen from A then parity is 'e', + otherwise 'o' + unknown parity = 'u' + Example (see AXYZ above): if A is above the plane XYZ then parity = 'e' + This approach may be used also in case of an implicit H attached to A. + + ============================================= + allene + ============================================= + + X Y neighbor[4] : {#X,#A,#B,#Y} + \ / central_atom : #C + A=C=B type : INCHI_StereoType_Allene + + Y X + | | + when seen from A along A=C=B: X-A Y-A + + parity: 'e' 'o' + + parity: if A, B, Y are clockwise when seen from X then parity is 'e', + otherwise 'o' + unknown parity = 'u' + Example (see XACBY above): if X on the diagram is above the plane ABY + then parity is 'o' + + Limitations + =========== + o Atoms A and B in allenes MUST be connected by a chain of double bonds; + + + How InChI uses 0D parities + ========================== + + 1. 0D parities are used if all atom coordinates are zeroes. + + In addition to that: + + 2. 0D parities are used for Stereobonds, Allenes, or Cumulenes if: + + 2a. A bond to the end-atom is shorter than MIN_BOND_LEN=0.000001 + 2b. A ratio of two bond lengths to the end-atom is smaller than MIN_SINE=0.03 + 2c. In case of a linear fragment X-A=B end-atom A is treated as satisfying 2a-b + + 0D parities are used if 2a or 2b or 2c applies to one or both end-atoms. + + 3. 0D parities are used for Tetrahedral Atoms if at least one of 3a-c is true: + + 3a. One of bonds to the central atom is shorter than MIN_BOND_LEN=0.000001 + 3b. A ratio of two bond lengths to the central atom is smaller than MIN_SINE=0.03 + 3c. The four neighbors are almost in one plane or the central atom and + its only 3 explicit neighbors are almost in one plane + + Notes on 0D parities and 'undefined' stereogenic elements + ========================================================= + + If 0D parity is to be used according to 1-3 but CH3 CH3 + has not been provided then the corresponding \ / + stereogenic element is considered 'undefined'. C=CH + / + For example, if in the structure (Fig. 3) H + the explicit H has been moved so that it Fig. 3 + has same coordinates as atom >C= (that is, + the length of the bond H-C became zero) + then the double bond is assigned 'undefined' CH3 CH3 + parity which by default is omitted from the \ / + Identifier. CH=CH + + However, the structure on Fig. 4 will have double Fig. 4 + bond parity 'o' and its parity in the Identifier is (-). + + Notes on 0D parities in structures containing metals + ==================================================== + Since InChI disconnects bonds to metals the 0D parities upon the + disconnection may change in several different ways: + + 1) previously non-stereogenic bond may become stereogenic: + + \ / \ / + CH==CH disconnection CH==CH + \ / ======> + M M + + before the disconnection: after the disconnection: + atoms C have valence=5 and the double bond may become + the double bond is not stereogenic + recognized as stereogenic + + 2) previously stereogenic bond may become non-stereogenic: + + M M(+) + \ / / + N==C disconnection (-)N==C + \ ======> \ + + 3) Oddball structures, usually resulting from projecting 3D + structures on the plane, may contain fragment like that + depicted on Fig. 5: + + M A M A + |\ / B / B + | X / disconnection / / + |/ \ / ======> / / + C===C C===C + Fig. 5 + (X stands for bond intersection) + + A-C=C-B parity is A-C=C-B parity is + trans (e) cis (o) or undefined + because the bond because C valence = 3, + orientation is same not 4. + as on Fig, 6 below: + + A M + \ / Removal of M from the structure + C===C on Fig. 5 changes the geometry from trans + / \ to cis. + M' B Removal of M and M' from the structure + Fig. 6 on Fig. 6 does not change the A-C=C-B + geometry: it is trans. + + To resolve the problem InChI API accepts the second parity + corresponding to the metal-disconnected structure. + To store both bond parities use left shift by 3 bits: + + inchi_Stereo0D::parity = ParityOfConnected | (ParityOfDisconnected<<3) + + In case when only disconnected structure parity exists set + ParityOfConnected = INCHI_PARITY_UNDEFINED. + This is the only case when INCHI_PARITY_UNDEFINED parity + may be fed to the InChI. + + In cases when the bond parity in a disconnected structure exists and + differs from the parity in the connected structure the atoms A and B + should be non-metals. + +****************************************************************************/ + +#define NO_ATOM (-1) /* non-existent (central) atom */ + +/* 0D parity types */ +typedef enum tagINCHIStereoType0D { + INCHI_StereoType_None = 0, + INCHI_StereoType_DoubleBond = 1, + INCHI_StereoType_Tetrahedral = 2, + INCHI_StereoType_Allene = 3 +} inchi_StereoType0D; + +/* 0D parities */ +typedef enum tagINCHIStereoParity0D { + INCHI_PARITY_NONE = 0, + INCHI_PARITY_ODD = 1, /* 'o' */ + INCHI_PARITY_EVEN = 2, /* 'e' */ + INCHI_PARITY_UNKNOWN = 3, /* 'u' */ /* (see also readinch.c) + used in: Extract0DParities, InchiToAtom */ + INCHI_PARITY_UNDEFINED = 4 /* '?' -- should not be used; however, see Note above */ +} inchi_StereoParity0D; + + +/************************************************* + * + * + * 0D - S T E R E O (if no coordinates given) + * + * + *************************************************/ + + +typedef struct tagINCHIStereo0D { + AT_NUM neighbor[4]; /* 4 atoms always */ + AT_NUM central_atom; /* central tetrahedral atom or a central */ + /* atom of allene; otherwise NO_ATOM */ + S_CHAR type; /* inchi_StereoType0D */ + S_CHAR parity; /* inchi_StereoParity0D: may be a combination of two parities: */ + /* ParityOfConnected | (ParityOfDisconnected << 3), see Note above */ +}inchi_Stereo0D; + + + + +/************************************************* + * + * + * I N C h I D L L I n p u t + * + * + *************************************************/ + + +/* + Structure -> InChI + + GetINCHI() + GetStdINCHI() + GetINCHIEx() + +*/ + + +typedef struct tagINCHI_Input { + /* the caller is responsible for the data allocation and deallocation */ + inchi_Atom *atom; /* array of num_atoms elements */ + inchi_Stereo0D *stereo0D; /* array of num_stereo0D 0D stereo elements or NULL */ + char *szOptions; /* InChI options: space-delimited; each is preceded by */ + /* '/' or '-' depending on OS and compiler */ + AT_NUM num_atoms; /* number of atoms in the structure < MAX_ATOMS */ + AT_NUM num_stereo0D; /* number of 0D stereo elements */ +}inchi_Input; + + +/* + Extended input supporting v. 1.05 extensions: V3000; polymers + + Mainly follows Accelrys CTFile cpecification. + + See: + CTFile Formats. Accelrys, December 2011. + http://accelrys.com/products/collaborative-science/biovia-draw/ctfile-no-fee.html + + Note that V3000 extensions are supported onlyprovisionally: the data are read but not used + +*/ + + +/* Polymers */ + +typedef struct inchi_Input_PolymerUnit +{ + int id; /* Unit id; it is what is called 'Sgroup number' */ + /* in CTFile (not used, kept for compatibility) */ + int type; /* Unit type as per CTFile format (STY) */ + int subtype; /* Unit subtype as per CTFile format (SST) */ + int conn; /* Unit connection scheme as per CTFile format (SCN) */ + int label; /* One more unit id; what is called 'unique Sgroup */ + /* identifier' in CTFile (not used, for compatibility) */ + int na; /* Number of atoms in the unit */ + int nb; /* Number of bonds in the unit */ + double xbr1[4]; /* Bracket ends coordinates (SDI) */ + double xbr2[4]; /* Bracket ends coordinates (SDI) */ + char smt[80]; /* Sgroup Subscript (SMT) ('n' or so ) */ + int *alist; /* List of atoms in the unit (SAL), atomic numbers */ + int *blist; /* List of crossing bonds of unit: */ + /* [bond1end1, bond1end2, bond2end1, bond2end2] */ +} inchi_Input_PolymerUnit; + + +typedef struct inchi_Input_Polymer +{ + /* List of pointers to polymer units */ + inchi_Input_PolymerUnit **units; + int n; /* Number of polymer units */ +} inchi_Input_Polymer; + + +/* + V3000 Extensions + + Note that V3000 extensions are supported only + provisionally, the data are read but not used +*/ +typedef struct inchi_Input_V3000 +{ + int n_non_star_atoms; + int n_star_atoms; + int *atom_index_orig; /* Index as supplied for atoms */ + int *atom_index_fin; /* = index or -1 for star atom */ + int n_sgroups; /* Not used. */ + int n_3d_constraints; /* Not used. */ + int n_collections; + int n_non_haptic_bonds; + int n_haptic_bonds; + int **lists_haptic_bonds;/* Haptic_bonds[i] is pointer to int */ + /* array which contains: */ + /* bond_type, non-star atom number, */ + /* nendpts, then endpts themselves */ + /* Enhanced stereo collections */ + int n_steabs; + int **lists_steabs; /* steabs[k][0] - not used */ + /* steabs[k][1] - number of members in collection */ + /* steabs[k][2..] - member atom numbers */ + int n_sterel; + int **lists_sterel; /* sterel[k][0] - n from "STERELn" tag */ + /* sterel[k][1] - number of members in collection */ + /* sterel[k][2..] - member atom numbers */ + int n_sterac; + int **lists_sterac; /* sterac[k][0] - n from "STERACn" tag */ + /* sterac[k][1] - number of members in collection */ + /* sterac[k][0] - number from "STERACn" tag */ +} inchi_Input_V3000; + + +/* Input data structure for GetINCHIEx() */ + + +typedef struct inchi_InputEx +{ + /* the caller is responsible for the data allocation and deallocation */ + + /* same as in older inchi_Input */ + inchi_Atom *atom; /* array of num_atoms elements */ + /* same as in older inchi_Input */ + inchi_Stereo0D *stereo0D; /* array of num_stereo0D 0D stereo elements or NULL */ + /* same as in older inchi_Input */ + char *szOptions; /* InChI options: space-delimited; each is preceded by */ + /* '/' or '-' depending on OS and compiler */ + /* same as in older inchi_Input */ + AT_NUM num_atoms; /* number of atoms in the structure */ + /* same as in older inchi_Input */ + AT_NUM num_stereo0D; /* number of 0D stereo elements */ + /* v. 1.05 extension */ + inchi_Input_Polymer *polymer; /* v. 1.05 extended data, polymers */ + /* NULL if not a polymer */ + /* v. 1.05 extension */ + inchi_Input_V3000 *v3000; /* v. 1.05 extended data, V3000 Molfile features */ + /* NULL if no V3000 extensions present */ +} inchi_InputEx; + + +/* + InChI -> Structure + + GetStructFromINCHI() + GetStructFromStdINCHI() + GetStructFromINCHIEx() +*/ +typedef struct tagINCHI_InputINCHI { + /* the caller is responsible for the data allocation and deallocation */ + char *szInChI; /* InChI ASCIIZ string to be converted to a strucure */ + char *szOptions; /* InChI options: space-delimited; each is preceded by */ + /* '/' or '-' depending on OS and compiler */ +} inchi_InputINCHI; + + +typedef inchi_Input_PolymerUnit inchi_Output_PolymerUnit; /* InChI v. 1.05 extension */ +typedef inchi_Input_Polymer inchi_Output_Polymer; /* InChI v. 1.05 extension */ +typedef inchi_Input_V3000 inchi_Output_V3000; /* InChI v. 1.05 extension */ + + + + +/************************************************* + * + * + * I N C h I D L L O u t p u t + * + * + *************************************************/ + + + +/* + Structure -> InChI +*/ + + +typedef struct tagINCHI_Output +{ + /* zero-terminated C-strings allocated by GetStdINCHI() */ + /* to deallocate all of them call FreeStdINCHI() (see below) */ + char *szInChI; /* InChI ASCIIZ string */ + char *szAuxInfo; /* Aux info ASCIIZ string */ + char *szMessage; /* Error/warning ASCIIZ message */ + char *szLog; /* log-file ASCIIZ string, contains a human-readable list */ + /* of recognized options and possibly an Error/warning message */ +} inchi_Output; + + +/* InChI -> Structure */ + +typedef struct tagINCHI_OutputStruct +{ + + /* Pointers are allocated by GetStructFromINCHI()/GetStructFromStdINCHI() */ + /* to deallocate all of them call FreeStructFromStdINCHI()/FreeStructFromStdINCHI() */ + + inchi_Atom *atom; /* array of num_atoms elements */ + inchi_Stereo0D *stereo0D; /* array of num_stereo0D 0D stereo elements or NULL */ + AT_NUM num_atoms; /* number of atoms in the structure */ + AT_NUM num_stereo0D; /* number of 0D stereo elements */ + char *szMessage; /* Error/warning ASCIIZ message */ + char *szLog; /* log-file ASCIIZ string, contains a human-readable list */ + /* of recognized options and possibly an Error/warn message */ + unsigned long WarningFlags[2][2]; /* warnings, see INCHIDIFF in inchicmp.h */ + /* [x][y]: + x=0 => Reconnected if present in InChI + otherwise Disconnected/Normal + x=1 => Disconnected layer if Reconnected layer is present + y=1 => Main layer or Mobile-H + y=0 => Fixed-H layer */ +}inchi_OutputStruct; + + + +typedef struct tagINCHI_OutputStructEx +{ + + /* Pointers are allocated by GetStructFromINCHIEx() */ + /* to deallocate all of them call FreeStructFromNCHIEx() */ + + inchi_Atom *atom; /* array of num_atoms elements */ + inchi_Stereo0D *stereo0D; /* array of num_stereo0D 0D stereo elements or NULL */ + AT_NUM num_atoms; /* number of atoms in the structure */ + AT_NUM num_stereo0D; /* number of 0D stereo elements */ + char *szMessage; /* Error/warning ASCIIZ message */ + char *szLog; /* log-file ASCIIZ string, contains a human-readable list */ + /* of recognized options and possibly an Error/Warn message */ + unsigned long WarningFlags[2][2]; /* warnings, see INCHIDIFF in inchicmp.h */ + /* [x][y]: x=0 => Reconnected if present in InChI + otherwise Disconnected/Normal + x=1 => Disconnected layer if Reconnected layer is present + y=1 => Main layer or Mobile-H + y=0 => Fixed-H layer */ + inchi_Output_Polymer *polymer; /* v. 1.05 extended data, polymers */ + inchi_Output_V3000 *v3000; /* v. 1.05 extended data, V3000 Molfile features */ +} inchi_OutputStructEx; + +void FreeInChIExtInput( inchi_Input_Polymer *polymer, inchi_Input_V3000 *v3000 ); + + + +/************************************************* + * + * + * I N C h I D L L I n t e r f a c e + * + * + *************************************************/ + + +#if (defined( _WIN32 ) && defined( _MSC_VER ) && defined(BUILD_LINK_AS_DLL) ) + /* Win32 & MS VC ++, compile and link as a DLL */ + #ifdef _USRDLL + /* InChI library dll */ + #define INCHI_API __declspec(dllexport) + #define EXPIMP_TEMPLATE + #define INCHI_DECL + #else + /* calling the InChI dll program */ + #define INCHI_API __declspec(dllimport) + #define EXPIMP_TEMPLATE extern + #define INCHI_DECL + #endif +#else + /* create a statically linked InChI library or link to an executable */ + #define INCHI_API + #define EXPIMP_TEMPLATE + #define INCHI_DECL +#endif + +/* Return codes for + GetINCHI + GetStdINCHI + GetINCHIEx + Get_inchi_Input_FromAuxInfo + Get_std_inchi_Input_FromAuxInfo + GetStructFromINCHI + GetStructFromStdINCHI + GetStructFromINCHIEx +*/ + +typedef enum tagRetValGetINCHI +{ + inchi_Ret_BREAK = -100, + inchi_Ret_SKIP = -2, /* not used in InChI library */ + inchi_Ret_EOF = -1, /* no structural data has been provided */ + inchi_Ret_OKAY = 0, /* Success; no errors or warnings */ + inchi_Ret_WARNING = 1, /* Success; warning(s) issued */ + inchi_Ret_ERROR = 2, /* Error: no InChI has been created */ + inchi_Ret_FATAL = 3, /* Severe error: no InChI has been created (typically, memory allocation failure) */ + inchi_Ret_UNKNOWN = 4, /* Unknown program error */ + inchi_Ret_BUSY = 5 /* Previuos call to InChI has not returned yet */ +} RetValGetINCHI; + +/* Return codes for + MakeINCHIFromMolfileText +*/ +typedef enum tagRetValMOL2INCHI +{ + mol2inchi_Ret_OKAY = 0, /* Success; no errors or warnings */ + mol2inchi_Ret_WARNING = 1, /* Success; warning(s) issued */ + mol2inchi_Ret_ERROR = 2, /* generic Error: no InChI has been created */ + mol2inchi_Ret_ERROR_get = 4, /* get structure Error: no InChI has been created */ + mol2inchi_Ret_ERROR_comp = 5 /* compute InChI Error: no InChI has been created */ +} RetValMol2INCHI; + + + +/* Return codes for CheckINCHI */ +typedef enum tagRetValCheckINCHI +{ + INCHI_VALID_STANDARD = 0, + INCHI_VALID_NON_STANDARD = 1, + INCHI_VALID_BETA = 2, + INCHI_INVALID_PREFIX = 3, + INCHI_INVALID_VERSION = 4, + INCHI_INVALID_LAYOUT = 5, + INCHI_FAIL_I2I = 6 +} RetValCheckINCHI; + + + +/* to compile all InChI code as a C++ code #define COMPILE_ALL_CPP */ +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + + +/* InChI PREFIX */ +#define INCHI_STRING_PREFIX "InChI=" +#define LEN_INCHI_STRING_PREFIX 6 + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Format: + + Standard InChI starts with: InChI=1S/ + Non-standard one with: InChI=1/ + Empty std InChI: InChI=1S// + Empty InChI: InChI=1// + AuxInfo=1// + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ + + + + +/* EXPORTED FUNCTIONS */ + + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +GetINCHI / GetStdINCHI + + + inchi_Input is created by the user; strings in inchi_Output are allocated and deallocated by InChI + inchi_Output does not need to be initilized out to zeroes; see FreeNCHI()/FreeSTDINCHI() on how to deallocate it + + + Valid options for GetINCHI: + (use - instead of / for O.S. other than MS Windows) + + Structure perception (compatible with stdInChI) + /NEWPSOFF /DoNotAddH /SNon + Stereo interpretation (lead to generation of non-standard InChI) + /SRel /SRac /SUCF /ChiralFlagON /ChiralFlagOFF + InChI creation options (lead to generation of non-standard InChI) + /SUU /SLUUD /FixedH /RecMet /KET /15T + + + GetINCHI produces standard InChI if no InChI creation/stereo modification options + are specified. Inveresely, if any of SUU/SLUUD/RecMet/FixedH/Ket/15T/SRel/SRac/SUCF + options are specified, generated InChI will be non-standard one. + + + GetStdINCHI produces standard InChI only. + The valid structure perception options are: + /NEWPSOFF /DoNotAddH /SNon + + + Other options are: + /AuxNone Omit auxiliary information (default: Include) + /Wnumber Set time-out per structure in seconds; W0 means unlimited + In InChI library the default value is unlimited + /OutputSDF Output SDfile instead of InChI + /WarnOnEmptyStructure + Warn and produce empty InChI for empty structure + /SaveOpt Save custom InChI creation options (non-standard InChI) + + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetINCHI( inchi_Input *inp, inchi_Output *out ); +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStdINCHI( inchi_Input *inp, inchi_Output *out ); + + +/* Extended version of GetINCHI supporting v. 1.05 extensions: V3000; polymers */ +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetINCHIEx( inchi_InputEx *inp, inchi_Output *out ); + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +FreeINCHI / FreeStdINCHI + + should be called to deallocate char* pointers + obtained from each GetINCHI /GetStdINCHI call + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeINCHI ( inchi_Output *out ); +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeStdINCHI ( inchi_Output *out ); + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +GetStringLength + + helper: get string length + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStringLength( char *p ); + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +GetStructFromINCHI / GetStructFromStdINCHI / GetStructFromINCHIEx + + inchi_Inputinchi_InputINCHI is created by the user; pointers in inchi_OutputStruct are allocated and deallocated by InChI + inchi_OutputStruct does not need to be initialized out to zeroes; see FreeStructFromStdINCHI() on how to deallocate it + Option /Inchi2Struct is not needed for GetStructFromINCHI()/GetStructFromStdINCHI() + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStructFromINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ); +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStructFromStdINCHI( inchi_InputINCHI *inpInChI, inchi_OutputStruct *outStruct ); +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStructFromINCHIEx( inchi_InputINCHI *inpInChI, inchi_OutputStructEx *outStruct ); + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +FreeStructFromINCHI / FreeStructFromStdINCHI / FreeStructFromINCHIEx + + should be called to deallocate pointers obtained from each + GetStructFromStdINCHI / GetStructFromINCHI / GetStructFromINCHIEx + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeStructFromINCHI( inchi_OutputStruct *out ); +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeStructFromStdINCHI( inchi_OutputStruct *out ); +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL FreeStructFromINCHIEx( inchi_OutputStructEx *out ); + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +GetINCHIfromINCHI + + GetINCHIfromINCHI does same as -InChI2InChI option: converts InChI into InChI for validation purposes + It may also be used to filter out specific layers. For instance, /Snon would remove stereochemical layer + Omitting /FixedH and/or /RecMet would remove Fixed-H or Reconnected layers + To keep all InChI layers use options string "/FixedH /RecMet"; option /InChI2InChI is not needed + inchi_InputINCHI is created by the user; strings in inchi_Output are allocated and deallocated by InChI + inchi_Output does not need to be initilized out to zeroes; see FreeINCHI() on how to deallocate it + + Note: there is no explicit tool to conversion from/to standard InChI + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetINCHIfromINCHI( inchi_InputINCHI *inpInChI, inchi_Output *out ); + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + + +/***************************************************************** + * + * + * C o n v e r s i o n: InChI AuxInfo string => inchi_Input + * + * + *****************************************************************/ + +#ifndef STR_ERR_LEN +#define STR_ERR_LEN 256 +#endif + +typedef struct tagInchiInpData { + inchi_Input *pInp; /* a pointer to pInp that has all items 0 or NULL */ + int bChiral; /* 1 => the structure was marked as chiral, 2=> not chiral, 0=> not marked */ + char szErrMsg[STR_ERR_LEN]; +} InchiInpData; + +/* to compile all InChI code as a C++ code #define COMPILE_ALL_CPP */ +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Get_inchi_Input_FromAuxInfo / Get_std_inchi_Input_FromAuxInfo + +Input: + szInchiAuxInfo: contains ASCIIZ string of InChI output for a single + structure or only the AuxInfo line + bDoNotAddH: if 0 then InChI will be allowed to add implicit H + bDiffUnkUndfStereo + if not 0, use different labels for unknown and undefined stereo + pInchiInp: should have a valid pointer pInchiInp->pInp to an empty + (all members = 0) inchi_Input structure + +Output: + pInchiInp: The following members of pInp may be filled during the call: + atom, num_atoms, stereo0D, num_stereo0D + Return value: see RetValGetINCHI + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL Get_inchi_Input_FromAuxInfo( + char *szInchiAuxInfo, + int bDoNotAddH, + int bDiffUnkUndfStereo, + InchiInpData *pInchiInp ); +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL Get_std_inchi_Input_FromAuxInfo( char *szInchiAuxInfo, + int bDoNotAddH, + InchiInpData *pInchiInp ); + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Free_inchi_Input / Free_std_inchi_Input + + To deallocate and write zeroes into the changed members of pInchiInp->pInp call + Free_inchi_Input( inchi_Input *pInp ) + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL Free_inchi_Input( inchi_Input *pInp ); +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL Free_std_inchi_Input( inchi_Input *pInp ); + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +CheckINCHI + +Check if the string represents valid InChI/standard InChI. +Input: + szINCHI source InChI + strict if 0, just briefly check for proper layout (prefix, version, etc.) + The result may not be strict. + If not 0, try to perform InChI2InChI conversion and + returns success if a resulting InChI string exactly match source. + The result may be 'false alarm' due to imperfectness of conversion. +Returns: + success/errors codes + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL CheckINCHI(const char *szINCHI, const int strict); + + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + InChIKey API + + + InChIKey description + + + + The InChIKey is a character signature based on a hash code of the InChI string. + Standard InChIKey is produced out of standard InChI. + Non-standard InChIKey is produced out of non-standard InChI. + + AAAAAAAAAAAAAA-BBBBBBBBCD-P + + + InChIKey layout is as follows: + + AAAAAAAAAAAAAA + First block (14 letters) + Encodes molecular skeleton (connectivity) + + BBBBBBBB + Second block (8 letters) + Encodes tautomers, stereochemistry, isotopomers, reconnected layer + C + 'S' for standard + 'N' for non-standard + D + InChI version ('A' for 1) + P - (de)protonation flag + Protonization encoding: + N 0 + O +1 P +2 Q +3 R +4 S +5 T +6 U +7 V +8 W +9 X +10 Y +11 Z +12 + M -1 L-2 K -3 J -4 I -5 H -6 G -7 F -8 E -9 D -10 C -11 B -12 + A < -12 or > +12 + + + All symbols except delimiter (dash, that is, minus) are uppercase English + letters representing a "base 26" encoding. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ + + +/* Return codes for key generation procedure */ +#define INCHIKEY_OK 0 +#define INCHIKEY_UNKNOWN_ERROR 1 +#define INCHIKEY_EMPTY_INPUT 2 +#define INCHIKEY_INVALID_INCHI_PREFIX 3 +#define INCHIKEY_NOT_ENOUGH_MEMORY 4 +#define INCHIKEY_INVALID_INCHI 20 +#define INCHIKEY_INVALID_STD_INCHI 21 + + +/* Return codes for CheckINCHIKey */ +typedef enum tagRetValGetINCHIKey +{ + INCHIKEY_VALID_STANDARD = 0, + INCHIKEY_VALID_NON_STANDARD = -1, + INCHIKEY_INVALID_LENGTH = 1, + INCHIKEY_INVALID_LAYOUT = 2, + INCHIKEY_INVALID_VERSION = 3 +} RetValCheckINCHIKeyv; + + + +/* EXPORTED FUNCTIONS */ + + + +/* To compile all InChI code as a C++ code #define COMPILE_ALL_CPP */ + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +GetINCHIKeyFromINCHI + +Calculate InChIKey by InChI string. + +Input: + szINCHISource + source InChI string + xtra1 + =1 calculate hash extension (up to 256 bits; 1st block) + xtra2 + =1 calculate hash extension (up to 256 bits; 2nd block) + +Output: + szINCHIKey + InChIKey string + The user-supplied buffer szINCHIKey should be at least 28 bytes long. + szXtra1 + hash extension (up to 256 bits; 1st block) string + Caller should allocate space for 64 characters + trailing NULL + szXtra2 + hash extension (up to 256 bits; 2nd block) string + Caller should allocate space for 64 characters + trailing NULL + +Returns: + success/errors codes + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetINCHIKeyFromINCHI(const char* szINCHISource, + const int xtra1, + const int xtra2, + char* szINCHIKey, + char* szXtra1, + char* szXtra2); + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +GetStdINCHIKeyFromStdINCHI + + "Standard" counterpart + + For compatibility with v. 1.02std, no extra hash calculation is allowed. + To calculate extra hash(es), use GetINCHIKeyFromINCHI with stdInChI as input. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL GetStdINCHIKeyFromStdINCHI(const char* szINCHISource, + char* szINCHIKey); + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +CheckINCHIKey + +Check if the string represents valid InChIKey. +Input: + szINCHIKey + source InChIKey string +Returns: + success/errors codes + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL CheckINCHIKey(const char *szINCHIKey); + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + Modularized InChI generation API + + + + Note. Functions with STDINCHIGEN prefix are + retained for compatibility with v. 1.02std + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ + + + + +/* Data structures holding intermediate (normalization) results */ + +#ifndef MAX_NUM_STEREO_ATOM_NEIGH +#define MAX_NUM_STEREO_ATOM_NEIGH 4 +#endif +#ifndef MAX_NUM_STEREO_BONDS +#define MAX_NUM_STEREO_BONDS 3 +#endif +#ifndef INCHI_NUM +#define INCHI_NUM 2 /* = array size; member indexes: */ +#endif + + +typedef unsigned short AT_NUMBR; +typedef signed short NUM_HS; +typedef unsigned long INCHI_MODES; + +typedef struct tagNormAtom +{ + char elname[ATOM_EL_LEN]; /* chem. element name */ + U_CHAR el_number; /* number of the element in the Periodic Table */ + AT_NUMBR neighbor[MAXVAL]; /* positions (from 0) of the neighbors in the NORM_ATOM array */ + AT_NUMBR orig_at_number; /* original atom number, starts from 1 */ + AT_NUMBR orig_compt_at_numb; /* atom number within a component before terminal H removal */ + S_CHAR bond_stereo[MAXVAL]; /* 1=Up,4=Either,6=Down (this atom is at the pointing wedge) + negative => on the opposite side of the wedge; 3=Either double bond */ + U_CHAR bond_type[MAXVAL]; /* 1=single, 2=double, 3=triple, 4=1/2 (bond order is 1 or 2) */ + /* 5=1/2/3, 6=1/3, 7=2/3, 8=tautomeric, 9=1/2 non-stereogenic */ + + S_CHAR valence; /* number of bonds = number of neighbors not greater than MAXVAL */ + S_CHAR chem_bonds_valence; /* sum of bond types (1,2,3); type 4 needs special treatment */ + S_CHAR num_H; /* number of adjacent implicit hydrogen atoms including D and T */ + S_CHAR num_iso_H[NUM_H_ISOTOPES];/* number of adjacent implicit 1H(protium), 2H(D), 3H(T) < 16 */ + S_CHAR iso_atw_diff; /* =0 => natural isotopic abundances */ + /* >0 => (isotopic mass) - (rounded average atomic mass) + 1 */ + /* <0 => (isotopic mass) - (rounded average atomic mass) */ + S_CHAR charge; /* charge */ + S_CHAR radical; /* RADICAL_SINGLET, RADICAL_DOUBLET, or RADICAL_TRIPLET */ + S_CHAR bAmbiguousStereo; /* flag of detected stereo ambiguity */ + S_CHAR cFlags; /* AT_FLAG_ISO_H_POINT: atom may have exchangeable isotopic H */ + AT_NUMBR at_type; /* ATT_NONE, ATT_ACIDIC, etc. See InChI normalization code */ + AT_NUMBR component; /* number of the structure component > 0 */ + AT_NUMBR endpoint; /* id of a tautomeric group */ + AT_NUMBR c_point; /* id of a positive charge group */ + double x; /* x coordinate */ + double y; /* y coordinate */ + double z; /* x coordinate */ + /*--------- 0D parities ----------*/ + S_CHAR bUsed0DParity; /* bit=1 => stereobond; bit=2 => stereocenter */ + /*----- tetrahedral stereo parity */ + S_CHAR p_parity; /* tetrahedral (sp3) cml parity */ + AT_NUMBR p_orig_at_num[MAX_NUM_STEREO_ATOM_NEIGH]; /* orig_at_number of each neighbor > 0; 0=> no neighbor */ + /*----- stereo bond (SB) parities */ + S_CHAR sb_ord[MAX_NUM_STEREO_BONDS]; /* neighbor[] index of another end of this SB, starts from 0 */ + S_CHAR sn_ord[MAX_NUM_STEREO_BONDS]; /* neighbor[] index of a bond that is not this SB; starts from 0; + -1 means the neighbor is a removed explicit H */ + /* atoms on both ends of a stereobond have same parity => trans/T/E/2, diff. parities => cis/C/Z/1 */ + S_CHAR sb_parity[MAX_NUM_STEREO_BONDS]; /* parities of stereobonds (sp2) incident to this atom */ + AT_NUMBR sn_orig_at_num[MAX_NUM_STEREO_BONDS]; /* orig_at_number of sn_ord[] neighbor > 0 */ + +#if ( FIND_RING_SYSTEMS == 1 ) + S_CHAR bCutVertex; /* is the atom a cut-vertex or not */ + AT_NUMBR nRingSystem; /* starts from 1; number of a ring system */ + AT_NUMBR nNumAtInRingSystem; /* number of atoms in a ring system to which this at belongs */ + AT_NUMBR nBlockSystem; /* ambiguous if the atom is a cut-vertex: better apply this to bonds */ + +#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) + AT_NUMBR nDistanceFromTerminal; /* not used */ +#endif + +#endif +} NORM_ATOM; + + + +typedef struct tagNormAtomData +{ + NORM_ATOM *at; /* atom list */ + NORM_ATOM *at_fixed_bonds; /* atom list with added or removed protons only */ + int num_at; /* number of atoms except removed terminal H */ + int num_removed_H; /* number of removed H; at[] has (num_at+num_removed_H) elements */ + int num_bonds; + int num_isotopic; /* number of isotopic atoms */ + int bExists; /* for internal use */ + int bDeleted; /* for internal use */ + int bHasIsotopicLayer; + int bTautomeric; + int bTautPreprocessed; /* for internal use */ + int nNumRemovedProtons; + NUM_HS nNumRemovedProtonsIsotopic[NUM_H_ISOTOPES]; + /* isotopic composition of removed protons, not included in num_iso_H[] */ + NUM_HS num_iso_H[NUM_H_ISOTOPES]; + /* isotopic H on tautomeric atoms and those + in nIsotopicEndpointAtomNumber */ + INCHI_MODES bTautFlags; /* for internal use */ + INCHI_MODES bTautFlagsDone; /* for internal use */ + INCHI_MODES bNormalizationFlags;/* for internal use */ +} NORM_ATOMS; + + +typedef struct tagINCHIGEN_DATA +{ + + char pStrErrStruct[STR_ERR_LEN]; /* intermediate log (warning/error report) */ + int num_components[INCHI_NUM]; /* number of allocated INChI, INChI_Aux data structures */ + /* index=0 => disconnected, 1 => reconnected structure */ + + /* The results of normalization stage */ + /* for each member of pair disconnected/reconnected structures: */ + NORM_ATOMS *NormAtomsNontaut[INCHI_NUM]; + NORM_ATOMS *NormAtomsTaut[INCHI_NUM]; +} INCHIGEN_DATA; + + +/* InChI Generator Handle */ + +typedef void* INCHIGEN_HANDLE; + + + + +/* EXPORTED FUNCTIONS */ + + + +/* to compile all InChI code as a C++ code #define COMPILE_ALL_CPP */ +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +INCHIGEN_Create / STDINCHIGEN_Create + + InChI Generator: create generator + Returns handle of generator object or NULL on failure + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +EXPIMP_TEMPLATE INCHI_API +INCHIGEN_HANDLE INCHI_DECL INCHIGEN_Create(void); +EXPIMP_TEMPLATE INCHI_API +INCHIGEN_HANDLE INCHI_DECL STDINCHIGEN_Create(void); + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +INCHIGEN_Setup / STDINCHIGEN_Setup + + InChI Generator: setup + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL INCHIGEN_Setup(INCHIGEN_HANDLE HGen, + INCHIGEN_DATA * pGenData, + inchi_Input * pInp); +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL STDINCHIGEN_Setup(INCHIGEN_HANDLE HGen, + INCHIGEN_DATA * pGenData, + inchi_Input * pInp); + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +INCHIGEN_DoNormalization / STDINCHIGEN_DoNormalization + + InChI Generator: structure normalization stage + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL INCHIGEN_DoNormalization(INCHIGEN_HANDLE HGen, + INCHIGEN_DATA * pGenData); +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL STDINCHIGEN_DoNormalization(INCHIGEN_HANDLE HGen, + INCHIGEN_DATA * pGenData); + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +INCHIGEN_DoCanonicalization / STDINCHIGEN_DoCanonicalization + + InChI Generator: structure canonicalization stage + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL INCHIGEN_DoCanonicalization + (INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData); +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL STDINCHIGEN_DoCanonicalization + (INCHIGEN_HANDLE HGen, INCHIGEN_DATA * pGenData); + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +INCHIGEN_DoSerialization / STDINCHIGEN_DoSerialization + + InChI Generator: InChI serialization stage + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL INCHIGEN_DoSerialization(INCHIGEN_HANDLE HGen, + INCHIGEN_DATA * pGenData, + inchi_Output * pResults); +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL STDINCHIGEN_DoSerialization(INCHIGEN_HANDLE HGen, + INCHIGEN_DATA * pGenData, + inchi_Output * pResults); + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +INCHIGEN_Reset / STDINCHIGEN_Reset + + InChI Generator: reset stage (use before get next structure) + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +EXPIMP_TEMPLATE INCHI_API +void INCHI_DECL INCHIGEN_Reset(INCHIGEN_HANDLE HGen, + INCHIGEN_DATA * pGenData, + inchi_Output * pResults); +EXPIMP_TEMPLATE INCHI_API +void INCHI_DECL STDINCHIGEN_Reset(INCHIGEN_HANDLE HGen, + INCHIGEN_DATA * pGenData, + inchi_Output * pResults); + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +INCHIGEN_Destroy / STDINCHIGEN_Destroy + + InChI Generator: destroy generator + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL INCHIGEN_Destroy(INCHIGEN_HANDLE HGen); +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL STDINCHIGEN_Destroy(INCHIGEN_HANDLE HGen); + + + +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +MakeINCHIFromMolfileText + + Direct generation of InChI from Molfile supplied as text + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ + + +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL + MakeINCHIFromMolfileText( const char *moltext, + char *options, + inchi_Output *result ); + + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + +/* + v. 1.05: + include IXA, "Extensible InChI API" +*/ +#include "ixa.h" + + +#endif /* _INHCH_API_H_ */ diff --git a/INCHI-1-SRC/INCHI_BASE/src/inchi_gui.c b/INCHI-1-SRC/INCHI_BASE/src/inchi_gui.c new file mode 100644 index 0000000..4e1755c --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/inchi_gui.c @@ -0,0 +1,355 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * + */ + + +#include + +#include "mode.h" + +#include "ichimain.h" +#include "inchi_gui.h" + + + + +/****************************************************************************/ + + +#ifndef COMPILE_ANSI_ONLY + + +#ifndef TARGET_LIB_FOR_WINCHI + +/****************************************************************************/ +int DisplayStructure( struct tagCANON_GLOBALS *pCG, + inp_ATOM *at, + int num_at, int num_removed_H, + int bAdd_DT_to_num_H, + int nNumRemovedProtons, + NUM_H *nNumRemovedProtonsIsotopic, + int bIsotopic, + int j /*bTautomeric*/, + INChI **cur_INChI, INChI_Aux **cur_INChI_Aux, + int bAbcNumbers, + DRAW_PARMS *dp, + INCHI_MODE nMode, + char *szTitle ) +{ +INF_ATOM_DATA inf_data = {NULL,}; +int err = -1; + + if ( CreateInfoAtomData( &inf_data, num_at, 1 ) ) + { + err = 0; + + FillOutInfAtom( pCG, at, &inf_data, num_at, num_removed_H, bAdd_DT_to_num_H, + nNumRemovedProtons, nNumRemovedProtonsIsotopic, bIsotopic, + cur_INChI ? cur_INChI[j] : NULL, + cur_INChI_Aux ? cur_INChI_Aux[j] : NULL, + bAbcNumbers, nMode); + + FillTableParms( &dp->sdp, cur_INChI, cur_INChI_Aux, nMode, bIsotopic, j ); + + err = DisplayInputStructure( szTitle, at, &inf_data, num_at, dp ); + + FreeInfoAtomData( &inf_data ); + } + + return err; +} + + + +/****************************************************************************/ +int DisplayCompositeStructure( struct tagCANON_GLOBALS *pCG, + COMP_ATOM_DATA *composite_norm_data, + int bIsotopic, int bTautomeric, + PINChI2 *pINChI2, + PINChI_Aux2 *pINChI_Aux2, + int bAbcNumbers, + DRAW_PARMS *dp, + INCHI_MODE nMode, + char *szTitle ) +{ +INF_ATOM_DATA inf_data; +int err = -1, ret; + + memset( &inf_data, 0, sizeof(inf_data) ); + + if ( CreateInfoAtomData( &inf_data, (composite_norm_data+bTautomeric)->num_at, + (composite_norm_data+bTautomeric)->num_components ) ) + { + + ret = FillOutCompositeCanonInfAtom( pCG, + composite_norm_data, + &inf_data, + bIsotopic, bTautomeric, + pINChI2, pINChI_Aux2, + bAbcNumbers, nMode); + if ( !ret ) + goto exit_function; + + if ( bTautomeric == TAUT_INI ) + { + /* + FillOutInfAtom( (composite_norm_data+bTautomeric)->at, &inf_data, (composite_norm_data+bTautomeric)->num_at, + (composite_norm_data+bTautomeric)->num_removed_H, bAdd_DT_to_num_H, + (composite_norm_data+bTautomeric)->nNumRemovedProtons, + (composite_norm_data+bTautomeric)->nNumRemovedProtonsIsotopic, bIsotopic, + NULL, NULL, bAbcNumbers, nMode); + */ + ; + } + else + { + /* real check for tautomeric components 02-04-2005 */ + int m, nNumTautComponents = 0; + + if ( 1 == bTautomeric ) + { + for ( m = 0; m < composite_norm_data[TAUT_YES].num_components; m ++ ) + { + if ( !pINChI2[m][TAUT_YES] ) + continue; + if ( pINChI2[m][TAUT_YES]->bDeleted || pINChI2[m][TAUT_YES]->lenTautomer > 0 ) + nNumTautComponents ++; + } + } + + FillCompositeTableParms( &dp->sdp, inf_data.StereoFlags, nMode, bIsotopic, nNumTautComponents ); + } + + err = DisplayInputStructure( szTitle, (composite_norm_data+bTautomeric)->at, &inf_data, (composite_norm_data+bTautomeric)->num_at, dp ); + + FreeInfoAtomData( &inf_data ); + } + +exit_function: + return err; +} + + + + + + + +#endif + + + +/****************************************************************************/ +void FillTableParms( SET_DRAW_PARMS *sdp, + INChI **cur_INChI, + INChI_Aux **cur_INChI_Aux, + INCHI_MODE nMode, + int bShowIsotopic, int indx ) +{ +TBL_DRAW_PARMS *tdp = sdp->tdp; +char (*ReqShownFound)[TDP_NUM_PAR] = tdp->ReqShownFound; +int i, j; +INChI_Stereo *Stereo; +int bShowTaut = (cur_INChI && cur_INChI[indx]->lenTautomer > 0)? 1 : 0; + +#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) + int bRelRac = 0!=(nMode & + (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO )); +#endif + + if ( !cur_INChI || !cur_INChI_Aux ) + { + sdp->tdp->bDrawTbl = 0; + sdp->bOrigAtom = 1; + return; + } + + /* Displayed */ + + ReqShownFound[ilSHOWN][itBASIC] = bShowTaut? 'T':'\0'; + ReqShownFound[ilSHOWN][itISOTOPIC] = bShowIsotopic? 'I':'\0'; + +/* + ReqShownFound[ilSHOWN][itBASIC] = bShowTaut? 'T':'B'; + ReqShownFound[ilSHOWN][itISOTOPIC] = bShowIsotopic? 'I':'N'; + */ + + i = indx; + if ( cur_INChI[i] ) + { + Stereo = bShowIsotopic ? cur_INChI[i]->StereoIsotopic + : cur_INChI[i]->Stereo; + } + else + { + Stereo = NULL; + } + +#if ( REL_RAC_STEREO_IGN_1_SC == 1 ) + + if ( Stereo && ( 0 < Stereo->nNumberOfStereoBonds || + 0 < Stereo->nNumberOfStereoCenters-bRelRac ) ) { + ReqShownFound[ilSHOWN][itSTEREO] = 'S'; + if ( Stereo->nNumberOfStereoCenters && Stereo->nCompInv2Abs == -1 && + ( nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO ))) + { + if ( Stereo->nNumberOfStereoCenters < 2 && + !Stereo->nNumberOfStereoBonds ) + { + ReqShownFound[ilSHOWN][itSTEREO] = '\0'; + } + else if ( Stereo->nNumberOfStereoCenters >= 2 ) + { + /* shown Inverted stereo */ + ReqShownFound[ilSHOWN][itSTEREO] = 's'; + } + } + +#else /* REL_RAC_STEREO_IGN_1_SC == 0 */ + + if ( Stereo && + (Stereo->nNumberOfStereoBonds || Stereo->nNumberOfStereoCenters) ) + { + + ReqShownFound[ilSHOWN][itSTEREO] = 'S'; + + if ( Stereo->nNumberOfStereoCenters && Stereo->nCompInv2Abs == -1 && + ( nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO) ) ) + { + /* + if ( Stereo->nNumberOfStereoCenters < 2 && !Stereo->nNumberOfStereoBonds ) + { + ReqShownFound[ilSHOWN][itSTEREO] = '\0'; + } else + if ( Stereo->nNumberOfStereoCenters >= 2 ) { + */ + + /* shown Inverted stereo */ + ReqShownFound[ilSHOWN][itSTEREO] = 's'; + + /* + } + */ + } +#endif /* REL_RAC_STEREO_IGN_1_SC */ + } + else + { + ReqShownFound[ilSHOWN][itSTEREO] = '\0'; + } + + /* + ReqShownFound[ilSHOWN][itSTEREO] = + (bShowIsotopic? (cur_INChI[i] && cur_INChI[i]->StereoIsotopic && + (cur_INChI[i]->StereoIsotopic->nNumberOfStereoBonds || + cur_INChI[i]->StereoIsotopic->nNumberOfStereoCenters)) + : + (cur_INChI[i] && cur_INChI[i]->Stereo && + (cur_INChI[i]->Stereo->nNumberOfStereoBonds || + cur_INChI[i]->Stereo->nNumberOfStereoCenters) ) + ) ? 'S':'\0'; + */ + + /* Remove zeroes between chars */ + for ( i = j = 0; i < TDP_NUM_PAR; i ++ ) + { + if ( ReqShownFound[ilSHOWN][i] >= ' ' ) + { + ReqShownFound[ilSHOWN][j++] = ReqShownFound[ilSHOWN][i]; + } + } + + i = j; + + for ( ; i < TDP_NUM_PAR; i ++ ) + { + ReqShownFound[ilSHOWN][i] = '\0'; + } + + sdp->tdp->bDrawTbl = j? 1 : 0; + sdp->bOrigAtom = 0; + + return; +} + + + +/****************************************************************************/ +void FillCompositeTableParms( SET_DRAW_PARMS *sdp, AT_NUMB StereoFlags, + INCHI_MODE nMode, int bShowIsotopic, int bShowTaut ) +{ +TBL_DRAW_PARMS *tdp = sdp->tdp; +char (*ReqShownFound)[TDP_NUM_PAR] = tdp->ReqShownFound; +int i, j; + + /* Displayed */ + + ReqShownFound[ilSHOWN][itBASIC] = bShowTaut? 'T':'\0'; + ReqShownFound[ilSHOWN][itISOTOPIC] = bShowIsotopic? 'I':'\0'; + +/* + ReqShownFound[ilSHOWN][itBASIC] = bShowTaut? 'T':'B'; + ReqShownFound[ilSHOWN][itISOTOPIC] = bShowIsotopic? 'I':'N'; + */ + + if ( StereoFlags & INF_STEREO ) + { + ReqShownFound[ilSHOWN][itSTEREO] = 'S'; + if ( (StereoFlags & INF_STEREO_INV) && + ( nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO ) ) ) + { + if (StereoFlags & (INF_STEREO_REL | INF_STEREO_RAC) ) + { + ReqShownFound[ilSHOWN][itSTEREO] = 's'; + } + else + { + ReqShownFound[ilSHOWN][itSTEREO] = '\0'; /* shown Inverted stereo */ + } + } + } + else + { + ReqShownFound[ilSHOWN][itSTEREO] = '\0'; + } + + /* + ReqShownFound[ilSHOWN][itSTEREO] = + (bShowIsotopic? (cur_INChI[i] && cur_INChI[i]->StereoIsotopic && + (cur_INChI[i]->StereoIsotopic->nNumberOfStereoBonds || + cur_INChI[i]->StereoIsotopic->nNumberOfStereoCenters) ) + : + (cur_INChI[i] && cur_INChI[i]->Stereo && + (cur_INChI[i]->Stereo->nNumberOfStereoBonds || + cur_INChI[i]->Stereo->nNumberOfStereoCenters) ) + ) ? 'S':'\0'; + */ + + /* Remove zeroes between chars */ + + for ( i = j = 0; i < TDP_NUM_PAR; i ++ ) + { + if ( ReqShownFound[ilSHOWN][i] >= ' ' ) + { + ReqShownFound[ilSHOWN][j++] = ReqShownFound[ilSHOWN][i]; + } + } + + i = j; + + for ( ; i < TDP_NUM_PAR; i ++ ) + { + ReqShownFound[ilSHOWN][i] = '\0'; + } + + sdp->tdp->bDrawTbl = j? 1 : 0; + sdp->bOrigAtom = 0; + + return; +} + + + +#endif diff --git a/INCHI-1-SRC/INCHI_BASE/src/inchi_gui.h b/INCHI-1-SRC/INCHI_BASE/src/inchi_gui.h new file mode 100644 index 0000000..5a40d87 --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/inchi_gui.h @@ -0,0 +1,90 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * + */ + + +#ifndef _INCHI_GUI_H_ +#define _INCHI_GUI_H_ + + +#include "strutil.h" +#include "ichicomn.h" + + +#ifndef COMPILE_ANSI_ONLY + +struct tagCANON_GLOBALS; + + +int DisplayStructure( struct tagCANON_GLOBALS *pCG, + inp_ATOM *at, + int num_at, + int num_removed_H, + int bAdd_DT_to_num_H, + int nNumRemovedProtons, + NUM_H nNumRemovedProtonsIsotopic[], + int bIsotopic, + int j /*bTautomeric*/, + INChI **cur_INChI, + INChI_Aux **cur_INChI_Aux, + int bAbcNumbers, + DRAW_PARMS *dp, + INCHI_MODE nMode, + char *szTitle ); + +int DisplayCompositeStructure( struct tagCANON_GLOBALS *pCG, + COMP_ATOM_DATA *composite_norm_data, + int bIsotopic, + int bTautomeric, + PINChI2 *pINChI2, + PINChI_Aux2 *pINChI_Aux2, + int bAbcNumbers, + DRAW_PARMS *dp, + INCHI_MODE nMode, + char *szTitle ); + + +int DisplayTheWholeStructure( struct tagCANON_GLOBALS *pCG, + struct tagINCHI_CLOCK *ic, + struct tagStructData *sd, + INPUT_PARMS *ip, + char *szTitle, + INCHI_IOSTREAM *inp_file, + INCHI_IOSTREAM *log_file, + ORIG_ATOM_DATA *orig_inp_data, + long num_inp, + int iINChI, + int bShowStruct, + int bINCHI_LIB_Flag ); + +int DisplayTheWholeCompositeStructure( struct tagCANON_GLOBALS *pCG, + struct tagINCHI_CLOCK *ic, + INPUT_PARMS *ip, + struct tagStructData *sd, + long num_inp, + int iINChI, + PINChI2 *pINChI2, + PINChI_Aux2 *pINChI_Aux2, + ORIG_ATOM_DATA *orig_inp_data, + ORIG_ATOM_DATA *prep_inp_data, + COMP_ATOM_DATA composite_norm_data[TAUT_NUM+1] ); + + +void FillTableParms( SET_DRAW_PARMS *sdp, + INChI **cur_INChI, + INChI_Aux **cur_INChI_Aux, + INCHI_MODE nMode, + int bShowIsotopic, + int bShowTaut ); + +void FillCompositeTableParms( SET_DRAW_PARMS *sdp, + AT_NUMB StereoFlags, + INCHI_MODE nMode, + int bShowIsotopic, + int bShowTaut ); + +#endif + +#endif /* _INCHI_GUI_H_ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/inchicmp.h b/INCHI-1-SRC/INCHI_BASE/src/inchicmp.h similarity index 87% rename from INCHI-1-SRC/INCHI_API/inchi_dll/inchicmp.h rename to INCHI-1-SRC/INCHI_BASE/src/inchicmp.h index bfec9b8..13d860d 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/inchicmp.h +++ b/INCHI-1-SRC/INCHI_BASE/src/inchicmp.h @@ -1,138 +1,135 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __INCHICMP_H__ -#define __INCHICMP_H__ - -typedef enum tagInchiCompareDiffBits { - INCHIDIFF_ZERO = 0x00000000, - INCHIDIFF_PROBLEM = 0x00000001, /* severe: at least one InChI does not exist */ - INCHIDIFF_NUM_AT = 0x00000001, /* severe: different number of atoms in the skeleton */ - INCHIDIFF_ATOMS = 0x00000001, /* severe: diiferent types of skeleton atoms */ - INCHIDIFF_NUM_EL = 0x00000001, /* severe: formulas differ in another element */ - INCHIDIFF_CON_LEN = 0x00000001, /* severe: different connection table lengths */ - INCHIDIFF_CON_TBL = 0x00000001, /* severe: different connection tables */ - INCHIDIFF_POSITION_H = 0x00000002, /* difference in non-taut (Mobile-H) or all H (Fixed-H) location/number */ - INCHIDIFF_MORE_FH = 0x00000004, /* extra fixed H */ - INCHIDIFF_LESS_FH = 0x00000004, /* missing fixed H */ - INCHIDIFF_MORE_H = 0x00000008, /* formulas differ in number of H */ - INCHIDIFF_LESS_H = 0x00000008, /* formulas differ in number of H */ - INCHIDIFF_NO_TAUT = 0x00000010, /* restored structure has no taut groups while the original InChI has some */ - INCHIDIFF_WRONG_TAUT = 0x00000020, /* restored has tautomerism while the original does not have it */ - INCHIDIFF_SINGLE_TG = 0x00000040, /* restored has 1 taut. group while the original InChI has multiple tg */ - INCHIDIFF_MULTIPLE_TG = 0x00000080, /* restored has multiple tg while the original InChI has only one tg */ - INCHIDIFF_EXTRA_TG_ENDP = 0x00000100, /* extra tautomeric endpoint(s) in restored structure */ - INCHIDIFF_MISS_TG_ENDP = 0x00000100, /* one or more tg endpoint is not in the restored structure */ - INCHIDIFF_DIFF_TG_ENDP = 0x00000100, /* lists of tg endpoints are different */ - INCHIDIFF_NUM_TG = 0x00000200, /* different number of tautomeric groups */ - INCHIDIFF_TG = 0x00000200, /* different tautomeric groups */ - INCHIDIFF_NUM_ISO_AT = 0x00000400, /* ?severe: restored struct. has different number of isotopic atoms */ - INCHIDIFF_ISO_AT = 0x00000400, /* ?severe: restored struct. has different locations/isotopes of isotopic atoms */ - INCHIDIFF_REM_ISO_H = 0x00000800, /* isotopic H removed */ - INCHIDIFF_MOB_ISO_H = 0x00001000, /* different number of mobile exchangeable isotopic H */ - INCHIDIFF_CHARGE = 0x00002000, /* restored structure has different charge */ - INCHIDIFF_REM_PROT = 0x00004000, /* proton(s) removed/added from the restored structure */ - INCHIDIFF_MOBH_PROTONS = 0x00008000, /* different proton balance */ - INCHIDIFF_SC_INV = 0x00010000, /* restores structure has different inversion stereocenter mark */ - INCHIDIFF_SC_PARITY = 0x00020000, /* restored structure has stereoatoms or allenes with different parity */ - INCHIDIFF_SC_EXTRA_UNDF = 0x00040000, /* restored structure has extra undefined stereocenter(s) */ - INCHIDIFF_SC_EXTRA = 0x00080000, /* restored structure has extra stereocenter(s) */ - INCHIDIFF_SC_MISS_UNDF = 0x00100000, /* restored structure has not some undefined stereocenter(s) */ - INCHIDIFF_SC_MISS = 0x00200000, /* restored structure has not some stereocenters that are not undefined */ - INCHIDIFF_SB_PARITY = 0x00400000, /* restored structure has stereobonds or cumulenes with different parity */ - INCHIDIFF_SB_EXTRA_UNDF = 0x00800000, /* restored structure has extra undefined stereobond(s) */ - INCHIDIFF_SB_EXTRA = 0x01000000, /* restored structure has extra stereobond(s) */ - INCHIDIFF_SB_MISS_UNDF = 0x02000000, /* restored structure has not some undefined stereocenters */ - INCHIDIFF_SB_MISS = 0x04000000, /* restored structure has not some stereobonds that are not undefined */ - INCHIDIFF_COMP_HLAYER = 0x08000000, /* Restored component has Mobile-H layer instead of both Mobile-H & Fixed-H or both instead of one */ - INCHIDIFF_COMP_NUMBER = 0x10000000, /* wrong number of components */ - INCHIDIFF_STR2INCHI_ERR = 0x20000000 /* Restored structure to InChI conversion error */ - /* reserved - 0x40000000 - 0x80000000 - */ -} INCHIDIFF; - -typedef enum tagtagCompareInchiMsgGroupID { - IDGRP_ZERO = 0, - IDGRP_ERR = 1, - IDGRP_H = 2, - IDGRP_MOB_GRP = 3, - IDGRP_ISO_AT = 4, - IDGRP_CHARGE = 5, - IDGRP_PROTONS = 6, - IDGRP_ISO_H = 7, - IDGRP_SC = 8, - IDGRP_SB = 9, - IDGRP_HLAYER =10, - IDGRP_COMP =11, - IDGRP_CONV_ERR =12 -} CMP_INCHI_MSG_GROUP_ID; - - -typedef struct tagCompareInchiMsg { - INCHIDIFF nBit; - CMP_INCHI_MSG_GROUP_ID nGroupID; - const char *szMsg; -} CMP_INCHI_MSG; - -typedef struct tagCompareInchiMsgGroup { - CMP_INCHI_MSG_GROUP_ID nGroupID; - const char *szGroupName; -} CMP_INCHI_MSG_GROUP; - - - - - - -#define INCHIDIFF_SB (INCHIDIFF_SB_PARITY | INCHIDIFF_SB_EXTRA_UNDF | INCHIDIFF_SB_EXTRA | INCHIDIFF_SB_MISS_UNDF | INCHIDIFF_SB_MISS) -#define INCHIDIFF_SC (INCHIDIFF_SC_PARITY | INCHIDIFF_SC_EXTRA_UNDF | INCHIDIFF_SC_EXTRA | INCHIDIFF_SC_MISS_UNDF | INCHIDIFF_SC_MISS) - -#define INCHIDIFF_CONSTIT (INCHIDIFF_POSITION_H | INCHIDIFF_MORE_FH | INCHIDIFF_LESS_FH | INCHIDIFF_MORE_H | INCHIDIFF_LESS_H |\ - INCHIDIFF_NO_TAUT | INCHIDIFF_WRONG_TAUT | INCHIDIFF_SINGLE_TG | INCHIDIFF_MULTIPLE_TG | \ - INCHIDIFF_NUM_TG | INCHIDIFF_EXTRA_TG_ENDP | INCHIDIFF_MISS_TG_ENDP | INCHIDIFF_TG | \ - INCHIDIFF_NUM_ISO_AT | INCHIDIFF_ISO_AT | INCHIDIFF_CHARGE | INCHIDIFF_REM_PROT | INCHIDIFF_REM_ISO_H |\ - INCHIDIFF_DIFF_TG_ENDP) -#define INCHIDIFF_STEREO (INCHIDIFF_SC_INV | INCHIDIFF_SC_PARITY | INCHIDIFF_SC_EXTRA_UNDF | INCHIDIFF_SC_EXTRA | \ - INCHIDIFF_SC_MISS_UNDF | INCHIDIFF_SC_MISS | INCHIDIFF_SB_PARITY | INCHIDIFF_SB_EXTRA_UNDF |\ - INCHIDIFF_SB_EXTRA | INCHIDIFF_SB_MISS_UNDF | INCHIDIFF_SB_MISS) - -#endif /* __INCHICMP_H__ */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _INCHICMP_H_ +#define _INCHICMP_H_ + + +typedef enum tagInchiCompareDiffBits { + INCHIDIFF_ZERO = 0x00000000, + INCHIDIFF_PROBLEM = 0x00000001, /* severe: at least one InChI does not exist */ + INCHIDIFF_NUM_AT = 0x00000001, /* severe: different number of atoms in the skeleton */ + INCHIDIFF_ATOMS = 0x00000001, /* severe: diiferent types of skeleton atoms */ + INCHIDIFF_NUM_EL = 0x00000001, /* severe: formulas differ in another element */ + INCHIDIFF_CON_LEN = 0x00000001, /* severe: different connection table lengths */ + INCHIDIFF_CON_TBL = 0x00000001, /* severe: different connection tables */ + INCHIDIFF_POSITION_H = 0x00000002, /* difference in non-taut (Mobile-H) or all H (Fixed-H) location/number */ + INCHIDIFF_MORE_FH = 0x00000004, /* extra fixed H */ + INCHIDIFF_LESS_FH = 0x00000004, /* missing fixed H */ + INCHIDIFF_MORE_H = 0x00000008, /* formulas differ in number of H */ + INCHIDIFF_LESS_H = 0x00000008, /* formulas differ in number of H */ + INCHIDIFF_NO_TAUT = 0x00000010, /* restored structure has no taut groups while the original InChI has some */ + INCHIDIFF_WRONG_TAUT = 0x00000020, /* restored has tautomerism while the original does not have it */ + INCHIDIFF_SINGLE_TG = 0x00000040, /* restored has 1 taut. group while the original InChI has multiple tg */ + INCHIDIFF_MULTIPLE_TG = 0x00000080, /* restored has multiple tg while the original InChI has only one tg */ + INCHIDIFF_EXTRA_TG_ENDP = 0x00000100, /* extra tautomeric endpoint(s) in restored structure */ + INCHIDIFF_MISS_TG_ENDP = 0x00000100, /* one or more tg endpoint is not in the restored structure */ + INCHIDIFF_DIFF_TG_ENDP = 0x00000100, /* lists of tg endpoints are different */ + INCHIDIFF_NUM_TG = 0x00000200, /* different number of tautomeric groups */ + INCHIDIFF_TG = 0x00000200, /* different tautomeric groups */ + INCHIDIFF_NUM_ISO_AT = 0x00000400, /* ?severe: restored struct. has different number of isotopic atoms */ + INCHIDIFF_ISO_AT = 0x00000400, /* ?severe: restored struct. has different locations/isotopes of isotopic atoms */ + INCHIDIFF_REM_ISO_H = 0x00000800, /* isotopic H removed */ + INCHIDIFF_MOB_ISO_H = 0x00001000, /* different number of mobile exchangeable isotopic H */ + INCHIDIFF_CHARGE = 0x00002000, /* restored structure has different charge */ + INCHIDIFF_REM_PROT = 0x00004000, /* proton(s) removed/added from the restored structure */ + INCHIDIFF_MOBH_PROTONS = 0x00008000, /* different proton balance */ + INCHIDIFF_SC_INV = 0x00010000, /* restores structure has different inversion stereocenter mark */ + INCHIDIFF_SC_PARITY = 0x00020000, /* restored structure has stereoatoms or allenes with different parity */ + INCHIDIFF_SC_EXTRA_UNDF = 0x00040000, /* restored structure has extra undefined stereocenter(s) */ + INCHIDIFF_SC_EXTRA = 0x00080000, /* restored structure has extra stereocenter(s) */ + INCHIDIFF_SC_MISS_UNDF = 0x00100000, /* restored structure has not some undefined stereocenter(s) */ + INCHIDIFF_SC_MISS = 0x00200000, /* restored structure has not some stereocenters that are not undefined */ + INCHIDIFF_SB_PARITY = 0x00400000, /* restored structure has stereobonds or cumulenes with different parity */ + INCHIDIFF_SB_EXTRA_UNDF = 0x00800000, /* restored structure has extra undefined stereobond(s) */ + INCHIDIFF_SB_EXTRA = 0x01000000, /* restored structure has extra stereobond(s) */ + INCHIDIFF_SB_MISS_UNDF = 0x02000000, /* restored structure has not some undefined stereocenters */ + INCHIDIFF_SB_MISS = 0x04000000, /* restored structure has not some stereobonds that are not undefined */ + INCHIDIFF_COMP_HLAYER = 0x08000000, /* Restored component has Mobile-H layer instead of both Mobile-H & Fixed-H or both instead of one */ + INCHIDIFF_COMP_NUMBER = 0x10000000, /* wrong number of components */ + INCHIDIFF_STR2INCHI_ERR = 0x20000000 /* Restored structure to InChI conversion error */ + /* reserved + 0x40000000 + 0x80000000 + */ +} INCHIDIFF; + +typedef enum tagtagCompareInchiMsgGroupID { + IDGRP_ZERO = 0, + IDGRP_ERR = 1, + IDGRP_H = 2, + IDGRP_MOB_GRP = 3, + IDGRP_ISO_AT = 4, + IDGRP_CHARGE = 5, + IDGRP_PROTONS = 6, + IDGRP_ISO_H = 7, + IDGRP_SC = 8, + IDGRP_SB = 9, + IDGRP_HLAYER =10, + IDGRP_COMP =11, + IDGRP_CONV_ERR =12 +} CMP_INCHI_MSG_GROUP_ID; + + +typedef struct tagCompareInchiMsg { + INCHIDIFF nBit; + CMP_INCHI_MSG_GROUP_ID nGroupID; + const char *szMsg; +} CMP_INCHI_MSG; + +typedef struct tagCompareInchiMsgGroup { + CMP_INCHI_MSG_GROUP_ID nGroupID; + const char *szGroupName; +} CMP_INCHI_MSG_GROUP; + + +#define INCHIDIFF_SB (INCHIDIFF_SB_PARITY | INCHIDIFF_SB_EXTRA_UNDF | INCHIDIFF_SB_EXTRA | INCHIDIFF_SB_MISS_UNDF | INCHIDIFF_SB_MISS) +#define INCHIDIFF_SC (INCHIDIFF_SC_PARITY | INCHIDIFF_SC_EXTRA_UNDF | INCHIDIFF_SC_EXTRA | INCHIDIFF_SC_MISS_UNDF | INCHIDIFF_SC_MISS) + +#define INCHIDIFF_CONSTIT (INCHIDIFF_POSITION_H | INCHIDIFF_MORE_FH | INCHIDIFF_LESS_FH | INCHIDIFF_MORE_H | INCHIDIFF_LESS_H |\ + INCHIDIFF_NO_TAUT | INCHIDIFF_WRONG_TAUT | INCHIDIFF_SINGLE_TG | INCHIDIFF_MULTIPLE_TG | \ + INCHIDIFF_NUM_TG | INCHIDIFF_EXTRA_TG_ENDP | INCHIDIFF_MISS_TG_ENDP | INCHIDIFF_TG | \ + INCHIDIFF_NUM_ISO_AT | INCHIDIFF_ISO_AT | INCHIDIFF_CHARGE | INCHIDIFF_REM_PROT | INCHIDIFF_REM_ISO_H |\ + INCHIDIFF_DIFF_TG_ENDP) +#define INCHIDIFF_STEREO (INCHIDIFF_SC_INV | INCHIDIFF_SC_PARITY | INCHIDIFF_SC_EXTRA_UNDF | INCHIDIFF_SC_EXTRA | \ + INCHIDIFF_SC_MISS_UNDF | INCHIDIFF_SC_MISS | INCHIDIFF_SB_PARITY | INCHIDIFF_SB_EXTRA_UNDF |\ + INCHIDIFF_SB_EXTRA | INCHIDIFF_SB_MISS_UNDF | INCHIDIFF_SB_MISS) + + +#endif /* _INCHICMP_H_ */ diff --git a/INCHI-1-SRC/INCHI/common/incomdef.h b/INCHI-1-SRC/INCHI_BASE/src/incomdef.h similarity index 71% rename from INCHI-1-SRC/INCHI/common/incomdef.h rename to INCHI-1-SRC/INCHI_BASE/src/incomdef.h index 72f1d08..7bb7958 100644 --- a/INCHI-1-SRC/INCHI/common/incomdef.h +++ b/INCHI-1-SRC/INCHI_BASE/src/incomdef.h @@ -1,181 +1,181 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Former COMDEF.H -Renamed 06/12/07 to avoid occassional conflict with Microsoft's COMDEF.H -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -/* common definitions -- do not change */ -#ifndef __INCOMDEF_H__ -#define __INCOMDEF_H__ - -#include "ichisize.h" - - -/* SDF treatment */ -#define MAX_SDF_HEADER 64 /* max length of the SDFile data header */ -#define MAX_SDF_VALUE 255 /* max lenght of the SDFile data value */ - -/* size resrictions */ -#define ATOM_EL_LEN 6 /* length of atom name string including zero termination */ -#define ATOM_INFO_LEN 36 /* inf_ATOM output string ^123Al^+2H12..(+)/999/999/999/999: 32 chars */ -#define MAXVAL 20 /* max number of bonds per atom */ -#define MAX_STEREO_BONDS 3 /* max number of stereogenic bonds per atom */ -#define NUM_H_ISOTOPES 3 /* number of hydrogen isotopes: protium, deuterium, tritium */ -#define ATW_H 1 /* hydrogen atomic weight */ - -/* input bond type definition */ -#define MIN_INPUT_BOND_TYPE 1 -#define MAX_INPUT_BOND_TYPE 4 - -#define BOND_TYPE_SINGLE 1 -#define BOND_TYPE_DOUBLE 2 -#define BOND_TYPE_TRIPLE 3 -#define BOND_TYPE_ALTERN 4 - -#define STEREO_SNGL_UP 1 -#define STEREO_SNGL_EITHER 4 -#define STEREO_SNGL_DOWN 6 -#define STEREO_DBLE_EITHER 3 - - -/* MOlfile */ -#define INPUT_STEREO_SNGL_UP 1 -#define INPUT_STEREO_SNGL_EITHER 4 -#define INPUT_STEREO_SNGL_DOWN 6 -#define INPUT_STEREO_DBLE_EITHER 3 - -/* -#define BOND_MARK_ODD 0x10 -#define BOND_MARK_EVEN 0x20 -*/ -#define BOND_MARK_PARITY 0x30 -#define BOND_MARK_HIGHLIGHT 0x40 /* highlight equivalent components */ - -#define BOND_MARK_ODD '-' -#define BOND_MARK_EVEN '+' -#define BOND_MARK_UNDF '?' -#define BOND_MARK_UNKN 'u' -#define BOND_MARK_ERR '*' - -#define SALT_DONOR_H 1 -#define SALT_DONOR_Neg 2 -#define SALT_ACCEPTOR 4 -#define SALT_p_DONOR 8 /* >C-SH */ -#define SALT_p_ACCEPTOR 16 /* >C-S(-) */ -#define SALT_DONOR_ALL (SALT_DONOR_Neg | SALT_DONOR_H | SALT_p_ACCEPTOR | SALT_p_DONOR) -#define SALT_DONOR_Neg2 (SALT_DONOR_Neg | SALT_p_ACCEPTOR) -#define SALT_DONOR_H2 (SALT_DONOR_H | SALT_p_DONOR) -#define SALT_DONOR (SALT_DONOR_Neg | SALT_DONOR_H) - -#define SALT_SELECTED 32 - -/* radical definitions */ -#define RADICAL_SINGLET 1 -#define RADICAL_DOUBLET 2 -#define RADICAL_TRIPLET 3 - -/* metal definition */ -#define METAL 1 /* definition of an element: lowest valence */ -#define METAL2 3 /* definition of an element: lowest and next to it valence */ -#define IS_METAL 3 /* metal bitmap */ -/* isotopic shift */ -#define ZERO_ATW_DIFF 127 /* mark mass of the most abundant isotope */ - -/* other types */ - -#define UCINT (int)(unsigned char) - -#ifndef INCHI_US_CHAR_DEF -typedef signed char S_CHAR; -typedef unsigned char U_CHAR; -#define INCHI_US_CHAR_DEF -#endif - -#ifndef INCHI_US_SHORT_DEF -typedef signed short S_SHORT; -typedef unsigned short U_SHORT; -#define INCHI_US_SHORT_DEF -#endif - -/* BILLY 8/6/04 */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -#define STR_ERR_LEN 256 -int AddMOLfileError( char *pStrErr, const char *szMsg ); - -/* allocator */ -#ifndef inchi_malloc -void *inchi_malloc(size_t c); -#endif -#ifndef inchi_calloc -void *inchi_calloc(size_t c, size_t n); -#endif -#ifndef inchi_free -void inchi_free(void *p); -#endif - - - -/* sorting etc */ -void inchi_swap ( char *a, char *b, size_t width ); -int insertions_sort( void *base, size_t num, size_t width, int ( *compare )(const void *e1, const void *e2 ) ); -int insertions_sort_AT_NUMBERS( AT_NUMB *base, int num, int ( *compare )(const void *e1, const void *e2 ) ); - - -#define MOLFILE_ERR_FIN(err, new_err, err_fin, msg) \ - if ( !(err) && (new_err) ) { (err) = (new_err);} AddMOLfileError(pStrErr, (msg)); goto err_fin -#define MOLFILE_ERR_SET(err, new_err, msg) \ - if ( !(err) && (new_err) ) { (err) = (new_err);} AddMOLfileError(pStrErr, (msg)) - - -/* BILLY 8/6/04 */ -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - -#endif /* __INCOMDEF_H__ */ - +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _INCOMDEF_H_ +#define _INCOMDEF_H_ + + +#include "ichisize.h" +#include "mode.h" + +/* Common definitions -- do not change */ + +/* SDF treatment */ +#define MAX_SDF_HEADER 64 /* max length of the SDFile data header */ +#define MAX_SDF_VALUE 255 /* max lenght of the SDFile data value */ + +/* size resrictions */ +#define ATOM_EL_LEN 6 /* length of atom name string including zero termination */ +#define ATOM_INFO_LEN 36 /* inf_ATOM output string ^123Al^+2H12..(+)/999/999/999/999: 32 chars */ +#define MAXVAL 20 /* max number of bonds per atom */ +#define MAX_STEREO_BONDS 3 /* max number of stereogenic bonds per atom */ +#define NUM_H_ISOTOPES 3 /* number of hydrogen isotopes: protium, deuterium, tritium */ +#define ATW_H 1 /* hydrogen atomic weight */ + +/* input bond type definition */ +#define MIN_INPUT_BOND_TYPE 1 +#define MAX_INPUT_BOND_TYPE 4 + +#define BOND_TYPE_SINGLE 1 +#define BOND_TYPE_DOUBLE 2 +#define BOND_TYPE_TRIPLE 3 +#define BOND_TYPE_ALTERN 4 + +#define STEREO_SNGL_UP 1 +#define STEREO_SNGL_EITHER 4 +#define STEREO_SNGL_DOWN 6 +#define STEREO_DBLE_EITHER 3 + + +/* MOlfile */ +#define INPUT_STEREO_SNGL_UP 1 +#define INPUT_STEREO_SNGL_EITHER 4 +#define INPUT_STEREO_SNGL_DOWN 6 +#define INPUT_STEREO_DBLE_EITHER 3 + +/* +#define BOND_MARK_ODD 0x10 +#define BOND_MARK_EVEN 0x20 +*/ +#define BOND_MARK_PARITY 0x30 +#define BOND_MARK_HIGHLIGHT 0x40 /* highlight equivalent components */ + +#define BOND_MARK_ODD '-' +#define BOND_MARK_EVEN '+' +#define BOND_MARK_UNDF '?' +#define BOND_MARK_UNKN 'u' +#define BOND_MARK_ERR '*' + +#define SALT_DONOR_H 1 +#define SALT_DONOR_Neg 2 +#define SALT_ACCEPTOR 4 +#define SALT_p_DONOR 8 /* >C-SH */ +#define SALT_p_ACCEPTOR 16 /* >C-S(-) */ +#define SALT_DONOR_ALL (SALT_DONOR_Neg | SALT_DONOR_H | SALT_p_ACCEPTOR | SALT_p_DONOR) +#define SALT_DONOR_Neg2 (SALT_DONOR_Neg | SALT_p_ACCEPTOR) +#define SALT_DONOR_H2 (SALT_DONOR_H | SALT_p_DONOR) +#define SALT_DONOR (SALT_DONOR_Neg | SALT_DONOR_H) + +#define SALT_SELECTED 32 + +/* radical definitions */ +#define RADICAL_SINGLET 1 +#define RADICAL_DOUBLET 2 +#define RADICAL_TRIPLET 3 + +/* metal definition */ +#define METAL 1 /* definition of an element: lowest valence */ +#define METAL2 3 /* definition of an element: lowest and next to it valence */ +#define IS_METAL 3 /* metal bitmap */ +/* isotopic shift */ +#define ZERO_ATW_DIFF 127 /* mark mass of the most abundant isotope */ + +/* other types */ + +#define UCINT (int)(unsigned char) + +#ifndef INCHI_US_CHAR_DEF +typedef signed char S_CHAR; +typedef unsigned char U_CHAR; +#define INCHI_US_CHAR_DEF +#endif + +#ifndef INCHI_US_SHORT_DEF +typedef signed short S_SHORT; +typedef unsigned short U_SHORT; +#define INCHI_US_SHORT_DEF +#endif + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + + +/* allocator */ +#ifndef inchi_malloc +void *inchi_malloc(size_t c); +#endif +#ifndef inchi_calloc +void *inchi_calloc(size_t c, size_t n); +#endif +#ifndef inchi_free +void inchi_free(void *p); +#endif + + + +/* sorting etc */ + +void inchi_swap ( char *a, char *b, size_t width ); + +int insertions_sort( void *pCG, + void *base, size_t num, size_t width, int ( *compare )(const void *e1, const void *e2, void *) ); +int insertions_sort_AT_NUMBERS( void *pCG, + AT_NUMB *base, int num, int ( *compare )(const void *e1, const void *e2, void *) ); +/* +int insertions_sort( void *base, size_t num, size_t width, int ( *compare )(const void *e1, const void *e2 ) ); +int insertions_sort_AT_NUMBERS( AT_NUMB *base, int num, int ( *compare )(const void *e1, const void *e2 ) ); +*/ + + +/* min-max */ + +#define inchi_max(a,b) (((a)>(b))?(a):(b)) +#define inchi_min(a,b) (((a)<(b))?(a):(b)) + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + + +#endif /* _INCOMDEF_H_ */ diff --git a/INCHI-1-SRC/INCHI_BASE/src/inpdef.h b/INCHI-1-SRC/INCHI_BASE/src/inpdef.h new file mode 100644 index 0000000..1eada2e --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/inpdef.h @@ -0,0 +1,658 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _INPDEF_H_ +#define _INPDEF_H_ + + +/* input/output format */ + + +#include "mode.h" +#include "ichidrp.h" +#include "mol_fmt.h" + + +#define CLOSING_STARRED_SRU_IS_A_MUST 1 +#define ALLOW_CLOSING_SRU_VIA_HIGHER_ORDER_BOND 1 +#define ALLOW_CLOSING_SRU_VIA_DIRADICAL 1 + +#define CLOSING_SRU_NOT_APPLICABLE 0 +#define CLOSING_SRU_RING 1 +#define CLOSING_SRU_HIGHER_ORDER_BOND 2 +#define CLOSING_SRU_DIRADICAL 3 + +#define bDrawingLabelLeftShift endpoint /* for drawing only */ +typedef S_SHORT ST_CAP_FLOW; + +/* inp_ATOM::at_type */ +#define ATT_NONE 0x0000 +#define ATT_ACIDIC_CO 0x0001 +#define ATT_ACIDIC_S 0x0002 +#define ATT_OO 0x0004 +#define ATT_ZOO 0x0008 +#define ATT_NO 0x0010 +#define ATT_N_O 0x0020 +#define ATT_ATOM_N 0x0040 +#define ATT_ATOM_P 0x0080 +#define ATT_OTHER_NEG_O 0x0100 +#define ATT_OTHER_ZO 0x0200 /* -Z=O or =Z=O */ +#define ATT_OH_MINUS 0x0400 /* OH(-), O=O,S,Se,Te */ +#define ATT_O_PLUS 0x0800 /* -OH2(+), =OH(+), -OH(+)-, OH3(+), =O(+)-, etc; O=O,S,Se,Te */ +#define ATT_PROTON 0x1000 +#define ATT_HalAnion 0x2000 +#define ATT_HalAcid 0x4000 +#if ( FIX_NP_MINUS_BUG == 1 ) +#define ATT_NP_MINUS_V23 0x8000 /* =N(-) or =P(-) where = previously was triple */ +#endif + +#define AT_FLAG_ISO_H_POINT 0x01 /* may have isotopic H */ + +#define PERIODIC_NUMBER_H 1 + +#ifndef NUMH +#define NUM_ISO_H(AT,N) (AT[N].num_iso_H[0]+AT[N].num_iso_H[1]+AT[N].num_iso_H[2]) +#define NUMH(AT,N) (AT[N].num_H+NUM_ISO_H(AT,N)) +#endif + +#define FlagSC_0D 1 /* bUsed0DParity */ +#define FlagSB_0D 2 /* bUsed0DParity */ + +#define SB_PARITY_FLAG 0x38 /* mask for disconnected metal parity if it is different */ +#define SB_PARITY_SHFT 3 /* number of right shift bits to get disconnected metal parity */ +#define SB_PARITY_MASK 0x07 +#define SB_PARITY_1(X) (X & SB_PARITY_MASK) /* refers to connected structure */ +#define SB_PARITY_2(X) (((X) >> SB_PARITY_SHFT) & SB_PARITY_MASK) /* refers to connected structure */ + + + +typedef struct tagInputAtom +{ + char elname[ATOM_EL_LEN]; /* chem. element name */ + U_CHAR el_number; /* number of the element in the Periodic Table */ + AT_NUMB neighbor[MAXVAL]; /* positions (from 0) of the neighbors in the inp_ATOM array */ + AT_NUMB orig_at_number; /* original atom number */ + AT_NUMB orig_compt_at_numb; /* atom number within the component before terminal H removal */ + S_CHAR bond_stereo[MAXVAL]; /* 1=Up,4=Either,6=Down; this atom is at the pointing wedge, + negative => on the opposite side; 3=Either double bond */ + U_CHAR bond_type[MAXVAL]; /* 1..4; 4="aromatic", should be discouraged on input */ + + S_CHAR valence; /* actually it is coordination number, CN; + number of bonds = number of neighbors */ + S_CHAR chem_bonds_valence; /* actually it is what usually called valence; + sum of bond types (type 4 needs special treatment) */ + S_CHAR num_H; /* number of implicit hydrogens, including D and T */ + S_CHAR num_iso_H[NUM_H_ISOTOPES]; /* number of implicit 1H, 2H(D), 3H(T) < 16 */ + S_CHAR iso_atw_diff; /* =0 => natural isotopic abundances */ + /* >0 => (mass) - (mass of the most abundant isotope) + 1 */ + /* <0 => (mass) - (mass of the most abundant isotope) */ + S_CHAR charge; /* charge */ + S_CHAR radical; /* RADICAL_SINGLET, RADICAL_DOUBLET, or RADICAL_TRIPLET */ + S_CHAR bAmbiguousStereo; + S_CHAR cFlags; /* AT_FLAG_ISO_H_POINT */ + AT_NUMB at_type; /* ATT_NONE, ATT_ACIDIC */ + AT_NUMB component; /* number of the structure component > 0 */ + AT_NUMB endpoint; /* id of a tautomeric group */ + AT_NUMB c_point; /* id of a positive charge group */ + double x; + double y; + double z; + /* cml 0D parities */ + S_CHAR bUsed0DParity; /* bit=1 => stereobond; bit=2 => stereocenter */ + /* cml tetrahedral parity */ + S_CHAR p_parity; + AT_NUMB p_orig_at_num[MAX_NUM_STEREO_ATOM_NEIGH]; + /* cml bond parities */ + S_CHAR sb_ord[MAX_NUM_STEREO_BONDS]; /* stereo bond/neighbor ordering number, starts from 0 */ + /* neighbors on both sides of stereobond have same sign=> trans/T/E, diff. signs => cis/C/Z */ + S_CHAR sn_ord[MAX_NUM_STEREO_BONDS]; /* ord. num. of the neighbor adjacent to the SB; starts from 0; + -1 means removed explicit H */ + /* neighbors on both sides of stereobond have same parity => trans/T/E/2, diff. parities => cis/C/Z/1 */ + S_CHAR sb_parity[MAX_NUM_STEREO_BONDS]; + AT_NUMB sn_orig_at_num[MAX_NUM_STEREO_BONDS]; /* orig. at number of sn_ord[] neighbors */ + +#if ( FIND_RING_SYSTEMS == 1 ) + S_CHAR bCutVertex; + AT_NUMB nRingSystem; + AT_NUMB nNumAtInRingSystem; + AT_NUMB nBlockSystem; + +#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) + AT_NUMB nDistanceFromTerminal; /* terminal atom or ring system has 1, next has 2, etc. */ +#endif + +#endif +} inp_ATOM; + + +/* Polymer representation type */ +#define NO_POLYMER -1 +#define POLYMER_REPRESENTATION_SOURCE_BASED 1 +#define POLYMER_REPRESENTATION_STRUCTURE_BASED 2 +#define POLYMER_REPRESENTATION_MIXED 3 +#define POLYMER_REPRESENTATION_UNRECOGNIZED 4 + +#define POLYMER_COMPOSITION_SIMPLE_POLYMER 1 +#define POLYMER_COMPOSITION_COPOLYMER 1 + + + +#define POLYMER_STY_NON 0 +#define POLYMER_STY_SRU 1 +#define POLYMER_STY_MON 2 +#define POLYMER_STY_COP 3 +#define POLYMER_STY_MOD 4 +#define POLYMER_STY_CRO 5 +#define POLYMER_STY_MER 6 + +#define POLYMER_SST_NON 0 +#define POLYMER_SST_ALT 1 +#define POLYMER_SST_RAN 2 +#define POLYMER_SST_BLK 3 + +#define POLYMER_CONN_NON 0 +#define POLYMER_CONN_HT 1 +#define POLYMER_CONN_HH 2 +#define POLYMER_CONN_EU 3 +/* OrigAtDataPolymerUnit.real_kind values */ +#define POLYMER_UNIT_KIND_UNKNOWN 0 +#define POLYMER_UNIT_KIND_SOURCE_BASED_COMPONENT 10 +#define POLYMER_UNIT_KIND_SOURCE_BASED_POLYMER 11 +#define POLYMER_UNIT_KIND_SOURCE_BASED_COPOLYMER 12 +#define POLYMER_UNIT_KIND_SOURCE_BASED_RAN_COPOLYMER 13 +#define POLYMER_UNIT_KIND_SOURCE_BASED_ALT_COPOLYMER 14 +#define POLYMER_UNIT_KIND_SOURCE_BASED_BLK_COPOLYMER 15 +#define POLYMER_UNIT_KIND_STRUCTURE_BASED_SRU_WITH_TWO_STARS 21 +#define POLYMER_UNIT_KIND_STRUCTURE_BASED_SRU_WITH_ONE_STAR 22 +#define POLYMER_UNIT_KIND_STRUCTURE_BASED_SRU_WITH_NO_STARS 23 +#define POLYMER_UNIT_KIND_SRU_EMBEDDING_STRUCTURE_BASED_SRUS 25 +#define POLYMER_UNIT_KIND_STRUCTURE_BASED_SRU_MOD 26 +#define POLYMER_UNIT_KIND_STRUCTURE_BASED_SRU_CRO 27 +#define POLYMER_UNIT_KIND_STRUCTURE_BASED_SRU_MER 28 + +/* Extended input supporting v. 1.05 extensions: V3000; polymers */ +typedef struct OrigAtDataPolymerUnit +{ + int id; /* it is what is called 'Sgroup number' in CTFILE */ + int type; /* type as by MDL format (STY) */ + int subtype; /* subtype as by MDL format (SST) */ + int conn; /* connection scheme as by MDL format (SCN) */ + int label; /* it is what is called 'unique Sgroup identifier' in CTFILE */ + int na; /* number of atoms in the unit */ + int nb; /* number of bonds in the unit */ + int real_kind; /* actual meaning of the unit, to be deduced */ + int disjoint; /* =1 if > 1 connected_components */ + int closeable;/* =1 if CRU phase shift is applicable and should be considerd */ + int already_closed; + /* =1 i CRU alredy was phase_shift' treated */ + double xbr1[4]; /* bracket ends coordinates (SDI) */ + double xbr2[4]; /* bracket ends coordinates (SDI) */ + char smt[80]; /* Sgroup Subscript (SMT) */ + int representation; + int star1; + int star_partner1; + int star2; + int star_partner2; + int *alist; /* list of atoms in the unit (SAL) */ + int *blist; /* bonds in the unit as list [atom1, atom2; atom1, atom2,.. ] */ + /* for crosing bonds (S) */ + int maxpsbonds; /* max (allocd) number of phase_shift involved bonds */ + int npsbonds; /* number of phases_shift involved bonds */ + int **psbonds; /* list of those bonds */ +} OrigAtDataPolymerUnit; + + +typedef struct OrigAtDataPolymer +{ + OrigAtDataPolymerUnit** units; /* array of pointers to units */ + int n; + int n_star_atoms; /* numbers of star atoms */ + int *star_atoms; /* numbers of star atoms */ + int valid; /* -1 not parsed + 0 parsed, invalid + 1 parsed, valid */ + int really_do_phase_shift; + int representation; + int is_in_reconn; +} OrigAtDataPolymer; + + +typedef struct OrigAtDataPolymerAtomProps +{ + int erank; /* rank of element; 2 - C, >2 - rank of heteroatom in chain, + O > S > Se > Te > N ...., Rule 4 */ + int ring_erank; /* 0 - not ring or just carbocycle, + >2 - rank of senior heteroatom in this cycle + according to Rule 2 ( N > O >... ) */ + int ring_num; + int ring_size; /* 0 or ring system size */ + /* that is: + ring_erank != 0 heterocycle of ring_size + ring_erank == 0 && ring_size > 0 carbocycle of ring_size + */ +} OrigAtDataPolymerAtomProps; + +/* Extended input supporting v. 1.05 extensions: V3000; polymers */ +typedef struct OrigAtDataV3000 +{ + int n_non_star_atoms; + int n_star_atoms; + int *atom_index_orig; /* index as supplied for atoms */ + int *atom_index_fin; /* = index or -1 for star atom */ + int n_sgroups; /* currently, we do not use this. */ + int n_3d_constraints; /* currently, we do not use this. */ + int n_collections; + int n_non_haptic_bonds; + int n_haptic_bonds; + int **lists_haptic_bonds;/* haptic_bonds[i] is pointer to int* which contains: */ + /* bond_type, non-star atom number, nendpts, then endpts themselves */ + /* Enhanced stereo */ + int n_steabs; + int **lists_steabs; /* steabs[k][0] - not used */ + /* steabs[k][1] - number of members in collection */ + /* steabs[k][2..] - member atom numbers */ + int n_sterel; + int **lists_sterel; /* sterel[k][0] - n from "STERELn" tag */ + /* sterel[k][1] - number of members in collection */ + /* sterel[k][2..] - member atom numbers */ + int n_sterac; + int **lists_sterac; /* sterac[k][0] - n from "STERACn" tag */ + /* sterac[k][1] - number of members in collection */ + /* sterac[k][0] - number from "STERACn" tag */ +} OrigAtDataV3000; + + + + +typedef struct tagOrigAtom +{ + /* initially filled out by CreateOrigInpDataFromMolfile() */ + /* may be changed by disconnecting salts and disconnecting metals */ + + inp_ATOM *at; + int num_dimensions; + int num_inp_bonds; + int num_inp_atoms; + + /* may be changed by disconnecting salts and disconnecting metals */ + int num_components; /* set by MarkDisconnectedComponents() and disconnecting metals */ + int bDisconnectSalts; /* whether salt disconnection is possible */ + int bDisconnectCoord; /* 0 if no disconnection needed else (Num Implicit H to disconnect)+1 */ + +#if ( bRELEASE_VERSION == 0 ) + int bExtract; +#endif + + AT_NUMB *nCurAtLen; /* has max_num_components elements */ + AT_NUMB *nOldCompNumber; /* 0 or component number in previous numbering */ + int nNumEquSets; /* number of found component equivalence sets */ + AT_NUMB *nEquLabels; /* num_inp_atoms elements, value>0 marks atoms in the set #value */ + AT_NUMB *nSortedOrder; /* num_components elements, values = 1..num_components; only if num_components > 1 */ + int bSavedInINCHI_LIB[INCHI_NUM]; + int bPreprocessed[INCHI_NUM]; + MOL_COORD *szCoord; + /* v. 1.05 extensions */ + OrigAtDataPolymer *polymer; + OrigAtDataV3000 *v3000; +} ORIG_ATOM_DATA; + + + +typedef struct tagOriginalStruct +{ + int num_atoms; + char *szAtoms; + char *szBonds; + char *szCoord; + /* v. 1.05 extensions */ + OrigAtDataPolymer *polymer; /* use pointer copy from orig_inp_data, do not free after use! */ + OrigAtDataV3000 *v3000; /* use pointer copy from orig_inp_data, do not free after use! */ +} ORIG_STRUCT; + + + +typedef struct tagAtomParmsForDrawing +{ + char at_string[ATOM_INFO_LEN]; + int DrawingLabelLeftShift; + int DrawingLabelLength; + AT_NUMB nCanonNbr; /* if zero then do not use all data for the atom */ + AT_NUMB nCanonEquNbr; + AT_NUMB nTautGroupCanonNbr; + AT_NUMB nTautGroupEquNbr; + S_CHAR cFlags; /* AT_FLAG_ISO_H_POINT */ +#ifdef DISPLAY_DEBUG_DATA + int nDebugData; +#endif + S_CHAR cHighlightTheAtom; + S_CHAR cStereoCenterParity; + S_CHAR cStereoBondParity[MAX_STEREO_BONDS]; + S_CHAR cStereoBondWarning[MAX_STEREO_BONDS]; + S_CHAR cStereoBondNumber[MAX_STEREO_BONDS]; +} inf_ATOM; + + + +#define INF_STEREO_ABS 0x0001 +#define INF_STEREO_REL 0x0002 +#define INF_STEREO_RAC 0x0004 +#define INF_STEREO_NORM 0x0008 +#define INF_STEREO_INV 0x0010 +#define INF_STEREO 0x0020 +#define INF_STEREO_ABS_REL_RAC (INF_STEREO_ABS | INF_STEREO_REL | INF_STEREO_RAC) +#define INF_STEREO_NORM_INV (INF_STEREO_NORM | INF_STEREO_INV) + +#define MAX_LEN_REMOVED_PROTONS 128 + + + +typedef struct tagInfoAtomData +{ + inf_ATOM *at; + int num_at; + AT_NUMB StereoFlags; + AT_NUMB num_components; + AT_NUMB *pStereoFlags; + + int nNumRemovedProtons; + int num_removed_iso_H; /* number of exchangable isotopic H */ + NUM_H num_iso_H[NUM_H_ISOTOPES]; /* number of exchangable isotopic H */ + char szRemovedProtons[MAX_LEN_REMOVED_PROTONS]; +} INF_ATOM_DATA; + + + +typedef struct tagInputAtomData +{ + inp_ATOM *at; + inp_ATOM *at_fixed_bonds; /* tautomeric case, added or removed H */ + int num_at; + int num_removed_H; + int num_bonds; + int num_isotopic; + int bExists; + int bDeleted; + int bHasIsotopicLayer; + int bTautomeric; + int bTautPreprocessed; + int nNumRemovedProtons; + NUM_H nNumRemovedProtonsIsotopic[NUM_H_ISOTOPES]; /* isotopic composition of removed protons, not included in num_iso_H[] */ + NUM_H num_iso_H[NUM_H_ISOTOPES]; /* isotopic H on tautomeric atoms and those in nIsotopicEndpointAtomNumber */ + INCHI_MODE bTautFlags; + INCHI_MODE bTautFlagsDone; + INCHI_MODE bNormalizationFlags; +} INP_ATOM_DATA; + +typedef INP_ATOM_DATA INP_ATOM_DATA2[TAUT_NUM]; + + + +typedef struct tagNormCanonFlags +{ + INCHI_MODE bTautFlags[INCHI_NUM][TAUT_NUM]; + INCHI_MODE bTautFlagsDone[INCHI_NUM][TAUT_NUM]; + INCHI_MODE bNormalizationFlags[INCHI_NUM][TAUT_NUM]; + int nCanonFlags[INCHI_NUM][TAUT_NUM]; +} NORM_CANON_FLAGS; + + + +typedef struct tagCompositeAtomData +{ + inp_ATOM *at; + int num_at; + int num_removed_H; + int num_bonds; + int num_isotopic; + int bExists; + int bDeleted; /* unused */ + int bHasIsotopicLayer; + int bTautomeric; + int nNumRemovedProtons; + NUM_H nNumRemovedProtonsIsotopic[NUM_H_ISOTOPES]; /* isotopic composition of removed protons, not included in num_iso_H[] */ + NUM_H num_iso_H[NUM_H_ISOTOPES]; /* isotopic H on tautomeric atoms and those in nIsotopicEndpointAtomNumber */ + + AT_NUMB *nOffsetAtAndH; + int num_components; +} COMP_ATOM_DATA; + +/* +typedef COMP_ATOM_DATA COMP_ATOM_DATA3[TAUT_NUM+1]; +*/ + +#define ADD_LEN_STRUCT_FPTRS 100 /* allocation increments */ + +typedef long INCHI_FPTR; + + + +typedef struct tagStructFptrs +{ + INCHI_FPTR *fptr; /* input: fptr[cur_fptr] = file pointer to the structure to read */ + /* output: fptr[cur_fptr+1] = file pointer to the next structure or EOF */ + int len_fptr; /* allocated length of fptr */ + int cur_fptr; /* input: k-1 to read the kth struct, k = 1, 2, 3,...; left unchanged; struct number := cur_fptr+1 */ + int max_fptr; /* length of the filled out portion of fptr */ +} STRUCT_FPTRS; + + + +#define FLAG_INP_AT_CHIRAL 1 +#define FLAG_INP_AT_NONCHIRAL 2 +#define FLAG_SET_INP_AT_CHIRAL 4 +#define FLAG_SET_INP_AT_NONCHIRAL 8 + +#define FLAG_SET_INP_LARGE_ALLOWED 16 +#define FLAG_SET_INP_POLYMERS_RECOGNIZED 32 + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + + + +int CreateOrigInpDataFromMolfile( INCHI_IOSTREAM *inp_file, + ORIG_ATOM_DATA *orig_at_data, + int bMergeAllInputStructures, + int bGetOrigCoord, + int bDoNotAddH, + const char *pSdfLabel, + char *pSdfValue, + long *lSdfId, + long *lMolfileNumber, + INCHI_MODE *pInpAtomFlags, + int *err, + char *pStrErr ); + +int InchiToOrigAtom( INCHI_IOSTREAM *infile, + ORIG_ATOM_DATA *orig_at_data, + int bMergeAllInputStructures, + int bGetOrigCoord, + int bDoNotAddH, + int vABParityUnknown, + INPUT_TYPE nInputType, + char *pSdfLabel, + char *pSdfValue, + long *lSdfId, + INCHI_MODE *pInpAtomFlags, + int *err, + char *pStrErr ); + +int MarkDisconnectedComponents( ORIG_ATOM_DATA *orig_at_data, + int bProcessOldCompNumbers ); + +int DisconnectSalts( ORIG_ATOM_DATA *orig_inp_data, + int bDisconnect ); +int DisconnectMetals( ORIG_ATOM_DATA *orig_inp_data, + int bCheckMetalValence, + INCHI_MODE *bTautFlagsDone ); +int bMayDisconnectMetals( ORIG_ATOM_DATA *orig_inp_data, + int bCheckMetalValence, + INCHI_MODE *bTautFlagsDone ); +int bHasMetalAtom( ORIG_ATOM_DATA *orig_inp_data ); +int FixAdjacentRadicals( int num_inp_atoms, + inp_ATOM *at ); /* FIX_ADJ_RAD == 1 */ +int fix_odd_things( int num_atoms, + inp_ATOM *at, + int bFixBug, + int bFixNonUniformDraw ); +int post_fix_odd_things( int num_atoms, + inp_ATOM *at ); +int remove_ion_pairs( int num_atoms, + inp_ATOM *at ); +int bFoundFeature( inp_ATOM *at, int num_atoms ); + +int OrigAtData_WriteToSDfile( const ORIG_ATOM_DATA *inp_at_data, + INCHI_IOSTREAM * fcb, + const char* name, + const char* comment, + int bChiralFlag, + int bAtomsDT, + const char *szLabel, const char *szValue); + +void FreeInpAtom( inp_ATOM **at ); +void FreeInfAtom( inf_ATOM **at ); +void FreeOrigAtData( ORIG_ATOM_DATA *orig_at_data ); +void FreeExtOrigAtData( OrigAtDataPolymer *pd, OrigAtDataV3000 *v3k ); +void FreeInpAtomData( INP_ATOM_DATA *inp_at_data ); +void FreeCompAtomData( COMP_ATOM_DATA *inp_at_data ); +void FreeInfoAtomData( INF_ATOM_DATA *inf_at_data ); + + +int OrigAtDataPolymer_ParseAndValidate( ORIG_ATOM_DATA *orig_at_data, int allow_polymers, char *pStrErr ); +int OrigAtDataPolymer_GetRepresentation( OrigAtDataPolymer *p ); +int OrigAtDataPolymer_CyclizeCloseableUnits( ORIG_ATOM_DATA *orig_at_data, char *pStrErr ); +void OrigAtDataPolymer_CollectPhaseShiftBonds( ORIG_ATOM_DATA *where_to_look, + COMP_ATOM_DATA *composite_norm_data, + int *err, char *pStrErr ); +int OrigAtDataPolymer_PrepareWorkingSet( OrigAtDataPolymer *p, + int *cano_nums, + int *compnt_nums, + OrigAtDataPolymerUnit** units2, + int *unum ); +void OrigAtDataPolymer_Free( OrigAtDataPolymer *p ); +void OrigAtDataPolymer_DebugTrace( OrigAtDataPolymer *p ); +OrigAtDataPolymerUnit * OrigAtDataPolymerUnit_New( int maxatoms, + int maxbonds, + int id, int label, + int type, int subtype, + int conn, char *smt, + int na, INT_ARRAY *alist, + int nb, INT_ARRAY *blist, + int npsbonds, int **psbonds ); +OrigAtDataPolymerUnit * OrigAtDataPolymerUnit_CreateCopy( OrigAtDataPolymerUnit *u); +void OrigAtDataPolymerUnit_Free( OrigAtDataPolymerUnit *unit ); +void OrigAtDataPolymerUnit_DebugTrace( OrigAtDataPolymerUnit *unit ); +void OrigAtDataPolymerUnit_FindStarsAndPartners( OrigAtDataPolymerUnit *unit, + ORIG_ATOM_DATA *orig_at_data, + int *err, char *pStrErr ); +void OrigAtDataPolymerUnit_DetachStarsAndConnectStarPartners( OrigAtDataPolymerUnit *unit, + ORIG_ATOM_DATA *orig_at_data, + int *err, char *pStrErr ); +void OrigAtDataPolymerUnit_PrepareToPhaseShift( OrigAtDataPolymerUnit *unit, + ORIG_ATOM_DATA *orig_at_data, + int *err, char *pStrErr ); +void OrigAtDataPolymerUnit_PreselectPSBonds( OrigAtDataPolymerUnit *unit, + ORIG_ATOM_DATA *orig_at_data, + int *err, char *pStrErr ); +void OrigAtDataPolymerUnit_DelistIntraRingPSBonds( OrigAtDataPolymerUnit *unit, + ORIG_ATOM_DATA *orig_at_data, + int *err, char *pStrErr ); +void OrigAtDataPolymerUnit_DelistMultiplePSBonds( OrigAtDataPolymerUnit *unit, + ORIG_ATOM_DATA *orig_at_data, + COMP_ATOM_DATA *composite_norm_data, + int *err, char *pStrErr ); +void OrigAtDataPolymerUnit_SortPSBonds( OrigAtDataPolymerUnit *u, OrigAtDataPolymerAtomProps *aprops, int *bnum ); +int OrigAtDataPolymerUnit_ComparePSBonds( int* b1, int* b2, OrigAtDataPolymerAtomProps *aprops ); +int OrigAtDataPolymerUnit_CompareAtomLists( OrigAtDataPolymerUnit* u1, OrigAtDataPolymerUnit* u2); +int OrigAtDataPolymerUnit_CompareAtomListsMod( OrigAtDataPolymerUnit* u1, OrigAtDataPolymerUnit* u2); +int OrigAtDataPolymerUnit_OrderBondAtomsAndBondsThemselves( OrigAtDataPolymerUnit *u, int n_stars, int *stars ); +int OrigAtDataPolymerUnit_HasMetal( OrigAtDataPolymerUnit *u, inp_ATOM *at); + +int FixUnkn0DStereoBonds(inp_ATOM *at, int num_at); +inf_ATOM *CreateInfAtom( int num_atoms ); +inp_ATOM *CreateInpAtom( int num_atoms ); +int CreateInfoAtomData( INF_ATOM_DATA *inf_at_data, + int num_atoms, + int num_components ); +int AllocateInfoAtomData( INF_ATOM_DATA *inf_at_data, + int num_atoms, + int num_components ); +int DuplicateInfoAtomData( INF_ATOM_DATA *inf_at_data_to, + const INF_ATOM_DATA *inf_at_data_from); +int CreateInpAtomData( INP_ATOM_DATA *inp_at_data, + int num_atoms, + int create_at_fixed_bonds ); +int CreateCompAtomData( COMP_ATOM_DATA *inp_at_data, + int num_atoms, + int num_components, + int bIntermediateTaut ); + +#ifndef COMPILE_ANSI_ONLY +int DisplayInputStructure( char *szOutputString, + inp_ATOM *at, + INF_ATOM_DATA *inf_at_data, + int num_at, + DRAW_PARMS *dp ); +#endif + +void PrintFileName( const char *fmt, + FILE *out_file, + const char *szFname ); + +void MySleep( unsigned long ms ); + +#ifndef __ICHITIME_H__ +struct tagInchiTime; +struct tagINCHI_CLOCK; +int bInchiTimeIsOver( struct tagINCHI_CLOCK *ic, + struct tagInchiTime *TickEnd ); +#endif + +int ReconcileAllCmlBondParities( inp_ATOM *at, int num_atoms, int bDisconnected ); + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + +#endif /* _INPDEF_H_ */ diff --git a/INCHI-1-SRC/INCHI_BASE/src/ixa.h b/INCHI-1-SRC/INCHI_BASE/src/ixa.h new file mode 100644 index 0000000..e644b03 --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/ixa.h @@ -0,0 +1,574 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef __IXA_H__ +#define __IXA_H__ + + +/****************************************************************************/ +/* InChI Extensible API Object Handles */ +/* + * These are the "handles" which can be used to refer to "Objects" + * used in the InChI Extensible API. + * + * To ensure that each handle has a different formal type, each is declared + * as a pointer to a C struct containing a dummy integer field (which is in + * fact not used). In practice, the values are simply cast from int, but + * this approach provides greater type security, and prevents handles for + * different objects getting mixed up. +*/ + +typedef struct {int dummy;}* IXA_STATUS_HANDLE; + +typedef struct {int dummy;}* IXA_MOL_HANDLE; + +typedef struct {int dummy;}* IXA_INCHIBUILDER_HANDLE; + +typedef struct {int dummy;}* IXA_INCHIKEYBUILDER_HANDLE; + + +/****************************************************************************/ +/* Types for Atom, Bond and Stereo Descriptor Identifiers */ +/* + * These types are for the identifiers for individual atoms, bonds and + * stereodescriptors in an IXA Molecule Object. + * + * To ensure that each identifier has a different formal type, each is + * declared as a pointer to a C struct containing a dummy integer field + * (which is in fact not used). In practice, the values are simply cast + * from int, but this approach provides greater type security, and + * prevents different sorts of identifier from getting mixed up. +*/ + +typedef struct {int dummy;}* IXA_ATOMID; + +typedef struct {int dummy;}* IXA_BONDID; + +typedef struct {int dummy;}* IXA_STEREOID; + + +/****************************************************************************/ +/* Constants and enumerated types */ + +#define IXA_ATOMID_INVALID ((IXA_ATOMID)0) + +#define IXA_ATOMID_IMPLICIT_H ((IXA_ATOMID)-1) + +#define IXA_BONDID_INVALID ((IXA_BONDID)0) + +#define IXA_STEREOID_INVALID ((IXA_STEREOID)0) + +#define IXA_ATOM_NATURAL_MASS 0 + +#define IXA_EXT_POLYMER_INVALID (-1) +#define IXA_EXT_V3000_INVALID (-1) + +typedef enum +{ + IXA_STATUS_SUCCESS, + IXA_STATUS_WARNING, + IXA_STATUS_ERROR +} IXA_STATUS; + +typedef enum +{ + IXA_FALSE = 0, + IXA_TRUE = 1 +} IXA_BOOL; + +typedef enum +{ + IXA_ATOM_RADICAL_NONE = 0, + IXA_ATOM_RADICAL_SINGLET = 1, + IXA_ATOM_RADICAL_DOUBLET = 2, + IXA_ATOM_RADICAL_TRIPLET = 3 +} IXA_ATOM_RADICAL; + +typedef enum +{ + IXA_BOND_TYPE_SINGLE = 1, + IXA_BOND_TYPE_DOUBLE = 2, + IXA_BOND_TYPE_TRIPLE = 3, + IXA_BOND_TYPE_AROMATIC = 4 +} IXA_BOND_TYPE; + +typedef enum +{ + IXA_BOND_WEDGE_NONE = 0, + IXA_BOND_WEDGE_UP = 1, + IXA_BOND_WEDGE_DOWN = 2, + IXA_BOND_WEDGE_EITHER = 3 +} IXA_BOND_WEDGE; + +typedef enum +{ + IXA_DBLBOND_CONFIG_PERCEIVE = 0, + IXA_DBLBOND_CONFIG_EITHER = 1 +} IXA_DBLBOND_CONFIG; + +typedef enum +{ + IXA_STEREO_TOPOLOGY_INVALID = 0, + IXA_STEREO_TOPOLOGY_TETRAHEDRON = 2, + IXA_STEREO_TOPOLOGY_RECTANGLE = 3, + IXA_STEREO_TOPOLOGY_ANTIRECTANGLE = 4 +} IXA_STEREO_TOPOLOGY; + +typedef enum +{ + IXA_STEREO_PARITY_NONE = 0, + IXA_STEREO_PARITY_ODD = 1, + IXA_STEREO_PARITY_EVEN = 2, + IXA_STEREO_PARITY_UNKNOWN = 3 +} IXA_STEREO_PARITY; + +typedef enum +{ + IXA_INCHIBUILDER_OPTION_NewPsOff, + IXA_INCHIBUILDER_OPTION_DoNotAddH, + IXA_INCHIBUILDER_OPTION_SUU, + IXA_INCHIBUILDER_OPTION_SLUUD, + IXA_INCHIBUILDER_OPTION_FixedH, + IXA_INCHIBUILDER_OPTION_RecMet, + IXA_INCHIBUILDER_OPTION_KET, + IXA_INCHIBUILDER_OPTION_15T, + IXA_INCHIBUILDER_OPTION_SaveOpt, + IXA_INCHIBUILDER_OPTION_AuxNone, + IXA_INCHIBUILDER_OPTION_WarnOnEmptyStructure, + IXA_INCHIBUILDER_OPTION_LargeMolecules +} IXA_INCHIBUILDER_OPTION; + +typedef enum +{ + IXA_INCHIBUILDER_STEREOOPTION_SAbs, + IXA_INCHIBUILDER_STEREOOPTION_SNon, + IXA_INCHIBUILDER_STEREOOPTION_SRel, + IXA_INCHIBUILDER_STEREOOPTION_SRac, + IXA_INCHIBUILDER_STEREOOPTION_SUCF +} IXA_INCHIBUILDER_STEREOOPTION; + + + +/* Uncomment the next line if old-API coverage is intended - instead of GetINCHIEx(), GetStructFromINCHIEx() */ +/* #define IXA_USES_NON_EX_CORE_API 1 */ + + + + +/****************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/****************************************************************************/ +/* Functions handling IXA Status Objects */ + +EXPIMP_TEMPLATE INCHI_API IXA_STATUS_HANDLE INCHI_DECL IXA_STATUS_Create(); + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_STATUS_Clear(IXA_STATUS_HANDLE hStatus); + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_STATUS_Destroy(IXA_STATUS_HANDLE hStatus); + +EXPIMP_TEMPLATE INCHI_API IXA_BOOL INCHI_DECL IXA_STATUS_HasError(IXA_STATUS_HANDLE hStatus); + +EXPIMP_TEMPLATE INCHI_API IXA_BOOL INCHI_DECL IXA_STATUS_HasWarning(IXA_STATUS_HANDLE hStatus); + +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL IXA_STATUS_GetCount(IXA_STATUS_HANDLE hStatus); + +EXPIMP_TEMPLATE INCHI_API IXA_STATUS INCHI_DECL IXA_STATUS_GetSeverity(IXA_STATUS_HANDLE hStatus, + int vIndex); + +EXPIMP_TEMPLATE INCHI_API const char* INCHI_DECL IXA_STATUS_GetMessage(IXA_STATUS_HANDLE hStatus, + int vIndex); + + +/****************************************************************************/ +/* Functions to Create, Clear and Destroy Molecule Objects */ + +EXPIMP_TEMPLATE INCHI_API IXA_MOL_HANDLE INCHI_DECL IXA_MOL_Create(IXA_STATUS_HANDLE hStatus); + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_MOL_Clear(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule); + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_MOL_Destroy(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule); + + +/****************************************************************************/ +/* Functions Operating on Complete Molecules */ + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_MOL_ReadMolfile(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + const char* pBytes); + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_MOL_ReadInChI(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + const char* pInChI); + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_MOL_SetChiral(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_BOOL vChiral); + +EXPIMP_TEMPLATE INCHI_API IXA_BOOL INCHI_DECL IXA_MOL_GetChiral(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule); + + +/****************************************************************************/ +/* Functions to Add and Define Atoms */ + +EXPIMP_TEMPLATE INCHI_API IXA_ATOMID INCHI_DECL IXA_MOL_CreateAtom(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule); + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_MOL_SetAtomElement(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom, + const char* pElement); + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_MOL_SetAtomAtomicNumber(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom, + int vAtomicNumber); + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_MOL_SetAtomMass(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom, + int vMassNumber); + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_MOL_SetAtomCharge(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom, + int vCharge); + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_MOL_SetAtomRadical(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom, + IXA_ATOM_RADICAL vRadical); + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_MOL_SetAtomHydrogens(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom, + int vHydrogenMassNumber, + int vHydrogenCount); + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_MOL_SetAtomX(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom, + double vX); + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_MOL_SetAtomY(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom, + double vY); + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_MOL_SetAtomZ(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom, + double vZ); + + +/****************************************************************************/ +/* Functions to Add and Define Bonds */ + +EXPIMP_TEMPLATE INCHI_API IXA_BONDID INCHI_DECL IXA_MOL_CreateBond(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom1, + IXA_ATOMID vAtom2); + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_MOL_SetBondType(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_BONDID vBond, + IXA_BOND_TYPE vType); + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_MOL_SetBondWedge(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_BONDID vBond, + IXA_ATOMID vRefAtom, + IXA_BOND_WEDGE vDirection); + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_MOL_SetDblBondConfig(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_BONDID vBond, + IXA_DBLBOND_CONFIG vConfig); + +/*****************************************************************************/ +/* Functions to Add and Define Stereodescriptors */ + +EXPIMP_TEMPLATE INCHI_API IXA_STEREOID INCHI_DECL IXA_MOL_CreateStereoTetrahedron(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vCentralAtom, + IXA_ATOMID vVertex1, + IXA_ATOMID vVertex2, + IXA_ATOMID vVertex3, + IXA_ATOMID vVertex4); + +EXPIMP_TEMPLATE INCHI_API IXA_STEREOID INCHI_DECL IXA_MOL_CreateStereoRectangle(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_BONDID vCentralBond, + IXA_ATOMID vVertex1, + IXA_ATOMID vVertex2, + IXA_ATOMID vVertex3, + IXA_ATOMID vVertex4); + +EXPIMP_TEMPLATE INCHI_API IXA_STEREOID INCHI_DECL IXA_MOL_CreateStereoAntiRectangle(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vCentralAtom, + IXA_ATOMID vVertex1, + IXA_ATOMID vVertex2, + IXA_ATOMID vVertex3, + IXA_ATOMID vVertex4); + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_MOL_SetStereoParity(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_STEREOID vStereo, + IXA_STEREO_PARITY vParity); + + + +/****************************************************************************/ +/* Functions to Navigate Within a Molecule */ + +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL IXA_MOL_GetNumAtoms(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule); + +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL IXA_MOL_GetNumBonds(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule); + +EXPIMP_TEMPLATE INCHI_API IXA_ATOMID INCHI_DECL IXA_MOL_GetAtomId(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + int vAtomIndex); + +EXPIMP_TEMPLATE INCHI_API IXA_BONDID INCHI_DECL IXA_MOL_GetBondId(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + int vBondIndex); + +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL IXA_MOL_GetAtomIndex(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom); + +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL IXA_MOL_GetBondIndex(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_BONDID vBond); + +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL IXA_MOL_GetAtomNumBonds(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom); + +EXPIMP_TEMPLATE INCHI_API IXA_BONDID INCHI_DECL IXA_MOL_GetAtomBond(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom, + int vBondIndex); + +EXPIMP_TEMPLATE INCHI_API IXA_BONDID INCHI_DECL IXA_MOL_GetCommonBond(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom1, + IXA_ATOMID vAtom2); + +EXPIMP_TEMPLATE INCHI_API IXA_ATOMID INCHI_DECL IXA_MOL_GetBondAtom1(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_BONDID vBond); + +EXPIMP_TEMPLATE INCHI_API IXA_ATOMID INCHI_DECL IXA_MOL_GetBondAtom2(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_BONDID vBond); + + +/*****************************************************************************/ +/* Functions to Return Information About Atoms */ + +EXPIMP_TEMPLATE INCHI_API const char* INCHI_DECL IXA_MOL_GetAtomElement(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom); + +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL IXA_MOL_GetAtomAtomicNumber(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom); + +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL IXA_MOL_GetAtomMass(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom); + +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL IXA_MOL_GetAtomCharge(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom); + +EXPIMP_TEMPLATE INCHI_API IXA_ATOM_RADICAL INCHI_DECL IXA_MOL_GetAtomRadical(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom); + +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL IXA_MOL_GetAtomHydrogens(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom, + int vHydrogenMassNumber); + +EXPIMP_TEMPLATE INCHI_API double INCHI_DECL IXA_MOL_GetAtomX(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom); + +EXPIMP_TEMPLATE INCHI_API double INCHI_DECL IXA_MOL_GetAtomY(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom); + +EXPIMP_TEMPLATE INCHI_API double INCHI_DECL IXA_MOL_GetAtomZ(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_ATOMID vAtom); + + +/*****************************************************************************/ +/* Functions to Return Information About Bonds */ + +EXPIMP_TEMPLATE INCHI_API IXA_BOND_TYPE INCHI_DECL IXA_MOL_GetBondType(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_BONDID vBond); + +EXPIMP_TEMPLATE INCHI_API IXA_BOND_WEDGE INCHI_DECL IXA_MOL_GetBondWedge(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_BONDID vBond, + IXA_ATOMID vRefAtom); + +EXPIMP_TEMPLATE INCHI_API IXA_DBLBOND_CONFIG INCHI_DECL IXA_MOL_GetDblBondConfig(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_BONDID vBond); + +/*****************************************************************************/ +/* Functions to return Information About Stereodescriptors */ + + +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL IXA_MOL_GetNumStereos(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule); + +EXPIMP_TEMPLATE INCHI_API IXA_STEREOID INCHI_DECL IXA_MOL_GetStereoId(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + int vStereoIndex); + +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL IXA_MOL_GetStereoIndex(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_STEREOID vStereo); + +EXPIMP_TEMPLATE INCHI_API IXA_STEREO_TOPOLOGY INCHI_DECL IXA_MOL_GetStereoTopology(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_STEREOID vStereo); + +EXPIMP_TEMPLATE INCHI_API IXA_ATOMID INCHI_DECL IXA_MOL_GetStereoCentralAtom(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_STEREOID vStereo); + +EXPIMP_TEMPLATE INCHI_API IXA_BONDID INCHI_DECL IXA_MOL_GetStereoCentralBond(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_STEREOID vStereo); + +EXPIMP_TEMPLATE INCHI_API int INCHI_DECL IXA_MOL_GetStereoNumVertices(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_STEREOID vStereo); + +EXPIMP_TEMPLATE INCHI_API IXA_ATOMID INCHI_DECL IXA_MOL_GetStereoVertex(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_STEREOID vStereo, + int vVertexIndex); + +EXPIMP_TEMPLATE INCHI_API IXA_STEREO_PARITY INCHI_DECL IXA_MOL_GetStereoParity(IXA_STATUS_HANDLE hStatus, + IXA_MOL_HANDLE hMolecule, + IXA_STEREOID vStereo); + +/****************************************************************************/ +/* Functions for Generating InChIs */ + +EXPIMP_TEMPLATE INCHI_API IXA_INCHIBUILDER_HANDLE INCHI_DECL IXA_INCHIBUILDER_Create(IXA_STATUS_HANDLE hStatus); + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_INCHIBUILDER_SetMolecule(IXA_STATUS_HANDLE hStatus, + IXA_INCHIBUILDER_HANDLE hInChIBuilder, + IXA_MOL_HANDLE hMolecule); + +EXPIMP_TEMPLATE INCHI_API const char* INCHI_DECL IXA_INCHIBUILDER_GetInChI(IXA_STATUS_HANDLE hStatus, + IXA_INCHIBUILDER_HANDLE hInChIBuilder); + +EXPIMP_TEMPLATE INCHI_API const char* INCHI_DECL IXA_INCHIBUILDER_GetInChIEx(IXA_STATUS_HANDLE hStatus, + IXA_INCHIBUILDER_HANDLE hBuilder); + +EXPIMP_TEMPLATE INCHI_API const char* INCHI_DECL IXA_INCHIBUILDER_GetAuxInfo(IXA_STATUS_HANDLE hStatus, + IXA_INCHIBUILDER_HANDLE hInChIBuilder); + +EXPIMP_TEMPLATE INCHI_API const char* INCHI_DECL IXA_INCHIBUILDER_GetLog(IXA_STATUS_HANDLE hStatus, + IXA_INCHIBUILDER_HANDLE hInChIBuilder); + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_INCHIBUILDER_Destroy(IXA_STATUS_HANDLE hStatus, + IXA_INCHIBUILDER_HANDLE hInChIBuilder); + + + +/****************************************************************************/ +/* Functions for Specifying InChI Generation Options */ + + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_INCHIBUILDER_SetOption(IXA_STATUS_HANDLE hStatus, + IXA_INCHIBUILDER_HANDLE hInChIBuilder, + IXA_INCHIBUILDER_OPTION vOption, + IXA_BOOL vValue); + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_INCHIBUILDER_SetOption_Stereo(IXA_STATUS_HANDLE hStatus, + IXA_INCHIBUILDER_HANDLE hInChIBuilder, + IXA_INCHIBUILDER_STEREOOPTION vValue); + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_INCHIBUILDER_SetOption_Timeout(IXA_STATUS_HANDLE hStatus, + IXA_INCHIBUILDER_HANDLE hInChIBuilder, + int vValue); + + +/****************************************************************************/ +/* Functions for Generating InChI Keys */ + + +EXPIMP_TEMPLATE INCHI_API IXA_INCHIKEYBUILDER_HANDLE INCHI_DECL IXA_INCHIKEYBUILDER_Create(IXA_STATUS_HANDLE hStatus); + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_INCHIKEYBUILDER_SetInChI(IXA_STATUS_HANDLE hStatus, + IXA_INCHIKEYBUILDER_HANDLE hInChIKeyBuilder, + const char* pInChI); + +EXPIMP_TEMPLATE INCHI_API const char* INCHI_DECL IXA_INCHIKEYBUILDER_GetInChIKey(IXA_STATUS_HANDLE hStatus, + IXA_INCHIKEYBUILDER_HANDLE hInChIKeyBuilder); + +EXPIMP_TEMPLATE INCHI_API void INCHI_DECL IXA_INCHIKEYBUILDER_Destroy(IXA_STATUS_HANDLE hStatus, + IXA_INCHIKEYBUILDER_HANDLE hInChIKeyBuilder); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/INCHI-1-SRC/INCHI/main/mode.h b/INCHI-1-SRC/INCHI_BASE/src/mode.h similarity index 84% rename from INCHI-1-SRC/INCHI/main/mode.h rename to INCHI-1-SRC/INCHI_BASE/src/mode.h index dc3a3e2..2fa225a 100644 --- a/INCHI-1-SRC/INCHI/main/mode.h +++ b/INCHI-1-SRC/INCHI_BASE/src/mode.h @@ -1,1067 +1,1045 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - - - -#ifndef __MODE_H__ -#define __MODE_H__ - -#include - - - - -/*******************/ -/* */ -/* BUILD TARGETS */ -/* */ -/*******************/ - -/* Valid targets are: - -TARGET_EXE_STANDALONE - Stand-alone executable inchi-1[.exe] -TARGET_API_LIB - Library (libinchi) for using InChI API described in inchi_api.h -TARGET_EXE_USING_API - Executable (INCHI_MAIN) which uses API library (e.g., libinchi.dll) -TARGET_LIB_FOR_WINCHI - library for wInChI - - Select and uncomment one from the list below. */ - - -#define TARGET_EXE_STANDALONE 1 -/* #define TARGET_API_LIB */ -/* #define TARGET_EXE_USING_API */ -/* #define TARGET_LIB_FOR_WINCHI 1 */ - - -/****************************/ -/* */ -/* BUILD OPTIONS/FEATURES */ -/* */ -/****************************/ - -/* Possible options are: - -BUILD_LINK_AS_DLL - Link library as a Win32 DLL or to eliminate stricmp duplication - (use with TARGET_API_LIB or TARGET_EXE_USING_API) - -BUILD_WITH_ENG_OPTIONS - Expose engineering options - -BUILD_WITH_AMI - Turns on AMI (Allow Multiple Inputs) mode for standalone executable - - Select and uncomment whichever are necessary from the list below. */ - - -/* #define BUILD_LINK_AS_DLL */ - -/* #define BUILD_WITH_ENG_OPTIONS 1 */ - -#ifndef BUILD_WITH_AMI -/* this allows BUILD_WITH_AMI be #defined in a makefile */ -#define BUILD_WITH_AMI 1 -#endif -/* NB: AMI mode is only for stand-alone executable */ -#ifndef TARGET_EXE_STANDALONE -#ifdef BUILD_WITH_AMI -#undef BUILD_WITH_AMI -#endif -#endif - - - -/* CML input is not supported started from v. 1.04 */ -/* set ADD_CMLPPP to zero to override possble makefile define */ -#define ADD_CMLPP 0 -#if 0 /* obsolete */ -#ifndef ADD_CMLPP -/* this allows ADD_CMLPP be #defined in a makefile */ -#define ADD_CMLPP 1 -#endif -#if ( ADD_CMLPP == 1 ) -#ifdef USE_CMLPPDLL -/* 1200 is VC++ 6.0 version, 1300 is VC++ .NET; USE_CMLPPDLL may be #defined in a makefile*/ -#if ( defined(_WIN32) && defined(_MSC_VER) && _MSC_VER >= 1200 ) -#define MSC_DELAY_LOAD_CMLPPDLL -#endif -#endif -#endif -#endif - - - - -/*****************************/ -/* */ -/* COMPILE OPTIONS/FEATURES */ -/* */ -/*****************************/ - -/* Possible options are: - -COMPILE_ANSI_ONLY - Unconditionally force ANSI-89 C, no Win32 specific code - -COMPILE_ADD_NON_ANSI_FUNCTIONS - Use with COMPILE_ANSI_ONLY to add stricmp(), etc., see util.c - -COMPILE_ALL_CPP - allow C++ compilation/linkage of functions prototyped in .h files - -MS VC compiler pragmas - - Select and uncomment whichever are necessary from the list below. */ - - -/* #define COMPILE_ANSI_ONLY */ -#if ( !defined(_MSC_VER) || defined(TARGET_API_LIB)) /* non-Microsoft GNU C, BCC, etc. compilers */ -#ifndef COMPILE_ANSI_ONLY -#define COMPILE_ANSI_ONLY -#endif -#endif -#ifdef COMPILE_ANSI_ONLY -/*#define COMPILE_ADD_NON_ANSI_FUNCTIONS */ -#endif - -/* #define COMPILE_ALL_CPP */ - -#ifdef _MSC_VER -/* -========== disable MS VC++ 6.0 Level 4 compiler warnings: ============== - C4706: assignment within conditional expression - C4127: conditional expression is constant - C4244: '=' : conversion from 'int ' to '???', possible loss of data - C4267: '=' : conversion from 'size_t' to 'int', possible loss of data - C4701: local variable '???' may be used without having been initialized (removed) - C4514: unreferenced inline/local function has been removed (C++) - C4100: 'identifier' : unreferenced formal parameter - C4786: 'identifier' : identifier was truncated to 'number' characters in the debug information - C4996: 'identifier' was declared deprecated -======================================================================== -*/ - #pragma warning( disable : 4706 4127 4514 4100 4786 4996 4244 4267 ) -#endif - - - -/* TARGET_ID_STRING */ -#define TARGET_ID_STRING ", Software version 1.04 Build of September 9, 2011" - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - - - - -/*********************/ -/* */ -/* INCHI ALGORITHM */ -/* */ -/*********************/ - -#define INCHI_VERSION "1" - -#if 0 /* obsolete */ -/*#define INCHI_VERSION "0.9Beta" */ -/*#define INCHI_VERSION "0.91Beta" */ /* 10-10-2002: sent to Jonathan Goodman */ -/*#define INCHI_VERSION "0.92Beta" */ /* 11-15-2002: added Hill notation; sent to S.Heller & S.Stein */ -/*#define INCHI_VERSION "0.93Beta" */ /* 12-09-2002: Fixed isotopic canon. bug & chiralanes; sent to S.Heller & A. McNaught */ -/*#define INCHI_VERSION "0.931Beta" */ /* Non-BNS without salts released to PMR 01-2003 */ -/*#define INCHI_VERSION "0.932Beta" */ /* Released to CAS 04-01-2003: - * - Improved taut. definitions as compared to 01-2003; - * - fixed bug: non-isotopic components' stereo missing from isotopic stereo - * - fixed bug: couldn't properly read Unix files (EOL = LF instead of CR/LF) - * (effective only for MS VC++, Borland and MinGW/GCC compiles that accept "rb" mode of fopen) - * DJGPP/GCC does not seem to need this fix. - */ -/*==== Release version ===*/ -/*#define INCHI_VERSION "0.94Beta" */ /* 02-27-2003: Balanced network search to find alt paths and non-stereo bonds; - Implemented salts disconnection; added (-) to taut groups */ -/*#define INCHI_VERSION "1.12Beta" */ /* 1.12: 07-06-2004: sort order: No H formula,..; Pointed end stereo ON, Aggressive (de)protonation OFF */ - /* 1.11: 05-19-2004: annotated plain text output, fixed bugs */ - /* 1.1: 04-08-2004: variable protonation version */ - /* 1.01: 12-23-2003 protected bonds, isotopic canonicalization in GetBaseCanonRanking() */ - /* 1.02: 01-26-2004 fixed new isotopic tgroup canon bug, molfile merge bug */ - -/*#define INCHI_VERSION "1.0RC"*/ /* 02-07-2005 v1.0 Release Candidate */ -#endif - -#define INCHI_NAME "InChI" -#if 0 /* obsolete */ -#define INCHI_REC_NAME "ReChI" -#endif - -#define INCHI_NAM_VER_DELIM "=" - -#ifdef _WIN32 -#define INCHI_OPTION_PREFX '/' -#define INCHI_PATH_DELIM '\\' -#else -#define INCHI_OPTION_PREFX '-' -#define INCHI_PATH_DELIM '/' -#endif - -#define INCHI_ALT_OPT_PREFIX '-' -#define INCHI_ACD_LABS_PREFIX '-' - -#define bRELEASE_VERSION 1 /* 1=> release version; comment out to disable */ -#ifndef bRELEASE_VERSION -#define bRELEASE_VERSION 0 /* 0=> debug version */ -#endif - - -/* display (non-canonical) c-groups, display orig at numbers */ -#if ( bRELEASE_VERSION == 1 ) -#define DISPLAY_DEBUG_DATA_C_POINT 0 /* disabled release version for now */ -#define DISPLAY_ORIG_AT_NUMBERS 1 /* 1 => in an uncanonicalized components display orig. atom numbers (default) */ -#else -#define DISPLAY_DEBUG_DATA_C_POINT 1 /* debug: 1=>display (non-canonically numbered) c-groups, 0=>do not display */ -#define DISPLAY_ORIG_AT_NUMBERS 1 /* 0 => in an uncanonicalized components display ordering atom numbers (debug) */ -#endif - -#if ( DISPLAY_DEBUG_DATA_C_POINT > 0 ) -#define DISPLAY_DEBUG_DATA DISPLAY_DEBUG_DATA_C_POINT -#endif - - - -/* BUG FIXES */ - -/**************************/ -/* bug fixes in v1.00 */ -/**************************/ -#define FIX_ChCh_STEREO_CANON_BUG 1 /* 1=> (NEEDED) */ -#define ADD_ChCh_STEREO_CANON_CHK 0 /* 1 is NOT needed; let it always be 0 */ -#define FIX_ChCh_CONSTIT_CANON_BUG 1 /* 1=> (NEEDED) */ -#define FIX_EITHER_STEREO_IN_AUX_INFO 1 /* 1=> fix bug: Either stereobond direction in Aux_Info; 0=> do not fix */ -#define FIX_NORM_BUG_ADD_ION_PAIR 1 /* 1=> (NEEDED) fix bug: Miscount number of charges when creating an ion pair */ -#define FIX_REM_PROTON_COUNT_BUG 1 /* 1=> (NEEDED) check for number of actually removed protons and issue an error if mismatch */ -#define FIX_READ_AUX_MEM_LEAK 1 -#define FIX_READ_LONG_LINE_BUG 1 /* 1=> (NEEDED) prevent failure when reading AuxInfo and InChI is too long */ -#define FIX_N_V_METAL_BONDS_GPF 1 /* 1=> (NEEDED) InChI v1 GPF bug fix */ -#define BNS_RAD_SEARCH 1 /* 1=> prevent normalization failures due to radical centers */ - -/*******************************/ -/* bug fixes in post-v1.00 */ -/*******************************/ -#define FIX_ODD_THINGS_REM_Plus_BUG 0 -#define FIX_N_MINUS_NORN_BUG 0 -#define FIX_CANCEL_CHARGE_COUNT_BUG 0 -#define FIX_2D_STEREO_BORDER_CASE 0 -#define FIX_REM_ION_PAIRS_Si_BUG 0 -#define FIX_STEREO_SCALING_BUG 0 -#define FIX_EMPTY_LAYER_BUG 0 -#define FIX_EITHER_DB_AS_NONSTEREO 0 -#define FIX_BOND23_IN_TAUT 0 -#define FIX_TACN_POSSIBLE_BUG 0 -#define FIX_KEEP_H_ON_NH_ANION 0 -#define FIX_AVOID_ADP 0 -/* may change InChI */ -#define FIX_NUM_TG 0 /* increase number of t-groups for isothiocyanate */ -/* changes InChI for isothiocyanate */ -#define FIX_CPOINT_BOND_CAP2 0 - -/*******************************/ -/* bug fixes in post-v1.02b */ -/*******************************/ - -#define FIX_ISO_FIXEDH_BUG 1 /* (2007-09-24) 1=> Fix bug: missing fixed-H iso segment in case of single removed D(+) */ -#define FIX_ISO_FIXEDH_BUG_READ 0 /* (2007-09-24) 1=> Accommodate this InChI bug in reading InChI */ -#define FIX_DALKE_BUGS 1 -#define FIX_TRANSPOSITION_CHARGE_BUG 1 /* (2008-01-02) fix bug that leads to missed charge in some cases when /o is present */ -#define FIX_I2I_STEREOCONVERSION_BUG 1 /* (2008-03-06) 1=> Fix bug of i2i conversion SAbs-->(SRel||Srac) */ -#define FIX_I2I_STEREOCONVERSION_BUG2 1 /* (2008-04-02) 1=> Fix bug of i2i conversion (missed empty /t) */ -#define FIX_I2I_STEREOCONVERSION_BUG3 1 /* (2008-04-10) 1=> Fix bug of i2i conversion */ - /* (missed repeating /s in FI after F for multi-component case) */ -#define FIX_TERM_H_CHRG_BUG 1 /* (2008-06-06) IPl) */ - /* fix bug: in some cases (dependent on ordering - numbers), moving a charge from terminal H to heavy - atom resulted in neutralizing H but not adjusting - charge of heavy atom */ - -#define FIX_AROM_RADICAL 1 /* (2011-05-09) 1=> Fix bug which leads for different InChI */ - /* on atomic permitations for systems containing radical at */ - /* atom in aromatic ring */ - -#if ( !defined(TARGET_API_LIB) && !defined(TARGET_EXE_USING_API) ) -#define I2S_MODIFY_OUTPUT 1 /* 1=> Allow various InChI2InChI output types from cInChI */ -#else -#define I2S_MODIFY_OUTPUT 0 /* 0=> Always */ -#endif - - -#define FIX_NP_MINUS_BUG 1 /* 2010-03-11 DCh */ - -/**************************/ -/* additions to v1.00 */ -/**************************/ -#define FIX_ADJ_RAD 0 - -#define SDF_OUTPUT_V2000 1 /* 1=>always output V2000 SDfile, 0=>only if needed */ -#define SDF_OUTPUT_DT 1 /* 1=> all option -SdfAtomsDT to output D and T into SDfile */ -#define CHECK_AROMBOND2ALT 1 /* 1=> check whether arom->alt bond conversion succeeded */ - -#ifdef TARGET_LIB_FOR_WINCHI -#define READ_INCHI_STRING 0 /* 1=> input InChI string and process it */ -#else -#define READ_INCHI_STRING 1 /* 1=> input InChI string and process it */ -#endif - -/****************************************************/ -/* disabled extra external calls to InChI algorithm */ -/****************************************************/ -#define INCLUDE_NORMALIZATION_ENTRY_POINT 0 - -/**************************/ -/* Normalization settings */ -/**************************/ - -/* post version 1 features */ -#define KETO_ENOL_TAUT 1 /* include keto-enol tautomerism */ -#define TAUT_15_NON_RING 1 /* 1,5 tautomerism with endpoints not in ring */ - -/* v.1.04 : still experimental but may be exposed (set to 1) */ -#define UNDERIVATIZE 0 /* split to possible underivatized fragments */ -#define RING2CHAIN 0 /* open rings R-C(-OH)-O-R => R-C(=O) OH-R */ - -/* post-2004-04-27 features */ -#define HAL_ACID_H_XCHG 1 /* allow iso H exchange to HX (X=halogen) and H2Y (Y=halcogen) */ -#define CANON_FIXH_TRANS 1 /* produce canonical fixed-H transposition */ -#define STEREO_WEDGE_ONLY 1 /* 1=> only pointed ends stereo bonds define stereo; 0=> both ends */ - -/* current new (with respect to v1.12 Beta) preprocessing */ -#define REMOVE_ION_PAIRS_EARLY 1 /* 1=> new preprocessing: step 1 before disconnecting metals in fix_odd_things() */ -#define REMOVE_ION_PAIRS_DISC_STRU 1 /* 1=> new post-preprocessing: remove charhes after metal disconnection */ -#define REMOVE_ION_PAIRS_FIX_BONDS 1 /* 1=> step2: set unchangeable bonds around removed ion pairs */ -#define S_VI_O_PLUS_METAL_FIX_BOND 1 /* 1=> count double bond M-O(+)=S as O=S in S(VI) ans S(VIII) fixing bonds */ -#define N_V_STEREOBONDS 1 /* 1=> detect stereobonds incident to N(V); 0 => don't */ -/* for testing */ -#define REMOVE_ION_PAIRS_ORIG_STRU 0 /* 0=> normal mode (default) - * 1=> testing mode only: remove ion pairs from the original structure - * to save the changes in the output Molfile (/OutputSDF) or AuxInfo - * NIP=No Ion Pairs - */ -/* salts treatment */ -#define DISCONNECT_SALTS 1 /* 1=>disconnect metal atoms from salts, 0=>dont */ -#define TEST_REMOVE_S_ATOMS 1 /* 1=>default: after merging into one group test & - * remove unreachable, - * 0=> old version: test only before merging into one t-group */ -#define CHARGED_SALTS_ONLY 1 /* 1=>(default)do not test far salts tautomerism if - * no negative charge(s) present */ -#define BNS_PROTECT_FROM_TAUT 1 /* 1=> do not allow testing of bonds to acetyl or nitro */ -#define BNS_MARK_EDGE_2_DISCONNECT 1 /* 1=> mark edge as temp forbidden instead of disconnection */ - -#define REPLACE_ALT_WITH_TAUT 1 /* 1 => replace alt bonds with tautomeric bonds in case of standard t-groups */ -#define MOVE_CHARGES 1 /* 1 => take moveable charges into account */ -#define NEUTRALIZE_ENDPOINTS 1 /* 1 => before checking whether an H is moveable make 2 endpoints neutral */ - /* implemented only if CHECK_TG_ALT_PATH = 0, defined in ichi_bns.c */ -#define FIX_H_CHECKING_TAUT 1 /* 1 => Fix moveable H or (-) before checking if taut. exchange is possible */ -#define ALWAYS_ADD_TG_ON_THE_FLY 1 /* 1 => disables radical calcellation by taut-charge movement */ -#define IGNORE_SINGLE_ENDPOINTS 1 /* 1 => see FindAccessibleEndPoints() in INChITaut.c */ - -/* recently added -- begin */ -#define INCL_NON_SALT_CANDIDATATES 1 /* 1=> allow H and (-) migrate between "acidic" O and - * other possible endpoints */ -#define SALT_WITH_PROTONS 1 /* 1=> (new new) include proton migrarion C-SH, =C-OH, NH+ */ -#define OPPOSITE_CHARGE_IN_CGROUP 1 /* 1=> allow N(-) in (+) c-group, 0=> disallow */ -#define MOVE_PPLUS_TO_REMOVE_PROTONS 0 /* 0=> default; 1=> (disabled) add P/P+ charge group during - * 'hard' proton removal */ -#define ADD_MOVEABLE_O_PLUS 1 /* 1=> allow charges on O(+) to move */ -/* recently added -- end */ - -#define DISCONNECT_METALS 1 /* make main layer disconnected */ -#define RECONNECT_METALS 0 /* 1=> by default add reconnected layer in case of coord. - * compound disconnection */ -#define CHECK_METAL_VALENCE 0 /* 1=> disconnect only metals that have abnormal valence */ -#define bREUSE_INCHI 1 /* 1=> do not recalulate INChI for components in reconnected - * structure that are same as in the connected one */ -#define OUTPUT_CONNECTED_METAL_ONLY 0 /* 0=> default; 1 => (debug) create only reconnected or - * initial struct. output */ -#define EMBED_REC_METALS_INCHI 1 /* 1=> (default) output Reconnected embedded in Disconnected INChI; - * 0=> separate output */ - -#define bOUTPUT_ONE_STRUCT_TIME 1 /* 1 => output each structure time (non-release only) */ - - - -/* constants and array sizes */ - -#define INCHI_NUM 2 /* = array size; member indexes: */ -#define INCHI_BAS 0 /* 0 => disconnected or normal */ -#define INCHI_REC 1 /* 1 => reconnected */ - -#define TAUT_NUM 2 /* = array size; member indexes: */ -#define TAUT_NON 0 /* 0 => normal structure */ -#define TAUT_YES 1 /* 1 => tautomeric */ -#define TAUT_INI 2 /* 2 => intermediate tautomeric structure */ -#define ALT_TAUT(X) ((X)>TAUT_YES? TAUT_YES : 1-(X)) /* was (1-(X)) */ - -/* INChI output modes */ -#define OUT_N1 0 /* non-tautomeric only */ -#define OUT_T1 1 /* tautomeric if present otherwise non-tautomeric */ -#define OUT_NT 2 /* only non-taut representations of tautomeric */ -#define OUT_TN 3 /* tautomeric if present otherwise non-tautomeric; - separately output non-taut representations of tautomeric if present */ -#define OUT_NN 4 /* only non-taut representations: non-taut else tautomeric */ - -/* OUT_TN = OUT_T1 + OUT_NT */ - -/* torture test */ - -#define TEST_RENUMB_ATOMS 0 /* 1 => heavy duty test by multiple renumbering of atoms */ -#define TEST_RENUMB_NEIGH 1 /* 1 => randomly permutate neighbors */ -#define TEST_RENUMB_SWITCH 0 /* 1 => display & output another (different) picture */ -#define TEST_RENUMB_ATOMS_SAVE_LONGEST 0 /* 1 => save the component with largest processing time into the problem file */ - - -/* stereo */ - -#define NEW_STEREOCENTER_CHECK 1 /* 1 => add new stereocenter categories (see bCanInpAtomBeAStereoCenter(...)) */ -#define MIN_SB_RING_SIZE 8 /* do not assume stereo bonds in rings containing 3..MIN_SB_RING_SIZE-1 atoms */ - -#define REMOVE_KNOWN_NONSTEREO 1 /* 1=> check in advance known stereo to remove parities from non-stereogenic elements */ -#define REMOVE_CALC_NONSTEREO 1 /* 1=> check new stereo numberings to remove parities from non-stereogenic elements */ -#define PROPAGATE_ILL_DEF_STEREO 1 /* 1=> if at least one of the pair of constitutionally identical (far) neighbors */ - /* (of the tested atom) has ill-defined stereo parity and another has any */ - /* stereo parity then set the parity of the tested atom to ill-defined value. */ - -#define ONLY_DOUBLE_BOND_STEREO 0 /* 1=> no alt bond stereo, no taut. bond attachment to stereo bond */ - /* 0=> allow other definitions (below) to be active */ -#define ONE_BAD_SB_NEIGHBOR 1 /* 1 => allow 1 "bad" bond type neighbor to a stereobond atom. 2004-06-02 */ - -/* more stereo settings */ -#define BREAK_ONE_MORE_SC_TIE 1 /* break one more tie when comparing possible stereocenter neighbors */ -#define BREAK_ALSO_NEIGH_TIE 0 /* post 1.12Beta 2004-08-20: if fixed neighbor has equ neighbors, fix the one with smaller canon. rank */ -#define BREAK_ALSO_NEIGH_TIE_ROTATE 1 /* post 1.12Beta 2004-09-02: break the second in 2nd psition; 1 works, 0 does not (example:MFCD01085607) */ - -#define STEREO_CENTER_BONDS_NORM 1 /* set length of the bonds around a stereocenter = 1 before getting the parity */ -#define STEREO_CENTER_BOND4_NORM 0 /* set length of the added bond around a stereocenter = 1 before getting the parity */ -#define NORMALIZE_INP_COORD 0 /* 0=>keep unchanged, 1 => make atom coordinates integer values, avg bond len=20 */ - -/* recent stereo */ -#define STEREO_WEDGE_ONLY 1 /* 1=> only pointed ends stereo bonds define stereo; 0=> both ends 1.12Beta */ -#define CHECK_C2v_S4_SYMM 0 /* post-1.12Beta 1=> check if a stereocenter has C2v or S4 symmetry; 0=>old mode */ - -#define EQL_H_NUM_TOGETHER 1 /* 1=> output 1-3,5H2 intead of 1-3H2,5H2 (CT_MODE_EQL_H_TOGETHER) */ -#define ABC_CT_NUM_CLOSURES 1 /* 1=> in coinnections compressed format output decimal number of closures instead of '-' */ - -/* temporary fix */ -#define SINGLET_IS_TRIPLET 1 /* 'singlet' means two electrons make a lone pair instead of 2 bonds - its effect on valence is same as the effect of a triplet */ - -/* defug: find structures where canonical partition is different from equitable */ -#define FIND_CANON_NE_EQUITABLE 0 /* 0=>normal mode */ - /* 1=> extract (set EXTR_FLAGS = (EXTR_CANON_NE_EQUITABLE)*/ - /* set cmd line options: /onlynonTAUT /: /UNCHARGEDACIDS:1 /DISCONSALT:0 /MOVEPOS:0 /DISCONMETAL:0 */ - -/* Debug: definitions for the extraction of the structures to the problem file */ - -/* definition of the flags for structure extraction to the - problem file (for debugging and non-standard searching) */ -#define EXTR_KNOWN_USED_TO_REMOVE_PARITY 0x000001 -#define EXTR_CALC_USED_TO_REMOVE_PARITY 0x000002 -#define EXTR_2EQL2CENTER_TO_REMOVE_PARITY 0x000004 -#define EXTR_HAS_ATOM_WITH_DEFINED_PARITY 0x000008 -#define EXTR_REMOVE_PARITY_WARNING 0x000010 -#define EXTR_SALT_WAS_DISCONNECTED 0x000020 -#define EXTR_SALT_PROTON_MOVED 0x000040 -#define EXTR_SALT_PROTON_MOVE_ERR_WARN 0x000080 -#define EXTR_METAL_WAS_DISCONNECTED 0x000100 -#define EXTR_METAL_WAS_NOT_DISCONNECTED 0x000200 -#define EXTR_NON_TRIVIAL_STEREO 0x000400 /* (Inv != Abs stereo) && (parities can't be obtained by inverting them) */ -#define EXTR_UNUSUAL_VALENCES 0x000800 -#define EXTR_HAS_METAL_ATOM 0x001000 -#define EXTR_TEST_TAUT3_SALTS_DONE 0x002000 /* non-oxygen t-points used to discover tautomerism of merged t-groups */ -#define EXTR_CANON_NE_EQUITABLE 0x004000 /* find structures where canonical partition is different from equitable */ -#define EXTR_HAS_PROTON_PN 0x008000 /* has movable H+ attached to N or P */ -#define EXTR_HAS_FEATURE 0x010000 /* found a feature */ -#define EXTR_TAUT_TREATMENT_CHARGES 0x020000 /* tautomeric treatment of charges */ -#define EXTR_TRANSPOSITION_EXAMPLES 0x040000 /* extract structures that have different mobile-H and fixed-H orders */ - -/* define conditions of structure extraction to the problem file */ -#define EXTR_MASK 0 /*EXTR_TAUT_TREATMENT_CHARGES*/ /*(EXTR_HAS_FEATURE)*/ /*(EXTR_UNUSUAL_VALENCES | EXTR_HAS_METAL_ATOM)*/ /* 0 to disable */ -#define EXTR_FLAGS 0 /*EXTR_TAUT_TREATMENT_CHARGES*/ /*(EXTR_HAS_FEATURE)*/ /*(EXTR_HAS_PROTON_PN)*/ /*(EXTR_UNUSUAL_VALENCES)*/ /*(EXTR_CANON_NE_EQUITABLE)*/ /*(EXTR_TEST_TAUT3_SALTS_DONE)*/ /*(EXTR_HAS_METAL_ATOM)*/ /* (EXTR_NON_TRIVIAL_STEREO)*/ /*(EXTR_METAL_WAS_DISCONNECTED)*/ /* (EXTR_REMOVE_PARITY_WARNING)*/ /*(EXTR_HAS_ATOM_WITH_DEFINED_PARITY) */ - - -#define ENTITY_REFS_IN_XML_MESSAGES 1 /* 1=> replace ' " < > & in error/warning messages with xml entity references */ - -/* added tautomeric structures */ - -#define TAUT_TROPOLONE_7 1 /* 1=> tautomeric 7-member rings ON */ -#define TAUT_TROPOLONE_5 1 /* 1=> taut. similar to tropolone, 5-member ring */ -#define TAUT_4PYRIDINOL_RINGS 1 /* 1=> OH-C5H4N rings tautomerism */ -#define TAUT_PYRAZOLE_RINGS 1 /* 1=> tautomerizm in pyrazole rings */ -/* limitation on tautomerism detection: */ -#define TAUT_IGNORE_EQL_ENDPOINTS 0 /* 0=> even though 2 endpoints belong to same t-group check - them to find more alt bonds (new) - 1=> ignore and do not check (old mode) */ -#define TAUT_RINGS_ATTACH_CHAIN 1 /* 1=> allow only chain attachments to tautomeric endpoints */ - /* (except pyrazole, where is no tautomeric attachment) */ - /* 0=> allow taut. attachments from same ring system. Default=1 */ - -#define FIND_RING_SYSTEMS 1 /* 1 => find and mark ring systems, blocks, cut-vertices */ - /* Needed for 5- and 6-member ring tautomers and in other places */ - -#define FIND_RINS_SYSTEMS_DISTANCES 0 /* 1 => find ring system and atom distance from terminal */ -#define USE_DISTANCES_FOR_RANKING 0 /* 1 => rank ring systems according to distances from terminal */ - -#define DISPLAY_RING_SYSTEMS 0 /* 1 => for debug only; displays: */ - /* "block no"/"ring system no"/"cut-vertex (num. intersecting blocks-1)" */ - /* instead of ranks */ -/* consistency */ - -#if ( bRELEASE_VERSION==1 && bOUTPUT_ONE_STRUCT_TIME==1) -#undef bOUTPUT_ONE_STRUCT_TIME -#define bOUTPUT_ONE_STRUCT_TIME 0 -#endif - -/* consistency: bRELEASE_VERSION==1 needs FIND_RING_SYSTEMS=1 */ -#if ( bRELEASE_VERSION==1 && FIND_RING_SYSTEMS!=1 ) -#ifdef FIND_RING_SYSTEMS -#undef FIND_RING_SYSTEMS -#endif -#define FIND_RING_SYSTEMS 1 -#endif - -/* consistency: FIND_RINS_SYSTEMS_DISTANCES needs FIND_RING_SYSTEMS */ -#if ( FIND_RING_SYSTEMS != 1 ) - -#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) -#undef FIND_RINS_SYSTEMS_DISTANCES -#define FIND_RINS_SYSTEMS_DISTANCES 0 -#endif - -#endif - -/* consistency: USE_DISTANCES_FOR_RANKING and DISPLAY_RING_SYSTEMS need FIND_RINS_SYSTEMS_DISTANCES */ -#if ( FIND_RINS_SYSTEMS_DISTANCES != 1 ) - -#if ( USE_DISTANCES_FOR_RANKING == 1 ) -#undef USE_DISTANCES_FOR_RANKING -#define USE_DISTANCES_FOR_RANKING 0 -#endif - -#if ( DISPLAY_RING_SYSTEMS == 1 ) -#undef DISPLAY_RING_SYSTEMS -#define DISPLAY_RING_SYSTEMS 0 -#endif - -#endif - - -#if ( FIND_RING_SYSTEMS==1 && (TAUT_TROPOLONE_7==1 || TAUT_TROPOLONE_5==1 || TAUT_4PYRIDINOL_RINGS==1 || TAUT_PYRAZOLE_RINGS) ) -#define TAUT_OTHER 1 -#else -#define TAUT_OTHER 0 -#endif - -#define APPLY_IMPLICIT_H_DOWN_RULE 0 /* 1=> if 3 non-H atoms around stereocenter are in same plane */ - /* then add "down" hydrogen to obtain sterecenter oparity */ - /* 0=> Implicit H stereo is unknown if all bonds to 3 non-H atoms */ - /* are in XY plane */ -#define ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS 1 /* 1=> consider bond in an alternating circuit stereogenic */ - /* even though it has adjacent tautomeric atom(s) */ - -#define IGNORE_TGROUP_WITHOUT_H 1 /* ignore tautomeric groups containing charges only */ - -#if ( DISCONNECT_SALTS == 1 ) -#define REMOVE_TGROUP_CHARGE 0 /* 0: do not remove charge information from tautomeric groups */ -#else -#define REMOVE_TGROUP_CHARGE 1 /* 1: remove charge information from tautomeric groups */ -#endif - -#if ( REMOVE_TGROUP_CHARGE == 1 ) -#define INCHI_T_NUM_MOVABLE 1 -#else -#define INCHI_T_NUM_MOVABLE 2 -#endif - -/******************************************/ -/* define canonicalization modes here */ -/******************************************/ - -#define USE_AUX_RANKING 1 /* 1=> get auxiliary ranking to accelerate canonicalization of H layers */ -#define USE_AUX_RANKING_ALL 1 /* 1=> include all vertices in CellGetMinNode() selection 0=> only vertices with highest ranks */ - -#define USE_ISO_SORT_KEY_HFIXED 0 /* 0=> normal mode: merge isotopic taut H to isotopic atom sorting key in - taut H-fixed canonicalization; - 1=> add one more "string" iso_sort_Hfixed to the canonicalization */ - -/************************ - questionable behavior - ************************/ -#define REL_RAC_STEREO_IGN_1_SC 0 /* 1=> drop from InChI sp3 stereo in components that have a single stereocenter */ - /* 0=> old-old mode (all such sp3 stereo is in the Identifier) */ -/* internal definitions; see also REQ_MODE_BASIC etc in ichi.h */ -#define CMODE_CT 0x000001 -#define CMODE_ISO 0x000002 -#define CMODE_ISO_OUT 0x000004 /* obsolete ? */ -#define CMODE_STEREO 0x000008 -#define CMODE_ISO_STEREO 0x000010 -#define CMODE_TAUT 0x000020 -#define CMODE_NOEQ_STEREO 0x000040 /* 5-24-2002: do not use stereo equivalence to accelerate */ -#define CMODE_REDNDNT_STEREO 0x000080 /* 6-11-2002: do not check for redundant stereo elements */ -#define CMODE_NO_ALT_SBONDS 0x000100 /* 6-14-2002: do not assign stereo to alternating bonds */ -/* new 10-10-2003 */ -#define CMODE_RELATIVE_STEREO 0x000200 /* REL All Relative Stereo */ -#define CMODE_RACEMIC_STEREO 0x000400 /* RAC All Racemic Stereo */ -#define CMODE_SC_IGN_ALL_UU 0x000800 /* IAUSC Ignore stereocenters if All Undef/Unknown */ -#define CMODE_SB_IGN_ALL_UU 0x001000 /* IAUSC Ignore stereobonds if All Undef/Unknown */ -/* end of 10-10-2003 */ - -/* external definitions */ -#define CANON_MODE_CT (CMODE_CT) -#define CANON_MODE_TAUT (CMODE_CT|CMODE_TAUT) -#define CANON_MODE_ISO (CMODE_CT|CMODE_ISO|CMODE_ISO_OUT) -#define CANON_MODE_STEREO (CMODE_CT|CMODE_STEREO) -#define CANON_MODE_ISO_STEREO (CMODE_CT|CMODE_ISO|CMODE_ISO_OUT|CMODE_ISO_STEREO) - -#define CANON_MODE_MASK 0x00FF /* used to determine canonicalization mode */ - -/************************************************* - * from d_norm.c - */ - -/* implemented definitions for CT_ATOMID */ -#define CT_ATOMID_DONTINCLUDE 1 -#define CT_ATOMID_IS_INITRANK 2 -#define CT_ATOMID_IS_CURRANK 3 - -/*************************************** - * canonicalization settings I - ***************************************/ - -#define CANON_TAUTOMERS 1 /* 1=> process tautomers */ -#define HYDROGENS_IN_INIT_RANKS 1 /* 1=> include num_H in initial ranking */ - -#define DOUBLE_BOND_NEIGH_LIST 0 /* 1 => include double bond neighbor in NeighList 2 times */ -#define INCL_NON_6AROM 1 /* 1 => mark all arom. bonds; 0=>mark arom. bonds only in 6-member rings */ - -#define CT_SMALLEST /* minimal CT */ - -#define CT_NEIGH_SMALLER /* in CT, include neighbors with smaller ranks */ - -#define CT_ATOMID CT_ATOMID_IS_CURRANK /*CT_ATOMID_DONTINCLUDE */ - -#define CT_NEIGH_INCREASE /* in CT, neighbors ranks increase */ - -#define USE_SYMMETRY_TO_ACCELERATE 1 /*1 => for fast CT canonicalization, to avoid full enumeration */ - -/* dependent definitions due to settings */ - -#ifdef CT_SMALLEST -#define CT_GREATER_THAN > -#define CT_INITVALUE ~0 -#define BEST_PARITY 1 /* odd */ -#define WORSE_PARITY 2 -#else -#define CT_GREATER_THAN < -#define CT_INITVALUE 0 -#define BEST_PARITY 2 /* even */ -#define WORSE_PARITY 1 -#endif - -#ifdef CT_NEIGH_SMALLER -#define CT_NEIGH_SMALLER_THAN < -#else -#define CT_NEIGH_SMALLER_THAN > -#endif - -/* verify corectness of dependent settings */ -#if !defined( CT_ATOMID ) - #error You have to #define CT_ATOMID -#else -#if ( defined( CT_ATOMID ) && CT_ATOMID==CT_ATOMID_DONTINCLUDE ) - #error CT_DELIMITER should be #defined if CT_ATOMID is not included -#endif -#endif - -/*************************************** - * canonicalization settings II - ***************************************/ -/* from extr_ct.h */ -#define ALL_ALT_AS_AROMATIC 1 /* 1 => all altrnate bonds (even in cyclooctateraene) treat as aromatic */ - /* and set DOUBLE_BOND_NEIGH_LIST = 0 */ -#define ANY_ATOM_IN_ALT_CYCLE 1 /* 1=> accept any atom in alternating bond circuit, 0=>only some */ - -#define EXCL_ALL_AROM_BOND_PARITY 0 /* 1 => any arom atom cannot belong to stereo bond. */ - /* This has presedence over ADD_6MEMB_AROM_BOND_PARITY=1 */ - /* 0 => include arom bonds parities according to */ - /* ADD_6MEMB_AROM_BOND_PARITY definition */ - -#if ( EXCL_ALL_AROM_BOND_PARITY == 0 ) -#define ADD_6MEMB_AROM_BOND_PARITY 1 /* 1 => all arom bonds are stereo bonds */ - /* 0 => only those arom bonds which do not belong to */ - /* 6-member arom rings are stereo bonds */ -#else -#define ADD_6MEMB_AROM_BOND_PARITY 0 /* 0 => standard; 1 => meaningless: ignore parities of non-6-member ring alt. bonds */ -#endif - -#define CML_NUM_AT_IN_ATREF4 4 -#define MAX_NUM_STEREO_BONDS 3 -#define MAX_NUM_STEREO_BOND_NEIGH 3 -#define MIN_NUM_STEREO_BOND_NEIGH 2 - -#define MAX_NUM_STEREO_ATOM_NEIGH 4 -#define STEREO_AT_MARK 8 /* > MAX_NUM_STEREO_BONDS */ - -#if ( ONLY_DOUBLE_BOND_STEREO == 1 ) /* { */ - -#ifdef ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS -#undef ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS -#define ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS 0 -#endif - -#ifdef EXCL_ALL_AROM_BOND_PARITY -#undef EXCL_ALL_AROM_BOND_PARITY -#define EXCL_ALL_AROM_BOND_PARITY 1 -#endif - -#ifdef ADD_6MEMB_AROM_BOND_PARITY -#undef ADD_6MEMB_AROM_BOND_PARITY -#define ADD_6MEMB_AROM_BOND_PARITY 0 -#endif - -#endif /* } ONLY_DOUBLE_BOND_STEREO */ - -/* dependent definitions due to settings */ -#if ( ALL_ALT_AS_AROMATIC == 1 && DOUBLE_BOND_NEIGH_LIST != 0 ) -#undef DOUBLE_BOND_NEIGH_LIST -#define DOUBLE_BOND_NEIGH_LIST 0 -#endif - - -/************************************* - * Drawing - */ - -#define DRAW_AROM_TAUT 1 /* 1=> draw distinct aromatic & tautomer bonds, 0=> don't */ - -/******************************************************/ -/* C O M M O N D E F I N I T I O N S */ -/******************************************************/ - - -/* input bTautFlags flags */ -#define TG_FLAG_TEST_TAUT__ATOMS 0x00000001 /* find regular tautomerism */ -#define TG_FLAG_DISCONNECT_SALTS 0x00000002 /* DISCONNECT_SALTS disconnect */ -#define TG_FLAG_TEST_TAUT__SALTS 0x00000004 /* DISCONNECT_SALTS if possible find long-range H/(-) taut. on =C-OH, >C=O */ -#define TG_FLAG_MOVE_POS_CHARGES 0x00000008 /* MOVE_CHARGES allow long-range movement of N(+), P(+) charges */ -#define TG_FLAG_TEST_TAUT2_SALTS 0x00000010 /* TEST_REMOVE_S_ATOMS multi-attachement long-range H/(-) taut. on =C-OH, >C=O */ -#define TG_FLAG_ALLOW_NO_NEGTV_O 0x00000020 /* CHARGED_SALTS_ONLY=0 (debug) find long-range H-only tautomerism on =C-OH, >C=O */ -#define TG_FLAG_MERGE_TAUT_SALTS 0x00000040 /* DISCONNECT_SALTS merge all "salt"-t-groups and other =C-OH into one t-group */ - -#define TG_FLAG_ALL_TAUTOMERIC (TG_FLAG_TEST_TAUT__ATOMS| \ - TG_FLAG_TEST_TAUT__SALTS| \ - TG_FLAG_TEST_TAUT2_SALTS| \ - TG_FLAG_MERGE_TAUT_SALTS) - -#define TG_FLAG_DISCONNECT_COORD 0x00000080 /* find "coord. centers" and disconnect them */ -#define TG_FLAG_RECONNECT_COORD 0x00000100 /* reconnect disconnected "coord. centers" */ -#define TG_FLAG_CHECK_VALENCE_COORD 0x00000200 /* do not disconnect "coord. centers" with usual valence */ -#define TG_FLAG_MOVE_HPLUS2NEUTR 0x00000400 /* move protons to neutralize */ -#define TG_FLAG_VARIABLE_PROTONS 0x00000800 /* add/remove protons to neutralize */ -#define TG_FLAG_HARD_ADD_REM_PROTONS 0x00001000 /* add/remove protons to neutralize in hard way */ -#define TG_FLAG_POINTED_EDGE_STEREO 0x00002000 /* only pointed edge of stereo bond defines stereo */ -#if ( FIX_ADJ_RAD == 1 ) -#define TG_FLAG_FIX_ADJ_RADICALS 0x00004000 /* remove adjacent radical-doubletes, fix valence */ -#endif -#define TG_FLAG_PHOSPHINE_STEREO 0x00008000 /* add phosphine sp3 stereo */ -#define TG_FLAG_ARSINE_STEREO 0x00010000 /* add arsine sp3 stereo */ -#define TG_FLAG_H_ALREADY_REMOVED 0x00020000 /* processing structure restored from InChI */ -#define TG_FLAG_FIX_SP3_BUG 0x00040000 /* fix sp3 stereo bug: overlapping 2D stereo bond & coordinate scaling */ - -#define TG_FLAG_KETO_ENOL_TAUT 0x00080000 /* turn on keto-enol tautomerism detection */ -#define TG_FLAG_1_5_TAUT 0x00100000 /* turn on 1,5 tautomerism detection */ - -/*^^^ FB2 */ -#define TG_FLAG_FIX_ISO_FIXEDH_BUG 0x00200000 /* fix bug found after v.102b (isotopic H representation) */ -#define TG_FLAG_FIX_TERM_H_CHRG_BUG 0x00400000 /* fix bug found after v.102b (moving H charge in 'remove_terminal_HDT') */ - -/* output bTautFlags flags */ - -#define TG_FLAG_MOVE_HPLUS2NEUTR_DONE 0x00000001 /* protons have been moved to neutralize */ -#define TG_FLAG_TEST_TAUT__ATOMS_DONE 0x00000002 -#define TG_FLAG_DISCONNECT_SALTS_DONE 0x00000004 -#define TG_FLAG_TEST_TAUT__SALTS_DONE 0x00000008 /* multiple H tautomerism */ -#define TG_FLAG_MOVE_POS_CHARGES_DONE 0x00000010 -#define TG_FLAG_TEST_TAUT2_SALTS_DONE 0x00000020 /* merged t-groups */ -#define TG_FLAG_ALLOW_NO_NEGTV_O_DONE 0x00000040 -#define TG_FLAG_MERGE_TAUT_SALTS_DONE 0x00000080 /* added non-taut O to taut groups */ - -#define TG_FLAG_ALL_SALT_DONE (TG_FLAG_TEST_TAUT__SALTS_DONE | \ - TG_FLAG_TEST_TAUT2_SALTS_DONE | \ - TG_FLAG_MERGE_TAUT_SALTS_DONE ) - -#define TG_FLAG_DISCONNECT_COORD_DONE 0x00000100 /* found and disconnected "coord. centers" */ -#define TG_FLAG_CHECK_VALENCE_COORD_DONE 0x00000200 /* did not disconnect "coord. centers" with usual valence */ -#define TG_FLAG_MOVE_CHARGE_COORD_DONE 0x00000400 /* changed charge of a disconnected ligand to fit its valence */ -#define TG_FLAG_FIX_ODD_THINGS_DONE 0x00000800 /* fixed drawing ambiguities in fix_odd_things */ -#define TG_FLAG_TEST_TAUT3_SALTS_DONE 0x00001000 /* merged t-groups + non-O taut atoms */ -#define TG_FLAG_FOUND_SALT_CHARGES_DONE 0x00002000 /* not assigned: preprocessing detected possibility of salt-type tautomerism */ -#define TG_FLAG_FOUND_ISOTOPIC_H_DONE 0x00004000 /* preprocessing detected isotopic H on "good" heteroatoms or isotopic H(+) */ -#define TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE 0x00008000 /* preprocessing detected isotopic H on "good" heteroatoms or isotopic H(+) */ -#if ( FIX_ADJ_RAD == 1 ) -#define TG_FLAG_FIX_ADJ_RADICALS_DONE 0x00010000 -#endif - -#if ( READ_INCHI_STRING == 1 ) -#define READ_INCHI_OUTPUT_INCHI 0x00000001 -#define READ_INCHI_SPLIT_OUTPUT 0x00000002 -#define READ_INCHI_KEEP_BALANCE_P 0x00000004 -#define READ_INCHI_TO_STRUCTURE 0x00000008 -#endif - - - - -/*********/ -/* */ -/* I/O */ -/* */ -/*********/ - -typedef struct tagOutputString -{ - char *pStr; - int nAllocatedLength; - int nUsedLength; - int nPtr; -} INCHI_OUTPUT; - -typedef struct tagOutputStream -{ - /* output is directed either to resizable string buffer: */ - INCHI_OUTPUT s; - /* or to the plain file: */ - FILE* f; - int type; -} INCHI_IOSTREAM; -/* INCHI_IOSTREAM.type values */ -#define INCHI_IOSTREAM_NONE 0 -#define INCHI_IOSTREAM_STRING 1 -#define INCHI_IOSTREAM_FILE 2 - - - - -/***********/ -/* */ -/* DEBUG */ -/* */ -/***********/ - -#if ( defined(_WIN32) && defined(_DEBUG) && defined(_MSC_VER) /*&& !defined(COMPILE_ANSI_ONLY)*/ ) -/* debug: memory leaks tracking */ -#ifndef TARGET_LIB_FOR_WINCHI -#ifndef DO_NOT_TRACE_MEMORY_LEAKS -#define TRACE_MEMORY_LEAKS 1 /* 1=>trace, 0 => do not trace (Debug only) */ -#else -#define TRACE_MEMORY_LEAKS 0 -#endif -#else -#define TRACE_MEMORY_LEAKS 1 /* 1=>trace, **ALWAYS** =1 for TARGET_LIB_FOR_WINCHI */ -#endif -#else /* not MSC and not Debug */ -#define TRACE_MEMORY_LEAKS 0 /* 0: do not change */ -#endif - - -/* memory leaks tracking */ -#define INCHI_HEAPCHK /* default: no explicit heap checking during the execution */ - -#if ( TRACE_MEMORY_LEAKS == 1 ) -#ifdef _DEBUG - -#define inchi_malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) -#define inchi_calloc(c, s) _calloc_dbg(c, s, _NORMAL_BLOCK, __FILE__, __LINE__) -#define inchi_free(p) _free_dbg(p, _NORMAL_BLOCK) - -#ifdef TARGET_EXE_USING_API -/* INChI_MAIN specific */ -#define e_inchi_malloc(a) inchi_malloc(a) -#define e_inchi_calloc(a,b) inchi_calloc(a,b) -#define e_inchi_free(a) inchi_free(a) -#endif - -/*#define _CRTDBG_MAP_ALLOC*/ /* standard VC++ tool -- does not work with inchi_malloc(), etc */ - -#include - -/* to enable heap checking: #define CHECK_WIN32_VC_HEAP above #include "mode.h" in each source file or here */ -#ifdef CHECK_WIN32_VC_HEAP -/* -- Confirms the integrity of the memory blocks allocated in the debug heap -- */ -#undef INCHI_HEAPCHK -#define INCHI_HEAPCHK \ -do { \ - int tmp = _crtDbgFlag; \ - _crtDbgFlag |= _CRTDBG_ALLOC_MEM_DF; \ - _ASSERT( _CrtCheckMemory( ) ); \ - _crtDbgFlag = tmp; \ -} while(0); - -/* -- less thorough than _CrtCheckMemory() check: check minimal consistency of the heap -- */ -/* -#include -#define INCHI_HEAPCHK \ -do {\ - int heapstatus = _heapchk(); \ - _ASSERT( heapstatus != _HEAPBADBEGIN && heapstatus != _HEAPBADNODE && heapstatus != _HEAPBADPTR); \ -} while(0); -*/ -#endif - -#else -#undef TRACE_MEMORY_LEAKS -#define TRACE_MEMORY_LEAKS 0 -#endif /* _DEBUG */ -#endif /* TRACE_MEMORY_LEAKS */ - - - -/***********/ -/* */ -/* ALLOC */ -/* */ -/***********/ - -#ifdef TARGET_EXE_USING_API -/* INChI_MAIN specific */ -#ifndef inchi_malloc -#define inchi_malloc e_inchi_malloc -#endif -#ifndef inchi_calloc -#define inchi_calloc e_inchi_calloc -#endif -#ifndef inchi_free -#define inchi_free e_inchi_free -#endif - -#ifndef e_inchi_malloc -#define e_inchi_malloc malloc -#endif -#ifndef e_inchi_calloc -#define e_inchi_calloc calloc -#endif -#ifndef e_inchi_free -#define e_inchi_free(X) do{ if(X) free(X); }while(0) -#endif - -#else /* not TARGET_EXE_USING_API */ - -#ifndef inchi_malloc -#define inchi_malloc malloc -#endif -#ifndef inchi_calloc -#define inchi_calloc calloc -#endif -#ifndef inchi_free -#define inchi_free(X) do{ if(X) free(X); }while(0) -#endif - -#endif /* TARGET_EXE_USING_API */ - -/* allocation/deallocation */ -#define USE_ALLOCA 0 - -#if ( USE_ALLOCA == 1 ) -#define qmalloc(X) _alloca(X) -#define qfree(X) do{(X)=NULL;}while(0) -#else -#define qmalloc(X) inchi_malloc(X) -#define qfree(X) do{if(X){inchi_free(X);(X)=NULL;}}while(0) -#endif - -#if ( defined(_MSC_VER) && _MSC_VER >= 800 ) -#define fast_alloc(X) _alloca(X) -#define fast_free(X) -#else -#define fast_alloc(X) inchi_malloc(X) -#define fast_free(X) inchi_free(X) -#endif - -#define qzfree(X) do{if(X){inchi_free(X);(X)=NULL;}}while(0) - -/* rellocation */ - -#define MYREALLOC2(PTRTYPE1, PTRTYPE2, PTR1, PTR2, LEN1, LEN2, ERR) \ - do { \ - if( (LEN1) <= (LEN2) ) {\ - PTRTYPE1 * newPTR1 = (PTRTYPE1 *)inchi_calloc( (LEN2)+1, sizeof(PTRTYPE1) );\ - PTRTYPE2 * newPTR2 = (PTRTYPE2 *)inchi_calloc( (LEN2)+1, sizeof(PTRTYPE2) );\ - if ( newPTR1 && newPTR2 ) { \ - if ( (PTR1) && (LEN1) > 0 ) \ - (memcpy) ( newPTR1, (PTR1), (LEN1) * sizeof(PTRTYPE1) ); \ - if ( (PTR2) && (LEN1) > 0 ) \ - (memcpy) ( newPTR2, (PTR2), (LEN1) * sizeof(PTRTYPE2) ); \ - if ( PTR1 ) \ - inchi_free(PTR1); \ - if ( PTR2 ) \ - inchi_free(PTR2); \ - (PTR1) = newPTR1; \ - (PTR2) = newPTR2; \ - (LEN1) = (LEN2); \ - (ERR) = 0; \ - } else { \ - (ERR) = 1; \ - } \ - } else { (ERR) = 0; } \ - } while(0) - - - - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /* __MODE_H__ */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _MODE_H_ +#define _MODE_H_ + +#include +#include + +/*******************/ +/* */ +/* BUILD TARGETS */ +/* */ +/*******************/ + +/* + One of targets below should be explicitly + indicated to compiler (makefile/directive): + + TARGET_EXE_STANDALONE Stand-alone executable inchi-1[.exe] + TARGET_API_LIB Library (libinchi) for using InChI API + described in inchi_api.h + TARGET_EXE_USING_API Executable (INCHI_MAIN) which uses + API library (e.g., libinchi.dll) + TARGET_LIB_FOR_WINCHI library for wInChI +*/ + +#if defined(TARGET_EXE_STANDALONE) +#define APP_DESCRIPTION "InChI version 1, Software v. 1.05 (inchi-1 executable)" +#elif defined(TARGET_API_LIB) +#define APP_DESCRIPTION "InChI version 1, Software v. 1.05 (API Library)" +#elif defined(TARGET_EXE_USING_API) +#ifndef APP_DESCRIPTION +#define APP_DESCRIPTION "InChI version 1, Software v. 1.05 (executable calling API Library)" +#endif +#elif defined(TARGET_LIB_FOR_WINCHI) +#define APP_DESCRIPTION "InChI version 1, Software v. 1.05 (Library for wInChI GUI executable)" +#elif defined(TARGET_WINCHI) +#define APP_DESCRIPTION "InChI version 1, Software v. 1.05 (wInChI GUI executable)" +#else + #error No build target #defined, pls check compiler options... (TARGET_EXE_STANDALONE|TARGET_API_LIB|TARGET_EXE_USING_API|TARGET_LIB_FOR_WINCHI) +#endif + + +#ifndef TARGET_PLATFORM +#if defined(_WIN32) +#define TARGET_PLATFORM "Windows" +#else +#define TARGET_PLATFORM "Linux" +#endif +#endif + +/****************************/ +/* */ +/* BUILD OPTIONS/FEATURES */ +/* */ +/****************************/ + +/* Possible options are: + +BUILD_LINK_AS_DLL + Link library as a Win32 DLL or to eliminate inchi_stricmp duplication + (use with TARGET_API_LIB or TARGET_EXE_USING_API) + +BUILD_WITH_ENG_OPTIONS + Expose engineering options + +BUILD_WITH_AMI + Turns on AMI (Allow Multiple Inputs) mode for standalone executable + + Select and uncomment whichever are necessary from the list below. */ + + +/* #define BUILD_LINK_AS_DLL */ + +/* #define BUILD_WITH_ENG_OPTIONS 1 */ + +#ifndef BUILD_WITH_AMI +/* this allows BUILD_WITH_AMI be #defined in a makefile */ +#define BUILD_WITH_AMI 1 +#endif +/* NB: AMI mode is only for stand-alone executable */ +#ifndef TARGET_EXE_STANDALONE +#ifdef BUILD_WITH_AMI +#undef BUILD_WITH_AMI +#endif +#endif + +/* Smarter AMI for Windows */ +/* Thanks, DT (2013-12-18) */ +#if( BUILD_WITH_AMI == 1 ) +#if( defined( _MSC_VER ) ) +#define MSC_AMI 1 +/* use MSC _findfirst(...), etc. Do not link with setargv.obj */ +#endif +#ifdef BUILD_WITH_ENG_OPTIONS +#define OUTPUT_FILE_EXT 1 /* options "/.": /.extOut /.extLog /.extPrb replace input file extension instead of adding .txt, .log, .prb 2013-12-18 DCh */ +#define ALLOW_EMPTY_PATHS 1 /* let command line argument "" be interpreted as path */ +#endif +#endif + +/* CML input is not supported started from v. 1.04 */ +/* set ADD_CMLPPP to zero to override possble makefile define */ +#define ADD_CMLPP 0 + +/* v. 1.05 OutErrInchi allowed */ +#define ERR_INCHI_STRING_ALLOWED 1 + + +/*****************************/ +/* */ +/* COMPILE OPTIONS/FEATURES */ +/* */ +/*****************************/ + +/* Possible options are: + +COMPILE_ANSI_ONLY + Unconditionally force ANSI-89 C, no Win32 specific code + +COMPILE_ADD_NON_ANSI_FUNCTIONS + Use with COMPILE_ANSI_ONLY to add inchi_stricmp(), etc., see util.c + +COMPILE_ALL_CPP + allow C++ compilation/linkage of functions prototyped in .h files + +MS VC compiler pragmas + + Select and uncomment whichever are necessary from the list below. */ + + +/* #define COMPILE_ANSI_ONLY */ +#if ( !defined(_MSC_VER) || defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API)) /* non-Microsoft GNU C, BCC, etc. compilers */ +#ifndef COMPILE_ANSI_ONLY +#define COMPILE_ANSI_ONLY +#endif +#endif +#ifdef COMPILE_ANSI_ONLY +/*#define COMPILE_ADD_NON_ANSI_FUNCTIONS */ +#endif + +/* +#define COMPILE_ALL_CPP 1 +*/ + +#ifdef _MSC_VER +/* +========== disable MS VC++ 6.0 Level 4 compiler warnings: ============== + C4706: assignment within conditional expression + C4127: conditional expression is constant + C4244: '=' : conversion from 'int ' to '???', possible loss of data + C4267: '=' : conversion from 'size_t' to 'int', possible loss of data + C4701: local variable '???' may be used without having been initialized (removed) + C4514: unreferenced inline/local function has been removed (C++) + C4100: 'identifier' : unreferenced formal parameter + C4786: 'identifier' : identifier was truncated to 'number' characters in the debug information + C4996: 'identifier' was declared deprecated +======================================================================== +*/ +#pragma warning( disable : 4706 4127 4514 4100 4786 4996 4244 4267 ) +#endif + + + + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + + + + +/*********************/ +/* */ +/* INCHI ALGORITHM */ +/* */ +/*********************/ + +#define INCHI_VERSION "1" +#define INCHI_NAME "InChI" +#define INCHI_NAM_VER_DELIM "=" + +#ifdef _WIN32 +#define INCHI_OPTION_PREFX '/' +#define INCHI_PATH_DELIM '\\' +#else +#define INCHI_OPTION_PREFX '-' +#define INCHI_PATH_DELIM '/' +#endif + +#define INCHI_ALT_OPT_PREFIX '-' +#define INCHI_ACD_LABS_PREFIX '-' + +#define bRELEASE_VERSION 1 /* 1=> release version; comment out to disable */ +#ifndef bRELEASE_VERSION +#define bRELEASE_VERSION 0 /* 0=> debug version */ +#endif + +/*#define RELEASE_IS_FINAL 0*/ /* 1=> pre-release version; comment out to disable */ +#ifndef RELEASE_IS_FINAL +#define RELEASE_IS_FINAL 1 /* final release */ +#endif + +/* display (non-canonical) c-groups, display orig at numbers */ +#if ( bRELEASE_VERSION == 1 ) +#define DISPLAY_DEBUG_DATA_C_POINT 0 /* disabled release version for now */ +#define DISPLAY_ORIG_AT_NUMBERS 1 /* 1 => in an uncanonicalized components display orig. atom numbers (default) */ +#else +#define DISPLAY_DEBUG_DATA_C_POINT 1 /* debug: 1=>display (non-canonically numbered) c-groups, 0=>do not display */ +#define DISPLAY_ORIG_AT_NUMBERS 1 /* 0 => in an uncanonicalized components display ordering atom numbers (debug) */ +#endif + +#if ( DISPLAY_DEBUG_DATA_C_POINT > 0 ) +#define DISPLAY_DEBUG_DATA DISPLAY_DEBUG_DATA_C_POINT +#endif + + + +/* BUG FIXES */ + +/**************************/ +/* bug fixes in v1.00 */ +/**************************/ +#define FIX_ChCh_STEREO_CANON_BUG 1 /* 1=> (NEEDED) */ +#define ADD_ChCh_STEREO_CANON_CHK 0 /* 1 is NOT needed; let it always be 0 */ +#define FIX_ChCh_CONSTIT_CANON_BUG 1 /* 1=> (NEEDED) */ +#define FIX_EITHER_STEREO_IN_AUX_INFO 1 /* 1=> fix bug: Either stereobond direction in Aux_Info; 0=> do not fix */ +#define FIX_NORM_BUG_ADD_ION_PAIR 1 /* 1=> (NEEDED) fix bug: Miscount number of charges when creating an ion pair */ +#define FIX_REM_PROTON_COUNT_BUG 1 /* 1=> (NEEDED) check for number of actually removed protons and issue an error if mismatch */ +#define FIX_READ_AUX_MEM_LEAK 1 +#define FIX_READ_LONG_LINE_BUG 1 /* 1=> (NEEDED) prevent failure when reading AuxInfo and InChI is too long */ +#define FIX_N_V_METAL_BONDS_GPF 1 /* 1=> (NEEDED) InChI v1 GPF bug fix */ +#define BNS_RAD_SEARCH 1 /* 1=> prevent normalization failures due to radical centers */ + +/*******************************/ +/* bug fixes in post-v1.00 */ +/*******************************/ +#define FIX_ODD_THINGS_REM_Plus_BUG 0 +#define FIX_N_MINUS_NORN_BUG 0 +#define FIX_CANCEL_CHARGE_COUNT_BUG 0 +#define FIX_2D_STEREO_BORDER_CASE 0 +#define FIX_REM_ION_PAIRS_Si_BUG 0 +#define FIX_STEREO_SCALING_BUG 0 +#define FIX_EMPTY_LAYER_BUG 0 +#define FIX_EITHER_DB_AS_NONSTEREO 0 +#define FIX_BOND23_IN_TAUT 0 +#define FIX_TACN_POSSIBLE_BUG 0 +#define FIX_KEEP_H_ON_NH_ANION 0 +#define FIX_AVOID_ADP 0 +/* may change InChI */ +#define FIX_NUM_TG 0 /* increase number of t-groups for isothiocyanate */ +/* changes InChI for isothiocyanate */ +#define FIX_CPOINT_BOND_CAP2 0 + +/*******************************/ +/* bug fixes in post-v1.02b */ +/*******************************/ + +#define FIX_ISO_FIXEDH_BUG 1 /* (2007-09-24) 1=> Fix bug: missing fixed-H iso segment in case of single removed D(+) */ +#define FIX_ISO_FIXEDH_BUG_READ 0 /* (2007-09-24) 1=> Accommodate this InChI bug in reading InChI */ +#define FIX_DALKE_BUGS 1 +#define FIX_TRANSPOSITION_CHARGE_BUG 1 /* (2008-01-02) fix bug that leads to missed charge in some cases when /o is present */ +#define FIX_I2I_STEREOCONVERSION_BUG 1 /* (2008-03-06) 1=> Fix bug of i2i conversion SAbs-->(SRel||Srac) */ +#define FIX_I2I_STEREOCONVERSION_BUG2 1 /* (2008-04-02) 1=> Fix bug of i2i conversion (missed empty /t) */ +#define FIX_I2I_STEREOCONVERSION_BUG3 1 /* (2008-04-10) 1=> Fix bug of i2i conversion */ + /* (missed repeating /s in FI after F for multi-component case) */ +#define FIX_TERM_H_CHRG_BUG 1 /* (2008-06-06) IPl) */ + /* fix bug: in some cases (dependent on ordering + numbers), moving a charge from terminal H to heavy + atom resulted in neutralizing H but not adjusting + charge of heavy atom */ + +#define FIX_AROM_RADICAL 1 /* (2011-05-09) 1=> Fix bug which leads for different InChI */ + /* on atomic permitations for systems containing radical at */ + /* atom in aromatic ring */ + +#if ( !defined(TARGET_API_LIB) && !defined(TARGET_EXE_USING_API) ) +#define I2S_MODIFY_OUTPUT 1 /* 1=> Allow various InChI2InChI output types from cInChI */ +#else +#define I2S_MODIFY_OUTPUT 0 /* 0=> Always */ +#endif + + +#define FIX_NP_MINUS_BUG 1 /* 2010-03-11 DCh */ + +/**************************/ +/* additions to v1.00 */ +/**************************/ +#define FIX_ADJ_RAD 0 + +#define SDF_OUTPUT_V2000 1 /* 1=>always output V2000 SDfile, 0=>only if needed */ +#define SDF_OUTPUT_DT 1 /* 1=> all option -SdfAtomsDT to output D and T into SDfile */ +#define CHECK_AROMBOND2ALT 1 /* 1=> check whether arom->alt bond conversion succeeded */ + +#ifdef TARGET_LIB_FOR_WINCHI +#define READ_INCHI_STRING 0 /* 1=> input InChI string and process it */ +#else +#define READ_INCHI_STRING 1 /* 1=> input InChI string and process it */ +#endif + +/****************************************************/ +/* disabled extra external calls to InChI algorithm */ +/****************************************************/ +#define INCLUDE_NORMALIZATION_ENTRY_POINT 0 + +/**************************/ +/* Normalization settings */ +/**************************/ + +/* post version 1 features */ +#define KETO_ENOL_TAUT 1 /* include keto-enol tautomerism */ +#define TAUT_15_NON_RING 1 /* 1,5 tautomerism with endpoints not in ring */ + +/* v.1.04 : still experimental but may be exposed (set to 1) */ +#define UNDERIVATIZE 0 /* split to possible underivatized fragments */ +#define RING2CHAIN 0 /* open rings R-C(-OH)-O-R => R-C(=O) OH-R */ + +/* post-2004-04-27 features */ +#define HAL_ACID_H_XCHG 1 /* allow iso H exchange to HX (X=halogen) and H2Y (Y=halcogen) */ +#define CANON_FIXH_TRANS 1 /* produce canonical fixed-H transposition */ +#define STEREO_WEDGE_ONLY 1 /* 1=> only pointed ends stereo bonds define stereo; 0=> both ends */ + +/* current new (with respect to v1.12 Beta) preprocessing */ +#define REMOVE_ION_PAIRS_EARLY 1 /* 1=> new preprocessing: step 1 before disconnecting metals in fix_odd_things() */ +#define REMOVE_ION_PAIRS_DISC_STRU 1 /* 1=> new post-preprocessing: remove charhes after metal disconnection */ +#define REMOVE_ION_PAIRS_FIX_BONDS 1 /* 1=> step2: set unchangeable bonds around removed ion pairs */ +#define S_VI_O_PLUS_METAL_FIX_BOND 1 /* 1=> count double bond M-O(+)=S as O=S in S(VI) ans S(VIII) fixing bonds */ +#define N_V_STEREOBONDS 1 /* 1=> detect stereobonds incident to N(V); 0 => don't */ +/* for testing */ +#define REMOVE_ION_PAIRS_ORIG_STRU 0 /* 0=> normal mode (default) + * 1=> testing mode only: remove ion pairs from the original structure + * to save the changes in the output Molfile (/OutputSDF) or AuxInfo + * NIP=No Ion Pairs + */ +/* salts treatment */ +#define DISCONNECT_SALTS 1 /* 1=>disconnect metal atoms from salts, 0=>dont */ +#define TEST_REMOVE_S_ATOMS 1 /* 1=>default: after merging into one group test & + * remove unreachable, + * 0=> old version: test only before merging into one t-group */ +#define CHARGED_SALTS_ONLY 1 /* 1=>(default)do not test far salts tautomerism if + * no negative charge(s) present */ +#define BNS_PROTECT_FROM_TAUT 1 /* 1=> do not allow testing of bonds to acetyl or nitro */ +#define BNS_MARK_EDGE_2_DISCONNECT 1 /* 1=> mark edge as temp forbidden instead of disconnection */ + +#define REPLACE_ALT_WITH_TAUT 1 /* 1 => replace alt bonds with tautomeric bonds in case of standard t-groups */ +#define MOVE_CHARGES 1 /* 1 => take moveable charges into account */ +#define NEUTRALIZE_ENDPOINTS 1 /* 1 => before checking whether an H is moveable make 2 endpoints neutral */ + /* implemented only if CHECK_TG_ALT_PATH = 0, defined in ichi_bns.c */ +#define FIX_H_CHECKING_TAUT 1 /* 1 => Fix moveable H or (-) before checking if taut. exchange is possible */ +#define ALWAYS_ADD_TG_ON_THE_FLY 1 /* 1 => disables radical calcellation by taut-charge movement */ +#define IGNORE_SINGLE_ENDPOINTS 1 /* 1 => see FindAccessibleEndPoints() in INChITaut.c */ + +/* recently added -- begin */ +#define INCL_NON_SALT_CANDIDATATES 1 /* 1=> allow H and (-) migrate between "acidic" O and + * other possible endpoints */ +#define SALT_WITH_PROTONS 1 /* 1=> (new new) include proton migrarion C-SH, =C-OH, NH+ */ +#define OPPOSITE_CHARGE_IN_CGROUP 1 /* 1=> allow N(-) in (+) c-group, 0=> disallow */ +#define MOVE_PPLUS_TO_REMOVE_PROTONS 0 /* 0=> default; 1=> (disabled) add P/P+ charge group during + * 'hard' proton removal */ +#define ADD_MOVEABLE_O_PLUS 1 /* 1=> allow charges on O(+) to move */ +/* recently added -- end */ + +#define DISCONNECT_METALS 1 /* make main layer disconnected */ +#define RECONNECT_METALS 0 /* 1=> by default add reconnected layer in case of coord. + * compound disconnection */ +#define CHECK_METAL_VALENCE 0 /* 1=> disconnect only metals that have abnormal valence */ +#define bREUSE_INCHI 1 /* 1=> do not recalulate INChI for components in reconnected + * structure that are same as in the connected one */ +#define OUTPUT_CONNECTED_METAL_ONLY 0 /* 0=> default; 1 => (debug) create only reconnected or + * initial struct. output */ +#define EMBED_REC_METALS_INCHI 1 /* 1=> (default) output Reconnected embedded in Disconnected INChI; + * 0=> separate output */ + +#define bOUTPUT_ONE_STRUCT_TIME 1 /* 1 => output each structure time (non-release only) */ + + + +/* constants and array sizes */ + +#define INCHI_NUM 2 /* = array size; member indexes: */ +#define INCHI_BAS 0 /* 0 => disconnected or normal */ +#define INCHI_REC 1 /* 1 => reconnected */ + +#define TAUT_NUM 2 /* = array size; member indexes: */ +#define TAUT_NON 0 /* 0 => normal structure */ +#define TAUT_YES 1 /* 1 => tautomeric */ +#define TAUT_INI 2 /* 2 => intermediate tautomeric structure */ +#define ALT_TAUT(X) ((X)>TAUT_YES? TAUT_YES : 1-(X)) /* was (1-(X)) */ + +/* INChI output modes */ +#define OUT_N1 0 /* non-tautomeric only */ +#define OUT_T1 1 /* tautomeric if present otherwise non-tautomeric */ +#define OUT_NT 2 /* only non-taut representations of tautomeric */ +#define OUT_TN 3 /* tautomeric if present otherwise non-tautomeric; + separately output non-taut representations of tautomeric if present */ +#define OUT_NN 4 /* only non-taut representations: non-taut else tautomeric */ + +/* OUT_TN = OUT_T1 + OUT_NT */ + +/* stereo */ + +#define NEW_STEREOCENTER_CHECK 1 /* 1 => add new stereocenter categories (see bCanInpAtomBeAStereoCenter(...)) */ +#define MIN_SB_RING_SIZE 8 /* do not assume stereo bonds in rings containing 3..MIN_SB_RING_SIZE-1 atoms */ + +#define REMOVE_KNOWN_NONSTEREO 1 /* 1=> check in advance known stereo to remove parities from non-stereogenic elements */ +#define REMOVE_CALC_NONSTEREO 1 /* 1=> check new stereo numberings to remove parities from non-stereogenic elements */ +#define PROPAGATE_ILL_DEF_STEREO 1 /* 1=> if at least one of the pair of constitutionally identical (far) neighbors */ + /* (of the tested atom) has ill-defined stereo parity and another has any */ + /* stereo parity then set the parity of the tested atom to ill-defined value. */ + +#define ONLY_DOUBLE_BOND_STEREO 0 /* 1=> no alt bond stereo, no taut. bond attachment to stereo bond */ + /* 0=> allow other definitions (below) to be active */ +#define ONE_BAD_SB_NEIGHBOR 1 /* 1 => allow 1 "bad" bond type neighbor to a stereobond atom. 2004-06-02 */ + +/* more stereo settings */ +#define BREAK_ONE_MORE_SC_TIE 1 /* break one more tie when comparing possible stereocenter neighbors */ +#define BREAK_ALSO_NEIGH_TIE 0 /* post 1.12Beta 2004-08-20: if fixed neighbor has equ neighbors, fix the one with smaller canon. rank */ +#define BREAK_ALSO_NEIGH_TIE_ROTATE 1 /* post 1.12Beta 2004-09-02: break the second in 2nd psition; 1 works, 0 does not (example:MFCD01085607) */ + +#define STEREO_CENTER_BONDS_NORM 1 /* set length of the bonds around a stereocenter = 1 before getting the parity */ +#define STEREO_CENTER_BOND4_NORM 0 /* set length of the added bond around a stereocenter = 1 before getting the parity */ +#define NORMALIZE_INP_COORD 0 /* 0=>keep unchanged, 1 => make atom coordinates integer by normalizing to avg bond len 20 */ + +/* recent stereo */ +#define STEREO_WEDGE_ONLY 1 /* 1=> only pointed ends stereo bonds define stereo; 0=> both ends 1.12Beta */ +#define CHECK_C2v_S4_SYMM 0 /* post-1.12Beta 1=> check if a stereocenter has C2v or S4 symmetry; 0=>old mode */ + +#define EQL_H_NUM_TOGETHER 1 /* 1=> output 1-3,5H2 intead of 1-3H2,5H2 (CT_MODE_EQL_H_TOGETHER) */ +#define ABC_CT_NUM_CLOSURES 1 /* 1=> in coinnections compressed format output decimal number of closures instead of '-' */ + +/* temporary fix */ +#define SINGLET_IS_TRIPLET 1 /* 'singlet' means two electrons make a lone pair instead of 2 bonds + its effect on valence is same as the effect of a triplet */ + +/* defug: find structures where canonical partition is different from equitable */ +#define FIND_CANON_NE_EQUITABLE 0 /* 0=>normal mode */ + /* 1=> extract (set EXTR_FLAGS = (EXTR_CANON_NE_EQUITABLE)*/ + /* set cmd line options: /onlynonTAUT /: /UNCHARGEDACIDS:1 /DISCONSALT:0 /MOVEPOS:0 /DISCONMETAL:0 */ + +/* Debug: definitions for the extraction of the structures to the problem file */ + +/* definition of the flags for structure extraction to the + problem file (for debugging and non-standard searching) */ +#define EXTR_KNOWN_USED_TO_REMOVE_PARITY 0x000001 +#define EXTR_CALC_USED_TO_REMOVE_PARITY 0x000002 +#define EXTR_2EQL2CENTER_TO_REMOVE_PARITY 0x000004 +#define EXTR_HAS_ATOM_WITH_DEFINED_PARITY 0x000008 +#define EXTR_REMOVE_PARITY_WARNING 0x000010 +#define EXTR_SALT_WAS_DISCONNECTED 0x000020 +#define EXTR_SALT_PROTON_MOVED 0x000040 +#define EXTR_SALT_PROTON_MOVE_ERR_WARN 0x000080 +#define EXTR_METAL_WAS_DISCONNECTED 0x000100 +#define EXTR_METAL_WAS_NOT_DISCONNECTED 0x000200 +#define EXTR_NON_TRIVIAL_STEREO 0x000400 /* (Inv != Abs stereo) && (parities can't be obtained by inverting them) */ +#define EXTR_UNUSUAL_VALENCES 0x000800 +#define EXTR_HAS_METAL_ATOM 0x001000 +#define EXTR_TEST_TAUT3_SALTS_DONE 0x002000 /* non-oxygen t-points used to discover tautomerism of merged t-groups */ +#define EXTR_CANON_NE_EQUITABLE 0x004000 /* find structures where canonical partition is different from equitable */ +#define EXTR_HAS_PROTON_PN 0x008000 /* has movable H+ attached to N or P */ +#define EXTR_HAS_FEATURE 0x010000 /* found a feature */ +#define EXTR_TAUT_TREATMENT_CHARGES 0x020000 /* tautomeric treatment of charges */ +#define EXTR_TRANSPOSITION_EXAMPLES 0x040000 /* extract structures that have different mobile-H and fixed-H orders */ + +/* define conditions of structure extraction to the problem file */ +#define EXTR_MASK 0 /*EXTR_TAUT_TREATMENT_CHARGES*/ /*(EXTR_HAS_FEATURE)*/ /*(EXTR_UNUSUAL_VALENCES | EXTR_HAS_METAL_ATOM)*/ /* 0 to disable */ +#define EXTR_FLAGS 0 /*EXTR_TAUT_TREATMENT_CHARGES*/ /*(EXTR_HAS_FEATURE)*/ /*(EXTR_HAS_PROTON_PN)*/ /*(EXTR_UNUSUAL_VALENCES)*/ /*(EXTR_CANON_NE_EQUITABLE)*/ /*(EXTR_TEST_TAUT3_SALTS_DONE)*/ /*(EXTR_HAS_METAL_ATOM)*/ /* (EXTR_NON_TRIVIAL_STEREO)*/ /*(EXTR_METAL_WAS_DISCONNECTED)*/ /* (EXTR_REMOVE_PARITY_WARNING)*/ /*(EXTR_HAS_ATOM_WITH_DEFINED_PARITY) */ + + + +/* added tautomeric structures */ + +#define TAUT_TROPOLONE_7 1 /* 1=> tautomeric 7-member rings ON */ +#define TAUT_TROPOLONE_5 1 /* 1=> taut. similar to tropolone, 5-member ring */ +#define TAUT_4PYRIDINOL_RINGS 1 /* 1=> OH-C5H4N rings tautomerism */ +#define TAUT_PYRAZOLE_RINGS 1 /* 1=> tautomerizm in pyrazole rings */ +/* limitation on tautomerism detection: */ +#define TAUT_IGNORE_EQL_ENDPOINTS 0 /* 0=> even though 2 endpoints belong to same t-group check + them to find more alt bonds (new) + 1=> ignore and do not check (old mode) */ +#define TAUT_RINGS_ATTACH_CHAIN 1 /* 1=> allow only chain attachments to tautomeric endpoints */ + /* (except pyrazole, where is no tautomeric attachment) */ + /* 0=> allow taut. attachments from same ring system. Default=1 */ + +#define FIND_RING_SYSTEMS 1 /* 1 => find and mark ring systems, blocks, cut-vertices */ + /* Needed for 5- and 6-member ring tautomers and in other places */ + +#define FIND_RINS_SYSTEMS_DISTANCES 0 /* 1 => find ring system and atom distance from terminal */ +#define USE_DISTANCES_FOR_RANKING 0 /* 1 => rank ring systems according to distances from terminal */ + +#define DISPLAY_RING_SYSTEMS 0 /* 1 => for debug only; displays: */ + /* "block no"/"ring system no"/"cut-vertex (num. intersecting blocks-1)" */ + /* instead of ranks */ +/* consistency */ + +#if ( bRELEASE_VERSION==1 && bOUTPUT_ONE_STRUCT_TIME==1) +#undef bOUTPUT_ONE_STRUCT_TIME +#define bOUTPUT_ONE_STRUCT_TIME 0 +#endif + +/* consistency: bRELEASE_VERSION==1 needs FIND_RING_SYSTEMS=1 */ +#if ( bRELEASE_VERSION==1 && FIND_RING_SYSTEMS!=1 ) +#ifdef FIND_RING_SYSTEMS +#undef FIND_RING_SYSTEMS +#endif +#define FIND_RING_SYSTEMS 1 +#endif + +/* consistency: FIND_RINS_SYSTEMS_DISTANCES needs FIND_RING_SYSTEMS */ +#if ( FIND_RING_SYSTEMS != 1 ) + +#if ( FIND_RINS_SYSTEMS_DISTANCES == 1 ) +#undef FIND_RINS_SYSTEMS_DISTANCES +#define FIND_RINS_SYSTEMS_DISTANCES 0 +#endif + +#endif + +/* consistency: USE_DISTANCES_FOR_RANKING and DISPLAY_RING_SYSTEMS need FIND_RINS_SYSTEMS_DISTANCES */ +#if ( FIND_RINS_SYSTEMS_DISTANCES != 1 ) + +#if ( USE_DISTANCES_FOR_RANKING == 1 ) +#undef USE_DISTANCES_FOR_RANKING +#define USE_DISTANCES_FOR_RANKING 0 +#endif + +#if ( DISPLAY_RING_SYSTEMS == 1 ) +#undef DISPLAY_RING_SYSTEMS +#define DISPLAY_RING_SYSTEMS 0 +#endif + +#endif + + +#if ( FIND_RING_SYSTEMS==1 && (TAUT_TROPOLONE_7==1 || TAUT_TROPOLONE_5==1 || TAUT_4PYRIDINOL_RINGS==1 || TAUT_PYRAZOLE_RINGS) ) +#define TAUT_OTHER 1 +#else +#define TAUT_OTHER 0 +#endif + +#define APPLY_IMPLICIT_H_DOWN_RULE 0 /* 1=> if 3 non-H atoms around stereocenter are in same plane */ + /* then add "down" hydrogen to obtain sterecenter oparity */ + /* 0=> Implicit H stereo is unknown if all bonds to 3 non-H atoms */ + /* are in XY plane */ +#define ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS 1 /* 1=> consider bond in an alternating circuit stereogenic */ + /* even though it has adjacent tautomeric atom(s) */ + +#define IGNORE_TGROUP_WITHOUT_H 1 /* ignore tautomeric groups containing charges only */ + +#if ( DISCONNECT_SALTS == 1 ) +#define REMOVE_TGROUP_CHARGE 0 /* 0: do not remove charge information from tautomeric groups */ +#else +#define REMOVE_TGROUP_CHARGE 1 /* 1: remove charge information from tautomeric groups */ +#endif + +#if ( REMOVE_TGROUP_CHARGE == 1 ) +#define INCHI_T_NUM_MOVABLE 1 +#else +#define INCHI_T_NUM_MOVABLE 2 +#endif + +/******************************************/ +/* define canonicalization modes here */ +/******************************************/ + +#define USE_AUX_RANKING 1 /* 1=> get auxiliary ranking to accelerate canonicalization of H layers */ +#define USE_AUX_RANKING_ALL 1 /* 1=> include all vertices in CellGetMinNode() selection 0=> only vertices with highest ranks */ + +#define USE_ISO_SORT_KEY_HFIXED 0 /* 0=> normal mode: merge isotopic taut H to isotopic atom sorting key in + taut H-fixed canonicalization; + 1=> add one more "string" iso_sort_Hfixed to the canonicalization */ + +/************************ + questionable behavior + ************************/ +#define REL_RAC_STEREO_IGN_1_SC 0 /* 1=> drop from InChI sp3 stereo in components that have a single stereocenter */ + /* 0=> old-old mode (all such sp3 stereo is in the Identifier) */ +/* internal definitions; see also REQ_MODE_BASIC etc in ichi.h */ +#define CMODE_CT 0x000001 +#define CMODE_ISO 0x000002 +#define CMODE_ISO_OUT 0x000004 /* obsolete ? */ +#define CMODE_STEREO 0x000008 +#define CMODE_ISO_STEREO 0x000010 +#define CMODE_TAUT 0x000020 +#define CMODE_NOEQ_STEREO 0x000040 /* 5-24-2002: do not use stereo equivalence to accelerate */ +#define CMODE_REDNDNT_STEREO 0x000080 /* 6-11-2002: do not check for redundant stereo elements */ +#define CMODE_NO_ALT_SBONDS 0x000100 /* 6-14-2002: do not assign stereo to alternating bonds */ +/* new 10-10-2003 */ +#define CMODE_RELATIVE_STEREO 0x000200 /* REL All Relative Stereo */ +#define CMODE_RACEMIC_STEREO 0x000400 /* RAC All Racemic Stereo */ +#define CMODE_SC_IGN_ALL_UU 0x000800 /* IAUSC Ignore stereocenters if All Undef/Unknown */ +#define CMODE_SB_IGN_ALL_UU 0x001000 /* IAUSC Ignore stereobonds if All Undef/Unknown */ +/* end of 10-10-2003 */ + +/* external definitions */ +#define CANON_MODE_CT (CMODE_CT) +#define CANON_MODE_TAUT (CMODE_CT|CMODE_TAUT) +#define CANON_MODE_ISO (CMODE_CT|CMODE_ISO|CMODE_ISO_OUT) +#define CANON_MODE_STEREO (CMODE_CT|CMODE_STEREO) +#define CANON_MODE_ISO_STEREO (CMODE_CT|CMODE_ISO|CMODE_ISO_OUT|CMODE_ISO_STEREO) + +#define CANON_MODE_MASK 0x00FF /* used to determine canonicalization mode */ + +/************************************************* + * from d_norm.c + */ + +/* implemented definitions for CT_ATOMID */ +#define CT_ATOMID_DONTINCLUDE 1 +#define CT_ATOMID_IS_INITRANK 2 +#define CT_ATOMID_IS_CURRANK 3 + +/*************************************** + * canonicalization settings I + ***************************************/ + +#define CANON_TAUTOMERS 1 /* 1=> process tautomers */ +#define HYDROGENS_IN_INIT_RANKS 1 /* 1=> include num_H in initial ranking */ + +#define DOUBLE_BOND_NEIGH_LIST 0 /* 1 => include double bond neighbor in NeighList 2 times */ +#define INCL_NON_6AROM 1 /* 1 => mark all arom. bonds; 0=>mark arom. bonds only in 6-member rings */ + +#define CT_SMALLEST /* minimal CT */ + +#define CT_NEIGH_SMALLER /* in CT, include neighbors with smaller ranks */ + +#define CT_ATOMID CT_ATOMID_IS_CURRANK /*CT_ATOMID_DONTINCLUDE */ + +#define CT_NEIGH_INCREASE /* in CT, neighbors ranks increase */ + +#define USE_SYMMETRY_TO_ACCELERATE 1 /*1 => for fast CT canonicalization, to avoid full enumeration */ + +/* dependent definitions due to settings */ + +#ifdef CT_SMALLEST +#define CT_GREATER_THAN > +#define CT_INITVALUE ~0 +#define BEST_PARITY 1 /* odd */ +#define WORSE_PARITY 2 +#else +#define CT_GREATER_THAN < +#define CT_INITVALUE 0 +#define BEST_PARITY 2 /* even */ +#define WORSE_PARITY 1 +#endif + +#ifdef CT_NEIGH_SMALLER +#define CT_NEIGH_SMALLER_THAN < +#else +#define CT_NEIGH_SMALLER_THAN > +#endif + +/* verify corectness of dependent settings */ +#if !defined( CT_ATOMID ) + #error You have to #define CT_ATOMID +#else +#if ( defined( CT_ATOMID ) && CT_ATOMID==CT_ATOMID_DONTINCLUDE ) + #error CT_DELIMITER should be #defined if CT_ATOMID is not included +#endif +#endif + +/*************************************** + * canonicalization settings II + ***************************************/ +/* from extr_ct.h */ +#define ALL_ALT_AS_AROMATIC 1 /* 1 => all altrnate bonds (even in cyclooctateraene) treat as aromatic */ + /* and set DOUBLE_BOND_NEIGH_LIST = 0 */ +#define ANY_ATOM_IN_ALT_CYCLE 1 /* 1=> accept any atom in alternating bond circuit, 0=>only some */ + +#define EXCL_ALL_AROM_BOND_PARITY 0 /* 1 => any arom atom cannot belong to stereo bond. */ + /* This has presedence over ADD_6MEMB_AROM_BOND_PARITY=1 */ + /* 0 => include arom bonds parities according to */ + /* ADD_6MEMB_AROM_BOND_PARITY definition */ + +#if ( EXCL_ALL_AROM_BOND_PARITY == 0 ) +#define ADD_6MEMB_AROM_BOND_PARITY 1 /* 1 => all arom bonds are stereo bonds */ + /* 0 => only those arom bonds which do not belong to */ + /* 6-member arom rings are stereo bonds */ +#else +#define ADD_6MEMB_AROM_BOND_PARITY 0 /* 0 => standard; 1 => meaningless: ignore parities of non-6-member ring alt. bonds */ +#endif + +#define MAX_NUM_STEREO_BONDS 3 +#define MAX_NUM_STEREO_BOND_NEIGH 3 +#define MIN_NUM_STEREO_BOND_NEIGH 2 + +#define MAX_NUM_STEREO_ATOM_NEIGH 4 +#define STEREO_AT_MARK 8 /* > MAX_NUM_STEREO_BONDS */ + +#if ( ONLY_DOUBLE_BOND_STEREO == 1 ) /* { */ + +#ifdef ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS +#undef ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS +#define ALLOW_TAUT_ATTACHMENTS_TO_STEREO_BONDS 0 +#endif + +#ifdef EXCL_ALL_AROM_BOND_PARITY +#undef EXCL_ALL_AROM_BOND_PARITY +#define EXCL_ALL_AROM_BOND_PARITY 1 +#endif + +#ifdef ADD_6MEMB_AROM_BOND_PARITY +#undef ADD_6MEMB_AROM_BOND_PARITY +#define ADD_6MEMB_AROM_BOND_PARITY 0 +#endif + +#endif /* } ONLY_DOUBLE_BOND_STEREO */ + +/* dependent definitions due to settings */ +#if ( ALL_ALT_AS_AROMATIC == 1 && DOUBLE_BOND_NEIGH_LIST != 0 ) +#undef DOUBLE_BOND_NEIGH_LIST +#define DOUBLE_BOND_NEIGH_LIST 0 +#endif + + +/************************************* + * Drawing + */ + +#define DRAW_AROM_TAUT 1 /* 1=> draw distinct aromatic & tautomer bonds, 0=> don't */ + +/******************************************************/ +/* C O M M O N D E F I N I T I O N S */ +/******************************************************/ + + +/* input bTautFlags flags */ +#define TG_FLAG_TEST_TAUT__ATOMS 0x00000001 /* find regular tautomerism */ +#define TG_FLAG_DISCONNECT_SALTS 0x00000002 /* DISCONNECT_SALTS disconnect */ +#define TG_FLAG_TEST_TAUT__SALTS 0x00000004 /* DISCONNECT_SALTS if possible find long-range H/(-) taut. on =C-OH, >C=O */ +#define TG_FLAG_MOVE_POS_CHARGES 0x00000008 /* MOVE_CHARGES allow long-range movement of N(+), P(+) charges */ +#define TG_FLAG_TEST_TAUT2_SALTS 0x00000010 /* TEST_REMOVE_S_ATOMS multi-attachement long-range H/(-) taut. on =C-OH, >C=O */ +#define TG_FLAG_ALLOW_NO_NEGTV_O 0x00000020 /* CHARGED_SALTS_ONLY=0 (debug) find long-range H-only tautomerism on =C-OH, >C=O */ +#define TG_FLAG_MERGE_TAUT_SALTS 0x00000040 /* DISCONNECT_SALTS merge all "salt"-t-groups and other =C-OH into one t-group */ + +#define TG_FLAG_ALL_TAUTOMERIC (TG_FLAG_TEST_TAUT__ATOMS| \ + TG_FLAG_TEST_TAUT__SALTS| \ + TG_FLAG_TEST_TAUT2_SALTS| \ + TG_FLAG_MERGE_TAUT_SALTS) + +#define TG_FLAG_DISCONNECT_COORD 0x00000080 /* find "coord. centers" and disconnect them */ +#define TG_FLAG_RECONNECT_COORD 0x00000100 /* reconnect disconnected "coord. centers" */ +#define TG_FLAG_CHECK_VALENCE_COORD 0x00000200 /* do not disconnect "coord. centers" with usual valence */ +#define TG_FLAG_MOVE_HPLUS2NEUTR 0x00000400 /* move protons to neutralize */ +#define TG_FLAG_VARIABLE_PROTONS 0x00000800 /* add/remove protons to neutralize */ +#define TG_FLAG_HARD_ADD_REM_PROTONS 0x00001000 /* add/remove protons to neutralize in hard way */ +#define TG_FLAG_POINTED_EDGE_STEREO 0x00002000 /* only pointed edge of stereo bond defines stereo */ +#if ( FIX_ADJ_RAD == 1 ) +#define TG_FLAG_FIX_ADJ_RADICALS 0x00004000 /* remove adjacent radical-doubletes, fix valence */ +#endif +#define TG_FLAG_PHOSPHINE_STEREO 0x00008000 /* add phosphine sp3 stereo */ +#define TG_FLAG_ARSINE_STEREO 0x00010000 /* add arsine sp3 stereo */ +#define TG_FLAG_H_ALREADY_REMOVED 0x00020000 /* processing structure restored from InChI */ +#define TG_FLAG_FIX_SP3_BUG 0x00040000 /* fix sp3 stereo bug: overlapping 2D stereo bond & coordinate scaling */ + +#define TG_FLAG_KETO_ENOL_TAUT 0x00080000 /* turn on keto-enol tautomerism detection */ +#define TG_FLAG_1_5_TAUT 0x00100000 /* turn on 1,5 tautomerism detection */ + +/* FB2 */ +#define TG_FLAG_FIX_ISO_FIXEDH_BUG 0x00200000 /* fix bug found after v.102b (isotopic H representation) */ +#define TG_FLAG_FIX_TERM_H_CHRG_BUG 0x00400000 /* fix bug found after v.102b (moving H charge in 'remove_terminal_HDT') */ + +/* output bTautFlags flags */ + +#define TG_FLAG_MOVE_HPLUS2NEUTR_DONE 0x00000001 /* protons have been moved to neutralize */ +#define TG_FLAG_TEST_TAUT__ATOMS_DONE 0x00000002 +#define TG_FLAG_DISCONNECT_SALTS_DONE 0x00000004 +#define TG_FLAG_TEST_TAUT__SALTS_DONE 0x00000008 /* multiple H tautomerism */ +#define TG_FLAG_MOVE_POS_CHARGES_DONE 0x00000010 +#define TG_FLAG_TEST_TAUT2_SALTS_DONE 0x00000020 /* merged t-groups */ +#define TG_FLAG_ALLOW_NO_NEGTV_O_DONE 0x00000040 +#define TG_FLAG_MERGE_TAUT_SALTS_DONE 0x00000080 /* added non-taut O to taut groups */ + +#define TG_FLAG_ALL_SALT_DONE (TG_FLAG_TEST_TAUT__SALTS_DONE | \ + TG_FLAG_TEST_TAUT2_SALTS_DONE | \ + TG_FLAG_MERGE_TAUT_SALTS_DONE ) + +#define TG_FLAG_DISCONNECT_COORD_DONE 0x00000100 /* found and disconnected "coord. centers" */ +#define TG_FLAG_CHECK_VALENCE_COORD_DONE 0x00000200 /* did not disconnect "coord. centers" with usual valence */ +#define TG_FLAG_MOVE_CHARGE_COORD_DONE 0x00000400 /* changed charge of a disconnected ligand to fit its valence */ +#define TG_FLAG_FIX_ODD_THINGS_DONE 0x00000800 /* fixed drawing ambiguities in fix_odd_things */ +#define TG_FLAG_TEST_TAUT3_SALTS_DONE 0x00001000 /* merged t-groups + non-O taut atoms */ +#define TG_FLAG_FOUND_SALT_CHARGES_DONE 0x00002000 /* not assigned: preprocessing detected possibility of salt-type tautomerism */ +#define TG_FLAG_FOUND_ISOTOPIC_H_DONE 0x00004000 /* preprocessing detected isotopic H on "good" heteroatoms or isotopic H(+) */ +#define TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE 0x00008000 /* preprocessing detected isotopic H on "good" heteroatoms or isotopic H(+) */ +#if ( FIX_ADJ_RAD == 1 ) +#define TG_FLAG_FIX_ADJ_RADICALS_DONE 0x00010000 +#endif + +#if ( READ_INCHI_STRING == 1 ) +#define READ_INCHI_OUTPUT_INCHI 0x00000001 +#define READ_INCHI_SPLIT_OUTPUT 0x00000002 +#define READ_INCHI_KEEP_BALANCE_P 0x00000004 +#define READ_INCHI_TO_STRUCTURE 0x00000008 +#endif + + + + +/*********/ +/* */ +/* I/O */ +/* */ +/*********/ + +typedef struct tagOutputString +{ + char *pStr; + int nAllocatedLength; + int nUsedLength; + int nPtr; /* if the struct is used as expanding string buffer, */ + /* this field will store an expansion increment */ +} INCHI_IOSTREAM_STRING; + +typedef struct tagOutputStream +{ + /* output is directed either to resizable string buffer: */ + INCHI_IOSTREAM_STRING s; + /* or to the plain file: */ + FILE* f; + int type; +} INCHI_IOSTREAM; +/* INCHI_IOSTREAM.type values */ +#define INCHI_IOSTREAM_TYPE_NONE 0 +#define INCHI_IOSTREAM_TYPE_STRING 1 +#define INCHI_IOSTREAM_TYPE_FILE 2 + + + + +/***********/ +/* */ +/* DEBUG */ +/* */ +/***********/ + +#if ( defined(_WIN32) && defined(_DEBUG) && defined(_MSC_VER) /*&& !defined(COMPILE_ANSI_ONLY)*/ ) +/* debug: memory leaks tracking */ +#ifndef TARGET_LIB_FOR_WINCHI +#ifndef DO_NOT_TRACE_MEMORY_LEAKS +#define TRACE_MEMORY_LEAKS 1 /* 1=>trace, 0 => do not trace (Debug only) */ +#else +#define TRACE_MEMORY_LEAKS 0 +#endif +#else +#define TRACE_MEMORY_LEAKS 1 /* 1=>trace, **ALWAYS** =1 for TARGET_LIB_FOR_WINCHI */ +#endif +#else /* not MSC and not Debug */ +#define TRACE_MEMORY_LEAKS 0 /* 0: do not change */ +#endif + + +/* memory leaks tracking */ +#define INCHI_HEAPCHK /* default: no explicit heap checking during the execution */ + +#if ( TRACE_MEMORY_LEAKS == 1 ) +#ifdef _DEBUG + +#define inchi_malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define inchi_calloc(c, s) _calloc_dbg(c, s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define inchi_free(p) _free_dbg(p, _NORMAL_BLOCK) + +#ifdef TARGET_EXE_USING_API +/* INChI_MAIN specific */ +#define e_inchi_malloc(a) inchi_malloc(a) +#define e_inchi_calloc(a,b) inchi_calloc(a,b) +#define e_inchi_free(a) inchi_free(a) +#endif + +/*#define _CRTDBG_MAP_ALLOC*/ /* standard VC++ tool -- does not work with inchi_malloc(), etc */ + +#include + +/* to enable heap checking: #define CHECK_WIN32_VC_HEAP above #include "mode.h" in each source file or here */ +#ifdef CHECK_WIN32_VC_HEAP +/* -- Confirms the integrity of the memory blocks allocated in the debug heap -- */ +#undef INCHI_HEAPCHK +#define INCHI_HEAPCHK \ +do { \ + int tmp = _crtDbgFlag; \ + _crtDbgFlag |= _CRTDBG_ALLOC_MEM_DF; \ + _ASSERT( _CrtCheckMemory( ) ); \ + _crtDbgFlag = tmp; \ +} while(0); + +/* -- less thorough than _CrtCheckMemory() check: check minimal consistency of the heap -- */ +/* +#include +#define INCHI_HEAPCHK \ +do {\ + int heapstatus = _heapchk(); \ + _ASSERT( heapstatus != _HEAPBADBEGIN && heapstatus != _HEAPBADNODE && heapstatus != _HEAPBADPTR); \ +} while(0); +*/ +#endif + +#else +#undef TRACE_MEMORY_LEAKS +#define TRACE_MEMORY_LEAKS 0 +#endif /* _DEBUG */ +#endif /* TRACE_MEMORY_LEAKS */ + + + +/***********/ +/* */ +/* ALLOC */ +/* */ +/***********/ + +#ifdef TARGET_EXE_USING_API +/* INChI_MAIN specific */ +#ifndef inchi_malloc +#define inchi_malloc e_inchi_malloc +#endif +#ifndef inchi_calloc +#define inchi_calloc e_inchi_calloc +#endif +#ifndef inchi_free +#define inchi_free e_inchi_free +#endif + +#ifndef e_inchi_malloc +#define e_inchi_malloc malloc +#endif +#ifndef e_inchi_calloc +#define e_inchi_calloc calloc +#endif +#ifndef e_inchi_free +#define e_inchi_free(X) do{ if(X) free(X); }while(0) +#endif + +#else /* not TARGET_EXE_USING_API */ + +#ifndef inchi_malloc +#define inchi_malloc malloc +#endif +#ifndef inchi_calloc +#define inchi_calloc calloc +#endif +#ifndef inchi_free +#define inchi_free(X) do{ if(X) free(X); }while(0) +#endif + +#endif /* TARGET_EXE_USING_API */ + +/* allocation/deallocation */ +#define USE_ALLOCA 0 + +#if ( USE_ALLOCA == 1 ) +#define qmalloc(X) _alloca(X) +#define qfree(X) do{(X)=NULL;}while(0) +#else +#define qmalloc(X) inchi_malloc(X) +#define qfree(X) do{if(X){inchi_free(X);(X)=NULL;}}while(0) +#endif + +#if ( defined(_MSC_VER) && _MSC_VER >= 800 ) +#define fast_alloc(X) _alloca(X) +#define fast_free(X) +#else +#define fast_alloc(X) inchi_malloc(X) +#define fast_free(X) inchi_free(X) +#endif + +#define qzfree(X) do{if(X){inchi_free(X);(X)=NULL;}}while(0) + +/* rellocation */ + +#define MYREALLOC2(PTRTYPE1, PTRTYPE2, PTR1, PTR2, LEN1, LEN2, ERR) \ + do { \ + if( (LEN1) <= (LEN2) ) {\ + PTRTYPE1 * newPTR1 = (PTRTYPE1 *)inchi_calloc( (LEN2)+1, sizeof(PTRTYPE1) );\ + PTRTYPE2 * newPTR2 = (PTRTYPE2 *)inchi_calloc( (LEN2)+1, sizeof(PTRTYPE2) );\ + if ( newPTR1 && newPTR2 ) { \ + if ( (PTR1) && (LEN1) > 0 ) \ + (memcpy) ( newPTR1, (PTR1), (LEN1) * sizeof(PTRTYPE1) ); \ + if ( (PTR2) && (LEN1) > 0 ) \ + (memcpy) ( newPTR2, (PTR2), (LEN1) * sizeof(PTRTYPE2) ); \ + if ( PTR1 ) \ + inchi_free(PTR1); \ + if ( PTR2 ) \ + inchi_free(PTR2); \ + (PTR1) = newPTR1; \ + (PTR2) = newPTR2; \ + (LEN1) = (LEN2); \ + (ERR) = 0; \ + } else { \ + (ERR) = 1; \ + } \ + } else { (ERR) = 0; } \ + } while(0) + + +/* comment out the next line to disable */ +/*#define DEBUG_POLYMERS 0*/ +#ifndef DEBUG_POLYMERS +#define DEBUG_POLYMERS 2 +#endif + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + + +#endif /* _MODE_H_ */ diff --git a/INCHI-1-SRC/INCHI_BASE/src/mol2atom.c b/INCHI-1-SRC/INCHI_BASE/src/mol2atom.c new file mode 100644 index 0000000..34a1004 --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/mol2atom.c @@ -0,0 +1,1547 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include +#include + +#include "mode.h" +#include "mol_fmt.h" +#include "ichierr.h" +#include "util.h" +#include "strutil.h" +#include "inchi_api.h" + +/* + Convert input molecular data to internal representation + +*/ + +/* Local prototypes */ + +int ReadMolfileToInpAtoms( INCHI_IOSTREAM *inp_file, + int bDoNotAddH, inp_ATOM **at, + MOL_COORD **szCoord, + OrigAtDataPolymer **polymer, + OrigAtDataV3000 **v3000, + int max_num_at, + int *num_dimensions, + int *num_bonds, + const char *pSdfLabel, + char *pSdfValue, + long *Id, + long *lMolfileNumber, + INCHI_MODE *pInpAtomFlags, + int *err, + char *pStrErr ); +inp_ATOM* MakeInpAtomsFromMolfileData( MOL_FMT_DATA* mfdata, + int *num_atoms, + int *num_bonds, + inp_ATOM* at_inp, + int bDoNotAddH, + int *err, + char *pStrErr ); +int SetInpAtomsXYZ( MOL_FMT_DATA* mfdata, + int num_atoms, + inp_ATOM* at, + int *err, + char *pStrErr ); +void calculate_valences ( MOL_FMT_DATA* mfdata, + inp_ATOM* at, + int *num_atoms, + int bDoNotAddH, + int *err, + char *pStrErr); + + +int SetExtOrigAtDataByMolfileExtInput( MOL_FMT_DATA* mfdata, + OrigAtDataPolymer **polymer, + OrigAtDataV3000 **v3000, + char *pStrErr ); + + + + +/* + Create OrigInpData From Molfile +*/ +int CreateOrigInpDataFromMolfile( INCHI_IOSTREAM *inp_file, + ORIG_ATOM_DATA *orig_at_data, + int bMergeAllInputStructures, + int bGetOrigCoord, + int bDoNotAddH, + const char *pSdfLabel, + char *pSdfValue, + long *lSdfId, + long *lMolfileNumber, + INCHI_MODE *pInpAtomFlags, + int *err, + char *pStrErr ) +{ +int num_dimensions_new; +int num_inp_bonds_new; +int num_inp_atoms_new; +inp_ATOM *at_new = NULL; +inp_ATOM *at_old = NULL; +int nNumAtoms = 0; +MOL_COORD *szCoordNew = NULL; +MOL_COORD *szCoordOld = NULL; +OrigAtDataPolymer *polymer = NULL; +OrigAtDataV3000 *v3000 = NULL; + +int i, j; +int max_num_at; + + + if ( pStrErr ) + pStrErr[0] = '\0'; + + + /* NB: currently (v. 1.04) legacy CLI option "MERGE" is unsupported + so the loop below is always a single pass */ + + max_num_at = MAX_ATOMS; + if ( !( *pInpAtomFlags & FLAG_SET_INP_LARGE_ALLOWED ) ) + max_num_at = NORMALLY_ALLOWED_INP_MAX_ATOMS; + + do /* while ( !*err && bMergeAllInputStructures ) */ + { + + at_old = orig_at_data ? orig_at_data->at:NULL; /* save pointer to the previous allocation */ + + szCoordOld = orig_at_data ? orig_at_data->szCoord:NULL; + + + num_inp_atoms_new = ReadMolfileToInpAtoms( inp_file, bDoNotAddH, + orig_at_data ? &at_new:NULL, + (bGetOrigCoord && orig_at_data) ? &szCoordNew : NULL, + &polymer, &v3000, + max_num_at, &num_dimensions_new, &num_inp_bonds_new, + pSdfLabel, pSdfValue, lSdfId, lMolfileNumber, + pInpAtomFlags, err, pStrErr ); + + + if ( num_inp_atoms_new <= 0 && !*err ) + { + TREAT_ERR (*err, 0, "Empty structure"); + *err = 98; + } + else if ( orig_at_data && !num_inp_atoms_new && + 10 < *err && *err < 20 && + orig_at_data->num_inp_atoms > 0 && + bMergeAllInputStructures ) + { + *err = 0; /* end of file */ + break; + } + else if ( num_inp_atoms_new > 0 && orig_at_data ) + { + /* merge pOrigDataTmp + orig_at_data => pOrigDataTmp; */ + nNumAtoms = num_inp_atoms_new + orig_at_data->num_inp_atoms; + if ( nNumAtoms >= max_num_at ) /*MAX_ATOMS ) */ + { + TREAT_ERR (*err, 0, "Too many atoms [did you forget 'LargeMolecules' switch?]"); + *err = 70; + orig_at_data->num_inp_atoms = -1; + } + else if ( !at_old ) + { + /* the first structure */ + orig_at_data->at = at_new; + orig_at_data->szCoord = szCoordNew; + at_new = NULL; + szCoordNew = NULL; + orig_at_data->num_inp_atoms = num_inp_atoms_new; + orig_at_data->num_inp_bonds = num_inp_bonds_new; + orig_at_data->num_dimensions = num_dimensions_new; + /* v 1.05 */ + orig_at_data->polymer = polymer; + orig_at_data->v3000 = v3000; + polymer = NULL; + v3000 = NULL; + } + else if ( (orig_at_data->at = ( inp_ATOM* ) inchi_calloc( nNumAtoms, sizeof(inp_ATOM) )) && + (!szCoordNew || (orig_at_data->szCoord = (MOL_COORD *) inchi_calloc( nNumAtoms, sizeof(MOL_COORD) ))) + ) + { + /* switch at_new <--> orig_at_data->at; */ + if ( orig_at_data->num_inp_atoms ) + { + memcpy( orig_at_data->at, at_old, orig_at_data->num_inp_atoms * sizeof(orig_at_data->at[0]) ); + /* adjust numbering in the newly read structure */ + for ( i = 0; i < num_inp_atoms_new; i ++ ) + { + for ( j = 0; j < at_new[i].valence; j ++ ) + { + at_new[i].neighbor[j] += orig_at_data->num_inp_atoms; + } + at_new[i].orig_at_number += orig_at_data->num_inp_atoms; /* 12-19-2003 */ + } + if ( orig_at_data->szCoord && szCoordOld ) + memcpy( orig_at_data->szCoord, szCoordOld, orig_at_data->num_inp_atoms * sizeof(MOL_COORD) ); + } + if ( at_old ) + { + inchi_free( at_old ); + at_old = NULL; + } + if ( szCoordOld ) + { + inchi_free( szCoordOld ); + szCoordOld = NULL; + } + + /* Copy newly read structure */ + memcpy( orig_at_data->at + orig_at_data->num_inp_atoms, + at_new, + num_inp_atoms_new * sizeof(orig_at_data->at[0]) ); + + if ( orig_at_data->szCoord && szCoordNew ) + { + memcpy( orig_at_data->szCoord + orig_at_data->num_inp_atoms, + szCoordNew, + num_inp_atoms_new * sizeof(MOL_COORD) ); + } + + /* Add other things */ + orig_at_data->num_inp_atoms += num_inp_atoms_new; + orig_at_data->num_inp_bonds += num_inp_bonds_new; + orig_at_data->num_dimensions = inchi_max(num_dimensions_new, orig_at_data->num_dimensions); + /* v 1.05 */ + orig_at_data->polymer = polymer; + orig_at_data->v3000 = v3000; + } + else + { + TREAT_ERR (*err, 0, "Out of RAM"); + *err = -1; + } + } + else if ( num_inp_atoms_new > 0 ) + { + nNumAtoms += num_inp_atoms_new; + } + + if ( at_new ) + { + inchi_free( at_new ); + at_new = NULL; + } + if ( polymer ) + { + inchi_free( polymer ); + polymer = NULL; + } + if ( v3000 ) + { + inchi_free( v3000 ); + v3000 = NULL; + } + } while ( !*err && bMergeAllInputStructures ); + + + if ( szCoordNew ) + inchi_free( szCoordNew ); + if ( at_new ) + inchi_free( at_new ); + + if ( *err ) + FreeOrigAtData( orig_at_data ); + + if ( *err && !(10 < *err && *err < 20) && pStrErr && !pStrErr[0] ) + { + TREAT_ERR (*err, 0, "Unknown error"); /* */ + } + + return orig_at_data ? orig_at_data->num_inp_atoms + : nNumAtoms; +} + + +/* + ReadMolfileToInpAtoms +*/ +int ReadMolfileToInpAtoms( INCHI_IOSTREAM *inp_file, + int bDoNotAddH, + inp_ATOM **at, + MOL_COORD **szCoord, + OrigAtDataPolymer **polymer, + OrigAtDataV3000 **v3000, + int max_num_at, + int *num_dimensions, + int *num_bonds, + const char *pSdfLabel, + char *pSdfValue, + long *Id, + long *lMolfileNumber, + INCHI_MODE *pInpAtomFlags, + int *err, + char *pStrErr ) +{ +int num_atoms=0; +MOL_FMT_DATA *mfdata=NULL; +MOL_FMT_HEADER_BLOCK OnlyHeaderBlock, + *pOnlyHeaderBlock = NULL, + *pHdr; +MOL_FMT_CTAB OnlyCTab, + *pOnlyCTab = NULL; +char cSdfValueFirstChar=0; +int treat_polymers = *pInpAtomFlags & FLAG_SET_INP_POLYMERS_RECOGNIZED; + + if ( at ) + { + pOnlyHeaderBlock = NULL; + if ( *at && max_num_at ) + { + memset( *at, 0, max_num_at * sizeof(inp_ATOM) ); + } + if ( szCoord && *szCoord ) + { + inchi_free( *szCoord ); + *szCoord = NULL; + } + } + else + { + pOnlyHeaderBlock = &OnlyHeaderBlock; + pOnlyCTab = &OnlyCTab; + } + + if ( pSdfValue ) + { + cSdfValueFirstChar = pSdfValue[0]; + pSdfValue[0] = '\0'; + } + + + /* Read mol-formatted file (MOL or SD) block */ + mfdata = ReadMolfile( inp_file, + pOnlyHeaderBlock, + pOnlyCTab, + NULL != szCoord, + treat_polymers, + NULL, + 0, + Id, + pSdfLabel, + pSdfValue, + err, + pStrErr ); + + pHdr = ( mfdata && !pOnlyHeaderBlock ) ? &mfdata->hdr + : ( !mfdata && pOnlyHeaderBlock ) ? pOnlyHeaderBlock + : NULL; + + if ( lMolfileNumber && pHdr ) + *lMolfileNumber = MolfileExtractStrucNum( pHdr ); + + if ( pSdfValue && !pSdfValue[0] && + pSdfLabel && pSdfLabel[0] && + pHdr ) + { + if ( !inchi_stricmp(pSdfLabel, "MolfileName") ) + { + mystrncpy( pSdfValue, pHdr->molname, MAX_SDF_VALUE+1 ); + lrtrim( pSdfValue, NULL ); + } + else if ( !inchi_stricmp(pSdfLabel, "MolfileLine2") ) + { + mystrncpy( pSdfValue, pHdr->line2, MAX_SDF_VALUE+1 ); + lrtrim( pSdfValue, NULL ); + } + else if ( !inchi_stricmp(pSdfLabel, "MolfileComment") ) + { + mystrncpy( pSdfValue, pHdr->comment , MAX_SDF_VALUE+1 ); + lrtrim( pSdfValue, NULL ); + } + else if ( !inchi_stricmp(pSdfLabel, "MolfileIntRegNo") && pHdr->internal_regno ) + { + sprintf( pSdfValue, "%ld", pHdr->internal_regno ); + } + + if ( !pSdfValue[0] ) + pSdfValue[0] = cSdfValueFirstChar; + } + + + if ( mfdata && at && !*err ) + { + /* (*at) either points to already allocated memory or NULL */ + if ( mfdata->ctab.n_atoms <= max_num_at ) + { + + *at = MakeInpAtomsFromMolfileData( mfdata, &num_atoms, num_bonds, + *at, bDoNotAddH, err, pStrErr ); + + if ( *err >= 0 ) + { + *num_dimensions = SetInpAtomsXYZ( mfdata, num_atoms, + *at, err, pStrErr ); + + if ( szCoord ) + { + *szCoord = mfdata->ctab.coords; + mfdata->ctab.coords = NULL; + } + + /* Check that all atoms nos of Hs are in allowed range ( <=MAXVAL, currently 20 ) */ + if ( 1 ) + { + int i; + for ( i = 0; i < num_atoms; i++ ) + { + if ( (*at)[i].num_iso_H[0] + (*at)[i].num_iso_H[1] + (*at)[i].num_iso_H[2] > MAXVAL) + { + *err = 70 + 8; + TREAT_ERR(*err, 0, "Too many hydrogens at heavy atom"); + num_atoms = -1; + break; + } + } + } + + if ( !*err ) + { + *err = SetExtOrigAtDataByMolfileExtInput( mfdata, polymer, v3000, pStrErr ); + if ( *err ) + { + /*TREAT_ERR (*err, 0, "Error while getting extended Molfile input");*/ + *err = 80; + num_atoms = -1; + } + } + } + + else + { + if ( *err == -2 ) /* unk element */ + { + *err = 90; + num_atoms = -1; + } + } + } + + else + { + /* non-affordable struct size */ + TREAT_ERR (*err, 0, "Too many atoms [did you forget 'LargeMolecules' switch?]"); + *err = 70; + num_atoms = -1; + } + + if ( *err > 0 ) + *err += 100; + + /* 11-16-2004: use Chiral flag */ + if ( num_atoms > 0 && at && *at && mfdata && pInpAtomFlags ) + { + if ( mfdata->ctab.chiral_flag ) + *pInpAtomFlags |= FLAG_INP_AT_CHIRAL; + else + *pInpAtomFlags |= FLAG_INP_AT_NONCHIRAL; + } + } + + else if ( !at ) + { + num_atoms = pOnlyCTab->n_atoms; + } + + + if ( !pOnlyHeaderBlock ) + FreeMolfileData( mfdata ); + + return num_atoms; +} + + +/* + Make Inp Atoms From Molfile Data +*/ +inp_ATOM* MakeInpAtomsFromMolfileData( MOL_FMT_DATA* mfdata, + int *num_atoms, + int *num_bonds, + inp_ATOM* at_inp, + int bDoNotAddH, + int *err, char *pStrErr ) +{ +inp_ATOM *at = NULL; +/* char *bond_stereo = NULL; */ +AT_NUMB *p1, *p2; +int i, a1, a2, n1, n2, bonds, iso_atw_diff; +char bond_stereo, bond_type; +static int el_number_H = 0; + + if ( !el_number_H ) + { + el_number_H = get_periodic_table_number( "H" ); /* one-time initialization */ + } + + *err = 0; + + *num_atoms = mfdata->ctab.n_atoms; + *num_bonds = 0; + + + if ( MolfileHasNoChemStruc(mfdata) ) + goto exit_function; + + + /* Allocate memory if necessary */ + if ( at_inp ) + { + at = at_inp; + } + else + { + at = CreateInpAtom( *num_atoms ); + if ( !at ) + { + *err = -1; + TREAT_ERR_AND_FIN (*err, -1, exit_function, "Out of RAM"); + } + } + + + /* Copy atoms info */ + for ( i = 0; i < *num_atoms; i ++ ) + { + + mystrncpy( at[i].elname, mfdata->ctab.atoms[i].symbol, sizeof(at->elname) ); + /* at[i].chem_bonds_valence = mfdata->ctab.atoms[i].valence; */ + /* MOLfile valence; will change */ + + at[i].orig_at_number = (AT_NUMB)(i+1); + at[i].iso_atw_diff = mfdata->ctab.atoms[i].mass_difference; + at[i].charge = mfdata->ctab.atoms[i].charge; + at[i].radical = mfdata->ctab.atoms[i].radical; + + /* see SetInpAtomXYZ() + at[i].x = mfdata->ctab.atoms[i].fx; + at[i].y = mfdata->ctab.atoms[i].fy; + at[i].z = mfdata->ctab.atoms[i].fz; + */ + + iso_atw_diff = mfdata->ctab.atoms[i].mass_difference; + at[i].iso_atw_diff = iso_atw_diff==ZERO_ATW_DIFF ? 1 + : iso_atw_diff> 0 ? iso_atw_diff+1 + : iso_atw_diff; + +#if ( SINGLET_IS_TRIPLET == 1 ) + if ( at[i].radical == RADICAL_SINGLET ) + { + at[i].radical = RADICAL_TRIPLET; + } +#endif +#if ( bRELEASE_VERSION != 1 ) + if ( isdigit( at[i].elname[0] ) ) { /* for testing */ + mystrncpy( at[i].elname, "C", sizeof(at->elname) ); + } +#endif + + if ( ERR_ELEM == (n1 = get_periodic_table_number( at[i].elname ) ) ) + { + /* Case when elname contains more than 1 element: extract number of H if possible */ + at[i].num_H = extract_H_atoms( at[i].elname, at[i].num_iso_H ); + + if ( !at[i].elname[0] && NUMH(at, i) ) + { + /* alias contains only H. Added 2004-07-21, fixed 2004-07-22 + * move the heaviest isotope to the "central atom" + * Note: this must be consistent with H-H treatment in remove_terminal_HDT() + */ + strcpy( at[i].elname, "H" ); + if ( NUM_ISO_H(at,i) ) + { + int j; + for ( j = NUM_H_ISOTOPES-1; 0 <= j; j -- ) + { + if ( at[i].num_iso_H[j] ) + { + at[i].num_iso_H[j] --; + at[i].iso_atw_diff = 1 + j; + break; + } + } + } + else + { + at[i].num_H --; + } + } + if ( ERR_ELEM == (n1 = get_periodic_table_number( at[i].elname ) ) ) + { + n1 = 0; + } + } /* if ( ERR_ELEM == */ + + + at[i].el_number = (U_CHAR) n1; + if ( !n1 ) + { + *err = -2; + TREAT_ERR (*err, -2, "Unknown element(s):"); + TREAT_ERR_AND_FIN (*err, -2, exit_function, at[i].elname ); + } + else + /* replace explicit D or T with isotopic H (added 2003-06-02) */ + if ( el_number_H == n1 && !at[i].iso_atw_diff ) + { + switch( at[i].elname[0] ) + { + case 'D': + at[i].iso_atw_diff = 2; + mystrncpy( at[i].elname, "H", sizeof(at->elname) ); + break; + case 'T': + at[i].iso_atw_diff = 3; + mystrncpy( at[i].elname, "H", sizeof(at->elname) ); + break; + } + } + } /* eof copy atom info */ + + + + /*---------------- stereo information notes. ------------------------ + + Currently: 1. stereo sign + ========= -------------- + MOLfile (atom number = MOLfile atom number - 1, no stdata as an intermediate) + | if mfdata->ctab.bonds[i].atnum1 < mfdata->ctab.bonds[i].atnum2 + v then + inp_ATOM stereo > 0 + else + stereo < 0 + + 2. neighbor z-coordinate + ------------------------ + neighbor z-coord > 0 for Up if sign(stdata_bond_no) = sign(at[i].neighbor[j]-i) + + --------------------------------------------------------------------*/ + + + /* Copy bond info */ + for ( i = 0, bonds = 0; i < mfdata->ctab.n_bonds; i ++ ) + { + + bond_stereo = mfdata->ctab.bonds[i].bond_stereo; + bond_type = mfdata->ctab.bonds[i].bond_type; + + a1 = mfdata->ctab.bonds[i].atnum1-1; + a2 = mfdata->ctab.bonds[i].atnum2-1; + + if ( a1 < 0 || a1 >= *num_atoms || + a2 < 0 || a2 >= *num_atoms || + a1 == a2 ) + { + *err |= 1; /* bond for impossible atom number(s); ignored */ + TREAT_ERR (*err, 0, "Bond to nonexistent atom"); + continue; + } + + /* check for multiple bonds between same atoms */ + p1 = is_in_the_list( at[a1].neighbor, (AT_NUMB)a2, at[a1].valence ); + p2 = is_in_the_list( at[a2].neighbor, (AT_NUMB)a1, at[a2].valence ); + + if ( (p1 || p2) && (p1 || at[a1].valence < MAXVAL) && (p2 || at[a2].valence < MAXVAL) ) + { + n1 = p1? (p1 - at[a1].neighbor) : at[a1].valence ++; + n2 = p2? (p2 - at[a2].neighbor) : at[a2].valence ++; + TREAT_ERR (*err, 0, "Multiple bonds between two atoms"); + *err |= 2; /* multiple bonds between atoms */ + } + else if ( !p1 && !p2 && at[a1].valence < MAXVAL && at[a2].valence < MAXVAL ) + { + n1 = at[a1].valence ++; + n2 = at[a2].valence ++; + bonds ++; + } + else + { + char szMsg[64]; + *err |= 4; /* too large number of bonds. Some bonds ignored. */ + sprintf( szMsg, "Atom '%s' has more than %d bonds", + at[a1].valence>= MAXVAL? at[a1].elname:at[a2].elname, MAXVAL ); + TREAT_ERR (*err, 0, szMsg); + continue; + } + + if ( bond_type < MIN_INPUT_BOND_TYPE || bond_type > MAX_INPUT_BOND_TYPE ) + { + char szBondType[16]; + sprintf( szBondType, "%d", bond_type ); + bond_type = 1; + TREAT_ERR (*err, 0, "Unrecognized bond type:"); + TREAT_ERR (*err, 0, szBondType); + *err |= 8; /* Unrecognized Bond type replaced with single bond */ + } + + /* bond type */ + at[a1].bond_type[n1] = + at[a2].bond_type[n2] = bond_type; + + /* connection */ + at[a1].neighbor[n1] = (AT_NUMB)a2; + at[a2].neighbor[n2] = (AT_NUMB)a1; + + /* stereo */ + if ( bond_stereo == INPUT_STEREO_DBLE_EITHER /* 3 */ ) + { + at[a1].bond_stereo[n1] = + at[a2].bond_stereo[n2] = + STEREO_DBLE_EITHER; + } + else if ( bond_stereo == INPUT_STEREO_SNGL_UP || /* 1 */ + bond_stereo == INPUT_STEREO_SNGL_EITHER || /* 4 */ + bond_stereo == INPUT_STEREO_SNGL_DOWN /* 6 */ ) + { + char cStereo; + switch ( bond_stereo ) + { + case INPUT_STEREO_SNGL_UP: + cStereo = STEREO_SNGL_UP; + break; + case INPUT_STEREO_SNGL_EITHER: + cStereo = STEREO_SNGL_EITHER; + break; + case INPUT_STEREO_SNGL_DOWN: + cStereo = STEREO_SNGL_DOWN; + break; + } + at[a1].bond_stereo[n1] = cStereo; /* >0: the wedge (pointed) end is at this atom, a1 */ + at[a2].bond_stereo[n2] = -cStereo; /* <0: the wedge (pointed) end is at the opposite atom, a1 */ + } + else if ( bond_stereo ) + { + *err |= 16; /* Ignored unrecognized Bond stereo */ + TREAT_ERR (*err, 0, "Unrecognized bond stereo"); + continue; + } + } /* eof copy bond info */ + + *num_bonds = bonds; + + /* special valences */ + calculate_valences (mfdata, at, num_atoms, bDoNotAddH, err, pStrErr); + + +exit_function:; + return at; +} + + +/* + +*/ +void calculate_valences ( MOL_FMT_DATA* mfdata, + inp_ATOM* at, + int *num_atoms, + int bDoNotAddH, + int *err, + char *pStrErr) +{ +int bNonMetal; +int a1, a2, n1, n2, valence; +AT_NUMB *p1; + + /* special valences */ + + for ( bNonMetal = 0; bNonMetal < 2; bNonMetal ++ ) + { + for ( a1 = 0; a1 < *num_atoms; a1 ++ ) + { + int num_bond_type[MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE + 1], + bond_type, + bHasMetalNeighbor; + /* should the "!=" be replaced with "==" ??? */ + if ( bNonMetal == is_el_a_metal( at[a1].el_number ) ) + { + /* first process all metals, after that all non-metals */ + continue; + } + + memset( num_bond_type, 0, sizeof(num_bond_type) ); + + /* valence = at[a1].chem_bonds_valence; */ /* save atom valence if available */ + /* 2006-08-31: fix for uncharged >N(IV)- in an aromatic ring */ + + valence = + (mfdata && mfdata->ctab.atoms) ? mfdata->ctab.atoms[a1].valence + : at[a1].chem_bonds_valence; + + at[a1].chem_bonds_valence = 0; + bHasMetalNeighbor = 0; + for ( n1 = 0; n1 < at[a1].valence; n1 ++ ) + { + bond_type = at[a1].bond_type[n1] - MIN_INPUT_BOND_TYPE; + if ( bond_type < 0 || bond_type > MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE ) + { + bond_type = 0; + TREAT_ERR (*err, 0, "Unknown bond type in MOLfile assigned as a single bond"); + } + num_bond_type[ bond_type ] ++; + /* -- too a radical solution -- removed from next to ver 1.12B --- */ + } + + for ( n1 = 0; + MIN_INPUT_BOND_TYPE + n1 <= 3 && + MIN_INPUT_BOND_TYPE + n1 <= MAX_INPUT_BOND_TYPE; + n1 ++ ) + { + /* add all bond orders except for "aromatic" bonds */ + at[a1].chem_bonds_valence += (MIN_INPUT_BOND_TYPE + n1) * num_bond_type[n1]; + } + + n2 = 0; + if ( MIN_INPUT_BOND_TYPE <= BOND_TYPE_ALTERN && + BOND_TYPE_ALTERN <= MAX_INPUT_BOND_TYPE && + ( n2 = num_bond_type[BOND_TYPE_ALTERN-MIN_INPUT_BOND_TYPE] ) ) + { + /* accept input aromatic bonds for now */ + switch ( n2 ) + { + case 2: + at[a1].chem_bonds_valence += 3; /* =A- */ + break; + case 3: + at[a1].chem_bonds_valence += 4; /* =A< */ + break; + default: + /* if 1 or >= 4 aromatic bonds then replace */ + /* such bonds with single bonds */ + /* and detect an error in the input structure */ + for ( n1 = 0; n1 < at[a1].valence; n1 ++ ) + { + if ( at[a1].bond_type[n1] == BOND_TYPE_ALTERN ) + { + a2 = at[a1].neighbor[n1]; + p1 = is_in_the_list( at[a2].neighbor, ( AT_NUMB)a1, + at[a2].valence ); + if ( p1 ) + { + at[a1].bond_type[n1] = + at[a2].bond_type[p1-at[a2].neighbor] = BOND_TYPE_SINGLE; + } + else + { + *err = -2; /* Program error */ + TREAT_ERR (*err, 0, "Program error interpreting MOLfile"); + return; /* no structure */ + } + } + } + + at[a1].chem_bonds_valence += n2; + *err |= 32; + TREAT_ERR (*err, 0, "Atom has 1 or more than 3 aromatic bonds"); + n2 = 0; + break; + } + } + + if ( n2 && !valence ) + { + /* atom has aromatic bonds AND the chemical valence is not known */ + + int num_H = NUMH(at, a1); + /* bug fix 2006-08-25: aliased H result in num_H > 0 */ + /* => wrong call to detect_unusual_el_valence() */ + int chem_valence = at[a1].chem_bonds_valence /*+ num_H*/; + + int bUnusualValenceArom = + detect_unusual_el_valence( (int)at[a1].el_number, at[a1].charge, + at[a1].radical, chem_valence, + num_H, at[a1].valence ); + int bUnusualValenceNoArom = + detect_unusual_el_valence( (int)at[a1].el_number, at[a1].charge, + at[a1].radical, chem_valence-1, + num_H, at[a1].valence ); + +#if ( CHECK_AROMBOND2ALT == 1 ) + if ( bUnusualValenceArom && + !bUnusualValenceNoArom && + 0 == nBondsValToMetal( at, a1) ) +#else + if ( bUnusualValenceArom && !bUnusualValenceNoArom ) +#endif + { + /* typically NH in 5-member aromatic ring */ + at[a1].chem_bonds_valence --; + } + } + else if ( n2 && valence ) + { + /* atom has aromatic bonds AND the chemical valence is known */ + int num_H = NUMH(at, a1); + int chem_valence = at[a1].chem_bonds_valence + num_H; + if ( valence == chem_valence-1 ) + { + /* typically NH in 5-member aromatic ring */ + at[a1].chem_bonds_valence --; + } + } + + /* Set number of hydrogen atoms */ + if (mfdata) + { + at[a1].num_H = get_num_H( at[a1].elname, + at[a1].num_H, + at[a1].num_iso_H, + at[a1].charge, at[a1].radical, + at[a1].chem_bonds_valence, + mfdata->ctab.atoms[a1].valence, /* instead of valence */ + mfdata->ctab.atoms[a1].atom_aliased_flag, + bDoNotAddH, + bHasMetalNeighbor ); + } + } + } /* for ( bNonMetal = ... */ + + return; +} + + +/* + SetInpAtomsXYZ +*/ +int SetInpAtomsXYZ( MOL_FMT_DATA* mfdata, + int num_atoms, + inp_ATOM* at, + int *err, + char *pStrErr ) +{ +int i, num_dimensions=0; + +#if ( NORMALIZE_INP_COORD == 1 ) +int do_scale_xyz=1; +#else +int do_scale_xyz=0; +#endif +double x0, y0, z0, xmin, ymin, zmin, scaler; + + + num_dimensions = MolfileGetXYZDimAndNormFactors( mfdata, + do_scale_xyz, + &x0, &y0, &z0, + &xmin, &ymin, &zmin, + &scaler, + err, pStrErr ); + + + if ( num_dimensions==0 ) + goto exit_function; + + for ( i = 0; i < num_atoms; i ++ ) + { + + double x = mfdata->ctab.atoms[i].fx; + double y = mfdata->ctab.atoms[i].fy; + double z = mfdata->ctab.atoms[i].fz; + + if ( !do_scale_xyz ) + { + at[i].x = x; + at[i].y = y; + at[i].z = z; + } + else + { + x = (x - xmin)*scaler + x0; + y = (y - ymin)*scaler + y0; + z = (z - zmin)*scaler + z0; + /* floor() behavior is not well defined for negative arguments. + * Use positive arguments only to get nearest integer. + */ + at[i].x = ( x >= 0.0 )? (int)floor( x + 0.5 ) : -(int)floor( -x + 0.5 ); + at[i].y = ( y >= 0.0 )? (int)floor( y + 0.5 ) : -(int)floor( -y + 0.5 ); + at[i].z = ( z >= 0.0 )? (int)floor( z + 0.5 ) : -(int)floor( -z + 0.5 ); + } + } + + +exit_function:; + return num_dimensions; +} + + +/* + +*/ +inp_ATOM *CreateInpAtom( int num_atoms ) +{ + void *p = inchi_calloc(num_atoms, sizeof(inp_ATOM) ); + return (inp_ATOM* )p; +} + + +/* + +*/ +void FreeInpAtom( inp_ATOM **at ) +{ + if ( at && *at ) + { + inchi_free( *at ); + *at = NULL; + } + return; +} + + +/* + +*/ +void FreeInpAtomData( INP_ATOM_DATA *inp_at_data ) +{ + if ( inp_at_data ) + { + FreeInpAtom( &inp_at_data->at ); + FreeInpAtom( &inp_at_data->at_fixed_bonds ); + memset( inp_at_data, 0, sizeof(*inp_at_data) ); + } + return; +} + + +/* + +*/ +int CreateInpAtomData( INP_ATOM_DATA *inp_at_data, + int num_atoms, + int create_at_fixed_bonds ) +{ + FreeInpAtomData( inp_at_data ); + + if ( (inp_at_data->at = CreateInpAtom( num_atoms )) && + (!create_at_fixed_bonds || (inp_at_data->at_fixed_bonds = CreateInpAtom( num_atoms) ) ) ) + { + inp_at_data->num_at = num_atoms; + return 1; + } + + FreeInpAtomData( inp_at_data ); + + return 0; +} + + +/* + +*/ +void FreeCompAtomData( COMP_ATOM_DATA *inp_at_data ) +{ + FreeInpAtom( &inp_at_data->at ); + + if ( inp_at_data->nOffsetAtAndH ) + inchi_free( inp_at_data->nOffsetAtAndH ); + memset( inp_at_data, 0, sizeof(*inp_at_data) ); +} + + +#ifndef TARGET_API_LIB +/* + +*/ +int CreateCompAtomData( COMP_ATOM_DATA *inp_at_data, + int num_atoms, + int num_components, + int bIntermediateTaut ) +{ + FreeCompAtomData( inp_at_data ); + + if ( (inp_at_data->at = CreateInpAtom( num_atoms )) && + (num_components <= 1 || bIntermediateTaut || + (inp_at_data->nOffsetAtAndH = (AT_NUMB*)inchi_calloc(sizeof(inp_at_data->nOffsetAtAndH[0]), 2*(num_components+1))))) + { + inp_at_data->num_at = num_atoms; + inp_at_data->num_components = (num_components>1)? num_components : 0; + return 1; + } + + FreeCompAtomData( inp_at_data ); + + return 0; +} +#endif + +#ifndef COMPILE_ANSI_ONLY +/* + FreeInfAtom +*/ +void FreeInfAtom( inf_ATOM **at ) +{ + if ( at && *at ) + { + inchi_free( *at ); + *at = NULL; + } + return; +} + + +/* + CreateInfAtom +*/ +inf_ATOM *CreateInfAtom( int num_atoms ) +{ + return (inf_ATOM* ) inchi_calloc(num_atoms, sizeof(inf_ATOM) ); +} + + +/* + FreeInfoAtomData +*/ +void FreeInfoAtomData( INF_ATOM_DATA *inf_at_data ) +{ + FreeInfAtom( &inf_at_data->at ); + if ( inf_at_data->pStereoFlags ) + inchi_free( inf_at_data->pStereoFlags ); + memset(inf_at_data, 0, sizeof(*inf_at_data)); + return; +} + + +/* + CreateInfoAtomData +*/ +int CreateInfoAtomData( INF_ATOM_DATA *inf_at_data, + int num_atoms, + int num_components ) +{ + FreeInfoAtomData( inf_at_data ); + + memset( inf_at_data, 0, sizeof(*inf_at_data) ); + + if ( (inf_at_data->at = CreateInfAtom( num_atoms )) && + (num_components <= 1 || + (inf_at_data->pStereoFlags = (AT_NUMB *)inchi_calloc(num_components+1, sizeof(inf_at_data->pStereoFlags[0]))) + ) + ) + { + inf_at_data->num_at = num_atoms; + inf_at_data->num_components = num_components; + return 1; + } + + FreeInfoAtomData( inf_at_data ); + + return 0; +} + + +/* + AllocateInfoAtomData +*/ +int AllocateInfoAtomData( INF_ATOM_DATA *inf_at_data, + int num_atoms, + int num_components ) +{ + if ( inf_at_data->at = CreateInfAtom( num_atoms ) ) + { + if ( num_components > 1 && + !(inf_at_data->pStereoFlags = (AT_NUMB *)inchi_calloc(num_components+1, sizeof(inf_at_data->pStereoFlags[0]))) ) + { + FreeInfAtom( &inf_at_data->at ); + return 0; + } + return 1; + } + + return 0; +} + + +/* + DuplicateInfoAtomData +*/ +int DuplicateInfoAtomData( INF_ATOM_DATA *inf_at_data_to, + const INF_ATOM_DATA *inf_at_data_from) +{ + *inf_at_data_to = *inf_at_data_from; + + if ( AllocateInfoAtomData( inf_at_data_to, inf_at_data_from->num_at, inf_at_data_from->num_components ) ) + { + memcpy( inf_at_data_to->at, inf_at_data_from->at, + inf_at_data_from->num_at * sizeof(inf_at_data_to->at[0])); + if ( inf_at_data_to->pStereoFlags && inf_at_data_from->pStereoFlags ) + { + memcpy( inf_at_data_to->pStereoFlags, inf_at_data_from->pStereoFlags, + (inf_at_data_from->num_components+1)*sizeof(inf_at_data_to->pStereoFlags[0])); + } + return 1; + } + + return 0; +} +#endif /* COMPILE_ANSI_ONLY */ + + + +/* + FreeOrigAtData +*/ +void FreeOrigAtData( ORIG_ATOM_DATA *orig_at_data ) +{ + if ( !orig_at_data ) + return; + + FreeInpAtom( &orig_at_data->at ); + + if ( orig_at_data->nCurAtLen ) + inchi_free( orig_at_data->nCurAtLen ); + + if ( orig_at_data->nOldCompNumber ) + inchi_free( orig_at_data->nOldCompNumber ); + + if ( orig_at_data->szCoord ) + inchi_free( orig_at_data->szCoord ); + + if ( orig_at_data->nEquLabels ) + inchi_free( orig_at_data->nEquLabels ); + + if ( orig_at_data->nSortedOrder ) + inchi_free( orig_at_data->nSortedOrder ); + + /* v 1.05 */ + FreeExtOrigAtData ( orig_at_data->polymer, orig_at_data->v3000 ); + + memset( orig_at_data, 0, sizeof(*orig_at_data) ); + + return; +} + + +/* + Free v. 1.05 extensions stuff +*/ +void FreeExtOrigAtData( OrigAtDataPolymer *pd, OrigAtDataV3000 *v3k ) +{ + int k; + + OrigAtDataPolymer_Free( pd ); + pd = NULL; + + if ( v3k ) + { + if ( v3k->atom_index_orig ) + { + inchi_free( v3k->atom_index_orig ); + v3k->atom_index_orig = NULL; + } + if ( v3k->atom_index_fin ) + { + inchi_free( v3k->atom_index_fin ); + v3k->atom_index_fin = NULL; + } + if ( v3k->n_haptic_bonds && v3k->lists_haptic_bonds ) + { + for (k=0; kn_haptic_bonds; k++) + if ( v3k->lists_haptic_bonds[k] ) + { + inchi_free( v3k->lists_haptic_bonds[k] ); + v3k->lists_haptic_bonds[k] = NULL; + } + inchi_free( v3k->lists_haptic_bonds ); + v3k->lists_haptic_bonds = NULL; + } + if ( v3k->n_steabs && v3k->lists_steabs ) + { + for (k=0; kn_steabs; k++) + if ( v3k->lists_steabs[k] ) + { + inchi_free( v3k->lists_steabs[k] ); + v3k->lists_steabs[k] = NULL; + } + inchi_free( v3k->lists_steabs ); + v3k->lists_steabs = NULL; + } + if ( v3k->n_sterel && v3k->lists_sterel ) + { + for (k=0; kn_sterel; k++) + if ( v3k->lists_sterel[k] ) + { + inchi_free( v3k->lists_sterel[k] ); + v3k->lists_sterel[k] = NULL; + } + inchi_free( v3k->lists_sterel ); + v3k->lists_sterel = NULL; + } + if ( v3k->n_sterac && v3k->lists_sterac ) + { + for (k=0; kn_sterac; k++) + if ( v3k->lists_sterac[k] ) + { + inchi_free( v3k->lists_sterac[k] ); + v3k->lists_sterac[k] = NULL; + } + inchi_free( v3k->lists_sterac ); + v3k->lists_sterac = NULL; + } + memset( v3k, 0, sizeof( *v3k ) ); + } + + return; +} + + +int SetExtOrigAtDataByMolfileExtInput( MOL_FMT_DATA* mfdata, + OrigAtDataPolymer **ppPolymer, + OrigAtDataV3000 **ppV3000, + char *pStrErr ) +{ + int k, m, err = 0; + OrigAtDataV3000 *pv=NULL; + int nsgroups = mfdata->ctab.sgroups.used; + + /* Polymers */ + if ( nsgroups > 0 ) + { + /* Prepare OrigAtDataPolymer container */ + *ppPolymer = (OrigAtDataPolymer *) inchi_calloc( 1, sizeof(OrigAtDataPolymer) ); + if ( !(*ppPolymer) ) + { + TREAT_ERR( err, 9001, "Out of RAM"); + goto exitf; + } + + + /* Convert Molfile's Sgroup's to OrigAtDataPolymerUnit's */ + (*ppPolymer)->units = (OrigAtDataPolymerUnit**) inchi_calloc( nsgroups, sizeof((*ppPolymer)->units[0]) ); + if ( !(*ppPolymer)->units ) + { + TREAT_ERR( err, 9001, "Out of RAM"); + goto exitf; + } + memset( (*ppPolymer)->units, 0, sizeof( *(*ppPolymer)->units ) ); + + (*ppPolymer)->n = nsgroups; + (*ppPolymer)->valid = -1; + (*ppPolymer)->really_do_phase_shift = 0; + (*ppPolymer)->is_in_reconn = 0; + (*ppPolymer)->star_atoms = NULL; + + for (k=0; kctab.sgroups.group[k]; + + OrigAtDataPolymerUnit* unitk = (*ppPolymer)->units[k] = (OrigAtDataPolymerUnit*) inchi_calloc( 1, sizeof(OrigAtDataPolymerUnit) ); + + if (!unitk ) + { + TREAT_ERR( err, 9001, "Out of RAM"); + goto exitf; + } + + memset( unitk, 0, sizeof( *unitk ) ); + unitk->id = groupk->id; + unitk->type = groupk->type; + unitk->subtype = groupk->subtype; + unitk->conn = groupk->conn; + unitk->label = groupk->label; + unitk->real_kind = POLYMER_UNIT_KIND_UNKNOWN; + + for (q=0; q<4; q++) + { + unitk->xbr1[q] = groupk->xbr1[q]; + unitk->xbr2[q] = groupk->xbr2[q]; + } + strcpy( unitk->smt, groupk->smt ); + unitk->na = groupk->alist.used; + unitk->alist = (int *) inchi_calloc( unitk->na, sizeof(int) ); + if (!unitk->alist ) + { + TREAT_ERR( err, 9001, "Out of RAM"); + goto exitf; + } + for (m=0; mna; m++) + { + unitk->alist[m] = groupk->alist.item[m]; + } + unitk->nb = groupk->blist.used; + if ( unitk->nb > 0 ) + { + unitk->blist = (int *) inchi_calloc( 2*unitk->nb, sizeof(int) ); + if (!unitk->blist ) + { + TREAT_ERR( err, 9001, "Out of RAM"); + goto exitf; + } + for (m=0; m < groupk->blist.used; m++) + { + int ib, ia1, ia2; + ib = groupk->blist.item[m]; + if ( ib<1 || ib>mfdata->ctab.n_bonds ) + { + TREAT_ERR( err, 9004, "Polymer unit in ctab.bonds[ib-1].atnum1; + ia2 = mfdata->ctab.bonds[ib-1].atnum2; + unitk->blist[2*m] = ia1; + unitk->blist[2*m+1] = ia2; + if ( !strcmp(mfdata->ctab.atoms[ia1-1].symbol,"H") || + !strcmp(mfdata->ctab.atoms[ia2-1].symbol,"H") ) + { + TREAT_ERR ( err, 9002, "Hydrogen as polymer end group is not supported"); + goto exitf; + } + } + } + else + unitk->blist = NULL; + } + } + + /* V3000 Extensions */ + if ( mfdata->ctab.v3000 ) + { + int m, k, nn; + MOL_FMT_v3000 *mpv = mfdata->ctab.v3000; + + *ppV3000 = (OrigAtDataV3000 *) inchi_calloc( 1, sizeof(OrigAtDataV3000) ); + pv = *ppV3000; + if ( !pv ) + { + TREAT_ERR( err, 9001, "Out of RAM"); + goto exitf; + } + memset( pv, 0, sizeof(*pv) ); + + + pv->n_collections = mpv->n_collections; + pv->n_haptic_bonds = mpv->n_haptic_bonds; + pv->n_non_haptic_bonds = mpv->n_non_haptic_bonds; + pv->n_sgroups = mpv->n_sgroups; + pv->n_non_star_atoms = mpv->n_non_star_atoms; + pv->n_star_atoms = mpv->n_star_atoms; + pv->n_steabs = mpv->n_steabs; + pv->n_sterac = mpv->n_sterac; + pv->n_sterel = mpv->n_sterel; + pv->n_3d_constraints = mpv->n_3d_constraints; + + if ( mpv->atom_index_orig ) + { + pv->atom_index_orig = (int *) inchi_calloc( mfdata->ctab.n_atoms, sizeof(int) ); + if ( NULL==pv->atom_index_orig ) + { + TREAT_ERR( err, 9001, "Out of RAM"); + goto exitf; + } + memcpy( pv->atom_index_orig, mpv->atom_index_orig, mfdata->ctab.n_atoms); + } + if ( mpv->atom_index_fin ) + { + pv->atom_index_fin = (int *) inchi_calloc( mfdata->ctab.n_atoms, sizeof(int) ); + if ( NULL==pv->atom_index_fin ) + { + TREAT_ERR( err, 9001, "Out of RAM"); + goto exitf; + } + memcpy( pv->atom_index_fin, mpv->atom_index_fin, mfdata->ctab.n_atoms); + } + if ( mpv->n_haptic_bonds && mpv->haptic_bonds ) + { + pv->lists_haptic_bonds = (int **) calloc( mpv->n_haptic_bonds, sizeof (int*) ); + if ( NULL==pv->lists_haptic_bonds ) { TREAT_ERR( err, 9001, "Out of RAM"); goto exitf; } + for (m=0; mn_haptic_bonds; m++) + { + int *lst=NULL; + int *mol_lst = mpv->haptic_bonds->lists[m]; + nn = mol_lst[2] + 3; + lst = pv->lists_haptic_bonds[m] = (int *) calloc( nn, sizeof (int) ); + if ( NULL==lst ) { TREAT_ERR( err, 9001, "Out of RAM"); goto exitf; } + for (k=0; kn_steabs && mpv->steabs ) + { + pv->lists_steabs = (int **) calloc( mpv->n_steabs, sizeof (int*) ); + if ( NULL==pv->lists_steabs ) { TREAT_ERR( err, 9001, "Out of RAM"); goto exitf; } + for (m=0; mn_steabs; m++) + { + int *lst=NULL; + int *mol_lst = mpv->steabs->lists[m]; + nn = mol_lst[1] + 2; + lst = pv->lists_steabs[m] = (int *) calloc( nn, sizeof (int) ); + if ( NULL==lst ) { TREAT_ERR( err, 9001, "Out of RAM"); goto exitf; } + for (k=0; kn_sterac && mpv->sterac ) + { + pv->lists_sterac = (int **) calloc( mpv->n_sterac, sizeof (int*) ); + if ( NULL==pv->lists_sterac ) { TREAT_ERR( err, 9001, "Out of RAM"); goto exitf; } + for (m=0; mn_sterac; m++) + { + int *lst=NULL; + int *mol_lst = mpv->sterac->lists[m]; + nn = mol_lst[1] + 2; + lst = pv->lists_sterac[m] = (int *) calloc( nn, sizeof (int) ); + if ( NULL==lst ) { TREAT_ERR( err, 9001, "Out of RAM"); goto exitf; } + for (k=0; kn_sterel && mpv->sterel ) + { + pv->lists_sterel = (int **) calloc( mpv->n_sterel, sizeof (int*) ); + if ( NULL==pv->lists_sterel ) { TREAT_ERR( err, 9001, "Out of RAM"); goto exitf; } + for (m=0; mn_sterel; m++) + { + int *lst=NULL; + int *mol_lst = mpv->sterel->lists[m]; + nn = mol_lst[1] + 2; + lst = pv->lists_sterel[m] = (int *) calloc( nn, sizeof (int) ); + if ( NULL==lst ) { TREAT_ERR( err, 9001, "Out of RAM"); goto exitf; } + for (k=0; k + +#include "ichisize.h" + +/* + Data structures and constants +*/ + + +/*************** read MOL file V2000.************************/ +/* ref: A.Dalby et al, "Description of Several Chemical Structure + * File Formats Used by Computer Programs Developed at Molecular + * Design Limited", J. Chem. Inf. Comput. Sci., 1992, 32, 244-255. + */ + +/*************** read MOL file V3000.************************/ +/* http://download.accelrys.com/freeware/ctfile-formats/CTFile-formats.zip + * Last accessed 2013-06-11 +*/ + +/*-----------*/ +/* CONSTANTS */ +/*-----------*/ + +#define SD_FMT_END_OF_DATA "$$$$" + +#define MOL_FMT_INPLINELEN 204 /* add cr, lf, double zero termination */ +#ifndef MOL_FMT_MAXLINELEN +#define MOL_FMT_MAXLINELEN 200 +#endif + +#define MOL_FMT_PRESENT 1 +#define MOL_FMT_ABSENT 0 + +/* configuration */ +#define MOL_FMT_QUERY MOL_FMT_ABSENT +#define MOL_FMT_CPSS MOL_FMT_ABSENT +#define MOL_FMT_REACT MOL_FMT_ABSENT + +#define MOL_FMT_STRING_DATA 'S' +#define MOL_FMT_CHAR_INT_DATA 'C' +#define MOL_FMT_SHORT_INT_DATA 'N' +#define MOL_FMT_LONG_INT_DATA 'L' +#define MOL_FMT_DOUBLE_DATA 'D' +#define MOL_FMT_FLOAT_DATA 'F' +#define MOL_FMT_JUMP_TO_RIGHT 'J' +#define MOL_FMT_MAX_VALUE_LEN 32 /* max length of string containing a numerical value */ +#define MOL_FMT_INT_DATA 'I' + +#define MOL_FMT_M_STY_NON 0 +#define MOL_FMT_M_STY_SRU 1 +#define MOL_FMT_M_STY_MON 2 +#define MOL_FMT_M_STY_COP 3 +#define MOL_FMT_M_STY_MOD 4 +#define MOL_FMT_M_STY_CRO 5 +#define MOL_FMT_M_STY_MER 6 + +#define MOL_FMT_M_SST_NON 0 +#define MOL_FMT_M_SST_ALT 1 +#define MOL_FMT_M_SST_RAN 2 +#define MOL_FMT_M_SST_BLK 3 + +#define MOL_FMT_M_CONN_NON 0 +#define MOL_FMT_M_CONN_HT 1 +#define MOL_FMT_M_CONN_HH 2 +#define MOL_FMT_M_CONN_EU 3 + + +/* V3000 specific constants */ +#define MOL_FMT_V3000_STENON -1 +#define MOL_FMT_V3000_STEABS 1 +#define MOL_FMT_V3000_STEREL 2 +#define MOL_FMT_V3000_STERAC 3 + +/* provisional limits for V3000 */ +/* TODO: remove, replace affected strings with reallocatable buffers */ +#define MOL_FMT_V3000_INPLINELEN 32004 /* add cr, lf, double zero termination */ +#ifndef MOL_FMT_V3000_MAXLINELEN +#define MOL_FMT_V3000_MAXLINELEN 32000 +#endif +#define MOL_FMT_V3000_MAXFIELDLEN 4096 + +/*#ifdef TARGET_EXE_USING_API*/ +#ifndef ISOTOPIC_SHIFT_FLAG +#define ISOTOPIC_SHIFT_FLAG 10000 /* add to isotopic mass if isotopic_mass = */ +#endif +/*#endif*/ + +/*-------------------*/ +/* SIMPLE DATA TYPES */ +/*-------------------*/ + +#ifndef INCHI_US_CHAR_DEF +typedef signed char S_CHAR; +typedef unsigned char U_CHAR; +#define INCHI_US_CHAR_DEF +#endif + +#ifndef LEN_COORD +#define LEN_COORD 10 +#endif +#ifndef NUM_COORD +#define NUM_COORD 3 +#endif + + +/*-----------------*/ +/* DATA STRUCTURES */ +/*-----------------*/ + + +/* NUM_LISTS - dynamically growing array of numeric lists */ +typedef struct A_NUM_LISTS +{ + int **lists; + int allocated; + int used; + int increment; +} NUM_LISTS; +int NumLists_Alloc( NUM_LISTS *num_lists, int nlists ); +int NumLists_ReAlloc( NUM_LISTS *num_lists ); +int NumLists_Append( NUM_LISTS *num_lists, int *list ); +void NumLists_Free( NUM_LISTS *num_lists); +/* INT_ARRAY - dynamically growing array of int */ +typedef struct tagINT_ARRAY +{ + int *item; + int allocated; + int used; + int increment; +} INT_ARRAY; +int IntArray_Alloc( INT_ARRAY *items, int nitems ); +int IntArray_ReAlloc( INT_ARRAY *items ); +int IntArray_Append( INT_ARRAY *items, int new_item ); +void IntArray_Reset( INT_ARRAY *items); +void IntArray_Free( INT_ARRAY *items); +void IntArray_DebugPrint( INT_ARRAY *items); +/* MOL_FMT_SGROUP is a container for Sgroup data */ +typedef struct A_MOL_FMT_SGROUP +{ + int id; /* it is what is called 'Sgroup number' in CTFile */ + int type; /* type (STY) */ + int subtype; /* (SST) */ + int conn; /* (SCN) */ + int label; /* it is what is called 'unique Sgroup identifier' in CTFile */ + double xbr1[4]; /* bracket ends coordinates (SDI) */ + double xbr2[4]; /* bracket ends coordinates (SDI) */ + char smt[80]; /* Sgroup Subscript (SMT) */ + INT_ARRAY alist; + INT_ARRAY blist; +} MOL_FMT_SGROUP; +int MolFmtSgroup_Create( MOL_FMT_SGROUP **sgroup, int id, int type ); +void MolFmtSgroup_Free( MOL_FMT_SGROUP *sgroup ); +/* MOL_FMT_SGROUPS is a dynamically growing array of pointers to MOL_FMT_SGROUP objects */ +typedef struct A_MOL_FMT_SGROUPS +{ + MOL_FMT_SGROUP **group; /* growable array of pointers to MOL_FMT_SGROUP objects */ + int allocated; /* allocated number of objects */ + int used; /* current number of objects */ + int increment; /* array expansion icrement */ +} MOL_FMT_SGROUPS; +int MolFmtSgroups_Alloc( MOL_FMT_SGROUPS *items, int nitems ); +int MolFmtSgroups_ReAlloc( MOL_FMT_SGROUPS *items ); +int MolFmtSgroups_Append( MOL_FMT_SGROUPS *items, int id, int type ); +void MolFmtSgroups_Free( MOL_FMT_SGROUPS *items); +int MolFmtSgroups_GetIndexBySgroupId( int id, MOL_FMT_SGROUPS *items); + +typedef struct A_MOL_FMT_HEADER_BLOCK +{ + /* Line #1 */ + char molname[MOL_FMT_MAXLINELEN+1]; /* up to 80 characters */ + /* Line #2: optional */ + char line2[MOL_FMT_MAXLINELEN+1]; /* the whole line2 -- up to 80 characters */ + char user_initls[3]; /* 2 bytes; char */ + char prog_name[9]; /* 8 bytes; char */ + char month; /* 2 bytes; integral */ + char day; /* 2 bytes; integral */ + char year; /* 2 bytes; integral */ + char hour; /* 2 bytes; integral */ + char minute; /* 2 bytes; integral */ + char dim_code[3]; /* 2 bytes: dimensional code; char */ + short scaling_factor1; /* 2 bytes; I2 */ + double scaling_factor2; /* 10 bytes, F10.5 */ + double energy; /* 10 bytes, F10.5 */ + long internal_regno; /* 6 bytes, integral */ + /* Line #3: comment */ + char comment[81]; +} MOL_FMT_HEADER_BLOCK; + + +typedef struct A_MOL_FMT_ATOM +{ + double fx; /* F10.5; Generic */ + double fy; /* F10.5; Generic */ + double fz; /* F10.5; Generic */ + char symbol[6]; /* aaa; Generic + changed from 4 to 6 to match STDATA */ + S_CHAR mass_difference; /* dd; (M_ISO) + Generic: -3..+4 otherwise 0 or + 127=most abund. isotope */ + S_CHAR charge; /* ccc; (M CHG), + Generic: 1=+3, 2=+2,3=+1, + 4=doublet,5=-1,6=-2,7=-3 */ + char radical; /* (M RAD) */ + char stereo_parity; /* sss; Generic */ +#if ( MOL_FMT_QUERY == MOL_FMT_PRESENT ) + char H_count_plus_1; /* hhh; Query; + Hn means >= n H; + H0 means no H*/ + char stereo_care; /* bbb; Query: 0=ignore; + 1=must match */ +#endif + char valence; /* vvv: + 0=no marking; (1..14)=(1..14); + 15=zero valence. Number of bonds + includes bonds to impl. H's */ + +#if ( MOL_FMT_CPSS == MOL_FMT_PRESENT ) + char H0_designator; /* HHH: CPSS */ + char reaction_component_type; /* rrr: + CPSS: 1=reactant, + 2=product, + 3=intermediate */ + char reaction_component_num; /* iii: CPSS: 0 to (n-1)*/ +#endif + +#if ( MOL_FMT_REACT == MOL_FMT_PRESENT ) + short atom_atom_mapping_num; /* mmm: Reaction: 1..255 */ + char cInversionRetentionFlag; /* nnn: + 1=inverted, 2=retained config.; + 0=property not applied */ +#endif +#if ( MOL_FMT_REACT == MOL_FMT_PRESENT || MOL_FMT_QUERY == MOL_FMT_PRESENT ) + char exact_change_flag; /* eee */ +#endif + char my_n_impH; /* number of implicit H calculated for + adding H to strings in STDATA */ + char display_tom; /* Do not hide element's name + (applies to C 7-25-98 DCh */ + char atom_aliased_flag; /* Do not remove + charge/radical/isotope if it + is in the alias. 9-3-99 DCh */ +} MOL_FMT_ATOM; + + +typedef struct A_MOL_FMT_BOND +{ + short atnum1; /* 111: First atom number: Generic */ + short atnum2; /* 222: Second atom number: Generic */ + char bond_type; /* ttt: + 1,2,3=single, double, triple; + 4=aromatic; + 5=single or double; + 6=single or aromatic; + 7=double or aromatic; + 8=any. + Values 4-8 are for + SSS queries only */ + char bond_stereo; /* sss: + Single bonds: + 0=not stereo, 1=up, 4=either, 6=down + Double bonds: + 0=use x,y,z to determine cis/trans, + 3=cis or trans (either) */ + /* xxx: not used */ + +#if ( MOL_FMT_QUERY == MOL_FMT_PRESENT ) + char bond_topology; /* rrr: + 0=either, 1=ring, 2=chain: + SSS queries only */ +#endif +#if ( MOL_FMT_REACT == MOL_FMT_PRESENT ) + char react_center_status; /* ccc: + 0 = unmarked, + 1 = a center, + -1 = not a center; + Additional: + 2 = no charge, + 4 = bond made/broken, + 8 = bond order changes + 12=4+8; 5=4+1, 9=8+1, 13=12+1 are also possible */ +#endif +} MOL_FMT_BOND; + + +typedef struct A_MOL_FMT_v3000 +{ + int n_non_star_atoms; + int n_star_atoms; + int * atom_index_orig; /* index as supplied for atoms */ + int * atom_index_fin; /* = index or -1 for star atom */ + int n_sgroups; /* currently, we do not use this. */ + int n_3d_constraints; /* currently, we do not use this. */ + int n_collections; + int n_non_haptic_bonds; + int n_haptic_bonds; + NUM_LISTS *haptic_bonds;/* haptic_bonds[i] is pointer to int* which contains: */ + /* bond_type, non-star atom number, nendpts, then endpts themselves */ + /* Enhanced stereo */ + int n_steabs; + NUM_LISTS *steabs; /* steabs[k][0] - not used */ + /* steabs[k][1] - number of members in collection */ + /* steabs[k][2..] - member atom numbers */ + int n_sterel; + NUM_LISTS *sterel; /* sterel[k][0] - n from "STERELn" tag */ + /* sterel[k][1] - number of members in collection */ + /* sterel[k][2..] - member atom numbers */ + int n_sterac; + NUM_LISTS *sterac; /* sterac[k][0] - n from "STERACn" tag */ + /* sterac[k][1] - number of members in collection */ + /* sterac[k][0] - number from "STERACn" tag */ +} MOL_FMT_v3000; + + +typedef struct A_MOL_FMT_CTAB +{ + /* Line #1: Counts line */ + int n_atoms; /* int accounts for possible V3000. Was: aaa; <= 255; Generic */ + int n_bonds; /* int accounts for possible V3000. Was: bbb; <= 255; Generic */ +#if ( MOL_FMT_QUERY == MOL_FMT_PRESENT ) + short n_atom_lists; /* lll; <= 30; Query */ +#endif + + char chiral_flag; /* ccc; 0 or 1; Generic */ + short n_stext_entries; /* sss; CPSS */ +#if ( MOL_FMT_CPSS == MOL_FMT_PRESENT ) + short n_reaction_components_plus_1; /* xxx; CPSS */ + short n_reactants; /* rrr; CPSS */ + short n_products; /* ppp; CPSS */ + short n_intermediates; /* iii; CPSS */ +#endif + short n_property_lines; /* mmm; Generic */ + short follow_inchi_1_treating_iso_mass; + char version_string[7]; /* vvvvvv; Generic; 'V2000' */ + MOL_FMT_ATOM *atoms; /* The Atom Block */ + MOL_FMT_BOND *bonds; + MOL_COORD *coords; + MOL_FMT_SGROUPS sgroups; /* growable array of pointers to Sgroup objects */ + MOL_FMT_v3000 *v3000; /* 2013 + intentionally separated from + older version_string[7] */ +} MOL_FMT_CTAB; + + +typedef struct A_MOL_FMT_DATA +{ + + MOL_FMT_HEADER_BLOCK hdr; + + MOL_FMT_CTAB ctab; +} MOL_FMT_DATA; + + + +/* + Functions +*/ + + +MOL_FMT_DATA* ReadMolfile( INCHI_IOSTREAM *inp_file, + MOL_FMT_HEADER_BLOCK *OnlyHeaderBlock, + MOL_FMT_CTAB *OnlyCTab, + int bGetOrigCoord, + int treat_polymers, + char *pname, + int lname, + long *Id, + const char *pSdfLabel, + char *pSdfValue, + int *err, + char *pStrErr ); +int MolfileStrnread( char* dest, + char* source, + int len, + char **first_space ); +int MolfileReadField( void* data, + int field_len, + int data_type, + char** line_ptr ); +long MolfileExtractStrucNum( MOL_FMT_HEADER_BLOCK *pHdr ); +int MolfileHasNoChemStruc( MOL_FMT_DATA* mfdata); +int MolfileSaveCopy( INCHI_IOSTREAM *inp_file, + long fPtrStart, + long fPtrEnd, + FILE *outfile, + long num ); +int MolfileGetXYZDimAndNormFactors( MOL_FMT_DATA* mfdata, + int find_norm_factors, + double *x0, + double *y0, + double *z0, + double *xmin, + double *ymin, + double *zmin, + double *scaler, + int *err, + char *pStrErr ); +MOL_FMT_DATA* FreeMolfileData( MOL_FMT_DATA* mfdata ); + +/* + V3000 Molfile +*/ + +int MolfileV3000Init( MOL_FMT_CTAB* ctab, + char *pStrErr ); +int MolfileV3000ReadCTABBeginAndCountsLine( MOL_FMT_CTAB* ctab, + INCHI_IOSTREAM *inp_file, + char *pStrErr ); +int MolfileV3000ReadAtomsBlock( MOL_FMT_CTAB* ctab, + INCHI_IOSTREAM *inp_file, + int err, + char *pStrErr ); +int MolfileV3000ReadBondsBlock( MOL_FMT_CTAB* ctab, + INCHI_IOSTREAM *inp_file, + int err, + char *pStrErr ); +int MolfileV3000ReadTailOfCTAB( MOL_FMT_CTAB* ctab, + INCHI_IOSTREAM *inp_file, + int err, + char *pStrErr ); +int MolfileV3000ReadHapticBond( MOL_FMT_CTAB* ctab, + char** line_ptr, + int **num_list, + char *pStrErr ); +int MolfileV3000ReadStereoCollection( MOL_FMT_CTAB* ctab, + char** line_ptr, + int **num_list, + char *pStrErr ); +int MolfileV3000ReadSGroup( MOL_FMT_CTAB* ctab, + INCHI_IOSTREAM *inp_file, + int err, + char *pStrErr ); +int MolfileV3000Read3DBlock( MOL_FMT_CTAB* ctab, + INCHI_IOSTREAM *inp_file, + int err, + char *pStrErr ); +int MolfileV3000ReadCollections( MOL_FMT_CTAB* ctab, + INCHI_IOSTREAM *inp_file, + int err, + char *pStrErr ); +/* Clean V3000 stuff */ +int DeleteMolfileV3000Info( MOL_FMT_v3000* v3000 ) ; + +char* inchi_fgetsLf_V3000( char* line, INCHI_IOSTREAM* inp_stream ); +int get_V3000_input_line_to_strbuf( INCHI_IOSTREAM_STRING *buf, INCHI_IOSTREAM* inp_stream ); + +/* Extract the 'data' in specified mol file field at given text position 'line_ptr' */ +int MolfileV3000ReadField( void* data, int data_type, char** line_ptr ); +/* Read keyword */ +int MolfileV3000ReadKeyword(char* key, char** line_ptr); + +/* + SDF +*/ + +int SDFileSkipExtraData( INCHI_IOSTREAM *inp_file, + long *CAS_num, + char* comment, + int lcomment, + char *name, + int lname, + int prev_err, + const char *pSdfLabel, + char *pSdfValue, + char *pStrErr ); +int SDFileIdentifyLabel( char* inp_line, const char *pSdfLabel ); +long SDFileExtractCASNo( char *line ); + +#endif /* _MOL_FMT_H_ */ diff --git a/INCHI-1-SRC/INCHI_BASE/src/mol_fmt1.c b/INCHI-1-SRC/INCHI_BASE/src/mol_fmt1.c new file mode 100644 index 0000000..3a8f4bb --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/mol_fmt1.c @@ -0,0 +1,1762 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include +#include +#include +#include +#include + +#include "mode.h" +#include "mol_fmt.h" + +#include "ichierr.h" +#include "util.h" +#include "ichi_io.h" +#include "strutil.h" + + +/* + MolFile related procedures - 1 + + +*/ + + + +/* + Local functions; static +*/ +/* MOL V2000 */ +static MOL_FMT_DATA* MolfileReadDataLines( INCHI_IOSTREAM *inp_file, + MOL_FMT_HEADER_BLOCK *OnlyHeaderBlock, + MOL_FMT_CTAB *OnlyCTab, + int bGetOrigCoord, + int treat_polymers, + int *err, char *pStrErr ); +static int MolfileReadHeaderLines( MOL_FMT_HEADER_BLOCK *hdr, INCHI_IOSTREAM *inp_file, char *pStrErr); +static int MolfileReadCountsLine( MOL_FMT_CTAB* ctab, INCHI_IOSTREAM *inp_file, char *pStrErr ); +static int MolfileReadAtomsBlock( MOL_FMT_CTAB* ctab, INCHI_IOSTREAM *inp_file, + int err, char *pStrErr ); +static int MolfileReadBondsBlock( MOL_FMT_CTAB* ctab, INCHI_IOSTREAM *inp_file, + int err, char *pStrErr ); +static int MolfileReadSTextBlock( MOL_FMT_CTAB* ctab, INCHI_IOSTREAM *inp_file, + int err, char *pStrErr ); +static int MolfileReadPropBlock( MOL_FMT_CTAB* ctab, MOL_FMT_HEADER_BLOCK *pHdr, + INCHI_IOSTREAM *inp_file, + int treat_polymers, + int err, char *pStrErr ); +static int MolfileReadSgroupOfPolymer( MOL_FMT_CTAB* ctab, MOL_FMT_HEADER_BLOCK *pHdr, INCHI_IOSTREAM *inp_file, + char line[MOL_FMT_INPLINELEN], + char *szType, char *p, + int err, char *pStrErr ); +static int MolfileReplaceStarAtoms( MOL_FMT_CTAB* ctab, int err, char *pStrErr ); + + +/* + Read MOL-format data from SD or MOL file +*/ +MOL_FMT_DATA* ReadMolfile( INCHI_IOSTREAM *inp_file, + MOL_FMT_HEADER_BLOCK *OnlyHeaderBlock, + MOL_FMT_CTAB *OnlyCTab, + int bGetOrigCoord, + int treat_polymers, + char *pname, + int lname, + long *Id, + const char *pSdfLabel, + char *pSdfValue, + int *err, + char *pStrErr ) +{ +MOL_FMT_DATA* mfdata; + + if ( pname && lname ) + pname[0] = '\0'; + if ( Id ) + *Id = 0L; /* ignore for now */ + + mfdata = MolfileReadDataLines( inp_file, + OnlyHeaderBlock, + OnlyCTab, + bGetOrigCoord, + treat_polymers, + err, pStrErr); + + if ( *err < 0 ) + { + /* read OK, and end of data encountered */ + *err = -*err; + } + else + { + /* unnecessary extra data may have present in SDF; skip them for now */ + int ret_skip_extras = SDFileSkipExtraData( inp_file, + Id, + NULL, 0, + pname, lname, + *err, + pSdfLabel, pSdfValue, + pStrErr ); + + + if ( ret_skip_extras ) + { + /* important to continue to the next good structure */ + *err = ret_skip_extras; + } + } + + /* Intercept and modify if star atoms (assumed to be polymer related) present */ + if ( mfdata ) + { + if ( mfdata->ctab.sgroups.used > 0 && + treat_polymers ) + { + *err = MolfileReplaceStarAtoms( &mfdata->ctab, *err, pStrErr ); + } + } + + return + mfdata; +} + + +/* + Read data lines completely ingnore STEXT block, queries, and 3D features +*/ +MOL_FMT_DATA * MolfileReadDataLines( INCHI_IOSTREAM *inp_file, + MOL_FMT_HEADER_BLOCK *OnlyHeaderBlock, + MOL_FMT_CTAB *OnlyCTab, + int bGetOrigCoord, + int treat_polymers, + int *err, + char *pStrErr ) +{ +int n_alloc_atoms; +MOL_FMT_CTAB ctab, *pCtab=NULL; +MOL_FMT_HEADER_BLOCK *pHdr=NULL; +MOL_FMT_DATA *mfdata=NULL; +int retcode, prevcode; +int data_ended=0, should_read_all=0; + + if ( !OnlyHeaderBlock ) + should_read_all = 1; + + retcode = 0; + *err = 0; + + if ( should_read_all ) + { + + mfdata = ( MOL_FMT_DATA* ) inchi_calloc( 1, sizeof(MOL_FMT_DATA) ); + if ( !mfdata ) + { + retcode = 1; + AddErrorMessage( pStrErr, "Out of RAM" ); + goto err_fin; + } + + pHdr = &mfdata->hdr; + pCtab = &mfdata->ctab; + } + else + { + pHdr = OnlyHeaderBlock; + pCtab = OnlyCTab ? OnlyCTab + : &ctab; + memset( pHdr, 0, sizeof(MOL_FMT_HEADER_BLOCK) ); + memset( pCtab, 0, sizeof(MOL_FMT_CTAB) ); + } + + pCtab->bonds = NULL; + pCtab->atoms = NULL; + pCtab->coords = NULL; + pCtab->v3000 = NULL; + + /* Header lines */ + retcode = MolfileReadHeaderLines(pHdr, inp_file, pStrErr); + if ( retcode ) + { + /* most likely end of file */ + retcode+= 10; + goto err_fin; + } + + /* Read counts line and also check if we deal with V3000 Molfile */ + retcode = MolfileReadCountsLine( pCtab , inp_file, pStrErr); + if ( retcode ) + { + retcode+= 20; + goto err_fin; + } + + if ( pCtab->v3000 ) + { + + /* In V3000, the previously read counts should be neglected + and new counts line (1st in Ctab) should be read preceded + by "M V30 BEGIN CTAB" */ + + retcode = MolfileV3000ReadCTABBeginAndCountsLine( pCtab, inp_file, pStrErr ); + if ( retcode ) + { + retcode+= 70; + TREAT_ERR_AND_FIN (retcode, 1, err_fin, pStrErr ); + } + + retcode = MolfileV3000Init( pCtab, pStrErr ); + if ( retcode ) + { + retcode+= 70; + TREAT_ERR_AND_FIN (retcode, 1, err_fin, pStrErr ); + } + } + + /* Atomic block */ + + if ( should_read_all ) + { + n_alloc_atoms = inchi_max( mfdata->ctab.n_atoms, 1 ); + mfdata->ctab.atoms = (MOL_FMT_ATOM*) + inchi_calloc( n_alloc_atoms, sizeof(MOL_FMT_ATOM) ); + if ( !mfdata->ctab.atoms ) + { + retcode = 2; + TREAT_ERR_AND_FIN (retcode, 2, err_fin, "Out of RAM"); + } + + if ( bGetOrigCoord ) + { + mfdata->ctab.coords = (MOL_COORD*) + inchi_calloc( n_alloc_atoms, sizeof(MOL_COORD) ); + if ( !mfdata->ctab.coords ) + { + retcode = 2; + TREAT_ERR_AND_FIN (retcode, 2, err_fin, "Out of RAM"); + } + } + } + + if ( !pCtab->v3000 ) + { + retcode = MolfileReadAtomsBlock( pCtab, + inp_file, + retcode, + pStrErr ); + } + else + { + retcode = MolfileV3000ReadAtomsBlock( pCtab, + inp_file, + retcode, + pStrErr ); + } + if ( retcode ) + { + if ( retcode < 0 ) + { + retcode = -retcode; + data_ended = 1; + } + retcode+= 30; + /* goto err_fin; */ + } + + /* Bonds block */ + + if ( should_read_all && retcode < 30 ) + { + if ( !data_ended) + { + int n_alloc_bonds = inchi_max( mfdata->ctab.n_bonds, 1 ); + + mfdata->ctab.bonds = (MOL_FMT_BOND *) + inchi_calloc( n_alloc_bonds, sizeof(MOL_FMT_BOND) ); + + if ( !mfdata->ctab.bonds ) + { + /* can't allocate bonds structure */ + retcode = 3; + TREAT_ERR_AND_FIN (retcode, 3, err_fin, "Out of RAM"); + } + } + } + + prevcode = retcode; + if ( !data_ended ) + { + + + if ( !pCtab->v3000 ) + { + retcode = MolfileReadBondsBlock( pCtab, inp_file, retcode, pStrErr ); + } + else + { + retcode = MolfileV3000ReadBondsBlock( pCtab, inp_file, retcode, pStrErr ); + } + + + if ( retcode ) + { + if ( retcode < 0 ) + { + retcode = -retcode; + data_ended = 1; + } + retcode = prevcode ? prevcode + : retcode + 40; + } + + + /* SGroup, 3D, link line(s), collections, END CTAB */ + + if ( pCtab->v3000 ) + { + retcode = MolfileV3000ReadTailOfCTAB( pCtab, inp_file, retcode, pStrErr ); + if ( retcode ) + { + if ( retcode < 0 ) + { + retcode = -retcode; + data_ended = 1; + } + retcode = prevcode ? prevcode + : retcode + 70; + } + } + } + prevcode = retcode; + + /* SText */ + + if ( !data_ended ) + { + retcode = MolfileReadSTextBlock( pCtab, inp_file, retcode, pStrErr ); + + if ( retcode ) + retcode = prevcode ? prevcode + : retcode + 50; + } + prevcode = retcode; + + /* Prop block */ + + if ( !data_ended ) + { + retcode = MolfileReadPropBlock( pCtab, pHdr, inp_file, treat_polymers, retcode, pStrErr ); + + if (retcode) + { + if ( retcode < 0 ) + { + retcode = -retcode; + data_ended = 1; + } + retcode = prevcode ? prevcode + : retcode + 60; + } + } + + /* Check that all valences are in allowed range ( <=MAXVAL, currently 20 ) */ + if ( 1 ) +#ifdef TARGET_LIB_FOR_WINCHI + if ( pCtab && pCtab->atoms ) +#endif + { + int i; + for ( i = 0; i < pCtab->n_atoms; i++ ) + { + if ( pCtab->atoms[i].valence > MAXVAL) + { + retcode = 70 + 9; + TREAT_ERR( retcode, 0, "Too large input atomic valence"); + break; + } + } + } + + +err_fin: + *err = data_ended ? -retcode + : retcode; + + if ( should_read_all ) + { + if ( retcode ) + mfdata = FreeMolfileData( mfdata ); /* delete all results */ + return mfdata; + } + else + { + if ( retcode ) + return NULL; + else + return (MOL_FMT_DATA*) OnlyHeaderBlock; + } +} + + +/* + Read Molfile header +*/ +int MolfileReadHeaderLines( MOL_FMT_HEADER_BLOCK *hdr, + INCHI_IOSTREAM *inp_file, + char *pStrErr) +{ +/* All input lines can have up to 80 characters */ +/* Header Block */ + +char line[MOL_FMT_INPLINELEN]; /* + cr +lf +zero termination + reserve */ +int err = 0, len; +const int line_len = sizeof(line); +char *p; + + + /* Header line #1: name */ + p = inchi_fgetsLf( line, line_len, inp_file ); + if ( !p ) + { + /* can't read the input file line */ + err = 1; + /* AddErrorMessage( pStrErr, "Can't read header block name line" ); */ + goto err_fin; + } + + remove_one_lf( line ); + /* -- Disabled to relax strictness: allow > 80 chars names. */ + /* + if ( line[MOL_FMT_MAXLINELEN] ) + { + err = 2; // too long line + goto err_fin; + } + */ + len = MolfileReadField( hdr->molname, + sizeof(hdr->molname)-1, + MOL_FMT_STRING_DATA, + &p ); + + /* Header line #2 */ + p = inchi_fgetsLf( line, line_len, inp_file ); + if ( !p ) + { + /* can't read the input file line */ + err = 3; + /* AddErrorMessage( pStrErr, "Can't read header block line 2" ); */ + goto err_fin; + } + + remove_one_lf( line ); + + /* -- Disabled to relax strictness: allow > 80 chars names. */ + /* if ( line[MOL_FMT_MAXLINELEN] ) + { + err = 4; // too long input file line + goto err_fin; + } + */ + + len = MolfileReadField( hdr->user_initls, + sizeof(hdr->user_initls)-1, + MOL_FMT_STRING_DATA, + &p ); + len = MolfileReadField( hdr->prog_name, + sizeof(hdr->prog_name)-1, + MOL_FMT_STRING_DATA, + &p ); + + /*------------ Relax strictness -----------------------*/ + len = MolfileReadField( &hdr->month, 2, MOL_FMT_CHAR_INT_DATA, &p ); + len = MolfileReadField( &hdr->day, 2, MOL_FMT_CHAR_INT_DATA, &p ); + len = MolfileReadField( &hdr->year, 2, MOL_FMT_CHAR_INT_DATA, &p ); + len = MolfileReadField( &hdr->hour, 2, MOL_FMT_CHAR_INT_DATA, &p ); + len = MolfileReadField( &hdr->minute, 2, MOL_FMT_CHAR_INT_DATA, &p ); + len = MolfileReadField( hdr->dim_code, sizeof(hdr->dim_code)-1, MOL_FMT_STRING_DATA, &p ); + len = MolfileReadField( &hdr->scaling_factor1, 2, MOL_FMT_SHORT_INT_DATA, &p ); + len = MolfileReadField( &hdr->scaling_factor2, 10, MOL_FMT_DOUBLE_DATA, &p ); + len = MolfileReadField( &hdr->energy, 12, MOL_FMT_DOUBLE_DATA, &p ); + len = MolfileReadField( &hdr->internal_regno, 6, MOL_FMT_LONG_INT_DATA, &p ); + + /* Save the whole line 2 */ + p = line; + len = MolfileReadField( hdr->line2, + sizeof(hdr->line2)-1, + MOL_FMT_STRING_DATA, + &p ); + + /* Header line #3: comment */ + p = inchi_fgetsLf( line, line_len, inp_file ); + + if ( !p ) + { + err = 7; /* can't read the line */ + /* AddErrorMessage( pStrErr, "Can't read header block comment line" ); */ + goto err_fin; + } + remove_one_lf( line ); + /* -- Disabled to relax strictness: allow > 80 chars comments. + if ( line[MOL_FMT_MAXLINELEN] ){ + err = 8; // too long line + goto err_fin; + } + */ + len = MolfileReadField( hdr->comment, + sizeof(hdr->comment)-1, + MOL_FMT_STRING_DATA, + &p ); + +err_fin: + return err; +} + + +/* + Read counts line +*/ +int MolfileReadCountsLine( MOL_FMT_CTAB* ctab, + INCHI_IOSTREAM *inp_file, + char *pStrErr ) +{ +char *p; +char line[MOL_FMT_INPLINELEN]; +const int line_len = sizeof(line); +int err = 0, len; + + p = inchi_fgetsLf( line, line_len, inp_file ); + + if ( !p ) + { + TREAT_ERR_AND_FIN (err, 1, err_fin, "Cannot read counts line"); + /* can't read the input file line */ + } + + remove_one_lf( line ); + + if ( line[MOL_FMT_MAXLINELEN] ) + { + TREAT_ERR( err, 0, "Too long counts line"); /* too long input file line */ + } + + if ( 0 > MolfileReadField( &ctab->n_atoms, 3, MOL_FMT_SHORT_INT_DATA, &p ) /* V2000 only: short int */ + || 0 > MolfileReadField( &ctab->n_bonds, 3, MOL_FMT_SHORT_INT_DATA, &p ) /* V2000 only: short int */ + +#if ( MOL_FMT_QUERY == MOL_FMT_PRESENT ) + || 0 > MolfileReadField( &ctab->n_atom_lists, 3, MOL_FMT_SHORT_INT_DATA, &p ) +#else + || 0 > MolfileReadField( NULL, 3, MOL_FMT_JUMP_TO_RIGHT, &p ) +#endif + + || 0 > MolfileReadField( NULL, /*obsolete*/ 3, MOL_FMT_JUMP_TO_RIGHT, &p ) + || 0 > MolfileReadField( &ctab->chiral_flag, 3, MOL_FMT_CHAR_INT_DATA, &p ) + || 0 > MolfileReadField( &ctab->n_stext_entries, 3, MOL_FMT_SHORT_INT_DATA, &p ) + +#if ( MOL_FMT_CPSS == MOL_FMT_PRESENT ) + || 0 > MolfileReadField( &ctab->n_reaction_components_plus_1, 3, MOL_FMT_SHORT_INT_DATA, &p ) + || 0 > MolfileReadField( &ctab->n_reactants, 3, MOL_FMT_SHORT_INT_DATA, &p ) + || 0 > MolfileReadField( &ctab->n_products, 3, MOL_FMT_SHORT_INT_DATA, &p ) + || 0 > MolfileReadField( &ctab->n_intermediates, 3, MOL_FMT_SHORT_INT_DATA, &p ) +#else + || 0 > MolfileReadField( NULL, 3, MOL_FMT_JUMP_TO_RIGHT, &p ) + || 0 > MolfileReadField( NULL, 3, MOL_FMT_JUMP_TO_RIGHT, &p ) + || 0 > MolfileReadField( NULL, 3, MOL_FMT_JUMP_TO_RIGHT, &p ) + || 0 > MolfileReadField( NULL, 3, MOL_FMT_JUMP_TO_RIGHT, &p ) +#endif + + || 0 > MolfileReadField( &ctab->n_property_lines,3, MOL_FMT_SHORT_INT_DATA, &p ) ) + { + err = 3; /* can't interpret counts line */ + TREAT_ERR( err, 3, "Cannot interpret counts line:"); /* too long input file line */ + dotify_non_printable_chars( line ); + AddErrorMessage(pStrErr, line); + goto err_fin; + } + + /* Get CTFile version (V2000 or other) */ + len = MolfileReadField( ctab->version_string, + sizeof(ctab->version_string)-1, + MOL_FMT_STRING_DATA, + &p ); + + /* Allocate additional space if V3000 */ + if ( !strcmp( ctab->version_string, "V3000" ) ) + { + ctab->v3000 = (MOL_FMT_v3000*) + inchi_calloc( 1, sizeof(MOL_FMT_v3000) ); + + if ( !ctab->v3000 ) + { + AddErrorMessage(pStrErr, "Out of RAM"); + return -1; + } + } + else + ctab->v3000 = NULL; /* paranoia */ + + /* Polymer Sgroups */ + MolFmtSgroups_Alloc( &(ctab->sgroups), 1); + +err_fin: + return err; +} + + +/* + Read V2000 atomic block +*/ +int MolfileReadAtomsBlock( MOL_FMT_CTAB* ctab, + INCHI_IOSTREAM *inp_file, + int err, + char *pStrErr ) +{ +char *p; +char line[MOL_FMT_INPLINELEN]; +const int line_len = sizeof(line); +int i; +S_SHORT chg; +static const S_SHORT charge_val[] = {0, 3, 2, 1, 'R', -1, -2, -3}; + + for ( i = 0; i < ctab->n_atoms; i++ ) + { + p = inchi_fgetsLf( line, line_len, inp_file ); + + if ( !p ) + { + if ( !err ) + { + TREAT_ERR( err, 2, "Cannot read atom block line"); + } + break; + } + + remove_one_lf( line ); + + + if ( line[MOL_FMT_MAXLINELEN] ) + { + TREAT_ERR( err, 0, "Too long atom block line"); + } + if ( err ) + { + if ( !strcmp( line, SD_FMT_END_OF_DATA ) ) + { + err = -abs(err); + break; + } + continue; /* bypass the rest of the Atom block */ + } + + if ( NULL != ctab->coords ) + { + mystrncpy( ctab->coords[i], p, 31 ); /* original coordinates */ + } + + if ( NULL != ctab->atoms ) + { + if ( 0 > MolfileReadField( &ctab->atoms[i].fx, 10, MOL_FMT_DOUBLE_DATA, &p ) + || 0 > MolfileReadField( &ctab->atoms[i].fy, 10, MOL_FMT_DOUBLE_DATA, &p ) + || 0 > MolfileReadField( &ctab->atoms[i].fz, 10, MOL_FMT_DOUBLE_DATA, &p ) + || 0 > MolfileReadField( NULL, /* undescribed in article*/ 1, MOL_FMT_JUMP_TO_RIGHT, &p ) + || 0 == MolfileReadField( &ctab->atoms[i].symbol,3, MOL_FMT_STRING_DATA, &p ) /* was sizeof(ctab->atoms[0].symbol)-1 */ + || 0 > MolfileReadField( &ctab->atoms[i].mass_difference, 2, MOL_FMT_CHAR_INT_DATA, &p ) + || 0 > MolfileReadField( &ctab->atoms[i].charge, 3, MOL_FMT_CHAR_INT_DATA, &p ) + || 0 > MolfileReadField( &ctab->atoms[i].stereo_parity, 3, MOL_FMT_CHAR_INT_DATA, &p ) + +#if ( MOL_FMT_QUERY == MOL_FMT_PRESENT ) + || 0 > MolfileReadField( &ctab->atoms[i].H_count_plus_1, 3, MOL_FMT_CHAR_INT_DATA, &p ) + || 0 > MolfileReadField( &ctab->atoms[i].stereo_care, 3, MOL_FMT_CHAR_INT_DATA, &p ) +#else + || 0 > MolfileReadField( NULL, 3, MOL_FMT_JUMP_TO_RIGHT, &p ) + || 0 > MolfileReadField( NULL, 3, MOL_FMT_JUMP_TO_RIGHT, &p ) +#endif + + || 0 > MolfileReadField( &ctab->atoms[i].valence, 3, MOL_FMT_CHAR_INT_DATA, &p ) ) + { + + err = 4; + TREAT_ERR( err, 4, "Cannot interpret atom block line:"); + dotify_non_printable_chars( line ); + AddErrorMessage(pStrErr, line); + + if ( !strcmp( line, SD_FMT_END_OF_DATA ) ) + { + err = -abs(err); + break; + } + continue; /* can't interpret a first half of atom block line */ + } + + + if ( 2 == strlen(ctab->atoms[i].symbol) && isupper(UCINT ctab->atoms[i].symbol[1])) + { + ctab->atoms[i].symbol[1] = (char)tolower(UCINT ctab->atoms[i].symbol[1]); /* 5-4-99 DCh*/ + } + + if ( (chg = (S_SHORT) ctab->atoms[i].charge)< 0 || chg >= (int)(sizeof ( charge_val ) / sizeof( charge_val[0] )) ) + { + /* ctab->atoms[i].charge = 0; */ /* error; ignore for now */ + ctab->atoms[i].charge = (S_CHAR)(4 - chg); /* allow greater charges to accommodate NCI structures. 8-20-2002 */ + ctab->atoms[i].radical = 0; + } + else if ( 'R' == (chg = charge_val[chg]) ) + { + ctab->atoms[i].charge = 0; + ctab->atoms[i].radical = RADICAL_DOUBLET; + } + else + { + ctab->atoms[i].charge = (S_CHAR)chg; /* actual charge value */ + ctab->atoms[i].radical = 0; + } + + if ( + +#if ( MOL_FMT_CPSS == MOL_FMT_PRESENT ) + 0 > MolfileReadField( &ctab->atoms[i].H0_designator, 3, MOL_FMT_CHAR_INT_DATA, &p ) + || 0 > MolfileReadField( &ctab->atoms[i].reaction_component_type, 3, MOL_FMT_CHAR_INT_DATA, &p ) + || 0 > MolfileReadField( &ctab->atoms[i].reaction_component_num, 3, MOL_FMT_CHAR_INT_DATA, &p ) +#else + 0 > MolfileReadField( NULL, 3, MOL_FMT_JUMP_TO_RIGHT, &p ) + || 0 > MolfileReadField( NULL, 3, MOL_FMT_JUMP_TO_RIGHT, &p ) + || 0 > MolfileReadField( NULL, 3, MOL_FMT_JUMP_TO_RIGHT, &p ) +#endif + +#if ( MOL_FMT_REACT == MOL_FMT_PRESENT ) + || 0 > MolfileReadField( &ctab->atoms[i].atom_atom_mapping_num, 3, MOL_FMT_SHORT_INT_DATA, &p ) + || 0 > MolfileReadField( &ctab->atoms[i].reaction_component_type, 3, MOL_FMT_CHAR_INT_DATA, &p ) +#else + || 0 > MolfileReadField( NULL, 3, MOL_FMT_JUMP_TO_RIGHT, &p ) + || 0 > MolfileReadField( NULL, 3, MOL_FMT_JUMP_TO_RIGHT, &p ) +#endif + +#if ( MOL_FMT_REACT == MOL_FMT_PRESENT || MOL_FMT_QUERY == MOL_FMT_PRESENT ) + || 0 > MolfileReadField( &ctab->atoms[i].exact_change_flag, 3, MOL_FMT_CHAR_INT_DATA, &p ) +#else + || 0 > MolfileReadField( NULL, 3, MOL_FMT_JUMP_TO_RIGHT, &p ) +#endif + + ) + { + err = 5; /* can't interpret a second half of atom block line */ + + TREAT_ERR( err, 5, "Cannot interpret atom block line:"); + dotify_non_printable_chars( line ); + AddErrorMessage(pStrErr, line); + + if ( !strcmp( line, SD_FMT_END_OF_DATA ) ) + { + err = -abs(err); + break; + } + continue; + } + } + } + +/* err_fin: */ + return err; +} + + +/* + Read V2000 bonds block +*/ +int MolfileReadBondsBlock( MOL_FMT_CTAB* ctab, + INCHI_IOSTREAM *inp_file, + int err, + char *pStrErr ) +{ +char *p; +char line[MOL_FMT_INPLINELEN]; +const int line_len = sizeof(line); +int i; + + /* + if ( NULL == ctab->bonds ) + { + err = 1; + goto err_fin; // internal error: memory has not been allocated for bonds structure + } + */ + + for ( i = 0; i < ctab->n_bonds; i++ ) + { + p = inchi_fgetsLf( line, line_len, inp_file ) ; + + if ( !p ) + { + if ( !err ) + { + TREAT_ERR( err, 2, "Cannot read bond block line"); + } + break; + } + + remove_one_lf( line ); + + if ( line[MOL_FMT_MAXLINELEN] ) + { + err = err? err : 3; /* too long input file line */ + } + + if ( err ) + { + if ( !strcmp( line, SD_FMT_END_OF_DATA ) ) + { + err = -abs(err); + break; + } + continue; + } + + if ( ctab->bonds ) + { + + if ( 0 > MolfileReadField( &ctab->bonds[i].atnum1, 3, MOL_FMT_SHORT_INT_DATA, &p ) + || 0 > MolfileReadField( &ctab->bonds[i].atnum2, 3, MOL_FMT_SHORT_INT_DATA, &p ) + || 0 > MolfileReadField( &ctab->bonds[i].bond_type, 3, MOL_FMT_CHAR_INT_DATA, &p ) + || 0 > MolfileReadField( &ctab->bonds[i].bond_stereo, 3, MOL_FMT_CHAR_INT_DATA, &p ) + +#if ( MOL_FMT_QUERY == MOL_FMT_PRESENT ) + || 0 > MolfileReadField( &ctab->bonds[i].cBondTopology,3, MOL_FMT_CHAR_INT_DATA, &p ) /* ring/chain */ +#else + || 0 > MolfileReadField( NULL, 3, MOL_FMT_JUMP_TO_RIGHT, &p ) +#endif + +#if ( MOL_FMT_REACT == MOL_FMT_PRESENT ) + || 0 > MolfileReadField( &ctab->bonds[i].cReactingCenterStatus,3, MOL_FMT_CHAR_INT_DATA, &p ) +#else + || 0 > MolfileReadField( NULL, 3, MOL_FMT_JUMP_TO_RIGHT, &p ) +#endif + + ) + { + + if ( !err ) + { + /* can't interpret bonds block line */ + TREAT_ERR( err, 4, "Cannot interpret bond block line:"); + dotify_non_printable_chars( line ); + AddErrorMessage(pStrErr, line); + } + if ( !strcmp( line, SD_FMT_END_OF_DATA ) ) + { + err = -abs(err); + break; + } + } + } + } + + +/* err_fin: */ + return err; +} + + +/* + Read SText +*/ +int MolfileReadSTextBlock( MOL_FMT_CTAB* ctab, + INCHI_IOSTREAM *inp_file, + int err, + char *pStrErr ) +{ +/* just pass by all stext enties without attemp to interpret */ +char *p; +char line[MOL_FMT_INPLINELEN]; +const int line_len = sizeof(line); +S_SHORT i; + + for ( i = 0; i < 2*ctab->n_stext_entries; i++ ) + { + p = inchi_fgetsLf( line, line_len, inp_file ); + + if ( !p ) + { + if ( !err ) + { + TREAT_ERR_AND_FIN (err, 2, err_fin, "Cannot read STEXT block line"); + } + break; + /* can't read the input file line */ + } + + /* + remove_one_lf( line ); + if ( line[MOL_FMT_MAXLINELEN] ){ + TREAT_ERR( err, 2, "Warning: Too long STEXT block line"); + // too long input file line + } + + */ + } + +err_fin: + return err; +} + + + +/* + Read properties block +*/ +int MolfileReadPropBlock( MOL_FMT_CTAB* ctab, + MOL_FMT_HEADER_BLOCK *pHdr, + INCHI_IOSTREAM *inp_file, + int treat_polymers, + int err, + char *pStrErr ) +{ +enum { MULTI_LINE_MODE_NO_MODE, MULTI_LINE_MODE_ISIS_ALIAS }; +char *p; +char line[MOL_FMT_INPLINELEN]; +const int line_len = sizeof(line); +int nMultiLineMode = MULTI_LINE_MODE_NO_MODE, nAtomNumber=0; +S_SHORT i, j; +char charM[2]; +char szBlank[3]; +char szType[4]; +S_SHORT skip_lines=0; +S_SHORT num_entries; +S_SHORT num_atoms = ctab->n_atoms; + +int charge_encountered = 0; +int radical_encountered = 0; +int isotope_encountered = 0; +int polymer_occurred = 0; + +int debug_polymers = 0; +#if ( DEBUG_POLYMERS == 1 ) + debug_polymers = 1; +#elif ( DEBUG_POLYMERS == 2 ) + debug_polymers = 2; +#endif + + + for ( i = 0; ctab->version_string[0]? 1 : (i < ctab->n_property_lines); i++ ) + { + /* the last line should be M END */ + + /* ctab->version_string[0] == 0: + exactly ctab->n_property_lines lines including M END */ + /* ctab->version_string[0] != 0: + read until M END line was encountered */ + + p = inchi_fgetsLf( line, line_len, inp_file ); + + if ( !p ) + { + if ( !err ) + { + TREAT_ERR( err, 2, "Cannot read properties block line"); + } + goto err_fin; + } + + remove_one_lf( line ); + + if ( line[MOL_FMT_MAXLINELEN] ) + { + TREAT_ERR( err, 3, "Too long properties block line"); + continue; + } + + if ( skip_lines > 0 ) + { + skip_lines --; + continue; + } + + /* alias */ + if ( nMultiLineMode == MULTI_LINE_MODE_ISIS_ALIAS && nAtomNumber ) + { + int len; + + nMultiLineMode = MULTI_LINE_MODE_NO_MODE; + if ( 0 >= (len=normalize_string( p )) ) + { + nAtomNumber = 0; + continue; + } + + if ( 0 < len && len < (int)(sizeof(ctab->atoms->symbol)) ) + { + int nCharge, nRad; + + MOL_FMT_ATOM* atom = ctab->atoms + nAtomNumber-1; + /* ctab->atoms[nAtomNumber-1].atom_aliased_flag = 1; */ + /* extract radicals & charges */ + + extract_charges_and_radicals( p, &nRad, &nCharge ); + + /* Aliased atom cannot have charge, radical & mass difference */ + /* in the atom table or "M CHG", "M RAD", "M ISO" */ + /* if ( nCharge ) */ + atom->charge = (S_CHAR)nCharge; + /* if ( nRad ) */ + atom->radical = (char)nRad; + + if ( 1 == len && 'D' == p[0] ) + { + /* H isotope */ + p[0] = 'H'; + atom->mass_difference=1; + } + else + if ( 1 == len && 'T' == p[0] ) + { + /* H isotope */ + + p[0] = 'H'; + atom->mass_difference=2; + } + else + atom->mass_difference=0; + + if ( strlen(p) < sizeof(ctab->atoms[0].symbol) ) + { + strcpy(atom->symbol, p); + } + else + { + strcpy(atom->symbol, "???"); + } + atom->atom_aliased_flag ++; + } + /* else if( 0 < len ) + { + ^^^ Just too long alias name. + For consistency with parsing {H,D,T}-containing alias names, this + would result in issuing error rather than ignoring... However, + for compatibility reasons, the 'ignore' behavior remained intact. + } + */ + + skip_lines = 0; + nAtomNumber = 0; + continue; + } + + if ( 1 != MolfileReadField( charM, sizeof(charM)-1, MOL_FMT_STRING_DATA, &p ) + || 0 != MolfileReadField( szBlank, sizeof(szBlank)-1, MOL_FMT_STRING_DATA, &p ) /* must contain 0 bytes */ + || 0 >= MolfileReadField( szType, sizeof(szType)-1, MOL_FMT_STRING_DATA, &p ) /* must contain 3 bytes */ + ) + { + if ( !strcmp( line, SD_FMT_END_OF_DATA ) ) + { + err = err? -abs(err): -4; + break; + } + continue; /* ignore because cannot recognize */ + } + + if ( charM[0] == 'V' ) + { + skip_lines = 0; /* ISIS/Desktop Atom Value: one-line property */ + continue; + } + + if ( charM[0] == 'G' ) + { + skip_lines = 1; /* ISIS/Desktop Group abbreviation: two-line property */ + continue; + } + + if ( charM[0] == 'A' ) + { + if ( NULL != ctab->atoms && + 0 < ( nAtomNumber = (int)strtol(szType, NULL, 10) ) && + nAtomNumber <= ctab->n_atoms ){ + /* Atom Alias [ISIS/Desktop] two-line property */ + nMultiLineMode = MULTI_LINE_MODE_ISIS_ALIAS; + continue; + } + else + { + nAtomNumber = 0; + skip_lines = 1; + continue; + } + } + + if ( charM[0] == 'S' && !strcmp( szType, "SKP" ) ){ /* skip lines */ + if ( 0 >= MolfileReadField( &skip_lines, 3, MOL_FMT_SHORT_INT_DATA, &p ) ) + { + skip_lines = 0; + } + continue; + } + + if ( charM[0] != 'M' ) {/* cannot recognize a line */ + continue; + } + + if ( !strcmp( szType, "REG" ) ) + { + int len; + p = p + strspn( p, " " ); + len = strcspn( p, " " ); + len = inchi_min( len, MOL_FMT_MAX_VALUE_LEN ); + MolfileReadField( &pHdr->internal_regno, len, MOL_FMT_LONG_INT_DATA, &p ); + continue; + } + + if ( !strcmp( szType, "END" ) ) + { + if ( ctab->version_string[0] ) + break; /* end of property lines */ + continue; + } + + if ( NULL == ctab->atoms ) + continue; /* ignore because the user requested to bypass all this stuff */ + + /* Charge: Generic */ + if ( !strcmp( szType, "CHG" ) && + 0 < MolfileReadField( &num_entries, 3, MOL_FMT_SHORT_INT_DATA, &p ) && + 1 <= num_entries && num_entries <= 8 ) + { + + S_SHORT atoms[8]; + S_SHORT charges[8]; + + if ( !charge_encountered && !radical_encountered ) + { + /* first charge or radical record clears all Atom Block */ + /* entered charge and radical data to zeroes */ + charge_encountered = -1; + } + for ( j = 0; j < num_entries; j++ ) + { + if ( 0 > MolfileReadField( &atoms[j], 0, MOL_FMT_SHORT_INT_DATA, &p ) || + 0 > MolfileReadField( &charges[j], 0, MOL_FMT_SHORT_INT_DATA, &p ) || + atoms[j] <= 0 || atoms[j] > num_atoms || + charges[j] < -15 || charges[j] > 15 ) + { + goto charge_error; + } + } + if ( charge_encountered == -1 ) + { + for ( j = 0; j < num_atoms; j++ ) + { + if ( !ctab->atoms[j].atom_aliased_flag ) /* do not clear aliased atoms.*/ + ctab->atoms[j].charge = ctab->atoms[j].radical = '\0'; + } + charge_encountered = 1; + } + for ( j = 0; j < num_entries; j++ ) + { + if ( !ctab->atoms[atoms[j]-1].atom_aliased_flag ) /* do not change aliased atoms.*/ + ctab->atoms[atoms[j]-1].charge = (S_CHAR)charges[j]; + } + continue; + charge_error: + TREAT_ERR( err, 0, "Charge not recognized:"); + dotify_non_printable_chars( line ); + AddErrorMessage(pStrErr, line); + continue; /* ignore for now */ + } + + /* Radical: Generic */ + if ( !strcmp( szType, "RAD" ) && + 0 < MolfileReadField( &num_entries, 3, MOL_FMT_SHORT_INT_DATA, &p ) && + 1 <= num_entries && num_entries <= 8 ) + { + + S_SHORT atoms[8]; + S_SHORT radicals[8]; + + if ( !charge_encountered && !radical_encountered ) + { + /* first charge or radical record clears all Atom Block */ + /* entered charge and radical data to zeroes */ + radical_encountered = -1; + } + for ( j = 0; j < num_entries; j++ ) + { + if ( 0 > MolfileReadField( &atoms[j], 0, MOL_FMT_SHORT_INT_DATA, &p ) || + 0 > MolfileReadField( &radicals[j], 0, MOL_FMT_SHORT_INT_DATA, &p ) || + atoms[j] <= 0 || atoms[j] > num_atoms || + radicals[j] < 0 || radicals[j] > 3 ) + { + goto radical_error; + } + } + if ( radical_encountered == -1 ) + { + for ( j = 0; j < num_atoms; j++ ) + { + if ( !ctab->atoms[j].atom_aliased_flag ) /* do not clear aliased atoms. 5-3-99 DCh */ + ctab->atoms[j].charge = ctab->atoms[j].radical = '\0'; + } + radical_encountered = 1; + } + for ( j = 0; j < num_entries; j++ ) + { + if ( !ctab->atoms[atoms[j]-1].atom_aliased_flag ) + { + /* do not change aliased atoms. 5-3-99 DCh */ + ctab->atoms[atoms[j]-1].radical = (S_CHAR)radicals[j]; + } + } + continue; + radical_error: + TREAT_ERR( err, 0, "Radical not recognized:"); + dotify_non_printable_chars( line ); + AddErrorMessage(pStrErr, line); + continue; /* ignore error for now */ + } + + /* Isotope: Generic */ + if ( !strcmp( szType, "ISO" ) && + 0 < MolfileReadField( &num_entries, 3, MOL_FMT_SHORT_INT_DATA, &p ) && + 1 <= num_entries && num_entries <= 8 ) + { + + S_SHORT atoms[8]; + S_SHORT iso_mass[8]; /* contains istotope mass number, not difference. 7-14-00 DCh. */ + + if ( !isotope_encountered ) + { + /* first charge or radical record clears all Atom Block */ + /* entered charge and radical data to zeroes */ + isotope_encountered = -1; + } + for ( j = 0; j < num_entries; j++ ) + { + if ( 0 > MolfileReadField( &atoms[j], 0, MOL_FMT_SHORT_INT_DATA, &p ) || + 0 > MolfileReadField( &iso_mass[j], 0, MOL_FMT_SHORT_INT_DATA, &p ) || + atoms[j] <= 0 || atoms[j] > num_atoms + /*|| iso_mass[j] < -18 || iso_mass[j] > 12*/ ) + { + /* goto isotope_error; */ + atoms[j] = -1; /* flag error */ + TREAT_ERR( err, 0, "Isotopic data not recognized:"); + dotify_non_printable_chars( line ); + AddErrorMessage(pStrErr, line); + continue; /* ignore isotopic error for now */ + } + } + + if ( isotope_encountered == -1 ) + { + for ( j = 0; j < num_atoms; j++ ) + { + /*if ( !ctab->atoms[j].atom_aliased_flag )*/ /* clear even aliased atoms */ + ctab->atoms[j].mass_difference = 0; + } + isotope_encountered = 1; + } + for ( j = 0; j < num_entries; j++ ) + { + if ( atoms[j] <= 0 ) + continue; /* ignore isotopic error for now */ + + if ( 1 /* !ctab->atoms[atoms[j]-1].atom_aliased_flag */) + { + char *at = ctab->atoms[atoms[j]-1].symbol; + if ( at[1] || at[0] != 'D' && at[0] != 'T' ) + { /* D & T cannot have ISO */ + /* need atomic weight to calculate isotope difference. 7-14-00 DCh. */ + + int atw, atw_diff; + /* + NB: According to CTFile specification, difference should be in + [-18; +12] range, not in [-19; +19] as is checked below. */ + if ( (atw = get_atomic_mass( at )) && abs( atw_diff = (int)iso_mass[j] - atw ) < 20 ) { + ctab->atoms[atoms[j]-1].mass_difference = (char)(atw_diff? atw_diff : ZERO_ATW_DIFF); + } + } + } + } + continue; + } + + /* Sgroup, polymeric */ + if ( !strcmp( szType, "STY" ) || + !strcmp( szType, "SST" ) || + !strcmp( szType, "SLB" ) || + !strcmp( szType, "SCN" ) || + !strcmp( szType, "SAL" ) || + !strcmp( szType, "SBL" ) || + !strcmp( szType, "SDI" ) || + !strcmp( szType, "SMT" ) || + !strcmp( szType, "SBT" ) ) + { + int result; + if ( !treat_polymers ) + { + polymer_occurred = 1; + continue; + } + result = MolfileReadSgroupOfPolymer( ctab, pHdr, inp_file, line, szType, p, err, pStrErr ); + if ( result!=0 ) + { + TREAT_ERR( err, result, "Could not interpret polymer data:"); + dotify_non_printable_chars( line ); + AddErrorMessage(pStrErr, line); + continue; + } + } + } + +err_fin: + if ( !treat_polymers && polymer_occurred ) + { + /* for compatibility reasons, inchi-1 by default + ignores polymer related lines (as v. 1.04 did) */ + WarningMessage(pStrErr, "Ignore polymer data" ); + } + + if ( (ctab->sgroups.used > 0) && (debug_polymers > 1) ) + { + int i; + ITRACE_( "\n* THE FOLLOWING %-d POLYMER SGROUP(S) WERE RECOGNISED *\n", ctab->sgroups.used); + for (i=0; isgroups.used; i++) + { + char *sty[] = {"NON", "SRU", "MON", "COP", "MOD", "XL", "MER" }; + char *sst[] = {"NON", "ALT", "RAN", "BLK" }; + char *con[] = {"NON", "HT", "HH", "EU" }; + + ITRACE_( "\n* GROUP %-d\n", i); + ITRACE_( "* \tindex=%-d\n", ctab->sgroups.group[i]->id ); + ITRACE_( "* \ttype=%-d %-s\n", ctab->sgroups.group[i]->type, sty[ctab->sgroups.group[i]->type] ); + if ( ctab->sgroups.group[i]->subtype > -1 ) + ITRACE_( "* \tsubtype=%-d %-s\n", ctab->sgroups.group[i]->subtype, sst[ctab->sgroups.group[i]->subtype] ); + if ( ctab->sgroups.group[i]->conn ) + ITRACE_( "* \tconnection_type=%-d %-s\n", ctab->sgroups.group[i]->conn, + con[ctab->sgroups.group[i]->conn] ); + ITRACE_( "* \tlabel=%-d\n", ctab->sgroups.group[i]->label ); + ITRACE_( "* \t%-d atoms:\t", ctab->sgroups.group[i]->alist.used ); + IntArray_DebugPrint( &(ctab->sgroups.group[i]->alist) ); + ITRACE_( "* \t%-d bonds:\t", ctab->sgroups.group[i]->blist.used ); + IntArray_DebugPrint( &(ctab->sgroups.group[i]->blist) ); + ITRACE_( "\n"); + } + } + + return err; +} + + +/* + Parse polymer SGroups of Molfile +*/ +int MolfileReadSgroupOfPolymer( MOL_FMT_CTAB* ctab, + MOL_FMT_HEADER_BLOCK *pHdr, + INCHI_IOSTREAM *inp_file, + char line[MOL_FMT_INPLINELEN], + char *szType, + char *p, int err, char *pStrErr ) +{ +S_SHORT num_entries; +S_SHORT num_atoms = ctab->n_atoms; +S_SHORT num_bonds = ctab->n_bonds; +int j, index=-1, ret; +char stmp[4], stmplong[81]; +S_SHORT sg_nums[8], sg_num=-1, sg_atoms[15], sg_bonds[15], tmp; +int q, fail=0, len; +int debug_polymers = 0; +#if ( DEBUG_POLYMERS == 1 ) + debug_polymers = 1; +#elif ( DEBUG_POLYMERS == 2 ) + debug_polymers = 2; +#endif + debug_polymers = 0; + + /* Check for possible lead codes */ + + /* STY - Sgroup type + Polymer-related recognized types are: + SRU = SRU type, + MON = monomer, + COP = copolymer, + MER = Mer type, + MOD + CRO + */ + if ( !strcmp( szType, "STY" ) && + 0 < MolfileReadField( &num_entries, 3, MOL_FMT_SHORT_INT_DATA, &p ) && + 1 <= num_entries && num_entries <= 8 ) + { + for ( j = 0; j < num_entries; j++ ) + { + fail = 0 > MolfileReadField( &sg_nums[j], 0, MOL_FMT_SHORT_INT_DATA, &p ) || + 0 > MolfileReadField( stmp, 4, MOL_FMT_STRING_DATA, &p ) ; + if ( !fail ) + { + int type = MOL_FMT_M_STY_NON; + + lrtrim(stmp, &len); + + if ( !strcmp(stmp,"SRU") ) type = MOL_FMT_M_STY_SRU; + else if ( !strcmp(stmp,"MON") ) type = MOL_FMT_M_STY_MON; + else if ( !strcmp(stmp,"COP") ) type = MOL_FMT_M_STY_COP; + else if ( !strcmp(stmp,"MOD") ) type = MOL_FMT_M_STY_MOD; + else if ( !strcmp(stmp,"CRO") ) type = MOL_FMT_M_STY_CRO; + else if ( !strcmp(stmp,"MER") ) type = MOL_FMT_M_STY_MER; + else + fail = 1; + if ( !fail ) + { + index = MolFmtSgroups_GetIndexBySgroupId( sg_nums[j], &(ctab->sgroups) ); + if ( -1==index ) + { + ret = MolFmtSgroups_Append( &ctab->sgroups, sg_nums[j], type ); + if ( 0 != ret ) + fail = 1; + else + index = ctab->sgroups.used-1; + } + else + ctab->sgroups.group[index]->type = type; + if ( !fail ) + { + for (q=0; q<4; q++) + { + ctab->sgroups.group[index]->xbr1[q] = -777777.777; + ctab->sgroups.group[index]->xbr2[q] = -777777.777; + } + ctab->sgroups.group[index]->smt[0] = '\0'; + } + } + } + if ( fail ) + { + err = 5; + goto err_exit; + } + } + } + /* SST - Polymer Sgroup subtypes: + ALT = alternating, RAN = random, BLK = block */ + else if ( !strcmp( szType, "SST" ) && + 0 < MolfileReadField( &num_entries, 3, MOL_FMT_SHORT_INT_DATA, &p ) && + 1 <= num_entries && num_entries <= 8 ) + { + for ( j = 0; j < num_entries; j++ ) + { + fail = 0 > MolfileReadField( &sg_nums[j], 0, MOL_FMT_SHORT_INT_DATA, &p ) || + 0 > MolfileReadField( stmp, 4, MOL_FMT_STRING_DATA, &p ) ; + if ( !fail ) + { + index = MolFmtSgroups_GetIndexBySgroupId( sg_nums[j], &(ctab->sgroups) ); + if ( -1==index ) + fail = 1; + } + if ( !fail ) + { + ctab->sgroups.group[index]->subtype = MOL_FMT_M_SST_NON; + lrtrim(stmp, &len); + if ( !strcmp(stmp,"ALT") ) ctab->sgroups.group[index]->subtype = MOL_FMT_M_SST_ALT; + else if ( !strcmp(stmp,"RAN") ) ctab->sgroups.group[index]->subtype = MOL_FMT_M_SST_RAN; + else if ( !strcmp(stmp,"BLO") ) ctab->sgroups.group[index]->subtype = MOL_FMT_M_SST_BLK; + else if ( !strcmp(stmp,"BLK") ) ctab->sgroups.group[index]->subtype = MOL_FMT_M_SST_BLK;else + { + fail = 1; + break; + } + } + } + if ( fail ) + { + err = 6; + goto err_exit; + } + } + /* SLB - Sgroup Labels */ + else if ( !strcmp( szType, "SLB" ) && + 0 < MolfileReadField( &num_entries, 3, MOL_FMT_SHORT_INT_DATA, &p ) && + 1 <= num_entries && num_entries <= 8 ) + { + for ( j = 0; j < num_entries; j++ ) + { + fail = 0 > MolfileReadField( &sg_nums[j], 0, MOL_FMT_SHORT_INT_DATA, &p ) || + 0 > MolfileReadField( &tmp, 0, MOL_FMT_SHORT_INT_DATA, &p ); + if ( !fail ) + { + index = MolFmtSgroups_GetIndexBySgroupId( sg_nums[j], &(ctab->sgroups) ); + if ( -1==index ) + fail = 1; + } + if ( !fail ) + { + ctab->sgroups.group[index]->label = tmp; + } + } + if ( fail ) + { + err = 7; + goto err_exit; + } + } + /* SCN - Sgroup Connectivity + HH = head-to-head, HT = head-to-tail, EU = either unknown. + Left justified. */ + else if ( !strcmp( szType, "SCN" ) && + 0 < MolfileReadField( &num_entries, 3, MOL_FMT_SHORT_INT_DATA, &p ) && + 1 <= num_entries && num_entries <= 8 ) + { + for ( j = 0; j < num_entries; j++ ) + { + fail = 0 > MolfileReadField( &sg_nums[j], 0, MOL_FMT_SHORT_INT_DATA, &p ) || + 0 > MolfileReadField( stmp, 4, MOL_FMT_STRING_DATA, &p ) ; + if ( !fail ) + { + index = MolFmtSgroups_GetIndexBySgroupId( sg_nums[j], &(ctab->sgroups) ); + if ( -1==index ) + fail = 1; + } + if ( !fail ) + { + ctab->sgroups.group[index]->conn = MOL_FMT_M_CONN_NON; + lrtrim(stmp, &len); + if ( !strcmp(stmp,"HT") ) ctab->sgroups.group[index]->conn = MOL_FMT_M_CONN_HT; + else if ( !strcmp(stmp,"HH") ) ctab->sgroups.group[index]->conn = MOL_FMT_M_CONN_HH; + else if ( !strcmp(stmp,"EU") ) ctab->sgroups.group[index]->conn = MOL_FMT_M_CONN_EU; + else + /* NB: we do not allow explicit different abbrevn - + but note that totally skipping SCN line is allowed ("EU" will be inserted further) */ + fail = 1; + } + if ( fail ) + { + err = 8; + goto err_exit; + } + } + } + /* SAL - Sgroup atoms list */ + else if ( !strcmp( szType,"SAL" ) ) + { + if (0 < MolfileReadField( &sg_num, 4, MOL_FMT_SHORT_INT_DATA, &p ) && + 0 < MolfileReadField( &num_entries, 3, MOL_FMT_SHORT_INT_DATA, &p ) ) + { + index = MolFmtSgroups_GetIndexBySgroupId( sg_num, &(ctab->sgroups) ); + if ( -1==index ) + fail = 1; + if ( !fail ) + { + for ( j = 0; j < num_entries; j++ ) + { + if ( 0 > MolfileReadField( &sg_atoms[j], 0, MOL_FMT_SHORT_INT_DATA, &p ) || + sg_atoms[j] <= 0 || sg_atoms[j] > num_atoms + ) + { + fail = 1; + break; + } + } + } + if ( !fail ) + { + for ( j = 0; j < num_entries; j++ ) + { + if ( 0!=IntArray_Append( &(ctab->sgroups.group[index]->alist), sg_atoms[j] ) ) + { + fail = 1; + break; + } + } + } + } + else + { + fail = 1; + } + if ( fail ) + { + err = 9; + goto err_exit; + } + } + /* SBL - Sgroup bonds list */ + else if ( !strcmp( szType,"SBL" ) ) + { + if (0 < MolfileReadField( &sg_num, 4, MOL_FMT_SHORT_INT_DATA, &p ) && + 0 < MolfileReadField( &num_entries, 3, MOL_FMT_SHORT_INT_DATA, &p ) ) + { + index = MolFmtSgroups_GetIndexBySgroupId( sg_num, &(ctab->sgroups) ); + if ( -1==index ) + fail = 1; + if ( !fail ) + { + for ( j = 0; j < num_entries; j++ ) + { + if ( 0 > MolfileReadField( &sg_bonds[j], 0, MOL_FMT_SHORT_INT_DATA, &p ) || + sg_bonds[j] <= 0 || sg_bonds[j] > num_bonds + ) + { + fail = 1; + break; + } + } + } + if ( !fail ) + { + for ( j = 0; j < num_entries; j++ ) + { + if ( 0!=IntArray_Append( &(ctab->sgroups.group[index]->blist), sg_bonds[j] ) ) + { + fail = 1; + break; + } + } + } + } + else + { + fail = 1; + } + if ( fail ) + { + err = 10; + goto err_exit; + } + } + /* SDI */ + else if ( !strcmp( szType, "SDI" ) ) + { + double x[4]; + + if (0 < MolfileReadField( &sg_num, 4, MOL_FMT_SHORT_INT_DATA, &p ) && + 0 < MolfileReadField( &num_entries, 3, MOL_FMT_SHORT_INT_DATA, &p ) ) + { + index = MolFmtSgroups_GetIndexBySgroupId( sg_num, &(ctab->sgroups) ); + if ( -1==index ) + fail = 1; + else if ( num_entries != 4 ) + fail = 1; + if ( !fail ) + { + for ( j = 0; j < num_entries; j++ ) + { + if ( 0 > MolfileReadField( &x[j], 0, MOL_FMT_DOUBLE_DATA, &p ) ) + { + fail = 1; + break; + } + } + } + if ( !fail ) + { + if ( fabs( -fabs(ctab->sgroups.group[index]->xbr1[0]) + 777777.777 ) < 1.e-7 ) /* brkt1 coords not yet here */ + for (q=0; q<4; q++) ctab->sgroups.group[index]->xbr1[q] = x[q]; + else + for (q=0; q<4; q++) ctab->sgroups.group[index]->xbr2[q] = x[q]; + } + } + else + { + fail = 1; + } + if ( fail ) + { + err = 11; + goto err_exit; + } + } + /* SMT - Sgroup Subscript */ + else if ( !strcmp( szType, "SMT" ) ) + { + int index = -1; + if (0 < MolfileReadField( &sg_num, 4, MOL_FMT_SHORT_INT_DATA, &p ) && + 0 < MolfileReadField( stmplong, 80, MOL_FMT_STRING_DATA, &p ) ) + { + index = MolFmtSgroups_GetIndexBySgroupId( sg_num, &(ctab->sgroups) ); + } + if ( -1==index ) + fail = 1; + if ( !fail ) + { + lrtrim(stmplong, &len); + strcpy( ctab->sgroups.group[index]->smt, stmplong ); + } + if ( fail ) + { + err = 11; + goto err_exit; + } + } + + +ITRACE_( "\n"); + return 0; + +err_exit: + MolFmtSgroups_Free( &ctab->sgroups ); + + return err; /* ignore polymeric error for now */ +} + + +/* + Element symbol '*' ===> 'Zz' +*/ +static int MolfileReplaceStarAtoms( MOL_FMT_CTAB* ctab, int err, char *pStrErr ) +{ + + if ( ctab->sgroups.used > 0 ) + { + /* OK, preprocess the polymeric Sgroups */ + int num_atoms, i, nstar = 0; + num_atoms = ctab->n_atoms; + for ( i = 0; i < num_atoms; i ++ ) + { + if ( !strcmp( ctab->atoms[i].symbol, "*" ) ) + { + mystrncpy( ctab->atoms[i].symbol, "Zz", sizeof("Zz") ); + nstar++; + } + } + + if ( nstar%2 ) + { + TREAT_ERR( err, 9, "Odd number of star atoms is not supported"); + goto err_exit; + } + } + +err_exit: + return err; +} diff --git a/INCHI-1-SRC/INCHI_BASE/src/mol_fmt2.c b/INCHI-1-SRC/INCHI_BASE/src/mol_fmt2.c new file mode 100644 index 0000000..0e711a0 --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/mol_fmt2.c @@ -0,0 +1,650 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include +#include +#include +#include +#include + +#include "mode.h" +#include "mol_fmt.h" + +#include "ichierr.h" +#include "util.h" +#include "ichi_io.h" + + +/* + MolFile related procedures - 2 + +*/ + + +/* + Read n chars and find where they are + terminated with space or trailing 0 +*/ +int MolfileStrnread( char* dest, char* source, int len, char **first_space ) +{ +/* required len >= 0; dest must have at least len+1 bytes */ + + int i, c; + + if ( len > 0 ) + strncpy( dest, source, len ); + dest[len]='\0'; + + len = ( len > 0 ) ? (int)strlen( dest) + : 0; + + for ( i = (len-1); i >= 0 && 0 != (c=source[i]) && isspace(UCINT c); i-- ) ; + + *first_space = dest + (i+1); /* first blank or zero terminating byte in dest */ + + return len; /* number of actually processed bytes excluding zero terminator */ +} + + +/* Extract the 'data' in specified mol file field at given text position 'line_ptr' + * + * + * 1. 'field_len' for MOL_FMT_STRING_DATA does not include trailing zero, + * that is actual length of the string pointed by 'data' + * should be at least field_len+1 bytes. + * For numerical data 'field_len' is length of input data field + * For numerical integral data field_len <= 0 means read up to first + * non-numeric character as strtod() does ("free format") + * 2. return value: for MOL_FMT_STRING_DATA: number of bytes excluding trailing zero + * for all others: 1=success; 0 = empty; -1= error + * 3. on exit *line_ptr points to the next byte after the last entered + * + * + */ +int MolfileReadField(void* data, int field_len, int data_type, char** line_ptr) +{ +char *p = *line_ptr, *q, *p_end; +int i, c, len, ret=1; +long ldata; +double ddata; + +int DEFINITE_LENGTH_FIELD = 0; +int FIELD_ENDS_AT_FIRST_NON_DIGIT = 0; +int TOO_LONG_FIELD = 0; + + if ( field_len > MOL_FMT_MAX_VALUE_LEN ) + TOO_LONG_FIELD = 1; + else if ( field_len <= 0 ) + FIELD_ENDS_AT_FIRST_NON_DIGIT = 1; + else + DEFINITE_LENGTH_FIELD = 1; + + switch( data_type ) + { + case MOL_FMT_STRING_DATA: + /* pass by all leading spaces */ + for ( i=0; + i < field_len && 0 != (c=p[i]) && isspace(UCINT c); + i++ ) + ; + + len = MolfileStrnread( (char*) data, &p[i], field_len-i, &q ); + + ret = ( q - (char*) data );/* actual data length */ + *q = '\0'; /* add zero termination to data if it is not there yet*/ + *line_ptr += (len+i); /* ptr to the 1st byte of the next input field or to zero termination */ + break; + + + case MOL_FMT_CHAR_INT_DATA: + case MOL_FMT_SHORT_INT_DATA: + case MOL_FMT_LONG_INT_DATA: + { + char str[MOL_FMT_MAX_VALUE_LEN+1]; + ldata = 0L; + if ( TOO_LONG_FIELD ) + { + ret = -1; + } + else if ( DEFINITE_LENGTH_FIELD ) + { + /* fixed length */ + *line_ptr += ( len = MolfileStrnread( str, p, field_len, &q ) ); + + *q = '\0'; + if ( !len || !(q-str) ) + ret = 0; /* empty string */ + else + if ( (ldata=strtol(str,&p_end,10), p_end != q) ) + ret = -1; /* wrong data: incompletely interpreted */ + } + else if ( FIELD_ENDS_AT_FIRST_NON_DIGIT ) + { + /* free format: field_len <= 0 */ + ldata = strtol( p, &p_end, 10 ); + *line_ptr += ( len = p_end - p ); + if ( len == 0 ) + ret = 0; + } + else + { + /* should not come here */ + ret = -1; + } + + switch( data_type ) + { + case MOL_FMT_CHAR_INT_DATA: + if ( SCHAR_MIN <= ldata && ldata <= SCHAR_MAX ) + { + /* from || to &&: 11-19-96 */ + *(S_CHAR*) data = (S_CHAR)ldata; + } + else + { + *(S_CHAR*) data = (S_CHAR)0; + ret = -1; + } + break; + case MOL_FMT_SHORT_INT_DATA: + if ( SHRT_MIN <= ldata && ldata <= SHRT_MAX ) + { + *(S_SHORT*) data = (S_SHORT)ldata; + } + else + { + *(S_SHORT*) data = (S_SHORT)0; + ret = -1; + } + break; + case MOL_FMT_LONG_INT_DATA: + if ( LONG_MIN < ldata && ldata < LONG_MAX ) + { + *(long*) data = (long)ldata; + } + else + { + *(long*) data = 0L; + ret = -1; + } + break; + default: + ret=-1; + } + } /* MOL_FMT_CHAR_INT_DATA... */ + break; + + + case MOL_FMT_DOUBLE_DATA: + case MOL_FMT_FLOAT_DATA: + { + char str[MOL_FMT_MAX_VALUE_LEN+1]; + + if ( TOO_LONG_FIELD ) + { + ret = -1; + ddata = 0.0; + } + else if ( DEFINITE_LENGTH_FIELD ) + { + *line_ptr += (len = MolfileStrnread( str, p, field_len, &q )); + *q = '\0'; + if ( !len || !(q-str) ) + { + /* empty string */ + ddata = 0.0; + ret = 0; + } + else if ( (ddata=strtod(str,&p_end), p_end != q) ) + { + /* wrong data */ + ret = -1; + } + } + else if ( FIELD_ENDS_AT_FIRST_NON_DIGIT ) + { + /* free format */ + ddata = strtod( p, &p_end ); + *line_ptr += ( len = p_end - p ); + if ( len == 0 ) + { + ret = 0; + } + } + else + { + /* should not come here */ + ret = -1; + } + + switch(data_type) + { + + case MOL_FMT_DOUBLE_DATA: + if ( ddata != HUGE_VAL && /*ldata*/ ddata != -HUGE_VAL ) + { /* replaced ldata with ddata 6-30-98 DCh */ + *(double*)data = ddata; + } + else + { + *(double*)data = 0.0; + ret = -1; + } + break; + + case MOL_FMT_FLOAT_DATA: + if ( fabs(ddata) <= (double)FLT_MIN ) + { + *(float*)data = 0.0; + } + else + if ( fabs(ddata) >= (double)FLT_MAX ) + { + *(float*)data = 0.0; + ret = -1; + } + else + { + *(float*)data = (float)ddata; + } + break; + } + } /* MOL_FMT_DOUBLE_DATA... */ + break; + + + case MOL_FMT_JUMP_TO_RIGHT: + { + + for ( i = 0; i < field_len && p[i]; i++ ) + ; + + *line_ptr += i; + ret = i; + } + break; + + + default: + ret = -1; + } + + + return ret; +} + + +/* + Read molfile number from the name line like "Structure #22" +*/ +long MolfileExtractStrucNum( MOL_FMT_HEADER_BLOCK *pHdr ) +{ +static char sStruct[] = "Structure #"; +static char sINCHI[] = INCHI_NAME; +long lMolfileNumber = 0; +char *p, *q = NULL; + + if ( pHdr ) + { + if ( !inchi_memicmp( pHdr->molname, sStruct, sizeof(sStruct)-1 ) ) + { + p = pHdr->molname + sizeof(sStruct)-1; + lMolfileNumber = strtol( p, &q, 10 ); + p = pHdr->line2; + if ( !q || *q || + inchi_memicmp( p, sINCHI, sizeof(sINCHI)-1) || + !strstr( p+sizeof(sINCHI)-1, "SDfile Output" ) ) + { + lMolfileNumber = 0; + } + } + } + + return lMolfileNumber; +} + + +/* + Check if MOL file contains no structure +*/ +int MolfileHasNoChemStruc(MOL_FMT_DATA* mfdata) +{ + if ( !mfdata || !mfdata->ctab.atoms ) + return 1; + + if ( mfdata->ctab.n_atoms <= 0 ) + return 1; + + if ( 0 < mfdata->ctab.n_bonds && !mfdata->ctab.bonds ) + return 1; + + return 0; +} + + +/* + Copy MOL-formatted data of SDF record or Molfile to another file +*/ +int MolfileSaveCopy( INCHI_IOSTREAM *inp_file, + long fPtrStart, + long fPtrEnd, + FILE *outfile, + long num ) +{ +char line[MOL_FMT_INPLINELEN], *p; +long fPtr; +int ret = 1; +char szNumber[32]; + + + if ( inp_file->type == INCHI_IOSTREAM_TYPE_FILE ) + { + + FILE* infile = inp_file-> f; + + if ( !infile ) + return 1; + if ( !outfile ) + return 1; + + if ( fPtrStart < 0L && fPtrEnd <= fPtrStart ) + return 1; + + if ( 0 != fseek(infile, fPtrStart, SEEK_SET) ) + return 1; + + + while ( fPtrEnd > (fPtr = ftell(infile)) && + fPtr >= 0L && + inchi_fgetsLf( line, sizeof(line)-1, inp_file ) ) + { + + line[sizeof(line)-1] = '\0'; /* unnecessary extra precaution */ + + if ( fPtr == fPtrStart && num ) + { + int len; + lrtrim( line, &len ); + len = sprintf( szNumber, "#%ld%s", num, len ? "/" : "" ); + mystrncpy( line+len, line, sizeof(line)-len-1 ); + memcpy( line, szNumber, len ); + } + + if ( !strchr(line, '\n') ) + { + p = line+strlen(line); + p[0] = '\n'; + p[1] = '\0'; + } + + fputs( line, outfile ); + } + + + ret = fseek( infile, fPtrEnd, SEEK_SET ); + } + + else if ( inp_file->type == INCHI_IOSTREAM_TYPE_STRING ) + { + /* TODO: add code here. */ + ; + } + else + ; + + return ret; +} + + +#define MIN_STDATA_X_COORD 0.0 +#define MAX_STDATA_X_COORD 256.0 +#define MIN_STDATA_Y_COORD 0.0 +#define MAX_STDATA_Y_COORD 256.0 +#define MIN_STDATA_Z_COORD 0.0 +#define MAX_STDATA_Z_COORD 256.0 +#define MAX_STDATA_AVE_BOND_LENGTH 20.0 +#define MIN_STDATA_AVE_BOND_LENGTH 10.0 + + +/* + Get xyz dimensionality and normalization factors +*/ +int MolfileGetXYZDimAndNormFactors( MOL_FMT_DATA* mfdata, + int find_norm_factors, + double *x0, + double *y0, + double *z0, + double *xmin, + double *ymin, + double *zmin, + double *scaler, + int *err, + char *pStrErr ) + +{ +int i; +int num_dimensions=0, num_atoms , num_bonds; +double max_x=-1.0e32, max_y=-1.0e32, max_z=-1.0e32; +double min_x= 1.0e32, min_y= 1.0e32, min_z= 1.0e32; +double macheps = 1.0e-10, small_coeff = 0.00001; +double x_coeff, y_coeff, z_coeff, coeff=1.0, average_bond_length; + + + *x0 = MIN_STDATA_X_COORD; + *y0 = MIN_STDATA_Y_COORD; + *z0 = MIN_STDATA_Z_COORD; + *xmin = *ymin = *zmin =0.0; + *scaler = coeff; + + if ( MolfileHasNoChemStruc( mfdata ) ) + goto exit_function; + + num_atoms = mfdata->ctab.n_atoms; + for ( i = 0; i < num_atoms; i ++ ) + { + max_x = inchi_max(mfdata->ctab.atoms[i].fx, max_x); + min_x = inchi_min(mfdata->ctab.atoms[i].fx, min_x); + max_y = inchi_max(mfdata->ctab.atoms[i].fy, max_y); + min_y = inchi_min(mfdata->ctab.atoms[i].fy, min_y); + max_z = inchi_max(mfdata->ctab.atoms[i].fz, max_z); + min_z = inchi_min(mfdata->ctab.atoms[i].fz, min_z); + } + + num_bonds = 0; + average_bond_length = 0.0; + for ( i = 0; i < mfdata->ctab.n_bonds; i ++ ) + { + int a1 = mfdata->ctab.bonds[i].atnum1-1; + int a2 = mfdata->ctab.bonds[i].atnum2-1; + double dx = mfdata->ctab.atoms[a1].fx-mfdata->ctab.atoms[a2].fx; + double dy = mfdata->ctab.atoms[a1].fy-mfdata->ctab.atoms[a2].fy; + double dz = mfdata->ctab.atoms[a1].fz-mfdata->ctab.atoms[a2].fz; + + if ( a1 < 0 || a1 >= num_atoms || + a2 < 0 || a2 >= num_atoms || + a1 == a2 ) + { + *err |= 1; /* bond for invalid atom number(s); ignored */ + TREAT_ERR (*err, 0, "Bond to nonexistent atom"); + continue; + } + + average_bond_length += sqrt( dx*dx + dy*dy + dz*dz ); + num_bonds ++; + } + + + if ( max_x - min_x <= small_coeff*(fabs(max_x) + fabs(min_x)) ) + x_coeff = 0.0; + else + x_coeff = (MAX_STDATA_X_COORD - MIN_STDATA_X_COORD)/(max_x - min_x); + + if ( max_y - min_y <= small_coeff*(fabs(max_y) + fabs(min_y)) ) + y_coeff = 0.0; + else + y_coeff = (MAX_STDATA_Y_COORD - MIN_STDATA_Y_COORD)/(max_y - min_y); + + if ( max_z - min_z <= small_coeff*(fabs(max_z) + fabs(min_z)) ) + z_coeff = 0.0; + else + z_coeff = (MAX_STDATA_Z_COORD - MIN_STDATA_Z_COORD)/(max_z - min_z); + + + num_dimensions = ((x_coeff > macheps || y_coeff >macheps ) && fabs(z_coeff) < macheps)? 2: + (fabs(z_coeff) > macheps)? 3: 0; + + + if ( !find_norm_factors ) + goto exit_function; + + + /* Find normalization parameters */ + switch ( num_dimensions ) + { + case 0: + coeff = 0.0; + break; + + case 2: + /* choose the smallest stretching coefficient */ + if ( x_coeff > macheps && y_coeff > macheps ) + { + coeff = inchi_min( x_coeff, y_coeff ); + } + else if ( x_coeff > macheps ) + { + coeff = x_coeff; + } + else if ( y_coeff > macheps ) + { + coeff = y_coeff; + } + else + { + coeff = 1.0; + } + break; + + case 3: + /* choose the smallest stretching coefficient */ + if ( x_coeff > macheps && y_coeff > macheps ) + { + coeff = inchi_min( x_coeff, y_coeff ); + coeff = inchi_min( coeff, z_coeff ); + } + else if ( x_coeff > macheps ) + { + coeff = inchi_min( x_coeff, z_coeff ); + } + else if ( y_coeff > macheps ) + { + coeff = inchi_min( y_coeff, z_coeff ); + }else + { + coeff = z_coeff; + } + break; + + default: + coeff = 0.0; + } + + + if ( num_bonds > 0 ) + { + + average_bond_length /= (double)num_bonds; + if ( average_bond_length * coeff > MAX_STDATA_AVE_BOND_LENGTH ) + { + coeff = MAX_STDATA_AVE_BOND_LENGTH / average_bond_length; /* avoid too long bonds */ + } + else if ( average_bond_length * coeff < macheps ) + { + coeff = 1.0; /* all lengths are of zero length */ + } + else if ( average_bond_length * coeff < MIN_STDATA_AVE_BOND_LENGTH ) + { + coeff = MIN_STDATA_AVE_BOND_LENGTH / average_bond_length; /* avoid too short bonds */ + } + } + +exit_function:; + + *x0 = min_x; + *y0 = min_y; + *z0 = min_z; + *xmin = MIN_STDATA_X_COORD; + *ymin = MIN_STDATA_Y_COORD; + *zmin = MIN_STDATA_Z_COORD; + *scaler = coeff; + + return num_dimensions; +} + + +/* + Clean up MOL-format parser data +*/ +MOL_FMT_DATA* FreeMolfileData( MOL_FMT_DATA* mfdata ) +{ + if ( mfdata ) + { + if ( mfdata->ctab.atoms ) + inchi_free( mfdata->ctab.atoms ); + + if ( mfdata->ctab.bonds ) + inchi_free( mfdata->ctab.bonds ); + + if ( mfdata->ctab.coords ) + inchi_free( mfdata->ctab.coords ); + + /*if ( 0!=mfdata->ctab.sgroups.used )*/ + MolFmtSgroups_Free( &(mfdata->ctab.sgroups) ); + + if ( mfdata->ctab.v3000 ) + DeleteMolfileV3000Info( mfdata->ctab.v3000 ); + + inchi_free( mfdata ); + + mfdata = NULL; + } + + return mfdata; +} diff --git a/INCHI-1-SRC/INCHI_BASE/src/mol_fmt3.c b/INCHI-1-SRC/INCHI_BASE/src/mol_fmt3.c new file mode 100644 index 0000000..429c5a2 --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/mol_fmt3.c @@ -0,0 +1,1634 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include +#include +#include +#include +#include + +#include "mode.h" +#include "mol_fmt.h" + +#include "ichierr.h" +#include "util.h" +#include "ichi_io.h" + + + +/* + Molfile V3000 related procedures + +*/ + + +static int get_actual_atom_number( int index, int n, int *orig, int *fin ); + + +/* + Init V3000 reader +*/ +int MolfileV3000Init( MOL_FMT_CTAB* ctab, + char *pStrErr ) +{ + int ret=0; + + /* STAR ATOMS */ + ctab->v3000->n_star_atoms = 0; + ctab->v3000->n_non_star_atoms = 0; + + if ( ctab->n_atoms ) + { + ctab->v3000->atom_index_orig = (int *) inchi_calloc( ctab->n_atoms, sizeof(int) ); + ctab->v3000->atom_index_fin = (int *) inchi_calloc( ctab->n_atoms,sizeof(int) ); + memset( ctab->v3000->atom_index_orig, -1, ctab->n_atoms ); + memset( ctab->v3000->atom_index_fin, -1, ctab->n_atoms ); + } + else + { + ctab->v3000->atom_index_orig = + ctab->v3000->atom_index_fin = NULL; + } + + /* HAPTIC BONDS */ + ctab->v3000->n_haptic_bonds = 0; + ctab->v3000->haptic_bonds = (NUM_LISTS *) inchi_calloc(1, sizeof( NUM_LISTS ) ); + if ( !ctab->v3000->haptic_bonds ) + { + AddErrorMessage(pStrErr, "Out of RAM"); + return -1; + } + ret = NumLists_Alloc( ctab->v3000->haptic_bonds, 8 ); + if ( ret<0 ) + { + ctab->v3000->haptic_bonds = NULL; + AddErrorMessage(pStrErr, "Out of RAM"); + return -1; + } + + + /* STEABS */ + ctab->v3000->n_steabs = 0; + ctab->v3000->steabs = (NUM_LISTS *) inchi_calloc(1, sizeof( NUM_LISTS ) ); + if ( !ctab->v3000->steabs ) + { + AddErrorMessage(pStrErr, "Out of RAM"); + return -1; + } + ret = NumLists_Alloc( ctab->v3000->steabs, 1 ); + if ( ret<0 ) + { + ctab->v3000->steabs = NULL; + AddErrorMessage(pStrErr, "Out of RAM"); + return -1; + } + /* STEREL */ + ctab->v3000->n_sterel = 0; + ctab->v3000->sterel = (NUM_LISTS *) inchi_calloc(1, sizeof( NUM_LISTS ) ); + if ( !ctab->v3000->sterel ) + { + AddErrorMessage(pStrErr, "Out of RAM"); + return -1; + } + ret = NumLists_Alloc( ctab->v3000->sterel, 4 ); + if ( ret<0 ) + { + ctab->v3000->sterel = NULL; + AddErrorMessage(pStrErr, "Out of RAM"); + return -1; + } + /* STERAC */ + ctab->v3000->n_sterac = 0; + ctab->v3000->sterac = (NUM_LISTS *) inchi_calloc(1, sizeof( NUM_LISTS ) ); + if ( !ctab->v3000->sterac ) + { + AddErrorMessage(pStrErr, "Out of RAM"); + return -1; + } + ret = NumLists_Alloc( ctab->v3000->sterac, 4 ); + if ( ret<0 ) + { + ctab->v3000->sterac = NULL; + AddErrorMessage(pStrErr, "Out of RAM"); + return -1; + } + + + return ret; +} + + +/* + Delete V3000 reader data +*/ +int DeleteMolfileV3000Info( MOL_FMT_v3000* v3000 ) +{ + if ( v3000 ) + { + + if ( v3000->atom_index_orig ) + inchi_free ( v3000->atom_index_orig ); + if ( v3000->atom_index_fin ) + inchi_free ( v3000->atom_index_fin ); + + if ( v3000->haptic_bonds ) + { + NumLists_Free( v3000->haptic_bonds ); + free ( v3000->haptic_bonds ); + } + + if ( v3000->steabs ) + { + NumLists_Free( v3000->steabs ); + free ( v3000->steabs ); + } + + if ( v3000->sterel ) + { + NumLists_Free( v3000->sterel ); + free ( v3000->sterel ); + } + + if ( v3000->sterac ) + { + NumLists_Free( v3000->sterac ); + free ( v3000->sterac ); + } + + inchi_free ( v3000 ); + v3000 = NULL; + } + + return 0; +} + + +/* + Extended version of inchi_fgetsLf which is able of reading + concatenated lines (ending with '-') of V3000 Molfile. + Also removes "M V30 " prefix" and normalizes the rest of string +*/ +char* inchi_fgetsLf_V3000( char* line, INCHI_IOSTREAM* inp_stream ) +{ +char *p=NULL; +int len=0; + + p = inchi_fgetsLf( line, MOL_FMT_V3000_INPLINELEN, inp_stream ); + if ( !p ) + return NULL; + + len = (int) strlen(p); + if ( len<7 ) + return NULL; + + if ( strncmp( p, "M V30 ", 7) ) + return NULL; + + p+= 7; + len = normalize_string( p ); + + return p; +} + + + + +/* + Read V3000 field. + + It is MolfileReadField updated for V3000. + It considers right whitespace as stop sign, + no predefined len is used. + + Returns -1 on error otherwise number of bytes read. + + NB: ASSUMES THAT STRING HAS BEEN NORMALIZED with normalize_string() + + TODO: strings with spaces in double quotes +*/ +int MolfileV3000ReadField( void* data, + int data_type, + char** line_ptr) +{ +int nread = 0; +char field[MOL_FMT_V3000_MAXFIELDLEN]; +const int max_field_len=sizeof(field); +long ldata = 0L; +double ddata = 0.0; +char *p_end; + + memset( field, 0, max_field_len ); + + nread = read_upto_delim( line_ptr, field, max_field_len, " \t\n\v\f\r" ); + + switch( data_type ) + { + case MOL_FMT_STRING_DATA: + { + if ( nread ) + mystrncpy ( (char *) data, field, nread + 1 ); + else + ( (char *)data )[0] = '\0'; + } + break; + + case MOL_FMT_CHAR_INT_DATA: + case MOL_FMT_SHORT_INT_DATA: + case MOL_FMT_LONG_INT_DATA: + case MOL_FMT_INT_DATA: + { + /* assume that field ends at first non-digit */ + ldata = strtol( field, &p_end, 10 ); + + if ( p_end==field ) + nread = 0; + + if ( data_type == MOL_FMT_LONG_INT_DATA ) + { + if ( LONG_MIN < ldata && ldata < LONG_MAX ) + *(long*) data = (long) ldata; + else + { + *(long*) data = 0L; + nread = -1; + } + } + else if ( data_type == MOL_FMT_INT_DATA ) + { + if ( INT_MIN <= ldata && ldata <= INT_MAX ) + *(int*) data = (int)ldata; + else + { + *(int*) data = (int)0; + nread = -1; + } + } + + else if ( data_type == MOL_FMT_CHAR_INT_DATA ) + { + if ( SCHAR_MIN <= ldata && ldata <= SCHAR_MAX ) + *(S_CHAR*) data = (S_CHAR)ldata; + else + { + *(S_CHAR*) data = (S_CHAR)0; + nread = -1; + } + } + + else if ( data_type == MOL_FMT_SHORT_INT_DATA ) + { + if ( SHRT_MIN <= ldata && ldata <= SHRT_MAX ) + *(S_SHORT*) data = (S_SHORT)ldata; + else + { + *(S_SHORT*) data = (S_SHORT)0; + nread = -1; + } + } + else + nread =-1; + } + break; /* INT's */ + + + case MOL_FMT_DOUBLE_DATA: + case MOL_FMT_FLOAT_DATA: + { + /* assume that field ends at first non-digit */ + ddata = strtod( field, &p_end ); + + if ( p_end == field ) + nread = 0; + + if ( data_type == MOL_FMT_DOUBLE_DATA ) + { + if ( ddata != HUGE_VAL && /*ldata*/ ddata != -HUGE_VAL ) + *(double*)data = ddata; + else + { + *(double*)data = 0.0; + nread = -1; + } + } + else if ( data_type == MOL_FMT_FLOAT_DATA ) + { + if ( fabs(ddata) <= (double)FLT_MIN ) + *(float*)data = 0.0; + else if ( fabs(ddata) >= (double)FLT_MAX ) + { + *(float*)data = 0.0; + nread = -1; + } + } + else + { + *(float*)data = (float)ddata; + } + } + break; /* REAL's */ + + default: + nread = -1; + } + + return nread; +} + + +/* + Read V3000 keyword. + TODO: strings with spaces in double quotes +*/ +int MolfileV3000ReadKeyword( char* key, + char** line_ptr) +{ +int nread = 0; +char field[MOL_FMT_V3000_MAXFIELDLEN]; +const int max_field_len=sizeof(field); + + + memset( field, 0, max_field_len ); + + nread = read_upto_delim( line_ptr, field, max_field_len, "= \t\n\v\f\r" ); + + if ( nread ) + { + mystrncpy ( key, field, nread + 1 ); + /* consume '=' sign if present */ + if ( *line_ptr ) + { + if ( *line_ptr[0] == '=' ) + { + *line_ptr = *line_ptr + 1; + } + } + } + else + key[0] = '\0'; + + return nread; +} + + +/* + Read V3000 head of CTab +*/ +int MolfileV3000ReadCTABBeginAndCountsLine( MOL_FMT_CTAB* ctab, + INCHI_IOSTREAM *inp_file, + char *pStrErr ) +{ +char field[MOL_FMT_V3000_MAXFIELDLEN]; +int err = 0, len; +int failed = 0; + +int nc; +char *p=NULL, *line = NULL; +INCHI_IOSTREAM tmpin; +INCHI_IOSTREAM_STRING *pin= &tmpin.s; +inchi_ios_init( &tmpin, INCHI_IOSTREAM_TYPE_STRING, NULL ); + + + /* Check for proper start */ + + /*p = inchi_fgetsLf_V3000( line, inp_file );*/ + inchi_strbuf_reset( pin ); + nc = get_V3000_input_line_to_strbuf(pin, inp_file); + if ( nc<1 ) p = NULL; + else p = line = pin->pStr; + if ( !p || strcmp(p, "BEGIN CTAB") ) + { + TREAT_ERR_AND_FIN (err, 1, err_fin, "Error: No V3000 CTab start marker"); + } + remove_one_lf( line ); + + + /* Reset all previosly read data from quasi-counts line */ + /* (which contains only single meaningful value, 'V3000' marker */ + + ctab->n_atoms = -1; + ctab->n_bonds = -1; + ctab->chiral_flag = -1; + ctab->n_stext_entries = -1; + /* Relax stricthness of V3000 conformance: */ + /* Do not check if '999' supplied, just use this. */ + ctab->n_property_lines = 999; + + + /* Read counts line */ + + /*p = inchi_fgetsLf_V3000( line, inp_file );*/ + inchi_strbuf_reset( pin ); + nc = get_V3000_input_line_to_strbuf(pin, inp_file); + if ( nc<1 ) p = NULL; + else p = line = pin->pStr; + + if ( !p ) + { + TREAT_ERR_AND_FIN (err, 1, err_fin, "Cannot read V3000 counts line"); + } + + remove_one_lf( line ); + + + /* Parse counts line */ + + len = MolfileV3000ReadField(field, MOL_FMT_STRING_DATA, &p); + if ( strcmp( field, "COUNTS" ) ) + { + TREAT_ERR_AND_FIN (err, 1, err_fin, "Cannot read V3000 counts line"); + } + + failed = 0; + if (0 > MolfileV3000ReadField(&ctab->n_atoms,MOL_FMT_INT_DATA, &p) ) + failed = 2; + else if ( 0 > MolfileV3000ReadField(&ctab->n_bonds,MOL_FMT_INT_DATA, &p) ) + failed = 1; + else if ( 0 > MolfileV3000ReadField(&ctab->v3000->n_sgroups,MOL_FMT_INT_DATA, &p) ) + failed = 1; + else if ( 0 > MolfileV3000ReadField(&ctab->v3000->n_3d_constraints,MOL_FMT_INT_DATA, &p) ) + failed = 1; + else if ( 0 > MolfileV3000ReadField(&ctab->chiral_flag, MOL_FMT_CHAR_INT_DATA, &p) ) + failed = 1; + + if ( failed ) + { + err = 3; + if ( failed==2 ) + { + TREAT_ERR( err, 3, "Number of atoms too large. V3000 counts line:"); + } + else + { + /* too long input file line or other value min-max range mismatch */ + TREAT_ERR( err, 3, "Cannot interpret V3000 counts line:"); + } + dotify_non_printable_chars( line ); + AddErrorMessage(pStrErr, line); + goto err_fin; + } + +err_fin: + return err; +} + + +/* + Read V3000 SGroup +*/ +int MolfileV3000ReadSGroup( MOL_FMT_CTAB* ctab, + INCHI_IOSTREAM *inp_file, + int err, char *pStrErr ) +{ +int nc; +char *p=NULL, *line = NULL; +INCHI_IOSTREAM tmpin; +INCHI_IOSTREAM_STRING *pin= &tmpin.s; +inchi_ios_init( &tmpin, INCHI_IOSTREAM_TYPE_STRING, NULL ); + + /*p = inchi_fgetsLf_V3000( line, inp_file );*/ + + while (1 ) + { + nc = get_V3000_input_line_to_strbuf(pin, inp_file); + if ( nc<1 ) p = NULL; + else p = line = pin->pStr; + remove_one_lf( line ); + if ( p && !strcmp(p, "END SGROUP") ) + return 0; + } + + /* if ( !p || strcmp(p, "END SGROUP") ) */ + { + TREAT_ERR_AND_FIN (err, 1, err_fin, "Error: No V3000 SGroup end marker"); + } + +err_fin: + return err; +} + + +/* + Read V3000 3DBlock +*/ +int MolfileV3000Read3DBlock( MOL_FMT_CTAB* ctab, + INCHI_IOSTREAM *inp_file, + int err, char *pStrErr ) +{ +int nc; +char *p=NULL, *line = NULL; +INCHI_IOSTREAM tmpin; +INCHI_IOSTREAM_STRING *pin= &tmpin.s; +inchi_ios_init( &tmpin, INCHI_IOSTREAM_TYPE_STRING, NULL ); + + /*p = inchi_fgetsLf_V3000( line, inp_file );*/ + nc = get_V3000_input_line_to_strbuf(pin, inp_file); + if ( nc<1 ) p = NULL; + else p = line = pin->pStr; + remove_one_lf( line ); + + if ( !p || strcmp(p, "END OBJ3D") ) + { + TREAT_ERR_AND_FIN (err, 1, err_fin, "Error: No V3000 3DBlock end marker"); + } +goto err_fin; + +err_fin: + return err; +} + + +/* + Read V3000 collections +*/ +int MolfileV3000ReadCollections( MOL_FMT_CTAB* ctab, + INCHI_IOSTREAM *inp_file, + int err, char *pStrErr ) +{ +char field[MOL_FMT_V3000_MAXFIELDLEN]; +const int max_field_len=sizeof(field); +int nread, len, n_coll; +int failed = 0; +int nc; +char *p=NULL, *line = NULL; +INCHI_IOSTREAM tmpin; +INCHI_IOSTREAM_STRING *pin= &tmpin.s; +inchi_ios_init( &tmpin, INCHI_IOSTREAM_TYPE_STRING, NULL ); + + /*p = inchi_fgetsLf_V3000( line, inp_file );*/ + nc = get_V3000_input_line_to_strbuf(pin, inp_file); + if ( nc<1 ) p = NULL; + else p = line = pin->pStr; + remove_one_lf( line ); + + while ( p && strcmp(p, "END COLLECTION") ) + { + int stereo_kind = MOL_FMT_V3000_STENON; + /* stereo collection of interest */ + NUM_LISTS *ste_coll=NULL; + + nread = read_upto_delim( &p, field, max_field_len, "/" ); + if ( nread < 6 ) + { + failed = 1; + break; + } + if ( strcmp(field, "MDLV30") ) + { + failed = 1; + break; + } + + nread = read_upto_delim( &p, field, max_field_len, "1234567890 \t\n\v\f\r" ); + if ( !strcmp(field, "/STEABS") ) + { + n_coll = 1; + stereo_kind = MOL_FMT_V3000_STEABS; + ste_coll = ctab->v3000->steabs; + } + else if ( !strcmp(field, "/STEREL") ) + { + /* get number of collection */ + if ( 0 > MolfileV3000ReadField( &n_coll, MOL_FMT_CHAR_INT_DATA, &p)) + { failed = 1; break; } + stereo_kind = MOL_FMT_V3000_STEREL; + ste_coll = ctab->v3000->sterel; + } + else if ( !strcmp(field, "/STERAC") ) + { + /* get number of collection */ + if ( 0 > MolfileV3000ReadField( &n_coll, MOL_FMT_CHAR_INT_DATA, &p)) + { failed = 1; break; } + stereo_kind = MOL_FMT_V3000_STERAC; + ste_coll = ctab->v3000->sterac; + } + else + ; + + if ( stereo_kind != MOL_FMT_V3000_STENON ) + /* currently skip non-stereo collections */ + { + /* consume atoms= */ + if ( (len=MolfileV3000ReadKeyword(field, &p) > 0 ) ) + { + if ( !strcmp(field, "ATOMS") ) + { + int res, *num_list = NULL; + + if ( 0 > MolfileV3000ReadStereoCollection( ctab, &p, &num_list, pStrErr) ) + failed = 1; + + else if ( !num_list ) + failed = 1; + + else + { + int k, nnum; + num_list[0] = n_coll; + nnum = num_list[1]; + for (k=2; kv3000->n_non_star_atoms+ctab->v3000->n_star_atoms, + ctab->v3000->atom_index_orig, + ctab->v3000->atom_index_fin ); + } + res = NumLists_Append( ste_coll, num_list ); + if ( res < 0 ) + { + failed = 1; + } + else + { + if ( stereo_kind == MOL_FMT_V3000_STEABS ) + ctab->v3000->n_steabs++; + else if ( stereo_kind == MOL_FMT_V3000_STEREL ) + ctab->v3000->n_sterel++; + else if ( stereo_kind == MOL_FMT_V3000_STERAC ) + ctab->v3000->n_sterac++; + } + } + } + } + else + { failed = 1; break; } + } + +/*next_line:*/ + /*p = inchi_fgetsLf_V3000( line, inp_file );*/ + inchi_strbuf_reset( pin ); + nc = get_V3000_input_line_to_strbuf(pin, inp_file); + if ( nc<1 ) p = NULL; + else p = line = pin->pStr; + remove_one_lf( line ); + } + + if ( failed ) + { + /*p = inchi_fgetsLf_V3000( line, inp_file );*/ + inchi_strbuf_reset( pin ); + nc = get_V3000_input_line_to_strbuf(pin, inp_file); + if ( nc<1 ) p = NULL; + else p = line = pin->pStr; + remove_one_lf( line ); + } + + if ( !p ) + failed = 1; + + if ( failed ) + { + err = 7; + TREAT_ERR( err, 7, "Cannot interpret V3000 collection line(s)"); + dotify_non_printable_chars( line ); + AddErrorMessage(pStrErr, line); + goto err_fin; + } + + /* Error: No V3000 Collection end marker */ + if ( ctab->v3000->n_steabs || + ctab->v3000->n_sterel || + ctab->v3000->n_sterac ) + { + AddErrorMessage(pStrErr, "V3000 enhanced stereo read/stored but ignored"); + } + +err_fin: + return err; +} + + +/* + Read V3000 atoms +*/ +int MolfileV3000ReadAtomsBlock( MOL_FMT_CTAB* ctab, + INCHI_IOSTREAM *inp_file, + int err, char *pStrErr ) +{ +int i; +static const S_SHORT charge_val[] = {0, 3, 2, 1, 'R', -1, -2, -3}; +char field[MOL_FMT_V3000_MAXFIELDLEN]; +int nc; +char *p=NULL, *line = NULL; +INCHI_IOSTREAM tmpin; +INCHI_IOSTREAM_STRING *pin= &tmpin.s; +inchi_ios_init( &tmpin, INCHI_IOSTREAM_TYPE_STRING, NULL ); + + /* Check for proper start */ + + /*p = inchi_fgetsLf_V3000( line, inp_file );*/ + nc = get_V3000_input_line_to_strbuf(pin, inp_file); + if ( nc<1 ) p = NULL; + else p = line = pin->pStr; + if ( !p || strcmp(p, "BEGIN ATOM") ) + { + TREAT_ERR_AND_FIN (err, 1, err_fin, "Error: No V3000 Atom block start marker"); + } + remove_one_lf( line ); + + ctab->v3000->n_non_star_atoms = 0; + for ( i = 0; i < ctab->n_atoms; i++ ) + { + int ii = -1; + + /*p = inchi_fgetsLf_V3000( line, inp_file );*/ + inchi_strbuf_reset( pin ); + nc = get_V3000_input_line_to_strbuf(pin, inp_file); + if ( nc<1 ) p = NULL; + else p = line = pin->pStr; + if ( !p ) + { + if ( !err ) + { + TREAT_ERR( err, 2, "Cannot read V3000 atom block line"); + } + break; + } + remove_one_lf( line ); + + if ( err ) + { + if ( !strcmp( line, SD_FMT_END_OF_DATA ) ) + { + err = -abs(err); + break; + } + continue; /* bypass the rest of the Atom block */ + } + + if ( ctab->atoms ) + { + int index, aamap; /* not used actually, just read them */ + int len; + char symbol[6]; /* TODO: treat possibly long V3000 atom names */ + double fx, fy, fz; + + + /* Read positional parameters */ + int failed = 0; + if ( 0 > MolfileV3000ReadField( &index,MOL_FMT_INT_DATA, &p ) ) + failed = 1; + else if ( 0 > MolfileV3000ReadField( &symbol, MOL_FMT_STRING_DATA, &p) ) + failed = 1; + else if ( 0 > MolfileV3000ReadField( &fx, MOL_FMT_DOUBLE_DATA, &p ) ) + failed = 1; + else if ( 0 > MolfileV3000ReadField( &fy, MOL_FMT_DOUBLE_DATA, &p ) ) + failed = 1; + else if ( 0 > MolfileV3000ReadField( &fz, MOL_FMT_DOUBLE_DATA, &p ) ) + failed = 1; + else if ( 0 > MolfileV3000ReadField( &aamap ,MOL_FMT_INT_DATA, &p ) ) + failed = 1; + + if ( failed ) + { + + err = 4; + TREAT_ERR( err, 4, "Cannot interpret V3000 atom block line:"); + dotify_non_printable_chars( line ); + AddErrorMessage(pStrErr, line); + + if ( !strcmp( line, SD_FMT_END_OF_DATA ) ) + { + err = -abs(err); + break; + } + continue; /* can't interpret a first half of atom block line */ + } + + + if ( !strcmp(symbol, "*" ) ) + { + /* ignore star atoms but save index info */ + ctab->v3000->atom_index_orig[i] = index; + ctab->v3000->atom_index_fin[i] = -1; + ctab->v3000->n_star_atoms++; + continue; + } + + ctab->v3000->n_non_star_atoms++; + ctab->v3000->atom_index_orig[i] = index; + ctab->v3000->atom_index_fin[i] = ctab->v3000->n_non_star_atoms; + ii = ctab->v3000->n_non_star_atoms - 1; + + mystrncpy( ctab->atoms[ii].symbol, symbol, sizeof(ctab->atoms[ii].symbol) ); + if ( 2 == strlen(ctab->atoms[ii].symbol) && isupper(UCINT ctab->atoms[ii].symbol[1])) + { + ctab->atoms[ii].symbol[1] = (char)tolower(UCINT ctab->atoms[ii].symbol[1]); /* 5-4-99 DCh*/ + } + ctab->atoms[ii].fx = fx; + ctab->atoms[ii].fy = fy; + ctab->atoms[ii].fz = fz; + + + /* Read key-val pairs if any */ + while ( p && (len=MolfileV3000ReadKeyword(field, &p)) > 0 ) + { + + int itmp; + char ctmp; + char stmp[MOL_FMT_V3000_MAXFIELDLEN]; + + int failed = 0; + + if ( !strcmp(field, "CHG") ) + { + if ( 0 > MolfileV3000ReadField( &ctab->atoms[ii].charge, MOL_FMT_CHAR_INT_DATA, &p)) + failed = 1; + } + else if ( !strcmp(field, "RAD") ) + { + if ( 0 > MolfileV3000ReadField( &ctab->atoms[ii].radical, MOL_FMT_CHAR_INT_DATA, &p)) + failed = 1; + } + else if ( !strcmp(field, "CFG") ) + { + if ( 0 > MolfileV3000ReadField( &ctab->atoms[ii].stereo_parity, MOL_FMT_CHAR_INT_DATA, &p)) + failed = 1; + } + + else if ( !strcmp(field, "MASS") ) + { + /* + Default = natural abundance + A specified value indicates the absolute + atomic weight of the designated atom. + */ + S_SHORT iso_mass; + if ( 0 > MolfileV3000ReadField( &iso_mass, MOL_FMT_SHORT_INT_DATA, &p)) + { + failed = 1; + TREAT_ERR( err, 0, "Isotopic data not recognized:"); + AddErrorMessage(pStrErr, line); + /* ignore isotopic error for now */ + } + else + { + /* What we read is an absolute isotopic mass, by V3000 spec. + Adjust this to old convention for further processing: + set 'ctab->atoms[ii].mass_difference' to + 127 if isotopic mass is the same as element mass + in Periodic Table (rounded avg by all isotopes), 'atw' + delta otherwise, the value of difference 'delta' = ( isotopic mass - 'atw') + */ + int atw, delta; + atw = get_atomic_mass( ctab->atoms[ii].symbol ); + delta = (int) iso_mass - atw ; + ctab->atoms[ii].mass_difference = (char) ( delta ? delta : ZERO_ATW_DIFF); + } + } + + else if ( !strcmp(field, "VAL") ) + { + if ( 0 > MolfileV3000ReadField( &itmp, MOL_FMT_INT_DATA, &p)) + failed = 1; + else + { + /* adjust to old convention: was 15 for zero, now -1 for zero */ + if ( itmp == -1 ) + ctmp = 15; + else + ctmp = ( char ) itmp; + ctab->atoms[ii].valence = ctmp; + } + } + else if ( !strcmp(field, "HCOUNT") ) + { + if ( 0 > MolfileV3000ReadField( &itmp, MOL_FMT_INT_DATA, &p)) + ; /* skip query-related stuff */ + } + else if ( !strcmp(field, "STBOX") ) + { + if ( 0 > MolfileV3000ReadField( &itmp, MOL_FMT_INT_DATA, &p)) + ; /* skip for now */ + } + else if ( !strcmp(field, "INVRET") || !strcmp(field, "EXACHG") ) + { + if ( 0 > MolfileV3000ReadField( &itmp, MOL_FMT_INT_DATA, &p)) + ; /* skip reaction-related stuff */ + } + else if ( !strcmp(field, "SUBST") || !strcmp(field, "UNSAT") || !strcmp(field, "RBCNT") ) + { + if ( 0 > MolfileV3000ReadField( &itmp, MOL_FMT_INT_DATA, &p)) + ; ; /* skip query-related stuff */ + } + else if ( !strcmp(field, "ATTCHPT") ) + { + if ( 0 > MolfileV3000ReadField( &itmp, MOL_FMT_INT_DATA, &p)) + ; + } + else if ( !strcmp(field, "RGROUPS") ) + { + if ( 0 > MolfileV3000ReadField( &stmp, MOL_FMT_STRING_DATA, &p)) + ; + } + else if ( !strcmp(field, "ATTCHORD") ) + { + if ( 0 > MolfileV3000ReadField( &stmp, MOL_FMT_STRING_DATA, &p)) + ; + } + else if ( !strcmp(field, "CLASS") ) + { + if ( 0 > MolfileV3000ReadField( &stmp, MOL_FMT_STRING_DATA, &p)) + ; + } + else if ( !strcmp(field, "SEQID") ) + { + if ( 0 > MolfileV3000ReadField( &itmp, MOL_FMT_INT_DATA, &p)) + ; + } + + + if ( failed ) + { + err = 4; + TREAT_ERR( err, 4, "Cannot interpret V3000 atom block key-value pair"); + dotify_non_printable_chars( line ); + AddErrorMessage(pStrErr, line); + + if ( !strcmp( line, SD_FMT_END_OF_DATA ) ) + { + err = -abs(err); + break; + } + continue; + } + } + } /* if ( NULL != ctab->atoms ) */ + } /* for ( i = 0; i < ctab->n_atoms; i++ ) */ + + if ( ctab->v3000->n_star_atoms ) + { + AddErrorMessage(pStrErr, "V3000 star atoms ignored"); + ctab->n_atoms = ctab->v3000->n_non_star_atoms; + } + + /* Check for proper finish */ + + /*p = inchi_fgetsLf_V3000( line, inp_file );*/ + inchi_strbuf_reset( pin ); + nc = get_V3000_input_line_to_strbuf(pin, inp_file); + if ( nc<1 ) p = NULL; + else p = line = pin->pStr; + if ( !p || strcmp(p, "END ATOM") ) + { + TREAT_ERR_AND_FIN (err, 1, err_fin, "Error: No V3000 Atom block end marker"); + } + remove_one_lf( line ); + +err_fin: + return err; +} + + +/* + Read V3000 bonds +*/ +int MolfileV3000ReadBondsBlock( MOL_FMT_CTAB* ctab, + INCHI_IOSTREAM *inp_file, + int err, + char *pStrErr ) +{ +int i; +char field[MOL_FMT_V3000_MAXFIELDLEN]; +int nc; +char *p=NULL, *line = NULL; +INCHI_IOSTREAM tmpin; +INCHI_IOSTREAM_STRING *pin= &tmpin.s; +inchi_ios_init( &tmpin, INCHI_IOSTREAM_TYPE_STRING, NULL ); + + /* Check for proper start */ + + /*p = inchi_fgetsLf_V3000( line, inp_file );*/ + nc = get_V3000_input_line_to_strbuf(pin, inp_file); + if ( nc<1 ) p = NULL; + else p = line = pin->pStr; + if ( !p || strcmp(p, "BEGIN BOND") ) + { + TREAT_ERR_AND_FIN (err, 1, err_fin, "Error: No V3000 Bond block start marker"); + } + remove_one_lf( line ); + + ctab->v3000->n_haptic_bonds = 0; + ctab->v3000->n_non_haptic_bonds = 0; + + for ( i = 0; i < ctab->n_bonds; i++ ) + { + int is_haptic = 0; + + /*p = inchi_fgetsLf_V3000( line, inp_file );*/ + inchi_strbuf_reset( pin ); + nc = get_V3000_input_line_to_strbuf(pin, inp_file); + if ( nc<1 ) p = NULL; + else p = line = pin->pStr; + if ( !p ) + { + if ( !err ) + { + TREAT_ERR( err, 2, "Cannot read V3000 bond block line"); + } + break; + } + remove_one_lf( line ); + + if ( err ) + { + if ( !strcmp( line, SD_FMT_END_OF_DATA ) ) + { + err = -abs(err); + break; + } + continue; + } + + + if ( ctab->bonds ) + { + int index, n_orig_at, len; + short int atnum1=-1, atnum2=-1; + char bond_type=0, stereo=0; + int failed = 0; + int has_non_existent_atom = 0; + + n_orig_at = ctab->v3000->n_non_star_atoms + ctab->v3000->n_star_atoms; + + /* read positional parameters */ + + if ( 0 > MolfileV3000ReadField( &index,MOL_FMT_INT_DATA, &p ) ) + failed = 1; + else if ( 0 > MolfileV3000ReadField( &bond_type, MOL_FMT_CHAR_INT_DATA, &p ) ) + failed = 1; + else if ( 0 > MolfileV3000ReadField( &atnum1, MOL_FMT_SHORT_INT_DATA, &p ) ) + failed = 1; + else if ( 0 > MolfileV3000ReadField( &atnum2, MOL_FMT_SHORT_INT_DATA, &p ) ) + failed = 1; + + atnum1 = + get_actual_atom_number( atnum1, + n_orig_at, + ctab->v3000->atom_index_orig, + ctab->v3000->atom_index_fin ); + + atnum2 = + get_actual_atom_number( atnum2, + n_orig_at, + ctab->v3000->atom_index_orig, + ctab->v3000->atom_index_fin ); + + if ( (atnum1 < 0) && (atnum2 < 0) ) + failed = 1; + else if ( atnum1 < 0 || atnum2 < 0 ) + has_non_existent_atom = 1; + + if ( failed ) + { + + if ( !err ) + { + /* can't interpret bonds block line */ + TREAT_ERR( err, 4, "Cannot interpret V3000 bond block line:"); + dotify_non_printable_chars( line ); + AddErrorMessage(pStrErr, line); + } + if ( !strcmp( line, SD_FMT_END_OF_DATA ) ) + { + err = -abs(err); + break; + } + } + + + /* TODO: treat new bond types 9 10 */ + /* read key-val pairs if any */ + while ( p && (len=MolfileV3000ReadKeyword(field, &p)) > 0 ) + { + + int itmp; + char stmp[MOL_FMT_V3000_MAXFIELDLEN]; + + int failed = 0; + + if ( !strcmp(field, "CFG") ) + { + if ( 0 > MolfileV3000ReadField( &stereo, MOL_FMT_CHAR_INT_DATA, &p)) + { + failed = 1; + } + else + { + /* adjust stereo to old convention for wedges which was: + 0 = not stereo, 1 = Up, 4 = Either, 6 = Down + now: + 0 = none (default), 1 = up, 2 = either, 3 = down + */ + if ( stereo == 2 ) + stereo = 4; + else if ( stereo == 3 ) + stereo = 6; + } + } + else if ( !strcmp(field, "TOPO") ) + { + if ( 0 > MolfileV3000ReadField( &itmp, MOL_FMT_INT_DATA, &p)) + ; /* skip query-related stuff */ + } + else if ( !strcmp(field, "RXCTR") ) + { + if ( 0 > MolfileV3000ReadField( &itmp, MOL_FMT_INT_DATA, &p)) + ; /* skip reaction-related stuff */ + } + else if ( !strcmp(field, "STBOX") ) + { + if ( 0 > MolfileV3000ReadField( &itmp, MOL_FMT_INT_DATA, &p)) + ; /* skip for now */ + } + else if ( !strcmp(field, "ENDPTS") ) + { + int res, *num_list = NULL; + if ( 0 > MolfileV3000ReadHapticBond( ctab, &p, &num_list, pStrErr)) + failed = 1; + else if ( !num_list ) + failed = 1; + else + { + int existent_atom = atnum1; + if ( existent_atom < 0 ) + existent_atom = atnum2; + + if ( existent_atom < 0 ) /* should not be here */ + { + failed = 1; + } + else + { + int k, nnum; + nnum = num_list[2]; + num_list[1] = existent_atom; + for (k=3; kv3000->atom_index_orig, + ctab->v3000->atom_index_fin ); + } + res = NumLists_Append( ctab->v3000->haptic_bonds, num_list ); + if ( res < 0 ) + { + failed = 1; + } + else + { + is_haptic = 1; + } + } + } + } + else if ( !strcmp(field, "DISP") ) + { + if ( 0 > MolfileV3000ReadField( &stmp, MOL_FMT_STRING_DATA, &p)) + ; + } + else if ( !strcmp(field, "ATTACH") ) + { + if ( 0 > MolfileV3000ReadField( &stmp, MOL_FMT_STRING_DATA, &p)) + ; + } + + + if ( failed ) + { + if ( !err ) + { + /* can't interpret bonds block line */ + TREAT_ERR( err, 4, "Cannot interpret V3000 bond block line:"); + dotify_non_printable_chars( line ); + AddErrorMessage(pStrErr, line); + } + if ( !strcmp( line, SD_FMT_END_OF_DATA ) ) + { + err = -abs(err); + break; + } + } + } /* while ( p && (len=MolfileV3000ReadKeyword(field, &p)) > 0 ) */ + + + if ( is_haptic ) + { + int ii = ctab->v3000->n_haptic_bonds; + ctab->v3000->haptic_bonds->lists[ii][0] = bond_type; + ctab->v3000->n_haptic_bonds++; + continue; + } + else + { + int ii = ctab->v3000->n_non_haptic_bonds; + ctab->bonds[ii].atnum1 = atnum1; + ctab->bonds[ii].atnum2 = atnum2; + ctab->bonds[ii].bond_type = bond_type; + ctab->bonds[ii].bond_stereo = stereo; + ctab->v3000->n_non_haptic_bonds++; + } + } /* if ctab->bonds */ + } /* for ( i = 0; i < ctab->n_bonds; i++ ) */ + + + if ( ctab->v3000->n_haptic_bonds ) + { + AddErrorMessage(pStrErr, "V3000 haptic bonds read/stored but ignored"); + ctab->n_bonds = ctab->v3000->n_non_haptic_bonds; + } + + + /* Check for proper finish */ + + /*p = inchi_fgetsLf_V3000( line, inp_file );*/ + inchi_strbuf_reset( pin ); + nc = get_V3000_input_line_to_strbuf(pin, inp_file); + if ( nc<1 ) p = NULL; + else p = line = pin->pStr; + if ( !p || strcmp(p, "END BOND") ) + { + TREAT_ERR_AND_FIN (err, 1, err_fin, "Error: No V3000 Bond block end marker"); + } + + remove_one_lf( line ); + +err_fin: + return err; +} + + +/* + Convert atom index to the final consequitive atom number (starting from 1) + Returns -1 for star atom or not found index +*/ +int get_actual_atom_number( int index, int n, int *orig, int *fin ) +{ + int i; + for (i=0; ipStr; + remove_one_lf( line ); + + if ( p && !strcmp(p, "BEGIN SGROUP") ) + { + retcode = MolfileV3000ReadSGroup( ctab, inp_file, retcode, pStrErr ); + if ( retcode ) + { + retcode+= 70; + TREAT_ERR_AND_FIN (retcode, 1, err_fin, pStrErr ); + } + /*p = inchi_fgetsLf_V3000( line, inp_file );*/ + inchi_strbuf_reset( pin ); + nc = get_V3000_input_line_to_strbuf(pin, inp_file); + if ( nc<1 ) p = NULL; + else p = line = pin->pStr; + remove_one_lf( line ); + } + + if ( p && !strcmp(p, "BEGIN OBJ3D") ) + { + retcode = MolfileV3000Read3DBlock( ctab, inp_file, retcode, pStrErr ); + if ( retcode ) + { + retcode+= 70; + TREAT_ERR_AND_FIN (retcode, 1, err_fin, pStrErr ); + } + /*p = inchi_fgetsLf_V3000( line, inp_file );*/ + inchi_strbuf_reset( pin ); + nc = get_V3000_input_line_to_strbuf(pin, inp_file); + if ( nc<1 ) p = NULL; + else p = line = pin->pStr; + remove_one_lf( line ); + } + + while ( p && !strcmp(p, "LINKNODE") ) + { + /* skip for now */ + /*p = inchi_fgetsLf_V3000( line, inp_file );*/ + inchi_strbuf_reset( pin ); + nc = get_V3000_input_line_to_strbuf(pin, inp_file); + if ( nc<1 ) p = NULL; + else p = line = pin->pStr; + remove_one_lf( line ); + } + + /* Collections */ + while ( p && !strcmp(p, "BEGIN COLLECTION") ) + { + retcode = MolfileV3000ReadCollections( ctab, inp_file, retcode, pStrErr ); + if ( retcode ) + { + retcode+= 70; + TREAT_ERR_AND_FIN (retcode, 1, err_fin, pStrErr ); + } + /*p = inchi_fgetsLf_V3000( line, inp_file );*/ + inchi_strbuf_reset( pin ); + nc = get_V3000_input_line_to_strbuf(pin, inp_file); + if ( nc<1 ) p = NULL; + else p = line = pin->pStr; + remove_one_lf( line ); + } + + if ( !p || strcmp(p, "END CTAB") ) + { + TREAT_ERR_AND_FIN (err, 1, err_fin, "Error: No V3000 CTAB end marker"); + } + + remove_one_lf( line ); + + +err_fin: + return err; +} + + +/* + Read haptic bond info +*/ +int MolfileV3000ReadHapticBond( MOL_FMT_CTAB* ctab, + char** line_ptr, + int **num_list, + char *pStrErr ) +{ +int nread = 0; +char field[MOL_FMT_V3000_MAXFIELDLEN]; +const int max_field_len=sizeof(field); +char *p_end; +int i, nnum = 0; + + *num_list = NULL; + + memset( field, 0, max_field_len ); + + nread = read_upto_delim( line_ptr, field, max_field_len, "1234567890 \t\n\v\f\r" ); + if ( strcmp(field,"(") ) + return -1; + + nread = read_upto_delim( line_ptr, field, max_field_len, " \t\n\v\f\r" ); + + nnum = strtol( field, &p_end, 10 ); + + if ( p_end==field ) + return -1; /* paranoia */ + if ( nnum < 0 ) + return -1; + + + *num_list = (int *) calloc( nnum + 3 , sizeof(int) ); + + + if ( !*num_list ) + { + nread = -1; + goto ret; + } + + (*num_list)[0] = -1; /* will be bond type, to be filled by caller */ + (*num_list)[1] = -1; /* will be atom number, to be filled by caller */ + (*num_list)[2] = nnum; + + for ( i=3; i MolfileV3000ReadField( &((*num_list)[i]), MOL_FMT_INT_DATA, line_ptr ) ) + { + nread = -1; + goto ret; + } + } + + + /* ')' should have been consumed by strtol */ + + /* check for ATTACH=ALL */ + + nread = read_upto_delim( line_ptr, field, max_field_len, " \t\n\v\f\r" ); + + if ( nread > 0 ) + if ( strcmp( field, "ATTACH=ALL" ) ) + { + nread = -1; + goto ret; + } + + +ret:if ( nread < 0 ) + { + if ( *num_list ) + { + inchi_free( *num_list ); + *num_list = NULL; + } + } + return nread; +} + + +/* + Read V3000 stereo collection +*/ +int MolfileV3000ReadStereoCollection( MOL_FMT_CTAB* ctab, + char** line_ptr, + int **num_list, + char *pStrErr) +{ +int nread = 0; +char field[MOL_FMT_V3000_MAXFIELDLEN]; +const int max_field_len=sizeof(field); +char *p_end; +int i, nnum = 0; + + + *num_list = NULL; + + memset( field, 0, max_field_len ); + + nread = read_upto_delim( line_ptr, field, max_field_len, "1234567890 \t\n\v\f\r" ); + if ( strcmp(field,"(") ) + return -1; + + nread = read_upto_delim( line_ptr, field, max_field_len, " \t\n\v\f\r" ); + + nnum = strtol( field, &p_end, 10 ); + + if ( p_end==field ) + return -1; /* paranoia */ + if ( nnum < 0 ) + return -1; + + + *num_list = (int *) calloc( nnum + 3 , sizeof(int) ); + + + if ( !*num_list ) + { + nread = -1; + goto ret; + } + + (*num_list)[0] = -1; /* reserved, may be filled by caller */ + (*num_list)[1] = nnum; + + for ( i=2; i MolfileV3000ReadField( &((*num_list)[i]), MOL_FMT_INT_DATA, line_ptr ) ) + { + nread = -1; + goto ret; + } + } + + + /* ')' should have been consumed by strtol */ + + +ret:if ( nread < 0 ) + { + if ( *num_list ) + { + inchi_free( *num_list ); + *num_list = NULL; + } + } + return nread; +} + + +/* + Returns + -1 @ error +*/ +int get_V3000_input_line_to_strbuf( INCHI_IOSTREAM_STRING *buf, INCHI_IOSTREAM* inp_stream ) +{ + int old_used, crlf2lf = 1, preserve_lf = 0; + + inchi_strbuf_reset( buf ); + + old_used = buf->nUsedLength; + while ( 1 ) + { + inchi_strbuf_addline( buf, inp_stream, crlf2lf, preserve_lf ); + + if ( buf->nUsedLength - old_used < 8 ) + return -1; + if ( strncmp( buf->pStr + old_used, "M V30 ", 7) ) + return -1; + + memmove( (void*) (buf->pStr+old_used), (void*) (buf->pStr+old_used+7), buf->nUsedLength - old_used + 1 ); + buf->nUsedLength-= 7; + + if ( buf->pStr[buf->nUsedLength-1] != '-' ) + break; + buf->pStr[--buf->nUsedLength] = '\0'; + + old_used = buf->nUsedLength; + } + + + + +#if 0 + p = check and normalize ( p ) + + pp = empty + while ( p is proper ) + { + + pp = pp + p + + if pp ends with '-' + /* expect continuation to next line */ + in pp, replace trail '-' with space + else + break + p = get new p + } + + if pp is empty + return NULL + else + return pp + + + if ( !p ) + return NULL; + + while ( p ) + { + len = strlen(p); + if ( len<7 ) break; + if ( strncmp( p, "M V30 ", 7) ) break; + ITRACE_( "\n\nV3000 line, original \'%-s\'", p ); + p+= 7; + len = normalize_string( p ); + ITRACE_( "\nV3000 line, normalized \'%-s\'", p ); + //pp = pp + p; + break; + } + +#endif + + return buf->nUsedLength; +} diff --git a/INCHI-1-SRC/INCHI_BASE/src/mol_fmt4.c b/INCHI-1-SRC/INCHI_BASE/src/mol_fmt4.c new file mode 100644 index 0000000..d3a272b --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/mol_fmt4.c @@ -0,0 +1,1736 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + +#include +#include +#include +#include +#include +#include + +#include "mode.h" +#include "mol_fmt.h" + +#include "ichierr.h" +#include "util.h" +#include "ichi_io.h" +#include "ichimain.h" + +/* + SDFile related procedures + +*/ + +#define ALIASED_AT(i) (0 < NUM_ISO_H(at, i)) +#define IS_DEUTERIUM(i) (!strcmp( at[i].elname, "D" ) || at[i].iso_atw_diff == 2 && !strcmp( at[i].elname, "H" )) +#define IS_TRITIUM(i) (!strcmp( at[i].elname, "T" ) || at[i].iso_atw_diff == 3 && !strcmp( at[i].elname, "H" )) + +#define ABNORMAL_ISO(i) (at[i].iso_atw_diff == 1 || at[i].iso_atw_diff < -3 || at[i].iso_atw_diff > 5 ) +#define ABNORMAL_CHG(i) (abs(at[i].charge) > 3) +#define ABNORMAL_RAD(i) (RADICAL_SINGLET <= at[i].radical && at[i].radical <= RADICAL_TRIPLET ) + +#define ANY_ISO(i, X) ((X)? (at[i].iso_atw_diff && !IS_DEUTERIUM(i) && !IS_TRITIUM(i)) :\ + (at[i].iso_atw_diff || IS_DEUTERIUM(i) || IS_TRITIUM(i))) +#define ANY_CHG(i) (0 != at[i].charge) +#define ANY_RAD(i) (RADICAL_SINGLET <= at[i].radical && at[i].radical <= RADICAL_TRIPLET ) + +#define NORMAL_ISO(i, X) (ANY_ISO(i, X) && !ABNORMAL_ISO(i)) + +/* needs additional M CHG. M RAD, M ISO line */ +/* due to ISIS/Draw feature always include M RAD for any radical */ +#define ABNORMAL_AT(i) ( at[i].radical || abs(at[i].charge) > 3 || \ + ABNORMAL_ISO(i) ) + +/* always add M ISO, M RAD, M CHG; Except: (bAtomsDT && D or T) */ +#define ADD_LINE_AT(i) ( at[i].charge || \ + at[i].radical || \ + at[i].iso_atw_diff && (bAtomsDT? (at[i].iso_atw_diff != 1 || strcmp(at[i].elname, "H")) : 1) ) + +/* Local */ + +static const char sdf_data_hdr_name[] = "NAME"; +static const char sdf_data_hdr_comm[] = "COMMENT"; + +enum { SDF_START, SDF_DATA_HEADER, SDF_DATA_HEADER_NAME + , SDF_DATA_HEADER_COMMENT, SDF_DATA_HEADER_CAS + , SDF_DATA_HEADER_USER, SDF_DATA_LINE + , SD_FMT_END_OF_DATA_ITEM, SDF_EMPTY_LINE, SD_FMT_END_OF_DATA_BLOCK }; + +int OrigAtData_WriteToSDfileHeaderAndCountThings( const ORIG_ATOM_DATA *inp_at_data, + INCHI_IOSTREAM * fcb, + const char* name, + const char* comment, + int bChiralFlag, + int bAtomsDT, + const char *szLabel, + const char *szValue, + int *nNumAliasLines, + int *nNumChargeLines, + int *nNumRadicalLines, + int *nNumIsoLines, + int *nNumAddLines, + int *num_bonds ); +int OrigAtData_WriteToSDfileAtomsBlock( const ORIG_ATOM_DATA *inp_at_data, + INCHI_IOSTREAM * fcb, + const char* name, + const char* comment, + int bAtomsDT, + const char *szLabel, + const char *szValue); + +int OrigAtData_WriteToSDfileBondsBlock( const ORIG_ATOM_DATA *inp_at_data, + INCHI_IOSTREAM * fcb, + const char* name, + const char* comment, + const char *szLabel, + const char *szValue, + INT_ARRAY *written_bond_ends ); + +int OrigAtData_WriteToSDfileAddLines( const ORIG_ATOM_DATA *inp_at_data, + INCHI_IOSTREAM * fcb, + const char* name, + const char* comment, + int bAtomsDT, + const char *szLabel, + const char *szValue, + int nNumAliasLines, + int nNumChargeLines, + int nNumRadicalLines, + int nNumIsoLines, + INT_ARRAY *written_bond_ends ); + +int OrigAtData_WriteToSDfileOrigAtDataPolymer( const ORIG_ATOM_DATA *inp_at_data, + INCHI_IOSTREAM * fcb, + const char* name, + const char* comment, + const char *szLabel, + const char *szValue, + INT_ARRAY *written_bond_ends ); + +static int compare_ranks_of_1st_and_2nd(int atom1, int atom2, OrigAtDataPolymerAtomProps *aprops ); +static int ranks_increase_from_1st_to_2nd(int a1, int a2, OrigAtDataPolymerAtomProps *aprops ); + +/* + Skip extra data ( != Molfile) which SDF contains +*/ +int SDFileSkipExtraData( INCHI_IOSTREAM *inp_file, + long *CAS_num, + char* comment, + int lcomment, + char *name, + int lname, + int prev_err, + const char *pSdfLabel, + char *pSdfValue, + char *pStrErr ) +{ +char* p = NULL; +char line[MOL_FMT_INPLINELEN]; +const int line_len=sizeof(line); +int n_blank_lines=0, n_lines=0; +int current_state=SDF_START; +int err=0; +int wait_for_CAS=0; +int CAS_num_is_user=0; +int wait_for_name = name && lname > 0 && !name[0]; +int wait_for_comment = comment && lcomment > 0 && !comment[0]; +int wait_for_user = pSdfLabel && pSdfLabel[0] && pSdfValue; + + if ( CAS_num != NULL ) + { + wait_for_CAS = 1; + *CAS_num = 0; + CAS_num_is_user = (wait_for_user && !inchi_memicmp(pSdfLabel,"CAS", 3)); + } + + while ( !err && + current_state != SD_FMT_END_OF_DATA_BLOCK && + NULL != (p=inchi_fgetsLf(line, line_len, inp_file)) ) + { + if ( !n_lines && !memcmp(line, "M END", 6) ) + { + /* allow subtle errors */ + continue; + } + + n_lines++; + remove_trailing_spaces( line ); + + if ( line[MOL_FMT_MAXLINELEN] ) + { + if ( current_state!=SDF_DATA_HEADER && + current_state!=SDF_DATA_LINE && + current_state!=SDF_DATA_HEADER_NAME && + current_state!=SDF_DATA_HEADER_USER && + current_state!=SDF_DATA_HEADER_COMMENT ) + { + line[MOL_FMT_MAXLINELEN] = '\0'; + if ( !prev_err ) + { + TREAT_ERR( err, 0, "Too long SData line truncated"); + } + } + else + { + /* allow long lines in SDF data. 9-29-00 DCh */ + line[MOL_FMT_MAXLINELEN] = '\0'; + } + } + + n_blank_lines += ( *line == '\0' ); + + switch( current_state ) + { + case SDF_START: + case SD_FMT_END_OF_DATA_ITEM: + case SDF_EMPTY_LINE: /* Added 9-25-97 DCh */ + + if ( !strcmp( line, SD_FMT_END_OF_DATA ) ) + { + current_state = SD_FMT_END_OF_DATA_BLOCK; + } + else if ( '>' == *line ) + { + current_state = ( wait_for_name || wait_for_comment || wait_for_CAS || wait_for_user )? SDFileIdentifyLabel(line, pSdfLabel) : SDF_DATA_HEADER; + } + else if ( *line == '\0' ) { /* Added 9-25-97 DCh */ + /* Relax the strictness: Allow more than 1 empty line. */ + current_state = SDF_EMPTY_LINE; + } + else if ( !prev_err ) + { + TREAT_ERR( err, 3, "Unexpected SData header line:"); + dotify_non_printable_chars( line ); + AddErrorMessage(pStrErr, line); + /* unexpected contents of data header line */ + } + else + { + err = 3; + } + break; + + case SDF_DATA_HEADER_NAME: + + if ( wait_for_name && 0 < normalize_string( line ) ) + { + wait_for_name = 0; + mystrncpy( name, line, lname ); + } + goto got_data_line; + + case SDF_DATA_HEADER_COMMENT: + + if ( wait_for_comment && 0 < normalize_string( line ) ) + { + wait_for_comment = 0; + mystrncpy( comment, line, lcomment ); + } + goto got_data_line; + + case SDF_DATA_HEADER_USER: + + if ( wait_for_user && 0 < normalize_string( line ) ) + { + wait_for_user = 0; + mystrncpy( pSdfValue, line, MAX_SDF_VALUE+1 ); + + if ( CAS_num_is_user && wait_for_CAS ) + { + *CAS_num = SDFileExtractCASNo( line ); + wait_for_CAS = (0 == *CAS_num); + } + } + goto got_data_line; + + case SDF_DATA_HEADER_CAS: + + if ( wait_for_CAS && 0 < normalize_string( line ) ) + { + *CAS_num = SDFileExtractCASNo( line ); + wait_for_CAS = (0 == *CAS_num); + } + goto got_data_line; + + case SDF_DATA_HEADER: + case SDF_DATA_LINE: + +got_data_line: + current_state = *line? SDF_DATA_LINE : SD_FMT_END_OF_DATA_ITEM; + break; + } + } + + if ( !err && SD_FMT_END_OF_DATA_BLOCK!=current_state && NULL==p ) + ; + /* err = 4; */ /* unexpected end of file: missing $$$$ */ + + else if ( err && ( n_blank_lines == n_lines && *line == '\0' ) ) + /* empty lines -- do not know when this can happen */ + err = 5; + + if ( err && err != 5 && current_state != SD_FMT_END_OF_DATA_BLOCK && p ) + { + /* bypass up to $$$$ */ + while ( (p=inchi_fgetsLf(line, line_len, inp_file)) && + memcmp(line, SD_FMT_END_OF_DATA, 4) ) + ; + if ( p ) + { + /* arrived to $$$$; non-fatal */ + err = 9; + WarningMessage(pStrErr, "Bypassing to next structure"); + } + } + + return err; +} + +/* + SDFileIdentifyLabel +*/ +int SDFileIdentifyLabel( char* inp_line, const char *pSdfLabel ) +{ +char line[MOL_FMT_MAXLINELEN]; +char *p, *q; +int i, j, len; + + if ( (p = strchr( inp_line, '<' )) && + (q = strchr( p, '>' )) && + (len = q-p-1) > 0 && len < (int)sizeof(line) ) + { + memcpy( line, p+1, len ); + line[len] = '\0'; + + for ( i = 0; isspace( UCINT line[i] ); i ++ ) + ; + + for ( j = len-1; j >= i && isspace( UCINT line[i] ); j -- ) + ; + + len = j-i+1; + p = line+i; + + if ( pSdfLabel && pSdfLabel[0] && len == (int)strlen(pSdfLabel) && !inchi_memicmp( p, pSdfLabel, len ) ) + return SDF_DATA_HEADER_USER; + + if ( len == sizeof(sdf_data_hdr_name)-1 && !inchi_memicmp( p, sdf_data_hdr_name, len ) ) + return SDF_DATA_HEADER_NAME; + + if ( len == sizeof(sdf_data_hdr_comm)-1 && !inchi_memicmp( p, sdf_data_hdr_comm, len ) ) + return SDF_DATA_HEADER_COMMENT; + + if ( !inchi_memicmp( p, "CAS", 3 ) ) + return SDF_DATA_HEADER_CAS; + } + + return SDF_DATA_HEADER; +} + +/* + SDFileExtractCASNo +*/ +long SDFileExtractCASNo( char *line ) +{ +int i, j; + + i = line[0] == '-'? 1 : 0; + + for ( j = i; line[i]; i ++ ) + { + if ( isdigit( UCINT line[i] ) ) + { + line[j++] = line[i]; + } + else if ( line[i] != '-' ) + { + break; + } + } + + line[j] = '\0'; + return strtol( line, NULL, 10 ); +} + +/* + NUM_LISTS - dynamically growing array of int lists +*/ +int NumLists_Alloc( NUM_LISTS *num_lists, int nlists ) +{ + if ( num_lists ) + { + if ( num_lists->lists = (int **)inchi_calloc( nlists, sizeof(int*) ) ) + { + num_lists->increment = + num_lists->allocated = nlists; + return 0; /* ok */ + } + } + return -1; /* error */ +} +int NumLists_ReAlloc( NUM_LISTS *num_lists ) +{ + if ( num_lists ) + { + if ( num_lists->lists && num_lists->allocated > 0 && num_lists->increment > 0 ) + { + void *p = num_lists->lists; + if ( num_lists->lists = + (int **)inchi_calloc( num_lists->allocated + num_lists->increment, sizeof(int *) ) ) + { + memcpy( num_lists->lists, p, num_lists->used * sizeof(num_lists->lists[0]) ); + inchi_free( p ); + num_lists->allocated += num_lists->increment; + return 0; /* ok */ + } + } + } + return -1; /* error */ +} +int NumLists_Append( NUM_LISTS *num_lists, int *list ) +{ + if ( num_lists ) + { + if ( num_lists->used + 1 > num_lists->allocated ) + { + /* need to expand buffer */ + if ( NumLists_ReAlloc( num_lists ) ) + { + return -1; /* error */ + } + } + num_lists->lists[num_lists->used++] = list; + return 0; + } + return -1; +} +void NumLists_Free( NUM_LISTS *num_lists) +{ + if ( num_lists ) + { + int i; + for (i=0; iused; i++) + inchi_free( num_lists->lists[i] ); + inchi_free( num_lists->lists ); + memset( num_lists, 0, sizeof(NUM_LISTS) ); + } +} + +/* + INT_ARRAY - dynamically growing array of int +*/ + +/* Allocate new array, return 0 if OK, -1 otherwise */ +int IntArray_Alloc( INT_ARRAY *items, int nitems ) +{ + if ( items->item = (int *) inchi_calloc( nitems, sizeof(int) ) ) + { + items->increment = items->allocated = nitems; + items->used = 0; + return 0; + } + return -1; +} +/* Expand array, return 0 if OK, -1 otherwise */ +int IntArray_ReAlloc( INT_ARRAY *items ) +{ + if ( items ) + { + if ( items->item && items->allocated > 0 && items->increment > 0 ) + { + void *p = items->item; + if ( items->item = + (int *) inchi_calloc( items->allocated + items->increment, sizeof(items->item[0]) ) ) + { + memcpy( items->item, p, items->used * sizeof(items->item[0]) ); + inchi_free( p ); + items->allocated += items->increment; + return 0; + } + } + } + return -1; +} +/* Push new item to the end of array */ +int IntArray_Append( INT_ARRAY *items, int new_item ) +{ + if ( items ) + { + if ( items->used + 1 > items->allocated ) + { + /* need to expand buffer */ + if ( IntArray_ReAlloc( items ) ) + return -1; + } + items->item[items->used++] = new_item; + return 0; + } + return -1; +} +void IntArray_DebugPrint( INT_ARRAY *items) +{ + if ( items ) + { + int i; + if ( items->used > 0 ) + { + for (i=0; iused-1; i++) + { + ITRACE_( "%-d, ", items->item[i] ); + } + ITRACE_( "%-d\n", items->item[items->used-1] ); + } + else + ; /*ITRACE_( "[None]\n");*/ + } +} +void IntArray_Reset( INT_ARRAY *items) +{ + items->used = 0; + return; +} +/* Free memory */ +void IntArray_Free( INT_ARRAY *items) +{ + if ( items ) + { + if ( items->item ) + inchi_free( items->item ); + } + return; +} + +/* + MOL_FMT_SGROUPS - dynamically growing array of pointers to SGroups +*/ + +/* + SGroup +*/ + +/* Allocate new array Sgroup, return 0 if OK, -1 otherwise */ +int MolFmtSgroup_Create( MOL_FMT_SGROUP **sgroup, int id, int type ) +{ + *sgroup = (MOL_FMT_SGROUP *) inchi_calloc( 1, sizeof(MOL_FMT_SGROUP) ); + if ( *sgroup ) + { + if ( IntArray_Alloc( &((*sgroup)->alist), 8 ) || + IntArray_Alloc( &((*sgroup)->blist), 8 ) ) + { + MolFmtSgroup_Free( *sgroup ); + return -1; + } + (*sgroup)->id = id; + (*sgroup)->type = type; + + (*sgroup)->subtype = 0; + (*sgroup)->conn = 0; + (*sgroup)->label = 0; + + return 0; + } + return -1; +} +void MolFmtSgroup_Free( MOL_FMT_SGROUP *sgroup ) +{ + if ( sgroup ) + { + IntArray_Free( &(sgroup->alist) ); + IntArray_Free( &(sgroup->blist) ); + inchi_free( sgroup ); + } +} + +/* + SGroups +*/ + +/* Allocate new array of Sgroups, return 0 if OK, -1 otherwise */ +int MolFmtSgroups_Alloc( MOL_FMT_SGROUPS *sgroups, int nsgroups ) +{ + if ( sgroups ) + { + if ( NULL != (sgroups->group = (MOL_FMT_SGROUP **)inchi_calloc( nsgroups, sizeof(MOL_FMT_SGROUP *) ) ) ) + { + ITRACE_( "\nAllocated sgroups->group at %-p \n", sgroups->group ); + sgroups->increment = sgroups->allocated = nsgroups; + return 0; + } + } + return -1; +} +/* Expand array of Sgroups, return 0 if OK, -1 otherwise */ +int MolFmtSgroups_ReAlloc( MOL_FMT_SGROUPS *sgroups ) +{ + if ( sgroups ) + { + if ( sgroups->group && sgroups->allocated > 0 && sgroups->increment > 0 ) + { + void *p = sgroups->group; + if ( sgroups->group = (MOL_FMT_SGROUP **)inchi_calloc( sgroups->allocated + sgroups->increment, + sizeof(sgroups->group[0]) ) ) + { + memcpy( sgroups->group, p, sgroups->used * sizeof(sgroups->group[0]) ); + inchi_free( p ); + sgroups->allocated += sgroups->increment; + return 0; /* ok */ + } + } + } + return -1; +} +int MolFmtSgroups_Append( MOL_FMT_SGROUPS *sgroups, int id, int type ) +{ + if ( sgroups ) + { + /* Make new Sgroup */ + MOL_FMT_SGROUP *sgroup=NULL; + if ( 0!=MolFmtSgroup_Create( &sgroup, id, type )) + return -1; + /* Add new created Sgroup to Sgroups */ + if ( sgroups->used + 1 > sgroups->allocated ) + { + /* expand buffer */ + if ( MolFmtSgroups_ReAlloc( sgroups ) ) + return -1; /* no RAM */ + } + sgroups->group[sgroups->used++] = sgroup; + + /* + { + int num = sgroups->used-1; + printf("\nCreated/added Sgroup: id=%-d ( num in Sgroups=%-d ) of type=%-d \n", sgroups->group[num]->id, num, sgroups->group[num]->type ); + }*/ + + return 0; + } + return -1; +} +void MolFmtSgroups_Free( MOL_FMT_SGROUPS *sgroups) +{ + if ( sgroups ) + { + int i; + for (i=0; iused; i++) + MolFmtSgroup_Free( sgroups->group[i] ); + + ITRACE_( "\nAbout to free sgroups->group at %-p\n", sgroups->group ); + inchi_free( sgroups->group ); + + memset( sgroups, 0, sizeof(MOL_FMT_SGROUPS) ); + } +} +int MolFmtSgroups_GetIndexBySgroupId( int id, MOL_FMT_SGROUPS *sgroups) +{ + int i; + for (i=0; iused; i++) + if ( sgroups->group[i]->id == id ) + return i; + return -1; +} + +/* + OrigAtData Write To SDfile +*/ +int OrigAtData_WriteToSDfile( const ORIG_ATOM_DATA *inp_at_data, + INCHI_IOSTREAM * fcb, + const char* name, + const char* comment, + int bChiralFlag, + int bAtomsDT, + const char *szLabel, + const char *szValue) +{ + int num_bonds=0, nNumAddLines=0, nNumIsoLines=0, nNumChargeLines=0, + nNumRadicalLines=0, nNumAliasLines=0, ret=0; + + INT_ARRAY written_bond_ends; + + /* if ( inp_at_data->polymer ) + OrigAtData_CheckAndMakePolymerPhaseShifts( (ORIG_ATOM_DATA *) inp_at_data ); */ + + OrigAtData_WriteToSDfileHeaderAndCountThings( (ORIG_ATOM_DATA *) inp_at_data, + fcb, name, comment, + bChiralFlag, bAtomsDT, + szLabel, szValue, + &nNumAliasLines, + &nNumChargeLines, + &nNumRadicalLines, + &nNumIsoLines, + &nNumAddLines, + &num_bonds ); + + if ( IntArray_Alloc( &written_bond_ends, num_bonds?num_bonds:255) ) + { + ret = _IS_ERROR; + goto exitf; + } + + OrigAtData_WriteToSDfileAtomsBlock( inp_at_data, fcb, name, comment, + bAtomsDT, szLabel, szValue); + + OrigAtData_WriteToSDfileBondsBlock( inp_at_data, fcb, name, comment, + szLabel, szValue, &written_bond_ends); + + if ( nNumAddLines ) + OrigAtData_WriteToSDfileAddLines( inp_at_data, fcb, name, comment, + bAtomsDT, szLabel, szValue, + nNumAliasLines,nNumChargeLines, + nNumRadicalLines, nNumIsoLines, + &written_bond_ends ); + + /* Add field with label/ID if applicable and mark the end of record */ + if ( szValue && szValue[0] ) + { + if ( szLabel && szLabel[0] ) + inchi_ios_print_nodisplay( fcb, "> <%s>\n", szLabel ); + else + inchi_ios_print_nodisplay( fcb, "> \n" ); + inchi_ios_print_nodisplay( fcb, " %s\n\n", szValue ); + } + inchi_ios_print_nodisplay(fcb, "$$$$\n"); + +exitf: + IntArray_Free( &written_bond_ends ); + + return ret; +} + +/* + OrigAtData : Write To SDfile : Atoms Block +*/ +int OrigAtData_WriteToSDfileHeaderAndCountThings( const ORIG_ATOM_DATA *inp_at_data, + INCHI_IOSTREAM * fcb, + const char* name, + const char* comment, + int bChiralFlag, + int bAtomsDT, + const char *szLabel, + const char *szValue, + int *nNumAliasLines, + int *nNumChargeLines, + int *nNumRadicalLines, + int *nNumIsoLines, + int *nNumAddLines, + int *num_bonds ) +{ + int i, ret=0; + int bAtomNeedsAlias, + nNumNecessaryIsoLines=0, + nNumNecessaryChgLines=0, + nNumNecessaryRadLines=0; + int num_atoms = inp_at_data->num_inp_atoms; + int bV2000 = SDF_OUTPUT_V2000; + const inp_ATOM *at = inp_at_data->at; + + { + char strLocName[82]; + memset(strLocName, 0, sizeof(strLocName) ); + if ( name && *name ) + { + strncpy( strLocName, name, 80 ); + } + inchi_ios_print_nodisplay( fcb,"%s\n", strLocName ); + } + + /**********************************************************************/ + /** **/ + /** Important: Atoms with alias cannot have charge, radical **/ + /** isotope differences are allowed **/ + /** **/ + /** Atoms with alias cannot be abnormal. **/ + /** **/ + /** Abnormal atoms are atoms which need M CHG, M RAD, M ISO **/ + /** **/ + /** Output aliased atoms if they have implicit D or T **/ + /** **/ + /**********************************************************************/ + +/* F10.5 F12.5 I6 + IIPPPPPPPPMMDDYYHHmmddSSssssssssssEEEEEEEEEEEERRRRRR +inchi_ios_eprint( fcb,"NISTTRANHP09089809272D 1 1.0 0.0 %6ld\n", lEpa);*/ +/*^^^ +inchi_ios_print_nodisplay( fcb," %s v%s SDfile Output \n", INCHI_NAME, INCHI_VERSION); + +Changed 01/10/2009 to conform CTFile specification (by Symyx request)*/ + + inchi_ios_print_nodisplay( fcb, + /* IIPPPPPPPPMMDDYYHHmmddSSssssssssssEEEEEEEEEEEERRRRRR*/ + " InChIV10 \n"); + /*y_fprintf(fcb, " -CPSS- 1213981200n\n");*/ + + { char strLocName[82]; + + memset(strLocName, 0, sizeof(strLocName) ); + if ( comment && *comment ) + { + strncpy( strLocName, comment, 80 ); + } + inchi_ios_print_nodisplay( fcb,"%s\n", strLocName ); + } + + *num_bonds = 0; + for (i=0; i< num_atoms; i++) + (*num_bonds) += at[i].valence; + (*num_bonds) /= 2; + + /*find if we need "M CHG" and "M RAD"*/ + for (i=0; i < num_atoms; i++) + { + if ( bAtomNeedsAlias = ALIASED_AT(i) ) + { + /* has isotopic implicit D or T; ignoring pure 1H */ + (*nNumAliasLines) += 2 * bAtomNeedsAlias; + } + else + { + /* abnormal means atom needs CHG, RAD, or ISO entry */ + /* nNumAddLines += ABNORMAL_AT(i); */ + /* nNumIso += ( 0 == strcmp( at[i].elname, "D" ) || ( 0 == strcmp( at[i].elname, "T" ) || at[i].iso_atw_diff ) ); */ + /* nNumAddIso += at[i].iso_atw_diff && (at[i].iso_atw_diff == 1 || at[i].iso_atw_diff < -3 || at[i].iso_atw_diff > 5 ); */ + nNumNecessaryIsoLines += ABNORMAL_ISO(i); + nNumNecessaryChgLines += ABNORMAL_CHG(i); + nNumNecessaryRadLines += ABNORMAL_RAD(i); + (*nNumIsoLines) += ANY_ISO(i, bAtomsDT); + (*nNumChargeLines) += ANY_CHG(i); + (*nNumRadicalLines) += ANY_RAD(i); + } + } + + *nNumChargeLines = ( *nNumChargeLines + 7 ) / 8; + *nNumRadicalLines = ( *nNumRadicalLines + 7 ) / 8; + *nNumIsoLines = ( *nNumIsoLines + 7 ) / 8; + + if ( !bV2000 ) + { + if ( !nNumNecessaryRadLines && !nNumNecessaryChgLines ) + { + *nNumRadicalLines = 0; + *nNumChargeLines = 0; + } + if ( !nNumNecessaryIsoLines ) + { + *nNumIsoLines = 0; + } + } + + /* recalculate number of added lines */ + *nNumAddLines = *nNumChargeLines + *nNumRadicalLines + *nNumIsoLines + *nNumAliasLines; /* 1 for M END*/ + + if ( *nNumAddLines || bV2000 ) + { + *nNumAddLines += 1; /* add 1 for "M END" line*/ + } + +/* aaabbblllfffcccsssxxxrrrpppiiimmmvvvvvv*/ + + inchi_ios_print_nodisplay(fcb,"%3d%3d 0 0%3d 0 0 0 0 0%3d%s\n", + num_atoms, *num_bonds, bChiralFlag?1:0, *nNumAddLines, *nNumAddLines?" V2000":""); + + return ret; +} + +/* + OrigAtData : Write To SDfile : Atoms Block +*/ +int OrigAtData_WriteToSDfileAtomsBlock( const ORIG_ATOM_DATA *inp_at_data, + INCHI_IOSTREAM * fcb, + const char* name, + const char* comment, + int bAtomsDT, + const char *szLabel, + const char *szValue) +{ + int i, ret=0; + int bAtomNeedsAlias; + int flag_bad_charge=0, flag_bad_iso = 0; + int num_atoms = inp_at_data->num_inp_atoms; + const inp_ATOM *at = inp_at_data->at; + double x, y, z; + + for (i=0; i < num_atoms; i++) + { + char elname[ATOM_EL_LEN] = "\0\0\0\0\0"; + int iso = 0; + int charge = 0; + int valence = 0; + int nIsotopeH = IS_DEUTERIUM(i)? 1 : IS_TRITIUM(i)? 2 : 0; + int bonds_val; + bAtomNeedsAlias = ALIASED_AT(i); + memset( elname, 0, sizeof(elname) ); + + if ( bAtomNeedsAlias ) + { + /* alias */ + strcpy ( elname, "C" ); + } + else + { + /* isotope*/ + if ( nIsotopeH ) + { + strcpy( elname, bAtomsDT? ( nIsotopeH==1? "D" : "T" ) : "H" ); + } + else + { + strncpy ( elname, at[i].elname, sizeof(elname)-1 ); + } + if ( !ABNORMAL_CHG(i) && !ANY_RAD(i) ) + { + /* charge*/ + /* Only atoms without alias can be here*/ + switch ( at[i].charge ) + { + case 3: charge = 1; break; + case 2: charge = 2; break; + case 1: charge = 3; break; + case -1: charge = 5; break; + case -2: charge = 6; break; + case -3: charge = 7; break; + case 0: charge = 0; break; + default: flag_bad_charge = 1; break; + } + } + + /* radical*/ + if ( ANY_RAD(i) && !ANY_CHG(i) ) + { + if ( at[i].radical == RADICAL_DOUBLET ) + { + charge = 4; + } + } + } + + /* allow isotopic shift for aliased atoms */ + if ( NORMAL_ISO(i, bAtomsDT) ) + { + iso = at[i].iso_atw_diff > 0? at[i].iso_atw_diff-1: + at[i].iso_atw_diff < 0? at[i].iso_atw_diff : + nIsotopeH? nIsotopeH : (flag_bad_iso ++, 0); + } + + x = at[i].x; + y = at[i].y; + z = at[i].z; + + /* valence -- set only if needed */ + bonds_val = nBondsValenceInpAt( at+i, NULL, NULL ); + valence=needed_unusual_el_valence( at[i].el_number, at[i].charge, at[i].radical, + at[i].chem_bonds_valence, bonds_val, NUMH(at, i), at[i].valence ); + if ( valence < 0 ) + { + valence = 15; /* means no bonds nor H */ + } + + if ( !strcmp( elname, "Zz" ) ) + strcpy(elname, "*"); + + /*inchi_ios_eprint(fcb,"%10.4f%10.4f%10.4f %-3.3s%2d%3d 0 0 0 0 0 0 0\n",*/ + /* (float)at[i].x, (float)(-at[i].y), fzero, at[i].elname, iso, charge);*/ + /* xxxxxxyyyyyyzzzzzz aaa____ddcccsssnnnbbbvvvrrriiimmmeee */ + inchi_ios_print_nodisplay(fcb,"%10.4f%10.4f%10.4f %-3.3s%2d%3d 0 0%3d 0 0 0 0\n", + x, y, z, elname, (int)iso, (int)charge, valence /* at[i].special*/); + + /* reflect image against x-axis; + when transforming MOLfile back to STDATA in mol_to_stdata(...), + make one more reflection to restore original orientation. + Reason: in MS Search y-axis is directed from top to bottom, + while in MOLfile y-axis goes from bottom to top. + */ + } + + return ret; +} + +/* + OrigAtData : Write To SDfile : Bonds Block +*/ +int OrigAtData_WriteToSDfileBondsBlock( const ORIG_ATOM_DATA *inp_at_data, + INCHI_IOSTREAM * fcb, + const char* name, + const char* comment, + const char *szLabel, + const char *szValue, + INT_ARRAY *written_bond_ends ) +{ + int i, j, k, ret=0; + int num_atoms = inp_at_data->num_inp_atoms; + const inp_ATOM *at = inp_at_data->at; + + /* bonds*/ + for (i=0; i< num_atoms; i++) + { + for (j=0; jnum_inp_atoms; + int is_polymer = inp_at_data && inp_at_data->polymer && inp_at_data->polymer->n > 0 && inp_at_data->polymer->valid; + const inp_ATOM *at = inp_at_data->at; + + /* Aliases. 5-3-99 DCh.*/ + if ( nNumAliasLines ) + { + num_m = 0; + for (i=0; i < num_atoms; i++) + { + if ( ALIASED_AT(i) ) + { + int len; + inchi_ios_print_nodisplay( fcb, "A %d\n", i+1 ); + num_m ++; + len = sprintf( str_m, "%s", at[i].elname ); + + /* add isotopic H to the alias */ + for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) + { + int num_H = at[i].num_iso_H[k] + (k? 0:at[i].num_H); + if ( num_H ) + { + len += sprintf( str_m+len, "%s", k == 0? "H" : k==1? "D" : k==2? "T" : "?" ); + if ( num_H != 1 ) + { + len += sprintf( str_m+len, "%d", num_H ); + } + } + } + + /* Add charge to the Alias */ + if ( at[i].charge) + { + len += sprintf(str_m+len, "%s", at[i].charge>0? "+" : "-"); + if ( 1 < (j=abs(at[i].charge)) ) + { + len += sprintf( str_m+len, "%d", j ); + } + } + + /* Add radical to the Alias */ + if ( at[i].radical == RADICAL_SINGLET ) + { + len += sprintf( str_m+len, "%s", ":" ); + } + else if ( at[i].radical == RADICAL_DOUBLET ) + { + len += sprintf( str_m+len, "%s", "^" ); + } + else if ( at[i].radical == RADICAL_TRIPLET ) + { + len += sprintf( str_m+len, "%s", "^^" ); + } + inchi_ios_print_nodisplay( fcb, "%s\n", str_m ); + num_m ++; + } + } + + if ( num_m != nNumAliasLines ) + { + /* error in lines counting*/ + ret ++; + } + } + + /* charges*/ + str_m[0] = 0; + num_m = 0; + if ( nNumChargeLines ) + { + for (i=0; i < num_atoms; i++) + { + if ( at[i].charge && !ALIASED_AT(i) ) + { + sprintf( entry, " %3d %3d", i+1, (int)at[i].charge ); + strcat( str_m, entry ); + num_m ++; + } + if ( i == num_atoms-1 && num_m || num_m == 8 ) + { + inchi_ios_print_nodisplay( fcb, "M CHG%3d%s\n", num_m, str_m ); + str_m[0] = 0; + num_m = 0; + } + } + } + + /* radicals*/ + str_m[0] = 0; + num_m = 0; + + if ( nNumRadicalLines ) + { + for (i=0; i < num_atoms; i++) + { + if ( at[i].radical && !ALIASED_AT(i) ) + { + int radical = (at[i].radical==RADICAL_SINGLET || + at[i].radical==RADICAL_DOUBLET || + at[i].radical==RADICAL_TRIPLET)? at[i].radical : 0; + if ( radical ) + { + sprintf( entry, " %3d %3d", i+1, radical ); + strcat( str_m, entry ); + num_m ++; + } + } + if ( i == num_atoms-1 && num_m || num_m == 8 ) + { + inchi_ios_print_nodisplay( fcb, "M RAD%3d%s\n", num_m, str_m ); + str_m[0] = 0; + num_m = 0; + } + } + } + + /* isotopes*/ + str_m[0] = 0; + num_m = 0; + if ( nNumIsoLines ) + { + int el_num, iso; + for (i=0; i < num_atoms; i++) + { + /* + if ( 0 == strcmp( at[i].elname, "D" ) ) { + sprintf( entry, " %3d %3d", i+1, 2 ); + strcat( str_m, entry ); + num_m ++; + } else + if ( 0 == strcmp( at[i].elname, "T" ) ) { + sprintf( entry, " %3d %3d", i+1, 3 ); + strcat( str_m, entry ); + num_m ++; + } else + if ( k = at[i].iso_atw_diff ) { + int mw = get_atomic_mass_from_elnum( at[i].el_number ); + mw += (k > 0)? k-1 : k; + sprintf( entry, " %3d %3d", i+1, mw ); + strcat( str_m, entry ); + num_m ++; + } + */ + + if ( ANY_ISO(i, bAtomsDT) && !ALIASED_AT(i) ) + { + if ( IS_DEUTERIUM(i) ) + { + iso = 1; + el_num = 1; + } + else if ( IS_TRITIUM(i) ) + { + iso = 2; + el_num = 1; + } + else + { + iso = at[i].iso_atw_diff > 0? at[i].iso_atw_diff-1 : at[i].iso_atw_diff; + el_num = at[i].el_number; + } + iso += get_atomic_mass_from_elnum( el_num ); + + sprintf( entry, " %3d %3d", i+1, iso ); + strcat( str_m, entry ); + num_m ++; + } + + if ( i == num_atoms-1 && num_m || num_m == 8 ) + { + inchi_ios_print_nodisplay( fcb, "M ISO%3d%s\n", num_m, str_m ); + str_m[0] = 0; + num_m = 0; + } + } + } + + if ( is_polymer ) + OrigAtData_WriteToSDfileOrigAtDataPolymer( inp_at_data, fcb, name, comment, + szLabel, szValue, written_bond_ends ); + + inchi_ios_print_nodisplay( fcb, "M END\n" ); + + return ret; +} + +/* + OrigAtData : Write To SDfile : Polymer Data +*/ +int OrigAtData_WriteToSDfileOrigAtDataPolymer( const ORIG_ATOM_DATA *inp_at_data, + INCHI_IOSTREAM * fcb, + const char* name, + const char* comment, + const char *szLabel, + const char *szValue, + INT_ARRAY *written_bond_ends ) +{ + int j, k, ju, jj, jprev, ret=0; + const char *sty[] = {"NON", "SRU", "MON", "COP", "MOD", "CRO", "MER" }; + const char *sst[] = {"NON", "ALT", "RAN", "BLO" }; + const char *con[] = {"NON", "HT", "HH", "EU" }; + OrigAtDataPolymerUnit *u = NULL; + + /* STY */ + jj = 0; jprev = -1; + for (j=0; jpolymer->n; j++) + { + u = inp_at_data->polymer->units[j]; + if ( u->type >0 && u->type<=6 ) + jj++; + if ( jj==8 || j==inp_at_data->polymer->n - 1 ) + { + inchi_ios_print_nodisplay( fcb, "M STY%3d", jj%8?jj%8:8 ); + for (k=jprev+1; k<=j; k++) + { + u = inp_at_data->polymer->units[k]; + if ( u->type >0 && u->type<=6 ) + inchi_ios_print_nodisplay( fcb, " %3d %3s", u->id, sty[u->type]); + } + inchi_ios_print_nodisplay( fcb, "\n"); + jj = 0; + jprev = j; + } + } + /* SLB */ + jj = 0; jprev = -1; + for (j=0; jpolymer->n; j++) + { + u = inp_at_data->polymer->units[j]; + if ( j==8 || j==inp_at_data->polymer->n - 1 ) + { + jj = j + 1; + inchi_ios_print_nodisplay( fcb, "M SLB%3d", jj%8?jj%8:8 ); + for (k=jprev+1; kpolymer->units[k]; + inchi_ios_print_nodisplay( fcb, " %3d %3d", u->id, u->label ); + } + inchi_ios_print_nodisplay( fcb, "\n"); + jj = 0; + jprev = j; + } + } + + /* SST */ + jj = 0; jprev = -1; + for (j=0; jpolymer->n; j++) + { + u = inp_at_data->polymer->units[j]; + if ( u->subtype == MOL_FMT_M_SST_ALT || u->subtype == MOL_FMT_M_SST_RAN || u->subtype == MOL_FMT_M_SST_BLK ) + jj++; + } + if ( jj ) + { + jj = 0; jprev = -1; + for (j=0; jpolymer->n; j++) + { + u = inp_at_data->polymer->units[j]; + if ( u->subtype == MOL_FMT_M_SST_ALT || u->subtype == MOL_FMT_M_SST_RAN || u->subtype == MOL_FMT_M_SST_BLK ) + jj++; + if ( jj==8 || j==inp_at_data->polymer->n - 1 ) + { + inchi_ios_print_nodisplay( fcb, "M SST%3d", jj%8?jj%8:8 ); + for (k=jprev+1; k<=j; k++) + { + u = inp_at_data->polymer->units[k]; + if ( u->subtype == MOL_FMT_M_SST_ALT || u->subtype == MOL_FMT_M_SST_RAN || u->subtype == MOL_FMT_M_SST_BLK ) + inchi_ios_print_nodisplay( fcb, " %3d %3s", u->id, sst[u->subtype]); + } + inchi_ios_print_nodisplay( fcb, "\n"); + jj = 0; + jprev = j; + } + } + } + + /* SCN */ + jj = 0; jprev = -1; + for (j=0; jpolymer->n; j++) + { + u = inp_at_data->polymer->units[j]; + if ( u->conn==MOL_FMT_M_CONN_HT || u->conn==MOL_FMT_M_CONN_HH || u->conn==MOL_FMT_M_CONN_EU ) + jj++; + } + if ( jj ) + { + jj = 0; jprev = -1; + for (j=0; jpolymer->n; j++) + { + u = inp_at_data->polymer->units[j]; + if ( u->conn==MOL_FMT_M_CONN_HT || u->conn==MOL_FMT_M_CONN_HH || u->conn==MOL_FMT_M_CONN_EU ) + jj++; + if ( jj==8 || j==inp_at_data->polymer->n - 1 ) + { + inchi_ios_print_nodisplay( fcb, "M SCN%3d", jj%8?jj%8:8 ); + for (k=jprev+1; k<=j; k++) + { + u = inp_at_data->polymer->units[k]; + if ( u->conn==MOL_FMT_M_CONN_HT || u->conn==MOL_FMT_M_CONN_HH || u->conn==MOL_FMT_M_CONN_EU ) + inchi_ios_print_nodisplay( fcb, " %3d %3s", u->id, con[u->conn]); + } + inchi_ios_print_nodisplay( fcb, "\n"); + jj = 0; + jprev = j; + } + } + } + /* SAL */ + for (ju=0; jupolymer->n; ju++) + { + u = inp_at_data->polymer->units[ju]; + jj = 0; jprev = -1; + for (j=0; jna; j++) + { + jj++; + if ( jj==15 || j==u->na - 1 ) + { + inchi_ios_print_nodisplay( fcb, "M SAL %3d%3d", u->id, jj%15?jj%15:15 ); + for (k=jprev+1; k<=j; k++) + inchi_ios_print_nodisplay( fcb, " %3d", u->alist[k] ); + inchi_ios_print_nodisplay( fcb, "\n"); + jj = 0; + jprev = j; + } + } + } + /* SBL */ + for (ju=0; jupolymer->n; ju++) + { + u = inp_at_data->polymer->units[ju]; + jj = 0; jprev = -1; + for (j=0; jnb; j++) + { + jj++; + if ( jj==15 || j==u->nb - 1 ) + { + inchi_ios_print_nodisplay( fcb, "M SBL %3d%3d", u->id, jj%15?jj%15:15 ); + + for (k=jprev+1; k<=j; k++) + { + int a1, a2, e1, e2, wb, bond_num=0; + a1 = u->blist[2*k]; + a2 = u->blist[2*k + 1]; + for ( wb = 0; wbused/2; wb++ ) + { + e1 = written_bond_ends->item[2*wb]; + e2 = written_bond_ends->item[2*wb + 1]; + if ( (a1==e1&&a2==e2) || (a2==e1&&a1==e2) ) + { + bond_num = wb + 1; + break; + } + } + if ( bond_num ) + inchi_ios_print_nodisplay( fcb, " %3d", bond_num ); + } + + inchi_ios_print_nodisplay( fcb, "\n"); + jj = 0; + jprev = j; + } + } + } + + /* SDI */ + for (j=0; jpolymer->n; j++) + { + /* better than nothing */ + float xmin, xmax, ymin, ymax; + xmin = ymin = -1.0*(j + 1); + xmax = ymax = +1.0*(j + 1); + for (k=0; kna; k++ ) + u = inp_at_data->polymer->units[j]; + /* u->xbr1[0], x1, y1, x2, y2 u->xbr1[1], u->xbr1[2], u->xbr1[3] */ + inchi_ios_print_nodisplay( fcb, "M SDI %3d%3d%10.4f%10.4f%10.4f%10.4f\n", u->id, 4, xmin, ymin, xmin, ymax ); + /* u->xbr2[0], u->xbr2[1], u->xbr2[2], u->xbr2[3] */ + inchi_ios_print_nodisplay( fcb, "M SDI %3d%3d%10.4f%10.4f%10.4f%10.4f\n", u->id, 4, xmax, ymax, xmax, ymin ); + } + + return ret; +} + +/* + OrigAtData_CheckAndMakePolymerPhaseShifts +*/ +void OrigAtData_CheckAndMakePolymerPhaseShifts( OrigAtDataPolymer *p, + inp_ATOM *at, + int nat, + int *num_inp_bonds ) +{ +int i, j, k, senior_bond = 0, *bnum = NULL; +int bond_type, bond_stereo; +OrigAtDataPolymerAtomProps *aprops=NULL; + + if ( !p ) return; + if ( p->n < 1 ) return; + if ( !p->really_do_phase_shift ) return; + + /* Set atom properties for sorting */ + aprops = (OrigAtDataPolymerAtomProps *) inchi_calloc( nat, sizeof(OrigAtDataPolymerAtomProps) ); + if (!aprops) + return; + OrigAtData_FillAtProps( p, at, nat, num_inp_bonds, aprops ); + + for (i=0; in; i++) + { + OrigAtDataPolymerUnit *u = p->units[i]; + if ( !u->closeable || u->already_closed || u->npsbonds < 1 || u->star1<1 || u->star2<1 ) /* || !u->psbonds */ + continue; + + /* Check de-closure type */ + + /* Stars are separated by one atom - that's not error but do nothing */ + if ( u->npsbonds == 0 ) + { + ; + } + else if ( u->npsbonds == 1 ) + { + u->star_partner1 = u->psbonds[0][0]; + u->star_partner2 = u->psbonds[0][1]; + + if ( u->star_partner1 == u->star_partner2 ) + { +#ifdef ALLOW_CLOSING_SRU_VIA_DIRADICAL + u->closeable = CLOSING_SRU_DIRADICAL; +#else + u->closeable = CLOSING_SRU_NOT_APPLICABLE; +#endif + } + else + { + /* If stars are separated by two atoms - that's not error but do nothing */ + for (k=0; kstar_partner1-1].valence; k++) + { + if ( at[u->star_partner1-1].neighbor[k] == u->star_partner2 - 1 ) + { + if ( at[u->star_partner1-1].bond_type[k] > 1 ) +#ifdef ALLOW_CLOSING_SRU_VIA_HIGHER_ORDER_BOND + u->closeable = CLOSING_SRU_HIGHER_ORDER_BOND; +#else +/* u->closeable = CLOSING_SRU_NOT_APPLICABLE;*/ +#endif + break; + } + } + } + } + + senior_bond = 0; + + /* Sort phase shiftable bonds if necessary */ + if ( u->npsbonds > 1 ) + { + bnum = (int *) inchi_calloc( u->npsbonds, sizeof(int) ); + if ( bnum ) + { + for (j=0; jnpsbonds; j++) + bnum[j] = j; + OrigAtDataPolymerUnit_SortPSBonds( u, aprops, bnum ); + senior_bond = bnum[0]; + inchi_free( bnum ); + } + } + + u->star_partner1 = u->psbonds[senior_bond][0]; + u->star_partner2 = u->psbonds[senior_bond][1]; + + if ( u->closeable == CLOSING_SRU_RING ) + { + /* Decyclize artificially introducd bond */ + OrigAtData_RemoveBond( u->star_partner1 - 1, u->star_partner2 - 1 , at, + &bond_type, &bond_stereo, num_inp_bonds ); + } + else if ( u->closeable == CLOSING_SRU_HIGHER_ORDER_BOND ) + { + OrigAtData_DecreaseBondOrder( u->star_partner1 - 1, u->star_partner2 - 1, at ); + } + else if ( u->closeable == CLOSING_SRU_DIRADICAL ) + { + if ( at[u->star_partner1 - 1].radical == RADICAL_TRIPLET ) + at[u->star_partner1 - 1].radical = 0; + } + + /* Add explicitly connections to star atoms */ + OrigAtData_AddSingleStereolessBond( u->star1 - 1, u->star_partner1 - 1, + at, num_inp_bonds ); + OrigAtData_AddSingleStereolessBond( u->star2 - 1, u->star_partner2 - 1, + at, num_inp_bonds ); + + /* Create crossing bonds */ + u->nb = 2; + u->npsbonds = 0; + if ( !u->blist ) u->blist = (int *) inchi_calloc( 2*u->nb, sizeof(int) ); + if ( !u->blist ) return; + u->blist[0] = u->star1; + u->blist[1] = u->star_partner1; + u->blist[2] = u->star2; + u->blist[3] = u->star_partner2; + } + + p->really_do_phase_shift = 0; + inchi_free( aprops ); + + return; +} + +/* + +*/ +void OrigAtDataPolymerUnit_SortPSBonds( OrigAtDataPolymerUnit *u, OrigAtDataPolymerAtomProps *aprops, int *bnum ) +{ +int i, j, tmp; + int n = u->npsbonds; + if ( NULL == bnum ) + return; + for (i=1; i= 0 && OrigAtDataPolymerUnit_ComparePSBonds( u->psbonds[ bnum[j] ], u->psbonds[ tmp ], aprops ) > 0 ) + { + bnum[j+1] = bnum[j]; + j--; + } + bnum[j+1] = tmp; + } + return; +} + +/* + For sorting SRU cyclizing bonds (PS=='phase-shift') in descending order + + In general: + favor greater max-rank end + if max ends are the same, favor lesser min-rank end + +*/ +int OrigAtDataPolymerUnit_ComparePSBonds( int* b1, int* b2, OrigAtDataPolymerAtomProps *aprops ) +{ +int b1min, b1max, b2min, b2max, tmp, cmp=0; + + /* Find min and max ext-ranked ends of the both bonds */ + b1max = b1[0]; b1min = b1[1]; + b2max = b2[0]; b2min = b2[1]; + if ( ranks_increase_from_1st_to_2nd( b1min, b1max, aprops ) == -1 ) + { + tmp = b1max; + b1max = b1min; + b1min = tmp; + } + if ( ranks_increase_from_1st_to_2nd( b2min, b2max, aprops ) == -1 ) + { + tmp = b2max; + b2max = b2min; + b2min = tmp; + } + + /* Compare bonds' seniority */ + + /* First, favor the bond which has greater ext-rank end + NB: the result may be 0, that is, equal max ext. ranks + */ + cmp = compare_ranks_of_1st_and_2nd( b1max, b2max, aprops ); + if ( cmp == 1 ) return 1; /* rank(b1max) < rank(b2max), so bond2 is senior */ + else if ( cmp == -1 ) return -1; /* rank(b1max) > rank(b2max), so bond1 is senior */ + + /* Max ends are of the same rank, so favor the bond with lesser min-rank end + NB: the result may NOT be 0, that is, the case is always resolved + */ + + cmp = compare_ranks_of_1st_and_2nd( b1min, b2min, aprops ); /*ranks_increase_from_1st_to_2nd( b1min, b2min, aprops );*/ + + if ( cmp == 1 ) return -1; /* rank(b1min) < rank(b2min), so bond1 is senior */ + else if ( cmp == -1 ) return 1; /* rank(b1min) > rank(b2min), so bond2 is senior */ + + /* Min ends are of the same rank. Here is the time to compare directly + which canonical number is larger of max-ends ... */ + if ( b1max < b2max ) return 1; + if ( b1max > b2max ) return -1; + + /* ... they are the same, so compare which canonical number is larger for min-ends ... */ + if ( b1min < b2min ) return -1; /* b1min < b2min, so bond1 is senior */ + if ( b1min > b2min ) return -1; /* b1min > b2min, so bond2 is senior */ + + return 0; /* We should not reach there */ +} + +/* + Compare seniority of two atoms in polymer SRU loosely following IUPAC guidelines + NB: no last resort check here, so 0 (=='same seniority') may be returned +*/ +int compare_ranks_of_1st_and_2nd( int atom1, int atom2, OrigAtDataPolymerAtomProps *aprops ) +{ + const int HETEROCYC=3, HETEROAT=2, CARBOCYC=1, CARBOAT=0; + /* NB: Carbon's rank is always 2, next to the lowest */ + + int a1 = atom1 - 1; + int a2 = atom2 - 1; + int a1typ = CARBOAT; + int a2typ = CARBOAT; + + if ( aprops[a1].ring_size >2 ) { if ( aprops[a1].ring_erank<=2 ) a1typ = CARBOCYC; else a1typ = HETEROCYC; } + else { if ( aprops[a1].erank==2 ) a1typ = CARBOAT; else a1typ = HETEROAT; } + + if ( aprops[a2].ring_size >2 ) { if ( aprops[a2].ring_erank<=2 ) a2typ = CARBOCYC; else a2typ = HETEROCYC; } + else { if ( aprops[a2].erank==2 ) a2typ = CARBOAT; else a2typ = HETEROAT; } + + /* Compare */ + + /* + Follow IUPAC Rule 1 + 'The basic order of seniority of subunits is: + heterocyclic rings and ring systems > heteroatom chains > + > carbocyclic rings and ring systems > acyclic carbon chains' + */ + + if ( a1typ==HETEROCYC && a2typ==HETEROCYC ) /* a1 and a2 are HETEROCYC */ + { + /* Try resolving by senior-heteroatom ring */ + if ( aprops[a1].ring_erank < aprops[a2].ring_erank ) return 1; + if ( aprops[a1].ring_erank > aprops[a2].ring_erank ) return -1; + /* Same senior-heteroatom rings, try resolving by total ring size */ + if ( aprops[a1].ring_size < aprops[a2].ring_size ) return 1; + if ( aprops[a1].ring_size > aprops[a2].ring_size ) return -1; + /* Could not resolve... */ + return 0; + } + else if ( a1typ==HETEROCYC ) return -1; /* a1 is HETEROCYC, a2 is any other (==junior) */ + else if ( a2typ==HETEROCYC ) return 1; /* a2 is HETEROCYC, a1 is any other (==junior) */ + + /* HETEROCYC left out */ + + if ( a1typ==HETEROAT && a2typ==HETEROAT ) /* a1 and a2 are HETEROAT */ + { + if ( aprops[a1].erank < aprops[a2].erank ) return 1; + if ( aprops[a1].erank > aprops[a2].erank ) return -1; + /* Could not resolve... */ + return 0; + } + else if ( a1typ==HETEROAT ) return -1; /* a1 is HETEROAT, a2 is any other (==junior) */ + else if ( a2typ==HETEROAT ) return 1; /* a2 is HETEROAT, a1 is any other (==junior) */ + + /* HETEROAT left out */ + + if ( a1typ==CARBOCYC && a2typ==CARBOCYC ) /* a1 and a2 are CARBOCYC */ + { + /* Same senior-atom (C) ring, try resolving by total ring size */ + if ( aprops[a1].ring_size < aprops[a2].ring_size ) return 1; + if ( aprops[a1].ring_size > aprops[a2].ring_size ) return -1; + /* Could not resolve... */ + return 0; + } + else if ( a1typ==CARBOCYC ) return -1; + else if ( a2typ==CARBOCYC ) return 1; + + return 0; /* It is legal here */ +} + +/* + Compare seniority of two atoms in polymer SRU loosely following IUPAC guidelines + Always return non-0 result +*/ +int ranks_increase_from_1st_to_2nd(int atom1, int atom2, OrigAtDataPolymerAtomProps *aprops ) +{ + /* Compare ext-ranks */ + int result = compare_ranks_of_1st_and_2nd(atom1, atom2, aprops ); + + if ( result) return result; + + /* Could not resolve by ext-ranks, as a last resort simply check which canonical number is larger */ + if ( atom1 < atom2 ) return 1; + if ( atom1 > atom2 ) return -1; + + return 0; /* We should not reach there */ +} diff --git a/INCHI-1-SRC/INCHI_BASE/src/readinch.c b/INCHI-1-SRC/INCHI_BASE/src/readinch.c new file mode 100644 index 0000000..b2cdc27 --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/readinch.c @@ -0,0 +1,1781 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include +#include +#include +#include +#include + +#include "mode.h" + +#include "ichierr.h" +#include "extr_ct.h" +#include "ichi_io.h" + +#include "inchi_api.h" +#include "readinch.h" + +#define NO_ATOM (-1) /* non-existent (central) atom */ + + +#ifndef AB_MAX_WELL_DEFINED_PARITY +#define AB_MAX_WELL_DEFINED_PARITY inchi_max(INCHI_PARITY_ODD, INCHI_PARITY_EVEN) /* 1, 2 => well defined parities, uncluding 'unknown' */ +#endif + +#ifndef AB_MIN_WELL_DEFINED_PARITY +#define AB_MIN_WELL_DEFINED_PARITY inchi_min(INCHI_PARITY_ODD, INCHI_PARITY_EVEN) /* min(INCHI_PARITY_ODD, INCHI_PARITY_EVEN) */ +#endif + + +#if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) + +#ifndef AB_PARITY_UNKN +#define AB_PARITY_UNKN 3 /* 3 => user marked as unknown parity */ +#endif +#ifndef AB_PARITY_UNDF +#define AB_PARITY_UNDF 4 /* 4 => parity cannot be defined because of symmetry or not well defined geometry */ +#endif + +#define ATOM_PARITY_WELL_DEF(X) (AB_MIN_WELL_DEFINED_PARITY <= (X) && (X) <= AB_MAX_WELL_DEFINED_PARITY) + +#define SB_PARITY_FLAG 0x38 /* disconnected structure has undef. parity */ + +#define SB_PARITY_SHFT 3 + +#define SB_PARITY_MASK 0x07 + +#define SB_PARITY_1(X) (X & SB_PARITY_MASK) /* refers to connected structure */ + +#define SB_PARITY_2(X) (((X) >> SB_PARITY_SHFT) & SB_PARITY_MASK) /* refers to connected structure */ + + + +#endif /* #if ( defined(TARGET_API_LIB) || defined(TARGET_EXE_USING_API) ) */ + + +#if ( defined( TARGET_LIB_FOR_WINCHI ) || defined(TARGET_EXE_STANDALONE) ) +int Extract0DParities( inp_ATOM *at, + int nNumAtoms, + inchi_Stereo0D *stereo0D, + int num_stereo0D, + char *pStrErr, + int *err, + int vABParityUnknown); +#endif + + +void find_and_interpret_structure_header( char *szLine, + char *pSdfLabel, + char *pSdfValue, + long *Id, + int hlen, + ReadINCHI_CtlData *ir ); + + +/* + CreateInchi_Stereo0D +*/ +inchi_Stereo0D * CreateInchi_Stereo0D( int num_stereo0D ) +{ + return (inchi_Stereo0D* ) inchi_calloc(num_stereo0D, sizeof(inchi_Stereo0D) ); +} + + +/* + FreeInchi_Stereo0D +*/ +void FreeInchi_Stereo0D( inchi_Stereo0D **stereo0D ) +{ + if ( stereo0D && *stereo0D ) + { + inchi_free( *stereo0D ); + *stereo0D = NULL; + } +} + + +/* + Extract0DParities +*/ +int Extract0DParities( inp_ATOM *at, + int nNumAtoms, + inchi_Stereo0D *stereo0D, + int num_stereo0D, + char *pStrErr, + int *err, + int vABParityUnknown) +{ + + /* + vABParityUnknown holds actual value of an internal constant signifying + unknown parity: either the same as for undefined parity (default==standard) + or a specific one (non-std; requested by SLUUD switch). + */ + if ( stereo0D && num_stereo0D > 0 ) + { + int i0D, a2, k, k_prev, type, j, j1, j2, len, parity, parityNM; + int sb_ord_from_i1, sb_ord_from_i2, sn_ord_from_i1, sn_ord_from_i2; + AT_NUMB i1n, i2n, i1, i2; + + for ( i0D = 0; i0D < num_stereo0D; i0D ++ ) + { + parity = (stereo0D[i0D].parity & SB_PARITY_MASK); + parityNM = (stereo0D[i0D].parity & SB_PARITY_FLAG) >> SB_PARITY_SHFT; + + if ( parity == INCHI_PARITY_NONE || + parity != INCHI_PARITY_ODD && parity != INCHI_PARITY_EVEN && + parity != INCHI_PARITY_UNKNOWN && parity != INCHI_PARITY_UNDEFINED ) + { + char szTemp[16]; + sprintf( szTemp, "#%d", i0D+1 ); + TREAT_ERR (*err, 0, "Wrong 0D stereo descriptor(s):"); + TREAT_ERR (*err, 0, szTemp); + continue; /* warning */ + } + + type = stereo0D[i0D].type; + a2 = stereo0D[i0D].central_atom; /* central atom or -1 */ + j = -1; + len = 0; + sb_ord_from_i1 = sb_ord_from_i2 = sn_ord_from_i1 = sn_ord_from_i2 = -1; + i1n = i2n = i1 = i2 = MAX_ATOMS+1; + + if ( (type == INCHI_StereoType_Tetrahedral || + type == INCHI_StereoType_Allene ) && + 0 <= a2 && a2 < nNumAtoms || + type == INCHI_StereoType_DoubleBond && + a2 == NO_ATOM) + { + /* test the quadruplet */ + for ( j = 0, k_prev = -1; j < 4; j ++, k_prev = k ) + { + k = stereo0D[i0D].neighbor[j]; + if ( k < 0 || k >= nNumAtoms || k_prev == k ) + break; + /* tetrahedral atom connectivity test */ + if ( type == INCHI_StereoType_Tetrahedral && + k != a2 && + !is_in_the_list( at[a2].neighbor, (AT_NUMB)k, at[a2].valence) ) + { + break; + } + /* Double bond, Cumulene and allene are tested in the next if() */ + } + } + + /* Find in the adjacency lists the double bond neighbor that leads to the opposite atom */ + if ( j == 4 && (type == INCHI_StereoType_Allene || + type == INCHI_StereoType_DoubleBond) ) + { + AT_NUMB *p1 = NULL, *p2 = NULL, *q1 = NULL, *q2 = NULL; + i1n = (AT_NUMB)stereo0D[i0D].neighbor[0]; + i1 = (AT_NUMB)stereo0D[i0D].neighbor[1]; + i2 = (AT_NUMB)stereo0D[i0D].neighbor[2]; + i2n = (AT_NUMB)stereo0D[i0D].neighbor[3]; + + /* find q1 and q2 */ + if ( !(q1 = is_in_the_list( at[i1].neighbor, i1n, at[i1].valence)) || + !(q2 = is_in_the_list( at[i2].neighbor, i2n, at[i2].valence)) ) { + j = -2; /* error flag */ + } + else + /* allene or cumulene; follow double bonds from i1 to i2 */ + if ( !(p1 = is_in_the_list( at[i1].neighbor, i2, at[i1].valence)) ) + { + /* at[i1] and at[i2] are not connected: can be only allene or cumulene */ + + AT_NUMB prev, cur, next; + int num_dbond, i, next_ord, half_len; + + cur = next = i1; + len = half_len = 0; + while ( len < 20 ) + { + /* arbitrary very high upper limit to prevent infinite loop */ + prev = cur; + cur = next; + + for ( i = 0, num_dbond = 0; i < at[cur].valence; i ++ ) + { + /* follow double bond path && avoid going back */ + if ( at[cur].bond_type[i] == BOND_TYPE_DOUBLE && + prev != at[cur].neighbor[i] ) { + next = at[cur].neighbor[i]; + next_ord = i; + num_dbond ++; + } + } + + if ( num_dbond == 1 && next != i1 ) + { + len ++; + if ( len == 1 ) + sb_ord_from_i1 = next_ord; + + if ( type == INCHI_StereoType_Allene && next == (AT_NUMB)a2 ) + half_len = len; + } + else + break; + } + + if ( cur == i2 && prev != cur && 0 == num_dbond && len > 1 && + (p2 = is_in_the_list( at[i2].neighbor, prev, at[i2].valence)) && + (type != INCHI_StereoType_Allene || len == 2*half_len )) + { + sb_ord_from_i2 = p2 - at[i2].neighbor; + sn_ord_from_i1 = q1 - at[i1].neighbor; + sn_ord_from_i2 = q2 - at[i2].neighbor; + } + else + { + j = -5; /* error flag */ + } + } + else + /* allene must have been already processed, otherwise error */ + if ( type == INCHI_StereoType_Allene ) + { + /* error: atoms #1 and #2 of allene are connected */ + j = -3; /* error flag */ + } + else + /* double bond only; the bond type is not checked because at the end + of the normalization it may happen to be alternating */ + if ( type == INCHI_StereoType_DoubleBond && + (p2 = is_in_the_list( at[i2].neighbor, i1, at[i2].valence) ) ) + { + sb_ord_from_i1 = p1 - at[i1].neighbor; + sb_ord_from_i2 = p2 - at[i2].neighbor; + sn_ord_from_i1 = q1 - at[i1].neighbor; + sn_ord_from_i2 = q2 - at[i2].neighbor; + } + else + { + j = -4; /* error flag */ + } + } + + if ( j != 4 ) + { + char szTemp[16]; + sprintf( szTemp, "#%d", i0D+1 ); + TREAT_ERR (*err, 0, "Wrong 0D stereo descriptor(s):"); + TREAT_ERR (*err, 0, szTemp); + continue; /* error */ + } + + switch ( type ) + { + case INCHI_StereoType_None: + continue; + case INCHI_StereoType_DoubleBond: + case INCHI_StereoType_Allene: + for ( j1 = 0; j1 < MAX_NUM_STEREO_BONDS && at[i1].sb_parity[j1]; j1 ++ ) + ; + for ( j2 = 0; j2 < MAX_NUM_STEREO_BONDS && at[i2].sb_parity[j2]; j2 ++ ) + ; + if ( j1 < MAX_NUM_STEREO_BONDS && j2 < MAX_NUM_STEREO_BONDS && + sb_ord_from_i1 >= 0 && sb_ord_from_i2 >= 0 && + sn_ord_from_i1 >= 0 && sn_ord_from_i2 >= 0) + { + switch( parity ) + { + case INCHI_PARITY_ODD: + at[i1].sb_parity[j1] = AB_PARITY_ODD; + at[i2].sb_parity[j2] = AB_PARITY_EVEN; + break; + case INCHI_PARITY_EVEN: + at[i1].sb_parity[j1] = AB_PARITY_ODD; + at[i2].sb_parity[j2] = AB_PARITY_ODD; + break; + case INCHI_PARITY_UNDEFINED: + at[i1].sb_parity[j1] = AB_PARITY_UNDF; + at[i2].sb_parity[j2] = AB_PARITY_UNDF; + break; + default: + if ( parity == INCHI_PARITY_UNKNOWN ) + { + at[i1].sb_parity[j1] = vABParityUnknown; + at[i2].sb_parity[j2] = vABParityUnknown; + } + else + { + at[i1].sb_parity[j1] = AB_PARITY_NONE; + at[i2].sb_parity[j2] = AB_PARITY_NONE; + } + break; + } + switch( parityNM ) + { + case INCHI_PARITY_ODD: + at[i1].sb_parity[j1] |= AB_PARITY_ODD << SB_PARITY_SHFT; + at[i2].sb_parity[j2] |= AB_PARITY_EVEN << SB_PARITY_SHFT; + break; + case INCHI_PARITY_EVEN: + at[i1].sb_parity[j1] |= AB_PARITY_ODD << SB_PARITY_SHFT; + at[i2].sb_parity[j2] |= AB_PARITY_ODD << SB_PARITY_SHFT; + break; + case INCHI_PARITY_UNDEFINED: + at[i1].sb_parity[j1] |= AB_PARITY_UNDF << SB_PARITY_SHFT; + at[i2].sb_parity[j2] |= AB_PARITY_UNDF << SB_PARITY_SHFT; + break; + default: + if ( parityNM == INCHI_PARITY_UNKNOWN ) + { + at[i1].sb_parity[j1] |= vABParityUnknown << SB_PARITY_SHFT; + at[i2].sb_parity[j2] |= vABParityUnknown << SB_PARITY_SHFT; + } + break; + } + at[i1].sb_ord[j1] = sb_ord_from_i1; + at[i1].sn_ord[j1] = sn_ord_from_i1; + at[i1].sn_orig_at_num[j1] = at[i1n].orig_at_number; + + at[i2].sb_ord[j2] = sb_ord_from_i2; + at[i2].sn_ord[j2] = sn_ord_from_i2; + at[i2].sn_orig_at_num[j2] = at[i2n].orig_at_number; + } + break; +case INCHI_StereoType_Tetrahedral: + switch( parity ) + { + case INCHI_PARITY_ODD: + at[a2].p_parity = AB_PARITY_ODD; + break; + case INCHI_PARITY_EVEN: + at[a2].p_parity = AB_PARITY_EVEN; + break; + case INCHI_PARITY_UNDEFINED: + at[a2].p_parity = AB_PARITY_UNDF; + break; + default: + if (parity == INCHI_PARITY_UNKNOWN ) + { + at[a2].p_parity = vABParityUnknown; + break; + } + else + continue; + } + for ( j = 0; j < 4; j ++ ) + { + k = stereo0D[i0D].neighbor[j]; + at[a2].p_orig_at_num[j] = at[k].orig_at_number; + } + break; + + default: + break; + } + } + /* take care of Unknown stereobonds: */ + /* copy their Unknown stereo descriptors to at->bond_stereo (2005-03-01) */ + /* Note: to this stage, unk/undef set to what was requested */ + /*( through vABParityUnknown ) (2009-12-12) */ + FixUnkn0DStereoBonds(at, nNumAtoms); +#ifdef TARGET_API_LIB + + if ( k = ReconcileAllCmlBondParities( at, nNumAtoms, 0 ) ) { + char szErrCode[16]; + sprintf( szErrCode, "%d", k); + AddErrorMessage( pStrErr, "0D Parities Reconciliation failed:" ); + AddErrorMessage( pStrErr, szErrCode ); + } + +#endif + } + return 0; +} + + +/* + FindToken +*/ +char* FindToken( INCHI_IOSTREAM *inp_file, + int *bTooLongLine, + const char *sToken, + int lToken, + char *szLine, + int nLenLine, + char *p, + int *res ) +{ +char *q; +int res2; + + while ( !(q = strstr( p, sToken ) ) ) + { + if ( (q = strrchr( p, '/' )) && (q + lToken > szLine + *res) ) + { + *res -= q - szLine; /* res = the length of the szLine to be left in */ + memmove( szLine, q, *res + 1); + } + else + { + *res = 0; + } + + res2 = inchi_ios_getsTab1( szLine + *res, nLenLine - *res - 1, + inp_file, bTooLongLine ) ; + + if ( !*bTooLongLine || 0 > res2 ) + { + /* the line is over or end of file */ + return NULL; + } + else + { + *res += res2; + p = szLine; + } + } + + return q + lToken; +} + + +/* + LoadLine +*/ +char *LoadLine( INCHI_IOSTREAM *inp_file, + int *bTooLongLine, + int *bItemIsOver, + char **s, + char *szLine, + int nLenLine, + int nMinLen2Load, + char *p, + int *res ) +{ + + int pos = p - szLine, res2; + + if ( !*bItemIsOver && nLenLine - (*res - pos) > nMinLen2Load ) + { + /* load the next portion if possible */ + + if ( pos ) + { + *res -= pos; + memmove( szLine, p, *res+1 ); + p = szLine; + if ( *s ) + *s -= pos; + + pos = 0; + } + + res2 = inchi_ios_getsTab1( szLine + *res, + nLenLine - *res - 1, + inp_file, + bTooLongLine ); + + if ( res2 > 0 ) + { + *bItemIsOver = ( (*s = strchr( p + *res, '/') ) || !*bTooLongLine ); + *res += res2; + } + else + *bItemIsOver = 1; + } + + return p; +} + + +/*****************************************************************************/ +#define AT_BONDS_VAL(AT,I) AT[I].chem_bonds_valence +#define ISOLATED_ATOM 15 +#define NUM_ISO_Hk(AT,I,K) AT[I].num_iso_H[K] +#define inchi_NUMH2(AT,N) NUMH(AT,N) +#define AT_NUM_BONDS(AT) (AT).valence +#define IS_METAL_ATOM(AT,I) is_el_a_metal( AT[I].el_number ) + + +/* + InchiToInpAtom +*/ +int InchiToInpAtom ( INCHI_IOSTREAM *inp_file, + MOL_COORD **szCoord, + int bDoNotAddH, + int vABParityUnknown, + INPUT_TYPE nInputType, + inp_ATOM **at, + int max_num_at, + int *num_dimensions, + int *num_bonds, + char *pSdfLabel, + char *pSdfValue, + long *Id, + INCHI_MODE *pInpAtomFlags, + int *err, + char *pStrErr ) +{ +int num_atoms = 0, bFindNext = 0, bItemIsOver; +int i, k, k2, res, bond_type, bond_stereo1, bond_stereo2, bond_char, neigh, bond_parity, bond_parityNM; +int res2, bTooLongLine2, hk; +char szLine[INCHI_LINE_LEN], *p, *q, *s, parity; +int b2D=0, b3D=0, b23D, nNumBonds = 0, bNonZeroXYZ, bNonMetal; +int len_stereo0D = 0, max_len_stereo0D = 0; +inp_ATOM *atom = NULL; +MOL_COORD *pszCoord = NULL; +INCHI_MODE InpAtomFlags = 0; /* 0 or FLAG_INP_AT_NONCHIRAL or FLAG_INP_AT_CHIRAL */ +inchi_Stereo0D *atom_stereo0D = NULL; +static const char szIsoH[] = "hdt"; +/* plain tags */ +static const char sStructHdrPln[] = "Structure:"; +static char sStructHdrPlnAuxStart[64] =""; /*"$1.1Beta/";*/ +static int lenStructHdrPlnAuxStart = 0; +static const char sStructHdrPlnRevAt[] = "/rA:"; +static const char sStructHdrPlnRevBn[] = "/rB:"; +static const char sStructHdrPlnRevXYZ[] = "/rC:"; +const char *sToken; +int lToken, len, hlen; + + +ReadINCHI_CtlData ir; + + + if ( !lenStructHdrPlnAuxStart ) + lenStructHdrPlnAuxStart = sprintf( sStructHdrPlnAuxStart, "AuxInfo=" ); + + if ( at ) + { + if ( *at && max_num_at ) + memset( *at, 0, max_num_at * sizeof(**at) ); + if ( szCoord && *szCoord ) + { + inchi_free( *szCoord ); + *szCoord = NULL; + } + } + else + { + bFindNext = 1; + } + + ir.bHeaderRead = ir.bErrorMsg = ir.bRestoreInfo = 0; + *num_dimensions = *num_bonds = 0; + + + if ( nInputType != INPUT_INCHI_PLAIN ) + return num_atoms; + + + /* + Extract reversibility info from plain text INChI format + */ + + + ir.bHeaderRead = hk = 0; + while ( 0 < (res = inchi_ios_getsTab( szLine, sizeof(szLine)-1, inp_file, &ir.bTooLongLine ) ) ) + { + + + if ( !ir.bTooLongLine && + (hlen=sizeof(sStructHdrPln)-1, !memcmp(szLine, sStructHdrPln, hlen)) ) + + { + num_atoms = 0; + find_and_interpret_structure_header( szLine, pSdfLabel, pSdfValue, + Id, hlen, &ir ); + } + + else if ( !memcmp( szLine, sStructHdrPlnAuxStart, lenStructHdrPlnAuxStart) ) + { + /* Reject to deal with polymers for now */ + if ( szLine && strstr(szLine, "/Z:")) + { + *err = INCHI_INP_ERROR_ERR; + num_atoms = INCHI_INP_ERROR_RET; + TREAT_ERR (*err, 0, "Reading polymer AuxInfo is not supported yet"); + goto bypass_end_of_INChI_plain; + } + + /* Found the header of the AuxInfo, read AuxInfo head of the line */ + if ( !ir.bHeaderRead ) + { + ir.longID = 0; + if ( Id ) + *Id = ir.longID; + if ( pSdfLabel ) + pSdfLabel[0] = '\0'; + if ( pSdfValue ) + pSdfValue[0] = '\0'; + } + + ir.bHeaderRead = 0; + + /* Check for empty "AuxInfo=ver//" */ + p = strchr( szLine + lenStructHdrPlnAuxStart, '/' ); + + if ( p && p[1] == '/' && (!p[2] || '\n' == p[2]) ) + { + goto bypass_end_of_INChI_plain; + } + + + /* + Search for atoms block (plain) + */ + + p = szLine; + sToken = sStructHdrPlnRevAt; + lToken = sizeof(sStructHdrPlnRevAt)-1; + + /* Search for sToken in the line; load next segments of the line if sToken has not found */ + + p = FindToken( inp_file, &ir.bTooLongLine, sToken, lToken, + szLine, sizeof(szLine), p, &res ); + + if ( !p ) + { + *err = INCHI_INP_ERROR_ERR; + num_atoms = INCHI_INP_ERROR_RET; + TREAT_ERR (*err, 0, "Missing atom data"); + goto bypass_end_of_INChI_plain; + } + else + { + /* atoms block started */ + + i = 0; + res2 = bTooLongLine2 = -1; + bItemIsOver = (s = strchr( p, '/') ) || !ir.bTooLongLine; + + while ( 1 ) + { + + p = LoadLine( inp_file, &ir.bTooLongLine, &bItemIsOver, &s, + szLine, sizeof(szLine), INCHI_LINE_ADD, p, &res ); + + if ( !i ) + { + /* allocate atom */ + num_atoms = strtol( p, &q, 10 ); + + if ( !num_atoms || !q || !*q ) + { + num_atoms = 0; /* no atom data */ + goto bypass_end_of_INChI_plain; + } + p = q; + + /* Molfile chirality flag */ + switch( *p ) + { + case 'c': + InpAtomFlags |= FLAG_INP_AT_CHIRAL; + p ++; + break; + case 'n': + InpAtomFlags |= FLAG_INP_AT_NONCHIRAL; + p ++; + break; + } + + if ( at && *at ) + { + if ( num_atoms > max_num_at ) + { + inchi_free( *at ); + *at = NULL; + } + else + { + memset( *at, 0, max_num_at * sizeof( **at ) ); + atom = *at; + } + } + + if ( !at || !*at ) + { + + atom = CreateInpAtom( num_atoms+1 ); + + if ( !atom ) + { + num_atoms = INCHI_INP_FATAL_RET; /* was -1; error */ + *err = INCHI_INP_FATAL_ERR; + TREAT_ERR (*err, 0, "Out of RAM"); + goto bypass_end_of_INChI_plain; + } + } + + + { + max_len_stereo0D = num_atoms+1; + + atom_stereo0D = CreateInchi_Stereo0D( max_len_stereo0D ); + + if ( !atom_stereo0D ) + { + num_atoms = INCHI_INP_FATAL_RET; /* fatal error: cannot allocate */ + *err = INCHI_INP_FATAL_ERR; + TREAT_ERR (*err, 0, "Out of RAM"); + goto bypass_end_of_INChI_plain; + } + } + } + + /* element, first char */ + if ( !isalpha( UCINT *p ) || !isupper( UCINT *p ) || i >= num_atoms ) + { + break; /* end of atoms block */ + } + + atom[i].elname[0] = *p ++; + + /* element, second char */ + if ( isalpha( UCINT *p ) && islower( UCINT *p ) ) + { + atom[i].elname[1] = *p ++; + } + + atom[i].el_number = get_periodic_table_number( atom[i].elname ); + + /* bonds' valence + number of non-isotopic H */ + if ( isdigit( UCINT *p ) ) + { + AT_BONDS_VAL(atom,i) = (char)strtol( p, &q, 10 ); + if ( !AT_BONDS_VAL(atom,i) ) + AT_BONDS_VAL(atom,i) = ISOLATED_ATOM; /* same convention as in MOLfile, found zero bonds valence */ + p = q; + } + + /* charge */ + atom[i].charge = (*p == '+')? 1 : (*p == '-') ? -1 + : 0; + if ( atom[i].charge ) + { + p ++; + if ( isdigit( UCINT *p ) ) + { + atom[i].charge *= (S_CHAR)(strtol( p, &q, 10 ) & CHAR_MASK); + p = q; + } + } + + /* radical */ + if ( *p == '.' ) + { + p ++; + if ( isdigit( UCINT *p ) ) + { + atom[i].radical = (S_CHAR)strtol( p, &q, 10 ); + p = q; + } + } + + /* isotopic mass */ + if ( *p == 'i' ) + { + p ++; + if ( isdigit( UCINT *p ) ) + { + int mw = strtol( p, &q, 10 ); + p = q; + mw -= get_atomic_mass_from_elnum( atom[i].el_number ); + if ( mw >= 0 ) + mw ++; + atom[i].iso_atw_diff = mw; + } + } + + /* parity */ + switch( *p ) + { + case 'o': + parity = INCHI_PARITY_ODD; + p ++; + break; + case 'e': + parity = INCHI_PARITY_EVEN; + p ++; + break; + case 'u': + parity = INCHI_PARITY_UNKNOWN; + p ++; + break; + case '?': + parity = INCHI_PARITY_UNDEFINED; + p ++; + break; + default: + parity = 0; + break; + } + + if ( parity ) + { + atom_stereo0D[len_stereo0D].central_atom = i; + atom_stereo0D[len_stereo0D].parity = parity; + atom_stereo0D[len_stereo0D].type = INCHI_StereoType_Tetrahedral; + len_stereo0D ++; + } + + /* isotopic h, d, t */ + for ( k = 0; k < NUM_H_ISOTOPES; k ++ ) + { + if ( *p == szIsoH[k] ) { + NUM_ISO_Hk(atom,i,k) = 1; + p ++; + if ( isdigit( UCINT *p ) ) { + NUM_ISO_Hk(atom,i,k) = (char)strtol( p, &q, 10 ); + p = q; + } + } + } + + i ++; + } + + + if ( !bItemIsOver || i != num_atoms || s && p != s ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong number of atoms"); + goto bypass_end_of_INChI_plain; + } + } + + + /* + Search for bonds block (plain) and read it + */ + + + /*p = szLine;*/ + sToken = sStructHdrPlnRevBn; + lToken = sizeof(sStructHdrPlnRevBn)-1; + + /* Search for sToken in the line; load next segments of the line if sToken has not found */ + p = FindToken( inp_file, &ir.bTooLongLine, sToken, lToken, szLine, sizeof(szLine), p, &res ); + + if ( !p ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Missing bonds data"); + goto bypass_end_of_INChI_plain; + } + else + { + /* bonds block started */ + + i = 1; + + res2 = bTooLongLine2 = -1; + + bItemIsOver = (s = strchr( p, '/') ) || !ir.bTooLongLine; + + if ( 1 == num_atoms ) + { + /* needed because the next '/' may be still out of szLine */ + + p = LoadLine( inp_file, &ir.bTooLongLine, &bItemIsOver, &s, + szLine, sizeof(szLine), INCHI_LINE_ADD, p, &res ); + } + + while ( i < num_atoms ) + { + + p = LoadLine( inp_file, &ir.bTooLongLine, &bItemIsOver, &s, + szLine, sizeof(szLine), INCHI_LINE_ADD, p, &res ); + + if ( i >= num_atoms || s && p >= s ) + { + break; /* end of bonds (plain) */ + } + + /* bond, first char */ + if ( *p == ';' ) + { + p ++; + i ++; + continue; + } + + if ( !isalpha( UCINT *p ) ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong bonds data"); + goto bypass_end_of_INChI_plain; + } + + bond_char = *p ++; + + /* bond parity */ + switch( *p ) + { + case '-': + bond_parity = INCHI_PARITY_ODD; + p ++; + break; + case '+': + bond_parity = INCHI_PARITY_EVEN; + p ++; + break; + case 'u': + bond_parity = INCHI_PARITY_UNKNOWN; + p ++; + break; + case '?': + bond_parity = INCHI_PARITY_UNDEFINED; + p ++; + break; + default: + bond_parity = 0; + break; + } + + if ( bond_parity ) + { + switch( *p ) + { + case '-': + bond_parityNM = INCHI_PARITY_ODD; + p ++; + break; + case '+': + bond_parityNM = INCHI_PARITY_EVEN; + p ++; + break; + case 'u': + bond_parityNM = INCHI_PARITY_UNKNOWN; + p ++; + break; + case '?': + bond_parityNM = INCHI_PARITY_UNDEFINED; + p ++; + break; + default: + bond_parityNM = 0; + break; + } + } + else + { + bond_parityNM = 0; + } + + /* neighbor of the current atom */ + if ( !isdigit( UCINT *p ) ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong bonds data"); + goto bypass_end_of_INChI_plain; + } + + neigh = (int)strtol( p, &q, 10 )-1; + + if ( i >= num_atoms || neigh >= num_atoms ) { + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Bond to nonexistent atom"); + goto bypass_end_of_INChI_plain; + } + + p = q; + bond_stereo1 = bond_stereo2 = 0; + + /* bond type & 2D stereo */ + switch( bond_char ) + { + case 'v': + bond_type = INCHI_BOND_TYPE_SINGLE; + bond_stereo1 = INCHI_BOND_STEREO_SINGLE_1EITHER; + bond_stereo2 = INCHI_BOND_STEREO_SINGLE_2EITHER; + break; + case 'V': + bond_type = INCHI_BOND_TYPE_SINGLE; + bond_stereo1 = INCHI_BOND_STEREO_SINGLE_2EITHER; + bond_stereo2 = INCHI_BOND_STEREO_SINGLE_1EITHER; + break; + case 'w': + bond_type = INCHI_BOND_TYPE_DOUBLE; + bond_stereo1 = + bond_stereo2 = INCHI_BOND_STEREO_DOUBLE_EITHER; + break; + case 's': + bond_type = INCHI_BOND_TYPE_SINGLE; + break; + case 'd': + bond_type = INCHI_BOND_TYPE_DOUBLE; + break; + case 't': + bond_type = INCHI_BOND_TYPE_TRIPLE; + break; + case 'a': + bond_type = INCHI_BOND_TYPE_ALTERN; + break; + case 'p': + bond_type = INCHI_BOND_TYPE_SINGLE; + bond_stereo1 = INCHI_BOND_STEREO_SINGLE_1UP; + bond_stereo2 = INCHI_BOND_STEREO_SINGLE_2UP; + break; + case 'P': + bond_type = INCHI_BOND_TYPE_SINGLE; + bond_stereo1 = INCHI_BOND_STEREO_SINGLE_2UP; + bond_stereo2 = INCHI_BOND_STEREO_SINGLE_1UP; + break; + case 'n': + bond_type = INCHI_BOND_TYPE_SINGLE; + bond_stereo1 = INCHI_BOND_STEREO_SINGLE_1DOWN; + bond_stereo2 = INCHI_BOND_STEREO_SINGLE_2DOWN; + break; + case 'N': + bond_type = INCHI_BOND_TYPE_SINGLE; + bond_stereo1 = INCHI_BOND_STEREO_SINGLE_2DOWN; + bond_stereo2 = INCHI_BOND_STEREO_SINGLE_1DOWN; + break; + default: + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong bond type"); + goto bypass_end_of_INChI_plain; + } + + k = AT_NUM_BONDS(atom[i]) ++; /* AT_NUM_BONDS(AT) ==> (AT).valence */ + + atom[i].bond_type[k] = bond_type; + atom[i].bond_stereo[k] = bond_stereo1; + atom[i].neighbor[k] = (AT_NUMB)neigh; + + k2 = AT_NUM_BONDS(atom[neigh]) ++; /* AT_NUM_BONDS(AT) ==> (AT).valence */ + atom[neigh].bond_type[k2] = bond_type; + atom[neigh].bond_stereo[k2] = bond_stereo2; + atom[neigh].neighbor[k2] = (AT_NUMB)i; + + bond_parity |= (bond_parityNM << SB_PARITY_SHFT); + + if ( bond_parity ) + { + if ( max_len_stereo0D <= len_stereo0D ) + { + /* realloc atom_Stereo0D */ + + inchi_Stereo0D *new_atom_stereo0D = CreateInchi_Stereo0D( max_len_stereo0D+num_atoms ); + + if ( !new_atom_stereo0D ) + { + num_atoms = INCHI_INP_FATAL_RET; /* fatal error: cannot allocate */ + *err = INCHI_INP_FATAL_ERR; + TREAT_ERR (*err, 0, "Out of RAM"); + goto bypass_end_of_INChI_plain; + } + + memcpy( new_atom_stereo0D, atom_stereo0D, len_stereo0D * sizeof(*atom_stereo0D) ); + FreeInchi_Stereo0D( &atom_stereo0D ); + atom_stereo0D = new_atom_stereo0D; + max_len_stereo0D += num_atoms; + } + + /* (a) i may be allene endpoint and neigh = allene middle point or + (b) i may be allene middle point and neigh = allene endpoint + !!!!! CURRENTLY ONLY (b) IS ALLOWED !!!!! + */ + + atom_stereo0D[len_stereo0D].neighbor[1] = neigh; /* neigh < i */ + atom_stereo0D[len_stereo0D].neighbor[2] = i; + atom_stereo0D[len_stereo0D].parity = bond_parity; + atom_stereo0D[len_stereo0D].type = INCHI_StereoType_DoubleBond; /* incl allenes & cumulenes */ + len_stereo0D ++; + } + } + + if ( !bItemIsOver || i != num_atoms || s && p != s ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong number of bonds"); + goto bypass_end_of_INChI_plain; + } + } + + + /* + Search for coordinates block (plain) + */ + + /*p = szLine;*/ + sToken = sStructHdrPlnRevXYZ; + lToken = sizeof(sStructHdrPlnRevXYZ)-1; + + + /* search for sToken in the line; load next segments of the line if sToken has not found */ + p = FindToken( inp_file, &ir.bTooLongLine, sToken, lToken, szLine, sizeof(szLine), p, &res ); + + if ( !p ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Missing atom coordinates data"); + goto bypass_end_of_INChI_plain; + } + else + { + /* Coordinates block started */ + if ( pszCoord = (MOL_COORD*) inchi_malloc(inchi_max(num_atoms,1) * sizeof(MOL_COORD)) ) + { + memset( pszCoord, ' ', inchi_max(num_atoms,1) * sizeof(MOL_COORD)); + } + else + { + num_atoms = INCHI_INP_FATAL_RET; /* allocation error */ + *err = INCHI_INP_FATAL_ERR; + TREAT_ERR (*err, 0, "Out of RAM"); + goto bypass_end_of_INChI_plain; + } + + i = 0; + res2 = bTooLongLine2 = -1; + bItemIsOver = (s = strchr( p, '/') ) || !ir.bTooLongLine; + + while ( i < num_atoms ) + { + + p = LoadLine( inp_file, &ir.bTooLongLine, &bItemIsOver, &s, + szLine, sizeof(szLine), INCHI_LINE_ADD, p, &res ); + + if ( i >= num_atoms || s && p >= s ) { + break; /* end of bonds (plain) */ + } + + /* coord, first char */ + if ( *p == ';' ) + { + for ( k = 0; k < NUM_COORD; k ++ ) + { + pszCoord[i][LEN_COORD*k + 4] = '0'; + } + p ++; + i ++; + continue; + } + + for ( k = 0; k < 3; k ++ ) + { + double xyz; + bNonZeroXYZ = 0; + if ( *p == ';' ) + { + pszCoord[i][LEN_COORD*k + 4] = '0'; + xyz = 0.0; + } else + if ( *p == ',' ) + { + /* empty */ + pszCoord[i][LEN_COORD*k + 4] = '0'; + xyz = 0.0; + p ++; + } + else + { + xyz = strtod( p, &q ); + bNonZeroXYZ = fabs(xyz) > MIN_BOND_LENGTH; + if ( q != NULL ) { + memcpy( pszCoord[i]+LEN_COORD*k, p, q-p ); + if ( *q == ',' ) + q ++; + p = q; + } + else + pszCoord[i][LEN_COORD*k + 4] = '0'; + } + + switch( k ) + { + case 0: + atom[i].x = xyz; + b2D |= bNonZeroXYZ; + break; + case 1: + atom[i].y = xyz; + b2D |= bNonZeroXYZ; + break; + case 2: + b3D |= bNonZeroXYZ; + atom[i].z = xyz; + break; + } + } + + if ( *p == ';' ) + { + p ++; /* end of this triple of coordinates */ + i ++; + } + else + { + num_atoms = INCHI_INP_ERROR_RET; /* error in input data: atoms, bonds & coord must be present together */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong atom coordinates data"); + goto bypass_end_of_INChI_plain; + } + } + + if ( !bItemIsOver || s && p != s || i != num_atoms ) + { + num_atoms = INCHI_INP_ERROR_RET; /* error */ + *err = INCHI_INP_ERROR_ERR; + TREAT_ERR (*err, 0, "Wrong number of coordinates"); + goto bypass_end_of_INChI_plain; + } + } /* end of coordinates */ + + + /* + Set special valences and implicit H (xml) + */ + + b23D = b2D | b3D; + b2D = b3D = 0; + if ( at ) + { + if ( !*at ) + { + int a1, a2, n1, n2, valence; + int chem_bonds_valence; + int nX=0, nY=0, nZ=0, nXYZ; + *at = atom; + + /* special valences */ + + for ( bNonMetal = 0; bNonMetal < 1; bNonMetal ++ ) + { + + for ( a1 = 0; a1 < num_atoms; a1 ++ ) + { + + int num_bond_type[MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE + 1]; + int bHasMetalNeighbor=0; + + memset( num_bond_type, 0, sizeof(num_bond_type) ); + + valence = AT_BONDS_VAL(atom, a1); /* save atom valence if available */ + AT_BONDS_VAL(atom, a1) = 0; + + atom[a1].orig_at_number = a1+1; + + nX = nY = nZ = 0; + + for ( n1 = 0; n1 < AT_NUM_BONDS(atom[a1]); n1 ++ ) /*AT_NUM_BONDS(AT) ==> (AT).valence */ + { + bond_type = atom[a1].bond_type[n1] - MIN_INPUT_BOND_TYPE; + if ( bond_type < 0 || bond_type > MAX_INPUT_BOND_TYPE - MIN_INPUT_BOND_TYPE ) + { + bond_type = 0; + TREAT_ERR (*err, 0, "Unknown bond type in InChI aux assigned as a single bond"); + } + + num_bond_type[ bond_type ] ++; + nNumBonds ++; + if ( b23D ) + { + neigh = atom[a1].neighbor[n1]; + nX |= (fabs(atom[a1].x - atom[neigh].x) > MIN_BOND_LENGTH); + nY |= (fabs(atom[a1].y - atom[neigh].y) > MIN_BOND_LENGTH); + nZ |= (fabs(atom[a1].z - atom[neigh].z) > MIN_BOND_LENGTH); + } + } + + chem_bonds_valence = 0; + for ( n1 = 0; MIN_INPUT_BOND_TYPE + n1 <= 3 && MIN_INPUT_BOND_TYPE + n1 <= MAX_INPUT_BOND_TYPE; n1 ++ ) + { + chem_bonds_valence += (MIN_INPUT_BOND_TYPE + n1) * num_bond_type[n1]; + } + + if ( MIN_INPUT_BOND_TYPE <= INCHI_BOND_TYPE_ALTERN && INCHI_BOND_TYPE_ALTERN <= MAX_INPUT_BOND_TYPE && + ( n2 = num_bond_type[INCHI_BOND_TYPE_ALTERN-MIN_INPUT_BOND_TYPE] ) ) + { + + /* accept input aromatic bonds for now */ + + switch ( n2 ) + { + case 2: + chem_bonds_valence += 3; /* =A- */ + break; + + case 3: + chem_bonds_valence += 4; /* =A< */ + break; + + default: + /* if 1 or >= 4 aromatic bonds then replace such bonds with single bonds */ + for ( n1 = 0; n1 < AT_NUM_BONDS(atom[a1]); n1 ++ ) /* AT_NUM_BONDS(AT) ==> (AT).valence */ + { + if ( atom[a1].bond_type[n1] == INCHI_BOND_TYPE_ALTERN ) + { + AT_NUMB *p1; + a2 = atom[a1].neighbor[n1]; + p1 = is_in_the_list( atom[a2].neighbor, (AT_NUMB)a1, AT_NUM_BONDS(atom[a2]) ); /*AT_NUM_BONDS(AT) ==> (AT).valence*/ + if ( p1 ) + { + atom[a1].bond_type[n1] = + atom[a2].bond_type[p1-atom[a2].neighbor] = INCHI_BOND_TYPE_SINGLE; + } + else + { + *err = -2; /* Program error */ + TREAT_ERR (*err, 0, "Program error interpreting InChI aux"); + num_atoms = 0; + goto bypass_end_of_INChI_plain; /* no structure */ + } + } + } + + chem_bonds_valence += n2; + *err |= 32; /* Unrecognized aromatic bond(s) replaced with single */ + TREAT_ERR (*err, 0, "Atom has 1 or more than 3 aromatic bonds"); + break; + } + } + + /* added 2006-07-19 to process aromatic bonds same way as from molfile */ + if ( n2 && !valence ) + { + int num_H = NUMH(atom, a1); /* only isotopic */ + int chem_valence = chem_bonds_valence; + int bUnusualValenceArom = + detect_unusual_el_valence( (int)atom[a1].el_number, atom[a1].charge, + atom[a1].radical, chem_valence, + num_H, atom[a1].valence ); + int bUnusualValenceNoArom = + detect_unusual_el_valence( (int)atom[a1].el_number, atom[a1].charge, + atom[a1].radical, chem_valence-1, + num_H, atom[a1].valence ); + +#if ( CHECK_AROMBOND2ALT == 1 ) + if ( bUnusualValenceArom && !bUnusualValenceNoArom && 0 == nBondsValToMetal( atom, a1) ) +#else + if ( bUnusualValenceArom && !bUnusualValenceNoArom ) +#endif + + { + /* typically NH in 5-member aromatic ring */ + chem_bonds_valence --; + } + } + else if ( n2 && valence ) + { + /* atom has aromatic bonds AND the chemical valence is known */ + int num_H = NUMH(atom, a1); + int chem_valence = chem_bonds_valence + num_H; + if ( valence == chem_valence-1 ) { + /* typically NH in 5-member aromatic ring */ + chem_bonds_valence --; + } + } + + atom[a1].chem_bonds_valence = chem_bonds_valence; + + atom[a1].num_H = get_num_H( atom[a1].elname, + atom[a1].num_H, + atom[a1].num_iso_H, + atom[a1].charge, + atom[a1].radical, + atom[a1].chem_bonds_valence, + valence, + 0, + bDoNotAddH, + bHasMetalNeighbor ); + } + } + + nNumBonds /= 2; + + if ( b23D && nNumBonds ) + { + nXYZ = nX+nY+nZ; + b2D = (nXYZ > 0); + b3D = (nXYZ == 3); + *num_dimensions = b3D? 3 : b2D? 2 : 0; + *num_bonds = nNumBonds; + } + + /*======= 0D parities =================================*/ + + for ( i = 0; i < len_stereo0D; i ++ ) + { + AT_NUMB *p1, *p2; + int sb_ord_from_a1 = -1, sb_ord_from_a2 = -1, bEnd1 = 0, bEnd2 = 0; + + switch( atom_stereo0D[i].type ) + { + + case INCHI_StereoType_Tetrahedral: + a1 = atom_stereo0D[i].central_atom; + if ( atom_stereo0D[i].parity && (AT_NUM_BONDS(atom[a1]) == 3 || AT_NUM_BONDS(atom[a1]) == 4) ) + { + int ii, kk = 0; + if ( AT_NUM_BONDS(atom[a1]) == 3 ) + atom_stereo0D[i].neighbor[kk++] = a1; + for ( ii = 0; ii < AT_NUM_BONDS(atom[a1]); ii ++ ) + atom_stereo0D[i].neighbor[kk++] = atom[a1].neighbor[ii]; + } + + break; + + case INCHI_StereoType_DoubleBond: +#define MAX_CHAIN_LEN 20 + a1 = atom_stereo0D[i].neighbor[1]; + a2 = atom_stereo0D[i].neighbor[2]; + p1 = is_in_the_list( atom[a1].neighbor, (AT_NUMB)a2, AT_NUM_BONDS(atom[a1]) ); + p2 = is_in_the_list( atom[a2].neighbor, (AT_NUMB)a1, AT_NUM_BONDS(atom[a2]) ); + if ( !p1 || !p2 ) + { + atom_stereo0D[i].type = INCHI_StereoType_None; + atom_stereo0D[i].central_atom = NO_ATOM; + atom_stereo0D[i].neighbor[0] = + atom_stereo0D[i].neighbor[3] = -1; + *err |= 64; /* Error in cumulene stereo */ + TREAT_ERR (*err, 0, "0D stereobond not recognized"); + break; + } + + /* streobond, allene, or cumulene */ + + sb_ord_from_a1 = p1 - atom[a1].neighbor; + sb_ord_from_a2 = p2 - atom[a2].neighbor; + + if ( AT_NUM_BONDS(atom[a1]) == 2 && + atom[a1].bond_type[0] + atom[a1].bond_type[1] == 2*INCHI_BOND_TYPE_DOUBLE && + 0 == inchi_NUMH2(atom, a1) && + (AT_NUM_BONDS(atom[a2]) != 2 || + atom[a2].bond_type[0] + atom[a2].bond_type[1] != 2*INCHI_BOND_TYPE_DOUBLE ) ) + { + bEnd2 = 1; /* a2 is the end-atom, a1 is middle atom */ + } + + if ( AT_NUM_BONDS(atom[a2]) == 2 && + atom[a2].bond_type[0] + atom[a2].bond_type[1] == 2*INCHI_BOND_TYPE_DOUBLE && + 0 == inchi_NUMH2(atom, a2) && + (AT_NUM_BONDS(atom[a1]) != 2 || + atom[a1].bond_type[0] + atom[a1].bond_type[1] != 2*INCHI_BOND_TYPE_DOUBLE ) ) + { + bEnd1 = 1; /* a1 is the end-atom, a2 is middle atom */ + } + + if ( bEnd2 + bEnd1 == 1 ) + { + /* allene or cumulene */ + + AT_NUMB chain[MAX_CHAIN_LEN+1], prev, cur, next; + + if ( bEnd2 && !bEnd1 ) + { + cur = a1; + a1 = a2; + a2 = cur; + sb_ord_from_a1 = sb_ord_from_a2; + } + + sb_ord_from_a2 = -1; + cur = a1; + next = a2; + len = 0; + chain[len++] = cur; + chain[len++] = next; + + while ( len < MAX_CHAIN_LEN ) + { + /* arbitrary very high upper limit to prevent infinite loop */ + + prev = cur; + cur = next; + /* follow double bond path && avoid going back */ + if ( AT_NUM_BONDS(atom[cur]) == 2 && + atom[cur].bond_type[0]+atom[cur].bond_type[1] == 2*INCHI_BOND_TYPE_DOUBLE && + 0 == inchi_NUMH2(atom, cur) ) + { + next = atom[cur].neighbor[atom[cur].neighbor[0] == prev]; + chain[len++] = next; + } + else + { + break; + } + } + if ( len > 2 && + (p2 = is_in_the_list( atom[cur].neighbor, (AT_NUMB)prev, AT_NUM_BONDS(atom[cur]))) ) + { + sb_ord_from_a2 = p2 - atom[cur].neighbor; + a2 = cur; + /* by design we need to pick up the first non-stereo-bond-neighbor as "sn"-atom */ + atom_stereo0D[i].neighbor[0] = atom[a1].neighbor[sb_ord_from_a1 == 0]; + atom_stereo0D[i].neighbor[1] = a1; + atom_stereo0D[i].neighbor[2] = a2; + atom_stereo0D[i].neighbor[3] = atom[a2].neighbor[sb_ord_from_a2 == 0]; + + if ( len % 2 ) + { + atom_stereo0D[i].central_atom = chain[len/2]; + atom_stereo0D[i].type = INCHI_StereoType_Allene; + } + else + { + atom_stereo0D[i].central_atom = NO_ATOM; + } + } + else + { + /* error */ + atom_stereo0D[i].type = INCHI_StereoType_None; + atom_stereo0D[i].central_atom = NO_ATOM; + atom_stereo0D[i].neighbor[0] = + atom_stereo0D[i].neighbor[3] = -1; + *err |= 64; /* Error in cumulene stereo */ + TREAT_ERR (*err, 0, "Cumulene stereo not recognized (0D)"); + } +#undef MAX_CHAIN_LEN + } + else + { + /****** a normal possibly stereogenic bond -- not an allene or cumulene *******/ + /* by design we need to pick up the first non-stereo-bond-neighbor as "sn"-atom */ + sb_ord_from_a1 = p1 - atom[a1].neighbor; + sb_ord_from_a2 = p2 - atom[a2].neighbor; + atom_stereo0D[i].neighbor[0] = atom[a1].neighbor[p1 == atom[a1].neighbor]; + atom_stereo0D[i].neighbor[3] = atom[a2].neighbor[p2 == atom[a2].neighbor]; + atom_stereo0D[i].central_atom = NO_ATOM; + } + + if ( atom_stereo0D[i].type != INCHI_StereoType_None && + sb_ord_from_a1 >= 0 && sb_ord_from_a2 >= 0 && + ATOM_PARITY_WELL_DEF( SB_PARITY_2(atom_stereo0D[i].parity) ) ) + { + /* Detected well-defined disconnected stereo + * locate first non-metal neighbors */ + + int a, n, j, /* k,*/ sb_ord, cur_neigh, min_neigh; + + for ( k = 0; k < 2; k ++ ) + { + a = k? atom_stereo0D[i].neighbor[2] : atom_stereo0D[i].neighbor[1]; + sb_ord = k? sb_ord_from_a2 : sb_ord_from_a1; + min_neigh = num_atoms; + for ( n = j = 0; j < AT_NUM_BONDS(atom[a]); j ++ ) + { + cur_neigh = atom[a].neighbor[j]; + if ( j != sb_ord && !IS_METAL_ATOM(atom, cur_neigh) ) + { + min_neigh = inchi_min( cur_neigh, min_neigh ); + } + } + if ( min_neigh < num_atoms ) { + atom_stereo0D[i].neighbor[k?3:0] = min_neigh; + } else { + TREAT_ERR (*err, 0, "Cannot find non-metal stereobond neighor (0D)"); + } + } + } + + break; + } + } + /* end of 0D parities extraction */ +/*exit_cycle:;*/ + } + + /* Transfer atom_stereo0D[] to atom[] */ + if ( len_stereo0D ) + { + Extract0DParities( atom, num_atoms, atom_stereo0D, len_stereo0D, + pStrErr, err, vABParityUnknown ); + } + + if ( pInpAtomFlags ) + { + /* save chirality flag */ + *pInpAtomFlags |= InpAtomFlags; + } + } + else if ( atom ) + { + inchi_free( atom ); + atom = NULL; + } + +#if ( FIX_READ_AUX_MEM_LEAK == 1 ) + /* 2005-08-04 avoid memory leak */ + if ( atom_stereo0D ) /* && !(stereo0D && *stereo0D == atom_stereo0D) ) */ + { + FreeInchi_Stereo0D( &atom_stereo0D ); + } +#endif + + if ( szCoord ) + { + *szCoord = pszCoord; + pszCoord = NULL; + } + else if ( pszCoord ) + { + inchi_free( pszCoord ); + pszCoord = NULL; + } + + goto bypass_end_of_INChI_plain; + /*return num_atoms;*/ + } + } /* while ( 0 < (res = inchi_ios_getsTab( szLine, sizeof(szLine)-1, inp_file, &ir.bTooLongLine ) ) ) */ + + /* End of structure reading cycle */ + if ( atom_stereo0D ) + FreeInchi_Stereo0D( &atom_stereo0D ); + if ( res <= 0 ) + { + if ( *err == INCHI_INP_ERROR_ERR ) + return num_atoms; + *err = INCHI_INP_EOF_ERR; + return INCHI_INP_EOF_RET; /* no more data */ + } + + + +bypass_end_of_INChI_plain: + + /* Cleanup */ + if ( num_atoms == INCHI_INP_ERROR_RET && atom_stereo0D ) + FreeInchi_Stereo0D( &atom_stereo0D ); + while ( ir.bTooLongLine && + 0 < inchi_ios_getsTab1( szLine, sizeof(szLine)-1, inp_file, &ir.bTooLongLine ) ) + ; + + + return num_atoms; +#undef AT_NUM_BONDS +#undef AT_NUMB +#undef is_in_the_list +#undef inchi_NUMH2 + +#undef MoreParms +#undef INPUT_FILE +#undef CreateInpAtom +#undef AT_BONDS_VAL +#undef ISOLATED_ATOM +#undef NUM_ISO_Hk +#undef IS_METAL_ATOM +} + +/* + Find and interpret structure header +*/ +void find_and_interpret_structure_header( char *szLine, + char *pSdfLabel, + char *pSdfValue, + long *Id, + int hlen, + ReadINCHI_CtlData *ir) +{ +int len; +char *p, *q; +static const char sStructHdrPlnNoLblVal[] = " is missing"; + + + p = szLine + hlen; + ir->longID = 0; + + /* structure number */ + ir->longID = strtol( p, &q, 10 ); + if ( q && q[0] == '.' && q[1] == ' ' ) + p = q+2; + p = p + strspn( p, " \n\r" ); + + if ( pSdfLabel ) + pSdfLabel[0] = '\0'; + if ( pSdfValue ) + pSdfValue[0] = '\0'; + + if ( *p ) + { + /* has label name */ + + /*p ++;*/ + if ( q = strchr( p, '=' ) ) + { + + /* '=' separates label name from the value */ + len = inchi_min( q-p+1, MAX_SDF_HEADER-1); + + if ( pSdfLabel ) + { + mystrncpy( pSdfLabel, p, len ); + lrtrim( pSdfLabel, &len ); + } + + p = q+1; + q = p + (int)strlen( p ); + + if ( q-p > 0 ) + { + len = inchi_min( q-p+1, MAX_SDF_VALUE-1); + if ( pSdfValue ) + { + mystrncpy( pSdfValue, p, len ); + } + p = q; + } + } + else if ( q = strstr( p, sStructHdrPlnNoLblVal ) ) + { + len = inchi_min( q-p+1, MAX_SDF_HEADER-1); + if ( pSdfLabel ) { + mystrncpy( pSdfLabel, p, len ); + } + p = q+1; + } + } + + if ( Id ) + *Id = ir->longID; + + ir->bHeaderRead = 1; + ir->bErrorMsg = ir->bRestoreInfo = 0; + + return; +} diff --git a/INCHI-1-SRC/INCHI_BASE/src/readinch.h b/INCHI-1-SRC/INCHI_BASE/src/readinch.h new file mode 100644 index 0000000..41b492e --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/readinch.h @@ -0,0 +1,110 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _READINCH_H_ +#define _READINCH_H_ + +#define INPUT_FILE INCHI_IOSTREAM + +#define MIN_BOND_LENGTH (1.0e-6) +#define INCHI_LINE_LEN 262144 /*32767*/ /*512*/ /*1024*/ /*256*/ +#define INCHI_LINE_ADD 32767 /*384*/ /*128*/ /*64*/ + + +/* Convenience storage for InChI read control data */ +typedef struct ReadINCHI_CtlData +{ + long longID; + int bTooLongLine; + int bHeaderRead; + int bErrorMsg; + int bRestoreInfo; +} +ReadINCHI_CtlData; + + +/* +Note: +(INCHI_LINE_LEN - INCHI_LINE_ADD) > (length of the longest item: szCoord) = 33 +*/ + +char *FindToken( INCHI_IOSTREAM *inp_molfile, + int *bTooLongLine, + const char *sToken, + int lToken, + char *szLine, + int nLenLine, + char *p, + int *res ); +char *LoadLine( INPUT_FILE *inp_molfile, + int *bTooLongLine, + int *bItemIsOver, + char **s, + char *szLine, + int nLenLine, + int nMinLen2Load, + char *p, + int *res ); +inchi_Stereo0D *CreateInchi_Stereo0D( int num_stereo0D ); +void FreeInchi_Stereo0D( inchi_Stereo0D **stereo0D ); +int Extract0DParities( inp_ATOM *at, + int nNumAtoms, + inchi_Stereo0D *stereo0D, + int num_stereo0D, + char *pStrErr, + int *err, + int vABParityUnknown); +int InchiToInpAtom ( INCHI_IOSTREAM *inp_molfile, + MOL_COORD **szCoord, + int bDoNotAddH, + int vABParityUnknown, + INPUT_TYPE nInputType, + inp_ATOM **at, + int max_num_at, + int *num_dimensions, + int *num_bonds, + char *pSdfLabel, + char *pSdfValue, + long *Id, + INCHI_MODE *pInpAtomFlags, + int *err, + char *pStrErr ); + + +#endif /* _READINCH_H_ */ diff --git a/INCHI-1-SRC/INCHI_BASE/src/runichi.c b/INCHI-1-SRC/INCHI_BASE/src/runichi.c new file mode 100644 index 0000000..6e8bc4e --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/runichi.c @@ -0,0 +1,1860 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +/* + General processing procedures + +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "mode.h" +#include "ichitime.h" +#ifndef COMPILE_ANSI_ONLY +#include +#endif + +#include "ichimain.h" +#include "ichi_io.h" +#include "mol_fmt.h" +#include "inchi_api.h" +#include "readinch.h" +#include "ichicant.h" +#ifdef TARGET_LIB_FOR_WINCHI +#include "../../../IChI_lib/src/ichi_lib.h" +#include "inchi_api.h" +#endif +#include "inchi_gui.h" +#include "readinch.h" + + +extern int DisplayTheWholeStructure( struct tagCANON_GLOBALS *pCG, + struct tagINCHI_CLOCK *ic, + struct tagStructData *sd, + INPUT_PARMS *ip, + char *szTitle, + INCHI_IOSTREAM *inp_file, + INCHI_IOSTREAM *log_file, + ORIG_ATOM_DATA *orig_inp_data, + long num_inp, + int iINChI, + int bShowStruct, + int bINCHI_LIB_Flag ); +extern int DisplayStructure( struct tagCANON_GLOBALS *pCG, + inp_ATOM *at, + int num_at, int num_removed_H, + int bAdd_DT_to_num_H, + int nNumRemovedProtons, + NUM_H *nNumRemovedProtonsIsotopic, + int bIsotopic, + int j /*bTautomeric*/, + INChI **cur_INChI, INChI_Aux **cur_INChI_Aux, + int bAbcNumbers, + DRAW_PARMS *dp, + INCHI_MODE nMode, + char *szTitle ); + +/* Local functions */ +static int DoOneStructureEarlyPreprocessing( long num_inp, STRUCT_DATA *sd, INPUT_PARMS *ip, + INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, + INCHI_IOSTREAM *out_file, INCHI_IOSTREAM *prb_file, + ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data ); +static int OrigAtData_SaveMolfile( ORIG_ATOM_DATA *orig_inp_data, + STRUCT_DATA *sd, + INPUT_PARMS *ip, + long num_inp, + INCHI_IOSTREAM *out_file ); +ORIG_STRUCT *OrigAtData_StoreNativeInput( CANON_GLOBALS *pCG, int *nRet, + STRUCT_DATA *sd, INPUT_PARMS *ip, + ORIG_ATOM_DATA *orig_inp_data, + ORIG_STRUCT *pOrigStruct); +void prepare_saveopt_bits( unsigned char *save_opt_bits, INPUT_PARMS *ip ); +void DisplayOrigAndResultStructuresAndComponents( int nRet, INCHI_CLOCK *ic, CANON_GLOBALS *pCG, + STRUCT_DATA *sd, INPUT_PARMS *ip, char *szTitle, + PINChI2 *pINChI[INCHI_NUM], + PINChI_Aux2 *pINChI_Aux[INCHI_NUM], + INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, + INCHI_IOSTREAM *out_file, + ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data, + long num_inp, int maxINChI, + COMP_ATOM_DATA composite_norm_data[INCHI_NUM][TAUT_NUM+1]); +void SaveOkProcessedMolfile( int nRet, STRUCT_DATA *sd, INPUT_PARMS *ip, + INCHI_IOSTREAM *prb_file, INCHI_IOSTREAM *inp_file); +static int snapshot_data_structs_passed_to_ProcessOneStructure( + struct tagINCHI_CLOCK *ic, struct tagINCHI_CLOCK *ic2, + struct tagCANON_GLOBALS *CG, struct tagCANON_GLOBALS *CG2, + STRUCT_DATA *sd, STRUCT_DATA *sd2, + INPUT_PARMS *ip, INPUT_PARMS *ip2, + PINChI2 *pINChI2[INCHI_NUM], PINChI2 *pINChI22[INCHI_NUM], + PINChI_Aux2 *pINChI_Aux2[INCHI_NUM], PINChI_Aux2 *pINChI_Aux22[INCHI_NUM], + INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *inp_file2, + INCHI_IOSTREAM *log_file, INCHI_IOSTREAM *log_file2, + INCHI_IOSTREAM *out_file, INCHI_IOSTREAM *out_file2, + INCHI_IOSTREAM *prb_file, INCHI_IOSTREAM *prb_file2, + ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *orig_inp_data2, + ORIG_ATOM_DATA *prep_inp_data, ORIG_ATOM_DATA *prep_inp_data2, + INCHI_IOSTREAM_STRING *strbuf, INCHI_IOSTREAM_STRING *strbuf2 + ); + + +/* Callbacks */ +/* Console user issued CTRL+C etc. */ +int (*ConsoleQuit)(void) = NULL; +int (*UserAction)(void) = NULL; + + +/********************************************** + * output " L=V" or " L missing" or "" + * The fprintf format string must contain %s%s%s%s + */ +const char gsMissing[] = "is missing"; +const char gsEmpty[] = ""; +const char gsSpace[] = " "; +const char gsEqual[] = "="; + + +/* + Process a portion of input data (molecule, InChI string, ...) + in a relevant way (generate InChI, restore molecule by InChI ) +*/ +int ProcessOneStructure( INCHI_CLOCK *ic, + CANON_GLOBALS *pCG, + STRUCT_DATA *sd, + INPUT_PARMS *ip, + char *szTitle, + PINChI2 *pINChI[INCHI_NUM], + PINChI_Aux2 *pINChI_Aux[INCHI_NUM], + INCHI_IOSTREAM *inp_file, + INCHI_IOSTREAM *log_file, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM *prb_file, + ORIG_ATOM_DATA *orig_inp_data, + ORIG_ATOM_DATA *prep_inp_data, + long num_inp, + INCHI_IOSTREAM_STRING *strbuf, + unsigned char save_opt_bits) +{ + int nRet = 0, + nRet1, i, k, + maxINChI=0, + bSortPrintINChIFlags=0; + COMP_ATOM_DATA + composite_norm_data[INCHI_NUM][TAUT_NUM+1]; /* [0]:non-taut, + [1]:taut, + [2]:intermediate taut struct */ + NORM_CANON_FLAGS ncFlags; + NORM_CANON_FLAGS *pncFlags = &ncFlags; + ORIG_STRUCT OrigStruct; + ORIG_STRUCT *pOrigStruct = NULL; + int err, ret1 = 0; + +#if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) + int /*ret1=0,*/ret2=0; +#endif + + int is_polymer = orig_inp_data && + orig_inp_data->polymer && + orig_inp_data->polymer->n > 0 && + orig_inp_data->polymer->valid; + int is_polymer2inchi = is_polymer && + (ip->nInputType == INPUT_MOLFILE || ip->nInputType == INPUT_SDFILE); + + + /* 1. Preliminary work */ + + sd->bUserQuitComponent = 0; + sd->bUserQuitComponentDisplay = 0; + memset( composite_norm_data, 0, sizeof(composite_norm_data) ); + memset( pncFlags, 0, sizeof(*pncFlags) ); + + /* For experimental purposes only */ + ret1 = DoOneStructureEarlyPreprocessing( num_inp, sd, ip, + inp_file, log_file, out_file, prb_file, + orig_inp_data, prep_inp_data); + if ( ret1 ) + goto exit_function; + + if ( is_polymer && !is_polymer2inchi ) + /* Do this when restoring polymer structure from InChI */ + OrigAtData_CheckAndMakePolymerPhaseShifts( orig_inp_data->polymer, + orig_inp_data->at, + orig_inp_data->num_inp_atoms, + &orig_inp_data->num_inp_bonds); + + ret1 = OrigAtData_SaveMolfile( orig_inp_data, sd, ip, num_inp, out_file ); + if ( ret1 ) + goto exit_function; + + pOrigStruct = &OrigStruct; + memset( pOrigStruct, 0, sizeof(*pOrigStruct)); + + OrigAtData_StoreNativeInput( pCG, &nRet, sd, ip, orig_inp_data, pOrigStruct ); + + + /* 2. Create INChI for the whole disconnected or original structure */ + + if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) + { + nRet1 = CreateOneStructureINChI( pCG, ic, sd, ip, + szTitle, pINChI,pINChI_Aux, INCHI_BAS, + inp_file, log_file, out_file, prb_file, + orig_inp_data, prep_inp_data, + composite_norm_data, + num_inp, strbuf, pncFlags ); + + nRet = inchi_max(nRet, nRet1); + + if ( is_polymer2inchi ) + { + int polymer_repr_type = OrigAtDataPolymer_GetRepresentation( orig_inp_data->polymer ); + + if ( polymer_repr_type == POLYMER_REPRESENTATION_STRUCTURE_BASED ) + { + /* Temporarily copy ptr to polymer data to prep_inp_data */ + prep_inp_data->polymer = orig_inp_data->polymer; + + + OrigAtDataPolymer_CollectPhaseShiftBonds( prep_inp_data, /* NB: not orig_inp_data! */ + &(composite_norm_data[INCHI_BAS][TAUT_YES]), + &err, sd->pStrErrStruct ); + if ( err ) + ret1 = _IS_ERROR; + nRet = inchi_max(nRet, ret1); + prep_inp_data->polymer = NULL; /* remove temp copied */ + } + } + } + if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) + maxINChI = 1; + + + /* 3. Create INChI for the whole metal-reconnected structure */ + + if ( nRet != _IS_FATAL && nRet != _IS_ERROR && + (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) && + (ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) + { + + nRet1 = CreateOneStructureINChI( pCG, ic, sd, ip, + szTitle, pINChI, pINChI_Aux, INCHI_REC, + inp_file, log_file, out_file, prb_file, + orig_inp_data, prep_inp_data, + composite_norm_data, + num_inp, strbuf, pncFlags); + nRet = inchi_max(nRet, nRet1); + + if ( is_polymer2inchi ) + { + int err, ret1=0; + /* temporarily copy ptr to polymer data to prep_inp_data */ + prep_inp_data->polymer = orig_inp_data->polymer; + + OrigAtDataPolymer_CollectPhaseShiftBonds( prep_inp_data, /* NB: not orig_inp_data! */ + &(composite_norm_data[INCHI_REC][TAUT_YES]), + &err, sd->pStrErrStruct ); + if ( err ) + ret1 = _IS_ERROR; + nRet = inchi_max(nRet, ret1); + prep_inp_data->polymer = NULL; /* remove temp copied */ + } + if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) + maxINChI = 2; + } + + if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) + { + if ( (sd->bChiralFlag & FLAG_INP_AT_CHIRAL) && + (ip->nMode & REQ_MODE_STEREO) && + !(ip->nMode & (REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO)) && + !bIsStructChiral( pINChI, sd->num_components ) ) + { + WarningMessage(sd->pStrErrStruct, "Not chiral"); + } + if ( !sd->bUserQuitComponent && !sd->bUserQuit ) + { + nRet1 = TreatCreateINChIWarning( sd, ip, prep_inp_data, num_inp, + inp_file, log_file, out_file, prb_file ); + nRet = inchi_max(nRet, nRet1); + } + } + + + /* 4. Sort and print INChI for the whole structure */ + + prepare_saveopt_bits( &save_opt_bits, ip); + if ( nRet != _IS_FATAL && nRet != _IS_ERROR ) + { + nRet1 = SortAndPrintINChI( pCG, out_file, strbuf, log_file, ip, + orig_inp_data, prep_inp_data, + composite_norm_data, + pOrigStruct, + sd->num_components, sd->num_non_taut, sd->num_taut, + sd->bTautFlags, sd->bTautFlagsDone, pncFlags, + num_inp, pINChI, pINChI_Aux, + &bSortPrintINChIFlags, save_opt_bits); + } + + + /* 5. Post-process */ + + DisplayOrigAndResultStructuresAndComponents( nRet,ic, pCG, sd, ip, szTitle, + pINChI, pINChI_Aux, + inp_file, log_file, out_file, + orig_inp_data, prep_inp_data, + num_inp,maxINChI, + composite_norm_data ); + + SaveOkProcessedMolfile( nRet, sd, ip, prb_file, inp_file ); + + /* Cleanup */ + for ( i = 0; i < INCHI_NUM; i ++ ) + for ( k = 0; k < TAUT_NUM+1; k ++ ) + FreeCompAtomData( &composite_norm_data[i][k] ); + FreeOrigStruct( pOrigStruct); + +exit_function: + return nRet; +} + + +/* + Early preprocessing stage: + used if defined REMOVE_ION_PAIRS_ORIG_STRU or UNDERIVATIZE or RING2CHAIN +*/ +int DoOneStructureEarlyPreprocessing( long num_inp, STRUCT_DATA *sd, INPUT_PARMS *ip, + INCHI_IOSTREAM *inp_file, INCHI_IOSTREAM *log_file, + INCHI_IOSTREAM *out_file, INCHI_IOSTREAM *prb_file, + ORIG_ATOM_DATA *orig_inp_data, ORIG_ATOM_DATA *prep_inp_data ) +{ +#if ( REMOVE_ION_PAIRS_ORIG_STRU == 1 ) + fix_odd_things( orig_inp_data->num_inp_atoms, orig_inp_data->at, 0, ip->bFixNonUniformDraw ); +#endif +#if ( UNDERIVATIZE == 1 ) + if ( ip->bUnderivatize && 0 > (ret2=underivatize( orig_inp_data )) ) + { + long num_inp2 = num_inp; + AddErrorMessage(sd->pStrErrStruct, "Underivatization error"); + sd->nStructReadError = 99; + sd->nErrorType = _IS_ERROR; + TreatErrorsInReadTheStructure( sd, ip, LOG_MASK_ALL, inp_file, log_file, out_file, prb_file, + prep_inp_data, &num_inp2 ); + return _IS_ERROR; /* output only if derivatives found */ + } +#endif /* UNDERIVATIZE == 1 */ +#if ( RING2CHAIN == 1 ) + if ( ip->bRing2Chain && 0 > (ret1 = Ring2Chain( orig_inp_data )) ) + { + long num_inp2 = num_inp; + AddErrorMessage(sd->pStrErrStruct, "Ring to chain error"); + sd->nStructReadError = 99; + sd->nErrorType = _IS_ERROR; + nRet = _IS_ERROR; + TreatErrorsInReadTheStructure( sd, ip, + LOG_MASK_ALL, + inp_file, log_file, + out_file, prb_file, + prep_inp_data, &num_inp2 ); + return _IS_ERROR; /* output only if derivatives found */ + } +#endif /* RING2CHAIN == 1 */ +#if ( RING2CHAIN == 1 || UNDERIVATIZE == 1 ) /***** post v.1 feature *****/ + if ( ip->bIngnoreUnchanged && !ret1 && !ret2 ) + { + return _IS_SKIP; /* output only if derivatives or ring/chain found */ + } +#endif /* RING2CHAIN == 1 || UNDERIVATIZE == 1 */ + return 0; +} + + +/* + If requested, save input data to a Molfile instead of creating INChI + Also used for output in case of combination of options 'InChI2Struct' and 'OutputSDF' +*/ +int OrigAtData_SaveMolfile( ORIG_ATOM_DATA *orig_inp_data, + STRUCT_DATA *sd, + INPUT_PARMS *ip, + long num_inp, + INCHI_IOSTREAM *out_file ) +{ +int ret=0; + + if ( ! (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY) ) + return _IS_OKAY; + else + { + char szNumber[32]; + + sprintf( szNumber, "Structure #%ld", num_inp ); + ret = OrigAtData_WriteToSDfile( orig_inp_data, out_file, szNumber, NULL, + (sd->bChiralFlag&FLAG_INP_AT_CHIRAL) ? 1 : 0, + (ip->bINChIOutputOptions&INCHI_OUT_SDFILE_ATOMS_DT) ? 1 : 0, + ip->pSdfLabel, ip->pSdfValue ); + } + return ret; +} + + +/* + Optionally save native input data as 'OrigStruct' data package +*/ +ORIG_STRUCT * OrigAtData_StoreNativeInput( CANON_GLOBALS *pCG, int *nRet, + STRUCT_DATA *sd, INPUT_PARMS *ip, + ORIG_ATOM_DATA *orig_inp_data, + ORIG_STRUCT *pOrigStruct) +{ + + /* v. 1.05 always create and fill OrigStruc as it may be used to store e.g. polymer info */ + /* If normal AuxInfo is requested, create full reversibility information from native inp data + if ( ip->bINChIOutputOptions & (INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO)) + return NULL; */ + + if ( OrigStruct_FillOut( pCG, orig_inp_data, pOrigStruct, sd ) ) + { + AddErrorMessage(sd->pStrErrStruct, "Cannot interpret reversibility information"); + sd->nStructReadError = 99; + sd->nErrorType = _IS_ERROR; + *nRet = _IS_ERROR; + } + return pOrigStruct; +} + + +/* + Prepare SaveOpt bits +*/ +void prepare_saveopt_bits( unsigned char *save_opt_bits, INPUT_PARMS *ip ) +{ + if ( ip->nInputType != INPUT_INCHI ) + { + *save_opt_bits = 0; + if ( ip->bINChIOutputOptions & INCHI_OUT_SAVEOPT ) + { + if ( 0 != ( ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) + (*save_opt_bits) |= SAVE_OPT_RECMET; + if ( 0 != ( ip->nMode & REQ_MODE_BASIC) ) + (*save_opt_bits) |= SAVE_OPT_FIXEDH; + if ( 0 != ( ip->nMode & REQ_MODE_DIFF_UU_STEREO) ) + (*save_opt_bits) |= SAVE_OPT_SLUUD; + if ( 0 == (ip->nMode & (REQ_MODE_SB_IGN_ALL_UU | REQ_MODE_SC_IGN_ALL_UU)) ) + (*save_opt_bits) |= SAVE_OPT_SUU; + if ( 0 != (ip->bTautFlags & TG_FLAG_KETO_ENOL_TAUT) ) + (*save_opt_bits) |= SAVE_OPT_KET; + if ( 0 != (ip->bTautFlags & TG_FLAG_1_5_TAUT) ) + (*save_opt_bits) |= SAVE_OPT_15T; + /* Check if /SNon requested and turn OFF stereo bits if so */ + if ( ! (ip->nMode & REQ_MODE_STEREO) ) + { + (*save_opt_bits) &= ~SAVE_OPT_SUU; + (*save_opt_bits) &= ~SAVE_OPT_SLUUD; + } + } + } +} + + +/* + Display structures/components on screen +*/ +void DisplayOrigAndResultStructuresAndComponents( int nRet, + INCHI_CLOCK *ic, + CANON_GLOBALS *pCG, + STRUCT_DATA *sd, + INPUT_PARMS *ip, + char *szTitle, + PINChI2 *pINChI[INCHI_NUM], + PINChI_Aux2 *pINChI_Aux[INCHI_NUM], + INCHI_IOSTREAM *inp_file, + INCHI_IOSTREAM *log_file, + INCHI_IOSTREAM *out_file, + ORIG_ATOM_DATA *orig_inp_data, + ORIG_ATOM_DATA *prep_inp_data, + long num_inp, + int maxINChI, + COMP_ATOM_DATA composite_norm_data[INCHI_NUM][TAUT_NUM+1]) +{ + + + if ( ip->bDisplay ) ip->bDisplayCompositeResults = 1; /* v. 1.05 */ + +#ifndef COMPILE_ANSI_ONLY /* { */ + + /* Display equivalent components on original or preprocessed structure(s) */ +#ifndef TARGET_LIB_FOR_WINCHI + if ( nRet != _IS_FATAL && nRet != _IS_ERROR && /*ip->bDisplay &&*/ + (ip->bCompareComponents & CMP_COMPONENTS) && !sd->bUserQuit && !sd->bUserQuitComponent ) + { + int j, ret, ord; + int bDisplaySaved = ip->bDisplay; + ORIG_ATOM_DATA *inp_data; + AT_NUMB nEquSet; + for ( ord = -1; ord < INCHI_NUM; ord ++ ) + { + switch( ord ) { + case -1: + j = INCHI_BAS; /* preprocessed non-tautomeric */ + break; + case 0: + j = INCHI_REC; /* preprocessed tautomeric */ + break; + case 1: + j = -1; /* original input */ + break; + default: + continue; + } + inp_data = j < 0? orig_inp_data : prep_inp_data+j; + if ( inp_data && inp_data->num_inp_atoms && inp_data->at && + inp_data->nEquLabels && + inp_data->nNumEquSets ) { + for ( nEquSet = 1; nEquSet <= inp_data->nNumEquSets; nEquSet ++ ) { + ip->dp.nEquLabels = inp_data->nEquLabels; + ip->dp.nCurEquLabel = nEquSet; + ip->dp.nNumEquSets = inp_data->nNumEquSets; + ip->bDisplay = 1; /* force display if it was not requested */ + ret = DisplayTheWholeStructure( pCG,ic, sd, ip, szTitle, + inp_file, log_file, inp_data, + num_inp, j, 1 /*bShowStructure*/, 0 ); + ip->dp.nEquLabels = NULL; + ip->dp.nCurEquLabel = 0; + ip->dp.nNumEquSets = 0; + ip->bDisplay = bDisplaySaved; /* restore display option */ + if ( ret ) { + /* user pressed Esc */ + goto exit_loop; + } + } + } + } +exit_loop:; + } +#endif + + + /* Display composite results and equivalent components on composite results */ + if ( nRet != _IS_FATAL && nRet != _IS_ERROR && /*ip->bDisplay &&*/ ip->bDisplayCompositeResults ) + { + int iINChI; + for ( iINChI = 0; iINChI < maxINChI && !sd->bUserQuitComponentDisplay; iINChI ++ ) + { + DisplayTheWholeCompositeStructure( pCG, ic, ip, sd, num_inp, + iINChI, pINChI[iINChI], pINChI_Aux[iINChI], + orig_inp_data, prep_inp_data, composite_norm_data[iINChI] ); + } +#ifndef TARGET_LIB_FOR_WINCHI + if( !ip->bDisplay && sd->bUserQuitComponentDisplay ) + { + sd->bUserQuit = 1; + } +#endif + } + +#endif /* } COMPILE_ANSI_ONLY */ + + return; +} + + +/* + Special mode (option /PGO) : extract all good MOLfiles into the problem file; + do not extract any MOLfile that could not be processed. +*/ +void SaveOkProcessedMolfile( int nRet, STRUCT_DATA *sd, INPUT_PARMS *ip, + INCHI_IOSTREAM *prb_file, INCHI_IOSTREAM *inp_file) +{ + if ( ip->bSaveAllGoodStructsAsProblem && + nRet != _IS_FATAL && + nRet != _IS_ERROR && + prb_file && + prb_file->f && + 0L <= sd->fPtrStart && + sd->fPtrStart < sd->fPtrEnd ) + MolfileSaveCopy( inp_file, sd->fPtrStart, sd->fPtrEnd, prb_file->f, 0); + return; +} + + +/* + Generate InChI for the whole (may be multi-component) structure +*/ +int CreateOneStructureINChI( CANON_GLOBALS *pCG, + INCHI_CLOCK *ic, + STRUCT_DATA *sd, + INPUT_PARMS *ip, + char *szTitle, + PINChI2 *pINChI2[INCHI_NUM], + PINChI_Aux2 *pINChI_Aux2[INCHI_NUM], + int iINChI, + INCHI_IOSTREAM *inp_file, + INCHI_IOSTREAM *log_file, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM *prb_file, + ORIG_ATOM_DATA *orig_inp_data, + ORIG_ATOM_DATA *prep_inp_data, + COMP_ATOM_DATA composite_norm_data2[][TAUT_NUM+1], + long num_inp, + INCHI_IOSTREAM_STRING *strbuf, + NORM_CANON_FLAGS *pncFlags ) +{ + int i, j, k, nRet=0, n=0l; + int err=0; + + + PINChI2 *pINChI = NULL; + PINChI_Aux2 *pINChI_Aux = NULL; + + INP_ATOM_DATA InpCurAtData; + INP_ATOM_DATA *inp_cur_data; + + INP_ATOM_DATA InpNormAtData, InpNormTautData; + INP_ATOM_DATA *inp_norm_data[TAUT_NUM]; /* = { &InpNormAtData, &InpNormTautData }; */ + ORIG_ATOM_DATA *cur_prep_inp_data = prep_inp_data + iINChI; + inchiTime ulTStart; + +/* Always create info data structures (but do not display them always ) +#ifndef COMPILE_ANSI_ONLY +*/ + int bShowStructure = 0; + int bStructurePreprocessed = 0; /* All changes except disconnection */ + int bStructureDisconnected = 0; + int bAlsoOutputReconnected = 0, bINCHI_LIB_Flag = 0; + COMP_ATOM_DATA *composite_norm_data = composite_norm_data2[iINChI]; + INP_ATOM_DATA2 *all_inp_norm_data = NULL; +/*#endif*/ + +/* Order of actions: + + if ( orig_inp_data is NOT empty AND + prep_inp_data[0] IS empty ) then do + in PreprocessOneStructure() : + + 1. copy orig_inp_data --> prep_inp_data[0] + 2. fix odd things in prep_inp_data[0] + 3. if( orig_inp_data->bDisconnectSalts ) then + -- disconnect salts in prep_inp_data[0] + 4. move protons to neutralize charges on heteroatoms + 5. if( orig_inp_data->bDisconnectCoord ) then + -- copy prep_inp_data[0] --> prep_inp_data[1] + -- disconnect metals in prep_inp_data[0] + + iINChI = 0 + ========= + (normal/disconnected layer) + + 1. normalize prep_inp_data[0] in inp_norm_data[0,1] + 2. create INChI[ iINChI ] out of inp_norm_data[0,1] + + + iINChI = 1 AND orig_inp_data->bDisconnectCoord > 0 + ================================================= + (reconnected layer) + + 1. normalize prep_inp_data[1] in inp_norm_data[0,1] + 2. create INChI[ iINChI ] out of inp_norm_data[0,1] + +*/ + + ip->msec_LeftTime = ip->msec_MaxTime; /* start timeout countdown for each component */ + + inp_cur_data = &InpCurAtData; + inp_norm_data[TAUT_NON] = &InpNormAtData; + inp_norm_data[TAUT_YES] = &InpNormTautData; + + memset( inp_cur_data , 0, sizeof( *inp_cur_data ) ); + memset( inp_norm_data[TAUT_NON], 0, sizeof( *inp_norm_data[0] ) ); + memset( inp_norm_data[TAUT_YES], 0, sizeof( *inp_norm_data[0] ) ); + + { /*#ifndef COMPILE_ANSI_ONLY*/ + memset( composite_norm_data+TAUT_NON, 0, sizeof( composite_norm_data[0] ) ); + memset( composite_norm_data+TAUT_YES, 0, sizeof( composite_norm_data[0] ) ); + memset( composite_norm_data+TAUT_INI, 0, sizeof( composite_norm_data[0] ) ); + } /*#endif*/ + + if ( ip->bAllowEmptyStructure && !orig_inp_data->at && !orig_inp_data->num_inp_atoms ) + { + ; + } + else if ( !orig_inp_data->at || !orig_inp_data->num_inp_atoms ) + { + return 0; /* nothing to do */ + } + if ( iINChI == 1 && orig_inp_data->bDisconnectCoord <= 0 ) + { + return 0; + } + + /* m = iINChI; */ /* orig_inp_data index */ + + if ( iINChI != INCHI_BAS && iINChI != INCHI_REC ) + { + AddErrorMessage(sd->pStrErrStruct, "Fatal undetermined program error"); + sd->nStructReadError = 97; + nRet = sd->nErrorType = _IS_FATAL; + goto exit_function; + } + + /******************************************************************* + * * + * * + * Whole structure preprocessing: 1st step of the normalization * + * * + * Happen only on the first call to CreateOneStructureINChI() * + * * + * * + *******************************************************************/ + + if ( (!prep_inp_data->at || !prep_inp_data->num_inp_atoms) && + orig_inp_data->num_inp_atoms > 0 ) + { + /* the structure has not been preprocessed */ + if ( ip->msec_MaxTime ) + { + InchiTimeGet( &ulTStart ); + } + + + PreprocessOneStructure( ic, sd, ip, orig_inp_data, prep_inp_data ); + + pncFlags->bTautFlags[iINChI][TAUT_YES] = + pncFlags->bTautFlags[iINChI][TAUT_NON] = + sd->bTautFlags[INCHI_BAS] | ip->bTautFlags; + + pncFlags->bTautFlagsDone[iINChI][TAUT_YES] = + pncFlags->bTautFlagsDone[iINChI][TAUT_NON] = + sd->bTautFlagsDone[INCHI_BAS] | ip->bTautFlagsDone; + + { /*#ifndef COMPILE_ANSI_ONLY*/ + /* in this location the call happens once for each input structure, before preprocessing */ + bStructurePreprocessed = (0 != (sd->bTautFlagsDone[INCHI_BAS] & ( + TG_FLAG_MOVE_HPLUS2NEUTR_DONE | + TG_FLAG_DISCONNECT_SALTS_DONE | + TG_FLAG_MOVE_POS_CHARGES_DONE | + TG_FLAG_FIX_ODD_THINGS_DONE ))); + + bStructureDisconnected = (0 != (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE)); + + bShowStructure = ( bStructurePreprocessed || + bStructureDisconnected || + prep_inp_data[0].num_components > 1); + + /* sd->bTautFlags[] contains output flags + ip->bTautFlags contains input flags + */ + bAlsoOutputReconnected = (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) && + (ip->bTautFlags & TG_FLAG_RECONNECT_COORD); + bINCHI_LIB_Flag = 0; + + /*************** output structures to TARGET_LIB_FOR_WINCHI conditions ********************* + * + * Send to TARGET_LIB_FOR_WINCHI: + * + * type component conditions + * + * COMPONENT_ORIGINAL #0: (num_components > 1) + * COMPONENT_ORIGINAL_PREPROCESSED #0: (num_components > 1) && (preprocessed) + * COMPONENT_ORIGINAL #1: (num_components = 1) && (preprocessed) + * + * Flags explanation: + * MAIN => iINChI=0, RECN => iINChI=1 (Reconnected) + * ORIG => Original, PREP => Preprocessed + * + * Possible flags: k + * + * COMP_ORIG_0_MAIN 0x0001 0 COMPONENT_ORIGINAL, bMain, component #0 + * COMP_ORIG_0_RECN 0x0002 1 COMPONENT_ORIGINAL, bRecn, component #0 + * + * COMP_PREP_0_MAIN 0x0004 2 COMPONENT_ORIGINAL_PREPROCESSED, bMain, component #0 + * COMP_PREP_0_RECN 0x0008 3 COMPONENT_ORIGINAL_PREPROCESSED, bRecn, component #0 + * + * COMP_ORIG_1_MAIN 0x0010 4 COMPONENT_ORIGINAL, bMain, component #1 + * COMP_ORIG_1_RECN 0x0020 5 COMPONENT_ORIGINAL, bRecn, component #1 + * + * bReconnected = k%2 (0 or 1) + * nComponent = k/4 (0 or 1) + * bPreprocessed = (k/2)%2 (0 or 1) + * + ******************************************************************************/ + + /* Original -> Main, component #0, Original */ + if ( prep_inp_data[INCHI_BAS].num_components > 1 ) + { + bINCHI_LIB_Flag |= COMP_ORIG_0_MAIN; + } + else + /* Original -> Main, component #1, Original */ + if ( prep_inp_data[INCHI_BAS].num_components == 1 && bStructurePreprocessed ) + { + bINCHI_LIB_Flag |= COMP_ORIG_1_MAIN; + /* preprocessed will be added when output canonicalization results */ + } + + if ( bAlsoOutputReconnected ) + { + /* Original -> Reconnected, component #0, Original */ + if ( prep_inp_data[INCHI_REC].num_components > 1 ) + { + bINCHI_LIB_Flag |= COMP_ORIG_0_RECN; + } + else if ( prep_inp_data[INCHI_BAS].num_components == 1 && bStructurePreprocessed ) + { + /* Original -> Reconnected, component #1, Original */ + bINCHI_LIB_Flag |= COMP_ORIG_1_RECN; + /* preprocessed will be added when output canonicalization results */ + } + } + if ( ip->msec_MaxTime ) + { + ip->msec_LeftTime -= InchiTimeElapsed( ic, &ulTStart ); + } + + /* display the ORIGINAL, UN-PREPROCESSED structure */ + + if ( ip->bDisplay ) + { + if ( DisplayTheWholeStructure( pCG, ic, sd, ip, szTitle, + inp_file, log_file, orig_inp_data, num_inp, + -1, bShowStructure, bINCHI_LIB_Flag ) ) + { + goto exit_function; + } + } + } /*#endif */ + + switch (sd->nErrorType) + { + case _IS_ERROR: + case _IS_FATAL: + /* error message */ + nRet = TreatErrorsInReadTheStructure( sd, ip, + LOG_MASK_ALL, + inp_file, log_file, out_file, prb_file, + prep_inp_data, &num_inp ); + goto exit_cycle; + } + } + /* tranfer flags from INChI_Aux to sd */ + + + { /*#ifndef COMPILE_ANSI_ONLY */ /* { */ + + /******************************************/ + /* Displaying the structures */ + /* Only under WIN32 */ + /******************************************/ + if ( /* + ip->bDisplayCompositeResults && + !sd->bUserQuitComponentDisplay && */ + prep_inp_data[iINChI].num_components > 1) + { + all_inp_norm_data = (INP_ATOM_DATA2 *)inchi_calloc( prep_inp_data[iINChI].num_components, sizeof(all_inp_norm_data[0])); + } + + /* Display the input structure AFTER PREPROCESSING */ + switch ( iINChI ) + { + case INCHI_BAS: + /*------------ Possibly disconnected structure -------------------*/ + bStructurePreprocessed = 0 != (sd->bTautFlagsDone[iINChI] & ( + TG_FLAG_MOVE_HPLUS2NEUTR_DONE | + TG_FLAG_DISCONNECT_SALTS_DONE | + TG_FLAG_MOVE_POS_CHARGES_DONE | + TG_FLAG_MOVE_CHARGE_COORD_DONE | + TG_FLAG_DISCONNECT_COORD_DONE | + TG_FLAG_FIX_ODD_THINGS_DONE )); + bINCHI_LIB_Flag = 0; + /* Preprocessed/Main -> Main, component #0, Preprocessed */ + if ( prep_inp_data[iINChI].num_components > 1 && + bStructurePreprocessed ) + { + bINCHI_LIB_Flag |= COMP_PREP_0_MAIN; + } + bShowStructure = ( bStructurePreprocessed && + prep_inp_data[iINChI].num_components > 1); + break; + + case INCHI_REC: + /*------------ Reconnected structure ------------------------------*/ + bAlsoOutputReconnected = + (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) && + (ip->bTautFlags & TG_FLAG_RECONNECT_COORD); + + if ( !bAlsoOutputReconnected ) + { + break; + } + + bStructurePreprocessed = 0 != (sd->bTautFlagsDone[iINChI] & ( + TG_FLAG_MOVE_HPLUS2NEUTR_DONE | + TG_FLAG_DISCONNECT_SALTS_DONE | + TG_FLAG_MOVE_POS_CHARGES_DONE | + TG_FLAG_FIX_ODD_THINGS_DONE )); + bINCHI_LIB_Flag = 0; + /* Preprocessed/Reconnected -> Reconnected, component #0, Preprocessed */ + if ( prep_inp_data[iINChI].num_components > 1 && bStructurePreprocessed ) { + bINCHI_LIB_Flag |= COMP_PREP_0_RECN; + } + bShowStructure = ( bStructurePreprocessed && + prep_inp_data[iINChI].num_components > 1 ); + break; + + default: + bShowStructure = 0; + } + + + if ( ip->bDisplay && prep_inp_data[iINChI].num_inp_atoms > 0 ) + { + if ( DisplayTheWholeStructure( pCG, ic, sd, ip, szTitle, + inp_file, log_file, + prep_inp_data+iINChI, + num_inp, + iINChI, + bShowStructure, + bINCHI_LIB_Flag ) ) + goto exit_function; + } + } /* #endif */ /* } ifndef COMPILE_ANSI_ONLY */ + + + /* allocate pINChI[iINChI] and pINChI_Aux2[iINChI] -- arrays of pointers to INChI and INChI_Aux */ + /* assign values to sd->num_components[] */ + MYREALLOC2(PINChI2, PINChI_Aux2, pINChI2[iINChI], pINChI_Aux2[iINChI], sd->num_components[iINChI], cur_prep_inp_data->num_components, k); + + if ( k ) + { + AddErrorMessage(sd->pStrErrStruct, "Cannot allocate output data. Terminating"); + sd->nStructReadError = 99; + sd->nErrorType = _IS_FATAL; + goto exit_function; + } + + pINChI = pINChI2[iINChI]; + pINChI_Aux = pINChI_Aux2[iINChI]; + + /**************************************************************************/ + /* */ + /* */ + /* M A I N C Y C L E: P R O C E S S C O M P O N E N T S */ + /* */ + /* */ + /* O N E B Y O N E */ + /* */ + /* */ + /**************************************************************************/ + + for ( i = 0, nRet = 0; + !sd->bUserQuitComponent && i < cur_prep_inp_data->num_components; + i ++ ) + { + if ( ip->msec_MaxTime ) + { + InchiTimeGet( &ulTStart ); + } + +#ifndef TARGET_LIB_FOR_WINCHI /* { */ +#if ( bREUSE_INCHI == 1 ) + + if ( iINChI == INCHI_REC && + /*( !ip->bDisplay && + !ip->bDisplayCompositeResults && */ + !(ip->bCompareComponents & CMP_COMPONENTS) || + sd->bUserQuitComponentDisplay ) + { + /* Reconnected structure (06-20-2005: added "&& !ip->bDisplayCompositeResults" to display composite structure) */ + int m = iINChI-1; + + /* Find whether we have already calculated this INChI in basic (disconnected) layer */ + for ( j = n = 0; j < prep_inp_data[m].num_components; j ++ ) + { + if ( i+1 == prep_inp_data[m].nOldCompNumber[j] && + (pINChI2[m][j][TAUT_NON] || pINChI2[m][j][TAUT_YES]) ) + { + /* Yes, we have already done this */ + if ( !n++ ) + { + memcpy( pINChI +i, pINChI2 [m]+j, sizeof(pINChI[0])); + memcpy( pINChI_Aux+i, pINChI_Aux2[m]+j, sizeof(pINChI_Aux[0])); + for ( k = 0; k < TAUT_NUM; k ++ ) + { + if ( pINChI[i][k] ) + { + pINChI[i][k]->nRefCount ++; + if ( pINChI[i][k]->nNumberOfAtoms > 0 ) + { + switch( k ) + { + case TAUT_NON: + sd->num_non_taut[iINChI] ++; + break; + case TAUT_YES: + if ( pINChI[i][k]->lenTautomer > 0 ) + { + sd->num_taut[iINChI] ++; + } + else + if ( !pINChI[i][TAUT_NON] || + !pINChI[i][TAUT_NON]->nNumberOfAtoms ) + { + sd->num_non_taut[iINChI] ++; + } + break; + } + } + } + if ( pINChI_Aux[i][k] ) + { + pINChI_Aux[i][k]->nRefCount ++; + } + } + } + } + } + + if ( n == 1 ) + { + continue; + } + if ( n > 1 ) + { + /* ith component is equivalent to more than one another component */ + AddErrorMessage(sd->pStrErrStruct, "Cannot distinguish components"); + sd->nStructReadError = 99; + sd->nErrorType = _IS_ERROR; + goto exit_function; + } + } + +#endif +#endif /* } TARGET_LIB_FOR_WINCHI */ + + /*****************************************************/ + /* a) Allocate memory and extract current component */ + /*****************************************************/ + + nRet = GetOneComponent( ic, sd, ip, + log_file, out_file, + inp_cur_data, cur_prep_inp_data, + i, num_inp ); + + if ( ip->msec_MaxTime ) + ip->msec_LeftTime -= InchiTimeElapsed( ic, &ulTStart ); + + switch ( nRet ) + { + case _IS_ERROR: + case _IS_FATAL: + goto exit_cycle; + } + +#if !defined(TARGET_API_LIB) && !defined(COMPILE_ANSI_ONLY) + /* console request: Display the component? */ + if ( ip->bDisplay && inp_file->f != stdin ) + { + if ( user_quit(ic, "Enter=Display Component, Esc=Stop ?", ip->ulDisplTime) ) + { + sd->bUserQuitComponent = 1; + break; + } + } +#endif + + /*#ifndef COMPILE_ANSI_ONLY /* { */ + + /* b) Display the extracted original component structure */ + if ( ip->bDisplay && inp_cur_data->at && !sd->bUserQuitComponentDisplay ) + { + if ( cur_prep_inp_data->num_components == 1 ) + { + sprintf( szTitle, "%sInput Structure #%ld.%s%s%s%s%s", + bStructurePreprocessed? "Preprocessed ":"", + num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), iINChI? " (Reconnected)":""); + } + else + { + sprintf( szTitle, "Component #%d of %d, Input Structure #%ld.%s%s%s%s%s", + i+1, cur_prep_inp_data->num_components, + num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), iINChI? " (Reconnected)":""); + } + +#ifndef TARGET_LIB_FOR_WINCHI + + err = DisplayStructure( pCG, + inp_cur_data->at, + inp_cur_data->num_at, + 0, + 1, + 0, + NULL, + 1/*isotopic*/, + 0/*taut*/, + NULL, + NULL, + ip->bAbcNumbers, + &ip->dp, + ip->nMode, + szTitle ); + + sd->bUserQuitComponentDisplay = (err==ESC_KEY); + + if ( !err ) + { + inchi_fprintf( stderr, "Cannot display the structure\n"); + } + +#else + + if(DRAWDATA && DRAWDATA_EXISTS) + { + struct DrawData vDrawData; + int nType = COMPONENT_ORIGINAL; + vDrawData.pWindowData = CreateWinData_( pCG, + inp_cur_data->at, + inp_cur_data->num_at, + 0, + 1 /* bAdd_DT_to_num_H */, + 0, + NULL, + 1 /* display isotopic if present */, + 0, + NULL, + NULL, + ip->bAbcNumbers, + &ip->dp, + ip->nMode ); + if( vDrawData.pWindowData != NULL ) + { + if ( DRAWDATA_EXISTS ( i+1, nType, iINChI ) ) + { + /* i = component number */ + nType = COMPONENT_ORIGINAL_PREPROCESSED; + } + vDrawData.nComponent = i+1; + vDrawData.nType = nType; + vDrawData.bReconnected = iINChI; /* 0=>main; 1=>reconnected */ + vDrawData.szTitle = inchi__strdup(szTitle); + vDrawData.pWindowData->szTitle = inchi__strdup(szTitle); + DRAWDATA(&vDrawData); + } + } +#endif + } + /*#endif */ /* } COMPILE_ANSI_ONLY */ + + + /*******************************************************************************/ + /* */ + /* N O R M A L I Z A T I O N a n d C A N O N I C A L I Z A T I O N */ + /* */ + /* (both tautomeric and non-tautomeric if requested) */ + /* */ + /*******************************************************************************/ + /* c) Create the component's INChI ( copies ip->bTautFlags into sd->bTautFlags)*/ + /*******************************************************************************/ + + nRet = CreateOneComponentINChI( pCG, ic, sd, ip, + inp_cur_data, orig_inp_data, + pINChI/*2[iINChI]*/, + pINChI_Aux/*2[iINChI]*/, + iINChI, i, num_inp, + inp_norm_data, + pncFlags, + log_file ); + + + + /* d) Display one component structure and/or INChI results only if there was no error */ + + /* #ifndef COMPILE_ANSI_ONLY */ /* { */ + if ( !nRet ) + { + /* output one component INChI to the stdout if requested */ + /* + if ( ip->bDisplayEachComponentINChI ) { + int cur_num_non_taut = (pINChI[i][TAUT_NON] && pINChI[i][TAUT_NON]->nNumberOfAtoms>0); + int cur_num_taut = (pINChI[i][TAUT_YES] && pINChI[i][TAUT_YES]->nNumberOfAtoms>0); + if ( ip->bDisplayEachComponentINChI && cur_num_non_taut + cur_num_taut ) { + SortAndPrintINChI( pCG, stdout, pStr, nStrLen, NULL, + ip, 1, cur_num_non_taut, cur_num_taut, + num_inp, pINChI+i, pINChI_Aux+i, + save_opt_bits); + } + } + */ + /************************************************************************** + * display from one up to 4 structure pictures-results for each component * + * Enable buttons: * + * BN (non-tautomeric non-isotopic): inp_norm_data[0]->bExists * + * TN (tautomeric non-isotopic): inp_norm_data[1]->bExists * + * BI (non-tautomeric isotopic): inp_norm_data[0]->bExists && * + * inp_norm_data[0]->bHasIsotopicLayer * + * TI (tautomeric isotopic): inp_norm_data[1]->bExists && * + * inp_norm_data[1]->bHasIsotopicLayer * + **************************************************************************/ + + int bIsotopic, bTautomeric, bDisplayTaut, bHasIsotopicLayer, bFixedBondsTaut, m_max, m, nNumDisplayedFixedBondTaut=0; + + for ( j = 0; + ip->bDisplay && + !sd->bUserQuitComponentDisplay && + j < TAUT_NUM; + j ++ ) + { + if ( inp_norm_data[j]->bExists && !inp_norm_data[j]->bDeleted ) + { + bTautomeric = (pINChI[i][j]->lenTautomer > 0); + /* same as (inp_norm_data[j]->bTautomeric > 0) */ + + /* If requested tautomeric and no tautmerism found then do not say mobile or fixed H. 2004-10-27 */ + bDisplayTaut = (!(ip->nMode & REQ_MODE_BASIC) && !bTautomeric)? -1 : bTautomeric; + bHasIsotopicLayer = (inp_norm_data[j]->bHasIsotopicLayer > 0); + + for ( k = 0; k <= bHasIsotopicLayer; k ++ ) + { + bIsotopic = (k > 0); + m_max = inp_norm_data[j]->at_fixed_bonds && inp_norm_data[j]->bTautPreprocessed? 1 : 0; + for ( m = m_max; 0 <= m; m -- ) + { + bFixedBondsTaut = (m>0); + nNumDisplayedFixedBondTaut += bFixedBondsTaut; + /* display only one time */ + + /* Added number of components, added another format for a single component case - DCh */ + if ( cur_prep_inp_data->num_components > 1 ) + { + sprintf( szTitle, "%s Component #%d of %d, Structure #%ld%s%s.%s%s%s%s%s", + bFixedBondsTaut? "Preprocessed":"Result for", + i+1, cur_prep_inp_data->num_components, num_inp, + bDisplayTaut==1? ", mobile H": bDisplayTaut==0?", fixed H":"", + bIsotopic? ", isotopic":"", + SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), iINChI? " (Reconnected)":""); + } + else + { + sprintf( szTitle, "%s Structure #%ld%s%s.%s%s%s%s%s", + bFixedBondsTaut? "Preprocessed":"Result for", + num_inp, + bDisplayTaut==1? ", mobile H": bDisplayTaut==0?", fixed H":"", + bIsotopic? ", isotopic":"", + SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), iINChI? " (Reconnected)":""); + } + +#ifndef TARGET_LIB_FOR_WINCHI + if ( bFixedBondsTaut && nNumDisplayedFixedBondTaut != 1 ) + continue; + if ( ip->bDisplay ) + { + if ( bFixedBondsTaut ) + { + err = DisplayStructure( pCG, + inp_norm_data[j]->at_fixed_bonds, + inp_norm_data[j]->num_at, + inp_norm_data[j]->num_removed_H, + 0 /*bAdd_DT_to_num_H*/, + inp_norm_data[j]->nNumRemovedProtons, + inp_norm_data[j]->nNumRemovedProtonsIsotopic, + bHasIsotopicLayer, + j, + NULL, + NULL, + ip->bAbcNumbers, + &ip->dp, + ip->nMode, + szTitle ); + } + else + { + err = DisplayStructure( pCG, + inp_norm_data[j]->at, + inp_norm_data[j]->num_at, + 0, + 0 /*bAdd_DT_to_num_H*/, + 0, + NULL, + k, + j, + pINChI[i], + pINChI_Aux[i], + ip->bAbcNumbers, + &ip->dp, + ip->nMode, szTitle ); + } + if ( sd->bUserQuitComponentDisplay = (err==ESC_KEY) ) + { + break; + } + } + +#else + + if(DRAWDATA && !bFixedBondsTaut) + { + struct DrawData vDrawData; + vDrawData.pWindowData = + CreateWinData_( pCG, + inp_norm_data[j]->at, + inp_norm_data[j]->num_at, + 0, + 0 /* bAdd_DT_to_num_H */, + 0, + NULL, + k, + j, + pINChI[i], + pINChI_Aux[i], + ip->bAbcNumbers, + &ip->dp, + ip->nMode ); + + if( vDrawData.pWindowData != NULL ) + { + int nType; + vDrawData.nComponent = i+1; + if( bTautomeric == 0 ) + nType = (bIsotopic == 0) ? COMPONENT_BN + : COMPONENT_BI; + else + nType = (bIsotopic == 0) ? COMPONENT_TN + : COMPONENT_TI; + vDrawData.nType = nType; + + vDrawData.bReconnected = iINChI; /* 0=>main; 1=>reconnected */ + vDrawData.szTitle = inchi__strdup(szTitle); + vDrawData.pWindowData->szTitle = inchi__strdup(szTitle); + DRAWDATA(&vDrawData); + } + } + else if(DRAWDATA && bFixedBondsTaut) + { + struct DrawData vDrawData; + if ( (ip->bCompareComponents & CMP_COMPONENTS) && + !(ip->bCompareComponents & CMP_COMPONENTS_NONTAUT) && + !bIsotopic == !inp_norm_data[j]->bHasIsotopicLayer ) { + + vDrawData.pWindowData = + CreateWinData_( pCG, + inp_norm_data[j]->at_fixed_bonds, + inp_norm_data[j]->num_at, + inp_norm_data[j]->num_removed_H, + 0 /* bAdd_DT_to_num_H */, + inp_norm_data[j]->nNumRemovedProtons, + inp_norm_data[j]->nNumRemovedProtonsIsotopic, + k, + j, + NULL, + NULL, + ip->bAbcNumbers, + &ip->dp, + ip->nMode ); + } + else + { + continue; + } + if( vDrawData.pWindowData != NULL ) + { + vDrawData.nComponent = i+1; + vDrawData.nType = COMPONENT_ORIGINAL_PREPROCESSED; + vDrawData.bReconnected = iINChI; /* 0=>main; 1=>reconnected */ + vDrawData.szTitle = inchi__strdup(szTitle); + vDrawData.pWindowData->szTitle = inchi__strdup(szTitle); + DRAWDATA(&vDrawData); + } + } +#endif + } + } + } + } + + /* Save normalized components for composite display */ + if ( /*ip->bDisplayCompositeResults && */ + all_inp_norm_data + ) + { + for ( j = 0; j < TAUT_NUM; j ++ ) + { + if ( inp_norm_data[j]->bExists ) + { + all_inp_norm_data[i][j] = *inp_norm_data[j]; + memset( inp_norm_data[j], 0, sizeof(*inp_norm_data[0]) ); + } + } + } + } + + /* #endif */ /* } COMPILE_ANSI_ONLY */ + + + if ( nRet ) + { + nRet = TreatErrorsInCreateOneComponentINChI( sd, + ip, + cur_prep_inp_data, + i, + num_inp, + inp_file, + log_file, out_file, prb_file ); + break; + } + } + /**************************************************************************/ + /* */ + /* */ + /* E N D O F T H E M A I N C Y C L E P R O C E S S I N G */ + /* */ + /* C O M P O N E N T S O N E B Y O N E */ + /* */ + /* */ + /**************************************************************************/ + + +exit_cycle: + switch ( nRet ) + { + case _IS_FATAL: + case _IS_ERROR: + break; + default: + + /* #ifndef COMPILE_ANSI_ONLY *//* { */ + /* composite results picture(s) */ + if ( all_inp_norm_data ) + { + CreateCompositeNormAtom( composite_norm_data, + all_inp_norm_data, + prep_inp_data[iINChI].num_components ); + /* + for ( i = 0; i < prep_inp_data[iINChI].num_components; i ++ ) { + for ( k = 0; k < TAUT_NUM; k ++ ) { + FreeInpAtomData( &all_inp_norm_data[i][k] ); + } + } + inchi_free( all_inp_norm_data ); + all_inp_norm_data = NULL; + */ + } + /* #endif */ /* } COMPILE_ANSI_ONLY */ + + break; + } + + /*#ifndef COMPILE_ANSI_ONLY*/ /* { */ + + /* avoid memory leaks in case of error */ + if ( all_inp_norm_data ) + { + for ( i = 0; i < prep_inp_data[iINChI].num_components; i ++ ) + { + for ( k = 0; k < TAUT_NUM; k ++ ) + { + FreeInpAtomData( &all_inp_norm_data[i][k] ); + } + } + inchi_free( all_inp_norm_data ); + all_inp_norm_data = NULL; + } + /*#endif */ /* } COMPILE_ANSI_ONLY */ + + FreeInpAtomData( inp_cur_data ); + for ( i = 0; i < TAUT_NUM; i ++ ) + { + FreeInpAtomData( inp_norm_data[i] ); + } + + +exit_function: + return nRet; +} + + +/* + Generate InChI for one connected component (of possibly multi-component structure) +*/ +int CreateOneComponentINChI( CANON_GLOBALS *pCG, + INCHI_CLOCK *ic, + STRUCT_DATA *sd, + INPUT_PARMS *ip, + INP_ATOM_DATA *inp_cur_data, + ORIG_ATOM_DATA *orig_inp_data, + PINChI2 *pINChI, + PINChI_Aux2 *pINChI_Aux, + int iINChI, + int i, long num_inp, + INP_ATOM_DATA **inp_norm_data, + NORM_CANON_FLAGS *pncFlags, + INCHI_IOSTREAM *log_file ) +{ + inchiTime ulTStart, ulTEnd, *pulTEnd = NULL; + int k, num_at, ret = 0; + int bOrigCoord; + INCHI_MODE bTautFlags = ip->bTautFlags; + INCHI_MODE bTautFlagsDone = (ip->bTautFlagsDone | sd->bTautFlagsDone[INCHI_BAS]); + INChI *cur_INChI[TAUT_NUM]; + INChI_Aux *cur_INChI_Aux[TAUT_NUM]; + long lElapsedTime; + + InchiTimeGet( &ulTStart ); + bOrigCoord = + !(ip->bINChIOutputOptions & (INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO)); + + for ( k = 0; k < TAUT_NUM; k ++ ) + { + cur_INChI[k] = NULL; + cur_INChI_Aux[k] = NULL; + } + + /* Allocate memory for non-tautomeric (k=0) and tautomeric (k=1) results */ + for ( k = 0; k < TAUT_NUM; k ++ ) + { + int nAllocMode = (k==TAUT_YES? REQ_MODE_TAUT:0) | + (bTautFlagsDone & ( TG_FLAG_FOUND_ISOTOPIC_H_DONE | + TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE )) + ? (ip->nMode & REQ_MODE_ISO) :0; + + if ( k==TAUT_NON && (ip->nMode & REQ_MODE_BASIC ) || + k==TAUT_YES && (ip->nMode & REQ_MODE_TAUT ) ) + { + /* alloc INChI and INChI_Aux */ + cur_INChI[k] = Alloc_INChI( inp_cur_data->at, + inp_cur_data->num_at, + &inp_cur_data->num_bonds, + &inp_cur_data->num_isotopic, + nAllocMode ); + cur_INChI_Aux[k] = Alloc_INChI_Aux( inp_cur_data->num_at, + inp_cur_data->num_isotopic, + nAllocMode, + bOrigCoord ); + if ( cur_INChI_Aux[k] ) + { + cur_INChI_Aux[k]->bIsIsotopic = inp_cur_data->num_isotopic; + } + /* alloc memory for the output structure: non-tautomeric and tautomeric (for displaying) */ + + CreateInpAtomData( inp_norm_data[k], inp_cur_data->num_at, k ); + } + else + { + FreeInpAtomData( inp_norm_data[k] ); + } + } + + lElapsedTime = InchiTimeElapsed( ic, &ulTStart ); + if ( ip->msec_MaxTime ) + { + ip->msec_LeftTime -= lElapsedTime; + } + sd->ulStructTime += lElapsedTime; + + +/*^^^#if ( !defined( TARGET_LIB_FOR_WINCHI ) && !defined( TARGET_API_LIB ) ) */ +#if ( !defined( TARGET_LIB_FOR_WINCHI ) && !defined( TARGET_API_LIB ) && !defined(TARGET_EXE_STANDALONE) ) +#endif + + + /****************************************************** + * + * Get one component canonical numberings, etc. + * + ******************************************************/ + + /* + * Create_INChI() return value: + * num_at <= 0: error code + * num_at > 0: number of atoms (excluding terminal hydrogen atoms) + * inp_norm_data[0] => non-tautomeric, inp_norm_data[1] => tautomeric + */ + + InchiTimeGet( &ulTStart ); + + if ( ip->msec_MaxTime ) + { + ulTEnd = ulTStart; + pulTEnd = &ulTEnd; + if ( ip->msec_LeftTime > 0 ) + { + InchiTimeAddMsec( ic, pulTEnd, ip->msec_LeftTime ); + } + } + + num_at = Create_INChI( pCG, ic, cur_INChI, cur_INChI_Aux, + orig_inp_data/* not used */, + inp_cur_data->at, inp_norm_data, inp_cur_data->num_at, + ip->nMode, + ip->bLargeMolecules, + ip->bPolymers, + &bTautFlags, &bTautFlagsDone, + pulTEnd, NULL, sd->pStrErrStruct); + + SetConnectedComponentNumber( inp_cur_data->at, + inp_cur_data->num_at, + i+1 ); + /* normalization alters structure component number */ + + for ( k = 0; k < TAUT_NUM; k ++ ) + { + if ( cur_INChI_Aux[k] && cur_INChI_Aux[k]->nNumberOfAtoms > 0 ) + { + pncFlags->bNormalizationFlags[iINChI][k] |= + cur_INChI_Aux[k]->bNormalizationFlags; + pncFlags->bTautFlags[iINChI][k] |= + cur_INChI_Aux[k]->bTautFlags; + pncFlags->bTautFlagsDone[iINChI][k] |= + cur_INChI_Aux[k]->bTautFlagsDone; + pncFlags->nCanonFlags[iINChI][k] |= + cur_INChI_Aux[k]->nCanonFlags; + } + } + + /* Detect errors */ + if ( num_at < 0 ) + { + sd->nErrorCode = num_at; + } + else if ( num_at == 0 ) + { + sd->nErrorCode = -1; + } + else if ( cur_INChI[TAUT_NON] && cur_INChI[TAUT_NON]->nErrorCode ) + { + /* non-tautomeric error */ + sd->nErrorCode = cur_INChI[TAUT_NON]->nErrorCode; + } + else if ( cur_INChI[TAUT_YES] && cur_INChI[TAUT_YES]->nErrorCode ) + { + /* tautomeric error */ + sd->nErrorCode = cur_INChI[TAUT_YES]->nErrorCode; + } + +#if ( bRELEASE_VERSION == 0 ) + if ( cur_INChI[TAUT_NON] ) sd->bExtract |= cur_INChI[TAUT_NON]->bExtract; + if ( cur_INChI[TAUT_YES] ) sd->bExtract |= cur_INChI[TAUT_YES]->bExtract; + if ( (TG_FLAG_TEST_TAUT3_SALTS_DONE & bTautFlagsDone) ) { + sd->bExtract |= EXTR_TEST_TAUT3_SALTS_DONE; + } +#endif + + /* Detect and store stereo warnings */ + if ( !sd->nErrorCode ) + { + GetProcessingWarningsOneComponentInChI(cur_INChI, inp_norm_data, sd); + } + + lElapsedTime = InchiTimeElapsed( ic, &ulTStart ); + if ( ip->msec_MaxTime ) + { + ip->msec_LeftTime -= lElapsedTime; + } + sd->ulStructTime += lElapsedTime; + +#if !defined(TARGET_API_LIB) && !defined(COMPILE_ANSI_ONLY) + /* Display the results */ + if ( ip->bDisplay ) + eat_keyboard_input(); +#endif + + /* a) No matter what happened save the allocated INChI pointers */ + /* save the INChI of the current component */ + + InchiTimeGet( &ulTStart ); + for ( k = 0; k < TAUT_NUM; k ++ ) + { + pINChI[i][k] = cur_INChI[k]; + pINChI_Aux[i][k] = cur_INChI_Aux[k]; + cur_INChI[k] = NULL; + cur_INChI_Aux[k] = NULL; + } + + /* b) Count one component structure and/or INChI results only + if there was no error + Set inp_norm_data[j]->num_removed_H = number of removed explicit H + */ + + if ( !sd->nErrorCode ) + { + /* find where the current processed structure is located */ + int cur_is_in_non_taut = ( pINChI[i][TAUT_NON] && + pINChI[i][TAUT_NON]->nNumberOfAtoms>0); + int cur_is_in_taut = ( pINChI[i][TAUT_YES] && + pINChI[i][TAUT_YES]->nNumberOfAtoms>0); + + int cur_is_non_taut = cur_is_in_non_taut && 0 == pINChI[i][TAUT_NON]->lenTautomer || + cur_is_in_taut && 0 == pINChI[i][TAUT_YES]->lenTautomer; + int cur_is_taut = cur_is_in_taut && 0 < pINChI[i][TAUT_YES]->lenTautomer; + + /* + sd->bTautFlags[iINChI] |= bTautFlags; + sd->bTautFlagsDone[iINChI] |= bTautFlagsDone; + */ + + if ( cur_is_non_taut + cur_is_taut ) + { + /* count tautomeric and non-tautomeric components of the structures */ + int j1 = cur_is_in_non_taut ? TAUT_NON : TAUT_YES; + int j2 = cur_is_in_taut ? TAUT_YES : TAUT_NON; + int j; + sd->num_non_taut[iINChI] += cur_is_non_taut; + sd->num_taut[iINChI] += cur_is_taut; + for ( j = j1; j <= j2; j ++ ) + { + int bIsotopic = ( pINChI[i][j]->nNumberOfIsotopicAtoms || + pINChI[i][j]->nNumberOfIsotopicTGroups || + pINChI[i][j]->nPossibleLocationsOfIsotopicH && pINChI[i][j]->nPossibleLocationsOfIsotopicH[0]>1); + if ( j == TAUT_YES ) + { + bIsotopic |= (0 < pINChI_Aux[i][j]->nNumRemovedIsotopicH[0] + + pINChI_Aux[i][j]->nNumRemovedIsotopicH[1] + + pINChI_Aux[i][j]->nNumRemovedIsotopicH[2]); + } + + inp_norm_data[j]->bExists = 1; /* j=0: non-taut exists, j=1: taut exists */ + inp_norm_data[j]->bHasIsotopicLayer = bIsotopic; + /*inp_norm_data[j]->num_removed_H = inp_norm_data[j]->num_at - num_at;*/ + } + } + } + +/* + return (sd->nErrorCode==CT_OUT_OF_RAM || sd->nErrorCode==CT_USER_QUIT_ERR)? _IS_FATAL : + sd->nErrorCode? _IS_ERROR : 0; +*/ + + if ( sd->nErrorCode==CT_OUT_OF_RAM || sd->nErrorCode==CT_USER_QUIT_ERR ) + { + ret = _IS_FATAL; + } + else if ( sd->nErrorCode ) + { + ret = _IS_ERROR; + } + + lElapsedTime = InchiTimeElapsed( ic, &ulTStart ); + if ( ip->msec_MaxTime ) + { + ip->msec_LeftTime -= lElapsedTime; + } + sd->ulStructTime += lElapsedTime; + + return ret; +} + + +int ProcessOneStructureEx( struct tagINCHI_CLOCK *ic, + struct tagCANON_GLOBALS *CG, + STRUCT_DATA *sd, + INPUT_PARMS *ip, + char *szTitle, + PINChI2 *pINChI2[INCHI_NUM], + PINChI_Aux2 *pINChI_Aux2[INCHI_NUM], + INCHI_IOSTREAM *inp_file, + INCHI_IOSTREAM *log_file, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM *prb_file, + ORIG_ATOM_DATA *orig_inp_data, + ORIG_ATOM_DATA *prep_inp_data, + long num_inp, + INCHI_IOSTREAM_STRING *strbuf, + unsigned char save_opt_bits) +{ +int res; + + + int is_polymer = orig_inp_data && + orig_inp_data->polymer && + orig_inp_data->polymer->n > 0 && + orig_inp_data->polymer->valid != 0 && + (ip->nInputType == INPUT_MOLFILE || ip->nInputType == INPUT_SDFILE); + +#ifdef TARGET_LIB_FOR_WINCHI + inchi_ios_reset( out_file ); +#endif + + if ( is_polymer ) + { + /* Determine the kind of polymer units and polymer as a whole */ + res = OrigAtDataPolymer_ParseAndValidate( orig_inp_data, ip->bPolymers, sd->pStrErrStruct ); + + if ( res ) + { + sd->nErrorCode = res; + + inchi_ios_eprint( log_file, + "Error %d (%s) structure #%ld.%s%s%s%s\n", + sd->nErrorCode, sd->pStrErrStruct, + num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); + res = _IS_ERROR; + orig_inp_data->num_inp_atoms = -1; + goto exitf; + } + OrigAtData_DebugTrace( orig_inp_data ); + + /* Polymer-specific preprocessing + + Analyze and modify canonical CRU if applicable. + Use the canonical numbers from ready InChI. + */ + res = OrigAtDataPolymer_CyclizeCloseableUnits( orig_inp_data, sd->pStrErrStruct ); + + if ( res ) + { + AddErrorMessage(sd->pStrErrStruct, "Error while processing polymer-related input"); + res = _IS_ERROR; + orig_inp_data->num_inp_atoms = -1; + goto exitf; + } + OrigAtData_DebugTrace( orig_inp_data ); + } + + res = ProcessOneStructure( ic, CG, sd, ip, szTitle, + pINChI2, pINChI_Aux2, + inp_file, log_file, + out_file, prb_file, + orig_inp_data, prep_inp_data, + num_inp, strbuf, + save_opt_bits); + + +exitf: + return res; +} diff --git a/INCHI-1-SRC/INCHI_BASE/src/runichi2.c b/INCHI-1-SRC/INCHI_BASE/src/runichi2.c new file mode 100644 index 0000000..faf3386 --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/runichi2.c @@ -0,0 +1,1210 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +/* + Reading input data + +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "mode.h" +#include "ichitime.h" +#ifndef COMPILE_ANSI_ONLY +#include +#endif + +#include "ichimain.h" +#include "ichi_io.h" +#include "mol_fmt.h" +#include "inchi_api.h" +#include "readinch.h" +#ifdef TARGET_LIB_FOR_WINCHI +#include "../../../IChI_lib/src/ichi_lib.h" +#include "inchi_api.h" +#else +#include "inchi_gui.h" +#endif +#include "readinch.h" + + +/* + Get (the next) one portion of input data of any possible kind + (Molfile, InChI string, ...) from a sequential input stream +*/ +int GetOneStructure( INCHI_CLOCK *ic, + STRUCT_DATA *sd, + INPUT_PARMS *ip, + char *szTitle, + INCHI_IOSTREAM *inp_file, + INCHI_IOSTREAM *log_file, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM *prb_file, + ORIG_ATOM_DATA *orig_inp_data, + long *num_inp, + STRUCT_FPTRS *struct_fptrs ) +{ +int nRet, inp_index, out_index, bUseFptr = (NULL != struct_fptrs); + + + FreeOrigAtData( orig_inp_data ); + + + /* added for TARGET_LIB_FOR_WINCHI early EOF detection */ + + inp_index = -1; + out_index = -1; + + + if ( struct_fptrs ) + { + + if ( inp_file->f == stdin ) + return _IS_FATAL; + + if ( ip->nInputType == INPUT_CMLFILE ) + bUseFptr = 0; + + + /* initially allocate or increase length of struct_fptrs->fptr array */ + + if ( !struct_fptrs->fptr || + struct_fptrs->len_fptr <= struct_fptrs->cur_fptr+1 ) + { + + INCHI_FPTR *new_fptr = (INCHI_FPTR *) + inchi_calloc( struct_fptrs->len_fptr + ADD_LEN_STRUCT_FPTRS, + sizeof(new_fptr[0]) ); + + if ( new_fptr ) + { + if ( struct_fptrs->fptr ) + { + if ( struct_fptrs->len_fptr ) + memcpy( new_fptr, + struct_fptrs->fptr, + struct_fptrs->len_fptr*sizeof(new_fptr[0])); + inchi_free( struct_fptrs->fptr ); + } + else + { + struct_fptrs->len_fptr = 0; + struct_fptrs->cur_fptr = 0; + struct_fptrs->max_fptr = 0; + } + struct_fptrs->len_fptr += ADD_LEN_STRUCT_FPTRS; + struct_fptrs->fptr = new_fptr; + } + else + { + return _IS_FATAL; /* new_fptr allocation error */ + } + } + + if ( struct_fptrs->fptr[struct_fptrs->cur_fptr] == EOF ) + return _IS_EOF; + else + { + + if ( bUseFptr ) + { + if( fseek( inp_file->f, + struct_fptrs->fptr[struct_fptrs->cur_fptr], + SEEK_SET) ) + { + return _IS_FATAL; + } + if ( struct_fptrs->cur_fptr && + struct_fptrs->max_fptr <= struct_fptrs->cur_fptr ) + { + return _IS_FATAL; + } + } + else + { + inp_index = struct_fptrs->fptr[struct_fptrs->cur_fptr]; + out_index = EOF; + } + } + + *num_inp = struct_fptrs->cur_fptr; /* set structure count */ + } + + + + nRet = ReadTheStructure( ic, sd, ip, inp_file, + orig_inp_data, + inp_index, &out_index ); + + + if ( !nRet ) + { + if ( ip->nInputType == INPUT_INCHI_PLAIN || + ip->nInputType == INPUT_MOLFILE || ip->nInputType == INPUT_SDFILE) + { + if ( ip->lMolfileNumber ) + *num_inp = ip->lMolfileNumber; + else + *num_inp += 1; + } + else + { + *num_inp += 1; + } + + nRet = TreatErrorsInReadTheStructure( sd, ip, + LOG_MASK_ALL, inp_file, + log_file, out_file, prb_file, + orig_inp_data, num_inp ); + } + + + /************************************************************/ + /* added for TARGET_LIB_FOR_WINCHI: + look ahead for end of file detection */ + + /************************************************************/ + + if ( inp_file->type==INCHI_IOSTREAM_TYPE_FILE && + inp_file->f && + struct_fptrs && + struct_fptrs->fptr && + struct_fptrs->fptr[struct_fptrs->cur_fptr+1] <= 0 ) + { + + int nRet2=0; + INCHI_FPTR next_fptr=0; + STRUCT_DATA sd2; + + if ( nRet != _IS_EOF && nRet != _IS_FATAL ) + { + if ( inp_file->f == stdin || struct_fptrs->len_fptr <= struct_fptrs->cur_fptr+1 ) + { + return _IS_FATAL; + } + /* get next structure fptr */ + if ( bUseFptr ) + { + next_fptr = ftell( inp_file->f ); + } + else + { + inp_index = out_index; + out_index = EOF; + } + + + /* Read the next structure */ + + nRet2 = ReadTheStructure( ic, + &sd2, ip, inp_file, NULL, + inp_index, &out_index ); + + /* restore fptr to the next structure */ + if ( bUseFptr ) + { + if ( next_fptr != -1L ) + { + fseek( inp_file->f, next_fptr, SEEK_SET); + } + } + } + else + { + /* treat current fatal error as end of file */ + struct_fptrs->fptr[struct_fptrs->cur_fptr] = EOF; + } + + /* next is end of file or fatal */ + if ( nRet == _IS_EOF || nRet == _IS_FATAL || + nRet2 == _IS_EOF || nRet2 == _IS_FATAL ) + { + struct_fptrs->fptr[struct_fptrs->cur_fptr+1] = EOF; + } + else + { + struct_fptrs->fptr[struct_fptrs->cur_fptr+1] = bUseFptr? sd->fPtrEnd : inp_index; + } + + /* update struct_fptrs->max_fptr */ + if ( struct_fptrs->max_fptr <= struct_fptrs->cur_fptr+1 ) + { + struct_fptrs->max_fptr = struct_fptrs->cur_fptr+2; + } + } + + + + switch ( nRet ) + { + case _IS_EOF: + *num_inp -= 1; + case _IS_FATAL: + case _IS_ERROR: + case _IS_SKIP: + goto exit_function; + } + + + /* + if ( !orig_inp_data->num_dimensions ) { + WarningMessage(sd->pStrErrStruct, "0D"); */ /* 0D-structure: no coordinates + } + */ + + +exit_function: + return nRet; +} + + +/* + Extract one connected component from the input structure +*/ +int GetOneComponent( INCHI_CLOCK *ic, + STRUCT_DATA *sd, + INPUT_PARMS *ip, + INCHI_IOSTREAM *log_file, + INCHI_IOSTREAM *out_file, + INP_ATOM_DATA *inp_cur_data, + ORIG_ATOM_DATA *orig_inp_data, + int i, long num_inp ) +{ +inchiTime ulTStart; + + InchiTimeGet( &ulTStart ); + + CreateInpAtomData( inp_cur_data, orig_inp_data->nCurAtLen[i], 0 ); + + inp_cur_data->num_at = ExtractConnectedComponent( orig_inp_data->at, orig_inp_data->num_inp_atoms, i+1, inp_cur_data->at ); + + sd->ulStructTime += InchiTimeElapsed( ic, &ulTStart ); + + + /* error processing */ + + if ( inp_cur_data->num_at <= 0 || orig_inp_data->nCurAtLen[i] != inp_cur_data->num_at ) + { + /* log error message */ + AddErrorMessage(sd->pStrErrStruct, "Cannot extract Component"); + inchi_ios_eprint( log_file, "%s #%d structure #%ld.%s%s%s%s\n", sd->pStrErrStruct, i+1, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue)); + sd->nErrorCode = inp_cur_data->num_at < 0? inp_cur_data->num_at : (orig_inp_data->nCurAtLen[i] != inp_cur_data->num_at)? CT_ATOMCOUNT_ERR : CT_UNKNOWN_ERR; + /* num_err ++; */ + sd->nErrorType = _IS_ERROR; + +#ifdef TARGET_LIB_FOR_WINCHI + if (( ip->bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW) && + (ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) ) + { + sd->nErrorType = ProcessStructError( out_file, + log_file, + sd->pStrErrStruct, + sd->nErrorType, + num_inp, + ip ); + } +#endif + } + + return sd->nErrorType; +} + + +/* Read input data of any possible kind (Molfile, InChI string, ...) */ +int ReadTheStructure( struct tagINCHI_CLOCK *ic, + STRUCT_DATA *sd, + INPUT_PARMS *ip, + INCHI_IOSTREAM *inp_file, + ORIG_ATOM_DATA *orig_inp_data, + /* deprecated, for CML support: */ + int inp_index, int *out_index ) +{ + inchiTime ulTStart; + int nRet = 0, nRet2 = 0; + + int bGetOrigCoord = ! (ip->bINChIOutputOptions & + (INCHI_OUT_NO_AUX_INFO | INCHI_OUT_SHORT_AUX_INFO)); + + INCHI_MODE InpAtomFlags = 0; + /* NB: reading Molfile may set FLAG_INP_AT_CHIRAL bit */ + + int vABParityUnknown = AB_PARITY_UNDF; + /* vABParityUnknown holds actual value of an internal constant */ + /* signifying unknown parity: either the same as for undefined */ + /* parity (default==standard) or a specific one (non-std; */ + /* requested by SLUUD switch). */ + + if ( 0 != ( ip->nMode & REQ_MODE_DIFF_UU_STEREO) ) + { + /* Make labels for unknown and undefined stereo different */ + vABParityUnknown = AB_PARITY_UNKN; + } + + + if ( ip->bLargeMolecules ) + InpAtomFlags |= FLAG_SET_INP_LARGE_ALLOWED; + if ( ip->bPolymers ) + InpAtomFlags |= FLAG_SET_INP_POLYMERS_RECOGNIZED; + + + memset( sd, 0, sizeof(*sd) ); + + + switch ( ip->nInputType ) + { + + case INPUT_MOLFILE: + case INPUT_SDFILE: + + /* Read the original input structure from Molfile */ + + if ( orig_inp_data ) + { + + if ( ip->pSdfValue && ip->pSdfValue[0] ) + { + /* Added 07-29-2003 to avoid inheriting exact value from prev. + structure and to make reference to a (bad) structure + with unknown ID Value */ + char *p, *q; /* q shadows prev declaration of const char *q */ + int n; + if ( ( p = strrchr( ip->pSdfValue, '+' ) ) && + '[' == *(p-1) && + 0 < ( n=strtol(p+1,&q,10) ) && + q[0] && + ']' == q[0] && + !q[1] ) + { + sprintf( p+1, "%d]", n+1 ); + } + else + { + strcat( ip->pSdfValue, " [+1]" ); + } + } + + InchiTimeGet( &ulTStart ); + + if ( inp_file->type==INCHI_IOSTREAM_TYPE_FILE && inp_file->f ) + sd->fPtrStart = (inp_file->f == stdin)? -1 : ftell( inp_file->f ); + + + nRet2 = CreateOrigInpDataFromMolfile( inp_file, + orig_inp_data, + ip->bMergeAllInputStructures, + bGetOrigCoord, + ip->bDoNotAddH, + ip->pSdfLabel, + ip->pSdfValue, + &ip->lSdfId, + &ip->lMolfileNumber, + &InpAtomFlags, + &sd->nStructReadError, + sd->pStrErrStruct ); + + + + if ( !ip->bGetSdfileId || ip->lSdfId == 999999) + ip->lSdfId = 0; + + if ( !ip->bGetMolfileNumber || ip->lMolfileNumber < 0 ) + ip->lMolfileNumber = 0; + + if ( inp_file->type==INCHI_IOSTREAM_TYPE_FILE && inp_file->f ) + sd->fPtrEnd = (inp_file->f == stdin) ? -1 : ftell( inp_file->f ); + + sd->ulStructTime+= InchiTimeElapsed( ic, &ulTStart ); + +#if ( bRELEASE_VERSION == 0 ) + sd->bExtract |= orig_inp_data->bExtract; +#endif + + /* 2004-11-16: added Molfile Chiral Flag Mode */ + /* ******************************************** + * Chiral flags are set in: + * - RunICHI.c -- ReadTheStructure() + * - e_IchiMain.c -- main() + * - inchi_dll.c -- ExtractOneStructure + **********************************************/ + + /* 1. Highest precedence: Chiral Flag set by the user */ + if ( ip->bChiralFlag & FLAG_SET_INP_AT_CHIRAL ) + { + InpAtomFlags = FLAG_INP_AT_CHIRAL; /* forced by the user */ + } + + else if ( ip->bChiralFlag & FLAG_SET_INP_AT_NONCHIRAL ) + { + InpAtomFlags = FLAG_INP_AT_NONCHIRAL; /* forced by the user */ + } + + else if ( (InpAtomFlags & FLAG_INP_AT_CHIRAL) && (InpAtomFlags && FLAG_INP_AT_NONCHIRAL) ) + { + InpAtomFlags &= ~FLAG_INP_AT_NONCHIRAL; + } + + /* Save requested flags in the AuxInfo */ + sd->bChiralFlag &= ~( FLAG_INP_AT_CHIRAL | FLAG_INP_AT_NONCHIRAL ); + sd->bChiralFlag |= InpAtomFlags & ( FLAG_INP_AT_CHIRAL | FLAG_INP_AT_NONCHIRAL ); + + /* Quick fix: modify ip->nMode on the fly */ + + /* 2. The user requested both Stereo AND Chiral flag */ + if ( (ip->nMode & REQ_MODE_CHIR_FLG_STEREO) && (ip->nMode & REQ_MODE_STEREO) ) + { + if ( InpAtomFlags & FLAG_INP_AT_CHIRAL ) + { + /* structure has chiral flag or the user said it is chiral */ + ip->nMode &= ~(REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO); + sd->bChiralFlag |= FLAG_INP_AT_CHIRAL; /* write AuxInfo as chiral */ + } + else + { + ip->nMode &= ~REQ_MODE_RACEMIC_STEREO; + ip->nMode |= REQ_MODE_RELATIVE_STEREO; + sd->bChiralFlag |= FLAG_INP_AT_NONCHIRAL; /* write AuxInfo as explicitly not chiral */ + } + } + } /* if ( orig_inp_data ) */ + + else /* !orig_inp_data */ + { + /* read the next original structure */ + int nStructReadError=0; + + if ( !ip->bMergeAllInputStructures ) + { + nRet2 = CreateOrigInpDataFromMolfile( inp_file, + NULL, + 0, + 0, + 0, + NULL, + NULL, + NULL, + NULL, + &InpAtomFlags, /*NULL, */ + &nStructReadError, + NULL ); + if ( nRet2 <= 0 && + 10 < nStructReadError && nStructReadError < 20 ) + return _IS_EOF; + } + else + { + return _IS_EOF; + } + } + + break; + + + + case INPUT_INCHI_PLAIN: + + /* Read the original input data as text (InChI string ) */ + if ( orig_inp_data ) + { + if ( ip->pSdfValue && ip->pSdfValue[0] ) + { + /* Added 07-29-2003 to avoid inheriting exact value from prev. structure + and to make reference to a (bad) structure with unknown ID Value */ + char *p, *q; + int n; + if ( ( p = strrchr( ip->pSdfValue, '+' )) && + '[' == *(p-1) && + 0 < (n=strtol(p+1,&q,10)) && + q[0] && + ']'==q[0] && + !q[1] ) + { + sprintf( p+1, "%d]", n+1 ); + } + else + { + strcat( ip->pSdfValue, " [+1]" ); + } + } + + InchiTimeGet( &ulTStart ); + + if ( inp_file->type==INCHI_IOSTREAM_TYPE_FILE && inp_file->f ) + sd->fPtrStart = (inp_file->f == stdin) ? -1 : ftell( inp_file->f ); + + + /* Read and make internal molecular data */ + nRet2 = InchiToOrigAtom( inp_file, + orig_inp_data, + ip->bMergeAllInputStructures, + bGetOrigCoord, + ip->bDoNotAddH, + vABParityUnknown, + ip->nInputType, + ip->pSdfLabel, + ip->pSdfValue, + &ip->lMolfileNumber, + &InpAtomFlags, + &sd->nStructReadError, + sd->pStrErrStruct ); + + /*if ( !ip->bGetSdfileId || ip->lSdfId == 999999) ip->lSdfId = 0;*/ + if ( inp_file->type==INCHI_IOSTREAM_TYPE_FILE && inp_file->f ) + sd->fPtrEnd = (inp_file->f == stdin)? -1 : ftell( inp_file->f ); + + sd->ulStructTime += InchiTimeElapsed( ic, &ulTStart ); + +#if ( bRELEASE_VERSION == 0 ) + sd->bExtract |= orig_inp_data->bExtract; +#endif + + /* 2004-11-16: added Molfile Chiral Flag Mode */ + if ( ip->bChiralFlag & FLAG_SET_INP_AT_CHIRAL ) + { + InpAtomFlags = FLAG_INP_AT_CHIRAL; /* forced by the user */ + } + else if ( ip->bChiralFlag & FLAG_SET_INP_AT_NONCHIRAL ) + { + InpAtomFlags = FLAG_INP_AT_NONCHIRAL; /* forced by the user */ + } + else if ( (InpAtomFlags & FLAG_INP_AT_CHIRAL) && (InpAtomFlags && FLAG_INP_AT_NONCHIRAL) ) + { + InpAtomFlags &= ~FLAG_INP_AT_NONCHIRAL; + } + + sd->bChiralFlag |= InpAtomFlags; /* copy chiral flag to AuxInfo */ + + /* Quick fix: modify ip->nMode on the fly */ + if ( (ip->nMode & REQ_MODE_CHIR_FLG_STEREO) && (ip->nMode & REQ_MODE_STEREO) ) + { + if ( InpAtomFlags & FLAG_INP_AT_CHIRAL ) + { + ip->nMode &= ~(REQ_MODE_RELATIVE_STEREO | REQ_MODE_RACEMIC_STEREO); + } + else + { + ip->nMode &= ~REQ_MODE_RACEMIC_STEREO; + ip->nMode |= REQ_MODE_RELATIVE_STEREO; + } + } + } + else + { + /* Read the next original structure */ + int nStructReadError=0; + if ( !ip->bMergeAllInputStructures ) + { + nRet2 = InchiToOrigAtom( inp_file, + NULL, 0, 0, 0, 0, + ip->nInputType, + NULL, NULL, NULL, NULL, + &nStructReadError, + NULL ); + if ( nRet2 <= 0 && 10 < nStructReadError && nStructReadError < 20 ) + return _IS_EOF; + } + else + { + return _IS_EOF; + } + } + break; + + default: + nRet = _IS_FATAL; /* wrong file type */ + } + + return nRet; +} + + +/* + Interpret and treat input reading errors/warnings +*/ +int TreatErrorsInReadTheStructure( STRUCT_DATA *sd, + INPUT_PARMS *ip, + int nLogMask, + INCHI_IOSTREAM *inp_file, + INCHI_IOSTREAM *log_file, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM *prb_file, + ORIG_ATOM_DATA *orig_inp_data, + long *num_inp ) +{ +int nRet = _IS_OKAY; + + if ( 10 < sd->nStructReadError && sd->nStructReadError < 20 ) + { + /* End of file */ + if ( sd->pStrErrStruct[0] ) + inchi_ios_eprint( log_file, "%s inp structure #%ld: End of file.%s%s%s%s \n", sd->pStrErrStruct, *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); + + inchi_ios_eprint( log_file, "End of file detected after structure #%ld. \n", *num_inp-1 ); + + nRet = _IS_EOF; + goto exit_function; /* end of file */ + } + + /*(*num_inp) ++;*/ + + if ( *num_inp < ip->first_struct_number ) + { + /* Skip the structure */ + +#if ( !defined(TARGET_API_LIB) && !defined(TARGET_EXE_STANDALONE) ) +/* #ifndef TARGET_API_LIB */ + if ( log_file->f != stderr ) + inchi_fprintf( stderr, "\rSkipping structure #%ld.%s%s%s%s...", *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue)); +#endif + + nRet = sd->nErrorType = _IS_SKIP; + goto exit_function; + } + + + sd->nErrorType = GetInpStructErrorType( ip, sd->nStructReadError, + sd->pStrErrStruct, + orig_inp_data->num_inp_atoms ); + + + if ( sd->nErrorType == _IS_FATAL ) + { + /* Fatal error */ + + if ( nLogMask & LOG_MASK_FATAL ) + inchi_ios_eprint( log_file, "Fatal Error %d (aborted; %s) inp structure #%ld.%s%s%s%s\n", + sd->nStructReadError, sd->pStrErrStruct, *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); + +#if ( bRELEASE_VERSION == 1 || EXTR_FLAGS == 0 ) + if ( prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd && !ip->bSaveAllGoodStructsAsProblem ) { + MolfileSaveCopy(inp_file, sd->fPtrStart, sd->fPtrEnd, prb_file->f, *num_inp); + } +#endif + /* goto exit_function; */ + } + + if ( sd->nErrorType == _IS_ERROR ) + { + /* Non-fatal errors: do not produce INChI */ + + /* 70 => too many atoms */ + if ( nLogMask & LOG_MASK_ERR ) + inchi_ios_eprint( log_file, "Error %d (no %s; %s) inp structure #%ld.%s%s%s%s\n", + sd->nStructReadError, (ip->bINChIOutputOptions & INCHI_OUT_SDFILE_ONLY)?"Molfile":INCHI_NAME, + sd->pStrErrStruct, *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); + +#if ( bRELEASE_VERSION == 1 || EXTR_FLAGS == 0 ) + if ( prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd && !ip->bSaveAllGoodStructsAsProblem) { + MolfileSaveCopy(inp_file, sd->fPtrStart, sd->fPtrEnd, prb_file->f, *num_inp); + } +#endif + } + + if ( sd->nErrorType == _IS_WARNING ) + { + /* Warnings: try to produce INChI */ + + if ( nLogMask & LOG_MASK_WARN ) + inchi_ios_eprint( log_file, "Warning: (%s) inp structure #%ld.%s%s%s%s\n", + sd->pStrErrStruct, *num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); + } + + + +#ifdef TARGET_LIB_FOR_WINCHI + if ( (ip->bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW) && + (ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) ) + { + if ( sd->nErrorType != _IS_OKAY && sd->nErrorType != _IS_WARNING ) + { + sd->nErrorType = + ProcessStructError( out_file, log_file, sd->pStrErrStruct, sd->nErrorType, *num_inp, ip ); + } + } +#endif + +exit_function: + if ( nRet <= _IS_OKAY && sd->nErrorType > 0 ) + nRet = sd->nErrorType; + + return nRet; +} + + + +#ifdef TARGET_EXE_USING_API + +/* + +*/ +int InchiToInchi_Input( INCHI_IOSTREAM *inp_file, + inchi_Input *orig_at_data, + int bMergeAllInputStructures, + int bDoNotAddH, + int vABParityUnknown, + INPUT_TYPE nInputType, + char *pSdfLabel, + char *pSdfValue, + long *lSdfId, + INCHI_MODE *pInpAtomFlags, + int *err, + char *pStrErr ) +{ +/* inp_ATOM *at = NULL; */ +int num_dimensions_new; +int num_inp_bonds_new; +int num_inp_atoms_new; +int num_inp_0D_new; +inp_ATOM *at_new = NULL; +inp_ATOM *at_old = NULL; +inchi_Stereo0D *stereo0D_new = NULL; +inchi_Stereo0D *stereo0D_old = NULL; +int nNumAtoms = 0, nNumStereo0D = 0; +MOL_COORD *szCoordNew = NULL; +MOL_COORD *szCoordOld = NULL; +int i, j; +int max_num_at; + + + max_num_at = MAX_ATOMS; + if ( *pInpAtomFlags & FLAG_SET_INP_LARGE_ALLOWED ) + max_num_at = MAX_ATOMS_LARGE_MOL; + + if ( pStrErr ) + pStrErr[0] = '\0'; + + + /*FreeOrigAtData( orig_at_data );*/ + if ( lSdfId ) + *lSdfId = 0; + + do + { + at_old = orig_at_data? orig_at_data->atom : NULL; /* save pointer to the previous allocation */ + + stereo0D_old = orig_at_data? orig_at_data->stereo0D : NULL; + + szCoordOld = NULL; + + num_inp_atoms_new = + InchiToinp_ATOM( inp_file, orig_at_data? &stereo0D_new:NULL, &num_inp_0D_new, + bDoNotAddH, vABParityUnknown, nInputType, + orig_at_data? &at_new:NULL, MAX_ATOMS, + &num_dimensions_new, &num_inp_bonds_new, + pSdfLabel, pSdfValue, lSdfId, pInpAtomFlags, err, pStrErr ); + + if ( num_inp_atoms_new <= 0 && !*err ) + { + TREAT_ERR (*err, 0, "Empty structure"); + *err = 98; + } + else if ( orig_at_data && + !num_inp_atoms_new && + 10 < *err && *err < 20 && + orig_at_data->num_atoms > 0 + && bMergeAllInputStructures ) + { + *err = 0; /* end of file */ + break; + } + else if ( num_inp_atoms_new > 0 && orig_at_data ) + { + /* merge pOrigDataTmp + orig_at_data => pOrigDataTmp; */ + nNumAtoms = num_inp_atoms_new + orig_at_data->num_atoms; + nNumStereo0D = num_inp_0D_new + orig_at_data->num_stereo0D; + + if ( nNumAtoms >= max_num_at ) /*MAX_ATOMS ) */ + { + TREAT_ERR (*err, 0, "Too many atoms [check 'LargeMolecules' switch]"); + *err = 70; + orig_at_data->num_atoms = -1; + } + else if ( !at_old ) + { + /* the first structure */ + + orig_at_data->atom = at_new; at_new = NULL; + orig_at_data->num_atoms = num_inp_atoms_new; num_inp_atoms_new = 0; + orig_at_data->stereo0D = stereo0D_new; stereo0D_new = NULL; + orig_at_data->num_stereo0D = num_inp_0D_new; num_inp_0D_new = 0; + } + else if ( orig_at_data->atom = Createinp_ATOM( nNumAtoms ) ) + { + /* switch at_new <--> orig_at_data->at; */ + + if ( orig_at_data->num_atoms ) + { + memcpy( orig_at_data->atom, at_old, orig_at_data->num_atoms * sizeof(orig_at_data->atom[0]) ); + /* adjust numbering in the newly read structure */ + for ( i = 0; i < num_inp_atoms_new; i ++ ) + { + for ( j = 0; j < at_new[i].num_bonds; j ++ ) + { + at_new[i].neighbor[j] += orig_at_data->num_atoms; + } + } + } + Freeinp_ATOM( &at_old ); + + /* copy newly read structure */ + memcpy( orig_at_data->atom + orig_at_data->num_atoms, + at_new, + num_inp_atoms_new * sizeof(orig_at_data->atom[0]) ); + + /* copy newly read 0D stereo */ + if ( num_inp_0D_new > 0 && stereo0D_new ) + { + if ( orig_at_data->stereo0D = CreateInchi_Stereo0D( nNumStereo0D ) ) + { + int ncopy = orig_at_data->num_stereo0D * sizeof(orig_at_data->stereo0D[0]); + memcpy( orig_at_data->stereo0D, stereo0D_old, ncopy ); + + /* adjust numbering in the newly read structure */ + for ( i = 0; i < num_inp_0D_new; i ++ ) + { + if ( stereo0D_new[i].central_atom >= 0 ) + stereo0D_new[i].central_atom += orig_at_data->num_atoms; + + for ( j = 0; j < 4; j ++ ) + stereo0D_new[i].neighbor[j] += orig_at_data->num_atoms; + } + + FreeInchi_Stereo0D( &stereo0D_old ); + + int ncopy = num_inp_0D_new * sizeof(orig_at_data->stereo0D[0]); + memcpy( orig_at_data->stereo0D+orig_at_data->num_stereo0D, + stereo0D_new, + ncopy ); + } + else + { + num_inp_0D_new = 0; + TREAT_ERR (*err, 0, "Out of RAM"); + *err = -1; + } + } + else + { + num_inp_0D_new = 0; + } + + /* update lengths */ + orig_at_data->num_atoms += num_inp_atoms_new; + orig_at_data->num_stereo0D += num_inp_0D_new; + } + else + { + TREAT_ERR (*err, 0, "Out of RAM"); + *err = -1; + } + } + else if ( num_inp_atoms_new > 0 ) + { + nNumAtoms += num_inp_atoms_new; + } + + Freeinp_ATOM( &at_new ); + num_inp_atoms_new = 0; + FreeInchi_Stereo0D( &stereo0D_new ); + num_inp_0D_new = 0; + } while ( !*err && bMergeAllInputStructures ); + + /* + if ( !*err ) + { + orig_at_data->num_components = + MarkDisconnectedComponents( orig_at_data ); + if ( orig_at_data->num_components == 0 ) + { + TREAT_ERR (*err, 0, "No components found"); + *err = 99; + } + if ( orig_at_data->num_components < 0 ) + { + TREAT_ERR (*err, 0, "Too many components"); + *err = 99; + } + } + */ + + if ( szCoordNew ) + inchi_free( szCoordNew ); + + if ( at_new ) + inchi_free( at_new ); + + /* + if ( !*err ) + { + if ( ReconcileAllCmlBondParities( orig_at_data->atom, orig_at_data->num_atoms ) ) + { + TREAT_ERR (*err, 0, "Cannot reconcile stereobond parities"); + if (!orig_at_data->num_atoms) + { + *err = 1; + } + } + } + */ + + if ( *err ) + FreeInchi_Input( orig_at_data ); + + if ( *err && !(10 < *err && *err < 20) && pStrErr && !pStrErr[0] ) + { + TREAT_ERR (*err, 0, "Unknown error"); /* */ + } + + return orig_at_data? orig_at_data->num_atoms : nNumAtoms; +} + +#endif /* #ifdef TARGET_EXE_USING_API */ + + + + +/* + Read input InChI string and create/fill internal data structures +*/ +int InchiToOrigAtom( INCHI_IOSTREAM *inp_molfile, + ORIG_ATOM_DATA *orig_at_data, + int bMergeAllInputStructures, + int bGetOrigCoord, + int bDoNotAddH, + int vABParityUnknown, + INPUT_TYPE nInputType, + char *pSdfLabel, + char *pSdfValue, + long *lSdfId, + INCHI_MODE *pInpAtomFlags, + int *err, + char *pStrErr ) +{ +/* inp_ATOM *at = NULL; */ +int num_dimensions_new; +int num_inp_bonds_new; +int num_inp_atoms_new; +inp_ATOM *at_new = NULL; +inp_ATOM *at_old = NULL; +int nNumAtoms = 0; +MOL_COORD *szCoordNew = NULL; +MOL_COORD *szCoordOld = NULL; +int i, j; +int max_num_at; + + + max_num_at = MAX_ATOMS; + if ( !( *pInpAtomFlags & FLAG_SET_INP_LARGE_ALLOWED ) ) + max_num_at = NORMALLY_ALLOWED_INP_MAX_ATOMS; + + if ( pStrErr ) + { + pStrErr[0] = '\0'; + } + + /*FreeOrigAtData( orig_at_data );*/ + if ( lSdfId ) + *lSdfId = 0; + do { + at_old = orig_at_data ? orig_at_data->at + : NULL; /* save pointer to the previous allocation */ + szCoordOld = orig_at_data ? orig_at_data->szCoord + : NULL; + + num_inp_atoms_new = InchiToInpAtom( inp_molfile, + (bGetOrigCoord && orig_at_data)? &szCoordNew : NULL, + bDoNotAddH, vABParityUnknown, + nInputType, + orig_at_data? &at_new : NULL, + MAX_ATOMS, &num_dimensions_new, &num_inp_bonds_new, + pSdfLabel, pSdfValue, lSdfId, + pInpAtomFlags, err, pStrErr ); + + if ( num_inp_atoms_new <= 0 && !*err ) + { + TREAT_ERR (*err, 0, "Empty structure"); + *err = 98; + } + else if ( orig_at_data && !num_inp_atoms_new && + 10 < *err && *err < 20 && + orig_at_data->num_inp_atoms > 0 && + bMergeAllInputStructures ) + { + *err = 0; /* end of file */ + break; + } + else if ( num_inp_atoms_new > 0 && orig_at_data ) + { + /* merge pOrigDataTmp + orig_at_data => pOrigDataTmp; */ + nNumAtoms = num_inp_atoms_new + orig_at_data->num_inp_atoms; + if ( nNumAtoms >= max_num_at ) /*MAX_ATOMS ) */ + { + TREAT_ERR (*err, 0, "Too many atoms [check 'LargeMolecules' switch]"); + *err = 70; + orig_at_data->num_inp_atoms = -1; + } + else if ( !at_old ) + { + /* the first structure */ + orig_at_data->at = at_new; + orig_at_data->szCoord = szCoordNew; + at_new = NULL; + szCoordNew = NULL; + orig_at_data->num_inp_atoms = num_inp_atoms_new; + orig_at_data->num_inp_bonds = num_inp_bonds_new; + orig_at_data->num_dimensions = num_dimensions_new; + } + else if ( (orig_at_data->at = ( inp_ATOM* ) inchi_calloc( nNumAtoms, sizeof(inp_ATOM) )) && + (!szCoordNew || (orig_at_data->szCoord = (MOL_COORD *) inchi_calloc( nNumAtoms, sizeof(MOL_COORD) ))) ) + { + /* switch at_new <--> orig_at_data->at; */ + if ( orig_at_data->num_inp_atoms ) + { + memcpy( orig_at_data->at, + at_old, + orig_at_data->num_inp_atoms * sizeof(orig_at_data->at[0]) ); + /* adjust numbering in the newly read structure */ + for ( i = 0; i < num_inp_atoms_new; i ++ ) + { + for ( j = 0; j < at_new[i].valence; j ++ ) + { + at_new[i].neighbor[j] += orig_at_data->num_inp_atoms; + } + at_new[i].orig_at_number += orig_at_data->num_inp_atoms; /* 12-19-2003 */ + } + if ( orig_at_data->szCoord && szCoordOld ) + { + memcpy( orig_at_data->szCoord, + szCoordOld, + orig_at_data->num_inp_atoms * sizeof(MOL_COORD) ); + } + } + if ( at_old ) + { + inchi_free( at_old ); + at_old = NULL; + } + if ( szCoordOld ) + { + inchi_free( szCoordOld ); + szCoordOld = NULL; + } + /* copy newly read structure */ + memcpy( orig_at_data->at + orig_at_data->num_inp_atoms, + at_new, + num_inp_atoms_new * sizeof(orig_at_data->at[0]) ); + if ( orig_at_data->szCoord && szCoordNew ) + { + memcpy( orig_at_data->szCoord + orig_at_data->num_inp_atoms, + szCoordNew, + num_inp_atoms_new * sizeof(MOL_COORD) ); + } + /* add other things */ + orig_at_data->num_inp_atoms += num_inp_atoms_new; + orig_at_data->num_inp_bonds += num_inp_bonds_new; + orig_at_data->num_dimensions = inchi_max(num_dimensions_new, orig_at_data->num_dimensions); + } + else + { + TREAT_ERR (*err, 0, "Out of RAM"); + *err = -1; + } + } + else if ( num_inp_atoms_new > 0 ) + { + nNumAtoms += num_inp_atoms_new; + } + if ( at_new ) + { + inchi_free( at_new ); + at_new = NULL; + } + } while ( !*err && bMergeAllInputStructures ); + /* + if ( !*err ) { + orig_at_data->num_components = + MarkDisconnectedComponents( orig_at_data ); + if ( orig_at_data->num_components == 0 ) { + TREAT_ERR (*err, 0, "No components found"); + *err = 99; + } + if ( orig_at_data->num_components < 0 ) { + TREAT_ERR (*err, 0, "Too many components"); + *err = 99; + } + } + */ + if ( szCoordNew ) + { + inchi_free( szCoordNew ); + } + if ( at_new ) + { + inchi_free( at_new ); + } + if ( !*err && orig_at_data ) + { + /* added testing (orig_at_data != NULL) */ + if ( ReconcileAllCmlBondParities( orig_at_data->at, + orig_at_data->num_inp_atoms, 0 ) ) + { + TREAT_ERR (*err, 0, "Cannot reconcile stereobond parities"); /* */ + if (!orig_at_data->num_dimensions) { + *err = 1; + } + } + } + + if ( *err ) + { + FreeOrigAtData( orig_at_data ); + } + if ( *err && !(10 < *err && *err < 20) && pStrErr && !pStrErr[0] ) + { + TREAT_ERR (*err, 0, "Unknown error"); /* */ + } + + return orig_at_data ? orig_at_data->num_inp_atoms + : nNumAtoms; +} diff --git a/INCHI-1-SRC/INCHI_BASE/src/runichi3.c b/INCHI-1-SRC/INCHI_BASE/src/runichi3.c new file mode 100644 index 0000000..a3ee935 --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/runichi3.c @@ -0,0 +1,2840 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +/* + Pre-processing related functions + +*/ + +#include +#include +#include +#include +#include +#include +#include + + +#include "mode.h" +#include "ichitime.h" +#ifndef COMPILE_ANSI_ONLY +#include +#endif +#include "ichimain.h" +#include "ichi_io.h" +#include "mol_fmt.h" +#include "inchi_api.h" +#include "readinch.h" +#ifdef TARGET_LIB_FOR_WINCHI +#include "../../../IChI_lib/src/ichi_lib.h" +#include "inchi_api.h" +#else +#include "inchi_gui.h" +#endif +#include "readinch.h" +#include "inpdef.h" +#include "ichi_io.h" + + +/* Local prototypes */ +int bCheckUnusualValences( ORIG_ATOM_DATA *orig_at_data, int bAddIsoH, char *pStrErrStruct ); +void throw_away_inappropriate_bond( int at1, int at2, int *nbonds, int **bonds); + + + + + +/* Check inp_ATOM's for unusual valence */ +int bCheckUnusualValences( ORIG_ATOM_DATA *orig_at_data, + int bAddIsoH, char *pStrErrStruct ) +{ +int i, val, num_found = 0; +char msg[32]; +int len, num_H; + + int already_here = ( orig_at_data && orig_at_data->num_inp_atoms > 0 ); + + inp_ATOM *at = already_here ? orig_at_data->at : NULL; + + if ( at ) + { + for ( i = 0, num_found = 0; i < orig_at_data->num_inp_atoms; i ++ ) + { + num_H = bAddIsoH ? NUMH(at,i) : at[i].num_H; + + val = detect_unusual_el_valence( at[i].el_number, + at[i].charge, + at[i].radical, + at[i].chem_bonds_valence, + num_H, + at[i].valence ); + if ( val ) + { + num_found ++; + WarningMessage(pStrErrStruct, "Accepted unusual valence(s):"); + len = sprintf( msg, "%s", at[i].elname ); + if ( at[i].charge ) + { + len += sprintf( msg+len, "%+d", at[i].charge ); + } + if ( at[i].radical ) + { + len += sprintf( msg + len, ",%s", at[i].radical == RADICAL_SINGLET? "s" : + at[i].radical == RADICAL_DOUBLET? "d" : + at[i].radical == RADICAL_TRIPLET? "t" : "?" ); + } + len += sprintf( msg + len, "(%d)", val ); + WarningMessage(pStrErrStruct, msg); + } + } + } + + return num_found; +} + + +/* + Make a copy of ORIG_ATOM_DATA structure + ( inp_ATOM array, etc., etc. ) +*/ +int DuplicateOrigAtom( ORIG_ATOM_DATA *new_orig_atom, + ORIG_ATOM_DATA *orig_atom ) +{ + inp_ATOM *at = NULL; + AT_NUMB *nCurAtLen = NULL; + AT_NUMB *nOldCompNumber = NULL; + + if ( new_orig_atom->at && + new_orig_atom->num_inp_atoms >= orig_atom->num_inp_atoms ) + { + at = new_orig_atom->at; + } + else + { + at = (inp_ATOM *)inchi_calloc( orig_atom->num_inp_atoms+1, + sizeof(at[0])); + } + + if ( new_orig_atom->nOldCompNumber && + new_orig_atom->num_components >= orig_atom->num_components ) + { + nCurAtLen = new_orig_atom->nCurAtLen; + } + else + { + nCurAtLen = (AT_NUMB *)inchi_calloc( orig_atom->num_components+1, + sizeof(nCurAtLen[0])); + } + + if ( new_orig_atom->nCurAtLen && + new_orig_atom->num_components >= orig_atom->num_components ) + { + nOldCompNumber = new_orig_atom->nOldCompNumber; + } + else + { + nOldCompNumber = (AT_NUMB *)inchi_calloc( orig_atom->num_components+1, + sizeof(nOldCompNumber[0])); + } + + if ( at && nCurAtLen && nOldCompNumber ) + { + /* Copy */ + if ( orig_atom->at ) + memcpy( at, orig_atom->at, + orig_atom->num_inp_atoms * sizeof(new_orig_atom->at[0]) ); + if ( orig_atom->nCurAtLen ) + memcpy( nCurAtLen, orig_atom->nCurAtLen, + orig_atom->num_components*sizeof(nCurAtLen[0]) ); + if ( orig_atom->nOldCompNumber ) + memcpy( nOldCompNumber, orig_atom->nOldCompNumber, + orig_atom->num_components*sizeof(nOldCompNumber[0]) ); + + /* Deallocate */ + if ( new_orig_atom->at && new_orig_atom->at != at ) + inchi_free( new_orig_atom->at ); + if ( new_orig_atom->nCurAtLen && new_orig_atom->nCurAtLen!=nCurAtLen ) + inchi_free( new_orig_atom->nCurAtLen ); + if ( new_orig_atom->nOldCompNumber && + new_orig_atom->nOldCompNumber != nOldCompNumber ) + inchi_free( new_orig_atom->nOldCompNumber ); + + *new_orig_atom = *orig_atom; + new_orig_atom->at = at; + new_orig_atom->nCurAtLen = nCurAtLen; + new_orig_atom->nOldCompNumber = nOldCompNumber; + + /* Data that are not to be copied */ + new_orig_atom->nNumEquSets = 0; + memset(new_orig_atom->bSavedInINCHI_LIB, 0, sizeof(new_orig_atom->bSavedInINCHI_LIB)); + memset(new_orig_atom->bPreprocessed, 0, sizeof(new_orig_atom->bPreprocessed)); + + /* Arrays that are not to be copied */ + new_orig_atom->szCoord = NULL; + new_orig_atom->nEquLabels = NULL; + new_orig_atom->nSortedOrder = NULL; + + new_orig_atom->polymer = NULL; + new_orig_atom->v3000 = NULL; + + return 0; + } + + /* Deallocate */ + if ( at && new_orig_atom->at != at ) + inchi_free( at ); + if ( nCurAtLen && new_orig_atom->nCurAtLen != nCurAtLen ) + inchi_free( nCurAtLen ); + if ( nOldCompNumber && new_orig_atom->nOldCompNumber != nOldCompNumber ) + inchi_free( nOldCompNumber ); + + return -1; /* Failed */ +} + + +/* + Preprocess the whole structure + + The plan is: + + 1. Copy orig_inp_data --> prep_inp_data (then work with the latter) + + 2. Fix odd things in prep_inp_data + + Find whether the structure can be disconnected or is a salt + - check if needs salt disconnection + - check if needs metal disconnection + + 3. If ( orig_inp_data->bDisconnectSalts ) then + disconnect salts in prep_inp_data + + Mark the (disconnected) components in prep_inp_data + + Detect isotopic H on heteroatoms (necessary condition + for global isotopic tautomerism) + + 4. Detect unusual valences (should be called before metal disconnection) + + 5. Create metal-disconnected structure if applicable. + - save reconnected structure in prep_inp_data+1 if requested + - make Disconnected structure in prep_inp_data + +*/ +int PreprocessOneStructure( struct tagINCHI_CLOCK *ic, + STRUCT_DATA *sd, + INPUT_PARMS *ip, + ORIG_ATOM_DATA *orig_inp_data, + ORIG_ATOM_DATA *prep_inp_data ) +{ +int i; +INCHI_MODE bTautFlags = 0; +INCHI_MODE bTautFlagsDone = 0; + + + + /* 1. Copy orig_inp_data --> prep_inp_data */ + + if ( 0 > DuplicateOrigAtom( prep_inp_data, orig_inp_data ) ) + { + AddErrorMessage(sd->pStrErrStruct, "Out of RAM"); + sd->nStructReadError = 99; + sd->nErrorType = _IS_FATAL; + goto exit_function; + } + +#if ( bRELEASE_VERSION == 0 && (EXTR_HAS_METAL_ATOM & (EXTR_MASK | EXTR_FLAG) ) ) + if ( bHasMetalAtom( orig_inp_data ) ) { + sd->bExtract |= EXTR_HAS_METAL_ATOM; + } +#endif + + + + /* 2. Fix odd things in prep_inp_data */ + + if ( 0 < fix_odd_things( prep_inp_data->num_inp_atoms, prep_inp_data->at, /*0*/ip->bTautFlags & TG_FLAG_FIX_SP3_BUG, ip->bFixNonUniformDraw ) ) + { + /* changed 2010-03-17 DT */ + WarningMessage(sd->pStrErrStruct, "Charges were rearranged"); + if ( sd->nErrorType < _IS_WARNING ) + { + sd->nErrorType = _IS_WARNING; + } + sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ODD_THINGS_DONE; + } + +#if ( FIX_ADJ_RAD == 1 ) + if ( ip->bTautFlags & TG_FLAG_FIX_ADJ_RADICALS ) { + if ( 0 < FixAdjacentRadicals( prep_inp_data->num_inp_atoms, prep_inp_data->at ) ) { + sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ADJ_RADICALS_DONE; + } + } +#endif + +#if ( bRELEASE_VERSION == 0 && (EXTR_FLAGS & EXTR_HAS_FEATURE) ) + if ( bFoundFeature( prep_inp_data->at, prep_inp_data->num_inp_atoms ) ) { + sd->bExtract |= EXTR_HAS_FEATURE; + } +#endif + + + /* Find whether the structure can be disconnected or is a salt */ + + /* Needs salt disconnection? */ + + if ( ip->bTautFlags & TG_FLAG_DISCONNECT_SALTS ) + { + prep_inp_data->bDisconnectSalts = (0 < DisconnectSalts( prep_inp_data, 0 )); + } + else + { + prep_inp_data->bDisconnectSalts = 0; + } + + /* Needs metal disconnection? */ + + if ( ip->bTautFlags & TG_FLAG_DISCONNECT_COORD ) + { + i = (0 != (ip->bTautFlags & TG_FLAG_CHECK_VALENCE_COORD)); + bMayDisconnectMetals( prep_inp_data, i, &bTautFlagsDone ); /* changes prep_inp_data->bDisconnectCoord */ + sd->bTautFlagsDone[INCHI_BAS] |= bTautFlagsDone; /* whether any disconnection has been rejected because of the metal proper valence */ + +#if ( bRELEASE_VERSION == 0 ) + if ( i && (bTautFlagsDone & TG_FLAG_CHECK_VALENCE_COORD_DONE) ) { + sd->bExtract |= EXTR_METAL_WAS_NOT_DISCONNECTED; + } +#endif + } + else + { + prep_inp_data->bDisconnectCoord = 0; + } + orig_inp_data->bDisconnectSalts = prep_inp_data->bDisconnectSalts; + orig_inp_data->bDisconnectCoord = prep_inp_data->bDisconnectCoord; + + + + /* 3. if( orig_inp_data->bDisconnectSalts ) then + disconnect salts in prep_inp_data */ + + if ( ( ip->bTautFlags & TG_FLAG_DISCONNECT_SALTS ) && prep_inp_data->bDisconnectSalts && + 0 < (i=DisconnectSalts( prep_inp_data, 1 )) ) + { + WarningMessage(sd->pStrErrStruct, "Salt was disconnected"); + sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_DISCONNECT_SALTS_DONE; + if ( sd->nErrorType < _IS_WARNING ) + { + sd->nErrorType = _IS_WARNING; + } + if ( i = ReconcileAllCmlBondParities( prep_inp_data->at, prep_inp_data->num_inp_atoms, 0 ) ) + { + char szErrCode[16]; + sprintf( szErrCode, "%d", i); + AddErrorMessage( sd->pStrErrStruct, "0D Parities Reconciliation failed:" ); + AddErrorMessage( sd->pStrErrStruct, szErrCode ); + } + +#if ( bRELEASE_VERSION == 0 ) + sd->bExtract |= EXTR_SALT_WAS_DISCONNECTED; +#endif + } + else + { + prep_inp_data->bDisconnectSalts = 0; + } + + + /* Mark the (disconnected) components in prep_inp_data */ + + prep_inp_data->num_components = MarkDisconnectedComponents( prep_inp_data, 0 ); + + if ( prep_inp_data->num_components < 0 ) + { + AddErrorMessage(sd->pStrErrStruct, "Out of RAM"); + sd->nStructReadError = 99; + sd->nErrorType = _IS_FATAL; + goto exit_function; + } + + + /* Detect isotopic H on heteroatoms -- necessary condition + for global isotopic tautomerism */ + + if ( i = bNumHeterAtomHasIsotopicH( prep_inp_data->at, prep_inp_data->num_inp_atoms ) ) + { + if ( i & 1 ) + { + sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FOUND_ISOTOPIC_H_DONE; + } + if ( i & 2 ) + { + sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FOUND_ISOTOPIC_ATOM_DONE; + } + } + + + /* 4a. Detect unusual valences */ + + if ( bCheckUnusualValences( prep_inp_data, 1, sd->pStrErrStruct ) ) + { + +#if ( bRELEASE_VERSION == 0 ) + sd->bExtract |= EXTR_UNUSUAL_VALENCES; +#else + ; +#endif + } + + + /* 5. if( orig_inp_data->bDisconnectCoord ) then + -- copy prep_inp_data --> prep_inp_data+1 + -- disconnect metals in prep_inp_data */ + + if ( prep_inp_data->bDisconnectCoord ) + { + + prep_inp_data->num_components = MarkDisconnectedComponents( prep_inp_data, 0 ); + if ( prep_inp_data->num_components < 0 ) { + AddErrorMessage(sd->pStrErrStruct, "Out of RAM"); + sd->nStructReadError = 99; + sd->nErrorType = _IS_FATAL; + goto exit_function; + } + + /* Save reconnected structure in prep_inp_data+1 if requested */ + if ( 0 != ( ip->bTautFlags & TG_FLAG_RECONNECT_COORD) ) + { + if ( 0 > DuplicateOrigAtom( prep_inp_data+1, prep_inp_data ) ) + { + AddErrorMessage(sd->pStrErrStruct, "Out of RAM"); + sd->nStructReadError = 99; + sd->nErrorType = _IS_FATAL; + goto exit_function; + } + sd->bTautFlags[INCHI_REC] = sd->bTautFlags[INCHI_BAS]; + sd->bTautFlagsDone[INCHI_REC] = sd->bTautFlagsDone[INCHI_BAS]; + { + /* Remove "parity undefined in disconnected structure" flag from reconnected structure */ + int k, m, p; + inp_ATOM *at = (prep_inp_data+1)->at; + int num_at = (prep_inp_data+1)->num_inp_atoms; + for ( k = 0; k < num_at; k ++ ) { + for ( m = 0; m < MAX_NUM_STEREO_BONDS && (p=at[k].sb_parity[m]); m ++ ) { + at[k].sb_parity[m] &= SB_PARITY_MASK; + } + } + } + } + + /* Make disconnected structure in prep_inp_data */ + i = (0 != ( ip->bTautFlags & TG_FLAG_CHECK_VALENCE_COORD )); + + /* prep_inp_data->bDisconnectCoord > 1 means add + prep_inp_data->bDisconnectCoord-1 explicit H atoms */ + if ( 0 < (i = DisconnectMetals( prep_inp_data, i, &bTautFlagsDone ) ) ) + { + WarningMessage(sd->pStrErrStruct, "Metal was disconnected"); + sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_DISCONNECT_COORD_DONE; + if ( sd->nErrorType < _IS_WARNING ) + { + sd->nErrorType = _IS_WARNING; + } + +#if ( bRELEASE_VERSION == 0 ) + sd->bExtract |= EXTR_METAL_WAS_DISCONNECTED; +#endif + + /* last parm=1 means find link between unchanged by Metal Disconnection components */ + prep_inp_data->num_components = MarkDisconnectedComponents( prep_inp_data, 1 ); + + if ( prep_inp_data->num_components < 0 ) + { + AddErrorMessage(sd->pStrErrStruct, "Out of RAM"); + sd->nStructReadError = 99; + sd->nErrorType = _IS_FATAL; + goto exit_function; + } + + { + /* Set parities for the disconnected structure */ + int k, m, p; + inp_ATOM *at = (prep_inp_data)->at; + int num_at = (prep_inp_data)->num_inp_atoms; + for ( k = 0; k < num_at; k ++ ) { + for ( m = 0; m < MAX_NUM_STEREO_BONDS && (p=at[k].sb_parity[m]); m ++ ) { + if ( p & SB_PARITY_FLAG ) { + at[k].sb_parity[m] = (p >> SB_PARITY_SHFT) & SB_PARITY_MASK; + } + } + } + } + + if ( i = ReconcileAllCmlBondParities( prep_inp_data->at, prep_inp_data->num_inp_atoms, 1 ) ) + { + char szErrCode[16]; + sprintf( szErrCode, "%d", i); + AddErrorMessage( sd->pStrErrStruct, "0D Parities Reconciliation failed:" ); + AddErrorMessage( sd->pStrErrStruct, szErrCode ); + } + +#if ( REMOVE_ION_PAIRS_DISC_STRU == 1 ) + if ( 0 < remove_ion_pairs( prep_inp_data->num_inp_atoms, prep_inp_data->at ) ) + { + WarningMessage(sd->pStrErrStruct, "Charges were rearranged"); + if ( sd->nErrorType < _IS_WARNING ) + { + sd->nErrorType = _IS_WARNING; + } + sd->bTautFlagsDone[INCHI_REC] |= TG_FLAG_FIX_ODD_THINGS_DONE; + sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ODD_THINGS_DONE; + } +#endif + + /* + if prep_inp_data->nOldCompNumber[i] = iINChI+1 > 0 then + component #(i+1) in prep_inp_data is identical to component #(iINChI+1) in prep_inp_data+1 + */ + } + else if ( i < 0 ) + { + AddErrorMessage(sd->pStrErrStruct, "Cannot disconnect metal error"); + sd->nStructReadError = i; + sd->nErrorType = _IS_ERROR; + goto exit_function; + } + } + else + { + /* Remove "disconnected structure parities" from the structure */ + int k, m, p; + inp_ATOM *at = (prep_inp_data)->at; + int num_at = (prep_inp_data)->num_inp_atoms; + for ( k = 0; k < num_at; k ++ ) { + for ( m = 0; m < MAX_NUM_STEREO_BONDS && (p=at[k].sb_parity[m]); m ++ ) { + at[k].sb_parity[m] &= SB_PARITY_MASK; + } + } + } + + + +exit_function: + if ( sd->nErrorType < _IS_ERROR && prep_inp_data ) + { + if ( 0 < post_fix_odd_things( prep_inp_data->num_inp_atoms, prep_inp_data->at ) ) + { + WarningMessage(sd->pStrErrStruct, "Charges were rearranged"); + if ( sd->nErrorType < _IS_WARNING ) + sd->nErrorType = _IS_WARNING; + sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ODD_THINGS_DONE; + } + if ( (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) && + (prep_inp_data+1)->at && (prep_inp_data+1)->num_inp_atoms > 0 ) + { + if ( 0 < post_fix_odd_things( (prep_inp_data+1)->num_inp_atoms, (prep_inp_data+1)->at ) ) + { + WarningMessage(sd->pStrErrStruct, "Charges were rearranged"); + if ( sd->nErrorType < _IS_WARNING ) + sd->nErrorType = _IS_WARNING; + + sd->bTautFlagsDone[INCHI_REC] |= TG_FLAG_FIX_ODD_THINGS_DONE; + sd->bTautFlagsDone[INCHI_BAS] |= TG_FLAG_FIX_ODD_THINGS_DONE; + } + } + } + + sd->bTautFlags[INCHI_BAS] |= bTautFlags; /* TG_FLAG_CHECK_VALENCE_COORD_DONE, TG_FLAG_MOVE_CHARGE_COORD_DONE */ + sd->bTautFlagsDone[INCHI_BAS] |= bTautFlagsDone; /* TG_FLAG_CHECK_VALENCE_COORD_DONE, TG_FLAG_MOVE_CHARGE_COORD_DONE */ + + return sd->nErrorType; +} + + + + +#ifndef TARGET_API_LIB + + +/* + Create Composite Norm Atom +*/ +int CreateCompositeNormAtom(COMP_ATOM_DATA *composite_norm_data, + INP_ATOM_DATA2 *all_inp_norm_data, + int num_components) +{ + int i, j, jj, k, n, m, tot_num_at, tot_num_H, cur_num_at, cur_num_H, nNumRemovedProtons; + int num_comp[TAUT_NUM+1], num_taut[TAUT_NUM+1], num_del[TAUT_NUM+1], num_at[TAUT_NUM+1], num_inp_at[TAUT_NUM+1]; + int ret = 0, indicator = 1; + inp_ATOM *at, *at_from; + memset( num_comp, 0, sizeof(num_comp) ); + memset( num_taut, 0, sizeof(num_taut) ); + memset( num_del, 0, sizeof(num_taut) ); + /* count taut and non-taut components */ + for ( j = 0; j < TAUT_NUM; j ++ ) { + num_comp[j] = num_taut[j] = 0; + for ( i = 0; i < num_components; i ++ ) { + if ( all_inp_norm_data[i][j].bExists ) { + num_del[j] += (0 != all_inp_norm_data[i][j].bDeleted ); + num_comp[j] ++; + num_taut[j] += (0 != all_inp_norm_data[i][j].bTautomeric); + } + } + } + /* count intermediate taut structure components */ + if ( num_comp[TAUT_YES] > num_del[TAUT_YES] && num_taut[TAUT_YES] ) { + /* + num_comp[TAUT_INI] = num_comp[TAUT_YES] - num_del[TAUT_YES]; + */ + + for ( i = 0, j=TAUT_YES; i < num_components; i ++ ) { + if ( all_inp_norm_data[i][j].bExists && + (all_inp_norm_data[i][j].bDeleted || + all_inp_norm_data[i][j].bTautomeric && + all_inp_norm_data[i][j].at_fixed_bonds && + all_inp_norm_data[i][j].bTautPreprocessed) ) { + num_comp[TAUT_INI] ++; + } + } + } + /* count atoms and allocate composite atom data */ + for ( jj = 0; jj <= TAUT_INI; jj ++ ) { + num_at[jj] = num_inp_at[jj] = 0; + j = inchi_min (jj, TAUT_YES); + if ( num_comp[jj] ) { + for ( i = 0; i < num_components; i ++ ) { + if ( all_inp_norm_data[i][j].bDeleted ) + continue; + /* find k = the normaized structure index */ + if ( jj == TAUT_INI ) { + if ( all_inp_norm_data[i][j].bExists && + all_inp_norm_data[i][j].at_fixed_bonds ) { + k = j; + } else + if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted && + !all_inp_norm_data[i][j].bDeleted ) { + k = ALT_TAUT(j); + } else + if ( all_inp_norm_data[i][j].bExists ) { + k = j; + } else { + continue; + } + } else { + if ( all_inp_norm_data[i][j].bExists ) { + k = j; + } else + if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted) { + k = ALT_TAUT(j); + } else { + continue; + } + } + num_inp_at[jj] += all_inp_norm_data[i][k].num_at; /* all atoms including terminal H */ + num_at[jj] += all_inp_norm_data[i][k].num_at - all_inp_norm_data[i][k].num_removed_H; + } + if ( num_inp_at[jj] ) { + if ( !CreateCompAtomData( composite_norm_data+jj, num_inp_at[jj], num_components, jj == TAUT_INI ) ) + goto exit_error; + composite_norm_data[jj].num_removed_H = num_inp_at[jj] - num_at[jj]; + } + } + } + /* fill out composite atom */ + for ( jj = 0; jj <= TAUT_INI; jj ++, indicator <<= 1 ) { + j = inchi_min (jj, TAUT_YES); + if ( num_comp[jj] ) { + tot_num_at = 0; + tot_num_H = 0; + for ( i = 0; i < num_components; i ++ ) { + if ( all_inp_norm_data[i][j].bDeleted ) { + composite_norm_data[jj].nNumRemovedProtons += all_inp_norm_data[i][j].nNumRemovedProtons; + for ( n = 0; n < NUM_H_ISOTOPES; n ++ ) { + composite_norm_data[jj].nNumRemovedProtonsIsotopic[n] += all_inp_norm_data[i][j].nNumRemovedProtonsIsotopic[n]; + } + continue; + } + nNumRemovedProtons = 0; + k = TAUT_NUM; + /* find k = the normaized structure index */ + if ( jj == TAUT_INI ) { + if ( all_inp_norm_data[i][j].bExists && all_inp_norm_data[i][j].at_fixed_bonds ) { + k = j; + } else + if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists ) { + k = ALT_TAUT(j); + } else + if ( all_inp_norm_data[i][j].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted ) { + k = j; + } else { + continue; + } + } else { + if ( all_inp_norm_data[i][j].bExists ) { + k = j; + } else + if ( all_inp_norm_data[i][ALT_TAUT(j)].bExists && !all_inp_norm_data[i][ALT_TAUT(j)].bDeleted ) { + k = ALT_TAUT(j); + } else { + continue; + } + } + /* copy main atoms */ + cur_num_H = all_inp_norm_data[i][k].num_removed_H; /* number of terminal H atoms */ + cur_num_at = all_inp_norm_data[i][k].num_at - cur_num_H; /* number of all but explicit terminal H atoms */ + + if ( (tot_num_at + cur_num_at) > num_at[jj] || + (num_at[jj] + tot_num_H + cur_num_H) > num_inp_at[jj] ) { + goto exit_error; /* miscount */ + } + at = composite_norm_data[jj].at+tot_num_at; /* points to the 1st destination atom */ + at_from = (jj == TAUT_INI && k == TAUT_YES && all_inp_norm_data[i][k].at_fixed_bonds)? + all_inp_norm_data[i][k].at_fixed_bonds : all_inp_norm_data[i][k].at; + memcpy( at, at_from, sizeof(composite_norm_data[0].at[0]) * cur_num_at ); /* copy atoms except terminal H */ + /* shift neighbors of main atoms */ + for ( n = 0; n < cur_num_at; n ++, at ++ ) { + for ( m = 0; m < at->valence; m ++ ) { + at->neighbor[m] += tot_num_at; + } + } + /* copy explicit H */ + if ( cur_num_H ) { + at = composite_norm_data[jj].at+num_at[jj]+tot_num_H; /* points to the 1st destination atom */ + memcpy( at, at_from+cur_num_at, + sizeof(composite_norm_data[0].at[0]) * cur_num_H ); + /* shift neighbors of explicit H atoms */ + for ( n = 0; n < cur_num_H; n ++, at ++ ) { + for ( m = 0; m < at->valence; m ++ ) { + at->neighbor[m] += tot_num_at; + } + } + } + /* composite counts */ + composite_norm_data[jj].bHasIsotopicLayer |= all_inp_norm_data[i][k].bHasIsotopicLayer; + composite_norm_data[jj].num_isotopic += all_inp_norm_data[i][k].num_isotopic; + composite_norm_data[jj].num_bonds += all_inp_norm_data[i][k].num_bonds; + composite_norm_data[jj].bTautomeric += (j == jj) && all_inp_norm_data[i][k].bTautomeric; + composite_norm_data[jj].nNumRemovedProtons += all_inp_norm_data[i][k].nNumRemovedProtons; + for ( n = 0; n < NUM_H_ISOTOPES; n ++ ) { + composite_norm_data[jj].nNumRemovedProtonsIsotopic[n] += all_inp_norm_data[i][k].nNumRemovedProtonsIsotopic[n]; + composite_norm_data[jj].num_iso_H[n] += all_inp_norm_data[i][k].num_iso_H[n]; + } + /* + composite_norm_data[j].num_at += cur_num_at + cur_num_H; + composite_norm_data[j].num_removed_H += cur_num_H; + */ + /* total count */ + tot_num_at += cur_num_at; + tot_num_H += cur_num_H; + /* offset for the next component */ + if ( composite_norm_data[jj].nOffsetAtAndH ) { + composite_norm_data[jj].nOffsetAtAndH[2*i] = tot_num_at; + composite_norm_data[jj].nOffsetAtAndH[2*i+1] = num_at[jj]+tot_num_H; + } + } + if ( tot_num_at != num_at[jj] || + num_at[jj] + tot_num_H != num_inp_at[jj] ) { + goto exit_error; /* miscount */ + } + composite_norm_data[jj].bExists = (tot_num_at>0); + ret |= indicator; + } + } + return ret; + +exit_error: + return ret; +} +#endif + + +/* + Make a copy of ORIG_ATOM_DATA structure + ( inp_ATOM array, etc., etc. ) +*/ +int OrigAtData_CreateCopy( ORIG_ATOM_DATA *new_orig_atom, + ORIG_ATOM_DATA *orig_atom ) +{ + inp_ATOM *at = NULL; + AT_NUMB *nCurAtLen = NULL; + AT_NUMB *nOldCompNumber = NULL; + + at = (inp_ATOM *)inchi_calloc( orig_atom->num_inp_atoms+1, + sizeof(at[0])); + + nCurAtLen = (AT_NUMB *)inchi_calloc( orig_atom->num_components+1, + sizeof(nCurAtLen[0])); + + nOldCompNumber = (AT_NUMB *)inchi_calloc( orig_atom->num_components+1, + sizeof(nOldCompNumber[0])); + + if ( at && nCurAtLen && nOldCompNumber ) + { + /* Copy */ + if ( orig_atom->at ) + memcpy( at, orig_atom->at, + orig_atom->num_inp_atoms * sizeof(new_orig_atom->at[0]) ); + if ( orig_atom->nCurAtLen ) + memcpy( nCurAtLen, orig_atom->nCurAtLen, + orig_atom->num_components*sizeof(nCurAtLen[0]) ); + if ( orig_atom->nOldCompNumber ) + memcpy( nOldCompNumber, orig_atom->nOldCompNumber, + orig_atom->num_components*sizeof(nOldCompNumber[0]) ); + + /* Deallocate */ + if ( new_orig_atom->at && new_orig_atom->at != at ) + inchi_free( new_orig_atom->at ); + if ( new_orig_atom->nCurAtLen && new_orig_atom->nCurAtLen!=nCurAtLen ) + inchi_free( new_orig_atom->nCurAtLen ); + if ( new_orig_atom->nOldCompNumber && + new_orig_atom->nOldCompNumber != nOldCompNumber ) + inchi_free( new_orig_atom->nOldCompNumber ); + + *new_orig_atom = *orig_atom; + new_orig_atom->at = at; + + new_orig_atom->nCurAtLen = nCurAtLen; + new_orig_atom->nOldCompNumber = nOldCompNumber; + + new_orig_atom->nCurAtLen = nCurAtLen; + new_orig_atom->nOldCompNumber = nOldCompNumber; + + /* Data that are not to be copied */ + new_orig_atom->nNumEquSets = 0; /*** no matter ***/ + memset(new_orig_atom->bSavedInINCHI_LIB, 0, sizeof(new_orig_atom->bSavedInINCHI_LIB)); /*** no matter ***/ + memset(new_orig_atom->bPreprocessed, 0, sizeof(new_orig_atom->bPreprocessed)); /*** no matter ***/ + + /* Arrays that are not to be copied */ + new_orig_atom->szCoord = NULL; /*** no matter ***/ + new_orig_atom->nEquLabels = NULL; /*** no matter ***/ + new_orig_atom->nSortedOrder = NULL; /*** no matter ***/ + + new_orig_atom->bDisconnectCoord = orig_atom->bDisconnectCoord; + new_orig_atom->bDisconnectSalts = orig_atom->bDisconnectSalts; + + new_orig_atom->polymer = NULL; + new_orig_atom->v3000 = NULL; + + return 0; + } + + /* Deallocate */ + if ( at && new_orig_atom->at != at ) + inchi_free( at ); + if ( nCurAtLen && new_orig_atom->nCurAtLen != nCurAtLen ) + inchi_free( nCurAtLen ); + if ( nOldCompNumber && new_orig_atom->nOldCompNumber != nOldCompNumber ) + inchi_free( nOldCompNumber ); + + return -1; /* Failed */ +} + + +/* + OrigAtData debug output +*/ +void OrigAtData_DebugTrace( ORIG_ATOM_DATA* d ) +{ +int i, k; + + ITRACE_( "\n\n*********************************************************************\n* ORIG_ATOM_DATA @ 0x%p", d ); + ITRACE_( "\n* num_inp_atoms = %-d\n* num_inp_bonds = %-d\n* num_dimensions = %-d\n* num_components = %-d", + d->num_inp_atoms, d->num_inp_bonds, d->num_dimensions, d->num_components ); + ITRACE_( "\n* ATOMS"); + for (i=0; inum_inp_atoms; i++) + { + ITRACE_( "\n* #%-5d %s%-d ( charge %-d, rad %-d nH %-d val %-d) [%-f %-f %-f]", + i, d->at[i].elname, d->at[i].orig_at_number, d->at[i].charge, d->at[i].radical, d->at[i].num_H, d->at[i].valence, + d->at[i].x, d->at[i].y, d->at[i].z); + if ( d->at[i].valence > 0 ) + { + ITRACE_( "\n bonds to " ); + for (k=0; k < d->at[i].valence; k++) + ITRACE_( "%-3d ", d->at[i].neighbor[k] ); + } + if ( d->at[i].valence > 0 ) + { + ITRACE_( "\n bond types " ); + for (k=0; k < d->at[i].valence; k++) + ITRACE_( "%-3d ", d->at[i].bond_type[k] ); + } + } + /*OrigAtDataPolymer_DebugTrace( d->polymer );*/ + ITRACE_( "\n* V3000 INFO @ 0x%-p", d->v3000 ); + ITRACE_( "\n*\n" ); + if ( d->v3000 ) + { + ITRACE_( "\n* n_star_atoms = %-d\n* n_haptic_bonds = %-d\n* n_collections = %-d", + d->v3000->n_star_atoms , d->v3000->n_haptic_bonds, d->v3000->n_collections ); + } + ITRACE_( "\n*\n* End ORIG_ATOM_DATA\n*********************************************************************\n" ); + + return; +} + + + + +/* + + Polymer related procedures + +*/ + + + + +/* Create a new OrigAtDataPolymerUnit */ +OrigAtDataPolymerUnit * OrigAtDataPolymerUnit_New( int maxatoms, int maxbonds, + int id, int label, int type, + int subtype, int conn, + char *smt, + int na, INT_ARRAY *alist, + int nb, INT_ARRAY *blist, + int npsbonds, int **psbonds ) +{ +int k, err = 0; +OrigAtDataPolymerUnit *u2 = NULL; + + u2 = (OrigAtDataPolymerUnit*) inchi_calloc( 1, sizeof(OrigAtDataPolymerUnit) ); + if ( NULL==u2 ) + { + err = 1; + goto exitf; + } + u2->id = id; + u2->label = label; + u2->type = type; + u2->subtype = subtype; + u2->conn = conn; + u2->na = na; + u2->nb = nb; + u2->real_kind = POLYMER_UNIT_KIND_UNKNOWN; + u2->disjoint = 0; + u2->closeable = CLOSING_SRU_NOT_APPLICABLE; + u2->already_closed = 0; + for (k=0;k<4;k++) + { + u2->xbr1[k] = 0.0; + u2->xbr2[k] = 0.0; + } + strcpy( u2->smt, smt ); + u2->star1 = 0; + u2->star_partner1 = 0; + u2->star2 = 0; + u2->star_partner2 = 0; + u2-> maxpsbonds = maxbonds; + u2->npsbonds = npsbonds; + + u2->alist = NULL; + if ( na > 0 || maxatoms > 0 ) + { + u2->alist = (int *) inchi_calloc( na > 0 ? na : maxatoms, sizeof(int) ); + if ( !u2->alist ) + { + err = 2; + goto exitf; + } + for (k=0; kalist[k] = alist->item[k]; + } + u2->blist = NULL; + if ( nb > 0 || maxbonds > 0 ) + { + u2->blist = (int *) inchi_calloc( nb > 0 ? 2*nb : 2*maxbonds, sizeof(int) ); + if ( !u2->blist ) + { + err = 3; + goto exitf; + } + if ( blist ) + { + for (k=0; k<2*nb; k++) + u2->blist[k] = blist->item[k]; + } + } + u2->psbonds = NULL; + +exitf: + if ( err ) + { + OrigAtDataPolymerUnit_Free( u2 ); + return NULL; + } + + return u2; +} + + +/* Create a copy of OrigAtDataPolymerUnit */ +OrigAtDataPolymerUnit * +OrigAtDataPolymerUnit_CreateCopy(OrigAtDataPolymerUnit *u) +{ +int k, err = 0; +OrigAtDataPolymerUnit *u2 = NULL; + + u2 = (OrigAtDataPolymerUnit*) inchi_calloc( 1, sizeof(OrigAtDataPolymerUnit) ); + if ( NULL==u2 ) + { + err = 1; + goto exitf; + } + u2->id = u->id; + u2->type = u->type; + u2->subtype = u->subtype; + u2->conn = u->conn; + u2->label = u->label; + u2->na = u->na; + u2->nb = u->nb; + u2->real_kind = u->real_kind; + u2->disjoint = u->disjoint; + u2->closeable = u->closeable; + u2->already_closed = u->already_closed; + for (k=0;k<4;k++) + { + u2->xbr1[k] = u->xbr1[k]; + u2->xbr2[k] = u->xbr2[k]; + } + strcpy( u2->smt, u->smt ); + u2->star1 = u->star1; + u2->star_partner1 = u->star_partner1; + u2->star2 = u->star2; + u2->star_partner2 = u->star_partner2; + u2->npsbonds = u->npsbonds; + u2-> maxpsbonds = inchi_max( u->maxpsbonds, u->npsbonds ); + + u2->alist = (int *) inchi_calloc( u2->na, sizeof(int) ); + if ( NULL == u2->alist ) + { + err = 2; + goto exitf; + } + for (k=0; kna; k++) + u2->alist[k] = u->alist[k]; + + u2->blist = (int *) inchi_calloc( 2*u2->nb, sizeof(int) ); + if ( NULL == u2->blist ) + { + err = 2; + goto exitf; + } + for (k=0; k<2*u2->nb; k++) + u2->blist[k] = u->blist[k]; + + err = imat_new( u2->maxpsbonds, 2, &(u2->psbonds) ); + if ( !err ) + { + for ( k = 0; knpsbonds; k++ ) + { + u2->psbonds[k][0] = u->psbonds[k][0]; + u2->psbonds[k][1] = u->psbonds[k][1]; + } + } + +exitf: + if ( err ) + { + OrigAtDataPolymerUnit_Free( u2 ); + return NULL; + } + return u2; +} + + +/* OrigAtDataPolymerUnit_Free */ +void OrigAtDataPolymerUnit_Free( OrigAtDataPolymerUnit *unit ) +{ + ITRACE_("\n************** About to free OrigAtDataPolymerUnit @ %-p\n", unit ); + OrigAtDataPolymerUnit_DebugTrace( unit); + if ( unit ) + { + if ( unit->alist ) { inchi_free( unit->alist ); unit->alist = NULL; } + if ( unit->blist ) { inchi_free( unit->blist ); unit->blist = NULL; } + if ( unit->psbonds ) + { + imat_free( unit->maxpsbonds, unit->psbonds ); + unit->psbonds = NULL; + } + } + inchi_free( unit ); + return; +} + + +/* + Compare two polymer units, modified lexicographic order + Modification: unit with smaller alist always go first +*/ +int OrigAtDataPolymerUnit_CompareAtomListsMod( OrigAtDataPolymerUnit* u1, OrigAtDataPolymerUnit* u2 ) +{ +int i; + int n1 = u1->na; + int n2 = u2->na; + int n = n1; + if ( n1 < n2 ) return -1; + if ( n1 > n2 ) return 1; + /* n1 == n2 == n */ + for (i=0; ialist[i] < u2->alist[i] ) return -1; + if ( u1->alist[i] > u2->alist[i] ) return 1; + } + return 0; +} + + +/* Compare two polymer units, lexicographic order */ +int OrigAtDataPolymerUnit_CompareAtomLists( OrigAtDataPolymerUnit* u1, OrigAtDataPolymerUnit* u2 ) +{ +int i; + int n1 = u1->na; + int n2 = u2->na; + int n = inchi_min(n1, n2); + for (i=0; ialist[i] < u2->alist[i] ) return -1; + if ( u1->alist[i] > u2->alist[i] ) return 1; + } + if ( n1 < n2 ) return -1; + if ( n1 > n2 ) return 1; + return 0; +} + + +/* Sort SRU bond lists atoms and bonds themselves */ +int OrigAtDataPolymerUnit_OrderBondAtomsAndBondsThemselves( OrigAtDataPolymerUnit *u, + int n_star_atoms, + int *star_atoms ) +{ +int k; + /* Sort bond atoms */ + for (k=0; knb; k++) + { + /* Place not-in-unit bond end to first place */ + + int a1 = u->blist[ 2*k ]; + int a2 = u->blist[ 2*k+1 ]; + int a1_is_not_in_alist = 0, + a1_is_star_atom = 0, + a2_is_not_in_alist = 0, + a2_is_star_atom = 0; + + if ( !is_in_the_ilist( u->alist, a1, u->na ) ) + a1_is_not_in_alist = 1; + if ( is_in_the_ilist( star_atoms, a1, n_star_atoms ) ) + a1_is_star_atom = 1; + + if ( !is_in_the_ilist( u->alist, a2, u->na ) ) + a2_is_not_in_alist = 1; + if ( is_in_the_ilist( star_atoms, a2, n_star_atoms ) ) + a2_is_star_atom = 1; + + if ( ( a1_is_not_in_alist || a1_is_star_atom ) && + ( a2_is_not_in_alist || a2_is_star_atom ) ) + { + /* Both the ends are out of unit: the crossing bond is invalid */ + return 1; + } + /* If a2 is star atom or non-star external to the current unit, swap(a2,a1) */ + if ( a2_is_star_atom || a2_is_not_in_alist ) + { + u->blist[ 2*k ] = a2; + u->blist[ 2*k + 1 ] = a1; + } + } + + /* Sort bond themselves + for now, consider only the simplest cases of 2 bonds + */ + if ( u->nb == 2 ) /* two bonds in SBL */ + { + int b1a1 = u->blist[0]; + int b1a2 = u->blist[1]; + int b2a1 = u->blist[2]; + int b2a2 = u->blist[3]; + if ( b1a1 > b2a1 ) + { + /* swap */ + u->blist[0] = b2a1; u->blist[1] = b2a2; + u->blist[2] = b1a1; u->blist[3] = b1a2; + } + } + + /* for single or no bonds, do nothing + else + ; + */ + + return 0; +} + + +/* + Parse polymer data (unit, types, subtypes, connections, etc.) +*/ +int OrigAtDataPolymer_ParseAndValidate( ORIG_ATOM_DATA *orig_at_data, + int allowed, char *pStrErr ) +{ + int i, k, kk, type, subtype, representation, err=0; + int nsgroups = orig_at_data->polymer->n; + int nat = orig_at_data->num_inp_atoms; + OrigAtDataPolymer *pd = orig_at_data->polymer; + OrigAtDataPolymerUnit* u = NULL; + + + /* Quick first checks */ + + if ( !orig_at_data->polymer ) + goto exitf; + + if ( nsgroups < 1 ) + { + /* not a polymer */ + goto exitf; + } + + if ( nsgroups == 1 ) + { + /* Check if copolymer */ + type = pd->units[0]->type; + if ( type == POLYMER_STY_COP ) + { TREAT_ERR (err, 9001, "Copolymer contains a single unit"); goto exitf; } + /* Check if copolymer subtype */ + subtype = pd->units[0]->subtype; + if ( subtype == POLYMER_SST_RAN || subtype == POLYMER_SST_ALT || subtype == POLYMER_SST_BLK ) + { TREAT_ERR (err, 9002, "Single polymer unit may not be RAN/ALT/BLO" ); goto exitf; } + } + + for (i=0; i< nsgroups; i++) + { + u = pd->units[i]; + + if ( u->nb != 0 && u->nb!=2 ) + { TREAT_ERR (err, 9003, "Number of crossing bonds in polymer unit is not 0 or 2"); goto exitf; } + if ( u->na < 1 ) + { TREAT_ERR (err, 9004, "Empty polymer unit"); goto exitf; } + if ( u->na > nat ) + { TREAT_ERR (err, 9005, "Too large polymer unit"); goto exitf; } + for (k=0; kna; k++) + { + int atom = u->alist[k]; + if ( atom < 1 || atom > nat ) + { TREAT_ERR (err, 9006, "Invalid atom number in polymer unit"); goto exitf; } + if ( is_in_the_ilist( pd->star_atoms, atom, pd->n_star_atoms ) ) + { TREAT_ERR (err, 9007, "Star atom inside polymer unit"); goto exitf; } + } + + /* Set possibly missing unit parameters */ + u->npsbonds = 0; + u->disjoint = 0; + u->closeable = CLOSING_SRU_NOT_APPLICABLE; + u->already_closed = 0; + u->star1 = 0; + u->star2 = 0; + u->star_partner1 = 0; + u->star_partner2 = 0; + u->real_kind = POLYMER_UNIT_KIND_UNKNOWN; + } + + + /* Collect star atoms info */ + + pd->n_star_atoms = 0; + for (k=0; kat[k].elname,"Zz") ) + pd->n_star_atoms++; + if ( pd->n_star_atoms > 0 ) + { + pd->star_atoms = + (int *) inchi_calloc( pd->n_star_atoms, sizeof( int ) ); + if ( !pd->star_atoms ) + { TREAT_ERR (err, 9010, "Not enough memory"); goto exitf; } + kk = 0; + for (k=0; kat[k].elname,"Zz") ) + pd->star_atoms[ kk++ ] = k + 1; + } + + + /* Check copolymers and ensure that COP includes > 1 SRU */ + + for (i=0; in; i++) + { + u = pd->units[i]; + + if ( u->type == POLYMER_STY_COP ) + { + int j, in_units = 0; + + if ( u->nb > 0 ) + { TREAT_ERR (err, 9026, "Polymer COP unit contains bracket-crossing bonds, not supported"); goto exitf; } + + for (j=0; jn; j++) + { + if ( pd->units[j]->type == POLYMER_STY_COP ) + continue; + if ( is_ilist_inside( pd->units[j]->alist, pd->units[j]->na, pd->units[i]->alist, pd->units[i]->na ) ) + { + in_units++; + if ( in_units == 2 ) + break; + } + } + if ( in_units < 2 ) + { TREAT_ERR (err, 9027, "Polymer COP unit contains a single SRU instead of multiple"); goto exitf; } + } + } + + + representation = OrigAtDataPolymer_GetRepresentation( pd ); + + + /* More checks and some corrections*/ + + if ( representation == POLYMER_REPRESENTATION_SOURCE_BASED ) + { + for (i=0; i< nsgroups; i++) + { + /* Replace source-based 'SRU' with 'MON' */ + if ( pd->units[i]->type == POLYMER_STY_SRU ) + { + pd->units[i]->type = POLYMER_STY_MON; + WarningMessage( pStrErr, "Converted src-based polymer unit type to MON" ); + } + if ( pd->units[i]->type == POLYMER_STY_COP ) + { + /* Set missing copolymer subtype to RAN */ + if ( pd->units[i]->subtype == POLYMER_SST_NON ) + { + pd->units[i]->subtype = POLYMER_SST_RAN; + WarningMessage( pStrErr, "Set missing copolymer subtype to RAN" ); + } + } + /* Suppress connectivity (�HH�, �HT�, �EU�) */ + if ( pd->units[i]->conn != POLYMER_CONN_NON ) + { + pd->units[i]->conn = POLYMER_CONN_NON; + WarningMessage( pStrErr, "Ignore connection pattern for src-based polymer unit" ); + } + /* Recognize finally */ + if ( pd->units[i]->type == POLYMER_STY_MON ) + { + u->real_kind = POLYMER_UNIT_KIND_SOURCE_BASED_COMPONENT; + } + else if ( pd->units[i]->type == POLYMER_STY_MOD ) + { + u->real_kind = POLYMER_UNIT_KIND_SOURCE_BASED_COMPONENT; + } + else if ( pd->units[i]->type == POLYMER_STY_MER ) + { + u->real_kind = POLYMER_UNIT_KIND_SOURCE_BASED_COMPONENT; + } + else if ( pd->units[i]->type == POLYMER_STY_CRO ) + { + u->real_kind = POLYMER_UNIT_KIND_SOURCE_BASED_COMPONENT; + } + else if ( pd->units[i]->type == POLYMER_STY_COP ) + { + u->real_kind = POLYMER_UNIT_KIND_SOURCE_BASED_COPOLYMER; + if ( u->subtype == POLYMER_SST_ALT ) + u->real_kind = POLYMER_UNIT_KIND_SOURCE_BASED_ALT_COPOLYMER; + else if ( u->subtype == POLYMER_SST_BLK ) + u->real_kind = POLYMER_UNIT_KIND_SOURCE_BASED_BLK_COPOLYMER; + else if ( u->subtype == POLYMER_SST_RAN || u->subtype == POLYMER_SST_NON ) + u->real_kind = POLYMER_UNIT_KIND_SOURCE_BASED_RAN_COPOLYMER; + } + else if ( pd->units[i]->type == POLYMER_STY_NON ) + { + u->real_kind = POLYMER_UNIT_KIND_SOURCE_BASED_POLYMER; + } + else + { + TREAT_ERR (err, 9028, "Unrecognized kind of source-based represented polymer unit"); + goto exitf; + } + } + } + + else if ( representation == POLYMER_REPRESENTATION_STRUCTURE_BASED ) + { + for (i=0; i< nsgroups; i++) + { + int a1, a2, a1_is_not_in_alist, a1_is_star_atom, a2_is_not_in_alist, a2_is_star_atom; + + u = pd->units[i]; + + OrigAtDataPolymerUnit_FindStarsAndPartners( u, orig_at_data, &err, pStrErr ); + + /* SRU that is copolymer unit embedding other SRU's */ + if ( u->nb == 0 ) + { + if ( u->type == POLYMER_STY_COP ) + ; + else if ( u->type == POLYMER_STY_SRU ) + { + u->type = POLYMER_STY_COP; + WarningMessage( pStrErr, "Set copolymer embedding unit mark to COP" ); + } + } + if ( u->type == POLYMER_STY_COP ) + { + u->real_kind = POLYMER_UNIT_KIND_SRU_EMBEDDING_STRUCTURE_BASED_SRUS; + u->closeable = CLOSING_SRU_NOT_APPLICABLE; + /* Set possibly missing copolymer subtype to RAN */ + if ( u->subtype == POLYMER_SST_NON ) + { + u->subtype = POLYMER_SST_RAN; + WarningMessage( pStrErr, "Set missing copolymer subtype to RAN" ); + } + continue; + } + + /* SRU with endgroups or stars. + Check it. */ + for (k=0; knb; k++) + { + a1 = u->blist[ 2*k ]; a2 = u->blist[ 2*k+1 ]; + if ( !strcmp( orig_at_data->at[a1-1].elname, "H" ) || + !strcmp( orig_at_data->at[a1-1].elname, "D" ) || + !strcmp( orig_at_data->at[a1-1].elname, "T" ) ) + { TREAT_ERR (err, 9030, "H as polymer end group is not supported"); goto exitf; } + if ( !strcmp( orig_at_data->at[a2-1].elname, "H" ) || + !strcmp( orig_at_data->at[a2-1].elname, "D" ) || + !strcmp( orig_at_data->at[a2-1].elname, "T" ) ) + { TREAT_ERR (err, 9031, "H as polymer end group is not supported"); goto exitf; } + a1_is_not_in_alist = a1_is_star_atom = 0; + a2_is_not_in_alist = a2_is_star_atom = 0; + if ( !is_in_the_ilist( u->alist, a1, u->na ) ) + a1_is_not_in_alist = 1; + if ( is_in_the_ilist( pd->star_atoms, a1, pd->n_star_atoms ) ) + a1_is_star_atom = 1; + if ( !is_in_the_ilist( u->alist, a2, u->na ) ) + a2_is_not_in_alist = 1; + if ( is_in_the_ilist( pd->star_atoms, a2, pd->n_star_atoms ) ) + a2_is_star_atom = 1; + if ( ( a1_is_not_in_alist || a1_is_star_atom ) && + ( a2_is_not_in_alist || a2_is_star_atom ) ) + { TREAT_ERR (err, 9032, "Ends of crossing bond lie inside polymer unit"); goto exitf; } + } + + + if ( u->type==POLYMER_STY_SRU || u->type==POLYMER_STY_MOD || + u->type==POLYMER_STY_CRO || u->type==POLYMER_STY_MER ) + { + + /* If SRU connection is missing, set to default ('either') */ + if ( u->conn == POLYMER_CONN_NON ) + { + WarningMessage( pStrErr, "Set missing copolymer unit connection to EU" ); + u->conn = POLYMER_CONN_EU; + } + + if ( u->star1 && u->star2 ) + { + u->real_kind = POLYMER_UNIT_KIND_STRUCTURE_BASED_SRU_WITH_TWO_STARS; + + /* Set SRU closure type */ + if ( u->na == 1 ) + { +#ifdef ALLOW_CLOSING_SRU_VIA_DIRADICAL + u->closeable = CLOSING_SRU_DIRADICAL; +#else + u->closeable = CLOSING_SRU_NOT_APPLICABLE; +#ifdef CLOSING_STARRED_SRU_IS_A_MUST + TREAT_ERR (err, 9029, "Could not perform SRU closure"); + goto exitf; +#endif +#endif + } + else if ( u->na == 2 ) + { + +#ifdef ALLOW_CLOSING_SRU_VIA_HIGHER_ORDER_BOND + u->closeable = CLOSING_SRU_HIGHER_ORDER_BOND; +#else + u->closeable = CLOSING_SRU_NOT_APPLICABLE; +#ifdef CLOSING_STARRED_SRU_IS_A_MUST + TREAT_ERR (err, 9029, "Could not perform SRU closure"); + goto exitf; +#endif +#endif + } + else + { + u->closeable = CLOSING_SRU_RING; + } + } + + else if ( u->star1 < 1 && u->star2 < 1 ) + u->real_kind = POLYMER_UNIT_KIND_STRUCTURE_BASED_SRU_WITH_NO_STARS; + + if ( u->closeable ) + { + /* Allocate PS (phase-shiftable) bonds */ + u->maxpsbonds = orig_at_data->num_inp_bonds + 2; + err = imat_new( u->maxpsbonds, 2, &(u->psbonds) ); + if ( err ) + { TREAT_ERR ( err, 9034, "Not enough memory (polymers)" ); goto exitf; } + } + } + + if ( u->real_kind == POLYMER_UNIT_KIND_UNKNOWN ) + { TREAT_ERR (err, 9035, "Could not recognize type of polymer unit"); goto exitf; } + } + } + else + { + { TREAT_ERR (err, 9035, "Invalid kind of polymer representation"); goto exitf; } + } + + pd->valid = 1; + +exitf: + if ( err ) + pd->valid = 0; + + return err; +} + + +int UnMarkRingSystemsInp( inp_ATOM *at, int num_atoms ); +/* + +*/ +int UnMarkRingSystemsInp( inp_ATOM *at, int num_atoms ) +{ + int i; + for ( i = 0; i < num_atoms; i ++ ) { + at[i].bCutVertex = 0; + at[i].nRingSystem = 0; + at[i].nNumAtInRingSystem = 0; + at[i].nBlockSystem = 0; + } + return 0; +} + + +/* + Preprocess OrigAtDataPolymer + (NB: phase shift is invoked from here) +*/ +int OrigAtDataPolymer_CyclizeCloseableUnits( ORIG_ATOM_DATA *orig_at_data, char *pStrErr ) +{ +int i, ncyclized=0, err=0; + + for (i=0; ipolymer->n; i++) + { + OrigAtDataPolymerUnit *unit = orig_at_data->polymer->units[i]; + + if ( unit->real_kind != POLYMER_UNIT_KIND_STRUCTURE_BASED_SRU_WITH_TWO_STARS ) + continue; + if ( !unit->closeable ) + continue; + + /* Find stars and their partners */ + OrigAtDataPolymerUnit_FindStarsAndPartners( unit, orig_at_data, &err, pStrErr ); + + if ( err ) break; + if ( !unit->closeable ) continue; + + + if ( OrigAtDataPolymerUnit_HasMetal( unit, orig_at_data->at ) ) + { + /*unit->closeable = CLOSING_SRU_NOT_APPLICABLE;*/ + if ( unit->closeable == CLOSING_SRU_RING ) + { + /*unit->closeable = CLOSING_SRU_HIGHER_ORDER_BOND;*/ + WarningMessage( pStrErr, "Phase shift in metallated polymer unit may be missed"); + } + } + + /* Now remove bonds to star atoms and cyclize a SRU */ + OrigAtDataPolymerUnit_DetachStarsAndConnectStarPartners( unit, orig_at_data, &err, pStrErr ); + + if ( err ) break; + if ( !unit->closeable ) continue; + + ncyclized++; + } + + /* + if ( ncyclized ) + WarningMessage( pStrErr, "Made provision for phase shift in polymer unit(s)" ); + */ + + return err; +} + + +/* + +*/ +int OrigAtDataPolymerUnit_HasMetal( OrigAtDataPolymerUnit *u, inp_ATOM *at) +{ +int i; + for (i=0; i < u->na; i++) + if ( is_el_a_metal( at[ u->alist[i]-1].el_number ) ) + return 1; + + return 0; +} + + + +/* + OrigAtDataPolymer_Free +*/ +void OrigAtDataPolymer_Free( OrigAtDataPolymer *pd ) +{ + if ( pd ) + { + if ( pd->star_atoms ) + { + inchi_free( pd->star_atoms ); + pd->star_atoms = NULL; + pd->n_star_atoms = 0; + } + if ( pd->n && pd->units ) + { + int k; + for (k=0; kn; k++) + { + OrigAtDataPolymerUnit_Free( pd->units[k] ); + } + inchi_free( pd->units ); + pd->units = NULL; + pd->n = 0; + } + inchi_free( pd ); + pd = NULL; + } + + return; +} + + +/* + OrigAtDataPolymerUnit_DetachStarsAndConnectStarPartners +*/ +void OrigAtDataPolymerUnit_DetachStarsAndConnectStarPartners( OrigAtDataPolymerUnit *unit, + ORIG_ATOM_DATA *orig_inp_data, + int *err, char *pStrErr ) +{ +int bond_type, bond_stereo; + + *err = 0; + if ( !unit->closeable ) + return; + + if ( unit->closeable == CLOSING_SRU_RING ) + { + /* Disconnect both star atoms */ + OrigAtData_RemoveBond( unit->star1 - 1, unit->star_partner1 - 1, orig_inp_data->at, + &bond_type, &bond_stereo, &orig_inp_data->num_inp_bonds ); + + OrigAtData_RemoveBond( unit->star2 - 1, unit->star_partner2 - 1, orig_inp_data->at, + &bond_type, &bond_stereo, &orig_inp_data->num_inp_bonds ); + + OrigAtData_AddSingleStereolessBond( unit->star_partner1 - 1, unit->star_partner2 - 1, + orig_inp_data->at, &orig_inp_data->num_inp_bonds ); + } + + else if ( unit->closeable == CLOSING_SRU_HIGHER_ORDER_BOND ) + { + int elevated; + elevated = OrigAtData_IncreaseBondOrder( unit->star_partner1 - 1, unit->star_partner2 - 1, orig_inp_data->at ); +#if 0 +/* the bond may already be broken at metal disconnection, so ignore the result here */ + if ( !elevated) + { + /* *err = 1; */ + WarningMessage( pStrErr, "SRU closure via higher order bond failed"); + unit->closeable = CLOSING_SRU_NOT_APPLICABLE; + return; + } +#endif + OrigAtData_RemoveBond( unit->star1 - 1, unit->star_partner1 - 1, orig_inp_data->at, + &bond_type, &bond_stereo, &orig_inp_data->num_inp_bonds ); + OrigAtData_RemoveBond( unit->star2 - 1, unit->star_partner2 - 1, orig_inp_data->at, + &bond_type, &bond_stereo, &orig_inp_data->num_inp_bonds ); + } + + else if ( unit->closeable == CLOSING_SRU_DIRADICAL ) + { + orig_inp_data->at[unit->star_partner1 - 1].radical = RADICAL_TRIPLET; + OrigAtData_RemoveBond( unit->star1 - 1, unit->star_partner1 - 1, orig_inp_data->at, + &bond_type, &bond_stereo, &orig_inp_data->num_inp_bonds ); + OrigAtData_RemoveBond( unit->star2 - 1, unit->star_partner2 - 1, orig_inp_data->at, + &bond_type, &bond_stereo, &orig_inp_data->num_inp_bonds ); + } + + if ( !*err ) + unit->already_closed = 1; + + return; +} + + +/* + OrigAtDataPolymerUnit_FindStarsAndPartners +*/ +void OrigAtDataPolymerUnit_FindStarsAndPartners( OrigAtDataPolymerUnit *unit, + ORIG_ATOM_DATA *orig_at_data, + int *err, char *pStrErr ) +{ +int i, j, k; +int num_atoms; +int debug_polymers = 0; +#if ( DEBUG_POLYMERS == 1 ) + debug_polymers = 1; +#elif ( DEBUG_POLYMERS == 2 ) + debug_polymers = 2; +#endif + + *err = 0; + + if ( !unit->blist || unit->nb < 1 ) + /* We may get here e.g. for copolymer SRU (no crossing bonds) that embed + actual structure-based SRU's which do have crossing bonds */ + return; + + num_atoms = orig_at_data->num_inp_atoms; + + /* left bond 0-1 */ + i = unit->blist[0]; + j = unit->blist[1]; + unit->star_partner1 = i; + unit->star1 = j; + if ( strcmp( orig_at_data->at[unit->star1-1].elname, "Zz" ) ) + { + unit->star_partner1 = j; + unit->star1 = i; + } + if ( strcmp( orig_at_data->at[unit->star1-1].elname, "Zz" ) ) + { + /* unexpectedly, not a unit->star atom */ + unit->closeable = CLOSING_SRU_NOT_APPLICABLE; + unit->star1 = 0; + return; + } + if ( unit->star_partner1 <= 0 || unit->star_partner1 > num_atoms || unit->star1 <= 0 || unit->star1 > num_atoms ) + { + TREAT_ERR (*err, 9090, "Invalid polymeric CRU crossing bond"); + unit->closeable = CLOSING_SRU_NOT_APPLICABLE; + return; + } + /* right bond 2-3 */ + i = unit->blist[2]; + j = unit->blist[3]; + unit->star_partner2 = i; + unit->star2 = j; + if ( strcmp( orig_at_data->at[unit->star2-1].elname, "Zz" ) ) + { + unit->star_partner2 = j; + unit->star2 = i; + } + if ( strcmp( orig_at_data->at[unit->star2-1].elname, "Zz" ) ) + { + /* unexpectedly, not a unit->star atom */ + unit->closeable = CLOSING_SRU_NOT_APPLICABLE; + unit->star2 = 0; + return; + } + if ( unit->star_partner2 <= 0 || unit->star_partner2 > num_atoms || unit->star2 <= 0 || unit->star2 > num_atoms ) + { + TREAT_ERR (*err, 9091, "Invalid polymeric CRU crossing bond"); + unit->closeable = CLOSING_SRU_NOT_APPLICABLE; + return; + } + + if ( debug_polymers ) + ITRACE_( "Star atom-partner pairs (nums base is 1) are: %-d-%-d and %-d-%-d\n", + unit->star1, unit->star_partner1, unit->star2, unit->star_partner2); + + /* Stars are separated by one atom - that's not error but do nothing */ + if ( unit->star_partner1 == unit->star_partner2 ) + { +#ifdef ALLOW_CLOSING_SRU_VIA_DIRADICAL + unit->closeable = CLOSING_SRU_DIRADICAL; +#else + unit->closeable = CLOSING_SRU_NOT_APPLICABLE; +#endif + return; + } + + /* Stars are separated by two atoms - that's not error but do nothing */ + for (k=0; kat[unit->star_partner1-1].valence; k++) + { + if ( orig_at_data->at[unit->star_partner1-1].neighbor[k] == unit->star_partner2 - 1 ) + { +#ifdef ALLOW_CLOSING_SRU_VIA_HIGHER_ORDER_BOND + unit->closeable = CLOSING_SRU_HIGHER_ORDER_BOND; +#else + unit->closeable = CLOSING_SRU_NOT_APPLICABLE; +#endif + return; + } + } + + unit->closeable = CLOSING_SRU_RING; + + return; +} + + +/* + Replace original atom numbers in polymer data with canonical ones ( + 1 ). + Then prepare: + units2 a copy of original polymer units (p->units) with atomic numbers + changed to curr canonical ones; atoms in alists sorted; atoms in blists + and blists themselves sorted + unum numbers of units (0..p->n) as they go + when sorted by alist's in lexicographic orders +*/ +int OrigAtDataPolymer_PrepareWorkingSet( OrigAtDataPolymer *p, + int *cano_nums, + int *compnt_nums, + OrigAtDataPolymerUnit** units2, /* allocd by caller, to be filled */ + int *unum /* allocd by caller, to be filled */ + ) +{ + int i, k, err = 0, cano_num1 = -1, cano_num2 = -1; + OrigAtDataPolymerUnit *u; + + OrigAtDataPolymer_DebugTrace (p ); + + /* Replace original atom numbers in polymer data with canonical ones. + Note that here cano nums are 'cano1', started from 1 (InChI internals 'cano' are 0-based). + Also remove from the list atoms who mapped to cano number 0 + (i.e. -1 + 1offset): they are explicit H which have already been deleted. */ + + + for (k=0; k < p->n_star_atoms; k++ ) + { + cano_num1 = cano_nums[ p->star_atoms[k] ] + 1; + if ( cano_num1 == 0 ) + { + /* we shouldn't arrive here */ + err = 10; + goto exitf; + } + p->star_atoms[k] = cano_num1; + } + + for (i=0; in; i++) + { + int na_new = -1; + u = units2[i]; + + for (k=0; kna; k++) + { + cano_num1 = cano_nums[ u->alist[k] ] + 1; + if ( cano_num1 == 0 ) + continue; + u->alist[++na_new] = cano_num1; + } + u->na = na_new + 1; + for (k=0; k<2*u->nb; k++) + { + cano_num1 = cano_nums[ u->blist[k] ] + 1; + if ( cano_num1 == 0 ) + { + /* one of PU crossing bond ends leads to explicit H which disappeared already */ + err = 11; + goto exitf; + } + u->blist[k] = cano_num1; + } + + cano_num1 = cano_nums[ u->star1 ] + 1; + if ( cano_num1 == 0 ) + { err = 11; goto exitf; } + u->star1 = cano_num1; + + cano_num1 = cano_nums[ u->star2 ] + 1; + if ( cano_num1 == 0 ) + { err = 11; goto exitf; } + u->star2 = cano_num1; + + cano_num1 = cano_nums[ u->star_partner1 ] + 1; + if ( cano_num1 == 0 ) + { err = 11; goto exitf; } + u->star_partner1 = cano_num1; + + cano_num1 = cano_nums[ u->star_partner2 ] + 1; + if ( cano_num1 == 0 ) + { err = 11; goto exitf; } + u->star_partner2 = cano_num1; + + for (k=0; knpsbonds; k++) + { + cano_num1 = cano_nums[ u->psbonds[k][0] ] + 1; + if ( cano_num1 == 0 ) + continue; + cano_num2 = cano_nums[ u->psbonds[k][1] ] + 1; + if ( cano_num2 == 0 ) + continue; + u->psbonds[k][0] = inchi_min(cano_num1, cano_num2); + u->psbonds[k][1] = inchi_max(cano_num1, cano_num2); + } + } + + /* Sort atoms and bonds in all units */ + for (i=0; in; i++) + { + int icompnt; + + u = units2[i]; + + /* sort atoms (alist) */ + iisort( u->alist, u->na ); + + ITRACE_("\n*** Polymer unit %-d : ( ", i); + for (k=0; kna-1; k++) + ITRACE_("%-d-", u->alist[k] ); + ITRACE_("%-d )\n", u->alist[u->na-1] ); + + /* sort bonds (blist) */ + err = OrigAtDataPolymerUnit_OrderBondAtomsAndBondsThemselves( u, p->n_star_atoms, p->star_atoms ); + if ( err ) + { + /* crossing bonds in blist are invalid */ + err = 12; + goto exitf; + } + + /* check each unit for >1 connected components */ + icompnt = compnt_nums[ u->alist[0]-1]; + for (k=1; kna; k++) + { + if ( compnt_nums[ u->alist[k]-1 ] != icompnt ) + { + u->disjoint = 1; + break; + } + } + } + + /* Sort all units in modified alist's lexicographic order (modificn. is: longer list always go first ) */ + for (i=0; in; i++) + unum[i] = i; + for (i=1; in; i++) + { + int tmp = unum[i]; + int j = i - 1; + while ( j >= 0 && OrigAtDataPolymerUnit_CompareAtomListsMod( units2[ unum[j] ], units2[ tmp ] ) > 0 ) + /*while ( j >= 0 && OrigAtDataPolymerUnit_CompareAtomLists( units2[ unum[j] ], units2[ tmp ] ) > 0 )*/ + { + unum[j+1] = unum[j]; + j--; + } + unum[j+1] = tmp; + } + + +exitf: + return err; +} + + +/* + Helper for cyclizing CRU + NB: end1, end1 are 0-based +*/ +int OrigAtData_RemoveHalfBond( int this_atom, int other_atom, inp_ATOM *at, int *bond_type, int *bond_stereo ) +{ +int k, kk; +inp_ATOM *a; + a = &( at[this_atom] ); + for (k=0; kvalence; k++) + { + if ( a->neighbor[k] != other_atom ) + continue; + + *bond_type = a->bond_type[k]; + *bond_stereo = a->bond_stereo[k]; + + a->neighbor[k] = a->bond_type[k] = a->bond_stereo[k] = 0; + + for (kk=k+1; kk < a->valence; kk++ ) + { + a->neighbor[kk-1] = a->neighbor[kk]; + a->bond_type[kk-1] = a->bond_type[kk]; + a->bond_stereo[kk-1] = a->bond_stereo[kk]; + } + for (kk=a->valence-1; kk < MAXVAL; kk++ ) + { + a->neighbor[kk] = 0; + a->bond_type[kk] = (U_CHAR) 0; + a->bond_stereo[kk] = (S_CHAR) 0; + } + return 1; + } /* k */ + + return 0; +} + + +int OrigAtData_DestroyBond( int this_atom, int other_atom,inp_ATOM *at, int *num_inp_bonds) +{ +int del = 0, bond_type, bond_stereo; + del = OrigAtData_RemoveHalfBond( this_atom, other_atom, at, &bond_type, &bond_stereo ); + del+= OrigAtData_RemoveHalfBond( other_atom, this_atom, at, &bond_type, &bond_stereo ); + if ( del == 2 ) + { + (*num_inp_bonds)--; + at[this_atom].valence--; + at[this_atom].chem_bonds_valence-= bond_type; + at[other_atom].valence--; + at[other_atom].chem_bonds_valence-=bond_type; + return 1; + } + + return 0; +} + + +int OrigAtData_RemoveBond( int this_atom, int other_atom, inp_ATOM *at, + int *bond_type, int *bond_stereo, int *num_inp_bonds) +{ +int del = 0; + del = OrigAtData_RemoveHalfBond( this_atom, other_atom, at, bond_type, bond_stereo ); + del+= OrigAtData_RemoveHalfBond( other_atom, this_atom, at, bond_type, bond_stereo ); + if ( del == 2 ) + { + (*num_inp_bonds)--; + at[this_atom].valence--; + at[this_atom].chem_bonds_valence-= *bond_type; + at[other_atom].valence--; + at[other_atom].chem_bonds_valence-= *bond_type; + return 1; + } + + return 0; +} + + + +int OrigAtData_AddBond( int this_atom, int other_atom, inp_ATOM *at, + int bond_type, int bond_stereo, int *num_bonds ) +{ +int i, k, already_here; +inp_ATOM *a; + + if ( at[this_atom].valence >= MAXVAL || + at[other_atom].valence >= MAXVAL ) + return 0; + + if ( bond_type!=INCHI_BOND_TYPE_DOUBLE && bond_type!=INCHI_BOND_TYPE_TRIPLE ) + bond_type = INCHI_BOND_TYPE_SINGLE; + + a = &(at[this_atom] ); + k = a->valence; + already_here = 0; + for (i=0; ineighbor[i] == other_atom ) + { already_here = 1; break; } + if ( !already_here ) + { + a->neighbor[k] = other_atom; + a->bond_type[k] = (U_CHAR) bond_type; + a->bond_stereo[k] = (S_CHAR) bond_stereo; + a->chem_bonds_valence+= bond_type; + a->valence++; + } + + a = &(at[other_atom] ); + k = a->valence; + already_here = 0; + for (i=0; ineighbor[i] == this_atom ) + { already_here = 1; break; } + if ( !already_here ) + { + a->neighbor[k] = this_atom; + a->bond_type[k] = (U_CHAR) bond_type; + a->bond_stereo[k] = (S_CHAR) bond_stereo; + a->chem_bonds_valence+= bond_type; + a->valence++; + } + + (*num_bonds)++; + + return 1; +} + +int OrigAtData_AddSingleStereolessBond( int this_atom, + int other_atom, + inp_ATOM *at, + int *num_bonds) +{ + return OrigAtData_AddBond( this_atom, other_atom, at, INCHI_BOND_TYPE_SINGLE, 0, num_bonds ); +} + + +/* + +*/ +int OrigAtData_IncreaseBondOrder( int this_atom, int other_atom, inp_ATOM *at ) +{ +int i, k, n_up=0; +inp_ATOM *a; + + if ( at[this_atom].valence >= MAXVAL || + at[other_atom].valence >= MAXVAL ) + return 0; + + a = &(at[this_atom] ); + if ( a->chem_bonds_valence > MAXVAL - 1) + return 0; + k = a->valence; + for (i=0; ineighbor[i] != other_atom ) + continue; + if ( a->bond_type[i] > 3 ) + return 0; + a->bond_type[i]++; + a->chem_bonds_valence++; + n_up++; + break; + } + + a = &(at[other_atom] ); + if ( a->chem_bonds_valence > MAXVAL - 1) + return 0; + k = a->valence; + for (i=0; ineighbor[i] != this_atom ) + continue; + if ( a->bond_type[i] > 3 ) + return 0; + a->bond_type[i]++; + a->chem_bonds_valence++; + n_up++; + break; + } + + return n_up; +} + + +/* + +*/ +int OrigAtData_DecreaseBondOrder( int this_atom, int other_atom, inp_ATOM *at ) +{ +int i, k, n_dn=0; +inp_ATOM *a; + + + a = &(at[this_atom] ); + if ( a->chem_bonds_valence > MAXVAL - 1) + return 0; + k = a->valence; + for (i=0; ineighbor[i] != other_atom ) + continue; + if ( a->bond_type[i] < 2 ) + return 0; + a->bond_type[i]--; + a->chem_bonds_valence--; + n_dn++; + break; + } + + a = &(at[other_atom] ); + k = a->valence; + for (i=0; ineighbor[i] != this_atom ) + continue; + if ( a->bond_type[i] < 2 ) + return 0; + a->bond_type[i]--; + a->chem_bonds_valence--; + n_dn++; + break; + } + + return n_dn; +} + + + +/* + OrigAtDataPolymer Collect Phase Shiftable Bonds +*/ +void OrigAtDataPolymer_CollectPhaseShiftBonds( ORIG_ATOM_DATA *at_data, + COMP_ATOM_DATA *composite_norm_data, + int *err, char *pStrErr ) +{ +int i; + + ITRACE_("\n\n*** Now revealing paths between original star partners. "); + + *err = 0; + + for (i=0; ipolymer->n; i++) + { + + if ( !at_data->polymer->units[i]->closeable ) + continue; + + ITRACE_("\n\tUnit %-d, paths between orig nums %-d and %-d. ", i+1, + at_data->polymer->units[i]->star_partner1, + at_data->polymer->units[i]->star_partner2); + OrigAtDataPolymerUnit_PreselectPSBonds( at_data->polymer->units[i], + at_data, err, pStrErr ); + if ( *err ) + continue; + + if ( at_data->polymer->units[i]->npsbonds < 1) + continue; + + if ( at_data->polymer->units[i]->npsbonds == 1) + /* + Special case: we got only one bond between star partner 1 and star partner 2 + (result of metal disconnection) + */ + continue; + + ITRACE_("\n\n*** Now detecting and removing intra-ring edges. " ); + OrigAtDataPolymerUnit_DelistIntraRingPSBonds( at_data->polymer->units[i], + at_data, err, pStrErr ); + if ( *err ) continue; + + ITRACE_("\n\n*** Now detecting and removing high-order bonds. " ); + OrigAtDataPolymerUnit_DelistMultiplePSBonds( at_data->polymer->units[i], at_data, + composite_norm_data, err, pStrErr ); + if ( *err ) continue; + + if ( at_data->polymer->units[i]->npsbonds == 0 ) + { + /* We already cyclized phase-shiftable unit and preprocessed it (in 'prep_inp_data'). + Despite that, now we discovered that there are no bonds eligible for phase shift + (as either ring systems or alternate bonds cover all possibly useful in-unit bonds). + We can not simply restore original connections as the structure may have been already heavily touched. + The most viable action is to hold a single phase-shift bond (between original partners of star atoms). + It is for sure will be converted to original bonds to star atoms on possible inchi2struct. + */ + at_data->polymer->units[i]->closeable = 1; + at_data->polymer->units[i]->npsbonds = 1; + at_data->polymer->units[i]->psbonds[0][0] = at_data->polymer->units[i]->star_partner1; + at_data->polymer->units[i]->psbonds[0][1] = at_data->polymer->units[i]->star_partner2; + } + } + /*OrigAtDataPolymer_DebugTrace (at_data->polymer );*/ + + return; +} + + +/* + Find bonds eligible for phase shift +*/ +void OrigAtDataPolymerUnit_PreselectPSBonds( OrigAtDataPolymerUnit *unit, + ORIG_ATOM_DATA *at_data, + int *err, char *pStrErr ) +{ +int start=0, end=0; +subgraf *sg=NULL; +subgraf_pathfinder *spf=NULL; + + unit->npsbonds = 0; + + sg = subgraf_new( at_data, unit->na, unit->alist, &unit->npsbonds, unit->psbonds ); + if ( !sg ) + { + TREAT_ERR (*err, 9037, "Not enough memory (polymers)"); + unit->closeable = CLOSING_SRU_NOT_APPLICABLE; + return; + } + + start = sg->orig2node[ unit->star_partner1 ]; end = sg->orig2node[ unit->star_partner2 ]; + if ( start > end ) { int tmp = end; end = start; start = tmp; } + spf = subgraf_pathfinder_new( sg, at_data, start, end ); + if ( !spf ) + { + TREAT_ERR (*err, 9039, "Not enough memory (polymers)"); + unit->closeable = CLOSING_SRU_NOT_APPLICABLE; + return; + } + + spf->seen[0] = spf->start; spf->nseen = 1; unit->npsbonds = 0; + subgraf_pathfinder_run( spf, &(unit->npsbonds), unit->psbonds ); + + subgraf_free( sg ); + subgraf_pathfinder_free( spf ); + *err = 0; + + return; +} + + +/* + Detect and throw away intra-ring bonds +*/ +void OrigAtDataPolymerUnit_DelistIntraRingPSBonds( OrigAtDataPolymerUnit *unit, + ORIG_ATOM_DATA *at_data, + int *err, char *pStrErr ) +{ + /* Establish ring systems assignments for atoms */ + int nrings = 0; + int *num_ring_sys = NULL; + + if ( !unit ) + return; + if ( unit->npsbonds < 1 ) + return; + + *err = 1; + num_ring_sys = (int *) calloc( at_data->num_inp_atoms + 1, sizeof(int) ); + if ( !num_ring_sys ) + goto exitf; + *err = 0; + + nrings = OrigAtData_FindRingSystems( at_data->polymer, at_data->at, + at_data->num_inp_atoms, &at_data->num_inp_bonds, + num_ring_sys, NULL, + unit->star_partner1 - 1 /* NB: start dfs within connected compt! */ + ); + + if ( nrings == 0 ) + goto exitf; + else + { + int at1, at2, j=0; +repeatj: + at1 = unit->psbonds[j][0]; + at2 = unit->psbonds[j][1]; + /* ITRACE_("\n\tat1=%-d at2=%-d num_ring_sys[at1]=%-d num_ring_sys[at2]=%-d ", at1, at2, num_ring_sys[at1], num_ring_sys[at2] ); */ + if ( ( num_ring_sys[at1] == num_ring_sys[at2] ) && + ( num_ring_sys[at1] != -1 ) ) + { + ITRACE_("\n\tThrowing away intra-ring bond (%-d, %-d) ", at1, at2 ); + throw_away_inappropriate_bond( at1, at2, &unit->npsbonds, unit->psbonds ); + } + else + ++j; + if ( j < unit->npsbonds ) + goto repeatj; + } + + +exitf: + if ( num_ring_sys ) + inchi_free( num_ring_sys ); + + return; +} + + +/* + Find ring systems accounting for possible cuclizing bonds in polymer SRU's +*/ +int OrigAtData_FindRingSystems( OrigAtDataPolymer *pd, inp_ATOM *at, int nat, int *num_inp_bonds, + int *num_ring_sys, int *size_ring_sys, int start ) +{ +int i, j, nrings = 0, bond_type, bond_stereo; + + if ( NULL==num_ring_sys ) + return 0; + + /* remove polymer SRU 'cyclizing' bonds */ + for (j=0; jn; j++) + if ( pd->units[j]->already_closed ) + OrigAtData_RemoveBond( pd->units[j]->star_partner1 - 1, + pd->units[j]->star_partner2 - 1, + at, + &bond_type, &bond_stereo, num_inp_bonds ); + + + MarkRingSystemsInp( at, nat, start); /*0 );*/ + + for (i=0; i<=nat; i++) + num_ring_sys[i] = -1; + for (i=0; i 2;*/ + if ( at[i].nNumAtInRingSystem > 2 ) + { + int atnum = at[i].orig_at_number; + num_ring_sys[ atnum ] = at[i].nRingSystem; + if ( NULL!= size_ring_sys ) + size_ring_sys[atnum] = at[i].nNumAtInRingSystem; + } + } + + UnMarkRingSystemsInp( at, nat ); + + ITRACE_("\nRing system numbers for in-ring atoms (atom@ringsystem; original atom numbering) : "); + for (i=0; i -1 ) + { + ITRACE_("%-d@%-d ", at[i].orig_at_number, num_ring_sys[i] ); + nrings++; + } + } + if ( !nrings ) + ITRACE_("None"); + ITRACE_("\n"); + + /* restore polymer SRU 'cyclizing' bonds */ + for (j=0; jn; j++) + if ( pd->units[j]->already_closed ) + OrigAtData_AddSingleStereolessBond( pd->units[j]->star_partner1 - 1, + pd->units[j]->star_partner2 - 1, + at, num_inp_bonds ); + + return nrings; +} + + +/* + OrigAtData FillAtProps (for polymer SRU analysis) +*/ +void OrigAtData_FillAtProps( OrigAtDataPolymer *pd, inp_ATOM *at, + int nat, int *num_inp_bonds, + OrigAtDataPolymerAtomProps *aprops ) +{ + +/* + Max rank for in-ring atom is 216 which is achieved for N (element number 7 in Periodic system & erank_rule2[] ), + then goes O with rank 215 (element number 8), and so on... lowest rank is 1 for H . + + This follows to IUPAC rule 2 [Pure Appl. Chem., Vol. 74, No. 10, 2002, p. 1926] which states: + a. a ring or ring system containing nitrogen; + b. a ring or ring system containing the heteroatom occurring earliest in the order given in Rule 4; + ( which is O > S > Se > Te > N > P > As > Sb > Bi > Si > Ge > Sn > Pb > B > Hg ) + ... + +*/ +int erank_rule2[] = { 0,1,198,197,196,202,2,216,215,191,190,189,188,187,206,210,214,183,182,181,180,179,178,177,176, + 175,174,173,172,171,170,169,205,209,213,165,164,163,162,161,160,159,158,157,156,155,154,153,152, + 151,204,208,212,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128, + 127,126,125,124,123,122,121,201,119,203,207,116,115,114,113,112,111,110,109,108,107,106,105,104, + 103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81}; + + + +/* + Max rank for chain atom is 215 which is achieved for O (element number 8 in Periodic system & erank_rule4[] ), + then goes N with rank 212 (element number 8), and so on... lowest rank is 1 for H . + + This follows to IUPAC rule 4 [Pure Appl. Chem., Vol. 74, No. 10, 2002, p. 1927] which states: + O > S > Se > Te > N > P > As > Sb > Bi > Si > Ge > Sn > Pb > B > Hg + Note: Other heteroatoms may be placed within this order as indicated by their positions in the + periodic table [5]. +*/ +int erank_rule4[] = { 0,1,198,197,196,202,2,211,215,191,190,189,188,187,206,210,214,183,182,181,180,179,178,177,176, + 175,174,173,172,171,170,169,205,209,213,165,164,163,162,161,160,159,158,157,156,155,154,153,152, + 151,204,208,212,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128, + 127,126,125,124,123,122,121,201,119,203,207,116,115,114,113,112,111,110,109,108,107,106,105,104, + 103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81}; + + +int i, j, k, nrings = 0; +int a1, a2, dummy = 0, bond_type, bond_stereo; +int *num_ring_sys = NULL, + *size_ring_sys = NULL; + + if ( NULL == aprops ) + return; + + /* Establish element ranks for atoms */ + for (k=0; kn; j++) + { + if ( pd->units[j]->na > 2 && + pd->units[j]->npsbonds > 0 && + pd->units[j]->already_closed ==0 && + pd->units[j]->closeable == CLOSING_SRU_RING ) + { + a1 = pd->units[j]->psbonds[0][0] - 1; + a2 = pd->units[j]->psbonds[0][1] - 1; + OrigAtData_RemoveBond( a1, a2, at, &bond_type, &bond_stereo, &dummy ); + } + } + + + nrings = OrigAtData_FindRingSystems( pd, at, nat, num_inp_bonds, + num_ring_sys, size_ring_sys, 0 ); + + + /* Immediately restore just broken bond(s) */ + for (j=0; jn; j++) + { + if ( pd->units[j]->na > 2 && + pd->units[j]->npsbonds > 0 && + pd->units[j]->already_closed ==0 && + pd->units[j]->closeable == CLOSING_SRU_RING ) + { + a1 = pd->units[j]->psbonds[0][0] - 1; + a2 = pd->units[j]->psbonds[0][1] - 1; + /*OrigAtData_AddSingleStereolessBond( a1, a2, at, &dummy ); */ + OrigAtData_AddBond( a1, a2, at, bond_type, bond_stereo, &dummy ); + } + } + + + if ( nrings ) + { + int max_ring_num = 0; + /* SRU contains ring[s], proceed with them loosely following the IUPAC guidelines */ + for (k=0; k= 0 ) + { + aprops[k].ring_num = num_ring_sys[atnum]; /* temporarily */ + if ( max_ring_num < aprops[k].ring_num ) + max_ring_num = aprops[k].ring_num; /* NB: OrigAtData_FindRingSystems may return num_ring_sys[] + which is not a list of consecutive numbers */ + aprops[k].ring_size = size_ring_sys[atnum]; /* Size of ring system which includes the atom k . + It is used as an additional score for in-ring + atoms' prioritizing (instead of criteria in + 2c-2h of IUPAC rule 2 which deal with ring sizes). + */ + } + } + + /*for (k=0; k max_erank ) + max_erank = erank; + } + } + for (k=0; k 2 ) + aprops[k].ring_erank = max_erank; + } + } + +exitf: + if ( num_ring_sys ) + free( num_ring_sys ); + if ( size_ring_sys ) + free( size_ring_sys ); + + return; +} + + + +/* + Detecting and removing high-order bonds +*/ +void OrigAtDataPolymerUnit_DelistMultiplePSBonds( OrigAtDataPolymerUnit *unit, + ORIG_ATOM_DATA *orig_at_data, + COMP_ATOM_DATA *composite_norm_data, + int *err, char *pStrErr ) +{ +int at1, at2, border, j=0, k, check_taut = 0, remove; + + int *orig_num = NULL, *curr_num = NULL; + + if ( unit->na < 2 ) + return; + if ( unit->nb < 2 ) + return; + if ( unit->npsbonds < 1 ) + return; + + /* Take care on the tautomeric bonds */ + if ( composite_norm_data ) + { + check_taut = 1; + orig_num = (int *) inchi_calloc( orig_at_data->num_inp_atoms + 2, sizeof(int) ); + curr_num = (int *) inchi_calloc( orig_at_data->num_inp_atoms + 2, sizeof(int) ); + if ( orig_num && curr_num ) + { + check_taut = 1; + CompAtomData_GetNumMapping( composite_norm_data, orig_num, curr_num ); + } + } + + +repeatj: + remove = 0; + at1 = unit->psbonds[j][0]; + at2 = unit->psbonds[j][1]; + border = 0; + for (k=0; kat[at1-1].valence; k++ ) + { + if ( orig_at_data->at[at1-1].neighbor[k] != at2 - 1 ) + continue; + border = orig_at_data->at[at1-1].bond_type[k]; + } + /*if ( border > 1 ) */ + { + int bond_is_untouchable = 0, btype; + if ( check_taut && composite_norm_data && composite_norm_data->at ) + { + for (k=0; kat[ curr_num[at1] ].valence; k++ ) + { + if ( composite_norm_data->at[ curr_num[at1] ].neighbor[k] != curr_num[at2] ) + continue; + btype = composite_norm_data->at[ curr_num[at1] ].bond_type[k]; + bond_is_untouchable = ( btype == BOND_TAUTOM ); /*|| btype == BOND_ALTERN );*/ + break; + } + } + if ( bond_is_untouchable ) + remove = 1; + } + if ( remove ) + { + ITRACE_("\n\tThrowing away bond (%-d, %-d) of order %-d ", at1, at2, border ); + throw_away_inappropriate_bond( at1, at2, &unit->npsbonds, unit->psbonds ); + } + else + ++j; + if ( j < unit->npsbonds ) + goto repeatj; + + if ( orig_num ) + inchi_free( orig_num ); + if ( curr_num ) + inchi_free( curr_num ); + + return; +} + + + +/* Remove bond (at1, at2) */ +void throw_away_inappropriate_bond( int at1, int at2, int *nbonds, int **bonds) +{ +int p, q; + if ( at1 > at2 ) + { + int tmp = at1; + at1 = at2; + at2 = tmp; + } + for (p=0; p<*nbonds; p++) + { + if ( bonds[p][0]==at1 && bonds[p][1]==at2 ) + { + for (q=p+1; q<*nbonds; q++) + { + bonds[q-1][0] = bonds[q][0]; + bonds[q-1][1] = bonds[q][1]; + } + (*nbonds)--; + break; + } + } + return; +} + + +void OrigAtDataPolymerUnit_DebugTrace( OrigAtDataPolymerUnit *u ) +{ +int i, k; + + int na, nb; + char *conn = "ABSENT", *typ="ABSENT", *styp="ABSENT"; + + if ( !u ) + return; + if ( u->conn==1 ) conn = "HT"; + else if ( u->conn==2 ) conn = "HH"; + else if ( u->conn==3 ) conn = "EU"; + + if ( u->type==0 ) typ = "NONE"; + else if ( u->type==1 ) typ = "SRU"; + else if ( u->type==2 ) typ = "MON"; + else if ( u->type==3 ) typ = "COP"; + else if ( u->type==4 ) typ = "MOD"; + else if ( u->type==5 ) typ = "MER"; + + if ( u->subtype==1 ) styp = "ALT"; + else if ( u->subtype==2 ) styp = "RAN"; + else if ( u->subtype==3 ) styp = "BLK"; + + ITRACE_( "\n\tid=%-d label=%-d type=%-s subtype=%-s conn=%-s subscr='%-s'\n", + u->id, u->label, typ, styp, conn, u->smt ); + ITRACE_( "\tBracket1 coords: %-f, %-f, %-f, %-f\n", u->xbr1[0], u->xbr1[1], u->xbr1[2], u->xbr1[3] ); + ITRACE_( "\tBracket2 coords: %-f, %-f, %-f, %-f\n", u->xbr2[0], u->xbr2[1], u->xbr2[2], u->xbr2[3] ); + na = u->na; + ITRACE_( "\t%-d atoms { ", na); + for (k=0;kalist[k]); + ITRACE_( " %-d }\n", u->alist[na-1]); + nb = u->nb; + ITRACE_( "\t%-d bonds { ", nb); + for (k=0; kblist[2*k], u->blist[2*k+1] ); + ITRACE_( "}\n" ); + + ITRACE_("\tPhase-shift bonds candidates (may include cyclizing one) : %-d ", u->npsbonds ); + if ( u->npsbonds ) + { + for(i=0; inpsbonds; i++) + { + ITRACE_("(%-d, %-d) ", u->psbonds[i][0], u->psbonds[i][1] ); + } + } + return; +} + + +void OrigAtDataPolymer_DebugTrace( OrigAtDataPolymer *p ) +{ +int i; + ITRACE_( "\n\n* POLYMER INFO @ %-p (%-d group(s))", p , p->n); + ITRACE_( "\n\n* %-d star atoms: ", p->n_star_atoms ); + for (i=0; in_star_atoms; i++) + ITRACE_( " %-d", p->star_atoms[i] ); + + for (i=0; in; i++) + { + ITRACE_( "\n* Polymer unit %-d", i ); + OrigAtDataPolymerUnit_DebugTrace( p->units[i] ); + } + ITRACE_( "\n* Really-do-PS = %-d", p->really_do_phase_shift ); + ITRACE_( "\n* End POLYMER INFO\n" ); + return; +} + + +int OrigAtDataPolymer_GetRepresentation( OrigAtDataPolymer *p ) +{ +int i, nsrc=0, nstruct=0; + + if ( !p ) + return NO_POLYMER; + + for (i=0; in; i++) + { + if ( p->units[i]->nb==2 || p->units[i]->npsbonds>0 || ((p->units[i]->star1 > 0)&&(p->units[i]->star2 > 0)) ) + { + p->units[i]->representation = POLYMER_REPRESENTATION_STRUCTURE_BASED; + nstruct++; + } + else if ( p->units[i]->nb == 0 ) + { + p->units[i]->representation = POLYMER_REPRESENTATION_SOURCE_BASED; + nsrc++; + } + } + if ( p->n == nsrc ) + return POLYMER_REPRESENTATION_SOURCE_BASED; + else if ( p->n == nstruct ) + return POLYMER_REPRESENTATION_STRUCTURE_BASED; + else if ( p->n == ( nsrc + nstruct ) ) + { + /* + Structure based presentation may include no-crossing bond units + which only serve as embedding for (>1) structure-based SRU's + Account for this in code below. + */ + if ( nsrc < nstruct ) + { + int j, atom, atom_is_shared_with_struct_based_unit=0; + for (i=0; in; i++) + { + int k; + if ( p->units[i]->representation != POLYMER_REPRESENTATION_SOURCE_BASED ) + continue; + for (k=0; kunits[i]->na; k++ ) + { + atom = p->units[i]->alist[k]; + if ( is_in_the_ilist( p->star_atoms, atom, p->n_star_atoms ) ) + continue; + atom_is_shared_with_struct_based_unit = 0; + for (j=0; jn; j++) + { + if ( p->units[j]->representation != POLYMER_REPRESENTATION_STRUCTURE_BASED ) + continue; + if ( is_in_the_ilist( p->units[j]->alist, atom, p->units[j]->na ) ) + { + atom_is_shared_with_struct_based_unit = 1; + break; + } + } + if ( !atom_is_shared_with_struct_based_unit ) + break; + } + if ( !atom_is_shared_with_struct_based_unit ) + break; + } + if ( atom_is_shared_with_struct_based_unit ) + return POLYMER_REPRESENTATION_STRUCTURE_BASED; + } + return POLYMER_REPRESENTATION_MIXED; + } + + return POLYMER_REPRESENTATION_UNRECOGNIZED; +} diff --git a/INCHI-1-SRC/INCHI_BASE/src/runichi4.c b/INCHI-1-SRC/INCHI_BASE/src/runichi4.c new file mode 100644 index 0000000..fcc6ecb --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/runichi4.c @@ -0,0 +1,1510 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +/* + SortAndPrintINChI and misc. processing (display, tests, error treatment, etc.) + +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "mode.h" + +#ifndef COMPILE_ANSI_ONLY +#include +#endif + +#include "ichimain.h" +#include "ichi_io.h" +#include "mol_fmt.h" +#include "ichicant.h" +#include "inchi_api.h" +#include "readinch.h" +#ifdef TARGET_LIB_FOR_WINCHI +#include "../../../IChI_lib/src/ichi_lib.h" +#include "inchi_api.h" +#else +#include "inchi_gui.h" +#endif +#include "readinch.h" + + +#ifdef TARGET_LIB_FOR_WINCHI + +void (*FWPRINT) + (const char * format, va_list argptr ) = NULL; +void (*DRAWDATA) + ( struct DrawData * pDrawData) = NULL; +int (*DRAWDATA_EXISTS) + ( int nComponent, int nType, int bReconnected ) = NULL; +struct DrawData * (*GET_DRAWDATA) + ( int nComponent, int nType, int bReconnected ) = NULL; +#endif + +int get_processing_warnings_one_InChI( INChI *pINChI, INP_ATOM_DATA *inp_norm_data, char *pStrErrStruct); + +/* + Main serialization (i.e. InChI output) procedure +*/ +int SortAndPrintINChI( CANON_GLOBALS *pCG, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM_STRING *strbuf, + INCHI_IOSTREAM *log_file, + INPUT_PARMS *ip, + ORIG_ATOM_DATA *orig_inp_data, + ORIG_ATOM_DATA *prep_inp_data, + COMP_ATOM_DATA composite_norm_data[INCHI_NUM][TAUT_NUM+1], + ORIG_STRUCT *pOrigStruct, + int num_components[INCHI_NUM], + int num_non_taut[INCHI_NUM], + int num_taut[INCHI_NUM], + INCHI_MODE bTautFlags[INCHI_NUM], + INCHI_MODE bTautFlagsDone[INCHI_NUM], + NORM_CANON_FLAGS *pncFlags, + long num_inp, + PINChI2 *pINChI[INCHI_NUM], + PINChI_Aux2 *pINChI_Aux[INCHI_NUM], + int *pSortPrintINChIFlags, + unsigned char save_opt_bits) +{ +INCHI_SORT *pINChISort[INCHI_NUM][TAUT_NUM]; +int j, i, k, k1, ret, ret2, iINChI, max_num_components; +int INCHI_basic_or_INCHI_reconnected; +INCHI_MODE nMode; +int bDisconnectedCoord = (0 != (bTautFlagsDone[0] & TG_FLAG_DISCONNECT_COORD_DONE)); +int bINChIOutputOptions0, bCurOption, bINChIOutputOptionsCur, bEmbedReconnected; +static const char szAnnHdr[] = "InChI ANNOTATED CONTENTS"; +#ifdef TARGET_LIB_FOR_WINCHI +int ikflag=0; +#endif + + /* + Note: + + pINChI[INCHI_BAS] refers to either disconnected or original structure; + num_components[INCHI_BAS] > 0 if there was input structure + pINChI[INCHI_REC] refers to the reconnected structure, + and only if the input structure has been disconnected, that is, + num_components[INCHI_REC] > 0 + + */ + + + ret = 1; + + for ( i = 0; i < INCHI_NUM; i ++ ) + { + for ( k = 0; k < TAUT_NUM; k ++ ) + { + bTautFlags[i] |= pncFlags->bTautFlags[i][k]; + bTautFlagsDone[i] |= pncFlags->bTautFlagsDone[i][k]; + } + } + + nMode = ip->nMode; + if ( !(nMode & (REQ_MODE_BASIC|REQ_MODE_TAUT)) ) + { + nMode |= (REQ_MODE_BASIC|REQ_MODE_TAUT); + } + + + max_num_components = 0; + for ( j = 0; j < INCHI_NUM; j ++ ) + { + if ( max_num_components < num_components[j] ) + max_num_components = num_components[j]; + } + if ( max_num_components <= 0 ) + max_num_components = 1; + + + for ( j = 0, i = 0; j < INCHI_NUM; j ++ ) + { + if ( num_components[j] ) + { + for ( k1 = 0; k1 < TAUT_NUM; k1 ++ ) + { + pINChISort[j][k1] = + (INCHI_SORT *)inchi_calloc(max_num_components, + sizeof(pINChISort[0][0][0]) ); + i += !pINChISort[j][k1]; /* number of failed allocatons */ + } + } + else + { + for ( k1 = 0; k1 < TAUT_NUM; k1 ++ ) + { + pINChISort[j][k1] = NULL; /* keep BC happy */ + } + } + } + + if ( i ) + { + ret = CT_OUT_OF_RAM; + goto exit_function; + } + + for ( j = 0; j < INCHI_NUM; j ++ ) + { + if ( !num_components[j] ) + continue; + iINChI = j; + +#if ( OUTPUT_CONNECTED_METAL_ONLY == 1 ) /* test: output connected as the only one INChI */ + if ( INCHI_BAS == j && num_components[INCHI_REC] ) { + j = INCHI_REC; + } +#endif + + /* j = INCHI_BAS; <- for debug only */ + /* for only normal or disconnected coord compounds */ + /* (j=0=INCHI_BAS => normal or disconnected, j=1=INCHI_REC => reconnected */ + + for ( k1 = 0; k1 < TAUT_NUM; k1 ++ ) + { + for ( i = 0; i < num_components[j]; i ++ ) + { + for ( k = 0; k < TAUT_NUM; k ++ ) + { + pINChISort[j][k1][i].pINChI[k] = pINChI[j][i][k]; + pINChISort[j][k1][i].pINChI_Aux[k] = pINChI_Aux[j][i][k]; + } + pINChISort[j][k1][i].ord_number = i; + } + } + + /* Sort component INChIs */ + for ( k1 = 0; k1 < TAUT_NUM; k1 ++ ) + { + switch ( k1 ) + { + case TAUT_NON: + qsort( pINChISort[j][k1], + num_components[j], + sizeof(pINChISort[0][0][0]), + CompINChINonTaut2 ); + break; + case TAUT_YES: + qsort( pINChISort[j][k1], + num_components[j], + sizeof(pINChISort[0][0][0]), + CompINChITaut2 ); + break; + } + } + +#ifndef COMPILE_ANSI_ONLY + /* Find equivalent and wINChI display order; + use requested in ip->bCompareComponents comparison */ + ret = SaveEquComponentsInfoAndSortOrder ( iINChI, pINChISort[j], num_components, + orig_inp_data, prep_inp_data, +#if ( FIX_DALKE_BUGS == 1 ) + composite_norm_data + ? composite_norm_data[j] : NULL, +#else + composite_norm_data[j], +#endif + ip->bCompareComponents ); + if ( RETURNED_ERROR( ret ) ) + { + ret = 0; + goto exit_function; + } + else + ret = 1; +#endif + } /* j */ + + if ( !( ip->bINChIOutputOptions & INCHI_OUT_PRINT_OPTIONS ) ) + { + /* Prepare InChI from the structures obtained by + reversing InChI for returning to the caller */ + for ( j = 0; j < INCHI_NUM; j ++ ) + { + if ( !num_components[j] ) + continue; + + /* pINChI[iINCHI][iComponent][bTaut] */ + /* j = disconnected/connected */ + /* k1 = sort order for Mobile or Fixed H */ + + k1 = TAUT_YES; /* in Mobile H order */ + /* store components in Mobile H order */ + + for ( i = 0; i < num_components[j]; i ++ ) + { + + if ( pINChISort[j][k1][i].pINChI[TAUT_NON] && + !pINChISort[j][k1][i].pINChI[TAUT_YES] ) + { + /* make sure Mobile-H is always present */ + for ( k = 0; k < TAUT_NUM; k ++ ) + { + pINChI[j][i][k] = pINChISort[j][k1][i].pINChI[ALT_TAUT(k)]; + pINChI_Aux[j][i][k] = pINChISort[j][k1][i].pINChI_Aux[ALT_TAUT(k)]; + } + } + else + { + for ( k = 0; k < TAUT_NUM; k ++ ) + { + pINChI[j][i][k] = pINChISort[j][k1][i].pINChI[k]; + pINChI_Aux[j][i][k] = pINChISort[j][k1][i].pINChI_Aux[k]; + } + } + } + } + } + else + { + /* Print InChI string(s) */ + bINChIOutputOptions0 = ip->bINChIOutputOptions & ~INCHI_OUT_PRINT_OPTIONS; + bEmbedReconnected = ip->bINChIOutputOptions & INCHI_OUT_EMBED_REC; +#ifdef TARGET_LIB_FOR_WINCHI + out_file->type = INCHI_IOSTREAM_TYPE_STRING; +#endif + + for ( i=1; i<=2; i++ ) + { + bCurOption = INCHI_OUT_PLAIN_TEXT; + if ( i==2 ) + bCurOption = INCHI_OUT_PLAIN_TEXT_COMMENTS; + /* continue; */ + if ( ! (ip->bINChIOutputOptions & bCurOption) ) + continue; + + bINChIOutputOptionsCur = bINChIOutputOptions0 | bCurOption; + if ( i==1 ) + { + /* output INChI */ + bINChIOutputOptionsCur |= bEmbedReconnected; + } + else if ( i==2) + { + /* output annotation */ + inchi_ios_print( out_file, "\n==== %s ====\n", szAnnHdr ); + bINChIOutputOptionsCur |= bEmbedReconnected; + bINChIOutputOptionsCur &= ~INCHI_OUT_TABBED_OUTPUT; + } + else + continue; + +#ifdef TARGET_LIB_FOR_WINCHI + /*if ( ikflag==0 )*/ + out_file->type = INCHI_IOSTREAM_TYPE_STRING; +#endif + INCHI_basic_or_INCHI_reconnected = INCHI_BAS; + + ret2 = OutputINChI2( pCG, strbuf, pINChISort, + INCHI_basic_or_INCHI_reconnected, + pOrigStruct, bDisconnectedCoord, OUT_TN, bINChIOutputOptionsCur, + ip->bAbcNumbers, ip->bCtPredecessors, ip->bNoStructLabels, + num_components, num_non_taut, num_taut, + out_file, log_file, + num_inp,ip->pSdfLabel,ip->pSdfValue, ip->lSdfId, + pSortPrintINChIFlags, save_opt_bits); + + ret &= ret2; + +/************************** not used anymore **************************************************************/ + if ( ret && + !(bINChIOutputOptionsCur & INCHI_OUT_EMBED_REC) ) + { + INCHI_basic_or_INCHI_reconnected = INCHI_REC; + + ret2 = OutputINChI2( pCG, strbuf,pINChISort, + INCHI_basic_or_INCHI_reconnected, + pOrigStruct, bDisconnectedCoord, OUT_TN, bINChIOutputOptionsCur, + ip->bAbcNumbers, ip->bCtPredecessors, ip->bNoStructLabels, + num_components, num_non_taut, num_taut, + out_file, log_file, + num_inp,ip->pSdfLabel,ip->pSdfValue, ip->lSdfId, + pSortPrintINChIFlags, save_opt_bits); + ret &= ret2; + } +/************************** end of not used anymore *********************************************************/ + +#ifdef TARGET_LIB_FOR_WINCHI + /* always calculate InChIKey */ + ikflag++; + if (ikflag == 1) + { + if (ret) + { + char ik_string[256]; /* Resulting InChIKey string */ + int ik_ret=0; /* InChIKey-calc result code */ + int xhash1, xhash2; + char szXtra1[256], szXtra2[256]; + size_t slen = out_file->s.nUsedLength; + char *buf = NULL; + + extract_inchi_substring(&buf, out_file->s.pStr, slen); + + inchi_ios_flush(out_file); + out_file->type = INCHI_IOSTREAM_TYPE_FILE; + + /* calculate and print InChIKey */ + + if (NULL!=buf) + { + xhash1 = xhash2 = 0; + if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1 ) || + ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) + xhash1 = 1; + if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA2 ) || + ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) + xhash2 = 1; + ik_ret = GetINCHIKeyFromINCHI(buf, xhash1, xhash2, ik_string, szXtra1, szXtra2); + inchi_free(buf); + } + else + ik_ret = 3; + + if (ik_ret==INCHIKEY_OK) + { + /* NB: correctly treat tabbed output with InChIKey & hash extensions */ + char csep = '\n'; + if ( ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT ) + csep = '\t'; + inchi_ios_print(out_file, "InChIKey=%-s",ik_string); + if ( xhash1 ) + inchi_ios_print(out_file, "%cXHash1=%-s",csep,szXtra1); + if ( xhash2 ) + inchi_ios_print(out_file, "%cXHash2=%-s",csep,szXtra2); + inchi_ios_print(out_file, "\n"); + } + else + { + inchi_ios_print(log_file, "Warning (Could not compute InChIKey: ", num_inp); + } + + /*inchi_ios_flush(out_file); + inchi_ios_flush2(log_file, stderr);*/ + } + else + { + inchi_ios_flush(out_file); + out_file->type = INCHI_IOSTREAM_TYPE_FILE; + } + } +#endif /* #ifdef TARGET_LIB_FOR_WINCHI */ + + if ( !ret ) + break; + } /* i */ + } + + +exit_function: + for ( j = 0; j < INCHI_NUM; j ++ ) + { + for ( k1 = 0, i = 0; k1 < TAUT_NUM; k1 ++ ) + { + if ( pINChISort[j][k1] ) + inchi_free( pINChISort[j][k1] ); + } + } + ret = ret ? 0 : _IS_FATAL; + + return ret; +} + + +#ifndef COMPILE_ANSI_ONLY /* { */ + + +/* + SaveEquComponentsInfoAndSortOrder +*/ +int SaveEquComponentsInfoAndSortOrder ( int iINChI, + INCHI_SORT *pINChISort[TAUT_NUM], + int *num_components, + ORIG_ATOM_DATA *orig_inp_data, + ORIG_ATOM_DATA *prep_inp_data, + COMP_ATOM_DATA composite_norm_data[TAUT_NUM+1], + int bCompareComponents ) +{ +int nRet = 0, i, k, nNumDeleted; +/* equivalent components and sorting order */ +/* bCompareComponents: bit = 1 => compare */ +/* bit = 2 => compare non-isotopic */ +/* bit = 4 => compare non-tautomeric */ +int bCompareIsotopic, bCompareTaut, bCompareAlt; +ORIG_ATOM_DATA *inp_data = NULL; + + if ( num_components[iINChI] <= 1 ) + return 0; + +#ifdef TARGET_LIB_FOR_WINCHI + if ( !DRAWDATA ) + return 0; +#endif + + if ( !(bCompareComponents & CMP_COMPONENTS) ) + return 0; + + bCompareIsotopic = !(bCompareComponents & CMP_COMPONENTS_NONISO); + + bCompareTaut = (bCompareComponents & CMP_COMPONENTS_NONTAUT) + ? TAUT_NON + : TAUT_YES; + + bCompareAlt = ALT_TAUT(bCompareTaut); + + if ( num_components[iINChI] > 1 ) + { + if ( prep_inp_data[iINChI].bSavedInINCHI_LIB[iINChI] && + prep_inp_data[iINChI].bPreprocessed[iINChI] ) + { + inp_data = prep_inp_data+iINChI; + } + else if ( orig_inp_data->bSavedInINCHI_LIB[iINChI] && + !orig_inp_data->bPreprocessed[iINChI] ) + { + inp_data = orig_inp_data; + } + else + { + inp_data = NULL; + } + + if ( inp_data && !inp_data->nEquLabels && + !prep_inp_data[iINChI].nSortedOrder ) + { + int i1, i2, nSet; + AT_NUMB nAtNo; + AT_NUMB nNumAtoms = (AT_NUMB)inp_data->num_inp_atoms; + + if ( ( prep_inp_data[iINChI].nSortedOrder = + (AT_NUMB *)inchi_calloc(num_components[iINChI]+1, + sizeof(prep_inp_data[0].nSortedOrder[0])) ) ) + { + inp_data->nNumEquSets = 0; + + for ( i1 = 0, nSet = 0; i1 < num_components[iINChI]; i1 = i2 ) + { + nNumDeleted = + (pINChISort[bCompareTaut][i1].pINChI[bCompareTaut] && + pINChISort[bCompareTaut][i1].pINChI[bCompareTaut]->bDeleted); + + for ( i2 = i1+1; i2 < num_components[iINChI]; i2 ++ ) + { + /* isotopic/non-isotopic comparison does not separate equivalent components */ + if ( CompINChI2( pINChISort[bCompareTaut]+i1, + pINChISort[bCompareTaut]+i2, + bCompareTaut, bCompareIsotopic ) ) + { + break; + } + else + { + nNumDeleted += + (pINChISort[bCompareTaut][i2].pINChI[bCompareTaut] && + pINChISort[bCompareTaut][i2].pINChI[bCompareTaut]->bDeleted); + } + } + + if ( i2 - i1 - nNumDeleted > 1 ) + { + if ( inp_data->nEquLabels || + (inp_data->nEquLabels = + (AT_NUMB *)inchi_calloc(inp_data->num_inp_atoms+1, + sizeof(inp_data->nEquLabels[0]))) ) + { + nSet ++; + /* found i2-i1 equivalent components && */ + /* memory has been allocated */ + for ( i = i1; i < i2; i ++ ) + { + INChI_Aux *pINChI_Aux; + int aux_test; + + if ( pINChISort[bCompareTaut][i].pINChI[bCompareTaut] && + pINChISort[bCompareTaut][i].pINChI[bCompareTaut]->bDeleted) + continue; + + aux_test = + ( pINChISort[bCompareTaut][i].pINChI_Aux[bCompareTaut] && + pINChISort[bCompareTaut][i].pINChI_Aux[bCompareTaut]->nNumberOfAtoms ); + + pINChI_Aux = + aux_test ? pINChISort[bCompareTaut][i].pINChI_Aux[bCompareTaut] + : ( pINChISort[bCompareTaut][i].pINChI_Aux[bCompareAlt] && + pINChISort[bCompareTaut][i].pINChI_Aux[bCompareAlt]->nNumberOfAtoms ) + ? pINChISort[bCompareTaut][i].pINChI_Aux[bCompareAlt] + : (INChI_Aux *)NULL; + + if ( pINChI_Aux && pINChI_Aux->nOrigAtNosInCanonOrd ) + { + for ( k = 0; k < pINChI_Aux->nNumberOfAtoms; k ++ ) + { + if ( (nAtNo = pINChI_Aux->nOrigAtNosInCanonOrd[k]) && + nAtNo <= nNumAtoms ) + { + inp_data->nEquLabels[nAtNo-1] = nSet; + } + } + } + } + } + else + { + return CT_OUT_OF_RAM; + } + } + } + + nRet |= nSet? 1:0; + } + else + { + return CT_OUT_OF_RAM; + } + + inp_data->nNumEquSets = nSet; + /* output order */ + prep_inp_data[iINChI].nSortedOrder[0] = 0; + for ( i1 = 0; i1 < num_components[iINChI]; i1 ++ ) + { + prep_inp_data[iINChI].nSortedOrder[i1+1] = + pINChISort[TAUT_YES][i1].ord_number+1; + } + +#ifdef TARGET_LIB_FOR_WINCHI /* { */ + if ( DRAWDATA && GET_DRAWDATA && + inp_data->nNumEquSets > 0 && + inp_data->nEquLabels ) + { + int nType = inp_data->bPreprocessed[iINChI] + ? COMPONENT_ORIGINAL_PREPROCESSED + : COMPONENT_ORIGINAL; + + struct DrawData *pDrawData = GET_DRAWDATA( 0, nType, iINChI); + if ( pDrawData && + pDrawData->pWindowData && + !pDrawData->pWindowData->nEquLabels ) + { + /* copy equivalence data from inp_data */ + /* to pDrawData->pWindowData */ + if ( inp_data->nEquLabels && + (pDrawData->pWindowData->nEquLabels = + (AT_NUMB *)inchi_calloc(inp_data->num_inp_atoms, + sizeof(inp_data->nEquLabels[0])))) + { + memcpy( pDrawData->pWindowData->nEquLabels, + inp_data->nEquLabels, + inp_data->num_inp_atoms* + sizeof(inp_data->nEquLabels[0])); + pDrawData->pWindowData->nNumEquSets = + inp_data->nNumEquSets; + pDrawData->pWindowData->nCurEquLabel = 0; + } + } + } + +#endif /* } TARGET_LIB_FOR_WINCHI */ + } + } + + return nRet; +} + + +/* + DisplayTheWholeCompositeStructure +*/ +int DisplayTheWholeCompositeStructure( struct tagCANON_GLOBALS *pCG, + struct tagINCHI_CLOCK *ic, + INPUT_PARMS *ip, + struct tagStructData *sd, + long num_inp, + int iINChI, + PINChI2 *pINChI2, + PINChI_Aux2 *pINChI_Aux2, + ORIG_ATOM_DATA *orig_inp_data, + ORIG_ATOM_DATA *prep_inp_data, + COMP_ATOM_DATA composite_norm_data[TAUT_NUM+1] ) +{ +ORIG_ATOM_DATA *inp_data = NULL; +int jj, j, k, err = 0, nNumIntermediateTaut = 0, bDisplayTaut; +char szTitle[256]; +int nNumTautComponents, m; +int bCompareIsotopic = !(ip->bCompareComponents & CMP_COMPONENTS_NONISO); +int bCompareTaut = (ip->bCompareComponents & CMP_COMPONENTS_NONTAUT) ? TAUT_NON : TAUT_YES; + + if ( ip->bCompareComponents & CMP_COMPONENTS ) + { + if ( prep_inp_data[iINChI].bSavedInINCHI_LIB[iINChI] && prep_inp_data[iINChI].bPreprocessed[iINChI] ) + { + inp_data = prep_inp_data+iINChI; + } + else if ( orig_inp_data->bSavedInINCHI_LIB[iINChI] && !orig_inp_data->bPreprocessed[iINChI] ) + { + inp_data = orig_inp_data; + } + } + + /************************************************************************** + * display from one up to 4 structure pictures-results for all components * + * Enable buttons: * + * BN (non-tautomeric non-isotopic): inp_norm_data[0]->bExists * + * TN (tautomeric non-isotopic): inp_norm_data[1]->bExists * + * BI (non-tautomeric isotopic): inp_norm_data[0]->bExists && * + * inp_norm_data[0]->bHasIsotopicLayer * + * TI (tautomeric isotopic): inp_norm_data[1]->bExists && * + * inp_norm_data[1]->bHasIsotopicLayer * + **************************************************************************/ + + for ( jj = 0; + ip->bDisplayCompositeResults && + !sd->bUserQuitComponentDisplay && + jj <= TAUT_INI; + jj ++ ) + { + j = (jj==0) ? TAUT_NON + : (jj==1)? TAUT_INI + : (jj==2) ? TAUT_YES + : -1; + + if ( j < 0 ) + continue; + + if ( composite_norm_data[j].bExists && + composite_norm_data[j].num_components > 1 ) + { + + bDisplayTaut = ( !(ip->nMode & REQ_MODE_BASIC) && !j ) ? -1 + : j; + nNumTautComponents = 0; + if ( bDisplayTaut ) + { + /* find whether the structure is actually tautomeric */ + for ( m = 0; + m < composite_norm_data[TAUT_YES].num_components; + m ++ ) + { + if ( !pINChI2[m][TAUT_YES] ) + continue; + if ( pINChI2[m][TAUT_YES]->bDeleted || + pINChI2[m][TAUT_YES]->lenTautomer > 0 ) + nNumTautComponents ++; + } + } + + for ( k = 0; + k <= composite_norm_data[j].bHasIsotopicLayer && + !sd->bUserQuitComponentDisplay; + k ++ ) + { + /* added number of components, added another format */ + /* for a single component case - DCh */ + int bMobileH = (bDisplayTaut>0 && nNumTautComponents); + + sprintf( szTitle, "%s Structure #%ld%s%s.%s%s%s%s%s", + j == TAUT_INI? "Preprocessed":"Result for", num_inp, + bMobileH? ", mobile H": + bDisplayTaut==0?", fixed H":"", + /*j? ", mobile H":", fixed H",*/ + k? ", isotopic":"", + SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), + iINChI? " (Reconnected)":""); + +#ifndef TARGET_LIB_FOR_WINCHI + + /****** Display composite Result structure **************/ + nNumIntermediateTaut += (j == TAUT_INI ); + /* display TAUT_INI (preprocessed) only once */ + if ( j != TAUT_INI || nNumIntermediateTaut == 1 ) + { + err = + DisplayCompositeStructure( pCG, + composite_norm_data, + j==TAUT_INI ? 1:k, + /* bIsotopic*/ + j, /*tautomeric*/ + j==TAUT_INI ? NULL + : pINChI2, + j==TAUT_INI ? NULL + : pINChI_Aux2, + ip->bAbcNumbers, + &ip->dp, + ip->nMode, + szTitle ); + } + if ( sd->bUserQuitComponentDisplay = (err==ESC_KEY) ) + break; + + if ( inp_data && + inp_data->nEquLabels && + inp_data->nNumEquSets && + !sd->bUserQuitComponentDisplay && + ( ( j == bCompareTaut || bCompareTaut && j == TAUT_INI ) || + bCompareTaut && !composite_norm_data[bCompareTaut].bExists ) && + ( k == bCompareIsotopic || bCompareIsotopic && !composite_norm_data[j].bHasIsotopicLayer ) + ) + { + AT_NUMB nEquSet; + int bDisplaySaved = ip->bDisplay; + + /****** Display Equ Sets of composite Result structure **************/ + for ( nEquSet = 1; + nEquSet <= inp_data->nNumEquSets; + nEquSet ++ ) + { + sprintf( szTitle, "Equ set %d of %d, %s Structure #%ld%s%s.%s%s%s%s%s", + nEquSet, inp_data->nNumEquSets, + j == TAUT_INI? "Preprocessed":"Result for", + num_inp, + (bDisplayTaut>0 && nNumTautComponents)? ", mobile H": bDisplayTaut==0?", fixed H":"", + /*j? ", mobile H":", fixed H",*/ + k? ", isotopic":"", + SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), iINChI? " (Reconnected)":""); + ip->dp.nEquLabels = inp_data->nEquLabels; + ip->dp.nCurEquLabel = nEquSet; + ip->dp.nNumEquSets = inp_data->nNumEquSets; + ip->bDisplay = 1; /* force display if it was not requested */ + err = DisplayCompositeStructure( pCG, + composite_norm_data, + k, j, + pINChI2, pINChI_Aux2, + ip->bAbcNumbers, + &ip->dp, + ip->nMode, + szTitle ); + ip->dp.nEquLabels = NULL; + ip->dp.nCurEquLabel = 0; + ip->dp.nNumEquSets = 0; + ip->bDisplay = bDisplaySaved; /* restore display option */ + + if ( sd->bUserQuitComponentDisplay = (err==ESC_KEY) ) + break; + } + } +#else + if(DRAWDATA && j <= TAUT_YES) + { + struct DrawData vDrawData; + vDrawData.pWindowData = + CreateWinDataComposite_( pCG, + composite_norm_data, + k, + j, + pINChI2, + pINChI_Aux2, + ip->bAbcNumbers, + &ip->dp, + ip->nMode); + + /* vDrawData.pWindowData = CreateWinData_( composite_norm_data[j].at, composite_norm_data[j].num_at, + k, j, pINChI[i], pINChI_Aux[i],ip->bAbcNumbers, &ip->dp, ip->nMode ); */ + + if( vDrawData.pWindowData != NULL ) + { + int nType; + vDrawData.nComponent = 0; + if( j == 0 ) + nType = (k == 0) ? COMPONENT_BN: COMPONENT_BI; + else + nType = (k == 0) ? COMPONENT_TN: COMPONENT_TI; + vDrawData.nType = nType; + vDrawData.bReconnected = iINChI; /* 0=>main; 1=>reconnected */ + vDrawData.szTitle = inchi__strdup(szTitle); + vDrawData.pWindowData->szTitle = inchi__strdup(szTitle); + if ( inp_data && inp_data->nEquLabels && + inp_data->nNumEquSets && + (j == bCompareTaut || bCompareTaut && !composite_norm_data[bCompareTaut].bExists) && + (k == bCompareIsotopic || bCompareIsotopic && !composite_norm_data[j].bHasIsotopicLayer) && + (vDrawData.pWindowData->nEquLabels = (AT_NUMB *)inchi_calloc(inp_data->num_inp_atoms, + sizeof(inp_data->nEquLabels[0])))) { + memcpy( vDrawData.pWindowData->nEquLabels, inp_data->nEquLabels, + inp_data->num_inp_atoms * sizeof(inp_data->nEquLabels[0])); + vDrawData.pWindowData->nNumEquSets = inp_data->nNumEquSets; + vDrawData.pWindowData->nCurEquLabel = 0; + } + DRAWDATA(&vDrawData); + } + } + else if(DRAWDATA && GET_DRAWDATA && j == TAUT_INI) + { + struct DrawData vDrawData; + struct DrawData *pDrawData; + + if ( !(ip->bCompareComponents & CMP_COMPONENTS) || + (ip->bCompareComponents & CMP_COMPONENTS_NONTAUT) || + !k != !composite_norm_data[j].bHasIsotopicLayer ) + { + + continue; + } + vDrawData.pWindowData = + CreateWinDataComposite_( pCG, + composite_norm_data, + 1 /*k*/, + j, + NULL, + NULL, + ip->bAbcNumbers, + &ip->dp, + ip->nMode); + + + + if( vDrawData.pWindowData != NULL ) + { + int nType = COMPONENT_ORIGINAL_PREPROCESSED; + pDrawData = GET_DRAWDATA( 0, nType, iINChI); + if ( pDrawData ) + { + FreeDrawData( pDrawData ); + pDrawData->pWindowData = vDrawData.pWindowData; + vDrawData.pWindowData = NULL; + } + else + { + pDrawData = &vDrawData; + } + + /* vDrawData.pWindowData = CreateWinData_( composite_norm_data[j].at, composite_norm_data[j].num_at, + k, j, pINChI[i], pINChI_Aux[i],ip->bAbcNumbers, &ip->dp, ip->nMode ); */ + + pDrawData->nComponent = 0; + pDrawData->nType = nType; + pDrawData->bReconnected = iINChI; /* 0=>main; 1=>reconnected */ + pDrawData->szTitle = inchi__strdup(szTitle); + pDrawData->pWindowData->szTitle = inchi__strdup(szTitle); + if ( inp_data && inp_data->nEquLabels && + inp_data->nNumEquSets && + /*(j == bCompareTaut || bCompareTaut && !composite_norm_data[bCompareTaut].bExists) &&*/ + /*(k == bCompareIsotopic || bCompareIsotopic && !composite_norm_data[j].bHasIsotopicLayer) &&*/ + (pDrawData->pWindowData->nEquLabels = (AT_NUMB *)inchi_calloc(inp_data->num_inp_atoms, + sizeof(inp_data->nEquLabels[0])))) + { + memcpy( pDrawData->pWindowData->nEquLabels, + inp_data->nEquLabels, + inp_data->num_inp_atoms * sizeof(inp_data->nEquLabels[0])); + pDrawData->pWindowData->nNumEquSets = inp_data->nNumEquSets; + pDrawData->pWindowData->nCurEquLabel = 0; + } + if ( pDrawData == &vDrawData ) { + DRAWDATA(pDrawData); /* there was no prepocessed structure */ + } + } + } + +#endif /* #ifndef TARGET_LIB_FOR_WINCHI */ + } + } + } + + return err; +} + + +/* + DisplayTheWholeStructure +*/ +int DisplayTheWholeStructure( struct tagCANON_GLOBALS *pCG, + struct tagINCHI_CLOCK *ic, + STRUCT_DATA *sd, + INPUT_PARMS *ip, + char *szTitle, + INCHI_IOSTREAM *inp_file, + INCHI_IOSTREAM *log_file, + ORIG_ATOM_DATA *orig_inp_data, + long num_inp, + int iINChI, + int bShowStruct, + int bINCHI_LIB_Flag ) +{ + +int bDisplayEqu = 0; + +#ifndef TARGET_LIB_FOR_WINCHI + + /* Displaying equivalent input structures when disconnection has been done: */ + /* in case of TARGET_LIB_FOR_WINCHI equivalence info is always unknown here and bOriginalReconnected=0 */ + + int bOriginalReconnected = iINChI < 0 && + orig_inp_data && orig_inp_data->nEquLabels && + (sd->bTautFlagsDone[INCHI_BAS] & TG_FLAG_DISCONNECT_COORD_DONE) && + (ip->bTautFlags & TG_FLAG_RECONNECT_COORD); + + const char *lpszType = + bOriginalReconnected ? " (Reconnected)" + : (iINChI < 0 ) ? "" + : ( iINChI==INCHI_BAS ) ? " (Preprocessed)" + : (iINChI == INCHI_REC ) ? " (Reconnected)" + : ""; + + int err = 0; + + /* Display the original structure */ + + bDisplayEqu = bShowStruct && ip->bDisplay && + ip->dp.nEquLabels && 0 < ip->dp.nCurEquLabel && ip->dp.nCurEquLabel <= ip->dp.nNumEquSets; +#else + if(!DRAWDATA || !DRAWDATA_EXISTS) + return 0; +#endif + + +#if !defined(TARGET_API_LIB) && !defined(COMPILE_ANSI_ONLY) + /* Ask the user whether to process the input structure or quit*/ + if ( ip->bDisplay && inp_file->f != stdin ) + { + if ( user_quit(ic, bDisplayEqu ? "Enter=Display identical components, Esc=Stop ?" + : "Enter=Display, Esc=Stop ?", + ip->ulDisplTime) ) + { + sd->bUserQuit = 1; + goto exit_function; + } + } +#endif + + /* + * Display the whole input structure in console app + */ + +/* #ifndef TARGET_LIB_FOR_WINCHI */ + +#if ( !defined( TARGET_LIB_FOR_WINCHI ) && !defined(TARGET_EXE_STANDALONE) ) + if ( bShowStruct && ip->bDisplay ) { + if ( bDisplayEqu ) { + sprintf( szTitle, " Equ Set %d of %d, Input Structure #%ld.%s%s%s%s%s", + ip->dp.nCurEquLabel, ip->dp.nNumEquSets, + num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), lpszType); + } else { + sprintf( szTitle, "Input Structure #%ld.%s%s%s%s%s", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), lpszType); + } + err = DisplayStructure( orig_inp_data->at, orig_inp_data->num_inp_atoms, 0, 1, 0, NULL, 1/*isotopic*/, 0/*taut*/, NULL, NULL, + ip->bAbcNumbers, &ip->dp, ip->nMode, szTitle ); + sd->bUserQuitComponent = (err==ESC_KEY); + if ( !err ) { + inchi_fprintf( stderr, "Cannot display the structure\n"); + } + } + if( !bDisplayEqu ) { + /* console output progress report */ + if ( ip->bDisplay && !sd->bUserQuitComponent ) { + if ( iINChI == 1 ) { + if ( ip->bDisplay ) + inchi_ios_eprint( log_file, "Processing (rec) structure #%ld.%s%s%s%s...\n", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); + else + inchi_fprintf( stderr, "Processing (rec) structure #%ld.%s%s%s%s...\r", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); + } else { + if ( ip->bDisplay ) + inchi_ios_eprint( log_file, "Processing structure #%ld.%s%s%s%s...\n", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); + else + inchi_fprintf( stderr, "Processing structure #%ld.%s%s%s%s...\r", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); + } + } + } +#endif + + + /* + * Store the whole input structure in GUI application + */ + +#ifdef TARGET_LIB_FOR_WINCHI + if ( ip->bDisplay && bINCHI_LIB_Flag ) +#else + if ( (ip->bDisplay || (ip->bCompareComponents & CMP_COMPONENTS)) && bINCHI_LIB_Flag ) +#endif + + { + + int bBit, k, bReconnected, nComponent, bPreprocessed; + + for ( bBit = 1, k = 0; k < 8; k ++, bBit <<= 1 ) { + /****************************************************************************** + * bReconnected = k%2 (0 or 1) + * nComponent = k/4 (0 or 1) + * bPreprocessed = (k/2)%2 (0 or 1) + ******************************************************************************/ + if ( !(bINCHI_LIB_Flag & bBit) ) + continue; + + bReconnected = k%2; + nComponent = k/4; + bPreprocessed = ((k/2)%2); + + sprintf( szTitle, "%s Structure #%ld.%s%s%s%s", + bPreprocessed? "Preprocessed" : bReconnected? "Reconnected" : "Input", + num_inp, + SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue)); + +#ifdef TARGET_LIB_FOR_WINCHI + if(DRAWDATA && DRAWDATA_EXISTS) + { + struct DrawData vDrawData; + int nType = bPreprocessed? COMPONENT_ORIGINAL_PREPROCESSED : COMPONENT_ORIGINAL; + if ( DRAWDATA_EXISTS( nComponent, bPreprocessed, bReconnected ) ) + { + sd->nErrorType = _IS_FATAL; + sd->nErrorCode = CT_UNKNOWN_ERR; + return -1; + } + vDrawData.pWindowData = CreateWinData_( pCG, + orig_inp_data->at, + orig_inp_data->num_inp_atoms, + 0, + 1 /* bAdd_DT_to_num_H */, + 0, + NULL, + 1, + 0, + NULL, + NULL, + ip->bAbcNumbers, + &ip->dp, + ip->nMode ); + if( vDrawData.pWindowData != NULL ) + { + vDrawData.nComponent = nComponent; + vDrawData.nType = nType; /* COMPONENT_ORIGINAL or COMPONENT_ORIGINAL_PREPROCESSED */ + vDrawData.bReconnected = bReconnected; /* 0=>main; 1=>reconnected */ + vDrawData.pWindowData->szTitle = inchi__strdup(szTitle); + vDrawData.szTitle = inchi__strdup(szTitle); + DRAWDATA(&vDrawData); + if ( !nComponent ) + { + /* keep track of saved INCHI_LIB data */ + orig_inp_data->bSavedInINCHI_LIB[bReconnected] ++; + orig_inp_data->bPreprocessed[bReconnected] = bPreprocessed; + } + } + } +#else + if ( !nComponent ) { + /* keep track of saved INCHI_LIB data */ + orig_inp_data->bSavedInINCHI_LIB[bReconnected] ++; + orig_inp_data->bPreprocessed[bReconnected] = bPreprocessed; + } +#endif + } + } + +exit_function: + return sd->bUserQuit; +} + +#else +/* dummies */ +int DisplayTheWholeStructure( struct tagCANON_GLOBALS *pCG, + struct tagINCHI_CLOCK *ic, + STRUCT_DATA *sd, + INPUT_PARMS *ip, + char *szTitle, + INCHI_IOSTREAM *inp_file, + INCHI_IOSTREAM *log_file, + ORIG_ATOM_DATA *orig_inp_data, + long num_inp, + int iINChI, + int bShowStruct, + int bINCHI_LIB_Flag ) +{ return 0; } +int DisplayStructure( struct tagCANON_GLOBALS *pCG, + inp_ATOM *at, + int num_at, int num_removed_H, + int bAdd_DT_to_num_H, + int nNumRemovedProtons, + NUM_H *nNumRemovedProtonsIsotopic, + int bIsotopic, + int j /*bTautomeric*/, + INChI **cur_INChI, INChI_Aux **cur_INChI_Aux, + int bAbcNumbers, + DRAW_PARMS *dp, + INCHI_MODE nMode, + char *szTitle ) +{ return 0; } + + + + +#endif /* }COMPILE_ANSI_ONLY */ + + +#ifndef TARGET_API_LIB + +void SplitTime( unsigned long ulTotalTime, + int *hours, + int *minutes, + int *seconds, + int *mseconds ) +{ + + *mseconds = (int)(ulTotalTime % 1000); + ulTotalTime /= 1000; + *seconds = (int)(ulTotalTime % 60); + ulTotalTime /= 60; + *minutes = (int)(ulTotalTime % 60); + ulTotalTime /= 60; + *hours = (int)(ulTotalTime); +} +#endif + + +/* + Check if structure is chiral +*/ +int bIsStructChiral( PINChI2 *pINChI2[INCHI_NUM], int num_components[] ) +{ + int i, j, k; + INChI *pINChI; + INChI_Stereo *Stereo; + + for ( j = 0; j < INCHI_NUM; j ++ ) + { + /* disconnected / reconnected */ + if ( !num_components[j] ) + continue; + + for ( i = 0; i < num_components[j]; i ++ ) + { + /* i-th component */ + for ( k = 0; k < TAUT_NUM; k ++ ) + { + /* mobile/immobile H */ + if ( (pINChI = pINChI2[j][i][k]) && + !pINChI->bDeleted && + pINChI->nNumberOfAtoms > 0 ) + { + + if ( (Stereo = pINChI->Stereo) && + Stereo->t_parity && + Stereo->nNumberOfStereoCenters > 0 && + Stereo->nCompInv2Abs ) + { + return 1; /* inversion changed stereo */ + } + if ( (Stereo = pINChI->StereoIsotopic) && + Stereo->t_parity && + Stereo->nNumberOfStereoCenters > 0 && + Stereo->nCompInv2Abs ) + { + return 1; /* inversion changed stereo */ + } + } + } + } + } + + return 0; +} + + +/* + Release IChI working memory... +*/ +void FreeAllINChIArrays( PINChI2 *pINChI[INCHI_NUM], + PINChI_Aux2 *pINChI_Aux[INCHI_NUM], + int num_components[INCHI_NUM] ) +{ + int k; + for ( k = 0; k < INCHI_NUM; k ++ ) + { + int nk = num_components[k]; + + FreeINChIArrays( pINChI[k], pINChI_Aux[k], num_components[k] ); + + num_components[k] = 0; + + if ( nk && /* added check for nk: 2013-12-15 IPl */ + pINChI[k] ) + { + inchi_free( pINChI[k] ); + pINChI[k] = NULL; + } + + if ( nk && /* added check for nk: 2013-12-15 IPl */ + pINChI_Aux[k] ) + { + inchi_free( pINChI_Aux[k] ); + pINChI_Aux[k] = NULL; + } + } + + return; +} + + +/* + Release InChI working memory... +*/ +void FreeINChIArrays( PINChI2 *pINChI, + PINChI_Aux2 *pINChI_Aux, + int num_components ) +{ + int i, k; + /* Release allocated memory */ + if ( pINChI ) + { + for ( i = 0; i < num_components; i++ ) + for ( k = 0; k < TAUT_NUM; k++ ) + Free_INChI( &pINChI[i][k] ); + } + if ( pINChI_Aux ) + { + for ( i = 0; i < num_components; i++ ) + for ( k = 0; k < TAUT_NUM; k++ ) + Free_INChI_Aux( &pINChI_Aux[i][k] ); + } +} + + +/* + Treat errors returned by CreateOneComponentINChI( ... ) +*/ +int TreatErrorsInCreateOneComponentINChI( STRUCT_DATA *sd, + INPUT_PARMS *ip, + ORIG_ATOM_DATA *orig_inp_data, + int i, + long num_inp, + INCHI_IOSTREAM *inp_file, + INCHI_IOSTREAM *log_file, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM *prb_file ) +{ + if ( sd->nErrorCode ) + { + AddErrorMessage( sd->pStrErrStruct, ErrMsg(sd->nErrorCode) ); + inchi_ios_eprint( log_file, + "Error %d (%s) structure #%ld component %d.%s%s%s%s\n", + sd->nErrorCode, sd->pStrErrStruct, + num_inp, i+1, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); + sd->nErrorType = (sd->nErrorCode==CT_OUT_OF_RAM || sd->nErrorCode==CT_USER_QUIT_ERR) + ? _IS_FATAL + : _IS_ERROR; + +#ifdef TARGET_LIB_FOR_WINCHI + if ( (ip->bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW) && + (ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) ) + { + sd->nErrorType = ProcessStructError( out_file, log_file, + sd->pStrErrStruct, sd->nErrorType, + num_inp, ip ); + /* Save the problem structure */ + if ( prb_file->f && + 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd && + !ip->bSaveAllGoodStructsAsProblem ) + { + MolfileSaveCopy( inp_file, sd->fPtrStart, sd->fPtrEnd, prb_file->f, num_inp); + } + } + else + { + /* Save the problem structure */ + if ( sd->nErrorCode && + prb_file->f && + 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd && + !ip->bSaveAllGoodStructsAsProblem ) + { + MolfileSaveCopy( inp_file, sd->fPtrStart, sd->fPtrEnd, prb_file->f, num_inp); + } + } +#endif + } + +/* #ifndef TARGET_API_LIB */ +#if ( !defined( TARGET_API_LIB ) && !defined(TARGET_EXE_STANDALONE) ) + /* print the logfile record */ + if ( log_file->f && log_file->f != stderr && (sd->ulStructTime >= 1000 || sd->nErrorCode) ) { + fprintf( log_file->f, "%10lu msec structure #%ld.%s%s%s%s (%d component%s, %d atom%s, error=%d).\n", + sd->ulStructTime, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), + orig_inp_data->num_components, orig_inp_data->num_components==1?"":"s", + orig_inp_data->num_inp_atoms, orig_inp_data->num_inp_atoms==1?"":"s", sd->nErrorCode ); + } +#endif + + return sd->nErrorType; +} + + +int TreatCreateINChIWarning( STRUCT_DATA *sd, + INPUT_PARMS *ip, + ORIG_ATOM_DATA *orig_inp_data, + long num_inp, + INCHI_IOSTREAM *inp_file, + INCHI_IOSTREAM *log_file, + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM *prb_file ) +{ + +#if ( bRELEASE_VERSION == 0 && (EXTR_FLAGS || EXTR_MASK) ) + if ( EXTR_MASK ? ((sd->bExtract & EXTR_MASK) == EXTR_FLAGS) + : (sd->bExtract & EXTR_FLAGS) + ) + { + char szMsg[64]; + sprintf( szMsg, "ExtractStruct.code=0x%X", sd->bExtract); + WarningMessage(sd->pStrErrStruct, szMsg); + } +#endif + + if ( !sd->nErrorCode && sd->pStrErrStruct[0] ) + { + inchi_ios_eprint( log_file, "Warning (%s) structure #%ld.%s%s%s%s\n", + sd->pStrErrStruct, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); + sd->nErrorType = _IS_WARNING; + +#ifdef TARGET_LIB_FOR_WINCHI + if ( (ip->bINChIOutputOptions & INCHI_OUT_WINCHI_WINDOW) && + (ip->bINChIOutputOptions & INCHI_OUT_PLAIN_TEXT) ) + { + sd->nErrorType = ProcessStructError( out_file, + log_file, + sd->pStrErrStruct, + sd->nErrorType, + num_inp, + ip ); + } +#endif + /* save the structure as a problem structure if requested */ + if ( ip->bSaveWarningStructsAsProblem && !ip->bSaveAllGoodStructsAsProblem && + prb_file->f && 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd ) + { + MolfileSaveCopy( inp_file, + sd->fPtrStart, + sd->fPtrEnd, + prb_file->f, + num_inp); + } + +#if ( bRELEASE_VERSION == 0 ) + /* otherwise extract the structure as a problem structure if requested */ + else + if ( (EXTR_MASK ? ((sd->bExtract & EXTR_MASK) == EXTR_FLAGS) : (sd->bExtract & EXTR_FLAGS)) + && !ip->bSaveAllGoodStructsAsProblem && + prb_file->f && + 0L <= sd->fPtrStart && sd->fPtrStart < sd->fPtrEnd ) + { + MolfileSaveCopy( inp_file->f, + sd->fPtrStart, + sd->fPtrEnd, + prb_file->f, + num_inp); + } +#endif + } + +#if ( bRELEASE_VERSION != 1 && bOUTPUT_ONE_STRUCT_TIME == 1 ) +#ifndef TARGET_API_LIB + if ( log_file && log_file != stderr ) + { + fprintf( log_file, "%10lu msec structure %1dD #%ld.%s%s%s%s (%d component%s, %d atom%s, error=%d).\n", + sd->ulStructTime, orig_inp_data->num_dimensions, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), + orig_inp_data->num_components, orig_inp_data->num_components==1?"":"s", + orig_inp_data->num_inp_atoms, orig_inp_data->num_inp_atoms==1?"":"s", sd->nErrorCode ); + } +#else + if ( log_file ) +{ + inchi_ios_eprint( log_file, "%10lu msec structure %1dD #%ld.%s%s%s%s (%d component%s, %d atom%s, error=%d).\n", + sd->ulStructTime, orig_inp_data->num_dimensions, num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), + orig_inp_data->num_components, orig_inp_data->num_components==1?"":"s", + orig_inp_data->num_inp_atoms, orig_inp_data->num_inp_atoms==1?"":"s", sd->nErrorCode ); + } +#endif +#endif + + return sd->nErrorType; +} + + +/* Treat errors/warnings... */ +int GetProcessingWarningsOneComponentInChI( INChI *cur_INChI[], + INP_ATOM_DATA **inp_norm_data, + STRUCT_DATA *sd) +{ + int i, ret = 0; + for (i = 0; i < TAUT_NUM; i ++ ) { + if ( cur_INChI[i] && cur_INChI[i]->nNumberOfAtoms>0 ) { + ret |= get_processing_warnings_one_InChI(cur_INChI[i], inp_norm_data[i], sd->pStrErrStruct); + } + } + return ret; +} + + +/* Treat errors/warnings... */ +int get_processing_warnings_one_InChI( INChI *pINChI, + INP_ATOM_DATA *inp_norm_data, + char *pStrErrStruct) +{ +int j; +int nAmbiguousStereoAtoms, nAmbiguousStereoBonds; +nAmbiguousStereoAtoms = 0; +nAmbiguousStereoBonds = 0; + + if ( inp_norm_data->at ) + { + for ( j = 0; j < pINChI->nNumberOfAtoms; j ++ ) + { + if ( inp_norm_data->at[j].bAmbiguousStereo & (AMBIGUOUS_STEREO_ATOM | AMBIGUOUS_STEREO_ATOM_ISO) ) + { + nAmbiguousStereoAtoms ++; + } + if ( inp_norm_data->at[j].bAmbiguousStereo & (AMBIGUOUS_STEREO_BOND | AMBIGUOUS_STEREO_BOND_ISO) ) + { + nAmbiguousStereoBonds ++; + } + } + if ( nAmbiguousStereoAtoms ) + { + WarningMessage(pStrErrStruct, "Ambiguous stereo:"); + WarningMessage(pStrErrStruct, "center(s)"); + } + if ( nAmbiguousStereoBonds ) + { + WarningMessage(pStrErrStruct, "Ambiguous stereo:"); + WarningMessage(pStrErrStruct, "bond(s)"); + } + } + + return (nAmbiguousStereoAtoms || nAmbiguousStereoBonds); +} diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/sha2.c b/INCHI-1-SRC/INCHI_BASE/src/sha2.c similarity index 93% rename from INCHI-1-SRC/INCHI_API/inchi_dll/sha2.c rename to INCHI-1-SRC/INCHI_BASE/src/sha2.c index 0e78671..ed06a65 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/sha2.c +++ b/INCHI-1-SRC/INCHI_BASE/src/sha2.c @@ -4,7 +4,7 @@ * Copyright (C) Brainspark B.V. * * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 + * International Chemical Identifier (InChI) * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it @@ -12,27 +12,22 @@ * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust - * c/o FIZ CHEMIE Berlin + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. + * or e-mail to alan@inchi-trust.org * */ - /* * The SHA-256 standard was published by NIST in 2002. * @@ -452,5 +447,5 @@ int sha2_self_test( void ) { return( 0 ); } -#endif +#endif diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/sha2.h b/INCHI-1-SRC/INCHI_BASE/src/sha2.h similarity index 84% rename from INCHI-1-SRC/INCHI_API/inchi_dll/sha2.h rename to INCHI-1-SRC/INCHI_BASE/src/sha2.h index 67a6f4a..dc36456 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/sha2.h +++ b/INCHI-1-SRC/INCHI_BASE/src/sha2.h @@ -14,27 +14,27 @@ * International Chemical Identifier (InChI) Software version 1.04 * Copyright (C) IUPAC and InChI Trust Limited * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * * This library is free software; you can redistribute it and/or modify it * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust - * c/o FIZ CHEMIE Berlin + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. + * or e-mail to alan@inchi-trust.org * */ @@ -45,8 +45,9 @@ * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf */ -#ifndef _SHA2_H -#define _SHA2_H + +#ifndef _SHA2_H_ +#define _SHA2_H_ #ifdef __cplusplus extern "C" { @@ -130,5 +131,6 @@ int sha2_self_test( void ); } #endif -#endif /* sha2.h */ + +#endif /* _SHA2_H_ */ diff --git a/INCHI-1-SRC/INCHI/common/strutil.c b/INCHI-1-SRC/INCHI_BASE/src/strutil.c similarity index 55% rename from INCHI-1-SRC/INCHI/common/strutil.c rename to INCHI-1-SRC/INCHI_BASE/src/strutil.c index 504a09a..4aa6915 100644 --- a/INCHI-1-SRC/INCHI/common/strutil.c +++ b/INCHI-1-SRC/INCHI_BASE/src/strutil.c @@ -1,4621 +1,5280 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include - -#include "mode.h" - -#include "inpdef.h" -#include "util.h" -#include "ichi.h" -#include "strutil.h" -#include "ichierr.h" - -#include "ichicomp.h" -#include "extr_ct.h" -#include "ichister.h" - -#include "ichi_io.h" - - -#define FIX_P_IV_Plus_O_Minus /* added fix to remove_ion_pairs() -- 2010-03-17 DT */ - -/* local prototypes */ -int cmp_components( const void *a1, const void *a2 ); -/*int mark_one_struct_component( inp_ATOM* at, int j, AT_NUMB *mark, AT_NUMB num_disconnected_components );*/ -INChI_Stereo *Alloc_INChI_Stereo(int num_at, int num_bonds); -int RemoveInpAtBond( inp_ATOM *at, int iat, int k ); -int DisconnectInpAtBond( inp_ATOM *at, AT_NUMB *nOldCompNumber, int iat, int neigh_ord ); -int move_explicit_Hcation(inp_ATOM *at, int num_at, int iat, int iat_H, int bInAllComponents); -int DisconnectOneLigand( inp_ATOM *at, AT_NUMB *nOldCompNumber, S_CHAR *bMetal, char *elnumber_Heteroat, - int num_halogens, int num_atoms, int iMetal, int jLigand, INCHI_MODE *bTautFlagsDone ); -int bIsAmmoniumSalt( inp_ATOM *at, int i, int *piO, int *pk, S_CHAR *num_explicit_H ); -int DisconnectAmmoniumSalt ( inp_ATOM *at, int i, int iO, int k, S_CHAR *num_explicit_H ); -/*int bIsMetalSalt( inp_ATOM *at, int i ); - moved to strutil,h */ -int DisconnectMetalSalt( inp_ATOM *at, int i ); -int bIsMetalToDisconnect(inp_ATOM *at, int i, int bCheckMetalValence); - -int get_iat_number( int el_number, const int el_num[], int el_num_len ); -int tot_unsat( int unsat[] ); -int max_unsat( int unsat[] ); - -double dist3D( inp_ATOM *at1, inp_ATOM *at2 ); -double dist2D( inp_ATOM *at1, inp_ATOM *at2 ); -double dist_from_segm( double x, double y, double x1, double y1, double x2, double y2); -int segments_intersect( double x11, double y11, double x12, double y12, /* segment #1 */ - double x21, double y21, double x22, double y22 ); -double GetMinDistDistribution( inp_ATOM *at, int num_at, int iat, int iat_H, - int bInAllComponents, double min_dist[], int num_segm ); - -int nFindOneOM(inp_ATOM *at, int at_no, int ord_OM[], int num_OM); -int the_only_doublet_neigh(inp_ATOM *at, int i1, int *ineigh1, int *ineigh2); - - -#ifndef NUMH -#define NUM_ISO_H(AT,N) (AT[N].num_iso_H[0]+AT[N].num_iso_H[1]+AT[N].num_iso_H[2]) -#define NUMH(AT,N) (AT[N].num_H+NUM_ISO_H(AT,N)) -#endif - -/************************************************************************/ -int the_only_doublet_neigh(inp_ATOM *at, int i1, int *ineigh1, int *ineigh2) -{ - int i, neigh1, num_rad1=0, num_rad2=0; - inp_ATOM *a = at+i1, *b; - if ( RADICAL_DOUBLET != a->radical ) - return -1; - for ( i = 0; i < a->valence; i ++ ) { - b = at + (neigh1 = (int)a->neighbor[i]); - if ( RADICAL_DOUBLET == b->radical ) { - num_rad1 ++; - *ineigh1 = i; - } - } - if ( 1 == num_rad1 ) { - a = at + (neigh1 = (int)a->neighbor[*ineigh1]); - for ( i = 0; i < a->valence; i ++ ) { - b = at +(int)a->neighbor[i]; - if ( RADICAL_DOUBLET == b->radical ) { - num_rad2 ++; - *ineigh2 = i; - } - } - if ( 1 == num_rad2 ) { - return neigh1; - } - } - return -1; -} - -/************************************************************************/ -int fix_odd_things( int num_atoms, inp_ATOM *at, int bFixBug, int bFixNonUniformDraw ) -{ /* 0 1 2 3 4 5 6 7 8 9 */ - static const char el[] = "N;P;As;Sb;O;S;Se;Te;"; /* 8 elements + C, Si */ - static U_CHAR en[10]; /* same number: 8 elements */ - static int ne=0, ne2; /* will be 8 and 10 */ - static int el_number_P; - static int el_number_H; - static int el_number_C; - static int el_number_O; - static int el_number_Si; - -#define FIRST_NEIGHB2 4 -#define FIRST_CENTER2 5 -#define NUM_CENTERS_N 4 - - int i1, i2, k1, k2, c, num_changes = 0; - char elname[ATOM_EL_LEN]; - - - /* constants for element numbers */ - enum elems { dNone, dCl=17,dBr=35,dI=53,dAt=85,dO=8,dS=16,dSe=34,dTe=52, dP=15, dC=6, dN=7 } ; - - - - - if (bFixNonUniformDraw) - { - - - /* Correct non-uniformly drawn oxoanions and amidinium cations. */ - { - - /* For central halogen, apply the following correftion rules: - - O O(-) - || | - O=Hal(-)=O ===> O=Hal=O - || || - O O - - (perchlorate, etc.) - - - O O(-) - || | - Hal(-)=O ===> Hal=O - || || - O O - - (chlorate, etc.) - - O O(-) - || | - Hal(-)=O ===> Hal=O - - - (chlorite, etc.) - - Hal(-)=O ===> Hal-O(-) - - - (hypochlorite, etc.) - - - - For halcogenes (S, Se, Te) - Y Y(-) - || | - RnX(-) ===> RnX - - if: - 1) (X = S, Y = O) || (X = Se, Y = S, O) || (X = Te, Y = O, S, Se) - 2) valence of X exceeds 6, in initially drawn form - - - So the following is corrected: - - O O(-) - || | - O=S(-)-R ===> O=S-R - || || - O O - - or - - O O- - || | - F5Te(-) ===> F5Te - - but the following remains unchanged: - - - O - || - O=S(-)-R - - - The central atom (of IUPAC Group 16-17) is shown as negative but it contains double bond(s) - to terminal atom of greater electronegativity (of Group 16). - The central atom is halcogen (S,Se,Te) in highest oxidation state or halogen. - - Fix: - move negative charge to terminal atom and change double bond to a single one. - - Eligible central atom Eligible terminal atom at double bond's end - Cl O - Br O - I O - [At S,Se,Te] - S O - Se O,S - Te O, S, Se - - Comments: - 1. Central atoms of Groups 13-15 are not considered. - 2. Pauling electronegativities are: - F(3.98) > O(3.44) > Cl (3.16) > N (3.04) > Br(2.96) > I(2.66) > S(2.58) > Se(2.55) > At (2.2) > Te(2.1) - - - */ - - - static U_CHAR allowed_elnums_center_halogen[] = {dCl, dBr, dI, dAt} ; - static U_CHAR allowed_elnums_center_halcogen[] = {dS, dSe, dTe} ; - - int en_center; - int i, j, k; - - for (i=0; i 0? at[i].iso_atw_diff-1 : at[i].iso_atw_diff; - continue; - } - /* From same-element candidates, select one with less isotopic mass (arbitrary choice). */ - else if (en_term==min_en) - { - iso = at[j].iso_atw_diff > 0? at[i].iso_atw_diff-1 : at[i].iso_atw_diff; - if ( iso =0) - { - at[i].charge = 0; - at[jj].charge = -1; - at[i].bond_type[kk] = BOND_TYPE_SINGLE; - at[jj].bond_type[0] = BOND_TYPE_SINGLE; - at[i].bond_stereo[kk] = at[jj].bond_stereo[0] = 0; - at[i].chem_bonds_valence--; - at[jj].chem_bonds_valence--; - num_changes ++; - } - } - - } /* end of search for candidate centers. */ - - } /* end of correcting oxoanions */ - - - - - /* Correct non-uniformly drawn amidinium cations. */ - { - - /* Amidines include carboxamidines RC(=NR)NR2, - sulfinamidines RS(=NR)NR2 and phosphinamidines, R2P(=NR)NR2. - - - NR NR - | | - R"-Y-NHR' ===> R"-Y=N(+)HR' - (+) - - - Y = C, S, P - - - Fix: - move positive charge to nitrogen and change single bond to a double one. - - - Comment: - Fix is applied only if at least one of R's at N is hydrogen - (otherwise we have just a '+' delocalization which is already recognized). - - - */ - - static U_CHAR allowed_elnums_center[] = {dC, dS, dP} ; - int en_center; - int i, j, k, jj, kk; - int mismatch = 0, nuH=0, nuN = 0, nitrogens[MAXVAL]; - - for (i=0; i 3 ) || ( at[j].chem_bonds_valence > 3 ) ) - { - mismatch = 1; - break; - } - nuH+= NUMH(at,j); - nuN++; - if (jj<0) - { - jj = j; - kk = k; - } - } - } - - /* If OK, apply changes. */ - if (mismatch) continue; - if (nuN!=2) continue; - if (nuH<1) continue; - if (jj>=0) - { - at[i].charge = 0; - at[jj].charge = 1; - at[i].bond_type[kk] = BOND_TYPE_DOUBLE; - for ( k1 = 0; k1 < at[jj].valence && i != at[jj].neighbor[k1]; k1 ++ ) - ; - at[jj].bond_type[k1] = BOND_TYPE_DOUBLE; - at[i].chem_bonds_valence++; - at[jj].chem_bonds_valence++; - /* NB: do nothing with wedge stereo bonds (retain wedge) */ - num_changes ++; - } - - } /* end of search for candidate centers. */ - - } /* end of correcting amidiniums */ - - - - } /*( if (bFixNonUniformDraw) */ - - - - - if ( !ne ) - { - /* one time initialization */ - const char *b, *e; - int len; - for ( b = el; e = strchr( b, ';'); b = e+1 ) - { - len = e-b; - memcpy( elname, b, len ); - elname[len] = '\0'; - en[ne++] = get_periodic_table_number( elname ); - } - ne2 = ne; - el_number_P = get_periodic_table_number( "P" ); - el_number_H = get_periodic_table_number( "H" ); - el_number_O = get_periodic_table_number( "O" ); - en[ne2++] = el_number_C = get_periodic_table_number( "C" ); - en[ne2++] = el_number_Si = get_periodic_table_number( "Si" ); - } - - /* H(-)-X -> H-X(-); H(+)-X -> H-X(+) */ - for ( i1 = 0; i1 < num_atoms; i1 ++ ) - { - if ( 1 == at[i1].valence && - 1 == abs(at[i1].charge) && - (0 == at[i1].radical || RADICAL_SINGLET == at[i1].radical) && - BOND_TYPE_SINGLE == at[i1].bond_type[0] && - el_number_H == at[i1].el_number && - el_number_H != at[i2=(int)at[i1].neighbor[0]].el_number && - !NUMH(at,i1) && - !NUMH(at,i2) - ) - { - at[i2].charge += at[i1].charge; - at[i1].charge = 0; - } - } - - /* replace XHm(-)--Y==XHn(+) with XHm==Y--XHn, (n>=0 ,m>=0, X=N,P,As,Sb,O,S,Se,Te) */ - for ( i1 = 0; i1 < num_atoms; i1 ++ ) - { - if ( 1 != at[i1].charge || - at[i1].radical && RADICAL_SINGLET != at[i1].radical || - at[i1].chem_bonds_valence == at[i1].valence || - !memchr(en, at[i1].el_number, ne) || - get_el_valence( at[i1].el_number, at[i1].charge, 0 ) != at[i1].chem_bonds_valence+NUMH(at,i1) ) - { - continue; - } - - /* found a candidate at[i1] for X in XHn(+) */ - if ( 1 == at[i1].valence && - BOND_TYPE_DOUBLE == at[i1].bond_type[0] ) - { - c = (int)at[i1].neighbor[0]; - for ( k2 = 0; k2 < at[c].valence; k2 ++ ) - { - i2 = at[c].neighbor[k2]; - if ( 1 == at[i2].valence && - -1 == at[i2].charge && - at[i2].el_number == at[i1].el_number && /* exact match */ - (0 == at[i2].radical || RADICAL_SINGLET == at[i2].radical) && - BOND_TYPE_SINGLE == at[i2].bond_type[0] && - /*memchr(en, at[i2].el_number, ne) &&*/ - get_el_valence( at[i2].el_number, at[i2].charge, 0 ) == at[i2].chem_bonds_valence+NUMH(at,i2) ) { - /* found both X(-) and X(+); change bonds and remove charges */ - for ( k1 = 0; k1 < at[c].valence && i1 != at[c].neighbor[k1]; k1 ++ ) - ; - at[i1].charge = at[i2].charge = 0; - at[i1].bond_type[0] = at[c].bond_type[k1] = BOND_TYPE_SINGLE; - at[i1].chem_bonds_valence --; - at[i2].bond_type[0] = at[c].bond_type[k2] = BOND_TYPE_DOUBLE; - at[i2].chem_bonds_valence ++; - num_changes ++; - break; - } - } - } - else - { - /* explicit H case: detect H-neighbors and Y */ - int ineigh, neigh, i1_c, i2_c, num_H_i1, num_H_i2; - for ( ineigh = 0, num_H_i1 = 0, i1_c = -1; ineigh < at[i1].valence; ineigh ++ ) - { - neigh = at[i1].neighbor[ineigh]; - if ( at[neigh].el_number == el_number_H ) - { - if ( at[neigh].chem_bonds_valence == 1 && - (0 == at[neigh].radical || RADICAL_SINGLET == at[neigh].radical) ) - { - num_H_i1 ++; /* found H-neighbor */ - } - else - { - break; /* wrong neighbor */ - } - } - else if ( at[i1].bond_type[ineigh] == BOND_TYPE_DOUBLE ) - { - /* found a candidate for Y; bond must be double */ - i1_c = ineigh; - c = neigh; - } - } - if ( i1_c < 0 || num_H_i1 + 1 != at[i1].valence ) - { - continue; - } - for ( k2 = 0; k2 < at[c].valence; k2 ++ ) - { - i2 = at[c].neighbor[k2]; - if (-1 == at[i2].charge && - at[i2].el_number == at[i1].el_number && /* exact match */ - (0 == at[i2].radical || RADICAL_SINGLET == at[i2].radical) && - get_el_valence( at[i2].el_number, at[i2].charge, 0 ) == at[i2].chem_bonds_valence+NUMH(at,i2) ) - { - for ( ineigh = 0, num_H_i2 = 0, i2_c = -1; ineigh < at[i2].valence; ineigh ++ ) - { - neigh = at[i2].neighbor[ineigh]; - if ( at[neigh].el_number == el_number_H ) - { - if ( at[neigh].chem_bonds_valence == 1 && - (0 == at[neigh].radical || RADICAL_SINGLET == at[neigh].radical) ) - { - num_H_i2 ++; /* found H-neighbor */ - } - else - { - break; /* wrong neighbor */ - } - } - else - if ( c == neigh && at[i2].bond_type[ineigh] == BOND_TYPE_SINGLE ) - { - i2_c = ineigh; /* position of Y neighbor; bond must be single */ - } - else - { - break; - } - } - if ( num_H_i2 + (i2_c >= 0) != at[i2].valence ) - { - continue; - } - /* found both X(-) and X(+); change bonds and remove charges */ - for ( k1 = 0; k1 < at[c].valence && i1 != at[c].neighbor[k1]; k1 ++ ) - ; - at[i1].charge = at[i2].charge = 0; - at[i1].bond_type[i1_c] = at[c].bond_type[k1] = BOND_TYPE_SINGLE; - at[i1].chem_bonds_valence --; - at[i2].bond_type[i2_c] = at[c].bond_type[k2] = BOND_TYPE_DOUBLE; - at[i2].chem_bonds_valence ++; - num_changes ++; - break; - } - } - } - } - - /* Replace - - X- X X=O,S,Se,Te -- terminal atoms (NEIGHB2) - \ | \ || - >Y++ with >Y Y=S,Se,Te -- central cation (CENTER2) - / | / || - X- X Y valence=4, original Y bond valence = 4 - - - --- the following case of P is processed separately in remove_ion_pairs() - --- therefire, it has been disabled here, see #ifndef FIX_P_IV_Plus_O_Minus -- 2010-03-17 DT - - X- X X=O,S,Se,Te -- terminal atoms (NEIGHB2) - \ | \ || - >P+ with >P - / | / | - X- X- Y valence=4, original Y bond valence = 4 - - */ - - for ( i1 = 0; i1 < num_atoms; i1 ++ ) - { - if ( 1 == at[i1].valence && - -1 == at[i1].charge && - (0 == at[i1].radical || RADICAL_SINGLET == at[i1].radical) && - !NUMH(at,i1) && - BOND_TYPE_SINGLE == at[i1].bond_type[0] && - memchr( en+FIRST_NEIGHB2, at[i1].el_number, ne-FIRST_NEIGHB2 ) ) - { - int charge, i; - /* found a candidate for X */ - c = (int)at[i1].neighbor[0]; /* candidate for Y */ - if ( ((charge=2) == at[c].charge && memchr( en+FIRST_CENTER2, at[c].el_number, ne-FIRST_CENTER2) -#ifndef FIX_P_IV_Plus_O_Minus - || (charge=1) == at[c].charge && el_number_P==at[c].el_number -#endif - ) && - 4 == at[c].valence && - (0 == at[c].radical || RADICAL_SINGLET == at[c].radical ) && - at[c].valence == at[c].chem_bonds_valence && - !NUMH(at,c) ) - { - ; /* accept */ - } - else - { - continue; /* ignore at[i1] */ - } - for ( k2 = 0; k2 < at[c].valence; k2 ++ ) - { - i2 = at[c].neighbor[k2]; - if ( i2 == i1 ) - { - continue; - } - if ( 1 == at[i2].valence && - -1 == at[i2].charge && - memchr( en+FIRST_NEIGHB2, at[i2].el_number, ne-FIRST_NEIGHB2 ) && - /*at[i2].el_number == at[i1].el_number &&*/ /* exact match */ - (0 == at[i2].radical || RADICAL_SINGLET == at[i2].radical) && - !NUMH(at,i2) && - BOND_TYPE_SINGLE == at[i2].bond_type[0] ) - { - /* found both X(-) and X(-); change bonds and remove charges */ - for ( k1 = 0; k1 < at[c].valence && i1 != at[c].neighbor[k1]; k1 ++ ) - ; - for ( i = 0; i < charge; i ++ ) - { - /* in case of P it does not matter which X atom is neutralized - because of tautomerism. However, neutral central atom is important - for the neutralization of the components */ - switch ( i ) - { - case 0: - at[i1].charge ++; /* = 0; changed 2010-03-17 DT*/ - at[i1].bond_type[0] = at[c].bond_type[k1] = BOND_TYPE_DOUBLE; - at[i1].bond_stereo[0] = at[c].bond_stereo[k1] = 0; - at[i1].chem_bonds_valence ++; - at[c].chem_bonds_valence ++; - if ( bFixBug ) at[c].charge --; /* added 2010-03-17 DT*/ - num_changes ++; - break; - case 1: - at[i2].charge ++; /*= 0; changed 2010-03-17 DT*/ - at[i2].bond_type[0] = at[c].bond_type[k2] = BOND_TYPE_DOUBLE; - at[i2].bond_stereo[0] = at[c].bond_stereo[k2] = 0; - at[i2].chem_bonds_valence ++; - at[c].chem_bonds_valence ++; - if ( bFixBug ) at[c].charge --; /* added 2010-03-17 DT */ - num_changes ++; - break; - } - } -/* -- removed -- 2010-03-17 DT -#if ( FIX_ODD_THINGS_REM_Plus_BUG == 1 ) - at[c].charge -= charge; -#else - if ( bFixBug ) - { - at[c].charge -= charge; - } -#endif -*/ - break; - } - } - } - } - - - - - /* A(doublet)-B(doublet) -> A=B (A and B have no other doublet neighbors) */ - /* A(doublet)=B(doublet) -> A#B (A and B have no other doublet neighbors) */ - for( i1 = 0; i1 < num_atoms; i1 ++ ) - { - if ( RADICAL_DOUBLET == at[i1].radical && - 0 <= (i2=the_only_doublet_neigh(at, i1, &k1, &k2)) ) - { - if ( at[i1].bond_type[k1] <= BOND_TYPE_DOUBLE ) - { - at[i1].bond_type[k1] ++; - at[i1].chem_bonds_valence ++; - at[i2].bond_type[k2] ++; - at[i2].chem_bonds_valence ++; - at[i1].radical = 0; - at[i2].radical = 0; - } - } - } - -#if ( REMOVE_ION_PAIRS_EARLY == 1 ) - num_changes += remove_ion_pairs( num_atoms, at ); -#endif - - - - - return num_changes; -} - - - - - - - - -/************************************************************************/ -int post_fix_odd_things( int num_atoms, inp_ATOM *at ) -{ - int num_changes = 0; - /* currently does nothing */ - return num_changes; -} - - - -/************************************************************************/ -int nFindOneOM(inp_ATOM *at, int at_no, int ord_OM[], int num_OM) -{ - int i, n_OM, n_OM_best, best_value, cur_value, diff; - int num_best; - - if ( 1 == num_OM ) { - return ord_OM[0]; - } - if ( 1 > num_OM ) { - return -1; - } - - /* select neighbors with min. number of bonds */ - num_best = 1; - n_OM = (int)at[at_no].neighbor[ord_OM[0]]; - best_value = (int)at[n_OM].valence; - /* compare number of bonds; move indexes of the best neighbors to the first elements of ord_OM[] */ - for ( i = 1; i < num_OM; i ++ ) { - n_OM = at[at_no].neighbor[ord_OM[i]]; - cur_value = (int)at[n_OM].valence; - diff = cur_value - best_value; - if ( diff < 0 ) { - n_OM_best = n_OM; - best_value = cur_value; - ord_OM[0] = ord_OM[i]; - num_best = 1; - } else - if ( diff == 0 ) { /* was '=', pointed by WDI */ - ord_OM[num_best ++] = ord_OM[i]; - } - } - num_OM = num_best; - if ( 1 == num_OM ) { - return ord_OM[0]; - } - /* select neighbors with min. periodic numbers */ - num_best = 1; - n_OM = (int)at[at_no].neighbor[ord_OM[0]]; - best_value = (int)at[n_OM].el_number; - /* compare periodic numbers; move indexes of the best neighbors to the first elements of ord_OM[] */ - for ( i = 1; i < num_OM; i ++ ) { - n_OM = at[at_no].neighbor[ord_OM[i]]; - cur_value = (int)at[n_OM].el_number; - diff = cur_value - best_value; - if ( diff < 0 ) { - n_OM_best = n_OM; - best_value = cur_value; - ord_OM[0] = ord_OM[i]; - num_best = 1; - } else - if ( diff == 0 ) { /* was '=', pointed by WDI */ - ord_OM[num_best ++] = ord_OM[i]; - } - } - num_OM = num_best; - if ( 1 == num_OM ) { - return ord_OM[0]; - } - /* if neighbors are not terminal atoms then reject */ - if ( 1 < at[n_OM].valence ) { - return -1; - } - /* if neighbors are terminal atoms then the one without isotope or with lightest isotope */ - num_best = 1; - n_OM = (int)at[at_no].neighbor[ord_OM[0]]; - best_value = (int)at[n_OM].iso_atw_diff; - /* compare periodic numbers; move indexes of the best neighbors to the first elements of ord_OM[] */ - for ( i = 1; i < num_OM; i ++ ) { - n_OM = at[at_no].neighbor[ord_OM[i]]; - cur_value = (int)at[n_OM].el_number; - diff = cur_value - best_value; - if ( (!cur_value && best_value) || diff < 0 ) { - n_OM_best = n_OM; - best_value = cur_value; - ord_OM[0] = ord_OM[i]; - num_best = 1; - } else - if ( diff == 0 ) { /* was '=', pointed by WDI */ - ord_OM[num_best ++] = ord_OM[i]; - } - } - num_OM = num_best; - if ( 1 == num_OM ) { - return ord_OM[0]; - } - /* return any */ - return ord_OM[0]; -} - - - -/************************************************************************/ -/* the bonds are fixed in fix_special_bonds() */ -int remove_ion_pairs( int num_atoms, inp_ATOM *at ) -{ - int num_changes = 0; - - /* 0 1 2 3 4 5 6 7 8 9 8 9 */ -#if ( FIX_REM_ION_PAIRS_Si_BUG == 1 ) - static const char el[] = "N;P;As;Sb;O;S;Se;Te;C;Si;"; /* 8 elements + C, Si */ -#else - static const char el[] = "N;P;As;Sb;O;S;Se;Te;C;Si"; /* 8 elements + C, Si */ -#endif - static char en[12]; /* same number: 8 elements */ - static int ne=0; /* will be 8 and 10 */ - -#define ELEM_N_FST 0 -#define ELEM_N_LEN 4 -#define ELEM_O_FST 4 -#define ELEM_O_LEN 4 -#define ELEM_C_FST 8 -#define ELEM_C_LEN 2 - -#define MAX_NEIGH 6 - - int i, n, n2, i1, i2, i3, i4, type, chrg; - int num_C_II=0, num_C_plus=0, num_C_minus=0, num_N_plus=0, num_N_minus=0, num_O_plus=0, num_O_minus=0, num_All; -#ifdef FIX_P_IV_Plus_O_Minus - int num_P_IV_plus=0; /* added 2010-03-17 DT */ -#endif - inp_ATOM *a; - char elname[ATOM_EL_LEN], *p; - if ( !ne ) { /* one time initialization */ - const char *b, *e; - int len; - for ( b = el; e = strchr( b, ';'); b = e+1 ) { - len = e-b; - memcpy( elname, b, len ); - elname[len] = '\0'; - en[ne++] = get_periodic_table_number( elname ); - } - en[ne] = '\0'; - } - - /****** count candidates ********/ - for ( i = 0, a = at; i < num_atoms; i ++, a++ ) { - if ( 1 == (chrg=a->charge) || -1 == chrg ) { - if ( p = (char*)memchr( en, a->el_number, ne) ) { - n = p - en; - if ( n >= ELEM_C_FST ) { - if ( chrg > 0 ) - num_C_plus ++; - else - num_C_minus ++; - } else - if ( n >= ELEM_O_FST ) { - if ( chrg > 0 ) - num_O_plus ++; - else - num_O_minus ++; - } else { - if ( chrg > 0 ) - num_N_plus ++; - else - num_N_minus ++; -#ifdef FIX_P_IV_Plus_O_Minus - num_P_IV_plus += n > 0 && chrg == 1 && a->valence == 4 && a->chem_bonds_valence == 4; /* added 2010-03-17 DT */ -#endif - } - } - } else - if ( !chrg && a->chem_bonds_valence + NUMH(a, 0) == 2 && - get_el_valence( a->el_number, 0, 0 ) == 4 && - NULL != memchr( en+ELEM_C_FST, a->el_number, ELEM_C_LEN) ) { - num_C_II ++; - } - } - num_All = num_C_II + num_C_plus + num_C_minus + num_N_plus + num_N_minus + num_O_plus + num_O_minus; - /* do not add num_P_IV_plus ! -- 2010-03-17 DT */ - if ( !num_All ) { - return 0; - } - - /**************************************************************************/ - /*************************** Terminal ion pairs ***************************/ - /**************************************************************************/ - - /*------------------------------------------------------------------------- - Pair type 1 N=N,P,As,Sb; O=O,S,Se,Te - =========== - - X X if X is another -O(-) then neutralize O(-) - | | that has the smallest periodic table number - O=N(+)-O(-) => O=N=O - i n - --------------------------------------------------------------------------*/ - for ( type = 1; type <= 18; type ++ ) { - if ( (!type || 1 == type) ) { - for ( i = 0; i < num_atoms && 0 < num_N_plus && 0 < num_O_minus; i ++ ) { - if ( 1 == at[i].charge && 3 == nNoMetalNumBonds(at, i) && - 4 == nNoMetalBondsValence(at, i) && - NULL != memchr( en+ELEM_N_FST, at[i].el_number, ELEM_N_LEN) ) { - int num_OM = 0, ord_OM[3]; /* -O(-) */ - int num_O = 0; /* =O */ - int num_O_other = 0; - for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { - n = at[i].neighbor[i1]; - if ( 1 == nNoMetalNumBonds(at, n) && 0 == num_of_H( at, n ) && - NULL != (p = (char*)memchr( en+ELEM_O_FST, at[n].el_number, ELEM_O_LEN)) ) { - if ( BOND_TYPE_SINGLE == at[i].bond_type[i1] && - -1 == at[n].charge ) { - ord_OM[num_OM ++] = i1; - } else - if ( BOND_TYPE_DOUBLE == at[n].bond_type[0] && - 0 == at[n].charge ) { - num_O ++; - } else { - num_O_other ++; - } - } - } - if ( num_OM > 0 && num_O > 0 && !num_O_other && - 0 <= (i1=nFindOneOM(at, i, ord_OM, num_OM)) ) { - /* remove charges and increase bond order */ - n = at[i].neighbor[i1]; - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[i].charge --; - at[n].charge ++; - at[i].radical = 0; - at[n].radical = 0; - num_changes ++; - num_N_plus --; - num_O_minus --; - num_All -= 2; - } - } - } -#ifdef FIX_P_IV_Plus_O_Minus - /*------------------------------------------------------------------------- - Pair type 1a P=P,As,Sb; O=O,S,Se,Te -- added 2010-03-17 - ============= - - X X if X, Y, or Z is another -O(-) then neutralize O(-) - | | that has the smallest periodic table number - Y-P(+)-O(-) => Y-P=O - |i n | - Z Z - - --------------------------------------------------------------------------*/ - for ( i = 0; i < num_atoms && 0 < num_P_IV_plus /*&& 0 < num_N_plus*/ && 0 < num_O_minus; i ++ ) { - if ( 1 == at[i].charge && 4 == nNoMetalNumBonds(at, i) && - 4 == nNoMetalBondsValence(at, i) && - NULL != memchr( en+ELEM_N_FST+1, at[i].el_number, ELEM_N_LEN-1) ) { - int num_OM = 0, ord_OM[4]; /* -O(-) */ - /*int num_O = 0;*/ /* =O */ - int num_O_other = 0; - for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { - n = at[i].neighbor[i1]; - if ( 1 == nNoMetalNumBonds(at, n) && 0 == num_of_H( at, n ) && - NULL != (p = (char*)memchr( en+ELEM_O_FST, at[n].el_number, ELEM_O_LEN)) ) { - if ( BOND_TYPE_SINGLE == at[i].bond_type[i1] && - -1 == at[n].charge ) { - ord_OM[num_OM ++] = i1; - /* - } - if ( BOND_TYPE_DOUBLE == at[n].bond_type[0] && - 0 == at[n].charge ) { - num_O ++; - */ - } else { - num_O_other ++; - } - } - } - if ( num_OM > 0 /*&& num_O > 0 && !num_O_other*/ && - 0 <= (i1=nFindOneOM(at, i, ord_OM, num_OM)) ) { - /* remove charges and increase bond order */ - n = at[i].neighbor[i1]; - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[i].charge --; - at[n].charge ++; - at[i].radical = 0; - at[n].radical = 0; - num_changes ++; - num_N_plus --; - num_O_minus --; - num_P_IV_plus --; - num_All -= 2; - } - } - } -#endif /* FIX_P_IV_Plus_O_Minus */ - } - /*------------------------------------------------------------------------- - Terminal pair types: 2,3,4,5,6,7,8,9 N=N,P,As,Sb; O=O,S,Se,Te; C=C,Si - ==================================== - type # - 2 2: O=N-C(II)- => O=N#C- N=N,P,As,Sb; O=O,S,Se,Te; C=C,Si - 3 9: O=O(+)-C(-)(III) => O=O=C(IV) - 4 3: O(-)-N(+)(IV) => O=N(V) (input structure has at least 1 double bond) - 5 4: O(-)-O(+)(III) => O=O(IV) - 6 8: O(-)-O-C(+)(III) => O=O=C(IV) - 7 5: N(-)=N(+)(IV) => N#N(V) allow terminal H on N(-) - 8 6: N(-)=O(+)(III) => N#O- - 9 7: N(-)=C(+)(III) => N#C- - --------------------------------------------------------------------------*/ - if ( !type || 2 <= type && type <= 9 ) { - for ( i = 0; i < num_atoms && 0 < num_All; i ++ ) { - if ( 0 == at[i].charge && 1 == nNoMetalNumBonds(at, i) && 2 == nNoMetalBondsValence(at, i) && - 0 == num_of_H( at, i ) && - NULL != memchr( en+ELEM_O_FST, at[i].el_number, ELEM_O_LEN) && - 0 <= ( i1 = nNoMetalNeighIndex(at, i)) && - at[i].bond_type[i1] <= BOND_TYPE_TRIPLE ) { - /* terminal O= */ - n = at[i].neighbor[i1]; - if ( (!type || type == 2) && 0 < num_C_II ) { /* avoid alternating bonds */ - if ( 0 == at[n].charge && - 2 == nNoMetalNumBonds(at, n) && 3 == nNoMetalBondsValence(at, n) && - 0 == num_of_H( at, n ) && - NULL != memchr( en+ELEM_N_FST, at[n].el_number, ELEM_N_LEN) && - 0 <= (i2 = nNoMetalOtherNeighIndex( at, n, i ) ) && - at[n].bond_type[i2] <= BOND_TYPE_TRIPLE ) { - /* i2 = index of opposite to at[i] neighbor of at[n] */ - /*i2 = (at[n].neighbor[0] == i);*/ - n2 = at[n].neighbor[i2]; - if ( 0 == at[n2].charge && - 2 == at[n2].valence && 2 == at[n2].chem_bonds_valence && - 0 == num_of_H( at, n2 ) && - NULL != memchr( en+ELEM_C_FST, at[n2].el_number, ELEM_C_LEN) ) { - /* i n n2 */ - /* found O=N-C(II)- */ - /* convert O=N-C(II)- => O=N#C- */ - i3 = (at[n2].neighbor[0] != n); /* index of at[n] neighbor of n2 */ - at[ n].chem_bonds_valence = 5; /* N */ - at[n2].chem_bonds_valence = 4; /* C */ - at[ n].bond_type[i2] = BOND_TYPE_TRIPLE; - at[n2].bond_type[i3] = BOND_TYPE_TRIPLE; - at[n2].radical = 0; - num_changes ++; - num_C_II --; - num_All --; - continue; - } - } - } - if ( (!type || type == 3) && 0 < num_O_plus && 0 < num_C_minus ) { - if ( 1 == at[n].charge && 2 == nNoMetalNumBonds(at, n) && 3 == nNoMetalBondsValence(at, n) && - 0 == num_of_H( at, n ) && - NULL != memchr( en+ELEM_O_FST, at[n].el_number, ELEM_O_LEN) && - 0 <= (i2 = nNoMetalOtherNeighIndex( at, n, i ) ) && - at[n].bond_type[i2] <= BOND_TYPE_TRIPLE ) { - /* found O=O(+)- */ - /* i2 = index of opposite to at[i] neighbor of at[n] */ - /*i2 = (at[n].neighbor[0] == i);*/ - n2 = at[n].neighbor[i2]; - if ( -1 == at[n2].charge && 3 >= nNoMetalNumBonds(at, n2) && 3 == nNoMetalBondsValence(at, n2)+NUMH(at,n2) && - NULL != memchr( en+ELEM_C_FST, at[n2].el_number, ELEM_C_LEN) ) { - /* i n n2 */ - /* found found O=O(+)-C(-)(III) */ - /* convert O=O(+)-C(-)(III) => O=O=C(IV) */ - i3 = (at[n2].neighbor[0] != n); /* index of at[n] neighbor of n2 */ - at[ n].charge --; - at[n2].charge ++; - at[ n].chem_bonds_valence += 1; /* =O- => =O= */ - at[n2].chem_bonds_valence += 1; /* -C => =C */ - at[ n].bond_type[i2] = BOND_TYPE_DOUBLE; - at[n2].bond_type[i3] = BOND_TYPE_DOUBLE; - num_changes ++; - num_O_plus --; - num_C_minus --; - num_All -= 2; - continue; - } - } - } - } else - if ( -1 == at[i].charge && - 0 < num_O_minus + num_N_minus && - 0 < num_N_plus + num_O_plus + num_C_plus && - 1 == nNoMetalNumBonds(at, i) && 1 == nNoMetalBondsValence(at, i) && - 0 == num_of_H( at, i ) && - NULL != memchr( en+ELEM_O_FST, at[i].el_number, ELEM_O_LEN) && - 0 <= (i1 = nNoMetalNeighIndex( at, i )) && - at[i].bond_type[i1] <= BOND_TYPE_TRIPLE ) { - - /* terminal O(-)- */ - - n = at[i].neighbor[i1]; - - if ( (!type || type == 4) && 0 < num_O_minus && 0 < num_N_plus && /* O(-)-N(+)(IV) */ - 1 == at[n].charge && 3 >= nNoMetalNumBonds(at, n) && 4 == nNoMetalBondsValence(at, n) && - 0 == num_of_H( at, n ) && - NULL != memchr( en+ELEM_N_FST, at[n].el_number, ELEM_N_LEN) /* except >O(+)- */ - ) { - /* found O(-)-N(+)(IV) */ - /* convert O(-)-N(+)(IV) => O=N(V) */ - - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; /* index of at[i] neighbor of at[n] */ - at[i].charge ++; - at[n].charge --; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - num_changes ++; - num_O_minus --; - num_N_plus --; - num_All -= 2; - continue; - } - - if ( (!type || type == 5) && 0 < num_O_minus && 0 < num_O_plus &&/* O(-)-O(+)(III) */ - 1 == at[n].charge && 3 >= nNoMetalNumBonds(at, n) && 3 == nNoMetalBondsValence(at, n) && - 0 == num_of_H( at, n ) && - NULL != memchr( en+ELEM_O_FST, at[n].el_number, ELEM_O_LEN) /* except >O(+)- */ - ) { - /* found O(+)(III) */ - /* convert O(-)-O(+)(III) => O=O(IV) */ - - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; /* index of at[i] neighbor of at[n] */ - at[i].charge ++; - at[n].charge --; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - num_changes ++; - num_O_minus --; - num_O_plus --; - num_All -= 2; - continue; - } - /* i n n2 */ - if ( (!type || type == 6) && /* O(-)-O-C(+)(III) */ - 0 < num_O_minus && 0 < num_C_plus && - 0 == at[n].charge && 2 == nNoMetalNumBonds(at, n) && 2 == nNoMetalBondsValence(at, n) && - 0 == num_of_H( at, n ) && - NULL != memchr( en+ELEM_O_FST, at[n].el_number, ELEM_O_LEN) && - 0 <= (i2=nNoMetalOtherNeighIndex( at, n, i )) && - at[n].bond_type[i2] <= BOND_TYPE_TRIPLE ) { - /* found O(-)-O- */ - /* i2 = index of opposite to at[i] neighbor of at[n] */ - /*i2 = (at[n].neighbor[0] == i);*/ - n2 = at[n].neighbor[i2]; - if ( 1 == at[n2].charge && 3 >= nNoMetalNumBonds(at, n2) && - 3 == nNoMetalBondsValence(at, n2)+NUMH(at,n2) && - NULL != memchr( en+ELEM_C_FST, at[n2].el_number, ELEM_C_LEN) ) { - /* i n n2 */ - /* found O(-)-O-C(+)(III) */ - /* convert O(-)-O-C(+)(III) => O=O=C(IV) */ - /*i3 = (at[n2].neighbor[0] != n);*/ /* i3 = index of at[n] neighbor of at[n2] */ - i3 = is_in_the_list( at[n2].neighbor, (AT_NUMB)n, at[n2].valence ) - at[n2].neighbor; - /*i4 = index of at[i] in the adjacency list of at[n] */ - i4 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; - at[ i].charge ++; - at[n2].charge --; - at[ i].chem_bonds_valence += 1; /* O- => O= */ - at[ n].chem_bonds_valence += 2; /* -O- => =O= */ - at[n2].chem_bonds_valence += 1; /* -C => =C */ - at[ i].bond_type[i1] = BOND_TYPE_DOUBLE; - at[ n].bond_type[i4] = BOND_TYPE_DOUBLE; - at[ n].bond_type[i2] = BOND_TYPE_DOUBLE; - at[n2].bond_type[i3] = BOND_TYPE_DOUBLE; - num_changes ++; - num_O_minus --; - num_C_plus --; - num_All -= 2; - continue; - } - } - } else - if ( -1 == at[i].charge && 0 < num_N_minus && 0 < num_N_plus+num_O_plus+num_C_plus && - 1 == nNoMetalNumBonds(at, i) && 2 == nNoMetalBondsValence(at, i)+NUMH(at, i) && - /*0 == num_of_H( at, i ) &&*/ - NULL != memchr( en+ELEM_N_FST, at[i].el_number, ELEM_N_LEN) && - 0 <= (i1 = nNoMetalNeighIndex( at, i )) && - at[i].bond_type[i1] <= BOND_TYPE_TRIPLE ) { - /* terminal N(-)= */ - n = at[i].neighbor[i1 = 0]; - if ( (!type || type == 7) && 0 < num_N_plus && /* N(-)=N(+)(IV) */ - 1 == at[n].charge && 3 >= nNoMetalNumBonds(at, n) && 4 == nNoMetalBondsValence(at, n) && - 0 == num_of_H( at, n ) && - NULL != memchr( en+ELEM_N_FST, at[n].el_number, ELEM_N_LEN) - ) { - /* found N(-)-N(+)(IV) */ - /* convert N(-)=N(+)(IV) => N#N(V) */ - - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; /* index of at[i] neighbor of at[n] */ - at[i].charge ++; - at[n].charge --; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - num_changes ++; - num_N_minus --; - num_N_plus --; - num_All -= 2; - continue; - } - if ( (!type || type == 8) && 0 < num_O_plus && /* N(-)=O(+)(III) */ - 1 == at[n].charge && 2 == nNoMetalNumBonds(at, n) && 3 == nNoMetalBondsValence(at, n) && - 0 == num_of_H( at, n ) && - NULL != memchr( en+ELEM_O_FST, at[n].el_number, ELEM_O_LEN) - ) { - /* found N(-)-O(+)(III) */ - /* convert N(-)=O(+)(III) => N#O(IV)- */ - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; /* index of at[i] neighbor of at[n] */ - at[i].charge ++; - at[n].charge --; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - num_changes ++; - num_N_minus --; - num_O_plus --; - num_All -= 2; - continue; - } - if ( (!type || type == 9) && 0 < num_C_plus && /* N(-)=C(+)(III) */ - 1 == at[n].charge && 2 == at[n].valence && 3 == at[n].chem_bonds_valence && - 0 == num_of_H( at, n ) && - NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) - ) { - /* found N(-)=C(+)(III) */ - /* convert N(-)=C(+)(III) => N#C(IV)- */ - - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; /* index of at[i] neighbor of at[n] */ - at[i].charge ++; - at[n].charge --; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - num_changes ++; - num_N_minus --; - num_C_plus --; - num_All -= 2; - continue; - } - } - } - } - - /**************************************************************************/ - /*********************** NON-Terminal ion pairs ***************************/ - /**************************************************************************/ - /*------------------------------------------------------------------------- - Non-Terminal pair types: 10,11,12,13,14 N=N,P,As,Sb; O=O,S,Se,Te; C=C,Si - ======================================== - - 10: N(+)(IV)-C(-)(III) => N(V)=C(IV) (N has 3 or 2 bonds) - 11: N(+)(IV)=C(-)(III) => N(V)#C(IV) (N has 3 or 2 bonds) - 12: N(+)(IV)-N(-)(II) => N(V)=N(III) (allow terminal H on N(-)) - 13: -O(+)-C(-)(III) => -O=C- - 14: -O(+)=C(-)(III) => -O#C- - 15: O(+)(III)-N(-)(II) => O(IV)=N(III) (allow terminal H on N(-)) - --------------------------------------------------------------------------*/ - if ( !type || 10 <= type && type <= 15 ) { - for ( i = 0; i < num_atoms && 0 < num_All; i ++ ) { - if ( 1 == at[i].charge && - 0 < num_N_plus + num_O_plus && 0 < num_C_minus + num_N_minus && - 4 >= nNoMetalNumBonds(at, i) && 4 == nNoMetalBondsValence(at, i) && - 0 == num_of_H( at, i ) && - NULL != memchr( en+ELEM_N_FST, at[i].el_number, ELEM_N_LEN) ) { - /* found non-terminal N(+)(IV) */ - if ( (!type || 10 == type) && 0 < num_N_plus && 0 < num_C_minus ) { - int num_neigh = 0, pos_neigh = -1; - for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { - n = at[i].neighbor[i1]; - if ( -1 == at[n].charge && 3 >= at[n].valence && 3 == at[n].chem_bonds_valence+NUMH(at,n) && - /*0 == at[n].num_H &&*/ - at[i].bond_type[i1] == BOND_TYPE_SINGLE && - NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) { - /* found N(+)(IV)-C(-)(III); prepare conversion to N(V)=C(IV) */ - num_neigh ++; - pos_neigh = i1; - } - } - i1=pos_neigh; - if ( 1 == num_neigh && - at[i].bond_type[i1] <= BOND_TYPE_TRIPLE && - !has_other_ion_neigh( at, i, n=at[i].neighbor[i1], en, ne ) && - !has_other_ion_neigh( at, n, i, en, ne )) { - /*n = at[i].neighbor[i1=pos_neigh];*/ - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; - at[i].charge --; - at[n].charge ++; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - num_changes ++; - num_C_minus --; - num_N_plus --; - num_All -= 2; - continue; - } - } - if ( (!type || 11 == type) && 0 < num_N_plus && 0 < num_C_minus ) { - int num_neigh = 0, pos_neigh = -1; - for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { - n = at[i].neighbor[i1]; - if ( -1 == at[n].charge && 3 >= at[n].valence && 3 == at[n].chem_bonds_valence+NUMH(at,n) && - /*0 == at[n].num_H &&*/ - at[i].bond_type[i1] == BOND_TYPE_DOUBLE && - NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) { - /* found N(+)(IV)=C(-)(III); prepare conversion to N(V)#C(IV) */ - num_neigh ++; - pos_neigh = i1; - } - } - if ( 1 == num_neigh && - !has_other_ion_neigh( at, i, n=at[i].neighbor[i1=pos_neigh], en, ne ) && - !has_other_ion_neigh( at, n, i, en, ne )) { - /*n = at[i].neighbor[i1=pos_neigh];*/ - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; - at[i].charge --; - at[n].charge ++; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - num_changes ++; - num_C_minus --; - num_N_plus --; - num_All -= 2; - continue; - } - } - if ( !type || 12 == type && 0 < num_N_plus && 0 < num_N_minus ) { - int num_neigh = 0, pos_neigh = -1; - for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { - n = at[i].neighbor[i1]; - if ( -1 == at[n].charge && 2 >= nNoMetalNumBonds(at, n) && - 2 == nNoMetalBondsValence(at, n)+NUMH(at, n) && - /*0 == num_of_H( at, n ) &&*/ - at[i].bond_type[i1] == BOND_TYPE_SINGLE && - NULL != memchr( en+ELEM_N_FST, at[n].el_number, ELEM_N_LEN) ) { - /* found N(+)(IV)=N(-)(II); prepare conversion to N(V)#N(III) */ - num_neigh ++; - pos_neigh = i1; - } - } - if ( 1 == num_neigh && - !has_other_ion_neigh( at, i, n=at[i].neighbor[i1=pos_neigh], en, ne ) && - !has_other_ion_neigh( at, n, i, en, ne )) { - /*n = at[i].neighbor[i1=pos_neigh];*/ - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; - at[i].charge --; - at[n].charge ++; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - num_changes ++; - num_N_minus --; - num_N_plus --; - num_All -= 2; - continue; - } - } - } else - if ( 1 == at[i].charge && - 0 < num_O_plus && 0 < num_C_minus + num_N_minus && - 3 >= nNoMetalNumBonds(at, i) && 3 == nNoMetalBondsValence(at, i) && - 0 == num_of_H( at, i ) && - NULL != memchr( en+ELEM_O_FST, at[i].el_number, ELEM_O_LEN) ) { - /* found non-terminal O(+)(III) */ - if ( (!type || 13 == type) && 0 < num_C_minus ) { - int num_neigh = 0, pos_neigh = -1; - for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { - n = at[i].neighbor[i1]; - if ( -1 == at[n].charge && 3 >= at[n].valence && 3 == at[n].chem_bonds_valence+NUMH(at,n) && - /*0 == at[n].num_H &&*/ - at[i].bond_type[i1] == BOND_TYPE_SINGLE && - NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) { - /* found O(+)(III)-C(-)(II); prepare conversion to O(IV)=C(IV) */ - num_neigh ++; - pos_neigh = i1; - } - } - if ( 1 == num_neigh && - !has_other_ion_neigh( at, i, n=at[i].neighbor[i1=pos_neigh], en, ne ) && - !has_other_ion_neigh( at, n, i, en, ne )) { - /*n = at[i].neighbor[i1=pos_neigh];*/ - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; - at[i].charge --; - at[n].charge ++; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - num_changes ++; - num_C_minus --; - num_O_plus --; - num_All -= 2; - continue; - } - } - if ( (!type || 14 == type) && 0 < num_C_minus ) { - int num_neigh = 0, pos_neigh = -1; - for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { - n = at[i].neighbor[i1]; - if ( -1 == at[n].charge && 3 >= at[n].valence && 3 == at[n].chem_bonds_valence+NUMH(at,n) && - /*0 == at[n].num_H &&*/ - at[i].bond_type[i1] == BOND_TYPE_DOUBLE && - NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) { - /* found O(+)(III)=C(-)(III); prepare conversion to O(IV)#C(IV) */ - num_neigh ++; - pos_neigh = i1; - } - } - if ( 1 == num_neigh && - !has_other_ion_neigh( at, i, n=at[i].neighbor[i1=pos_neigh], en, ne ) && - !has_other_ion_neigh( at, n, i, en, ne )) { - /*n = at[i].neighbor[i1=pos_neigh];*/ - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; - at[i].charge --; - at[n].charge ++; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - num_changes ++; - num_C_minus --; - num_O_plus --; - num_All -= 2; - continue; - } - } - if ( (!type || 15 == type) && 0 < num_N_minus ) { - int num_neigh = 0, pos_neigh = -1; - for ( i1 = 0; i1 < at[i].valence; i1 ++ ) { - n = at[i].neighbor[i1]; - if ( -1 == at[n].charge && 2 >= nNoMetalNumBonds(at, n) && - 2 == nNoMetalBondsValence(at, n)+NUMH(at, n) && - /*0 == num_of_H( at, n ) &&*/ - at[i].bond_type[i1] == BOND_TYPE_SINGLE && - NULL != memchr( en+ELEM_N_FST, at[n].el_number, ELEM_N_LEN) ) { - /* found O(+)(III)=N(-)(II); prepare conversion to O(IV)#N(III) */ - num_neigh ++; - pos_neigh = i1; - } - } - if ( 1 == num_neigh && - !has_other_ion_neigh( at, i, n=at[i].neighbor[i1=pos_neigh], en, ne ) && - !has_other_ion_neigh( at, n, i, en, ne )) { - /*n = at[i].neighbor[i1=pos_neigh];*/ - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; - at[i].charge --; - at[n].charge ++; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - num_changes ++; - num_N_minus --; - num_O_plus --; - num_All -= 2; - continue; - } - } - } - } - } - /**************************************************************************/ - /*********************** NON-Terminal ion triples *************************/ - /**************************************************************************/ - /*------------------------------------------------------------------------- - Non-Terminal triple types: 16, 17, 18 N=N,P,As,Sb; O=O,S,Se,Te; C=C,Si - ======================================== - 16: C(+)(III)-O-N(-)(II) => C(IV)=O=N(III) (allow terminal H on N(-)) - - | | - 17: C(+)(III)-N-C(-)(III) => C(IV)=N=C(IV) - - 18: C(-)(III)-N=C(+)(III) => C(IV)=N#C(IV) (may have two or no charges) - C(IV)=N-C(II) => C(IV)=N#C(IV) - - */ - if ( (!type || 16 == type) && 0 < num_C_plus && 0 < num_N_minus ) { - int m[2], j[2], k; - for ( i = 0; i < num_atoms; i ++ ) { - if ( 0 == at[i].charge && 2 == nNoMetalNumBonds(at, i) && 2 == nNoMetalBondsValence(at, i) && - 0 == num_of_H( at, i ) && - 0 <= (j[0] = nNoMetalNeighIndex( at, i )) && - at[m[0]=at[i].neighbor[j[0]]].charge && - 0 <= (j[1] = nNoMetalOtherNeighIndex( at, i, m[0] )) && - 0 == at[m[0]].charge + at[m[1]=at[i].neighbor[j[1]]].charge && - 5 >= nNoMetalBondsValence(at, m[0]) + nNoMetalBondsValence(at, m[1]) && - /*5 >= at[m[0]].chem_bonds_valence + at[m[1]].chem_bonds_valence &&*/ - NULL != memchr( en+ELEM_O_FST, at[i].el_number, ELEM_O_LEN) ) { - /* found non-terminal A(+)-O-B(-); chem_bond_val of A+B <= 5 */ - int n_N=-1, n_C=-1, i_C=-1; - for ( k = 0; k < 2; k ++ ) { - n = m[k]; - if ( -1 == at[n].charge && 2 == nNoMetalNumBonds(at, n)+NUMH(at, n) && - /*0 == num_of_H( at, n ) &&*/ - NULL != memchr( en+ELEM_N_FST, at[n].el_number, ELEM_N_LEN) ) { - n_N = n; - } else - if ( 1 == at[n].charge && 3 == at[n].chem_bonds_valence+NUMH(at,n) && - NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) { - n_C = n; - i_C = k; - } - } - if ( n_C < 0 || n_N < 0 || - has_other_ion_in_sphere_2(at, n_C, n_N, en, ne ) || - has_other_ion_in_sphere_2(at, n_N, n_C, en, ne ) ) { - continue; - } - /* C(+)(III)-O-N(-)(II) => C(IV)=O=N(III) */ - for ( k = 0; k < 2; k ++ ) { - n = k? n_C : n_N; - i1 = k? j[i_C] : j[1-i_C]; - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[n].charge += (k? -1:1); - } - num_changes ++; - num_N_minus --; - num_C_plus --; - num_All -= 2; - } - } - } - if ( (!type || 17 == type) && 0 < num_C_plus && 0 < num_C_minus ) { - int m[3], c[3], j[3], k; - for ( i = 0; i < num_atoms; i ++ ) { - if ( 0 == at[i].charge && 3 == nNoMetalNumBonds(at, i) && 3 == nNoMetalBondsValence(at, i) && - 0 == num_of_H( at, i ) && - 0 <= ( j[0] = nNoMetalNeighIndex(at, i) ) && - 0 <= ( j[1] = nNoMetalOtherNeighIndex( at, i, m[0] = at[i].neighbor[j[0]] ) ) && - 0 <= ( j[2] = nNoMetalOtherNeighIndex2( at, i, m[0], m[1] = at[i].neighbor[j[1]] ) ) && - 1 == !(c[0]=at[m[0]].charge) - + !(c[1]=at[m[1]].charge) - + !(c[2]=at[m[2]=at[i].neighbor[j[2]]].charge) && - 0 == c[0] + c[1] + c[2] && - 2 == (3== (c[0]? at[m[0]].chem_bonds_valence+NUMH(at,m[0]):0)) - + (3== (c[1]? at[m[1]].chem_bonds_valence+NUMH(at,m[1]):0)) - + (3== (c[2]? at[m[2]].chem_bonds_valence+NUMH(at,m[2]):0)) && - NULL != memchr( en+ELEM_N_FST, at[i].el_number, ELEM_N_LEN) ) { - /* found non-terminal A(+)-O-B(-) */ - int n_Cp=-1, n_Cm=-1, i_Cp=-1, i_Cm=-1; /* p = positive, m = negatice ion C */ - for ( k = 0; k < 3; k ++ ) { - if ( c[k] ) { - n = m[k]; - if ( -1 == at[n].charge && - NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) { - n_Cm = n; - i_Cm = k; - } else - if ( 1 == at[n].charge && - NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) { - n_Cp = n; - i_Cp = k; - } - } - } - if ( n_Cp < 0 || n_Cm < 0 || - has_other_ion_in_sphere_2(at, n_Cp, n_Cm, en, ne ) || - has_other_ion_in_sphere_2(at, n_Cm, n_Cp, en, ne )) { - continue; - } - /* | | */ - /* C(+)(III)-N-C(-)(III) => C(IV)=N=C(IV) */ - for ( k = 0; k < 2; k ++ ) { - n = k? n_Cp : n_Cm; - i1 = k? j[i_Cp] : j[i_Cm]; - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; - at[i].bond_type[i1] ++; - at[n].bond_type[i2] ++; - at[i].chem_bonds_valence ++; - at[n].chem_bonds_valence ++; - at[n].charge += (k? -1:1); - } - num_changes ++; - num_C_minus --; - num_C_plus --; - num_All -= 2; - } - } - } - if ( (!type || 18 == type) && (0 < num_C_plus && 0 < num_C_minus || 0 < num_C_II) ) { - int m[2], v[2], j[2], k; - for ( i = 0; i < num_atoms; i ++ ) { - if ( 0 == at[i].charge && 2 == nNoMetalNumBonds(at, i) && 3 == nNoMetalBondsValence(at, i) && - 0 == num_of_H( at, i ) && - 0 <= (j[0] = nNoMetalNeighIndex( at, i )) && - 0 <= (j[1] = nNoMetalOtherNeighIndex( at, i, m[0] = at[i].neighbor[j[0]] )) && - 0 == at[m[0]].charge - +at[m[1]=at[i].neighbor[j[1]]].charge && - 6 == (v[0]=at[m[0]].chem_bonds_valence+NUMH(at,m[0])) - +(v[1]=at[m[1]].chem_bonds_valence+NUMH(at,m[1])) && - 2 >= abs(v[0]-v[1]) && - NULL != memchr( en+ELEM_N_FST, at[i].el_number, ELEM_N_LEN) && - NULL != memchr( en+ELEM_C_FST, at[m[0]].el_number, ELEM_C_LEN) && - NULL != memchr( en+ELEM_C_FST, at[m[1]].el_number, ELEM_C_LEN) - ) { - /* n_Cm i n_Cp */ - /* found non-terminal C(-)(III)-N=C(+)(III) or C(IV)=N-C(II): Cm-N-Cp */ - /* convert to C(IV)=N#C(IV) */ - int n_Cp=-1, n_Cm=-1, i_Cp=-1, i_Cm=-1; /* p = positive, m = negatice ion C */ - for ( k = 0; k < 2; k ++ ) { - n = m[k]; - if ( v[k] == 4 || v[k] == 3 && at[i].bond_type[j[k]] == BOND_TYPE_SINGLE ) { - n_Cm = n; - i_Cm = k; - } else - if ( v[k] == 2 || v[k] == 3 && at[i].bond_type[j[k]] == BOND_TYPE_DOUBLE ) { - n_Cp = n; - i_Cp = k; - } - } - if ( n_Cp < 0 || n_Cm < 0 || at[n_Cp].valence+NUMH(at,n_Cp) != 2 ) { - continue; /* guarantees at[n_Cp].valence <= 2 */ - } - if ( v[i_Cp] == 2 || !at[n_Cp].charge ) { - if ( at[n_Cp].valence == 2 ) { - /* neighbor of at[n_Cp] opposite to at[i] */ - k = at[n_Cp].neighbor[at[n_Cp].neighbor[0]==i]; - if ( NULL != memchr( en+ELEM_N_FST, at[k].el_number, ELEM_N_LEN) ) { - continue; - } - } - } else - if ( at[n_Cp].charge ) { - if ( has_other_ion_in_sphere_2(at, n_Cp, n_Cm, en, ne ) || - has_other_ion_in_sphere_2(at, n_Cm, n_Cp, en, ne )) { - continue; - } - } else { - continue; /* unknown case */ - } - /* */ - /* C(-)(III)-N=C(+)(III) => C(IV)=N#C(IV) */ - /* C(IV)=N-C(II) => C(IV)=N#C(IV) */ - if ( at[n_Cp].charge ) { - num_C_minus --; - num_C_plus --; - num_All -= 2; - } else { - num_C_II --; - num_All --; - } - - for ( k = 0; k < 2; k ++ ) { - n = k? n_Cp : n_Cm; - i3 = k? i_Cp : i_Cm; /* added to fix the bug */ - /*i1 = k? j[i_Cp] : j[i_Cm];*/ /* replaced with next line */ - i1 = j[i3]; - if ( v[i3 /*was i1*/] < 4 ) { /* WDI found a bug here: bounds violation */ - int delta = 4 - v[i3 /*was i1*/]; - i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; - at[i].bond_type[i1] += delta; - at[n].bond_type[i2] += delta; - at[i].chem_bonds_valence += delta; - at[n].chem_bonds_valence += delta; - at[n].charge = 0; - at[n].radical = 0; - } - } - at[i].charge = 0; - at[i].radical = 0; - num_changes ++; - } - } - } - } - - return num_changes; -} - -/*#if ( DISCONNECT_SALTS == 1 )*/ /* { */ - - - -/*************************************************************************************************/ -int RemoveInpAtBond( inp_ATOM *atom, int iat, int k ) -{ - int i, j, m, m2, k2; - inp_ATOM *at = atom + iat; - inp_ATOM *at2 = NULL; - int val = at->valence - 1; - if ( val >= 0 ) { - int bond = at->bond_type[k]; - if ( bond > BOND_TYPE_TRIPLE ) - bond = BOND_TYPE_SINGLE; /* added 08-06-2003 */ - - /* update CML tetrahedral atom parity. */ - if ( at->p_parity ) { - for( m = 0; m < MAX_NUM_STEREO_ATOM_NEIGH; m ++ ) { - if ( at->p_orig_at_num[m] == at->orig_at_number ) { - at->p_parity = 0; - break; /* only 3 bonds are present; removing one bond removes stereo */ - } - } - if ( at->p_parity /* at->valence == MAX_NUM_STEREO_ATOM_NEIGH*/ ) { - for ( m = 0; m < at->valence; m ++ ) { - if ( atom[(int)at->neighbor[k]].orig_at_number == at->p_orig_at_num[m] ) { - break; - } - } - if ( m < at->valence ) { - at->p_orig_at_num[m] = at->orig_at_number; - } else { - at->p_parity = 0; /* wrong neighbors: at->neighbor[k] is not in the list of a stereo neighbors */ - } - } - } - - /* update CML stereogenic bond parities; at this point no removed explicit H exist yet */ - if ( at->sb_parity[0] ) { - for ( m = 0; m < MAX_NUM_STEREO_BONDS && at->sb_parity[m]; ) { - if ( k == at->sb_ord[m] || k == at->sn_ord[m] && val < 2 && ATOM_PARITY_WELL_DEF(at->sb_parity[m]) ) { - /* !!! FLAW: does take into account removed H !!! */ - /* stereogenic bond is being removed OR */ - /* remove stereogenic bond because its only neighbor is being removed */ - int pnxt_atom, pinxt2cur, pinxt_sb_parity_ord; - int len= get_opposite_sb_atom( atom, iat, at->sb_ord[m], &pnxt_atom, &pinxt2cur, &pinxt_sb_parity_ord ); - if ( len ) { - i = pinxt_sb_parity_ord; - at2 = atom + pnxt_atom; - k2 = pinxt2cur; - } else { - i = MAX_NUM_STEREO_BONDS; - } - /* - at2 = atom + at->neighbor[ (int)at->sb_ord[m] ]; - for ( i = 0; i < MAX_NUM_STEREO_BONDS && at2->sb_parity[i]; i ++ ) { - if ( iat == at2->neighbor[ (int)at2->sb_ord[i] ] ) - break; - } - */ - if ( i < MAX_NUM_STEREO_BONDS && at2->sb_parity[i] ) { - m2 = i; - /* remove bond parity from at */ - if ( m < MAX_NUM_STEREO_BONDS-1 ) { - memmove( at->sb_parity+m, at->sb_parity+m+1, (MAX_NUM_STEREO_BONDS-1 - m) * sizeof(at->sb_parity[0])); - memmove( at->sb_ord+m, at->sb_ord+m+1, (MAX_NUM_STEREO_BONDS-1 - m) * sizeof(at->sb_ord[0])); - memmove( at->sn_ord+m, at->sn_ord+m+1, (MAX_NUM_STEREO_BONDS-1 - m) * sizeof(at->sn_ord[0])); - memmove( at->sn_orig_at_num+m, at->sn_orig_at_num+m+1, (MAX_NUM_STEREO_BONDS-1 - m) * sizeof(at->sn_orig_at_num[0])); - } - at->sb_parity[MAX_NUM_STEREO_BONDS-1] = 0; - at->sb_ord[MAX_NUM_STEREO_BONDS-1] = 0; - at->sn_ord[MAX_NUM_STEREO_BONDS-1] = 0; - at->sn_orig_at_num[MAX_NUM_STEREO_BONDS-1] = 0; - /* remove bond parity from at2 */ - if ( m2 < MAX_NUM_STEREO_BONDS-1 ) { - memmove( at2->sb_parity+m2, at2->sb_parity+m2+1, (MAX_NUM_STEREO_BONDS-1 - m2) * sizeof(at2->sb_parity[0])); - memmove( at2->sb_ord+m2, at2->sb_ord+m2+1, (MAX_NUM_STEREO_BONDS-1 - m2) * sizeof(at2->sb_ord[0])); - memmove( at2->sn_ord+m2, at2->sn_ord+m2+1, (MAX_NUM_STEREO_BONDS-1 - m2) * sizeof(at2->sn_ord[0])); - memmove( at2->sn_orig_at_num+m2, at2->sn_orig_at_num+m2+1, (MAX_NUM_STEREO_BONDS-1 - m2) * sizeof(at2->sn_orig_at_num[0])); - } - at2->sb_parity[MAX_NUM_STEREO_BONDS-1] = 0; - at2->sb_ord[MAX_NUM_STEREO_BONDS-1] = 0; - at2->sn_ord[MAX_NUM_STEREO_BONDS-1] = 0; - at2->sn_orig_at_num[MAX_NUM_STEREO_BONDS-1] = 0; - /* do not increment m here because the array elements have been shifted */ - } else { - m ++; /* program error: inconsistent stereobond parity */ - } - } else - if ( k == at->sn_ord[m] ) { - /* stereogenic bond neighbor is being removed; another neighbor remains */ - /* !!! FLAW: does take into account removed H !!! */ - for ( j = 0, i = -1; j < at->valence; j ++ ) { - if ( j != k && j != at->sb_ord[m] ) { - i = j; - break; - } - } - /* i is the position of the neighbor that will become a new neighbor */ - /*************************************************************************** - * at->sb_parity[m] is the direction (EVEN=clockwise, ODD=counterclockwise) - * from stereobond to the neighbor. If the neighbor is removed then - * the parity should invert, otherwise it should be unchanged. - ***************************************************************************/ - if ( i < 0 ) { - /* no alternative neighbor is available */ - if ( ATOM_PARITY_WELL_DEF(at->sb_parity[m] ) ) { - /* parity cannot be not well-defined anymore */ - int pnxt_atom, pinxt2cur, pinxt_sb_parity_ord; - int len= get_opposite_sb_atom( atom, iat, at->sb_ord[m], &pnxt_atom, &pinxt2cur, &pinxt_sb_parity_ord ); - if ( len > 0 ) { - atom[pnxt_atom].sb_parity[pinxt_sb_parity_ord] = at->sb_parity[m] = AB_PARITY_UNDF; - } -#ifdef _DEBUG - else { - int stop = 1; /* sb parities error */ - } -#endif - } - at->sn_ord[m] = -99; /* sb neighbor has been disconnected */ - at->sb_ord[m] -= (at->sb_ord[m] > k); /* same as above */ - at->sn_orig_at_num[m] = 0; - } else - if ( i < at->valence ) { - /* choose another stereogenic bond neighbor, its ord. number is i before bond removal */ - if ( ATOM_PARITY_WELL_DEF(at->sb_parity[m]) ) { - /* ALL WRONG: 'move' previous stereo bond neighbor to the last position (pos. 2 out of 0,1,2) */ - /* the parity of the transpositions is (2 - at->sn_ord[m])%2 = at->sn_ord[m] % 2 */ - /* and replace the neighbor with another; the contribution to the parity is 1 */ - - /*at->sb_parity[m] = 2 - ( at->sb_parity[m] + at->sn_ord[m] + 1 ) % 2;*/ - - /*at->sb_parity[m] = 2 - ( at->sb_parity[m] + k + i + - (i > k) + (i > at->sb_ord[m]) ) % 2;*/ - /*=== parity should be INVERTED ===*/ - at->sb_parity[m] = 3 - at->sb_parity[m]; - } - at->sn_ord[m] = i - (i > k); /* ord. number shifted because preceding bond is removed */ - at->sb_ord[m] -= (at->sb_ord[m] > k); /* same as above */ - at->sn_orig_at_num[m] = atom[(int)at->neighbor[i]].orig_at_number; - /*at->sb_parity[m] = 2 - ( at->sb_parity[m] + 1 ) % 2;*/ - } else { - at->sb_parity[m] = 0; /* program error: inconsistent stereobond parity */ - } - m ++; - } else { - /* removing another neighbor, k: first move it to the last position (pos. 2 out of 0,1,2) */ - if ( k < 2 && ATOM_PARITY_WELL_DEF(at->sb_parity[m]) ) { - /*at->sb_parity[m] = 2 - ( at->sb_parity[m] + k ) % 2;*/ - /*at->sb_parity[m] = 2 - ( at->sb_parity[m] + (at->sn_ord[m] > k) + (at->sb_ord[m] > k) ) % 2;*/ - ;/*==== Parity should remain UNCHANGED ===*/ - } - if ( at->sb_ord[m] > k ) { - at->sb_ord[m] --; - } - if ( at->sn_ord[m] > k ) { - at->sn_ord[m] --; - } - m ++; - } - } - } - - if ( k < val ) { - memmove( at->neighbor+k, at->neighbor+k+1, sizeof(at->neighbor[0])*(val-k) ); - memmove( at->bond_stereo+k, at->bond_stereo+k+1, sizeof(at->bond_stereo[0])*(val-k) ); - memmove( at->bond_type+k, at->bond_type+k+1, sizeof(at->bond_type[0])*(val-k) ); - } - at->neighbor[val] = 0; - at->bond_stereo[val] = 0; - at->bond_type[val] = 0; - at->valence = val; - at->chem_bonds_valence -= bond; - return 1; - } - return 0; -} - - - -/*************************************************************************************************/ -int DisconnectInpAtBond( inp_ATOM *at, AT_NUMB *nOldCompNumber, int iat, int neigh_ord ) -{ - int neigh, i, ret = 0; - int component; - neigh = at[iat].neighbor[neigh_ord]; - for ( i = 0; i < at[neigh].valence; i ++ ) { - if ( iat == (int)at[neigh].neighbor[i] ) - break; - } - if ( i < at[neigh].valence ) { - ret += RemoveInpAtBond( at, iat, neigh_ord ); - ret += RemoveInpAtBond( at, neigh, i ); - if ( nOldCompNumber && ret ) { - if ( component = at[iat].component ) { - nOldCompNumber[component-1] = 0; - } - if ( component = at[neigh].component ) { - nOldCompNumber[component-1] = 0; - } - } - } - return (ret == 2); -} - - - -/*************************************************************************************************/ -int bIsAmmoniumSalt( inp_ATOM *at, int i, int *piO, int *pk, S_CHAR *num_explicit_H ) -{ - /* NH4(+charge)-O(-charge)-C -> NH3 + HO-C; any charge including 0, any C except charged or radical */ - /* F, Cl, Br, I */ - static U_CHAR el_number_C=0, el_number_O=0, el_number_H=0, el_number_N=0; - static U_CHAR el_number_F=0, el_number_Cl=0, el_number_Br=0, el_number_I=0; - int num_H, num_non_iso_H, num_impl_iso_H, bDisconnect = 1; - int j, val, neigh, iO=-1, iC, k=-1; - if ( 0 == el_number_C ) { - /* one time initialization */ - el_number_C = get_periodic_table_number( "C" ); - el_number_O = get_periodic_table_number( "O" ); - el_number_H = get_periodic_table_number( "H" ); - el_number_N = get_periodic_table_number( "N" ); - el_number_F = get_periodic_table_number( "F" ); - el_number_Cl= get_periodic_table_number( "Cl" ); - el_number_Br= get_periodic_table_number( "Br" ); - el_number_I = get_periodic_table_number( "I" ); - } - if ( at[i].el_number != el_number_N ) - return 0; - - /* check for NH4-O-C... -> NH3 + HO-C... */ - val = at[i].valence; - num_impl_iso_H = NUM_ISO_H(at,i); - num_non_iso_H = at[i].num_H; - num_H = num_non_iso_H + num_impl_iso_H; - if ( val + num_H == 5 ) { - int num_O = 0; - memset( num_explicit_H, 0, (NUM_H_ISOTOPES+1)*sizeof(num_explicit_H[0]) ); - for ( j = 0; j < val; j ++ ) { /* looking for O: H4N-O-C... */ - neigh = at[i].neighbor[j]; - if ( at[neigh].num_H || - at[neigh].charge && (at[neigh].el_number != el_number_O || at[neigh].charge + at[i].charge) || - at[neigh].radical && at[neigh].radical != RADICAL_SINGLET ) { - bDisconnect = 0; - break; /* reject */ - } - if ( at[neigh].el_number == el_number_H && at[neigh].valence == 1 && - !at[neigh].charge && !at[neigh].radical ) { - num_H ++; /* at this point at[].num_H does not include explicit H count */ - num_non_iso_H += (0==at[neigh].iso_atw_diff); - num_explicit_H[at[neigh].iso_atw_diff] ++; /* explicit H on N */ - } else - if ( at[neigh].el_number == el_number_O && at[neigh].valence == 2 && !num_O ) { - num_O ++; /* found O: N-O- */ - iO = neigh; - k = j; - iC = at[iO].neighbor[at[iO].neighbor[0] == i]; - if ( at[iC].el_number != el_number_C || /* - at[iC].num_H || - at[iC].chem_bonds_valence != 4 || */ - at[iC].charge || - at[iC].radical && at[iC].radical != RADICAL_SINGLET /*|| - at[iC].valence == at[iC].chem_bonds_valence*/ ) { - bDisconnect = 0; - break; /* reject */ - } - } else - if ( (at[neigh].el_number == el_number_F || - at[neigh].el_number == el_number_Cl || - at[neigh].el_number == el_number_Br || - at[neigh].el_number == el_number_I ) && - at[neigh].valence == 1 && at[neigh].chem_bonds_valence == 1 && - !at[neigh].charge && !NUMH(at,neigh) && !num_O ) { - num_O ++; /* found O: N-O- */ - iO = neigh; - k = j; - iC = -1; - } else { - bDisconnect = 0; - break; /* reject */ - } - } - if ( bDisconnect && (num_O != 1 || num_H != 4) ) { - bDisconnect = 0; /* reject */ - } - } else { - bDisconnect = 0; - } - if ( bDisconnect ) { - *piO = iO; - *pk = k; - } - return bDisconnect; -} - - - -/*************************************************************************************************/ -int DisconnectAmmoniumSalt ( inp_ATOM *at, int iN, int iO, int k, S_CHAR *num_explicit_H ) -{ - /* disconnect NH4-O from O */ - /* Note: iO = at[iN].neighbor[k], at[iN] is N, at[iO].neighbor[0] is either N=at[iN] or C=at[iC] */ - int nMove_H_iso_diff = -1; /* do not move explicit H */ - int j, neigh, iso_diff, neigh_pos; - static U_CHAR el_number_H = 0; - int val = at[iN].valence; - - if ( !el_number_H ) { - el_number_H = get_periodic_table_number( "H" ); - } - if ( at[iN].charge && !(at[iN].charge + at[iO].charge) ) { - at[iN].charge = at[iO].charge = 0; /* remove charges */ - } - neigh_pos = (at[iO].valence == 2)? (at[iO].neighbor[1] == iN) : 0; /* position of at[iN] in the neigh list of iO */ - /* disconnect bond O-N */ - RemoveInpAtBond( at, iO, neigh_pos ); - RemoveInpAtBond( at, iN, k ); - val --; - - /* move 1 H from NH4 to O- or Cl */ - - /* find non-isotopic or the lightest isotopic H to move from N to O */ - for ( iso_diff = 0; iso_diff <= NUM_H_ISOTOPES; iso_diff ++ ) { - if ( !iso_diff ) { - /* find non-isotopic H */ - if ( at[iN].num_H ) { - at[iN].num_H --; /* move non-isotopic implicit H */ - at[iO].num_H ++; - break; - } else - if ( num_explicit_H[0] ) { - nMove_H_iso_diff = 0; /* flag: move explicit non-isotopic H */ - break; - } - } else { - /* find isotopic H */ - if ( at[iN].num_iso_H[iso_diff] ) { - at[iN].num_iso_H[iso_diff] --; /* move implicit isotopic H, atw = 1 */ - at[iO].num_iso_H[iso_diff] ++; - break; - } else - if ( num_explicit_H[iso_diff] ) { - nMove_H_iso_diff = iso_diff; /* flag: move explicit isotopic H, atw = 1 */ - break; - } - } - } - if ( nMove_H_iso_diff >= 0 ) { - /* move explicit H, it is isotopic if nMove_H_iso_diff > 0 */ - double dist2_H_O, min_dist2_H_O = -1.0; - int jH = -1, iH = -1; - for ( j = 0; j < val; j ++ ) { /* looking H in N-H such that H-O is shortest */ - neigh = at[iN].neighbor[j]; - if ( at[neigh].el_number == el_number_H && - at[neigh].iso_atw_diff == nMove_H_iso_diff ) { - dist2_H_O = (at[neigh].x - at[iO].x) * (at[neigh].x - at[iO].x) + - (at[neigh].y - at[iO].y) * (at[neigh].y - at[iO].y) + - (at[neigh].z - at[iO].z) * (at[neigh].z - at[iO].z); - if ( min_dist2_H_O < 0.0 || min_dist2_H_O > dist2_H_O ) { - min_dist2_H_O = dist2_H_O; - iH = neigh; - jH = j; - } - } - } - /* reconnect; bonds do not need changes except stereo */ - neigh_pos = at[iO].valence; - at[iO].neighbor[neigh_pos] = iH; - at[iO].bond_stereo[neigh_pos] = 0; - at[iO].bond_type[neigh_pos] = at[iH].bond_type[0]; - at[iO].chem_bonds_valence += at[iH].bond_type[0]; - at[iO].valence ++; - at[iH].neighbor[0] = iO; - at[iH].bond_stereo[0] = 0; - /* disconnect H from N */ - RemoveInpAtBond( at, iN, jH ); - val --; - if ( k > jH ) { - k --; - } - } - return 1; -} - - - -/*************************************************************************************************/ -int bIsMetalSalt( inp_ATOM *at, int i ) -{ - int type, val, k, iO, iC, j, neigh; - int bDisconnect = 1; - static U_CHAR el_number_C=0, el_number_O=0, el_number_H=0; - static U_CHAR el_number_F=0, el_number_Cl=0, el_number_Br=0, el_number_I=0; - if ( 0 == el_number_C ) { - /* one time initialization */ - el_number_C = get_periodic_table_number( "C" ); - el_number_O = get_periodic_table_number( "O" ); - el_number_H = get_periodic_table_number( "H" ); - el_number_F = get_periodic_table_number( "F" ); - el_number_Cl= get_periodic_table_number( "Cl" ); - el_number_Br= get_periodic_table_number( "Br" ); - el_number_I = get_periodic_table_number( "I" ); - } - /* check for a metal atom: - metal atom should be connected and be a metal */ - if ( !(val = at[i].valence) || - !(type = get_el_type( at[i].el_number )) || - !(type & IS_METAL) ) { - bDisconnect = 0; /* reject */ - } else - /* metal atom should not have adjacent H or multiple bonds or radical */ - if ( at[i].num_H ) { - bDisconnect = 0; /* reject */ - } else - /* check valence */ - if ( at[i].charge == 0 && - ( (type & 1) && val == get_el_valence( at[i].el_number, 0, 0 ) || - (type & 2) && val == get_el_valence( at[i].el_number, 0, 1 ) ) || - at[i].charge > 0 && - (type & 1) && val == get_el_valence( at[i].el_number, at[i].charge, 0 ) ) { - ; /* accept */ - } else { - bDisconnect = 0; /* reject */ - } - if ( bDisconnect ) { - /************************************************************************* - * | * - * check M neighbors. Disconnect if all neighbors are M-O-C# or M-O-C= * - * | * - *************************************************************************/ - for ( k = 0; k < at[i].valence; k ++ ) { - iO = at[i].neighbor[k]; - /* halogenide 2004-07-08 */ - if ( (at[iO].el_number == el_number_F || - at[iO].el_number == el_number_Cl || - at[iO].el_number == el_number_Br || - at[iO].el_number == el_number_I ) && - at[iO].valence == 1 && at[iO].chem_bonds_valence == 1 && - !at[iO].charge && !(at[iO].radical && at[iO].radical != RADICAL_SINGLET) && !NUMH(at,iO) ) { - ; /* found */ - } else { - /* -O-C= */ - if ( at[iO].el_number != el_number_O || - NUMH(at, iO) || - at[iO].valence != 2 || - at[iO].charge || - at[iO].radical && at[iO].radical != RADICAL_SINGLET || - at[iO].valence != at[iO].chem_bonds_valence ) { - bDisconnect = 0; /* reject */ - break; - } - iC = at[iO].neighbor[at[iO].neighbor[0] == i]; - if ( at[iC].el_number != el_number_C || - at[iC].num_H || - at[iC].chem_bonds_valence != 4 || - at[iC].charge || - at[iC].radical && at[iC].radical != RADICAL_SINGLET || - at[iC].valence == at[iC].chem_bonds_valence ) { - bDisconnect = 0; /* reject */ - break; - } - for ( j = 0; j < at[iC].valence; j ++ ) { - neigh = at[iC].neighbor[j]; - if ( at[neigh].el_number == el_number_H ) { - break; - } - } - if ( j != at[iC].valence ) { - bDisconnect = 0; /* reject */ - break; - } - } - } - } - return bDisconnect; -} - - - -/*************************************************************************************************/ -int DisconnectMetalSalt( inp_ATOM *at, int i ) -{ - int k, iO; - /* disconnect metal atom or ion at[i] */ - for ( k = 0; k < at[i].valence; k ++ ) { - iO = at[i].neighbor[k]; - if ( at[iO].valence == 2 ) { - if ( at[iO].neighbor[0] == i ) { /* assuming atom O always has 2 bonds */ - /* copy the remaining neighbor to the 0 position */ - at[iO].neighbor[0] = at[iO].neighbor[1]; - at[iO].bond_stereo[0] = at[iO].bond_stereo[1]; - at[iO].bond_type[0] = at[iO].bond_type[1]; - } - /* clear neighbor at position 1 */ - at[iO].neighbor[1] = 0; - at[iO].bond_stereo[1] = 0; - at[iO].bond_type[1] = 0; - } else { - /* clear neighbor at position 1 */ - at[iO].neighbor[0] = 0; - at[iO].bond_stereo[0] = 0; - at[iO].bond_type[0] = 0; - } - /* make O negatively charged */ - at[iO].charge = -1; - /* reduce O valence to account for the removed single bond */ - at[iO].valence --; - at[iO].chem_bonds_valence --; - - /* clear metal neighbor (O) */ - at[i].neighbor[k] = 0; - at[i].bond_stereo[k] = 0; - at[i].bond_type[k] = 0; - /* add a positive charge to the metal */ - at[i].charge ++; - } - /* set metal valence to zero because it has been disconnected */ - at[i].valence = 0; - at[i].chem_bonds_valence = 0; - return k; -} - - - -/*************************************************************************************************/ -int DisconnectSalts( ORIG_ATOM_DATA *orig_inp_data, int bDisconnect ) -{ - int i, k, iO, num_changes, val; - S_CHAR num_explicit_H[NUM_H_ISOTOPES+1]; - inp_ATOM *at = orig_inp_data->at; - int num_at = orig_inp_data->num_inp_atoms; - - /* check each atom */ - for ( i = 0, num_changes = 0; i < num_at; i ++ ) { - - if ( !(val = at[i].valence) || /* disconnected atom */ - val != at[i].chem_bonds_valence || /* a bond has higher multiplicity than 1 */ - at[i].radical && at[i].radical != RADICAL_SINGLET /* radical */ ) { - continue; /* reject */ - } - if ( bIsAmmoniumSalt( at, i, &iO, &k, num_explicit_H ) ) { - if ( bDisconnect ) { - DisconnectAmmoniumSalt ( at, i, iO, k, num_explicit_H ); - orig_inp_data->num_inp_bonds --; - } - /* count disconnected atoms */ - num_changes ++; - } else - if ( bIsMetalSalt( at, i ) ) { - if ( bDisconnect ) { - k = DisconnectMetalSalt( at, i ); - orig_inp_data->num_inp_bonds -= k; - } - num_changes ++; - } - } - return num_changes; -} - - - -/*****************************************************************************/ -/* Important: Salt disconnection is independent from coord. disconnection: */ -/* because different atoms are disconnected. */ -/* However, sal disconnection may need to be rerun after metal disconnection */ -/* because metal disconnection may make certain atoms be eligible for salt */ -/* disconnection */ -/*****************************************************************************/ -int bIsMetalToDisconnect(inp_ATOM *at, int i, int bCheckMetalValence) -{ - int type, at_valence, num_H; -/* - if ( !at[i].valence ) -*/ - if ( !(type = get_el_type( at[i].el_number )) || - !(type & IS_METAL ) ) { - return 0; - } - num_H = NUMH(at,i); - at_valence = num_H + at[i].chem_bonds_valence; - if ( !at_valence ) { - return 0; /* nothing to disconnect */ - } - if ( bCheckMetalValence ) { - if ( abs(at[i].charge) > 1 ) { - return 1; /* multiple charges */ - } - for ( i = 0; i < 2 && (i & type); i ++ ) { - if ( at_valence == get_el_valence( at[i].el_number, at[i].charge, i ) ) { - return 2; /* atom has normal valence */ - } - } - } - return 1; - -} - - - -/*****************************************************************************/ -int bMayDisconnectMetals( ORIG_ATOM_DATA *orig_inp_data, int bCheckMetalValence, INCHI_MODE *bTautFlagsDone ) -{ - int i, j, k, iO, num_changes, val, bRadOrMultBonds, num_impl_H = 0; - S_CHAR num_explicit_H[NUM_H_ISOTOPES+1]; - inp_ATOM *at = orig_inp_data->at; - int num_at = orig_inp_data->num_inp_atoms; - int *nNumImplH = &orig_inp_data->bDisconnectCoord; - /* check each atom */ - for ( i = 0, num_changes = 0; i < num_at; i ++ ) { - - if ( !(val = at[i].valence) && !NUMH(at,i) ) { - continue; /* disconnected atom */ - } - bRadOrMultBonds = (val == 0) || - (val != at[i].chem_bonds_valence) || /* a bond has higher multiplicity than 1 */ - (at[i].radical && at[i].radical != RADICAL_SINGLET); /* radical */ - - if ( !bRadOrMultBonds && bIsAmmoniumSalt( at, i, &iO, &k, num_explicit_H ) ) { - ; - } else - if ( !bRadOrMultBonds && bIsMetalSalt( at, i ) ) { - ; - } else - if ( 1 == (j = bIsMetalToDisconnect(at, i, bCheckMetalValence)) ) { - num_impl_H += NUMH(at,i); - num_changes ++; - } else - if ( 2 == j && bTautFlagsDone ) { - *bTautFlagsDone |= TG_FLAG_CHECK_VALENCE_COORD_DONE; - } - } - if ( nNumImplH ) - *nNumImplH = num_changes? num_impl_H+1 : 0; - return num_changes; -} - - - -/*****************************************************************************/ -#if ( bRELEASE_VERSION == 0 && (EXTR_HAS_METAL_ATOM & (EXTR_MASK | EXTR_FLAG) ) ) -int bHasMetalAtom( ORIG_ATOM_DATA *orig_inp_data ) -{ - int i; - inp_ATOM *at; - if ( orig_inp_data && (at = orig_inp_data->at) ) { - int num_at = orig_inp_data->num_inp_atoms; - /* check each atom */ - for ( i = 0; i < num_at; i ++ ) { - if ( IS_METAL & get_el_type( at[i].el_number ) ) { - return 1; - } - } - } - return 0; -} -#endif -/***************************************************************************** -{ "F", 19, 19, 18.998403220, 0 , 0, {{0,}, {0,}, {1,}, {2,}, {3,5}, },}, -{ "Cl", 35, 35, 34.968852730, 0 , 0, {{0,}, {0,}, {1,3,5,7}, {2,4,6}, {3,5,}, },}, -{ "Br", 80, 79, 78.918336100, 0 , 0, {{0,}, {0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, },}, -{ "I", 127, 127, 126.904500000, 0 , 0, {{0,}, {0,}, {1,3,5,7,}, {2,4,6}, {3,5,}, },}, -{ "At", 210, 210, 209.987100000, 0 , 0, {{0,}, {0,}, {1,3,5,7,}, {2,4,6}, {3,5,}, },}, -{ "N", 14, 14, 14.003074000, 0 , 0, {{1,}, {2,}, {3,5}, {4,}, {3,}, },}, -{ "P", 31, 31, 30.973762000, 0 , 0, {{1,3,5,7,}, {2,4,6,}, {3,5,}, {4,}, {3,}, },}, -{ "As", 75, 75, 74.921594200, 0 , 0, {{0,}, {2,4,6,}, {3,5,}, {4,}, {3,}, },}, -{ "Sb", 122, 121, 120.903800000, 0 , 0, {{1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,}, {3,}, },}, -{ "O", 16, 16, 15.994914630, 0 , 0, {{0,}, {1,}, {2,}, {3,5,}, {4,}, },}, -{ "S", 32, 32, 31.972070700, 0 , 0, {{0,}, {1,3,5,7,}, {2,4,6}, {3,5,}, {4,}, },}, -{ "Se", 79, 80, 79.916519600, 0 , 0, {{0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, {4,}, },}, -{ "Te", 128, 130, 129.906200000, 0 , 0, {{0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,}, },}, -{ "Po", 209, 209, 208.982400000, 0 , 0, {{0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,}, },}, -{ "B", 11, 11, 11.009300000, 0 , 0, {{3,}, {4,}, {3,}, {2,}, {1,}, },}, -*****************************************************************************/ - - - -int DisconnectMetals( ORIG_ATOM_DATA *orig_inp_data, int bCheckMetalValence, INCHI_MODE *bTautFlagsDone ) - /*inp_ATOM *atom, int num_atoms, int nNumExplH, int *new_num_atoms */ -{ - int i, j, k, n, iO, num_changes, val, bRadOrMultBonds; - int num_impl_H, num_at, err, num_disconnected; - S_CHAR num_explicit_H[NUM_H_ISOTOPES+1]; - static char elnumber_Heteroat[16] = {'\0', }; - static int num_halogens; - inp_ATOM *at = NULL; - S_CHAR *bMetal = NULL; - inp_ATOM *atom = orig_inp_data->at; - int num_atoms = orig_inp_data->num_inp_atoms; - int nNumExplH = (orig_inp_data->bDisconnectCoord > 0)? orig_inp_data->bDisconnectCoord - 1 : 0; - AT_NUMB *nOldCompNumber = orig_inp_data->nOldCompNumber; - - err = 0; - num_impl_H = 0; - num_at = num_atoms; - num_disconnected = 0; - if ( !(at = (inp_ATOM *)inchi_calloc( num_at + nNumExplH, sizeof(at[0] ) )) || - !(bMetal = ( S_CHAR *)inchi_calloc( num_at + nNumExplH, sizeof(bMetal[0]) )) ) { - err = 1; - goto exit_function; - } - if (!elnumber_Heteroat[0] ) { - i = 0; - /* halogens */ - elnumber_Heteroat[i++] = (char)get_periodic_table_number( "F" ); /* 0 */ - elnumber_Heteroat[i++] = (char)get_periodic_table_number( "Cl" ); - elnumber_Heteroat[i++] = (char)get_periodic_table_number( "Br" ); - elnumber_Heteroat[i++] = (char)get_periodic_table_number( "I" ); - elnumber_Heteroat[i++] = (char)get_periodic_table_number( "At" ); /* 4 */ - num_halogens = i; - /* other non-metal */ - elnumber_Heteroat[i++] = (char)get_periodic_table_number( "N" ); - elnumber_Heteroat[i++] = (char)get_periodic_table_number( "P" ); - elnumber_Heteroat[i++] = (char)get_periodic_table_number( "As" ); - /*elnumber_Heteroat[i++] = get_periodic_table_number( "Sb" );*/ /* metal 10-28-2003 */ - elnumber_Heteroat[i++] = (char)get_periodic_table_number( "O" ); - elnumber_Heteroat[i++] = (char)get_periodic_table_number( "S" ); - elnumber_Heteroat[i++] = (char)get_periodic_table_number( "Se" ); - elnumber_Heteroat[i++] = (char)get_periodic_table_number( "Te" ); - /*elnumber_Heteroat[i++] = get_periodic_table_number( "Po" );*/ /* metal 10-28-2003 */ - elnumber_Heteroat[i++] = (char)get_periodic_table_number( "B" ); - elnumber_Heteroat[i++] = 0; - } - - memcpy( at, atom, num_atoms * sizeof(at[0]) ); - - /* check each atom, mark metals */ - for ( i = 0, k = 0, num_changes = 0; i < num_atoms; i ++ ) { - - if ( !(val = at[i].valence) && !NUMH(at,i) ) { - continue; /* disconnected atom */ - } - bRadOrMultBonds = (val == 0) || - (val != at[i].chem_bonds_valence) || /* a bond has higher multiplicity than 1 */ - (at[i].radical && at[i].radical != RADICAL_SINGLET); /* radical */ - - if ( !bRadOrMultBonds && bIsAmmoniumSalt( at, i, &iO, &k, num_explicit_H ) ) { - ; - } else - if ( !bRadOrMultBonds && bIsMetalSalt( at, i ) ) { - ; - } else - if ( 1 == (j = bIsMetalToDisconnect(at, i, bCheckMetalValence)) ) { - num_impl_H += (k = NUMH(at,i)); - bMetal[i] = 1+k; - num_changes ++; - } else - if ( 2 == j && bTautFlagsDone ) { - *bTautFlagsDone |= TG_FLAG_CHECK_VALENCE_COORD_DONE; - } - } - if ( num_impl_H != nNumExplH ) { - err = 2; - goto exit_function; - } - - - /* replace implicit H atoms with explicit H atoms */ - for ( i = 0; i < num_atoms && 0 < num_impl_H; i ++ ) { - if ( bMetal[i] <= 1 ) { - continue; - } - for ( k = 0; k < NUM_H_ISOTOPES+1; k ++ ) { - n = k? at[i].num_iso_H[k-1] : at[i].num_H; - for ( j = 0; j < n; j ++ ) { - if ( num_at >= num_atoms + nNumExplH ) { - err = 3; - goto exit_function; - } - at[num_at].elname[0] = 'H'; - at[num_at].el_number = get_periodic_table_number(at[num_at].elname); - at[num_at].iso_atw_diff = k; - at[num_at].component = at[i].component; - move_explicit_Hcation(at, num_at+1, i, num_at, 1); - at[num_at].orig_at_number = num_at+1; - num_at ++; - num_impl_H --; - bMetal[i] --; - if ( k ) { - at[i].num_iso_H[k-1] --; - } else { - at[i].num_H --; - } - } - } - if ( bMetal[i] != 1 ) { - err = 4; - goto exit_function; - } - } - if ( num_at != num_atoms + nNumExplH ) { - err = 5; - goto exit_function; - } - - /* disconnect metal - ligand bonds */ - for ( i = 0; i < num_atoms; i ++ ) { - if ( !bMetal[i] ) { - continue; - } - /* disconnect metal atom M - - Note: Defect in case of bridging ligands: - - M M M M M M(+) - \ / will be transformed to , not to - N(+) N(+) N(-) - / \ / \ / \ - R R R R R R - - Non-bridging are OK: - - M R M(+) R - \ / / - N(+) ---> N - / \ / \ - R R R R - - */ - for ( j = at[i].valence-1; 0 <= j; j -- ) { - if ( j < at[i].valence && !bMetal[ (int)at[i].neighbor[j] ] ) { - /* do not break metal-metal bond here */ - num_disconnected += DisconnectOneLigand( at, nOldCompNumber, bMetal, elnumber_Heteroat, - num_halogens, num_atoms, i, j, bTautFlagsDone ); - } - } - } - /* disconnect metal-metal bonds */ - for ( i = 0; i < num_atoms; i ++ ) { - if ( !bMetal[i] ) { - continue; - } - for ( j = at[i].valence-1; 0 <= j; j -- ) { - if ( j < at[i].valence && bMetal[ (int)at[i].neighbor[j] ] ) { - /* break metal-metal bond here */ - num_disconnected += DisconnectOneLigand( at, nOldCompNumber, bMetal, elnumber_Heteroat, - num_halogens, num_atoms, i, j, bTautFlagsDone ); - } - } - } - - -exit_function: - if ( !num_disconnected ) { - err = 6; - } - if ( at && err ) { - inchi_free( at ); - at = NULL; - } - if ( atom && at ) { /* changed if ( at ) to if ( atom && at ) 2004-04-03 */ - inchi_free( atom ); - atom = NULL; - } - if ( bMetal ) - inchi_free( bMetal ); - - if ( at ) { - orig_inp_data->at = at; - orig_inp_data->num_inp_atoms = num_at; - } - return err? -err : num_disconnected; -} - - - -/*****************************************************************************/ -int DisconnectOneLigand( inp_ATOM *at, AT_NUMB *nOldCompNumber, S_CHAR *bMetal, char *elnumber_Heteroat, - int num_halogens, int num_atoms, int iMetal, int jLigand, INCHI_MODE *bTautFlagsDone ) -{ - int i, j, iLigand, neigh, val; - int metal_neigh_ord[MAXVAL], num_neigh_arom_bonds[MAXVAL]; - int num_metal_neigh, num_disconnections; - int num_del_arom_bonds, num_tot_arom_bonds, new_charge; - char *p; - - iLigand = at[iMetal].neighbor[jLigand]; - num_metal_neigh = 0; - num_disconnections = 0; - num_del_arom_bonds = num_tot_arom_bonds = 0; - - /* find bonds to disconnect */ - for ( i = 0; i < at[iLigand].valence; i ++ ) { - num_neigh_arom_bonds[i] = 0; - neigh = (int)at[iLigand].neighbor[i]; - if ( neigh < num_atoms && bMetal[ neigh ] ) { - metal_neigh_ord[ num_metal_neigh ++ ] = i; - if ( at[iLigand].bond_type[i] > BOND_TYPE_TRIPLE ) { - /* aromatic bond */ - for ( j = 0; j < at[neigh].valence; j ++ ) { - num_neigh_arom_bonds[i] += ( at[neigh].bond_type[j] > BOND_TYPE_TRIPLE ); - } - num_del_arom_bonds ++; - } - } - num_tot_arom_bonds += (at[iLigand].bond_type[i] > BOND_TYPE_TRIPLE); - } - /* Disconnect */ - if ( num_del_arom_bonds ) { - /* fix chem_valence of the ligand and its neighbors in case of disconnecting arom. bonds */ - /* because in this case special care should be taken of updating at[].chem_bonds_valence */ - for ( i = 0; i < num_metal_neigh; i ++ ) { - j = metal_neigh_ord[i]; - if ( num_neigh_arom_bonds[j] ) { - neigh = at[iLigand].neighbor[j]; - at[neigh].chem_bonds_valence -= num_neigh_arom_bonds[j]/2 - (num_neigh_arom_bonds[j]-1)/2; - } - } - at[iLigand].chem_bonds_valence -= num_tot_arom_bonds/2 - (num_tot_arom_bonds-num_del_arom_bonds)/2; - } - /* disconnect in reverse order, otherwise the metal_neigh_ord[i] - becomes invalid after the first disconnection - */ - for ( i = num_metal_neigh-1; 0 <= i; i -- ) { - num_disconnections += DisconnectInpAtBond( at, nOldCompNumber, iLigand, metal_neigh_ord[i] ); - } - - /* attempt to change ligand charge to make its valence 'natural' */ - i = num_tot_arom_bonds - num_del_arom_bonds; - if ( i && i != 2 && i != 3 || - at[iLigand].radical && at[iLigand].radical != RADICAL_SINGLET || - !(p = strchr( elnumber_Heteroat, at[iLigand].el_number ) ) ) { - goto exit_function; /* non-standard atom */ - } - val = at[iLigand].chem_bonds_valence + NUMH(at, iLigand); - new_charge = MAX_ATOMS; /* impossible value */ - if ( !val ) { - if ( p - elnumber_Heteroat < num_halogens ) { - new_charge = -1; - } - } else { - for ( i = -1; i <= 1; i ++ ) { - if ( val == get_el_valence( at[iLigand].el_number, i, 0 ) ) { - new_charge = i; /* found charge that fits chem. valence */ - break; - } - } - } - if ( new_charge != MAX_ATOMS ) { - if ( (new_charge != at[iLigand].charge || - (at[iLigand].radical && at[iLigand].radical != RADICAL_SINGLET)) && - 1 == num_metal_neigh ) { - if ( 1 == new_charge && 4 == val && 2 == at[iLigand].valence && - 4 == at[iLigand].chem_bonds_valence && - at[iLigand].bond_type[0] == at[iLigand].bond_type[1] ) { - ; /* do not add +1 charge to disconnected =N=, etc. 2004-10-27 */ - } else { - if ( bTautFlagsDone && new_charge != at[iLigand].charge ) { - *bTautFlagsDone |= TG_FLAG_MOVE_CHARGE_COORD_DONE; - } - at[iMetal].charge -= new_charge - at[iLigand].charge; - at[iLigand].charge = new_charge; - /*at[iLigand].radical = 0;*/ - } - } - } -exit_function: - return num_disconnections; /* ret;*/ -} - - - - -/****************************************************************************************/ -double dist3D( inp_ATOM *at1, inp_ATOM *at2 ) -{ - double dx = at1->x - at2->x; - double dy = at1->y - at2->y; - double dz = at1->z - at2->z; - return sqrt( dx*dx+dy*dy+dz*dz ); -} -/****************************************************************************************/ -#define MIN_BOND_LENGTH (1.0e-6) -#define MIN_COS (1.0e-6) -#define MIN_BOND_LENGTH2 (MIN_BOND_LENGTH*MIN_BOND_LENGTH) -#define MAX_BOND_LENGTH (1.0e30) -/****************************************************************************************/ -double GetMinDistDistribution( inp_ATOM *at, int num_at, int iat, int iat_H, - int bInAllComponents, double min_dist[], int num_segm ) -{ -/* const double one_pi = 2.0*atan2(1.0 , 0.0 ); */ - const double one_pi = 3.14159265358979323846; /* M_PI */ - const double two_pi = 2.0*one_pi; - const double f_step = two_pi / num_segm; - const double h_step = f_step/2.0; - - int i, j, k, kk, ki, kn, n, num_bonds; - double xi, yi, xn, yn, cross_prod_in, dot_prod_in, xni, yni, rni, tni, rmin; - double fi, fk, fn, ft, rt, rk, ri, rn, c, ave_bond_len; - - for ( i = 0; i < num_segm; i ++ ) { - min_dist[i] = MAX_BOND_LENGTH; /* more than any distance */ - } - num_bonds = 0; - ave_bond_len = 0.0; - for ( i = 0; i < num_at; i ++ ) { - if ( i != iat && i != iat_H && (bInAllComponents || at[i].component == at[iat].component) ) { - for ( j = 0; j < at[i].valence; j ++ ) { - n = at[i].neighbor[j]; - if ( (n > i && n != iat) || n == iat_H ) - continue; -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - if ( n == iat ) { - int stop = 1; /* */ - } -#endif - xi = at[i].x - at[iat].x; /* ri; i != iat */ - yi = at[i].y - at[iat].y; - xn = at[n].x - at[iat].x; /* rn; possibly n == iat */ - yn = at[n].y - at[iat].y; - cross_prod_in = xi*yn - xn*yi; /* ((r(i)-r(iat)) x (r(n)-r(iat)) */ - if ( cross_prod_in < -0.01*MIN_BOND_LENGTH2 ) { - /* make sure the r(i)->r(n) vector is counterclockwise around at[iat] */ - inchi_swap( (char*)&xi, (char*)&xn, sizeof(xi) ); - inchi_swap( (char*)&yi, (char*)&yn, sizeof(yi) ); - cross_prod_in = -cross_prod_in; - } - xni = xn - xi; /* r(n)->r(i) */ - yni = yn - yi; - rni = xni*xni + yni*yni; - if ( rni > 0.01*MIN_BOND_LENGTH2 ) { - /* vector length |ri->rn| is not too small */ - /* arrowhead of the vector r(t) = ri + (rn-ri)*t; 0 <= t <= 1 points to the bond ri->rn */ - /* r(tni) is perpendicular to the bond ri->rn so that min|r(t)| = r(tni) = |tni|*rni */ - tni = -(xni*xi + yni*yi)/rni; - /* find min. distance from n-i bond to at[iat] */ - if ( tni < 0.0 ) { - rmin = sqrt( xi*xi + yi*yi ); - } else - if ( tni > 1.0 ) { - rmin = sqrt( xn*xn + yn*yn ); - } else { - rmin = sqrt(tni*tni*rni); - } - ave_bond_len += sqrt( rni ); - num_bonds ++; - } else { - /* zero length i-n bond */ - tni = 0.5; /* fake */ - rmin = sqrt( xi*xi + yi*yi ); /* arbitrarily choose one */ - } - if ( rmin >= 0.1*MIN_BOND_LENGTH ) { - /* at[iat] does not belong to at[i]-at[n] bond */ - int bCalc_rt = 1; - fi = atan2( yi, xi ); - fn = (n == iat)? fi : atan2( yn, xn ); - if ( fi > fn ) { - /* make sure fn - fi >= 0 */ - fn += two_pi; - } - if ( fi < 0.0 ) { - fi += two_pi; - fn += two_pi; - } - ki = (int)floor((fi+h_step)/f_step); /* cast does not match function type */ - kn = (int)floor((fn+h_step)/f_step); - /* the bond may affect several segments */ - for ( k = ki; k <= kn; k ++ ) { - kk = k % num_segm; - if ( min_dist[kk] < rmin ) - continue; - if ( bCalc_rt ) { - if ( n == iat ) { - ft = fi; - rt = rmin; - } else { - double xt, yt; - xt = xi + xni*tni; - yt = yi + yni*tni; - ft = atan2( yt, xt ); - rt = sqrt(xt*xt + yt*yt); - } - bCalc_rt = 0; - } - fk = f_step * kk; - c = fabs(cos( fk - ft )); - if ( c < MIN_COS ) - c = MIN_COS; - rk = rt / c; - if ( min_dist[kk] > rk ) { - min_dist[kk] = rk; - } - } - } else { - /* rmin < 0.1*MIN_BOND_LENGTH */ - ri = xi*xi + yi*yi; - rn = xn*xn + yn*yn; - if ( ri > MIN_BOND_LENGTH2 && rn > MIN_BOND_LENGTH2 ) { - dot_prod_in = xn*xi + yn*yi; - /* a very short bond */ - if ( dot_prod_in > 0.01*MIN_BOND_LENGTH2 ) { - /* bond does not cross at[iat] */ - double fyixi = atan2( yi, xi ); - if ( fyixi < 0.0 ) fyixi += two_pi; - kk = (int)floor((fyixi+h_step)/f_step) % num_segm; - if ( min_dist[kk] > rmin ) { - min_dist[kk] = rmin; - } - } else - if ( dot_prod_in < -0.01*MIN_BOND_LENGTH2 ) { - /* bond does cross at[iat] */ - double fyixi = atan2( yi, xi ); - if ( fyixi < 0.0 ) fyixi += two_pi; - kk = (int)floor((fyixi+h_step)/f_step) % num_segm; - if ( min_dist[kk] > rmin ) { - min_dist[kk] = rmin; - } - fyixi += one_pi; - kk = (int)floor((fyixi+h_step)/f_step) % num_segm; - if ( min_dist[kk] > rmin ) { - min_dist[kk] = rmin; - } - } else { - ; /* error, should not happen */ - } - } else - if ( ri <= MIN_BOND_LENGTH2 && rn <= MIN_BOND_LENGTH2 ) { - /* a very short bond coincides with at[iat]; ignore */ - ; - } else { - /* one end of the bond coincides with at[iat] */ - fi = ri>rn? atan2( yi, xi) : atan2( yn, xn ); - if ( fi < 0.0 ) fi += two_pi; - kk = (int)floor((fi+h_step)/f_step) % num_segm; - if ( min_dist[kk] > rmin ) { - min_dist[kk] = rmin; - } - } - } - } - } - } - if ( num_bonds ) { - return ave_bond_len / (double)num_bonds; - } else { - return 0.0; - } -} - - - -/****************************************************************************************/ -int move_explicit_Hcation(inp_ATOM *at, int num_at, int iat, int iat_H, int bInAllComponents) -{ -#define NUM_SEGM 20 - /* const double one_pi = 2.0*atan2(1.0 , 0.0 ); */ - const double one_pi = 3.14159265358979323846; /* M_PI */ - const double two_pi = 2.0*one_pi; - const double f_step = two_pi / NUM_SEGM; - const double h_step = f_step/2.0; - double min_dist[NUM_SEGM]; - int nB, i, k, kk, next, val; - double r, r0, xd, yd, zd, xr, yr, zr, ave_bond_len; - /*double step = 4.0*atan(1.0)/NUM_SEGM;*/ - /* find at[iat] neighbors coordinates */ - xd=yd=zd=0.0; - if ( at[iat].valence ) { - for ( i = 0, nB=0, r = 0.0; i < at[iat].valence; i ++ ) { - next = at[iat].neighbor[i]; - xd += at[next].x; - yd += at[next].y; - zd += at[next].z; - r += dist3D( at+iat, at+next ); - nB ++; - } - xd /= (double)nB; - yd /= (double)nB; - zd /= (double)nB; - r /= (double)nB; - r0 = sqrt((double)(xd-at[iat].x)*(xd-at[iat].x) - + (double)(yd-at[iat].y)*(yd-at[iat].y)); - } else { - if ( at[iat_H].valence ) { - r = dist3D( at+iat_H, at+ (int)at[iat_H].neighbor[0] ); - } else { - r = 0.0; - } - r0 = 0.0; - } - ave_bond_len = GetMinDistDistribution( at, num_at, iat, iat_H, bInAllComponents, min_dist, NUM_SEGM ); - if ( r < MIN_BOND_LENGTH && ave_bond_len > MIN_BOND_LENGTH ) { - r = ave_bond_len; /* ave_bond_len = 0.0 may mean that it is 0D structure */ - } - if ( r > MIN_BOND_LENGTH ) { - /* process non-zero bond lengths */ - double f; - if ( 10.0*r0 < r ) { - xr = -r; /* arbitrary */ - yr = 0.0; - zr = 0.0; - } else { - /* - if ( r0 < MIN_BOND_LENGTH ) { - r0 = 1.0; - } - */ - xr = r * ( at[iat].x - xd )/r0; - yr = r * ( at[iat].y - yd )/r0; /* length = r */ - zr = r * ( at[iat].z - zd )/r0; - -/* -- test: opposire direction -- - xr = -r * ( at[iat].x - xd )/r0; - yr = -r * ( at[iat].y - yd )/r0; - zr = -r * ( at[iat].z - zd )/r0; -*/ - if ( xr*xr + yr*yr < 0.04*r*r ) { - xr = -r; - yr = 0.0; - } - } - r = sqrt( xr*xr + yr*yr ); - f = atan2( yr, xr ); - - if ( f < 0.0 ) - f += two_pi; - - - - kk = (int)floor((f+h_step)/f_step) % NUM_SEGM; /* cast does not match function type by design */ - if ( min_dist[kk] < 1.5* r ) { - double dist = 1.5*r; - int start=-1, len=0, start_max=-1, len_max=0; -again: - /* look for longest kk interval with min_dist[kk] >= dist */ - for ( k = 0, start = 0, len = 0, len_max = 0; k < 2*NUM_SEGM; k ++ ) { - kk = k % NUM_SEGM; - if ( min_dist[kk] >= dist ) { - if ( !len ++) { - start = k; - } - } else { - if ( len > len_max ) { - len_max = len; - start_max = start; - } - len = 0; - } - } - if ( !len_max ) { - if ( dist > 0.1*r ) { - dist *= 0.75; - goto again; - } else { - goto done; /* do it anyway */ - } - } else { - /* found a good sector */ - f = f_step * (start_max + (double)(len_max - 1)/2.0); - r0 = dist / 1.5; - xr = r0 * cos(f); - yr = r0 * sin(f); - zr = zr/r*r0; - } - } - } else { - xr = yr = zr = 0; - } - -done: - - if ( at[iat_H].valence ) { - /* disconnect H */ - next = at[iat_H].neighbor[0]; - for ( i = 0; i < at[next].valence; i ++ ) { - if ( at[next].neighbor[i] == iat_H ) { - RemoveInpAtBond( at, next, i ); - i = 0; /* success */ - break; - } - } - } else { - /* isolated H+ cation */ - next = iat_H; - i = 0; - at[iat_H].valence = 1; - at[iat_H].chem_bonds_valence = 1; - at[iat_H].bond_type[0] = BOND_TYPE_SINGLE; - } - if ( 0 == i /*i < at[next].valence*/ ) { - /* move charge */ - if ( at[next].charge > 0 && at[iat].charge < 0 ) { - at[next].charge --; - at[iat].charge ++; - } - /* connect H to at[iat] */ - val = at[iat].valence; - at[iat].neighbor[val] = iat_H; - at[iat].bond_type[val] = at[iat_H].bond_type[0]; - at[iat].bond_stereo[val] = 0; - at[iat].chem_bonds_valence += at[iat_H].bond_type[0]; - at[iat].valence = val+1; - - at[iat_H].component = at[iat].component; - at[iat_H].neighbor[0] = iat; - at[iat_H].bond_stereo[0] = 0; /* possible loss of stereo info */ - at[iat_H].x = at[iat].x + xr; - at[iat_H].y = at[iat].y + yr; - at[iat_H].z = at[iat].z + zr; - return 1; /* success */ - } - return 0; /* failed */ -} -/****************************************************************************************/ -int get_iat_number( int el_number, const int el_num[], int el_num_len ) -{ - int i; - for ( i = 0; i < el_num_len; i ++ ) { - if ( el_num[i] == el_number ) - return i; - } - return -1; -} - - -/*#endif*/ /* } DISCONNECT_SALTS */ - typedef enum tagIonAtomType { - IAT_H=0, - IAT_C, - IAT_N, - IAT_P, - IAT_O, - IAT_S, - IAT_Se, - IAT_Te, - IAT_F, - IAT_Cl, - IAT_Br, - IAT_I, - IAT_MAX - } ION_ATOM_TYPE; - -#if ( READ_INCHI_STRING == 1 ) -/****************************************************************************************/ -int bHeteroAtomMayHaveXchgIsoH( inp_ATOM *atom, int iat ) -{ - inp_ATOM *at = atom + iat, *at2; - static int el_num[IAT_MAX]; - int j, val, is_O=0, is_Cl=0, is_N=0, is_H=0, num_H, iat_numb, bAccept, cur_num_iso_H; - - if ( !el_num[IAT_H]) { - el_num[IAT_H ] = get_periodic_table_number( "H" ); - el_num[IAT_C ] = get_periodic_table_number( "C" ); - el_num[IAT_N ] = get_periodic_table_number( "N" ); - el_num[IAT_P ] = get_periodic_table_number( "P" ); - el_num[IAT_O ] = get_periodic_table_number( "O" ); - el_num[IAT_S ] = get_periodic_table_number( "S" ); - el_num[IAT_Se] = get_periodic_table_number( "Se"); - el_num[IAT_Te] = get_periodic_table_number( "Te"); - el_num[IAT_F ] = get_periodic_table_number( "F" ); - el_num[IAT_Cl] = get_periodic_table_number( "Cl"); - el_num[IAT_Br] = get_periodic_table_number( "Br"); - el_num[IAT_I ] = get_periodic_table_number( "I" ); - } - if ( 0 > (iat_numb = get_iat_number( at->el_number, el_num, IAT_MAX )) ) { - return 0; - } - if ( abs(at->charge) > 1 || at->radical && RADICAL_SINGLET != at->radical ) { - return 0; - } - val = -1; - switch( iat_numb ) { - case IAT_N: - case IAT_P: - is_N = 1; - val = 3+at->charge; - break; - case IAT_O: - case IAT_S: - case IAT_Se: - case IAT_Te: - is_O = 1; - val = 2+at->charge; - break; - case IAT_F: - case IAT_Cl: - case IAT_Br: - case IAT_I: - if ( at->charge == 0 ) { - is_Cl = 1; /* isolated HCl */ - val = 1; - } - break; - case IAT_H: - if ( at->valence == 0 && - at->charge == 1 ) { - is_H = 1; /* isolated proton */ - val = 0; - } - } - if ( val < 0 ) { - return 0; - } - num_H = NUMH(at,0); - if ( val != at->chem_bonds_valence + num_H ) { - return 0; - } - if ( is_H ) { - return 2; /* H atom */ - } else { - cur_num_iso_H = 0; - for ( j = 0, bAccept = 1; j < at->valence && bAccept; j ++ ) { - at2 = atom + (int)at->neighbor[j]; - if ( at2->charge && at->charge || (at2->radical && RADICAL_SINGLET != at2->radical ) ) { - return 0; /* adjacent charged/radical atoms: do not neutralizate */ - } - } - } - return 1; -} -#endif -/****************************************************************************************/ -int bNumHeterAtomHasIsotopicH( inp_ATOM *atom, int num_atoms ) -{ - static int el_num[IAT_MAX]; - int i, j, val, is_O=0, is_Cl=0, is_N=0, is_H=0, num_H, iat_numb, bAccept, num_iso_H, cur_num_iso_H, num_iso_atoms; - inp_ATOM *at, *at2; - /* one time initialization */ - if ( !el_num[IAT_H]) { - el_num[IAT_H ] = get_periodic_table_number( "H" ); - el_num[IAT_C ] = get_periodic_table_number( "C" ); - el_num[IAT_N ] = get_periodic_table_number( "N" ); - el_num[IAT_P ] = get_periodic_table_number( "P" ); - el_num[IAT_O ] = get_periodic_table_number( "O" ); - el_num[IAT_S ] = get_periodic_table_number( "S" ); - el_num[IAT_Se] = get_periodic_table_number( "Se"); - el_num[IAT_Te] = get_periodic_table_number( "Te"); - el_num[IAT_F ] = get_periodic_table_number( "F" ); - el_num[IAT_Cl] = get_periodic_table_number( "Cl"); - el_num[IAT_Br] = get_periodic_table_number( "Br"); - el_num[IAT_I ] = get_periodic_table_number( "I" ); - } - num_iso_H = 0; - num_iso_atoms = 0; - for ( i = 0, at = atom; i < num_atoms; i ++, at ++ ) { - - num_iso_atoms += ( at->iso_atw_diff != 0 || NUM_ISO_H(at,0) ); /* isotopic atoms and implicit isotopic H */ - - if ( 0 > (iat_numb = get_iat_number( at->el_number, el_num, IAT_MAX )) ) { - continue; - } - - if ( abs(at->charge) > 1 || at->radical && RADICAL_SINGLET != at->radical ) { - continue; - } - - val = -1; - switch( iat_numb ) { - case IAT_N: - case IAT_P: - is_N = 1; - val = 3+at->charge; - break; - case IAT_O: - case IAT_S: - case IAT_Se: - case IAT_Te: - is_O = 1; - val = 2+at->charge; - break; - case IAT_F: - case IAT_Cl: - case IAT_Br: - case IAT_I: - if ( at->charge == 0 ) { - is_Cl = 1; /* isolated HCl */ - val = 1; - } - break; - case IAT_H: - if ( at->valence == 0 && - at->charge == 1 ) { - is_H = 1; /* isolated proton */ - val = 0; - } - } - if ( val < 0 ) { - continue; - } - num_H = NUMH(at,0); - if ( val != at->chem_bonds_valence + num_H ) { - continue; - } - if ( is_H ) { - bAccept = 1; - cur_num_iso_H = (at->iso_atw_diff != 0); - } else { - cur_num_iso_H = 0; - for ( j = 0, bAccept = 1; j < at->valence && bAccept; j ++ ) { - at2 = atom + (int)at->neighbor[j]; - if ( at2->charge && at->charge || (at2->radical && RADICAL_SINGLET != at2->radical ) ) { - bAccept = 0; /* adjacent charged/radical atoms: do not neutralizate */ - break; - } else - if ( at2->el_number == el_num[IAT_H ] && at2->valence == 1 && at2->iso_atw_diff ) { - cur_num_iso_H ++; /* isotopic explicit H */ - } - } - if ( bAccept ) { - num_iso_atoms -= cur_num_iso_H; /* avoid counting explicit H as isotopic atom */ - cur_num_iso_H += NUM_ISO_H(at,0); - } - - } - num_iso_H += (bAccept && cur_num_iso_H); /* number of acceptable heteroatoms that have isotopic H */ - } - return ((num_iso_H? 1:0) | (num_iso_atoms? 2:0)); -} - - -/****************************************************/ -/* Mark and count disconnected structure components */ -/* by Depth-first searching each component */ -/****************************************************/ -int cmp_components( const void *a1, const void *a2 ) -{ - int ret; - AT_NUMB n1; - AT_NUMB n2; - - n1 = ((const AT_NUMB *)a1)[0]; /* number of atoms in the component -- descending order */ - n2 = ((const AT_NUMB *)a2)[0]; - if ( ret = (int)n2 - (int)n1 ) { - return ret; - } - /* stable sort */ - n1 = ((const AT_NUMB *)a1)[1]; /* component ordering number -- ascending order */ - n2 = ((const AT_NUMB *)a2)[1]; - ret = (int)n1 - (int)n2; - - return ret; - -} -/*************************************************************************************************/ -int MarkDisconnectedComponents( ORIG_ATOM_DATA *orig_at_data, int bProcessOldCompNumbers ) -{ - typedef AT_NUMB AT_TRIPLE[3]; - - inp_ATOM *at = orig_at_data->at; - int num_at = orig_at_data->num_inp_atoms; - AT_NUMB *nCurAtLen = NULL; - - AT_NUMB *nNewCompNumber = NULL; - AT_NUMB *nPrevAtom = NULL; - S_CHAR *iNeigh = NULL; - - AT_NUMB *nOldCompNumber = NULL; - int i, j, num_components, ret; - int new_comp_no; - AT_NUMB old_comp_no, another_comp_no, no_component; - - /* component_nbr[i][0] = number of atoms in the component i-1 - * component_nbr[i][1] = original component number (id-1) = i - * after sorting: - * component_nbr[j][2] = new number of component #(component_nbr[i][1]+1) - */ - AT_TRIPLE *component_nbr = NULL; - - /* initialize */ - if ( bProcessOldCompNumbers && !orig_at_data->nOldCompNumber ) { - bProcessOldCompNumbers = 0; - } - num_components = 0; - /* - for ( j = 0; j < num_at; j ++ ) { - at[j].component = 0; - } - */ - ret = -1; - if ( !num_at ) { - return 0; - } - if ( !( nNewCompNumber = (AT_NUMB *) inchi_calloc( num_at, sizeof(nNewCompNumber[0]) ) ) || - /* for non-recursive DFS only: */ - !( nPrevAtom = (AT_NUMB *) inchi_calloc( num_at, sizeof(nPrevAtom[0]) ) ) || - !( iNeigh = (S_CHAR *) inchi_calloc( num_at, sizeof(iNeigh[0]) ) )) { - goto exit_function; - } - /* mark and count; avoid deep DFS recursion: it may make verifying software unhappy */ - /* nNewCompNumber[i] will contain new component number for atoms at[i], i=0..num_at-1 */ - for ( j = 0; j < num_at; j++ ) { - if ( !nNewCompNumber[j] ) { - /* mark starting with at[j] */ - int fst_at, nxt_at, cur_at = j; - num_components ++; - /* first time at at[j] */ - nNewCompNumber[fst_at = cur_at] = (AT_NUMB) num_components; - /* find next neighbor */ - while ( 1 ) { - if ( iNeigh[cur_at] < at[cur_at].valence ) { - nxt_at = at[cur_at].neighbor[(int)iNeigh[cur_at] ++]; - if ( !nNewCompNumber[nxt_at] ) { - /* forward edge: found new atom */ - nNewCompNumber[nxt_at] = (AT_NUMB) num_components; - nPrevAtom[nxt_at] = (AT_NUMB) cur_at; - cur_at = nxt_at; - } - } else - if ( cur_at == fst_at ) { - break; /* done */ - } else { - cur_at = nPrevAtom[cur_at]; /* retract */ - } - } - } - } - inchi_free( nPrevAtom ); nPrevAtom = NULL; - inchi_free( iNeigh ); iNeigh = NULL; - - /* Allocate more memory */ - i = inchi_max( num_components, orig_at_data->num_components ); - if ( !(nCurAtLen = (AT_NUMB *) inchi_calloc( num_components+1, sizeof(nCurAtLen[0]) ) ) || - !(nOldCompNumber = (AT_NUMB *) inchi_calloc( i +1, sizeof(nOldCompNumber[0]) ) ) || - !(component_nbr = (AT_TRIPLE *) inchi_calloc( num_components+1, sizeof(component_nbr[0]) ) ) ) { - goto exit_function; - } - - /* count atoms per component and renumber the components */ - for ( i = 0; i < num_components; i ++ ) { - component_nbr[i][0] = 0; /* number of atoms in the component */ - component_nbr[i][1] = i; /* component ordering number */ - } - for ( j = 0; j < num_at; j ++ ) { - component_nbr[(int)nNewCompNumber[j]-1][0] ++; /* count atoms in each component */ - } - /* sort key: number of atoms; order: descending */ - qsort( (void*)component_nbr[0], num_components, - sizeof(component_nbr[0]), cmp_components); - /* invert the transposition */ - for ( i = 0; i < num_components; i ++ ) { - nCurAtLen[i] = component_nbr[i][0]; - component_nbr[ component_nbr[i][1] ][2] = i+1; - } - /* renumber the components so that the component with the greatest number of atoms is the first */ - no_component = num_at+1; - for ( j = 0; j < num_at; j ++ ) { - /* new component number for at[j] */ - new_comp_no = component_nbr[(int)nNewCompNumber[j]-1][2]-1; /* starts from 0 */ - if ( bProcessOldCompNumbers ) { - /* old component number for at[j] */ - old_comp_no = at[j].component; - /* fill out nOldCompNumber[]; initially it contains zeroes */ - if ( !old_comp_no ) { - nOldCompNumber[new_comp_no] = no_component; /* atom did not have component number */ - } else - if ( nOldCompNumber[new_comp_no] != old_comp_no ) { - if ( !nOldCompNumber[new_comp_no] ) { - nOldCompNumber[new_comp_no] = old_comp_no; - } else { - /* at[j] moved from old comp #old_comp_no to old comp #nOldCompNumber[new_comp_no] - Both components cannot be equal to any current component */ - another_comp_no = nOldCompNumber[new_comp_no]; - for ( i = 0; i < num_components; i ++ ) { - if ( nOldCompNumber[i] == old_comp_no || - nOldCompNumber[i] == another_comp_no ) { - nOldCompNumber[i] = no_component; - } - } - /* nOldCompNumber[new_comp_no] = num_at+1; */ - } - } - } - /* orig_at_data->nOldCompNumber */ - at[j].component = new_comp_no+1; /* starts from 1 */ - } - if ( bProcessOldCompNumbers ) { - for ( j = 0; j < num_components; j ++ ) { - if ( nOldCompNumber[j] == no_component ) { - /* the component has atom from another component */ - nOldCompNumber[j] = 0; - } else - if ( nOldCompNumber[j] && - !orig_at_data->nOldCompNumber[nOldCompNumber[j]-1] ) { - /* the component has changed in the previous processing */ - nOldCompNumber[j] = 0; - } - } - } else { - for ( j = 0; j < num_components; j ++ ) { - nOldCompNumber[j] = j + 1; - } - } - ret = num_components; -exit_function: - if ( nNewCompNumber ) - inchi_free( nNewCompNumber ); - if ( component_nbr ) - inchi_free( component_nbr ); - - if ( ret < 0 ) { - if ( nPrevAtom ) { - inchi_free( nPrevAtom ); - nPrevAtom = NULL; - } - if ( iNeigh ) { - inchi_free( iNeigh ); - iNeigh = NULL; - } - if ( nCurAtLen ) { - inchi_free( nCurAtLen ); - nCurAtLen = NULL; - } - if ( nOldCompNumber ) { - inchi_free( nOldCompNumber ); - nOldCompNumber = NULL; - } - num_components = ret; - } - /* avoid memory leaks */ - if ( orig_at_data->nCurAtLen ) - inchi_free ( orig_at_data->nCurAtLen ); - if ( orig_at_data->nOldCompNumber ) - inchi_free ( orig_at_data->nOldCompNumber ); - - orig_at_data->nCurAtLen = nCurAtLen; - orig_at_data->nOldCompNumber = nOldCompNumber; - - orig_at_data->num_components = num_components; - - return ret; /* number of disconnected components; 1=>single connected structure*/ -} -/******************************************************************************/ -/* Extract one (connected) component */ -/******************************************************************************/ -int ExtractConnectedComponent( inp_ATOM *at, int num_at, int component_number, inp_ATOM *component_at ) -{ - int i, j, num_component_at; - AT_NUMB *number; - if ( NULL == (number = (AT_NUMB*)inchi_calloc(num_at, sizeof(AT_NUMB)))){ - return CT_OUT_OF_RAM; /* out of memory */ /* */ - } - /* copy atoms */ - for ( i = 0, num_component_at = 0; i < num_at; i ++ ) { - if ( at[i].component == component_number ) { - number[i] = num_component_at; - component_at[num_component_at ++] = at[i]; - } - } - /* renumber neighbors */ - for ( i = 0; i < num_component_at; i ++ ) { - component_at[i].orig_compt_at_numb = (AT_NUMB)(i + 1); - for ( j = 0; j < component_at[i].valence; j ++ ) { - component_at[i].neighbor[j] = number[(int)component_at[i].neighbor[j]]; - } - } - inchi_free( number ); - return num_component_at; -} -/****************************************************************/ -int SetConnectedComponentNumber( inp_ATOM *at, int num_at, int component_number ) -{ - int i; - for ( i = 0; i < num_at; i ++ ) { - at[i].component = (AT_NUMB)component_number; - } - return 0; -} -/****************************************************************/ - -int Free_INChI_Stereo( INChI_Stereo *pINChI_Stereo ) -{ - if ( pINChI_Stereo ) { - qzfree( pINChI_Stereo->nNumber ); - qzfree( pINChI_Stereo->t_parity ); - qzfree( pINChI_Stereo->nNumberInv ); - qzfree( pINChI_Stereo->t_parityInv ); - qzfree( pINChI_Stereo->nBondAtom1 ); - qzfree( pINChI_Stereo->nBondAtom2 ); - qzfree( pINChI_Stereo->b_parity ); - } - return 0; -} - -/****************************************************************/ -INChI_Stereo *Alloc_INChI_Stereo(int num_at, int num_bonds) -{ - INChI_Stereo *pINChI_Stereo = (INChI_Stereo *)inchi_calloc(1, sizeof(INChI_Stereo)); - if ( pINChI_Stereo ) { - if ( num_at && - (pINChI_Stereo->nNumber = (AT_NUMB *)inchi_calloc(num_at, sizeof(pINChI_Stereo->nNumber[0]))) && - (pINChI_Stereo->t_parity = (S_CHAR *)inchi_calloc(num_at, sizeof(pINChI_Stereo->t_parity[0]))) - && (pINChI_Stereo->nNumberInv = (AT_NUMB *)inchi_calloc(num_at, sizeof(pINChI_Stereo->nNumberInv[0]))) - && (pINChI_Stereo->t_parityInv = (S_CHAR *)inchi_calloc(num_at, sizeof(pINChI_Stereo->t_parityInv[0]))) - ) { - ; - } else - if ( num_at ) { - goto out_of_RAM; - } - if ( num_bonds && - (pINChI_Stereo->nBondAtom1 =(AT_NUMB *)inchi_calloc(num_bonds, sizeof(pINChI_Stereo->nBondAtom1[0]))) && - (pINChI_Stereo->nBondAtom2 =(AT_NUMB *)inchi_calloc(num_bonds, sizeof(pINChI_Stereo->nBondAtom2[0]))) && - (pINChI_Stereo->b_parity =(S_CHAR *)inchi_calloc(num_bonds, sizeof(pINChI_Stereo->b_parity[0]))) ) { - ; - } else - if ( num_bonds ) { - goto out_of_RAM; - } - return pINChI_Stereo; - -out_of_RAM: - Free_INChI_Stereo( pINChI_Stereo ); - qzfree( pINChI_Stereo ); - } - return NULL; -} -/****************************************************************/ -int Free_INChI(INChI **ppINChI) -{ - INChI *pINChI; - - if ( pINChI = *ppINChI ) { -#if ( bREUSE_INCHI == 1 ) - if ( pINChI->nRefCount -- > 0 ) - return 1; -#endif - Free_INChI_Members(pINChI); - - qzfree( pINChI ); - *ppINChI = NULL; - - } - return 0; -} -/****************************************************************/ -int Free_INChI_Members(INChI *pINChI) -{ - if ( pINChI ) { - - Free_INChI_Stereo(pINChI->Stereo ); - Free_INChI_Stereo(pINChI->StereoIsotopic ); - qzfree(pINChI->nAtom ); - qzfree(pINChI->nConnTable ); - qzfree(pINChI->nTautomer ); - qzfree(pINChI->nNum_H ); - qzfree(pINChI->nNum_H_fixed ); - qzfree(pINChI->IsotopicAtom ); - qzfree(pINChI->IsotopicTGroup ); - qzfree(pINChI->nPossibleLocationsOfIsotopicH); - qzfree(pINChI->Stereo ); - qzfree(pINChI->StereoIsotopic ); - qzfree(pINChI->szHillFormula ); - } - return 0; -} -/****************************************************************/ -INChI *Alloc_INChI( inp_ATOM *at, int num_at, int *found_num_bonds, int *found_num_isotopic, int nAllocMode ) -{ - int i, num_bonds, num_isotopic_atoms; - INChI *pINChI; - int bIsotopic = (nAllocMode & REQ_MODE_ISO); - /* int bTautomeric = (nAllocMode & REQ_MODE_TAUT); */ - - if ( num_at <= 0 || NULL == (pINChI = (INChI *)inchi_calloc( 1, sizeof(INChI)))) { - return NULL; - } - - for ( i = 0, num_bonds = 0, num_isotopic_atoms = 0; i < num_at; i ++ ) { - num_bonds += at[i].valence; - /* if ( bIsotopic ) { */ - num_isotopic_atoms += (0 != at[i].iso_atw_diff || - !strcmp(at[i].elname, "D") || - !strcmp(at[i].elname, "T") || - at[i].num_iso_H[0] || - at[i].num_iso_H[1] || - at[i].num_iso_H[2]); - /* } */ - - } - num_bonds /= 2; - - *found_num_bonds = num_bonds; - *found_num_isotopic = num_isotopic_atoms; - - - if ( (pINChI->nAtom = (U_CHAR*) inchi_calloc( num_at, sizeof(pINChI->nAtom[0]))) && - (pINChI->nConnTable = (AT_NUMB*)inchi_calloc( num_at+num_bonds, sizeof(pINChI->nConnTable[0]))) && - (pINChI->nTautomer = (AT_NUMB*)inchi_calloc( ((3+INCHI_T_NUM_MOVABLE)*num_at)/2+1, sizeof(pINChI->nTautomer[0]))) && - (pINChI->nNum_H = (S_CHAR*) inchi_calloc( num_at, sizeof(pINChI->nNum_H[0]))) && - (pINChI->nNum_H_fixed= (S_CHAR*) inchi_calloc( num_at, sizeof(pINChI->nNum_H_fixed[0]))) - ) { - ; /* nTautomer length: max. number of tautomeric groups is num_at/2 - - 1 word -> number of t-groups - - each group has: - - 1 word -> number of endpoints+INCHI_T_NUM_MOVABLE - INCHI_T_NUM_MOVABLE words -> number(s) of moveable attachments - numbers of endpoints words -> canon. numbers - - max. occurs if each t-group has 2 atoms (num_at/2 t-groups) and all atoms - belong to t-groups (num_at endpoints) - - Total: 1 + (number of t-groups)*(1+INCHI_T_NUM_MOVABLE) + (number of endpoints) <= - 1 + (num_at/2) * (1+INCHI_T_NUM_MOVABLE) + num_at <= - 1 + (3+INCHI_T_NUM_MOVABLE)*num_at/2 words. - */ - - } else { - goto out_of_RAM; - } - pINChI->szHillFormula = NULL; /* the length is unknown */ - if ( bIsotopic ) { - if ( num_isotopic_atoms && - (pINChI->IsotopicAtom = (INChI_IsotopicAtom *)inchi_calloc(num_isotopic_atoms, sizeof(INChI_IsotopicAtom) )) && - (pINChI->IsotopicTGroup = (INChI_IsotopicTGroup *)inchi_calloc(num_isotopic_atoms, sizeof(INChI_IsotopicTGroup) )) - ) { - ; - } else - if ( num_isotopic_atoms ) { - goto out_of_RAM; - } - if ( !(pINChI->nPossibleLocationsOfIsotopicH = (AT_NUMB *)inchi_calloc( num_at+1, sizeof(pINChI->nPossibleLocationsOfIsotopicH[0]) ) ) ) { - goto out_of_RAM; - } - } - - if ((pINChI->Stereo = Alloc_INChI_Stereo(num_at, num_bonds)) - ) { - ; - } else { - goto out_of_RAM; - } - if ( bIsotopic ) { - if ((pINChI->StereoIsotopic = Alloc_INChI_Stereo(num_at, num_bonds)) - ) { - ; - } else { - goto out_of_RAM; - } - } - return pINChI; - -out_of_RAM: - if ( pINChI ) { - Free_INChI(&pINChI); - /* - inchi_free(pINChI); - */ - } - return NULL; -} -/****************************************************************/ -int Free_INChI_Aux( INChI_Aux **ppINChI_Aux ) -{ - INChI_Aux *pINChI_Aux = *ppINChI_Aux; - if ( pINChI_Aux ) { -#if ( bREUSE_INCHI == 1 ) - if ( pINChI_Aux->nRefCount -- > 0 ) - return 1; -#endif - - qzfree( pINChI_Aux->nOrigAtNosInCanonOrd ); - qzfree( pINChI_Aux->nIsotopicOrigAtNosInCanonOrd ); - qzfree( pINChI_Aux->nOrigAtNosInCanonOrdInv ); - qzfree( pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv ); - qzfree( pINChI_Aux->szOrigCoord ); - qzfree( pINChI_Aux->OrigInfo ); -/* - qzfree( pINChI_Aux->nOriginalAtomNumber ); - qzfree( pINChI_Aux->nCanonicalTGroupNumbers ); - qzfree( pINChI_Aux->nIsotopicCanonicalTGroupNumbers); - qzfree( pINChI_Aux->nTautomer ); - qzfree( pINChI_Aux->nNontautomericCanonicalNumbers ); - qzfree( pINChI_Aux->nIsotopicCanonicalNumbers ); - qzfree( pINChI_Aux->nNontautomericIsotopicCanonicalNumbers ); - qzfree( pINChI_Aux->nNontautomericEquNumbers ); - qzfree( pINChI_Aux->nNontautomericIsotopicEquNumbers ); -*/ - qzfree( pINChI_Aux->nConstitEquNumbers ); - qzfree( pINChI_Aux->nConstitEquTGroupNumbers ); - qzfree( pINChI_Aux->nConstitEquIsotopicNumbers ); - qzfree( pINChI_Aux->nConstitEquIsotopicTGroupNumbers ); - qzfree( pINChI_Aux ); - *ppINChI_Aux = NULL; - } - return 0; -} -/****************************************************************/ -INChI_Aux *Alloc_INChI_Aux( int num_at, int num_isotopic_atoms, int nAllocMode, int bOrigCoord ) -{ - INChI_Aux *pINChI_Aux; - int bIsotopic = (nAllocMode & REQ_MODE_ISO); - int num_at_tg = num_at + num_at/2; - /* int bTautomeric = (nAllocMode & REQ_MODE_TAUT); */ - - if ( num_at <= 0 || NULL == (pINChI_Aux = (INChI_Aux *)inchi_calloc(sizeof(INChI_Aux), 1))) { - return NULL; - } - if ( (pINChI_Aux->nOrigAtNosInCanonOrd = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nOrigAtNosInCanonOrd[0]), num_at_tg)) && - (pINChI_Aux->nOrigAtNosInCanonOrdInv = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nOrigAtNosInCanonOrd[0]), num_at_tg)) && - (pINChI_Aux->nConstitEquNumbers = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nConstitEquNumbers[0]), num_at_tg)) ) { - ; - } else { - goto out_of_RAM; - } - - if ( num_at > 1 && - (pINChI_Aux->nConstitEquTGroupNumbers = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nConstitEquTGroupNumbers[0]), num_at/2+1)) ) { - ; - } else - if ( num_at > 1 ) { - goto out_of_RAM; - } - - if ( num_at > 0 ) { - pINChI_Aux->OrigInfo = (ORIG_INFO *)inchi_calloc(sizeof(pINChI_Aux->OrigInfo[0]), num_at); - if ( !pINChI_Aux->OrigInfo ) - goto out_of_RAM; - } - if ( bOrigCoord && num_at > 0 ) { - pINChI_Aux->szOrigCoord = (MOL_COORD *)inchi_calloc(sizeof(pINChI_Aux->szOrigCoord[0]), num_at); - if ( !pINChI_Aux->szOrigCoord ) - goto out_of_RAM; - } - if ( bIsotopic ) { - if ( /*num_isotopic_atoms &&*/ - (pINChI_Aux->nIsotopicOrigAtNosInCanonOrd = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nIsotopicOrigAtNosInCanonOrd[0]), num_at_tg)) && - (pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nIsotopicOrigAtNosInCanonOrd[0]), num_at_tg)) && - (pINChI_Aux->nConstitEquIsotopicNumbers = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nConstitEquIsotopicNumbers[0]), num_at_tg)) ) { - ; - } else - if ( num_isotopic_atoms ) { - goto out_of_RAM; - } - if ( /*num_isotopic_atoms && num_at > 1 &&*/ - (pINChI_Aux->nConstitEquIsotopicTGroupNumbers = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nConstitEquIsotopicTGroupNumbers[0]), num_at/2+1)) ) { - ; - } else - if ( num_isotopic_atoms && num_at > 1 ) { - goto out_of_RAM; - } - } - return pINChI_Aux; - - -out_of_RAM: - if ( pINChI_Aux ) { - Free_INChI_Aux(&pINChI_Aux); - /* - inchi_free(pINChI_Aux); - */ - } - return NULL; -} -/***********************************************************************************/ - -#define IS_DEUTERIUM(i) (!strcmp( at[i].elname, "D" ) || at[i].iso_atw_diff == 2 && !strcmp( at[i].elname, "H" )) -#define IS_TRITIUM(i) (!strcmp( at[i].elname, "T" ) || at[i].iso_atw_diff == 3 && !strcmp( at[i].elname, "H" )) - -#define ABNORMAL_ISO(i) (at[i].iso_atw_diff == 1 || at[i].iso_atw_diff < -3 || at[i].iso_atw_diff > 5 ) -#define ABNORMAL_CHG(i) (abs(at[i].charge) > 3) -#define ABNORMAL_RAD(i) (RADICAL_SINGLET <= at[i].radical && at[i].radical <= RADICAL_TRIPLET ) - -#define ANY_ISO(i, X) ((X)? (at[i].iso_atw_diff && !IS_DEUTERIUM(i) && !IS_TRITIUM(i)) :\ - (at[i].iso_atw_diff || IS_DEUTERIUM(i) || IS_TRITIUM(i))) -#define ANY_CHG(i) (0 != at[i].charge) -#define ANY_RAD(i) (RADICAL_SINGLET <= at[i].radical && at[i].radical <= RADICAL_TRIPLET ) - -#define NORMAL_ISO(i, X) (ANY_ISO(i, X) && !ABNORMAL_ISO(i)) - - -/* needs additional M CHG. M RAD, M ISO line */ -/* due to ISIS/Draw feature always include M RAD for any radical */ -#define ABNORMAL_AT(i) ( at[i].radical || abs(at[i].charge) > 3 || \ - ABNORMAL_ISO(i) ) - -/* always add M ISO, M RAD, M CHG; Except: (bAtomsDT && D or T) */ -#define ADD_LINE_AT(i) ( at[i].charge || \ - at[i].radical || \ - at[i].iso_atw_diff && (bAtomsDT? (at[i].iso_atw_diff != 1 || strcmp(at[i].elname, "H")) : 1) ) -#define ALIASED_AT(i) (0 < NUM_ISO_H(at, i)) -/***********************************************************************************/ -#if ( TEST_RENUMB_ATOMS_SAVE_LONGEST == 1 || TEST_RENUMB_SWITCH == 1 ) -int WriteToSDfile( const INP_ATOM_DATA *inp_at_data, INCHI_IOSTREAM* fcb, const char* name, const char* comment, - const char *szLabel, const char *szValue) -{ - int i, j, k, num_bonds=0, ret=0, bAtomsDT = 1 /* treat D, T as normal atoms */, bV2000 = 0 /*V2000 Molfile */; - int bAtomNeedsAlias; - int flag_bad_charge=0, flag_bad_iso=0, nNumAddLines=0, nNumIsoLines=0, nNumChargeLines=0, nNumRadicalLines=0, nNumAliasLines=0; - int nNumNecessaryIsoLines = 0, nNumNecessaryChgLines = 0, nNumNecessaryRadLines = 0; - /*sp_ATOM *at; */ - /*float fzero=0.0F;*/ - double x, y, z; - int bNext /*, s*/; - const inp_ATOM *at = inp_at_data->at_fixed_bonds? inp_at_data->at_fixed_bonds : inp_at_data->at; - int num_atoms = inp_at_data->num_at; - /*at = species->atom;*/ - - /*inchi_ios_eprint(fcb,"%ld.MOL\n",species->casno);*/ - { /* block start */ - char strLocName[82]; - memset(strLocName, 0, sizeof(strLocName) ); - if ( name && *name ) { - strncpy( strLocName, name, 80 ); - } - inchi_ios_print_nodisplay( fcb,"%s\n", strLocName ); - } /* block end */ - /**********************************************************************/ - /** **/ - /** Important: Atoms with alias cannot have charge, radical, or **/ - /** isotope differences. **/ - /** **/ - /** Atoms with alias cannot be abnormal. **/ - /** **/ - /** Abnormal atoms are atoms which need M CHG, M RAD, M ISO **/ - /** **/ - /**********************************************************************/ - - /* F10.5 F12.5 I6 - IIPPPPPPPPMMDDYYHHmmddSSssssssssssEEEEEEEEEEEERRRRRR - inchi_ios_eprint( fcb,"NISTTRANHP09089809272D 1 1.0 0.0 %6ld\n", lEpa);*/ - - /*^^^ - inchi_ios_print_nodisplay( fcb," -%s v%s SDfile Output \n", INCHI_NAME, INCHI_VERSION); - - Changed 01/10/2009 to conform CTFile specification (by Symyx request)*/ - inchi_ios_print_nodisplay( fcb, - /* IIPPPPPPPPMMDDYYHHmmddSSssssssssssEEEEEEEEEEEERRRRRR*/ - " InChIV10 \n"); - - - - /*y_fprintf(fcb, " -CPSS- 1213981200n\n");*/ - - { /*block start*/ - char strLocName[82]; - - memset(strLocName, 0, sizeof(strLocName) ); - if ( comment && *comment ) { - strncpy( strLocName, comment, 80 ); - } - inchi_ios_print_nodisplay( fcb,"%s\n", strLocName ); - } /*block end*/ - for (i=0; i< num_atoms; i++) - num_bonds += at[i].valence; - num_bonds /= 2; - - /*find if we need "M CHG", "M RAD", "M ISO" */ - for (i=0, nNumAddLines = 0; i < num_atoms; i++) { - if ( bAtomNeedsAlias = ALIASED_AT(i) ) { - nNumAliasLines += 2 * bAtomNeedsAlias; - } else { - nNumNecessaryIsoLines += ABNORMAL_ISO(i); - nNumNecessaryChgLines += ABNORMAL_CHG(i); - nNumNecessaryRadLines += ABNORMAL_RAD(i); - nNumIsoLines += ANY_ISO(i, bAtomsDT); - nNumChargeLines += ANY_CHG(i); - nNumRadicalLines += ANY_RAD(i); - } - } - if ( !bV2000 ) { - if ( !nNumNecessaryRadLines && !nNumNecessaryChgLines ) { - nNumRadicalLines = 0; - nNumChargeLines = 0; - } - if ( !nNumNecessaryIsoLines ) { - nNumIsoLines = 0; - } - } - - - /* count additional M lines*/ - nNumChargeLines = ( nNumChargeLines + 7 ) / 8; - nNumRadicalLines = ( nNumRadicalLines + 7 ) / 8; - nNumIsoLines = ( nNumIsoLines + 7 ) / 8; - - nNumAddLines = nNumChargeLines + nNumRadicalLines + nNumIsoLines + nNumAliasLines; /* 1 for M END*/ - - if ( nNumAddLines || bV2000 ) { - nNumAddLines += 1; /* add 1 for "M END" line*/ - } - - /* aaabbblllfffcccsssxxxrrrpppiiimmmvvvvvv*/ - inchi_ios_print_nodisplay(fcb,"%3d%3d 0 0 0 0 0 0 0 0%3d%s\n",num_atoms, num_bonds, nNumAddLines,nNumAddLines?" V2000":""); - /* atoms block*/ - for (i=0; i < num_atoms; i++) { - char elname[ATOM_EL_LEN]; - int iso = 0; - int charge = 0; - int valence = 0; - int nIsotopeH = IS_DEUTERIUM(i)? 1 : IS_TRITIUM(i)? 2 : 0; - bAtomNeedsAlias = ALIASED_AT(i); /* Has implicit D and/or T neighbors */ - memset( elname, 0, sizeof(elname) ); - - if ( bAtomNeedsAlias ) { - /* alias */ - strcpy ( elname, "C" ); - } else { - /* isotope*/ - if ( nIsotopeH ) { - strcpy( elname, bAtomsDT? ( nIsotopeH==1? "D" : "T" ) : "H" ); - } else { - strncpy ( elname, at[i].elname, sizeof(elname)-1 ); - } - if ( !ABNORMAL_CHG(i) && !ANY_RAD(i) ) { - /* charge*/ - /* Only atoms without alias can be here*/ - switch ( at[i].charge ) { - case 3: charge = 1; break; - case 2: charge = 2; break; - case 1: charge = 3; break; - case -1: charge = 5; break; - case -2: charge = 6; break; - case -3: charge = 7; break; - case 0: charge = 0; break; - default: flag_bad_charge = 1; break; - }; - } - /* radical*/ - if ( ANY_RAD(i) && !ANY_CHG(i) ) { - if ( at[i].radical == RADICAL_DOUBLET ) { - charge = 4; - } - } - } - /* allow isotopic shift for aliased atoms */ - if ( NORMAL_ISO(i, bAtomsDT) ) { - iso = at[i].iso_atw_diff > 0? at[i].iso_atw_diff-1: - at[i].iso_atw_diff < 0? at[i].iso_atw_diff : - nIsotopeH? nIsotopeH : (flag_bad_iso ++, 0); - } - - x = at[i].x; - y = at[i].y; - z = at[i].z; - - if( at[i].num_H > 0 ) { - for ( j = 0, valence = 0; j < at[i].valence; j++ ) { - switch( k = at[i].bond_type[j] ) { /* fixed valence calculation 12-23-99 DCh.*/ - case 2: - case 3: - valence += 2*k; - break; - case 4: - valence += 3; - break; - default: - valence += 2; - } - } - valence = valence/2 + at[i].num_H; - } else - /* Added 07-09-2003 DCh*/ - if ( at[i].chem_bonds_valence > 0 ) { - valence = at[i].chem_bonds_valence; - } else - /* Added 07-09-2003 DCh*/ - if ( !at[i].valence && !at[i].num_H && !at[i].chem_bonds_valence ) { - valence = 15; - } - /*inchi_ios_eprint(fcb,"%10.4f%10.4f%10.4f %-3.3s%2d%3d 0 0 0 0 0 0 0\n",*/ - /* (float)at[i].x, (float)(-at[i].y), fzero, at[i].elname, iso, charge);*/ - /* xxxxxxyyyyyyzzzzzz aaa____ddcccsssnnnbbbvvvrrriiimmmeee */ - inchi_ios_print_nodisplay(fcb,"%10.4f%10.4f%10.4f %-3.3s%2d%3d 0 0%3d 0 0 0 0\n", - x, y, z, elname, (int)iso, (int)charge, valence /* at[i].special*/); - /* reflect image against x-axis; - when transforming MOLfile back to STDATA in mol_to_stdata(...), - make one more reflection to restore original orientation. - Reason: in MS Search y-axis is directed from top to bottom, - while in MOLfile y-axis goes from bottom to top. - */ - } - bNext = 0; /* debug only*/ - - /* bonds*/ - for (i=0; i< num_atoms; i++) { - for (j=0; j 1 ) { - sprintf( str_m + strlen(str_m), "%d", num_H ); - } - } - if ( num_H = at[i].num_iso_H[1] ) { /* deuterium */ - strcat( str_m, "D" ); - if ( num_H > 1 ) { - sprintf( str_m + strlen(str_m), "%d", num_H ); - } - } - if ( num_H = at[i].num_iso_H[2] ) { /* Tritium */ - strcat( str_m, "T" ); - if ( num_H > 1 ) { - sprintf( str_m + strlen(str_m), "%d", num_H ); - } - } - /* Add charge to the Alias */ - if ( at[i].charge){ - strcat(str_m, at[i].charge>0? "+" : "-"); - if ( 1 < (j=abs(at[i].charge)) ) - sprintf( str_m+strlen(str_m), "%d", j ); - } - /* Add radical to the Alias */ - switch( at[i].radical ) { - case RADICAL_SINGLET: - strcat( str_m, ":" ); - break; - case RADICAL_DOUBLET: - strcat( str_m, "^" ); - break; - case RADICAL_TRIPLET: - strcat( str_m, "^^" ); - break; - } - inchi_ios_print_nodisplay( fcb, "%s\n", str_m ); - num_m ++; - } - } - if ( num_m != nNumAliasLines ) { - /* error in lines counting*/ - ret ++; - } - } - /* charges*/ - str_m[0] = 0; - num_m = 0; - if ( nNumChargeLines ) { - for (i=0; i < num_atoms; i++) { - if ( ANY_CHG(i) && !ALIASED_AT(i) ) { - sprintf( entry, " %3d %3d", i+1, (int)at[i].charge ); - strcat( str_m, entry ); - num_m ++; - } - if ( i == num_atoms-1 && num_m || num_m == 8 ) { - inchi_ios_print_nodisplay( fcb, "M CHG%3d%s\n", num_m, str_m ); - str_m[0] = 0; - num_m = 0; - } - } - } - /* radicals*/ - str_m[0] = 0; - num_m = 0; - if ( nNumRadicalLines ) { - for (i=0; i < num_atoms; i++) { - if ( ANY_RAD(i) && !ALIASED_AT(i) ) { - int radical = (at[i].radical==RADICAL_SINGLET || - at[i].radical==RADICAL_DOUBLET || - at[i].radical==RADICAL_TRIPLET)? at[i].radical : 0; - if ( radical ) { - sprintf( entry, " %3d %3d", i+1, radical ); - strcat( str_m, entry ); - num_m ++; - } - } - if ( i == num_atoms-1 && num_m || num_m == 8 ) { - inchi_ios_print_nodisplay( fcb, "M RAD%3d%s\n", num_m, str_m ); - str_m[0] = 0; - num_m = 0; - } - } - } - /* isotopes*/ - str_m[0] = 0; - num_m = 0; - if ( nNumIsoLines ) { - int el_num, iso; - for (i=0; i < num_atoms; i++) { - if ( ANY_ISO(i,bAtomsDT) && !ALIASED_AT(i) ) { - if ( IS_DEUTERIUM(i) ) { - iso = 1; - el_num = 1; - } else - if ( IS_TRITIUM(i) ) { - iso = 2; - el_num = 1; - } else { - iso = at[i].iso_atw_diff > 0? at[i].iso_atw_diff-1 : at[i].iso_atw_diff; - el_num = at[i].el_number; - } - iso += get_atw_from_elnum( el_num ); - - sprintf( entry, " %3d %3d", i+1, iso ); - strcat( str_m, entry ); - num_m ++; - } - - if ( i == num_atoms-1 && num_m || num_m == 8 ) { - inchi_ios_print_nodisplay( fcb, "M ISO%3d%s\n", num_m, str_m ); - str_m[0] = 0; - num_m = 0; - } - } - } - inchi_ios_print_nodisplay( fcb, "M END\n" ); - } - if ( szValue && szValue[0] ) { - if ( szLabel && szLabel[0] ) { - inchi_ios_print_nodisplay( fcb, "> <%s>\n", szLabel ); - } else { - inchi_ios_print_nodisplay( fcb, "> \n" ); - } - inchi_ios_print_nodisplay( fcb, "%s\n\n", szValue ); - } - inchi_ios_print_nodisplay(fcb, "$$$$\n"); - - - return ret; - -} -#endif -/***************************************************************************************************/ -int WriteOrigAtomDataToSDfile(const ORIG_ATOM_DATA *inp_at_data, INCHI_IOSTREAM * fcb, const char* name, - const char* comment, int bChiralFlag, int bAtomsDT, const char *szLabel, const char *szValue) -{ - int i, j, k, num_bonds=0, ret=0; - int bAtomNeedsAlias; - int flag_bad_charge=0, flag_bad_iso = 0; - int nNumAddLines=0, nNumIsoLines=0, nNumChargeLines=0, nNumRadicalLines=0, nNumAliasLines=0; - int nNumNecessaryIsoLines = 0, nNumNecessaryChgLines = 0, nNumNecessaryRadLines = 0; - int bV2000 = SDF_OUTPUT_V2000; - double x, y, z; - int bNext /*, s*/; - const inp_ATOM *at = inp_at_data->at; - int num_atoms = inp_at_data->num_inp_atoms; - /*at = species->atom;*/ - - /*inchi_ios_eprint(fcb,"%ld.MOL\n",species->casno);*/ - { /* block start */ - char strLocName[82]; - memset(strLocName, 0, sizeof(strLocName) ); - if ( name && *name ) { - strncpy( strLocName, name, 80 ); - /* --- debug only --- - if ( strstr( name, "#3959" ) ) { - int stop = 1; - } - */ - } - inchi_ios_print_nodisplay( fcb,"%s\n", strLocName ); - } /* block end */ - /**********************************************************************/ - /** **/ - /** Important: Atoms with alias cannot have charge, radical **/ - /** isotope differences are allowed **/ - /** **/ - /** Atoms with alias cannot be abnormal. **/ - /** **/ - /** Abnormal atoms are atoms which need M CHG, M RAD, M ISO **/ - /** **/ - /** Output aliased atoms if they have implicit D or T **/ - /** **/ - /**********************************************************************/ - - /* F10.5 F12.5 I6 - IIPPPPPPPPMMDDYYHHmmddSSssssssssssEEEEEEEEEEEERRRRRR - inchi_ios_eprint( fcb,"NISTTRANHP09089809272D 1 1.0 0.0 %6ld\n", lEpa);*/ - /*^^^ - inchi_ios_print_nodisplay( fcb," %s v%s SDfile Output \n", INCHI_NAME, INCHI_VERSION); - - Changed 01/10/2009 to conform CTFile specification (by Symyx request)*/ - inchi_ios_print_nodisplay( fcb, - /* IIPPPPPPPPMMDDYYHHmmddSSssssssssssEEEEEEEEEEEERRRRRR*/ - " InChIV10 \n"); - /*y_fprintf(fcb, " -CPSS- 1213981200n\n");*/ - - { /*block start*/ - char strLocName[82]; - - memset(strLocName, 0, sizeof(strLocName) ); - if ( comment && *comment ) { - strncpy( strLocName, comment, 80 ); - } - inchi_ios_print_nodisplay( fcb,"%s\n", strLocName ); - } /*block end*/ - for (i=0; i< num_atoms; i++) - num_bonds += at[i].valence; - num_bonds /= 2; - - /*find if we need "M CHG" and "M RAD"*/ - for (i=0; i < num_atoms; i++) { - if ( bAtomNeedsAlias = ALIASED_AT(i) ) { /* has isotopic implicit D or T; ignoring pure 1H */ - nNumAliasLines += 2 * bAtomNeedsAlias; - } else { - /* abnormal means atom needs CHG, RAD, or ISO entry */ - /* nNumAddLines += ABNORMAL_AT(i); */ - /* nNumIso += ( 0 == strcmp( at[i].elname, "D" ) || ( 0 == strcmp( at[i].elname, "T" ) || at[i].iso_atw_diff ) ); */ - /* nNumAddIso += at[i].iso_atw_diff && (at[i].iso_atw_diff == 1 || at[i].iso_atw_diff < -3 || at[i].iso_atw_diff > 5 ); */ - nNumNecessaryIsoLines += ABNORMAL_ISO(i); - nNumNecessaryChgLines += ABNORMAL_CHG(i); - nNumNecessaryRadLines += ABNORMAL_RAD(i); - nNumIsoLines += ANY_ISO(i, bAtomsDT); - nNumChargeLines += ANY_CHG(i); - nNumRadicalLines += ANY_RAD(i); - } - } - nNumChargeLines = ( nNumChargeLines + 7 ) / 8; - nNumRadicalLines = ( nNumRadicalLines + 7 ) / 8; - nNumIsoLines = ( nNumIsoLines + 7 ) / 8; - - if ( !bV2000 ) { - if ( !nNumNecessaryRadLines && !nNumNecessaryChgLines ) { - nNumRadicalLines = 0; - nNumChargeLines = 0; - } - if ( !nNumNecessaryIsoLines ) { - nNumIsoLines = 0; - } - } - - - /* recalculate number of added lines */ - nNumAddLines = nNumChargeLines + nNumRadicalLines + nNumIsoLines + nNumAliasLines; /* 1 for M END*/ - - if ( nNumAddLines || bV2000 ) { - nNumAddLines += 1; /* add 1 for "M END" line*/ - } - - /* aaabbblllfffcccsssxxxrrrpppiiimmmvvvvvv*/ - inchi_ios_print_nodisplay(fcb,"%3d%3d 0 0%3d 0 0 0 0 0%3d%s\n", - num_atoms, num_bonds, bChiralFlag?1:0, nNumAddLines,nNumAddLines?" V2000":""); - /* atoms block*/ - for (i=0; i < num_atoms; i++) { - char elname[ATOM_EL_LEN] = "\0\0\0\0\0"; - int iso = 0; - int charge = 0; - int valence = 0; - int nIsotopeH = IS_DEUTERIUM(i)? 1 : IS_TRITIUM(i)? 2 : 0; - int bonds_val; - bAtomNeedsAlias = ALIASED_AT(i); - memset( elname, 0, sizeof(elname) ); - - if ( bAtomNeedsAlias ) { - /* alias */ - strcpy ( elname, "C" ); - } else { - /* isotope*/ - if ( nIsotopeH ) { - strcpy( elname, bAtomsDT? ( nIsotopeH==1? "D" : "T" ) : "H" ); - } else { - strncpy ( elname, at[i].elname, sizeof(elname)-1 ); - } - if ( !ABNORMAL_CHG(i) && !ANY_RAD(i) ) { - /* charge*/ - /* Only atoms without alias can be here*/ - switch ( at[i].charge ) { - case 3: charge = 1; break; - case 2: charge = 2; break; - case 1: charge = 3; break; - case -1: charge = 5; break; - case -2: charge = 6; break; - case -3: charge = 7; break; - case 0: charge = 0; break; - default: flag_bad_charge = 1; break; - }; - } - /* radical*/ - if ( ANY_RAD(i) && !ANY_CHG(i) ) { - if ( at[i].radical == RADICAL_DOUBLET ) { - charge = 4; - } - } - } - /* allow isotopic shift for aliased atoms */ - if ( NORMAL_ISO(i, bAtomsDT) ) { - iso = at[i].iso_atw_diff > 0? at[i].iso_atw_diff-1: - at[i].iso_atw_diff < 0? at[i].iso_atw_diff : - nIsotopeH? nIsotopeH : (flag_bad_iso ++, 0); - } - - x = at[i].x; - y = at[i].y; - z = at[i].z; - - /* valence -- set only if needed */ - bonds_val = nBondsValenceInpAt( at+i, NULL, NULL ); - valence=needed_unusual_el_valence( at[i].el_number, at[i].charge, at[i].radical, - at[i].chem_bonds_valence, bonds_val, NUMH(at, i), at[i].valence ); - if ( valence < 0 ) { - valence = 15; /* means no bonds nor H */ - } - - /*inchi_ios_eprint(fcb,"%10.4f%10.4f%10.4f %-3.3s%2d%3d 0 0 0 0 0 0 0\n",*/ - /* (float)at[i].x, (float)(-at[i].y), fzero, at[i].elname, iso, charge);*/ - /* xxxxxxyyyyyyzzzzzz aaa____ddcccsssnnnbbbvvvrrriiimmmeee */ - inchi_ios_print_nodisplay(fcb,"%10.4f%10.4f%10.4f %-3.3s%2d%3d 0 0%3d 0 0 0 0\n", - x, y, z, elname, (int)iso, (int)charge, valence /* at[i].special*/); - /* reflect image against x-axis; - when transforming MOLfile back to STDATA in mol_to_stdata(...), - make one more reflection to restore original orientation. - Reason: in MS Search y-axis is directed from top to bottom, - while in MOLfile y-axis goes from bottom to top. - */ - } - bNext = 0; /* debug only*/ - - /* bonds*/ - for (i=0; i< num_atoms; i++) { - for (j=0; j0? "+" : "-"); - if ( 1 < (j=abs(at[i].charge)) ) { - len += sprintf( str_m+len, "%d", j ); - } - } - /* Add radical to the Alias */ - if ( at[i].radical == RADICAL_SINGLET ) { - len += sprintf( str_m+len, "%s", ":" ); - } else - if ( at[i].radical == RADICAL_DOUBLET ) { - len += sprintf( str_m+len, "%s", "^" ); - } else - if ( at[i].radical == RADICAL_TRIPLET ) { - len += sprintf( str_m+len, "%s", "^^" ); - } - inchi_ios_print_nodisplay( fcb, "%s\n", str_m ); - num_m ++; - } - } - if ( num_m != nNumAliasLines ) { - /* error in lines counting*/ - ret ++; - } - } - /* charges*/ - str_m[0] = 0; - num_m = 0; - if ( nNumChargeLines ) { - for (i=0; i < num_atoms; i++) { - if ( at[i].charge && !ALIASED_AT(i) ) { - sprintf( entry, " %3d %3d", i+1, (int)at[i].charge ); - strcat( str_m, entry ); - num_m ++; - } - if ( i == num_atoms-1 && num_m || num_m == 8 ) { - inchi_ios_print_nodisplay( fcb, "M CHG%3d%s\n", num_m, str_m ); - str_m[0] = 0; - num_m = 0; - } - } - } - /* radicals*/ - str_m[0] = 0; - num_m = 0; - if ( nNumRadicalLines ) { - for (i=0; i < num_atoms; i++) { - if ( at[i].radical && !ALIASED_AT(i) ) { - int radical = (at[i].radical==RADICAL_SINGLET || - at[i].radical==RADICAL_DOUBLET || - at[i].radical==RADICAL_TRIPLET)? at[i].radical : 0; - if ( radical ) { - sprintf( entry, " %3d %3d", i+1, radical ); - strcat( str_m, entry ); - num_m ++; - } - } - if ( i == num_atoms-1 && num_m || num_m == 8 ) { - inchi_ios_print_nodisplay( fcb, "M RAD%3d%s\n", num_m, str_m ); - str_m[0] = 0; - num_m = 0; - } - } - } - /* isotopes*/ - str_m[0] = 0; - num_m = 0; - if ( nNumIsoLines ) { - int el_num, iso; - for (i=0; i < num_atoms; i++) { - /* - if ( 0 == strcmp( at[i].elname, "D" ) ) { - sprintf( entry, " %3d %3d", i+1, 2 ); - strcat( str_m, entry ); - num_m ++; - } else - if ( 0 == strcmp( at[i].elname, "T" ) ) { - sprintf( entry, " %3d %3d", i+1, 3 ); - strcat( str_m, entry ); - num_m ++; - } else - if ( k = at[i].iso_atw_diff ) { - int mw = get_atw_from_elnum( at[i].el_number ); - mw += (k > 0)? k-1 : k; - sprintf( entry, " %3d %3d", i+1, mw ); - strcat( str_m, entry ); - num_m ++; - } - */ - if ( ANY_ISO(i, bAtomsDT) && !ALIASED_AT(i) ) { - if ( IS_DEUTERIUM(i) ) { - iso = 1; - el_num = 1; - } else - if ( IS_TRITIUM(i) ) { - iso = 2; - el_num = 1; - } else { - iso = at[i].iso_atw_diff > 0? at[i].iso_atw_diff-1 : at[i].iso_atw_diff; - el_num = at[i].el_number; - } - iso += get_atw_from_elnum( el_num ); - - sprintf( entry, " %3d %3d", i+1, iso ); - strcat( str_m, entry ); - num_m ++; - } - - - if ( i == num_atoms-1 && num_m || num_m == 8 ) { - inchi_ios_print_nodisplay( fcb, "M ISO%3d%s\n", num_m, str_m ); - str_m[0] = 0; - num_m = 0; - } - } - } - inchi_ios_print_nodisplay( fcb, "M END\n" ); - } - if ( szValue && szValue[0] ) { - if ( szLabel && szLabel[0] ) { - inchi_ios_print_nodisplay( fcb, "> <%s>\n", szLabel ); - } else { - inchi_ios_print_nodisplay( fcb, "> \n" ); - } - inchi_ios_print_nodisplay( fcb, " %s\n\n", szValue ); - } - inchi_ios_print_nodisplay(fcb, "$$$$\n"); - - - return ret; - -} -#if ( FIX_ADJ_RAD == 1 ) -/*************************************************************************/ -int FixNextRadicals( int cur_at, inp_ATOM *at ); -int FixNextRadicals( int cur_at, inp_ATOM *at ) -{ - int j, neigh, num_found = 0; - for ( j = 0; j < at[cur_at].valence; j ++ ) { - neigh = at[cur_at].neighbor[j]; - if ( at[neigh].radical == RADICAL_DOUBLET ) { - at[neigh].radical = 0; - num_found ++; - num_found += FixNextRadicals( neigh, at ); - } - } - return num_found; -} -/*************************************************************************/ -int FixAdjacentRadicals( int num_inp_atoms, inp_ATOM *at ) -{ - int i, j; - char *bVisited = NULL; - int nNumFound = 0, neigh, cur_found; - for ( i = 0; i < num_inp_atoms; i ++ ) { - if ( at[i].radical == RADICAL_DOUBLET ) { - cur_found = 1; - for ( j = 0; j < at[i].valence; j ++ ) { - neigh = at[i].neighbor[j]; - if ( at[neigh].radical == RADICAL_DOUBLET ) { - cur_found ++; - } - } - if ( cur_found >= 3 ) { - nNumFound ++; - at[i].radical = 0; - nNumFound += FixNextRadicals( i, at ); - } - } - } - return nNumFound; -} -#endif - -#ifdef COMPILE_ANSI_ONLY -#ifndef TARGET_API_LIB -/* -#include -#include "inpdef.h" -*/ -void PrintFileName( const char *fmt, - FILE *output_file, /* INCHI_IOSTREAM *output_file, */ - const char *szFname ) -{ - inchi_print_nodisplay( output_file, fmt, szFname ); -} -#endif -#endif - +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include +#include +#include + +#include "mode.h" + +#include "strutil.h" +#include "ichister.h" +#include "ichi_io.h" +#include "ichimain.h" + +/* Added fix to remove_ion_pairs() -- 2010-03-17 DT */ +#define FIX_P_IV_Plus_O_Minus + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + +/* Defined in ichisort.c, prototype in ichicomn.h */ +int insertions_sort_AT_RANK( AT_RANK *base, int num ); + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + + +typedef struct tagTreeAtom { + AT_NUMB neighbor[MAXVAL]; /* positions (from 0) of the neighbors in the inp_ATOM array */ + S_CHAR valence; /* number of bonds = number of neighbors */ + AT_NUMB nRingSystem; + AT_NUMB nBlockSystem; + S_CHAR bCutVertex; +} tre_ATOM; + + +/* Local prototypes */ + +int cmp_iso_atw_diff_component_no( const void *a1, const void *a2 ); +int cmp_components( const void *a1, const void *a2 ); +/* int mark_one_struct_component( inp_ATOM* at, + int j, + AT_NUMB *mark, + AT_NUMB num_disconnected_components ); +*/ +INChI_Stereo *Alloc_INChI_Stereo(int num_at, int num_bonds); +int RemoveInpAtBond( inp_ATOM *at, int iat, int k ); +int DisconnectInpAtBond( inp_ATOM *at, + AT_NUMB *nOldCompNumber, + int iat, + int neigh_ord ); +int move_explicit_Hcation( inp_ATOM *at, + int num_at, + int iat, + int iat_H, + int bInAllComponents); +int DisconnectOneLigand( inp_ATOM *at, + AT_NUMB *nOldCompNumber, + S_CHAR *bMetal, + char *elnumber_Heteroat, + int num_halogens, + int num_atoms, + int iMetal, + int jLigand, + INCHI_MODE *bTautFlagsDone ); +int bIsAmmoniumSalt( inp_ATOM *at, + int i, + int *piO, + int *pk, + S_CHAR *num_explicit_H ); +int DisconnectAmmoniumSalt ( inp_ATOM *at, + int i, + int iO, + int k, + S_CHAR *num_explicit_H ); +/*int bIsMetalSalt( inp_ATOM *at, int i ); - moved to strutil,h */ +int DisconnectMetalSalt( inp_ATOM *at, int i ); +int bIsMetalToDisconnect( inp_ATOM *at, int i, int bCheckMetalValence); +int get_iat_number( int el_number, const int el_num[], int el_num_len ); +int tot_unsat( int unsat[] ); +int max_unsat( int unsat[] ); +double dist3D( inp_ATOM *at1, inp_ATOM *at2 ); +double dist2D( inp_ATOM *at1, inp_ATOM *at2 ); +double dist_from_segm( double x, double y, + double x1, double y1, + double x2, double y2); +int segments_intersect( double x11, double y11, + double x12, double y12, /* segment #1 */ + double x21, double y21, + double x22, double y22 ); +double GetMinDistDistribution( inp_ATOM *at, + int num_at, + int iat, + int iat_H, + int bInAllComponents, + double min_dist[], + int num_segm ); +int nFindOneOM( inp_ATOM *at, + int at_no, + int ord_OM[], + int num_OM); +int the_only_doublet_neigh( inp_ATOM *at, int i1, int *ineigh1, int *ineigh2); +int fix_non_uniform_drawn_oxoanions( int num_atoms, inp_ATOM *at, int *num_changes ); +int fix_non_uniform_drawn_amidiniums( int num_atoms, inp_ATOM *at, int *num_changes ); + + +void add_bond_if_unseen( subgraf_pathfinder *spf, + int node0, int node, + int *nbonds, int **bonds); + +/************************************************************************/ +#ifndef NUMH +#define NUM_ISO_H(AT,N) (AT[N].num_iso_H[0]+AT[N].num_iso_H[1]+AT[N].num_iso_H[2]) +#define NUMH(AT,N) (AT[N].num_H+NUM_ISO_H(AT,N)) +#endif +/************************************************************************/ + +/********************************************************************************/ +int cmp_iso_atw_diff_component_no( const void *a1, const void *a2 ) +{ + int ret = (int)((const inp_ATOM*)a1)->iso_atw_diff - (int)((const inp_ATOM*)a2)->iso_atw_diff; + if ( !ret ) /* make the sort stable */ + ret = (int)((const inp_ATOM*)a1)->component - (int)((const inp_ATOM*)a2)->component; + return ret; +} + + +int the_only_doublet_neigh( inp_ATOM *at, + int i1, + int *ineigh1, + int *ineigh2) +{ +int i, neigh1, num_rad1=0, num_rad2=0; + + inp_ATOM *a = at+i1, *b; + if ( RADICAL_DOUBLET != a->radical ) + return -1; + for ( i = 0; i < a->valence; i ++ ) + { + b = at + (neigh1 = (int)a->neighbor[i]); + if ( RADICAL_DOUBLET == b->radical ) + { + num_rad1 ++; + *ineigh1 = i; + } + } + + if ( 1 == num_rad1 ) + { + a = at + (neigh1 = (int)a->neighbor[*ineigh1]); + for ( i = 0; i < a->valence; i ++ ) { + b = at +(int)a->neighbor[i]; + if ( RADICAL_DOUBLET == b->radical ) + { + num_rad2 ++; + *ineigh2 = i; + } + } + + if ( 1 == num_rad2 ) + return neigh1; + } + + return -1; +} + + +/* Correct non-uniformly drawn oxoanions */ +int fix_non_uniform_drawn_oxoanions( int num_atoms, + inp_ATOM *at, + int *num_changes ) +{ + /* For central halogen, apply the following + correction rules: + + O O(-) + || | + O=Hal(-)=O ===> O=Hal=O + || || + O O + + (perchlorate, etc.) + + + O O(-) + || | + Hal(-)=O ===> Hal=O + || || + O O + + (chlorate, etc.) + + O O(-) + || | + Hal(-)=O ===> Hal=O + + + (chlorite, etc.) + + Hal(-)=O ===> Hal-O(-) + + + (hypochlorite, etc.) + + + + For halcogenes (S, Se, Te) + Y Y(-) + || | + RnX(-) ===> RnX + + if: + 1) (X = S, Y = O) || (X = Se, Y = S, O) || (X = Te, Y = O, S, Se) + 2) valence of X exceeds 6, in initially drawn form + + + So the following is corrected: + + O O(-) + || | + O=S(-)-R ===> O=S-R + || || + O O + + Or + + O O- + || | + F5Te(-) ===> F5Te + + but the following remains unchanged: + + + O + || + O=S(-)-R + + + The central atom (of IUPAC Group 16-17) is shown as negative but it contains double bond(s) + to terminal atom of greater electronegativity (of Group 16). + The central atom is halcogen (S,Se,Te) in highest oxidation state or halogen. + + Fix: + move negative charge to terminal atom and change double bond to a single one. + + Eligible central atom Eligible terminal atom at double bond's end + Cl O + Br O + I O + [At S,Se,Te] + S O + Se O,S + Te O, S, Se + + Comments: + 1. Central atoms of Groups 13-15 are not considered. + 2. Pauling electronegativities are: + F(3.98) > O(3.44) > Cl (3.16) > N (3.04) > Br(2.96) > I(2.66) > S(2.58) > Se(2.55) > At (2.2) > Te(2.1) + + + */ + + + /* constants for element numbers */ + enum elems { dNone, dCl=17,dBr=35,dI=53,dAt=85, dO=8, + dS=16,dSe=34,dTe=52, dP=15, dC=6, dN=7 }; + + static U_CHAR allowed_elnums_center_halogen[] = {dCl, dBr, dI, dAt} ; + static U_CHAR allowed_elnums_center_halcogen[] = {dS, dSe, dTe} ; + + int en_center; + int i, j, k; + + for (i=0; i 0? at[i].iso_atw_diff-1 : at[i].iso_atw_diff; + continue; + } + /* From same-element candidates, select one with less isotopic mass (arbitrary choice). */ + else if (en_term==min_en) + { + iso = at[j].iso_atw_diff > 0? at[i].iso_atw_diff-1 : at[i].iso_atw_diff; + if ( iso =0) + { + at[i].charge = 0; + at[jj].charge = -1; + at[i].bond_type[kk] = BOND_TYPE_SINGLE; + at[jj].bond_type[0] = BOND_TYPE_SINGLE; + at[i].bond_stereo[kk] = at[jj].bond_stereo[0] = 0; + at[i].chem_bonds_valence--; + at[jj].chem_bonds_valence--; + (*num_changes)++; + } + } + } /* end of search for candidate centers. */ + + return 0; +} + + +/* Correct non-uniformly drawn amidinium cations. */ +int fix_non_uniform_drawn_amidiniums( int num_atoms, + inp_ATOM *at, + int *num_changes ) + +{ + + /* Amidines include carboxamidines RC(=NR)NR2, + sulfinamidines RS(=NR)NR2 and phosphinamidines, R2P(=NR)NR2. + + + NR NR + | | + R"-Y-NHR' ===> R"-Y=N(+)HR' + (+) + + + Y = C, S, P + + + Fix: + move positive charge to nitrogen and change single bond to a double one. + + + Comment: + Fix is applied only if at least one of R's at N is hydrogen + (otherwise we have just a '+' delocalization which is already recognized). + + + */ + + /* constants for element numbers */ + enum elems { dNone, dCl=17,dBr=35,dI=53,dAt=85, dO=8, + dS=16,dSe=34,dTe=52, dP=15, dC=6, dN=7 }; + + static U_CHAR allowed_elnums_center[] = {dC, dS, dP} ; + int en_center; + int i, j, k, jj, kk, k1; + int mismatch = 0, nuH=0, nuN = 0, nitrogens[MAXVAL]; + + for (i=0; i 3 ) || ( at[j].chem_bonds_valence > 3 ) ) + { + mismatch = 1; + break; + } + nuH+= NUMH(at,j); + nuN++; + if (jj<0) + { + jj = j; + kk = k; + } + } + } + + /* If OK, apply changes. */ + if (mismatch) continue; + if (nuN!=2) continue; + if (nuH<1) continue; + if (jj>=0) + { + at[i].charge = 0; + at[jj].charge = 1; + at[i].bond_type[kk] = BOND_TYPE_DOUBLE; + for ( k1 = 0; k1 < at[jj].valence && i != at[jj].neighbor[k1]; k1 ++ ) + ; + at[jj].bond_type[k1] = BOND_TYPE_DOUBLE; + at[i].chem_bonds_valence++; + at[jj].chem_bonds_valence++; + /* NB: do nothing with wedge stereo bonds (retain wedge) */ + + (*num_changes)++; + } + } /* end of search for candidate centers. */ + + return 0; +} + + +/* +Not used -- +int FixAromaticOxygenAndSulfur( inp_ATOM *atom ) +{ + if ( !atom->elname[1] && (atom->elname[0]=='O' || atom->elname[0]=='S') && + atom->valence==2 && !atom->charge && !atom->radical && + atom->bond_type[0] + atom->bond_type[1] == 3 ) { + atom->charge = 1; + return 1; // fixed + } + return 0; +} +*/ + + +int fix_odd_things( int num_atoms, + inp_ATOM *at, + int bFixBug, + int bFixNonUniformDraw ) +{ +/* 0 1 2 3 4 5 6 7 8 9 */ +static const char el[] = "N;P;As;Sb;O;S;Se;Te;"; /* 8 elements + C, Si */ +static U_CHAR en[10]; /* same number: 8 elements */ +static int ne=0, ne2; /* will be 8 and 10 */ +static int el_number_P; +static int el_number_H; +static int el_number_C; +static int el_number_O; +static int el_number_Si; + +#define FIRST_NEIGHB2 4 +#define FIRST_CENTER2 5 +#define NUM_CENTERS_N 4 + +int i1, i2, k1, k2, c=-1, num_changes = 0; +char elname[ATOM_EL_LEN]; +int ne3; + + if (bFixNonUniformDraw) + { + int ret1; + ret1 = fix_non_uniform_drawn_oxoanions( num_atoms, at, &num_changes ); + ret1 = fix_non_uniform_drawn_amidiniums( num_atoms, at, &num_changes ); + } + + + if ( !ne ) + { + /* one time initialization */ + const char *b, *e; + int len; + ne3=0; + for ( b = el; e = strchr( b, ';'); b = e+1 ) + { + len = (int) (e-b); + memcpy( elname, b, len ); + elname[len] = '\0'; + en[ne3++] = get_periodic_table_number( elname ); + } + ne2 = ne3; + el_number_P = get_periodic_table_number( "P" ); + el_number_H = get_periodic_table_number( "H" ); + el_number_O = get_periodic_table_number( "O" ); + en[ne2++] = el_number_C = get_periodic_table_number( "C" ); + en[ne2++] = el_number_Si = get_periodic_table_number( "Si" ); + ne = ne3; + } + + + /* H(-)-X -> H-X(-); H(+)-X -> H-X(+) */ + for ( i1 = 0; i1 < num_atoms; i1 ++ ) + { + if ( 1 == at[i1].valence && + 1 == abs(at[i1].charge) && + (0 == at[i1].radical || RADICAL_SINGLET == at[i1].radical) && + BOND_TYPE_SINGLE == at[i1].bond_type[0] && + el_number_H == at[i1].el_number && el_number_H != at[i2=(int)at[i1].neighbor[0]].el_number && + !NUMH(at,i1) && !NUMH(at,i2) ) + { + at[i2].charge += at[i1].charge; + at[i1].charge = 0; + } + } + + + /* replace XHm(-)--Y==XHn(+) with XHm==Y--XHn, (n>=0 ,m>=0, X=N,P,As,Sb,O,S,Se,Te) */ + for ( i1 = 0; i1 < num_atoms; i1 ++ ) + { + if ( 1 != at[i1].charge || + at[i1].radical && RADICAL_SINGLET != at[i1].radical || + at[i1].chem_bonds_valence == at[i1].valence || + !memchr(en, at[i1].el_number, ne) || + get_el_valence( at[i1].el_number, at[i1].charge, 0 ) != at[i1].chem_bonds_valence+NUMH(at,i1) ) + { + continue; + } + + /* found a candidate at[i1] for X in XHn(+) */ + if ( 1 == at[i1].valence && + BOND_TYPE_DOUBLE == at[i1].bond_type[0] ) + { + c = (int) at[i1].neighbor[0]; + for ( k2 = 0; k2 < at[c].valence; k2 ++ ) + { + i2 = at[c].neighbor[k2]; + if ( 1 == at[i2].valence && + -1 == at[i2].charge && + at[i2].el_number == at[i1].el_number && /* exact match */ + (0 == at[i2].radical || RADICAL_SINGLET == at[i2].radical) && + BOND_TYPE_SINGLE == at[i2].bond_type[0] && + /*memchr(en, at[i2].el_number, ne) &&*/ + get_el_valence( at[i2].el_number, at[i2].charge, 0 ) == at[i2].chem_bonds_valence+NUMH(at,i2) ) { + /* found both X(-) and X(+); change bonds and remove charges */ + for ( k1 = 0; k1 < at[c].valence && i1 != at[c].neighbor[k1]; k1 ++ ) + ; + at[i1].charge = at[i2].charge = 0; + at[i1].bond_type[0] = at[c].bond_type[k1] = BOND_TYPE_SINGLE; + at[i1].chem_bonds_valence --; + at[i2].bond_type[0] = at[c].bond_type[k2] = BOND_TYPE_DOUBLE; + at[i2].chem_bonds_valence ++; + num_changes ++; + break; + } + } + } + else + { + /* explicit H case: detect H-neighbors and Y */ + int ineigh, neigh, i1_c, i2_c, num_H_i1, num_H_i2; + for ( ineigh = 0, num_H_i1 = 0, i1_c = -1; ineigh < at[i1].valence; ineigh ++ ) + { + neigh = at[i1].neighbor[ineigh]; + if ( at[neigh].el_number == el_number_H ) + { + if ( at[neigh].chem_bonds_valence == 1 && + (0 == at[neigh].radical || RADICAL_SINGLET == at[neigh].radical) ) + { + num_H_i1 ++; /* found H-neighbor */ + } + else + { + break; /* wrong neighbor */ + } + } + else if ( at[i1].bond_type[ineigh] == BOND_TYPE_DOUBLE ) + { + /* found a candidate for Y; bond must be double */ + i1_c = ineigh; + c = neigh; + } + } + if ( i1_c < 0 || num_H_i1 + 1 != at[i1].valence ) + { + continue; + } + for ( k2 = 0; k2 < at[c].valence; k2 ++ ) + { + i2 = at[c].neighbor[k2]; + if (-1 == at[i2].charge && + at[i2].el_number == at[i1].el_number && /* exact match */ + (0 == at[i2].radical || RADICAL_SINGLET == at[i2].radical) && + get_el_valence( at[i2].el_number, at[i2].charge, 0 ) == at[i2].chem_bonds_valence+NUMH(at,i2) ) + { + for ( ineigh = 0, num_H_i2 = 0, i2_c = -1; ineigh < at[i2].valence; ineigh ++ ) + { + neigh = at[i2].neighbor[ineigh]; + if ( at[neigh].el_number == el_number_H ) + { + if ( at[neigh].chem_bonds_valence == 1 && + (0 == at[neigh].radical || RADICAL_SINGLET == at[neigh].radical) ) + { + num_H_i2 ++; /* found H-neighbor */ + } + else + { + break; /* wrong neighbor */ + } + } + else + if ( c == neigh && at[i2].bond_type[ineigh] == BOND_TYPE_SINGLE ) + { + i2_c = ineigh; /* position of Y neighbor; bond must be single */ + } + else + { + break; + } + } + if ( num_H_i2 + (i2_c >= 0) != at[i2].valence ) + { + continue; + } + /* found both X(-) and X(+); change bonds and remove charges */ + for ( k1 = 0; k1 < at[c].valence && i1 != at[c].neighbor[k1]; k1 ++ ) + ; + at[i1].charge = at[i2].charge = 0; + at[i1].bond_type[i1_c] = at[c].bond_type[k1] = BOND_TYPE_SINGLE; + at[i1].chem_bonds_valence --; + at[i2].bond_type[i2_c] = at[c].bond_type[k2] = BOND_TYPE_DOUBLE; + at[i2].chem_bonds_valence ++; + num_changes ++; + break; + } + } /* k2 */ + } + } + + + /* Replace + + X- X X=O,S,Se,Te -- terminal atoms (NEIGHB2) + \ | \ || + >Y++ with >Y Y=S,Se,Te -- central cation (CENTER2) + / | / || + X- X Y valence=4, original Y bond valence = 4 + + + --- the following case of P is processed separately in remove_ion_pairs() + --- therefire, it has been disabled here, see #ifndef FIX_P_IV_Plus_O_Minus -- 2010-03-17 DT + + X- X X=O,S,Se,Te -- terminal atoms (NEIGHB2) + \ | \ || + >P+ with >P + / | / | + X- X- Y valence=4, original Y bond valence = 4 + + */ + + for ( i1 = 0; i1 < num_atoms; i1 ++ ) + { + if ( 1 == at[i1].valence && + -1 == at[i1].charge && + (0 == at[i1].radical || RADICAL_SINGLET == at[i1].radical) && + !NUMH(at,i1) && + BOND_TYPE_SINGLE == at[i1].bond_type[0] && + memchr( en+FIRST_NEIGHB2, at[i1].el_number, ne-FIRST_NEIGHB2 ) ) + { + int charge, i; + /* found a candidate for X */ + c = (int)at[i1].neighbor[0]; /* candidate for Y */ + if ( ((charge=2) == at[c].charge && memchr( en+FIRST_CENTER2, at[c].el_number, ne-FIRST_CENTER2) + +#ifndef FIX_P_IV_Plus_O_Minus + || (charge=1) == at[c].charge && el_number_P==at[c].el_number +#endif + ) && + 4 == at[c].valence && + (0 == at[c].radical || RADICAL_SINGLET == at[c].radical ) && + at[c].valence == at[c].chem_bonds_valence && + !NUMH(at,c) ) + { + ; /* accept */ + } + else + { + continue; /* ignore at[i1] */ + } + for ( k2 = 0; k2 < at[c].valence; k2 ++ ) + { + i2 = at[c].neighbor[k2]; + if ( i2 == i1 ) + { + continue; + } + if ( 1 == at[i2].valence && + -1 == at[i2].charge && + memchr( en+FIRST_NEIGHB2, at[i2].el_number, ne-FIRST_NEIGHB2 ) && + /*at[i2].el_number == at[i1].el_number &&*/ /* exact match */ + (0 == at[i2].radical || RADICAL_SINGLET == at[i2].radical) && + !NUMH(at,i2) && + BOND_TYPE_SINGLE == at[i2].bond_type[0] ) + { + /* found both X(-) and X(-); change bonds and remove charges */ + for ( k1 = 0; k1 < at[c].valence && i1 != at[c].neighbor[k1]; k1 ++ ) + ; + for ( i = 0; i < charge; i ++ ) + { + /* in case of P it does not matter which X atom is neutralized + because of tautomerism. However, neutral central atom is important + for the neutralization of the components */ + switch ( i ) + { + case 0: + at[i1].charge ++; /* = 0; changed 2010-03-17 DT*/ + at[i1].bond_type[0] = at[c].bond_type[k1] = BOND_TYPE_DOUBLE; + at[i1].bond_stereo[0] = at[c].bond_stereo[k1] = 0; + at[i1].chem_bonds_valence ++; + at[c].chem_bonds_valence ++; + if ( bFixBug ) at[c].charge --; /* added 2010-03-17 DT*/ + num_changes ++; + break; + case 1: + at[i2].charge ++; /*= 0; changed 2010-03-17 DT*/ + at[i2].bond_type[0] = at[c].bond_type[k2] = BOND_TYPE_DOUBLE; + at[i2].bond_stereo[0] = at[c].bond_stereo[k2] = 0; + at[i2].chem_bonds_valence ++; + at[c].chem_bonds_valence ++; + if ( bFixBug ) at[c].charge --; /* added 2010-03-17 DT */ + num_changes ++; + break; + } + } + + /* -- removed -- 2010-03-17 DT +#if ( FIX_ODD_THINGS_REM_Plus_BUG == 1 ) + at[c].charge -= charge; +#else + if ( bFixBug ) + { + at[c].charge -= charge; + } +#endif + */ + break; + } + } + } + } + + + /* A(doublet)-B(doublet) -> A=B (A and B have no other doublet neighbors) */ + /* A(doublet)=B(doublet) -> A#B (A and B have no other doublet neighbors) */ + for( i1 = 0; i1 < num_atoms; i1 ++ ) + { + if ( RADICAL_DOUBLET == at[i1].radical && + 0 <= (i2=the_only_doublet_neigh(at, i1, &k1, &k2)) ) + { + if ( at[i1].bond_type[k1] <= BOND_TYPE_DOUBLE ) + { + at[i1].bond_type[k1] ++; + at[i1].chem_bonds_valence ++; + at[i2].bond_type[k2] ++; + at[i2].chem_bonds_valence ++; + at[i1].radical = 0; + at[i2].radical = 0; + } + } + } + +#if ( REMOVE_ION_PAIRS_EARLY == 1 ) + num_changes += remove_ion_pairs( num_atoms, at ); +#endif + + return num_changes; +} + + +int post_fix_odd_things( int num_atoms, inp_ATOM *at ) +{ + int num_changes = 0; + /* currently does nothing */ + return + num_changes; +} + + +int nFindOneOM(inp_ATOM *at, int at_no, int ord_OM[], int num_OM) +{ + int i, n_OM, n_OM_best, best_value, cur_value, diff; + int num_best; + + if ( 1 == num_OM ) + { + return ord_OM[0]; + } + if ( 1 > num_OM ) + { + return -1; + } + + /* select neighbors with min. number of bonds */ + num_best = 1; + n_OM = (int)at[at_no].neighbor[ord_OM[0]]; + best_value = (int)at[n_OM].valence; + /* compare number of bonds; move indexes of the best neighbors to the first elements of ord_OM[] */ + for ( i = 1; i < num_OM; i ++ ) + { + n_OM = at[at_no].neighbor[ord_OM[i]]; + cur_value = (int)at[n_OM].valence; + diff = cur_value - best_value; + if ( diff < 0 ) + { + n_OM_best = n_OM; + best_value = cur_value; + ord_OM[0] = ord_OM[i]; + num_best = 1; + } + else if ( diff == 0 ) + { /* was '=', pointed by WDI */ + ord_OM[num_best ++] = ord_OM[i]; + } + } + num_OM = num_best; + if ( 1 == num_OM ) + { + return ord_OM[0]; + } + + /* select neighbors with min. periodic numbers */ + num_best = 1; + n_OM = (int)at[at_no].neighbor[ord_OM[0]]; + best_value = (int)at[n_OM].el_number; + + /* compare periodic numbers; move indexes of the best neighbors to the first elements of ord_OM[] */ + for ( i = 1; i < num_OM; i ++ ) + { + n_OM = at[at_no].neighbor[ord_OM[i]]; + cur_value = (int)at[n_OM].el_number; + diff = cur_value - best_value; + if ( diff < 0 ) + { + n_OM_best = n_OM; + best_value = cur_value; + ord_OM[0] = ord_OM[i]; + num_best = 1; + } + else if ( diff == 0 ) + { /* was '=', pointed by WDI */ + ord_OM[num_best ++] = ord_OM[i]; + } + } + num_OM = num_best; + if ( 1 == num_OM ) + { + return ord_OM[0]; + } + + /* if neighbors are not terminal atoms then reject */ + if ( 1 < at[n_OM].valence ) + { + return -1; + } + + /* if neighbors are terminal atoms then the one without isotope or with lightest isotope */ + num_best = 1; + n_OM = (int)at[at_no].neighbor[ord_OM[0]]; + best_value = (int)at[n_OM].iso_atw_diff; + + /* compare periodic numbers; move indexes of the best neighbors to the first elements of ord_OM[] */ + for ( i = 1; i < num_OM; i ++ ) + { + n_OM = at[at_no].neighbor[ord_OM[i]]; + cur_value = (int)at[n_OM].el_number; + diff = cur_value - best_value; + if ( (!cur_value && best_value) || diff < 0 ) + { + n_OM_best = n_OM; + best_value = cur_value; + ord_OM[0] = ord_OM[i]; + num_best = 1; + } + else if ( diff == 0 ) + { + /* was '=', pointed by WDI */ + ord_OM[num_best ++] = ord_OM[i]; + } + } + num_OM = num_best; + if ( 1 == num_OM ) + { + return ord_OM[0]; + } + + /* return any */ + return ord_OM[0]; +} + + +/* NB: + the bonds are fixed in fix_special_bonds() +*/ +int remove_ion_pairs( int num_atoms, inp_ATOM *at ) +{ +int num_changes = 0; + +/* 0 1 2 3 4 5 6 7 8 9 8 9 */ +#if ( FIX_REM_ION_PAIRS_Si_BUG == 1 ) + static const char el[] = "N;P;As;Sb;O;S;Se;Te;C;Si;"; /* 8 elements + C, Si */ +#else + static const char el[] = "N;P;As;Sb;O;S;Se;Te;C;Si"; /* 8 elements + C, Si */ +#endif + static char en[12]; /* same number: 8 elements */ + static int ne=0; /* will be 8 and 10 */ + +#define ELEM_N_FST 0 +#define ELEM_N_LEN 4 +#define ELEM_O_FST 4 +#define ELEM_O_LEN 4 +#define ELEM_C_FST 8 +#define ELEM_C_LEN 2 + +#define MAX_NEIGH 6 + + int i, n, n2, i1, i2, i3, i4, type, chrg; + int num_C_II=0, num_C_plus=0, num_C_minus=0, num_N_plus=0, num_N_minus=0, num_O_plus=0, num_O_minus=0, num_All; + +#ifdef FIX_P_IV_Plus_O_Minus + int num_P_IV_plus=0; /* added 2010-03-17 DT */ +#endif + + inp_ATOM *a; + char elname[ATOM_EL_LEN], *p; + + int ne2; + + if ( !ne ) + { + /* one time initialization */ + const char *b, *e; + int len; + ne2=0; + for ( b = el; e = strchr( b, ';'); b = e+1 ) + { + len = (int) (e-b); + memcpy( elname, b, len ); + elname[len] = '\0'; + en[ne2++] = get_periodic_table_number( elname ); + } + en[ne2] = '\0'; + ne=ne2; + } + + /****** count candidates ********/ + for ( i = 0, a = at; i < num_atoms; i ++, a++ ) + { + if ( 1 == (chrg=a->charge) || -1 == chrg ) + { + if ( p = (char*)memchr( en, a->el_number, ne) ) + { + n = (int) (p - en); + if ( n >= ELEM_C_FST ) + { + if ( chrg > 0 ) + num_C_plus ++; + else + num_C_minus ++; + } + else if ( n >= ELEM_O_FST ) + { + if ( chrg > 0 ) + num_O_plus ++; + else + num_O_minus ++; + } + else + { + if ( chrg > 0 ) + num_N_plus ++; + else + num_N_minus ++; + +#ifdef FIX_P_IV_Plus_O_Minus + num_P_IV_plus += n > 0 && chrg == 1 && a->valence == 4 && a->chem_bonds_valence == 4; /* added 2010-03-17 DT */ +#endif + } + } + } + else if ( !chrg && a->chem_bonds_valence + NUMH(a, 0) == 2 && + get_el_valence( a->el_number, 0, 0 ) == 4 && + NULL != memchr( en+ELEM_C_FST, a->el_number, ELEM_C_LEN) ) + { + num_C_II ++; + } + } + + num_All = num_C_II + num_C_plus + num_C_minus + num_N_plus + num_N_minus + num_O_plus + num_O_minus; + + /* do not add num_P_IV_plus ! -- 2010-03-17 DT */ + if ( !num_All ) + { + return 0; + } + + + /**************************************************************************/ + /*************************** Terminal ion pairs ***************************/ + /**************************************************************************/ + + /*------------------------------------------------------------------------- + Pair type 1 N=N,P,As,Sb; O=O,S,Se,Te + =========== + + X X if X is another -O(-) then neutralize O(-) + | | that has the smallest periodic table number + O=N(+)-O(-) => O=N=O + i n + --------------------------------------------------------------------------*/ + + for ( type = 1; type <= 18; type ++ ) + { + if ( (!type || 1 == type) ) + { + for ( i = 0; i < num_atoms && 0 < num_N_plus && 0 < num_O_minus; i ++ ) + { + if ( 1 == at[i].charge && 3 == nNoMetalNumBonds(at, i) && + 4 == nNoMetalBondsValence(at, i) && + NULL != memchr( en+ELEM_N_FST, at[i].el_number, ELEM_N_LEN) ) + { + int num_OM = 0, ord_OM[3]; /* -O(-) */ + int num_O = 0; /* =O */ + int num_O_other = 0; + for ( i1 = 0; i1 < at[i].valence; i1 ++ ) + { + n = at[i].neighbor[i1]; + if ( 1 == nNoMetalNumBonds(at, n) && 0 == num_of_H( at, n ) && + NULL != (p = (char*)memchr( en+ELEM_O_FST, at[n].el_number, ELEM_O_LEN)) ) + { + if ( BOND_TYPE_SINGLE == at[i].bond_type[i1] && + -1 == at[n].charge ) + { + ord_OM[num_OM ++] = i1; + } + else if ( BOND_TYPE_DOUBLE == at[n].bond_type[0] && + 0 == at[n].charge ) + { + num_O ++; + } + else + { + num_O_other ++; + } + } + } + if ( num_OM > 0 && num_O > 0 && !num_O_other && + 0 <= (i1=nFindOneOM(at, i, ord_OM, num_OM)) ) + { + /* remove charges and increase bond order */ + n = at[i].neighbor[i1]; + i2 = (int) ( is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor ); + at[i].bond_type[i1] ++; + at[n].bond_type[i2] ++; + at[i].chem_bonds_valence ++; + at[n].chem_bonds_valence ++; + at[i].charge --; + at[n].charge ++; + at[i].radical = 0; + at[n].radical = 0; + num_changes ++; + num_N_plus --; + num_O_minus --; + num_All -= 2; + } + } + } + +#ifdef FIX_P_IV_Plus_O_Minus + + /*------------------------------------------------------------------------- + Pair type 1a P=P,As,Sb; O=O,S,Se,Te -- added 2010-03-17 + ============= + + X X if X, Y, or Z is another -O(-) then neutralize O(-) + | | that has the smallest periodic table number + Y-P(+)-O(-) => Y-P=O + |i n | + Z Z + + --------------------------------------------------------------------------*/ + + for ( i = 0; i < num_atoms && 0 < num_P_IV_plus /*&& 0 < num_N_plus*/ && 0 < num_O_minus; i ++ ) + { + if ( 1 == at[i].charge && 4 == nNoMetalNumBonds(at, i) && + 4 == nNoMetalBondsValence(at, i) && + NULL != memchr( en+ELEM_N_FST+1, at[i].el_number, ELEM_N_LEN-1) ) + { + int num_OM = 0, ord_OM[4]; /* -O(-) */ + /*int num_O = 0;*/ /* =O */ + int num_O_other = 0; + for ( i1 = 0; i1 < at[i].valence; i1 ++ ) + { + n = at[i].neighbor[i1]; + if ( 1 == nNoMetalNumBonds(at, n) && 0 == num_of_H( at, n ) && + NULL != (p = (char*)memchr( en+ELEM_O_FST, at[n].el_number, ELEM_O_LEN)) ) + { + if ( BOND_TYPE_SINGLE == at[i].bond_type[i1] && + -1 == at[n].charge ) + { + ord_OM[num_OM ++] = i1; + /* + } + if ( BOND_TYPE_DOUBLE == at[n].bond_type[0] && + 0 == at[n].charge ) { + num_O ++; + */ + } + else + { + num_O_other ++; + } + } + } + if ( num_OM > 0 /*&& num_O > 0 && !num_O_other*/ && + 0 <= (i1=nFindOneOM(at, i, ord_OM, num_OM)) ) + { + /* remove charges and increase bond order */ + n = at[i].neighbor[i1]; + i2 = is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor; + at[i].bond_type[i1] ++; + at[n].bond_type[i2] ++; + at[i].chem_bonds_valence ++; + at[n].chem_bonds_valence ++; + at[i].charge --; + at[n].charge ++; + at[i].radical = 0; + at[n].radical = 0; + num_changes ++; + num_N_plus --; + num_O_minus --; + num_P_IV_plus --; + num_All -= 2; + } + } + } +#endif /* FIX_P_IV_Plus_O_Minus */ + } + + + /*------------------------------------------------------------------------- + Terminal pair types: 2,3,4,5,6,7,8,9 N=N,P,As,Sb; O=O,S,Se,Te; C=C,Si + ==================================== + type # + 2 2: O=N-C(II)- => O=N#C- N=N,P,As,Sb; O=O,S,Se,Te; C=C,Si + 3 9: O=O(+)-C(-)(III) => O=O=C(IV) + 4 3: O(-)-N(+)(IV) => O=N(V) (input structure has at least 1 double bond) + 5 4: O(-)-O(+)(III) => O=O(IV) + 6 8: O(-)-O-C(+)(III) => O=O=C(IV) + 7 5: N(-)=N(+)(IV) => N#N(V) allow terminal H on N(-) + 8 6: N(-)=O(+)(III) => N#O- + 9 7: N(-)=C(+)(III) => N#C- + --------------------------------------------------------------------------*/ + + if ( !type || 2 <= type && type <= 9 ) + { + for ( i = 0; i < num_atoms && 0 < num_All; i ++ ) + { + if ( 0 == at[i].charge && 1 == nNoMetalNumBonds(at, i) && 2 == nNoMetalBondsValence(at, i) && + 0 == num_of_H( at, i ) && + NULL != memchr( en+ELEM_O_FST, at[i].el_number, ELEM_O_LEN) && + 0 <= ( i1 = nNoMetalNeighIndex(at, i)) && + at[i].bond_type[i1] <= BOND_TYPE_TRIPLE ) + { + /* terminal O= */ + n = at[i].neighbor[i1]; + if ( (!type || type == 2) && 0 < num_C_II ) + { + /* avoid alternating bonds */ + if ( 0 == at[n].charge && + 2 == nNoMetalNumBonds(at, n) && 3 == nNoMetalBondsValence(at, n) && + 0 == num_of_H( at, n ) && + NULL != memchr( en+ELEM_N_FST, at[n].el_number, ELEM_N_LEN) && + 0 <= (i2 = nNoMetalOtherNeighIndex( at, n, i ) ) && + at[n].bond_type[i2] <= BOND_TYPE_TRIPLE ) + { + /* i2 = index of opposite to at[i] neighbor of at[n] */ + /*i2 = (at[n].neighbor[0] == i);*/ + n2 = at[n].neighbor[i2]; + if ( 0 == at[n2].charge && + 2 == at[n2].valence && 2 == at[n2].chem_bonds_valence && + 0 == num_of_H( at, n2 ) && + NULL != memchr( en+ELEM_C_FST, at[n2].el_number, ELEM_C_LEN) ) + { + /* i n n2 */ + /* found O=N-C(II)- */ + /* convert O=N-C(II)- => O=N#C- */ + + i3 = (at[n2].neighbor[0] != n); /* index of at[n] neighbor of n2 */ + at[ n].chem_bonds_valence = 5; /* N */ + at[n2].chem_bonds_valence = 4; /* C */ + at[ n].bond_type[i2] = BOND_TYPE_TRIPLE; + at[n2].bond_type[i3] = BOND_TYPE_TRIPLE; + at[n2].radical = 0; + num_changes ++; + num_C_II --; + num_All --; + continue; + } + } + } + + if ( (!type || type == 3) && 0 < num_O_plus && 0 < num_C_minus ) + { + if ( 1 == at[n].charge && 2 == nNoMetalNumBonds(at, n) && 3 == nNoMetalBondsValence(at, n) && + 0 == num_of_H( at, n ) && + NULL != memchr( en+ELEM_O_FST, at[n].el_number, ELEM_O_LEN) && + 0 <= (i2 = nNoMetalOtherNeighIndex( at, n, i ) ) && + at[n].bond_type[i2] <= BOND_TYPE_TRIPLE ) + { + /* found O=O(+)- */ + /* i2 = index of opposite to at[i] neighbor of at[n] */ + /*i2 = (at[n].neighbor[0] == i);*/ + n2 = at[n].neighbor[i2]; + if ( -1 == at[n2].charge && 3 >= nNoMetalNumBonds(at, n2) && 3 == nNoMetalBondsValence(at, n2)+NUMH(at,n2) && + NULL != memchr( en+ELEM_C_FST, at[n2].el_number, ELEM_C_LEN) ) + { + /* i n n2 */ + /* found found O=O(+)-C(-)(III) */ + /* convert O=O(+)-C(-)(III) => O=O=C(IV) */ + i3 = (at[n2].neighbor[0] != n); /* index of at[n] neighbor of n2 */ + at[ n].charge --; + at[n2].charge ++; + at[ n].chem_bonds_valence += 1; /* =O- => =O= */ + at[n2].chem_bonds_valence += 1; /* -C => =C */ + at[ n].bond_type[i2] = BOND_TYPE_DOUBLE; + at[n2].bond_type[i3] = BOND_TYPE_DOUBLE; + num_changes ++; + num_O_plus --; + num_C_minus --; + num_All -= 2; + continue; + } + } + } + } + else if ( -1 == at[i].charge && + 0 < num_O_minus + num_N_minus && + 0 < num_N_plus + num_O_plus + num_C_plus && + 1 == nNoMetalNumBonds(at, i) && 1 == nNoMetalBondsValence(at, i) && + 0 == num_of_H( at, i ) && + NULL != memchr( en+ELEM_O_FST, at[i].el_number, ELEM_O_LEN) && + 0 <= (i1 = nNoMetalNeighIndex( at, i )) && + at[i].bond_type[i1] <= BOND_TYPE_TRIPLE ) + { + + /* terminal O(-)- */ + + n = at[i].neighbor[i1]; + + if ( (!type || type == 4) && 0 < num_O_minus && 0 < num_N_plus && /* O(-)-N(+)(IV) */ + 1 == at[n].charge && 3 >= nNoMetalNumBonds(at, n) && 4 == nNoMetalBondsValence(at, n) && + 0 == num_of_H( at, n ) && + NULL != memchr( en+ELEM_N_FST, at[n].el_number, ELEM_N_LEN) /* except >O(+)- */ + ) + { + /* found O(-)-N(+)(IV) */ + /* convert O(-)-N(+)(IV) => O=N(V) */ + + i2 = (int) ( is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor ); /* index of at[i] neighbor of at[n] */ + at[i].charge ++; + at[n].charge --; + at[i].chem_bonds_valence ++; + at[n].chem_bonds_valence ++; + at[i].bond_type[i1] ++; + at[n].bond_type[i2] ++; + num_changes ++; + num_O_minus --; + num_N_plus --; + num_All -= 2; + continue; + } + + if ( (!type || type == 5) && 0 < num_O_minus && 0 < num_O_plus &&/* O(-)-O(+)(III) */ + 1 == at[n].charge && 3 >= nNoMetalNumBonds(at, n) && 3 == nNoMetalBondsValence(at, n) && + 0 == num_of_H( at, n ) && + NULL != memchr( en+ELEM_O_FST, at[n].el_number, ELEM_O_LEN) /* except >O(+)- */ + ) + { + /* found O(+)(III) */ + /* convert O(-)-O(+)(III) => O=O(IV) */ + + i2 = (int) ( is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor ); /* index of at[i] neighbor of at[n] */ + at[i].charge ++; + at[n].charge --; + at[i].chem_bonds_valence ++; + at[n].chem_bonds_valence ++; + at[i].bond_type[i1] ++; + at[n].bond_type[i2] ++; + num_changes ++; + num_O_minus --; + num_O_plus --; + num_All -= 2; + continue; + } + + /* i n n2 */ + if ( (!type || type == 6) && /* O(-)-O-C(+)(III) */ + 0 < num_O_minus && 0 < num_C_plus && + 0 == at[n].charge && 2 == nNoMetalNumBonds(at, n) && 2 == nNoMetalBondsValence(at, n) && + 0 == num_of_H( at, n ) && + NULL != memchr( en+ELEM_O_FST, at[n].el_number, ELEM_O_LEN) && + 0 <= (i2=nNoMetalOtherNeighIndex( at, n, i )) && + at[n].bond_type[i2] <= BOND_TYPE_TRIPLE ) + { + /* found O(-)-O- */ + /* i2 = index of opposite to at[i] neighbor of at[n] */ + /*i2 = (at[n].neighbor[0] == i);*/ + n2 = at[n].neighbor[i2]; + if ( 1 == at[n2].charge && 3 >= nNoMetalNumBonds(at, n2) && + 3 == nNoMetalBondsValence(at, n2)+NUMH(at,n2) && + NULL != memchr( en+ELEM_C_FST, at[n2].el_number, ELEM_C_LEN) ) { + /* i n n2 */ + /* found O(-)-O-C(+)(III) */ + /* convert O(-)-O-C(+)(III) => O=O=C(IV) */ + /*i3 = (at[n2].neighbor[0] != n);*/ /* i3 = index of at[n] neighbor of at[n2] */ + i3 = (int) ( is_in_the_list( at[n2].neighbor, (AT_NUMB)n, at[n2].valence ) - at[n2].neighbor ); + /*i4 = index of at[i] in the adjacency list of at[n] */ + i4 = (int) ( is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor ); + at[ i].charge ++; + at[n2].charge --; + at[ i].chem_bonds_valence += 1; /* O- => O= */ + at[ n].chem_bonds_valence += 2; /* -O- => =O= */ + at[n2].chem_bonds_valence += 1; /* -C => =C */ + at[ i].bond_type[i1] = BOND_TYPE_DOUBLE; + at[ n].bond_type[i4] = BOND_TYPE_DOUBLE; + at[ n].bond_type[i2] = BOND_TYPE_DOUBLE; + at[n2].bond_type[i3] = BOND_TYPE_DOUBLE; + num_changes ++; + num_O_minus --; + num_C_plus --; + num_All -= 2; + continue; + } + } + } + else if ( -1 == at[i].charge && 0 < num_N_minus && 0 < num_N_plus+num_O_plus+num_C_plus && + 1 == nNoMetalNumBonds(at, i) && 2 == nNoMetalBondsValence(at, i)+NUMH(at, i) && + /*0 == num_of_H( at, i ) &&*/ + NULL != memchr( en+ELEM_N_FST, at[i].el_number, ELEM_N_LEN) && + 0 <= (i1 = nNoMetalNeighIndex( at, i )) && + at[i].bond_type[i1] <= BOND_TYPE_TRIPLE ) + { + /* terminal N(-)= */ + n = at[i].neighbor[i1 = 0]; + if ( (!type || type == 7) && 0 < num_N_plus && /* N(-)=N(+)(IV) */ + 1 == at[n].charge && 3 >= nNoMetalNumBonds(at, n) && 4 == nNoMetalBondsValence(at, n) && + 0 == num_of_H( at, n ) && + NULL != memchr( en+ELEM_N_FST, at[n].el_number, ELEM_N_LEN) ) + { + /* found N(-)-N(+)(IV) */ + /* convert N(-)=N(+)(IV) => N#N(V) */ + + i2 = (int) ( is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor ); /* index of at[i] neighbor of at[n] */ + at[i].charge ++; + at[n].charge --; + at[i].chem_bonds_valence ++; + at[n].chem_bonds_valence ++; + at[i].bond_type[i1] ++; + at[n].bond_type[i2] ++; + num_changes ++; + num_N_minus --; + num_N_plus --; + num_All -= 2; + continue; + } + if ( (!type || type == 8) && 0 < num_O_plus && /* N(-)=O(+)(III) */ + 1 == at[n].charge && 2 == nNoMetalNumBonds(at, n) && 3 == nNoMetalBondsValence(at, n) && + 0 == num_of_H( at, n ) && + NULL != memchr( en+ELEM_O_FST, at[n].el_number, ELEM_O_LEN) ) + { + /* found N(-)-O(+)(III) */ + /* convert N(-)=O(+)(III) => N#O(IV)- */ + i2 = (int) ( is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor ); /* index of at[i] neighbor of at[n] */ + at[i].charge ++; + at[n].charge --; + at[i].chem_bonds_valence ++; + at[n].chem_bonds_valence ++; + at[i].bond_type[i1] ++; + at[n].bond_type[i2] ++; + num_changes ++; + num_N_minus --; + num_O_plus --; + num_All -= 2; + continue; + } + if ( (!type || type == 9) && 0 < num_C_plus && /* N(-)=C(+)(III) */ + 1 == at[n].charge && 2 == at[n].valence && 3 == at[n].chem_bonds_valence && + 0 == num_of_H( at, n ) && + NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) + { + /* found N(-)=C(+)(III) */ + /* convert N(-)=C(+)(III) => N#C(IV)- */ + + i2 = (int) ( is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor ); /* index of at[i] neighbor of at[n] */ + at[i].charge ++; + at[n].charge --; + at[i].chem_bonds_valence ++; + at[n].chem_bonds_valence ++; + at[i].bond_type[i1] ++; + at[n].bond_type[i2] ++; + num_changes ++; + num_N_minus --; + num_C_plus --; + num_All -= 2; + continue; + } + } + } + } + + + /**************************************************************************/ + /*********************** NON-Terminal ion pairs ***************************/ + /**************************************************************************/ + /*------------------------------------------------------------------------- + Non-Terminal pair types: 10,11,12,13,14 N=N,P,As,Sb; O=O,S,Se,Te; C=C,Si + ======================================== + + 10: N(+)(IV)-C(-)(III) => N(V)=C(IV) (N has 3 or 2 bonds) + 11: N(+)(IV)=C(-)(III) => N(V)#C(IV) (N has 3 or 2 bonds) + 12: N(+)(IV)-N(-)(II) => N(V)=N(III) (allow terminal H on N(-)) + 13: -O(+)-C(-)(III) => -O=C- + 14: -O(+)=C(-)(III) => -O#C- + 15: O(+)(III)-N(-)(II) => O(IV)=N(III) (allow terminal H on N(-)) + --------------------------------------------------------------------------*/ + + if ( !type || 10 <= type && type <= 15 ) + { + for ( i = 0; i < num_atoms && 0 < num_All; i ++ ) + { + if ( 1 == at[i].charge && + 0 < num_N_plus + num_O_plus && 0 < num_C_minus + num_N_minus && + 4 >= nNoMetalNumBonds(at, i) && 4 == nNoMetalBondsValence(at, i) && + 0 == num_of_H( at, i ) && + NULL != memchr( en+ELEM_N_FST, at[i].el_number, ELEM_N_LEN) ) + { + /* found non-terminal N(+)(IV) */ + if ( (!type || 10 == type) && 0 < num_N_plus && 0 < num_C_minus ) + { + int num_neigh = 0, pos_neigh = -1; + for ( i1 = 0; i1 < at[i].valence; i1 ++ ) + { + n = at[i].neighbor[i1]; + if ( -1 == at[n].charge && 3 >= at[n].valence && 3 == at[n].chem_bonds_valence+NUMH(at,n) && + /*0 == at[n].num_H &&*/ + at[i].bond_type[i1] == BOND_TYPE_SINGLE && + NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) + { + /* found N(+)(IV)-C(-)(III); prepare conversion to N(V)=C(IV) */ + num_neigh ++; + pos_neigh = i1; + } + } + i1=pos_neigh; + if ( 1 == num_neigh && + at[i].bond_type[i1] <= BOND_TYPE_TRIPLE && + !has_other_ion_neigh( at, i, n=at[i].neighbor[i1], en, ne ) && + !has_other_ion_neigh( at, n, i, en, ne )) + { + /*n = at[i].neighbor[i1=pos_neigh];*/ + i2 = (int) ( is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor ); + at[i].charge --; + at[n].charge ++; + at[i].chem_bonds_valence ++; + at[n].chem_bonds_valence ++; + at[i].bond_type[i1] ++; + at[n].bond_type[i2] ++; + num_changes ++; + num_C_minus --; + num_N_plus --; + num_All -= 2; + continue; + } + } + if ( (!type || 11 == type) && 0 < num_N_plus && 0 < num_C_minus ) + { + int num_neigh = 0, pos_neigh = -1; + for ( i1 = 0; i1 < at[i].valence; i1 ++ ) + { + n = at[i].neighbor[i1]; + if ( -1 == at[n].charge && 3 >= at[n].valence && 3 == at[n].chem_bonds_valence+NUMH(at,n) && + /*0 == at[n].num_H &&*/ + at[i].bond_type[i1] == BOND_TYPE_DOUBLE && + NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) + { + /* found N(+)(IV)=C(-)(III); prepare conversion to N(V)#C(IV) */ + num_neigh ++; + pos_neigh = i1; + } + } + if ( 1 == num_neigh && + !has_other_ion_neigh( at, i, n=at[i].neighbor[i1=pos_neigh], en, ne ) && + !has_other_ion_neigh( at, n, i, en, ne )) + { + /*n = at[i].neighbor[i1=pos_neigh];*/ + i2 = (int) ( is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor ); + at[i].charge --; + at[n].charge ++; + at[i].chem_bonds_valence ++; + at[n].chem_bonds_valence ++; + at[i].bond_type[i1] ++; + at[n].bond_type[i2] ++; + num_changes ++; + num_C_minus --; + num_N_plus --; + num_All -= 2; + continue; + } + } + if ( !type || 12 == type && 0 < num_N_plus && 0 < num_N_minus ) + { + int num_neigh = 0, pos_neigh = -1; + for ( i1 = 0; i1 < at[i].valence; i1 ++ ) + { + n = at[i].neighbor[i1]; + if ( -1 == at[n].charge && 2 >= nNoMetalNumBonds(at, n) && + 2 == nNoMetalBondsValence(at, n)+NUMH(at, n) && + /*0 == num_of_H( at, n ) &&*/ + at[i].bond_type[i1] == BOND_TYPE_SINGLE && + NULL != memchr( en+ELEM_N_FST, at[n].el_number, ELEM_N_LEN) ) + { + /* found N(+)(IV)=N(-)(II); prepare conversion to N(V)#N(III) */ + num_neigh ++; + pos_neigh = i1; + } + } + if ( 1 == num_neigh && + !has_other_ion_neigh( at, i, n=at[i].neighbor[i1=pos_neigh], en, ne ) && + !has_other_ion_neigh( at, n, i, en, ne )) + { + /*n = at[i].neighbor[i1=pos_neigh];*/ + i2 = (int) ( is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor ); + at[i].charge --; + at[n].charge ++; + at[i].chem_bonds_valence ++; + at[n].chem_bonds_valence ++; + at[i].bond_type[i1] ++; + at[n].bond_type[i2] ++; + num_changes ++; + num_N_minus --; + num_N_plus --; + num_All -= 2; + continue; + } + } + } + else if ( 1 == at[i].charge && + 0 < num_O_plus && 0 < num_C_minus + num_N_minus && + 3 >= nNoMetalNumBonds(at, i) && 3 == nNoMetalBondsValence(at, i) && + 0 == num_of_H( at, i ) && + NULL != memchr( en+ELEM_O_FST, at[i].el_number, ELEM_O_LEN) ) + { + /* found non-terminal O(+)(III) */ + if ( (!type || 13 == type) && 0 < num_C_minus ) + { + int num_neigh = 0, pos_neigh = -1; + for ( i1 = 0; i1 < at[i].valence; i1 ++ ) + { + n = at[i].neighbor[i1]; + if ( -1 == at[n].charge && 3 >= at[n].valence && 3 == at[n].chem_bonds_valence+NUMH(at,n) && + /*0 == at[n].num_H &&*/ + at[i].bond_type[i1] == BOND_TYPE_SINGLE && + NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) + { + /* found O(+)(III)-C(-)(II); prepare conversion to O(IV)=C(IV) */ + num_neigh ++; + pos_neigh = i1; + } + } + if ( 1 == num_neigh && + !has_other_ion_neigh( at, i, n=at[i].neighbor[i1=pos_neigh], en, ne ) && + !has_other_ion_neigh( at, n, i, en, ne )) + { + /*n = at[i].neighbor[i1=pos_neigh];*/ + i2 = (int) ( is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor ); + at[i].charge --; + at[n].charge ++; + at[i].chem_bonds_valence ++; + at[n].chem_bonds_valence ++; + at[i].bond_type[i1] ++; + at[n].bond_type[i2] ++; + num_changes ++; + num_C_minus --; + num_O_plus --; + num_All -= 2; + continue; + } + } + if ( (!type || 14 == type) && 0 < num_C_minus ) + { + int num_neigh = 0, pos_neigh = -1; + for ( i1 = 0; i1 < at[i].valence; i1 ++ ) + { + n = at[i].neighbor[i1]; + if ( -1 == at[n].charge && 3 >= at[n].valence && 3 == at[n].chem_bonds_valence+NUMH(at,n) && + /*0 == at[n].num_H &&*/ + at[i].bond_type[i1] == BOND_TYPE_DOUBLE && + NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) + { + /* found O(+)(III)=C(-)(III); prepare conversion to O(IV)#C(IV) */ + num_neigh ++; + pos_neigh = i1; + } + } + if ( 1 == num_neigh && + !has_other_ion_neigh( at, i, n=at[i].neighbor[i1=pos_neigh], en, ne ) && + !has_other_ion_neigh( at, n, i, en, ne )) + { + /*n = at[i].neighbor[i1=pos_neigh];*/ + i2 = (int) ( is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor ); + at[i].charge --; + at[n].charge ++; + at[i].chem_bonds_valence ++; + at[n].chem_bonds_valence ++; + at[i].bond_type[i1] ++; + at[n].bond_type[i2] ++; + num_changes ++; + num_C_minus --; + num_O_plus --; + num_All -= 2; + continue; + } + } + if ( (!type || 15 == type) && 0 < num_N_minus ) + { + int num_neigh = 0, pos_neigh = -1; + for ( i1 = 0; i1 < at[i].valence; i1 ++ ) + { + n = at[i].neighbor[i1]; + if ( -1 == at[n].charge && 2 >= nNoMetalNumBonds(at, n) && + 2 == nNoMetalBondsValence(at, n)+NUMH(at, n) && + /*0 == num_of_H( at, n ) &&*/ + at[i].bond_type[i1] == BOND_TYPE_SINGLE && + NULL != memchr( en+ELEM_N_FST, at[n].el_number, ELEM_N_LEN) ) + { + /* found O(+)(III)=N(-)(II); prepare conversion to O(IV)#N(III) */ + num_neigh ++; + pos_neigh = i1; + } + } + if ( 1 == num_neigh && + !has_other_ion_neigh( at, i, n=at[i].neighbor[i1=pos_neigh], en, ne ) && + !has_other_ion_neigh( at, n, i, en, ne )) + { + /*n = at[i].neighbor[i1=pos_neigh];*/ + i2 = (int) ( is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor ); + at[i].charge --; + at[n].charge ++; + at[i].chem_bonds_valence ++; + at[n].chem_bonds_valence ++; + at[i].bond_type[i1] ++; + at[n].bond_type[i2] ++; + num_changes ++; + num_N_minus --; + num_O_plus --; + num_All -= 2; + continue; + } + } + } + } + } + + /**************************************************************************/ + /*********************** NON-Terminal ion triples *************************/ + /**************************************************************************/ + /*------------------------------------------------------------------------- + Non-Terminal triple types: 16, 17, 18 N=N,P,As,Sb; O=O,S,Se,Te; C=C,Si + ======================================== + 16: C(+)(III)-O-N(-)(II) => C(IV)=O=N(III) (allow terminal H on N(-)) + | | + 17: C(+)(III)-N-C(-)(III) => C(IV)=N=C(IV) + + 18: C(-)(III)-N=C(+)(III) => C(IV)=N#C(IV) (may have two or no charges) + C(IV)=N-C(II) => C(IV)=N#C(IV) + + */ + + if ( (!type || 16 == type) && 0 < num_C_plus && 0 < num_N_minus ) + { + int m[2], j[2], k; + for ( i = 0; i < num_atoms; i ++ ) + { + if ( 0 == at[i].charge && 2 == nNoMetalNumBonds(at, i) && 2 == nNoMetalBondsValence(at, i) && + 0 == num_of_H( at, i ) && + 0 <= (j[0] = nNoMetalNeighIndex( at, i )) && + at[m[0]=at[i].neighbor[j[0]]].charge && + 0 <= (j[1] = nNoMetalOtherNeighIndex( at, i, m[0] )) && + 0 == at[m[0]].charge + at[m[1]=at[i].neighbor[j[1]]].charge && + 5 >= nNoMetalBondsValence(at, m[0]) + nNoMetalBondsValence(at, m[1]) && + /*5 >= at[m[0]].chem_bonds_valence + at[m[1]].chem_bonds_valence &&*/ + NULL != memchr( en+ELEM_O_FST, at[i].el_number, ELEM_O_LEN) ) + { + /* found non-terminal A(+)-O-B(-); chem_bond_val of A+B <= 5 */ + int n_N=-1, n_C=-1, i_C=-1; + for ( k = 0; k < 2; k ++ ) + { + n = m[k]; + if ( -1 == at[n].charge && 2 == nNoMetalNumBonds(at, n)+NUMH(at, n) && + /*0 == num_of_H( at, n ) &&*/ + NULL != memchr( en+ELEM_N_FST, at[n].el_number, ELEM_N_LEN) ) + { + n_N = n; + } + else if ( 1 == at[n].charge && 3 == at[n].chem_bonds_valence+NUMH(at,n) && + NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) + { + n_C = n; + i_C = k; + } + } + if ( n_C < 0 || n_N < 0 || + has_other_ion_in_sphere_2(at, n_C, n_N, en, ne ) || + has_other_ion_in_sphere_2(at, n_N, n_C, en, ne ) ) + { + continue; + } + + /* C(+)(III)-O-N(-)(II) => C(IV)=O=N(III) */ + for ( k = 0; k < 2; k ++ ) + { + n = k? n_C : n_N; + i1 = k? j[i_C] : j[1-i_C]; + i2 = (int) ( is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor ); + at[i].bond_type[i1] ++; + at[n].bond_type[i2] ++; + at[i].chem_bonds_valence ++; + at[n].chem_bonds_valence ++; + at[n].charge += (k? -1:1); + } + num_changes ++; + num_N_minus --; + num_C_plus --; + num_All -= 2; + } + } + } + + if ( (!type || 17 == type) && 0 < num_C_plus && 0 < num_C_minus ) + { + int m[3], c[3], j[3], k; + for ( i = 0; i < num_atoms; i ++ ) + { + if ( 0 == at[i].charge && 3 == nNoMetalNumBonds(at, i) && 3 == nNoMetalBondsValence(at, i) && + 0 == num_of_H( at, i ) && + 0 <= ( j[0] = nNoMetalNeighIndex(at, i) ) && + 0 <= ( j[1] = nNoMetalOtherNeighIndex( at, i, m[0] = at[i].neighbor[j[0]] ) ) && + 0 <= ( j[2] = nNoMetalOtherNeighIndex2( at, i, m[0], m[1] = at[i].neighbor[j[1]] ) ) && + 1 == !(c[0]=at[m[0]].charge) + + !(c[1]=at[m[1]].charge) + + !(c[2]=at[m[2]=at[i].neighbor[j[2]]].charge) && + 0 == c[0] + c[1] + c[2] && + 2 == (3== (c[0]? at[m[0]].chem_bonds_valence+NUMH(at,m[0]):0)) + + (3== (c[1]? at[m[1]].chem_bonds_valence+NUMH(at,m[1]):0)) + + (3== (c[2]? at[m[2]].chem_bonds_valence+NUMH(at,m[2]):0)) && + NULL != memchr( en+ELEM_N_FST, at[i].el_number, ELEM_N_LEN) ) + { + /* found non-terminal A(+)-O-B(-) */ + int n_Cp=-1, n_Cm=-1, i_Cp=-1, i_Cm=-1; /* p = positive, m = negatice ion C */ + for ( k = 0; k < 3; k ++ ) + { + if ( c[k] ) + { + n = m[k]; + if ( -1 == at[n].charge && + NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) + { + n_Cm = n; + i_Cm = k; + } + else if ( 1 == at[n].charge && + NULL != memchr( en+ELEM_C_FST, at[n].el_number, ELEM_C_LEN) ) + { + n_Cp = n; + i_Cp = k; + } + } + } + if ( n_Cp < 0 || n_Cm < 0 || + has_other_ion_in_sphere_2(at, n_Cp, n_Cm, en, ne ) || + has_other_ion_in_sphere_2(at, n_Cm, n_Cp, en, ne )) + { + continue; + } + + /* | | */ + /* C(+)(III)-N-C(-)(III) => C(IV)=N=C(IV) */ + for ( k = 0; k < 2; k ++ ) + { + n = k? n_Cp : n_Cm; + i1 = k? j[i_Cp] : j[i_Cm]; + i2 = (int) ( is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor ); + at[i].bond_type[i1] ++; + at[n].bond_type[i2] ++; + at[i].chem_bonds_valence ++; + at[n].chem_bonds_valence ++; + at[n].charge += (k? -1:1); + } + num_changes ++; + num_C_minus --; + num_C_plus --; + num_All -= 2; + } + } + } + + if ( (!type || 18 == type) && (0 < num_C_plus && 0 < num_C_minus || 0 < num_C_II) ) + { + int m[2], v[2], j[2], k; + for ( i = 0; i < num_atoms; i ++ ) + { + if ( 0 == at[i].charge && 2 == nNoMetalNumBonds(at, i) && 3 == nNoMetalBondsValence(at, i) && + 0 == num_of_H( at, i ) && + 0 <= (j[0] = nNoMetalNeighIndex( at, i )) && + 0 <= (j[1] = nNoMetalOtherNeighIndex( at, i, m[0] = at[i].neighbor[j[0]] )) && + 0 == at[m[0]].charge + +at[m[1]=at[i].neighbor[j[1]]].charge && + 6 == (v[0]=at[m[0]].chem_bonds_valence+NUMH(at,m[0])) + +(v[1]=at[m[1]].chem_bonds_valence+NUMH(at,m[1])) && + 2 >= abs(v[0]-v[1]) && + NULL != memchr( en+ELEM_N_FST, at[i].el_number, ELEM_N_LEN) && + NULL != memchr( en+ELEM_C_FST, at[m[0]].el_number, ELEM_C_LEN) && + NULL != memchr( en+ELEM_C_FST, at[m[1]].el_number, ELEM_C_LEN) ) + { + /* n_Cm i n_Cp */ + /* found non-terminal C(-)(III)-N=C(+)(III) or C(IV)=N-C(II): Cm-N-Cp */ + /* convert to C(IV)=N#C(IV) */ + int n_Cp=-1, n_Cm=-1, i_Cp=-1, i_Cm=-1; /* p = positive, m = negatice ion C */ + for ( k = 0; k < 2; k ++ ) + { + n = m[k]; + if ( v[k] == 4 || v[k] == 3 && at[i].bond_type[j[k]] == BOND_TYPE_SINGLE ) + { + n_Cm = n; + i_Cm = k; + } + else if ( v[k] == 2 || v[k] == 3 && at[i].bond_type[j[k]] == BOND_TYPE_DOUBLE ) + { + n_Cp = n; + i_Cp = k; + } + } + if ( n_Cp < 0 || n_Cm < 0 || at[n_Cp].valence+NUMH(at,n_Cp) != 2 ) + { + continue; /* guarantees at[n_Cp].valence <= 2 */ + } + if ( v[i_Cp] == 2 || !at[n_Cp].charge ) + { + if ( at[n_Cp].valence == 2 ) + { + /* neighbor of at[n_Cp] opposite to at[i] */ + k = at[n_Cp].neighbor[at[n_Cp].neighbor[0]==i]; + if ( NULL != memchr( en+ELEM_N_FST, at[k].el_number, ELEM_N_LEN) ) + { + continue; + } + } + } + else if ( at[n_Cp].charge ) + { + if ( has_other_ion_in_sphere_2(at, n_Cp, n_Cm, en, ne ) || + has_other_ion_in_sphere_2(at, n_Cm, n_Cp, en, ne )) + { + continue; + } + } + else + { + continue; /* unknown case */ + } + + /* */ + /* C(-)(III)-N=C(+)(III) => C(IV)=N#C(IV) */ + /* C(IV)=N-C(II) => C(IV)=N#C(IV) */ + if ( at[n_Cp].charge ) + { + num_C_minus --; + num_C_plus --; + num_All -= 2; + } + else + { + num_C_II --; + num_All --; + } + + for ( k = 0; k < 2; k ++ ) + { + n = k? n_Cp : n_Cm; + i3 = k? i_Cp : i_Cm; /* added to fix the bug */ + /*i1 = k? j[i_Cp] : j[i_Cm];*/ /* replaced with next line */ + i1 = j[i3]; + if ( v[i3 /*was i1*/] < 4 ) + { + /* WDI found a bug here: bounds violation */ + int delta = 4 - v[i3 /*was i1*/]; + i2 = (int) ( is_in_the_list( at[n].neighbor, (AT_NUMB)i, at[n].valence ) - at[n].neighbor ); + at[i].bond_type[i1] += delta; + at[n].bond_type[i2] += delta; + at[i].chem_bonds_valence += delta; + at[n].chem_bonds_valence += delta; + at[n].charge = 0; + at[n].radical = 0; + } + } + at[i].charge = 0; + at[i].radical = 0; + num_changes ++; + } + } + } + } + + return num_changes; +} + + + + +/*#if ( DISCONNECT_SALTS == 1 )*/ /* { */ + + +int RemoveInpAtBond( inp_ATOM *atom, int iat, int k ) +{ + int i, j, m, m2, k2; + inp_ATOM *at = atom + iat; + inp_ATOM *at2 = NULL; + int val = at->valence - 1; + + if ( val >= 0 ) + { + int bond = at->bond_type[k]; + if ( bond > BOND_TYPE_TRIPLE ) + bond = BOND_TYPE_SINGLE; /* added 08-06-2003 */ + + /* update CML tetrahedral atom parity. */ + if ( at->p_parity ) + { + for( m = 0; m < MAX_NUM_STEREO_ATOM_NEIGH; m ++ ) + { + if ( at->p_orig_at_num[m] == at->orig_at_number ) + { + at->p_parity = 0; + break; /* only 3 bonds are present; removing one bond removes stereo */ + } + } + if ( at->p_parity /* at->valence == MAX_NUM_STEREO_ATOM_NEIGH*/ ) + { + for ( m = 0; m < at->valence; m ++ ) + { + if ( atom[(int)at->neighbor[k]].orig_at_number == at->p_orig_at_num[m] ) + { + break; + } + } + if ( m < at->valence ) + { + at->p_orig_at_num[m] = at->orig_at_number; + } + else + { + at->p_parity = 0; /* wrong neighbors: at->neighbor[k] is not in the list of a stereo neighbors */ + } + } + } + + + /* update CML stereogenic bond parities; at this point no removed explicit H exist yet */ + if ( at->sb_parity[0] ) + { + for ( m = 0; m < MAX_NUM_STEREO_BONDS && at->sb_parity[m]; ) + { + if ( k == at->sb_ord[m] || k == at->sn_ord[m] && val < 2 && ATOM_PARITY_WELL_DEF(at->sb_parity[m]) ) + { + /* !!! FLAW: does take into account removed H !!! */ + /* stereogenic bond is being removed OR */ + /* remove stereogenic bond because its only neighbor is being removed */ + int pnxt_atom, pinxt2cur, pinxt_sb_parity_ord; + int len= get_opposite_sb_atom( atom, iat, at->sb_ord[m], &pnxt_atom, &pinxt2cur, &pinxt_sb_parity_ord ); + if ( len ) + { + i = pinxt_sb_parity_ord; + at2 = atom + pnxt_atom; + k2 = pinxt2cur; + } + else + { + i = MAX_NUM_STEREO_BONDS; + } + /* + at2 = atom + at->neighbor[ (int)at->sb_ord[m] ]; + for ( i = 0; i < MAX_NUM_STEREO_BONDS && at2->sb_parity[i]; i ++ ) + { + if ( iat == at2->neighbor[ (int)at2->sb_ord[i] ] ) + break; + } + */ + if ( i < MAX_NUM_STEREO_BONDS && at2->sb_parity[i] ) + { + m2 = i; + /* remove bond parity from at */ + if ( m < MAX_NUM_STEREO_BONDS-1 ) + { + memmove( at->sb_parity+m, at->sb_parity+m+1, (MAX_NUM_STEREO_BONDS-1 - m) * sizeof(at->sb_parity[0])); + memmove( at->sb_ord+m, at->sb_ord+m+1, (MAX_NUM_STEREO_BONDS-1 - m) * sizeof(at->sb_ord[0])); + memmove( at->sn_ord+m, at->sn_ord+m+1, (MAX_NUM_STEREO_BONDS-1 - m) * sizeof(at->sn_ord[0])); + memmove( at->sn_orig_at_num+m, at->sn_orig_at_num+m+1, (MAX_NUM_STEREO_BONDS-1 - m) * sizeof(at->sn_orig_at_num[0])); + } + at->sb_parity[MAX_NUM_STEREO_BONDS-1] = 0; + at->sb_ord[MAX_NUM_STEREO_BONDS-1] = 0; + at->sn_ord[MAX_NUM_STEREO_BONDS-1] = 0; + at->sn_orig_at_num[MAX_NUM_STEREO_BONDS-1] = 0; + /* remove bond parity from at2 */ + if ( m2 < MAX_NUM_STEREO_BONDS-1 ) + { + memmove( at2->sb_parity+m2, at2->sb_parity+m2+1, (MAX_NUM_STEREO_BONDS-1 - m2) * sizeof(at2->sb_parity[0])); + memmove( at2->sb_ord+m2, at2->sb_ord+m2+1, (MAX_NUM_STEREO_BONDS-1 - m2) * sizeof(at2->sb_ord[0])); + memmove( at2->sn_ord+m2, at2->sn_ord+m2+1, (MAX_NUM_STEREO_BONDS-1 - m2) * sizeof(at2->sn_ord[0])); + memmove( at2->sn_orig_at_num+m2, at2->sn_orig_at_num+m2+1, (MAX_NUM_STEREO_BONDS-1 - m2) * sizeof(at2->sn_orig_at_num[0])); + } + at2->sb_parity[MAX_NUM_STEREO_BONDS-1] = 0; + at2->sb_ord[MAX_NUM_STEREO_BONDS-1] = 0; + at2->sn_ord[MAX_NUM_STEREO_BONDS-1] = 0; + at2->sn_orig_at_num[MAX_NUM_STEREO_BONDS-1] = 0; + /* do not increment m here because the array elements have been shifted */ + } + else + { + m ++; /* program error: inconsistent stereobond parity */ + } + } + else if ( k == at->sn_ord[m] ) + { + /* stereogenic bond neighbor is being removed; another neighbor remains */ + /* !!! FLAW: does take into account removed H !!! */ + for ( j = 0, i = -1; j < at->valence; j ++ ) + { + if ( j != k && j != at->sb_ord[m] ) + { + i = j; + break; + } + } + + /* i is the position of the neighbor that will become a new neighbor */ + /*************************************************************************** + * at->sb_parity[m] is the direction (EVEN=clockwise, ODD=counterclockwise) + * from stereobond to the neighbor. If the neighbor is removed then + * the parity should invert, otherwise it should be unchanged. + ***************************************************************************/ + if ( i < 0 ) + { + /* no alternative neighbor is available */ + if ( ATOM_PARITY_WELL_DEF(at->sb_parity[m] ) ) + { + /* parity cannot be not well-defined anymore */ + int pnxt_atom, pinxt2cur, pinxt_sb_parity_ord; + int len= get_opposite_sb_atom( atom, iat, at->sb_ord[m], &pnxt_atom, &pinxt2cur, &pinxt_sb_parity_ord ); + if ( len > 0 ) + { + atom[pnxt_atom].sb_parity[pinxt_sb_parity_ord] = at->sb_parity[m] = AB_PARITY_UNDF; + } + } + at->sn_ord[m] = -99; /* sb neighbor has been disconnected */ + at->sb_ord[m] -= (at->sb_ord[m] > k); /* same as above */ + at->sn_orig_at_num[m] = 0; + } + else if ( i < at->valence ) + { + /* choose another stereogenic bond neighbor, its ord. number is i before bond removal */ + if ( ATOM_PARITY_WELL_DEF(at->sb_parity[m]) ) + { + /* ALL WRONG: 'move' previous stereo bond neighbor to the last position (pos. 2 out of 0,1,2) */ + /* the parity of the transpositions is (2 - at->sn_ord[m])%2 = at->sn_ord[m] % 2 */ + /* and replace the neighbor with another; the contribution to the parity is 1 */ + + /*at->sb_parity[m] = 2 - ( at->sb_parity[m] + at->sn_ord[m] + 1 ) % 2;*/ + + /*at->sb_parity[m] = 2 - ( at->sb_parity[m] + k + i + + (i > k) + (i > at->sb_ord[m]) ) % 2;*/ + /*=== parity should be INVERTED ===*/ + at->sb_parity[m] = 3 - at->sb_parity[m]; + } + at->sn_ord[m] = i - (i > k); /* ord. number shifted because preceding bond is removed */ + at->sb_ord[m] -= (at->sb_ord[m] > k); /* same as above */ + at->sn_orig_at_num[m] = atom[(int)at->neighbor[i]].orig_at_number; + /*at->sb_parity[m] = 2 - ( at->sb_parity[m] + 1 ) % 2;*/ + } + else + { + at->sb_parity[m] = 0; /* program error: inconsistent stereobond parity */ + } + m ++; + } + else + { + /* removing another neighbor, k: first move it to the last position (pos. 2 out of 0,1,2) */ + if ( k < 2 && ATOM_PARITY_WELL_DEF(at->sb_parity[m]) ) + { + /*at->sb_parity[m] = 2 - ( at->sb_parity[m] + k ) % 2;*/ + /*at->sb_parity[m] = 2 - ( at->sb_parity[m] + (at->sn_ord[m] > k) + (at->sb_ord[m] > k) ) % 2;*/ + ;/*==== Parity should remain UNCHANGED ===*/ + } + if ( at->sb_ord[m] > k ) + { + at->sb_ord[m] --; + } + if ( at->sn_ord[m] > k ) + { + at->sn_ord[m] --; + } + m ++; + } + } + } + + if ( k < val ) + { + memmove( at->neighbor+k, at->neighbor+k+1, sizeof(at->neighbor[0])*(val-k) ); + memmove( at->bond_stereo+k, at->bond_stereo+k+1, sizeof(at->bond_stereo[0])*(val-k) ); + memmove( at->bond_type+k, at->bond_type+k+1, sizeof(at->bond_type[0])*(val-k) ); + } + + at->neighbor[val] = 0; + at->bond_stereo[val] = 0; + at->bond_type[val] = 0; + at->valence = val; + at->chem_bonds_valence -= bond; + return 1; + } + + return 0; +} + + +/***************************************************************************************/ +int DisconnectInpAtBond( inp_ATOM *at, AT_NUMB *nOldCompNumber, int iat, int neigh_ord ) +{ + int neigh, i, ret = 0; + int component; + neigh = at[iat].neighbor[neigh_ord]; + + for ( i = 0; i < at[neigh].valence; i ++ ) + { + if ( iat == (int)at[neigh].neighbor[i] ) + break; + } + + if ( i < at[neigh].valence ) + { + ret += RemoveInpAtBond( at, iat, neigh_ord ); + ret += RemoveInpAtBond( at, neigh, i ); + if ( nOldCompNumber && ret ) + { + if ( component = at[iat].component ) + { + nOldCompNumber[component-1] = 0; + } + if ( component = at[neigh].component ) + { + nOldCompNumber[component-1] = 0; + } + } + } + + return (ret == 2); +} + + + + +/*************************************************************************************************/ +int bIsAmmoniumSalt( inp_ATOM *at, + int i, + int *piO, + int *pk, + S_CHAR *num_explicit_H ) +{ + /* NH4(+charge)-O(-charge)-C -> NH3 + HO-C; any charge including 0, */ + /* any C except charged or radical F, Cl, Br, I */ + + static U_CHAR el_number_C=0, el_number_O=0, el_number_H=0, el_number_N=0; + static U_CHAR el_number_F=0, el_number_Cl=0, el_number_Br=0, el_number_I=0; + int num_H, num_non_iso_H, num_impl_iso_H, bDisconnect = 1; + int j, val, neigh, iO=-1, iC, k=-1; + + if ( 0 == el_number_C ) + { + /* one time initialization */ + el_number_C = get_periodic_table_number( "C" ); + el_number_O = get_periodic_table_number( "O" ); + el_number_H = get_periodic_table_number( "H" ); + el_number_N = get_periodic_table_number( "N" ); + el_number_F = get_periodic_table_number( "F" ); + el_number_Cl= get_periodic_table_number( "Cl" ); + el_number_Br= get_periodic_table_number( "Br" ); + el_number_I = get_periodic_table_number( "I" ); + } + if ( at[i].el_number != el_number_N ) + return 0; + + /* check for NH4-O-C... -> NH3 + HO-C... */ + val = at[i].valence; + num_impl_iso_H = NUM_ISO_H(at,i); + num_non_iso_H = at[i].num_H; + num_H = num_non_iso_H + num_impl_iso_H; + if ( val + num_H == 5 ) + { + int num_O = 0; + memset( num_explicit_H, 0, (NUM_H_ISOTOPES+1)*sizeof(num_explicit_H[0]) ); + for ( j = 0; j < val; j ++ ) + { /* looking for O: H4N-O-C... */ + neigh = at[i].neighbor[j]; + if ( at[neigh].num_H || + at[neigh].charge && (at[neigh].el_number != el_number_O || at[neigh].charge + at[i].charge) || + at[neigh].radical && at[neigh].radical != RADICAL_SINGLET ) + { + bDisconnect = 0; + break; /* reject */ + } + if ( at[neigh].el_number == el_number_H && at[neigh].valence == 1 && + !at[neigh].charge && !at[neigh].radical ) { + num_H ++; /* at this point at[].num_H does not include explicit H count */ + num_non_iso_H += (0==at[neigh].iso_atw_diff); + num_explicit_H[at[neigh].iso_atw_diff] ++; /* explicit H on N */ + } + else if ( at[neigh].el_number == el_number_O && at[neigh].valence == 2 && !num_O ) + { + num_O ++; /* found O: N-O- */ + iO = neigh; + k = j; + iC = at[iO].neighbor[at[iO].neighbor[0] == i]; + if ( at[iC].el_number != el_number_C || /* + at[iC].num_H || + at[iC].chem_bonds_valence != 4 || */ + at[iC].charge || + at[iC].radical && at[iC].radical != RADICAL_SINGLET /*|| + at[iC].valence == at[iC].chem_bonds_valence*/ ) + { + bDisconnect = 0; + break; /* reject */ + } + } + else if ( (at[neigh].el_number == el_number_F || + at[neigh].el_number == el_number_Cl || + at[neigh].el_number == el_number_Br || + at[neigh].el_number == el_number_I ) && + at[neigh].valence == 1 && at[neigh].chem_bonds_valence == 1 && + !at[neigh].charge && !NUMH(at,neigh) && !num_O ) + { + num_O ++; /* found O: N-O- */ + iO = neigh; + k = j; + iC = -1; + } else + { + bDisconnect = 0; + break; /* reject */ + } + } + if ( bDisconnect && (num_O != 1 || num_H != 4) ) + { + bDisconnect = 0; /* reject */ + } + } + else + { + bDisconnect = 0; + } + if ( bDisconnect ) + { + *piO = iO; + *pk = k; + } + + return bDisconnect; +} + + + + +/*************************************************************************************************/ +int DisconnectAmmoniumSalt ( inp_ATOM *at, + int iN, + int iO, + int k, + S_CHAR *num_explicit_H ) +{ + + /* disconnect NH4-O from O */ + /* Note: iO = at[iN].neighbor[k], at[iN] is N, at[iO].neighbor[0] is either N=at[iN] or C=at[iC] */ + + int nMove_H_iso_diff = -1; /* do not move explicit H */ + int j, neigh, iso_diff, neigh_pos; + static U_CHAR el_number_H = 0; + int val = at[iN].valence; + + if ( !el_number_H ) + { + el_number_H = get_periodic_table_number( "H" ); + } + if ( at[iN].charge && !(at[iN].charge + at[iO].charge) ) + { + at[iN].charge = at[iO].charge = 0; /* remove charges */ + } + + neigh_pos = (at[iO].valence == 2)? (at[iO].neighbor[1] == iN) : 0; /* position of at[iN] in the neigh list of iO */ + /* disconnect bond O-N */ + RemoveInpAtBond( at, iO, neigh_pos ); + RemoveInpAtBond( at, iN, k ); + val --; + + /* move 1 H from NH4 to O- or Cl */ + + /* find non-isotopic or the lightest isotopic H to move from N to O */ + for ( iso_diff = 0; iso_diff <= NUM_H_ISOTOPES; iso_diff ++ ) + { + if ( !iso_diff ) + { + /* find non-isotopic H */ + if ( at[iN].num_H ) { + at[iN].num_H --; /* move non-isotopic implicit H */ + at[iO].num_H ++; + break; + } + else if ( num_explicit_H[0] ) + { + nMove_H_iso_diff = 0; /* flag: move explicit non-isotopic H */ + break; + } + } + else + { + /* find isotopic H */ + if ( at[iN].num_iso_H[iso_diff] ) + { + at[iN].num_iso_H[iso_diff] --; /* move implicit isotopic H, atw = 1 */ + at[iO].num_iso_H[iso_diff] ++; + break; + } + else if ( num_explicit_H[iso_diff] ) + { + nMove_H_iso_diff = iso_diff; /* flag: move explicit isotopic H, atw = 1 */ + break; + } + } + } + + if ( nMove_H_iso_diff >= 0 ) + { + /* move explicit H, it is isotopic if nMove_H_iso_diff > 0 */ + double dist2_H_O, min_dist2_H_O = -1.0; + int jH = -1, iH = -1; + for ( j = 0; j < val; j ++ ) + { /* looking H in N-H such that H-O is shortest */ + neigh = at[iN].neighbor[j]; + if ( at[neigh].el_number == el_number_H && + at[neigh].iso_atw_diff == nMove_H_iso_diff ) + { + dist2_H_O = (at[neigh].x - at[iO].x) * (at[neigh].x - at[iO].x) + + (at[neigh].y - at[iO].y) * (at[neigh].y - at[iO].y) + + (at[neigh].z - at[iO].z) * (at[neigh].z - at[iO].z); + if ( min_dist2_H_O < 0.0 || min_dist2_H_O > dist2_H_O ) { + min_dist2_H_O = dist2_H_O; + iH = neigh; + jH = j; + } + } + } + + /* reconnect; bonds do not need changes except stereo */ + neigh_pos = at[iO].valence; + at[iO].neighbor[neigh_pos] = iH; + at[iO].bond_stereo[neigh_pos] = 0; + at[iO].bond_type[neigh_pos] = at[iH].bond_type[0]; + at[iO].chem_bonds_valence += at[iH].bond_type[0]; + at[iO].valence ++; + at[iH].neighbor[0] = iO; + at[iH].bond_stereo[0] = 0; + + /* disconnect H from N */ + RemoveInpAtBond( at, iN, jH ); + val --; + if ( k > jH ) + { + k --; + } + } + + return 1; +} + + +/*************************************************************************************************/ +int bIsMetalSalt( inp_ATOM *at, int i ) +{ + int type, val, k, iO, iC, j, neigh; + int bDisconnect = 1; + static U_CHAR el_number_C=0, el_number_O=0, el_number_H=0; + static U_CHAR el_number_F=0, el_number_Cl=0, el_number_Br=0, el_number_I=0; + + if ( 0 == el_number_C ) + { + /* one time initialization */ + el_number_C = get_periodic_table_number( "C" ); + el_number_O = get_periodic_table_number( "O" ); + el_number_H = get_periodic_table_number( "H" ); + el_number_F = get_periodic_table_number( "F" ); + el_number_Cl= get_periodic_table_number( "Cl" ); + el_number_Br= get_periodic_table_number( "Br" ); + el_number_I = get_periodic_table_number( "I" ); + } + + /* check for a metal atom: + metal atom should be connected and be a metal */ + if ( !(val = at[i].valence) || + !(type = get_el_type( at[i].el_number )) || + !(type & IS_METAL) ) + { + bDisconnect = 0; /* reject */ + } + else if ( at[i].num_H ) + /* metal atom should not have adjacent H or multiple bonds or radical */ + { + bDisconnect = 0; /* reject */ + } + else + /* check valence */ + if ( at[i].charge == 0 && + ( (type & 1) && val == get_el_valence( at[i].el_number, 0, 0 ) || + (type & 2) && val == get_el_valence( at[i].el_number, 0, 1 ) ) || + at[i].charge > 0 && + (type & 1) && val == get_el_valence( at[i].el_number, at[i].charge, 0 ) ) + { + ; /* accept */ + } + else + { + bDisconnect = 0; /* reject */ + } + + if ( bDisconnect ) + { + /************************************************************************* + * | * + * check M neighbors. Disconnect if all neighbors are M-O-C# or M-O-C= * + * | * + *************************************************************************/ + for ( k = 0; k < at[i].valence; k ++ ) + { + iO = at[i].neighbor[k]; + /* halogenide 2004-07-08 */ + if ( (at[iO].el_number == el_number_F || + at[iO].el_number == el_number_Cl || + at[iO].el_number == el_number_Br || + at[iO].el_number == el_number_I ) && + at[iO].valence == 1 && at[iO].chem_bonds_valence == 1 && + !at[iO].charge && !(at[iO].radical && at[iO].radical != RADICAL_SINGLET) && !NUMH(at,iO) ) + { + ; /* found */ + } + else + { + /* -O-C= */ + if ( at[iO].el_number != el_number_O || + NUMH(at, iO) || + at[iO].valence != 2 || + at[iO].charge || + at[iO].radical && at[iO].radical != RADICAL_SINGLET || + at[iO].valence != at[iO].chem_bonds_valence ) + { + bDisconnect = 0; /* reject */ + break; + } + iC = at[iO].neighbor[at[iO].neighbor[0] == i]; + if ( at[iC].el_number != el_number_C || + at[iC].num_H || + at[iC].chem_bonds_valence != 4 || + at[iC].charge || + at[iC].radical && at[iC].radical != RADICAL_SINGLET || + at[iC].valence == at[iC].chem_bonds_valence ) + { + bDisconnect = 0; /* reject */ + break; + } + for ( j = 0; j < at[iC].valence; j ++ ) + { + neigh = at[iC].neighbor[j]; + if ( at[neigh].el_number == el_number_H ) + { + break; + } + } + if ( j != at[iC].valence ) + { + bDisconnect = 0; /* reject */ + break; + } + } + } + } + + return bDisconnect; +} + + +/*************************************************************************************************/ +int DisconnectMetalSalt( inp_ATOM *at, int i ) +{ + int k, iO; + /* disconnect metal atom or ion at[i] */ + + for ( k = 0; k < at[i].valence; k ++ ) + { + iO = at[i].neighbor[k]; + if ( at[iO].valence == 2 ) + { + if ( at[iO].neighbor[0] == i ) + { + /* assuming atom O always has 2 bonds */ + /* copy the remaining neighbor to the 0 position */ + at[iO].neighbor[0] = at[iO].neighbor[1]; + at[iO].bond_stereo[0] = at[iO].bond_stereo[1]; + at[iO].bond_type[0] = at[iO].bond_type[1]; + } + /* clear neighbor at position 1 */ + at[iO].neighbor[1] = 0; + at[iO].bond_stereo[1] = 0; + at[iO].bond_type[1] = 0; + } + else + { + /* clear neighbor at position 1 */ + at[iO].neighbor[0] = 0; + at[iO].bond_stereo[0] = 0; + at[iO].bond_type[0] = 0; + } + + /* make O negatively charged */ + at[iO].charge = -1; + + /* reduce O valence to account for the removed single bond */ + at[iO].valence --; + at[iO].chem_bonds_valence --; + + /* clear metal neighbor (O) */ + at[i].neighbor[k] = 0; + at[i].bond_stereo[k] = 0; + at[i].bond_type[k] = 0; + + /* add a positive charge to the metal */ + at[i].charge ++; + } + + /* set metal valence to zero because it has been disconnected */ + at[i].valence = 0; + at[i].chem_bonds_valence = 0; + + return k; +} + + +/*************************************************************************************************/ +int DisconnectSalts( ORIG_ATOM_DATA *orig_inp_data, int bDisconnect ) +{ + int i, k, iO, num_changes, val; + S_CHAR num_explicit_H[NUM_H_ISOTOPES+1]; + inp_ATOM *at = orig_inp_data->at; + int num_at = orig_inp_data->num_inp_atoms; + + /* check each atom */ + for ( i = 0, num_changes = 0; i < num_at; i ++ ) + { + + if ( !(val = at[i].valence) || /* disconnected atom */ + val != at[i].chem_bonds_valence || /* a bond has higher multiplicity than 1 */ + at[i].radical && at[i].radical != RADICAL_SINGLET /* radical */ ) + { + continue; /* reject */ + } + + if ( bIsAmmoniumSalt( at, i, &iO, &k, num_explicit_H ) ) + { + if ( bDisconnect ) + { + DisconnectAmmoniumSalt ( at, i, iO, k, num_explicit_H ); + orig_inp_data->num_inp_bonds --; + } + + /* count disconnected atoms */ + num_changes ++; + } + else if ( bIsMetalSalt( at, i ) ) + { + if ( bDisconnect ) + { + k = DisconnectMetalSalt( at, i ); + orig_inp_data->num_inp_bonds -= k; + } + num_changes ++; + } + } + + return num_changes; +} + + +/*****************************************************************************/ +/* Important: Salt disconnection is independent from coord. disconnection: */ +/* because different atoms are disconnected. */ +/* However, sal disconnection may need to be rerun after metal disconnection */ +/* because metal disconnection may make certain atoms be eligible for salt */ +/* disconnection */ +/*****************************************************************************/ +int bIsMetalToDisconnect(inp_ATOM *at, int i, int bCheckMetalValence) +{ + int type, at_valence, num_H; + + /* + if ( !at[i].valence ) + */ + + if ( !(type = get_el_type( at[i].el_number )) || + !(type & IS_METAL ) ) + { + return 0; + } + + num_H = NUMH(at,i); + at_valence = num_H + at[i].chem_bonds_valence; + + if ( !at_valence ) + { + return 0; /* nothing to disconnect */ + } + + if ( bCheckMetalValence ) + { + if ( abs(at[i].charge) > 1 ) + { + return 1; /* multiple charges */ + } + + for ( i = 0; i < 2 && (i & type); i ++ ) + { + if ( at_valence == get_el_valence( at[i].el_number, at[i].charge, i ) ) + { + return 2; /* atom has normal valence */ + } + } + } + + return 1; +} + + +/*****************************************************************************/ +int bMayDisconnectMetals( ORIG_ATOM_DATA *orig_inp_data, + int bCheckMetalValence, + INCHI_MODE *bTautFlagsDone ) +{ + int i, j, k, iO, num_changes, val, bRadOrMultBonds, num_impl_H = 0; + S_CHAR num_explicit_H[NUM_H_ISOTOPES+1]; + inp_ATOM *at = orig_inp_data->at; + int num_at = orig_inp_data->num_inp_atoms; + int *nNumImplH = &orig_inp_data->bDisconnectCoord; + + /* check each atom */ + for ( i = 0, num_changes = 0; i < num_at; i ++ ) + { + + if ( !(val = at[i].valence) && !NUMH(at,i) ) + { + continue; /* disconnected atom */ + } + + bRadOrMultBonds = (val == 0) || + (val != at[i].chem_bonds_valence) || /* a bond has higher multiplicity than 1 */ + (at[i].radical && at[i].radical != RADICAL_SINGLET); /* radical */ + + if ( !bRadOrMultBonds && bIsAmmoniumSalt( at, i, &iO, &k, num_explicit_H ) ) + { + ; + } + else if ( !bRadOrMultBonds && bIsMetalSalt( at, i ) ) + { + ; + } + else if ( 1 == (j = bIsMetalToDisconnect(at, i, bCheckMetalValence)) ) + { + num_impl_H += NUMH(at,i); + num_changes ++; + } + else if ( 2 == j && bTautFlagsDone ) + { + *bTautFlagsDone |= TG_FLAG_CHECK_VALENCE_COORD_DONE; + } + } + + if ( nNumImplH ) + *nNumImplH = num_changes ? num_impl_H+1 + : 0; + + return num_changes; +} + + + + +/*****************************************************************************/ +#if ( bRELEASE_VERSION == 0 && (EXTR_HAS_METAL_ATOM & (EXTR_MASK | EXTR_FLAG) ) ) + +int bHasMetalAtom( ORIG_ATOM_DATA *orig_inp_data ) +{ + int i; + inp_ATOM *at; + + if ( orig_inp_data && (at = orig_inp_data->at) ) + { + int num_at = orig_inp_data->num_inp_atoms; + /* check each atom */ + for ( i = 0; i < num_at; i ++ ) + { + if ( IS_METAL & get_el_type( at[i].el_number ) ) + { + return 1; + } + } + } + + return 0; +} +#endif + + + +/***************************************************************************** +{ "F", 19, 19, 18.998403220, 0 , 0, {{0,}, {0,}, {1,}, {2,}, {3,5}, },}, +{ "Cl", 35, 35, 34.968852730, 0 , 0, {{0,}, {0,}, {1,3,5,7}, {2,4,6}, {3,5,}, },}, +{ "Br", 80, 79, 78.918336100, 0 , 0, {{0,}, {0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, },}, +{ "I", 127, 127, 126.904500000, 0 , 0, {{0,}, {0,}, {1,3,5,7,}, {2,4,6}, {3,5,}, },}, +{ "At", 210, 210, 209.987100000, 0 , 0, {{0,}, {0,}, {1,3,5,7,}, {2,4,6}, {3,5,}, },}, +{ "N", 14, 14, 14.003074000, 0 , 0, {{1,}, {2,}, {3,5}, {4,}, {3,}, },}, +{ "P", 31, 31, 30.973762000, 0 , 0, {{1,3,5,7,}, {2,4,6,}, {3,5,}, {4,}, {3,}, },}, +{ "As", 75, 75, 74.921594200, 0 , 0, {{0,}, {2,4,6,}, {3,5,}, {4,}, {3,}, },}, +{ "Sb", 122, 121, 120.903800000, 0 , 0, {{1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,}, {3,}, },}, +{ "O", 16, 16, 15.994914630, 0 , 0, {{0,}, {1,}, {2,}, {3,5,}, {4,}, },}, +{ "S", 32, 32, 31.972070700, 0 , 0, {{0,}, {1,3,5,7,}, {2,4,6}, {3,5,}, {4,}, },}, +{ "Se", 79, 80, 79.916519600, 0 , 0, {{0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, {4,}, },}, +{ "Te", 128, 130, 129.906200000, 0 , 0, {{0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,}, },}, +{ "Po", 209, 209, 208.982400000, 0 , 0, {{0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,}, },}, +{ "B", 11, 11, 11.009300000, 0 , 0, {{3,}, {4,}, {3,}, {2,}, {1,}, },}, +*****************************************************************************/ + + + + +int DisconnectMetals( ORIG_ATOM_DATA *orig_inp_data, + int bCheckMetalValence, + INCHI_MODE *bTautFlagsDone ) +{ + int i, j, k, n, iO, num_changes, val, bRadOrMultBonds; + int num_impl_H, num_at, err, num_disconnected; + S_CHAR num_explicit_H[NUM_H_ISOTOPES+1]; + static char elnumber_Heteroat[16] = {'\0', }; + static int num_halogens=0; + int num_halogens2; + + inp_ATOM *at = NULL; + S_CHAR *bMetal = NULL; + inp_ATOM *atom = orig_inp_data->at; + int num_atoms = orig_inp_data->num_inp_atoms; + int nNumExplH = (orig_inp_data->bDisconnectCoord > 0)? orig_inp_data->bDisconnectCoord - 1 : 0; + AT_NUMB *nOldCompNumber = orig_inp_data->nOldCompNumber; + + err = 0; + num_impl_H = 0; + num_at = num_atoms; + num_disconnected = 0; + + if ( !(at = (inp_ATOM *)inchi_calloc( num_at + nNumExplH, sizeof(at[0] ) )) || + !(bMetal = ( S_CHAR *)inchi_calloc( num_at + nNumExplH, sizeof(bMetal[0]) )) ) + { + err = 1; + goto exit_function; + } + + if (!num_halogens) /* if (!elnumber_Heteroat[0] ) */ + { + i = 0; + /* halogens */ + elnumber_Heteroat[i++] = (char)get_periodic_table_number( "F" ); /* 0 */ + elnumber_Heteroat[i++] = (char)get_periodic_table_number( "Cl" ); + elnumber_Heteroat[i++] = (char)get_periodic_table_number( "Br" ); + elnumber_Heteroat[i++] = (char)get_periodic_table_number( "I" ); + elnumber_Heteroat[i++] = (char)get_periodic_table_number( "At" ); /* 4 */ + num_halogens2 = i; + /* other non-metal */ + elnumber_Heteroat[i++] = (char)get_periodic_table_number( "N" ); + elnumber_Heteroat[i++] = (char)get_periodic_table_number( "P" ); + elnumber_Heteroat[i++] = (char)get_periodic_table_number( "As" ); + /*elnumber_Heteroat[i++] = get_periodic_table_number( "Sb" );*/ /* metal 10-28-2003 */ + elnumber_Heteroat[i++] = (char)get_periodic_table_number( "O" ); + elnumber_Heteroat[i++] = (char)get_periodic_table_number( "S" ); + elnumber_Heteroat[i++] = (char)get_periodic_table_number( "Se" ); + elnumber_Heteroat[i++] = (char)get_periodic_table_number( "Te" ); + /*elnumber_Heteroat[i++] = get_periodic_table_number( "Po" );*/ /* metal 10-28-2003 */ + elnumber_Heteroat[i++] = (char)get_periodic_table_number( "B" ); + elnumber_Heteroat[i++] = 0; + num_halogens=num_halogens2; + } + + memcpy( at, atom, num_atoms * sizeof(at[0]) ); + + + /* check each atom, mark metals */ + for ( i = 0, k = 0, num_changes = 0; i < num_atoms; i ++ ) + { + if ( !(val = at[i].valence) && !NUMH(at,i) ) + { + continue; /* disconnected atom */ + } + bRadOrMultBonds = (val == 0) || + (val != at[i].chem_bonds_valence) || /* a bond has higher multiplicity than 1 */ + (at[i].radical && at[i].radical != RADICAL_SINGLET); /* radical */ + + if ( !bRadOrMultBonds && bIsAmmoniumSalt( at, i, &iO, &k, num_explicit_H ) ) + { + ; + } + else if ( !bRadOrMultBonds && bIsMetalSalt( at, i ) ) + { + ; + } + else if ( 1 == (j = bIsMetalToDisconnect(at, i, bCheckMetalValence)) ) + { + num_impl_H += (k = NUMH(at,i)); + bMetal[i] = 1+k; + num_changes ++; + } + else if ( 2 == j && bTautFlagsDone ) + { + *bTautFlagsDone |= TG_FLAG_CHECK_VALENCE_COORD_DONE; + } + } + + if ( num_impl_H != nNumExplH ) + { + err = 2; + goto exit_function; + } + + /* replace implicit H atoms with explicit H atoms */ + for ( i = 0; i < num_atoms && 0 < num_impl_H; i ++ ) + { + if ( bMetal[i] <= 1 ) + { + continue; + } + for ( k = 0; k < NUM_H_ISOTOPES+1; k ++ ) + { + n = k? at[i].num_iso_H[k-1] : at[i].num_H; + for ( j = 0; j < n; j ++ ) + { + if ( num_at >= num_atoms + nNumExplH ) + { + err = 3; + goto exit_function; + } + at[num_at].elname[0] = 'H'; + at[num_at].el_number = get_periodic_table_number(at[num_at].elname); + at[num_at].iso_atw_diff = k; + at[num_at].component = at[i].component; + move_explicit_Hcation(at, num_at+1, i, num_at, 1); + at[num_at].orig_at_number = num_at+1; + num_at ++; + num_impl_H --; + bMetal[i] --; + if ( k ) + { + at[i].num_iso_H[k-1] --; + } + else + { + at[i].num_H --; + } + } + } + + if ( bMetal[i] != 1 ) + { + err = 4; + goto exit_function; + } + } + + if ( num_at != num_atoms + nNumExplH ) + { + err = 5; + goto exit_function; + } + + + /* disconnect metal - ligand bonds */ + for ( i = 0; i < num_atoms; i ++ ) + { + if ( !bMetal[i] ) + { + continue; + } + + /* disconnect metal atom M + + Note: Defect in case of bridging ligands: + + M M M M M M(+) + \ / will be transformed to , not to + N(+) N(+) N(-) + / \ / \ / \ + R R R R R R + + Non-bridging are OK: + + M R M(+) R + \ / / + N(+) ---> N + / \ / \ + R R R R + + */ + + for ( j = at[i].valence-1; 0 <= j; j -- ) + { + if ( j < at[i].valence && !bMetal[ (int)at[i].neighbor[j] ] ) + { + /* do not break metal-metal bond here */ + + num_disconnected+= DisconnectOneLigand( at, + nOldCompNumber, + bMetal, + elnumber_Heteroat, + num_halogens, + num_atoms, + i, + j, + bTautFlagsDone ); + } + } + } + + /* disconnect metal-metal bonds */ + for ( i = 0; i < num_atoms; i ++ ) + { + if ( !bMetal[i] ) + { + continue; + } + for ( j = at[i].valence-1; 0 <= j; j -- ) + { + if ( j < at[i].valence && bMetal[ (int)at[i].neighbor[j] ] ) + { + /* break metal-metal bond here */ + + num_disconnected+= DisconnectOneLigand( at, + nOldCompNumber, + bMetal, + elnumber_Heteroat, + num_halogens, + num_atoms, + i, + j, + bTautFlagsDone ); + } + } + } + +exit_function: + if ( !num_disconnected ) + { + err = 6; + } + if ( at && err ) + { + inchi_free( at ); + at = NULL; + } + if ( atom && at ) + { /* changed if ( at ) to if ( atom && at ) 2004-04-03 */ + inchi_free( atom ); + atom = NULL; + } + if ( bMetal ) + inchi_free( bMetal ); + + if ( at ) + { + orig_inp_data->at = at; + orig_inp_data->num_inp_atoms = num_at; + } + + return err ? -err + : num_disconnected; +} + + +/*****************************************************************************/ +int DisconnectOneLigand( inp_ATOM *at, + AT_NUMB *nOldCompNumber, + S_CHAR *bMetal, + char *elnumber_Heteroat, + int num_halogens, + int num_atoms, + int iMetal, + int jLigand, + INCHI_MODE *bTautFlagsDone ) +{ + int i, j, iLigand, neigh, val; + int metal_neigh_ord[MAXVAL], num_neigh_arom_bonds[MAXVAL]; + int num_metal_neigh, num_disconnections; + int num_del_arom_bonds, num_tot_arom_bonds, new_charge; + char *p; + + iLigand = at[iMetal].neighbor[jLigand]; + num_metal_neigh = 0; + num_disconnections = 0; + num_del_arom_bonds = num_tot_arom_bonds = 0; + + /* find bonds to disconnect */ + for ( i = 0; i < at[iLigand].valence; i ++ ) + { + num_neigh_arom_bonds[i] = 0; + neigh = (int)at[iLigand].neighbor[i]; + if ( neigh < num_atoms && bMetal[ neigh ] ) + { + metal_neigh_ord[ num_metal_neigh ++ ] = i; + if ( at[iLigand].bond_type[i] > BOND_TYPE_TRIPLE ) + { + /* aromatic bond */ + for ( j = 0; j < at[neigh].valence; j ++ ) + { + num_neigh_arom_bonds[i] += ( at[neigh].bond_type[j] > BOND_TYPE_TRIPLE ); + } + num_del_arom_bonds ++; + } + } + num_tot_arom_bonds += (at[iLigand].bond_type[i] > BOND_TYPE_TRIPLE); + } + + /* Disconnect */ + if ( num_del_arom_bonds ) + { + /* fix chem_valence of the ligand and its neighbors in case of disconnecting arom. bonds */ + /* because in this case special care should be taken of updating at[].chem_bonds_valence */ + for ( i = 0; i < num_metal_neigh; i ++ ) + { + j = metal_neigh_ord[i]; + if ( num_neigh_arom_bonds[j] ) + { + neigh = at[iLigand].neighbor[j]; + at[neigh].chem_bonds_valence -= num_neigh_arom_bonds[j]/2 - (num_neigh_arom_bonds[j]-1)/2; + } + } + at[iLigand].chem_bonds_valence -= num_tot_arom_bonds/2 - (num_tot_arom_bonds-num_del_arom_bonds)/2; + } + + /* disconnect in reverse order, otherwise the metal_neigh_ord[i] + becomes invalid after the first disconnection + */ + for ( i = num_metal_neigh-1; 0 <= i; i -- ) + { + num_disconnections+= DisconnectInpAtBond( at, + nOldCompNumber, + iLigand, + metal_neigh_ord[i] ); + } + + /* attempt to change ligand charge to make its valence 'natural' */ + i = num_tot_arom_bonds - num_del_arom_bonds; + if ( i && i != 2 && i != 3 || + at[iLigand].radical && at[iLigand].radical != RADICAL_SINGLET || + !(p = strchr( elnumber_Heteroat, at[iLigand].el_number ) ) ) + { + goto exit_function; /* non-standard atom */ + } + + val = at[iLigand].chem_bonds_valence + NUMH(at, iLigand); + new_charge = MAX_ATOMS; /* impossible value */ + + if ( !val ) + { + if ( p - elnumber_Heteroat < num_halogens ) { + new_charge = -1; + } + } + else + { + for ( i = -1; i <= 1; i ++ ) + { + if ( val == get_el_valence( at[iLigand].el_number, i, 0 ) ) + { + new_charge = i; /* found charge that fits chem. valence */ + break; + } + } + } + + if ( new_charge != MAX_ATOMS ) + { + if ( (new_charge != at[iLigand].charge || + (at[iLigand].radical && at[iLigand].radical != RADICAL_SINGLET)) && + 1 == num_metal_neigh ) + { + if ( 1 == new_charge && 4 == val && 2 == at[iLigand].valence && + 4 == at[iLigand].chem_bonds_valence && + at[iLigand].bond_type[0] == at[iLigand].bond_type[1] ) + { + ; /* do not add +1 charge to disconnected =N=, etc. 2004-10-27 */ + } + else + { + if ( bTautFlagsDone && new_charge != at[iLigand].charge ) + { + *bTautFlagsDone |= TG_FLAG_MOVE_CHARGE_COORD_DONE; + } + at[iMetal].charge -= new_charge - at[iLigand].charge; + at[iLigand].charge = new_charge; + /*at[iLigand].radical = 0;*/ + } + } + } + +exit_function: + return num_disconnections; /* ret;*/ +} + + + + +/*********************************************************************/ +double dist3D( inp_ATOM *at1, inp_ATOM *at2 ) +{ + double dx = at1->x - at2->x; + double dy = at1->y - at2->y; + double dz = at1->z - at2->z; + + return sqrt( dx*dx+dy*dy+dz*dz ); +} + +/*********************************************************************/ +#define MIN_BOND_LENGTH (1.0e-6) +#define MIN_COS (1.0e-6) +#define MIN_BOND_LENGTH2 (MIN_BOND_LENGTH*MIN_BOND_LENGTH) +#define MAX_BOND_LENGTH (1.0e30) +/*********************************************************************/ + +/*********************************************************************/ +double GetMinDistDistribution( inp_ATOM *at, + int num_at, + int iat, + int iat_H, + int bInAllComponents, + double min_dist[], + int num_segm ) +{ +/* const double one_pi = 2.0*atan2(1.0 , 0.0 ); */ + + const double one_pi = 3.14159265358979323846; /* M_PI */ + const double two_pi = 2.0*one_pi; + const double f_step = two_pi / num_segm; + const double h_step = f_step/2.0; + + int i, j, k, kk, ki, kn, n, num_bonds; + double xi, yi, xn, yn, cross_prod_in, dot_prod_in, xni, yni, rni, tni, rmin; + double fi, fk, fn, ft=0, rt=0, rk, ri, rn, c, ave_bond_len; + + for ( i = 0; i < num_segm; i ++ ) + { + min_dist[i] = MAX_BOND_LENGTH; /* more than any distance */ + } + num_bonds = 0; + ave_bond_len = 0.0; + + for ( i = 0; i < num_at; i ++ ) + { + if ( i != iat && i != iat_H && + (bInAllComponents || at[i].component == at[iat].component) ) + { + for ( j = 0; j < at[i].valence; j ++ ) + { + n = at[i].neighbor[j]; + if ( (n > i && n != iat) || n == iat_H ) + continue; + +#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) + if ( n == iat ) + { + int stop = 1; /* */ + } +#endif + + xi = at[i].x - at[iat].x; /* ri; i != iat */ + yi = at[i].y - at[iat].y; + xn = at[n].x - at[iat].x; /* rn; possibly n == iat */ + yn = at[n].y - at[iat].y; + cross_prod_in = xi*yn - xn*yi; /* ((r(i)-r(iat)) x (r(n)-r(iat)) */ + if ( cross_prod_in < -0.01*MIN_BOND_LENGTH2 ) + { + /* make sure the r(i)->r(n) vector is counterclockwise around at[iat] */ + inchi_swap( (char*)&xi, (char*)&xn, sizeof(xi) ); + inchi_swap( (char*)&yi, (char*)&yn, sizeof(yi) ); + cross_prod_in = -cross_prod_in; + } + + xni = xn - xi; /* r(n)->r(i) */ + yni = yn - yi; + rni = xni*xni + yni*yni; + if ( rni > 0.01*MIN_BOND_LENGTH2 ) + { + /* vector length |ri->rn| is not too small */ + /* arrowhead of the vector r(t) = ri + (rn-ri)*t; 0 <= t <= 1 points to the bond ri->rn */ + /* r(tni) is perpendicular to the bond ri->rn so that min|r(t)| = r(tni) = |tni|*rni */ + tni = -(xni*xi + yni*yi)/rni; + /* find min. distance from n-i bond to at[iat] */ + if ( tni < 0.0 ) + { + rmin = sqrt( xi*xi + yi*yi ); + } + else if ( tni > 1.0 ) + { + rmin = sqrt( xn*xn + yn*yn ); + } + else + { + rmin = sqrt(tni*tni*rni); + } + ave_bond_len += sqrt( rni ); + num_bonds ++; + } else + { + /* zero length i-n bond */ + tni = 0.5; /* fake */ + rmin = sqrt( xi*xi + yi*yi ); /* arbitrarily choose one */ + } + + if ( rmin >= 0.1*MIN_BOND_LENGTH ) + { + /* at[iat] does not belong to at[i]-at[n] bond */ + int bCalc_rt = 1; + fi = atan2( yi, xi ); + fn = (n == iat)? fi : atan2( yn, xn ); + if ( fi > fn ) + { + /* make sure fn - fi >= 0 */ + fn += two_pi; + } + if ( fi < 0.0 ) + { + fi += two_pi; + fn += two_pi; + } + ki = (int)floor((fi+h_step)/f_step); /* cast does not match function type */ + kn = (int)floor((fn+h_step)/f_step); + + /* the bond may affect several segments */ + for ( k = ki; k <= kn; k ++ ) + { + kk = k % num_segm; + if ( min_dist[kk] < rmin ) + continue; + if ( bCalc_rt ) + { + if ( n == iat ) + { + ft = fi; + rt = rmin; + } + else + { + double xt, yt; + xt = xi + xni*tni; + yt = yi + yni*tni; + ft = atan2( yt, xt ); + rt = sqrt(xt*xt + yt*yt); + } + bCalc_rt = 0; + } + fk = f_step * kk; + c = fabs(cos( fk - ft )); + if ( c < MIN_COS ) + c = MIN_COS; + rk = rt / c; + if ( min_dist[kk] > rk ) + { + min_dist[kk] = rk; + } + } + } + else + { + /* rmin < 0.1*MIN_BOND_LENGTH */ + ri = xi*xi + yi*yi; + rn = xn*xn + yn*yn; + if ( ri > MIN_BOND_LENGTH2 && rn > MIN_BOND_LENGTH2 ) + { + dot_prod_in = xn*xi + yn*yi; + /* a very short bond */ + if ( dot_prod_in > 0.01*MIN_BOND_LENGTH2 ) + { + /* bond does not cross at[iat] */ + double fyixi = atan2( yi, xi ); + if ( fyixi < 0.0 ) fyixi += two_pi; + kk = (int)floor((fyixi+h_step)/f_step) % num_segm; + if ( min_dist[kk] > rmin ) + { + min_dist[kk] = rmin; + } + } + else if ( dot_prod_in < -0.01*MIN_BOND_LENGTH2 ) + { + /* bond does cross at[iat] */ + double fyixi = atan2( yi, xi ); + if ( fyixi < 0.0 ) fyixi += two_pi; + kk = (int)floor((fyixi+h_step)/f_step) % num_segm; + if ( min_dist[kk] > rmin ) + { + min_dist[kk] = rmin; + } + fyixi += one_pi; + kk = (int)floor((fyixi+h_step)/f_step) % num_segm; + if ( min_dist[kk] > rmin ) + { + min_dist[kk] = rmin; + } + } + else + { + ; /* error, should not happen */ + } + } + else if ( ri <= MIN_BOND_LENGTH2 && rn <= MIN_BOND_LENGTH2 ) + { + /* a very short bond coincides with at[iat]; ignore */ + ; + } + else + { + /* one end of the bond coincides with at[iat] */ + fi = ri>rn? atan2( yi, xi) : atan2( yn, xn ); + if ( fi < 0.0 ) fi += two_pi; + kk = (int)floor((fi+h_step)/f_step) % num_segm; + if ( min_dist[kk] > rmin ) + { + min_dist[kk] = rmin; + } + } + } + } + } + } + + if ( num_bonds ) + { + return ave_bond_len / (double)num_bonds; + } + else + { + return 0.0; + } +} + + + + +/****************************************************************************************/ +int move_explicit_Hcation( inp_ATOM *at, + int num_at, + int iat, + int iat_H, + int bInAllComponents) +{ + +#define NUM_SEGM 20 + + /* const double one_pi = 2.0*atan2(1.0 , 0.0 ); */ + const double one_pi = 3.14159265358979323846; /* M_PI */ + const double two_pi = 2.0*one_pi; + const double f_step = two_pi / NUM_SEGM; + const double h_step = f_step/2.0; + double min_dist[NUM_SEGM]; + int nB, i, k, kk, next, val; + double r, r0, xd, yd, zd, xr, yr, zr, ave_bond_len; + /*double step = 4.0*atan(1.0)/NUM_SEGM;*/ + /* find at[iat] neighbors coordinates */ + + xd=yd=zd=0.0; + + if ( at[iat].valence ) + { + for ( i = 0, nB=0, r = 0.0; i < at[iat].valence; i ++ ) + { + next = at[iat].neighbor[i]; + xd += at[next].x; + yd += at[next].y; + zd += at[next].z; + r += dist3D( at+iat, at+next ); + nB ++; + } + xd /= (double)nB; + yd /= (double)nB; + zd /= (double)nB; + r /= (double)nB; + r0 = sqrt((double)(xd-at[iat].x)*(xd-at[iat].x) + + (double)(yd-at[iat].y)*(yd-at[iat].y)); + } + else + { + if ( at[iat_H].valence ) + { + r = dist3D( at+iat_H, at+ (int)at[iat_H].neighbor[0] ); + } + else + { + r = 0.0; + } + r0 = 0.0; + } + + ave_bond_len = GetMinDistDistribution( at, + num_at, + iat, + iat_H, + bInAllComponents, + min_dist, + NUM_SEGM ); + + if ( r < MIN_BOND_LENGTH && ave_bond_len > MIN_BOND_LENGTH ) + { + r = ave_bond_len; /* ave_bond_len = 0.0 may mean that it is 0D structure */ + } + + if ( r > MIN_BOND_LENGTH ) + { + /* process non-zero bond lengths */ + double f; + if ( 10.0*r0 < r ) + { + xr = -r; /* arbitrary */ + yr = 0.0; + zr = 0.0; + } + else + { + /* + if ( r0 < MIN_BOND_LENGTH ) { + r0 = 1.0; + } + */ + xr = r * ( at[iat].x - xd )/r0; + yr = r * ( at[iat].y - yd )/r0; /* length = r */ + zr = r * ( at[iat].z - zd )/r0; + + /* -- test: opposire direction -- + xr = -r * ( at[iat].x - xd )/r0; + yr = -r * ( at[iat].y - yd )/r0; + zr = -r * ( at[iat].z - zd )/r0; + */ + if ( xr*xr + yr*yr < 0.04*r*r ) + { + xr = -r; + yr = 0.0; + } + } + + r = sqrt( xr*xr + yr*yr ); + f = atan2( yr, xr ); + + if ( f < 0.0 ) + f += two_pi; + + kk = (int)floor((f+h_step)/f_step) % NUM_SEGM; + /* cast does not match function type by design */ + + if ( min_dist[kk] < 1.5* r ) + { + double dist = 1.5*r; + int start=-1, len=0, start_max=-1, len_max=0; + +again: + /* look for longest kk interval with min_dist[kk] >= dist */ + for ( k = 0, start = 0, len = 0, len_max = 0; k < 2*NUM_SEGM; k ++ ) + { + kk = k % NUM_SEGM; + if ( min_dist[kk] >= dist ) + { + if ( !len ++) + { + start = k; + } + } + else + { + if ( len > len_max ) + { + len_max = len; + start_max = start; + } + len = 0; + } + } + if ( !len_max ) + { + if ( dist > 0.1*r ) + { + dist *= 0.75; + goto again; + } + else + { + goto done; /* do it anyway */ + } + } + else + { + /* found a good sector */ + f = f_step * (start_max + (double)(len_max - 1)/2.0); + r0 = dist / 1.5; + xr = r0 * cos(f); + yr = r0 * sin(f); + zr = zr/r*r0; + } + } + } + else + { + xr = yr = zr = 0; + } + + +done: + + if ( at[iat_H].valence ) + { + /* disconnect H */ + next = at[iat_H].neighbor[0]; + + for ( i = 0; i < at[next].valence; i ++ ) + { + if ( at[next].neighbor[i] == iat_H ) + { + RemoveInpAtBond( at, next, i ); + + i = 0; /* success */ + break; + } + } + } + else + { + /* isolated H+ cation */ + next = iat_H; + i = 0; + at[iat_H].valence = 1; + at[iat_H].chem_bonds_valence = 1; + at[iat_H].bond_type[0] = BOND_TYPE_SINGLE; + } + + if ( 0 == i /*i < at[next].valence*/ ) + { + /* move charge */ + if ( at[next].charge > 0 && at[iat].charge < 0 ) + { + at[next].charge --; + at[iat].charge ++; + } + + /* connect H to at[iat] */ + val = at[iat].valence; + at[iat].neighbor[val] = iat_H; + at[iat].bond_type[val] = at[iat_H].bond_type[0]; + at[iat].bond_stereo[val] = 0; + at[iat].chem_bonds_valence += at[iat_H].bond_type[0]; + at[iat].valence = val+1; + + at[iat_H].component = at[iat].component; + at[iat_H].neighbor[0] = iat; + at[iat_H].bond_stereo[0] = 0; /* possible loss of stereo info */ + at[iat_H].x = at[iat].x + xr; + at[iat_H].y = at[iat].y + yr; + at[iat_H].z = at[iat].z + zr; + + return 1; /* success */ + } + + + return 0; /* failed */ +} + + +/************************************************************************/ +int add_DT_to_num_H( int num_atoms, inp_ATOM *at ) +/* assume num_1H, num_D and num_T are not included in num_H */ +{ + int i, j; + for ( i = 0; i < num_atoms; i ++ ) { + for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) + at[i].num_H += at[i].num_iso_H[j]; + } + return 0; +} + + +/********************************************************************************/ +/* Return value: new number of atoms > 0 or -1=out of RAM */ +int remove_terminal_HDT( int num_atoms, inp_ATOM *at, int bFixTermHChrg ) +{ + AT_NUMB *new_ord; + inp_ATOM *new_at; + char *p; + static const char szHDT[]="HDT"; + static const int kMax = sizeof(szHDT); /* = 4 */ + int ret = -1; + int num_hydrogens=0, num_H = 0; /* number of terminal H, D, T */ + int i, j, k, n, m; + int val; + AT_RANK new_HydrogenAt_order[NUM_H_ISOTOPES+1]; + AT_RANK new_OtherNeigh_order[MAXVAL]; + S_CHAR old_trans[MAX_NUM_STEREO_BONDS]; + + int num_OtherNeigh, num_HydrogenAt; + + new_ord=(AT_NUMB *)inchi_calloc(num_atoms, sizeof(new_ord[0])); /* changed malloc to calloc 9-11-2003 */ + new_at =(inp_ATOM *) inchi_malloc(sizeof(new_at[0]) *num_atoms); + if (!new_ord || !new_at) + goto exit_function; + + /* move H. D, T to the end of the list of atoms */ + for ( i = 0; i < num_atoms; i ++ ) + { + at[i].component = i; /* temporarily save original numbering */ + /* get k = temp. hydrogen isotope/non-hydrogen atom type: */ + /* k=0:H, k=2:D, k=3:T, k=4=kMax: not a hydrogen */ + k = at[i].elname[1]? kMax : (p=(char*)strchr(szHDT, at[i].elname[0]))? (int) (p-szHDT) : kMax; + /* set hydrogen isotope atw differences */ + /* Notes: k-value of isotopic H is incremented to correct iso_atw_diff value later. */ + /* 1H isotope cannot be detected here. */ + if ( k == ATW_H || k == ATW_H+1 ) + { + /* D or T, k = 1 or 2 */ + at[i].elname[0] = 'H'; /* hydrogen isotope */ + at[i].iso_atw_diff = ++k; /* increment k to make k = iso_atw_diff ( 2 for D, 3 for T ) */ + } + num_H += (k != kMax && at[i].valence == 1 && at[i].chem_bonds_valence == 1 && !NUMH(at,i) ); + } + + /* special case: HD, HT, DT, HH: the only non-isotopic H or + * the lightest isotopic H out of two is removed + * to become implicit (make the heavier H the "central atom"). + * Note: This must be consistent with MOL_FMT_to_atom() + * treatment of isotopic Hn aliases. + */ + if ( 2 == num_H && 2 == num_atoms && !NUMH(at,0) && !NUMH(at,1) ) + { + + if ( at[0].iso_atw_diff >= at[1].iso_atw_diff ) { + new_ord[0] = 0; + new_ord[1] = 1; + } else { + new_ord[0] = 1; + new_ord[1] = 0; + } + if ( at[new_ord[1]].charge ) { + at[new_ord[0]].charge += at[new_ord[1]].charge; + at[new_ord[1]].charge = 0; + } + new_at[new_ord[0]] = at[0]; + new_at[new_ord[1]] = at[1]; + num_hydrogens = 1; + } + else + { + /* general case except H-H */ + for ( i = 0; i < num_atoms; i ++ ) + { + k = (at[i].elname[1] || NUMH(at,i))? kMax : (at[i].elname[0]=='H')? at[i].iso_atw_diff : kMax; + if ( k < kMax && at[i].valence == 1 && at[i].chem_bonds_valence == 1 && + /* the order of comparison is important */ + ((n=(int)at[i].neighbor[0]) > i /* at[n] has not been encountered yet*/ || + (int)new_ord[n] < num_atoms - num_hydrogens) /* at[n] might have been encountered; it has not been moved */ ) + { + /* found an explicit terminal hydrogen */ + num_hydrogens ++; + if ( k==0 && ATW_H <= at[i].iso_atw_diff && at[i].iso_atw_diff < ATW_H+NUM_H_ISOTOPES ) + { + k = at[i].iso_atw_diff; /* H isotope has already been marked above or elsewhere */ + } + if ( at[i].charge ) + { + /* transfer charge from the hydrogen */ + at[n].charge += at[i].charge; + at[i].charge = 0; + if (bFixTermHChrg) + { + /* Fixed bug (July 6, 2008 IPl) : + if terminal H was charged (not neutralized before call of remove_terminal_HDT) + and had an ordering number > than that of heavy-atom neighbour, then + charge on neighbour atom was not adjusted (though charge on H was removed). */ + if ( i > n ) + /* new_at[new_ord[n]] has been created and filled already */ + new_at[new_ord[n]].charge = at[n].charge; + } + } + new_ord[i] = num_atoms - num_hydrogens; /* move hydrogens to the end of the list */ + } + else + { + /* atom is not an explicit terminal hydrogen */ + new_ord[i] = i - num_hydrogens; /* adjust non-hydrogens positions */ + } + + /* copy atom to the new position */ + new_at[new_ord[i]] = at[i]; + } /* i */ + } /* general case except H-H */ + + if ( num_hydrogens ) { + int num_others = num_atoms-num_hydrogens; /* atoms which are not terminal H, D, T */ + if ( num_hydrogens > 1 ) { + /* sort hydrogen isotopes in ascending order, */ + /* orig, numbers being the secondary sorting key */ + qsort( new_at+num_others, num_hydrogens, sizeof(new_at[0]), cmp_iso_atw_diff_component_no ); + } + /* save new numbering of hydrogen atoms using temporarily saved orig numbering */ + for ( i = num_others; i < num_atoms; i ++ ) { + new_ord[(int)new_at[i].component] = i; + } + + /* renumber neighbors according to new_ord[] and detach terminal hydrogens */ + for ( i = 0; i < num_others; i++ ) { + memset( new_HydrogenAt_order, 0, sizeof(new_HydrogenAt_order) ); + memset( new_OtherNeigh_order, 0, sizeof(new_OtherNeigh_order) ); + num_OtherNeigh = 0; + num_HydrogenAt = 0; + num_H = 0; + + for ( m = 0; m < MAX_NUM_STEREO_BONDS && new_at[i].sb_parity[m]; m ++ ) { + old_trans[m] = 2 - (new_at[i].sn_ord[m] + new_at[i].sb_ord[m] + (new_at[i].sn_ord[m] > new_at[i].sb_ord[m]))%2; + } + + for ( k = j = val = 0; k < new_at[i].valence; k++ ) { + if ( num_others <= ( n = new_ord[new_at[i].neighbor[k]] ) ) { + /* discovered neighbor = disconnected explicit hydrogen + * i = new atom new_at[i] ordering number + * n = new number of the explicit H + * k = ordering number of the explicit H in new_at[i] adjacency list + */ + if ( 0 < new_at[n].iso_atw_diff && new_at[n].iso_atw_diff < ATW_H+NUM_H_ISOTOPES ) { + /* make explicit isotopic H implicit */ + new_at[i].num_iso_H[new_at[n].iso_atw_diff-1] ++; /* isotopic H */ + num_HydrogenAt += !new_HydrogenAt_order[new_at[n].iso_atw_diff]; + new_HydrogenAt_order[new_at[n].iso_atw_diff] = k+1; + } else { + /* make explicit non-isotopic H implicit */ + new_at[i].num_H ++; /* non-isotopic H */ + num_HydrogenAt += !num_H; + num_H ++; + new_HydrogenAt_order[0] = k+1; + } + /* decrement chem. bonds valence because one bond is removed */ + new_at[i].chem_bonds_valence = inchi_max( 0, new_at[i].chem_bonds_valence-1 ); + new_at[n].neighbor[0] = i; /* update removed hydrogen neighbor number */ + if ( new_at[i].sb_parity[0] ) { + /* if the removed H is an SB neighbor then mark it as removed */ + for ( m = 0; m < MAX_NUM_STEREO_BONDS && new_at[i].sb_parity[m]; m ++ ) { + if ( k == (int)new_at[i].sn_ord[m] ) { + new_at[i].sn_ord[m] = -(new_at[n].iso_atw_diff+1); + /* means the SB neighbor has been removed; (-4)=H, (-3)=1H, (-2)=D, (-1)=T */ + } + } + } + } else { + /* discovered a regular (not an explicit H) neighbor */ + if ( new_at[i].sb_parity[0] ) { + if ( num_OtherNeigh < MAX_NUM_STEREO_BONDS ) { + new_OtherNeigh_order[num_OtherNeigh] = k+1; + } + num_OtherNeigh ++; /* increment outside of if() to detect overflow */ + if ( val != k ) { + /* store new stereobond and sb-neighbor ordering numbers */ + for ( m = 0; m < MAX_NUM_STEREO_BONDS && new_at[i].sb_parity[m]; m ++ ) { + if ( k == (int)new_at[i].sb_ord[m] ) + new_at[i].sb_ord[m] = val; + else + if ( k == (int)new_at[i].sn_ord[m] ) + new_at[i].sn_ord[m] = val; + } + } + } + new_at[i].neighbor[val] = new_ord[new_at[i].neighbor[k]]; + new_at[i].bond_type[val] = new_at[i].bond_type[k]; + new_at[i].bond_stereo[val] = new_at[i].bond_stereo[k]; + val ++; + } + } + if ( new_at[i].valence > val && new_at[i].sb_parity[0] ) { + if ( num_HydrogenAt == new_at[i].valence - val && num_HydrogenAt + num_OtherNeigh <= MAXVAL ) { + /* recalculate parity so that it would describe neighbor sequence H,1H,D,T,neigh[0],neigh[1]... */ + memmove( new_OtherNeigh_order + num_HydrogenAt, new_OtherNeigh_order, num_OtherNeigh*sizeof(new_OtherNeigh_order[0])); + for ( k = 0, j = 1; k <= NUM_H_ISOTOPES; k ++ ) { + if ( new_HydrogenAt_order[k] ) { + new_OtherNeigh_order[num_HydrogenAt - j] = new_HydrogenAt_order[k]; + for ( m = 0; m < MAX_NUM_STEREO_BONDS && new_at[i].sb_parity[m]; m ++ ) { + if ( (int)new_at[i].sn_ord[m] == -(k+1) ) { + new_at[i].sn_ord[m] = -j; + /* negative means explicit H isotope ord are + (contiguously) in front of the adjacency list */ + } + } + j ++; + } + } + /* at this point new_OtherNeigh_order[] contains + incremented old ordering numbers in new order */ + k = insertions_sort_AT_RANK( new_OtherNeigh_order, num_HydrogenAt + num_OtherNeigh ); + k = k%2; /* seems to be of no use */ + /*if ( k ) {*/ + /* + for ( m = 0; m < MAX_NUM_STEREO_BONDS && new_at[i].sb_parity[m]; m ++ ) { + if ( PARITY_WELL_DEF(new_at[i].sb_parity[m]) ) { + if ( old_trans[m] != 2 - (4 + new_at[i].sn_ord[m] + new_at[i].sb_ord[m] + (new_at[i].sn_ord[m] > new_at[i].sb_ord[m]))%2 ) { + new_at[i].sb_parity[m] = 3 - new_at[i].sb_parity[m]; + } + } + } + */ + /*}*/ + } + } + new_at[i].valence = val; + } + memcpy( at, new_at, sizeof(at[0])*num_atoms ); + ret = num_others; + } else { + ret = num_atoms; + } +exit_function: + if ( new_ord ) + inchi_free ( new_ord ); + if ( new_at ) + inchi_free ( new_at ); + return ret; +} + + + + +/*********************************************************************/ +int get_iat_number( int el_number, const int el_num[], int el_num_len ) +{ + int i; + for ( i = 0; i < el_num_len; i ++ ) + { + if ( el_num[i] == el_number ) + return i; + } + + return -1; +} + + +/*#endif*/ /* } DISCONNECT_SALTS */ + +typedef enum tagIonAtomType +{ + IAT_H=0, + IAT_C, + IAT_N, + IAT_P, + IAT_O, + IAT_S, + IAT_Se, + IAT_Te, + IAT_F, + IAT_Cl, + IAT_Br, + IAT_I, + IAT_MAX +} ION_ATOM_TYPE; + + + +#if ( READ_INCHI_STRING == 1 ) + +/****************************************************************************************/ +int bHeteroAtomMayHaveXchgIsoH( inp_ATOM *atom, int iat ) +{ + inp_ATOM *at = atom + iat, *at2; + static int el_num[IAT_MAX]; + int j, val, is_O=0, is_Cl=0, is_N=0, is_H=0, num_H, iat_numb, bAccept, cur_num_iso_H; + + if ( !el_num[IAT_H]) + { + el_num[IAT_H ] = get_periodic_table_number( "H" ); + el_num[IAT_C ] = get_periodic_table_number( "C" ); + el_num[IAT_N ] = get_periodic_table_number( "N" ); + el_num[IAT_P ] = get_periodic_table_number( "P" ); + el_num[IAT_O ] = get_periodic_table_number( "O" ); + el_num[IAT_S ] = get_periodic_table_number( "S" ); + el_num[IAT_Se] = get_periodic_table_number( "Se"); + el_num[IAT_Te] = get_periodic_table_number( "Te"); + el_num[IAT_F ] = get_periodic_table_number( "F" ); + el_num[IAT_Cl] = get_periodic_table_number( "Cl"); + el_num[IAT_Br] = get_periodic_table_number( "Br"); + el_num[IAT_I ] = get_periodic_table_number( "I" ); + } + + if ( 0 > (iat_numb = get_iat_number( at->el_number, el_num, IAT_MAX )) ) + { + return 0; + } + + if ( abs(at->charge) > 1 || at->radical && RADICAL_SINGLET != at->radical ) + { + return 0; + } + + val = -1; + switch( iat_numb ) + { + case IAT_N: + case IAT_P: + is_N = 1; + val = 3+at->charge; + break; + + case IAT_O: + case IAT_S: + case IAT_Se: + case IAT_Te: + is_O = 1; + val = 2+at->charge; + break; + + case IAT_F: + case IAT_Cl: + case IAT_Br: + case IAT_I: + if ( at->charge == 0 ) + { + is_Cl = 1; /* isolated HCl */ + val = 1; + } + break; + + case IAT_H: + if ( at->valence == 0 && + at->charge == 1 ) + { + is_H = 1; /* isolated proton */ + val = 0; + } + } + if ( val < 0 ) + { + return 0; + } + num_H = NUMH(at,0); + if ( val != at->chem_bonds_valence + num_H ) + { + return 0; + } + if ( is_H ) + { + return 2; /* H atom */ + } + else + { + cur_num_iso_H = 0; + for ( j = 0, bAccept = 1; j < at->valence && bAccept; j ++ ) + { + at2 = atom + (int)at->neighbor[j]; + if ( at2->charge && at->charge || + (at2->radical && RADICAL_SINGLET != at2->radical ) ) + { + return 0; /* adjacent charged/radical atoms: do not neutralizate */ + } + } + } + + return 1; +} + + + +#endif + + + +/************************************************************/ +int bNumHeterAtomHasIsotopicH( inp_ATOM *atom, int num_atoms ) +{ + static int el_num[IAT_MAX]; + int i, j, val, is_O=0, is_Cl=0, is_N=0, is_H=0, num_H, iat_numb, bAccept, num_iso_H, cur_num_iso_H, num_iso_atoms; + inp_ATOM *at, *at2; + + /* one time initialization */ + if ( !el_num[IAT_H]) + { + el_num[IAT_H ] = get_periodic_table_number( "H" ); + el_num[IAT_C ] = get_periodic_table_number( "C" ); + el_num[IAT_N ] = get_periodic_table_number( "N" ); + el_num[IAT_P ] = get_periodic_table_number( "P" ); + el_num[IAT_O ] = get_periodic_table_number( "O" ); + el_num[IAT_S ] = get_periodic_table_number( "S" ); + el_num[IAT_Se] = get_periodic_table_number( "Se"); + el_num[IAT_Te] = get_periodic_table_number( "Te"); + el_num[IAT_F ] = get_periodic_table_number( "F" ); + el_num[IAT_Cl] = get_periodic_table_number( "Cl"); + el_num[IAT_Br] = get_periodic_table_number( "Br"); + el_num[IAT_I ] = get_periodic_table_number( "I" ); + } + + num_iso_H = 0; + num_iso_atoms = 0; + + for ( i = 0, at = atom; i < num_atoms; i ++, at ++ ) + { + + num_iso_atoms += ( at->iso_atw_diff != 0 || NUM_ISO_H(at,0) ); + /* isotopic atoms and implicit isotopic H */ + + if ( 0 > (iat_numb = get_iat_number( at->el_number, el_num, IAT_MAX )) ) + { + continue; + } + + if ( abs(at->charge) > 1 || at->radical && RADICAL_SINGLET != at->radical ) + { + continue; + } + + val = -1; + switch( iat_numb ) + { + case IAT_N: + case IAT_P: + is_N = 1; + val = 3+at->charge; + break; + + case IAT_O: + case IAT_S: + case IAT_Se: + case IAT_Te: + is_O = 1; + val = 2+at->charge; + break; + + case IAT_F: + case IAT_Cl: + case IAT_Br: + case IAT_I: + if ( at->charge == 0 ) + { + is_Cl = 1; /* isolated HCl */ + val = 1; + } + break; + + case IAT_H: + if ( at->valence == 0 && + at->charge == 1 ) + { + is_H = 1; /* isolated proton */ + val = 0; + } + } + if ( val < 0 ) + { + continue; + } + + num_H = NUMH(at,0); + if ( val != at->chem_bonds_valence + num_H ) + { + continue; + } + + if ( is_H ) + { + bAccept = 1; + cur_num_iso_H = (at->iso_atw_diff != 0); + } + else + { + cur_num_iso_H = 0; + for ( j = 0, bAccept = 1; j < at->valence && bAccept; j ++ ) + { + at2 = atom + (int)at->neighbor[j]; + if ( at2->charge && at->charge || + (at2->radical && RADICAL_SINGLET != at2->radical ) ) + { + bAccept = 0; /* adjacent charged/radical atoms: do not neutralizate */ + break; + } + else if ( at2->el_number == el_num[IAT_H ] && + at2->valence == 1 && at2->iso_atw_diff ) + { + cur_num_iso_H ++; /* isotopic explicit H */ + } + } + + if ( bAccept ) + { + num_iso_atoms -= cur_num_iso_H; /* avoid counting explicit H as isotopic atom */ + cur_num_iso_H += NUM_ISO_H(at,0); + } + } + + num_iso_H += (bAccept && cur_num_iso_H); /* number of acceptable heteroatoms that have isotopic H */ + } + + return + ((num_iso_H ? 1:0) | (num_iso_atoms? 2:0)); +} + + +/****************************************************/ +/* Mark and count disconnected structure components */ +/* by Depth-first searching each component */ +/****************************************************/ +int cmp_components( const void *a1, const void *a2 ) +{ + int ret; + AT_NUMB n1; + AT_NUMB n2; + + n1 = ((const AT_NUMB *)a1)[0]; + /* number of atoms in the component -- descending order */ + n2 = ((const AT_NUMB *)a2)[0]; + + if ( ret = (int)n2 - (int)n1 ) + { + return ret; + } + + /* stable sort */ + n1 = ((const AT_NUMB *)a1)[1]; + /* component ordering number -- ascending order */ + n2 = ((const AT_NUMB *)a2)[1]; + + ret = (int)n1 - (int)n2; + + return ret; +} + + +/**********************************************************************/ +int MarkDisconnectedComponents( ORIG_ATOM_DATA *orig_at_data, + int bProcessOldCompNumbers ) +{ + typedef AT_NUMB AT_TRIPLE[3]; + + inp_ATOM *at = orig_at_data->at; + int num_at = orig_at_data->num_inp_atoms; + AT_NUMB *nCurAtLen = NULL; + + AT_NUMB *nNewCompNumber = NULL; + AT_NUMB *nPrevAtom = NULL; + S_CHAR *iNeigh = NULL; + + AT_NUMB *nOldCompNumber = NULL; + int i, j, num_components, ret; + int new_comp_no; + AT_NUMB old_comp_no, another_comp_no, no_component; + + /* component_nbr[i][0] = number of atoms in the component i-1 + * component_nbr[i][1] = original component number (id-1) = i + * after sorting: + * component_nbr[j][2] = new number of component #(component_nbr[i][1]+1) + */ + AT_TRIPLE *component_nbr = NULL; + + /* initialize */ + if ( bProcessOldCompNumbers && !orig_at_data->nOldCompNumber ) + { + bProcessOldCompNumbers = 0; + } + num_components = 0; + + /* + for ( j = 0; j < num_at; j ++ ) + { + at[j].component = 0; + } + */ + + ret = -1; + if ( !num_at ) + { + return 0; + } + + if ( !( nNewCompNumber = (AT_NUMB *) + inchi_calloc( num_at, sizeof(nNewCompNumber[0]) ) ) || + /* for non-recursive DFS only: */ + !( nPrevAtom = (AT_NUMB *) + inchi_calloc( num_at, sizeof(nPrevAtom[0]) ) ) || + !( iNeigh = (S_CHAR *) + inchi_calloc( num_at, sizeof(iNeigh[0]) ) )) + { + goto exit_function; + } + + /* mark and count; avoid deep DFS recursion: it may make verifying software unhappy */ + /* nNewCompNumber[i] will contain new component number for atoms at[i], i=0..num_at-1 */ + for ( j = 0; j < num_at; j++ ) + { + if ( !nNewCompNumber[j] ) + { + /* mark starting with at[j] */ + int fst_at, nxt_at, cur_at = j; + num_components ++; + + /* first time at at[j] */ + nNewCompNumber[fst_at = cur_at] = (AT_NUMB) num_components; + + /* find next neighbor */ + while ( 1 ) + { + + if ( iNeigh[cur_at] < at[cur_at].valence ) + { + nxt_at = at[cur_at].neighbor[(int)iNeigh[cur_at] ++]; + + if ( !nNewCompNumber[nxt_at] ) + { + /* forward edge: found new atom */ + nNewCompNumber[nxt_at] = (AT_NUMB) num_components; + nPrevAtom[nxt_at] = (AT_NUMB) cur_at; + cur_at = nxt_at; + } + } + else if ( cur_at == fst_at ) + { + break; /* done */ + } + else + { + cur_at = nPrevAtom[cur_at]; /* retract */ + } + } + } + } + + inchi_free( nPrevAtom ); nPrevAtom = NULL; + inchi_free( iNeigh ); iNeigh = NULL; + + /* Allocate more memory */ + i = inchi_max( num_components, orig_at_data->num_components ); + + if ( !(nCurAtLen = (AT_NUMB *) + inchi_calloc( num_components+1, sizeof(nCurAtLen[0]) ) ) || + !(nOldCompNumber = (AT_NUMB *) + inchi_calloc( i +1, sizeof(nOldCompNumber[0]) ) ) || + !(component_nbr = (AT_TRIPLE *) + inchi_calloc( num_components+1, sizeof(component_nbr[0]) ) ) ) + { + goto exit_function; + } + + /* count atoms per component and renumber the components */ + for ( i = 0; i < num_components; i ++ ) + { + component_nbr[i][0] = 0; /* number of atoms in the component */ + component_nbr[i][1] = i; /* component ordering number */ + } + + for ( j = 0; j < num_at; j ++ ) + { + component_nbr[(int)nNewCompNumber[j]-1][0] ++; /* count atoms in each component */ + } + + /* sort key: number of atoms; order: descending */ + + qsort( (void*)component_nbr[0], + num_components, + sizeof(component_nbr[0]), + cmp_components); + + /* invert the transposition */ + for ( i = 0; i < num_components; i ++ ) + { + nCurAtLen[i] = component_nbr[i][0]; + component_nbr[ component_nbr[i][1] ][2] = i+1; + } + + /* renumber the components so that the component with the greatest number of atoms is the first */ + no_component = num_at+1; + + for ( j = 0; j < num_at; j ++ ) + { + /* new component number for at[j] */ + new_comp_no = component_nbr[(int)nNewCompNumber[j]-1][2]-1; /* starts from 0 */ + if ( bProcessOldCompNumbers ) + { + /* old component number for at[j] */ + old_comp_no = at[j].component; + /* fill out nOldCompNumber[]; initially it contains zeroes */ + if ( !old_comp_no ) + { + nOldCompNumber[new_comp_no] = no_component; /* atom did not have component number */ + } + else if ( nOldCompNumber[new_comp_no] != old_comp_no ) + { + if ( !nOldCompNumber[new_comp_no] ) + { + nOldCompNumber[new_comp_no] = old_comp_no; + } + else + { + /* at[j] moved from old comp #old_comp_no to old comp #nOldCompNumber[new_comp_no] + Both components cannot be equal to any current component */ + another_comp_no = nOldCompNumber[new_comp_no]; + for ( i = 0; i < num_components; i ++ ) + { + if ( nOldCompNumber[i] == old_comp_no || + nOldCompNumber[i] == another_comp_no ) + { + nOldCompNumber[i] = no_component; + } + } + /* nOldCompNumber[new_comp_no] = num_at+1; */ + } + } + } + /* orig_at_data->nOldCompNumber */ + at[j].component = new_comp_no+1; /* starts from 1 */ + } + + if ( bProcessOldCompNumbers ) + { + for ( j = 0; j < num_components; j ++ ) + { + if ( nOldCompNumber[j] == no_component ) + { + /* the component has atom from another component */ + nOldCompNumber[j] = 0; + } + else if ( nOldCompNumber[j] && + !orig_at_data->nOldCompNumber[nOldCompNumber[j]-1] ) + { + /* the component has changed in the previous processing */ + nOldCompNumber[j] = 0; + } + } + } + else + { + for ( j = 0; j < num_components; j ++ ) + { + nOldCompNumber[j] = j + 1; + } + } + + ret = num_components; + + +exit_function: + if ( nNewCompNumber ) + inchi_free( nNewCompNumber ); + if ( component_nbr ) + inchi_free( component_nbr ); + + if ( ret < 0 ) + { + if ( nPrevAtom ) + { + inchi_free( nPrevAtom ); + nPrevAtom = NULL; + } + if ( iNeigh ) + { + inchi_free( iNeigh ); + iNeigh = NULL; + } + if ( nCurAtLen ) + { + inchi_free( nCurAtLen ); + nCurAtLen = NULL; + } + if ( nOldCompNumber ) + { + inchi_free( nOldCompNumber ); + nOldCompNumber = NULL; + } + num_components = ret; + } + + /* avoid memory leaks */ + if ( orig_at_data->nCurAtLen ) + inchi_free ( orig_at_data->nCurAtLen ); + if ( orig_at_data->nOldCompNumber ) + inchi_free ( orig_at_data->nOldCompNumber ); + + orig_at_data->nCurAtLen = nCurAtLen; + orig_at_data->nOldCompNumber = nOldCompNumber; + + orig_at_data->num_components = num_components; + + return + ret; /* number of disconnected components; + 1=>single connected structure */ +} + + +/*************************************/ +/* Extract one (connected) component */ +/************************************/ +int ExtractConnectedComponent( inp_ATOM *at, + int num_at, + int component_number, + inp_ATOM *component_at ) +{ + int i, j, num_component_at; + AT_NUMB *number; + + if ( NULL == (number = (AT_NUMB*)inchi_calloc(num_at, sizeof(AT_NUMB)))) + { + return CT_OUT_OF_RAM; /* out of memory */ /* */ + } + + /* copy atoms */ + for ( i = 0, num_component_at = 0; i < num_at; i ++ ) + { + if ( at[i].component == component_number ) + { + number[i] = num_component_at; + component_at[num_component_at ++] = at[i]; + } + } + + /* renumber neighbors */ + for ( i = 0; i < num_component_at; i ++ ) + { + component_at[i].orig_compt_at_numb = (AT_NUMB)(i + 1); + for ( j = 0; j < component_at[i].valence; j ++ ) + { + component_at[i].neighbor[j] = number[(int)component_at[i].neighbor[j]]; + } + } + + inchi_free( number ); + + return num_component_at; +} + + +/****************************************************************/ +int SetConnectedComponentNumber( inp_ATOM *at, int num_at, int component_number ) +{ + int i; + for ( i = 0; i < num_at; i ++ ) + { + at[i].component = (AT_NUMB)component_number; + } + + return 0; +} + + +/****************************************************************/ +int Free_INChI_Stereo( INChI_Stereo *pINChI_Stereo ) +{ + if ( pINChI_Stereo ) + { + qzfree( pINChI_Stereo->nNumber ); + qzfree( pINChI_Stereo->t_parity ); + qzfree( pINChI_Stereo->nNumberInv ); + qzfree( pINChI_Stereo->t_parityInv ); + qzfree( pINChI_Stereo->nBondAtom1 ); + qzfree( pINChI_Stereo->nBondAtom2 ); + qzfree( pINChI_Stereo->b_parity ); + } + + return 0; +} + + +/****************************************************************/ +INChI_Stereo *Alloc_INChI_Stereo(int num_at, int num_bonds) +{ + + INChI_Stereo *pINChI_Stereo = (INChI_Stereo *) + inchi_calloc(1, sizeof(INChI_Stereo)); + + if ( pINChI_Stereo ) + { + if ( num_at && + (pINChI_Stereo->nNumber = (AT_NUMB *)inchi_calloc(num_at, sizeof(pINChI_Stereo->nNumber[0]))) && + (pINChI_Stereo->t_parity = (S_CHAR *)inchi_calloc(num_at, sizeof(pINChI_Stereo->t_parity[0]))) && + (pINChI_Stereo->nNumberInv = (AT_NUMB *)inchi_calloc(num_at, sizeof(pINChI_Stereo->nNumberInv[0]))) && + (pINChI_Stereo->t_parityInv = (S_CHAR *)inchi_calloc(num_at, sizeof(pINChI_Stereo->t_parityInv[0]))) ) + { + ; + } + else if ( num_at ) + { + goto out_of_RAM; + } + + if ( num_bonds && + (pINChI_Stereo->nBondAtom1 =(AT_NUMB *)inchi_calloc(num_bonds, sizeof(pINChI_Stereo->nBondAtom1[0]))) && + (pINChI_Stereo->nBondAtom2 =(AT_NUMB *)inchi_calloc(num_bonds, sizeof(pINChI_Stereo->nBondAtom2[0]))) && + (pINChI_Stereo->b_parity =(S_CHAR *)inchi_calloc(num_bonds, sizeof(pINChI_Stereo->b_parity[0]))) ) + { + ; + } + else if ( num_bonds ) + { + goto out_of_RAM; + } + + return pINChI_Stereo; + +out_of_RAM: + Free_INChI_Stereo( pINChI_Stereo ); + qzfree( pINChI_Stereo ); + } /* if ( pINChI_Stereo ) */ + + return NULL; +} + + +/****************************************************************/ +int Free_INChI(INChI **ppINChI) +{ + + INChI *pINChI; + + if ( pINChI = *ppINChI ) + { + +#if ( bREUSE_INCHI == 1 ) + if ( pINChI->nRefCount -- > 0 ) + return 1; +#endif + + Free_INChI_Members(pINChI); + qzfree( pINChI ); + *ppINChI = NULL; + } + + return 0; +} + + +/****************************************************************/ +int Free_INChI_Members(INChI *pINChI) +{ + if ( pINChI ) + { + Free_INChI_Stereo(pINChI->Stereo ); + Free_INChI_Stereo(pINChI->StereoIsotopic ); + qzfree(pINChI->nAtom ); + qzfree(pINChI->nConnTable ); + qzfree(pINChI->nTautomer ); + qzfree(pINChI->nNum_H ); + qzfree(pINChI->nNum_H_fixed ); + qzfree(pINChI->IsotopicAtom ); + qzfree(pINChI->IsotopicTGroup ); + qzfree(pINChI->nPossibleLocationsOfIsotopicH); + qzfree(pINChI->Stereo ); + qzfree(pINChI->StereoIsotopic ); + qzfree(pINChI->szHillFormula ); + } + + return 0; +} + + +/****************************************************************/ +INChI *Alloc_INChI( inp_ATOM *at, + int num_at, + int *found_num_bonds, + int *found_num_isotopic, + int nAllocMode ) +{ + int i, num_bonds, num_isotopic_atoms; + INChI *pINChI; + int bIsotopic = (nAllocMode & REQ_MODE_ISO); + /* int bTautomeric = (nAllocMode & REQ_MODE_TAUT); */ + + if ( num_at <= 0 || + NULL == (pINChI = (INChI *)inchi_calloc( 1, sizeof(INChI))) ) + { + return NULL; + } + + for ( i = 0, num_bonds = 0, num_isotopic_atoms = 0; i < num_at; i ++ ) + { + num_bonds += at[i].valence; + /* if ( bIsotopic ) { */ + num_isotopic_atoms += (0 != at[i].iso_atw_diff || + !strcmp(at[i].elname, "D") || + !strcmp(at[i].elname, "T") || + at[i].num_iso_H[0] || + at[i].num_iso_H[1] || + at[i].num_iso_H[2]); + /* } */ + } + num_bonds /= 2; + + *found_num_bonds = num_bonds; + *found_num_isotopic = num_isotopic_atoms; + + if ( (pINChI->nAtom = (U_CHAR*) inchi_calloc( num_at, sizeof(pINChI->nAtom[0]))) && + (pINChI->nConnTable = (AT_NUMB*)inchi_calloc( num_at+num_bonds, sizeof(pINChI->nConnTable[0]))) && + (pINChI->nTautomer = (AT_NUMB*)inchi_calloc( ((3+INCHI_T_NUM_MOVABLE)*num_at)/2+1, sizeof(pINChI->nTautomer[0]))) && + (pINChI->nNum_H = (S_CHAR*) inchi_calloc( num_at, sizeof(pINChI->nNum_H[0]))) && + (pINChI->nNum_H_fixed= (S_CHAR*) inchi_calloc( num_at, sizeof(pINChI->nNum_H_fixed[0]))) ) + { + ; + /* nTautomer length: max. number of tautomeric groups is num_at/2 + + 1 word -> number of t-groups + + each group has: + + 1 word -> number of endpoints+INCHI_T_NUM_MOVABLE + INCHI_T_NUM_MOVABLE words -> number(s) of moveable attachments + numbers of endpoints words -> canon. numbers + + max. occurs if each t-group has 2 atoms (num_at/2 t-groups) and all atoms + belong to t-groups (num_at endpoints) + + Total: 1 + (number of t-groups)*(1+INCHI_T_NUM_MOVABLE) + (number of endpoints) <= + 1 + (num_at/2) * (1+INCHI_T_NUM_MOVABLE) + num_at <= + 1 + (3+INCHI_T_NUM_MOVABLE)*num_at/2 words. + */ + } + else + goto out_of_RAM; + + pINChI->szHillFormula = NULL; /* the length is unknown */ + + if ( bIsotopic ) + { + if ( num_isotopic_atoms && + (pINChI->IsotopicAtom = (INChI_IsotopicAtom *)inchi_calloc(num_isotopic_atoms, sizeof(INChI_IsotopicAtom) )) && + (pINChI->IsotopicTGroup = (INChI_IsotopicTGroup *)inchi_calloc(num_isotopic_atoms, sizeof(INChI_IsotopicTGroup) )) ) + { + ; + } + else if ( num_isotopic_atoms ) + { + goto out_of_RAM; + } + if ( !(pINChI->nPossibleLocationsOfIsotopicH = (AT_NUMB *)inchi_calloc( num_at+1, sizeof(pINChI->nPossibleLocationsOfIsotopicH[0]) ) ) ) { + goto out_of_RAM; + } + } + + if ((pINChI->Stereo = Alloc_INChI_Stereo(num_at, num_bonds)) ) + { + ; + } + else + goto out_of_RAM; + + if ( bIsotopic ) + { + if ((pINChI->StereoIsotopic = Alloc_INChI_Stereo(num_at, num_bonds)) ) + { + ; + } + else + goto out_of_RAM; + } + + return pINChI; + +out_of_RAM: + if ( pINChI ) + { + Free_INChI(&pINChI); + /* + inchi_free(pINChI); + */ + } + + return NULL; +} + + +/****************************************************************/ +int Free_INChI_Aux( INChI_Aux **ppINChI_Aux ) +{ + INChI_Aux *pINChI_Aux = *ppINChI_Aux; + if ( pINChI_Aux ) + { + +#if ( bREUSE_INCHI == 1 ) + if ( pINChI_Aux->nRefCount -- > 0 ) + return 1; +#endif + + qzfree( pINChI_Aux->nOrigAtNosInCanonOrd ); + qzfree( pINChI_Aux->nIsotopicOrigAtNosInCanonOrd ); + qzfree( pINChI_Aux->nOrigAtNosInCanonOrdInv ); + qzfree( pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv ); + qzfree( pINChI_Aux->szOrigCoord ); + qzfree( pINChI_Aux->OrigInfo ); +/* + qzfree( pINChI_Aux->nOriginalAtomNumber ); + qzfree( pINChI_Aux->nCanonicalTGroupNumbers ); + qzfree( pINChI_Aux->nIsotopicCanonicalTGroupNumbers); + qzfree( pINChI_Aux->nTautomer ); + qzfree( pINChI_Aux->nNontautomericCanonicalNumbers ); + qzfree( pINChI_Aux->nIsotopicCanonicalNumbers ); + qzfree( pINChI_Aux->nNontautomericIsotopicCanonicalNumbers ); + qzfree( pINChI_Aux->nNontautomericEquNumbers ); + qzfree( pINChI_Aux->nNontautomericIsotopicEquNumbers ); +*/ + qzfree( pINChI_Aux->nConstitEquNumbers ); + qzfree( pINChI_Aux->nConstitEquTGroupNumbers ); + qzfree( pINChI_Aux->nConstitEquIsotopicNumbers ); + qzfree( pINChI_Aux->nConstitEquIsotopicTGroupNumbers ); + qzfree( pINChI_Aux ); + *ppINChI_Aux = NULL; + } + + return 0; +} + + +/****************************************************************/ +INChI_Aux *Alloc_INChI_Aux( int num_at, + int num_isotopic_atoms, + int nAllocMode, + int bOrigCoord ) +{ + INChI_Aux *pINChI_Aux; + int bIsotopic = (nAllocMode & REQ_MODE_ISO); + int num_at_tg = num_at + num_at/2; + /* int bTautomeric = (nAllocMode & REQ_MODE_TAUT); */ + + if ( num_at <= 0 || + NULL == (pINChI_Aux = (INChI_Aux *)inchi_calloc(sizeof(INChI_Aux), 1))) + { + return NULL; + } + + if ( (pINChI_Aux->nOrigAtNosInCanonOrd = (AT_NUMB*) + inchi_calloc(sizeof(pINChI_Aux->nOrigAtNosInCanonOrd[0]), num_at_tg)) && + (pINChI_Aux->nOrigAtNosInCanonOrdInv = (AT_NUMB*) + inchi_calloc(sizeof(pINChI_Aux->nOrigAtNosInCanonOrd[0]), num_at_tg)) && + (pINChI_Aux->nConstitEquNumbers = (AT_NUMB*) + inchi_calloc(sizeof(pINChI_Aux->nConstitEquNumbers[0]), num_at_tg)) ) + { + ; + } + else + goto out_of_RAM; + + if ( num_at > 1 && + (pINChI_Aux->nConstitEquTGroupNumbers = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nConstitEquTGroupNumbers[0]), num_at/2+1)) ) + { + ; + } + else + if ( num_at > 1 ) + goto out_of_RAM; + + if ( num_at > 0 ) + { + pINChI_Aux->OrigInfo = (ORIG_INFO *)inchi_calloc(sizeof(pINChI_Aux->OrigInfo[0]), num_at); + if ( !pINChI_Aux->OrigInfo ) + goto out_of_RAM; + } + + if ( bOrigCoord && num_at > 0 ) + { + pINChI_Aux->szOrigCoord = (MOL_COORD *)inchi_calloc(sizeof(pINChI_Aux->szOrigCoord[0]), num_at); + if ( !pINChI_Aux->szOrigCoord ) + goto out_of_RAM; + } + + if ( bIsotopic ) + { + if ( /*num_isotopic_atoms &&*/ + (pINChI_Aux->nIsotopicOrigAtNosInCanonOrd = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nIsotopicOrigAtNosInCanonOrd[0]), num_at_tg)) && + (pINChI_Aux->nIsotopicOrigAtNosInCanonOrdInv = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nIsotopicOrigAtNosInCanonOrd[0]), num_at_tg)) && + (pINChI_Aux->nConstitEquIsotopicNumbers = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nConstitEquIsotopicNumbers[0]), num_at_tg)) ) + { + ; + } + else if ( num_isotopic_atoms ) + goto out_of_RAM; + + if ( /*num_isotopic_atoms && num_at > 1 &&*/ + (pINChI_Aux->nConstitEquIsotopicTGroupNumbers = (AT_NUMB*)inchi_calloc(sizeof(pINChI_Aux->nConstitEquIsotopicTGroupNumbers[0]), num_at/2+1)) ) { + ; + } + else if ( num_isotopic_atoms && num_at > 1 ) + goto out_of_RAM; + } + + return pINChI_Aux; + + +out_of_RAM: + + if ( pINChI_Aux ) + { + Free_INChI_Aux(&pINChI_Aux); + /* + inchi_free(pINChI_Aux); + */ + } + + return NULL; +} + + +/* + Note that orig_num, curr_num are allocd by caller as (n+1)-long lists +*/ +void CompAtomData_GetNumMapping( COMP_ATOM_DATA *adata, int *orig_num, int *curr_num ) +{ +int i; + if ( !orig_num || !curr_num ) + return; + for (i=0; inum_at; i++) + { + int orig = adata->at[i].orig_at_number; + orig_num[i] = orig; /* orig's are from 1 */ + curr_num[orig] = i; /* curr's are from 0 */ + } +} + + +/****************************************************************/ +/****************************************************************/ +int imat_new( int m, int n, int ***a ) +{ +int i; + *a = ( int **) calloc( m, sizeof( int * ) ); + if (NULL==*a) + return 1; + for (i=0;inum_inp_atoms; + + err = 1; + if ( ! (sg->orig2node = (int *) inchi_calloc( nat+1, sizeof( int ) )) ) goto exitf; + if ( ! (sg->nodes = (int *) inchi_calloc( nnodes, sizeof( int ) )) ) goto exitf; + if ( ! (sg->degrees = (int *) inchi_calloc( nnodes, sizeof( int ) )) ) goto exitf; + /* NB: input list of 'nodes' is assumed to be in 'original_atom_numbering domain' which starts from 1. + Now it is mapped to current atom numbers which starts from 0/connections using at[j].orig_at_number */ + sg->nnodes = 0; + for (i=0;inodes[ sg->nnodes++ ] = nodes[i]; + + for (i=0;i<=nat;i++) sg->orig2node[ i ] = -1; + for (i=0;iorig2node[ sg->nodes[i] ] = i; + + sg->adj = ( subgraf_edge **) inchi_calloc( nnodes, sizeof( subgraf_edge * ) ); + if ( !sg->adj) goto exitf; + + for (i=0;innodes;i++) + { + iat = nodes[i] -1; /* current atom number for this node */ + degree = orig_inp_data->at[iat].valence; + nj = -1; + sg->adj[i] = (subgraf_edge *) inchi_calloc( degree, sizeof( subgraf_edge )); + if ( !sg->adj[i] ) goto exitf; + for (j=0; jat[iat].neighbor[j]; /* for curr num jat, a (jat+1) would be an orig num */ + nbr = sg->orig2node[jat+1]; + if ( nbr < 0 ) + continue; + nj++; + sg->adj[i][nj].nbr = nbr; + sg->adj[i][nj].etype = orig_inp_data->at[iat].bond_type[j]; + } + sg->degrees[i] = nj+1; + } + err = 0; + + subgraf_debug_trace( sg ); + + +exitf: + if ( err ) + subgraf_free( sg ); + + return sg; +} +void subgraf_free( subgraf *sg ) +{ +int i; + if ( !sg ) return; + if ( sg->nodes ) inchi_free ( sg->nodes ); + if ( sg->degrees ) inchi_free ( sg->degrees ); + if ( sg->orig2node) inchi_free ( sg->orig2node ); + if ( sg->adj ) + { + for (i=0; innodes; i++) + if ( sg->adj[i] ) + inchi_free ( sg->adj[i]); + inchi_free( sg->adj ); + } + inchi_free( sg ); + sg = NULL; + + return; +} +void subgraf_debug_trace( subgraf *sg) +{ +int p, q; + + ITRACE_("\n\n*********************************************************************\n* Subgraf:"); + ITRACE_("\n\tNodes: %-d ( ", sg->nnodes); + for(p=0;pnnodes;p++) + ITRACE_("%-d ", sg->nodes[p]); + ITRACE_(")\n\tAdj lists:\n"); + for(p=0;pnnodes;p++) + { + ITRACE_("\tNode #%-d (atno: curr %-d, orig num %-d) ::: Neighbors (node, curr, orig) : ", + p, sg->nodes[p]-1, sg->nodes[p]); + for (q=0; qdegrees[p]; q++) + { + int nbr = sg->adj[p][q].nbr; + ITRACE_("(%-d/%-d/%-d) ", nbr, sg->nodes[nbr]-1, sg->nodes[nbr]); + } + ITRACE_("\n"); + } + ITRACE_("\n* End Subgraf\n*********************************************************************\n"); + + return; +} +subgraf_pathfinder * subgraf_pathfinder_new( subgraf *sg, ORIG_ATOM_DATA *orig_inp_data, + int start, int end ) +{ +subgraf_pathfinder *spf = NULL; + + spf = (subgraf_pathfinder *) inchi_calloc(1, sizeof(subgraf_pathfinder)); + if ( !spf) + goto exitf; + + spf->sg = sg; + spf->start = start; + spf->end = end; + spf->nbonds = 0; + spf->nseen = 0; + + spf->seen = (int *) inchi_calloc( spf->sg->nnodes, sizeof(int)); + if ( !spf->seen ) + { + inchi_free( spf ); + spf = NULL; + } + +exitf: + return spf; +} +void subgraf_pathfinder_free(subgraf_pathfinder *spf) +{ + if ( !spf ) return; + if ( spf->seen ) + inchi_free( spf->seen ); + inchi_free( spf ); + return; +} +void subgraf_pathfinder_run( subgraf_pathfinder *spf, int *nbonds, int **bonds ) +{ +int j, k, node, node0; + + if ( spf->nseen < 1 ) + { + /* Even at very beginning, push start node to seen and set nseen = 1 + and put end node into subgraf_pathfinder's end */ + return; + } + + node0 = spf->seen[ spf->nseen - 1 ]; + for ( j=0; jsg->degrees[node0]; j++ ) + { + node = spf->sg->adj[node0][j].nbr; + if ( is_in_the_ilist( spf->seen, node, spf->nseen ) ) + continue; + if ( node == spf->end ) + { + spf->seen[ spf->nseen++ ] = node; + + ITRACE_("\n\tFound path:\t"); + for (k=0; knseen; k++) + { + ITRACE_("%-d ", spf->sg->nodes[ spf->seen[k] ]); + } + ITRACE_("\t( In node nums: "); + for (k=1; knseen; k++) + { + int at1 = spf->seen[k-1]; + int at2 = spf->seen[k]; + add_bond_if_unseen( spf, at1, at2, nbonds, bonds); + + ITRACE_("%-d ", spf->seen[k]); + } + ITRACE_(")"); + + spf->seen[ spf->nseen - 1 ] = 0; + spf->nseen--; /* pop_back */ + + break; + } + } + for ( j=0; jsg->degrees[node0]; j++ ) + { + node = spf->sg->adj[node0][j].nbr; + if ( node == spf->end || is_in_the_ilist( spf->seen, node, spf->nseen ) ) + continue; + + spf->seen[ spf->nseen++ ] = node; + subgraf_pathfinder_run( spf, nbonds, bonds); + spf->seen[ spf->nseen - 1 ] = 0; + spf->nseen--; + } + + return; +} +void add_bond_if_unseen( subgraf_pathfinder *spf, + int node0, int node, + int *nbonds, int **bonds) +{ +int seen, p, at1, at2; + + at1 = spf->sg->nodes[ node0 ]; + at2 = spf->sg->nodes[ node ]; + if ( at1 > at2 ) + { + int tmp = at1; + at1 = at2; + at2 = tmp; + } + seen = 0; + for (p=0; p<*nbonds; p++) + { + if ( bonds[p][0]==at1 && bonds[p][1]==at2 ) + { + seen = 1; + break; + } + } + if (!seen ) + { + bonds[*nbonds][0] = at1; + bonds[*nbonds][1] = at2; + (*nbonds)++; + } + return; +} + + + +#if ( FIX_ADJ_RAD == 1 ) + +/*************************************************************************/ +int FixNextRadicals( int cur_at, inp_ATOM *at ); +int FixNextRadicals( int cur_at, inp_ATOM *at ) +{ + int j, neigh, num_found = 0; + + for ( j = 0; j < at[cur_at].valence; j ++ ) + { + neigh = at[cur_at].neighbor[j]; + if ( at[neigh].radical == RADICAL_DOUBLET ) + { + at[neigh].radical = 0; + num_found ++; + num_found += FixNextRadicals( neigh, at ); + } + } + + return num_found; +} + + +/*************************************************************************/ +int FixAdjacentRadicals( int num_inp_atoms, inp_ATOM *at ) +{ + int i, j; + char *bVisited = NULL; + int nNumFound = 0, neigh, cur_found; + + for ( i = 0; i < num_inp_atoms; i ++ ) + { + if ( at[i].radical == RADICAL_DOUBLET ) + { + cur_found = 1; + for ( j = 0; j < at[i].valence; j ++ ) + { + neigh = at[i].neighbor[j]; + if ( at[neigh].radical == RADICAL_DOUBLET ) + { + cur_found ++; + } + } + if ( cur_found >= 3 ) + { + nNumFound ++; + at[i].radical = 0; + nNumFound += FixNextRadicals( i, at ); + } + } + } + + return nNumFound; +} + +#endif + +#ifdef COMPILE_ANSI_ONLY + +#ifndef TARGET_API_LIB +/* +#include +#include "inpdef.h" +*/ +void PrintFileName( const char *fmt, + FILE *out_file, + /* INCHI_IOSTREAM *out_file, */ + const char *szFname ) +{ + inchi_print_nodisplay( out_file, fmt, szFname ); +} +#endif + +#endif diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/strutil.h b/INCHI-1-SRC/INCHI_BASE/src/strutil.h similarity index 65% rename from INCHI-1-SRC/INCHI_API/inchi_dll/strutil.h rename to INCHI-1-SRC/INCHI_BASE/src/strutil.h index 9e17f99..fc0e34b 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/strutil.h +++ b/INCHI-1-SRC/INCHI_BASE/src/strutil.h @@ -1,410 +1,473 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __STRUTIL_H__ -#define __STRUTIL_H__ - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -/* forward declaration */ -struct tagTautomerGroupsInfo; - -int ExtractConnectedComponent(inp_ATOM *at, int num_at, - int component_number, - inp_ATOM *component_at ); -int SetConnectedComponentNumber(inp_ATOM *at, int num_at, int component_number ); - -INChI *Alloc_INChI(inp_ATOM *at, int num_at, int *found_num_bonds, - int *found_num_isotopic, int nAllocMode ); -int Free_INChI(INChI **ppINChI); -int Free_INChI_Members(INChI *pINChI); -int Free_INChI_Stereo( INChI_Stereo *pINChI_Stereo ); -INChI_Aux *Alloc_INChI_Aux(int num_at, int num_isotopic_atoms, - int nAllocMode, int bOrigData ); -int Free_INChI_Aux(INChI_Aux **ppINChI_Aux ); - -int Create_INChI(INChI **ppINChI, - INChI_Aux **ppINChI_Aux, - ORIG_ATOM_DATA *orig_inp_data, - inp_ATOM *inp_at, - INP_ATOM_DATA *inp_norm_data[2], - int num_inp_at, - INCHI_MODE nUserMode, - INCHI_MODE *pbTautFlags, INCHI_MODE *pbTautFlagsDone, - struct tagInchiTime *ulMaxTime, - struct tagTautomerGroupsInfo *ti_out, - char *pStrErrStruct); - -int FillOutInfAtom(inp_ATOM *norm_at, - INF_ATOM_DATA *inf_norm_at_data, - int init_num_at, - int num_removed_H, int bAdd_DT_to_num_H, - int nNumRemovedProtons, NUM_H *nNumRemovedProtonsIsotopic, - int bIsotopic, - INChI *pINChI, - INChI_Aux *pINChI_Aux, - int bAbcNumbers, - INCHI_MODE nMode ); - -int FillOutCompositeCanonInfAtom(COMP_ATOM_DATA *composite_norm_data, - INF_ATOM_DATA *inf_norm_at_data, - int bIsotopic, int bTautomeric, - PINChI2 *pINChI2, - PINChI_Aux2 *pINChI_Aux2, - int bAbcNumbers, - INCHI_MODE nMode); - -#if ( FIX_DALKE_BUGS == 1 ) -char *AllocateAndFillHillFormula(INChI *pINChI ); -#endif - -typedef enum tagInchiDiffBits -{ - IDIF_PROBLEM = 0x00000001, /* severe: at least one InChI does not exist */ - IDIF_NUM_AT = 0x00000001, /* severe: different number of atoms in the skeleton */ - IDIF_ATOMS = 0x00000001, /* severe: diiferent types of skeleton atoms */ - IDIF_NUM_EL = 0x00000001, /* severe: formulas differ in another element */ - IDIF_CON_LEN = 0x00000001, /* severe: different connection table lengths */ - IDIF_CON_TBL = 0x00000001, /* severe: different connection tables */ - IDIF_POSITION_H = 0x00000002, /* difference in non-taut (Mobile-H) or all H (Fixed-H) location/number */ - IDIF_MORE_FH = 0x00000004, /* extra fixed H */ - IDIF_LESS_FH = 0x00000008, /* missing fixed H */ - IDIF_MORE_H = 0x00000010, /* formulas differ in number of H */ - IDIF_LESS_H = 0x00000020, /* formulas differ in number of H */ - /*IDIF_TAUT_LEN = 0x00000008,*/ /* different lengths of tautomer lists */ - IDIF_NO_TAUT = 0x00000040, /* restored structure has no taut groups while the original InChI has some */ - IDIF_WRONG_TAUT = 0x00000080, /* restored has tautomerism while the original does not have it */ - IDIF_SINGLE_TG = 0x00000100, /* restored has 1 taut. group while the original InChI has multiple tg */ - IDIF_MULTIPLE_TG = 0x00000200, /* restored has multiple tg while the original InChI has only one tg */ - IDIF_NUM_TG = 0x00000400, /* different number of tautomeric groups */ - /*IDIF_LESS_TG_ENDP = 0x00000200,*/ /* restores structure has less taut. endpoints */ - /*IDIF_MORE_TG_ENDP = 0x00000400,*/ /* restores structure has more taut. endpoints */ - IDIF_EXTRA_TG_ENDP = 0x00000800, /* extra tautomeric endpoint(s) in restored structure */ - IDIF_MISS_TG_ENDP = 0x00001000, /* one or more tg endpoint is not in the restored structure */ - IDIF_DIFF_TG_ENDP = 0x00002000, /* lists of tg endpoints are different */ - IDIF_TG = 0x00004000, /* different tautomeric groups */ - IDIF_NUM_ISO_AT = 0x00008000, /* ?severe: restored struct. has different number of isotopic atoms */ - IDIF_ISO_AT = 0x00010000, /* ?severe: restored struct. has different locations/isotopes of isotopic atoms */ - IDIF_CHARGE = 0x00020000, /* restored structure has different charge */ - IDIF_REM_PROT = 0x00040000, /* proton(s) removed/added from the restored structure */ - IDIF_REM_ISO_H = 0x00080000, /* isotopic H removed */ - IDIF_SC_INV = 0x00100000, /* restores structure has different inversion stereocenter mark */ - IDIF_SC_PARITY = 0x00200000, /* restored structure has stereoatoms or allenes with different parity */ - IDIF_SC_EXTRA_UNDF = 0x00400000, /* restored structure has extra undefined stereocenter(s) */ - IDIF_SC_EXTRA = 0x00800000, /* restored structure has extra stereocenter(s) */ - IDIF_SC_MISS_UNDF = 0x01000000, /* restored structure has not some undefined stereocenter(s) */ - IDIF_SC_MISS = 0x02000000, /* restored structure has not some stereocenters that are not undefined */ - IDIF_SB_PARITY = 0x04000000, /* restored structure has stereobonds or cumulenes with different parity */ - IDIF_SB_EXTRA_UNDF = 0x08000000, /* restored structure has extra undefined stereobond(s) */ - IDIF_SB_EXTRA = 0x10000000, /* restored structure has extra stereobond(s) */ - IDIF_SB_MISS_UNDF = 0x20000000, /* restored structure has not some undefined stereocenters */ - IDIF_SB_MISS = 0x40000000 /* restored structure has not some stereobonds that are not undefined */ -} IDIF; - - -#define IDIFF_SB (IDIF_SB_PARITY | IDIF_SB_EXTRA_UNDF | IDIF_SB_EXTRA | IDIF_SB_MISS_UNDF | IDIF_SB_MISS) -#define IDIFF_SC (IDIF_SC_PARITY | IDIF_SC_EXTRA_UNDF | IDIF_SC_EXTRA | IDIF_SC_MISS_UNDF | IDIF_SC_MISS) - -#define IDIFF_CONSTIT (IDIF_POSITION_H | IDIF_MORE_FH | IDIF_LESS_FH | IDIF_MORE_H | IDIF_LESS_H |\ - IDIF_NO_TAUT | IDIF_WRONG_TAUT | IDIF_SINGLE_TG | IDIF_MULTIPLE_TG | \ - IDIF_NUM_TG | IDIF_EXTRA_TG_ENDP | IDIF_MISS_TG_ENDP | IDIF_TG | \ - IDIF_NUM_ISO_AT | IDIF_ISO_AT | IDIF_CHARGE | IDIF_REM_PROT | IDIF_REM_ISO_H |\ - IDIF_DIFF_TG_ENDP) -#define IDIFF_STEREO (IDIF_SC_INV | IDIF_SC_PARITY | IDIF_SC_EXTRA_UNDF | IDIF_SC_EXTRA | \ - IDIF_SC_MISS_UNDF | IDIF_SC_MISS | IDIF_SB_PARITY | IDIF_SB_EXTRA_UNDF |\ - IDIF_SB_EXTRA | IDIF_SB_MISS_UNDF | IDIF_SB_MISS) - - -/*************************************************************************************/ -#define ICR_MAX_ENDP_IN1_ONLY 32 -#define ICR_MAX_ENDP_IN2_ONLY 32 -#define ICR_MAX_DIFF_FIXED_H 32 -#define ICR_MAX_SB_IN1_ONLY 32 -#define ICR_MAX_SB_IN2_ONLY 32 -#define ICR_MAX_SC_IN1_ONLY 32 -#define ICR_MAX_SC_IN2_ONLY 32 -#define ICR_MAX_SB_UNDF 32 -#define ICR_MAX_SC_UNDF 32 - -typedef struct tagInChICompareResults -{ - INCHI_MODE flags; - - int tot_num_H1; - int tot_num_H2; - int num_taut_H1; - int num_taut_H2; - int num_taut_M1; - int num_taut_M2; - - /* 1 => InChI from reversed struct. 2 => input InChI */ - - AT_NUMB endp_in1_only[ICR_MAX_ENDP_IN1_ONLY]; /* endpoint canonical number = index+1 */ - int num_endp_in1_only; - - AT_NUMB endp_in2_only[ICR_MAX_ENDP_IN2_ONLY]; /* endpoint canonical number = index+1 */ - int num_endp_in2_only; - - AT_NUMB diff_pos_H_at[ICR_MAX_DIFF_FIXED_H]; /* non-tautomeric H */ - S_CHAR diff_pos_H_nH[ICR_MAX_DIFF_FIXED_H]; - int num_diff_pos_H; - - AT_NUMB fixed_H_at1_more[ICR_MAX_DIFF_FIXED_H]; /* extra fixed_H */ - S_CHAR fixed_H_nH1_more[ICR_MAX_DIFF_FIXED_H]; - int num_fixed_H1_more; - - AT_NUMB fixed_H_at2_more[ICR_MAX_DIFF_FIXED_H]; /* missed fixed_H */ - S_CHAR fixed_H_nH2_more[ICR_MAX_DIFF_FIXED_H]; - int num_fixed_H2_more; - - AT_NUMB sc_in1_only[ICR_MAX_SC_IN1_ONLY]; - int num_sc_in1_only; - AT_NUMB sc_in2_only[ICR_MAX_SC_IN2_ONLY]; - int num_sc_in2_only; - - AT_NUMB sb_in1_only[ICR_MAX_SB_IN1_ONLY]; - int num_sb_in1_only; - AT_NUMB sb_in2_only[ICR_MAX_SB_IN2_ONLY]; - int num_sb_in2_only; - - AT_NUMB sb_undef_in1_only[ICR_MAX_SC_UNDF]; - int num_sb_undef_in1_only; - AT_NUMB sb_undef_in2_only[ICR_MAX_SC_UNDF]; - int num_sb_undef_in2_only; - - AT_NUMB sc_undef_in1_only[ICR_MAX_SB_UNDF]; - int num_sc_undef_in1_only; - AT_NUMB sc_undef_in2_only[ICR_MAX_SB_UNDF]; - int num_sc_undef_in2_only; - -} ICR; /* tagInChICompareResults */ - - - -INCHI_MODE CompareReversedINChI2(INChI *i1 /* InChI from reversed struct */, - INChI *i2 /* input InChI */, - INChI_Aux *a1, INChI_Aux *a2, - ICR *picr, int *err ); -int CompareIcr(ICR *picr1, ICR *picr2, INCHI_MODE *pin1, INCHI_MODE *pin2, INCHI_MODE mask ); - -int CompareReversedINChI(INChI *i1, INChI *i2, INChI_Aux *a1, INChI_Aux *a2 ); -const char *CompareReversedInchiMsg(int code); - -#define EQL_EXISTS 1 -#define EQL_SP3 2 -#define EQL_SP3_INV 4 -#define EQL_SP2 8 - -int Eql_INChI_Stereo(INChI_Stereo *s1, int eql1, INChI_Stereo *s2, int eql2, int bRelRac ); -int Eql_INChI_Isotopic(INChI *i1, INChI *i2 ); - -#define EQL_EQU 0 -#define EQL_EQU_TG 1 -#define EQL_EQU_ISO 2 - -int Eql_INChI_Aux_Equ(INChI_Aux *a1, int eql1, INChI_Aux *a2, int eql2 ); - -#define EQL_NUM 0 -#define EQL_NUM_INV 1 -#define EQL_NUM_ISO 2 -int Eql_INChI_Aux_Num(INChI_Aux *a1, int eql1, INChI_Aux *a2, int eql2 ); - -int bHasEquString(AT_NUMB *LinearCT, int nLenCT ); - -int CompINChINonTaut2(const void *p1, const void *p2); -int CompINChITaut2(const void *p1, const void *p2); -int CompINChI2(const INCHI_SORT *p1, const INCHI_SORT *p2, int bTaut, int bCompareIsotopic); -int CompINChITautVsNonTaut(const INCHI_SORT *p1, const INCHI_SORT *p2, int bCompareIsotopic); - -typedef enum tagDiffINChISegments -{ /* r = repetitive, n = non-repetitive */ - DIFS_f_FORMULA, /* 0 r; fixed-H <-> mobile-H */ - DIFS_c_CONNECT, /* 1 n; connection table; mobile-H only */ - DIFS_h_H_ATOMS, /* 2 n; hydrogen atoms: mobile-H and Fixed-H; have different meanings */ - DIFS_q_CHARGE, /* 3 r; charge; fixed-H <-> mobile-H */ - DIFS_p_PROTONS, /* 4 n; protons; mobile-H only */ - DIFS_b_SBONDS, /* 5 r: stereobonds: fixed-H <-> mobile-H * isotopic <-> non-isotopic */ - DIFS_t_SATOMS, /* 6 r: stereoatoms: fixed-H <-> mobile-H * isotopic <-> non-isotopic */ - DIFS_m_SP3INV, /* 7 r: stereo-abs-inv: fixed-H <-> mobile-H * isotopic <-> non-isotopic */ - DIFS_s_STYPE, /* 8 r: stereo-type: fixed-H <-> mobile-H * isotopic <-> non-isotopic */ - DIFS_i_IATOMS, /* 9 r: isotopic atoms: fixed-H <-> mobile-H * isotopic <-> non-isotopic */ - DIFS_o_TRANSP, /* 10 n: Fixed-H transposition */ - DIFS_idf_LENGTH, /* 11 length of the array relevant to the INChI Identifier */ - /* later elements referring to AuxInfo may be added */ - DIFS_LENGTH = DIFS_idf_LENGTH /* length of the array */ -} DIF_SEGMENTS; - -typedef enum tagDiffINChILayers -{ - DIFL_M, /* 0 main layer */ - DIFL_MI, /* 1 main isotopic */ - DIFL_F, /* 2 fixed-H */ - DIFL_FI, /* 3 fixed-H isotopic */ - DIFL_LENGTH /* number of layers */ -} DIF_LAYERS; - -/* Value meaning */ -typedef enum tagMarkDiff -{ - DIFV_BOTH_EMPTY = 0, /* both this and the component in the preceding namesake segment are empty */ - DIFV_EQL2PRECED = 1, /* equal to the component in the preceding namesake segment */ - DIFV_NEQ2PRECED = 2, /* different from the component in the preceding namesake segment */ - DIFV_IS_EMPTY = 4, /* is empty while the preceding namesake segment is not empty */ - DIFV_FI_EQ_MI = 8, /* FI stereo component is equal to the component in the MI namesake segment */ - /* while M and F components are empty */ - /* decision_F = bitmask: bits that should not be present */ - /* decision_T = bitmask: at least one of the bits should be present */ - /* decision = true if( !( BITS & decision_F ) && ( BITS & decision_F ) ) */ - DIFV_OUTPUT_EMPTY_T = (DIFV_IS_EMPTY), /* bits present for empty segment output */ - DIFV_OUTPUT_EMPTY_F = (DIFV_EQL2PRECED | DIFV_NEQ2PRECED | DIFV_FI_EQ_MI), /* bits NOT present */ - - DIFV_OUTPUT_OMIT_F = (DIFV_NEQ2PRECED | DIFV_IS_EMPTY), /* bits NOT present for omitting */ - - DIFV_OUTPUT_FILL_T = (DIFV_EQL2PRECED | DIFV_NEQ2PRECED | DIFV_FI_EQ_MI) - -} DIF_VALUES; - -typedef enum tagINChISegmAction -{ - INCHI_SEGM_OMIT = 0, - INCHI_SEGM_FILL = 1, /* the value is used in str_LineEnd() */ - INCHI_SEGM_EMPTY = 2 /* the value is used in str_LineEnd() */ -} INCHI_SEGM_ACTION; - -int CompINChILayers(const INCHI_SORT *p1, const INCHI_SORT *p2, - char sDifSegs[][DIFS_LENGTH], int bFixTranspChargeBug ); -int MarkUnusedAndEmptyLayers( char sDifSegs[][DIFS_LENGTH] ); -int INChI_SegmentAction( char cDifSegs ); - -#define FLAG_SORT_PRINT_TRANSPOS_BAS 1 /* transposition in the main InChI layer */ -#define FLAG_SORT_PRINT_TRANSPOS_REC 2 /* transposition in the reconnected InChI layer */ -#define FLAG_SORT_PRINT_NO_NFIX_H_BAS 4 /* no fixed H non-isotopic in the main InChI layer */ -#define FLAG_SORT_PRINT_NO_NFIX_H_REC 8 /* no fixed H non-isotopic in the reconnected InChI layer */ -#define FLAG_SORT_PRINT_NO_IFIX_H_BAS 16 /* no fixed H isotopic in the main InChI layer */ -#define FLAG_SORT_PRINT_NO_IFIX_H_REC 32 /* no fixed H isotopic in the the reconnected InChI layer */ -#define FLAG_SORT_PRINT_ReChI_PREFIX 64 /* Output ReChI instead of InChI */ - -int OutputINChI1(char *pStr, int nStrLen, - INCHI_SORT *pINChISortTautAndNonTaut2[][TAUT_NUM], - int iINChI, - ORIG_STRUCT *pOrigStruct, - int bDisconnectedCoord, int bOutputType, int bINChIOutputOptions, - int bXml, int bAbcNumbers,int bCtPredecessors, int bNoStructLabels, - int num_components2[], - int num_non_taut2[], int num_taut2[], - INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *log_file, - int num_input_struct, - const char *szSdfLabel, const char *szSdfValue, long lSdfId, - int *pSortPrintINChIFlags, - unsigned char save_opt_bits); - -int OutputINChI2(char *pStr, int nStrLen, - INCHI_SORT *pINChISortTautAndNonTaut2[][TAUT_NUM], - int iINChI, - ORIG_STRUCT *pOrigStruct, - int bDisconnectedCoord, int bOutputType, int bINChIOutputOptions, - int bXml, int bAbcNumbers,int bCtPredecessors, int bNoStructLabels, - int num_components2[], - int num_non_taut2[], int num_taut2[], - INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *log_file, - int num_input_struct, - const char *szSdfLabel, const char *szSdfValue, long lSdfId, - int *pSortPrintINChIFlags, - unsigned char save_opt_bits); - -int SaveEquComponentsInfoAndSortOrder(int iINChI, - INCHI_SORT *pINChISort[TAUT_NUM], - int *num_components, - ORIG_ATOM_DATA *orig_inp_data, - ORIG_ATOM_DATA *prep_inp_data, - COMP_ATOM_DATA composite_norm_data[TAUT_NUM], - int bCompareComponents ); -int OutputINChIXmlRootStartTag( INCHI_IOSTREAM *output_file ); -int OutputINChIXmlRootEndTag( INCHI_IOSTREAM *output_file ); -int OutputINChIXmlError( INCHI_IOSTREAM *output_file, char *pStr, int nStrLen, int ind, - /*int nErrorNumber,*/ char *szErrorText, int bError ); -int OutputINChIPlainError( INCHI_IOSTREAM *output_file, char *pStr, int nStrLen, - char *pErrorText, int bError ); -int OutputINChIXmlStructStartTag( INCHI_IOSTREAM *output_file, char *pStr, int ind /* indent*/, - int nStrLen, int bNoStructLabels, - int num_input_struct, const char *szSdfLabel, const char *szSdfValue ); -int OutputINChIXmlStructEndTag( INCHI_IOSTREAM *output_file, char *pStr, int nStrLen, int ind ); - -int GetInpStructErrorType(INPUT_PARMS *ip, int err, char *pStrErrStruct, int num_inp_atoms ); -int ProcessStructError( INCHI_IOSTREAM *output_file, INCHI_IOSTREAM *log_file, /*int err,*/ - char *pStrErrStruct, int nErrorType, - int *bXmlStructStarted, long num_inp, INPUT_PARMS *ip, char *pStr, int nStrLen ); - -int bNumHeterAtomHasIsotopicH( inp_ATOM *atom, int num_atoms ); - -int WriteToSDfile( const INP_ATOM_DATA *inp_at_data, INCHI_IOSTREAM* fcb, const char* name, const char* comment, - const char *szLabel, const char *szValue ); -int WriteOrigAtomDataToSDfile( const ORIG_ATOM_DATA *inp_at_data, INCHI_IOSTREAM * fcb, const char* name, - const char* comment, int bChiral, int bAtomsDT, const char *szLabel, const char *szValue); -int bIsMetalSalt( inp_ATOM *at, int i ); - - -extern const char gsMissing[]; -extern const char gsEmpty[]; -extern const char gsSpace[]; -extern const char gsEqual[]; -/* -#define gsMissing "is missing" -#define gsEmpty "" -#define gsSpace " " -#define gsEqual "=" -*/ -/* format string for SDF_LBL_VAL(L,V): %s%s%s%s (four strings) */ -/*#define SDF_LBL_VAL(L,V) ((L)&&(L)[0])?gsSpace:gsEmpty, ((L)&&(L)[0])?L:gsEmpty, ((L)&&(L)[0])? (((V)&&(V)[0])?gsEqual:gsSpace):gsEmpty, ((L)&&(L)[0])?((V)&&(V)[0]?V:gsMissing):gsEmpty*/ -#define SDF_LBL_VAL(L,V) ((L)&&(L)[0])?gsSpace:gsEmpty, ((L)&&(L)[0])?L:gsEmpty, ((L)&&(L)[0])? (((V)&&(V)[0])?gsEqual:gsSpace):gsEmpty, ((V)&&(V)[0])?V:((L)&&(L)[0])?gsMissing:gsEmpty - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif /* __STRUTIL_H__ */ - +/* +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _STRUTIL_H_ +#define _STRUTIL_H_ + + +/* Mol structure utlities */ + + +#include "inpdef.h" +#include "ichi.h" + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + +/* forward declaration */ +struct tagTautomerGroupsInfo; +struct tagCANON_GLOBALS; + +int ExtractConnectedComponent(inp_ATOM *at, int num_at, + int component_number, + inp_ATOM *component_at ); +int SetConnectedComponentNumber(inp_ATOM *at, int num_at, int component_number ); + +INChI *Alloc_INChI(inp_ATOM *at, int num_at, int *found_num_bonds, + int *found_num_isotopic, int nAllocMode ); +int Free_INChI(INChI **ppINChI); +int Free_INChI_Members(INChI *pINChI); +int Free_INChI_Stereo( INChI_Stereo *pINChI_Stereo ); +INChI_Aux *Alloc_INChI_Aux(int num_at, int num_isotopic_atoms, + int nAllocMode, int bOrigData ); +int Free_INChI_Aux(INChI_Aux **ppINChI_Aux ); + +int Create_INChI( struct tagCANON_GLOBALS *pCG, + struct tagINCHI_CLOCK *ic, + INChI **ppINChI, + INChI_Aux **ppINChI_Aux, + ORIG_ATOM_DATA *orig_inp_data, + inp_ATOM *inp_at, + INP_ATOM_DATA *inp_norm_data[2], + int num_inp_at, + INCHI_MODE nUserMode, + int LargeMolecules, + int Polymers, + INCHI_MODE *pbTautFlags, + INCHI_MODE *pbTautFlagsDone, + struct tagInchiTime *ulMaxTime, + struct tagTautomerGroupsInfo *ti_out, + char *pStrErrStruct); + +int FillOutInfAtom( struct tagCANON_GLOBALS *pCG, + inp_ATOM *norm_at, + INF_ATOM_DATA *inf_norm_at_data, + int init_num_at, + int num_removed_H, + int bAdd_DT_to_num_H, + int nNumRemovedProtons, + NUM_H *nNumRemovedProtonsIsotopic, + int bIsotopic, + INChI *pINChI, + INChI_Aux *pINChI_Aux, + int bAbcNumbers, + INCHI_MODE nMode ); + +int FillOutCompositeCanonInfAtom( struct tagCANON_GLOBALS *pCG, + COMP_ATOM_DATA *composite_norm_data, + INF_ATOM_DATA *inf_norm_at_data, + int bIsotopic, + int bTautomeric, + PINChI2 *pINChI2, + PINChI_Aux2 *pINChI_Aux2, + int bAbcNumbers, + INCHI_MODE nMode); + +#if ( FIX_DALKE_BUGS == 1 ) +char *AllocateAndFillHillFormula(INChI *pINChI ); +#endif + +typedef enum tagInchiDiffBits +{ + IDIF_PROBLEM = 0x00000001, /* severe: at least one InChI does not exist */ + IDIF_NUM_AT = 0x00000001, /* severe: different number of atoms in the skeleton */ + IDIF_ATOMS = 0x00000001, /* severe: diiferent types of skeleton atoms */ + IDIF_NUM_EL = 0x00000001, /* severe: formulas differ in another element */ + IDIF_CON_LEN = 0x00000001, /* severe: different connection table lengths */ + IDIF_CON_TBL = 0x00000001, /* severe: different connection tables */ + IDIF_POSITION_H = 0x00000002, /* difference in non-taut (Mobile-H) or all H (Fixed-H) location/number */ + IDIF_MORE_FH = 0x00000004, /* extra fixed H */ + IDIF_LESS_FH = 0x00000008, /* missing fixed H */ + IDIF_MORE_H = 0x00000010, /* formulas differ in number of H */ + IDIF_LESS_H = 0x00000020, /* formulas differ in number of H */ + /*IDIF_TAUT_LEN = 0x00000008,*/ /* different lengths of tautomer lists */ + IDIF_NO_TAUT = 0x00000040, /* restored structure has no taut groups while the original InChI has some */ + IDIF_WRONG_TAUT = 0x00000080, /* restored has tautomerism while the original does not have it */ + IDIF_SINGLE_TG = 0x00000100, /* restored has 1 taut. group while the original InChI has multiple tg */ + IDIF_MULTIPLE_TG = 0x00000200, /* restored has multiple tg while the original InChI has only one tg */ + IDIF_NUM_TG = 0x00000400, /* different number of tautomeric groups */ + /*IDIF_LESS_TG_ENDP = 0x00000200,*/ /* restores structure has less taut. endpoints */ + /*IDIF_MORE_TG_ENDP = 0x00000400,*/ /* restores structure has more taut. endpoints */ + IDIF_EXTRA_TG_ENDP = 0x00000800, /* extra tautomeric endpoint(s) in restored structure */ + IDIF_MISS_TG_ENDP = 0x00001000, /* one or more tg endpoint is not in the restored structure */ + IDIF_DIFF_TG_ENDP = 0x00002000, /* lists of tg endpoints are different */ + IDIF_TG = 0x00004000, /* different tautomeric groups */ + IDIF_NUM_ISO_AT = 0x00008000, /* ?severe: restored struct. has different number of isotopic atoms */ + IDIF_ISO_AT = 0x00010000, /* ?severe: restored struct. has different locations/isotopes of isotopic atoms */ + IDIF_CHARGE = 0x00020000, /* restored structure has different charge */ + IDIF_REM_PROT = 0x00040000, /* proton(s) removed/added from the restored structure */ + IDIF_REM_ISO_H = 0x00080000, /* isotopic H removed */ + IDIF_SC_INV = 0x00100000, /* restores structure has different inversion stereocenter mark */ + IDIF_SC_PARITY = 0x00200000, /* restored structure has stereoatoms or allenes with different parity */ + IDIF_SC_EXTRA_UNDF = 0x00400000, /* restored structure has extra undefined stereocenter(s) */ + IDIF_SC_EXTRA = 0x00800000, /* restored structure has extra stereocenter(s) */ + IDIF_SC_MISS_UNDF = 0x01000000, /* restored structure has not some undefined stereocenter(s) */ + IDIF_SC_MISS = 0x02000000, /* restored structure has not some stereocenters that are not undefined */ + IDIF_SB_PARITY = 0x04000000, /* restored structure has stereobonds or cumulenes with different parity */ + IDIF_SB_EXTRA_UNDF = 0x08000000, /* restored structure has extra undefined stereobond(s) */ + IDIF_SB_EXTRA = 0x10000000, /* restored structure has extra stereobond(s) */ + IDIF_SB_MISS_UNDF = 0x20000000, /* restored structure has not some undefined stereocenters */ + IDIF_SB_MISS = 0x40000000 /* restored structure has not some stereobonds that are not undefined */ +} IDIF; + + +#define IDIFF_SB (IDIF_SB_PARITY | IDIF_SB_EXTRA_UNDF | IDIF_SB_EXTRA | IDIF_SB_MISS_UNDF | IDIF_SB_MISS) +#define IDIFF_SC (IDIF_SC_PARITY | IDIF_SC_EXTRA_UNDF | IDIF_SC_EXTRA | IDIF_SC_MISS_UNDF | IDIF_SC_MISS) + +#define IDIFF_CONSTIT (IDIF_POSITION_H | IDIF_MORE_FH | IDIF_LESS_FH | IDIF_MORE_H | IDIF_LESS_H |\ + IDIF_NO_TAUT | IDIF_WRONG_TAUT | IDIF_SINGLE_TG | IDIF_MULTIPLE_TG | \ + IDIF_NUM_TG | IDIF_EXTRA_TG_ENDP | IDIF_MISS_TG_ENDP | IDIF_TG | \ + IDIF_NUM_ISO_AT | IDIF_ISO_AT | IDIF_CHARGE | IDIF_REM_PROT | IDIF_REM_ISO_H |\ + IDIF_DIFF_TG_ENDP) +#define IDIFF_STEREO (IDIF_SC_INV | IDIF_SC_PARITY | IDIF_SC_EXTRA_UNDF | IDIF_SC_EXTRA | \ + IDIF_SC_MISS_UNDF | IDIF_SC_MISS | IDIF_SB_PARITY | IDIF_SB_EXTRA_UNDF |\ + IDIF_SB_EXTRA | IDIF_SB_MISS_UNDF | IDIF_SB_MISS) + + +/*************************************************************************************/ +#define ICR_MAX_ENDP_IN1_ONLY 32 +#define ICR_MAX_ENDP_IN2_ONLY 32 +#define ICR_MAX_DIFF_FIXED_H 32 +#define ICR_MAX_SB_IN1_ONLY 32 +#define ICR_MAX_SB_IN2_ONLY 32 +#define ICR_MAX_SC_IN1_ONLY 32 +#define ICR_MAX_SC_IN2_ONLY 32 +#define ICR_MAX_SB_UNDF 32 +#define ICR_MAX_SC_UNDF 32 + +typedef struct tagInChICompareResults +{ + INCHI_MODE flags; + + int tot_num_H1; + int tot_num_H2; + int num_taut_H1; + int num_taut_H2; + int num_taut_M1; + int num_taut_M2; + + /* 1 => InChI from reversed struct. 2 => input InChI */ + + AT_NUMB endp_in1_only[ICR_MAX_ENDP_IN1_ONLY]; /* endpoint canonical number = index+1 */ + int num_endp_in1_only; + + AT_NUMB endp_in2_only[ICR_MAX_ENDP_IN2_ONLY]; /* endpoint canonical number = index+1 */ + int num_endp_in2_only; + + AT_NUMB diff_pos_H_at[ICR_MAX_DIFF_FIXED_H]; /* non-tautomeric H */ + S_CHAR diff_pos_H_nH[ICR_MAX_DIFF_FIXED_H]; + int num_diff_pos_H; + + AT_NUMB fixed_H_at1_more[ICR_MAX_DIFF_FIXED_H]; /* extra fixed_H */ + S_CHAR fixed_H_nH1_more[ICR_MAX_DIFF_FIXED_H]; + int num_fixed_H1_more; + + AT_NUMB fixed_H_at2_more[ICR_MAX_DIFF_FIXED_H]; /* missed fixed_H */ + S_CHAR fixed_H_nH2_more[ICR_MAX_DIFF_FIXED_H]; + int num_fixed_H2_more; + + AT_NUMB sc_in1_only[ICR_MAX_SC_IN1_ONLY]; + int num_sc_in1_only; + AT_NUMB sc_in2_only[ICR_MAX_SC_IN2_ONLY]; + int num_sc_in2_only; + + AT_NUMB sb_in1_only[ICR_MAX_SB_IN1_ONLY]; + int num_sb_in1_only; + AT_NUMB sb_in2_only[ICR_MAX_SB_IN2_ONLY]; + int num_sb_in2_only; + + AT_NUMB sb_undef_in1_only[ICR_MAX_SC_UNDF]; + int num_sb_undef_in1_only; + AT_NUMB sb_undef_in2_only[ICR_MAX_SC_UNDF]; + int num_sb_undef_in2_only; + + AT_NUMB sc_undef_in1_only[ICR_MAX_SB_UNDF]; + int num_sc_undef_in1_only; + AT_NUMB sc_undef_in2_only[ICR_MAX_SB_UNDF]; + int num_sc_undef_in2_only; +} ICR; /* tagInChICompareResults */ + + + +INCHI_MODE CompareReversedINChI2(INChI *i1 /* InChI from reversed struct */, + INChI *i2 /* input InChI */, + INChI_Aux *a1, INChI_Aux *a2, + ICR *picr, int *err ); +int CompareIcr(ICR *picr1, ICR *picr2, INCHI_MODE *pin1, INCHI_MODE *pin2, INCHI_MODE mask ); + +int CompareReversedINChI(INChI *i1, INChI *i2, INChI_Aux *a1, INChI_Aux *a2 ); +const char *CompareReversedInchiMsg(int code); + +#define EQL_EXISTS 1 +#define EQL_SP3 2 +#define EQL_SP3_INV 4 +#define EQL_SP2 8 + +int Eql_INChI_Stereo(INChI_Stereo *s1, int eql1, INChI_Stereo *s2, int eql2, int bRelRac ); +int Eql_INChI_Isotopic(INChI *i1, INChI *i2 ); + +#define EQL_EQU 0 +#define EQL_EQU_TG 1 +#define EQL_EQU_ISO 2 + +int Eql_INChI_Aux_Equ(INChI_Aux *a1, int eql1, INChI_Aux *a2, int eql2 ); + +#define EQL_NUM 0 +#define EQL_NUM_INV 1 +#define EQL_NUM_ISO 2 +int Eql_INChI_Aux_Num(INChI_Aux *a1, int eql1, INChI_Aux *a2, int eql2 ); + +int bHasEquString(AT_NUMB *LinearCT, int nLenCT ); + +int CompINChINonTaut2(const void *p1, const void *p2); +int CompINChITaut2(const void *p1, const void *p2); +int CompINChI2(const INCHI_SORT *p1, const INCHI_SORT *p2, int bTaut, int bCompareIsotopic); +int CompINChITautVsNonTaut(const INCHI_SORT *p1, const INCHI_SORT *p2, int bCompareIsotopic); + +typedef enum tagDiffINChISegments +{ /* r = repetitive, n = non-repetitive */ + DIFS_f_FORMULA, /* 0 r; fixed-H <-> mobile-H */ + DIFS_c_CONNECT, /* 1 n; connection table; mobile-H only */ + DIFS_h_H_ATOMS, /* 2 n; hydrogen atoms: mobile-H and Fixed-H; have different meanings */ + DIFS_q_CHARGE, /* 3 r; charge; fixed-H <-> mobile-H */ + DIFS_p_PROTONS, /* 4 n; protons; mobile-H only */ + DIFS_b_SBONDS, /* 5 r: stereobonds: fixed-H <-> mobile-H * isotopic <-> non-isotopic */ + DIFS_t_SATOMS, /* 6 r: stereoatoms: fixed-H <-> mobile-H * isotopic <-> non-isotopic */ + DIFS_m_SP3INV, /* 7 r: stereo-abs-inv: fixed-H <-> mobile-H * isotopic <-> non-isotopic */ + DIFS_s_STYPE, /* 8 r: stereo-type: fixed-H <-> mobile-H * isotopic <-> non-isotopic */ + DIFS_i_IATOMS, /* 9 r: isotopic atoms: fixed-H <-> mobile-H * isotopic <-> non-isotopic */ + DIFS_o_TRANSP, /* 10 n: Fixed-H transposition */ + DIFS_idf_LENGTH, /* 11 length of the array relevant to the INChI Identifier */ + /* later elements referring to AuxInfo may be added */ + DIFS_LENGTH = DIFS_idf_LENGTH /* length of the array */ +} DIF_SEGMENTS; + +typedef enum tagDiffINChILayers +{ + DIFL_M, /* 0 main layer */ + DIFL_MI, /* 1 main isotopic */ + DIFL_F, /* 2 fixed-H */ + DIFL_FI, /* 3 fixed-H isotopic */ + DIFL_LENGTH /* number of layers */ +} DIF_LAYERS; + +/* Value meaning */ +typedef enum tagMarkDiff +{ + DIFV_BOTH_EMPTY = 0, /* both this and the component in the preceding namesake segment are empty */ + DIFV_EQL2PRECED = 1, /* equal to the component in the preceding namesake segment */ + DIFV_NEQ2PRECED = 2, /* different from the component in the preceding namesake segment */ + DIFV_IS_EMPTY = 4, /* is empty while the preceding namesake segment is not empty */ + DIFV_FI_EQ_MI = 8, /* FI stereo component is equal to the component in the MI namesake segment */ + /* while M and F components are empty */ + /* decision_F = bitmask: bits that should not be present */ + /* decision_T = bitmask: at least one of the bits should be present */ + /* decision = true if( !( BITS & decision_F ) && ( BITS & decision_F ) ) */ + DIFV_OUTPUT_EMPTY_T = (DIFV_IS_EMPTY), /* bits present for empty segment output */ + DIFV_OUTPUT_EMPTY_F = (DIFV_EQL2PRECED | DIFV_NEQ2PRECED | DIFV_FI_EQ_MI), /* bits NOT present */ + + DIFV_OUTPUT_OMIT_F = (DIFV_NEQ2PRECED | DIFV_IS_EMPTY), /* bits NOT present for omitting */ + + DIFV_OUTPUT_FILL_T = (DIFV_EQL2PRECED | DIFV_NEQ2PRECED | DIFV_FI_EQ_MI) +} DIF_VALUES; + +typedef enum tagINChISegmAction +{ + INCHI_SEGM_OMIT = 0, + INCHI_SEGM_FILL = 1, /* the value is used in str_LineEnd() */ + INCHI_SEGM_EMPTY = 2 /* the value is used in str_LineEnd() */ +} INCHI_SEGM_ACTION; + +int CompINChILayers(const INCHI_SORT *p1, const INCHI_SORT *p2, + char sDifSegs[][DIFS_LENGTH], int bFixTranspChargeBug ); +int MarkUnusedAndEmptyLayers( char sDifSegs[][DIFS_LENGTH] ); +int INChI_SegmentAction( char cDifSegs ); + +#define FLAG_SORT_PRINT_TRANSPOS_BAS 1 /* transposition in the main InChI layer */ +#define FLAG_SORT_PRINT_TRANSPOS_REC 2 /* transposition in the reconnected InChI layer */ +#define FLAG_SORT_PRINT_NO_NFIX_H_BAS 4 /* no fixed H non-isotopic in the main InChI layer */ +#define FLAG_SORT_PRINT_NO_NFIX_H_REC 8 /* no fixed H non-isotopic in the reconnected InChI layer */ +#define FLAG_SORT_PRINT_NO_IFIX_H_BAS 16 /* no fixed H isotopic in the main InChI layer */ +#define FLAG_SORT_PRINT_NO_IFIX_H_REC 32 /* no fixed H isotopic in the the reconnected InChI layer */ +#define FLAG_SORT_PRINT_ReChI_PREFIX 64 /* Output ReChI instead of InChI */ + + +struct tagCANON_GLOBALS; + +int OutputINChI1( struct tagCANON_GLOBALS *pCG, + INCHI_IOSTREAM_STRING *strbuf, + INCHI_SORT *pINChISortTautAndNonTaut2[][TAUT_NUM], + int iINChI, + ORIG_STRUCT *pOrigStruct, + int bDisconnectedCoord, + int bOutputType, + int bINChIOutputOptions, + int bAbcNumbers, + int bCtPredecessors, + int bNoStructLabels, + int num_components2[], + int num_non_taut2[], + int num_taut2[], + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM *log_file, + int num_input_struct, + const char *szSdfLabel, + const char *szSdfValue, + long lSdfId, + int *pSortPrintINChIFlags, + unsigned char save_opt_bits); + +int OutputINChI2( struct tagCANON_GLOBALS *pCG, + INCHI_IOSTREAM_STRING *strbuf, + INCHI_SORT *pINChISortTautAndNonTaut2[][TAUT_NUM], + int INCHI_basic_or_INCHI_reconnected, + ORIG_STRUCT *pOrigStruct, + int bDisconnectedCoord, + int bOutputType, + int bINChIOutputOptions, + int bAbcNumbers, + int bCtPredecessors, + int bNoStructLabels, + int num_components2[], + int num_non_taut2[], + int num_taut2[], + INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM *log_file, + int num_input_struct, + const char *szSdfLabel, + const char *szSdfValue, + long lSdfId, + int *pSortPrintINChIFlags, + unsigned char save_opt_bits); + +int SaveEquComponentsInfoAndSortOrder( int iINChI, + INCHI_SORT *pINChISort[TAUT_NUM], + int *num_components, + ORIG_ATOM_DATA *orig_inp_data, + ORIG_ATOM_DATA *prep_inp_data, + COMP_ATOM_DATA composite_norm_data[TAUT_NUM], + int bCompareComponents ); + +int OutputINChIPlainError( INCHI_IOSTREAM *out_file, + char *pErrorText, + int bError ); +int GetInpStructErrorType(INPUT_PARMS *ip, int err, char *pStrErrStruct, int num_inp_atoms ); +int ProcessStructError( INCHI_IOSTREAM *out_file, + INCHI_IOSTREAM *log_file, + char *pStrErrStruct, + int nErrorType, + long num_inp, + INPUT_PARMS *ip ); + +int bNumHeterAtomHasIsotopicH( inp_ATOM *atom, int num_atoms ); + +int WriteToSDfile( const INP_ATOM_DATA *inp_at_data, INCHI_IOSTREAM* fcb, const char* name, const char* comment, + const char *szLabel, const char *szValue ); +int bIsMetalSalt( inp_ATOM *at, int i ); + + +extern const char gsMissing[]; +extern const char gsEmpty[]; +extern const char gsSpace[]; +extern const char gsEqual[]; + +#define SDF_LBL_VAL(L,V) ((L)&&(L)[0])?gsSpace:gsEmpty, ((L)&&(L)[0])?L:gsEmpty, ((L)&&(L)[0])? (((V)&&(V)[0])?gsEqual:gsSpace):gsEmpty, ((V)&&(V)[0])?V:((L)&&(L)[0])?gsMissing:gsEmpty + + +typedef struct subgraf_edge +{ + int nbr; + int etype; +} +subgraf_edge; +typedef struct subgraf +{ + int nnodes; + int *nodes; + int *degrees; + int *orig2node; + subgraf_edge **adj; +} subgraf; +typedef struct subgraf_pathfinder +{ + subgraf *sg; + int start; + int end; + int maxbonds; + int nbonds; + int nseen; + int *seen; +} subgraf_pathfinder; + +int imat_new( int m, int n, int ***a ); +void imat_free( int m, int **a ); +subgraf * subgraf_new( ORIG_ATOM_DATA *orig_inp_data, + int nnodes, int *nodes, + int *npsbonds, int **psbonds ); +void subgraf_free( subgraf *sg); +void subgraf_debug_trace( subgraf *sg); +subgraf_pathfinder * + subgraf_pathfinder_new( subgraf *sg, ORIG_ATOM_DATA *orig_inp_data, int start, int end ); +void subgraf_pathfinder_free(subgraf_pathfinder *spf); +void subgraf_pathfinder_run( subgraf_pathfinder *spf, int *nbonds, int **bonds ); + +void CompAtomData_GetNumMapping( COMP_ATOM_DATA *adata, int *orig_num, int *curr_num ); + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + + +#endif /* _STRUTIL_H_ */ diff --git a/INCHI-1-SRC/INCHI_API/inchi_dll/util.c b/INCHI-1-SRC/INCHI_BASE/src/util.c similarity index 58% rename from INCHI-1-SRC/INCHI_API/inchi_dll/util.c rename to INCHI-1-SRC/INCHI_BASE/src/util.c index d5c8f2f..ccb8853 100644 --- a/INCHI-1-SRC/INCHI_API/inchi_dll/util.c +++ b/INCHI-1-SRC/INCHI_BASE/src/util.c @@ -1,1173 +1,1742 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include - -#include "mode.h" - -#include "incomdef.h" -#include "inpdef.h" -#include "util.h" -#include "extr_ct.h" - - -#include "ichicomp.h" - -#define MIN_ATOM_CHARGE (-2) -#define MAX_ATOM_CHARGE 2 -#define NEUTRAL_STATE (-MIN_ATOM_CHARGE) -#define NUM_ATOM_CHARGES (MAX_ATOM_CHARGE - MIN_ATOM_CHARGE + 1) -#define MAX_NUM_VALENCES 5 /* max. number + 1 to provide zero termination */ - -typedef struct tagElData { - const char *szElName; - int nAtMass; /* Avg. atomic mass from the Periodic Chart of the Elements (Fisher cat. no. 05-702-10) */ - int nNormAtMass; /* Atomic mass of the most abundant isotope */ - double dAtMass; /* exact mw of the most abundant isotope */ - int nType; /* METAL or METAL2 */ - int nElNegPauling10; /* Pauling electronegativity x 10; 0 => unknown */ - int bDoNotAddH; /* InChI does not add implicit H to atoms that have bDoNotAddH != 0 */ - S_CHAR cValence[NUM_ATOM_CHARGES][MAX_NUM_VALENCES]; -} ELDATA; - -/* 2004=05-10: Added valences {1,3,5,7,} for As(2-) */ - -const ELDATA ElData[] = { -/* avg norm El No -------- Valence(s) of an ion or neutral atom -------------*/ -/* mw mass exact mw type neg H -2 -1 0 +1 +2 */ -{ "H", 1, 1, 1.007825035, 0 , 21, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -{ "D", 2, 2, 2.014101778, 0 , 21, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -{ "T", 3, 3, 3.016049268, 0 , 21, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -{ "He", 4, 4, 4.002600000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, -{ "Li", 7, 7, 7.016000000, METAL , 10, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -{ "Be", 9, 9, 9.012180000, METAL , 15, 0, {{0,}, {0,}, {2,}, {1,}, {0,} }}, -{ "B", 11, 11, 11.009300000, 0 , 20, 0, {{3,}, {4,}, {3,}, {2,}, {1,} }}, -{ "C", 12, 12, 12.000000000, 0 , 25, 0, {{2,}, {3,}, {4,}, {3,}, {2,} }}, -{ "N", 14, 14, 14.003074000, 0 , 30, 0, {{1,}, {2,}, {3,5}, {4,}, {3,} }}, -{ "O", 16, 16, 15.994914630, 0 , 35, 0, {{0,}, {1,}, {2,}, {3,5,}, {4,} }}, -{ "F", 19, 19, 18.998403220, 0 , 40, 0, {{0,}, {0,}, {1,}, {2,}, {3,5} }}, -{ "Ne", 20, 20, 19.992440000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, -{ "Na", 23, 23, 22.989770000, METAL , 9, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -{ "Mg", 24, 24, 23.985000000, METAL , 12, 0, {{0,}, {0,}, {2,}, {1,}, {0,} }}, -{ "Al", 27, 27, 26.981540000, METAL , 15, 0, {{3,5,}, {4,}, {3,}, {2,}, {1,} }}, -{ "Si", 28, 28, 27.976927100, 0 , 18, 0, {{2,}, {3,5}, {4,}, {3,}, {2,} }}, -{ "P", 31, 31, 30.973762000, 0 , 21, 0, {{1,3,5,7,}, {2,4,6,}, {3,5,}, {4,}, {3,} }}, -{ "S", 32, 32, 31.972070700, 0 , 25, 0, {{0,}, {1,3,5,7,}, {2,4,6}, {3,5,}, {4,} }}, -{ "Cl", 35, 35, 34.968852730, 0 , 30, 0, {{0,}, {0,}, {1,3,5,7}, {2,4,6}, {3,5,} }}, -{ "Ar", 40, 40, 39.962400000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, -{ "K", 39, 39, 38.963700000, METAL , 8, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -{ "Ca", 40, 40, 39.962600000, METAL , 10, 0, {{0,}, {0,}, {2,}, {1,}, {0,} }}, -{ "Sc", 45, 45, 44.955910000, METAL , 13, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Ti", 48, 48, 47.947950000, METAL , 15, 1, {{0,}, {0,}, {3,4}, {0,}, {0,} }}, -{ "V", 51, 51, 50.943960000, METAL , 16, 1, {{0,}, {0,}, {2,3,4,5,}, {0,}, {0,} }}, -{ "Cr", 52, 52, 51.940500000, METAL , 16, 1, {{0,}, {0,}, {2,3,6,}, {0,}, {0,} }}, -{ "Mn", 55, 55, 54.938050000, METAL2, 15, 1, {{0,}, {0,}, {2,3,4,6,}, {0,}, {0,} }}, -{ "Fe", 56, 56, 55.934900000, METAL2, 18, 1, {{0,}, {0,}, {2,3,4,6,}, {0,}, {0,} }}, -{ "Co", 59, 59, 58.933200000, METAL2, 18, 1, {{0,}, {0,}, {2,3,}, {0,}, {0,} }}, -{ "Ni", 59, 58, 57.935300000, METAL2, 18, 1, {{0,}, {0,}, {2,3,}, {0,}, {0,} }}, -{ "Cu", 64, 63, 62.929600000, METAL , 19, 1, {{0,}, {0,}, {1,2,}, {0,}, {0,} }}, -{ "Zn", 65, 64, 63.929147000, METAL , 16, 1, {{0,}, {0,}, {2,}, {0,}, {0,} }}, -{ "Ga", 70, 69, 68.925600000, METAL , 18, 0, {{3,5,}, {4,}, {3,}, {0,}, {1,} }}, -{ "Ge", 73, 74, 73.921177400, 0 , 18, 0, {{2,4,6,}, {3,5,}, {4,}, {3,}, {0,} }}, -{ "As", 75, 75, 74.921594200, 0 , 20, 0, {{1,3,5,7,}, {2,4,6,}, {3,5,}, {4,}, {3,} }}, -{ "Se", 79, 80, 79.916519600, 0 , 24, 0, {{0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, {4,} }}, -{ "Br", 80, 79, 78.918336100, 0 , 28, 0, {{0,}, {0,}, {1,3,5,7,}, {2,4,6,}, {3,5,} }}, -{ "Kr", 84, 84, 83.911500000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, -{ "Rb", 85, 85, 84.911800000, METAL , 8, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -{ "Sr", 88, 88, 87.905600000, METAL , 10, 0, {{0,}, {0,}, {2,}, {1,}, {0,} }}, -{ "Y", 89, 89, 88.905860000, METAL , 12, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Zr", 91, 90, 89.904700000, METAL , 14, 1, {{0,}, {0,}, {4,}, {0,}, {0,} }}, -{ "Nb", 93, 93, 92.906400000, METAL , 16, 1, {{0,}, {0,}, {3,5,}, {0,}, {0,} }}, -{ "Mo", 96, 98, 97.905400000, METAL , 18, 1, {{0,}, {0,}, {3,4,5,6,}, {0,}, {0,} }}, -{ "Tc", 98, 98, 97.907200000, METAL , 19, 1, {{0,}, {0,}, {7,}, {0,}, {0,} }}, -{ "Ru", 101, 102, 101.904300000, METAL , 22, 1, {{0,}, {0,}, {2,3,4,6,}, {0,}, {0,} }}, -{ "Rh", 103, 103, 102.905500000, METAL , 22, 1, {{0,}, {0,}, {2,3,4,}, {0,}, {0,} }}, -{ "Pd", 106, 106, 105.903500000, METAL , 22, 1, {{0,}, {0,}, {2,4,}, {0,}, {0,} }}, -{ "Ag", 108, 107, 106.905100000, METAL , 19, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -{ "Cd", 112, 114, 113.903400000, METAL , 17, 1, {{0,}, {0,}, {2,}, {0,}, {0,} }}, -{ "In", 115, 115, 114.903900000, METAL , 17, 0, {{3,5,}, {2,4,}, {3,}, {0,}, {1,} }}, -{ "Sn", 119, 120, 119.902200000, METAL2, 18, 0, {{2,4,6,}, {3,5}, {2,4,}, {3,}, {0,} }}, -{ "Sb", 122, 121, 120.903800000, METAL, 19, 0, {{1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,}, {3,} }}, -{ "Te", 128, 130, 129.906200000, 0 , 21, 0, {{0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,} }}, -{ "I", 127, 127, 126.904500000, 0 , 25, 0, {{0,}, {0,}, {1,3,5,7,}, {2,4,6}, {3,5,} }}, -{ "Xe", 131, 132, 131.904100000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, -{ "Cs", 133, 133, 132.905430000, METAL , 7, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -{ "Ba", 137, 138, 137.905200000, METAL , 9, 0, {{0,}, {0,}, {2,}, {1,}, {0,} }}, -{ "La", 139, 139, 138.906360000, METAL , 11, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Ce", 140, 140, 139.905400000, METAL2, 0, 1, {{0,}, {0,}, {3,4,}, {0,}, {0,} }}, -{ "Pr", 141, 141, 140.907660000, METAL2, 0, 1, {{0,}, {0,}, {3,4,}, {0,}, {0,} }}, -{ "Nd", 144, 142, 141.907719000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Pm", 145, 145, 144.912800000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Sm", 150, 152, 151.919700000, METAL2, 0, 1, {{0,}, {0,}, {2,3,}, {0,}, {0,} }}, -{ "Eu", 152, 153, 152.921200000, METAL2, 0, 1, {{0,}, {0,}, {2,3,}, {0,}, {0,} }}, -{ "Gd", 157, 158, 157.924099000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Tb", 159, 159, 158.925350000, METAL2, 0, 1, {{0,}, {0,}, {3,4,}, {0,}, {0,} }}, -{ "Dy", 163, 164, 163.929200000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, /* mw rounding uncertain */ -{ "Ho", 165, 165, 164.930300000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Er", 167, 166, 165.930300000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Tm", 169, 169, 168.934230000, METAL2, 0, 1, {{0,}, {0,}, {2,3,}, {0,}, {0,} }}, -{ "Yb", 173, 174, 173.938900000, METAL2, 0, 1, {{0,}, {0,}, {2,3,}, {0,}, {0,} }}, -{ "Lu", 175, 175, 174.940800000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Hf", 178, 180, 179.946600000, METAL , 13, 1, {{0,}, {0,}, {4,}, {0,}, {0,} }}, -{ "Ta", 181, 181, 180.948010000, METAL , 15, 1, {{0,}, {0,}, {5,}, {0,}, {0,} }}, -{ "W", 184, 184, 183.951000000, METAL2, 17, 1, {{0,}, {0,}, {3,4,5,6,}, {0,}, {0,} }}, -{ "Re", 186, 187, 186.955800000, METAL2, 19, 1, {{0,}, {0,}, {2,4,6,7,}, {0,}, {0,} }}, -{ "Os", 190, 192, 191.961500000, METAL2, 22, 1, {{0,}, {0,}, {2,3,4,6,}, {0,}, {0,} }}, -{ "Ir", 192, 193, 192.962900000, METAL2, 22, 1, {{0,}, {0,}, {2,3,4,6,}, {0,}, {0,} }}, -{ "Pt", 195, 195, 194.964800000, METAL2, 22, 1, {{0,}, {0,}, {2,4,}, {0,}, {0,} }}, -{ "Au", 197, 197, 196.966560000, METAL , 24, 1, {{0,}, {0,}, {1,3,}, {0,}, {0,} }}, -{ "Hg", 201, 202, 201.970617000, METAL2, 19, 1, {{0,}, {0,}, {1,2,}, {0,}, {0,} }}, -{ "Tl", 204, 205, 204.974400000, METAL2, 18, 0, {{3,5,}, {2,4,}, {1,3,}, {0,}, {0,} }}, -{ "Pb", 207, 208, 207.976627000, METAL2, 18, 0, {{2,4,6,}, {3,5}, {2,4,}, {3,}, {0,} }}, -{ "Bi", 209, 209, 208.980390000, METAL , 19, 0, {{1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,}, {3,} }}, -{ "Po", 209, 209, 208.982400000, METAL2, 20, 0, {{0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,} }}, -{ "At", 210, 210, 209.987100000, 0 , 22, 0, {{0,}, {0,}, {1,3,5,7,}, {2,4,6}, {3,5,} }}, -{ "Rn", 222, 222, 222.017500000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, -{ "Fr", 223, 223, 223.019700000, METAL , 0, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -{ "Ra", 226, 226, 226.025410000, METAL , 0, 0, {{0,}, {0,}, {2,}, {1,}, {0,} }}, -{ "Ac", 227, 227, 227.027750000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Th", 232, 232, 232.038050000, METAL2, 0, 1, {{0,}, {0,}, {3,4,}, {0,}, {0,} }}, -{ "Pa", 231, 231, 231.035880000, METAL2, 0, 1, {{0,}, {0,}, {3,4,5,}, {0,}, {0,} }}, -{ "U", 238, 238, 238.050790000, METAL2, 0, 1, {{0,}, {0,}, {3,4,5,6,}, {0,}, {0,} }}, -{ "Np", 237, 237, 237.048170000, METAL2, 0, 1, {{0,}, {0,}, {3,4,5,6,}, {0,}, {0,} }}, -{ "Pu", 244, 244, 244.064200000, METAL2, 0, 1, {{0,}, {0,}, {3,4,5,6,}, {0,}, {0,} }}, -{ "Am", 243, 243, 243.061370000, METAL2, 0, 1, {{0,}, {0,}, {3,4,5,6,}, {0,}, {0,} }}, -{ "Cm", 247, 247, 247.070300000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Bk", 247, 247, 247.070300000, METAL , 0, 1, {{0,}, {0,}, {3,4,}, {0,}, {0,} }}, -{ "Cf", 251, 251, 251.079600000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Es", 252, 252, 252.082800000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Fm", 257, 257, 257.095100000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "Md", 258, 258, 258.098600000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, -{ "No", 259, 259, 259.100900000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -{ "Lr", 260, 260, 260.105400000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -{ "Rf", 261, 261, 261.108700000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, - -/*^^^ Added in v. 1.04 */ - -/* - Reference: - M. E. WIESER AND T. B. COPLEN. - Atomic weights of the elements 2009 (IUPAC Technical Report). - Pure Appl. Chem., Vol. 83, No. 2, pp. 359–396, 2011. - When available, the mass is given for isotope with the longest half-life. -*/ -/* 105 dubnium Db */ /* ? Like: Ta */ -{ "Db", 268, 268, 268.125000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -/* 106 seaborgium Sg */ /* ? Like: W */ -{ "Sg", 271, 271, 271.133000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -/* 107 bohrium Bh */ /* ? Like: Re */ -{ "Bh", 267, 267, 267.127700000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -/* 108 hassium Hs */ /* ? Like: Os */ -{ "Hs", 277, 277, 277.150000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -/* 109 meitnerium Mt */ /* ? Like: Ir */ -{ "Mt", 276, 276, 276.151000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -/* 110 darmstadtium Ds */ /* ? Like: Pt */ -{ "Ds", 281, 281, 281.162000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -/* 111 roentgenium Rg */ /* ? Like: Au */ -{ "Rg", 280, 280, 280.164000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -/* 112 copernicium Cn */ /* ? Like: Hg */ -{ "Cn", 285, 285, 285.174000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, -/*^^^ End of added in v. 1.04 */ - -#ifdef INCHI_ZFRAG -{ "Zu", 0, 0, 0.000000000, 0 , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, //single bond fragment -{ "Zv", 0, 0, 0.000000000, 0 , 0, 1, {{0,}, {0,}, {2,}, {0,}, {0,} }}, //double bond fragment -{ "Zw", 0, 0, 0.000000000, 0 , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, //triple bond fragment -{ "Zx", 0, 0, 0.000000000, 0 , 0, 1, {{0,}, {0,}, {1,2,}, {0,}, {0,} }}, //aromatic bond fragment -#endif -{ "", 0, 0, 0.000000000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, - -}; - /* -#ifdef __cplusplus -} -#endif -*/ - -int ERR_ELEM = 255; -int nElDataLen = sizeof(ElData)/sizeof(ElData[0])-1; - -/***********************************************************************************/ -int GetElementFormulaFromAtNum(int nAtNum, char *szElement ) -{ - nAtNum -= 1; - if ( 0 < nAtNum ) - nAtNum += 2; /* bypass D, T */ - if ( 0 <= nAtNum && nAtNum < nElDataLen ) { - strcpy( szElement, ElData[nAtNum].szElName ); - return 0; - } - strcpy( szElement, "??" ); - return -1; -} -/***********************************************************************************/ -int get_el_number( const char* elname ) -{ - int i; - const char *p; - for ( i = 0; (p=ElData[i].szElName)[0] && strcmp( p, elname ); i++ ) - ; - return p[0]? i : ERR_ELEM; -} -/***********************************************************************************/ -int get_periodic_table_number( const char* elname ) -{ - int num; - num = get_el_number( elname ); - if ( num < ERR_ELEM ) - num = inchi_max(1, num-1); - return num; -} -/***********************************************************************************/ -int do_not_add_H( int nPeriodicNum ) -{ - return ElData[nPeriodicNum>1? nPeriodicNum+1:0].bDoNotAddH; -} -/***********************************************************************************/ -int get_el_valence( int nPeriodicNum, int charge, int val_num ) -{ - if ( charge < MIN_ATOM_CHARGE || charge > MAX_ATOM_CHARGE || val_num >= MAX_NUM_VALENCES ) - return 0; - return ElData[nPeriodicNum>1? nPeriodicNum+1:0].cValence[NEUTRAL_STATE+charge][val_num]; -} -/*********************************************************************************** - * output valence needed to unumbiguosly reconstruct bonds - ***********************************************************************************/ -int get_unusual_el_valence( int nPeriodicNum, int charge, int radical, int bonds_valence, int num_H, int num_bonds ) -{ - int i, num_found, chem_valence, rad_adj, known_chem_valence, exact_found; - if ( !num_bonds && !num_H ) - return 0; - if ( charge < MIN_ATOM_CHARGE || charge > MAX_ATOM_CHARGE ) { - if ( bonds_valence == num_bonds ) - return 0; /* all single bonds */ - return bonds_valence; - } - if ( !get_el_valence( nPeriodicNum, charge, 0 ) && bonds_valence == num_bonds ) - return 0; - - chem_valence = bonds_valence + num_H; - rad_adj = 0; - num_found = 0; - exact_found = 0; - - /* take into account radical */ - if (radical==RADICAL_DOUBLET) - rad_adj = 1; - else - if (radical==RADICAL_TRIPLET ) - rad_adj = 2; - - for ( i = 0; i < MAX_NUM_VALENCES; i ++ ) { - if ( 0 < (known_chem_valence = get_el_valence( nPeriodicNum, charge, i )-rad_adj) && - num_bonds <= known_chem_valence && known_chem_valence <= chem_valence ) { - num_found ++; - if ( known_chem_valence == chem_valence ) { - exact_found = 1; - break; - } - } - } - return (exact_found && 1 == num_found)? 0 : chem_valence; -} -/*********************************************************************************** - * output valence needed to unumbiguosly reconstruct number of H - ***********************************************************************************/ -int needed_unusual_el_valence( int nPeriodicNum, int charge, int radical, int bonds_valence, - int actual_bonds_valence, int num_H, int num_bonds ) -{ - int i, num_found, num_found_known, chem_valence, rad_adj, known_chem_valence, exact_found; - int num_H_expected; - char szElement[4]; - /* - if ( !num_bonds && !num_H ) - return 0; - */ - if ( num_bonds && !GetElementFormulaFromAtNum(nPeriodicNum, szElement ) ) { - num_H_expected = get_num_H( szElement, 0, NULL, charge, radical, actual_bonds_valence, 0,0,0,0 ); - } else { - num_H_expected = num_H; - } - - chem_valence = bonds_valence + num_H; - if ( charge < MIN_ATOM_CHARGE || charge > MAX_ATOM_CHARGE || - !get_el_valence( nPeriodicNum, charge, 0 ) || - do_not_add_H( nPeriodicNum ) || bonds_valence != actual_bonds_valence || - num_H_expected != num_H ) { - if ( !num_H && !num_H_expected && bonds_valence == actual_bonds_valence ) - return 0; /* no H */ - return chem_valence; /* needs to add H-atoms */ - } - - /* take into account radical */ - if (radical==RADICAL_DOUBLET) - rad_adj = 1; - else - if (radical==RADICAL_TRIPLET ) - rad_adj = 2; - else - rad_adj = 0; - - num_found_known = 0; - num_found = 0; - exact_found = 0; - - for ( i = 0; i < MAX_NUM_VALENCES; i ++ ) { - if ( 0 < (known_chem_valence = get_el_valence( nPeriodicNum, charge, i )) && - bonds_valence <= (known_chem_valence -= rad_adj) ) { - /* found known valence that fits without H */ - num_found_known ++; - if ( known_chem_valence <= chem_valence ) { - /* known valence is large enough to accommodate (implicit) H */ - num_found ++; - } - if ( known_chem_valence == chem_valence ) { - exact_found = 1; - break; - } - } - } - return (exact_found && 1 == num_found && 1 == num_found_known)? 0 : chem_valence? chem_valence : -1 /* needs zero */; -} -/*********************************************************************************** - * output valence that does not fit any known valences - ***********************************************************************************/ -int detect_unusual_el_valence( int nPeriodicNum, int charge, int radical, int bonds_valence, int num_H, int num_bonds ) -{ - int i, chem_valence, rad_adj, known_chem_valence; - - if ( !num_bonds && !num_H ) - return 0; - - if ( charge < MIN_ATOM_CHARGE || charge > MAX_ATOM_CHARGE ) { - if ( bonds_valence == num_bonds ) - return 0; /* all single bonds */ - return bonds_valence; - } - if ( !get_el_valence( nPeriodicNum, charge, 0 ) && bonds_valence == num_bonds ) - return 0; - - chem_valence = bonds_valence + num_H; - rad_adj = 0; - - /* take into account radical */ - if (radical==RADICAL_DOUBLET) - rad_adj = 1; - else - if (radical==RADICAL_TRIPLET || radical==RADICAL_SINGLET ) - rad_adj = 2; - - for ( i = 0; i < MAX_NUM_VALENCES; i ++ ) { - if ( 0 < (known_chem_valence = get_el_valence( nPeriodicNum, charge, i )-rad_adj) ) { - if ( known_chem_valence == chem_valence ) { - return 0; - } - } - } - return chem_valence; -} -/***********************************************************************************/ -int get_el_type( int nPeriodicNum ) -{ - return ElData[nPeriodicNum+1].nType; -} -/***********************************************************************************/ -int is_el_a_metal( int nPeriodicNum ) -{ - return 0!=(ElData[nPeriodicNum+1].nType & IS_METAL); -} -/******************************************************************************************************/ -/*#ifndef TARGET_API_LIB*/ -int extract_ChargeRadical( char *elname, int *pnRadical, int *pnCharge ) -{ - char *q, *r, *p; - int nCharge=0, nRad = 0, charge_len = 0, k, nVal, nSign, nLastSign=1, len; - - p = elname; - - /* extract radicals & charges */ - while ( q = strpbrk( p, "+-^" ) ) { - switch ( *q ) { - case '+': - case '-': - for ( k = 0, nVal=0; (nSign = ('+' == q[k])) || (nSign = -('-' == q[k])); k++ ) { - nVal += (nLastSign = nSign); - charge_len ++; - } - if ( nSign = (int)strtol( q+k, &r, 10 ) ) { /* fixed 12-5-2001 */ - nVal += nLastSign * (nSign-1); - } - charge_len = r - q; - nCharge += nVal; - break; - /* case '.': */ /* singlet '.' may be confused with '.' in formulas like CaO.H2O */ - case '^': - nRad = 1; /* doublet here is 1. See below */ - charge_len = 1; - for ( k = 1; q[0] == q[k]; k++ ) { - nRad ++; - charge_len ++; - } - break; - } - memmove( q, q+charge_len, strlen(q+charge_len)+1 ); - } - len = (int) strlen(p); - /* radical */ - if ( (q = strrchr( p, ':' )) && !q[1]) { - nRad = RADICAL_SINGLET; - q[0] = '\0'; - len --; - } else { - while( (q = strrchr( p, '.' )) && !q[1] ) { - nRad ++; - q[0] = '\0'; - len --; - } - - nRad = nRad == 1? RADICAL_DOUBLET : - nRad == 2? RADICAL_TRIPLET : 0; - } - *pnRadical = nRad; - *pnCharge = nCharge; - return ( nRad || nCharge ); - -} -/*#endif*/ -/****************************************************************/ -int extract_H_atoms( char *elname, S_CHAR num_iso_H[] ) -{ - int i, len, c, k, num_H, val; - char *q; - i = 0; - num_H = 0; - len = (int)strlen(elname); - c = UCINT elname[0]; - while ( i < len ) { - switch ( c ) { - case 'H': - k = 0; - break; - case 'D': - k = 1; - break; - case 'T': - k = 2; - break; - default: - k = -1; - break; - } - q = elname+i+1; /* pointer to the next to elname[i] character */ - c = UCINT q[0]; - if ( k >= 0 && !islower( c ) ) { - /* found a hydrogen */ - if ( isdigit( c ) ) { - val = (int)strtol( q, &q, 10 ); - /* q = pointer to the next to number of hydrogen atom(s) character */ - } else { - val = 1; - } - if ( k ) { - num_iso_H[k] += val; - } else { - num_H += val; - } - /* remove the hydrogen atom from the string */ - len -= (q-elname)-i; - memmove( elname+i, q, len + 1 ); - /* c = UCINT elname[i]; */ - } else { - i ++; - } - c = UCINT elname[i]; /* moved here 11-04-2002 */ - } - return num_H; -} -/***********************************************************************************/ -int get_num_H (const char* elname, int inp_num_H, S_CHAR inp_num_iso_H[], - int charge, int radical, int chem_bonds_valence, int atom_input_valence, - int bAliased, int bDoNotAddH, int bHasMetalNeighbor ) -{ - int val, i, el_number, num_H = 0, num_iso_H; - static int el_number_N = 0, el_number_S, el_number_O, el_number_C; - if ( !el_number_N ) { - el_number_N = get_el_number( "N" ); - el_number_S = get_el_number( "S" ); - el_number_O = get_el_number( "O" ); - el_number_C = get_el_number( "C" ); - } - /* atom_input_valence (cValence) cannot be specified in case of */ - /* aliased MOLFile atom with known inp_num_H or inp_num_iso_H[] */ - if ( bAliased ) { - num_H = inp_num_H; - } else - if ( atom_input_valence && (atom_input_valence !=15 || chem_bonds_valence) ) { - num_H = inchi_max( 0, atom_input_valence - chem_bonds_valence ); - } else - if ( atom_input_valence == 15 && !chem_bonds_valence ) { - num_H = 0; - } else - if ( MIN_ATOM_CHARGE <= charge && - MAX_ATOM_CHARGE >= charge && - ERR_ELEM != (el_number = get_el_number( elname ) ) && - !ElData[el_number].bDoNotAddH && !bDoNotAddH ) { - /* add hydrogen atoms according to standard element valence */ - if ( radical && radical != RADICAL_SINGLET ) { - if ( val = ElData[el_number].cValence[NEUTRAL_STATE+charge][0] ) { - val -= (radical==RADICAL_DOUBLET)? 1 : - (radical==RADICAL_SINGLET || radical==RADICAL_TRIPLET )? 2 : val; - /* if unknown radical then do not add H */ - num_H = inchi_max( 0, val - chem_bonds_valence ); - } - } else { - /* find the smallest valence that is greater than the sum of the chemical bond valences */ - for ( i = 0; - (val=ElData[el_number].cValence[NEUTRAL_STATE+charge][i]) && - val < chem_bonds_valence; - i++ ) - ; - /* special case: do not add H to N(IV), S(III), S+(II), S-(II) */ /* S ions added 2004-05-10 */ - if ( el_number == el_number_N && !charge && !radical && val == 5 ) - val = 3; - else - /* - if ( el_number == el_number_N && !charge && !radical && val == 3 && - chem_bonds_valence == 2 && bHasMetalNeighbor ) - val = 2; - else - */ - if ( el_number == el_number_S && !charge && !radical && val == 4 && chem_bonds_valence == 3 ) - val = 3; - else - if ( bHasMetalNeighbor && el_number != el_number_C && val > 0 ) { - val --; - } - /* - if ( (el_number == el_number_S || el_number == el_number_O) && - abs(charge)==1 && !radical && val == 3 && chem_bonds_valence == 2 && bHasMetalNeighbor ) - val = 2; - else - */ - num_H = inchi_max( 0, val - chem_bonds_valence ); - } - num_iso_H = 0; - if ( inp_num_iso_H ) { - for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) { - num_iso_H += inp_num_iso_H[i]; - } - } - /* should not happen because atom here is not aliased */ - if ( num_iso_H ) { - if ( num_H >= num_iso_H ) { - num_H -= num_iso_H; - } else { - num_H = inp_num_H; /* as requested in the alias */ - /* num_H = (num_iso_H - num_H) % 2; */ /* keep unchanged parity of the total number of H atoms */ - } - } - /* should not happen because atom here is not aliased */ - if ( inp_num_H > num_H ) { - num_H = inp_num_H; /* as requested in the alias */ - /* num_H = inp_num_H + (inp_num_H - num_H)%2; */ /* keep unchanged parity of the number of non-isotopic H atoms */ - } - } else { - num_H = inp_num_H; - } - return num_H; -} -/***********************************************************************************/ -int get_atw_from_elnum( int nAtNum ) -{ - nAtNum -= 1; - if ( 0 < nAtNum ) - nAtNum += 2; /* bypass D, T */ - if ( 0 <= nAtNum && nAtNum < nElDataLen ) { - return (int)ElData[nAtNum].nAtMass; - } - return 0; -} -/***********************************************************************************/ -/* -int get_mw(char elname[]) -{ - int i; - - for (i=0; i 0 ) { - memmove( (void*) &name[i-n], (void*) &name[i], len-i+1 ); - i -= n; - len -= n; - } - n = -1; - } - } - if ( n == len ) /* empty line */ - name[len=0] = '\0'; - else - if ( ++n && n <= len ) { - len -= n; - name[len] = '\0'; - } - return len; -} -#endif /* ifndef TARGET_API_LIB */ -/************************************************/ -#ifndef inchi_malloc -void *inchi_malloc(size_t c) -{ - return malloc(c); -} -#endif -#ifndef inchi_calloc -void *inchi_calloc(size_t c, size_t n) -{ - return calloc(c,n); -} -#endif -#ifndef inchi_free -void inchi_free(void *p) -{ - if(p) { - free(p); /*added check if zero*/ - } -} -#endif - - - - - -#ifndef TARGET_API_LIB -/*************************************************************************/ -void remove_trailing_spaces( char* p ) -{ - int len; - for( len = (int)strlen( p ) - 1; len >= 0 && isspace( UCINT p[len] ); len-- ) - ; - p[++len] = '\0'; -} -/*************************************************************************/ -void remove_one_lf( char* p) -{ - size_t len; - if ( p && 0 < (len = strlen(p)) && p[len-1] == '\n' ){ - p[len-1] = '\0'; - if ( len >= 2 && p[len-2] == '\r' ) - p[len-2] = '\0'; - } -} -#endif /* ifndef TARGET_API_LIB */ - - - -/***************************************************************************/ -/* Copies up to maxlen characters INCLUDING end null from source to target */ -/* Fills out the rest of the target with null bytes */ -int mystrncpy(char *target,const char *source,unsigned maxlen) -{ /* protected from non-zero-terminated source and overlapped target/source. 7-9-99 DCh. */ - const char *p; - unsigned len; - - if (target==NULL || maxlen == 0 || source == NULL) - return 0; - if ( p = (const char*)memchr(source, 0, maxlen) ) { - len = p-source; /* maxlen does not include the found zero termination */ - } else { - len = maxlen-1; /* reduced length does not include one more byte for zero termination */ - } - if ( len ) - memmove( target, source, len ); - /* target[len] = '\0'; */ - memset( target+len, 0, maxlen-len); /* zero termination */ - return 1; -} -/************************************************************************/ -/* Remove leading and trailing white spaces */ -char* LtrimRtrim( char *p, int* nLen ) -{ - int i, len=0; - if ( p && (len = (int) strlen( p )) ) { - for ( i = 0; i < len && __isascii( p[i] ) && isspace( p[i] ); i++ ) - ; - if ( i ) - (memmove)( p, p+i, (len -= i)+1 ); - for ( ; 0 < len && __isascii( p[len-1] ) && isspace( p[len-1] ); len-- ) - ; - p[len] = '\0'; - } - if ( nLen ) - *nLen = len; - return p; -} -/*************************************************************************/ -AT_NUMB *is_in_the_list( AT_NUMB *pathAtom, AT_NUMB nNextAtom, int nPathLen ) -{ - for ( ; nPathLen && *pathAtom != nNextAtom; nPathLen--, pathAtom++ ) - ; - return nPathLen? pathAtom : NULL; -} -/******************************************************************************************************/ -int nBondsValToMetal( inp_ATOM* at, int iat ) -{ - int i, neigh, bond_type, nVal2Metal = 0; - inp_ATOM* a = at + iat; - for ( i = 0; i < a->valence; i ++ ) { - neigh = a->neighbor[i]; - if ( is_el_a_metal( at[(int)a->neighbor[i]].el_number ) ) { - bond_type = a->bond_type[i]; - if ( bond_type <= BOND_TYPE_TRIPLE ) { - nVal2Metal += bond_type; - } else { - return -1; /* bond to metal order is not well defined */ - } - } - } - return nVal2Metal; -} -/************************************************************************/ -int num_of_H( inp_ATOM *at, int iat ) -{ - static int el_number_H; - int i, n, num_explicit_H = 0; - inp_ATOM *a = at + iat; - if ( !el_number_H ) - el_number_H = get_periodic_table_number( "H" ); - for ( i = 0; i < a->valence; i ++ ) { - n = a->neighbor[i]; - num_explicit_H += ( 1 == at[n].valence && el_number_H == at[n].el_number ); - } - return num_explicit_H+NUMH(at,iat); -} -/************************************************************************/ -int has_other_ion_neigh( inp_ATOM *at, int iat, int iat_ion_neigh, const char *el, int el_len ) -{ - int charge = at[iat_ion_neigh].charge; - int i, neigh; - for ( i = 0; i < at[iat].valence; i ++ ) { - neigh = at[iat].neighbor[i]; - if ( neigh != iat_ion_neigh && at[neigh].charge == charge && - NULL != memchr( el, at[neigh].el_number, el_len ) ) { - return 1; - } - } - return 0; -} -/************************************************************************/ -/* BFS r=2 */ -int has_other_ion_in_sphere_2(inp_ATOM *at, int iat, int iat_ion_neigh, const char *el, int el_len ) -{ -#define MAXQ 16 - AT_NUMB q[MAXQ]; - int lenq=0, lenq2, dist = 0, i = 0, iq, neigh, j, nRet=0; - q[lenq++] = iat; - at[iat].cFlags = 1; - - iq = 0; - dist = 1; - /* use at->cFlags as an indicator */ - while ( dist <= 2 ) { - for ( lenq2 = lenq; iq < lenq2; iq ++ ) { - i = q[iq]; - for ( j = 0; j < at[i].valence; j ++ ) { - neigh = at[i].neighbor[j]; - if ( !at[neigh].cFlags && - at[neigh].valence <= 3 && - NULL != memchr( el, at[neigh].el_number, el_len ) ) { - q[lenq ++] = neigh; - at[neigh].cFlags = 1; - if ( neigh != iat_ion_neigh && - at[iat_ion_neigh].charge == at[neigh].charge ) { - nRet ++; - } - } - } - } - dist ++; - } - for ( iq = 0; iq < lenq; iq ++ ) { - i = q[iq]; - at[i].cFlags = 0; - } - return nRet; -} -/************************************************************************/ -int nNoMetalNumBonds( inp_ATOM *at, int at_no ) -{ - inp_ATOM *a = at + at_no; - int num_H = NUMH(a, 0); - int std_chem_bonds_valence = get_el_valence( a->el_number, a->charge, 0 ); - int i; - if ( a->chem_bonds_valence + num_H > std_chem_bonds_valence ) { - int valence_to_metal = 0; - int num_bonds_to_metal = 0; - for ( i = 0; i < a->valence; i ++ ) { - if ( is_el_a_metal( at[(int)a->neighbor[i]].el_number ) ) { - if ( (a->bond_type[i] & BOND_TYPE_MASK) >= BOND_TYPE_ALTERN ) { - return a->valence; /* fall back */ - } - num_bonds_to_metal ++; - valence_to_metal += (a->bond_type[i] & BOND_TYPE_MASK); - } - } - if ( a->chem_bonds_valence + num_H - valence_to_metal == std_chem_bonds_valence ) { - /* removing bonds to metal produces standard valence */ - return a->valence - num_bonds_to_metal; - } - } -#if ( S_VI_O_PLUS_METAL_FIX_BOND == 1 ) - else - if ( 1 == a->charge && 2 == get_endpoint_valence(a->el_number) && - a->chem_bonds_valence + num_H == std_chem_bonds_valence ) { - int valence_to_metal = 0; - int num_bonds_to_metal = 0; - for ( i = 0; i < a->valence; i ++ ) { - if ( is_el_a_metal( at[(int)a->neighbor[i]].el_number ) ) { - if ( (a->bond_type[i] & BOND_TYPE_MASK) >= BOND_TYPE_ALTERN ) { - return a->valence; /* fall back */ - } - num_bonds_to_metal ++; - valence_to_metal += (a->bond_type[i] & BOND_TYPE_MASK); - } - } - if ( 1 == valence_to_metal ) { - /* removing bonds to metal produces standard valence */ - return a->valence - num_bonds_to_metal; - } - } -#endif - - return a->valence; -} -/************************************************************************/ -int nNoMetalBondsValence( inp_ATOM *at, int at_no ) -{ - inp_ATOM *a = at + at_no; - int num_H = NUMH(a, 0); - int std_chem_bonds_valence = get_el_valence( a->el_number, a->charge, 0 ); - int i; - if ( a->chem_bonds_valence + num_H > std_chem_bonds_valence ) { - int valence_to_metal = 0; - /*int num_bonds_to_metal = 0;*/ - for ( i = 0; i < a->valence; i ++ ) { - if ( is_el_a_metal( at[(int)a->neighbor[i]].el_number ) ) { - if ( (a->bond_type[i] & BOND_TYPE_MASK) >= BOND_TYPE_ALTERN ) { - return a->valence; /* fall back */ - } - /*num_bonds_to_metal ++;*/ - valence_to_metal += (a->bond_type[i] & BOND_TYPE_MASK); - } - } - if ( a->chem_bonds_valence + num_H - valence_to_metal == std_chem_bonds_valence ) { - /* removing bonds to metal produces standard valence */ - return a->chem_bonds_valence - valence_to_metal; - } - } -#if ( S_VI_O_PLUS_METAL_FIX_BOND == 1 ) - else - if ( 1 == a->charge && 2 == get_endpoint_valence(a->el_number) && - a->chem_bonds_valence + num_H == std_chem_bonds_valence ) { - int valence_to_metal = 0; - /*int num_bonds_to_metal = 0;*/ - for ( i = 0; i < a->valence; i ++ ) { - if ( is_el_a_metal( at[(int)a->neighbor[i]].el_number ) ) { - if ( (a->bond_type[i] & BOND_TYPE_MASK) >= BOND_TYPE_ALTERN ) { - return a->valence; /* fall back */ - } - /*num_bonds_to_metal ++;*/ - valence_to_metal += (a->bond_type[i] & BOND_TYPE_MASK); - } - } - if ( 1 == valence_to_metal ) { - /* removing bonds to metal produces standard valence */ - return a->chem_bonds_valence - valence_to_metal; - } - } -#endif - return a->chem_bonds_valence; -} -/************************************************************************/ -int nNoMetalNeighIndex( inp_ATOM *at, int at_no ) -{ - inp_ATOM *a = at + at_no; - int i; - for ( i = 0; i < a->valence; i ++ ) { - if ( !is_el_a_metal( at[(int)a->neighbor[i]].el_number ) ) { - return i; - } - } - return -1; -} -/************************************************************************/ -int nNoMetalOtherNeighIndex( inp_ATOM *at, int at_no, int cur_neigh ) -{ - inp_ATOM *a = at + at_no; - int i, neigh; - for ( i = 0; i < a->valence; i ++ ) { - neigh = (int)a->neighbor[i]; - if ( neigh != cur_neigh && !is_el_a_metal( at[neigh].el_number ) ) { - return i; - } - } - return -1; -} -/************************************************************************/ -int nNoMetalOtherNeighIndex2( inp_ATOM *at, int at_no, int cur_neigh, int cur_neigh2 ) -{ - inp_ATOM *a = at + at_no; - int i, neigh; - for ( i = 0; i < a->valence; i ++ ) { - neigh = (int)a->neighbor[i]; - if ( neigh != cur_neigh && neigh != cur_neigh2 && !is_el_a_metal( at[neigh].el_number ) ) { - return i; - } - } - return -1; -} - - -#ifndef COMPILE_ANSI_ONLY -/**************************************************************************/ -int MakeRemovedProtonsString( int nNumRemovedProtons, NUM_H *nNumExchgIsotopicH, NUM_H *nNumRemovedProtonsIsotopic, - int bIsotopic, char *szRemovedProtons, int *num_removed_iso_H ) -{ - int i, j, len, num; - len = 0; - if ( nNumRemovedProtons ) { - len = sprintf ( szRemovedProtons, "Proton balance: %c %d H+", - nNumRemovedProtons>=0? '+':'-', abs(nNumRemovedProtons) ); - } - if ( bIsotopic && (nNumRemovedProtonsIsotopic || nNumExchgIsotopicH) ) { - for ( i = 0, j = 0; i < NUM_H_ISOTOPES; i ++ ) { - num = (nNumExchgIsotopicH? nNumExchgIsotopicH[i]:0) + - (nNumRemovedProtonsIsotopic? nNumRemovedProtonsIsotopic[i]:0); - if ( num ) { - len += sprintf( szRemovedProtons+len, "%s %d^%dH", - j? ", ":" [ removed ", num, i+1); - j ++; - } - } - if ( j ) { - len += sprintf( szRemovedProtons+len, " ]" ); - if ( num_removed_iso_H ) - *num_removed_iso_H = j; - } - } - if ( !len ) { - szRemovedProtons[0] = '\0'; - } - return len; -} -#endif - -/* - According to - http://info-uri.info/registry/OAIHandler?verb=GetRecord&metadataPrefix=reg&identifier=info:inchi/ - - An InChI identifier may contain the following characters: - - A-Z - a-z - 0-9 - ()*+,-./;=?@ - - - Here we consider any character not conforming this specification as a whitespace - which marks the end of the InChI string. - For example: - "InChI=1/Ar%" - "InChI=1/Ar\n" - "InChI=1/Ar\r\t" - all will be trimmed to - "InChI=1/Ar" - -*/ - -/**************************************************************************/ -void extract_inchi_substring(char ** buf, const char *str, size_t slen) -{ -size_t i; -char *p, pp; - - *buf = NULL; - if (str==NULL) - return; - if (strlen(str)<1) - return; - - p = strstr(str, "InChI="); - if (NULL==p) - return; - - for (i=0; i= 'A' && pp <='Z') continue; - if (pp >= 'a' && pp <='z') continue; - if (pp >= '0' && pp <='9') continue; - switch ( pp ) - { - case '(': - case ')': - case '*': - case '+': - case ',': - case '-': - case '.': - case '/': - case ';': - case '=': - case '?': - case '@': continue; - - default: break; - } - break; - } - - *buf = (char*) inchi_calloc(i+1, sizeof(char)); - memcpy(*buf, p, i); - (*buf)[i] = '\0'; - - return; -} - - - - - -#ifdef COMPILE_ANSI_ONLY -/*************************************************************************/ -/************* non-ANSI functions ****************/ -/*************************************************************************/ -#define __MYTOLOWER(c) ( ((c) >= 'A') && ((c) <= 'Z') ? ((c) - 'A' + 'a') : (c) ) - -#if ( defined(COMPILE_ADD_NON_ANSI_FUNCTIONS) || defined(__STDC__) && __STDC__ == 1 ) -/* support (VC++ Language extensions) = OFF && defined(COMPILE_ANSI_ONLY) */ -int memicmp ( const void * p1, const void * p2, size_t length ) -{ - const U_CHAR *s1 = (const U_CHAR*)p1; - const U_CHAR *s2 = (const U_CHAR*)p2; - while ( length-- ) { - if ( *s1 == *s2 || - __MYTOLOWER( (int)*s1 ) == __MYTOLOWER( (int)*s2 )) { - s1 ++; - s2 ++; - } else { - return __MYTOLOWER( (int)*s1 ) - __MYTOLOWER( (int)*s2 ); - } - } - return 0; -} -/*************************************************************************/ -int stricmp( const char *s1, const char *s2 ) -{ - while ( *s1 ) { - if ( *s1 == *s2 || - __MYTOLOWER( (int)*s1 ) == __MYTOLOWER( (int)*s2 )) { - s1 ++; - s2 ++; - } else { - return __MYTOLOWER( (int)*s1 ) - __MYTOLOWER( (int)*s2 ); - } - } - if ( *s2 ) - return -1; - return 0; -} -/*************************************************************************/ -char *_strnset( char *s, int val, size_t length ) -{ - char *ps = s; - while (length-- && *ps) - *ps++ = (char)val; - return s; -} -/*************************************************************************/ -char *_strdup( const char *string ) -{ - char *p = NULL; - if ( string ) { - size_t length = strlen( string ); - p = (char *) inchi_malloc( length + 1 ); - if ( p ) { - strcpy( p, string ); - } - } - return p; -} -#endif -#endif - - +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + + +#include +#include +#include + +#include "mode.h" + +#if defined(COMPILE_ANSI_ONLY) && defined(__APPLE__) +/* For build under OSX, advice from Burt Leland */ +#include "ichicomp.h" /* Needed for __isascii define */ +#endif + +#include "util.h" +#include "extr_ct.h" + + + +#define MIN_ATOM_CHARGE (-2) +#define MAX_ATOM_CHARGE 2 +#define NEUTRAL_STATE (-MIN_ATOM_CHARGE) +#define NUM_ATOM_CHARGES (MAX_ATOM_CHARGE - MIN_ATOM_CHARGE + 1) +#define MAX_NUM_VALENCES 5 /* max. number + 1 to provide zero termination */ + + + +/* + Local +*/ + +int el_number_in_internal_ref_table( const char* elname ); + + +/* CHEMICAL ELEMENTS & ATOMIC VALENCE MODEL + FOR VARIOUS OXIDATION STATES) +*/ +typedef struct tagElData +{ + /* Element chemical symbol */ + const char *szElName; + /* Average atomic mass from the Periodic Chart of the Elements + (Fisher cat. no. 05-702-10) */ + int nAtMass; + /* (not used currently) Atomic mass of the most abundant isotope */ + int nNormAtMass; + /* (not used currently) Exact mw of the most abundant isotope (not used) */ + double dAtMass; + /* METAL or METAL2 */ + int nType; + /* (not used currently) Pauling electronegativity x 10; 0 means unknown */ + int nElNegPauling10; + /* InChI does not add implicit H to atoms that have non-zero bSkipAddingH */ + /* NB: was called bDoNotAddH, renamed to avoid confusion with other procedures */ + int bSkipAddingH; + S_CHAR cValence[NUM_ATOM_CHARGES][MAX_NUM_VALENCES]; +} ELDATA; + + +const ELDATA ElData[] = +{ +/* avg norm El No -------- Valence(s) of an ion or neutral atom -------------*/ +/* mw mass exact mw type neg H -2 -1 0 +1 +2 */ +{ "H", 1, 1, 1.007825035, 0 , 21, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +{ "D", 2, 2, 2.014101778, 0 , 21, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +{ "T", 3, 3, 3.016049268, 0 , 21, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +{ "He", 4, 4, 4.002600000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, +{ "Li", 7, 7, 7.016000000, METAL , 10, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +{ "Be", 9, 9, 9.012180000, METAL , 15, 0, {{0,}, {0,}, {2,}, {1,}, {0,} }}, +{ "B", 11, 11, 11.009300000, 0 , 20, 0, {{3,}, {4,}, {3,}, {2,}, {1,} }}, +{ "C", 12, 12, 12.000000000, 0 , 25, 0, {{2,}, {3,}, {4,}, {3,}, {2,} }}, +{ "N", 14, 14, 14.003074000, 0 , 30, 0, {{1,}, {2,}, {3,5}, {4,}, {3,} }}, +{ "O", 16, 16, 15.994914630, 0 , 35, 0, {{0,}, {1,}, {2,}, {3,5,}, {4,} }}, +{ "F", 19, 19, 18.998403220, 0 , 40, 0, {{0,}, {0,}, {1,}, {2,}, {3,5} }}, +{ "Ne", 20, 20, 19.992440000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, +{ "Na", 23, 23, 22.989770000, METAL , 9, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +{ "Mg", 24, 24, 23.985000000, METAL , 12, 0, {{0,}, {0,}, {2,}, {1,}, {0,} }}, +{ "Al", 27, 27, 26.981540000, METAL , 15, 0, {{3,5,}, {4,}, {3,}, {2,}, {1,} }}, +{ "Si", 28, 28, 27.976927100, 0 , 18, 0, {{2,}, {3,5}, {4,}, {3,}, {2,} }}, +{ "P", 31, 31, 30.973762000, 0 , 21, 0, {{1,3,5,7,}, {2,4,6,}, {3,5,}, {4,}, {3,} }}, +{ "S", 32, 32, 31.972070700, 0 , 25, 0, {{0,}, {1,3,5,7,}, {2,4,6}, {3,5,}, {4,} }}, +{ "Cl", 35, 35, 34.968852730, 0 , 30, 0, {{0,}, {0,}, {1,3,5,7}, {2,4,6}, {3,5,} }}, +{ "Ar", 40, 40, 39.962400000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, +{ "K", 39, 39, 38.963700000, METAL , 8, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +{ "Ca", 40, 40, 39.962600000, METAL , 10, 0, {{0,}, {0,}, {2,}, {1,}, {0,} }}, +{ "Sc", 45, 45, 44.955910000, METAL , 13, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, +{ "Ti", 48, 48, 47.947950000, METAL , 15, 1, {{0,}, {0,}, {3,4}, {0,}, {0,} }}, +{ "V", 51, 51, 50.943960000, METAL , 16, 1, {{0,}, {0,}, {2,3,4,5,}, {0,}, {0,} }}, +{ "Cr", 52, 52, 51.940500000, METAL , 16, 1, {{0,}, {0,}, {2,3,6,}, {0,}, {0,} }}, +{ "Mn", 55, 55, 54.938050000, METAL2, 15, 1, {{0,}, {0,}, {2,3,4,6,}, {0,}, {0,} }}, +{ "Fe", 56, 56, 55.934900000, METAL2, 18, 1, {{0,}, {0,}, {2,3,4,6,}, {0,}, {0,} }}, +{ "Co", 59, 59, 58.933200000, METAL2, 18, 1, {{0,}, {0,}, {2,3,}, {0,}, {0,} }}, +{ "Ni", 59, 58, 57.935300000, METAL2, 18, 1, {{0,}, {0,}, {2,3,}, {0,}, {0,} }}, +{ "Cu", 64, 63, 62.929600000, METAL , 19, 1, {{0,}, {0,}, {1,2,}, {0,}, {0,} }}, +{ "Zn", 65, 64, 63.929147000, METAL , 16, 1, {{0,}, {0,}, {2,}, {0,}, {0,} }}, +{ "Ga", 70, 69, 68.925600000, METAL , 18, 0, {{3,5,}, {4,}, {3,}, {0,}, {1,} }}, +{ "Ge", 73, 74, 73.921177400, 0 , 18, 0, {{2,4,6,}, {3,5,}, {4,}, {3,}, {0,} }}, +{ "As", 75, 75, 74.921594200, 0 , 20, 0, {{1,3,5,7,}, {2,4,6,}, {3,5,}, {4,}, {3,} }}, +{ "Se", 79, 80, 79.916519600, 0 , 24, 0, {{0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, {4,} }}, +{ "Br", 80, 79, 78.918336100, 0 , 28, 0, {{0,}, {0,}, {1,3,5,7,}, {2,4,6,}, {3,5,} }}, +{ "Kr", 84, 84, 83.911500000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, +{ "Rb", 85, 85, 84.911800000, METAL , 8, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +{ "Sr", 88, 88, 87.905600000, METAL , 10, 0, {{0,}, {0,}, {2,}, {1,}, {0,} }}, +{ "Y", 89, 89, 88.905860000, METAL , 12, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, +{ "Zr", 91, 90, 89.904700000, METAL , 14, 1, {{0,}, {0,}, {4,}, {0,}, {0,} }}, +{ "Nb", 93, 93, 92.906400000, METAL , 16, 1, {{0,}, {0,}, {3,5,}, {0,}, {0,} }}, +{ "Mo", 96, 98, 97.905400000, METAL , 18, 1, {{0,}, {0,}, {3,4,5,6,}, {0,}, {0,} }}, +{ "Tc", 98, 98, 97.907200000, METAL , 19, 1, {{0,}, {0,}, {7,}, {0,}, {0,} }}, +{ "Ru", 101, 102, 101.904300000, METAL , 22, 1, {{0,}, {0,}, {2,3,4,6,}, {0,}, {0,} }}, +{ "Rh", 103, 103, 102.905500000, METAL , 22, 1, {{0,}, {0,}, {2,3,4,}, {0,}, {0,} }}, +{ "Pd", 106, 106, 105.903500000, METAL , 22, 1, {{0,}, {0,}, {2,4,}, {0,}, {0,} }}, +{ "Ag", 108, 107, 106.905100000, METAL , 19, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +{ "Cd", 112, 114, 113.903400000, METAL , 17, 1, {{0,}, {0,}, {2,}, {0,}, {0,} }}, +{ "In", 115, 115, 114.903900000, METAL , 17, 0, {{3,5,}, {2,4,}, {3,}, {0,}, {1,} }}, +{ "Sn", 119, 120, 119.902200000, METAL2, 18, 0, {{2,4,6,}, {3,5}, {2,4,}, {3,}, {0,} }}, +{ "Sb", 122, 121, 120.903800000, METAL, 19, 0, {{1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,}, {3,} }}, +{ "Te", 128, 130, 129.906200000, 0 , 21, 0, {{0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,} }}, +{ "I", 127, 127, 126.904500000, 0 , 25, 0, {{0,}, {0,}, {1,3,5,7,}, {2,4,6}, {3,5,} }}, +{ "Xe", 131, 132, 131.904100000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, +{ "Cs", 133, 133, 132.905430000, METAL , 7, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +{ "Ba", 137, 138, 137.905200000, METAL , 9, 0, {{0,}, {0,}, {2,}, {1,}, {0,} }}, +{ "La", 139, 139, 138.906360000, METAL , 11, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, +{ "Ce", 140, 140, 139.905400000, METAL2, 0, 1, {{0,}, {0,}, {3,4,}, {0,}, {0,} }}, +{ "Pr", 141, 141, 140.907660000, METAL2, 0, 1, {{0,}, {0,}, {3,4,}, {0,}, {0,} }}, +{ "Nd", 144, 142, 141.907719000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, +{ "Pm", 145, 145, 144.912800000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, +{ "Sm", 150, 152, 151.919700000, METAL2, 0, 1, {{0,}, {0,}, {2,3,}, {0,}, {0,} }}, +{ "Eu", 152, 153, 152.921200000, METAL2, 0, 1, {{0,}, {0,}, {2,3,}, {0,}, {0,} }}, +{ "Gd", 157, 158, 157.924099000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, +{ "Tb", 159, 159, 158.925350000, METAL2, 0, 1, {{0,}, {0,}, {3,4,}, {0,}, {0,} }}, +{ "Dy", 163, 164, 163.929200000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, /* mw rounding uncertain */ +{ "Ho", 165, 165, 164.930300000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, +{ "Er", 167, 166, 165.930300000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, +{ "Tm", 169, 169, 168.934230000, METAL2, 0, 1, {{0,}, {0,}, {2,3,}, {0,}, {0,} }}, +{ "Yb", 173, 174, 173.938900000, METAL2, 0, 1, {{0,}, {0,}, {2,3,}, {0,}, {0,} }}, +{ "Lu", 175, 175, 174.940800000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, +{ "Hf", 178, 180, 179.946600000, METAL , 13, 1, {{0,}, {0,}, {4,}, {0,}, {0,} }}, +{ "Ta", 181, 181, 180.948010000, METAL , 15, 1, {{0,}, {0,}, {5,}, {0,}, {0,} }}, +{ "W", 184, 184, 183.951000000, METAL2, 17, 1, {{0,}, {0,}, {3,4,5,6,}, {0,}, {0,} }}, +{ "Re", 186, 187, 186.955800000, METAL2, 19, 1, {{0,}, {0,}, {2,4,6,7,}, {0,}, {0,} }}, +{ "Os", 190, 192, 191.961500000, METAL2, 22, 1, {{0,}, {0,}, {2,3,4,6,}, {0,}, {0,} }}, +{ "Ir", 192, 193, 192.962900000, METAL2, 22, 1, {{0,}, {0,}, {2,3,4,6,}, {0,}, {0,} }}, +{ "Pt", 195, 195, 194.964800000, METAL2, 22, 1, {{0,}, {0,}, {2,4,}, {0,}, {0,} }}, +{ "Au", 197, 197, 196.966560000, METAL , 24, 1, {{0,}, {0,}, {1,3,}, {0,}, {0,} }}, +{ "Hg", 201, 202, 201.970617000, METAL2, 19, 1, {{0,}, {0,}, {1,2,}, {0,}, {0,} }}, +{ "Tl", 204, 205, 204.974400000, METAL2, 18, 0, {{3,5,}, {2,4,}, {1,3,}, {0,}, {0,} }}, +{ "Pb", 207, 208, 207.976627000, METAL2, 18, 0, {{2,4,6,}, {3,5}, {2,4,}, {3,}, {0,} }}, +{ "Bi", 209, 209, 208.980390000, METAL , 19, 0, {{1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,}, {3,} }}, +{ "Po", 209, 209, 208.982400000, METAL2, 20, 0, {{0,}, {1,3,5,7,}, {2,4,6,}, {3,5,}, {2,4,} }}, +{ "At", 210, 210, 209.987100000, 0 , 22, 0, {{0,}, {0,}, {1,3,5,7,}, {2,4,6}, {3,5,} }}, +{ "Rn", 222, 222, 222.017500000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, +{ "Fr", 223, 223, 223.019700000, METAL , 0, 0, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +{ "Ra", 226, 226, 226.025410000, METAL , 0, 0, {{0,}, {0,}, {2,}, {1,}, {0,} }}, +{ "Ac", 227, 227, 227.027750000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, +{ "Th", 232, 232, 232.038050000, METAL2, 0, 1, {{0,}, {0,}, {3,4,}, {0,}, {0,} }}, +{ "Pa", 231, 231, 231.035880000, METAL2, 0, 1, {{0,}, {0,}, {3,4,5,}, {0,}, {0,} }}, +{ "U", 238, 238, 238.050790000, METAL2, 0, 1, {{0,}, {0,}, {3,4,5,6,}, {0,}, {0,} }}, +{ "Np", 237, 237, 237.048170000, METAL2, 0, 1, {{0,}, {0,}, {3,4,5,6,}, {0,}, {0,} }}, +{ "Pu", 244, 244, 244.064200000, METAL2, 0, 1, {{0,}, {0,}, {3,4,5,6,}, {0,}, {0,} }}, +{ "Am", 243, 243, 243.061370000, METAL2, 0, 1, {{0,}, {0,}, {3,4,5,6,}, {0,}, {0,} }}, +{ "Cm", 247, 247, 247.070300000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, +{ "Bk", 247, 247, 247.070300000, METAL , 0, 1, {{0,}, {0,}, {3,4,}, {0,}, {0,} }}, +{ "Cf", 251, 251, 251.079600000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, +{ "Es", 252, 252, 252.082800000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, +{ "Fm", 257, 257, 257.095100000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, +{ "Md", 258, 258, 258.098600000, METAL , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, +{ "No", 259, 259, 259.100900000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +{ "Lr", 260, 260, 260.105400000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +{ "Rf", 261, 261, 261.108700000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, + +/* + The elements below were added after v. 1.03. + When available, the mass is given for isotope with the longest half-life. + Standard valences given here are just placeholders. + v. 1.04: added elements 105-112. + Ref.: M. E. WIESER AND T. B. COPLEN. + Atomic weights of the elements 2009 (IUPAC Technical Report). + Pure Appl. Chem., Vol. 83, No. 2, pp. 359�396, 2011. + v. 1.05: added elements 114 and 116; + updated data for elements 105-112. + Ref.: J. Meija, T.B. Coplen, M.Berglund et al. + Atomic weights of the elements 2013 (IUPAC Technical Report). + Pure Appl. Chem., Vol. 88, No. 3, pp. 265�291, 2016. + added elements 113, 115, 117, and 118, according to IUPAC provisional recommendations: + Ref.: L. Ohrstrom, J. Reedijk. + Names and Symbols of the Elements with Atomic Numbers 113, 115, 117 and 118. + Pure Appl. Chem., May 1, 2016, Manuscript ID PAC-REC-16-05-01 + http://iupac.org/cms/wp-content/uploads/2016/06/names-and-symbols-of-elements.pdf +*/ + +/* 105 dubnium Db ? Like: Ta */ +{ "Db", 270, 270, 270.131000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +/* 106 seaborgium Sg ? Like: W */ +{ "Sg", 269, 269, 269.129000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +/* 107 bohrium Bh ? Like: Re */ +{ "Bh", 270, 270, 270.133000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +/* 108 hassium Hs ? Like: Os */ +{ "Hs", 270, 270, 270.134000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +/* 109 meitnerium Mt ? Like: Ir */ +{ "Mt", 278, 278, 278.156000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +/* 110 darmstadtium Ds ? Like: Pt */ +{ "Ds", 281, 281, 281.165000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +/* 111 roentgenium Rg ? Like: Au */ +{ "Rg", 281, 281, 281.166000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +/* 112 copernicium Cn ? Like: Hg */ +{ "Cn", 285, 285, 285.177000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +/* 113 nihonium Nh ? Like: ? */ +{ "Nh", 278, 278, 278.000000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +/* 114 flerovium Fl ? Like: Pb */ +{ "Fl", 289, 289, 289.190000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +/* 115 moscovium Mc ? Like: ? */ +{ "Mc", 289, 289, 289.000000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +/* 116 livermorium Lv ? Like: Po */ +{ "Lv", 293, 293, 293.204000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +/* 117 tennessine Ts ? Like: ? */ +{ "Ts", 297, 297, 297.000000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +/* 118 oganesson Og ? Like: ? */ +{ "Og", 294, 294, 294.000000000, METAL , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +/* End of added in v. 1.04 - 1.05 */ +/* +{ "Zy", 0, 0, 0.000000000, 0 , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +*/ +{ "Zz", 0, 0, 0.000000000, 0 , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, +#ifdef INCHI_ZFRAG +{ "Zu", 0, 0, 0.000000000, 0 , 0, 1, {{0,}, {0,}, {1,}, {0,}, {0,} }}, //single bond fragment +{ "Zv", 0, 0, 0.000000000, 0 , 0, 1, {{0,}, {0,}, {2,}, {0,}, {0,} }}, //double bond fragment +{ "Zw", 0, 0, 0.000000000, 0 , 0, 1, {{0,}, {0,}, {3,}, {0,}, {0,} }}, //triple bond fragment +{ "Zx", 0, 0, 0.000000000, 0 , 0, 1, {{0,}, {0,}, {1,2,}, {0,}, {0,} }}, //aromatic bond fragment +#endif + +{ "", 0, 0, 0.000000000, 0 , 0, 0, {{0,}, {0,}, {0,}, {0,}, {0,} }}, +}; + + +/* +#ifdef __cplusplus +} +#endif +*/ + + +const int ERR_ELEM = 255; +const int nElDataLen = sizeof(ElData)/sizeof(ElData[0])-1; + + +/* + MISC. CHEMICAL-STRUCTURE RELATED UTILS/HELPERS +*/ + + +/* + For given element number finds its chemical symbol. + Returns 0 if OK and -1 if element was not found. +*/ +int get_element_chemical_symbol(int nAtNum, char *szElement ) +{ + nAtNum -= 1; + + if ( 0 < nAtNum ) + nAtNum += 2; /* bypass D, T */ + + if ( 0 <= nAtNum && nAtNum < nElDataLen ) + { + /* valid element symbol found */ + strcpy( szElement, ElData[nAtNum].szElName ); + return 0; + } + + /* not found */ + strcpy( szElement, "??" ); + return -1; +} + + +int el_number_in_internal_ref_table( const char* elname ) +{ +int i; +const char *p; + + for ( i = 0; (p=ElData[i].szElName)[0] && strcmp( p, elname ); i++ ) + ; + return p[0] ? i : ERR_ELEM; +} + + +int get_periodic_table_number( const char* elname ) +{ +int num; + + num = el_number_in_internal_ref_table( elname ); + + if ( num < ERR_ELEM ) + /* account for D,T in internal table (but not Mendeleev's table) */ + num = inchi_max(1, num-1); + + return num; +} + + +int if_skip_add_H( int nPeriodicNum ) + /* was called if_skip_add_H(, renamed to avoid confusion with other procedures */ +{ + return + ElData[nPeriodicNum>1? nPeriodicNum+1:0].bSkipAddingH; +} + + +int get_el_valence( int nPeriodicNum, int charge, int val_num ) +{ + if ( charge < MIN_ATOM_CHARGE || charge > MAX_ATOM_CHARGE || val_num >= MAX_NUM_VALENCES ) + return 0; + return + ElData[nPeriodicNum>1? nPeriodicNum+1:0].cValence[NEUTRAL_STATE+charge][val_num]; +} + + +/* + Output valence needed to unambiguosly reconstruct bonds +*/ +int get_unusual_el_valence( int nPeriodicNum, + int charge, + int radical, + int bonds_valence, + int num_H, + int num_bonds ) +{ +int i, num_found, chem_valence, rad_adj, known_chem_valence, exact_found; + + if ( !num_bonds && !num_H ) + return 0; + + if ( charge < MIN_ATOM_CHARGE || charge > MAX_ATOM_CHARGE ) + { + if ( bonds_valence == num_bonds ) + return 0; /* all single bonds */ + return bonds_valence; + } + + if ( !get_el_valence( nPeriodicNum, charge, 0 ) && bonds_valence == num_bonds ) + return 0; + + chem_valence = bonds_valence + num_H; + rad_adj = 0; + num_found = 0; + exact_found = 0; + + /* Take into account a radical */ + if (radical==RADICAL_DOUBLET) + rad_adj = 1; + else if (radical==RADICAL_TRIPLET ) + rad_adj = 2; + + for ( i = 0; i < MAX_NUM_VALENCES; i ++ ) + { + if ( 0 < (known_chem_valence = get_el_valence( nPeriodicNum, charge, i )-rad_adj) && + num_bonds <= known_chem_valence && known_chem_valence <= chem_valence ) + { + num_found ++; + if ( known_chem_valence == chem_valence ) + { + exact_found = 1; + break; + } + } + } + + return (exact_found && 1==num_found) ? 0 + : chem_valence; +} + + +/* + Output valence needed to unambiguosly reconstruct number of H +*/ +int needed_unusual_el_valence( int nPeriodicNum, + int charge, + int radical, + int bonds_valence, + int actual_bonds_valence, + int num_H, int + num_bonds ) +{ +int i, num_found, num_found_known, chem_valence, rad_adj, known_chem_valence, exact_found; +int num_H_expected; +char szElement[4]; + + /* + if ( !num_bonds && !num_H ) + return 0; + */ + + if ( num_bonds && get_element_chemical_symbol(nPeriodicNum, szElement )!=-1 ) + { + num_H_expected = get_num_H( szElement, 0, NULL, charge, radical, actual_bonds_valence, 0,0,0,0 ); + } + else + { + num_H_expected = num_H; + } + + chem_valence = bonds_valence + num_H; + if ( charge < MIN_ATOM_CHARGE || charge > MAX_ATOM_CHARGE || + !get_el_valence( nPeriodicNum, charge, 0 ) || + if_skip_add_H( nPeriodicNum ) || bonds_valence != actual_bonds_valence || + num_H_expected != num_H ) + { + if ( !num_H && !num_H_expected && bonds_valence == actual_bonds_valence ) + return 0; /* no H */ + return chem_valence; /* needs to add H-atoms */ + } + + /* take into account radical */ + if (radical==RADICAL_DOUBLET) + rad_adj = 1; + else if (radical==RADICAL_TRIPLET ) + rad_adj = 2; + else + rad_adj = 0; + + num_found_known = 0; + num_found = 0; + exact_found = 0; + + for ( i = 0; i < MAX_NUM_VALENCES; i ++ ) + { + if ( 0 < (known_chem_valence = get_el_valence( nPeriodicNum, charge, i )) && + bonds_valence <= (known_chem_valence -= rad_adj) ) + { + /* found known valence that fits without H */ + num_found_known ++; + if ( known_chem_valence <= chem_valence ) + { + /* known valence is large enough to accommodate (implicit) H */ + num_found ++; + } + if ( known_chem_valence == chem_valence ) + { + exact_found = 1; + break; + } + } + } + + return + (exact_found&&1==num_found&&1==num_found_known) ? 0 + : chem_valence?chem_valence:-1; /* needs zero */ +} + + +/* + Output valence that does not fit any known valences +*/ +int detect_unusual_el_valence( int nPeriodicNum, + int charge, + int radical, + int bonds_valence, + int num_H, + int num_bonds ) +{ +int i, chem_valence, rad_adj, known_chem_valence; + + if ( !num_bonds && !num_H ) + return 0; + + if ( charge < MIN_ATOM_CHARGE || charge > MAX_ATOM_CHARGE ) + { + if ( bonds_valence == num_bonds ) + return 0; /* all single bonds */ + return bonds_valence; + } + + if ( !get_el_valence( nPeriodicNum, charge, 0 ) && bonds_valence == num_bonds ) + return 0; + + chem_valence = bonds_valence + num_H; + rad_adj = 0; + + /* take into account radical */ + if (radical==RADICAL_DOUBLET) + rad_adj = 1; + else if (radical==RADICAL_TRIPLET || radical==RADICAL_SINGLET ) + rad_adj = 2; + + for ( i = 0; i < MAX_NUM_VALENCES; i ++ ) + { + if ( 0 < (known_chem_valence = get_el_valence( nPeriodicNum, charge, i )-rad_adj) ) + { + if ( known_chem_valence == chem_valence ) + { + return 0; + } + } + } + + return chem_valence; +} + + +/* Return element type */ +int get_el_type( int nPeriodicNum ) +{ + return ElData[nPeriodicNum+1].nType; +} + + +/* Check if element is metal */ +int is_el_a_metal( int nPeriodicNum ) +{ + return 0!=(ElData[nPeriodicNum+1].nType & IS_METAL); +} + + + +/*#ifndef TARGET_API_LIB*/ + + +/* Extract radicals and charges */ +int extract_charges_and_radicals( char *elname, int *pnRadical, int *pnCharge ) +{ +char *q, *r, *p; +int nCharge=0, nRad = 0, charge_len = 0, k, nVal, nSign, nLastSign=1, len; + + p = elname; + + /* extract radicals & charges */ + while ( q = strpbrk( p, "+-^" ) ) + { + switch ( *q ) + { + case '+': + case '-': + for ( k = 0, nVal=0; (nSign = ('+' == q[k])) || (nSign = -('-' == q[k])); k++ ) + { + nVal += (nLastSign = nSign); + charge_len ++; + } + if ( nSign = (int)strtol( q+k, &r, 10 ) ) + { + /* fixed 12-5-2001 */ + nVal += nLastSign * (nSign-1); + } + charge_len = (int) (r - q); + nCharge += nVal; + break; + /* case '.': */ /* singlet '.' may be confused with '.' in formulas like CaO.H2O */ + case '^': + nRad = 1; /* doublet here is 1. See below */ + charge_len = 1; + for ( k = 1; q[0] == q[k]; k++ ) + { + nRad ++; + charge_len ++; + } + break; + } + memmove( q, q+charge_len, strlen(q+charge_len)+1 ); + } + + len = (int) strlen(p); + + /* radical */ + if ( (q = strrchr( p, ':' )) && !q[1]) + { + nRad = RADICAL_SINGLET; + q[0] = '\0'; + len --; + } + else + { + while( (q = strrchr( p, '.' )) && !q[1] ) + { + nRad ++; + q[0] = '\0'; + len --; + } + + nRad = nRad == 1? RADICAL_DOUBLET : + nRad == 2? RADICAL_TRIPLET : 0; + } + + *pnRadical = nRad; + *pnCharge = nCharge; + return ( nRad || nCharge ); +} + +/*#endif*/ + + +int extract_H_atoms( char *elname, S_CHAR num_iso_H[] ) +{ +int i, len, c, k, num_H, val; +char *q; +char elname1 = '\0'; + + i = 0; + num_H = 0; + len = (int) strlen(elname); + c = UCINT elname[0]; + + if ( len > 1 ) + elname1 = elname[1]; + + while ( i < len ) + { + switch ( c ) + { + case 'H': + k = 0; + break; + case 'D': + k = 1; + break; + case 'T': + k = 2; + break; + default: + k = -1; + break; + } + + q = elname+i+1; /* pointer to the next to elname[i] character */ + c = UCINT q[0]; + + if ( k >= 0 && !islower( c ) ) + { + /* found a hydrogen */ + if ( isdigit( c ) ) + { + val = (int)strtol( q, &q, 10 ); + /* q = pointer to the next to number of hydrogen atom(s) character */ + } + else + { + val = 1; + } + if ( k ) + { + num_iso_H[k] += val; + } + else + { + num_H += val; + } + + /* remove the hydrogen atom from the string */ + len -= (int) (q-elname)-i; + memmove( elname+i, q, len + 1 ); + /* c = UCINT elname[i]; */ + } + else + { + i ++; + } + + c = UCINT elname[i]; /* moved here 11-04-2002 */ + } + + len = (int) strlen(elname); + if ( len == 2 ) + { + if ( elname[1] != elname1 ) + /* Error, incorrect 2nd char of elname appears after 'subtracting' {H,D,T} */ + /* See a bug reported to inchi-discuss by A. Dalke for alias atom "pH4d" */ + /*^^^ 2017-01-06 */ + elname[1] = '?'; + } + + return num_H; +} + + +/* Return number of attached hydrogens */ +int get_num_H ( const char* elname, + int inp_num_H, + S_CHAR inp_num_iso_H[], + int charge, + int radical, + int chem_bonds_valence, + int atom_input_valence, + int bAliased, + int bDoNotAddH, + int bHasMetalNeighbor ) +{ +int val, i, el_number, num_H = 0, num_iso_H; +static int el_number_N = 0, el_number_S, el_number_O, el_number_C; + + if ( !el_number_N ) + { + el_number_N = el_number_in_internal_ref_table( "N" ); + el_number_S = el_number_in_internal_ref_table( "S" ); + el_number_O = el_number_in_internal_ref_table( "O" ); + el_number_C = el_number_in_internal_ref_table( "C" ); + } + + /* atom_input_valence (cValence) cannot be specified in case of */ + /* aliased MOLFile atom with known inp_num_H or inp_num_iso_H[] */ + + if ( bAliased ) + { + num_H = inp_num_H; + } + else if ( atom_input_valence && (atom_input_valence !=15 || chem_bonds_valence) ) + { + num_H = inchi_max( 0, atom_input_valence - chem_bonds_valence ); + } + else if ( atom_input_valence == 15 && !chem_bonds_valence ) + { + num_H = 0; + } + else if ( MIN_ATOM_CHARGE <= charge && + MAX_ATOM_CHARGE >= charge && + ERR_ELEM != (el_number=el_number_in_internal_ref_table( elname ) ) && + !ElData[el_number].bSkipAddingH && !bDoNotAddH ) + { + /* add hydrogen atoms according to standard element valence */ + if ( radical && radical != RADICAL_SINGLET ) { + if ( val = ElData[el_number].cValence[NEUTRAL_STATE+charge][0] ) + { + val -= (radical==RADICAL_DOUBLET) ? 1 + : (radical==RADICAL_SINGLET || radical==RADICAL_TRIPLET )? 2 : val; + /* if unknown radical then do not add H */ + num_H = inchi_max( 0, val - chem_bonds_valence ); + } + } + else + { + /* find the smallest valence that is greater than the sum of the chemical bond valences */ + for ( i = 0; + (val=ElData[el_number].cValence[NEUTRAL_STATE+charge][i]) && + val < chem_bonds_valence; + i++ ) + ; + + /* special case: do not add H to N(IV), S(III), S+(II), S-(II) */ /* S ions added 2004-05-10 */ + if ( el_number == el_number_N && !charge && !radical && val == 5 ) + val = 3; + /*else if ( el_number == el_number_N && !charge && !radical && val == 3 && + chem_bonds_valence == 2 && bHasMetalNeighbor ) + val = 2; + */ + else if ( el_number == el_number_S && !charge && !radical && val == 4 && chem_bonds_valence == 3 ) + val = 3; + else if ( bHasMetalNeighbor && el_number != el_number_C && val > 0 ) + { + val --; + } + /* + if ( (el_number == el_number_S || el_number == el_number_O) && + abs(charge)==1 && !radical && val == 3 && chem_bonds_valence == 2 && bHasMetalNeighbor ) + val = 2; + else + */ + + num_H = inchi_max( 0, val - chem_bonds_valence ); + } + + num_iso_H = 0; + if ( inp_num_iso_H ) + { + for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) + { + num_iso_H += inp_num_iso_H[i]; + } + } + + /* should not happen because atom here is not aliased */ + if ( num_iso_H ) + { + if ( num_H >= num_iso_H ) + { + num_H -= num_iso_H; + } + else + { + num_H = inp_num_H; /* as requested in the alias */ + /* num_H = (num_iso_H - num_H) % 2; */ /* keep unchanged parity of the total number of H atoms */ + } + } + + /* should not happen because atom here is not aliased */ + if ( inp_num_H > num_H ) + { + num_H = inp_num_H; /* as requested in the alias */ + /* num_H = inp_num_H + (inp_num_H - num_H)%2; */ /* keep unchanged parity of the number of non-isotopic H atoms */ + } + } + else + { + num_H = inp_num_H; + } + + return num_H; +} + + + +int get_atomic_mass_from_elnum( int nAtNum ) +{ + nAtNum-= 1; + + if ( 0 < nAtNum ) + nAtNum+= 2; /* bypass D, T */ + + if ( 0 <= nAtNum && nAtNum < nElDataLen ) + return (int)ElData[nAtNum].nAtMass; + + return 0; +} + + +/* +int get_mw(char elname[]) +{ + int i; + + for (i=0; ivalence; i ++ ) + { + neigh = a->neighbor[i]; + + if ( is_el_a_metal( at[(int)a->neighbor[i]].el_number ) ) + { + bond_type = a->bond_type[i]; + + if ( bond_type <= BOND_TYPE_TRIPLE ) + { + nVal2Metal += bond_type; + } + else + { + return -1; /* bond to metal order is not well defined */ + } + } + } + + return nVal2Metal; +} + + +/* + +*/ +int num_of_H( inp_ATOM *at, int iat ) +{ +static int el_number_H; +int i, n, num_explicit_H = 0; +inp_ATOM *a = at + iat; + + if ( !el_number_H ) + el_number_H = get_periodic_table_number( "H" ); + + for ( i = 0; i < a->valence; i ++ ) + { + n = a->neighbor[i]; + num_explicit_H += ( 1 == at[n].valence && el_number_H == at[n].el_number ); + } + + return num_explicit_H+NUMH(at,iat); +} + + +/* + +*/ +int has_other_ion_neigh( inp_ATOM *at, + int iat, + int iat_ion_neigh, + const char *el, + int el_len ) +{ +int charge = at[iat_ion_neigh].charge; +int i, neigh; + + for ( i = 0; i < at[iat].valence; i ++ ) + { + neigh = at[iat].neighbor[i]; + + if ( neigh != iat_ion_neigh && at[neigh].charge == charge && + NULL != memchr( el, at[neigh].el_number, el_len ) ) + { + return 1; + } + } + + return 0; +} + + +/* + Check if has_other_ion_in_sphere_2 + + BFS r=2 +*/ +int has_other_ion_in_sphere_2( inp_ATOM *at, + int iat, + int iat_ion_neigh, + const char *el, + int el_len ) +{ +#define MAXQ 16 + AT_NUMB q[MAXQ]; + int lenq=0, lenq2, dist = 0, i = 0, iq, neigh, j, nRet=0; + q[lenq++] = iat; + at[iat].cFlags = 1; + + iq = 0; + dist = 1; + /* use at->cFlags as an indicator */ + while ( dist <= 2 ) + { + for ( lenq2 = lenq; iq < lenq2; iq ++ ) + { + i = q[iq]; + + for ( j = 0; j < at[i].valence; j ++ ) + { + neigh = at[i].neighbor[j]; + + if ( !at[neigh].cFlags && + at[neigh].valence <= 3 && + NULL != memchr( el, at[neigh].el_number, el_len ) ) + { + q[lenq ++] = neigh; + at[neigh].cFlags = 1; + if ( neigh != iat_ion_neigh && + at[iat_ion_neigh].charge == at[neigh].charge ) + { + nRet ++; + } + } + } + } + + dist ++; + } + + for ( iq = 0; iq < lenq; iq ++ ) + { + i = q[iq]; + at[i].cFlags = 0; + } + + return nRet; +} + + +int nNoMetalNumBonds( inp_ATOM *at, int at_no ) +{ +int i; + + inp_ATOM *a = at + at_no; + int num_H = NUMH(a, 0); + int std_chem_bonds_valence = get_el_valence( a->el_number, a->charge, 0 ); + + if ( a->chem_bonds_valence + num_H > std_chem_bonds_valence ) + { + int valence_to_metal = 0; + int num_bonds_to_metal = 0; + + for ( i = 0; i < a->valence; i ++ ) + { + if ( is_el_a_metal( at[(int)a->neighbor[i]].el_number ) ) + { + if ( (a->bond_type[i] & BOND_TYPE_MASK) >= BOND_TYPE_ALTERN ) + { + return a->valence; /* fall back */ + } + num_bonds_to_metal ++; + valence_to_metal += (a->bond_type[i] & BOND_TYPE_MASK); + } + } + + if ( a->chem_bonds_valence + num_H - valence_to_metal == std_chem_bonds_valence ) + { + /* removing bonds to metal produces standard valence */ + return a->valence - num_bonds_to_metal; + } + } + +#if ( S_VI_O_PLUS_METAL_FIX_BOND == 1 ) + else + if ( 1 == a->charge && 2 == get_endpoint_valence(a->el_number) && + a->chem_bonds_valence + num_H == std_chem_bonds_valence ) + { + int valence_to_metal = 0; + int num_bonds_to_metal = 0; + for ( i = 0; i < a->valence; i ++ ) + { + if ( is_el_a_metal( at[(int)a->neighbor[i]].el_number ) ) + { + if ( (a->bond_type[i] & BOND_TYPE_MASK) >= BOND_TYPE_ALTERN ) + { + return a->valence; /* fall back */ + } + num_bonds_to_metal ++; + valence_to_metal += (a->bond_type[i] & BOND_TYPE_MASK); + } + } + if ( 1 == valence_to_metal ) + { + /* removing bonds to metal produces standard valence */ + return a->valence - num_bonds_to_metal; + } + } +#endif + + return a->valence; +} + + + +int nNoMetalBondsValence( inp_ATOM *at, int at_no ) +{ +int i; + + inp_ATOM *a = at + at_no; + int num_H = NUMH(a, 0); + int std_chem_bonds_valence = get_el_valence( a->el_number, a->charge, 0 ); + + if ( a->chem_bonds_valence + num_H > std_chem_bonds_valence ) + { + int valence_to_metal = 0; + /*int num_bonds_to_metal = 0;*/ + + for ( i = 0; i < a->valence; i ++ ) + { + if ( is_el_a_metal( at[(int)a->neighbor[i]].el_number ) ) + { + if ( (a->bond_type[i] & BOND_TYPE_MASK) >= BOND_TYPE_ALTERN ) + { + return a->valence; /* fall back */ + } + /* num_bonds_to_metal ++;*/ + valence_to_metal += (a->bond_type[i] & BOND_TYPE_MASK); + } + } + + if ( a->chem_bonds_valence + num_H - valence_to_metal == std_chem_bonds_valence ) + { + /* removing bonds to metal produces standard valence */ + return a->chem_bonds_valence - valence_to_metal; + } + } + +#if ( S_VI_O_PLUS_METAL_FIX_BOND == 1 ) + else if ( 1 == a->charge && 2 == get_endpoint_valence(a->el_number) && + a->chem_bonds_valence + num_H == std_chem_bonds_valence ) + { + + int valence_to_metal = 0; + /* int num_bonds_to_metal = 0;*/ + + for ( i = 0; i < a->valence; i ++ ) + { + if ( is_el_a_metal( at[(int)a->neighbor[i]].el_number ) ) + { + if ( (a->bond_type[i] & BOND_TYPE_MASK) >= BOND_TYPE_ALTERN ) + { + return a->valence; /* fall back */ + } + /* num_bonds_to_metal ++;*/ + valence_to_metal += (a->bond_type[i] & BOND_TYPE_MASK); + } + } + + if ( 1 == valence_to_metal ) + /* removing bonds to metal produces standard valence */ + return a->chem_bonds_valence - valence_to_metal; + } +#endif + + return a->chem_bonds_valence; +} + + +int nNoMetalNeighIndex( inp_ATOM *at, int at_no ) +{ +int i; + + inp_ATOM *a = at + at_no; + + for ( i = 0; i < a->valence; i ++ ) + { + if ( !is_el_a_metal( at[(int)a->neighbor[i]].el_number ) ) + return i; + } + + return -1; +} + + +int nNoMetalOtherNeighIndex( inp_ATOM *at, int at_no, int cur_neigh ) +{ +int i, neigh; + + inp_ATOM *a = at + at_no; + + for ( i = 0; i < a->valence; i ++ ) + { + neigh = (int)a->neighbor[i]; + + if ( neigh != cur_neigh && !is_el_a_metal( at[neigh].el_number ) ) + return i; + } + + return -1; +} + + +int nNoMetalOtherNeighIndex2( inp_ATOM *at, + int at_no, + int cur_neigh, + int cur_neigh2 ) +{ +int i, neigh; + + inp_ATOM *a = at + at_no; + + for ( i = 0; i < a->valence; i ++ ) + { + neigh = (int)a->neighbor[i]; + + if ( neigh != cur_neigh && neigh != cur_neigh2 && !is_el_a_metal( at[neigh].el_number ) ) + return i; + } + + return -1; +} + + + +#ifndef COMPILE_ANSI_ONLY + + +int MakeRemovedProtonsString( int nNumRemovedProtons, + NUM_H *nNumExchgIsotopicH, + NUM_H *nNumRemovedProtonsIsotopic, + int bIsotopic, + char *szRemovedProtons, + int *num_removed_iso_H ) +{ +int i, j, len, num; + + len = 0; + + if ( nNumRemovedProtons ) + { + len = sprintf ( szRemovedProtons, "Proton balance: %c %d H+", + nNumRemovedProtons>=0? '+':'-', abs(nNumRemovedProtons) ); + } + + if ( bIsotopic && (nNumRemovedProtonsIsotopic || nNumExchgIsotopicH) ) + { + + for ( i = 0, j = 0; i < NUM_H_ISOTOPES; i ++ ) + { + + num = (nNumExchgIsotopicH? nNumExchgIsotopicH[i]:0) + + (nNumRemovedProtonsIsotopic? nNumRemovedProtonsIsotopic[i]:0); + + if ( num ) + { + len += sprintf( szRemovedProtons+len, "%s %d^%dH", + j? ", ":" [ removed ", num, i+1); + j ++; + } + } + + if ( j ) + { + len += sprintf( szRemovedProtons+len, " ]" ); + if ( num_removed_iso_H ) + *num_removed_iso_H = j; + } + } + + if ( !len ) { + szRemovedProtons[0] = '\0'; + } + return len; +} +#endif + + +int get_endpoint_valence( U_CHAR el_number ) +{ + static U_CHAR el_numb[6]; + static int len, len2; + int i; + int len3; + if (!len) + { + len3=0; + el_numb[len3++] = (U_CHAR)get_periodic_table_number( "O" ); + el_numb[len3++] = (U_CHAR)get_periodic_table_number( "S" ); + el_numb[len3++] = (U_CHAR)get_periodic_table_number( "Se" ); + el_numb[len3++] = (U_CHAR)get_periodic_table_number( "Te" ); + len2 = len3; + el_numb[len3++] = (U_CHAR)get_periodic_table_number( "N" ); + len=len3; + } + for ( i = 0; i < len; i ++ ) { + if ( el_numb[i] == el_number ) { + return i < len2? 2 : 3; + } + } + return 0; +} + + +#if ( KETO_ENOL_TAUT == 1 ) /* post v.1 feature */ +int get_endpoint_valence_KET( U_CHAR el_number ) +{ + static U_CHAR el_numb[2]; + static int len, len2; + int len3; + int i; + if (!len ) + { + len3=0; + el_numb[len3++] = (U_CHAR)get_periodic_table_number( "O" ); + len2 = len3; + el_numb[len3++] = (U_CHAR)get_periodic_table_number( "C" ); + len=len3; + } + for ( i = 0; i < len; i ++ ) { + if ( el_numb[i] == el_number ) { + return i < len2? 2 : 4; + } + } + return 0; +} +#endif + + +/* + MEMORY MANAGE +*/ + + +#ifndef inchi_malloc +void *inchi_malloc(size_t c) +{ + return malloc(c); +} +#endif + + +#ifndef inchi_calloc +void *inchi_calloc(size_t c, size_t n) +{ + return calloc(c,n); +} +#endif + + +#ifndef inchi_free +void inchi_free(void *p) +{ + if(p) { + free(p); /*added check if zero*/ + } +} +#endif + + +/* + STRINGS/TEXT +*/ + + + +/* Remove leading & trailing spaces; replace consecutive spaces with a single space. */ +int normalize_string( char* name ) +{ +int i, len, n; + + len = (int)strlen(name); + + for ( i = 0, n = 0; i < len; i++ ) + { + if ( isspace( UCINT name[i] ) /*|| !isprint( UCINT name[i] )*/ ) + { + name[i] = ' '; /* exterminate tabs !!! */ + n++; + } + else + { + if ( n > 0 ) + { + memmove( (void*) &name[i-n], (void*) &name[i], len-i+1 ); + i -= n; + len -= n; + } + n = -1; + } + } + if ( n == len ) /* empty line */ + name[len=0] = '\0'; + else if ( ++n && n <= len ) + { + len -= n; + name[len] = '\0'; + } + + return len; +} + + + +/* Replace non-ASCII characters with '.' + and return number of replacements */ +int dotify_non_printable_chars( char *line ) +{ +int i, c, num = 0; + + if ( line ) + { + for ( i = 0; c = UCINT line[i]; i++ ) + { + /* assuming ASCII charset */ + if ( c < ' ' || c >= 0x7F ) + { + line[i] = '.'; + num++; + } + } + } + + return num; +} + + +/* + Reads char sequence pointed to by *pstring ( char *p = *ppstring) for + not more than maxlen bytes) to 'field' up to first occurrence of any of + delimiters in 'delims' or end of line, whichever occurs first. + Sets *pstring to point to character which matches delimiter. + Returns number of bytes copied, -1 on error. +*/ +int read_upto_delim( char **pstring, char *field, int maxlen, char* delims ) +{ + int i, n; + char *p = *pstring; + + if ( !p ) + return -1; + + /* skip leading spaces */ + for ( i=0; p[i] && isspace(UCINT p[i] ); i++ ) + ; + p+= i; + + /* read up to next delim or eol */ + n = 0; + while ( p[n] && !is_matching_any_delim( p[n], delims ) ) + { + n++; + } + + if ( n+1 > maxlen ) + return -1; + + mystrncpy( field, p, n+1 ); + field[n+1] = '\0'; + + if ( !p[n] ) + /* reached EOL */ + *pstring = NULL; + else + /* advance reading pos */ + *pstring = *pstring + i + n; + + return n; +} + + +/* + Check if a character is in the list of possible delimiters + NB: same as isspace if delims is " \t\n\v\f\r" + (0x20 and 0x09-0x0D) +*/ +int is_matching_any_delim( char c, char* delims ) +{ + int ic = UCINT c; + while ( *delims ) + { + if ( ic == *delims ) + return 1; + delims++; + } + return 0; +} + + +/* Remove trailing spaces */ +void remove_trailing_spaces( char* p ) +{ + int len; + for( len = (int)strlen( p ) - 1; len >= 0 && isspace( UCINT p[len] ); len-- ) + ; + p[++len] = '\0'; +} + + +void remove_one_lf( char* p) +{ + size_t len; + if ( p && 0 < (len = strlen(p)) && p[len-1] == '\n' ) + { + p[len-1] = '\0'; + if ( len >= 2 && p[len-2] == '\r' ) + p[len-2] = '\0'; + } +} + + +/* + Copies up to maxlen characters INCLUDING end null from source to target + Fills out the rest of the target with null bytes + + protected from non-zero-terminated source and overlapped target/source. +*/ +int mystrncpy(char *target,const char *source,unsigned maxlen) +{ +const char *p; +unsigned len; + + if (target==NULL || maxlen == 0 || source == NULL) + return 0; + + if ( p = (const char*)memchr(source, 0, maxlen) ) + { /* maxlen does not include the found zero termination */ + len = (int) (p-source); + } + else + { /* reduced length does not include one more byte for zero termination */ + len = maxlen-1; + } + + if ( len ) + memmove( target, source, len ); + + memset( target+len, 0, maxlen-len); /* zero termination */ + + return 1; +} + + +/* Remove leading and trailing white spaces */ +char* lrtrim( char *p, int* nLen ) +{ +int i, len=0; + + if ( p && (len = (int) strlen( p )) ) + { + for ( i = 0; i < len && __isascii( p[i] ) && isspace( p[i] ); i++ ) + ; + if ( i ) + (memmove)( p, p+i, (len -= i)+1 ); + for ( ; 0 < len && __isascii( p[len-1] ) && isspace( p[len-1] ); len--) + ; + p[len] = '\0'; + } + + if ( nLen ) + *nLen = len; + + return p; +} + +/* + extract_inchi_substring( ... ) + + Extract InChI substring embedded into a longer string. + + InChI should start from "InChI=". + + As for the end of InChI, + consider that according to + http://info-uri.info/registry/OAIHandler?verb=GetRecord&metadataPrefix=reg&identifier=info:inchi/ + an InChI identifier may contain the following characters: + A-Z + a-z + 0-9 + ()*+,-./;=?@ + + Here we treat any character not conforming this specification as a whitespace + which marks the end of the InChI string. + For example: + "InChI=1/Ar%" + "InChI=1/Ar\n" + "InChI=1/Ar\r\t" + all will be trimmed to + "InChI=1/Ar" + +*/ +void extract_inchi_substring(char ** buf, const char *str, size_t slen) +{ +size_t i; +const char *p; +char pp; + + + *buf = NULL; + + if (str==NULL) + return; + if (strlen(str)<1) + return; + + p = strstr(str, "InChI="); + if (NULL==p) + return; + + for (i=0; i= 'A' && pp <='Z') continue; + if (pp >= 'a' && pp <='z') continue; + if (pp >= '0' && pp <='9') continue; + switch ( pp ) + { + case '(': + case ')': + case '*': + case '+': + case ',': + case '-': + case '.': + case '/': + case ';': + case '=': + case '?': + case '@': continue; + + default: break; + } + + break; + } + + *buf = (char*) inchi_calloc(i+1, sizeof(char)); + memcpy(*buf, p, i); + (*buf)[i] = '\0'; + + return; +} + + +/* + For compatibility: local implementation of non-ANSI (MS-specific) functions, prefixed with "inchi_" +*/ + + +#define __MYTOLOWER(c) ( ((c) >= 'A') && ((c) <= 'Z') ? ((c) - 'A' + 'a') : (c) ) + + +int inchi_memicmp( const void * p1, const void * p2, size_t length ) +{ + const U_CHAR *s1 = (const U_CHAR*)p1; + const U_CHAR *s2 = (const U_CHAR*)p2; + while ( length-- ) + { + if ( *s1 == *s2 || + __MYTOLOWER( (int)*s1 ) == __MYTOLOWER( (int)*s2 )) + { + s1 ++; + s2 ++; + } + else + { + return + __MYTOLOWER( (int)*s1 ) - __MYTOLOWER( (int)*s2 ); + } + } + + return 0; +} + + +int inchi_stricmp( const char *s1, const char *s2 ) +{ + while ( *s1 ) + { + if ( *s1 == *s2 || + __MYTOLOWER( (int)*s1 ) == __MYTOLOWER( (int)*s2 )) + { + s1 ++; + s2 ++; + } + else + { + return + __MYTOLOWER( (int)*s1 ) - __MYTOLOWER( (int)*s2 ); + } + } + + if ( *s2 ) + return -1; + + return 0; +} + + +char *inchi__strnset( char *s, int val, size_t length ) +{ + char *ps = s; + while (length-- && *ps) + *ps++ = (char)val; + return s; +} + + +char *inchi__strdup( const char *string ) +{ + char *p = NULL; + if ( string ) + { + size_t length = strlen( string ); + p = (char *) inchi_malloc( length + 1 ); + if ( p ) + { + strcpy( p, string ); + } + } + return p; +} + + +#undef __MYTOLOWER + + +/* + End of local implementation of non-ANSI (MS-specific) functions +*/ diff --git a/INCHI-1-SRC/INCHI_BASE/src/util.h b/INCHI-1-SRC/INCHI_BASE/src/util.h new file mode 100644 index 0000000..69d7286 --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/util.h @@ -0,0 +1,186 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _UTIL_H_ +#define _UTIL_H_ + +#include "inpdef.h" + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + +int get_atomic_mass(const char *elname); +int get_atomic_mass_from_elnum( int nAtNum ); +int get_num_H ( const char* elname, + int inp_num_H, + S_CHAR num_iso_H[], + int charge, + int radical, + int chem_bonds_valence, + int atom_input_valence, + int bAliased, + int bDoNotAddH, + int bHasMetalNeighbor ); +int extract_charges_and_radicals( char *elname, + int *pnRadical, + int *pnCharge ); +int extract_H_atoms( char *elname, S_CHAR num_iso_H[] ); + +int normalize_string( char* name ); +int read_upto_delim( char **pstring, char *field, int maxlen, char* delims ); +int is_matching_any_delim( char c, char* delims ); +int dotify_non_printable_chars( char *line ); +char* lrtrim( char *p, int* nLen ); +void remove_trailing_spaces( char* p ); +void remove_one_lf( char* p); +int mystrncpy( char *target, + const char *source, + unsigned maxlen); +void mystrrev( char *p ); + + +#define ALPHA_BASE 27 + + +int inchi_memicmp( const void * p1, const void * p2, size_t length ); +int inchi_stricmp( const char *s1, const char *s2 ); +char *inchi__strnset( char *s, int val, size_t length ); +char *inchi__strdup( const char *string ); + + +long inchi_strtol( const char *str, const char **p, int base); +double inchi_strtod( const char *str, const char **p ); +AT_NUMB *is_in_the_list( AT_NUMB *pathAtom, AT_NUMB nNextAtom, int nPathLen ); +int *is_in_the_ilist( int *pathAtom, int nNextAtom, int nPathLen ); +int is_ilist_inside( int *ilist, int nlist, int *ilist2, int nlist2 ); + +int get_periodic_table_number( const char* elname ); +int is_el_a_metal( int nPeriodicNum ); +int get_el_valence( int nPeriodicNum, + int charge, + int val_num ); +int get_unusual_el_valence( int nPeriodicNum, + int charge, + int radical, + int bonds_valence, + int num_H, + int num_bonds ); +/* Output valence that does not fit any known valences */ +int detect_unusual_el_valence( int nPeriodicNum, + int charge, + int radical, + int bonds_valence, + int num_H, + int num_bonds ); +int needed_unusual_el_valence( int nPeriodicNum, + int charge, + int radical, + int bonds_valence, + int actual_bonds_val, + int num_H, + int num_bonds ); +int get_el_type( int nPeriodicNum ); +int if_skip_add_H( int nPeriodicNum ); +int get_element_chemical_symbol(int nAtNum, char *szElement ); +int MakeRemovedProtonsString( int nNumRemovedProtons, + NUM_H *nNumExchgIsotopicH, + NUM_H *nNumRemovedProtonsIsotopic, + int bIsotopic, + char *szRemovedProtons, + int *num_removed_iso_H ); + + +/* + Ion pairs and fixing bonds +*/ + + +int num_of_H( inp_ATOM *at, int iat ); +int has_other_ion_neigh( inp_ATOM *at, + int iat, + int iat_ion_neigh, + const char *el, + int el_len ); +int has_other_ion_in_sphere_2( inp_ATOM *at, + int iat, + int iat_ion_neigh, + const char *el, + int el_len ); +int nNoMetalNumBonds( inp_ATOM *at, int at_no ); +int nNoMetalBondsValence( inp_ATOM *at, int at_no ); +int nNoMetalNeighIndex( inp_ATOM *at, int at_no ); +int nNoMetalOtherNeighIndex( inp_ATOM *at, + int at_no, + int cur_neigh ); +int nNoMetalOtherNeighIndex2( inp_ATOM *at, + int at_no, + int cur_neigh, + int cur_neigh2 ); +void extract_inchi_substring(char ** buf, + const char *str, + size_t slen); +int nBondsValToMetal( inp_ATOM* at, int iat ); +int nBondsValenceInpAt( const inp_ATOM *at, + int *nNumAltBonds, + int *nNumWrongBonds ); +int bHeteroAtomMayHaveXchgIsoH( inp_ATOM *atom, int iat ); +int get_endpoint_valence( U_CHAR el_number ); +#if ( KETO_ENOL_TAUT == 1 ) +int get_endpoint_valence_KET( U_CHAR el_number ); +#endif + +/* Forward declaration */ +struct tagCANON_GLOBALS; + +int SetBitFree( struct tagCANON_GLOBALS *pCG); +void WriteCoord( char *str, double x ); +extern const int ERR_ELEM; +extern const int nElDataLen; + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + + +#endif /* _UTIL_H_ */ diff --git a/INCHI-1-SRC/INCHI_EXE/bin/readme.txt b/INCHI-1-SRC/INCHI_EXE/bin/readme.txt new file mode 100644 index 0000000..c2c9e1b --- /dev/null +++ b/INCHI-1-SRC/INCHI_EXE/bin/readme.txt @@ -0,0 +1,41 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +This directory presents (initially empty) layout of +sub-directories where the created binaries are stored. diff --git a/INCHI-1-SRC/INCHI_EXE/inchi-1/gcc/makefile b/INCHI-1-SRC/INCHI_EXE/inchi-1/gcc/makefile new file mode 100644 index 0000000..691ce85 --- /dev/null +++ b/INCHI-1-SRC/INCHI_EXE/inchi-1/gcc/makefile @@ -0,0 +1,153 @@ +# This will create: +# 32-bit executable under 32-bit Windows and 32-bit Linux, +# or 64-bit executable under 64-bit Linux/AMD64 +# +# To make 32-bit executable under 64-bit Linux/AMD64, use makefile32 +# +# +# +ifndef C_COMPILER + C_COMPILER = gcc +endif +ifndef CPP_COMPILER + CPP_COMPILER = g++ +endif +ifndef LINKER + LINKER = g++ -s +endif +ifndef INCHI_EXECUTABLE_NAME + ifdef windir + EXE = .exe + else + EXE = + endif + INCHI_EXECUTABLE_NAME = inchi-1$(EXE) +endif + # === executable directory === +ifndef BIN_DIR + BIN_DIR = ../../bin/Linux +endif +INCHI_EXECUTABLE_PATHNAME = $(BIN_DIR)/$(INCHI_EXECUTABLE_NAME) +ifndef P_MAIN + P_MAIN = ../src +endif +ifndef P_BASE + P_BASE = ../../../INCHI_BASE/src +endif +#P_INCL = -I$(P_MAIN) -I$(P_BASE) +P_INCL = -I$(P_MAIN) -I$(P_BASE) +C_COMPILER_OPTIONS = $(P_INCL) -ansi -DCOMPILE_ANSI_ONLY -DTARGET_EXE_STANDALONE -O3 -c +#C_COMPILER_OPTIONS = -c $(P_INCL) -ansi -O3 -fsigned-char -ffunction-sections -fexpensive-optimizations -fstack-check -fexceptions -Wall -pedantic -Wbad-function-cast -Wreturn-type -Wformat -Wuninitialized -Wcast-align -Wshadow -Wunused -Wunused-value -Wunused-variable -Wunused-function -Wunused-parameter -Wunused-label -Wcomment -Wcast-qual -Wconversion -Wimplicit-int -Wmissing-braces -Wmissing-declarations -Wmissing-prototypes -Wredundant-decls -Wsign-compare -Wfloat-equal -Wstrict-prototypes -Wwrite-strings -Wundef -Waggregate-return -Wchar-subscripts -Wformat-nonliteral -Wnested-externs -Wsequence-point -Wpointer-arith -mfancy-math-387 -mieee-fp -mno-soft-float +ifdef windir +# no -ansi option due to reported MinGw bug + CPP_COMPILER_OPTIONS = $(P_INCL) -D_LIB -DTARGET_EXE_STANDALONE -O3 -frtti -c + #CPP_COMPILER_OPTIONS = -c $(P_INCL) -D_LIB -O3 -frtti -Wall -pedantic -Wreturn-type -Wformat -Wuninitialized -ffunction-sections -fexpensive-optimizations -fstack-check -fexceptions -Wcast-align -Wshadow -Wunused -Wunused-value -Wunused-variable -Wunused-function -fsigned-char -Wcast-qual -Wconversion -Wmissing-braces -Wredundant-decls -Wsign-compare -Wfloat-equal -Wwrite-strings -mfancy-math-387 -mieee-fp -mno-soft-float +else + CPP_COMPILER_OPTIONS = $(P_INCL) -D_LIB -DTARGET_EXE_STANDALONE -ansi -O3 -frtti -c + #CPP_COMPILER_OPTIONS = -c $(P_INCL) -D_LIB -ansi -O3 -frtti -Wall -pedantic -Wreturn-type -Wformat -Wuninitialized -ffunction-sections -fexpensive-optimizations -fstack-check -fexceptions -Wcast-align -Wshadow -Wunused -Wunused-value -Wunused-variable -Wunused-function -fsigned-char -Wcast-qual -Wconversion -Wmissing-braces -Wredundant-decls -Wsign-compare -Wfloat-equal -Wwrite-strings -mfancy-math-387 -mieee-fp -mno-soft-float +endif +#LINKER_OPTIONS = -static-libgcc +#LINKER_OPTIONS = -Wall -Wunused -Wunused-function +INCHI_SRCS = $(P_LIBR)/ichi_bns.c \ +$(P_LIBR)/ichi_io.c \ +$(P_LIBR)/ichican2.c \ +$(P_LIBR)/ichicano.c \ +$(P_LIBR)/ichicans.c \ +$(P_LIBR)/ichierr.c \ +$(P_LIBR)/ichiprt3.c \ +$(P_LIBR)/ichiisot.c \ +$(P_LIBR)/ichimake.c \ +$(P_LIBR)/ichiqueu.c \ +$(P_LIBR)/ichiring.c \ +$(P_LIBR)/ichierr.c \ +$(P_LIBR)/ichimap1.c \ +$(P_LIBR)/ichimap2.c \ +$(P_LIBR)/ichimap4.c \ +$(P_LIBR)/ichimak2.c \ +$(P_LIBR)/ichinorm.c \ +$(P_LIBR)/ichiparm.c \ +$(P_LIBR)/ichiprt1.c \ +$(P_LIBR)/ichiprt2.c \ +$(P_LIBR)/ichirvr1.c \ +$(P_LIBR)/ichirvr2.c \ +$(P_LIBR)/ichirvr3.c \ +$(P_LIBR)/ichirvr4.c \ +$(P_LIBR)/ichirvr5.c \ +$(P_LIBR)/ichirvr6.c \ +$(P_LIBR)/ichirvr7.c \ +$(P_LIBR)/ichisort.c \ +$(P_LIBR)/ichister.c \ +$(P_LIBR)/ichitaut.c \ +$(P_LIBR)/ikey_base26.c \ +$(P_LIBR)/ikey_dll.c \ +$(P_LIBR)/mol_fmt1.c \ +$(P_LIBR)/mol_fmt2.c \ +$(P_LIBR)/mol_fmt3.c \ +$(P_LIBR)/mol_fmt4.c \ +$(P_MAIN)/readinch.c \ +$(P_LIBR)/runichi.c \ +$(P_LIBR)/runichi2.c \ +$(P_LIBR)/runichi3.c \ +$(P_LIBR)/runichi4.c \ +$(P_LIBR)/sha2.c \ +$(P_LIBR)/strutil.c \ +$(P_LIBR)/util.c \ +$(P_MAIN)/dispstru.c \ +$(P_MAIN)/mol2atom.c \ +$(P_MAIN)/ichimain.c +# +INCHI_OBJS = ichi_bns.o \ +ichi_io.o \ +ichicano.o \ +ichican2.o \ +ichicans.o \ +ichierr.o \ +ichiisot.o \ +ichimake.o \ +ichimak2.o \ +ichimap1.o \ +ichimap2.o \ +ichimap4.o \ +ichinorm.o \ +ichiprt1.o \ +ichiprt2.o \ +ichiprt3.o \ +ichiqueu.o \ +ichiring.o \ +ichisort.o \ +ichister.o \ +ichitaut.o \ +ichiparm.o \ +ichiread.o \ +ichirvr1.o \ +ichirvr2.o \ +ichirvr3.o \ +ichirvr4.o \ +ichirvr5.o \ +ichirvr6.o \ +ichirvr7.o \ +ikey_base26.o \ +ikey_dll.o \ +mol_fmt1.o \ +mol_fmt2.o \ +mol_fmt3.o \ +mol_fmt4.o \ +mol2atom.o \ +readinch.o \ +runichi.o \ +runichi2.o \ +runichi3.o \ +runichi4.o \ +sha2.o \ +strutil.o \ +util.o \ +dispstru.o \ +ichimain.o +$(INCHI_EXECUTABLE_PATHNAME) : $(INCHI_OBJS) + $(LINKER) $(LINKER_OPTIONS) -o $(INCHI_EXECUTABLE_PATHNAME) $(INCHI_OBJS) -lm +%.o: $(P_BASE)/%.c + $(C_COMPILER) $(C_COMPILER_OPTIONS) $< +%.o: $(P_MAIN)/%.c + $(C_COMPILER) $(C_COMPILER_OPTIONS) $< +%.o: $(P_MAIN)/%.cpp + $(CPP_COMPILER) $(CPP_COMPILER_OPTIONS) $< diff --git a/INCHI-1-SRC/INCHI_EXE/inchi-1/gcc/makefile32 b/INCHI-1-SRC/INCHI_EXE/inchi-1/gcc/makefile32 new file mode 100644 index 0000000..3b886a9 --- /dev/null +++ b/INCHI-1-SRC/INCHI_EXE/inchi-1/gcc/makefile32 @@ -0,0 +1,134 @@ +# This will create 32-bit executable under 64-bit Linux +# +# +ifndef C_COMPILER + C_COMPILER = gcc +endif +ifndef CPP_COMPILER + CPP_COMPILER = g++ +endif +ifndef LINKER + LINKER = g++ -s +endif +ifndef INCHI_EXECUTABLE_NAME + INCHI_EXECUTABLE_NAME = inchi-1-32 +endif + # === executable directory === +ifndef BIN_DIR + BIN_DIR = ../../bin/Linux/32bit +endif +INCHI_EXECUTABLE_PATHNAME = $(BIN_DIR)/$(INCHI_EXECUTABLE_NAME) +ifndef P_MAIN + P_MAIN = ../src +endif +ifndef P_BASE + P_BASE = ../../../INCHI_BASE/src +endif +#P_INCL = -I$(P_MAIN) -I$(P_BASE) +P_INCL = -I$(P_MAIN) -I$(P_BASE) +C_COMPILER_OPTIONS = $(P_INCL) -m32 -ansi -DCOMPILE_ANSI_ONLY -DTARGET_EXE_STANDALONE -O3 -c +CPP_COMPILER_OPTIONS = $(P_INCL) -m32 -D_LIB -DTARGET_EXE_STANDALONE -ansi -O3 -frtti -c +LINKER_OPTIONS = -m32 +INCHI_SRCS = $(P_LIBR)/ichi_bns.c \ +$(P_LIBR)/ichi_io.c \ +$(P_LIBR)/ichican2.c \ +$(P_LIBR)/ichicano.c \ +$(P_LIBR)/ichicans.c \ +$(P_LIBR)/ichierr.c \ +$(P_LIBR)/ichiprt3.c \ +$(P_LIBR)/ichiisot.c \ +$(P_LIBR)/ichimake.c \ +$(P_LIBR)/ichiqueu.c \ +$(P_LIBR)/ichiring.c \ +$(P_LIBR)/ichierr.c \ +$(P_LIBR)/ichimap1.c \ +$(P_LIBR)/ichimap2.c \ +$(P_LIBR)/ichimap4.c \ +$(P_LIBR)/ichimak2.c \ +$(P_LIBR)/ichinorm.c \ +$(P_LIBR)/ichiparm.c \ +$(P_LIBR)/ichiprt1.c \ +$(P_LIBR)/ichiprt2.c \ +$(P_LIBR)/ichirvr1.c \ +$(P_LIBR)/ichirvr2.c \ +$(P_LIBR)/ichirvr3.c \ +$(P_LIBR)/ichirvr4.c \ +$(P_LIBR)/ichirvr5.c \ +$(P_LIBR)/ichirvr6.c \ +$(P_LIBR)/ichirvr7.c \ +$(P_LIBR)/ichisort.c \ +$(P_LIBR)/ichister.c \ +$(P_LIBR)/ichitaut.c \ +$(P_LIBR)/ikey_base26.c \ +$(P_LIBR)/ikey_dll.c \ +$(P_LIBR)/mol_fmt1.c \ +$(P_LIBR)/mol_fmt2.c \ +$(P_LIBR)/mol_fmt3.c \ +$(P_LIBR)/mol_fmt4.c \ +$(P_MAIN)/readinch.c \ +$(P_LIBR)/runichi.c \ +$(P_LIBR)/runichi2.c \ +$(P_LIBR)/runichi3.c \ +$(P_LIBR)/runichi4.c \ +$(P_LIBR)/sha2.c \ +$(P_LIBR)/strutil.c \ +$(P_LIBR)/util.c \ +$(P_MAIN)/dispstru.c \ +$(P_MAIN)/mol2atom.c \ +$(P_MAIN)/ichimain.c +# +INCHI_OBJS = ichi_bns.o \ +ichi_io.o \ +ichicano.o \ +ichican2.o \ +ichicans.o \ +ichierr.o \ +ichiisot.o \ +ichimake.o \ +ichimak2.o \ +ichimap1.o \ +ichimap2.o \ +ichimap4.o \ +ichinorm.o \ +ichiprt1.o \ +ichiprt2.o \ +ichiprt3.o \ +ichiqueu.o \ +ichiring.o \ +ichisort.o \ +ichister.o \ +ichitaut.o \ +ichiparm.o \ +ichiread.o \ +ichirvr1.o \ +ichirvr2.o \ +ichirvr3.o \ +ichirvr4.o \ +ichirvr5.o \ +ichirvr6.o \ +ichirvr7.o \ +ikey_base26.o \ +ikey_dll.o \ +mol_fmt1.o \ +mol_fmt2.o \ +mol_fmt3.o \ +mol_fmt4.o \ +mol2atom.o \ +readinch.o \ +runichi.o \ +runichi2.o \ +runichi3.o \ +runichi4.o \ +sha2.o \ +strutil.o \ +util.o \ +dispstru.o \ +ichimain.o +$(INCHI_EXECUTABLE_PATHNAME) : $(INCHI_OBJS) + $(LINKER) $(LINKER_OPTIONS) -o $(INCHI_EXECUTABLE_PATHNAME) $(INCHI_OBJS) -lm +%.o: $(P_BASE)/%.c + $(C_COMPILER) $(C_COMPILER_OPTIONS) $< +%.o: $(P_MAIN)/%.c + $(C_COMPILER) $(C_COMPILER_OPTIONS) $< +%.o: $(P_MAIN)/%.cpp + $(CPP_COMPILER) $(CPP_COMPILER_OPTIONS) $< diff --git a/INCHI-1-SRC/INCHI_EXE/inchi-1/readme.txt b/INCHI-1-SRC/INCHI_EXE/inchi-1/readme.txt new file mode 100644 index 0000000..025f24c --- /dev/null +++ b/INCHI-1-SRC/INCHI_EXE/inchi-1/readme.txt @@ -0,0 +1,46 @@ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +This directory contains source codes (sub-directory 'src') of inchi-1 executable. + +Sub-directory 'gcc' contains gcc/Linux makefiles. + +MS VS 2008 project is placed in sub-directory 'vc9'. + +The created binaries are saved in upper-level directory 'bin'. diff --git a/INCHI-1-SRC/INCHI/main/dispstru.c b/INCHI-1-SRC/INCHI_EXE/inchi-1/src/dispstru.c similarity index 95% rename from INCHI-1-SRC/INCHI/main/dispstru.c rename to INCHI-1-SRC/INCHI_EXE/inchi-1/src/dispstru.c index 39803e9..00e6d48 100644 --- a/INCHI-1-SRC/INCHI/main/dispstru.c +++ b/INCHI-1-SRC/INCHI_EXE/inchi-1/src/dispstru.c @@ -1,2590 +1,2586 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -/* Draw input atom -- Win32 specific */ - -#include "mode.h" - -#ifndef COMPILE_ANSI_ONLY - - -#ifdef WIN32 -#include -#include -#include - -#include "inpdef.h" -#include "util.h" -#include "dispstru.h" -#include "extr_ct.h" -#include "ichicomp.h" - -#if ( defined(_WIN64) || defined(WIN64)) /* 64-bit build */ -#define GETWINDLONG GetWindowLongPtr( hWnd, GWLP_USERDATA ) -#define SETWINDLONG SetWindowLongPtr( hWnd, GWLP_USERDATA, (long)&WinData ) -#else /* 32-bit build */ -#define GETWINDLONG GetWindowLong( hWnd, GWL_USERDATA ) -#define SETWINDLONG SetWindowLong( hWnd, GWL_USERDATA, (long)&WinData ) -#endif - - -/* Font size */ -#define FONT_NAME "Arial" /* "MS Sans Serif"; */ -/* rgb colors */ -#define CLR_BLUE RGB( 0, 0, 255) -#define CLR_GREEN RGB( 0, 128, 0) -#define CLR_RED RGB(255, 0, 0) -#define CLR_PINK RGB(255, 128, 128) -#define CLR_CYAN RGB( 0, 255, 255) -#define CLR_LTGREEN RGB(128 ,255, 128) -#define CLR_LTPURPLE RGB(255 ,0xCC, 255) -#define CLR_YELLOW RGB(255, 255, 0) -#define CLR_BLACK RGB( 0, 0, 0) -#define CLR_LTGRAY RGB(0xCC, 0xCC, 0xCC) -#define CLR_MAGENTA RGB(255,0,255) -#define CLR_WHITE RGB(255, 255, 255) - -/* local prototypes */ -HWND GetConsoleHwnd(void); - -#define MY_TIMER_ID 1 - -typedef struct Box { - int xhigh, xlow; - int yhigh, ylow; -} BOX; - - -/* local prototypes */ -int DrawBond( HDC pDC, int x1, int y1, int x2, int y2, int b_type, int b_stereo, int b_parity, int bInvertBonds, COLORREF clrPen, int nPenWidth ); -int DrawBondStereo( HDC pDC, int x1, int y1, int x2, int y2, int b_stereo, int b_highlight, int bInvertBonds, COLORREF clrPen, int nPenWidth ); -int DrawBondNoStereo( HDC pDC, int x1, int y1, int x2, int y2, int b_type, int b_highlight, COLORREF clrPen, int nPenWidth ); -int DrawBondParity( HDC pDC, int x1, int y1, int x2, int y2, int parity_mark ); -void DrawLine( HDC, int, int, int, int ); -void DrawPenColorFilledPolygon( HDC pDC, const POINT* pnt, int num ); -int DrawTextColorDot( HDC pDC ); -int DrawString( HDC pDC, char *st1, int shift, int x, int y ); -int DrawPreparedString( HDC pDC, char *st1, int shift, int x, int y, int bHighlightTheAtom ); -int DrawColorString( HDC pDC, const char *st, int xs, int ys, int bHighlightTheAtom ); - /* structure drawing */ -int DrawStructure( HDC pDC, inp_ATOM *at, INF_ATOM_DATA *inf_at_data, int num_at, int xoff, int yoff, COLORREF clrPen, int nPenWidth ); - /* all drawing, including text strings and table */ -int DrawTheInputStructure( inp_ATOM *at, INF_ATOM_DATA *inf_at_data, int num_at, - HDC pDC, int tx_off, int ty_off, int xoff, int yoff, - int width_pix, int height_pix, int bDraw, int bOrigAtom, COLORREF clrPen, int nPenWidth ); - /* calculate sizes, run drawing */ -int CreateInputStructPicture( HDC hDC, MY_WINDOW_DATA *pWinData, RECT *rc, int bPrint, AT_NUMB nNewEquLabel ); - -void FreeWinData( MY_WINDOW_DATA* pWinData ); -void InpStructureMarkEquComponents( MY_WINDOW_DATA *pWinData, AT_NUMB nNewEquLabel, - inp_ATOM *at0, inp_ATOM *at1, inf_ATOM *inf_at, int num_at ); -int MyTextOutABC( const char *p, int iFst, int iLst, HDC pDC ); - -/* window procedure */ -LRESULT CALLBACK WndProcDisplayInputStructure(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); - -/* main drawing function: create window, save drawing parameters in the window */ -int DisplayInputStructure( char *szOutputString, inp_ATOM *at, INF_ATOM_DATA *inf_at_data, int num_at, DRAW_PARMS *dp /*, int bTaut, unsigned long ulDisplTime, long *rcPict, int nFontSize*/ ); - - -int GetFontHeight( HDC ); -int GetFontAscent( HDC ); -int GetFontDescent( HDC pDC ); -int GetFontAveWidth( HDC ); -int GetStringWidth( HDC pDC, char *pString ); -int GetOneCharInStringWidth( HDC pDC, const char *pString ); -int MoveHydrogenAtomToTheLeft( char *s, int start, int H ); - -int nRound( double X ); -double RoundDouble(double X); -void roundoff_coord( double dx1, double dx2, int *new_ix1, int *new_ix2 ); -BOOL ReallySetForegroundWindow(HWND hWnd); -HWND GetConsoleHwnd(void); - - -/*********************************************************************/ -char szWindowClassName[] = "INChI_DrawStructWnd"; - -/*********************************************************************/ - -void PrintFileName( const char *fmt, FILE *output_file, const char *szFname ) -{ - char szBuf[_MAX_PATH]; - long lBufLen = sizeof(szBuf); - long lReqBufLen; - char *pName; - const char *p; - - lReqBufLen = GetFullPathName( szFname, lBufLen, szBuf, &pName ); - - if ( lReqBufLen && lReqBufLen < lBufLen ) { - p = szBuf; - } else { - p = szFname; - } - fprintf( output_file, fmt, p ); -} - -/*********************************************************************/ -BOOL ReallySetForegroundWindow(HWND hWnd) -{ - BOOL retVal = FALSE; - HWND hForegroundWnd; - if ( hWnd && IsWindow(hWnd) && (hForegroundWnd = GetForegroundWindow()) ) { - DWORD dwWindowThreadProcessId = GetWindowThreadProcessId(hForegroundWnd, NULL); - DWORD dwCurrentThreadId = GetCurrentThreadId(); - AttachThreadInput(dwWindowThreadProcessId, dwCurrentThreadId, TRUE); - retVal = SetForegroundWindow(hWnd); - AttachThreadInput(dwWindowThreadProcessId, dwCurrentThreadId, FALSE); - } - return retVal; -} -/*********************************************************************/ -HWND GetConsoleHwnd(void) -{ -#define MY_BUFSIZE 1024 /* Buffer size for console window titles. */ - HWND hwndFound; /* This is what is returned to the caller. */ - char pszNewWindowTitle[MY_BUFSIZE]; /* Contains fabricated */ - /* WindowTitle. */ - char pszOldWindowTitle[MY_BUFSIZE]; /* Contains original */ - /* WindowTitle. */ - - /* Fetch current window title. */ - - GetConsoleTitle(pszOldWindowTitle, MY_BUFSIZE); - - /* Format a "unique" NewWindowTitle. */ - - wsprintf(pszNewWindowTitle,"InChITmpWnd%ul/%ul", - (unsigned long)GetTickCount(), - (unsigned long)GetCurrentProcessId()); - - /* Change current window title. */ - SetConsoleTitle(pszNewWindowTitle); - - /* Ensure window title has been updated. */ - - Sleep(40); - - /* Look for NewWindowTitle. */ - - hwndFound=FindWindow(NULL, pszNewWindowTitle); - - /* download INChI packages - ShellExecute(hwndFound, "open", "http://www.iupac.org/inchi", "", "C:\\", SW_SHOWNORMAL); - */ - - /* Restore original window title. */ - - SetConsoleTitle(pszOldWindowTitle); - - return(hwndFound); -} -/****************************************************************************/ -int GetFontHeight( HDC pDC ) -{ - TEXTMETRIC TextMetric; - - GetTextMetrics( pDC, &TextMetric ); - - return TextMetric.tmHeight; -} -/****************************************************************************/ -int GetFontAscent( HDC pDC ) -{ - TEXTMETRIC TextMetric; - - GetTextMetrics( pDC, &TextMetric ); - - return TextMetric.tmAscent; -} -/****************************************************************************/ -int GetFontDescent( HDC pDC ) -{ - TEXTMETRIC TextMetric; - - GetTextMetrics( pDC, &TextMetric ); - - return TextMetric.tmDescent; -} - -/****************************************************************************/ -int GetFontAveWidth( HDC pDC) -{ - TEXTMETRIC TextMetric; - - GetTextMetrics( pDC, &TextMetric ); - - return TextMetric.tmAveCharWidth; -} -/****************************************************************************/ -int GetSubstringWidth( HDC pDC, int len, char *pString) -{ - SIZE Size; - int widthABC, i; - ABC abc; - GetTextExtentPoint32( pDC, pString, len, &Size ); - for ( i = widthABC = 0; i < len; i ++ ) { - if ( GetCharABCWidths( pDC, /* handle to DC */ - (int)pString[i], /* first character in range */ - (int)pString[i], /* last character in range */ - &abc ) /* array of character widths */ - ) { - widthABC += (int)abc.abcB + abs(abc.abcA) + abs(abc.abcC); - } else { - break; - } - } - if ( widthABC > Size.cx ) { - return (widthABC + 2*Size.cx)/3; /* hunch */ - } - return Size.cx; -} -/****************************************************************************/ -void GetTextSize( HDC pDC, int len, char *pString, int *width, int *height ) -{ - SIZE Size; - - GetTextExtentPoint32( pDC, pString, len, &Size ); - *width = Size.cx; - *height = Size.cy; -} -/****************************************************************************/ -void GetVertTextSize( HDC pDC, int len, char *pString, int *width, int *height ) -{ - SIZE Size; - TEXTMETRIC TextMetric; - int i; - GetTextMetrics( pDC, &TextMetric ); - *height = len * TextMetric.tmHeight; - *width = 0; - for ( i = 0; i < len; i ++ ) { - GetTextExtentPoint32( pDC, &pString[i], 1, &Size ); - *width = inchi_max( *width, (int)Size.cx ); - } -} -/****************************************************************************/ -BOOL TextOutVert( - HDC pDC, /* handle to DC */ - int nXStart, /* x-coordinate of starting position */ - int nYStart, /* y-coordinate of starting position */ - LPCTSTR lpString, /* character string */ - int cbString, /* number of characters */ - int cell_width /* width for center alignment */ - ) { - TEXTMETRIC TextMetric; - int i, dy, ret, char_width; - GetTextMetrics( pDC, &TextMetric ); - dy = TextMetric.tmHeight; - for ( i = 0, ret = 1; ret && i < cbString; nYStart += dy, i ++ ) { - char_width = GetOneCharInStringWidth( pDC, lpString+i ); - ret = TextOut( pDC, nXStart+(cell_width-char_width)/2, nYStart, lpString+i, 1 ); - } - return ret; -} -/****************************************************************************/ -BOOL TextOutHoriz( - HDC pDC, /* handle to DC */ - int nXStart, /* x-coordinate of starting position */ - int nYStart, /* y-coordinate of starting position */ - LPCTSTR lpString, /* character string */ - int cbString, /* number of characters */ - int cell_width - ) { - int dX = (cell_width && cbString == 1)? (cell_width - GetOneCharInStringWidth( pDC, lpString ))/2:0; - return TextOut( pDC, nXStart+dX, nYStart, lpString, cbString ); -} -/****************************************************************************/ -int GetStringWidth( HDC pDC, char *pString) -{ - SIZE Size; - - GetTextExtentPoint32( pDC, pString, (int) strlen( pString ), &Size ); - return Size.cx; -} - -/****************************************************************************/ -int GetOneCharInStringWidth( HDC pDC, const char *pString) -{ - SIZE Size; - GetTextExtentPoint32( pDC, pString, 1, &Size ); - - return Size.cx; -} - - -/****************************************************************************/ -int DrawStructure( HDC pDC, inp_ATOM *at, INF_ATOM_DATA *inf_at_data, int num_at, int xoff, int yoff, COLORREF clrPen, int nPenWidth ) -{ - int i, next, k; - int j, r, shift, b_parity=0, bDraw; - char str[64], *atname; /*str[sizeof(st->str[0])+1]; */ - inf_ATOM *inf_at = inf_at_data? inf_at_data->at : NULL; - int bUseInvFlags = (!inf_at_data)? 0 : inf_at_data->pStereoFlags? 1 : 2; - int bInvertBonds = 0; - /* - int bInvertBonds = inf_at_data && (inf_at_data->StereoFlags & INF_STEREO_INV); - */ - /* draw all straight lines (bonds) */ - - for ( i = 0; i < num_at; i ++ ) { - /* draw atom #i. */ - for ( j = 0; j < at[i].valence; j++ ) { - next = at[i].neighbor[j]; - bDraw = 1; - /* normally draw bonds if next > i; exception: disconnected terminal hydrogen atoms */ - if ( next < i ) { - /* check if it is a disconnected terminal atom. Disconnected atoms */ - /* have bonds to the rest of the structure; the bond from the rest */ - /* of the structure to the removed terminal atom has been removed. */ - for ( r = 0; r < at[next].valence; r ++ ) { - if ( at[next].neighbor[r] == i ) { - bDraw = 0; - break; - } - } - } - if ( bDraw ) { - /* at[i].bond_stereo[j] is negative if the pointing wedge */ - /* of a stereo bond is at the at[next] atom */ - if ( inf_at ) { - for ( k =0, b_parity = 0; k < MAX_STEREO_BONDS && inf_at[i].cStereoBondParity[k]; k ++ ) { - if ( inf_at[i].cStereoBondNumber[k] == j ) { - b_parity = inf_at[i].cStereoBondParity[k]; - if ( inf_at[i].cStereoBondWarning[k] ) { - b_parity = -b_parity; - } - break; - } - } - } - switch( bUseInvFlags ) { - case 1: - if ( at[i].component <= inf_at_data->num_components ) { - bInvertBonds = (0 != (inf_at_data->pStereoFlags[at[i].component] & INF_STEREO_INV)); - } else { - bInvertBonds = 0; - } - break; - case 2: - bInvertBonds = 0 != (inf_at_data->StereoFlags & INF_STEREO_INV); - break; - } - DrawBond( pDC, nRound(at[i].x+xoff), nRound(at[i].y+yoff), - nRound(at[next].x+xoff), nRound(at[next].y+yoff), - at[i].bond_type[j], at[i].bond_stereo[j], b_parity, bInvertBonds, clrPen, nPenWidth); - } - } - } - /* write all strings (heteroatoms + H) */ - if ( inf_at ) { - for ( i = 0; i < num_at; i++ ) { - strcpy( str, inf_at[i].at_string ); - DrawPreparedString( pDC, str, -inf_at[i].DrawingLabelLeftShift, nRound(at[i].x+xoff), nRound(at[i].y+yoff), inf_at[i].cHighlightTheAtom ); - } - } else { - /* version which does not use inf_at */ - for ( i = 0; i < num_at; i++ ) { - - /* the direction of the shift: */ - shift = at[i].bDrawingLabelLeftShift; - /* input structure before normalizing: */ - /* terminal H atoms have not been disconnected; */ - /* isotopic H atoms numbers have not been added to num_H */ - atname = at[i].elname; - j = 0; - k = 0; - if ( at[i].iso_atw_diff && (!atname[0] || isupper(UCINT atname[0])) ) { - int atw = get_atw_from_elnum( (int)at[i].el_number ); - if ( atw ) { - k += sprintf( str+k, "^%d", atw+at[i].iso_atw_diff-(at[i].iso_atw_diff>0)); - } else { - k += sprintf( str+k, "^+%d", at[i].iso_atw_diff-(at[i].iso_atw_diff>0)); - } - } - /* obsolete section, this never happens for now */ - if ( atname[0] && atname[0] < ' ' && atname[1] ) { - /* special encoding. The 1st byte contains the 1st delimiter; next delimiters are '/'. */ - /* this allows to draw up to 5 numbers from 1..255 range. */ - int t; - int ch = ' ' + atname[0]; /* delimiter */ - k += sprintf( str+k, "%u", (unsigned)(unsigned char)atname[1]); - for ( t = 2; t < sizeof(at->elname); t ++ ) { - if ( atname[t] ) { - if ( t == 3 ) - ch = ','; /* comma after 2nd number to separate tautomer group info */ - if ( ch != '/' && ch != ',' ) - k += sprintf( str+k, "(%c)%u", ch, (unsigned)(unsigned char)atname[t]); - else - k += sprintf( str+k, "%c%u", ch, (unsigned)(unsigned char)atname[t]); - - ch = '/'; - } - } - } else { - /* this is the main section to display hydrogen atoms, charges, radicals */ - strncpy( str+k, atname+j, sizeof(at->elname)-j ); - str[sizeof(at->elname)+k-j] = '\0'; - k = strlen( str ); - if ( at[i].num_H ) { - strcat( str, "H" ); - k ++; - if ( at[i].num_H > 1 ) { - k += sprintf( str+k, "%d", (int)at[i].num_H ); - } - } - for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { - if ( at[i].num_iso_H[j] ) { - if ( j == 0 || j !=1 && j != 2 ) { - k += sprintf( str+k, "^%dH", j+1 ); - } else { - k += sprintf( str+k, j == 1? "D" : "T" ); - } - if ( at[i].num_iso_H[j] > 1 ) { - k += sprintf( str+k, "%d", (int)at[i].num_iso_H[j] ); - } - } - } - if ( abs(at[i].charge) > 1 ) - sprintf( str+k, "%+d", at[i].charge ); - else - if ( abs(at[i].charge) == 1 ) - strcat( str, at[i].charge>0? "+" : "-" ); - if ( at[i].radical ) - strcat( str, at[i].radical==RADICAL_SINGLET? ":" : - at[i].radical==RADICAL_DOUBLET? "." : - at[i].radical==RADICAL_TRIPLET? "^^" : "?"); - k = strlen(str); - sprintf( str+k, "/%d", i+1 ); /* atom ordering number, 1,2,... */ - } - - DrawString( pDC, str, shift, nRound(at[i].x+xoff), nRound(at[i].y+yoff) ); - } - } - return ( 1 ); -} - -/****************************************************************************/ -int DrawBond( HDC pDC, int x1, int y1, int x2, int y2, int b_type, int b_stereo, int b_parity, int bInvertBonds, COLORREF clrPen, int nPenWidth ) -{ - int abs_value_of_stereo = abs( b_stereo ); - int bond_parity_mark = b_type & BOND_MARK_PARITY; - int bond_highlight = b_type & BOND_MARK_HIGHLIGHT; - int ret; - b_type ^= (bond_parity_mark | bond_highlight); - - if ( !b_stereo || - /* => no stereo */ - b_type== BOND_TYPE_DOUBLE && abs_value_of_stereo!=STEREO_DBLE_EITHER || - /* => ignore unknown double bond stereo */ - b_type >= BOND_TYPE_TRIPLE || - /* => ignore bond stereo in case of triple bonds */ - b_type==BOND_TYPE_SINGLE && !(abs_value_of_stereo==STEREO_SNGL_UP || - abs_value_of_stereo==STEREO_SNGL_EITHER || - abs_value_of_stereo==STEREO_SNGL_DOWN) - /* => ignore unknown single bond stereo */ ) { - /*if ( b_type || !abs_value_of_stereo || (abs_value_of_stereo != 1 && abs_value_of_stereo != 4 && abs_value_of_stereo != 6) ) */ - ret = DrawBondNoStereo( pDC, x1, y1, x2, y2, b_type, 0!=bond_highlight, clrPen, nPenWidth ); - } else { - ret = DrawBondStereo( pDC, x1, y1, x2, y2, b_stereo, 0!=bond_highlight, bInvertBonds, clrPen, nPenWidth ); - } - if ( b_parity /* bond_parity_mark*/ ) { - DrawBondParity( pDC, x1, y1, x2, y2, b_parity ); - } - return ret; -} -/****************************************************************************/ -double RoundDouble(double X) -{ - return ((X)>0.0? floor((X)+0.5):-floor(0.4999999-(X))); -} -/****************************************************************************/ -int nRound( double X ) -{ - return (int)RoundDouble( X ); -} -/****************************************************************************/ -void roundoff_coord( double dx1, double dx2, int *new_ix1, int *new_ix2 ) -{ - int new_x1; - int new_x2; - int diff = (int) RoundDouble(dx2-dx1); - - if ( dx1 >= dx2 ) { - new_x1 = (int) RoundDouble(dx1); - new_x2 = new_x1 + diff; - } else { - new_x2 = (int) RoundDouble(dx2); - new_x1 = new_x2 - diff; - } - if ( new_ix1 ) - *new_ix1 = new_x1; - if ( new_ix2 ) - *new_ix2 = new_x2; -} -/****************************************************************************/ -void DrawPenColorFilledPolygon( HDC pDC, const POINT* pnt, int num ) -{ - LOGPEN LogPen; - HPEN penNew=(HPEN)GetStockObject(BLACK_PEN); - HPEN penOld=(HPEN)SelectObject(pDC, penNew); /* get current pen */ - HBRUSH brushNew, brushOld; - int ret; - ret = GetObject( penOld, sizeof(LogPen), &LogPen ); - SelectObject(pDC, penOld); - /* do not need to delete stock object penNew */ - if ( ret ) { - if ( brushNew = CreateSolidBrush(LogPen.lopnColor) ) { - brushOld = (HBRUSH)SelectObject( pDC, brushNew ); - Polygon( pDC, pnt, num ); - SelectObject( pDC, brushOld); - DeleteObject( brushNew ); - } - } -} -/****************************************************************************/ -int DrawTextColorDot( HDC pDC ) -{ - COLORREF clrColor = GetTextColor( pDC ); - int width = GetSubstringWidth( pDC, 1, "."); - HPEN penNew = CreatePen(PS_SOLID, 0, clrColor); - HBRUSH brushNew = CreateSolidBrush(clrColor); - /*int hfont = GetFontHeight( pDC ); */ - POINT pt; - int bSuccess = 0; - int nVertShift; - int nTextAlign = (TA_BOTTOM | TA_TOP | TA_BASELINE) & GetTextAlign( pDC ); - if ( TA_BASELINE == nTextAlign ) { - nVertShift = 0; - } else - if ( TA_BOTTOM == nTextAlign ) { - nVertShift = -GetFontDescent( pDC ); - } else - if ( TA_TOP == nTextAlign ) { - nVertShift = GetFontHeight( pDC ); /*GetFontAscent( pDC ); */ - } else { - nVertShift = GetFontHeight( pDC ); - } - if ( width % 2 ) { - width ++; - } - - GetCurrentPositionEx( pDC, &pt ); - if ( penNew && brushNew ) { - HPEN penOld = (HPEN) SelectObject( pDC, penNew ); - HBRUSH brushOld = (HBRUSH)SelectObject( pDC, brushNew ); - bSuccess = Ellipse( pDC, pt.x+width/2, pt.y - (3*width)/2+nVertShift, pt.x + (3*width)/2, pt.y-width/2+nVertShift ); - SelectObject( pDC, penOld ); - SelectObject( pDC, brushOld ); - if ( bSuccess ) { - MoveToEx( pDC, pt.x+(3*width)/2, pt.y, NULL ); - } - } - if ( penNew ) - DeleteObject( penNew ); - if ( brushNew ) - DeleteObject( brushNew ); - return bSuccess; -} -/****************************************************************************/ -int DrawBondStereo( HDC pDC, int x1, int y1, int x2, int y2, int b_stereo, int b_highlight, int bInvertBonds, COLORREF clrPen, int nPenWidth ) -{ - double lambda, mult, ax1, ay1, bond_sep, bond_width, bond_step, dx, dy, dx1, dy1; - int ix, ix1, iy, iy1, ix21, ix22, iy21, iy22; - int i, n; - int hfont = GetFontHeight( pDC ); - HPEN hHighlightPen=0, hOldPen = 0; - COLORREF clrHighlight = CLR_MAGENTA; - - - POINT pnt[4]; - - if ( b_highlight ) { - hHighlightPen = CreatePen( PS_SOLID, nPenWidth, clrHighlight ); - if ( !hHighlightPen ) - hHighlightPen = (HPEN)GetStockObject( BLACK_PEN ); /*should not fail */ - hOldPen = (HPEN)SelectObject( pDC, hHighlightPen ); - } - /* the wedge (pointed) end is at (x1, y1) unless b_stereo is negative */ - if ( b_stereo < 0 ) { - i = x1; - x1 = x2; - x2 = i; - i = y1; - y1 = y2; - y2 = i; - b_stereo = -b_stereo; /* 1=Up (solid triangle), 6=Down (Dashed triangle), 4=Either (zigzag triangle) */ - } - if ( bInvertBonds ) { - switch ( b_stereo ) { - case STEREO_SNGL_UP: - b_stereo = STEREO_SNGL_DOWN; - break; - case STEREO_SNGL_DOWN: - b_stereo = STEREO_SNGL_UP; - break; - } - } - - dx = x2 - x1; - dy = y2 - y1; - lambda = dx*dx + dy*dy; - if ( lambda == 0.0 ) - return 0; - lambda = sqrt( lambda ); /* bond length */ - - bond_width = hfont / 6.0; - if ( bond_width < 2.0 ) - bond_width = 2.0; /* half-width */ - /* from x1 to x2 */ - bond_width = inchi_min( bond_width, lambda / 8.0 ); - bond_width = floor( 2.0*bond_width + 0.5 )/2; /* half of actual bond width */ - - bond_step = hfont / 4.0; - bond_step = inchi_min( bond_step, lambda/6.0 ); - bond_step = inchi_max( bond_step, 3 ); - - if ( b_stereo == 3 ) { - bond_width *= 2.0; /* otherwise looks strange */ - } - - ax1 = -bond_width * dy / lambda; - ay1 = bond_width * dx / lambda; - - switch( b_stereo ) { - - case STEREO_SNGL_UP: /* Up */ - roundoff_coord( x2 - ax1, x2 + ax1, &ix, &ix1 ); - roundoff_coord( y2 - ay1, y2 + ay1, &iy, &iy1 ); - pnt[0].x = x1; - pnt[0].y = y1; - - pnt[1].x = ix1; - pnt[1].y = iy1; - - pnt[2].x = ix; - pnt[2].y = iy; - - DrawPenColorFilledPolygon( pDC, pnt, 3 ); - break; - case STEREO_DBLE_EITHER: /* cis or trans double bond */ - roundoff_coord( x2 - ax1, x2 + ax1, &ix21, &ix22 ); - roundoff_coord( y2 - ay1, y2 + ay1, &iy21, &iy22 ); - - roundoff_coord( x1 - ax1, x1 + ax1, &ix, &ix1 ); - roundoff_coord( y1 - ay1, y1 + ay1, &iy, &iy1 ); - - DrawLine( pDC, ix21, iy21, ix1, iy1 ); - DrawLine( pDC, ix22, iy22, ix, iy ); - - break; - - case STEREO_SNGL_EITHER: /* either */ - - n = (int)floor( lambda / bond_step ); - n = inchi_max( n, 4 ); - dx = dy = 0.0; - ix = iy = 0; - for ( i = 1; i <= 3*n/2; i ++ ) { - - mult = (double)i/(double)n/1.5; - dx1 = (x2-x1)*mult; - dy1 = (y2-y1)*mult; - if ( i % 2 ) { - dx1 += ax1*mult; - dy1 += ay1*mult; - } else { - dx1 -= ax1*mult+ax1/bond_width; - dy1 -= ay1*mult+ay1/bond_width; - } - - roundoff_coord( dx, dx1, NULL, &ix1 ); - roundoff_coord( dy, dy1, NULL, &iy1 ); - - /*line( win, x1 + ix, y1 + iy, x1 + ix1, y1 + iy1 ); */ - DrawLine( pDC, x1 + ix, y1 + iy, x1 + ix1, y1 + iy1 ); - dx = dx1; - dy = dy1; - ix = ix1; - iy = iy1; - /*line( win, x1 + ( int ) dx, y1 + ( int ) dy, x1 + ( int ) dx1, y1 + ( int ) dy1 ); */ - } - break; - - case STEREO_SNGL_DOWN: /* Down */ - - n = (int)floor( lambda / bond_step ); - n = inchi_max( n, 4 ); - - for ( i = 0; i <= n; i ++ ) { - mult = (double)i/(double)n; - - dx = dx1 = (x2-x1)*mult; /* x1,y1 offset */ - dy = dy1 = (y2-y1)*mult; - - mult = (double)(i)/(double)(n); - - bond_sep = ax1*mult; - dx += bond_sep; - dx1 -= bond_sep+ax1/bond_width; - - bond_sep = ay1*mult; - dy += bond_sep; - dy1 -= bond_sep+ay1/bond_width; - - roundoff_coord( dx, dx1, &ix, &ix1 ); - roundoff_coord( dy, dy1, &iy, &iy1 ); - - DrawLine( pDC, x1 + ix, y1 + iy, x1 + ix1, y1 + iy1 ); - } - break; - } - /* cleanup */ - if ( hOldPen ) - SelectObject( pDC, hOldPen ); - if ( hHighlightPen ) - DeleteObject( hHighlightPen ); - - return 0; -} -/****************************************************************************/ -int DrawBondParity( HDC pDC, int x1, int y1, int x2, int y2, int parity_mark0 ) -{ -/* int hfont = GetFontHeight( pDC ); */ - int xs, ys, width, height, parity_mark; - char *p; - COLORREF clrTextOld; - - if ( parity_mark0 < 0 ) { - parity_mark = -parity_mark0; - clrTextOld = SetTextColor( pDC, CLR_RED ); - } else { - parity_mark = parity_mark0; - } - - if ( parity_mark == BOND_MARK_ODD ) p = "(-)"; else - if ( parity_mark == BOND_MARK_EVEN ) p = "(+)"; else - if ( parity_mark == BOND_MARK_UNDF ) p = "(?)"; else - if ( parity_mark == BOND_MARK_UNKN ) p = "(u)"; else - if ( parity_mark == BOND_MARK_ERR ) p = "(!)"; else return 0; - - if ( abs( x2-x1 ) > 10 * abs(y2 - y1) ) { - /* almost horizontal bond; draw parity closer (1:2) to the right end */ - if ( x2 < x1 ) { - int tmp; - tmp = x2; - x2 = x1; - x1 = tmp; - tmp = y2; - y2 = y1; - y1 = tmp; - } - xs = 2*x1 + (4*(x2-x1))/3; /* 2/3 shift */ - ys = 2*y1 + (4*(y2-y1))/3; /* 2/3 shift */ - } else { - xs = x1 + x2; /* middle */ - ys = y1 + y2; /* middle */ - } - GetTextSize( pDC, (int) strlen(p), p, &width, &height ); - /* - width = GetFontAveWidth( pDC); - height = GetFontAscent( pDC ); - */ - xs = (xs - width )/2; - ys = (ys - height )/2; - - TextOut( pDC, xs, ys, p, 3 ); - - if ( parity_mark0 < 0 ) { - SetTextColor( pDC, clrTextOld ); - } - - return 0; -} -/****************************************************************************/ -int DrawBondNoStereo( HDC pDC, int x1, int y1, int x2, int y2, int b_type, int b_highlight, COLORREF clrPen, int nPenWidth ) -{ - double lambda, ax1, ax2, ay1, ay2, bond_sep; - int hfont = GetFontHeight( pDC ); - int ret = 0; - HPEN hSolidPen=0, hDashedPen=0, hLongDashedPen=0, hHighlightPen = 0, hOldPen=0; - char c=0, r=0, l=0, bDashed=1, bLongDashed=0, i; - COLORREF clr = clrPen; - - switch( b_type ) { /* c=center, l=left, r=right */ - case 0: - c = 'D'; - b_highlight = 1; - break; - case BOND_SINGLE: /* S=solid, D=dashed, L=long dashed(Green) */ - c = 'S'; - bDashed = 0; - break; - case BOND_DOUBLE: - l = r = 'S'; - bDashed = 0; - break; - case BOND_TRIPLE: - l = r = c = 'S'; - bDashed = 0; - break; - case BOND_TAUTOM: - l = r = 'D'; - break; - case BOND_ALTERN: /* 1 or 2 */ - l = 'S'; - r = 'D'; - break; - case BOND_ALT12NS: /* 1 or 2, non-stereo */ - l = 'L'; - r = 'D'; - bLongDashed = 1; - break; - case BOND_ALT_13: /* 1 or 3 */ - l = 'S'; - c = r = 'D'; - break; - case BOND_ALT_23: /* 2 or 3 */ - l = c = 'S'; - r = 'D'; - break; - case BOND_ALT_123: /* 1 or 2 or 3 */ - c = 'S'; - l = r = 'D'; - break; - default: - c = 'D'; - break; - } - if ( bLongDashed ) { - clr = CLR_GREEN; - } - /* -- debug -- - if ( c && l && r ) { - int stop = 1; - } - */ - if ( b_highlight ) { - clr = CLR_MAGENTA; - if ( c=='S' || l == 'S' || r == 'S' ) { - hHighlightPen = CreatePen( PS_SOLID, nPenWidth, clr ); - if ( !hHighlightPen ) - hHighlightPen = (HPEN)GetStockObject( BLACK_PEN ); /*should not fail */ - hOldPen = (HPEN)SelectObject( pDC, hHighlightPen ); - } - } - - if ( bDashed ) { - hDashedPen = CreatePen( PS_DOT, 1, clr ); - if ( !hDashedPen ) - hDashedPen = (HPEN)GetStockObject( BLACK_PEN ); /*should not fail */ - } - if ( bLongDashed ) { - hLongDashedPen = CreatePen( PS_DASH, 1, clr ); - if ( !hLongDashedPen ) - hLongDashedPen = (HPEN)GetStockObject( BLACK_PEN ); /*should not fail */ - } - - /* draw lines between the atoms */ - if ( c == 'S' ) { - DrawLine( pDC, x1, y1, x2, y2 ); - } else - if ( c == 'D' ) { - hSolidPen = (HPEN)SelectObject( pDC, hDashedPen ); - DrawLine( pDC, x1, y1, x2, y2 ); - hDashedPen = (HPEN)SelectObject( pDC, hSolidPen ); - } else - if ( c == 'L' ) { - hSolidPen = (HPEN)SelectObject( pDC, hLongDashedPen ); - DrawLine( pDC, x1, y1, x2, y2 ); - hLongDashedPen = (HPEN)SelectObject( pDC, hSolidPen ); - } - - /* draw lines parallel to line between bonds */ - if ( l || r ) { - - if ( b_type == BOND_DOUBLE ) { - bond_sep = hfont / 12.0; - if( ( x1 == x2 ) || ( y1 == y2 ) ) { - if ( bond_sep < 1.0 ) bond_sep = 1.0; - } else { - if ( bond_sep < 2.0 ) bond_sep = 2.0; - } - } else { - bond_sep = hfont / 6.0; - if( ( x1 == x2 ) || ( y1 == y2 ) ) { - if ( bond_sep < 2.0 ) bond_sep = 2.0; - } else { - if ( bond_sep < 4.0 ) bond_sep = 4.0; - } - } - - lambda = ( ( double ) ( x2 - x1 ) ) * ( x2 - x1 ) + - ( ( double ) ( y2 - y1 ) ) * ( y2 - y1 ); - - if ( lambda > 0.0 ) - lambda = bond_sep / sqrt( lambda ); - else { - ret = 1; - goto exit_function; - } - for ( i = 0; i < 2; i ++, lambda = -lambda ) { - c = i? r : l; - ax1 = lambda * ( y1 - y2 ) + x1; - ay1 = lambda * ( x2 - x1 ) + y1; - ax2 = lambda * ( y1 - y2 ) + x2; - ay2 = lambda * ( x2 - x1 ) + y2; - - if ( c == 'S' ) { - DrawLine( pDC, ( int ) ax1, ( int ) ay1, ( int ) ax2, ( int ) ay2 ); - } else - if ( c == 'D' ) { - hSolidPen = (HPEN)SelectObject( pDC, hDashedPen ); - DrawLine( pDC, ( int ) ax1, ( int ) ay1, ( int ) ax2, ( int ) ay2 ); - hDashedPen = (HPEN)SelectObject( pDC, hSolidPen ); - } else - if ( c == 'L' ) { - hSolidPen = (HPEN)SelectObject( pDC, hLongDashedPen ); - DrawLine( pDC, x1, y1, x2, y2 ); - hLongDashedPen = (HPEN)SelectObject( pDC, hSolidPen ); - } - } - - } - -exit_function: - /* make sure DC has its initial pen */ - if ( hSolidPen ) - SelectObject( pDC, hSolidPen ); - if ( hOldPen ) - SelectObject( pDC, hOldPen ); - /* delete all newly created pens */ - if ( hDashedPen ) - DeleteObject( hDashedPen ); - if ( hLongDashedPen ) - DeleteObject( hLongDashedPen ); - if ( hHighlightPen ) - DeleteObject( hHighlightPen ); - - return ( ret ); -} -/****************************************************************************/ -int MoveHydrogenAtomToTheLeft( char *s, int start, int H ) -{ - int len, c, num_alpha; - char szBuffer[16]; - char *pH = strchr( s+start, H ); - for ( pH = s+start, num_alpha = 0; (c= UCINT*pH) && c != H && c != '/'; pH ++ ) { - num_alpha = isalpha( c ); - } - if ( c == H && num_alpha ) { /* do not search beyond the first slash */ - for ( len = 1; pH[len] && isdigit( UCINT pH[len] ); len ++ ) - ; - if ( len >= (int)sizeof(szBuffer) ) - return start; /* too long string */ - memcpy( szBuffer, pH, len ); - memmove( s+len, s, pH - s ); - memmove( s, szBuffer, len ); - return start+len; /* (pH-s)+i; */ - } - return start; -} -/****************************************************************************/ -int MyTextOutABC( const char *p, int iFst, int iLst, HDC pDC ) -{ - ABC abc; - POINT pt; - if ( iFst > iLst || iFst < 0 || iLst < 0 ) - return 0; - GetCurrentPositionEx( pDC, &pt ); - if ( GetCharABCWidths( - pDC, /* handle to DC */ - (int)p[iFst], /* first character in range */ - (int)p[iFst], /* last character in range */ - &abc /* array of character widths */ - ) && abc.abcA < 0 ) { - pt.x -= abc.abcA; - MoveToEx( pDC, pt.x, pt.y, NULL ); - } - TextOut( pDC, pt.x, pt.y, p+iFst, iLst-iFst+1 ); - if ( GetCharABCWidths( - pDC, /* handle to DC */ - (int)p[iLst], /* first character in range */ - (int)p[iLst], /* last character in range */ - &abc /* array of character widths */ - ) && abc.abcC < 0 ) { - GetCurrentPositionEx( pDC, &pt ); - pt.x -= abc.abcC; - MoveToEx( pDC, pt.x, pt.y, NULL ); - } - return 1; -} -/****************************************************************************/ -int DrawColorString( HDC pDC, const char *st, int xs, int ys, int bHighlightTheAtom ) -{ - int afont = GetFontAscent( pDC ); - COLORREF clrBk = CLR_WHITE; - int nNumSlash = 0; - COLORREF OrigBkColor = CLR_WHITE; - COLORREF OrigTxColor = GetTextColor( pDC ); - COLORREF NewBkColor; - int bWritingAtomStringColored = 0; - /* Draw the string character by character. */ - /* For each character within the first part of the string */ - /* make a decision if it is a subscript or a superscript */ - /* The first part ends with '/'=start of the canonical number or '(' = start of the parity mark */ - /* The superscript first character is ^ or + or - or . (. initiates double shift) */ - /* The the superscript ends with not the first character and not a digit or the end of the first part */ - /* The first character of a subscript (if it is not in the superscript) is a digit */ - /* The subscript ends a non-digit */ - const char *p=st; - UINT uPrevTextAlign = SetTextAlign( pDC, TA_UPDATECP); - POINT pt, pt0; - int i, i0, len, bSuperscript, bSubscript, bWritingAtomString, nShift, nShift2; - int bNewBkColor, bMoveToEx, bBypassCurrentChar; - nShift = afont/2; - nShift2 = nShift/2; - if ( bHighlightTheAtom ) { - clrBk = SetBkColor( pDC, CLR_LTPURPLE ); - } else - if ( strchr(st, '~') ) { - clrBk = SetBkColor( pDC, CLR_CYAN ); - } /* else { == for debugging == - clrBk = SetBkColor( pDC, CLR_LTGRAY ); - } */ - if ( st[0] == '!' ) { - OrigTxColor = SetTextColor( pDC, CLR_RED ); - bWritingAtomStringColored = 1; - p ++; - } - bSuperscript = bSubscript = 0; - bWritingAtomString = 1; - MoveToEx( pDC, xs, ys, NULL ); - for ( i = i0 = 0, len = strlen( p ); i < len; i ++ ) { - bNewBkColor = 0; - bMoveToEx = 0; - if ( bWritingAtomString ) { - GetCurrentPositionEx( pDC, &pt ); - pt0 = pt; - if ( !p[i] || p[i] == '/' || p[i] == '(' ) { - bWritingAtomString = 0; - if ( bSuperscript ) { - while( bSuperscript > 1 ) { - pt.y += nShift2; - bSuperscript --; - } - pt.y += nShift; - /* MoveToEx( pDC, pt.x, pt.y, NULL ); */ - bMoveToEx = 1; - bSuperscript = 0; - } - if ( bSubscript ) { - pt.y -= nShift; - bMoveToEx = 1; - /* MoveToEx( pDC, pt.x, pt.y, NULL ); */ - bSubscript = 0; - } - } else { - /* turn Superscript off */ - if ( bSuperscript == 2 && p[i] != '.' ) { - pt.y += nShift2; - bMoveToEx = 1; - /* MoveToEx( pDC, pt.x, pt.y, NULL ); */ - bSuperscript -= 1; - } - if ( bSuperscript && !isdigit((int)(unsigned char)p[i]) && p[i] != '+' && p[i] != '-' && p[i] != '.' ) { - while( bSuperscript > 1 ) { - pt.y += nShift2; - bSuperscript --; - } - pt.y += nShift; - bMoveToEx = 1; - /* MoveToEx( pDC, pt.x, pt.y, NULL ); */ - bSuperscript = 0; - } - /* turn Subscript off */ - if ( bSubscript && !isdigit((int)(unsigned char)p[i] ) ) { - pt.y -= nShift; - bMoveToEx = 1; - /* MoveToEx( pDC, pt.x, pt.y, NULL ); */ - bSubscript = 0; - } - /* turn Subscript on after non-space and non-digit */ - if ( !bSuperscript && !bSubscript && ( i && p[i-1] != ' ' && !isdigit((int)(unsigned char)p[i-1]) && isdigit((int)(unsigned char)p[i])) ) { - pt.y += nShift; - bMoveToEx = 1; - /* MoveToEx( pDC, pt.x, pt.y, NULL ); */ - bSubscript = 1; - } - /* turn Superscript on */ - if ( !bSuperscript && !bSubscript && - (p[i] == '^' || p[i] == '.' || - (p[i] == '+' || p[i] == '-') && (i && p[i-1]!=' ' ) ) ) { - pt.y -= nShift; - bSuperscript += 1; - if ( p[i] == '.' ) { - bSuperscript += 1; /* special case */ - pt.y -= nShift2; - } - bMoveToEx = 1; - /* MoveToEx( pDC, pt.x, pt.y, NULL ); */ - if ( p[i] == '^' ) { - goto output_the_string; - continue; /* leading superscript: isotopic mass */ - } - } - if ( bSuperscript == 1 && !bSubscript && p[i] == '.' ) { - pt.y -= nShift2; - bSuperscript += 1; - bMoveToEx = 1; - /* MoveToEx( pDC, pt.x, pt.y, NULL ); */ - } - - } - } - if ( p[i] == '/' ) { - nNumSlash ++; - if ( nNumSlash == 1 ) { - OrigBkColor = GetBkColor( pDC ); - bNewBkColor ++; - NewBkColor = CLR_YELLOW; - /* OrigBkColor = SetBkColor( pDC, CLR_YELLOW);*/ /* CLR_CYAN ); */ - } - if ( nNumSlash == 3 ) { - bNewBkColor ++; - NewBkColor = CLR_CYAN; - /* SetBkColor( pDC, CLR_CYAN ); */ - } - } -#ifdef DISPLAY_DEBUG_DATA - else if ( nNumSlash && p[i] == '`' ) { - bNewBkColor ++; - NewBkColor = CLR_LTPURPLE; - /* SetBkColor( pDC, CLR_LTPURPLE );*/ /* DebugData */ - } -#endif - /* output one character; special treatment for '.' */ -output_the_string: - bBypassCurrentChar = ( p[i] == '.' && bSuperscript || p[i] == '^' ); - if ( bBypassCurrentChar || bNewBkColor || bMoveToEx ) { - /* output the accumulated string */ - int iLast = i-(bBypassCurrentChar || bMoveToEx); - /* - int k; - for ( k = i0; k <= iLast; k ++ ) { - MyTextOutABC( p, k, k, pDC ); - } - */ - MyTextOutABC( p, i0, iLast, pDC ); /* including iLast */ - i0 = i+1-bMoveToEx; - if ( p[i0] == '^' ) - i0 ++; - if ( bMoveToEx ) { - int dx = pt.x - pt0.x; - int dy = pt.y - pt0.y; - GetCurrentPositionEx( pDC, &pt ); - pt.x += dx; - pt.y += dy; - MoveToEx( pDC, pt.x, pt.y, NULL ); - } - if ( bNewBkColor ) { - SetBkColor( pDC, NewBkColor); - } - if ( p[i] == '.' && bSuperscript ) { - if ( !DrawTextColorDot( pDC ) ) { - TextOut( pDC, xs, ys, p+i, 1 ); - } - } - } - /* - if ( p[i] != '.' || !bSuperscript || !DrawTextColorDot( pDC ) ) { - TextOut( pDC, xs, ys, p+i, 1 ); - } - */ - } - MyTextOutABC( p, i0, i-1, pDC ); /* output the rest of the string */ - - SetTextAlign( pDC, uPrevTextAlign); - if ( nNumSlash ) { - SetBkColor( pDC, OrigBkColor ); - } - if ( bHighlightTheAtom || strchr(st, '~') ) { - SetBkColor( pDC, clrBk ); - } - if ( bWritingAtomStringColored ) { - SetTextColor( pDC, OrigTxColor ); - bWritingAtomStringColored = 0; - } - - return ( 1 ); -} -/****************************************************************************/ -int DrawPreparedString( HDC pDC, char *st1, int shift, int x, int y, int bHighlightTheAtom ) -{ - DrawColorString( pDC, st1, x+shift, y-GetFontAscent( pDC )/2, bHighlightTheAtom ); - return 1; -} -/****************************************************************************/ -int DrawString( HDC pDC, char *st1, int shift, int x, int y ) -{ - char st[256]; - int xs, ys, l, k; -/* int hfont = GetFontHeight( pDC ); */ - int afont = GetFontAscent( pDC ); -/* COLORREF clrBk; */ -/* int nNumSlash = 0; */ -/* COLORREF OrigBkColor; */ - - strncpy( st, st1, sizeof(st)-1 ); - st[sizeof(st)-1] = '\0'; - - l = GetStringWidth( pDC, st ); - - /* Default values */ - xs = x - l / 2; - ys = y - afont / 2; - - /* Single element */ - - if( strlen( st ) == 1 ) goto draw; - - /* Changing order for right/left connection */ - - if( shift ) { - int start = 0; - start = MoveHydrogenAtomToTheLeft( st, start, 'T' ); - start = MoveHydrogenAtomToTheLeft( st, start, 'D' ); - start = MoveHydrogenAtomToTheLeft( st, start, 'H' ); - /* determine the position */ - - if( ( strlen( st ) == 2 ) && islower( st[1] ) ) goto draw; - - k = GetStringWidth( pDC, st ); - k -= GetOneCharInStringWidth( pDC, ( st + strlen( st ) - 1 ) ) / 2; - xs = x - k; - } else { - - /* determine the position */ - - k = GetOneCharInStringWidth( pDC, st ); - xs = x - k / 2; - } - - draw: - DrawColorString( pDC, st, xs, ys, 0 ); - return ( 1 ); -} - -/****************************************************************************/ -void DrawLine( HDC pDC, int x1, int y1, int x2, int y2 ) -{ - MoveToEx( pDC, x1, y1, NULL ); - LineTo( pDC, x2, y2 ); -} -/****************************************************************************/ -int nGetNumLegendOptions( inf_ATOM *inf_at, int num_at ) -{ - int i, n, nmax=0; - char *p; - for ( i = 0; i < num_at; i ++ ) { - for ( n = 0, p = inf_at[i].at_string; p = strchr( p, '/' ); p ++, n++ ) - ; - nmax = inchi_max( nmax, n ); - } - return nmax; -} -/****************************************************************************/ -int DrawTheInputStructure( inp_ATOM *at, INF_ATOM_DATA *inf_at_data, int num_at, - HDC pDC, int tx_off, int ty_off, int xoff, int yoff, - int width_pix, int height_pix, int bDraw, int bOrigAtom, COLORREF clrPen, int nPenWidth ) -{ - int RetVal; - char *NoRoom = "Window is too small"; -#ifdef TARGET_LIB_FOR_WINCHI - static char PressEnter[] = ""; -#else - static char PressEnter[] = "Press Enter to continue."; -#endif - static char Legend[] = "Legend:"; - char **Str; - int num_str; - inf_ATOM *inf_at = inf_at_data? inf_at_data->at : NULL; - static char LastString[256]; - static char *LastStr[] = { "Atom / Atom Id", " / Non-stereo class", " / Mobile group id", " / Mobile group class", "" }; - static char *StrOrig[] = {PressEnter, Legend, "Atom / Input atom number"}; - static char *StrInfo[] = {PressEnter, Legend, LastString}; - - int nFontHeight, nFontAveWidth, afont, i, x, y, n_opt; - COLORREF rgbColor; - - RetVal = 0; - nFontHeight = GetFontHeight( pDC ); - nFontAveWidth = GetFontAveWidth( pDC ); - afont = GetFontAscent( pDC ); - - - if ( bDraw ) { - /* drawing */ - DrawStructure( pDC, at, inf_at_data, num_at, xoff, yoff, clrPen, nPenWidth ); - /* draw the message and legend: */ - if ( !inf_at || bOrigAtom ) { - Str = StrOrig; - num_str = sizeof(StrOrig)/sizeof(StrOrig[0]); - } else { - n_opt = nGetNumLegendOptions( inf_at, num_at ); - Str = StrInfo; - num_str = sizeof(StrInfo)/sizeof(StrInfo[0]); - for ( i = 0, LastString[0] = '\0'; i < n_opt && LastStr[i][0]; i ++ ) { - strcat(LastString, LastStr[i]); - } - } - rgbColor = SetBkColor( pDC, CLR_CYAN ); - x = nFontAveWidth; - y = nFontHeight/5; - for ( i = 0; i < num_str; i ++ ) { - if ( i+1 < num_str ) { - TextOut( pDC, x+tx_off, y+ty_off, Str[i], (int) strlen(Str[i]) ); - x += GetStringWidth( pDC, Str[i] )+2*nFontAveWidth; - if ( i == 0 ) { - SetBkColor( pDC, rgbColor ); - rgbColor = SetTextColor( pDC, CLR_BLUE /*CLR_RED*/ ); - } else - if ( i == 1 ) { - SetTextColor( pDC, rgbColor ); - } - } else { - DrawString( pDC, Str[i], 0, x+tx_off, y+afont/2+ty_off ); - } - } - } else { - rgbColor = SetTextColor( pDC, CLR_RED ); - TextOut( pDC, nFontAveWidth+tx_off, nFontHeight/5+ty_off, NoRoom, (int) strlen(NoRoom) ); - rgbColor = SetTextColor( pDC, rgbColor ); - } - return RetVal; -} - - -/****************************************************************************/ -typedef struct tagTableParms { - int thdrHeight; - int thdrWidth; - int tcellHeight; - int tcellWidth; - int tblHeight; - int tblWidth; - int tblRows; - int tblCols; - int xtblOffs; - int ytblOffs; -} TBL_PARMS; -/****************************************************************************/ -void CalcTblParms( HDC hMemoryDC, TBL_PARMS *tp, TBL_DRAW_PARMS *tdp, - int *xStructOffs, int *yStructOffs, int *xStructSize, int *yStructSize, int yoffs1) -{ - int i, j, n, w, h; - tp->tblCols = tdp->bDrawTbl; - tp->tblRows = 0; - for ( i = 0; i < tp->tblCols; i ++ ) { - (tdp->nOrientation? GetVertTextSize - : GetTextSize)( hMemoryDC, (int) strlen(tdp->ReqShownFoundTxt[i]), tdp->ReqShownFoundTxt[i], &w, &h ); - tp->thdrHeight=inchi_max(h, tp->thdrHeight); - tp->thdrWidth =inchi_max(w, tp->thdrWidth ); - - for ( j = 0, n = 0; j < TDP_NUM_PAR; j ++ ) { - if ( tdp->ReqShownFound[i][j] >= ' ' ) { - GetTextSize( hMemoryDC, 1, &tdp->ReqShownFound[i][j], &w, &h ); - tp->tcellHeight=inchi_max(h, tp->tcellHeight); - tp->tcellWidth =inchi_max(w, tp->tcellWidth ); - n ++; /* number of types of requested or found or shown features (type: B/T, I/N, S) */ - } - } - tp->tblRows = inchi_max(tp->tblRows, n); - } - if ( tdp->nOrientation ) { /* here are tp->tblCols columns and tp->tblRows rows. */ - tp->tblHeight = tp->thdrHeight + (2*tp->tblRows+2)*tp->tcellHeight; /* empty lines above the header and around each cell */ - tp->thdrWidth = tp->tcellWidth = inchi_max( tp->tcellWidth, tp->thdrWidth ); - tp->tblWidth = (2*tp->tblCols+1) * tp->tcellWidth; /* add empty columns around each column */ - *xStructOffs += tp->tblWidth; /* draw on the left margine */ - /* *yStructSize -= tp->tblHeight; */ - *xStructSize -= tp->tblWidth; - tp->xtblOffs = 0; - tp->ytblOffs = yoffs1; - } else { /* Do not believe your eyes: here are tp->tblCols rows and tp->tblRows columns. */ - tp->thdrHeight = tp->tcellHeight = inchi_max(tp->thdrHeight, tp->tcellHeight); - tp->tblWidth = tp->thdrWidth + (2*tp->tblRows+2)*tp->tcellWidth; - tp->tblHeight = (2*tp->tblCols+1)*tp->tcellHeight; - /* draw the table on the left margine */ - *xStructOffs += tp->tblWidth; - *xStructSize -= tp->tblWidth; - /* *xStructSize -= tp->tblWidth; */ - tp->xtblOffs = 0; - tp->ytblOffs = yoffs1; - } -} -/****************************************************************************/ -int DrawTheTable( HDC hDC, TBL_PARMS *tp, TBL_DRAW_PARMS *tdp, int x_offs, int y_offs ) -{ - int i, j, ret; - int dx = tp->tcellWidth/2; - int dy = tp->tcellHeight/2; - int x1, y1, x2, y2; - /* draw frame around the table */ - ret = Rectangle( hDC, tp->xtblOffs+dx+x_offs, tp->ytblOffs+dy+y_offs, tp->xtblOffs+tp->tblWidth-dx+x_offs, tp->ytblOffs+tp->tblHeight-dy+y_offs); - /* draw lines between labeled rows or columns */ - for ( i = 1; i < tp->tblCols; i ++ ) { - if ( tdp->nOrientation ) { - /* parallel to vertical columns */ - x1 = x2 = tp->xtblOffs+dx + 2 * i * tp->tcellWidth; - y1 = tp->ytblOffs+dy; - y2 = tp->ytblOffs+tp->tblHeight-dy; - } else { - /* parallel to horizontal rows */ - x1 = tp->xtblOffs+dx; - x2 = tp->xtblOffs+tp->tblWidth-dx; - y1 = y2 = tp->ytblOffs+dy + 2 * i * tp->tcellHeight; - } - DrawLine( hDC, x1+x_offs, y1+y_offs, x2+x_offs, y2+y_offs ); - } - /* draw lines between requested/Shown/Found types */ - for ( i = 0; i < tp->tblRows; i ++ ) { - if ( tdp->nOrientation ) { - /* perpendicular to vertical columns */ - x1 = tp->xtblOffs+dx; - x2 = tp->xtblOffs+tp->tblWidth-dx; - y1 = y2 = tp->ytblOffs + tp->thdrHeight + tp->tcellHeight + 2 * i * tp->tcellHeight + dy; - } else { - /* perpendicular to horizontal rows */ - x1 = x2 = tp->xtblOffs + tp->thdrWidth + tp->tcellWidth + 2 * i * tp->tcellWidth + dx; - y1 = tp->ytblOffs+dy; - y2 = tp->ytblOffs+tp->tblHeight-dy; - } - DrawLine( hDC, x1+x_offs, y1+y_offs, x2+x_offs, y2+y_offs ); - } - /* draw the text */ - for ( i = 0; i < tp->tblCols; i ++ ) { - if ( tdp->nOrientation ) { - /* vertical column */ - x1 = tp->xtblOffs + (2 * i + 1) * tp->tcellWidth; - y1 = tp->ytblOffs + tp->tcellHeight; - } else { - /* horizontal row */ - x1 = tp->xtblOffs + tp->tcellWidth; - y1 = tp->ytblOffs + tp->tcellHeight + 2 * i * tp->tcellHeight; - } - (tdp->nOrientation? TextOutVert - : TextOutHoriz)( hDC, x1+x_offs, y1+y_offs, tdp->ReqShownFoundTxt[i], (int) strlen(tdp->ReqShownFoundTxt[i]), tp->tcellWidth ); - - for ( j = 0; j < tp->tblRows; j ++ ) { - if ( tdp->ReqShownFound[i][j] >= ' ' ) { - if ( tdp->nOrientation ) { - /* vertical column */ - y1 = tp->ytblOffs + tp->thdrHeight + (2*j + 2) * tp->tcellHeight; - } else { - /* horizontal row */ - x1 = tp->xtblOffs + tp->thdrWidth + (2*j + 2) * tp->tcellWidth; - } - (tdp->nOrientation? TextOutVert:TextOutHoriz)( hDC, x1+x_offs, y1+y_offs, &tdp->ReqShownFound[i][j], 1, tp->tcellWidth ); - } - } - } - - - return 0; -} -/****************************************************************************/ -void GetStructSizes( HDC hDC, inf_ATOM *inf_at, inp_ATOM *at0, inp_ATOM *at1, int num_at, int *xoffs1, int *xoffs2, INT_DRAW_PARMS *idp) -{ - int i, j, k, num_bonds; - double xmin, xmax, ymin, ymax; - double x2, x, y2, y, dist; - char *str; - int len, cur_len, half_char_width; - int Left_shift, Right_shift, Other_shift; - char cLeftChar, cRightChar; - int max_left_label_width_pix; - int max_right_label_width_pix; - - if ( idp ) { - if ( !inf_at ) { - idp->max_left_label_width_pix = *xoffs1; - idp->max_right_label_width_pix = *xoffs2; - } else { - idp->max_left_label_width_pix = idp->max_right_label_width_pix = 0; - } - } else { - if ( !inf_at ) { - max_left_label_width_pix = *xoffs1; - max_right_label_width_pix = *xoffs2; - } else { - max_left_label_width_pix = max_right_label_width_pix = 0; - } - } - - xmin=xmax=at0[0].x; - ymin=ymax=at0[0].y; - - for ( num_bonds = 0, i=0; i < num_at; i ++ ) { - - x = at0[i].x; - y = at0[i].y; - Left_shift = Right_shift = Other_shift = 0; - - for ( j = 0; j < at0[i].valence; j ++ ) { - k = at0[i].neighbor[j]; - x2 = at0[k].x; - y2 = at0[k].y; - dist = sqrt( (x-x2)*(x-x2)+(y-y2)*(y-y2) ); - if ( x < x2 - 0.2*dist ) - Left_shift ++; - else - if ( x > x2 + 0.1*dist ) - Right_shift ++; - else - Other_shift ++; - } - - if ( inf_at ) { - len = 0; - str = inf_at[i].at_string; - do { - if ( cur_len=strcspn(str, "^") ) { - if ( !len ) { - cLeftChar = str[0]; - } - cRightChar = str[cur_len-1]; - len += GetSubstringWidth( hDC, cur_len, str); - } - str += cur_len+1; - } while ( str[0] ); - inf_at[i].DrawingLabelLength = len; - if ( Left_shift && !Right_shift ) { - /* Atom label should be to the left from the vertex */ - half_char_width = len? GetOneCharInStringWidth( hDC, &cRightChar )/2:0; - inf_at[i].DrawingLabelLeftShift = len - half_char_width; - if ( idp ) { - idp->max_left_label_width_pix = inchi_max( idp->max_left_label_width_pix, inf_at[i].DrawingLabelLeftShift); - idp->max_right_label_width_pix = inchi_max(idp->max_right_label_width_pix, half_char_width); - } else { - max_left_label_width_pix = inchi_max(max_left_label_width_pix, inf_at[i].DrawingLabelLeftShift); - max_right_label_width_pix = inchi_max(max_right_label_width_pix, half_char_width); - } - /* convert NH2 to H2N, etc. */ - len = 0; - str = inf_at[i].at_string; - len = MoveHydrogenAtomToTheLeft( str, len, 'T' ); - len = MoveHydrogenAtomToTheLeft( str, len, 'D' ); - len = MoveHydrogenAtomToTheLeft( str, len, 'H' ); - } else { - /* Atom label should be to the right from the vertex */ - half_char_width = len? GetOneCharInStringWidth( hDC, &cLeftChar )/2:0; - inf_at[i].DrawingLabelLeftShift = half_char_width; - if ( idp ) { - idp->max_left_label_width_pix = inchi_max( idp->max_left_label_width_pix, half_char_width); - idp->max_right_label_width_pix = inchi_max( idp->max_right_label_width_pix, - inf_at[i].DrawingLabelLength - - inf_at[i].DrawingLabelLeftShift); - } else { - max_left_label_width_pix = inchi_max( max_left_label_width_pix, half_char_width); - max_right_label_width_pix = inchi_max( max_right_label_width_pix, - inf_at[i].DrawingLabelLength - - inf_at[i].DrawingLabelLeftShift); - } - } - } else { - at1[i].bDrawingLabelLeftShift = ( Left_shift && !Right_shift ); - } - - xmin = inchi_min( xmin, x ); - xmax = inchi_max( xmax, x ); - ymin = inchi_min( ymin, y ); - ymax = inchi_max( ymax, y ); - } - if ( idp ) { - idp->xmin = xmin; - idp->xmax = xmax; - idp->ymin = ymin; - idp->ymax = ymax; - if ( inf_at ) { - *xoffs1 = idp->max_left_label_width_pix; - *xoffs2 = idp->max_right_label_width_pix; - } - } else - if ( inf_at ) { - *xoffs1 = max_left_label_width_pix; - *xoffs2 = max_right_label_width_pix; - } - -} -/****************************************************************************/ -void ResizeAtomForDrawing( inf_ATOM *inf_at, inp_ATOM *at0, inp_ATOM *at1, int num_at, - INT_DRAW_PARMS *idp, int width, int height, int nFontWidth, int *xoffs1, int *xoffs2, - int *draw_width, int *draw_height, int *xdraw_offs, int *ydraw_offs ) -{ - int i; - double xmin = idp->xmin; - double xmax = idp->xmax; - double ymin = idp->ymin; - double ymax = idp->ymax; - double dx = 0.0, dy = 0.0, new_dx; - double coeff=0.0, xShift=0.0, yShift=0.0; - double coeffx = 0.0, coeffy = 0.0, new_coeffx; - - - if ( xmax > xmin || ymax > ymin ) { - - dx = xmax-xmin; - dy = ymax-ymin; - - if ( width > 0 && height > 0 ) { - coeffx = dx > 0.0? (double)width/dx : 0.0; - coeffy = dy > 0.0? (double)height/dy : 0.0; - - if ( coeffx > 0.0 && coeffy > 0.0 ) - coeff = inchi_min( coeffx, coeffy ); - else - coeff = inchi_max( coeffx, coeffy ); - } - if ( coeffx == 0.0 ) { - xShift = width/2.0; - } - if ( coeffy == 0.0 ) { - yShift = height/2.0; - } - - } else { - coeff = 0.0; - xShift = width/2.0; - yShift = height/2.0; - } - - - /* set screen coordinates for drawing */ - for ( i = 0; i < num_at; i ++ ) { - at1[i].y = (ymax - at0[i].y)*coeff+yShift; /* screen y axis is directed down */ - at1[i].x = (at0[i].x-xmin)*coeff+xShift; - } - /* horizontal screen coordinates rescale if x dimension determines struct. size */ - if ( coeffx > 0.0 && coeffy > 0.0 && inf_at ) { - double new_xmin = 1.0e32; - double new_xmax = -1.0e32; - double dx1, dx2; - int nPass=0; - int new_width = width + idp->max_left_label_width_pix + idp->max_right_label_width_pix; - for ( i = 0; i < num_at; i ++ ) { - dx1 = at1[i].x - inf_at[i].DrawingLabelLeftShift; - dx2 = dx1 + inf_at[i].DrawingLabelLength; - new_xmin = inchi_min( new_xmin, dx1 ); - new_xmax = inchi_max( new_xmax, dx2 ); - } - new_dx = new_xmax - new_xmin; - if ( coeffx > coeffy && new_dx < (double)new_width ) - goto done; - if ( new_dx < (double)new_width && new_dx > (double)(new_width-2*nFontWidth) ) - goto done; -again: - new_dx += nFontWidth; /* precaution */ - new_coeffx = coeffx + ((double)new_width-new_dx)/dx; - if ( coeffy > 0.0 ) - coeff = inchi_min(new_coeffx, coeffy); - else - coeff = new_coeffx; - - new_xmin = 1.0e32; - new_xmax = -1.0e32; - for ( i = 0; i < num_at; i ++ ) { - dy = at1[i].y = (ymax - at0[i].y)*coeff+yShift; /* screen y axis is directed down */ - dx = at1[i].x = (at0[i].x-xmin)*coeff+xShift; - dx1 = dx - inf_at[i].DrawingLabelLeftShift; - dx2 = dx1 + inf_at[i].DrawingLabelLength; - new_xmin = inchi_min( new_xmin, dx1 ); - new_xmax = inchi_max( new_xmax, dx2 ); - } - new_dx = new_xmax - new_xmin; - if ( new_dx > new_width && nPass++ < 3 ) { - coeffx = new_coeffx; - goto again; - } -done: - *xoffs1 = -nRound( new_xmin ); - *xoffs2 = nRound( new_xmax - (xmax-xmin)*coeff ); - *draw_width = nRound( (xmax-xmin)*coeff ); /* nRound( new_dx ); */ - *draw_height = nRound( (ymax-ymin)*coeff ); - } else { - *draw_width = nRound( (xmax-xmin)*coeff ); - *draw_height = nRound( (ymax-ymin)*coeff ); - } - *xdraw_offs = nRound(xShift); - *ydraw_offs = nRound(yShift); -} - -/******************************************************************************/ -void InpStructureMarkEquComponents( MY_WINDOW_DATA *pWinData, AT_NUMB nNewEquLabel, - inp_ATOM *at0, inp_ATOM *at1, inf_ATOM *inf_at, int num_at ) -{ - int bHighlight = 0; - AT_NUMB *nEquLabels = pWinData->nEquLabels; - /* highlight equivalent components */ - int i, neigh, j, ni, nj, nh; - if ( nNewEquLabel ) { - for ( i = 0; i < num_at; i ++ ) { - ni = (int)at0[i].orig_at_number-1; - if ( 0 <= ni && ni < num_at ) { - if ( nNewEquLabel == nEquLabels[ni] || - 1==at0[i].el_number && 1==at0[i].chem_bonds_valence && - 0<=(nh=(int)at0[at0[i].neighbor[0]].orig_at_number-1) && - nh < num_at && nNewEquLabel == nEquLabels[nh] ) { - inf_at[i].cHighlightTheAtom = 1; /* highlight the atom */ - bHighlight |= 1; - for ( j = 0; j < at0[i].valence; j ++ ) { - neigh = (int)at0[i].neighbor[j]; - if ( neigh < num_at && - 0 <= (nj = (int)at0[neigh].orig_at_number-1) && - nj < num_at && - ( - /* highlighted atom */ - ( nNewEquLabel == nEquLabels[nj] ) || - /* terminal H */ - ( 1==at0[neigh].el_number && 1 == at0[neigh].chem_bonds_valence) - ) - ) { - at0[i].bond_type[j] |= BOND_MARK_HIGHLIGHT; /* highlight the bond */ - at1[i].bond_type[j] |= BOND_MARK_HIGHLIGHT; /* highlight the bond */ - } - } - } else - if ( inf_at[i].cHighlightTheAtom ) { - inf_at[i].cHighlightTheAtom = 0; - for ( j = 0; j < at0[i].valence; j ++ ) { - at0[i].bond_type[j] &= ~BOND_MARK_HIGHLIGHT; /* remove highlight from the bond */ - at1[i].bond_type[j] &= ~BOND_MARK_HIGHLIGHT; /* remove highlight from the bond */ - } - - } - } - } - } else { - for ( i = 0; i < num_at; i ++ ) { - ni = (int)at0[i].orig_at_number-1; - if ( 0 <= ni && ni < num_at ) { - if ( inf_at[i].cHighlightTheAtom ) { - inf_at[i].cHighlightTheAtom = 0; - for ( j = 0; j < at0[i].valence; j ++ ) { - at0[i].bond_type[j] &= ~BOND_MARK_HIGHLIGHT; /* remove highlight from the bond */ - at1[i].bond_type[j] &= ~BOND_MARK_HIGHLIGHT; /* remove highlight from the bond */ - } - - } - - } - } - } - if ( !bHighlight ) { - nNewEquLabel = 0; - } - pWinData->nCurEquLabel = nNewEquLabel; - pWinData->bHighlight = bHighlight; -} -/****************************************************************************/ -int CreateInputStructPicture( HDC hDC, MY_WINDOW_DATA *pWinData, RECT *rc, int bPrint, AT_NUMB nNewEquLabel ) -{ - int ErrCode = 1, Res, width=0, height=0, yoffs0=0, xoffs1=0, yoffs1=0, xoffs2, yoffs2; - int xDim, yDim, w, h, xs, ys; - - HDC hMemoryDC = NULL; - HBITMAP hBitmap = NULL, hOldBitmap=NULL; - HPEN Pen = 0, OldPen = 0; - LOGFONT MyLogFont; - HFONT Font = 0, OldFont = 0; - char *FaceName = FONT_NAME; - int bDrawTbl = 0; - int bStereoFlags = 0; - - int win_top = rc->top; - int win_left = rc->left; - - int win_width = rc->right - rc->left; - int win_height = rc->bottom - rc->top; - - int bm_top = rc->top; - int bm_left = rc->left; - - int bm_width = rc->right - rc->left; - int bm_height = rc->bottom - rc->top; - - TBL_PARMS tp; - - int num_at = 0; - int bOrigAtom = 0; - int nFontSize = 10; - inp_ATOM *at0 = NULL; - inp_ATOM *at1 = NULL; - inf_ATOM *inf_at = NULL; - INT_DRAW_PARMS *idp = NULL; - TBL_DRAW_PARMS *tdp = NULL; - INT_DRAW_PARMS idp_print; - - /* structure + headers rect: offsets, width, height */ - int xStructOffs=0, yStructOffs=0, xStructSize, yStructSize; - - int nFontHeight=0; - int nFontWidth=0; - int nPenWidth = 1; - COLORREF clrPen = CLR_BLUE; - int afont; - - /*bPrint = 1;*/ /* test */ - - if ( pWinData ) { - num_at = pWinData->num_at; - bOrigAtom = pWinData->bOrigAtom; - nFontSize = pWinData->nFontSize; - at0 = pWinData->at0; - at1 = pWinData->at1; - inf_at = pWinData->inf_at_data.at; - idp = &pWinData->idp; - tdp = &pWinData->tdp; - bStereoFlags = pWinData->inf_at_data.StereoFlags; - if ( bPrint ) { - idp = &idp_print; - memset( idp, 0, sizeof(idp[0]) ); - } - - if ( pWinData->nCurEquLabel != nNewEquLabel && - pWinData->nEquLabels && - nNewEquLabel <= pWinData->nNumEquSets && at0 && at1 && inf_at && num_at ) { - InpStructureMarkEquComponents( pWinData, nNewEquLabel, at0, at1, inf_at, num_at ); - } - } - - - xDim = xStructSize = win_width; - yDim = yStructSize = win_height; - if ( !bPrint ) { - /* create bitmap: drawing on a bitmap reduces screen flicker */ - if ( !(hMemoryDC = CreateCompatibleDC(hDC)) || - !(hBitmap = CreateCompatibleBitmap(hDC, xDim, yDim )) || - !(hOldBitmap = (HBITMAP) SelectObject(hMemoryDC, hBitmap)) || - !PatBlt( hMemoryDC, 0, 0, xDim, yDim, PATCOPY )) { - ErrCode = 0; - goto _end; - } - bm_top = 0; - bm_left = 0; - bm_height = win_height - win_top; - bm_width = win_width - win_left; - } else { - hMemoryDC = hDC; - } - - if ( pWinData ) { - /* create drawing tools: font */ - memset( &MyLogFont, 0, sizeof( LOGFONT ) ); - if ( nFontSize < 0 ) { - int iLogPixsY = GetDeviceCaps(hDC, LOGPIXELSY); - nFontSize = -MulDiv(iLogPixsY, -nFontSize, 72); - } - nPenWidth = bPrint? inchi_max(abs(nFontSize)/10,1):1; - clrPen = bPrint? CLR_BLACK : CLR_BLUE; - MyLogFont.lfHeight = nFontSize; - MyLogFont.lfWeight = FW_NORMAL; - /* MyLogFont.lfItalic = 1; */ /* test MyTextOutABC() */ - strncpy( MyLogFont.lfFaceName, FaceName, LF_FACESIZE ); - Font = CreateFontIndirect( &MyLogFont ); /* black */ - - /* create drawing tools: pen */ - Pen = CreatePen( PS_SOLID, nPenWidth, clrPen ); - - /* select drawing tools into the bitmap */ - OldFont = (HFONT)SelectObject( hMemoryDC, Font ); - OldPen = (HPEN) SelectObject( hMemoryDC, Pen ); - - /* find sizes */ - - nFontHeight = GetFontHeight( hMemoryDC ); - nFontWidth = GetFontAveWidth( hMemoryDC ); - afont = GetFontAscent( hMemoryDC ); - - /* offsets within the (0, 0, xStructSize, yStructSize) rectangle */ - xoffs1 = xoffs2 = 16*nFontWidth; /* define structure atom coordinate margins here */ - yoffs0 = (bPrint && pWinData->szTitle && pWinData->szTitle[0] )? (3*nFontHeight)/2 : 0; /* 1.5 or 0 lines at the top */ - yoffs1 = (5*nFontHeight)/2; /* 2.5 lines at the top */ - yoffs2 = (5*nFontHeight)/2; /* 2.5 lines at the bottom */ - - /***********************************************/ - /* Calculate structure size */ - /***********************************************/ - if ( idp->bInit ) { - /* structure sizes are known */ - xoffs1 = idp->max_left_label_width_pix; - xoffs2 = idp->max_right_label_width_pix; - } else { - GetStructSizes( hMemoryDC, inf_at, at0, at1, num_at, &xoffs1, &xoffs2, idp); - idp->bInit = 1; - } - - /***********************************************/ - /* Calculate requested/Shown/Found table sizes */ - /***********************************************/ - - memset( &tp, 0, sizeof(tp) ); -#ifdef TARGET_LIB_FOR_WINCHI - bDrawTbl = 0; -#else - bDrawTbl = tdp && tdp->bDrawTbl; - /*bDrawTbl = 0;*/ -#endif - if ( bDrawTbl ) { - double dx = idp->xmax - idp->xmin; - double dy = idp->ymax - idp->ymin; - int nOrientation_tmp = tdp->nOrientation; - if ( dx > 0.0 && dy > 0.0 ) { - int xStructOffs_tmp0=xStructOffs, yStructOffs_tmp0=yStructOffs; - int xStructSize_tmp0=xStructSize, yStructSize_tmp0=yStructSize; - int twidth0; - /* - int xStructOffs_tmp1=xStructOffs, yStructOffs_tmp1=yStructOffs; - int xStructSize_tmp1=xStructSize, yStructSize_tmp1=yStructSize; - int twidth1; - */ - tdp->nOrientation = 0; - CalcTblParms( hMemoryDC, &tp, tdp, - &xStructOffs_tmp0, &yStructOffs_tmp0, &xStructSize_tmp0, &yStructSize_tmp0, yoffs0+yoffs1); - twidth0 = tp.tblWidth; - xStructSize_tmp0 -= xoffs1 + xoffs2; - yStructSize_tmp0 -= yoffs1 + yoffs2; - /* - memset( &tp, 0, sizeof(tp) ); - tdp->nOrientation = 0; - CalcTblParms( hMemoryDC, &tp, tdp, - &xStructOffs_tmp1, &yStructOffs_tmp1, &xStructSize_tmp1, &yStructSize_tmp1, yoffs1); - twidth1 = tp.tblWidth; - xStructSize_tmp1 -= xoffs1 + xoffs2; - yStructSize_tmp1 -= yoffs1 + yoffs2; - */ - if ( xStructSize_tmp0 > 0 && yStructSize_tmp0 > 0 ) { - nOrientation_tmp = ( (double)yStructSize_tmp0/(double)xStructSize_tmp0 > dy / dx ); - } - } - tdp->nOrientation = nOrientation_tmp; - memset( &tp, 0, sizeof(tp) ); - CalcTblParms( hMemoryDC, &tp, tdp, - &xStructOffs, &yStructOffs, &xStructSize, &yStructSize, yoffs0+yoffs1); - } else { - xStructSize -= 2*nFontWidth; /* drawing area sizes */ - yStructSize -= nFontHeight; - xStructOffs += nFontWidth; /* drawing area offsets */ - yStructOffs += nFontHeight/2; - } - - width = xStructSize - xoffs1 - xoffs2; /* drawing structre area sizes */ - height = yStructSize - yoffs0 - yoffs1 - yoffs2; - ResizeAtomForDrawing( inf_at, at0, at1, num_at, idp, width, height, nFontWidth, &xoffs1, &xoffs2, &w, &h, &xs, &ys ); - - /* At this point xStructOffs = the left margin for the drawing */ - - if ( 2*xs+xoffs1+xoffs2+w < win_width - xStructOffs ) { /*compare 2*x-coordinate of the center */ - xStructOffs += (win_width - (xStructOffs+xoffs1+xoffs2+w))/2-xs; - } - - } - - /* draw */ - if ( bPrint ) { - ; /*PatBlt( hMemoryDC, 0, 0, xDim+win_left, yDim+win_top, PATCOPY ); */ - } else { - PatBlt( hMemoryDC, 0, 0, xDim, yDim, PATCOPY ); - } - - if ( pWinData ) { - char str[128]=""; - - /* exact rectangle around the structure drawing */ - /*Rectangle( hMemoryDC, xStructOffs+xs-1, yStructOffs+ys+yoffs1-afont-1, xStructOffs+xs+xoffs1+xoffs2+w+1, yStructOffs+ys+yoffs1+nFontHeight+h+1); */ - Res = DrawTheInputStructure( at1, &pWinData->inf_at_data, num_at, hMemoryDC, - bm_left, /* text output offsets */ - bm_top+yoffs0, - bm_left + xoffs1+xStructOffs, /* structure offsets */ - bm_top + +yoffs0+yoffs1+yStructOffs, - xDim-xoffs1, /* structure width */ - yDim-yoffs0-yoffs1, /* structure height */ - ( width >= 0 && height >= 0 ), bOrigAtom, clrPen, nPenWidth ); - if( Res == -1 ){ - ErrCode = 0; - goto _end; - } - if ( bDrawTbl || bStereoFlags || pWinData->inf_at_data.szRemovedProtons[0] ) { - /* - if ( bDrawTbl ) { - char *str = "Abbreviations: Tautomeric, Isotopic, Stereo"; - DrawTheTable( hMemoryDC, &tp, tdp, bPrint?win_left:0, bPrint?win_top:0 ); - TextOut( hMemoryDC, nFontWidth+(bPrint?win_left:0), win_height - nFontHeight+(bPrint?win_top:0), str, strlen(str) ); - } - */ - if ( bStereoFlags ) { - switch ( bStereoFlags & INF_STEREO_ABS_REL_RAC ) { - case INF_STEREO_ABS: - strcat( str, "Absolute" ); - break; - case INF_STEREO_REL: - strcat( str, "Relative" ); - break; - case INF_STEREO_RAC: - strcat( str, "Racemic mixture" ); - break; - } - if ( str[0] ) { - strcat( str, " stereo" ); - switch( bStereoFlags & INF_STEREO_NORM_INV ) { - case INF_STEREO_NORM: - strcat( str, " (normal)" ); - break; - case INF_STEREO_INV: - strcat( str, " (inverted)" ); - break; - case INF_STEREO_NORM_INV: - strcat( str, " (normal and inverted)" ); - break; - } - } - } - if ( bDrawTbl ) { - int bTaut=0, bIso=0, bSter=0; - if ( str[0] ) { - strcat( str, "; "); - } - bTaut = (NULL != memchr(tdp->ReqShownFound[ilSHOWN], 'T', TDP_NUM_PAR)); - bIso = (NULL != memchr(tdp->ReqShownFound[ilSHOWN], 'I', TDP_NUM_PAR)); - bSter = (NULL != memchr(tdp->ReqShownFound[ilSHOWN], 'S', TDP_NUM_PAR)) || - (NULL != memchr(tdp->ReqShownFound[ilSHOWN], 's', TDP_NUM_PAR)); - strcat( str, "Abbreviation" ); - if ( bTaut+bIso+bSter > 1 ) { - strcat( str, "s:"); - } else { - strcat( str, ":" ); - } - if ( bTaut ) strcat( str, " Mobile H" ); - if ( bIso ) strcat( str, " Isotopic" ); - if ( bSter ) strcat( str, " Stereo" ); - DrawTheTable( hMemoryDC, &tp, tdp, bm_left, bm_top); - } - if ( pWinData->inf_at_data.szRemovedProtons[0] ) { - if ( str[0] ) strcat( str, "; "); - strcat( str, pWinData->inf_at_data.szRemovedProtons ); - } - /*TextOut( hMemoryDC, nFontWidth+bm_left, bm_height - nFontHeight +bm_top, str, strlen(str) );*/ - /*DrawColorString( hMemoryDC, str, nFontWidth+bm_left, bm_height - nFontHeight +bm_top, 0 );*/ - } - if ( pWinData->bHighlight ) { - /* draw highlighted (identical) components description */ - char *p1 = " Highlighted "; - char *p2 = " components are identical"; - int x = bm_left + nFontWidth; - int y = bm_top + bm_height - nFontHeight; - COLORREF clrBk; - UINT uPrevTextAlign; - POINT pt; - /* save current position */ - GetCurrentPositionEx( hMemoryDC, &pt ); - /* move to the starting point */ - MoveToEx( hMemoryDC, x, y, NULL ); - /* set text aligh that do not require coordinates in TextOut() */ - uPrevTextAlign = SetTextAlign( hMemoryDC, TA_UPDATECP); - /* set highlighted background color */ - clrBk = SetBkColor( hMemoryDC, CLR_LTPURPLE ); - /* output the 1st word */ - TextOut( hMemoryDC, 0, 0, p1, (int) strlen(p1) ); - /* restore text background */ - SetBkColor( hMemoryDC, clrBk ); - /* output the rest of the text as normal text */ - TextOut( hMemoryDC, 0, 0, p2, (int) strlen(p2) ); - - if ( str[0] ) { - POINT pt2; - TextOut( hMemoryDC, 0, 0, ";", 1 ); - GetCurrentPositionEx( hMemoryDC, &pt2 ); - DrawColorString( hMemoryDC, str, pt2.x+2*nFontWidth, pt2.y, 0 ); - } - /* restore text align */ - SetTextAlign( hMemoryDC, uPrevTextAlign); - /* restore current position */ - MoveToEx( hMemoryDC, pt.x, pt.y, NULL ); - } else - if ( str[0] ) { - DrawColorString( hMemoryDC, str, nFontWidth+bm_left, bm_height - nFontHeight +bm_top, 0 ); - } - if ( yoffs0 > 0 && bPrint && pWinData->szTitle && pWinData->szTitle[0] ) { - /* print window title */ - char *p1 = pWinData->szTitle; - int x = bm_left + 3*nFontWidth; - int y = bm_top + yoffs0 - (3*nFontHeight)/2; - UINT uPrevTextAlign; - POINT pt; - /* save current position */ - GetCurrentPositionEx( hMemoryDC, &pt ); - /* move to the starting point */ - MoveToEx( hMemoryDC, x, y, NULL ); - /* set text aligh that do not require coordinates in TextOut() */ - uPrevTextAlign = SetTextAlign( hMemoryDC, TA_UPDATECP); - /* output the text */ - TextOut( hMemoryDC, 0, 0, p1, (int) strlen(p1) ); - /* restore text align */ - SetTextAlign( hMemoryDC, uPrevTextAlign); - /* restore current position */ - MoveToEx( hMemoryDC, pt.x, pt.y, NULL ); - } - } - - if ( !bPrint ) { - /* copy bitmap onto the window */ - ErrCode = BitBlt( - hDC, /* handle to the destination device context */ - win_left, /* x-coordinate of destination rectangle's upper-left corner */ - win_top, /* y-coordinate of destination rectangle's upper-left corner */ - xDim, /* width of destination rectangle */ - yDim, /* height of destination rectangle */ - hMemoryDC, /* handle to source device context */ - bm_left, /* x-coordinate of source rectangle's upper-left corner */ - bm_top, /* y-coordinate of source rectangle's upper-left corner */ - SRCCOPY /* raster operation code */ - ); - } - - if ( pWinData ) { - - /* remove drawing tools */ - if( Pen ){ - SelectObject( hMemoryDC, OldPen ); - DeleteObject( Pen ); - } - if( Font ){ - SelectObject( hMemoryDC, OldFont ); - DeleteObject( Font ); - } - - } -_end: - if ( !bPrint ) { - if( hBitmap ) { - if ( hOldBitmap ) - SelectObject(hMemoryDC, hOldBitmap); - DeleteObject( hBitmap ); - } - if( hMemoryDC && hMemoryDC != hDC ) - DeleteDC(hMemoryDC); - } - return ErrCode; -} - -/********************************************************************* - - FUNCTION: WndProcDisplayCanonStructure(HWND, unsigned, WORD, LONG) - - PURPOSE: Processes messages for the main window. - - MESSAGES: - - WM_COMMAND - process the application menu - WM_PAINT - Paint the main window - WM_DESTROY - post a quit message and return - WM_DISPLAYCHANGE - message sent to Plug & Play systems when the display changes - WM_RBUTTONDOWN - Right mouse click -- put up context menu here if appropriate - WM_NCRBUTTONUP - User has clicked the right button on the application's system menu - -********************************************************************/ -LRESULT CALLBACK WndProcDisplayInputStructure(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - int wmId, wmEvent; - PAINTSTRUCT ps; - HDC hdc; - MY_WINDOW_DATA *pWinData; - RECT rc; - -#define IS_WIN95 0 - - switch (message) { - - case WM_COMMAND: - wmId = LOWORD(wParam); /* Remember, these are... */ - wmEvent = HIWORD(wParam); /* ...different for Win32! */ - break; - - case WM_CLOSE: - pWinData = (MY_WINDOW_DATA *) GETWINDLONG; - pWinData->bEsc = 1; - goto close_window; - - case WM_RBUTTONUP: /* RightClick in the window client area */ - case WM_LBUTTONUP: /* LeftClick in the window client area */ - /* stop the timer */ - pWinData = (MY_WINDOW_DATA *) GETWINDLONG; - if ( pWinData->nTimerId ) { - KillTimer( hWnd, pWinData->nTimerId ); - pWinData->nTimerId = 0; - pWinData->bUserIntervened = 1; - } - /*InvalidateRect( hWnd, NULL, 0 ); */ - break; - - case WM_CHAR: - - pWinData = (MY_WINDOW_DATA *) GETWINDLONG; - if ( pWinData->nTimerId ) { - KillTimer( hWnd, pWinData->nTimerId ); - pWinData->nTimerId = 0; - pWinData->bUserIntervened = 1; - } - if ( wParam == '\r' || wParam == 27 ) { - pWinData->bEsc = (wParam == 27); - goto close_window; - } - - break; - - case WM_ERASEBKGND: - return TRUE; /* to prevent flicker do not let Windows erase background */ - - case WM_SIZE: - case WM_MOVE: - pWinData = (MY_WINDOW_DATA *) GETWINDLONG; - if ( pWinData->nTimerId ) { - KillTimer( hWnd, pWinData->nTimerId ); - pWinData->nTimerId = 0; - pWinData->bUserIntervened = 1; - } - break; - - case WM_DISPLAYCHANGE: /* Only comes through on plug'n'play systems */ - { - SIZE szScreen; - BOOL fChanged = (BOOL)wParam; - - szScreen.cx = LOWORD(lParam); - szScreen.cy = HIWORD(lParam); - - if (fChanged) { - /* The display 'has' changed. szScreen reflects the */ - /* new size. */ - ; /*MessageBox (GetFocus(), "Display Changed", szWindowClassName, 0); */ - } else { - /* The display 'is' changing. szScreen reflects the */ - /* original size. */ - MessageBeep(0); - } - } - break; - - case WM_TIMER: - pWinData = (MY_WINDOW_DATA *) GETWINDLONG; - if ( wParam == pWinData->nTimerId ) { - KillTimer( hWnd, pWinData->nTimerId ); - pWinData->nTimerId = 0; - goto close_window; - } - break; -/* - case WM_SHOWWINDOW: - break; -*/ - case WM_PAINT: - pWinData = (MY_WINDOW_DATA *) GETWINDLONG; - GetClientRect( hWnd, &rc ); - hdc = BeginPaint (hWnd, &ps); - /* the drawing code is here */ - /* - rc.top = 30; - rc.left= 50; - */ - CreateInputStructPicture( hdc, pWinData, &rc, 0, pWinData->nNewEquLabel ); - EndPaint (hWnd, &ps); - /* start the timer if requested */ - if ( !pWinData->nTimerId && !pWinData->bUserIntervened && pWinData->ulDisplTime ) { - pWinData->nTimerId = SetTimer( - hWnd, /* handle to window */ - MY_TIMER_ID, /* timer identifier */ - pWinData->ulDisplTime, /* time-out value */ - NULL /* ptr to the timer procedure */ - ); - } - break; - - case WM_DESTROY: - /* Tell WinHelp we don't need it any more... */ - /* WinHelp (hWnd, APPNAME".HLP", HELP_QUIT,(DWORD)0); */ - /*PostQuitMessage(0); */ - return (DefWindowProc(hWnd, message, wParam, lParam)); - break; - - default: - return (DefWindowProc(hWnd, message, wParam, lParam)); - } - goto exit_function; - -close_window: - - pWinData = (MY_WINDOW_DATA *) GETWINDLONG; - GetWindowRect( hWnd, &pWinData->rc ); - ReallySetForegroundWindow(GetConsoleHwnd()); - DestroyWindow( hWnd ); - - -exit_function: - - return (0); -} -/**********************************************************************************************/ -void FreeWinData( MY_WINDOW_DATA* pWinData ) -{ - if ( pWinData ) { - if ( pWinData->at0 ) { - inchi_free( pWinData->at0 ); - pWinData->at0 = NULL; - } - if ( pWinData->at1 ) { - inchi_free( pWinData->at1 ); - pWinData->at1 = NULL; - } - FreeInfoAtomData( &pWinData->inf_at_data ); - - if ( pWinData->nEquLabels ) { - inchi_free( pWinData->nEquLabels ); - pWinData->nEquLabels = NULL; - pWinData->nNumEquSets = 0; - } - - if ( pWinData->szTitle ) { - inchi_free(pWinData->szTitle); - pWinData->szTitle = NULL; - } - } -} -#ifndef TARGET_LIB_FOR_WINCHI -/**********************************************************************************************/ -int DisplayInputStructure( char *szOutputString, inp_ATOM *at, INF_ATOM_DATA *inf_at_data, int num_at, DRAW_PARMS *dp ) -{ -#define IS_WIN95 0 - HWND hWnd = NULL; - WNDCLASS wc; - HINSTANCE hInstance = 0; - RECT rc, rc2, rc3; - MSG msg; - MY_WINDOW_DATA WinData; - int ret, ret2, ret3, bRectVisible; - HDC hDesktopDC; - inf_ATOM *inf_at = inf_at_data? inf_at_data->at : NULL; - /*WINDOWPLACEMENT wndpl = {sizeof(WINDOWPLACEMENT),}; */ /*set wndpl.length */ - /* get console application window handle */ - HWND hConsoleWnd = GetConsoleHwnd(); - HWND hDesktopWnd = GetDesktopWindow(); - int bSetForeground = (hConsoleWnd == GetForegroundWindow()); - - /*printf("hConsoleWnd = %ld, hDesktopWnd = %ld\n", (long)hConsoleWnd, (long)hDesktopWnd ); */ - - if ( !hConsoleWnd || !hDesktopWnd ) - return (FALSE); /* failed */ - if ( !IsWindowVisible(hConsoleWnd) ) - return (FALSE); /* failed */ - - - /* we will create graphics window of the same size and position as our console window */ - /* to do that we need to get console app window size and position. */ - ret = GetWindowRect( hConsoleWnd, &rc ); - /*printf( "ConsoleWnd: ret=%d, rc=%ld %ld %ld %ld\n", ret, rc.left, rc.top, rc.right, rc.bottom); */ - /* full screen: "ConsoleWnd: ret=1, rc=-32000 -32000 -31840 -31976" */ - - ret2 = GetWindowRect( hDesktopWnd, &rc2 ); - /*printf( "DesktopWnd: ret=%d, rc2=%ld %ld %ld %ld\n", ret, rc2.left, rc2.top, rc2.right, rc2.bottom); */ - /* full screen: "DesktopWnd: ret=1, rc2=0 0 1280 1024" */ - - if ( !(hDesktopDC = GetWindowDC(hDesktopWnd) ) ) - return (FALSE); - bRectVisible = RectVisible( hDesktopDC, &rc ); - /*printf( "Console rect visible=%d\n", bRectVisible); */ - /* full screen: "Console rect visible=1" */ - - /*ret3 = GetClipBox(hDesktopDC, &rc3); */ - /*printf( "DesktopClip: ret=%d, rc3=%ld %ld %ld %ld\n", ret3, rc3.left, rc3.top, rc3.right, rc3.bottom); */ - /* full screen: "DesktopClip: ret=3, rc3=0 0 0 0", 3=COMPLEXREGION */ - - ret3 = ReleaseDC( hDesktopWnd, hDesktopDC ); - if ( !bRectVisible ) - return (FALSE); /* usually happens in MS-DOS full-screen mode, but the API call may fail and return TRUE */ - - if ( !IntersectRect(&rc3, &rc2, &rc) ) { - /*printf( "Console rect invisible\n", bRectVisible); */ - /* full screen: "Console rect invisible" */ - return (FALSE); /* usually happens in MS-DOS full-screen mode */ - } - - hInstance = GetModuleHandle (NULL); /* or =(HINSTANCE)GetWindowLong(hConsoleWnd, GWL_HINSTANCE); */ - if ( !dp->pdp->rcPict[2] || !dp->pdp->rcPict[3] ) { - dp->pdp->rcPict[0] = rc.left; - dp->pdp->rcPict[1] = rc.top; - dp->pdp->rcPict[2] = rc.right-rc.left; - dp->pdp->rcPict[3] = rc.bottom-rc.top; - } - - /* Fill in window class structure with parameters that describe */ - /* the main window. */ - wc.style = CS_HREDRAW | CS_VREDRAW; - wc.lpfnWndProc = (WNDPROC)WndProcDisplayInputStructure; /* window procedure */ - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.hInstance = hInstance; - wc.hIcon = LoadIcon (NULL, IDI_APPLICATION); - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); /*(COLOR_WINDOW+1); */ - wc.lpszMenuName = NULL; - wc.lpszClassName = szWindowClassName; - /* Register the window class and return success/failure code. */ - if ( !RegisterClass(&wc) ) - return FALSE; - - hWnd = CreateWindow(szWindowClassName, szOutputString, WS_OVERLAPPEDWINDOW, - dp->pdp->rcPict[0], dp->pdp->rcPict[1], dp->pdp->rcPict[2], dp->pdp->rcPict[3], - /*rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, */ - hConsoleWnd, NULL, hInstance, NULL); - - if (!hWnd) { - /* avoid nasty messages about exception 0x5 in Windows dlls. */ - UnregisterClass(szWindowClassName, /* address of class name string */ - hInstance /* handle of application instance */ - ); - return (FALSE); - } - - /* provide window procedure with the pointer to the chemical structure */ - memset( &WinData, 0, sizeof(WinData) ); - WinData.at0 =(inp_ATOM *)inchi_calloc(num_at+1, sizeof(WinData.at0[0])); - WinData.at1 =(inp_ATOM *)inchi_calloc(num_at+1, sizeof(WinData.at1[0])); - - if ( dp->nEquLabels && dp->nNumEquSets ) { - WinData.nEquLabels = (AT_NUMB *)inchi_calloc( num_at+1, sizeof(WinData.nEquLabels[0])); - WinData.nNumEquSets = dp->nNumEquSets; - WinData.nCurEquLabel = 0; - WinData.nNewEquLabel = dp->nCurEquLabel; - } - - WinData.nFontSize = dp->sdp.nFontSize; - WinData.szTitle = NULL; - /*WinData.szTitle = _strdup(szOutputString);*/ /* for testing INCHI_LIB printing */ - if ( inf_at ) { - DuplicateInfoAtomData( &WinData.inf_at_data, inf_at_data); - } - if ( !WinData.at0 || !WinData.at1 || inf_at && !WinData.inf_at_data.at - || dp->nEquLabels && dp->nNumEquSets && !WinData.nEquLabels - ) { - FreeWinData( &WinData ); - } else { - memcpy( WinData.at0, at, sizeof(at[0])*num_at ); - if ( inf_at ) - memcpy( WinData.inf_at_data.at, inf_at, sizeof(inf_at[0])*num_at ); - if ( WinData.nEquLabels ) { - memcpy( WinData.nEquLabels, dp->nEquLabels, num_at*sizeof(WinData.nEquLabels[0])); - } - - memcpy( WinData.at1, WinData.at0, sizeof(at[0])*num_at ); - - WinData.num_at = num_at; - WinData.bOrigAtom = dp->sdp.bOrigAtom; - WinData.nTimerId = 0; - WinData.ulDisplTime = dp->sdp.ulDisplTime; - if ( dp->sdp.tdp ) { - WinData.tdp = *dp->sdp.tdp; - } - } - - SETWINDLONG; - - ShowWindow(hWnd, SW_SHOWNORMAL /*SW_SHOW*/); - UpdateWindow(hWnd); - - - /* Message Loop for Display Canon Struct Window */ - while( IsWindow(hWnd) ) { - if ( PeekMessage( - &msg, /* pointer to structure for message */ - hWnd, /* or NULL,*/ /* handle to window */ - 0, /* UINT wMsgFilterMin, */ /* first message */ - 0, /* UINT wMsgFilterMax, */ /* last message */ - PM_REMOVE /* UINT wRemoveMsg */ /* removal flags */ - ) ) { - TranslateMessage( &msg ); - DispatchMessage( &msg ); - } else { - if ( bSetForeground ) { - ReallySetForegroundWindow( hWnd ); - bSetForeground = 0; - } - SleepEx( 10L, TRUE ); /* provides a nice behavior of the app */ - } - } - /* deallocate memory */ - FreeWinData( &WinData ); - /* show console window on the top upon closing hWnd */ - ReallySetForegroundWindow( hConsoleWnd ); - /* avoid nasty messages about exception 0x5 in Windows dlls. */ - UnregisterClass(szWindowClassName, /* address of class name string */ - hInstance /* handle of application instance */ - ); - /* Save window size and position */ - if ( WinData.rc.bottom > WinData.rc.top && WinData.rc.right > WinData.rc.left ) { - dp->pdp->rcPict[0] = WinData.rc.left; - dp->pdp->rcPict[1] = WinData.rc.top; - dp->pdp->rcPict[2] = WinData.rc.right-WinData.rc.left; - dp->pdp->rcPict[3] = WinData.rc.bottom-WinData.rc.top; - } - if ( WinData.bEsc ) { - dp->rdp.bEsc = 1; - } - return WinData.bEsc? 27:1; - -} -#endif -/****************************************************************************/ -void MySleep( unsigned long ms ) -{ - Sleep( ms ); -} - -#endif /* WIN32 */ -#else -int dummyDispStru_c; /* make translation unit non-empty for ANSI-C compatibility */ -#endif /* COMPILE_ANSI_ONLY */ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +/* Draw input atom -- Win32 specific */ + +#include "../../../INCHI_BASE/src/mode.h" + +#ifndef COMPILE_ANSI_ONLY + + +#ifdef _WIN32 +#include +#include +#include + + +#include "../../../INCHI_BASE/src/incomdef.h" +#include "../../../INCHI_BASE/src/ichidrp.h" +#include "../../../INCHI_BASE/src/inpdef.h" +#include "../../../INCHI_BASE/src/util.h" +#include "dispstru.h" +#include "../../../INCHI_BASE/src/extr_ct.h" + +#if ( defined(_WIN64) || defined(WIN64)) /* 64-bit build */ +#define GETWINDLONG GetWindowLongPtr( hWnd, GWLP_USERDATA ) +#define SETWINDLONG SetWindowLongPtr( hWnd, GWLP_USERDATA, (long)&WinData ) +#else /* 32-bit build */ +#define GETWINDLONG GetWindowLong( hWnd, GWL_USERDATA ) +#define SETWINDLONG SetWindowLong( hWnd, GWL_USERDATA, (long)&WinData ) +#endif + + +/* Font size */ +#define FONT_NAME "Arial" /* "MS Sans Serif"; */ +/* rgb colors */ +#define CLR_BLUE RGB( 0, 0, 255) +#define CLR_GREEN RGB( 0, 128, 0) +#define CLR_RED RGB(255, 0, 0) +#define CLR_PINK RGB(255, 128, 128) +#define CLR_CYAN RGB( 0, 255, 255) +#define CLR_LTGREEN RGB(128 ,255, 128) +#define CLR_LTPURPLE RGB(255 ,0xCC, 255) +#define CLR_YELLOW RGB(255, 255, 0) +#define CLR_BLACK RGB( 0, 0, 0) +#define CLR_LTGRAY RGB(0xCC, 0xCC, 0xCC) +#define CLR_MAGENTA RGB(255,0,255) +#define CLR_WHITE RGB(255, 255, 255) + +/* local prototypes */ +HWND GetConsoleHwnd(void); + +#define MY_TIMER_ID 1 + +typedef struct Box { + int xhigh, xlow; + int yhigh, ylow; +} BOX; + + +/* local prototypes */ +int DrawBond( HDC pDC, int x1, int y1, int x2, int y2, int b_type, int b_stereo, int b_parity, int bInvertBonds, COLORREF clrPen, int nPenWidth ); +int DrawBondStereo( HDC pDC, int x1, int y1, int x2, int y2, int b_stereo, int b_highlight, int bInvertBonds, COLORREF clrPen, int nPenWidth ); +int DrawBondNoStereo( HDC pDC, int x1, int y1, int x2, int y2, int b_type, int b_highlight, COLORREF clrPen, int nPenWidth ); +int DrawBondParity( HDC pDC, int x1, int y1, int x2, int y2, int parity_mark ); +void DrawLine( HDC, int, int, int, int ); +void DrawPenColorFilledPolygon( HDC pDC, const POINT* pnt, int num ); +int DrawTextColorDot( HDC pDC ); +int DrawString( HDC pDC, char *st1, int shift, int x, int y ); +int DrawPreparedString( HDC pDC, char *st1, int shift, int x, int y, int bHighlightTheAtom ); +int DrawColorString( HDC pDC, const char *st, int xs, int ys, int bHighlightTheAtom ); + /* structure drawing */ +int DrawStructure( HDC pDC, inp_ATOM *at, INF_ATOM_DATA *inf_at_data, int num_at, int xoff, int yoff, COLORREF clrPen, int nPenWidth ); + /* all drawing, including text strings and table */ +int DrawTheInputStructure( inp_ATOM *at, INF_ATOM_DATA *inf_at_data, int num_at, + HDC pDC, int tx_off, int ty_off, int xoff, int yoff, + int width_pix, int height_pix, int bDraw, int bOrigAtom, COLORREF clrPen, int nPenWidth ); + /* calculate sizes, run drawing */ +int CreateInputStructPicture( HDC hDC, MY_WINDOW_DATA *pWinData, RECT *rc, int bPrint, AT_NUMB nNewEquLabel ); + +void FreeWinData( MY_WINDOW_DATA* pWinData ); +void InpStructureMarkEquComponents( MY_WINDOW_DATA *pWinData, AT_NUMB nNewEquLabel, + inp_ATOM *at0, inp_ATOM *at1, inf_ATOM *inf_at, int num_at ); +int MyTextOutABC( const char *p, int iFst, int iLst, HDC pDC ); + +/* window procedure */ +LRESULT CALLBACK WndProcDisplayInputStructure(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + +/* main drawing function: create window, save drawing parameters in the window */ +int DisplayInputStructure( char *szOutputString, inp_ATOM *at, INF_ATOM_DATA *inf_at_data, int num_at, DRAW_PARMS *dp /*, int bTaut, unsigned long ulDisplTime, long *rcPict, int nFontSize*/ ); + + +int GetFontHeight( HDC ); +int GetFontAscent( HDC ); +int GetFontDescent( HDC pDC ); +int GetFontAveWidth( HDC ); +int GetStringWidth( HDC pDC, char *pString ); +int GetOneCharInStringWidth( HDC pDC, const char *pString ); +int MoveHydrogenAtomToTheLeft( char *s, int start, int H ); + +int nRound( double X ); +double RoundDouble(double X); +void roundoff_coord( double dx1, double dx2, int *new_ix1, int *new_ix2 ); +BOOL ReallySetForegroundWindow(HWND hWnd); +HWND GetConsoleHwnd(void); + + +/*********************************************************************/ +char szWindowClassName[] = "INChI_DrawStructWnd"; + +/*********************************************************************/ + +void PrintFileName( const char *fmt, FILE *out_file, const char *szFname ) +{ + char szBuf[_MAX_PATH]; + long lBufLen = sizeof(szBuf); + long lReqBufLen; + char *pName; + const char *p; + + lReqBufLen = GetFullPathName( szFname, lBufLen, szBuf, &pName ); + + if ( lReqBufLen && lReqBufLen < lBufLen ) { + p = szBuf; + } else { + p = szFname; + } + fprintf( out_file, fmt, p ); +} + +/*********************************************************************/ +BOOL ReallySetForegroundWindow(HWND hWnd) +{ + BOOL retVal = FALSE; + HWND hForegroundWnd; + if ( hWnd && IsWindow(hWnd) && (hForegroundWnd = GetForegroundWindow()) ) { + DWORD dwWindowThreadProcessId = GetWindowThreadProcessId(hForegroundWnd, NULL); + DWORD dwCurrentThreadId = GetCurrentThreadId(); + AttachThreadInput(dwWindowThreadProcessId, dwCurrentThreadId, TRUE); + retVal = SetForegroundWindow(hWnd); + AttachThreadInput(dwWindowThreadProcessId, dwCurrentThreadId, FALSE); + } + return retVal; +} +/*********************************************************************/ +HWND GetConsoleHwnd(void) +{ +#define MY_BUFSIZE 1024 /* Buffer size for console window titles. */ + HWND hwndFound; /* This is what is returned to the caller. */ + char pszNewWindowTitle[MY_BUFSIZE]; /* Contains fabricated */ + /* WindowTitle. */ + char pszOldWindowTitle[MY_BUFSIZE]; /* Contains original */ + /* WindowTitle. */ + + /* Fetch current window title. */ + + GetConsoleTitle(pszOldWindowTitle, MY_BUFSIZE); + + /* Format a "unique" NewWindowTitle. */ + + wsprintf(pszNewWindowTitle,"InChITmpWnd%ul/%ul", + (unsigned long)GetTickCount(), + (unsigned long)GetCurrentProcessId()); + + /* Change current window title. */ + SetConsoleTitle(pszNewWindowTitle); + + /* Ensure window title has been updated. */ + + Sleep(40); + + /* Look for NewWindowTitle. */ + + hwndFound=FindWindow(NULL, pszNewWindowTitle); + + /* download INChI packages + ShellExecute(hwndFound, "open", "http://www.iupac.org/inchi", "", "C:\\", SW_SHOWNORMAL); + */ + + /* Restore original window title. */ + + SetConsoleTitle(pszOldWindowTitle); + + return(hwndFound); +} +/****************************************************************************/ +int GetFontHeight( HDC pDC ) +{ + TEXTMETRIC TextMetric; + + GetTextMetrics( pDC, &TextMetric ); + + return TextMetric.tmHeight; +} +/****************************************************************************/ +int GetFontAscent( HDC pDC ) +{ + TEXTMETRIC TextMetric; + + GetTextMetrics( pDC, &TextMetric ); + + return TextMetric.tmAscent; +} +/****************************************************************************/ +int GetFontDescent( HDC pDC ) +{ + TEXTMETRIC TextMetric; + + GetTextMetrics( pDC, &TextMetric ); + + return TextMetric.tmDescent; +} + +/****************************************************************************/ +int GetFontAveWidth( HDC pDC) +{ + TEXTMETRIC TextMetric; + + GetTextMetrics( pDC, &TextMetric ); + + return TextMetric.tmAveCharWidth; +} +/****************************************************************************/ +int GetSubstringWidth( HDC pDC, int len, char *pString) +{ + SIZE Size; + int widthABC, i; + ABC abc; + GetTextExtentPoint32( pDC, pString, len, &Size ); + for ( i = widthABC = 0; i < len; i ++ ) { + if ( GetCharABCWidths( pDC, /* handle to DC */ + (int)pString[i], /* first character in range */ + (int)pString[i], /* last character in range */ + &abc ) /* array of character widths */ + ) { + widthABC += (int)abc.abcB + abs(abc.abcA) + abs(abc.abcC); + } else { + break; + } + } + if ( widthABC > Size.cx ) { + return (widthABC + 2*Size.cx)/3; /* hunch */ + } + return Size.cx; +} +/****************************************************************************/ +void GetTextSize( HDC pDC, int len, char *pString, int *width, int *height ) +{ + SIZE Size; + + GetTextExtentPoint32( pDC, pString, len, &Size ); + *width = Size.cx; + *height = Size.cy; +} +/****************************************************************************/ +void GetVertTextSize( HDC pDC, int len, char *pString, int *width, int *height ) +{ + SIZE Size; + TEXTMETRIC TextMetric; + int i; + GetTextMetrics( pDC, &TextMetric ); + *height = len * TextMetric.tmHeight; + *width = 0; + for ( i = 0; i < len; i ++ ) { + GetTextExtentPoint32( pDC, &pString[i], 1, &Size ); + *width = inchi_max( *width, (int)Size.cx ); + } +} +/****************************************************************************/ +BOOL TextOutVert( + HDC pDC, /* handle to DC */ + int nXStart, /* x-coordinate of starting position */ + int nYStart, /* y-coordinate of starting position */ + LPCTSTR lpString, /* character string */ + int cbString, /* number of characters */ + int cell_width /* width for center alignment */ + ) { + TEXTMETRIC TextMetric; + int i, dy, ret, char_width; + GetTextMetrics( pDC, &TextMetric ); + dy = TextMetric.tmHeight; + for ( i = 0, ret = 1; ret && i < cbString; nYStart += dy, i ++ ) { + char_width = GetOneCharInStringWidth( pDC, lpString+i ); + ret = TextOut( pDC, nXStart+(cell_width-char_width)/2, nYStart, lpString+i, 1 ); + } + return ret; +} +/****************************************************************************/ +BOOL TextOutHoriz( + HDC pDC, /* handle to DC */ + int nXStart, /* x-coordinate of starting position */ + int nYStart, /* y-coordinate of starting position */ + LPCTSTR lpString, /* character string */ + int cbString, /* number of characters */ + int cell_width + ) { + int dX = (cell_width && cbString == 1)? (cell_width - GetOneCharInStringWidth( pDC, lpString ))/2:0; + return TextOut( pDC, nXStart+dX, nYStart, lpString, cbString ); +} +/****************************************************************************/ +int GetStringWidth( HDC pDC, char *pString) +{ + SIZE Size; + + GetTextExtentPoint32( pDC, pString, (int) strlen( pString ), &Size ); + return Size.cx; +} + +/****************************************************************************/ +int GetOneCharInStringWidth( HDC pDC, const char *pString) +{ + SIZE Size; + GetTextExtentPoint32( pDC, pString, 1, &Size ); + + return Size.cx; +} + + +/****************************************************************************/ +int DrawStructure( HDC pDC, inp_ATOM *at, INF_ATOM_DATA *inf_at_data, int num_at, int xoff, int yoff, COLORREF clrPen, int nPenWidth ) +{ + int i, next, k; + int j, r, shift, b_parity=0, bDraw; + char str[64], *atname; /*str[sizeof(st->str[0])+1]; */ + inf_ATOM *inf_at = inf_at_data? inf_at_data->at : NULL; + int bUseInvFlags = (!inf_at_data)? 0 : inf_at_data->pStereoFlags? 1 : 2; + int bInvertBonds = 0; + /* + int bInvertBonds = inf_at_data && (inf_at_data->StereoFlags & INF_STEREO_INV); + */ + /* draw all straight lines (bonds) */ + + for ( i = 0; i < num_at; i ++ ) { + /* draw atom #i. */ + for ( j = 0; j < at[i].valence; j++ ) { + next = at[i].neighbor[j]; + bDraw = 1; + /* normally draw bonds if next > i; exception: disconnected terminal hydrogen atoms */ + if ( next < i ) { + /* check if it is a disconnected terminal atom. Disconnected atoms */ + /* have bonds to the rest of the structure; the bond from the rest */ + /* of the structure to the removed terminal atom has been removed. */ + for ( r = 0; r < at[next].valence; r ++ ) { + if ( at[next].neighbor[r] == i ) { + bDraw = 0; + break; + } + } + } + if ( bDraw ) { + /* at[i].bond_stereo[j] is negative if the pointing wedge */ + /* of a stereo bond is at the at[next] atom */ + if ( inf_at ) { + for ( k =0, b_parity = 0; k < MAX_STEREO_BONDS && inf_at[i].cStereoBondParity[k]; k ++ ) { + if ( inf_at[i].cStereoBondNumber[k] == j ) { + b_parity = inf_at[i].cStereoBondParity[k]; + if ( inf_at[i].cStereoBondWarning[k] ) { + b_parity = -b_parity; + } + break; + } + } + } + switch( bUseInvFlags ) { + case 1: + if ( at[i].component <= inf_at_data->num_components ) { + bInvertBonds = (0 != (inf_at_data->pStereoFlags[at[i].component] & INF_STEREO_INV)); + } else { + bInvertBonds = 0; + } + break; + case 2: + bInvertBonds = 0 != (inf_at_data->StereoFlags & INF_STEREO_INV); + break; + } + DrawBond( pDC, + nRound(at[i].x+xoff), nRound(at[i].y+yoff), + nRound(at[next].x+xoff), nRound(at[next].y+yoff), + at[i].bond_type[j], at[i].bond_stereo[j], b_parity, bInvertBonds, + clrPen, nPenWidth); + } + } + } + /* write all strings (heteroatoms + H) */ + if ( inf_at ) { + for ( i = 0; i < num_at; i++ ) + { + strcpy( str, inf_at[i].at_string ); + DrawPreparedString( pDC, str, -inf_at[i].DrawingLabelLeftShift, nRound(at[i].x+xoff), nRound(at[i].y+yoff), inf_at[i].cHighlightTheAtom ); + } + } else { + /* version which does not use inf_at */ + for ( i = 0; i < num_at; i++ ) { + + /* the direction of the shift: */ + shift = at[i].bDrawingLabelLeftShift; + /* input structure before normalizing: */ + /* terminal H atoms have not been disconnected; */ + /* isotopic H atoms numbers have not been added to num_H */ + atname = at[i].elname; + j = 0; + k = 0; + if ( at[i].iso_atw_diff && (!atname[0] || isupper(UCINT atname[0])) ) { + int atw = get_atomic_mass_from_elnum( (int)at[i].el_number ); + if ( atw ) { + k += sprintf( str+k, "^%d", atw+at[i].iso_atw_diff-(at[i].iso_atw_diff>0)); + } else { + k += sprintf( str+k, "^+%d", at[i].iso_atw_diff-(at[i].iso_atw_diff>0)); + } + } + /* obsolete section, this never happens for now */ + if ( atname[0] && atname[0] < ' ' && atname[1] ) { + /* special encoding. The 1st byte contains the 1st delimiter; next delimiters are '/'. */ + /* this allows to draw up to 5 numbers from 1..255 range. */ + int t; + int ch = ' ' + atname[0]; /* delimiter */ + k += sprintf( str+k, "%u", (unsigned)(unsigned char)atname[1]); + for ( t = 2; t < sizeof(at->elname); t ++ ) { + if ( atname[t] ) { + if ( t == 3 ) + ch = ','; /* comma after 2nd number to separate tautomer group info */ + if ( ch != '/' && ch != ',' ) + k += sprintf( str+k, "(%c)%u", ch, (unsigned)(unsigned char)atname[t]); + else + k += sprintf( str+k, "%c%u", ch, (unsigned)(unsigned char)atname[t]); + + ch = '/'; + } + } + } else { + /* this is the main section to display hydrogen atoms, charges, radicals */ + strncpy( str+k, atname+j, sizeof(at->elname)-j ); + str[sizeof(at->elname)+k-j] = '\0'; + k = strlen( str ); + if ( at[i].num_H ) { + strcat( str, "H" ); + k ++; + if ( at[i].num_H > 1 ) { + k += sprintf( str+k, "%d", (int)at[i].num_H ); + } + } + for ( j = 0; j < NUM_H_ISOTOPES; j ++ ) { + if ( at[i].num_iso_H[j] ) { + if ( j == 0 || j !=1 && j != 2 ) { + k += sprintf( str+k, "^%dH", j+1 ); + } else { + k += sprintf( str+k, j == 1? "D" : "T" ); + } + if ( at[i].num_iso_H[j] > 1 ) { + k += sprintf( str+k, "%d", (int)at[i].num_iso_H[j] ); + } + } + } + if ( abs(at[i].charge) > 1 ) + sprintf( str+k, "%+d", at[i].charge ); + else + if ( abs(at[i].charge) == 1 ) + strcat( str, at[i].charge>0? "+" : "-" ); + if ( at[i].radical ) + strcat( str, at[i].radical==RADICAL_SINGLET? ":" : + at[i].radical==RADICAL_DOUBLET? "." : + at[i].radical==RADICAL_TRIPLET? "^^" : "?"); + k = strlen(str); + sprintf( str+k, "/%d", i+1 ); /* atom ordering number, 1,2,... */ + } + + DrawString( pDC, str, shift, nRound(at[i].x+xoff), nRound(at[i].y+yoff) ); + } + } + return ( 1 ); +} + +/****************************************************************************/ +int DrawBond( HDC pDC, int x1, int y1, int x2, int y2, int b_type, int b_stereo, int b_parity, int bInvertBonds, COLORREF clrPen, int nPenWidth ) +{ + int abs_value_of_stereo = abs( b_stereo ); + int bond_parity_mark = b_type & BOND_MARK_PARITY; + int bond_highlight = b_type & BOND_MARK_HIGHLIGHT; + int ret; + b_type ^= (bond_parity_mark | bond_highlight); + + if ( !b_stereo || + /* => no stereo */ + b_type== BOND_TYPE_DOUBLE && abs_value_of_stereo!=STEREO_DBLE_EITHER || + /* => ignore unknown double bond stereo */ + b_type >= BOND_TYPE_TRIPLE || + /* => ignore bond stereo in case of triple bonds */ + b_type==BOND_TYPE_SINGLE && !(abs_value_of_stereo==STEREO_SNGL_UP || + abs_value_of_stereo==STEREO_SNGL_EITHER || + abs_value_of_stereo==STEREO_SNGL_DOWN) + /* => ignore unknown single bond stereo */ ) { + /*if ( b_type || !abs_value_of_stereo || (abs_value_of_stereo != 1 && abs_value_of_stereo != 4 && abs_value_of_stereo != 6) ) */ + ret = DrawBondNoStereo( pDC, x1, y1, x2, y2, b_type, 0!=bond_highlight, clrPen, nPenWidth ); + } else { + ret = DrawBondStereo( pDC, x1, y1, x2, y2, b_stereo, 0!=bond_highlight, bInvertBonds, clrPen, nPenWidth ); + } + if ( b_parity /* bond_parity_mark*/ ) { + DrawBondParity( pDC, x1, y1, x2, y2, b_parity ); + } + return ret; +} +/****************************************************************************/ +double RoundDouble(double X) +{ + return ((X)>0.0? floor((X)+0.5):-floor(0.4999999-(X))); +} +/****************************************************************************/ +int nRound( double X ) +{ + return (int)RoundDouble( X ); +} +/****************************************************************************/ +void roundoff_coord( double dx1, double dx2, int *new_ix1, int *new_ix2 ) +{ + int new_x1; + int new_x2; + int diff = (int) RoundDouble(dx2-dx1); + + if ( dx1 >= dx2 ) { + new_x1 = (int) RoundDouble(dx1); + new_x2 = new_x1 + diff; + } else { + new_x2 = (int) RoundDouble(dx2); + new_x1 = new_x2 - diff; + } + if ( new_ix1 ) + *new_ix1 = new_x1; + if ( new_ix2 ) + *new_ix2 = new_x2; +} +/****************************************************************************/ +void DrawPenColorFilledPolygon( HDC pDC, const POINT* pnt, int num ) +{ + LOGPEN LogPen; + HPEN penNew=(HPEN)GetStockObject(BLACK_PEN); + HPEN penOld=(HPEN)SelectObject(pDC, penNew); /* get current pen */ + HBRUSH brushNew, brushOld; + int ret; + ret = GetObject( penOld, sizeof(LogPen), &LogPen ); + SelectObject(pDC, penOld); + /* do not need to delete stock object penNew */ + if ( ret ) { + if ( brushNew = CreateSolidBrush(LogPen.lopnColor) ) { + brushOld = (HBRUSH)SelectObject( pDC, brushNew ); + Polygon( pDC, pnt, num ); + SelectObject( pDC, brushOld); + DeleteObject( brushNew ); + } + } +} +/****************************************************************************/ +int DrawTextColorDot( HDC pDC ) +{ + COLORREF clrColor = GetTextColor( pDC ); + int width = GetSubstringWidth( pDC, 1, "."); + HPEN penNew = CreatePen(PS_SOLID, 0, clrColor); + HBRUSH brushNew = CreateSolidBrush(clrColor); + /*int hfont = GetFontHeight( pDC ); */ + POINT pt; + int bSuccess = 0; + int nVertShift; + int nTextAlign = (TA_BOTTOM | TA_TOP | TA_BASELINE) & GetTextAlign( pDC ); + if ( TA_BASELINE == nTextAlign ) { + nVertShift = 0; + } else + if ( TA_BOTTOM == nTextAlign ) { + nVertShift = -GetFontDescent( pDC ); + } else + if ( TA_TOP == nTextAlign ) { + nVertShift = GetFontHeight( pDC ); /*GetFontAscent( pDC ); */ + } else { + nVertShift = GetFontHeight( pDC ); + } + if ( width % 2 ) { + width ++; + } + + GetCurrentPositionEx( pDC, &pt ); + if ( penNew && brushNew ) { + HPEN penOld = (HPEN) SelectObject( pDC, penNew ); + HBRUSH brushOld = (HBRUSH)SelectObject( pDC, brushNew ); + bSuccess = Ellipse( pDC, pt.x+width/2, pt.y - (3*width)/2+nVertShift, pt.x + (3*width)/2, pt.y-width/2+nVertShift ); + SelectObject( pDC, penOld ); + SelectObject( pDC, brushOld ); + if ( bSuccess ) { + MoveToEx( pDC, pt.x+(3*width)/2, pt.y, NULL ); + } + } + if ( penNew ) + DeleteObject( penNew ); + if ( brushNew ) + DeleteObject( brushNew ); + return bSuccess; +} +/****************************************************************************/ +int DrawBondStereo( HDC pDC, int x1, int y1, int x2, int y2, int b_stereo, int b_highlight, int bInvertBonds, COLORREF clrPen, int nPenWidth ) +{ + double lambda, mult, ax1, ay1, bond_sep, bond_width, bond_step, dx, dy, dx1, dy1; + int ix, ix1, iy, iy1, ix21, ix22, iy21, iy22; + int i, n; + int hfont = GetFontHeight( pDC ); + HPEN hHighlightPen=0, hOldPen = 0; + COLORREF clrHighlight = CLR_MAGENTA; + + + POINT pnt[4]; + + if ( b_highlight ) { + hHighlightPen = CreatePen( PS_SOLID, nPenWidth, clrHighlight ); + if ( !hHighlightPen ) + hHighlightPen = (HPEN)GetStockObject( BLACK_PEN ); /*should not fail */ + hOldPen = (HPEN)SelectObject( pDC, hHighlightPen ); + } + /* the wedge (pointed) end is at (x1, y1) unless b_stereo is negative */ + if ( b_stereo < 0 ) { + i = x1; + x1 = x2; + x2 = i; + i = y1; + y1 = y2; + y2 = i; + b_stereo = -b_stereo; /* 1=Up (solid triangle), 6=Down (Dashed triangle), 4=Either (zigzag triangle) */ + } + if ( bInvertBonds ) { + switch ( b_stereo ) { + case STEREO_SNGL_UP: + b_stereo = STEREO_SNGL_DOWN; + break; + case STEREO_SNGL_DOWN: + b_stereo = STEREO_SNGL_UP; + break; + } + } + + dx = x2 - x1; + dy = y2 - y1; + lambda = dx*dx + dy*dy; + if ( lambda == 0.0 ) + return 0; + lambda = sqrt( lambda ); /* bond length */ + + bond_width = hfont / 6.0; + if ( bond_width < 2.0 ) + bond_width = 2.0; /* half-width */ + /* from x1 to x2 */ + bond_width = inchi_min( bond_width, lambda / 8.0 ); + bond_width = floor( 2.0*bond_width + 0.5 )/2; /* half of actual bond width */ + + bond_step = hfont / 4.0; + bond_step = inchi_min( bond_step, lambda/6.0 ); + bond_step = inchi_max( bond_step, 3 ); + + if ( b_stereo == 3 ) { + bond_width *= 2.0; /* otherwise looks strange */ + } + + ax1 = -bond_width * dy / lambda; + ay1 = bond_width * dx / lambda; + + switch( b_stereo ) { + + case STEREO_SNGL_UP: /* Up */ + roundoff_coord( x2 - ax1, x2 + ax1, &ix, &ix1 ); + roundoff_coord( y2 - ay1, y2 + ay1, &iy, &iy1 ); + pnt[0].x = x1; + pnt[0].y = y1; + + pnt[1].x = ix1; + pnt[1].y = iy1; + + pnt[2].x = ix; + pnt[2].y = iy; + + DrawPenColorFilledPolygon( pDC, pnt, 3 ); + break; + case STEREO_DBLE_EITHER: /* cis or trans double bond */ + roundoff_coord( x2 - ax1, x2 + ax1, &ix21, &ix22 ); + roundoff_coord( y2 - ay1, y2 + ay1, &iy21, &iy22 ); + + roundoff_coord( x1 - ax1, x1 + ax1, &ix, &ix1 ); + roundoff_coord( y1 - ay1, y1 + ay1, &iy, &iy1 ); + + DrawLine( pDC, ix21, iy21, ix1, iy1 ); + DrawLine( pDC, ix22, iy22, ix, iy ); + + break; + + case STEREO_SNGL_EITHER: /* either */ + + n = (int)floor( lambda / bond_step ); + n = inchi_max( n, 4 ); + dx = dy = 0.0; + ix = iy = 0; + for ( i = 1; i <= 3*n/2; i ++ ) { + + mult = (double)i/(double)n/1.5; + dx1 = (x2-x1)*mult; + dy1 = (y2-y1)*mult; + if ( i % 2 ) { + dx1 += ax1*mult; + dy1 += ay1*mult; + } else { + dx1 -= ax1*mult+ax1/bond_width; + dy1 -= ay1*mult+ay1/bond_width; + } + + roundoff_coord( dx, dx1, NULL, &ix1 ); + roundoff_coord( dy, dy1, NULL, &iy1 ); + + /*line( win, x1 + ix, y1 + iy, x1 + ix1, y1 + iy1 ); */ + DrawLine( pDC, x1 + ix, y1 + iy, x1 + ix1, y1 + iy1 ); + dx = dx1; + dy = dy1; + ix = ix1; + iy = iy1; + /*line( win, x1 + ( int ) dx, y1 + ( int ) dy, x1 + ( int ) dx1, y1 + ( int ) dy1 ); */ + } + break; + + case STEREO_SNGL_DOWN: /* Down */ + + n = (int)floor( lambda / bond_step ); + n = inchi_max( n, 4 ); + + for ( i = 0; i <= n; i ++ ) { + mult = (double)i/(double)n; + + dx = dx1 = (x2-x1)*mult; /* x1,y1 offset */ + dy = dy1 = (y2-y1)*mult; + + mult = (double)(i)/(double)(n); + + bond_sep = ax1*mult; + dx += bond_sep; + dx1 -= bond_sep+ax1/bond_width; + + bond_sep = ay1*mult; + dy += bond_sep; + dy1 -= bond_sep+ay1/bond_width; + + roundoff_coord( dx, dx1, &ix, &ix1 ); + roundoff_coord( dy, dy1, &iy, &iy1 ); + + DrawLine( pDC, x1 + ix, y1 + iy, x1 + ix1, y1 + iy1 ); + } + break; + } + /* cleanup */ + if ( hOldPen ) + SelectObject( pDC, hOldPen ); + if ( hHighlightPen ) + DeleteObject( hHighlightPen ); + + return 0; +} +/****************************************************************************/ +int DrawBondParity( HDC pDC, int x1, int y1, int x2, int y2, int parity_mark0 ) +{ +/* int hfont = GetFontHeight( pDC ); */ + int xs, ys, width, height, parity_mark; + char *p; + COLORREF clrTextOld; + + if ( parity_mark0 < 0 ) { + parity_mark = -parity_mark0; + clrTextOld = SetTextColor( pDC, CLR_RED ); + } else { + parity_mark = parity_mark0; + } + + if ( parity_mark == BOND_MARK_ODD ) p = "(-)"; else + if ( parity_mark == BOND_MARK_EVEN ) p = "(+)"; else + if ( parity_mark == BOND_MARK_UNDF ) p = "(?)"; else + if ( parity_mark == BOND_MARK_UNKN ) p = "(u)"; else + if ( parity_mark == BOND_MARK_ERR ) p = "(!)"; else return 0; + + if ( abs( x2-x1 ) > 10 * abs(y2 - y1) ) { + /* almost horizontal bond; draw parity closer (1:2) to the right end */ + if ( x2 < x1 ) { + int tmp; + tmp = x2; + x2 = x1; + x1 = tmp; + tmp = y2; + y2 = y1; + y1 = tmp; + } + xs = 2*x1 + (4*(x2-x1))/3; /* 2/3 shift */ + ys = 2*y1 + (4*(y2-y1))/3; /* 2/3 shift */ + } else { + xs = x1 + x2; /* middle */ + ys = y1 + y2; /* middle */ + } + GetTextSize( pDC, (int) strlen(p), p, &width, &height ); + /* + width = GetFontAveWidth( pDC); + height = GetFontAscent( pDC ); + */ + xs = (xs - width )/2; + ys = (ys - height )/2; + + TextOut( pDC, xs, ys, p, 3 ); + + if ( parity_mark0 < 0 ) { + SetTextColor( pDC, clrTextOld ); + } + + return 0; +} +/****************************************************************************/ +int DrawBondNoStereo( HDC pDC, int x1, int y1, int x2, int y2, int b_type, int b_highlight, COLORREF clrPen, int nPenWidth ) +{ + double lambda, ax1, ax2, ay1, ay2, bond_sep; + int hfont = GetFontHeight( pDC ); + int ret = 0; + HPEN hSolidPen=0, hDashedPen=0, hLongDashedPen=0, hHighlightPen = 0, hOldPen=0; + char c=0, r=0, l=0, bDashed=1, bLongDashed=0, i; + COLORREF clr = clrPen; + + switch( b_type ) { /* c=center, l=left, r=right */ + case 0: + c = 'D'; + b_highlight = 1; + break; + case BOND_SINGLE: /* S=solid, D=dashed, L=long dashed(Green) */ + c = 'S'; + bDashed = 0; + break; + case BOND_DOUBLE: + l = r = 'S'; + bDashed = 0; + break; + case BOND_TRIPLE: + l = r = c = 'S'; + bDashed = 0; + break; + case BOND_TAUTOM: + l = r = 'D'; + break; + case BOND_ALTERN: /* 1 or 2 */ + l = 'S'; + r = 'D'; + break; + case BOND_ALT12NS: /* 1 or 2, non-stereo */ + l = 'L'; + r = 'D'; + bLongDashed = 1; + break; + case BOND_ALT_13: /* 1 or 3 */ + l = 'S'; + c = r = 'D'; + break; + case BOND_ALT_23: /* 2 or 3 */ + l = c = 'S'; + r = 'D'; + break; + case BOND_ALT_123: /* 1 or 2 or 3 */ + c = 'S'; + l = r = 'D'; + break; + default: + c = 'D'; + break; + } + if ( bLongDashed ) { + clr = CLR_GREEN; + } + /* -- debug -- + if ( c && l && r ) { + int stop = 1; + } + */ + if ( b_highlight ) { + clr = CLR_MAGENTA; + if ( c=='S' || l == 'S' || r == 'S' ) { + hHighlightPen = CreatePen( PS_SOLID, nPenWidth, clr ); + if ( !hHighlightPen ) + hHighlightPen = (HPEN)GetStockObject( BLACK_PEN ); /*should not fail */ + hOldPen = (HPEN)SelectObject( pDC, hHighlightPen ); + } + } + + if ( bDashed ) { + hDashedPen = CreatePen( PS_DOT, 1, clr ); + if ( !hDashedPen ) + hDashedPen = (HPEN)GetStockObject( BLACK_PEN ); /*should not fail */ + } + if ( bLongDashed ) { + hLongDashedPen = CreatePen( PS_DASH, 1, clr ); + if ( !hLongDashedPen ) + hLongDashedPen = (HPEN)GetStockObject( BLACK_PEN ); /*should not fail */ + } + + /* draw lines between the atoms */ + if ( c == 'S' ) { + DrawLine( pDC, x1, y1, x2, y2 ); + } else + if ( c == 'D' ) { + hSolidPen = (HPEN)SelectObject( pDC, hDashedPen ); + DrawLine( pDC, x1, y1, x2, y2 ); + hDashedPen = (HPEN)SelectObject( pDC, hSolidPen ); + } else + if ( c == 'L' ) { + hSolidPen = (HPEN)SelectObject( pDC, hLongDashedPen ); + DrawLine( pDC, x1, y1, x2, y2 ); + hLongDashedPen = (HPEN)SelectObject( pDC, hSolidPen ); + } + + /* draw lines parallel to line between bonds */ + if ( l || r ) { + + if ( b_type == BOND_DOUBLE ) { + bond_sep = hfont / 12.0; + if( ( x1 == x2 ) || ( y1 == y2 ) ) { + if ( bond_sep < 1.0 ) bond_sep = 1.0; + } else { + if ( bond_sep < 2.0 ) bond_sep = 2.0; + } + } else { + bond_sep = hfont / 6.0; + if( ( x1 == x2 ) || ( y1 == y2 ) ) { + if ( bond_sep < 2.0 ) bond_sep = 2.0; + } else { + if ( bond_sep < 4.0 ) bond_sep = 4.0; + } + } + + lambda = ( ( double ) ( x2 - x1 ) ) * ( x2 - x1 ) + + ( ( double ) ( y2 - y1 ) ) * ( y2 - y1 ); + + if ( lambda > 0.0 ) + lambda = bond_sep / sqrt( lambda ); + else { + ret = 1; + goto exit_function; + } + for ( i = 0; i < 2; i ++, lambda = -lambda ) { + c = i? r : l; + ax1 = lambda * ( y1 - y2 ) + x1; + ay1 = lambda * ( x2 - x1 ) + y1; + ax2 = lambda * ( y1 - y2 ) + x2; + ay2 = lambda * ( x2 - x1 ) + y2; + + if ( c == 'S' ) { + DrawLine( pDC, ( int ) ax1, ( int ) ay1, ( int ) ax2, ( int ) ay2 ); + } else + if ( c == 'D' ) { + hSolidPen = (HPEN)SelectObject( pDC, hDashedPen ); + DrawLine( pDC, ( int ) ax1, ( int ) ay1, ( int ) ax2, ( int ) ay2 ); + hDashedPen = (HPEN)SelectObject( pDC, hSolidPen ); + } else + if ( c == 'L' ) { + hSolidPen = (HPEN)SelectObject( pDC, hLongDashedPen ); + DrawLine( pDC, x1, y1, x2, y2 ); + hLongDashedPen = (HPEN)SelectObject( pDC, hSolidPen ); + } + } + } + +exit_function: + /* make sure DC has its initial pen */ + if ( hSolidPen ) + SelectObject( pDC, hSolidPen ); + if ( hOldPen ) + SelectObject( pDC, hOldPen ); + /* delete all newly created pens */ + if ( hDashedPen ) + DeleteObject( hDashedPen ); + if ( hLongDashedPen ) + DeleteObject( hLongDashedPen ); + if ( hHighlightPen ) + DeleteObject( hHighlightPen ); + + return ( ret ); +} +/****************************************************************************/ +int MoveHydrogenAtomToTheLeft( char *s, int start, int H ) +{ + int len, c, num_alpha; + char szBuffer[16]; + char *pH = strchr( s+start, H ); + for ( pH = s+start, num_alpha = 0; (c= UCINT*pH) && c != H && c != '/'; pH ++ ) { + num_alpha = isalpha( c ); + } + if ( c == H && num_alpha ) { /* do not search beyond the first slash */ + for ( len = 1; pH[len] && isdigit( UCINT pH[len] ); len ++ ) + ; + if ( len >= (int)sizeof(szBuffer) ) + return start; /* too long string */ + memcpy( szBuffer, pH, len ); + memmove( s+len, s, pH - s ); + memmove( s, szBuffer, len ); + return start+len; /* (pH-s)+i; */ + } + return start; +} +/****************************************************************************/ +int MyTextOutABC( const char *p, int iFst, int iLst, HDC pDC ) +{ + ABC abc; + POINT pt; + if ( iFst > iLst || iFst < 0 || iLst < 0 ) + return 0; + GetCurrentPositionEx( pDC, &pt ); + if ( GetCharABCWidths( + pDC, /* handle to DC */ + (int)p[iFst], /* first character in range */ + (int)p[iFst], /* last character in range */ + &abc /* array of character widths */ + ) && abc.abcA < 0 ) { + pt.x -= abc.abcA; + MoveToEx( pDC, pt.x, pt.y, NULL ); + } + TextOut( pDC, pt.x, pt.y, p+iFst, iLst-iFst+1 ); + if ( GetCharABCWidths( + pDC, /* handle to DC */ + (int)p[iLst], /* first character in range */ + (int)p[iLst], /* last character in range */ + &abc /* array of character widths */ + ) && abc.abcC < 0 ) { + GetCurrentPositionEx( pDC, &pt ); + pt.x -= abc.abcC; + MoveToEx( pDC, pt.x, pt.y, NULL ); + } + return 1; +} +/****************************************************************************/ +int DrawColorString( HDC pDC, const char *st, int xs, int ys, int bHighlightTheAtom ) +{ + int afont = GetFontAscent( pDC ); + COLORREF clrBk = CLR_WHITE; + int nNumSlash = 0; + COLORREF OrigBkColor = CLR_WHITE; + COLORREF OrigTxColor = GetTextColor( pDC ); + COLORREF NewBkColor; + int bWritingAtomStringColored = 0; + /* Draw the string character by character. */ + /* For each character within the first part of the string */ + /* make a decision if it is a subscript or a superscript */ + /* The first part ends with '/'=start of the canonical number or '(' = start of the parity mark */ + /* The superscript first character is ^ or + or - or . (. initiates double shift) */ + /* The the superscript ends with not the first character and not a digit or the end of the first part */ + /* The first character of a subscript (if it is not in the superscript) is a digit */ + /* The subscript ends a non-digit */ + const char *p=st; + UINT uPrevTextAlign = SetTextAlign( pDC, TA_UPDATECP); + POINT pt, pt0; + int i, i0, len, bSuperscript, bSubscript, bWritingAtomString, nShift, nShift2; + int bNewBkColor, bMoveToEx, bBypassCurrentChar; + nShift = afont/2; + nShift2 = nShift/2; + if ( bHighlightTheAtom ) { + clrBk = SetBkColor( pDC, CLR_LTPURPLE ); + } else + if ( strchr(st, '~') ) { + clrBk = SetBkColor( pDC, CLR_CYAN ); + } /* else { == for debugging == + clrBk = SetBkColor( pDC, CLR_LTGRAY ); + } */ + if ( st[0] == '!' ) { + OrigTxColor = SetTextColor( pDC, CLR_RED ); + bWritingAtomStringColored = 1; + p ++; + } + bSuperscript = bSubscript = 0; + bWritingAtomString = 1; + MoveToEx( pDC, xs, ys, NULL ); + for ( i = i0 = 0, len = strlen( p ); i < len; i ++ ) { + bNewBkColor = 0; + bMoveToEx = 0; + if ( bWritingAtomString ) { + GetCurrentPositionEx( pDC, &pt ); + pt0 = pt; + if ( !p[i] || p[i] == '/' || p[i] == '(' ) { + bWritingAtomString = 0; + if ( bSuperscript ) { + while( bSuperscript > 1 ) { + pt.y += nShift2; + bSuperscript --; + } + pt.y += nShift; + /* MoveToEx( pDC, pt.x, pt.y, NULL ); */ + bMoveToEx = 1; + bSuperscript = 0; + } + if ( bSubscript ) { + pt.y -= nShift; + bMoveToEx = 1; + /* MoveToEx( pDC, pt.x, pt.y, NULL ); */ + bSubscript = 0; + } + } else { + /* turn Superscript off */ + if ( bSuperscript == 2 && p[i] != '.' ) { + pt.y += nShift2; + bMoveToEx = 1; + /* MoveToEx( pDC, pt.x, pt.y, NULL ); */ + bSuperscript -= 1; + } + if ( bSuperscript && !isdigit((int)(unsigned char)p[i]) && p[i] != '+' && p[i] != '-' && p[i] != '.' ) { + while( bSuperscript > 1 ) { + pt.y += nShift2; + bSuperscript --; + } + pt.y += nShift; + bMoveToEx = 1; + /* MoveToEx( pDC, pt.x, pt.y, NULL ); */ + bSuperscript = 0; + } + /* turn Subscript off */ + if ( bSubscript && !isdigit((int)(unsigned char)p[i] ) ) { + pt.y -= nShift; + bMoveToEx = 1; + /* MoveToEx( pDC, pt.x, pt.y, NULL ); */ + bSubscript = 0; + } + /* turn Subscript on after non-space and non-digit */ + if ( !bSuperscript && !bSubscript && ( i && p[i-1] != ' ' && !isdigit((int)(unsigned char)p[i-1]) && isdigit((int)(unsigned char)p[i])) ) { + pt.y += nShift; + bMoveToEx = 1; + /* MoveToEx( pDC, pt.x, pt.y, NULL ); */ + bSubscript = 1; + } + /* turn Superscript on */ + if ( !bSuperscript && !bSubscript && + (p[i] == '^' || p[i] == '.' || + (p[i] == '+' || p[i] == '-') && (i && p[i-1]!=' ' ) ) ) { + pt.y -= nShift; + bSuperscript += 1; + if ( p[i] == '.' ) { + bSuperscript += 1; /* special case */ + pt.y -= nShift2; + } + bMoveToEx = 1; + /* MoveToEx( pDC, pt.x, pt.y, NULL ); */ + if ( p[i] == '^' ) { + goto output_the_string; + continue; /* leading superscript: isotopic mass */ + } + } + if ( bSuperscript == 1 && !bSubscript && p[i] == '.' ) { + pt.y -= nShift2; + bSuperscript += 1; + bMoveToEx = 1; + /* MoveToEx( pDC, pt.x, pt.y, NULL ); */ + } + } + } + if ( p[i] == '/' ) { + nNumSlash ++; + if ( nNumSlash == 1 ) { + OrigBkColor = GetBkColor( pDC ); + bNewBkColor ++; + NewBkColor = CLR_YELLOW; + /* OrigBkColor = SetBkColor( pDC, CLR_YELLOW);*/ /* CLR_CYAN ); */ + } + if ( nNumSlash == 3 ) { + bNewBkColor ++; + NewBkColor = CLR_CYAN; + /* SetBkColor( pDC, CLR_CYAN ); */ + } + } +#ifdef DISPLAY_DEBUG_DATA + else if ( nNumSlash && p[i] == '`' ) { + bNewBkColor ++; + NewBkColor = CLR_LTPURPLE; + /* SetBkColor( pDC, CLR_LTPURPLE );*/ /* DebugData */ + } +#endif + /* output one character; special treatment for '.' */ +output_the_string: + bBypassCurrentChar = ( p[i] == '.' && bSuperscript || p[i] == '^' ); + if ( bBypassCurrentChar || bNewBkColor || bMoveToEx ) { + /* output the accumulated string */ + int iLast = i-(bBypassCurrentChar || bMoveToEx); + /* + int k; + for ( k = i0; k <= iLast; k ++ ) { + MyTextOutABC( p, k, k, pDC ); + } + */ + MyTextOutABC( p, i0, iLast, pDC ); /* including iLast */ + i0 = i+1-bMoveToEx; + if ( p[i0] == '^' ) + i0 ++; + if ( bMoveToEx ) { + int dx = pt.x - pt0.x; + int dy = pt.y - pt0.y; + GetCurrentPositionEx( pDC, &pt ); + pt.x += dx; + pt.y += dy; + MoveToEx( pDC, pt.x, pt.y, NULL ); + } + if ( bNewBkColor ) { + SetBkColor( pDC, NewBkColor); + } + if ( p[i] == '.' && bSuperscript ) { + if ( !DrawTextColorDot( pDC ) ) { + TextOut( pDC, xs, ys, p+i, 1 ); + } + } + } + /* + if ( p[i] != '.' || !bSuperscript || !DrawTextColorDot( pDC ) ) { + TextOut( pDC, xs, ys, p+i, 1 ); + } + */ + } + MyTextOutABC( p, i0, i-1, pDC ); /* output the rest of the string */ + + SetTextAlign( pDC, uPrevTextAlign); + if ( nNumSlash ) { + SetBkColor( pDC, OrigBkColor ); + } + if ( bHighlightTheAtom || strchr(st, '~') ) { + SetBkColor( pDC, clrBk ); + } + if ( bWritingAtomStringColored ) { + SetTextColor( pDC, OrigTxColor ); + bWritingAtomStringColored = 0; + } + + return ( 1 ); +} +/****************************************************************************/ +int DrawPreparedString( HDC pDC, char *st1, int shift, int x, int y, int bHighlightTheAtom ) +{ + DrawColorString( pDC, st1, x+shift, y-GetFontAscent( pDC )/2, bHighlightTheAtom ); + return 1; +} +/****************************************************************************/ +int DrawString( HDC pDC, char *st1, int shift, int x, int y ) +{ + char st[256]; + int xs, ys, l, k; +/* int hfont = GetFontHeight( pDC ); */ + int afont = GetFontAscent( pDC ); +/* COLORREF clrBk; */ +/* int nNumSlash = 0; */ +/* COLORREF OrigBkColor; */ + + strncpy( st, st1, sizeof(st)-1 ); + st[sizeof(st)-1] = '\0'; + + l = GetStringWidth( pDC, st ); + + /* Default values */ + xs = x - l / 2; + ys = y - afont / 2; + + /* Single element */ + + if( strlen( st ) == 1 ) goto draw; + + /* Changing order for right/left connection */ + + if( shift ) { + int start = 0; + start = MoveHydrogenAtomToTheLeft( st, start, 'T' ); + start = MoveHydrogenAtomToTheLeft( st, start, 'D' ); + start = MoveHydrogenAtomToTheLeft( st, start, 'H' ); + /* determine the position */ + + if( ( strlen( st ) == 2 ) && islower( st[1] ) ) goto draw; + + k = GetStringWidth( pDC, st ); + k -= GetOneCharInStringWidth( pDC, ( st + strlen( st ) - 1 ) ) / 2; + xs = x - k; + } else { + + /* determine the position */ + + k = GetOneCharInStringWidth( pDC, st ); + xs = x - k / 2; + } + + draw: + DrawColorString( pDC, st, xs, ys, 0 ); + return ( 1 ); +} + +/****************************************************************************/ +void DrawLine( HDC pDC, int x1, int y1, int x2, int y2 ) +{ + MoveToEx( pDC, x1, y1, NULL ); + LineTo( pDC, x2, y2 ); +} +/****************************************************************************/ +int nGetNumLegendOptions( inf_ATOM *inf_at, int num_at ) +{ + int i, n, nmax=0; + char *p; + for ( i = 0; i < num_at; i ++ ) { + for ( n = 0, p = inf_at[i].at_string; p = strchr( p, '/' ); p ++, n++ ) + ; + nmax = inchi_max( nmax, n ); + } + return nmax; +} +/****************************************************************************/ +int DrawTheInputStructure( inp_ATOM *at, INF_ATOM_DATA *inf_at_data, int num_at, + HDC pDC, int tx_off, int ty_off, int xoff, int yoff, + int width_pix, int height_pix, int bDraw, int bOrigAtom, COLORREF clrPen, int nPenWidth ) +{ + int RetVal; + char *NoRoom = "Window is too small"; +#ifdef TARGET_LIB_FOR_WINCHI + static char PressEnter[] = ""; +#else + static char PressEnter[] = "Press Enter to continue."; +#endif + static char Legend[] = "Legend:"; + char **Str; + int num_str; + inf_ATOM *inf_at = inf_at_data? inf_at_data->at : NULL; + static char LastString[256]; + static char *LastStr[] = { "Atom / Atom Id", " / Non-stereo class", " / Mobile group id", " / Mobile group class", "" }; + static char *StrOrig[] = {PressEnter, Legend, "Atom / Input atom number"}; + static char *StrInfo[] = {PressEnter, Legend, LastString}; + + int nFontHeight, nFontAveWidth, afont, i, x, y, n_opt; + COLORREF rgbColor; + + RetVal = 0; + nFontHeight = GetFontHeight( pDC ); + nFontAveWidth = GetFontAveWidth( pDC ); + afont = GetFontAscent( pDC ); + + + if ( bDraw ) { + /* drawing */ + DrawStructure( pDC, at, inf_at_data, num_at, xoff, yoff, clrPen, nPenWidth ); + /* draw the message and legend: */ + if ( !inf_at || bOrigAtom ) { + Str = StrOrig; + num_str = sizeof(StrOrig)/sizeof(StrOrig[0]); + } else { + n_opt = nGetNumLegendOptions( inf_at, num_at ); + Str = StrInfo; + num_str = sizeof(StrInfo)/sizeof(StrInfo[0]); + for ( i = 0, LastString[0] = '\0'; i < n_opt && LastStr[i][0]; i ++ ) { + strcat(LastString, LastStr[i]); + } + } + rgbColor = SetBkColor( pDC, CLR_CYAN ); + x = nFontAveWidth; + y = nFontHeight/5; + for ( i = 0; i < num_str; i ++ ) { + if ( i+1 < num_str ) { + TextOut( pDC, x+tx_off, y+ty_off, Str[i], (int) strlen(Str[i]) ); + x += GetStringWidth( pDC, Str[i] )+2*nFontAveWidth; + if ( i == 0 ) { + SetBkColor( pDC, rgbColor ); + rgbColor = SetTextColor( pDC, CLR_BLUE /*CLR_RED*/ ); + } else + if ( i == 1 ) { + SetTextColor( pDC, rgbColor ); + } + } else { + DrawString( pDC, Str[i], 0, x+tx_off, y+afont/2+ty_off ); + } + } + } else { + rgbColor = SetTextColor( pDC, CLR_RED ); + TextOut( pDC, nFontAveWidth+tx_off, nFontHeight/5+ty_off, NoRoom, (int) strlen(NoRoom) ); + rgbColor = SetTextColor( pDC, rgbColor ); + } + return RetVal; +} + + +/****************************************************************************/ +typedef struct tagTableParms { + int thdrHeight; + int thdrWidth; + int tcellHeight; + int tcellWidth; + int tblHeight; + int tblWidth; + int tblRows; + int tblCols; + int xtblOffs; + int ytblOffs; +} TBL_PARMS; +/****************************************************************************/ +void CalcTblParms( HDC hMemoryDC, TBL_PARMS *tp, TBL_DRAW_PARMS *tdp, + int *xStructOffs, int *yStructOffs, int *xStructSize, int *yStructSize, int yoffs1) +{ + int i, j, n, w, h; + tp->tblCols = tdp->bDrawTbl; + tp->tblRows = 0; + for ( i = 0; i < tp->tblCols; i ++ ) { + (tdp->nOrientation? GetVertTextSize + : GetTextSize)( hMemoryDC, (int) strlen(tdp->ReqShownFoundTxt[i]), tdp->ReqShownFoundTxt[i], &w, &h ); + tp->thdrHeight=inchi_max(h, tp->thdrHeight); + tp->thdrWidth =inchi_max(w, tp->thdrWidth ); + + for ( j = 0, n = 0; j < TDP_NUM_PAR; j ++ ) { + if ( tdp->ReqShownFound[i][j] >= ' ' ) { + GetTextSize( hMemoryDC, 1, &tdp->ReqShownFound[i][j], &w, &h ); + tp->tcellHeight=inchi_max(h, tp->tcellHeight); + tp->tcellWidth =inchi_max(w, tp->tcellWidth ); + n ++; /* number of types of requested or found or shown features (type: B/T, I/N, S) */ + } + } + tp->tblRows = inchi_max(tp->tblRows, n); + } + if ( tdp->nOrientation ) { /* here are tp->tblCols columns and tp->tblRows rows. */ + tp->tblHeight = tp->thdrHeight + (2*tp->tblRows+2)*tp->tcellHeight; /* empty lines above the header and around each cell */ + tp->thdrWidth = tp->tcellWidth = inchi_max( tp->tcellWidth, tp->thdrWidth ); + tp->tblWidth = (2*tp->tblCols+1) * tp->tcellWidth; /* add empty columns around each column */ + *xStructOffs += tp->tblWidth; /* draw on the left margine */ + /* *yStructSize -= tp->tblHeight; */ + *xStructSize -= tp->tblWidth; + tp->xtblOffs = 0; + tp->ytblOffs = yoffs1; + } else { /* Do not believe your eyes: here are tp->tblCols rows and tp->tblRows columns. */ + tp->thdrHeight = tp->tcellHeight = inchi_max(tp->thdrHeight, tp->tcellHeight); + tp->tblWidth = tp->thdrWidth + (2*tp->tblRows+2)*tp->tcellWidth; + tp->tblHeight = (2*tp->tblCols+1)*tp->tcellHeight; + /* draw the table on the left margine */ + *xStructOffs += tp->tblWidth; + *xStructSize -= tp->tblWidth; + /* *xStructSize -= tp->tblWidth; */ + tp->xtblOffs = 0; + tp->ytblOffs = yoffs1; + } +} +/****************************************************************************/ +int DrawTheTable( HDC hDC, TBL_PARMS *tp, TBL_DRAW_PARMS *tdp, int x_offs, int y_offs ) +{ + int i, j, ret; + int dx = tp->tcellWidth/2; + int dy = tp->tcellHeight/2; + int x1, y1, x2, y2; + /* draw frame around the table */ + ret = Rectangle( hDC, tp->xtblOffs+dx+x_offs, tp->ytblOffs+dy+y_offs, tp->xtblOffs+tp->tblWidth-dx+x_offs, tp->ytblOffs+tp->tblHeight-dy+y_offs); + /* draw lines between labeled rows or columns */ + for ( i = 1; i < tp->tblCols; i ++ ) { + if ( tdp->nOrientation ) { + /* parallel to vertical columns */ + x1 = x2 = tp->xtblOffs+dx + 2 * i * tp->tcellWidth; + y1 = tp->ytblOffs+dy; + y2 = tp->ytblOffs+tp->tblHeight-dy; + } else { + /* parallel to horizontal rows */ + x1 = tp->xtblOffs+dx; + x2 = tp->xtblOffs+tp->tblWidth-dx; + y1 = y2 = tp->ytblOffs+dy + 2 * i * tp->tcellHeight; + } + DrawLine( hDC, x1+x_offs, y1+y_offs, x2+x_offs, y2+y_offs ); + } + /* draw lines between requested/Shown/Found types */ + for ( i = 0; i < tp->tblRows; i ++ ) { + if ( tdp->nOrientation ) { + /* perpendicular to vertical columns */ + x1 = tp->xtblOffs+dx; + x2 = tp->xtblOffs+tp->tblWidth-dx; + y1 = y2 = tp->ytblOffs + tp->thdrHeight + tp->tcellHeight + 2 * i * tp->tcellHeight + dy; + } else { + /* perpendicular to horizontal rows */ + x1 = x2 = tp->xtblOffs + tp->thdrWidth + tp->tcellWidth + 2 * i * tp->tcellWidth + dx; + y1 = tp->ytblOffs+dy; + y2 = tp->ytblOffs+tp->tblHeight-dy; + } + DrawLine( hDC, x1+x_offs, y1+y_offs, x2+x_offs, y2+y_offs ); + } + /* draw the text */ + for ( i = 0; i < tp->tblCols; i ++ ) { + if ( tdp->nOrientation ) { + /* vertical column */ + x1 = tp->xtblOffs + (2 * i + 1) * tp->tcellWidth; + y1 = tp->ytblOffs + tp->tcellHeight; + } else { + /* horizontal row */ + x1 = tp->xtblOffs + tp->tcellWidth; + y1 = tp->ytblOffs + tp->tcellHeight + 2 * i * tp->tcellHeight; + } + (tdp->nOrientation? TextOutVert + : TextOutHoriz)( hDC, x1+x_offs, y1+y_offs, tdp->ReqShownFoundTxt[i], (int) strlen(tdp->ReqShownFoundTxt[i]), tp->tcellWidth ); + + for ( j = 0; j < tp->tblRows; j ++ ) { + if ( tdp->ReqShownFound[i][j] >= ' ' ) { + if ( tdp->nOrientation ) { + /* vertical column */ + y1 = tp->ytblOffs + tp->thdrHeight + (2*j + 2) * tp->tcellHeight; + } else { + /* horizontal row */ + x1 = tp->xtblOffs + tp->thdrWidth + (2*j + 2) * tp->tcellWidth; + } + (tdp->nOrientation? TextOutVert:TextOutHoriz)( hDC, x1+x_offs, y1+y_offs, &tdp->ReqShownFound[i][j], 1, tp->tcellWidth ); + } + } + } + + + return 0; +} +/****************************************************************************/ +void GetStructSizes( HDC hDC, inf_ATOM *inf_at, inp_ATOM *at0, inp_ATOM *at1, int num_at, int *xoffs1, int *xoffs2, INT_DRAW_PARMS *idp) +{ + int i, j, k, num_bonds; + double xmin, xmax, ymin, ymax; + double x2, x, y2, y, dist; + char *str; + int len, cur_len, half_char_width; + int Left_shift, Right_shift, Other_shift; + char cLeftChar, cRightChar; + int max_left_label_width_pix; + int max_right_label_width_pix; + + if ( idp ) { + if ( !inf_at ) { + idp->max_left_label_width_pix = *xoffs1; + idp->max_right_label_width_pix = *xoffs2; + } else { + idp->max_left_label_width_pix = idp->max_right_label_width_pix = 0; + } + } else { + if ( !inf_at ) { + max_left_label_width_pix = *xoffs1; + max_right_label_width_pix = *xoffs2; + } else { + max_left_label_width_pix = max_right_label_width_pix = 0; + } + } + + xmin=xmax=at0[0].x; + ymin=ymax=at0[0].y; + + for ( num_bonds = 0, i=0; i < num_at; i ++ ) { + + x = at0[i].x; + y = at0[i].y; + Left_shift = Right_shift = Other_shift = 0; + + for ( j = 0; j < at0[i].valence; j ++ ) { + k = at0[i].neighbor[j]; + x2 = at0[k].x; + y2 = at0[k].y; + dist = sqrt( (x-x2)*(x-x2)+(y-y2)*(y-y2) ); + if ( x < x2 - 0.2*dist ) + Left_shift ++; + else + if ( x > x2 + 0.1*dist ) + Right_shift ++; + else + Other_shift ++; + } + + if ( inf_at ) { + len = 0; + str = inf_at[i].at_string; + do { + if ( cur_len=strcspn(str, "^") ) { + if ( !len ) { + cLeftChar = str[0]; + } + cRightChar = str[cur_len-1]; + len += GetSubstringWidth( hDC, cur_len, str); + } + str += cur_len+1; + } while ( str[0] ); + inf_at[i].DrawingLabelLength = len; + if ( Left_shift && !Right_shift ) { + /* Atom label should be to the left from the vertex */ + half_char_width = len? GetOneCharInStringWidth( hDC, &cRightChar )/2:0; + inf_at[i].DrawingLabelLeftShift = len - half_char_width; + if ( idp ) { + idp->max_left_label_width_pix = inchi_max( idp->max_left_label_width_pix, inf_at[i].DrawingLabelLeftShift); + idp->max_right_label_width_pix = inchi_max(idp->max_right_label_width_pix, half_char_width); + } else { + max_left_label_width_pix = inchi_max(max_left_label_width_pix, inf_at[i].DrawingLabelLeftShift); + max_right_label_width_pix = inchi_max(max_right_label_width_pix, half_char_width); + } + /* convert NH2 to H2N, etc. */ + len = 0; + str = inf_at[i].at_string; + len = MoveHydrogenAtomToTheLeft( str, len, 'T' ); + len = MoveHydrogenAtomToTheLeft( str, len, 'D' ); + len = MoveHydrogenAtomToTheLeft( str, len, 'H' ); + } else { + /* Atom label should be to the right from the vertex */ + half_char_width = len? GetOneCharInStringWidth( hDC, &cLeftChar )/2:0; + inf_at[i].DrawingLabelLeftShift = half_char_width; + if ( idp ) { + idp->max_left_label_width_pix = inchi_max( idp->max_left_label_width_pix, half_char_width); + idp->max_right_label_width_pix = inchi_max( idp->max_right_label_width_pix, + inf_at[i].DrawingLabelLength + - inf_at[i].DrawingLabelLeftShift); + } else { + max_left_label_width_pix = inchi_max( max_left_label_width_pix, half_char_width); + max_right_label_width_pix = inchi_max( max_right_label_width_pix, + inf_at[i].DrawingLabelLength + - inf_at[i].DrawingLabelLeftShift); + } + } + } else { + at1[i].bDrawingLabelLeftShift = ( Left_shift && !Right_shift ); + } + + xmin = inchi_min( xmin, x ); + xmax = inchi_max( xmax, x ); + ymin = inchi_min( ymin, y ); + ymax = inchi_max( ymax, y ); + } + if ( idp ) { + idp->xmin = xmin; + idp->xmax = xmax; + idp->ymin = ymin; + idp->ymax = ymax; + if ( inf_at ) { + *xoffs1 = idp->max_left_label_width_pix; + *xoffs2 = idp->max_right_label_width_pix; + } + } else + if ( inf_at ) { + *xoffs1 = max_left_label_width_pix; + *xoffs2 = max_right_label_width_pix; + } +} +/****************************************************************************/ +void ResizeAtomForDrawing( inf_ATOM *inf_at, inp_ATOM *at0, inp_ATOM *at1, int num_at, + INT_DRAW_PARMS *idp, int width, int height, int nFontWidth, int *xoffs1, int *xoffs2, + int *draw_width, int *draw_height, int *xdraw_offs, int *ydraw_offs ) +{ + int i; + double xmin = idp->xmin; + double xmax = idp->xmax; + double ymin = idp->ymin; + double ymax = idp->ymax; + double dx = 0.0, dy = 0.0, new_dx; + double coeff=0.0, xShift=0.0, yShift=0.0; + double coeffx = 0.0, coeffy = 0.0, new_coeffx; + + + if ( xmax > xmin || ymax > ymin ) { + + dx = xmax-xmin; + dy = ymax-ymin; + + if ( width > 0 && height > 0 ) { + coeffx = dx > 0.0? (double)width/dx : 0.0; + coeffy = dy > 0.0? (double)height/dy : 0.0; + + if ( coeffx > 0.0 && coeffy > 0.0 ) + coeff = inchi_min( coeffx, coeffy ); + else + coeff = inchi_max( coeffx, coeffy ); + } + if ( coeffx == 0.0 ) { + xShift = width/2.0; + } + if ( coeffy == 0.0 ) { + yShift = height/2.0; + } + } else { + coeff = 0.0; + xShift = width/2.0; + yShift = height/2.0; + } + + + /* set screen coordinates for drawing */ + for ( i = 0; i < num_at; i ++ ) { + at1[i].y = (ymax - at0[i].y)*coeff+yShift; /* screen y axis is directed down */ + at1[i].x = (at0[i].x-xmin)*coeff+xShift; + } + /* horizontal screen coordinates rescale if x dimension determines struct. size */ + if ( coeffx > 0.0 && coeffy > 0.0 && inf_at ) { + double new_xmin = 1.0e32; + double new_xmax = -1.0e32; + double dx1, dx2; + int nPass=0; + int new_width = width + idp->max_left_label_width_pix + idp->max_right_label_width_pix; + for ( i = 0; i < num_at; i ++ ) { + dx1 = at1[i].x - inf_at[i].DrawingLabelLeftShift; + dx2 = dx1 + inf_at[i].DrawingLabelLength; + new_xmin = inchi_min( new_xmin, dx1 ); + new_xmax = inchi_max( new_xmax, dx2 ); + } + new_dx = new_xmax - new_xmin; + if ( coeffx > coeffy && new_dx < (double)new_width ) + goto done; + if ( new_dx < (double)new_width && new_dx > (double)(new_width-2*nFontWidth) ) + goto done; +again: + new_dx += nFontWidth; /* precaution */ + new_coeffx = coeffx + ((double)new_width-new_dx)/dx; + if ( coeffy > 0.0 ) + coeff = inchi_min(new_coeffx, coeffy); + else + coeff = new_coeffx; + + new_xmin = 1.0e32; + new_xmax = -1.0e32; + for ( i = 0; i < num_at; i ++ ) { + dy = at1[i].y = (ymax - at0[i].y)*coeff+yShift; /* screen y axis is directed down */ + dx = at1[i].x = (at0[i].x-xmin)*coeff+xShift; + dx1 = dx - inf_at[i].DrawingLabelLeftShift; + dx2 = dx1 + inf_at[i].DrawingLabelLength; + new_xmin = inchi_min( new_xmin, dx1 ); + new_xmax = inchi_max( new_xmax, dx2 ); + } + new_dx = new_xmax - new_xmin; + if ( new_dx > new_width && nPass++ < 3 ) { + coeffx = new_coeffx; + goto again; + } +done: + *xoffs1 = -nRound( new_xmin ); + *xoffs2 = nRound( new_xmax - (xmax-xmin)*coeff ); + *draw_width = nRound( (xmax-xmin)*coeff ); /* nRound( new_dx ); */ + *draw_height = nRound( (ymax-ymin)*coeff ); + } else { + *draw_width = nRound( (xmax-xmin)*coeff ); + *draw_height = nRound( (ymax-ymin)*coeff ); + } + *xdraw_offs = nRound(xShift); + *ydraw_offs = nRound(yShift); +} + +/******************************************************************************/ +void InpStructureMarkEquComponents( MY_WINDOW_DATA *pWinData, AT_NUMB nNewEquLabel, + inp_ATOM *at0, inp_ATOM *at1, inf_ATOM *inf_at, int num_at ) +{ + int bHighlight = 0; + AT_NUMB *nEquLabels = pWinData->nEquLabels; + /* highlight equivalent components */ + int i, neigh, j, ni, nj, nh; + if ( nNewEquLabel ) { + for ( i = 0; i < num_at; i ++ ) { + ni = (int)at0[i].orig_at_number-1; + if ( 0 <= ni && ni < num_at ) { + if ( nNewEquLabel == nEquLabels[ni] || + 1==at0[i].el_number && 1==at0[i].chem_bonds_valence && + 0<=(nh=(int)at0[at0[i].neighbor[0]].orig_at_number-1) && + nh < num_at && nNewEquLabel == nEquLabels[nh] ) { + inf_at[i].cHighlightTheAtom = 1; /* highlight the atom */ + bHighlight |= 1; + for ( j = 0; j < at0[i].valence; j ++ ) { + neigh = (int)at0[i].neighbor[j]; + if ( neigh < num_at && + 0 <= (nj = (int)at0[neigh].orig_at_number-1) && + nj < num_at && + ( + /* highlighted atom */ + ( nNewEquLabel == nEquLabels[nj] ) || + /* terminal H */ + ( 1==at0[neigh].el_number && 1 == at0[neigh].chem_bonds_valence) + ) + ) { + at0[i].bond_type[j] |= BOND_MARK_HIGHLIGHT; /* highlight the bond */ + at1[i].bond_type[j] |= BOND_MARK_HIGHLIGHT; /* highlight the bond */ + } + } + } else + if ( inf_at[i].cHighlightTheAtom ) { + inf_at[i].cHighlightTheAtom = 0; + for ( j = 0; j < at0[i].valence; j ++ ) { + at0[i].bond_type[j] &= ~BOND_MARK_HIGHLIGHT; /* remove highlight from the bond */ + at1[i].bond_type[j] &= ~BOND_MARK_HIGHLIGHT; /* remove highlight from the bond */ + } + } + } + } + } else { + for ( i = 0; i < num_at; i ++ ) { + ni = (int)at0[i].orig_at_number-1; + if ( 0 <= ni && ni < num_at ) { + if ( inf_at[i].cHighlightTheAtom ) { + inf_at[i].cHighlightTheAtom = 0; + for ( j = 0; j < at0[i].valence; j ++ ) { + at0[i].bond_type[j] &= ~BOND_MARK_HIGHLIGHT; /* remove highlight from the bond */ + at1[i].bond_type[j] &= ~BOND_MARK_HIGHLIGHT; /* remove highlight from the bond */ + } + } + } + } + } + if ( !bHighlight ) { + nNewEquLabel = 0; + } + pWinData->nCurEquLabel = nNewEquLabel; + pWinData->bHighlight = bHighlight; +} +/****************************************************************************/ +int CreateInputStructPicture( HDC hDC, MY_WINDOW_DATA *pWinData, RECT *rc, int bPrint, AT_NUMB nNewEquLabel ) +{ + int ErrCode = 1, Res, width=0, height=0, yoffs0=0, xoffs1=0, yoffs1=0, xoffs2, yoffs2; + int xDim, yDim, w, h, xs, ys; + + HDC hMemoryDC = NULL; + HBITMAP hBitmap = NULL, hOldBitmap=NULL; + HPEN Pen = 0, OldPen = 0; + LOGFONT MyLogFont; + HFONT Font = 0, OldFont = 0; + char *FaceName = FONT_NAME; + int bDrawTbl = 0; + int bStereoFlags = 0; + + int win_top = rc->top; + int win_left = rc->left; + + int win_width = rc->right - rc->left; + int win_height = rc->bottom - rc->top; + + int bm_top = rc->top; + int bm_left = rc->left; + + int bm_width = rc->right - rc->left; + int bm_height = rc->bottom - rc->top; + + TBL_PARMS tp; + + int num_at = 0; + int bOrigAtom = 0; + int nFontSize = 10; + inp_ATOM *at0 = NULL; + inp_ATOM *at1 = NULL; + inf_ATOM *inf_at = NULL; + INT_DRAW_PARMS *idp = NULL; + TBL_DRAW_PARMS *tdp = NULL; + INT_DRAW_PARMS idp_print; + + /* structure + headers rect: offsets, width, height */ + int xStructOffs=0, yStructOffs=0, xStructSize, yStructSize; + + int nFontHeight=0; + int nFontWidth=0; + int nPenWidth = 1; + COLORREF clrPen = CLR_BLUE; + int afont; + + /*bPrint = 1;*/ /* test */ + + if ( pWinData ) { + num_at = pWinData->num_at; + bOrigAtom = pWinData->bOrigAtom; + nFontSize = pWinData->nFontSize; + at0 = pWinData->at0; + at1 = pWinData->at1; + inf_at = pWinData->inf_at_data.at; + idp = &pWinData->idp; + tdp = &pWinData->tdp; + bStereoFlags = pWinData->inf_at_data.StereoFlags; + if ( bPrint ) { + idp = &idp_print; + memset( idp, 0, sizeof(idp[0]) ); + } + + if ( pWinData->nCurEquLabel != nNewEquLabel && + pWinData->nEquLabels && + nNewEquLabel <= pWinData->nNumEquSets && at0 && at1 && inf_at && num_at ) { + InpStructureMarkEquComponents( pWinData, nNewEquLabel, at0, at1, inf_at, num_at ); + } + } + + + xDim = xStructSize = win_width; + yDim = yStructSize = win_height; + if ( !bPrint ) { + /* create bitmap: drawing on a bitmap reduces screen flicker */ + if ( !(hMemoryDC = CreateCompatibleDC(hDC)) || + !(hBitmap = CreateCompatibleBitmap(hDC, xDim, yDim )) || + !(hOldBitmap = (HBITMAP) SelectObject(hMemoryDC, hBitmap)) || + !PatBlt( hMemoryDC, 0, 0, xDim, yDim, PATCOPY )) { + ErrCode = 0; + goto _end; + } + bm_top = 0; + bm_left = 0; + bm_height = win_height - win_top; + bm_width = win_width - win_left; + } else { + hMemoryDC = hDC; + } + + if ( pWinData ) { + /* create drawing tools: font */ + memset( &MyLogFont, 0, sizeof( LOGFONT ) ); + if ( nFontSize < 0 ) { + int iLogPixsY = GetDeviceCaps(hDC, LOGPIXELSY); + nFontSize = -MulDiv(iLogPixsY, -nFontSize, 72); + } + nPenWidth = bPrint? inchi_max(abs(nFontSize)/10,1):1; + clrPen = bPrint? CLR_BLACK : CLR_BLUE; + MyLogFont.lfHeight = nFontSize; + MyLogFont.lfWeight = FW_NORMAL; + /* MyLogFont.lfItalic = 1; */ /* test MyTextOutABC() */ + strncpy( MyLogFont.lfFaceName, FaceName, LF_FACESIZE ); + Font = CreateFontIndirect( &MyLogFont ); /* black */ + + /* create drawing tools: pen */ + Pen = CreatePen( PS_SOLID, nPenWidth, clrPen ); + + /* select drawing tools into the bitmap */ + OldFont = (HFONT)SelectObject( hMemoryDC, Font ); + OldPen = (HPEN) SelectObject( hMemoryDC, Pen ); + + /* find sizes */ + + nFontHeight = GetFontHeight( hMemoryDC ); + nFontWidth = GetFontAveWidth( hMemoryDC ); + afont = GetFontAscent( hMemoryDC ); + + /* offsets within the (0, 0, xStructSize, yStructSize) rectangle */ + xoffs1 = xoffs2 = 16*nFontWidth; /* define structure atom coordinate margins here */ + yoffs0 = (bPrint && pWinData->szTitle && pWinData->szTitle[0] )? (3*nFontHeight)/2 : 0; /* 1.5 or 0 lines at the top */ + yoffs1 = (5*nFontHeight)/2; /* 2.5 lines at the top */ + yoffs2 = (5*nFontHeight)/2; /* 2.5 lines at the bottom */ + + /***********************************************/ + /* Calculate structure size */ + /***********************************************/ + if ( idp->bInit ) { + /* structure sizes are known */ + xoffs1 = idp->max_left_label_width_pix; + xoffs2 = idp->max_right_label_width_pix; + } else { + GetStructSizes( hMemoryDC, inf_at, at0, at1, num_at, &xoffs1, &xoffs2, idp); + idp->bInit = 1; + } + + /***********************************************/ + /* Calculate requested/Shown/Found table sizes */ + /***********************************************/ + + memset( &tp, 0, sizeof(tp) ); +#ifdef TARGET_LIB_FOR_WINCHI + bDrawTbl = 0; +#else + bDrawTbl = tdp && tdp->bDrawTbl; + /*bDrawTbl = 0;*/ +#endif + if ( bDrawTbl ) { + double dx = idp->xmax - idp->xmin; + double dy = idp->ymax - idp->ymin; + int nOrientation_tmp = tdp->nOrientation; + if ( dx > 0.0 && dy > 0.0 ) { + int xStructOffs_tmp0=xStructOffs, yStructOffs_tmp0=yStructOffs; + int xStructSize_tmp0=xStructSize, yStructSize_tmp0=yStructSize; + int twidth0; + /* + int xStructOffs_tmp1=xStructOffs, yStructOffs_tmp1=yStructOffs; + int xStructSize_tmp1=xStructSize, yStructSize_tmp1=yStructSize; + int twidth1; + */ + tdp->nOrientation = 0; + CalcTblParms( hMemoryDC, &tp, tdp, + &xStructOffs_tmp0, &yStructOffs_tmp0, &xStructSize_tmp0, &yStructSize_tmp0, yoffs0+yoffs1); + twidth0 = tp.tblWidth; + xStructSize_tmp0 -= xoffs1 + xoffs2; + yStructSize_tmp0 -= yoffs1 + yoffs2; + /* + memset( &tp, 0, sizeof(tp) ); + tdp->nOrientation = 0; + CalcTblParms( hMemoryDC, &tp, tdp, + &xStructOffs_tmp1, &yStructOffs_tmp1, &xStructSize_tmp1, &yStructSize_tmp1, yoffs1); + twidth1 = tp.tblWidth; + xStructSize_tmp1 -= xoffs1 + xoffs2; + yStructSize_tmp1 -= yoffs1 + yoffs2; + */ + if ( xStructSize_tmp0 > 0 && yStructSize_tmp0 > 0 ) { + nOrientation_tmp = ( (double)yStructSize_tmp0/(double)xStructSize_tmp0 > dy / dx ); + } + } + tdp->nOrientation = nOrientation_tmp; + memset( &tp, 0, sizeof(tp) ); + CalcTblParms( hMemoryDC, &tp, tdp, + &xStructOffs, &yStructOffs, &xStructSize, &yStructSize, yoffs0+yoffs1); + } else { + xStructSize -= 2*nFontWidth; /* drawing area sizes */ + yStructSize -= nFontHeight; + xStructOffs += nFontWidth; /* drawing area offsets */ + yStructOffs += nFontHeight/2; + } + + width = xStructSize - xoffs1 - xoffs2; /* drawing structre area sizes */ + height = yStructSize - yoffs0 - yoffs1 - yoffs2; + ResizeAtomForDrawing( inf_at, at0, at1, num_at, idp, width, height, nFontWidth, &xoffs1, &xoffs2, &w, &h, &xs, &ys ); + + /* At this point xStructOffs = the left margin for the drawing */ + + if ( 2*xs+xoffs1+xoffs2+w < win_width - xStructOffs ) { /*compare 2*x-coordinate of the center */ + xStructOffs += (win_width - (xStructOffs+xoffs1+xoffs2+w))/2-xs; + } + } + + /* draw */ + if ( bPrint ) { + ; /*PatBlt( hMemoryDC, 0, 0, xDim+win_left, yDim+win_top, PATCOPY ); */ + } else { + PatBlt( hMemoryDC, 0, 0, xDim, yDim, PATCOPY ); + } + + if ( pWinData ) { + char str[128]=""; + + /* exact rectangle around the structure drawing */ + /*Rectangle( hMemoryDC, xStructOffs+xs-1, yStructOffs+ys+yoffs1-afont-1, xStructOffs+xs+xoffs1+xoffs2+w+1, yStructOffs+ys+yoffs1+nFontHeight+h+1); */ + Res = DrawTheInputStructure( at1, &pWinData->inf_at_data, num_at, hMemoryDC, + bm_left, /* text output offsets */ + bm_top+yoffs0, + bm_left + xoffs1+xStructOffs, /* structure offsets */ + bm_top + +yoffs0+yoffs1+yStructOffs, + xDim-xoffs1, /* structure width */ + yDim-yoffs0-yoffs1, /* structure height */ + ( width >= 0 && height >= 0 ), bOrigAtom, clrPen, nPenWidth ); + if( Res == -1 ){ + ErrCode = 0; + goto _end; + } + if ( bDrawTbl || bStereoFlags || pWinData->inf_at_data.szRemovedProtons[0] ) { + /* + if ( bDrawTbl ) { + char *str = "Abbreviations: Tautomeric, Isotopic, Stereo"; + DrawTheTable( hMemoryDC, &tp, tdp, bPrint?win_left:0, bPrint?win_top:0 ); + TextOut( hMemoryDC, nFontWidth+(bPrint?win_left:0), win_height - nFontHeight+(bPrint?win_top:0), str, strlen(str) ); + } + */ + if ( bStereoFlags ) { + switch ( bStereoFlags & INF_STEREO_ABS_REL_RAC ) { + case INF_STEREO_ABS: + strcat( str, "Absolute" ); + break; + case INF_STEREO_REL: + strcat( str, "Relative" ); + break; + case INF_STEREO_RAC: + strcat( str, "Racemic mixture" ); + break; + } + if ( str[0] ) { + strcat( str, " stereo" ); + switch( bStereoFlags & INF_STEREO_NORM_INV ) { + case INF_STEREO_NORM: + strcat( str, " (normal)" ); + break; + case INF_STEREO_INV: + strcat( str, " (inverted)" ); + break; + case INF_STEREO_NORM_INV: + strcat( str, " (normal and inverted)" ); + break; + } + } + } + if ( bDrawTbl ) { + int bTaut=0, bIso=0, bSter=0; + if ( str[0] ) { + strcat( str, "; "); + } + bTaut = (NULL != memchr(tdp->ReqShownFound[ilSHOWN], 'T', TDP_NUM_PAR)); + bIso = (NULL != memchr(tdp->ReqShownFound[ilSHOWN], 'I', TDP_NUM_PAR)); + bSter = (NULL != memchr(tdp->ReqShownFound[ilSHOWN], 'S', TDP_NUM_PAR)) || + (NULL != memchr(tdp->ReqShownFound[ilSHOWN], 's', TDP_NUM_PAR)); + strcat( str, "Abbreviation" ); + if ( bTaut+bIso+bSter > 1 ) { + strcat( str, "s:"); + } else { + strcat( str, ":" ); + } + if ( bTaut ) strcat( str, " Mobile H" ); + if ( bIso ) strcat( str, " Isotopic" ); + if ( bSter ) strcat( str, " Stereo" ); + DrawTheTable( hMemoryDC, &tp, tdp, bm_left, bm_top); + } + if ( pWinData->inf_at_data.szRemovedProtons[0] ) { + if ( str[0] ) strcat( str, "; "); + strcat( str, pWinData->inf_at_data.szRemovedProtons ); + } + /*TextOut( hMemoryDC, nFontWidth+bm_left, bm_height - nFontHeight +bm_top, str, strlen(str) );*/ + /*DrawColorString( hMemoryDC, str, nFontWidth+bm_left, bm_height - nFontHeight +bm_top, 0 );*/ + } + if ( pWinData->bHighlight ) { + /* draw highlighted (identical) components description */ + char *p1 = " Highlighted "; + char *p2 = " components are identical"; + int x = bm_left + nFontWidth; + int y = bm_top + bm_height - nFontHeight; + COLORREF clrBk; + UINT uPrevTextAlign; + POINT pt; + /* save current position */ + GetCurrentPositionEx( hMemoryDC, &pt ); + /* move to the starting point */ + MoveToEx( hMemoryDC, x, y, NULL ); + /* set text aligh that do not require coordinates in TextOut() */ + uPrevTextAlign = SetTextAlign( hMemoryDC, TA_UPDATECP); + /* set highlighted background color */ + clrBk = SetBkColor( hMemoryDC, CLR_LTPURPLE ); + /* output the 1st word */ + TextOut( hMemoryDC, 0, 0, p1, (int) strlen(p1) ); + /* restore text background */ + SetBkColor( hMemoryDC, clrBk ); + /* output the rest of the text as normal text */ + TextOut( hMemoryDC, 0, 0, p2, (int) strlen(p2) ); + + if ( str[0] ) { + POINT pt2; + TextOut( hMemoryDC, 0, 0, ";", 1 ); + GetCurrentPositionEx( hMemoryDC, &pt2 ); + DrawColorString( hMemoryDC, str, pt2.x+2*nFontWidth, pt2.y, 0 ); + } + /* restore text align */ + SetTextAlign( hMemoryDC, uPrevTextAlign); + /* restore current position */ + MoveToEx( hMemoryDC, pt.x, pt.y, NULL ); + } else + if ( str[0] ) { + DrawColorString( hMemoryDC, str, nFontWidth+bm_left, bm_height - nFontHeight +bm_top, 0 ); + } + if ( yoffs0 > 0 && bPrint && pWinData->szTitle && pWinData->szTitle[0] ) { + /* print window title */ + char *p1 = pWinData->szTitle; + int x = bm_left + 3*nFontWidth; + int y = bm_top + yoffs0 - (3*nFontHeight)/2; + UINT uPrevTextAlign; + POINT pt; + /* save current position */ + GetCurrentPositionEx( hMemoryDC, &pt ); + /* move to the starting point */ + MoveToEx( hMemoryDC, x, y, NULL ); + /* set text aligh that do not require coordinates in TextOut() */ + uPrevTextAlign = SetTextAlign( hMemoryDC, TA_UPDATECP); + /* output the text */ + TextOut( hMemoryDC, 0, 0, p1, (int) strlen(p1) ); + /* restore text align */ + SetTextAlign( hMemoryDC, uPrevTextAlign); + /* restore current position */ + MoveToEx( hMemoryDC, pt.x, pt.y, NULL ); + } + } + + if ( !bPrint ) { + /* copy bitmap onto the window */ + ErrCode = BitBlt( + hDC, /* handle to the destination device context */ + win_left, /* x-coordinate of destination rectangle's upper-left corner */ + win_top, /* y-coordinate of destination rectangle's upper-left corner */ + xDim, /* width of destination rectangle */ + yDim, /* height of destination rectangle */ + hMemoryDC, /* handle to source device context */ + bm_left, /* x-coordinate of source rectangle's upper-left corner */ + bm_top, /* y-coordinate of source rectangle's upper-left corner */ + SRCCOPY /* raster operation code */ + ); + } + + if ( pWinData ) { + + /* remove drawing tools */ + if( Pen ){ + SelectObject( hMemoryDC, OldPen ); + DeleteObject( Pen ); + } + if( Font ){ + SelectObject( hMemoryDC, OldFont ); + DeleteObject( Font ); + } + } +_end: + if ( !bPrint ) { + if( hBitmap ) { + if ( hOldBitmap ) + SelectObject(hMemoryDC, hOldBitmap); + DeleteObject( hBitmap ); + } + if( hMemoryDC && hMemoryDC != hDC ) + DeleteDC(hMemoryDC); + } + return ErrCode; +} + +/********************************************************************* + + FUNCTION: WndProcDisplayCanonStructure(HWND, unsigned, WORD, LONG) + + PURPOSE: Processes messages for the main window. + + MESSAGES: + + WM_COMMAND - process the application menu + WM_PAINT - Paint the main window + WM_DESTROY - post a quit message and return + WM_DISPLAYCHANGE - message sent to Plug & Play systems when the display changes + WM_RBUTTONDOWN - Right mouse click -- put up context menu here if appropriate + WM_NCRBUTTONUP - User has clicked the right button on the application's system menu + +********************************************************************/ +LRESULT CALLBACK WndProcDisplayInputStructure(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + PAINTSTRUCT ps; + HDC hdc; + MY_WINDOW_DATA *pWinData; + RECT rc; + +#define IS_WIN95 0 + + switch (message) { + + case WM_COMMAND: + wmId = LOWORD(wParam); /* Remember, these are... */ + wmEvent = HIWORD(wParam); /* ...different for Win32! */ + break; + + case WM_CLOSE: + pWinData = (MY_WINDOW_DATA *) GETWINDLONG; + pWinData->bEsc = 1; + goto close_window; + + case WM_RBUTTONUP: /* RightClick in the window client area */ + case WM_LBUTTONUP: /* LeftClick in the window client area */ + /* stop the timer */ + pWinData = (MY_WINDOW_DATA *) GETWINDLONG; + if ( pWinData->nTimerId ) { + KillTimer( hWnd, pWinData->nTimerId ); + pWinData->nTimerId = 0; + pWinData->bUserIntervened = 1; + } + /*InvalidateRect( hWnd, NULL, 0 ); */ + break; + + case WM_CHAR: + + pWinData = (MY_WINDOW_DATA *) GETWINDLONG; + if ( pWinData->nTimerId ) { + KillTimer( hWnd, pWinData->nTimerId ); + pWinData->nTimerId = 0; + pWinData->bUserIntervened = 1; + } + if ( wParam == '\r' || wParam == 27 ) { + pWinData->bEsc = (wParam == 27); + goto close_window; + } + + break; + + case WM_ERASEBKGND: + return TRUE; /* to prevent flicker do not let Windows erase background */ + + case WM_SIZE: + case WM_MOVE: + pWinData = (MY_WINDOW_DATA *) GETWINDLONG; + if ( pWinData->nTimerId ) { + KillTimer( hWnd, pWinData->nTimerId ); + pWinData->nTimerId = 0; + pWinData->bUserIntervened = 1; + } + break; + + case WM_DISPLAYCHANGE: /* Only comes through on plug'n'play systems */ + { + SIZE szScreen; + BOOL fChanged = (BOOL)wParam; + + szScreen.cx = LOWORD(lParam); + szScreen.cy = HIWORD(lParam); + + if (fChanged) { + /* The display 'has' changed. szScreen reflects the */ + /* new size. */ + ; /*MessageBox (GetFocus(), "Display Changed", szWindowClassName, 0); */ + } else { + /* The display 'is' changing. szScreen reflects the */ + /* original size. */ + MessageBeep(0); + } + } + break; + + case WM_TIMER: + pWinData = (MY_WINDOW_DATA *) GETWINDLONG; + if ( wParam == pWinData->nTimerId ) { + KillTimer( hWnd, pWinData->nTimerId ); + pWinData->nTimerId = 0; + goto close_window; + } + break; +/* + case WM_SHOWWINDOW: + break; +*/ + case WM_PAINT: + pWinData = (MY_WINDOW_DATA *) GETWINDLONG; + GetClientRect( hWnd, &rc ); + hdc = BeginPaint (hWnd, &ps); + /* the drawing code is here */ + /* + rc.top = 30; + rc.left= 50; + */ + CreateInputStructPicture( hdc, pWinData, &rc, 0, pWinData->nNewEquLabel ); + EndPaint (hWnd, &ps); + /* start the timer if requested */ + if ( !pWinData->nTimerId && !pWinData->bUserIntervened && pWinData->ulDisplTime ) { + pWinData->nTimerId = SetTimer( + hWnd, /* handle to window */ + MY_TIMER_ID, /* timer identifier */ + pWinData->ulDisplTime, /* time-out value */ + NULL /* ptr to the timer procedure */ + ); + } + break; + + case WM_DESTROY: + /* Tell WinHelp we don't need it any more... */ + /* WinHelp (hWnd, APPNAME".HLP", HELP_QUIT,(DWORD)0); */ + /*PostQuitMessage(0); */ + return (DefWindowProc(hWnd, message, wParam, lParam)); + break; + + default: + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + goto exit_function; + +close_window: + + pWinData = (MY_WINDOW_DATA *) GETWINDLONG; + GetWindowRect( hWnd, &pWinData->rc ); + ReallySetForegroundWindow(GetConsoleHwnd()); + DestroyWindow( hWnd ); + + +exit_function: + + return (0); +} +/**********************************************************************************************/ +void FreeWinData( MY_WINDOW_DATA* pWinData ) +{ + if ( pWinData ) { + if ( pWinData->at0 ) { + inchi_free( pWinData->at0 ); + pWinData->at0 = NULL; + } + if ( pWinData->at1 ) { + inchi_free( pWinData->at1 ); + pWinData->at1 = NULL; + } + FreeInfoAtomData( &pWinData->inf_at_data ); + + if ( pWinData->nEquLabels ) { + inchi_free( pWinData->nEquLabels ); + pWinData->nEquLabels = NULL; + pWinData->nNumEquSets = 0; + } + + if ( pWinData->szTitle ) { + inchi_free(pWinData->szTitle); + pWinData->szTitle = NULL; + } + } +} +#ifndef TARGET_LIB_FOR_WINCHI +/**********************************************************************************************/ +int DisplayInputStructure( char *szOutputString, inp_ATOM *at, INF_ATOM_DATA *inf_at_data, int num_at, DRAW_PARMS *dp ) +{ +#define IS_WIN95 0 + HWND hWnd = NULL; + WNDCLASS wc; + HINSTANCE hInstance = 0; + RECT rc, rc2, rc3; + MSG msg; + MY_WINDOW_DATA WinData; + int ret, ret2, ret3, bRectVisible; + HDC hDesktopDC; + inf_ATOM *inf_at = inf_at_data? inf_at_data->at : NULL; + /*WINDOWPLACEMENT wndpl = {sizeof(WINDOWPLACEMENT),}; */ /*set wndpl.length */ + /* get console application window handle */ + HWND hConsoleWnd = GetConsoleHwnd(); + HWND hDesktopWnd = GetDesktopWindow(); + int bSetForeground = (hConsoleWnd == GetForegroundWindow()); + + /*printf("hConsoleWnd = %ld, hDesktopWnd = %ld\n", (long)hConsoleWnd, (long)hDesktopWnd ); */ + + if ( !hConsoleWnd || !hDesktopWnd ) + return (FALSE); /* failed */ + if ( !IsWindowVisible(hConsoleWnd) ) + return (FALSE); /* failed */ + + + /* we will create graphics window of the same size and position as our console window */ + /* to do that we need to get console app window size and position. */ + ret = GetWindowRect( hConsoleWnd, &rc ); + /*printf( "ConsoleWnd: ret=%d, rc=%ld %ld %ld %ld\n", ret, rc.left, rc.top, rc.right, rc.bottom); */ + /* full screen: "ConsoleWnd: ret=1, rc=-32000 -32000 -31840 -31976" */ + + ret2 = GetWindowRect( hDesktopWnd, &rc2 ); + /*printf( "DesktopWnd: ret=%d, rc2=%ld %ld %ld %ld\n", ret, rc2.left, rc2.top, rc2.right, rc2.bottom); */ + /* full screen: "DesktopWnd: ret=1, rc2=0 0 1280 1024" */ + + if ( !(hDesktopDC = GetWindowDC(hDesktopWnd) ) ) + return (FALSE); + bRectVisible = RectVisible( hDesktopDC, &rc ); + /*printf( "Console rect visible=%d\n", bRectVisible); */ + /* full screen: "Console rect visible=1" */ + + /*ret3 = GetClipBox(hDesktopDC, &rc3); */ + /*printf( "DesktopClip: ret=%d, rc3=%ld %ld %ld %ld\n", ret3, rc3.left, rc3.top, rc3.right, rc3.bottom); */ + /* full screen: "DesktopClip: ret=3, rc3=0 0 0 0", 3=COMPLEXREGION */ + + ret3 = ReleaseDC( hDesktopWnd, hDesktopDC ); + if ( !bRectVisible ) + return (FALSE); /* usually happens in MS-DOS full-screen mode, but the API call may fail and return TRUE */ + + if ( !IntersectRect(&rc3, &rc2, &rc) ) { + /*printf( "Console rect invisible\n", bRectVisible); */ + /* full screen: "Console rect invisible" */ + return (FALSE); /* usually happens in MS-DOS full-screen mode */ + } + + hInstance = GetModuleHandle (NULL); /* or =(HINSTANCE)GetWindowLong(hConsoleWnd, GWL_HINSTANCE); */ + if ( !dp->pdp->rcPict[2] || !dp->pdp->rcPict[3] ) { + dp->pdp->rcPict[0] = rc.left; + dp->pdp->rcPict[1] = rc.top; + dp->pdp->rcPict[2] = rc.right-rc.left; + dp->pdp->rcPict[3] = rc.bottom-rc.top; + } + + /* Fill in window class structure with parameters that describe */ + /* the main window. */ + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = (WNDPROC)WndProcDisplayInputStructure; /* window procedure */ + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon (NULL, IDI_APPLICATION); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); /*(COLOR_WINDOW+1); */ + wc.lpszMenuName = NULL; + wc.lpszClassName = szWindowClassName; + /* Register the window class and return success/failure code. */ + if ( !RegisterClass(&wc) ) + return FALSE; + + hWnd = CreateWindow(szWindowClassName, szOutputString, WS_OVERLAPPEDWINDOW, + dp->pdp->rcPict[0], dp->pdp->rcPict[1], dp->pdp->rcPict[2], dp->pdp->rcPict[3], + /*rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, */ + hConsoleWnd, NULL, hInstance, NULL); + + if (!hWnd) { + /* avoid nasty messages about exception 0x5 in Windows dlls. */ + UnregisterClass(szWindowClassName, /* address of class name string */ + hInstance /* handle of application instance */ + ); + return (FALSE); + } + + /* provide window procedure with the pointer to the chemical structure */ + memset( &WinData, 0, sizeof(WinData) ); + WinData.at0 =(inp_ATOM *)inchi_calloc(num_at+1, sizeof(WinData.at0[0])); + WinData.at1 =(inp_ATOM *)inchi_calloc(num_at+1, sizeof(WinData.at1[0])); + + if ( dp->nEquLabels && dp->nNumEquSets ) { + WinData.nEquLabels = (AT_NUMB *)inchi_calloc( num_at+1, sizeof(WinData.nEquLabels[0])); + WinData.nNumEquSets = dp->nNumEquSets; + WinData.nCurEquLabel = 0; + WinData.nNewEquLabel = dp->nCurEquLabel; + } + + WinData.nFontSize = dp->sdp.nFontSize; + WinData.szTitle = NULL; + /*WinData.szTitle = inchi__strdup(szOutputString);*/ /* for testing INCHI_LIB printing */ + if ( inf_at ) { + DuplicateInfoAtomData( &WinData.inf_at_data, inf_at_data); + } + if ( !WinData.at0 || !WinData.at1 || inf_at && !WinData.inf_at_data.at + || dp->nEquLabels && dp->nNumEquSets && !WinData.nEquLabels + ) { + FreeWinData( &WinData ); + } else { + memcpy( WinData.at0, at, sizeof(at[0])*num_at ); + if ( inf_at ) + memcpy( WinData.inf_at_data.at, inf_at, sizeof(inf_at[0])*num_at ); + if ( WinData.nEquLabels ) { + memcpy( WinData.nEquLabels, dp->nEquLabels, num_at*sizeof(WinData.nEquLabels[0])); + } + + + memcpy( WinData.at1, WinData.at0, sizeof(at[0])*num_at ); + + WinData.num_at = num_at; + WinData.bOrigAtom = dp->sdp.bOrigAtom; + WinData.nTimerId = 0; + WinData.ulDisplTime = dp->sdp.ulDisplTime; + if ( dp->sdp.tdp ) { + WinData.tdp = *dp->sdp.tdp; + } + } + + SETWINDLONG; + + ShowWindow(hWnd, SW_SHOWNORMAL /*SW_SHOW*/); + UpdateWindow(hWnd); + + + /* Message Loop for Display Canon Struct Window */ + while( IsWindow(hWnd) ) { + if ( PeekMessage( + &msg, /* pointer to structure for message */ + hWnd, /* or NULL,*/ /* handle to window */ + 0, /* UINT wMsgFilterMin, */ /* first message */ + 0, /* UINT wMsgFilterMax, */ /* last message */ + PM_REMOVE /* UINT wRemoveMsg */ /* removal flags */ + ) ) { + TranslateMessage( &msg ); + DispatchMessage( &msg ); + } else { + if ( bSetForeground ) { + ReallySetForegroundWindow( hWnd ); + bSetForeground = 0; + } + SleepEx( 10L, TRUE ); /* provides a nice behavior of the app */ + } + } + /* deallocate memory */ + FreeWinData( &WinData ); + /* show console window on the top upon closing hWnd */ + ReallySetForegroundWindow( hConsoleWnd ); + /* avoid nasty messages about exception 0x5 in Windows dlls. */ + UnregisterClass(szWindowClassName, /* address of class name string */ + hInstance /* handle of application instance */ + ); + /* Save window size and position */ + if ( WinData.rc.bottom > WinData.rc.top && WinData.rc.right > WinData.rc.left ) { + dp->pdp->rcPict[0] = WinData.rc.left; + dp->pdp->rcPict[1] = WinData.rc.top; + dp->pdp->rcPict[2] = WinData.rc.right-WinData.rc.left; + dp->pdp->rcPict[3] = WinData.rc.bottom-WinData.rc.top; + } + if ( WinData.bEsc ) { + dp->rdp.bEsc = 1; + } + return WinData.bEsc? 27:1; +} +#endif +/****************************************************************************/ +void MySleep( unsigned long ms ) +{ + Sleep( ms ); +} + +#endif /* WIN32 */ +#else +int dummyDispStru_c; /* make translation unit non-empty for ANSI-C compatibility */ + +#endif /* COMPILE_ANSI_ONLY */ diff --git a/INCHI-1-SRC/INCHI/main/dispstru.h b/INCHI-1-SRC/INCHI_EXE/inchi-1/src/dispstru.h similarity index 60% rename from INCHI-1-SRC/INCHI/main/dispstru.h rename to INCHI-1-SRC/INCHI_EXE/inchi-1/src/dispstru.h index f42a2c9..7df7281 100644 --- a/INCHI-1-SRC/INCHI/main/dispstru.h +++ b/INCHI-1-SRC/INCHI_EXE/inchi-1/src/dispstru.h @@ -1,104 +1,122 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#ifndef __DISP_STRU_H__ -#define __DISP_STRU_H__ -#include - -/* local types */ -/*****************************************************/ -typedef struct tagInternalDrawParms { - double xmin, xmax, ymin, ymax; - int max_label_width_char; - int max_left_label_width_pix; - int max_right_label_width_pix; - int bInit; -} INT_DRAW_PARMS; /* internal: saved for redisplaying one structure */ - - -/***************************************************** - * Window data - */ -typedef struct tagWindowData { - - inp_ATOM *at0; /* [MAX_ATOMS]; */ - inp_ATOM *at1; /* [MAX_ATOMS]; */ - INF_ATOM_DATA inf_at_data; - /*inf_ATOM *inf_at;*/ /* [MAX_ATOMS]; */ - int num_at; - int bOrigAtom; - int bHighlight; - int bEsc; - int bUserIntervened; - UINT nTimerId; - - unsigned long ulDisplTime; - int nFontSize; - RECT rc; /* window rectangle size for saving */ - INT_DRAW_PARMS idp; /* structure geom. parameters for redrawing */ - TBL_DRAW_PARMS tdp; /* table data for displaying */ - char *szTitle; /* for TARGET_LIB_FOR_WINCHI printing */ - - /* component equivalence info */ - AT_NUMB *nEquLabels; /* num_at elements or NULL */ - AT_NUMB nNumEquSets; /* number of equivalent sets or 0 */ - AT_NUMB nCurEquLabel; /* in range 0..nNumEquSets; 0=>do not display equivalent components */ - - AT_NUMB nNewEquLabel; /* non-zero only if DISPLAY_EQU_COMPONENTS==1 */ - -} MY_WINDOW_DATA; - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -extern "C" { -#endif -#endif - -void FreeWinData( MY_WINDOW_DATA* pWinData ); -int CreateInputStructPicture( HDC hDC, MY_WINDOW_DATA *pWinData, RECT *rc, int bPrint, AT_NUMB nNewEquLabel ); - -#ifndef COMPILE_ALL_CPP -#ifdef __cplusplus -} -#endif -#endif - - -#endif +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#ifndef _DISP_STRU_H_ +#define _DISP_STRU_H_ + + + +#include + + + +/* Local types */ + +/****************************************************************************/ + +typedef struct tagInternalDrawParms +{ + double xmin, xmax, ymin, ymax; + int max_label_width_char; + int max_left_label_width_pix; + int max_right_label_width_pix; + int bInit; +} INT_DRAW_PARMS; /* internal: saved for redisplaying one structure */ + + +/****************************************************************************/ +/* Window data */ +/****************************************************************************/ + +typedef struct tagWindowData +{ + + inp_ATOM *at0; /* [MAX_ATOMS]; */ + inp_ATOM *at1; /* [MAX_ATOMS]; */ + INF_ATOM_DATA inf_at_data; + /*inf_ATOM *inf_at;*/ /* [MAX_ATOMS]; */ + int num_at; + int bOrigAtom; + int bHighlight; + int bEsc; + int bUserIntervened; + UINT nTimerId; + + unsigned long ulDisplTime; + int nFontSize; + RECT rc; /* window rectangle size for saving */ + INT_DRAW_PARMS idp; /* structure geom. parameters for redrawing */ + TBL_DRAW_PARMS tdp; /* table data for displaying */ + char *szTitle; /* for TARGET_LIB_FOR_WINCHI printing */ + + /* component equivalence info */ + AT_NUMB *nEquLabels; /* num_at elements or NULL */ + AT_NUMB nNumEquSets; /* number of equivalent sets or 0 */ + AT_NUMB nCurEquLabel; /* in range 0..nNumEquSets; 0=>do not display equivalent components */ + + AT_NUMB nNewEquLabel; /* non-zero only if DISPLAY_EQU_COMPONENTS==1 */ +} MY_WINDOW_DATA; + + + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +extern "C" { +#endif +#endif + + + +void FreeWinData( MY_WINDOW_DATA* pWinData ); + +int CreateInputStructPicture( HDC hDC, + MY_WINDOW_DATA *pWinData, + RECT *rc, + int bPrint, + AT_NUMB nNewEquLabel ); + + + +#ifndef COMPILE_ALL_CPP +#ifdef __cplusplus +} +#endif +#endif + +#endif /* _DISP_STRU_H_ */ diff --git a/INCHI-1-SRC/INCHI/main/ichimain.c b/INCHI-1-SRC/INCHI_EXE/inchi-1/src/ichimain.c similarity index 53% rename from INCHI-1-SRC/INCHI/main/ichimain.c rename to INCHI-1-SRC/INCHI_EXE/inchi-1/src/ichimain.c index f7a94ff..204afa0 100644 --- a/INCHI-1-SRC/INCHI/main/ichimain.c +++ b/INCHI-1-SRC/INCHI_EXE/inchi-1/src/ichimain.c @@ -1,875 +1,1073 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef COMPILE_ANSI_ONLY -#include -#ifndef TARGET_LIB_FOR_WINCHI -#include -#endif -#endif - -#include "mode.h" - -#include "ichitime.h" -#include "inpdef.h" -#include "ichi.h" -#include "strutil.h" -#include "util.h" -#include "ichidrp.h" -#include "ichierr.h" -#include "ichimain.h" -#include "ichicomp.h" -#include "ichi_io.h" -#ifdef TARGET_EXE_STANDALONE -#include "inchi_api.h" -#endif -#if ( ADD_CMLPP == 1 ) -#include "readcml.hpp" -#endif - - - -/* console-specific */ - -#ifdef COMPILE_ANSI_ONLY -/* Force strict ANSI C */ - -/*****************************************************************************/ -int user_quit( const char *msg, unsigned long ulMaxTime ) -{ - return 0; -} -/*****************************************************************************/ -void eat_keyboard_input( void ) -{ -} -#endif - -#ifndef COMPILE_ANSI_ONLY - -/* Use Windows additional features */ - - -/*****************************************************************************/ -int user_quit( const char *msg, unsigned long ulMaxTime ) -{ -#if defined(TARGET_LIB_FOR_WINCHI) - return 0; -#endif -#if ( !defined(TARGET_LIB_FOR_WINCHI) && defined(_WIN32) ) - int quit, enter, ret; - printf(msg); - if ( ulMaxTime ) - { - inchiTime ulEndTime; - InchiTimeGet( &ulEndTime ); - InchiTimeAddMsec( &ulEndTime, ulMaxTime ); - while ( !_kbhit() ) { - if ( bInchiTimeIsOver( &ulEndTime ) ) - { - printf("\n"); - return 0; - } - MySleep( 100 ); - } - } - while( 1 ) - { - quit = ( 'q' == (ret = _getch()) || 'Q'==ret || /*Esc*/ 27==ret ); - enter = ( '\r' == ret ); - if ( ret == 0xE0 ) - ret = _getch(); - else - _putch(ret); /* echo */ - if ( quit || enter ) - break; - printf( "\r" ); - printf( msg ); - } - _putch('\n'); - return quit; -#else - return 0; -#endif /* #if ( defined(_WIN32) && !defined(TARGET_LIB_FOR_WINCHI) ) */ -} - - -/*****************************************************************************/ -void eat_keyboard_input( void ) -{ -#ifndef TARGET_LIB_FOR_WINCHI - while ( _kbhit() ) { - if ( 0xE0 == _getch() ) - _getch(); - } -#endif -} - -#endif /* end of !COMPILE_ANSI_ONLY */ - - - - - -#ifndef TARGET_LIB_FOR_WINCHI - /* COVERS THE CODE FROM HERE TO THE END OF FILE */ - - - -/* Enable/disable internal tests */ -/*#define TEST_FPTRS*/ /* uncomment for INCHI_LIB testing only */ -#define REPEAT_ALL 0 /* set to 1 for mapping tests */ - - -/* Windows-console-mode specific */ -int bInterrupted = 0; -#if ( defined( _WIN32 ) && defined( _CONSOLE ) ) -#ifndef COMPILE_ANSI_ONLY -BOOL WINAPI MyHandlerRoutine( - DWORD dwCtrlType /* control signal type */ - ) { - if ( dwCtrlType == CTRL_C_EVENT || - dwCtrlType == CTRL_BREAK_EVENT || - dwCtrlType == CTRL_CLOSE_EVENT || - dwCtrlType == CTRL_LOGOFF_EVENT ) { - bInterrupted = 1; - return TRUE; - } - return FALSE; -} -int WasInterrupted(void) -{ -#ifdef _DEBUG - if ( bInterrupted ) - { - int stop=1; /* for debug only */ - } -#endif - return bInterrupted; -} -#if ( BUILD_WITH_AMI == 1 ) -#define CTRL_STOP_EVENT 101 -#endif -#endif /* ifndef COMPILE_ANSI_ONLY */ -#endif /* if( defined( _WIN32 ) && defined( _CONSOLE ) ) */ - - - - -/*****************************************************************************/ -/*****************************************************************************/ -int main( int argc, char *argv[ ] ) -{ -/*************************/ -#if ( BUILD_WITH_AMI == 1 ) -/*************************/ - /* if in AMI mode, main() starts here and */ - /* captures command line to pass to actual */ - /* worker - process_single_input() */ - /* [ which previously was known as main() ] */ -int i, p, ret=0, ami=0, AMIOutStd=0, AMILogStd=0, AMIPrbNone=0, nfn_ins=0; -char *fn_out, *fn_log, *fn_prb; -char **fn_ins=NULL, **targv=NULL; -char pNUL[] = "NUL"; - - /* Check if multiple inputs expected */ - for (i=1; i < argc; i++) - { - if (argv[i][0] == INCHI_OPTION_PREFX) - { - if ( !stricmp(argv[i]+1, "AMI") ) - { - ami = 1; - break; - } - } - } - - - /**********************/ - /* Single input file. */ - /**********************/ - if ( !ami ) - { - ret = process_single_input(argc,argv); - goto exit_ami_main; - } - - - /**********************************/ - /* Multiple input files expected. */ - /**********************************/ - - fn_ins = (char**) calloc( argc, sizeof(char *) ); - if ( !fn_ins ) - { - fprintf(stderr, "Not enough memory.\n"); - goto exit_ami_main; - } - - /* Check for other options and collect inputs. */ - for (i=1; i < argc; i++) - { - if (argv[i][0] == INCHI_OPTION_PREFX) - { - if ( !stricmp(argv[i]+1, "STDIO") ) - { - fprintf( stderr, "Options AMI and STDIO are not compatible.\n" ); - goto exit_ami_main; - } - else if ( !stricmp(argv[i]+1, "AMIOutStd") ) - { - AMIOutStd = 1; - } - else if ( !stricmp(argv[i]+1, "AMILogStd") ) - { - AMILogStd = 1; - } - else if ( !stricmp(argv[i]+1, "AMIPrbNone") ) - { - AMIPrbNone = 1; - } - } - else - { - fn_ins[nfn_ins] = argv[i]; - nfn_ins++; - } - } - - if ( !nfn_ins ) - { - fprintf(stderr, "At least one input file is expected in AMI mode.\n"); - goto exit_ami_main; - } - - targv = (char**) calloc( argc+3, sizeof(char *) ); - if ( !targv ) - { - fprintf(stderr, "Not enough memory.\n"); - goto exit_ami_main; - } - - for ( p=0; p < nfn_ins; p++ ) - { - int targc; - const char *fn_in = fn_ins[p]; - int inlen = strlen(fn_in); - fn_out=fn_log=fn_prb=NULL; - - targv[0] = argv[0]; - targv[1] = (char *) fn_in; - targc = 1; - - if ( AMIOutStd ) - { - targv[++targc] = pNUL; - } - else - { - /* make output name as input name plus ext. */ - fn_out = (char*) calloc( inlen+6, sizeof(char ) ); - if ( fn_out ) - { - strcpy( fn_out, fn_in ); - strcat( fn_out, ".txt" ); - } - targv[++targc] = fn_out; - } - - if ( AMILogStd ) - { - targv[++targc] = pNUL; - } - else - { - /* make log name as input name plus ext. */ - fn_log = (char*) calloc( inlen+6, sizeof(char ) ); - if ( fn_log ) - { - strcpy( fn_log, fn_in ); - strcat( fn_log, ".log" ); - } - targv[++targc] = fn_log; - } - - if ( AMIPrbNone ) - { - targv[++targc] = pNUL; - } - else - { - /* make problem file name as input file name plus ext. */ - fn_prb = (char*) calloc( inlen+6, sizeof(char ) ); - if ( fn_prb ) - { - strcpy( fn_prb, fn_in ); - strcat( fn_prb, ".prb" ); - } - targv[++targc] = fn_prb; - } - - - for (i=1; i < argc; i++) - { - if (argv[i][0] == INCHI_OPTION_PREFX) - { - /* avoid strnicmp/strncasecmp */ - if ( (strlen(argv[i])>3)&& - (toupper(argv[i][1])=='A')&&(toupper(argv[i][2])=='M')&&(toupper(argv[i][3])=='I') ) - continue; - targv[++targc] = argv[i]; - } - } - targv[++targc] = NULL; - - - ret = process_single_input(targc,targv); /* process_single_input() is a former main() */ - - if ( fn_out ) - free( fn_out ); - if ( fn_log ) - free( fn_log ); - if ( fn_prb ) - free( fn_prb ); - - -#if ( defined( _WIN32 ) && defined( _CONSOLE ) && !defined( COMPILE_ANSI_ONLY ) ) - if ( ret == CTRL_STOP_EVENT) - goto exit_ami_main; -#endif - - } - -exit_ami_main: - if ( targv ) - free( targv ); - if ( fn_ins ) - free( fn_ins ); - return 0; -} - - - - -/**************************************************/ -int process_single_input( int argc, char *argv[ ] ) -/**************************************************/ -{ - -/**************************************/ -#endif /* #if ( BUILD_WITH_AMI == 1 ) */ -/**************************************/ - /* if not in AMI mode, main() starts here */ - - - int bReleaseVersion = bRELEASE_VERSION; - const int nStrLen = INCHI_SEGM_BUFLEN; - int nRet = 0, nRet1; - int i, k; - long num_err, num_output, num_inp; - /* long rcPict[4] = {0,0,0,0}; */ - unsigned long ulDisplTime = 0; /* infinite, milliseconds */ - unsigned long ulTotalProcessingTime = 0; - - char szTitle[MAX_SDF_HEADER+MAX_SDF_VALUE+256]; - char szSdfDataValue[MAX_SDF_VALUE+1]; - char *pStr = NULL; - - INPUT_PARMS inp_parms; - INPUT_PARMS *ip = &inp_parms; - - STRUCT_DATA struct_data; - STRUCT_DATA *sd = &struct_data; - - ORIG_ATOM_DATA OrigAtData; /* 0=> disconnected, 1=> original */ - ORIG_ATOM_DATA *orig_inp_data = &OrigAtData; - ORIG_ATOM_DATA PrepAtData[2]; /* 0=> disconnected, 1=> original */ - ORIG_ATOM_DATA *prep_inp_data = PrepAtData; - - PINChI2 *pINChI[INCHI_NUM]; - PINChI_Aux2 *pINChI_Aux[INCHI_NUM]; - - INCHI_IOSTREAM outputstr, logstr, prbstr, instr; - INCHI_IOSTREAM *pout=&outputstr, *plog = &logstr, *pprb = &prbstr, *inp_file = &instr; -#ifdef TARGET_EXE_STANDALONE - char ik_string[256]; /*^^^ Resulting InChIKey string */ - int ik_ret=0; /*^^^ InChIKey-calc result code */ - int xhash1, xhash2; - char szXtra1[65], szXtra2[65]; - int inchi_ios_type = INCHI_IOSTREAM_STRING; -#else - int inchi_ios_type = INCHI_IOSTREAM_FILE; -#endif - - - -/* internal tests --- */ -#ifndef TEST_FPTRS - STRUCT_FPTRS *pStructPtrs = NULL; -#else - STRUCT_FPTRS struct_fptrs, *pStructPtrs =&struct_fptrs; /* INCHI_LIB debug only */ -#endif -#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) - int num_repeat = REPEAT_ALL; -#endif - -#if ( TRACE_MEMORY_LEAKS == 1 ) - _CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF); -/* for execution outside the VC++ debugger uncomment one of the following two */ -/*#define MY_REPORT_FILE _CRTDBG_FILE_STDERR */ -/*#define MY_REPORT_FILE _CRTDBG_FILE_STDOUT */ -#ifdef MY_REPORT_FILE - _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_WARN, MY_REPORT_FILE ); - _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_ERROR, MY_REPORT_FILE ); - _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); - _CrtSetReportFile( _CRT_ASSERT, MY_REPORT_FILE ); -#else - _CrtSetReportMode(_CRT_WARN | _CRT_ERROR, _CRTDBG_MODE_DEBUG); -#endif - /* turn on floating point exceptions */ - { - /* Get the default control word. */ - int cw = _controlfp( 0,0 ); - - /* Set the exception masks OFF, turn exceptions on. */ - /*cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_INEXACT|EM_ZERODIVIDE|EM_DENORMAL);*/ - cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE|EM_DENORMAL); - - /* Set the control word. */ - _controlfp( cw, MCW_EM ); - - } -#endif - -#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) -repeat: - inchi_ios_close(inp_file); - inchi_ios_close(pout); - inchi_ios_close(plog); - inchi_ios_close(pprb); - pStr = NULL; -#endif -/* --- internal tests */ - - - - sd->bUserQuit = 0; -#if ( defined( _WIN32 ) && defined( _CONSOLE ) && !defined( COMPILE_ANSI_ONLY ) ) - if ( SetConsoleCtrlHandler( MyHandlerRoutine, 1 ) ) - ConsoleQuit = WasInterrupted; -#endif - - num_inp = 0; - num_err = 0; - num_output = 0; - - inchi_ios_init(inp_file, INCHI_IOSTREAM_FILE, NULL); - inchi_ios_init(pout, inchi_ios_type, NULL); - inchi_ios_init(plog, inchi_ios_type, stdout); - inchi_ios_init(pprb, inchi_ios_type, NULL); - - - if ( argc == 1 || argc==2 && ( argv[1][0]==INCHI_OPTION_PREFX ) && - (!strcmp(argv[1]+1, "?") || !stricmp(argv[1]+1, "help") ) ) - { - HelpCommandLineParms(plog); - inchi_ios_flush(plog); - return 0; - } - - - /* original input structure */ - memset( orig_inp_data , 0, sizeof( *orig_inp_data ) ); - memset( prep_inp_data , 0, 2*sizeof( *prep_inp_data ) ); - memset( pINChI, 0, sizeof(pINChI ) ); - memset( pINChI_Aux, 0, sizeof(pINChI_Aux) ); - memset( szSdfDataValue , 0, sizeof( szSdfDataValue ) ); - - - plog->f = stderr; - if ( 0 > ReadCommandLineParms( argc, (const char **)argv, ip, szSdfDataValue, &ulDisplTime, bReleaseVersion, plog) ) - /* explicitly cast to (const char **) to avoid a warning about "incompatible pointer type":*/ - { - goto exit_function; - } - - - if ( !OpenFiles( &(inp_file->f), &(pout->f), &(plog->f), &(pprb->f), ip ) ) - { - goto exit_function; - } - - - if ( ip->bNoStructLabels ) - { - ip->pSdfLabel = NULL; - ip->pSdfValue = NULL; - } - else if ( ip->nInputType == INPUT_INCHI_XML || ip->nInputType == INPUT_INCHI_PLAIN || - ip->nInputType == INPUT_CMLFILE || ip->nInputType == INPUT_INCHI ) - { - /* the input may contain both the header and the label of the structure */ - if ( !ip->pSdfLabel ) - ip->pSdfLabel = ip->szSdfDataHeader; - if ( !ip->pSdfValue ) - ip->pSdfValue = szSdfDataValue; - } - - - inchi_ios_eprint( plog, "The command line used:\n\""); - for(k=0; knInputType == INPUT_INCHI ) { - memset( sd, 0, sizeof(*sd) ); - ReadWriteInChI( inp_file, pout, plog, ip, sd, NULL, NULL, NULL, 0, NULL); - inchi_ios_flush2(plog, stderr); - ulTotalProcessingTime = sd->ulStructTime; - num_inp = sd->fPtrStart; - num_err = sd->fPtrEnd; - goto exit_function; - } -#endif - - - ulTotalProcessingTime = 0; - if ( pStructPtrs ) - { - memset ( pStructPtrs, 0, sizeof(pStructPtrs[0]) ); - /* debug: set CML reading sequence - pStructPtrs->fptr = (INCHI_FPTR *)inchi_calloc(16, sizeof(INCHI_FPTR)); - for ( i = 0; i < 15; i ++ ) - pStructPtrs->fptr[i] = 15-i; - pStructPtrs->cur_fptr = 7; - pStructPtrs->len_fptr = 16; - pStructPtrs->max_fptr = 14; - */ - } - - - /**********************************************************************************************/ - /* Main cycle : read input structures and create their INChI */ - /**********************************************************************************************/ - while ( !sd->bUserQuit && !bInterrupted ) - { - - if ( ip->last_struct_number && num_inp >= ip->last_struct_number ) - { - nRet = _IS_EOF; /* simulate end of file */ - goto exit_function; - } - - /* read one structure from input and display optionally it */ - - nRet = GetOneStructure( sd, ip, szTitle, inp_file, plog, pout, pprb, - orig_inp_data, &num_inp, pStr, nStrLen, pStructPtrs ); - inchi_ios_flush2(plog, stderr); - - - if ( pStructPtrs ) - pStructPtrs->cur_fptr ++; - - if ( sd->bUserQuit ) - break; - - switch ( nRet ) - { - case _IS_FATAL: - num_err ++; - case _IS_EOF: - goto exit_function; - case _IS_ERROR: - num_err ++; - case _IS_SKIP: - continue; - } - - /* create INChI for each connected component of the structure and optionally display them */ - /* output INChI for the whole structure */ - - nRet1 = ProcessOneStructure( sd, ip, szTitle, pINChI, pINChI_Aux, - inp_file, plog, pout, pprb, - orig_inp_data, prep_inp_data, - num_inp, pStr, nStrLen, - 0 /* save_opt_bits */); - inchi_ios_flush2(plog, stderr); - - -#ifdef TARGET_EXE_STANDALONE - /* correctly treat tabbed output with InChIKey */ - if ( ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT ) - if ( ip->bCalcInChIHash != INCHIHASH_NONE ) - if (pout->s.pStr) - if (pout->s.nUsedLength>0) - if (pout->s.pStr[pout->s.nUsedLength-1]=='\n') - /* replace LF with TAB */ - pout->s.pStr[pout->s.nUsedLength-1] = '\t'; -#endif - - /* free INChI memory */ - FreeAllINChIArrays( pINChI, pINChI_Aux, sd->num_components ); - /* free structure data */ - FreeOrigAtData( orig_inp_data ); - FreeOrigAtData( prep_inp_data ); - FreeOrigAtData( prep_inp_data+1 ); - - ulTotalProcessingTime += sd->ulStructTime; - - nRet = inchi_max(nRet, nRet1); - switch ( nRet ) - { - case _IS_FATAL: - num_err ++; - goto exit_function; - case _IS_ERROR: - num_err ++; - continue; - } - - -#ifdef TARGET_EXE_STANDALONE - if ( ip->bCalcInChIHash != INCHIHASH_NONE ) - { - char *buf = NULL; - size_t slen = pout->s.nUsedLength; - - extract_inchi_substring(&buf, pout->s.pStr, slen); - - if (NULL==buf) - { - ik_ret = INCHIKEY_NOT_ENOUGH_MEMORY; - } - else - { - xhash1 = xhash2 = 0; - if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1 ) || - ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) - xhash1 = 1; - if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA2 ) || - ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) - xhash2 = 1; - ik_ret = GetINCHIKeyFromINCHI(buf, xhash1, xhash2, - ik_string, szXtra1, szXtra2); - inchi_free(buf); - } - - - - if (ik_ret==INCHIKEY_OK) - { - /* NB: correctly treat tabbed output with InChIKey & hash extensions */ - char csep = '\n'; -#ifdef TARGET_EXE_STANDALONE - if ( ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT ) - csep = '\t'; -#endif - inchi_ios_print(pout, "InChIKey=%-s",ik_string); - if ( xhash1 ) - inchi_ios_print(pout, "%cXHash1=%-s",csep,szXtra1); - if ( xhash2 ) - inchi_ios_print(pout, "%cXHash2=%-s",csep,szXtra2); - inchi_ios_print(pout, "\n"); - } - else - { - inchi_ios_print(plog, "Warning (Could not compute InChIKey: ", num_inp); - switch(ik_ret) - { - case INCHIKEY_UNKNOWN_ERROR: - inchi_ios_print(plog, "unresolved error)"); - break; - case INCHIKEY_EMPTY_INPUT: - inchi_ios_print(plog, "got an empty string)"); - break; - case INCHIKEY_INVALID_INCHI_PREFIX: - case INCHIKEY_INVALID_INCHI: - case INCHIKEY_INVALID_STD_INCHI: - inchi_ios_print(plog, "got non-InChI string)"); - break; - case INCHIKEY_NOT_ENOUGH_MEMORY: - inchi_ios_print(plog, "not enough memory to treat the string)"); - break; - default:inchi_ios_print(plog, "internal program error)"); - break; - } - inchi_ios_print(plog, " structure #%-lu.\n", num_inp); - if ( ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT ) - inchi_ios_print(pout, "\n"); - } - inchi_ios_flush(pout); - inchi_ios_flush2(plog, stderr); - } - - else - inchi_ios_flush(pout); - - -#endif - - /* --- debug only --- - if ( pStructPtrs->cur_fptr > 5 ) { - pStructPtrs->cur_fptr = 5; - } - */ - - - - } /* end of main cycle - while ( !sd->bUserQuit && !bInterrupted ) */ - - - -exit_function: - if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) && sd->bXmlStructStarted > 0 ) - { - if ( !OutputINChIXmlStructEndTag( pout, pStr, nStrLen, 1 ) ) - { - inchi_ios_eprint( plog, "Cannot create end xml tag for structure #%ld.%s%s%s%s Terminating.\n", num_inp, SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue) ); - inchi_ios_flush2(plog, stderr); - sd->bXmlStructStarted = -1; /* do not repeat same message */ - } - } - if ( (ip->bINChIOutputOptions & INCHI_OUT_XML) && ip->bXmlStarted ) - { - OutputINChIXmlRootEndTag( pout ); - inchi_ios_flush(pout); - ip->bXmlStarted = 0; - } - - /* avoid memory leaks in case of fatal error */ - if ( pStructPtrs && pStructPtrs->fptr ) { - inchi_free( pStructPtrs->fptr ); - } - - /* free INChI memory */ - FreeAllINChIArrays( pINChI, pINChI_Aux, sd->num_components ); - /* free structure data */ - FreeOrigAtData( orig_inp_data ); - FreeOrigAtData( prep_inp_data ); - FreeOrigAtData( prep_inp_data+1 ); -#if ( ADD_CMLPP == 1 ) - /* BILLY 8/6/04 */ - /* free CML memory */ - FreeCml (); - FreeCmlDoc( 1 ); -#endif - - /* close output(s) */ - inchi_ios_close(inp_file); - inchi_ios_close(pout); - inchi_ios_close(pprb); - { - int hours, minutes, seconds, mseconds; - SplitTime( ulTotalProcessingTime, &hours, &minutes, &seconds, &mseconds ); - inchi_ios_eprint( plog, "Finished processing %ld structure%s: %ld error%s, processing time %d:%02d:%02d.%02d\n", - num_inp, num_inp==1?"":"s", - num_err, num_err==1?"":"s", - hours, minutes, seconds,mseconds/10); - inchi_ios_flush2(plog, stderr); - } - inchi_ios_close(plog); - if ( pStr ) - inchi_free( pStr ); - - - /* frees */ - for ( i = 0; i < MAX_NUM_PATHS; i ++ ) - { - if ( ip->path[i] ) - { - free( (void*) ip->path[i] ); /* cast deliberately discards 'const' qualifier */ - ip->path[i] = NULL; - } - } - SetBitFree( ); - -/* internal tests --- */ -#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) - if ( num_repeat-- > 0 ) - goto repeat; -#endif -/* --- internal tests */ - -#if ( bRELEASE_VERSION != 1 && defined(_DEBUG) ) - if ( inp_file->f && inp_file->f != stdin ) - { - user_quit("Press Enter to exit ?", ulDisplTime); - } -#endif - -#if ( ( BUILD_WITH_AMI==1 ) && defined( _WIN32 ) && defined( _CONSOLE ) && !defined( COMPILE_ANSI_ONLY ) ) - if ( bInterrupted ) - return CTRL_STOP_EVENT; -#endif - return 0; -} - - - -/*****************************************************************/ -#endif /* ifndef TARGET_LIB_FOR_WINCHI */ -/*****************************************************************/ +/* + * International Chemical Identifier (InChI) + * Version 1 + * Software version 1.05 + * January 27, 2017 + * + * The InChI library and programs are free software developed under the + * auspices of the International Union of Pure and Applied Chemistry (IUPAC). + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. + * + * IUPAC/InChI-Trust Licence No.1.0 for the + * International Chemical Identifier (InChI) + * Copyright (C) IUPAC and InChI Trust Limited + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, + * or any later version. + * + * Please note that this library is distributed WITHOUT ANY WARRANTIES + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. + * + * You should have received a copy of the IUPAC/InChI Trust InChI + * Licence No. 1.0 with this library; if not, please write to: + * + * The InChI Trust + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK + * + * or e-mail to alan@inchi-trust.org + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef COMPILE_ANSI_ONLY +#include +#ifndef TARGET_LIB_FOR_WINCHI +#include +#endif +#endif + +#include "../../INCHI_BASE/src/mode.h" + +#if( BUILD_WITH_AMI == 1 && defined( _MSC_VER ) && MSC_AMI == 1 ) +#include +#include +#endif + +#include "../../INCHI_BASE/src/ichitime.h" +#include "../../INCHI_BASE/src/incomdef.h" +#include "../../INCHI_BASE/src/ichidrp.h" +#include "../../INCHI_BASE/src/inpdef.h" +#include "../../INCHI_BASE/src/ichi.h" +#include "../../INCHI_BASE/src/strutil.h" +#include "../../INCHI_BASE/src/util.h" +#include "../../INCHI_BASE/src/ichierr.h" +#include "../../INCHI_BASE/src/ichimain.h" +#include "../../INCHI_BASE/src/ichicomp.h" +#include "../../INCHI_BASE/src/ichi_io.h" +#ifdef TARGET_EXE_STANDALONE +#include "../../INCHI_BASE/src/inchi_api.h" +#endif + +/* console-specific */ + +#if !defined(TARGET_API_LIB) && !defined(COMPILE_ANSI_ONLY) +/* Use Windows additional features */ + +/*****************************************************************************/ +int user_quit( struct tagINCHI_CLOCK *ic, const char *msg, unsigned long ulMaxTime ) +{ +#if defined(TARGET_LIB_FOR_WINCHI) + return 0; +#endif + +#if ( !defined(TARGET_LIB_FOR_WINCHI) && defined(_WIN32) ) + + int quit, enter, ret; + printf(msg); + if ( ulMaxTime ) + { + inchiTime ulEndTime; + InchiTimeGet( &ulEndTime ); + InchiTimeAddMsec( ic, &ulEndTime, ulMaxTime ); + while ( !_kbhit() ) { + if ( bInchiTimeIsOver( ic, &ulEndTime ) ) + { + printf("\n"); + return 0; + } + MySleep( 100 ); + } + } + while( 1 ) + { + quit = ( 'q' == (ret = _getch()) || 'Q'==ret || /*Esc*/ 27==ret ); + enter = ( '\r' == ret ); + if ( ret == 0xE0 ) + ret = _getch(); + else + _putch(ret); /* echo */ + if ( quit || enter ) + break; + printf( "\r" ); + printf( msg ); + } + _putch('\n'); + return quit; + +#else + + return 0; + +#endif /* #if ( defined(_WIN32) && !defined(TARGET_LIB_FOR_WINCHI) ) */ +} + + +/*****************************************************************************/ +void eat_keyboard_input( void ) +{ +#ifndef TARGET_LIB_FOR_WINCHI + + while ( _kbhit() ) + { + if ( 0xE0 == _getch() ) + _getch(); + } + +#endif +} + +#endif /* end of !COMPILE_ANSI_ONLY */ + + + + +#ifndef TARGET_LIB_FOR_WINCHI + /* COVERS THE CODE FROM HERE TO THE END OF FILE */ + + + +/* Enable/disable internal tests */ + +/*#define TEST_FPTRS*/ /* uncomment for INCHI_LIB testing only */ +#define REPEAT_ALL 0 /* set to 1 for mapping tests */ + + +/* Windows-console-mode specific */ + +int bInterrupted = 0; + +#if ( defined( _WIN32 ) && defined( _CONSOLE ) ) +#ifndef COMPILE_ANSI_ONLY +BOOL WINAPI MyHandlerRoutine( + DWORD dwCtrlType /* control signal type */ + ) { + if ( dwCtrlType == CTRL_C_EVENT || + dwCtrlType == CTRL_BREAK_EVENT || + dwCtrlType == CTRL_CLOSE_EVENT || + dwCtrlType == CTRL_LOGOFF_EVENT ) { + bInterrupted = 1; + return TRUE; + } + return FALSE; +} +int WasInterrupted(void) +{ +#ifdef _DEBUG + if ( bInterrupted ) + { + int stop=1; /* for debug only */ + } +#endif + return bInterrupted; +} + +#if ( BUILD_WITH_AMI == 1 ) +#define CTRL_STOP_EVENT 101 +#endif +#endif /* ifndef COMPILE_ANSI_ONLY */ +#endif /* if( defined( _WIN32 ) && defined( _CONSOLE ) ) */ + + + + + +/*****************************************************************************/ +int main( int argc, char *argv[ ] ) +{ + +/*************************/ +#if ( BUILD_WITH_AMI == 1 ) +/*************************/ + + /* if in AMI mode, main() starts here and */ + /* captures command line to pass to actual */ + /* workers - ProcessSingleInputFile() */ + /* ProcessMultipleInputFiles() */ +int i, ret=0, ami=0; + + /* Check if multiple inputs expected */ + for (i=1; i < argc; i++) + { + if (argv[i][0] == INCHI_OPTION_PREFX) + { + if ( !inchi_stricmp(argv[i]+1, "AMI") ) + { + ami = 1; + break; + } + } + } + + if ( ami ) + ret = ProcessMultipleInputFiles( argc, argv); + else + ret = ProcessSingleInputFile(argc,argv); + + return 0; +} + + + +/*******************************************************/ +int ProcessMultipleInputFiles( int argc, char *argv[ ] ) +/*******************************************************/ +{ + int i, ret=0, nfn_ins=0, + AMIOutStd=0, AMILogStd=0, AMIPrbNone=0; + char *fn_out, *fn_log, *fn_prb; + char pNUL[] = "NUL"; + char **fn_ins=NULL, **targv=NULL; + + +#if( BUILD_WITH_AMI == 1 && defined( _MSC_VER ) && MSC_AMI == 1 ) + struct _finddata_t file_info; + intptr_t hFile=-1; + int retFile, lenPath; + char *pName, *pOutPath=NULL; + char pathname[_MAX_PATH]; + char szBlank[] = ""; + int numFiles=0; /* counts processed files */ + #else + int p; +#endif + + fn_ins = (char**) inchi_calloc( argc, sizeof(char *) ); + if ( !fn_ins ) + { + fprintf(stderr, "Not enough memory.\n"); + goto exit_ami; + } + + /* Check for other options and collect inputs. */ + for (i=1; i < argc; i++) + { + if (argv[i][0] == INCHI_OPTION_PREFX) + { + if ( !inchi_stricmp(argv[i]+1, "STDIO") ) + { + fprintf( stderr, "Options AMI and STDIO are not compatible.\n" ); + goto exit_ami; + } + else if ( !inchi_stricmp(argv[i]+1, "AMIOutStd") ) + { + AMIOutStd = 1; + } + else if ( !inchi_stricmp(argv[i]+1, "AMILogStd") ) + { + AMILogStd = 1; + } + else if ( !inchi_stricmp(argv[i]+1, "AMIPrbNone") ) + { + AMIPrbNone = 1; + } +#if( BUILD_WITH_AMI == 1 && defined( _MSC_VER ) && MSC_AMI == 1 ) + else if ( !inchi_memicmp(argv[i]+1, "OP:", 3) ) + { + pOutPath = argv[i]+4; /* output path */ + } +#endif + } + else + { + fn_ins[nfn_ins] = argv[i]; + nfn_ins++; + } + } + + if ( !nfn_ins ) + { + fprintf(stderr, "At least one input file is expected in AMI mode.\n"); + goto exit_ami; + } + + + targv = (char**) calloc( argc+3, sizeof(char *) ); + + + if ( !targv ) + { + fprintf(stderr, "Not enough memory.\n"); + goto exit_ami; + } + +#if( BUILD_WITH_AMI == 1 && defined( _MSC_VER ) && MSC_AMI == 1 ) + if ( pName = strrchr( fn_ins[0], INCHI_PATH_DELIM ) ) { + pName ++; + lenPath = pName - fn_ins[0]; + } else { + pName = fn_ins[0]; + lenPath = 0; + } + for ( hFile = _findfirst(fn_ins[0], &file_info), retFile=0; + !retFile&&-1 != hFile; + retFile = _findnext(hFile, &file_info), numFiles ++ ) +#else + for ( p=0; p < nfn_ins; p++ ) +#endif + { + int targc; +#if( BUILD_WITH_AMI == 1 && defined( _MSC_VER ) && MSC_AMI == 1 ) + const char *fn_in; + int inlen = lenPath+strlen(file_info.name); + if ( !file_info.size || (file_info.attrib & _A_SUBDIR) || inlen >= _MAX_PATH ) + continue; + memcpy( pathname, fn_ins[0], lenPath ); + strcpy( pathname+lenPath, file_info.name ); + fn_in = pathname; + if ( 0==numFiles%5000 ) + _heapmin(); /* reduce heap fragmentation */ +#else + const char *fn_in = fn_ins[p]; + int inlen = strlen(fn_in); +#endif + fn_out=fn_log=fn_prb=NULL; + + targv[0] = argv[0]; + targv[1] = (char *) fn_in; + targc = 1; + + if ( AMIOutStd ) + { + targv[++targc] = pNUL; + } +#if( BUILD_WITH_AMI == 1 && defined( _MSC_VER ) && MSC_AMI == 1 && ALLOW_EMPTY_PATHS == 1 ) + else if ( pOutPath ) + { + targv[++targc]=szBlank; /* output name will be created in process_single_input(...) */ + } +#endif + else + { + /* make output name as input name plus ext. */ + fn_out = (char*) inchi_calloc( inlen+6, sizeof(char ) ); + if ( fn_out ) + { + strcpy( fn_out, fn_in ); + strcat( fn_out, ".txt" ); + } + targv[++targc] = fn_out; + } + + if ( AMILogStd ) + { + targv[++targc] = pNUL; + } +#if( BUILD_WITH_AMI == 1 && defined( _MSC_VER ) && MSC_AMI == 1 && ALLOW_EMPTY_PATHS == 1 ) + else if ( pOutPath ) + { + targv[++targc]=szBlank; /* output name will be created in process_single_input(...) */ + } +#endif + else + { + /* make log name as input name plus ext. */ + fn_log = (char*) inchi_calloc( inlen+6, sizeof(char ) ); + if ( fn_log ) + { + strcpy( fn_log, fn_in ); + strcat( fn_log, ".log" ); + } + targv[++targc] = fn_log; + } + if ( AMIPrbNone ) + { + targv[++targc] = pNUL; + } +#if( BUILD_WITH_AMI == 1 && defined( _MSC_VER ) && MSC_AMI == 1 && ALLOW_EMPTY_PATHS == 1 ) + else if ( pOutPath ) + { + targv[++targc]=szBlank; /* output name will be created in process_single_input(...) */ + } +#endif + else + { + /* make problem file name as input file name plus ext. */ + fn_prb = (char*) inchi_calloc( inlen+6, sizeof(char ) ); + if ( fn_prb ) + { + strcpy( fn_prb, fn_in ); + strcat( fn_prb, ".prb" ); + } + targv[++targc] = fn_prb; + } + + for (i=1; i < argc; i++) + { + if (argv[i][0] == INCHI_OPTION_PREFX) + { + /* avoid strnicmp/strncasecmp */ + if ( (strlen(argv[i])>3)&& + (toupper(argv[i][1])=='A')&&(toupper(argv[i][2])=='M')&&(toupper(argv[i][3])=='I') ) + continue; + targv[++targc] = argv[i]; + } + } + + targv[++targc] = NULL; + + ret = ProcessSingleInputFile(targc,targv); /* ProcessSingleInputFile() is a former main() */ + + if ( fn_out ) + inchi_free( fn_out ); + if ( fn_log ) + inchi_free( fn_log ); + if ( fn_prb ) + inchi_free( fn_prb ); + +#if ( defined( _WIN32 ) && defined( _CONSOLE ) && !defined( COMPILE_ANSI_ONLY ) ) + if ( ret == CTRL_STOP_EVENT) + goto exit_ami; +#endif + } + + +exit_ami: +#if( BUILD_WITH_AMI == 1 && defined( _MSC_VER ) && MSC_AMI == 1 ) + if ( hFile != -1 ) + _findclose( hFile ); +#endif + if ( targv ) + inchi_free( targv ); + if ( fn_ins ) + inchi_free( fn_ins ); + + return 0; +} + + + +/**************************************************/ +int ProcessSingleInputFile( int argc, char *argv[ ] ) +/**************************************************/ +{ + +/**************************************/ +#endif /* #if ( BUILD_WITH_AMI == 1 ) */ +/**************************************/ + + /* if not in AMI mode, main() starts here */ + + int bReleaseVersion = bRELEASE_VERSION; + const int nStrLen = INCHI_SEGM_BUFLEN; + int nRet = 0, nRet1; + int i; + long num_err, num_output, num_inp; + /* long rcPict[4] = {0,0,0,0}; */ + unsigned long ulDisplTime = 0; /* infinite, milliseconds */ + unsigned long ulTotalProcessingTime = 0; + + CANON_GLOBALS CG; + INCHI_CLOCK ic; + + char szTitle[MAX_SDF_HEADER+MAX_SDF_VALUE+256]; + char szSdfDataValue[MAX_SDF_VALUE+1]; + + INPUT_PARMS inp_parms; + INPUT_PARMS *ip = &inp_parms; + + STRUCT_DATA struct_data; + STRUCT_DATA *sd = &struct_data; + + ORIG_ATOM_DATA OrigAtData; /* 0=> disconnected, 1=> original */ + ORIG_ATOM_DATA *orig_inp_data = &OrigAtData; + ORIG_ATOM_DATA PrepAtData[2]; /* 0=> disconnected, 1=> original */ + ORIG_ATOM_DATA *prep_inp_data = PrepAtData; + + PINChI2 *pINChI[INCHI_NUM]; + PINChI_Aux2 *pINChI_Aux[INCHI_NUM]; + + char *pLF, *pTAB; + INCHI_IOSTREAM_STRING temp_string_container; + INCHI_IOSTREAM_STRING *strbuf = &temp_string_container; + INCHI_IOSTREAM outputstr, logstr, prbstr, instr; + INCHI_IOSTREAM *pout=&outputstr, *plog = &logstr, *pprb = &prbstr, *inp_file = &instr; +#ifdef TARGET_EXE_STANDALONE + char ik_string[256]; /*^^^ Resulting InChIKey string */ + int ik_ret=0; /*^^^ InChIKey-calc result code */ + int xhash1, xhash2; + char szXtra1[65], szXtra2[65]; + int inchi_ios_type = INCHI_IOSTREAM_TYPE_STRING; +#else + int inchi_ios_type = INCHI_IOSTREAM_TYPE_FILE; +#endif + +int have_err_in_GetOneStructure = 0; +int output_error_inchi = 0; + + + /* internal tests --- */ +#ifndef TEST_FPTRS + STRUCT_FPTRS *pStructPtrs = NULL; +#else + STRUCT_FPTRS struct_fptrs, *pStructPtrs =&struct_fptrs; /* INCHI_LIB debug only */ +#endif +#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) + int num_repeat = REPEAT_ALL; +#endif + + +#if ( TRACE_MEMORY_LEAKS == 1 ) + _CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF); +/* for execution outside the VC++ debugger uncomment one of the following two */ +/*#define MY_REPORT_FILE _CRTDBG_FILE_STDERR */ +/*#define MY_REPORT_FILE _CRTDBG_FILE_STDOUT */ +#ifdef MY_REPORT_FILE + _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_WARN, MY_REPORT_FILE ); + _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ERROR, MY_REPORT_FILE ); + _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ASSERT, MY_REPORT_FILE ); +#else + _CrtSetReportMode(_CRT_WARN | _CRT_ERROR, _CRTDBG_MODE_DEBUG); +#endif + /* turn on floating point exceptions */ + { + /* Get the default control word. */ + int cw = _controlfp( 0,0 ); + + /* Set the exception masks OFF, turn exceptions on. */ + /*cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_INEXACT|EM_ZERODIVIDE|EM_DENORMAL);*/ + cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE|EM_DENORMAL); + + /* Set the control word. */ + _controlfp( cw, MCW_EM ); + } +#endif + +#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) +repeat: + inchi_ios_close(inp_file); + inchi_ios_close(pout); + inchi_ios_close(plog); + inchi_ios_close(pprb); + pStr = NULL; +#endif + +/* --- internal tests */ + + sd->bUserQuit = 0; + +#if ( defined( _WIN32 ) && defined( _CONSOLE ) && !defined( COMPILE_ANSI_ONLY ) ) + if ( SetConsoleCtrlHandler( MyHandlerRoutine, 1 ) ) + ConsoleQuit = WasInterrupted; +#endif + + num_inp = 0; + num_err = 0; + num_output = 0; + + inchi_ios_init(inp_file, INCHI_IOSTREAM_TYPE_FILE, NULL); + inchi_ios_init(pout, inchi_ios_type, NULL); + inchi_ios_init(plog, inchi_ios_type, stdout); + inchi_ios_init(pprb, inchi_ios_type, NULL); + memset( strbuf, 0, sizeof(strbuf) ); + + + + if ( argc == 1 || argc==2 && ( argv[1] [0]==INCHI_OPTION_PREFX ) && + (!strcmp(argv[1]+1, "?") || !inchi_stricmp(argv[1]+1, "help") ) ) + { + HelpCommandLineParms(plog); + inchi_ios_flush(plog); + return 0; + } + + /* original input structure */ + memset( orig_inp_data , 0, sizeof( *orig_inp_data ) ); + memset( prep_inp_data , 0, 2*sizeof( *prep_inp_data ) ); + memset( pINChI, 0, sizeof(pINChI ) ); + memset( pINChI_Aux, 0, sizeof(pINChI_Aux) ); + memset( szSdfDataValue , 0, sizeof( szSdfDataValue ) ); + + memset( &CG, 0, sizeof(CG)); + memset( &ic, 0, sizeof(ic)); + + plog->f = stderr; + + if ( 0 > ReadCommandLineParms( argc, (const char **)argv, + ip, + szSdfDataValue, + &ulDisplTime, + bReleaseVersion, + plog) ) + { + goto exit_function; + } + + + if ( !OpenFiles( &(inp_file->f), &(pout->f), &(plog->f), &(pprb->f), ip ) ) + { + goto exit_function; + } + + + if ( ip->bNoStructLabels ) + { + ip->pSdfLabel = NULL; + ip->pSdfValue = NULL; + } + else if ( ip->nInputType == INPUT_INCHI_PLAIN || + ip->nInputType == INPUT_INCHI ) + { + /* the input may contain both the header and the label of the structure */ + if ( !ip->pSdfLabel ) + ip->pSdfLabel = ip->szSdfDataHeader; + if ( !ip->pSdfValue ) + ip->pSdfValue = szSdfDataValue; + } + + set_line_separators( ip->bINChIOutputOptions, &pLF, &pTAB ); + + save_command_line( argc, argv, plog); + + + PrintInputParms(plog,ip); + + inchi_ios_flush2(plog, stderr); + + if ( 0>=inchi_strbuf_init( strbuf, INCHI_STRBUF_INITIAL_SIZE, INCHI_STRBUF_SIZE_INCREMENT ) ) + { + inchi_ios_eprint( plog, "Cannot allocate internal string buffer. Terminating\n"); + inchi_ios_flush2(plog, stderr); + goto exit_function; + } + + +#if ( READ_INCHI_STRING == 1 ) + + if ( ip->nInputType == INPUT_INCHI ) + { + const int bInChI2Structure = 0 != (ip->bReadInChIOptions & READ_INCHI_TO_STRUCTURE); + memset( sd, 0, sizeof(*sd) ); + + if ( bInChI2Structure ) + { + /* loop through file lines here */ + INCHI_IOSTREAM tmpinpustream; + INCHI_IOSTREAM_STRING *pTmpIn= &tmpinpustream.s; + int crlf2lf = 0, preserve_lf = 1, read_result = 0; + inchi_ios_init( &tmpinpustream, INCHI_IOSTREAM_TYPE_STRING, NULL ); + while ( 1 ) + { + char *p, *pi; + + read_result = inchi_strbuf_getline( pTmpIn, inp_file->f, crlf2lf, preserve_lf ); + + if ( read_result == -1 ) + break; /* EOF or read error */ + + p = pTmpIn->pStr; + if ( !p ) + continue; + pi = strstr( p, "InChI=1" ); + if ( pi != p ) + continue; + + num_inp++; + ip->lMolfileNumber = num_inp; + + ReadWriteInChI( &tmpinpustream, pout, plog, ip, sd, NULL, 0, 0, NULL, NULL, NULL, 0, NULL, &ic, &CG); + + /*fprintf( stderr, "%ld", num_inp );*/ + inchi_strbuf_reset( pTmpIn ); + inchi_ios_flush2(plog, stderr); + } + fprintf( stderr, "\r" ); + inchi_strbuf_close( pTmpIn); + } + else + { + /* loop through file lines within ReadWriteInChI */ + ReadWriteInChI( inp_file, pout, plog, ip, sd, NULL, 0, 0, NULL, NULL, NULL, 0, NULL, &ic, &CG); + + num_inp = sd->fPtrStart; + num_err = sd->fPtrEnd; + } + + inchi_ios_flush2(plog, stderr); + ulTotalProcessingTime = sd->ulStructTime; + /*num_inp = sd->fPtrStart; + num_err = sd->fPtrEnd; + */ + goto exit_function; + } + +#endif + + + ulTotalProcessingTime = 0; + if ( pStructPtrs ) + { + memset ( pStructPtrs, 0, sizeof(pStructPtrs[0]) ); + } + +#ifdef ERR_INCHI_STRING_ALLOWED + output_error_inchi = ip->bINChIOutputOptions2 & INCHI_OUT_INCHI_GEN_ERROR; +#endif + + /*************************************************************/ + /* Main cycle : read input structures and create their INChI */ + /*************************************************************/ + while ( !sd->bUserQuit && !bInterrupted ) + { + + if ( ip->last_struct_number && num_inp >= ip->last_struct_number ) + { + nRet = _IS_EOF; /* simulate end of file */ + goto exit_function; + } + + /* Get (the next) one structure from input stream */ + nRet = GetOneStructure( &ic, sd, ip, + szTitle, inp_file, + plog, pout, pprb, + orig_inp_data, &num_inp, + pStructPtrs ); + + inchi_ios_flush2(plog, stderr); + + if ( pStructPtrs ) + pStructPtrs->cur_fptr ++; + + if ( sd->bUserQuit ) + break; + + have_err_in_GetOneStructure = 0; + switch ( nRet ) + { + case _IS_FATAL: + num_err ++; + if ( output_error_inchi == 0 ) + goto exit_function; + else + sd->pStrErrStruct[0] = '\0'; /* depress re-appearance of error as warning in ProcessOneStr */ + break; + case _IS_EOF: + goto exit_function; + case _IS_ERROR: + num_err ++; + have_err_in_GetOneStructure = 1; + if ( output_error_inchi == 0 ) + continue; + else + sd->pStrErrStruct[0] = '\0'; /* depress re-appearance of error as warning in ProcessOneStr */ + break; + case _IS_SKIP: + continue; + default: + sd->pStrErrStruct[0] = '\0'; /* depress re-appearance of error as warning in ProcessOneStr */ + } + + + /* + Create INChI for each connected component of the structure; + optionally display them; + output INChI for the whole structure + */ + + if ( orig_inp_data->polymer ) + { + if ( ip->bPolymers ) + { + orig_inp_data->polymer->valid = 1; + } + else + { + inchi_ios_eprint( plog, "Ignore polymer data" ); + orig_inp_data->polymer->valid = 0; + } + } + + nRet1 = ProcessOneStructureEx( &ic, &CG, sd, ip, szTitle, + pINChI,pINChI_Aux, + inp_file, plog, pout, pprb, + orig_inp_data, prep_inp_data, + num_inp, strbuf, + 0 /* save_opt_bits */); + + inchi_ios_flush2(plog, stderr); + +#ifdef TARGET_EXE_STANDALONE + /* correctly treat tabbed output with InChIKey */ + if ( ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT ) + if ( ip->bCalcInChIHash != INCHIHASH_NONE ) + if (pout->s.pStr) + if (pout->s.nUsedLength>0) + if (pout->s.pStr[pout->s.nUsedLength-1]=='\n') + /* replace LF with TAB */ + pout->s.pStr[pout->s.nUsedLength-1] = '\t'; +#endif + + /* free INChI memory */ + FreeAllINChIArrays( pINChI, pINChI_Aux, sd->num_components ); + + /* free structure data */ + FreeOrigAtData( orig_inp_data ); + FreeOrigAtData( prep_inp_data ); + FreeOrigAtData( prep_inp_data+1 ); + + ulTotalProcessingTime += sd->ulStructTime; + + if (nRet1 == _IS_SKIP ) + continue; + + nRet = inchi_max(nRet, nRet1); + switch ( nRet ) + { + + case _IS_FATAL: + if ( !have_err_in_GetOneStructure ) + /* increment error counter only if error is not appear earlier */ + num_err ++; + if ( output_error_inchi ) + emit_error_inchi_text( ip,num_inp, pLF, pTAB, pout); + else + goto exit_function; + break; + case _IS_ERROR: + if ( !have_err_in_GetOneStructure ) + /* increment error counter only if error is not appear earlier */ + num_err ++; + if ( output_error_inchi ) + emit_error_inchi_text( ip,num_inp, pLF, pTAB, pout); + else + continue; + break; + case _IS_SKIP: + continue; + } + +#ifdef TARGET_EXE_STANDALONE + if ( ip->bCalcInChIHash != INCHIHASH_NONE ) + { + char *buf = NULL; + size_t slen = pout->s.nUsedLength; + + extract_inchi_substring(&buf, pout->s.pStr, slen); + + if (NULL==buf) + { + ik_ret = INCHIKEY_NOT_ENOUGH_MEMORY; + } + else + { + xhash1 = xhash2 = 0; + if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1 ) || + ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) + xhash1 = 1; + if ( ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA2 ) || + ( ip->bCalcInChIHash == INCHIHASH_KEY_XTRA1_XTRA2 ) ) + xhash2 = 1; + ik_ret = GetINCHIKeyFromINCHI( buf, + xhash1, + xhash2, + ik_string, + szXtra1, + szXtra2); + inchi_free(buf); + } + + if (ik_ret==INCHIKEY_OK) + { + /* NB: correctly treat tabbed output with InChIKey & hash extensions */ + char csep = '\n'; + +#ifdef TARGET_EXE_STANDALONE + if ( ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT ) + csep = '\t'; +#endif + + inchi_ios_print(pout, "InChIKey=%-s",ik_string); + if ( xhash1 ) + inchi_ios_print(pout, "%cXHash1=%-s",csep,szXtra1); + if ( xhash2 ) + inchi_ios_print(pout, "%cXHash2=%-s",csep,szXtra2); + inchi_ios_print(pout, "\n"); + } + else + { + inchi_ios_print(plog, "Warning (Could not compute InChIKey: ", num_inp); + switch(ik_ret) + { + case INCHIKEY_UNKNOWN_ERROR: + inchi_ios_print(plog, "unresolved error)"); + break; + case INCHIKEY_EMPTY_INPUT: + inchi_ios_print(plog, "got an empty string)"); + break; + case INCHIKEY_INVALID_INCHI_PREFIX: + case INCHIKEY_INVALID_INCHI: + case INCHIKEY_INVALID_STD_INCHI: + inchi_ios_print(plog, "got non-InChI string)"); + break; + case INCHIKEY_NOT_ENOUGH_MEMORY: + inchi_ios_print(plog, "not enough memory to treat the string)"); + break; + default:inchi_ios_print(plog, "internal program error)"); + break; + } + inchi_ios_print(plog, " structure #%-lu.\n", num_inp); + if ( ip->bINChIOutputOptions & INCHI_OUT_TABBED_OUTPUT ) + inchi_ios_print(pout, "\n"); + } + inchi_ios_flush(pout); + inchi_ios_flush2(plog, stderr); + } + + else + inchi_ios_flush(pout); + +#endif + + /* --- debug only --- + if ( pStructPtrs->cur_fptr > 5 ) { + pStructPtrs->cur_fptr = 5; + } + */ + } /* end of main cycle - while ( !sd->bUserQuit && !bInterrupted ) */ + + + +exit_function: + + /* avoid memory leaks in case of fatal error */ + if ( pStructPtrs && pStructPtrs->fptr ) +{ + inchi_free( pStructPtrs->fptr ); + } + + /* free INChI memory */ + FreeAllINChIArrays( pINChI, pINChI_Aux, sd->num_components ); + /* free structure data */ + FreeOrigAtData( orig_inp_data ); + FreeOrigAtData( prep_inp_data ); + FreeOrigAtData( prep_inp_data+1 ); + + + /* close output(s) */ + inchi_ios_close(inp_file); + inchi_ios_close(pout); + inchi_ios_close(pprb); + + + { + int hours, minutes, seconds, mseconds; + + SplitTime( ulTotalProcessingTime, &hours, &minutes, &seconds, &mseconds ); + + inchi_ios_eprint( plog, "Finished processing %ld structure%s: %ld error%s, processing time %d:%02d:%02d.%02d\n", + num_inp, num_inp==1?"":"s", + num_err, num_err==1?"":"s", + hours, minutes, seconds,mseconds/10); + inchi_ios_flush2(plog, stderr); + } + + + /* cleanup */ + inchi_ios_close( plog ); + inchi_strbuf_close( strbuf ); + for ( i = 0; i < MAX_NUM_PATHS; i ++ ) + { + if ( ip->path[i] ) + { + inchi_free( (void*) ip->path[i] ); /* cast deliberately discards 'const' qualifier */ + ip->path[i] = NULL; + } + } + SetBitFree( &CG ); + +/* internal tests --- */ +#if ( defined(REPEAT_ALL) && REPEAT_ALL > 0 ) + if ( num_repeat-- > 0 ) + goto repeat; +#endif + +#if ( ( BUILD_WITH_AMI==1 ) && defined( _WIN32 ) && defined( _CONSOLE ) && !defined( COMPILE_ANSI_ONLY ) ) + if ( bInterrupted ) + return CTRL_STOP_EVENT; +#endif + + return 0; +} + + + + + +/*****************************************************************************/ +void save_command_line( int argc, char *argv[ ], INCHI_IOSTREAM *plog) +{ +int k; + + inchi_ios_eprint( plog, "The command line used:\n\""); + for(k=0; kbNoStructLabels ) + { + if ( !(ip->pSdfLabel&&ip->pSdfLabel[0]) && !(ip->pSdfValue&&ip->pSdfValue[0]) ) + { + inchi_ios_print( pout, "%sStructure: %ld%s", + pLF, num_inp, pTAB ); + } + else + { + inchi_ios_print( pout, "%sStructure: %ld.%s%s%s%s%s", + pLF, + num_inp, + SDF_LBL_VAL(ip->pSdfLabel,ip->pSdfValue), + pTAB ); + } + } + if ( ip->bINChIOutputOptions & INCHI_OUT_STDINCHI ) + inchi_ios_eprint( pout, "InChI=1S//\n"); /* emit err string */ + else + inchi_ios_eprint( pout, "InChI=1//\n"); /* emit err string */ +} + +#endif /* ifndef TARGET_LIB_FOR_WINCHI */ diff --git a/INCHI-1-SRC/INCHI_EXE/inchi-1/vc9/inchi-1.aps b/INCHI-1-SRC/INCHI_EXE/inchi-1/vc9/inchi-1.aps new file mode 100644 index 0000000..cd14e39 Binary files /dev/null and b/INCHI-1-SRC/INCHI_EXE/inchi-1/vc9/inchi-1.aps differ diff --git a/INCHI-1-SRC/INCHI/vc9/inchi-1/inchi-1.rc b/INCHI-1-SRC/INCHI_EXE/inchi-1/vc9/inchi-1.rc similarity index 82% rename from INCHI-1-SRC/INCHI/vc9/inchi-1/inchi-1.rc rename to INCHI-1-SRC/INCHI_EXE/inchi-1/vc9/inchi-1.rc index 3309f7b..ee26752 100644 --- a/INCHI-1-SRC/INCHI/vc9/inchi-1/inchi-1.rc +++ b/INCHI-1-SRC/INCHI_EXE/inchi-1/vc9/inchi-1.rc @@ -1,113 +1,114 @@ -// Microsoft Visual C++ generated resource script. -// -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "afxres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// Russian resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS) -#ifdef _WIN32 -LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT -#pragma code_page(1251) -#endif //_WIN32 - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""afxres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - -#endif // Russian resources -///////////////////////////////////////////////////////////////////////////// - - -///////////////////////////////////////////////////////////////////////////// -// English (U.S.) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -#ifdef _WIN32 -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) -#endif //_WIN32 - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,0,4,1 - PRODUCTVERSION 1,0,4,1 - FILEFLAGSMASK 0x17L -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x4L - FILETYPE 0x1L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "Comments", "IUPAC International Chemical Identifier (InChI) version 1, software (command line) version 1.04. Build of September 9, 2011." - VALUE "FileDescription", "inchi-1 Application" - VALUE "FileVersion", "1, 0, 4, 1" - VALUE "InternalName", "inchi-1.exe" - VALUE "OriginalFilename", "inchi-1.exe" - VALUE "ProductName", "IUPAC International Chemical Identifier (InChI) version 1, software (command line) version 1.04. Build of September 9, 2011." - VALUE "ProductVersion", "1, 0, 4, 1" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END - -#endif // English (U.S.) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Russian resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS) +#ifdef _WIN32 +LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT +#pragma code_page(1251) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // Russian resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,5,0 + PRODUCTVERSION 1,0,5,0 + FILEFLAGSMASK 0x37L +#ifdef _DEBUG + FILEFLAGS 0x21L +#else + FILEFLAGS 0x20L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "IUPAC International Chemical Identifier (InChI) version 1, software (command line) version 1.05, Winter 2017" + VALUE "FileDescription", "Command-line InChI utility" + VALUE "FileVersion", "1, 0, 5, 0" + VALUE "InternalName", "inchi-1.exe" + VALUE "OriginalFilename", "inchi-1.exe" + VALUE "ProductName", "IUPAC International Chemical Identifier (InChI) version 1, software (command line) version 1.05, Winter 2017" + VALUE "ProductVersion", "1, 0, 5, 0" + VALUE "SpecialBuild", "RELEASE" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/INCHI-1-SRC/INCHI/vc9/inchi-1/inchi-1.sln b/INCHI-1-SRC/INCHI_EXE/inchi-1/vc9/inchi-1.sln similarity index 100% rename from INCHI-1-SRC/INCHI/vc9/inchi-1/inchi-1.sln rename to INCHI-1-SRC/INCHI_EXE/inchi-1/vc9/inchi-1.sln diff --git a/INCHI-1-SRC/INCHI/vc9/inchi-1/inchi-1.vcproj b/INCHI-1-SRC/INCHI_EXE/inchi-1/vc9/inchi-1.vcproj similarity index 57% rename from INCHI-1-SRC/INCHI/vc9/inchi-1/inchi-1.vcproj rename to INCHI-1-SRC/INCHI_EXE/inchi-1/vc9/inchi-1.vcproj index c2aac65..c7dfc5c 100644 --- a/INCHI-1-SRC/INCHI/vc9/inchi-1/inchi-1.vcproj +++ b/INCHI-1-SRC/INCHI_EXE/inchi-1/vc9/inchi-1.vcproj @@ -21,8 +21,8 @@ @@ -45,7 +45,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="../../common;../../main;../../cmlpp;../../ICHI_LIB" - PreprocessorDefinitions="WIN32;DEBUG;_CONSOLE;ADD_CMLPP=0" + PreprocessorDefinitions="WIN32;DEBUG;_CONSOLE;ADD_CMLPP=0;ADD_AMI_MODE=1;TARGET_EXE_STANDALONE=1" MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="1" @@ -70,6 +70,7 @@ LinkIncremental="2" GenerateDebugInformation="true" SubSystem="1" + StackReserveSize="16000000" TargetMachine="1" /> @@ -121,7 +122,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="../../common;../../main;../../cmlpp;../../ICHI_LIB" - PreprocessorDefinitions="WIN32;WIN64;DEBUG;_CONSOLE;ADD_CMLPP=0;ADD_AMI_MODE=1" + PreprocessorDefinitions="COMPILE_ANSI_ONLY;WIN32;WIN64;DEBUG;_CONSOLE;ADD_CMLPP=0;ADD_AMI_MODE=1;TARGET_EXE_STANDALONE=1" MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="1" @@ -142,10 +143,10 @@ /> - - + + + + + + + + + + + + diff --git a/INCHI-1-SRC/INCHI/vc9/inchi-1/resource.h b/INCHI-1-SRC/INCHI_EXE/inchi-1/vc9/resource.h similarity index 100% rename from INCHI-1-SRC/INCHI/vc9/inchi-1/resource.h rename to INCHI-1-SRC/INCHI_EXE/inchi-1/vc9/resource.h diff --git a/INCHI-1-SRC/LICENCE b/INCHI-1-SRC/LICENCE index 3154827..88e26b6 100644 --- a/INCHI-1-SRC/LICENCE +++ b/INCHI-1-SRC/LICENCE @@ -1,314 +1,262 @@ -IUPAC/InChI-Trust Licence for the International Chemical Identifier (InChI) -Software version 1.04, September 2011 +IUPAC/InChI-Trust Licence for the +International Chemical Identifier (InChI) Software ("IUPAC/InChI-Trust InChI Licence No. 1.0") -Copyright (C) IUPAC and InChI Trust Limited -This library is free software; you can redistribute it and/or modify it under -the terms of the IUPAC/InChI Trust InChI Licence No. 1.0), or (at your option) -any later version. +Copyright (c) IUPAC and InChI Trust Limited +This library is free software; you can redistribute it and/or modify it under the terms of the +IUPAC/InChI Trust InChI Licence No. 1.0), or (at your option) any later version. -Terms and Conditions for Copying, Distribution and Modification of the InChI -Software +Terms and Conditions for Copying, Distribution and Modification of the InChI Software -0. This Licence Agreement applies to any software library or other program -which contains a notice placed by the copyright holder or other authorized -party saying it may be distributed under the terms of this Licence. The -Licensee is addressed as "you". + 0. This Licence Agreement applies to any software library or other program which contains a notice +placed by the copyright holder or other authorized party saying it may be distributed under the terms of +this Licence. The Licensee is addressed as "you". 'IUPAC' means the International Union of Pure and Applied Chemistry. - A "library" means a collection of software functions and/or data prepared so -as to be conveniently linked with application programs (which use some of -those functions and data) to form executables. - - The "Library", below, refers to any such software library or work which has -been distributed under these terms. A "work based on the Library" means either -the Library or any derivative work under copyright law: that is to say, a work -containing the Library or a portion of it, either verbatim or with modifications -and/or translated straightforwardly into another language. (Hereinafter, -translation is included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for making -modifications to it. For a library, complete source code means all the source -code for all modules it contains, plus any associated interface definition -files, plus the scripts used to control compilation and installation of the -library. + A "library" means a collection of software functions and/or data prepared so as to be conveniently +linked with application programs (which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work which has been distributed under +these terms. A "work based on the Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with +modifications and/or translated straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) - Activities other than copying, distribution and modification are not covered -by this Licence; they are outside its scope. The act of running a program using -the Library is not restricted, and output from such a program is covered only -if its contents constitute a work based on the Library (independent of the use -of the Library in a tool for writing it). Whether that is true depends on what -the Library does and what the program that uses the Library does. + "Source code" for a work means the preferred form of the work for making modifications to it. For a +library, complete source code means all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation and installation of the library. -1. You may copy and distribute verbatim copies of the Library's complete source -code as you receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice and -disclaimer of warranty; keep intact all the notices that refer to this Licence -and to the absence of any warranty; and distribute a copy of this Licence -along with the Library. + Activities other than copying, distribution and modification are not covered by this Licence; they are +outside its scope. The act of running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based on the Library (independent of +the use of the Library in a tool for writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. -You may charge a fee for the physical act of transferring a copy, and you may at -your option offer warranty protection in exchange for a fee. +1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, +in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the notices that refer to this Licence and to +the absence of any warranty; and distribute a copy of this Licence along with the Library. -2. You may modify your copy or copies of the Library or any portion of it, thus -forming a work based on the Library, and copy and distribute such modifications -or work under the terms of Section 1 above, provided that you also meet all -of these conditions: +You may charge a fee for the physical act of transferring a copy, and you may at your option offer +warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based +on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, +provided that you also meet all of these conditions: a) The modified work must itself be a software library. -b) You must cause the files modified to carry prominent notices stating that you -changed the files and the date of any change. - -c) You must cause the whole of the work to be licensed at no charge to all third -parties under the terms of this Licence. This requirement does not extend to -any "work that uses the Library" that might also be compiled or linked against -the "work based on the Library." - -d) If a facility in the modified Library refers to a function or a table of data -to be supplied by an application program that uses the facility, other than as -an argument passed when the facility is invoked, then you must make a good faith -effort to ensure that, in the event an application does not supply such function -or table, the facility still operates, and performs whatever part of its purpose -remains meaningful. - -(For example, a function in a library to compute square roots has a purpose that -is entirely well-defined independent of the application. Therefore, Subsection -2d requires that any application-supplied function or table used by this -function must be optional: if the application does not supply it, the square +b) You must cause the files modified to carry prominent notices stating that you changed the files and +the date of any change. + +c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms +of this Licence. This requirement does not extend to any "work that uses the Library" that might also +be compiled or linked against the "work based on the Library." + +d) If a facility in the modified Library refers to a function or a table of data to be supplied by an +application program that uses the facility, other than as an argument passed when the facility is invoked, +then you must make a good faith effort to ensure that, in the event an application does not supply such +function or table, the facility still operates, and performs whatever part of its purpose remains +meaningful. + +(For example, a function in a library to compute square roots has a purpose that is entirely well-defined +independent of the application. Therefore, Subsection 2d requires that any application-supplied +function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) -These requirements apply to the modified work as a whole. If identifiable -sections of that work are not derived from the Library, and can be reasonably -considered independent and separate works in themselves, then this Licence, and -its terms, do not apply to those sections when you distribute them as separate -works. But when you distribute the same sections as part of a whole which is a -work based on the Library, the distribution of the whole must be on the terms of -this Licence, whose permissions for other Licensees extend to the entire whole, -and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest your -rights to work written entirely by you; rather, the intent is to exercise the -right to control the distribution of derivative or collective works based on -the Library. - -In addition, mere aggregation of another work not based on the Library with the -Library (or with a work based on the Library) on a volume of a storage or -distribution medium does not bring the other work under the scope of -this Licence. +These requirements apply to the modified work as a whole. If identifiable sections of that work are not +derived from the Library, and can be reasonably considered independent and separate works in +themselves, then this Licence, and its terms, do not apply to those sections when you distribute them as +separate works. But when you distribute the same sections as part of a whole which is a work based on +the Library, the distribution of the whole must be on the terms of this Licence, whose permissions for +other Licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by +you; rather, the intent is to exercise the right to control the distribution of derivative or collective works +based on the Library. -3. You may opt to apply the terms of the ordinary GNU General Public Licence -instead of this Licence to a given copy of the Library. To do this, you must -alter all the notices that refer to this Licence, so that they refer to the -ordinary GNU General Public Licence, version 2, instead of to this Licence. -(If a newer version than version 2 of the ordinary GNU General Public Licence -has appeared, then you can specify that version instead if you wish.) -Do not make any other change in these notices. - -Once this change is made in a given copy, it is irreversible for that copy, so -the ordinary GNU General Public Licence applies to all subsequent copies and -derivative works made from that copy. - -This option is useful when you wish to copy part of the code of the Library into -a program that is not a library. - -4. You may copy and distribute the Library (or a portion or derivative of it, -under Section 2) in object code or executable form under the terms of Sections 1 -and 2 above provided that you accompany it with the complete corresponding -machine-readable source code, which must be distributed under the terms of -Sections 1 and 2 above on a medium customarily used for software interchange. - -If distribution of object code is made by offering access to copy from a -designated place, then offering equivalent access to copy the source code from -the same place satisfies the requirement to distribute the source code, even -though third parties are not compelled to copy the source along with the object +In addition, mere aggregation of another work not based on the Library with the Library (or with a +work based on the Library) on a volume of a storage or distribution medium does not bring the other +work under the scope of this Licence. + +3. You may opt to apply the terms of the ordinary GNU General Public Licence instead of this Licence +to a given copy of the Library. To do this, you must alter all the notices that refer to this Licence, so +that they refer to the ordinary GNU General Public Licence, version 2, instead of to this Licence. (If a +newer version than version 2 of the ordinary GNU General Public Licence has appeared, then you can +specify that version instead if you wish.) Do not make any other change in these notices. + +Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General +Public Licence applies to all subsequent copies and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of the Library into a program that is not a +library. + +4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object +code or executable form under the terms of Sections 1 and 2 above provided that you accompany it +with the complete corresponding machine-readable source code, which must be distributed under the +terms of Sections 1 and 2 above on a medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy from a designated place, then offering +equivalent access to copy the source code from the same place satisfies the requirement to distribute +the source code, even though third parties are not compelled to copy the source along with the object code. -5. A program that contains no derivative of any portion of the Library, but is -designed to work with the Library by being compiled or linked with it, is called -a "work that uses the Library". Such a work, in isolation, is not a derivative -work of the Library, and therefore falls outside the scope of this Licence. - -6. You may combine or link a "work that uses the Library" with the Library to -produce a work containing portions of the Library, and distribute that work -under terms of your choice. - -You must give prominent notice with each copy of the work that the Library is -used in it and that the Library and its use are covered by this Licence. -You must supply a copy of this Licence. If the work during execution displays -copyright notices, you must include the copyright notice for the Library among -them, as well as a reference directing the user to the copy of this Licence. -Also, you must do one of these things: - -a) Accompany the work with the complete corresponding machine-readable source -code for the Library including whatever changes to the Library were used in the -work (which must be distributed under Sections 1 and 2 above). - -b) Use a suitable shared library mechanism for linking with the Library. -A suitable mechanism is one that (1) uses at run time a copy of the library -already present on the user's computer system, rather than copying library -functions into the executable, and (2) will operate properly with a modified -version of the library, if the user installs one, as long as the modified -version is interface-compatible with the version that the work was made with. - -c) Accompany the work with a written offer, valid for at least three years, to -give the same user the materials specified in Subsection 6a, above, for a charge -no more than the cost of performing this distribution. - -d) If distribution of the work is made by offering access to copy from a -designated place, offer equivalent access to copy the above specified materials -from the same place. - -e) Verify that the user has already received a copy of these materials or that -you have already sent this user a copy. - -7. You may place library facilities that are a work based on the Library -side-by-side in a single library together with other library facilities not -covered by this Licence, and distribute such a combined library, provided that -the separate distribution of the work based on the Library and of the other -library facilities is otherwise permitted, and provided that you do these two -things: - -a) Accompany the combined library with a copy of the same work based on the -Library, uncombined with any other library facilities. This must be distributed -under the terms of the Sections above. - -b) Give prominent notice with the combined library of the fact that part of it -is a work based on the Library, and explaining where to find the accompanying -uncombined form of the same work. - -8. You may not copy, modify, sublicense, link with, or distribute the Library -except as expressly provided under this Licence. Any attempt otherwise to copy, -modify, sublicense, link with, or distribute the Library is void, and will -automatically terminate your rights under this Licence. However, parties who -have received copies, or rights, from you under this Licence will not have their +5. A program that contains no derivative of any portion of the Library, but is designed to work with the +Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in +isolation, is not a derivative work of the Library, and therefore falls outside the scope of this Licence. + +6. You may combine or link a "work that uses the Library" with the Library to produce a work +containing portions of the Library, and distribute that work under terms of your choice. + +You must give prominent notice with each copy of the work that the Library is used in it and that the +Library and its use are covered by this Licence. You must supply a copy of this Licence. If the work +during execution displays copyright notices, you must include the copyright notice for the Library +among them, as well as a reference directing the user to the copy of this Licence. Also, you must do +one of these things: + +a) Accompany the work with the complete corresponding machine-readable source code for the +Library including whatever changes to the Library were used in the work (which must be distributed +under Sections 1 and 2 above). + +b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one +that (1) uses at run time a copy of the library already present on the user's computer system, rather than +copying library functions into the executable, and (2) will operate properly with a modified version of +the library, if the user installs one, as long as the modified version is interface-compatible with the +version that the work was made with. + +c) Accompany the work with a written offer, valid for at least three years, to give the same user the +materials specified in Subsection 6a, above, for a charge no more than the cost of performing this +distribution. + +d) If distribution of the work is made by offering access to copy from a designated place, offer +equivalent access to copy the above specified materials from the same place. + +e) Verify that the user has already received a copy of these materials or that you have already sent this +user a copy. + +7. You may place library facilities that are a work based on the Library side-by-side in a single library +together with other library facilities not covered by this Licence, and distribute such a combined library, +provided that the separate distribution of the work based on the Library and of the other library +facilities is otherwise permitted, and provided that you do these two things: + +a) Accompany the combined library with a copy of the same work based on the Library, uncombined +with any other library facilities. This must be distributed under the terms of the Sections above. + +b) Give prominent notice with the combined library of the fact that part of it is a work based on the +Library, and explaining where to find the accompanying uncombined form of the same work. + +8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly +provided under this Licence. Any attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your rights under this Licence. +However, parties who have received copies, or rights, from you under this Licence will not have their Licences terminated so long as such parties remain in full compliance. -9. You are not required to accept this Licence, since you have not signed it. -However, nothing else grants you permission to modify or distribute the Library -or its derivative works. These actions are prohibited by law if you do not -accept this Licence. Therefore, by modifying or distributing the Library (or -any work based on the Library), you indicate your acceptance of this Licence to -do so, and all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - -10. Each time you redistribute the Library (or any work based on the Library), -the recipient automatically receives a Licence from the original licensor to -copy, distribute, link with or modify the Library subject to these terms and -conditions. You may not impose any further restrictions on the recipients' -exercise of the rights granted herein. You are not responsible for enforcing -compliance by third parties with this Licence. - -11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), conditions -are imposed on you (whether by court order, agreement or otherwise) that -contradict the conditions of this Licence, they do not excuse you from the -conditions of this Licence. If you cannot distribute so as to satisfy -simultaneously your obligations under this -Licence and any other pertinent obligations, then as a consequence you may not -distribute the Library at all. For example, if a patent Licence would not -permit royalty-free redistribution of the Library by all those who receive -copies directly or indirectly through you, then the only way you could satisfy -both it and this Licence would be to refrain entirely from distribution of the -Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, and -the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any patents or -other property right claims or to contest validity of any such claims; this -section has the sole purpose of protecting the integrity of the free software -distribution system which is implemented by public Licence practices. Many -people have made generous contributions to the wide range of software -distributed through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing to -distribute software through any other system and a licensee cannot impose that -choice. - -This section is intended to make thoroughly clear what is believed to be a -consequence of the rest of this Licence. - -12. If the distribution and/or use of the Library is restricted in certain -countries either by patents or by copyrighted interfaces, the original copyright -holder who places the Library under this Licence may add an explicit -geographical distribution limitation excluding those countries, so that -distribution is permitted only in or among countries not thus excluded. In such -case, this Licence incorporates the limitation as if written in the body of this -Licence. - -13. IUPAC and the InChI Trust Limited may publish revised and/or new versions of -the IUPAC/InChI Trust Licence for the International Chemical Identifier (InChI) -Software from time to time. Such new versions will be similar in spirit to the -present version, but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library specifies -a version number of this Licence which applied to it and "any later version", -you have the option of following the terms and conditions either of that version -or of any later version published by IUPAC and the InChI Trust Limited. - -14. If you wish to incorporate parts of the Library into other free programs -whose distribution conditions are incompatible with these, write to the author -to ask for permission. - -15. If you modify the Library in any way whatsoever, the output from any such -modified Library may not be referred to as 'InChI' or any similar name. Any -attempt to refer to such output as 'InChI' will automatically terminate your -rights under this Licence. +9. You are not required to accept this Licence, since you have not signed it. However, nothing else +grants you permission to modify or distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this Licence. Therefore, by modifying or distributing the +Library (or any work based on the Library), you indicate your acceptance of this Licence to do so, and +all its terms and conditions for copying, distributing or modifying the Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the Library), the recipient +automatically receives a Licence from the original licensor to copy, distribute, link with or modify the +Library subject to these terms and conditions. You may not impose any further restrictions on the +recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by +third parties with this Licence. + +11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason +(not limited to patent issues), conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this Licence, they do not excuse you from the conditions of +this Licence. If you cannot distribute so as to satisfy simultaneously your obligations under this +Licence and any other pertinent obligations, then as a consequence you may not distribute the Library +at all. For example, if a patent Licence would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then the only way you could satisfy +both it and this Licence would be to refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the +balance of the section is intended to apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims +or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of +the free software distribution system which is implemented by public Licence practices. Many people +have made generous contributions to the wide range of software distributed through that system in +reliance on consistent application of that system; it is up to the author/donor to decide if he or she is +willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of +this Licence. + +12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by +copyrighted interfaces, the original copyright holder who places the Library under this Licence may +add an explicit geographical distribution limitation excluding those countries, so that distribution is +permitted only in or among countries not thus excluded. In such case, this Licence incorporates the +limitation as if written in the body of this Licence. + +13. IUPAC and the InChI Trust Limited may publish revised and/or new versions of the IUPAC/InChI +Trust Licence for the International Chemical Identifier (InChI) Software from time to time. Such new +versions will be similar in spirit to the present version, but may differ in detail to address new problems +or concerns. + +Each version is given a distinguishing version number. If the Library specifies a version number of +this Licence which applied to it and "any later version", you have the option of following the terms and +conditions either of that version or of any later version published by IUPAC and the InChI Trust +Limited. + +14. If you wish to incorporate parts of the Library into other free programs whose distribution +conditions are incompatible with these, write to the author to ask for permission. + +15. If you modify the Library in any way whatsoever, the output from any such modified Library may +not be referred to as 'InChI' or any similar name. Any attempt to refer to such output as 'InChI' will +automatically terminate your rights under this Licence. NO WARRANTY -15. Because the Library is licensed free of charge, there is no warranty for the -Library, to the extent permitted by applicable law. Except when otherwise stated -in writing the copyright holders and other parties provide the Library "as is" -without warranty of any kind, either expressed or implied, including, but not -limited to, the implied warranties of merchantability and fitness for a -particular purpose. The entire risk as to the quality and performance of the -Library is with you. Should the Library prove defective, you assume the cost of -all necessary servicing, repair or correction. - -16. In no event unless required by applicable law or agreed to in writing will -any copyright holder, or any party who may modify and/or redistribute the -Library as permitted above, be liable to you for damages, including any general, -special, incidental or consequential damages arising out of the use or inability -to use the Library (including but not limited to loss of data or data being -rendered inaccurate or losses sustained by you or third parties or a failure of -the Library to operate with any other software), even if such holder or other -party has been advised of the possibility of such damages. +16. Because the Library is licensed free of charge, there is no warranty for the Library, to the +extent permitted by applicable law. Except when otherwise stated in writing the copyright +holders and other parties provide the Library "as is" without warranty of any kind, either +expressed or implied, including, but not limited to, the implied warranties of merchantability and +fitness for a particular purpose. The entire risk as to the quality and performance of the Library +is with you. Should the Library prove defective, you assume the cost of all necessary servicing, +repair or correction. + +17. In no event unless required by applicable law or agreed to in writing will any copyright +holder, or any party who may modify and/or redistribute the Library as permitted above, be +liable to you for damages, including any general, special, incidental or consequential damages +arising out of the use or inability to use the Library (including but not limited to loss of data or +data being rendered inaccurate or losses sustained by you or third parties or a failure of the +Library to operate with any other software), even if such holder or other party has been advised +of the possibility of such damages. END OF TERMS AND CONDITIONS Instructions for Use -You must attach the following notices to the library at the beginning of each -source file - as a minimum each file needs to contain the "copyright" line and -a link to the full notice. +You must attach the following notices to the library at the beginning of each source file - as a +minimum each file needs to contain the "copyright" line and a link to the full notice. [INSERT YOUR LIBRARY'S NAME AND ITS PURPOSE] -Copyright (C) [YEAR][COPYRIGHT OWNER] -This library is free software; you can redistribute it and/or modify it under -the terms of the IUPAC/InChI Trust InChI Licence 1.0, or any later version. +Copyright (c) [YEAR][COPYRIGHT OWNER] +This library is free software; you can redistribute it and/or modify it under the terms of the +IUPAC/InChI Trust InChI Licence 1.0, or any later version. Please note that this library is distributed WITHOUT ANY WARRANTIES whatsoever, -whether expressed or implied. See the IUPAC/InChI Trust Licence for the -International Chemical Identifier (InChI) Software version 1.04, October 2011 -("IUPAC/InChI-Trust InChI Licence No. 1.0") for more details. +whether expressed or implied. See the IUPAC/InChI Trust Licence for the International +Chemical Identifier (InChI) Software ("IUPAC/InChI-Trust InChI Licence No. 1.0") for +more details. -You should have received a copy of the IUPAC/InChI Trust InChI Licence No. 1.0 -with this library; if not, please write to: +You should have received a copy of the IUPAC/InChI Trust InChI Licence No. 1.0 with this +library; if not, please write to: The InChI Trust -c/o FIZ CHEMIE Berlin -Franklinstrasse 11 -10587 Berlin -GERMANY +8 Cavendish Avenue +Cambridge CB1 7US +UK -or email to: ulrich@inchi-trust.org. +or e-mail to alan@inchi-trust.org -In the event that you require anything else or have any questions, -please write to: +In the event that you require anything else or have any questions, please write to: [INSERT COPYRIGHT OWNERS DETAILS] [INSERT ADDRESS] @@ -316,5 +264,5 @@ please write to: or contact us via email at: [INSERT EMAIL ADDRESS] -(C) 2011 IUPAC and InChI Trust Limited +(c) 2011 IUPAC and InChI Trust Limited diff --git a/INCHI-1-SRC/readme.txt b/INCHI-1-SRC/readme.txt index 3db3b8f..891a0a8 100644 --- a/INCHI-1-SRC/readme.txt +++ b/INCHI-1-SRC/readme.txt @@ -1,16 +1,19 @@ /* * International Chemical Identifier (InChI) * Version 1 - * Software version 1.04 - * September 9, 2011 + * Software version 1.05 + * January 27, 2017 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. * * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 + * International Chemical Identifier (InChI) * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it @@ -18,26 +21,34 @@ * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust - * c/o FIZ CHEMIE Berlin + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. + * or e-mail to alan@inchi-trust.org * */ +This directory contains InChI Software source codes. +It also contains examples of InChI API usage, for C +('inchi_main', 'mol2inchi', 'test_ixa'; see projects +for MS Visual Studio 2008 in 'vc9' and for gcc/Linux +in 'gcc' subdirs) and for Python 3 ('python_sample'). + +Also supplied are InChI API Library source codes and +related projects/makefiles. + +For more details, please refer to Release Notes +(file 'RelNotes.pdf', Section 'Distribution package) +and files 'readme.txt' in sub-directories. The portion of this distribution, the files sha2.c and sha2.h are Copyright (C) Brainspark B.V., and are distributed under @@ -48,13 +59,3 @@ International Chemical Identifier (InChI) Software version 1.0. The text of IUPAC/InChI-Trust Licence for the International Chemical Identifier (InChI) Software version 1.0 is included (the file LICENCE) in this distribution. - - - -========= - LINKS -========= - -IUPAC http://www.iupac.org/inchi -InChI Trust http://www.inchi-trust.org -InChI discussion group https://lists.sourceforge.net/lists/listinfo/inchi-discuss diff --git a/INCHI-1-SRC/readme2.txt b/INCHI-1-SRC/readme2.txt deleted file mode 100644 index 0f9199e..0000000 --- a/INCHI-1-SRC/readme2.txt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * International Chemical Identifier (InChI) - * Version 1 - * Software version 1.04 - * September 9, 2011 - * - * The InChI library and programs are free software developed under the - * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. - * - * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 - * Copyright (C) IUPAC and InChI Trust Limited - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the IUPAC/InChI Trust InChI Licence No.1.0, - * or any later version. - * - * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. - * - * You should have received a copy of the IUPAC/InChI Trust InChI - * Licence No. 1.0 with this library; if not, please write to: - * - * The InChI Trust - * c/o FIZ CHEMIE Berlin - * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. - * - */ - - - -This directory includes the two sub-directories: - -INCHI contains code, VC++ projects and gcc makefiles of - inchi-1 (command-line program). - For the details, see file readme.txt in the sub-directory. - -INCHI_API contains code, VC++ projects and gcc makefiles of - InChI generation library and sample application which - uses the Library. The library produces both standard - and non-standard InChI/InChIKey. For the details, see file - readme.txt in the sub-directory. - - - -========= - LINKS -========= - -IUPAC http://www.iupac.org/inchi -InChI Trust http://www.inchi-trust.org -InChI discussion group https://lists.sourceforge.net/lists/listinfo/inchi-discuss diff --git a/INCHI-1-TEST/readme.txt b/INCHI-1-TEST/readme.txt index 4d57b34..d6fc57c 100644 --- a/INCHI-1-TEST/readme.txt +++ b/INCHI-1-TEST/readme.txt @@ -1,16 +1,19 @@ /* * International Chemical Identifier (InChI) * Version 1 - * Software version 1.04 - * September 9, 2011 + * Software version 1.05 + * January 27, 2017 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. * * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 + * International Chemical Identifier (InChI) * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it @@ -18,54 +21,23 @@ * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust - * c/o FIZ CHEMIE Berlin + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. + * or e-mail to alan@inchi-trust.org * */ +This directory contains a number of sample files and a simple test suite +for InChI version 1, software version 1.05. -This directory contains, in the two sub-directories, a set of test files -representing various chemical structures. - - -========= - FILES -========= - -readme.txt this file - -samples SUB-DIRECTORY - -Sample chemical structure files for testing InChI/InChIKey software. - -test SUB-DIRECTORY - -Test suite including data file (SDF) and reference InChI/InChIKey identifiers -obtained with InChI software v. 1.04 using various command-line options. - - - - - -========= - LINKS -========= - -IUPAC http://www.iupac.org/inchi -InChI Trust http://www.inchi-trust.org -InChI discussion group https://lists.sourceforge.net/lists/listinfo/inchi-discuss +For the details, see 'readme.txt' files in the sub-directories. diff --git a/INCHI-1-TEST/samples/Polymers/pex.sdf b/INCHI-1-TEST/samples/Polymers/pex.sdf new file mode 100644 index 0000000..d1836af --- /dev/null +++ b/INCHI-1-TEST/samples/Polymers/pex.sdf @@ -0,0 +1,3971 @@ + + ACCLDraw09061616042D + + 2 1 0 0 0 0 0 0 0 0999 V2000 + 7.7929 -4.3750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3321 -4.3750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2 1 2 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 2 1 2 +M SDI 1 4 5.1690 -5.1603 5.1690 -3.6645 +M SDI 1 4 8.9719 -3.6645 8.9719 -5.1603 +M SMT 1 n +M END +> +PEX0001 + +$$$$ + + ACCLDraw09061616272D + + 8 8 0 0 0 0 0 0 0 0999 V2000 + 9.9102 -5.8919 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1914 -6.3069 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4726 -8.3819 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4726 -7.5519 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1914 -8.7969 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1914 -7.1369 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.9102 -8.3819 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.9102 -7.5519 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2 1 2 0 0 0 0 + 6 2 1 0 0 0 0 + 3 4 2 0 0 0 0 + 3 5 1 0 0 0 0 + 4 6 1 0 0 0 0 + 5 7 2 0 0 0 0 + 6 8 2 0 0 0 0 + 7 8 1 0 0 0 0 +M STY 1 1 MON +M SLB 1 1 1 +M SAL 1 8 1 2 3 4 5 6 7 8 +M SDI 1 4 7.6903 -9.5792 7.6903 -5.2424 +M SDI 1 4 11.1244 -5.2424 11.1244 -9.5792 +M END +> +PEX0002 + +$$$$ + + ACCLDraw09061619282D + + 14 13 0 0 0 0 0 0 0 0999 V2000 + 15.5483 -4.5318 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.2940 -4.5318 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 14.9212 -5.6181 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.5682 -5.4329 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.8494 -5.0179 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.1306 -5.4329 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7150 -3.5400 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.9962 -3.9550 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2774 -6.0300 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2774 -5.2000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.9962 -6.4450 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.9962 -4.7850 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7150 -6.0300 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7150 -5.2000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 3 1 0 0 0 0 + 2 1 1 0 0 0 0 + 3 2 1 0 0 0 0 + 5 4 2 0 0 0 0 + 6 5 1 0 0 0 0 + 8 7 2 0 0 0 0 + 12 8 1 0 0 0 0 + 9 10 2 0 0 0 0 + 9 11 1 0 0 0 0 + 10 12 1 0 0 0 0 + 11 13 2 0 0 0 0 + 12 14 2 0 0 0 0 + 13 14 1 0 0 0 0 +M STY 4 1 SRU 2 SRU 3 SRU 4 COP +M SLB 4 1 1 2 2 3 3 4 4 +M SST 1 4 RAN +M SCN 4 1 HT 2 HT 3 HT 4 HT +M SAL 1 8 7 8 9 10 11 12 13 14 +M SDI 1 4 4.4951 -7.2272 4.4951 -2.8905 +M SDI 1 4 7.9292 -2.8905 7.9292 -7.2272 +M SMT 1 n +M SAL 2 3 4 5 6 +M SDI 2 4 8.9676 -6.2793 8.9676 -4.2356 +M SDI 2 4 12.7120 -4.2356 12.7120 -6.2793 +M SMT 2 n +M SAL 3 3 1 2 3 +M SDI 3 4 13.6041 -6.4004 13.6041 -3.7495 +M SDI 3 4 16.3306 -3.7495 16.3306 -6.4004 +M SMT 3 n +M SAL 4 14 1 2 3 4 5 6 7 8 9 10 11 12 13 14 +M SDI 4 4 3.9226 -7.9205 3.9226 -2.3181 +M SDI 4 4 17.3252 -2.3181 17.3252 -7.9205 +M END +> +PEX0003 + +$$$$ + + ACCLDraw09061619322D + + 14 13 0 0 0 0 0 0 0 0999 V2000 + 15.5483 -4.5318 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.2940 -4.5318 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 14.9212 -5.6181 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2557 -4.8391 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.5369 -4.4241 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.8181 -4.8391 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.1837 -4.3212 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4649 -4.7362 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.7461 -6.8112 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.7461 -5.9812 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4649 -7.2262 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4649 -5.5662 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.1837 -6.8112 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.1837 -5.9812 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 3 1 0 0 0 0 + 2 1 1 0 0 0 0 + 3 2 1 0 0 0 0 + 5 4 2 0 0 0 0 + 6 5 1 0 0 0 0 + 8 7 2 0 0 0 0 + 12 8 1 0 0 0 0 + 9 10 2 0 0 0 0 + 9 11 1 0 0 0 0 + 10 12 1 0 0 0 0 + 11 13 2 0 0 0 0 + 12 14 2 0 0 0 0 + 13 14 1 0 0 0 0 +M STY 4 1 SRU 2 SRU 3 SRU 4 COP +M SLB 4 1 1 2 2 3 3 4 4 +M SST 1 4 ALT +M SCN 4 1 HT 2 HT 3 HT 4 HT +M SAL 1 8 7 8 9 10 11 12 13 14 +M SDI 1 4 8.9638 -8.0085 8.9638 -3.6718 +M SDI 1 4 12.3980 -3.6718 12.3980 -8.0085 +M SMT 1 n +M SAL 2 3 4 5 6 +M SDI 2 4 4.6551 -5.6855 4.6551 -3.6418 +M SDI 2 4 8.3995 -3.6418 8.3995 -5.6855 +M SMT 2 n +M SAL 3 3 1 2 3 +M SDI 3 4 13.6041 -6.4004 13.6041 -3.7495 +M SDI 3 4 16.3306 -3.7495 16.3306 -6.4004 +M SMT 3 n +M SAL 4 14 1 2 3 4 5 6 7 8 9 10 11 12 13 14 +M SDI 4 4 3.9226 -7.9205 3.9226 -2.3181 +M SDI 4 4 17.3252 -2.3181 17.3252 -7.9205 +M END +> +PEX0004 + +$$$$ + + ACCLDraw09061619362D + + 14 13 0 0 0 0 0 0 0 0999 V2000 + 6.7671 -3.9693 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5128 -3.9693 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.1399 -5.0556 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.2557 -4.9641 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.5369 -4.5491 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8181 -4.9641 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.7150 -3.5087 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.9962 -3.9237 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.2774 -5.9987 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.2774 -5.1687 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.9962 -6.4137 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.9962 -4.7537 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.7150 -5.9987 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.7150 -5.1687 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 3 1 0 0 0 0 + 2 1 1 0 0 0 0 + 3 2 1 0 0 0 0 + 5 4 2 0 0 0 0 + 6 5 1 0 0 0 0 + 8 7 2 0 0 0 0 + 12 8 1 0 0 0 0 + 9 10 2 0 0 0 0 + 9 11 1 0 0 0 0 + 10 12 1 0 0 0 0 + 11 13 2 0 0 0 0 + 12 14 2 0 0 0 0 + 13 14 1 0 0 0 0 +M STY 4 1 MON 2 MON 3 MON 4 COP +M SLB 4 1 1 2 2 3 3 4 4 +M SST 1 4 BLO +M SCN 1 4 HT +M SAL 1 8 7 8 9 10 11 12 13 14 +M SDI 1 4 13.4951 -7.1960 13.4951 -2.8593 +M SDI 1 4 16.9292 -2.8593 16.9292 -7.1960 +M SAL 2 3 4 5 6 +M SDI 2 4 8.6551 -5.8105 8.6551 -3.7668 +M SDI 2 4 12.3995 -3.7668 12.3995 -5.8105 +M SAL 3 3 1 2 3 +M SDI 3 4 4.8229 -5.8379 4.8229 -3.1870 +M SDI 3 4 7.5494 -3.1870 7.5494 -5.8379 +M SAL 4 14 1 2 3 4 5 6 7 8 9 10 11 12 13 14 +M SDI 4 4 4.2504 -7.8893 4.2504 -2.2868 +M SDI 4 4 18.4325 -2.2868 18.4325 -7.8893 +M END +> +PEX0005 + +$$$$ + + ACCLDraw09061619432D + + 14 13 0 0 0 0 0 0 0 0999 V2000 + 17.5483 -6.2506 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 16.2940 -6.2506 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 16.9212 -7.3368 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.7245 -6.9329 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.0057 -6.5179 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.2869 -6.9329 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8712 -5.0400 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.1524 -5.4550 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4336 -7.5300 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4336 -6.7000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.1524 -7.9450 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.1524 -6.2850 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8712 -7.5300 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8712 -6.7000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 3 1 0 0 0 0 + 2 1 1 0 0 0 0 + 3 2 1 0 0 0 0 + 5 4 2 0 0 0 0 + 6 5 1 0 0 0 0 + 8 7 2 0 0 0 0 + 12 8 1 0 0 0 0 + 9 10 2 0 0 0 0 + 9 11 1 0 0 0 0 + 10 12 1 0 0 0 0 + 11 13 2 0 0 0 0 + 12 14 2 0 0 0 0 + 13 14 1 0 0 0 0 +M STY 5 1 SRU 2 SRU 3 SRU 4 COP 5 COP +M SLB 5 1 1 2 2 3 3 4 4 5 5 +M SST 2 4 RAN 5 BLO +M SCN 5 1 HT 2 HT 3 HT 4 HT 5 HT +M SAL 1 8 7 8 9 10 11 12 13 14 +M SDI 1 4 4.6513 -8.7272 4.6513 -4.3905 +M SDI 1 4 8.0855 -4.3905 8.0855 -8.7272 +M SMT 1 n +M SAL 2 3 4 5 6 +M SDI 2 4 9.1238 -7.7793 9.1238 -5.7356 +M SDI 2 4 12.8682 -5.7356 12.8682 -7.7793 +M SMT 2 n +M SAL 3 3 1 2 3 +M SDI 3 4 15.6041 -8.1191 15.6041 -5.4683 +M SDI 3 4 18.3306 -5.4683 18.3306 -8.1191 +M SMT 3 n +M SAL 4 11 4 5 6 7 8 9 10 11 12 13 14 +M SDI 4 4 4.0789 -9.4205 4.0789 -3.8181 +M SDI 4 4 13.8628 -3.8181 13.8628 -9.4205 +M SAL 5 14 1 2 3 4 5 6 7 8 9 10 11 12 13 14 +M SDI 5 4 3.5065 -10.1138 3.5065 -3.2456 +M SDI 5 4 19.3252 -3.2456 19.3252 -10.1138 +M END +> +PEX0006 + +$$$$ + + ACCLDraw09061615372D + + 20 20 0 0 0 0 0 0 0 0999 V2000 + 11.4925 -9.2093 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 11.4925 -5.8893 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7737 -7.9643 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7737 -7.1343 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.4925 -8.3793 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.4925 -6.7193 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.2114 -7.9643 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.2114 -7.1343 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.9736 -9.5942 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.4112 -9.5943 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.6925 -9.1793 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.4114 -5.4442 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 7.9737 -5.4443 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.6925 -5.8593 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.9737 -7.9343 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.9737 -7.1043 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.6925 -8.3493 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.6925 -6.6893 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.4114 -7.9343 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.4114 -7.1043 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5 1 1 0 0 0 0 + 6 2 1 0 0 0 0 + 3 4 2 0 0 0 0 + 3 5 1 0 0 0 0 + 4 6 1 0 0 0 0 + 5 7 2 0 0 0 0 + 6 8 2 0 0 0 0 + 7 8 1 0 0 0 0 + 17 11 1 0 0 0 0 + 9 11 1 0 0 0 0 + 10 11 2 0 0 0 0 + 18 14 1 0 0 0 0 + 12 14 1 0 0 0 0 + 13 14 2 0 0 0 0 + 15 16 2 0 0 0 0 + 15 17 1 0 0 0 0 + 16 18 1 0 0 0 0 + 17 19 2 0 0 0 0 + 18 20 2 0 0 0 0 + 19 20 1 0 0 0 0 +M STY 1 1 MON +M SLB 1 1 1 +M SAL 1 15 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +M SAL 1 5 16 17 18 19 20 +M SDI 1 4 6.9558 -10.3048 6.9558 -4.7337 +M SDI 1 4 12.9936 -4.7337 12.9936 -10.3048 +M END +> +PEX0007 + +$$$$ + + ACCLDraw09061615412D + + 20 20 0 0 0 0 0 0 0 0999 V2000 + 12.2114 -7.9643 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.2114 -7.1343 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.4925 -6.7193 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.4925 -8.3793 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7737 -7.1343 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7737 -7.9643 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.4925 -5.8893 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 11.4925 -9.2093 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0364 -7.9655 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0364 -7.1355 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3175 -6.7205 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3175 -8.3805 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5987 -7.1355 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5987 -7.9655 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5987 -5.4755 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3175 -5.8905 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0364 -5.4755 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0362 -9.6255 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3175 -9.2105 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5986 -9.6255 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 2 0 0 0 0 + 1 4 2 0 0 0 0 + 3 5 1 0 0 0 0 + 4 6 1 0 0 0 0 + 6 5 2 0 0 0 0 + 3 7 1 0 0 0 0 + 4 8 1 0 0 0 0 + 9 10 1 0 0 0 0 + 10 11 2 0 0 0 0 + 9 12 2 0 0 0 0 + 11 13 1 0 0 0 0 + 12 14 1 0 0 0 0 + 14 13 2 0 0 0 0 + 15 16 2 0 0 0 0 + 16 17 1 0 0 0 0 + 11 16 1 0 0 0 0 + 18 19 2 0 0 0 0 + 19 20 1 0 0 0 0 + 12 19 1 0 0 0 0 +M STY 3 1 MER 2 MER 3 MON +M SLB 3 1 1 2 2 3 3 +M SAL 1 8 1 2 3 4 5 6 7 8 +M SDI 1 4 9.9914 -9.9198 9.9914 -5.1787 +M SDI 1 4 12.9936 -5.1787 12.9936 -9.9198 +M SAL 2 12 9 10 11 12 13 14 15 16 17 18 19 20 +M SDI 2 4 4.5808 -10.3361 4.5808 -4.7650 +M SDI 2 4 8.0479 -4.7650 8.0479 -10.3361 +M SAL 3 15 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +M SAL 3 5 16 17 18 19 20 +M SDI 3 4 4.0084 -11.0293 4.0084 -4.1925 +M SDI 3 4 14.4151 -4.1925 14.4151 -11.0293 +M END +> +PEX0008 + +$$$$ + + ACCLDraw09071612122D + + 5 3 0 0 0 0 0 0 0 0999 V2000 + 12.1468 -6.8416 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.7567 -6.1660 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.9268 -6.8416 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2168 -7.0799 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.7967 -6.3522 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3 1 1 0 0 0 0 + 2 1 2 0 0 0 0 + 5 4 2 0 0 0 0 +M STY 3 1 MON 2 MOD 3 COP +M SLB 3 1 1 2 2 3 3 +M SCN 2 2 HT 3 HT +M SAL 1 2 4 5 +M SDI 1 4 7.6336 -7.8652 7.6336 -5.6417 +M SDI 1 4 10.3958 -5.6417 10.3958 -7.8652 +M SAL 2 3 1 2 3 +M SDI 2 4 10.6544 -7.6239 10.6544 -5.4204 +M SDI 2 4 13.6935 -5.4204 13.6935 -7.6239 +M SAL 3 5 1 2 3 4 5 +M SDI 3 4 7.0611 -8.5585 7.0611 -4.8480 +M SDI 3 4 15.1967 -4.8480 15.1967 -8.5585 +M END +> +PEX0009 + +$$$$ + + ACCLDraw09071614262D + + 18 18 0 0 0 0 0 0 0 0999 V2000 + 11.6468 -9.4700 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.3656 -9.0550 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.0844 -5.3200 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.3656 -5.7350 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.6468 -7.8100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.6468 -6.9800 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.3656 -8.2250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.3656 -6.5650 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.0845 -7.8100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.0845 -6.9800 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.2344 -5.3200 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5156 -5.7350 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7968 -7.8100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7968 -6.9800 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5156 -8.2250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5156 -6.5650 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.2345 -7.8100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.2345 -6.9800 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2 1 2 0 0 0 0 + 7 2 1 0 0 0 0 + 4 3 2 0 0 0 0 + 8 4 1 0 0 0 0 + 5 6 2 0 0 0 0 + 5 7 1 0 0 0 0 + 6 8 1 0 0 0 0 + 7 9 2 0 0 0 0 + 8 10 2 0 0 0 0 + 9 10 1 0 0 0 0 + 12 11 2 0 0 0 0 + 16 12 1 0 0 0 0 + 13 14 2 0 0 0 0 + 13 15 1 0 0 0 0 + 14 16 1 0 0 0 0 + 15 17 2 0 0 0 0 + 16 18 2 0 0 0 0 + 17 18 1 0 0 0 0 +M STY 3 1 MON 2 CRO 3 COP +M SLB 3 1 1 2 2 3 3 +M SCN 2 2 HT 3 HT +M SAL 1 8 11 12 13 14 15 16 17 18 +M SDI 1 4 6.0145 -9.0073 6.0145 -4.6706 +M SDI 1 4 9.4487 -4.6706 9.4487 -9.0073 +M SAL 2 10 1 2 3 4 5 6 7 8 9 10 +M SDI 2 4 10.4484 -10.1942 10.4484 -4.6706 +M SDI 2 4 14.2987 -4.6706 14.2987 -10.1942 +M SAL 3 15 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +M SAL 3 3 16 17 18 +M SDI 3 4 5.4421 -10.8875 5.4421 -4.0981 +M SDI 3 4 15.3541 -4.0981 15.3541 -10.8875 +M END +> +PEX0010 + +$$$$ + + ACCLDraw08021617232D + + 12 11 0 0 0 0 0 0 0 0999 V2000 + 4.1563 -20.5000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.2813 -21.4375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.1164 -20.6023 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3125 -21.5313 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.4459 -23.9720 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.4459 -25.1531 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4688 -25.7436 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4916 -25.1531 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4916 -23.9720 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4688 -23.3814 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5144 -25.7436 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.6793 -26.5788 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 2 0 0 0 0 + 2 3 1 0 0 0 0 + 3 4 2 0 0 0 0 + 9 10 1 0 0 0 0 + 8 9 2 0 0 0 0 + 7 8 1 0 0 0 0 + 6 7 2 0 0 0 0 + 5 6 1 0 0 0 0 + 5 10 2 0 0 0 0 + 8 11 1 0 0 0 0 + 11 12 2 0 0 0 0 +M STY 3 1 MON 2 MON 3 COP +M SLB 3 1 1 2 2 3 3 +M SST 1 3 BLO +M SCN 1 3 HT +M SAL 1 4 1 2 3 4 +M SDI 1 4 3.3740 -22.3135 3.3740 -19.7177 +M SDI 1 4 8.0948 -19.7177 8.0948 -22.3135 +M SAL 2 8 5 6 7 8 9 10 11 12 +M SDI 2 4 3.6636 -27.3611 3.6636 -22.5991 +M SDI 2 4 8.2967 -22.5991 8.2967 -27.3611 +M SAL 3 12 1 2 3 4 5 6 7 8 9 10 11 12 +M SDI 3 4 2.8015 -28.0543 2.8015 -19.1453 +M SDI 3 4 9.8000 -19.1453 9.8000 -28.0543 +M END +> +PEX0011 + +$$$$ + + ACCLDraw07241614572D + + 12 11 0 0 0 0 0 0 0 0999 V2000 + 8.4509 -13.2223 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4509 -14.6223 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.6633 -15.3223 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.8758 -14.6223 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.8758 -13.2223 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.6633 -12.5223 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.6633 -11.1223 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.8588 -10.4319 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.4809 -12.8510 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.6933 -13.5510 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.9058 -12.8510 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 16.1182 -13.5510 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 2 0 0 0 0 + 2 3 1 0 0 0 0 + 3 4 2 0 0 0 0 + 4 5 1 0 0 0 0 + 5 6 2 0 0 0 0 + 1 6 1 0 0 0 0 + 6 7 1 0 0 0 0 + 7 8 2 0 0 0 0 + 9 10 2 0 0 0 0 + 10 11 1 0 0 0 0 + 11 12 2 0 0 0 0 +M STY 3 1 MON 2 MON 3 COP +M SLB 3 1 1 2 2 3 3 +M SST 1 3 BLO +M SCN 1 3 HH +M SAL 1 4 9 10 11 12 +M SDI 1 4 11.6986 -14.3333 11.6986 -12.0687 +M SDI 1 4 16.9005 -12.0687 16.9005 -14.3333 +M SAL 2 8 1 2 3 4 5 6 7 8 +M SDI 2 4 7.1999 -16.2608 7.1999 -9.8059 +M SDI 2 4 11.1893 -9.8059 11.1893 -16.2608 +M SAL 3 12 1 2 3 4 5 6 7 8 9 10 11 12 +M SDI 3 4 6.6274 -16.9541 6.6274 -9.2334 +M SDI 3 4 18.4037 -9.2334 18.4037 -16.9541 +M END +> +PEX0012 + +$$$$ + + ACCLDraw09061615162D + + 12 11 0 0 0 0 0 0 0 0999 V2000 + 8.3420 -15.8161 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.3420 -17.2161 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.5544 -17.9161 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7669 -17.2161 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7669 -15.8161 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.5544 -15.1161 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.5544 -13.7161 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7499 -13.0257 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.3720 -15.4448 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.5844 -16.1448 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.7969 -15.4448 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 16.0093 -16.1448 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 2 0 0 0 0 + 2 3 1 0 0 0 0 + 3 4 2 0 0 0 0 + 4 5 1 0 0 0 0 + 5 6 2 0 0 0 0 + 1 6 1 0 0 0 0 + 6 7 1 0 0 0 0 + 7 8 2 0 0 0 0 + 9 10 2 0 0 0 0 + 10 11 1 0 0 0 0 + 11 12 2 0 0 0 0 +M END +> +PEX0013 (not a polymer) + +$$$$ + + + + 12 11 0 0 0 0 0 0 0 0999 V2000 + 10.2200 -14.2100 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 11.4100 -14.9100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.6700 -14.2100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.8600 -14.9100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 19.1800 -14.7000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 19.1800 -16.1000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 20.3924 -16.8000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 21.6049 -16.1000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 21.6049 -14.7000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 20.3924 -14.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 20.3924 -12.6002 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 21.5880 -11.9098 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 3 0 0 0 + 2 3 1 0 0 0 + 3 4 2 0 0 0 + 5 6 2 0 0 0 + 6 7 1 0 0 0 + 7 8 2 0 0 0 + 8 9 1 0 0 0 + 9 10 2 0 0 0 + 5 10 1 0 0 0 + 10 11 1 0 0 0 + 11 12 2 0 0 0 +M STY 2 1 SRU 2 SRU +M SLB 2 1 1 2 2 +M SCN 2 1 HT 2 HT +M SAL 1 4 1 2 3 4 +M SDI 1 4 9.5900 -16.0300 9.5900 -13.5100 +M SDI 1 4 15.6100 -13.5100 15.6100 -16.0300 +M SMT 1 n +M SAL 2 8 5 6 7 8 9 10 11 12 +M SDI 2 4 18.3400 -16.7300 18.3400 -10.7800 +M SDI 2 4 23.1700 -10.7800 23.1700 -16.7300 +M SMT 2 n +M END +> +PEX0014 (CHEBI_82568) + +$$$$ + + ACCLDraw09061615252D + + 12 11 0 0 0 0 0 0 0 0999 V2000 + 6.3789 -9.7213 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5689 -10.4213 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.8289 -9.7213 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.0189 -10.4213 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.3389 -10.2113 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.3389 -11.6113 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 16.5513 -12.3113 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 17.7638 -11.6113 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 17.7638 -10.2113 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 16.5513 -9.5113 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 16.5513 -8.1115 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 17.7469 -7.4211 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 3 0 0 0 0 + 2 3 1 0 0 0 0 + 3 4 2 0 0 0 0 + 5 6 2 0 0 0 0 + 6 7 1 0 0 0 0 + 7 8 2 0 0 0 0 + 8 9 1 0 0 0 0 + 9 10 2 0 0 0 0 + 5 10 1 0 0 0 0 + 10 11 1 0 0 0 0 + 11 12 2 0 0 0 0 +M STY 3 1 MON 2 MON 3 COP +M SLB 3 1 1 2 2 3 3 +M SST 1 3 BLO +M SCN 1 3 HT +M SAL 1 4 1 2 3 4 +M SDI 1 4 5.7489 -11.5413 5.7489 -9.0213 +M SDI 1 4 11.7689 -9.0213 11.7689 -11.5413 +M SAL 2 8 5 6 7 8 9 10 11 12 +M SDI 2 4 14.4989 -12.2413 14.4989 -6.2913 +M SDI 2 4 19.3289 -6.2913 19.3289 -12.2413 +M SAL 3 12 1 2 3 4 5 6 7 8 9 10 11 12 +M SDI 3 4 5.1765 -13.0936 5.1765 -5.7189 +M SDI 3 4 20.3235 -5.7189 20.3235 -13.0936 +M END +> +PEX0015 + +$$$$ + + ACCLDraw09061615272D + + 12 11 0 0 0 0 0 0 0 0999 V2000 + 6.3789 -9.7213 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5689 -10.4213 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.8289 -9.7213 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.0189 -10.4213 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.3389 -10.2113 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.3389 -11.6113 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 16.5513 -12.3113 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 17.7638 -11.6113 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 17.7638 -10.2113 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 16.5513 -9.5113 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 16.5513 -8.1115 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 17.7469 -7.4211 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 3 0 0 0 0 + 2 3 1 0 0 0 0 + 3 4 2 0 0 0 0 + 5 6 2 0 0 0 0 + 6 7 1 0 0 0 0 + 7 8 2 0 0 0 0 + 8 9 1 0 0 0 0 + 9 10 2 0 0 0 0 + 5 10 1 0 0 0 0 + 10 11 1 0 0 0 0 + 11 12 2 0 0 0 0 +M STY 3 1 MON 2 MON 3 COP +M SLB 3 1 1 2 2 3 3 +M SST 1 3 ALT +M SCN 1 3 HT +M SAL 1 4 1 2 3 4 +M SDI 1 4 5.7489 -11.5413 5.7489 -9.0213 +M SDI 1 4 11.7689 -9.0213 11.7689 -11.5413 +M SAL 2 8 5 6 7 8 9 10 11 12 +M SDI 2 4 14.4989 -12.2413 14.4989 -6.2913 +M SDI 2 4 19.3289 -6.2913 19.3289 -12.2413 +M SAL 3 12 1 2 3 4 5 6 7 8 9 10 11 12 +M SDI 3 4 5.1765 -13.0936 5.1765 -5.7189 +M SDI 3 4 20.3235 -5.7189 20.3235 -13.0936 +M END +> +PEX0016 + +$$$$ + + ACCLDraw09071615532D + + 12 12 0 0 0 0 0 0 0 0999 V2000 + 8.4975 -5.1883 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6222 -5.1883 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6222 -4.3583 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3410 -5.6033 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3410 -3.9433 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0599 -5.1883 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0599 -4.3583 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6539 -3.9433 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.9351 -4.3583 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2163 -3.9433 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4975 -4.3583 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 7.7787 -3.9433 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 11 1 1 0 0 0 0 + 12 7 1 0 0 0 0 + 2 3 2 0 0 0 0 + 2 4 1 0 0 0 0 + 3 5 1 0 0 0 0 + 4 6 2 0 0 0 0 + 5 7 2 0 0 0 0 + 6 7 1 0 0 0 0 + 8 9 1 0 0 0 0 + 9 10 1 0 0 0 0 + 10 11 1 0 0 0 0 + 11 12 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 4 11 12 10 1 +M SBL 1 2 2 10 +M SDI 1 4 7.4193 -4.5658 7.4193 -3.7358 +M SDI 1 4 9.5757 -3.7358 9.5757 -4.5658 +M SMT 1 n +M END +> +PEX0017 + +$$$$ + + ISISHOST03240423132D 1 1.00000 0.00000 4921 + + 18 18 0 0 0 999 V2000 + -1.6241 0.1138 0.0000 C 0 0 0 0 0 0 0 0 0 + -2.3345 -0.2966 0.0000 C 0 0 0 0 0 0 0 0 0 + -1.6241 0.9414 0.0000 C 0 0 0 0 0 0 0 0 0 + -0.8621 -0.3207 0.0000 C 0 0 0 0 0 0 0 0 0 + -3.0517 0.1138 0.0000 C 0 0 0 0 0 0 0 0 0 + -2.3345 -1.1241 0.0000 O 0 0 0 0 0 0 0 0 0 + -2.3345 1.3517 0.0000 C 0 0 0 0 0 0 0 0 0 + -0.1034 0.1241 0.0000 C 0 0 0 0 0 0 0 0 0 + -3.0517 0.9414 0.0000 C 0 0 0 0 0 0 0 0 0 + 0.6552 -0.3103 0.0000 C 0 0 0 0 0 0 0 0 0 + -3.7655 1.3517 0.0000 O 0 0 0 0 0 0 0 0 0 + 1.4138 0.1310 0.0000 C 0 0 0 0 0 0 0 0 0 + 0.6621 -1.1897 0.0000 C 0 0 0 0 0 0 0 0 0 + 2.2379 -0.3034 0.0000 C 0 0 0 0 0 0 0 0 0 + 2.9931 0.1379 0.0000 C 0 0 0 0 0 0 0 0 0 + 3.7552 -0.2966 0.0000 C 0 0 0 0 0 0 0 0 0 + 4.5138 0.1483 0.0000 C 0 0 0 0 0 0 0 0 0 + 3.7586 -1.1724 0.0000 C 0 0 0 0 0 0 0 0 0 + 1 2 2 0 0 0 + 1 3 1 0 0 0 + 1 4 1 0 0 0 + 2 5 1 0 0 0 + 2 6 1 0 0 0 + 3 7 2 0 0 0 + 4 8 1 0 0 0 + 5 9 2 0 0 0 + 8 10 2 0 0 0 + 9 11 1 0 0 0 + 10 12 1 0 0 0 + 10 13 1 0 0 0 + 12 14 1 0 0 0 + 14 15 1 0 0 0 + 15 16 2 0 0 0 + 16 17 1 0 0 0 + 16 18 1 0 0 0 + 7 9 1 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 5 4 8 10 12 13 +M SBL 1 2 3 13 +M SDI 1 4 -1.2517 -0.5103 -1.2517 0.3172 +M SDI 1 4 1.8000 0.3379 1.8000 -0.4897 +M END +> +CHEBI_2063 + +> +5-Hydroxy-2-polyprenylphenol + +> +PEX0018 + +$$$$ + + Marvin 02011010352D + + 30 30 0 0 1 0 999 V2000 + 11.5088 -12.6225 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 12.2232 -12.2100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.9377 -12.6225 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.6522 -12.2100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.3666 -12.6225 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 12.9377 -13.4475 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 15.1916 -12.6225 0.0000 P 0 0 0 0 0 0 0 0 0 0 0 0 + 15.1916 -13.4475 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 15.1916 -11.7975 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 16.0166 -12.6225 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 16.7195 -12.2100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 17.4340 -12.6225 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 18.1484 -12.2100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 18.8629 -12.6225 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 17.4340 -13.4475 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 19.6879 -12.6225 0.0000 P 0 0 0 0 0 0 0 0 0 0 0 0 + 19.6879 -13.4475 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 20.5129 -12.6225 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 19.6879 -11.7975 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 17.4340 -14.2725 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 16.7306 -14.6787 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 16.7308 -15.5037 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 17.4453 -15.9161 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 18.1486 -15.5099 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 18.1485 -14.6849 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 16.0163 -15.9162 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 18.8631 -15.9224 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 15.3019 -15.5037 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 17.4453 -16.7411 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 18.8630 -14.2724 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 3 4 1 0 0 0 0 + 4 5 1 0 0 0 0 + 3 6 1 0 0 0 0 + 5 7 1 0 0 0 0 + 7 8 2 0 0 0 0 + 7 9 1 0 0 0 0 + 7 10 1 0 0 0 0 + 10 11 1 0 0 0 0 + 11 12 1 0 0 0 0 + 12 13 1 0 0 0 0 + 13 14 1 0 0 0 0 + 12 15 1 0 0 0 0 + 14 16 1 0 0 0 0 + 16 17 2 0 0 0 0 + 16 18 1 0 0 0 0 + 16 19 1 0 0 0 0 + 20 15 1 6 0 0 0 + 20 21 1 0 0 0 0 + 21 22 1 0 0 0 0 + 22 23 1 0 0 0 0 + 23 24 1 0 0 0 0 + 24 25 1 0 0 0 0 + 20 25 1 0 0 0 0 + 22 26 1 1 0 0 0 + 24 27 1 1 0 0 0 + 26 28 1 0 0 0 0 + 23 29 1 6 0 0 0 + 25 30 1 6 0 0 0 +M STY 1 1 SRU +M SCN 1 1 HT +M SAL 1 15 10 11 12 13 14 15 16 17 19 20 21 22 23 24 25 +M SAL 1 5 26 27 28 29 30 +M SDI 1 4 20.0276 -12.2100 20.0276 -13.0350 +M SDI 1 4 15.6769 -13.0350 15.6769 -12.2100 +M SBL 1 2 9 17 +M SMT 1 n +M END +> +CHEBI_27602 + +> +O-alpha-D-glucosyl poly(glycerol phosphate) macromolecule + +> +PEX0019 + +$$$$ + + ACCLDraw09071616182D + + 30 30 0 0 1 0 0 0 0 0999 V2000 + 15.6022 -15.6906 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9624 -13.6282 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.6769 -14.0407 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 10.3914 -13.6282 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.1058 -14.0407 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.6769 -14.8657 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 11.9308 -14.0407 0.0000 P 0 0 3 0 0 0 0 0 0 0 0 0 + 11.9308 -14.8657 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 11.9308 -13.2157 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 12.7558 -14.0407 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 13.4587 -13.6282 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.1732 -14.0407 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 14.8876 -13.6282 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.6021 -14.0407 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 14.1732 -14.8657 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 16.4271 -14.0407 0.0000 P 0 0 3 0 0 0 0 0 0 0 0 0 + 16.4271 -14.8657 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 17.2521 -14.0407 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 16.4271 -13.2157 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 14.1732 -15.6907 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 13.4700 -16.0969 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 13.4700 -16.9219 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 14.1845 -17.3343 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 14.8878 -16.9281 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 14.8878 -16.1031 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 12.7555 -17.3344 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.6023 -17.3406 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 12.0411 -16.9219 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 14.1845 -18.1593 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.2480 -14.0407 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 30 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 3 4 1 0 0 0 0 + 4 5 1 0 0 0 0 + 3 6 1 0 0 0 0 + 5 7 1 0 0 0 0 + 7 8 2 0 0 0 0 + 7 9 1 0 0 0 0 + 7 10 1 0 0 0 0 + 10 11 1 0 0 0 0 + 11 12 1 0 0 0 0 + 12 13 1 0 0 0 0 + 13 14 1 0 0 0 0 + 12 15 1 0 0 0 0 + 14 16 1 0 0 0 0 + 16 17 2 0 0 0 0 + 16 18 1 0 0 0 0 + 16 19 1 0 0 0 0 + 20 15 1 6 0 0 0 + 20 21 1 0 0 0 0 + 21 22 1 0 0 0 0 + 22 23 1 0 0 0 0 + 23 24 1 0 0 0 0 + 24 25 1 0 0 0 0 + 20 25 1 0 0 0 0 + 22 26 1 1 0 0 0 + 24 27 1 1 0 0 0 + 26 28 1 0 0 0 0 + 23 29 1 6 0 0 0 + 25 1 1 6 0 0 0 +M END +> +PEX0020 + +$$$$ + + Mrv0541 03311412142D + + 3 2 0 0 0 0 999 V2000 + 0.8250 0.0000 0.0000 S 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 S 0 0 0 0 0 0 0 0 0 0 0 0 + -0.8250 0.0000 0.0000 S 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 2 0 0 0 0 + 2 3 2 0 0 0 0 +M STY 1 1 SRU +M SCN 1 1 HT +M SAL 1 1 2 +M SDI 1 4 -0.3397 -0.4950 -0.3397 0.4950 +M SDI 1 4 0.3397 0.4950 0.3397 -0.4950 +M SBL 1 2 1 2 +M SMT 1 n +M END +> +CHEBI_17909 + +> +polysulfur + +> +PEX0021 + +$$$$ + + Marvin 02191015002D + + 41 43 0 0 0 0 999 V2000 + -0.7145 0.4125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.7145 1.2375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + -1.4290 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 1.6500 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + -1.4290 1.6500 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7144 0.4125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -2.1434 0.4125 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + -1.4290 -0.8250 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7144 1.2375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -2.1434 1.2375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -2.8579 1.6500 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 2.1434 0.4125 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8579 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8579 -0.8250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5723 0.4125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5723 -1.2375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2868 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2868 -0.8250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0013 -1.2375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.0013 -2.0625 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 1.4289 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.0026 -0.8250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7170 -1.2375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.4315 -0.8250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.1460 -1.2375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.1460 -2.0625 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 12.8605 -0.8250 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 5.7157 -0.8250 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4302 -1.2375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1447 -0.8250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8592 -1.2375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4302 -2.0625 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.7157 -2.4750 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1447 -2.4750 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5736 -0.8250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5736 0.0000 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2881 0.4125 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 10.0026 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7170 0.4125 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2881 -1.2375 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 2 0 0 0 0 + 1 3 1 0 0 0 0 + 1 4 1 0 0 0 0 + 2 5 1 0 0 0 0 + 2 6 1 0 0 0 0 + 3 7 1 0 0 0 0 + 4 8 1 0 0 0 0 + 4 9 2 0 0 0 0 + 5 10 1 0 0 0 0 + 6 11 2 0 0 0 0 + 7 22 1 0 0 0 0 + 7 10 1 0 0 0 0 + 8 11 1 0 0 0 0 + 11 12 1 0 0 0 0 + 13 14 1 0 0 0 0 + 14 15 2 0 0 0 0 + 14 16 1 0 0 0 0 + 15 17 1 0 0 0 0 + 16 18 2 0 0 0 0 + 17 19 2 0 0 0 0 + 18 19 1 0 0 0 0 + 19 20 1 0 0 0 0 + 20 29 1 0 0 0 0 + 20 21 2 0 0 0 0 + 22 13 1 0 0 0 0 + 23 39 1 0 0 0 0 + 23 24 1 0 0 0 0 + 23 41 1 0 0 0 0 + 24 25 1 0 0 0 0 + 25 26 1 0 0 0 0 + 26 27 2 0 0 0 0 + 26 28 1 0 0 0 0 + 29 30 1 0 0 0 0 + 30 33 1 0 0 0 0 + 30 31 1 0 0 0 0 + 31 32 1 0 0 0 0 + 32 36 1 0 0 0 0 + 36 41 1 0 0 0 0 + 33 34 2 0 0 0 0 + 33 35 1 0 0 0 0 + 36 37 2 0 0 0 0 + 39 38 2 0 0 0 0 + 39 40 1 0 0 0 0 +M STY 1 1 SRU +M SAL 1 9 29 30 31 32 33 34 35 36 37 +M SDI 1 4 9.0740 -0.6376 8.6616 -1.3521 +M SDI 1 4 5.6278 -1.3521 5.2153 -0.6376 +M SBL 1 2 23 38 +M SMT 1 n +M END +> +CHEBI_28624 + +> +tetrahydrofolyl-poly(glutamic acid) macromolecule + +> +PEX0022 + +$$$$ + + Marvin 10080913492D + + 9 8 0 0 0 0 999 V2000 + 2.2830 -1.4139 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9976 -1.0014 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.5686 -1.0014 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2.2830 -2.2389 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0581 -1.0125 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 0.8541 -1.4139 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.1397 -1.0014 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.5748 -1.4139 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 0.1397 -0.1764 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 1 3 1 0 0 0 0 + 1 4 1 0 0 0 0 + 2 5 1 0 0 0 0 + 3 6 1 0 0 0 0 + 6 7 1 0 0 0 0 + 7 8 1 0 0 0 0 + 7 9 1 0 0 0 0 +M STY 1 1 SRU +M SCN 1 1 HT +M SAL 1 4 1 2 3 4 +M SDI 1 4 1.4806 -1.5285 1.0682 -0.8140 +M SDI 1 4 3.4386 -0.5935 3.4299 -1.4184 +M SBL 1 2 4 5 +M SMT 1 n +M END +> +CHEBI_53262 + +> +poly(propylene glycol) macromolecule + +> +PEX0023 + +$$$$ + + ACCLDraw03071616002D + + 6 5 0 0 0 0 0 0 0 0999 V2000 + 14.9421 -3.4500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 16.0379 -3.1208 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 16.6087 -3.4500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 17.1796 -3.1208 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 18.1296 -3.4500 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 19.1021 -3.0585 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 3 4 1 0 0 0 0 + 4 5 1 0 0 0 0 + 5 6 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 3 2 4 3 +M SBL 1 2 1 4 +M SDI 1 4 15.4504 -3.6958 15.4504 -2.8708 +M SDI 1 4 17.5671 -2.8708 17.5671 -3.6958 +M SMT 1 n +M END +> +PEX0024 + +$$$$ + + ACCLDraw03071616132D + + 7 6 0 0 0 0 0 0 0 0999 V2000 + 4.1643 -3.8238 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1018 -3.4946 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6726 -3.8196 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2435 -3.4905 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1018 -3.8196 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5226 -3.4321 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7435 -3.4946 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 3 4 1 0 0 0 0 + 4 5 1 0 0 0 0 + 1 6 1 0 0 0 0 + 5 7 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 3 2 4 3 +M SBL 1 2 1 4 +M SDI 1 4 4.5935 -3.9863 4.5935 -3.3280 +M SDI 1 4 6.5518 -3.3238 6.5518 -3.9863 +M SMT 1 n +M END +> +PEX0025 + +$$$$ + + Marvin 11300909372D + + 8 7 0 0 0 0 999 V2000 + 11.6428 -6.9333 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.3573 -6.5208 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.6436 -6.4983 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 12.3573 -5.6958 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 13.0718 -5.2833 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.0718 -4.4583 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 13.7862 -5.6958 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.5805 -6.9185 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 4 1 0 0 0 0 + 4 5 1 0 0 0 0 + 5 6 2 0 0 0 0 + 5 7 1 0 0 0 0 + 1 8 1 0 0 0 0 +M STY 1 1 SRU +M SCN 1 1 HT +M SAL 1 6 1 2 4 5 6 7 +M SDI 1 4 12.8797 -6.0991 12.8942 -6.9240 +M SDI 1 4 11.1997 -7.3397 11.2111 -6.5147 +M SBL 1 2 7 2 +M SMT 1 n +M END +> +CHEBI_32028 + +> +poly(vinyl acetate) + +> +PEX0026 + +$$$$ + + Marvin 11270815502D + + 7 6 0 0 0 0 999 V2000 + 0.0817 -0.3536 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7962 -0.7661 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.6328 0.8839 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0817 0.4714 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.2252 0.0589 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + -1.3473 -1.1786 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7962 0.8839 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 1 4 1 0 0 0 0 + 1 2 1 0 0 0 0 + 3 4 2 0 0 0 0 + 4 7 1 0 0 0 0 + 2 5 1 0 0 0 0 + 6 1 1 0 0 0 0 +M STY 1 1 SRU +M SCN 1 1 HT +M SAL 1 5 1 2 3 4 7 +M SDI 1 4 1.1784 -0.0692 1.5909 -0.7836 +M SDI 1 4 -0.3005 -1.0505 -0.7130 -0.3361 +M SBL 1 2 6 5 +M SMT 1 n +M END +> +CHEBI_51135 + +> +poly(acrylamide) macromolecule + +> +PEX0027 + +$$$$ +Structure #1 + ACCLDraw08261614282D + + 6 5 0 0 0 0 0 0 0 0999 V2000 + 13.5618 -12.2866 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.5390 -12.8771 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 12.5390 -14.0582 0.0000 Br 0 0 0 0 0 0 0 0 0 0 0 0 + 11.5161 -12.2866 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4933 -12.8771 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 14.5847 -12.8771 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 4 1 0 0 0 0 + 4 5 1 0 0 0 0 + 1 6 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 4 1 2 3 4 +M SBL 1 2 4 5 +M SDI 1 4 11.0047 -13.1724 11.0047 -11.9913 +M SDI 1 4 14.0732 -11.9913 14.0732 -13.1724 +M END +> +PEX0028 + +$$$$ + + ACCLDraw08251621322D + + 6 5 0 0 0 0 0 0 0 0999 V2000 + 10.1325 -11.7583 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 11.8107 -11.1642 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.8206 -11.7509 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 13.8379 -11.1642 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 15.3675 -11.7434 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 13.8379 -9.9836 0.0000 Br 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 3 4 1 0 0 0 0 + 4 5 1 0 0 0 0 + 4 6 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 4 2 4 3 6 +M SBL 1 2 1 4 +M SDI 1 4 10.8974 -12.0479 10.8974 -10.8672 +M SDI 1 4 14.3948 -10.8672 14.3948 -12.0405 +M SMT 1 n +M END +> +PEX0029 + +$$$$ + + ACCLDraw08251618542D + + 6 5 0 0 0 0 0 0 0 0999 V2000 + 10.1325 -13.7283 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 11.8107 -13.1342 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 12.8206 -13.7209 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.8379 -13.1342 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 15.3675 -13.7134 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 13.8379 -11.9536 0.0000 Br 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 3 4 1 0 0 0 0 + 4 5 1 0 0 0 0 + 4 6 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 4 2 4 3 6 +M SBL 1 2 1 4 +M SDI 1 4 10.8974 -14.0179 10.8974 -12.8372 +M SDI 1 4 14.3948 -12.8372 14.3948 -14.0105 +M SMT 1 n +M END +> +PEX0030 + +$$$$ +Structure #1 + ACCLDraw08261614542D + + 14 14 0 0 0 0 0 0 0 0999 V2000 + 10.4712 -5.8164 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 9.7524 -6.2314 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0336 -5.8164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.3148 -6.2314 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5960 -5.8164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8772 -6.2314 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.5960 -4.9864 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.1584 -5.8164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8772 -4.5714 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.1584 -4.9864 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4396 -3.7414 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 5.4396 -4.5714 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.7208 -4.9864 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.0020 -4.5714 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 2 1 1 0 0 0 0 + 3 2 1 0 0 0 0 + 4 3 1 0 0 0 0 + 4 5 1 0 0 0 0 + 12 10 1 0 0 0 0 + 5 6 2 0 0 0 0 + 5 7 1 0 0 0 0 + 6 8 1 0 0 0 0 + 7 9 2 0 0 0 0 + 8 10 2 0 0 0 0 + 9 10 1 0 0 0 0 + 12 11 2 0 0 0 0 + 13 12 1 0 0 0 0 + 14 13 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 12 12 13 11 10 9 7 5 6 8 4 3 2 +M SBL 1 2 14 1 +M SDI 1 4 4.3614 -5.1939 4.3614 -4.3639 +M SDI 1 4 10.1118 -5.6089 10.1118 -6.4389 +M SMT 1 n +M END +> +PEX0031 + +$$$$ +Structure #1 + ACCLDraw08261615002D + + 14 14 0 0 0 0 0 0 0 0999 V2000 + 12.7704 -6.0746 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 12.0516 -6.4896 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 11.3327 -6.0746 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6139 -6.4896 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8951 -6.0746 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1763 -6.4896 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4575 -6.0746 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1763 -7.3196 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7387 -6.4896 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4575 -7.7346 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7387 -7.3196 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0199 -8.5646 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0199 -7.7346 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3011 -7.3196 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 2 1 1 0 0 0 0 + 3 2 1 0 0 0 0 + 4 3 1 0 0 0 0 + 5 4 1 0 0 0 0 + 5 6 1 0 0 0 0 + 13 11 1 0 0 0 0 + 6 7 2 0 0 0 0 + 6 8 1 0 0 0 0 + 7 9 1 0 0 0 0 + 8 10 2 0 0 0 0 + 9 11 2 0 0 0 0 + 10 11 1 0 0 0 0 + 13 12 2 0 0 0 0 + 14 13 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 12 12 13 11 10 8 6 7 9 5 4 3 2 +M SBL 1 2 14 1 +M SDI 1 4 6.6605 -7.9421 6.6605 -7.1121 +M SDI 1 4 12.4110 -5.8671 12.4110 -6.6971 +M SMT 1 n +M END +> +PEX0032 + +$$$$ +Structure #1 + ACCLDraw08261615122D + + 14 14 0 0 0 0 0 0 0 0999 V2000 + 13.4400 -7.4736 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 12.7212 -6.2286 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 12.7212 -7.0586 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.0024 -7.4736 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 11.2836 -7.0586 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.5648 -7.4736 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8460 -7.0586 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1272 -7.4736 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4084 -7.0586 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1272 -8.3036 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.6896 -7.4736 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4084 -8.7186 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.6896 -8.3036 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.9708 -8.7186 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 3 1 1 0 0 0 0 + 3 2 2 0 0 0 0 + 4 3 1 0 0 0 0 + 5 4 1 0 0 0 0 + 6 5 1 0 0 0 0 + 7 6 1 0 0 0 0 + 7 8 1 0 0 0 0 + 14 13 1 0 0 0 0 + 8 9 2 0 0 0 0 + 8 10 1 0 0 0 0 + 9 11 1 0 0 0 0 + 10 12 2 0 0 0 0 + 11 13 2 0 0 0 0 + 12 13 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 12 12 13 11 9 8 10 7 6 5 4 3 2 +M SBL 1 2 8 1 +M SDI 1 4 7.3302 -8.9261 7.3302 -8.0961 +M SDI 1 4 13.0806 -6.8511 13.0806 -7.6811 +M SMT 1 n +M END +> +PEX0033 + +$$$$ + + Marvin 09300915412D + + 9 9 0 0 0 0 999 V2000 + 1.5029 1.6375 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.6779 1.6379 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.2650 0.9236 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.6771 0.2089 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.5021 0.2085 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.9150 0.9228 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.7400 0.9223 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5645 0.8929 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + -0.5595 0.9530 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 2 0 0 0 0 + 1 6 1 0 0 0 0 + 2 3 1 0 0 0 0 + 3 4 2 0 0 0 0 + 4 5 1 0 0 0 0 + 5 6 2 0 0 0 0 + 6 7 1 0 0 0 0 + 7 8 1 0 0 0 0 + 3 9 1 0 0 0 0 +M STY 1 1 SRU +M SCN 1 1 HT +M SAL 1 7 1 2 3 4 5 6 7 +M SDI 1 4 3.0942 1.3225 3.0648 0.4980 +M SDI 1 4 -0.0892 0.5235 -0.0598 1.3480 +M SBL 1 2 9 8 +M SMT 1 n +M END +> +CHEBI_53198 + +> +poly(aniline) macromolecule + +> +PEX0034 + +$$$$ + + ACCLDraw09071621592D + + 10 10 0 0 0 0 0 0 0 0999 V2000 + 12.7504 -14.9732 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.9254 -14.9732 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.5125 -15.6871 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.9246 -16.4018 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.7496 -16.4018 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.1625 -15.6879 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.9875 -15.6879 0.0000 N 0 0 3 0 0 0 0 0 0 0 0 0 + 14.8120 -15.6879 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6880 -15.6871 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 13.3410 -13.9503 0.0000 D 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 2 0 0 0 0 + 1 6 1 0 0 0 0 + 2 3 1 0 0 0 0 + 3 4 2 0 0 0 0 + 4 5 1 0 0 0 0 + 5 6 2 0 0 0 0 + 6 7 1 0 0 0 0 + 7 8 1 0 0 0 0 + 3 9 1 0 0 0 0 + 1 10 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 8 1 2 3 4 5 6 7 10 +M SBL 1 2 9 8 +M SDI 1 4 14.3417 -15.2882 14.3123 -16.1127 +M SDI 1 4 11.1583 -16.0872 11.1877 -15.2627 +M SMT 1 n +M END +> +PEX0035 + +$$$$ + + ACCLDraw09101617552D + + 10 10 0 0 0 0 0 0 0 0999 V2000 + 12.7504 -15.4522 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.9254 -15.4522 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.5125 -16.1661 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.9246 -16.8808 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.7496 -16.8808 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.1625 -16.1669 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.9875 -16.1669 0.0000 N 0 0 3 0 0 0 0 0 0 0 0 0 + 14.8120 -16.1669 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6880 -16.1661 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 11.3348 -14.4294 0.0000 D 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 2 0 0 0 0 + 1 6 1 0 0 0 0 + 2 3 1 0 0 0 0 + 3 4 2 0 0 0 0 + 4 5 1 0 0 0 0 + 5 6 2 0 0 0 0 + 6 7 1 0 0 0 0 + 7 8 1 0 0 0 0 + 3 9 1 0 0 0 0 + 2 10 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 8 1 2 3 4 5 6 7 10 +M SBL 1 2 9 8 +M SDI 1 4 14.3417 -15.7672 14.3123 -16.5917 +M SDI 1 4 11.1583 -16.5662 11.1877 -15.7417 +M SMT 1 n +M END +> +PEX0036 + +$$$$ + + Marvin 10060915282D + + 10 9 0 0 0 0 999 V2000 + 5.5978 1.7444 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.8833 1.3319 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.1688 1.7445 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.4544 1.3320 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.7399 1.7445 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.5814 1.7277 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0255 1.3321 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.3110 1.7446 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0263 1.7280 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 1.3110 2.5696 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 3 4 1 0 0 0 0 + 4 5 1 0 0 0 0 + 1 6 1 0 0 0 0 + 5 7 1 0 0 0 0 + 7 8 1 0 0 0 0 + 8 9 1 0 0 0 0 + 8 10 2 0 0 0 0 +M STY 1 1 SRU +M SCN 1 1 HT +M SAL 1 8 1 2 3 4 5 7 8 10 +M SDI 1 4 0.7873 1.3253 0.7767 2.1502 +M SDI 1 4 6.0098 2.1500 5.9958 1.3251 +M SBL 1 2 5 8 +M SMT 1 n +M END +> +CHEBI_53200 + +> +poly(caprolactone) macromolecule + +> +PEX0037 + +$$$$ + + Marvin 10060915302D + + 9 8 0 0 0 0 999 V2000 + -0.9186 1.8468 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -2.2813 1.8762 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + -0.9186 1.0217 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -1.6330 0.6093 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + -0.2041 2.2593 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.9817 2.2004 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + -0.9186 2.6718 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.2040 0.6093 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + -1.6136 -0.1876 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 1 3 1 0 0 0 0 + 3 4 1 0 0 0 0 + 5 1 1 0 0 0 0 + 5 6 1 0 0 0 0 + 1 7 1 0 0 0 0 + 3 8 2 0 0 0 0 + 4 9 1 0 0 0 0 +M STY 1 1 SRU +M SCN 1 1 HT +M SAL 1 7 1 3 4 5 7 8 9 +M SDI 1 4 0.3047 2.6470 0.2637 1.8230 +M SDI 1 4 -1.4886 1.4465 -1.4708 2.2713 +M SBL 1 2 1 5 +M SMT 1 n +M END +> +CHEBI_53205 + +> +poly(methyl methacrylate) macromolecule + +> +PEX0038 + +$$$$ + + ACCLDraw09101618152D + + 5 4 0 0 0 0 0 0 0 0999 V2000 + 12.4384 -15.3506 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 11.2573 -15.3506 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 13.0290 -14.3277 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.2101 -14.3277 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 13.0290 -16.3735 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 3 1 1 0 0 0 0 + 3 4 1 0 0 0 0 + 1 5 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 3 1 3 5 +M SBL 1 2 1 3 +M SDI 1 4 11.8478 -15.9411 11.8478 -14.7600 +M SDI 1 4 13.6195 -13.7371 13.6195 -14.9182 +M SMT 1 n +M END +> +PEX0039 + +$$$$ + + ACCLDraw09101618152D + + 5 4 0 0 0 0 0 0 0 0999 V2000 + 12.4384 -15.3506 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.2573 -15.3506 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 13.0290 -14.3277 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 14.2101 -14.3277 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 12.4384 -13.3048 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 3 1 1 0 0 0 0 + 3 4 1 0 0 0 0 + 3 5 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 3 1 3 5 +M SBL 1 2 1 3 +M SDI 1 4 11.8478 -15.9411 11.8478 -14.7600 +M SDI 1 4 13.6195 -13.7371 13.6195 -14.9182 +M SMT 1 n +M END +> +PEX0040 + +$$$$ + + Marvin 10110901122D + + 15 17 0 0 0 0 999 V2000 + -1.9820 0.8257 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -2.7915 0.6666 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -3.0585 -0.1140 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -2.5160 -0.7356 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -1.7065 -0.5765 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -1.4395 0.2041 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -1.0467 -1.0716 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.3718 -0.5971 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.6146 0.1914 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.4324 -0.7810 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.9939 -0.1766 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.7511 0.6119 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.0532 0.7959 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.7981 -0.3605 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + -3.8680 -0.2731 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 2 0 0 0 0 + 1 6 1 0 0 0 0 + 2 3 1 0 0 0 0 + 3 4 2 0 0 0 0 + 4 5 1 0 0 0 0 + 5 7 1 0 0 0 0 + 6 5 2 0 0 0 0 + 6 9 1 0 0 0 0 + 7 8 1 0 0 0 0 + 8 10 2 0 0 0 0 + 9 8 1 0 0 0 0 + 9 13 2 0 0 0 0 + 10 11 1 0 0 0 0 + 11 12 2 0 0 0 0 + 12 13 1 0 0 0 0 + 11 14 1 0 0 0 0 + 3 15 1 0 0 0 0 +M STY 1 1 SRU +M SCN 1 1 HT +M SAL 1 13 1 2 3 4 5 6 7 8 9 10 11 12 13 +M SDI 1 4 1.4170 0.1498 1.2330 -0.6544 +M SDI 1 4 -3.3123 -0.5843 -3.4714 0.2252 +M SBL 1 2 17 16 +M SMT 1 n +M END +> +CHEBI_53316 + +> +poly(fluorene-2,7-diyl) macromolecule + +> +PEX0041 + +$$$$ + + Marvin 10120913202D + + 26 26 0 0 0 0 999 V2000 + 11.1811 -4.7463 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.1811 -5.5713 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4666 -4.3338 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4666 -5.9838 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.7521 -4.7463 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.7521 -5.5713 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.8956 -4.3338 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.8956 -3.5088 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 12.6101 -4.7463 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 11.8956 -5.9838 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.6101 -5.5713 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 11.8956 -6.8089 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0377 -5.9838 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0377 -6.8088 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.3231 -5.5712 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0377 -4.3338 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0377 -3.5087 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 13.3247 -4.3339 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.0391 -4.7464 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.7536 -4.3340 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.4681 -4.7465 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 16.1826 -4.3340 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 16.8970 -4.7466 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 18.3431 -4.7438 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 8.3232 -4.7462 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8480 -4.7434 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 2 1 1 0 0 0 0 + 3 1 2 0 0 0 0 + 4 2 2 0 0 0 0 + 5 3 1 0 0 0 0 + 6 4 1 0 0 0 0 + 6 5 2 0 0 0 0 + 8 7 2 0 0 0 0 + 9 7 1 0 0 0 0 + 1 7 1 0 0 0 0 + 11 10 1 0 0 0 0 + 12 10 2 0 0 0 0 + 2 10 1 0 0 0 0 + 14 13 2 0 0 0 0 + 15 13 1 0 0 0 0 + 6 13 1 0 0 0 0 + 17 16 2 0 0 0 0 + 5 16 1 0 0 0 0 + 9 18 1 0 0 0 0 + 18 19 1 0 0 0 0 + 19 20 1 0 0 0 0 + 20 21 1 0 0 0 0 + 21 22 1 0 0 0 0 + 22 23 1 0 0 0 0 + 23 24 1 0 0 0 0 + 16 25 1 0 0 0 0 + 25 26 1 0 0 0 0 +M STY 1 1 SRU +M SCN 1 1 HT +M SAL 1 15 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +M SAL 1 9 16 17 18 19 20 21 22 23 25 +M SDI 1 4 7.7150 -5.1575 7.7166 -4.3325 +M SDI 1 4 17.4917 -4.3329 17.4932 -5.1579 +M SBL 1 2 24 26 +M SMT 1 n +M END +> +CHEBI_53307 + +> +poly[iminocarbonyl(4,6-dicarboxy-1,3-phenylene)carbonyliminohexane-1,6-diyl] + +> +PEX0042 + +$$$$ + + Marvin 10150914562D + + 76 84 0 0 1 0 999 V2000 + 9.1878 -2.9894 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4651 -3.3860 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4651 -4.2251 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 9.1878 -4.6373 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8875 -4.2251 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 10.6024 -4.6373 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8875 -3.3860 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 10.6024 -2.1812 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.3331 -1.7612 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.0328 -2.1812 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 12.0328 -2.9894 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 12.8175 -3.2462 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.2837 -2.5930 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.8175 -1.9249 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 12.0328 -1.3338 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8875 -2.5775 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.0328 -3.8211 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 13.5407 -1.5204 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7319 -4.6560 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6024 -2.9894 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 10.6024 -3.8147 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 12.1031 -0.6650 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.3331 -3.3860 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 11.3331 -2.5608 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11.3331 -4.2251 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.2464 -1.0775 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.9608 -0.6650 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.6754 -1.0775 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.6754 -1.9025 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.5319 -0.6650 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.8175 -1.0775 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 16.3898 -0.6650 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7383 -5.4809 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4530 -5.8935 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4530 -6.7185 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7383 -7.1310 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0239 -5.8935 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0239 -6.7185 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.9073 -8.3947 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2048 -7.9636 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4759 -8.3793 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 8.4759 -9.2112 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1806 -9.6147 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 9.1768 -10.4399 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.9094 -9.1991 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 11.3101 -9.2231 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.0371 -9.6497 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.0189 -10.4655 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 11.3170 -10.8659 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 11.4828 -11.6749 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.2811 -11.7562 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.6304 -11.0201 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 12.7551 -10.0457 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6117 -8.7984 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.5945 -11.2781 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 13.3401 -11.4478 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7383 -7.9560 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6083 -9.6234 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 9.8913 -10.0324 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 13.3708 -9.7753 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6257 -10.4547 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 11.3426 -10.0458 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8970 -10.8705 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.0746 -11.8414 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.7868 -12.2576 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.7825 -13.0826 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.0659 -13.4913 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.0788 -11.0164 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.3665 -10.6003 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 15.4949 -13.4989 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1674 -5.4809 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8818 -5.8934 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.3840 -5.8747 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 5.8031 -6.7372 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8875 -5.0501 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4661 -10.0272 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 11 10 1 0 0 0 0 + 14 10 1 0 0 0 0 + 9 10 1 0 0 0 0 + 10 15 1 1 0 0 0 + 23 11 1 0 0 0 0 + 12 11 1 0 0 0 0 + 11 17 1 6 0 0 0 + 8 20 1 0 0 0 0 + 7 20 1 0 0 0 0 + 23 20 1 0 0 0 0 + 25 23 1 0 0 0 0 + 5 7 1 0 0 0 0 + 7 16 1 1 0 0 0 + 1 7 1 0 0 0 0 + 13 14 1 0 0 0 0 + 31 14 1 0 0 0 0 + 14 18 1 6 0 0 0 + 8 9 1 0 0 0 0 + 13 12 1 0 0 0 0 + 6 25 1 0 0 0 0 + 4 5 1 0 0 0 0 + 6 5 1 0 0 0 0 + 2 1 1 0 0 0 0 + 3 4 1 0 0 0 0 + 3 2 1 0 0 0 0 + 3 19 1 1 0 0 0 + 20 21 1 6 0 0 0 + 31 22 1 6 0 0 0 + 23 24 1 1 0 0 0 + 30 26 1 0 0 0 0 + 27 26 1 0 0 0 0 + 28 27 1 0 0 0 0 + 29 28 1 0 0 0 0 + 32 28 1 0 0 0 0 + 30 31 1 0 0 0 0 + 19 33 1 0 0 0 0 + 35 34 1 0 0 0 0 + 33 34 2 0 0 0 0 + 36 35 2 0 0 0 0 + 37 33 1 0 0 0 0 + 38 36 1 0 0 0 0 + 38 37 2 0 0 0 0 + 36 57 1 0 0 0 0 + 49 48 1 0 0 0 0 + 52 48 1 0 0 0 0 + 47 48 1 0 0 0 0 + 48 53 1 1 0 0 0 + 61 49 1 0 0 0 0 + 50 49 1 0 0 0 0 + 49 55 1 6 0 0 0 + 46 58 1 0 0 0 0 + 45 58 1 0 0 0 0 + 61 58 1 0 0 0 0 + 63 61 1 0 0 0 0 + 43 45 1 0 0 0 0 + 45 54 1 1 0 0 0 + 39 45 1 0 0 0 0 + 51 52 1 0 0 0 0 + 69 52 1 0 0 0 0 + 52 56 1 6 0 0 0 + 46 47 1 0 0 0 0 + 51 50 1 0 0 0 0 + 44 63 1 0 0 0 0 + 42 43 1 0 0 0 0 + 44 43 1 0 0 0 0 + 40 39 1 0 0 0 0 + 41 42 1 0 0 0 0 + 41 40 1 0 0 0 0 + 41 57 1 1 0 0 0 + 58 59 1 6 0 0 0 + 69 60 1 6 0 0 0 + 61 62 1 1 0 0 0 + 68 64 1 0 0 0 0 + 65 64 1 0 0 0 0 + 66 65 1 0 0 0 0 + 67 66 1 0 0 0 0 + 70 66 1 0 0 0 0 + 68 69 1 0 0 0 0 + 34 71 1 0 0 0 0 + 71 72 2 0 0 0 0 + 72 73 1 0 0 0 0 + 38 74 1 0 0 0 0 + 5 75 1 6 0 0 0 + 43 76 1 6 0 0 0 +M STY 1 1 SRU +M SCN 1 1 HT +M SAL 1 15 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +M SAL 1 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 +M SAL 1 15 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 +M SAL 1 15 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 +M SAL 1 14 61 62 63 64 65 66 67 68 69 70 71 72 75 76 +M SDI 1 4 10.4952 -5.4732 10.5055 -6.2982 +M SDI 1 4 6.5275 -7.1387 6.5149 -6.3137 +M SBL 1 2 82 81 +M SMT 1 n +M END +> +CHEBI_53356 + +> +poly[2,5-bis(cholestanoxy)-1,4-phenylenevinylene] macromolecule + +> +PEX0043 + +$$$$ + + Marvin 10070911192D + + 63 69 0 0 0 0 999 V2000 + 11.3569 -3.5604 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.0713 -3.9729 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.7857 -3.5604 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.5002 -3.9729 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.2146 -3.5604 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.9291 -3.9729 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.9291 -4.7979 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.2146 -5.2104 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.5002 -4.7979 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.7857 -5.2104 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.0713 -4.7979 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.3569 -5.2104 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 11.3569 -2.7354 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6424 -2.3229 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.9279 -2.7354 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6402 -4.8019 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.9280 -5.2184 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.9326 -6.0434 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6493 -6.4519 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.3615 -6.0353 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.0782 -6.4439 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6354 -3.9770 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2112 -4.8100 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4990 -5.2264 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5037 -6.0514 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2204 -6.4599 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2251 -7.2848 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.9418 -7.6933 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6539 -7.2768 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7916 -6.4680 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7962 -7.2929 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5129 -7.7014 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0748 -6.0595 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3626 -6.4761 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3673 -7.3009 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0840 -7.7095 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0887 -8.5344 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8054 -8.9429 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5175 -8.5264 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6551 -7.7175 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.6597 -8.5424 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3765 -8.9509 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3811 -9.7759 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9383 -7.3090 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9475 -8.9590 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9522 -9.7839 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2373 -10.1957 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5233 -9.7824 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.8084 -10.1942 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0944 -9.7810 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.3795 -10.1927 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.6654 -9.7795 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.2281 -8.5552 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3.5188 -8.9764 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.7993 -8.5726 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.0899 -8.9938 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.3705 -8.5900 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.6611 -9.0111 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.0584 -8.6074 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6424 -1.4979 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.0640 -1.5274 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 9.9279 -1.0854 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4072 -1.1257 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 3 4 1 0 0 0 0 + 4 5 1 0 0 0 0 + 5 6 1 0 0 0 0 + 6 7 1 0 0 0 0 + 7 8 1 0 0 0 0 + 8 9 1 0 0 0 0 + 9 10 1 0 0 0 0 + 10 11 1 0 0 0 0 + 11 12 1 0 0 0 0 + 1 13 1 0 0 0 0 + 14 15 2 0 0 0 0 + 14 13 1 0 0 0 0 + 12 16 1 0 0 0 0 + 12 20 1 0 0 0 0 + 16 17 1 0 0 0 0 + 19 20 1 0 0 0 0 + 20 21 2 0 0 0 0 + 16 22 2 0 0 0 0 + 17 18 1 0 0 0 0 + 17 23 2 0 0 0 0 + 23 24 1 0 0 0 0 + 24 25 2 0 0 0 0 + 19 18 1 0 0 0 0 + 19 29 2 0 0 0 0 + 26 18 2 0 0 0 0 + 27 28 2 0 0 0 0 + 28 29 1 0 0 0 0 + 25 30 1 0 0 0 0 + 26 25 1 0 0 0 0 + 26 27 1 0 0 0 0 + 32 27 1 0 0 0 0 + 30 31 1 0 0 0 0 + 30 33 2 0 0 0 0 + 33 34 1 0 0 0 0 + 34 35 2 0 0 0 0 + 36 31 2 0 0 0 0 + 32 31 1 0 0 0 0 + 32 39 2 0 0 0 0 + 37 38 2 0 0 0 0 + 38 39 1 0 0 0 0 + 35 40 1 0 0 0 0 + 36 35 1 0 0 0 0 + 36 37 1 0 0 0 0 + 42 37 1 0 0 0 0 + 40 41 1 0 0 0 0 + 41 42 1 0 0 0 0 + 42 43 2 0 0 0 0 + 40 44 2 0 0 0 0 + 41 45 1 0 0 0 0 + 45 46 1 0 0 0 0 + 46 47 1 0 0 0 0 + 47 48 1 0 0 0 0 + 48 49 1 0 0 0 0 + 49 50 1 0 0 0 0 + 50 51 1 0 0 0 0 + 51 52 1 0 0 0 0 + 45 53 1 0 0 0 0 + 53 54 1 0 0 0 0 + 54 55 1 0 0 0 0 + 55 56 1 0 0 0 0 + 56 57 1 0 0 0 0 + 57 58 1 0 0 0 0 + 58 59 1 0 0 0 0 + 14 60 1 0 0 0 0 + 60 61 1 0 0 0 0 + 60 62 1 0 0 0 0 + 62 63 1 0 0 0 0 +M STY 1 1 SRU +M SCN 1 1 HT +M SAL 1 15 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +M SAL 1 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 +M SAL 1 15 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 +M SAL 1 15 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 +M SAL 1 1 62 +M SDI 1 4 9.3127 -1.5143 9.2908 -0.6896 +M SDI 1 4 11.2363 -1.0976 11.2192 -1.9224 +M SBL 1 2 67 69 +M SMT 1 n +M END +> +CHEBI_53241 + +> +poly(perylene bisimide acrylate) macromolecule + +> +PEX0044 + +$$$$ + + Mrv0541 02231215152D + + 71 74 0 0 1 0 999 V2000 + 12.8725 -11.1521 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 13.5903 -11.5667 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 12.8725 -10.3272 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 11.8517 -11.7493 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 14.2992 -11.1521 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 13.5903 -12.3914 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 13.5903 -9.9172 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 12.1547 -9.9172 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.8307 -12.3335 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 14.2992 -10.3272 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 14.9729 -11.9185 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 11.4415 -10.3272 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 10.8307 -13.1582 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 10.1175 -11.9232 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 15.3200 -9.7433 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 16.1934 -11.9139 0.0000 S 0 0 0 0 0 0 0 0 0 0 0 0 + 10.1175 -13.5728 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 11.7684 -14.1445 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.3996 -12.3335 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 16.3409 -9.1504 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 16.1889 -12.7387 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 16.1889 -11.0892 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 17.0225 -11.9139 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.3996 -13.1582 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 10.1175 -14.3975 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.6864 -11.9232 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 16.3409 -8.3257 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 17.0586 -9.5650 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 8.6819 -13.5683 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 7.9730 -12.3335 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.6864 -11.0985 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 17.0586 -7.9154 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 15.6276 -7.9154 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 17.7720 -9.1504 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 17.0586 -10.3942 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 6.9121 -13.5594 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 17.7720 -8.3257 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 14.9099 -8.3257 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 15.6276 -7.0907 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 19.3208 -10.0487 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 18.7974 -7.7326 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 19.8138 -7.1442 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 19.8138 -6.3194 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 20.5314 -7.5589 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 + 20.5314 -5.9093 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 19.1005 -5.9093 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 21.2449 -7.1442 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 20.5314 -8.3880 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 21.2449 -6.3194 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 18.2713 -5.9004 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 22.5298 -7.7348 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 22.7828 -5.4368 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 17.4465 -5.8959 0.0000 S 0 0 0 0 0 0 0 0 0 0 0 0 + 22.5342 -8.5639 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 17.4421 -6.7207 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 17.4421 -5.0712 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 16.6217 -5.8915 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 23.2475 -8.9741 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 21.8165 -8.9741 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 11.7684 -15.0239 0.0000 S 0 0 0 0 0 0 0 0 0 0 0 0 + 11.7684 -15.9034 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 12.6642 -15.0236 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 10.9056 -15.0242 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 13.5903 -13.2709 0.0000 S 0 0 0 0 0 0 0 0 0 0 0 0 + 13.5903 -14.1504 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 14.4672 -13.2709 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 12.7084 -13.2708 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 19.3208 -10.9282 0.0000 S 0 0 0 0 0 0 0 0 0 0 0 0 + 19.3208 -11.8076 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 20.1836 -10.9285 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 18.4251 -10.9278 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 1 3 1 0 0 0 0 + 1 4 1 6 0 0 0 + 2 5 1 0 0 0 0 + 2 6 1 1 0 0 0 + 3 7 1 0 0 0 0 + 3 8 1 1 0 0 0 + 9 4 1 1 0 0 0 + 5 10 1 0 0 0 0 + 5 11 1 6 0 0 0 + 8 12 1 0 0 0 0 + 9 13 1 0 0 0 0 + 9 14 1 0 0 0 0 + 10 15 1 6 0 0 0 + 11 16 1 0 0 0 0 + 13 17 1 0 0 0 0 + 13 18 1 6 0 0 0 + 14 19 1 0 0 0 0 + 20 15 1 6 0 0 0 + 16 21 1 0 0 0 0 + 16 22 2 0 0 0 0 + 16 23 2 0 0 0 0 + 17 24 1 0 0 0 0 + 17 25 1 1 0 0 0 + 19 26 1 6 0 0 0 + 20 27 1 0 0 0 0 + 20 28 1 0 0 0 0 + 24 29 1 6 0 0 0 + 26 30 1 0 0 0 0 + 26 31 2 0 0 0 0 + 27 32 1 0 0 0 0 + 27 33 1 1 0 0 0 + 28 34 1 0 0 0 0 + 28 35 1 1 0 0 0 + 29 36 1 0 0 0 0 + 32 37 1 0 0 0 0 + 33 38 1 0 0 0 0 + 33 39 2 0 0 0 0 + 34 40 1 6 0 0 0 + 37 41 1 1 0 0 0 + 42 41 1 6 0 0 0 + 42 43 1 0 0 0 0 + 42 44 1 0 0 0 0 + 43 45 1 0 0 0 0 + 43 46 1 1 0 0 0 + 44 47 1 0 0 0 0 + 44 48 1 1 0 0 0 + 45 49 1 0 0 0 0 + 46 50 1 0 0 0 0 + 47 51 1 6 0 0 0 + 49 52 1 0 0 0 0 + 50 53 1 0 0 0 0 + 51 54 1 0 0 0 0 + 53 55 1 0 0 0 0 + 53 56 2 0 0 0 0 + 53 57 2 0 0 0 0 + 54 58 1 0 0 0 0 + 54 59 2 0 0 0 0 + 7 10 1 0 0 0 0 + 19 24 1 0 0 0 0 + 34 37 1 0 0 0 0 + 47 49 1 0 0 0 0 + 18 60 1 0 0 0 0 + 60 61 2 0 0 0 0 + 60 62 2 0 0 0 0 + 60 63 1 0 0 0 0 + 6 64 1 0 0 0 0 + 64 65 2 0 0 0 0 + 64 66 2 0 0 0 0 + 64 67 1 0 0 0 0 + 40 68 1 0 0 0 0 + 68 69 2 0 0 0 0 + 68 70 2 0 0 0 0 + 68 71 1 0 0 0 0 +M STY 1 1 SRU +M SCN 1 1 HT +M SAL 1 15 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +M SAL 1 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 +M SAL 1 15 31 32 33 34 35 37 38 39 40 41 42 43 44 45 46 +M SAL 1 15 47 48 49 50 51 53 54 55 56 57 58 59 60 61 62 +M SAL 1 9 63 64 65 66 67 68 69 70 71 +M SDI 1 4 7.6954 -13.9396 7.6954 -13.1041 +M SDI 1 4 21.8988 -5.4527 21.8988 -6.2882 +M SBL 1 2 35 51 +M SMT 1 n +M END +> +DB01109 + +> +Heparin + +> +PEX0045 + +$$$$ + + ACCLDraw09121610172D + + 11 9 0 0 0 0 0 0 0 0999 V2000 + 9.4306 -15.8566 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4535 -16.4471 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.4764 -15.8566 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 16.0694 -14.6568 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 + 14.0237 -15.8379 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.0465 -16.4284 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 16.0694 -15.8379 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 8.4078 -16.4471 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 12.4992 -16.4471 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 13.0008 -16.4284 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 17.0923 -16.4284 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 5 6 1 0 0 0 0 + 6 7 1 0 0 0 0 + 4 7 1 0 0 0 0 + 1 8 1 0 0 0 0 + 3 9 1 0 0 0 0 + 5 10 1 0 0 0 0 + 7 11 1 0 0 0 0 +M STY 2 1 SRU 2 SRU +M SLB 2 1 1 2 2 +M SCN 2 1 HT 2 HT +M SAL 1 3 2 1 3 +M SBL 1 2 6 7 +M SDI 1 4 8.9192 -16.7424 8.9192 -15.5613 +M SDI 1 4 11.9878 -15.5613 11.9878 -16.7424 +M SMT 1 n +M SAL 2 4 6 5 7 4 +M SBL 2 2 8 9 +M SDI 2 4 13.5122 -16.7237 13.5122 -15.5426 +M SDI 2 4 16.5808 -15.5426 16.5808 -16.7237 +M SMT 2 n +M END +> +PEX0046 + +$$$$ + + ACCLDraw09121610192D + + 11 9 0 0 0 0 0 0 0 0999 V2000 + 9.4128 -15.8472 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 10.4357 -16.4377 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.4585 -15.8472 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.4128 -14.6661 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 + 16.0873 -16.2818 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.0644 -15.6913 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.0415 -16.2818 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.3900 -16.4377 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 12.4814 -16.4377 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 13.0187 -15.6913 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 17.1101 -15.6913 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 2 1 1 0 0 0 0 + 3 2 1 0 0 0 0 + 1 4 1 0 0 0 0 + 6 5 1 0 0 0 0 + 7 6 1 0 0 0 0 + 1 8 1 0 0 0 0 + 3 9 1 0 0 0 0 + 7 10 1 0 0 0 0 + 5 11 1 0 0 0 0 +M STY 2 1 SRU 2 SRU +M SLB 2 1 1 2 2 +M SCN 2 1 HT 2 HT +M SAL 1 4 2 1 4 3 +M SBL 1 2 6 7 +M SDI 1 4 8.9014 -16.7330 8.9014 -15.5519 +M SDI 1 4 11.9699 -15.5519 11.9699 -16.7330 +M SMT 1 n +M SAL 2 3 6 5 7 +M SBL 2 2 8 9 +M SDI 2 4 13.5301 -16.5771 13.5301 -15.3960 +M SDI 2 4 16.5987 -15.3960 16.5987 -16.5771 +M SMT 2 n +M END +> +PEX0047 + +$$$$ + + ACCLDraw09121610512D + + 11 9 0 0 0 0 0 0 0 0999 V2000 + 16.0873 -16.2818 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.0644 -15.6913 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 14.0415 -16.2818 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.0187 -15.6913 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 17.1101 -15.6913 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 10.3848 -17.6769 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 + 8.3593 -16.4614 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 12.4501 -16.5306 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 11.4374 -15.9229 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4047 -16.4960 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 9.3920 -15.8883 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2 1 1 0 0 0 0 + 3 2 1 0 0 0 0 + 3 4 1 0 0 0 0 + 1 5 1 0 0 0 0 + 11 7 1 0 0 0 0 + 9 8 1 0 0 0 0 + 9 10 1 0 0 0 0 + 10 11 1 0 0 0 0 + 10 6 1 0 0 0 0 +M STY 2 1 SRU 2 SRU +M SLB 2 1 1 2 2 +M SCN 2 1 HT 2 HT +M SAL 1 3 2 1 3 +M SBL 1 2 3 4 +M SDI 1 4 13.5301 -16.5771 13.5301 -15.3960 +M SDI 1 4 16.5987 -15.3960 16.5987 -16.5771 +M SMT 1 n +M SAL 2 4 6 9 10 11 +M SBL 2 2 5 6 +M SDI 2 4 8.8756 -16.7654 8.8756 -15.5844 +M SDI 2 4 11.9437 -15.6363 11.9437 -16.8173 +M SMT 2 n +M END +> +PEX0048 + +$$$$ + + ACCLDraw09131613312D + + 15 14 0 0 0 0 0 0 0 0999 V2000 + 12.8881 -17.1471 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 13.6214 -16.1804 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.9547 -16.8346 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 14.1881 -17.1637 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.6214 -16.8387 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 12.2297 -16.9262 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 11.5006 -17.2012 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.9339 -16.8721 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 10.1922 -17.2054 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 10.9339 -16.2137 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.9122 -14.4453 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.9122 -15.6228 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.9354 -13.8545 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.9612 -15.6206 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.9612 -14.4402 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5 2 1 0 0 0 0 + 4 3 1 0 0 0 0 + 5 4 1 0 0 0 0 + 1 5 1 0 0 0 0 + 7 6 1 0 0 0 0 + 8 7 1 0 0 0 0 + 9 8 1 0 0 0 0 + 8 10 1 0 0 0 0 + 15 13 1 0 0 0 0 + 14 15 2 0 0 0 0 + 11 12 1 0 0 0 0 + 13 11 2 0 0 0 0 + 12 10 2 0 0 0 0 + 10 14 1 0 0 0 0 +M STY 3 1 COP 2 SRU 3 SRU +M SLB 3 1 1 2 2 3 3 +M SST 1 1 ALT +M SCN 3 1 HT 2 HT 3 HT +M SPL 2 2 1 3 1 +M SAL 1 15 9 8 7 6 5 4 3 2 1 12 11 13 15 14 10 +M SDI 1 4 9.9047 -17.6096 9.9047 -16.0137 +M SDI 1 4 15.3756 -16.0137 15.3756 -17.6096 +M SAL 2 3 5 4 2 +M SBL 2 2 4 2 +M SDI 2 4 13.3089 -17.3304 13.3089 -16.6721 +M SDI 2 4 14.5006 -16.6679 14.5006 -17.3304 +M SMT 2 n +M SAL 3 8 8 7 12 11 13 15 14 10 +M SBL 3 2 7 5 +M SDI 3 4 10.6214 -17.3679 10.6214 -16.7054 +M SDI 3 4 11.8131 -16.7054 11.8131 -17.3637 +M SMT 3 n +M END +> +PEX0049 + +$$$$ + + ACCLDraw09131615102D + + 15 14 0 0 0 0 0 0 0 0999 V2000 + 12.7965 -17.1471 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 13.5298 -16.1804 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.8631 -16.8346 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 14.0965 -17.1637 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.5298 -16.8387 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 12.1381 -16.9262 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 11.4090 -17.2012 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.8423 -16.8721 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 10.1006 -17.2054 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 10.8423 -16.2137 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8206 -14.4453 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8206 -15.6228 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.8438 -13.8545 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.8696 -15.6206 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.8696 -14.4402 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5 2 1 0 0 0 0 + 4 3 1 0 0 0 0 + 5 4 1 0 0 0 0 + 1 5 1 0 0 0 0 + 7 6 1 0 0 0 0 + 8 7 1 0 0 0 0 + 9 8 1 0 0 0 0 + 8 10 1 0 0 0 0 + 15 13 1 0 0 0 0 + 14 15 2 0 0 0 0 + 11 12 1 0 0 0 0 + 13 11 2 0 0 0 0 + 12 10 2 0 0 0 0 + 10 14 1 0 0 0 0 +M STY 3 1 SRU 2 SRU 3 SRU +M SLB 3 1 1 2 2 3 3 +M SCN 3 1 HT 2 HT 3 HT +M SPL 2 2 1 3 1 +M SAL 1 15 9 8 7 6 5 4 3 2 1 12 11 13 15 14 10 +M SDI 1 4 9.8131 -17.6096 9.8131 -16.0137 +M SDI 1 4 15.2840 -16.0137 15.2840 -17.6096 +M SMT 1 n +M SAL 2 8 8 7 12 11 13 15 14 10 +M SBL 2 2 7 5 +M SDI 2 4 10.5298 -17.3679 10.5298 -16.7054 +M SDI 2 4 11.7215 -16.7054 11.7215 -17.3637 +M SMT 2 n +M SAL 3 3 5 4 2 +M SBL 3 2 4 2 +M SDI 3 4 13.2173 -17.3304 13.2173 -16.6721 +M SDI 3 4 14.4090 -16.6679 14.4090 -17.3304 +M SMT 3 n +M END +> +PEX0050 + +$$$$ + + ACCLDraw09131615292D + + 45 48 0 0 0 0 0 0 0 0999 V2000 + 16.3387 -12.9258 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 15.6199 -11.6808 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 15.6199 -12.5108 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.9011 -12.9258 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.5359 -14.1410 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 10.3659 -14.1410 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 13.4635 -12.9258 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.7447 -12.5108 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.1823 -12.5108 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.7447 -11.6808 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.1823 -11.6808 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.4635 -11.2658 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.0259 -11.2658 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 12.0259 -12.7034 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.6109 -13.4222 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.6109 -11.9846 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7809 -13.4222 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7809 -11.9846 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.3659 -12.7034 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.3071 -10.8508 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.4409 -10.5470 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4770 -17.2556 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4235 -16.5411 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.2486 -16.5411 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.7234 -17.9702 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.1360 -18.6847 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 18.0230 -20.1137 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 15.3735 -19.3991 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 16.1984 -19.3991 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 16.6110 -20.1137 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 16.1985 -20.8281 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.3734 -20.8281 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.9609 -20.1136 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.7234 -20.8281 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 14.1359 -20.1136 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.7235 -19.3991 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.8984 -19.3991 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.4859 -18.6846 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.8985 -17.9702 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.4860 -17.2556 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 11.6610 -17.2556 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.2484 -17.9701 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4234 -17.9701 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.0110 -17.2556 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1860 -17.2556 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 3 1 1 0 0 0 0 + 3 2 2 0 0 0 0 + 4 3 1 0 0 0 0 + 9 4 1 0 0 0 0 + 6 5 1 0 0 0 0 + 17 6 1 0 0 0 0 + 21 13 1 0 0 0 0 + 20 13 1 0 0 0 0 + 13 10 1 0 0 0 0 + 7 8 2 0 0 0 0 + 7 9 1 0 0 0 0 + 8 10 1 0 0 0 0 + 9 11 2 0 0 0 0 + 10 12 2 0 0 0 0 + 11 12 1 0 0 0 0 + 16 13 1 0 0 0 0 + 14 15 2 0 0 0 0 + 14 16 1 0 0 0 0 + 15 17 1 0 0 0 0 + 16 18 2 0 0 0 0 + 17 19 2 0 0 0 0 + 18 19 1 0 0 0 0 + 45 22 1 0 0 0 0 + 44 23 1 0 0 0 0 + 23 24 2 0 0 0 0 + 41 24 1 0 0 0 0 + 39 25 1 0 0 0 0 + 25 26 2 0 0 0 0 + 36 26 1 0 0 0 0 + 30 27 1 0 0 0 0 + 33 28 2 0 0 0 0 + 28 29 1 0 0 0 0 + 29 30 2 0 0 0 0 + 31 30 1 0 0 0 0 + 32 31 2 0 0 0 0 + 33 32 1 0 0 0 0 + 35 33 1 0 0 0 0 + 35 34 2 0 0 0 0 + 36 35 1 0 0 0 0 + 37 36 2 0 0 0 0 + 38 37 1 0 0 0 0 + 38 39 2 0 0 0 0 + 40 39 1 0 0 0 0 + 41 40 1 0 0 0 0 + 42 41 2 0 0 0 0 + 43 42 1 0 0 0 0 + 43 44 2 0 0 0 0 + 44 45 1 0 0 0 0 +M STY 2 1 SRU 2 SRU +M SLB 2 1 1 2 2 +M SCN 2 1 HT 2 HT +M SAL 1 15 17 19 18 16 14 15 13 10 12 11 9 7 8 4 3 +M SAL 1 4 2 20 21 6 +M SBL 1 2 5 1 +M SDI 1 4 9.9509 -14.5560 9.9509 -13.7260 +M SDI 1 4 15.9793 -12.3033 15.9793 -13.1333 +M SMT 1 n +M SAL 2 15 44 45 43 42 41 40 39 38 37 36 35 34 33 32 31 +M SAL 2 7 30 29 28 26 25 24 23 +M SBL 2 2 23 30 +M SDI 2 4 8.3315 -17.6846 8.3315 -16.8266 +M SDI 2 4 17.3170 -19.6847 17.3170 -20.5427 +M SMT 2 n +M END +> +PEX0051 + +$$$$ + + ACCLDraw09131615332D + + 45 48 0 0 0 0 0 0 0 0999 V2000 + 15.9954 -12.8294 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 15.2766 -11.5844 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 15.2766 -12.4144 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.5578 -12.8294 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1926 -14.0446 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 10.0226 -14.0446 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 13.1202 -12.8294 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.4014 -12.4144 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.8390 -12.4144 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.4014 -11.5844 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.8390 -11.5844 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.1202 -11.1694 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.6826 -11.1694 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 11.6826 -12.6070 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.2676 -13.3258 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.2676 -11.8882 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4376 -13.3258 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4376 -11.8882 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.0226 -12.6070 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.9638 -10.7544 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.0976 -10.4506 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1337 -17.1592 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 10.0802 -16.4447 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.9053 -16.4447 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.3801 -17.8738 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.7927 -18.5883 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 17.6797 -20.0173 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 15.0302 -19.3027 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.8551 -19.3027 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 16.2677 -20.0173 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.8552 -20.7317 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.0301 -20.7317 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.6176 -20.0172 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.3801 -20.7317 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 13.7926 -20.0172 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.3802 -19.3027 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.5551 -19.3027 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.1426 -18.5882 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.5552 -17.8738 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.1427 -17.1592 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 11.3177 -17.1592 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.9051 -17.8737 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.0801 -17.8737 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.6677 -17.1592 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.8427 -17.1592 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 3 1 1 0 0 0 0 + 3 2 2 0 0 0 0 + 4 3 1 0 0 0 0 + 9 4 1 0 0 0 0 + 6 5 1 0 0 0 0 + 17 6 1 0 0 0 0 + 21 13 1 0 0 0 0 + 20 13 1 0 0 0 0 + 13 10 1 0 0 0 0 + 7 8 2 0 0 0 0 + 7 9 1 0 0 0 0 + 8 10 1 0 0 0 0 + 9 11 2 0 0 0 0 + 10 12 2 0 0 0 0 + 11 12 1 0 0 0 0 + 16 13 1 0 0 0 0 + 14 15 2 0 0 0 0 + 14 16 1 0 0 0 0 + 15 17 1 0 0 0 0 + 16 18 2 0 0 0 0 + 17 19 2 0 0 0 0 + 18 19 1 0 0 0 0 + 45 22 1 0 0 0 0 + 44 23 1 0 0 0 0 + 23 24 2 0 0 0 0 + 41 24 1 0 0 0 0 + 39 25 1 0 0 0 0 + 25 26 2 0 0 0 0 + 36 26 1 0 0 0 0 + 30 27 1 0 0 0 0 + 33 28 2 0 0 0 0 + 28 29 1 0 0 0 0 + 29 30 2 0 0 0 0 + 31 30 1 0 0 0 0 + 32 31 2 0 0 0 0 + 33 32 1 0 0 0 0 + 35 33 1 0 0 0 0 + 35 34 2 0 0 0 0 + 36 35 1 0 0 0 0 + 37 36 2 0 0 0 0 + 38 37 1 0 0 0 0 + 38 39 2 0 0 0 0 + 40 39 1 0 0 0 0 + 41 40 1 0 0 0 0 + 42 41 2 0 0 0 0 + 43 42 1 0 0 0 0 + 43 44 2 0 0 0 0 + 44 45 1 0 0 0 0 +M STY 3 1 MOD 2 SRU 3 COP +M SLB 3 1 1 2 2 3 3 +M SST 1 3 BLO +M SCN 3 1 HT 2 HT 3 HT +M SAL 1 15 17 19 18 16 14 15 13 10 12 11 9 7 8 4 3 +M SAL 1 4 2 20 21 6 +M SBL 1 2 5 1 +M SDI 1 4 9.6076 -14.4596 9.6076 -13.6296 +M SDI 1 4 15.6360 -12.2069 15.6360 -13.0369 +M SAL 2 15 44 45 43 42 41 40 39 38 37 36 35 34 33 32 31 +M SAL 2 7 30 29 28 26 25 24 23 +M SBL 2 2 23 30 +M SDI 2 4 7.9882 -17.5882 7.9882 -16.7302 +M SDI 2 4 16.9737 -19.5883 16.9737 -20.4463 +M SMT 2 n +M SAL 3 15 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +M SAL 3 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 +M SAL 3 15 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 +M SDI 3 4 6.5164 -21.5140 6.5164 -9.7401 +M SDI 3 4 18.2969 -9.7401 18.2969 -21.5140 +M END +> +PEX0052 + +$$$$ + + ACCLDraw09131617132D + + 10 10 0 0 0 0 0 0 0 0999 V2000 + 12.7500 -15.6876 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.3376 -14.9731 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.5126 -14.9731 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.1000 -15.6873 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.5124 -16.4019 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.3374 -16.4019 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.2750 -15.6873 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 13.5750 -15.6876 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.4000 -15.6876 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.2250 -15.6876 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 1 6 2 0 0 0 0 + 2 3 2 0 0 0 0 + 3 4 1 0 0 0 0 + 4 5 2 0 0 0 0 + 5 6 1 0 0 0 0 + 4 7 1 0 0 0 0 + 1 8 1 0 0 0 0 + 8 9 3 0 0 0 0 + 9 10 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 8 1 2 3 4 5 6 8 9 +M SBL 1 2 7 10 +M SDI 1 4 14.7397 -15.2753 14.7397 -16.1003 +M SDI 1 4 10.7602 -16.0998 10.7604 -15.2748 +M SMT 1 n +M END +> +PEX0053 + +$$$$ + + ACCLDraw08271613092D + + 7 6 0 0 0 0 0 0 0 0999 V2000 + 10.9821 -5.2045 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 13.8573 -6.0345 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 13.1385 -6.4495 0.0000 N 0 0 3 0 0 0 0 0 0 0 0 0 + 12.4197 -6.0345 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.7009 -6.4495 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.9821 -6.0345 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.2633 -6.4495 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 6 1 2 0 0 0 0 + 2 3 1 0 0 0 0 + 3 4 1 0 0 0 0 + 4 5 1 0 0 0 0 + 5 6 1 0 0 0 0 + 6 7 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 5 5 6 1 4 3 +M SBL 1 2 6 2 +M SDI 1 4 10.6227 -6.6570 10.6227 -5.8270 +M SDI 1 4 13.4979 -5.8270 13.4979 -6.6570 +M SMT 1 n +M END +> +PEX0054 + +$$$$ + + ACCLDraw08271613092D + + 7 6 0 0 0 0 0 0 0 0999 V2000 + 10.9821 -5.2045 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 13.8573 -6.0345 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 13.1385 -6.4495 0.0000 N 0 0 3 0 0 0 0 0 0 0 0 0 + 12.4197 -6.0345 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.7009 -6.4495 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.9821 -6.0345 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.2633 -6.4495 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 6 1 2 0 0 0 0 + 2 3 1 0 0 0 0 + 3 4 1 0 0 0 0 + 4 5 1 0 0 0 0 + 5 6 1 0 0 0 0 + 6 7 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 5 5 6 1 4 3 +M SBL 1 2 6 2 +M SDI 1 4 10.6227 -6.6570 10.6227 -5.8270 +M SDI 1 4 13.4979 -5.8270 13.4979 -6.6570 +M SMT 1 n +M END +> +PEX0055 + +$$$$ +Structure #1 + ACCLDraw08271613552D + + 7 6 0 0 0 0 0 0 0 0999 V2000 + 13.1234 -8.8397 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.1463 -9.4302 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.1692 -8.8397 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 12.1006 -9.4302 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.0777 -8.8397 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 10.0549 -9.4302 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 12.1006 -10.6113 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 1 4 1 0 0 0 0 + 4 5 2 0 0 0 0 + 5 6 1 0 0 0 0 + 4 7 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 5 1 2 4 5 7 +M SBL 1 2 5 2 +M SDI 1 4 10.5663 -9.7255 10.5663 -8.5444 +M SDI 1 4 14.6577 -8.5444 14.6577 -9.7255 +M END +> +PEX0056 + +$$$$ + + ACCLDraw09131617332D + + 11 10 0 0 1 0 0 0 0 0999 V2000 + 11.6412 -15.6875 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 + 10.4512 -16.3875 0.0000 N 0 0 3 0 0 0 0 0 0 0 0 0 + 12.8312 -16.3875 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.6412 -14.2875 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.0912 -15.6875 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.2812 -16.3875 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 17.1012 -15.6175 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 15.2812 -17.7875 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 12.8312 -13.5875 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4512 -13.5875 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.3989 -15.9552 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 1 3 1 0 0 0 0 + 1 4 1 1 0 0 0 + 3 5 1 0 0 0 0 + 5 6 1 0 0 0 0 + 6 7 1 0 0 0 0 + 6 8 2 0 0 0 0 + 4 9 2 0 0 0 0 + 4 10 1 0 0 0 0 + 2 11 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 9 1 2 3 5 6 8 4 9 10 +M SBL 1 2 10 6 +M SDI 1 4 9.4250 -16.9323 9.4250 -15.4105 +M SDI 1 4 16.1912 -15.2416 16.1912 -16.7634 +M SMT 1 n +M END +> +CHEBI_8296 + +> +Poly-gamma-D-glutamate + +> +PEX0057 + +$$$$ + + ACCLDraw09131618142D + + 26 26 0 0 0 0 0 0 0 0999 V2000 + 11.3356 -8.9938 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.3356 -9.8188 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6211 -8.5813 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6211 -10.2313 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.9066 -8.9938 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.9066 -9.8188 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.0501 -8.5813 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.0501 -7.7563 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 12.7646 -8.9938 0.0000 N 0 0 3 0 0 0 0 0 0 0 0 0 + 12.0501 -10.2313 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.7646 -9.8188 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 12.0501 -11.0564 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1922 -10.2313 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1922 -11.0563 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4776 -9.8187 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1922 -8.5813 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1922 -7.7562 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 13.4792 -8.5814 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.1936 -8.9939 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.9081 -8.5815 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.6226 -8.9940 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 16.3371 -8.5815 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 17.0515 -8.9941 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 18.4976 -8.9941 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4777 -8.9937 0.0000 N 0 0 3 0 0 0 0 0 0 0 0 0 + 7.0025 -8.9937 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 2 1 1 0 0 0 0 + 3 1 2 0 0 0 0 + 4 2 2 0 0 0 0 + 5 3 1 0 0 0 0 + 6 4 1 0 0 0 0 + 6 5 2 0 0 0 0 + 8 7 2 0 0 0 0 + 9 7 1 0 0 0 0 + 1 7 1 0 0 0 0 + 11 10 1 0 0 0 0 + 12 10 2 0 0 0 0 + 2 10 1 0 0 0 0 + 14 13 2 0 0 0 0 + 15 13 1 0 0 0 0 + 6 13 1 0 0 0 0 + 17 16 2 0 0 0 0 + 5 16 1 0 0 0 0 + 9 18 1 0 0 0 0 + 18 19 1 0 0 0 0 + 19 20 1 0 0 0 0 + 20 21 1 0 0 0 0 + 21 22 1 0 0 0 0 + 22 23 1 0 0 0 0 + 23 24 1 0 0 0 0 + 16 25 1 0 0 0 0 + 25 26 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 15 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +M SAL 1 9 16 17 18 19 20 21 22 23 25 +M SBL 1 2 24 26 +M SDI 1 4 7.8695 -9.4050 7.8711 -8.5800 +M SDI 1 4 17.6462 -8.5804 17.6477 -9.4054 +M SMT 1 n +M END +> +PEX0058 + +$$$$ + + ACCLDraw09131618192D + + 6 5 0 0 0 0 0 0 0 0999 V2000 + 11.5125 -15.6998 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 12.3375 -15.6998 0.0000 P 0 0 3 0 0 0 0 0 0 0 0 0 + 12.3375 -16.4636 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 + 12.3375 -14.9115 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 + 13.1625 -15.6998 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 13.9875 -15.6998 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 4 1 0 0 0 0 + 5 2 2 0 0 0 0 + 5 6 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 4 2 3 4 5 +M SBL 1 2 1 5 +M SDI 1 4 13.5022 -15.2873 13.5022 -16.1123 +M SDI 1 4 11.9978 -16.1123 11.9978 -15.2873 +M SMT 1 n +M END +> +PEX0059 + +$$$$ + + Marvin 10080915352D + + 8 6 0 0 0 0 999 V2000 + -1.7900 0.3830 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -1.0755 0.7955 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.1862 0.7711 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + -3.1016 0.3875 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + -1.0755 1.6205 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -0.3611 2.0330 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + -1.7900 2.0330 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 + -3.0643 1.8268 0.0000 Na 0 3 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 1 4 1 0 0 0 0 + 2 5 1 0 0 0 0 + 5 6 2 0 0 0 0 + 5 7 1 0 0 0 0 +M CHG 2 7 -1 8 1 +M STY 1 1 SRU +M SCN 1 1 HT +M SAL 1 6 1 2 5 6 7 8 +M SDI 1 4 -0.5480 1.1979 -0.5639 0.3730 +M SDI 1 4 -2.3315 -0.0276 -2.3286 0.7974 +M SBL 1 2 3 2 +M SMT 1 n +M END +> +CHEBI_53269 + +> +poly(sodium acrylate) macromolecule + +> +PEX0060 + +$$$$ + + ACCLDraw09131618272D + + 21 23 0 0 0 0 0 0 0 0999 V2000 + 11.2149 -16.1001 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.5004 -16.5126 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.5004 -17.3376 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.2149 -17.7501 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.9294 -17.3376 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.9294 -16.5126 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.6439 -16.1001 0.0000 N 0 0 3 0 0 0 0 0 0 0 0 0 + 12.6439 -15.2750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.3583 -16.5126 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.0728 -16.1001 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.3583 -17.3376 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.0728 -17.7501 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.7874 -17.3376 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.7874 -16.5126 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.6439 -13.6250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.9294 -14.0374 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.9294 -14.8626 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.3583 -14.8626 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.3583 -14.0374 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.6055 -17.3376 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 15.8945 -17.3376 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 2 0 0 0 0 + 2 3 1 0 0 0 0 + 3 4 2 0 0 0 0 + 4 5 1 0 0 0 0 + 5 6 2 0 0 0 0 + 1 6 1 0 0 0 0 + 6 7 1 0 0 0 0 + 7 8 1 0 0 0 0 + 7 9 1 0 0 0 0 + 10 9 2 0 0 0 0 + 9 11 1 0 0 0 0 + 11 12 2 0 0 0 0 + 12 13 1 0 0 0 0 + 13 14 2 0 0 0 0 + 10 14 1 0 0 0 0 + 17 8 2 0 0 0 0 + 8 18 1 0 0 0 0 + 15 16 2 0 0 0 0 + 16 17 1 0 0 0 0 + 18 19 2 0 0 0 0 + 15 19 1 0 0 0 0 + 3 20 1 0 0 0 0 + 13 21 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 15 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +M SAL 1 4 16 17 18 19 +M SBL 1 2 22 23 +M SDI 1 4 15.2387 -16.9201 15.2477 -17.7451 +M SDI 1 4 10.1312 -17.7495 10.1326 -16.9245 +M SMT 1 n +M END +> +PEX0061 + +$$$$ + + ACCLDraw09131618312D + + 12 11 0 0 0 0 0 0 0 0999 V2000 + 11.1059 -14.3451 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 12.0857 -14.3451 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.8002 -14.7576 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 14.0749 -14.7576 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 12.8002 -15.5826 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.0857 -15.9951 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.0857 -16.8201 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.8002 -17.2326 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.5147 -16.8201 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.5147 -15.9951 0.0000 N 0 3 0 0 0 0 0 0 0 0 0 0 + 14.1409 -16.8790 0.0000 I 0 5 0 0 0 0 0 0 0 0 0 0 + 14.2291 -15.5826 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 3 4 1 0 0 0 0 + 3 5 1 0 0 0 0 + 5 6 2 0 0 0 0 + 5 10 1 0 0 0 0 + 6 7 1 0 0 0 0 + 7 8 2 0 0 0 0 + 8 9 1 0 0 0 0 + 9 10 2 0 0 0 0 + 10 12 1 0 0 0 0 +M CHG 2 10 1 11 -1 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 10 2 3 5 6 7 8 9 10 11 12 +M SBL 1 2 1 3 +M SDI 1 4 13.3156 -14.3332 13.3345 -15.1580 +M SDI 1 4 11.6821 -14.7575 11.6823 -13.9325 +M SMT 1 n +M END +> +PEX0062 + +$$$$ + + ACCLDraw09131621132D + + 15 15 0 0 0 0 0 0 0 0999 V2000 + 9.9774 -15.4938 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6918 -15.9063 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.4063 -15.4938 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6918 -16.7313 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 11.6593 -14.7086 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.0749 -15.9771 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 12.7412 -15.4905 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.4843 -14.7086 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.4556 -15.9030 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.1701 -15.4905 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 13.4556 -16.7280 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 14.8846 -15.9030 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.5990 -15.4905 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 16.6186 -15.4905 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 8.8815 -15.4938 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 2 3 1 0 0 0 0 + 2 4 2 0 0 0 0 + 5 3 2 0 0 0 0 + 3 6 1 0 0 0 0 + 5 8 1 0 0 0 0 + 6 7 1 0 0 0 0 + 7 8 2 0 0 0 0 + 7 9 1 0 0 0 0 + 9 10 1 0 0 0 0 + 9 11 2 0 0 0 0 + 2 1 1 0 0 0 0 + 10 12 1 0 0 0 0 + 12 13 1 0 0 0 0 + 13 14 1 0 0 0 0 + 1 15 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 13 1 2 3 4 5 6 7 8 9 10 11 12 13 +M SBL 1 2 15 14 +M SDI 1 4 16.0237 -15.0830 16.0140 -15.9079 +M SDI 1 4 9.5235 -15.9034 9.5288 -15.0784 +M SMT 1 n +M END +> +PEX0063 + +$$$$ + + ACCLDraw06211613102D + + 13 14 0 0 0 0 0 0 0 0999 V2000 + 8.8662 -14.7297 0.0000 Zn 0 2 0 0 0 0 0 0 0 0 0 0 + 5.6699 -14.1183 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4194 -13.6856 0.0000 N 0 0 3 0 0 0 0 0 0 0 0 0 + 7.1689 -14.1183 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1689 -14.9838 0.0000 S 0 0 0 0 0 0 0 0 0 0 0 0 + 7.9184 -13.6856 0.0000 S 0 5 0 0 0 0 0 0 0 0 0 0 + 12.8120 -13.6856 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.0626 -14.1183 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.3131 -13.6856 0.0000 N 0 0 3 0 0 0 0 0 0 0 0 0 + 10.5636 -14.1183 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.5636 -14.9838 0.0000 S 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8141 -13.6856 0.0000 S 0 5 0 0 0 0 0 0 0 0 0 0 + 13.5615 -14.1183 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 3 2 1 0 0 0 0 + 3 4 1 0 0 0 0 + 4 5 1 0 0 0 0 + 4 6 2 0 0 0 0 + 1 6 1 0 0 0 0 + 5 1 1 0 0 0 0 + 8 7 1 0 0 0 0 + 9 8 1 0 0 0 0 + 9 10 1 0 0 0 0 + 10 11 1 0 0 0 0 + 10 12 2 0 0 0 0 + 1 12 1 0 0 0 0 + 11 1 1 0 0 0 0 + 7 13 1 0 0 0 0 +M CHG 3 1 2 6 -1 12 -1 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SBT 1 1 0 +M SAL 1 11 4 3 5 1 6 12 10 9 8 7 11 +M SBL 1 2 1 14 +M SDI 1 4 6.0447 -14.4344 6.0447 -13.3696 +M SDI 1 4 13.1868 -13.3696 13.1868 -14.4344 +M SMT 1 n +M END +> +PEX0064 + +$$$$ + + Marvin 10140911312D + + 7 6 0 0 0 0 999 V2000 + -2.4677 0.4911 0.0000 V 0 0 0 0 0 0 0 0 0 0 0 0 + -1.7532 0.0786 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + -2.4677 1.3161 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + -2.4677 -0.3339 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 + -3.1821 0.0786 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + -0.3714 0.1018 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + -4.3568 0.0877 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 1 3 2 0 0 0 0 + 1 4 1 0 0 0 0 + 1 5 1 0 0 0 0 + 2 6 1 0 0 0 0 + 5 7 1 0 0 0 0 +M CHG 1 4 -1 +M STY 1 1 SRU +M SCN 1 1 HT +M SAL 1 5 1 2 3 4 5 +M SDI 1 4 -3.6690 -0.3301 -3.6626 0.4948 +M SDI 1 4 -1.1911 0.5006 -1.1773 -0.3243 +M SBL 1 2 5 6 +M SMT 1 n +M END +> +CHEBI_53340 + +> +polyvanadate + +> +Chemically, it is not polyvanadate but a peroxo compound + +> +PEX0065 + +$$$$ + + ACCLDraw09281611432D + + 20 22 0 0 0 0 0 0 0 0999 V2000 + 14.4751 -7.4082 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 6.1206 -8.3702 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8950 -7.5882 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.2408 -8.5730 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.1151 -8.2482 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.6537 -6.8128 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0687 -6.0940 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0687 -7.5316 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8987 -6.0940 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8987 -7.5316 0.0000 N 0 0 3 0 0 0 0 0 0 0 0 0 + 10.3137 -6.8128 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.5587 -7.5316 0.0000 N 0 0 3 0 0 0 0 0 0 0 0 0 + 11.1437 -6.8128 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.3887 -7.5316 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.5587 -6.0940 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.8037 -6.8128 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.3887 -6.0940 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7365 -8.7378 0.0000 Cu 0 0 0 0 0 0 0 0 0 0 0 0 + 9.9365 -9.9278 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 + 11.7365 -10.0178 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 + 20 18 1 0 0 0 0 + 19 18 1 0 0 0 0 + 18 10 1 0 0 0 0 + 18 12 1 0 0 0 0 + 5 1 1 0 0 0 0 + 3 2 1 0 0 0 0 + 3 4 1 0 0 0 0 + 4 8 1 0 0 0 0 + 5 14 1 0 0 0 0 + 13 11 1 0 0 0 0 + 6 7 2 0 0 0 0 + 6 8 1 0 0 0 0 + 7 9 1 0 0 0 0 + 8 10 2 0 0 0 0 + 9 11 2 0 0 0 0 + 10 11 1 0 0 0 0 + 12 13 2 0 0 0 0 + 12 14 1 0 0 0 0 + 13 15 1 0 0 0 0 + 14 16 2 0 0 0 0 + 15 17 2 0 0 0 0 + 16 17 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 15 4 8 10 11 9 7 6 13 15 17 16 14 12 18 19 +M SAL 1 3 20 5 3 +M SBL 1 2 6 5 +M SDI 1 4 7.0418 -7.9792 5.9739 -7.9792 +M SDI 1 4 13.7951 -7.2943 13.7951 -8.3622 +M SMT 1 n +M END +> +PEX0066 + +$$$$ + + ACCLDraw09161617272D + + 11 12 0 0 0 0 0 0 0 0999 V2000 + 12.0839 -7.3169 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 11.4933 -8.3397 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.4933 -6.2940 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.3122 -8.3397 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.3122 -6.2940 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.7217 -7.3169 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 12.9190 -6.4817 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 13.7542 -7.3169 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.9190 -8.1520 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.9190 -5.3006 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5406 -7.3169 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 2 1 1 0 0 0 0 + 3 1 1 0 0 0 0 + 4 2 2 0 0 0 0 + 5 3 1 0 0 0 0 + 6 4 1 0 0 0 0 + 6 5 1 0 0 0 0 + 1 7 1 0 0 0 0 + 7 8 1 0 0 0 0 + 8 9 2 0 0 0 0 + 9 1 1 0 0 0 0 + 7 10 1 0 0 0 0 + 6 11 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 9 1 2 3 4 5 6 7 8 9 +M SBL 1 2 12 11 +M SDI 1 4 9.1312 -7.9074 9.1312 -6.7263 +M SDI 1 4 12.3285 -5.8911 13.5096 -5.8911 +M SMT 1 n +M END +> +PEX0067 + +$$$$ + + ACCLDraw09161617372D + + 5 4 0 0 0 0 0 0 0 0999 V2000 + 8.9814 -6.5500 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 10.1625 -6.5500 0.0000 N 0 3 3 0 0 0 0 0 0 0 0 0 + 10.1625 -5.3689 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.3436 -6.5500 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 10.1625 -7.7311 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 4 1 0 0 0 0 + 2 5 1 0 0 0 0 +M CHG 1 2 1 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 3 5 2 3 +M SBL 1 2 1 3 +M SDI 1 4 9.5720 -7.1406 9.5720 -5.9595 +M SDI 1 4 10.7530 -5.9595 10.7530 -7.1405 +M SMT 1 n +M END +> +PEX0068 + +$$$$ + + ACCLDraw09161618502D + + 4 3 0 0 0 0 0 0 0 0999 V2000 + 5.2813 -7.7500 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 6.6563 -7.0625 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7188 -8.2188 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0313 -7.3750 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 2 0 0 0 0 + 3 4 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 2 3 2 +M SBL 1 2 1 3 +M SDI 1 4 5.9688 -8.1842 5.9688 -6.6283 +M SDI 1 4 8.3750 -7.0189 8.3750 -8.5749 +M SMT 1 n +M END +> +PEX0069 + +$$$$ + + ACCLDraw09161618512D + + 4 3 0 0 0 0 0 0 0 0999 V2000 + 6.2500 -4.6250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8750 -4.6250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.1563 -5.7188 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 8.7102 -5.4602 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 2 0 0 0 0 + 1 3 1 0 0 0 0 + 2 4 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 2 2 1 +M SBL 1 2 2 3 +M SDI 1 4 6.4286 -5.1719 4.9776 -5.1719 +M SDI 1 4 9.0181 -5.0426 7.5671 -5.0426 +M SMT 1 n +M END +> +PEX0070 + +$$$$ + + ACCLDraw08071613132D + + 20 17 0 0 0 0 0 0 0 0999 V2000 + 7.4970 -11.7980 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 8.5078 -13.5898 0.0000 O 0 5 0 0 0 0 0 0 0 0 0 0 + 7.4970 -12.9788 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4625 -13.5487 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 5.5099 -11.1870 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5552 -11.2280 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.3160 -11.6390 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 8.1684 -14.8337 0.0000 Na 0 3 0 0 0 0 0 0 0 0 0 0 + 14.2706 -11.9181 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.2706 -13.0993 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.2934 -13.6898 0.0000 N 0 3 0 0 0 0 0 0 0 0 0 0 + 16.3162 -13.0993 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 16.3162 -11.9181 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.2934 -11.3276 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.2934 -14.7337 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 17.3391 -11.3276 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 18.3620 -11.9181 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 19.3848 -11.3276 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 13.2477 -11.3276 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 17.4184 -14.1087 0.0000 Cl 0 5 0 0 0 0 0 0 0 0 0 0 + 2 3 1 0 0 0 0 + 4 3 2 0 0 0 0 + 3 1 1 0 0 0 0 + 1 5 1 0 0 0 0 + 1 6 1 0 0 0 0 + 6 7 1 0 0 0 0 + 13 14 1 0 0 0 0 + 12 13 2 0 0 0 0 + 11 12 1 0 0 0 0 + 10 11 2 0 0 0 0 + 9 10 1 0 0 0 0 + 9 14 2 0 0 0 0 + 11 15 1 0 0 0 0 + 13 16 1 0 0 0 0 + 16 17 1 0 0 0 0 + 17 18 1 0 0 0 0 + 9 19 1 0 0 0 0 +M CHG 4 2 -1 8 1 11 1 20 -1 +M STY 3 1 SRU 2 SRU 3 COP +M SLB 3 1 1 2 2 3 3 +M SST 1 3 RAN +M SCN 3 1 HT 2 HT 3 HT +M SAL 1 6 6 1 3 4 2 8 +M SBL 1 2 4 6 +M SDI 1 4 6.5034 -12.2119 6.5034 -10.7732 +M SDI 1 4 9.4356 -10.7142 9.4356 -12.1529 +M SMT 1 n +M SAL 2 10 14 9 10 11 12 13 16 17 15 20 +M SBL 2 2 17 16 +M SDI 2 4 13.7591 -12.2548 13.7591 -10.9909 +M SDI 2 4 18.8734 -10.9909 18.8734 -12.2548 +M SMT 2 n +M SAL 3 15 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +M SAL 3 5 16 17 18 19 20 +M SDI 3 4 4.7276 -15.6124 4.7276 -10.1418 +M SDI 3 4 20.1671 -10.1418 20.1671 -15.6124 +M END +> +PEX0071 + +$$$$ + + ACCLDraw09261609572D + + 3 2 0 0 0 0 0 0 0 0999 V2000 + 5.0625 -4.5938 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.2436 -4.5938 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.8342 -3.5709 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 2 0 0 0 0 + 2 3 1 0 0 0 0 +M STY 1 1 MON +M SLB 1 1 1 +M SAL 1 3 1 2 3 +M SDI 1 4 4.2802 -5.3760 4.2802 -2.7886 +M SDI 1 4 7.6164 -2.7886 7.6164 -5.3760 +M END +> +PEX0072 + +$$$$ + + ACCLDraw09261610002D + + 5 4 0 0 0 0 0 0 0 0999 V2000 + 6.0581 -5.4002 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1820 -4.7667 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 7.1939 -3.4741 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.2940 -5.4259 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9461 -4.7410 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 2 4 1 0 0 0 0 + 1 5 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 3 2 1 3 +M SBL 1 2 4 3 +M SDI 1 4 5.5021 -5.7166 5.5021 -4.4246 +M SDI 1 4 7.7380 -4.4503 7.7380 -5.7423 +M SMT 1 n +M END +> +PEX0073 + +$$$$ + + ACCLDraw09261618062D + + 3 2 0 0 0 0 0 0 0 0999 V2000 + 11.3988 -16.1386 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.5799 -16.1386 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.1705 -15.1157 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 2 0 0 0 0 + 2 3 1 0 0 0 0 +M END +> +PEX0074 + +$$$$ + + ACCLDraw09261610152D + + 8 8 0 0 0 0 0 0 0 0999 V2000 + 10.2532 -6.5058 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.5160 -7.6573 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.1890 -5.9934 0.0000 N 0 0 3 0 0 0 0 0 0 0 0 0 + 8.1249 -6.5058 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8621 -7.6573 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.5985 -8.5807 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.7796 -8.5807 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.1766 -5.7694 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2 1 1 0 0 0 0 + 3 1 1 0 0 0 0 + 4 3 1 0 0 0 0 + 5 4 1 0 0 0 0 + 5 6 1 0 0 0 0 + 6 7 1 0 0 0 0 + 7 2 1 0 0 0 0 + 1 8 2 0 0 0 0 +M STY 1 1 MON +M SLB 1 1 1 +M SAL 1 8 1 2 3 4 5 6 7 8 +M SDI 1 4 7.0798 -9.3629 7.0798 -4.9182 +M SDI 1 4 11.8665 -4.9182 11.8665 -9.3629 +M END +> +PEX0075 + +$$$$ + + ACCLDraw09261610172D + + 10 9 0 0 0 0 0 0 0 0999 V2000 + 8.2160 -7.6703 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2378 -7.0778 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.2617 -7.6665 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.2835 -7.0741 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.3074 -7.6628 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.3292 -7.0704 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.3531 -7.6591 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.3749 -7.0666 0.0000 N 0 0 3 0 0 0 0 0 0 0 0 0 + 16.3989 -7.6553 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2378 -5.8967 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2 1 1 0 0 0 0 + 3 2 1 0 0 0 0 + 4 3 1 0 0 0 0 + 5 4 1 0 0 0 0 + 6 5 1 0 0 0 0 + 7 6 1 0 0 0 0 + 8 7 1 0 0 0 0 + 9 8 1 0 0 0 0 + 2 10 2 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 8 10 2 3 4 5 6 7 8 +M SBL 1 2 1 8 +M SDI 1 4 8.7269 -7.9646 8.7269 -6.7835 +M SDI 1 4 15.8869 -6.7704 15.8869 -7.9515 +M SMT 1 n +M END +> +PEX0076 + +$$$$ + + ACCLDraw09261618112D + + 8 8 0 0 0 0 0 0 0 0999 V2000 + 13.0647 -14.9923 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.3275 -16.1438 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.0005 -14.4799 0.0000 N 0 0 3 0 0 0 0 0 0 0 0 0 + 10.9364 -14.9923 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6736 -16.1438 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.4100 -17.0672 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.5911 -17.0672 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.9881 -14.2559 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 2 1 1 0 0 0 0 + 3 1 1 0 0 0 0 + 4 3 1 0 0 0 0 + 5 4 1 0 0 0 0 + 5 6 1 0 0 0 0 + 6 7 1 0 0 0 0 + 7 2 1 0 0 0 0 + 1 8 2 0 0 0 0 +M END +> +PEX0077 + +$$$$ + + ACCLDraw12101621522D + + 22 25 0 0 0 0 0 0 0 0999 V2000 + 13.9838 -7.7892 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.9988 -8.3921 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 16.0284 -7.8145 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 16.0430 -6.6341 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.0280 -6.0312 0.0000 S 0 0 0 0 0 0 0 0 0 0 0 0 + 13.9984 -6.6087 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 17.0434 -8.4174 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 18.0731 -7.8398 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 18.0877 -6.6594 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 17.0727 -6.0565 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.9834 -6.0058 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.9538 -6.5834 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.9391 -7.7639 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.9541 -8.3668 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 19.1173 -6.0818 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0792 -5.1101 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 7.9474 -5.9115 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0755 -5.5603 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 9.6783 -4.5453 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.8299 -4.8050 0.0000 N 0 0 3 0 0 0 0 0 0 0 0 0 + 9.8545 -6.4473 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.9387 -5.9805 0.0000 C 0 0 3 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 3 2 1 0 0 0 0 + 4 3 2 0 0 0 0 + 5 4 1 0 0 0 0 + 6 5 1 0 0 0 0 + 1 6 2 0 0 0 0 + 8 7 2 0 0 0 0 + 9 8 1 0 0 0 0 + 10 9 2 0 0 0 0 + 4 10 1 0 0 0 0 + 3 7 1 0 0 0 0 + 12 11 2 0 0 0 0 + 13 12 1 0 0 0 0 + 14 13 2 0 0 0 0 + 1 14 1 0 0 0 0 + 6 11 1 0 0 0 0 + 9 15 1 0 0 0 0 + 17 16 1 0 0 0 0 + 18 17 1 0 0 0 0 + 21 18 1 0 0 0 0 + 18 19 1 0 0 0 0 + 19 20 1 0 0 0 0 + 20 22 1 0 0 0 0 + 22 21 1 0 0 0 0 + 22 12 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 15 18 19 20 22 12 13 14 1 6 11 5 4 10 9 8 +M SAL 1 5 7 3 2 21 17 +M SBL 1 2 18 17 +M SDI 1 4 7.5133 -6.1011 7.5133 -4.9205 +M SDI 1 4 18.6025 -5.7803 18.6025 -6.9609 +M SMT 1 n +M END +> +PEX0078 + +$$$$ + + ACCLDraw12101622352D + + 11 10 0 0 0 0 0 0 0 0999 V2000 + 6.6807 -4.7707 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7089 -5.3520 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.7267 -4.7520 0.0000 N 0 0 3 0 0 0 0 0 0 0 0 0 + 9.7552 -5.3334 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7729 -4.7334 0.0000 S 0 0 0 0 0 0 0 0 0 0 0 0 + 11.8014 -5.3149 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.8192 -4.7149 0.0000 Si 0 0 0 0 0 0 0 0 0 0 0 0 + 13.8424 -5.3056 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.8655 -4.7149 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 15.8887 -5.3056 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 16.9119 -4.7149 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 3 4 1 0 0 0 0 + 4 5 1 0 0 0 0 + 5 6 1 0 0 0 0 + 6 7 1 0 0 0 0 + 7 8 1 0 0 0 0 + 8 9 1 0 0 0 0 + 9 10 1 0 0 0 0 + 10 11 1 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 9 3 2 4 5 6 7 8 9 10 +M SBL 1 2 1 10 +M SDI 1 4 7.1948 -5.6520 7.1948 -4.4706 +M SDI 1 4 16.4003 -4.4195 16.4003 -5.6010 +M SMT 1 n +M END +> +PEX0079 + +$$$$ +Structure #1 + ACCLDraw12111615232D + + 26 30 0 0 0 0 0 0 0 0999 V2000 + 12.5206 -8.3042 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.4873 -7.7321 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.4660 -6.5512 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4327 -5.9791 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.4206 -6.5879 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.3873 -6.0159 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3753 -6.6247 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3419 -6.0526 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.3298 -6.6615 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.3511 -7.8424 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 4.3390 -8.4513 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 6.3844 -8.4145 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3964 -7.8056 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4298 -8.3777 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.4418 -7.7688 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.4781 -5.9423 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.5114 -6.5144 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.3749 -5.7084 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.8751 -4.6382 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.7029 -4.7828 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 14.5521 -3.6704 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 15.7288 -3.7728 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 16.2284 -4.8429 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 17.4051 -4.9452 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 + 15.5515 -5.8108 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 13.5327 -7.6953 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 2 0 0 0 0 + 3 4 1 0 0 0 0 + 5 4 1 0 0 0 0 + 6 5 2 0 0 0 0 + 6 7 1 0 0 0 0 + 8 7 1 0 0 0 0 + 9 8 2 0 0 0 0 + 9 10 1 0 0 0 0 + 10 11 1 0 0 0 0 + 12 10 2 0 0 0 0 + 12 13 1 0 0 0 0 + 13 7 2 0 0 0 0 + 14 13 1 0 0 0 0 + 14 15 2 0 0 0 0 + 15 5 1 0 0 0 0 + 16 3 1 0 0 0 0 + 17 16 2 0 0 0 0 + 17 18 1 0 0 0 0 + 19 18 2 0 0 0 0 + 20 19 1 0 0 0 0 + 20 16 1 0 0 0 0 + 21 19 1 0 0 0 0 + 22 21 2 0 0 0 0 + 22 23 1 0 0 0 0 + 23 24 1 0 0 0 0 + 25 23 2 0 0 0 0 + 25 18 1 0 0 0 0 + 26 17 1 0 0 0 0 + 1 26 2 0 0 0 0 +M STY 1 1 SRU +M SLB 1 1 1 +M SCN 1 1 HT +M SAL 1 15 12 13 14 15 5 6 7 8 9 10 4 3 16 20 19 +M SAL 1 9 21 22 23 25 18 17 26 1 2 +M SBL 1 2 10 26 +M SDI 1 4 4.8450 -8.7374 4.8450 -7.5563 +M SDI 1 4 16.8168 -4.3035 16.8168 -5.4846 +M SMT 1 n +M END +> +PEX0080 + +$$$$ diff --git a/INCHI-1-TEST/samples/readme.txt b/INCHI-1-TEST/samples/readme.txt index 9835ebd..4cf399c 100644 --- a/INCHI-1-TEST/samples/readme.txt +++ b/INCHI-1-TEST/samples/readme.txt @@ -1,16 +1,19 @@ /* * International Chemical Identifier (InChI) * Version 1 - * Software version 1.04 - * September 9, 2011 + * Software version 1.05 + * January 27, 2017 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. * * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 + * International Chemical Identifier (InChI) * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it @@ -18,73 +21,42 @@ * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust - * c/o FIZ CHEMIE Berlin + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. + * or e-mail to alan@inchi-trust.org * */ - This directory contains a number of sample files for testing InChI/InChIKey programs. In particular, included are the files containing (most of) chemical -structures drawn in InChI User's Guide and Technical Manual. +structures drawn in InChI User's Guide and Technical Manual, as well as +polymers (new in v. 1.05). -========= - FILES -========= -readme.txt this file - -UserGuide SUB-DIRECTORY - (S)-Glutamic_Acid.mol 3 sample files -- illustrations from the - benzoicacid.mol InChI User's Guide +UserGuide SUB-DIRECTORY + (S)-Glutamic_Acid.mol 3 sample files -- illustrations from the + benzoicacid.mol InChI User's Guide Benzoic_Acid_Sodium_salt.mol -TechMan SUB-DIRECTORY - SamplesTechMan.sdf Examples of structures from the - Technical Manual - SamplesTechMan.zip Individual structures from the - Technical Manual - -Misc SUB-DIRECTORY - Samples.sdf Sample structure files merged into one - SDfile - Samples.zip Individual structure sample files - - - -Note. ------- -When using winchi-1 exe, you may enter word - -NAME - -into "Structure ID Header" of an "Open" dialog -to view structure name in Samples and SamplesTechMan collections. - - - - +TechMan SUB-DIRECTORY + SamplesTechMan.sdf Examples of structures from the + Technical Manual -========= - LINKS -========= +Misc SUB-DIRECTORY + Samples.sdf Sample structure files merged into one + SDfile -IUPAC http://www.iupac.org/inchi -InChI Trust http://www.inchi-trust.org -InChI discussion group https://lists.sourceforge.net/lists/listinfo/inchi-discuss +Polymers SUB-DIRECTORY + pex.sdf Polymer examples corresponding to Table 2 + of InChI v. 1.05 Release Notes (RelNotes.pdf) diff --git a/INCHI-1-TEST/test/InChI_TestSet-result.zip b/INCHI-1-TEST/test/InChI_TestSet-result.zip index 14b3733..efcadb2 100644 Binary files a/INCHI-1-TEST/test/InChI_TestSet-result.zip and b/INCHI-1-TEST/test/InChI_TestSet-result.zip differ diff --git a/INCHI-1-TEST/test/TestSet2InChI.bat b/INCHI-1-TEST/test/TestSet2InChI.bat index c80a5ac..ffa1404 100644 --- a/INCHI-1-TEST/test/TestSet2InChI.bat +++ b/INCHI-1-TEST/test/TestSet2InChI.bat @@ -1,42 +1,39 @@ REM REM Standard InChI REM -inchi-1 InChI_TestSet.sdf InChI_TestSet-std-01.txt InChI_TestSet-std-01.log NUL /AuxNone /NoLabels /Key -inchi-1 InChI_TestSet.sdf InChI_TestSet-std-02.txt InChI_TestSet-std-02.log NUL /AuxNone /NoLabels /NEWPSOFF -inchi-1 InChI_TestSet.sdf InChI_TestSet-std-03.txt InChI_TestSet-std-03.log NUL /AuxNone /NoLabels /SNon /Key -inchi-1 InChI_TestSet.sdf InChI_TestSet-std-04.txt InChI_TestSet-std-04.log NUL /AuxNone /NoLabels /DoNotAddH -inchi-1 InChI_TestSet.sdf InChI_TestSet-std-05.txt InChI_TestSet-std-05.log NUL /AuxNone /NoLabels /SNon /DoNotAddH -inchi-1 InChI_TestSet.sdf InChI_TestSet-std-06.txt InChI_TestSet-std-06.log NUL /AuxNone /NoLabels /NEWPSOFF /DoNotAddH +inchi-1.exe InChI_TestSet.sdf its-std-01.txt its-std-01.log NUL /AuxNone /NoLabels /Key +inchi-1.exe InChI_TestSet.sdf its-std-02.txt its-std-02.log NUL /AuxNone /NoLabels /NEWPSOFF +inchi-1.exe InChI_TestSet.sdf its-std-03.txt its-std-03.log NUL /AuxNone /NoLabels /SNon /Key +inchi-1.exe InChI_TestSet.sdf its-std-04.txt its-std-04.log NUL /AuxNone /NoLabels /DoNotAddH +inchi-1.exe InChI_TestSet.sdf its-std-05.txt its-std-05.log NUL /AuxNone /NoLabels /SNon /DoNotAddH REM REM Non-standard InChI REM -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-01.txt InChI_TestSet-non-std-01.log NUL /AuxNone /NoLabels /SUU /SLUUD -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-02.txt InChI_TestSet-non-std-02.log NUL /AuxNone /NoLabels /SRel /Key -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-03.txt InChI_TestSet-non-std-03.log NUL /AuxNone /NoLabels /SRac -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-04.txt InChI_TestSet-non-std-04.log NUL /AuxNone /NoLabels /SUU /SLUUD /SUCF -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-05.txt InChI_TestSet-non-std-05.log NUL /AuxNone /NoLabels /NEWPSOFF /SRel -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-06.txt InChI_TestSet-non-std-06.log NUL /AuxNone /NoLabels /NEWPSOFF /SRac -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-07.txt InChI_TestSet-non-std-07.log NUL /AuxNone /NoLabels /NEWPSOFF /SLUUD /SUCF -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-08.txt InChI_TestSet-non-std-08.log NUL /AuxNone /NoLabels /FixedH /Key -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-09.txt InChI_TestSet-non-std-09.log NUL /AuxNone /NoLabels /NEWPSOFF /FixedH -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-10.txt InChI_TestSet-non-std-10.log NUL /AuxNone /NoLabels /FixedH /SNon -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-11.txt InChI_TestSet-non-std-11.log NUL /AuxNone /NoLabels /FixedH /SRel -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-12.txt InChI_TestSet-non-std-12.log NUL /AuxNone /NoLabels /RecMet /Key -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-13.txt InChI_TestSet-non-std-13.log NUL /AuxNone /NoLabels /NEWPSOFF /RecMet -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-14.txt InChI_TestSet-non-std-14.log NUL /AuxNone /NoLabels /RecMet /SNon -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-15.txt InChI_TestSet-non-std-15.log NUL /AuxNone /NoLabels /RecMet /SRel -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-16.txt InChI_TestSet-non-std-16.log NUL /AuxNone /NoLabels /FixedH /RecMet /Key -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-17.txt InChI_TestSet-non-std-17.log NUL /AuxNone /NoLabels /NEWPSOFF /FixedH /RecMet -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-18.txt InChI_TestSet-non-std-18.log NUL /AuxNone /NoLabels /FixedH /RecMet /SNon -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-19.txt InChI_TestSet-non-std-19.log NUL /AuxNone /NoLabels /FixedH /RecMet /SRel -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-20.txt InChI_TestSet-non-std-20.log NUL /AuxNone /NoLabels /KET /Key -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-21.txt InChI_TestSet-non-std-21.log NUL /AuxNone /NoLabels /KET /SNon -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-22.txt InChI_TestSet-non-std-22.log NUL /AuxNone /NoLabels /KET /SRel -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-23.txt InChI_TestSet-non-std-23.log NUL /AuxNone /NoLabels /15T /Key -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-24.txt InChI_TestSet-non-std-24.log NUL /AuxNone /NoLabels /15T /SNon -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-25.txt InChI_TestSet-non-std-25.log NUL /AuxNone /NoLabels /15T /SRel -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-26.txt InChI_TestSet-non-std-26.log NUL /AuxNone /NoLabels /KET /15T -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-27.txt InChI_TestSet-non-std-27.log NUL /AuxNone /NoLabels /NEWPSOFF /KET /15T /Key -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-28.txt InChI_TestSet-non-std-28.log NUL /AuxNone /NoLabels /NEWPSOFF /KET /15T /Key /FixedH -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-29.txt InChI_TestSet-non-std-29.log NUL /AuxNone /NoLabels /NEWPSOFF /KET /15T /SUU /SLUUD -inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-30.txt InChI_TestSet-non-std-30.log NUL /AuxNone /NoLabels /NEWPSOFF /KET /15T /SUU /SLUUD /RecMet +inchi-1.exe InChI_TestSet.sdf its-non-std-01.txt its-non-std-01.log NUL /AuxNone /NoLabels /SUU /SLUUD +inchi-1.exe InChI_TestSet.sdf its-non-std-02.txt its-non-std-02.log NUL /AuxNone /NoLabels /SRel /Key +inchi-1.exe InChI_TestSet.sdf its-non-std-03.txt its-non-std-03.log NUL /AuxNone /NoLabels /SRac +inchi-1.exe InChI_TestSet.sdf its-non-std-04.txt its-non-std-04.log NUL /AuxNone /NoLabels /SUU /SLUUD /SUCF +inchi-1.exe InChI_TestSet.sdf its-non-std-05.txt its-non-std-05.log NUL /AuxNone /NoLabels /NEWPSOFF /SRel +inchi-1.exe InChI_TestSet.sdf its-non-std-06.txt its-non-std-06.log NUL /AuxNone /NoLabels /NEWPSOFF /SRac +inchi-1.exe InChI_TestSet.sdf its-non-std-07.txt its-non-std-07.log NUL /AuxNone /NoLabels /NEWPSOFF /SLUUD /SUCF +inchi-1.exe InChI_TestSet.sdf its-non-std-08.txt its-non-std-08.log NUL /AuxNone /NoLabels /FixedH /Key +inchi-1.exe InChI_TestSet.sdf its-non-std-09.txt its-non-std-09.log NUL /AuxNone /NoLabels /NEWPSOFF /FixedH +inchi-1.exe InChI_TestSet.sdf its-non-std-10.txt its-non-std-10.log NUL /AuxNone /NoLabels /FixedH /SNon +inchi-1.exe InChI_TestSet.sdf its-non-std-11.txt its-non-std-11.log NUL /AuxNone /NoLabels /FixedH /SRel +inchi-1.exe InChI_TestSet.sdf its-non-std-12.txt its-non-std-12.log NUL /AuxNone /NoLabels /RecMet /Key +inchi-1.exe InChI_TestSet.sdf its-non-std-13.txt its-non-std-13.log NUL /AuxNone /NoLabels /NEWPSOFF /RecMet +inchi-1.exe InChI_TestSet.sdf its-non-std-14.txt its-non-std-14.log NUL /AuxNone /NoLabels /RecMet /SNon +inchi-1.exe InChI_TestSet.sdf its-non-std-15.txt its-non-std-15.log NUL /AuxNone /NoLabels /RecMet /SRel +inchi-1.exe InChI_TestSet.sdf its-non-std-16.txt its-non-std-16.log NUL /AuxNone /NoLabels /FixedH /RecMet /Key +inchi-1.exe InChI_TestSet.sdf its-non-std-17.txt its-non-std-17.log NUL /AuxNone /NoLabels /NEWPSOFF /FixedH /RecMet +inchi-1.exe InChI_TestSet.sdf its-non-std-18.txt its-non-std-18.log NUL /AuxNone /NoLabels /FixedH /RecMet /SNon +inchi-1.exe InChI_TestSet.sdf its-non-std-19.txt its-non-std-19.log NUL /AuxNone /NoLabels /FixedH /RecMet /SRel +inchi-1.exe InChI_TestSet.sdf its-non-std-20.txt its-non-std-20.log NUL /AuxNone /NoLabels /KET /Key +inchi-1.exe InChI_TestSet.sdf its-non-std-21.txt its-non-std-21.log NUL /AuxNone /NoLabels /KET /SNon +inchi-1.exe InChI_TestSet.sdf its-non-std-22.txt its-non-std-22.log NUL /AuxNone /NoLabels /KET /SRel +inchi-1.exe InChI_TestSet.sdf its-non-std-23.txt its-non-std-23.log NUL /AuxNone /NoLabels /15T /Key +inchi-1.exe InChI_TestSet.sdf its-non-std-24.txt its-non-std-24.log NUL /AuxNone /NoLabels /15T /SNon +inchi-1.exe InChI_TestSet.sdf its-non-std-25.txt its-non-std-25.log NUL /AuxNone /NoLabels /15T /SRel +inchi-1.exe InChI_TestSet.sdf its-non-std-26.txt its-non-std-26.log NUL /AuxNone /NoLabels /KET /15T +inchi-1.exe InChI_TestSet.sdf its-non-std-27.txt its-non-std-27.log NUL /AuxNone /NoLabels /NEWPSOFF /KET /15T /Key +inchi-1.exe InChI_TestSet.sdf its-non-std-28.txt its-non-std-28.log NUL /AuxNone /NoLabels /NEWPSOFF /KET /15T /SUU /SLUUD diff --git a/INCHI-1-TEST/test/TestSet2InChI.sh b/INCHI-1-TEST/test/TestSet2InChI.sh index 1e2b9a9..61e4f39 100644 --- a/INCHI-1-TEST/test/TestSet2InChI.sh +++ b/INCHI-1-TEST/test/TestSet2InChI.sh @@ -1,38 +1,39 @@ +# # Standard InChI -./inchi-1 InChI_TestSet.sdf InChI_TestSet-std-01.txt InChI_TestSet-std-01.log NUL -AuxNone -NoLabels -Key -./inchi-1 InChI_TestSet.sdf InChI_TestSet-std-02.txt InChI_TestSet-std-02.log NUL -AuxNone -NoLabels -NEWPSOFF -./inchi-1 InChI_TestSet.sdf InChI_TestSet-std-03.txt InChI_TestSet-std-03.log NUL -AuxNone -NoLabels -SNon -Key -./inchi-1 InChI_TestSet.sdf InChI_TestSet-std-04.txt InChI_TestSet-std-04.log NUL -AuxNone -NoLabels -DoNotAddH -./inchi-1 InChI_TestSet.sdf InChI_TestSet-std-05.txt InChI_TestSet-std-05.log NUL -AuxNone -NoLabels -SNon -DoNotAddH -./inchi-1 InChI_TestSet.sdf InChI_TestSet-std-06.txt InChI_TestSet-std-06.log NUL -AuxNone -NoLabels -NEWPSOFF -DoNotAddH +# +./inchi-1 InChI_TestSet.sdf its-std-01.txt its-std-01.log NUL -AuxNone -NoLabels -Key +./inchi-1 InChI_TestSet.sdf its-std-02.txt its-std-02.log NUL -AuxNone -NoLabels -NEWPSOFF +./inchi-1 InChI_TestSet.sdf its-std-03.txt its-std-03.log NUL -AuxNone -NoLabels -SNon -Key +./inchi-1 InChI_TestSet.sdf its-std-04.txt its-std-04.log NUL -AuxNone -NoLabels -DoNotAddH +./inchi-1 InChI_TestSet.sdf its-std-05.txt its-std-05.log NUL -AuxNone -NoLabels -SNon -DoNotAddH +# # Non-standard InChI -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-01.txt InChI_TestSet-non-std-01.log NUL -AuxNone -NoLabels -SUU -SLUUD -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-02.txt InChI_TestSet-non-std-02.log NUL -AuxNone -NoLabels -SRel -Key -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-03.txt InChI_TestSet-non-std-03.log NUL -AuxNone -NoLabels -SRac -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-04.txt InChI_TestSet-non-std-04.log NUL -AuxNone -NoLabels -SUU -SLUUD -SUCF -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-05.txt InChI_TestSet-non-std-05.log NUL -AuxNone -NoLabels -NEWPSOFF -SRel -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-06.txt InChI_TestSet-non-std-06.log NUL -AuxNone -NoLabels -NEWPSOFF -SRac -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-07.txt InChI_TestSet-non-std-07.log NUL -AuxNone -NoLabels -NEWPSOFF -SLUUD -SUCF -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-08.txt InChI_TestSet-non-std-08.log NUL -AuxNone -NoLabels -FixedH -Key -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-09.txt InChI_TestSet-non-std-09.log NUL -AuxNone -NoLabels -NEWPSOFF -FixedH -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-10.txt InChI_TestSet-non-std-10.log NUL -AuxNone -NoLabels -FixedH -SNon -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-11.txt InChI_TestSet-non-std-11.log NUL -AuxNone -NoLabels -FixedH -SRel -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-12.txt InChI_TestSet-non-std-12.log NUL -AuxNone -NoLabels -RecMet -Key -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-13.txt InChI_TestSet-non-std-13.log NUL -AuxNone -NoLabels -NEWPSOFF -RecMet -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-14.txt InChI_TestSet-non-std-14.log NUL -AuxNone -NoLabels -RecMet -SNon -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-15.txt InChI_TestSet-non-std-15.log NUL -AuxNone -NoLabels -RecMet -SRel -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-16.txt InChI_TestSet-non-std-16.log NUL -AuxNone -NoLabels -FixedH -RecMet -Key -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-17.txt InChI_TestSet-non-std-17.log NUL -AuxNone -NoLabels -NEWPSOFF -FixedH -RecMet -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-18.txt InChI_TestSet-non-std-18.log NUL -AuxNone -NoLabels -FixedH -RecMet -SNon -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-19.txt InChI_TestSet-non-std-19.log NUL -AuxNone -NoLabels -FixedH -RecMet -SRel -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-20.txt InChI_TestSet-non-std-20.log NUL -AuxNone -NoLabels -KET -Key -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-21.txt InChI_TestSet-non-std-21.log NUL -AuxNone -NoLabels -KET -SNon -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-22.txt InChI_TestSet-non-std-22.log NUL -AuxNone -NoLabels -KET -SRel -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-23.txt InChI_TestSet-non-std-23.log NUL -AuxNone -NoLabels -15T -Key -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-24.txt InChI_TestSet-non-std-24.log NUL -AuxNone -NoLabels -15T -SNon -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-25.txt InChI_TestSet-non-std-25.log NUL -AuxNone -NoLabels -15T -SRel -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-26.txt InChI_TestSet-non-std-26.log NUL -AuxNone -NoLabels -KET -15T -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-27.txt InChI_TestSet-non-std-27.log NUL -AuxNone -NoLabels -NEWPSOFF -KET -15T -Key -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-28.txt InChI_TestSet-non-std-28.log NUL -AuxNone -NoLabels -NEWPSOFF -KET -15T -Key -FixedH -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-29.txt InChI_TestSet-non-std-29.log NUL -AuxNone -NoLabels -NEWPSOFF -KET -15T -SUU -SLUUD -./inchi-1 InChI_TestSet.sdf InChI_TestSet-non-std-30.txt InChI_TestSet-non-std-30.log NUL -AuxNone -NoLabels -NEWPSOFF -KET -15T -SUU -SLUUD -RecMet +# +./inchi-1 InChI_TestSet.sdf its-non-std-01.txt its-non-std-01.log NUL -AuxNone -NoLabels -SUU -SLUUD +./inchi-1 InChI_TestSet.sdf its-non-std-02.txt its-non-std-02.log NUL -AuxNone -NoLabels -SRel -Key +./inchi-1 InChI_TestSet.sdf its-non-std-03.txt its-non-std-03.log NUL -AuxNone -NoLabels -SRac +./inchi-1 InChI_TestSet.sdf its-non-std-04.txt its-non-std-04.log NUL -AuxNone -NoLabels -SUU -SLUUD -SUCF +./inchi-1 InChI_TestSet.sdf its-non-std-05.txt its-non-std-05.log NUL -AuxNone -NoLabels -NEWPSOFF -SRel +./inchi-1 InChI_TestSet.sdf its-non-std-06.txt its-non-std-06.log NUL -AuxNone -NoLabels -NEWPSOFF -SRac +./inchi-1 InChI_TestSet.sdf its-non-std-07.txt its-non-std-07.log NUL -AuxNone -NoLabels -NEWPSOFF -SLUUD -SUCF +./inchi-1 InChI_TestSet.sdf its-non-std-08.txt its-non-std-08.log NUL -AuxNone -NoLabels -FixedH -Key +./inchi-1 InChI_TestSet.sdf its-non-std-09.txt its-non-std-09.log NUL -AuxNone -NoLabels -NEWPSOFF -FixedH +./inchi-1 InChI_TestSet.sdf its-non-std-10.txt its-non-std-10.log NUL -AuxNone -NoLabels -FixedH -SNon +./inchi-1 InChI_TestSet.sdf its-non-std-11.txt its-non-std-11.log NUL -AuxNone -NoLabels -FixedH -SRel +./inchi-1 InChI_TestSet.sdf its-non-std-12.txt its-non-std-12.log NUL -AuxNone -NoLabels -RecMet -Key +./inchi-1 InChI_TestSet.sdf its-non-std-13.txt its-non-std-13.log NUL -AuxNone -NoLabels -NEWPSOFF -RecMet +./inchi-1 InChI_TestSet.sdf its-non-std-14.txt its-non-std-14.log NUL -AuxNone -NoLabels -RecMet -SNon +./inchi-1 InChI_TestSet.sdf its-non-std-15.txt its-non-std-15.log NUL -AuxNone -NoLabels -RecMet -SRel +./inchi-1 InChI_TestSet.sdf its-non-std-16.txt its-non-std-16.log NUL -AuxNone -NoLabels -FixedH -RecMet -Key +./inchi-1 InChI_TestSet.sdf its-non-std-17.txt its-non-std-17.log NUL -AuxNone -NoLabels -NEWPSOFF -FixedH -RecMet +./inchi-1 InChI_TestSet.sdf its-non-std-18.txt its-non-std-18.log NUL -AuxNone -NoLabels -FixedH -RecMet -SNon +./inchi-1 InChI_TestSet.sdf its-non-std-19.txt its-non-std-19.log NUL -AuxNone -NoLabels -FixedH -RecMet -SRel +./inchi-1 InChI_TestSet.sdf its-non-std-20.txt its-non-std-20.log NUL -AuxNone -NoLabels -KET -Key +./inchi-1 InChI_TestSet.sdf its-non-std-21.txt its-non-std-21.log NUL -AuxNone -NoLabels -KET -SNon +./inchi-1 InChI_TestSet.sdf its-non-std-22.txt its-non-std-22.log NUL -AuxNone -NoLabels -KET -SRel +./inchi-1 InChI_TestSet.sdf its-non-std-23.txt its-non-std-23.log NUL -AuxNone -NoLabels -15T -Key +./inchi-1 InChI_TestSet.sdf its-non-std-24.txt its-non-std-24.log NUL -AuxNone -NoLabels -15T -SNon +./inchi-1 InChI_TestSet.sdf its-non-std-25.txt its-non-std-25.log NUL -AuxNone -NoLabels -15T -SRel +./inchi-1 InChI_TestSet.sdf its-non-std-26.txt its-non-std-26.log NUL -AuxNone -NoLabels -KET -15T +./inchi-1 InChI_TestSet.sdf its-non-std-27.txt its-non-std-27.log NUL -AuxNone -NoLabels -NEWPSOFF -KET -15T -Key +./inchi-1 InChI_TestSet.sdf its-non-std-28.txt its-non-std-28.log NUL -AuxNone -NoLabels -NEWPSOFF -KET -15T -SUU -SLUUD diff --git a/INCHI-1-TEST/test/readme.txt b/INCHI-1-TEST/test/readme.txt index a7541f1..2463d4a 100644 --- a/INCHI-1-TEST/test/readme.txt +++ b/INCHI-1-TEST/test/readme.txt @@ -1,16 +1,19 @@ /* * International Chemical Identifier (InChI) * Version 1 - * Software version 1.04 - * September 9, 2011 + * Software version 1.05 + * January 27, 2017 * * The InChI library and programs are free software developed under the * auspices of the International Union of Pure and Applied Chemistry (IUPAC). - * Originally developed at NIST. Modifications and additions by IUPAC - * and the InChI Trust. + * Originally developed at NIST. + * Modifications and additions by IUPAC and the InChI Trust. + * Some portions of code were developed/changed by external contributors + * (either contractor or volunteer) which are listed in the file + * 'External-contributors' included in this distribution. * * IUPAC/InChI-Trust Licence No.1.0 for the - * International Chemical Identifier (InChI) Software version 1.04 + * International Chemical Identifier (InChI) * Copyright (C) IUPAC and InChI Trust Limited * * This library is free software; you can redistribute it and/or modify it @@ -18,163 +21,31 @@ * or any later version. * * Please note that this library is distributed WITHOUT ANY WARRANTIES - * whatsoever, whether expressed or implied. See the IUPAC/InChI Trust - * Licence for the International Chemical Identifier (InChI) Software - * version 1.04, October 2011 ("IUPAC/InChI-Trust InChI Licence No.1.0") - * for more details. + * whatsoever, whether expressed or implied. + * See the IUPAC/InChI-Trust InChI Licence No.1.0 for more details. * * You should have received a copy of the IUPAC/InChI Trust InChI * Licence No. 1.0 with this library; if not, please write to: * * The InChI Trust - * c/o FIZ CHEMIE Berlin + * 8 Cavendish Avenue + * Cambridge CB1 7US + * UK * - * Franklinstrasse 11 - * 10587 Berlin - * GERMANY - * - * or email to: ulrich@inchi-trust.org. + * or e-mail to alan@inchi-trust.org * */ - This directory contains a simple test suite for InChI version 1, -software version 1.04. +software version 1.05. Included are: a) source structural data (in SDF format); -b) 36 reference result files containing InChI strings (in several cases, +b) reference result files containing InChI strings (in several cases, accompanied by InChIKey strings) generated from the source data by InChI -software v. 1.04 executable inchi-1 with different command-line options; +software v. 1.05 executable inchi-1 with different command-line options; c) scripts used to generate the reference identifiers. -Using these data, one may verify that InChI code included in an -application or InChI code ported to a particular compiler or operating -system (such an application containing InChI code later in this document -is called "software") produces the same InChI as the software distributed by -IUPAC. - -The method of verification is to compare InChI produced by the software out -of a representative set of chemical structures and with various options -to InChI produced by the official IUPAC software. -Identical results are necessary for the software to pass the verification. -The same procedure should be used for INChIKey, when appicable. - The source data includes a minimal set of chemical structures presented as a single SDfile, InChI_TestSet.sdf. - -The 6 sets of options were used for generation of reference standard -identifiers and the 30 sets of options were used to produce non-standard -ones (note that this does not provide the exhaustive coverage of all possible -option combinations). - -The options and corresponding reference output file names are given below. - - -File InChIKey InChI options - -* standard InChI * - -InChI_TestSet-std-01.txt yes - -InChI_TestSet-std-02.txt no NEWPSOFF -InChI_TestSet-std-03.txt yes SNon -InChI_TestSet-std-04.txt no DoNotAddH -InChI_TestSet-std-05.txt no SNon DoNotAddH -InChI_TestSet-std-06.txt no NEWPSOFF DoNotAddH - -* non-standard InChI * - -InChI_TestSet-non-std-01.txt no SUU SLUUD -InChI_TestSet-non-std-02.txt yes SRel -InChI_TestSet-non-std-03.txt no SRac -InChI_TestSet-non-std-04.txt no SUU SLUUD SUCF -InChI_TestSet-non-std-05.txt no NEWPSOFF SRel -InChI_TestSet-non-std-06.txt no NEWPSOFF SRac -InChI_TestSet-non-std-07.txt no NEWPSOFF SLUUD SUCF -InChI_TestSet-non-std-08.txt yes FixedH -InChI_TestSet-non-std-09.txt no NEWPSOFF FixedH -InChI_TestSet-non-std-10.txt no FixedH SNon -InChI_TestSet-non-std-11.txt no FixedH SRel -InChI_TestSet-non-std-12.txt yes RecMet -InChI_TestSet-non-std-13.txt no NEWPSOFF RecMet -InChI_TestSet-non-std-14.txt no RecMet SNon -InChI_TestSet-non-std-15.txt no RecMet SRel -InChI_TestSet-non-std-16.txt yes FixedH RecMet -InChI_TestSet-non-std-17.txt no NEWPSOFF FixedH RecMet -InChI_TestSet-non-std-18.txt no FixedH RecMet SNon -InChI_TestSet-non-std-19.txt no FixedH RecMet SRel -InChI_TestSet-non-std-20.txt yes KET -InChI_TestSet-non-std-21.txt no KET SNon -InChI_TestSet-non-std-22.txt no KET SRel -InChI_TestSet-non-std-23.txt yes 15T -InChI_TestSet-non-std-24.txt no 15T SNon -InChI_TestSet-non-std-25.txt no 15T SRel -InChI_TestSet-non-std-26.txt no KET 15T -InChI_TestSet-non-std-27.txt yes NEWPSOFF KET 15T -InChI_TestSet-non-std-28.txt no NEWPSOFF FixedH KET 15T Key -InChI_TestSet-non-std-29.txt no NEWPSOFF SUU SLUUD KET 15T -InChI_TestSet-non-std-30.txt no NEWPSOFF SUU SLUUD RecMet KET 15T - - -To reduce the size of the output, two additional options are added to each set: -/AuxNone /NoLabels. -If InChIKey should be printed, the option -/Key -is specified. - -For example, the 01 set of std InChI options is: -/AuxNone /NoLabels /Key - - -Note. In general, this test suite establishes necessary but not sufficient -conditions for the software compliance: it does not prove that there does -not exist a structure such that the identifier produced by the software -would be different from that produced by inchi-1. -A practical solution is to test the software on as great as possible -variety of structures. Therefore, at the discretion of the tester, -other structure collection(s) may be included in validation. - -Such a collection is, for example, NCI Open "September 2003 SD File of -Combined DTP Releases, 2D/3D, with Canonical Properties Added," containing -260,071 structures. The compressed SDfile may be downloaded from -http://cactus.nci.nih.gov/download/nci/ -The direct link to the file is "260,071 structures in SDF format", -http://cactus.nci.nih.gov/download/nci/NCI-Open_09-03.sdf.gz - -Another publicly available collections is the PubChem collection of -Compound chemical structures. It may be found at -ftp://ftp.ncbi.nlm.nih.gov/pubchem/Compound/CURRENT-Full/SDF/ - - -========= - FILES -========= - -readme.txt this file - -InChI_TestSet.sdf Source structural data (SDF) - -TestSet2InChI.bat Windows batch file for generation of identifiers -TestSet2InChI.sh Linux shell script for generation of identifiers - -InChI_TestSet-result.zip Archive containing the 36 reference - result files: - InChI_TestSet-std-01.txt - ... - InChI_TestSet-std-06.txt - InChI_TestSet-non-std-01.txt - ... - InChI_TestSet-non-std-30.txt - - - - - -========= - LINKS -========= - -IUPAC http://www.iupac.org/inchi -InChI Trust http://www.inchi-trust.org -InChI discussion group https://lists.sourceforge.net/lists/listinfo/inchi-discuss